diff --git a/LOCKS b/LOCKS index a7a1e833f44..40fa053a464 100644 --- a/LOCKS +++ b/LOCKS @@ -12,3 +12,7 @@ releng/5.* Requires Security Officer approval. releng/6.* Requires Security Officer approval. releng/7.* Requires Security Officer approval. releng/8.* Requires Security Officer approval. +head/sys/dev/random Requires Security Officer approval. +head/sys/libkern/arc4random.c Requires Security Officer approval. +stable/*/sys/dev/random Requires Security Officer approval. +stable/*/sys/libkern/arc4random.c Requires Security Officer approval. diff --git a/Makefile b/Makefile index c49a1dd5eac..da642f84e41 100644 --- a/Makefile +++ b/Makefile @@ -242,6 +242,7 @@ cleanworld: # skip this for -n to avoid changing previous behavior of # 'make -n buildworld' etc. ${TGTS}: .MAKE +tinderbox toolchains kernel-toolchains: .MAKE .endif ${TGTS}: @@ -348,19 +349,21 @@ make bmake: .PHONY @echo ">>> Building an up-to-date make(1)" @echo "--------------------------------------------------------------" ${_+_}@cd ${.CURDIR}/usr.bin/${.TARGET}; \ - ${MMAKE} obj && \ - ${MMAKE} depend && \ - ${MMAKE} all && \ + ${MMAKE} obj DESTDIR= && \ + ${MMAKE} depend DESTDIR= && \ + ${MMAKE} all DESTDIR= PROGNAME=${MYMAKE:T} && \ ${MMAKE} install DESTDIR=${MYMAKE:H} BINDIR= PROGNAME=${MYMAKE:T} +tinderbox toolchains kernel-toolchains: upgrade_checks + tinderbox: - @cd ${.CURDIR} && ${MAKE} DOING_TINDERBOX=YES universe + @cd ${.CURDIR} && ${SUB_MAKE} DOING_TINDERBOX=YES universe toolchains: - @cd ${.CURDIR} && ${MAKE} UNIVERSE_TARGET=toolchain universe + @cd ${.CURDIR} && ${SUB_MAKE} UNIVERSE_TARGET=toolchain universe kernel-toolchains: - @cd ${.CURDIR} && ${MAKE} UNIVERSE_TARGET=kernel-toolchain universe + @cd ${.CURDIR} && ${SUB_MAKE} UNIVERSE_TARGET=kernel-toolchain universe # # universe @@ -419,7 +422,7 @@ universe_${target}_prologue: universe_prologue .if !defined(MAKE_JUST_KERNELS) .for target_arch in ${TARGET_ARCHES_${target}} universe_${target}: universe_${target}_${target_arch} -universe_${target}_${target_arch}: universe_${target}_prologue +universe_${target}_${target_arch}: universe_${target}_prologue .MAKE @echo ">> ${target}.${target_arch} ${UNIVERSE_TARGET} started on `LC_ALL=C date`" @(cd ${.CURDIR} && env __MAKE_CONF=/dev/null \ ${SUB_MAKE} ${JFLAG} ${UNIVERSE_TARGET} \ @@ -440,7 +443,7 @@ universe_${target}_kernels: universe_${target}_${target_arch} .endfor .endif universe_${target}: universe_${target}_kernels -universe_${target}_kernels: universe_${target}_prologue +universe_${target}_kernels: universe_${target}_prologue .MAKE .if exists(${KERNSRCDIR}/${target}/conf/NOTES) @(cd ${KERNSRCDIR}/${target}/conf && env __MAKE_CONF=/dev/null \ ${SUB_MAKE} LINT > ${.CURDIR}/_.${target}.makeLINT 2>&1 || \ @@ -468,7 +471,7 @@ TARGET_ARCH_${kernel}!= cd ${KERNSRCDIR}/${TARGET}/conf && \ .error "Target architecture for ${TARGET}/conf/${kernel} unknown. config(8) likely too old." .endif universe_kernconfs: universe_kernconf_${TARGET}_${kernel} -universe_kernconf_${TARGET}_${kernel}: +universe_kernconf_${TARGET}_${kernel}: .MAKE @(cd ${.CURDIR} && env __MAKE_CONF=/dev/null \ ${SUB_MAKE} ${JFLAG} buildkernel \ TARGET=${TARGET} \ @@ -495,3 +498,11 @@ universe_epilogue: buildLINT: ${MAKE} -C ${.CURDIR}/sys/${_TARGET}/conf LINT + +.if defined(.PARSEDIR) +.if make(universe) +# we do not want a failure of one branch abort all. +MAKE_JOB_ERROR_TOKEN= no +.export MAKE_JOB_ERROR_TOKEN +.endif +.endif diff --git a/Makefile.inc1 b/Makefile.inc1 index 2099fa9e0dd..c2315416643 100644 --- a/Makefile.inc1 +++ b/Makefile.inc1 @@ -265,7 +265,7 @@ WMAKEENV= ${CROSSENV} \ PATH=${TMPPATH} # make hierarchy -HMAKE= PATH=${TMPPATH} ${MAKE} LOCAL_MTREE=${LOCAL_MTREE} +HMAKE= PATH=${TMPPATH} ${MAKE} LOCAL_MTREE=${LOCAL_MTREE:Q} .if defined(NO_ROOT) HMAKE+= PATH=${TMPPATH} METALOG=${METALOG} -DNO_ROOT .endif @@ -382,6 +382,7 @@ LIB32WMAKEENV+= MAKEOBJDIRPREFIX=${OBJTREE}/lib32 \ PATH=${TMPPATH} \ LIBDIR=/usr/lib32 \ SHLIBDIR=/usr/lib32 \ + LIBPRIVATEDIR=/usr/lib32/private \ COMPILER_TYPE=${WMAKE_COMPILER_TYPE} LIB32WMAKEFLAGS+= \ CC="${XCC} ${LIB32FLAGS}" \ @@ -675,8 +676,9 @@ kernel-toolchain: ${TOOLCHAIN_TGTS:N_includes:N_libraries} # # Checks to be sure system is ready for installworld/installkernel. # -installcheck: -installcheck_UGID: +installcheck: _installcheck_world _installcheck_kernel +_installcheck_world: +_installcheck_kernel: # # Require DESTDIR to be set if installing for a different architecture or @@ -685,8 +687,9 @@ installcheck_UGID: .if ${TARGET_ARCH} != ${MACHINE_ARCH} || ${TARGET} != ${MACHINE} || \ defined(DB_FROM_SRC) .if !make(distributeworld) -installcheck: installcheck_DESTDIR -installcheck_DESTDIR: +_installcheck_world: __installcheck_DESTDIR +_installcheck_kernel: __installcheck_DESTDIR +__installcheck_DESTDIR: .if !defined(DESTDIR) || empty(DESTDIR) @echo "ERROR: Please set DESTDIR!"; \ false @@ -708,7 +711,12 @@ CHECK_GIDS+= smmsp CHECK_UIDS+= proxy CHECK_GIDS+= proxy authpf .endif -installcheck_UGID: +.if ${MK_UNBOUND} != "no" +CHECK_UIDS+= unbound +CHECK_GIDS+= unbound +.endif +_installcheck_world: __installcheck_UGID +__installcheck_UGID: .for uid in ${CHECK_UIDS} @if ! `id -u ${uid} >/dev/null 2>&1`; then \ echo "ERROR: Required ${uid} user is missing, see /usr/src/UPDATING."; \ @@ -763,7 +771,7 @@ EXTRA_DISTRIBUTIONS+= lib32 MTREE_MAGIC?= mtree 2.0 -distributeworld installworld: installcheck installcheck_UGID +distributeworld installworld: _installcheck_world mkdir -p ${INSTALLTMP} progs=$$(for prog in ${ITOOLS}; do \ if progpath=`which $$prog`; then \ @@ -814,7 +822,7 @@ distributeworld installworld: installcheck installcheck_UGID cd ${.CURDIR}/etc; ${CROSSENV} PATH=${TMPPATH} ${MAKE} \ METALOG=${METALOG} ${IMAKE_INSTALL} ${IMAKE_MTREE} \ DISTBASE=/base DESTDIR=${DESTDIR}/${DISTDIR}/base \ - LOCAL_MTREE=${LOCAL_MTREE} distrib-dirs + LOCAL_MTREE=${LOCAL_MTREE:Q} distrib-dirs .endif ${_+_}cd ${.CURDIR}; ${IMAKE} re${.TARGET:S/world$//}; \ ${IMAKEENV} rm -rf ${INSTALLTMP} @@ -888,7 +896,7 @@ reinstall: @echo ">>> Making hierarchy" @echo "--------------------------------------------------------------" ${_+_}cd ${.CURDIR}; ${MAKE} -f Makefile.inc1 \ - LOCAL_MTREE=${LOCAL_MTREE} hierarchy + LOCAL_MTREE=${LOCAL_MTREE:Q} hierarchy @echo @echo "--------------------------------------------------------------" @echo ">>> Installing everything" @@ -1041,7 +1049,7 @@ buildkernel: # Install the kernel defined by INSTALLKERNEL # installkernel installkernel.debug \ -reinstallkernel reinstallkernel.debug: installcheck +reinstallkernel reinstallkernel.debug: _installcheck_kernel .if empty(INSTALLKERNEL) @echo "ERROR: No kernel \"${KERNCONF}\" to install."; \ false @@ -1331,7 +1339,8 @@ build-tools: .MAKE usr.bin/awk \ lib/libmagic \ usr.bin/mkesdb_static \ - usr.bin/mkcsmapper_static + usr.bin/mkcsmapper_static \ + usr.bin/vi/catalog ${_+_}@${ECHODIR} "===> ${_tool} (obj,build-tools)"; \ cd ${.CURDIR}/${_tool} && \ ${MAKE} DIRPRFX=${_tool}/ obj && \ @@ -1468,8 +1477,8 @@ _prebuild_libs= ${_kerberos5_lib_libasn1} \ ${_cddl_lib_libumem} ${_cddl_lib_libnvpair} \ ${_cddl_lib_libzfs_core} \ lib/libutil ${_lib_libypclnt} lib/libz lib/msun \ - ${_secure_lib_libcrypto} ${_secure_lib_libssh} \ - ${_secure_lib_libssl} + ${_secure_lib_libcrypto} ${_lib_libldns} \ + ${_secure_lib_libssh} ${_secure_lib_libssl} .if ${MK_ATF} != "no" _lib_atf_libatf_c= lib/atf/libatf-c @@ -1505,9 +1514,16 @@ cddl/lib/libzfs_core__L: cddl/lib/libnvpair__L _secure_lib_libcrypto= secure/lib/libcrypto _secure_lib_libssl= secure/lib/libssl lib/libradius__L secure/lib/libssl__L: secure/lib/libcrypto__L +.if ${MK_LDNS} != "no" +_lib_libldns= lib/libldns +lib/libldns__L: secure/lib/libcrypto__L +.endif .if ${MK_OPENSSH} != "no" _secure_lib_libssh= secure/lib/libssh secure/lib/libssh__L: lib/libz__L secure/lib/libcrypto__L lib/libcrypt__L +.if ${MK_LDNS} != "no" +secure/lib/libssh__L: lib/libldns__L +.endif .if ${MK_KERBEROS_SUPPORT} != "no" secure/lib/libssh__L: lib/libgssapi__L kerberos5/lib/libkrb5__L \ kerberos5/lib/libhx509__L kerberos5/lib/libasn1__L lib/libcom_err__L \ diff --git a/ObsoleteFiles.inc b/ObsoleteFiles.inc index 9d7d2b52634..e093be4fabd 100644 --- a/ObsoleteFiles.inc +++ b/ObsoleteFiles.inc @@ -38,6 +38,29 @@ # xargs -n1 | sort | uniq -d; # done +# 20130908: libssh becomes private +OLD_FILES+=usr/lib/libssh.a +OLD_FILES+=usr/lib/libssh.so +OLD_LIBS+=usr/lib/libssh.so.5 +OLD_FILES+=usr/lib/libssh_p.a +OLD_FILES+=usr/lib32/libssh.a +OLD_FILES+=usr/lib32/libssh.so +OLD_LIBS+=usr/lib32/libssh.so.5 +OLD_FILES+=usr/lib32/libssh_p.a +# 20130903: gnupatch is no more +OLD_FILES+=usr/bin/gnupatch +OLD_FILES+=usr/share/man/man1/gnupatch.1.gz +# 20130829: bsdpatch is patch unconditionally +OLD_FILES+=usr/bin/bsdpatch +OLD_FILES+=usr/share/man/man1/bsdpatch.1.gz +# 20130822: bind 9.9.3-P2 import +OLD_LIBS+=usr/lib/liblwres.so.80 +# 20130814: vm_page_busy(9) +OLD_FILES+=usr/share/man/man9/vm_page_flash.9.gz +OLD_FILES+=usr/share/man/man9/vm_page_io.9.gz +OLD_FILES+=usr/share/man/man9/vm_page_io_finish.9.gz +OLD_FILES+=usr/share/man/man9/vm_page_io_start.9.gz +OLD_FILES+=usr/share/man/man9/vm_page_wakeup.9.gz # 20130710: libkvm version bump OLD_LIBS+=lib/libkvm.so.5 OLD_LIBS+=usr/lib32/libkvm.so.5 @@ -114,6 +137,7 @@ OLD_FILES+=usr/include/clang/3.2/xmmintrin.h OLD_FILES+=usr/include/clang/3.2/xopintrin.h OLD_DIRS+=usr/include/clang/3.2 # 20130404: legacy ATA stack removed +OLD_FILES+=etc/periodic/daily/405.status-ata-raid OLD_FILES+=rescue/atacontrol OLD_FILES+=sbin/atacontrol OLD_FILES+=usr/share/man/man8/atacontrol.8.gz diff --git a/UPDATING b/UPDATING index d6f02812eb6..8466ec09d6a 100644 --- a/UPDATING +++ b/UPDATING @@ -31,6 +31,68 @@ NOTE TO PEOPLE WHO THINK THAT FreeBSD 10.x IS SLOW: disable the most expensive debugging functionality run "ln -s 'abort:false,junk:false' /etc/malloc.conf".) +20130916: + With the addition of unbound(8), a new unbound user is now + required during installworld. "mergemaster -p" can be used to + add the user prior to installworld, as documented in the handbook. + +20130911: + OpenSSH is now built with DNSSEC support, and will by default + silently trust signed SSHFP records. This can be controlled with + the VerifyHostKeyDNS client configuration setting. DNSSEC support + can be disabled entirely with the WITHOUT_LDNS option in src.conf. + +20130906: + The GNU Compiler Collection and C++ standard library (libstdc++) + are no longer built by default on platforms where clang is the system + compiler. You can enable them with the WITH_GCC and WITH_GNUCXX + options in src.conf. + +20130905: + The PROCDESC kernel option is now part of the GENERIC kernel + configuration and is required for the rwhod(8) to work. + If you are using custom kernel configuration, you should include + 'options PROCDESC'. + +20130905: + The API and ABI related to the Capsicum framework was modified + in backward incompatible way. The userland libraries and programs + have to be recompiled to work with the new kernel. This includes the + following libraries and programs, but the whole buildworld is + advised: libc, libprocstat, dhclient, tcpdump, hastd, hastctl, + kdump, procstat, rwho, rwhod, uniq. + +20130903: + AES-NI intrinsic support has been added to gcc. The AES-NI module + has been updated to use this support. A new gcc is required to build + the aesni module on both i386 and amd64. + +20130827: + Thomas Dickey (vendor author thereof) reports that dialog(1) since + 2011/10/18 has a bug in handling --hline. Testers and I noticed the + --hline is not ignored but displayed as a NULL string, regardless of + value. This will cause confusion in some bsdconfig dialogs where the + --hline is used to inform users which keybindings to use. This will + likewise affect any other persons relying on --hline. It also looks + rather strange seeing "[]" at the bottom of dialog(1) widgets when + passing --hline "anything". Thomas said he will have a look in a few + weeks. NOTE: The "[]" brackets appear with the left-edge where it + would normally appear given the width of text to display, but the + displayed text is not there (part of the bug). + +20130821: + The PADLOCK_RNG and RDRAND_RNG kernel options are now devices. + Thus "device padlock_rng" and "device rdrand_rng" should be + used instead of "options PADLOCK_RNG" & "options RDRAND_RNG". + +20130813: + WITH_ICONV has been split into two feature sets. WITH_ICONV now + enables just the iconv* functionality and is now on by default. + WITH_LIBICONV_COMPAT enables the libiconv api and link time + compatability. Set WITHOUT_ICONV to build the old way. + If you have been using WITH_ICONV before, you will very likely + need to turn on WITH_LIBICONV_COMPAT. + 20130806: INVARIANTS option now enables DEBUG for code with OpenSolaris and Illumos origin, including ZFS. If you have INVARIANTS in your @@ -245,8 +307,8 @@ NOTE TO PEOPLE WHO THINK THAT FreeBSD 10.x IS SLOW: 20121201: With the addition of auditdistd(8), a new auditdistd user is now - depended on during installworld. "mergemaster -p" can be used to add - the user prior to installworld, as documented in the handbook. + required during installworld. "mergemaster -p" can be used to + add the user prior to installworld, as documented in the handbook. 20121117: The sin6_scope_id member variable in struct sockaddr_in6 is now @@ -332,9 +394,9 @@ NOTE TO PEOPLE WHO THINK THAT FreeBSD 10.x IS SLOW: 20120913: The random(4) support for the VIA hardware random number generator (`PADLOCK') is no longer enabled unconditionally. - Add the PADLOCK_RNG option in the custom kernel config if + Add the padlock_rng device in the custom kernel config if needed. The GENERIC kernels on i386 and amd64 do include the - option, so the change only affects the custom kernel + device, so the change only affects the custom kernel configurations. 20120908: diff --git a/bin/chflags/chflags.1 b/bin/chflags/chflags.1 index fe9d700a903..47d5b181b45 100644 --- a/bin/chflags/chflags.1 +++ b/bin/chflags/chflags.1 @@ -32,7 +32,7 @@ .\" @(#)chflags.1 8.4 (Berkeley) 5/2/95 .\" $FreeBSD$ .\" -.Dd March 3, 2006 +.Dd April 8, 2013 .Dt CHFLAGS 1 .Os .Sh NAME @@ -101,20 +101,36 @@ The following keywords are currently defined: .Bl -tag -offset indent -width ".Cm opaque" .It Cm arch , archived set the archived flag (super-user only) -.It Cm opaque -set the opaque flag (owner or super-user only) .It Cm nodump set the nodump flag (owner or super-user only) +.It Cm opaque +set the opaque flag (owner or super-user only) .It Cm sappnd , sappend set the system append-only flag (super-user only) .It Cm schg , schange , simmutable set the system immutable flag (super-user only) +.It Cm snapshot +set the snapshot flag (filesystems do not allow changing this flag) .It Cm sunlnk , sunlink set the system undeletable flag (super-user only) .It Cm uappnd , uappend set the user append-only flag (owner or super-user only) +.It Cm uarch , uarchive +set the archive flag (owner or super-user only) .It Cm uchg , uchange , uimmutable set the user immutable flag (owner or super-user only) +.It Cm uhidden , hidden +set the hidden file attribute (owner or super-user only) +.It Cm uoffline , offline +set the offline file attribute (owner or super-user only) +.It Cm urdonly , rdonly , readonly +set the DOS, Windows and CIFS readonly flag (owner or super-user only) +.It Cm usparse , sparse +set the sparse file attribute (owner or super-user only) +.It Cm usystem , system +set the DOS, Windows and CIFS system flag (owner or super-user only) +.It Cm ureparse , reparse +set the Windows reparse point file attribute (owner or super-user only) .It Cm uunlnk , uunlink set the user undeletable flag (owner or super-user only) .El diff --git a/bin/ls/ls.1 b/bin/ls/ls.1 index 7c7265225e0..8b7672b996c 100644 --- a/bin/ls/ls.1 +++ b/bin/ls/ls.1 @@ -232,6 +232,9 @@ output. Include the file flags in a long .Pq Fl l output. +See +.Xr chflags 1 +for a list of file flags and their meanings. .It Fl p Write a slash .Pq Ql / diff --git a/bin/pkill/pkill.1 b/bin/pkill/pkill.1 index db7d78c96d7..1ca383fa395 100644 --- a/bin/pkill/pkill.1 +++ b/bin/pkill/pkill.1 @@ -29,7 +29,7 @@ .\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE .\" POSSIBILITY OF SUCH DAMAGE. .\" -.Dd February 11, 2010 +.Dd August 9, 2013 .Dt PKILL 1 .Os .Sh NAME @@ -44,6 +44,7 @@ .Op Fl N Ar system .Op Fl P Ar ppid .Op Fl U Ar uid +.Op Fl c Ar class .Op Fl d Ar delim .Op Fl g Ar pgrp .Op Fl j Ar jid @@ -60,6 +61,7 @@ .Op Fl N Ar system .Op Fl P Ar ppid .Op Fl U Ar uid +.Op Fl c Ar class .Op Fl g Ar pgrp .Op Fl j Ar jid .Op Fl s Ar sid @@ -130,6 +132,9 @@ or process and all of its ancestors are excluded (unless .Fl v is used). +.It Fl c Ar class +Restrict matches to processes running with specified login class +.Ar class . .It Fl f Match against full argument lists. The default is to match against process names. diff --git a/bin/pkill/pkill.c b/bin/pkill/pkill.c index cd919762899..23ac0e9d786 100644 --- a/bin/pkill/pkill.c +++ b/bin/pkill/pkill.c @@ -79,12 +79,14 @@ enum listtype { LT_TTY, LT_PGRP, LT_JID, - LT_SID + LT_SID, + LT_CLASS }; struct list { SLIST_ENTRY(list) li_chain; long li_number; + char *li_name; }; SLIST_HEAD(listhead, list); @@ -116,6 +118,7 @@ static struct listhead ppidlist = SLIST_HEAD_INITIALIZER(ppidlist); static struct listhead tdevlist = SLIST_HEAD_INITIALIZER(tdevlist); static struct listhead sidlist = SLIST_HEAD_INITIALIZER(sidlist); static struct listhead jidlist = SLIST_HEAD_INITIALIZER(jidlist); +static struct listhead classlist = SLIST_HEAD_INITIALIZER(classlist); static void usage(void) __attribute__((__noreturn__)); static int killact(const struct kinfo_proc *); @@ -179,7 +182,7 @@ main(int argc, char **argv) execf = NULL; coref = _PATH_DEVNULL; - while ((ch = getopt(argc, argv, "DF:G:ILM:N:P:SU:ad:fg:ij:lnoqs:t:u:vx")) != -1) + while ((ch = getopt(argc, argv, "DF:G:ILM:N:P:SU:ac:d:fg:ij:lnoqs:t:u:vx")) != -1) switch (ch) { case 'D': debug_opt++; @@ -222,6 +225,10 @@ main(int argc, char **argv) case 'a': ancestors++; break; + case 'c': + makelist(&classlist, LT_CLASS, optarg); + criteria = 1; + break; case 'd': if (!pgrep) usage(); @@ -469,6 +476,20 @@ main(int argc, char **argv) continue; } + SLIST_FOREACH(li, &classlist, li_chain) { + /* + * We skip P_SYSTEM processes to match ps(1) output. + */ + if ((kp->ki_flag & P_SYSTEM) == 0 && + kp->ki_loginclass != NULL && + strcmp(kp->ki_loginclass, li->li_name) == 0) + break; + } + if (SLIST_FIRST(&classlist) != NULL && li == NULL) { + selected[i] = 0; + continue; + } + if (argc == 0) selected[i] = 1; } @@ -562,9 +583,9 @@ usage(void) fprintf(stderr, "usage: %s %s [-F pidfile] [-G gid] [-M core] [-N system]\n" - " [-P ppid] [-U uid] [-g pgrp] [-j jid] [-s sid]\n" - " [-t tty] [-u euid] pattern ...\n", getprogname(), - ustr); + " [-P ppid] [-U uid] [-c class] [-g pgrp] [-j jid]\n" + " [-s sid] [-t tty] [-u euid] pattern ...\n", + getprogname(), ustr); exit(STATUS_BADUSAGE); } @@ -664,8 +685,10 @@ makelist(struct listhead *head, enum listtype type, char *src) SLIST_INSERT_HEAD(head, li, li_chain); empty = 0; - li->li_number = (uid_t)strtol(sp, &ep, 0); - if (*ep == '\0') { + if (type != LT_CLASS) + li->li_number = (uid_t)strtol(sp, &ep, 0); + + if (type != LT_CLASS && *ep == '\0') { switch (type) { case LT_PGRP: if (li->li_number == 0) @@ -750,6 +773,12 @@ foundtty: if ((st.st_mode & S_IFCHR) == 0) errx(STATUS_BADUSAGE, "Invalid jail ID `%s'", sp); break; + case LT_CLASS: + li->li_number = -1; + li->li_name = strdup(sp); + if (li->li_name == NULL) + err(STATUS_ERROR, "Cannot allocate memory"); + break; default: usage(); } diff --git a/bin/ps/keyword.c b/bin/ps/keyword.c index 5861129c82a..21e67912838 100644 --- a/bin/ps/keyword.c +++ b/bin/ps/keyword.c @@ -87,6 +87,7 @@ static VAR var[] = { {"etimes", "ELAPSED", NULL, USER, elapseds, 0, CHAR, NULL, 0}, {"euid", "", "uid", 0, NULL, 0, CHAR, NULL, 0}, {"f", "F", NULL, 0, kvar, KOFF(ki_flag), INT, "x", 0}, + {"fib", "FIB", NULL, 0, kvar, KOFF(ki_fibnum), INT, "d", 0}, {"flags", "", "f", 0, NULL, 0, CHAR, NULL, 0}, {"gid", "GID", NULL, 0, kvar, KOFF(ki_groups), UINT, UIDFMT, 0}, {"group", "GROUP", NULL, LJUST, egroupname, 0, CHAR, NULL, 0}, diff --git a/bin/ps/ps.1 b/bin/ps/ps.1 index 1a364f5f752..81a1f6d44ed 100644 --- a/bin/ps/ps.1 +++ b/bin/ps/ps.1 @@ -512,6 +512,9 @@ elapsed running time, format minutes:seconds. .It Cm etimes elapsed running time, in decimal integer seconds +.It Cm fib +default FIB number, see +.Xr setfib 1 .It Cm flags the process flags, in hexadecimal (alias .Cm f ) diff --git a/bin/sh/alias.c b/bin/sh/alias.c index da995bbd473..044c869ed94 100644 --- a/bin/sh/alias.c +++ b/bin/sh/alias.c @@ -237,17 +237,19 @@ printaliases(void) } int -aliascmd(int argc, char **argv) +aliascmd(int argc __unused, char **argv __unused) { char *n, *v; int ret = 0; struct alias *ap; - if (argc == 1) { + nextopt(""); + + if (*argptr == NULL) { printaliases(); return (0); } - while ((n = *++argv) != NULL) { + while ((n = *argptr++) != NULL) { if ((v = strchr(n+1, '=')) == NULL) /* n+1: funny ksh stuff */ if ((ap = lookupalias(n, 0)) == NULL) { warning("%s: not found", n); diff --git a/bin/sh/arith_yylex.c b/bin/sh/arith_yylex.c index f6eebdba264..ad0818426de 100644 --- a/bin/sh/arith_yylex.c +++ b/bin/sh/arith_yylex.c @@ -218,9 +218,13 @@ checkeqcur: value += ARITH_REM - '%'; goto checkeq; case '+': + if (buf[1] == '+') + return ARITH_BAD; value += ARITH_ADD - '+'; goto checkeq; case '-': + if (buf[1] == '-') + return ARITH_BAD; value += ARITH_SUB - '-'; goto checkeq; case '~': diff --git a/bin/sh/eval.c b/bin/sh/eval.c index 1daa3db9159..b8f76d57f3d 100644 --- a/bin/sh/eval.c +++ b/bin/sh/eval.c @@ -109,7 +109,6 @@ reseteval(void) { evalskip = 0; loopnest = 0; - funcnest = 0; } @@ -325,7 +324,7 @@ skipping: if (evalskip == SKIPCONT && --skipcount <= 0) { } if (evalskip == SKIPBREAK && --skipcount <= 0) evalskip = 0; - if (evalskip == SKIPFUNC || evalskip == SKIPFILE) + if (evalskip == SKIPRETURN) status = exitstatus; break; } @@ -1069,7 +1068,7 @@ evalcommand(union node *cmd, int flags, struct backcmd *backcmd) funcnest--; popredir(); INTON; - if (evalskip == SKIPFUNC) { + if (evalskip == SKIPRETURN) { evalskip = 0; skipcount = 0; } @@ -1306,14 +1305,8 @@ returncmd(int argc, char **argv) { int ret = argc > 1 ? number(argv[1]) : oexitstatus; - if (funcnest) { - evalskip = SKIPFUNC; - skipcount = 1; - } else { - /* skip the rest of the file */ - evalskip = SKIPFILE; - skipcount = 1; - } + evalskip = SKIPRETURN; + skipcount = 1; return ret; } diff --git a/bin/sh/eval.h b/bin/sh/eval.h index a6e87b237cb..4129757e9f0 100644 --- a/bin/sh/eval.h +++ b/bin/sh/eval.h @@ -67,5 +67,4 @@ extern int skipcount; /* reasons for skipping commands (see comment on breakcmd routine) */ #define SKIPBREAK 1 #define SKIPCONT 2 -#define SKIPFUNC 3 -#define SKIPFILE 4 +#define SKIPRETURN 3 diff --git a/bin/sh/exec.c b/bin/sh/exec.c index 5f78de91a32..9f8e029933e 100644 --- a/bin/sh/exec.c +++ b/bin/sh/exec.c @@ -762,5 +762,7 @@ typecmd_impl(int argc, char **argv, int cmd, const char *path) int typecmd(int argc, char **argv) { + if (argc > 2 && strcmp(argv[1], "--") == 0) + argc--, argv++; return typecmd_impl(argc, argv, TYPECMD_TYPE, bltinlookup("PATH", 1)); } diff --git a/bin/sh/jobs.c b/bin/sh/jobs.c index 89e481297fe..bbb954ac0a1 100644 --- a/bin/sh/jobs.c +++ b/bin/sh/jobs.c @@ -83,13 +83,12 @@ static struct job *bgjob = NULL; /* last background process */ static struct job *jobmru; /* most recently used job list */ static pid_t initialpgrp; /* pgrp of shell on invocation */ #endif -int in_waitcmd = 0; /* are we in waitcmd()? */ -volatile sig_atomic_t breakwaitcmd = 0; /* should wait be terminated? */ static int ttyfd = -1; /* mode flags for dowait */ #define DOWAIT_BLOCK 0x1 /* wait until a child exits */ -#define DOWAIT_SIG 0x2 /* if DOWAIT_BLOCK, abort on signals */ +#define DOWAIT_SIG 0x2 /* if DOWAIT_BLOCK, abort on SIGINT/SIGQUIT */ +#define DOWAIT_SIG_ANY 0x4 /* if DOWAIT_SIG, abort on any signal */ #if JOBS static void restartjob(struct job *); @@ -183,13 +182,14 @@ out: out2fmt_flush("sh: can't access tty; job control turned off\n"); #if JOBS int -fgcmd(int argc __unused, char **argv) +fgcmd(int argc __unused, char **argv __unused) { struct job *jp; pid_t pgrp; int status; - jp = getjob(argv[1]); + nextopt(""); + jp = getjob(*argptr); if (jp->jobctl == 0) error("job not created under job control"); printjobcmd(jp); @@ -210,8 +210,9 @@ bgcmd(int argc, char **argv) { struct job *jp; + nextopt(""); do { - jp = getjob(*++argv); + jp = getjob(*argptr); if (jp->jobctl == 0) error("job not created under job control"); if (jp->state == JOBDONE) @@ -220,7 +221,7 @@ bgcmd(int argc, char **argv) jp->foreground = 0; out1fmt("[%td] ", jp - jobtab + 1); printjobcmd(jp); - } while (--argc > 1); + } while (*argptr != NULL && *++argptr != NULL); return 0; } @@ -482,7 +483,7 @@ waitcmd(int argc __unused, char **argv __unused) static int waitcmdloop(struct job *job) { - int status, retval; + int status, retval, sig; struct job *jp; /* @@ -490,17 +491,12 @@ waitcmdloop(struct job *job) * received. */ - in_waitcmd++; do { if (job != NULL) { - if (job->state) { + if (job->state == JOBDONE) { status = job->ps[job->nprocs - 1].status; if (WIFEXITED(status)) retval = WEXITSTATUS(status); -#if JOBS - else if (WIFSTOPPED(status)) - retval = WSTOPSIG(status) + 128; -#endif else retval = WTERMSIG(status) + 128; if (! iflag || ! job->changed) @@ -510,7 +506,6 @@ waitcmdloop(struct job *job) if (job == bgjob) bgjob = NULL; } - in_waitcmd--; return retval; } } else { @@ -526,7 +521,6 @@ waitcmdloop(struct job *job) } for (jp = jobtab ; ; jp++) { if (jp >= jobtab + njobs) { /* no running procs */ - in_waitcmd--; return 0; } if (jp->used && jp->state == 0) @@ -534,20 +528,22 @@ waitcmdloop(struct job *job) } } } while (dowait(DOWAIT_BLOCK | DOWAIT_SIG, (struct job *)NULL) != -1); - in_waitcmd--; - return pendingsig + 128; + sig = pendingsig_waitcmd; + pendingsig_waitcmd = 0; + return sig + 128; } int -jobidcmd(int argc __unused, char **argv) +jobidcmd(int argc __unused, char **argv __unused) { struct job *jp; int i; - jp = getjob(argv[1]); + nextopt(""); + jp = getjob(*argptr); for (i = 0 ; i < jp->nprocs ; ) { out1fmt("%d", (int)jp->ps[i].pid); out1c(++i < jp->nprocs? ' ' : '\n'); @@ -991,7 +987,8 @@ waitforjob(struct job *jp, int *origstatus) INTOFF; TRACE(("waitforjob(%%%td) called\n", jp - jobtab + 1)); while (jp->state == 0) - if (dowait(DOWAIT_BLOCK | (Tflag ? DOWAIT_SIG : 0), jp) == -1) + if (dowait(DOWAIT_BLOCK | (Tflag ? DOWAIT_SIG | + DOWAIT_SIG_ANY : 0), jp) == -1) dotrap(); #if JOBS if (jp->jobctl) { @@ -1082,12 +1079,17 @@ dowait(int mode, struct job *job) pid = wait3(&status, wflags, (struct rusage *)NULL); TRACE(("wait returns %d, status=%d\n", (int)pid, status)); if (pid == 0 && (mode & DOWAIT_SIG) != 0) { - sigsuspend(&omask); pid = -1; + if (((mode & DOWAIT_SIG_ANY) != 0 ? + pendingsig : pendingsig_waitcmd) != 0) { + errno = EINTR; + break; + } + sigsuspend(&omask); if (int_pending()) break; } - } while (pid == -1 && errno == EINTR && breakwaitcmd == 0); + } while (pid == -1 && errno == EINTR); if (pid == -1 && errno == ECHILD && job != NULL) job->state = JOBDONE; if ((mode & DOWAIT_SIG) != 0) { @@ -1096,11 +1098,6 @@ dowait(int mode, struct job *job) sigprocmask(SIG_SETMASK, &omask, NULL); INTON; } - if (breakwaitcmd != 0) { - breakwaitcmd = 0; - if (pid <= 0) - return -1; - } if (pid <= 0) return pid; INTOFF; diff --git a/bin/sh/jobs.h b/bin/sh/jobs.h index 1570b4695da..892f12916eb 100644 --- a/bin/sh/jobs.h +++ b/bin/sh/jobs.h @@ -83,8 +83,6 @@ enum { }; extern int job_warning; /* user was warned about stopped jobs */ -extern int in_waitcmd; /* are we in waitcmd()? */ -extern volatile sig_atomic_t breakwaitcmd; /* break wait to process traps? */ void setjobctl(int); void showjobs(int, int); diff --git a/bin/sh/main.c b/bin/sh/main.c index 2b99eddaa2d..e4974ea7f45 100644 --- a/bin/sh/main.c +++ b/bin/sh/main.c @@ -185,8 +185,6 @@ reset(void) { reseteval(); resetinput(); - resetparser(); - resetredir(); } /* @@ -233,7 +231,7 @@ cmdloop(int top) popstackmark(&smark); setstackmark(&smark); if (evalskip != 0) { - if (evalskip == SKIPFILE) + if (evalskip == SKIPRETURN) evalskip = 0; break; } diff --git a/bin/sh/parser.c b/bin/sh/parser.c index f079c69c31d..a0def64c639 100644 --- a/bin/sh/parser.c +++ b/bin/sh/parser.c @@ -96,7 +96,7 @@ static struct heredoc *heredoclist; /* list of here documents to read */ static int doprompt; /* if set, prompt the user */ static int needprompt; /* true if interactive and at start of line */ static int lasttoken; /* last token read */ -int tokpushback; /* last token pushed back */ +static int tokpushback; /* last token pushed back */ static char *wordtext; /* text of last word returned by readtoken */ static int checkkwd; static struct nodelist *backquotelist; @@ -108,12 +108,13 @@ static int funclinno; /* line # where the current function started */ static struct parser_temp *parser_temp; -static union node *list(int, int); +static union node *list(int); static union node *andor(void); static union node *pipeline(void); static union node *command(void); static union node *simplecmd(union node **, union node *); static union node *makename(void); +static union node *makebinary(int type, union node *n1, union node *n2); static void parsefname(void); static void parseheredoc(void); static int peektoken(void); @@ -121,6 +122,7 @@ static int readtoken(void); static int xxreadtoken(void); static int readtoken1(int, const char *, const char *, int); static int noexpand(char *); +static void consumetoken(int); static void synexpect(int) __dead2; static void synerror(const char *) __dead2; static void setprompt(int); @@ -210,6 +212,7 @@ parsecmd(int interact) heredoclist = NULL; tokpushback = 0; + checkkwd = 0; doprompt = interact; if (doprompt) setprompt(1); @@ -222,18 +225,18 @@ parsecmd(int interact) if (t == TNL) return NULL; tokpushback++; - return list(1, 1); + return list(1); } static union node * -list(int nlflag, int erflag) +list(int nlflag) { union node *ntop, *n1, *n2, *n3; int tok; checkkwd = CHKNL | CHKKWD | CHKALIAS; - if (!nlflag && !erflag && tokendlist[peektoken()]) + if (!nlflag && tokendlist[peektoken()]) return NULL; ntop = n1 = NULL; for (;;) { @@ -255,17 +258,11 @@ list(int nlflag, int erflag) if (ntop == NULL) ntop = n2; else if (n1 == NULL) { - n1 = (union node *)stalloc(sizeof (struct nbinary)); - n1->type = NSEMI; - n1->nbinary.ch1 = ntop; - n1->nbinary.ch2 = n2; + n1 = makebinary(NSEMI, ntop, n2); ntop = n1; } else { - n3 = (union node *)stalloc(sizeof (struct nbinary)); - n3->type = NSEMI; - n3->nbinary.ch1 = n1->nbinary.ch2; - n3->nbinary.ch2 = n2; + n3 = makebinary(NSEMI, n1->nbinary.ch2, n2); n1->nbinary.ch2 = n3; n1 = n3; } @@ -286,8 +283,7 @@ list(int nlflag, int erflag) tokpushback++; } checkkwd = CHKNL | CHKKWD | CHKALIAS; - if (!nlflag && (erflag ? peektoken() == TEOF : - tokendlist[peektoken()])) + if (!nlflag && tokendlist[peektoken()]) return ntop; break; case TEOF: @@ -297,7 +293,7 @@ list(int nlflag, int erflag) pungetc(); /* push back EOF on input */ return ntop; default: - if (nlflag || erflag) + if (nlflag) synexpect(-1); tokpushback++; return ntop; @@ -310,10 +306,10 @@ list(int nlflag, int erflag) static union node * andor(void) { - union node *n1, *n2, *n3; + union node *n; int t; - n1 = pipeline(); + n = pipeline(); for (;;) { if ((t = readtoken()) == TAND) { t = NAND; @@ -321,14 +317,9 @@ andor(void) t = NOR; } else { tokpushback++; - return n1; + return n; } - n2 = pipeline(); - n3 = (union node *)stalloc(sizeof (struct nbinary)); - n3->type = t; - n3->nbinary.ch1 = n1; - n3->nbinary.ch2 = n2; - n1 = n3; + n = makebinary(t, n, pipeline()); } } @@ -410,49 +401,39 @@ command(void) case TIF: n1 = (union node *)stalloc(sizeof (struct nif)); n1->type = NIF; - if ((n1->nif.test = list(0, 0)) == NULL) + if ((n1->nif.test = list(0)) == NULL) synexpect(-1); - if (readtoken() != TTHEN) - synexpect(TTHEN); - n1->nif.ifpart = list(0, 0); + consumetoken(TTHEN); + n1->nif.ifpart = list(0); n2 = n1; while (readtoken() == TELIF) { n2->nif.elsepart = (union node *)stalloc(sizeof (struct nif)); n2 = n2->nif.elsepart; n2->type = NIF; - if ((n2->nif.test = list(0, 0)) == NULL) + if ((n2->nif.test = list(0)) == NULL) synexpect(-1); - if (readtoken() != TTHEN) - synexpect(TTHEN); - n2->nif.ifpart = list(0, 0); + consumetoken(TTHEN); + n2->nif.ifpart = list(0); } if (lasttoken == TELSE) - n2->nif.elsepart = list(0, 0); + n2->nif.elsepart = list(0); else { n2->nif.elsepart = NULL; tokpushback++; } - if (readtoken() != TFI) - synexpect(TFI); + consumetoken(TFI); checkkwd = CHKKWD | CHKALIAS; break; case TWHILE: - case TUNTIL: { - int got; - n1 = (union node *)stalloc(sizeof (struct nbinary)); - n1->type = (lasttoken == TWHILE)? NWHILE : NUNTIL; - if ((n1->nbinary.ch1 = list(0, 0)) == NULL) + case TUNTIL: + t = lasttoken; + if ((n1 = list(0)) == NULL) synexpect(-1); - if ((got=readtoken()) != TDO) { -TRACE(("expecting DO got %s %s\n", tokname[got], got == TWORD ? wordtext : "")); - synexpect(TDO); - } - n1->nbinary.ch2 = list(0, 0); - if (readtoken() != TDONE) - synexpect(TDONE); + consumetoken(TDO); + n1 = makebinary((t == TWHILE)? NWHILE : NUNTIL, n1, list(0)); + consumetoken(TDONE); checkkwd = CHKKWD | CHKALIAS; break; - } case TFOR: if (readtoken() != TWORD || quoteflag || ! goodname(wordtext)) synerror("Bad for loop variable"); @@ -464,10 +445,7 @@ TRACE(("expecting DO got %s %s\n", tokname[got], got == TWORD ? wordtext : "")); if (lasttoken == TWORD && ! quoteflag && equal(wordtext, "in")) { app = ≈ while (readtoken() == TWORD) { - n2 = (union node *)stalloc(sizeof (struct narg)); - n2->type = NARG; - n2->narg.text = wordtext; - n2->narg.backquote = backquotelist; + n2 = makename(); *app = n2; app = &n2->narg.next; } @@ -499,21 +477,15 @@ TRACE(("expecting DO got %s %s\n", tokname[got], got == TWORD ? wordtext : "")); t = TEND; else synexpect(-1); - n1->nfor.body = list(0, 0); - if (readtoken() != t) - synexpect(t); + n1->nfor.body = list(0); + consumetoken(t); checkkwd = CHKKWD | CHKALIAS; break; case TCASE: n1 = (union node *)stalloc(sizeof (struct ncase)); n1->type = NCASE; - if (readtoken() != TWORD) - synexpect(TWORD); - n1->ncase.expr = n2 = (union node *)stalloc(sizeof (struct narg)); - n2->type = NARG; - n2->narg.text = wordtext; - n2->narg.backquote = backquotelist; - n2->narg.next = NULL; + consumetoken(TWORD); + n1->ncase.expr = makename(); while (readtoken() == TNL); if (lasttoken != TWORD || ! equal(wordtext, "in")) synerror("expecting \"in\""); @@ -526,10 +498,7 @@ TRACE(("expecting DO got %s %s\n", tokname[got], got == TWORD ? wordtext : "")); if (lasttoken == TLP) readtoken(); for (;;) { - *app = ap = (union node *)stalloc(sizeof (struct narg)); - ap->type = NARG; - ap->narg.text = wordtext; - ap->narg.backquote = backquotelist; + *app = ap = makename(); checkkwd = CHKNL | CHKKWD; if (readtoken() != TPIPE) break; @@ -539,7 +508,7 @@ TRACE(("expecting DO got %s %s\n", tokname[got], got == TWORD ? wordtext : "")); ap->narg.next = NULL; if (lasttoken != TRP) synexpect(TRP); - cp->nclist.body = list(0, 0); + cp->nclist.body = list(0); checkkwd = CHKNL | CHKKWD | CHKALIAS; if ((t = readtoken()) != TESAC) { @@ -559,34 +528,31 @@ TRACE(("expecting DO got %s %s\n", tokname[got], got == TWORD ? wordtext : "")); case TLP: n1 = (union node *)stalloc(sizeof (struct nredir)); n1->type = NSUBSHELL; - n1->nredir.n = list(0, 0); + n1->nredir.n = list(0); n1->nredir.redirect = NULL; - if (readtoken() != TRP) - synexpect(TRP); + consumetoken(TRP); checkkwd = CHKKWD | CHKALIAS; is_subshell = 1; break; case TBEGIN: - n1 = list(0, 0); - if (readtoken() != TEND) - synexpect(TEND); + n1 = list(0); + consumetoken(TEND); checkkwd = CHKKWD | CHKALIAS; break; - /* Handle an empty command like other simple commands. */ + /* A simple command must have at least one redirection or word. */ case TBACKGND: case TSEMI: case TAND: case TOR: - /* - * An empty command before a ; doesn't make much sense, and - * should certainly be disallowed in the case of `if ;'. - */ + case TPIPE: + case TENDCASE: + case TFALLTHRU: + case TEOF: + case TNL: + case TRP: if (!redir) synexpect(-1); - case TNL: - case TEOF: case TWORD: - case TRP: tokpushback++; n1 = simplecmd(rpp, redir); return n1; @@ -644,10 +610,7 @@ simplecmd(union node **rpp, union node *redir) for (;;) { checkkwd = savecheckkwd; if (readtoken() == TWORD) { - n = (union node *)stalloc(sizeof (struct narg)); - n->type = NARG; - n->narg.text = wordtext; - n->narg.backquote = backquotelist; + n = makename(); *app = n; app = &n->narg.next; if (savecheckkwd != 0 && !isassignment(wordtext)) @@ -659,8 +622,7 @@ simplecmd(union node **rpp, union node *redir) } else if (lasttoken == TLP && app == &args->narg.next && rpp == orig_rpp) { /* We have a function */ - if (readtoken() != TRP) - synexpect(TRP); + consumetoken(TRP); funclinno = plinno; /* * - Require plain text. @@ -708,6 +670,18 @@ makename(void) return n; } +static union node * +makebinary(int type, union node *n1, union node *n2) +{ + union node *n; + + n = (union node *)stalloc(sizeof (struct nbinary)); + n->type = type; + n->nbinary.ch1 = n1; + n->nbinary.ch2 = n2; + return (n); +} + void fixredir(union node *n, const char *text, int err) { @@ -734,8 +708,7 @@ parsefname(void) { union node *n = redirnode; - if (readtoken() != TWORD) - synexpect(-1); + consumetoken(TWORD); if (n->type == NHERE) { struct heredoc *here = heredoc; struct heredoc *p; @@ -786,11 +759,7 @@ parseheredoc(void) } readtoken1(pgetc(), here->here->type == NHERE? SQSYNTAX : DQSYNTAX, here->eofmark, here->striptabs); - n = (union node *)stalloc(sizeof (struct narg)); - n->narg.type = NARG; - n->narg.next = NULL; - n->narg.text = wordtext; - n->narg.backquote = backquotelist; + n = makename(); here->here->nhere.doc = n; } } @@ -1090,14 +1059,14 @@ done: doprompt = 0; } - n = list(0, oldstyle); + n = list(0); - if (oldstyle) + if (oldstyle) { + if (peektoken() != TEOF) + synexpect(-1); doprompt = saveprompt; - else { - if (readtoken() != TRP) - synexpect(TRP); - } + } else + consumetoken(TRP); (*nlpp)->n = n; if (oldstyle) { @@ -1819,14 +1788,6 @@ parsearith: { } /* end of readtoken */ -void -resetparser(void) -{ - tokpushback = 0; - checkkwd = 0; -} - - /* * Returns true if the text contains nothing to expand (no dollar signs * or backquotes). @@ -1888,6 +1849,14 @@ isassignment(const char *p) } +static void +consumetoken(int token) +{ + if (readtoken() != token) + synexpect(token); +} + + /* * Called when an unexpected token is read during the parse. The argument * is the token that is expected, or -1 if more than one type of token can diff --git a/bin/sh/parser.h b/bin/sh/parser.h index 92b2e37b048..d500d2fd5b8 100644 --- a/bin/sh/parser.h +++ b/bin/sh/parser.h @@ -68,18 +68,15 @@ /* * NEOF is returned by parsecmd when it encounters an end of file. It - * must be distinct from NULL, so we use the address of a variable that - * happens to be handy. + * must be distinct from NULL. */ -extern int tokpushback; -#define NEOF ((union node *)&tokpushback) +#define NEOF ((union node *)-1) extern int whichprompt; /* 1 == PS1, 2 == PS2 */ extern const char *const parsekwd[]; union node *parsecmd(int); void fixredir(union node *, const char *, int); -void resetparser(void); int goodname(const char *); int isassignment(const char *); char *getprompt(void *); diff --git a/bin/sh/redir.c b/bin/sh/redir.c index dde4384b401..932554578e8 100644 --- a/bin/sh/redir.c +++ b/bin/sh/redir.c @@ -319,18 +319,6 @@ popredir(void) INTON; } -/* - * Undo all redirections. Called on error or interrupt. - */ - -void -resetredir(void) -{ - while (redirlist) - popredir(); -} - - /* Return true if fd 0 has already been redirected at least once. */ int fd0_redirected_p(void) diff --git a/bin/sh/redir.h b/bin/sh/redir.h index d012440a438..ad44c4eddd8 100644 --- a/bin/sh/redir.h +++ b/bin/sh/redir.h @@ -40,7 +40,6 @@ union node; void redirect(union node *, int); void popredir(void); -void resetredir(void); int fd0_redirected_p(void); void clearredir(void); diff --git a/bin/sh/sh.1 b/bin/sh/sh.1 index 174788ddc7c..ea1d899ed8e 100644 --- a/bin/sh/sh.1 +++ b/bin/sh/sh.1 @@ -1145,8 +1145,10 @@ command is .Pp .D1 Ic return Op Ar exitstatus .Pp -It terminates the current executional scope, returning from the previous -nested function, sourced script, or shell instance, in that order. +It terminates the current executional scope, returning from the closest +nested function or sourced script; +if no function or sourced script is being executed, +it exits the shell instance. The .Ic return command is implemented as a special built-in command. diff --git a/bin/sh/trap.c b/bin/sh/trap.c index 14112890c2b..3fc85668c64 100644 --- a/bin/sh/trap.c +++ b/bin/sh/trap.c @@ -74,6 +74,7 @@ __FBSDID("$FreeBSD$"); static char sigmode[NSIG]; /* current value of signal */ volatile sig_atomic_t pendingsig; /* indicates some signal received */ +volatile sig_atomic_t pendingsig_waitcmd; /* indicates SIGINT/SIGQUIT received */ int in_dotrap; /* do we execute in a trap handler? */ static char *volatile trap[NSIG]; /* trap handler commands */ static volatile sig_atomic_t gotsig[NSIG]; @@ -389,23 +390,13 @@ onsig(int signo) } /* If we are currently in a wait builtin, prepare to break it */ - if ((signo == SIGINT || signo == SIGQUIT) && in_waitcmd != 0) { - breakwaitcmd = 1; - pendingsig = signo; - } + if (signo == SIGINT || signo == SIGQUIT) + pendingsig_waitcmd = signo; if (trap[signo] != NULL && trap[signo][0] != '\0' && (signo != SIGCHLD || !ignore_sigchld)) { gotsig[signo] = 1; pendingsig = signo; - - /* - * If a trap is set, not ignored and not the null command, we - * need to make sure traps are executed even when a child - * blocks signals. - */ - if (Tflag && !(trap[signo][0] == ':' && trap[signo][1] == '\0')) - breakwaitcmd = 1; } #ifndef NO_HISTORY @@ -428,6 +419,7 @@ dotrap(void) in_dotrap++; for (;;) { pendingsig = 0; + pendingsig_waitcmd = 0; for (i = 1; i < NSIG; i++) { if (gotsig[i]) { gotsig[i] = 0; diff --git a/bin/sh/trap.h b/bin/sh/trap.h index 0a05d8d0ad2..a9622514c0b 100644 --- a/bin/sh/trap.h +++ b/bin/sh/trap.h @@ -34,6 +34,7 @@ */ extern volatile sig_atomic_t pendingsig; +extern volatile sig_atomic_t pendingsig_waitcmd; extern int in_dotrap; extern volatile sig_atomic_t gotwinch; diff --git a/bin/sh/var.c b/bin/sh/var.c index 0de13feccfb..c20d0326370 100644 --- a/bin/sh/var.c +++ b/bin/sh/var.c @@ -710,6 +710,7 @@ localcmd(int argc __unused, char **argv __unused) { char *name; + nextopt(""); if (! in_function()) error("Not in a function"); while ((name = *argptr++) != NULL) { diff --git a/cddl/contrib/opensolaris/cmd/zpool/zpool_main.c b/cddl/contrib/opensolaris/cmd/zpool/zpool_main.c index bfb71145516..0dcf11b9204 100644 --- a/cddl/contrib/opensolaris/cmd/zpool/zpool_main.c +++ b/cddl/contrib/opensolaris/cmd/zpool/zpool_main.c @@ -25,6 +25,7 @@ * Copyright (c) 2012 by Delphix. All rights reserved. * Copyright (c) 2012 by Frederik Wessels. All rights reserved. * Copyright (c) 2012 Martin Matuska . All rights reserved. + * Copyright (c) 2013 by Prasad Joshi (sTec). All rights reserved. */ #include @@ -1295,12 +1296,13 @@ print_status_config(zpool_handle_t *zhp, const char *name, nvlist_t *nv, int namewidth, int depth, boolean_t isspare) { nvlist_t **child; - uint_t c, children; + uint_t c, vsc, children; pool_scan_stat_t *ps = NULL; vdev_stat_t *vs; char rbuf[6], wbuf[6], cbuf[6]; char *vname; uint64_t notpresent; + uint64_t ashift; spare_cbdata_t cb; const char *state; @@ -1309,7 +1311,7 @@ print_status_config(zpool_handle_t *zhp, const char *name, nvlist_t *nv, children = 0; verify(nvlist_lookup_uint64_array(nv, ZPOOL_CONFIG_VDEV_STATS, - (uint64_t **)&vs, &c) == 0); + (uint64_t **)&vs, &vsc) == 0); state = zpool_state_to_name(vs->vs_state, vs->vs_aux); if (isspare) { @@ -1363,6 +1365,10 @@ print_status_config(zpool_handle_t *zhp, const char *name, nvlist_t *nv, (void) printf(gettext("unsupported feature(s)")); break; + case VDEV_AUX_ASHIFT_TOO_BIG: + (void) printf(gettext("unsupported minimum blocksize")); + break; + case VDEV_AUX_SPARED: verify(nvlist_lookup_uint64(nv, ZPOOL_CONFIG_GUID, &cb.cb_guid) == 0); @@ -1405,6 +1411,12 @@ print_status_config(zpool_handle_t *zhp, const char *name, nvlist_t *nv, (void) printf(gettext("corrupted data")); break; } + } else if (children == 0 && !isspare && + VDEV_STAT_VALID(vs_physical_ashift, vsc) && + vs->vs_configured_ashift < vs->vs_physical_ashift) { + (void) printf( + gettext(" block size: %dB configured, %dB native"), + 1 << vs->vs_configured_ashift, 1 << vs->vs_physical_ashift); } (void) nvlist_lookup_uint64_array(nv, ZPOOL_CONFIG_SCAN_STATS, @@ -2117,8 +2129,10 @@ zpool_do_import(int argc, char **argv) errno = 0; searchguid = strtoull(argv[0], &endptr, 10); - if (errno != 0 || *endptr != '\0') + if (errno != 0 || *endptr != '\0') { searchname = argv[0]; + searchguid = 0; + } found_config = NULL; /* @@ -4268,6 +4282,15 @@ status_callback(zpool_handle_t *zhp, void *data) "'zpool clear'.\n")); break; + case ZPOOL_STATUS_NON_NATIVE_ASHIFT: + (void) printf(gettext("status: One or more devices are " + "configured to use a non-native block size.\n" + "\tExpect reduced performance.\n")); + (void) printf(gettext("action: Replace affected devices with " + "devices that support the\n\tconfigured block size, or " + "migrate data to a properly configured\n\tpool.\n")); + break; + default: /* * The remaining errors can't actually be generated, yet. diff --git a/cddl/contrib/opensolaris/cmd/ztest/ztest.c b/cddl/contrib/opensolaris/cmd/ztest/ztest.c index eabdd4ed22f..b6dcf09963a 100644 --- a/cddl/contrib/opensolaris/cmd/ztest/ztest.c +++ b/cddl/contrib/opensolaris/cmd/ztest/ztest.c @@ -20,7 +20,7 @@ */ /* * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2012 by Delphix. All rights reserved. + * Copyright (c) 2013 by Delphix. All rights reserved. * Copyright 2011 Nexenta Systems, Inc. All rights reserved. * Copyright (c) 2012 Martin Matuska . All rights reserved. * Copyright (c) 2013 Steven Hartland. All rights reserved. @@ -186,6 +186,7 @@ static const ztest_shared_opts_t ztest_opts_defaults = { extern uint64_t metaslab_gang_bang; extern uint64_t metaslab_df_alloc_threshold; +extern uint64_t zfs_deadman_synctime; static ztest_shared_opts_t *ztest_shared_opts; static ztest_shared_opts_t ztest_opts; @@ -365,7 +366,7 @@ ztest_info_t ztest_info[] = { { ztest_fault_inject, 1, &zopt_sometimes }, { ztest_ddt_repair, 1, &zopt_sometimes }, { ztest_dmu_snapshot_hold, 1, &zopt_sometimes }, - { ztest_reguid, 1, &zopt_sometimes }, + { ztest_reguid, 1, &zopt_rarely }, { ztest_spa_rename, 1, &zopt_rarely }, { ztest_scrub, 1, &zopt_rarely }, { ztest_spa_upgrade, 1, &zopt_rarely }, @@ -768,6 +769,16 @@ ztest_kill(ztest_shared_t *zs) { zs->zs_alloc = metaslab_class_get_alloc(spa_normal_class(ztest_spa)); zs->zs_space = metaslab_class_get_space(spa_normal_class(ztest_spa)); + + /* + * Before we kill off ztest, make sure that the config is updated. + * See comment above spa_config_sync(). + */ + mutex_enter(&spa_namespace_lock); + spa_config_sync(ztest_spa, B_FALSE, B_FALSE); + mutex_exit(&spa_namespace_lock); + + zfs_dbgmsg_print(FTAG); (void) kill(getpid(), SIGKILL); } @@ -2732,7 +2743,7 @@ ztest_vdev_attach_detach(ztest_ds_t *zd, uint64_t id) uint64_t leaf, top; uint64_t ashift = ztest_get_ashift(); uint64_t oldguid, pguid; - size_t oldsize, newsize; + uint64_t oldsize, newsize; char oldpath[MAXPATHLEN], newpath[MAXPATHLEN]; int replacing; int oldvd_has_siblings = B_FALSE; @@ -2891,8 +2902,8 @@ ztest_vdev_attach_detach(ztest_ds_t *zd, uint64_t id) if (error != expected_error && expected_error != EBUSY) { fatal(0, "attach (%s %llu, %s %llu, %d) " "returned %d, expected %d", - oldpath, (longlong_t)oldsize, newpath, - (longlong_t)newsize, replacing, error, expected_error); + oldpath, oldsize, newpath, + newsize, replacing, error, expected_error); } VERIFY(mutex_unlock(&ztest_vdev_lock) == 0); @@ -3606,6 +3617,9 @@ ztest_dmu_read_write(ztest_ds_t *zd, uint64_t id) else dmu_tx_hold_write(tx, bigobj, bigoff, bigsize); + /* This accounts for setting the checksum/compression. */ + dmu_tx_hold_bonus(tx, bigobj); + txg = ztest_tx_assign(tx, TXG_MIGHTWAIT, FTAG); if (txg == 0) { umem_free(packbuf, packsize); @@ -4755,6 +4769,14 @@ ztest_fault_inject(ztest_ds_t *zd, uint64_t id) ASSERT(leaves >= 1); + /* + * Grab the name lock as reader. There are some operations + * which don't like to have their vdevs changed while + * they are in progress (i.e. spa_change_guid). Those + * operations will have grabbed the name lock as writer. + */ + (void) rw_rdlock(&ztest_name_lock); + /* * We need SCL_STATE here because we're going to look at vd0->vdev_tsd. */ @@ -4784,7 +4806,14 @@ ztest_fault_inject(ztest_ds_t *zd, uint64_t id) if (vd0 != NULL && vd0->vdev_top->vdev_islog) islog = B_TRUE; - if (vd0 != NULL && maxfaults != 1) { + /* + * If the top-level vdev needs to be resilvered + * then we only allow faults on the device that is + * resilvering. + */ + if (vd0 != NULL && maxfaults != 1 && + (!vdev_resilver_needed(vd0->vdev_top, NULL, NULL) || + vd0->vdev_resilver_txg != 0)) { /* * Make vd0 explicitly claim to be unreadable, * or unwriteable, or reach behind its back @@ -4815,6 +4844,7 @@ ztest_fault_inject(ztest_ds_t *zd, uint64_t id) if (sav->sav_count == 0) { spa_config_exit(spa, SCL_STATE, FTAG); + (void) rw_unlock(&ztest_name_lock); return; } vd0 = sav->sav_vdevs[ztest_random(sav->sav_count)]; @@ -4828,6 +4858,7 @@ ztest_fault_inject(ztest_ds_t *zd, uint64_t id) } spa_config_exit(spa, SCL_STATE, FTAG); + (void) rw_unlock(&ztest_name_lock); /* * If we can tolerate two or more faults, or we're dealing @@ -5293,16 +5324,33 @@ static void * ztest_deadman_thread(void *arg) { ztest_shared_t *zs = arg; - int grace = 300; - hrtime_t delta; + spa_t *spa = ztest_spa; + hrtime_t delta, total = 0; - delta = (zs->zs_thread_stop - zs->zs_thread_start) / NANOSEC + grace; + for (;;) { + delta = (zs->zs_thread_stop - zs->zs_thread_start) / + NANOSEC + zfs_deadman_synctime; - (void) poll(NULL, 0, (int)(1000 * delta)); + (void) poll(NULL, 0, (int)(1000 * delta)); - fatal(0, "failed to complete within %d seconds of deadline", grace); + /* + * If the pool is suspended then fail immediately. Otherwise, + * check to see if the pool is making any progress. If + * vdev_deadman() discovers that there hasn't been any recent + * I/Os then it will end up aborting the tests. + */ + if (spa_suspended(spa)) { + fatal(0, "aborting test after %llu seconds because " + "pool has transitioned to a suspended state.", + zfs_deadman_synctime); + return (NULL); + } + vdev_deadman(spa->spa_root_vdev); - return (NULL); + total += zfs_deadman_synctime; + (void) printf("ztest has been running for %lld seconds\n", + total); + } } static void @@ -5616,6 +5664,7 @@ ztest_run(ztest_shared_t *zs) zs->zs_alloc = metaslab_class_get_alloc(spa_normal_class(spa)); zs->zs_space = metaslab_class_get_space(spa_normal_class(spa)); + zfs_dbgmsg_print(FTAG); umem_free(tid, ztest_opts.zo_threads * sizeof (thread_t)); @@ -6031,6 +6080,7 @@ main(int argc, char **argv) (void) setvbuf(stdout, NULL, _IOLBF, 0); dprintf_setup(&argc, argv); + zfs_deadman_synctime = 300; ztest_fd_rand = open("/dev/urandom", O_RDONLY); ASSERT3S(ztest_fd_rand, >=, 0); diff --git a/cddl/contrib/opensolaris/common/ctf/ctf_create.c b/cddl/contrib/opensolaris/common/ctf/ctf_create.c index a4f3df34e8c..239d166f441 100644 --- a/cddl/contrib/opensolaris/common/ctf/ctf_create.c +++ b/cddl/contrib/opensolaris/common/ctf/ctf_create.c @@ -24,13 +24,15 @@ * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ - -#pragma ident "%Z%%M% %I% %E% SMI" +/* + * Copyright (c) 2013, Joyent, Inc. All rights reserved. + */ #include #include #include #include +#include /* * This static string is used as the template for initially populating a @@ -166,6 +168,51 @@ ctf_copy_membnames(ctf_dtdef_t *dtd, uchar_t *s) return (s); } +/* + * Only types of dyanmic CTF containers contain reference counts. These + * containers are marked RD/WR. Because of that we basically make this a no-op + * for compatability with non-dynamic CTF sections. This is also a no-op for + * types which are not dynamic types. It is the responsibility of the caller to + * make sure it is a valid type. We help that caller out on debug builds. + * + * Note that the reference counts are not maintained for types that are not + * within this container. In other words if we have a type in a parent, that + * will not have its reference count increased. On the flip side, the parent + * will not be allowed to remove dynamic types if it has children. + */ +static void +ctf_ref_inc(ctf_file_t *fp, ctf_id_t tid) +{ + ctf_dtdef_t *dtd = ctf_dtd_lookup(fp, tid); + + if (dtd == NULL) + return; + + if (!(fp->ctf_flags & LCTF_RDWR)) + return; + + dtd->dtd_ref++; +} + +/* + * Just as with ctf_ref_inc, this is a no-op on non-writeable containers and the + * caller should ensure that this is already a valid type. + */ +static void +ctf_ref_dec(ctf_file_t *fp, ctf_id_t tid) +{ + ctf_dtdef_t *dtd = ctf_dtd_lookup(fp, tid); + + if (dtd == NULL) + return; + + if (!(fp->ctf_flags & LCTF_RDWR)) + return; + + ASSERT(dtd->dtd_ref >= 1); + dtd->dtd_ref--; +} + /* * If the specified CTF container is writable and has been modified, reload * this container with the updated type definitions. In order to make this @@ -180,6 +227,10 @@ ctf_copy_membnames(ctf_dtdef_t *dtd, uchar_t *s) * ctf_bufopen() will return a new ctf_file_t, but we want to keep the fp * constant for the caller, so after ctf_bufopen() returns, we use bcopy to * swap the interior of the old and new ctf_file_t's, and then free the old. + * + * Note that the lists of dynamic types stays around and the resulting container + * is still writeable. Furthermore, the reference counts that are on the dtd's + * are still valid. */ int ctf_update(ctf_file_t *fp) @@ -432,6 +483,7 @@ ctf_dtd_delete(ctf_file_t *fp, ctf_dtdef_t *dtd) ctf_dtdef_t *p, **q = &fp->ctf_dthash[h]; ctf_dmdef_t *dmd, *nmd; size_t len; + int kind, i; for (p = *q; p != NULL; p = p->dtd_hash) { if (p != dtd) @@ -443,7 +495,8 @@ ctf_dtd_delete(ctf_file_t *fp, ctf_dtdef_t *dtd) if (p != NULL) *q = p->dtd_hash; - switch (CTF_INFO_KIND(dtd->dtd_data.ctt_info)) { + kind = CTF_INFO_KIND(dtd->dtd_data.ctt_info); + switch (kind) { case CTF_K_STRUCT: case CTF_K_UNION: case CTF_K_ENUM: @@ -454,14 +507,33 @@ ctf_dtd_delete(ctf_file_t *fp, ctf_dtdef_t *dtd) ctf_free(dmd->dmd_name, len); fp->ctf_dtstrlen -= len; } + if (kind != CTF_K_ENUM) + ctf_ref_dec(fp, dmd->dmd_type); nmd = ctf_list_next(dmd); ctf_free(dmd, sizeof (ctf_dmdef_t)); } break; case CTF_K_FUNCTION: + ctf_ref_dec(fp, dtd->dtd_data.ctt_type); + for (i = 0; i < CTF_INFO_VLEN(dtd->dtd_data.ctt_info); i++) + if (dtd->dtd_u.dtu_argv[i] != 0) + ctf_ref_dec(fp, dtd->dtd_u.dtu_argv[i]); ctf_free(dtd->dtd_u.dtu_argv, sizeof (ctf_id_t) * CTF_INFO_VLEN(dtd->dtd_data.ctt_info)); break; + case CTF_K_ARRAY: + ctf_ref_dec(fp, dtd->dtd_u.dtu_arr.ctr_contents); + ctf_ref_dec(fp, dtd->dtd_u.dtu_arr.ctr_index); + break; + case CTF_K_TYPEDEF: + ctf_ref_dec(fp, dtd->dtd_data.ctt_type); + break; + case CTF_K_POINTER: + case CTF_K_VOLATILE: + case CTF_K_CONST: + case CTF_K_RESTRICT: + ctf_ref_dec(fp, dtd->dtd_data.ctt_type); + break; } if (dtd->dtd_name) { @@ -495,7 +567,9 @@ ctf_dtd_lookup(ctf_file_t *fp, ctf_id_t type) * Discard all of the dynamic type definitions that have been added to the * container since the last call to ctf_update(). We locate such types by * scanning the list and deleting elements that have type IDs greater than - * ctf_dtoldid, which is set by ctf_update(), above. + * ctf_dtoldid, which is set by ctf_update(), above. Note that to work properly + * with our reference counting schemes, we must delete the dynamic list in + * reverse. */ int ctf_discard(ctf_file_t *fp) @@ -508,11 +582,11 @@ ctf_discard(ctf_file_t *fp) if (!(fp->ctf_flags & LCTF_DIRTY)) return (0); /* no update required */ - for (dtd = ctf_list_next(&fp->ctf_dtdefs); dtd != NULL; dtd = ntd) { + for (dtd = ctf_list_prev(&fp->ctf_dtdefs); dtd != NULL; dtd = ntd) { if (dtd->dtd_type <= fp->ctf_dtoldid) continue; /* skip types that have been committed */ - ntd = ctf_list_next(dtd); + ntd = ctf_list_prev(dtd); ctf_dtd_delete(fp, dtd); } @@ -614,6 +688,8 @@ ctf_add_reftype(ctf_file_t *fp, uint_t flag, ctf_id_t ref, uint_t kind) if ((type = ctf_add_generic(fp, flag, NULL, &dtd)) == CTF_ERR) return (CTF_ERR); /* errno is set for us */ + ctf_ref_inc(fp, ref); + dtd->dtd_data.ctt_info = CTF_TYPE_INFO(kind, flag, 0); dtd->dtd_data.ctt_type = (ushort_t)ref; @@ -645,16 +721,29 @@ ctf_add_array(ctf_file_t *fp, uint_t flag, const ctf_arinfo_t *arp) { ctf_dtdef_t *dtd; ctf_id_t type; + ctf_file_t *fpd; if (arp == NULL) return (ctf_set_errno(fp, EINVAL)); + fpd = fp; + if (ctf_lookup_by_id(&fpd, arp->ctr_contents) == NULL && + ctf_dtd_lookup(fp, arp->ctr_contents) == NULL) + return (ctf_set_errno(fp, ECTF_BADID)); + + fpd = fp; + if (ctf_lookup_by_id(&fpd, arp->ctr_index) == NULL && + ctf_dtd_lookup(fp, arp->ctr_index) == NULL) + return (ctf_set_errno(fp, ECTF_BADID)); + if ((type = ctf_add_generic(fp, flag, NULL, &dtd)) == CTF_ERR) return (CTF_ERR); /* errno is set for us */ dtd->dtd_data.ctt_info = CTF_TYPE_INFO(CTF_K_ARRAY, flag, 0); dtd->dtd_data.ctt_size = 0; dtd->dtd_u.dtu_arr = *arp; + ctf_ref_inc(fp, arp->ctr_contents); + ctf_ref_inc(fp, arp->ctr_index); return (type); } @@ -662,6 +751,7 @@ ctf_add_array(ctf_file_t *fp, uint_t flag, const ctf_arinfo_t *arp) int ctf_set_array(ctf_file_t *fp, ctf_id_t type, const ctf_arinfo_t *arp) { + ctf_file_t *fpd; ctf_dtdef_t *dtd = ctf_dtd_lookup(fp, type); if (!(fp->ctf_flags & LCTF_RDWR)) @@ -670,8 +760,22 @@ ctf_set_array(ctf_file_t *fp, ctf_id_t type, const ctf_arinfo_t *arp) if (dtd == NULL || CTF_INFO_KIND(dtd->dtd_data.ctt_info) != CTF_K_ARRAY) return (ctf_set_errno(fp, ECTF_BADID)); + fpd = fp; + if (ctf_lookup_by_id(&fpd, arp->ctr_contents) == NULL && + ctf_dtd_lookup(fp, arp->ctr_contents) == NULL) + return (ctf_set_errno(fp, ECTF_BADID)); + + fpd = fp; + if (ctf_lookup_by_id(&fpd, arp->ctr_index) == NULL && + ctf_dtd_lookup(fp, arp->ctr_index) == NULL) + return (ctf_set_errno(fp, ECTF_BADID)); + + ctf_ref_dec(fp, dtd->dtd_u.dtu_arr.ctr_contents); + ctf_ref_dec(fp, dtd->dtd_u.dtu_arr.ctr_index); fp->ctf_flags |= LCTF_DIRTY; dtd->dtd_u.dtu_arr = *arp; + ctf_ref_inc(fp, arp->ctr_contents); + ctf_ref_inc(fp, arp->ctr_index); return (0); } @@ -683,7 +787,9 @@ ctf_add_function(ctf_file_t *fp, uint_t flag, ctf_dtdef_t *dtd; ctf_id_t type; uint_t vlen; + int i; ctf_id_t *vdat = NULL; + ctf_file_t *fpd; if (ctc == NULL || (ctc->ctc_flags & ~CTF_FUNC_VARARG) != 0 || (ctc->ctc_argc != 0 && argv == NULL)) @@ -696,6 +802,18 @@ ctf_add_function(ctf_file_t *fp, uint_t flag, if (vlen > CTF_MAX_VLEN) return (ctf_set_errno(fp, EOVERFLOW)); + fpd = fp; + if (ctf_lookup_by_id(&fpd, ctc->ctc_return) == NULL && + ctf_dtd_lookup(fp, ctc->ctc_return) == NULL) + return (ctf_set_errno(fp, ECTF_BADID)); + + for (i = 0; i < ctc->ctc_argc; i++) { + fpd = fp; + if (ctf_lookup_by_id(&fpd, argv[i]) == NULL && + ctf_dtd_lookup(fp, argv[i]) == NULL) + return (ctf_set_errno(fp, ECTF_BADID)); + } + if (vlen != 0 && (vdat = ctf_alloc(sizeof (ctf_id_t) * vlen)) == NULL) return (ctf_set_errno(fp, EAGAIN)); @@ -707,6 +825,10 @@ ctf_add_function(ctf_file_t *fp, uint_t flag, dtd->dtd_data.ctt_info = CTF_TYPE_INFO(CTF_K_FUNCTION, flag, vlen); dtd->dtd_data.ctt_type = (ushort_t)ctc->ctc_return; + ctf_ref_inc(fp, ctc->ctc_return); + for (i = 0; i < ctc->ctc_argc; i++) + ctf_ref_inc(fp, argv[i]); + bcopy(argv, vdat, sizeof (ctf_id_t) * ctc->ctc_argc); if (ctc->ctc_flags & CTF_FUNC_VARARG) vdat[vlen - 1] = 0; /* add trailing zero to indicate varargs */ @@ -825,8 +947,11 @@ ctf_add_typedef(ctf_file_t *fp, uint_t flag, const char *name, ctf_id_t ref) { ctf_dtdef_t *dtd; ctf_id_t type; + ctf_file_t *fpd; - if (ref == CTF_ERR || ref < 0 || ref > CTF_MAX_TYPE) + fpd = fp; + if (ref == CTF_ERR || (ctf_lookup_by_id(&fpd, ref) == NULL && + ctf_dtd_lookup(fp, ref) == NULL)) return (ctf_set_errno(fp, EINVAL)); if ((type = ctf_add_generic(fp, flag, name, &dtd)) == CTF_ERR) @@ -834,6 +959,7 @@ ctf_add_typedef(ctf_file_t *fp, uint_t flag, const char *name, ctf_id_t ref) dtd->dtd_data.ctt_info = CTF_TYPE_INFO(CTF_K_TYPEDEF, flag, 0); dtd->dtd_data.ctt_type = (ushort_t)ref; + ctf_ref_inc(fp, ref); return (type); } @@ -1008,6 +1134,45 @@ ctf_add_member(ctf_file_t *fp, ctf_id_t souid, const char *name, ctf_id_t type) if (s != NULL) fp->ctf_dtstrlen += strlen(s) + 1; + ctf_ref_inc(fp, type); + fp->ctf_flags |= LCTF_DIRTY; + return (0); +} + +/* + * This removes a type from the dynamic section. This will fail if the type is + * referenced by another type. Note that the CTF ID is never reused currently by + * CTF. Note that if this container is a parent container then we just outright + * refuse to remove the type. There currently is no notion of searching for the + * ctf_dtdef_t in parent containers. If there is, then this constraint could + * become finer grained. + */ +int +ctf_delete_type(ctf_file_t *fp, ctf_id_t type) +{ + ctf_file_t *fpd; + ctf_dtdef_t *dtd = ctf_dtd_lookup(fp, type); + + if (!(fp->ctf_flags & LCTF_RDWR)) + return (ctf_set_errno(fp, ECTF_RDONLY)); + + /* + * We want to give as useful an errno as possible. That means that we + * want to distinguish between a type which does not exist and one for + * which the type is not dynamic. + */ + fpd = fp; + if (ctf_lookup_by_id(&fpd, type) == NULL && + ctf_dtd_lookup(fp, type) == NULL) + return (CTF_ERR); /* errno is set for us */ + + if (dtd == NULL) + return (ctf_set_errno(fp, ECTF_NOTDYN)); + + if (dtd->dtd_ref != 0 || fp->ctf_refcnt > 1) + return (ctf_set_errno(fp, ECTF_REFERENCED)); + + ctf_dtd_delete(fp, dtd); fp->ctf_flags |= LCTF_DIRTY; return (0); } @@ -1103,6 +1268,9 @@ ctf_add_type(ctf_file_t *dst_fp, ctf_file_t *src_fp, ctf_id_t src_type) ctf_hash_t *hp; ctf_helem_t *hep; + if (dst_fp == src_fp) + return (src_type); + if (!(dst_fp->ctf_flags & LCTF_RDWR)) return (ctf_set_errno(dst_fp, ECTF_RDONLY)); @@ -1313,6 +1481,14 @@ ctf_add_type(ctf_file_t *dst_fp, ctf_file_t *src_fp, ctf_id_t src_type) if (errs) return (CTF_ERR); /* errno is set for us */ + + /* + * Now that we know that we can't fail, we go through and bump + * all the reference counts on the member types. + */ + for (dmd = ctf_list_next(&dtd->dtd_u.dtu_members); + dmd != NULL; dmd = ctf_list_next(dmd)) + ctf_ref_inc(dst_fp, dmd->dmd_type); break; } diff --git a/cddl/contrib/opensolaris/common/ctf/ctf_error.c b/cddl/contrib/opensolaris/common/ctf/ctf_error.c index 888c6c848b3..fe3d0de0cb9 100644 --- a/cddl/contrib/opensolaris/common/ctf/ctf_error.c +++ b/cddl/contrib/opensolaris/common/ctf/ctf_error.c @@ -23,8 +23,9 @@ * Copyright 2003 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ - -#pragma ident "%Z%%M% %I% %E% SMI" +/* + * Copyright (c) 2012, Joyent, Inc. + */ #include @@ -73,6 +74,8 @@ static const char *const _ctf_errlist[] = { "Limit on number of dynamic types reached", /* ECTF_FULL */ "Duplicate member name definition", /* ECTF_DUPMEMBER */ "Conflicting type is already defined", /* ECTF_CONFLICT */ + "Type has outstanding references", /* ECTF_REFERENCED */ + "Type is not a dynamic type" /* ECTF_NOTDYN */ }; static const int _ctf_nerr = sizeof (_ctf_errlist) / sizeof (_ctf_errlist[0]); diff --git a/cddl/contrib/opensolaris/common/ctf/ctf_impl.h b/cddl/contrib/opensolaris/common/ctf/ctf_impl.h index 99990806a32..f56fa6a0054 100644 --- a/cddl/contrib/opensolaris/common/ctf/ctf_impl.h +++ b/cddl/contrib/opensolaris/common/ctf/ctf_impl.h @@ -24,12 +24,13 @@ * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ +/* + * Copyright (c) 2012, Joyent, Inc. All rights reserved. + */ #ifndef _CTF_IMPL_H #define _CTF_IMPL_H -#pragma ident "%Z%%M% %I% %E% SMI" - #include #include #include @@ -149,6 +150,7 @@ typedef struct ctf_dtdef { char *dtd_name; /* name associated with definition (if any) */ ctf_id_t dtd_type; /* type identifier for this definition */ ctf_type_t dtd_data; /* type node (see ) */ + int dtd_ref; /* recfount for dyanmic types */ union { ctf_list_t dtu_members; /* struct, union, or enum */ ctf_arinfo_t dtu_arr; /* array */ @@ -269,7 +271,9 @@ enum { ECTF_DTFULL, /* CTF type is full (no more members allowed) */ ECTF_FULL, /* CTF container is full */ ECTF_DUPMEMBER, /* duplicate member name definition */ - ECTF_CONFLICT /* conflicting type definition present */ + ECTF_CONFLICT, /* conflicting type definition present */ + ECTF_REFERENCED, /* type has outstanding references */ + ECTF_NOTDYN /* type is not a dynamic type */ }; extern ssize_t ctf_get_ctt_size(const ctf_file_t *, const ctf_type_t *, diff --git a/cddl/contrib/opensolaris/common/ctf/ctf_open.c b/cddl/contrib/opensolaris/common/ctf/ctf_open.c index e49a4cb3293..2148389fff8 100644 --- a/cddl/contrib/opensolaris/common/ctf/ctf_open.c +++ b/cddl/contrib/opensolaris/common/ctf/ctf_open.c @@ -24,8 +24,9 @@ * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ - -#pragma ident "%Z%%M% %I% %E% SMI" +/* + * Copyright (c) 2012, Joyent, Inc. All rights reserved. + */ #include #include @@ -810,8 +811,12 @@ ctf_close(ctf_file_t *fp) if (fp->ctf_parent != NULL) ctf_close(fp->ctf_parent); - for (dtd = ctf_list_next(&fp->ctf_dtdefs); dtd != NULL; dtd = ntd) { - ntd = ctf_list_next(dtd); + /* + * Note, to work properly with reference counting on the dynamic + * section, we must delete the list in reverse. + */ + for (dtd = ctf_list_prev(&fp->ctf_dtdefs); dtd != NULL; dtd = ntd) { + ntd = ctf_list_prev(dtd); ctf_dtd_delete(fp, dtd); } diff --git a/cddl/contrib/opensolaris/lib/libctf/common/ctf_lib.c b/cddl/contrib/opensolaris/lib/libctf/common/ctf_lib.c index 6cd003663ca..0df44cfda93 100644 --- a/cddl/contrib/opensolaris/lib/libctf/common/ctf_lib.c +++ b/cddl/contrib/opensolaris/lib/libctf/common/ctf_lib.c @@ -216,6 +216,7 @@ ctf_fdopen(int fd, int *errp) { ctf_sect_t ctfsect, symsect, strsect; ctf_file_t *fp = NULL; + size_t shstrndx, shnum; struct stat64 st; ssize_t nbytes; @@ -278,11 +279,10 @@ ctf_fdopen(int fd, int *errp) #else uchar_t order = ELFDATA2LSB; #endif - GElf_Half i, n; GElf_Shdr *sp; void *strs_map; - size_t strs_mapsz; + size_t strs_mapsz, i; char *strs; if (hdr.e32.e_ident[EI_DATA] != order) @@ -298,11 +298,38 @@ ctf_fdopen(int fd, int *errp) ehdr_to_gelf(&e32, &hdr.e64); } - if (hdr.e64.e_shstrndx >= hdr.e64.e_shnum) + shnum = hdr.e64.e_shnum; + shstrndx = hdr.e64.e_shstrndx; + + /* Extended ELF sections */ + if ((shstrndx == SHN_XINDEX) || (shnum == 0)) { + if (hdr.e32.e_ident[EI_CLASS] == ELFCLASS32) { + Elf32_Shdr x32; + + if (pread64(fd, &x32, sizeof (x32), + hdr.e64.e_shoff) != sizeof (x32)) + return (ctf_set_open_errno(errp, + errno)); + + shnum = x32.sh_size; + shstrndx = x32.sh_link; + } else { + Elf64_Shdr x64; + + if (pread64(fd, &x64, sizeof (x64), + hdr.e64.e_shoff) != sizeof (x64)) + return (ctf_set_open_errno(errp, + errno)); + + shnum = x64.sh_size; + shstrndx = x64.sh_link; + } + } + + if (shstrndx >= shnum) return (ctf_set_open_errno(errp, ECTF_CORRUPT)); - n = hdr.e64.e_shnum; - nbytes = sizeof (GElf_Shdr) * n; + nbytes = sizeof (GElf_Shdr) * shnum; if ((sp = malloc(nbytes)) == NULL) return (ctf_set_open_errno(errp, errno)); @@ -314,7 +341,7 @@ ctf_fdopen(int fd, int *errp) if (hdr.e32.e_ident[EI_CLASS] == ELFCLASS32) { Elf32_Shdr *sp32; - nbytes = sizeof (Elf32_Shdr) * n; + nbytes = sizeof (Elf32_Shdr) * shnum; if ((sp32 = malloc(nbytes)) == NULL || pread64(fd, sp32, nbytes, hdr.e64.e_shoff) != nbytes) { @@ -322,7 +349,7 @@ ctf_fdopen(int fd, int *errp) return (ctf_set_open_errno(errp, errno)); } - for (i = 0; i < n; i++) + for (i = 0; i < shnum; i++) shdr_to_gelf(&sp32[i], &sp[i]); free(sp32); @@ -336,14 +363,14 @@ ctf_fdopen(int fd, int *errp) * Now mmap the section header strings section so that we can * perform string comparison on the section names. */ - strs_mapsz = sp[hdr.e64.e_shstrndx].sh_size + - (sp[hdr.e64.e_shstrndx].sh_offset & ~_PAGEMASK); + strs_mapsz = sp[shstrndx].sh_size + + (sp[shstrndx].sh_offset & ~_PAGEMASK); strs_map = mmap64(NULL, strs_mapsz, PROT_READ, MAP_PRIVATE, - fd, sp[hdr.e64.e_shstrndx].sh_offset & _PAGEMASK); + fd, sp[shstrndx].sh_offset & _PAGEMASK); strs = (char *)strs_map + - (sp[hdr.e64.e_shstrndx].sh_offset & ~_PAGEMASK); + (sp[shstrndx].sh_offset & ~_PAGEMASK); if (strs_map == MAP_FAILED) { free(sp); @@ -354,15 +381,15 @@ ctf_fdopen(int fd, int *errp) * Iterate over the section header array looking for the CTF * section and symbol table. The strtab is linked to symtab. */ - for (i = 0; i < n; i++) { + for (i = 0; i < shnum; i++) { const GElf_Shdr *shp = &sp[i]; const GElf_Shdr *lhp = &sp[shp->sh_link]; - if (shp->sh_link >= hdr.e64.e_shnum) + if (shp->sh_link >= shnum) continue; /* corrupt sh_link field */ - if (shp->sh_name >= sp[hdr.e64.e_shstrndx].sh_size || - lhp->sh_name >= sp[hdr.e64.e_shstrndx].sh_size) + if (shp->sh_name >= sp[shstrndx].sh_size || + lhp->sh_name >= sp[shstrndx].sh_size) continue; /* corrupt sh_name field */ if (shp->sh_type == SHT_PROGBITS && diff --git a/cddl/contrib/opensolaris/lib/libdtrace/common/dt_open.c b/cddl/contrib/opensolaris/lib/libdtrace/common/dt_open.c index 25f9956a691..9c9b2a6e63c 100644 --- a/cddl/contrib/opensolaris/lib/libdtrace/common/dt_open.c +++ b/cddl/contrib/opensolaris/lib/libdtrace/common/dt_open.c @@ -1086,7 +1086,17 @@ dt_vopen(int version, int flags, int *errp, dtfd = open("/dev/dtrace/dtrace", O_RDWR); err = errno; /* save errno from opening dtfd */ - +#if defined(__FreeBSD__) + /* + * Automatically load the 'dtraceall' module if we couldn't open the + * char device. + */ + if (err == ENOENT && modfind("dtraceall") < 0) { + kldload("dtraceall"); /* ignore the error */ + dtfd = open("/dev/dtrace/dtrace", O_RDWR); + err = errno; + } +#endif #if defined(sun) ftfd = open("/dev/dtrace/provider/fasttrap", O_RDWR); #else diff --git a/cddl/contrib/opensolaris/lib/libzfs/common/libzfs.h b/cddl/contrib/opensolaris/lib/libzfs/common/libzfs.h index 981e9bd9e07..0a5ca82e4c9 100644 --- a/cddl/contrib/opensolaris/lib/libzfs/common/libzfs.h +++ b/cddl/contrib/opensolaris/lib/libzfs/common/libzfs.h @@ -326,6 +326,7 @@ typedef enum { ZPOOL_STATUS_RESILVERING, /* device being resilvered */ ZPOOL_STATUS_OFFLINE_DEV, /* device online */ ZPOOL_STATUS_REMOVED_DEV, /* removed device */ + ZPOOL_STATUS_NON_NATIVE_ASHIFT, /* (e.g. 512e dev with ashift of 9) */ /* * Finally, the following indicates a healthy pool. diff --git a/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_dataset.c b/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_dataset.c index 7186c6fb4f9..e330ad220e5 100644 --- a/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_dataset.c +++ b/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_dataset.c @@ -3572,7 +3572,6 @@ zfs_rollback(zfs_handle_t *zhp, zfs_handle_t *snap, boolean_t force) { rollback_data_t cb = { 0 }; int err; - zfs_cmd_t zc = { 0 }; boolean_t restore_resv = 0; uint64_t old_volsize, new_volsize; zfs_prop_t resv_prop; @@ -3604,22 +3603,15 @@ zfs_rollback(zfs_handle_t *zhp, zfs_handle_t *snap, boolean_t force) (old_volsize == zfs_prop_get_int(zhp, resv_prop)); } - (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); - - if (ZFS_IS_VOLUME(zhp)) - zc.zc_objset_type = DMU_OST_ZVOL; - else - zc.zc_objset_type = DMU_OST_ZFS; - /* * We rely on zfs_iter_children() to verify that there are no * newer snapshots for the given dataset. Therefore, we can * simply pass the name on to the ioctl() call. There is still * an unlikely race condition where the user has taken a * snapshot since we verified that this was the most recent. - * */ - if ((err = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_ROLLBACK, &zc)) != 0) { + err = lzc_rollback(zhp->zfs_name, NULL, 0); + if (err != 0) { (void) zfs_standard_error_fmt(zhp->zfs_hdl, errno, dgettext(TEXT_DOMAIN, "cannot rollback '%s'"), zhp->zfs_name); diff --git a/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_iter.c b/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_iter.c index 278bfd4cfd8..259d045e58a 100644 --- a/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_iter.c +++ b/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_iter.c @@ -21,10 +21,10 @@ /* * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. - * Copyright 2010 Nexenta Systems, Inc. All rights reserved. * Copyright (c) 2012 by Delphix. All rights reserved. * Copyright (c) 2012 Pawel Jakub Dawidek . * All rights reserved. + * Copyright 2013 Nexenta Systems, Inc. All rights reserved. */ #include @@ -452,8 +452,12 @@ iter_dependents_cb(zfs_handle_t *zhp, void *arg) } ida->stack = isf.next; } + if (!first && err == 0) err = ida->func(zhp, ida->data); + else + zfs_close(zhp); + return (err); } diff --git a/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_status.c b/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_status.c index 71b27a12140..906883ca60d 100644 --- a/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_status.c +++ b/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_status.c @@ -73,57 +73,66 @@ static char *zfs_msgid_table[] = { /* ARGSUSED */ static int -vdev_missing(uint64_t state, uint64_t aux, uint64_t errs) +vdev_missing(vdev_stat_t *vs, uint_t vsc) { - return (state == VDEV_STATE_CANT_OPEN && - aux == VDEV_AUX_OPEN_FAILED); + return (vs->vs_state == VDEV_STATE_CANT_OPEN && + vs->vs_aux == VDEV_AUX_OPEN_FAILED); } /* ARGSUSED */ static int -vdev_faulted(uint64_t state, uint64_t aux, uint64_t errs) +vdev_faulted(vdev_stat_t *vs, uint_t vsc) { - return (state == VDEV_STATE_FAULTED); + return (vs->vs_state == VDEV_STATE_FAULTED); } /* ARGSUSED */ static int -vdev_errors(uint64_t state, uint64_t aux, uint64_t errs) +vdev_errors(vdev_stat_t *vs, uint_t vsc) { - return (state == VDEV_STATE_DEGRADED || errs != 0); + return (vs->vs_state == VDEV_STATE_DEGRADED || + vs->vs_read_errors != 0 || vs->vs_write_errors != 0 || + vs->vs_checksum_errors != 0); } /* ARGSUSED */ static int -vdev_broken(uint64_t state, uint64_t aux, uint64_t errs) +vdev_broken(vdev_stat_t *vs, uint_t vsc) { - return (state == VDEV_STATE_CANT_OPEN); + return (vs->vs_state == VDEV_STATE_CANT_OPEN); } /* ARGSUSED */ static int -vdev_offlined(uint64_t state, uint64_t aux, uint64_t errs) +vdev_offlined(vdev_stat_t *vs, uint_t vsc) { - return (state == VDEV_STATE_OFFLINE); + return (vs->vs_state == VDEV_STATE_OFFLINE); } /* ARGSUSED */ static int -vdev_removed(uint64_t state, uint64_t aux, uint64_t errs) +vdev_removed(vdev_stat_t *vs, uint_t vsc) { - return (state == VDEV_STATE_REMOVED); + return (vs->vs_state == VDEV_STATE_REMOVED); +} + +static int +vdev_non_native_ashift(vdev_stat_t *vs, uint_t vsc) +{ + return (VDEV_STAT_VALID(vs_physical_ashift, vsc) && + vs->vs_configured_ashift < vs->vs_physical_ashift); } /* * Detect if any leaf devices that have seen errors or could not be opened. */ static boolean_t -find_vdev_problem(nvlist_t *vdev, int (*func)(uint64_t, uint64_t, uint64_t)) +find_vdev_problem(nvlist_t *vdev, int (*func)(vdev_stat_t *, uint_t), + boolean_t ignore_replacing) { nvlist_t **child; vdev_stat_t *vs; - uint_t c, children; - char *type; + uint_t c, vsc, children; /* * Ignore problems within a 'replacing' vdev, since we're presumably in @@ -131,23 +140,25 @@ find_vdev_problem(nvlist_t *vdev, int (*func)(uint64_t, uint64_t, uint64_t)) * out again. We'll pick up the fact that a resilver is happening * later. */ - verify(nvlist_lookup_string(vdev, ZPOOL_CONFIG_TYPE, &type) == 0); - if (strcmp(type, VDEV_TYPE_REPLACING) == 0) - return (B_FALSE); + if (ignore_replacing == B_TRUE) { + char *type; + + verify(nvlist_lookup_string(vdev, ZPOOL_CONFIG_TYPE, + &type) == 0); + if (strcmp(type, VDEV_TYPE_REPLACING) == 0) + return (B_FALSE); + } if (nvlist_lookup_nvlist_array(vdev, ZPOOL_CONFIG_CHILDREN, &child, &children) == 0) { for (c = 0; c < children; c++) - if (find_vdev_problem(child[c], func)) + if (find_vdev_problem(child[c], func, ignore_replacing)) return (B_TRUE); } else { verify(nvlist_lookup_uint64_array(vdev, ZPOOL_CONFIG_VDEV_STATS, - (uint64_t **)&vs, &c) == 0); + (uint64_t **)&vs, &vsc) == 0); - if (func(vs->vs_state, vs->vs_aux, - vs->vs_read_errors + - vs->vs_write_errors + - vs->vs_checksum_errors)) + if (func(vs, vsc) != 0) return (B_TRUE); } @@ -157,7 +168,7 @@ find_vdev_problem(nvlist_t *vdev, int (*func)(uint64_t, uint64_t, uint64_t)) if (nvlist_lookup_nvlist_array(vdev, ZPOOL_CONFIG_L2CACHE, &child, &children) == 0) { for (c = 0; c < children; c++) - if (find_vdev_problem(child[c], func)) + if (find_vdev_problem(child[c], func, ignore_replacing)) return (B_TRUE); } @@ -270,15 +281,15 @@ check_status(nvlist_t *config, boolean_t isimport) * Bad devices in non-replicated config. */ if (vs->vs_state == VDEV_STATE_CANT_OPEN && - find_vdev_problem(nvroot, vdev_faulted)) + find_vdev_problem(nvroot, vdev_faulted, B_TRUE)) return (ZPOOL_STATUS_FAULTED_DEV_NR); if (vs->vs_state == VDEV_STATE_CANT_OPEN && - find_vdev_problem(nvroot, vdev_missing)) + find_vdev_problem(nvroot, vdev_missing, B_TRUE)) return (ZPOOL_STATUS_MISSING_DEV_NR); if (vs->vs_state == VDEV_STATE_CANT_OPEN && - find_vdev_problem(nvroot, vdev_broken)) + find_vdev_problem(nvroot, vdev_broken, B_TRUE)) return (ZPOOL_STATUS_CORRUPT_LABEL_NR); /* @@ -300,31 +311,37 @@ check_status(nvlist_t *config, boolean_t isimport) /* * Missing devices in a replicated config. */ - if (find_vdev_problem(nvroot, vdev_faulted)) + if (find_vdev_problem(nvroot, vdev_faulted, B_TRUE)) return (ZPOOL_STATUS_FAULTED_DEV_R); - if (find_vdev_problem(nvroot, vdev_missing)) + if (find_vdev_problem(nvroot, vdev_missing, B_TRUE)) return (ZPOOL_STATUS_MISSING_DEV_R); - if (find_vdev_problem(nvroot, vdev_broken)) + if (find_vdev_problem(nvroot, vdev_broken, B_TRUE)) return (ZPOOL_STATUS_CORRUPT_LABEL_R); /* * Devices with errors */ - if (!isimport && find_vdev_problem(nvroot, vdev_errors)) + if (!isimport && find_vdev_problem(nvroot, vdev_errors, B_TRUE)) return (ZPOOL_STATUS_FAILING_DEV); /* * Offlined devices */ - if (find_vdev_problem(nvroot, vdev_offlined)) + if (find_vdev_problem(nvroot, vdev_offlined, B_TRUE)) return (ZPOOL_STATUS_OFFLINE_DEV); /* * Removed device */ - if (find_vdev_problem(nvroot, vdev_removed)) + if (find_vdev_problem(nvroot, vdev_removed, B_TRUE)) return (ZPOOL_STATUS_REMOVED_DEV); + /* + * Suboptimal, but usable, ashift configuration. + */ + if (find_vdev_problem(nvroot, vdev_non_native_ashift, B_FALSE)) + return (ZPOOL_STATUS_NON_NATIVE_ASHIFT); + /* * Outdated, but usable, version */ diff --git a/cddl/contrib/opensolaris/lib/libzfs_core/common/libzfs_core.c b/cddl/contrib/opensolaris/lib/libzfs_core/common/libzfs_core.c index c5c3b0e23cc..6f4b46cf281 100644 --- a/cddl/contrib/opensolaris/lib/libzfs_core/common/libzfs_core.c +++ b/cddl/contrib/opensolaris/lib/libzfs_core/common/libzfs_core.c @@ -20,7 +20,7 @@ */ /* - * Copyright (c) 2012 by Delphix. All rights reserved. + * Copyright (c) 2013 by Delphix. All rights reserved. * Copyright (c) 2013 Steven Hartland. All rights reserved. */ @@ -628,3 +628,27 @@ out: free((void*)(uintptr_t)zc.zc_nvlist_dst); return (error); } + +/* + * Roll back this filesystem or volume to its most recent snapshot. + * If snapnamebuf is not NULL, it will be filled in with the name + * of the most recent snapshot. + * + * Return 0 on success or an errno on failure. + */ +int +lzc_rollback(const char *fsname, char *snapnamebuf, int snapnamelen) +{ + nvlist_t *args; + nvlist_t *result; + int err; + + args = fnvlist_alloc(); + err = lzc_ioctl(ZFS_IOC_ROLLBACK, fsname, args, &result); + nvlist_free(args); + if (err == 0 && snapnamebuf != NULL) { + const char *snapname = fnvlist_lookup_string(result, "target"); + (void) strlcpy(snapnamebuf, snapname, snapnamelen); + } + return (err); +} diff --git a/cddl/contrib/opensolaris/lib/libzfs_core/common/libzfs_core.h b/cddl/contrib/opensolaris/lib/libzfs_core/common/libzfs_core.h index b10098b378c..c55caf44f0f 100644 --- a/cddl/contrib/opensolaris/lib/libzfs_core/common/libzfs_core.h +++ b/cddl/contrib/opensolaris/lib/libzfs_core/common/libzfs_core.h @@ -20,7 +20,7 @@ */ /* - * Copyright (c) 2012 by Delphix. All rights reserved. + * Copyright (c) 2013 by Delphix. All rights reserved. * Copyright (c) 2013 by Martin Matuska . All rights reserved. */ @@ -59,6 +59,7 @@ int lzc_send_space(const char *snapname, const char *fromsnap, boolean_t lzc_exists(const char *dataset); +int lzc_rollback(const char *fsname, char *snapnamebuf, int snapnamelen); #ifdef __cplusplus } diff --git a/cddl/contrib/opensolaris/lib/libzpool/common/kernel.c b/cddl/contrib/opensolaris/lib/libzpool/common/kernel.c index c5c7b66142a..c4e2d2e78aa 100644 --- a/cddl/contrib/opensolaris/lib/libzpool/common/kernel.c +++ b/cddl/contrib/opensolaris/lib/libzpool/common/kernel.c @@ -349,6 +349,41 @@ top: return (1); } +/*ARGSUSED*/ +clock_t +cv_timedwait_hires(kcondvar_t *cv, kmutex_t *mp, hrtime_t tim, hrtime_t res, + int flag) +{ + int error; + timestruc_t ts; + hrtime_t delta; + + ASSERT(flag == 0); + +top: + delta = tim - gethrtime(); + if (delta <= 0) + return (-1); + + ts.tv_sec = delta / NANOSEC; + ts.tv_nsec = delta % NANOSEC; + + ASSERT(mutex_owner(mp) == curthread); + mp->m_owner = NULL; + error = pthread_cond_timedwait(cv, &mp->m_lock, &ts); + mp->m_owner = curthread; + + if (error == ETIMEDOUT) + return (-1); + + if (error == EINTR) + goto top; + + ASSERT(error == 0); + + return (1); +} + void cv_signal(kcondvar_t *cv) { @@ -591,6 +626,12 @@ dprintf_setup(int *argc, char **argv) dprintf_print_all = 1; } +int +sysctl_handle_64(SYSCTL_HANDLER_ARGS) +{ + return (0); +} + /* * ========================================================================= * debug printfs diff --git a/cddl/contrib/opensolaris/lib/libzpool/common/sys/zfs_context.h b/cddl/contrib/opensolaris/lib/libzpool/common/sys/zfs_context.h index 21075055a1d..15c181e204a 100644 --- a/cddl/contrib/opensolaris/lib/libzpool/common/sys/zfs_context.h +++ b/cddl/contrib/opensolaris/lib/libzpool/common/sys/zfs_context.h @@ -313,6 +313,8 @@ extern void cv_init(kcondvar_t *cv, char *name, int type, void *arg); extern void cv_destroy(kcondvar_t *cv); extern void cv_wait(kcondvar_t *cv, kmutex_t *mp); extern clock_t cv_timedwait(kcondvar_t *cv, kmutex_t *mp, clock_t abstime); +extern clock_t cv_timedwait_hires(kcondvar_t *cvp, kmutex_t *mp, hrtime_t tim, + hrtime_t res, int flag); extern void cv_signal(kcondvar_t *cv); extern void cv_broadcast(kcondvar_t *cv); @@ -659,11 +661,55 @@ typedef uint32_t idmap_rid_t; #define SX_SYSINIT(name, lock, desc) +#define SYSCTL_HANDLER_ARGS struct sysctl_oid *oidp, void *arg1, \ + intptr_t arg2, struct sysctl_req *req + +/* + * This describes the access space for a sysctl request. This is needed + * so that we can use the interface from the kernel or from user-space. + */ +struct sysctl_req { + struct thread *td; /* used for access checking */ + int lock; /* wiring state */ + void *oldptr; + size_t oldlen; + size_t oldidx; + int (*oldfunc)(struct sysctl_req *, const void *, size_t); + void *newptr; + size_t newlen; + size_t newidx; + int (*newfunc)(struct sysctl_req *, void *, size_t); + size_t validlen; + int flags; +}; + +SLIST_HEAD(sysctl_oid_list, sysctl_oid); + +/* + * This describes one "oid" in the MIB tree. Potentially more nodes can + * be hidden behind it, expanded by the handler. + */ +struct sysctl_oid { + struct sysctl_oid_list *oid_parent; + SLIST_ENTRY(sysctl_oid) oid_link; + int oid_number; + u_int oid_kind; + void *oid_arg1; + intptr_t oid_arg2; + const char *oid_name; + int (*oid_handler)(SYSCTL_HANDLER_ARGS); + const char *oid_fmt; + int oid_refcnt; + u_int oid_running; + const char *oid_descr; +}; + #define SYSCTL_DECL(...) #define SYSCTL_NODE(...) #define SYSCTL_INT(...) #define SYSCTL_UINT(...) #define SYSCTL_ULONG(...) +#define SYSCTL_PROC(...) #define SYSCTL_QUAD(...) #define SYSCTL_UQUAD(...) #ifdef TUNABLE_INT @@ -675,6 +721,8 @@ typedef uint32_t idmap_rid_t; #define TUNABLE_ULONG(...) #define TUNABLE_QUAD(...) +int sysctl_handle_64(SYSCTL_HANDLER_ARGS); + /* Errors */ #ifndef ERESTART diff --git a/cddl/contrib/opensolaris/tools/ctf/cvt/dwarf.c b/cddl/contrib/opensolaris/tools/ctf/cvt/dwarf.c index c7f785eb044..0738dd080f8 100644 --- a/cddl/contrib/opensolaris/tools/ctf/cvt/dwarf.c +++ b/cddl/contrib/opensolaris/tools/ctf/cvt/dwarf.c @@ -23,8 +23,6 @@ * Use is subject to license terms. */ -#pragma ident "%Z%%M% %I% %E% SMI" - /* * DWARF to tdata conversion * @@ -1796,6 +1794,59 @@ die_resolve(dwarf_t *dw) } while (dw->dw_nunres != 0); } +/* + * Any object containing a function or object symbol at any scope should also + * contain DWARF data. + */ +static boolean_t +should_have_dwarf(Elf *elf) +{ + Elf_Scn *scn = NULL; + Elf_Data *data = NULL; + GElf_Shdr shdr; + GElf_Sym sym; + uint32_t symdx = 0; + size_t nsyms = 0; + boolean_t found = B_FALSE; + + while ((scn = elf_nextscn(elf, scn)) != NULL) { + gelf_getshdr(scn, &shdr); + + if (shdr.sh_type == SHT_SYMTAB) { + found = B_TRUE; + break; + } + } + + if (!found) + terminate("cannot convert stripped objects\n"); + + data = elf_getdata(scn, NULL); + nsyms = shdr.sh_size / shdr.sh_entsize; + + for (symdx = 0; symdx < nsyms; symdx++) { + gelf_getsym(data, symdx, &sym); + + if ((GELF_ST_TYPE(sym.st_info) == STT_FUNC) || + (GELF_ST_TYPE(sym.st_info) == STT_TLS) || + (GELF_ST_TYPE(sym.st_info) == STT_OBJECT)) { + char *name; + + name = elf_strptr(elf, shdr.sh_link, sym.st_name); + + /* Studio emits these local symbols regardless */ + if ((strcmp(name, "Bbss.bss") != 0) && + (strcmp(name, "Ttbss.bss") != 0) && + (strcmp(name, "Ddata.data") != 0) && + (strcmp(name, "Ttdata.data") != 0) && + (strcmp(name, "Drodata.rodata") != 0)) + return (B_TRUE); + } + } + + return (B_FALSE); +} + /*ARGSUSED*/ int dw_read(tdata_t *td, Elf *elf, char *filename __unused) @@ -1820,8 +1871,12 @@ dw_read(tdata_t *td, Elf *elf, char *filename __unused) if ((rc = dwarf_elf_init(elf, DW_DLC_READ, &dw.dw_dw, &dw.dw_err)) == DW_DLV_NO_ENTRY) { - errno = ENOENT; - return (-1); + if (should_have_dwarf(elf)) { + errno = ENOENT; + return (-1); + } else { + return (0); + } } else if (rc != DW_DLV_OK) { if (dwarf_errno(&dw.dw_err) == DW_DLE_DEBUG_INFO_NULL) { /* @@ -1839,9 +1894,14 @@ dw_read(tdata_t *td, Elf *elf, char *filename __unused) &addrsz, &nxthdr, &dw.dw_err)) != DW_DLV_OK) terminate("rc = %d %s\n", rc, dwarf_errmsg(&dw.dw_err)); - if ((cu = die_sibling(&dw, NULL)) == NULL) + if ((cu = die_sibling(&dw, NULL)) == NULL || + (((child = die_child(&dw, cu)) == NULL) && + should_have_dwarf(elf))) { terminate("file does not contain dwarf type data " "(try compiling with -g)\n"); + } else if (child == NULL) { + return (0); + } dw.dw_maxoff = nxthdr - 1; diff --git a/cddl/lib/libdtrace/Makefile b/cddl/lib/libdtrace/Makefile index 4fd7faefc3d..46f7046d874 100644 --- a/cddl/lib/libdtrace/Makefile +++ b/cddl/lib/libdtrace/Makefile @@ -48,8 +48,11 @@ SRCS= dt_aggregate.c \ DSRCS= errno.d \ io.d \ + ip.d \ psinfo.d \ signal.d \ + tcp.d \ + udp.d \ unistd.d WARNS?= 1 diff --git a/cddl/lib/libdtrace/ip.d b/cddl/lib/libdtrace/ip.d new file mode 100644 index 00000000000..b886a0ad254 --- /dev/null +++ b/cddl/lib/libdtrace/ip.d @@ -0,0 +1,285 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + * + * $FreeBSD$ + */ +/* + * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013 Mark Johnston + */ + +#pragma D depends_on provider ip + +/* + * pktinfo is where packet ID info can be made available for deeper + * analysis if packet IDs become supported by the kernel in the future. + * The pkt_addr member is currently always NULL. + */ +typedef struct pktinfo { + uintptr_t pkt_addr; +} pktinfo_t; + +/* + * csinfo is where connection state info is made available. + */ +typedef uint32_t zoneid_t; +typedef struct csinfo { + uintptr_t cs_addr; + uint64_t cs_cid; + pid_t cs_pid; + zoneid_t cs_zoneid; +} csinfo_t; + +/* + * ipinfo contains common IP info for both IPv4 and IPv6. + */ +typedef struct ipinfo { + uint8_t ip_ver; /* IP version (4, 6) */ + uint32_t ip_plength; /* payload length */ + string ip_saddr; /* source address */ + string ip_daddr; /* destination address */ +} ipinfo_t; + +/* + * ifinfo contains network interface info. + */ +typedef struct ifinfo { + string if_name; /* interface name */ + int8_t if_local; /* is delivered locally */ + /*netstackid_t if_ipstack;*/ /* ipstack ID */ + uintptr_t if_addr; /* pointer to raw ill_t */ +} ifinfo_t; + +typedef uint32_t ipaddr_t; +typedef struct { + uint8_t ipha_version_and_hdr_length; + uint8_t ipha_type_of_service; + uint16_t ipha_length; + uint16_t ipha_ident; + uint16_t ipha_fragment_offset_and_flags; + uint8_t ipha_ttl; + uint8_t ipha_protocol; + uint16_t ipha_hdr_checksum; + ipaddr_t ipha_src; + ipaddr_t ipha_dst; +} ipha_t; + +/* + * ipv4info is a translated version of the IPv4 header (with raw pointer). + * These values are NULL if the packet is not IPv4. + */ +typedef struct ipv4info { + uint8_t ipv4_ver; /* IP version (4) */ + uint8_t ipv4_ihl; /* header length, bytes */ + uint8_t ipv4_tos; /* type of service field */ + uint16_t ipv4_length; /* length (header + payload) */ + uint16_t ipv4_ident; /* identification */ + uint8_t ipv4_flags; /* IP flags */ + uint16_t ipv4_offset; /* fragment offset */ + uint8_t ipv4_ttl; /* time to live */ + uint8_t ipv4_protocol; /* next level protocol */ + string ipv4_protostr; /* next level protocol, as a string */ + uint16_t ipv4_checksum; /* header checksum */ + ipaddr_t ipv4_src; /* source address */ + ipaddr_t ipv4_dst; /* destination address */ + string ipv4_saddr; /* source address, string */ + string ipv4_daddr; /* destination address, string */ + ipha_t *ipv4_hdr; /* pointer to raw header */ +} ipv4info_t; + +/* + * ipv6info is a translated version of the IPv6 header (with raw pointer). + * These values are NULL if the packet is not IPv6. + */ +typedef struct in6_addr in6_addr_t; +typedef struct ip6_hdr ip6_t; +typedef struct ipv6info { + uint8_t ipv6_ver; /* IP version (6) */ + uint8_t ipv6_tclass; /* traffic class */ + uint32_t ipv6_flow; /* flow label */ + uint16_t ipv6_plen; /* payload length */ + uint8_t ipv6_nexthdr; /* next header protocol */ + string ipv6_nextstr; /* next header protocol, as a string */ + uint8_t ipv6_hlim; /* hop limit */ + in6_addr_t *ipv6_src; /* source address */ + in6_addr_t *ipv6_dst; /* destination address */ + string ipv6_saddr; /* source address, string */ + string ipv6_daddr; /* destination address, string */ + ip6_t *ipv6_hdr; /* pointer to raw header */ +} ipv6info_t; + +#pragma D binding "1.0" IPPROTO_IP +inline short IPPROTO_IP = 0; +#pragma D binding "1.0" IPPROTO_ICMP +inline short IPPROTO_ICMP = 1; +#pragma D binding "1.0" IPPROTO_IGMP +inline short IPPROTO_IGMP = 2; +#pragma D binding "1.0" IPPROTO_IPV4 +inline short IPPROTO_IPV4 = 4; +#pragma D binding "1.0" IPPROTO_TCP +inline short IPPROTO_TCP = 6; +#pragma D binding "1.0" IPPROTO_UDP +inline short IPPROTO_UDP = 17; +#pragma D binding "1.0" IPPROTO_IPV6 +inline short IPPROTO_IPV6 = 41; +#pragma D binding "1.0" IPPROTO_ROUTING +inline short IPPROTO_ROUTING = 43; +#pragma D binding "1.0" IPPROTO_FRAGMENT +inline short IPPROTO_FRAGMENT = 44; +#pragma D binding "1.0" IPPROTO_RSVP +inline short IPPROTO_RSVP = 46; +#pragma D binding "1.0" IPPROTO_GRE +inline short IPPROTO_GRE = 47; +#pragma D binding "1.0" IPPROTO_ESP +inline short IPPROTO_ESP = 50; +#pragma D binding "1.0" IPPROTO_AH +inline short IPPROTO_AH = 51; +#pragma D binding "1.0" IPPROTO_MOBILE +inline short IPPROTO_MOBILE = 55; +#pragma D binding "1.0" IPPROTO_ICMPV6 +inline short IPPROTO_ICMPV6 = 58; +#pragma D binding "1.0" IPPROTO_DSTOPTS +inline short IPPROTO_DSTOPTS = 60; +#pragma D binding "1.0" IPPROTO_ETHERIP +inline short IPPROTO_ETHERIP = 97; +#pragma D binding "1.0" IPPROTO_PIM +inline short IPPROTO_PIM = 103; +#pragma D binding "1.0" IPPROTO_IPCOMP +inline short IPPROTO_IPCOMP = 108; +#pragma D binding "1.0" IPPROTO_SCTP +inline short IPPROTO_SCTP = 132; +#pragma D binding "1.0" IPPROTO_RAW +inline short IPPROTO_RAW = 255; + +inline uint8_t INP_IPV4 = 0x01; +inline uint8_t INP_IPV6 = 0x02; + +#pragma D binding "1.0" protocols +inline string protocols[int proto] = + proto == IPPROTO_IP ? "IP" : + proto == IPPROTO_ICMP ? "ICMP" : + proto == IPPROTO_IGMP ? "IGMP" : + proto == IPPROTO_IPV4 ? "IPV4" : + proto == IPPROTO_TCP ? "TCP" : + proto == IPPROTO_UDP ? "UDP" : + proto == IPPROTO_IPV6 ? "IPV6" : + proto == IPPROTO_ROUTING ? "ROUTING" : + proto == IPPROTO_FRAGMENT ? "FRAGMENT" : + proto == IPPROTO_RSVP ? "RSVP" : + proto == IPPROTO_GRE ? "GRE" : + proto == IPPROTO_ESP ? "ESP" : + proto == IPPROTO_AH ? "AH" : + proto == IPPROTO_MOBILE ? "MOBILE" : + proto == IPPROTO_ICMPV6 ? "ICMPV6" : + proto == IPPROTO_DSTOPTS ? "DSTOPTS" : + proto == IPPROTO_ETHERIP ? "ETHERIP" : + proto == IPPROTO_PIM ? "PIM" : + proto == IPPROTO_IPCOMP ? "IPCOMP" : + proto == IPPROTO_SCTP ? "SCTP" : + proto == IPPROTO_RAW ? "RAW" : + ""; + +/* + * This field is always NULL according to the current definition of the ip + * probes. + */ +#pragma D binding "1.0" translator +translator pktinfo_t < void *p > { + pkt_addr = NULL; +}; + +#pragma D binding "1.0" translator +translator csinfo_t < void *p > { + cs_addr = NULL; + cs_cid = (uint64_t)p; + cs_pid = 0; + cs_zoneid = 0; +}; + +#pragma D binding "1.0" translator +translator csinfo_t < struct inpcb *p > { + cs_addr = NULL; + cs_cid = (uint64_t)p; + cs_pid = 0; /* XXX */ + cs_zoneid = 0; +}; + +#pragma D binding "1.0" translator +translator ipinfo_t < uint8_t *p > { + ip_ver = p == NULL ? 0 : ((struct ip *)p)->ip_v; + ip_plength = p == NULL ? 0 : + ((struct ip *)p)->ip_v == 4 ? + ntohs(((struct ip *)p)->ip_len) - (((struct ip *)p)->ip_hl << 2): + ntohs(((struct ip6_hdr *)p)->ip6_ctlun.ip6_un1.ip6_un1_plen); + ip_saddr = p == NULL ? 0 : + ((struct ip *)p)->ip_v == 4 ? + inet_ntoa(&((struct ip *)p)->ip_src.s_addr) : + inet_ntoa6(&((struct ip6_hdr *)p)->ip6_src); + ip_daddr = p == NULL ? 0 : + ((struct ip *)p)->ip_v == 4 ? + inet_ntoa(&((struct ip *)p)->ip_dst.s_addr) : + inet_ntoa6(&((struct ip6_hdr *)p)->ip6_dst); +}; + +#pragma D binding "1.0" IFF_LOOPBACK +inline int IFF_LOOPBACK = 0x8; + +#pragma D binding "1.0" translator +translator ifinfo_t < struct ifnet *p > { + if_name = p->if_xname; + if_local = (p->if_flags & IFF_LOOPBACK) == 0 ? 0 : 1; + if_addr = (uintptr_t)p; +}; + +#pragma D binding "1.0" translator +translator ipv4info_t < struct ip *p > { + ipv4_ver = p == NULL ? 0 : p->ip_v; + ipv4_ihl = p == NULL ? 0 : p->ip_hl; + ipv4_tos = p == NULL ? 0 : p->ip_tos; + ipv4_length = p == NULL ? 0 : ntohs(p->ip_len); + ipv4_ident = p == NULL ? 0 : ntohs(p->ip_id); + ipv4_flags = p == NULL ? 0 : (p->ip_off & 0xe000); + ipv4_offset = p == NULL ? 0 : p->ip_off; + ipv4_ttl = p == NULL ? 0 : p->ip_ttl; + ipv4_protocol = p == NULL ? 0 : p->ip_p; + ipv4_protostr = p == NULL ? "" : protocols[p->ip_p]; + ipv4_checksum = p == NULL ? 0 : ntohs(p->ip_sum); + ipv4_src = p == NULL ? 0 : (ipaddr_t)ntohl(p->ip_src.s_addr); + ipv4_dst = p == NULL ? 0 : (ipaddr_t)ntohl(p->ip_dst.s_addr); + ipv4_saddr = p == NULL ? 0 : inet_ntoa(&p->ip_src.s_addr); + ipv4_daddr = p == NULL ? 0 : inet_ntoa(&p->ip_dst.s_addr); + ipv4_hdr = (ipha_t *)p; +}; + +#pragma D binding "1.0" translator +translator ipv6info_t < struct ip6_hdr *p > { + ipv6_ver = p == NULL ? 0 : (ntohl(p->ip6_ctlun.ip6_un1.ip6_un1_flow) & 0xf0000000) >> 28; + ipv6_tclass = p == NULL ? 0 : (ntohl(p->ip6_ctlun.ip6_un1.ip6_un1_flow) & 0x0ff00000) >> 20; + ipv6_flow = p == NULL ? 0 : ntohl(p->ip6_ctlun.ip6_un1.ip6_un1_flow) & 0x000fffff; + ipv6_plen = p == NULL ? 0 : ntohs(p->ip6_ctlun.ip6_un1.ip6_un1_plen); + ipv6_nexthdr = p == NULL ? 0 : p->ip6_ctlun.ip6_un1.ip6_un1_nxt; + ipv6_nextstr = p == NULL ? "" : protocols[p->ip6_ctlun.ip6_un1.ip6_un1_nxt]; + ipv6_hlim = p == NULL ? 0 : p->ip6_ctlun.ip6_un1.ip6_un1_hlim; + ipv6_src = p == NULL ? 0 : (in6_addr_t *)&p->ip6_src; + ipv6_dst = p == NULL ? 0 : (in6_addr_t *)&p->ip6_dst; + ipv6_saddr = p == NULL ? 0 : inet_ntoa6(&p->ip6_src); + ipv6_daddr = p == NULL ? 0 : inet_ntoa6(&p->ip6_dst); + ipv6_hdr = (ip6_t *)p; +}; diff --git a/cddl/lib/libdtrace/tcp.d b/cddl/lib/libdtrace/tcp.d new file mode 100644 index 00000000000..6d56eb7fca3 --- /dev/null +++ b/cddl/lib/libdtrace/tcp.d @@ -0,0 +1,203 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + * + * $FreeBSD$ + */ +/* + * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013 Mark Johnston + */ + +#pragma D depends_on library ip.d +#pragma D depends_on provider tcp + +/* + * Convert a TCP state value to a string. + */ +#pragma D binding "1.0" TCPS_CLOSED +inline int TCPS_CLOSED = 0; +#pragma D binding "1.0" TCPS_LISTEN +inline int TCPS_LISTEN = 1; +#pragma D binding "1.0" TCPS_SYN_SENT +inline int TCPS_SYN_SENT = 2; +#pragma D binding "1.0" TCPS_SYN_RECEIVED +inline int TCPS_SYN_RECEIVED = 3; +#pragma D binding "1.0" TCPS_ESTABLISHED +inline int TCPS_ESTABLISHED = 4; +#pragma D binding "1.0" TCPS_CLOSE_WAIT +inline int TCPS_CLOSE_WAIT = 5; +#pragma D binding "1.0" TCPS_FIN_WAIT_1 +inline int TCPS_FIN_WAIT_1 = 6; +#pragma D binding "1.0" TCPS_CLOSING +inline int TCPS_CLOSING = 7; +#pragma D binding "1.0" TCPS_LAST_ACK +inline int TCPS_LAST_ACK = 8; +#pragma D binding "1.0" TCPS_FIN_WAIT_2 +inline int TCPS_FIN_WAIT_2 = 9; +#pragma D binding "1.0" TCPS_TIME_WAIT +inline int TCPS_TIME_WAIT = 10; + +/* TCP segment flags. */ +#pragma D binding "1.0" TH_FIN +inline uint8_t TH_FIN = 0x01; +#pragma D binding "1.0" TH_SYN +inline uint8_t TH_SYN = 0x02; +#pragma D binding "1.0" TH_RST +inline uint8_t TH_RST = 0x04; +#pragma D binding "1.0" TH_PUSH +inline uint8_t TH_PUSH = 0x08; +#pragma D binding "1.0" TH_ACK +inline uint8_t TH_ACK = 0x10; +#pragma D binding "1.0" TH_URG +inline uint8_t TH_URG = 0x20; +#pragma D binding "1.0" TH_ECE +inline uint8_t TH_ECE = 0x40; +#pragma D binding "1.0" TH_CWR +inline uint8_t TH_CWR = 0x80; + +/* TCP connection state strings. */ +#pragma D binding "1.0" tcp_state_string +inline string tcp_state_string[int32_t state] = + state == TCPS_CLOSED ? "state-closed" : + state == TCPS_LISTEN ? "state-listen" : + state == TCPS_SYN_SENT ? "state-syn-sent" : + state == TCPS_SYN_RECEIVED ? "state-syn-received" : + state == TCPS_ESTABLISHED ? "state-established" : + state == TCPS_CLOSE_WAIT ? "state-close-wait" : + state == TCPS_FIN_WAIT_1 ? "state-fin-wait-1" : + state == TCPS_CLOSING ? "state-closing" : + state == TCPS_LAST_ACK ? "state-last-ack" : + state == TCPS_FIN_WAIT_2 ? "state-fin-wait-2" : + state == TCPS_TIME_WAIT ? "state-time-wait" : + ""; + +/* + * tcpsinfo contains stable TCP details from tcp_t. + */ +typedef struct tcpsinfo { + uintptr_t tcps_addr; + int tcps_local; /* is delivered locally, boolean */ + int tcps_active; /* active open (from here), boolean */ + uint16_t tcps_lport; /* local port */ + uint16_t tcps_rport; /* remote port */ + string tcps_laddr; /* local address, as a string */ + string tcps_raddr; /* remote address, as a string */ + int32_t tcps_state; /* TCP state */ + uint32_t tcps_iss; /* Initial sequence # sent */ + uint32_t tcps_suna; /* sequence # sent but unacked */ + uint32_t tcps_snxt; /* next sequence # to send */ + uint32_t tcps_rack; /* sequence # we have acked */ + uint32_t tcps_rnxt; /* next sequence # expected */ + uint32_t tcps_swnd; /* send window size */ + int32_t tcps_snd_ws; /* send window scaling */ + uint32_t tcps_rwnd; /* receive window size */ + int32_t tcps_rcv_ws; /* receive window scaling */ + uint32_t tcps_cwnd; /* congestion window */ + uint32_t tcps_cwnd_ssthresh; /* threshold for congestion avoidance */ + uint32_t tcps_sack_fack; /* SACK sequence # we have acked */ + uint32_t tcps_sack_snxt; /* next SACK seq # for retransmission */ + uint32_t tcps_rto; /* round-trip timeout, msec */ + uint32_t tcps_mss; /* max segment size */ + int tcps_retransmit; /* retransmit send event, boolean */ +} tcpsinfo_t; + +/* + * tcplsinfo provides the old tcp state for state changes. + */ +typedef struct tcplsinfo { + int32_t tcps_state; /* previous TCP state */ +} tcplsinfo_t; + +/* + * tcpinfo is the TCP header fields. + */ +typedef struct tcpinfo { + uint16_t tcp_sport; /* source port */ + uint16_t tcp_dport; /* destination port */ + uint32_t tcp_seq; /* sequence number */ + uint32_t tcp_ack; /* acknowledgment number */ + uint8_t tcp_offset; /* data offset, in bytes */ + uint8_t tcp_flags; /* flags */ + uint16_t tcp_window; /* window size */ + uint16_t tcp_checksum; /* checksum */ + uint16_t tcp_urgent; /* urgent data pointer */ + struct tcphdr *tcp_hdr; /* raw TCP header */ +} tcpinfo_t; + +#pragma D binding "1.0" translator +translator csinfo_t < struct tcpcb *p > { + cs_addr = NULL; + cs_cid = (uint64_t)(p == NULL ? 0 : p->t_inpcb); + cs_pid = 0; + cs_zoneid = 0; +}; + +#pragma D binding "1.0" translator +translator tcpsinfo_t < struct tcpcb *p > { + tcps_addr = (uintptr_t)p; + tcps_local = -1; /* XXX */ + tcps_active = -1; /* XXX */ + tcps_lport = p == NULL ? 0 : ntohs(p->t_inpcb->inp_inc.inc_ie.ie_lport); + tcps_rport = p == NULL ? 0 : ntohs(p->t_inpcb->inp_inc.inc_ie.ie_fport); + tcps_laddr = p == NULL ? 0 : + p->t_inpcb->inp_vflag == INP_IPV4 ? + inet_ntoa(&p->t_inpcb->inp_inc.inc_ie.ie_dependladdr.ie46_local.ia46_addr4.s_addr) : + inet_ntoa6(&p->t_inpcb->inp_inc.inc_ie.ie_dependladdr.ie6_local); + tcps_raddr = p == NULL ? 0 : + p->t_inpcb->inp_vflag == INP_IPV4 ? + inet_ntoa(&p->t_inpcb->inp_inc.inc_ie.ie_dependfaddr.ie46_foreign.ia46_addr4.s_addr) : + inet_ntoa6(&p->t_inpcb->inp_inc.inc_ie.ie_dependfaddr.ie6_foreign); + tcps_state = p == NULL ? -1 : p->t_state; + tcps_iss = p == NULL ? 0 : p->iss; + tcps_suna = p == NULL ? 0 : p->snd_una; + tcps_snxt = p == NULL ? 0 : p->snd_nxt; + tcps_rack = p == NULL ? 0 : p->last_ack_sent; + tcps_rnxt = p == NULL ? 0 : p->rcv_nxt; + tcps_swnd = p == NULL ? -1 : p->snd_wnd; + tcps_snd_ws = p == NULL ? -1 : p->snd_scale; + tcps_rwnd = p == NULL ? -1 : p->rcv_wnd; + tcps_rcv_ws = p == NULL ? -1 : p->rcv_scale; + tcps_cwnd = p == NULL ? -1 : p->snd_cwnd; + tcps_cwnd_ssthresh = p == NULL ? -1 : p->snd_ssthresh; + tcps_sack_fack = p == NULL ? 0 : p->snd_fack; + tcps_sack_snxt = p == NULL ? 0 : p->sack_newdata; + tcps_rto = p == NULL ? -1 : p->t_rxtcur / 1000; /* XXX */ + tcps_mss = p == NULL ? -1 : p->t_maxseg; + tcps_retransmit = -1; /* XXX */ +}; + +#pragma D binding "1.0" translator +translator tcpinfo_t < struct tcphdr *p > { + tcp_sport = p == NULL ? 0 : ntohs(p->th_sport); + tcp_dport = p == NULL ? 0 : ntohs(p->th_dport); + tcp_seq = p == NULL ? -1 : ntohl(p->th_seq); + tcp_ack = p == NULL ? -1 : ntohl(p->th_ack); + tcp_offset = p == NULL ? -1 : (p->th_off >> 2); + tcp_flags = p == NULL ? 0 : p->th_flags; + tcp_window = p == NULL ? 0 : ntohs(p->th_win); + tcp_checksum = p == NULL ? 0 : ntohs(p->th_sum); + tcp_urgent = p == NULL ? 0 : ntohs(p->th_urp); + tcp_hdr = (struct tcphdr *)p; +}; + +#pragma D binding "1.0" translator +translator tcplsinfo_t < int s > { + tcps_state = s; +}; diff --git a/cddl/lib/libdtrace/udp.d b/cddl/lib/libdtrace/udp.d new file mode 100644 index 00000000000..5ad625de1bc --- /dev/null +++ b/cddl/lib/libdtrace/udp.d @@ -0,0 +1,75 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + * + * $FreeBSD$ + */ +/* + * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013 Mark Johnston + */ + +#pragma D depends_on library ip.d +#pragma D depends_on provider udp + +/* + * udpsinfo contains stable UDP details. + */ +typedef struct udpsinfo { + uintptr_t udps_addr; + uint16_t udps_lport; /* local port */ + uint16_t udps_rport; /* remote port */ + string udps_laddr; /* local address, as a string */ + string udps_raddr; /* remote address, as a string */ +} udpsinfo_t; + +/* + * udpinfo is the UDP header fields. + */ +typedef struct udpinfo { + uint16_t udp_sport; /* source port */ + uint16_t udp_dport; /* destination port */ + uint16_t udp_length; /* total length */ + uint16_t udp_checksum; /* headers + data checksum */ + struct udphdr *udp_hdr; /* raw UDP header */ +} udpinfo_t; + +#pragma D binding "1.0" translator +translator udpsinfo_t < struct inpcb *p > { + udps_addr = (uintptr_t)p; + udps_lport = p == NULL ? 0 : ntohs(p->inp_inc.inc_ie.ie_lport); + udps_rport = p == NULL ? 0 : ntohs(p->inp_inc.inc_ie.ie_fport); + udps_laddr = p == NULL ? "" : + p->inp_vflag == INP_IPV4 ? + inet_ntoa(&p->inp_inc.inc_ie.ie_dependladdr.ie46_local.ia46_addr4.s_addr) : + inet_ntoa6(&p->inp_inc.inc_ie.ie_dependladdr.ie6_local); + udps_raddr = p == NULL ? "" : + p->inp_vflag == INP_IPV4 ? + inet_ntoa(&p->inp_inc.inc_ie.ie_dependfaddr.ie46_foreign.ia46_addr4.s_addr) : + inet_ntoa6(&p->inp_inc.inc_ie.ie_dependfaddr.ie6_foreign); +}; + +#pragma D binding "1.0" translator +translator udpinfo_t < struct udphdr *p > { + udp_sport = p == NULL ? 0 : ntohs(p->uh_sport); + udp_dport = p == NULL ? 0 : ntohs(p->uh_dport); + udp_length = p == NULL ? 0 : ntohs(p->uh_ulen); + udp_checksum = p == NULL ? 0 : ntohs(p->uh_sum); + udp_hdr = p; +}; diff --git a/cddl/lib/libzpool/Makefile b/cddl/lib/libzpool/Makefile index b159d3af731..848325aff84 100644 --- a/cddl/lib/libzpool/Makefile +++ b/cddl/lib/libzpool/Makefile @@ -64,7 +64,9 @@ NO_PROFILE= CSTD= c99 -CFLAGS+= -DDEBUG=1 -#DEBUG_FLAGS+= -g +# Since there are many asserts in this library, it makes no sense to compile +# it without debugging. + +CFLAGS+= -g -DDEBUG=1 .include diff --git a/cddl/usr.bin/ztest/Makefile b/cddl/usr.bin/ztest/Makefile index 965300ef297..370eacb5724 100644 --- a/cddl/usr.bin/ztest/Makefile +++ b/cddl/usr.bin/ztest/Makefile @@ -25,7 +25,8 @@ LDADD= -lgeom -lm -lnvpair -lumem -lzpool -lpthread -lavl -lzfs_core -lzfs \ CSTD= c99 -CFLAGS+= -DDEBUG=1 -#DEBUG_FLAGS+= -g +# Since there are many asserts in this program, it makes no sense to compile +# it without debugging. +CFLAGS+= -g -DDEBUG=1 .include diff --git a/cddl/usr.sbin/zdb/Makefile b/cddl/usr.sbin/zdb/Makefile index 806838560a7..fa9ab98f5b3 100644 --- a/cddl/usr.sbin/zdb/Makefile +++ b/cddl/usr.sbin/zdb/Makefile @@ -27,7 +27,8 @@ DPADD= ${LIBGEOM} ${LIBM} ${LIBNVPAIR} ${LIBPTHREAD} ${LIBUMEM} \ ${LIBUUTIL} ${LIBZFS_CORE} ${LIBZFS} ${LIBZPOOL} LDADD= -lgeom -lm -lnvpair -lpthread -lumem -luutil -lzfs_core -lzfs -lzpool -CFLAGS+= -DDEBUG=1 -#DEBUG_FLAGS+= -g +# Since there are many asserts in this program, it makes no sense to compile +# it without debugging. +CFLAGS+= -g -DDEBUG=1 .include diff --git a/contrib/bind9/CHANGES b/contrib/bind9/CHANGES index e8383c62baf..4e3152fd0bb 100644 --- a/contrib/bind9/CHANGES +++ b/contrib/bind9/CHANGES @@ -1,15 +1,15 @@ - --- 9.8.5-P2 released --- + --- 9.9.3-P2 released --- 3621. [security] Incorrect bounds checking on private type 'keydata' can lead to a remotely triggerable REQUIRE failure (CVE-2013-4854). [RT #34238] - --- 9.8.5-P1 released --- + --- 9.9.3-P1 released --- 3584. [security] Caching data from an incompletely signed zone could trigger an assertion failure in resolver.c [RT #33690] - --- 9.8.5 released --- + --- 9.9.3 released --- 3568. [cleanup] Add a product description line to the version file, to be reported by named -v/-V. [RT #33366] @@ -21,7 +21,7 @@ 3561. [bug] dig: issue a warning if an EDNS query returns FORMERR or NOTIMP. Adjust usage message. [RT #33363] - --- 9.8.5rc1 released --- + --- 9.9.3rc2 released --- 3560. [bug] isc-config.sh did not honor includedir and libdir when set via configure. [RT #33345] @@ -31,6 +31,8 @@ 3558. [bug] IXFR of a DLZ stored zone was broken. [RT #33331] +3557. [bug] Reloading redirect zones was broken. [RT #33292] + 3556. [maint] Added AAAA for D.ROOT-SERVERS.NET. 3555. [bug] Address theoretical race conditions in acache.c @@ -51,9 +53,7 @@ 3547. [bug] Some malformed unknown rdata records were not properly detected and rejected. [RT #33129] -3056. [func] Added support for URI resource record. [RT #23386] - - --- 9.8.5rc1 released --- + --- 9.9.3rc1 released --- 3546. [func] Add EUI48 and EUI64 types. [RT #33082] @@ -64,8 +64,6 @@ 3543. [bug] Update socket structure before attaching to socket manager after accept. [RT #33084] -3542. [bug] masterformat system test was broken. [RT #33086] - 3541. [bug] Parts of libdns were not properly initialized when built in libexport mode. [RT #33028] @@ -94,6 +92,17 @@ 3530. [contrib] Better RTT tracking in queryperf. [RT #30128] +3528. [func] New "dnssec-coverage" command scans the timing + metadata for a set of DNSSEC keys and reports if a + lapse in signing coverage has been scheduled + inadvertently. (Note: This tool depends on python; + it will not be built or installed on systems that + do not have a python interpreter.) [RT #28098] + +3527. [compat] Add a URI to allow applications to explicitly + request a particular XML schema from the statistics + channel, returning 404 if not supported. [RT #32481] + 3526. [cleanup] Set up dependencies for unit tests correctly during build. [RT #32803] @@ -102,7 +111,7 @@ 3520. [bug] 'mctx' was not being referenced counted in some places where it should have been. [RT #32794] - --- 9.8.5b2 released --- + --- 9.9.3b2 released --- 3517. [bug] Reorder destruction to avoid shutdown race. [RT #32777] @@ -114,6 +123,8 @@ to 1024 bits for hmac-sha384 and hmac-sha512. [RT #32753] +3511. [doc] Improve documentation of redirect zones. [RT #32756] + 3509. [cleanup] Added a product line to version file to allow for easy naming of different products (BIND vs BIND ESV, for example). [RT #32755] @@ -121,8 +132,24 @@ 3508. [contrib] queryperf was incorrectly rejecting the -T option. [RT #32338] +3507. [bug] Statistics channel XSL (when built with + --enable-newstats) had a glitch when attempting + to chart query data before any queries had been + received. [RT #32620] + +3505. [bug] When setting "max-cache-size" and "max-acache-size", + larger values than 4 gigabytes could not be set + explicitly, though larger sizes were available + when setting cache size to 0. This has been + corrected; the full range is now available. + [RT #32358] + 3503. [doc] Clarify size_spec syntax. [RT #32449] +3501. [func] zone-statistics now takes three options: full, + terse, and none. "yes" and "no" are retained as + synonyms for full and terse, respectively. [RT #29165] + 3500. [security] Support NAPTR regular expression validation on all platforms without using libregex, which can be vulnerable to memory exhaustion attack @@ -141,6 +168,15 @@ NSIP and NSDNAME checking. --enable-rpz-nsip and --enable-rpz-nsdname are now the default. [RT #32251] +3493. [contrib] Added BDBHPT dynamically-lodable DLZ module, + contributed by Mark Goldfinch. [RT #32549] + +3492. [bug] Fixed a regression in zone loading performance + due to lock contention. [RT #30399] + +3491. [bug] Slave zones using inline-signing must specify a + file name. [RT #31946] + 3489. [bug] --enable-developer now turns on ISC_LIST_CHECKINIT. When cloning a rdataset do not copy the link contents. [RT #32651] @@ -156,8 +192,14 @@ 3485. [cleanup] Only compile openssl_gostlink.c if we support GOST. +3483. [bug] Corrected XSL code in use with --enable-newstats. + [RT #32587] + 3481. [cleanup] Removed use of const const in atf. +3480. [bug] Silence logging noise when setting up zone + statistics. [RT #32525] + 3479. [bug] Address potential memory leaks in gssapi support code. [RT #32405] @@ -167,10 +209,18 @@ 3474. [bug] nsupdate could assert when the local and remote address families didn't match. [RT #22897] +3473. [bug] dnssec-signzone/verify could incorrectly report + an error condition due to an empty node above an + opt-out delegation lacking an NSEC3. [RT #32072] + +3471. [bug] The number of UDP dispatches now defaults to + the number of CPUs even if -n has been set to + a higher value. [RT #30964] + 3470. [bug] Slave zones could fail to dump when successfully refreshing after an initial failure. [RT #31276] - --- 9.8.5b1 released --- + --- 9.9.3b1 released --- 3468. [security] RPZ rules to generate A records (but not AAAA records) could trigger an assertion failure when used in @@ -179,6 +229,9 @@ 3467. [bug] Added checks in dnssec-keygen and dnssec-settime to check for delete date < inactive date. [RT #31719] +3466. [contrib] Corrected the DNS_CLIENTINFOMETHODS_VERSION check + in DLZ example driver. [RT #32275] + 3465. [bug] Handle isolated reserved ports. [RT #31778] 3464. [maint] Updates to PKCS#11 openssl patches, supporting @@ -192,6 +245,8 @@ 3461. [bug] Negative responses could incorrectly have AD=1 set. [RT #32237] +3460. [bug] Only link against readline where needed. [RT #29810] + 3458. [bug] Return FORMERR when presented with a overly long domain named in a request. [RT #29682] @@ -203,6 +258,9 @@ 3454. [port] sparc64: improve atomic support. [RT #25182] +3453. [bug] 'rndc addzone' of a zone with 'inline-signing yes;' + failed. [RT #31960] + 3452. [bug] Accept duplicate singleton records. [RT #32329] 3451. [port] Increase per thread stack size from 64K to 1M. @@ -266,9 +324,19 @@ 3427. [bug] dig +trace incorrectly displayed name server addresses instead of names. [RT #31641] +3426. [bug] dnssec-checkds: Clearer output when records are not + found. [RT #31968] + 3425. [bug] "acacheentry" reference counting was broken resulting in use after free. [RT #31908] +3424. [func] dnssec-dsfromkey now emits the hash without spaces. + [RT #31951] + +3423. [bug] "rndc signing -nsec3param" didn't accept the full + range of possible values. Address portability issues. + [RT #31938] + 3422. [bug] Added a clear error message for when the SOA does not match the referral. [RT #31281] @@ -279,9 +347,22 @@ 3419. [bug] Memory leak on validation cancel. [RT #31869] +3417. [func] Optional new XML schema (version 3.0) for the + statistics channel adds query type statistics at the + zone level, and flattens the XML tree and uses + compressed format to optimize parsing. Includes new XSL + that permits charting via the Google Charts API on + browsers that support javascript in XSL. To enable, + build with "configure --enable-newstats". [RT #30023] + +3416. [bug] Named could die on shutdown if running with 128 UDP + dispatches per interface. [RT #31743] + 3415. [bug] named could die with a REQUIRE failure if a validation was canceled. [RT #31804] +3414. [bug] Address locking issues found by Coverity. [RT #31626] + 3412. [bug] Copy timeval structure from control message data. [RT #31548] @@ -295,6 +376,11 @@ (DNS-based Authentication of Named Entities). [RT #30513] +3408. [bug] Some DNSSEC-related options (update-check-ksk, + dnssec-loadkeys-interval, dnssec-dnskey-kskonly) + are now legal in slave zones as long as + inline-signing is in use. [RT #31078] + 3406. [bug] mem.c: Fix compilation errors when building with ISC_MEM_TRACKLINES or ISC_MEMPOOL_NAMES disabled. Also, ISC_MEM_DEBUG is no longer optional. [RT #31559] @@ -316,6 +402,13 @@ in the "srcid" file in the build tree and normally set to the most recent git hash. [RT #31494] +3399. [port] netbsd: rename 'bool' parameter to avoid namespace + clash. [RT #31515] + +3398. [bug] SOA parameters were not being updated with inline + signed zones if the zone was modified while the + server was offline. [RT #29272] + 3397. [bug] dig crashed when using +nssearch with +tcp. [RT #25298] 3396. [bug] OPT records were incorrectly removed from signed, @@ -348,11 +441,10 @@ 3386. [bug] Address locking violation when generating new NSEC / NSEC3 chains. [RT #31224] -3384. [bug] Improved logging of crypto errors. [RT #30963] +3385. [bug] named-checkconf didn't detect missing master lists + in also-notify clauses. [RT #30810] -3383. [security] A certain combination of records in the RBT could - cause named to hang while populating the additional - section of a response. [RT #31090] +3384. [bug] Improved logging of crypto errors. [RT #30963] 3382. [bug] SOA query from slave used use-v6-udp-ports range, if set, regardless of the address family in use. @@ -370,6 +462,9 @@ 3378. [bug] Handle missing 'managed-keys-directory' better. [RT #30625] +3377. [bug] Removed spurious newline from NSEC3 multiline + output. [RT #31044] + 3376. [bug] Lack of EDNS support was being recorded without a successful response. [RT #30811] @@ -386,19 +481,34 @@ add NS RRsets to the additional section or not. [RT #30479] - --- 9.8.4 released --- +3316. [tuning] Improved locking performance when recursing. + [RT #28836] + +3315. [tuning] Use multiple dispatch objects for sending upstream + queries; this can improve performance on busy + multiprocessor systems by reducing lock contention. + [RT #28605] + + --- 9.9.2 released --- + +3383. [security] A certain combination of records in the RBT could + cause named to hang while populating the additional + section of a response. [RT #31090] 3373. [bug] win32: open raw files in binary mode. [RT #30944] 3364. [security] Named could die on specially crafted record. [RT #30416] - --- 9.8.4rc1 released --- + --- 9.9.2rc1 released --- + +3370. [bug] Address use after free while shutting down. [RT #30241] 3369. [bug] nsupdate terminated unexpectedly in interactive mode if built with readline support. [RT #29550] -3368. [bug] and were not C++ safe. +3368. [bug] , and + were not C++ safe. 3367. [bug] dns_dnsseckey_create() result was not being checked. [RT #30685] @@ -417,6 +527,9 @@ could trigger an assertion failure on startup. [RT #27730] +3361. [bug] "rndc signing -nsec3param" didn't work correctly + when salt was set to '-' (no salt). [RT #30099] + 3360. [bug] 'host -w' could die. [RT #18723] 3359. [bug] An improperly-formed TSIG secret could cause a @@ -428,10 +541,12 @@ approaching their expiry, so they don't remain in caches after expiry. [RT #26429] - --- 9.8.4b1 released --- +3355. [port] Use more portable awk in verify system test. 3354. [func] Improve OpenSSL error logging. [RT #29932] + --- 9.9.2b1 released --- + 3353. [bug] Use a single task for task exclusive operations. [RT #29872] @@ -446,6 +561,8 @@ ISC_MEM_DEBUGCTX memory debugging flag is set. [RT #30240] +3349. [bug] Change #3345 was incomplete. [RT #30233] + 3348. [bug] Prevent RRSIG data from being cached if a negative record matching the covering type exists at a higher trust level. Such data already can't be retrieved from @@ -459,16 +576,42 @@ 3346. [security] Bad-cache data could be used before it was initialized, causing an assert. [RT #30025] +3345. [bug] Addressed race condition when removing the last item + or inserting the first item in an ISC_QUEUE. + [RT #29539] + +3344. [func] New "dnssec-checkds" command checks a zone to + determine which DS records should be published + in the parent zone, or which DLV records should be + published in a DLV zone, and queries the DNS to + ensure that it exists. (Note: This tool depends + on python; it will not be built or installed on + systems that do not have a python interpreter.) + [RT #28099] + 3342. [bug] Change #3314 broke saving of stub zones to disk resulting in excessive cpu usage in some cases. [RT #29952] +3341. [func] New "dnssec-verify" command checks a signed zone + to ensure correctness of signatures and of NSEC/NSEC3 + chains. [RT #23673] + +3339. [func] Allow the maximum supported rsa exponent size to be + specified: "max-rsa-exponent-size ;" [RT #29228] + +3338. [bug] Address race condition in units tests: asyncload_zone + and asyncload_zt. [RT #26100] + 3337. [bug] Change #3294 broke support for the multiple keys in controls. [RT #29694] 3335. [func] nslookup: return a nonzero exit code when unable to get an answer. [RT #29492] +3334. [bug] Hold a zone table reference while performing a + asynchronous load of a zone. [RT #28326] + 3333. [bug] Setting resolver-query-timeout too low can cause named to not recover if it loses connectivity. [RT #29623] @@ -504,7 +647,7 @@ 3317. [func] Add ECDSA support (RFC 6605). [RT #21918] - --- 9.8.3 released --- + --- 9.9.1 released --- 3318. [tuning] Reduce the amount of work performed while holding a bucket lock when finished with a fetch context. @@ -536,6 +679,8 @@ 3304. [bug] Use hmctx, not mctx when freeing rbtdb->heaps. [RT #28571] +3303. [bug] named could die when reloading. [RT #28606] + 3302. [bug] dns_dnssec_findmatchingkeys could fail to find keys if the zone name contained character that required special mappings. [RT #28600] @@ -549,22 +694,15 @@ 3299. [bug] Make SDB handle errors from database drivers better. [RT #28534] -3232. [bug] Zero zone->curmaster before return in - dns_zone_setmasterswithkeys(). [RT #26732] - -3183. [bug] Added RTLD_GLOBAL flag to dlopen call. [RT #26301] - -3197. [bug] Don't try to log the filename and line number when - the config parser can't open a file. [RT #22263] - - --- 9.8.2 released --- - 3298. [bug] Named could dereference a NULL pointer in zmgr_start_xfrin_ifquota if the zone was being removed. [RT #28419] 3297. [bug] Named could die on a malformed master file. [RT #28467] +3296. [bug] Named could die with a INSIST failure in + client.c:exit_check. [RT #28346] + 3295. [bug] Adjust isc_time_secondsastimet range check to be more portable. [RT # 26542] @@ -576,6 +714,16 @@ 3290. [bug] was not being installed. [RT #28169] +3273. [bug] AAAA responses could be returned in the additional + section even when filter-aaaa-on-v4 was in use. + [RT #27292] + + --- 9.9.0 released --- + + --- 9.9.0rc4 released --- + +3289. [bug] 'rndc retransfer' failed for inline zones. [RT #28036] + 3288. [bug] dlz_destroy() function wasn't correctly registered by the DLZ dlopen driver. [RT #28056] @@ -584,7 +732,7 @@ 3286. [bug] Managed key maintenance timer could fail to start after 'rndc reconfig'. [RT #26786] - --- 9.8.2rc2 released --- + --- 9.9.0rc3 released --- 3285. [bug] val-frdataset was incorrectly disassociated in proveunsecure after calling startfinddlvsep. @@ -607,24 +755,34 @@ 3280. [bug] Potential double free of a rdataset on out of memory with DNS64. [RT #27762] +3279. [bug] Hold a internal reference to the zone while performing + a asynchronous load. Address potential memory leak + if the asynchronous is cancelled. [RT #27750] + 3278. [bug] Make sure automatic key maintenance is started when "auto-dnssec maintain" is turned on during "rndc reconfig". [RT #26805] +3277. [bug] win32: isc_socket_dup is not implemented. [RT #27696] + 3276. [bug] win32: ns_os_openfile failed to return NULL on safe_open failure. [RT #27696] -3274. [bug] Log when a zone is not reusable. Only set loadtime - on successful loads. [RT #27650] - -3273. [bug] AAAA responses could be returned in the additional - section even when filter-aaaa-on-v4 was in use. - [RT #27292] +3275. [bug] Corrected rndc -h output; the 'rndc sync -clean' + option had been misspelled as '-clear'. (To avoid + future confusion, both options now work.) [RT #27173] 3271. [port] darwin: mksymtbl is not always stable, loop several times before giving up. mksymtbl was using non portable perl to covert 64 bit hex strings. [RT #27653] + --- 9.9.0rc2 released --- + +3270. [bug] "rndc reload" didn't reuse existing zones correctly + when inline-signing was in use. [RT #27650] + +3269. [port] darwin 11 and later now built threaded by default. + 3268. [bug] Convert RRSIG expiry times to 64 timestamps to work out the earliest expiry time. [RT #23311] @@ -636,14 +794,26 @@ DNSKEY RRset was not being properly computed. [RT #26543] +3265. [bug] Corrected a problem with lock ordering in the + inline-signing code. [RT #27557] + +3264. [bug] Automatic regeneration of signatures in an + inline-signing zone could stall when the server + was restarted. [RT #27344] + +3263. [bug] "rndc sync" did not affect the unsigned side of an + inline-signing zone. [RT #27337] + 3262. [bug] Signed responses were handled incorrectly by RPZ. [RT #27316] - --- 9.8.2rc1 released --- +3261. [func] RRset ordering now defaults to random. [RT #27174] 3260. [bug] "rrset-order cyclic" could appear not to rotate for some query patterns. [RT #27170/27185] + --- 9.9.0rc1 released --- + 3259. [bug] named-compilezone: Suppress "dump zone to " message when writing to stdout. [RT #27109] @@ -655,12 +825,21 @@ 3256. [bug] Disable empty zones for lwresd -C. [RT #27139] +3255. [func] No longer require that a empty zones be explicitly + enabled or that a empty zone is disabled for + RFC 1918 empty zones to be configured. [RT #27139] + 3254. [bug] Set isc_socket_ipv6only() on the IPv6 control channels. [RT #22249] 3253. [bug] Return DNS_R_SYNTAX when the input to a text field is too long. [RT #26956] +3252. [bug] When master zones using inline-signing were + updated while the server was offline, the source + zone could fall out of sync with the signed + copy. They can now resynchronize. [RT #26676] + 3251. [bug] Enforce a upper bound (65535 bytes) on the amount of memory dns_sdlz_putrr() can allocate per record to prevent run away memory consumption on ISC_R_NOSPACE. @@ -680,8 +859,34 @@ 3247. [bug] 'raw' format zones failed to preserve load order breaking 'fixed' sort order. [RT #27087] -3243. [port] netbsd,bsdi: the thread defaults were not being - properly set. +3246. [bug] Named failed to start with a empty also-notify list. + [RT #27087] + +3245. [bug] Don't report a error unchanged serials unless there + were other changes when thawing a zone with + ixfr-fromdifferences. [RT #26845] + +3244. [func] Added readline support to nslookup and nsupdate. + Also simplified nsupdate syntax to make "update" + and "prereq" optional. [RT #24659] + +3243. [port] freebsd,netbsd,bsdi: the thread defaults were not + being properly set. + +3242. [func] Extended the header of raw-format master files to + include the serial number of the zone from which + they were generated, if different (as in the case + of inline-signing zones). This is to be used in + inline-signing zones, to track changes between the + unsigned and signed versions of the zone, which may + have different serial numbers. + + (Note: raw zonefiles generated by this version of + BIND are no longer compatible with prior versions. + To generate a backward-compatible raw zonefile + using dnssec-signzone or named-compilezone, specify + output format "raw=0" instead of simply "raw".) + [RT #26587] 3241. [bug] Address race conditions in the resolver code. [RT #26889] @@ -696,10 +901,21 @@ 3237. [bug] dig -6 didn't work with +trace. [RT #26906] - --- 9.8.2b1 released --- +3236. [bug] Backed out changes #3182 and #3202, related to + EDNS(0) fallback behavior. [RT #26416] + +3235. [func] dns_db_diffx, a extended dns_db_diff which returns + the generated diff and optionally writes it to a + journal. [RT #26386] 3234. [bug] 'make depend' produced invalid makefiles. [RT #26830] +3233. [bug] 'rndc freeze/thaw' didn't work for inline zones. + [RT #26632] + +3232. [bug] Zero zone->curmaster before return in + dns_zone_setmasterswithkeys(). [RT #26732] + 3231. [bug] named could fail to send a incompressible zone. [RT #26796] @@ -717,14 +933,29 @@ 3226. [bug] Address minor resource leakages. [RT #26624] +3225. [bug] Silence spurious "setsockopt(517, IPV6_V6ONLY) failed" + messages. [RT #26507] + +3224. [bug] 'rndc signing' argument parsing was broken. [RT #26684] + +3223. [bug] 'task_test privilege_drop' generated false positives. + [RT #26766] + +3222. [cleanup] Replace dns_journal_{get,set}_bitws with + dns_journal_{get,set}_sourceserial. [RT #26634] + 3221. [bug] Fixed a potential core dump on shutdown due to referencing fetch context after it's been freed. [RT #26720] + --- 9.9.0b2 released --- + 3220. [bug] Change #3186 was incomplete; dns_db_rpz_findips() could fail to set the database version correctly, causing an assertion failure. [RT #26180] +3219. [bug] Disable NOEDNS caching following a timeout. + 3218. [security] Cache lookup could return RRSIG data associated with nonexistent records, leading to an assertion failure. [RT #26590] @@ -733,12 +964,24 @@ 3216. [bug] resolver.c:validated() was not thread-safe. [RT #26478] +3215. [bug] 'rndc recursing' could cause a core dump. [RT #26495] + +3214. [func] Add 'named -U' option to set the number of UDP + listener threads per interface. [RT #26485] + 3213. [doc] Clarify ixfr-from-differences behavior. [RT #25188] 3212. [bug] rbtdb.c: failed to remove a node from the deadnodes list prior to adding a reference to it leading a possible assertion failure. [RT #23219] +3211. [func] dnssec-signzone: "-f -" prints to stdout; "-O full" + option prints in single-line-per-record format. + [RT #20287] + +3210. [bug] Canceling the oldest query due to recursive-client + overload could trigger an assertion failure. [RT #26463] + 3209. [func] Add "dnssec-lookaside 'no'". [RT #24858] 3208. [bug] 'dig -y' handle unknown tsig algorithm better. @@ -748,6 +991,11 @@ 3206. [cleanup] Add ISC information to log at start time. [RT #25484] +3205. [func] Upgrade dig's defaults to better reflect modern + nameserver behavior. Enable "dig +adflag" and + "dig +edns=0" by default. Enable "+dnssec" when + running "dig +trace". [RT #23497] + 3204. [bug] When a master server that has been marked as unreachable sends a NOTIFY, mark it reachable again. [RT #25960] @@ -755,12 +1003,24 @@ 3203. [bug] Increase log level to 'info' for validation failures from expired or not-yet-valid RRSIGs. [RT #21796] +3202. [bug] NOEDNS caching on timeout was too aggressive. + [RT #26416] + +3201. [func] 'rndc querylog' can now be given an on/off parameter + instead of only being used as a toggle. [RT #18351] + 3200. [doc] Some rndc functions were undocumented or were missing from 'rndc -h' output. [RT #25555] +3199. [func] When logging client information, include the name + being queried. [RT #25944] + 3198. [doc] Clarified that dnssec-settime can alter keyfile permissions. [RT #24866] +3197. [bug] Don't try to log the filename and line number when + the config parser can't open a file. [RT #22263] + 3196. [bug] nsupdate: return nonzero exit code when target zone doesn't exist. [RT #25783] @@ -789,10 +1049,50 @@ 3187. [port] win32: support for Visual Studio 2008. [RT #26356] + --- 9.9.0b1 released --- + 3186. [bug] Version/db mis-match in rpz code. [RT #26180] +3185. [func] New 'rndc signing' option for auto-dnssec zones: + - 'rndc signing -list' displays the current + state of signing operations + - 'rndc signing -clear' clears the signing state + records for keys that have fully signed the zone + - 'rndc signing -nsec3param' sets the NSEC3 + parameters for the zone + The 'rndc keydone' syntax is removed. [RT #23729] + +3184. [bug] named had excessive cpu usage when a redirect zone was + configured. [RT #26013] + +3183. [bug] Added RTLD_GLOBAL flag to dlopen call. [RT #26301] + +3182. [bug] Auth servers behind firewalls which block packets + greater than 512 bytes may cause other servers to + perform poorly. Now, adb retains edns information + and caches noedns servers. [RT #23392/24964] + +3181. [func] Inline-signing is now supported for master zones. + [RT #26224] + +3180. [func] Local copies of slave zones are now saved in raw + format by default, to improve startup performance. + 'masterfile-format text;' can be used to override + the default, if desired. [RT #25867] + 3179. [port] kfreebsd: build issues. [RT #26273] +3178. [bug] A race condition introduced by change #3163 could + cause an assertion failure on shutdown. [RT #26271] + +3177. [func] 'rndc keydone', remove the indicator record that + named has finished signing the zone with the + corresponding key. [RT #26206] + +3176. [doc] Corrected example code and added a README to the + sample external DLZ module in contrib/dlz/example. + [RT #26215] + 3175. [bug] Fix how DNSSEC positive wildcard responses from a NSEC3 signed zone are validated. Stop sending a unnecessary NSEC3 record when generating such @@ -803,9 +1103,14 @@ 3173. [port] Correctly validate root DS responses. [RT #25726] +3172. [port] darwin 10.* and freebsd [89] are now built threaded by + default. + 3171. [bug] Exclusively lock the task when adding a zone using 'rndc addzone'. [RT #25600] + --- 9.9.0a3 released --- + 3170. [func] RPZ update: - fix precedence among competing rules - improve ARM text including documenting rule precedence @@ -820,10 +1125,28 @@ 3169. [func] Catch db/version mis-matches when calling dns_db_*(). [RT #26017] +3168. [bug] Nxdomain redirection could trigger an assert with + a ANY query. [RT #26017] + 3167. [bug] Negative answers from forwarders were not being correctly tagged making them appear to not be cached. [RT #25380] +3166. [bug] Upgrading a zone to support inline-signing failed. + [RT #26014] + +3165. [bug] dnssec-signzone could generate new signatures when + resigning, even when valid signatures were already + present. [RT #26025] + +3164. [func] Enable DLZ modules to retrieve client information, + so that responses can be changed depending on the + source address of the query. [RT #25768] + +3163. [bug] Use finer-grained locking in client.c to address + concurrency problems with large numbers of threads. + [RT #26044] + 3162. [test] start.pl: modified to allow for "named.args" in ns*/ subdirectory to override stock arguments to named. Largely from RT#26044, but no separate ticket. @@ -831,24 +1154,52 @@ 3161. [bug] zone.c:del_sigs failed to always reset rdata leading assertion failures. [RT #25880] +3160. [bug] When printing out a NSEC3 record in multiline form + the newline was not being printed causing type codes + to be run together. [RT #25873] + +3159. [bug] On some platforms, named could assert on startup + when running in a chrooted environment without + /proc. [RT #25863] + +3158. [bug] Recursive servers would prefer a particular UDP + socket instead of using all available sockets. + [RT #26038] + 3157. [tuning] Reduce the time spent in "rndc reconfig" by parsing the config file before pausing the server. [RT #21373] +3156. [placeholder] + + --- 9.9.0a2 released --- + 3155. [bug] Fixed a build failure when using contrib DLZ drivers (e.g., mysql, postgresql, etc). [RT #25710] 3154. [bug] Attempting to print an empty rdataset could trigger an assert. [RT #25452] +3153. [func] Extend request-ixfr to zone level and remove the + side effect of forcing an AXFR. [RT #25156] + 3152. [cleanup] Some versions of gcc and clang failed due to incorrect use of __builtin_expect. [RT #25183] 3151. [bug] Queries for type RRSIG or SIG could be handled incorrectly. [RT #21050] +3150. [func] Improved startup and reconfiguration time by + enabling zones to load in multiple threads. [RT #25333] + +3149. [placeholder] + 3148. [bug] Processing of normal queries could be stalled when forwarding a UPDATE message. [RT #24711] +3147. [func] Initial inline signing support. [RT #23657] + + --- 9.9.0a1 released --- + 3146. [test] Fixed gcc4.6.0 errors in ATF. [RT #25598] 3145. [test] Capture output of ATF unit tests in "./atf.out" if @@ -859,29 +1210,31 @@ 3143. [bug] Silence clang compiler warnings. [RT #25174] -3139. [test] Added tests from RFC 6234, RFC 2202, and RFC 1321 - for the hashing algorithms (md5, sha1 - sha512, and - their hmac counterparts). [RT #25067] - - --- 9.8.1 released --- - - --- 9.8.1rc1 released --- +3142. [bug] NAPTR is class agnostic. [RT #25429] 3141. [bug] Silence spurious "zone serial (0) unchanged" messages associated with empty zones. [RT #25079] +3140. [func] New command "rndc flushtree " clears the + specified name from the server cache along with + all names under it. [RT #19970] + +3139. [test] Added tests from RFC 6234, RFC 2202, and RFC 1321 + for the hashing algorithms (md5, sha1 - sha512, and + their hmac counterparts). [RT #25067] + 3138. [bug] Address memory leaks and out-of-order operations when shutting named down. [RT #25210] +3137. [func] Improve hardware scalability by allowing multiple + worker threads to process incoming UDP packets. + This can significantly increase query throughput + on some systems. [RT #22992] + 3136. [func] Add RFC 1918 reverse zones to the list of built-in empty zones switched on by the 'empty-zones-enable' option. [RT #24990] - Note: empty-zones-enable must be "yes;" or a empty - zone needs to be disabled in named.conf for RFC 1918 - zones to be activated. This requirement may be - removed in future releases. - 3135. [port] FreeBSD: workaround broken IPV6_USE_MIN_MTU processing. See http://www.freebsd.org/cgi/query-pr.cgi?pr=158307 [RT #24950] @@ -889,19 +1242,34 @@ 3134. [bug] Improve the accuracy of dnssec-signzone's signing statistics. [RT #16030] - --- 9.8.1b3 released --- - 3133. [bug] Change #3114 was incomplete. [RT #24577] +3132. [placeholder] + 3131. [tuning] Improve scalability by allocating one zone task per 100 zones at startup time, rather than using a fixed-size task table. [RT #24406] +3130. [func] Support alternate methods for managing a dynamic + zone's serial number. Two methods are currently + defined using serial-update-method, "increment" + (default) and "unixtime". [RT #23849] + 3129. [bug] Named could crash on 'rndc reconfig' when allow-new-zones was set to yes and named ACLs were used. [RT #22739] - --- 9.8.1b2 released --- +3128. [func] Inserting an NSEC3PARAM via dynamic update in an + auto-dnssec zone that has not been signed yet + will cause it to be signed with the specified NSEC3 + parameters when keys are activated. The + NSEC3PARAM record will not appear in the zone until + it is signed, but the parameters will be stored. + [RT #23684] + +3127. [bug] 'rndc thaw' will now remove a zone's journal file + if the zone serial number has been changed and + ixfr-from-differences is not in use. [RT #24687] 3126. [security] Using DNAME record to generate replacements caused RPZ to exit with a assertion failure. [RT #24766] @@ -941,6 +1309,12 @@ never-implemented 'auto-dnssec create' option. [RT #24533] +3116. [func] New 'dnssec-update-mode' option controls updates + of DNSSEC records in signed dynamic zones. Set to + 'no-resign' to disable automatic RRSIG regeneration + while retaining the ability to sign new or changed + data. [RT #24533] + 3115. [bug] Named could fail to return requested data when following a CNAME that points into the same zone. [RT #24455] @@ -951,8 +1325,6 @@ 3113. [doc] Document the relationship between serial-query-rate and NOTIFY messages. - --- 9.8.1b1 released --- - 3112. [doc] Add missing descriptions of the update policy name types "ms-self", "ms-subdomain", "krb5-self" and "krb5-subdomain", which allow machines to update @@ -965,9 +1337,23 @@ 3110. [bug] dnssec-signzone: Wrong error message could appear when attempting to sign with no KSK. [RT #24369] +3109. [func] The also-notify option now uses the same syntax + as a zone's masters clause. This means it is + now possible to specify a TSIG key to use when + sending notifies to a given server, or to include + an explicit named masters list in an also-notfiy + statement. [RT #23508] + +3108. [cleanup] dnssec-signzone: Clarified some error and + warning messages; removed #ifdef ALLOW_KSKLESS_ZONES + code (use -P instead). [RT #20852] + 3107. [bug] dnssec-signzone: Report the correct number of ZSKs when using -x. [RT #20852] +3106. [func] When logging client requests, include the name of + the TSIG key if any. [RT #23619] + 3105. [bug] GOST support can be suppressed by "configure --without-gost" [RT #24367] @@ -977,6 +1363,12 @@ instead of in the options statement could trigger an assertion failure in named-checkconf. [RT #24382] +3102. [func] New 'dnssec-loadkeys-interval' option configures + how often, in minutes, to check the key repository + for updates when using automatic key maintenance. + Default is every 60 minutes (formerly hard-coded + to 12 hours). [RT #23744] + 3101. [bug] Zones using automatic key maintenance could fail to check the key repository for updates. [RT #23744] @@ -1012,6 +1404,9 @@ 3090. [func] Make --with-gssapi default [RT #23738] +3089. [func] dnssec-dsfromkey now supports reading keys from + standard input "dnssec-dsfromkey -f -". [RT# 20662] + 3088. [bug] Remove bin/tests/system/logfileconfig/ns1/named.conf and add setup.sh in order to resolve changing named.conf issue. [RT #23687] @@ -1024,6 +1419,17 @@ other change has been specified, using "-P now -A now" as default values. [RT #22474] +3085. [func] New '-R' option in dnssec-signzone forces removal + of signatures which have not yet expired but + were generated by a key that no longer exists. + [RT #22471] + +3084. [func] A new command "rndc sync" dumps pending changes in + a dynamic zone to disk; "rndc sync -clean" also + removes the journal file after syncing. Also, + "rndc freeze" no longer removes journal files. + [RT #22473] + 3083. [bug] NOTIFY messages were not being sent when generating a NSEC3 chain incrementally. [RT #23702] @@ -1044,6 +1450,11 @@ 3077. [bug] zone.c:zone_refreshkeys() incorrectly called dns_zone_attach(), use zone->irefs instead. [RT #23303] +3076. [func] New '-L' option in dnssec-keygen, dnsset-settime, and + dnssec-keyfromlabel sets the default TTL of the + key. When possible, automatic signing will use that + TTL when the key is published. [RT #23304] + 3075. [bug] dns_dnssec_findzonekeys{2} used a inconsistent timestamp when determining which keys are active. [RT #23642] @@ -1076,7 +1487,7 @@ 3066. [func] The DLZ "dlopen" driver is now built by default, no longer requiring a configure option. To disable it, use "configure --without-dlopen". - (Note: driver not supported on win32.) [RT #23467] + Driver also supported on win32. [RT #23467] 3065. [bug] RRSIG could have time stamps too far in the future. [RT #23356] @@ -1086,6 +1497,25 @@ 3063. [contrib] More verbose error reporting from DLZ LDAP. [RT #23402] +3062. [func] Made several changes to enhance human readability + of DNSSEC data in dig output and in generated + zone files: + - DNSKEY record comments are more verbose, no + longer used in multiline mode only + - multiline RRSIG records reformatted + - multiline output mode for NSEC3PARAM records + - "dig +norrcomments" suppresses DNSKEY comments + - "dig +split=X" breaks hex/base64 records into + fields of width X; "dig +nosplit" disables this. + [RT #22820] + +3061. [func] New option "dnssec-signzone -D", only write out + generated DNSSEC records. [RT #22896] + +3060. [func] New option "dnssec-signzone -X " allows + specification of a separate expiration date + for DNSKEY RRSIGs and other RRSIGs. [RT #22141] + 3059. [test] Added a regression test for change #3023. 3058. [bug] Cause named to terminate at startup or rndc reconfig/ @@ -1095,6 +1525,10 @@ 3057. [bug] "rndc secroots" would abort after the first error and so could miss some views. [RT #23488] +3056. [func] Added support for URI resource record. [RT #23386] + +3055. [placeholder] + 3054. [bug] Added elliptic curve support check in GOST OpenSSL engine detection. [RT #23485] @@ -1123,6 +1557,8 @@ 3046. [bug] Use RRSIG original TTL to compute validated RRset and RRSIG TTL. [RT #23332] +3045. [removed] Replaced by change #3050. + 3044. [bug] Hold the socket manager lock while freeing the socket. [RT #23333] @@ -1143,6 +1579,8 @@ with a CNAME existed between the trust anchor and the top of the zone. [RT #23338] +3039. [func] Redirect on NXDOMAIN support. [RT #23146] + 3038. [bug] Install . [RT #23342] 3037. [doc] Update COPYRIGHT to contain all the individual @@ -1180,8 +1618,6 @@ after calling grow_headerspace() and if not re-call grow_headerspace() until we do. [RT #22521] - --- 9.8.0 released --- - 3025. [bug] Fixed a possible deadlock due to zone resigning. [RT #22964] @@ -1203,8 +1639,6 @@ 3019. [test] Test: check apex NSEC3 records after adding DNSKEY record via UPDATE. [RT #23229] - --- 9.8.0rc1 released --- - 3018. [bug] Named failed to check for the "none;" acl when deciding if a zone may need to be re-signed. [RT #23120] @@ -1216,6 +1650,8 @@ 3015. [port] win32: fix IN6_IS_ADDR_LINKLOCAL and IN6_IS_ADDR_SITELOCAL macros. [RT #22724] +3014. [placeholder] + 3013. [bug] The DNS64 ttl was not always being set as expected. [RT #23034] @@ -1223,7 +1659,8 @@ signing records for any remaining DNSKEY changes. [RT #22590] -3011. [func] Allow setting this in named.conf using the new +3011. [func] Change the default query timeout from 30 seconds + to 10. Allow setting this in named.conf using the new 'resolver-query-timeout' option, which specifies a max time in seconds. 0 means 'default' and anything longer than 30 will be silently set to 30. [RT #22852] diff --git a/contrib/bind9/COPYRIGHT b/contrib/bind9/COPYRIGHT index cc19db471b6..525c2228db3 100644 --- a/contrib/bind9/COPYRIGHT +++ b/contrib/bind9/COPYRIGHT @@ -13,7 +13,7 @@ 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. -$Id: COPYRIGHT,v 1.17.14.2 2012/01/04 23:46:18 tbox Exp $ +$Id: COPYRIGHT,v 1.19 2012/01/03 23:46:59 tbox Exp $ Portions of this code release fall under one or more of the following Copyright notices. Please see individual source diff --git a/contrib/bind9/HISTORY b/contrib/bind9/HISTORY index e98f9b41460..9a3b7599965 100644 --- a/contrib/bind9/HISTORY +++ b/contrib/bind9/HISTORY @@ -1,5 +1,57 @@ Summary of functional enhancements from prior major releases of BIND 9: +BIND 9.8.0 + + BIND 9.8.0 includes a number of changes from BIND 9.7 and earlier + releases. New features include: + + - Built-in trust anchor for the root zone, which can be + switched on via "dnssec-validation auto;" + - Support for DNS64. + - Support for response policy zones (RPZ). + - Support for writable DLZ zones. + - Improved ease of configuration of GSS/TSIG for + interoperability with Active Directory + - Support for GOST signing algorithm for DNSSEC. + - Removed RTT Banding from server selection algorithm. + - New "static-stub" zone type. + - Allow configuration of resolver timeouts via + "resolver-query-timeout" option. + - The DLZ "dlopen" driver is now built by default. + - Added a new include file with function typedefs + for the DLZ "dlopen" driver. + - Made "--with-gssapi" default. + - More verbose error reporting from DLZ LDAP. + +BIND 9.7.0 + + BIND 9.7.0 includes a number of changes from BIND 9.6 and earlier + releases. Most are intended to simplify DNSSEC configuration. + + New features include: + + - Fully automatic signing of zones by "named". + - Simplified configuration of DNSSEC Lookaside Validation (DLV). + - Simplified configuration of Dynamic DNS, using the "ddns-confgen" + command line tool or the "local" update-policy option. (As a side + effect, this also makes it easier to configure automatic zone + re-signing.) + - New named option "attach-cache" that allows multiple views to + share a single cache. + - DNS rebinding attack prevention. + - New default values for dnssec-keygen parameters. + - Support for RFC 5011 automated trust anchor maintenance + - Smart signing: simplified tools for zone signing and key + maintenance. + - The "statistics-channels" option is now available on Windows. + - A new DNSSEC-aware libdns API for use by non-BIND9 applications + - On some platforms, named and other binaries can now print out + a stack backtrace on assertion failure, to aid in debugging. + - A "tools only" installation mode on Windows, which only installs + dig, host, nslookup and nsupdate. + - Improved PKCS#11 support, including Keyper support and explicit + OpenSSL engine selection. + BIND 9.6.0 Full NSEC3 support diff --git a/contrib/bind9/Makefile.in b/contrib/bind9/Makefile.in index 4e41fe50974..7c1d66523fa 100644 --- a/contrib/bind9/Makefile.in +++ b/contrib/bind9/Makefile.in @@ -13,7 +13,7 @@ # OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR # PERFORMANCE OF THIS SOFTWARE. -# $Id: Makefile.in,v 1.58.250.4 2011/09/06 04:06:11 marka Exp $ +# $Id: Makefile.in,v 1.62 2011/09/06 04:06:37 marka Exp $ srcdir = @srcdir@ VPATH = @srcdir@ diff --git a/contrib/bind9/README b/contrib/bind9/README index 9d839b49fce..88d799e4f7c 100644 --- a/contrib/bind9/README +++ b/contrib/bind9/README @@ -51,119 +51,64 @@ BIND 9 For up-to-date release notes and errata, see http://www.isc.org/software/bind9/releasenotes -BIND 9.8.5 +BIND 9.9.3 - BIND 9.8.5 includes several bug fixes and patches security - flaws described in CVE-2012-5688, CVE-2012-5689 and CVE-2013-2266. + BIND 9.9.3 is a maintenance release and patches the security + flaws described in CVE-2012-5688, CVE-2012-5689 and CVE-2013-2266. -BIND 9.8.4 +BIND 9.9.2 - BIND 9.8.4 includes several bug fixes and patches security - flaws described in CVE-2012-1667, CVE-2012-3817 and CVE-2012-4244. + BIND 9.9.2 is a maintenance release and patches the security + flaw described in CVE-2012-4244. -BIND 9.8.3 +BIND 9.9.1 - BIND 9.8.3 is a maintenance release. + BIND 9.9.1 is a maintenance release. -BIND 9.8.2 +BIND 9.9.0 - BIND 9.8.2 includes a number of bug fixes and prevents a security - problem described in CVE-2011-4313 + BIND 9.9.0 includes a number of changes from BIND 9.8 and earlier + releases. New features include: -BIND 9.8.1 - - BIND 9.8.1 includes a number of bug fixes and enhancements from - BIND 9.8 and earlier releases. New features include: - - - The DLZ "dlopen" driver is now built by default. - - Added a new include file with function typedefs - for the DLZ "dlopen" driver. - - Made "--with-gssapi" default. - - More verbose error reporting from DLZ LDAP. - -BIND 9.8.0 - - BIND 9.8.0 includes a number of changes from BIND 9.7 and earlier - releases. New features include: - - - Built-in trust anchor for the root zone, which can be - switched on via "dnssec-validation auto;" - - Support for DNS64. - - Support for response policy zones (RPZ). - - Support for writable DLZ zones. - - Improved ease of configuration of GSS/TSIG for - interoperability with Active Directory - - Support for GOST signing algorithm for DNSSEC. - - Removed RTT Banding from server selection algorithm. - - New "static-stub" zone type. - - Allow configuration of resolver timeouts via - "resolver-query-timeout" option. - -BIND 9.7.0 - - BIND 9.7.0 includes a number of changes from BIND 9.6 and earlier - releases. Most are intended to simplify DNSSEC configuration. - - New features include: - - - Fully automatic signing of zones by "named". - - Simplified configuration of DNSSEC Lookaside Validation (DLV). - - Simplified configuration of Dynamic DNS, using the "ddns-confgen" - command line tool or the "local" update-policy option. (As a side - effect, this also makes it easier to configure automatic zone - re-signing.) - - New named option "attach-cache" that allows multiple views to - share a single cache. - - DNS rebinding attack prevention. - - New default values for dnssec-keygen parameters. - - Support for RFC 5011 automated trust anchor maintenance - - Smart signing: simplified tools for zone signing and key - maintenance. - - The "statistics-channels" option is now available on Windows. - - A new DNSSEC-aware libdns API for use by non-BIND9 applications - - On some platforms, named and other binaries can now print out - a stack backtrace on assertion failure, to aid in debugging. - - A "tools only" installation mode on Windows, which only installs - dig, host, nslookup and nsupdate. - - Improved PKCS#11 support, including Keyper support and explicit - OpenSSL engine selection. - - Known issues in this release: - - - In rare cases, DNSSEC validation can leak memory. When this - happens, it will cause an assertion failure when named exits, - but is otherwise harmless. A fix exists, but was too late for - this release; it will be included in BIND 9.7.1. - - Compatibility notes: - - - If you had built BIND 9.6 with any of ALLOW_NSEC3PARAM_UPDATE, - ALLOW_SECURE_TO_INSECURE or ALLOW_INSECURE_TO_SECURE defined, then - you should ensure that all changes that are in progress have - completed prior to upgrading to BIND 9.7. BIND 9.7 implements - those features in a way which is not backwards compatible. - - - Prior releases had a bug which caused HMAC-SHA* keys with long - secrets to be used incorrectly. Fixing this bug means that older - versions of BIND 9 may fail to interoperate with this version - when using TSIG keys. If this occurs, the new "isc-hmac-fixup" - tool will convert a key with a long secret into a form that works - correctly with all versions of BIND 9. See the "isc-hmac-fixup" - man page for additional details. - - - Revoking a DNSSEC key with "dnssec-revoke" changes its key ID. - It is possible for the new key ID to collide with that of a - different key. Newly generated keys will not have this problem, - as "dnssec-keygen" looks for potential collisions before - generating keys, but exercise caution if using key revokation - with keys that were generated by older versions of BIND 9. See - the Administrator's Reference Manual, section 4.10 ("Dynamic - Trust Anchor Management") for more details. - - - A bug was fixed in which a key's scheduled inactivity date was - stored incorectly. Users who participated in the 9.7.0 BETA test - and had DNSSEC keys with scheduled inactivity dates will need to - reset those keys' dates using "dnssec-settime -I". + - Inline signing, allowing automatic DNSSEC signing of + master zones without modification of the zonefile, or + "bump in the wire" signing in slaves. + - NXDOMAIN redirection. + - New 'rndc flushtree' command clears all data under a given + name from the DNS cache. + - New 'rndc sync' command dumps pending changes in a dynamic + zone to disk without a freeze/thaw cycle. + - New 'rndc signing' command displays or clears signing status + records in 'auto-dnssec' zones. + - NSEC3 parameters for 'auto-dnssec' zones can now be set prior + to signing, eliminating the need to initially sign with NSEC. + - Startup time improvements on large authoritative servers. + - Slave zones are now saved in raw format by default. + - Several improvements to response policy zones (RPZ). + - Improved hardware scalability by using multiple threads + to listen for queries and using finer-grained client locking + - The 'also-notify' option now takes the same syntax as + 'masters', so it can used named masterlists and TSIG keys. + - 'dnssec-signzone -D' writes an output file containing only DNSSEC + data, which can be included by the primary zone file. + - 'dnssec-signzone -R' forces removal of signatures that are + not expired but were created by a key which no longer exists. + - 'dnssec-signzone -X' allows a separate expiration date to + be specified for DNSKEY signatures from other signatures. + - New '-L' option to dnssec-keygen, dnssec-settime, and + dnssec-keyfromlabel sets the default TTL for the key. + - dnssec-dsfromkey now supports reading from standard input, + to make it easier to convert DNSKEY to DS. + - RFC 1918 reverse zones have been added to the empty-zones + table per RFC 6303. + - Dynamic updates can now optionally set the zone's SOA serial + number to the current UNIX time. + - DLZ modules can now retrieve the source IP address of + the querying client. + - 'request-ixfr' option can now be set at the per-zone level. + - 'dig +rrcomments' turns on comments about DNSKEY records, + indicating their key ID, algorithm and function + - Simplified nsupdate syntax and added readline support Building @@ -193,12 +138,12 @@ Building AIX 4.3, 5L CentOS 4, 4.5, 5 Darwin 9.0.0d1/ARM - Debian 4 - Fedora Core 5, 7 - FreeBSD 6.1 + Debian 4, 5, 6 + Fedora Core 5, 7, 8 + FreeBSD 6, 7, 8 HP-UX 11.23 PA - MacOS X 10.4, 10.5 - Red Hat Enterprise Linux 4, 5 + MacOS X 10.5, 10.6, 10.7 + Red Hat Enterprise Linux 4, 5, 6 SCO OpenServer 5.0.6 Slackware 9, 10 SuSE 9, 10 @@ -219,7 +164,8 @@ Building CFLAGS C compiler flags. Defaults to include -g and/or -O2 - as supported by the compiler. + as supported by the compiler. Please include '-g' + if you need to set CFLAGS. STD_CINCLUDES System header file directories. Can be used to specify @@ -336,6 +282,10 @@ Building libraries. sh-utils-1.16 provides a "printf" which compiles on SunOS 4. +Known limitations + + Linux requires kernel build 2.6.39 or later to get the + performance benefits from using multiple sockets. Documentation diff --git a/contrib/bind9/bin/Makefile.in b/contrib/bind9/bin/Makefile.in index 897c0f37ebb..87ca5b27056 100644 --- a/contrib/bind9/bin/Makefile.in +++ b/contrib/bind9/bin/Makefile.in @@ -20,7 +20,7 @@ VPATH = @srcdir@ top_srcdir = @top_srcdir@ SUBDIRS = named rndc dig dnssec tools tests nsupdate \ - check confgen @PKCS11_TOOLS@ + check confgen @PYTHON_TOOLS@ @PKCS11_TOOLS@ TARGETS = @BIND9_MAKE_RULES@ diff --git a/contrib/bind9/bin/check/check-tool.c b/contrib/bind9/bin/check/check-tool.c index 46cf2bf48a5..1e534071d0c 100644 --- a/contrib/bind9/bin/check/check-tool.c +++ b/contrib/bind9/bin/check/check-tool.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004-2010, 2012 Internet Systems Consortium, Inc. ("ISC") + * Copyright (C) 2004-2012 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 2000-2002 Internet Software Consortium. * * Permission to use, copy, modify, and/or distribute this software for any @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: check-tool.c,v 1.41 2010/09/07 23:46:59 tbox Exp $ */ +/* $Id: check-tool.c,v 1.44 2011/12/22 07:32:39 each Exp $ */ /*! \file */ @@ -638,7 +638,8 @@ load_zone(isc_mem_t *mctx, const char *zonename, const char *filename, /*% dump the zone */ isc_result_t dump_zone(const char *zonename, dns_zone_t *zone, const char *filename, - dns_masterformat_t fileformat, const dns_master_style_t *style) + dns_masterformat_t fileformat, const dns_master_style_t *style, + const isc_uint32_t rawversion) { isc_result_t result; FILE *output = stdout; @@ -664,8 +665,8 @@ dump_zone(const char *zonename, dns_zone_t *zone, const char *filename, } } - result = dns_zone_dumptostream2(zone, output, fileformat, style); - + result = dns_zone_dumptostream3(zone, output, fileformat, style, + rawversion); if (output != stdout) (void)isc_stdio_close(output); diff --git a/contrib/bind9/bin/check/check-tool.h b/contrib/bind9/bin/check/check-tool.h index e988597a740..0794729ee05 100644 --- a/contrib/bind9/bin/check/check-tool.h +++ b/contrib/bind9/bin/check/check-tool.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004, 2005, 2007, 2010 Internet Systems Consortium, Inc. ("ISC") + * Copyright (C) 2004, 2005, 2007, 2010, 2011 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 2000-2002 Internet Software Consortium. * * Permission to use, copy, modify, and/or distribute this software for any @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: check-tool.h,v 1.16 2010/09/07 23:46:59 tbox Exp $ */ +/* $Id: check-tool.h,v 1.18 2011/12/09 23:47:02 tbox Exp $ */ #ifndef CHECK_TOOL_H #define CHECK_TOOL_H @@ -41,7 +41,8 @@ load_zone(isc_mem_t *mctx, const char *zonename, const char *filename, isc_result_t dump_zone(const char *zonename, dns_zone_t *zone, const char *filename, - dns_masterformat_t fileformat, const dns_master_style_t *style); + dns_masterformat_t fileformat, const dns_master_style_t *style, + const isc_uint32_t rawversion); #ifdef _WIN32 void InitSockets(void); diff --git a/contrib/bind9/bin/check/named-checkconf.c b/contrib/bind9/bin/check/named-checkconf.c index ef754ff29af..0b3c508f28c 100644 --- a/contrib/bind9/bin/check/named-checkconf.c +++ b/contrib/bind9/bin/check/named-checkconf.c @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: named-checkconf.c,v 1.54.62.2 2011/03/12 04:59:13 tbox Exp $ */ +/* $Id: named-checkconf.c,v 1.56 2011/03/12 04:59:46 tbox Exp $ */ /*! \file */ diff --git a/contrib/bind9/bin/check/named-checkzone.8 b/contrib/bind9/bin/check/named-checkzone.8 index b7b8a4c5467..8538ca886c2 100644 --- a/contrib/bind9/bin/check/named-checkzone.8 +++ b/contrib/bind9/bin/check/named-checkzone.8 @@ -1,4 +1,4 @@ -.\" Copyright (C) 2004-2007, 2009, 2010, 2013 Internet Systems Consortium, Inc. ("ISC") +.\" Copyright (C) 2004-2007, 2009-2011, 2013 Internet Systems Consortium, Inc. ("ISC") .\" Copyright (C) 2000-2002 Internet Software Consortium. .\" .\" Permission to use, copy, modify, and/or distribute this software for any @@ -33,9 +33,9 @@ named\-checkzone, named\-compilezone \- zone file validity checking or converting tool .SH "SYNOPSIS" .HP 16 -\fBnamed\-checkzone\fR [\fB\-d\fR] [\fB\-h\fR] [\fB\-j\fR] [\fB\-q\fR] [\fB\-v\fR] [\fB\-c\ \fR\fB\fIclass\fR\fR] [\fB\-f\ \fR\fB\fIformat\fR\fR] [\fB\-F\ \fR\fB\fIformat\fR\fR] [\fB\-i\ \fR\fB\fImode\fR\fR] [\fB\-k\ \fR\fB\fImode\fR\fR] [\fB\-m\ \fR\fB\fImode\fR\fR] [\fB\-M\ \fR\fB\fImode\fR\fR] [\fB\-n\ \fR\fB\fImode\fR\fR] [\fB\-o\ \fR\fB\fIfilename\fR\fR] [\fB\-r\ \fR\fB\fImode\fR\fR] [\fB\-s\ \fR\fB\fIstyle\fR\fR] [\fB\-S\ \fR\fB\fImode\fR\fR] [\fB\-t\ \fR\fB\fIdirectory\fR\fR] [\fB\-T\ \fR\fB\fImode\fR\fR] [\fB\-w\ \fR\fB\fIdirectory\fR\fR] [\fB\-D\fR] [\fB\-W\ \fR\fB\fImode\fR\fR] {zonename} {filename} +\fBnamed\-checkzone\fR [\fB\-d\fR] [\fB\-h\fR] [\fB\-j\fR] [\fB\-q\fR] [\fB\-v\fR] [\fB\-c\ \fR\fB\fIclass\fR\fR] [\fB\-f\ \fR\fB\fIformat\fR\fR] [\fB\-F\ \fR\fB\fIformat\fR\fR] [\fB\-i\ \fR\fB\fImode\fR\fR] [\fB\-k\ \fR\fB\fImode\fR\fR] [\fB\-m\ \fR\fB\fImode\fR\fR] [\fB\-M\ \fR\fB\fImode\fR\fR] [\fB\-n\ \fR\fB\fImode\fR\fR] [\fB\-L\ \fR\fB\fIserial\fR\fR] [\fB\-o\ \fR\fB\fIfilename\fR\fR] [\fB\-r\ \fR\fB\fImode\fR\fR] [\fB\-s\ \fR\fB\fIstyle\fR\fR] [\fB\-S\ \fR\fB\fImode\fR\fR] [\fB\-t\ \fR\fB\fIdirectory\fR\fR] [\fB\-T\ \fR\fB\fImode\fR\fR] [\fB\-w\ \fR\fB\fIdirectory\fR\fR] [\fB\-D\fR] [\fB\-W\ \fR\fB\fImode\fR\fR] {zonename} {filename} .HP 18 -\fBnamed\-compilezone\fR [\fB\-d\fR] [\fB\-j\fR] [\fB\-q\fR] [\fB\-v\fR] [\fB\-c\ \fR\fB\fIclass\fR\fR] [\fB\-C\ \fR\fB\fImode\fR\fR] [\fB\-f\ \fR\fB\fIformat\fR\fR] [\fB\-F\ \fR\fB\fIformat\fR\fR] [\fB\-i\ \fR\fB\fImode\fR\fR] [\fB\-k\ \fR\fB\fImode\fR\fR] [\fB\-m\ \fR\fB\fImode\fR\fR] [\fB\-n\ \fR\fB\fImode\fR\fR] [\fB\-r\ \fR\fB\fImode\fR\fR] [\fB\-s\ \fR\fB\fIstyle\fR\fR] [\fB\-t\ \fR\fB\fIdirectory\fR\fR] [\fB\-T\ \fR\fB\fImode\fR\fR] [\fB\-w\ \fR\fB\fIdirectory\fR\fR] [\fB\-D\fR] [\fB\-W\ \fR\fB\fImode\fR\fR] {\fB\-o\ \fR\fB\fIfilename\fR\fR} {zonename} {filename} +\fBnamed\-compilezone\fR [\fB\-d\fR] [\fB\-j\fR] [\fB\-q\fR] [\fB\-v\fR] [\fB\-c\ \fR\fB\fIclass\fR\fR] [\fB\-C\ \fR\fB\fImode\fR\fR] [\fB\-f\ \fR\fB\fIformat\fR\fR] [\fB\-F\ \fR\fB\fIformat\fR\fR] [\fB\-i\ \fR\fB\fImode\fR\fR] [\fB\-k\ \fR\fB\fImode\fR\fR] [\fB\-m\ \fR\fB\fImode\fR\fR] [\fB\-n\ \fR\fB\fImode\fR\fR] [\fB\-L\ \fR\fB\fIserial\fR\fR] [\fB\-r\ \fR\fB\fImode\fR\fR] [\fB\-s\ \fR\fB\fIstyle\fR\fR] [\fB\-t\ \fR\fB\fIdirectory\fR\fR] [\fB\-T\ \fR\fB\fImode\fR\fR] [\fB\-w\ \fR\fB\fIdirectory\fR\fR] [\fB\-D\fR] [\fB\-W\ \fR\fB\fImode\fR\fR] {\fB\-o\ \fR\fB\fIfilename\fR\fR} {zonename} {filename} .SH "DESCRIPTION" .PP \fBnamed\-checkzone\fR @@ -139,11 +139,19 @@ Specify the format of the zone file. Possible formats are .PP \-F \fIformat\fR .RS 4 -Specify the format of the output file specified. Possible formats are +Specify the format of the output file specified. For +\fBnamed\-checkzone\fR, this does not cause any effects unless it dumps the zone contents. +.sp +Possible formats are \fB"text"\fR (default) and -\fB"raw"\fR. For -\fBnamed\-checkzone\fR, this does not cause any effects unless it dumps the zone contents. +\fB"raw"\fR +or +\fB"raw=N"\fR, which store the zone in a binary format for rapid loading by +\fBnamed\fR. +\fB"raw=N"\fR +specifies the format version of the raw zone file: if N is 0, the raw file can be read by any version of +\fBnamed\fR; if N is 1, the file can be read by release 9.9.0 or higher. The default is 1. .RE .PP \-k \fImode\fR @@ -160,6 +168,11 @@ checks with the specified failure mode. Possible modes are \fB"ignore"\fR. .RE .PP +\-L \fIserial\fR +.RS 4 +When compiling a zone to 'raw' format, set the "source serial" value in the header to the specified serial number. (This is expected to be used primarily for testing purposes.) +.RE +.PP \-m \fImode\fR .RS 4 Specify whether MX records should be checked to see if they are addresses. Possible modes are @@ -289,7 +302,7 @@ BIND 9 Administrator Reference Manual. .PP Internet Systems Consortium .SH "COPYRIGHT" -Copyright \(co 2004\-2007, 2009, 2010, 2013 Internet Systems Consortium, Inc. ("ISC") +Copyright \(co 2004\-2007, 2009\-2011, 2013 Internet Systems Consortium, Inc. ("ISC") .br Copyright \(co 2000\-2002 Internet Software Consortium. .br diff --git a/contrib/bind9/bin/check/named-checkzone.c b/contrib/bind9/bin/check/named-checkzone.c index fdc3ddc2caa..7e779c2d17f 100644 --- a/contrib/bind9/bin/check/named-checkzone.c +++ b/contrib/bind9/bin/check/named-checkzone.c @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: named-checkzone.c,v 1.61.62.2 2011/12/22 23:45:54 tbox Exp $ */ +/* $Id: named-checkzone.c,v 1.65 2011/12/22 17:29:22 each Exp $ */ /*! \file */ @@ -39,6 +39,7 @@ #include #include #include +#include #include #include #include @@ -112,8 +113,12 @@ main(int argc, char **argv) { const char *outputformatstr = NULL; dns_masterformat_t inputformat = dns_masterformat_text; dns_masterformat_t outputformat = dns_masterformat_text; + dns_masterrawheader_t header; + isc_uint32_t rawversion = 1, serialnum = 0; + isc_boolean_t snset = ISC_FALSE; isc_boolean_t logdump = ISC_FALSE; FILE *errout = stdout; + char *endp; outputstyle = &dns_master_style_full; @@ -159,7 +164,7 @@ main(int argc, char **argv) { isc_commandline_errprint = ISC_FALSE; while ((c = isc_commandline_parse(argc, argv, - "c:df:hi:jk:m:n:qr:s:t:o:vw:DF:M:S:T:W:")) + "c:df:hi:jk:L:m:n:qr:s:t:o:vw:DF:M:S:T:W:")) != EOF) { switch (c) { case 'c': @@ -237,6 +242,17 @@ main(int argc, char **argv) { } break; + case 'L': + snset = ISC_TRUE; + endp = NULL; + serialnum = strtol(isc_commandline_argument, &endp, 0); + if (*endp != '\0') { + fprintf(stderr, "source serial number " + "must be numeric"); + exit(1); + } + break; + case 'n': if (ARGCMP("ignore")) { zone_options &= ~(DNS_ZONEOPT_CHECKNS| @@ -413,7 +429,11 @@ main(int argc, char **argv) { inputformat = dns_masterformat_text; else if (strcasecmp(inputformatstr, "raw") == 0) inputformat = dns_masterformat_raw; - else { + else if (strncasecmp(inputformatstr, "raw=", 4) == 0) { + inputformat = dns_masterformat_raw; + fprintf(stderr, + "WARNING: input format raw, version ignored\n"); + } else { fprintf(stderr, "unknown file format: %s\n", inputformatstr); exit(1); @@ -421,11 +441,22 @@ main(int argc, char **argv) { } if (outputformatstr != NULL) { - if (strcasecmp(outputformatstr, "text") == 0) + if (strcasecmp(outputformatstr, "text") == 0) { outputformat = dns_masterformat_text; - else if (strcasecmp(outputformatstr, "raw") == 0) + } else if (strcasecmp(outputformatstr, "raw") == 0) { outputformat = dns_masterformat_raw; - else { + } else if (strncasecmp(outputformatstr, "raw=", 4) == 0) { + char *end; + + outputformat = dns_masterformat_raw; + rawversion = strtol(outputformatstr + 4, &end, 10); + if (end == outputformatstr + 4 || *end != '\0' || + rawversion > 1U) { + fprintf(stderr, + "unknown raw format version\n"); + exit(1); + } + } else { fprintf(stderr, "unknown file format: %s\n", outputformatstr); exit(1); @@ -480,13 +511,20 @@ main(int argc, char **argv) { result = load_zone(mctx, origin, filename, inputformat, classname, &zone); + if (snset) { + dns_master_initrawheader(&header); + header.flags = DNS_MASTERRAW_SOURCESERIALSET; + header.sourceserial = serialnum; + dns_zone_setrawdata(zone, &header); + } + if (result == ISC_R_SUCCESS && dumpzone) { if (logdump) { fprintf(errout, "dump zone to %s...", output_filename); fflush(errout); } result = dump_zone(origin, zone, output_filename, - outputformat, outputstyle); + outputformat, outputstyle, rawversion); if (logdump) fprintf(errout, "done\n"); } diff --git a/contrib/bind9/bin/check/named-checkzone.docbook b/contrib/bind9/bin/check/named-checkzone.docbook index c25dd1eaf2e..ea37fa2b57f 100644 --- a/contrib/bind9/bin/check/named-checkzone.docbook +++ b/contrib/bind9/bin/check/named-checkzone.docbook @@ -2,7 +2,7 @@ "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd" []> - + June 13, 2000 @@ -38,6 +38,7 @@ 2007 2009 2010 + 2011 2013 Internet Systems Consortium, Inc. ("ISC") @@ -71,6 +72,7 @@ + @@ -97,6 +99,7 @@ + @@ -250,12 +253,20 @@ Specify the format of the output file specified. - Possible formats are "text" (default) - and "raw". For named-checkzone, this does not cause any effects unless it dumps the zone contents. + + Possible formats are "text" (default) + and "raw" or "raw=N", + which store the zone in a binary format for rapid loading + by named. "raw=N" + specifies the format version of the raw zone file: if N + is 0, the raw file can be read by any version of + named; if N is 1, the file can be read + by release 9.9.0 or higher. The default is 1. + @@ -274,6 +285,17 @@ + + -L serial + + + When compiling a zone to 'raw' format, set the "source serial" + value in the header to the specified serial number. (This is + expected to be used primarily for testing purposes.) + + + + -m mode diff --git a/contrib/bind9/bin/check/named-checkzone.html b/contrib/bind9/bin/check/named-checkzone.html index ab9c8efc50d..6941326996c 100644 --- a/contrib/bind9/bin/check/named-checkzone.html +++ b/contrib/bind9/bin/check/named-checkzone.html @@ -1,5 +1,5 @@ - + @@ -45,6 +45,7 @@ 2008 2009 2010 + 2011 2013 Internet Systems Consortium, Inc. ("ISC") @@ -467,7 +468,8 @@ policy of the server. AD=1 indicates that all records have been validated as secure and the answer is not from a OPT-OUT range. AD=0 indicate that some part - of the answer was insecure or not validated. + of the answer was insecure or not validated. This + bit is set by default. @@ -504,19 +506,17 @@ - - - Toggle the setting of the RD (recursion desired) bit in the - query. - This bit is set by default, which means dig - normally sends recursive queries. Recursion is automatically - disabled - when the +nssearch or - +trace query options are - used. - - - + + + Toggle the setting of the RD (recursion desired) bit + in the query. This bit is set by default, which means + dig normally sends recursive + queries. Recursion is automatically disabled when + the +nssearch or + +trace query options are used. + + + @@ -536,20 +536,21 @@ - - Toggle tracing of the delegation path from the root name servers - for - the name being looked up. Tracing is disabled by default. When - tracing is enabled, dig makes - iterative queries to - resolve the name being looked up. It will follow referrals from - the - root servers, showing the answer from each server that was used - to - resolve the lookup. - - - + + Toggle tracing of the delegation path from the root + name servers for the name being looked up. Tracing + is disabled by default. When tracing is enabled, + dig makes iterative queries to + resolve the name being looked up. It will follow + referrals from the root servers, showing the answer + from each server that was used to resolve the lookup. + + + +dnssec is also set when +trace is + set to better emulate the default queries from a nameserver. + + + @@ -594,8 +595,35 @@ Toggle the display of comment lines in the output. The default - is to - print comments. + is to print comments. + + + + + + + + + Toggle the display of per-record comments in the output (for + example, human-readable key information about DNSKEY records). + The default is not to print record comments unless multiline + mode is active. + + + + + + + + + Split long hex- or base64-formatted fields in resource + records into chunks of W characters + (where W is rounded up to the nearest + multiple of 4). + +nosplit or + +split=0 causes fields not to be + split at all. The default is 56 characters, or 44 characters + when multiline mode is active. @@ -755,9 +783,10 @@ Specify the EDNS version to query with. Valid values - are 0 to 255. Setting the EDNS version will cause a - EDNS query to be sent. clears the - remembered EDNS version. + are 0 to 255. Setting the EDNS version will cause + a EDNS query to be sent. + clears the remembered EDNS version. EDNS is set to + 0 by default. diff --git a/contrib/bind9/bin/dig/dig.html b/contrib/bind9/bin/dig/dig.html index a1cd5cf52da..768582e37bb 100644 --- a/contrib/bind9/bin/dig/dig.html +++ b/contrib/bind9/bin/dig/dig.html @@ -1,5 +1,5 @@ - + August 26, 2009 @@ -39,6 +39,7 @@ 2008 2009 2010 + 2011 2012 Internet Systems Consortium, Inc. ("ISC") @@ -52,6 +53,7 @@ + keyfile @@ -64,6 +66,7 @@ + @@ -114,6 +117,15 @@ + + -T TTL + + + Specifies the TTL of the DS records. + + + + -K directory @@ -134,6 +146,15 @@ from . If the zone name is the same as , then it may be omitted. + + If is set to "-", then + the zone data is read from the standard input. This makes it + possible to use the output of the dig + command as input, as in: + + + dig dnskey example.com | dnssec-dsfromkey -f - example.com + diff --git a/contrib/bind9/bin/dnssec/dnssec-dsfromkey.html b/contrib/bind9/bin/dnssec/dnssec-dsfromkey.html index 24bc0c13389..50d4d7802bd 100644 --- a/contrib/bind9/bin/dnssec/dnssec-dsfromkey.html +++ b/contrib/bind9/bin/dnssec/dnssec-dsfromkey.html @@ -1,5 +1,5 @@ - + February 8, 2008 @@ -60,6 +60,7 @@ + @@ -236,6 +237,20 @@ + + -L ttl + + + Sets the default TTL to use for this key when it is converted + into a DNSKEY RR. If the key is imported into a zone, + this is the TTL that will be used for it, unless there was + already a DNSKEY RRset in place, in which case the existing TTL + would take precedence. Setting the default TTL to + 0 or none removes it. + + + + -p protocol diff --git a/contrib/bind9/bin/dnssec/dnssec-keyfromlabel.html b/contrib/bind9/bin/dnssec/dnssec-keyfromlabel.html index 0fa3affa277..f0e2c5c3b86 100644 --- a/contrib/bind9/bin/dnssec/dnssec-keyfromlabel.html +++ b/contrib/bind9/bin/dnssec/dnssec-keyfromlabel.html @@ -28,10 +28,10 @@

Synopsis

-

dnssec-keyfromlabel {-l label} [-3] [-a algorithm] [-A date/offset] [-c class] [-D date/offset] [-E engine] [-f flag] [-G] [-I date/offset] [-k] [-K directory] [-n nametype] [-P date/offset] [-p protocol] [-R date/offset] [-t type] [-v level] [-y] {name}

+

dnssec-keyfromlabel {-l label} [-3] [-a algorithm] [-A date/offset] [-c class] [-D date/offset] [-E engine] [-f flag] [-G] [-I date/offset] [-k] [-K directory] [-L ttl] [-n nametype] [-P date/offset] [-p protocol] [-R date/offset] [-t type] [-v level] [-y] {name}

-

DESCRIPTION

+

DESCRIPTION

dnssec-keyfromlabel gets keys with the given label from a crypto hardware and builds key files for DNSSEC (Secure DNS), as defined in RFC 2535 @@ -44,7 +44,7 @@

-

OPTIONS

+

OPTIONS

-a algorithm
@@ -135,6 +135,15 @@

Generate KEY records rather than DNSKEY records.

+
-L ttl
+

+ Sets the default TTL to use for this key when it is converted + into a DNSKEY RR. If the key is imported into a zone, + this is the TTL that will be used for it, unless there was + already a DNSKEY RRset in place, in which case the existing TTL + would take precedence. Setting the default TTL to + 0 or none removes it. +

-p protocol

Sets the protocol value for the key. The protocol @@ -164,7 +173,7 @@

-

TIMING OPTIONS

+

TIMING OPTIONS

Dates can be expressed in the format YYYYMMDD or YYYYMMDDHHMMSS. If the argument begins with a '+' or '-', it is interpreted as @@ -211,7 +220,7 @@

-

GENERATED KEY FILES

+

GENERATED KEY FILES

When dnssec-keyfromlabel completes successfully, @@ -250,7 +259,7 @@

-

SEE ALSO

+

SEE ALSO

dnssec-keygen(8), dnssec-signzone(8), BIND 9 Administrator Reference Manual, @@ -258,7 +267,7 @@

-

AUTHOR

+

AUTHOR

Internet Systems Consortium

diff --git a/contrib/bind9/bin/dnssec/dnssec-keygen.8 b/contrib/bind9/bin/dnssec/dnssec-keygen.8 index 689f23df4ed..90daddd1569 100644 --- a/contrib/bind9/bin/dnssec/dnssec-keygen.8 +++ b/contrib/bind9/bin/dnssec/dnssec-keygen.8 @@ -1,4 +1,4 @@ -.\" Copyright (C) 2004, 2005, 2007-2010, 2012 Internet Systems Consortium, Inc. ("ISC") +.\" Copyright (C) 2004, 2005, 2007-2012 Internet Systems Consortium, Inc. ("ISC") .\" Copyright (C) 2000-2003 Internet Software Consortium. .\" .\" Permission to use, copy, modify, and/or distribute this software for any @@ -33,7 +33,7 @@ dnssec\-keygen \- DNSSEC key generation tool .SH "SYNOPSIS" .HP 14 -\fBdnssec\-keygen\fR [\fB\-a\ \fR\fB\fIalgorithm\fR\fR] [\fB\-b\ \fR\fB\fIkeysize\fR\fR] [\fB\-n\ \fR\fB\fInametype\fR\fR] [\fB\-3\fR] [\fB\-A\ \fR\fB\fIdate/offset\fR\fR] [\fB\-C\fR] [\fB\-c\ \fR\fB\fIclass\fR\fR] [\fB\-D\ \fR\fB\fIdate/offset\fR\fR] [\fB\-E\ \fR\fB\fIengine\fR\fR] [\fB\-e\fR] [\fB\-f\ \fR\fB\fIflag\fR\fR] [\fB\-G\fR] [\fB\-g\ \fR\fB\fIgenerator\fR\fR] [\fB\-h\fR] [\fB\-I\ \fR\fB\fIdate/offset\fR\fR] [\fB\-i\ \fR\fB\fIinterval\fR\fR] [\fB\-K\ \fR\fB\fIdirectory\fR\fR] [\fB\-k\fR] [\fB\-P\ \fR\fB\fIdate/offset\fR\fR] [\fB\-p\ \fR\fB\fIprotocol\fR\fR] [\fB\-q\fR] [\fB\-R\ \fR\fB\fIdate/offset\fR\fR] [\fB\-r\ \fR\fB\fIrandomdev\fR\fR] [\fB\-S\ \fR\fB\fIkey\fR\fR] [\fB\-s\ \fR\fB\fIstrength\fR\fR] [\fB\-t\ \fR\fB\fItype\fR\fR] [\fB\-v\ \fR\fB\fIlevel\fR\fR] [\fB\-z\fR] {name} +\fBdnssec\-keygen\fR [\fB\-a\ \fR\fB\fIalgorithm\fR\fR] [\fB\-b\ \fR\fB\fIkeysize\fR\fR] [\fB\-n\ \fR\fB\fInametype\fR\fR] [\fB\-3\fR] [\fB\-A\ \fR\fB\fIdate/offset\fR\fR] [\fB\-C\fR] [\fB\-c\ \fR\fB\fIclass\fR\fR] [\fB\-D\ \fR\fB\fIdate/offset\fR\fR] [\fB\-E\ \fR\fB\fIengine\fR\fR] [\fB\-f\ \fR\fB\fIflag\fR\fR] [\fB\-G\fR] [\fB\-g\ \fR\fB\fIgenerator\fR\fR] [\fB\-h\fR] [\fB\-I\ \fR\fB\fIdate/offset\fR\fR] [\fB\-i\ \fR\fB\fIinterval\fR\fR] [\fB\-K\ \fR\fB\fIdirectory\fR\fR] [\fB\-L\ \fR\fB\fIttl\fR\fR] [\fB\-k\fR] [\fB\-P\ \fR\fB\fIdate/offset\fR\fR] [\fB\-p\ \fR\fB\fIprotocol\fR\fR] [\fB\-q\fR] [\fB\-R\ \fR\fB\fIdate/offset\fR\fR] [\fB\-r\ \fR\fB\fIrandomdev\fR\fR] [\fB\-S\ \fR\fB\fIkey\fR\fR] [\fB\-s\ \fR\fB\fIstrength\fR\fR] [\fB\-t\ \fR\fB\fItype\fR\fR] [\fB\-v\ \fR\fB\fIlevel\fR\fR] [\fB\-z\fR] {name} .SH "DESCRIPTION" .PP \fBdnssec\-keygen\fR @@ -103,11 +103,6 @@ Indicates that the DNS record containing the key should have the specified class Uses a crypto hardware (OpenSSL engine) for random number and, when supported, key generation. When compiled with PKCS#11 support it defaults to pkcs11; the empty name resets it to no engine. .RE .PP -\-e -.RS 4 -If generating an RSAMD5/RSASHA1 key, use a large exponent. -.RE -.PP \-f \fIflag\fR .RS 4 Set the specified flag in the flag field of the KEY/DNSKEY record. The only recognized flags are KSK (Key Signing Key) and REVOKE. @@ -139,6 +134,15 @@ Sets the directory in which the key files are to be written. Deprecated in favor of \-T KEY. .RE .PP +\-L \fIttl\fR +.RS 4 +Sets the default TTL to use for this key when it is converted into a DNSKEY RR. If the key is imported into a zone, this is the TTL that will be used for it, unless there was already a DNSKEY RRset in place, in which case the existing TTL would take precedence. Setting the default TTL to +0 +or +none +removes it. +.RE +.PP \-p \fIprotocol\fR .RS 4 Sets the protocol value for the generated key. The protocol is a number between 0 and 255. The default is 3 (DNSSEC). Other possible values for this argument are listed in RFC 2535 and its successors. @@ -298,7 +302,7 @@ RFC 4034. .PP Internet Systems Consortium .SH "COPYRIGHT" -Copyright \(co 2004, 2005, 2007\-2010, 2012 Internet Systems Consortium, Inc. ("ISC") +Copyright \(co 2004, 2005, 2007\-2012 Internet Systems Consortium, Inc. ("ISC") .br Copyright \(co 2000\-2003 Internet Software Consortium. .br diff --git a/contrib/bind9/bin/dnssec/dnssec-keygen.c b/contrib/bind9/bin/dnssec/dnssec-keygen.c index 3d22f997cc2..1e61ca392e8 100644 --- a/contrib/bind9/bin/dnssec/dnssec-keygen.c +++ b/contrib/bind9/bin/dnssec/dnssec-keygen.c @@ -29,7 +29,7 @@ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: dnssec-keygen.c,v 1.115.14.4 2011/11/30 00:51:38 marka Exp $ */ +/* $Id: dnssec-keygen.c,v 1.120 2011/11/30 00:48:51 marka Exp $ */ /*! \file */ @@ -124,11 +124,12 @@ usage(void) { #else fprintf(stderr, " -E \n"); #endif - fprintf(stderr, " -e: use large exponent (RSAMD5/RSASHA1 only)\n"); fprintf(stderr, " -f : KSK | REVOKE\n"); fprintf(stderr, " -g : use specified generator " "(DH only)\n"); + fprintf(stderr, " -L : default key TTL\n"); fprintf(stderr, " -p : (default: 3 [dnssec])\n"); + fprintf(stderr, " -r : a file containing random data\n"); fprintf(stderr, " -s : strength value this key signs DNS " "records with (default: 0)\n"); fprintf(stderr, " -T : DNSKEY | KEY (default: DNSKEY; " @@ -137,8 +138,6 @@ usage(void) { fprintf(stderr, " -t : " "AUTHCONF | NOAUTHCONF | NOAUTH | NOCONF " "(default: AUTHCONF)\n"); - fprintf(stderr, " -r : a file containing random data\n"); - fprintf(stderr, " -h: print usage and exit\n"); fprintf(stderr, " -m :\n"); fprintf(stderr, " usage | trace | record | size | mctx\n"); @@ -212,7 +211,7 @@ main(int argc, char **argv) { isc_boolean_t conflict = ISC_FALSE, null_key = ISC_FALSE; isc_boolean_t oldstyle = ISC_FALSE; isc_mem_t *mctx = NULL; - int ch, rsa_exp = 0, generator = 0, param = 0; + int ch, generator = 0, param = 0; int protocol = -1, size = -1, signatory = 0; isc_result_t ret; isc_textregion_t r; @@ -231,6 +230,7 @@ main(int argc, char **argv) { dns_rdataclass_t rdclass; int options = DST_TYPE_PRIVATE | DST_TYPE_PUBLIC; int dbits = 0; + dns_ttl_t ttl = 0; isc_boolean_t use_default = ISC_FALSE, use_nsec3 = ISC_FALSE; isc_stdtime_t publish = 0, activate = 0, revoke = 0; isc_stdtime_t inactive = 0, delete = 0; @@ -238,7 +238,7 @@ main(int argc, char **argv) { int prepub = -1; isc_boolean_t setpub = ISC_FALSE, setact = ISC_FALSE; isc_boolean_t setrev = ISC_FALSE, setinact = ISC_FALSE; - isc_boolean_t setdel = ISC_FALSE; + isc_boolean_t setdel = ISC_FALSE, setttl = ISC_FALSE; isc_boolean_t unsetpub = ISC_FALSE, unsetact = ISC_FALSE; isc_boolean_t unsetrev = ISC_FALSE, unsetinact = ISC_FALSE; isc_boolean_t unsetdel = ISC_FALSE; @@ -257,7 +257,7 @@ main(int argc, char **argv) { /* * Process memory debugging argument first. */ -#define CMDLINE_FLAGS "3A:a:b:Cc:D:d:E:eFf:Gg:hI:i:K:km:n:P:p:qR:r:S:s:T:t:v:" +#define CMDLINE_FLAGS "3A:a:b:Cc:D:d:E:eFf:Gg:hI:i:K:kL:m:n:P:p:qR:r:S:s:T:t:v:" while ((ch = isc_commandline_parse(argc, argv, CMDLINE_FLAGS)) != -1) { switch (ch) { case 'm': @@ -310,7 +310,9 @@ main(int argc, char **argv) { engine = isc_commandline_argument; break; case 'e': - rsa_exp = 1; + fprintf(stderr, + "phased-out option -e " + "(was 'use (RSA) large exponent)\n"); break; case 'f': c = (unsigned char)(isc_commandline_argument[0]); @@ -340,6 +342,13 @@ main(int argc, char **argv) { "To generate a key-signing key, use -f KSK.\n" "To generate a key with TYPE=KEY, use -T KEY.\n"); break; + case 'L': + if (strcmp(isc_commandline_argument, "none") == 0) + ttl = 0; + else + ttl = strtottl(isc_commandline_argument); + setttl = ISC_TRUE; + break; case 'n': nametype = isc_commandline_argument; break; @@ -782,13 +791,6 @@ main(int argc, char **argv) { break; } - if (!(alg == DNS_KEYALG_RSAMD5 || alg == DNS_KEYALG_RSASHA1 || - alg == DNS_KEYALG_NSEC3RSASHA1 || alg == DNS_KEYALG_RSASHA256 || - alg == DNS_KEYALG_RSASHA512 || alg == DST_ALG_ECCGOST || - alg == DST_ALG_ECDSA256 || alg == DST_ALG_ECDSA384) && - rsa_exp != 0) - fatal("specified RSA exponent for a non-RSA key"); - if (alg != DNS_KEYALG_DH && generator != 0) fatal("specified DH generator for a non-DH key"); @@ -848,7 +850,6 @@ main(int argc, char **argv) { case DNS_KEYALG_NSEC3RSASHA1: case DNS_KEYALG_RSASHA256: case DNS_KEYALG_RSASHA512: - param = rsa_exp; show_progress = ISC_TRUE; break; @@ -983,6 +984,10 @@ main(int argc, char **argv) { dst_key_setprivateformat(key, 1, 2); } + /* Set the default key TTL */ + if (setttl) + dst_key_setttl(key, ttl); + /* * Do not overwrite an existing key, or create a key * if there is a risk of ID collision due to this key diff --git a/contrib/bind9/bin/dnssec/dnssec-keygen.docbook b/contrib/bind9/bin/dnssec/dnssec-keygen.docbook index 0a1926bd839..bc50c0226da 100644 --- a/contrib/bind9/bin/dnssec/dnssec-keygen.docbook +++ b/contrib/bind9/bin/dnssec/dnssec-keygen.docbook @@ -2,7 +2,7 @@ "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd" []> - + June 30, 2000 @@ -43,6 +43,7 @@ 2008 2009 2010 + 2011 2012 Internet Systems Consortium, Inc. ("ISC") @@ -67,7 +68,6 @@ - @@ -75,6 +75,7 @@ + @@ -231,15 +232,6 @@
- - -e - - - If generating an RSAMD5/RSASHA1 key, use a large exponent. - - - - -f flag @@ -300,6 +292,20 @@ + + -L ttl + + + Sets the default TTL to use for this key when it is converted + into a DNSKEY RR. If the key is imported into a zone, + this is the TTL that will be used for it, unless there was + already a DNSKEY RRset in place, in which case the existing TTL + would take precedence. Setting the default TTL to + 0 or none removes it. + + + + -p protocol diff --git a/contrib/bind9/bin/dnssec/dnssec-keygen.html b/contrib/bind9/bin/dnssec/dnssec-keygen.html index 3bdfa0739f2..ecf512b165f 100644 --- a/contrib/bind9/bin/dnssec/dnssec-keygen.html +++ b/contrib/bind9/bin/dnssec/dnssec-keygen.html @@ -1,5 +1,5 @@ - + June 1, 2009 diff --git a/contrib/bind9/bin/dnssec/dnssec-settime.8 b/contrib/bind9/bin/dnssec/dnssec-settime.8 index 8a5e2e78900..7c0c3b28928 100644 --- a/contrib/bind9/bin/dnssec/dnssec-settime.8 +++ b/contrib/bind9/bin/dnssec/dnssec-settime.8 @@ -32,7 +32,7 @@ dnssec\-settime \- Set the key timing metadata for a DNSSEC key .SH "SYNOPSIS" .HP 15 -\fBdnssec\-settime\fR [\fB\-f\fR] [\fB\-K\ \fR\fB\fIdirectory\fR\fR] [\fB\-P\ \fR\fB\fIdate/offset\fR\fR] [\fB\-A\ \fR\fB\fIdate/offset\fR\fR] [\fB\-R\ \fR\fB\fIdate/offset\fR\fR] [\fB\-I\ \fR\fB\fIdate/offset\fR\fR] [\fB\-D\ \fR\fB\fIdate/offset\fR\fR] [\fB\-h\fR] [\fB\-v\ \fR\fB\fIlevel\fR\fR] [\fB\-E\ \fR\fB\fIengine\fR\fR] {keyfile} +\fBdnssec\-settime\fR [\fB\-f\fR] [\fB\-K\ \fR\fB\fIdirectory\fR\fR] [\fB\-L\ \fR\fB\fIttl\fR\fR] [\fB\-P\ \fR\fB\fIdate/offset\fR\fR] [\fB\-A\ \fR\fB\fIdate/offset\fR\fR] [\fB\-R\ \fR\fB\fIdate/offset\fR\fR] [\fB\-I\ \fR\fB\fIdate/offset\fR\fR] [\fB\-D\ \fR\fB\fIdate/offset\fR\fR] [\fB\-h\fR] [\fB\-v\ \fR\fB\fIlevel\fR\fR] [\fB\-E\ \fR\fB\fIengine\fR\fR] {keyfile} .SH "DESCRIPTION" .PP \fBdnssec\-settime\fR @@ -67,6 +67,15 @@ will fail when attempting to update a legacy key. With this option, the key will Sets the directory in which the key files are to reside. .RE .PP +\-L \fIttl\fR +.RS 4 +Sets the default TTL to use for this key when it is converted into a DNSKEY RR. If the key is imported into a zone, this is the TTL that will be used for it, unless there was already a DNSKEY RRset in place, in which case the existing TTL would take precedence. Setting the default TTL to +0 +or +none +removes it. +.RE +.PP \-h .RS 4 Emit usage message and exit. diff --git a/contrib/bind9/bin/dnssec/dnssec-settime.c b/contrib/bind9/bin/dnssec/dnssec-settime.c index a7fbed3bcbd..4c88a07ac45 100644 --- a/contrib/bind9/bin/dnssec/dnssec-settime.c +++ b/contrib/bind9/bin/dnssec/dnssec-settime.c @@ -14,7 +14,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: dnssec-settime.c,v 1.28.16.3 2011/06/02 20:24:11 each Exp $ */ +/* $Id: dnssec-settime.c,v 1.32 2011/06/02 20:24:45 each Exp $ */ /*! \file */ @@ -66,6 +66,7 @@ usage(void) { fprintf(stderr, " -f: force update of old-style " "keys\n"); fprintf(stderr, " -K directory: set key file location\n"); + fprintf(stderr, " -L ttl: set default key TTL\n"); fprintf(stderr, " -v level: set level of verbosity\n"); fprintf(stderr, " -h: help\n"); fprintf(stderr, "Timing options:\n"); @@ -137,12 +138,13 @@ main(int argc, char **argv) { unsigned int size = 0; isc_uint16_t flags = 0; int prepub = -1; + dns_ttl_t ttl = 0; isc_stdtime_t now; isc_stdtime_t pub = 0, act = 0, rev = 0, inact = 0, del = 0; isc_stdtime_t prevact = 0, previnact = 0, prevdel = 0; isc_boolean_t setpub = ISC_FALSE, setact = ISC_FALSE; isc_boolean_t setrev = ISC_FALSE, setinact = ISC_FALSE; - isc_boolean_t setdel = ISC_FALSE; + isc_boolean_t setdel = ISC_FALSE, setttl = ISC_FALSE; isc_boolean_t unsetpub = ISC_FALSE, unsetact = ISC_FALSE; isc_boolean_t unsetrev = ISC_FALSE, unsetinact = ISC_FALSE; isc_boolean_t unsetdel = ISC_FALSE; @@ -169,7 +171,7 @@ main(int argc, char **argv) { isc_stdtime_get(&now); -#define CMDLINE_FLAGS "A:D:E:fhI:i:K:P:p:R:S:uv:" +#define CMDLINE_FLAGS "A:D:E:fhI:i:K:L:P:p:R:S:uv:" while ((ch = isc_commandline_parse(argc, argv, CMDLINE_FLAGS)) != -1) { switch (ch) { case 'E': @@ -233,6 +235,13 @@ main(int argc, char **argv) { "directory"); } break; + case 'L': + if (strcmp(isc_commandline_argument, "none") == 0) + ttl = 0; + else + ttl = strtottl(isc_commandline_argument); + setttl = ISC_TRUE; + break; case 'v': verbose = strtol(isc_commandline_argument, &endp, 0); if (*endp != '\0') @@ -535,6 +544,9 @@ main(int argc, char **argv) { else if (unsetdel) dst_key_unsettime(key, DST_TIME_DELETE); + if (setttl) + dst_key_setttl(key, ttl); + /* * No metadata changes were made but we're forcing an upgrade * to the new format anyway: use "-P now -A now" as the default @@ -545,6 +557,9 @@ main(int argc, char **argv) { changed = ISC_TRUE; } + if (!changed && setttl) + changed = ISC_TRUE; + /* * Print out time values, if -p was used. */ diff --git a/contrib/bind9/bin/dnssec/dnssec-settime.docbook b/contrib/bind9/bin/dnssec/dnssec-settime.docbook index 3d89b651b47..bc6870b1321 100644 --- a/contrib/bind9/bin/dnssec/dnssec-settime.docbook +++ b/contrib/bind9/bin/dnssec/dnssec-settime.docbook @@ -17,7 +17,7 @@ - PERFORMANCE OF THIS SOFTWARE. --> - + July 15, 2009 @@ -48,6 +48,7 @@ dnssec-settime + @@ -116,6 +117,20 @@ + + -L ttl + + + Sets the default TTL to use for this key when it is converted + into a DNSKEY RR. If the key is imported into a zone, + this is the TTL that will be used for it, unless there was + already a DNSKEY RRset in place, in which case the existing TTL + would take precedence. Setting the default TTL to + 0 or none removes it. + + + + -h diff --git a/contrib/bind9/bin/dnssec/dnssec-settime.html b/contrib/bind9/bin/dnssec/dnssec-settime.html index 0ac82bcbd3d..7b022658c05 100644 --- a/contrib/bind9/bin/dnssec/dnssec-settime.html +++ b/contrib/bind9/bin/dnssec/dnssec-settime.html @@ -28,10 +28,10 @@

Synopsis

-

dnssec-settime [-f] [-K directory] [-P date/offset] [-A date/offset] [-R date/offset] [-I date/offset] [-D date/offset] [-h] [-v level] [-E engine] {keyfile}

+

dnssec-settime [-f] [-K directory] [-L ttl] [-P date/offset] [-A date/offset] [-R date/offset] [-I date/offset] [-D date/offset] [-h] [-v level] [-E engine] {keyfile}

-

DESCRIPTION

+

DESCRIPTION

dnssec-settime reads a DNSSEC private key file and sets the key timing metadata as specified by the -P, -A, @@ -57,7 +57,7 @@

-

OPTIONS

+

OPTIONS

-f

@@ -74,6 +74,15 @@

Sets the directory in which the key files are to reside.

+
-L ttl
+

+ Sets the default TTL to use for this key when it is converted + into a DNSKEY RR. If the key is imported into a zone, + this is the TTL that will be used for it, unless there was + already a DNSKEY RRset in place, in which case the existing TTL + would take precedence. Setting the default TTL to + 0 or none removes it. +

-h

Emit usage message and exit. @@ -90,7 +99,7 @@

-

TIMING OPTIONS

+

TIMING OPTIONS

Dates can be expressed in the format YYYYMMDD or YYYYMMDDHHMMSS. If the argument begins with a '+' or '-', it is interpreted as @@ -169,7 +178,7 @@

-

PRINTING OPTIONS

+

PRINTING OPTIONS

dnssec-settime can also be used to print the timing metadata associated with a key. @@ -195,7 +204,7 @@

-

SEE ALSO

+

SEE ALSO

dnssec-keygen(8), dnssec-signzone(8), BIND 9 Administrator Reference Manual, @@ -203,7 +212,7 @@

-

AUTHOR

+

AUTHOR

Internet Systems Consortium

diff --git a/contrib/bind9/bin/dnssec/dnssec-signzone.8 b/contrib/bind9/bin/dnssec/dnssec-signzone.8 index 028068803cd..c91749569a5 100644 --- a/contrib/bind9/bin/dnssec/dnssec-signzone.8 +++ b/contrib/bind9/bin/dnssec/dnssec-signzone.8 @@ -1,4 +1,4 @@ -.\" Copyright (C) 2004-2009 Internet Systems Consortium, Inc. ("ISC") +.\" Copyright (C) 2004-2009, 2011 Internet Systems Consortium, Inc. ("ISC") .\" Copyright (C) 2000-2003 Internet Software Consortium. .\" .\" Permission to use, copy, modify, and/or distribute this software for any @@ -33,7 +33,7 @@ dnssec\-signzone \- DNSSEC zone signing tool .SH "SYNOPSIS" .HP 16 -\fBdnssec\-signzone\fR [\fB\-a\fR] [\fB\-c\ \fR\fB\fIclass\fR\fR] [\fB\-d\ \fR\fB\fIdirectory\fR\fR] [\fB\-E\ \fR\fB\fIengine\fR\fR] [\fB\-e\ \fR\fB\fIend\-time\fR\fR] [\fB\-f\ \fR\fB\fIoutput\-file\fR\fR] [\fB\-g\fR] [\fB\-h\fR] [\fB\-K\ \fR\fB\fIdirectory\fR\fR] [\fB\-k\ \fR\fB\fIkey\fR\fR] [\fB\-l\ \fR\fB\fIdomain\fR\fR] [\fB\-i\ \fR\fB\fIinterval\fR\fR] [\fB\-I\ \fR\fB\fIinput\-format\fR\fR] [\fB\-j\ \fR\fB\fIjitter\fR\fR] [\fB\-N\ \fR\fB\fIsoa\-serial\-format\fR\fR] [\fB\-o\ \fR\fB\fIorigin\fR\fR] [\fB\-O\ \fR\fB\fIoutput\-format\fR\fR] [\fB\-p\fR] [\fB\-P\fR] [\fB\-r\ \fR\fB\fIrandomdev\fR\fR] [\fB\-S\fR] [\fB\-s\ \fR\fB\fIstart\-time\fR\fR] [\fB\-T\ \fR\fB\fIttl\fR\fR] [\fB\-t\fR] [\fB\-u\fR] [\fB\-v\ \fR\fB\fIlevel\fR\fR] [\fB\-x\fR] [\fB\-z\fR] [\fB\-3\ \fR\fB\fIsalt\fR\fR] [\fB\-H\ \fR\fB\fIiterations\fR\fR] [\fB\-A\fR] {zonefile} [key...] +\fBdnssec\-signzone\fR [\fB\-a\fR] [\fB\-c\ \fR\fB\fIclass\fR\fR] [\fB\-d\ \fR\fB\fIdirectory\fR\fR] [\fB\-D\fR] [\fB\-E\ \fR\fB\fIengine\fR\fR] [\fB\-e\ \fR\fB\fIend\-time\fR\fR] [\fB\-f\ \fR\fB\fIoutput\-file\fR\fR] [\fB\-g\fR] [\fB\-h\fR] [\fB\-K\ \fR\fB\fIdirectory\fR\fR] [\fB\-k\ \fR\fB\fIkey\fR\fR] [\fB\-L\ \fR\fB\fIserial\fR\fR] [\fB\-l\ \fR\fB\fIdomain\fR\fR] [\fB\-i\ \fR\fB\fIinterval\fR\fR] [\fB\-I\ \fR\fB\fIinput\-format\fR\fR] [\fB\-j\ \fR\fB\fIjitter\fR\fR] [\fB\-N\ \fR\fB\fIsoa\-serial\-format\fR\fR] [\fB\-o\ \fR\fB\fIorigin\fR\fR] [\fB\-O\ \fR\fB\fIoutput\-format\fR\fR] [\fB\-P\fR] [\fB\-p\fR] [\fB\-R\fR] [\fB\-r\ \fR\fB\fIrandomdev\fR\fR] [\fB\-S\fR] [\fB\-s\ \fR\fB\fIstart\-time\fR\fR] [\fB\-T\ \fR\fB\fIttl\fR\fR] [\fB\-t\fR] [\fB\-u\fR] [\fB\-v\ \fR\fB\fIlevel\fR\fR] [\fB\-X\ \fR\fB\fIextended\ end\-time\fR\fR] [\fB\-x\fR] [\fB\-z\fR] [\fB\-3\ \fR\fB\fIsalt\fR\fR] [\fB\-H\ \fR\fB\fIiterations\fR\fR] [\fB\-A\fR] {zonefile} [key...] .SH "DESCRIPTION" .PP \fBdnssec\-signzone\fR @@ -72,6 +72,15 @@ files in \fBdirectory\fR. .RE .PP +\-D +.RS 4 +Output only those record types automatically managed by +\fBdnssec\-signzone\fR, i.e. RRSIG, NSEC, NSEC3 and NSEC3PARAM records. If smart signing (\fB\-S\fR) is used, DNSKEY records are also included. The resulting file can be included in the original zone file with +\fB$INCLUDE\fR. This option cannot be combined with +\fB\-O raw\fR +or serial number updating. +.RE +.PP \-E \fIengine\fR .RS 4 Uses a crypto hardware (OpenSSL engine) for the crypto operations it supports, for instance signing with private keys from a secure key store. When compiled with PKCS#11 support it defaults to pkcs11; the empty name resets it to no engine. @@ -119,11 +128,29 @@ must be later than \fBstart\-time\fR. .RE .PP +\-X \fIextended end\-time\fR +.RS 4 +Specify the date and time when the generated RRSIG records for the DNSKEY RRset will expire. This is to be used in cases when the DNSKEY signatures need to persist longer than signatures on other records; e.g., when the private component of the KSK is kept offline and the KSK signature is to be refreshed manually. +.sp +As with +\fBstart\-time\fR, an absolute time is indicated in YYYYMMDDHHMMSS notation. A time relative to the start time is indicated with +N, which is N seconds from the start time. A time relative to the current time is indicated with now+N. If no +\fBextended end\-time\fR +is specified, the value of +\fBend\-time\fR +is used as the default. (\fBend\-time\fR, in turn, defaults to 30 days from the start time.) +\fBextended end\-time\fR +must be later than +\fBstart\-time\fR. +.RE +.PP \-f \fIoutput\-file\fR .RS 4 The name of the output file containing the signed zone. The default is to append \fI.signed\fR -to the input filename. +to the input filename. If +\fBoutput\-file\fR +is set to +"\-", then the signed zone is written to the standard output, with a default output format of "full". .RE .PP \-h @@ -164,6 +191,11 @@ option specifies a jitter window that will be used to randomize the signature ex Signature lifetime jitter also to some extent benefits validators and servers by spreading out cache expiration, i.e. if large numbers of RRSIGs don't expire at the same time from all caches there will be less congestion than if all validators need to refetch at mostly the same time. .RE .PP +\-L \fIserial\fR +.RS 4 +When writing a signed zone to 'raw' format, set the "source serial" value in the header to the specified serial number. (This is expected to be used primarily for testing purposes.) +.RE +.PP \-n \fIncpus\fR .RS 4 Specifies the number of threads to use. By default, one thread is started for each detected CPU. @@ -205,8 +237,15 @@ The zone origin. If not specified, the name of the zone file is assumed to be th .RS 4 The format of the output file containing the signed zone. Possible formats are \fB"text"\fR -(default) and -\fB"raw"\fR. +(default) +\fB"full"\fR, which is text output in a format suitable for processing by external scripts, and +\fB"raw"\fR +or +\fB"raw=N"\fR, which store the zone in a binary format for rapid loading by +\fBnamed\fR. +\fB"raw=N"\fR +specifies the format version of the raw zone file: if N is 0, the raw file can be read by any version of +\fBnamed\fR; if N is 1, the file can be read by release 9.9.0 or higher. The default is 1. .RE .PP \-p @@ -221,6 +260,17 @@ Disable post sign verification tests. The post sign verification test ensures that for each algorithm in use there is at least one non revoked self signed KSK key, that all revoked KSK keys are self signed, and that all records in the zone are signed by the algorithm. This option skips these tests. .RE .PP +\-R +.RS 4 +Remove signatures from keys that no longer exist. +.sp +Normally, when a previously\-signed zone is passed as input to the signer, and a DNSKEY record has been removed and replaced with a new one, signatures from the old key that are still within their validity period are retained. This allows the zone to continue to validate with cached copies of the old DNSKEY RRset. The +\fB\-R\fR +forces +\fBdnssec\-signzone\fR +to remove all orphaned signatures. +.RE +.PP \-r \fIrandomdev\fR .RS 4 Specifies the source of randomness. If the operating system does not provide a @@ -265,8 +315,8 @@ If either of the key's unpublication or deletion dates are set and in the past, .PP \-T \fIttl\fR .RS 4 -Specifies the TTL to be used for new DNSKEY records imported into the zone from the key repository. If not specified, the default is the minimum TTL value from the zone's SOA record. This option is ignored when signing without -\fB\-S\fR, since DNSKEY records are not imported from the key repository in that case. It is also ignored if there are any pre\-existing DNSKEY records at the zone apex, in which case new records' TTL values will be set to match them. +Specifies a TTL to be used for new DNSKEY records imported into the zone from the key repository. If not specified, the default is the TTL value from the zone's SOA record. This option is ignored when signing without +\fB\-S\fR, since DNSKEY records are not imported from the key repository in that case. It is also ignored if there are any pre\-existing DNSKEY records at the zone apex, in which case new records' TTL values will be set to match them, or if any of the imported DNSKEY records had a default TTL value. In the event of a a conflict between TTL values in imported keys, the shortest one is used. .RE .PP \-t @@ -378,7 +428,7 @@ RFC 4033. .PP Internet Systems Consortium .SH "COPYRIGHT" -Copyright \(co 2004\-2009 Internet Systems Consortium, Inc. ("ISC") +Copyright \(co 2004\-2009, 2011 Internet Systems Consortium, Inc. ("ISC") .br Copyright \(co 2000\-2003 Internet Software Consortium. .br diff --git a/contrib/bind9/bin/dnssec/dnssec-signzone.c b/contrib/bind9/bin/dnssec/dnssec-signzone.c index 86c3aee7095..83456a7db7d 100644 --- a/contrib/bind9/bin/dnssec/dnssec-signzone.c +++ b/contrib/bind9/bin/dnssec/dnssec-signzone.c @@ -1,5 +1,5 @@ /* - * Portions Copyright (C) 2004-2012 Internet Systems Consortium, Inc. ("ISC") + * Portions Copyright (C) 2004-2013 Internet Systems Consortium, Inc. ("ISC") * Portions Copyright (C) 1999-2003 Internet Software Consortium. * * Permission to use, copy, modify, and/or distribute this software for any @@ -29,7 +29,7 @@ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: dnssec-signzone.c,v 1.262.110.9 2011/07/19 23:47:12 tbox Exp $ */ +/* $Id: dnssec-signzone.c,v 1.285 2011/12/22 07:32:39 each Exp $ */ /*! \file */ @@ -37,6 +37,7 @@ #include #include +#include #include #include @@ -124,7 +125,7 @@ struct signer_event { static dns_dnsseckeylist_t keylist; static unsigned int keycount = 0; isc_rwlock_t keylist_lock; -static isc_stdtime_t starttime = 0, endtime = 0, now; +static isc_stdtime_t starttime = 0, endtime = 0, dnskey_endtime = 0, now; static int cycle = -1; static int jitter = 0; static isc_boolean_t tryverify = ISC_FALSE; @@ -133,11 +134,13 @@ static isc_mem_t *mctx = NULL; static isc_entropy_t *ectx = NULL; static dns_ttl_t zone_soa_min_ttl; static dns_ttl_t soa_ttl; -static FILE *fp; +static FILE *fp = NULL; static char *tempfile = NULL; static const dns_master_style_t *masterstyle; static dns_masterformat_t inputformat = dns_masterformat_text; static dns_masterformat_t outputformat = dns_masterformat_text; +static isc_uint32_t rawversion = 1, serialnum = 0; +static isc_boolean_t snset = ISC_FALSE; static unsigned int nsigned = 0, nretained = 0, ndropped = 0; static unsigned int nverified = 0, nverifyfailed = 0; static const char *directory = NULL, *dsdir = NULL; @@ -171,6 +174,10 @@ static isc_boolean_t disable_zone_check = ISC_FALSE; static isc_boolean_t update_chain = ISC_FALSE; static isc_boolean_t set_keyttl = ISC_FALSE; static dns_ttl_t keyttl; +static isc_boolean_t smartsign = ISC_FALSE; +static isc_boolean_t remove_orphans = ISC_FALSE; +static isc_boolean_t output_dnssec_only = ISC_FALSE; +static isc_boolean_t output_stdout = ISC_FALSE; #define INCSTAT(counter) \ if (printstats) { \ @@ -182,19 +189,71 @@ static dns_ttl_t keyttl; static void sign(isc_task_t *task, isc_event_t *event); -#define check_dns_dbiterator_current(result) \ - check_result((result == DNS_R_NEWORIGIN) ? ISC_R_SUCCESS : result, \ - "dns_dbiterator_current()") - static void dumpnode(dns_name_t *name, dns_dbnode_t *node) { + dns_rdataset_t rds; + dns_rdatasetiter_t *iter = NULL; + isc_buffer_t *buffer = NULL; + isc_region_t r; isc_result_t result; + unsigned bufsize = 4096; if (outputformat != dns_masterformat_text) return; - result = dns_master_dumpnodetostream(mctx, gdb, gversion, node, name, - masterstyle, fp); - check_result(result, "dns_master_dumpnodetostream"); + + if (!output_dnssec_only) { + result = dns_master_dumpnodetostream(mctx, gdb, gversion, node, + name, masterstyle, fp); + check_result(result, "dns_master_dumpnodetostream"); + return; + } + + result = dns_db_allrdatasets(gdb, node, gversion, 0, &iter); + check_result(result, "dns_db_allrdatasets"); + + dns_rdataset_init(&rds); + + result = isc_buffer_allocate(mctx, &buffer, bufsize); + check_result(result, "isc_buffer_allocate"); + + for (result = dns_rdatasetiter_first(iter); + result == ISC_R_SUCCESS; + result = dns_rdatasetiter_next(iter)) { + + dns_rdatasetiter_current(iter, &rds); + + if (rds.type != dns_rdatatype_rrsig && + rds.type != dns_rdatatype_nsec && + rds.type != dns_rdatatype_nsec3 && + rds.type != dns_rdatatype_nsec3param && + (!smartsign || rds.type != dns_rdatatype_dnskey)) { + dns_rdataset_disassociate(&rds); + continue; + } + + for (;;) { + result = dns_master_rdatasettotext(name, &rds, + masterstyle, buffer); + if (result != ISC_R_NOSPACE) + break; + + bufsize <<= 1; + isc_buffer_free(&buffer); + result = isc_buffer_allocate(mctx, &buffer, bufsize); + check_result(result, "isc_buffer_allocate"); + } + check_result(result, "dns_master_rdatasettotext"); + + isc_buffer_usedregion(buffer, &r); + result = isc_stdio_write(r.base, 1, r.length, fp, NULL); + check_result(result, "isc_stdio_write"); + isc_buffer_clear(buffer); + + dns_rdataset_disassociate(&rds); + } + + isc_buffer_free(&buffer); + dns_rdatasetiter_destroy(&iter); } /*% @@ -206,7 +265,7 @@ signwithkey(dns_name_t *name, dns_rdataset_t *rdataset, dst_key_t *key, dns_ttl_t ttl, dns_diff_t *add, const char *logmsg) { isc_result_t result; - isc_stdtime_t jendtime; + isc_stdtime_t jendtime, expiry; char keystr[DST_KEY_FORMATSIZE]; dns_rdata_t trdata = DNS_RDATA_INIT; unsigned char array[BUFSIZE]; @@ -216,7 +275,12 @@ signwithkey(dns_name_t *name, dns_rdataset_t *rdataset, dst_key_t *key, dst_key_format(key, keystr, sizeof(keystr)); vbprintf(1, "\t%s %s\n", logmsg, keystr); - jendtime = (jitter != 0) ? isc_random_jitter(endtime, jitter) : endtime; + if (rdataset->type == dns_rdatatype_dnskey) + expiry = dnskey_endtime; + else + expiry = endtime; + + jendtime = (jitter != 0) ? isc_random_jitter(expiry, jitter) : expiry; isc_buffer_init(&b, array, sizeof(array)); result = dns_dnssec_sign(name, rdataset, key, &starttime, &jendtime, mctx, &b, &trdata); @@ -253,6 +317,12 @@ issigningkey(dns_dnsseckey_t *key) { return (key->force_sign || key->hint_sign); } +static inline isc_boolean_t +ispublishedkey(dns_dnsseckey_t *key) { + return ((key->force_publish || key->hint_publish) && + !key->hint_remove); +} + static inline isc_boolean_t iszonekey(dns_dnsseckey_t *key) { return (ISC_TF(dns_name_equal(dst_key_name(key->key), gorigin) && @@ -334,13 +404,16 @@ keythatsigned(dns_rdata_rrsig_t *rrsig) { directory, mctx, &privkey); if (result == ISC_R_SUCCESS) { dst_key_free(&pubkey); - dns_dnsseckey_create(mctx, &privkey, &key); - } else { - dns_dnsseckey_create(mctx, &pubkey, &key); + result = dns_dnsseckey_create(mctx, &privkey, &key); + } else + result = dns_dnsseckey_create(mctx, &pubkey, &key); + + if (result == ISC_R_SUCCESS) { + key->force_publish = ISC_FALSE; + key->force_sign = ISC_FALSE; + key->index = keycount++; + ISC_LIST_APPEND(keylist, key, link); } - key->force_publish = ISC_FALSE; - key->force_sign = ISC_FALSE; - ISC_LIST_APPEND(keylist, key, link); isc_rwlock_unlock(&keylist_lock, isc_rwlocktype_write); return (key); @@ -481,38 +554,38 @@ signset(dns_diff_t *del, dns_diff_t *add, dns_dbnode_t *node, dns_name_t *name, "private dnskey not found\n", sigstr); } else if (key == NULL || future) { + keep = (!expired && !remove_orphans); vbprintf(2, "\trrsig by %s %s - dnskey not found\n", - expired ? "retained" : "dropped", sigstr); - if (!expired) - keep = ISC_TRUE; + keep ? "retained" : "dropped", sigstr); } else if (issigningkey(key)) { + wassignedby[key->index] = ISC_TRUE; + if (!expired && rrsig.originalttl == set->ttl && setverifies(name, set, key->key, &sigrdata)) { vbprintf(2, "\trrsig by %s retained\n", sigstr); keep = ISC_TRUE; - wassignedby[key->index] = ISC_TRUE; - nowsignedby[key->index] = ISC_TRUE; } else { vbprintf(2, "\trrsig by %s dropped - %s\n", sigstr, expired ? "expired" : rrsig.originalttl != set->ttl ? "ttl change" : "failed to verify"); - wassignedby[key->index] = ISC_TRUE; resign = ISC_TRUE; } + } else if (!ispublishedkey(key) && remove_orphans) { + vbprintf(2, "\trrsig by %s dropped - dnskey removed\n", + sigstr); } else if (iszonekey(key)) { + wassignedby[key->index] = ISC_TRUE; + if (!expired && rrsig.originalttl == set->ttl && setverifies(name, set, key->key, &sigrdata)) { vbprintf(2, "\trrsig by %s retained\n", sigstr); keep = ISC_TRUE; - wassignedby[key->index] = ISC_TRUE; - nowsignedby[key->index] = ISC_TRUE; } else { vbprintf(2, "\trrsig by %s dropped - %s\n", sigstr, expired ? "expired" : rrsig.originalttl != set->ttl ? "ttl change" : "failed to verify"); - wassignedby[key->index] = ISC_TRUE; } } else if (!expired) { vbprintf(2, "\trrsig by %s retained\n", sigstr); @@ -545,6 +618,7 @@ signset(dns_diff_t *del, dns_diff_t *add, dns_dbnode_t *node, dns_name_t *name, } } else { tuple = NULL; + vbprintf(2, "removing signature by %s\n", sigstr); result = dns_difftuple_create(mctx, DNS_DIFFOP_DEL, name, sigset.ttl, &sigrdata, &tuple); @@ -921,26 +995,6 @@ loadds(dns_name_t *name, isc_uint32_t ttl, dns_rdataset_t *dsset) { return (result); } -static isc_boolean_t -delegation(dns_name_t *name, dns_dbnode_t *node, isc_uint32_t *ttlp) { - dns_rdataset_t nsset; - isc_result_t result; - - if (dns_name_equal(name, gorigin)) - return (ISC_FALSE); - - dns_rdataset_init(&nsset); - result = dns_db_findrdataset(gdb, node, gversion, dns_rdatatype_ns, - 0, 0, &nsset, NULL); - if (dns_rdataset_isassociated(&nsset)) { - if (ttlp != NULL) - *ttlp = nsset.ttl; - dns_rdataset_disassociate(&nsset); - } - - return (ISC_TF(result == ISC_R_SUCCESS)); -} - static isc_boolean_t secure(dns_name_t *name, dns_dbnode_t *node) { dns_rdataset_t dsset; @@ -976,7 +1030,7 @@ signname(dns_dbnode_t *node, dns_name_t *name) { /* * Determine if this is a delegation point. */ - if (delegation(name, node, NULL)) + if (is_delegation(gdb, gversion, gorigin, name, node, NULL)) isdelegation = ISC_TRUE; /* @@ -1309,485 +1363,6 @@ postsign(void) { dns_dbiterator_destroy(&gdbiter); } -static isc_boolean_t -goodsig(dns_rdata_t *sigrdata, dns_name_t *name, dns_rdataset_t *keyrdataset, - dns_rdataset_t *rdataset) -{ - dns_rdata_dnskey_t key; - dns_rdata_rrsig_t sig; - dst_key_t *dstkey = NULL; - isc_result_t result; - - dns_rdata_tostruct(sigrdata, &sig, NULL); - - for (result = dns_rdataset_first(keyrdataset); - result == ISC_R_SUCCESS; - result = dns_rdataset_next(keyrdataset)) { - dns_rdata_t rdata = DNS_RDATA_INIT; - dns_rdataset_current(keyrdataset, &rdata); - dns_rdata_tostruct(&rdata, &key, NULL); - result = dns_dnssec_keyfromrdata(gorigin, &rdata, mctx, - &dstkey); - if (result != ISC_R_SUCCESS) - return (ISC_FALSE); - if (sig.algorithm != key.algorithm || - sig.keyid != dst_key_id(dstkey) || - !dns_name_equal(&sig.signer, gorigin)) { - dst_key_free(&dstkey); - continue; - } - result = dns_dnssec_verify(name, rdataset, dstkey, ISC_FALSE, - mctx, sigrdata); - dst_key_free(&dstkey); - if (result == ISC_R_SUCCESS) - return(ISC_TRUE); - } - return (ISC_FALSE); -} - -static void -verifyset(dns_rdataset_t *rdataset, dns_name_t *name, dns_dbnode_t *node, - dns_rdataset_t *keyrdataset, unsigned char *ksk_algorithms, - unsigned char *bad_algorithms) -{ - unsigned char set_algorithms[256]; - char namebuf[DNS_NAME_FORMATSIZE]; - char algbuf[80]; - char typebuf[80]; - dns_rdataset_t sigrdataset; - dns_rdatasetiter_t *rdsiter = NULL; - isc_result_t result; - int i; - - dns_rdataset_init(&sigrdataset); - result = dns_db_allrdatasets(gdb, node, gversion, 0, &rdsiter); - check_result(result, "dns_db_allrdatasets()"); - for (result = dns_rdatasetiter_first(rdsiter); - result == ISC_R_SUCCESS; - result = dns_rdatasetiter_next(rdsiter)) { - dns_rdatasetiter_current(rdsiter, &sigrdataset); - if (sigrdataset.type == dns_rdatatype_rrsig && - sigrdataset.covers == rdataset->type) - break; - dns_rdataset_disassociate(&sigrdataset); - } - if (result != ISC_R_SUCCESS) { - dns_name_format(name, namebuf, sizeof(namebuf)); - type_format(rdataset->type, typebuf, sizeof(typebuf)); - fprintf(stderr, "no signatures for %s/%s\n", namebuf, typebuf); - for (i = 0; i < 256; i++) - if (ksk_algorithms[i] != 0) - bad_algorithms[i] = 1; - return; - } - - memset(set_algorithms, 0, sizeof(set_algorithms)); - for (result = dns_rdataset_first(&sigrdataset); - result == ISC_R_SUCCESS; - result = dns_rdataset_next(&sigrdataset)) { - dns_rdata_t rdata = DNS_RDATA_INIT; - dns_rdata_rrsig_t sig; - - dns_rdataset_current(&sigrdataset, &rdata); - dns_rdata_tostruct(&rdata, &sig, NULL); - if (rdataset->ttl != sig.originalttl) { - dns_name_format(name, namebuf, sizeof(namebuf)); - type_format(rdataset->type, typebuf, sizeof(typebuf)); - fprintf(stderr, "TTL mismatch for %s %s keytag %u\n", - namebuf, typebuf, sig.keyid); - continue; - } - if ((set_algorithms[sig.algorithm] != 0) || - (ksk_algorithms[sig.algorithm] == 0)) - continue; - if (goodsig(&rdata, name, keyrdataset, rdataset)) - set_algorithms[sig.algorithm] = 1; - } - dns_rdatasetiter_destroy(&rdsiter); - if (memcmp(set_algorithms, ksk_algorithms, sizeof(set_algorithms))) { - dns_name_format(name, namebuf, sizeof(namebuf)); - type_format(rdataset->type, typebuf, sizeof(typebuf)); - for (i = 0; i < 256; i++) - if ((ksk_algorithms[i] != 0) && - (set_algorithms[i] == 0)) { - dns_secalg_format(i, algbuf, sizeof(algbuf)); - fprintf(stderr, "Missing %s signature for " - "%s %s\n", algbuf, namebuf, typebuf); - bad_algorithms[i] = 1; - } - } - dns_rdataset_disassociate(&sigrdataset); -} - -static void -verifynode(dns_name_t *name, dns_dbnode_t *node, isc_boolean_t delegation, - dns_rdataset_t *keyrdataset, unsigned char *ksk_algorithms, - unsigned char *bad_algorithms) -{ - dns_rdataset_t rdataset; - dns_rdatasetiter_t *rdsiter = NULL; - isc_result_t result; - - result = dns_db_allrdatasets(gdb, node, gversion, 0, &rdsiter); - check_result(result, "dns_db_allrdatasets()"); - result = dns_rdatasetiter_first(rdsiter); - dns_rdataset_init(&rdataset); - while (result == ISC_R_SUCCESS) { - dns_rdatasetiter_current(rdsiter, &rdataset); - if (rdataset.type != dns_rdatatype_rrsig && - rdataset.type != dns_rdatatype_dnskey && - (!delegation || rdataset.type == dns_rdatatype_ds || - rdataset.type == dns_rdatatype_nsec)) { - verifyset(&rdataset, name, node, keyrdataset, - ksk_algorithms, bad_algorithms); - } - dns_rdataset_disassociate(&rdataset); - result = dns_rdatasetiter_next(rdsiter); - } - if (result != ISC_R_NOMORE) - fatal("rdataset iteration failed: %s", - isc_result_totext(result)); - dns_rdatasetiter_destroy(&rdsiter); -} - -/*% - * Verify that certain things are sane: - * - * The apex has a DNSKEY RRset with at least one KSK, and at least - * one ZSK if the -x flag was not used. - * - * The DNSKEY record was signed with at least one of the KSKs in - * the DNSKEY RRset. - * - * The rest of the zone was signed with at least one of the ZSKs - * present in the DNSKEY RRset. - */ -static void -verifyzone(void) { - char algbuf[80]; - dns_dbiterator_t *dbiter = NULL; - dns_dbnode_t *node = NULL, *nextnode = NULL; - dns_fixedname_t fname, fnextname, fzonecut; - dns_name_t *name, *nextname, *zonecut; - dns_rdata_dnskey_t dnskey; - dns_rdata_t rdata = DNS_RDATA_INIT; - dns_rdataset_t keyset, soaset; - dns_rdataset_t keysigs, soasigs; - int i; - isc_boolean_t done = ISC_FALSE; - isc_boolean_t first = ISC_TRUE; - isc_boolean_t goodksk = ISC_FALSE; - isc_result_t result; - unsigned char revoked_ksk[256]; - unsigned char revoked_zsk[256]; - unsigned char standby_ksk[256]; - unsigned char standby_zsk[256]; - unsigned char ksk_algorithms[256]; - unsigned char zsk_algorithms[256]; - unsigned char bad_algorithms[256]; -#ifdef ALLOW_KSKLESS_ZONES - isc_boolean_t allzsksigned = ISC_TRUE; - unsigned char self_algorithms[256]; -#endif - - if (disable_zone_check) - return; - - result = dns_db_findnode(gdb, gorigin, ISC_FALSE, &node); - if (result != ISC_R_SUCCESS) - fatal("failed to find the zone's origin: %s", - isc_result_totext(result)); - - dns_rdataset_init(&keyset); - dns_rdataset_init(&keysigs); - dns_rdataset_init(&soaset); - dns_rdataset_init(&soasigs); - - result = dns_db_findrdataset(gdb, node, gversion, - dns_rdatatype_dnskey, - 0, 0, &keyset, &keysigs); - if (result != ISC_R_SUCCESS) - fatal("cannot find DNSKEY rrset\n"); - - result = dns_db_findrdataset(gdb, node, gversion, - dns_rdatatype_soa, - 0, 0, &soaset, &soasigs); - dns_db_detachnode(gdb, &node); - if (result != ISC_R_SUCCESS) - fatal("cannot find SOA rrset\n"); - - if (!dns_rdataset_isassociated(&keysigs)) - fatal("cannot find DNSKEY RRSIGs\n"); - - if (!dns_rdataset_isassociated(&soasigs)) - fatal("cannot find SOA RRSIGs\n"); - - memset(revoked_ksk, 0, sizeof(revoked_ksk)); - memset(revoked_zsk, 0, sizeof(revoked_zsk)); - memset(standby_ksk, 0, sizeof(standby_ksk)); - memset(standby_zsk, 0, sizeof(standby_zsk)); - memset(ksk_algorithms, 0, sizeof(ksk_algorithms)); - memset(zsk_algorithms, 0, sizeof(zsk_algorithms)); - memset(bad_algorithms, 0, sizeof(bad_algorithms)); -#ifdef ALLOW_KSKLESS_ZONES - memset(self_algorithms, 0, sizeof(self_algorithms)); -#endif - - /* - * Check that the DNSKEY RR has at least one self signing KSK - * and one ZSK per algorithm in it (or, if -x was used, one - * self-signing KSK). - */ - for (result = dns_rdataset_first(&keyset); - result == ISC_R_SUCCESS; - result = dns_rdataset_next(&keyset)) { - dns_rdataset_current(&keyset, &rdata); - result = dns_rdata_tostruct(&rdata, &dnskey, NULL); - check_result(result, "dns_rdata_tostruct"); - - if ((dnskey.flags & DNS_KEYOWNER_ZONE) == 0) - ; - else if ((dnskey.flags & DNS_KEYFLAG_REVOKE) != 0) { - if ((dnskey.flags & DNS_KEYFLAG_KSK) != 0 && - !dns_dnssec_selfsigns(&rdata, gorigin, &keyset, - &keysigs, ISC_FALSE, - mctx)) { - char namebuf[DNS_NAME_FORMATSIZE]; - char buffer[1024]; - isc_buffer_t buf; - - dns_name_format(gorigin, namebuf, - sizeof(namebuf)); - isc_buffer_init(&buf, buffer, sizeof(buffer)); - result = dns_rdata_totext(&rdata, NULL, &buf); - check_result(result, "dns_rdata_totext"); - fatal("revoked KSK is not self signed:\n" - "%s DNSKEY %.*s", namebuf, - (int)isc_buffer_usedlength(&buf), buffer); - } - if ((dnskey.flags & DNS_KEYFLAG_KSK) != 0 && - revoked_ksk[dnskey.algorithm] != 255) - revoked_ksk[dnskey.algorithm]++; - else if ((dnskey.flags & DNS_KEYFLAG_KSK) == 0 && - revoked_zsk[dnskey.algorithm] != 255) - revoked_zsk[dnskey.algorithm]++; - } else if ((dnskey.flags & DNS_KEYFLAG_KSK) != 0) { - if (dns_dnssec_selfsigns(&rdata, gorigin, &keyset, - &keysigs, ISC_FALSE, mctx)) { - if (ksk_algorithms[dnskey.algorithm] != 255) - ksk_algorithms[dnskey.algorithm]++; - goodksk = ISC_TRUE; - } else { - if (standby_ksk[dnskey.algorithm] != 255) - standby_ksk[dnskey.algorithm]++; - } - } else if (dns_dnssec_selfsigns(&rdata, gorigin, &keyset, - &keysigs, ISC_FALSE, - mctx)) { -#ifdef ALLOW_KSKLESS_ZONES - if (self_algorithms[dnskey.algorithm] != 255) - self_algorithms[dnskey.algorithm]++; -#endif - if (zsk_algorithms[dnskey.algorithm] != 255) - zsk_algorithms[dnskey.algorithm]++; - } else if (dns_dnssec_signs(&rdata, gorigin, &soaset, - &soasigs, ISC_FALSE, mctx)) { - if (zsk_algorithms[dnskey.algorithm] != 255) - zsk_algorithms[dnskey.algorithm]++; - } else { - if (standby_zsk[dnskey.algorithm] != 255) - standby_zsk[dnskey.algorithm]++; -#ifdef ALLOW_KSKLESS_ZONES - allzsksigned = ISC_FALSE; -#endif - } - dns_rdata_freestruct(&dnskey); - dns_rdata_reset(&rdata); - } - dns_rdataset_disassociate(&keysigs); - dns_rdataset_disassociate(&soaset); - dns_rdataset_disassociate(&soasigs); - -#ifdef ALLOW_KSKLESS_ZONES - if (!goodksk) { - if (!ignore_kskflag) - fprintf(stderr, "No self signing KSK found. Using " - "self signed ZSK's for active " - "algorithm list.\n"); - memcpy(ksk_algorithms, self_algorithms, sizeof(ksk_algorithms)); - if (!allzsksigned) - fprintf(stderr, "warning: not all ZSK's are self " - "signed.\n"); - } -#else - if (!goodksk) { - fatal("No self signed KSK's found"); - } -#endif - - fprintf(stderr, "Verifying the zone using the following algorithms:"); - for (i = 0; i < 256; i++) { -#ifdef ALLOW_KSKLESS_ZONES - if (ksk_algorithms[i] != 0 || zsk_algorithms[i] != 0) -#else - if (ksk_algorithms[i] != 0) -#endif - { - dns_secalg_format(i, algbuf, sizeof(algbuf)); - fprintf(stderr, " %s", algbuf); - } - } - fprintf(stderr, ".\n"); - - if (!ignore_kskflag && !keyset_kskonly) { - for (i = 0; i < 256; i++) { - /* - * The counts should both be zero or both be non-zero. - * Mark the algorithm as bad if this is not met. - */ - if ((ksk_algorithms[i] != 0) == - (zsk_algorithms[i] != 0)) - continue; - dns_secalg_format(i, algbuf, sizeof(algbuf)); - fprintf(stderr, "Missing %s for algorithm %s\n", - (ksk_algorithms[i] != 0) - ? "ZSK" - : "self signing KSK", - algbuf); - bad_algorithms[i] = 1; - } - } - - /* - * Check that all the other records were signed by keys that are - * present in the DNSKEY RRSET. - */ - - dns_fixedname_init(&fname); - name = dns_fixedname_name(&fname); - dns_fixedname_init(&fnextname); - nextname = dns_fixedname_name(&fnextname); - dns_fixedname_init(&fzonecut); - zonecut = NULL; - - result = dns_db_createiterator(gdb, DNS_DB_NONSEC3, &dbiter); - check_result(result, "dns_db_createiterator()"); - - result = dns_dbiterator_first(dbiter); - check_result(result, "dns_dbiterator_first()"); - - while (!done) { - isc_boolean_t isdelegation = ISC_FALSE; - - result = dns_dbiterator_current(dbiter, &node, name); - check_dns_dbiterator_current(result); - if (!dns_name_issubdomain(name, gorigin)) { - dns_db_detachnode(gdb, &node); - result = dns_dbiterator_next(dbiter); - if (result == ISC_R_NOMORE) - done = ISC_TRUE; - else - check_result(result, "dns_dbiterator_next()"); - continue; - } - if (delegation(name, node, NULL)) { - zonecut = dns_fixedname_name(&fzonecut); - dns_name_copy(name, zonecut, NULL); - isdelegation = ISC_TRUE; - } - verifynode(name, node, isdelegation, &keyset, - ksk_algorithms, bad_algorithms); - result = dns_dbiterator_next(dbiter); - nextnode = NULL; - while (result == ISC_R_SUCCESS) { - result = dns_dbiterator_current(dbiter, &nextnode, - nextname); - check_dns_dbiterator_current(result); - if (!dns_name_issubdomain(nextname, gorigin) || - (zonecut != NULL && - dns_name_issubdomain(nextname, zonecut))) - { - dns_db_detachnode(gdb, &nextnode); - result = dns_dbiterator_next(dbiter); - continue; - } - dns_db_detachnode(gdb, &nextnode); - break; - } - if (result == ISC_R_NOMORE) { - done = ISC_TRUE; - } else if (result != ISC_R_SUCCESS) - fatal("iterating through the database failed: %s", - isc_result_totext(result)); - dns_db_detachnode(gdb, &node); - } - - dns_dbiterator_destroy(&dbiter); - - result = dns_db_createiterator(gdb, DNS_DB_NSEC3ONLY, &dbiter); - check_result(result, "dns_db_createiterator()"); - - for (result = dns_dbiterator_first(dbiter); - result == ISC_R_SUCCESS; - result = dns_dbiterator_next(dbiter) ) { - result = dns_dbiterator_current(dbiter, &node, name); - check_dns_dbiterator_current(result); - verifynode(name, node, ISC_FALSE, &keyset, - ksk_algorithms, bad_algorithms); - dns_db_detachnode(gdb, &node); - } - dns_dbiterator_destroy(&dbiter); - - dns_rdataset_disassociate(&keyset); - - /* - * If we made it this far, we have what we consider a properly signed - * zone. Set the good flag. - */ - for (i = 0; i < 256; i++) { - if (bad_algorithms[i] != 0) { - if (first) - fprintf(stderr, "The zone is not fully signed " - "for the following algorithms:"); - dns_secalg_format(i, algbuf, sizeof(algbuf)); - fprintf(stderr, " %s", algbuf); - first = ISC_FALSE; - } - } - if (!first) { - fprintf(stderr, ".\n"); - fatal("DNSSEC completeness test failed."); - } - - if (goodksk || ignore_kskflag) { - /* - * Print the success summary. - */ - fprintf(stderr, "Zone signing complete:\n"); - for (i = 0; i < 256; i++) { - if ((ksk_algorithms[i] != 0) || - (standby_ksk[i] != 0) || - (revoked_zsk[i] != 0) || - (zsk_algorithms[i] != 0) || - (standby_zsk[i] != 0) || - (revoked_zsk[i] != 0)) { - dns_secalg_format(i, algbuf, sizeof(algbuf)); - fprintf(stderr, "Algorithm: %s: KSKs: " - "%u active, %u stand-by, %u revoked\n", - algbuf, ksk_algorithms[i], - standby_ksk[i], revoked_ksk[i]); - fprintf(stderr, "%*sZSKs: " - "%u active, %u %s, %u revoked\n", - (int) strlen(algbuf) + 13, "", - zsk_algorithms[i], - standby_zsk[i], - keyset_kskonly ? "present" : "stand-by", - revoked_zsk[i]); - } - } - } -} - /*% * Sign the apex of the zone. * Note the origin may not be the first node if there are out of zone @@ -1885,7 +1460,7 @@ assignwork(isc_task_t *task, isc_task_t *worker) { if (dns_name_issubdomain(name, gorigin) && (zonecut == NULL || !dns_name_issubdomain(name, zonecut))) { - if (delegation(name, node, NULL)) { + if (is_delegation(gdb, gversion, gorigin, name, node, NULL)) { dns_fixedname_init(&fzonecut); zonecut = dns_fixedname_name(&fzonecut); dns_name_copy(name, zonecut, NULL); @@ -2181,7 +1756,7 @@ nsecify(void) { remove_records(node, dns_rdatatype_nsec3param, ISC_TRUE); - if (delegation(name, node, &nsttl)) { + if (is_delegation(gdb, gversion, gorigin, name, node, &nsttl)) { zonecut = dns_fixedname_name(&fzonecut); dns_name_copy(name, zonecut, NULL); remove_sigs(node, 0); @@ -2622,7 +2197,9 @@ nsec3ify(unsigned int hashalg, unsigned int iterations, result = dns_dbiterator_next(dbiter); continue; } - if (delegation(nextname, nextnode, &nsttl)) { + if (is_delegation(gdb, gversion, gorigin, + nextname, nextnode, &nsttl)) + { zonecut = dns_fixedname_name(&fzonecut); dns_name_copy(nextname, zonecut, NULL); remove_sigs(nextnode, 0); @@ -2751,7 +2328,9 @@ nsec3ify(unsigned int hashalg, unsigned int iterations, result = dns_dbiterator_next(dbiter); continue; } - if (delegation(nextname, nextnode, NULL)) { + if (is_delegation(gdb, gversion, gorigin, + nextname, nextnode, NULL)) + { zonecut = dns_fixedname_name(&fzonecut); dns_name_copy(nextname, zonecut, NULL); if (OPTOUT(nsec3flags) && @@ -3324,10 +2903,16 @@ usage(void) { fprintf(stderr, "update DS records based on child zones' " "dsset-* files\n"); fprintf(stderr, "\t-s [YYYYMMDDHHMMSS|+offset]:\n"); - fprintf(stderr, "\t\tRRSIG start time - absolute|offset (now - 1 hour)\n"); + fprintf(stderr, "\t\tRRSIG start time " + "- absolute|offset (now - 1 hour)\n"); fprintf(stderr, "\t-e [YYYYMMDDHHMMSS|+offset|\"now\"+offset]:\n"); - fprintf(stderr, "\t\tRRSIG end time - absolute|from start|from now " + fprintf(stderr, "\t\tRRSIG end time " + "- absolute|from start|from now " "(now + 30 days)\n"); + fprintf(stderr, "\t-X [YYYYMMDDHHMMSS|+offset|\"now\"+offset]:\n"); + fprintf(stderr, "\t\tDNSKEY RRSIG end " + "- absolute|from start|from now " + "(matches -e)\n"); fprintf(stderr, "\t-i interval:\n"); fprintf(stderr, "\t\tcycle interval - resign " "if < interval from end ( (end-start)/4 )\n"); @@ -3345,6 +2930,8 @@ usage(void) { fprintf(stderr, "\t\tfile format of signed zone file (text)\n"); fprintf(stderr, "\t-N format:\n"); fprintf(stderr, "\t\tsoa serial format of signed zone file (keep)\n"); + fprintf(stderr, "\t-D:\n"); + fprintf(stderr, "\t\toutput only DNSSEC-related records\n"); fprintf(stderr, "\t-r randomdev:\n"); fprintf(stderr, "\t\ta file containing random data\n"); fprintf(stderr, "\t-a:\t"); @@ -3361,6 +2948,8 @@ usage(void) { fprintf(stderr, "use pseudorandom data (faster but less secure)\n"); fprintf(stderr, "\t-P:\t"); fprintf(stderr, "disable post-sign verification\n"); + fprintf(stderr, "\t-R:\t"); + fprintf(stderr, "remove signatures from keys that no longer exist\n"); fprintf(stderr, "\t-T TTL:\tTTL for newly added DNSKEYs\n"); fprintf(stderr, "\t-t:\t"); fprintf(stderr, "print statistics\n"); @@ -3398,36 +2987,39 @@ print_stats(isc_time_t *timer_start, isc_time_t *timer_finish, isc_uint64_t time_us; /* Time in microseconds */ isc_uint64_t time_ms; /* Time in milliseconds */ isc_uint64_t sig_ms; /* Signatures per millisecond */ + FILE *out = output_stdout ? stderr : stdout; - printf("Signatures generated: %10d\n", nsigned); - printf("Signatures retained: %10d\n", nretained); - printf("Signatures dropped: %10d\n", ndropped); - printf("Signatures successfully verified: %10d\n", nverified); - printf("Signatures unsuccessfully verified: %10d\n", nverifyfailed); + fprintf(out, "Signatures generated: %10d\n", nsigned); + fprintf(out, "Signatures retained: %10d\n", nretained); + fprintf(out, "Signatures dropped: %10d\n", ndropped); + fprintf(out, "Signatures successfully verified: %10d\n", nverified); + fprintf(out, "Signatures unsuccessfully " + "verified: %10d\n", nverifyfailed); time_us = isc_time_microdiff(sign_finish, sign_start); time_ms = time_us / 1000; - printf("Signing time in seconds: %7u.%03u\n", - (unsigned int) (time_ms / 1000), - (unsigned int) (time_ms % 1000)); + fprintf(out, "Signing time in seconds: %7u.%03u\n", + (unsigned int) (time_ms / 1000), + (unsigned int) (time_ms % 1000)); if (time_us > 0) { sig_ms = ((isc_uint64_t)nsigned * 1000000000) / time_us; - printf("Signatures per second: %7u.%03u\n", - (unsigned int) sig_ms / 1000, - (unsigned int) sig_ms % 1000); + fprintf(out, "Signatures per second: %7u.%03u\n", + (unsigned int) sig_ms / 1000, + (unsigned int) sig_ms % 1000); } time_us = isc_time_microdiff(timer_finish, timer_start); time_ms = time_us / 1000; - printf("Runtime in seconds: %7u.%03u\n", - (unsigned int) (time_ms / 1000), - (unsigned int) (time_ms % 1000)); + fprintf(out, "Runtime in seconds: %7u.%03u\n", + (unsigned int) (time_ms / 1000), + (unsigned int) (time_ms % 1000)); } int main(int argc, char *argv[]) { int i, ch; char *startstr = NULL, *endstr = NULL, *classname = NULL; + char *dnskey_endstr = NULL; char *origin = NULL, *file = NULL, *output = NULL; char *inputformatstr = NULL, *outputformatstr = NULL; char *serialformatstr = NULL; @@ -3447,20 +3039,20 @@ main(int argc, char *argv[]) { #endif unsigned int eflags; isc_boolean_t free_output = ISC_FALSE; - int tempfilelen; + int tempfilelen = 0; dns_rdataclass_t rdclass; isc_task_t **tasks = NULL; isc_buffer_t b; int len; hashlist_t hashlist; - isc_boolean_t smartsign = ISC_FALSE; isc_boolean_t make_keyset = ISC_FALSE; isc_boolean_t set_salt = ISC_FALSE; isc_boolean_t set_optout = ISC_FALSE; isc_boolean_t set_iter = ISC_FALSE; + isc_boolean_t nonsecify = ISC_FALSE; #define CMDLINE_FLAGS \ - "3:AaCc:Dd:E:e:f:FghH:i:I:j:K:k:l:m:n:N:o:O:pPr:s:ST:tuUv:xz" + "3:AaCc:Dd:E:e:f:FghH:i:I:j:K:k:L:l:m:n:N:o:O:PpRr:s:ST:tuUv:X:xzZ:" /* * Process memory debugging argument first. @@ -3546,6 +3138,10 @@ main(int argc, char *argv[]) { dsdir, isc_result_totext(result)); break; + case 'D': + output_dnssec_only = ISC_TRUE; + break; + case 'E': engine = isc_commandline_argument; break; @@ -3556,6 +3152,8 @@ main(int argc, char *argv[]) { case 'f': output = isc_commandline_argument; + if (strcmp(output, "-") == 0) + output_stdout = ISC_TRUE; break; case 'g': @@ -3604,6 +3202,17 @@ main(int argc, char *argv[]) { dskeyfile[ndskeys++] = isc_commandline_argument; break; + case 'L': + snset = ISC_TRUE; + endp = NULL; + serialnum = strtol(isc_commandline_argument, &endp, 0); + if (*endp != '\0') { + fprintf(stderr, "source serial number " + "must be numeric"); + exit(1); + } + break; + case 'l': len = strlen(isc_commandline_argument); isc_buffer_init(&b, isc_commandline_argument, len); @@ -3646,6 +3255,10 @@ main(int argc, char *argv[]) { pseudorandom = ISC_TRUE; break; + case 'R': + remove_orphans = ISC_TRUE; + break; + case 'r': setup_entropy(mctx, isc_commandline_argument, &ectx); break; @@ -3683,6 +3296,10 @@ main(int argc, char *argv[]) { fatal("verbose level must be numeric"); break; + case 'X': + dnskey_endstr = isc_commandline_argument; + break; + case 'x': keyset_kskonly = ISC_TRUE; break; @@ -3705,6 +3322,10 @@ main(int argc, char *argv[]) { fprintf(stderr, "%s: unhandled option -%c\n", program, isc_commandline_option); exit(1); + case 'Z': /* Undocumented test options */ + if (!strcmp(isc_commandline_argument, "nonsecify")) + nonsecify = ISC_TRUE; + break; } } @@ -3730,11 +3351,19 @@ main(int argc, char *argv[]) { } else starttime = now - 3600; /* Allow for some clock skew. */ - if (endstr != NULL) { + if (endstr != NULL) endtime = strtotime(endstr, now, starttime); - } else + else endtime = starttime + (30 * 24 * 60 * 60); + if (dnskey_endstr != NULL) { + dnskey_endtime = strtotime(dnskey_endstr, now, starttime); + if (endstr != NULL && dnskey_endtime == endtime) + fprintf(stderr, "WARNING: -e and -X were both set, " + "but have identical values.\n"); + } else + dnskey_endtime = endtime; + if (cycle == -1) cycle = (endtime - starttime) / 4; @@ -3777,16 +3406,36 @@ main(int argc, char *argv[]) { inputformat = dns_masterformat_text; else if (strcasecmp(inputformatstr, "raw") == 0) inputformat = dns_masterformat_raw; - else - fatal("unknown file format: %s\n", inputformatstr); + else if (strncasecmp(inputformatstr, "raw=", 4) == 0) { + inputformat = dns_masterformat_raw; + fprintf(stderr, + "WARNING: input format version ignored\n"); + } else + fatal("unknown file format: %s", inputformatstr); + } if (outputformatstr != NULL) { - if (strcasecmp(outputformatstr, "text") == 0) + if (strcasecmp(outputformatstr, "text") == 0) { outputformat = dns_masterformat_text; - else if (strcasecmp(outputformatstr, "raw") == 0) + } else if (strcasecmp(outputformatstr, "full") == 0) { + outputformat = dns_masterformat_text; + masterstyle = &dns_master_style_full; + } else if (strcasecmp(outputformatstr, "raw") == 0) { outputformat = dns_masterformat_raw; - else + } else if (strncasecmp(outputformatstr, "raw=", 4) == 0) { + char *end; + outputformat = dns_masterformat_raw; + + outputformat = dns_masterformat_raw; + rawversion = strtol(outputformatstr + 4, &end, 10); + if (end == outputformatstr + 4 || *end != '\0' || + rawversion > 1U) { + fprintf(stderr, + "unknown raw format version\n"); + exit(1); + } + } else fatal("unknown file format: %s\n", outputformatstr); } @@ -3803,6 +3452,12 @@ main(int argc, char *argv[]) { serialformatstr); } + if (output_dnssec_only && outputformat != dns_masterformat_text) + fatal("option -D can only be used with \"-O text\"\n"); + + if (output_dnssec_only && serialformat != SOA_SERIAL_KEEP) + fatal("option -D can only be used with \"-N keep\"\n"); + result = dns_master_stylecreate(&dsstyle, DNS_STYLEFLAG_NO_TTL, 0, 24, 0, 0, 0, 8, mctx); check_result(result, "dns_master_stylecreate"); @@ -3826,18 +3481,6 @@ main(int argc, char *argv[]) { else set_nsec3params(update_chain, set_salt, set_optout, set_iter); - if (IS_NSEC3) { - isc_boolean_t answer; - hash_length = dns_nsec3_hashlength(dns_hash_sha1); - hashlist_init(&hashlist, dns_db_nodecount(gdb) * 2, - hash_length); - result = dns_nsec_nseconly(gdb, gversion, &answer); - check_result(result, "dns_nsec_nseconly"); - if (answer) - fatal("NSEC3 generation requested with " - "NSEC only DNSKEY"); - } - /* * We need to do this early on, as we start messing with the list * of keys rather early. @@ -3890,6 +3533,22 @@ main(int argc, char *argv[]) { if (IS_NSEC3) { unsigned int max; + isc_boolean_t answer; + + hash_length = dns_nsec3_hashlength(dns_hash_sha1); + hashlist_init(&hashlist, dns_db_nodecount(gdb) * 2, + hash_length); + result = dns_nsec_nseconly(gdb, gversion, &answer); + if (result == ISC_R_NOTFOUND) + fprintf(stderr, "%s: warning: NSEC3 generation " + "requested with no DNSKEY; ignoring\n", + program); + else if (result != ISC_R_SUCCESS) + check_result(result, "dns_nsec_nseconly"); + else if (answer) + fatal("NSEC3 generation requested with " + "NSEC-only DNSKEY"); + result = dns_nsec3_maxiterations(gdb, NULL, mctx, &max); check_result(result, "dns_nsec3_maxiterations()"); if (nsec3iter > max) @@ -3916,11 +3575,13 @@ main(int argc, char *argv[]) { remove_duplicates(); - if (IS_NSEC3) - nsec3ify(dns_hash_sha1, nsec3iter, salt, salt_length, - &hashlist); - else - nsecify(); + if (!nonsecify) { + if (IS_NSEC3) + nsec3ify(dns_hash_sha1, nsec3iter, salt, salt_length, + &hashlist); + else + nsecify(); + } if (!nokeys) { writeset("dsset-", dns_rdatatype_ds); @@ -3931,24 +3592,29 @@ main(int argc, char *argv[]) { } } - tempfilelen = strlen(output) + 20; - tempfile = isc_mem_get(mctx, tempfilelen); - if (tempfile == NULL) - fatal("out of memory"); + if (output_stdout) { + fp = stdout; + if (outputformatstr == NULL) + masterstyle = &dns_master_style_full; + } else { + tempfilelen = strlen(output) + 20; + tempfile = isc_mem_get(mctx, tempfilelen); + if (tempfile == NULL) + fatal("out of memory"); - result = isc_file_mktemplate(output, tempfile, tempfilelen); - check_result(result, "isc_file_mktemplate"); + result = isc_file_mktemplate(output, tempfile, tempfilelen); + check_result(result, "isc_file_mktemplate"); - fp = NULL; - if (outputformat == dns_masterformat_text) - result = isc_file_openunique(tempfile, &fp); - else - result = isc_file_bopenunique(tempfile, &fp); - if (result != ISC_R_SUCCESS) - fatal("failed to open temporary output file: %s", - isc_result_totext(result)); - removefile = ISC_TRUE; - setfatalcallback(&removetempfile); + if (outputformat == dns_masterformat_text) + result = isc_file_openunique(tempfile, &fp); + else + result = isc_file_bopenunique(tempfile, &fp); + if (result != ISC_R_SUCCESS) + fatal("failed to open temporary output file: %s", + isc_result_totext(result)); + removefile = ISC_TRUE; + setfatalcallback(&removetempfile); + } print_time(fp); print_version(fp); @@ -4005,29 +3671,42 @@ main(int argc, char *argv[]) { isc_mem_put(mctx, tasks, ntasks * sizeof(isc_task_t *)); postsign(); TIME_NOW(&sign_finish); - verifyzone(); + + if (!disable_zone_check) + verifyzone(gdb, gversion, gorigin, mctx, + ignore_kskflag, keyset_kskonly); if (outputformat != dns_masterformat_text) { - result = dns_master_dumptostream2(mctx, gdb, gversion, + dns_masterrawheader_t header; + dns_master_initrawheader(&header); + if (rawversion == 0U) + header.flags = DNS_MASTERRAW_COMPAT; + else if (snset) { + header.flags = DNS_MASTERRAW_SOURCESERIALSET; + header.sourceserial = serialnum; + } + result = dns_master_dumptostream3(mctx, gdb, gversion, masterstyle, outputformat, - fp); - check_result(result, "dns_master_dumptostream2"); + &header, fp); + check_result(result, "dns_master_dumptostream3"); } - result = isc_stdio_close(fp); - check_result(result, "isc_stdio_close"); - removefile = ISC_FALSE; - - result = isc_file_rename(tempfile, output); - if (result != ISC_R_SUCCESS) - fatal("failed to rename temp file to %s: %s\n", - output, isc_result_totext(result)); - DESTROYLOCK(&namelock); if (printstats) DESTROYLOCK(&statslock); - printf("%s\n", output); + if (!output_stdout) { + result = isc_stdio_close(fp); + check_result(result, "isc_stdio_close"); + removefile = ISC_FALSE; + + result = isc_file_rename(tempfile, output); + if (result != ISC_R_SUCCESS) + fatal("failed to rename temp file to %s: %s\n", + output, isc_result_totext(result)); + + printf("%s\n", output); + } dns_db_closeversion(gdb, &gversion, ISC_FALSE); dns_db_detach(&gdb); @@ -4038,7 +3717,8 @@ main(int argc, char *argv[]) { dns_dnsseckey_destroy(mctx, &key); } - isc_mem_put(mctx, tempfile, tempfilelen); + if (tempfilelen != 0) + isc_mem_put(mctx, tempfile, tempfilelen); if (free_output) isc_mem_free(mctx, output); diff --git a/contrib/bind9/bin/dnssec/dnssec-signzone.docbook b/contrib/bind9/bin/dnssec/dnssec-signzone.docbook index 128ebe96341..e427fc1266b 100644 --- a/contrib/bind9/bin/dnssec/dnssec-signzone.docbook +++ b/contrib/bind9/bin/dnssec/dnssec-signzone.docbook @@ -2,7 +2,7 @@ "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd" []> - + June 05, 2009 @@ -43,6 +43,7 @@ 2007 2008 2009 + 2011 Internet Systems Consortium, Inc. ("ISC") @@ -60,6 +61,7 @@ + @@ -67,6 +69,7 @@ + @@ -74,8 +77,9 @@ - + + @@ -83,6 +87,7 @@ + @@ -151,6 +156,22 @@
+ + -D + + + Output only those record types automatically managed by + dnssec-signzone, i.e. RRSIG, NSEC, + NSEC3 and NSEC3PARAM records. If smart signing + () is used, DNSKEY records are also + included. The resulting file can be included in the original + zone file with $INCLUDE. This option + cannot be combined with or serial + number updating. + + + + -E engine @@ -237,14 +258,41 @@ + + -X extended end-time + + + Specify the date and time when the generated RRSIG records + for the DNSKEY RRset will expire. This is to be used in cases + when the DNSKEY signatures need to persist longer than + signatures on other records; e.g., when the private component + of the KSK is kept offline and the KSK signature is to be + refreshed manually. + + + As with , an absolute + time is indicated in YYYYMMDDHHMMSS notation. A time relative + to the start time is indicated with +N, which is N seconds from + the start time. A time relative to the current time is + indicated with now+N. If no is + specified, the value of is used as + the default. (, in turn, defaults to + 30 days from the start time.) + must be later than . + + + + -f output-file The name of the output file containing the signed zone. The default is to append .signed to - the - input filename. + the input filename. If is + set to "-", then the signed zone is + written to the standard output, with a default output + format of "full". @@ -324,6 +372,17 @@ + + -L serial + + + When writing a signed zone to 'raw' format, set the "source serial" + value in the header to the specified serial number. (This is + expected to be used primarily for testing purposes.) + + + + -n ncpus @@ -388,7 +447,15 @@ The format of the output file containing the signed zone. Possible formats are "text" (default) - and "raw". + "full", which is text output in a + format suitable for processing by external scripts, + and "raw" or "raw=N", + which store the zone in a binary format for rapid loading + by named. "raw=N" + specifies the format version of the raw zone file: if N + is 0, the raw file can be read by any version of + named; if N is 1, the file can be + read by release 9.9.0 or higher. The default is 1. @@ -421,6 +488,24 @@ + + -R + + + Remove signatures from keys that no longer exist. + + + Normally, when a previously-signed zone is passed as input + to the signer, and a DNSKEY record has been removed and + replaced with a new one, signatures from the old key + that are still within their validity period are retained. + This allows the zone to continue to validate with cached + copies of the old DNSKEY RRset. The forces + dnssec-signzone to remove all orphaned + signatures. + + + -r randomdev @@ -508,15 +593,17 @@ -T ttl - Specifies the TTL to be used for new DNSKEY records imported - into the zone from the key repository. If not specified, - the default is the minimum TTL value from the zone's SOA + Specifies a TTL to be used for new DNSKEY records imported + into the zone from the key repository. If not + specified, the default is the TTL value from the zone's SOA record. This option is ignored when signing without , since DNSKEY records are not imported from the key repository in that case. It is also ignored if there are any pre-existing DNSKEY records at the zone apex, in which case new records' TTL values will be set to match - them. + them, or if any of the imported DNSKEY records had a default + TTL value. In the event of a a conflict between TTL values in + imported keys, the shortest one is used. diff --git a/contrib/bind9/bin/dnssec/dnssec-signzone.html b/contrib/bind9/bin/dnssec/dnssec-signzone.html index 82185c6477d..37994539b60 100644 --- a/contrib/bind9/bin/dnssec/dnssec-signzone.html +++ b/contrib/bind9/bin/dnssec/dnssec-signzone.html @@ -1,5 +1,5 @@ + + + + + April 12, 2012 + + + + dnssec-verify + 8 + BIND9 + + + + dnssec-verify + DNSSEC zone verification tool + + + + + 2012 + Internet Systems Consortium, Inc. ("ISC") + + + + + + dnssec-verify + + + + + + + + zonefile + + + + + DESCRIPTION + dnssec-verify + verifies that a zone is fully signed for each algorithm found + in the DNSKEY RRset for the zone, and that the NSEC / NSEC3 + chains are complete. + + + + + OPTIONS + + + + -c class + + + Specifies the DNS class of the zone. + + + + + + -I input-format + + + The format of the input zone file. + Possible formats are "text" (default) + and "raw". + This option is primarily intended to be used for dynamic + signed zones so that the dumped zone file in a non-text + format containing updates can be verified independently. + The use of this option does not make much sense for + non-dynamic zones. + + + + + + -o origin + + + The zone origin. If not specified, the name of the zone file + is assumed to be the origin. + + + + + + -v level + + + Sets the debugging level. + + + + + + -x + + + Only verify that the DNSKEY RRset is signed with key-signing + keys. Without this flag, it is assumed that the DNSKEY RRset + will be signed by all active keys. When this flag is set, + it will not be an error if the DNSKEY RRset is not signed + by zone-signing keys. This corresponds to the + option in dnssec-signzone. + + + + + + -z + + + Ignore the KSK flag on the keys when determining whether + the zone if correctly signed. Without this flag it is + assumed that there will be a non-revoked, self-signed + DNSKEY with the KSK flag set for each algorithm and + that RRsets other than DNSKEY RRset will be signed with + a different DNSKEY without the KSK flag set. + + + With this flag set, we only require that for each algorithm, + there will be at least one non-revoked, self-signed DNSKEY, + regardless of the KSK flag state, and that other RRsets + will be signed by a non-revoked key for the same algorithm + that includes the self-signed key; the same key may be used + for both purposes. This corresponds to the + option in dnssec-signzone. + + + + + + zonefile + + + The file containing the zone to be signed. + + + + + + + + + SEE ALSO + + + dnssec-signzone8 + , + BIND 9 Administrator Reference Manual, + RFC 4033. + + + + + AUTHOR + Internet Systems Consortium + + + + diff --git a/contrib/bind9/bin/dnssec/dnssec-verify.html b/contrib/bind9/bin/dnssec/dnssec-verify.html new file mode 100644 index 00000000000..135556f5100 --- /dev/null +++ b/contrib/bind9/bin/dnssec/dnssec-verify.html @@ -0,0 +1,117 @@ + + + + + +dnssec-verify + + +
+
+
+

Name

+

dnssec-verify — DNSSEC zone verification tool

+
+
+

Synopsis

+

dnssec-verify [-c class] [-E engine] [-I input-format] [-o origin] [-v level] [-x] [-z] {zonefile}

+
+
+

DESCRIPTION

+

dnssec-verify + verifies that a zone is fully signed for each algorithm found + in the DNSKEY RRset for the zone, and that the NSEC / NSEC3 + chains are complete. +

+
+
+

OPTIONS

+
+
-c class
+

+ Specifies the DNS class of the zone. +

+
-I input-format
+

+ The format of the input zone file. + Possible formats are "text" (default) + and "raw". + This option is primarily intended to be used for dynamic + signed zones so that the dumped zone file in a non-text + format containing updates can be verified independently. + The use of this option does not make much sense for + non-dynamic zones. +

+
-o origin
+

+ The zone origin. If not specified, the name of the zone file + is assumed to be the origin. +

+
-v level
+

+ Sets the debugging level. +

+
-x
+

+ Only verify that the DNSKEY RRset is signed with key-signing + keys. Without this flag, it is assumed that the DNSKEY RRset + will be signed by all active keys. When this flag is set, + it will not be an error if the DNSKEY RRset is not signed + by zone-signing keys. This corresponds to the -x + option in dnssec-signzone. +

+
-z
+
+

+ Ignore the KSK flag on the keys when determining whether + the zone if correctly signed. Without this flag it is + assumed that there will be a non-revoked, self-signed + DNSKEY with the KSK flag set for each algorithm and + that RRsets other than DNSKEY RRset will be signed with + a different DNSKEY without the KSK flag set. +

+

+ With this flag set, we only require that for each algorithm, + there will be at least one non-revoked, self-signed DNSKEY, + regardless of the KSK flag state, and that other RRsets + will be signed by a non-revoked key for the same algorithm + that includes the self-signed key; the same key may be used + for both purposes. This corresponds to the -z + option in dnssec-signzone. +

+
+
zonefile
+

+ The file containing the zone to be signed. +

+
+
+
+

SEE ALSO

+

+ dnssec-signzone(8), + BIND 9 Administrator Reference Manual, + RFC 4033. +

+
+
+

AUTHOR

+

Internet Systems Consortium +

+
+
+ diff --git a/contrib/bind9/bin/dnssec/dnssectool.c b/contrib/bind9/bin/dnssec/dnssectool.c index 882b042f1b8..7c8c6ce2254 100644 --- a/contrib/bind9/bin/dnssec/dnssectool.c +++ b/contrib/bind9/bin/dnssec/dnssectool.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004, 2005, 2007, 2009-2011 Internet Systems Consortium, Inc. ("ISC") + * Copyright (C) 2004, 2005, 2007, 2009-2013 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 2000, 2001, 2003 Internet Software Consortium. * * Permission to use, copy, modify, and/or distribute this software for any @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: dnssectool.c,v 1.60.162.3 2011/10/21 03:56:32 marka Exp $ */ +/* $Id: dnssectool.c,v 1.63 2011/10/21 03:55:33 marka Exp $ */ /*! \file */ @@ -27,9 +27,11 @@ #include +#include #include #include #include +#include #include #include #include @@ -37,12 +39,19 @@ #include #include +#include +#include #include +#include #include #include #include +#include +#include #include #include +#include +#include #include #include #include @@ -50,6 +59,18 @@ #include "dnssectool.h" +static isc_heap_t *expected_chains, *found_chains; + +struct nsec3_chain_fixed { + isc_uint8_t hash; + isc_uint8_t salt_length; + isc_uint8_t next_length; + isc_uint16_t iterations; + /* unsigned char salt[0]; */ + /* unsigned char owner[0]; */ + /* unsigned char next[0]; */ +}; + extern int verbose; extern const char *program; @@ -467,3 +488,1314 @@ key_collision(dst_key_t *dstkey, dns_name_t *name, const char *dir, return (conflict); } + +isc_boolean_t +is_delegation(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *origin, + dns_name_t *name, dns_dbnode_t *node, isc_uint32_t *ttlp) +{ + dns_rdataset_t nsset; + isc_result_t result; + + if (dns_name_equal(name, origin)) + return (ISC_FALSE); + + dns_rdataset_init(&nsset); + result = dns_db_findrdataset(db, node, ver, dns_rdatatype_ns, + 0, 0, &nsset, NULL); + if (dns_rdataset_isassociated(&nsset)) { + if (ttlp != NULL) + *ttlp = nsset.ttl; + dns_rdataset_disassociate(&nsset); + } + + return (ISC_TF(result == ISC_R_SUCCESS)); +} + +static isc_boolean_t +goodsig(dns_name_t *origin, dns_rdata_t *sigrdata, dns_name_t *name, + dns_rdataset_t *keyrdataset, dns_rdataset_t *rdataset, isc_mem_t *mctx) +{ + dns_rdata_dnskey_t key; + dns_rdata_rrsig_t sig; + dst_key_t *dstkey = NULL; + isc_result_t result; + + result = dns_rdata_tostruct(sigrdata, &sig, NULL); + check_result(result, "dns_rdata_tostruct()"); + + for (result = dns_rdataset_first(keyrdataset); + result == ISC_R_SUCCESS; + result = dns_rdataset_next(keyrdataset)) { + dns_rdata_t rdata = DNS_RDATA_INIT; + dns_rdataset_current(keyrdataset, &rdata); + result = dns_rdata_tostruct(&rdata, &key, NULL); + check_result(result, "dns_rdata_tostruct()"); + result = dns_dnssec_keyfromrdata(origin, &rdata, mctx, + &dstkey); + if (result != ISC_R_SUCCESS) + return (ISC_FALSE); + if (sig.algorithm != key.algorithm || + sig.keyid != dst_key_id(dstkey) || + !dns_name_equal(&sig.signer, origin)) { + dst_key_free(&dstkey); + continue; + } + result = dns_dnssec_verify(name, rdataset, dstkey, ISC_FALSE, + mctx, sigrdata); + dst_key_free(&dstkey); + if (result == ISC_R_SUCCESS) + return(ISC_TRUE); + } + return (ISC_FALSE); +} + +static isc_result_t +verifynsec(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name, + dns_dbnode_t *node, dns_name_t *nextname) +{ + unsigned char buffer[DNS_NSEC_BUFFERSIZE]; + char namebuf[DNS_NAME_FORMATSIZE]; + char nextbuf[DNS_NAME_FORMATSIZE]; + char found[DNS_NAME_FORMATSIZE]; + dns_rdataset_t rdataset; + dns_rdata_t rdata = DNS_RDATA_INIT; + dns_rdata_t tmprdata = DNS_RDATA_INIT; + dns_rdata_nsec_t nsec; + isc_result_t result; + + dns_rdataset_init(&rdataset); + result = dns_db_findrdataset(db, node, ver, dns_rdatatype_nsec, + 0, 0, &rdataset, NULL); + if (result != ISC_R_SUCCESS) { + dns_name_format(name, namebuf, sizeof(namebuf)); + fprintf(stderr, "Missing NSEC record for %s\n", namebuf); + goto failure; + } + + result = dns_rdataset_first(&rdataset); + check_result(result, "dns_rdataset_first()"); + + dns_rdataset_current(&rdataset, &rdata); + result = dns_rdata_tostruct(&rdata, &nsec, NULL); + check_result(result, "dns_rdata_tostruct()"); + /* Check bit next name is consistent */ + if (!dns_name_equal(&nsec.next, nextname)) { + dns_name_format(name, namebuf, sizeof(namebuf)); + dns_name_format(nextname, nextbuf, sizeof(nextbuf)); + dns_name_format(&nsec.next, found, sizeof(found)); + fprintf(stderr, "Bad NSEC record for %s, next name " + "mismatch (expected:%s, found:%s)\n", namebuf, + nextbuf, found); + goto failure; + } + /* Check bit map is consistent */ + result = dns_nsec_buildrdata(db, ver, node, nextname, buffer, + &tmprdata); + check_result(result, "dns_nsec_buildrdata()"); + if (dns_rdata_compare(&rdata, &tmprdata) != 0) { + dns_name_format(name, namebuf, sizeof(namebuf)); + fprintf(stderr, "Bad NSEC record for %s, bit map " + "mismatch\n", namebuf); + goto failure; + } + result = dns_rdataset_next(&rdataset); + if (result != ISC_R_NOMORE) { + dns_name_format(name, namebuf, sizeof(namebuf)); + fprintf(stderr, "Multipe NSEC records for %s\n", namebuf); + goto failure; + + } + dns_rdataset_disassociate(&rdataset); + return (ISC_R_SUCCESS); + failure: + if (dns_rdataset_isassociated(&rdataset)) + dns_rdataset_disassociate(&rdataset); + return (ISC_R_FAILURE); +} + +static void +check_no_rrsig(dns_db_t *db, dns_dbversion_t *ver, dns_rdataset_t *rdataset, + dns_name_t *name, dns_dbnode_t *node) +{ + char namebuf[DNS_NAME_FORMATSIZE]; + char typebuf[80]; + dns_rdataset_t sigrdataset; + dns_rdatasetiter_t *rdsiter = NULL; + isc_result_t result; + + dns_rdataset_init(&sigrdataset); + result = dns_db_allrdatasets(db, node, ver, 0, &rdsiter); + check_result(result, "dns_db_allrdatasets()"); + for (result = dns_rdatasetiter_first(rdsiter); + result == ISC_R_SUCCESS; + result = dns_rdatasetiter_next(rdsiter)) { + dns_rdatasetiter_current(rdsiter, &sigrdataset); + if (sigrdataset.type == dns_rdatatype_rrsig && + sigrdataset.covers == rdataset->type) + break; + dns_rdataset_disassociate(&sigrdataset); + } + if (result == ISC_R_SUCCESS) { + dns_name_format(name, namebuf, sizeof(namebuf)); + type_format(rdataset->type, typebuf, sizeof(typebuf)); + fprintf(stderr, "Warning: Found unexpected signatures for " + "%s/%s\n", namebuf, typebuf); + } + if (dns_rdataset_isassociated(&sigrdataset)) + dns_rdataset_disassociate(&sigrdataset); + dns_rdatasetiter_destroy(&rdsiter); +} + +static isc_boolean_t +chain_compare(void *arg1, void *arg2) { + struct nsec3_chain_fixed *e1 = arg1, *e2 = arg2; + size_t len; + + /* + * Do each element in turn to get a stable sort. + */ + if (e1->hash < e2->hash) + return (ISC_TRUE); + if (e1->hash > e2->hash) + return (ISC_FALSE); + if (e1->iterations < e2->iterations) + return (ISC_TRUE); + if (e1->iterations > e2->iterations) + return (ISC_FALSE); + if (e1->salt_length < e2->salt_length) + return (ISC_TRUE); + if (e1->salt_length > e2->salt_length) + return (ISC_FALSE); + if (e1->next_length < e2->next_length) + return (ISC_TRUE); + if (e1->next_length > e2->next_length) + return (ISC_FALSE); + len = e1->salt_length + 2 * e1->next_length; + if (memcmp(e1 + 1, e2 + 1, len) < 0) + return (ISC_TRUE); + return (ISC_FALSE); +} + +static isc_boolean_t +chain_equal(struct nsec3_chain_fixed *e1, struct nsec3_chain_fixed *e2) { + size_t len; + + if (e1->hash != e2->hash) + return (ISC_FALSE); + if (e1->iterations != e2->iterations) + return (ISC_FALSE); + if (e1->salt_length != e2->salt_length) + return (ISC_FALSE); + if (e1->next_length != e2->next_length) + return (ISC_FALSE); + len = e1->salt_length + 2 * e1->next_length; + if (memcmp(e1 + 1, e2 + 1, len) != 0) + return (ISC_FALSE); + return (ISC_TRUE); +} + +static isc_result_t +record_nsec3(const unsigned char *rawhash, const dns_rdata_nsec3_t *nsec3, + isc_mem_t *mctx, isc_heap_t *chains) +{ + struct nsec3_chain_fixed *element; + size_t len; + unsigned char *cp; + isc_result_t result; + + len = sizeof(*element) + nsec3->next_length * 2 + nsec3->salt_length; + + element = isc_mem_get(mctx, len); + if (element == NULL) + return (ISC_R_NOMEMORY); + memset(element, 0, len); + element->hash = nsec3->hash; + element->salt_length = nsec3->salt_length; + element->next_length = nsec3->next_length; + element->iterations = nsec3->iterations; + cp = (unsigned char *)(element + 1); + memcpy(cp, nsec3->salt, nsec3->salt_length); + cp += nsec3->salt_length; + memcpy(cp, rawhash, nsec3->next_length); + cp += nsec3->next_length; + memcpy(cp, nsec3->next, nsec3->next_length); + result = isc_heap_insert(chains, element); + if (result != ISC_R_SUCCESS) { + fprintf(stderr, "isc_heap_insert failed: %s\n", + isc_result_totext(result)); + isc_mem_put(mctx, element, len); + } + return (result); +} + +static isc_result_t +match_nsec3(dns_name_t *name, isc_mem_t *mctx, + dns_rdata_nsec3param_t *nsec3param, dns_rdataset_t *rdataset, + unsigned char types[8192], unsigned int maxtype, + unsigned char *rawhash, size_t rhsize) +{ + unsigned char cbm[8244]; + char namebuf[DNS_NAME_FORMATSIZE]; + dns_rdata_nsec3_t nsec3; + isc_result_t result; + unsigned int len; + + /* + * Find matching NSEC3 record. + */ + for (result = dns_rdataset_first(rdataset); + result == ISC_R_SUCCESS; + result = dns_rdataset_next(rdataset)) { + dns_rdata_t rdata = DNS_RDATA_INIT; + dns_rdataset_current(rdataset, &rdata); + result = dns_rdata_tostruct(&rdata, &nsec3, NULL); + check_result(result, "dns_rdata_tostruct()"); + if (nsec3.hash == nsec3param->hash && + nsec3.next_length == rhsize && + nsec3.iterations == nsec3param->iterations && + nsec3.salt_length == nsec3param->salt_length && + memcmp(nsec3.salt, nsec3param->salt, + nsec3param->salt_length) == 0) + break; + } + if (result != ISC_R_SUCCESS) { + dns_name_format(name, namebuf, sizeof(namebuf)); + fprintf(stderr, "Missing NSEC3 record for %s\n", namebuf); + return (result); + } + + /* + * Check the type list. + */ + len = dns_nsec_compressbitmap(cbm, types, maxtype); + if (nsec3.len != len || memcmp(cbm, nsec3.typebits, len) != 0) { + dns_name_format(name, namebuf, sizeof(namebuf)); + fprintf(stderr, "Bad NSEC3 record for %s, bit map " + "mismatch\n", namebuf); + return (ISC_R_FAILURE); + } + + /* + * Record chain. + */ + result = record_nsec3(rawhash, &nsec3, mctx, expected_chains); + check_result(result, "record_nsec3()"); + + /* + * Make sure there is only one NSEC3 record with this set of + * parameters. + */ + for (result = dns_rdataset_next(rdataset); + result == ISC_R_SUCCESS; + result = dns_rdataset_next(rdataset)) { + dns_rdata_t rdata = DNS_RDATA_INIT; + dns_rdataset_current(rdataset, &rdata); + result = dns_rdata_tostruct(&rdata, &nsec3, NULL); + check_result(result, "dns_rdata_tostruct()"); + if (nsec3.hash == nsec3param->hash && + nsec3.iterations == nsec3param->iterations && + nsec3.salt_length == nsec3param->salt_length && + memcmp(nsec3.salt, nsec3param->salt, + nsec3.salt_length) == 0) { + dns_name_format(name, namebuf, sizeof(namebuf)); + fprintf(stderr, "Multiple NSEC3 records with the " + "same parameter set for %s", namebuf); + result = DNS_R_DUPLICATE; + break; + } + } + if (result != ISC_R_NOMORE) + return (result); + + result = ISC_R_SUCCESS; + return (result); +} + +static isc_boolean_t +innsec3params(dns_rdata_nsec3_t *nsec3, dns_rdataset_t *nsec3paramset) { + dns_rdata_nsec3param_t nsec3param; + isc_result_t result; + + for (result = dns_rdataset_first(nsec3paramset); + result == ISC_R_SUCCESS; + result = dns_rdataset_next(nsec3paramset)) { + dns_rdata_t rdata = DNS_RDATA_INIT; + + dns_rdataset_current(nsec3paramset, &rdata); + result = dns_rdata_tostruct(&rdata, &nsec3param, NULL); + check_result(result, "dns_rdata_tostruct()"); + if (nsec3param.flags == 0 && + nsec3param.hash == nsec3->hash && + nsec3param.iterations == nsec3->iterations && + nsec3param.salt_length == nsec3->salt_length && + memcmp(nsec3param.salt, nsec3->salt, + nsec3->salt_length) == 0) + return (ISC_TRUE); + } + return (ISC_FALSE); +} + +static isc_result_t +record_found(dns_db_t *db, dns_dbversion_t *ver, isc_mem_t *mctx, + dns_name_t *name, dns_dbnode_t *node, + dns_rdataset_t *nsec3paramset) +{ + unsigned char owner[NSEC3_MAX_HASH_LENGTH]; + dns_rdata_nsec3_t nsec3; + dns_rdataset_t rdataset; + dns_label_t hashlabel; + isc_buffer_t b; + isc_result_t result; + + if (nsec3paramset == NULL || !dns_rdataset_isassociated(nsec3paramset)) + return (ISC_R_SUCCESS); + + dns_rdataset_init(&rdataset); + result = dns_db_findrdataset(db, node, ver, dns_rdatatype_nsec3, + 0, 0, &rdataset, NULL); + if (result != ISC_R_SUCCESS) + return (ISC_R_SUCCESS); + + dns_name_getlabel(name, 0, &hashlabel); + isc_region_consume(&hashlabel, 1); + isc_buffer_init(&b, owner, sizeof(owner)); + result = isc_base32hex_decoderegion(&hashlabel, &b); + if (result != ISC_R_SUCCESS) + goto cleanup; + + for (result = dns_rdataset_first(&rdataset); + result == ISC_R_SUCCESS; + result = dns_rdataset_next(&rdataset)) { + dns_rdata_t rdata = DNS_RDATA_INIT; + dns_rdataset_current(&rdataset, &rdata); + result = dns_rdata_tostruct(&rdata, &nsec3, NULL); + check_result(result, "dns_rdata_tostruct()"); + if (nsec3.next_length != isc_buffer_usedlength(&b)) + continue; + /* + * We only care about NSEC3 records that match a NSEC3PARAM + * record. + */ + if (!innsec3params(&nsec3, nsec3paramset)) + continue; + + /* + * Record chain. + */ + result = record_nsec3(owner, &nsec3, mctx, found_chains); + check_result(result, "record_nsec3()"); + } + + cleanup: + dns_rdataset_disassociate(&rdataset); + return (ISC_R_SUCCESS); +} + +static isc_boolean_t +isoptout(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *origin, + dns_rdata_t *nsec3rdata) +{ + dns_rdataset_t rdataset; + dns_rdata_t rdata = DNS_RDATA_INIT; + dns_rdata_nsec3_t nsec3; + dns_rdata_nsec3param_t nsec3param; + dns_fixedname_t fixed; + dns_name_t *hashname; + isc_result_t result; + dns_dbnode_t *node = NULL; + unsigned char rawhash[NSEC3_MAX_HASH_LENGTH]; + size_t rhsize = sizeof(rawhash); + isc_boolean_t ret; + + result = dns_rdata_tostruct(nsec3rdata, &nsec3param, NULL); + check_result(result, "dns_rdata_tostruct()"); + + dns_fixedname_init(&fixed); + result = dns_nsec3_hashname(&fixed, rawhash, &rhsize, origin, origin, + nsec3param.hash, nsec3param.iterations, + nsec3param.salt, nsec3param.salt_length); + check_result(result, "dns_nsec3_hashname()"); + + dns_rdataset_init(&rdataset); + hashname = dns_fixedname_name(&fixed); + result = dns_db_findnsec3node(db, hashname, ISC_FALSE, &node); + if (result == ISC_R_SUCCESS) + result = dns_db_findrdataset(db, node, ver, dns_rdatatype_nsec3, + 0, 0, &rdataset, NULL); + if (result != ISC_R_SUCCESS) + return (ISC_FALSE); + + result = dns_rdataset_first(&rdataset); + check_result(result, "dns_rdataset_first()"); + + dns_rdataset_current(&rdataset, &rdata); + + result = dns_rdata_tostruct(&rdata, &nsec3, NULL); + if (result != ISC_R_SUCCESS) + ret = ISC_FALSE; + else + ret = ISC_TF((nsec3.flags & DNS_NSEC3FLAG_OPTOUT) != 0); + + if (dns_rdataset_isassociated(&rdataset)) + dns_rdataset_disassociate(&rdataset); + if (node != NULL) + dns_db_detachnode(db, &node); + + return (ret); +} + +static isc_result_t +verifynsec3(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *origin, + isc_mem_t *mctx, dns_name_t *name, dns_rdata_t *rdata, + isc_boolean_t delegation, isc_boolean_t empty, + unsigned char types[8192], unsigned int maxtype) +{ + char namebuf[DNS_NAME_FORMATSIZE]; + char hashbuf[DNS_NAME_FORMATSIZE]; + dns_rdataset_t rdataset; + dns_rdata_nsec3param_t nsec3param; + dns_fixedname_t fixed; + dns_name_t *hashname; + isc_result_t result; + dns_dbnode_t *node = NULL; + unsigned char rawhash[NSEC3_MAX_HASH_LENGTH]; + size_t rhsize = sizeof(rawhash); + isc_boolean_t optout; + + result = dns_rdata_tostruct(rdata, &nsec3param, NULL); + check_result(result, "dns_rdata_tostruct()"); + + if (nsec3param.flags != 0) + return (ISC_R_SUCCESS); + + if (!dns_nsec3_supportedhash(nsec3param.hash)) + return (ISC_R_SUCCESS); + + optout = isoptout(db, ver, origin, rdata); + + dns_fixedname_init(&fixed); + result = dns_nsec3_hashname(&fixed, rawhash, &rhsize, name, origin, + nsec3param.hash, nsec3param.iterations, + nsec3param.salt, nsec3param.salt_length); + check_result(result, "dns_nsec3_hashname()"); + + /* + * We don't use dns_db_find() here as it works with the choosen + * nsec3 chain and we may also be called with uncommitted data + * from dnssec-signzone so the secure status of the zone may not + * be up to date. + */ + dns_rdataset_init(&rdataset); + hashname = dns_fixedname_name(&fixed); + result = dns_db_findnsec3node(db, hashname, ISC_FALSE, &node); + if (result == ISC_R_SUCCESS) + result = dns_db_findrdataset(db, node, ver, dns_rdatatype_nsec3, + 0, 0, &rdataset, NULL); + if (result != ISC_R_SUCCESS && + (!delegation || (empty && !optout) || + (!empty && dns_nsec_isset(types, dns_rdatatype_ds)))) + { + dns_name_format(name, namebuf, sizeof(namebuf)); + dns_name_format(hashname, hashbuf, sizeof(hashbuf)); + fprintf(stderr, "Missing NSEC3 record for %s (%s)\n", + namebuf, hashbuf); + } else if (result == ISC_R_NOTFOUND && + delegation && (!empty || optout)) + { + result = ISC_R_SUCCESS; + } else if (result == ISC_R_SUCCESS) { + result = match_nsec3(name, mctx, &nsec3param, &rdataset, + types, maxtype, rawhash, rhsize); + } + + if (dns_rdataset_isassociated(&rdataset)) + dns_rdataset_disassociate(&rdataset); + if (node != NULL) + dns_db_detachnode(db, &node); + + return (result); +} + +static isc_result_t +verifynsec3s(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *origin, + isc_mem_t *mctx, dns_name_t *name, dns_rdataset_t *nsec3paramset, + isc_boolean_t delegation, isc_boolean_t empty, + unsigned char types[8192], unsigned int maxtype) +{ + isc_result_t result; + + for (result = dns_rdataset_first(nsec3paramset); + result == ISC_R_SUCCESS; + result = dns_rdataset_next(nsec3paramset)) { + dns_rdata_t rdata = DNS_RDATA_INIT; + + dns_rdataset_current(nsec3paramset, &rdata); + result = verifynsec3(db, ver, origin, mctx, name, &rdata, + delegation, empty, types, maxtype); + if (result != ISC_R_SUCCESS) + break; + } + if (result == ISC_R_NOMORE) + result = ISC_R_SUCCESS; + return (result); +} + +static void +verifyset(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *origin, + isc_mem_t *mctx, dns_rdataset_t *rdataset, dns_name_t *name, + dns_dbnode_t *node, dns_rdataset_t *keyrdataset, + unsigned char *act_algorithms, unsigned char *bad_algorithms) +{ + unsigned char set_algorithms[256]; + char namebuf[DNS_NAME_FORMATSIZE]; + char algbuf[80]; + char typebuf[80]; + dns_rdataset_t sigrdataset; + dns_rdatasetiter_t *rdsiter = NULL; + isc_result_t result; + int i; + + dns_rdataset_init(&sigrdataset); + result = dns_db_allrdatasets(db, node, ver, 0, &rdsiter); + check_result(result, "dns_db_allrdatasets()"); + for (result = dns_rdatasetiter_first(rdsiter); + result == ISC_R_SUCCESS; + result = dns_rdatasetiter_next(rdsiter)) { + dns_rdatasetiter_current(rdsiter, &sigrdataset); + if (sigrdataset.type == dns_rdatatype_rrsig && + sigrdataset.covers == rdataset->type) + break; + dns_rdataset_disassociate(&sigrdataset); + } + if (result != ISC_R_SUCCESS) { + dns_name_format(name, namebuf, sizeof(namebuf)); + type_format(rdataset->type, typebuf, sizeof(typebuf)); + fprintf(stderr, "No signatures for %s/%s\n", namebuf, typebuf); + for (i = 0; i < 256; i++) + if (act_algorithms[i] != 0) + bad_algorithms[i] = 1; + dns_rdatasetiter_destroy(&rdsiter); + return; + } + + memset(set_algorithms, 0, sizeof(set_algorithms)); + for (result = dns_rdataset_first(&sigrdataset); + result == ISC_R_SUCCESS; + result = dns_rdataset_next(&sigrdataset)) { + dns_rdata_t rdata = DNS_RDATA_INIT; + dns_rdata_rrsig_t sig; + + dns_rdataset_current(&sigrdataset, &rdata); + result = dns_rdata_tostruct(&rdata, &sig, NULL); + check_result(result, "dns_rdata_tostruct()"); + if (rdataset->ttl != sig.originalttl) { + dns_name_format(name, namebuf, sizeof(namebuf)); + type_format(rdataset->type, typebuf, sizeof(typebuf)); + fprintf(stderr, "TTL mismatch for %s %s keytag %u\n", + namebuf, typebuf, sig.keyid); + continue; + } + if ((set_algorithms[sig.algorithm] != 0) || + (act_algorithms[sig.algorithm] == 0)) + continue; + if (goodsig(origin, &rdata, name, keyrdataset, rdataset, mctx)) + set_algorithms[sig.algorithm] = 1; + } + dns_rdatasetiter_destroy(&rdsiter); + if (memcmp(set_algorithms, act_algorithms, sizeof(set_algorithms))) { + dns_name_format(name, namebuf, sizeof(namebuf)); + type_format(rdataset->type, typebuf, sizeof(typebuf)); + for (i = 0; i < 256; i++) + if ((act_algorithms[i] != 0) && + (set_algorithms[i] == 0)) { + dns_secalg_format(i, algbuf, sizeof(algbuf)); + fprintf(stderr, "No correct %s signature for " + "%s %s\n", algbuf, namebuf, typebuf); + bad_algorithms[i] = 1; + } + } + dns_rdataset_disassociate(&sigrdataset); +} + +static isc_result_t +verifynode(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *origin, + isc_mem_t *mctx, dns_name_t *name, dns_dbnode_t *node, + isc_boolean_t delegation, dns_rdataset_t *keyrdataset, + unsigned char *act_algorithms, unsigned char *bad_algorithms, + dns_rdataset_t *nsecset, dns_rdataset_t *nsec3paramset, + dns_name_t *nextname) +{ + unsigned char types[8192]; + unsigned int maxtype = 0; + dns_rdataset_t rdataset; dns_rdatasetiter_t *rdsiter = NULL; + isc_result_t result, tresult; + + memset(types, 0, sizeof(types)); + result = dns_db_allrdatasets(db, node, ver, 0, &rdsiter); + check_result(result, "dns_db_allrdatasets()"); + result = dns_rdatasetiter_first(rdsiter); + dns_rdataset_init(&rdataset); + while (result == ISC_R_SUCCESS) { + dns_rdatasetiter_current(rdsiter, &rdataset); + /* + * If we are not at a delegation then everything should be + * signed. If we are at a delegation then only the DS set + * is signed. The NS set is not signed at a delegation but + * its existance is recorded in the bit map. Anything else + * other than NSEC and DS is not signed at a delegation. + */ + if (rdataset.type != dns_rdatatype_rrsig && + rdataset.type != dns_rdatatype_dnskey && + (!delegation || rdataset.type == dns_rdatatype_ds || + rdataset.type == dns_rdatatype_nsec)) { + verifyset(db, ver, origin, mctx, &rdataset, + name, node, keyrdataset, + act_algorithms, bad_algorithms); + dns_nsec_setbit(types, rdataset.type, 1); + if (rdataset.type > maxtype) + maxtype = rdataset.type; + } else if (rdataset.type != dns_rdatatype_rrsig && + rdataset.type != dns_rdatatype_dnskey) { + if (rdataset.type == dns_rdatatype_ns) + dns_nsec_setbit(types, rdataset.type, 1); + check_no_rrsig(db, ver, &rdataset, name, node); + } else + dns_nsec_setbit(types, rdataset.type, 1); + dns_rdataset_disassociate(&rdataset); + result = dns_rdatasetiter_next(rdsiter); + } + if (result != ISC_R_NOMORE) + fatal("rdataset iteration failed: %s", + isc_result_totext(result)); + dns_rdatasetiter_destroy(&rdsiter); + + result = ISC_R_SUCCESS; + + if (nsecset != NULL && dns_rdataset_isassociated(nsecset)) + result = verifynsec(db, ver, name, node, nextname); + + if (nsec3paramset != NULL && dns_rdataset_isassociated(nsec3paramset)) { + tresult = verifynsec3s(db, ver, origin, mctx, name, + nsec3paramset, delegation, ISC_FALSE, + types, maxtype); + if (result == ISC_R_SUCCESS && tresult != ISC_R_SUCCESS) + result = tresult; + } + return (result); +} + +static isc_boolean_t +is_empty(dns_db_t *db, dns_dbversion_t *ver, dns_dbnode_t *node) { + dns_rdatasetiter_t *rdsiter = NULL; + isc_result_t result; + + result = dns_db_allrdatasets(db, node, ver, 0, &rdsiter); + check_result(result, "dns_db_allrdatasets()"); + result = dns_rdatasetiter_first(rdsiter); + dns_rdatasetiter_destroy(&rdsiter); + if (result == ISC_R_NOMORE) + return (ISC_TRUE); + return (ISC_FALSE); +} + +static void +check_no_nsec(dns_name_t *name, dns_dbnode_t *node, dns_db_t *db, + dns_dbversion_t *ver) +{ + dns_rdataset_t rdataset; + isc_result_t result; + + dns_rdataset_init(&rdataset); + result = dns_db_findrdataset(db, node, ver, dns_rdatatype_nsec, + 0, 0, &rdataset, NULL); + if (result != ISC_R_NOTFOUND) { + char namebuf[DNS_NAME_FORMATSIZE]; + dns_name_format(name, namebuf, sizeof(namebuf)); + fatal("unexpected NSEC RRset at %s\n", namebuf); + } + + if (dns_rdataset_isassociated(&rdataset)) + dns_rdataset_disassociate(&rdataset); +} + +static isc_boolean_t +newchain(const struct nsec3_chain_fixed *first, + const struct nsec3_chain_fixed *e) +{ + if (first->hash != e->hash || + first->iterations != e->iterations || + first->salt_length != e->salt_length || + first->next_length != e->next_length || + memcmp(first + 1, e + 1, first->salt_length) != 0) + return (ISC_TRUE); + return (ISC_FALSE); +} + +static void +free_element(isc_mem_t *mctx, struct nsec3_chain_fixed *e) { + size_t len; + + len = sizeof(*e) + e->salt_length + 2 * e->next_length; + isc_mem_put(mctx, e, len); +} + +static isc_boolean_t +checknext(const struct nsec3_chain_fixed *first, + const struct nsec3_chain_fixed *e) +{ + char buf[512]; + const unsigned char *d1 = (const unsigned char *)(first + 1); + const unsigned char *d2 = (const unsigned char *)(e + 1); + isc_buffer_t b; + isc_region_t sr; + + d1 += first->salt_length + first->next_length; + d2 += e->salt_length; + + if (memcmp(d1, d2, first->next_length) == 0) + return (ISC_TRUE); + + DE_CONST(d1 - first->next_length, sr.base); + sr.length = first->next_length; + isc_buffer_init(&b, buf, sizeof(buf)); + isc_base32hex_totext(&sr, 1, "", &b); + fprintf(stderr, "Break in NSEC3 chain at: %.*s\n", + (int) isc_buffer_usedlength(&b), buf); + + DE_CONST(d1, sr.base); + sr.length = first->next_length; + isc_buffer_init(&b, buf, sizeof(buf)); + isc_base32hex_totext(&sr, 1, "", &b); + fprintf(stderr, "Expected: %.*s\n", (int) isc_buffer_usedlength(&b), + buf); + + DE_CONST(d2, sr.base); + sr.length = first->next_length; + isc_buffer_init(&b, buf, sizeof(buf)); + isc_base32hex_totext(&sr, 1, "", &b); + fprintf(stderr, "Found: %.*s\n", (int) isc_buffer_usedlength(&b), buf); + + return (ISC_FALSE); +} + +#define EXPECTEDANDFOUND "Expected and found NSEC3 chains not equal\n" + +static isc_result_t +verify_nsec3_chains(isc_mem_t *mctx) { + isc_result_t result = ISC_R_SUCCESS; + struct nsec3_chain_fixed *e, *f = NULL; + struct nsec3_chain_fixed *first = NULL, *prev = NULL; + + while ((e = isc_heap_element(expected_chains, 1)) != NULL) { + isc_heap_delete(expected_chains, 1); + if (f == NULL) + f = isc_heap_element(found_chains, 1); + if (f != NULL) { + isc_heap_delete(found_chains, 1); + + /* + * Check that they match. + */ + if (chain_equal(e, f)) { + free_element(mctx, f); + f = NULL; + } else { + if (result == ISC_R_SUCCESS) + fprintf(stderr, EXPECTEDANDFOUND); + result = ISC_R_FAILURE; + /* + * Attempt to resync found_chain. + */ + while (f != NULL && !chain_compare(e, f)) { + free_element(mctx, f); + f = isc_heap_element(found_chains, 1); + if (f != NULL) + isc_heap_delete(found_chains, 1); + if (f != NULL && chain_equal(e, f)) { + free_element(mctx, f); + f = NULL; + break; + } + } + } + } else if (result == ISC_R_SUCCESS) { + fprintf(stderr, EXPECTEDANDFOUND); + result = ISC_R_FAILURE; + } + if (first == NULL || newchain(first, e)) { + if (prev != NULL) { + if (!checknext(prev, first)) + result = ISC_R_FAILURE; + if (prev != first) + free_element(mctx, prev); + } + if (first != NULL) + free_element(mctx, first); + prev = first = e; + continue; + } + if (!checknext(prev, e)) + result = ISC_R_FAILURE; + if (prev != first) + free_element(mctx, prev); + prev = e; + } + if (prev != NULL) { + if (!checknext(prev, first)) + result = ISC_R_FAILURE; + if (prev != first) + free_element(mctx, prev); + } + if (first != NULL) + free_element(mctx, first); + do { + if (f != NULL) { + if (result == ISC_R_SUCCESS) { + fprintf(stderr, EXPECTEDANDFOUND); + result = ISC_R_FAILURE; + } + free_element(mctx, f); + } + f = isc_heap_element(found_chains, 1); + if (f != NULL) + isc_heap_delete(found_chains, 1); + } while (f != NULL); + + return (result); +} + +static isc_result_t +verifyemptynodes(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *origin, + isc_mem_t *mctx, dns_name_t *name, dns_name_t *prevname, + isc_boolean_t isdelegation, dns_rdataset_t *nsec3paramset) +{ + dns_namereln_t reln; + int order; + unsigned int labels, nlabels, i; + dns_name_t suffix; + isc_result_t result = ISC_R_SUCCESS, tresult; + + reln = dns_name_fullcompare(prevname, name, &order, &labels); + if (order >= 0) + return (result); + + nlabels = dns_name_countlabels(name); + + if (reln == dns_namereln_commonancestor || + reln == dns_namereln_contains) { + dns_name_init(&suffix, NULL); + for (i = labels + 1; i < nlabels; i++) { + dns_name_getlabelsequence(name, nlabels - i, i, + &suffix); + if (nsec3paramset != NULL && + dns_rdataset_isassociated(nsec3paramset)) { + tresult = verifynsec3s(db, ver, origin, mctx, + &suffix, nsec3paramset, + isdelegation, ISC_TRUE, + NULL, 0); + if (result == ISC_R_SUCCESS && + tresult != ISC_R_SUCCESS) + result = tresult; + } + } + } + return (result); +} + +/*% + * Verify that certain things are sane: + * + * The apex has a DNSKEY record with at least one KSK, and at least + * one ZSK if the -x flag was not used. + * + * The DNSKEY record was signed with at least one of the KSKs in this + * set. + * + * The rest of the zone was signed with at least one of the ZSKs + * present in the DNSKEY RRSET. + */ +void +verifyzone(dns_db_t *db, dns_dbversion_t *ver, + dns_name_t *origin, isc_mem_t *mctx, + isc_boolean_t ignore_kskflag, isc_boolean_t keyset_kskonly) +{ + char algbuf[80]; + dns_dbiterator_t *dbiter = NULL; + dns_dbnode_t *node = NULL, *nextnode = NULL; + dns_fixedname_t fname, fnextname, fprevname, fzonecut; + dns_name_t *name, *nextname, *prevname, *zonecut; + dns_rdata_dnskey_t dnskey; + dns_rdata_t rdata = DNS_RDATA_INIT; + dns_rdataset_t keyset, soaset; + dns_rdataset_t keysigs, soasigs; + dns_rdataset_t nsecset, nsecsigs; + dns_rdataset_t nsec3paramset, nsec3paramsigs; + int i; + isc_boolean_t done = ISC_FALSE; + isc_boolean_t first = ISC_TRUE; + isc_boolean_t goodksk = ISC_FALSE; + isc_boolean_t goodzsk = ISC_FALSE; + isc_result_t result, vresult = ISC_R_UNSET; + unsigned char revoked_ksk[256]; + unsigned char revoked_zsk[256]; + unsigned char standby_ksk[256]; + unsigned char standby_zsk[256]; + unsigned char ksk_algorithms[256]; + unsigned char zsk_algorithms[256]; + unsigned char bad_algorithms[256]; + unsigned char act_algorithms[256]; + + result = isc_heap_create(mctx, chain_compare, NULL, 1024, + &expected_chains); + check_result(result, "isc_heap_create()"); + result = isc_heap_create(mctx, chain_compare, NULL, 1024, + &found_chains); + check_result(result, "isc_heap_create()"); + + result = dns_db_findnode(db, origin, ISC_FALSE, &node); + if (result != ISC_R_SUCCESS) + fatal("failed to find the zone's origin: %s", + isc_result_totext(result)); + + dns_rdataset_init(&keyset); + dns_rdataset_init(&keysigs); + dns_rdataset_init(&soaset); + dns_rdataset_init(&soasigs); + dns_rdataset_init(&nsecset); + dns_rdataset_init(&nsecsigs); + dns_rdataset_init(&nsec3paramset); + dns_rdataset_init(&nsec3paramsigs); + result = dns_db_findrdataset(db, node, ver, dns_rdatatype_dnskey, + 0, 0, &keyset, &keysigs); + if (result != ISC_R_SUCCESS) + fatal("Zone contains no DNSSEC keys\n"); + + result = dns_db_findrdataset(db, node, ver, dns_rdatatype_soa, + 0, 0, &soaset, &soasigs); + if (result != ISC_R_SUCCESS) + fatal("Zone contains no SOA record\n"); + + result = dns_db_findrdataset(db, node, ver, dns_rdatatype_nsec, + 0, 0, &nsecset, &nsecsigs); + if (result != ISC_R_SUCCESS && result != ISC_R_NOTFOUND) + fatal("NSEC lookup failed\n"); + + result = dns_db_findrdataset(db, node, ver, dns_rdatatype_nsec3param, + 0, 0, &nsec3paramset, &nsec3paramsigs); + if (result != ISC_R_SUCCESS && result != ISC_R_NOTFOUND) + fatal("NSEC3PARAM lookup failed\n"); + + if (!dns_rdataset_isassociated(&keysigs)) + fatal("DNSKEY is not signed (keys offline or inactive?)\n"); + + if (!dns_rdataset_isassociated(&soasigs)) + fatal("SOA is not signed (keys offline or inactive?)\n"); + + if (dns_rdataset_isassociated(&nsecset) && + !dns_rdataset_isassociated(&nsecsigs)) + fatal("NSEC is not signed (keys offline or inactive?)\n"); + + if (dns_rdataset_isassociated(&nsec3paramset) && + !dns_rdataset_isassociated(&nsec3paramsigs)) + fatal("NSEC3PARAM is not signed (keys offline or inactive?)\n"); + + if (!dns_rdataset_isassociated(&nsecset) && + !dns_rdataset_isassociated(&nsec3paramset)) + fatal("No valid NSEC/NSEC3 chain for testing\n"); + + dns_db_detachnode(db, &node); + + memset(revoked_ksk, 0, sizeof(revoked_ksk)); + memset(revoked_zsk, 0, sizeof(revoked_zsk)); + memset(standby_ksk, 0, sizeof(standby_ksk)); + memset(standby_zsk, 0, sizeof(standby_zsk)); + memset(ksk_algorithms, 0, sizeof(ksk_algorithms)); + memset(zsk_algorithms, 0, sizeof(zsk_algorithms)); + memset(bad_algorithms, 0, sizeof(bad_algorithms)); + memset(act_algorithms, 0, sizeof(act_algorithms)); + + /* + * Check that the DNSKEY RR has at least one self signing KSK + * and one ZSK per algorithm in it (or, if -x was used, one + * self-signing KSK). + */ + for (result = dns_rdataset_first(&keyset); + result == ISC_R_SUCCESS; + result = dns_rdataset_next(&keyset)) { + dns_rdataset_current(&keyset, &rdata); + result = dns_rdata_tostruct(&rdata, &dnskey, NULL); + check_result(result, "dns_rdata_tostruct"); + + if ((dnskey.flags & DNS_KEYOWNER_ZONE) == 0) + ; + else if ((dnskey.flags & DNS_KEYFLAG_REVOKE) != 0) { + if ((dnskey.flags & DNS_KEYFLAG_KSK) != 0 && + !dns_dnssec_selfsigns(&rdata, origin, &keyset, + &keysigs, ISC_FALSE, + mctx)) { + char namebuf[DNS_NAME_FORMATSIZE]; + char buffer[1024]; + isc_buffer_t buf; + + dns_name_format(origin, namebuf, + sizeof(namebuf)); + isc_buffer_init(&buf, buffer, sizeof(buffer)); + result = dns_rdata_totext(&rdata, NULL, &buf); + check_result(result, "dns_rdata_totext"); + fatal("revoked KSK is not self signed:\n" + "%s DNSKEY %.*s", namebuf, + (int)isc_buffer_usedlength(&buf), buffer); + } + if ((dnskey.flags & DNS_KEYFLAG_KSK) != 0 && + revoked_ksk[dnskey.algorithm] != 255) + revoked_ksk[dnskey.algorithm]++; + else if ((dnskey.flags & DNS_KEYFLAG_KSK) == 0 && + revoked_zsk[dnskey.algorithm] != 255) + revoked_zsk[dnskey.algorithm]++; + } else if ((dnskey.flags & DNS_KEYFLAG_KSK) != 0) { + if (dns_dnssec_selfsigns(&rdata, origin, &keyset, + &keysigs, ISC_FALSE, mctx)) { + if (ksk_algorithms[dnskey.algorithm] != 255) + ksk_algorithms[dnskey.algorithm]++; + goodksk = ISC_TRUE; + } else { + if (standby_ksk[dnskey.algorithm] != 255) + standby_ksk[dnskey.algorithm]++; + } + } else if (dns_dnssec_selfsigns(&rdata, origin, &keyset, + &keysigs, ISC_FALSE, mctx)) { + if (zsk_algorithms[dnskey.algorithm] != 255) + zsk_algorithms[dnskey.algorithm]++; + goodzsk = ISC_TRUE; + } else if (dns_dnssec_signs(&rdata, origin, &soaset, + &soasigs, ISC_FALSE, mctx)) { + if (zsk_algorithms[dnskey.algorithm] != 255) + zsk_algorithms[dnskey.algorithm]++; + } else { + if (standby_zsk[dnskey.algorithm] != 255) + standby_zsk[dnskey.algorithm]++; + } + dns_rdata_freestruct(&dnskey); + dns_rdata_reset(&rdata); + } + dns_rdataset_disassociate(&keysigs); + dns_rdataset_disassociate(&soaset); + dns_rdataset_disassociate(&soasigs); + if (dns_rdataset_isassociated(&nsecsigs)) + dns_rdataset_disassociate(&nsecsigs); + if (dns_rdataset_isassociated(&nsec3paramsigs)) + dns_rdataset_disassociate(&nsec3paramsigs); + + if (ignore_kskflag ) { + if (!goodksk && !goodzsk) + fatal("No self-signed DNSKEY found."); + } else if (!goodksk) + fatal("No self-signed KSK DNSKEY found. Supply an active\n" + "key with the KSK flag set, or use '-P'."); + + fprintf(stderr, "Verifying the zone using the following algorithms:"); + for (i = 0; i < 256; i++) { + if (ignore_kskflag) + act_algorithms[i] = (ksk_algorithms[i] != 0 || + zsk_algorithms[i] != 0) ? 1 : 0; + else + act_algorithms[i] = ksk_algorithms[i] != 0 ? 1 : 0; + if (act_algorithms[i] != 0) { + dns_secalg_format(i, algbuf, sizeof(algbuf)); + fprintf(stderr, " %s", algbuf); + } + } + fprintf(stderr, ".\n"); + + if (!ignore_kskflag && !keyset_kskonly) { + for (i = 0; i < 256; i++) { + /* + * The counts should both be zero or both be non-zero. + * Mark the algorithm as bad if this is not met. + */ + if ((ksk_algorithms[i] != 0) == + (zsk_algorithms[i] != 0)) + continue; + dns_secalg_format(i, algbuf, sizeof(algbuf)); + fprintf(stderr, "Missing %s for algorithm %s\n", + (ksk_algorithms[i] != 0) + ? "ZSK" + : "self-signed KSK", + algbuf); + bad_algorithms[i] = 1; + } + } + + /* + * Check that all the other records were signed by keys that are + * present in the DNSKEY RRSET. + */ + + dns_fixedname_init(&fname); + name = dns_fixedname_name(&fname); + dns_fixedname_init(&fnextname); + nextname = dns_fixedname_name(&fnextname); + dns_fixedname_init(&fprevname); + prevname = NULL; + dns_fixedname_init(&fzonecut); + zonecut = NULL; + + result = dns_db_createiterator(db, DNS_DB_NONSEC3, &dbiter); + check_result(result, "dns_db_createiterator()"); + + result = dns_dbiterator_first(dbiter); + check_result(result, "dns_dbiterator_first()"); + + while (!done) { + isc_boolean_t isdelegation = ISC_FALSE; + + result = dns_dbiterator_current(dbiter, &node, name); + check_dns_dbiterator_current(result); + if (!dns_name_issubdomain(name, origin)) { + check_no_nsec(name, node, db, ver); + dns_db_detachnode(db, &node); + result = dns_dbiterator_next(dbiter); + if (result == ISC_R_NOMORE) + done = ISC_TRUE; + else + check_result(result, "dns_dbiterator_next()"); + continue; + } + if (is_delegation(db, ver, origin, name, node, NULL)) { + zonecut = dns_fixedname_name(&fzonecut); + dns_name_copy(name, zonecut, NULL); + isdelegation = ISC_TRUE; + } + nextnode = NULL; + result = dns_dbiterator_next(dbiter); + while (result == ISC_R_SUCCESS) { + result = dns_dbiterator_current(dbiter, &nextnode, + nextname); + check_dns_dbiterator_current(result); + if (!dns_name_issubdomain(nextname, origin) || + (zonecut != NULL && + dns_name_issubdomain(nextname, zonecut))) + { + check_no_nsec(nextname, nextnode, db, ver); + dns_db_detachnode(db, &nextnode); + result = dns_dbiterator_next(dbiter); + continue; + } + if (is_empty(db, ver, nextnode)) { + dns_db_detachnode(db, &nextnode); + result = dns_dbiterator_next(dbiter); + continue; + } + dns_db_detachnode(db, &nextnode); + break; + } + if (result == ISC_R_NOMORE) { + done = ISC_TRUE; + nextname = origin; + } else if (result != ISC_R_SUCCESS) + fatal("iterating through the database failed: %s", + isc_result_totext(result)); + result = verifynode(db, ver, origin, mctx, name, node, + isdelegation, &keyset, act_algorithms, + bad_algorithms, &nsecset, &nsec3paramset, + nextname); + if (vresult == ISC_R_UNSET) + vresult = ISC_R_SUCCESS; + if (vresult == ISC_R_SUCCESS && result != ISC_R_SUCCESS) + vresult = result; + if (prevname != NULL) { + result = verifyemptynodes(db, ver, origin, mctx, name, + prevname, isdelegation, + &nsec3paramset); + } else + prevname = dns_fixedname_name(&fprevname); + dns_name_copy(name, prevname, NULL); + if (vresult == ISC_R_SUCCESS && result != ISC_R_SUCCESS) + vresult = result; + dns_db_detachnode(db, &node); + } + + dns_dbiterator_destroy(&dbiter); + + result = dns_db_createiterator(db, DNS_DB_NSEC3ONLY, &dbiter); + check_result(result, "dns_db_createiterator()"); + + for (result = dns_dbiterator_first(dbiter); + result == ISC_R_SUCCESS; + result = dns_dbiterator_next(dbiter) ) { + result = dns_dbiterator_current(dbiter, &node, name); + check_dns_dbiterator_current(result); + result = verifynode(db, ver, origin, mctx, name, node, + ISC_FALSE, &keyset, act_algorithms, + bad_algorithms, NULL, NULL, NULL); + check_result(result, "verifynode"); + record_found(db, ver, mctx, name, node, &nsec3paramset); + dns_db_detachnode(db, &node); + } + dns_dbiterator_destroy(&dbiter); + + dns_rdataset_disassociate(&keyset); + if (dns_rdataset_isassociated(&nsecset)) + dns_rdataset_disassociate(&nsecset); + if (dns_rdataset_isassociated(&nsec3paramset)) + dns_rdataset_disassociate(&nsec3paramset); + + result = verify_nsec3_chains(mctx); + if (vresult == ISC_R_UNSET) + vresult = ISC_R_SUCCESS; + if (result != ISC_R_SUCCESS && vresult == ISC_R_SUCCESS) + vresult = result; + isc_heap_destroy(&expected_chains); + isc_heap_destroy(&found_chains); + + /* + * If we made it this far, we have what we consider a properly signed + * zone. Set the good flag. + */ + for (i = 0; i < 256; i++) { + if (bad_algorithms[i] != 0) { + if (first) + fprintf(stderr, "The zone is not fully signed " + "for the following algorithms:"); + dns_secalg_format(i, algbuf, sizeof(algbuf)); + fprintf(stderr, " %s", algbuf); + first = ISC_FALSE; + } + } + if (!first) { + fprintf(stderr, ".\n"); + fatal("DNSSEC completeness test failed."); + } + + if (vresult != ISC_R_SUCCESS) + fatal("DNSSEC completeness test failed (%s).", + dns_result_totext(vresult)); + + if (goodksk || ignore_kskflag) { + /* + * Print the success summary. + */ + fprintf(stderr, "Zone fully signed:\n"); + for (i = 0; i < 256; i++) { + if ((ksk_algorithms[i] != 0) || + (standby_ksk[i] != 0) || + (revoked_zsk[i] != 0) || + (zsk_algorithms[i] != 0) || + (standby_zsk[i] != 0) || + (revoked_zsk[i] != 0)) { + dns_secalg_format(i, algbuf, sizeof(algbuf)); + fprintf(stderr, "Algorithm: %s: KSKs: " + "%u active, %u stand-by, %u revoked\n", + algbuf, ksk_algorithms[i], + standby_ksk[i], revoked_ksk[i]); + fprintf(stderr, "%*sZSKs: " + "%u active, %u %s, %u revoked\n", + (int) strlen(algbuf) + 13, "", + zsk_algorithms[i], + standby_zsk[i], + keyset_kskonly ? "present" : "stand-by", + revoked_zsk[i]); + } + } + } +} diff --git a/contrib/bind9/bin/dnssec/dnssectool.h b/contrib/bind9/bin/dnssec/dnssectool.h index e6dfe51aeed..09b4fb103b1 100644 --- a/contrib/bind9/bin/dnssec/dnssectool.h +++ b/contrib/bind9/bin/dnssec/dnssectool.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004, 2007-2011 Internet Systems Consortium, Inc. ("ISC") + * Copyright (C) 2004, 2007-2012 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 2000, 2001, 2003 Internet Software Consortium. * * Permission to use, copy, modify, and/or distribute this software for any @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: dnssectool.h,v 1.31.162.2 2011/10/20 23:46:27 tbox Exp $ */ +/* $Id: dnssectool.h,v 1.33 2011/10/20 23:46:51 tbox Exp $ */ #ifndef DNSSECTOOL_H #define DNSSECTOOL_H 1 @@ -25,6 +25,11 @@ #include #include +#define check_dns_dbiterator_current(result) \ + check_result((result == DNS_R_NEWORIGIN) ? ISC_R_SUCCESS : result, \ + "dns_dbiterator_current()") + + typedef void (fatalcallback_t)(void); ISC_PLATFORM_NORETURN_PRE void @@ -81,4 +86,12 @@ isc_boolean_t key_collision(dst_key_t *key, dns_name_t *name, const char *dir, isc_mem_t *mctx, isc_boolean_t *exact); +isc_boolean_t +is_delegation(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *origin, + dns_name_t *name, dns_dbnode_t *node, isc_uint32_t *ttlp); + +void +verifyzone(dns_db_t *db, dns_dbversion_t *ver, + dns_name_t *origin, isc_mem_t *mctx, + isc_boolean_t ignore_kskflag, isc_boolean_t keyset_kskonly); #endif /* DNSSEC_DNSSECTOOL_H */ diff --git a/contrib/bind9/bin/named/Makefile.in b/contrib/bind9/bin/named/Makefile.in index e3ce3bd1547..68941357037 100644 --- a/contrib/bind9/bin/named/Makefile.in +++ b/contrib/bind9/bin/named/Makefile.in @@ -13,7 +13,7 @@ # OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR # PERFORMANCE OF THIS SOFTWARE. -# $Id: Makefile.in,v 1.114.14.2 2011/03/10 23:47:25 tbox Exp $ +# $Id: Makefile.in,v 1.116 2011/03/10 23:47:49 tbox Exp $ srcdir = @srcdir@ VPATH = @srcdir@ @@ -161,8 +161,11 @@ maintainer-clean:: bind9.xsl.h: bind9.xsl ${srcdir}/convertxsl.pl ${PERL} ${srcdir}/convertxsl.pl < ${srcdir}/bind9.xsl > bind9.xsl.h -depend: bind9.xsl.h -statschannel.@O@: bind9.xsl.h +bind9.ver3.xsl.h: bind9.ver3.xsl ${srcdir}/convertxsl.pl + ${PERL} ${srcdir}/convertxsl.pl < ${srcdir}/bind9.ver3.xsl > bind9.ver3.xsl.h + +depend: bind9.xsl.h bind9.ver3.xsl.h +statschannel.@O@: bind9.xsl.h bind9.ver3.xsl.h installdirs: $(SHELL) ${top_srcdir}/mkinstalldirs ${DESTDIR}${sbindir} diff --git a/contrib/bind9/bin/named/bind9.ver3.xsl b/contrib/bind9/bin/named/bind9.ver3.xsl new file mode 100644 index 00000000000..22e5c45fbef --- /dev/null +++ b/contrib/bind9/bin/named/bind9.ver3.xsl @@ -0,0 +1,738 @@ + + + + + + + + + + + + + + + + + ISC BIND 9 Statistics + + +
+

ISC Bind 9 Configuration and Statistics

+
+
+

Server Times

+ + + + + + + + + +
Boot time: + +
Sample time: + +
+
+

Incoming Requests

+ + +
[no incoming requests]
+
+ + + + + + + + + + + + +
+ + + +
Total: + +
+
+

Incoming Queries by Type

+ + +
[no incoming queries]
+
+ + + + + + even + odd + + + + + + + + + + + +
+ + + +
Total: + +
+
+

Outgoing Queries per view

+ +

View

+ + + + + + +
+ + + + + + + even + odd + + + + + + + +
+ + + +
+
+ +

Server Statistics

+ + + +
+ + + + + + + even + odd + + + + + + + +
+ + + +
+
+

Zone Maintenance Statistics

+ + + +
+ + + + + + + even + odd + + + + + + + +
+ + + +
+

Resolver Statistics (Common)

+ + + + + + even + odd + + + + + + + +
+ + + +
+ +

Resolver Statistics for View

+ + + + + + even + odd + + + + + + + +
+ + + +
+
+

Cache DB RRsets for View

+ + + + + + even + odd + + + + + + + +
+ + + +
+
+
+

Socket I/O Statistics

+ + + + + even + odd + + + + + + + +
+ + + +
+
+
+

Response Codes per view/zone

+ +

View

+ + + + + +

Zone

+ + + + + + +
+ + + + + + + even + odd + + + + + + + +
+ + + +
+ + + +

Received QTYPES per view/zone

+ +

View

+ + + + + +

Zone

+ + + + + + +
+ + + + + + + even + odd + + + + + + + +
+ + + +
+ + + +

Network Status

+ + + + + + + + + + + + + + + even + odd + + + + + + + + + + + + +
IDNameTypeReferencesLocalAddressPeerAddressState
+ + + + + + + + + + + + + + + +
+
+

Task Manager Configuration

+ + + + + + + + + + + + + + + + + +
Thread-Model + +
Worker Threads + +
Default Quantum + +
Tasks Running + +
+
+

Tasks

+ + + + + + + + + + + + + even + odd + + + + + + + + + + +
IDNameReferencesStateQuantum
+ + + + + + + + + +
+
+

Memory Usage Summary

+ + + + + even + odd + + + + + + + +
+ + + +
+
+

Memory Contexts

+ + + + + + + + + + + + + + + + + + even + odd + + + + + + + + + + + + + + + +
IDNameReferencesTotalUseInUseMaxUseBlockSizePoolsHiWaterLoWater
+ + + + + + + + + + + + + + + + + + + +
+
+ + + + + diff --git a/contrib/bind9/bin/named/bind9.ver3.xsl.h b/contrib/bind9/bin/named/bind9.ver3.xsl.h new file mode 100644 index 00000000000..c55714a6fb7 --- /dev/null +++ b/contrib/bind9/bin/named/bind9.ver3.xsl.h @@ -0,0 +1,740 @@ +/* + * Generated by convertxsl.pl 1.14 2008/07/17 23:43:26 jinmei Exp + * From \n" + "\n" + "\n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " ISC BIND 9 Statistics\n" + " \n" + " \n" + "
\n" + "

ISC Bind 9 Configuration and Statistics

\n" + "
\n" + "
\n" + "

Server Times

\n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + "
Boot time:\n" + " \n" + "
Sample time:\n" + " \n" + "
\n" + "
\n" + "

Incoming Requests

\n" + " \n" + " \n" + "
[graph incoming requests]
\n" + "
\n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + "
\n" + " \n" + " \n" + " \n" + "
Total:\n" + " \n" + "
\n" + "
\n" + "

Incoming Queries by Type

\n" + " \n" + " \n" + "
[graph incoming qtypes]
\n" + "
\n" + " \n" + " \n" + " \n" + " \n" + " \n" + " even\n" + " odd\n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + "
\n" + " \n" + " \n" + " \n" + "
Total:\n" + " \n" + "
\n" + "
\n" + "

Outgoing Queries per view

\n" + " \n" + "

View

\n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + "
\n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " even\n" + " odd\n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + "
\n" + " \n" + " \n" + " \n" + "
\n" + "
\n" + " \n" + "

Server Statistics

\n" + " \n" + " \n" + " \n" + "
\n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " even\n" + " odd\n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + "
\n" + " \n" + " \n" + " \n" + "
\n" + "
\n" + "

Zone Maintenance Statistics

\n" + " \n" + " \n" + " \n" + "
\n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " even\n" + " odd\n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + "
\n" + " \n" + " \n" + " \n" + "
\n" + "

Resolver Statistics (Common)

\n" + " \n" + " \n" + " \n" + " \n" + " \n" + " even\n" + " odd\n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + "
\n" + " \n" + " \n" + " \n" + "
\n" + " \n" + "

Resolver Statistics for View

\n" + " \n" + " \n" + " \n" + " \n" + " \n" + " even\n" + " odd\n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + "
\n" + " \n" + " \n" + " \n" + "
\n" + "
\n" + "

Cache DB RRsets for View

\n" + " \n" + " \n" + " \n" + " \n" + " \n" + " even\n" + " odd\n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + "
\n" + " \n" + " \n" + " \n" + "
\n" + "
\n" + "
\n" + "

Socket I/O Statistics

\n" + " \n" + " \n" + " \n" + " \n" + " even\n" + " odd\n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + "
\n" + " \n" + " \n" + " \n" + "
\n" + "
\n" + "
\n" + "

Response Codes per view/zone

\n" + " \n" + "

View

\n" + " \n" + " \n" + " \n" + " \n" + " \n" + "

Zone

\n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + "
\n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " even\n" + " odd\n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + "
\n" + " \n" + " \n" + " \n" + "
\n" + " \n" + " \n" + " \n" + "

Received QTYPES per view/zone

\n" + " \n" + "

View

\n" + " \n" + " \n" + " \n" + " \n" + " \n" + "

Zone

\n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + "
\n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " even\n" + " odd\n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + "
\n" + " \n" + " \n" + " \n" + "
\n" + " \n" + " \n" + " \n" + "

Network Status

\n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " even\n" + " odd\n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + "
IDNameTypeReferencesLocalAddressPeerAddressState
\n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + "
\n" + "
\n" + "

Task Manager Configuration

\n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + "
Thread-Model\n" + " \n" + "
Worker Threads\n" + " \n" + "
Default Quantum\n" + " \n" + "
Tasks Running\n" + " \n" + "
\n" + "
\n" + "

Tasks

\n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " even\n" + " odd\n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + "
IDNameReferencesStateQuantum
\n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + "
\n" + "
\n" + "

Memory Usage Summary

\n" + " \n" + " \n" + " \n" + " \n" + " even\n" + " odd\n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + "
\n" + " \n" + " \n" + " \n" + "
\n" + "
\n" + "

Memory Contexts

\n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " even\n" + " odd\n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + "
IDNameReferencesTotalUseInUseMaxUseBlockSizePoolsHiWaterLoWater
\n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + "
\n" + "
\n" + "

Internet Systems Consortium Inc.
http://www.isc.org

\n" + " \n" + " \n" + " \n" + "\n"; diff --git a/contrib/bind9/bin/named/builtin.c b/contrib/bind9/bin/named/builtin.c index 14204cd295c..4604cb3ce07 100644 --- a/contrib/bind9/bin/named/builtin.c +++ b/contrib/bind9/bin/named/builtin.c @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: builtin.c,v 1.20.14.3 2012/01/11 20:19:40 ckb Exp $ */ +/* $Id: builtin.c,v 1.26 2012/01/21 19:44:18 each Exp $ */ /*! \file * \brief @@ -281,11 +281,14 @@ dns64_cname(const dns_name_t *zone, const dns_name_t *name, static isc_result_t builtin_lookup(const char *zone, const char *name, void *dbdata, - dns_sdblookup_t *lookup) + dns_sdblookup_t *lookup, dns_clientinfomethods_t *methods, + dns_clientinfo_t *clientinfo) { builtin_t *b = (builtin_t *) dbdata; UNUSED(zone); + UNUSED(methods); + UNUSED(clientinfo); if (strcmp(name, "@") == 0) return (b->do_lookup(lookup)); @@ -295,10 +298,14 @@ builtin_lookup(const char *zone, const char *name, void *dbdata, static isc_result_t dns64_lookup(const dns_name_t *zone, const dns_name_t *name, void *dbdata, - dns_sdblookup_t *lookup) + dns_sdblookup_t *lookup, dns_clientinfomethods_t *methods, + dns_clientinfo_t *clientinfo) { builtin_t *b = (builtin_t *) dbdata; + UNUSED(methods); + UNUSED(clientinfo); + if (name->labels == 0 && name->length == 0) return (b->do_lookup(lookup)); else @@ -353,6 +360,8 @@ do_authors_lookup(dns_sdblookup_t *lookup) { "Curtis Blackburn", "James Brister", "Ben Cottrell", + "John H. DuBois III", + "Francis Dupont", "Michael Graff", "Andreas Gustafsson", "Bob Halley", diff --git a/contrib/bind9/bin/named/client.c b/contrib/bind9/bin/named/client.c index ff4ab691c18..933abc7631e 100644 --- a/contrib/bind9/bin/named/client.c +++ b/contrib/bind9/bin/named/client.c @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: client.c,v 1.271.10.4 2012/01/31 23:46:39 tbox Exp $ */ +/* $Id$ */ #include @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -116,15 +117,26 @@ struct ns_clientmgr { /* Unlocked. */ unsigned int magic; + + /* The queue object has its own locks */ + client_queue_t inactive; /*%< To be recycled */ + isc_mem_t * mctx; isc_taskmgr_t * taskmgr; isc_timermgr_t * timermgr; + + /* Lock covers manager state. */ isc_mutex_t lock; - /* Locked by lock. */ isc_boolean_t exiting; - client_list_t active; /*%< Active clients */ - client_list_t recursing; /*%< Recursing clients */ - client_list_t inactive; /*%< To be recycled */ + + /* Lock covers the clients list */ + isc_mutex_t listlock; + client_list_t clients; /*%< All active clients */ + + /* Lock covers the recursing list */ + isc_mutex_t reclock; + client_list_t recursing; /*%< Recursing clients */ + #if NMCTXS > 0 /*%< mctx pool for clients. */ unsigned int nextmctx; @@ -188,6 +200,12 @@ struct ns_clientmgr { * recursion quota, and an outstanding write request. */ +#define NS_CLIENTSTATE_RECURSING 5 +/*%< + * The client object is recursing. It will be on the 'recursing' + * list. + */ + #define NS_CLIENTSTATE_MAX 9 /*%< * Sentinel value used to indicate "no state". When client->newstate @@ -210,20 +228,21 @@ static void client_udprecv(ns_client_t *client); static void clientmgr_destroy(ns_clientmgr_t *manager); static isc_boolean_t exit_check(ns_client_t *client); static void ns_client_endrequest(ns_client_t *client); -static void ns_client_checkactive(ns_client_t *client); static void client_start(isc_task_t *task, isc_event_t *event); static void client_request(isc_task_t *task, isc_event_t *event); static void ns_client_dumpmessage(ns_client_t *client, const char *reason); +static isc_result_t get_client(ns_clientmgr_t *manager, ns_interface_t *ifp, + dns_dispatch_t *disp, isc_boolean_t tcp); void ns_client_recursing(ns_client_t *client) { REQUIRE(NS_CLIENT_VALID(client)); + REQUIRE(client->state == NS_CLIENTSTATE_WORKING); - LOCK(&client->manager->lock); - ISC_LIST_UNLINK(*client->list, client, link); - ISC_LIST_APPEND(client->manager->recursing, client, link); - client->list = &client->manager->recursing; - UNLOCK(&client->manager->lock); + LOCK(&client->manager->reclock); + client->newstate = client->state = NS_CLIENTSTATE_RECURSING; + ISC_LIST_APPEND(client->manager->recursing, client, rlink); + UNLOCK(&client->manager->reclock); } void @@ -231,15 +250,14 @@ ns_client_killoldestquery(ns_client_t *client) { ns_client_t *oldest; REQUIRE(NS_CLIENT_VALID(client)); - LOCK(&client->manager->lock); + LOCK(&client->manager->reclock); oldest = ISC_LIST_HEAD(client->manager->recursing); if (oldest != NULL) { + ISC_LIST_UNLINK(client->manager->recursing, oldest, rlink); + UNLOCK(&client->manager->reclock); ns_query_cancel(oldest); - ISC_LIST_UNLINK(*oldest->list, oldest, link); - ISC_LIST_APPEND(client->manager->active, oldest, link); - oldest->list = &client->manager->active; - } - UNLOCK(&client->manager->lock); + } else + UNLOCK(&client->manager->reclock); } void @@ -268,15 +286,16 @@ ns_client_settimeout(ns_client_t *client, unsigned int seconds) { */ static isc_boolean_t exit_check(ns_client_t *client) { - ns_clientmgr_t *locked_manager = NULL; - ns_clientmgr_t *destroy_manager = NULL; + isc_boolean_t destroy_manager = ISC_FALSE; + ns_clientmgr_t *manager = NULL; REQUIRE(NS_CLIENT_VALID(client)); + manager = client->manager; if (client->state <= client->newstate) return (ISC_FALSE); /* Business as usual. */ - INSIST(client->newstate < NS_CLIENTSTATE_WORKING); + INSIST(client->newstate < NS_CLIENTSTATE_RECURSING); /* * We need to detach from the view early when shutting down @@ -293,13 +312,16 @@ exit_check(ns_client_t *client) { client->newstate == NS_CLIENTSTATE_FREED && client->view != NULL) dns_view_detach(&client->view); - if (client->state == NS_CLIENTSTATE_WORKING) { + if (client->state == NS_CLIENTSTATE_WORKING || + client->state == NS_CLIENTSTATE_RECURSING) + { INSIST(client->newstate <= NS_CLIENTSTATE_READING); /* * Let the update processing complete. */ if (client->nupdates > 0) return (ISC_TRUE); + /* * We are trying to abort request processing. */ @@ -322,23 +344,28 @@ exit_check(ns_client_t *client) { */ return (ISC_TRUE); } + /* * I/O cancel is complete. Burn down all state * related to the current request. Ensure that - * the client is on the active list and not the - * recursing list. + * the client is no longer on the recursing list. + * + * We need to check whether the client is still linked, + * because it may already have been removed from the + * recursing list by ns_client_killoldestquery() */ - LOCK(&client->manager->lock); - if (client->list == &client->manager->recursing) { - ISC_LIST_UNLINK(*client->list, client, link); - ISC_LIST_APPEND(client->manager->active, client, link); - client->list = &client->manager->active; + if (client->state == NS_CLIENTSTATE_RECURSING) { + LOCK(&manager->reclock); + if (ISC_LINK_LINKED(client, rlink)) + ISC_LIST_UNLINK(manager->recursing, + client, rlink); + UNLOCK(&manager->reclock); } - UNLOCK(&client->manager->lock); ns_client_endrequest(client); client->state = NS_CLIENTSTATE_READING; INSIST(client->recursionquota == NULL); + if (NS_CLIENTSTATE_READING == client->newstate) { client_read(client); client->newstate = NS_CLIENTSTATE_MAX; @@ -389,8 +416,27 @@ exit_check(ns_client_t *client) { * or UDP request, but we may have enough clients doing * that already. Check whether this client needs to remain * active and force it to go inactive if not. + * + * UDP clients go inactive at this point, but TCP clients + * may remain active if we have fewer active TCP client + * objects than desired due to an earlier quota exhaustion. */ - ns_client_checkactive(client); + if (client->mortal && TCP_CLIENT(client) && !ns_g_clienttest) { + LOCK(&client->interface->lock); + if (client->interface->ntcpcurrent < + client->interface->ntcptarget) + client->mortal = ISC_FALSE; + UNLOCK(&client->interface->lock); + } + + /* + * We don't need the client; send it to the inactive + * queue for recycling. + */ + if (client->mortal) { + if (client->newstate > NS_CLIENTSTATE_INACTIVE) + client->newstate = NS_CLIENTSTATE_INACTIVE; + } if (NS_CLIENTSTATE_READY == client->newstate) { if (TCP_CLIENT(client)) { @@ -404,6 +450,7 @@ exit_check(ns_client_t *client) { if (client->state == NS_CLIENTSTATE_READY) { INSIST(client->newstate <= NS_CLIENTSTATE_INACTIVE); + /* * We are trying to enter the inactive state. */ @@ -411,25 +458,22 @@ exit_check(ns_client_t *client) { isc_socket_cancel(client->tcplistener, client->task, ISC_SOCKCANCEL_ACCEPT); - if (! (client->naccepts == 0)) { - /* Still waiting for accept cancel completion. */ + /* Still waiting for accept cancel completion. */ + if (! (client->naccepts == 0)) return (ISC_TRUE); - } - /* Accept cancel is complete. */ + /* Accept cancel is complete. */ if (client->nrecvs > 0) isc_socket_cancel(client->udpsocket, client->task, ISC_SOCKCANCEL_RECV); - if (! (client->nrecvs == 0)) { - /* Still waiting for recv cancel completion. */ - return (ISC_TRUE); - } - /* Recv cancel is complete. */ - if (client->nctls > 0) { - /* Still waiting for control event to be delivered */ + /* Still waiting for recv cancel completion. */ + if (! (client->nrecvs == 0)) + return (ISC_TRUE); + + /* Still waiting for control event to be delivered */ + if (client->nctls > 0) return (ISC_TRUE); - } /* Deactivate the client. */ if (client->interface) @@ -449,7 +493,6 @@ exit_check(ns_client_t *client) { client->attributes = 0; client->mortal = ISC_FALSE; - LOCK(&client->manager->lock); /* * Put the client on the inactive list. If we are aiming for * the "freed" state, it will be removed from the inactive @@ -457,18 +500,18 @@ exit_check(ns_client_t *client) { * that has been done, lest the manager decide to reactivate * the dying client inbetween. */ - locked_manager = client->manager; - ISC_LIST_UNLINK(*client->list, client, link); - ISC_LIST_APPEND(client->manager->inactive, client, link); - client->list = &client->manager->inactive; client->state = NS_CLIENTSTATE_INACTIVE; INSIST(client->recursionquota == NULL); if (client->state == client->newstate) { client->newstate = NS_CLIENTSTATE_MAX; + if (!ns_g_clienttest && manager != NULL && + !manager->exiting) + ISC_QUEUE_PUSH(manager->inactive, client, + ilink); if (client->needshutdown) isc_task_shutdown(client->task); - goto unlock; + return (ISC_TRUE); } } @@ -485,6 +528,7 @@ exit_check(ns_client_t *client) { REQUIRE(client->state == NS_CLIENTSTATE_INACTIVE); INSIST(client->recursionquota == NULL); + INSIST(!ISC_QLINK_LINKED(client, ilink)); ns_query_free(client); isc_mem_put(client->mctx, client->recvbuf, RECV_BUFFER_SIZE); @@ -493,27 +537,27 @@ exit_check(ns_client_t *client) { isc_timer_detach(&client->timer); if (client->tcpbuf != NULL) - isc_mem_put(client->mctx, client->tcpbuf, TCP_BUFFER_SIZE); + isc_mem_put(client->mctx, client->tcpbuf, + TCP_BUFFER_SIZE); if (client->opt != NULL) { INSIST(dns_rdataset_isassociated(client->opt)); dns_rdataset_disassociate(client->opt); - dns_message_puttemprdataset(client->message, &client->opt); + dns_message_puttemprdataset(client->message, + &client->opt); } + dns_message_destroy(&client->message); - if (client->manager != NULL) { - ns_clientmgr_t *manager = client->manager; - if (locked_manager == NULL) { - LOCK(&manager->lock); - locked_manager = manager; - } - ISC_LIST_UNLINK(*client->list, client, link); - client->list = NULL; + if (manager != NULL) { + LOCK(&manager->listlock); + ISC_LIST_UNLINK(manager->clients, client, link); + LOCK(&manager->lock); if (manager->exiting && - ISC_LIST_EMPTY(manager->active) && - ISC_LIST_EMPTY(manager->inactive) && - ISC_LIST_EMPTY(manager->recursing)) - destroy_manager = manager; + ISC_LIST_EMPTY(manager->clients)) + destroy_manager = ISC_TRUE; + UNLOCK(&manager->lock); + UNLOCK(&manager->listlock); } + /* * Detaching the task must be done after unlinking from * the manager's lists because the manager accesses @@ -524,6 +568,7 @@ exit_check(ns_client_t *client) { CTRACE("free"); client->magic = 0; + /* * Check that there are no other external references to * the memory context. @@ -533,22 +578,10 @@ exit_check(ns_client_t *client) { INSIST(0); } isc_mem_putanddetach(&client->mctx, client, sizeof(*client)); - - goto unlock; } - unlock: - if (locked_manager != NULL) { - UNLOCK(&locked_manager->lock); - locked_manager = NULL; - } - - /* - * Only now is it safe to destroy the client manager (if needed), - * because we have accessed its lock for the last time. - */ - if (destroy_manager != NULL) - clientmgr_destroy(destroy_manager); + if (destroy_manager && manager != NULL) + clientmgr_destroy(manager); return (ISC_TRUE); } @@ -604,6 +637,9 @@ client_shutdown(isc_task_t *task, isc_event_t *event) { client->shutdown_arg = NULL; } + if (ISC_QLINK_LINKED(client, ilink)) + ISC_QUEUE_UNLINK(client->manager->inactive, client, ilink); + client->newstate = NS_CLIENTSTATE_FREED; client->needshutdown = ISC_FALSE; (void)exit_check(client); @@ -616,7 +652,8 @@ ns_client_endrequest(ns_client_t *client) { INSIST(client->nsends == 0); INSIST(client->nrecvs == 0); INSIST(client->nupdates == 0); - INSIST(client->state == NS_CLIENTSTATE_WORKING); + INSIST(client->state == NS_CLIENTSTATE_WORKING || + client->state == NS_CLIENTSTATE_RECURSING); CTRACE("endrequest"); @@ -649,46 +686,13 @@ ns_client_endrequest(ns_client_t *client) { client->attributes &= NS_CLIENTATTR_TCP; } -static void -ns_client_checkactive(ns_client_t *client) { - if (client->mortal) { - /* - * This client object should normally go inactive - * at this point, but if we have fewer active client - * objects than desired due to earlier quota exhaustion, - * keep it active to make up for the shortage. - */ - isc_boolean_t need_another_client = ISC_FALSE; - if (TCP_CLIENT(client) && !ns_g_clienttest) { - LOCK(&client->interface->lock); - if (client->interface->ntcpcurrent < - client->interface->ntcptarget) - need_another_client = ISC_TRUE; - UNLOCK(&client->interface->lock); - } else { - /* - * The UDP client quota is enforced by making - * requests fail rather than by not listening - * for new ones. Therefore, there is always a - * full set of UDP clients listening. - */ - } - if (! need_another_client) { - /* - * We don't need this client object. Recycle it. - */ - if (client->newstate >= NS_CLIENTSTATE_INACTIVE) - client->newstate = NS_CLIENTSTATE_INACTIVE; - } - } -} - void ns_client_next(ns_client_t *client, isc_result_t result) { int newstate; REQUIRE(NS_CLIENT_VALID(client)); REQUIRE(client->state == NS_CLIENTSTATE_WORKING || + client->state == NS_CLIENTSTATE_RECURSING || client->state == NS_CLIENTSTATE_READING); CTRACE("next"); @@ -745,9 +749,6 @@ client_senddone(isc_task_t *task, isc_event_t *event) { client->tcpbuf = NULL; } - if (exit_check(client)) - return; - ns_client_next(client, ISC_R_SUCCESS); } @@ -1974,6 +1975,11 @@ static isc_result_t get_clientmctx(ns_clientmgr_t *manager, isc_mem_t **mctxp) { isc_mem_t *clientmctx; isc_result_t result; +#if NMCTXS > 0 + unsigned int nextmctx; +#endif + + MTRACE("clientmctx"); /* * Caller must be holding the manager lock. @@ -1985,19 +1991,21 @@ get_clientmctx(ns_clientmgr_t *manager, isc_mem_t **mctxp) { return (result); } #if NMCTXS > 0 - INSIST(manager->nextmctx < NMCTXS); - clientmctx = manager->mctxpool[manager->nextmctx]; + nextmctx = manager->nextmctx++; + if (manager->nextmctx == NMCTXS) + manager->nextmctx = 0; + + INSIST(nextmctx < NMCTXS); + + clientmctx = manager->mctxpool[nextmctx]; if (clientmctx == NULL) { result = isc_mem_create(0, 0, &clientmctx); if (result != ISC_R_SUCCESS) return (result); isc_mem_setname(clientmctx, "client", NULL); - manager->mctxpool[manager->nextmctx] = clientmctx; + manager->mctxpool[nextmctx] = clientmctx; } - manager->nextmctx++; - if (manager->nextmctx == NMCTXS) - manager->nextmctx = 0; #else clientmctx = manager->mctx; #endif @@ -2118,6 +2126,8 @@ client_create(ns_clientmgr_t *manager, ns_client_t **clientp) { #ifdef ALLOW_FILTER_AAAA_ON_V4 client->filter_aaaa = dns_v4_aaaa_ok; #endif + client->needshutdown = ns_g_clienttest; + ISC_EVENT_INIT(&client->ctlevent, sizeof(client->ctlevent), 0, NULL, NS_EVENT_CLIENTCONTROL, client_start, client, client, NULL, NULL); @@ -2129,7 +2139,8 @@ client_create(ns_clientmgr_t *manager, ns_client_t **clientp) { client->formerrcache.time = 0; client->formerrcache.id = 0; ISC_LINK_INIT(client, link); - client->list = NULL; + ISC_LINK_INIT(client, rlink); + ISC_QLINK_INIT(client, ilink); /* * We call the init routines for the various kinds of client here, @@ -2144,8 +2155,6 @@ client_create(ns_clientmgr_t *manager, ns_client_t **clientp) { if (result != ISC_R_SUCCESS) goto cleanup_query; - client->needshutdown = ns_g_clienttest; - CTRACE("create"); *clientp = client; @@ -2410,10 +2419,8 @@ ns_client_replace(ns_client_t *client) { REQUIRE(client != NULL); REQUIRE(client->manager != NULL); - result = ns_clientmgr_createclients(client->manager, - 1, client->interface, - (TCP_CLIENT(client) ? - ISC_TRUE : ISC_FALSE)); + result = get_client(client->manager, client->interface, + client->dispatch, TCP_CLIENT(client)); if (result != ISC_R_SUCCESS) return (result); @@ -2437,9 +2444,7 @@ clientmgr_destroy(ns_clientmgr_t *manager) { int i; #endif - REQUIRE(ISC_LIST_EMPTY(manager->active)); - REQUIRE(ISC_LIST_EMPTY(manager->inactive)); - REQUIRE(ISC_LIST_EMPTY(manager->recursing)); + REQUIRE(ISC_LIST_EMPTY(manager->clients)); MTRACE("clientmgr_destroy"); @@ -2450,7 +2455,10 @@ clientmgr_destroy(ns_clientmgr_t *manager) { } #endif + ISC_QUEUE_DESTROY(manager->inactive); DESTROYLOCK(&manager->lock); + DESTROYLOCK(&manager->listlock); + DESTROYLOCK(&manager->reclock); manager->magic = 0; isc_mem_put(manager->mctx, manager, sizeof(*manager)); } @@ -2473,13 +2481,21 @@ ns_clientmgr_create(isc_mem_t *mctx, isc_taskmgr_t *taskmgr, if (result != ISC_R_SUCCESS) goto cleanup_manager; + result = isc_mutex_init(&manager->listlock); + if (result != ISC_R_SUCCESS) + goto cleanup_lock; + + result = isc_mutex_init(&manager->reclock); + if (result != ISC_R_SUCCESS) + goto cleanup_listlock; + manager->mctx = mctx; manager->taskmgr = taskmgr; manager->timermgr = timermgr; manager->exiting = ISC_FALSE; - ISC_LIST_INIT(manager->active); - ISC_LIST_INIT(manager->inactive); + ISC_LIST_INIT(manager->clients); ISC_LIST_INIT(manager->recursing); + ISC_QUEUE_INIT(manager->inactive, ilink); #if NMCTXS > 0 manager->nextmctx = 0; for (i = 0; i < NMCTXS; i++) @@ -2493,6 +2509,12 @@ ns_clientmgr_create(isc_mem_t *mctx, isc_taskmgr_t *taskmgr, return (ISC_R_SUCCESS); + cleanup_listlock: + (void) isc_mutex_destroy(&manager->listlock); + + cleanup_lock: + (void) isc_mutex_destroy(&manager->lock); + cleanup_manager: isc_mem_put(manager->mctx, manager, sizeof(*manager)); @@ -2501,9 +2523,10 @@ ns_clientmgr_create(isc_mem_t *mctx, isc_taskmgr_t *taskmgr, void ns_clientmgr_destroy(ns_clientmgr_t **managerp) { + isc_result_t result; ns_clientmgr_t *manager; ns_client_t *client; - isc_boolean_t need_destroy = ISC_FALSE; + isc_boolean_t need_destroy = ISC_FALSE, unlock = ISC_FALSE; REQUIRE(managerp != NULL); manager = *managerp; @@ -2511,31 +2534,27 @@ ns_clientmgr_destroy(ns_clientmgr_t **managerp) { MTRACE("destroy"); - LOCK(&manager->lock); + /* + * Check for success because we may already be task-exclusive + * at this point. Only if we succeed at obtaining an exclusive + * lock now will we need to relinquish it later. + */ + result = isc_task_beginexclusive(ns_g_server->task); + if (result == ISC_R_SUCCESS) + unlock = ISC_TRUE; manager->exiting = ISC_TRUE; - for (client = ISC_LIST_HEAD(manager->recursing); + for (client = ISC_LIST_HEAD(manager->clients); client != NULL; client = ISC_LIST_NEXT(client, link)) isc_task_shutdown(client->task); - for (client = ISC_LIST_HEAD(manager->active); - client != NULL; - client = ISC_LIST_NEXT(client, link)) - isc_task_shutdown(client->task); - - for (client = ISC_LIST_HEAD(manager->inactive); - client != NULL; - client = ISC_LIST_NEXT(client, link)) - isc_task_shutdown(client->task); - - if (ISC_LIST_EMPTY(manager->active) && - ISC_LIST_EMPTY(manager->inactive) && - ISC_LIST_EMPTY(manager->recursing)) + if (ISC_LIST_EMPTY(manager->clients)) need_destroy = ISC_TRUE; - UNLOCK(&manager->lock); + if (unlock) + isc_task_endexclusive(ns_g_server->task); if (need_destroy) clientmgr_destroy(manager); @@ -2543,81 +2562,86 @@ ns_clientmgr_destroy(ns_clientmgr_t **managerp) { *managerp = NULL; } +static isc_result_t +get_client(ns_clientmgr_t *manager, ns_interface_t *ifp, + dns_dispatch_t *disp, isc_boolean_t tcp) +{ + isc_result_t result = ISC_R_SUCCESS; + isc_event_t *ev; + ns_client_t *client; + MTRACE("get client"); + + REQUIRE(manager != NULL); + + if (manager->exiting) + return (ISC_R_SHUTTINGDOWN); + + /* + * Allocate a client. First try to get a recycled one; + * if that fails, make a new one. + */ + client = NULL; + if (!ns_g_clienttest) + ISC_QUEUE_POP(manager->inactive, ilink, client); + + if (client != NULL) + MTRACE("recycle"); + else { + MTRACE("create new"); + + LOCK(&manager->lock); + result = client_create(manager, &client); + UNLOCK(&manager->lock); + if (result != ISC_R_SUCCESS) + return (result); + + LOCK(&manager->listlock); + ISC_LIST_APPEND(manager->clients, client, link); + UNLOCK(&manager->listlock); + } + + client->manager = manager; + ns_interface_attach(ifp, &client->interface); + client->state = NS_CLIENTSTATE_READY; + INSIST(client->recursionquota == NULL); + + if (tcp) { + client->attributes |= NS_CLIENTATTR_TCP; + isc_socket_attach(ifp->tcpsocket, + &client->tcplistener); + } else { + isc_socket_t *sock; + + dns_dispatch_attach(disp, &client->dispatch); + sock = dns_dispatch_getsocket(client->dispatch); + isc_socket_attach(sock, &client->udpsocket); + } + + INSIST(client->nctls == 0); + client->nctls++; + ev = &client->ctlevent; + isc_task_send(client->task, &ev); + + return (ISC_R_SUCCESS); +} + isc_result_t ns_clientmgr_createclients(ns_clientmgr_t *manager, unsigned int n, ns_interface_t *ifp, isc_boolean_t tcp) { isc_result_t result = ISC_R_SUCCESS; - unsigned int i; - ns_client_t *client; + unsigned int disp; REQUIRE(VALID_MANAGER(manager)); REQUIRE(n > 0); MTRACE("createclients"); - /* - * We MUST lock the manager lock for the entire client creation - * process. If we didn't do this, then a client could get a - * shutdown event and disappear out from under us. - */ - - LOCK(&manager->lock); - - for (i = 0; i < n; i++) { - isc_event_t *ev; - /* - * Allocate a client. First try to get a recycled one; - * if that fails, make a new one. - */ - client = NULL; - if (!ns_g_clienttest) - client = ISC_LIST_HEAD(manager->inactive); - if (client != NULL) { - MTRACE("recycle"); - ISC_LIST_UNLINK(manager->inactive, client, link); - client->list = NULL; - } else { - MTRACE("create new"); - result = client_create(manager, &client); - if (result != ISC_R_SUCCESS) - break; - } - - ns_interface_attach(ifp, &client->interface); - client->state = NS_CLIENTSTATE_READY; - INSIST(client->recursionquota == NULL); - - if (tcp) { - client->attributes |= NS_CLIENTATTR_TCP; - isc_socket_attach(ifp->tcpsocket, - &client->tcplistener); - } else { - isc_socket_t *sock; - - dns_dispatch_attach(ifp->udpdispatch, - &client->dispatch); - sock = dns_dispatch_getsocket(client->dispatch); - isc_socket_attach(sock, &client->udpsocket); - } - client->manager = manager; - ISC_LIST_APPEND(manager->active, client, link); - client->list = &manager->active; - - INSIST(client->nctls == 0); - client->nctls++; - ev = &client->ctlevent; - isc_task_send(client->task, &ev); + for (disp = 0; disp < n; disp++) { + result = get_client(manager, ifp, ifp->udpdispatch[disp], tcp); + if (result != ISC_R_SUCCESS) + break; } - if (i != 0) { - /* - * We managed to create at least one client, so we - * declare victory. - */ - result = ISC_R_SUCCESS; - } - - UNLOCK(&manager->lock); return (result); } @@ -2702,19 +2726,41 @@ ns_client_logv(ns_client_t *client, isc_logcategory_t *category, { char msgbuf[2048]; char peerbuf[ISC_SOCKADDR_FORMATSIZE]; - const char *name = ""; - const char *sep = ""; + char signerbuf[DNS_NAME_FORMATSIZE], qnamebuf[DNS_NAME_FORMATSIZE]; + const char *viewname = ""; + const char *sep1 = "", *sep2 = "", *sep3 = "", *sep4 = ""; + const char *signer = "", *qname = ""; + dns_name_t *q = NULL; vsnprintf(msgbuf, sizeof(msgbuf), fmt, ap); + ns_client_name(client, peerbuf, sizeof(peerbuf)); + + if (client->signer != NULL) { + dns_name_format(client->signer, signerbuf, sizeof(signerbuf)); + sep1 = "/key "; + signer = signerbuf; + } + + q = client->query.origqname != NULL + ? client->query.origqname : client->query.qname; + if (q != NULL) { + dns_name_format(q, qnamebuf, sizeof(qnamebuf)); + sep2 = " ("; + sep3 = ")"; + qname = qnamebuf; + } + if (client->view != NULL && strcmp(client->view->name, "_bind") != 0 && strcmp(client->view->name, "_default") != 0) { - name = client->view->name; - sep = ": view "; + sep4 = ": view "; + viewname = client->view->name; } isc_log_write(ns_g_lctx, category, module, level, - "client %s%s%s: %s", peerbuf, sep, name, msgbuf); + "client %s%s%s%s%s%s%s%s: %s", + peerbuf, sep1, signer, sep2, qname, sep3, + sep4, viewname, msgbuf); } void @@ -2796,9 +2842,11 @@ ns_client_dumprecursing(FILE *f, ns_clientmgr_t *manager) { REQUIRE(VALID_MANAGER(manager)); - LOCK(&manager->lock); + LOCK(&manager->reclock); client = ISC_LIST_HEAD(manager->recursing); while (client != NULL) { + INSIST(client->state == NS_CLIENTSTATE_RECURSING); + ns_client_name(client, peerbuf, sizeof(peerbuf)); if (client->view != NULL && strcmp(client->view->name, "_bind") != 0 && @@ -2809,6 +2857,9 @@ ns_client_dumprecursing(FILE *f, ns_clientmgr_t *manager) { name = ""; sep = ""; } + + LOCK(&client->query.fetchlock); + INSIST(client->query.qname != NULL); dns_name_format(client->query.qname, namebuf, sizeof(namebuf)); if (client->query.qname != client->query.origqname && client->query.origqname != NULL) { @@ -2831,20 +2882,19 @@ ns_client_dumprecursing(FILE *f, ns_clientmgr_t *manager) { strcpy(typebuf, "-"); strcpy(classbuf, "-"); } + UNLOCK(&client->query.fetchlock); fprintf(f, "; client %s%s%s: id %u '%s/%s/%s'%s%s " "requesttime %d\n", peerbuf, sep, name, client->message->id, namebuf, typebuf, classbuf, origfor, original, client->requesttime); - client = ISC_LIST_NEXT(client, link); + client = ISC_LIST_NEXT(client, rlink); } - UNLOCK(&manager->lock); + UNLOCK(&manager->reclock); } void ns_client_qnamereplace(ns_client_t *client, dns_name_t *name) { - - if (client->manager != NULL) - LOCK(&client->manager->lock); + LOCK(&client->query.fetchlock); if (client->query.restarts > 0) { /* * client->query.qname was dynamically allocated. @@ -2853,6 +2903,16 @@ ns_client_qnamereplace(ns_client_t *client, dns_name_t *name) { &client->query.qname); } client->query.qname = name; - if (client->manager != NULL) - UNLOCK(&client->manager->lock); + UNLOCK(&client->query.fetchlock); +} + +isc_result_t +ns_client_sourceip(dns_clientinfo_t *ci, isc_sockaddr_t **addrp) { + ns_client_t *client = (ns_client_t *) ci->data; + + REQUIRE(NS_CLIENT_VALID(client)); + REQUIRE(addrp != NULL); + + *addrp = &client->peeraddr; + return (ISC_R_SUCCESS); } diff --git a/contrib/bind9/bin/named/config.c b/contrib/bind9/bin/named/config.c index 25ebac4db0c..fa349eeade2 100644 --- a/contrib/bind9/bin/named/config.c +++ b/contrib/bind9/bin/named/config.c @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: config.c,v 1.113.16.2 2011/02/28 01:19:58 tbox Exp $ */ +/* $Id: config.c,v 1.123 2012/01/06 23:46:41 tbox Exp $ */ /*! \file */ @@ -73,6 +73,7 @@ options {\n\ listen-on {any;};\n\ listen-on-v6 {none;};\n\ match-mapped-addresses no;\n\ + max-rsa-exponent-size 0; /* no limit */\n\ memstatistics-file \"named.memstats\";\n\ multiple-cnames no;\n\ # named-xfer ;\n\ @@ -90,7 +91,7 @@ options {\n\ "\ recursive-clients 1000;\n\ resolver-query-timeout 10;\n\ - rrset-order {type NS order random; order cyclic; };\n\ + rrset-order { order random; };\n\ serial-queries 20;\n\ serial-query-rate 20;\n\ server-id none;\n\ @@ -200,7 +201,8 @@ options {\n\ sig-signing-nodes 100;\n\ sig-signing-signatures 10;\n\ sig-signing-type 65534;\n\ - zone-statistics false;\n\ + inline-signing no;\n\ + zone-statistics terse;\n\ max-journal-size unlimited;\n\ ixfr-from-differences false;\n\ check-wildcard yes;\n\ @@ -210,7 +212,10 @@ options {\n\ check-srv-cname warn;\n\ zero-no-soa-ttl yes;\n\ update-check-ksk yes;\n\ + serial-update-method increment;\n\ + dnssec-update-mode maintain;\n\ dnssec-dnskey-kskonly no;\n\ + dnssec-loadkeys-interval 60;\n\ try-tcp-refresh yes; /* BIND 8 compat */\n\ };\n\ " @@ -292,7 +297,8 @@ ns_checknames_get(const cfg_obj_t **maps, const char *which, if (maps[i] == NULL) return (ISC_R_NOTFOUND); checknames = NULL; - if (cfg_map_get(maps[i], "check-names", &checknames) == ISC_R_SUCCESS) { + if (cfg_map_get(maps[i], "check-names", + &checknames) == ISC_R_SUCCESS) { /* * Zone map entry is not a list. */ @@ -305,7 +311,8 @@ ns_checknames_get(const cfg_obj_t **maps, const char *which, element = cfg_list_next(element)) { value = cfg_listelt_value(element); type = cfg_tuple_get(value, "type"); - if (strcasecmp(cfg_obj_asstring(type), which) == 0) { + if (strcasecmp(cfg_obj_asstring(type), + which) == 0) { *obj = cfg_tuple_get(value, "mode"); return (ISC_R_SUCCESS); } @@ -378,6 +385,8 @@ ns_config_getzonetype(const cfg_obj_t *zonetypeobj) { ztype = dns_zone_stub; else if (strcasecmp(str, "static-stub") == 0) ztype = dns_zone_staticstub; + else if (strcasecmp(str, "redirect") == 0) + ztype = dns_zone_redirect; else INSIST(0); return (ztype); diff --git a/contrib/bind9/bin/named/control.c b/contrib/bind9/bin/named/control.c index 2a1a5a8e734..fabe442aabc 100644 --- a/contrib/bind9/bin/named/control.c +++ b/contrib/bind9/bin/named/control.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004-2007, 2009, 2010, 2012 Internet Systems Consortium, Inc. ("ISC") + * Copyright (C) 2004-2007, 2009-2012 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 2001-2003 Internet Software Consortium. * * Permission to use, copy, modify, and/or distribute this software for any @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: control.c,v 1.41 2010/12/03 22:05:19 each Exp $ */ +/* $Id$ */ /*! \file */ @@ -154,7 +154,7 @@ ns_control_docommand(isccc_sexpr_t *message, isc_buffer_t *text) { } else if (command_compare(command, NS_COMMAND_DUMPSTATS)) { result = ns_server_dumpstats(ns_g_server); } else if (command_compare(command, NS_COMMAND_QUERYLOG)) { - result = ns_server_togglequerylog(ns_g_server); + result = ns_server_togglequerylog(ns_g_server, command); } else if (command_compare(command, NS_COMMAND_DUMPDB)) { ns_server_dumpdb(ns_g_server, command); result = ISC_R_SUCCESS; @@ -169,7 +169,9 @@ ns_control_docommand(isccc_sexpr_t *message, isc_buffer_t *text) { } else if (command_compare(command, NS_COMMAND_FLUSH)) { result = ns_server_flushcache(ns_g_server, command); } else if (command_compare(command, NS_COMMAND_FLUSHNAME)) { - result = ns_server_flushname(ns_g_server, command); + result = ns_server_flushnode(ns_g_server, command, ISC_FALSE); + } else if (command_compare(command, NS_COMMAND_FLUSHTREE)) { + result = ns_server_flushnode(ns_g_server, command, ISC_TRUE); } else if (command_compare(command, NS_COMMAND_STATUS)) { result = ns_server_status(ns_g_server, text); } else if (command_compare(command, NS_COMMAND_TSIGLIST)) { @@ -183,6 +185,8 @@ ns_control_docommand(isccc_sexpr_t *message, isc_buffer_t *text) { command_compare(command, NS_COMMAND_THAW)) { result = ns_server_freeze(ns_g_server, ISC_FALSE, command, text); + } else if (command_compare(command, NS_COMMAND_SYNC)) { + result = ns_server_sync(ns_g_server, command, text); } else if (command_compare(command, NS_COMMAND_RECURSING)) { result = ns_server_dumprecursing(ns_g_server); } else if (command_compare(command, NS_COMMAND_TIMERPOKE)) { @@ -201,6 +205,8 @@ ns_control_docommand(isccc_sexpr_t *message, isc_buffer_t *text) { result = ns_server_add_zone(ns_g_server, command); } else if (command_compare(command, NS_COMMAND_DELZONE)) { result = ns_server_del_zone(ns_g_server, command); + } else if (command_compare(command, NS_COMMAND_SIGNING)) { + result = ns_server_signing(ns_g_server, command, text); } else { isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_CONTROL, ISC_LOG_WARNING, diff --git a/contrib/bind9/bin/named/controlconf.c b/contrib/bind9/bin/named/controlconf.c index 73c0f37e973..c46a6e15f46 100644 --- a/contrib/bind9/bin/named/controlconf.c +++ b/contrib/bind9/bin/named/controlconf.c @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: controlconf.c,v 1.60.544.3 2011/12/22 08:10:09 marka Exp $ */ +/* $Id: controlconf.c,v 1.63 2011/12/22 08:07:48 marka Exp $ */ /*! \file */ diff --git a/contrib/bind9/bin/named/include/dlz/dlz_dlopen_driver.h b/contrib/bind9/bin/named/include/dlz/dlz_dlopen_driver.h index 7af325a13b3..602b3c074d1 100644 --- a/contrib/bind9/bin/named/include/dlz/dlz_dlopen_driver.h +++ b/contrib/bind9/bin/named/include/dlz/dlz_dlopen_driver.h @@ -14,7 +14,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: dlz_dlopen_driver.h,v 1.1.4.4 2011/03/17 09:41:06 fdupont Exp $ */ +/* $Id: dlz_dlopen_driver.h,v 1.4 2011/03/17 09:25:53 fdupont Exp $ */ #ifndef DLZ_DLOPEN_DRIVER_H #define DLZ_DLOPEN_DRIVER_H diff --git a/contrib/bind9/bin/named/include/named/client.h b/contrib/bind9/bin/named/include/named/client.h index e6414d2f7a7..98e79df7066 100644 --- a/contrib/bind9/bin/named/include/named/client.h +++ b/contrib/bind9/bin/named/include/named/client.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004-2009, 2012 Internet Systems Consortium, Inc. ("ISC") + * Copyright (C) 2004-2009, 2011, 2012 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 1999-2003 Internet Software Consortium. * * Permission to use, copy, modify, and/or distribute this software for any @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: client.h,v 1.91.278.2 2012/01/31 23:46:39 tbox Exp $ */ +/* $Id$ */ #ifndef NAMED_CLIENT_H #define NAMED_CLIENT_H 1 @@ -66,7 +66,9 @@ #include #include #include +#include +#include #include #include #include @@ -81,8 +83,6 @@ *** Types ***/ -typedef ISC_LIST(ns_client_t) client_list_t; - /*% nameserver client structure */ struct ns_client { unsigned int magic; @@ -155,13 +155,15 @@ struct ns_client { isc_stdtime_t time; dns_messageid_t id; } formerrcache; + ISC_LINK(ns_client_t) link; - /*% - * The list 'link' is part of, or NULL if not on any list. - */ - client_list_t *list; + ISC_LINK(ns_client_t) rlink; + ISC_QLINK(ns_client_t) ilink; }; +typedef ISC_QUEUE(ns_client_t) client_queue_t; +typedef ISC_LIST(ns_client_t) client_list_t; + #define NS_CLIENT_MAGIC ISC_MAGIC('N','S','C','c') #define NS_CLIENT_VALID(c) ISC_MAGIC_VALID(c, NS_CLIENT_MAGIC) @@ -379,4 +381,7 @@ ns_client_isself(dns_view_t *myview, dns_tsigkey_t *mykey, * Isself callback. */ +isc_result_t +ns_client_sourceip(dns_clientinfo_t *ci, isc_sockaddr_t **addrp); + #endif /* NAMED_CLIENT_H */ diff --git a/contrib/bind9/bin/named/include/named/control.h b/contrib/bind9/bin/named/include/named/control.h index 24e59093b4d..d730a83280c 100644 --- a/contrib/bind9/bin/named/include/named/control.h +++ b/contrib/bind9/bin/named/include/named/control.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004-2007, 2009, 2010 Internet Systems Consortium, Inc. ("ISC") + * Copyright (C) 2004-2007, 2009-2012 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 2001-2003 Internet Software Consortium. * * Permission to use, copy, modify, and/or distribute this software for any @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: control.h,v 1.31 2010/08/16 22:21:06 marka Exp $ */ +/* $Id$ */ #ifndef NAMED_CONTROL_H #define NAMED_CONTROL_H 1 @@ -47,6 +47,7 @@ #define NS_COMMAND_NOTRACE "notrace" #define NS_COMMAND_FLUSH "flush" #define NS_COMMAND_FLUSHNAME "flushname" +#define NS_COMMAND_FLUSHTREE "flushtree" #define NS_COMMAND_STATUS "status" #define NS_COMMAND_TSIGLIST "tsig-list" #define NS_COMMAND_TSIGDELETE "tsig-delete" @@ -62,6 +63,8 @@ #define NS_COMMAND_LOADKEYS "loadkeys" #define NS_COMMAND_ADDZONE "addzone" #define NS_COMMAND_DELZONE "delzone" +#define NS_COMMAND_SYNC "sync" +#define NS_COMMAND_SIGNING "signing" isc_result_t ns_controls_create(ns_server_t *server, ns_controls_t **ctrlsp); diff --git a/contrib/bind9/bin/named/include/named/globals.h b/contrib/bind9/bin/named/include/named/globals.h index 39307f36996..cbc14d8b72d 100644 --- a/contrib/bind9/bin/named/include/named/globals.h +++ b/contrib/bind9/bin/named/include/named/globals.h @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: globals.h,v 1.89.54.2 2011/06/17 23:47:10 tbox Exp $ */ +/* $Id: globals.h,v 1.92 2011/11/09 18:44:04 each Exp $ */ #ifndef NAMED_GLOBALS_H #define NAMED_GLOBALS_H 1 @@ -51,6 +51,7 @@ EXTERN isc_mem_t * ns_g_mctx INIT(NULL); EXTERN unsigned int ns_g_cpus INIT(0); +EXTERN unsigned int ns_g_udpdisp INIT(0); EXTERN isc_taskmgr_t * ns_g_taskmgr INIT(NULL); EXTERN dns_dispatchmgr_t * ns_g_dispatchmgr INIT(NULL); EXTERN isc_entropy_t * ns_g_entropy INIT(NULL); diff --git a/contrib/bind9/bin/named/include/named/interfacemgr.h b/contrib/bind9/bin/named/include/named/interfacemgr.h index 2724c393cdc..380dbedd768 100644 --- a/contrib/bind9/bin/named/include/named/interfacemgr.h +++ b/contrib/bind9/bin/named/include/named/interfacemgr.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004, 2005, 2007 Internet Systems Consortium, Inc. ("ISC") + * Copyright (C) 2004, 2005, 2007, 2011 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 1999-2002 Internet Software Consortium. * * Permission to use, copy, modify, and/or distribute this software for any @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: interfacemgr.h,v 1.33 2007/06/19 23:46:59 tbox Exp $ */ +/* $Id: interfacemgr.h,v 1.35 2011/07/28 23:47:58 tbox Exp $ */ #ifndef NAMED_INTERFACEMGR_H #define NAMED_INTERFACEMGR_H 1 @@ -65,7 +65,8 @@ #define NS_INTERFACE_VALID(t) ISC_MAGIC_VALID(t, IFACE_MAGIC) #define NS_INTERFACEFLAG_ANYADDR 0x01U /*%< bound to "any" address */ - +#define MAX_UDP_DISPATCH 128 /*%< Maximum number of UDP dispatchers + to start per interface */ /*% The nameserver interface structure */ struct ns_interface { unsigned int magic; /*%< Magic number. */ @@ -76,11 +77,13 @@ struct ns_interface { isc_sockaddr_t addr; /*%< Address and port. */ unsigned int flags; /*%< Interface characteristics */ char name[32]; /*%< Null terminated. */ - dns_dispatch_t * udpdispatch; /*%< UDP dispatcher. */ + dns_dispatch_t * udpdispatch[MAX_UDP_DISPATCH]; + /*%< UDP dispatchers. */ isc_socket_t * tcpsocket; /*%< TCP socket. */ int ntcptarget; /*%< Desired number of concurrent TCP accepts */ int ntcpcurrent; /*%< Current ditto, locked */ + int nudpdispatch; /*%< Number of UDP dispatches */ ns_clientmgr_t * clientmgr; /*%< Client manager. */ ISC_LINK(ns_interface_t) link; }; diff --git a/contrib/bind9/bin/named/include/named/server.h b/contrib/bind9/bin/named/include/named/server.h index 9982e88e09e..3ba0c64a712 100644 --- a/contrib/bind9/bin/named/include/named/server.h +++ b/contrib/bind9/bin/named/include/named/server.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004-2010, 2013 Internet Systems Consortium, Inc. ("ISC") + * Copyright (C) 2004-2013 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 1999-2003 Internet Software Consortium. * * Permission to use, copy, modify, and/or distribute this software for any @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: server.h,v 1.110 2010/08/16 23:46:52 tbox Exp $ */ +/* $Id$ */ #ifndef NAMED_SERVER_H #define NAMED_SERVER_H 1 @@ -230,9 +230,10 @@ ns_server_retransfercommand(ns_server_t *server, char *args); */ isc_result_t -ns_server_togglequerylog(ns_server_t *server); +ns_server_togglequerylog(ns_server_t *server, char *args); /*%< - * Toggle logging of queries, as in BIND 8. + * Enable/disable logging of queries. (Takes "yes" or "no" argument, + * but can also be used as a toggle for backward comptibility.) */ /*% @@ -266,10 +267,12 @@ isc_result_t ns_server_flushcache(ns_server_t *server, char *args); /*% - * Flush a particular name from the server's cache(s) + * Flush a particular name from the server's cache. If 'tree' is false, + * also flush the name from the ADB and badcache. If 'tree' is true, also + * flush all the names under the specified name. */ isc_result_t -ns_server_flushname(ns_server_t *server, char *args); +ns_server_flushnode(ns_server_t *server, char *args, isc_boolean_t tree); /*% * Report the server's status. @@ -296,6 +299,12 @@ isc_result_t ns_server_freeze(ns_server_t *server, isc_boolean_t freeze, char *args, isc_buffer_t *text); +/*% + * Dump zone updates to disk, optionally removing the journal file + */ +isc_result_t +ns_server_sync(ns_server_t *server, char *args, isc_buffer_t *text); + /*% * Update a zone's DNSKEY set from the key repository. If * the command that triggered the call to this function was "sign", @@ -336,4 +345,9 @@ ns_server_add_zone(ns_server_t *server, char *args); isc_result_t ns_server_del_zone(ns_server_t *server, char *args); +/*% + * Lists the status of the signing records for a given zone. + */ +isc_result_t +ns_server_signing(ns_server_t *server, char *args, isc_buffer_t *text); #endif /* NAMED_SERVER_H */ diff --git a/contrib/bind9/bin/named/include/named/zoneconf.h b/contrib/bind9/bin/named/include/named/zoneconf.h index ebaad684ae7..0e684d2d4c7 100644 --- a/contrib/bind9/bin/named/include/named/zoneconf.h +++ b/contrib/bind9/bin/named/include/named/zoneconf.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004-2007, 2010 Internet Systems Consortium, Inc. ("ISC") + * Copyright (C) 2004-2007, 2010, 2011 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 1999-2002 Internet Software Consortium. * * Permission to use, copy, modify, and/or distribute this software for any @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: zoneconf.h,v 1.28 2010/12/20 23:47:20 tbox Exp $ */ +/* $Id: zoneconf.h,v 1.30 2011/08/30 23:46:51 tbox Exp $ */ #ifndef NS_ZONECONF_H #define NS_ZONECONF_H 1 @@ -33,7 +33,7 @@ ISC_LANG_BEGINDECLS isc_result_t ns_zone_configure(const cfg_obj_t *config, const cfg_obj_t *vconfig, const cfg_obj_t *zconfig, cfg_aclconfctx_t *ac, - dns_zone_t *zone); + dns_zone_t *zone, dns_zone_t *raw); /*%< * Configure or reconfigure a zone according to the named.conf * data in 'cctx' and 'czone'. diff --git a/contrib/bind9/bin/named/interfacemgr.c b/contrib/bind9/bin/named/interfacemgr.c index 15ffe00aa51..84bf21d7714 100644 --- a/contrib/bind9/bin/named/interfacemgr.c +++ b/contrib/bind9/bin/named/interfacemgr.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004-2009, 2011, 2013 Internet Systems Consortium, Inc. ("ISC") + * Copyright (C) 2004-2009, 2011-2013 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 1999-2002 Internet Software Consortium. * * Permission to use, copy, modify, and/or distribute this software for any @@ -15,13 +15,14 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: interfacemgr.c,v 1.95.426.2 2011/03/12 04:59:14 tbox Exp $ */ +/* $Id: interfacemgr.c,v 1.101 2011/11/09 18:44:03 each Exp $ */ /*! \file */ #include #include +#include #include #include #include @@ -185,11 +186,14 @@ ns_interface_create(ns_interfacemgr_t *mgr, isc_sockaddr_t *addr, { ns_interface_t *ifp; isc_result_t result; + int disp; REQUIRE(NS_INTERFACEMGR_VALID(mgr)); + ifp = isc_mem_get(mgr->mctx, sizeof(*ifp)); if (ifp == NULL) return (ISC_R_NOMEMORY); + ifp->mgr = NULL; ifp->generation = mgr->generation; ifp->addr = *addr; @@ -212,9 +216,11 @@ ns_interface_create(ns_interfacemgr_t *mgr, isc_sockaddr_t *addr, goto clientmgr_create_failure; } - ifp->udpdispatch = NULL; + for (disp = 0; disp < MAX_UDP_DISPATCH; disp++) + ifp->udpdispatch[disp] = NULL; ifp->tcpsocket = NULL; + /* * Create a single TCP client object. It will replace itself * with a new one as soon as it gets a connection, so the actual @@ -223,6 +229,7 @@ ns_interface_create(ns_interfacemgr_t *mgr, isc_sockaddr_t *addr, */ ifp->ntcptarget = 1; ifp->ntcpcurrent = 0; + ifp->nudpdispatch = 0; ISC_LINK_INIT(ifp, link); @@ -237,6 +244,7 @@ ns_interface_create(ns_interfacemgr_t *mgr, isc_sockaddr_t *addr, clientmgr_create_failure: DESTROYLOCK(&ifp->lock); + lock_create_failure: ifp->magic = 0; isc_mem_put(mgr->mctx, ifp, sizeof(*ifp)); @@ -249,6 +257,7 @@ ns_interface_listenudp(ns_interface_t *ifp) { isc_result_t result; unsigned int attrs; unsigned int attrmask; + int disp, i; attrs = 0; attrs |= DNS_DISPATCHATTR_UDP; @@ -260,18 +269,28 @@ ns_interface_listenudp(ns_interface_t *ifp) { attrmask = 0; attrmask |= DNS_DISPATCHATTR_UDP | DNS_DISPATCHATTR_TCP; attrmask |= DNS_DISPATCHATTR_IPV4 | DNS_DISPATCHATTR_IPV6; - result = dns_dispatch_getudp(ifp->mgr->dispatchmgr, ns_g_socketmgr, - ns_g_taskmgr, &ifp->addr, - 4096, 1000, 32768, 8219, 8237, - attrs, attrmask, &ifp->udpdispatch); - if (result != ISC_R_SUCCESS) { - isc_log_write(IFMGR_COMMON_LOGARGS, ISC_LOG_ERROR, - "could not listen on UDP socket: %s", - isc_result_totext(result)); - goto udp_dispatch_failure; + + ifp->nudpdispatch = ISC_MIN(ns_g_udpdisp, MAX_UDP_DISPATCH); + for (disp = 0; disp < ifp->nudpdispatch; disp++) { + result = dns_dispatch_getudp_dup(ifp->mgr->dispatchmgr, + ns_g_socketmgr, + ns_g_taskmgr, &ifp->addr, + 4096, 1000, 32768, 8219, 8237, + attrs, attrmask, + &ifp->udpdispatch[disp], + disp == 0 + ? NULL + : ifp->udpdispatch[0]); + if (result != ISC_R_SUCCESS) { + isc_log_write(IFMGR_COMMON_LOGARGS, ISC_LOG_ERROR, + "could not listen on UDP socket: %s", + isc_result_totext(result)); + goto udp_dispatch_failure; + } + } - result = ns_clientmgr_createclients(ifp->clientmgr, ns_g_cpus, + result = ns_clientmgr_createclients(ifp->clientmgr, ifp->nudpdispatch, ifp, ISC_FALSE); if (result != ISC_R_SUCCESS) { UNEXPECTED_ERROR(__FILE__, __LINE__, @@ -279,12 +298,17 @@ ns_interface_listenudp(ns_interface_t *ifp) { isc_result_totext(result)); goto addtodispatch_failure; } + return (ISC_R_SUCCESS); addtodispatch_failure: - dns_dispatch_changeattributes(ifp->udpdispatch, 0, - DNS_DISPATCHATTR_NOLISTEN); - dns_dispatch_detach(&ifp->udpdispatch); + for (i = disp - 1; i <= 0; i--) { + dns_dispatch_changeattributes(ifp->udpdispatch[i], 0, + DNS_DISPATCHATTR_NOLISTEN); + dns_dispatch_detach(&(ifp->udpdispatch[i])); + } + ifp->nudpdispatch = 0; + udp_dispatch_failure: return (result); } @@ -398,15 +422,19 @@ ns_interface_shutdown(ns_interface_t *ifp) { static void ns_interface_destroy(ns_interface_t *ifp) { isc_mem_t *mctx = ifp->mgr->mctx; + int disp; + REQUIRE(NS_INTERFACE_VALID(ifp)); ns_interface_shutdown(ifp); - if (ifp->udpdispatch != NULL) { - dns_dispatch_changeattributes(ifp->udpdispatch, 0, - DNS_DISPATCHATTR_NOLISTEN); - dns_dispatch_detach(&ifp->udpdispatch); - } + for (disp = 0; disp < ifp->nudpdispatch; disp++) + if (ifp->udpdispatch[disp] != NULL) { + dns_dispatch_changeattributes(ifp->udpdispatch[disp], 0, + DNS_DISPATCHATTR_NOLISTEN); + dns_dispatch_detach(&(ifp->udpdispatch[disp])); + } + if (ifp->tcpsocket != NULL) isc_socket_detach(&ifp->tcpsocket); diff --git a/contrib/bind9/bin/named/logconf.c b/contrib/bind9/bin/named/logconf.c index f02b97fcdde..b99a167d12f 100644 --- a/contrib/bind9/bin/named/logconf.c +++ b/contrib/bind9/bin/named/logconf.c @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: logconf.c,v 1.42.816.3 2011/03/05 23:52:06 tbox Exp $ */ +/* $Id: logconf.c,v 1.45 2011/03/05 23:52:29 tbox Exp $ */ /*! \file */ diff --git a/contrib/bind9/bin/named/main.c b/contrib/bind9/bin/named/main.c index f6c929e5b96..a5467249082 100644 --- a/contrib/bind9/bin/named/main.c +++ b/contrib/bind9/bin/named/main.c @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: main.c,v 1.180.14.4 2011/11/05 00:45:52 each Exp $ */ +/* $Id$ */ /*! \file */ @@ -418,7 +418,7 @@ parse_command_line(int argc, char *argv[]) { isc_commandline_errprint = ISC_FALSE; while ((ch = isc_commandline_parse(argc, argv, "46c:C:d:E:fFgi:lm:n:N:p:P:" - "sS:t:T:u:vVx:")) != -1) { + "sS:t:T:U:u:vVx:")) != -1) { switch (ch) { case '4': if (disable4) @@ -531,6 +531,11 @@ parse_command_line(int argc, char *argv[]) { fprintf(stderr, "unknown -T flag '%s\n", isc_commandline_argument); break; + case 'U': + ns_g_udpdisp = parse_int(isc_commandline_argument, + "number of UDP listeners " + "per interface"); + break; case 'u': ns_g_username = isc_commandline_argument; break; @@ -595,6 +600,18 @@ create_managers(void) { #else ns_g_cpus = 1; #endif +#ifdef WIN32 + ns_g_udpdisp = 1; +#else + if (ns_g_udpdisp == 0) + ns_g_udpdisp = ns_g_cpus_detected; + if (ns_g_udpdisp > ns_g_cpus) + ns_g_udpdisp = ns_g_cpus; +#endif + isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, + ISC_LOG_INFO, "using %u UDP listener%s per interface", + ns_g_udpdisp, ns_g_udpdisp == 1 ? "" : "s"); + result = isc_taskmgr_create(ns_g_mctx, ns_g_cpus, 0, &ns_g_taskmgr); if (result != ISC_R_SUCCESS) { UNEXPECTED_ERROR(__FILE__, __LINE__, diff --git a/contrib/bind9/bin/named/named.8 b/contrib/bind9/bin/named/named.8 index 222ff426cab..b27be318599 100644 --- a/contrib/bind9/bin/named/named.8 +++ b/contrib/bind9/bin/named/named.8 @@ -1,4 +1,4 @@ -.\" Copyright (C) 2004-2009 Internet Systems Consortium, Inc. ("ISC") +.\" Copyright (C) 2004-2009, 2011, 2013 Internet Systems Consortium, Inc. ("ISC") .\" Copyright (C) 2000, 2001, 2003 Internet Software Consortium. .\" .\" Permission to use, copy, modify, and/or distribute this software for any @@ -33,7 +33,7 @@ named \- Internet domain name server .SH "SYNOPSIS" .HP 6 -\fBnamed\fR [\fB\-4\fR] [\fB\-6\fR] [\fB\-c\ \fR\fB\fIconfig\-file\fR\fR] [\fB\-d\ \fR\fB\fIdebug\-level\fR\fR] [\fB\-E\ \fR\fB\fIengine\-name\fR\fR] [\fB\-f\fR] [\fB\-g\fR] [\fB\-m\ \fR\fB\fIflag\fR\fR] [\fB\-n\ \fR\fB\fI#cpus\fR\fR] [\fB\-p\ \fR\fB\fIport\fR\fR] [\fB\-s\fR] [\fB\-S\ \fR\fB\fI#max\-socks\fR\fR] [\fB\-t\ \fR\fB\fIdirectory\fR\fR] [\fB\-u\ \fR\fB\fIuser\fR\fR] [\fB\-v\fR] [\fB\-V\fR] [\fB\-x\ \fR\fB\fIcache\-file\fR\fR] +\fBnamed\fR [\fB\-4\fR] [\fB\-6\fR] [\fB\-c\ \fR\fB\fIconfig\-file\fR\fR] [\fB\-d\ \fR\fB\fIdebug\-level\fR\fR] [\fB\-E\ \fR\fB\fIengine\-name\fR\fR] [\fB\-f\fR] [\fB\-g\fR] [\fB\-m\ \fR\fB\fIflag\fR\fR] [\fB\-n\ \fR\fB\fI#cpus\fR\fR] [\fB\-p\ \fR\fB\fIport\fR\fR] [\fB\-s\fR] [\fB\-S\ \fR\fB\fI#max\-socks\fR\fR] [\fB\-t\ \fR\fB\fIdirectory\fR\fR] [\fB\-U\ \fR\fB\fI#listeners\fR\fR] [\fB\-u\ \fR\fB\fIuser\fR\fR] [\fB\-v\fR] [\fB\-V\fR] [\fB\-x\ \fR\fB\fIcache\-file\fR\fR] .SH "DESCRIPTION" .PP \fBnamed\fR @@ -168,6 +168,19 @@ is defined allows a process with root privileges to escape a chroot jail. .RE .RE .PP +\-U \fI#listeners\fR +.RS 4 +Use +\fI#listeners\fR +worker threads to listen for incoming UDP packets on each address. If not specified, +\fBnamed\fR +will use the number of detected CPUs. If +\fB\-n\fR +has been set to a higher value than the number of CPUs, then +\fB\-U\fR +may be increased as high as that value, but no higher. +.RE +.PP \-u \fIuser\fR .RS 4 Setuid to @@ -267,7 +280,7 @@ BIND 9 Administrator Reference Manual. .PP Internet Systems Consortium .SH "COPYRIGHT" -Copyright \(co 2004\-2009 Internet Systems Consortium, Inc. ("ISC") +Copyright \(co 2004\-2009, 2011, 2013 Internet Systems Consortium, Inc. ("ISC") .br Copyright \(co 2000, 2001, 2003 Internet Software Consortium. .br diff --git a/contrib/bind9/bin/named/named.conf.5 b/contrib/bind9/bin/named/named.conf.5 index 09b147ee7de..8d0122280b8 100644 --- a/contrib/bind9/bin/named/named.conf.5 +++ b/contrib/bind9/bin/named/named.conf.5 @@ -289,7 +289,8 @@ options { notify\-delay \fIseconds\fR; notify\-to\-soa \fIboolean\fR; also\-notify [ port \fIinteger\fR ] { ( \fIipv4_address\fR | \fIipv6_address\fR ) - [ port \fIinteger\fR ]; ... }; + [ port \fIinteger\fR ]; ... + [ key \fIkeyname\fR ] ... }; allow\-notify { \fIaddress_match_element\fR; ... }; forward ( first | only ); forwarders [ port \fIinteger\fR ] { @@ -458,7 +459,8 @@ view \fIstring\fR \fIoptional_class\fR { notify\-delay \fIseconds\fR; notify\-to\-soa \fIboolean\fR; also\-notify [ port \fIinteger\fR ] { ( \fIipv4_address\fR | \fIipv6_address\fR ) - [ port \fIinteger\fR ]; ... }; + [ port \fIinteger\fR ]; ... + [ key \fIkeyname\fR ] ... }; allow\-notify { \fIaddress_match_element\fR; ... }; forward ( first | only ); forwarders [ port \fIinteger\fR ] { @@ -502,7 +504,7 @@ view \fIstring\fR \fIoptional_class\fR { .RS 4 .nf zone \fIstring\fR \fIoptional_class\fR { - type ( master | slave | stub | hint | + type ( master | slave | stub | hint | redirect | forward | delegation\-only ); file \fIquoted_string\fR; masters [ port \fIinteger\fR ] { @@ -544,7 +546,8 @@ zone \fIstring\fR \fIoptional_class\fR { notify\-delay \fIseconds\fR; notify\-to\-soa \fIboolean\fR; also\-notify [ port \fIinteger\fR ] { ( \fIipv4_address\fR | \fIipv6_address\fR ) - [ port \fIinteger\fR ]; ... }; + [ port \fIinteger\fR ]; ... + [ key \fIkeyname\fR ] ... }; allow\-notify { \fIaddress_match_element\fR; ... }; forward ( first | only ); forwarders [ port \fIinteger\fR ] { @@ -560,6 +563,7 @@ zone \fIstring\fR \fIoptional_class\fR { max\-refresh\-time \fIinteger\fR; min\-refresh\-time \fIinteger\fR; multi\-master \fIboolean\fR; + request\-ixfr \fIboolean\fR; sig\-validity\-interval \fIinteger\fR; transfer\-source ( \fIipv4_address\fR | * ) [ port ( \fIinteger\fR | * ) ]; diff --git a/contrib/bind9/bin/named/named.conf.docbook b/contrib/bind9/bin/named/named.conf.docbook index 2527ac3ae7e..d778706930e 100644 --- a/contrib/bind9/bin/named/named.conf.docbook +++ b/contrib/bind9/bin/named/named.conf.docbook @@ -17,7 +17,7 @@ - PERFORMANCE OF THIS SOFTWARE. --> - + Aug 13, 2004 @@ -326,7 +326,8 @@ options { notify-delay seconds; notify-to-soa boolean; also-notify port integer { ( ipv4_address | ipv6_address ) - port integer ; ... }; + port integer ; ... + key keyname ... }; allow-notify { address_match_element; ... }; forward ( first | only ); @@ -513,7 +514,8 @@ view string optional_class notify-delay seconds; notify-to-soa boolean; also-notify port integer { ( ipv4_address | ipv6_address ) - port integer ; ... }; + port integer ; ... + key keyname ... }; allow-notify { address_match_element; ... }; forward ( first | only ); @@ -563,7 +565,7 @@ view string optional_class ZONE zone string optional_class { - type ( master | slave | stub | hint | + type ( master | slave | stub | hint | redirect | forward | delegation-only ); file quoted_string; @@ -609,7 +611,8 @@ zone string optional_class notify-delay seconds; notify-to-soa boolean; also-notify port integer { ( ipv4_address | ipv6_address ) - port integer ; ... }; + port integer ; ... + key keyname ... }; allow-notify { address_match_element; ... }; forward ( first | only ); @@ -627,6 +630,7 @@ zone string optional_class max-refresh-time integer; min-refresh-time integer; multi-master boolean; + request-ixfr boolean; sig-validity-interval integer; transfer-source ( ipv4_address | * ) diff --git a/contrib/bind9/bin/named/named.conf.html b/contrib/bind9/bin/named/named.conf.html index a8b35edc860..23d9391af3d 100644 --- a/contrib/bind9/bin/named/named.conf.html +++ b/contrib/bind9/bin/named/named.conf.html @@ -21,7 +21,7 @@
-
+

Name

named.conf — configuration file for named

@@ -31,7 +31,7 @@

named.conf

-

DESCRIPTION

+

DESCRIPTION

named.conf is the configuration file for named. Statements are enclosed @@ -50,14 +50,14 @@

-

ACL

+

ACL


acl string { address_match_element; ... };

-

KEY

+

KEY


key domain_name {
algorithm string;
@@ -66,7 +66,7 @@ key

-

MASTERS

+

MASTERS


masters string [ port integer ] {
masters | ipv4_address [port integer] |
@@ -75,7 +75,7 @@ masters

-

SERVER

+

SERVER


server ( ipv4_address[/prefixlen] | ipv6_address[/prefixlen] ) {
bogus boolean;
@@ -97,7 +97,7 @@ server

-

TRUSTED-KEYS

+

TRUSTED-KEYS


trusted-keys {
domain_name flags protocol algorithm key; ... 
@@ -105,7 +105,7 @@ trusted-keys

-

MANAGED-KEYS

+

MANAGED-KEYS


managed-keys {
domain_name initial-key flags protocol algorithm key; ... 
@@ -113,7 +113,7 @@ managed-keys

-

CONTROLS

+

CONTROLS


controls {
inet ( ipv4_address | ipv6_address | * )
@@ -125,7 +125,7 @@ controls

-

LOGGING

+

LOGGING


logging {
channel string {
@@ -143,7 +143,7 @@ logging

-

LWRES

+

LWRES


lwres {
listen-on [ port integer ] {
@@ -156,7 +156,7 @@ lwres

-

OPTIONS

+

OPTIONS


options {
avoid-v4-udp-ports { port; ... };
@@ -291,7 +291,8 @@ options notify-delay seconds;
notify-to-soa boolean;
also-notify [ port integer ] { ( ipv4_address | ipv6_address )
- [ port integer ]; ... };
+ [ port integer ]; ...
+ [ key keyname ] ... };
allow-notify { address_match_element; ... };

forward ( first | only );
@@ -360,7 +361,7 @@ options

-

VIEW

+

VIEW


view string optional_class {
match-clients { address_match_element; ... };
@@ -477,7 +478,8 @@ view notify-delay seconds;
notify-to-soa boolean;
also-notify [ port integer ] { ( ipv4_address | ipv6_address )
- [ port integer ]; ... };
+ [ port integer ]; ...
+ [ key keyname ] ... };
allow-notify { address_match_element; ... };

forward ( first | only );
@@ -523,10 +525,10 @@ view

-

ZONE

+

ZONE


zone string optional_class {
- type ( master | slave | stub | hint |
+ type ( master | slave | stub | hint | redirect |
forward | delegation-only );
file quoted_string;

@@ -572,7 +574,8 @@ zone notify-delay seconds;
notify-to-soa boolean;
also-notify [ port integer ] { ( ipv4_address | ipv6_address )
- [ port integer ]; ... };
+ [ port integer ]; ...
+ [ key keyname ] ... };
allow-notify { address_match_element; ... };

forward ( first | only );
@@ -590,6 +593,7 @@ zone max-refresh-time integer;
min-refresh-time integer;
multi-master boolean;
+ request-ixfr boolean;
sig-validity-interval integer;

transfer-source ( ipv4_address | * )
@@ -618,12 +622,12 @@ zone

-

FILES

+

FILES

/etc/named.conf

-

SEE ALSO

+

SEE ALSO

named(8), named-checkconf(8), rndc(8), diff --git a/contrib/bind9/bin/named/named.docbook b/contrib/bind9/bin/named/named.docbook index c748911e24a..1f08e196e04 100644 --- a/contrib/bind9/bin/named/named.docbook +++ b/contrib/bind9/bin/named/named.docbook @@ -2,7 +2,7 @@ "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd" []> - + May 21, 2009 @@ -43,6 +43,8 @@ 2007 2008 2009 + 2011 + 2013 Internet Systems Consortium, Inc. ("ISC") @@ -69,6 +71,7 @@ + @@ -281,6 +284,21 @@ + + -U #listeners + + + Use #listeners + worker threads to listen for incoming UDP packets on each + address. If not specified, named will + use the number of detected CPUs. If + has been set to a higher value than the number of CPUs, + then may be increased as high as that + value, but no higher. + + + + -u user diff --git a/contrib/bind9/bin/named/named.html b/contrib/bind9/bin/named/named.html index cf3cb2678f3..fc8de5147ad 100644 --- a/contrib/bind9/bin/named/named.html +++ b/contrib/bind9/bin/named/named.html @@ -1,5 +1,5 @@ - + Aug 25, 2009 @@ -42,6 +42,8 @@ 2008 2009 2010 + 2011 + 2012 Internet Systems Consortium, Inc. ("ISC") @@ -424,7 +426,7 @@ - prereq nxdomain + prereq nxdomain domain-name @@ -438,7 +440,7 @@ - prereq yxdomain + prereq yxdomain domain-name @@ -452,7 +454,7 @@ - prereq nxrrset + prereq nxrrset domain-name class type @@ -474,7 +476,7 @@ - prereq yxrrset + prereq yxrrset domain-name class type @@ -496,7 +498,7 @@ - prereq yxrrset + prereq yxrrset domain-name class type @@ -530,7 +532,7 @@ - update delete + update delete domain-name ttl class @@ -556,7 +558,7 @@ - update add + update add domain-name ttl class diff --git a/contrib/bind9/bin/nsupdate/nsupdate.html b/contrib/bind9/bin/nsupdate/nsupdate.html index 5c108e37461..276d4af6fbe 100644 --- a/contrib/bind9/bin/nsupdate/nsupdate.html +++ b/contrib/bind9/bin/nsupdate/nsupdate.html @@ -1,5 +1,5 @@ - + Feb 19, 2009 @@ -39,7 +39,6 @@ 2009 2010 2011 - 2012 Internet Systems Consortium, Inc. ("ISC") diff --git a/contrib/bind9/bin/tools/genrandom.html b/contrib/bind9/bin/tools/genrandom.html index f69b7ca2da2..6b704340797 100644 --- a/contrib/bind9/bin/tools/genrandom.html +++ b/contrib/bind9/bin/tools/genrandom.html @@ -1,5 +1,5 @@ - + DNSSEC, Dynamic Zones, and Automatic Signing @@ -100,8 +100,7 @@ named can search the key directory for keys matching the zone, insert them into the zone, and use them to sign the zone. It will do so only when it receives an - rndc sign <zonename> or - rndc loadkeys <zonename> command. + rndc sign <zonename>. auto-dnssec maintain includes the above @@ -109,12 +108,34 @@ DNSKEY records on schedule according to the keys' timing metadata. (See and for more information.) + + + named will periodically search the key directory + for keys matching the zone, and if the keys' metadata indicates + that any change should be made the zone, such as adding, removing, + or revoking a key, then that action will be carried out. By default, + the key directory is checked for changes every 60 minutes; this period + can be adjusted with the , up + to a maximum of 24 hours. The rndc loadkeys forces + named to check for key updates immediately. + + If keys are present in the key directory the first time the zone - is loaded, it will be signed immediately, without waiting for an + is loaded, the zone will be signed immediately, without waiting for an rndc sign or rndc loadkeys command. (Those commands can still be used when there are unscheduled key changes, however.) + + If you wish the zone to be signed using NSEC3 instead of NSEC, + submit an NSEC3PARAM record via dynamic update prior to the + scheduled publication and activation of the keys. If you wish the + NSEC3 chain to have the OPTOUT bit set, set it in the flags field + of the NSEC3PARAM record. The NSEC3PARAM record will not appear in + the zone immediately, but it will be stored for later reference. When + the zone is signed and the NSEC3 chain is completed, the NSEC3PARAM + record will appear in the zone. + Using the auto-dnssec option requires the zone to be configured to allow dynamic updates, by adding an diff --git a/contrib/bind9/doc/arm/man.arpaname.html b/contrib/bind9/doc/arm/man.arpaname.html index c57540be6fa..45391da16d9 100644 --- a/contrib/bind9/doc/arm/man.arpaname.html +++ b/contrib/bind9/doc/arm/man.arpaname.html @@ -50,20 +50,20 @@

arpaname {ipaddress ...}

-

DESCRIPTION

+

DESCRIPTION

arpaname translates IP addresses (IPv4 and IPv6) to the corresponding IN-ADDR.ARPA or IP6.ARPA names.

-

SEE ALSO

+

SEE ALSO

BIND 9 Administrator Reference Manual.

-

AUTHOR

+

AUTHOR

Internet Systems Consortium

diff --git a/contrib/bind9/doc/arm/man.ddns-confgen.html b/contrib/bind9/doc/arm/man.ddns-confgen.html index 3555d5152ce..fed8fbcf787 100644 --- a/contrib/bind9/doc/arm/man.ddns-confgen.html +++ b/contrib/bind9/doc/arm/man.ddns-confgen.html @@ -50,7 +50,7 @@

ddns-confgen [-a algorithm] [-h] [-k keyname] [-r randomfile] [ -s name | -z zone ] [-q] [name]

-

DESCRIPTION

+

DESCRIPTION

ddns-confgen generates a key for use by nsupdate and named. It simplifies configuration @@ -77,7 +77,7 @@

-

OPTIONS

+

OPTIONS

-a algorithm

@@ -144,7 +144,7 @@

-

SEE ALSO

+

SEE ALSO

nsupdate(1), named.conf(5), named(8), @@ -152,7 +152,7 @@

-

AUTHOR

+

AUTHOR

Internet Systems Consortium

diff --git a/contrib/bind9/doc/arm/man.dig.html b/contrib/bind9/doc/arm/man.dig.html index b2d2b541dd4..556e34b12b4 100644 --- a/contrib/bind9/doc/arm/man.dig.html +++ b/contrib/bind9/doc/arm/man.dig.html @@ -52,7 +52,7 @@

dig [global-queryopt...] [query...]

-

DESCRIPTION

+

DESCRIPTION

dig (domain information groper) is a flexible tool for interrogating DNS name servers. It performs DNS lookups and @@ -99,7 +99,7 @@

-

SIMPLE USAGE

+

SIMPLE USAGE

A typical invocation of dig looks like:

@@ -152,7 +152,7 @@

-

OPTIONS

+

OPTIONS

The -b option sets the source IP address of the query to address. This must be a valid @@ -256,7 +256,7 @@

-

QUERY OPTIONS

+

QUERY OPTIONS

dig provides a number of query options which affect the way in which lookups are made and the results displayed. Some of @@ -341,7 +341,8 @@ policy of the server. AD=1 indicates that all records have been validated as secure and the answer is not from a OPT-OUT range. AD=0 indicate that some part - of the answer was insecure or not validated. + of the answer was insecure or not validated. This + bit is set by default.

+[no]cdflag

@@ -360,15 +361,13 @@

+[no]recurse

- Toggle the setting of the RD (recursion desired) bit in the - query. - This bit is set by default, which means dig - normally sends recursive queries. Recursion is automatically - disabled - when the +nssearch or - +trace query options are - used. -

+ Toggle the setting of the RD (recursion desired) bit + in the query. This bit is set by default, which means + dig normally sends recursive + queries. Recursion is automatically disabled when + the +nssearch or + +trace query options are used. +

+[no]nssearch

When this option is set, dig @@ -380,18 +379,21 @@ zone.

+[no]trace
-

- Toggle tracing of the delegation path from the root name servers - for - the name being looked up. Tracing is disabled by default. When - tracing is enabled, dig makes - iterative queries to - resolve the name being looked up. It will follow referrals from - the - root servers, showing the answer from each server that was used - to - resolve the lookup. -

+
+

+ Toggle tracing of the delegation path from the root + name servers for the name being looked up. Tracing + is disabled by default. When tracing is enabled, + dig makes iterative queries to + resolve the name being looked up. It will follow + referrals from the root servers, showing the answer + from each server that was used to resolve the lookup. +

+

+ +dnssec is also set when +trace is + set to better emulate the default queries from a nameserver. +

+
+[no]cmd

Toggles the printing of the initial comment in the output @@ -418,8 +420,25 @@

+[no]comments

Toggle the display of comment lines in the output. The default - is to - print comments. + is to print comments. +

+
+[no]rrcomments
+

+ Toggle the display of per-record comments in the output (for + example, human-readable key information about DNSKEY records). + The default is not to print record comments unless multiline + mode is active. +

+
+split=W
+

+ Split long hex- or base64-formatted fields in resource + records into chunks of W characters + (where W is rounded up to the nearest + multiple of 4). + +nosplit or + +split=0 causes fields not to be + split at all. The default is 56 characters, or 44 characters + when multiline mode is active.

+[no]stats

@@ -514,9 +533,10 @@

+edns=#

Specify the EDNS version to query with. Valid values - are 0 to 255. Setting the EDNS version will cause a - EDNS query to be sent. +noedns clears the - remembered EDNS version. + are 0 to 255. Setting the EDNS version will cause + a EDNS query to be sent. +noedns + clears the remembered EDNS version. EDNS is set to + 0 by default.

+[no]multiline

@@ -587,7 +607,7 @@

-

MULTIPLE QUERIES

+

MULTIPLE QUERIES

The BIND 9 implementation of dig supports @@ -633,7 +653,7 @@ dig +qr www.isc.org any -x 127.0.0.1 isc.org ns +noqr

-

IDN SUPPORT

+

IDN SUPPORT

If dig has been built with IDN (internationalized domain name) support, it can accept and display non-ASCII domain names. @@ -647,14 +667,14 @@ dig +qr www.isc.org any -x 127.0.0.1 isc.org ns +noqr

-

FILES

+

FILES

/etc/resolv.conf

${HOME}/.digrc

-

SEE ALSO

+

SEE ALSO

host(1), named(8), dnssec-keygen(8), @@ -662,7 +682,7 @@ dig +qr www.isc.org any -x 127.0.0.1 isc.org ns +noqr

-

BUGS

+

BUGS

There are probably too many query options.

diff --git a/contrib/bind9/doc/arm/man.dnssec-dsfromkey.html b/contrib/bind9/doc/arm/man.dnssec-dsfromkey.html index 6d8eeabe6f4..bb8d13607ff 100644 --- a/contrib/bind9/doc/arm/man.dnssec-dsfromkey.html +++ b/contrib/bind9/doc/arm/man.dnssec-dsfromkey.html @@ -22,7 +22,7 @@ - + @@ -31,7 +31,7 @@ dnssec-dsfromkey -Prev  +Prev  Manual pages  Next @@ -47,18 +47,18 @@

Synopsis

-

dnssec-dsfromkey [-v level] [-1] [-2] [-a alg] [-l domain] {keyfile}

-

dnssec-dsfromkey {-s} [-1] [-2] [-a alg] [-K directory] [-l domain] [-s] [-c class] [-f file] [-A] [-v level] {dnsname}

+

dnssec-dsfromkey [-v level] [-1] [-2] [-a alg] [-l domain] [-T TTL] {keyfile}

+

dnssec-dsfromkey {-s} [-1] [-2] [-a alg] [-K directory] [-l domain] [-s] [-c class] [-T TTL] [-f file] [-A] [-v level] {dnsname}

-

DESCRIPTION

+

DESCRIPTION

dnssec-dsfromkey outputs the Delegation Signer (DS) resource record (RR), as defined in RFC 3658 and RFC 4509, for the given key(s).

-

OPTIONS

+

OPTIONS

-1

@@ -76,6 +76,10 @@ SHA-256 (SHA256), GOST or SHA-384 (SHA384). These values are case insensitive.

+
-T TTL
+

+ Specifies the TTL of the DS records. +

-K directory

Look for key files (or, in keyset mode, @@ -83,12 +87,23 @@ directory.

-f file
-

+

+

Zone file mode: in place of the keyfile name, the argument is the DNS domain name of a zone master file, which can be read from file. If the zone name is the same as file, then it may be omitted. -

+

+

+ If file is set to "-", then + the zone data is read from the standard input. This makes it + possible to use the output of the dig + command as input, as in: +

+

+ dig dnskey example.com | dnssec-dsfromkey -f - example.com +

+
-A

Include ZSK's when generating DS records. Without this option, @@ -120,7 +135,7 @@

-

EXAMPLE

+

EXAMPLE

To build the SHA-256 DS RR from the Kexample.com.+003+26160 @@ -135,7 +150,7 @@

-

FILES

+

FILES

The keyfile can be designed by the key identification Knnnn.+aaa+iiiii or the full file name @@ -149,13 +164,13 @@

-

CAVEAT

+

CAVEAT

A keyfile error can give a "file not found" even if the file exists.

-

SEE ALSO

+

SEE ALSO

dnssec-keygen(8), dnssec-signzone(8), BIND 9 Administrator Reference Manual, @@ -165,7 +180,7 @@

-

AUTHOR

+

AUTHOR

Internet Systems Consortium

@@ -175,13 +190,14 @@ +Prev  - + diff --git a/contrib/bind9/doc/arm/man.dnssec-keyfromlabel.html b/contrib/bind9/doc/arm/man.dnssec-keyfromlabel.html index b691828823b..dad88378e89 100644 --- a/contrib/bind9/doc/arm/man.dnssec-keyfromlabel.html +++ b/contrib/bind9/doc/arm/man.dnssec-keyfromlabel.html @@ -47,10 +47,10 @@

Synopsis

-

dnssec-keyfromlabel {-l label} [-3] [-a algorithm] [-A date/offset] [-c class] [-D date/offset] [-E engine] [-f flag] [-G] [-I date/offset] [-k] [-K directory] [-n nametype] [-P date/offset] [-p protocol] [-R date/offset] [-t type] [-v level] [-y] {name}

+

dnssec-keyfromlabel {-l label} [-3] [-a algorithm] [-A date/offset] [-c class] [-D date/offset] [-E engine] [-f flag] [-G] [-I date/offset] [-k] [-K directory] [-L ttl] [-n nametype] [-P date/offset] [-p protocol] [-R date/offset] [-t type] [-v level] [-y] {name}

-

DESCRIPTION

+

DESCRIPTION

dnssec-keyfromlabel gets keys with the given label from a crypto hardware and builds key files for DNSSEC (Secure DNS), as defined in RFC 2535 @@ -63,7 +63,7 @@

-

OPTIONS

+

OPTIONS

-a algorithm
@@ -154,6 +154,15 @@

Generate KEY records rather than DNSKEY records.

+
-L ttl
+

+ Sets the default TTL to use for this key when it is converted + into a DNSKEY RR. If the key is imported into a zone, + this is the TTL that will be used for it, unless there was + already a DNSKEY RRset in place, in which case the existing TTL + would take precedence. Setting the default TTL to + 0 or none removes it. +

-p protocol

Sets the protocol value for the key. The protocol @@ -183,7 +192,7 @@

-

TIMING OPTIONS

+

TIMING OPTIONS

Dates can be expressed in the format YYYYMMDD or YYYYMMDDHHMMSS. If the argument begins with a '+' or '-', it is interpreted as @@ -230,7 +239,7 @@

-

GENERATED KEY FILES

+

GENERATED KEY FILES

When dnssec-keyfromlabel completes successfully, @@ -269,7 +278,7 @@

-

SEE ALSO

+

SEE ALSO

dnssec-keygen(8), dnssec-signzone(8), BIND 9 Administrator Reference Manual, @@ -277,7 +286,7 @@

-

AUTHOR

+

AUTHOR

Internet Systems Consortium

diff --git a/contrib/bind9/doc/arm/man.dnssec-keygen.html b/contrib/bind9/doc/arm/man.dnssec-keygen.html index 2852022cdb8..8f846e0c34e 100644 --- a/contrib/bind9/doc/arm/man.dnssec-keygen.html +++ b/contrib/bind9/doc/arm/man.dnssec-keygen.html @@ -47,10 +47,10 @@

Synopsis

-

dnssec-keygen [-a algorithm] [-b keysize] [-n nametype] [-3] [-A date/offset] [-C] [-c class] [-D date/offset] [-E engine] [-e] [-f flag] [-G] [-g generator] [-h] [-I date/offset] [-i interval] [-K directory] [-k] [-P date/offset] [-p protocol] [-q] [-R date/offset] [-r randomdev] [-S key] [-s strength] [-t type] [-v level] [-z] {name}

+

dnssec-keygen [-a algorithm] [-b keysize] [-n nametype] [-3] [-A date/offset] [-C] [-c class] [-D date/offset] [-E engine] [-f flag] [-G] [-g generator] [-h] [-I date/offset] [-i interval] [-K directory] [-L ttl] [-k] [-P date/offset] [-p protocol] [-q] [-R date/offset] [-r randomdev] [-S key] [-s strength] [-t type] [-v level] [-z] {name}

-

DESCRIPTION

+

DESCRIPTION

dnssec-keygen generates keys for DNSSEC (Secure DNS), as defined in RFC 2535 and RFC 4034. It can also generate keys for use with @@ -64,7 +64,7 @@

-

OPTIONS

+

OPTIONS

-a algorithm
@@ -157,10 +157,6 @@ support it defaults to pkcs11; the empty name resets it to no engine.

-
-e
-

- If generating an RSAMD5/RSASHA1 key, use a large exponent. -

-f flag

Set the specified flag in the flag field of the KEY/DNSKEY record. @@ -191,6 +187,15 @@

Deprecated in favor of -T KEY.

+
-L ttl
+

+ Sets the default TTL to use for this key when it is converted + into a DNSKEY RR. If the key is imported into a zone, + this is the TTL that will be used for it, unless there was + already a DNSKEY RRset in place, in which case the existing TTL + would take precedence. Setting the default TTL to + 0 or none removes it. +

-p protocol

Sets the protocol value for the generated key. The protocol @@ -269,7 +274,7 @@

-

TIMING OPTIONS

+

TIMING OPTIONS

Dates can be expressed in the format YYYYMMDD or YYYYMMDDHHMMSS. If the argument begins with a '+' or '-', it is interpreted as @@ -340,7 +345,7 @@

-

GENERATED KEYS

+

GENERATED KEYS

When dnssec-keygen completes successfully, @@ -386,7 +391,7 @@

-

EXAMPLE

+

EXAMPLE

To generate a 768-bit DSA key for the domain example.com, the following command would be @@ -407,7 +412,7 @@

-

SEE ALSO

+

SEE ALSO

dnssec-signzone(8), BIND 9 Administrator Reference Manual, RFC 2539, @@ -416,7 +421,7 @@

-

AUTHOR

+

AUTHOR

Internet Systems Consortium

diff --git a/contrib/bind9/doc/arm/man.dnssec-revoke.html b/contrib/bind9/doc/arm/man.dnssec-revoke.html index b1eb6914fa8..c6490eff2b8 100644 --- a/contrib/bind9/doc/arm/man.dnssec-revoke.html +++ b/contrib/bind9/doc/arm/man.dnssec-revoke.html @@ -50,7 +50,7 @@

dnssec-revoke [-hr] [-v level] [-K directory] [-E engine] [-f] [-R] {keyfile}

-

DESCRIPTION

+

DESCRIPTION

dnssec-revoke reads a DNSSEC key file, sets the REVOKED bit on the key as defined in RFC 5011, and creates a new pair of key files containing the @@ -58,7 +58,7 @@

-

OPTIONS

+

OPTIONS

-h

@@ -96,14 +96,14 @@

-

SEE ALSO

+

SEE ALSO

dnssec-keygen(8), BIND 9 Administrator Reference Manual, RFC 5011.

-

AUTHOR

+

AUTHOR

Internet Systems Consortium

diff --git a/contrib/bind9/doc/arm/man.dnssec-settime.html b/contrib/bind9/doc/arm/man.dnssec-settime.html index 541223ccda8..3e121c4e264 100644 --- a/contrib/bind9/doc/arm/man.dnssec-settime.html +++ b/contrib/bind9/doc/arm/man.dnssec-settime.html @@ -47,10 +47,10 @@

Synopsis

-

dnssec-settime [-f] [-K directory] [-P date/offset] [-A date/offset] [-R date/offset] [-I date/offset] [-D date/offset] [-h] [-v level] [-E engine] {keyfile}

+

dnssec-settime [-f] [-K directory] [-L ttl] [-P date/offset] [-A date/offset] [-R date/offset] [-I date/offset] [-D date/offset] [-h] [-v level] [-E engine] {keyfile}

-

DESCRIPTION

+

DESCRIPTION

dnssec-settime reads a DNSSEC private key file and sets the key timing metadata as specified by the -P, -A, @@ -76,7 +76,7 @@

-

OPTIONS

+

OPTIONS

-f

@@ -93,6 +93,15 @@

Sets the directory in which the key files are to reside.

+
-L ttl
+

+ Sets the default TTL to use for this key when it is converted + into a DNSKEY RR. If the key is imported into a zone, + this is the TTL that will be used for it, unless there was + already a DNSKEY RRset in place, in which case the existing TTL + would take precedence. Setting the default TTL to + 0 or none removes it. +

-h

Emit usage message and exit. @@ -109,7 +118,7 @@

-

TIMING OPTIONS

+

TIMING OPTIONS

Dates can be expressed in the format YYYYMMDD or YYYYMMDDHHMMSS. If the argument begins with a '+' or '-', it is interpreted as @@ -188,7 +197,7 @@

-

PRINTING OPTIONS

+

PRINTING OPTIONS

dnssec-settime can also be used to print the timing metadata associated with a key. @@ -214,7 +223,7 @@

-

SEE ALSO

+

SEE ALSO

dnssec-keygen(8), dnssec-signzone(8), BIND 9 Administrator Reference Manual, @@ -222,7 +231,7 @@

-

AUTHOR

+

AUTHOR

Internet Systems Consortium

diff --git a/contrib/bind9/doc/arm/man.dnssec-signzone.html b/contrib/bind9/doc/arm/man.dnssec-signzone.html index 4f73bf4dfb3..290e7700973 100644 --- a/contrib/bind9/doc/arm/man.dnssec-signzone.html +++ b/contrib/bind9/doc/arm/man.dnssec-signzone.html @@ -23,7 +23,7 @@ - + -
-Prev  Up  Next
host  +dnssec-coverage  Home  dnssec-keyfromlabel Prev  Manual pages Next + Next
@@ -47,10 +47,10 @@

Synopsis

-

dnssec-signzone [-a] [-c class] [-d directory] [-E engine] [-e end-time] [-f output-file] [-g] [-h] [-K directory] [-k key] [-l domain] [-i interval] [-I input-format] [-j jitter] [-N soa-serial-format] [-o origin] [-O output-format] [-p] [-P] [-r randomdev] [-S] [-s start-time] [-T ttl] [-t] [-u] [-v level] [-x] [-z] [-3 salt] [-H iterations] [-A] {zonefile} [key...]

+

dnssec-signzone [-a] [-c class] [-d directory] [-D] [-E engine] [-e end-time] [-f output-file] [-g] [-h] [-K directory] [-k key] [-L serial] [-l domain] [-i interval] [-I input-format] [-j jitter] [-N soa-serial-format] [-o origin] [-O output-format] [-P] [-p] [-R] [-r randomdev] [-S] [-s start-time] [-T ttl] [-t] [-u] [-v level] [-X extended end-time] [-x] [-z] [-3 salt] [-H iterations] [-A] {zonefile} [key...]

-

DESCRIPTION

+

DESCRIPTION

dnssec-signzone signs a zone. It generates NSEC and RRSIG records and produces a signed version of the @@ -61,7 +61,7 @@

-

OPTIONS

+

OPTIONS

-a

@@ -85,6 +85,17 @@ Look for dsset- or keyset- files in directory.

+
-D
+

+ Output only those record types automatically managed by + dnssec-signzone, i.e. RRSIG, NSEC, + NSEC3 and NSEC3PARAM records. If smart signing + (-S) is used, DNSKEY records are also + included. The resulting file can be included in the original + zone file with $INCLUDE. This option + cannot be combined with -O raw or serial + number updating. +

-E engine

Uses a crypto hardware (OpenSSL engine) for the crypto operations @@ -136,12 +147,36 @@ end-time must be later than start-time.

+
-X extended end-time
+
+

+ Specify the date and time when the generated RRSIG records + for the DNSKEY RRset will expire. This is to be used in cases + when the DNSKEY signatures need to persist longer than + signatures on other records; e.g., when the private component + of the KSK is kept offline and the KSK signature is to be + refreshed manually. +

+

+ As with start-time, an absolute + time is indicated in YYYYMMDDHHMMSS notation. A time relative + to the start time is indicated with +N, which is N seconds from + the start time. A time relative to the current time is + indicated with now+N. If no extended end-time is + specified, the value of end-time is used as + the default. (end-time, in turn, defaults to + 30 days from the start time.) extended end-time + must be later than start-time. +

+
-f output-file

The name of the output file containing the signed zone. The default is to append .signed to - the - input filename. + the input filename. If output-file is + set to "-", then the signed zone is + written to the standard output, with a default output + format of "full".

-h

@@ -202,6 +237,12 @@ validators need to refetch at mostly the same time.

+
-L serial
+

+ When writing a signed zone to 'raw' format, set the "source serial" + value in the header to the specified serial number. (This is + expected to be used primarily for testing purposes.) +

-n ncpus

Specifies the number of threads to use. By default, one @@ -235,7 +276,15 @@

The format of the output file containing the signed zone. Possible formats are "text" (default) - and "raw". + "full", which is text output in a + format suitable for processing by external scripts, + and "raw" or "raw=N", + which store the zone in a binary format for rapid loading + by named. "raw=N" + specifies the format version of the raw zone file: if N + is 0, the raw file can be read by any version of + named; if N is 1, the file can be + read by release 9.9.0 or higher. The default is 1.

-p

@@ -257,6 +306,22 @@ This option skips these tests.

+
-R
+
+

+ Remove signatures from keys that no longer exist. +

+

+ Normally, when a previously-signed zone is passed as input + to the signer, and a DNSKEY record has been removed and + replaced with a new one, signatures from the old key + that are still within their validity period are retained. + This allows the zone to continue to validate with cached + copies of the old DNSKEY RRset. The -R forces + dnssec-signzone to remove all orphaned + signatures. +

+
-r randomdev

Specifies the source of randomness. If the operating @@ -315,15 +380,17 @@

-T ttl

- Specifies the TTL to be used for new DNSKEY records imported - into the zone from the key repository. If not specified, - the default is the minimum TTL value from the zone's SOA + Specifies a TTL to be used for new DNSKEY records imported + into the zone from the key repository. If not + specified, the default is the TTL value from the zone's SOA record. This option is ignored when signing without -S, since DNSKEY records are not imported from the key repository in that case. It is also ignored if there are any pre-existing DNSKEY records at the zone apex, in which case new records' TTL values will be set to match - them. + them, or if any of the imported DNSKEY records had a default + TTL value. In the event of a a conflict between TTL values in + imported keys, the shortest one is used.

-t

@@ -397,7 +464,7 @@

-

EXAMPLE

+

EXAMPLE

The following command signs the example.com zone with the DSA key generated by dnssec-keygen @@ -427,14 +494,14 @@ db.example.com.signed %

-

SEE ALSO

+

SEE ALSO

dnssec-keygen(8), BIND 9 Administrator Reference Manual, RFC 4033.

-

AUTHOR

+

AUTHOR

Internet Systems Consortium

@@ -446,14 +513,14 @@ db.example.com.signed Prev  UpNextNext dnssec-settime  Homenamed-checkconfdnssec-verify diff --git a/contrib/bind9/doc/arm/man.dnssec-verify.html b/contrib/bind9/doc/arm/man.dnssec-verify.html new file mode 100644 index 00000000000..d8a0bf0ebb4 --- /dev/null +++ b/contrib/bind9/doc/arm/man.dnssec-verify.html @@ -0,0 +1,156 @@ + + + + + +dnssec-verify + + + + + + + + +
+
+
+

Name

+

dnssec-verify — DNSSEC zone verification tool

+
+
+

Synopsis

+

dnssec-verify [-c class] [-E engine] [-I input-format] [-o origin] [-v level] [-x] [-z] {zonefile}

+
+
+

DESCRIPTION

+

dnssec-verify + verifies that a zone is fully signed for each algorithm found + in the DNSKEY RRset for the zone, and that the NSEC / NSEC3 + chains are complete. +

+
+
+

OPTIONS

+
+
-c class
+

+ Specifies the DNS class of the zone. +

+
-I input-format
+

+ The format of the input zone file. + Possible formats are "text" (default) + and "raw". + This option is primarily intended to be used for dynamic + signed zones so that the dumped zone file in a non-text + format containing updates can be verified independently. + The use of this option does not make much sense for + non-dynamic zones. +

+
-o origin
+

+ The zone origin. If not specified, the name of the zone file + is assumed to be the origin. +

+
-v level
+

+ Sets the debugging level. +

+
-x
+

+ Only verify that the DNSKEY RRset is signed with key-signing + keys. Without this flag, it is assumed that the DNSKEY RRset + will be signed by all active keys. When this flag is set, + it will not be an error if the DNSKEY RRset is not signed + by zone-signing keys. This corresponds to the -x + option in dnssec-signzone. +

+
-z
+
+

+ Ignore the KSK flag on the keys when determining whether + the zone if correctly signed. Without this flag it is + assumed that there will be a non-revoked, self-signed + DNSKEY with the KSK flag set for each algorithm and + that RRsets other than DNSKEY RRset will be signed with + a different DNSKEY without the KSK flag set. +

+

+ With this flag set, we only require that for each algorithm, + there will be at least one non-revoked, self-signed DNSKEY, + regardless of the KSK flag state, and that other RRsets + will be signed by a non-revoked key for the same algorithm + that includes the self-signed key; the same key may be used + for both purposes. This corresponds to the -z + option in dnssec-signzone. +

+
+
zonefile
+

+ The file containing the zone to be signed. +

+
+
+
+

SEE ALSO

+

+ dnssec-signzone(8), + BIND 9 Administrator Reference Manual, + RFC 4033. +

+
+
+

AUTHOR

+

Internet Systems Consortium +

+
+
+ + + diff --git a/contrib/bind9/doc/arm/man.genrandom.html b/contrib/bind9/doc/arm/man.genrandom.html index 596dddacce7..e7f0eebaf6f 100644 --- a/contrib/bind9/doc/arm/man.genrandom.html +++ b/contrib/bind9/doc/arm/man.genrandom.html @@ -50,7 +50,7 @@

genrandom [-n number] {size} {filename}

-

DESCRIPTION

+

DESCRIPTION

genrandom generates a file or a set of files containing a specified quantity @@ -59,7 +59,7 @@

-

ARGUMENTS

+

ARGUMENTS

-n number

@@ -77,14 +77,14 @@

-

SEE ALSO

+

SEE ALSO

rand(3), arc4random(3)

-

AUTHOR

+

AUTHOR

Internet Systems Consortium

diff --git a/contrib/bind9/doc/arm/man.host.html b/contrib/bind9/doc/arm/man.host.html index f4f0ef80018..2166dd224a7 100644 --- a/contrib/bind9/doc/arm/man.host.html +++ b/contrib/bind9/doc/arm/man.host.html @@ -23,7 +23,7 @@ - +
-

DESCRIPTION

+

DESCRIPTION

host is a simple utility for performing DNS lookups. It is normally used to convert names to IP addresses and vice versa. @@ -202,7 +202,7 @@

-

IDN SUPPORT

+

IDN SUPPORT

If host has been built with IDN (internationalized domain name) support, it can accept and display non-ASCII domain names. @@ -216,12 +216,12 @@

-

FILES

+

FILES

/etc/resolv.conf

-

SEE ALSO

+

SEE ALSO

dig(1), named(8).

@@ -234,13 +234,13 @@ Prev  UpNextNext dig  Homednssec-dsfromkeydnssec-checkds diff --git a/contrib/bind9/doc/arm/man.isc-hmac-fixup.html b/contrib/bind9/doc/arm/man.isc-hmac-fixup.html index 9c2a9611237..5b35c3854ec 100644 --- a/contrib/bind9/doc/arm/man.isc-hmac-fixup.html +++ b/contrib/bind9/doc/arm/man.isc-hmac-fixup.html @@ -50,7 +50,7 @@

isc-hmac-fixup {algorithm} {secret}

-

DESCRIPTION

+

DESCRIPTION

Versions of BIND 9 up to and including BIND 9.6 had a bug causing HMAC-SHA* TSIG keys which were longer than the digest length of the @@ -76,7 +76,7 @@

-

SECURITY CONSIDERATIONS

+

SECURITY CONSIDERATIONS

Secrets that have been converted by isc-hmac-fixup are shortened, but as this is how the HMAC protocol works in @@ -87,14 +87,14 @@

-

SEE ALSO

+

SEE ALSO

BIND 9 Administrator Reference Manual, RFC 2104.

-

AUTHOR

+

AUTHOR

Internet Systems Consortium

diff --git a/contrib/bind9/doc/arm/man.named-checkconf.html b/contrib/bind9/doc/arm/man.named-checkconf.html index 9a2f687843c..d120cb44d1b 100644 --- a/contrib/bind9/doc/arm/man.named-checkconf.html +++ b/contrib/bind9/doc/arm/man.named-checkconf.html @@ -22,7 +22,7 @@ - + @@ -31,7 +31,7 @@ named-checkconf -Prev  +Prev  Manual pages  Next @@ -50,7 +50,7 @@

named-checkconf [-h] [-v] [-j] [-t directory] {filename} [-p] [-z]

-

DESCRIPTION

+

DESCRIPTION

named-checkconf checks the syntax, but not the semantics, of a named configuration file. The file is parsed @@ -70,7 +70,7 @@

-

OPTIONS

+

OPTIONS

-h

@@ -109,21 +109,21 @@

-

RETURN VALUES

+

RETURN VALUES

named-checkconf returns an exit status of 1 if errors were detected and 0 otherwise.

-

SEE ALSO

+

SEE ALSO

named(8), named-checkzone(8), BIND 9 Administrator Reference Manual.

-

AUTHOR

+

AUTHOR

Internet Systems Consortium

@@ -133,14 +133,14 @@ +Prev  +dnssec-verify  diff --git a/contrib/bind9/doc/arm/man.named-checkzone.html b/contrib/bind9/doc/arm/man.named-checkzone.html index 78fade2cf73..b828f19764c 100644 --- a/contrib/bind9/doc/arm/man.named-checkzone.html +++ b/contrib/bind9/doc/arm/man.named-checkzone.html @@ -47,11 +47,11 @@

Synopsis

-

named-checkzone [-d] [-h] [-j] [-q] [-v] [-c class] [-f format] [-F format] [-i mode] [-k mode] [-m mode] [-M mode] [-n mode] [-o filename] [-r mode] [-s style] [-S mode] [-t directory] [-T mode] [-w directory] [-D] [-W mode] {zonename} {filename}

-

named-compilezone [-d] [-j] [-q] [-v] [-c class] [-C mode] [-f format] [-F format] [-i mode] [-k mode] [-m mode] [-n mode] [-r mode] [-s style] [-t directory] [-T mode] [-w directory] [-D] [-W mode] {-o filename} {zonename} {filename}

+

named-checkzone [-d] [-h] [-j] [-q] [-v] [-c class] [-f format] [-F format] [-i mode] [-k mode] [-m mode] [-M mode] [-n mode] [-L serial] [-o filename] [-r mode] [-s style] [-S mode] [-t directory] [-T mode] [-w directory] [-D] [-W mode] {zonename} {filename}

+

named-compilezone [-d] [-j] [-q] [-v] [-c class] [-C mode] [-f format] [-F format] [-i mode] [-k mode] [-m mode] [-n mode] [-L serial] [-r mode] [-s style] [-t directory] [-T mode] [-w directory] [-D] [-W mode] {-o filename} {zonename} {filename}

-

DESCRIPTION

+

DESCRIPTION

named-checkzone checks the syntax and integrity of a zone file. It performs the same checks as named does when loading a @@ -71,7 +71,7 @@

-

OPTIONS

+

OPTIONS

-d

@@ -146,14 +146,24 @@ and "raw".

-F format
-

+

+

Specify the format of the output file specified. - Possible formats are "text" (default) - and "raw". For named-checkzone, this does not cause any effects unless it dumps the zone contents. -

+

+

+ Possible formats are "text" (default) + and "raw" or "raw=N", + which store the zone in a binary format for rapid loading + by named. "raw=N" + specifies the format version of the raw zone file: if N + is 0, the raw file can be read by any version of + named; if N is 1, the file can be read + by release 9.9.0 or higher. The default is 1. +

+
-k mode

Perform "check-names" checks with the @@ -164,6 +174,12 @@ (default for named-checkzone) and "ignore".

+
-L serial
+

+ When compiling a zone to 'raw' format, set the "source serial" + value in the header to the specified serial number. (This is + expected to be used primarily for testing purposes.) +

-m mode

Specify whether MX records should be checked to see if they @@ -272,14 +288,14 @@

-

RETURN VALUES

+

RETURN VALUES

named-checkzone returns an exit status of 1 if errors were detected and 0 otherwise.

-

SEE ALSO

+

SEE ALSO

named(8), named-checkconf(8), RFC 1035, @@ -287,7 +303,7 @@

-

AUTHOR

+

AUTHOR

Internet Systems Consortium

diff --git a/contrib/bind9/doc/arm/man.named-journalprint.html b/contrib/bind9/doc/arm/man.named-journalprint.html index 2869ceaf3f6..5c1f3db111f 100644 --- a/contrib/bind9/doc/arm/man.named-journalprint.html +++ b/contrib/bind9/doc/arm/man.named-journalprint.html @@ -50,7 +50,7 @@

named-journalprint {journal}

-

DESCRIPTION

+

DESCRIPTION

named-journalprint prints the contents of a zone journal file in a human-readable @@ -76,7 +76,7 @@

-

SEE ALSO

+

SEE ALSO

named(8), nsupdate(8), @@ -84,7 +84,7 @@

-

AUTHOR

+

AUTHOR

Internet Systems Consortium

diff --git a/contrib/bind9/doc/arm/man.named.html b/contrib/bind9/doc/arm/man.named.html index c7371d2d2a6..02d61353a8c 100644 --- a/contrib/bind9/doc/arm/man.named.html +++ b/contrib/bind9/doc/arm/man.named.html @@ -47,10 +47,10 @@

Synopsis

-

named [-4] [-6] [-c config-file] [-d debug-level] [-E engine-name] [-f] [-g] [-m flag] [-n #cpus] [-p port] [-s] [-S #max-socks] [-t directory] [-u user] [-v] [-V] [-x cache-file]

+

named [-4] [-6] [-c config-file] [-d debug-level] [-E engine-name] [-f] [-g] [-m flag] [-n #cpus] [-p port] [-s] [-S #max-socks] [-t directory] [-U #listeners] [-u user] [-v] [-V] [-x cache-file]

-

DESCRIPTION

+

DESCRIPTION

named is a Domain Name System (DNS) server, part of the BIND 9 distribution from ISC. For more @@ -65,7 +65,7 @@

-

OPTIONS

+

OPTIONS

-4

@@ -196,6 +196,16 @@

+
-U #listeners
+

+ Use #listeners + worker threads to listen for incoming UDP packets on each + address. If not specified, named will + use the number of detected CPUs. If -n + has been set to a higher value than the number of CPUs, + then -U may be increased as high as that + value, but no higher. +

-u user

Setuid @@ -246,7 +256,7 @@

-

SIGNALS

+

SIGNALS

In routine operation, signals should not be used to control the nameserver; rndc should be used @@ -267,7 +277,7 @@

-

CONFIGURATION

+

CONFIGURATION

The named configuration file is too complex to describe in detail here. A complete description is provided @@ -284,7 +294,7 @@

-

FILES

+

FILES

/etc/named.conf

@@ -297,7 +307,7 @@

-

SEE ALSO

+

SEE ALSO

RFC 1033, RFC 1034, RFC 1035, @@ -310,7 +320,7 @@

-

AUTHOR

+

AUTHOR

Internet Systems Consortium

diff --git a/contrib/bind9/doc/arm/man.nsec3hash.html b/contrib/bind9/doc/arm/man.nsec3hash.html index 11df5186384..bdba8a683d2 100644 --- a/contrib/bind9/doc/arm/man.nsec3hash.html +++ b/contrib/bind9/doc/arm/man.nsec3hash.html @@ -48,7 +48,7 @@

nsec3hash {salt} {algorithm} {iterations} {domain}

-

DESCRIPTION

+

DESCRIPTION

nsec3hash generates an NSEC3 hash based on a set of NSEC3 parameters. This can be used to check the validity @@ -56,7 +56,7 @@

-

ARGUMENTS

+

ARGUMENTS

salt

@@ -80,14 +80,14 @@

-

SEE ALSO

+

SEE ALSO

BIND 9 Administrator Reference Manual, RFC 5155.

-

AUTHOR

+

AUTHOR

Internet Systems Consortium

diff --git a/contrib/bind9/doc/arm/man.nsupdate.html b/contrib/bind9/doc/arm/man.nsupdate.html index d7f164ef1fe..6793b42389c 100644 --- a/contrib/bind9/doc/arm/man.nsupdate.html +++ b/contrib/bind9/doc/arm/man.nsupdate.html @@ -50,7 +50,7 @@

nsupdate [-d] [-D] [[-g] | [-o] | [-l] | [-y [hmac:]keyname:secret] | [-k keyfile]] [-t timeout] [-u udptimeout] [-r udpretries] [-R randomdev] [-v] [filename]

-

DESCRIPTION

+

DESCRIPTION

nsupdate is used to submit Dynamic DNS Update requests as defined in RFC 2136 to a name server. @@ -210,7 +210,7 @@

-

INPUT FORMAT

+

INPUT FORMAT

nsupdate reads input from filename @@ -349,7 +349,7 @@ realm is specified the saved realm is cleared.

- prereq nxdomain + [prereq] nxdomain {domain-name}

@@ -357,7 +357,7 @@ domain-name.

- prereq yxdomain + [prereq] yxdomain {domain-name}

@@ -366,7 +366,7 @@ exists (has as at least one resource record, of any type).

- prereq nxrrset + [prereq] nxrrset {domain-name} [class] {type} @@ -382,7 +382,7 @@ is omitted, IN (internet) is assumed.

- prereq yxrrset + [prereq] yxrrset {domain-name} [class] {type} @@ -399,7 +399,7 @@ is omitted, IN (internet) is assumed.

- prereq yxrrset + [prereq] yxrrset {domain-name} [class] {type} @@ -428,7 +428,7 @@ RDATA.

- update delete + [update] del[ete] {domain-name} [ttl] [class] @@ -449,7 +449,7 @@ is ignored, and is only allowed for compatibility.

- update add + [update] add {domain-name} {ttl} [class] @@ -498,7 +498,7 @@

-

EXAMPLES

+

EXAMPLES

The examples below show how nsupdate @@ -552,7 +552,7 @@

-

FILES

+

FILES

/etc/resolv.conf

@@ -575,7 +575,7 @@

-

SEE ALSO

+

SEE ALSO

RFC 2136, RFC 3007, @@ -590,7 +590,7 @@

-

BUGS

+

BUGS

The TSIG key is redundantly stored in two separate files. This is a consequence of nsupdate using the DST library diff --git a/contrib/bind9/doc/arm/man.rndc-confgen.html b/contrib/bind9/doc/arm/man.rndc-confgen.html index 977ec220457..1ad009bffb1 100644 --- a/contrib/bind9/doc/arm/man.rndc-confgen.html +++ b/contrib/bind9/doc/arm/man.rndc-confgen.html @@ -50,7 +50,7 @@

rndc-confgen [-a] [-b keysize] [-c keyfile] [-h] [-k keyname] [-p port] [-r randomfile] [-s address] [-t chrootdir] [-u user]

-

DESCRIPTION

+

DESCRIPTION

rndc-confgen generates configuration files for rndc. It can be used as a @@ -66,7 +66,7 @@

-

OPTIONS

+

OPTIONS

-a
@@ -173,7 +173,7 @@
-

EXAMPLES

+

EXAMPLES

To allow rndc to be used with no manual configuration, run @@ -190,7 +190,7 @@

-

SEE ALSO

+

SEE ALSO

rndc(8), rndc.conf(5), named(8), @@ -198,7 +198,7 @@

-

AUTHOR

+

AUTHOR

Internet Systems Consortium

diff --git a/contrib/bind9/doc/arm/man.rndc.conf.html b/contrib/bind9/doc/arm/man.rndc.conf.html index ebb4e40dd4f..6b9ea5fb7d7 100644 --- a/contrib/bind9/doc/arm/man.rndc.conf.html +++ b/contrib/bind9/doc/arm/man.rndc.conf.html @@ -50,7 +50,7 @@

rndc.conf

-

DESCRIPTION

+

DESCRIPTION

rndc.conf is the configuration file for rndc, the BIND 9 name server control utility. This file has a similar structure and syntax to @@ -135,7 +135,7 @@

-

EXAMPLE

+

EXAMPLE

       options {
         default-server  localhost;
@@ -209,7 +209,7 @@
     

-

NAME SERVER CONFIGURATION

+

NAME SERVER CONFIGURATION

The name server must be configured to accept rndc connections and to recognize the key specified in the rndc.conf @@ -219,7 +219,7 @@

-

SEE ALSO

+

SEE ALSO

rndc(8), rndc-confgen(8), mmencode(1), @@ -227,7 +227,7 @@

-

AUTHOR

+

AUTHOR

Internet Systems Consortium

diff --git a/contrib/bind9/doc/arm/man.rndc.html b/contrib/bind9/doc/arm/man.rndc.html index d433baf5fa0..059f7263ca7 100644 --- a/contrib/bind9/doc/arm/man.rndc.html +++ b/contrib/bind9/doc/arm/man.rndc.html @@ -50,7 +50,7 @@

rndc [-b source-address] [-c config-file] [-k key-file] [-s server] [-p port] [-V] [-y key_id] {command}

-

DESCRIPTION

+

DESCRIPTION

rndc controls the operation of a name server. It supersedes the ndc utility @@ -79,7 +79,7 @@

-

OPTIONS

+

OPTIONS

-b source-address

@@ -151,7 +151,7 @@

-

LIMITATIONS

+

LIMITATIONS

rndc does not yet support all the commands of the BIND 8 ndc utility. @@ -165,7 +165,7 @@

-

SEE ALSO

+

SEE ALSO

rndc.conf(5), rndc-confgen(8), named(8), @@ -175,7 +175,7 @@

-

AUTHOR

+

AUTHOR

Internet Systems Consortium

diff --git a/contrib/bind9/doc/arm/pkcs11.xml b/contrib/bind9/doc/arm/pkcs11.xml index 2eb85cb0204..8a0062f4adb 100644 --- a/contrib/bind9/doc/arm/pkcs11.xml +++ b/contrib/bind9/doc/arm/pkcs11.xml @@ -17,7 +17,7 @@ - PERFORMANCE OF THIS SOFTWARE. --> - + PKCS #11 (Cryptoki) support diff --git a/contrib/bind9/doc/misc/options b/contrib/bind9/doc/misc/options index 9cfc5a600e4..2c9cddde4bb 100644 --- a/contrib/bind9/doc/misc/options +++ b/contrib/bind9/doc/misc/options @@ -67,8 +67,9 @@ options { allow-update { ; ... }; allow-update-forwarding { ; ... }; allow-v6-synthesis { ; ... }; // obsolete - also-notify [ port ] { ( | - ) [ port ]; ... }; + also-notify [ port ] { ( | [ + port ] | [ port ] ) [ key + ]; ... }; alt-transfer-source ( | * ) [ port ( | * ) ]; alt-transfer-source-v6 ( | * ) [ port ( | * ) ]; @@ -115,9 +116,11 @@ options { dnssec-accept-expired ; dnssec-dnskey-kskonly ; dnssec-enable ; + dnssec-loadkeys-interval ; dnssec-lookaside ( trust-anchor | auto | no ); dnssec-must-be-secure ; dnssec-secure-to-insecure ; + dnssec-update-mode ( maintain | no-resign ); dnssec-validation ( yes | no | auto ); dual-stack-servers [ port ] { ( [ port ] | [ port ] | @@ -141,6 +144,7 @@ options { host-statistics ; // not implemented host-statistics-max ; // not implemented hostname ( | none ); + inline-signing ; interface-interval ; ixfr-from-differences ; key-directory ; @@ -160,6 +164,7 @@ options { max-ncache-ttl ; max-refresh-time ; max-retry-time ; + max-rsa-exponent-size ; max-transfer-idle-in ; max-transfer-idle-out ; max-transfer-time-in ; @@ -194,6 +199,7 @@ options { recursion ; recursive-clients ; request-ixfr ; + request-ixfr ; request-nsid ; reserved-sockets ; resolver-query-timeout ; @@ -209,6 +215,7 @@ options { secroots-file ; serial-queries ; // obsolete serial-query-rate ; + serial-update-method ( increment | unixtime ); server-id ( | none | hostname ); session-keyalg ; session-keyfile ( | none ); @@ -247,7 +254,7 @@ options { version ( | none ); zero-no-soa-ttl ; zero-no-soa-ttl-cache ; - zone-statistics ; + zone-statistics ; }; server { @@ -293,8 +300,9 @@ view { allow-update { ; ... }; allow-update-forwarding { ; ... }; allow-v6-synthesis { ; ... }; // obsolete - also-notify [ port ] { ( | - ) [ port ]; ... }; + also-notify [ port ] { ( | [ + port ] | [ port ] ) [ key + ]; ... }; alt-transfer-source ( | * ) [ port ( | * ) ]; alt-transfer-source-v6 ( | * ) [ port ( | * ) ]; @@ -337,9 +345,11 @@ view { dnssec-accept-expired ; dnssec-dnskey-kskonly ; dnssec-enable ; + dnssec-loadkeys-interval ; dnssec-lookaside ( trust-anchor | auto | no ); dnssec-must-be-secure ; dnssec-secure-to-insecure ; + dnssec-update-mode ( maintain | no-resign ); dnssec-validation ( yes | no | auto ); dual-stack-servers [ port ] { ( [ port ] | [ port ] | @@ -354,6 +364,7 @@ view { forward ( first | only ); forwarders [ port ] { ( | ) [ port ]; ... }; + inline-signing ; ixfr-from-differences ; key { algorithm ; @@ -401,6 +412,7 @@ view { queryport-pool-updateinterval ; // obsolete recursion ; request-ixfr ; + request-ixfr ; request-nsid ; resolver-query-timeout ; response-policy { zone [ policy ( given | disabled @@ -412,6 +424,7 @@ view { root-delegation-only [ exclude { ; ... } ]; rrset-order { [ class ] [ type ] [ name ] ; ... }; + serial-update-method ( increment | unixtime ); server { bogus ; edns ; @@ -459,8 +472,9 @@ view { allow-transfer { ; ... }; allow-update { ; ... }; allow-update-forwarding { ; ... }; - also-notify [ port ] { ( | - ) [ port ]; ... }; + also-notify [ port ] { ( | + [ port ] | [ + port ] ) [ key ]; ... }; alt-transfer-source ( | * ) [ port ( | * ) ]; alt-transfer-source-v6 ( | * ) [ port ( @@ -479,11 +493,14 @@ view { delegation-only ; dialup ; dnssec-dnskey-kskonly ; + dnssec-loadkeys-interval ; dnssec-secure-to-insecure ; + dnssec-update-mode ( maintain | no-resign ); file ; forward ( first | only ); forwarders [ port ] { ( | ) [ port ]; ... }; + inline-signing ; ixfr-base ; // obsolete ixfr-from-differences ; ixfr-tmp-file ; // obsolete @@ -515,6 +532,8 @@ view { nsec3-test-zone ; // test only pubkey ; // obsolete + request-ixfr ; + serial-update-method ( increment | unixtime ); server-addresses { ( | ) [ port ]; ... }; server-names { ; ... }; @@ -528,7 +547,7 @@ view { | * ) ]; try-tcp-refresh ; type ( master | slave | stub | static-stub | hint | forward - | delegation-only ); + | delegation-only | redirect ); update-check-ksk ; update-policy ( local | { ( grant | deny ) ( name | subdomain | wildcard | self | selfsub | selfwild | @@ -537,9 +556,9 @@ view { ] ; ... }; use-alt-transfer-source ; zero-no-soa-ttl ; - zone-statistics ; + zone-statistics ; }; - zone-statistics ; + zone-statistics ; }; zone { @@ -549,8 +568,9 @@ zone { allow-transfer { ; ... }; allow-update { ; ... }; allow-update-forwarding { ; ... }; - also-notify [ port ] { ( | - ) [ port ]; ... }; + also-notify [ port ] { ( | [ + port ] | [ port ] ) [ key + ]; ... }; alt-transfer-source ( | * ) [ port ( | * ) ]; alt-transfer-source-v6 ( | * ) [ port ( | * ) ]; @@ -568,11 +588,14 @@ zone { delegation-only ; dialup ; dnssec-dnskey-kskonly ; + dnssec-loadkeys-interval ; dnssec-secure-to-insecure ; + dnssec-update-mode ( maintain | no-resign ); file ; forward ( first | only ); forwarders [ port ] { ( | ) [ port ]; ... }; + inline-signing ; ixfr-base ; // obsolete ixfr-from-differences ; ixfr-tmp-file ; // obsolete @@ -601,6 +624,8 @@ zone { notify-to-soa ; nsec3-test-zone ; // test only pubkey ; // obsolete + request-ixfr ; + serial-update-method ( increment | unixtime ); server-addresses { ( | ) [ port ]; ... }; server-names { ; ... }; @@ -612,7 +637,7 @@ zone { transfer-source-v6 ( | * ) [ port ( | * ) ]; try-tcp-refresh ; type ( master | slave | stub | static-stub | hint | forward | - delegation-only ); + delegation-only | redirect ); update-check-ksk ; update-policy ( local | { ( grant | deny ) ( name | subdomain | wildcard | self | selfsub | selfwild | krb5-self | @@ -620,6 +645,6 @@ zone { | zonesub | external ) [ ] ; ... }; use-alt-transfer-source ; zero-no-soa-ttl ; - zone-statistics ; + zone-statistics ; }; diff --git a/contrib/bind9/lib/bind9/api b/contrib/bind9/lib/bind9/api index 99f8d317f46..a27437f4335 100644 --- a/contrib/bind9/lib/bind9/api +++ b/contrib/bind9/lib/bind9/api @@ -4,6 +4,6 @@ # 9.8: 80-89, 120-129 # 9.9: 90-109 # 9.9-sub: 130-139 -LIBINTERFACE = 80 -LIBREVISION = 8 +LIBINTERFACE = 90 +LIBREVISION = 7 LIBAGE = 0 diff --git a/contrib/bind9/lib/bind9/check.c b/contrib/bind9/lib/bind9/check.c index 7c975c9846a..91f8bff13e9 100644 --- a/contrib/bind9/lib/bind9/check.c +++ b/contrib/bind9/lib/bind9/check.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004-2012 Internet Systems Consortium, Inc. ("ISC") + * Copyright (C) 2004-2013 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 2001-2003 Internet Software Consortium. * * Permission to use, copy, modify, and/or distribute this software for any @@ -732,6 +732,20 @@ check_options(const cfg_obj_t *options, isc_log_t *logctx, isc_mem_t *mctx, } } + obj = NULL; + cfg_map_get(options, "max-rsa-exponent-size", &obj); + if (obj != NULL) { + isc_uint32_t val; + + val = cfg_obj_asuint32(obj); + if (val != 0 && (val < 35 || val > 4096)) { + cfg_obj_log(obj, logctx, ISC_LOG_ERROR, + "max-rsa-exponent-size '%u' is out of " + "range (35..4096)", val); + result = ISC_R_RANGE; + } + } + obj = NULL; cfg_map_get(options, "sig-validity-interval", &obj); if (obj != NULL) { @@ -1247,7 +1261,9 @@ check_update_policy(const cfg_obj_t *policy, isc_log_t *logctx) { #define FORWARDZONE 16 #define DELEGATIONZONE 32 #define STATICSTUBZONE 64 -#define CHECKACL 128 +#define REDIRECTZONE 128 +#define STREDIRECTZONE 0 /* Set to REDIRECTZONE to allow xfr-in. */ +#define CHECKACL 512 typedef struct { const char *name; @@ -1299,74 +1315,76 @@ check_zoneconf(const cfg_obj_t *zconfig, const cfg_obj_t *voptions, const cfg_listelt_t *element; static optionstable options[] = { - { "allow-query", MASTERZONE | SLAVEZONE | STUBZONE | CHECKACL | - STATICSTUBZONE }, + { "allow-query", MASTERZONE | SLAVEZONE | STUBZONE | REDIRECTZONE | + CHECKACL | STATICSTUBZONE }, { "allow-notify", SLAVEZONE | CHECKACL }, { "allow-transfer", MASTERZONE | SLAVEZONE | CHECKACL }, { "notify", MASTERZONE | SLAVEZONE }, { "also-notify", MASTERZONE | SLAVEZONE }, - { "dialup", MASTERZONE | SLAVEZONE | STUBZONE }, + { "dialup", MASTERZONE | SLAVEZONE | STUBZONE | STREDIRECTZONE }, { "delegation-only", HINTZONE | STUBZONE | DELEGATIONZONE }, { "forward", MASTERZONE | SLAVEZONE | STUBZONE | STATICSTUBZONE | FORWARDZONE }, { "forwarders", MASTERZONE | SLAVEZONE | STUBZONE | STATICSTUBZONE | FORWARDZONE }, - { "maintain-ixfr-base", MASTERZONE | SLAVEZONE }, - { "max-ixfr-log-size", MASTERZONE | SLAVEZONE }, + { "maintain-ixfr-base", MASTERZONE | SLAVEZONE | STREDIRECTZONE }, + { "max-ixfr-log-size", MASTERZONE | SLAVEZONE | STREDIRECTZONE }, { "notify-source", MASTERZONE | SLAVEZONE }, { "notify-source-v6", MASTERZONE | SLAVEZONE }, - { "transfer-source", SLAVEZONE | STUBZONE }, - { "transfer-source-v6", SLAVEZONE | STUBZONE }, - { "max-transfer-time-in", SLAVEZONE | STUBZONE }, + { "transfer-source", SLAVEZONE | STUBZONE | STREDIRECTZONE }, + { "transfer-source-v6", SLAVEZONE | STUBZONE | STREDIRECTZONE }, + { "max-transfer-time-in", SLAVEZONE | STUBZONE | STREDIRECTZONE }, { "max-transfer-time-out", MASTERZONE | SLAVEZONE }, - { "max-transfer-idle-in", SLAVEZONE | STUBZONE }, + { "max-transfer-idle-in", SLAVEZONE | STUBZONE | STREDIRECTZONE }, { "max-transfer-idle-out", MASTERZONE | SLAVEZONE }, - { "max-retry-time", SLAVEZONE | STUBZONE }, - { "min-retry-time", SLAVEZONE | STUBZONE }, - { "max-refresh-time", SLAVEZONE | STUBZONE }, - { "min-refresh-time", SLAVEZONE | STUBZONE }, + { "max-retry-time", SLAVEZONE | STUBZONE | STREDIRECTZONE }, + { "min-retry-time", SLAVEZONE | STUBZONE | STREDIRECTZONE }, + { "max-refresh-time", SLAVEZONE | STUBZONE | STREDIRECTZONE }, + { "min-refresh-time", SLAVEZONE | STUBZONE | STREDIRECTZONE }, { "dnssec-secure-to-insecure", MASTERZONE }, - { "sig-validity-interval", MASTERZONE }, - { "sig-re-signing-interval", MASTERZONE }, - { "sig-signing-nodes", MASTERZONE }, - { "sig-signing-type", MASTERZONE }, - { "sig-signing-signatures", MASTERZONE }, + { "sig-re-signing-interval", MASTERZONE | SLAVEZONE }, + { "sig-signing-nodes", MASTERZONE | SLAVEZONE }, + { "sig-signing-signatures", MASTERZONE | SLAVEZONE }, + { "sig-signing-type", MASTERZONE | SLAVEZONE }, + { "sig-validity-interval", MASTERZONE | SLAVEZONE }, + { "signing", MASTERZONE | SLAVEZONE }, { "zone-statistics", MASTERZONE | SLAVEZONE | STUBZONE | - STATICSTUBZONE}, + STATICSTUBZONE | REDIRECTZONE }, { "allow-update", MASTERZONE | CHECKACL }, { "allow-update-forwarding", SLAVEZONE | CHECKACL }, - { "file", MASTERZONE | SLAVEZONE | STUBZONE | HINTZONE }, - { "journal", MASTERZONE | SLAVEZONE }, + { "file", MASTERZONE | SLAVEZONE | STUBZONE | HINTZONE | REDIRECTZONE }, + { "journal", MASTERZONE | SLAVEZONE | STREDIRECTZONE }, { "ixfr-base", MASTERZONE | SLAVEZONE }, { "ixfr-tmp-file", MASTERZONE | SLAVEZONE }, - { "masters", SLAVEZONE | STUBZONE }, + { "masters", SLAVEZONE | STUBZONE | REDIRECTZONE }, { "pubkey", MASTERZONE | SLAVEZONE | STUBZONE }, { "update-policy", MASTERZONE }, - { "database", MASTERZONE | SLAVEZONE | STUBZONE }, - { "key-directory", MASTERZONE }, + { "database", MASTERZONE | SLAVEZONE | STUBZONE | REDIRECTZONE }, + { "key-directory", MASTERZONE | SLAVEZONE }, { "check-wildcard", MASTERZONE }, { "check-mx", MASTERZONE }, { "check-dup-records", MASTERZONE }, { "integrity-check", MASTERZONE }, { "check-mx-cname", MASTERZONE }, { "check-srv-cname", MASTERZONE }, - { "masterfile-format", MASTERZONE | SLAVEZONE | STUBZONE | HINTZONE }, - { "update-check-ksk", MASTERZONE }, - { "dnssec-dnskey-kskonly", MASTERZONE }, - { "auto-dnssec", MASTERZONE }, - { "try-tcp-refresh", SLAVEZONE }, + { "masterfile-format", MASTERZONE | SLAVEZONE | STUBZONE | HINTZONE | + REDIRECTZONE }, + { "update-check-ksk", MASTERZONE | SLAVEZONE }, + { "dnssec-dnskey-kskonly", MASTERZONE | SLAVEZONE }, + { "dnssec-loadkeys-interval", MASTERZONE | SLAVEZONE }, + { "auto-dnssec", MASTERZONE | SLAVEZONE }, + { "try-tcp-refresh", SLAVEZONE | STREDIRECTZONE }, { "server-addresses", STATICSTUBZONE }, { "server-names", STATICSTUBZONE }, }; static optionstable dialups[] = { - { "notify", MASTERZONE | SLAVEZONE }, - { "notify-passive", SLAVEZONE }, - { "refresh", SLAVEZONE | STUBZONE }, - { "passive", SLAVEZONE | STUBZONE }, + { "notify", MASTERZONE | SLAVEZONE | STREDIRECTZONE }, + { "notify-passive", SLAVEZONE | STREDIRECTZONE }, + { "refresh", SLAVEZONE | STUBZONE | STREDIRECTZONE }, + { "passive", SLAVEZONE | STUBZONE | STREDIRECTZONE }, }; - znamestr = cfg_obj_asstring(cfg_tuple_get(zconfig, "name")); zoptions = cfg_tuple_get(zconfig, "options"); @@ -1397,6 +1415,8 @@ check_zoneconf(const cfg_obj_t *zconfig, const cfg_obj_t *voptions, ztype = HINTZONE; else if (strcasecmp(typestr, "delegation-only") == 0) ztype = DELEGATIONZONE; + else if (strcasecmp(typestr, "redirect") == 0) + ztype = REDIRECTZONE; else { cfg_obj_log(obj, logctx, ISC_LOG_ERROR, "zone '%s': invalid type %s", @@ -1404,6 +1424,11 @@ check_zoneconf(const cfg_obj_t *zconfig, const cfg_obj_t *voptions, return (ISC_R_FAILURE); } + if (ztype == REDIRECTZONE && strcmp(znamestr, ".") != 0) { + cfg_obj_log(zconfig, logctx, ISC_LOG_ERROR, + "redirect zones must be called \".\""); + return (ISC_R_FAILURE); + } obj = cfg_tuple_get(zconfig, "class"); if (cfg_obj_isstring(obj)) { isc_textregion_t r; @@ -1445,7 +1470,8 @@ check_zoneconf(const cfg_obj_t *zconfig, const cfg_obj_t *voptions, zname = dns_fixedname_name(&fixedname); dns_name_format(zname, namebuf, sizeof(namebuf)); - tresult = nameexist(zconfig, namebuf, ztype == HINTZONE ? 1 : 2, + tresult = nameexist(zconfig, namebuf, ztype == HINTZONE ? 1 : + ztype == REDIRECTZONE ? 2 : 3, symtab, "zone '%s': already exists " "previous definition: %s:%u", logctx, mctx); if (tresult != ISC_R_SUCCESS) @@ -1497,6 +1523,21 @@ check_zoneconf(const cfg_obj_t *zconfig, const cfg_obj_t *voptions, } + /* + * Master & slave zones must have a "also-notify" field. + */ + if (ztype == MASTERZONE || ztype == SLAVEZONE ) { + obj = NULL; + tresult = cfg_map_get(zoptions, "also-notify", &obj); + if (tresult == ISC_R_SUCCESS) { + isc_uint32_t count; + tresult = validate_masters(obj, config, &count, + logctx, mctx); + if (tresult != ISC_R_SUCCESS && result == ISC_R_SUCCESS) + result = tresult; + } + } + /* * Slave & stub zones must have a "masters" field. */ @@ -1525,10 +1566,10 @@ check_zoneconf(const cfg_obj_t *zconfig, const cfg_obj_t *voptions, /* * Master zones can't have both "allow-update" and "update-policy". */ - if (ztype == MASTERZONE) { + if (ztype == MASTERZONE || ztype == SLAVEZONE) { isc_result_t res1, res2, res3; const char *arg; - isc_boolean_t ddns; + isc_boolean_t ddns = ISC_FALSE, signing = ISC_FALSE; obj = NULL; res1 = cfg_map_get(zoptions, "allow-update", &obj); @@ -1545,16 +1586,23 @@ check_zoneconf(const cfg_obj_t *zconfig, const cfg_obj_t *voptions, result = ISC_R_FAILURE; ddns = ISC_TF(res1 == ISC_R_SUCCESS || res2 == ISC_R_SUCCESS); + obj = NULL; + res1 = cfg_map_get(zoptions, "inline-signing", &obj); + if (res1 == ISC_R_SUCCESS) + signing = cfg_obj_asboolean(obj); + obj = NULL; arg = "off"; res3 = cfg_map_get(zoptions, "auto-dnssec", &obj); if (res3 == ISC_R_SUCCESS) arg = cfg_obj_asstring(obj); - if (strcasecmp(arg, "off") != 0 && !ddns) { + if (strcasecmp(arg, "off") != 0 && !ddns && !signing) { cfg_obj_log(obj, logctx, ISC_LOG_ERROR, - "'auto-dnssec %s;' requires " - "dynamic DNS to be configured in the zone", - arg); + "'auto-dnssec %s;' requires%s " + "inline-signing to be configured for " + "the zone", arg, + (ztype == MASTERZONE) ? + " dynamic DNS or" : ""); result = ISC_R_FAILURE; } if (strcasecmp(arg, "create") == 0) { @@ -1575,6 +1623,33 @@ check_zoneconf(const cfg_obj_t *zconfig, const cfg_obj_t *voptions, 0xff00U, 0xffffU); result = ISC_R_FAILURE; } + + obj = NULL; + res1 = cfg_map_get(zoptions, "dnssec-dnskey-kskonly", &obj); + if (res1 == ISC_R_SUCCESS && ztype == SLAVEZONE && !signing) { + cfg_obj_log(obj, logctx, ISC_LOG_ERROR, + "dnssec-dnskey-kskonly: requires " + "inline-signing when used in slave zone"); + result = ISC_R_FAILURE; + } + + obj = NULL; + res1 = cfg_map_get(zoptions, "dnssec-loadkeys-interval", &obj); + if (res1 == ISC_R_SUCCESS && ztype == SLAVEZONE && !signing) { + cfg_obj_log(obj, logctx, ISC_LOG_ERROR, + "dnssec-loadkeys-interval: requires " + "inline-signing when used in slave zone"); + result = ISC_R_FAILURE; + } + + obj = NULL; + res1 = cfg_map_get(zoptions, "update-check-ksk", &obj); + if (res1 == ISC_R_SUCCESS && ztype == SLAVEZONE && !signing) { + cfg_obj_log(obj, logctx, ISC_LOG_ERROR, + "update-check-ksk: requires " + "inline-signing when used in slave zone"); + result = ISC_R_FAILURE; + } } /* @@ -1710,20 +1785,27 @@ check_zoneconf(const cfg_obj_t *zconfig, const cfg_obj_t *voptions, /* * If the zone type is rbt/rbt64 then master/hint zones * require file clauses. + * If inline signing is used, then slave zones require a + * file clause as well */ obj = NULL; tresult = cfg_map_get(zoptions, "database", &obj); if (tresult == ISC_R_NOTFOUND || (tresult == ISC_R_SUCCESS && (strcmp("rbt", cfg_obj_asstring(obj)) == 0 || - strcmp("rbt64", cfg_obj_asstring(obj)) == 0))) { + strcmp("rbt64", cfg_obj_asstring(obj)) == 0))) + { + isc_result_t res1; obj = NULL; tresult = cfg_map_get(zoptions, "file", &obj); - if (tresult != ISC_R_SUCCESS && - (ztype == MASTERZONE || ztype == HINTZONE)) { + obj = NULL; + res1 = cfg_map_get(zoptions, "inline-signing", &obj); + if ((tresult != ISC_R_SUCCESS && + (ztype == MASTERZONE || ztype == HINTZONE)) || + (ztype == SLAVEZONE && res1 == ISC_R_SUCCESS)) { cfg_obj_log(zconfig, logctx, ISC_LOG_ERROR, - "zone '%s': missing 'file' entry", - znamestr); + "zone '%s': missing 'file' entry", + znamestr); result = tresult; } } diff --git a/contrib/bind9/lib/dns/Makefile.in b/contrib/bind9/lib/dns/Makefile.in index 51d60666dec..b712ab1cd87 100644 --- a/contrib/bind9/lib/dns/Makefile.in +++ b/contrib/bind9/lib/dns/Makefile.in @@ -13,7 +13,7 @@ # OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR # PERFORMANCE OF THIS SOFTWARE. -# $Id$ +# $Id: Makefile.in,v 1.180 2011/10/11 00:09:03 each Exp $ srcdir = @srcdir@ VPATH = @srcdir@ @@ -57,7 +57,7 @@ DSTOBJS = @DST_EXTRA_OBJS@ @OPENSSLLINKOBJS@ \ # Alphabetically DNSOBJS = acache.@O@ acl.@O@ adb.@O@ byaddr.@O@ \ - cache.@O@ callbacks.@O@ compress.@O@ \ + cache.@O@ callbacks.@O@ clientinfo.@O@ compress.@O@ \ db.@O@ dbiterator.@O@ dbtable.@O@ diff.@O@ dispatch.@O@ \ dlz.@O@ dns64.@O@ dnssec.@O@ ds.@O@ forward.@O@ iptable.@O@ \ journal.@O@ keydata.@O@ keytable.@O@ \ @@ -71,7 +71,7 @@ DNSOBJS = acache.@O@ acl.@O@ adb.@O@ byaddr.@O@ \ rriterator.@O@ sdb.@O@ \ sdlz.@O@ soa.@O@ ssu.@O@ ssu_external.@O@ \ stats.@O@ tcpmsg.@O@ time.@O@ timer.@O@ tkey.@O@ \ - tsec.@O@ tsig.@O@ ttl.@O@ validator.@O@ \ + tsec.@O@ tsig.@O@ ttl.@O@ update.@O@ validator.@O@ \ version.@O@ view.@O@ xfrin.@O@ zone.@O@ zonekey.@O@ zt.@O@ OBJS= ${DNSOBJS} ${OTHEROBJS} ${DSTOBJS} @@ -87,7 +87,7 @@ DSTSRCS = @DST_EXTRA_SRCS@ @OPENSSLLINKSRCS@ \ hmac_link.c key.c DNSSRCS = acache.c acl.c adb.c byaddr.c \ - cache.c callbacks.c compress.c \ + cache.c callbacks.c clientinfo.c compress.c \ db.c dbiterator.c dbtable.c diff.c dispatch.c \ dlz.c dns64.c dnssec.c ds.c forward.c iptable.c journal.c \ keydata.c keytable.c lib.c log.c lookup.c \ @@ -98,7 +98,7 @@ DNSSRCS = acache.c acl.c adb.c byaddr.c \ resolver.c result.c rootns.c rpz.c rriterator.c \ sdb.c sdlz.c soa.c ssu.c ssu_external.c \ stats.c tcpmsg.c time.c timer.c tkey.c \ - tsec.c tsig.c ttl.c validator.c \ + tsec.c tsig.c ttl.c update.c validator.c \ version.c view.c xfrin.c zone.c zonekey.c zt.c ${OTHERSRCS} SRCS = ${DSTSRCS} ${DNSSRCS} diff --git a/contrib/bind9/lib/dns/acache.c b/contrib/bind9/lib/dns/acache.c index 863df35535f..6df9b983863 100644 --- a/contrib/bind9/lib/dns/acache.c +++ b/contrib/bind9/lib/dns/acache.c @@ -1781,9 +1781,8 @@ dns_acache_setcleaninginterval(dns_acache_t *acache, unsigned int t) { * function for more details about the logic. */ void -dns_acache_setcachesize(dns_acache_t *acache, isc_uint32_t size) { - isc_uint32_t lowater; - isc_uint32_t hiwater; +dns_acache_setcachesize(dns_acache_t *acache, size_t size) { + size_t hiwater, lowater; REQUIRE(DNS_ACACHE_VALID(acache)); diff --git a/contrib/bind9/lib/dns/acl.c b/contrib/bind9/lib/dns/acl.c index ec29bc7b54c..3221d30c2b5 100644 --- a/contrib/bind9/lib/dns/acl.c +++ b/contrib/bind9/lib/dns/acl.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004-2009, 2011, 2012 Internet Systems Consortium, Inc. ("ISC") + * Copyright (C) 2004-2009, 2011, 2013 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 1999-2002 Internet Software Consortium. * * Permission to use, copy, modify, and/or distribute this software for any @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id$ */ +/* $Id: acl.c,v 1.55 2011/06/17 23:47:49 tbox Exp $ */ /*! \file */ @@ -48,7 +48,10 @@ dns_acl_create(isc_mem_t *mctx, int n, dns_acl_t **target) { acl = isc_mem_get(mctx, sizeof(*acl)); if (acl == NULL) return (ISC_R_NOMEMORY); - acl->mctx = mctx; + + acl->mctx = NULL; + isc_mem_attach(mctx, &acl->mctx); + acl->name = NULL; result = isc_refcount_init(&acl->refcount, 1); @@ -467,7 +470,7 @@ destroy(dns_acl_t *dacl) { dns_iptable_detach(&dacl->iptable); isc_refcount_destroy(&dacl->refcount); dacl->magic = 0; - isc_mem_put(dacl->mctx, dacl, sizeof(*dacl)); + isc_mem_putanddetach(&dacl->mctx, dacl, sizeof(*dacl)); } void diff --git a/contrib/bind9/lib/dns/adb.c b/contrib/bind9/lib/dns/adb.c index 6aa5e5a730d..ef7875dcb46 100644 --- a/contrib/bind9/lib/dns/adb.c +++ b/contrib/bind9/lib/dns/adb.c @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id$ */ +/* $Id: adb.c,v 1.264 2011/12/05 17:10:51 each Exp $ */ /*! \file * @@ -4130,9 +4130,8 @@ water(void *arg, int mark) { } void -dns_adb_setadbsize(dns_adb_t *adb, isc_uint32_t size) { - isc_uint32_t hiwater; - isc_uint32_t lowater; +dns_adb_setadbsize(dns_adb_t *adb, size_t size) { + size_t hiwater, lowater; INSIST(DNS_ADB_VALID(adb)); diff --git a/contrib/bind9/lib/dns/api b/contrib/bind9/lib/dns/api index 5241a88477c..a8881101f4e 100644 --- a/contrib/bind9/lib/dns/api +++ b/contrib/bind9/lib/dns/api @@ -4,6 +4,6 @@ # 9.8: 80-89, 120-129 # 9.9: 90-109 # 9.9-sub: 130-139 -LIBINTERFACE = 122 +LIBINTERFACE = 99 LIBREVISION = 1 LIBAGE = 0 diff --git a/contrib/bind9/lib/dns/byaddr.c b/contrib/bind9/lib/dns/byaddr.c index 6a3a6036180..eb05f9f80f3 100644 --- a/contrib/bind9/lib/dns/byaddr.c +++ b/contrib/bind9/lib/dns/byaddr.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004, 2005, 2007, 2009 Internet Systems Consortium, Inc. ("ISC") + * Copyright (C) 2004, 2005, 2007, 2009, 2013 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 2000-2003 Internet Software Consortium. * * Permission to use, copy, modify, and/or distribute this software for any @@ -224,7 +224,8 @@ dns_byaddr_create(isc_mem_t *mctx, isc_netaddr_t *address, dns_view_t *view, byaddr = isc_mem_get(mctx, sizeof(*byaddr)); if (byaddr == NULL) return (ISC_R_NOMEMORY); - byaddr->mctx = mctx; + byaddr->mctx = NULL; + isc_mem_attach(mctx, &byaddr->mctx); byaddr->options = options; byaddr->event = isc_mem_get(mctx, sizeof(*byaddr->event)); @@ -277,7 +278,7 @@ dns_byaddr_create(isc_mem_t *mctx, isc_netaddr_t *address, dns_view_t *view, isc_task_detach(&byaddr->task); cleanup_byaddr: - isc_mem_put(mctx, byaddr, sizeof(*byaddr)); + isc_mem_putanddetach(&mctx, byaddr, sizeof(*byaddr)); return (result); } @@ -310,7 +311,7 @@ dns_byaddr_destroy(dns_byaddr_t **byaddrp) { DESTROYLOCK(&byaddr->lock); byaddr->magic = 0; - isc_mem_put(byaddr->mctx, byaddr, sizeof(*byaddr)); + isc_mem_putanddetach(&byaddr->mctx, byaddr, sizeof(*byaddr)); *byaddrp = NULL; } diff --git a/contrib/bind9/lib/dns/cache.c b/contrib/bind9/lib/dns/cache.c index bced80e6e5a..d0f05b9a09a 100644 --- a/contrib/bind9/lib/dns/cache.c +++ b/contrib/bind9/lib/dns/cache.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004-2009, 2011-2013 Internet Systems Consortium, Inc. ("ISC") + * Copyright (C) 2004-2009, 2011, 2013 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 1999-2003 Internet Software Consortium. * * Permission to use, copy, modify, and/or distribute this software for any @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id$ */ +/* $Id: cache.c,v 1.91 2011/08/26 05:12:56 marka Exp $ */ /*! \file */ @@ -136,7 +136,7 @@ struct dns_cache { char *db_type; int db_argc; char **db_argv; - isc_uint32_t size; + size_t size; /* Locked by 'filelock'. */ char *filename; @@ -1028,9 +1028,8 @@ water(void *arg, int mark) { } void -dns_cache_setcachesize(dns_cache_t *cache, isc_uint32_t size) { - isc_uint32_t lowater; - isc_uint32_t hiwater; +dns_cache_setcachesize(dns_cache_t *cache, size_t size) { + size_t hiwater, lowater; REQUIRE(VALID_CACHE(cache)); @@ -1068,9 +1067,9 @@ dns_cache_setcachesize(dns_cache_t *cache, isc_uint32_t size) { isc_mem_setwater(cache->mctx, water, cache, hiwater, lowater); } -isc_uint32_t +size_t dns_cache_getcachesize(dns_cache_t *cache) { - isc_uint32_t size; + size_t size; REQUIRE(VALID_CACHE(cache)); @@ -1153,31 +1152,14 @@ dns_cache_flush(dns_cache_t *cache) { return (ISC_R_SUCCESS); } -isc_result_t -dns_cache_flushname(dns_cache_t *cache, dns_name_t *name) { +static isc_result_t +clearnode(dns_db_t *db, dns_dbnode_t *node) { isc_result_t result; dns_rdatasetiter_t *iter = NULL; - dns_dbnode_t *node = NULL; - dns_db_t *db = NULL; - LOCK(&cache->lock); - if (cache->db != NULL) - dns_db_attach(cache->db, &db); - UNLOCK(&cache->lock); - if (db == NULL) - return (ISC_R_SUCCESS); - result = dns_db_findnode(cache->db, name, ISC_FALSE, &node); - if (result == ISC_R_NOTFOUND) { - result = ISC_R_SUCCESS; - goto cleanup_db; - } + result = dns_db_allrdatasets(db, node, NULL, (isc_stdtime_t)0, &iter); if (result != ISC_R_SUCCESS) - goto cleanup_db; - - result = dns_db_allrdatasets(cache->db, node, NULL, - (isc_stdtime_t)0, &iter); - if (result != ISC_R_SUCCESS) - goto cleanup_node; + return (result); for (result = dns_rdatasetiter_first(iter); result == ISC_R_SUCCESS; @@ -1187,19 +1169,110 @@ dns_cache_flushname(dns_cache_t *cache, dns_name_t *name) { dns_rdataset_init(&rdataset); dns_rdatasetiter_current(iter, &rdataset); - result = dns_db_deleterdataset(cache->db, node, NULL, + result = dns_db_deleterdataset(db, node, NULL, rdataset.type, rdataset.covers); dns_rdataset_disassociate(&rdataset); if (result != ISC_R_SUCCESS && result != DNS_R_UNCHANGED) break; } + if (result == ISC_R_NOMORE) result = ISC_R_SUCCESS; dns_rdatasetiter_destroy(&iter); + return (result); +} - cleanup_node: - dns_db_detachnode(cache->db, &node); +static isc_result_t +cleartree(dns_db_t *db, dns_name_t *name) { + isc_result_t result, answer = ISC_R_SUCCESS; + dns_dbiterator_t *iter = NULL; + dns_dbnode_t *node = NULL; + dns_fixedname_t fnodename; + dns_name_t *nodename; + + dns_fixedname_init(&fnodename); + nodename = dns_fixedname_name(&fnodename); + + result = dns_db_createiterator(db, 0, &iter); + if (result != ISC_R_SUCCESS) + goto cleanup; + + result = dns_dbiterator_seek(iter, name); + if (result != ISC_R_SUCCESS) + goto cleanup; + + while (result == ISC_R_SUCCESS) { + result = dns_dbiterator_current(iter, &node, nodename); + if (result == DNS_R_NEWORIGIN) + result = ISC_R_SUCCESS; + if (result != ISC_R_SUCCESS) + goto cleanup; + /* + * Are we done? + */ + if (! dns_name_issubdomain(nodename, name)) + goto cleanup; + + /* + * If clearnode fails record and move onto the next node. + */ + result = clearnode(db, node); + if (result != ISC_R_SUCCESS && answer == ISC_R_SUCCESS) + answer = result; + dns_db_detachnode(db, &node); + result = dns_dbiterator_next(iter); + } + + cleanup: + if (result == ISC_R_NOMORE || result == ISC_R_NOTFOUND) + result = ISC_R_SUCCESS; + if (result != ISC_R_SUCCESS && answer == ISC_R_SUCCESS) + answer = result; + if (node != NULL) + dns_db_detachnode(db, &node); + if (iter != NULL) + dns_dbiterator_destroy(&iter); + + return (answer); +} + +isc_result_t +dns_cache_flushname(dns_cache_t *cache, dns_name_t *name) { + return (dns_cache_flushnode(cache, name, ISC_FALSE)); +} + +isc_result_t +dns_cache_flushnode(dns_cache_t *cache, dns_name_t *name, + isc_boolean_t tree) +{ + isc_result_t result; + dns_dbnode_t *node = NULL; + dns_db_t *db = NULL; + + if (dns_name_equal(name, dns_rootname)) + return (dns_cache_flush(cache)); + + LOCK(&cache->lock); + if (cache->db != NULL) + dns_db_attach(cache->db, &db); + UNLOCK(&cache->lock); + if (db == NULL) + return (ISC_R_SUCCESS); + + if (tree) { + result = cleartree(cache->db, name); + } else { + result = dns_db_findnode(cache->db, name, ISC_FALSE, &node); + if (result == ISC_R_NOTFOUND) { + result = ISC_R_SUCCESS; + goto cleanup_db; + } + if (result != ISC_R_SUCCESS) + goto cleanup_db; + result = clearnode(cache->db, node); + dns_db_detachnode(cache->db, &node); + } cleanup_db: dns_db_detach(&db); diff --git a/contrib/bind9/lib/dns/callbacks.c b/contrib/bind9/lib/dns/callbacks.c index 705b6f10a8d..0ef17abce7b 100644 --- a/contrib/bind9/lib/dns/callbacks.c +++ b/contrib/bind9/lib/dns/callbacks.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004, 2005, 2007, 2012 Internet Systems Consortium, Inc. ("ISC") + * Copyright (C) 2004, 2005, 2007, 2011 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 1999-2001 Internet Software Consortium. * * Permission to use, copy, modify, and/or distribute this software for any @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id$ */ +/* $Id: callbacks.c,v 1.19 2011/12/09 23:47:05 tbox Exp $ */ /*! \file */ @@ -88,6 +88,8 @@ dns_rdatacallbacks_initcommon(dns_rdatacallbacks_t *callbacks) { REQUIRE(callbacks != NULL); callbacks->add = NULL; + callbacks->rawdata = NULL; + callbacks->zone = NULL; callbacks->add_private = NULL; callbacks->error_private = NULL; callbacks->warn_private = NULL; diff --git a/contrib/bind9/lib/dns/client.c b/contrib/bind9/lib/dns/client.c index c4780f7bb01..fc551cf9dfe 100644 --- a/contrib/bind9/lib/dns/client.c +++ b/contrib/bind9/lib/dns/client.c @@ -14,7 +14,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id$ */ +/* $Id: client.c,v 1.14 2011/03/12 04:59:47 tbox Exp $ */ #include @@ -318,7 +318,7 @@ dns_client_createview(isc_mem_t *mctx, dns_rdataclass_t rdclass, return (result); } - result = dns_view_createresolver(view, taskmgr, ntasks, socketmgr, + result = dns_view_createresolver(view, taskmgr, ntasks, 1, socketmgr, timermgr, 0, dispatchmgr, dispatchv4, dispatchv6); if (result != ISC_R_SUCCESS) { diff --git a/contrib/bind9/lib/dns/clientinfo.c b/contrib/bind9/lib/dns/clientinfo.c new file mode 100644 index 00000000000..fd5a5e28164 --- /dev/null +++ b/contrib/bind9/lib/dns/clientinfo.c @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC") + * + * Permission to use, copy, modify, and/or 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 ISC DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL ISC 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. + */ + +/* $Id: clientinfo.c,v 1.3 2011/10/11 00:25:12 marka Exp $ */ + +/*! \file */ + +#include "config.h" + +#include + +void +dns_clientinfomethods_init(dns_clientinfomethods_t *methods, + dns_clientinfo_sourceip_t sourceip) +{ + methods->version = DNS_CLIENTINFOMETHODS_VERSION; + methods->age = DNS_CLIENTINFOMETHODS_AGE; + methods->sourceip = sourceip; +} + +void +dns_clientinfo_init(dns_clientinfo_t *ci, void *data) { + ci->version = DNS_CLIENTINFO_VERSION; + ci->data = data; +} diff --git a/contrib/bind9/lib/dns/db.c b/contrib/bind9/lib/dns/db.c index 77f82f15abf..bf4a5b37540 100644 --- a/contrib/bind9/lib/dns/db.c +++ b/contrib/bind9/lib/dns/db.c @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id$ */ +/* $Id: db.c,v 1.99.4.1 2011/10/23 20:12:07 vjs Exp $ */ /*! \file */ @@ -33,6 +33,7 @@ #include #include +#include #include #include #include @@ -478,7 +479,31 @@ dns_db_findnode(dns_db_t *db, dns_name_t *name, REQUIRE(DNS_DB_VALID(db)); REQUIRE(nodep != NULL && *nodep == NULL); - return ((db->methods->findnode)(db, name, create, nodep)); + if (db->methods->findnode != NULL) + return ((db->methods->findnode)(db, name, create, nodep)); + else + return ((db->methods->findnodeext)(db, name, create, + NULL, NULL, nodep)); +} + +isc_result_t +dns_db_findnodeext(dns_db_t *db, dns_name_t *name, + isc_boolean_t create, dns_clientinfomethods_t *methods, + dns_clientinfo_t *clientinfo, dns_dbnode_t **nodep) +{ + /* + * Find the node with name 'name', passing 'arg' to the database + * implementation. + */ + + REQUIRE(DNS_DB_VALID(db)); + REQUIRE(nodep != NULL && *nodep == NULL); + + if (db->methods->findnodeext != NULL) + return ((db->methods->findnodeext)(db, name, create, + methods, clientinfo, nodep)); + else + return ((db->methods->findnode)(db, name, create, nodep)); } isc_result_t @@ -502,7 +527,6 @@ dns_db_find(dns_db_t *db, dns_name_t *name, dns_dbversion_t *version, dns_dbnode_t **nodep, dns_name_t *foundname, dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset) { - /* * Find the best match for 'name' and 'type' in version 'version' * of 'db'. @@ -519,8 +543,50 @@ dns_db_find(dns_db_t *db, dns_name_t *name, dns_dbversion_t *version, (DNS_RDATASET_VALID(sigrdataset) && ! dns_rdataset_isassociated(sigrdataset))); - return ((db->methods->find)(db, name, version, type, options, now, - nodep, foundname, rdataset, sigrdataset)); + if (db->methods->find != NULL) + return ((db->methods->find)(db, name, version, type, + options, now, nodep, foundname, + rdataset, sigrdataset)); + else + return ((db->methods->findext)(db, name, version, type, + options, now, nodep, foundname, + NULL, NULL, + rdataset, sigrdataset)); +} + +isc_result_t +dns_db_findext(dns_db_t *db, dns_name_t *name, dns_dbversion_t *version, + dns_rdatatype_t type, unsigned int options, isc_stdtime_t now, + dns_dbnode_t **nodep, dns_name_t *foundname, + dns_clientinfomethods_t *methods, dns_clientinfo_t *clientinfo, + dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset) +{ + + /* + * Find the best match for 'name' and 'type' in version 'version' + * of 'db', passing in 'arg'. + */ + + REQUIRE(DNS_DB_VALID(db)); + REQUIRE(type != dns_rdatatype_rrsig); + REQUIRE(nodep == NULL || (nodep != NULL && *nodep == NULL)); + REQUIRE(dns_name_hasbuffer(foundname)); + REQUIRE(rdataset == NULL || + (DNS_RDATASET_VALID(rdataset) && + ! dns_rdataset_isassociated(rdataset))); + REQUIRE(sigrdataset == NULL || + (DNS_RDATASET_VALID(sigrdataset) && + ! dns_rdataset_isassociated(sigrdataset))); + + if (db->methods->findext != NULL) + return ((db->methods->findext)(db, name, version, type, + options, now, nodep, foundname, + methods, clientinfo, + rdataset, sigrdataset)); + else + return ((db->methods->find)(db, name, version, type, + options, now, nodep, foundname, + rdataset, sigrdataset)); } isc_result_t @@ -653,11 +719,6 @@ dns_db_findrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version, isc_stdtime_t now, dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset) { - /* - * Search for an rdataset of type 'type' at 'node' that are in version - * 'version' of 'db'. If found, make 'rdataset' refer to it. - */ - REQUIRE(DNS_DB_VALID(db)); REQUIRE(node != NULL); REQUIRE(DNS_RDATASET_VALID(rdataset)); @@ -668,8 +729,9 @@ dns_db_findrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version, (DNS_RDATASET_VALID(sigrdataset) && ! dns_rdataset_isassociated(sigrdataset))); - return ((db->methods->findrdataset)(db, node, version, type, covers, - now, rdataset, sigrdataset)); + return ((db->methods->findrdataset)(db, node, version, type, + covers, now, rdataset, + sigrdataset)); } isc_result_t diff --git a/contrib/bind9/lib/dns/dbtable.c b/contrib/bind9/lib/dns/dbtable.c index 57bbfc1ef81..20092209d58 100644 --- a/contrib/bind9/lib/dns/dbtable.c +++ b/contrib/bind9/lib/dns/dbtable.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004, 2005, 2007 Internet Systems Consortium, Inc. ("ISC") + * Copyright (C) 2004, 2005, 2007, 2013 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 1999-2001 Internet Software Consortium. * * Permission to use, copy, modify, and/or distribute this software for any @@ -89,7 +89,8 @@ dns_dbtable_create(isc_mem_t *mctx, dns_rdataclass_t rdclass, goto clean3; dbtable->default_db = NULL; - dbtable->mctx = mctx; + dbtable->mctx = NULL; + isc_mem_attach(mctx, &dbtable->mctx); dbtable->rdclass = rdclass; dbtable->magic = DBTABLE_MAGIC; dbtable->references = 1; @@ -105,7 +106,7 @@ dns_dbtable_create(isc_mem_t *mctx, dns_rdataclass_t rdclass, dns_rbt_destroy(&dbtable->rbt); clean1: - isc_mem_put(mctx, dbtable, sizeof(*dbtable)); + isc_mem_putanddetach(&mctx, dbtable, sizeof(*dbtable)); return (result); } @@ -129,7 +130,7 @@ dbtable_free(dns_dbtable_t *dbtable) { dbtable->magic = 0; - isc_mem_put(dbtable->mctx, dbtable, sizeof(*dbtable)); + isc_mem_putanddetach(&dbtable->mctx, dbtable, sizeof(*dbtable)); } void diff --git a/contrib/bind9/lib/dns/diff.c b/contrib/bind9/lib/dns/diff.c index de00d0f9566..ff60d462f37 100644 --- a/contrib/bind9/lib/dns/diff.c +++ b/contrib/bind9/lib/dns/diff.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004, 2005, 2007-2009, 2011, 2012 Internet Systems Consortium, Inc. ("ISC") + * Copyright (C) 2004, 2005, 2007-2009, 2011, 2013 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 2000-2003 Internet Software Consortium. * * Permission to use, copy, modify, and/or distribute this software for any @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id$ */ +/* $Id: diff.c,v 1.26 2011/03/25 23:53:02 each Exp $ */ /*! \file */ @@ -73,7 +73,8 @@ dns_difftuple_create(isc_mem_t *mctx, t = isc_mem_allocate(mctx, size); if (t == NULL) return (ISC_R_NOMEMORY); - t->mctx = mctx; + t->mctx = NULL; + isc_mem_attach(mctx, &t->mctx); t->op = op; datap = (unsigned char *)(t + 1); @@ -105,10 +106,15 @@ dns_difftuple_create(isc_mem_t *mctx, void dns_difftuple_free(dns_difftuple_t **tp) { dns_difftuple_t *t = *tp; + isc_mem_t *mctx; + REQUIRE(DNS_DIFFTUPLE_VALID(t)); + dns_name_invalidate(&t->name); t->magic = 0; - isc_mem_free(t->mctx, t); + mctx = t->mctx; + isc_mem_free(mctx, t); + isc_mem_detach(&mctx); *tp = NULL; } diff --git a/contrib/bind9/lib/dns/dispatch.c b/contrib/bind9/lib/dns/dispatch.c index 9848ac28a64..5063914a9b2 100644 --- a/contrib/bind9/lib/dns/dispatch.c +++ b/contrib/bind9/lib/dns/dispatch.c @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id$ */ +/* $Id: dispatch.c,v 1.175 2011/11/29 01:03:47 marka Exp $ */ /*! \file */ @@ -32,6 +32,7 @@ #include #include #include +#include #include #include #include @@ -101,12 +102,16 @@ struct dns_dispatchmgr { unsigned int maxbuffers; /*%< max buffers */ /* Locked internally. */ - isc_mutex_t pool_lock; - isc_mempool_t *epool; /*%< memory pool for events */ - isc_mempool_t *rpool; /*%< memory pool for replies */ + isc_mutex_t depool_lock; + isc_mempool_t *depool; /*%< pool for dispatch events */ + isc_mutex_t rpool_lock; + isc_mempool_t *rpool; /*%< pool for replies */ + isc_mutex_t dpool_lock; isc_mempool_t *dpool; /*%< dispatch allocations */ - isc_mempool_t *bpool; /*%< memory pool for buffers */ - isc_mempool_t *spool; /*%< memory pool for dispsocs */ + isc_mutex_t bpool_lock; + isc_mempool_t *bpool; /*%< pool for buffers */ + isc_mutex_t spool_lock; + isc_mempool_t *spool; /*%< pool for dispsocks */ /*% * Locked by qid->lock if qid exists; otherwise, can be used without @@ -226,6 +231,9 @@ struct dns_dispatch { unsigned int maxrequests; /*%< max requests */ isc_event_t *ctlevent; + isc_mutex_t sepool_lock; + isc_mempool_t *sepool; /*%< pool for socket events */ + /*% Locked by mgr->lock. */ ISC_LINK(dns_dispatch_t) link; @@ -301,8 +309,8 @@ static isc_uint32_t dns_hash(dns_qid_t *, isc_sockaddr_t *, dns_messageid_t, in_port_t); static void free_buffer(dns_dispatch_t *disp, void *buf, unsigned int len); static void *allocate_udp_buffer(dns_dispatch_t *disp); -static inline void free_event(dns_dispatch_t *disp, dns_dispatchevent_t *ev); -static inline dns_dispatchevent_t *allocate_event(dns_dispatch_t *disp); +static inline void free_devent(dns_dispatch_t *disp, dns_dispatchevent_t *ev); +static inline dns_dispatchevent_t *allocate_devent(dns_dispatch_t *disp); static void do_cancel(dns_dispatch_t *disp); static dns_dispentry_t *linear_first(dns_qid_t *disp); static dns_dispentry_t *linear_next(dns_qid_t *disp, @@ -312,14 +320,16 @@ static isc_result_t get_udpsocket(dns_dispatchmgr_t *mgr, dns_dispatch_t *disp, isc_socketmgr_t *sockmgr, isc_sockaddr_t *localaddr, - isc_socket_t **sockp); + isc_socket_t **sockp, + isc_socket_t *dup_socket); static isc_result_t dispatch_createudp(dns_dispatchmgr_t *mgr, isc_socketmgr_t *sockmgr, isc_taskmgr_t *taskmgr, isc_sockaddr_t *localaddr, unsigned int maxrequests, unsigned int attributes, - dns_dispatch_t **dispp); + dns_dispatch_t **dispp, + isc_socket_t *dup_socket); static isc_boolean_t destroy_mgr_ok(dns_dispatchmgr_t *mgr); static void destroy_mgr(dns_dispatchmgr_t **mgrp); static isc_result_t qid_allocate(dns_dispatchmgr_t *mgr, unsigned int buckets, @@ -327,7 +337,8 @@ static isc_result_t qid_allocate(dns_dispatchmgr_t *mgr, unsigned int buckets, isc_boolean_t needaddrtable); static void qid_destroy(isc_mem_t *mctx, dns_qid_t **qidp); static isc_result_t open_socket(isc_socketmgr_t *mgr, isc_sockaddr_t *local, - unsigned int options, isc_socket_t **sockp); + unsigned int options, isc_socket_t **sockp, + isc_socket_t *dup_socket); static isc_boolean_t portavailable(dns_dispatchmgr_t *mgr, isc_socket_t *sock, isc_sockaddr_t *sockaddrp); @@ -720,6 +731,11 @@ destroy_disp(isc_task_t *task, isc_event_t *event) { "shutting down; detaching from sock %p, task %p", disp->socket, disp->task[0]); /* XXXX */ + if (disp->sepool != NULL) { + isc_mempool_destroy(&disp->sepool); + (void)isc_mutex_destroy(&disp->sepool_lock); + } + if (disp->socket != NULL) isc_socket_detach(&disp->socket); while ((dispsocket = ISC_LIST_HEAD(disp->inactivesockets)) != NULL) { @@ -784,6 +800,7 @@ new_portentry(dns_dispatch_t *disp, in_port_t port) { static void deref_portentry(dns_dispatch_t *disp, dispportentry_t **portentryp) { dispportentry_t *portentry = *portentryp; + isc_boolean_t unlink = ISC_FALSE; dns_qid_t *qid; REQUIRE(disp->port_table != NULL); @@ -792,7 +809,10 @@ deref_portentry(dns_dispatch_t *disp, dispportentry_t **portentryp) { qid = DNS_QID(disp); LOCK(&qid->lock); portentry->refs--; - if (portentry->refs == 0) { + unlink = ISC_TF(portentry->refs == 0); + UNLOCK(&qid->lock); + + if (unlink) { ISC_LIST_UNLINK(disp->port_table[portentry->port % DNS_DISPATCH_PORTTABLESIZE], portentry, link); @@ -800,7 +820,6 @@ deref_portentry(dns_dispatch_t *disp, dispportentry_t **portentryp) { } *portentryp = NULL; - UNLOCK(&qid->lock); } /*% @@ -830,12 +849,12 @@ socket_search(dns_qid_t *qid, isc_sockaddr_t *dest, in_port_t port, /*% * Make a new socket for a single dispatch with a random port number. - * The caller must hold the disp->lock and qid->lock. + * The caller must hold the disp->lock */ static isc_result_t get_dispsocket(dns_dispatch_t *disp, isc_sockaddr_t *dest, - isc_socketmgr_t *sockmgr, dns_qid_t *qid, - dispsocket_t **dispsockp, in_port_t *portp) + isc_socketmgr_t *sockmgr, dispsocket_t **dispsockp, + in_port_t *portp) { int i; isc_uint32_t r; @@ -850,6 +869,7 @@ get_dispsocket(dns_dispatch_t *disp, isc_sockaddr_t *dest, in_port_t *ports; unsigned int bindoptions; dispportentry_t *portentry = NULL; + dns_qid_t *qid; if (isc_sockaddr_pf(&disp->local) == AF_INET) { nports = disp->mgr->nv4ports; @@ -890,19 +910,27 @@ get_dispsocket(dns_dispatch_t *disp, isc_sockaddr_t *dest, * very likely to fail in bind(2) or connect(2). */ localaddr = disp->local; + qid = DNS_QID(disp); + for (i = 0; i < 64; i++) { port = ports[dispatch_uniformrandom(DISP_ARC4CTX(disp), nports)]; isc_sockaddr_setport(&localaddr, port); + LOCK(&qid->lock); bucket = dns_hash(qid, dest, 0, port); - if (socket_search(qid, dest, port, bucket) != NULL) + if (socket_search(qid, dest, port, bucket) != NULL) { + UNLOCK(&qid->lock); continue; + } + UNLOCK(&qid->lock); bindoptions = 0; portentry = port_search(disp, port); + if (portentry != NULL) bindoptions |= ISC_SOCKET_REUSEADDRESS; - result = open_socket(sockmgr, &localaddr, bindoptions, &sock); + result = open_socket(sockmgr, &localaddr, bindoptions, &sock, + NULL); if (result == ISC_R_SUCCESS) { if (portentry == NULL) { portentry = new_portentry(disp, port); @@ -928,7 +956,9 @@ get_dispsocket(dns_dispatch_t *disp, isc_sockaddr_t *dest, dispsock->host = *dest; dispsock->portentry = portentry; dispsock->bucket = bucket; + LOCK(&qid->lock); ISC_LIST_APPEND(qid->sock_table[bucket], dispsock, blink); + UNLOCK(&qid->lock); *dispsockp = dispsock; *portp = port; } else { @@ -1063,6 +1093,7 @@ entry_search(dns_qid_t *qid, isc_sockaddr_t *dest, dns_messageid_t id, static void free_buffer(dns_dispatch_t *disp, void *buf, unsigned int len) { + isc_mempool_t *bpool; INSIST(buf != NULL && len != 0); @@ -1077,8 +1108,9 @@ free_buffer(dns_dispatch_t *disp, void *buf, unsigned int len) { INSIST(disp->mgr->buffers > 0); INSIST(len == disp->mgr->buffersize); disp->mgr->buffers--; - isc_mempool_put(disp->mgr->bpool, buf); + bpool = disp->mgr->bpool; UNLOCK(&disp->mgr->buffer_lock); + isc_mempool_put(bpool, buf); break; default: INSIST(0); @@ -1088,20 +1120,60 @@ free_buffer(dns_dispatch_t *disp, void *buf, unsigned int len) { static void * allocate_udp_buffer(dns_dispatch_t *disp) { + isc_mempool_t *bpool; void *temp; LOCK(&disp->mgr->buffer_lock); - temp = isc_mempool_get(disp->mgr->bpool); - - if (temp != NULL) - disp->mgr->buffers++; + bpool = disp->mgr->bpool; + disp->mgr->buffers++; UNLOCK(&disp->mgr->buffer_lock); + temp = isc_mempool_get(bpool); + + if (temp == NULL) { + LOCK(&disp->mgr->buffer_lock); + disp->mgr->buffers--; + UNLOCK(&disp->mgr->buffer_lock); + } + return (temp); } static inline void -free_event(dns_dispatch_t *disp, dns_dispatchevent_t *ev) { +free_sevent(isc_event_t *ev) { + isc_mempool_t *pool = ev->ev_destroy_arg; + isc_socketevent_t *sev = (isc_socketevent_t *) ev; + isc_mempool_put(pool, sev); +} + +static inline isc_socketevent_t * +allocate_sevent(dns_dispatch_t *disp, isc_socket_t *socket, + isc_eventtype_t type, isc_taskaction_t action, const void *arg) +{ + isc_socketevent_t *ev; + void *deconst_arg; + + ev = isc_mempool_get(disp->sepool); + if (ev == NULL) + return (NULL); + DE_CONST(arg, deconst_arg); + ISC_EVENT_INIT(ev, sizeof(*ev), 0, NULL, type, + action, deconst_arg, socket, + free_sevent, disp->sepool); + ev->result = ISC_R_UNSET; + ISC_LINK_INIT(ev, ev_link); + ISC_LIST_INIT(ev->bufferlist); + ev->region.base = NULL; + ev->n = 0; + ev->offset = 0; + ev->attributes = 0; + + return (ev); +} + + +static inline void +free_devent(dns_dispatch_t *disp, dns_dispatchevent_t *ev) { if (disp->failsafe_ev == ev) { INSIST(disp->shutdown_out == 1); disp->shutdown_out = 0; @@ -1109,14 +1181,14 @@ free_event(dns_dispatch_t *disp, dns_dispatchevent_t *ev) { return; } - isc_mempool_put(disp->mgr->epool, ev); + isc_mempool_put(disp->mgr->depool, ev); } static inline dns_dispatchevent_t * -allocate_event(dns_dispatch_t *disp) { +allocate_devent(dns_dispatch_t *disp) { dns_dispatchevent_t *ev; - ev = isc_mempool_get(disp->mgr->epool); + ev = isc_mempool_get(disp->mgr->depool); if (ev == NULL) return (NULL); ISC_EVENT_INIT(ev, sizeof(*ev), 0, NULL, 0, @@ -1381,7 +1453,7 @@ udp_recv(isc_event_t *ev_in, dns_dispatch_t *disp, dispsocket_t *dispsock) { sendresponse: queue_response = resp->item_out; - rev = allocate_event(resp->disp); + rev = allocate_devent(resp->disp); if (rev == NULL) { free_buffer(disp, ev->region.base, ev->region.length); goto unlock; @@ -1579,7 +1651,7 @@ tcp_recv(isc_task_t *task, isc_event_t *ev_in) { if (resp == NULL) goto unlock; queue_response = resp->item_out; - rev = allocate_event(disp); + rev = allocate_devent(disp); if (rev == NULL) goto unlock; @@ -1660,16 +1732,33 @@ startrecv(dns_dispatch_t *disp, dispsocket_t *dispsock) { if (region.base == NULL) return (ISC_R_NOMEMORY); if (dispsock != NULL) { - res = isc_socket_recv(socket, ®ion, 1, - dispsock->task, udp_exrecv, - dispsock); + isc_task_t *dt = dispsock->task; + isc_socketevent_t *sev = + allocate_sevent(disp, socket, + ISC_SOCKEVENT_RECVDONE, + udp_exrecv, dispsock); + if (sev == NULL) { + free_buffer(disp, region.base, region.length); + return (ISC_R_NOMEMORY); + } + + res = isc_socket_recv2(socket, ®ion, 1, dt, sev, 0); if (res != ISC_R_SUCCESS) { free_buffer(disp, region.base, region.length); return (res); } } else { - res = isc_socket_recv(socket, ®ion, 1, - disp->task[0], udp_shrecv, disp); + isc_task_t *dt = disp->task[0]; + isc_socketevent_t *sev = + allocate_sevent(disp, socket, + ISC_SOCKEVENT_RECVDONE, + udp_shrecv, disp); + if (sev == NULL) { + free_buffer(disp, region.base, region.length); + return (ISC_R_NOMEMORY); + } + + res = isc_socket_recv2(socket, ®ion, 1, dt, sev, 0); if (res != ISC_R_SUCCESS) { free_buffer(disp, region.base, region.length); disp->shutdown_why = res; @@ -1709,16 +1798,16 @@ static isc_boolean_t destroy_mgr_ok(dns_dispatchmgr_t *mgr) { mgr_log(mgr, LVL(90), "destroy_mgr_ok: shuttingdown=%d, listnonempty=%d, " - "epool=%d, rpool=%d, dpool=%d", + "depool=%d, rpool=%d, dpool=%d", MGR_IS_SHUTTINGDOWN(mgr), !ISC_LIST_EMPTY(mgr->list), - isc_mempool_getallocated(mgr->epool), + isc_mempool_getallocated(mgr->depool), isc_mempool_getallocated(mgr->rpool), isc_mempool_getallocated(mgr->dpool)); if (!MGR_IS_SHUTTINGDOWN(mgr)) return (ISC_FALSE); if (!ISC_LIST_EMPTY(mgr->list)) return (ISC_FALSE); - if (isc_mempool_getallocated(mgr->epool) != 0) + if (isc_mempool_getallocated(mgr->depool) != 0) return (ISC_FALSE); if (isc_mempool_getallocated(mgr->rpool) != 0) return (ISC_FALSE); @@ -1748,7 +1837,7 @@ destroy_mgr(dns_dispatchmgr_t **mgrp) { DESTROYLOCK(&mgr->arc4_lock); - isc_mempool_destroy(&mgr->epool); + isc_mempool_destroy(&mgr->depool); isc_mempool_destroy(&mgr->rpool); isc_mempool_destroy(&mgr->dpool); if (mgr->bpool != NULL) @@ -1756,7 +1845,11 @@ destroy_mgr(dns_dispatchmgr_t **mgrp) { if (mgr->spool != NULL) isc_mempool_destroy(&mgr->spool); - DESTROYLOCK(&mgr->pool_lock); + DESTROYLOCK(&mgr->spool_lock); + DESTROYLOCK(&mgr->bpool_lock); + DESTROYLOCK(&mgr->dpool_lock); + DESTROYLOCK(&mgr->rpool_lock); + DESTROYLOCK(&mgr->depool_lock); #ifdef BIND9 if (mgr->entropy != NULL) @@ -1787,19 +1880,14 @@ destroy_mgr(dns_dispatchmgr_t **mgrp) { static isc_result_t open_socket(isc_socketmgr_t *mgr, isc_sockaddr_t *local, - unsigned int options, isc_socket_t **sockp) + unsigned int options, isc_socket_t **sockp, + isc_socket_t *dup_socket) { isc_socket_t *sock; isc_result_t result; sock = *sockp; - if (sock == NULL) { - result = isc_socket_create(mgr, isc_sockaddr_pf(local), - isc_sockettype_udp, &sock); - if (result != ISC_R_SUCCESS) - return (result); - isc_socket_setname(sock, "dispatcher", NULL); - } else { + if (sock != NULL) { #ifdef BIND9 result = isc_socket_open(sock); if (result != ISC_R_SUCCESS) @@ -1807,8 +1895,23 @@ open_socket(isc_socketmgr_t *mgr, isc_sockaddr_t *local, #else INSIST(0); #endif + } else if (dup_socket != NULL) { + result = isc_socket_dup(dup_socket, &sock); + if (result != ISC_R_SUCCESS) + return (result); + + isc_socket_setname(sock, "dispatcher", NULL); + *sockp = sock; + return (ISC_R_SUCCESS); + } else { + result = isc_socket_create(mgr, isc_sockaddr_pf(local), + isc_sockettype_udp, &sock); + if (result != ISC_R_SUCCESS) + return (result); } + isc_socket_setname(sock, "dispatcher", NULL); + #ifndef ISC_ALLOW_MAPPED isc_socket_ipv6only(sock, ISC_TRUE); #endif @@ -1886,22 +1989,38 @@ dns_dispatchmgr_create(isc_mem_t *mctx, isc_entropy_t *entropy, if (result != ISC_R_SUCCESS) goto kill_arc4_lock; - result = isc_mutex_init(&mgr->pool_lock); + result = isc_mutex_init(&mgr->depool_lock); if (result != ISC_R_SUCCESS) goto kill_buffer_lock; - mgr->epool = NULL; + result = isc_mutex_init(&mgr->rpool_lock); + if (result != ISC_R_SUCCESS) + goto kill_depool_lock; + + result = isc_mutex_init(&mgr->dpool_lock); + if (result != ISC_R_SUCCESS) + goto kill_rpool_lock; + + result = isc_mutex_init(&mgr->bpool_lock); + if (result != ISC_R_SUCCESS) + goto kill_dpool_lock; + + result = isc_mutex_init(&mgr->spool_lock); + if (result != ISC_R_SUCCESS) + goto kill_bpool_lock; + + mgr->depool = NULL; if (isc_mempool_create(mgr->mctx, sizeof(dns_dispatchevent_t), - &mgr->epool) != ISC_R_SUCCESS) { + &mgr->depool) != ISC_R_SUCCESS) { result = ISC_R_NOMEMORY; - goto kill_pool_lock; + goto kill_spool_lock; } mgr->rpool = NULL; if (isc_mempool_create(mgr->mctx, sizeof(dns_dispentry_t), &mgr->rpool) != ISC_R_SUCCESS) { result = ISC_R_NOMEMORY; - goto kill_epool; + goto kill_depool; } mgr->dpool = NULL; @@ -1911,17 +2030,23 @@ dns_dispatchmgr_create(isc_mem_t *mctx, isc_entropy_t *entropy, goto kill_rpool; } - isc_mempool_setname(mgr->epool, "dispmgr_epool"); - isc_mempool_setfreemax(mgr->epool, 1024); - isc_mempool_associatelock(mgr->epool, &mgr->pool_lock); + isc_mempool_setname(mgr->depool, "dispmgr_depool"); + isc_mempool_setmaxalloc(mgr->depool, 32768); + isc_mempool_setfreemax(mgr->depool, 32768); + isc_mempool_associatelock(mgr->depool, &mgr->depool_lock); + isc_mempool_setfillcount(mgr->depool, 256); isc_mempool_setname(mgr->rpool, "dispmgr_rpool"); - isc_mempool_setfreemax(mgr->rpool, 1024); - isc_mempool_associatelock(mgr->rpool, &mgr->pool_lock); + isc_mempool_setmaxalloc(mgr->rpool, 32768); + isc_mempool_setfreemax(mgr->rpool, 32768); + isc_mempool_associatelock(mgr->rpool, &mgr->rpool_lock); + isc_mempool_setfillcount(mgr->rpool, 256); isc_mempool_setname(mgr->dpool, "dispmgr_dpool"); - isc_mempool_setfreemax(mgr->dpool, 1024); - isc_mempool_associatelock(mgr->dpool, &mgr->pool_lock); + isc_mempool_setmaxalloc(mgr->dpool, 32768); + isc_mempool_setfreemax(mgr->dpool, 32768); + isc_mempool_associatelock(mgr->dpool, &mgr->dpool_lock); + isc_mempool_setfillcount(mgr->dpool, 256); mgr->buffers = 0; mgr->buffersize = 0; @@ -1970,10 +2095,18 @@ dns_dispatchmgr_create(isc_mem_t *mctx, isc_entropy_t *entropy, isc_mempool_destroy(&mgr->dpool); kill_rpool: isc_mempool_destroy(&mgr->rpool); - kill_epool: - isc_mempool_destroy(&mgr->epool); - kill_pool_lock: - DESTROYLOCK(&mgr->pool_lock); + kill_depool: + isc_mempool_destroy(&mgr->depool); + kill_spool_lock: + DESTROYLOCK(&mgr->spool_lock); + kill_bpool_lock: + DESTROYLOCK(&mgr->bpool_lock); + kill_dpool_lock: + DESTROYLOCK(&mgr->dpool_lock); + kill_rpool_lock: + DESTROYLOCK(&mgr->rpool_lock); + kill_depool_lock: + DESTROYLOCK(&mgr->depool_lock); kill_buffer_lock: DESTROYLOCK(&mgr->buffer_lock); kill_arc4_lock: @@ -2127,6 +2260,7 @@ dns_dispatchmgr_setudp(dns_dispatchmgr_t *mgr, */ if (maxbuffers > mgr->maxbuffers) { isc_mempool_setmaxalloc(mgr->bpool, maxbuffers); + isc_mempool_setfreemax(mgr->bpool, maxbuffers); mgr->maxbuffers = maxbuffers; } } else { @@ -2137,12 +2271,16 @@ dns_dispatchmgr_setudp(dns_dispatchmgr_t *mgr, } isc_mempool_setname(mgr->bpool, "dispmgr_bpool"); isc_mempool_setmaxalloc(mgr->bpool, maxbuffers); - isc_mempool_associatelock(mgr->bpool, &mgr->pool_lock); + isc_mempool_setfreemax(mgr->bpool, maxbuffers); + isc_mempool_associatelock(mgr->bpool, &mgr->bpool_lock); + isc_mempool_setfillcount(mgr->bpool, 256); } /* Create or adjust socket pool */ if (mgr->spool != NULL) { - isc_mempool_setmaxalloc(mgr->spool, DNS_DISPATCH_POOLSOCKS * 2); + if (maxrequests < DNS_DISPATCH_POOLSOCKS * 2) + isc_mempool_setmaxalloc(mgr->spool, DNS_DISPATCH_POOLSOCKS * 2); + isc_mempool_setfreemax(mgr->spool, DNS_DISPATCH_POOLSOCKS * 2); UNLOCK(&mgr->buffer_lock); return (ISC_R_SUCCESS); } @@ -2154,7 +2292,9 @@ dns_dispatchmgr_setudp(dns_dispatchmgr_t *mgr, } isc_mempool_setname(mgr->spool, "dispmgr_spool"); isc_mempool_setmaxalloc(mgr->spool, maxrequests); - isc_mempool_associatelock(mgr->spool, &mgr->pool_lock); + isc_mempool_setfreemax(mgr->spool, maxrequests); + isc_mempool_associatelock(mgr->spool, &mgr->spool_lock); + isc_mempool_setfillcount(mgr->spool, 256); result = qid_allocate(mgr, buckets, increment, &mgr->qid, ISC_TRUE); if (result != ISC_R_SUCCESS) @@ -2480,7 +2620,7 @@ dispatch_allocate(dns_dispatchmgr_t *mgr, unsigned int maxrequests, if (result != ISC_R_SUCCESS) goto deallocate; - disp->failsafe_ev = allocate_event(disp); + disp->failsafe_ev = allocate_devent(disp); if (disp->failsafe_ev == NULL) { result = ISC_R_NOMEMORY; goto kill_lock; @@ -2531,7 +2671,7 @@ dispatch_free(dns_dispatch_t **dispp) INSIST(ISC_LIST_EMPTY(disp->activesockets)); INSIST(ISC_LIST_EMPTY(disp->inactivesockets)); - isc_mempool_put(mgr->epool, disp->failsafe_ev); + isc_mempool_put(mgr->depool, disp->failsafe_ev); disp->failsafe_ev = NULL; if (disp->qid != NULL) @@ -2595,6 +2735,8 @@ dns_dispatch_createtcp(dns_dispatchmgr_t *mgr, isc_socket_t *sock, disp->socket = NULL; isc_socket_attach(sock, &disp->socket); + disp->sepool = NULL; + disp->ntasks = 1; disp->task[0] = NULL; result = isc_task_create(taskmgr, 0, &disp->task[0]); @@ -2646,13 +2788,13 @@ dns_dispatch_createtcp(dns_dispatchmgr_t *mgr, isc_socket_t *sock, } isc_result_t -dns_dispatch_getudp(dns_dispatchmgr_t *mgr, isc_socketmgr_t *sockmgr, +dns_dispatch_getudp_dup(dns_dispatchmgr_t *mgr, isc_socketmgr_t *sockmgr, isc_taskmgr_t *taskmgr, isc_sockaddr_t *localaddr, unsigned int buffersize, unsigned int maxbuffers, unsigned int maxrequests, unsigned int buckets, unsigned int increment, unsigned int attributes, unsigned int mask, - dns_dispatch_t **dispp) + dns_dispatch_t **dispp, dns_dispatch_t *dup_dispatch) { isc_result_t result; dns_dispatch_t *disp = NULL; @@ -2683,28 +2825,31 @@ dns_dispatch_getudp(dns_dispatchmgr_t *mgr, isc_socketmgr_t *sockmgr, /* * See if we have a dispatcher that matches. */ - result = dispatch_find(mgr, localaddr, attributes, mask, &disp); - if (result == ISC_R_SUCCESS) { - disp->refcount++; + if (dup_dispatch == NULL) { + result = dispatch_find(mgr, localaddr, attributes, mask, &disp); + if (result == ISC_R_SUCCESS) { + disp->refcount++; - if (disp->maxrequests < maxrequests) - disp->maxrequests = maxrequests; + if (disp->maxrequests < maxrequests) + disp->maxrequests = maxrequests; - if ((disp->attributes & DNS_DISPATCHATTR_NOLISTEN) == 0 && - (attributes & DNS_DISPATCHATTR_NOLISTEN) != 0) - { - disp->attributes |= DNS_DISPATCHATTR_NOLISTEN; - if (disp->recv_pending != 0) - isc_socket_cancel(disp->socket, disp->task[0], - ISC_SOCKCANCEL_RECV); + if ((disp->attributes & DNS_DISPATCHATTR_NOLISTEN) == 0 + && (attributes & DNS_DISPATCHATTR_NOLISTEN) != 0) + { + disp->attributes |= DNS_DISPATCHATTR_NOLISTEN; + if (disp->recv_pending != 0) + isc_socket_cancel(disp->socket, + disp->task[0], + ISC_SOCKCANCEL_RECV); + } + + UNLOCK(&disp->lock); + UNLOCK(&mgr->lock); + + *dispp = disp; + + return (ISC_R_SUCCESS); } - - UNLOCK(&disp->lock); - UNLOCK(&mgr->lock); - - *dispp = disp; - - return (ISC_R_SUCCESS); } createudp: @@ -2712,7 +2857,11 @@ dns_dispatch_getudp(dns_dispatchmgr_t *mgr, isc_socketmgr_t *sockmgr, * Nope, create one. */ result = dispatch_createudp(mgr, sockmgr, taskmgr, localaddr, - maxrequests, attributes, &disp); + maxrequests, attributes, &disp, + dup_dispatch == NULL + ? NULL + : dup_dispatch->socket); + if (result != ISC_R_SUCCESS) { UNLOCK(&mgr->lock); return (result); @@ -2720,9 +2869,25 @@ dns_dispatch_getudp(dns_dispatchmgr_t *mgr, isc_socketmgr_t *sockmgr, UNLOCK(&mgr->lock); *dispp = disp; + return (ISC_R_SUCCESS); } +isc_result_t +dns_dispatch_getudp(dns_dispatchmgr_t *mgr, isc_socketmgr_t *sockmgr, + isc_taskmgr_t *taskmgr, isc_sockaddr_t *localaddr, + unsigned int buffersize, + unsigned int maxbuffers, unsigned int maxrequests, + unsigned int buckets, unsigned int increment, + unsigned int attributes, unsigned int mask, + dns_dispatch_t **dispp) +{ + return (dns_dispatch_getudp_dup(mgr, sockmgr, taskmgr, localaddr, + buffersize, maxbuffers, maxrequests, + buckets, increment, attributes, + mask, dispp, NULL)); +} + /* * mgr should be locked. */ @@ -2734,7 +2899,7 @@ dns_dispatch_getudp(dns_dispatchmgr_t *mgr, isc_socketmgr_t *sockmgr, static isc_result_t get_udpsocket(dns_dispatchmgr_t *mgr, dns_dispatch_t *disp, isc_socketmgr_t *sockmgr, isc_sockaddr_t *localaddr, - isc_socket_t **sockp) + isc_socket_t **sockp, isc_socket_t *dup_socket) { unsigned int i, j; isc_socket_t *held[DNS_DISPATCH_HELD]; @@ -2774,7 +2939,7 @@ get_udpsocket(dns_dispatchmgr_t *mgr, dns_dispatch_t *disp, nports)]; isc_sockaddr_setport(&localaddr_bound, prt); result = open_socket(sockmgr, &localaddr_bound, - 0, &sock); + 0, &sock, NULL); /* * Continue if the port choosen is already in use * or the OS has reserved it. @@ -2794,7 +2959,8 @@ get_udpsocket(dns_dispatchmgr_t *mgr, dns_dispatch_t *disp, } else { /* Allow to reuse address for non-random ports. */ result = open_socket(sockmgr, localaddr, - ISC_SOCKET_REUSEADDRESS, &sock); + ISC_SOCKET_REUSEADDRESS, &sock, + dup_socket); if (result == ISC_R_SUCCESS) *sockp = sock; @@ -2806,7 +2972,7 @@ get_udpsocket(dns_dispatchmgr_t *mgr, dns_dispatch_t *disp, i = 0; for (j = 0; j < 0xffffU; j++) { - result = open_socket(sockmgr, localaddr, 0, &sock); + result = open_socket(sockmgr, localaddr, 0, &sock, NULL); if (result != ISC_R_SUCCESS) goto end; else if (portavailable(mgr, sock, NULL)) @@ -2843,7 +3009,8 @@ dispatch_createudp(dns_dispatchmgr_t *mgr, isc_socketmgr_t *sockmgr, isc_sockaddr_t *localaddr, unsigned int maxrequests, unsigned int attributes, - dns_dispatch_t **dispp) + dns_dispatch_t **dispp, + isc_socket_t *dup_socket) { isc_result_t result; dns_dispatch_t *disp; @@ -2859,9 +3026,21 @@ dispatch_createudp(dns_dispatchmgr_t *mgr, isc_socketmgr_t *sockmgr, return (result); if ((attributes & DNS_DISPATCHATTR_EXCLUSIVE) == 0) { - result = get_udpsocket(mgr, disp, sockmgr, localaddr, &sock); + result = get_udpsocket(mgr, disp, sockmgr, localaddr, &sock, + dup_socket); if (result != ISC_R_SUCCESS) goto deallocate_dispatch; + + if (isc_log_wouldlog(dns_lctx, 90)) { + char addrbuf[ISC_SOCKADDR_FORMATSIZE]; + + isc_sockaddr_format(localaddr, addrbuf, + ISC_SOCKADDR_FORMATSIZE); + mgr_log(mgr, LVL(90), "dns_dispatch_createudp: Created" + " UDP dispatch for %s with socket fd %d\n", + addrbuf, isc_socket_getfd(sock)); + } + } else { isc_sockaddr_t sa_any; @@ -2873,7 +3052,7 @@ dispatch_createudp(dns_dispatchmgr_t *mgr, isc_socketmgr_t *sockmgr, */ isc_sockaddr_anyofpf(&sa_any, isc_sockaddr_pf(localaddr)); if (!isc_sockaddr_eqaddr(&sa_any, localaddr)) { - result = open_socket(sockmgr, localaddr, 0, &sock); + result = open_socket(sockmgr, localaddr, 0, &sock, NULL); if (sock != NULL) isc_socket_detach(&sock); if (result != ISC_R_SUCCESS) @@ -2925,6 +3104,24 @@ dispatch_createudp(dns_dispatchmgr_t *mgr, isc_socketmgr_t *sockmgr, goto kill_task; } + disp->sepool = NULL; + if (isc_mempool_create(mgr->mctx, sizeof(isc_socketevent_t), + &disp->sepool) != ISC_R_SUCCESS) + { + result = ISC_R_NOMEMORY; + goto kill_ctlevent; + } + + result = isc_mutex_init(&disp->sepool_lock); + if (result != ISC_R_SUCCESS) + goto kill_sepool; + + isc_mempool_setname(disp->sepool, "disp_sepool"); + isc_mempool_setmaxalloc(disp->sepool, 32768); + isc_mempool_setfreemax(disp->sepool, 32768); + isc_mempool_associatelock(disp->sepool, &disp->sepool_lock); + isc_mempool_setfillcount(disp->sepool, 16); + attributes &= ~DNS_DISPATCHATTR_TCP; attributes |= DNS_DISPATCHATTR_UDP; disp->attributes = attributes; @@ -2940,11 +3137,16 @@ dispatch_createudp(dns_dispatchmgr_t *mgr, isc_socketmgr_t *sockmgr, dispatch_log(disp, LVL(90), "created socket %p", disp->socket); *dispp = disp; + return (result); /* * Error returns. */ + kill_sepool: + isc_mempool_destroy(&disp->sepool); + kill_ctlevent: + isc_event_free(&disp->ctlevent); kill_task: for (i = 0; i < disp->ntasks; i++) isc_task_detach(&disp->task[i]); @@ -3061,7 +3263,7 @@ dns_dispatch_addresponse2(dns_dispatch_t *disp, isc_sockaddr_t *dest, oldestsocket = ISC_LIST_HEAD(disp->activesockets); oldestresp = oldestsocket->resp; if (oldestresp != NULL && !oldestresp->item_out) { - rev = allocate_event(oldestresp->disp); + rev = allocate_devent(oldestresp->disp); if (rev != NULL) { rev->buffer.base = NULL; rev->result = ISC_R_CANCELED; @@ -3088,16 +3290,14 @@ dns_dispatch_addresponse2(dns_dispatch_t *disp, isc_sockaddr_t *dest, } qid = DNS_QID(disp); - LOCK(&qid->lock); if ((disp->attributes & DNS_DISPATCHATTR_EXCLUSIVE) != 0) { /* * Get a separate UDP socket with a random port number. */ - result = get_dispsocket(disp, dest, sockmgr, qid, &dispsocket, + result = get_dispsocket(disp, dest, sockmgr, &dispsocket, &localport); if (result != ISC_R_SUCCESS) { - UNLOCK(&qid->lock); UNLOCK(&disp->lock); inc_stats(disp->mgr, dns_resstatscounter_dispsockfail); return (result); @@ -3109,6 +3309,7 @@ dns_dispatch_addresponse2(dns_dispatch_t *disp, isc_sockaddr_t *dest, /* * Try somewhat hard to find an unique ID. */ + LOCK(&qid->lock); id = (dns_messageid_t)dispatch_random(DISP_ARC4CTX(disp)); bucket = dns_hash(qid, dest, id, localport); ok = ISC_FALSE; @@ -3121,16 +3322,15 @@ dns_dispatch_addresponse2(dns_dispatch_t *disp, isc_sockaddr_t *dest, id &= 0x0000ffff; bucket = dns_hash(qid, dest, id, localport); } + UNLOCK(&qid->lock); if (!ok) { - UNLOCK(&qid->lock); UNLOCK(&disp->lock); return (ISC_R_NOMORE); } res = isc_mempool_get(disp->mgr->rpool); if (res == NULL) { - UNLOCK(&qid->lock); UNLOCK(&disp->lock); if (dispsocket != NULL) destroy_dispsocket(disp, &dispsocket); @@ -3155,6 +3355,8 @@ dns_dispatch_addresponse2(dns_dispatch_t *disp, isc_sockaddr_t *dest, ISC_LIST_INIT(res->items); ISC_LINK_INIT(res, link); res->magic = RESPONSE_MAGIC; + + LOCK(&qid->lock); ISC_LIST_APPEND(qid->qid_table[bucket], res, link); UNLOCK(&qid->lock); @@ -3302,7 +3504,7 @@ dns_dispatch_removeresponse(dns_dispentry_t **resp, res->item_out = ISC_FALSE; if (ev->buffer.base != NULL) free_buffer(disp, ev->buffer.base, ev->buffer.length); - free_event(disp, ev); + free_devent(disp, ev); } request_log(disp, res, LVL(90), "detaching from task %p", res->task); @@ -3322,7 +3524,7 @@ dns_dispatch_removeresponse(dns_dispentry_t **resp, ISC_LIST_UNLINK(res->items, ev, ev_link); if (ev->buffer.base != NULL) free_buffer(disp, ev->buffer.base, ev->buffer.length); - free_event(disp, ev); + free_devent(disp, ev); ev = ISC_LIST_HEAD(res->items); } res->magic = 0; @@ -3519,6 +3721,128 @@ dns_dispatch_importrecv(dns_dispatch_t *disp, isc_event_t *event) { isc_task_send(disp->task[0], ISC_EVENT_PTR(&newsevent)); } +dns_dispatch_t * +dns_dispatchset_get(dns_dispatchset_t *dset) { + dns_dispatch_t *disp; + + /* check that dispatch set is configured */ + if (dset == NULL || dset->ndisp == 0) + return (NULL); + + LOCK(&dset->lock); + disp = dset->dispatches[dset->cur]; + dset->cur++; + if (dset->cur == dset->ndisp) + dset->cur = 0; + UNLOCK(&dset->lock); + + return (disp); +} + +isc_result_t +dns_dispatchset_create(isc_mem_t *mctx, isc_socketmgr_t *sockmgr, + isc_taskmgr_t *taskmgr, dns_dispatch_t *source, + dns_dispatchset_t **dsetp, int n) +{ + isc_result_t result; + dns_dispatchset_t *dset; + dns_dispatchmgr_t *mgr; + int i, j; + + REQUIRE(VALID_DISPATCH(source)); + REQUIRE((source->attributes & DNS_DISPATCHATTR_UDP) != 0); + REQUIRE(dsetp != NULL && *dsetp == NULL); + + mgr = source->mgr; + + dset = isc_mem_get(mctx, sizeof(dns_dispatchset_t)); + if (dset == NULL) + return (ISC_R_NOMEMORY); + memset(dset, 0, sizeof(*dset)); + + result = isc_mutex_init(&dset->lock); + if (result != ISC_R_SUCCESS) + goto fail_alloc; + + dset->dispatches = isc_mem_get(mctx, sizeof(dns_dispatch_t *) * n); + if (dset == NULL) { + result = ISC_R_NOMEMORY; + goto fail_lock; + } + + isc_mem_attach(mctx, &dset->mctx); + dset->ndisp = n; + dset->cur = 0; + + dset->dispatches[0] = NULL; + dns_dispatch_attach(source, &dset->dispatches[0]); + + LOCK(&mgr->lock); + for (i = 1; i < n; i++) { + dset->dispatches[i] = NULL; + result = dispatch_createudp(mgr, sockmgr, taskmgr, + &source->local, + source->maxrequests, + source->attributes, + &dset->dispatches[i], + source->socket); + if (result != ISC_R_SUCCESS) + goto fail; + } + + UNLOCK(&mgr->lock); + *dsetp = dset; + + return (ISC_R_SUCCESS); + + fail: + UNLOCK(&mgr->lock); + + for (j = 0; j < i; j++) + dns_dispatch_detach(&(dset->dispatches[j])); + isc_mem_put(mctx, dset->dispatches, sizeof(dns_dispatch_t *) * n); + if (dset->mctx == mctx) + isc_mem_detach(&dset->mctx); + + fail_lock: + DESTROYLOCK(&dset->lock); + + fail_alloc: + isc_mem_put(mctx, dset, sizeof(dns_dispatchset_t)); + return (result); +} + +void +dns_dispatchset_cancelall(dns_dispatchset_t *dset, isc_task_t *task) { + int i; + + REQUIRE(dset != NULL); + + for (i = 0; i < dset->ndisp; i++) { + isc_socket_t *sock; + sock = dns_dispatch_getsocket(dset->dispatches[i]); + isc_socket_cancel(sock, task, ISC_SOCKCANCEL_ALL); + } +} + +void +dns_dispatchset_destroy(dns_dispatchset_t **dsetp) { + dns_dispatchset_t *dset; + int i; + + REQUIRE(dsetp != NULL && *dsetp != NULL); + + dset = *dsetp; + for (i = 0; i < dset->ndisp; i++) + dns_dispatch_detach(&(dset->dispatches[i])); + isc_mem_put(dset->mctx, dset->dispatches, + sizeof(dns_dispatch_t *) * dset->ndisp); + DESTROYLOCK(&dset->lock); + isc_mem_putanddetach(&dset->mctx, dset, sizeof(dns_dispatchset_t)); + + *dsetp = NULL; +} + #if 0 void dns_dispatchmgr_dump(dns_dispatchmgr_t *mgr) { diff --git a/contrib/bind9/lib/dns/dns64.c b/contrib/bind9/lib/dns/dns64.c index 0b3f1d48b89..78eff579a2b 100644 --- a/contrib/bind9/lib/dns/dns64.c +++ b/contrib/bind9/lib/dns/dns64.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010-2012 Internet Systems Consortium, Inc. ("ISC") + * Copyright (C) 2010, 2011 Internet Systems Consortium, Inc. ("ISC") * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -14,7 +14,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id$ */ +/* $Id: dns64.c,v 1.8 2011/03/12 04:59:47 tbox Exp $ */ #include diff --git a/contrib/bind9/lib/dns/dnssec.c b/contrib/bind9/lib/dns/dnssec.c index 587bd1c3c23..d00c99b4124 100644 --- a/contrib/bind9/lib/dns/dnssec.c +++ b/contrib/bind9/lib/dns/dnssec.c @@ -372,6 +372,15 @@ isc_result_t dns_dnssec_verify2(dns_name_t *name, dns_rdataset_t *set, dst_key_t *key, isc_boolean_t ignoretime, isc_mem_t *mctx, dns_rdata_t *sigrdata, dns_name_t *wild) +{ + return (dns_dnssec_verify3(name, set, key, ignoretime, 0, mctx, + sigrdata, wild)); +} + +isc_result_t +dns_dnssec_verify3(dns_name_t *name, dns_rdataset_t *set, dst_key_t *key, + isc_boolean_t ignoretime, unsigned int maxbits, + isc_mem_t *mctx, dns_rdata_t *sigrdata, dns_name_t *wild) { dns_rdata_rrsig_t sig; dns_fixedname_t fnewname; @@ -546,7 +555,7 @@ dns_dnssec_verify2(dns_name_t *name, dns_rdataset_t *set, dst_key_t *key, r.base = sig.signature; r.length = sig.siglen; - ret = dst_context_verify(ctx, &r); + ret = dst_context_verify2(ctx, maxbits, &r); if (ret == ISC_R_SUCCESS && downcase) { char namebuf[DNS_NAME_FORMATSIZE]; dns_name_format(&sig.signer, namebuf, sizeof(namebuf)); @@ -683,6 +692,8 @@ dns_dnssec_findzonekeys2(dns_db_t *db, dns_dbversion_t *ver, pubkey = NULL; dns_rdataset_current(&rdataset, &rdata); RETERR(dns_dnssec_keyfromrdata(name, &rdata, mctx, &pubkey)); + dst_key_setttl(pubkey, rdataset.ttl); + if (!is_zone_key(pubkey) || (dst_key_flags(pubkey) & DNS_KEYTYPE_NOAUTH) != 0) goto next; @@ -760,6 +771,12 @@ dns_dnssec_findzonekeys2(dns_db_t *db, dns_dbversion_t *ver, goto next; } + /* + * Whatever the key's default TTL may have + * been, the rdataset TTL takes priority. + */ + dst_key_setttl(keys[count], rdataset.ttl); + if ((dst_key_flags(keys[count]) & DNS_KEYTYPE_NOAUTH) != 0) { /* We should never get here. */ dst_key_free(&keys[count]); @@ -1509,6 +1526,7 @@ dns_dnssec_keylistfromrdataset(dns_name_t *origin, dns_rdata_reset(&rdata); dns_rdataset_current(&keys, &rdata); RETERR(dns_dnssec_keyfromrdata(origin, &rdata, mctx, &pubkey)); + dst_key_setttl(pubkey, keys.ttl); if (!is_zone_key(pubkey) || (dst_key_flags(pubkey) & DNS_KEYTYPE_NOAUTH) != 0) @@ -1581,6 +1599,12 @@ dns_dnssec_keylistfromrdataset(dns_name_t *origin, if ((dst_key_flags(privkey) & DNS_KEYTYPE_NOAUTH) != 0) goto skip; + /* + * Whatever the key's default TTL may have + * been, the rdataset TTL takes priority. + */ + dst_key_setttl(privkey, dst_key_getttl(pubkey)); + RETERR(addkey(keylist, &privkey, savekeys, mctx)); skip: if (pubkey != NULL) @@ -1706,16 +1730,22 @@ remove_key(dns_diff_t *diff, dns_dnsseckey_t *key, dns_name_t *origin, isc_result_t dns_dnssec_updatekeys(dns_dnsseckeylist_t *keys, dns_dnsseckeylist_t *newkeys, dns_dnsseckeylist_t *removed, dns_name_t *origin, - dns_ttl_t ttl, dns_diff_t *diff, isc_boolean_t allzsk, - isc_mem_t *mctx, void (*report)(const char *, ...)) + dns_ttl_t hint_ttl, dns_diff_t *diff, + isc_boolean_t allzsk, isc_mem_t *mctx, + void (*report)(const char *, ...)) { isc_result_t result; dns_dnsseckey_t *key, *key1, *key2, *next; + isc_boolean_t found_ttl = ISC_FALSE; + dns_ttl_t ttl = hint_ttl; /* * First, look through the existing key list to find keys * supplied from the command line which are not in the zone. * Update the zone to include them. + * + * Also, if there are keys published in the zone already, + * use their TTL for all subsequent published keys. */ for (key = ISC_LIST_HEAD(*keys); key != NULL; @@ -1725,6 +1755,30 @@ dns_dnssec_updatekeys(dns_dnsseckeylist_t *keys, dns_dnsseckeylist_t *newkeys, RETERR(publish_key(diff, key, origin, ttl, mctx, allzsk, report)); } + if (key->source == dns_keysource_zoneapex) { + ttl = dst_key_getttl(key->key); + found_ttl = ISC_TRUE; + } + } + + /* + * If there were no existing keys, use the smallest nonzero + * TTL of the keys found in the repository. + */ + if (!found_ttl && !ISC_LIST_EMPTY(*newkeys)) { + dns_ttl_t shortest = 0; + + for (key = ISC_LIST_HEAD(*newkeys); + key != NULL; + key = ISC_LIST_NEXT(key, link)) { + dns_ttl_t thisttl = dst_key_getttl(key->key); + if (thisttl != 0 && + (shortest == 0 || thisttl < shortest)) + shortest = thisttl; + } + + if (shortest != 0) + ttl = shortest; } /* diff --git a/contrib/bind9/lib/dns/dst_api.c b/contrib/bind9/lib/dns/dst_api.c index 53978bce070..98607246eff 100644 --- a/contrib/bind9/lib/dns/dst_api.c +++ b/contrib/bind9/lib/dns/dst_api.c @@ -31,7 +31,7 @@ /* * Principal Author: Brian Wellington - * $Id$ + * $Id: dst_api.c,v 1.65 2011/10/20 21:20:02 marka Exp $ */ /*! \file */ @@ -58,6 +58,8 @@ #include #include +#define DST_KEY_INTERNAL + #include #include #include @@ -92,6 +94,7 @@ static dst_key_t * get_key_struct(dns_name_t *name, unsigned int protocol, unsigned int bits, dns_rdataclass_t rdclass, + dns_ttl_t ttl, isc_mem_t *mctx); static isc_result_t write_public_key(const dst_key_t *key, int type, const char *directory); @@ -370,6 +373,25 @@ dst_context_verify(dst_context_t *dctx, isc_region_t *sig) { return (dctx->key->func->verify(dctx, sig)); } +isc_result_t +dst_context_verify2(dst_context_t *dctx, unsigned int maxbits, + isc_region_t *sig) +{ + REQUIRE(VALID_CTX(dctx)); + REQUIRE(sig != NULL); + + CHECKALG(dctx->key->key_alg); + if (dctx->key->keydata.generic == NULL) + return (DST_R_NULLKEY); + if (dctx->key->func->verify == NULL && + dctx->key->func->verify2 == NULL) + return (DST_R_NOTPUBLICKEY); + + return (dctx->key->func->verify2 != NULL ? + dctx->key->func->verify2(dctx, maxbits, sig) : + dctx->key->func->verify(dctx, sig)); +} + isc_result_t dst_key_computesecret(const dst_key_t *pub, const dst_key_t *priv, isc_buffer_t *secret) @@ -525,7 +547,7 @@ dst_key_fromnamedfile(const char *filename, const char *dirname, key = get_key_struct(pubkey->key_name, pubkey->key_alg, pubkey->key_flags, pubkey->key_proto, 0, - pubkey->key_class, mctx); + pubkey->key_class, pubkey->key_ttl, mctx); if (key == NULL) { dst_key_free(&pubkey); return (ISC_R_NOMEMORY); @@ -726,7 +748,7 @@ dst_key_fromgssapi(dns_name_t *name, gss_ctx_id_t gssctx, isc_mem_t *mctx, REQUIRE(keyp != NULL && *keyp == NULL); key = get_key_struct(name, DST_ALG_GSSAPI, 0, DNS_KEYPROTO_DNSSEC, - 0, dns_rdataclass_in, mctx); + 0, dns_rdataclass_in, 0, mctx); if (key == NULL) return (ISC_R_NOMEMORY); @@ -747,6 +769,40 @@ out: return result; } +isc_result_t +dst_key_buildinternal(dns_name_t *name, unsigned int alg, + unsigned int bits, unsigned int flags, + unsigned int protocol, dns_rdataclass_t rdclass, + void *data, isc_mem_t *mctx, dst_key_t **keyp) +{ + dst_key_t *key; + isc_result_t result; + + REQUIRE(dst_initialized == ISC_TRUE); + REQUIRE(dns_name_isabsolute(name)); + REQUIRE(mctx != NULL); + REQUIRE(keyp != NULL && *keyp == NULL); + REQUIRE(data != NULL); + + CHECKALG(alg); + + key = get_key_struct(name, alg, flags, protocol, bits, rdclass, + 0, mctx); + if (key == NULL) + return (ISC_R_NOMEMORY); + + key->keydata.generic = data; + + result = computeid(key); + if (result != ISC_R_SUCCESS) { + dst_key_free(&key); + return (result); + } + + *keyp = key; + return (ISC_R_SUCCESS); +} + isc_result_t dst_key_fromlabel(dns_name_t *name, int alg, unsigned int flags, unsigned int protocol, dns_rdataclass_t rdclass, @@ -764,7 +820,7 @@ dst_key_fromlabel(dns_name_t *name, int alg, unsigned int flags, CHECKALG(alg); - key = get_key_struct(name, alg, flags, protocol, 0, rdclass, mctx); + key = get_key_struct(name, alg, flags, protocol, 0, rdclass, 0, mctx); if (key == NULL) return (ISC_R_NOMEMORY); @@ -818,7 +874,8 @@ dst_key_generate2(dns_name_t *name, unsigned int alg, CHECKALG(alg); - key = get_key_struct(name, alg, flags, protocol, bits, rdclass, mctx); + key = get_key_struct(name, alg, flags, protocol, bits, + rdclass, 0, mctx); if (key == NULL) return (ISC_R_NOMEMORY); @@ -1078,7 +1135,7 @@ dst_key_free(dst_key_t **keyp) { isc_buffer_free(&key->key_tkeytoken); } memset(key, 0, sizeof(dst_key_t)); - isc_mem_put(mctx, key, sizeof(dst_key_t)); + isc_mem_putanddetach(&mctx, key, sizeof(dst_key_t)); *keyp = NULL; } @@ -1220,7 +1277,7 @@ dst_key_restore(dns_name_t *name, unsigned int alg, unsigned int flags, if (dst_t_func[alg]->restore == NULL) return (ISC_R_NOTIMPLEMENTED); - key = get_key_struct(name, alg, flags, protocol, 0, rdclass, mctx); + key = get_key_struct(name, alg, flags, protocol, 0, rdclass, 0, mctx); if (key == NULL) return (ISC_R_NOMEMORY); @@ -1244,7 +1301,7 @@ static dst_key_t * get_key_struct(dns_name_t *name, unsigned int alg, unsigned int flags, unsigned int protocol, unsigned int bits, dns_rdataclass_t rdclass, - isc_mem_t *mctx) + dns_ttl_t ttl, isc_mem_t *mctx) { dst_key_t *key; isc_result_t result; @@ -1277,13 +1334,14 @@ get_key_struct(dns_name_t *name, unsigned int alg, isc_mem_put(mctx, key, sizeof(dst_key_t)); return (NULL); } + isc_mem_attach(mctx, &key->mctx); key->key_alg = alg; key->key_flags = flags; key->key_proto = protocol; - key->mctx = mctx; key->keydata.generic = NULL; key->key_size = bits; key->key_class = rdclass; + key->key_ttl = ttl; key->func = dst_t_func[alg]; key->fmt_major = 0; key->fmt_minor = 0; @@ -1312,7 +1370,7 @@ dst_key_read_public(const char *filename, int type, unsigned int opt = ISC_LEXOPT_DNSMULTILINE; dns_rdataclass_t rdclass = dns_rdataclass_in; isc_lexspecials_t specials; - isc_uint32_t ttl; + isc_uint32_t ttl = 0; isc_result_t result; dns_rdatatype_t keytype; @@ -1413,6 +1471,8 @@ dst_key_read_public(const char *filename, int type, if (ret != ISC_R_SUCCESS) goto cleanup; + dst_key_setttl(*keyp, ttl); + cleanup: if (lex != NULL) isc_lex_destroy(&lex); @@ -1581,9 +1641,11 @@ write_public_key(const dst_key_t *key, int type, const char *directory) { /* Now print the actual key */ ret = dns_name_print(key->key_name, fp); - fprintf(fp, " "); + if (key->key_ttl != 0) + fprintf(fp, "%d ", key->key_ttl); + isc_buffer_usedregion(&classb, &r); if ((unsigned) fwrite(r.base, 1, r.length, fp) != r.length) ret = DST_R_WRITEERROR; @@ -1675,7 +1737,7 @@ frombuffer(dns_name_t *name, unsigned int alg, unsigned int flags, REQUIRE(mctx != NULL); REQUIRE(keyp != NULL && *keyp == NULL); - key = get_key_struct(name, alg, flags, protocol, 0, rdclass, mctx); + key = get_key_struct(name, alg, flags, protocol, 0, rdclass, 0, mctx); if (key == NULL) return (ISC_R_NOMEMORY); diff --git a/contrib/bind9/lib/dns/dst_internal.h b/contrib/bind9/lib/dns/dst_internal.h index ee824f40e88..c3e8e29a46e 100644 --- a/contrib/bind9/lib/dns/dst_internal.h +++ b/contrib/bind9/lib/dns/dst_internal.h @@ -29,7 +29,7 @@ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -/* $Id$ */ +/* $Id: dst_internal.h,v 1.31 2011/10/20 21:20:02 marka Exp $ */ #ifndef DST_DST_INTERNAL_H #define DST_DST_INTERNAL_H 1 @@ -98,6 +98,7 @@ struct dst_key { revoked */ isc_uint16_t key_bits; /*%< hmac digest bits */ dns_rdataclass_t key_class; /*%< class of the key record */ + dns_ttl_t key_ttl; /*%< default/initial dnskey ttl */ isc_mem_t *mctx; /*%< memory context */ char *engine; /*%< engine name (HSM) */ char *label; /*%< engine label (HSM) */ @@ -170,6 +171,8 @@ struct dst_func { */ isc_result_t (*sign)(dst_context_t *dctx, isc_buffer_t *sig); isc_result_t (*verify)(dst_context_t *dctx, const isc_region_t *sig); + isc_result_t (*verify2)(dst_context_t *dctx, int maxbits, + const isc_region_t *sig); isc_result_t (*computesecret)(const dst_key_t *pub, const dst_key_t *priv, isc_buffer_t *secret); diff --git a/contrib/bind9/lib/dns/dst_openssl.h b/contrib/bind9/lib/dns/dst_openssl.h index e4d636e0492..99a43ef948a 100644 --- a/contrib/bind9/lib/dns/dst_openssl.h +++ b/contrib/bind9/lib/dns/dst_openssl.h @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id$ */ +/* $Id: dst_openssl.h,v 1.11 2011/03/12 04:59:48 tbox Exp $ */ #ifndef DST_OPENSSL_H #define DST_OPENSSL_H 1 diff --git a/contrib/bind9/lib/dns/dst_parse.c b/contrib/bind9/lib/dns/dst_parse.c index 95896c4a887..ca43cb3d124 100644 --- a/contrib/bind9/lib/dns/dst_parse.c +++ b/contrib/bind9/lib/dns/dst_parse.c @@ -31,7 +31,7 @@ /*% * Principal Author: Brian Wellington - * $Id$ + * $Id: dst_parse.c,v 1.29 2011/08/18 23:46:35 tbox Exp $ */ #include diff --git a/contrib/bind9/lib/dns/ecdb.c b/contrib/bind9/lib/dns/ecdb.c index 20da5b0e6a0..8b3f7740081 100644 --- a/contrib/bind9/lib/dns/ecdb.c +++ b/contrib/bind9/lib/dns/ecdb.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009-2013 Internet Systems Consortium, Inc. ("ISC") + * Copyright (C) 2009-2011, 2013 Internet Systems Consortium, Inc. ("ISC") * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -14,7 +14,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id$ */ +/* $Id: ecdb.c,v 1.10 2011/12/20 00:06:53 marka Exp $ */ #include "config.h" @@ -583,7 +583,9 @@ static dns_dbmethods_t ecdb_methods = { NULL, /* isdnssec */ NULL, /* getrrsetstats */ NULL, /* rpz_enabled */ - NULL /* rpz_findips */ + NULL, /* rpz_findips */ + NULL, /* findnodeext */ + NULL /* findext */ }; static isc_result_t diff --git a/contrib/bind9/lib/dns/gssapi_link.c b/contrib/bind9/lib/dns/gssapi_link.c index a992a8953f2..5ad81cd80ce 100644 --- a/contrib/bind9/lib/dns/gssapi_link.c +++ b/contrib/bind9/lib/dns/gssapi_link.c @@ -16,7 +16,7 @@ */ /* - * $Id$ + * $Id: gssapi_link.c,v 1.17 2011/03/28 05:32:16 marka Exp $ */ #include @@ -362,6 +362,7 @@ static dst_func_t gssapi_functions = { gssapi_adddata, gssapi_sign, gssapi_verify, + NULL, /*%< verify2 */ NULL, /*%< computesecret */ gssapi_compare, NULL, /*%< paramcompare */ diff --git a/contrib/bind9/lib/dns/gssapictx.c b/contrib/bind9/lib/dns/gssapictx.c index e4047d25219..a8c5900e6d1 100644 --- a/contrib/bind9/lib/dns/gssapictx.c +++ b/contrib/bind9/lib/dns/gssapictx.c @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id$ */ +/* $Id: gssapictx.c,v 1.29 2011/08/29 06:33:25 marka Exp $ */ #include diff --git a/contrib/bind9/lib/dns/hmac_link.c b/contrib/bind9/lib/dns/hmac_link.c index bc0e9a04ed0..256abb6eb47 100644 --- a/contrib/bind9/lib/dns/hmac_link.c +++ b/contrib/bind9/lib/dns/hmac_link.c @@ -1,5 +1,5 @@ /* - * Portions Copyright (C) 2004-2011 Internet Systems Consortium, Inc. ("ISC") + * Portions Copyright (C) 2004-2012 Internet Systems Consortium, Inc. ("ISC") * Portions Copyright (C) 1999-2002 Internet Software Consortium. * * Permission to use, copy, modify, and/or distribute this software for any @@ -313,6 +313,7 @@ static dst_func_t hmacmd5_functions = { hmacmd5_adddata, hmacmd5_sign, hmacmd5_verify, + NULL, /*%< verify2 */ NULL, /*%< computesecret */ hmacmd5_compare, NULL, /*%< paramcompare */ @@ -589,6 +590,7 @@ static dst_func_t hmacsha1_functions = { hmacsha1_adddata, hmacsha1_sign, hmacsha1_verify, + NULL, /* verify2 */ NULL, /* computesecret */ hmacsha1_compare, NULL, /* paramcompare */ @@ -867,6 +869,7 @@ static dst_func_t hmacsha224_functions = { hmacsha224_adddata, hmacsha224_sign, hmacsha224_verify, + NULL, /* verify2 */ NULL, /* computesecret */ hmacsha224_compare, NULL, /* paramcompare */ @@ -1145,6 +1148,7 @@ static dst_func_t hmacsha256_functions = { hmacsha256_adddata, hmacsha256_sign, hmacsha256_verify, + NULL, /* verify2 */ NULL, /* computesecret */ hmacsha256_compare, NULL, /* paramcompare */ @@ -1423,6 +1427,7 @@ static dst_func_t hmacsha384_functions = { hmacsha384_adddata, hmacsha384_sign, hmacsha384_verify, + NULL, /* verify2 */ NULL, /* computesecret */ hmacsha384_compare, NULL, /* paramcompare */ @@ -1701,6 +1706,7 @@ static dst_func_t hmacsha512_functions = { hmacsha512_adddata, hmacsha512_sign, hmacsha512_verify, + NULL, /* verify2 */ NULL, /* computesecret */ hmacsha512_compare, NULL, /* paramcompare */ diff --git a/contrib/bind9/lib/dns/include/dns/Makefile.in b/contrib/bind9/lib/dns/include/dns/Makefile.in index ad8bc383e4b..1a69f2c814f 100644 --- a/contrib/bind9/lib/dns/include/dns/Makefile.in +++ b/contrib/bind9/lib/dns/include/dns/Makefile.in @@ -13,7 +13,7 @@ # OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR # PERFORMANCE OF THIS SOFTWARE. -# $Id$ +# $Id: Makefile.in,v 1.60 2011/11/14 18:32:34 each Exp $ srcdir = @srcdir@ VPATH = @srcdir@ @@ -22,8 +22,8 @@ top_srcdir = @top_srcdir@ @BIND9_VERSION@ HEADERS = acl.h adb.h byaddr.h cache.h callbacks.h cert.h compress.h \ - db.h dbiterator.h dbtable.h diff.h dispatch.h dlz.h \ - dnssec.h ds.h events.h fixedname.h iptable.h journal.h \ + clientinfo.h db.h dbiterator.h dbtable.h diff.h dispatch.h \ + dlz.h dnssec.h ds.h events.h fixedname.h iptable.h journal.h \ keyflags.h keytable.h keyvalues.h lib.h log.h \ master.h masterdump.h message.h name.h ncache.h nsec.h \ peer.h portlist.h private.h rbt.h rcode.h \ diff --git a/contrib/bind9/lib/dns/include/dns/acache.h b/contrib/bind9/lib/dns/include/dns/acache.h index c372ed95a37..304cba758ad 100644 --- a/contrib/bind9/lib/dns/include/dns/acache.h +++ b/contrib/bind9/lib/dns/include/dns/acache.h @@ -238,7 +238,7 @@ dns_acache_setcleaninginterval(dns_acache_t *acache, unsigned int t); */ void -dns_acache_setcachesize(dns_acache_t *acache, isc_uint32_t size); +dns_acache_setcachesize(dns_acache_t *acache, size_t size); /* * Set the maximum additional cache size. 0 means unlimited. */ diff --git a/contrib/bind9/lib/dns/include/dns/acl.h b/contrib/bind9/lib/dns/include/dns/acl.h index 41b9522f031..f4fc4a3bf9e 100644 --- a/contrib/bind9/lib/dns/include/dns/acl.h +++ b/contrib/bind9/lib/dns/include/dns/acl.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004-2007, 2009, 2011, 2012 Internet Systems Consortium, Inc. ("ISC") + * Copyright (C) 2004-2007, 2009, 2011 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 1999-2002 Internet Software Consortium. * * Permission to use, copy, modify, and/or distribute this software for any @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id$ */ +/* $Id: acl.h,v 1.35 2011/06/17 23:47:49 tbox Exp $ */ #ifndef DNS_ACL_H #define DNS_ACL_H 1 diff --git a/contrib/bind9/lib/dns/include/dns/adb.h b/contrib/bind9/lib/dns/include/dns/adb.h index b8c41dcdd48..a5a312406af 100644 --- a/contrib/bind9/lib/dns/include/dns/adb.h +++ b/contrib/bind9/lib/dns/include/dns/adb.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004-2008, 2011, 2012 Internet Systems Consortium, Inc. ("ISC") + * Copyright (C) 2004-2008, 2011, 2013 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 1999-2003 Internet Software Consortium. * * Permission to use, copy, modify, and/or distribute this software for any @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id$ */ +/* $Id: adb.h,v 1.88 2011/12/05 17:10:51 each Exp $ */ #ifndef DNS_ADB_H #define DNS_ADB_H 1 @@ -607,7 +607,7 @@ dns_adb_flush(dns_adb_t *adb); */ void -dns_adb_setadbsize(dns_adb_t *adb, isc_uint32_t size); +dns_adb_setadbsize(dns_adb_t *adb, size_t size); /*%< * Set a target memory size. If memory usage exceeds the target * size entries will be removed before they would have expired on diff --git a/contrib/bind9/lib/dns/include/dns/cache.h b/contrib/bind9/lib/dns/include/dns/cache.h index f0825be3063..f7140aa7c6c 100644 --- a/contrib/bind9/lib/dns/include/dns/cache.h +++ b/contrib/bind9/lib/dns/include/dns/cache.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004-2007, 2009, 2011, 2012 Internet Systems Consortium, Inc. ("ISC") + * Copyright (C) 2004-2007, 2009, 2011, 2013 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 1999-2001 Internet Software Consortium. * * Permission to use, copy, modify, and/or distribute this software for any @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id$ */ +/* $Id: cache.h,v 1.32 2011/08/02 23:47:52 tbox Exp $ */ #ifndef DNS_CACHE_H #define DNS_CACHE_H 1 @@ -245,12 +245,6 @@ dns_cache_getcleaninginterval(dns_cache_t *cache); * Get the periodic cache cleaning interval to 'interval' seconds. */ -isc_uint32_t -dns_cache_getcachesize(dns_cache_t *cache); -/*%< - * Get the maximum cache size. - */ - const char * dns_cache_getname(dns_cache_t *cache); /*%< @@ -258,12 +252,12 @@ dns_cache_getname(dns_cache_t *cache); */ void -dns_cache_setcachesize(dns_cache_t *cache, isc_uint32_t size); +dns_cache_setcachesize(dns_cache_t *cache, size_t size); /*%< * Set the maximum cache size. 0 means unlimited. */ -isc_uint32_t +size_t dns_cache_getcachesize(dns_cache_t *cache); /*%< * Get the maximum cache size. @@ -279,10 +273,28 @@ dns_cache_flush(dns_cache_t *cache); *\li #ISC_R_NOMEMORY */ +isc_result_t +dns_cache_flushnode(dns_cache_t *cache, dns_name_t *name, + isc_boolean_t tree); +/* + * Flush a given name from the cache. If 'tree' is true, then + * also flush all names under 'name'. + * + * Requires: + *\li 'cache' to be valid. + *\li 'name' to be valid. + * + * Returns: + *\li #ISC_R_SUCCESS + *\li #ISC_R_NOMEMORY + *\li other error returns. + */ + isc_result_t dns_cache_flushname(dns_cache_t *cache, dns_name_t *name); /* - * Flushes a given name from the cache. + * Flush a given name from the cache. Equivalent to + * dns_cache_flushpartial(cache, name, ISC_FALSE). * * Requires: *\li 'cache' to be valid. diff --git a/contrib/bind9/lib/dns/include/dns/callbacks.h b/contrib/bind9/lib/dns/include/dns/callbacks.h index b686647b7d6..5e9cb717f81 100644 --- a/contrib/bind9/lib/dns/include/dns/callbacks.h +++ b/contrib/bind9/lib/dns/include/dns/callbacks.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004-2007, 2012 Internet Systems Consortium, Inc. ("ISC") + * Copyright (C) 2004-2007, 2011 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 1999-2002 Internet Software Consortium. * * Permission to use, copy, modify, and/or distribute this software for any @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id$ */ +/* $Id: callbacks.h,v 1.26 2011/12/09 23:47:05 tbox Exp $ */ #ifndef DNS_CALLBACKS_H #define DNS_CALLBACKS_H 1 @@ -41,6 +41,14 @@ struct dns_rdatacallbacks { * dns_load_master calls this when it has rdatasets to commit. */ dns_addrdatasetfunc_t add; + + /*% + * dns_master_load*() call this when loading a raw zonefile, + * to pass back information obtained from the file header + */ + dns_rawdatafunc_t rawdata; + dns_zone_t *zone; + /*% * dns_load_master / dns_rdata_fromtext call this to issue a error. */ diff --git a/contrib/bind9/lib/dns/include/dns/clientinfo.h b/contrib/bind9/lib/dns/include/dns/clientinfo.h new file mode 100644 index 00000000000..4f2b89cda43 --- /dev/null +++ b/contrib/bind9/lib/dns/include/dns/clientinfo.h @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC") + * + * Permission to use, copy, modify, and/or 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 ISC DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL ISC 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. + */ + +/* $Id: clientinfo.h,v 1.3 2011/10/11 23:46:45 tbox Exp $ */ + +#ifndef DNS_CLIENTINFO_H +#define DNS_CLIENTINFO_H 1 + +/***** + ***** Module Info + *****/ + +/*! \file dns/clientinfo.h + * \brief + * The DNS clientinfo interface allows libdns to retrieve information + * about the client from the caller. + * + * The clientinfo interface is used by the DNS DB and DLZ interfaces; + * it allows databases to modify their answers on the basis of information + * about the client, such as source IP address. + * + * dns_clientinfo_t contains a pointer to an opaque structure containing + * client information in some form. dns_clientinfomethods_t contains a + * list of methods which operate on that opaque structure to return + * potentially useful data. Both structures also contain versioning + * information. + */ + +/***** + ***** Imports + *****/ + +#include +#include + +ISC_LANG_BEGINDECLS + +/***** + ***** Types + *****/ + +#define DNS_CLIENTINFO_VERSION 1 +typedef struct dns_clientinfo { + isc_uint16_t version; + void *data; +} dns_clientinfo_t; + +typedef isc_result_t (*dns_clientinfo_sourceip_t)(dns_clientinfo_t *client, + isc_sockaddr_t **addrp); + +#define DNS_CLIENTINFOMETHODS_VERSION 1 +#define DNS_CLIENTINFOMETHODS_AGE 0 + +typedef struct dns_clientinfomethods { + isc_uint16_t version; + isc_uint16_t age; + dns_clientinfo_sourceip_t sourceip; +} dns_clientinfomethods_t; + +/***** + ***** Methods + *****/ +void +dns_clientinfomethods_init(dns_clientinfomethods_t *methods, + dns_clientinfo_sourceip_t sourceip); + +void +dns_clientinfo_init(dns_clientinfo_t *ci, void *data); + +ISC_LANG_ENDDECLS + +#endif /* DNS_CLIENTINFO_H */ diff --git a/contrib/bind9/lib/dns/include/dns/db.h b/contrib/bind9/lib/dns/include/dns/db.h index ef090a2b861..66bc3e3481e 100644 --- a/contrib/bind9/lib/dns/include/dns/db.h +++ b/contrib/bind9/lib/dns/include/dns/db.h @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id$ */ +/* $Id: db.h,v 1.107.4.1 2011/10/23 20:12:08 vjs Exp $ */ #ifndef DNS_DB_H #define DNS_DB_H 1 @@ -59,6 +59,7 @@ #include #include +#include #include #include #include @@ -179,6 +180,20 @@ typedef struct dns_dbmethods { dns_rdataset_t *ardataset, dns_rpz_st_t *st, dns_name_t *query_qname); + isc_result_t (*findnodeext)(dns_db_t *db, dns_name_t *name, + isc_boolean_t create, + dns_clientinfomethods_t *methods, + dns_clientinfo_t *clientinfo, + dns_dbnode_t **nodep); + isc_result_t (*findext)(dns_db_t *db, dns_name_t *name, + dns_dbversion_t *version, + dns_rdatatype_t type, unsigned int options, + isc_stdtime_t now, + dns_dbnode_t **nodep, dns_name_t *foundname, + dns_clientinfomethods_t *methods, + dns_clientinfo_t *clientinfo, + dns_rdataset_t *rdataset, + dns_rdataset_t *sigrdataset); } dns_dbmethods_t; typedef isc_result_t @@ -660,9 +675,19 @@ dns_db_closeversion(dns_db_t *db, dns_dbversion_t **versionp, isc_result_t dns_db_findnode(dns_db_t *db, dns_name_t *name, isc_boolean_t create, dns_dbnode_t **nodep); + +isc_result_t +dns_db_findnodeext(dns_db_t *db, dns_name_t *name, isc_boolean_t create, + dns_clientinfomethods_t *methods, + dns_clientinfo_t *clientinfo, dns_dbnode_t **nodep); /*%< * Find the node with name 'name'. * + * dns_db_findnodeext() (findnode extended) also accepts parameters + * 'methods' and 'clientinfo', which, when provided, enable the database to + * retreive information about the client from the caller, and modify its + * response on the basis of that information. + * * Notes: * \li If 'create' is ISC_TRUE and no node with name 'name' exists, then * such a node will be created. @@ -699,9 +724,21 @@ dns_db_find(dns_db_t *db, dns_name_t *name, dns_dbversion_t *version, dns_rdatatype_t type, unsigned int options, isc_stdtime_t now, dns_dbnode_t **nodep, dns_name_t *foundname, dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset); + +isc_result_t +dns_db_findext(dns_db_t *db, dns_name_t *name, dns_dbversion_t *version, + dns_rdatatype_t type, unsigned int options, isc_stdtime_t now, + dns_dbnode_t **nodep, dns_name_t *foundname, + dns_clientinfomethods_t *methods, dns_clientinfo_t *clientinfo, + dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset); /*%< * Find the best match for 'name' and 'type' in version 'version' of 'db'. * + * dns_db_findext() (find extended) also accepts parameters 'methods' + * and 'clientinfo', which when provided enable the database to retreive + * information about the client from the caller, and modify its response + * on the basis of this information. + * * Notes: * * \li If type == dns_rdataset_any, then rdataset will not be bound. @@ -733,6 +770,10 @@ dns_db_find(dns_db_t *db, dns_name_t *name, dns_dbversion_t *version, * that it is correct. This only affects answers returned from the * cache. * + * \li In the #DNS_DBFIND_FORCENSEC3 option is set, then we are looking + * in the NSEC3 tree and not the main tree. Without this option being + * set NSEC3 records will not be found. + * * \li To respond to a query for SIG records, the caller should create a * rdataset iterator and extract the signatures from each rdataset. * @@ -1048,6 +1089,7 @@ dns_db_findrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version, dns_rdatatype_t type, dns_rdatatype_t covers, isc_stdtime_t now, dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset); + /*%< * Search for an rdataset of type 'type' at 'node' that are in version * 'version' of 'db'. If found, make 'rdataset' refer to it. diff --git a/contrib/bind9/lib/dns/include/dns/dispatch.h b/contrib/bind9/lib/dns/include/dns/dispatch.h index 3f881dfcf1d..1235f7ca40f 100644 --- a/contrib/bind9/lib/dns/include/dns/dispatch.h +++ b/contrib/bind9/lib/dns/include/dns/dispatch.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004-2009, 2012 Internet Systems Consortium, Inc. ("ISC") + * Copyright (C) 2004-2009, 2011, 2012 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 1999-2003 Internet Software Consortium. * * Permission to use, copy, modify, and/or distribute this software for any @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id$ */ +/* $Id: dispatch.h,v 1.64 2011/07/28 23:47:58 tbox Exp $ */ #ifndef DNS_DISPATCH_H #define DNS_DISPATCH_H 1 @@ -54,6 +54,7 @@ #include #include +#include #include #include @@ -88,6 +89,18 @@ struct dns_dispatchevent { isc_uint32_t attributes; /*%< mirrored from socket.h */ }; +/*% + * This is a set of one or more dispatches which can be retrieved + * round-robin fashion. + */ +struct dns_dispatchset { + isc_mem_t *mctx; + dns_dispatch_t **dispatches; + int ndisp; + int cur; + isc_mutex_t lock; +}; + /*@{*/ /*% * Attributes for added dispatchers. @@ -245,6 +258,15 @@ dns_dispatch_getudp(dns_dispatchmgr_t *mgr, isc_socketmgr_t *sockmgr, unsigned int buckets, unsigned int increment, unsigned int attributes, unsigned int mask, dns_dispatch_t **dispp); + +isc_result_t +dns_dispatch_getudp_dup(dns_dispatchmgr_t *mgr, isc_socketmgr_t *sockmgr, + isc_taskmgr_t *taskmgr, isc_sockaddr_t *localaddr, + unsigned int buffersize, + unsigned int maxbuffers, unsigned int maxrequests, + unsigned int buckets, unsigned int increment, + unsigned int attributes, unsigned int mask, + dns_dispatch_t **dispp, dns_dispatch_t *dup); /*%< * Attach to existing dns_dispatch_t if one is found with dns_dispatchmgr_find, * otherwise create a new UDP dispatch. @@ -496,6 +518,46 @@ dns_dispatch_importrecv(dns_dispatch_t *disp, isc_event_t *event); * event != NULL */ +dns_dispatch_t * +dns_dispatchset_get(dns_dispatchset_t *dset); +/*%< + * Retrieve the next dispatch from dispatch set 'dset', and increment + * the round-robin counter. + * + * Requires: + *\li dset != NULL + */ + +isc_result_t +dns_dispatchset_create(isc_mem_t *mctx, isc_socketmgr_t *sockmgr, + isc_taskmgr_t *taskmgr, dns_dispatch_t *source, + dns_dispatchset_t **dsetp, int n); +/*%< + * Given a valid dispatch 'source', create a dispatch set containing + * 'n' UDP dispatches, with the remainder filled out by clones of the + * source. + * + * Requires: + *\li source is a valid UDP dispatcher + *\li dsetp != NULL, *dsetp == NULL + */ + +void +dns_dispatchset_cancelall(dns_dispatchset_t *dset, isc_task_t *task); +/*%< + * Cancel socket operations for the dispatches in 'dset'. + */ + +void +dns_dispatchset_destroy(dns_dispatchset_t **dsetp); +/*%< + * Dereference all the dispatches in '*dsetp', free the dispatchset + * memory, and set *dsetp to NULL. + * + * Requires: + *\li dset is valid + */ + ISC_LANG_ENDDECLS #endif /* DNS_DISPATCH_H */ diff --git a/contrib/bind9/lib/dns/include/dns/dlz_dlopen.h b/contrib/bind9/lib/dns/include/dns/dlz_dlopen.h index 6ad7e7a33ae..f87722c3db8 100644 --- a/contrib/bind9/lib/dns/include/dns/dlz_dlopen.h +++ b/contrib/bind9/lib/dns/include/dns/dlz_dlopen.h @@ -30,7 +30,7 @@ ISC_LANG_BEGINDECLS * for the entry points of an external DLZ module for bind9. */ -#define DLZ_DLOPEN_VERSION 1 +#define DLZ_DLOPEN_VERSION 2 /* * dlz_dlopen_version() is required for all DLZ external drivers. It @@ -65,7 +65,9 @@ typedef isc_result_t dlz_dlopen_findzonedb_t (void *dbdata, typedef isc_result_t dlz_dlopen_lookup_t (const char *zone, const char *name, void *dbdata, - dns_sdlzlookup_t *lookup); + dns_sdlzlookup_t *lookup, + dns_clientinfomethods_t *methods, + dns_clientinfo_t *clientinfo); /* * dlz_dlopen_authority is optional() if dlz_dlopen_lookup() @@ -115,6 +117,15 @@ typedef void dlz_dlopen_closeversion_t (const char *zone, typedef isc_result_t dlz_dlopen_configure_t (dns_view_t *view, void *dbdata); +/* + * dlz_dlopen_setclientcallback() is optional, but must be supplied if you + * want to retrieve information about the client (e.g., source address) + * before sending a replay. + */ +typedef isc_result_t dlz_dlopen_setclientcallback_t (dns_view_t *view, + void *dbdata); + + /* * dlz_dlopen_ssumatch() is optional, but must be supplied if you want * to support dynamic updates diff --git a/contrib/bind9/lib/dns/include/dns/dnssec.h b/contrib/bind9/lib/dns/include/dns/dnssec.h index e986d406f61..e443f91b635 100644 --- a/contrib/bind9/lib/dns/include/dns/dnssec.h +++ b/contrib/bind9/lib/dns/include/dns/dnssec.h @@ -132,12 +132,19 @@ isc_result_t dns_dnssec_verify2(dns_name_t *name, dns_rdataset_t *set, dst_key_t *key, isc_boolean_t ignoretime, isc_mem_t *mctx, dns_rdata_t *sigrdata, dns_name_t *wild); + +isc_result_t +dns_dnssec_verify3(dns_name_t *name, dns_rdataset_t *set, dst_key_t *key, + isc_boolean_t ignoretime, unsigned int maxbits, + isc_mem_t *mctx, dns_rdata_t *sigrdata, dns_name_t *wild); /*%< * Verifies the RRSIG record covering this rdataset signed by a specific * key. This does not determine if the key's owner is authorized to sign * this record, as this requires a resolver or database. * If 'ignoretime' is ISC_TRUE, temporal validity will not be checked. * + * 'maxbits' specifies the maximum number of rsa exponent bits accepted. + * * Requires: *\li 'name' (the owner name of the record) is a valid name *\li 'set' is a valid rdataset @@ -309,7 +316,7 @@ dns_dnssec_keylistfromrdataset(dns_name_t *origin, isc_result_t dns_dnssec_updatekeys(dns_dnsseckeylist_t *keys, dns_dnsseckeylist_t *newkeys, dns_dnsseckeylist_t *removed, dns_name_t *origin, - dns_ttl_t ttl, dns_diff_t *diff, isc_boolean_t allzsk, + dns_ttl_t hint_ttl, dns_diff_t *diff, isc_boolean_t allzsk, isc_mem_t *mctx, void (*report)(const char *, ...)); /*%< * Update the list of keys in 'keys' with new key information in 'newkeys'. @@ -328,9 +335,11 @@ dns_dnssec_updatekeys(dns_dnsseckeylist_t *keys, dns_dnsseckeylist_t *newkeys, * If 'allzsk' is true, we are allowing KSK-flagged keys to be used as * ZSKs. * - * 'ttl' is the TTL of the DNSKEY RRset; if it is longer than the - * time until a new key will be activated, then we have to delay the - * key's activation. + * 'hint_ttl' is the TTL to use for the DNSKEY RRset if there is no + * existing RRset, and if none of the keys to be added has a default TTL + * (in which case we would use the shortest one). If the TTL is longer + * than the time until a new key will be activated, then we have to delay + * the key's activation. * * 'report' points to a function for reporting status. * diff --git a/contrib/bind9/lib/dns/include/dns/events.h b/contrib/bind9/lib/dns/include/dns/events.h index 3a28ab2aec1..fd2144f6493 100644 --- a/contrib/bind9/lib/dns/include/dns/events.h +++ b/contrib/bind9/lib/dns/include/dns/events.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004-2007, 2009, 2010, 2012 Internet Systems Consortium, Inc. ("ISC") + * Copyright (C) 2004-2007, 2009-2011 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 1999-2002 Internet Software Consortium. * * Permission to use, copy, modify, and/or distribute this software for any @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id$ */ +/* $Id: events.h,v 1.61 2011/10/28 06:20:06 each Exp $ */ #ifndef DNS_EVENTS_H #define DNS_EVENTS_H 1 @@ -74,6 +74,11 @@ #define DNS_EVENT_CLIENTREQDONE (ISC_EVENTCLASS_DNS + 44) #define DNS_EVENT_ADBGROWENTRIES (ISC_EVENTCLASS_DNS + 45) #define DNS_EVENT_ADBGROWNAMES (ISC_EVENTCLASS_DNS + 46) +#define DNS_EVENT_ZONESECURESERIAL (ISC_EVENTCLASS_DNS + 47) +#define DNS_EVENT_ZONESECUREDB (ISC_EVENTCLASS_DNS + 48) +#define DNS_EVENT_ZONELOAD (ISC_EVENTCLASS_DNS + 49) +#define DNS_EVENT_KEYDONE (ISC_EVENTCLASS_DNS + 50) +#define DNS_EVENT_SETNSEC3PARAM (ISC_EVENTCLASS_DNS + 51) #define DNS_EVENT_FIRSTEVENT (ISC_EVENTCLASS_DNS + 0) #define DNS_EVENT_LASTEVENT (ISC_EVENTCLASS_DNS + 65535) diff --git a/contrib/bind9/lib/dns/include/dns/journal.h b/contrib/bind9/lib/dns/include/dns/journal.h index d750fbef3dd..68ba8b35ae9 100644 --- a/contrib/bind9/lib/dns/include/dns/journal.h +++ b/contrib/bind9/lib/dns/include/dns/journal.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004-2009, 2012 Internet Systems Consortium, Inc. ("ISC") + * Copyright (C) 2004-2009, 2011 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 1999-2001 Internet Software Consortium. * * Permission to use, copy, modify, and/or distribute this software for any @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id$ */ +/* $Id: journal.h,v 1.43 2011/12/22 07:32:41 each Exp $ */ #ifndef DNS_JOURNAL_H #define DNS_JOURNAL_H 1 @@ -46,6 +46,10 @@ ***/ #define DNS_JOURNALOPT_RESIGN 0x00000001 +#define DNS_JOURNAL_READ 0x00000000 /* ISC_FALSE */ +#define DNS_JOURNAL_CREATE 0x00000001 /* ISC_TRUE */ +#define DNS_JOURNAL_WRITE 0x00000002 + /*** *** Types ***/ @@ -72,7 +76,7 @@ ISC_LANG_BEGINDECLS isc_result_t dns_db_createsoatuple(dns_db_t *db, dns_dbversion_t *ver, isc_mem_t *mctx, - dns_diffop_t op, dns_difftuple_t **tp); + dns_diffop_t op, dns_difftuple_t **tp); /*!< brief * Create a diff tuple for the current database SOA. * XXX this probably belongs somewhere else. @@ -95,16 +99,15 @@ dns_db_createsoatuple(dns_db_t *db, dns_dbversion_t *ver, isc_mem_t *mctx, */ isc_result_t -dns_journal_open(isc_mem_t *mctx, const char *filename, isc_boolean_t write, +dns_journal_open(isc_mem_t *mctx, const char *filename, unsigned int mode, dns_journal_t **journalp); /*%< * Open the journal file 'filename' and create a dns_journal_t object for it. * - * If 'write' is ISC_TRUE, the journal is open for writing. If it does - * not exist, it is created. - * - * If 'write' is ISC_FALSE, the journal is open for reading. If it does - * not exist, ISC_R_NOTFOUND is returned. + * DNS_JOURNAL_CREATE open the journal for reading and writing and create + * the journal if it does not exist. + * DNS_JOURNAL_WRITE open the journal for reading and writing. + * DNS_JOURNAL_READ open the journal for reading only. */ void @@ -267,12 +270,18 @@ dns_db_diff(isc_mem_t *mctx, dns_db_t *dba, dns_dbversion_t *dbvera, dns_db_t *dbb, dns_dbversion_t *dbverb, const char *journal_filename); + +isc_result_t +dns_db_diffx(dns_diff_t *diff, dns_db_t *dba, dns_dbversion_t *dbvera, + dns_db_t *dbb, dns_dbversion_t *dbverb, + const char *journal_filename); /*%< - * Compare the databases 'dba' and 'dbb' and generate a journal + * Compare the databases 'dba' and 'dbb' and generate a diff/journal * entry containing the changes to make 'dba' from 'dbb' (note * the order). This journal entry will consist of a single, * possibly very large transaction. Append the journal - * entry to the journal file specified by 'journal_filename'. + * entry to the journal file specified by 'journal_filename' if + * non-NULL. */ isc_result_t @@ -284,6 +293,17 @@ dns_journal_compact(isc_mem_t *mctx, char *filename, isc_uint32_t serial, * exists and is non-empty 'serial' must exist in the journal. */ +isc_boolean_t +dns_journal_get_sourceserial(dns_journal_t *j, isc_uint32_t *sourceserial); +void +dns_journal_set_sourceserial(dns_journal_t *j, isc_uint32_t sourceserial); +/*%< + * Get and set source serial. + * + * Returns: + * ISC_TRUE if sourceserial has previously been set. + */ + ISC_LANG_ENDDECLS #endif /* DNS_JOURNAL_H */ diff --git a/contrib/bind9/lib/dns/include/dns/log.h b/contrib/bind9/lib/dns/include/dns/log.h index 689b148eb8f..3c4df8a4500 100644 --- a/contrib/bind9/lib/dns/include/dns/log.h +++ b/contrib/bind9/lib/dns/include/dns/log.h @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id$ */ +/* $Id: log.h,v 1.47 2011/10/13 22:48:24 tbox Exp $ */ /*! \file dns/log.h * \author Principal Authors: DCL */ diff --git a/contrib/bind9/lib/dns/include/dns/master.h b/contrib/bind9/lib/dns/include/dns/master.h index a852ae4b2aa..896c6e95ecd 100644 --- a/contrib/bind9/lib/dns/include/dns/master.h +++ b/contrib/bind9/lib/dns/include/dns/master.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004-2009, 2012 Internet Systems Consortium, Inc. ("ISC") + * Copyright (C) 2004-2009, 2011, 2012 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 1999-2002 Internet Software Consortium. * * Permission to use, copy, modify, and/or distribute this software for any @@ -66,18 +66,29 @@ ISC_LANG_BEGINDECLS * encoding, we directly read/write each field so that the encoded data * is always "packed", regardless of the hardware architecture. */ -#define DNS_RAWFORMAT_VERSION 0 +#define DNS_RAWFORMAT_VERSION 1 + +/* + * Flags to indicate the status of the data in the raw file header + */ +#define DNS_MASTERRAW_COMPAT 0x01 +#define DNS_MASTERRAW_SOURCESERIALSET 0x02 +#define DNS_MASTERRAW_LASTXFRINSET 0x04 /* Common header */ -typedef struct { +struct dns_masterrawheader { isc_uint32_t format; /* must be * dns_masterformat_raw */ isc_uint32_t version; /* compatibility for future * extensions */ isc_uint32_t dumptime; /* timestamp on creation - * (currently unused) - */ -} dns_masterrawheader_t; + * (currently unused) */ + isc_uint32_t flags; /* Flags */ + isc_uint32_t sourceserial; /* Source serial number (used + * by inline-signing zones) */ + isc_uint32_t lastxfrin; /* timestamp of last transfer + * (used by slave zones) */ +}; /* The structure for each RRset */ typedef struct { @@ -302,6 +313,12 @@ dns_loadctx_cancel(dns_loadctx_t *ctx); *\li 'ctx' to be valid */ +void +dns_master_initrawheader(dns_masterrawheader_t *header); +/*%< + * Initializes the header for a raw master file, setting all + * values to zero. + */ ISC_LANG_ENDDECLS #endif /* DNS_MASTER_H */ diff --git a/contrib/bind9/lib/dns/include/dns/masterdump.h b/contrib/bind9/lib/dns/include/dns/masterdump.h index f7e30f13d53..8631248cc2e 100644 --- a/contrib/bind9/lib/dns/include/dns/masterdump.h +++ b/contrib/bind9/lib/dns/include/dns/masterdump.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004-2008, 2011, 2012 Internet Systems Consortium, Inc. ("ISC") + * Copyright (C) 2004-2008, 2011 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 1999-2002 Internet Software Consortium. * * Permission to use, copy, modify, and/or distribute this software for any @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id$ */ +/* $Id: masterdump.h,v 1.47 2011/12/08 23:46:49 tbox Exp $ */ #ifndef DNS_MASTERDUMP_H #define DNS_MASTERDUMP_H 1 @@ -220,13 +220,25 @@ dns_master_dumptostream2(isc_mem_t *mctx, dns_db_t *db, dns_dbversion_t *version, const dns_master_style_t *style, dns_masterformat_t format, FILE *f); + +isc_result_t +dns_master_dumptostream3(isc_mem_t *mctx, dns_db_t *db, + dns_dbversion_t *version, + const dns_master_style_t *style, + dns_masterformat_t format, + dns_masterrawheader_t *header, FILE *f); /*%< * Dump the database 'db' to the steam 'f' in the specified format by * 'format'. If the format is dns_masterformat_text (the RFC1035 format), * 'style' specifies the file style (e.g., &dns_master_style_default). * - * dns_master_dumptostream() is an old form of dns_master_dumptostream2(), + * dns_master_dumptostream() is an old form of dns_master_dumptostream3(), * which always specifies the dns_masterformat_text format. + * dns_master_dumptostream2() is an old form which always specifies + * a NULL header. + * + * If 'format' is dns_masterformat_raw, then 'header' can contain + * information to be written to the file header. * * Temporary dynamic memory may be allocated from 'mctx'. * @@ -256,6 +268,13 @@ dns_master_dumpinc2(isc_mem_t *mctx, dns_db_t *db, dns_dbversion_t *version, const dns_master_style_t *style, const char *filename, isc_task_t *task, dns_dumpdonefunc_t done, void *done_arg, dns_dumpctx_t **dctxp, dns_masterformat_t format); +isc_result_t +dns_master_dumpinc3(isc_mem_t *mctx, dns_db_t *db, dns_dbversion_t *version, + const dns_master_style_t *style, const char *filename, + isc_task_t *task, dns_dumpdonefunc_t done, void + *done_arg, dns_dumpctx_t **dctxp, + dns_masterformat_t format, dns_masterrawheader_t *header); + isc_result_t dns_master_dump(isc_mem_t *mctx, dns_db_t *db, dns_dbversion_t *version, @@ -267,14 +286,24 @@ dns_master_dump2(isc_mem_t *mctx, dns_db_t *db, const dns_master_style_t *style, const char *filename, dns_masterformat_t format); +isc_result_t +dns_master_dump3(isc_mem_t *mctx, dns_db_t *db, + dns_dbversion_t *version, + const dns_master_style_t *style, const char *filename, + dns_masterformat_t format, dns_masterrawheader_t *header); + /*%< * Dump the database 'db' to the file 'filename' in the specified format by * 'format'. If the format is dns_masterformat_text (the RFC1035 format), * 'style' specifies the file style (e.g., &dns_master_style_default). * - * dns_master_dumpinc() and dns_master_dump() are old forms of _dumpinc2() - * and _dump2(), respectively, which always specify the dns_masterformat_text - * format. + * dns_master_dumpinc() and dns_master_dump() are old forms of _dumpinc3() + * and _dump3(), respectively, which always specify the dns_masterformat_text + * format. dns_master_dumpinc2() and dns_master_dump2() are old forms which + * always specify a NULL header. + * + * If 'format' is dns_masterformat_raw, then 'header' can contain + * information to be written to the file header. * * Temporary dynamic memory may be allocated from 'mctx'. * @@ -329,6 +358,12 @@ dns_master_stylecreate(dns_master_style_t **style, unsigned int flags, unsigned int line_length, unsigned int tab_width, isc_mem_t *mctx); +isc_result_t +dns_master_stylecreate2(dns_master_style_t **style, unsigned int flags, + unsigned int ttl_column, unsigned int class_column, + unsigned int type_column, unsigned int rdata_column, + unsigned int line_length, unsigned int tab_width, + unsigned int split_width, isc_mem_t *mctx); void dns_master_styledestroy(dns_master_style_t **style, isc_mem_t *mctx); diff --git a/contrib/bind9/lib/dns/include/dns/nsec.h b/contrib/bind9/lib/dns/include/dns/nsec.h index 510d96baec2..440ee4e0151 100644 --- a/contrib/bind9/lib/dns/include/dns/nsec.h +++ b/contrib/bind9/lib/dns/include/dns/nsec.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004-2008, 2012 Internet Systems Consortium, Inc. ("ISC") + * Copyright (C) 2004-2008, 2011, 2012 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 1999-2001, 2003 Internet Software Consortium. * * Permission to use, copy, modify, and/or distribute this software for any @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id$ */ +/* $Id: nsec.h,v 1.14 2011/06/10 23:47:32 tbox Exp $ */ #ifndef DNS_NSEC_H #define DNS_NSEC_H 1 @@ -69,12 +69,35 @@ dns_nsec_nseconly(dns_db_t *db, dns_dbversion_t *version, isc_boolean_t *answer); /* * Report whether the DNSKEY RRset has a NSEC only algorithm. Unknown - * algorithms are assumed to support NSEC3. + * algorithms are assumed to support NSEC3. If DNSKEY is not found, + * *answer is set to ISC_FALSE, and ISC_R_NOTFOUND is returned. * * Requires: * 'answer' to be non NULL. */ +unsigned int +dns_nsec_compressbitmap(unsigned char *map, const unsigned char *raw, + unsigned int max_type); +/*%< + * Convert a raw bitmap into a compressed windowed bit map. 'map' and 'raw' + * may overlap. + * + * Returns the length of the compressed windowed bit map. + */ + +void +dns_nsec_setbit(unsigned char *array, unsigned int type, unsigned int bit); +/*%< + * Set type bit in raw 'array' to 'bit'. + */ + +isc_boolean_t +dns_nsec_isset(const unsigned char *array, unsigned int type); +/*%< + * Test if the corresponding 'type' bit is set in 'array'. + */ + isc_result_t dns_nsec_noexistnodata(dns_rdatatype_t type, dns_name_t *name, dns_name_t *nsecname, dns_rdataset_t *nsecset, diff --git a/contrib/bind9/lib/dns/include/dns/nsec3.h b/contrib/bind9/lib/dns/include/dns/nsec3.h index 588dd053c06..e4a22868a2d 100644 --- a/contrib/bind9/lib/dns/include/dns/nsec3.h +++ b/contrib/bind9/lib/dns/include/dns/nsec3.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008-2010, 2012 Internet Systems Consortium, Inc. ("ISC") + * Copyright (C) 2008-2012 Internet Systems Consortium, Inc. ("ISC") * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -14,7 +14,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id$ */ +/* $Id: nsec3.h,v 1.14 2011/10/28 12:20:31 tbox Exp $ */ #ifndef DNS_NSEC3_H #define DNS_NSEC3_H 1 @@ -241,7 +241,8 @@ dns_nsec3param_toprivate(dns_rdata_t *src, dns_rdata_t *target, isc_result_t dns_nsec3param_deletechains(dns_db_t *db, dns_dbversion_t *ver, - dns_zone_t *zone, dns_diff_t *diff); + dns_zone_t *zone, isc_boolean_t nonsec, + dns_diff_t *diff); /*%< * Mark NSEC3PARAM for deletion. diff --git a/contrib/bind9/lib/dns/include/dns/private.h b/contrib/bind9/lib/dns/include/dns/private.h index 7bc59b2cea6..c4a2ae64f71 100644 --- a/contrib/bind9/lib/dns/include/dns/private.h +++ b/contrib/bind9/lib/dns/include/dns/private.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009, 2012 Internet Systems Consortium, Inc. ("ISC") + * Copyright (C) 2009, 2011, 2012 Internet Systems Consortium, Inc. ("ISC") * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -14,7 +14,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id$ */ +/* $Id: private.h,v 1.5 2011/10/28 12:20:31 tbox Exp $ */ #include #include @@ -50,6 +50,23 @@ dns_private_chains(dns_db_t *db, dns_dbversion_t *ver, * \li other on error */ +isc_result_t +dns_private_totext(dns_rdata_t *privaterdata, isc_buffer_t *buffer); +/*%< + * Convert a private-type RR 'privaterdata' to human-readable form, + * and place the result in 'buffer'. The text should indicate + * which action the private-type record specifies and whether the + * action has been completed. + * + * Requires: + * \li 'privaterdata' is a valid rdata containing at least five bytes + * \li 'buffer' is a valid buffer + * + * Returns: + * \li ISC_R_SUCCESS + * \li other on error + */ + ISC_LANG_ENDDECLS #endif diff --git a/contrib/bind9/lib/dns/include/dns/rdata.h b/contrib/bind9/lib/dns/include/dns/rdata.h index 2a67dc90521..89ecaf80069 100644 --- a/contrib/bind9/lib/dns/include/dns/rdata.h +++ b/contrib/bind9/lib/dns/include/dns/rdata.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004-2009, 2012 Internet Systems Consortium, Inc. ("ISC") + * Copyright (C) 2004-2009, 2011, 2012 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 1998-2003 Internet Software Consortium. * * Permission to use, copy, modify, and/or distribute this software for any @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id$ */ +/* $Id: rdata.h,v 1.80 2011/03/20 02:31:53 marka Exp $ */ #ifndef DNS_RDATA_H #define DNS_RDATA_H 1 @@ -169,6 +169,7 @@ struct dns_rdata { /*% Output explanatory comments. */ #define DNS_STYLEFLAG_COMMENT 0x00000002U +#define DNS_STYLEFLAG_RRCOMMENT 0x00000004U #define DNS_RDATA_DOWNCASE DNS_NAME_DOWNCASE #define DNS_RDATA_CHECKNAMES DNS_NAME_CHECKNAMES @@ -435,8 +436,8 @@ dns_rdata_totext(dns_rdata_t *rdata, dns_name_t *origin, isc_buffer_t *target); isc_result_t dns_rdata_tofmttext(dns_rdata_t *rdata, dns_name_t *origin, unsigned int flags, - unsigned int width, const char *linebreak, - isc_buffer_t *target); + unsigned int width, unsigned int split_width, + const char *linebreak, isc_buffer_t *target); /*%< * Like dns_rdata_totext, but do formatted output suitable for * database dumps. This is intended for use by dns_db_dump(); @@ -458,6 +459,11 @@ dns_rdata_tofmttext(dns_rdata_t *rdata, dns_name_t *origin, unsigned int flags, * comments next to things like the SOA timer fields. Some * comments (e.g., the SOA ones) are only printed when multiline * output is selected. + * + * base64 rdata text (e.g., DNSKEY records) will be split into chunks + * of 'split_width' characters. If split_width == 0, the text will + * not be split at all. If split_width == UINT_MAX (0xffffffff), then + * it is undefined and falls back to the default value of 'width' */ isc_result_t diff --git a/contrib/bind9/lib/dns/include/dns/rdataset.h b/contrib/bind9/lib/dns/include/dns/rdataset.h index b2b8370db05..31bcd15f142 100644 --- a/contrib/bind9/lib/dns/include/dns/rdataset.h +++ b/contrib/bind9/lib/dns/include/dns/rdataset.h @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id$ */ +/* $Id: rdataset.h,v 1.72 2011/06/08 22:13:51 each Exp $ */ #ifndef DNS_RDATASET_H #define DNS_RDATASET_H 1 diff --git a/contrib/bind9/lib/dns/include/dns/resolver.h b/contrib/bind9/lib/dns/include/dns/resolver.h index 7f7d09317c8..095269ea2dd 100644 --- a/contrib/bind9/lib/dns/include/dns/resolver.h +++ b/contrib/bind9/lib/dns/include/dns/resolver.h @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id$ */ +/* $Id: resolver.h,v 1.72 2011/12/05 17:10:51 each Exp $ */ #ifndef DNS_RESOLVER_H #define DNS_RESOLVER_H 1 @@ -126,7 +126,8 @@ typedef struct dns_fetchevent { isc_result_t dns_resolver_create(dns_view_t *view, - isc_taskmgr_t *taskmgr, unsigned int ntasks, + isc_taskmgr_t *taskmgr, + unsigned int ntasks, unsigned int ndisp, isc_socketmgr_t *socketmgr, isc_timermgr_t *timermgr, unsigned int options, @@ -155,9 +156,11 @@ dns_resolver_create(dns_view_t *view, * *\li 'timermgr' is a valid timer manager. * - *\li 'dispatchv4' is a valid dispatcher with an IPv4 UDP socket, or is NULL. + *\li 'dispatchv4' is a dispatch with an IPv4 UDP socket, or is NULL. + * If not NULL, 'ndisp' clones of it will be created by the resolver. * - *\li 'dispatchv6' is a valid dispatcher with an IPv6 UDP socket, or is NULL. + *\li 'dispatchv6' is a dispatch with an IPv6 UDP socket, or is NULL. + * If not NULL, 'ndisp' clones of it will be created by the resolver. * *\li resp != NULL && *resp == NULL. * diff --git a/contrib/bind9/lib/dns/include/dns/result.h b/contrib/bind9/lib/dns/include/dns/result.h index 9a7d2c23117..12aacf9ba78 100644 --- a/contrib/bind9/lib/dns/include/dns/result.h +++ b/contrib/bind9/lib/dns/include/dns/result.h @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id$ */ +/* $Id: result.h,v 1.123 2011/03/21 07:22:14 each Exp $ */ #ifndef DNS_RESULT_H #define DNS_RESULT_H 1 diff --git a/contrib/bind9/lib/dns/include/dns/rpz.h b/contrib/bind9/lib/dns/include/dns/rpz.h index ceea26d04ba..e1d50a53b55 100644 --- a/contrib/bind9/lib/dns/include/dns/rpz.h +++ b/contrib/bind9/lib/dns/include/dns/rpz.h @@ -16,6 +16,7 @@ /* $Id$ */ + #ifndef DNS_RPZ_H #define DNS_RPZ_H 1 diff --git a/contrib/bind9/lib/dns/include/dns/rriterator.h b/contrib/bind9/lib/dns/include/dns/rriterator.h index a3e8e479b92..c979f224999 100644 --- a/contrib/bind9/lib/dns/include/dns/rriterator.h +++ b/contrib/bind9/lib/dns/include/dns/rriterator.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009, 2011, 2012 Internet Systems Consortium, Inc. ("ISC") + * Copyright (C) 2009, 2011 Internet Systems Consortium, Inc. ("ISC") * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -14,7 +14,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id$ */ +/* $Id: rriterator.h,v 1.4 2011/11/01 23:47:00 tbox Exp $ */ #ifndef DNS_RRITERATOR_H #define DNS_RRITERATOR_H 1 diff --git a/contrib/bind9/lib/dns/include/dns/sdb.h b/contrib/bind9/lib/dns/include/dns/sdb.h index c57c4a1d038..27519034f17 100644 --- a/contrib/bind9/lib/dns/include/dns/sdb.h +++ b/contrib/bind9/lib/dns/include/dns/sdb.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004-2007, 2009, 2012 Internet Systems Consortium, Inc. ("ISC") + * Copyright (C) 2004-2007, 2009, 2011, 2012 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 2000, 2001 Internet Software Consortium. * * Permission to use, copy, modify, and/or distribute this software for any @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id$ */ +/* $Id: sdb.h,v 1.25 2011/10/11 23:46:45 tbox Exp $ */ #ifndef DNS_SDB_H #define DNS_SDB_H 1 @@ -35,6 +35,7 @@ #include +#include #include /*** @@ -58,10 +59,14 @@ typedef struct dns_sdballnodes dns_sdballnodes_t; typedef isc_result_t (*dns_sdblookupfunc_t)(const char *zone, const char *name, void *dbdata, - dns_sdblookup_t *); + dns_sdblookup_t *lookup, + dns_clientinfomethods_t *methods, + dns_clientinfo_t *clientinfo); typedef isc_result_t (*dns_sdblookup2func_t)(const dns_name_t *zone, const dns_name_t *name, - void *dbdata, dns_sdblookup_t *lookup); + void *dbdata, dns_sdblookup_t *lookup, + dns_clientinfomethods_t *methods, + dns_clientinfo_t *clientinfo); typedef isc_result_t (*dns_sdbauthorityfunc_t)(const char *zone, void *dbdata, dns_sdblookup_t *); diff --git a/contrib/bind9/lib/dns/include/dns/sdlz.h b/contrib/bind9/lib/dns/include/dns/sdlz.h index 375a99a6d69..fbc6b95e70d 100644 --- a/contrib/bind9/lib/dns/include/dns/sdlz.h +++ b/contrib/bind9/lib/dns/include/dns/sdlz.h @@ -57,6 +57,7 @@ #ifndef SDLZ_H #define SDLZ_H 1 +#include #include ISC_LANG_BEGINDECLS @@ -182,18 +183,23 @@ typedef isc_result_t typedef isc_result_t (*dns_sdlzlookupfunc_t)(const char *zone, const char *name, void *driverarg, - void *dbdata, dns_sdlzlookup_t *lookup); + void *dbdata, dns_sdlzlookup_t *lookup, + dns_clientinfomethods_t *methods, + dns_clientinfo_t *clientinfo); /*%< * Method prototype. Drivers implementing the SDLZ interface MUST - * supply a lookup method. This method is called when the DNS server - * is performing a query, after the find zone and before any other - * methods have been called. This function returns record DNS record + * supply a lookup method. This method is called when the + * DNS server is performing a query, after the find zone and before any + * other methods have been called. This function returns DNS record * information using the dns_sdlz_putrr and dns_sdlz_putsoa functions. * If this function supplies authority information for the DNS record * the authority method is not required. If it does not, the - * authority function is required. A SDLZ driver must implement a - * lookup method. + * authority function is required. + * + * The 'methods' and 'clientinfo' args allow an SDLZ driver to retrieve + * information about the querying client (such as source IP address) + * from the caller. */ typedef isc_result_t (*dns_sdlznewversion_t)(const char *zone, diff --git a/contrib/bind9/lib/dns/include/dns/time.h b/contrib/bind9/lib/dns/include/dns/time.h index b646ba80edb..6a59c8a056e 100644 --- a/contrib/bind9/lib/dns/include/dns/time.h +++ b/contrib/bind9/lib/dns/include/dns/time.h @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id$ */ +/* $Id: time.h,v 1.19 2012/01/27 23:46:58 tbox Exp $ */ #ifndef DNS_TIME_H #define DNS_TIME_H 1 diff --git a/contrib/bind9/lib/dns/include/dns/types.h b/contrib/bind9/lib/dns/include/dns/types.h index a0318256b6a..76167c2f88d 100644 --- a/contrib/bind9/lib/dns/include/dns/types.h +++ b/contrib/bind9/lib/dns/include/dns/types.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004-2010, 2012, 2013 Internet Systems Consortium, Inc. ("ISC") + * Copyright (C) 2004-2013 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 1998-2003 Internet Software Consortium. * * Permission to use, copy, modify, and/or distribute this software for any @@ -65,6 +65,7 @@ typedef struct dns_decompress dns_decompress_t; typedef struct dns_dispatch dns_dispatch_t; typedef struct dns_dispatchevent dns_dispatchevent_t; typedef struct dns_dispatchlist dns_dispatchlist_t; +typedef struct dns_dispatchset dns_dispatchset_t; typedef struct dns_dispatchmgr dns_dispatchmgr_t; typedef struct dns_dispentry dns_dispentry_t; typedef struct dns_dns64 dns_dns64_t; @@ -86,6 +87,7 @@ typedef struct dns_keytable dns_keytable_t; typedef isc_uint16_t dns_keytag_t; typedef struct dns_loadctx dns_loadctx_t; typedef struct dns_loadmgr dns_loadmgr_t; +typedef struct dns_masterrawheader dns_masterrawheader_t; typedef struct dns_message dns_message_t; typedef isc_uint16_t dns_messageid_t; typedef isc_region_t dns_label_t; @@ -333,6 +335,20 @@ typedef enum { dns_severity_fail } dns_severity_t; +/*% + * DNS Serial Number Update Method. + * + * \li _increment: Add one to the current serial, skipping 0. + * \li _unixtime: Set to the seconds since 00:00 Jan 1, 1970, + * if possible. + * \li _yyyymmvv: Set to Year, Month, Version, if possible. + * (Not yet implemented) + */ +typedef enum { + dns_updatemethod_increment = 0, + dns_updatemethod_unixtime +} dns_updatemethod_t; + /* * Functions. */ @@ -342,6 +358,9 @@ typedef void typedef void (*dns_loaddonefunc_t)(void *, isc_result_t); +typedef void +(*dns_rawdatafunc_t)(dns_zone_t *, dns_masterrawheader_t *); + typedef isc_result_t (*dns_addrdatasetfunc_t)(void *, dns_name_t *, dns_rdataset_t *); diff --git a/contrib/bind9/lib/dns/include/dns/update.h b/contrib/bind9/lib/dns/include/dns/update.h new file mode 100644 index 00000000000..2d2c491f38a --- /dev/null +++ b/contrib/bind9/lib/dns/include/dns/update.h @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC") + * + * Permission to use, copy, modify, and/or 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 ISC DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL ISC 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. + */ + +/* $Id: update.h,v 1.5 2011/08/30 23:46:53 tbox Exp $ */ + +#ifndef DNS_UPDATE_H +#define DNS_UPDATE_H 1 + +/*! \file dns/update.h */ + +/*** + *** Imports + ***/ + +#include + +#include +#include + +typedef struct { + void (*func)(void *arg, dns_zone_t *zone, int level, + const char *message); + void *arg; +} dns_update_log_t; + +ISC_LANG_BEGINDECLS + +/*** + *** Functions + ***/ + +isc_uint32_t +dns_update_soaserial(isc_uint32_t serial, dns_updatemethod_t method); +/*%< + * Return the next serial number after 'serial', depending on the + * update method 'method': + * + *\li * dns_updatemethod_increment increments the serial number by one + *\li * dns_updatemethod_unixtime sets the serial number to the current + * time (seconds since UNIX epoch) if possible, or increments by one + * if not. + */ + +isc_result_t +dns_update_signatures(dns_update_log_t *log, dns_zone_t *zone, dns_db_t *db, + dns_dbversion_t *oldver, dns_dbversion_t *newver, + dns_diff_t *diff, isc_uint32_t sigvalidityinterval); + +ISC_LANG_ENDDECLS + +#endif /* DNS_UPDATE_H */ diff --git a/contrib/bind9/lib/dns/include/dns/view.h b/contrib/bind9/lib/dns/include/dns/view.h index d999fa1db60..d0c1931d27b 100644 --- a/contrib/bind9/lib/dns/include/dns/view.h +++ b/contrib/bind9/lib/dns/include/dns/view.h @@ -76,6 +76,7 @@ #include #include #include +#include ISC_LANG_BEGINDECLS @@ -141,7 +142,6 @@ struct dns_view { dns_rbt_t * answeracl_exclude; dns_rbt_t * denyanswernames; dns_rbt_t * answernames_exclude; - isc_boolean_t requestixfr; isc_boolean_t provideixfr; isc_boolean_t requestnsid; dns_ttl_t maxcachettl; @@ -157,6 +157,7 @@ struct dns_view { dns_name_t * dlv; dns_fixedname_t dlv_fixed; isc_uint16_t maxudp; + unsigned int maxbits; dns_v4_aaaa_t v4_aaaa; dns_acl_t * v4_aaaa_acl; dns_dns64list_t dns64; @@ -185,6 +186,7 @@ struct dns_view { dns_viewlist_t * viewlist; dns_zone_t * managed_keys; + dns_zone_t * redirect; #ifdef BIND9 /* File in which to store configuration for newly added zones */ @@ -312,7 +314,8 @@ dns_view_weakdetach(dns_view_t **targetp); isc_result_t dns_view_createresolver(dns_view_t *view, - isc_taskmgr_t *taskmgr, unsigned int ntasks, + isc_taskmgr_t *taskmgr, + unsigned int ntasks, unsigned int ndisp, isc_socketmgr_t *socketmgr, isc_timermgr_t *timermgr, unsigned int options, @@ -730,14 +733,21 @@ dns_view_load(dns_view_t *view, isc_boolean_t stop); isc_result_t dns_view_loadnew(dns_view_t *view, isc_boolean_t stop); + +isc_result_t +dns_view_asyncload(dns_view_t *view, dns_zt_allloaded_t callback, void *arg); /*%< * Load zones attached to this view. dns_view_load() loads * all zones whose master file has changed since the last * load; dns_view_loadnew() loads only zones that have never * been loaded. * + * dns_view_asyncload() loads zones asynchronously. When all zones + * in the view have finished loading, 'callback' is called with argument + * 'arg' to inform the caller. + * * If 'stop' is ISC_TRUE, stop on the first error and return it. - * If 'stop' is ISC_FALSE, ignore errors. + * If 'stop' is ISC_FALSE (or we are loading asynchronously), ignore errors. * * Requires: * @@ -841,9 +851,31 @@ dns_view_flushcache2(dns_view_t *view, isc_boolean_t fixuponly); */ isc_result_t -dns_view_flushname(dns_view_t *view, dns_name_t *); +dns_view_flushnode(dns_view_t *view, dns_name_t *name, isc_boolean_t tree); /*%< - * Flush the given name from the view's cache (and ADB). + * Flush the given name from the view's cache (and optionally ADB/badcache). + * + * If 'tree' is true, flush 'name' and all names below it + * from the cache, but do not flush ADB. + * + * If 'tree' is false, flush 'name' frmo both the cache and ADB, + * but do not touch any other nodes. + * + * Requires: + *\li 'view' is valid. + *\li 'name' is valid. + * + * Returns: + *\li #ISC_R_SUCCESS + * other returns are failures. + */ + +isc_result_t +dns_view_flushname(dns_view_t *view, dns_name_t *name); +/*%< + * Flush the given name from the view's cache, ADB and badcache. + * Equivalent to dns_view_flushnode(view, name, ISC_FALSE). + * * * Requires: *\li 'view' is valid. @@ -859,7 +891,6 @@ dns_view_adddelegationonly(dns_view_t *view, dns_name_t *name); /*%< * Add the given name to the delegation only table. * - * * Requires: *\li 'view' is valid. *\li 'name' is valid. diff --git a/contrib/bind9/lib/dns/include/dns/zone.h b/contrib/bind9/lib/dns/include/dns/zone.h index 6b9911d5897..f91801f6fe3 100644 --- a/contrib/bind9/lib/dns/include/dns/zone.h +++ b/contrib/bind9/lib/dns/include/dns/zone.h @@ -32,10 +32,12 @@ #include #include +#include #include #include #include #include +#include typedef enum { dns_zone_none, @@ -44,9 +46,16 @@ typedef enum { dns_zone_stub, dns_zone_staticstub, dns_zone_key, - dns_zone_dlz + dns_zone_dlz, + dns_zone_redirect } dns_zonetype_t; +typedef enum { + dns_zonestat_none = 0, + dns_zonestat_terse, + dns_zonestat_full +} dns_zonestat_level_t; + #define DNS_ZONEOPT_SERVERS 0x00000001U /*%< perform server checks */ #define DNS_ZONEOPT_PARENTS 0x00000002U /*%< perform parent checks */ #define DNS_ZONEOPT_CHILDREN 0x00000004U /*%< perform child checks */ @@ -94,6 +103,7 @@ typedef enum { #define DNS_ZONEKEY_MAINTAIN 0x00000002U /*%< publish/sign on schedule */ #define DNS_ZONEKEY_CREATE 0x00000004U /*%< make keys when needed */ #define DNS_ZONEKEY_FULLSIGN 0x00000008U /*%< roll to new keys immediately */ +#define DNS_ZONEKEY_NORESIGN 0x00000010U /*%< no automatic resigning */ #ifndef DNS_ZONE_MINREFRESH #define DNS_ZONE_MINREFRESH 300 /*%< 5 minutes */ @@ -287,6 +297,7 @@ dns_zone_loadnew(dns_zone_t *zone); isc_result_t dns_zone_loadandthaw(dns_zone_t *zone); + /*%< * Cause the database to be loaded from its backing store. * Confirm that the minimum requirements for the zone type are @@ -311,6 +322,25 @@ dns_zone_loadandthaw(dns_zone_t *zone); *\li Any result value from dns_db_load(). */ +isc_result_t +dns_zone_asyncload(dns_zone_t *zone, dns_zt_zoneloaded_t done, void *arg); +/*%< + * Cause the database to be loaded from its backing store asynchronously. + * Other zone maintenance functions are suspended until this is complete. + * When finished, 'done' is called to inform the caller, with 'arg' as + * its first argument and 'zone' as its second. (Normally, 'arg' is + * expected to point to the zone table but is left undefined for testing + * purposes.) + */ + +isc_boolean_t +dns__zone_loadpending(dns_zone_t *zone); +/*%< + * Indicates whether the zone is waiting to be loaded asynchronously. + * (Not currently intended for use outside of this module and associated + * tests.) + */ + void dns_zone_attach(dns_zone_t *source, dns_zone_t **target); /*%< @@ -489,6 +519,10 @@ dns_zone_dumptostream(dns_zone_t *zone, FILE *fd); isc_result_t dns_zone_dumptostream2(dns_zone_t *zone, FILE *fd, dns_masterformat_t format, const dns_master_style_t *style); +isc_result_t +dns_zone_dumptostream3(dns_zone_t *zone, FILE *fd, dns_masterformat_t format, + const dns_master_style_t *style, + const isc_uint32_t rawversion); /*%< * Write the zone to stream 'fd' in the specified 'format'. * If the 'format' is dns_masterformat_text (RFC1035), 'style' also @@ -498,7 +532,11 @@ dns_zone_dumptostream2(dns_zone_t *zone, FILE *fd, dns_masterformat_t format, * dns_zone_dumptostream2(), which always uses the dns_masterformat_text * format and the dns_master_style_default style. * - * Note that dns_zone_dumptostream2() is the most flexible form. It + * dns_zone_dumptostream2() is a backward-compatible form of + * dns_zone_dumptostream3(), which always uses the current + * default raw file format version. + * + * Note that dns_zone_dumptostream3() is the most flexible form. It * can also provide the functionality of dns_zone_fulldumptostream(). * * Require: @@ -558,10 +596,16 @@ dns_zone_setmasterswithkeys(dns_zone_t *zone, isc_result_t dns_zone_setalsonotify(dns_zone_t *zone, const isc_sockaddr_t *notify, isc_uint32_t count); +isc_result_t +dns_zone_setalsonotifywithkeys(dns_zone_t *zone, const isc_sockaddr_t *notify, + dns_name_t **keynames, isc_uint32_t count); /*%< * Set the list of additional servers to be notified when * a zone changes. To clear the list use 'count = 0'. * + * dns_zone_alsonotifywithkeys() allows each notify address to + * be associated with a TSIG key. + * * Require: *\li 'zone' to be a valid zone. *\li 'notify' to be non-NULL if count != 0. @@ -1404,6 +1448,18 @@ dns_zonemgr_setsize(dns_zonemgr_t *zmgr, int num_zones); *\li zmgr->zonetasks has been initialized. */ +isc_result_t +dns_zonemgr_createzone(dns_zonemgr_t *zmgr, dns_zone_t **zonep); +/*%< + * Allocate a new zone using a memory context from the + * zone manager's memory context pool. + * + * Require: + *\li 'zmgr' to be a valid zone manager. + *\li 'zonep' != NULL and '*zonep' == NULL. + */ + + isc_result_t dns_zonemgr_managezone(dns_zonemgr_t *zmgr, dns_zone_t *zone); /*%< @@ -1421,6 +1477,14 @@ dns_zonemgr_forcemaint(dns_zonemgr_t *zmgr); * earliest convenience. */ +void +dns__zonemgr_run(isc_task_t *task, isc_event_t *event); +/*%< + * Event handler to call dns_zonemgr_forcemaint(); used to start + * zone operations from a unit test. Not intended for use outside + * libdns or related tests. + */ + void dns_zonemgr_resumexfrs(dns_zonemgr_t *zmgr); /*%< @@ -1647,9 +1711,13 @@ dns_zone_setstats(dns_zone_t *zone, isc_stats_t *stats); void dns_zone_setrequeststats(dns_zone_t *zone, isc_stats_t *stats); + +void +dns_zone_setrcvquerystats(dns_zone_t *zone, dns_stats_t *stats); /*%< - * Set an additional statistics set to zone. It is attached in the zone - * but is not counted in the zone module; only the caller updates the counters. + * Set additional statistics sets to zone. These are attached to the zone + * but are not counted in the zone module; only the caller updates the + * counters. * * Requires: * \li 'zone' to be a valid zone. @@ -1657,8 +1725,19 @@ dns_zone_setrequeststats(dns_zone_t *zone, isc_stats_t *stats); *\li stats is a valid statistics. */ +#ifdef NEWSTATS +void +dns_zone_setrcvquerystats(dns_zone_t *zone, dns_stats_t *stats); +#endif + isc_stats_t * dns_zone_getrequeststats(dns_zone_t *zone); + +#ifdef NEWSTATS +dns_stats_t * +dns_zone_getrcvquerystats(dns_zone_t *zone); +#endif + /*%< * Get the additional statistics for zone, if one is installed. * @@ -1893,6 +1972,107 @@ dns_zone_dlzpostload(dns_zone_t *zone, dns_db_t *db); * Load the origin names for a writeable DLZ database. */ +isc_boolean_t +dns_zone_isdynamic(dns_zone_t *zone, isc_boolean_t ignore_freeze); +/*% + * Return true iff the zone is "dynamic", in the sense that the zone's + * master file (if any) is written by the server, rather than being + * updated manually and read by the server. + * + * This is true for slave zones, stub zones, key zones, and zones that + * allow dynamic updates either by having an update policy ("ssutable") + * or an "allow-update" ACL with a value other than exactly "{ none; }". + * + * If 'ignore_freeze' is true, then the zone which has had updates disabled + * will still report itself to be dynamic. + * + * Requires: + * \li 'zone' to be valid. + */ + +isc_result_t +dns_zone_setrefreshkeyinterval(dns_zone_t *zone, isc_uint32_t interval); +/*% + * Sets the frequency, in minutes, with which the key repository will be + * checked to see if the keys for this zone have been updated. Any value + * higher than 1440 minutes (24 hours) will be silently reduced. A + * value of zero will return an out-of-range error. + * + * Requires: + * \li 'zone' to be valid. + */ + +isc_boolean_t +dns_zone_getrequestixfr(dns_zone_t *zone); +/*% + * Returns the true/false value of the request-ixfr option in the zone. + * + * Requires: + * \li 'zone' to be valid. + */ + +void +dns_zone_setrequestixfr(dns_zone_t *zone, isc_boolean_t flag); +/*% + * Sets the request-ixfr option for the zone. Either true or false. The + * default value is determined by the setting of this option in the view. + * + * Requires: + * \li 'zone' to be valid. + */ + +void +dns_zone_setserialupdatemethod(dns_zone_t *zone, dns_updatemethod_t method); +/*% + * Sets the update method to use when incrementing the zone serial number + * due to a DDNS update. Valid options are dns_updatemethod_increment + * and dns_updatemethod_unixtime. + * + * Requires: + * \li 'zone' to be valid. + */ + +dns_updatemethod_t +dns_zone_getserialupdatemethod(dns_zone_t *zone); +/*% + * Returns the update method to be used when incrementing the zone serial + * number due to a DDNS update. + * + * Requires: + * \li 'zone' to be valid. + */ + +isc_result_t +dns_zone_link(dns_zone_t *zone, dns_zone_t *raw); + +void +dns_zone_getraw(dns_zone_t *zone, dns_zone_t **raw); + +isc_result_t +dns_zone_keydone(dns_zone_t *zone, const char *data); + +isc_result_t +dns_zone_setnsec3param(dns_zone_t *zone, isc_uint8_t hash, isc_uint8_t flags, + isc_uint16_t iter, isc_uint8_t saltlen, + unsigned char *salt, isc_boolean_t replace); +/*% + * Set the NSEC3 parameters for the zone. + * + * If 'replace' is ISC_TRUE, then the existing NSEC3 chain, if any, will + * be replaced with the new one. If 'hash' is zero, then the replacement + * chain will be NSEC rather than NSEC3. + * + * Requires: + * \li 'zone' to be valid. + */ + +void +dns_zone_setrawdata(dns_zone_t *zone, dns_masterrawheader_t *header); +/*% + * Set the data to be included in the header when the zone is dumped in + * binary format. + */ + isc_result_t dns_zone_synckeyzone(dns_zone_t *zone); /*% @@ -1909,6 +2089,16 @@ dns_zone_rpz_enable(dns_zone_t *zone); isc_boolean_t dns_zone_get_rpz(dns_zone_t *zone); +void +dns_zone_setstatlevel(dns_zone_t *zone, dns_zonestat_level_t level); + +dns_zonestat_level_t +dns_zone_getstatlevel(dns_zone_t *zone); +/*% + * Set and get the statistics reporting level for the zone; + * full, terse, or none. + */ + ISC_LANG_ENDDECLS #endif /* DNS_ZONE_H */ diff --git a/contrib/bind9/lib/dns/include/dns/zt.h b/contrib/bind9/lib/dns/include/dns/zt.h index 7d1e8bfdfd8..f91d7e8dbe0 100644 --- a/contrib/bind9/lib/dns/include/dns/zt.h +++ b/contrib/bind9/lib/dns/include/dns/zt.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004-2007, 2012 Internet Systems Consortium, Inc. ("ISC") + * Copyright (C) 2004-2007, 2011 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 1999-2002 Internet Software Consortium. * * Permission to use, copy, modify, and/or distribute this software for any @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id$ */ +/* $Id: zt.h,v 1.40 2011/09/02 23:46:32 tbox Exp $ */ #ifndef DNS_ZT_H #define DNS_ZT_H 1 @@ -30,6 +30,21 @@ ISC_LANG_BEGINDECLS +typedef isc_result_t +(*dns_zt_allloaded_t)(void *arg); +/*%< + * Method prototype: when all pending zone loads are complete, + * the zone table can inform the caller via a callback function with + * this signature. + */ + +typedef isc_result_t +(*dns_zt_zoneloaded_t)(dns_zt_t *zt, dns_zone_t *zone, isc_task_t *task); +/*%< + * Method prototype: when a zone finishes loading, the zt object + * can be informed via a callback function with this signature. + */ + isc_result_t dns_zt_create(isc_mem_t *mctx, dns_rdataclass_t rdclass, dns_zt_t **zt); /*%< @@ -134,6 +149,9 @@ dns_zt_load(dns_zt_t *zt, isc_boolean_t stop); isc_result_t dns_zt_loadnew(dns_zt_t *zt, isc_boolean_t stop); + +isc_result_t +dns_zt_asyncload(dns_zt_t *zt, dns_zt_allloaded_t alldone, void *arg); /*%< * Load all zones in the table. If 'stop' is ISC_TRUE, * stop on the first error and return it. If 'stop' @@ -142,6 +160,10 @@ dns_zt_loadnew(dns_zt_t *zt, isc_boolean_t stop); * dns_zt_loadnew() only loads zones that are not yet loaded. * dns_zt_load() also loads zones that are already loaded and * and whose master file has changed since the last load. + * dns_zt_asyncload() loads zones asynchronously; when all + * zones in the zone table have finished loaded (or failed due + * to errors), the caller is informed by calling 'alldone' + * with an argument of 'arg'. * * Requires: * \li 'zt' to be valid @@ -178,6 +200,16 @@ dns_zt_apply2(dns_zt_t *zt, isc_boolean_t stop, isc_result_t *sub, * any error code from 'action'. */ +isc_boolean_t +dns_zt_loadspending(dns_zt_t *zt); +/*%< + * Returns ISC_TRUE if and only if there are zones still waiting to + * be loaded in zone table 'zt'. + * + * Requires: + * \li 'zt' to be valid. + */ + ISC_LANG_ENDDECLS #endif /* DNS_ZT_H */ diff --git a/contrib/bind9/lib/dns/include/dst/dst.h b/contrib/bind9/lib/dns/include/dst/dst.h index 87d844bf227..4724fc64c95 100644 --- a/contrib/bind9/lib/dns/include/dst/dst.h +++ b/contrib/bind9/lib/dns/include/dst/dst.h @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id$ */ +/* $Id: dst.h,v 1.34 2011/10/20 21:20:02 marka Exp $ */ #ifndef DST_DST_H #define DST_DST_H 1 @@ -239,9 +239,16 @@ dst_context_sign(dst_context_t *dctx, isc_buffer_t *sig); isc_result_t dst_context_verify(dst_context_t *dctx, isc_region_t *sig); + +isc_result_t +dst_context_verify2(dst_context_t *dctx, unsigned int maxbits, + isc_region_t *sig); /*%< * Verifies the signature using the data and key stored in the context. * + * 'maxbits' specifies the maximum number of bits permitted in the RSA + * exponent. + * * Requires: * \li "dctx" is a valid context. * \li "sig" is a valid region. @@ -498,6 +505,14 @@ dst_key_fromgssapi(dns_name_t *name, gss_ctx_id_t gssctx, isc_mem_t *mctx, * the context id. */ +#ifdef DST_KEY_INTERNAL +isc_result_t +dst_key_buildinternal(dns_name_t *name, unsigned int alg, + unsigned int bits, unsigned int flags, + unsigned int protocol, dns_rdataclass_t rdclass, + void *data, isc_mem_t *mctx, dst_key_t **keyp); +#endif + isc_result_t dst_key_fromlabel(dns_name_t *name, int alg, unsigned int flags, unsigned int protocol, dns_rdataclass_t rdclass, @@ -518,6 +533,7 @@ dst_key_generate2(dns_name_t *name, unsigned int alg, dns_rdataclass_t rdclass, isc_mem_t *mctx, dst_key_t **keyp, void (*callback)(int)); + /*%< * Generate a DST key (or keypair) with the supplied parameters. The * interpretation of the "param" field depends on the algorithm: @@ -748,6 +764,26 @@ dst_key_setbits(dst_key_t *key, isc_uint16_t bits); * "key" is a valid key. */ +void +dst_key_setttl(dst_key_t *key, dns_ttl_t ttl); +/*%< + * Set the default TTL to use when converting the key + * to a KEY or DNSKEY RR. + * + * Requires: + * "key" is a valid key. + */ + +dns_ttl_t +dst_key_getttl(const dst_key_t *key); +/*%< + * Get the default TTL to use when converting the key + * to a KEY or DNSKEY RR. + * + * Requires: + * "key" is a valid key. + */ + isc_result_t dst_key_setflags(dst_key_t *key, isc_uint32_t flags); /* diff --git a/contrib/bind9/lib/dns/iptable.c b/contrib/bind9/lib/dns/iptable.c index e960d5c48cd..701950533c9 100644 --- a/contrib/bind9/lib/dns/iptable.c +++ b/contrib/bind9/lib/dns/iptable.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007-2009 Internet Systems Consortium, Inc. ("ISC") + * Copyright (C) 2007-2009, 2013 Internet Systems Consortium, Inc. ("ISC") * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -36,7 +36,8 @@ dns_iptable_create(isc_mem_t *mctx, dns_iptable_t **target) { tab = isc_mem_get(mctx, sizeof(*tab)); if (tab == NULL) return (ISC_R_NOMEMORY); - tab->mctx = mctx; + tab->mctx = NULL; + isc_mem_attach(mctx, &tab->mctx); isc_refcount_init(&tab->refcount, 1); tab->radix = NULL; tab->magic = DNS_IPTABLE_MAGIC; @@ -184,5 +185,5 @@ destroy_iptable(dns_iptable_t *dtab) { isc_refcount_destroy(&dtab->refcount); dtab->magic = 0; - isc_mem_put(dtab->mctx, dtab, sizeof(*dtab)); + isc_mem_putanddetach(&dtab->mctx, dtab, sizeof(*dtab)); } diff --git a/contrib/bind9/lib/dns/journal.c b/contrib/bind9/lib/dns/journal.c index 1564a811ffe..022a3e280f8 100644 --- a/contrib/bind9/lib/dns/journal.c +++ b/contrib/bind9/lib/dns/journal.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004, 2005, 2007-2012 Internet Systems Consortium, Inc. ("ISC") + * Copyright (C) 2004, 2005, 2007-2011, 2013 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 1999-2002 Internet Software Consortium. * * Permission to use, copy, modify, and/or distribute this software for any @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id$ */ +/* $Id: journal.c,v 1.120 2011/12/22 07:32:41 each Exp $ */ #include @@ -111,6 +111,8 @@ static isc_boolean_t bind8_compat = ISC_TRUE; /* XXX config */ if (result != ISC_R_SUCCESS) goto failure; \ } while (0) +#define JOURNAL_SERIALSET 0x01U + static isc_result_t index_to_disk(dns_journal_t *); static inline isc_uint32_t @@ -213,6 +215,9 @@ typedef union { journal_rawpos_t end; /*% Number of index entries following the header. */ unsigned char index_size[4]; + /*% Source serial number. */ + unsigned char sourceserial[4]; + unsigned char flags; } h; /* Pad the header to a fixed size. */ unsigned char pad[JOURNAL_HEADER_SIZE]; @@ -252,6 +257,8 @@ typedef struct { journal_pos_t begin; journal_pos_t end; isc_uint32_t index_size; + isc_uint32_t sourceserial; + isc_boolean_t serialset; } journal_header_t; /*% @@ -284,7 +291,7 @@ typedef struct { */ static journal_header_t -initial_journal_header = { ";BIND LOG V9\n", { 0, 0 }, { 0, 0 }, 0 }; +initial_journal_header = { ";BIND LOG V9\n", { 0, 0 }, { 0, 0 }, 0, 0, 0 }; #define JOURNAL_EMPTY(h) ((h)->begin.offset == (h)->end.offset) @@ -292,7 +299,8 @@ typedef enum { JOURNAL_STATE_INVALID, JOURNAL_STATE_READ, JOURNAL_STATE_WRITE, - JOURNAL_STATE_TRANSACTION + JOURNAL_STATE_TRANSACTION, + JOURNAL_STATE_INLINE } journal_state_t; struct dns_journal { @@ -353,16 +361,24 @@ journal_header_decode(journal_rawheader_t *raw, journal_header_t *cooked) { journal_pos_decode(&raw->h.begin, &cooked->begin); journal_pos_decode(&raw->h.end, &cooked->end); cooked->index_size = decode_uint32(raw->h.index_size); + cooked->sourceserial = decode_uint32(raw->h.sourceserial); + cooked->serialset = ISC_TF(raw->h.flags & JOURNAL_SERIALSET); } static void journal_header_encode(journal_header_t *cooked, journal_rawheader_t *raw) { + unsigned char flags = 0; + INSIST(sizeof(cooked->format) == sizeof(raw->h.format)); memset(raw->pad, 0, sizeof(raw->pad)); memcpy(raw->h.format, cooked->format, sizeof(raw->h.format)); journal_pos_encode(&raw->h.begin, &cooked->begin); journal_pos_encode(&raw->h.end, &cooked->end); encode_uint32(cooked->index_size, raw->h.index_size); + encode_uint32(cooked->sourceserial, raw->h.sourceserial); + if (cooked->serialset) + flags |= JOURNAL_SERIALSET; + raw->h.flags = flags; } /* @@ -540,7 +556,8 @@ journal_file_create(isc_mem_t *mctx, const char *filename) { static isc_result_t journal_open(isc_mem_t *mctx, const char *filename, isc_boolean_t write, - isc_boolean_t create, dns_journal_t **journalp) { + isc_boolean_t create, dns_journal_t **journalp) +{ FILE *fp = NULL; isc_result_t result; journal_rawheader_t rawheader; @@ -551,7 +568,8 @@ journal_open(isc_mem_t *mctx, const char *filename, isc_boolean_t write, if (j == NULL) return (ISC_R_NOMEMORY); - j->mctx = mctx; + j->mctx = NULL; + isc_mem_attach(mctx, &j->mctx); j->state = JOURNAL_STATE_INVALID; j->fp = NULL; j->filename = filename; @@ -662,18 +680,23 @@ journal_open(isc_mem_t *mctx, const char *filename, isc_boolean_t write, } if (j->fp != NULL) (void)isc_stdio_close(j->fp); - isc_mem_put(j->mctx, j, sizeof(*j)); + isc_mem_putanddetach(&j->mctx, j, sizeof(*j)); return (result); } isc_result_t -dns_journal_open(isc_mem_t *mctx, const char *filename, isc_boolean_t write, - dns_journal_t **journalp) { +dns_journal_open(isc_mem_t *mctx, const char *filename, unsigned int mode, + dns_journal_t **journalp) +{ isc_result_t result; int namelen; char backup[1024]; + isc_boolean_t write, create; - result = journal_open(mctx, filename, write, write, journalp); + create = ISC_TF(mode & DNS_JOURNAL_CREATE); + write = ISC_TF(mode & (DNS_JOURNAL_WRITE|DNS_JOURNAL_CREATE)); + + result = journal_open(mctx, filename, write, create, journalp); if (result == ISC_R_NOTFOUND) { namelen = strlen(filename); if (namelen > 4 && strcmp(filename + namelen - 4, ".jnl") == 0) @@ -944,7 +967,8 @@ dns_journal_begin_transaction(dns_journal_t *j) { journal_rawxhdr_t hdr; REQUIRE(DNS_JOURNAL_VALID(j)); - REQUIRE(j->state == JOURNAL_STATE_WRITE); + REQUIRE(j->state == JOURNAL_STATE_WRITE || + j->state == JOURNAL_STATE_INLINE); /* * Find the file offset where the new transaction should @@ -1067,7 +1091,21 @@ dns_journal_commit(dns_journal_t *j) { journal_rawheader_t rawheader; REQUIRE(DNS_JOURNAL_VALID(j)); - REQUIRE(j->state == JOURNAL_STATE_TRANSACTION); + REQUIRE(j->state == JOURNAL_STATE_TRANSACTION || + j->state == JOURNAL_STATE_INLINE); + + /* + * Just write out a updated header. + */ + if (j->state == JOURNAL_STATE_INLINE) { + CHECK(journal_fsync(j)); + journal_header_encode(&j->header, &rawheader); + CHECK(journal_seek(j, 0)); + CHECK(journal_write(j, &rawheader, sizeof(rawheader))); + CHECK(journal_fsync(j)); + j->state = JOURNAL_STATE_WRITE; + return (ISC_R_SUCCESS); + } /* * Perform some basic consistency checks. @@ -1124,20 +1162,23 @@ dns_journal_commit(dns_journal_t *j) { */ CHECK(journal_fsync(j)); - /* - * Update the transaction header. - */ - CHECK(journal_seek(j, j->x.pos[0].offset)); - CHECK(journal_write_xhdr(j, (j->x.pos[1].offset - j->x.pos[0].offset) - - sizeof(journal_rawxhdr_t), - j->x.pos[0].serial, j->x.pos[1].serial)); + if (j->state == JOURNAL_STATE_TRANSACTION) { + isc_offset_t offset; + offset = (j->x.pos[1].offset - j->x.pos[0].offset) - + sizeof(journal_rawxhdr_t); + /* + * Update the transaction header. + */ + CHECK(journal_seek(j, j->x.pos[0].offset)); + CHECK(journal_write_xhdr(j, offset, j->x.pos[0].serial, + j->x.pos[1].serial)); + } /* * Update the journal header. */ - if (JOURNAL_EMPTY(&j->header)) { + if (JOURNAL_EMPTY(&j->header)) j->header.begin = j->x.pos[0]; - } j->header.end = j->x.pos[1]; journal_header_encode(&j->header, &rawheader); CHECK(journal_seek(j, 0)); @@ -1204,7 +1245,7 @@ dns_journal_destroy(dns_journal_t **journalp) { if (j->fp != NULL) (void)isc_stdio_close(j->fp); j->magic = 0; - isc_mem_put(j->mctx, j, sizeof(*j)); + isc_mem_putanddetach(&j->mctx, j, sizeof(*j)); *journalp = NULL; } @@ -1369,7 +1410,7 @@ dns_journal_rollforward2(isc_mem_t *mctx, dns_db_t *db, unsigned int options, REQUIRE(filename != NULL); j = NULL; - result = dns_journal_open(mctx, filename, ISC_FALSE, &j); + result = dns_journal_open(mctx, filename, DNS_JOURNAL_READ, &j); if (result == ISC_R_NOTFOUND) { isc_log_write(JOURNAL_DEBUG_LOGARGS(3), "no journal file, but that's OK"); @@ -1402,7 +1443,7 @@ dns_journal_print(isc_mem_t *mctx, const char *filename, FILE *file) { REQUIRE(filename != NULL); j = NULL; - result = dns_journal_open(mctx, filename, ISC_FALSE, &j); + result = dns_journal_open(mctx, filename, DNS_JOURNAL_READ, &j); if (result == ISC_R_NOTFOUND) { isc_log_write(JOURNAL_DEBUG_LOGARGS(3), "no journal file"); return (DNS_R_NOJOURNAL); @@ -1415,6 +1456,8 @@ dns_journal_print(isc_mem_t *mctx, const char *filename, FILE *file) { return (result); } + if (j->header.serialset) + fprintf(file, "Source serial = %u\n", j->header.sourceserial); dns_diff_init(j->mctx, &diff); /* @@ -1497,14 +1540,39 @@ dns_journal_print(isc_mem_t *mctx, const char *filename, FILE *file) { /* * Miscellaneous accessors. */ -isc_uint32_t dns_journal_first_serial(dns_journal_t *j) { +isc_uint32_t +dns_journal_first_serial(dns_journal_t *j) { return (j->header.begin.serial); } -isc_uint32_t dns_journal_last_serial(dns_journal_t *j) { +isc_uint32_t +dns_journal_last_serial(dns_journal_t *j) { return (j->header.end.serial); } +void +dns_journal_set_sourceserial(dns_journal_t *j, isc_uint32_t sourceserial) { + + REQUIRE(j->state == JOURNAL_STATE_WRITE || + j->state == JOURNAL_STATE_INLINE || + j->state == JOURNAL_STATE_TRANSACTION); + + j->header.sourceserial = sourceserial; + j->header.serialset = ISC_TRUE; + if (j->state == JOURNAL_STATE_WRITE) + j->state = JOURNAL_STATE_INLINE; +} + +isc_boolean_t +dns_journal_get_sourceserial(dns_journal_t *j, isc_uint32_t *sourceserial) { + REQUIRE(sourceserial != NULL); + + if (!j->header.serialset) + return (ISC_FALSE); + *sourceserial = j->header.sourceserial; + return (ISC_TRUE); +} + /**************************************************************************/ /* * Iteration support. @@ -1860,8 +1928,7 @@ dns_diff_subtract(dns_diff_t diff[2], dns_diff_t *r) { } static isc_result_t -diff_namespace(isc_mem_t *mctx, - dns_db_t *dba, dns_dbversion_t *dbvera, +diff_namespace(dns_db_t *dba, dns_dbversion_t *dbvera, dns_db_t *dbb, dns_dbversion_t *dbverb, unsigned int options, dns_diff_t *resultdiff) { @@ -1877,8 +1944,8 @@ diff_namespace(isc_mem_t *mctx, db[0] = dba, db[1] = dbb; ver[0] = dbvera, ver[1] = dbverb; - dns_diff_init(mctx, &diff[0]); - dns_diff_init(mctx, &diff[1]); + dns_diff_init(resultdiff->mctx, &diff[0]); + dns_diff_init(resultdiff->mctx, &diff[1]); dns_fixedname_init(&fixname[0]); dns_fixedname_init(&fixname[1]); @@ -1956,8 +2023,11 @@ diff_namespace(isc_mem_t *mctx, failure: dns_dbiterator_destroy(&dbit[1]); + cleanup_iterator: dns_dbiterator_destroy(&dbit[0]); + dns_diff_clear(&diff[0]); + dns_diff_clear(&diff[1]); return (result); } @@ -1968,33 +2038,48 @@ diff_namespace(isc_mem_t *mctx, * possibly very large transaction. */ isc_result_t -dns_db_diff(isc_mem_t *mctx, - dns_db_t *dba, dns_dbversion_t *dbvera, - dns_db_t *dbb, dns_dbversion_t *dbverb, - const char *journal_filename) +dns_db_diff(isc_mem_t *mctx, dns_db_t *dba, dns_dbversion_t *dbvera, + dns_db_t *dbb, dns_dbversion_t *dbverb, const char *filename) +{ + isc_result_t result; + dns_diff_t diff; + + dns_diff_init(mctx, &diff); + + result = dns_db_diffx(&diff, dba, dbvera, dbb, dbverb, filename); + + dns_diff_clear(&diff); + + return (result); +} + +isc_result_t +dns_db_diffx(dns_diff_t *diff, dns_db_t *dba, dns_dbversion_t *dbvera, + dns_db_t *dbb, dns_dbversion_t *dbverb, const char *filename) { isc_result_t result; dns_journal_t *journal = NULL; - dns_diff_t resultdiff; - result = dns_journal_open(mctx, journal_filename, ISC_TRUE, &journal); - if (result != ISC_R_SUCCESS) - return (result); - - dns_diff_init(mctx, &resultdiff); - - CHECK(diff_namespace(mctx, dba, dbvera, dbb, dbverb, - DNS_DB_NONSEC3, &resultdiff)); - CHECK(diff_namespace(mctx, dba, dbvera, dbb, dbverb, - DNS_DB_NSEC3ONLY, &resultdiff)); - if (ISC_LIST_EMPTY(resultdiff.tuples)) { - isc_log_write(JOURNAL_DEBUG_LOGARGS(3), "no changes"); - } else { - CHECK(dns_journal_write_transaction(journal, &resultdiff)); + if (filename != NULL) { + result = dns_journal_open(diff->mctx, filename, + DNS_JOURNAL_CREATE, &journal); + if (result != ISC_R_SUCCESS) + return (result); } + + CHECK(diff_namespace(dba, dbvera, dbb, dbverb, DNS_DB_NONSEC3, diff)); + CHECK(diff_namespace(dba, dbvera, dbb, dbverb, DNS_DB_NSEC3ONLY, diff)); + + if (journal != NULL) { + if (ISC_LIST_EMPTY(diff->tuples)) + isc_log_write(JOURNAL_DEBUG_LOGARGS(3), "no changes"); + else + CHECK(dns_journal_write_transaction(journal, diff)); + } + failure: - dns_diff_clear(&resultdiff); - dns_journal_destroy(&journal); + if (journal != NULL) + dns_journal_destroy(&journal); return (result); } @@ -2145,6 +2230,8 @@ dns_journal_compact(isc_mem_t *mctx, char *filename, isc_uint32_t serial, new->header.begin.offset = indexend; new->header.end.serial = j->header.end.serial; new->header.end.offset = indexend + copy_length; + new->header.sourceserial = j->header.sourceserial; + new->header.serialset = j->header.serialset; /* * Update the journal header. diff --git a/contrib/bind9/lib/dns/key.c b/contrib/bind9/lib/dns/key.c index bf9b16c170c..ccac157c13f 100644 --- a/contrib/bind9/lib/dns/key.c +++ b/contrib/bind9/lib/dns/key.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004-2007, 2011, 2012 Internet Systems Consortium, Inc. ("ISC") + * Copyright (C) 2004-2007, 2011 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 2001 Internet Software Consortium. * * Permission to use, copy, modify, and/or distribute this software for any @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id$ */ +/* $Id: key.c,v 1.11 2011/10/20 21:20:02 marka Exp $ */ #include @@ -177,4 +177,16 @@ dst_key_getbits(const dst_key_t *key) { return (key->key_bits); } +void +dst_key_setttl(dst_key_t *key, dns_ttl_t ttl) { + REQUIRE(VALID_KEY(key)); + key->key_ttl = ttl; +} + +dns_ttl_t +dst_key_getttl(const dst_key_t *key) { + REQUIRE(VALID_KEY(key)); + return (key->key_ttl); +} + /*! \file */ diff --git a/contrib/bind9/lib/dns/keytable.c b/contrib/bind9/lib/dns/keytable.c index 309e9dd2d8d..c49847f326a 100644 --- a/contrib/bind9/lib/dns/keytable.c +++ b/contrib/bind9/lib/dns/keytable.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004, 2005, 2007, 2009, 2010 Internet Systems Consortium, Inc. ("ISC") + * Copyright (C) 2004, 2005, 2007, 2009, 2010, 2013 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 2000, 2001 Internet Software Consortium. * * Permission to use, copy, modify, and/or distribute this software for any @@ -67,7 +67,8 @@ dns_keytable_create(isc_mem_t *mctx, dns_keytable_t **keytablep) { if (result != ISC_R_SUCCESS) goto cleanup_lock; - keytable->mctx = mctx; + keytable->mctx = NULL; + isc_mem_attach(mctx, &keytable->mctx); keytable->active_nodes = 0; keytable->references = 1; keytable->magic = KEYTABLE_MAGIC; @@ -82,7 +83,7 @@ dns_keytable_create(isc_mem_t *mctx, dns_keytable_t **keytablep) { dns_rbt_destroy(&keytable->table); cleanup_keytable: - isc_mem_put(mctx, keytable, sizeof(*keytable)); + isc_mem_putanddetach(&mctx, keytable, sizeof(*keytable)); return (result); } @@ -137,7 +138,8 @@ dns_keytable_detach(dns_keytable_t **keytablep) { isc_rwlock_destroy(&keytable->rwlock); DESTROYLOCK(&keytable->lock); keytable->magic = 0; - isc_mem_put(keytable->mctx, keytable, sizeof(*keytable)); + isc_mem_putanddetach(&keytable->mctx, + keytable, sizeof(*keytable)); } *keytablep = NULL; diff --git a/contrib/bind9/lib/dns/log.c b/contrib/bind9/lib/dns/log.c index d286d103e63..c4d644e3899 100644 --- a/contrib/bind9/lib/dns/log.c +++ b/contrib/bind9/lib/dns/log.c @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id$ */ +/* $Id: log.c,v 1.49 2011/10/13 22:48:24 tbox Exp $ */ /*! \file */ diff --git a/contrib/bind9/lib/dns/lookup.c b/contrib/bind9/lib/dns/lookup.c index d5fc7aae476..9387a955cd3 100644 --- a/contrib/bind9/lib/dns/lookup.c +++ b/contrib/bind9/lib/dns/lookup.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004, 2005, 2007 Internet Systems Consortium, Inc. ("ISC") + * Copyright (C) 2004, 2005, 2007, 2013 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 2000, 2001, 2003 Internet Software Consortium. * * Permission to use, copy, modify, and/or distribute this software for any @@ -356,7 +356,7 @@ static void levent_destroy(isc_event_t *event) { dns_lookupevent_t *levent; isc_mem_t *mctx; - + REQUIRE(event->ev_type == DNS_EVENT_LOOKUPDONE); mctx = event->ev_destroy_arg; levent = (dns_lookupevent_t *)event; @@ -393,7 +393,8 @@ dns_lookup_create(isc_mem_t *mctx, dns_name_t *name, dns_rdatatype_t type, lookup = isc_mem_get(mctx, sizeof(*lookup)); if (lookup == NULL) return (ISC_R_NOMEMORY); - lookup->mctx = mctx; + lookup->mctx = NULL; + isc_mem_attach(mctx, &lookup->mctx); lookup->options = options; ievent = isc_event_allocate(mctx, lookup, DNS_EVENT_LOOKUPDONE, @@ -452,7 +453,7 @@ dns_lookup_create(isc_mem_t *mctx, dns_name_t *name, dns_rdatatype_t type, isc_task_detach(&lookup->task); cleanup_lookup: - isc_mem_put(mctx, lookup, sizeof(*lookup)); + isc_mem_putanddetach(&mctx, lookup, sizeof(*lookup)); return (result); } @@ -491,7 +492,7 @@ dns_lookup_destroy(dns_lookup_t **lookupp) { DESTROYLOCK(&lookup->lock); lookup->magic = 0; - isc_mem_put(lookup->mctx, lookup, sizeof(*lookup)); + isc_mem_putanddetach(&lookup->mctx, lookup, sizeof(*lookup)); *lookupp = NULL; } diff --git a/contrib/bind9/lib/dns/master.c b/contrib/bind9/lib/dns/master.c index 1b7460c4567..d0c175876f5 100644 --- a/contrib/bind9/lib/dns/master.c +++ b/contrib/bind9/lib/dns/master.c @@ -133,6 +133,7 @@ struct dns_loadctx { /* Members specific to the raw format: */ FILE *f; isc_boolean_t first; + dns_masterrawheader_t header; /* Which fixed buffers we are using? */ unsigned int loop_cnt; /*% records per quantum, @@ -597,6 +598,7 @@ loadctx_create(dns_masterformat_t format, isc_mem_t *mctx, lctx->f = NULL; lctx->first = ISC_TRUE; + dns_master_initrawheader(&lctx->header); lctx->loop_cnt = (done != NULL) ? 100 : 0; lctx->callbacks = callbacks; @@ -2105,50 +2107,74 @@ load_raw(dns_loadctx_t *lctx) { int target_size = TSIZ; isc_buffer_t target, buf; unsigned char *target_mem = NULL; + dns_masterrawheader_t header; dns_decompress_t dctx; REQUIRE(DNS_LCTX_VALID(lctx)); callbacks = lctx->callbacks; dns_decompress_init(&dctx, -1, DNS_DECOMPRESS_NONE); + dns_master_initrawheader(&header); + if (lctx->first) { - dns_masterrawheader_t header; - isc_uint32_t format, version, dumptime; - size_t hdrlen = sizeof(format) + sizeof(version) + - sizeof(dumptime); + unsigned char data[sizeof(header)]; + size_t commonlen = + sizeof(header.format) + sizeof(header.version); + size_t remainder; - INSIST(hdrlen <= sizeof(header)); - isc_buffer_init(&target, &header, sizeof(header)); + INSIST(commonlen <= sizeof(header)); + isc_buffer_init(&target, data, sizeof(data)); - result = isc_stdio_read(&header, 1, hdrlen, lctx->f, NULL); + result = isc_stdio_read(data, 1, commonlen, lctx->f, NULL); if (result != ISC_R_SUCCESS) { UNEXPECTED_ERROR(__FILE__, __LINE__, "isc_stdio_read failed: %s", isc_result_totext(result)); return (result); } - isc_buffer_add(&target, hdrlen); - format = isc_buffer_getuint32(&target); - if (format != dns_masterformat_raw) { + isc_buffer_add(&target, commonlen); + header.format = isc_buffer_getuint32(&target); + if (header.format != dns_masterformat_raw) { (*callbacks->error)(callbacks, "dns_master_load: " "file format mismatch"); return (ISC_R_NOTIMPLEMENTED); } - version = isc_buffer_getuint32(&target); - if (version > DNS_RAWFORMAT_VERSION) { + header.version = isc_buffer_getuint32(&target); + switch (header.version) { + case 0: + remainder = sizeof(header.dumptime); + break; + case DNS_RAWFORMAT_VERSION: + remainder = sizeof(header) - commonlen; + break; + default: (*callbacks->error)(callbacks, "dns_master_load: " "unsupported file format version"); return (ISC_R_NOTIMPLEMENTED); } - /* Empty read: currently, we do not use dumptime */ - dumptime = isc_buffer_getuint32(&target); - POST(dumptime); + result = isc_stdio_read(data + commonlen, 1, remainder, + lctx->f, NULL); + if (result != ISC_R_SUCCESS) { + UNEXPECTED_ERROR(__FILE__, __LINE__, + "isc_stdio_read failed: %s", + isc_result_totext(result)); + return (result); + } + + isc_buffer_add(&target, remainder); + header.dumptime = isc_buffer_getuint32(&target); + if (header.version == DNS_RAWFORMAT_VERSION) { + header.flags = isc_buffer_getuint32(&target); + header.sourceserial = isc_buffer_getuint32(&target); + header.lastxfrin = isc_buffer_getuint32(&target); + } lctx->first = ISC_FALSE; + lctx->header = header; } ISC_LIST_INIT(head); @@ -2383,6 +2409,9 @@ load_raw(dns_loadctx_t *lctx) { } else if (result == ISC_R_SUCCESS && lctx->result != ISC_R_SUCCESS) result = lctx->result; + if (result == ISC_R_SUCCESS && callbacks->rawdata != NULL) + (*callbacks->rawdata)(callbacks->zone, &header); + cleanup: if (rdata != NULL) isc_mem_put(mctx, rdata, rdata_size * sizeof(*rdata)); @@ -2969,3 +2998,8 @@ dns_loadctx_cancel(dns_loadctx_t *lctx) { lctx->canceled = ISC_TRUE; UNLOCK(&lctx->lock); } + +void +dns_master_initrawheader(dns_masterrawheader_t *header) { + memset(header, 0, sizeof(dns_masterrawheader_t)); +} diff --git a/contrib/bind9/lib/dns/masterdump.c b/contrib/bind9/lib/dns/masterdump.c index a10e6f2d559..2717658e691 100644 --- a/contrib/bind9/lib/dns/masterdump.c +++ b/contrib/bind9/lib/dns/masterdump.c @@ -74,6 +74,7 @@ struct dns_master_style { unsigned int rdata_column; unsigned int line_length; unsigned int tab_width; + unsigned int split_width; }; /*% @@ -108,15 +109,16 @@ dns_master_style_default = { DNS_STYLEFLAG_OMIT_TTL | DNS_STYLEFLAG_TTL | DNS_STYLEFLAG_COMMENT | + DNS_STYLEFLAG_RRCOMMENT | DNS_STYLEFLAG_MULTILINE, - 24, 24, 24, 32, 80, 8 + 24, 24, 24, 32, 80, 8, UINT_MAX }; LIBDNS_EXTERNAL_DATA const dns_master_style_t dns_master_style_full = { DNS_STYLEFLAG_COMMENT | DNS_STYLEFLAG_RESIGN, - 46, 46, 46, 64, 120, 8 + 46, 46, 46, 64, 120, 8, UINT_MAX }; LIBDNS_EXTERNAL_DATA const dns_master_style_t @@ -126,8 +128,9 @@ dns_master_style_explicitttl = { DNS_STYLEFLAG_REL_OWNER | DNS_STYLEFLAG_REL_DATA | DNS_STYLEFLAG_COMMENT | + DNS_STYLEFLAG_RRCOMMENT | DNS_STYLEFLAG_MULTILINE, - 24, 32, 32, 40, 80, 8 + 24, 32, 32, 40, 80, 8, UINT_MAX }; LIBDNS_EXTERNAL_DATA const dns_master_style_t @@ -137,13 +140,13 @@ dns_master_style_cache = { DNS_STYLEFLAG_MULTILINE | DNS_STYLEFLAG_TRUST | DNS_STYLEFLAG_NCACHE, - 24, 32, 32, 40, 80, 8 + 24, 32, 32, 40, 80, 8, UINT_MAX }; LIBDNS_EXTERNAL_DATA const dns_master_style_t dns_master_style_simple = { 0, - 24, 32, 32, 40, 80, 8 + 24, 32, 32, 40, 80, 8, UINT_MAX }; /*% @@ -152,7 +155,7 @@ dns_master_style_simple = { LIBDNS_EXTERNAL_DATA const dns_master_style_t dns_master_style_debug = { DNS_STYLEFLAG_REL_OWNER, - 24, 32, 40, 48, 80, 8 + 24, 32, 40, 48, 80, 8, UINT_MAX }; @@ -185,6 +188,7 @@ struct dns_dumpctx { char *file; char *tmpfile; dns_masterformat_t format; + dns_masterrawheader_t header; isc_result_t (*dumpsets)(isc_mem_t *mctx, dns_name_t *name, dns_rdatasetiter_t *rdsiter, dns_totext_ctx_t *ctx, @@ -373,7 +377,7 @@ ncache_summary(dns_rdataset_t *rdataset, isc_boolean_t omit_final_dot, dns_rdataset_current(&rds, &rdata); CHECK(str_totext(" ", target)); CHECK(dns_rdata_tofmttext(&rdata, dns_rootname, - 0, 0, " ", target)); + 0, 0, 0, " ", target)); CHECK(str_totext("\n", target)); } } @@ -533,6 +537,7 @@ rdataset_totext(dns_rdataset_t *rdataset, ctx->style.flags, ctx->style.line_length - ctx->style.rdata_column, + ctx->style.split_width, ctx->linebreak, target)); @@ -1146,21 +1151,53 @@ dns_dumpctx_cancel(dns_dumpctx_t *dctx) { UNLOCK(&dctx->lock); } +static isc_result_t +flushandsync(FILE *f, isc_result_t result, const char *temp) { + isc_boolean_t logit = ISC_TF(result == ISC_R_SUCCESS); + + if (result == ISC_R_SUCCESS) + result = isc_stdio_flush(f); + if (result != ISC_R_SUCCESS && logit) { + if (temp != NULL) + isc_log_write(dns_lctx, ISC_LOGCATEGORY_GENERAL, + DNS_LOGMODULE_MASTERDUMP, ISC_LOG_ERROR, + "dumping to master file: %s: flush: %s", + temp, isc_result_totext(result)); + else + isc_log_write(dns_lctx, ISC_LOGCATEGORY_GENERAL, + DNS_LOGMODULE_MASTERDUMP, ISC_LOG_ERROR, + "dumping to stream: flush: %s", + isc_result_totext(result)); + logit = ISC_FALSE; + } + + if (result == ISC_R_SUCCESS) + result = isc_stdio_sync(f); + if (result != ISC_R_SUCCESS && logit) { + if (temp != NULL) + isc_log_write(dns_lctx, ISC_LOGCATEGORY_GENERAL, + DNS_LOGMODULE_MASTERDUMP, ISC_LOG_ERROR, + "dumping to master file: %s: fsync: %s", + temp, isc_result_totext(result)); + else + isc_log_write(dns_lctx, ISC_LOGCATEGORY_GENERAL, + DNS_LOGMODULE_MASTERDUMP, ISC_LOG_ERROR, + "dumping to stream: fsync: %s", + isc_result_totext(result)); + } + return (result); +} + static isc_result_t closeandrename(FILE *f, isc_result_t result, const char *temp, const char *file) { isc_result_t tresult; isc_boolean_t logit = ISC_TF(result == ISC_R_SUCCESS); - if (result == ISC_R_SUCCESS) - result = isc_stdio_sync(f); - if (result != ISC_R_SUCCESS && logit) { - isc_log_write(dns_lctx, ISC_LOGCATEGORY_GENERAL, - DNS_LOGMODULE_MASTERDUMP, ISC_LOG_ERROR, - "dumping master file: %s: fsync: %s", - temp, isc_result_totext(result)); + result = flushandsync(f, result, temp); + if (result != ISC_R_SUCCESS) logit = ISC_FALSE; - } + tresult = isc_stdio_close(f); if (result == ISC_R_SUCCESS) result = tresult; @@ -1208,7 +1245,8 @@ dump_quantum(isc_task_t *task, isc_event_t *event) { dctx->tmpfile, dctx->file); if (tresult != ISC_R_SUCCESS && result == ISC_R_SUCCESS) result = tresult; - } + } else + result = flushandsync(dctx->f, result, NULL); (dctx->done)(dctx->done_arg, result); isc_event_free(&event); dns_dumpctx_detach(&dctx); @@ -1229,7 +1267,7 @@ task_send(dns_dumpctx_t *dctx) { static isc_result_t dumpctx_create(isc_mem_t *mctx, dns_db_t *db, dns_dbversion_t *version, const dns_master_style_t *style, FILE *f, dns_dumpctx_t **dctxp, - dns_masterformat_t format) + dns_masterformat_t format, dns_masterrawheader_t *header) { dns_dumpctx_t *dctx; isc_result_t result; @@ -1253,6 +1291,10 @@ dumpctx_create(isc_mem_t *mctx, dns_db_t *db, dns_dbversion_t *version, dctx->file = NULL; dctx->tmpfile = NULL; dctx->format = format; + if (header == NULL) + dns_master_initrawheader(&dctx->header); + else + dctx->header = *header; switch (format) { case dns_masterformat_text: @@ -1320,7 +1362,7 @@ dumptostreaminc(dns_dumpctx_t *dctx) { dns_fixedname_t fixname; unsigned int nodes; dns_masterrawheader_t rawheader; - isc_uint32_t now32; + isc_uint32_t rawversion, now32; isc_time_t start; bufmem = isc_mem_get(dctx->mctx, initial_buffer_length); @@ -1355,8 +1397,6 @@ dumptostreaminc(dns_dumpctx_t *dctx) { r.base = (unsigned char *)&rawheader; r.length = sizeof(rawheader); isc_buffer_region(&buffer, &r); - isc_buffer_putuint32(&buffer, dns_masterformat_raw); - isc_buffer_putuint32(&buffer, DNS_RAWFORMAT_VERSION); #if !defined(STDTIME_ON_32BITS) || (STDTIME_ON_32BITS + 0) != 1 /* * We assume isc_stdtime_t is a 32-bit integer, @@ -1375,7 +1415,22 @@ dumptostreaminc(dns_dumpctx_t *dctx) { #else now32 = dctx->now; #endif + rawversion = 1; + if ((dctx->header.flags & DNS_MASTERRAW_COMPAT) != 0) + rawversion = 0; + isc_buffer_putuint32(&buffer, dns_masterformat_raw); + isc_buffer_putuint32(&buffer, rawversion); isc_buffer_putuint32(&buffer, now32); + + if (rawversion == 1) { + isc_buffer_putuint32(&buffer, + dctx->header.flags); + isc_buffer_putuint32(&buffer, + dctx->header.sourceserial); + isc_buffer_putuint32(&buffer, + dctx->header.lastxfrin); + } + INSIST(isc_buffer_usedlength(&buffer) <= sizeof(rawheader)); result = isc_stdio_write(buffer.base, 1, @@ -1494,7 +1549,7 @@ dns_master_dumptostreaminc(isc_mem_t *mctx, dns_db_t *db, REQUIRE(done != NULL); result = dumpctx_create(mctx, db, version, style, f, &dctx, - dns_masterformat_text); + dns_masterformat_text, NULL); if (result != ISC_R_SUCCESS) return (result); isc_task_attach(task, &dctx->task); @@ -1521,8 +1576,8 @@ dns_master_dumptostream(isc_mem_t *mctx, dns_db_t *db, const dns_master_style_t *style, FILE *f) { - return (dns_master_dumptostream2(mctx, db, version, style, - dns_masterformat_text, f)); + return (dns_master_dumptostream3(mctx, db, version, style, + dns_masterformat_text, NULL, f)); } isc_result_t @@ -1530,17 +1585,31 @@ dns_master_dumptostream2(isc_mem_t *mctx, dns_db_t *db, dns_dbversion_t *version, const dns_master_style_t *style, dns_masterformat_t format, FILE *f) +{ + return (dns_master_dumptostream3(mctx, db, version, style, + format, NULL, f)); +} + +isc_result_t +dns_master_dumptostream3(isc_mem_t *mctx, dns_db_t *db, + dns_dbversion_t *version, + const dns_master_style_t *style, + dns_masterformat_t format, + dns_masterrawheader_t *header, FILE *f) { dns_dumpctx_t *dctx = NULL; isc_result_t result; - result = dumpctx_create(mctx, db, version, style, f, &dctx, format); + result = dumpctx_create(mctx, db, version, style, f, &dctx, + format, header); if (result != ISC_R_SUCCESS) return (result); result = dumptostreaminc(dctx); INSIST(result != DNS_R_CONTINUE); dns_dumpctx_detach(&dctx); + + result = flushandsync(f, result, NULL); return (result); } @@ -1587,9 +1656,9 @@ dns_master_dumpinc(isc_mem_t *mctx, dns_db_t *db, dns_dbversion_t *version, isc_task_t *task, dns_dumpdonefunc_t done, void *done_arg, dns_dumpctx_t **dctxp) { - return (dns_master_dumpinc2(mctx, db, version, style, filename, task, + return (dns_master_dumpinc3(mctx, db, version, style, filename, task, done, done_arg, dctxp, - dns_masterformat_text)); + dns_masterformat_text, NULL)); } isc_result_t @@ -1597,6 +1666,17 @@ dns_master_dumpinc2(isc_mem_t *mctx, dns_db_t *db, dns_dbversion_t *version, const dns_master_style_t *style, const char *filename, isc_task_t *task, dns_dumpdonefunc_t done, void *done_arg, dns_dumpctx_t **dctxp, dns_masterformat_t format) +{ + return (dns_master_dumpinc3(mctx, db, version, style, filename, task, + done, done_arg, dctxp, format, NULL)); +} + +isc_result_t +dns_master_dumpinc3(isc_mem_t *mctx, dns_db_t *db, dns_dbversion_t *version, + const dns_master_style_t *style, const char *filename, + isc_task_t *task, dns_dumpdonefunc_t done, void *done_arg, + dns_dumpctx_t **dctxp, dns_masterformat_t format, + dns_masterrawheader_t *header) { FILE *f = NULL; isc_result_t result; @@ -1612,7 +1692,8 @@ dns_master_dumpinc2(isc_mem_t *mctx, dns_db_t *db, dns_dbversion_t *version, if (result != ISC_R_SUCCESS) goto cleanup; - result = dumpctx_create(mctx, db, version, style, f, &dctx, format); + result = dumpctx_create(mctx, db, version, style, f, &dctx, + format, header); if (result != ISC_R_SUCCESS) { (void)isc_stdio_close(f); (void)isc_file_remove(tempname); @@ -1648,14 +1729,23 @@ isc_result_t dns_master_dump(isc_mem_t *mctx, dns_db_t *db, dns_dbversion_t *version, const dns_master_style_t *style, const char *filename) { - return (dns_master_dump2(mctx, db, version, style, filename, - dns_masterformat_text)); + return (dns_master_dump3(mctx, db, version, style, filename, + dns_masterformat_text, NULL)); } isc_result_t dns_master_dump2(isc_mem_t *mctx, dns_db_t *db, dns_dbversion_t *version, const dns_master_style_t *style, const char *filename, dns_masterformat_t format) +{ + return (dns_master_dump3(mctx, db, version, style, filename, + format, NULL)); +} + +isc_result_t +dns_master_dump3(isc_mem_t *mctx, dns_db_t *db, dns_dbversion_t *version, + const dns_master_style_t *style, const char *filename, + dns_masterformat_t format, dns_masterrawheader_t *header) { FILE *f = NULL; isc_result_t result; @@ -1666,7 +1756,8 @@ dns_master_dump2(isc_mem_t *mctx, dns_db_t *db, dns_dbversion_t *version, if (result != ISC_R_SUCCESS) return (result); - result = dumpctx_create(mctx, db, version, style, f, &dctx, format); + result = dumpctx_create(mctx, db, version, style, f, &dctx, + format, header); if (result != ISC_R_SUCCESS) goto cleanup; @@ -1776,6 +1867,19 @@ dns_master_stylecreate(dns_master_style_t **stylep, unsigned int flags, unsigned int type_column, unsigned int rdata_column, unsigned int line_length, unsigned int tab_width, isc_mem_t *mctx) +{ + return (dns_master_stylecreate2(stylep, flags, ttl_column, + class_column, type_column, + rdata_column, line_length, + tab_width, 0xffffffff, mctx)); +} + +isc_result_t +dns_master_stylecreate2(dns_master_style_t **stylep, unsigned int flags, + unsigned int ttl_column, unsigned int class_column, + unsigned int type_column, unsigned int rdata_column, + unsigned int line_length, unsigned int tab_width, + unsigned int split_width, isc_mem_t *mctx) { dns_master_style_t *style; @@ -1791,6 +1895,7 @@ dns_master_stylecreate(dns_master_style_t **stylep, unsigned int flags, style->rdata_column = rdata_column; style->line_length = line_length; style->tab_width = tab_width; + style->split_width = split_width; *stylep = style; return (ISC_R_SUCCESS); diff --git a/contrib/bind9/lib/dns/message.c b/contrib/bind9/lib/dns/message.c index d36edbae3b1..53efc5a1beb 100644 --- a/contrib/bind9/lib/dns/message.c +++ b/contrib/bind9/lib/dns/message.c @@ -732,7 +732,9 @@ dns_message_create(isc_mem_t *mctx, unsigned int intent, dns_message_t **msgp) for (i = 0; i < DNS_SECTION_MAX; i++) ISC_LIST_INIT(m->sections[i]); - m->mctx = mctx; + + m->mctx = NULL; + isc_mem_attach(mctx, &m->mctx); ISC_LIST_INIT(m->scratchpad); ISC_LIST_INIT(m->cleanup); @@ -786,7 +788,7 @@ dns_message_create(isc_mem_t *mctx, unsigned int intent, dns_message_t **msgp) if (m->rdspool != NULL) isc_mempool_destroy(&m->rdspool); m->magic = 0; - isc_mem_put(mctx, m, sizeof(dns_message_t)); + isc_mem_putanddetach(&mctx, m, sizeof(dns_message_t)); return (ISC_R_NOMEMORY); } @@ -815,7 +817,7 @@ dns_message_destroy(dns_message_t **msgp) { isc_mempool_destroy(&msg->namepool); isc_mempool_destroy(&msg->rdspool); msg->magic = 0; - isc_mem_put(msg->mctx, msg, sizeof(dns_message_t)); + isc_mem_putanddetach(&msg->mctx, msg, sizeof(dns_message_t)); } static isc_result_t diff --git a/contrib/bind9/lib/dns/nsec.c b/contrib/bind9/lib/dns/nsec.c index 41b5dc3293e..e446806b4e6 100644 --- a/contrib/bind9/lib/dns/nsec.c +++ b/contrib/bind9/lib/dns/nsec.c @@ -42,28 +42,61 @@ goto failure; \ } while (0) -static void -set_bit(unsigned char *array, unsigned int index, unsigned int bit) { +void +dns_nsec_setbit(unsigned char *array, unsigned int type, unsigned int bit) { unsigned int shift, mask; - shift = 7 - (index % 8); + shift = 7 - (type % 8); mask = 1 << shift; if (bit != 0) - array[index / 8] |= mask; + array[type / 8] |= mask; else - array[index / 8] &= (~mask & 0xFF); + array[type / 8] &= (~mask & 0xFF); } -static unsigned int -bit_isset(unsigned char *array, unsigned int index) { +isc_boolean_t +dns_nsec_isset(const unsigned char *array, unsigned int type) { unsigned int byte, shift, mask; - byte = array[index / 8]; - shift = 7 - (index % 8); + byte = array[type / 8]; + shift = 7 - (type % 8); mask = 1 << shift; - return ((byte & mask) != 0); + return (ISC_TF(byte & mask)); +} + +unsigned int +dns_nsec_compressbitmap(unsigned char *map, const unsigned char *raw, + unsigned int max_type) +{ + unsigned char *start = map; + unsigned int window; + int octet; + + if (raw == NULL) + return (0); + + for (window = 0; window < 256; window++) { + if (window * 256 > max_type) + break; + for (octet = 31; octet >= 0; octet--) + if (*(raw + octet) != 0) + break; + if (octet < 0) { + raw += 32; + continue; + } + *map++ = window; + *map++ = octet + 1; + /* + * Note: potential overlapping move. + */ + memmove(map, raw, octet + 1); + map += octet + 1; + raw += 32; + } + return (map - start); } isc_result_t @@ -74,8 +107,7 @@ dns_nsec_buildrdata(dns_db_t *db, dns_dbversion_t *version, isc_result_t result; dns_rdataset_t rdataset; isc_region_t r; - unsigned int i, window; - int octet; + unsigned int i; unsigned char *nsec_bits, *bm; unsigned int max_type; @@ -91,8 +123,8 @@ dns_nsec_buildrdata(dns_db_t *db, dns_dbversion_t *version, */ bm = r.base + r.length + 512; nsec_bits = r.base + r.length; - set_bit(bm, dns_rdatatype_rrsig, 1); - set_bit(bm, dns_rdatatype_nsec, 1); + dns_nsec_setbit(bm, dns_rdatatype_rrsig, 1); + dns_nsec_setbit(bm, dns_rdatatype_nsec, 1); max_type = dns_rdatatype_nsec; dns_rdataset_init(&rdataset); rdsiter = NULL; @@ -109,7 +141,7 @@ dns_nsec_buildrdata(dns_db_t *db, dns_dbversion_t *version, rdataset.type != dns_rdatatype_rrsig) { if (rdataset.type > max_type) max_type = rdataset.type; - set_bit(bm, rdataset.type, 1); + dns_nsec_setbit(bm, rdataset.type, 1); } dns_rdataset_disassociate(&rdataset); } @@ -117,12 +149,12 @@ dns_nsec_buildrdata(dns_db_t *db, dns_dbversion_t *version, /* * At zone cuts, deny the existence of glue in the parent zone. */ - if (bit_isset(bm, dns_rdatatype_ns) && - ! bit_isset(bm, dns_rdatatype_soa)) { + if (dns_nsec_isset(bm, dns_rdatatype_ns) && + ! dns_nsec_isset(bm, dns_rdatatype_soa)) { for (i = 0; i <= max_type; i++) { - if (bit_isset(bm, i) && + if (dns_nsec_isset(bm, i) && ! dns_rdatatype_iszonecutauth((dns_rdatatype_t)i)) - set_bit(bm, i, 0); + dns_nsec_setbit(bm, i, 0); } } @@ -130,22 +162,8 @@ dns_nsec_buildrdata(dns_db_t *db, dns_dbversion_t *version, if (result != ISC_R_NOMORE) return (result); - for (window = 0; window < 256; window++) { - if (window * 256 > max_type) - break; - for (octet = 31; octet >= 0; octet--) - if (bm[window * 32 + octet] != 0) - break; - if (octet < 0) - continue; - nsec_bits[0] = window; - nsec_bits[1] = octet + 1; - /* - * Note: potential overlapping move. - */ - memmove(&nsec_bits[2], &bm[window * 32], octet + 1); - nsec_bits += 3 + octet; - } + nsec_bits += dns_nsec_compressbitmap(nsec_bits, bm, max_type); + r.length = nsec_bits - r.base; INSIST(r.length <= DNS_NSEC_BUFFERSIZE); dns_rdata_fromregion(rdata, @@ -156,7 +174,6 @@ dns_nsec_buildrdata(dns_db_t *db, dns_dbversion_t *version, return (ISC_R_SUCCESS); } - isc_result_t dns_nsec_build(dns_db_t *db, dns_dbversion_t *version, dns_dbnode_t *node, dns_name_t *target, dns_ttl_t ttl) @@ -217,8 +234,8 @@ dns_nsec_typepresent(dns_rdata_t *nsec, dns_rdatatype_t type) { if ((window + 1) * 256 <= type) continue; if (type < (window * 256) + len * 8) - present = ISC_TF(bit_isset(&nsecstruct.typebits[i], - type % 256)); + present = ISC_TF(dns_nsec_isset(&nsecstruct.typebits[i], + type % 256)); break; } dns_rdata_freestruct(&nsecstruct); @@ -246,10 +263,8 @@ dns_nsec_nseconly(dns_db_t *db, dns_dbversion_t *version, 0, 0, &rdataset, NULL); dns_db_detachnode(db, &node); - if (result == ISC_R_NOTFOUND) { + if (result == ISC_R_NOTFOUND) *answer = ISC_FALSE; - return (ISC_R_SUCCESS); - } if (result != ISC_R_SUCCESS) return (result); for (result = dns_rdataset_first(&rdataset); diff --git a/contrib/bind9/lib/dns/nsec3.c b/contrib/bind9/lib/dns/nsec3.c index 7ec6b4cb881..935f515d23e 100644 --- a/contrib/bind9/lib/dns/nsec3.c +++ b/contrib/bind9/lib/dns/nsec3.c @@ -34,6 +34,7 @@ #include #include #include +#include #include #include #include @@ -50,32 +51,9 @@ #define OPTOUT(x) (((x) & DNS_NSEC3FLAG_OPTOUT) != 0) #define CREATE(x) (((x) & DNS_NSEC3FLAG_CREATE) != 0) +#define INITIAL(x) (((x) & DNS_NSEC3FLAG_INITIAL) != 0) #define REMOVE(x) (((x) & DNS_NSEC3FLAG_REMOVE) != 0) -static void -set_bit(unsigned char *array, unsigned int index, unsigned int bit) { - unsigned int shift, mask; - - shift = 7 - (index % 8); - mask = 1 << shift; - - if (bit != 0) - array[index / 8] |= mask; - else - array[index / 8] &= (~mask & 0xFF); -} - -static unsigned int -bit_isset(unsigned char *array, unsigned int index) { - unsigned int byte, shift, mask; - - byte = array[index / 8]; - shift = 7 - (index % 8); - mask = 1 << shift; - - return ((byte & mask) != 0); -} - isc_result_t dns_nsec3_buildrdata(dns_db_t *db, dns_dbversion_t *version, dns_dbnode_t *node, unsigned int hashalg, @@ -87,8 +65,7 @@ dns_nsec3_buildrdata(dns_db_t *db, dns_dbversion_t *version, isc_result_t result; dns_rdataset_t rdataset; isc_region_t r; - unsigned int i, window; - int octet; + unsigned int i; isc_boolean_t found; isc_boolean_t found_ns; isc_boolean_t need_rrsig; @@ -156,7 +133,7 @@ dns_nsec3_buildrdata(dns_db_t *db, dns_dbversion_t *version, rdataset.type != dns_rdatatype_rrsig) { if (rdataset.type > max_type) max_type = rdataset.type; - set_bit(bm, rdataset.type, 1); + dns_nsec_setbit(bm, rdataset.type, 1); /* * Work out if we need to set the RRSIG bit for * this node. We set the RRSIG bit if either of @@ -179,18 +156,18 @@ dns_nsec3_buildrdata(dns_db_t *db, dns_dbversion_t *version, if ((found && !found_ns) || need_rrsig) { if (dns_rdatatype_rrsig > max_type) max_type = dns_rdatatype_rrsig; - set_bit(bm, dns_rdatatype_rrsig, 1); + dns_nsec_setbit(bm, dns_rdatatype_rrsig, 1); } /* * At zone cuts, deny the existence of glue in the parent zone. */ - if (bit_isset(bm, dns_rdatatype_ns) && - ! bit_isset(bm, dns_rdatatype_soa)) { + if (dns_nsec_isset(bm, dns_rdatatype_ns) && + ! dns_nsec_isset(bm, dns_rdatatype_soa)) { for (i = 0; i <= max_type; i++) { - if (bit_isset(bm, i) && + if (dns_nsec_isset(bm, i) && ! dns_rdatatype_iszonecutauth((dns_rdatatype_t)i)) - set_bit(bm, i, 0); + dns_nsec_setbit(bm, i, 0); } } @@ -199,22 +176,7 @@ dns_nsec3_buildrdata(dns_db_t *db, dns_dbversion_t *version, return (result); collapse_bitmap: - for (window = 0; window < 256; window++) { - if (window * 256 > max_type) - break; - for (octet = 31; octet >= 0; octet--) - if (bm[window * 32 + octet] != 0) - break; - if (octet < 0) - continue; - nsec_bits[0] = window; - nsec_bits[1] = octet + 1; - /* - * Note: potentially overlapping move. - */ - memmove(&nsec_bits[2], &bm[window * 32], octet + 1); - nsec_bits += 3 + octet; - } + nsec_bits += dns_nsec_compressbitmap(nsec_bits, bm, max_type); r.length = nsec_bits - r.base; INSIST(r.length <= DNS_NSEC3_BUFFERSIZE); dns_rdata_fromregion(rdata, dns_db_class(db), dns_rdatatype_nsec3, &r); @@ -249,8 +211,8 @@ dns_nsec3_typepresent(dns_rdata_t *rdata, dns_rdatatype_t type) { if ((window + 1) * 256 <= type) continue; if (type < (window * 256) + len * 8) - present = ISC_TF(bit_isset(&nsec3.typebits[i], - type % 256)); + present = ISC_TF(dns_nsec_isset(&nsec3.typebits[i], + type % 256)); break; } dns_rdata_freestruct(&nsec3); @@ -1054,7 +1016,8 @@ rr_exists(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name, #ifdef BIND9 isc_result_t dns_nsec3param_deletechains(dns_db_t *db, dns_dbversion_t *ver, - dns_zone_t *zone, dns_diff_t *diff) + dns_zone_t *zone, isc_boolean_t nonsec, + dns_diff_t *diff) { dns_dbnode_t *node = NULL; dns_difftuple_t *tuple = NULL; @@ -1098,7 +1061,9 @@ dns_nsec3param_deletechains(dns_db_t *db, dns_dbversion_t *ver, dns_nsec3param_toprivate(&rdata, &private, privatetype, buf, sizeof(buf)); - buf[2] = DNS_NSEC3FLAG_REMOVE | DNS_NSEC3FLAG_NONSEC; + buf[2] = DNS_NSEC3FLAG_REMOVE; + if (nonsec) + buf[2] |= DNS_NSEC3FLAG_NONSEC; CHECK(rr_exists(db, ver, origin, &private, &flag)); @@ -1129,6 +1094,7 @@ dns_nsec3param_deletechains(dns_db_t *db, dns_dbversion_t *ver, for (result = dns_rdataset_first(&rdataset); result == ISC_R_SUCCESS; result = dns_rdataset_next(&rdataset)) { + dns_rdata_reset(&rdata); dns_rdataset_current(&rdataset, &rdata); INSIST(rdata.length <= sizeof(buf)); memcpy(buf, rdata.data, rdata.length); @@ -1138,10 +1104,9 @@ dns_nsec3param_deletechains(dns_db_t *db, dns_dbversion_t *ver, * <0(1), hash(1), flags(1), iterations(2), saltlen(1)> */ if (rdata.length < 6 || buf[0] != 0 || - buf[2] == (DNS_NSEC3FLAG_REMOVE | DNS_NSEC3FLAG_NONSEC)) { - dns_rdata_reset(&rdata); + (buf[2] & DNS_NSEC3FLAG_REMOVE) != 0 || + (nonsec && (buf[2] & DNS_NSEC3FLAG_NONSEC) != 0)) continue; - } CHECK(dns_difftuple_create(diff->mctx, DNS_DIFFOP_DEL, origin, 0, &rdata, &tuple)); @@ -1149,7 +1114,9 @@ dns_nsec3param_deletechains(dns_db_t *db, dns_dbversion_t *ver, INSIST(tuple == NULL); rdata.data = buf; - buf[2] = DNS_NSEC3FLAG_REMOVE | DNS_NSEC3FLAG_NONSEC; + buf[2] = DNS_NSEC3FLAG_REMOVE; + if (nonsec) + buf[2] |= DNS_NSEC3FLAG_NONSEC; CHECK(rr_exists(db, ver, origin, &rdata, &flag)); @@ -1159,7 +1126,6 @@ dns_nsec3param_deletechains(dns_db_t *db, dns_dbversion_t *ver, CHECK(do_one_tuple(&tuple, db, ver, diff)); INSIST(tuple == NULL); } - dns_rdata_reset(&rdata); } if (result != ISC_R_NOMORE) goto failure; diff --git a/contrib/bind9/lib/dns/openssldh_link.c b/contrib/bind9/lib/dns/openssldh_link.c index 9fe9bb52524..36b8a412a3e 100644 --- a/contrib/bind9/lib/dns/openssldh_link.c +++ b/contrib/bind9/lib/dns/openssldh_link.c @@ -634,6 +634,7 @@ static dst_func_t openssldh_functions = { NULL, /*%< adddata */ NULL, /*%< openssldh_sign */ NULL, /*%< openssldh_verify */ + NULL, /*%< openssldh_verify2 */ openssldh_computesecret, openssldh_compare, openssldh_paramcompare, diff --git a/contrib/bind9/lib/dns/openssldsa_link.c b/contrib/bind9/lib/dns/openssldsa_link.c index 66d47bb604a..8bea1c09e05 100644 --- a/contrib/bind9/lib/dns/openssldsa_link.c +++ b/contrib/bind9/lib/dns/openssldsa_link.c @@ -624,6 +624,7 @@ static dst_func_t openssldsa_functions = { openssldsa_adddata, openssldsa_sign, openssldsa_verify, + NULL, /*%< verify2 */ NULL, /*%< computesecret */ openssldsa_compare, NULL, /*%< paramcompare */ diff --git a/contrib/bind9/lib/dns/opensslecdsa_link.c b/contrib/bind9/lib/dns/opensslecdsa_link.c index 1cf30f839ab..c3f5061b754 100644 --- a/contrib/bind9/lib/dns/opensslecdsa_link.c +++ b/contrib/bind9/lib/dns/opensslecdsa_link.c @@ -572,6 +572,7 @@ static dst_func_t opensslecdsa_functions = { opensslecdsa_adddata, opensslecdsa_sign, opensslecdsa_verify, + NULL, /*%< verify2 */ NULL, /*%< computesecret */ opensslecdsa_compare, NULL, /*%< paramcompare */ diff --git a/contrib/bind9/lib/dns/opensslgost_link.c b/contrib/bind9/lib/dns/opensslgost_link.c index 098e31243df..1ce4405eb21 100644 --- a/contrib/bind9/lib/dns/opensslgost_link.c +++ b/contrib/bind9/lib/dns/opensslgost_link.c @@ -373,6 +373,7 @@ static dst_func_t opensslgost_functions = { opensslgost_adddata, opensslgost_sign, opensslgost_verify, + NULL, /*%< verify2 */ NULL, /*%< computesecret */ opensslgost_compare, NULL, /*%< paramcompare */ diff --git a/contrib/bind9/lib/dns/opensslrsa_link.c b/contrib/bind9/lib/dns/opensslrsa_link.c index 2430f242913..fa7412cbddb 100644 --- a/contrib/bind9/lib/dns/opensslrsa_link.c +++ b/contrib/bind9/lib/dns/opensslrsa_link.c @@ -55,6 +55,13 @@ #include #endif +/* + * Limit the size of public exponents. + */ +#ifndef RSA_MAX_PUBEXP_BITS +#define RSA_MAX_PUBEXP_BITS 35 +#endif + /* * We don't use configure for windows so enforce the OpenSSL version * here. Unlike with configure we don't support overriding this test. @@ -503,12 +510,14 @@ opensslrsa_sign(dst_context_t *dctx, isc_buffer_t *sig) { } static isc_result_t -opensslrsa_verify(dst_context_t *dctx, const isc_region_t *sig) { +opensslrsa_verify2(dst_context_t *dctx, int maxbits, const isc_region_t *sig) { dst_key_t *key = dctx->key; int status = 0; #if USE_EVP EVP_MD_CTX *evp_md_ctx = dctx->ctxdata.evp_md_ctx; EVP_PKEY *pkey = key->keydata.pkey; + RSA *rsa; + int bits; #else /* note: ISC_SHA512_DIGESTLENGTH >= ISC_*_DIGESTLENGTH */ unsigned char digest[ISC_SHA512_DIGESTLENGTH]; @@ -528,6 +537,14 @@ opensslrsa_verify(dst_context_t *dctx, const isc_region_t *sig) { dctx->key->key_alg == DST_ALG_RSASHA512); #if USE_EVP + rsa = EVP_PKEY_get1_RSA(pkey); + if (rsa == NULL) + return (dst__openssl_toresult(DST_R_OPENSSLFAILURE)); + bits = BN_num_bits(rsa->e); + RSA_free(rsa); + if (bits > maxbits && maxbits != 0) + return (DST_R_VERIFYFAILURE); + status = EVP_VerifyFinal(evp_md_ctx, sig->base, sig->length, pkey); switch (status) { case 1: @@ -540,6 +557,9 @@ opensslrsa_verify(dst_context_t *dctx, const isc_region_t *sig) { DST_R_VERIFYFAILURE)); } #else + if (BN_num_bits(rsa->e) > maxbits && maxbits != 0) + return (DST_R_VERIFYFAILURE); + switch (dctx->key->key_alg) { case DST_ALG_RSAMD5: { @@ -652,6 +672,11 @@ opensslrsa_verify(dst_context_t *dctx, const isc_region_t *sig) { #endif } +static isc_result_t +opensslrsa_verify(dst_context_t *dctx, const isc_region_t *sig) { + return (opensslrsa_verify2(dctx, 0, sig)); +} + static isc_boolean_t opensslrsa_compare(const dst_key_t *key1, const dst_key_t *key2) { int status; @@ -764,7 +789,7 @@ opensslrsa_generate(dst_key_t *key, int exp, void (*callback)(int)) { BN_set_bit(e, 0); BN_set_bit(e, 16); } else { - /* F5 0x100000001 */ + /* (phased-out) F5 0x100000001 */ BN_set_bit(e, 0); BN_set_bit(e, 32); } @@ -1219,6 +1244,8 @@ opensslrsa_parse(dst_key_t *key, isc_lex_t *lexer, dst_key_t *pub) { DST_RET(dst__openssl_toresult(DST_R_OPENSSLFAILURE)); if (rsa_check(rsa, pubrsa) != ISC_R_SUCCESS) DST_RET(DST_R_INVALIDPRIVATEKEY); + if (BN_num_bits(rsa->e) > RSA_MAX_PUBEXP_BITS) + DST_RET(ISC_R_RANGE); if (pubrsa != NULL) RSA_free(pubrsa); key->key_size = EVP_PKEY_bits(pkey); @@ -1301,6 +1328,8 @@ opensslrsa_parse(dst_key_t *key, isc_lex_t *lexer, dst_key_t *pub) { if (rsa_check(rsa, pubrsa) != ISC_R_SUCCESS) DST_RET(DST_R_INVALIDPRIVATEKEY); + if (BN_num_bits(rsa->e) > RSA_MAX_PUBEXP_BITS) + DST_RET(ISC_R_RANGE); key->key_size = BN_num_bits(rsa->n); if (pubrsa != NULL) RSA_free(pubrsa); @@ -1319,7 +1348,7 @@ opensslrsa_parse(dst_key_t *key, isc_lex_t *lexer, dst_key_t *pub) { RSA_free(rsa); if (pubrsa != NULL) RSA_free(pubrsa); - opensslrsa_destroy(key); + key->keydata.generic = NULL; dst__privstruct_free(&priv, mctx); memset(&priv, 0, sizeof(priv)); return (ret); @@ -1374,6 +1403,8 @@ opensslrsa_fromlabel(dst_key_t *key, const char *engine, const char *label, DST_RET(dst__openssl_toresult(DST_R_OPENSSLFAILURE)); if (rsa_check(rsa, pubrsa) != ISC_R_SUCCESS) DST_RET(DST_R_INVALIDPRIVATEKEY); + if (BN_num_bits(rsa->e) > RSA_MAX_PUBEXP_BITS) + DST_RET(ISC_R_RANGE); if (pubrsa != NULL) RSA_free(pubrsa); key->key_size = EVP_PKEY_bits(pkey); @@ -1409,6 +1440,7 @@ static dst_func_t opensslrsa_functions = { opensslrsa_adddata, opensslrsa_sign, opensslrsa_verify, + opensslrsa_verify2, NULL, /*%< computesecret */ opensslrsa_compare, NULL, /*%< paramcompare */ diff --git a/contrib/bind9/lib/dns/private.c b/contrib/bind9/lib/dns/private.c index b0cb96f5ee0..6521279f2d9 100644 --- a/contrib/bind9/lib/dns/private.c +++ b/contrib/bind9/lib/dns/private.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009, 2012 Internet Systems Consortium, Inc. ("ISC") + * Copyright (C) 2009, 2011, 2012 Internet Systems Consortium, Inc. ("ISC") * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -44,6 +44,7 @@ #define REMOVE(x) (((x) & DNS_NSEC3FLAG_REMOVE) != 0) #define CREATE(x) (((x) & DNS_NSEC3FLAG_CREATE) != 0) +#define INITIAL(x) (((x) & DNS_NSEC3FLAG_INITIAL) != 0) #define NONSEC(x) (((x) & DNS_NSEC3FLAG_NONSEC) != 0) #define CHECK(x) do { \ @@ -149,7 +150,7 @@ dns_private_chains(dns_db_t *db, dns_dbversion_t *ver, } /* - * Look to see if we also need to be creating a NSEC3 chains. + * Look to see if we also need to be creating a NSEC3 chain. */ if (dns_rdataset_isassociated(&nsecset)) { if (build_nsec != NULL) @@ -293,3 +294,78 @@ dns_private_chains(dns_db_t *db, dns_dbversion_t *ver, dns_db_detachnode(db, &node); return (result); } + +isc_result_t +dns_private_totext(dns_rdata_t *private, isc_buffer_t *buf) { + isc_result_t result; + + if (private->length < 5) + return (ISC_R_NOTFOUND); + + if (private->data[0] == 0) { + unsigned char nsec3buf[DNS_NSEC3PARAM_BUFFERSIZE]; + unsigned char newbuf[DNS_NSEC3PARAM_BUFFERSIZE]; + dns_rdata_t rdata = DNS_RDATA_INIT; + dns_rdata_nsec3param_t nsec3param; + isc_boolean_t remove, init, nonsec; + isc_buffer_t b; + + if (!dns_nsec3param_fromprivate(private, &rdata, nsec3buf, + sizeof(nsec3buf))) + CHECK(ISC_R_FAILURE); + + CHECK(dns_rdata_tostruct(&rdata, &nsec3param, NULL)); + + remove = ISC_TF((nsec3param.flags & DNS_NSEC3FLAG_REMOVE) != 0); + init = ISC_TF((nsec3param.flags & DNS_NSEC3FLAG_INITIAL) != 0); + nonsec = ISC_TF((nsec3param.flags & DNS_NSEC3FLAG_NONSEC) != 0); + + nsec3param.flags &= ~(DNS_NSEC3FLAG_CREATE| + DNS_NSEC3FLAG_REMOVE| + DNS_NSEC3FLAG_INITIAL| + DNS_NSEC3FLAG_NONSEC); + + if (init) + isc_buffer_putstr(buf, "Pending NSEC3 chain "); + else if (remove) + isc_buffer_putstr(buf, "Removing NSEC3 chain "); + else + isc_buffer_putstr(buf, "Creating NSEC3 chain "); + + dns_rdata_reset(&rdata); + isc_buffer_init(&b, newbuf, sizeof(newbuf)); + CHECK(dns_rdata_fromstruct(&rdata, dns_rdataclass_in, + dns_rdatatype_nsec3param, + &nsec3param, &b)); + + CHECK(dns_rdata_totext(&rdata, NULL, buf)); + + if (remove && !nonsec) + isc_buffer_putstr(buf, " / creating NSEC chain"); + } else if (private->length == 5) { + unsigned char alg = private->data[0]; + dns_keytag_t keyid = (private->data[2] | private->data[1] << 8); + char keybuf[BUFSIZ], algbuf[DNS_SECALG_FORMATSIZE]; + isc_boolean_t remove = ISC_TF(private->data[3] != 0); + isc_boolean_t complete = ISC_TF(private->data[4] != 0); + + if (remove && complete) + isc_buffer_putstr(buf, "Done removing signatures for "); + else if (remove) + isc_buffer_putstr(buf, "Removing signatures for "); + else if (complete) + isc_buffer_putstr(buf, "Done signing with "); + else + isc_buffer_putstr(buf, "Signing with "); + + dns_secalg_format(alg, algbuf, sizeof(algbuf)); + sprintf(keybuf, "key %d/%s", keyid, algbuf); + isc_buffer_putstr(buf, keybuf); + } else + return (ISC_R_NOTFOUND); + + isc_buffer_putuint8(buf, 0); + result = ISC_R_SUCCESS; + failure: + return (result); +} diff --git a/contrib/bind9/lib/dns/rbt.c b/contrib/bind9/lib/dns/rbt.c index eb95d14fbc2..7381b4a325b 100644 --- a/contrib/bind9/lib/dns/rbt.c +++ b/contrib/bind9/lib/dns/rbt.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004, 2005, 2007-2009, 2011, 2012 Internet Systems Consortium, Inc. ("ISC") + * Copyright (C) 2004, 2005, 2007-2009, 2011-2013 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 1999-2003 Internet Software Consortium. * * Permission to use, copy, modify, and/or distribute this software for any @@ -248,7 +248,8 @@ dns_rbt_create(isc_mem_t *mctx, void (*deleter)(void *, void *), if (rbt == NULL) return (ISC_R_NOMEMORY); - rbt->mctx = mctx; + rbt->mctx = NULL; + isc_mem_attach(mctx, &rbt->mctx); rbt->data_deleter = deleter; rbt->deleter_arg = deleter_arg; rbt->root = NULL; @@ -259,7 +260,7 @@ dns_rbt_create(isc_mem_t *mctx, void (*deleter)(void *, void *), #ifdef DNS_RBT_USEHASH result = inithash(rbt); if (result != ISC_R_SUCCESS) { - isc_mem_put(mctx, rbt, sizeof(*rbt)); + isc_mem_putanddetach(&rbt->mctx, rbt, sizeof(*rbt)); return (result); } #endif @@ -299,7 +300,7 @@ dns_rbt_destroy2(dns_rbt_t **rbtp, unsigned int quantum) { rbt->magic = 0; - isc_mem_put(rbt->mctx, rbt, sizeof(*rbt)); + isc_mem_putanddetach(&rbt->mctx, rbt, sizeof(*rbt)); *rbtp = NULL; return (ISC_R_SUCCESS); } diff --git a/contrib/bind9/lib/dns/rbtdb.c b/contrib/bind9/lib/dns/rbtdb.c index f6f96ab9315..bff52b87ef9 100644 --- a/contrib/bind9/lib/dns/rbtdb.c +++ b/contrib/bind9/lib/dns/rbtdb.c @@ -4553,7 +4553,8 @@ find_coveringnsec(rbtdb_search_t *search, dns_dbnode_t **nodep, */ #ifdef BIND9 static isc_result_t -rpz_enabled(dns_db_t *db, dns_rpz_st_t *st) { +rpz_enabled(dns_db_t *db, dns_rpz_st_t *st) +{ dns_rbtdb_t *rbtdb; isc_result_t result; @@ -7464,8 +7465,10 @@ static dns_dbmethods_t zone_methods = { rpz_findips, #else NULL, - NULL + NULL, #endif + NULL, + NULL }; static dns_dbmethods_t cache_methods = { @@ -7506,6 +7509,8 @@ static dns_dbmethods_t cache_methods = { isdnssec, getrrsetstats, NULL, + NULL, + NULL, NULL }; diff --git a/contrib/bind9/lib/dns/rdata.c b/contrib/bind9/lib/dns/rdata.c index 3865f42fe08..a83dab462ce 100644 --- a/contrib/bind9/lib/dns/rdata.c +++ b/contrib/bind9/lib/dns/rdata.c @@ -15,6 +15,8 @@ * PERFORMANCE OF THIS SOFTWARE. */ +/* $Id$ */ + /*! \file */ #include @@ -878,7 +880,8 @@ dns_rdata_totext(dns_rdata_t *rdata, dns_name_t *origin, isc_buffer_t *target) isc_result_t dns_rdata_tofmttext(dns_rdata_t *rdata, dns_name_t *origin, unsigned int flags, unsigned int width, - const char *linebreak, isc_buffer_t *target) + unsigned int split_width, const char *linebreak, + isc_buffer_t *target) { dns_rdata_textctx_t tctx; @@ -889,11 +892,16 @@ dns_rdata_tofmttext(dns_rdata_t *rdata, dns_name_t *origin, */ tctx.origin = origin; tctx.flags = flags; - if ((flags & DNS_STYLEFLAG_MULTILINE) != 0) { + if (split_width == 0xffffffff) tctx.width = width; + else + tctx.width = split_width; + + if ((flags & DNS_STYLEFLAG_MULTILINE) != 0) tctx.linebreak = linebreak; - } else { - tctx.width = 60; /* Used for hex word length only. */ + else { + if (split_width == 0xffffffff) + tctx.width = 60; /* Used for hex word length only. */ tctx.linebreak = " "; } return (rdata_totext(rdata, &tctx, target)); @@ -1409,7 +1417,8 @@ multitxt_fromwire(isc_buffer_t *source, isc_buffer_t *target) { if (n > tregion.length) return (ISC_R_NOSPACE); - memcpy(tregion.base, sregion.base, n); + if (tregion.base != sregion.base) + memcpy(tregion.base, sregion.base, n); isc_buffer_forward(source, n); isc_buffer_add(target, n); isc_buffer_activeregion(source, &sregion); diff --git a/contrib/bind9/lib/dns/rdata/any_255/tsig_250.c b/contrib/bind9/lib/dns/rdata/any_255/tsig_250.c index 0046ed052ac..3f91f91c009 100644 --- a/contrib/bind9/lib/dns/rdata/any_255/tsig_250.c +++ b/contrib/bind9/lib/dns/rdata/any_255/tsig_250.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004, 2005, 2007, 2009, 2012 Internet Systems Consortium, Inc. ("ISC") + * Copyright (C) 2004, 2005, 2007, 2009, 2011, 2012 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 1999-2003 Internet Software Consortium. * * Permission to use, copy, modify, and/or distribute this software for any @@ -202,8 +202,11 @@ totext_any_tsig(ARGS_TOTEXT) { if ((tctx->flags & DNS_STYLEFLAG_MULTILINE) != 0) RETERR(str_totext(" (", target)); RETERR(str_totext(tctx->linebreak, target)); - RETERR(isc_base64_totext(&sigr, tctx->width - 2, - tctx->linebreak, target)); + if (tctx->width == 0) /* No splitting */ + RETERR(isc_base64_totext(&sigr, 60, "", target)); + else + RETERR(isc_base64_totext(&sigr, tctx->width - 2, + tctx->linebreak, target)); if ((tctx->flags & DNS_STYLEFLAG_MULTILINE) != 0) RETERR(str_totext(" ) ", target)); else @@ -236,7 +239,10 @@ totext_any_tsig(ARGS_TOTEXT) { /* * Other. */ - return (isc_base64_totext(&sr, 60, " ", target)); + if (tctx->width == 0) /* No splitting */ + return (isc_base64_totext(&sr, 60, "", target)); + else + return (isc_base64_totext(&sr, 60, " ", target)); } static inline isc_result_t diff --git a/contrib/bind9/lib/dns/rdata/generic/cert_37.c b/contrib/bind9/lib/dns/rdata/generic/cert_37.c index 2a447a67a26..a03290a60a0 100644 --- a/contrib/bind9/lib/dns/rdata/generic/cert_37.c +++ b/contrib/bind9/lib/dns/rdata/generic/cert_37.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004, 2005, 2007, 2009, 2012 Internet Systems Consortium, Inc. ("ISC") + * Copyright (C) 2004, 2005, 2007, 2009, 2011, 2012 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 1999-2003 Internet Software Consortium. * * Permission to use, copy, modify, and/or distribute this software for any @@ -109,8 +109,11 @@ totext_cert(ARGS_TOTEXT) { if ((tctx->flags & DNS_STYLEFLAG_MULTILINE) != 0) RETERR(str_totext(" (", target)); RETERR(str_totext(tctx->linebreak, target)); - RETERR(isc_base64_totext(&sr, tctx->width - 2, - tctx->linebreak, target)); + if (tctx->width == 0) /* No splitting */ + RETERR(isc_base64_totext(&sr, 60, "", target)); + else + RETERR(isc_base64_totext(&sr, tctx->width - 2, + tctx->linebreak, target)); if ((tctx->flags & DNS_STYLEFLAG_MULTILINE) != 0) RETERR(str_totext(" )", target)); return (ISC_R_SUCCESS); diff --git a/contrib/bind9/lib/dns/rdata/generic/dlv_32769.c b/contrib/bind9/lib/dns/rdata/generic/dlv_32769.c index 97f37f7d9ed..5751ad89452 100644 --- a/contrib/bind9/lib/dns/rdata/generic/dlv_32769.c +++ b/contrib/bind9/lib/dns/rdata/generic/dlv_32769.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004, 2006, 2007, 2009, 2010, 2012, 2013 Internet Systems Consortium, Inc. ("ISC") + * Copyright (C) 2004, 2006, 2007, 2009-2013 Internet Systems Consortium, Inc. ("ISC") * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -137,7 +137,11 @@ totext_dlv(ARGS_TOTEXT) { if ((tctx->flags & DNS_STYLEFLAG_MULTILINE) != 0) RETERR(str_totext(" (", target)); RETERR(str_totext(tctx->linebreak, target)); - RETERR(isc_hex_totext(&sr, tctx->width - 2, tctx->linebreak, target)); + if (tctx->width == 0) /* No splitting */ + RETERR(isc_hex_totext(&sr, 0, "", target)); + else + RETERR(isc_hex_totext(&sr, tctx->width - 2, + tctx->linebreak, target)); if ((tctx->flags & DNS_STYLEFLAG_MULTILINE) != 0) RETERR(str_totext(" )", target)); return (ISC_R_SUCCESS); diff --git a/contrib/bind9/lib/dns/rdata/generic/dnskey_48.c b/contrib/bind9/lib/dns/rdata/generic/dnskey_48.c index b7eeb34f2b0..688e7ac5e18 100644 --- a/contrib/bind9/lib/dns/rdata/generic/dnskey_48.c +++ b/contrib/bind9/lib/dns/rdata/generic/dnskey_48.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004, 2005, 2007, 2009, 2012 Internet Systems Consortium, Inc. ("ISC") + * Copyright (C) 2004, 2005, 2007, 2009, 2011, 2012 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 2003 Internet Software Consortium. * * Permission to use, copy, modify, and/or distribute this software for any @@ -76,7 +76,8 @@ totext_dnskey(ARGS_TOTEXT) { char buf[sizeof("64000")]; unsigned int flags; unsigned char algorithm; - char namebuf[DNS_NAME_FORMATSIZE]; + char algbuf[DNS_NAME_FORMATSIZE]; + const char *keyinfo; REQUIRE(rdata->type == 48); REQUIRE(rdata->length != 0); @@ -89,6 +90,13 @@ totext_dnskey(ARGS_TOTEXT) { sprintf(buf, "%u", flags); RETERR(str_totext(buf, target)); RETERR(str_totext(" ", target)); + if ((flags & DNS_KEYFLAG_KSK) != 0) { + if (flags & DNS_KEYFLAG_REVOKE) + keyinfo = "revoked KSK"; + else + keyinfo = "KSK"; + } else + keyinfo = "ZSK"; /* protocol */ sprintf(buf, "%u", sr.base[0]); @@ -106,23 +114,28 @@ totext_dnskey(ARGS_TOTEXT) { if ((flags & 0xc000) == 0xc000) return (ISC_R_SUCCESS); - if ((tctx->flags & DNS_STYLEFLAG_COMMENT) != 0 && + if ((tctx->flags & DNS_STYLEFLAG_RRCOMMENT) != 0 && algorithm == DNS_KEYALG_PRIVATEDNS) { dns_name_t name; dns_name_init(&name, NULL); dns_name_fromregion(&name, &sr); - dns_name_format(&name, namebuf, sizeof(namebuf)); - } else - namebuf[0] = 0; + dns_name_format(&name, algbuf, sizeof(algbuf)); + } else { + dns_secalg_format((dns_secalg_t) algorithm, algbuf, + sizeof(algbuf)); + } /* key */ if ((tctx->flags & DNS_STYLEFLAG_MULTILINE) != 0) RETERR(str_totext(" (", target)); RETERR(str_totext(tctx->linebreak, target)); - RETERR(isc_base64_totext(&sr, tctx->width - 2, - tctx->linebreak, target)); + if (tctx->width == 0) /* No splitting */ + RETERR(isc_base64_totext(&sr, 0, "", target)); + else + RETERR(isc_base64_totext(&sr, tctx->width - 2, + tctx->linebreak, target)); - if ((tctx->flags & DNS_STYLEFLAG_COMMENT) != 0) + if ((tctx->flags & DNS_STYLEFLAG_RRCOMMENT) != 0) RETERR(str_totext(tctx->linebreak, target)); else if ((tctx->flags & DNS_STYLEFLAG_MULTILINE) != 0) RETERR(str_totext(" ", target)); @@ -130,18 +143,17 @@ totext_dnskey(ARGS_TOTEXT) { if ((tctx->flags & DNS_STYLEFLAG_MULTILINE) != 0) RETERR(str_totext(")", target)); - if ((tctx->flags & DNS_STYLEFLAG_COMMENT) != 0) { + if ((tctx->flags & DNS_STYLEFLAG_RRCOMMENT) != 0) { isc_region_t tmpr; - RETERR(str_totext(" ; key id = ", target)); + RETERR(str_totext(" ; ", target)); + RETERR(str_totext(keyinfo, target)); + RETERR(str_totext("; alg = ", target)); + RETERR(str_totext(algbuf, target)); + RETERR(str_totext("; key id = ", target)); dns_rdata_toregion(rdata, &tmpr); sprintf(buf, "%u", dst_region_computeid(&tmpr, algorithm)); RETERR(str_totext(buf, target)); - if (algorithm == DNS_KEYALG_PRIVATEDNS) { - RETERR(str_totext(tctx->linebreak, target)); - RETERR(str_totext("; alg = ", target)); - RETERR(str_totext(namebuf, target)); - } } return (ISC_R_SUCCESS); } diff --git a/contrib/bind9/lib/dns/rdata/generic/ds_43.c b/contrib/bind9/lib/dns/rdata/generic/ds_43.c index 20bac85ccac..dd47c8d5e83 100644 --- a/contrib/bind9/lib/dns/rdata/generic/ds_43.c +++ b/contrib/bind9/lib/dns/rdata/generic/ds_43.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004, 2005, 2007, 2009, 2010, 2012 Internet Systems Consortium, Inc. ("ISC") + * Copyright (C) 2004, 2005, 2007, 2009-2012 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 2002 Internet Software Consortium. * * Permission to use, copy, modify, and/or distribute this software for any @@ -137,7 +137,11 @@ totext_ds(ARGS_TOTEXT) { if ((tctx->flags & DNS_STYLEFLAG_MULTILINE) != 0) RETERR(str_totext(" (", target)); RETERR(str_totext(tctx->linebreak, target)); - RETERR(isc_hex_totext(&sr, tctx->width - 2, tctx->linebreak, target)); + if (tctx->width == 0) /* No splitting */ + RETERR(isc_hex_totext(&sr, 0, "", target)); + else + RETERR(isc_hex_totext(&sr, tctx->width - 2, + tctx->linebreak, target)); if ((tctx->flags & DNS_STYLEFLAG_MULTILINE) != 0) RETERR(str_totext(" )", target)); return (ISC_R_SUCCESS); diff --git a/contrib/bind9/lib/dns/rdata/generic/ipseckey_45.c b/contrib/bind9/lib/dns/rdata/generic/ipseckey_45.c index 7e65e655d29..1d2508c42e2 100644 --- a/contrib/bind9/lib/dns/rdata/generic/ipseckey_45.c +++ b/contrib/bind9/lib/dns/rdata/generic/ipseckey_45.c @@ -190,8 +190,11 @@ totext_ipseckey(ARGS_TOTEXT) { */ if (region.length > 0U) { RETERR(str_totext(tctx->linebreak, target)); - RETERR(isc_base64_totext(®ion, tctx->width - 2, - tctx->linebreak, target)); + if (tctx->width == 0) /* No splitting */ + RETERR(isc_base64_totext(®ion, 60, "", target)); + else + RETERR(isc_base64_totext(®ion, tctx->width - 2, + tctx->linebreak, target)); } if ((tctx->flags & DNS_STYLEFLAG_MULTILINE) != 0) diff --git a/contrib/bind9/lib/dns/rdata/generic/key_25.c b/contrib/bind9/lib/dns/rdata/generic/key_25.c index 26ca9a9b82d..1d0ba83a9b3 100644 --- a/contrib/bind9/lib/dns/rdata/generic/key_25.c +++ b/contrib/bind9/lib/dns/rdata/generic/key_25.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004, 2005, 2007, 2009, 2012 Internet Systems Consortium, Inc. ("ISC") + * Copyright (C) 2004, 2005, 2007, 2009, 2011, 2012 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 1999-2003 Internet Software Consortium. * * Permission to use, copy, modify, and/or distribute this software for any @@ -106,7 +106,7 @@ totext_key(ARGS_TOTEXT) { if ((flags & 0xc000) == 0xc000) return (ISC_R_SUCCESS); - if ((tctx->flags & DNS_STYLEFLAG_COMMENT) != 0 && + if ((tctx->flags & DNS_STYLEFLAG_RRCOMMENT) != 0 && algorithm == DNS_KEYALG_PRIVATEDNS) { dns_name_t name; dns_name_init(&name, NULL); @@ -119,10 +119,13 @@ totext_key(ARGS_TOTEXT) { if ((tctx->flags & DNS_STYLEFLAG_MULTILINE) != 0) RETERR(str_totext(" (", target)); RETERR(str_totext(tctx->linebreak, target)); - RETERR(isc_base64_totext(&sr, tctx->width - 2, - tctx->linebreak, target)); + if (tctx->width == 0) /* No splitting */ + RETERR(isc_base64_totext(&sr, 60, "", target)); + else + RETERR(isc_base64_totext(&sr, tctx->width - 2, + tctx->linebreak, target)); - if ((tctx->flags & DNS_STYLEFLAG_COMMENT) != 0) + if ((tctx->flags & DNS_STYLEFLAG_RRCOMMENT) != 0) RETERR(str_totext(tctx->linebreak, target)); else if ((tctx->flags & DNS_STYLEFLAG_MULTILINE) != 0) RETERR(str_totext(" ", target)); @@ -130,7 +133,7 @@ totext_key(ARGS_TOTEXT) { if ((tctx->flags & DNS_STYLEFLAG_MULTILINE) != 0) RETERR(str_totext(")", target)); - if ((tctx->flags & DNS_STYLEFLAG_COMMENT) != 0) { + if ((tctx->flags & DNS_STYLEFLAG_RRCOMMENT) != 0) { isc_region_t tmpr; RETERR(str_totext(" ; key id = ", target)); diff --git a/contrib/bind9/lib/dns/rdata/generic/keydata_65533.c b/contrib/bind9/lib/dns/rdata/generic/keydata_65533.c index 317e1a87246..a2d83f456e4 100644 --- a/contrib/bind9/lib/dns/rdata/generic/keydata_65533.c +++ b/contrib/bind9/lib/dns/rdata/generic/keydata_65533.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009, 2012 Internet Systems Consortium, Inc. ("ISC") + * Copyright (C) 2009, 2011, 2012 Internet Systems Consortium, Inc. ("ISC") * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -89,6 +89,8 @@ totext_keydata(ARGS_TOTEXT) { unsigned int flags; unsigned char algorithm; unsigned long when; + char algbuf[DNS_NAME_FORMATSIZE]; + const char *keyinfo; REQUIRE(rdata->type == 65533); REQUIRE(rdata->length != 0); @@ -119,6 +121,13 @@ totext_keydata(ARGS_TOTEXT) { sprintf(buf, "%u", flags); RETERR(str_totext(buf, target)); RETERR(str_totext(" ", target)); + if ((flags & DNS_KEYFLAG_KSK) != 0) { + if (flags & DNS_KEYFLAG_REVOKE) + keyinfo = "revoked KSK"; + else + keyinfo = "KSK"; + } else + keyinfo = "ZSK"; /* protocol */ sprintf(buf, "%u", sr.base[0]); @@ -140,10 +149,13 @@ totext_keydata(ARGS_TOTEXT) { if ((tctx->flags & DNS_STYLEFLAG_MULTILINE) != 0) RETERR(str_totext(" (", target)); RETERR(str_totext(tctx->linebreak, target)); - RETERR(isc_base64_totext(&sr, tctx->width - 2, - tctx->linebreak, target)); + if (tctx->width == 0) /* No splitting */ + RETERR(isc_base64_totext(&sr, 60, "", target)); + else + RETERR(isc_base64_totext(&sr, tctx->width - 2, + tctx->linebreak, target)); - if ((tctx->flags & DNS_STYLEFLAG_COMMENT) != 0) + if ((tctx->flags & DNS_STYLEFLAG_RRCOMMENT) != 0) RETERR(str_totext(tctx->linebreak, target)); else if ((tctx->flags & DNS_STYLEFLAG_MULTILINE) != 0) RETERR(str_totext(" ", target)); @@ -151,10 +163,16 @@ totext_keydata(ARGS_TOTEXT) { if ((tctx->flags & DNS_STYLEFLAG_MULTILINE) != 0) RETERR(str_totext(")", target)); - if ((tctx->flags & DNS_STYLEFLAG_COMMENT) != 0) { + if ((tctx->flags & DNS_STYLEFLAG_RRCOMMENT) != 0) { isc_region_t tmpr; - RETERR(str_totext(" ; key id = ", target)); + RETERR(str_totext(" ; ", target)); + RETERR(str_totext(keyinfo, target)); + dns_secalg_format((dns_secalg_t) algorithm, algbuf, + sizeof(algbuf)); + RETERR(str_totext("; alg = ", target)); + RETERR(str_totext(algbuf, target)); + RETERR(str_totext("; key id = ", target)); dns_rdata_toregion(rdata, &tmpr); /* Skip over refresh, addhd, and removehd */ isc_region_consume(&tmpr, 12); diff --git a/contrib/bind9/lib/dns/rdata/in_1/naptr_35.c b/contrib/bind9/lib/dns/rdata/generic/naptr_35.c similarity index 92% rename from contrib/bind9/lib/dns/rdata/in_1/naptr_35.c rename to contrib/bind9/lib/dns/rdata/generic/naptr_35.c index e5df80b0c7b..83439a59293 100644 --- a/contrib/bind9/lib/dns/rdata/in_1/naptr_35.c +++ b/contrib/bind9/lib/dns/rdata/generic/naptr_35.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004, 2005, 2007-2009, 2012, 2013 Internet Systems Consortium, Inc. ("ISC") + * Copyright (C) 2004, 2005, 2007-2009, 2011-2013 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 1999-2001, 2003 Internet Software Consortium. * * Permission to use, copy, modify, and/or distribute this software for any @@ -21,8 +21,8 @@ /* RFC2915 */ -#ifndef RDATA_IN_1_NAPTR_35_C -#define RDATA_IN_1_NAPTR_35_C +#ifndef RDATA_GENERIC_NAPTR_35_C +#define RDATA_GENERIC_NAPTR_35_C #define RRTYPE_NAPTR_ATTRIBUTES (0) @@ -121,14 +121,13 @@ txt_valid_regex(const unsigned char *txt) { } static inline isc_result_t -fromtext_in_naptr(ARGS_FROMTEXT) { +fromtext_naptr(ARGS_FROMTEXT) { isc_token_t token; dns_name_t name; isc_buffer_t buffer; unsigned char *regex; REQUIRE(type == 35); - REQUIRE(rdclass == 1); UNUSED(type); UNUSED(rdclass); @@ -188,7 +187,7 @@ fromtext_in_naptr(ARGS_FROMTEXT) { } static inline isc_result_t -totext_in_naptr(ARGS_TOTEXT) { +totext_naptr(ARGS_TOTEXT) { isc_region_t region; dns_name_t name; dns_name_t prefix; @@ -197,7 +196,6 @@ totext_in_naptr(ARGS_TOTEXT) { unsigned short num; REQUIRE(rdata->type == 35); - REQUIRE(rdata->rdclass == 1); REQUIRE(rdata->length != 0); dns_name_init(&name, NULL); @@ -250,13 +248,12 @@ totext_in_naptr(ARGS_TOTEXT) { } static inline isc_result_t -fromwire_in_naptr(ARGS_FROMWIRE) { +fromwire_naptr(ARGS_FROMWIRE) { dns_name_t name; isc_region_t sr; unsigned char *regex; REQUIRE(type == 35); - REQUIRE(rdclass == 1); UNUSED(type); UNUSED(rdclass); @@ -298,13 +295,12 @@ fromwire_in_naptr(ARGS_FROMWIRE) { } static inline isc_result_t -towire_in_naptr(ARGS_TOWIRE) { +towire_naptr(ARGS_TOWIRE) { dns_name_t name; dns_offsets_t offsets; isc_region_t sr; REQUIRE(rdata->type == 35); - REQUIRE(rdata->rdclass == 1); REQUIRE(rdata->length != 0); dns_compress_setmethods(cctx, DNS_COMPRESS_NONE); @@ -342,7 +338,7 @@ towire_in_naptr(ARGS_TOWIRE) { } static inline int -compare_in_naptr(ARGS_COMPARE) { +compare_naptr(ARGS_COMPARE) { dns_name_t name1; dns_name_t name2; isc_region_t region1; @@ -352,7 +348,6 @@ compare_in_naptr(ARGS_COMPARE) { REQUIRE(rdata1->type == rdata2->type); REQUIRE(rdata1->rdclass == rdata2->rdclass); REQUIRE(rdata1->type == 35); - REQUIRE(rdata1->rdclass == 1); REQUIRE(rdata1->length != 0); REQUIRE(rdata2->length != 0); @@ -411,12 +406,11 @@ compare_in_naptr(ARGS_COMPARE) { } static inline isc_result_t -fromstruct_in_naptr(ARGS_FROMSTRUCT) { - dns_rdata_in_naptr_t *naptr = source; +fromstruct_naptr(ARGS_FROMSTRUCT) { + dns_rdata_naptr_t *naptr = source; isc_region_t region; REQUIRE(type == 35); - REQUIRE(rdclass == 1); REQUIRE(source != NULL); REQUIRE(naptr->common.rdtype == type); REQUIRE(naptr->common.rdclass == rdclass); @@ -440,14 +434,13 @@ fromstruct_in_naptr(ARGS_FROMSTRUCT) { } static inline isc_result_t -tostruct_in_naptr(ARGS_TOSTRUCT) { - dns_rdata_in_naptr_t *naptr = target; +tostruct_naptr(ARGS_TOSTRUCT) { + dns_rdata_naptr_t *naptr = target; isc_region_t r; isc_result_t result; dns_name_t name; REQUIRE(rdata->type == 35); - REQUIRE(rdata->rdclass == 1); REQUIRE(target != NULL); REQUIRE(rdata->length != 0); @@ -511,11 +504,10 @@ tostruct_in_naptr(ARGS_TOSTRUCT) { } static inline void -freestruct_in_naptr(ARGS_FREESTRUCT) { - dns_rdata_in_naptr_t *naptr = source; +freestruct_naptr(ARGS_FREESTRUCT) { + dns_rdata_naptr_t *naptr = source; REQUIRE(source != NULL); - REQUIRE(naptr->common.rdclass == 1); REQUIRE(naptr->common.rdtype == 35); if (naptr->mctx == NULL) @@ -532,7 +524,7 @@ freestruct_in_naptr(ARGS_FREESTRUCT) { } static inline isc_result_t -additionaldata_in_naptr(ARGS_ADDLDATA) { +additionaldata_naptr(ARGS_ADDLDATA) { dns_name_t name; dns_offsets_t offsets; isc_region_t sr; @@ -541,7 +533,6 @@ additionaldata_in_naptr(ARGS_ADDLDATA) { char *cp; REQUIRE(rdata->type == 35); - REQUIRE(rdata->rdclass == 1); /* * Order, preference. @@ -590,14 +581,13 @@ additionaldata_in_naptr(ARGS_ADDLDATA) { } static inline isc_result_t -digest_in_naptr(ARGS_DIGEST) { +digest_naptr(ARGS_DIGEST) { isc_region_t r1, r2; unsigned int length, n; isc_result_t result; dns_name_t name; REQUIRE(rdata->type == 35); - REQUIRE(rdata->rdclass == 1); dns_rdata_toregion(rdata, &r1); r2 = r1; @@ -649,10 +639,9 @@ digest_in_naptr(ARGS_DIGEST) { } static inline isc_boolean_t -checkowner_in_naptr(ARGS_CHECKOWNER) { +checkowner_naptr(ARGS_CHECKOWNER) { REQUIRE(type == 35); - REQUIRE(rdclass == 1); UNUSED(name); UNUSED(type); @@ -663,10 +652,9 @@ checkowner_in_naptr(ARGS_CHECKOWNER) { } static inline isc_boolean_t -checknames_in_naptr(ARGS_CHECKNAMES) { +checknames_naptr(ARGS_CHECKNAMES) { REQUIRE(rdata->type == 35); - REQUIRE(rdata->rdclass == 1); UNUSED(rdata); UNUSED(owner); @@ -676,8 +664,8 @@ checknames_in_naptr(ARGS_CHECKNAMES) { } static inline int -casecompare_in_naptr(ARGS_COMPARE) { - return (compare_in_naptr(rdata1, rdata2)); +casecompare_naptr(ARGS_COMPARE) { + return (compare_naptr(rdata1, rdata2)); } -#endif /* RDATA_IN_1_NAPTR_35_C */ +#endif /* RDATA_GENERIC_NAPTR_35_C */ diff --git a/contrib/bind9/lib/dns/rdata/in_1/naptr_35.h b/contrib/bind9/lib/dns/rdata/generic/naptr_35.h similarity index 82% rename from contrib/bind9/lib/dns/rdata/in_1/naptr_35.h rename to contrib/bind9/lib/dns/rdata/generic/naptr_35.h index 04e8d691a58..f88c52336f8 100644 --- a/contrib/bind9/lib/dns/rdata/in_1/naptr_35.h +++ b/contrib/bind9/lib/dns/rdata/generic/naptr_35.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004, 2005, 2007, 2012 Internet Systems Consortium, Inc. ("ISC") + * Copyright (C) 2004, 2005, 2007, 2011, 2012 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 1999-2001 Internet Software Consortium. * * Permission to use, copy, modify, and/or distribute this software for any @@ -15,15 +15,15 @@ * PERFORMANCE OF THIS SOFTWARE. */ -#ifndef IN_1_NAPTR_35_H -#define IN_1_NAPTR_35_H 1 +#ifndef GENERIC_NAPTR_35_H +#define GENERIC_NAPTR_35_H 1 /* $Id$ */ /*! * \brief Per RFC2915 */ -typedef struct dns_rdata_in_naptr { +typedef struct dns_rdata_naptr { dns_rdatacommon_t common; isc_mem_t *mctx; isc_uint16_t order; @@ -35,6 +35,6 @@ typedef struct dns_rdata_in_naptr { char *regexp; isc_uint8_t regexp_len; dns_name_t replacement; -} dns_rdata_in_naptr_t; +} dns_rdata_naptr_t; -#endif /* IN_1_NAPTR_35_H */ +#endif /* GENERIC_NAPTR_35_H */ diff --git a/contrib/bind9/lib/dns/rdata/generic/nsec3_50.c b/contrib/bind9/lib/dns/rdata/generic/nsec3_50.c index 96b2dc8f5fe..19b94efa06d 100644 --- a/contrib/bind9/lib/dns/rdata/generic/nsec3_50.c +++ b/contrib/bind9/lib/dns/rdata/generic/nsec3_50.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008, 2009, 2012 Internet Systems Consortium, Inc. ("ISC") + * Copyright (C) 2008, 2009, 2011, 2012 Internet Systems Consortium, Inc. ("ISC") * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -142,32 +142,32 @@ totext_nsec3(ARGS_TOTEXT) { unsigned char flags; char buf[sizeof("65535 ")]; isc_uint32_t iterations; + isc_boolean_t first; REQUIRE(rdata->type == 50); REQUIRE(rdata->length != 0); - UNUSED(tctx); - dns_rdata_toregion(rdata, &sr); + /* Hash */ hash = uint8_fromregion(&sr); isc_region_consume(&sr, 1); - - flags = uint8_fromregion(&sr); - isc_region_consume(&sr, 1); - - iterations = uint16_fromregion(&sr); - isc_region_consume(&sr, 2); - sprintf(buf, "%u ", hash); RETERR(str_totext(buf, target)); + /* Flags */ + flags = uint8_fromregion(&sr); + isc_region_consume(&sr, 1); sprintf(buf, "%u ", flags); RETERR(str_totext(buf, target)); + /* Iterations */ + iterations = uint16_fromregion(&sr); + isc_region_consume(&sr, 2); sprintf(buf, "%u ", iterations); RETERR(str_totext(buf, target)); + /* Salt */ j = uint8_fromregion(&sr); isc_region_consume(&sr, 1); INSIST(j <= sr.length); @@ -177,10 +177,14 @@ totext_nsec3(ARGS_TOTEXT) { sr.length = j; RETERR(isc_hex_totext(&sr, 1, "", target)); sr.length = i - j; - RETERR(str_totext(" ", target)); } else - RETERR(str_totext("- ", target)); + RETERR(str_totext("-", target)); + if ((tctx->flags & DNS_STYLEFLAG_MULTILINE) != 0) + RETERR(str_totext(" (", target)); + RETERR(str_totext(tctx->linebreak, target)); + + /* Next hash */ j = uint8_fromregion(&sr); isc_region_consume(&sr, 1); INSIST(j <= sr.length); @@ -190,7 +194,16 @@ totext_nsec3(ARGS_TOTEXT) { RETERR(isc_base32hex_totext(&sr, 1, "", target)); sr.length = i - j; + if ((tctx->flags & DNS_STYLEFLAG_MULTILINE) == 0) + RETERR(str_totext(" ", target)); + + /* Types covered */ + first = ISC_TRUE; for (i = 0; i < sr.length; i += len) { + if ((tctx->flags & DNS_STYLEFLAG_MULTILINE) != 0) { + RETERR(str_totext(tctx->linebreak, target)); + first = ISC_TRUE; + } INSIST(i + 2 <= sr.length); window = sr.base[i]; len = sr.base[i + 1]; @@ -205,7 +218,9 @@ totext_nsec3(ARGS_TOTEXT) { if ((sr.base[i + j] & (0x80 >> k)) == 0) continue; t = window * 256 + j * 8 + k; - RETERR(str_totext(" ", target)); + if (!first) + RETERR(str_totext(" ", target)); + first = ISC_FALSE; if (dns_rdatatype_isknown(t)) { RETERR(dns_rdatatype_totext(t, target)); } else { @@ -216,6 +231,10 @@ totext_nsec3(ARGS_TOTEXT) { } } } + + if ((tctx->flags & DNS_STYLEFLAG_MULTILINE) != 0) + RETERR(str_totext(" )", target)); + return (ISC_R_SUCCESS); } diff --git a/contrib/bind9/lib/dns/rdata/generic/nsec3_50.h b/contrib/bind9/lib/dns/rdata/generic/nsec3_50.h index 69a1141da00..5f2afb863b9 100644 --- a/contrib/bind9/lib/dns/rdata/generic/nsec3_50.h +++ b/contrib/bind9/lib/dns/rdata/generic/nsec3_50.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008, 2012 Internet Systems Consortium, Inc. ("ISC") + * Copyright (C) 2008, 2011, 2012 Internet Systems Consortium, Inc. ("ISC") * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -46,7 +46,16 @@ typedef struct dns_rdata_nsec3 { #define DNS_NSEC3FLAG_OPTOUT 0x01U /*% - * Non-standard, NSEC3PARAM only. + * The following flags are used in the private-type record (implemented in + * lib/dns/private.c) which is used to store NSEC3PARAM data during the + * time when it is not legal to have an actual NSEC3PARAM record in the + * zone. They are defined here because the private-type record uses the + * same flags field for the OPTOUT flag above and for the private flags + * below. XXX: This should be considered for refactoring. + */ + +/*% + * Non-standard, private type only. * * Create a corresponding NSEC3 chain. * Once the NSEC3 chain is complete this flag will be removed to signal @@ -55,13 +64,14 @@ typedef struct dns_rdata_nsec3 { * This flag is automatically set when a NSEC3PARAM record is added to * the zone via UPDATE. * - * NSEC3PARAM records with this flag set are supposed to be ignored by - * RFC 5155 compliant nameservers. + * NSEC3PARAM records containing this flag should never be published, + * but if they are, they should be ignored by RFC 5155 compliant + * nameservers. */ #define DNS_NSEC3FLAG_CREATE 0x80U /*% - * Non-standard, NSEC3PARAM only. + * Non-standard, private type only. * * The corresponding NSEC3 set is to be removed once the NSEC chain * has been generated. @@ -69,24 +79,39 @@ typedef struct dns_rdata_nsec3 { * This flag is automatically set when the last active NSEC3PARAM record * is removed from the zone via UPDATE. * - * NSEC3PARAM records with this flag set are supposed to be ignored by - * RFC 5155 compliant nameservers. + * NSEC3PARAM records containing this flag should never be published, + * but if they are, they should be ignored by RFC 5155 compliant + * nameservers. */ #define DNS_NSEC3FLAG_REMOVE 0x40U /*% - * Non-standard, NSEC3PARAM only. + * Non-standard, private type only. * - * Used to identify NSEC3PARAM records added in this UPDATE request. + * When set with the CREATE flag, a corresponding NSEC3 chain will be + * created when the zone becomes capable of supporting one (i.e., when it + * has a DNSKEY RRset containing at least one NSEC3-capable algorithm). + * Without this flag, NSEC3 chain creation would be attempted immediately, + * fail, and the private type record would be removed. With it, the NSEC3 + * parameters are stored until they can be used. When the zone has the + * necessary prerequisites for NSEC3, then the INITIAL flag can be cleared, + * and the record will be cleaned up normally. + * + * NSEC3PARAM records containing this flag should never be published, but + * if they are, they should be ignored by RFC 5155 compliant nameservers. */ -#define DNS_NSEC3FLAG_UPDATE 0x20U +#define DNS_NSEC3FLAG_INITIAL 0x20U /*% - * Non-standard, NSEC3PARAM only. + * Non-standard, private type only. * * Prevent the creation of a NSEC chain before the last NSEC3 chain * is removed. This will normally only be set when the zone is * transitioning from secure with NSEC3 chains to insecure. + * + * NSEC3PARAM records containing this flag should never be published, + * but if they are, they should be ignored by RFC 5155 compliant + * nameservers. */ #define DNS_NSEC3FLAG_NONSEC 0x10U diff --git a/contrib/bind9/lib/dns/rdata/generic/opt_41.c b/contrib/bind9/lib/dns/rdata/generic/opt_41.c index fa349f1f580..4b51804317c 100644 --- a/contrib/bind9/lib/dns/rdata/generic/opt_41.c +++ b/contrib/bind9/lib/dns/rdata/generic/opt_41.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004, 2005, 2007, 2009, 2012 Internet Systems Consortium, Inc. ("ISC") + * Copyright (C) 2004, 2005, 2007, 2009, 2011, 2012 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 1998-2002 Internet Software Consortium. * * Permission to use, copy, modify, and/or distribute this software for any @@ -76,8 +76,12 @@ totext_opt(ARGS_TOTEXT) { RETERR(str_totext(tctx->linebreak, target)); or = r; or.length = length; - RETERR(isc_base64_totext(&or, tctx->width - 2, - tctx->linebreak, target)); + if (tctx->width == 0) /* No splitting */ + RETERR(isc_base64_totext(&or, 60, "", target)); + else + RETERR(isc_base64_totext(&or, tctx->width - 2, + tctx->linebreak, + target)); isc_region_consume(&r, length); if ((tctx->flags & DNS_STYLEFLAG_MULTILINE) != 0) RETERR(str_totext(" )", target)); diff --git a/contrib/bind9/lib/dns/rdata/generic/rrsig_46.c b/contrib/bind9/lib/dns/rdata/generic/rrsig_46.c index 82dfce69d31..58a327c02ed 100644 --- a/contrib/bind9/lib/dns/rdata/generic/rrsig_46.c +++ b/contrib/bind9/lib/dns/rdata/generic/rrsig_46.c @@ -181,7 +181,10 @@ totext_rrsig(ARGS_TOTEXT) { isc_region_consume(&sr, 4); sprintf(buf, "%lu", ttl); RETERR(str_totext(buf, target)); - RETERR(str_totext(" ", target)); + + if ((tctx->flags & DNS_STYLEFLAG_MULTILINE) != 0) + RETERR(str_totext(" (", target)); + RETERR(str_totext(tctx->linebreak, target)); /* * Sig exp. @@ -189,10 +192,7 @@ totext_rrsig(ARGS_TOTEXT) { exp = uint32_fromregion(&sr); isc_region_consume(&sr, 4); RETERR(dns_time32_totext(exp, target)); - - if ((tctx->flags & DNS_STYLEFLAG_MULTILINE) != 0) - RETERR(str_totext(" (", target)); - RETERR(str_totext(tctx->linebreak, target)); + RETERR(str_totext(" ", target)); /* * Time signed. @@ -223,8 +223,11 @@ totext_rrsig(ARGS_TOTEXT) { * Sig. */ RETERR(str_totext(tctx->linebreak, target)); - RETERR(isc_base64_totext(&sr, tctx->width - 2, - tctx->linebreak, target)); + if (tctx->width == 0) /* No splitting */ + RETERR(isc_base64_totext(&sr, 60, "", target)); + else + RETERR(isc_base64_totext(&sr, tctx->width - 2, + tctx->linebreak, target)); if ((tctx->flags & DNS_STYLEFLAG_MULTILINE) != 0) RETERR(str_totext(" )", target)); diff --git a/contrib/bind9/lib/dns/rdata/generic/sig_24.c b/contrib/bind9/lib/dns/rdata/generic/sig_24.c index 3cdd17a06b8..803a864067f 100644 --- a/contrib/bind9/lib/dns/rdata/generic/sig_24.c +++ b/contrib/bind9/lib/dns/rdata/generic/sig_24.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004, 2005, 2007, 2009, 2012 Internet Systems Consortium, Inc. ("ISC") + * Copyright (C) 2004, 2005, 2007, 2009, 2011, 2012 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 1999-2003 Internet Software Consortium. * * Permission to use, copy, modify, and/or distribute this software for any @@ -227,8 +227,11 @@ totext_sig(ARGS_TOTEXT) { * Sig. */ RETERR(str_totext(tctx->linebreak, target)); - RETERR(isc_base64_totext(&sr, tctx->width - 2, - tctx->linebreak, target)); + if (tctx->width == 0) /* No splitting */ + RETERR(isc_base64_totext(&sr, 60, "", target)); + else + RETERR(isc_base64_totext(&sr, tctx->width - 2, + tctx->linebreak, target)); if ((tctx->flags & DNS_STYLEFLAG_MULTILINE) != 0) RETERR(str_totext(" )", target)); diff --git a/contrib/bind9/lib/dns/rdata/generic/soa_6.c b/contrib/bind9/lib/dns/rdata/generic/soa_6.c index a8676103575..ac0a38f7c19 100644 --- a/contrib/bind9/lib/dns/rdata/generic/soa_6.c +++ b/contrib/bind9/lib/dns/rdata/generic/soa_6.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004, 2007, 2009, 2012 Internet Systems Consortium, Inc. ("ISC") + * Copyright (C) 2004, 2007, 2009, 2011, 2012 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 1998-2002 Internet Software Consortium. * * Permission to use, copy, modify, and/or distribute this software for any @@ -102,7 +102,7 @@ totext_soa(ARGS_TOTEXT) { multiline = ISC_TF((tctx->flags & DNS_STYLEFLAG_MULTILINE) != 0); if (multiline) - comment = ISC_TF((tctx->flags & DNS_STYLEFLAG_COMMENT) != 0); + comment = ISC_TF((tctx->flags & DNS_STYLEFLAG_RRCOMMENT) != 0); else comment = ISC_FALSE; diff --git a/contrib/bind9/lib/dns/rdata/generic/sshfp_44.c b/contrib/bind9/lib/dns/rdata/generic/sshfp_44.c index 03d5127133c..d553cd40380 100644 --- a/contrib/bind9/lib/dns/rdata/generic/sshfp_44.c +++ b/contrib/bind9/lib/dns/rdata/generic/sshfp_44.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004, 2006, 2007, 2009, 2012, 2013 Internet Systems Consortium, Inc. ("ISC") + * Copyright (C) 2004, 2006, 2007, 2009, 2011-2013 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 2003 Internet Software Consortium. * * Permission to use, copy, modify, and/or distribute this software for any @@ -95,7 +95,11 @@ totext_sshfp(ARGS_TOTEXT) { if ((tctx->flags & DNS_STYLEFLAG_MULTILINE) != 0) RETERR(str_totext(" (", target)); RETERR(str_totext(tctx->linebreak, target)); - RETERR(isc_hex_totext(&sr, tctx->width - 2, tctx->linebreak, target)); + if (tctx->width == 0) /* No splitting */ + RETERR(isc_hex_totext(&sr, 0, "", target)); + else + RETERR(isc_hex_totext(&sr, tctx->width - 2, + tctx->linebreak, target)); if ((tctx->flags & DNS_STYLEFLAG_MULTILINE) != 0) RETERR(str_totext(" )", target)); return (ISC_R_SUCCESS); diff --git a/contrib/bind9/lib/dns/rdata/generic/tkey_249.c b/contrib/bind9/lib/dns/rdata/generic/tkey_249.c index 3afee130824..6f1ec025388 100644 --- a/contrib/bind9/lib/dns/rdata/generic/tkey_249.c +++ b/contrib/bind9/lib/dns/rdata/generic/tkey_249.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004, 2007, 2009, 2012 Internet Systems Consortium, Inc. ("ISC") + * Copyright (C) 2004, 2007, 2009, 2011, 2012 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 1999-2003 Internet Software Consortium. * * Permission to use, copy, modify, and/or distribute this software for any @@ -201,8 +201,11 @@ totext_tkey(ARGS_TOTEXT) { if ((tctx->flags & DNS_STYLEFLAG_MULTILINE) != 0) RETERR(str_totext(" (", target)); RETERR(str_totext(tctx->linebreak, target)); - RETERR(isc_base64_totext(&dr, tctx->width - 2, - tctx->linebreak, target)); + if (tctx->width == 0) /* No splitting */ + RETERR(isc_base64_totext(&dr, 60, "", target)); + else + RETERR(isc_base64_totext(&dr, tctx->width - 2, + tctx->linebreak, target)); if ((tctx->flags & DNS_STYLEFLAG_MULTILINE) != 0) RETERR(str_totext(" ) ", target)); else @@ -227,8 +230,11 @@ totext_tkey(ARGS_TOTEXT) { if ((tctx->flags & DNS_STYLEFLAG_MULTILINE) != 0) RETERR(str_totext(" (", target)); RETERR(str_totext(tctx->linebreak, target)); - RETERR(isc_base64_totext(&dr, tctx->width - 2, - tctx->linebreak, target)); + if (tctx->width == 0) /* No splitting */ + RETERR(isc_base64_totext(&dr, 60, "", target)); + else + RETERR(isc_base64_totext(&dr, tctx->width - 2, + tctx->linebreak, target)); if ((tctx->flags & DNS_STYLEFLAG_MULTILINE) != 0) RETERR(str_totext(" )", target)); } diff --git a/contrib/bind9/lib/dns/rdata/generic/uri_256.c b/contrib/bind9/lib/dns/rdata/generic/uri_256.c index aa5b1946dc8..799eb694e66 100644 --- a/contrib/bind9/lib/dns/rdata/generic/uri_256.c +++ b/contrib/bind9/lib/dns/rdata/generic/uri_256.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011-2013 Internet Systems Consortium, Inc. ("ISC") + * Copyright (C) 2011, 2012 Internet Systems Consortium, Inc. ("ISC") * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -14,7 +14,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: uri_256.c,v 1.2 2011/03/03 14:10:27 fdupont Exp $ */ +/* $Id$ */ #ifndef GENERIC_URI_256_C #define GENERIC_URI_256_C 1 diff --git a/contrib/bind9/lib/dns/rdata/generic/uri_256.h b/contrib/bind9/lib/dns/rdata/generic/uri_256.h index 5061c03c652..13c8fd238b8 100644 --- a/contrib/bind9/lib/dns/rdata/generic/uri_256.h +++ b/contrib/bind9/lib/dns/rdata/generic/uri_256.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011-2013 Internet Systems Consortium, Inc. ("ISC") + * Copyright (C) 2011, 2012 Internet Systems Consortium, Inc. ("ISC") * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -17,7 +17,7 @@ #ifndef GENERIC_URI_256_H #define GENERIC_URI_256_H 1 -/* $Id: uri_256.h,v 1.2 2011/03/03 14:10:27 fdupont Exp $ */ +/* $Id$ */ typedef struct dns_rdata_uri { dns_rdatacommon_t common; diff --git a/contrib/bind9/lib/dns/rdata/in_1/dhcid_49.c b/contrib/bind9/lib/dns/rdata/in_1/dhcid_49.c index 1ec75ecacfb..7575da0d193 100644 --- a/contrib/bind9/lib/dns/rdata/in_1/dhcid_49.c +++ b/contrib/bind9/lib/dns/rdata/in_1/dhcid_49.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006, 2007, 2009, 2012 Internet Systems Consortium, Inc. ("ISC") + * Copyright (C) 2006, 2007, 2009, 2011, 2012 Internet Systems Consortium, Inc. ("ISC") * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -52,8 +52,11 @@ totext_in_dhcid(ARGS_TOTEXT) { if ((tctx->flags & DNS_STYLEFLAG_MULTILINE) != 0) RETERR(str_totext("( " /*)*/, target)); - RETERR(isc_base64_totext(&sr, tctx->width - 2, tctx->linebreak, - target)); + if (tctx->width == 0) /* No splitting */ + RETERR(isc_base64_totext(&sr, 60, "", target)); + else + RETERR(isc_base64_totext(&sr, tctx->width - 2, + tctx->linebreak, target)); if ((tctx->flags & DNS_STYLEFLAG_MULTILINE) != 0) { RETERR(str_totext(/* ( */ " )", target)); if (rdata->length > 2) { diff --git a/contrib/bind9/lib/dns/resolver.c b/contrib/bind9/lib/dns/resolver.c index e21d97e1e66..27d15b9329c 100644 --- a/contrib/bind9/lib/dns/resolver.c +++ b/contrib/bind9/lib/dns/resolver.c @@ -26,8 +26,9 @@ #include #include #include -#include +#include #include +#include #include #include @@ -160,6 +161,7 @@ typedef struct query { isc_buffer_t buffer; isc_buffer_t *tsig; dns_tsigkey_t *tsigkey; + isc_socketevent_t sendevent; unsigned int options; unsigned int attributes; unsigned int sends; @@ -393,11 +395,10 @@ struct dns_resolver { isc_boolean_t frozen; unsigned int options; dns_dispatchmgr_t * dispatchmgr; - dns_dispatch_t * dispatchv4; + dns_dispatchset_t * dispatches4; isc_boolean_t exclusivev4; - dns_dispatch_t * dispatchv6; + dns_dispatchset_t * dispatches6; isc_boolean_t exclusivev6; - unsigned int ndisps; unsigned int nbuckets; fctxbucket_t * buckets; isc_uint32_t lame_ttl; @@ -424,7 +425,6 @@ struct dns_resolver { unsigned int activebuckets; isc_boolean_t priming; unsigned int spillat; /* clients-per-query */ - unsigned int nextdisp; /* Bad cache. */ dns_badcache_t ** badcache; @@ -1218,7 +1218,8 @@ process_sendevent(resquery_t *query, isc_event_t *event) { } } - isc_event_free(&event); + if (event->ev_type == ISC_SOCKEVENT_CONNECT) + isc_event_free(&event); if (retry) { /* @@ -1413,14 +1414,14 @@ fctx_query(fetchctx_t *fctx, dns_adbaddrinfo_t *addrinfo, if (!have_addr) { switch (pf) { case PF_INET: - result = - dns_dispatch_getlocaladdress(res->dispatchv4, - &addr); + result = dns_dispatch_getlocaladdress( + res->dispatches4->dispatches[0], + &addr); break; case PF_INET6: - result = - dns_dispatch_getlocaladdress(res->dispatchv6, - &addr); + result = dns_dispatch_getlocaladdress( + res->dispatches6->dispatches[0], + &addr); break; default: result = ISC_R_NOTIMPLEMENTED; @@ -1476,13 +1477,15 @@ fctx_query(fetchctx_t *fctx, dns_adbaddrinfo_t *addrinfo, } else { switch (isc_sockaddr_pf(&addrinfo->sockaddr)) { case PF_INET: - dns_dispatch_attach(res->dispatchv4, - &query->dispatch); + dns_dispatch_attach( + dns_resolver_dispatchv4(res), + &query->dispatch); query->exclusivesocket = res->exclusivev4; break; case PF_INET6: - dns_dispatch_attach(res->dispatchv6, - &query->dispatch); + dns_dispatch_attach( + dns_resolver_dispatchv6(res), + &query->dispatch); query->exclusivesocket = res->exclusivev6; break; default: @@ -1975,8 +1978,11 @@ resquery_send(resquery_t *query) { * XXXRTH Make sure we don't send to ourselves! We should probably * prune out these addresses when we get them from the ADB. */ - result = isc_socket_sendto(socket, &r, task, resquery_senddone, - query, address, NULL); + ISC_EVENT_INIT(&query->sendevent, sizeof(query->sendevent), 0, NULL, + ISC_SOCKEVENT_SENDDONE, resquery_senddone, query, + NULL, NULL, NULL); + result = isc_socket_sendto2(socket, &r, task, address, NULL, + &query->sendevent, 0); if (result != ISC_R_SUCCESS) { if (connecting) { /* @@ -2510,9 +2516,9 @@ findname(fetchctx_t *fctx, dns_name_t *name, in_port_t port, */ if (need_alternate != NULL && !*need_alternate && unshared && - ((res->dispatchv4 == NULL && + ((res->dispatches4 == NULL && find->result_v6 != DNS_R_NXDOMAIN) || - (res->dispatchv6 == NULL && + (res->dispatches6 == NULL && find->result_v4 != DNS_R_NXDOMAIN))) *need_alternate = ISC_TRUE; } else { @@ -2527,9 +2533,9 @@ findname(fetchctx_t *fctx, dns_name_t *name, in_port_t port, * an alternative server. */ if (need_alternate != NULL && !*need_alternate && - ((res->dispatchv4 == NULL && + ((res->dispatches4 == NULL && find->result_v6 == DNS_R_NXRRSET) || - (res->dispatchv6 == NULL && + (res->dispatches6 == NULL && find->result_v4 == DNS_R_NXRRSET))) *need_alternate = ISC_TRUE; dns_adb_destroyfind(&find); @@ -2627,9 +2633,9 @@ fctx_getaddresses(fetchctx_t *fctx, isc_boolean_t badcache) { while (sa != NULL) { if ((isc_sockaddr_pf(sa) == AF_INET && - fctx->res->dispatchv4 == NULL) || + fctx->res->dispatches4 == NULL) || (isc_sockaddr_pf(sa) == AF_INET6 && - fctx->res->dispatchv6 == NULL)) { + fctx->res->dispatches6 == NULL)) { sa = ISC_LIST_NEXT(sa, link); continue; } @@ -2677,9 +2683,9 @@ fctx_getaddresses(fetchctx_t *fctx, isc_boolean_t badcache) { */ stdoptions |= DNS_ADBFIND_AVOIDFETCHES; } - if (res->dispatchv4 != NULL) + if (res->dispatches4 != NULL) stdoptions |= DNS_ADBFIND_INET; - if (res->dispatchv6 != NULL) + if (res->dispatches6 != NULL) stdoptions |= DNS_ADBFIND_INET6; isc_stdtime_get(&now); @@ -2712,7 +2718,7 @@ fctx_getaddresses(fetchctx_t *fctx, isc_boolean_t badcache) { if (need_alternate) { int family; alternate_t *a; - family = (res->dispatchv6 != NULL) ? AF_INET6 : AF_INET; + family = (res->dispatches6 != NULL) ? AF_INET6 : AF_INET; for (a = ISC_LIST_HEAD(fctx->res->alternates); a != NULL; a = ISC_LIST_NEXT(a, link)) { @@ -7567,10 +7573,10 @@ destroy(dns_resolver_t *res) { } isc_mem_put(res->mctx, res->buckets, res->nbuckets * sizeof(fctxbucket_t)); - if (res->dispatchv4 != NULL) - dns_dispatch_detach(&res->dispatchv4); - if (res->dispatchv6 != NULL) - dns_dispatch_detach(&res->dispatchv6); + if (res->dispatches4 != NULL) + dns_dispatchset_destroy(&res->dispatches4); + if (res->dispatches6 != NULL) + dns_dispatchset_destroy(&res->dispatches6); while ((a = ISC_LIST_HEAD(res->alternates)) != NULL) { ISC_LIST_UNLINK(res->alternates, a, link); if (!a->isaddress) @@ -7660,7 +7666,8 @@ spillattimer_countdown(isc_task_t *task, isc_event_t *event) { isc_result_t dns_resolver_create(dns_view_t *view, - isc_taskmgr_t *taskmgr, unsigned int ntasks, + isc_taskmgr_t *taskmgr, + unsigned int ntasks, unsigned int ndisp, isc_socketmgr_t *socketmgr, isc_timermgr_t *timermgr, unsigned int options, @@ -7682,6 +7689,7 @@ dns_resolver_create(dns_view_t *view, REQUIRE(DNS_VIEW_VALID(view)); REQUIRE(ntasks > 0); + REQUIRE(ndisp > 0); REQUIRE(resp != NULL && *resp == NULL); REQUIRE(dispatchmgr != NULL); REQUIRE(dispatchv4 != NULL || dispatchv6 != NULL); @@ -7712,8 +7720,6 @@ dns_resolver_create(dns_view_t *view, res->spillattimer = NULL; res->zero_no_soa_ttl = ISC_FALSE; res->query_timeout = DEFAULT_QUERY_TIMEOUT; - res->ndisps = 0; - res->nextdisp = 0; /* meaningless at this point, but init it */ res->nbuckets = ntasks; res->activebuckets = ntasks; res->buckets = isc_mem_get(view->mctx, @@ -7756,17 +7762,19 @@ dns_resolver_create(dns_view_t *view, buckets_created++; } - res->dispatchv4 = NULL; + res->dispatches4 = NULL; if (dispatchv4 != NULL) { - dns_dispatch_attach(dispatchv4, &res->dispatchv4); + dns_dispatchset_create(view->mctx, socketmgr, taskmgr, + dispatchv4, &res->dispatches4, ndisp); dispattr = dns_dispatch_getattributes(dispatchv4); res->exclusivev4 = ISC_TF((dispattr & DNS_DISPATCHATTR_EXCLUSIVE) != 0); } - res->dispatchv6 = NULL; + res->dispatches6 = NULL; if (dispatchv6 != NULL) { - dns_dispatch_attach(dispatchv6, &res->dispatchv6); + dns_dispatchset_create(view->mctx, socketmgr, taskmgr, + dispatchv6, &res->dispatches6, ndisp); dispattr = dns_dispatch_getattributes(dispatchv6); res->exclusivev6 = ISC_TF((dispattr & DNS_DISPATCHATTR_EXCLUSIVE) != 0); @@ -7842,10 +7850,10 @@ dns_resolver_create(dns_view_t *view, DESTROYLOCK(&res->lock); cleanup_dispatches: - if (res->dispatchv6 != NULL) - dns_dispatch_detach(&res->dispatchv6); - if (res->dispatchv4 != NULL) - dns_dispatch_detach(&res->dispatchv4); + if (res->dispatches6 != NULL) + dns_dispatchset_destroy(&res->dispatches6); + if (res->dispatches4 != NULL) + dns_dispatchset_destroy(&res->dispatches4); cleanup_buckets: for (i = 0; i < buckets_created; i++) { @@ -8034,7 +8042,6 @@ void dns_resolver_shutdown(dns_resolver_t *res) { unsigned int i; fetchctx_t *fctx; - isc_socket_t *sock; isc_result_t result; REQUIRE(VALID_RESOLVER(res)); @@ -8053,15 +8060,13 @@ dns_resolver_shutdown(dns_resolver_t *res) { fctx != NULL; fctx = ISC_LIST_NEXT(fctx, link)) fctx_shutdown(fctx); - if (res->dispatchv4 != NULL && !res->exclusivev4) { - sock = dns_dispatch_getsocket(res->dispatchv4); - isc_socket_cancel(sock, res->buckets[i].task, - ISC_SOCKCANCEL_ALL); + if (res->dispatches4 != NULL && !res->exclusivev4) { + dns_dispatchset_cancelall(res->dispatches4, + res->buckets[i].task); } - if (res->dispatchv6 != NULL && !res->exclusivev6) { - sock = dns_dispatch_getsocket(res->dispatchv6); - isc_socket_cancel(sock, res->buckets[i].task, - ISC_SOCKCANCEL_ALL); + if (res->dispatches6 != NULL && !res->exclusivev6) { + dns_dispatchset_cancelall(res->dispatches6, + res->buckets[i].task); } res->buckets[i].exiting = ISC_TRUE; if (ISC_LIST_EMPTY(res->buckets[i].fctxs)) { @@ -8444,13 +8449,13 @@ dns_resolver_dispatchmgr(dns_resolver_t *resolver) { dns_dispatch_t * dns_resolver_dispatchv4(dns_resolver_t *resolver) { REQUIRE(VALID_RESOLVER(resolver)); - return (resolver->dispatchv4); + return (dns_dispatchset_get(resolver->dispatches4)); } dns_dispatch_t * dns_resolver_dispatchv6(dns_resolver_t *resolver) { REQUIRE(VALID_RESOLVER(resolver)); - return (resolver->dispatchv6); + return (dns_dispatchset_get(resolver->dispatches6)); } isc_socketmgr_t * diff --git a/contrib/bind9/lib/dns/sdb.c b/contrib/bind9/lib/dns/sdb.c index 526ce87537b..191fda219f4 100644 --- a/contrib/bind9/lib/dns/sdb.c +++ b/contrib/bind9/lib/dns/sdb.c @@ -728,8 +728,9 @@ destroynode(dns_sdbnode_t *node) { } static isc_result_t -findnode(dns_db_t *db, dns_name_t *name, isc_boolean_t create, - dns_dbnode_t **nodep) +findnodeext(dns_db_t *db, dns_name_t *name, isc_boolean_t create, + dns_clientinfomethods_t *methods, dns_clientinfo_t *clientinfo, + dns_dbnode_t **nodep) { dns_sdb_t *sdb = (dns_sdb_t *)db; dns_sdbnode_t *node = NULL; @@ -786,10 +787,11 @@ findnode(dns_db_t *db, dns_name_t *name, isc_boolean_t create, MAYBE_LOCK(sdb); if (imp->methods->lookup2 != NULL) result = imp->methods->lookup2(&sdb->common.origin, name, - sdb->dbdata, node); + sdb->dbdata, node, methods, + clientinfo); else result = imp->methods->lookup(sdb->zone, namestr, sdb->dbdata, - node); + node, methods, clientinfo); MAYBE_UNLOCK(sdb); if (result != ISC_R_SUCCESS && !(result == ISC_R_NOTFOUND && @@ -814,10 +816,11 @@ findnode(dns_db_t *db, dns_name_t *name, isc_boolean_t create, } static isc_result_t -find(dns_db_t *db, dns_name_t *name, dns_dbversion_t *version, - dns_rdatatype_t type, unsigned int options, isc_stdtime_t now, - dns_dbnode_t **nodep, dns_name_t *foundname, - dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset) +findext(dns_db_t *db, dns_name_t *name, dns_dbversion_t *version, + dns_rdatatype_t type, unsigned int options, isc_stdtime_t now, + dns_dbnode_t **nodep, dns_name_t *foundname, + dns_clientinfomethods_t *methods, dns_clientinfo_t *clientinfo, + dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset) { dns_sdb_t *sdb = (dns_sdb_t *)db; dns_dbnode_t *node = NULL; @@ -857,7 +860,8 @@ find(dns_db_t *db, dns_name_t *name, dns_dbversion_t *version, * Look up the next label. */ dns_name_getlabelsequence(name, nlabels - i, i, xname); - result = findnode(db, xname, ISC_FALSE, &node); + result = findnodeext(db, xname, ISC_FALSE, methods, + clientinfo, &node); if (result == ISC_R_NOTFOUND) { /* * No data at zone apex? @@ -1264,8 +1268,8 @@ static dns_dbmethods_t sdb_methods = { newversion, attachversion, closeversion, - findnode, - find, + NULL, + NULL, findzonecut, attachnode, detachnode, @@ -1282,17 +1286,19 @@ static dns_dbmethods_t sdb_methods = { ispersistent, overmem, settask, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL + NULL, /* getoriginnode */ + NULL, /* transfernode */ + NULL, /* getnsec3parameters */ + NULL, /* findnsec3node */ + NULL, /* setsigningtime */ + NULL, /* getsigningtime */ + NULL, /* resigned */ + NULL, /* isdnssec */ + NULL, /* getrrsetstats */ + NULL, /* rpz_enabled */ + NULL, /* rpz_findips */ + findnodeext, + findext }; static isc_result_t diff --git a/contrib/bind9/lib/dns/sdlz.c b/contrib/bind9/lib/dns/sdlz.c index 6e518d1ecbc..9d4e615802f 100644 --- a/contrib/bind9/lib/dns/sdlz.c +++ b/contrib/bind9/lib/dns/sdlz.c @@ -538,8 +538,9 @@ destroynode(dns_sdlznode_t *node) { } static isc_result_t -findnode(dns_db_t *db, dns_name_t *name, isc_boolean_t create, - dns_dbnode_t **nodep) +findnodeext(dns_db_t *db, dns_name_t *name, isc_boolean_t create, + dns_clientinfomethods_t *methods, dns_clientinfo_t *clientinfo, + dns_dbnode_t **nodep) { dns_sdlz_db_t *sdlz = (dns_sdlz_db_t *)db; dns_sdlznode_t *node = NULL; @@ -598,17 +599,18 @@ findnode(dns_db_t *db, dns_name_t *name, isc_boolean_t create, /* try to lookup the host (namestr) */ result = sdlz->dlzimp->methods->lookup(zonestr, namestr, sdlz->dlzimp->driverarg, - sdlz->dbdata, node); + sdlz->dbdata, node, + methods, clientinfo); /* * if the host (namestr) was not found, try to lookup a * "wildcard" host. */ - if (result != ISC_R_SUCCESS && !create) { + if (result != ISC_R_SUCCESS && !create) result = sdlz->dlzimp->methods->lookup(zonestr, "*", sdlz->dlzimp->driverarg, - sdlz->dbdata, node); - } + sdlz->dbdata, node, + methods, clientinfo); MAYBE_UNLOCK(sdlz->dlzimp); @@ -651,6 +653,13 @@ findnode(dns_db_t *db, dns_name_t *name, isc_boolean_t create, return (ISC_R_SUCCESS); } +static isc_result_t +findnode(dns_db_t *db, dns_name_t *name, isc_boolean_t create, + dns_dbnode_t **nodep) +{ + return (findnodeext(db, name, create, NULL, NULL, nodep)); +} + static isc_result_t findzonecut(dns_db_t *db, dns_name_t *name, unsigned int options, isc_stdtime_t now, dns_dbnode_t **nodep, dns_name_t *foundname, @@ -825,10 +834,11 @@ findrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version, } static isc_result_t -find(dns_db_t *db, dns_name_t *name, dns_dbversion_t *version, - dns_rdatatype_t type, unsigned int options, isc_stdtime_t now, - dns_dbnode_t **nodep, dns_name_t *foundname, - dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset) +findext(dns_db_t *db, dns_name_t *name, dns_dbversion_t *version, + dns_rdatatype_t type, unsigned int options, isc_stdtime_t now, + dns_dbnode_t **nodep, dns_name_t *foundname, + dns_clientinfomethods_t *methods, dns_clientinfo_t *clientinfo, + dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset) { dns_sdlz_db_t *sdlz = (dns_sdlz_db_t *)db; dns_dbnode_t *node = NULL; @@ -867,7 +877,8 @@ find(dns_db_t *db, dns_name_t *name, dns_dbversion_t *version, * Look up the next label. */ dns_name_getlabelsequence(name, nlabels - i, i, xname); - result = findnode(db, xname, ISC_FALSE, &node); + result = findnodeext(db, xname, ISC_FALSE, + methods, clientinfo, &node); if (result != ISC_R_SUCCESS) { result = DNS_R_NXDOMAIN; continue; @@ -879,8 +890,8 @@ find(dns_db_t *db, dns_name_t *name, dns_dbversion_t *version, */ if (i < nlabels) { result = findrdataset(db, node, version, - dns_rdatatype_dname, - 0, now, rdataset, sigrdataset); + dns_rdatatype_dname, 0, now, + rdataset, sigrdataset); if (result == ISC_R_SUCCESS) { result = DNS_R_DNAME; break; @@ -893,8 +904,8 @@ find(dns_db_t *db, dns_name_t *name, dns_dbversion_t *version, */ if (i != olabels && (options & DNS_DBFIND_GLUEOK) == 0) { result = findrdataset(db, node, version, - dns_rdatatype_ns, - 0, now, rdataset, sigrdataset); + dns_rdatatype_ns, 0, now, + rdataset, sigrdataset); if (result == ISC_R_SUCCESS) { if (i == nlabels && type == dns_rdatatype_any) { @@ -933,8 +944,8 @@ find(dns_db_t *db, dns_name_t *name, dns_dbversion_t *version, /* * Look for the qtype. */ - result = findrdataset(db, node, version, type, - 0, now, rdataset, sigrdataset); + result = findrdataset(db, node, version, type, 0, now, + rdataset, sigrdataset); if (result == ISC_R_SUCCESS) break; @@ -943,8 +954,8 @@ find(dns_db_t *db, dns_name_t *name, dns_dbversion_t *version, */ if (type != dns_rdatatype_cname) { result = findrdataset(db, node, version, - dns_rdatatype_cname, - 0, now, rdataset, sigrdataset); + dns_rdatatype_cname, 0, now, + rdataset, sigrdataset); if (result == ISC_R_SUCCESS) { result = DNS_R_CNAME; break; @@ -979,6 +990,16 @@ find(dns_db_t *db, dns_name_t *name, dns_dbversion_t *version, return (result); } +static isc_result_t +find(dns_db_t *db, dns_name_t *name, dns_dbversion_t *version, + dns_rdatatype_t type, unsigned int options, isc_stdtime_t now, + dns_dbnode_t **nodep, dns_name_t *foundname, + dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset) +{ + return (findext(db, name, version, type, options, now, nodep, + foundname, NULL, NULL, rdataset, sigrdataset)); +} + static isc_result_t allrdatasets(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version, isc_stdtime_t now, dns_rdatasetiter_t **iteratorp) @@ -1194,7 +1215,8 @@ getoriginnode(dns_db_t *db, dns_dbnode_t **nodep) { if (sdlz->dlzimp->methods->newversion == NULL) return (ISC_R_NOTIMPLEMENTED); - result = findnode(db, &sdlz->common.origin, ISC_FALSE, nodep); + result = findnodeext(db, &sdlz->common.origin, ISC_FALSE, + NULL, NULL, nodep); if (result != ISC_R_SUCCESS) sdlz_log(ISC_LOG_ERROR, "sdlz getoriginnode failed : %s", isc_result_totext(result)); @@ -1230,16 +1252,18 @@ static dns_dbmethods_t sdlzdb_methods = { overmem, settask, getoriginnode, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL + NULL, /* transfernode */ + NULL, /* getnsec3parameters */ + NULL, /* findnsec3node */ + NULL, /* setsigningtime */ + NULL, /* getsigningtime */ + NULL, /* resigned */ + NULL, /* isdnssec */ + NULL, /* getrrsetstats */ + NULL, /* rpz_enabled */ + NULL, /* rpz_findips */ + findnodeext, + findext }; /* diff --git a/contrib/bind9/lib/dns/update.c b/contrib/bind9/lib/dns/update.c new file mode 100644 index 00000000000..14ffcc2234d --- /dev/null +++ b/contrib/bind9/lib/dns/update.c @@ -0,0 +1,1865 @@ +/* + * Copyright (C) 2011, 2012 Internet Systems Consortium, Inc. ("ISC") + * + * Permission to use, copy, modify, and/or 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 ISC DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL ISC 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. + */ + +/* $Id$ */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +/**************************************************************************/ + +/*% + * Log level for tracing dynamic update protocol requests. + */ +#define LOGLEVEL_PROTOCOL ISC_LOG_INFO + +/*% + * Log level for low-level debug tracing. + */ +#define LOGLEVEL_DEBUG ISC_LOG_DEBUG(8) + +/*% + * Check an operation for failure. These macros all assume that + * the function using them has a 'result' variable and a 'failure' + * label. + */ +#define CHECK(op) \ + do { result = (op); \ + if (result != ISC_R_SUCCESS) goto failure; \ + } while (0) + +/*% + * Fail unconditionally with result 'code', which must not + * be ISC_R_SUCCESS. The reason for failure presumably has + * been logged already. + * + * The test against ISC_R_SUCCESS is there to keep the Solaris compiler + * from complaining about "end-of-loop code not reached". + */ + +#define FAIL(code) \ + do { \ + result = (code); \ + if (result != ISC_R_SUCCESS) goto failure; \ + } while (0) + +/*% + * Fail unconditionally and log as a client error. + * The test against ISC_R_SUCCESS is there to keep the Solaris compiler + * from complaining about "end-of-loop code not reached". + */ +#define FAILC(code, msg) \ + do { \ + const char *_what = "failed"; \ + result = (code); \ + switch (result) { \ + case DNS_R_NXDOMAIN: \ + case DNS_R_YXDOMAIN: \ + case DNS_R_YXRRSET: \ + case DNS_R_NXRRSET: \ + _what = "unsuccessful"; \ + } \ + update_log(log, zone, LOGLEVEL_PROTOCOL, \ + "update %s: %s (%s)", _what, \ + msg, isc_result_totext(result)); \ + if (result != ISC_R_SUCCESS) goto failure; \ + } while (0) + +#define FAILN(code, name, msg) \ + do { \ + const char *_what = "failed"; \ + result = (code); \ + switch (result) { \ + case DNS_R_NXDOMAIN: \ + case DNS_R_YXDOMAIN: \ + case DNS_R_YXRRSET: \ + case DNS_R_NXRRSET: \ + _what = "unsuccessful"; \ + } \ + if (isc_log_wouldlog(dns_lctx, LOGLEVEL_PROTOCOL)) { \ + char _nbuf[DNS_NAME_FORMATSIZE]; \ + dns_name_format(name, _nbuf, sizeof(_nbuf)); \ + update_log(log, zone, LOGLEVEL_PROTOCOL, \ + "update %s: %s: %s (%s)", _what, _nbuf, \ + msg, isc_result_totext(result)); \ + } \ + if (result != ISC_R_SUCCESS) goto failure; \ + } while (0) + +#define FAILNT(code, name, type, msg) \ + do { \ + const char *_what = "failed"; \ + result = (code); \ + switch (result) { \ + case DNS_R_NXDOMAIN: \ + case DNS_R_YXDOMAIN: \ + case DNS_R_YXRRSET: \ + case DNS_R_NXRRSET: \ + _what = "unsuccessful"; \ + } \ + if (isc_log_wouldlog(dns_lctx, LOGLEVEL_PROTOCOL)) { \ + char _nbuf[DNS_NAME_FORMATSIZE]; \ + char _tbuf[DNS_RDATATYPE_FORMATSIZE]; \ + dns_name_format(name, _nbuf, sizeof(_nbuf)); \ + dns_rdatatype_format(type, _tbuf, sizeof(_tbuf)); \ + update_log(log, zone, LOGLEVEL_PROTOCOL, \ + "update %s: %s/%s: %s (%s)", \ + _what, _nbuf, _tbuf, msg, \ + isc_result_totext(result)); \ + } \ + if (result != ISC_R_SUCCESS) goto failure; \ + } while (0) + +/*% + * Fail unconditionally and log as a server error. + * The test against ISC_R_SUCCESS is there to keep the Solaris compiler + * from complaining about "end-of-loop code not reached". + */ +#define FAILS(code, msg) \ + do { \ + result = (code); \ + update_log(log, zone, LOGLEVEL_PROTOCOL, \ + "error: %s: %s", \ + msg, isc_result_totext(result)); \ + if (result != ISC_R_SUCCESS) goto failure; \ + } while (0) + +/**************************************************************************/ + +typedef struct rr rr_t; + +struct rr { + /* dns_name_t name; */ + isc_uint32_t ttl; + dns_rdata_t rdata; +}; + +typedef struct update_event update_event_t; + +/**************************************************************************/ + +static void +update_log(dns_update_log_t *callback, dns_zone_t *zone, + int level, const char *fmt, ...) ISC_FORMAT_PRINTF(4, 5); + +static void +update_log(dns_update_log_t *callback, dns_zone_t *zone, + int level, const char *fmt, ...) +{ + va_list ap; + char message[4096]; + + if (callback == NULL) + return; + + if (isc_log_wouldlog(dns_lctx, level) == ISC_FALSE) + return; + + + va_start(ap, fmt); + vsnprintf(message, sizeof(message), fmt, ap); + va_end(ap); + + (callback->func)(callback->arg, zone, level, message); +} + +/*% + * Update a single RR in version 'ver' of 'db' and log the + * update in 'diff'. + * + * Ensures: + * \li '*tuple' == NULL. Either the tuple is freed, or its + * ownership has been transferred to the diff. + */ +static isc_result_t +do_one_tuple(dns_difftuple_t **tuple, dns_db_t *db, dns_dbversion_t *ver, + dns_diff_t *diff) +{ + dns_diff_t temp_diff; + isc_result_t result; + + /* + * Create a singleton diff. + */ + dns_diff_init(diff->mctx, &temp_diff); + temp_diff.resign = diff->resign; + ISC_LIST_APPEND(temp_diff.tuples, *tuple, link); + + /* + * Apply it to the database. + */ + result = dns_diff_apply(&temp_diff, db, ver); + ISC_LIST_UNLINK(temp_diff.tuples, *tuple, link); + if (result != ISC_R_SUCCESS) { + dns_difftuple_free(tuple); + return (result); + } + + /* + * Merge it into the current pending journal entry. + */ + dns_diff_appendminimal(diff, tuple); + + /* + * Do not clear temp_diff. + */ + return (ISC_R_SUCCESS); +} + +static isc_result_t +update_one_rr(dns_db_t *db, dns_dbversion_t *ver, dns_diff_t *diff, + dns_diffop_t op, dns_name_t *name, dns_ttl_t ttl, + dns_rdata_t *rdata) +{ + dns_difftuple_t *tuple = NULL; + isc_result_t result; + result = dns_difftuple_create(diff->mctx, op, + name, ttl, rdata, &tuple); + if (result != ISC_R_SUCCESS) + return (result); + return (do_one_tuple(&tuple, db, ver, diff)); +} + +/**************************************************************************/ +/* + * Callback-style iteration over rdatasets and rdatas. + * + * foreach_rrset() can be used to iterate over the RRsets + * of a name and call a callback function with each + * one. Similarly, foreach_rr() can be used to iterate + * over the individual RRs at name, optionally restricted + * to RRs of a given type. + * + * The callback functions are called "actions" and take + * two arguments: a void pointer for passing arbitrary + * context information, and a pointer to the current RRset + * or RR. By convention, their names end in "_action". + */ + +/* + * XXXRTH We might want to make this public somewhere in libdns. + */ + +/*% + * Function type for foreach_rrset() iterator actions. + */ +typedef isc_result_t rrset_func(void *data, dns_rdataset_t *rrset); + +/*% + * Function type for foreach_rr() iterator actions. + */ +typedef isc_result_t rr_func(void *data, rr_t *rr); + +/*% + * Internal context struct for foreach_node_rr(). + */ +typedef struct { + rr_func * rr_action; + void * rr_action_data; +} foreach_node_rr_ctx_t; + +/*% + * Internal helper function for foreach_node_rr(). + */ +static isc_result_t +foreach_node_rr_action(void *data, dns_rdataset_t *rdataset) { + isc_result_t result; + foreach_node_rr_ctx_t *ctx = data; + for (result = dns_rdataset_first(rdataset); + result == ISC_R_SUCCESS; + result = dns_rdataset_next(rdataset)) + { + rr_t rr = { 0, DNS_RDATA_INIT }; + + dns_rdataset_current(rdataset, &rr.rdata); + rr.ttl = rdataset->ttl; + result = (*ctx->rr_action)(ctx->rr_action_data, &rr); + if (result != ISC_R_SUCCESS) + return (result); + } + if (result != ISC_R_NOMORE) + return (result); + return (ISC_R_SUCCESS); +} + +/*% + * For each rdataset of 'name' in 'ver' of 'db', call 'action' + * with the rdataset and 'action_data' as arguments. If the name + * does not exist, do nothing. + * + * If 'action' returns an error, abort iteration and return the error. + */ +static isc_result_t +foreach_rrset(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name, + rrset_func *action, void *action_data) +{ + isc_result_t result; + dns_dbnode_t *node; + dns_rdatasetiter_t *iter; + + node = NULL; + result = dns_db_findnode(db, name, ISC_FALSE, &node); + if (result == ISC_R_NOTFOUND) + return (ISC_R_SUCCESS); + if (result != ISC_R_SUCCESS) + return (result); + + iter = NULL; + result = dns_db_allrdatasets(db, node, ver, + (isc_stdtime_t) 0, &iter); + if (result != ISC_R_SUCCESS) + goto cleanup_node; + + for (result = dns_rdatasetiter_first(iter); + result == ISC_R_SUCCESS; + result = dns_rdatasetiter_next(iter)) + { + dns_rdataset_t rdataset; + + dns_rdataset_init(&rdataset); + dns_rdatasetiter_current(iter, &rdataset); + + result = (*action)(action_data, &rdataset); + + dns_rdataset_disassociate(&rdataset); + if (result != ISC_R_SUCCESS) + goto cleanup_iterator; + } + if (result == ISC_R_NOMORE) + result = ISC_R_SUCCESS; + + cleanup_iterator: + dns_rdatasetiter_destroy(&iter); + + cleanup_node: + dns_db_detachnode(db, &node); + + return (result); +} + +/*% + * For each RR of 'name' in 'ver' of 'db', call 'action' + * with the RR and 'action_data' as arguments. If the name + * does not exist, do nothing. + * + * If 'action' returns an error, abort iteration + * and return the error. + */ +static isc_result_t +foreach_node_rr(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name, + rr_func *rr_action, void *rr_action_data) +{ + foreach_node_rr_ctx_t ctx; + ctx.rr_action = rr_action; + ctx.rr_action_data = rr_action_data; + return (foreach_rrset(db, ver, name, + foreach_node_rr_action, &ctx)); +} + + +/*% + * For each of the RRs specified by 'db', 'ver', 'name', 'type', + * (which can be dns_rdatatype_any to match any type), and 'covers', call + * 'action' with the RR and 'action_data' as arguments. If the name + * does not exist, or if no RRset of the given type exists at the name, + * do nothing. + * + * If 'action' returns an error, abort iteration and return the error. + */ +static isc_result_t +foreach_rr(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name, + dns_rdatatype_t type, dns_rdatatype_t covers, rr_func *rr_action, + void *rr_action_data) +{ + + isc_result_t result; + dns_dbnode_t *node; + dns_rdataset_t rdataset; + + if (type == dns_rdatatype_any) + return (foreach_node_rr(db, ver, name, + rr_action, rr_action_data)); + + node = NULL; + if (type == dns_rdatatype_nsec3 || + (type == dns_rdatatype_rrsig && covers == dns_rdatatype_nsec3)) + result = dns_db_findnsec3node(db, name, ISC_FALSE, &node); + else + result = dns_db_findnode(db, name, ISC_FALSE, &node); + if (result == ISC_R_NOTFOUND) + return (ISC_R_SUCCESS); + if (result != ISC_R_SUCCESS) + return (result); + + dns_rdataset_init(&rdataset); + result = dns_db_findrdataset(db, node, ver, type, covers, + (isc_stdtime_t) 0, &rdataset, NULL); + if (result == ISC_R_NOTFOUND) { + result = ISC_R_SUCCESS; + goto cleanup_node; + } + if (result != ISC_R_SUCCESS) + goto cleanup_node; + + for (result = dns_rdataset_first(&rdataset); + result == ISC_R_SUCCESS; + result = dns_rdataset_next(&rdataset)) + { + rr_t rr = { 0, DNS_RDATA_INIT }; + dns_rdataset_current(&rdataset, &rr.rdata); + rr.ttl = rdataset.ttl; + result = (*rr_action)(rr_action_data, &rr); + if (result != ISC_R_SUCCESS) + goto cleanup_rdataset; + } + if (result != ISC_R_NOMORE) + goto cleanup_rdataset; + result = ISC_R_SUCCESS; + + cleanup_rdataset: + dns_rdataset_disassociate(&rdataset); + cleanup_node: + dns_db_detachnode(db, &node); + + return (result); +} + +/**************************************************************************/ +/* + * Various tests on the database contents (for prerequisites, etc). + */ + +/*% + * Function type for predicate functions that compare a database RR 'db_rr' + * against an update RR 'update_rr'. + */ +typedef isc_boolean_t rr_predicate(dns_rdata_t *update_rr, dns_rdata_t *db_rr); + +/*% + * Helper function for rrset_exists(). + */ +static isc_result_t +rrset_exists_action(void *data, rr_t *rr) { + UNUSED(data); + UNUSED(rr); + return (ISC_R_EXISTS); +} + +/*% + * Utility macro for RR existence checking functions. + * + * If the variable 'result' has the value ISC_R_EXISTS or + * ISC_R_SUCCESS, set *exists to ISC_TRUE or ISC_FALSE, + * respectively, and return success. + * + * If 'result' has any other value, there was a failure. + * Return the failure result code and do not set *exists. + * + * This would be more readable as "do { if ... } while(0)", + * but that form generates tons of warnings on Solaris 2.6. + */ +#define RETURN_EXISTENCE_FLAG \ + return ((result == ISC_R_EXISTS) ? \ + (*exists = ISC_TRUE, ISC_R_SUCCESS) : \ + ((result == ISC_R_SUCCESS) ? \ + (*exists = ISC_FALSE, ISC_R_SUCCESS) : \ + result)) + +/*% + * Set '*exists' to true iff an rrset of the given type exists, + * to false otherwise. + */ +static isc_result_t +rrset_exists(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name, + dns_rdatatype_t type, dns_rdatatype_t covers, + isc_boolean_t *exists) +{ + isc_result_t result; + result = foreach_rr(db, ver, name, type, covers, + rrset_exists_action, NULL); + RETURN_EXISTENCE_FLAG; +} + +/*% + * Set '*visible' to true if the RRset exists and is part of the + * visible zone. Otherwise '*visible' is set to false unless a + * error occurs. + */ +static isc_result_t +rrset_visible(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name, + dns_rdatatype_t type, isc_boolean_t *visible) +{ + isc_result_t result; + dns_fixedname_t fixed; + + dns_fixedname_init(&fixed); + result = dns_db_find(db, name, ver, type, DNS_DBFIND_NOWILD, + (isc_stdtime_t) 0, NULL, + dns_fixedname_name(&fixed), NULL, NULL); + switch (result) { + case ISC_R_SUCCESS: + *visible = ISC_TRUE; + break; + /* + * Glue, obscured, deleted or replaced records. + */ + case DNS_R_DELEGATION: + case DNS_R_DNAME: + case DNS_R_CNAME: + case DNS_R_NXDOMAIN: + case DNS_R_NXRRSET: + case DNS_R_EMPTYNAME: + case DNS_R_COVERINGNSEC: + *visible = ISC_FALSE; + result = ISC_R_SUCCESS; + break; + default: + break; + } + return (result); +} + +/*% + * Context struct and helper function for name_exists(). + */ + +static isc_result_t +name_exists_action(void *data, dns_rdataset_t *rrset) { + UNUSED(data); + UNUSED(rrset); + return (ISC_R_EXISTS); +} + +/*% + * Set '*exists' to true iff the given name exists, to false otherwise. + */ +static isc_result_t +name_exists(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name, + isc_boolean_t *exists) +{ + isc_result_t result; + result = foreach_rrset(db, ver, name, + name_exists_action, NULL); + RETURN_EXISTENCE_FLAG; +} + +/**************************************************************************/ +/* + * Checking of "RRset exists (value dependent)" prerequisites. + * + * In the RFC2136 section 3.2.5, this is the pseudocode involving + * a variable called "temp", a mapping of tuples to rrsets. + * + * Here, we represent the "temp" data structure as (non-minimal) "dns_diff_t" + * where each tuple has op==DNS_DIFFOP_EXISTS. + */ + +/*% + * A comparison function defining the sorting order for the entries + * in the "temp" data structure. The major sort key is the owner name, + * followed by the type and rdata. + */ +static int +temp_order(const void *av, const void *bv) { + dns_difftuple_t const * const *ap = av; + dns_difftuple_t const * const *bp = bv; + dns_difftuple_t const *a = *ap; + dns_difftuple_t const *b = *bp; + int r; + r = dns_name_compare(&a->name, &b->name); + if (r != 0) + return (r); + r = (b->rdata.type - a->rdata.type); + if (r != 0) + return (r); + r = dns_rdata_casecompare(&a->rdata, &b->rdata); + return (r); +} + +/**************************************************************************/ +/* + * Conditional deletion of RRs. + */ + +/*% + * Context structure for delete_if(). + */ + +typedef struct { + rr_predicate *predicate; + dns_db_t *db; + dns_dbversion_t *ver; + dns_diff_t *diff; + dns_name_t *name; + dns_rdata_t *update_rr; +} conditional_delete_ctx_t; + +/*% + * Predicate functions for delete_if(). + */ + +/*% + * Return true always. + */ +static isc_boolean_t +true_p(dns_rdata_t *update_rr, dns_rdata_t *db_rr) { + UNUSED(update_rr); + UNUSED(db_rr); + return (ISC_TRUE); +} + +/*% + * Return true if the record is a RRSIG. + */ +static isc_boolean_t +rrsig_p(dns_rdata_t *update_rr, dns_rdata_t *db_rr) { + UNUSED(update_rr); + return ((db_rr->type == dns_rdatatype_rrsig) ? + ISC_TRUE : ISC_FALSE); +} + +/*% + * Internal helper function for delete_if(). + */ +static isc_result_t +delete_if_action(void *data, rr_t *rr) { + conditional_delete_ctx_t *ctx = data; + if ((*ctx->predicate)(ctx->update_rr, &rr->rdata)) { + isc_result_t result; + result = update_one_rr(ctx->db, ctx->ver, ctx->diff, + DNS_DIFFOP_DEL, ctx->name, + rr->ttl, &rr->rdata); + return (result); + } else { + return (ISC_R_SUCCESS); + } +} + +/*% + * Conditionally delete RRs. Apply 'predicate' to the RRs + * specified by 'db', 'ver', 'name', and 'type' (which can + * be dns_rdatatype_any to match any type). Delete those + * RRs for which the predicate returns true, and log the + * deletions in 'diff'. + */ +static isc_result_t +delete_if(rr_predicate *predicate, dns_db_t *db, dns_dbversion_t *ver, + dns_name_t *name, dns_rdatatype_t type, dns_rdatatype_t covers, + dns_rdata_t *update_rr, dns_diff_t *diff) +{ + conditional_delete_ctx_t ctx; + ctx.predicate = predicate; + ctx.db = db; + ctx.ver = ver; + ctx.diff = diff; + ctx.name = name; + ctx.update_rr = update_rr; + return (foreach_rr(db, ver, name, type, covers, + delete_if_action, &ctx)); +} + +/**************************************************************************/ +/* + * Incremental updating of NSECs and RRSIGs. + */ + +/*% + * We abuse the dns_diff_t type to represent a set of domain names + * affected by the update. + */ +static isc_result_t +namelist_append_name(dns_diff_t *list, dns_name_t *name) { + isc_result_t result; + dns_difftuple_t *tuple = NULL; + static dns_rdata_t dummy_rdata = DNS_RDATA_INIT; + + CHECK(dns_difftuple_create(list->mctx, DNS_DIFFOP_EXISTS, name, 0, + &dummy_rdata, &tuple)); + dns_diff_append(list, &tuple); + failure: + return (result); +} + +static isc_result_t +namelist_append_subdomain(dns_db_t *db, dns_name_t *name, dns_diff_t *affected) +{ + isc_result_t result; + dns_fixedname_t fixedname; + dns_name_t *child; + dns_dbiterator_t *dbit = NULL; + + dns_fixedname_init(&fixedname); + child = dns_fixedname_name(&fixedname); + + CHECK(dns_db_createiterator(db, DNS_DB_NONSEC3, &dbit)); + + for (result = dns_dbiterator_seek(dbit, name); + result == ISC_R_SUCCESS; + result = dns_dbiterator_next(dbit)) + { + dns_dbnode_t *node = NULL; + CHECK(dns_dbiterator_current(dbit, &node, child)); + dns_db_detachnode(db, &node); + if (! dns_name_issubdomain(child, name)) + break; + CHECK(namelist_append_name(affected, child)); + } + if (result == ISC_R_NOMORE) + result = ISC_R_SUCCESS; + failure: + if (dbit != NULL) + dns_dbiterator_destroy(&dbit); + return (result); +} + + + +/*% + * Helper function for non_nsec_rrset_exists(). + */ +static isc_result_t +is_non_nsec_action(void *data, dns_rdataset_t *rrset) { + UNUSED(data); + if (!(rrset->type == dns_rdatatype_nsec || + rrset->type == dns_rdatatype_nsec3 || + (rrset->type == dns_rdatatype_rrsig && + (rrset->covers == dns_rdatatype_nsec || + rrset->covers == dns_rdatatype_nsec3)))) + return (ISC_R_EXISTS); + return (ISC_R_SUCCESS); +} + +/*% + * Check whether there is an rrset other than a NSEC or RRSIG NSEC, + * i.e., anything that justifies the continued existence of a name + * after a secure update. + * + * If such an rrset exists, set '*exists' to ISC_TRUE. + * Otherwise, set it to ISC_FALSE. + */ +static isc_result_t +non_nsec_rrset_exists(dns_db_t *db, dns_dbversion_t *ver, + dns_name_t *name, isc_boolean_t *exists) +{ + isc_result_t result; + result = foreach_rrset(db, ver, name, is_non_nsec_action, NULL); + RETURN_EXISTENCE_FLAG; +} + +/*% + * A comparison function for sorting dns_diff_t:s by name. + */ +static int +name_order(const void *av, const void *bv) { + dns_difftuple_t const * const *ap = av; + dns_difftuple_t const * const *bp = bv; + dns_difftuple_t const *a = *ap; + dns_difftuple_t const *b = *bp; + return (dns_name_compare(&a->name, &b->name)); +} + +static isc_result_t +uniqify_name_list(dns_diff_t *list) { + isc_result_t result; + dns_difftuple_t *p, *q; + + CHECK(dns_diff_sort(list, name_order)); + + p = ISC_LIST_HEAD(list->tuples); + while (p != NULL) { + do { + q = ISC_LIST_NEXT(p, link); + if (q == NULL || ! dns_name_equal(&p->name, &q->name)) + break; + ISC_LIST_UNLINK(list->tuples, q, link); + dns_difftuple_free(&q); + } while (1); + p = ISC_LIST_NEXT(p, link); + } + failure: + return (result); +} + +static isc_result_t +is_active(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name, + isc_boolean_t *flag, isc_boolean_t *cut, isc_boolean_t *unsecure) +{ + isc_result_t result; + dns_fixedname_t foundname; + dns_fixedname_init(&foundname); + result = dns_db_find(db, name, ver, dns_rdatatype_any, + DNS_DBFIND_GLUEOK | DNS_DBFIND_NOWILD, + (isc_stdtime_t) 0, NULL, + dns_fixedname_name(&foundname), + NULL, NULL); + if (result == ISC_R_SUCCESS || result == DNS_R_EMPTYNAME) { + *flag = ISC_TRUE; + *cut = ISC_FALSE; + if (unsecure != NULL) + *unsecure = ISC_FALSE; + return (ISC_R_SUCCESS); + } else if (result == DNS_R_ZONECUT) { + *flag = ISC_TRUE; + *cut = ISC_TRUE; + if (unsecure != NULL) { + /* + * We are at the zonecut. Check to see if there + * is a DS RRset. + */ + if (dns_db_find(db, name, ver, dns_rdatatype_ds, 0, + (isc_stdtime_t) 0, NULL, + dns_fixedname_name(&foundname), + NULL, NULL) == DNS_R_NXRRSET) + *unsecure = ISC_TRUE; + else + *unsecure = ISC_FALSE; + } + return (ISC_R_SUCCESS); + } else if (result == DNS_R_GLUE || result == DNS_R_DNAME || + result == DNS_R_DELEGATION || result == DNS_R_NXDOMAIN) { + *flag = ISC_FALSE; + *cut = ISC_FALSE; + if (unsecure != NULL) + *unsecure = ISC_FALSE; + return (ISC_R_SUCCESS); + } else { + /* + * Silence compiler. + */ + *flag = ISC_FALSE; + *cut = ISC_FALSE; + if (unsecure != NULL) + *unsecure = ISC_FALSE; + return (result); + } +} + +/*% + * Find the next/previous name that has a NSEC record. + * In other words, skip empty database nodes and names that + * have had their NSECs removed because they are obscured by + * a zone cut. + */ +static isc_result_t +next_active(dns_update_log_t *log, dns_zone_t *zone, dns_db_t *db, + dns_dbversion_t *ver, dns_name_t *oldname, dns_name_t *newname, + isc_boolean_t forward) +{ + isc_result_t result; + dns_dbiterator_t *dbit = NULL; + isc_boolean_t has_nsec = ISC_FALSE; + unsigned int wraps = 0; + isc_boolean_t secure = dns_db_issecure(db); + + CHECK(dns_db_createiterator(db, 0, &dbit)); + + CHECK(dns_dbiterator_seek(dbit, oldname)); + do { + dns_dbnode_t *node = NULL; + + if (forward) + result = dns_dbiterator_next(dbit); + else + result = dns_dbiterator_prev(dbit); + if (result == ISC_R_NOMORE) { + /* + * Wrap around. + */ + if (forward) + CHECK(dns_dbiterator_first(dbit)); + else + CHECK(dns_dbiterator_last(dbit)); + wraps++; + if (wraps == 2) { + update_log(log, zone, ISC_LOG_ERROR, + "secure zone with no NSECs"); + result = DNS_R_BADZONE; + goto failure; + } + } + CHECK(dns_dbiterator_current(dbit, &node, newname)); + dns_db_detachnode(db, &node); + + /* + * The iterator may hold the tree lock, and + * rrset_exists() calls dns_db_findnode() which + * may try to reacquire it. To avoid deadlock + * we must pause the iterator first. + */ + CHECK(dns_dbiterator_pause(dbit)); + if (secure) { + CHECK(rrset_exists(db, ver, newname, + dns_rdatatype_nsec, 0, &has_nsec)); + } else { + dns_fixedname_t ffound; + dns_name_t *found; + dns_fixedname_init(&ffound); + found = dns_fixedname_name(&ffound); + result = dns_db_find(db, newname, ver, + dns_rdatatype_soa, + DNS_DBFIND_NOWILD, 0, NULL, found, + NULL, NULL); + if (result == ISC_R_SUCCESS || + result == DNS_R_EMPTYNAME || + result == DNS_R_NXRRSET || + result == DNS_R_CNAME || + (result == DNS_R_DELEGATION && + dns_name_equal(newname, found))) { + has_nsec = ISC_TRUE; + result = ISC_R_SUCCESS; + } else if (result != DNS_R_NXDOMAIN) + break; + } + } while (! has_nsec); + failure: + if (dbit != NULL) + dns_dbiterator_destroy(&dbit); + + return (result); +} + +/*% + * Add a NSEC record for "name", recording the change in "diff". + * The existing NSEC is removed. + */ +static isc_result_t +add_nsec(dns_update_log_t *log, dns_zone_t *zone, dns_db_t *db, + dns_dbversion_t *ver, dns_name_t *name, dns_ttl_t nsecttl, + dns_diff_t *diff) +{ + isc_result_t result; + dns_dbnode_t *node = NULL; + unsigned char buffer[DNS_NSEC_BUFFERSIZE]; + dns_rdata_t rdata = DNS_RDATA_INIT; + dns_difftuple_t *tuple = NULL; + dns_fixedname_t fixedname; + dns_name_t *target; + + dns_fixedname_init(&fixedname); + target = dns_fixedname_name(&fixedname); + + /* + * Find the successor name, aka NSEC target. + */ + CHECK(next_active(log, zone, db, ver, name, target, ISC_TRUE)); + + /* + * Create the NSEC RDATA. + */ + CHECK(dns_db_findnode(db, name, ISC_FALSE, &node)); + dns_rdata_init(&rdata); + CHECK(dns_nsec_buildrdata(db, ver, node, target, buffer, &rdata)); + dns_db_detachnode(db, &node); + + /* + * Delete the old NSEC and record the change. + */ + CHECK(delete_if(true_p, db, ver, name, dns_rdatatype_nsec, 0, + NULL, diff)); + /* + * Add the new NSEC and record the change. + */ + CHECK(dns_difftuple_create(diff->mctx, DNS_DIFFOP_ADD, name, + nsecttl, &rdata, &tuple)); + CHECK(do_one_tuple(&tuple, db, ver, diff)); + INSIST(tuple == NULL); + + failure: + if (node != NULL) + dns_db_detachnode(db, &node); + return (result); +} + +/*% + * Add a placeholder NSEC record for "name", recording the change in "diff". + */ +static isc_result_t +add_placeholder_nsec(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name, + dns_diff_t *diff) +{ + isc_result_t result; + dns_difftuple_t *tuple = NULL; + isc_region_t r; + unsigned char data[1] = { 0 }; /* The root domain, no bits. */ + dns_rdata_t rdata = DNS_RDATA_INIT; + + r.base = data; + r.length = sizeof(data); + dns_rdata_fromregion(&rdata, dns_db_class(db), dns_rdatatype_nsec, &r); + CHECK(dns_difftuple_create(diff->mctx, DNS_DIFFOP_ADD, name, 0, + &rdata, &tuple)); + CHECK(do_one_tuple(&tuple, db, ver, diff)); + failure: + return (result); +} + +static isc_result_t +find_zone_keys(dns_zone_t *zone, dns_db_t *db, dns_dbversion_t *ver, + isc_mem_t *mctx, unsigned int maxkeys, + dst_key_t **keys, unsigned int *nkeys) +{ + isc_result_t result; + dns_dbnode_t *node = NULL; + const char *directory = dns_zone_getkeydirectory(zone); + CHECK(dns_db_findnode(db, dns_db_origin(db), ISC_FALSE, &node)); + CHECK(dns_dnssec_findzonekeys2(db, ver, node, dns_db_origin(db), + directory, mctx, maxkeys, keys, nkeys)); + failure: + if (node != NULL) + dns_db_detachnode(db, &node); + return (result); +} + +/*% + * Add RRSIG records for an RRset, recording the change in "diff". + */ +static isc_result_t +add_sigs(dns_update_log_t *log, dns_zone_t *zone, dns_db_t *db, + dns_dbversion_t *ver, dns_name_t *name, dns_rdatatype_t type, + dns_diff_t *diff, dst_key_t **keys, unsigned int nkeys, + isc_stdtime_t inception, isc_stdtime_t expire, + isc_boolean_t check_ksk, isc_boolean_t keyset_kskonly) +{ + isc_result_t result; + dns_dbnode_t *node = NULL; + dns_rdataset_t rdataset; + dns_rdata_t sig_rdata = DNS_RDATA_INIT; + isc_buffer_t buffer; + unsigned char data[1024]; /* XXX */ + unsigned int i, j; + isc_boolean_t added_sig = ISC_FALSE; + isc_mem_t *mctx = diff->mctx; + + dns_rdataset_init(&rdataset); + isc_buffer_init(&buffer, data, sizeof(data)); + + /* Get the rdataset to sign. */ + if (type == dns_rdatatype_nsec3) + CHECK(dns_db_findnsec3node(db, name, ISC_FALSE, &node)); + else + CHECK(dns_db_findnode(db, name, ISC_FALSE, &node)); + CHECK(dns_db_findrdataset(db, node, ver, type, 0, + (isc_stdtime_t) 0, &rdataset, NULL)); + dns_db_detachnode(db, &node); + +#define REVOKE(x) ((dst_key_flags(x) & DNS_KEYFLAG_REVOKE) != 0) +#define KSK(x) ((dst_key_flags(x) & DNS_KEYFLAG_KSK) != 0) +#define ALG(x) dst_key_alg(x) + + /* + * If we are honoring KSK flags then we need to check that we + * have both KSK and non-KSK keys that are not revoked per + * algorithm. + */ + for (i = 0; i < nkeys; i++) { + isc_boolean_t both = ISC_FALSE; + + if (!dst_key_isprivate(keys[i])) + continue; + + if (check_ksk && !REVOKE(keys[i])) { + isc_boolean_t have_ksk, have_nonksk; + if (KSK(keys[i])) { + have_ksk = ISC_TRUE; + have_nonksk = ISC_FALSE; + } else { + have_ksk = ISC_FALSE; + have_nonksk = ISC_TRUE; + } + for (j = 0; j < nkeys; j++) { + if (j == i || ALG(keys[i]) != ALG(keys[j])) + continue; + if (REVOKE(keys[j])) + continue; + if (KSK(keys[j])) + have_ksk = ISC_TRUE; + else + have_nonksk = ISC_TRUE; + both = have_ksk && have_nonksk; + if (both) + break; + } + } + + if (both) { + if (type == dns_rdatatype_dnskey) { + if (!KSK(keys[i]) && keyset_kskonly) + continue; + } else if (KSK(keys[i])) + continue; + } else if (REVOKE(keys[i]) && type != dns_rdatatype_dnskey) + continue; + + /* Calculate the signature, creating a RRSIG RDATA. */ + CHECK(dns_dnssec_sign(name, &rdataset, keys[i], + &inception, &expire, + mctx, &buffer, &sig_rdata)); + + /* Update the database and journal with the RRSIG. */ + /* XXX inefficient - will cause dataset merging */ + CHECK(update_one_rr(db, ver, diff, DNS_DIFFOP_ADDRESIGN, name, + rdataset.ttl, &sig_rdata)); + dns_rdata_reset(&sig_rdata); + isc_buffer_init(&buffer, data, sizeof(data)); + added_sig = ISC_TRUE; + } + if (!added_sig) { + update_log(log, zone, ISC_LOG_ERROR, + "found no active private keys, " + "unable to generate any signatures"); + result = ISC_R_NOTFOUND; + } + + failure: + if (dns_rdataset_isassociated(&rdataset)) + dns_rdataset_disassociate(&rdataset); + if (node != NULL) + dns_db_detachnode(db, &node); + return (result); +} + +/* + * Delete expired RRsigs and any RRsigs we are about to re-sign. + * See also zone.c:del_sigs(). + */ +static isc_result_t +del_keysigs(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name, + dns_diff_t *diff, dst_key_t **keys, unsigned int nkeys) +{ + isc_result_t result; + dns_dbnode_t *node = NULL; + dns_rdataset_t rdataset; + dns_rdata_t rdata = DNS_RDATA_INIT; + unsigned int i; + dns_rdata_rrsig_t rrsig; + isc_boolean_t found; + + dns_rdataset_init(&rdataset); + + result = dns_db_findnode(db, name, ISC_FALSE, &node); + if (result == ISC_R_NOTFOUND) + return (ISC_R_SUCCESS); + if (result != ISC_R_SUCCESS) + goto failure; + result = dns_db_findrdataset(db, node, ver, dns_rdatatype_rrsig, + dns_rdatatype_dnskey, (isc_stdtime_t) 0, + &rdataset, NULL); + dns_db_detachnode(db, &node); + + if (result == ISC_R_NOTFOUND) + return (ISC_R_SUCCESS); + if (result != ISC_R_SUCCESS) + goto failure; + + for (result = dns_rdataset_first(&rdataset); + result == ISC_R_SUCCESS; + result = dns_rdataset_next(&rdataset)) { + dns_rdataset_current(&rdataset, &rdata); + result = dns_rdata_tostruct(&rdata, &rrsig, NULL); + RUNTIME_CHECK(result == ISC_R_SUCCESS); + found = ISC_FALSE; + for (i = 0; i < nkeys; i++) { + if (rrsig.keyid == dst_key_id(keys[i])) { + found = ISC_TRUE; + if (!dst_key_isprivate(keys[i])) { + /* + * The re-signing code in zone.c + * will mark this as offline. + * Just skip the record for now. + */ + break; + } + result = update_one_rr(db, ver, diff, + DNS_DIFFOP_DEL, name, + rdataset.ttl, &rdata); + break; + } + } + /* + * If there is not a matching DNSKEY then delete the RRSIG. + */ + if (!found) + result = update_one_rr(db, ver, diff, DNS_DIFFOP_DEL, + name, rdataset.ttl, &rdata); + dns_rdata_reset(&rdata); + if (result != ISC_R_SUCCESS) + break; + } + dns_rdataset_disassociate(&rdataset); + if (result == ISC_R_NOMORE) + result = ISC_R_SUCCESS; +failure: + if (node != NULL) + dns_db_detachnode(db, &node); + return (result); +} + +static isc_result_t +add_exposed_sigs(dns_update_log_t *log, dns_zone_t *zone, dns_db_t *db, + dns_dbversion_t *ver, dns_name_t *name, isc_boolean_t cut, + dns_diff_t *diff, dst_key_t **keys, unsigned int nkeys, + isc_stdtime_t inception, isc_stdtime_t expire, + isc_boolean_t check_ksk, isc_boolean_t keyset_kskonly) +{ + isc_result_t result; + dns_dbnode_t *node; + dns_rdatasetiter_t *iter; + + node = NULL; + result = dns_db_findnode(db, name, ISC_FALSE, &node); + if (result == ISC_R_NOTFOUND) + return (ISC_R_SUCCESS); + if (result != ISC_R_SUCCESS) + return (result); + + iter = NULL; + result = dns_db_allrdatasets(db, node, ver, + (isc_stdtime_t) 0, &iter); + if (result != ISC_R_SUCCESS) + goto cleanup_node; + + for (result = dns_rdatasetiter_first(iter); + result == ISC_R_SUCCESS; + result = dns_rdatasetiter_next(iter)) + { + dns_rdataset_t rdataset; + dns_rdatatype_t type; + isc_boolean_t flag; + + dns_rdataset_init(&rdataset); + dns_rdatasetiter_current(iter, &rdataset); + type = rdataset.type; + dns_rdataset_disassociate(&rdataset); + + /* + * We don't need to sign unsigned NSEC records at the cut + * as they are handled elsewhere. + */ + if ((type == dns_rdatatype_rrsig) || + (cut && type != dns_rdatatype_ds)) + continue; + result = rrset_exists(db, ver, name, dns_rdatatype_rrsig, + type, &flag); + if (result != ISC_R_SUCCESS) + goto cleanup_iterator; + if (flag) + continue;; + result = add_sigs(log, zone, db, ver, name, type, diff, + keys, nkeys, inception, expire, + check_ksk, keyset_kskonly); + if (result != ISC_R_SUCCESS) + goto cleanup_iterator; + } + if (result == ISC_R_NOMORE) + result = ISC_R_SUCCESS; + + cleanup_iterator: + dns_rdatasetiter_destroy(&iter); + + cleanup_node: + dns_db_detachnode(db, &node); + + return (result); +} + +/*% + * Update RRSIG, NSEC and NSEC3 records affected by an update. The original + * update, including the SOA serial update but excluding the RRSIG & NSEC + * changes, is in "diff" and has already been applied to "newver" of "db". + * The database version prior to the update is "oldver". + * + * The necessary RRSIG, NSEC and NSEC3 changes will be applied to "newver" + * and added (as a minimal diff) to "diff". + * + * The RRSIGs generated will be valid for 'sigvalidityinterval' seconds. + */ +isc_result_t +dns_update_signatures(dns_update_log_t *log, dns_zone_t *zone, dns_db_t *db, + dns_dbversion_t *oldver, dns_dbversion_t *newver, + dns_diff_t *diff, isc_uint32_t sigvalidityinterval) +{ + isc_result_t result; + dns_difftuple_t *t; + dns_diff_t diffnames; + dns_diff_t affected; + dns_diff_t sig_diff; + dns_diff_t nsec_diff; + dns_diff_t nsec_mindiff; + isc_boolean_t flag, build_nsec, build_nsec3; + dst_key_t *zone_keys[DNS_MAXZONEKEYS]; + unsigned int nkeys = 0; + unsigned int i; + isc_stdtime_t now, inception, expire; + dns_ttl_t nsecttl; + dns_rdata_soa_t soa; + dns_rdata_t rdata = DNS_RDATA_INIT; + dns_rdataset_t rdataset; + dns_dbnode_t *node = NULL; + isc_boolean_t check_ksk, keyset_kskonly; + isc_boolean_t unsecure; + isc_boolean_t cut; + dns_rdatatype_t privatetype = dns_zone_getprivatetype(zone); + + dns_diff_init(diff->mctx, &diffnames); + dns_diff_init(diff->mctx, &affected); + + dns_diff_init(diff->mctx, &sig_diff); + sig_diff.resign = dns_zone_getsigresigninginterval(zone); + dns_diff_init(diff->mctx, &nsec_diff); + dns_diff_init(diff->mctx, &nsec_mindiff); + + result = find_zone_keys(zone, db, newver, diff->mctx, + DNS_MAXZONEKEYS, zone_keys, &nkeys); + if (result != ISC_R_SUCCESS) { + update_log(log, zone, ISC_LOG_ERROR, + "could not get zone keys for secure dynamic update"); + goto failure; + } + + isc_stdtime_get(&now); + inception = now - 3600; /* Allow for some clock skew. */ + expire = now + sigvalidityinterval; + + /* + * Do we look at the KSK flag on the DNSKEY to determining which + * keys sign which RRsets? First check the zone option then + * check the keys flags to make sure at least one has a ksk set + * and one doesn't. + */ + check_ksk = ISC_TF((dns_zone_getoptions(zone) & + DNS_ZONEOPT_UPDATECHECKKSK) != 0); + keyset_kskonly = ISC_TF((dns_zone_getoptions(zone) & + DNS_ZONEOPT_DNSKEYKSKONLY) != 0); + + /* + * Get the NSEC/NSEC3 TTL from the SOA MINIMUM field. + */ + CHECK(dns_db_findnode(db, dns_db_origin(db), ISC_FALSE, &node)); + dns_rdataset_init(&rdataset); + CHECK(dns_db_findrdataset(db, node, newver, dns_rdatatype_soa, 0, + (isc_stdtime_t) 0, &rdataset, NULL)); + CHECK(dns_rdataset_first(&rdataset)); + dns_rdataset_current(&rdataset, &rdata); + CHECK(dns_rdata_tostruct(&rdata, &soa, NULL)); + nsecttl = soa.minimum; + dns_rdataset_disassociate(&rdataset); + dns_db_detachnode(db, &node); + + /* + * Find all RRsets directly affected by the update, and + * update their RRSIGs. Also build a list of names affected + * by the update in "diffnames". + */ + CHECK(dns_diff_sort(diff, temp_order)); + + t = ISC_LIST_HEAD(diff->tuples); + while (t != NULL) { + dns_name_t *name = &t->name; + /* Now "name" is a new, unique name affected by the update. */ + + CHECK(namelist_append_name(&diffnames, name)); + + while (t != NULL && dns_name_equal(&t->name, name)) { + dns_rdatatype_t type; + type = t->rdata.type; + + /* + * Now "name" and "type" denote a new unique RRset + * affected by the update. + */ + + /* Don't sign RRSIGs. */ + if (type == dns_rdatatype_rrsig) + goto skip; + + /* + * Delete all old RRSIGs covering this type, since they + * are all invalid when the signed RRset has changed. + * We may not be able to recreate all of them - tough. + * Special case changes to the zone's DNSKEY records + * to support offline KSKs. + */ + if (type == dns_rdatatype_dnskey) + del_keysigs(db, newver, name, &sig_diff, + zone_keys, nkeys); + else + CHECK(delete_if(true_p, db, newver, name, + dns_rdatatype_rrsig, type, + NULL, &sig_diff)); + + /* + * If this RRset is still visible after the update, + * add a new signature for it. + */ + CHECK(rrset_visible(db, newver, name, type, &flag)); + if (flag) { + CHECK(add_sigs(log, zone, db, newver, name, + type, &sig_diff, zone_keys, + nkeys, inception, expire, + check_ksk, keyset_kskonly)); + } + skip: + /* Skip any other updates to the same RRset. */ + while (t != NULL && + dns_name_equal(&t->name, name) && + t->rdata.type == type) + { + t = ISC_LIST_NEXT(t, link); + } + } + } + update_log(log, zone, ISC_LOG_DEBUG(3), "updated data signatures"); + + /* Remove orphaned NSECs and RRSIG NSECs. */ + for (t = ISC_LIST_HEAD(diffnames.tuples); + t != NULL; + t = ISC_LIST_NEXT(t, link)) + { + CHECK(non_nsec_rrset_exists(db, newver, &t->name, &flag)); + if (! flag) { + CHECK(delete_if(true_p, db, newver, &t->name, + dns_rdatatype_any, 0, + NULL, &sig_diff)); + } + } + update_log(log, zone, ISC_LOG_DEBUG(3), + "removed any orphaned NSEC records"); + + /* + * See if we need to build NSEC or NSEC3 chains. + */ + CHECK(dns_private_chains(db, newver, privatetype, &build_nsec, + &build_nsec3)); + if (!build_nsec) + goto update_nsec3; + + update_log(log, zone, ISC_LOG_DEBUG(3), "rebuilding NSEC chain"); + + /* + * When a name is created or deleted, its predecessor needs to + * have its NSEC updated. + */ + for (t = ISC_LIST_HEAD(diffnames.tuples); + t != NULL; + t = ISC_LIST_NEXT(t, link)) + { + isc_boolean_t existed, exists; + dns_fixedname_t fixedname; + dns_name_t *prevname; + + dns_fixedname_init(&fixedname); + prevname = dns_fixedname_name(&fixedname); + + if (oldver != NULL) + CHECK(name_exists(db, oldver, &t->name, &existed)); + else + existed = ISC_FALSE; + CHECK(name_exists(db, newver, &t->name, &exists)); + if (exists == existed) + continue; + + /* + * Find the predecessor. + * When names become obscured or unobscured in this update + * transaction, we may find the wrong predecessor because + * the NSECs have not yet been updated to reflect the delegation + * change. This should not matter because in this case, + * the correct predecessor is either the delegation node or + * a newly unobscured node, and those nodes are on the + * "affected" list in any case. + */ + CHECK(next_active(log, zone, db, newver, + &t->name, prevname, ISC_FALSE)); + CHECK(namelist_append_name(&affected, prevname)); + } + + /* + * Find names potentially affected by delegation changes + * (obscured by adding an NS or DNAME, or unobscured by + * removing one). + */ + for (t = ISC_LIST_HEAD(diffnames.tuples); + t != NULL; + t = ISC_LIST_NEXT(t, link)) + { + isc_boolean_t ns_existed, dname_existed; + isc_boolean_t ns_exists, dname_exists; + + if (oldver != NULL) + CHECK(rrset_exists(db, oldver, &t->name, + dns_rdatatype_ns, 0, &ns_existed)); + else + ns_existed = ISC_FALSE; + if (oldver != NULL) + CHECK(rrset_exists(db, oldver, &t->name, + dns_rdatatype_dname, 0, + &dname_existed)); + else + dname_existed = ISC_FALSE; + CHECK(rrset_exists(db, newver, &t->name, dns_rdatatype_ns, 0, + &ns_exists)); + CHECK(rrset_exists(db, newver, &t->name, dns_rdatatype_dname, 0, + &dname_exists)); + if ((ns_exists || dname_exists) == (ns_existed || dname_existed)) + continue; + /* + * There was a delegation change. Mark all subdomains + * of t->name as potentially needing a NSEC update. + */ + CHECK(namelist_append_subdomain(db, &t->name, &affected)); + } + + ISC_LIST_APPENDLIST(affected.tuples, diffnames.tuples, link); + INSIST(ISC_LIST_EMPTY(diffnames.tuples)); + + CHECK(uniqify_name_list(&affected)); + + /* + * Determine which names should have NSECs, and delete/create + * NSECs to make it so. We don't know the final NSEC targets yet, + * so we just create placeholder NSECs with arbitrary contents + * to indicate that their respective owner names should be part of + * the NSEC chain. + */ + for (t = ISC_LIST_HEAD(affected.tuples); + t != NULL; + t = ISC_LIST_NEXT(t, link)) + { + isc_boolean_t exists; + dns_name_t *name = &t->name; + + CHECK(name_exists(db, newver, name, &exists)); + if (! exists) + continue; + CHECK(is_active(db, newver, name, &flag, &cut, NULL)); + if (!flag) { + /* + * This name is obscured. Delete any + * existing NSEC record. + */ + CHECK(delete_if(true_p, db, newver, name, + dns_rdatatype_nsec, 0, + NULL, &nsec_diff)); + CHECK(delete_if(rrsig_p, db, newver, name, + dns_rdatatype_any, 0, NULL, diff)); + } else { + /* + * This name is not obscured. It needs to have a + * NSEC unless it is the at the origin, in which + * case it should already exist if there is a complete + * NSEC chain and if there isn't a complete NSEC chain + * we don't want to add one as that would signal that + * there is a complete NSEC chain. + */ + if (!dns_name_equal(name, dns_db_origin(db))) { + CHECK(rrset_exists(db, newver, name, + dns_rdatatype_nsec, 0, + &flag)); + if (!flag) + CHECK(add_placeholder_nsec(db, newver, + name, diff)); + } + CHECK(add_exposed_sigs(log, zone, db, newver, name, + cut, &sig_diff, zone_keys, nkeys, + inception, expire, check_ksk, + keyset_kskonly)); + } + } + + /* + * Now we know which names are part of the NSEC chain. + * Make them all point at their correct targets. + */ + for (t = ISC_LIST_HEAD(affected.tuples); + t != NULL; + t = ISC_LIST_NEXT(t, link)) + { + CHECK(rrset_exists(db, newver, &t->name, + dns_rdatatype_nsec, 0, &flag)); + if (flag) { + /* + * There is a NSEC, but we don't know if it is correct. + * Delete it and create a correct one to be sure. + * If the update was unnecessary, the diff minimization + * will take care of eliminating it from the journal, + * IXFRs, etc. + * + * The RRSIG bit should always be set in the NSECs + * we generate, because they will all get RRSIG NSECs. + * (XXX what if the zone keys are missing?). + * Because the RRSIG NSECs have not necessarily been + * created yet, the correctness of the bit mask relies + * on the assumption that NSECs are only created if + * there is other data, and if there is other data, + * there are other RRSIGs. + */ + CHECK(add_nsec(log, zone, db, newver, &t->name, + nsecttl, &nsec_diff)); + } + } + + /* + * Minimize the set of NSEC updates so that we don't + * have to regenerate the RRSIG NSECs for NSECs that were + * replaced with identical ones. + */ + while ((t = ISC_LIST_HEAD(nsec_diff.tuples)) != NULL) { + ISC_LIST_UNLINK(nsec_diff.tuples, t, link); + dns_diff_appendminimal(&nsec_mindiff, &t); + } + + update_log(log, zone, ISC_LOG_DEBUG(3), "signing rebuilt NSEC chain"); + + /* Update RRSIG NSECs. */ + for (t = ISC_LIST_HEAD(nsec_mindiff.tuples); + t != NULL; + t = ISC_LIST_NEXT(t, link)) + { + if (t->op == DNS_DIFFOP_DEL) { + CHECK(delete_if(true_p, db, newver, &t->name, + dns_rdatatype_rrsig, dns_rdatatype_nsec, + NULL, &sig_diff)); + } else if (t->op == DNS_DIFFOP_ADD) { + CHECK(add_sigs(log, zone, db, newver, &t->name, + dns_rdatatype_nsec, &sig_diff, + zone_keys, nkeys, inception, expire, + check_ksk, keyset_kskonly)); + } else { + INSIST(0); + } + } + + update_nsec3: + + /* Record our changes for the journal. */ + while ((t = ISC_LIST_HEAD(sig_diff.tuples)) != NULL) { + ISC_LIST_UNLINK(sig_diff.tuples, t, link); + dns_diff_appendminimal(diff, &t); + } + while ((t = ISC_LIST_HEAD(nsec_mindiff.tuples)) != NULL) { + ISC_LIST_UNLINK(nsec_mindiff.tuples, t, link); + dns_diff_appendminimal(diff, &t); + } + + INSIST(ISC_LIST_EMPTY(sig_diff.tuples)); + INSIST(ISC_LIST_EMPTY(nsec_diff.tuples)); + INSIST(ISC_LIST_EMPTY(nsec_mindiff.tuples)); + + if (!build_nsec3) { + update_log(log, zone, ISC_LOG_DEBUG(3), + "no NSEC3 chains to rebuild"); + goto failure; + } + + update_log(log, zone, ISC_LOG_DEBUG(3), "rebuilding NSEC3 chains"); + + dns_diff_clear(&diffnames); + dns_diff_clear(&affected); + + CHECK(dns_diff_sort(diff, temp_order)); + + /* + * Find names potentially affected by delegation changes + * (obscured by adding an NS or DNAME, or unobscured by + * removing one). + */ + t = ISC_LIST_HEAD(diff->tuples); + while (t != NULL) { + dns_name_t *name = &t->name; + + isc_boolean_t ns_existed, dname_existed; + isc_boolean_t ns_exists, dname_exists; + isc_boolean_t exists, existed; + + if (t->rdata.type == dns_rdatatype_nsec || + t->rdata.type == dns_rdatatype_rrsig) { + t = ISC_LIST_NEXT(t, link); + continue; + } + + CHECK(namelist_append_name(&affected, name)); + + if (oldver != NULL) + CHECK(rrset_exists(db, oldver, name, dns_rdatatype_ns, + 0, &ns_existed)); + else + ns_existed = ISC_FALSE; + if (oldver != NULL) + CHECK(rrset_exists(db, oldver, name, + dns_rdatatype_dname, 0, + &dname_existed)); + else + dname_existed = ISC_FALSE; + CHECK(rrset_exists(db, newver, name, dns_rdatatype_ns, 0, + &ns_exists)); + CHECK(rrset_exists(db, newver, name, dns_rdatatype_dname, 0, + &dname_exists)); + + exists = ns_exists || dname_exists; + existed = ns_existed || dname_existed; + if (exists == existed) + goto nextname; + /* + * There was a delegation change. Mark all subdomains + * of t->name as potentially needing a NSEC3 update. + */ + CHECK(namelist_append_subdomain(db, name, &affected)); + + nextname: + while (t != NULL && dns_name_equal(&t->name, name)) + t = ISC_LIST_NEXT(t, link); + } + + for (t = ISC_LIST_HEAD(affected.tuples); + t != NULL; + t = ISC_LIST_NEXT(t, link)) { + dns_name_t *name = &t->name; + + unsecure = ISC_FALSE; /* Silence compiler warning. */ + CHECK(is_active(db, newver, name, &flag, &cut, &unsecure)); + + if (!flag) { + CHECK(delete_if(rrsig_p, db, newver, name, + dns_rdatatype_any, 0, NULL, diff)); + CHECK(dns_nsec3_delnsec3sx(db, newver, name, + privatetype, &nsec_diff)); + } else { + CHECK(add_exposed_sigs(log, zone, db, newver, name, + cut, &sig_diff, zone_keys, nkeys, + inception, expire, check_ksk, + keyset_kskonly)); + CHECK(dns_nsec3_addnsec3sx(db, newver, name, nsecttl, + unsecure, privatetype, + &nsec_diff)); + } + } + + /* + * Minimize the set of NSEC3 updates so that we don't + * have to regenerate the RRSIG NSEC3s for NSEC3s that were + * replaced with identical ones. + */ + while ((t = ISC_LIST_HEAD(nsec_diff.tuples)) != NULL) { + ISC_LIST_UNLINK(nsec_diff.tuples, t, link); + dns_diff_appendminimal(&nsec_mindiff, &t); + } + + update_log(log, zone, ISC_LOG_DEBUG(3), + "signing rebuilt NSEC3 chain"); + + /* Update RRSIG NSEC3s. */ + for (t = ISC_LIST_HEAD(nsec_mindiff.tuples); + t != NULL; + t = ISC_LIST_NEXT(t, link)) + { + if (t->op == DNS_DIFFOP_DEL) { + CHECK(delete_if(true_p, db, newver, &t->name, + dns_rdatatype_rrsig, + dns_rdatatype_nsec3, + NULL, &sig_diff)); + } else if (t->op == DNS_DIFFOP_ADD) { + CHECK(add_sigs(log, zone, db, newver, &t->name, + dns_rdatatype_nsec3, + &sig_diff, zone_keys, nkeys, + inception, expire, check_ksk, + keyset_kskonly)); + } else { + INSIST(0); + } + } + + /* Record our changes for the journal. */ + while ((t = ISC_LIST_HEAD(sig_diff.tuples)) != NULL) { + ISC_LIST_UNLINK(sig_diff.tuples, t, link); + dns_diff_appendminimal(diff, &t); + } + while ((t = ISC_LIST_HEAD(nsec_mindiff.tuples)) != NULL) { + ISC_LIST_UNLINK(nsec_mindiff.tuples, t, link); + dns_diff_appendminimal(diff, &t); + } + + INSIST(ISC_LIST_EMPTY(sig_diff.tuples)); + INSIST(ISC_LIST_EMPTY(nsec_diff.tuples)); + INSIST(ISC_LIST_EMPTY(nsec_mindiff.tuples)); + + failure: + dns_diff_clear(&sig_diff); + dns_diff_clear(&nsec_diff); + dns_diff_clear(&nsec_mindiff); + + dns_diff_clear(&affected); + dns_diff_clear(&diffnames); + + for (i = 0; i < nkeys; i++) + dst_key_free(&zone_keys[i]); + + return (result); +} + +isc_uint32_t +dns_update_soaserial(isc_uint32_t serial, dns_updatemethod_t method) { + isc_stdtime_t now; + + if (method == dns_updatemethod_unixtime) { + isc_stdtime_get(&now); + if (now != 0 && isc_serial_gt(now, serial)) + return (now); + } + + /* RFC1982 */ + serial = (serial + 1) & 0xFFFFFFFF; + if (serial == 0) + serial = 1; + + return (serial); +} diff --git a/contrib/bind9/lib/dns/validator.c b/contrib/bind9/lib/dns/validator.c index 3d7518a2beb..8cf7f665ec8 100644 --- a/contrib/bind9/lib/dns/validator.c +++ b/contrib/bind9/lib/dns/validator.c @@ -1459,8 +1459,10 @@ isselfsigned(dns_validator_t *val) { if (result != ISC_R_SUCCESS) continue; - result = dns_dnssec_verify2(name, rdataset, dstkey, - ISC_TRUE, mctx, &sigrdata, + result = dns_dnssec_verify3(name, rdataset, dstkey, + ISC_TRUE, + val->view->maxbits, + mctx, &sigrdata, dns_fixedname_name(&fixed)); dst_key_free(&dstkey); if (result != ISC_R_SUCCESS) @@ -1497,8 +1499,9 @@ verify(dns_validator_t *val, dst_key_t *key, dns_rdata_t *rdata, dns_fixedname_init(&fixed); wild = dns_fixedname_name(&fixed); again: - result = dns_dnssec_verify2(val->event->name, val->event->rdataset, - key, ignore, val->view->mctx, rdata, wild); + result = dns_dnssec_verify3(val->event->name, val->event->rdataset, + key, ignore, val->view->maxbits, + val->view->mctx, rdata, wild); if ((result == DNS_R_SIGEXPIRED || result == DNS_R_SIGFUTURE) && val->view->acceptexpired) { diff --git a/contrib/bind9/lib/dns/view.c b/contrib/bind9/lib/dns/view.c index 5b6ad6587d2..9c1a201a8bd 100644 --- a/contrib/bind9/lib/dns/view.c +++ b/contrib/bind9/lib/dns/view.c @@ -35,6 +35,7 @@ #include #include #include +#include #include #ifdef BIND9 #include @@ -183,7 +184,6 @@ dns_view_create(isc_mem_t *mctx, dns_rdataclass_t rdclass, view->answeracl_exclude = NULL; view->denyanswernames = NULL; view->answernames_exclude = NULL; - view->requestixfr = ISC_TRUE; view->provideixfr = ISC_TRUE; view->maxcachettl = 7 * 24 * 3600; view->maxncachettl = 3 * 3600; @@ -192,6 +192,7 @@ dns_view_create(isc_mem_t *mctx, dns_rdataclass_t rdclass, view->flush = ISC_FALSE; view->dlv = NULL; view->maxudp = 0; + view->maxbits = 0; view->v4_aaaa = dns_v4_aaaa_ok; view->v4_aaaa_acl = NULL; ISC_LIST_INIT(view->rpz_zones); @@ -199,6 +200,7 @@ dns_view_create(isc_mem_t *mctx, dns_rdataclass_t rdclass, view->rpz_break_dnssec = ISC_FALSE; dns_fixedname_init(&view->dlv_fixed); view->managed_keys = NULL; + view->redirect = NULL; #ifdef BIND9 view->new_zone_file = NULL; view->new_zone_config = NULL; @@ -435,6 +437,8 @@ destroy(dns_view_t *view) { } if (view->managed_keys != NULL) dns_zone_detach(&view->managed_keys); + if (view->redirect != NULL) + dns_zone_detach(&view->redirect); dns_view_setnewzones(view, ISC_FALSE, NULL, NULL); #endif dns_fwdtable_destroy(&view->fwdtable); @@ -486,7 +490,7 @@ view_flushanddetach(dns_view_t **viewp, isc_boolean_t flush) { isc_refcount_decrement(&view->references, &refs); if (refs == 0) { #ifdef BIND9 - dns_zone_t *mkzone = NULL; + dns_zone_t *mkzone = NULL, *rdzone = NULL; #endif LOCK(&view->lock); @@ -509,6 +513,12 @@ view_flushanddetach(dns_view_t **viewp, isc_boolean_t flush) { if (view->flush) dns_zone_flush(mkzone); } + if (view->redirect != NULL) { + rdzone = view->redirect; + view->redirect = NULL; + if (view->flush) + dns_zone_flush(rdzone); + } #endif done = all_done(view); UNLOCK(&view->lock); @@ -517,6 +527,9 @@ view_flushanddetach(dns_view_t **viewp, isc_boolean_t flush) { /* Need to detach zones outside view lock */ if (mkzone != NULL) dns_zone_detach(&mkzone); + + if (rdzone != NULL) + dns_zone_detach(&rdzone); #endif } @@ -661,7 +674,8 @@ req_shutdown(isc_task_t *task, isc_event_t *event) { isc_result_t dns_view_createresolver(dns_view_t *view, - isc_taskmgr_t *taskmgr, unsigned int ntasks, + isc_taskmgr_t *taskmgr, + unsigned int ntasks, unsigned int ndisp, isc_socketmgr_t *socketmgr, isc_timermgr_t *timermgr, unsigned int options, @@ -682,7 +696,7 @@ dns_view_createresolver(dns_view_t *view, return (result); isc_task_setname(view->task, "view", view); - result = dns_resolver_create(view, taskmgr, ntasks, socketmgr, + result = dns_resolver_create(view, taskmgr, ntasks, ndisp, socketmgr, timermgr, options, dispatchmgr, dispatchv4, dispatchv6, &view->resolver); @@ -714,8 +728,7 @@ dns_view_createresolver(dns_view_t *view, result = dns_requestmgr_create(view->mctx, timermgr, socketmgr, dns_resolver_taskmgr(view->resolver), dns_resolver_dispatchmgr(view->resolver), - dns_resolver_dispatchv4(view->resolver), - dns_resolver_dispatchv6(view->resolver), + dispatchv4, dispatchv6, &view->requestmgr); if (result != ISC_R_SUCCESS) { dns_adb_shutdown(view->adb); @@ -1434,6 +1447,7 @@ isc_result_t dns_view_load(dns_view_t *view, isc_boolean_t stop) { REQUIRE(DNS_VIEW_VALID(view)); + REQUIRE(view->zonetable != NULL); return (dns_zt_load(view->zonetable, stop)); } @@ -1442,9 +1456,20 @@ isc_result_t dns_view_loadnew(dns_view_t *view, isc_boolean_t stop) { REQUIRE(DNS_VIEW_VALID(view)); + REQUIRE(view->zonetable != NULL); return (dns_zt_loadnew(view->zonetable, stop)); } + +isc_result_t +dns_view_asyncload(dns_view_t *view, dns_zt_allloaded_t callback, void *arg) { + REQUIRE(DNS_VIEW_VALID(view)); + REQUIRE(view->zonetable != NULL); + + return (dns_zt_asyncload(view->zonetable, callback, arg)); +} + + #endif /* BIND9 */ isc_result_t @@ -1545,16 +1570,23 @@ dns_view_flushcache2(dns_view_t *view, isc_boolean_t fixuponly) { isc_result_t dns_view_flushname(dns_view_t *view, dns_name_t *name) { + return (dns_view_flushnode(view, name, ISC_FALSE)); +} + +isc_result_t +dns_view_flushnode(dns_view_t *view, dns_name_t *name, isc_boolean_t tree) { REQUIRE(DNS_VIEW_VALID(view)); - if (view->adb != NULL) - dns_adb_flushname(view->adb, name); - if (view->cache == NULL) - return (ISC_R_SUCCESS); - if (view->resolver != NULL) - dns_resolver_flushbadcache(view->resolver, name); - return (dns_cache_flushname(view->cache, name)); + if (!tree) { + if (view->adb != NULL) + dns_adb_flushname(view->adb, name); + if (view->cache == NULL) + return (ISC_R_SUCCESS); + if (view->resolver != NULL) + dns_resolver_flushbadcache(view->resolver, name); + } + return (dns_cache_flushnode(view->cache, name, tree)); } isc_result_t diff --git a/contrib/bind9/lib/dns/xfrin.c b/contrib/bind9/lib/dns/xfrin.c index 3026af97cdb..813f616e6fa 100644 --- a/contrib/bind9/lib/dns/xfrin.c +++ b/contrib/bind9/lib/dns/xfrin.c @@ -360,7 +360,7 @@ ixfr_init(dns_xfrin_ctx_t *xfr) { journalfile = dns_zone_getjournal(xfr->zone); if (journalfile != NULL) CHECK(dns_journal_open(xfr->mctx, journalfile, - ISC_TRUE, &xfr->ixfr.journal)); + DNS_JOURNAL_CREATE, &xfr->ixfr.journal)); result = ISC_R_SUCCESS; failure: @@ -631,7 +631,8 @@ dns_xfrin_create2(dns_zone_t *zone, dns_rdatatype_t xfrtype, isc_sockaddr_t *masteraddr, isc_sockaddr_t *sourceaddr, dns_tsigkey_t *tsigkey, isc_mem_t *mctx, isc_timermgr_t *timermgr, isc_socketmgr_t *socketmgr, - isc_task_t *task, dns_xfrindone_t done, dns_xfrin_ctx_t **xfrp) + isc_task_t *task, dns_xfrindone_t done, + dns_xfrin_ctx_t **xfrp) { dns_name_t *zonename = dns_zone_getorigin(zone); dns_xfrin_ctx_t *xfr = NULL; diff --git a/contrib/bind9/lib/dns/zone.c b/contrib/bind9/lib/dns/zone.c index c212bf68490..10ba807c52f 100644 --- a/contrib/bind9/lib/dns/zone.c +++ b/contrib/bind9/lib/dns/zone.c @@ -23,16 +23,18 @@ #include #include +#include #include +#include #include #include #include #include #include #include -#include #include #include +#include #include #include #include @@ -61,6 +63,7 @@ #include #include #include +#include #include #include #include @@ -76,8 +79,10 @@ #include #include #include +#include #include #include +#include #include @@ -145,6 +150,7 @@ typedef ISC_LIST(dns_signing_t) dns_signinglist_t; typedef struct dns_nsec3chain dns_nsec3chain_t; typedef ISC_LIST(dns_nsec3chain_t) dns_nsec3chainlist_t; typedef struct dns_keyfetch dns_keyfetch_t; +typedef struct dns_asyncload dns_asyncload_t; #define DNS_ZONE_CHECKLOCK #ifdef DNS_ZONE_CHECKLOCK @@ -217,6 +223,7 @@ struct dns_zone { isc_time_t signingtime; isc_time_t nsec3chaintime; isc_time_t refreshkeytime; + isc_uint32_t refreshkeyinterval; isc_uint32_t refreshkeycount; isc_uint32_t refresh; isc_uint32_t retry; @@ -239,9 +246,11 @@ struct dns_zone { isc_sockaddr_t masteraddr; dns_notifytype_t notifytype; isc_sockaddr_t *notify; + dns_name_t **notifykeynames; unsigned int notifycnt; isc_sockaddr_t notifyfrom; isc_task_t *task; + isc_task_t *loadtask; isc_sockaddr_t notifysrc4; isc_sockaddr_t notifysrc6; isc_sockaddr_t xfrsource4; @@ -297,8 +306,10 @@ struct dns_zone { * Optional per-zone statistics counters. Counted outside of this * module. */ + dns_zonestat_level_t statlevel; isc_boolean_t requeststats_on; isc_stats_t *requeststats; + dns_stats_t *rcvquerystats; isc_uint32_t notifydelay; dns_isselffunc_t isself; void *isselfarg; @@ -339,10 +350,26 @@ struct dns_zone { */ isc_boolean_t is_rpz; + /*% + * Serial number update method. + */ + dns_updatemethod_t updatemethod; + + /*% + * whether ixfr is requested + */ + isc_boolean_t requestixfr; + /*% * Outstanding forwarded UPDATE requests. */ dns_forwardlist_t forwards; + + dns_zone_t *raw; + dns_zone_t *secure; + + isc_boolean_t sourceserialset; + isc_uint32_t sourceserial; }; typedef struct { @@ -403,8 +430,9 @@ typedef struct { #define DNS_ZONEFLG_NEEDCOMPACT 0x02000000U #define DNS_ZONEFLG_REFRESHING 0x04000000U /*%< Refreshing keydata */ #define DNS_ZONEFLG_THAW 0x08000000U -/* #define DNS_ZONEFLG_XXXXX 0x10000000U XXXMPA unused. */ +#define DNS_ZONEFLG_LOADPENDING 0x10000000U /*%< Loading scheduled */ #define DNS_ZONEFLG_NODELAY 0x20000000U +#define DNS_ZONEFLG_SENDSECURE 0x40000000U #define DNS_ZONE_OPTION(z,o) (((z)->options & (o)) != 0) #define DNS_ZONEKEY_OPTION(z,o) (((z)->keyopts & (o)) != 0) @@ -437,7 +465,9 @@ struct dns_zonemgr { isc_timermgr_t * timermgr; isc_socketmgr_t * socketmgr; isc_taskpool_t * zonetasks; + isc_taskpool_t * loadtasks; isc_task_t * task; + isc_pool_t * mctxpool; isc_ratelimiter_t * rl; isc_rwlock_t rwlock; isc_mutex_t iolock; @@ -476,6 +506,7 @@ struct dns_notify { dns_request_t *request; dns_name_t ns; isc_sockaddr_t dst; + dns_tsigkey_t *key; ISC_LINK(dns_notify_t) link; }; @@ -594,6 +625,15 @@ struct dns_keyfetch { dns_fetch_t *fetch; }; +/*% + * Hold state for an asynchronous load + */ +struct dns_asyncload { + dns_zone_t *zone; + dns_zt_zoneloaded_t loaded; + void *loaded_arg; +}; + #define HOUR 3600 #define DAY (24*HOUR) #define MONTH (30*DAY) @@ -632,6 +672,9 @@ static void zone_namerd_tostr(dns_zone_t *zone, char *buf, size_t length); static void zone_name_tostr(dns_zone_t *zone, char *buf, size_t length); static void zone_rdclass_tostr(dns_zone_t *zone, char *buf, size_t length); static void zone_viewname_tostr(dns_zone_t *zone, char *buf, size_t length); +static isc_result_t zone_send_secureserial(dns_zone_t *zone, + isc_boolean_t secure_locked, + isc_uint32_t serial); #if 0 /* ondestroy example */ @@ -688,6 +731,8 @@ static isc_result_t delete_nsec(dns_db_t *db, dns_dbversion_t *ver, static void zone_rekey(dns_zone_t *zone); static isc_boolean_t delsig_ok(dns_rdata_rrsig_t *rrsig_ptr, dst_key_t **keys, unsigned int nkeys); +static isc_result_t zone_send_securedb(dns_zone_t *zone, isc_boolean_t locked, + dns_db_t *db); #define ENTER zone_debuglog(zone, me, 1, "enter") @@ -798,6 +843,7 @@ dns_zone_create(dns_zone_t **zonep, isc_mem_t *mctx) { isc_time_settoepoch(&zone->signingtime); isc_time_settoepoch(&zone->nsec3chaintime); isc_time_settoepoch(&zone->refreshkeytime); + zone->refreshkeyinterval = 0; zone->refreshkeycount = 0; zone->refresh = DNS_ZONE_DEFAULTREFRESH; zone->retry = DNS_ZONE_DEFAULTRETRY; @@ -813,9 +859,11 @@ dns_zone_create(dns_zone_t **zonep, isc_mem_t *mctx) { zone->masterscnt = 0; zone->curmaster = 0; zone->notify = NULL; + zone->notifykeynames = NULL; zone->notifytype = dns_notifytype_yes; zone->notifycnt = 0; zone->task = NULL; + zone->loadtask = NULL; zone->update_acl = NULL; zone->forward_acl = NULL; zone->notify_acl = NULL; @@ -857,7 +905,9 @@ dns_zone_create(dns_zone_t **zonep, isc_mem_t *mctx) { zone->statelist = NULL; zone->stats = NULL; zone->requeststats_on = ISC_FALSE; + zone->statlevel = dns_zonestat_none; zone->requeststats = NULL; + zone->rcvquerystats = NULL; zone->notifydelay = 5; zone->isself = NULL; zone->isselfarg = NULL; @@ -869,6 +919,10 @@ dns_zone_create(dns_zone_t **zonep, isc_mem_t *mctx) { zone->added = ISC_FALSE; zone->is_rpz = ISC_FALSE; ISC_LIST_INIT(zone->forwards); + zone->raw = NULL; + zone->secure = NULL; + zone->sourceserial = 0; + zone->sourceserialset = ISC_FALSE; zone->magic = ZONE_MAGIC; @@ -925,6 +979,8 @@ zone_free(dns_zone_t *zone) { if (zone->task != NULL) isc_task_detach(&zone->task); + if (zone->loadtask != NULL) + isc_task_detach(&zone->loadtask); if (zone->zmgr != NULL) dns_zonemgr_releasezone(zone->zmgr, zone); @@ -959,6 +1015,8 @@ zone_free(dns_zone_t *zone) { isc_stats_detach(&zone->stats); if (zone->requeststats != NULL) isc_stats_detach(&zone->requeststats); + if(zone->rcvquerystats != NULL ) + dns_stats_detach(&zone->rcvquerystats); if (zone->db != NULL) zone_detachdb(zone); if (zone->acache != NULL) @@ -1004,6 +1062,30 @@ zone_free(dns_zone_t *zone) { isc_mem_detach(&mctx); } +/* + * Returns ISC_TRUE iff this the signed side of an inline-signing zone. + * Caller should hold zone lock. + */ +static inline isc_boolean_t +inline_secure(dns_zone_t *zone) { + REQUIRE(DNS_ZONE_VALID(zone)); + if (zone->raw != NULL) + return (ISC_TRUE); + return (ISC_FALSE); +} + +/* + * Returns ISC_TRUE iff this the unsigned side of an inline-signing zone + * Caller should hold zone lock. + */ +static inline isc_boolean_t +inline_raw(dns_zone_t *zone) { + REQUIRE(DNS_ZONE_VALID(zone)); + if (zone->secure != NULL) + return (ISC_TRUE); + return (ISC_FALSE); +} + /* * Single shot. */ @@ -1032,6 +1114,8 @@ dns_zone_setclass(dns_zone_t *zone, dns_rdataclass_t rdclass) { zone_rdclass_tostr(zone, namebuf, sizeof namebuf); zone->strrdclass = isc_mem_strdup(zone->mctx, namebuf); + if (inline_secure(zone)) + dns_zone_setclass(zone->raw, rdclass); UNLOCK_ZONE(zone); } @@ -1092,6 +1176,7 @@ dns_zone_getserial(dns_zone_t *zone) { */ void dns_zone_settype(dns_zone_t *zone, dns_zonetype_t type) { + char namebuf[1024]; REQUIRE(DNS_ZONE_VALID(zone)); REQUIRE(type != dns_zone_none); @@ -1102,6 +1187,12 @@ dns_zone_settype(dns_zone_t *zone, dns_zonetype_t type) { LOCK_ZONE(zone); REQUIRE(zone->type == dns_zone_none || zone->type == type); zone->type = type; + + if (zone->strnamerd != NULL) + isc_mem_free(zone->mctx, zone->strnamerd); + + zone_namerd_tostr(zone, namebuf, sizeof namebuf); + zone->strnamerd = isc_mem_strdup(zone->mctx, namebuf); UNLOCK_ZONE(zone); } @@ -1220,10 +1311,12 @@ dns_zone_setview(dns_zone_t *zone, dns_view_t *view) { zone_viewname_tostr(zone, namebuf, sizeof namebuf); zone->strviewname = isc_mem_strdup(zone->mctx, namebuf); + if (inline_secure(zone)) + dns_zone_setview(zone->raw, view); + UNLOCK_ZONE(zone); } - dns_view_t * dns_zone_getview(dns_zone_t *zone) { REQUIRE(DNS_ZONE_VALID(zone)); @@ -1257,6 +1350,8 @@ dns_zone_setorigin(dns_zone_t *zone, const dns_name_t *origin) { zone_name_tostr(zone, namebuf, sizeof namebuf); zone->strname = isc_mem_strdup(zone->mctx, namebuf); + if (result == ISC_R_SUCCESS && inline_secure(zone)) + result = dns_zone_setorigin(zone->raw, origin); UNLOCK_ZONE(zone); return (result); } @@ -1394,16 +1489,24 @@ dns_zone_getjournal(dns_zone_t *zone) { * allow dynamic updates either by having an update policy ("ssutable") * or an "allow-update" ACL with a value other than exactly "{ none; }". */ -static isc_boolean_t -zone_isdynamic(dns_zone_t *zone) { +isc_boolean_t +dns_zone_isdynamic(dns_zone_t *zone, isc_boolean_t ignore_freeze) { REQUIRE(DNS_ZONE_VALID(zone)); - return (ISC_TF(zone->type == dns_zone_slave || - zone->type == dns_zone_stub || - zone->type == dns_zone_key || - (!zone->update_disabled && zone->ssutable != NULL) || - (!zone->update_disabled && zone->update_acl != NULL && - !dns_acl_isnone(zone->update_acl)))); + if (zone->type == dns_zone_slave || zone->type == dns_zone_stub || + zone->type == dns_zone_key || + (zone->type == dns_zone_redirect && zone->masters != NULL)) + return (ISC_TRUE); + + /* If !ignore_freeze, we need check whether updates are disabled. */ + if (zone->type == dns_zone_master && + (!zone->update_disabled || ignore_freeze) && + ((zone->ssutable != NULL) || + (zone->update_acl != NULL && !dns_acl_isnone(zone->update_acl)))) + return (ISC_TRUE); + + return (ISC_FALSE); + } /* @@ -1437,11 +1540,21 @@ zone_load(dns_zone_t *zone, unsigned int flags) { isc_time_t now; isc_time_t loadtime, filetime; dns_db_t *db = NULL; - isc_boolean_t rbt; + isc_boolean_t rbt, hasraw; REQUIRE(DNS_ZONE_VALID(zone)); LOCK_ZONE(zone); + hasraw = inline_secure(zone); + if (hasraw) { + result = zone_load(zone->raw, flags); + if (result != ISC_R_SUCCESS) { + UNLOCK_ZONE(zone); + return(result); + } + LOCK_ZONE(zone->raw); + } + TIME_NOW(&now); INSIST(zone->type != dns_zone_none); @@ -1453,7 +1566,6 @@ zone_load(dns_zone_t *zone, unsigned int flags) { goto cleanup; } - INSIST(zone->db_argc >= 1); rbt = strcmp(zone->db_argv[0], "rbt") == 0 || @@ -1467,7 +1579,7 @@ zone_load(dns_zone_t *zone, unsigned int flags) { goto cleanup; } - if (zone->db != NULL && zone_isdynamic(zone)) { + if (zone->db != NULL && dns_zone_isdynamic(zone, ISC_FALSE)) { /* * This is a slave, stub, or dynamically updated * zone being reloaded. Do nothing - the database @@ -1531,7 +1643,8 @@ zone_load(dns_zone_t *zone, unsigned int flags) { goto cleanup; } - if ((zone->type == dns_zone_slave || zone->type == dns_zone_stub) && + if ((zone->type == dns_zone_slave || zone->type == dns_zone_stub || + (zone->type == dns_zone_redirect && zone->masters != NULL)) && rbt) { if (zone->masterfile == NULL || !isc_file_exists(zone->masterfile)) { @@ -1569,7 +1682,9 @@ zone_load(dns_zone_t *zone, unsigned int flags) { result = zone_startload(db, zone, loadtime); } else { result = DNS_R_NOMASTERFILE; - if (zone->type == dns_zone_master) { + if (zone->type == dns_zone_master || + (zone->type == dns_zone_redirect && + zone->masters == NULL)) { dns_zone_log(zone, ISC_LOG_ERROR, "loading zone: " "no master file configured"); @@ -1590,6 +1705,8 @@ zone_load(dns_zone_t *zone, unsigned int flags) { result = zone_postload(zone, db, loadtime, result); cleanup: + if (hasraw) + UNLOCK_ZONE(zone->raw); UNLOCK_ZONE(zone); if (db != NULL) dns_db_detach(&db); @@ -1606,17 +1723,100 @@ dns_zone_loadnew(dns_zone_t *zone) { return (zone_load(zone, DNS_ZONELOADFLAG_NOSTAT)); } +static void +zone_asyncload(isc_task_t *task, isc_event_t *event) { + dns_asyncload_t *asl = event->ev_arg; + dns_zone_t *zone = asl->zone; + isc_result_t result = ISC_R_SUCCESS; + + UNUSED(task); + + REQUIRE(DNS_ZONE_VALID(zone)); + + if ((event->ev_attributes & ISC_EVENTATTR_CANCELED) != 0) + result = ISC_R_CANCELED; + isc_event_free(&event); + if (result == ISC_R_CANCELED || + !DNS_ZONE_FLAG(zone, DNS_ZONEFLG_LOADPENDING)) + goto cleanup; + + zone_load(zone, 0); + + LOCK_ZONE(zone); + DNS_ZONE_CLRFLAG(zone, DNS_ZONEFLG_LOADPENDING); + UNLOCK_ZONE(zone); + + /* Inform the zone table we've finished loading */ + if (asl->loaded != NULL) + (asl->loaded)(asl->loaded_arg, zone, task); + + cleanup: + isc_mem_put(zone->mctx, asl, sizeof (*asl)); + dns_zone_idetach(&zone); +} + +isc_result_t +dns_zone_asyncload(dns_zone_t *zone, dns_zt_zoneloaded_t done, void *arg) { + isc_event_t *e; + dns_asyncload_t *asl = NULL; + isc_result_t result = ISC_R_SUCCESS; + + REQUIRE(DNS_ZONE_VALID(zone)); + + if (zone->zmgr == NULL) + return (ISC_R_FAILURE); + + asl = isc_mem_get(zone->mctx, sizeof (*asl)); + if (asl == NULL) + CHECK(ISC_R_NOMEMORY); + + asl->zone = NULL; + asl->loaded = done; + asl->loaded_arg = arg; + + e = isc_event_allocate(zone->zmgr->mctx, zone->zmgr, + DNS_EVENT_ZONELOAD, + zone_asyncload, asl, + sizeof(isc_event_t)); + if (e == NULL) + CHECK(ISC_R_NOMEMORY); + + LOCK_ZONE(zone); + zone_iattach(zone, &asl->zone); + DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_LOADPENDING); + isc_task_send(zone->loadtask, &e); + UNLOCK_ZONE(zone); + + return (ISC_R_SUCCESS); + + failure: + if (asl != NULL) + isc_mem_put(zone->mctx, asl, sizeof (*asl)); + return (result); +} + +isc_boolean_t +dns__zone_loadpending(dns_zone_t *zone) { + REQUIRE(DNS_ZONE_VALID(zone)); + + return (ISC_TF(DNS_ZONE_FLAG(zone, DNS_ZONEFLG_LOADPENDING))); +} + isc_result_t dns_zone_loadandthaw(dns_zone_t *zone) { isc_result_t result; - result = zone_load(zone, DNS_ZONELOADFLAG_THAW); + if (inline_raw(zone)) + result = zone_load(zone->secure, DNS_ZONELOADFLAG_THAW); + else + result = zone_load(zone, DNS_ZONELOADFLAG_THAW); + switch (result) { case DNS_R_CONTINUE: /* Deferred thaw. */ break; - case ISC_R_SUCCESS: case DNS_R_UPTODATE: + case ISC_R_SUCCESS: case DNS_R_SEENINCLUDE: zone->update_disabled = ISC_FALSE; break; @@ -1635,7 +1835,8 @@ get_master_options(dns_zone_t *zone) { unsigned int options; options = DNS_MASTER_ZONE; - if (zone->type == dns_zone_slave) + if (zone->type == dns_zone_slave || + (zone->type == dns_zone_redirect && zone->masters == NULL)) options |= DNS_MASTER_SLAVE; if (zone->type == dns_zone_key) options |= DNS_MASTER_KEY; @@ -1653,9 +1854,9 @@ get_master_options(dns_zone_t *zone) { options |= DNS_MASTER_CHECKMXFAIL; if (DNS_ZONE_OPTION(zone, DNS_ZONEOPT_CHECKWILDCARD)) options |= DNS_MASTER_CHECKWILDCARD; - if (zone->type == dns_zone_master && + if (inline_secure(zone) || (zone->type == dns_zone_master && ((zone->update_acl != NULL && !dns_acl_isnone(zone->update_acl)) || - zone->ssutable != NULL)) + zone->ssutable != NULL))) options |= DNS_MASTER_RESIGN; return (options); } @@ -1679,8 +1880,7 @@ zone_gotreadhandle(isc_task_t *task, isc_event_t *event) { result = dns_master_loadfileinc3(load->zone->masterfile, dns_db_origin(load->db), dns_db_origin(load->db), - load->zone->rdclass, - options, + load->zone->rdclass, options, load->zone->sigresigninginterval, &load->callbacks, task, zone_loaddone, load, @@ -1695,12 +1895,30 @@ zone_gotreadhandle(isc_task_t *task, isc_event_t *event) { zone_loaddone(load, result); } +static void +get_raw_serial(dns_zone_t *raw, dns_masterrawheader_t *rawdata) { + isc_result_t result; + unsigned int soacount; + + LOCK(&raw->lock); + if (raw->db != NULL) { + result = zone_get_from_db(raw, raw->db, NULL, &soacount, + &rawdata->sourceserial, + NULL, NULL, NULL, NULL, + NULL); + if (result == ISC_R_SUCCESS && soacount > 0U) + rawdata->flags |= DNS_MASTERRAW_SOURCESERIALSET; + } + UNLOCK(&raw->lock); +} + static void zone_gotwritehandle(isc_task_t *task, isc_event_t *event) { const char me[] = "zone_gotwritehandle"; dns_zone_t *zone = event->ev_arg; isc_result_t result = ISC_R_SUCCESS; dns_dbversion_t *version = NULL; + dns_masterrawheader_t rawdata; REQUIRE(DNS_ZONE_VALID(zone)); INSIST(task == zone->task); @@ -1716,11 +1934,14 @@ zone_gotwritehandle(isc_task_t *task, isc_event_t *event) { ZONEDB_LOCK(&zone->dblock, isc_rwlocktype_read); if (zone->db != NULL) { dns_db_currentversion(zone->db, &version); - result = dns_master_dumpinc2(zone->mctx, zone->db, version, + dns_master_initrawheader(&rawdata); + if (inline_secure(zone)) + get_raw_serial(zone->raw, &rawdata); + result = dns_master_dumpinc3(zone->mctx, zone->db, version, &dns_master_style_default, zone->masterfile, zone->task, dump_done, zone, &zone->dctx, - zone->masterformat); + zone->masterformat, &rawdata); dns_db_closeversion(zone->db, &version, ISC_FALSE); } else result = ISC_R_CANCELED; @@ -1734,6 +1955,31 @@ zone_gotwritehandle(isc_task_t *task, isc_event_t *event) { dump_done(zone, result); } +/* + * Save the raw serial number for inline-signing zones. + * (XXX: Other information from the header will be used + * for other purposes in the future, but for now this is + * all we're interested in.) + */ +static void +zone_setrawdata(dns_zone_t *zone, dns_masterrawheader_t *header) { + if ((header->flags & DNS_MASTERRAW_SOURCESERIALSET) == 0) + return; + + zone->sourceserial = header->sourceserial; + zone->sourceserialset = ISC_TRUE; +} + +void +dns_zone_setrawdata(dns_zone_t *zone, dns_masterrawheader_t *header) { + if (zone == NULL) + return; + + LOCK_ZONE(zone); + zone_setrawdata(zone, header); + UNLOCK_ZONE(zone); +} + static isc_result_t zone_startload(dns_db_t *db, dns_zone_t *zone, isc_time_t loadtime) { dns_load_t *load; @@ -1753,7 +1999,7 @@ zone_startload(dns_db_t *db, dns_zone_t *zone, isc_time_t loadtime) { if (DNS_ZONE_OPTION(zone, DNS_ZONEOPT_MANYERRORS)) options |= DNS_MASTER_MANYERRORS; - if (zone->zmgr != NULL && zone->db != NULL && zone->task != NULL) { + if (zone->zmgr != NULL && zone->db != NULL && zone->loadtask != NULL) { load = isc_mem_get(zone->mctx, sizeof(*load)); if (load == NULL) return (ISC_R_NOMEMORY); @@ -1768,11 +2014,13 @@ zone_startload(dns_db_t *db, dns_zone_t *zone, isc_time_t loadtime) { zone_iattach(zone, &load->zone); dns_db_attach(db, &load->db); dns_rdatacallbacks_init(&load->callbacks); + load->callbacks.rawdata = zone_setrawdata; + zone_iattach(zone, &load->callbacks.zone); result = dns_db_beginload(db, &load->callbacks.add, &load->callbacks.add_private); if (result != ISC_R_SUCCESS) goto cleanup; - result = zonemgr_getio(zone->zmgr, ISC_TRUE, zone->task, + result = zonemgr_getio(zone->zmgr, ISC_TRUE, zone->loadtask, zone_gotreadhandle, load, &zone->readio); if (result != ISC_R_SUCCESS) { @@ -1789,18 +2037,24 @@ zone_startload(dns_db_t *db, dns_zone_t *zone, isc_time_t loadtime) { dns_rdatacallbacks_t callbacks; dns_rdatacallbacks_init(&callbacks); + callbacks.rawdata = zone_setrawdata; + zone_iattach(zone, &callbacks.zone); result = dns_db_beginload(db, &callbacks.add, &callbacks.add_private); - if (result != ISC_R_SUCCESS) + if (result != ISC_R_SUCCESS) { + zone_idetach(&callbacks.zone); return (result); - result = dns_master_loadfile3(zone->masterfile, &zone->origin, - &zone->origin, zone->rdclass, - options, zone->sigresigninginterval, + } + result = dns_master_loadfile3(zone->masterfile, + &zone->origin, &zone->origin, + zone->rdclass, options, + zone->sigresigninginterval, &callbacks, zone->mctx, zone->masterformat); tresult = dns_db_endload(db, &callbacks.add_private); if (result == ISC_R_SUCCESS) result = tresult; + zone_idetach(&callbacks.zone); } return (result); @@ -1809,6 +2063,7 @@ zone_startload(dns_db_t *db, dns_zone_t *zone, isc_time_t loadtime) { load->magic = 0; dns_db_detach(&load->db); zone_idetach(&load->zone); + zone_idetach(&load->callbacks.zone); isc_mem_detach(&load->mctx); isc_mem_put(zone->mctx, load, sizeof(*load)); return (result); @@ -2531,13 +2786,22 @@ resume_signingwithkey(dns_zone_t *zone) { static isc_result_t zone_addnsec3chain(dns_zone_t *zone, dns_rdata_nsec3param_t *nsec3param) { dns_nsec3chain_t *nsec3chain, *current; + dns_dbversion_t *version = NULL; + isc_boolean_t nseconly = ISC_FALSE, nsec3ok = ISC_FALSE; isc_result_t result; isc_time_t now; unsigned int options = 0; char saltbuf[255*2+1]; - char flags[sizeof("REMOVE|CREATE|NONSEC|OPTOUT")]; + char flags[sizeof("INITIAL|REMOVE|CREATE|NONSEC|OPTOUT")]; int i; + dns_db_currentversion(zone->db, &version); + result = dns_nsec_nseconly(zone->db, version, &nseconly); + nsec3ok = (result == ISC_R_SUCCESS && !nseconly); + dns_db_closeversion(zone->db, &version, ISC_FALSE); + if (!nsec3ok && (nsec3param->flags & DNS_NSEC3FLAG_REMOVE) == 0) + return (ISC_R_SUCCESS); + nsec3chain = isc_mem_get(zone->mctx, sizeof *nsec3chain); if (nsec3chain == NULL) return (ISC_R_NOMEMORY); @@ -2564,6 +2828,12 @@ zone_addnsec3chain(dns_zone_t *zone, dns_rdata_nsec3param_t *nsec3param) { flags[0] = '\0'; if (nsec3param->flags & DNS_NSEC3FLAG_REMOVE) strlcat(flags, "REMOVE", sizeof(flags)); + if (nsec3param->flags & DNS_NSEC3FLAG_INITIAL) { + if (flags[0] == '\0') + strlcpy(flags, "INITIAL", sizeof(flags)); + else + strlcat(flags, "|INITIAL", sizeof(flags)); + } if (nsec3param->flags & DNS_NSEC3FLAG_CREATE) { if (flags[0] == '\0') strlcpy(flags, "CREATE", sizeof(flags)); @@ -2592,6 +2862,7 @@ zone_addnsec3chain(dns_zone_t *zone, dns_rdata_nsec3param_t *nsec3param) { "zone_addnsec3chain(%u,%s,%u,%s)", nsec3param->hash, flags, nsec3param->iterations, saltbuf); + for (current = ISC_LIST_HEAD(zone->nsec3chain); current != NULL; current = ISC_LIST_NEXT(current, link)) { @@ -2644,6 +2915,7 @@ resume_addnsec3chain(dns_zone_t *zone) { dns_rdataset_t rdataset; isc_result_t result; dns_rdata_nsec3param_t nsec3param; + isc_boolean_t nseconly = ISC_FALSE, nsec3ok = ISC_FALSE; if (zone->privatetype == 0) return; @@ -2653,6 +2925,10 @@ resume_addnsec3chain(dns_zone_t *zone) { goto cleanup; dns_db_currentversion(zone->db, &version); + + result = dns_nsec_nseconly(zone->db, version, &nseconly); + nsec3ok = (result == ISC_R_SUCCESS && !nseconly); + dns_rdataset_init(&rdataset); result = dns_db_findrdataset(zone->db, node, version, zone->privatetype, dns_rdatatype_none, @@ -2676,8 +2952,9 @@ resume_addnsec3chain(dns_zone_t *zone) { continue; result = dns_rdata_tostruct(&rdata, &nsec3param, NULL); RUNTIME_CHECK(result == ISC_R_SUCCESS); - if ((nsec3param.flags & DNS_NSEC3FLAG_CREATE) != 0 || - (nsec3param.flags & DNS_NSEC3FLAG_REMOVE) != 0) { + if (((nsec3param.flags & DNS_NSEC3FLAG_REMOVE) != 0) || + ((nsec3param.flags & DNS_NSEC3FLAG_CREATE) != 0 && nsec3ok)) + { result = zone_addnsec3chain(zone, &nsec3param); if (result != ISC_R_SUCCESS) { dns_zone_log(zone, ISC_LOG_ERROR, @@ -2727,7 +3004,7 @@ check_nsec3param(dns_zone_t *zone, dns_db_t *db) { isc_result_t result; dns_rdata_t rdata = DNS_RDATA_INIT; isc_boolean_t dynamic = (zone->type == dns_zone_master) ? - zone_isdynamic(zone) : ISC_FALSE; + dns_zone_isdynamic(zone, ISC_FALSE) : ISC_FALSE; dns_rdataset_init(&rdataset); result = dns_db_findnode(db, &zone->origin, ISC_FALSE, &node); @@ -3149,8 +3426,8 @@ update_one_rr(dns_db_t *db, dns_dbversion_t *ver, dns_diff_t *diff, } static isc_result_t -increment_soa_serial(dns_db_t *db, dns_dbversion_t *ver, - dns_diff_t *diff, isc_mem_t *mctx) { +update_soa_serial(dns_db_t *db, dns_dbversion_t *ver, dns_diff_t *diff, + isc_mem_t *mctx, dns_updatemethod_t method) { dns_difftuple_t *deltuple = NULL; dns_difftuple_t *addtuple = NULL; isc_uint32_t serial; @@ -3161,12 +3438,7 @@ increment_soa_serial(dns_db_t *db, dns_dbversion_t *ver, addtuple->op = DNS_DIFFOP_ADD; serial = dns_soa_getserial(&addtuple->rdata); - - /* RFC1982 */ - serial = (serial + 1) & 0xFFFFFFFF; - if (serial == 0) - serial = 1; - + serial = dns_update_soaserial(serial, method); dns_soa_setserial(serial, &addtuple->rdata); CHECK(do_one_tuple(&deltuple, db, ver, diff)); CHECK(do_one_tuple(&addtuple, db, ver, diff)); @@ -3184,17 +3456,20 @@ increment_soa_serial(dns_db_t *db, dns_dbversion_t *ver, * Write all transactions in 'diff' to the zone journal file. */ static isc_result_t -zone_journal(dns_zone_t *zone, dns_diff_t *diff, const char *caller) { +zone_journal(dns_zone_t *zone, dns_diff_t *diff, isc_uint32_t *sourceserial, + const char *caller) +{ const char me[] = "zone_journal"; const char *journalfile; isc_result_t result = ISC_R_SUCCESS; dns_journal_t *journal = NULL; + unsigned int mode = DNS_JOURNAL_CREATE|DNS_JOURNAL_WRITE; ENTER; journalfile = dns_zone_getjournal(zone); if (journalfile != NULL) { - result = dns_journal_open(zone->mctx, journalfile, - ISC_TRUE, &journal); + result = dns_journal_open(zone->mctx, journalfile, mode, + &journal); if (result != ISC_R_SUCCESS) { dns_zone_log(zone, ISC_LOG_ERROR, "%s:dns_journal_open -> %s", @@ -3202,15 +3477,18 @@ zone_journal(dns_zone_t *zone, dns_diff_t *diff, const char *caller) { return (result); } + if (sourceserial != NULL) + dns_journal_set_sourceserial(journal, *sourceserial); + result = dns_journal_write_transaction(journal, diff); - dns_journal_destroy(&journal); if (result != ISC_R_SUCCESS) { dns_zone_log(zone, ISC_LOG_ERROR, "%s:dns_journal_write_transaction -> %s", caller, dns_result_totext(result)); - return (result); } + dns_journal_destroy(&journal); } + return (result); } @@ -3391,8 +3669,9 @@ sync_keyzone(dns_zone_t *zone, dns_db_t *db) { if (changed) { /* Write changes to journal file. */ - CHECK(increment_soa_serial(db, ver, &diff, zone->mctx)); - CHECK(zone_journal(zone, &diff, "sync_keyzone")); + CHECK(update_soa_serial(db, ver, &diff, zone->mctx, + zone->updatemethod)); + CHECK(zone_journal(zone, &diff, NULL, "sync_keyzone")); DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_LOADED); zone_needdump(zone, 30); @@ -3418,6 +3697,73 @@ sync_keyzone(dns_zone_t *zone, dns_db_t *db) { return (result); } +isc_result_t +dns_zone_synckeyzone(dns_zone_t *zone) { + isc_result_t result; + dns_db_t *db = NULL; + + if (zone->type != dns_zone_key) + return (DNS_R_BADZONE); + + CHECK(dns_zone_getdb(zone, &db)); + + LOCK_ZONE(zone); + result = sync_keyzone(zone, db); + UNLOCK_ZONE(zone); + + failure: + if (db != NULL) + dns_db_detach(&db); + return (result); +} + +static void +maybe_send_secure(dns_zone_t *zone) { + isc_result_t result; + + /* + * We've finished loading, or else failed to load, an inline-signing + * 'secure' zone. We now need information about the status of the + * 'raw' zone. If we failed to load, then we need it to send a + * copy of its database; if we succeeded, we need it to send its + * serial number so that we can sync with it. If it has not yet + * loaded, we set a flag so that it will send the necessary + * information when it has finished loading. + */ + if (zone->raw->db != NULL) { + if (zone->db != NULL) { + isc_uint32_t serial; + unsigned int soacount; + + result = zone_get_from_db(zone->raw, zone->raw->db, + NULL, &soacount, &serial, NULL, + NULL, NULL, NULL, NULL); + if (result == ISC_R_SUCCESS && soacount > 0U) + zone_send_secureserial(zone->raw, ISC_TRUE, serial); + } else + zone_send_securedb(zone->raw, ISC_TRUE, zone->raw->db); + + } else + DNS_ZONE_SETFLAG(zone->raw, DNS_ZONEFLG_SENDSECURE); +} + +static isc_boolean_t +zone_unchanged(dns_db_t *db1, dns_db_t *db2, isc_mem_t *mctx) { + isc_result_t result; + isc_boolean_t answer = ISC_FALSE; + dns_diff_t diff; + + dns_diff_init(mctx, &diff); + result = dns_db_diffx(&diff, db1, NULL, db2, NULL, NULL); + if (result == ISC_R_SUCCESS && ISC_LIST_EMPTY(diff.tuples)) + answer = ISC_TRUE; + dns_diff_clear(&diff); + return (answer); +} + +/* + * The zone is presumed to be locked. + */ static isc_result_t zone_postload(dns_zone_t *zone, dns_db_t *db, isc_time_t loadtime, isc_result_t result) @@ -3441,7 +3787,9 @@ zone_postload(dns_zone_t *zone, dns_db_t *db, isc_time_t loadtime, */ if (result != ISC_R_SUCCESS && result != DNS_R_SEENINCLUDE) { if (zone->type == dns_zone_slave || - zone->type == dns_zone_stub) { + zone->type == dns_zone_stub || + (zone->type == dns_zone_redirect && + zone->masters == NULL)) { if (result == ISC_R_FILENOTFOUND) dns_zone_log(zone, ISC_LOG_DEBUG(1), "no master file"); @@ -3451,6 +3799,12 @@ zone_postload(dns_zone_t *zone, dns_db_t *db, isc_time_t loadtime, "failed: %s", zone->masterfile, dns_result_totext(result)); + } else if (zone->type == dns_zone_master && + inline_secure(zone) && result == ISC_R_FILENOTFOUND) + { + dns_zone_log(zone, ISC_LOG_DEBUG(1), + "no master file, requesting db"); + maybe_send_secure(zone); } else { int level = ISC_LOG_ERROR; if (zone->type == dns_zone_key && @@ -3510,6 +3864,8 @@ zone_postload(dns_zone_t *zone, dns_db_t *db, isc_time_t loadtime, "journal rollforward failed: %s", dns_result_totext(result)); goto cleanup; + + } if (result == ISC_R_NOTFOUND || result == ISC_R_RANGE) { dns_zone_log(zone, ISC_LOG_ERROR, @@ -3525,7 +3881,6 @@ zone_postload(dns_zone_t *zone, dns_db_t *db, isc_time_t loadtime, needdump = ISC_TRUE; } - dns_zone_log(zone, ISC_LOG_DEBUG(1), "loaded; checking validity"); /* * Obtain ns, soa and cname counts for top of zone. */ @@ -3538,6 +3893,46 @@ zone_postload(dns_zone_t *zone, dns_db_t *db, isc_time_t loadtime, "could not find NS and/or SOA records"); } + /* + * Check to make sure the journal is up to date, and remove the + * journal file if it isn't, as we wouldn't be able to apply + * updates otherwise. + */ + if (zone->journal != NULL && dns_zone_isdynamic(zone, ISC_TRUE) && + ! DNS_ZONE_OPTION(zone, DNS_ZONEOPT_IXFRFROMDIFFS)) { + isc_uint32_t jserial; + dns_journal_t *journal = NULL; + + result = dns_journal_open(zone->mctx, zone->journal, + DNS_JOURNAL_READ, &journal); + if (result == ISC_R_SUCCESS) { + jserial = dns_journal_last_serial(journal); + dns_journal_destroy(&journal); + } else { + jserial = serial; + result = ISC_R_SUCCESS; + } + + if (jserial != serial) { + dns_zone_log(zone, ISC_LOG_INFO, + "journal file is out of date: " + "removing journal file"); + if (remove(zone->journal) < 0 && errno != ENOENT) { + char strbuf[ISC_STRERRORSIZE]; + isc__strerror(errno, strbuf, sizeof(strbuf)); + isc_log_write(dns_lctx, + DNS_LOGCATEGORY_GENERAL, + DNS_LOGMODULE_ZONE, + ISC_LOG_WARNING, + "unable to remove journal " + "'%s': '%s'", + zone->journal, strbuf); + } + } + } + + dns_zone_log(zone, ISC_LOG_DEBUG(1), "loaded; checking validity"); + /* * Master / Slave / Stub zones require both NS and SOA records at * the top of the zone. @@ -3548,6 +3943,7 @@ zone_postload(dns_zone_t *zone, dns_db_t *db, isc_time_t loadtime, case dns_zone_master: case dns_zone_slave: case dns_zone_stub: + case dns_zone_redirect: if (soacount != 1) { dns_zone_log(zone, ISC_LOG_ERROR, "has %d SOA records", soacount); @@ -3564,7 +3960,8 @@ zone_postload(dns_zone_t *zone, dns_db_t *db, isc_time_t loadtime, result = DNS_R_BADZONE; goto cleanup; } - if (zone->type != dns_zone_stub) { + if (zone->type != dns_zone_stub && + zone->type != dns_zone_redirect) { result = check_nsec3param(zone, db); if (result != ISC_R_SUCCESS) goto cleanup; @@ -3575,7 +3972,6 @@ zone_postload(dns_zone_t *zone, dns_db_t *db, isc_time_t loadtime, result = DNS_R_BADZONE; goto cleanup; } - if (zone->type == dns_zone_master && DNS_ZONE_OPTION(zone, DNS_ZONEOPT_CHECKDUPRR) && !zone_check_dup(zone, db)) { @@ -3602,6 +3998,14 @@ zone_postload(dns_zone_t *zone, dns_db_t *db, isc_time_t loadtime, INSIST(zone->type == dns_zone_master); + if (serial == oldserial && + zone_unchanged(zone->db, db, zone->mctx)) { + dns_zone_log(zone, ISC_LOG_INFO, + "ixfr-from-differences: " + "unchanged"); + return(ISC_R_SUCCESS); + } + serialmin = (oldserial + 1) & 0xffffffffU; serialmax = (oldserial + 0x7fffffffU) & 0xffffffffU; @@ -3644,7 +4048,9 @@ zone_postload(dns_zone_t *zone, dns_db_t *db, isc_time_t loadtime, DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_HAVETIMERS); if (zone->type == dns_zone_slave || - zone->type == dns_zone_stub) { + zone->type == dns_zone_stub || + (zone->type == dns_zone_redirect && + zone->masters != NULL)) { isc_time_t t; isc_uint32_t delay; @@ -3666,6 +4072,7 @@ zone_postload(dns_zone_t *zone, dns_db_t *db, isc_time_t loadtime, &zone->expiretime) >= 0) zone->refreshtime = now; } + break; case dns_zone_key: @@ -3717,8 +4124,24 @@ zone_postload(dns_zone_t *zone, dns_db_t *db, isc_time_t loadtime, ZONEDB_UNLOCK(&zone->dblock, isc_rwlocktype_write); DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_LOADED|DNS_ZONEFLG_NEEDNOTIFY); + if (DNS_ZONE_FLAG(zone, DNS_ZONEFLG_SENDSECURE) && + inline_raw(zone)) + { + if (zone->secure->db == NULL) + zone_send_securedb(zone, ISC_FALSE, db); + else + zone_send_secureserial(zone, ISC_FALSE, serial); + } } + /* + * Finished loading inline-signing zone; need to get status + * from the raw side now. + */ + if (zone->type == dns_zone_master && inline_secure(zone)) + maybe_send_secure(zone); + + result = ISC_R_SUCCESS; if (needdump) { @@ -3736,7 +4159,8 @@ zone_postload(dns_zone_t *zone, dns_db_t *db, isc_time_t loadtime, } if (zone->type == dns_zone_master && - zone_isdynamic(zone) && + !DNS_ZONEKEY_OPTION(zone, DNS_ZONEKEY_NORESIGN) && + dns_zone_isdynamic(zone, ISC_FALSE) && dns_db_issecure(db)) { dns_name_t *name; dns_fixedname_t fixed; @@ -3775,12 +4199,14 @@ zone_postload(dns_zone_t *zone, dns_db_t *db, isc_time_t loadtime, dns_db_issecure(db) ? " (DNSSEC signed)" : ""); zone->loadtime = loadtime; + DNS_ZONE_CLRFLAG(zone, DNS_ZONEFLG_LOADPENDING); return (result); cleanup: if (zone->type == dns_zone_slave || zone->type == dns_zone_stub || - zone->type == dns_zone_key) { + zone->type == dns_zone_key || + (zone->type == dns_zone_redirect && zone->masters != NULL)) { if (zone->journal != NULL) zone_saveunique(zone, zone->journal, "jn-XXXXXXXX"); if (zone->masterfile != NULL) @@ -3791,8 +4217,15 @@ zone_postload(dns_zone_t *zone, dns_db_t *db, isc_time_t loadtime, if (zone->task != NULL) zone_settimer(zone, &now); result = ISC_R_SUCCESS; - } else if (zone->type == dns_zone_master) - dns_zone_log(zone, ISC_LOG_ERROR, "not loaded due to errors."); + } else if (zone->type == dns_zone_master || + zone->type == dns_zone_redirect) { + if (!(inline_secure(zone) && result == ISC_R_FILENOTFOUND)) + dns_zone_log(zone, ISC_LOG_ERROR, + "not loaded due to errors."); + else if (zone->type == dns_zone_master) + result = ISC_R_SUCCESS; + } + return (result); } @@ -4087,6 +4520,8 @@ dns_zone_attach(dns_zone_t *source, dns_zone_t **target) { void dns_zone_detach(dns_zone_t **zonep) { dns_zone_t *zone; + dns_zone_t *raw = NULL; + dns_zone_t *secure = NULL; unsigned int refs; isc_boolean_t free_now = ISC_FALSE; @@ -4124,12 +4559,21 @@ dns_zone_detach(dns_zone_t **zonep) { */ INSIST(zone->view == NULL); free_now = ISC_TRUE; + raw = zone->raw; + zone->raw = NULL; + secure = zone->secure; + zone->secure = NULL; } UNLOCK_ZONE(zone); } *zonep = NULL; - if (free_now) + if (free_now) { + if (raw != NULL) + dns_zone_detach(&raw); + if (secure != NULL) + dns_zone_idetach(&secure); zone_free(zone); + } } void @@ -4141,26 +4585,6 @@ dns_zone_iattach(dns_zone_t *source, dns_zone_t **target) { UNLOCK_ZONE(source); } -isc_result_t -dns_zone_synckeyzone(dns_zone_t *zone) { - isc_result_t result; - dns_db_t *db = NULL; - - if (zone->type != dns_zone_key) - return (DNS_R_BADZONE); - - CHECK(dns_zone_getdb(zone, &db)); - - LOCK_ZONE(zone); - result = sync_keyzone(zone, db); - UNLOCK_ZONE(zone); - - failure: - if (db != NULL) - dns_db_detach(&db); - return (result); -} - static void zone_iattach(dns_zone_t *source, dns_zone_t **target) { @@ -4385,48 +4809,8 @@ dns_zone_getnotifysrc6(dns_zone_t *zone) { return (&zone->notifysrc6); } -isc_result_t -dns_zone_setalsonotify(dns_zone_t *zone, const isc_sockaddr_t *notify, - isc_uint32_t count) -{ - isc_sockaddr_t *new; - - REQUIRE(DNS_ZONE_VALID(zone)); - REQUIRE(count == 0 || notify != NULL); - - LOCK_ZONE(zone); - if (zone->notify != NULL) { - isc_mem_put(zone->mctx, zone->notify, - zone->notifycnt * sizeof(*new)); - zone->notify = NULL; - zone->notifycnt = 0; - } - if (count != 0) { - new = isc_mem_get(zone->mctx, count * sizeof(*new)); - if (new == NULL) { - UNLOCK_ZONE(zone); - return (ISC_R_NOMEMORY); - } - memcpy(new, notify, count * sizeof(*new)); - zone->notify = new; - zone->notifycnt = count; - } - UNLOCK_ZONE(zone); - return (ISC_R_SUCCESS); -} - -isc_result_t -dns_zone_setmasters(dns_zone_t *zone, const isc_sockaddr_t *masters, - isc_uint32_t count) -{ - isc_result_t result; - - result = dns_zone_setmasterswithkeys(zone, masters, NULL, count); - return (result); -} - static isc_boolean_t -same_masters(const isc_sockaddr_t *old, const isc_sockaddr_t *new, +same_addrs(const isc_sockaddr_t *old, const isc_sockaddr_t *new, isc_uint32_t count) { unsigned int i; @@ -4456,15 +4840,170 @@ same_keynames(dns_name_t **old, dns_name_t **new, isc_uint32_t count) { return (ISC_TRUE); } +static void +clear_addresskeylist(isc_sockaddr_t **addrsp, dns_name_t ***keynamesp, + unsigned int *countp, isc_mem_t *mctx) +{ + unsigned int count; + isc_sockaddr_t *addrs; + dns_name_t **keynames; + + REQUIRE(countp != NULL && addrsp != NULL && keynamesp != NULL); + + count = *countp; + *countp = 0; + addrs = *addrsp; + *addrsp = NULL; + keynames = *keynamesp; + *keynamesp = NULL; + + if (addrs != NULL) + isc_mem_put(mctx, addrs, count * sizeof(isc_sockaddr_t)); + + if (keynames != NULL) { + unsigned int i; + for (i = 0; i < count; i++) { + if (keynames[i] != NULL) { + dns_name_free(keynames[i], mctx); + isc_mem_put(mctx, keynames[i], + sizeof(dns_name_t)); + keynames[i] = NULL; + } + } + isc_mem_put(mctx, keynames, count * sizeof(dns_name_t *)); + } +} + +static isc_result_t +set_addrkeylist(unsigned int count, + const isc_sockaddr_t *addrs, isc_sockaddr_t **newaddrsp, + dns_name_t **names, dns_name_t ***newnamesp, + isc_mem_t *mctx) +{ + isc_result_t result; + isc_sockaddr_t *newaddrs = NULL; + dns_name_t **newnames = NULL; + unsigned int i; + + REQUIRE(newaddrsp != NULL && *newaddrsp == NULL); + REQUIRE(newnamesp != NULL && *newnamesp == NULL); + + newaddrs = isc_mem_get(mctx, count * sizeof(*newaddrs)); + if (newaddrs == NULL) + return (ISC_R_NOMEMORY); + memcpy(newaddrs, addrs, count * sizeof(*newaddrs)); + + newnames = NULL; + if (names != NULL) { + newnames = isc_mem_get(mctx, count * sizeof(*newnames)); + if (newnames == NULL) { + isc_mem_put(mctx, newaddrs, count * sizeof(*newaddrs)); + return (ISC_R_NOMEMORY); + } + for (i = 0; i < count; i++) + newnames[i] = NULL; + for (i = 0; i < count; i++) { + if (names[i] != NULL) { + newnames[i] = isc_mem_get(mctx, + sizeof(dns_name_t)); + if (newnames[i] == NULL) + goto allocfail; + dns_name_init(newnames[i], NULL); + result = dns_name_dup(names[i], mctx, + newnames[i]); + if (result != ISC_R_SUCCESS) { + allocfail: + for (i = 0; i < count; i++) + if (newnames[i] != NULL) + dns_name_free( + newnames[i], + mctx); + isc_mem_put(mctx, newaddrs, + count * sizeof(*newaddrs)); + isc_mem_put(mctx, newnames, + count * sizeof(*newnames)); + return (ISC_R_NOMEMORY); + } + } + } + } + + *newaddrsp = newaddrs; + *newnamesp = newnames; + return (ISC_R_SUCCESS); +} + +isc_result_t +dns_zone_setalsonotify(dns_zone_t *zone, const isc_sockaddr_t *notify, + isc_uint32_t count) +{ + return (dns_zone_setalsonotifywithkeys(zone, notify, NULL, count)); +} + +isc_result_t +dns_zone_setalsonotifywithkeys(dns_zone_t *zone, const isc_sockaddr_t *notify, + dns_name_t **keynames, isc_uint32_t count) +{ + isc_result_t result; + isc_sockaddr_t *newaddrs = NULL; + dns_name_t **newnames = NULL; + + REQUIRE(DNS_ZONE_VALID(zone)); + REQUIRE(count == 0 || notify != NULL); + if (keynames != NULL) + REQUIRE(count != 0); + + LOCK_ZONE(zone); + + if (count == zone->notifycnt && + same_addrs(zone->notify, notify, count) && + same_keynames(zone->notifykeynames, keynames, count)) + goto unlock; + + clear_addresskeylist(&zone->notify, &zone->notifykeynames, + &zone->notifycnt, zone->mctx); + + if (count == 0) + goto unlock; + + /* + * Set up the notify and notifykey lists + */ + result = set_addrkeylist(count, notify, &newaddrs, + keynames, &newnames, zone->mctx); + if (result != ISC_R_SUCCESS) + goto unlock; + + /* + * Everything is ok so attach to the zone. + */ + zone->notify = newaddrs; + zone->notifykeynames = newnames; + zone->notifycnt = count; + unlock: + UNLOCK_ZONE(zone); + return (ISC_R_SUCCESS); +} + +isc_result_t +dns_zone_setmasters(dns_zone_t *zone, const isc_sockaddr_t *masters, + isc_uint32_t count) +{ + isc_result_t result; + + result = dns_zone_setmasterswithkeys(zone, masters, NULL, count); + return (result); +} + isc_result_t dns_zone_setmasterswithkeys(dns_zone_t *zone, const isc_sockaddr_t *masters, dns_name_t **keynames, isc_uint32_t count) { - isc_sockaddr_t *new; isc_result_t result = ISC_R_SUCCESS; - dns_name_t **newname; + isc_sockaddr_t *newaddrs = NULL; + dns_name_t **newnames = NULL; isc_boolean_t *newok; unsigned int i; @@ -4482,38 +5021,24 @@ dns_zone_setmasterswithkeys(dns_zone_t *zone, * unlock and exit. */ if (count != zone->masterscnt || - !same_masters(zone->masters, masters, count) || + !same_addrs(zone->masters, masters, count) || !same_keynames(zone->masterkeynames, keynames, count)) { if (zone->request != NULL) dns_request_cancel(zone->request); } else goto unlock; - if (zone->masters != NULL) { - isc_mem_put(zone->mctx, zone->masters, - zone->masterscnt * sizeof(*new)); - zone->masters = NULL; - } - if (zone->masterkeynames != NULL) { - for (i = 0; i < zone->masterscnt; i++) { - if (zone->masterkeynames[i] != NULL) { - dns_name_free(zone->masterkeynames[i], - zone->mctx); - isc_mem_put(zone->mctx, - zone->masterkeynames[i], - sizeof(dns_name_t)); - zone->masterkeynames[i] = NULL; - } - } - isc_mem_put(zone->mctx, zone->masterkeynames, - zone->masterscnt * sizeof(dns_name_t *)); - zone->masterkeynames = NULL; - } + + /* + * This needs to happen before clear_addresskeylist() sets + * zone->masterscnt to 0: + */ if (zone->mastersok != NULL) { isc_mem_put(zone->mctx, zone->mastersok, zone->masterscnt * sizeof(isc_boolean_t)); zone->mastersok = NULL; } - zone->masterscnt = 0; + clear_addresskeylist(&zone->masters, &zone->masterkeynames, + &zone->masterscnt, zone->mctx); /* * If count == 0, don't allocate any space for masters, mastersok or * keynames so internally, those pointers are NULL if count == 0 @@ -4522,76 +5047,34 @@ dns_zone_setmasterswithkeys(dns_zone_t *zone, goto unlock; /* - * masters must contain count elements! - */ - new = isc_mem_get(zone->mctx, count * sizeof(*new)); - if (new == NULL) { - result = ISC_R_NOMEMORY; - goto unlock; - } - memcpy(new, masters, count * sizeof(*new)); - - /* - * Similarly for mastersok. + * mastersok must contain count elements */ newok = isc_mem_get(zone->mctx, count * sizeof(*newok)); if (newok == NULL) { result = ISC_R_NOMEMORY; - isc_mem_put(zone->mctx, new, count * sizeof(*new)); + isc_mem_put(zone->mctx, newaddrs, count * sizeof(*newaddrs)); goto unlock; }; for (i = 0; i < count; i++) newok[i] = ISC_FALSE; /* - * if keynames is non-NULL, it must contain count elements! + * Now set up the masters and masterkey lists */ - newname = NULL; - if (keynames != NULL) { - newname = isc_mem_get(zone->mctx, count * sizeof(*newname)); - if (newname == NULL) { - result = ISC_R_NOMEMORY; - isc_mem_put(zone->mctx, new, count * sizeof(*new)); - isc_mem_put(zone->mctx, newok, count * sizeof(*newok)); - goto unlock; - } - for (i = 0; i < count; i++) - newname[i] = NULL; - for (i = 0; i < count; i++) { - if (keynames[i] != NULL) { - newname[i] = isc_mem_get(zone->mctx, - sizeof(dns_name_t)); - if (newname[i] == NULL) - goto allocfail; - dns_name_init(newname[i], NULL); - result = dns_name_dup(keynames[i], zone->mctx, - newname[i]); - if (result != ISC_R_SUCCESS) { - allocfail: - for (i = 0; i < count; i++) - if (newname[i] != NULL) - dns_name_free( - newname[i], - zone->mctx); - isc_mem_put(zone->mctx, new, - count * sizeof(*new)); - isc_mem_put(zone->mctx, newok, - count * sizeof(*newok)); - isc_mem_put(zone->mctx, newname, - count * sizeof(*newname)); - goto unlock; - } - } - } + result = set_addrkeylist(count, masters, &newaddrs, + keynames, &newnames, zone->mctx); + if (result != ISC_R_SUCCESS) { + isc_mem_put(zone->mctx, newok, count * sizeof(*newok)); + goto unlock; } /* * Everything is ok so attach to the zone. */ zone->curmaster = 0; - zone->masters = new; zone->mastersok = newok; - zone->masterkeynames = newname; + zone->masters = newaddrs; + zone->masterkeynames = newnames; zone->masterscnt = count; DNS_ZONE_CLRFLAG(zone, DNS_ZONEFLG_NOMASTERS); @@ -5072,9 +5555,12 @@ zone_resigninc(dns_zone_t *zone) { zonediff_init(&zonediff, &_sig_diff); /* - * Updates are disabled. Pause for 5 minutes. + * Zone is frozen or automatic resigning is disabled. + * Pause for 5 minutes. */ - if (zone->update_disabled) { + if (zone->update_disabled || + DNS_ZONEKEY_OPTION(zone, DNS_ZONEKEY_NORESIGN)) + { result = ISC_R_FAILURE; goto failure; } @@ -5186,18 +5672,18 @@ zone_resigninc(dns_zone_t *zone) { */ if (ISC_LIST_EMPTY(zonediff.diff->tuples)) { /* - * Commit the changes if any key has been marked as offline. - */ + * Commit the changes if any key has been marked as offline. */ if (zonediff.offline) dns_db_closeversion(db, &version, ISC_TRUE); goto failure; } /* Increment SOA serial if we have made changes */ - result = increment_soa_serial(db, version, zonediff.diff, zone->mctx); + result = update_soa_serial(db, version, zonediff.diff, zone->mctx, + zone->updatemethod); if (result != ISC_R_SUCCESS) { dns_zone_log(zone, ISC_LOG_ERROR, - "zone_resigninc:increment_soa_serial -> %s", + "zone_resigninc:update_soa_serial -> %s", dns_result_totext(result)); goto failure; } @@ -5217,7 +5703,7 @@ zone_resigninc(dns_zone_t *zone) { } /* Write changes to journal file. */ - CHECK(zone_journal(zone, zonediff.diff, "zone_resigninc")); + CHECK(zone_journal(zone, zonediff.diff, NULL, "zone_resigninc")); /* Everything has succeeded. Commit the changes. */ dns_db_closeversion(db, &version, ISC_TRUE); @@ -5625,6 +6111,7 @@ fixup_nsec3param(dns_db_t *db, dns_dbversion_t *ver, dns_nsec3chain_t *chain, isc_buffer_t buffer; unsigned char parambuf[DNS_NSEC3PARAM_BUFFERSIZE]; dns_ttl_t ttl = 0; + isc_boolean_t nseconly = ISC_FALSE, nsec3ok = ISC_FALSE; dns_rdataset_init(&rdataset); @@ -5675,6 +6162,10 @@ fixup_nsec3param(dns_db_t *db, dns_dbversion_t *ver, dns_nsec3chain_t *chain, if (active) goto add; + + result = dns_nsec_nseconly(db, ver, &nseconly); + nsec3ok = (result == ISC_R_SUCCESS && !nseconly); + /* * Delete all private records which match that in nsec3chain. */ @@ -5697,7 +6188,9 @@ fixup_nsec3param(dns_db_t *db, dns_dbversion_t *ver, dns_nsec3chain_t *chain, continue; CHECK(dns_rdata_tostruct(&rdata, &nsec3param, NULL)); - if (nsec3param.hash != chain->nsec3param.hash || + if ((!nsec3ok && + (nsec3param.flags & DNS_NSEC3FLAG_INITIAL) != 0) || + nsec3param.hash != chain->nsec3param.hash || nsec3param.iterations != chain->nsec3param.iterations || nsec3param.salt_length != chain->nsec3param.salt_length || memcmp(nsec3param.salt, chain->nsec3param.salt, @@ -6271,7 +6764,8 @@ zone_nsec3chain(dns_zone_t *zone) { * of removing this NSEC3 chain. */ if (first && !updatensec && - (nsec3chain->nsec3param.flags & DNS_NSEC3FLAG_NONSEC) == 0) { + (nsec3chain->nsec3param.flags & DNS_NSEC3FLAG_NONSEC) == 0) + { result = need_nsec_chain(db, version, &nsec3chain->nsec3param, &buildnsecchain); @@ -6585,10 +7079,11 @@ zone_nsec3chain(dns_zone_t *zone) { goto failure; } - result = increment_soa_serial(db, version, zonediff.diff, zone->mctx); + result = update_soa_serial(db, version, zonediff.diff, zone->mctx, + zone->updatemethod); if (result != ISC_R_SUCCESS) { dns_zone_log(zone, ISC_LOG_ERROR, "zone_nsec3chain:" - "increment_soa_serial -> %s", + "update_soa_serial -> %s", dns_result_totext(result)); goto failure; } @@ -6603,7 +7098,7 @@ zone_nsec3chain(dns_zone_t *zone) { } /* Write changes to journal file. */ - CHECK(zone_journal(zone, zonediff.diff, "zone_nsec3chain")); + CHECK(zone_journal(zone, zonediff.diff, NULL, "zone_nsec3chain")); LOCK_ZONE(zone); zone_needdump(zone, DNS_DUMP_DELAY); @@ -7154,10 +7649,11 @@ zone_sign(dns_zone_t *zone) { goto failure; } - result = increment_soa_serial(db, version, zonediff.diff, zone->mctx); + result = update_soa_serial(db, version, zonediff.diff, zone->mctx, + zone->updatemethod); if (result != ISC_R_SUCCESS) { dns_zone_log(zone, ISC_LOG_ERROR, - "zone_sign:increment_soa_serial -> %s", + "zone_sign:update_soa_serial -> %s", dns_result_totext(result)); goto failure; } @@ -7179,7 +7675,7 @@ zone_sign(dns_zone_t *zone) { /* * Write changes to journal file. */ - CHECK(zone_journal(zone, zonediff.diff, "zone_sign")); + CHECK(zone_journal(zone, zonediff.diff, NULL, "zone_sign")); pauseall: /* @@ -7941,8 +8437,9 @@ keyfetch_done(isc_task_t *task, isc_event_t *event) { if (!ISC_LIST_EMPTY(diff.tuples)) { /* Write changes to journal file. */ - CHECK(increment_soa_serial(kfetch->db, ver, &diff, mctx)); - CHECK(zone_journal(zone, &diff, "keyfetch_done")); + CHECK(update_soa_serial(kfetch->db, ver, &diff, mctx, + zone->updatemethod)); + CHECK(zone_journal(zone, &diff, NULL, "keyfetch_done")); commit = ISC_TRUE; DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_LOADED); @@ -8115,8 +8612,9 @@ zone_refreshkeys(dns_zone_t *zone) { } } if (!ISC_LIST_EMPTY(diff.tuples)) { - CHECK(increment_soa_serial(db, ver, &diff, zone->mctx)); - CHECK(zone_journal(zone, &diff, "zone_refreshkeys")); + CHECK(update_soa_serial(db, ver, &diff, zone->mctx, + zone->updatemethod)); + CHECK(zone_journal(zone, &diff, NULL, "zone_refreshkeys")); commit = ISC_TRUE; DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_LOADED); zone_needdump(zone, 30); @@ -8163,12 +8661,18 @@ zone_maintenance(dns_zone_t *zone) { REQUIRE(DNS_ZONE_VALID(zone)); ENTER; + /* + * Are we pending load/reload? + */ + if (DNS_ZONE_FLAG(zone, DNS_ZONEFLG_LOADPENDING)) + return; + /* * Configuring the view of this zone may have * failed, for example because the config file * had a syntax error. In that case, the view - * db or resolver will be NULL, and we had better not try - * to do maintenance on it. + * adb or resolver will be NULL, and we had better not try + * to do further maintenance on it. */ if (zone->view == NULL || zone->view->adb == NULL) return; @@ -8179,6 +8683,9 @@ zone_maintenance(dns_zone_t *zone) { * Expire check. */ switch (zone->type) { + case dns_zone_redirect: + if (zone->masters == NULL) + break; case dns_zone_slave: case dns_zone_stub: LOCK_ZONE(zone); @@ -8197,6 +8704,9 @@ zone_maintenance(dns_zone_t *zone) { * Up to date check. */ switch (zone->type) { + case dns_zone_redirect: + if (zone->masters == NULL) + break; case dns_zone_slave: case dns_zone_stub: if (!DNS_ZONE_FLAG(zone, DNS_ZONEFLG_DIALREFRESH) && @@ -8222,6 +8732,7 @@ zone_maintenance(dns_zone_t *zone) { case dns_zone_master: case dns_zone_slave: case dns_zone_key: + case dns_zone_redirect: case dns_zone_stub: LOCK_ZONE(zone); if (zone->masterfile != NULL && @@ -8249,6 +8760,7 @@ zone_maintenance(dns_zone_t *zone) { */ switch (zone->type) { case dns_zone_master: + case dns_zone_redirect: if (DNS_ZONE_FLAG(zone, DNS_ZONEFLG_NEEDNOTIFY) && isc_time_compare(&now, &zone->notifytime) >= 0) zone_notify(zone, &now); @@ -8278,6 +8790,7 @@ zone_maintenance(dns_zone_t *zone) { switch (zone->type) { case dns_zone_master: + case dns_zone_redirect: case dns_zone_slave: /* * Do we need to sign/resign some RRsets? @@ -8299,6 +8812,7 @@ zone_maintenance(dns_zone_t *zone) { set_key_expiry_warning(zone, zone->key_expiry, isc_time_seconds(&now)); break; + default: break; } @@ -8307,10 +8821,31 @@ zone_maintenance(dns_zone_t *zone) { void dns_zone_markdirty(dns_zone_t *zone) { + isc_uint32_t serial; + isc_result_t result = ISC_R_SUCCESS; LOCK_ZONE(zone); - if (zone->type == dns_zone_master) - set_resigntime(zone); /* XXXMPA make separate call back */ + if (zone->type == dns_zone_master) { + if (inline_raw(zone)) { + unsigned int soacount; + + ZONEDB_LOCK(&zone->dblock, isc_rwlocktype_read); + if (zone->db != NULL) { + result = zone_get_from_db(zone, zone->db, NULL, + &soacount, &serial, + NULL, NULL, NULL, + NULL, NULL); + } else + result = DNS_R_NOTLOADED; + ZONEDB_UNLOCK(&zone->dblock, isc_rwlocktype_read); + if (result == ISC_R_SUCCESS && soacount > 0U) + zone_send_secureserial(zone, ISC_FALSE, serial); + } + + /* XXXMPA make separate call back */ + if (result == ISC_R_SUCCESS) + set_resigntime(zone); + } zone_needdump(zone, DNS_DUMP_DELAY); UNLOCK_ZONE(zone); } @@ -8497,6 +9032,22 @@ dump_done(void *arg, isc_result_t result) { version = dns_dumpctx_version(zone->dctx); tresult = dns_db_getsoaserial(db, version, &serial); + /* + * If there is a secure version of this zone + * use its serial if it is less than ours. + */ + if (tresult == ISC_R_SUCCESS && inline_raw(zone) && + zone->secure->db != NULL) + { + isc_uint32_t sserial; + isc_result_t mresult; + + mresult = dns_db_getsoaserial(zone->secure->db, + NULL, &sserial); + if (mresult == ISC_R_SUCCESS && + isc_serial_lt(sserial, serial)) + serial = sserial; + } /* * Note: we are task locked here so we can test * zone->xfr safely. @@ -8605,10 +9156,15 @@ zone_dump(dns_zone_t *zone, isc_boolean_t compact) { result = DNS_R_CONTINUE; UNLOCK_ZONE(zone); } else { + dns_masterrawheader_t rawdata; dns_db_currentversion(db, &version); - result = dns_master_dump2(zone->mctx, db, version, + dns_master_initrawheader(&rawdata); + if (inline_secure(zone)) + get_raw_serial(zone->raw, &rawdata); + result = dns_master_dump3(zone->mctx, db, version, &dns_master_style_default, - masterfile, masterformat); + masterfile, masterformat, + &rawdata); dns_db_closeversion(db, &version, ISC_FALSE); } fail: @@ -8647,11 +9203,12 @@ zone_dump(dns_zone_t *zone, isc_boolean_t compact) { static isc_result_t dumptostream(dns_zone_t *zone, FILE *fd, const dns_master_style_t *style, - dns_masterformat_t format) + dns_masterformat_t format, const isc_uint32_t rawversion) { isc_result_t result; dns_dbversion_t *version = NULL; dns_db_t *db = NULL; + dns_masterrawheader_t rawdata; REQUIRE(DNS_ZONE_VALID(zone)); @@ -8663,29 +9220,46 @@ dumptostream(dns_zone_t *zone, FILE *fd, const dns_master_style_t *style, return (DNS_R_NOTLOADED); dns_db_currentversion(db, &version); - result = dns_master_dumptostream2(zone->mctx, db, version, style, - format, fd); + dns_master_initrawheader(&rawdata); + if (rawversion == 0) + rawdata.flags |= DNS_MASTERRAW_COMPAT; + else if (inline_secure(zone)) + get_raw_serial(zone->raw, &rawdata); + else if (zone->sourceserialset) { + rawdata.flags = DNS_MASTERRAW_SOURCESERIALSET; + rawdata.sourceserial = zone->sourceserial; + } + result = dns_master_dumptostream3(zone->mctx, db, version, style, + format, &rawdata, fd); dns_db_closeversion(db, &version, ISC_FALSE); dns_db_detach(&db); return (result); } +isc_result_t +dns_zone_dumptostream3(dns_zone_t *zone, FILE *fd, dns_masterformat_t format, + const dns_master_style_t *style, + const isc_uint32_t rawversion) +{ + return (dumptostream(zone, fd, style, format, rawversion)); +} + isc_result_t dns_zone_dumptostream2(dns_zone_t *zone, FILE *fd, dns_masterformat_t format, const dns_master_style_t *style) { - return dumptostream(zone, fd, style, format); + return (dumptostream(zone, fd, style, format, DNS_RAWFORMAT_VERSION)); } isc_result_t dns_zone_dumptostream(dns_zone_t *zone, FILE *fd) { - return dumptostream(zone, fd, &dns_master_style_default, - dns_masterformat_text); + return (dumptostream(zone, fd, &dns_master_style_default, + dns_masterformat_text, 0)); } isc_result_t dns_zone_fulldumptostream(dns_zone_t *zone, FILE *fd) { - return dumptostream(zone, fd, &dns_master_style_full, - dns_masterformat_text); + return (dumptostream(zone, fd, &dns_master_style_full, + dns_masterformat_text, 0)); } void @@ -8880,6 +9454,8 @@ notify_destroy(dns_notify_t *notify, isc_boolean_t locked) { dns_request_destroy(¬ify->request); if (dns_name_dynamic(¬ify->ns)) dns_name_free(¬ify->ns, notify->mctx); + if (notify->key != NULL) + dns_tsigkey_detach(¬ify->key); mctx = notify->mctx; isc_mem_put(notify->mctx, notify, sizeof(*notify)); isc_mem_detach(&mctx); @@ -8901,6 +9477,7 @@ notify_create(isc_mem_t *mctx, unsigned int flags, dns_notify_t **notifyp) { notify->zone = NULL; notify->find = NULL; notify->request = NULL; + notify->key = NULL; isc_sockaddr_any(¬ify->dst); dns_name_init(¬ify->ns, NULL); ISC_LINK_INIT(notify, link); @@ -9045,15 +9622,23 @@ notify_send_toaddr(isc_task_t *task, isc_event_t *event) { if (result != ISC_R_SUCCESS) goto cleanup; - isc_netaddr_fromsockaddr(&dstip, ¬ify->dst); - isc_sockaddr_format(¬ify->dst, addrbuf, sizeof(addrbuf)); - result = dns_view_getpeertsig(notify->zone->view, &dstip, &key); - if (result != ISC_R_SUCCESS && result != ISC_R_NOTFOUND) { - notify_log(notify->zone, ISC_LOG_ERROR, "NOTIFY to %s not " - "sent. Peer TSIG key lookup failure.", addrbuf); - goto cleanup_message; + if (notify->key != NULL) { + /* Transfer ownership of key */ + key = notify->key; + notify->key = NULL; + } else { + isc_netaddr_fromsockaddr(&dstip, ¬ify->dst); + isc_sockaddr_format(¬ify->dst, addrbuf, sizeof(addrbuf)); + result = dns_view_getpeertsig(notify->zone->view, &dstip, &key); + if (result != ISC_R_SUCCESS && result != ISC_R_NOTFOUND) { + notify_log(notify->zone, ISC_LOG_ERROR, + "NOTIFY to %s not sent. " + "Peer TSIG key lookup failure.", addrbuf); + goto cleanup_message; + } } + /* XXX: should we log the tsig key too? */ notify_log(notify->zone, ISC_LOG_DEBUG(3), "sending notify to %s", addrbuf); if (notify->zone->view->peers != NULL) { @@ -9255,14 +9840,30 @@ zone_notify(dns_zone_t *zone, isc_time_t *now) { */ LOCK_ZONE(zone); for (i = 0; i < zone->notifycnt; i++) { + dns_tsigkey_t *key = NULL; + dst = zone->notify[i]; if (notify_isqueued(zone, NULL, &dst)) continue; + result = notify_create(zone->mctx, flags, ¬ify); if (result != ISC_R_SUCCESS) continue; + zone_iattach(zone, ¬ify->zone); notify->dst = dst; + + if ((zone->notifykeynames != NULL) && + (zone->notifykeynames[i] != NULL)) { + dns_view_t *view = dns_zone_getview(zone); + dns_name_t *keyname = zone->notifykeynames[i]; + result = dns_view_gettsig(view, keyname, &key); + if (result == ISC_R_SUCCESS) { + notify->key = key; + key = NULL; + } + } + ISC_LIST_APPEND(zone->notifies, notify, link); result = notify_send_queue(notify); if (result != ISC_R_SUCCESS) @@ -9753,7 +10354,8 @@ refresh_callback(isc_task_t *task, isc_event_t *event) { "master %s exceeded (source %s)", master, source); /* Try with slave with TCP. */ - if (zone->type == dns_zone_slave && + if ((zone->type == dns_zone_slave || + zone->type == dns_zone_redirect) && DNS_ZONE_OPTION(zone, DNS_ZONEOPT_TRYTCPREFRESH)) { if (!dns_zonemgr_unreachable(zone->zmgr, &zone->masteraddr, @@ -9819,7 +10421,8 @@ refresh_callback(isc_task_t *task, isc_event_t *event) { * Perhaps AXFR/IXFR is allowed even if SOA queries aren't. */ if (msg->rcode == dns_rcode_refused && - zone->type == dns_zone_slave) + (zone->type == dns_zone_slave || + zone->type == dns_zone_redirect)) goto tcp_transfer; goto next_master; } @@ -9828,7 +10431,8 @@ refresh_callback(isc_task_t *task, isc_event_t *event) { * If truncated punt to zone transfer which will query again. */ if ((msg->flags & DNS_MESSAGEFLAG_TC) != 0) { - if (zone->type == dns_zone_slave) { + if (zone->type == dns_zone_slave || + zone->type == dns_zone_redirect) { dns_zone_log(zone, ISC_LOG_INFO, "refresh: truncated UDP answer, " "initiating TCP zone xfer " @@ -9955,7 +10559,8 @@ refresh_callback(isc_task_t *task, isc_event_t *event) { dns_zone_log(zone, ISC_LOG_INFO, "refresh: skipping %s as master %s " "(source %s) is unreachable (cached)", - zone->type == dns_zone_slave ? + (zone->type == dns_zone_slave || + zone->type == dns_zone_redirect) ? "zone transfer" : "NS query", master, source); goto next_master; @@ -9963,7 +10568,8 @@ refresh_callback(isc_task_t *task, isc_event_t *event) { tcp_transfer: isc_event_free(&event); dns_request_destroy(&zone->request); - if (zone->type == dns_zone_slave) { + if (zone->type == dns_zone_slave || + zone->type == dns_zone_redirect) { do_queue_xfrin = ISC_TRUE; } else { INSIST(zone->type == dns_zone_stub); @@ -10643,6 +11249,7 @@ static void zone_shutdown(isc_task_t *task, isc_event_t *event) { dns_zone_t *zone = (dns_zone_t *) event->ev_arg; isc_boolean_t free_needed, linked = ISC_FALSE; + dns_zone_t *raw = NULL, *secure = NULL; UNUSED(task); REQUIRE(DNS_ZONE_VALID(zone)); @@ -10725,7 +11332,19 @@ zone_shutdown(isc_task_t *task, isc_event_t *event) { */ DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_SHUTDOWN); free_needed = exit_check(zone); + if (inline_secure(zone)) { + raw = zone->raw; + zone->raw = NULL; + } + if (inline_raw(zone)) { + secure = zone->secure; + zone->secure = NULL; + } UNLOCK_ZONE(zone); + if (raw != NULL) + dns_zone_detach(&raw); + if (secure != NULL) + dns_zone_idetach(&secure); if (free_needed) zone_free(zone); } @@ -10759,6 +11378,11 @@ zone_settimer(dns_zone_t *zone, isc_time_t *now) { isc_time_settoepoch(&next); switch (zone->type) { + case dns_zone_redirect: + if (zone->masters != NULL) + goto treat_as_slave; + /* FALLTHROUGH */ + case dns_zone_master: if (DNS_ZONE_FLAG(zone, DNS_ZONEFLG_NEEDNOTIFY)) next = zone->notifytime; @@ -10769,6 +11393,8 @@ zone_settimer(dns_zone_t *zone, isc_time_t *now) { isc_time_compare(&zone->dumptime, &next) < 0) next = zone->dumptime; } + if (zone->type == dns_zone_redirect) + break; if (!DNS_ZONE_FLAG(zone, DNS_ZONEFLG_REFRESHING) && !isc_time_isepoch(&zone->refreshkeytime)) { if (isc_time_isepoch(&next) || @@ -10798,9 +11424,10 @@ zone_settimer(dns_zone_t *zone, isc_time_t *now) { break; case dns_zone_slave: + treat_as_slave: if (DNS_ZONE_FLAG(zone, DNS_ZONEFLG_NEEDNOTIFY)) next = zone->notifytime; - /*FALLTHROUGH*/ + /* FALLTHROUGH */ case dns_zone_stub: if (!DNS_ZONE_FLAG(zone, DNS_ZONEFLG_REFRESH) && @@ -11078,9 +11705,17 @@ dns_zone_notifyreceive(dns_zone_t *zone, isc_sockaddr_t *from, isc_sockaddr_format(from, fromtext, sizeof(fromtext)); /* - * We only handle NOTIFY (SOA) at the present. + * Notify messages are processed by the raw zone. */ LOCK_ZONE(zone); + if (inline_secure(zone)) { + result = dns_zone_notifyreceive(zone->raw, from, msg); + UNLOCK_ZONE(zone); + return (result); + } + /* + * We only handle NOTIFY (SOA) at the present. + */ if (isc_sockaddr_pf(from) == PF_INET) inc_stats(zone, dns_zonestatscounter_notifyinv4); else @@ -11468,15 +12103,17 @@ zone_namerd_tostr(dns_zone_t *zone, char *buf, size_t length) { * Leave space for terminating '\0'. */ isc_buffer_init(&buffer, buf, length - 1); - if (dns_name_dynamic(&zone->origin)) - result = dns_name_totext(&zone->origin, ISC_TRUE, &buffer); - if (result != ISC_R_SUCCESS && - isc_buffer_availablelength(&buffer) >= (sizeof("") - 1)) - isc_buffer_putstr(&buffer, ""); + if (zone->type != dns_zone_redirect && zone->type != dns_zone_key) { + if (dns_name_dynamic(&zone->origin)) + result = dns_name_totext(&zone->origin, ISC_TRUE, &buffer); + if (result != ISC_R_SUCCESS && + isc_buffer_availablelength(&buffer) >= (sizeof("") - 1)) + isc_buffer_putstr(&buffer, ""); - if (isc_buffer_availablelength(&buffer) > 0) - isc_buffer_putstr(&buffer, "/"); - (void)dns_rdataclass_totext(zone->rdclass, &buffer); + if (isc_buffer_availablelength(&buffer) > 0) + isc_buffer_putstr(&buffer, "/"); + (void)dns_rdataclass_totext(zone->rdclass, &buffer); + } if (zone->view != NULL && strcmp(zone->view->name, "_bind") != 0 && strcmp(zone->view->name, "_default") != 0 && @@ -11484,6 +12121,10 @@ zone_namerd_tostr(dns_zone_t *zone, char *buf, size_t length) { isc_buffer_putstr(&buffer, "/"); isc_buffer_putstr(&buffer, zone->view->name); } + if (inline_secure(zone) && 9U < isc_buffer_availablelength(&buffer)) + isc_buffer_putstr(&buffer, " (signed)"); + if (inline_raw(zone) && 11U < isc_buffer_availablelength(&buffer)) + isc_buffer_putstr(&buffer, " (unsigned)"); buf[isc_buffer_usedlength(&buffer)] = '\0'; } @@ -11585,8 +12226,9 @@ dns_zone_logc(dns_zone_t *zone, isc_logcategory_t *category, vsnprintf(message, sizeof(message), fmt, ap); va_end(ap); isc_log_write(dns_lctx, category, DNS_LOGMODULE_ZONE, - level, "%s %s: %s", (zone->type == dns_zone_key) ? - "managed-keys-zone" : "zone", zone->strnamerd, message); + level, "%s%s: %s", (zone->type == dns_zone_key) ? + "managed-keys-zone" : (zone->type == dns_zone_redirect) ? + "redirect-zone" : "zone ", zone->strnamerd, message); } void @@ -11601,8 +12243,9 @@ dns_zone_log(dns_zone_t *zone, int level, const char *fmt, ...) { vsnprintf(message, sizeof(message), fmt, ap); va_end(ap); isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_ZONE, - level, "%s %s: %s", (zone->type == dns_zone_key) ? - "managed-keys-zone" : "zone", zone->strnamerd, message); + level, "%s%s: %s", (zone->type == dns_zone_key) ? + "managed-keys-zone" : (zone->type == dns_zone_redirect) ? + "redirect-zone" : "zone ", zone->strnamerd, message); } static void @@ -11612,6 +12255,7 @@ zone_debuglog(dns_zone_t *zone, const char *me, int debuglevel, va_list ap; char message[4096]; int level = ISC_LOG_DEBUG(debuglevel); + const char *zstr; if (isc_log_wouldlog(dns_lctx, level) == ISC_FALSE) return; @@ -11619,9 +12263,21 @@ zone_debuglog(dns_zone_t *zone, const char *me, int debuglevel, va_start(ap, fmt); vsnprintf(message, sizeof(message), fmt, ap); va_end(ap); + + switch (zone->type) { + case dns_zone_key: + zstr = "managed-keys-zone"; + break; + case dns_zone_redirect: + zstr = "redirect-zone"; + break; + default: + zstr = "zone"; + } + isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_ZONE, - level, "%s: %s %s: %s", me, zone->type != dns_zone_key ? - "zone" : "managed-keys-zone", zone->strnamerd, message); + level, "%s: %s %s: %s", me, zstr, zone->strnamerd, + message); } static int @@ -11799,6 +12455,572 @@ notify_done(isc_task_t *task, isc_event_t *event) { dns_message_destroy(&message); } +struct secure_event { + isc_event_t e; + dns_db_t *db; + isc_uint32_t serial; +}; + +static void +update_log_cb(void *arg, dns_zone_t *zone, int level, const char *message) { + UNUSED(arg); + dns_zone_log(zone, level, "%s", message); +} + +static isc_result_t +sync_secure_journal(dns_zone_t *zone, dns_journal_t *journal, + isc_uint32_t start, isc_uint32_t end, + dns_difftuple_t **soatuplep, dns_diff_t *diff) +{ + isc_result_t result; + dns_difftuple_t *tuple = NULL; + dns_diffop_t op = DNS_DIFFOP_ADD; + int n_soa = 0; + + REQUIRE(soatuplep != NULL); + + if (start == end) + return (DNS_R_UNCHANGED); + + CHECK(dns_journal_iter_init(journal, start, end)); + for (result = dns_journal_first_rr(journal); + result == ISC_R_SUCCESS; + result = dns_journal_next_rr(journal)) + { + dns_name_t *name = NULL; + isc_uint32_t ttl; + dns_rdata_t *rdata = NULL; + dns_journal_current_rr(journal, &name, &ttl, &rdata); + + if (rdata->type == dns_rdatatype_soa) { + n_soa++; + if (n_soa == 2) { + /* + * Save the latest raw SOA record. + */ + if (*soatuplep != NULL) + dns_difftuple_free(soatuplep); + CHECK(dns_difftuple_create(diff->mctx, + DNS_DIFFOP_ADD, + name, ttl, rdata, + soatuplep)); + } + if (n_soa == 3) + n_soa = 1; + continue; + } + + /* Sanity. */ + if (n_soa == 0) { + dns_zone_log(zone->raw, ISC_LOG_ERROR, + "corrupt journal file: '%s'\n", + zone->raw->journal); + return (ISC_R_FAILURE); + } + + if (zone->privatetype != 0 && + rdata->type == zone->privatetype) + continue; + + if (rdata->type == dns_rdatatype_nsec || + rdata->type == dns_rdatatype_rrsig || + rdata->type == dns_rdatatype_nsec3 || + rdata->type == dns_rdatatype_dnskey || + rdata->type == dns_rdatatype_nsec3param) + continue; + + op = (n_soa == 1) ? DNS_DIFFOP_DEL : DNS_DIFFOP_ADD; + + CHECK(dns_difftuple_create(diff->mctx, op, name, ttl, rdata, + &tuple)); + dns_diff_appendminimal(diff, &tuple); + } + if (result == ISC_R_NOMORE) + result = ISC_R_SUCCESS; + + failure: + return(result); +} + +static isc_result_t +sync_secure_db(dns_zone_t *seczone, dns_db_t *secdb, + dns_dbversion_t *secver, dns_difftuple_t **soatuple, + dns_diff_t *diff) +{ + isc_result_t result; + dns_db_t *rawdb = NULL; + dns_dbversion_t *rawver = NULL; + dns_difftuple_t *tuple = NULL, *next; + dns_difftuple_t *oldtuple = NULL, *newtuple = NULL; + dns_rdata_soa_t oldsoa, newsoa; + + REQUIRE(DNS_ZONE_VALID(seczone)); + REQUIRE(inline_secure(seczone)); + REQUIRE(soatuple != NULL && *soatuple == NULL); + + if (!seczone->sourceserialset) + return (DNS_R_UNCHANGED); + + dns_db_attach(seczone->raw->db, &rawdb); + dns_db_currentversion(rawdb, &rawver); + result = dns_db_diffx(diff, rawdb, rawver, secdb, secver, NULL); + dns_db_closeversion(rawdb, &rawver, ISC_FALSE); + dns_db_detach(&rawdb); + + if (result != ISC_R_SUCCESS) + return (result); + + for (tuple = ISC_LIST_HEAD(diff->tuples); + tuple != NULL; + tuple = next) + { + next = ISC_LIST_NEXT(tuple, link); + if (tuple->rdata.type == dns_rdatatype_nsec || + tuple->rdata.type == dns_rdatatype_rrsig || + tuple->rdata.type == dns_rdatatype_dnskey || + tuple->rdata.type == dns_rdatatype_nsec3 || + tuple->rdata.type == dns_rdatatype_nsec3param) + { + ISC_LIST_UNLINK(diff->tuples, tuple, link); + dns_difftuple_free(&tuple); + continue; + } + if (tuple->rdata.type == dns_rdatatype_soa) { + if (tuple->op == DNS_DIFFOP_DEL) { + INSIST(oldtuple == NULL); + oldtuple = tuple; + } + if (tuple->op == DNS_DIFFOP_ADD) { + INSIST(newtuple == NULL); + newtuple = tuple; + } + } + } + + if (oldtuple != NULL && newtuple != NULL) { + + result = dns_rdata_tostruct(&oldtuple->rdata, &oldsoa, NULL); + RUNTIME_CHECK(result == ISC_R_SUCCESS); + + result = dns_rdata_tostruct(&newtuple->rdata, &newsoa, NULL); + RUNTIME_CHECK(result == ISC_R_SUCCESS); + + /* + * If the SOA records are the same except for the serial + * remove them from the diff. + */ + if (oldsoa.refresh == newsoa.refresh && + oldsoa.retry == newsoa.retry && + oldsoa.minimum == newsoa.minimum && + oldsoa.expire == newsoa.expire && + dns_name_equal(&oldsoa.origin, &newsoa.origin) && + dns_name_equal(&oldsoa.contact, &newsoa.contact)) { + ISC_LIST_UNLINK(diff->tuples, oldtuple, link); + dns_difftuple_free(&oldtuple); + ISC_LIST_UNLINK(diff->tuples, newtuple, link); + dns_difftuple_free(&newtuple); + } + } + + if (ISC_LIST_EMPTY(diff->tuples)) + return (DNS_R_UNCHANGED); + + /* + * If there are still SOA records in the diff they can now be removed + * saving the new SOA record. + */ + if (oldtuple != NULL) { + ISC_LIST_UNLINK(diff->tuples, oldtuple, link); + dns_difftuple_free(&oldtuple); + } + + if (newtuple != NULL) { + ISC_LIST_UNLINK(diff->tuples, newtuple, link); + *soatuple = newtuple; + } + + return (ISC_R_SUCCESS); +} + +static void +receive_secure_serial(isc_task_t *task, isc_event_t *event) { + isc_result_t result; + dns_journal_t *rjournal = NULL; + isc_uint32_t start, end; + dns_zone_t *zone; + dns_db_t *db = NULL; + dns_dbversion_t *newver = NULL, *oldver = NULL; + dns_diff_t diff; + dns_difftuple_t *tuple = NULL, *soatuple = NULL; + dns_update_log_t log = { update_log_cb, NULL }; + isc_time_t timenow; + + zone = event->ev_arg; + end = ((struct secure_event *)event)->serial; + isc_event_free(&event); + + LOCK_ZONE(zone); + + dns_diff_init(zone->mctx, &diff); + + UNUSED(task); + + /* + * zone->db may be NULL if the load from disk failed. + */ + if (zone->db == NULL || !inline_secure(zone)) { + result = ISC_R_FAILURE; + goto failure; + } + + /* + * We first attempt to sync the raw zone to the secure zone + * by using the raw zone's journal, applying all the deltas + * from the latest source-serial of the secure zone up to + * the current serial number of the raw zone. + * + * If that fails, then we'll fall back to a direct comparison + * between raw and secure zones. + */ + result = dns_journal_open(zone->raw->mctx, zone->raw->journal, + DNS_JOURNAL_WRITE, &rjournal); + if (result != ISC_R_SUCCESS) + goto failure; + else { + dns_journal_t *sjournal = NULL; + + result = dns_journal_open(zone->mctx, zone->journal, + DNS_JOURNAL_READ, &sjournal); + if (result != ISC_R_SUCCESS && result != ISC_R_NOTFOUND) + goto failure; + + if (!dns_journal_get_sourceserial(rjournal, &start)) { + start = dns_journal_first_serial(rjournal); + dns_journal_set_sourceserial(rjournal, start); + } + if (sjournal != NULL) { + isc_uint32_t serial; + /* + * We read the secure journal first, if that exists + * use its value provided it is greater that from the + * raw journal. + */ + if (dns_journal_get_sourceserial(sjournal, &serial)) { + if (isc_serial_gt(serial, start)) + start = serial; + } + dns_journal_destroy(&sjournal); + } + } + + dns_db_attach(zone->db, &db); + dns_db_currentversion(db, &oldver); + CHECK(dns_db_newversion(db, &newver)); + + /* + * Try to apply diffs from the raw zone's journal to the secure + * zone. If that fails, we recover by syncing up the databases + * directly. + */ + result = sync_secure_journal(zone, rjournal, start, end, + &soatuple, &diff); + if (result == DNS_R_UNCHANGED) + goto failure; + else if (result != ISC_R_SUCCESS) + CHECK(sync_secure_db(zone, db, oldver, &soatuple, &diff)); + + CHECK(dns_diff_apply(&diff, db, newver)); + + if (soatuple != NULL) { + isc_uint32_t oldserial, newserial, desired; + + CHECK(dns_db_createsoatuple(db, oldver, diff.mctx, + DNS_DIFFOP_DEL, &tuple)); + oldserial = dns_soa_getserial(&tuple->rdata); + newserial = desired = dns_soa_getserial(&soatuple->rdata); + if (!isc_serial_gt(newserial, oldserial)) { + newserial = oldserial + 1; + if (newserial == 0) + newserial++; + dns_soa_setserial(newserial, &soatuple->rdata); + } + CHECK(do_one_tuple(&tuple, db, newver, &diff)); + CHECK(do_one_tuple(&soatuple, db, newver, &diff)); + dns_zone_log(zone, ISC_LOG_INFO, "serial %u (unsigned %u)", + newserial, desired); + } else + CHECK(update_soa_serial(db, newver, &diff, zone->mctx, + zone->updatemethod)); + + CHECK(dns_update_signatures(&log, zone, db, oldver, newver, + &diff, zone->sigvalidityinterval)); + + CHECK(zone_journal(zone, &diff, &end, "receive_secure_serial")); + + dns_journal_set_sourceserial(rjournal, end); + dns_journal_commit(rjournal); + + DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_NEEDNOTIFY); + + zone->sourceserial = end; + zone->sourceserialset = ISC_TRUE; + zone_needdump(zone, DNS_DUMP_DELAY); + + TIME_NOW(&timenow); + zone_settimer(zone, &timenow); + + dns_db_closeversion(db, &oldver, ISC_FALSE); + dns_db_closeversion(db, &newver, ISC_TRUE); + + failure: + UNLOCK_ZONE(zone); + if (result != ISC_R_SUCCESS) + dns_zone_log(zone, ISC_LOG_ERROR, "receive_secure_serial: %s", + dns_result_totext(result)); + if (tuple != NULL) + dns_difftuple_free(&tuple); + if (soatuple != NULL) + dns_difftuple_free(&soatuple); + if (db != NULL) { + if (oldver != NULL) + dns_db_closeversion(db, &oldver, ISC_FALSE); + if (newver != NULL) + dns_db_closeversion(db, &newver, ISC_FALSE); + dns_db_detach(&db); + } + if (rjournal != NULL) + dns_journal_destroy(&rjournal); + dns_diff_clear(&diff); + dns_zone_idetach(&zone); +} + +static isc_result_t +zone_send_secureserial(dns_zone_t *zone, isc_boolean_t locked, + isc_uint32_t serial) +{ + isc_event_t *e; + dns_zone_t *dummy = NULL; + + e = isc_event_allocate(zone->secure->mctx, zone, + DNS_EVENT_ZONESECURESERIAL, + receive_secure_serial, zone->secure, + sizeof(struct secure_event)); + if (e == NULL) + return (ISC_R_NOMEMORY); + ((struct secure_event *)e)->serial = serial; + if (locked) + zone_iattach(zone->secure, &dummy); + else + dns_zone_iattach(zone->secure, &dummy); + isc_task_send(zone->secure->task, &e); + + DNS_ZONE_CLRFLAG(zone, DNS_ZONEFLG_SENDSECURE); + return (ISC_R_SUCCESS); +} + +static isc_result_t +checkandaddsoa(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version, + dns_rdataset_t *rdataset, isc_uint32_t oldserial) +{ + dns_rdata_soa_t soa; + dns_rdata_t rdata = DNS_RDATA_INIT; + dns_rdatalist_t temprdatalist; + dns_rdataset_t temprdataset; + isc_buffer_t b; + isc_result_t result; + unsigned char buf[DNS_SOA_BUFFERSIZE]; + + result = dns_rdataset_first(rdataset); + RUNTIME_CHECK(result == ISC_R_SUCCESS); + dns_rdataset_current(rdataset, &rdata); + result = dns_rdata_tostruct(&rdata, &soa, NULL); + RUNTIME_CHECK(result == ISC_R_SUCCESS); + + if (isc_serial_gt(soa.serial, oldserial)) + return (dns_db_addrdataset(db, node, version, 0, rdataset, 0, + NULL)); + /* + * Always bump the serial. + */ + oldserial++; + if (oldserial == 0) + oldserial++; + soa.serial = oldserial; + + /* + * Construct a replacement rdataset. + */ + dns_rdata_reset(&rdata); + isc_buffer_init(&b, buf, sizeof(buf)); + result = dns_rdata_fromstruct(&rdata, rdataset->rdclass, + dns_rdatatype_soa, &soa, &b); + RUNTIME_CHECK(result == ISC_R_SUCCESS); + temprdatalist.rdclass = rdata.rdclass; + temprdatalist.type = rdata.type; + temprdatalist.covers = 0; + temprdatalist.ttl = rdataset->ttl; + ISC_LIST_INIT(temprdatalist.rdata); + ISC_LIST_APPEND(temprdatalist.rdata, &rdata, link); + + dns_rdataset_init(&temprdataset); + result = dns_rdatalist_tordataset(&temprdatalist, &temprdataset); + RUNTIME_CHECK(result == ISC_R_SUCCESS); + return (dns_db_addrdataset(db, node, version, 0, &temprdataset, + 0, NULL)); +} + +static void +receive_secure_db(isc_task_t *task, isc_event_t *event) { + isc_result_t result; + dns_zone_t *zone; + dns_db_t *rawdb, *db = NULL; + dns_dbnode_t *rawnode = NULL, *node = NULL; + dns_fixedname_t fname; + dns_name_t *name; + dns_dbiterator_t *dbiterator = NULL; + dns_rdatasetiter_t *rdsit = NULL; + dns_rdataset_t rdataset; + dns_dbversion_t *version = NULL; + isc_time_t loadtime; + unsigned int oldserial = 0; + isc_boolean_t have_oldserial = ISC_FALSE; + + UNUSED(task); + + zone = event->ev_arg; + rawdb = ((struct secure_event *)event)->db; + isc_event_free(&event); + + REQUIRE(inline_secure(zone)); + + dns_fixedname_init(&fname); + name = dns_fixedname_name(&fname); + dns_rdataset_init(&rdataset); + + TIME_NOW(&loadtime); + if (zone->db != NULL) { + result = dns_db_getsoaserial(zone->db, NULL, &oldserial); + if (result == ISC_R_SUCCESS) + have_oldserial = ISC_TRUE; + } + + result = dns_db_create(zone->mctx, zone->db_argv[0], + &zone->origin, dns_dbtype_zone, zone->rdclass, + zone->db_argc - 1, zone->db_argv + 1, &db); + if (result != ISC_R_SUCCESS) + goto failure; + + result = dns_db_newversion(db, &version); + if (result != ISC_R_SUCCESS) + goto failure; + + result = dns_db_createiterator(rawdb, 0, &dbiterator); + if (result != ISC_R_SUCCESS) + goto failure; + + for (result = dns_dbiterator_first(dbiterator); + result == ISC_R_SUCCESS; + result = dns_dbiterator_next(dbiterator)) { + result = dns_dbiterator_current(dbiterator, &rawnode, name); + if (result != ISC_R_SUCCESS) + continue; + + result = dns_db_findnode(db, name, ISC_TRUE, &node); + if (result != ISC_R_SUCCESS) + goto failure; + + result = dns_db_allrdatasets(rawdb, rawnode, NULL, 0, &rdsit); + if (result != ISC_R_SUCCESS) + goto failure; + + for (result = dns_rdatasetiter_first(rdsit); + result == ISC_R_SUCCESS; + result = dns_rdatasetiter_next(rdsit)) { + dns_rdatasetiter_current(rdsit, &rdataset); + if (rdataset.type == dns_rdatatype_nsec || + rdataset.type == dns_rdatatype_rrsig || + rdataset.type == dns_rdatatype_nsec3 || + rdataset.type == dns_rdatatype_dnskey || + rdataset.type == dns_rdatatype_nsec3param) { + dns_rdataset_disassociate(&rdataset); + continue; + } + if (rdataset.type == dns_rdatatype_soa && + have_oldserial) { + result = checkandaddsoa(db, node, version, + &rdataset, oldserial); + } else + result = dns_db_addrdataset(db, node, version, + 0, &rdataset, 0, + NULL); + if (result != ISC_R_SUCCESS) + goto failure; + + dns_rdataset_disassociate(&rdataset); + } + dns_rdatasetiter_destroy(&rdsit); + dns_db_detachnode(rawdb, &rawnode); + dns_db_detachnode(db, &node); + } + + dns_db_closeversion(db, &version, ISC_TRUE); + /* + * Lock hierarchy: zmgr, zone, raw. + */ + LOCK_ZONE(zone); + if (inline_secure(zone)) + LOCK_ZONE(zone->raw); + DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_NEEDNOTIFY); + result = zone_postload(zone, db, loadtime, ISC_R_SUCCESS); + zone_needdump(zone, 0); /* XXXMPA */ + if (inline_secure(zone)) + UNLOCK_ZONE(zone->raw); + UNLOCK_ZONE(zone); + + failure: + if (result != ISC_R_SUCCESS) + dns_zone_log(zone, ISC_LOG_ERROR, "receive_secure_db: %s", + dns_result_totext(result)); + + if (dns_rdataset_isassociated(&rdataset)) + dns_rdataset_disassociate(&rdataset); + if (db != NULL) { + if (node != NULL) + dns_db_detachnode(db, &node); + dns_db_detach(&db); + } + if (rawnode != NULL) + dns_db_detachnode(rawdb, &rawnode); + dns_db_detach(&rawdb); + if (dbiterator != NULL) + dns_dbiterator_destroy(&dbiterator); + dns_zone_idetach(&zone); +} + +static isc_result_t +zone_send_securedb(dns_zone_t *zone, isc_boolean_t locked, dns_db_t *db) { + isc_event_t *e; + dns_db_t *dummy = NULL; + dns_zone_t *secure = NULL; + + e = isc_event_allocate(zone->secure->mctx, zone, + DNS_EVENT_ZONESECUREDB, + receive_secure_db, zone->secure, + sizeof(struct secure_event)); + if (e == NULL) + return (ISC_R_NOMEMORY); + dns_db_attach(db, &dummy); + ((struct secure_event *)e)->db = dummy; + if (locked) + zone_iattach(zone->secure, &secure); + else + dns_zone_iattach(zone->secure, &secure); + + isc_task_send(zone->secure->task, &e); + DNS_ZONE_CLRFLAG(zone, DNS_ZONEFLG_SENDSECURE); + return (ISC_R_SUCCESS); +} + isc_result_t dns_zone_replacedb(dns_zone_t *zone, dns_db_t *db, isc_boolean_t dump) { isc_result_t result; @@ -11860,7 +13082,8 @@ zone_replacedb(dns_zone_t *zone, dns_db_t *db, isc_boolean_t dump) { */ if (zone->db != NULL && zone->journal != NULL && DNS_ZONE_OPTION(zone, DNS_ZONEOPT_IXFRFROMDIFFS) && - !DNS_ZONE_FLAG(zone, DNS_ZONEFLG_FORCEXFER)) { + !DNS_ZONE_FLAG(zone, DNS_ZONEFLG_FORCEXFER)) + { isc_uint32_t serial, oldserial; unsigned int soacount; @@ -11882,8 +13105,10 @@ zone_replacedb(dns_zone_t *zone, dns_db_t *db, isc_boolean_t dump) { NULL); RUNTIME_CHECK(result == ISC_R_SUCCESS); RUNTIME_CHECK(soacount > 0U); - if (zone->type == dns_zone_slave && - !isc_serial_gt(serial, oldserial)) { + if ((zone->type == dns_zone_slave || + (zone->type == dns_zone_redirect && + zone->masters != NULL)) + && !isc_serial_gt(serial, oldserial)) { isc_uint32_t serialmin, serialmax; serialmin = (oldserial + 1) & 0xffffffffU; serialmax = (oldserial + 0x7fffffffU) & 0xffffffffU; @@ -11919,6 +13144,8 @@ zone_replacedb(dns_zone_t *zone, dns_db_t *db, isc_boolean_t dump) { break; } } + if (zone->type == dns_zone_master && inline_raw(zone)) + zone_send_secureserial(zone, ISC_FALSE, serial); } else { if (dump && zone->masterfile != NULL) { /* @@ -11969,13 +13196,14 @@ zone_replacedb(dns_zone_t *zone, dns_db_t *db, isc_boolean_t dump) { zone->journal, strbuf); } } + + if (inline_raw(zone)) + zone_send_securedb(zone, ISC_FALSE, db); } dns_db_closeversion(db, &ver, ISC_FALSE); - isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL, - DNS_LOGMODULE_ZONE, ISC_LOG_DEBUG(3), - "replacing zone database"); + dns_zone_log(zone, ISC_LOG_DEBUG(3), "replacing zone database"); if (zone->db != NULL) zone_detachdb(zone); @@ -12120,6 +13348,8 @@ zone_xfrdone(dns_zone_t *zone, isc_result_t result) { dns_zone_log(zone, ISC_LOG_INFO, "transferred serial %u%s", serial, buf); + if (inline_raw(zone)) + zone_send_secureserial(zone, ISC_FALSE, serial); } /* @@ -12274,18 +13504,26 @@ zone_loaddone(void *arg, isc_result_t result) { (result == ISC_R_SUCCESS || result == DNS_R_SEENINCLUDE)) result = tresult; - LOCK_ZONE(load->zone); - (void)zone_postload(load->zone, load->db, load->loadtime, result); - zonemgr_putio(&load->zone->readio); - DNS_ZONE_CLRFLAG(load->zone, DNS_ZONEFLG_LOADING); + /* + * Lock hierarchy: zmgr, zone, raw. + */ + LOCK_ZONE(zone); + if (inline_secure(zone)) + LOCK_ZONE(zone->raw); + (void)zone_postload(zone, load->db, load->loadtime, result); + zonemgr_putio(&zone->readio); + DNS_ZONE_CLRFLAG(zone, DNS_ZONEFLG_LOADING); + zone_idetach(&load->callbacks.zone); /* * Leave the zone frozen if the reload fails. */ if ((result == ISC_R_SUCCESS || result == DNS_R_SEENINCLUDE) && - DNS_ZONE_FLAG(load->zone, DNS_ZONEFLG_THAW)) + DNS_ZONE_FLAG(zone, DNS_ZONEFLG_THAW)) zone->update_disabled = ISC_FALSE; - DNS_ZONE_CLRFLAG(load->zone, DNS_ZONEFLG_THAW); - UNLOCK_ZONE(load->zone); + DNS_ZONE_CLRFLAG(zone, DNS_ZONEFLG_THAW); + if (inline_secure(zone)) + UNLOCK_ZONE(zone->raw); + UNLOCK_ZONE(zone); load->magic = 0; dns_db_detach(&load->db); @@ -12383,7 +13621,7 @@ queue_xfrin(dns_zone_t *zone) { */ static void got_transfer_quota(isc_task_t *task, isc_event_t *event) { - isc_result_t result; + isc_result_t result = ISC_R_SUCCESS; dns_peer_t *peer = NULL; char master[ISC_SOCKADDR_FORMATSIZE]; char source[ISC_SOCKADDR_FORMATSIZE]; @@ -12432,14 +13670,6 @@ got_transfer_quota(isc_task_t *task, isc_event_t *event) { "no database exists yet, requesting AXFR of " "initial version from %s", master); xfrtype = dns_rdatatype_axfr; - } else if (DNS_ZONE_OPTION(zone, DNS_ZONEOPT_IXFRFROMDIFFS)) { - dns_zone_log(zone, ISC_LOG_DEBUG(1), "ixfr-from-differences " - "set, requesting %sAXFR from %s", soa_before, - master); - if (DNS_ZONE_FLAG(zone, DNS_ZONEFLG_SOABEFOREAXFR)) - xfrtype = dns_rdatatype_soa; - else - xfrtype = dns_rdatatype_axfr; } else if (DNS_ZONE_FLAG(zone, DNS_ZONEFLG_FORCEXFER)) { dns_zone_log(zone, ISC_LOG_DEBUG(1), "forced reload, requesting AXFR of " @@ -12455,13 +13685,10 @@ got_transfer_quota(isc_task_t *task, isc_event_t *event) { UNLOCK_ZONE(zone); } else { isc_boolean_t use_ixfr = ISC_TRUE; - if (peer != NULL && - dns_peer_getrequestixfr(peer, &use_ixfr) == - ISC_R_SUCCESS) { - ; /* Using peer setting */ - } else { - use_ixfr = zone->view->requestixfr; - } + if (peer != NULL) + result = dns_peer_getrequestixfr(peer, &use_ixfr); + if (peer == NULL || result != ISC_R_SUCCESS) + use_ixfr = zone->requestixfr; if (use_ixfr == ISC_FALSE) { dns_zone_log(zone, ISC_LOG_DEBUG(1), "IXFR disabled, requesting %sAXFR from %s", @@ -12806,6 +14033,8 @@ dns_zonemgr_create(isc_mem_t *mctx, isc_taskmgr_t *taskmgr, zmgr->timermgr = timermgr; zmgr->socketmgr = socketmgr; zmgr->zonetasks = NULL; + zmgr->loadtasks = NULL; + zmgr->mctxpool = NULL; zmgr->task = NULL; zmgr->rl = NULL; ISC_LIST_INIT(zmgr->zones); @@ -12873,6 +14102,33 @@ dns_zonemgr_create(isc_mem_t *mctx, isc_taskmgr_t *taskmgr, return (result); } +isc_result_t +dns_zonemgr_createzone(dns_zonemgr_t *zmgr, dns_zone_t **zonep) { + isc_result_t result; + isc_mem_t *mctx = NULL; + dns_zone_t *zone = NULL; + void *item; + + REQUIRE(DNS_ZONEMGR_VALID(zmgr)); + REQUIRE(zonep != NULL && *zonep == NULL); + + if (zmgr->mctxpool == NULL) + return (ISC_R_FAILURE); + + item = isc_pool_get(zmgr->mctxpool); + if (item == NULL) + return (ISC_R_FAILURE); + + isc_mem_attach((isc_mem_t *) item, &mctx); + result = dns_zone_create(&zone, mctx); + isc_mem_detach(&mctx); + + if (result == ISC_R_SUCCESS) + *zonep = zone; + + return (result); +} + isc_result_t dns_zonemgr_managezone(dns_zonemgr_t *zmgr, dns_zone_t *zone) { isc_result_t result; @@ -12890,6 +14146,7 @@ dns_zonemgr_managezone(dns_zonemgr_t *zmgr, dns_zone_t *zone) { REQUIRE(zone->zmgr == NULL); isc_taskpool_gettask(zmgr->zonetasks, &zone->task); + isc_taskpool_gettask(zmgr->loadtasks, &zone->loadtask); /* * Set the task name. The tag will arbitrarily point to one @@ -12897,6 +14154,7 @@ dns_zonemgr_managezone(dns_zonemgr_t *zmgr, dns_zone_t *zone) { * to be managed last). */ isc_task_setname(zone->task, "zone", zone); + isc_task_setname(zone->loadtask, "loadzone", zone); result = isc_timer_create(zmgr->timermgr, isc_timertype_inactive, NULL, NULL, @@ -12904,7 +14162,7 @@ dns_zonemgr_managezone(dns_zonemgr_t *zmgr, dns_zone_t *zone) { &zone->timer); if (result != ISC_R_SUCCESS) - goto cleanup_task; + goto cleanup_tasks; /* * The timer "holds" a iref. @@ -12918,7 +14176,8 @@ dns_zonemgr_managezone(dns_zonemgr_t *zmgr, dns_zone_t *zone) { goto unlock; - cleanup_task: + cleanup_tasks: + isc_task_detach(&zone->loadtask); isc_task_detach(&zone->task); unlock: @@ -13034,6 +14293,10 @@ dns_zonemgr_shutdown(dns_zonemgr_t *zmgr) { isc_task_destroy(&zmgr->task); if (zmgr->zonetasks != NULL) isc_taskpool_destroy(&zmgr->zonetasks); + if (zmgr->loadtasks != NULL) + isc_taskpool_destroy(&zmgr->loadtasks); + if (zmgr->mctxpool != NULL) + isc_pool_destroy(&zmgr->mctxpool); RWLOCK(&zmgr->rwlock, isc_rwlocktype_read); for (zone = ISC_LIST_HEAD(zmgr->zones); @@ -13047,23 +14310,56 @@ dns_zonemgr_shutdown(dns_zonemgr_t *zmgr) { RWUNLOCK(&zmgr->rwlock, isc_rwlocktype_read); } +static isc_result_t +mctxinit(void **target, void *arg) { + isc_result_t result; + isc_mem_t *mctx = NULL; + + UNUSED(arg); + + REQUIRE(target != NULL && *target == NULL); + + result = isc_mem_create(0, 0, &mctx); + if (result != ISC_R_SUCCESS) + return (result); + isc_mem_setname(mctx, "zonemgr-pool", NULL); + + *target = mctx; + return (ISC_R_SUCCESS); +} + +static void +mctxfree(void **target) { + isc_mem_t *mctx = *(isc_mem_t **) target; + isc_mem_detach(&mctx); + *target = NULL; +} + +#define ZONES_PER_TASK 100 +#define ZONES_PER_MCTX 1000 + isc_result_t dns_zonemgr_setsize(dns_zonemgr_t *zmgr, int num_zones) { isc_result_t result; - int ntasks = num_zones / 100; + int ntasks = num_zones / ZONES_PER_TASK; + int nmctx = num_zones / ZONES_PER_MCTX; isc_taskpool_t *pool = NULL; + isc_pool_t *mctxpool = NULL; REQUIRE(DNS_ZONEMGR_VALID(zmgr)); /* * For anything fewer than 1000 zones we use 10 tasks in - * the task pool. More than that, and we'll scale at one - * task per 100 zones. + * the task pools. More than that, and we'll scale at one + * task per 100 zones. Similarly, for anything smaller than + * 2000 zones we use 2 memory contexts, then scale at 1:1000. */ if (ntasks < 10) ntasks = 10; + if (nmctx < 2) + nmctx = 2; - /* Create or resize the zone task pool. */ + /* Create or resize the zone task pools. */ if (zmgr->zonetasks == NULL) result = isc_taskpool_create(zmgr->taskmgr, zmgr->mctx, ntasks, 2, &pool); @@ -13073,6 +14369,43 @@ dns_zonemgr_setsize(dns_zonemgr_t *zmgr, int num_zones) { if (result == ISC_R_SUCCESS) zmgr->zonetasks = pool; + pool = NULL; + if (zmgr->loadtasks == NULL) + result = isc_taskpool_create(zmgr->taskmgr, zmgr->mctx, + ntasks, 2, &pool); + else + result = isc_taskpool_expand(&zmgr->loadtasks, ntasks, &pool); + + if (result == ISC_R_SUCCESS) + zmgr->loadtasks = pool; + +#ifdef BIND9 + /* + * We always set all tasks in the zone-load task pool to + * privileged. This prevents other tasks in the system from + * running while the server task manager is in privileged + * mode. + * + * NOTE: If we start using task privileges for any other + * part of the system than zone tasks, then this will need to be + * revisted. In that case we'd want to turn on privileges for + * zone tasks only when we were loading, and turn them off the + * rest of the time. For now, however, it's okay to just + * set it and forget it. + */ + isc_taskpool_setprivilege(zmgr->loadtasks, ISC_TRUE); +#endif + + /* Create or resize the zone memory context pool. */ + if (zmgr->mctxpool == NULL) + result = isc_pool_create(zmgr->mctx, nmctx, mctxfree, + mctxinit, NULL, &mctxpool); + else + result = isc_pool_expand(&zmgr->mctxpool, nmctx, &mctxpool); + + if (result == ISC_R_SUCCESS) + zmgr->mctxpool = mctxpool; + return (result); } @@ -13309,12 +14642,14 @@ zonemgr_getio(dns_zonemgr_t *zmgr, isc_boolean_t high, io = isc_mem_get(zmgr->mctx, sizeof(*io)); if (io == NULL) return (ISC_R_NOMEMORY); + io->event = isc_event_allocate(zmgr->mctx, task, DNS_EVENT_IOREADY, action, arg, sizeof(*io->event)); if (io->event == NULL) { isc_mem_put(zmgr->mctx, io, sizeof(*io)); return (ISC_R_NOMEMORY); } + io->zmgr = zmgr; io->high = high; io->task = NULL; @@ -13334,9 +14669,8 @@ zonemgr_getio(dns_zonemgr_t *zmgr, isc_boolean_t high, UNLOCK(&zmgr->iolock); *iop = io; - if (!queue) { + if (!queue) isc_task_send(io->task, &io->event); - } return (ISC_R_SUCCESS); } @@ -13613,7 +14947,8 @@ void dns_zone_forcereload(dns_zone_t *zone) { REQUIRE(DNS_ZONE_VALID(zone)); - if (zone->type == dns_zone_master) + if (zone->type == dns_zone_master || + (zone->type == dns_zone_redirect && zone->masters == NULL)) return; LOCK_ZONE(zone); @@ -13661,6 +14996,7 @@ dns_zone_setstats(dns_zone_t *zone, isc_stats_t *stats) { void dns_zone_setrequeststats(dns_zone_t *zone, isc_stats_t *stats) { + REQUIRE(DNS_ZONE_VALID(zone)); LOCK_ZONE(zone); @@ -13673,10 +15009,25 @@ dns_zone_setrequeststats(dns_zone_t *zone, isc_stats_t *stats) { } } UNLOCK_ZONE(zone); - - return; } +#ifdef NEWSTATS +void +dns_zone_setrcvquerystats(dns_zone_t *zone, dns_stats_t *stats) { + + REQUIRE(DNS_ZONE_VALID(zone)); + + LOCK_ZONE(zone); + if (zone->requeststats_on && stats != NULL) { + if (zone->rcvquerystats == NULL) { + dns_stats_attach(stats, &zone->rcvquerystats); + zone->requeststats_on = ISC_TRUE; + } + } + UNLOCK_ZONE(zone); +} +#endif + isc_stats_t * dns_zone_getrequeststats(dns_zone_t *zone) { /* @@ -13693,6 +15044,20 @@ dns_zone_getrequeststats(dns_zone_t *zone) { return (NULL); } +#ifdef NEWSTATS +/* + * Return the received query stats bucket + * see note from dns_zone_getrequeststats() + */ +dns_stats_t * +dns_zone_getrcvquerystats(dns_zone_t *zone) { + if (zone->requeststats_on) + return (zone->rcvquerystats); + else + return (NULL); +} +#endif + void dns_zone_dialup(dns_zone_t *zone) { @@ -13705,7 +15070,7 @@ dns_zone_dialup(dns_zone_t *zone) { if (DNS_ZONE_FLAG(zone, DNS_ZONEFLG_DIALNOTIFY)) dns_zone_notify(zone); - if (zone->type != dns_zone_master && + if (zone->type != dns_zone_master && zone->masters != NULL && DNS_ZONE_FLAG(zone, DNS_ZONEFLG_DIALREFRESH)) dns_zone_refresh(zone); } @@ -14321,8 +15686,12 @@ dnskey_sane(dns_zone_t *zone, dns_db_t *db, dns_dbversion_t *ver, } /* Check existing DB for NSEC-only DNSKEY */ - if (!nseconly) - CHECK(dns_nsec_nseconly(db, ver, &nseconly)); + if (!nseconly) { + result = dns_nsec_nseconly(db, ver, &nseconly); + if (result == ISC_R_NOTFOUND) + result = ISC_R_SUCCESS; + CHECK(result); + } /* Check existing DB for NSEC3 */ if (!nsec3) @@ -14360,7 +15729,7 @@ clean_nsec3param(dns_zone_t *zone, dns_db_t *db, dns_dbversion_t *ver, if (result != ISC_R_NOTFOUND) goto failure; - result = dns_nsec3param_deletechains(db, ver, zone, diff); + result = dns_nsec3param_deletechains(db, ver, zone, ISC_TRUE, diff); failure: if (node != NULL) @@ -14476,14 +15845,11 @@ zone_rekey(dns_zone_t *zone) { dns_rdatatype_none, 0, &keyset, &keysigs); if (result == ISC_R_SUCCESS) { ttl = keyset.ttl; - result = dns_dnssec_keylistfromrdataset(&zone->origin, dir, - mctx, &keyset, - &keysigs, &soasigs, - ISC_FALSE, ISC_FALSE, - &dnskeys); - /* Can't get keys for some reason; try again later. */ - if (result != ISC_R_SUCCESS) - goto trylater; + CHECK(dns_dnssec_keylistfromrdataset(&zone->origin, dir, + mctx, &keyset, + &keysigs, &soasigs, + ISC_FALSE, ISC_FALSE, + &dnskeys)); } else if (result != ISC_R_NOTFOUND) goto failure; @@ -14509,7 +15875,7 @@ zone_rekey(dns_zone_t *zone) { dns_zone_log(zone, ISC_LOG_ERROR, "zone_rekey:" "couldn't update zone keys: %s", isc_result_totext(result)); - goto trylater; + goto failure; } /* @@ -14552,15 +15918,17 @@ zone_rekey(dns_zone_t *zone) { CHECK(add_signing_records(db, zone->privatetype, ver, &diff, ISC_TF(newalg || fullsign))); - CHECK(increment_soa_serial(db, ver, &diff, mctx)); + CHECK(update_soa_serial(db, ver, &diff, mctx, + zone->updatemethod)); CHECK(add_chains(zone, db, ver, &diff)); CHECK(sign_apex(zone, db, ver, &diff, &zonediff)); - CHECK(zone_journal(zone, zonediff.diff, "zone_rekey")); + CHECK(zone_journal(zone, zonediff.diff, NULL, + "zone_rekey")); commit = ISC_TRUE; } } - dns_db_closeversion(db, &ver, commit); + dns_db_closeversion(db, &ver, ISC_TRUE); if (commit) { dns_difftuple_t *tuple; @@ -14672,6 +16040,13 @@ zone_rekey(dns_zone_t *zone) { } } + /* + * Activate any NSEC3 chain updates that may have + * been scheduled before this rekey. + */ + if (fullsign || newalg) + resume_addnsec3chain(zone); + /* * Schedule the next resigning event */ @@ -14683,15 +16058,16 @@ zone_rekey(dns_zone_t *zone) { /* * If we're doing key maintenance, set the key refresh timer to - * the next scheduled key event or to one hour in the future, - * whichever is sooner. + * the next scheduled key event or to 'dnssec-loadkeys-interval' + * seconds in the future, whichever is sooner. */ if (DNS_ZONEKEY_OPTION(zone, DNS_ZONEKEY_MAINTAIN)) { isc_time_t timethen; isc_stdtime_t then; LOCK_ZONE(zone); - DNS_ZONE_TIME_ADD(&timenow, HOUR, &timethen); + DNS_ZONE_TIME_ADD(&timenow, zone->refreshkeyinterval, + &timethen); zone->refreshkeytime = timethen; UNLOCK_ZONE(zone); @@ -14718,7 +16094,7 @@ zone_rekey(dns_zone_t *zone) { dns_zone_log(zone, ISC_LOG_INFO, "next key event: %s", timebuf); } - failure: + done: dns_diff_clear(&diff); dns_diff_clear(&_sig_diff); @@ -14740,10 +16116,14 @@ zone_rekey(dns_zone_t *zone) { dns_db_detach(&db); return; - trylater: - isc_interval_set(&ival, HOUR, 0); + failure: + /* + * Something went wrong; try again in ten minutes or + * after a key refresh interval, whichever is shorter. + */ + isc_interval_set(&ival, ISC_MIN(zone->refreshkeyinterval, 600), 0); isc_time_nowplusinterval(&zone->refreshkeytime, &ival); - goto failure; + goto done; } void @@ -14802,10 +16182,572 @@ dns_zone_dlzpostload(dns_zone_t *zone, dns_db_t *db) { isc_time_t loadtime; isc_result_t result; + TIME_NOW(&loadtime); + /* + * Lock hierarchy: zmgr, zone, raw. + */ LOCK_ZONE(zone); + if (inline_secure(zone)) + LOCK_ZONE(zone->raw); result = zone_postload(zone, db, loadtime, ISC_R_SUCCESS); + if (inline_secure(zone)) + UNLOCK_ZONE(zone->raw); UNLOCK_ZONE(zone); return result; } + +isc_result_t +dns_zone_setrefreshkeyinterval(dns_zone_t *zone, isc_uint32_t interval) { + REQUIRE(DNS_ZONE_VALID(zone)); + if (interval == 0) + return (ISC_R_RANGE); + /* Maximum value: 24 hours (3600 minutes) */ + if (interval > (24 * 60)) + interval = (24 * 60); + /* Multiply by 60 for seconds */ + zone->refreshkeyinterval = interval * 60; + return (ISC_R_SUCCESS); +} + +void +dns_zone_setrequestixfr(dns_zone_t *zone, isc_boolean_t flag) { + REQUIRE(DNS_ZONE_VALID(zone)); + zone->requestixfr = flag; +} + +isc_boolean_t +dns_zone_getrequestixfr(dns_zone_t *zone) { + REQUIRE(DNS_ZONE_VALID(zone)); + return (zone->requestixfr); +} + +void +dns_zone_setserialupdatemethod(dns_zone_t *zone, dns_updatemethod_t method) { + REQUIRE(DNS_ZONE_VALID(zone)); + zone->updatemethod = method; +} + +dns_updatemethod_t +dns_zone_getserialupdatemethod(dns_zone_t *zone) { + REQUIRE(DNS_ZONE_VALID(zone)); + return(zone->updatemethod); +} + +/* + * Lock hierarchy: zmgr, zone, raw. + */ +isc_result_t +dns_zone_link(dns_zone_t *zone, dns_zone_t *raw) { + isc_result_t result; + dns_zonemgr_t *zmgr; + + REQUIRE(DNS_ZONE_VALID(zone)); + REQUIRE(zone->zmgr != NULL); + REQUIRE(zone->task != NULL); + REQUIRE(zone->loadtask != NULL); + REQUIRE(zone->raw == NULL); + + REQUIRE(DNS_ZONE_VALID(raw)); + REQUIRE(raw->zmgr == NULL); + REQUIRE(raw->task == NULL); + REQUIRE(raw->loadtask == NULL); + REQUIRE(raw->secure == NULL); + + /* + * Lock hierarchy: zmgr, zone, raw. + */ + zmgr = zone->zmgr; + RWLOCK(&zmgr->rwlock, isc_rwlocktype_write); + LOCK_ZONE(zone); + LOCK_ZONE(raw); + + result = isc_timer_create(zmgr->timermgr, isc_timertype_inactive, + NULL, NULL, zone->task, zone_timer, raw, + &raw->timer); + if (result != ISC_R_SUCCESS) + goto unlock; + + /* + * The timer "holds" a iref. + */ + raw->irefs++; + INSIST(raw->irefs != 0); + + + /* dns_zone_attach(raw, &zone->raw); */ + isc_refcount_increment(&raw->erefs, NULL); + zone->raw = raw; + + /* dns_zone_iattach(zone, &raw->secure); */ + zone_iattach(zone, &raw->secure); + + isc_task_attach(zone->task, &raw->task); + isc_task_attach(zone->loadtask, &raw->loadtask); + + ISC_LIST_APPEND(zmgr->zones, raw, link); + raw->zmgr = zmgr; + zmgr->refs++; + + unlock: + UNLOCK_ZONE(raw); + UNLOCK_ZONE(zone); + RWUNLOCK(&zmgr->rwlock, isc_rwlocktype_write); + return (result); +} + +void +dns_zone_getraw(dns_zone_t *zone, dns_zone_t **raw) { + REQUIRE(DNS_ZONE_VALID(zone)); + REQUIRE(raw != NULL && *raw == NULL); + + LOCK(&zone->lock); + if (zone->raw != NULL) + dns_zone_attach(zone->raw, raw); + UNLOCK(&zone->lock); +} + +struct keydone { + isc_event_t event; + isc_boolean_t all; + unsigned char data[5]; +}; + +#define PENDINGFLAGS (DNS_NSEC3FLAG_CREATE|DNS_NSEC3FLAG_INITIAL) + +static void +keydone(isc_task_t *task, isc_event_t *event) { + const char *me = "keydone"; + isc_boolean_t commit = ISC_FALSE; + isc_result_t result; + dns_rdata_t rdata = DNS_RDATA_INIT; + dns_dbversion_t *oldver = NULL, *newver = NULL; + dns_zone_t *zone; + dns_db_t *db = NULL; + dns_dbnode_t *node = NULL; + dns_rdataset_t rdataset; + dns_diff_t diff; + struct keydone *keydone = (struct keydone *)event; + dns_update_log_t log = { update_log_cb, NULL }; + isc_boolean_t clear_pending = ISC_FALSE; + + UNUSED(task); + + zone = event->ev_arg; + INSIST(DNS_ZONE_VALID(zone)); + + ENTER; + + dns_rdataset_init(&rdataset); + dns_diff_init(zone->mctx, &diff); + + ZONEDB_LOCK(&zone->dblock, isc_rwlocktype_read); + if (zone->db != NULL) { + dns_db_attach(zone->db, &db); + dns_db_currentversion(db, &oldver); + result = dns_db_newversion(db, &newver); + if (result != ISC_R_SUCCESS) { + dns_zone_log(zone, ISC_LOG_ERROR, + "keydone:dns_db_newversion -> %s", + dns_result_totext(result)); + goto failure; + } + } + ZONEDB_UNLOCK(&zone->dblock, isc_rwlocktype_read); + if (db == NULL) + goto failure; + + result = dns_db_getoriginnode(db, &node); + if (result != ISC_R_SUCCESS) + goto failure; + + result = dns_db_findrdataset(db, node, newver, zone->privatetype, + dns_rdatatype_none, 0, &rdataset, NULL); + if (result == ISC_R_NOTFOUND) { + INSIST(!dns_rdataset_isassociated(&rdataset)); + goto failure; + } + if (result != ISC_R_SUCCESS) { + INSIST(!dns_rdataset_isassociated(&rdataset)); + goto failure; + } + + for (result = dns_rdataset_first(&rdataset); + result == ISC_R_SUCCESS; + result = dns_rdataset_next(&rdataset)) { + isc_boolean_t found = ISC_FALSE; + + dns_rdataset_current(&rdataset, &rdata); + + if (keydone->all) { + if (rdata.length == 5 && rdata.data[0] != 0 && + rdata.data[3] == 0 && rdata.data[4] == 1) + found = ISC_TRUE; + else if (rdata.data[0] == 0 && + (rdata.data[2] & PENDINGFLAGS) != 0) { + found = ISC_TRUE; + clear_pending = ISC_TRUE; + } + } else if (rdata.length == 5 && + memcmp(rdata.data, keydone->data, 5) == 0) + found = ISC_TRUE; + + if (found) + CHECK(update_one_rr(db, newver, &diff, DNS_DIFFOP_DEL, + &zone->origin, rdataset.ttl, + &rdata)); + dns_rdata_reset(&rdata); + } + + if (!ISC_LIST_EMPTY(diff.tuples)) { + /* Write changes to journal file. */ + CHECK(update_soa_serial(db, newver, &diff, zone->mctx, + zone->updatemethod)); + + result = dns_update_signatures(&log, zone, db, + oldver, newver, &diff, + zone->sigvalidityinterval); + if (!clear_pending) + CHECK(result); + + CHECK(zone_journal(zone, &diff, NULL, "keydone")); + commit = ISC_TRUE; + + LOCK_ZONE(zone); + DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_LOADED); + zone_needdump(zone, 30); + UNLOCK_ZONE(zone); + } + + failure: + if (dns_rdataset_isassociated(&rdataset)) + dns_rdataset_disassociate(&rdataset); + if (db != NULL) { + if (node != NULL) + dns_db_detachnode(db, &node); + if (oldver != NULL) + dns_db_closeversion(db, &oldver, ISC_FALSE); + if (newver != NULL) + dns_db_closeversion(db, &newver, commit); + dns_db_detach(&db); + } + dns_diff_clear(&diff); + isc_event_free(&event); + dns_zone_idetach(&zone); +} + +isc_result_t +dns_zone_keydone(dns_zone_t *zone, const char *keystr) { + isc_result_t result = ISC_R_SUCCESS; + isc_event_t *e; + isc_buffer_t b; + dns_zone_t *dummy = NULL; + struct keydone *kd; + + REQUIRE(DNS_ZONE_VALID(zone)); + + LOCK_ZONE(zone); + + e = isc_event_allocate(zone->mctx, zone, DNS_EVENT_KEYDONE, keydone, + zone, sizeof(struct keydone)); + if (e == NULL) { + result = ISC_R_NOMEMORY; + goto failure; + } + + kd = (struct keydone *) e; + if (strcasecmp(keystr, "all") == 0) + kd->all = ISC_TRUE; + else { + isc_textregion_t r; + char *algstr; + dns_keytag_t keyid; + dns_secalg_t alg; + size_t n; + + kd->all = ISC_FALSE; + + n = sscanf(keystr, "%hd/", &keyid); + if (n == 0U) + CHECK(ISC_R_FAILURE); + + algstr = strchr(keystr, '/'); + if (algstr != NULL) + algstr++; + else + CHECK(ISC_R_FAILURE); + + n = sscanf(algstr, "%hhd", &alg); + if (n == 0U) { + DE_CONST(algstr, r.base); + r.length = strlen(algstr); + CHECK(dns_secalg_fromtext(&alg, &r)); + } + + /* construct a private-type rdata */ + isc_buffer_init(&b, kd->data, sizeof(kd->data)); + isc_buffer_putuint8(&b, alg); + isc_buffer_putuint8(&b, (keyid & 0xff00) >> 8); + isc_buffer_putuint8(&b, (keyid & 0xff)); + isc_buffer_putuint8(&b, 0); + isc_buffer_putuint8(&b, 1); + } + + zone_iattach(zone, &dummy); + isc_task_send(zone->task, &e); + + failure: + if (e != NULL) + isc_event_free(&e); + UNLOCK_ZONE(zone); + return (result); +} + +struct nsec3param { + isc_event_t event; + unsigned char data[DNS_NSEC3PARAM_BUFFERSIZE + 1]; + unsigned int length; + isc_boolean_t nsec; + isc_boolean_t replace; +}; + +static void +setnsec3param(isc_task_t *task, isc_event_t *event) { + const char *me = "setnsec3param"; + isc_boolean_t commit = ISC_FALSE; + isc_result_t result; + dns_dbversion_t *oldver = NULL, *newver = NULL; + dns_zone_t *zone; + dns_db_t *db = NULL; + dns_dbnode_t *node = NULL; + dns_rdataset_t prdataset, nrdataset; + dns_diff_t diff; + struct nsec3param *np = (struct nsec3param *)event; + dns_update_log_t log = { update_log_cb, NULL }; + dns_rdata_t rdata; + isc_boolean_t nseconly; + isc_boolean_t exists = ISC_FALSE; + + UNUSED(task); + + zone = event->ev_arg; + INSIST(DNS_ZONE_VALID(zone)); + + ENTER; + + dns_rdataset_init(&prdataset); + dns_rdataset_init(&nrdataset); + dns_diff_init(zone->mctx, &diff); + + ZONEDB_LOCK(&zone->dblock, isc_rwlocktype_read); + if (zone->db != NULL) { + dns_db_attach(zone->db, &db); + dns_db_currentversion(db, &oldver); + result = dns_db_newversion(db, &newver); + if (result != ISC_R_SUCCESS) { + dns_zone_log(zone, ISC_LOG_ERROR, + "setnsec3param:dns_db_newversion -> %s", + dns_result_totext(result)); + goto failure; + } + } + ZONEDB_UNLOCK(&zone->dblock, isc_rwlocktype_read); + if (db == NULL) + goto failure; + + CHECK(dns_db_getoriginnode(db, &node)); + + /* + * Does a private-type record already exist for this chain? + */ + result = dns_db_findrdataset(db, node, newver, zone->privatetype, + dns_rdatatype_none, 0, &prdataset, NULL); + if (result == ISC_R_SUCCESS) { + for (result = dns_rdataset_first(&prdataset); + result == ISC_R_SUCCESS; + result = dns_rdataset_next(&prdataset)) { + dns_rdata_init(&rdata); + dns_rdataset_current(&prdataset, &rdata); + + if (np->length == rdata.length && + memcmp(rdata.data, np->data, np->length) == 0) { + exists = ISC_TRUE; + break; + } + } + } else if (result != ISC_R_NOTFOUND) { + INSIST(!dns_rdataset_isassociated(&prdataset)); + goto failure; + } + + /* + * Does the chain already exist? + */ + result = dns_db_findrdataset(db, node, newver, + dns_rdatatype_nsec3param, + dns_rdatatype_none, 0, &nrdataset, NULL); + if (result == ISC_R_SUCCESS) { + for (result = dns_rdataset_first(&nrdataset); + result == ISC_R_SUCCESS; + result = dns_rdataset_next(&nrdataset)) { + dns_rdata_init(&rdata); + dns_rdataset_current(&nrdataset, &rdata); + + if (np->length == (rdata.length + 1) && + memcmp(rdata.data, np->data + 1, + np->length - 1) == 0) + { + exists = ISC_TRUE; + break; + } + } + } else if (result != ISC_R_NOTFOUND) { + INSIST(!dns_rdataset_isassociated(&nrdataset)); + goto failure; + } + + + /* + * We need to remove any existing NSEC3 chains. + */ + if (!exists && np->replace && (np->length != 0 || np->nsec)) + CHECK(dns_nsec3param_deletechains(db, newver, zone, + !np->nsec, &diff)); + + if (!exists && np->length != 0) { + /* + * We're creating an NSEC3 chain. + * + * If the zone is not currently capable of supporting + * an NSEC3 chain, add the INITIAL flag, so these + * parameters can be used later when NSEC3 becomes + * available. + */ + dns_rdata_init(&rdata); + + np->data[2] |= DNS_NSEC3FLAG_CREATE; + result = dns_nsec_nseconly(db, newver, &nseconly); + if (result == ISC_R_NOTFOUND || nseconly) + np->data[2] |= DNS_NSEC3FLAG_INITIAL; + + rdata.length = np->length; + rdata.data = np->data; + rdata.type = zone->privatetype; + rdata.rdclass = zone->rdclass; + CHECK(update_one_rr(db, newver, &diff, DNS_DIFFOP_ADD, + &zone->origin, 0, &rdata)); + } + + if (!ISC_LIST_EMPTY(diff.tuples)) { + /* Write changes to journal file. */ + CHECK(update_soa_serial(db, newver, &diff, zone->mctx, + zone->updatemethod)); + result = dns_update_signatures(&log, zone, db, + oldver, newver, &diff, + zone->sigvalidityinterval); + if (result != ISC_R_NOTFOUND) + CHECK(result); + CHECK(zone_journal(zone, &diff, NULL, "setnsec3param")); + commit = ISC_TRUE; + + LOCK_ZONE(zone); + DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_LOADED); + zone_needdump(zone, 30); + UNLOCK_ZONE(zone); + } + + failure: + if (dns_rdataset_isassociated(&prdataset)) + dns_rdataset_disassociate(&prdataset); + if (dns_rdataset_isassociated(&nrdataset)) + dns_rdataset_disassociate(&nrdataset); + if (node != NULL) + dns_db_detachnode(db, &node); + if (oldver != NULL) + dns_db_closeversion(db, &oldver, ISC_FALSE); + if (newver != NULL) + dns_db_closeversion(db, &newver, commit); + if (db != NULL) + dns_db_detach(&db); + if (commit) + resume_addnsec3chain(zone); + dns_diff_clear(&diff); + isc_event_free(&event); + dns_zone_idetach(&zone); +} + +isc_result_t +dns_zone_setnsec3param(dns_zone_t *zone, isc_uint8_t hash, isc_uint8_t flags, + isc_uint16_t iter, isc_uint8_t saltlen, + unsigned char *salt, isc_boolean_t replace) +{ + isc_result_t result = ISC_R_SUCCESS; + dns_rdata_nsec3param_t param; + dns_rdata_t nrdata = DNS_RDATA_INIT; + dns_rdata_t prdata = DNS_RDATA_INIT; + unsigned char nbuf[DNS_NSEC3PARAM_BUFFERSIZE]; + struct nsec3param *np; + dns_zone_t *dummy = NULL; + isc_buffer_t b; + isc_event_t *e; + + REQUIRE(DNS_ZONE_VALID(zone)); + REQUIRE(salt != NULL); + + LOCK_ZONE(zone); + + e = isc_event_allocate(zone->mctx, zone, DNS_EVENT_SETNSEC3PARAM, + setnsec3param, zone, sizeof(struct nsec3param)); + if (e == NULL) { + result = ISC_R_NOMEMORY; + goto failure; + } + + np = (struct nsec3param *) e; + np->replace = replace; + if (hash == 0) { + np->length = 0; + np->nsec = ISC_TRUE; + } else { + param.common.rdclass = zone->rdclass; + param.common.rdtype = dns_rdatatype_nsec3param; + ISC_LINK_INIT(¶m.common, link); + param.mctx = NULL; + param.hash = hash; + param.flags = flags; + param.iterations = iter; + param.salt_length = saltlen; + param.salt = salt; + isc_buffer_init(&b, nbuf, sizeof(nbuf)); + CHECK(dns_rdata_fromstruct(&nrdata, zone->rdclass, + dns_rdatatype_nsec3param, + ¶m, &b)); + dns_nsec3param_toprivate(&nrdata, &prdata, zone->privatetype, + np->data, sizeof(np->data)); + np->length = prdata.length; + } + + zone_iattach(zone, &dummy); + isc_task_send(zone->task, &e); + + failure: + if (e != NULL) + isc_event_free(&e); + UNLOCK_ZONE(zone); + return (result); +} + +void +dns_zone_setstatlevel(dns_zone_t *zone, dns_zonestat_level_t level) { + REQUIRE(DNS_ZONE_VALID(zone)); + + zone->statlevel = level; +} + +dns_zonestat_level_t +dns_zone_getstatlevel(dns_zone_t *zone) { + REQUIRE(DNS_ZONE_VALID(zone)); + + return (zone->statlevel); +} diff --git a/contrib/bind9/lib/dns/zt.c b/contrib/bind9/lib/dns/zt.c index 650d46bf900..eb1e4247247 100644 --- a/contrib/bind9/lib/dns/zt.c +++ b/contrib/bind9/lib/dns/zt.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include @@ -42,8 +43,12 @@ struct dns_zt { isc_mem_t *mctx; dns_rdataclass_t rdclass; isc_rwlock_t rwlock; + dns_zt_allloaded_t loaddone; + void * loaddone_arg; /* Locked by lock. */ + isc_boolean_t flush; isc_uint32_t references; + unsigned int loads_pending; dns_rbt_t *table; }; @@ -56,12 +61,18 @@ auto_detach(void *, void *); static isc_result_t load(dns_zone_t *zone, void *uap); +static isc_result_t +asyncload(dns_zone_t *zone, void *callback); + static isc_result_t loadnew(dns_zone_t *zone, void *uap); static isc_result_t freezezones(dns_zone_t *zone, void *uap); +static isc_result_t +doneloading(dns_zt_t *zt, dns_zone_t *zone, isc_task_t *task); + isc_result_t dns_zt_create(isc_mem_t *mctx, dns_rdataclass_t rdclass, dns_zt_t **ztp) { @@ -83,10 +94,15 @@ dns_zt_create(isc_mem_t *mctx, dns_rdataclass_t rdclass, dns_zt_t **ztp) if (result != ISC_R_SUCCESS) goto cleanup_rbt; - zt->mctx = mctx; + zt->mctx = NULL; + isc_mem_attach(mctx, &zt->mctx); zt->references = 1; + zt->flush = ISC_FALSE; zt->rdclass = rdclass; zt->magic = ZTMAGIC; + zt->loaddone = NULL; + zt->loaddone_arg = NULL; + zt->loads_pending = 0; *ztp = zt; return (ISC_R_SUCCESS); @@ -187,6 +203,16 @@ flush(dns_zone_t *zone, void *uap) { return (dns_zone_flush(zone)); } +static void +zt_destroy(dns_zt_t *zt) { + if (zt->flush) + (void)dns_zt_apply(zt, ISC_FALSE, flush, NULL); + dns_rbt_destroy(&zt->table); + isc_rwlock_destroy(&zt->rwlock); + zt->magic = 0; + isc_mem_putanddetach(&zt->mctx, zt, sizeof(*zt)); +} + static void zt_flushanddetach(dns_zt_t **ztp, isc_boolean_t need_flush) { isc_boolean_t destroy = ISC_FALSE; @@ -202,17 +228,13 @@ zt_flushanddetach(dns_zt_t **ztp, isc_boolean_t need_flush) { zt->references--; if (zt->references == 0) destroy = ISC_TRUE; + if (need_flush) + zt->flush = ISC_TRUE; RWUNLOCK(&zt->rwlock, isc_rwlocktype_write); - if (destroy) { - if (need_flush) - (void)dns_zt_apply(zt, ISC_FALSE, flush, NULL); - dns_rbt_destroy(&zt->table); - isc_rwlock_destroy(&zt->rwlock); - zt->magic = 0; - isc_mem_put(zt->mctx, zt, sizeof(*zt)); - } + if (destroy) + zt_destroy(zt); *ztp = NULL; } @@ -243,12 +265,66 @@ static isc_result_t load(dns_zone_t *zone, void *uap) { isc_result_t result; UNUSED(uap); + result = dns_zone_load(zone); if (result == DNS_R_CONTINUE || result == DNS_R_UPTODATE) result = ISC_R_SUCCESS; + return (result); } +isc_result_t +dns_zt_asyncload(dns_zt_t *zt, dns_zt_allloaded_t alldone, void *arg) { + isc_result_t result; + static dns_zt_zoneloaded_t dl = doneloading; + int pending; + + REQUIRE(VALID_ZT(zt)); + + RWLOCK(&zt->rwlock, isc_rwlocktype_write); + + INSIST(zt->loads_pending == 0); + result = dns_zt_apply2(zt, ISC_FALSE, NULL, asyncload, &dl); + + pending = zt->loads_pending; + if (pending != 0) { + zt->loaddone = alldone; + zt->loaddone_arg = arg; + } + + RWUNLOCK(&zt->rwlock, isc_rwlocktype_write); + + if (pending == 0) + alldone(arg); + + return (result); +} + +/* + * Initiates asynchronous loading of zone 'zone'. 'callback' is a + * pointer to a function which will be used to inform the caller when + * the zone loading is complete. + */ +static isc_result_t +asyncload(dns_zone_t *zone, void *callback) { + isc_result_t result; + dns_zt_zoneloaded_t *loaded = callback; + dns_zt_t *zt; + + REQUIRE(zone != NULL); + zt = dns_zone_getview(zone)->zonetable; + INSIST(VALID_ZT(zt)); + + result = dns_zone_asyncload(zone, *loaded, zt); + if (result == ISC_R_SUCCESS) { + INSIST(zt->references > 0); + zt->references++; + INSIST(zt->references != 0); + zt->loads_pending++; + } + return (ISC_R_SUCCESS); +} + isc_result_t dns_zt_loadnew(dns_zt_t *zt, isc_boolean_t stop) { isc_result_t result; @@ -265,6 +341,7 @@ static isc_result_t loadnew(dns_zone_t *zone, void *uap) { isc_result_t result; UNUSED(uap); + result = dns_zone_loadnew(zone); if (result == DNS_R_CONTINUE || result == DNS_R_UPTODATE || result == DNS_R_DYNAMIC) @@ -281,6 +358,8 @@ dns_zt_freezezones(dns_zt_t *zt, isc_boolean_t freeze) { RWLOCK(&zt->rwlock, isc_rwlocktype_read); result = dns_zt_apply2(zt, ISC_FALSE, &tresult, freezezones, &freeze); RWUNLOCK(&zt->rwlock, isc_rwlocktype_read); + if (tresult == ISC_R_NOTFOUND) + tresult = ISC_R_SUCCESS; return ((result == ISC_R_SUCCESS) ? tresult : result); } @@ -291,14 +370,25 @@ freezezones(dns_zone_t *zone, void *uap) { isc_result_t result = ISC_R_SUCCESS; char classstr[DNS_RDATACLASS_FORMATSIZE]; char zonename[DNS_NAME_FORMATSIZE]; + dns_zone_t *raw = NULL; dns_view_t *view; - char *journal; const char *vname; const char *sep; int level; - if (dns_zone_gettype(zone) != dns_zone_master) + dns_zone_getraw(zone, &raw); + if (raw != NULL) + zone = raw; + if (dns_zone_gettype(zone) != dns_zone_master) { + if (raw != NULL) + dns_zone_detach(&raw); return (ISC_R_SUCCESS); + } + if (!dns_zone_isdynamic(zone, ISC_TRUE)) { + if (raw != NULL) + dns_zone_detach(&raw); + return (ISC_R_SUCCESS); + } frozen = dns_zone_getupdatedisabled(zone); if (freeze) { @@ -306,11 +396,6 @@ freezezones(dns_zone_t *zone, void *uap) { result = DNS_R_FROZEN; if (result == ISC_R_SUCCESS) result = dns_zone_flush(zone); - if (result == ISC_R_SUCCESS) { - journal = dns_zone_getjournal(zone); - if (journal != NULL) - (void)isc_file_remove(journal); - } } else { if (frozen) { result = dns_zone_load(zone); @@ -340,6 +425,8 @@ freezezones(dns_zone_t *zone, void *uap) { freeze ? "freezing" : "thawing", zonename, classstr, sep, vname, isc_result_totext(result)); + if (raw != NULL) + dns_zone_detach(&raw); return (result); } @@ -368,6 +455,7 @@ dns_zt_apply2(dns_zt_t *zt, isc_boolean_t stop, isc_result_t *sub, /* * The tree is empty. */ + tresult = result; result = ISC_R_NOMORE; } while (result == DNS_R_NEWORIGIN || result == ISC_R_SUCCESS) { @@ -397,6 +485,46 @@ dns_zt_apply2(dns_zt_t *zt, isc_boolean_t stop, isc_result_t *sub, return (result); } +/* + * Decrement the loads_pending counter; when counter reaches + * zero, call the loaddone callback that was initially set by + * dns_zt_asyncload(). + */ +static isc_result_t +doneloading(dns_zt_t *zt, dns_zone_t *zone, isc_task_t *task) { + isc_boolean_t destroy = ISC_FALSE; + dns_zt_allloaded_t alldone = NULL; + void *arg = NULL; + + UNUSED(zone); + UNUSED(task); + + REQUIRE(VALID_ZT(zt)); + + RWLOCK(&zt->rwlock, isc_rwlocktype_write); + INSIST(zt->loads_pending != 0); + INSIST(zt->references != 0); + zt->references--; + if (zt->references == 0) + destroy = ISC_TRUE; + zt->loads_pending--; + if (zt->loads_pending == 0) { + alldone = zt->loaddone; + arg = zt->loaddone_arg; + zt->loaddone = NULL; + zt->loaddone_arg = NULL; + } + RWUNLOCK(&zt->rwlock, isc_rwlocktype_write); + + if (alldone != NULL) + alldone(arg); + + if (destroy) + zt_destroy(zt); + + return (ISC_R_SUCCESS); +} + /*** *** Private ***/ diff --git a/contrib/bind9/lib/irs/api b/contrib/bind9/lib/irs/api index 5c8dd5e14db..298e96a8abc 100644 --- a/contrib/bind9/lib/irs/api +++ b/contrib/bind9/lib/irs/api @@ -4,6 +4,6 @@ # 9.8: 80-89, 120-129 # 9.9: 90-109 # 9.9-sub: 130-139 -LIBINTERFACE = 80 -LIBREVISION = 4 +LIBINTERFACE = 90 +LIBREVISION = 1 LIBAGE = 0 diff --git a/contrib/bind9/lib/isc/Makefile.in b/contrib/bind9/lib/isc/Makefile.in index 2fa56335851..e68290cd297 100644 --- a/contrib/bind9/lib/isc/Makefile.in +++ b/contrib/bind9/lib/isc/Makefile.in @@ -58,7 +58,7 @@ OBJS = @ISC_EXTRA_OBJS@ \ httpd.@O@ inet_aton.@O@ iterated_hash.@O@ \ lex.@O@ lfsr.@O@ lib.@O@ log.@O@ \ md5.@O@ mem.@O@ mutexblock.@O@ \ - netaddr.@O@ netscope.@O@ ondestroy.@O@ \ + netaddr.@O@ netscope.@O@ pool.@O@ ondestroy.@O@ \ parseint.@O@ portset.@O@ quota.@O@ radix.@O@ random.@O@ \ ratelimiter.@O@ refcount.@O@ region.@O@ regex.@O@ result.@O@ \ rwlock.@O@ \ @@ -75,7 +75,7 @@ SRCS = @ISC_EXTRA_SRCS@ \ httpd.c inet_aton.c iterated_hash.c \ lex.c lfsr.c lib.c log.c \ md5.c mem.c mutexblock.c \ - netaddr.c netscope.c ondestroy.c \ + netaddr.c netscope.c pool.c ondestroy.c \ parseint.c portset.c quota.c radix.c random.c \ ratelimiter.c refcount.c region.c regex.c result.c rwlock.c \ serial.c sha1.c sha2.c sockaddr.c stats.c string.c strtoul.c \ diff --git a/contrib/bind9/lib/isc/api b/contrib/bind9/lib/isc/api index c7d281344cf..48bc766d937 100644 --- a/contrib/bind9/lib/isc/api +++ b/contrib/bind9/lib/isc/api @@ -4,6 +4,6 @@ # 9.8: 80-89, 120-129 # 9.9: 90-109 # 9.9-sub: 130-139 -LIBINTERFACE = 87 +LIBINTERFACE = 95 LIBREVISION = 1 -LIBAGE = 3 +LIBAGE = 0 diff --git a/contrib/bind9/lib/isc/include/isc/heap.h b/contrib/bind9/lib/isc/include/isc/heap.h index 77bf07c3449..0b3a53b01ef 100644 --- a/contrib/bind9/lib/isc/include/isc/heap.h +++ b/contrib/bind9/lib/isc/include/isc/heap.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004-2007, 2009 Internet Systems Consortium, Inc. ("ISC") + * Copyright (C) 2004-2007, 2009, 2012 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 1997-2001 Internet Software Consortium. * * Permission to use, copy, modify, and/or distribute this software for any @@ -60,6 +60,8 @@ isc_heap_create(isc_mem_t *mctx, isc_heapcompare_t compare, * storage method. When the heap elements are deleted space is not freed * but will be reused when new elements are inserted. * + * Heap elements are indexed from 1. + * * Requires: *\li "mctx" is valid. *\li "compare" is a function which takes two void * arguments and diff --git a/contrib/bind9/lib/isc/include/isc/list.h b/contrib/bind9/lib/isc/include/isc/list.h index 2b174eca575..401bbdad756 100644 --- a/contrib/bind9/lib/isc/include/isc/list.h +++ b/contrib/bind9/lib/isc/include/isc/list.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004, 2006, 2007, 2012, 2013 Internet Systems Consortium, Inc. ("ISC") + * Copyright (C) 2004, 2006, 2007, 2011-2013 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 1997-2002 Internet Software Consortium. * * Permission to use, copy, modify, and/or distribute this software for any @@ -171,6 +171,19 @@ (list2).tail = NULL; \ } while (0) +#define ISC_LIST_PREPENDLIST(list1, list2, link) \ + do { \ + if (ISC_LIST_EMPTY(list1)) \ + (list1) = (list2); \ + else if (!ISC_LIST_EMPTY(list2)) { \ + (list2).tail->link.next = (list1).head; \ + (list1).head->link.prev = (list2).tail; \ + (list1).head = (list2).head; \ + } \ + (list2).head = NULL; \ + (list2).tail = NULL; \ + } while (0) + #define ISC_LIST_ENQUEUE(list, elt, link) ISC_LIST_APPEND(list, elt, link) #define __ISC_LIST_ENQUEUEUNSAFE(list, elt, link) \ __ISC_LIST_APPENDUNSAFE(list, elt, link) diff --git a/contrib/bind9/lib/isc/include/isc/mem.h b/contrib/bind9/lib/isc/include/isc/mem.h index 317417f6366..320d0d83138 100644 --- a/contrib/bind9/lib/isc/include/isc/mem.h +++ b/contrib/bind9/lib/isc/include/isc/mem.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004-2010, 2012 Internet Systems Consortium, Inc. ("ISC") + * Copyright (C) 2004-2012 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 1997-2001 Internet Software Consortium. * * Permission to use, copy, modify, and/or distribute this software for any @@ -317,7 +317,7 @@ isc_mem_createx2(size_t max_size, size_t target_size, * ISC_MEMFLAG_INTERNAL is not set, 'target_size' is ignored. * * 'max_size' is also used to size the statistics arrays and the array - * used to record active memory when ISC_MEM_DEBUGRECORD is set. Settin + * used to record active memory when ISC_MEM_DEBUGRECORD is set. Setting * 'max_size' too low can have detrimental effects on performance. * * A memory context created using isc_mem_createx() will obtain diff --git a/contrib/bind9/lib/isc/include/isc/namespace.h b/contrib/bind9/lib/isc/include/isc/namespace.h index 45b769c5eeb..f8744d8ad63 100644 --- a/contrib/bind9/lib/isc/include/isc/namespace.h +++ b/contrib/bind9/lib/isc/include/isc/namespace.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009, 2010, 2012 Internet Systems Consortium, Inc. ("ISC") + * Copyright (C) 2009-2012 Internet Systems Consortium, Inc. ("ISC") * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -31,6 +31,7 @@ #define isc_app_run isc__app_run #define isc_app_ctxrun isc__app_ctxrun #define isc_app_shutdown isc__app_shutdown +#define isc_app_ctxfinish isc__app_ctxfinish #define isc_app_ctxshutdown isc__app_ctxshutdown #define isc_app_ctxsuspend isc__app_ctxsuspend #define isc_app_reload isc__app_reload @@ -89,6 +90,7 @@ #define isc_mempool_getfillcount isc__mempool_getfillcount #define isc_socket_create isc__socket_create +#define isc_socket_dup isc__socket_dup #define isc_socket_attach isc__socket_attach #define isc_socket_detach isc__socket_detach #define isc_socketmgr_create isc__socketmgr_create @@ -111,6 +113,7 @@ #define isc_socket_listen isc__socket_listen #define isc_socket_accept isc__socket_accept #define isc_socket_connect isc__socket_connect +#define isc_socket_getfd isc__socket_getfd #define isc_socket_getname isc__socket_getname #define isc_socket_gettag isc__socket_gettag #define isc_socket_getpeername isc__socket_getpeername @@ -146,11 +149,15 @@ #define isc_task_gettag isc__task_gettag #define isc_task_getcurrenttime isc__task_getcurrenttime #define isc_taskmgr_create isc__taskmgr_create +#define isc_taskmgr_setmode isc__taskmgr_setmode +#define isc_taskmgr_mode isc__taskmgr_mode #define isc_taskmgr_destroy isc__taskmgr_destroy #define isc_taskmgr_setexcltask isc__taskmgr_setexcltask #define isc_taskmgr_excltask isc__taskmgr_excltask #define isc_task_beginexclusive isc__task_beginexclusive #define isc_task_endexclusive isc__task_endexclusive +#define isc_task_setprivilege isc__task_setprivilege +#define isc_task_privilege isc__task_privilege #define isc_timer_create isc__timer_create #define isc_timer_reset isc__timer_reset diff --git a/contrib/bind9/lib/isc/include/isc/pool.h b/contrib/bind9/lib/isc/include/isc/pool.h new file mode 100644 index 00000000000..7b33c37bb79 --- /dev/null +++ b/contrib/bind9/lib/isc/include/isc/pool.h @@ -0,0 +1,149 @@ +/* + * Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC") + * + * Permission to use, copy, modify, and/or 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 ISC DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL ISC 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. + */ + +#ifndef ISC_OBJPOOL_H +#define ISC_OBJPOOL_H 1 + +/***** + ***** Module Info + *****/ + +/*! \file isc/pool.h + * \brief An object pool is a mechanism for sharing a small pool of + * fungible objects among a large number of objects that depend on them. + * + * This is useful, for example, when it causes performance problems for + * large number of zones to share a single memory context or task object, + * but it would create a different set of problems for them each to have an + * independent task or memory context. + */ + + +/*** + *** Imports. + ***/ + +#include +#include +#include + +ISC_LANG_BEGINDECLS + +/***** + ***** Types. + *****/ + +typedef void +(*isc_pooldeallocator_t)(void **object); + +typedef isc_result_t +(*isc_poolinitializer_t)(void **target, void *arg); + +typedef struct isc_pool isc_pool_t; + +/***** + ***** Functions. + *****/ + +isc_result_t +isc_pool_create(isc_mem_t *mctx, unsigned int count, + isc_pooldeallocator_t free, + isc_poolinitializer_t init, void *initarg, + isc_pool_t **poolp); +/*%< + * Create a pool of "count" object pointers. If 'free' is not NULL, + * it points to a function that will detach the objects. 'init' + * points to a function that will initialize the arguments, and + * 'arg' to an argument to be passed into that function (for example, + * a relevant manager or context object). + * + * Requires: + * + *\li 'mctx' is a valid memory context. + * + *\li init != NULL + * + *\li poolp != NULL && *poolp == NULL + * + * Ensures: + * + *\li On success, '*poolp' points to the new object pool. + * + * Returns: + * + *\li #ISC_R_SUCCESS + *\li #ISC_R_NOMEMORY + *\li #ISC_R_UNEXPECTED + */ + +void * +isc_pool_get(isc_pool_t *pool); +/*%< + * Returns a pointer to an object from the pool. Currently the object + * is chosen from the pool at random. (This may be changed in the future + * to something that guaratees balance.) + */ + +int +isc_pool_count(isc_pool_t *pool); +/*%< + * Returns the number of objcts in the pool 'pool'. + */ + +isc_result_t +isc_pool_expand(isc_pool_t **sourcep, unsigned int count, isc_pool_t **targetp); + +/*%< + * If 'size' is larger than the number of objects in the pool pointed to by + * 'sourcep', then a new pool of size 'count' is allocated, the existing + * objects are copied into it, additional ones created to bring the + * total number up to 'count', and the resulting pool is attached to + * 'targetp'. + * + * If 'count' is less than or equal to the number of objects in 'source', then + * 'sourcep' is attached to 'targetp' without any other action being taken. + * + * In either case, 'sourcep' is detached. + * + * Requires: + * + * \li 'sourcep' is not NULL and '*source' is not NULL + * \li 'targetp' is not NULL and '*source' is NULL + * + * Ensures: + * + * \li On success, '*targetp' points to a valid task pool. + * \li On success, '*sourcep' points to NULL. + * + * Returns: + * + * \li #ISC_R_SUCCESS + * \li #ISC_R_NOMEMORY + */ + +void +isc_pool_destroy(isc_pool_t **poolp); +/*%< + * Destroy a task pool. The tasks in the pool are detached but not + * shut down. + * + * Requires: + * \li '*poolp' is a valid task pool. + */ + +ISC_LANG_ENDDECLS + +#endif /* ISC_OBJPOOL_H */ diff --git a/contrib/bind9/lib/isc/include/isc/queue.h b/contrib/bind9/lib/isc/include/isc/queue.h new file mode 100644 index 00000000000..1cc6c12a4f1 --- /dev/null +++ b/contrib/bind9/lib/isc/include/isc/queue.h @@ -0,0 +1,165 @@ +/* + * Copyright (C) 2011-2013 Internet Systems Consortium, Inc. ("ISC") + * + * Permission to use, copy, modify, and/or 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 ISC DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL ISC 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. + */ + +/* $Id$ */ + +/* + * This is a generic implementation of a two-lock concurrent queue. + * There are built-in mutex locks for the head and tail of the queue, + * allowing elements to be safely added and removed at the same time. + * + * NULL is "end of list" + * -1 is "not linked" + */ + +#ifndef ISC_QUEUE_H +#define ISC_QUEUE_H 1 +#include +#include +#include + +#ifdef ISC_QUEUE_CHECKINIT +#define ISC_QLINK_INSIST(x) ISC_INSIST(x) +#else +#define ISC_QLINK_INSIST(x) (void)0 +#endif + +#define ISC_QLINK(type) struct { type *prev, *next; } + +#define ISC_QLINK_INIT(elt, link) \ + do { \ + (elt)->link.next = (elt)->link.prev = (void *)(-1); \ + } while(0) + +#define ISC_QLINK_LINKED(elt, link) ((void*)(elt)->link.next != (void*)(-1)) + +#define ISC_QUEUE(type) struct { \ + type *head, *tail; \ + isc_mutex_t headlock, taillock; \ +} + +#define ISC_QUEUE_INIT(queue, link) \ + do { \ + (void) isc_mutex_init(&(queue).taillock); \ + (void) isc_mutex_init(&(queue).headlock); \ + (queue).tail = (queue).head = NULL; \ + } while (0) + +#define ISC_QUEUE_EMPTY(queue) ISC_TF((queue).head == NULL) + +#define ISC_QUEUE_DESTROY(queue) \ + do { \ + ISC_QLINK_INSIST(ISC_QUEUE_EMPTY(queue)); \ + (void) isc_mutex_destroy(&(queue).taillock); \ + (void) isc_mutex_destroy(&(queue).headlock); \ + } while (0) + +/* + * queues are meant to separate the locks at either end. For best effect, that + * means keeping the ends separate - i.e. non-empty queues work best. + * + * a push to an empty queue has to take the pop lock to update + * the pop side of the queue. + * Popping the last entry has to take the push lock to update + * the push side of the queue. + * + * The order is (pop, push), because a pop is presumably in the + * latency path and a push is when we're done. + * + * We do an MT hot test in push to see if we need both locks, so we can + * acquire them in order. Hopefully that makes the case where we get + * the push lock and find we need the pop lock (and have to release it) rare. + * + * > 1 entry - no collision, push works on one end, pop on the other + * 0 entry - headlock race + * pop wins - return(NULL), push adds new as both head/tail + * push wins - updates head/tail, becomes 1 entry case. + * 1 entry - taillock race + * pop wins - return(pop) sets head/tail NULL, becomes 0 entry case + * push wins - updates {head,tail}->link.next, pop updates head + * with new ->link.next and doesn't update tail + * + */ +#define ISC_QUEUE_PUSH(queue, elt, link) \ + do { \ + isc_boolean_t headlocked = ISC_FALSE; \ + ISC_QLINK_INSIST(!ISC_QLINK_LINKED(elt, link)); \ + if ((queue).head == NULL) { \ + LOCK(&(queue).headlock); \ + headlocked = ISC_TRUE; \ + } \ + LOCK(&(queue).taillock); \ + if ((queue).tail == NULL && !headlocked) { \ + UNLOCK(&(queue).taillock); \ + LOCK(&(queue).headlock); \ + LOCK(&(queue).taillock); \ + headlocked = ISC_TRUE; \ + } \ + (elt)->link.prev = (queue).tail; \ + (elt)->link.next = NULL; \ + if ((queue).tail != NULL) \ + (queue).tail->link.next = (elt); \ + (queue).tail = (elt); \ + UNLOCK(&(queue).taillock); \ + if (headlocked) { \ + if ((queue).head == NULL) \ + (queue).head = (elt); \ + UNLOCK(&(queue).headlock); \ + } \ + } while (0) + +#define ISC_QUEUE_POP(queue, link, ret) \ + do { \ + LOCK(&(queue).headlock); \ + ret = (queue).head; \ + while (ret != NULL) { \ + if (ret->link.next == NULL) { \ + LOCK(&(queue).taillock); \ + if (ret->link.next == NULL) { \ + (queue).head = (queue).tail = NULL; \ + UNLOCK(&(queue).taillock); \ + break; \ + }\ + UNLOCK(&(queue).taillock); \ + } \ + (queue).head = ret->link.next; \ + (queue).head->link.prev = NULL; \ + break; \ + } \ + UNLOCK(&(queue).headlock); \ + if (ret != NULL) \ + (ret)->link.next = (ret)->link.prev = (void *)(-1); \ + } while(0) + +#define ISC_QUEUE_UNLINK(queue, elt, link) \ + do { \ + ISC_QLINK_INSIST(ISC_QLINK_LINKED(elt, link)); \ + LOCK(&(queue).headlock); \ + LOCK(&(queue).taillock); \ + if ((elt)->link.prev == NULL) \ + (queue).head = (elt)->link.next; \ + else \ + (elt)->link.prev->link.next = (elt)->link.next; \ + if ((elt)->link.next == NULL) \ + (queue).tail = (elt)->link.prev; \ + else \ + (elt)->link.next->link.prev = (elt)->link.prev; \ + UNLOCK(&(queue).taillock); \ + UNLOCK(&(queue).headlock); \ + (elt)->link.next = (elt)->link.prev = (void *)(-1); \ + } while(0) + +#endif /* ISC_QUEUE_H */ diff --git a/contrib/bind9/lib/isc/include/isc/radix.h b/contrib/bind9/lib/isc/include/isc/radix.h index 6b413a23b90..47512c72288 100644 --- a/contrib/bind9/lib/isc/include/isc/radix.h +++ b/contrib/bind9/lib/isc/include/isc/radix.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007, 2008 Internet Systems Consortium, Inc. ("ISC") + * Copyright (C) 2007, 2008, 2013 Internet Systems Consortium, Inc. ("ISC") * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -54,13 +54,14 @@ } while(0) typedef struct isc_prefix { - unsigned int family; /* AF_INET | AF_INET6, or AF_UNSPEC for "any" */ - unsigned int bitlen; /* 0 for "any" */ - isc_refcount_t refcount; - union { + isc_mem_t *mctx; + unsigned int family; /* AF_INET | AF_INET6, or AF_UNSPEC for "any" */ + unsigned int bitlen; /* 0 for "any" */ + isc_refcount_t refcount; + union { struct in_addr sin; struct in6_addr sin6; - } add; + } add; } isc_prefix_t; typedef void (*isc_radix_destroyfunc_t)(void *); @@ -90,12 +91,13 @@ typedef void (*isc_radix_processfunc_t)(isc_prefix_t *, void **); #define ISC_IS6(family) ((family) == AF_INET6 ? 1 : 0) typedef struct isc_radix_node { - isc_uint32_t bit; /* bit length of the prefix */ - isc_prefix_t *prefix; /* who we are in radix tree */ - struct isc_radix_node *l, *r; /* left and right children */ - struct isc_radix_node *parent; /* may be used */ - void *data[2]; /* pointers to IPv4 and IPV6 data */ - int node_num[2]; /* which node this was in the tree, + isc_mem_t *mctx; + isc_uint32_t bit; /* bit length of the prefix */ + isc_prefix_t *prefix; /* who we are in radix tree */ + struct isc_radix_node *l, *r; /* left and right children */ + struct isc_radix_node *parent; /* may be used */ + void *data[2]; /* pointers to IPv4 and IPV6 data */ + int node_num[2]; /* which node this was in the tree, or -1 for glue nodes */ } isc_radix_node_t; @@ -103,12 +105,12 @@ typedef struct isc_radix_node { #define RADIX_TREE_VALID(a) ISC_MAGIC_VALID(a, RADIX_TREE_MAGIC); typedef struct isc_radix_tree { - unsigned int magic; - isc_mem_t *mctx; - isc_radix_node_t *head; - isc_uint32_t maxbits; /* for IP, 32 bit addresses */ - int num_active_node; /* for debugging purposes */ - int num_added_node; /* total number of nodes */ + unsigned int magic; + isc_mem_t *mctx; + isc_radix_node_t *head; + isc_uint32_t maxbits; /* for IP, 32 bit addresses */ + int num_active_node; /* for debugging purposes */ + int num_added_node; /* total number of nodes */ } isc_radix_tree_t; isc_result_t diff --git a/contrib/bind9/lib/isc/include/isc/socket.h b/contrib/bind9/lib/isc/include/isc/socket.h index 4111ec2c6be..9d086b45201 100644 --- a/contrib/bind9/lib/isc/include/isc/socket.h +++ b/contrib/bind9/lib/isc/include/isc/socket.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004-2009, 2012 Internet Systems Consortium, Inc. ("ISC") + * Copyright (C) 2004-2009, 2011, 2012 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 1998-2002 Internet Software Consortium. * * Permission to use, copy, modify, and/or distribute this software for any @@ -283,12 +283,20 @@ typedef struct isc_socketmethods { isc_task_t *task, isc_taskaction_t action, const void *arg, isc_sockaddr_t *address, struct in6_pktinfo *pktinfo); + isc_result_t (*sendto2)(isc_socket_t *sock, isc_region_t *region, + isc_task_t *task, isc_sockaddr_t *address, + struct in6_pktinfo *pktinfo, + isc_socketevent_t *event, + unsigned int flags); isc_result_t (*connect)(isc_socket_t *sock, isc_sockaddr_t *addr, isc_task_t *task, isc_taskaction_t action, const void *arg); isc_result_t (*recv)(isc_socket_t *sock, isc_region_t *region, unsigned int minimum, isc_task_t *task, isc_taskaction_t action, const void *arg); + isc_result_t (*recv2)(isc_socket_t *sock, isc_region_t *region, + unsigned int minimum, isc_task_t *task, + isc_socketevent_t *event, unsigned int flags); void (*cancel)(isc_socket_t *sock, isc_task_t *task, unsigned int how); isc_result_t (*getsockname)(isc_socket_t *sock, @@ -296,6 +304,9 @@ typedef struct isc_socketmethods { isc_sockettype_t (*gettype)(isc_socket_t *sock); void (*ipv6only)(isc_socket_t *sock, isc_boolean_t yes); isc_result_t (*fdwatchpoke)(isc_socket_t *sock, int flags); + isc_result_t (*dup)(isc_socket_t *socket, + isc_socket_t **socketp); + int (*getfd)(isc_socket_t *socket); } isc_socketmethods_t; /*% @@ -449,6 +460,12 @@ isc_socket_create(isc_socketmgr_t *manager, *\li #ISC_R_UNEXPECTED */ +isc_result_t +isc_socket_dup(isc_socket_t *sock0, isc_socket_t **socketp); +/*%< + * Duplicate an existing socket, reusing its file descriptor. + */ + void isc_socket_cancel(isc_socket_t *sock, isc_task_t *task, unsigned int how); @@ -1102,6 +1119,11 @@ void *isc_socket_gettag(isc_socket_t *socket); * Get the tag associated with a socket, if any. */ +int isc_socket_getfd(isc_socket_t *socket); +/*%< + * Get the file descriptor associated with a socket + */ + void isc__socketmgr_setreserved(isc_socketmgr_t *mgr, isc_uint32_t); /*%< diff --git a/contrib/bind9/lib/isc/include/isc/task.h b/contrib/bind9/lib/isc/include/isc/task.h index ced70590b16..7abf2ef2be6 100644 --- a/contrib/bind9/lib/isc/include/isc/task.h +++ b/contrib/bind9/lib/isc/include/isc/task.h @@ -88,6 +88,7 @@ #define ISC_TASKEVENT_FIRSTEVENT (ISC_EVENTCLASS_TASK + 0) #define ISC_TASKEVENT_SHUTDOWN (ISC_EVENTCLASS_TASK + 1) +#define ISC_TASKEVENT_TEST (ISC_EVENTCLASS_TASK + 1) #define ISC_TASKEVENT_LASTEVENT (ISC_EVENTCLASS_TASK + 65535) /***** @@ -100,9 +101,17 @@ ISC_LANG_BEGINDECLS *** Types ***/ +typedef enum { + isc_taskmgrmode_normal = 0, + isc_taskmgrmode_privileged +} isc_taskmgrmode_t; + /*% Task and task manager methods */ typedef struct isc_taskmgrmethods { void (*destroy)(isc_taskmgr_t **managerp); + void (*setmode)(isc_taskmgr_t *manager, + isc_taskmgrmode_t mode); + isc_taskmgrmode_t (*mode)(isc_taskmgr_t *manager); isc_result_t (*taskcreate)(isc_taskmgr_t *manager, unsigned int quantum, isc_task_t **taskp); @@ -129,6 +138,8 @@ typedef struct isc_taskmethods { void *tag); isc_result_t (*beginexclusive)(isc_task_t *task); void (*endexclusive)(isc_task_t *task); + void (*setprivilege)(isc_task_t *task, isc_boolean_t priv); + isc_boolean_t (*privilege)(isc_task_t *task); } isc_taskmethods_t; /*% @@ -613,6 +624,32 @@ isc_task_exiting(isc_task_t *t); *\li 'task' is a valid task. */ +void +isc_task_setprivilege(isc_task_t *task, isc_boolean_t priv); +/*%< + * Set or unset the task's "privileged" flag depending on the value of + * 'priv'. + * + * Under normal circumstances this flag has no effect on the task behavior, + * but when the task manager has been set to privileged exeuction mode via + * isc_taskmgr_setmode(), only tasks with the flag set will be executed, + * and all other tasks will wait until they're done. Once all privileged + * tasks have finished executing, the task manager will automatically + * return to normal execution mode and nonprivileged task can resume. + * + * Requires: + *\li 'task' is a valid task. + */ + +isc_boolean_t +isc_task_privilege(isc_task_t *task); +/*%< + * Returns the current value of the task's privilege flag. + * + * Requires: + *\li 'task' is a valid task. + */ + /***** ***** Task Manager. *****/ @@ -665,6 +702,31 @@ isc_taskmgr_create(isc_mem_t *mctx, unsigned int workers, * manager shutting down. */ +void +isc_taskmgr_setmode(isc_taskmgr_t *manager, isc_taskmgrmode_t mode); + +isc_taskmgrmode_t +isc_taskmgr_mode(isc_taskmgr_t *manager); +/*%< + * Set/get the current operating mode of the task manager. Valid modes are: + * + *\li isc_taskmgrmode_normal + *\li isc_taskmgrmode_privileged + * + * In privileged execution mode, only tasks that have had the "privilege" + * flag set via isc_task_setprivilege() can be executed. When all such + * tasks are complete, the manager automatically returns to normal mode + * and proceeds with running non-privileged ready tasks. This means it is + * necessary to have at least one privileged task waiting on the ready + * queue *before* setting the manager into privileged execution mode, + * which in turn means the task which calls this function should be in + * task-exclusive mode when it does so. + * + * Requires: + * + *\li 'manager' is a valid task manager. + */ + void isc_taskmgr_destroy(isc_taskmgr_t **managerp); /*%< diff --git a/contrib/bind9/lib/isc/include/isc/taskpool.h b/contrib/bind9/lib/isc/include/isc/taskpool.h index 64c739a211a..46f395ea246 100644 --- a/contrib/bind9/lib/isc/include/isc/taskpool.h +++ b/contrib/bind9/lib/isc/include/isc/taskpool.h @@ -139,6 +139,19 @@ isc_taskpool_destroy(isc_taskpool_t **poolp); * \li '*poolp' is a valid task pool. */ +void +isc_taskpool_setprivilege(isc_taskpool_t *pool, isc_boolean_t priv); +/*%< + * Set the privilege flag on all tasks in 'pool' to 'priv'. If 'priv' is + * true, then when the task manager is set into privileged mode, only + * tasks wihin this pool will be able to execute. (Note: It is important + * to turn the pool tasks' privilege back off before the last task finishes + * executing.) + * + * Requires: + * \li 'pool' is a valid task pool. + */ + ISC_LANG_ENDDECLS #endif /* ISC_TASKPOOL_H */ diff --git a/contrib/bind9/lib/isc/log.c b/contrib/bind9/lib/isc/log.c index f1c925cd3fc..024d97c6a9f 100644 --- a/contrib/bind9/lib/isc/log.c +++ b/contrib/bind9/lib/isc/log.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004-2007, 2009, 2011, 2012 Internet Systems Consortium, Inc. ("ISC") + * Copyright (C) 2004-2007, 2009, 2011-2013 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 1999-2003 Internet Software Consortium. * * Permission to use, copy, modify, and/or distribute this software for any @@ -275,7 +275,8 @@ isc_log_create(isc_mem_t *mctx, isc_log_t **lctxp, isc_logconfig_t **lcfgp) { lctx = isc_mem_get(mctx, sizeof(*lctx)); if (lctx != NULL) { - lctx->mctx = mctx; + lctx->mctx = NULL; + isc_mem_attach(mctx, &lctx->mctx); lctx->categories = NULL; lctx->category_count = 0; lctx->modules = NULL; @@ -286,7 +287,7 @@ isc_log_create(isc_mem_t *mctx, isc_log_t **lctxp, isc_logconfig_t **lcfgp) { result = isc_mutex_init(&lctx->lock); if (result != ISC_R_SUCCESS) { - isc_mem_put(mctx, lctx, sizeof(*lctx)); + isc_mem_putanddetach(&mctx, lctx, sizeof(*lctx)); return (result); } @@ -493,7 +494,7 @@ isc_log_destroy(isc_log_t **lctxp) { lctx->mctx = NULL; lctx->magic = 0; - isc_mem_put(mctx, lctx, sizeof(*lctx)); + isc_mem_putanddetach(&mctx, lctx, sizeof(*lctx)); *lctxp = NULL; } diff --git a/contrib/bind9/lib/isc/pool.c b/contrib/bind9/lib/isc/pool.c new file mode 100644 index 00000000000..509abcb418d --- /dev/null +++ b/contrib/bind9/lib/isc/pool.c @@ -0,0 +1,177 @@ +/* + * Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC") + * + * Permission to use, copy, modify, and/or 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 ISC DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL ISC 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. + */ + +/* $Id$ */ + +/*! \file */ + +#include + +#include + +#include +#include +#include +#include + +/*** + *** Types. + ***/ + +struct isc_pool { + isc_mem_t * mctx; + unsigned int count; + isc_pooldeallocator_t free; + isc_poolinitializer_t init; + void * initarg; + void ** pool; +}; + +/*** + *** Functions. + ***/ + +static isc_result_t +alloc_pool(isc_mem_t *mctx, unsigned int count, isc_pool_t **poolp) { + isc_pool_t *pool; + + pool = isc_mem_get(mctx, sizeof(*pool)); + if (pool == NULL) + return (ISC_R_NOMEMORY); + pool->count = count; + pool->free = NULL; + pool->init = NULL; + pool->initarg = NULL; + pool->mctx = NULL; + isc_mem_attach(mctx, &pool->mctx); + pool->pool = isc_mem_get(mctx, count * sizeof(void *)); + if (pool->pool == NULL) { + isc_mem_put(mctx, pool, sizeof(*pool)); + return (ISC_R_NOMEMORY); + } + memset(pool->pool, 0, count * sizeof(void *)); + + *poolp = pool; + return (ISC_R_SUCCESS); +} + +isc_result_t +isc_pool_create(isc_mem_t *mctx, unsigned int count, + isc_pooldeallocator_t free, + isc_poolinitializer_t init, void *initarg, + isc_pool_t **poolp) +{ + isc_pool_t *pool = NULL; + isc_result_t result; + unsigned int i; + + INSIST(count > 0); + + /* Allocate the pool structure */ + result = alloc_pool(mctx, count, &pool); + if (result != ISC_R_SUCCESS) + return (result); + + pool->free = free; + pool->init = init; + pool->initarg = initarg; + + /* Populate the pool */ + for (i = 0; i < count; i++) { + result = init(&pool->pool[i], initarg); + if (result != ISC_R_SUCCESS) { + isc_pool_destroy(&pool); + return (result); + } + } + + *poolp = pool; + return (ISC_R_SUCCESS); +} + +void * +isc_pool_get(isc_pool_t *pool) { + isc_uint32_t i; + isc_random_get(&i); + return (pool->pool[i % pool->count]); +} + +int +isc_pool_count(isc_pool_t *pool) { + REQUIRE(pool != NULL); + return (pool->count); +} + +isc_result_t +isc_pool_expand(isc_pool_t **sourcep, unsigned int count, + isc_pool_t **targetp) +{ + isc_result_t result; + isc_pool_t *pool; + + REQUIRE(sourcep != NULL && *sourcep != NULL); + REQUIRE(targetp != NULL && *targetp == NULL); + + pool = *sourcep; + if (count > pool->count) { + isc_pool_t *newpool = NULL; + unsigned int i; + + /* Allocate a new pool structure */ + result = alloc_pool(pool->mctx, count, &newpool); + if (result != ISC_R_SUCCESS) + return (result); + + newpool->free = pool->free; + newpool->init = pool->init; + newpool->initarg = pool->initarg; + + /* Copy over the objects from the old pool */ + for (i = 0; i < pool->count; i++) { + newpool->pool[i] = pool->pool[i]; + pool->pool[i] = NULL; + } + + /* Populate the new entries */ + for (i = pool->count; i < count; i++) { + result = pool->init(&newpool->pool[i], pool->initarg); + if (result != ISC_R_SUCCESS) { + isc_pool_destroy(&pool); + return (result); + } + } + + isc_pool_destroy(&pool); + pool = newpool; + } + + *sourcep = NULL; + *targetp = pool; + return (ISC_R_SUCCESS); +} + +void +isc_pool_destroy(isc_pool_t **poolp) { + unsigned int i; + isc_pool_t *pool = *poolp; + for (i = 0; i < pool->count; i++) { + if (pool->free != NULL && pool->pool[i] != NULL) + pool->free(&pool->pool[i]); + } + isc_mem_put(pool->mctx, pool->pool, pool->count * sizeof(void *)); + isc_mem_putanddetach(&pool->mctx, pool, sizeof(*pool)); + *poolp = NULL; +} diff --git a/contrib/bind9/lib/isc/radix.c b/contrib/bind9/lib/isc/radix.c index ac211efb6a8..35088788614 100644 --- a/contrib/bind9/lib/isc/radix.c +++ b/contrib/bind9/lib/isc/radix.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007-2009, 2011, 2012 Internet Systems Consortium, Inc. ("ISC") + * Copyright (C) 2007-2009, 2011-2013 Internet Systems Consortium, Inc. ("ISC") * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -34,7 +34,7 @@ _new_prefix(isc_mem_t *mctx, isc_prefix_t **target, int family, void *dest, int bitlen); static void -_deref_prefix(isc_mem_t *mctx, isc_prefix_t *prefix); +_deref_prefix(isc_prefix_t *prefix); static isc_result_t _ref_prefix(isc_mem_t *mctx, isc_prefix_t **target, isc_prefix_t *prefix); @@ -70,6 +70,8 @@ _new_prefix(isc_mem_t *mctx, isc_prefix_t **target, int family, void *dest, } prefix->family = family; + prefix->mctx = NULL; + isc_mem_attach(mctx, &prefix->mctx); isc_refcount_init(&prefix->refcount, 1); @@ -78,7 +80,7 @@ _new_prefix(isc_mem_t *mctx, isc_prefix_t **target, int family, void *dest, } static void -_deref_prefix(isc_mem_t *mctx, isc_prefix_t *prefix) { +_deref_prefix(isc_prefix_t *prefix) { int refs; if (prefix == NULL) @@ -88,7 +90,8 @@ _deref_prefix(isc_mem_t *mctx, isc_prefix_t *prefix) { if (refs <= 0) { isc_refcount_destroy(&prefix->refcount); - isc_mem_put(mctx, prefix, sizeof(isc_prefix_t)); + isc_mem_putanddetach(&prefix->mctx, prefix, + sizeof(isc_prefix_t)); } } @@ -109,7 +112,7 @@ _ref_prefix(isc_mem_t *mctx, isc_prefix_t **target, isc_prefix_t *prefix) { isc_result_t ret; ret = _new_prefix(mctx, target, prefix->family, &prefix->add, prefix->bitlen); - return ret; + return (ret); } isc_refcount_increment(&prefix->refcount, NULL); @@ -146,7 +149,8 @@ isc_radix_create(isc_mem_t *mctx, isc_radix_tree_t **target, int maxbits) { if (radix == NULL) return (ISC_R_NOMEMORY); - radix->mctx = mctx; + radix->mctx = NULL; + isc_mem_attach(mctx, &radix->mctx); radix->maxbits = maxbits; radix->head = NULL; radix->num_active_node = 0; @@ -168,7 +172,6 @@ _clear_radix(isc_radix_tree_t *radix, isc_radix_destroyfunc_t func) { REQUIRE(radix != NULL); if (radix->head != NULL) { - isc_radix_node_t *Xstack[RADIX_MAXBITS+1]; isc_radix_node_t **Xsp = Xstack; isc_radix_node_t *Xrn = radix->head; @@ -178,7 +181,7 @@ _clear_radix(isc_radix_tree_t *radix, isc_radix_destroyfunc_t func) { isc_radix_node_t *r = Xrn->r; if (Xrn->prefix != NULL) { - _deref_prefix(radix->mctx, Xrn->prefix); + _deref_prefix(Xrn->prefix); if (func != NULL && (Xrn->data[0] != NULL || Xrn->data[1] != NULL)) func(Xrn->data); @@ -209,11 +212,10 @@ _clear_radix(isc_radix_tree_t *radix, isc_radix_destroyfunc_t func) { void -isc_radix_destroy(isc_radix_tree_t *radix, isc_radix_destroyfunc_t func) -{ +isc_radix_destroy(isc_radix_tree_t *radix, isc_radix_destroyfunc_t func) { REQUIRE(radix != NULL); _clear_radix(radix, func); - isc_mem_put(radix->mctx, radix, sizeof(*radix)); + isc_mem_putanddetach(&radix->mctx, radix, sizeof(*radix)); } @@ -221,8 +223,7 @@ isc_radix_destroy(isc_radix_tree_t *radix, isc_radix_destroyfunc_t func) * func will be called as func(node->prefix, node->data) */ void -isc_radix_process(isc_radix_tree_t *radix, isc_radix_processfunc_t func) -{ +isc_radix_process(isc_radix_tree_t *radix, isc_radix_processfunc_t func) { isc_radix_node_t *node; REQUIRE(func != NULL); @@ -461,8 +462,8 @@ isc_radix_insert(isc_radix_tree_t *radix, isc_radix_node_t **target, *target = node; return (ISC_R_SUCCESS); } else { - result = - _ref_prefix(radix->mctx, &node->prefix, prefix); + result = _ref_prefix(radix->mctx, + &node->prefix, prefix); if (result != ISC_R_SUCCESS) return (result); } @@ -623,7 +624,7 @@ isc_radix_remove(isc_radix_tree_t *radix, isc_radix_node_t *node) { * make sure there is a prefix associated with it! */ if (node->prefix != NULL) - _deref_prefix(radix->mctx, node->prefix); + _deref_prefix(node->prefix); node->prefix = NULL; node->data[0] = node->data[1] = NULL; @@ -632,7 +633,7 @@ isc_radix_remove(isc_radix_tree_t *radix, isc_radix_node_t *node) { if (node->r == NULL && node->l == NULL) { parent = node->parent; - _deref_prefix(radix->mctx, node->prefix); + _deref_prefix(node->prefix); isc_mem_put(radix->mctx, node, sizeof(*node)); radix->num_active_node--; @@ -680,7 +681,7 @@ isc_radix_remove(isc_radix_tree_t *radix, isc_radix_node_t *node) { parent = node->parent; child->parent = parent; - _deref_prefix(radix->mctx, node->prefix); + _deref_prefix(node->prefix); isc_mem_put(radix->mctx, node, sizeof(*node)); radix->num_active_node--; diff --git a/contrib/bind9/lib/isc/socket_api.c b/contrib/bind9/lib/isc/socket_api.c index e97a93149cc..1fba3e0ac19 100644 --- a/contrib/bind9/lib/isc/socket_api.c +++ b/contrib/bind9/lib/isc/socket_api.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009, 2012 Internet Systems Consortium, Inc. ("ISC") + * Copyright (C) 2009, 2011, 2012 Internet Systems Consortium, Inc. ("ISC") * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -140,6 +140,18 @@ isc_socket_sendto(isc_socket_t *sock, isc_region_t *region, isc_task_t *task, pktinfo)); } +isc_result_t +isc_socket_sendto2(isc_socket_t *sock, isc_region_t *region, + isc_task_t *task, isc_sockaddr_t *address, + struct in6_pktinfo *pktinfo, isc_socketevent_t *event, + unsigned int flags) +{ + REQUIRE(ISCAPI_SOCKET_VALID(sock)); + + return (sock->methods->sendto2(sock, region, task, address, + pktinfo, event, flags)); +} + isc_result_t isc_socket_connect(isc_socket_t *sock, isc_sockaddr_t *addr, isc_task_t *task, isc_taskaction_t action, const void *arg) @@ -158,6 +170,17 @@ isc_socket_recv(isc_socket_t *sock, isc_region_t *region, unsigned int minimum, return (sock->methods->recv(sock, region, minimum, task, action, arg)); } +isc_result_t +isc_socket_recv2(isc_socket_t *sock, isc_region_t *region, + unsigned int minimum, isc_task_t *task, + isc_socketevent_t *event, unsigned int flags) +{ + REQUIRE(ISCAPI_SOCKET_VALID(sock)); + + return (sock->methods->recv2(sock, region, minimum, task, + event, flags)); +} + void isc_socket_cancel(isc_socket_t *sock, isc_task_t *task, unsigned int how) { REQUIRE(ISCAPI_SOCKET_VALID(sock)); @@ -214,3 +237,18 @@ isc_socket_fdwatchpoke(isc_socket_t *sock, int flags) return(sock->methods->fdwatchpoke(sock, flags)); } + +isc_result_t +isc_socket_dup(isc_socket_t *sock, isc_socket_t **socketp) { + REQUIRE(ISCAPI_SOCKET_VALID(sock)); + REQUIRE(socketp != NULL && *socketp == NULL); + + return(sock->methods->dup(sock, socketp)); +} + +int +isc_socket_getfd(isc_socket_t *sock) { + REQUIRE(ISCAPI_SOCKET_VALID(sock)); + + return(sock->methods->getfd(sock)); +} diff --git a/contrib/bind9/lib/isc/task.c b/contrib/bind9/lib/isc/task.c index 94f1c6d6dab..b743271339f 100644 --- a/contrib/bind9/lib/isc/task.c +++ b/contrib/bind9/lib/isc/task.c @@ -64,9 +64,7 @@ #endif /* ISC_PLATFORM_USETHREADS */ #endif /* BIND9 */ -#ifndef USE_WORKER_THREADS #include "task_p.h" -#endif /* USE_WORKER_THREADS */ #ifdef ISC_TASK_TRACE #define XTRACE(m) fprintf(stderr, "task %p thread %lu: %s\n", \ @@ -120,9 +118,11 @@ struct isc__task { /* Locked by task manager lock. */ LINK(isc__task_t) link; LINK(isc__task_t) ready_link; + LINK(isc__task_t) ready_priority_link; }; #define TASK_F_SHUTTINGDOWN 0x01 +#define TASK_F_PRIVILEGED 0x02 #define TASK_SHUTTINGDOWN(t) (((t)->flags & TASK_F_SHUTTINGDOWN) \ != 0) @@ -145,11 +145,15 @@ struct isc__taskmgr { unsigned int default_quantum; LIST(isc__task_t) tasks; isc__tasklist_t ready_tasks; + isc__tasklist_t ready_priority_tasks; + isc_taskmgrmode_t mode; #ifdef ISC_PLATFORM_USETHREADS isc_condition_t work_available; isc_condition_t exclusive_granted; + isc_condition_t paused; #endif /* ISC_PLATFORM_USETHREADS */ unsigned int tasks_running; + isc_boolean_t pause_requested; isc_boolean_t exclusive_requested; isc_boolean_t exiting; isc__task_t *excl; @@ -230,6 +234,23 @@ ISC_TASKFUNC_SCOPE isc_result_t isc__task_beginexclusive(isc_task_t *task); ISC_TASKFUNC_SCOPE void isc__task_endexclusive(isc_task_t *task0); +ISC_TASKFUNC_SCOPE void +isc__task_setprivilege(isc_task_t *task0, isc_boolean_t priv); +ISC_TASKFUNC_SCOPE isc_boolean_t +isc__task_privilege(isc_task_t *task0); +ISC_TASKFUNC_SCOPE void +isc__taskmgr_setmode(isc_taskmgr_t *manager0, isc_taskmgrmode_t mode); +ISC_TASKFUNC_SCOPE isc_taskmgrmode_t +isc__taskmgr_mode(isc_taskmgr_t *manager0); + +static inline isc_boolean_t +empty_readyq(isc__taskmgr_t *manager); + +static inline isc__task_t * +pop_readyq(isc__taskmgr_t *manager); + +static inline void +push_readyq(isc__taskmgr_t *manager, isc__task_t *task); static struct isc__taskmethods { isc_taskmethods_t methods; @@ -254,7 +275,9 @@ static struct isc__taskmethods { isc__task_purge, isc__task_purgerange, isc__task_beginexclusive, - isc__task_endexclusive + isc__task_endexclusive, + isc__task_setprivilege, + isc__task_privilege } #ifndef BIND9 , @@ -266,6 +289,8 @@ static struct isc__taskmethods { static isc_taskmgrmethods_t taskmgrmethods = { isc__taskmgr_destroy, + isc__taskmgr_setmode, + isc__taskmgr_mode, isc__task_create, isc__taskmgr_setexcltask, isc__taskmgr_excltask @@ -340,6 +365,7 @@ isc__task_create(isc_taskmgr_t *manager0, unsigned int quantum, task->tag = NULL; INIT_LINK(task, link); INIT_LINK(task, ready_link); + INIT_LINK(task, ready_priority_link); exiting = ISC_FALSE; LOCK(&manager->lock); @@ -407,6 +433,7 @@ task_shutdown(isc__task_t *task) { } INSIST(task->state == task_state_ready || task->state == task_state_running); + /* * Note that we post shutdown events LIFO. */ @@ -422,9 +449,17 @@ task_shutdown(isc__task_t *task) { return (was_idle); } +/* + * Moves a task onto the appropriate run queue. + * + * Caller must NOT hold manager lock. + */ static inline void task_ready(isc__task_t *task) { isc__taskmgr_t *manager = task->manager; +#ifdef USE_WORKER_THREADS + isc_boolean_t has_privilege = isc__task_privilege((isc_task_t *) task); +#endif /* USE_WORKER_THREADS */ REQUIRE(VALID_MANAGER(manager)); REQUIRE(task->state == task_state_ready); @@ -432,12 +467,11 @@ task_ready(isc__task_t *task) { XTRACE("task_ready"); LOCK(&manager->lock); - - ENQUEUE(manager->ready_tasks, task, ready_link); + push_readyq(manager, task); #ifdef USE_WORKER_THREADS - SIGNAL(&manager->work_available); + if (manager->mode == isc_taskmgrmode_normal || has_privilege) + SIGNAL(&manager->work_available); #endif /* USE_WORKER_THREADS */ - UNLOCK(&manager->lock); } @@ -875,21 +909,81 @@ isc__task_getcurrenttime(isc_task_t *task0, isc_stdtime_t *t) { REQUIRE(t != NULL); LOCK(&task->lock); - *t = task->now; - UNLOCK(&task->lock); } /*** *** Task Manager. ***/ + +/* + * Return ISC_TRUE if the current ready list for the manager, which is + * either ready_tasks or the ready_priority_tasks, depending on whether + * the manager is currently in normal or privileged execution mode. + * + * Caller must hold the task manager lock. + */ +static inline isc_boolean_t +empty_readyq(isc__taskmgr_t *manager) { + isc__tasklist_t queue; + + if (manager->mode == isc_taskmgrmode_normal) + queue = manager->ready_tasks; + else + queue = manager->ready_priority_tasks; + + return (ISC_TF(EMPTY(queue))); +} + +/* + * Dequeue and return a pointer to the first task on the current ready + * list for the manager. + * If the task is privileged, dequeue it from the other ready list + * as well. + * + * Caller must hold the task manager lock. + */ +static inline isc__task_t * +pop_readyq(isc__taskmgr_t *manager) { + isc__task_t *task; + + if (manager->mode == isc_taskmgrmode_normal) + task = HEAD(manager->ready_tasks); + else + task = HEAD(manager->ready_priority_tasks); + + if (task != NULL) { + DEQUEUE(manager->ready_tasks, task, ready_link); + if (ISC_LINK_LINKED(task, ready_priority_link)) + DEQUEUE(manager->ready_priority_tasks, task, + ready_priority_link); + } + + return (task); +} + +/* + * Push 'task' onto the ready_tasks queue. If 'task' has the privilege + * flag set, then also push it onto the ready_priority_tasks queue. + * + * Caller must hold the task manager lock. + */ +static inline void +push_readyq(isc__taskmgr_t *manager, isc__task_t *task) { + ENQUEUE(manager->ready_tasks, task, ready_link); + if ((task->flags & TASK_F_PRIVILEGED) != 0) + ENQUEUE(manager->ready_priority_tasks, task, + ready_priority_link); +} + static void dispatch(isc__taskmgr_t *manager) { isc__task_t *task; #ifndef USE_WORKER_THREADS unsigned int total_dispatch_count = 0; - isc__tasklist_t ready_tasks; + isc__tasklist_t new_ready_tasks; + isc__tasklist_t new_priority_tasks; #endif /* USE_WORKER_THREADS */ REQUIRE(VALID_MANAGER(manager)); @@ -945,9 +1039,11 @@ dispatch(isc__taskmgr_t *manager) { */ #ifndef USE_WORKER_THREADS - ISC_LIST_INIT(ready_tasks); + ISC_LIST_INIT(new_ready_tasks); + ISC_LIST_INIT(new_priority_tasks); #endif LOCK(&manager->lock); + while (!FINISHED(manager)) { #ifdef USE_WORKER_THREADS /* @@ -956,10 +1052,12 @@ dispatch(isc__taskmgr_t *manager) { * the task while only holding the manager lock, and then * change the task to running state while only holding the * task lock. + * + * If a pause has been requested, don't do any work + * until it's been released. */ - while ((EMPTY(manager->ready_tasks) || - manager->exclusive_requested) && - !FINISHED(manager)) + while ((empty_readyq(manager) || manager->pause_requested || + manager->exclusive_requested) && !FINISHED(manager)) { XTHREADTRACE(isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL, @@ -971,13 +1069,13 @@ dispatch(isc__taskmgr_t *manager) { } #else /* USE_WORKER_THREADS */ if (total_dispatch_count >= DEFAULT_TASKMGR_QUANTUM || - EMPTY(manager->ready_tasks)) + empty_readyq(manager)) break; #endif /* USE_WORKER_THREADS */ XTHREADTRACE(isc_msgcat_get(isc_msgcat, ISC_MSGSET_TASK, ISC_MSG_WORKING, "working")); - task = HEAD(manager->ready_tasks); + task = pop_readyq(manager); if (task != NULL) { unsigned int dispatch_count = 0; isc_boolean_t done = ISC_FALSE; @@ -992,7 +1090,6 @@ dispatch(isc__taskmgr_t *manager) { * have a task to do. We must reacquire the manager * lock before exiting the 'if (task != NULL)' block. */ - DEQUEUE(manager->ready_tasks, task, ready_link); manager->tasks_running++; UNLOCK(&manager->lock); @@ -1113,6 +1210,9 @@ dispatch(isc__taskmgr_t *manager) { if (manager->exclusive_requested && manager->tasks_running == 1) { SIGNAL(&manager->exclusive_granted); + } else if (manager->pause_requested && + manager->tasks_running == 0) { + SIGNAL(&manager->paused); } #endif /* USE_WORKER_THREADS */ if (requeue) { @@ -1136,17 +1236,39 @@ dispatch(isc__taskmgr_t *manager) { * might even hurt rather than help. */ #ifdef USE_WORKER_THREADS - ENQUEUE(manager->ready_tasks, task, - ready_link); + push_readyq(manager, task); #else - ENQUEUE(ready_tasks, task, ready_link); + ENQUEUE(new_ready_tasks, task, ready_link); + if ((task->flags & TASK_F_PRIVILEGED) != 0) + ENQUEUE(new_priority_tasks, task, + ready_priority_link); #endif } } - } -#ifndef USE_WORKER_THREADS - ISC_LIST_APPENDLIST(manager->ready_tasks, ready_tasks, ready_link); + +#ifdef USE_WORKER_THREADS + /* + * If we are in privileged execution mode and there are no + * tasks remaining on the current ready queue, then + * we're stuck. Automatically drop privileges at that + * point and continue with the regular ready queue. + */ + if (manager->tasks_running == 0 && empty_readyq(manager)) { + manager->mode = isc_taskmgrmode_normal; + if (!empty_readyq(manager)) + BROADCAST(&manager->work_available); + } #endif + } + +#ifndef USE_WORKER_THREADS + ISC_LIST_APPENDLIST(manager->ready_tasks, new_ready_tasks, ready_link); + ISC_LIST_APPENDLIST(manager->ready_priority_tasks, new_priority_tasks, + ready_priority_link); + if (empty_readyq(manager)) + manager->mode = isc_taskmgrmode_normal; +#endif + UNLOCK(&manager->lock); } @@ -1181,6 +1303,7 @@ manager_free(isc__taskmgr_t *manager) { #ifdef USE_WORKER_THREADS (void)isc_condition_destroy(&manager->exclusive_granted); (void)isc_condition_destroy(&manager->work_available); + (void)isc_condition_destroy(&manager->paused); isc_mem_free(manager->mctx, manager->threads); #endif /* USE_WORKER_THREADS */ DESTROYLOCK(&manager->lock); @@ -1231,6 +1354,7 @@ isc__taskmgr_create(isc_mem_t *mctx, unsigned int workers, manager->common.methods = &taskmgrmethods; manager->common.impmagic = TASK_MANAGER_MAGIC; manager->common.magic = ISCAPI_TASKMGR_MAGIC; + manager->mode = isc_taskmgrmode_normal; manager->mctx = NULL; result = isc_mutex_init(&manager->lock); if (result != ISC_R_SUCCESS) @@ -1260,14 +1384,24 @@ isc__taskmgr_create(isc_mem_t *mctx, unsigned int workers, result = ISC_R_UNEXPECTED; goto cleanup_workavailable; } + if (isc_condition_init(&manager->paused) != ISC_R_SUCCESS) { + UNEXPECTED_ERROR(__FILE__, __LINE__, + "isc_condition_init() %s", + isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL, + ISC_MSG_FAILED, "failed")); + result = ISC_R_UNEXPECTED; + goto cleanup_exclusivegranted; + } #endif /* USE_WORKER_THREADS */ if (default_quantum == 0) default_quantum = DEFAULT_DEFAULT_QUANTUM; manager->default_quantum = default_quantum; INIT_LIST(manager->tasks); INIT_LIST(manager->ready_tasks); + INIT_LIST(manager->ready_priority_tasks); manager->tasks_running = 0; manager->exclusive_requested = ISC_FALSE; + manager->pause_requested = ISC_FALSE; manager->exiting = ISC_FALSE; manager->excl = NULL; @@ -1304,6 +1438,8 @@ isc__taskmgr_create(isc_mem_t *mctx, unsigned int workers, return (ISC_R_SUCCESS); #ifdef USE_WORKER_THREADS + cleanup_exclusivegranted: + (void)isc_condition_destroy(&manager->exclusive_granted); cleanup_workavailable: (void)isc_condition_destroy(&manager->work_available); cleanup_threads: @@ -1374,6 +1510,11 @@ isc__taskmgr_destroy(isc_taskmgr_t **managerp) { INSIST(!manager->exiting); manager->exiting = ISC_TRUE; + /* + * If privileged mode was on, turn it off. + */ + manager->mode = isc_taskmgrmode_normal; + /* * Post shutdown event(s) to every task (if they haven't already been * posted). @@ -1383,7 +1524,7 @@ isc__taskmgr_destroy(isc_taskmgr_t **managerp) { task = NEXT(task, link)) { LOCK(&task->lock); if (task_shutdown(task)) - ENQUEUE(manager->ready_tasks, task, ready_link); + push_readyq(manager, task); UNLOCK(&task->lock); } #ifdef USE_WORKER_THREADS @@ -1422,10 +1563,30 @@ isc__taskmgr_destroy(isc_taskmgr_t **managerp) { *managerp = NULL; } +ISC_TASKFUNC_SCOPE void +isc__taskmgr_setmode(isc_taskmgr_t *manager0, isc_taskmgrmode_t mode) { + isc__taskmgr_t *manager = (isc__taskmgr_t *)manager0; + + LOCK(&manager->lock); + manager->mode = mode; + UNLOCK(&manager->lock); +} + +ISC_TASKFUNC_SCOPE isc_taskmgrmode_t +isc__taskmgr_mode(isc_taskmgr_t *manager0) { + isc__taskmgr_t *manager = (isc__taskmgr_t *)manager0; + isc_taskmgrmode_t mode; + LOCK(&manager->lock); + mode = manager->mode; + UNLOCK(&manager->lock); + return (mode); +} + #ifndef USE_WORKER_THREADS isc_boolean_t isc__taskmgr_ready(isc_taskmgr_t *manager0) { isc__taskmgr_t *manager = (isc__taskmgr_t *)manager0; + isc_boolean_t is_ready; #ifdef USE_SHARED_MANAGER if (manager == NULL) @@ -1433,7 +1594,12 @@ isc__taskmgr_ready(isc_taskmgr_t *manager0) { #endif if (manager == NULL) return (ISC_FALSE); - return (ISC_TF(!ISC_LIST_EMPTY(manager->ready_tasks))); + + LOCK(&manager->lock); + is_ready = !empty_readyq(manager); + UNLOCK(&manager->lock); + + return (is_ready); } isc_result_t @@ -1452,6 +1618,29 @@ isc__taskmgr_dispatch(isc_taskmgr_t *manager0) { return (ISC_R_SUCCESS); } +#else +ISC_TASKFUNC_SCOPE void +isc__taskmgr_pause(isc_taskmgr_t *manager0) { + isc__taskmgr_t *manager = (isc__taskmgr_t *)manager0; + LOCK(&manager->lock); + while (manager->tasks_running > 0) { + WAIT(&manager->paused, &manager->lock); + } + manager->pause_requested = ISC_TRUE; + UNLOCK(&manager->lock); +} + +ISC_TASKFUNC_SCOPE void +isc__taskmgr_resume(isc_taskmgr_t *manager0) { + isc__taskmgr_t *manager = (isc__taskmgr_t *)manager0; + + LOCK(&manager->lock); + if (manager->pause_requested) { + manager->pause_requested = ISC_FALSE; + BROADCAST(&manager->work_available); + } + UNLOCK(&manager->lock); +} #endif /* USE_WORKER_THREADS */ ISC_TASKFUNC_SCOPE void @@ -1522,6 +1711,44 @@ isc__task_endexclusive(isc_task_t *task0) { #endif } +ISC_TASKFUNC_SCOPE void +isc__task_setprivilege(isc_task_t *task0, isc_boolean_t priv) { + isc__task_t *task = (isc__task_t *)task0; + isc__taskmgr_t *manager = task->manager; + isc_boolean_t oldpriv; + + LOCK(&task->lock); + oldpriv = ISC_TF((task->flags & TASK_F_PRIVILEGED) != 0); + if (priv) + task->flags |= TASK_F_PRIVILEGED; + else + task->flags &= ~TASK_F_PRIVILEGED; + UNLOCK(&task->lock); + + if (priv == oldpriv) + return; + + LOCK(&manager->lock); + if (priv && ISC_LINK_LINKED(task, ready_link)) + ENQUEUE(manager->ready_priority_tasks, task, + ready_priority_link); + else if (!priv && ISC_LINK_LINKED(task, ready_priority_link)) + DEQUEUE(manager->ready_priority_tasks, task, + ready_priority_link); + UNLOCK(&manager->lock); +} + +ISC_TASKFUNC_SCOPE isc_boolean_t +isc__task_privilege(isc_task_t *task0) { + isc__task_t *task = (isc__task_t *)task0; + isc_boolean_t priv; + + LOCK(&task->lock); + priv = ISC_TF((task->flags & TASK_F_PRIVILEGED) != 0); + UNLOCK(&task->lock); + return (priv); +} + #ifdef USE_SOCKETIMPREGISTER isc_result_t isc__task_register() { diff --git a/contrib/bind9/lib/isc/task_api.c b/contrib/bind9/lib/isc/task_api.c index 06a8d24849b..f49ab321b2d 100644 --- a/contrib/bind9/lib/isc/task_api.c +++ b/contrib/bind9/lib/isc/task_api.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009, 2010, 2012 Internet Systems Consortium, Inc. ("ISC") + * Copyright (C) 2009-2012 Internet Systems Consortium, Inc. ("ISC") * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -99,6 +99,20 @@ isc_taskmgr_destroy(isc_taskmgr_t **managerp) { ENSURE(*managerp == NULL); } +void +isc_taskmgr_setmode(isc_taskmgr_t *manager, isc_taskmgrmode_t mode) { + REQUIRE(ISCAPI_TASKMGR_VALID(manager)); + + manager->methods->setmode(manager, mode); +} + +isc_taskmgrmode_t +isc_taskmgr_mode(isc_taskmgr_t *manager) { + REQUIRE(ISCAPI_TASKMGR_VALID(manager)); + + return (manager->methods->mode(manager)); +} + isc_result_t isc_task_create(isc_taskmgr_t *manager, unsigned int quantum, isc_task_t **taskp) @@ -212,6 +226,20 @@ isc_task_endexclusive(isc_task_t *task) { task->methods->endexclusive(task); } +void +isc_task_setprivilege(isc_task_t *task, isc_boolean_t priv) { + REQUIRE(ISCAPI_TASK_VALID(task)); + + task->methods->setprivilege(task, priv); +} + +isc_boolean_t +isc_task_privilege(isc_task_t *task) { + REQUIRE(ISCAPI_TASK_VALID(task)); + + return (task->methods->privilege(task)); +} + /*% * This is necessary for libisc's internal timer implementation. Other diff --git a/contrib/bind9/lib/isc/task_p.h b/contrib/bind9/lib/isc/task_p.h index 85deeae0d0e..8c1e4c52fa4 100644 --- a/contrib/bind9/lib/isc/task_p.h +++ b/contrib/bind9/lib/isc/task_p.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004, 2005, 2007, 2009, 2012 Internet Systems Consortium, Inc. ("ISC") + * Copyright (C) 2004, 2005, 2007, 2009, 2011, 2012 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 2000, 2001 Internet Software Consortium. * * Permission to use, copy, modify, and/or distribute this software for any @@ -22,10 +22,18 @@ /*! \file */ +#if defined(BIND9) && defined(ISC_PLATFORM_USETHREADS) +void +isc__taskmgr_pause(isc_taskmgr_t *taskmgr); + +void +isc__taskmgr_resume(isc_taskmgr_t *taskmgr); +#else isc_boolean_t isc__taskmgr_ready(isc_taskmgr_t *taskmgr); isc_result_t isc__taskmgr_dispatch(isc_taskmgr_t *taskmgr); +#endif /* !BIND9 || !ISC_PLATFORM_USETHREADS */ #endif /* ISC_TASK_P_H */ diff --git a/contrib/bind9/lib/isc/taskpool.c b/contrib/bind9/lib/isc/taskpool.c index 7324cfa379d..a5ce0e8514d 100644 --- a/contrib/bind9/lib/isc/taskpool.c +++ b/contrib/bind9/lib/isc/taskpool.c @@ -165,9 +165,8 @@ isc_taskpool_destroy(isc_taskpool_t **poolp) { unsigned int i; isc_taskpool_t *pool = *poolp; for (i = 0; i < pool->ntasks; i++) { - if (pool->tasks[i] != NULL) { + if (pool->tasks[i] != NULL) isc_task_detach(&pool->tasks[i]); - } } isc_mem_put(pool->mctx, pool->tasks, pool->ntasks * sizeof(isc_task_t *)); @@ -175,4 +174,14 @@ isc_taskpool_destroy(isc_taskpool_t **poolp) { *poolp = NULL; } +void +isc_taskpool_setprivilege(isc_taskpool_t *pool, isc_boolean_t priv) { + unsigned int i; + REQUIRE(pool != NULL); + + for (i = 0; i < pool->ntasks; i++) { + if (pool->tasks[i] != NULL) + isc_task_setprivilege(pool->tasks[i], priv); + } +} diff --git a/contrib/bind9/lib/isc/unix/socket.c b/contrib/bind9/lib/isc/unix/socket.c index d007598e19d..7bd12aaa31e 100644 --- a/contrib/bind9/lib/isc/unix/socket.c +++ b/contrib/bind9/lib/isc/unix/socket.c @@ -334,7 +334,8 @@ struct isc__socket { listener : 1, /* listener socket */ connected : 1, connecting : 1, /* connect pending */ - bound : 1; /* bound to local addr */ + bound : 1, /* bound to local addr */ + dupped : 1; #ifdef ISC_NET_RECVOVERFLOW unsigned char overflow; /* used for MSG_TRUNC fake */ @@ -428,6 +429,10 @@ static isc__socketmgr_t *socketmgr = NULL; # define MAXSCATTERGATHER_RECV (ISC_SOCKET_MAXSCATTERGATHER) #endif +static isc_result_t socket_create(isc_socketmgr_t *manager0, int pf, + isc_sockettype_t type, + isc_socket_t **socketp, + isc_socket_t *dup_socket); static void send_recvdone_event(isc__socket_t *, isc_socketevent_t **); static void send_senddone_event(isc__socket_t *, isc_socketevent_t **); static void free_socket(isc__socket_t **); @@ -546,6 +551,10 @@ isc__socket_fdwatchcreate(isc_socketmgr_t *manager, int fd, int flags, isc_task_t *task, isc_socket_t **socketp); ISC_SOCKETFUNC_SCOPE isc_result_t isc__socket_fdwatchpoke(isc_socket_t *sock, int flags); +ISC_SOCKETFUNC_SCOPE isc_result_t +isc__socket_dup(isc_socket_t *sock, isc_socket_t **socketp); +ISC_SOCKETFUNC_SCOPE int +isc__socket_getfd(isc_socket_t *sock); static struct { isc_socketmethods_t methods; @@ -563,13 +572,17 @@ static struct { isc__socket_detach, isc__socket_bind, isc__socket_sendto, + isc__socket_sendto2, isc__socket_connect, isc__socket_recv, + isc__socket_recv2, isc__socket_cancel, isc__socket_getsockname, isc__socket_gettype, isc__socket_ipv6only, - isc__socket_fdwatchpoke + isc__socket_fdwatchpoke, + isc__socket_dup, + isc__socket_getfd } #ifndef BIND9 , @@ -2046,6 +2059,7 @@ allocate_socket(isc__socketmgr_t *manager, isc_sockettype_t type, sock->manager = manager; sock->type = type; sock->fd = -1; + sock->dupped = 0; sock->statsindex = NULL; ISC_LINK_INIT(sock, link); @@ -2251,7 +2265,9 @@ use_min_mtu(isc__socket_t *sock) { } static isc_result_t -opensocket(isc__socketmgr_t *manager, isc__socket_t *sock) { +opensocket(isc__socketmgr_t *manager, isc__socket_t *sock, + isc__socket_t *dup_socket) +{ isc_result_t result; char strbuf[ISC_STRERRORSIZE]; const char *err = "socket"; @@ -2265,22 +2281,29 @@ opensocket(isc__socketmgr_t *manager, isc__socket_t *sock) { #endif again: - switch (sock->type) { - case isc_sockettype_udp: - sock->fd = socket(sock->pf, SOCK_DGRAM, IPPROTO_UDP); - break; - case isc_sockettype_tcp: - sock->fd = socket(sock->pf, SOCK_STREAM, IPPROTO_TCP); - break; - case isc_sockettype_unix: - sock->fd = socket(sock->pf, SOCK_STREAM, 0); - break; - case isc_sockettype_fdwatch: - /* - * We should not be called for isc_sockettype_fdwatch sockets. - */ - INSIST(0); - break; + if (dup_socket == NULL) { + switch (sock->type) { + case isc_sockettype_udp: + sock->fd = socket(sock->pf, SOCK_DGRAM, IPPROTO_UDP); + break; + case isc_sockettype_tcp: + sock->fd = socket(sock->pf, SOCK_STREAM, IPPROTO_TCP); + break; + case isc_sockettype_unix: + sock->fd = socket(sock->pf, SOCK_STREAM, 0); + break; + case isc_sockettype_fdwatch: + /* + * We should not be called for isc_sockettype_fdwatch + * sockets. + */ + INSIST(0); + break; + } + } else { + sock->fd = dup(dup_socket->fd); + sock->dupped = 1; + sock->bound = dup_socket->bound; } if (sock->fd == -1 && errno == EINTR && tries++ < 42) goto again; @@ -2357,6 +2380,9 @@ opensocket(isc__socketmgr_t *manager, isc__socket_t *sock) { } } + if (dup_socket != NULL) + goto setup_done; + result = make_nonblock(sock->fd); if (result != ISC_R_SUCCESS) { (void)close(sock->fd); @@ -2521,20 +2547,21 @@ opensocket(isc__socketmgr_t *manager, isc__socket_t *sock) { } #endif /* defined(USE_CMSG) || defined(SO_RCVBUF) */ +setup_done: inc_stats(manager->stats, sock->statsindex[STATID_OPEN]); return (ISC_R_SUCCESS); } -/*% - * Create a new 'type' socket managed by 'manager'. Events - * will be posted to 'task' and when dispatched 'action' will be - * called with 'arg' as the arg value. The new socket is returned - * in 'socketp'. +/* + * Create a 'type' socket or duplicate an existing socket, managed + * by 'manager'. Events will be posted to 'task' and when dispatched + * 'action' will be called with 'arg' as the arg value. The new + * socket is returned in 'socketp'. */ -ISC_SOCKETFUNC_SCOPE isc_result_t -isc__socket_create(isc_socketmgr_t *manager0, int pf, isc_sockettype_t type, - isc_socket_t **socketp) +static isc_result_t +socket_create(isc_socketmgr_t *manager0, int pf, isc_sockettype_t type, + isc_socket_t **socketp, isc_socket_t *dup_socket) { isc__socket_t *sock = NULL; isc__socketmgr_t *manager = (isc__socketmgr_t *)manager0; @@ -2566,7 +2593,8 @@ isc__socket_create(isc_socketmgr_t *manager0, int pf, isc_sockettype_t type, } sock->pf = pf; - result = opensocket(manager, sock); + + result = opensocket(manager, sock, (isc__socket_t *)dup_socket); if (result != ISC_R_SUCCESS) { inc_stats(manager->stats, sock->statsindex[STATID_OPENFAIL]); free_socket(&sock); @@ -2601,11 +2629,40 @@ isc__socket_create(isc_socketmgr_t *manager0, int pf, isc_sockettype_t type, UNLOCK(&manager->lock); socket_log(sock, NULL, CREATION, isc_msgcat, ISC_MSGSET_SOCKET, - ISC_MSG_CREATED, "created"); + ISC_MSG_CREATED, dup_socket != NULL ? "dupped" : "created"); return (ISC_R_SUCCESS); } +/*% + * Create a new 'type' socket managed by 'manager'. Events + * will be posted to 'task' and when dispatched 'action' will be + * called with 'arg' as the arg value. The new socket is returned + * in 'socketp'. + */ +ISC_SOCKETFUNC_SCOPE isc_result_t +isc__socket_create(isc_socketmgr_t *manager0, int pf, isc_sockettype_t type, + isc_socket_t **socketp) +{ + return (socket_create(manager0, pf, type, socketp, NULL)); +} + +/*% + * Duplicate an existing socket. The new socket is returned + * in 'socketp'. + */ +ISC_SOCKETFUNC_SCOPE isc_result_t +isc__socket_dup(isc_socket_t *sock0, isc_socket_t **socketp) { + isc__socket_t *sock = (isc__socket_t *)sock0; + + REQUIRE(VALID_SOCKET(sock)); + REQUIRE(socketp != NULL && *socketp == NULL); + + return (socket_create((isc_socketmgr_t *) sock->manager, + sock->pf, sock->type, socketp, + sock0)); +} + #ifdef BIND9 ISC_SOCKETFUNC_SCOPE isc_result_t isc__socket_open(isc_socket_t *sock0) { @@ -2624,7 +2681,7 @@ isc__socket_open(isc_socket_t *sock0) { */ REQUIRE(sock->fd == -1); - result = opensocket(sock->manager, sock); + result = opensocket(sock->manager, sock, NULL); if (result != ISC_R_SUCCESS) sock->fd = -1; @@ -2804,6 +2861,7 @@ isc__socket_close(isc_socket_t *sock0) { int fd; isc__socketmgr_t *manager; + fflush(stdout); REQUIRE(VALID_SOCKET(sock)); LOCK(&sock->lock); @@ -2824,6 +2882,7 @@ isc__socket_close(isc_socket_t *sock0) { manager = sock->manager; fd = sock->fd; sock->fd = -1; + sock->dupped = 0; memset(sock->name, 0, sizeof(sock->name)); sock->tag = NULL; sock->listener = 0; @@ -4991,11 +5050,13 @@ isc__socket_bind(isc_socket_t *sock0, isc_sockaddr_t *sockaddr, LOCK(&sock->lock); INSIST(!sock->bound); + INSIST(!sock->dupped); if (sock->pf != sockaddr->type.sa.sa_family) { UNLOCK(&sock->lock); return (ISC_R_FAMILYMISMATCH); } + /* * Only set SO_REUSEADDR when we want a specific port. */ @@ -5680,6 +5741,7 @@ isc__socket_ipv6only(isc_socket_t *sock0, isc_boolean_t yes) { #endif REQUIRE(VALID_SOCKET(sock)); + INSIST(!sock->dupped); #ifdef IPV6_V6ONLY if (sock->pf == AF_INET6) { @@ -5846,6 +5908,13 @@ isc__socket_register() { } #endif +ISC_SOCKETFUNC_SCOPE int +isc__socket_getfd(isc_socket_t *socket0) { + isc__socket_t *socket = (isc__socket_t *)socket0; + + return ((short) socket->fd); +} + #if defined(HAVE_LIBXML2) && defined(BIND9) static const char * diff --git a/contrib/bind9/lib/isccc/api b/contrib/bind9/lib/isccc/api index 461b9ac31a9..47724c51e39 100644 --- a/contrib/bind9/lib/isccc/api +++ b/contrib/bind9/lib/isccc/api @@ -4,6 +4,6 @@ # 9.8: 80-89, 120-129 # 9.9: 90-109 # 9.9-sub: 130-139 -LIBINTERFACE = 80 +LIBINTERFACE = 90 LIBREVISION = 3 LIBAGE = 0 diff --git a/contrib/bind9/lib/isccfg/api b/contrib/bind9/lib/isccfg/api index 39585b0239d..864bdc90941 100644 --- a/contrib/bind9/lib/isccfg/api +++ b/contrib/bind9/lib/isccfg/api @@ -4,6 +4,6 @@ # 9.8: 80-89, 120-129 # 9.9: 90-109 # 9.9-sub: 130-139 -LIBINTERFACE = 82 -LIBREVISION = 7 +LIBINTERFACE = 90 +LIBREVISION = 6 LIBAGE = 0 diff --git a/contrib/bind9/lib/isccfg/namedconf.c b/contrib/bind9/lib/isccfg/namedconf.c index 287ce141f4c..431af746338 100644 --- a/contrib/bind9/lib/isccfg/namedconf.c +++ b/contrib/bind9/lib/isccfg/namedconf.c @@ -54,6 +54,9 @@ static isc_result_t parse_enum_or_other(cfg_parser_t *pctx, const cfg_type_t *enumtype, const cfg_type_t *othertype, cfg_obj_t **ret); +static void +doc_enum_or_other(cfg_printer_t *pctx, const cfg_type_t *type); + static isc_result_t parse_keyvalue(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret); @@ -228,9 +231,8 @@ static cfg_type_t cfg_type_namesockaddrkeylist = { }; /*% - * A list of socket addresses with an optional default port, - * as used in the also-notify option. E.g., - * "port 1234 { 10.0.0.1; 1::2 port 69; }" + * A list of socket addresses with an optional default port, as used + * in the lwresd 'listen-on' option. E.g., "{ 10.0.0.1; 1::2 port 69; }" */ static cfg_tuplefielddef_t portiplist_fields[] = { { "port", &cfg_type_optional_port, 0 }, @@ -238,8 +240,8 @@ static cfg_tuplefielddef_t portiplist_fields[] = { { NULL, NULL, 0 } }; static cfg_type_t cfg_type_portiplist = { - "portiplist", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple, &cfg_rep_tuple, - portiplist_fields + "portiplist", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple, + &cfg_rep_tuple, portiplist_fields }; /*% @@ -554,6 +556,35 @@ static cfg_type_t cfg_type_autodnssec = { &cfg_rep_string, &autodnssec_enums }; +static const char *dnssecupdatemode_enums[] = { "maintain", "no-resign", NULL }; +static cfg_type_t cfg_type_dnssecupdatemode = { + "dnssecupdatemode", cfg_parse_enum, cfg_print_ustring, cfg_doc_enum, + &cfg_rep_string, &dnssecupdatemode_enums +}; + +static const char *updatemethods_enums[] = { "increment", "unixtime", NULL }; +static cfg_type_t cfg_type_updatemethod = { + "updatemethod", cfg_parse_enum, cfg_print_ustring, cfg_doc_enum, + &cfg_rep_string, &updatemethods_enums +}; + +/* + * zone-statistics: full, terse, or none. + * + * for backward compatibility, we also support boolean values. + * yes represents "full", no represents "terse". in the future we + * may change no to mean "none". + */ +static const char *zonestat_enums[] = { "full", "terse", "none", NULL }; +static isc_result_t +parse_zonestat(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { + return (parse_enum_or_other(pctx, type, &cfg_type_boolean, ret)); +} +static cfg_type_t cfg_type_zonestat = { + "zonestat", parse_zonestat, cfg_print_ustring, doc_enum_or_other, + &cfg_rep_string, zonestat_enums +}; + static cfg_type_t cfg_type_rrsetorder = { "rrsetorder", cfg_parse_bracketed_list, cfg_print_bracketed_list, cfg_doc_bracketed_list, &cfg_rep_list, &cfg_type_rrsetorderingelement @@ -599,7 +630,7 @@ static cfg_type_t cfg_type_forwardtype = { static const char *zonetype_enums[] = { "master", "slave", "stub", "static-stub", "hint", "forward", - "delegation-only", NULL }; + "delegation-only", "redirect", NULL }; static cfg_type_t cfg_type_zonetype = { "zonetype", cfg_parse_enum, cfg_print_ustring, cfg_doc_enum, &cfg_rep_string, &zonetype_enums @@ -912,6 +943,7 @@ options_clauses[] = { { "listen-on-v6", &cfg_type_listenon, CFG_CLAUSEFLAG_MULTI }, { "managed-keys-directory", &cfg_type_qstring, 0 }, { "match-mapped-addresses", &cfg_type_boolean, 0 }, + { "max-rsa-exponent-size", &cfg_type_uint32, 0 }, { "memstatistics-file", &cfg_type_qstring, 0 }, { "memstatistics", &cfg_type_boolean, 0 }, { "multiple-cnames", &cfg_type_boolean, CFG_CLAUSEFLAG_OBSOLETE }, @@ -1458,7 +1490,7 @@ zone_clauses[] = { { "allow-transfer", &cfg_type_bracketed_aml, 0 }, { "allow-update", &cfg_type_bracketed_aml, 0 }, { "allow-update-forwarding", &cfg_type_bracketed_aml, 0 }, - { "also-notify", &cfg_type_portiplist, 0 }, + { "also-notify", &cfg_type_namesockaddrkeylist, 0 }, { "alt-transfer-source", &cfg_type_sockaddr4wild, 0 }, { "alt-transfer-source-v6", &cfg_type_sockaddr6wild, 0 }, { "auto-dnssec", &cfg_type_autodnssec, 0 }, @@ -1472,7 +1504,9 @@ zone_clauses[] = { { "check-wildcard", &cfg_type_boolean, 0 }, { "dialup", &cfg_type_dialuptype, 0 }, { "dnssec-dnskey-kskonly", &cfg_type_boolean, 0 }, + { "dnssec-loadkeys-interval", &cfg_type_uint32, 0 }, { "dnssec-secure-to-insecure", &cfg_type_boolean, 0 }, + { "dnssec-update-mode", &cfg_type_dnssecupdatemode, 0 }, { "forward", &cfg_type_forwardtype, 0 }, { "forwarders", &cfg_type_portiplist, 0 }, { "key-directory", &cfg_type_qstring, 0 }, @@ -1495,17 +1529,20 @@ zone_clauses[] = { { "notify-source-v6", &cfg_type_sockaddr6wild, 0 }, { "notify-to-soa", &cfg_type_boolean, 0 }, { "nsec3-test-zone", &cfg_type_boolean, CFG_CLAUSEFLAG_TESTONLY }, + { "serial-update-method", &cfg_type_updatemethod, 0 }, + { "request-ixfr", &cfg_type_boolean, 0 }, { "sig-signing-nodes", &cfg_type_uint32, 0 }, { "sig-signing-signatures", &cfg_type_uint32, 0 }, { "sig-signing-type", &cfg_type_uint32, 0 }, { "sig-validity-interval", &cfg_type_validityinterval, 0 }, + { "inline-signing", &cfg_type_boolean, 0 }, { "transfer-source", &cfg_type_sockaddr4wild, 0 }, { "transfer-source-v6", &cfg_type_sockaddr6wild, 0 }, { "try-tcp-refresh", &cfg_type_boolean, 0 }, { "update-check-ksk", &cfg_type_boolean, 0 }, { "use-alt-transfer-source", &cfg_type_boolean, 0 }, { "zero-no-soa-ttl", &cfg_type_boolean, 0 }, - { "zone-statistics", &cfg_type_boolean, 0 }, + { "zone-statistics", &cfg_type_zonestat, 0 }, { NULL, NULL, 0 } }; diff --git a/contrib/bind9/lib/lwres/api b/contrib/bind9/lib/lwres/api index 2a5c388460c..95bd2046233 100644 --- a/contrib/bind9/lib/lwres/api +++ b/contrib/bind9/lib/lwres/api @@ -4,6 +4,6 @@ # 9.8: 80-89, 120-129 # 9.9: 90-109 # 9.9-sub: 130-139 -LIBINTERFACE = 80 -LIBREVISION = 6 +LIBINTERFACE = 90 +LIBREVISION = 4 LIBAGE = 0 diff --git a/contrib/bind9/lib/lwres/man/lwres_config.3 b/contrib/bind9/lib/lwres/man/lwres_config.3 index a0919d95f25..42f0e695f7b 100644 --- a/contrib/bind9/lib/lwres/man/lwres_config.3 +++ b/contrib/bind9/lib/lwres/man/lwres_config.3 @@ -1,4 +1,4 @@ -.\" Copyright (C) 2004, 2005, 2007, 2012 Internet Systems Consortium, Inc. ("ISC") +.\" Copyright (C) 2004, 2005, 2007 Internet Systems Consortium, Inc. ("ISC") .\" Copyright (C) 2000, 2001 Internet Software Consortium. .\" .\" Permission to use, copy, modify, and/or distribute this software for any @@ -100,7 +100,7 @@ unless an error occurred when converting the network addresses to a numeric host .PP \fI/etc/resolv.conf\fR .SH "COPYRIGHT" -Copyright \(co 2004, 2005, 2007, 2012 Internet Systems Consortium, Inc. ("ISC") +Copyright \(co 2004, 2005, 2007 Internet Systems Consortium, Inc. ("ISC") .br Copyright \(co 2000, 2001 Internet Software Consortium. .br diff --git a/contrib/bind9/lib/lwres/man/lwres_config.docbook b/contrib/bind9/lib/lwres/man/lwres_config.docbook index 71475706e96..5736ef3b649 100644 --- a/contrib/bind9/lib/lwres/man/lwres_config.docbook +++ b/contrib/bind9/lib/lwres/man/lwres_config.docbook @@ -2,7 +2,7 @@ "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd" []> - + @@ -36,7 +36,6 @@ 2004 2005 2007 - 2012 Internet Systems Consortium, Inc. ("ISC") diff --git a/contrib/bind9/lib/lwres/man/lwres_config.html b/contrib/bind9/lib/lwres/man/lwres_config.html index ccc9db14dfa..ed10069c355 100644 --- a/contrib/bind9/lib/lwres/man/lwres_config.html +++ b/contrib/bind9/lib/lwres/man/lwres_config.html @@ -1,5 +1,5 @@ - + @@ -36,7 +36,6 @@ 2004 2005 2007 - 2012 Internet Systems Consortium, Inc. ("ISC") diff --git a/contrib/bind9/lib/lwres/man/lwres_context.html b/contrib/bind9/lib/lwres/man/lwres_context.html index 70efa240c80..e13539d9f92 100644 --- a/contrib/bind9/lib/lwres/man/lwres_context.html +++ b/contrib/bind9/lib/lwres/man/lwres_context.html @@ -1,5 +1,5 @@ - + @@ -36,7 +36,6 @@ 2004 2005 2007 - 2012 Internet Systems Consortium, Inc. ("ISC") diff --git a/contrib/bind9/lib/lwres/man/lwres_gabn.html b/contrib/bind9/lib/lwres/man/lwres_gabn.html index 30c9dda251e..270620d3d66 100644 --- a/contrib/bind9/lib/lwres/man/lwres_gabn.html +++ b/contrib/bind9/lib/lwres/man/lwres_gabn.html @@ -1,5 +1,5 @@ - + @@ -36,7 +36,6 @@ 2004 2005 2007 - 2012 Internet Systems Consortium, Inc. ("ISC") diff --git a/contrib/bind9/lib/lwres/man/lwres_gai_strerror.html b/contrib/bind9/lib/lwres/man/lwres_gai_strerror.html index e8d4935c303..f2faacecfb6 100644 --- a/contrib/bind9/lib/lwres/man/lwres_gai_strerror.html +++ b/contrib/bind9/lib/lwres/man/lwres_gai_strerror.html @@ -1,5 +1,5 @@ - + @@ -36,7 +36,6 @@ 2004 2005 2007 - 2012 Internet Systems Consortium, Inc. ("ISC") diff --git a/contrib/bind9/lib/lwres/man/lwres_getaddrinfo.html b/contrib/bind9/lib/lwres/man/lwres_getaddrinfo.html index 57025c0c232..27023674296 100644 --- a/contrib/bind9/lib/lwres/man/lwres_getaddrinfo.html +++ b/contrib/bind9/lib/lwres/man/lwres_getaddrinfo.html @@ -1,5 +1,5 @@ - + @@ -36,7 +36,6 @@ 2004 2005 2007 - 2012 Internet Systems Consortium, Inc. ("ISC") diff --git a/contrib/bind9/lib/lwres/man/lwres_gethostent.html b/contrib/bind9/lib/lwres/man/lwres_gethostent.html index e5f660cb0c7..2c990850805 100644 --- a/contrib/bind9/lib/lwres/man/lwres_gethostent.html +++ b/contrib/bind9/lib/lwres/man/lwres_gethostent.html @@ -1,5 +1,5 @@ - + @@ -36,7 +36,6 @@ 2004 2005 2007 - 2012 Internet Systems Consortium, Inc. ("ISC") diff --git a/contrib/bind9/lib/lwres/man/lwres_getipnode.html b/contrib/bind9/lib/lwres/man/lwres_getipnode.html index 410fec93755..0fc483d1a8a 100644 --- a/contrib/bind9/lib/lwres/man/lwres_getipnode.html +++ b/contrib/bind9/lib/lwres/man/lwres_getipnode.html @@ -1,5 +1,5 @@ - + @@ -36,7 +36,6 @@ 2004 2005 2007 - 2012 Internet Systems Consortium, Inc. ("ISC") diff --git a/contrib/bind9/lib/lwres/man/lwres_getnameinfo.html b/contrib/bind9/lib/lwres/man/lwres_getnameinfo.html index f4808e742dc..9cc7d5ae3cd 100644 --- a/contrib/bind9/lib/lwres/man/lwres_getnameinfo.html +++ b/contrib/bind9/lib/lwres/man/lwres_getnameinfo.html @@ -1,5 +1,5 @@ - + @@ -36,7 +36,6 @@ 2004 2005 2007 - 2012 Internet Systems Consortium, Inc. ("ISC") diff --git a/contrib/bind9/lib/lwres/man/lwres_getrrsetbyname.html b/contrib/bind9/lib/lwres/man/lwres_getrrsetbyname.html index 7f3b56dac22..e7d68bb54d3 100644 --- a/contrib/bind9/lib/lwres/man/lwres_getrrsetbyname.html +++ b/contrib/bind9/lib/lwres/man/lwres_getrrsetbyname.html @@ -1,5 +1,5 @@ - + @@ -36,7 +36,6 @@ 2004 2005 2007 - 2012 Internet Systems Consortium, Inc. ("ISC") diff --git a/contrib/bind9/lib/lwres/man/lwres_gnba.html b/contrib/bind9/lib/lwres/man/lwres_gnba.html index 774a166eba1..6d61b87bb43 100644 --- a/contrib/bind9/lib/lwres/man/lwres_gnba.html +++ b/contrib/bind9/lib/lwres/man/lwres_gnba.html @@ -1,5 +1,5 @@ - + @@ -36,7 +36,6 @@ 2004 2005 2007 - 2012 Internet Systems Consortium, Inc. ("ISC") diff --git a/contrib/bind9/lib/lwres/man/lwres_hstrerror.html b/contrib/bind9/lib/lwres/man/lwres_hstrerror.html index c698d55d4d5..8d4e9d60b5b 100644 --- a/contrib/bind9/lib/lwres/man/lwres_hstrerror.html +++ b/contrib/bind9/lib/lwres/man/lwres_hstrerror.html @@ -1,5 +1,5 @@ - + @@ -36,7 +36,6 @@ 2004 2005 2007 - 2012 Internet Systems Consortium, Inc. ("ISC") diff --git a/contrib/bind9/lib/lwres/man/lwres_inetntop.html b/contrib/bind9/lib/lwres/man/lwres_inetntop.html index 64be8a929f9..6f1a37f6d7a 100644 --- a/contrib/bind9/lib/lwres/man/lwres_inetntop.html +++ b/contrib/bind9/lib/lwres/man/lwres_inetntop.html @@ -1,5 +1,5 @@ - + @@ -36,7 +36,6 @@ 2004 2005 2007 - 2012 Internet Systems Consortium, Inc. ("ISC") diff --git a/contrib/bind9/lib/lwres/man/lwres_noop.html b/contrib/bind9/lib/lwres/man/lwres_noop.html index 9db4d062683..69d0d386b36 100644 --- a/contrib/bind9/lib/lwres/man/lwres_noop.html +++ b/contrib/bind9/lib/lwres/man/lwres_noop.html @@ -1,5 +1,5 @@ - + @@ -36,7 +36,6 @@ 2004 2005 2007 - 2012 Internet Systems Consortium, Inc. ("ISC") diff --git a/contrib/bind9/lib/lwres/man/lwres_packet.html b/contrib/bind9/lib/lwres/man/lwres_packet.html index 36274656372..fad9076a6e8 100644 --- a/contrib/bind9/lib/lwres/man/lwres_packet.html +++ b/contrib/bind9/lib/lwres/man/lwres_packet.html @@ -1,5 +1,5 @@ - + @@ -36,7 +36,6 @@ 2004 2005 2007 - 2012 Internet Systems Consortium, Inc. ("ISC") diff --git a/contrib/bind9/lib/lwres/man/lwres_resutil.html b/contrib/bind9/lib/lwres/man/lwres_resutil.html index cbe724b3d1f..4db7610448c 100644 --- a/contrib/bind9/lib/lwres/man/lwres_resutil.html +++ b/contrib/bind9/lib/lwres/man/lwres_resutil.html @@ -1,5 +1,5 @@ ERROR: You must psecify the IP address of the interface that collected the data!\n"; exit; } - + if("$opt_h" eq "1") {usage;exit 0}; if("$opt_H" eq "1") @@ -379,7 +379,7 @@ if("$opt_p" eq "") # -p arg must be all or AN INTEGER in range 1<=N<=64K if ("$opt_p" ne "all") { - $_=$opt_p; + $_=$opt_p; unless (/^[+-]?\d+$/) { usage; @@ -394,7 +394,7 @@ if ("$opt_p" ne "all") $lookat=$opt_p; # -o arg must be all or AN INTEGER in range 1<=N<=64K - $_=$opt_o; + $_=$opt_o; unless (/^[+-]?\d+$/) { usage; @@ -438,7 +438,7 @@ open (REC, $FILENAME) || die "Cant open $FILENAME: \n"; ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,$junk)=stat REC; print "Log file $FILENAME is $size bytes in size\n"; #each record is an element of array rec[] now -while() +while() { @recs[$numrec++]=$_; } @@ -456,7 +456,7 @@ while ($loop++ < $#recs ) $bit=substr(@recs[$loop],39); $bit =~ s/,/ /g; ($sourceip,$junkit)= split " " , $bit ; - + # NOTE the . is the string concat command NOT + .......!!!! $sourceip =~ split " ", $sourceip; @@ -467,7 +467,7 @@ while ($loop++ < $#recs ) $allips = $allips . "$sourceip " ; } } - + print "Put all unique ip addresses into a 1D array\n"; @allips=split " ", $allips; @@ -490,7 +490,7 @@ while ($loop++ < $#recs ) { $a = $srcip . $icmp . $ptr . $destip . $icmp . $icmp . $lenst . $lenicmp ; } - + # dump the "->" and commas from logging $a =~ s/->//g; $a =~ s/PR//g; @@ -503,7 +503,7 @@ while ($loop++ < $#recs ) ($srcip,$junk) = split " ","$a"; $numpackets=$numpacks{"$srcip"}; $numpackets++ ; - $numpacks{"$srcip"}=$numpackets; + $numpacks{"$srcip"}=$numpackets; } @@ -546,7 +546,7 @@ while ($cnt++ < $#allips) while ($loop++ < $#recs ) { -# get src IP num, src port number, +# get src IP num, src port number, # destination IP num, destnation port number,protocol ($srcip,$srcport,$destip,$destport,$pro)= split " " , @shortrecs[$loop]; # loop over all records for the machine $uniqip @@ -564,7 +564,7 @@ while ($cnt++ < $#allips) { $srcportnam=$services{$srcport}; } -# try and get dest portname, if not there, leave it as the +# try and get dest portname, if not there, leave it as the # dest portnumber if ("$destport" eq "icmp") { $destportnam="icmp";} @@ -581,15 +581,15 @@ while ($cnt++ < $#allips) if ($srcportnam eq "") { # increment number of times a (high)/unknown port has gone to destport - $value1=$unknownsrcports{$destportnam}; - $value1++ ; + $value1=$unknownsrcports{$destportnam}; + $value1++ ; $unknownsrcports{$destportnam}=$value1; } else { # want tally(srcport) counter to be increased by 1 $value3=$tally{$srcportnam}; - $value3++ ; + $value3++ ; $tally{$srcportnam}=$value3; } } @@ -603,7 +603,7 @@ if ($set eq "N") $set="Y"; print "\n#### with $uniqip as the the source for packets ####\n"; -while(($key,$value)=each %tally) +while(($key,$value)=each %tally) { if (not "$uniqip" eq "$gatekeep") { @@ -617,7 +617,7 @@ while(($key,$value)=each %tally) -while(($key2,$value2)=each %unknownsrcports) +while(($key2,$value2)=each %unknownsrcports) { if (not "$uniqip" eq "$gatekeep") { @@ -632,7 +632,7 @@ while(($key2,$value2)=each %unknownsrcports) } # print if rests for UNIQIP IF flag is set to N then toggle flag -} # end of all IPs loop +} # end of all IPs loop } # end of if verbose option set block diff --git a/contrib/ipfilter/perl/Isbgraph b/contrib/ipfilter/perl/Isbgraph index c68b672322f..8641099b608 100644 --- a/contrib/ipfilter/perl/Isbgraph +++ b/contrib/ipfilter/perl/Isbgraph @@ -67,7 +67,7 @@ close(CNF); # number datapoints/24 hours is 1440 (minutes) # # Split into N graphs where each graph has max of 240 datapoints (4 hours) -# +# $barset=0; $m=0; @@ -117,7 +117,7 @@ $teal=$im->colorAllocate(51,153,153); $xspace= $XINIT+$option{'XCELLGRIDSIZE'}*$i +$i; # $im->line($xspace,$YINIT,$xspace,$YGRAPH,gdStyled); $num = $i+1; - + use integer; { $posis=$num - ($num/60)*60; @@ -157,7 +157,7 @@ $nextdata="N"; $count=0; $i=0; $fname=$_; - + print "fname $fname\n"; # change entry for red in colour table to green for packets LEAVING target host @@ -180,14 +180,14 @@ $nextdata="N"; if($nextdata eq "Y") { - + #$im->line($XINIT,$YGRAPH,$X,$Y,$orange); $im->line($xspaceold,$yspaceold,$xspace,$yspace,$green); } else { $im->line($xspaceold,$yspaceold,$xspace,$yspace,$red); - } + } } else { @@ -214,7 +214,7 @@ $nextdata="N"; $im->line(500,60,530,60,$green); $im->string(gdSmallFont,535,35,"Packets IN",$fg); $im->string(gdSmallFont,535,55,"Packets OUT",$fg); - + if ($option{'Bar'} ne 0) { if ($X eq $option{'XMAX'}) @@ -237,7 +237,7 @@ $nextdata="N"; $nextdata="Y"; # TOP LEFT is 0,0 on GIF (image) -# origin of plot is xinit,yinit +# origin of plot is xinit,yinit # print "little line\n"; $im->line($xspace,$yspace,$xspace,$YGRAPH,$blue); $im->line($xspace,$YGRAPH,$XINIT,$YGRAPH,$blue); diff --git a/contrib/ipfilter/perl/Services b/contrib/ipfilter/perl/Services index 401fff00dd0..e9ae317367c 100644 --- a/contrib/ipfilter/perl/Services +++ b/contrib/ipfilter/perl/Services @@ -89,7 +89,7 @@ 110 pop3 PostOfficeProtocol-Version3 111 sunrpc SUNRemoteProcedureCall 112 mcidas McIDASDataTransmissionProtocol -113 ident +113 ident 114 audionews AudioNewsMulticast 115 sftp SimpleFileTransferProtocol 116 ansanotify ANSAREXNotify @@ -426,7 +426,7 @@ 515 printer spooler 516 videotex videotex 517 talk liketenexlink,butacross -518 ntalk +518 ntalk 519 utime unixtime 520 route 521 ripng ripng @@ -451,7 +451,7 @@ 540 uucp uucpd 541 uucp-rlogin uucp-rlogin 542 commerce commerce -543 klogin +543 klogin 544 kshell krcmd 545 appleqtcsrvr appleqtcsrvr 546 dhcpv6-client DHCPv6Client @@ -463,7 +463,7 @@ 552 deviceshare deviceshare 553 pirp pirp 554 rtsp RealTimeStreamControlProtocol -555 dsf +555 dsf 556 remotefs rfsserver 557 openvms-sysipc openvms-sysipc 558 sdnskmp SDNSKMP @@ -542,7 +542,7 @@ 637 lanserver lanserver 638 mcns-sec mcns-sec 639 msdp MSDP -666 mdqs +666 mdqs 667 disclose campaigncontributiondisclosures-SDRTechnologies 668 mecomm MeComm 669 meregister MeRegister @@ -569,32 +569,32 @@ 748 ris-cm RussellInfoSciCalendarManager 749 kerberos-adm kerberosadministration 750 kerberos-iv kerberosversioniv -751 pump -752 qrh -753 rrh +751 pump +752 qrh +753 rrh 754 tell send -758 nlogin -759 con -760 ns -761 rxe -762 quotad -763 cycleserv -764 omserv -765 webster +758 nlogin +759 con +760 ns +761 rxe +762 quotad +763 cycleserv +764 omserv +765 webster 767 phonebook phone -769 vid -770 cadlock -771 rtip -772 cycleserv2 -773 notify -774 rpasswd -775 acmaint_transd -776 wpages -780 wpgs +769 vid +770 cadlock +771 rtip +772 cycleserv2 +773 notify +774 rpasswd +775 acmaint_transd +776 wpages +780 wpgs 786 concert Concert 787 qsc QSC -800 mdbs_daemon -801 device +800 mdbs_daemon +801 device 829 pkix-3-ca-ra PKIX-3CA/RA 873 rsync rsync 886 iclcnet-locate ICLcoNETionlocateserver @@ -610,10 +610,10 @@ 994 ircs ircprotocoloverTLS/SSL 995 pop3s pop3protocoloverTLS/SSL(wasspop3) 996 vsinet vsinet -997 maitrd -998 busboy -999 garcon -1000 cadlock +997 maitrd +998 busboy +999 garcon +1000 cadlock 1008 ufsd 1010 surf surf 1011 Reserved @@ -654,7 +654,7 @@ 1222 nerv SNIR&Dnetwork 1234 search-agent InfoseekSearchAgent 1239 nmsd NMSD -1248 hermes +1248 hermes 1300 h323hostcallsc H323HostCallSecure 1313 bmc_patroldb BMC_PATROLDB 1314 pdps PhotoscriptDistributedPrintingSystem @@ -695,7 +695,7 @@ 1379 dbreporter IntegritySolutions 1380 telesis-licman TelesisNetworkLicenseManager 1381 apple-licman AppleNetworkLicenseManager -1382 udt_os +1382 udt_os 1383 gwha GWHannawayNetworkLicenseManager 1384 os-licman ObjectiveSolutionsLicenseManager 1385 atex_elmd AtexPublishingLicenseManager @@ -913,7 +913,7 @@ 1597 orbplus-iiop orbplus-iiop 1598 picknfs picknfs 1599 simbaservices simbaservices -1600 issd +1600 issd 1601 aas aas 1602 inspect inspect 1603 picodbc pickodbc @@ -1079,7 +1079,7 @@ 1772 essweb-gw EssWebGateway 1773 kmscontrol KMSControl 1774 global-dtserv global-dtserv -1775 Unknown +1775 Unknown 1776 femis FederalEmergencyManagementInformationSystem 1777 powerguardian powerguardian 1778 prodigy-intrnet prodigy-internet @@ -1180,49 +1180,49 @@ 1997 gdp-port ciscoGatewayDiscoveryProtocol 1998 x25-svc-port ciscoX.25service(XOT) 1999 tcp-id-port ciscoidentificationport -2000 callbook -2001 dc -2002 globe -2004 mailbox -2005 berknet -2006 invokator -2007 dectalk -2008 conf -2009 news -2010 search +2000 callbook +2001 dc +2002 globe +2004 mailbox +2005 berknet +2006 invokator +2007 dectalk +2008 conf +2009 news +2010 search 2011 raid-cc raid -2012 ttyinfo -2013 raid-am -2014 troff -2015 cypress -2016 bootserver -2017 cypress-stat -2018 terminaldb -2019 whosockami -2020 xinupageserver -2021 servexec -2022 down -2023 xinuexpansion3 -2024 xinuexpansion4 -2025 ellpack -2026 scrabble -2027 shadowserver -2028 submitserver -2030 device2 -2032 blackboard -2033 glogger -2034 scoremgr -2035 imsldoc -2038 objectmanager -2040 lam -2041 interbase +2012 ttyinfo +2013 raid-am +2014 troff +2015 cypress +2016 bootserver +2017 cypress-stat +2018 terminaldb +2019 whosockami +2020 xinupageserver +2021 servexec +2022 down +2023 xinuexpansion3 +2024 xinuexpansion4 +2025 ellpack +2026 scrabble +2027 shadowserver +2028 submitserver +2030 device2 +2032 blackboard +2033 glogger +2034 scoremgr +2035 imsldoc +2038 objectmanager +2040 lam +2041 interbase 2042 isis isis 2043 isis-bcast isis-bcast -2044 rimsl -2045 cdfunc -2046 sdfunc -2047 dls -2048 dls-monitor +2044 rimsl +2045 cdfunc +2046 sdfunc +2047 dls +2048 dls-monitor 2049 nfsd-or-shilp 2065 dlsrpn DataLinkSwitchReadPortNumber 2067 dlswpn DataLinkSwitchWritePortNumber @@ -1798,8 +1798,8 @@ 4868 phrelay PhotonRelay 4869 phrelaydbg PhotonRelayDebug 4885 abbs ABBS -5000 commplex-main -5001 commplex-link +5000 commplex-main +5001 commplex-link 5002 rfe radiofreeethernet 5003 fmpro-internal FileMaker,Inc.-Proprietarynamebinding 5004 avt-profile-1 avt-profile-1 @@ -1812,13 +1812,13 @@ 5051 ita-agent ITAAgent 5052 ita-manager ITAManager 5060 sip SIP -5145 rmonitor_secure +5145 rmonitor_secure 5150 atmp AscendTunnelManagementProtocol 5190 aol America-Online 5191 aol-1 AmericaOnline1 5192 aol-2 AmericaOnline2 5193 aol-3 AmericaOnline3 -5236 padl2sim +5236 padl2sim 5272 pk PK 5300 hacl-hb #HAclusterheartbeat 5301 hacl-gs #HAclustergeneralservices @@ -1975,7 +1975,7 @@ 6506 badm_pub BoKSAdminPublicPort 6507 bdir_priv BoKSDirServer,PrivatePort 6508 bdir_pub BoKSDirServer,PublicPort -6558 xdsxdm +6558 xdsxdm 6665 ircu 6666 ircu 6667 ircu @@ -2059,7 +2059,7 @@ 9000 cslistener CSlistener 9006 sctp SCTP 9090 websm WebSM -9535 man +9535 man 9594 msgsys MessageSystem 9595 pds PingDiscoveryService 9876 sd SessionDirector @@ -2093,7 +2093,7 @@ 13821 dsmcc-download DSMCCDownloadProtocol 13822 dsmcc-ccp DSMCCChannelChangeProtocol 14001 itu-sccp-ss7 ITUSCCP(SS7) -17007 isode-dua +17007 isode-dua 17219 chipper Chipper 18000 biimenu BeckmanInstruments,Inc. 19541 jcp JCPClient diff --git a/contrib/ipfilter/perl/ipfmeta.pl b/contrib/ipfilter/perl/ipfmeta.pl index 1a7bb3f1a0e..decc35b8bd2 100644 --- a/contrib/ipfilter/perl/ipfmeta.pl +++ b/contrib/ipfilter/perl/ipfmeta.pl @@ -83,7 +83,7 @@ sub expand { return @retlines; } - + __END__ =head1 NAME @@ -164,7 +164,7 @@ block in from UNWANTED to any pass in from NOC to WEBSERVERS port = MGMT-PORTS pass out all - + I ipfmeta ipf.objs ipf.rules diff --git a/contrib/ipfilter/perl/logfilter.pl b/contrib/ipfilter/perl/logfilter.pl index 6ebe401ab4e..fd0da6db181 100644 --- a/contrib/ipfilter/perl/logfilter.pl +++ b/contrib/ipfilter/perl/logfilter.pl @@ -3,7 +3,7 @@ # Author: Chris Grant # Copyright 1999, Codetalker Communications, Inc. # -# This script takes a firewall log and breaks it into several +# This script takes a firewall log and breaks it into several # different files. Each file is named based on the service that # runs on the port that was recognized in log line. After # this script has run, you should end up with several files. @@ -18,11 +18,11 @@ # # You may be wondering why I haven't simply parsed RFC1700 to come up # with a list of port numbers and files. The reason is that I don't -# believe reading firewall logs should be all that automated. You +# believe reading firewall logs should be all that automated. You # should be familiar with what probes are hitting your system. By -# manually adding entries to the data section this ensures that I -# have at least educated myself about what this protocol is, what -# the potential exposure is, and why you might be seeing this traffic. +# manually adding entries to the data section this ensures that I +# have at least educated myself about what this protocol is, what +# the potential exposure is, and why you might be seeing this traffic. %icmp = (); %udp = (); @@ -61,30 +61,30 @@ while($line = ) { # determine the protocol - send to unknown.log if not found SWITCH: { - ($line =~ m /\sicmp\s/) && do { + ($line =~ m /\sicmp\s/) && do { # - # ICMP Protocol + # ICMP Protocol # # Extract the icmp packet information specifying the type. - # + # # Note: Must check for ICMP first because this may be an ICMP reply # to a TCP or UDP connection (eg Port Unreachable). - + ($icmptype) = $line =~ m/icmp (\d+)\/\d+/; $filename = $TIDBITSFILE; $filename = $icmp{$icmptype} if (defined($icmp{$icmptype})); - last SWITCH; + last SWITCH; }; - ($line =~ m /\stcp\s/) && do { + ($line =~ m /\stcp\s/) && do { - # + # # TCP Protocol # - # extract the source and destination ports and compare them to + # extract the source and destination ports and compare them to # known ports in the tcp hash. For the first match, place this # line in the file specified by the tcp hash. Ignore one of the # port matches if both ports happen to be known services. @@ -96,14 +96,14 @@ while($line = ) { $filename = $tcp{$sport} if (defined($tcp{$sport})); $filename = $tcp{$dport} if (defined($tcp{$dport})); - last SWITCH; + last SWITCH; }; - ($line =~ m /\sudp\s/) && do { + ($line =~ m /\sudp\s/) && do { # # UDP Protocol - same procedure as with TCP, different hash - # + # ($sport, $dport) = $line =~ m/\d+\.\d+\.\d+\.\d+,(\d+) -> \d+\.\d+\.\d+\.\d+,(\d+)/; @@ -111,7 +111,7 @@ while($line = ) { $filename = $udp{$sport} if (defined($udp{$sport})); $filename = $udp{$dport} if (defined($udp{$dport})); - last SWITCH; + last SWITCH; }; # @@ -126,7 +126,7 @@ while($line = ) { # check for filename in the openfiles hash. if it exists then write # to the given handle. otherwise open a handle to the file and add # it to the hash of open files. - + if (defined($openfiles{$filename})) { $handle = $openfiles{$filename}; } else { @@ -178,4 +178,4 @@ tcp 6667 irc.log tcp 7070 realaudio.log tcp 8080 http.log tcp 12345 netbus.log -udp 31337 backorifice.log \ No newline at end of file +udp 31337 backorifice.log diff --git a/contrib/ipfilter/radix.c b/contrib/ipfilter/radix.c deleted file mode 100644 index 8c67562553e..00000000000 --- a/contrib/ipfilter/radix.c +++ /dev/null @@ -1,1214 +0,0 @@ -/* $FreeBSD$ */ - -/* - * Copyright (c) 1988, 1989, 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. - * - * 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. - * - * @(#)radix.c 8.6 (Berkeley) 10/17/95 - */ - -/* - * Routines to build and maintain radix trees for routing lookups. - */ -#if defined(KERNEL) || defined(_KERNEL) -# undef KERNEL -# undef _KERNEL -# define KERNEL 1 -# define _KERNEL 1 -#endif -#define __SYS_ATOMIC_OPS_H__ -#if !defined(__svr4__) && !defined(__SVR4) && !defined(__osf__) && \ - !defined(__hpux) && !defined(__sgi) -#include -#endif -#ifndef __P -# ifdef __STDC__ -# define __P(x) x -# else -# define __P(x) () -# endif -#endif -#ifdef __osf__ -# define CONST -# define _IPV6_SWTAB_H -# define _PROTO_NET_H_ -# define _PROTO_IPV6_H -# include -#endif - -#include -#ifdef _KERNEL -#include -#else -void panic __P((char *str)); -#include -#include -#include -#include -#endif -#ifdef __hpux -#include -#else -#include -#endif -#include -#include -#include -#include -#ifdef SOLARIS2 -# define _RADIX_H_ -#endif -#include "netinet/ip_compat.h" -#include "netinet/ip_fil.h" -#ifdef SOLARIS2 -# undef _RADIX_H_ -#endif -/* END OF INCLUDES */ -#include "radix_ipf.h" -#ifndef min -# define min MIN -#endif -#ifndef max -# define max MAX -#endif - -int max_keylen = 16; -static struct radix_mask *rn_mkfreelist; -static struct radix_node_head *mask_rnhead; -static char *addmask_key; -static u_char normal_chars[] = {0, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff}; -static char *rn_zeros = NULL, *rn_ones = NULL; - -#define rn_masktop (mask_rnhead->rnh_treetop) -#undef Bcmp -#define Bcmp(a, b, l) (l == 0 ? 0 : bcmp((caddr_t)(a), (caddr_t)(b), (u_long)l)) - -static int rn_satisfies_leaf __P((char *, struct radix_node *, int)); -static int rn_lexobetter __P((void *, void *)); -static struct radix_mask *rn_new_radix_mask __P((struct radix_node *, - struct radix_mask *)); -static int rn_freenode __P((struct radix_node *, void *)); -#if defined(AIX) && !defined(_KERNEL) -struct radix_node *rn_match __P((void *, struct radix_node_head *)); -struct radix_node *rn_addmask __P((int, int, void *)); -#define FreeS(x, y) KFREES(x, y) -#define Bcopy(x, y, z) bcopy(x, y, z) -#endif - -/* - * The data structure for the keys is a radix tree with one way - * branching removed. The index rn_b at an internal node n represents a bit - * position to be tested. The tree is arranged so that all descendants - * of a node n have keys whose bits all agree up to position rn_b - 1. - * (We say the index of n is rn_b.) - * - * There is at least one descendant which has a one bit at position rn_b, - * and at least one with a zero there. - * - * A route is determined by a pair of key and mask. We require that the - * bit-wise logical and of the key and mask to be the key. - * We define the index of a route to associated with the mask to be - * the first bit number in the mask where 0 occurs (with bit number 0 - * representing the highest order bit). - * - * We say a mask is normal if every bit is 0, past the index of the mask. - * If a node n has a descendant (k, m) with index(m) == index(n) == rn_b, - * and m is a normal mask, then the route applies to every descendant of n. - * If the index(m) < rn_b, this implies the trailing last few bits of k - * before bit b are all 0, (and hence consequently true of every descendant - * of n), so the route applies to all descendants of the node as well. - * - * Similar logic shows that a non-normal mask m such that - * index(m) <= index(n) could potentially apply to many children of n. - * Thus, for each non-host route, we attach its mask to a list at an internal - * node as high in the tree as we can go. - * - * The present version of the code makes use of normal routes in short- - * circuiting an explicit mask and compare operation when testing whether - * a key satisfies a normal route, and also in remembering the unique leaf - * that governs a subtree. - */ - -struct radix_node * -rn_search(v_arg, head) - void *v_arg; - struct radix_node *head; -{ - struct radix_node *x; - caddr_t v; - - for (x = head, v = v_arg; x->rn_b >= 0;) { - if (x->rn_bmask & v[x->rn_off]) - x = x->rn_r; - else - x = x->rn_l; - } - return (x); -} - -struct radix_node * -rn_search_m(v_arg, head, m_arg) - struct radix_node *head; - void *v_arg, *m_arg; -{ - struct radix_node *x; - caddr_t v = v_arg, m = m_arg; - - for (x = head; x->rn_b >= 0;) { - if ((x->rn_bmask & m[x->rn_off]) && - (x->rn_bmask & v[x->rn_off])) - x = x->rn_r; - else - x = x->rn_l; - } - return x; -} - -int -rn_refines(m_arg, n_arg) - void *m_arg, *n_arg; -{ - caddr_t m = m_arg, n = n_arg; - caddr_t lim, lim2 = lim = n + *(u_char *)n; - int longer = (*(u_char *)n++) - (int)(*(u_char *)m++); - int masks_are_equal = 1; - - if (longer > 0) - lim -= longer; - while (n < lim) { - if (*n & ~(*m)) - return 0; - if (*n++ != *m++) - masks_are_equal = 0; - } - while (n < lim2) - if (*n++) - return 0; - if (masks_are_equal && (longer < 0)) - for (lim2 = m - longer; m < lim2; ) - if (*m++) - return 1; - return (!masks_are_equal); -} - -struct radix_node * -rn_lookup(v_arg, m_arg, head) - void *v_arg, *m_arg; - struct radix_node_head *head; -{ - struct radix_node *x; - caddr_t netmask = 0; - - if (m_arg) { - if ((x = rn_addmask(m_arg, 1, head->rnh_treetop->rn_off)) == 0) - return (0); - netmask = x->rn_key; - } - x = rn_match(v_arg, head); - if (x && netmask) { - while (x && x->rn_mask != netmask) - x = x->rn_dupedkey; - } - return x; -} - -static int -rn_satisfies_leaf(trial, leaf, skip) - char *trial; - struct radix_node *leaf; - int skip; -{ - char *cp = trial, *cp2 = leaf->rn_key, *cp3 = leaf->rn_mask; - char *cplim; - int length = min(*(u_char *)cp, *(u_char *)cp2); - - if (cp3 == 0) - cp3 = rn_ones; - else - length = min(length, *(u_char *)cp3); - cplim = cp + length; - cp3 += skip; - cp2 += skip; - for (cp += skip; cp < cplim; cp++, cp2++, cp3++) - if ((*cp ^ *cp2) & *cp3) - return 0; - return 1; -} - -struct radix_node * -rn_match(v_arg, head) - void *v_arg; - struct radix_node_head *head; -{ - caddr_t v = v_arg; - struct radix_node *t = head->rnh_treetop, *x; - caddr_t cp = v, cp2; - caddr_t cplim; - struct radix_node *saved_t, *top = t; - int off = t->rn_off, vlen = *(u_char *)cp, matched_off; - int test, b, rn_b; - - /* - * Open code rn_search(v, top) to avoid overhead of extra - * subroutine call. - */ - for (; t->rn_b >= 0; ) { - if (t->rn_bmask & cp[t->rn_off]) - t = t->rn_r; - else - t = t->rn_l; - } - /* - * See if we match exactly as a host destination - * or at least learn how many bits match, for normal mask finesse. - * - * It doesn't hurt us to limit how many bytes to check - * to the length of the mask, since if it matches we had a genuine - * match and the leaf we have is the most specific one anyway; - * if it didn't match with a shorter length it would fail - * with a long one. This wins big for class B&C netmasks which - * are probably the most common case... - */ - if (t->rn_mask) - vlen = *(u_char *)t->rn_mask; - cp += off; - cp2 = t->rn_key + off; - cplim = v + vlen; - for (; cp < cplim; cp++, cp2++) - if (*cp != *cp2) - goto on1; - /* - * This extra grot is in case we are explicitly asked - * to look up the default. Ugh! - */ - if ((t->rn_flags & RNF_ROOT) && t->rn_dupedkey) - t = t->rn_dupedkey; - return t; -on1: - test = (*cp ^ *cp2) & 0xff; /* find first bit that differs */ - for (b = 7; (test >>= 1) > 0;) - b--; - matched_off = cp - v; - b += matched_off << 3; - rn_b = -1 - b; - /* - * If there is a host route in a duped-key chain, it will be first. - */ - if ((saved_t = t)->rn_mask == 0) - t = t->rn_dupedkey; - for (; t; t = t->rn_dupedkey) - /* - * Even if we don't match exactly as a host, - * we may match if the leaf we wound up at is - * a route to a net. - */ - if (t->rn_flags & RNF_NORMAL) { - if (rn_b <= t->rn_b) - return t; - } else if (rn_satisfies_leaf(v, t, matched_off)) - return t; - t = saved_t; - /* start searching up the tree */ - do { - struct radix_mask *m; - t = t->rn_p; - m = t->rn_mklist; - if (m) { - /* - * If non-contiguous masks ever become important - * we can restore the masking and open coding of - * the search and satisfaction test and put the - * calculation of "off" back before the "do". - */ - do { - if (m->rm_flags & RNF_NORMAL) { - if (rn_b <= m->rm_b) - return (m->rm_leaf); - } else { - off = min(t->rn_off, matched_off); - x = rn_search_m(v, t, m->rm_mask); - while (x && x->rn_mask != m->rm_mask) - x = x->rn_dupedkey; - if (x && rn_satisfies_leaf(v, x, off)) - return x; - } - m = m->rm_mklist; - } while (m); - } - } while (t != top); - return 0; -} - -#ifdef RN_DEBUG -int rn_nodenum; -struct radix_node *rn_clist; -int rn_saveinfo; -int rn_debug = 1; -#endif - -struct radix_node * -rn_newpair(v, b, nodes) - void *v; - int b; - struct radix_node nodes[2]; -{ - struct radix_node *tt = nodes, *t = tt + 1; - t->rn_b = b; - t->rn_bmask = 0x80 >> (b & 7); - t->rn_l = tt; - t->rn_off = b >> 3; - tt->rn_b = -1; - tt->rn_key = (caddr_t)v; - tt->rn_p = t; - tt->rn_flags = t->rn_flags = RNF_ACTIVE; -#ifdef RN_DEBUG - tt->rn_info = rn_nodenum++; - t->rn_info = rn_nodenum++; - tt->rn_twin = t; - tt->rn_ybro = rn_clist; - rn_clist = tt; -#endif - return t; -} - -struct radix_node * -rn_insert(v_arg, head, dupentry, nodes) - void *v_arg; - struct radix_node_head *head; - int *dupentry; - struct radix_node nodes[2]; -{ - caddr_t v = v_arg; - struct radix_node *top = head->rnh_treetop; - int head_off = top->rn_off, vlen = (int)*((u_char *)v); - struct radix_node *t = rn_search(v_arg, top); - caddr_t cp = v + head_off; - int b; - struct radix_node *tt; - -#ifdef RN_DEBUG - if (rn_debug) - log(LOG_DEBUG, "rn_insert(%p,%p,%p,%p)\n", v_arg, head, dupentry, nodes); -#endif - /* - * Find first bit at which v and t->rn_key differ - */ - { - caddr_t cp2 = t->rn_key + head_off; - int cmp_res; - caddr_t cplim = v + vlen; - - while (cp < cplim) - if (*cp2++ != *cp++) - goto on1; - *dupentry = 1; - return t; -on1: - *dupentry = 0; - cmp_res = (cp[-1] ^ cp2[-1]) & 0xff; - for (b = (cp - v) << 3; cmp_res; b--) - cmp_res >>= 1; - } - { - struct radix_node *p, *x = top; - cp = v; - do { - p = x; - if (cp[x->rn_off] & x->rn_bmask) - x = x->rn_r; - else - x = x->rn_l; - } while (b > (unsigned) x->rn_b); /* x->rn_b < b && x->rn_b >= 0 */ -#ifdef RN_DEBUG - if (rn_debug) - log(LOG_DEBUG, "rn_insert: Going In:\n"); // traverse(p); -#endif - t = rn_newpair(v_arg, b, nodes); - tt = t->rn_l; - if ((cp[p->rn_off] & p->rn_bmask) == 0) - p->rn_l = t; - else - p->rn_r = t; - x->rn_p = t; - t->rn_p = p; /* frees x, p as temp vars below */ - if ((cp[t->rn_off] & t->rn_bmask) == 0) { - t->rn_r = x; - } else { - t->rn_r = tt; - t->rn_l = x; - } -#ifdef RN_DEBUG - if (rn_debug) - log(LOG_DEBUG, "rn_insert: Coming Out:\n"); // traverse(p); -#endif - } - return (tt); -} - -struct radix_node * -rn_addmask(n_arg, search, skip) - int search, skip; - void *n_arg; -{ - caddr_t netmask = (caddr_t)n_arg; - struct radix_node *x; - caddr_t cp, cplim; - int b = 0, mlen, j; - int maskduplicated, m0, isnormal; - struct radix_node *saved_x; - static int last_zeroed = 0; - -#ifdef RN_DEBUG - if (rn_debug) - log(LOG_DEBUG, "rn_addmask(%p,%d,%d)\n", n_arg, search, skip); -#endif - mlen = *(u_char *)netmask; - if ((mlen = *(u_char *)netmask) > max_keylen) - mlen = max_keylen; - if (skip == 0) - skip = 1; - if (mlen <= skip) - return (mask_rnhead->rnh_nodes); - if (skip > 1) - Bcopy(rn_ones + 1, addmask_key + 1, skip - 1); - if ((m0 = mlen) > skip) - Bcopy(netmask + skip, addmask_key + skip, mlen - skip); - /* - * Trim trailing zeroes. - */ - for (cp = addmask_key + mlen; (cp > addmask_key) && cp[-1] == 0;) - cp--; - mlen = cp - addmask_key; - if (mlen <= skip) { - if (m0 >= last_zeroed) - last_zeroed = mlen; - return (mask_rnhead->rnh_nodes); - } - if (m0 < last_zeroed) - Bzero(addmask_key + m0, last_zeroed - m0); - *addmask_key = last_zeroed = mlen; - x = rn_search(addmask_key, rn_masktop); - if (Bcmp(addmask_key, x->rn_key, mlen) != 0) - x = 0; - if (x || search) - return (x); - R_Malloc(x, struct radix_node *, max_keylen + 2 * sizeof (*x)); - if ((saved_x = x) == 0) - return (0); - Bzero(x, max_keylen + 2 * sizeof (*x)); - netmask = cp = (caddr_t)(x + 2); - Bcopy(addmask_key, cp, mlen); - x = rn_insert(cp, mask_rnhead, &maskduplicated, x); - if (maskduplicated) { -#if 0 - log(LOG_ERR, "rn_addmask: mask impossibly already in tree\n"); -#endif - Free(saved_x); - return (x); - } - /* - * Calculate index of mask, and check for normalcy. - */ - cplim = netmask + mlen; - isnormal = 1; - for (cp = netmask + skip; (cp < cplim) && *(u_char *)cp == 0xff;) - cp++; - if (cp != cplim) { - for (j = 0x80; (j & *cp) != 0; j >>= 1) - b++; - if (*cp != normal_chars[b] || cp != (cplim - 1)) - isnormal = 0; - } - b += (cp - netmask) << 3; - x->rn_b = -1 - b; - if (isnormal) - x->rn_flags |= RNF_NORMAL; - return (x); -} - -static int /* XXX: arbitrary ordering for non-contiguous masks */ -rn_lexobetter(m_arg, n_arg) - void *m_arg, *n_arg; -{ - u_char *mp = m_arg, *np = n_arg, *lim; - - if (*mp > *np) - return 1; /* not really, but need to check longer one first */ - if (*mp == *np) - for (lim = mp + *mp; mp < lim;) - if (*mp++ > *np++) - return 1; - return 0; -} - -static struct radix_mask * -rn_new_radix_mask(tt, next) - struct radix_node *tt; - struct radix_mask *next; -{ - struct radix_mask *m; - - MKGet(m); - if (m == 0) { -#if 0 - log(LOG_ERR, "Mask for route not entered\n"); -#endif - return (0); - } - Bzero(m, sizeof *m); - m->rm_b = tt->rn_b; - m->rm_flags = tt->rn_flags; - if (tt->rn_flags & RNF_NORMAL) - m->rm_leaf = tt; - else - m->rm_mask = tt->rn_mask; - m->rm_mklist = next; - tt->rn_mklist = m; - return m; -} - -struct radix_node * -rn_addroute(v_arg, n_arg, head, treenodes) - void *v_arg, *n_arg; - struct radix_node_head *head; - struct radix_node treenodes[2]; -{ - caddr_t v = (caddr_t)v_arg, netmask = (caddr_t)n_arg; - struct radix_node *t, *x = NULL, *tt; - struct radix_node *saved_tt, *top = head->rnh_treetop; - short b = 0, b_leaf = 0; - int keyduplicated; - caddr_t mmask; - struct radix_mask *m, **mp; - -#ifdef RN_DEBUG - if (rn_debug) - log(LOG_DEBUG, "rn_addroute(%p,%p,%p,%p)\n", v_arg, n_arg, head, treenodes); -#endif - /* - * In dealing with non-contiguous masks, there may be - * many different routes which have the same mask. - * We will find it useful to have a unique pointer to - * the mask to speed avoiding duplicate references at - * nodes and possibly save time in calculating indices. - */ - if (netmask) { - if ((x = rn_addmask(netmask, 0, top->rn_off)) == 0) - return (0); - b_leaf = x->rn_b; - b = -1 - x->rn_b; - netmask = x->rn_key; - } - /* - * Deal with duplicated keys: attach node to previous instance - */ - saved_tt = tt = rn_insert(v, head, &keyduplicated, treenodes); - if (keyduplicated) { - for (t = tt; tt; t = tt, tt = tt->rn_dupedkey) { - if (tt->rn_mask == netmask) - return (0); - if (netmask == 0 || - (tt->rn_mask && - ((b_leaf < tt->rn_b) || /* index(netmask) > node */ - rn_refines(netmask, tt->rn_mask) || - rn_lexobetter(netmask, tt->rn_mask)))) - break; - } - /* - * If the mask is not duplicated, we wouldn't - * find it among possible duplicate key entries - * anyway, so the above test doesn't hurt. - * - * We sort the masks for a duplicated key the same way as - * in a masklist -- most specific to least specific. - * This may require the unfortunate nuisance of relocating - * the head of the list. - * - * We also reverse, or doubly link the list through the - * parent pointer. - */ - if (tt == saved_tt) { - struct radix_node *xx = x; - /* link in at head of list */ - (tt = treenodes)->rn_dupedkey = t; - tt->rn_flags = t->rn_flags; - tt->rn_p = x = t->rn_p; - t->rn_p = tt; - if (x->rn_l == t) - x->rn_l = tt; - else - x->rn_r = tt; - saved_tt = tt; - x = xx; - } else { - (tt = treenodes)->rn_dupedkey = t->rn_dupedkey; - t->rn_dupedkey = tt; - tt->rn_p = t; - if (tt->rn_dupedkey) - tt->rn_dupedkey->rn_p = tt; - } -#ifdef RN_DEBUG - t=tt+1; - tt->rn_info = rn_nodenum++; - t->rn_info = rn_nodenum++; - tt->rn_twin = t; - tt->rn_ybro = rn_clist; - rn_clist = tt; -#endif - tt->rn_key = (caddr_t) v; - tt->rn_b = -1; - tt->rn_flags = RNF_ACTIVE; - } - /* - * Put mask in tree. - */ - if (netmask) { - tt->rn_mask = netmask; - tt->rn_b = x->rn_b; - tt->rn_flags |= x->rn_flags & RNF_NORMAL; - } - t = saved_tt->rn_p; - if (keyduplicated) - goto on2; - b_leaf = -1 - t->rn_b; - if (t->rn_r == saved_tt) - x = t->rn_l; - else - x = t->rn_r; - /* Promote general routes from below */ - if (x->rn_b < 0) { - for (mp = &t->rn_mklist; x; x = x->rn_dupedkey) - if (x->rn_mask && (x->rn_b >= b_leaf) && x->rn_mklist == 0) { - *mp = m = rn_new_radix_mask(x, 0); - if (m) - mp = &m->rm_mklist; - } - } else if (x->rn_mklist) { - /* - * Skip over masks whose index is > that of new node - */ - for (mp = &x->rn_mklist; (m = *mp) != NULL; mp = &m->rm_mklist) - if (m->rm_b >= b_leaf) - break; - t->rn_mklist = m; - *mp = 0; - } -on2: - /* Add new route to highest possible ancestor's list */ - if ((netmask == 0) || (b > t->rn_b )) - return tt; /* can't lift at all */ - b_leaf = tt->rn_b; - do { - x = t; - t = t->rn_p; - } while (b <= t->rn_b && x != top); - /* - * Search through routes associated with node to - * insert new route according to index. - * Need same criteria as when sorting dupedkeys to avoid - * double loop on deletion. - */ - for (mp = &x->rn_mklist; (m = *mp) != NULL; mp = &m->rm_mklist) { - if (m->rm_b < b_leaf) - continue; - if (m->rm_b > b_leaf) - break; - if (m->rm_flags & RNF_NORMAL) { - mmask = m->rm_leaf->rn_mask; - if (tt->rn_flags & RNF_NORMAL) { -#if 0 - log(LOG_ERR, "Non-unique normal route," - " mask not entered\n"); -#endif - return tt; - } - } else - mmask = m->rm_mask; - if (mmask == netmask) { - m->rm_refs++; - tt->rn_mklist = m; - return tt; - } - if (rn_refines(netmask, mmask) - || rn_lexobetter(netmask, mmask)) - break; - } - *mp = rn_new_radix_mask(tt, *mp); - return tt; -} - -struct radix_node * -rn_delete(v_arg, netmask_arg, head) - void *v_arg, *netmask_arg; - struct radix_node_head *head; -{ - struct radix_node *t, *p, *x, *tt; - struct radix_mask *m, *saved_m, **mp; - struct radix_node *dupedkey, *saved_tt, *top; - caddr_t v, netmask; - int b, head_off, vlen; - - v = v_arg; - netmask = netmask_arg; - x = head->rnh_treetop; - tt = rn_search(v, x); - head_off = x->rn_off; - vlen = *(u_char *)v; - saved_tt = tt; - top = x; - if (tt == 0 || - Bcmp(v + head_off, tt->rn_key + head_off, vlen - head_off)) - return (0); - /* - * Delete our route from mask lists. - */ - if (netmask) { - if ((x = rn_addmask(netmask, 1, head_off)) == 0) - return (0); - netmask = x->rn_key; - while (tt->rn_mask != netmask) - if ((tt = tt->rn_dupedkey) == 0) - return (0); - } - if (tt->rn_mask == 0 || (saved_m = m = tt->rn_mklist) == 0) - goto on1; - if (tt->rn_flags & RNF_NORMAL) { - if (m->rm_leaf != tt || m->rm_refs > 0) { -#if 0 - log(LOG_ERR, "rn_delete: inconsistent annotation\n"); -#endif - return 0; /* dangling ref could cause disaster */ - } - } else { - if (m->rm_mask != tt->rn_mask) { -#if 0 - log(LOG_ERR, "rn_delete: inconsistent annotation\n"); -#endif - goto on1; - } - if (--m->rm_refs >= 0) - goto on1; - } - b = -1 - tt->rn_b; - t = saved_tt->rn_p; - if (b > t->rn_b) - goto on1; /* Wasn't lifted at all */ - do { - x = t; - t = t->rn_p; - } while (b <= t->rn_b && x != top); - for (mp = &x->rn_mklist; (m = *mp) != NULL; mp = &m->rm_mklist) - if (m == saved_m) { - *mp = m->rm_mklist; - MKFree(m); - break; - } - if (m == 0) { -#if 0 - log(LOG_ERR, "rn_delete: couldn't find our annotation\n"); -#endif - if (tt->rn_flags & RNF_NORMAL) - return (0); /* Dangling ref to us */ - } -on1: - /* - * Eliminate us from tree - */ - if (tt->rn_flags & RNF_ROOT) - return (0); -#ifdef RN_DEBUG - /* Get us out of the creation list */ - for (t = rn_clist; t && t->rn_ybro != tt; t = t->rn_ybro) - ; - if (t) t->rn_ybro = tt->rn_ybro; -#endif - t = tt->rn_p; - dupedkey = saved_tt->rn_dupedkey; - if (dupedkey) { - /* - * Here, tt is the deletion target and - * saved_tt is the head of the dupedkey chain. - */ - if (tt == saved_tt) { - x = dupedkey; - x->rn_p = t; - if (t->rn_l == tt) - t->rn_l = x; - else - t->rn_r = x; - } else { - /* find node in front of tt on the chain */ - for (x = p = saved_tt; p && p->rn_dupedkey != tt;) - p = p->rn_dupedkey; - if (p) { - p->rn_dupedkey = tt->rn_dupedkey; - if (tt->rn_dupedkey) - tt->rn_dupedkey->rn_p = p; - } -#if 0 - else - log(LOG_ERR, "rn_delete: couldn't find us\n"); -#endif - } - t = tt + 1; - if (t->rn_flags & RNF_ACTIVE) { -#ifndef RN_DEBUG - *++x = *t; - p = t->rn_p; -#else - b = t->rn_info; - *++x = *t; - t->rn_info = b; - p = t->rn_p; -#endif - if (p->rn_l == t) - p->rn_l = x; - else - p->rn_r = x; - x->rn_l->rn_p = x; - x->rn_r->rn_p = x; - } - goto out; - } - if (t->rn_l == tt) - x = t->rn_r; - else - x = t->rn_l; - p = t->rn_p; - if (p->rn_r == t) - p->rn_r = x; - else - p->rn_l = x; - x->rn_p = p; - /* - * Demote routes attached to us. - */ - if (t->rn_mklist) { - if (x->rn_b >= 0) { - for (mp = &x->rn_mklist; (m = *mp) != NULL;) - mp = &m->rm_mklist; - *mp = t->rn_mklist; - } else { - /* If there are any key,mask pairs in a sibling - duped-key chain, some subset will appear sorted - in the same order attached to our mklist */ - for (m = t->rn_mklist; m && x; x = x->rn_dupedkey) - if (m == x->rn_mklist) { - struct radix_mask *mm = m->rm_mklist; - x->rn_mklist = 0; - if (--(m->rm_refs) < 0) - MKFree(m); - m = mm; - } -#if 0 - if (m) - log(LOG_ERR, "%s %p at %p\n", - "rn_delete: Orphaned Mask", m, x); -#endif - } - } - /* - * We may be holding an active internal node in the tree. - */ - x = tt + 1; - if (t != x) { -#ifndef RN_DEBUG - *t = *x; -#else - b = t->rn_info; - *t = *x; - t->rn_info = b; -#endif - t->rn_l->rn_p = t; - t->rn_r->rn_p = t; - p = x->rn_p; - if (p->rn_l == x) - p->rn_l = t; - else - p->rn_r = t; - } -out: - tt->rn_flags &= ~RNF_ACTIVE; - tt[1].rn_flags &= ~RNF_ACTIVE; - return (tt); -} - -int -rn_walktree(h, f, w) - struct radix_node_head *h; - int (*f) __P((struct radix_node *, void *)); - void *w; -{ - int error; - struct radix_node *base, *next; - struct radix_node *rn = h->rnh_treetop; - /* - * This gets complicated because we may delete the node - * while applying the function f to it, so we need to calculate - * the successor node in advance. - */ - /* First time through node, go left */ - while (rn->rn_b >= 0) - rn = rn->rn_l; - for (;;) { - base = rn; - /* If at right child go back up, otherwise, go right */ - while (rn->rn_p->rn_r == rn && (rn->rn_flags & RNF_ROOT) == 0) - rn = rn->rn_p; - /* Find the next *leaf* since next node might vanish, too */ - for (rn = rn->rn_p->rn_r; rn->rn_b >= 0;) - rn = rn->rn_l; - next = rn; - /* Process leaves */ - while ((rn = base) != NULL) { - base = rn->rn_dupedkey; - if (!(rn->rn_flags & RNF_ROOT) - && (error = (*f)(rn, w))) - return (error); - } - rn = next; - if (rn->rn_flags & RNF_ROOT) - return (0); - } - /* NOTREACHED */ -} - -int -rn_inithead(head, off) - void **head; - int off; -{ - struct radix_node_head *rnh; - - if (*head) - return (1); - R_Malloc(rnh, struct radix_node_head *, sizeof (*rnh)); - if (rnh == 0) - return (0); - *head = rnh; - return rn_inithead0(rnh, off); -} - -int -rn_inithead0(rnh, off) - struct radix_node_head *rnh; - int off; -{ - struct radix_node *t, *tt, *ttt; - - Bzero(rnh, sizeof (*rnh)); - t = rn_newpair(rn_zeros, off, rnh->rnh_nodes); - ttt = rnh->rnh_nodes + 2; - t->rn_r = ttt; - t->rn_p = t; - tt = t->rn_l; - tt->rn_flags = t->rn_flags = RNF_ROOT | RNF_ACTIVE; - tt->rn_b = -1 - off; - *ttt = *tt; - ttt->rn_key = rn_ones; - rnh->rnh_addaddr = rn_addroute; - rnh->rnh_deladdr = rn_delete; - rnh->rnh_matchaddr = rn_match; - rnh->rnh_lookup = rn_lookup; - rnh->rnh_walktree = rn_walktree; - rnh->rnh_treetop = t; - return (1); -} - -void -rn_init() -{ - char *cp, *cplim; - - if (max_keylen == 0) { -#if 0 - log(LOG_ERR, - "rn_init: radix functions require max_keylen be set\n"); -#endif - return; - } - if (rn_zeros == NULL) { - R_Malloc(rn_zeros, char *, 3 * max_keylen); - } - if (rn_zeros == NULL) - panic("rn_init"); - Bzero(rn_zeros, 3 * max_keylen); - rn_ones = cp = rn_zeros + max_keylen; - addmask_key = cplim = rn_ones + max_keylen; - while (cp < cplim) - *cp++ = -1; - if (rn_inithead((void *)&mask_rnhead, 0) == 0) - panic("rn_init 2"); -} - - -static int -rn_freenode(struct radix_node *n, void *p) -{ - struct radix_node_head *rnh = p; - struct radix_node *d; - - d = rnh->rnh_deladdr(n->rn_key, NULL, rnh); - if (d != NULL) { - FreeS(d, max_keylen + 2 * sizeof (*d)); - } - return 0; -} - - -void -rn_freehead(rnh) - struct radix_node_head *rnh; -{ - - (void)rn_walktree(rnh, rn_freenode, rnh); - - rnh->rnh_addaddr = NULL; - rnh->rnh_deladdr = NULL; - rnh->rnh_matchaddr = NULL; - rnh->rnh_lookup = NULL; - rnh->rnh_walktree = NULL; - - Free(rnh); -} - - -void -rn_fini() -{ - struct radix_mask *m; - - if (rn_zeros != NULL) { - FreeS(rn_zeros, 3 * max_keylen); - rn_zeros = NULL; - } - - if (mask_rnhead != NULL) { - rn_freehead(mask_rnhead); - mask_rnhead = NULL; - } - - while ((m = rn_mkfreelist) != NULL) { - rn_mkfreelist = m->rm_mklist; - KFREE(m); - } -} - - -#ifdef USE_MAIN - -typedef struct myst { - addrfamily_t dst; - addrfamily_t mask; - struct radix_node nodes[2]; -} myst_t; - -int -main(int argc, char *argv[]) -{ - struct radix_node_head *rnh; - struct radix_node *rn; - addrfamily_t af, mf; - myst_t st1, st2, *stp; - - memset(&st1, 0, sizeof(st1)); - memset(&st2, 0, sizeof(st2)); - memset(&af, 0, sizeof(af)); - - rn_init(); - - rnh = NULL; - rn_inithead(&rnh, offsetof(addrfamily_t, adf_addr) << 3); - - st1.dst.adf_len = sizeof(st1); - st1.mask.adf_len = sizeof(st1); - st1.dst.adf_addr.in4.s_addr = inet_addr("127.0.0.0"); - st1.mask.adf_addr.in4.s_addr = inet_addr("255.0.0.0"); - rn = rnh->rnh_addaddr(&st1.dst, &st1.mask, rnh, st1.nodes); - printf("add.1 %p\n", rn); - - st2.dst.adf_len = sizeof(st2); - st2.mask.adf_len = sizeof(st2); - st2.dst.adf_addr.in4.s_addr = inet_addr("127.0.1.0"); - st2.mask.adf_addr.in4.s_addr = inet_addr("255.255.255.0"); - rn = rnh->rnh_addaddr(&st2.dst, &st2.mask, rnh, st2.nodes); - printf("add.2 %p\n", rn); - - af.adf_len = sizeof(af); - af.adf_addr.in4.s_addr = inet_addr("127.0.1.0"); - rn = rnh->rnh_matchaddr(&af, rnh); - if (rn != NULL) { - printf("1.lookup = %p key %p mask %p\n", rn, rn->rn_key, rn->rn_mask); - stp = rn->rn_key; - printf("%s/", inet_ntoa(stp->dst.adf_addr.in4)); - stp = rn->rn_mask; - printf("%s\n", inet_ntoa(stp->dst.adf_addr.in4)); - } - - mf.adf_len = sizeof(mf); - mf.adf_addr.in4.s_addr = inet_addr("255.255.255.0"); - rn = rnh->rnh_lookup(&af, &mf, rnh); - if (rn != NULL) { - printf("2.lookup = %p key %p mask %p\n", rn, rn->rn_key, rn->rn_mask); - stp = rn->rn_key; - printf("%s/", inet_ntoa(stp->dst.adf_addr.in4)); - stp = rn->rn_mask; - printf("%s\n", inet_ntoa(stp->dst.adf_addr.in4)); - } - - af.adf_len = sizeof(af); - af.adf_addr.in4.s_addr = inet_addr("126.0.0.1"); - rn = rnh->rnh_matchaddr(&af, rnh); - if (rn != NULL) { - printf("3.lookup = %p key %p mask %p\n", rn, rn->rn_key, rn->rn_mask); - stp = rn->rn_key; - printf("%s/", inet_ntoa(stp->dst.adf_addr.in4)); - stp = rn->rn_mask; - printf("%s\n", inet_ntoa(stp->dst.adf_addr.in4)); - } - - return 0; -} - - -void -log(int level, char *format, ...) -{ - va_list ap; - - va_start(ap, format); - vfprintf(stderr, format, ap); - va_end(ap); -} -#endif - - -#ifndef _KERNEL -void -panic(char *str) -{ - fputs(str, stderr); - abort(); -} -#endif diff --git a/contrib/ipfilter/radix_ipf.c b/contrib/ipfilter/radix_ipf.c new file mode 100644 index 00000000000..f145c38a94d --- /dev/null +++ b/contrib/ipfilter/radix_ipf.c @@ -0,0 +1,1528 @@ +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + */ +#include +#include +#include +#include +#include +#include +#if !defined(_KERNEL) +# include +# include +# include +# include +#endif +#include "netinet/ip_compat.h" +#include "netinet/ip_fil.h" +#ifdef RDX_DEBUG +# include +# include +# include +#endif +#include "netinet/radix_ipf.h" + +#define ADF_OFF offsetof(addrfamily_t, adf_addr) +#define ADF_OFF_BITS (ADF_OFF << 3) + +static ipf_rdx_node_t *ipf_rx_insert __P((ipf_rdx_head_t *, + ipf_rdx_node_t nodes[2], int *)); +static void ipf_rx_attach_mask __P((ipf_rdx_node_t *, ipf_rdx_mask_t *)); +static int count_mask_bits __P((addrfamily_t *, u_32_t **)); +static void buildnodes __P((addrfamily_t *, addrfamily_t *, + ipf_rdx_node_t n[2])); +static ipf_rdx_node_t *ipf_rx_find_addr __P((ipf_rdx_node_t *, u_32_t *)); +static ipf_rdx_node_t *ipf_rx_lookup __P((ipf_rdx_head_t *, addrfamily_t *, + addrfamily_t *)); +static ipf_rdx_node_t *ipf_rx_match __P((ipf_rdx_head_t *, addrfamily_t *)); + +/* + * Foreword. + * --------- + * The code in this file has been written to target using the addrfamily_t + * data structure to house the address information and no other. Thus there + * are certain aspects of thise code (such as offsets to the address itself) + * that are hard coded here whilst they might be more variable elsewhere. + * Similarly, this code enforces no maximum key length as that's implied by + * all keys needing to be stored in addrfamily_t. + */ + +/* ------------------------------------------------------------------------ */ +/* Function: count_mask_bits */ +/* Returns: number of consecutive bits starting at "mask". */ +/* */ +/* Count the number of bits set in the address section of addrfamily_t and */ +/* return both that number and a pointer to the last word with a bit set if */ +/* lastp is not NULL. The bit count is performed using network byte order */ +/* as the guide for which bit is the most significant bit. */ +/* ------------------------------------------------------------------------ */ +static int +count_mask_bits(mask, lastp) + addrfamily_t *mask; + u_32_t **lastp; +{ + u_32_t *mp = (u_32_t *)&mask->adf_addr; + u_32_t m; + int count = 0; + int mlen; + + mlen = mask->adf_len - offsetof(addrfamily_t, adf_addr); + for (; mlen > 0; mlen -= 4, mp++) { + if ((m = ntohl(*mp)) == 0) + break; + if (lastp != NULL) + *lastp = mp; + for (; m & 0x80000000; m <<= 1) + count++; + } + + return count; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: buildnodes */ +/* Returns: Nil */ +/* Parameters: addr(I) - network address for this radix node */ +/* mask(I) - netmask associated with the above address */ +/* nodes(O) - pair of ipf_rdx_node_t's to initialise with data */ +/* associated with addr and mask. */ +/* */ +/* Initialise the fields in a pair of radix tree nodes according to the */ +/* data supplied in the paramters "addr" and "mask". It is expected that */ +/* "mask" will contain a consecutive string of bits set. Masks with gaps in */ +/* the middle are not handled by this implementation. */ +/* ------------------------------------------------------------------------ */ +static void +buildnodes(addr, mask, nodes) + addrfamily_t *addr, *mask; + ipf_rdx_node_t nodes[2]; +{ + u_32_t maskbits; + u_32_t lastbits; + u_32_t lastmask; + u_32_t *last; + int masklen; + + last = NULL; + maskbits = count_mask_bits(mask, &last); + if (last == NULL) { + masklen = 0; + lastmask = 0; + } else { + masklen = last - (u_32_t *)mask; + lastmask = *last; + } + lastbits = maskbits & 0x1f; + + bzero(&nodes[0], sizeof(ipf_rdx_node_t) * 2); + nodes[0].maskbitcount = maskbits; + nodes[0].index = -1 - (ADF_OFF_BITS + maskbits); + nodes[0].addrkey = (u_32_t *)addr; + nodes[0].maskkey = (u_32_t *)mask; + nodes[0].addroff = nodes[0].addrkey + masklen; + nodes[0].maskoff = nodes[0].maskkey + masklen; + nodes[0].parent = &nodes[1]; + nodes[0].offset = masklen; + nodes[0].lastmask = lastmask; + nodes[1].offset = masklen; + nodes[1].left = &nodes[0]; + nodes[1].maskbitcount = maskbits; +#ifdef RDX_DEBUG + (void) strcpy(nodes[0].name, "_BUILD.0"); + (void) strcpy(nodes[1].name, "_BUILD.1"); +#endif +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_rx_find_addr */ +/* Returns: ipf_rdx_node_t * - pointer to a node in the radix tree. */ +/* Parameters: tree(I) - pointer to first right node in tree to search */ +/* addr(I) - pointer to address to match */ +/* */ +/* Walk the radix tree given by "tree", looking for a leaf node that is a */ +/* match for the address given by "addr". */ +/* ------------------------------------------------------------------------ */ +static ipf_rdx_node_t * +ipf_rx_find_addr(tree, addr) + ipf_rdx_node_t *tree; + u_32_t *addr; +{ + ipf_rdx_node_t *cur; + + for (cur = tree; cur->index >= 0;) { + if (cur->bitmask & addr[cur->offset]) { + cur = cur->right; + } else { + cur = cur->left; + } + } + + return (cur); +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_rx_match */ +/* Returns: ipf_rdx_node_t * - NULL on error, else pointer to the node */ +/* added to the tree. */ +/* Paramters: head(I) - pointer to tree head to search */ +/* addr(I) - pointer to address to find */ +/* */ +/* Search the radix tree for the best match to the address pointed to by */ +/* "addr" and return a pointer to that node. This search will not match the */ +/* address information stored in either of the root leaves as neither of */ +/* them are considered to be part of the tree of data being stored. */ +/* ------------------------------------------------------------------------ */ +static ipf_rdx_node_t * +ipf_rx_match(head, addr) + ipf_rdx_head_t *head; + addrfamily_t *addr; +{ + ipf_rdx_mask_t *masknode; + ipf_rdx_node_t *prev; + ipf_rdx_node_t *node; + ipf_rdx_node_t *cur; + u_32_t *data; + u_32_t *mask; + u_32_t *key; + u_32_t *end; + int len; + int i; + + len = addr->adf_len; + end = (u_32_t *)((u_char *)addr + len); + node = ipf_rx_find_addr(head->root, (u_32_t *)addr); + + /* + * Search the dupkey list for a potential match. + */ + for (cur = node; (cur != NULL) && (cur->root == 0); cur = cur->dupkey) { + i = cur[0].addroff - cur[0].addrkey; + data = cur[0].addrkey + i; + mask = cur[0].maskkey + i; + key = (u_32_t *)addr + i; + for (; key < end; data++, key++, mask++) + if ((*key & *mask) != *data) + break; + if ((end == key) && (cur->root == 0)) + return (cur); /* Equal keys */ + } + prev = node->parent; + key = (u_32_t *)addr; + + for (node = prev; node->root == 0; node = node->parent) { + /* + * We know that the node hasn't matched so therefore only + * the entries in the mask list are searched, not the top + * node nor the dupkey list. + */ + masknode = node->masks; + for (; masknode != NULL; masknode = masknode->next) { + if (masknode->maskbitcount > node->maskbitcount) + continue; + cur = masknode->node; + for (i = ADF_OFF >> 2; i <= node->offset; i++) { + if ((key[i] & masknode->mask[i]) == + cur->addrkey[i]) + return (cur); + } + } + } + + return NULL; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_rx_lookup */ +/* Returns: ipf_rdx_node_t * - NULL on error, else pointer to the node */ +/* added to the tree. */ +/* Paramters: head(I) - pointer to tree head to search */ +/* addr(I) - address part of the key to match */ +/* mask(I) - netmask part of the key to match */ +/* */ +/* ipf_rx_lookup searches for an exact match on (addr,mask). The intention */ +/* is to see if a given key is in the tree, not to see if a route exists. */ +/* ------------------------------------------------------------------------ */ +ipf_rdx_node_t * +ipf_rx_lookup(head, addr, mask) + ipf_rdx_head_t *head; + addrfamily_t *addr, *mask; +{ + ipf_rdx_node_t *found; + ipf_rdx_node_t *node; + u_32_t *akey; + int count; + + found = ipf_rx_find_addr(head->root, (u_32_t *)addr); + if (found->root == 1) + return NULL; + + /* + * It is possible to find a matching address in the tree but for the + * netmask to not match. If the netmask does not match and there is + * no list of alternatives present at dupkey, return a failure. + */ + count = count_mask_bits(mask, NULL); + if (count != found->maskbitcount && found->dupkey == NULL) + return (NULL); + + akey = (u_32_t *)addr; + if ((found->addrkey[found->offset] & found->maskkey[found->offset]) != + akey[found->offset]) + return NULL; + + if (found->dupkey != NULL) { + node = found; + while (node != NULL && node->maskbitcount != count) + node = node->dupkey; + if (node == NULL) + return (NULL); + found = node; + } + return found; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_rx_attach_mask */ +/* Returns: Nil */ +/* Parameters: node(I) - pointer to a radix tree node */ +/* mask(I) - pointer to mask structure to add */ +/* */ +/* Add the netmask to the given node in an ordering where the most specific */ +/* netmask is at the top of the list. */ +/* ------------------------------------------------------------------------ */ +static void +ipf_rx_attach_mask(node, mask) + ipf_rdx_node_t *node; + ipf_rdx_mask_t *mask; +{ + ipf_rdx_mask_t **pm; + ipf_rdx_mask_t *m; + + for (pm = &node->masks; (m = *pm) != NULL; pm = &m->next) + if (m->maskbitcount < mask->maskbitcount) + break; + mask->next = *pm; + *pm = mask; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_rx_insert */ +/* Returns: ipf_rdx_node_t * - NULL on error, else pointer to the node */ +/* added to the tree. */ +/* Paramters: head(I) - pointer to tree head to add nodes to */ +/* nodes(I) - pointer to radix nodes to be added */ +/* dup(O) - set to 1 if node is a duplicate, else 0. */ +/* */ +/* Add the new radix tree entry that owns nodes[] to the tree given by head.*/ +/* If there is already a matching key in the table, "dup" will be set to 1 */ +/* and the existing node pointer returned if there is a complete key match. */ +/* A complete key match is a matching of all key data that is presented by */ +/* by the netmask. */ +/* ------------------------------------------------------------------------ */ +static ipf_rdx_node_t * +ipf_rx_insert(head, nodes, dup) + ipf_rdx_head_t *head; + ipf_rdx_node_t nodes[2]; + int *dup; +{ + ipf_rdx_mask_t **pmask; + ipf_rdx_node_t *node; + ipf_rdx_node_t *prev; + ipf_rdx_mask_t *mask; + ipf_rdx_node_t *cur; + u_32_t nodemask; + u_32_t *addr; + u_32_t *data; + int nodebits; + u_32_t *key; + u_32_t *end; + u_32_t bits; + int nodekey; + int nodeoff; + int nlen; + int len; + + addr = nodes[0].addrkey; + + node = ipf_rx_find_addr(head->root, addr); + len = ((addrfamily_t *)addr)->adf_len; + key = (u_32_t *)&((addrfamily_t *)addr)->adf_addr; + data= (u_32_t *)&((addrfamily_t *)node->addrkey)->adf_addr; + end = (u_32_t *)((u_char *)addr + len); + for (nlen = 0; key < end; data++, key++, nlen += 32) + if (*key != *data) + break; + if (end == data) { + *dup = 1; + return (node); /* Equal keys */ + } + *dup = 0; + + bits = (ntohl(*data) ^ ntohl(*key)); + for (; bits != 0; nlen++) { + if ((bits & 0x80000000) != 0) + break; + bits <<= 1; + } + nlen += ADF_OFF_BITS; + nodes[1].index = nlen; + nodes[1].bitmask = htonl(0x80000000 >> (nlen & 0x1f)); + nodes[0].offset = nlen / 32; + nodes[1].offset = nlen / 32; + + /* + * Walk through the tree and look for the correct place to attach + * this node. ipf_rx_fin_addr is not used here because the place + * to attach this node may be an internal node (same key, different + * netmask.) Additionally, the depth of the search is forcibly limited + * here to not exceed the netmask, so that a short netmask will be + * added higher up the tree even if there are lower branches. + */ + cur = head->root; + key = nodes[0].addrkey; + do { + prev = cur; + if (key[cur->offset] & cur->bitmask) { + cur = cur->right; + } else { + cur = cur->left; + } + } while (nlen > (unsigned)cur->index); + + if ((key[prev->offset] & prev->bitmask) == 0) { + prev->left = &nodes[1]; + } else { + prev->right = &nodes[1]; + } + cur->parent = &nodes[1]; + nodes[1].parent = prev; + if ((key[nodes[1].offset] & nodes[1].bitmask) == 0) { + nodes[1].right = cur; + } else { + nodes[1].right = &nodes[0]; + nodes[1].left = cur; + } + + nodeoff = nodes[0].offset; + nodekey = nodes[0].addrkey[nodeoff]; + nodemask = nodes[0].lastmask; + nodebits = nodes[0].maskbitcount; + prev = NULL; + /* + * Find the node up the tree with the largest pattern that still + * matches the node being inserted to see if this mask can be + * moved there. + */ + for (cur = nodes[1].parent; cur->root == 0; cur = cur->parent) { + if (cur->maskbitcount <= nodebits) + break; + if (((cur - 1)->addrkey[nodeoff] & nodemask) != nodekey) + break; + prev = cur; + } + + KMALLOC(mask, ipf_rdx_mask_t *); + if (mask == NULL) + return NULL; + bzero(mask, sizeof(*mask)); + mask->next = NULL; + mask->node = &nodes[0]; + mask->maskbitcount = nodebits; + mask->mask = nodes[0].maskkey; + nodes[0].mymask = mask; + + if (prev != NULL) { + ipf_rdx_mask_t *m; + + for (pmask = &prev->masks; (m = *pmask) != NULL; + pmask = &m->next) { + if (m->maskbitcount < nodebits) + break; + } + } else { + /* + * No higher up nodes qualify, so attach mask locally. + */ + pmask = &nodes[0].masks; + } + mask->next = *pmask; + *pmask = mask; + + /* + * Search the mask list on each child to see if there are any masks + * there that can be moved up to this newly inserted node. + */ + cur = nodes[1].right; + if (cur->root == 0) { + for (pmask = &cur->masks; (mask = *pmask) != NULL; ) { + if (mask->maskbitcount < nodebits) { + *pmask = mask->next; + ipf_rx_attach_mask(&nodes[0], mask); + } else { + pmask = &mask->next; + } + } + } + cur = nodes[1].left; + if (cur->root == 0 && cur != &nodes[0]) { + for (pmask = &cur->masks; (mask = *pmask) != NULL; ) { + if (mask->maskbitcount < nodebits) { + *pmask = mask->next; + ipf_rx_attach_mask(&nodes[0], mask); + } else { + pmask = &mask->next; + } + } + } + return (&nodes[0]); +} + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_rx_addroute */ +/* Returns: ipf_rdx_node_t * - NULL on error, else pointer to the node */ +/* added to the tree. */ +/* Paramters: head(I) - pointer to tree head to search */ +/* addr(I) - address portion of "route" to add */ +/* mask(I) - netmask portion of "route" to add */ +/* nodes(I) - radix tree data nodes inside allocate structure */ +/* */ +/* Attempt to add a node to the radix tree. The key for the node is the */ +/* (addr,mask). No memory allocation for the radix nodes themselves is */ +/* performed here, the data structure that this radix node is being used to */ +/* find is expected to house the node data itself however the call to */ +/* ipf_rx_insert() will attempt to allocate memory in order for netmask to */ +/* be promoted further up the tree. */ +/* In this case, the ip_pool_node_t structure from ip_pool.h contains both */ +/* the key material (addr,mask) and the radix tree nodes[]. */ +/* */ +/* The mechanics of inserting the node into the tree is handled by the */ +/* function ipf_rx_insert() above. Here, the code deals with the case */ +/* where the data to be inserted is a duplicate. */ +/* ------------------------------------------------------------------------ */ +ipf_rdx_node_t * +ipf_rx_addroute(head, addr, mask, nodes) + ipf_rdx_head_t *head; + addrfamily_t *addr, *mask; + ipf_rdx_node_t *nodes; +{ + ipf_rdx_node_t *node; + ipf_rdx_node_t *prev; + ipf_rdx_node_t *x; + int dup; + + buildnodes(addr, mask, nodes); + x = ipf_rx_insert(head, nodes, &dup); + if (x == NULL) + return NULL; + + if (dup == 1) { + node = &nodes[0]; + prev = NULL; + /* + * The duplicate list is kept sorted with the longest + * mask at the top, meaning that the most specific entry + * in the listis found first. This list thus allows for + * duplicates such as 128.128.0.0/32 and 128.128.0.0/16. + */ + while ((x != NULL) && (x->maskbitcount > node->maskbitcount)) { + prev = x; + x = x->dupkey; + } + + /* + * Is it a complete duplicate? If so, return NULL and + * fail the insert. Otherwise, insert it into the list + * of netmasks active for this key. + */ + if ((x != NULL) && (x->maskbitcount == node->maskbitcount)) + return (NULL); + + if (prev != NULL) { + nodes[0].dupkey = x; + prev->dupkey = &nodes[0]; + nodes[0].parent = prev; + if (x != NULL) + x->parent = &nodes[0]; + } else { + nodes[0].dupkey = x->dupkey; + prev = x->parent; + nodes[0].parent = prev; + x->parent = &nodes[0]; + if (prev->left == x) + prev->left = &nodes[0]; + else + prev->right = &nodes[0]; + } + } + + return &nodes[0]; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_rx_delete */ +/* Returns: ipf_rdx_node_t * - NULL on error, else node removed from */ +/* the tree. */ +/* Paramters: head(I) - pointer to tree head to search */ +/* addr(I) - pointer to the address part of the key */ +/* mask(I) - pointer to the netmask part of the key */ +/* */ +/* Search for an entry in the radix tree that is an exact match for (addr, */ +/* mask) and remove it if it exists. In the case where (addr,mask) is a not */ +/* a unique key, the tree structure itself is not changed - only the list */ +/* of duplicate keys. */ +/* ------------------------------------------------------------------------ */ +ipf_rdx_node_t * +ipf_rx_delete(head, addr, mask) + ipf_rdx_head_t *head; + addrfamily_t *addr, *mask; +{ + ipf_rdx_mask_t **pmask; + ipf_rdx_node_t *parent; + ipf_rdx_node_t *found; + ipf_rdx_node_t *prev; + ipf_rdx_node_t *node; + ipf_rdx_node_t *cur; + ipf_rdx_mask_t *m; + int count; + + found = ipf_rx_find_addr(head->root, (u_32_t *)addr); + if (found == NULL) + return NULL; + if (found->root == 1) + return NULL; + count = count_mask_bits(mask, NULL); + parent = found->parent; + if (found->dupkey != NULL) { + node = found; + while (node != NULL && node->maskbitcount != count) + node = node->dupkey; + if (node == NULL) + return (NULL); + if (node != found) { + /* + * Remove from the dupkey list. Here, "parent" is + * the previous node on the list (rather than tree) + * and "dupkey" is the next node on the list. + */ + parent = node->parent; + parent->dupkey = node->dupkey; + node->dupkey->parent = parent; + } else { + /* + * + * When removing the top node of the dupkey list, + * the pointers at the top of the list that point + * to other tree nodes need to be preserved and + * any children must have their parent updated. + */ + node = node->dupkey; + node->parent = found->parent; + node->right = found->right; + node->left = found->left; + found->right->parent = node; + found->left->parent = node; + if (parent->left == found) + parent->left = node; + else + parent->right= node; + } + } else { + if (count != found->maskbitcount) + return (NULL); + /* + * Remove the node from the tree and reconnect the subtree + * below. + */ + /* + * If there is a tree to the left, look for something to + * attach in place of "found". + */ + prev = found + 1; + cur = parent->parent; + if (parent != found + 1) { + if ((found + 1)->parent->right == found + 1) + (found + 1)->parent->right = parent; + else + (found + 1)->parent->left = parent; + if (cur->right == parent) { + if (parent->left == found) { + cur->right = parent->right; + } else if (parent->left != parent - 1) { + cur->right = parent->left; + } else { + cur->right = parent - 1; + } + cur->right->parent = cur; + } else { + if (parent->right == found) { + cur->left = parent->left; + } else if (parent->right != parent - 1) { + cur->left = parent->right; + } else { + cur->left = parent - 1; + } + cur->left->parent = cur; + } + parent->left = (found + 1)->left; + if ((found + 1)->right != parent) + parent->right = (found + 1)->right; + parent->left->parent = parent; + parent->right->parent = parent; + parent->parent = (found + 1)->parent; + + parent->bitmask = prev->bitmask; + parent->offset = prev->offset; + parent->index = prev->index; + } else { + /* + * We found an edge node. + */ + cur = parent->parent; + if (cur->left == parent) { + if (parent->left == found) { + cur->left = parent->right; + parent->right->parent = cur; + } else { + cur->left = parent->left; + parent->left->parent = cur; + } + } else { + if (parent->right != found) { + cur->right = parent->right; + parent->right->parent = cur; + } else { + cur->right = parent->left; + prev->left->parent = cur; + } + } + } + } + + /* + * Remove mask associated with this node. + */ + for (cur = parent; cur->root == 0; cur = cur->parent) { + ipf_rdx_mask_t **pm; + + if (cur->maskbitcount <= found->maskbitcount) + break; + if (((cur - 1)->addrkey[found->offset] & found->bitmask) != + found->addrkey[found->offset]) + break; + for (pm = &cur->masks; (m = *pm) != NULL; ) + if (m->node == cur) { + *pm = m->next; + break; + } else { + pm = &m->next; + } + } + KFREE(found->mymask); + + /* + * Masks that have been brought up to this node from below need to + * be sent back down. + */ + for (pmask = &parent->masks; (m = *pmask) != NULL; ) { + *pmask = m->next; + cur = m->node; + if (cur == found) + continue; + if (found->addrkey[cur->offset] & cur->lastmask) { + ipf_rx_attach_mask(parent->right, m); + } else if (parent->left != found) { + ipf_rx_attach_mask(parent->left, m); + } + } + + return (found); +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_rx_walktree */ +/* Returns: Nil */ +/* Paramters: head(I) - pointer to tree head to search */ +/* walker(I) - function to call for each node in the tree */ +/* arg(I) - parameter to pass to walker, in addition to the */ +/* node pointer */ +/* */ +/* A standard tree walking function except that it is iterative, rather */ +/* than recursive and tracks the next node in case the "walker" function */ +/* should happen to delete and free the current node. It thus goes without */ +/* saying that the "walker" function is not permitted to cause any change */ +/* in the validity of the data found at either the left or right child. */ +/* ------------------------------------------------------------------------ */ +void +ipf_rx_walktree(head, walker, arg) + ipf_rdx_head_t *head; + radix_walk_func_t walker; + void *arg; +{ + ipf_rdx_node_t *next; + ipf_rdx_node_t *node = head->root; + ipf_rdx_node_t *base; + + while (node->index >= 0) + node = node->left; + + for (;;) { + base = node; + while ((node->parent->right == node) && (node->root == 0)) + node = node->parent; + + for (node = node->parent->right; node->index >= 0; ) + node = node->left; + next = node; + + for (node = base; node != NULL; node = base) { + base = node->dupkey; + if (node->root == 0) + walker(node, arg); + } + node = next; + if (node->root) + return; + } +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_rx_inithead */ +/* Returns: int - 0 = success, else failure */ +/* Paramters: softr(I) - pointer to radix context */ +/* headp(O) - location for where to store allocated tree head */ +/* */ +/* This function allocates and initialises a radix tree head structure. */ +/* As a traditional radix tree, node 0 is used as the "0" sentinel and node */ +/* "2" is used as the all ones sentinel, leaving node "1" as the root from */ +/* which the tree is hung with node "0" on its left and node "2" to the */ +/* right. The context, "softr", is used here to provide a common source of */ +/* the zeroes and ones data rather than have one per head. */ +/* ------------------------------------------------------------------------ */ +int +ipf_rx_inithead(softr, headp) + radix_softc_t *softr; + ipf_rdx_head_t **headp; +{ + ipf_rdx_head_t *ptr; + ipf_rdx_node_t *node; + + KMALLOC(ptr, ipf_rdx_head_t *); + *headp = ptr; + if (ptr == NULL) + return -1; + bzero(ptr, sizeof(*ptr)); + node = ptr->nodes; + ptr->root = node + 1; + node[0].index = ADF_OFF_BITS; + node[0].index = -1 - node[0].index; + node[1].index = ADF_OFF_BITS; + node[2].index = node[0].index; + node[0].parent = node + 1; + node[1].parent = node + 1; + node[2].parent = node + 1; + node[1].bitmask = htonl(0x80000000); + node[0].root = 1; + node[1].root = 1; + node[2].root = 1; + node[0].offset = ADF_OFF_BITS >> 5; + node[1].offset = ADF_OFF_BITS >> 5; + node[2].offset = ADF_OFF_BITS >> 5; + node[1].left = &node[0]; + node[1].right = &node[2]; + node[0].addrkey = (u_32_t *)softr->zeros; + node[2].addrkey = (u_32_t *)softr->ones; +#ifdef RDX_DEBUG + (void) strcpy(node[0].name, "0_ROOT"); + (void) strcpy(node[1].name, "1_ROOT"); + (void) strcpy(node[2].name, "2_ROOT"); +#endif + + ptr->addaddr = ipf_rx_addroute; + ptr->deladdr = ipf_rx_delete; + ptr->lookup = ipf_rx_lookup; + ptr->matchaddr = ipf_rx_match; + ptr->walktree = ipf_rx_walktree; + return 0; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_rx_freehead */ +/* Returns: Nil */ +/* Paramters: head(I) - pointer to tree head to free */ +/* */ +/* This function simply free's up the radix tree head. Prior to calling */ +/* this function, it is expected that the tree will have been emptied. */ +/* ------------------------------------------------------------------------ */ +void +ipf_rx_freehead(head) + ipf_rdx_head_t *head; +{ + KFREE(head); +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_rx_create */ +/* Parameters: Nil */ +/* */ +/* ------------------------------------------------------------------------ */ +void * +ipf_rx_create() +{ + radix_softc_t *softr; + + KMALLOC(softr, radix_softc_t *); + if (softr == NULL) + return NULL; + bzero((char *)softr, sizeof(*softr)); + + KMALLOCS(softr->zeros, u_char *, 3 * sizeof(addrfamily_t)); + if (softr->zeros == NULL) { + KFREE(softr); + return (NULL); + } + softr->ones = softr->zeros + sizeof(addrfamily_t); + + return softr; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_rx_init */ +/* Returns: int - 0 = success (always) */ +/* */ +/* ------------------------------------------------------------------------ */ +int +ipf_rx_init(ctx) + void *ctx; +{ + radix_softc_t *softr = ctx; + + memset(softr->zeros, 0, 3 * sizeof(addrfamily_t)); + memset(softr->ones, 0xff, sizeof(addrfamily_t)); + + return (0); +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_rx_destroy */ +/* Returns: Nil */ +/* */ +/* ------------------------------------------------------------------------ */ +void +ipf_rx_destroy(ctx) + void *ctx; +{ + radix_softc_t *softr = ctx; + + if (softr->zeros != NULL) + KFREES(softr->zeros, 3 * sizeof(addrfamily_t)); + KFREE(softr); +} + +/* ====================================================================== */ + +#ifdef RDX_DEBUG +/* + * To compile this file as a standalone test unit, use -DRDX_DEBUG=1 + */ +#define NAME(x) ((x)->index < 0 ? (x)->name : (x)->name) +#define GNAME(y) ((y) == NULL ? "NULL" : NAME(y)) + +typedef struct myst { + struct ipf_rdx_node nodes[2]; + addrfamily_t dst; + addrfamily_t mask; + struct myst *next; + int printed; +} myst_t; + +typedef struct tabe_s { + char *host; + char *mask; + char *what; +} tabe_t; + +tabe_t builtin[] = { +#if 1 + { "192:168:100::0", "48", "d" }, + { "192:168:100::2", "128", "d" }, +#else + { "127.192.0.0", "255.255.255.0", "d" }, + { "127.128.0.0", "255.255.255.0", "d" }, + { "127.96.0.0", "255.255.255.0", "d" }, + { "127.80.0.0", "255.255.255.0", "d" }, + { "127.72.0.0", "255.255.255.0", "d" }, + { "127.64.0.0", "255.255.255.0", "d" }, + { "127.56.0.0", "255.255.255.0", "d" }, + { "127.48.0.0", "255.255.255.0", "d" }, + { "127.40.0.0", "255.255.255.0", "d" }, + { "127.32.0.0", "255.255.255.0", "d" }, + { "127.24.0.0", "255.255.255.0", "d" }, + { "127.16.0.0", "255.255.255.0", "d" }, + { "127.8.0.0", "255.255.255.0", "d" }, + { "124.0.0.0", "255.0.0.0", "d" }, + { "125.0.0.0", "255.0.0.0", "d" }, + { "126.0.0.0", "255.0.0.0", "d" }, + { "127.0.0.0", "255.0.0.0", "d" }, + { "10.0.0.0", "255.0.0.0", "d" }, + { "128.250.0.0", "255.255.0.0", "d" }, + { "192.168.0.0", "255.255.0.0", "d" }, + { "192.168.1.0", "255.255.255.0", "d" }, +#endif + { NULL, NULL, NULL } +}; + +char *mtable[][1] = { +#if 1 + { "192:168:100::2" }, + { "192:168:101::2" }, +#else + { "9.0.0.0" }, + { "9.0.0.1" }, + { "11.0.0.0" }, + { "11.0.0.1" }, + { "127.0.0.1" }, + { "127.0.1.0" }, + { "255.255.255.0" }, + { "126.0.0.1" }, + { "128.251.0.0" }, + { "128.251.0.1" }, + { "128.251.255.255" }, + { "129.250.0.0" }, + { "129.250.0.1" }, + { "192.168.255.255" }, +#endif + { NULL } +}; + + +int forder[22] = { + 14, 13, 12, 5, 10, 3, 19, 7, 4, 20, 8, + 2, 17, 9, 16, 11, 15, 1, 6, 18, 0, 21 +}; + +static int nodecount = 0; +myst_t *myst_top = NULL; +tabe_t *ttable = NULL; + +void add_addr(ipf_rdx_head_t *, int , int); +void checktree(ipf_rdx_head_t *); +void delete_addr(ipf_rdx_head_t *rnh, int item); +void dumptree(ipf_rdx_head_t *rnh); +void nodeprinter(ipf_rdx_node_t *, void *); +void printroots(ipf_rdx_head_t *); +void random_add(ipf_rdx_head_t *); +void random_delete(ipf_rdx_head_t *); +void test_addr(ipf_rdx_head_t *rnh, int pref, addrfamily_t *, int); + + +static void +ipf_rx_freenode(node, arg) + ipf_rdx_node_t *node; + void *arg; +{ + ipf_rdx_head_t *head = arg; + ipf_rdx_node_t *rv; + myst_t *stp; + + stp = (myst_t *)node; + rv = ipf_rx_delete(head, &stp->dst, &stp->mask); + if (rv != NULL) { + free(rv); + } +} + + +const char * +addrname(ap) + addrfamily_t *ap; +{ + static char name[80]; + const char *txt; + + bzero((char *)name, sizeof(name)); + txt = inet_ntop(ap->adf_family, &ap->adf_addr, name, + sizeof(name)); + return txt; +} + + +void +fill6bits(bits, msk) + int bits; + u_int *msk; +{ + if (bits == 0) { + msk[0] = 0; + msk[1] = 0; + msk[2] = 0; + msk[3] = 0; + return; + } + + msk[0] = 0xffffffff; + msk[1] = 0xffffffff; + msk[2] = 0xffffffff; + msk[3] = 0xffffffff; + + if (bits == 128) + return; + if (bits > 96) { + msk[3] = htonl(msk[3] << (128 - bits)); + } else if (bits > 64) { + msk[3] = 0; + msk[2] = htonl(msk[2] << (96 - bits)); + } else if (bits > 32) { + msk[3] = 0; + msk[2] = 0; + msk[1] = htonl(msk[1] << (64 - bits)); + } else { + msk[3] = 0; + msk[2] = 0; + msk[1] = 0; + msk[0] = htonl(msk[0] << (32 - bits)); + } +} + + +void +setaddr(afp, str) + addrfamily_t *afp; + char *str; +{ + + bzero((char *)afp, sizeof(*afp)); + + if (strchr(str, ':') == NULL) { + afp->adf_family = AF_INET; + afp->adf_len = offsetof(addrfamily_t, adf_addr) + 4; + } else { + afp->adf_family = AF_INET6; + afp->adf_len = offsetof(addrfamily_t, adf_addr) + 16; + } + inet_pton(afp->adf_family, str, &afp->adf_addr); +} + + +void +setmask(afp, str) + addrfamily_t *afp; + char *str; +{ + if (strchr(str, '.') != NULL) { + afp->adf_addr.in4.s_addr = inet_addr(str); + afp->adf_len = offsetof(addrfamily_t, adf_addr) + 4; + } else if (afp->adf_family == AF_INET) { + afp->adf_addr.i6[0] = htonl(0xffffffff << (32 - atoi(str))); + afp->adf_len = offsetof(addrfamily_t, adf_addr) + 4; + } else if (afp->adf_family == AF_INET6) { + fill6bits(atoi(str), afp->adf_addr.i6); + afp->adf_len = offsetof(addrfamily_t, adf_addr) + 16; + } +} + + +void +nodeprinter(node, arg) + ipf_rdx_node_t *node; + void *arg; +{ + myst_t *stp = (myst_t *)node; + + printf("Node %-9.9s L %-9.9s R %-9.9s P %9.9s/%-9.9s %s/%d\n", + node[0].name, + GNAME(node[1].left), GNAME(node[1].right), + GNAME(node[0].parent), GNAME(node[1].parent), + addrname(&stp->dst), node[0].maskbitcount); + if (stp->printed == -1) + printf("!!! %d\n", stp->printed); + else + stp->printed = 1; +} + + +void +printnode(stp) + myst_t *stp; +{ + ipf_rdx_node_t *node = &stp->nodes[0]; + + if (stp->nodes[0].index > 0) + stp = (myst_t *)&stp->nodes[-1]; + + printf("Node %-9.9s ", node[0].name); + printf("L %-9.9s ", GNAME(node[1].left)); + printf("R %-9.9s ", GNAME(node[1].right)); + printf("P %9.9s", GNAME(node[0].parent)); + printf("/%-9.9s ", GNAME(node[1].parent)); + printf("%s P%d\n", addrname(&stp->dst), stp->printed); +} + + +void +buildtab(void) +{ + char line[80], *s; + tabe_t *tab; + int lines; + FILE *fp; + + lines = 0; + fp = fopen("hosts", "r"); + + while (fgets(line, sizeof(line), fp) != NULL) { + s = strchr(line, '\n'); + if (s != NULL) + *s = '\0'; + lines++; + if (lines == 1) + tab = malloc(sizeof(*tab) * 2); + else + tab = realloc(tab, (lines + 1) * sizeof(*tab)); + tab[lines - 1].host = strdup(line); + s = strchr(tab[lines - 1].host, '/'); + *s++ = '\0'; + tab[lines - 1].mask = s; + tab[lines - 1].what = "d"; + } + fclose(fp); + + tab[lines].host = NULL; + tab[lines].mask = NULL; + tab[lines].what = NULL; + ttable = tab; +} + + +void +printroots(rnh) + ipf_rdx_head_t *rnh; +{ + printf("Root.0.%s b %3d p %-9.9s l %-9.9s r %-9.9s\n", + GNAME(&rnh->nodes[0]), + rnh->nodes[0].index, GNAME(rnh->nodes[0].parent), + GNAME(rnh->nodes[0].left), GNAME(rnh->nodes[0].right)); + printf("Root.1.%s b %3d p %-9.9s l %-9.9s r %-9.9s\n", + GNAME(&rnh->nodes[1]), + rnh->nodes[1].index, GNAME(rnh->nodes[1].parent), + GNAME(rnh->nodes[1].left), GNAME(rnh->nodes[1].right)); + printf("Root.2.%s b %3d p %-9.9s l %-9.9s r %-9.9s\n", + GNAME(&rnh->nodes[2]), + rnh->nodes[2].index, GNAME(rnh->nodes[2].parent), + GNAME(rnh->nodes[2].left), GNAME(rnh->nodes[2].right)); +} + + +int +main(int argc, char *argv[]) +{ + addrfamily_t af; + ipf_rdx_head_t *rnh; + radix_softc_t *ctx; + int j; + int i; + + rnh = NULL; + + buildtab(); + ctx = ipf_rx_create(); + ipf_rx_init(ctx); + ipf_rx_inithead(ctx, &rnh); + + printf("=== ADD-0 ===\n"); + for (i = 0; ttable[i].host != NULL; i++) { + add_addr(rnh, i, i); + checktree(rnh); + } + printroots(rnh); + ipf_rx_walktree(rnh, nodeprinter, NULL); + printf("=== DELETE-0 ===\n"); + for (i = 0; ttable[i].host != NULL; i++) { + delete_addr(rnh, i); + printroots(rnh); + ipf_rx_walktree(rnh, nodeprinter, NULL); + } + printf("=== ADD-1 ===\n"); + for (i = 0; ttable[i].host != NULL; i++) { + setaddr(&af, ttable[i].host); + add_addr(rnh, i, i); /*forder[i]); */ + checktree(rnh); + } + dumptree(rnh); + ipf_rx_walktree(rnh, nodeprinter, NULL); + printf("=== TEST-1 ===\n"); + for (i = 0; ttable[i].host != NULL; i++) { + setaddr(&af, ttable[i].host); + test_addr(rnh, i, &af, -1); + } + + printf("=== TEST-2 ===\n"); + for (i = 0; mtable[i][0] != NULL; i++) { + setaddr(&af, mtable[i][0]); + test_addr(rnh, i, &af, -1); + } + printf("=== DELETE-1 ===\n"); + for (i = 0; ttable[i].host != NULL; i++) { + if (ttable[i].what[0] != 'd') + continue; + delete_addr(rnh, i); + for (j = 0; ttable[j].host != NULL; j++) { + setaddr(&af, ttable[j].host); + test_addr(rnh, i, &af, 3); + } + printroots(rnh); + ipf_rx_walktree(rnh, nodeprinter, NULL); + } + + dumptree(rnh); + + printf("=== ADD-2 ===\n"); + random_add(rnh); + checktree(rnh); + dumptree(rnh); + ipf_rx_walktree(rnh, nodeprinter, NULL); + printf("=== DELETE-2 ===\n"); + random_delete(rnh); + checktree(rnh); + dumptree(rnh); + + ipf_rx_walktree(rnh, ipf_rx_freenode, rnh); + + return 0; +} + + +void +dumptree(rnh) + ipf_rdx_head_t *rnh; +{ + myst_t *stp; + + printf("VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV\n"); + printroots(rnh); + for (stp = myst_top; stp; stp = stp->next) + printnode(stp); + printf("^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n"); +} + + +void +test_addr(rnh, pref, addr, limit) + ipf_rdx_head_t *rnh; + int pref; + addrfamily_t *addr; +{ + static int extras[14] = { 0, -1, 1, 3, 5, 8, 9, + 15, 16, 19, 255, 256, 65535, 65536 + }; + ipf_rdx_node_t *rn; + addrfamily_t af; + char name[80]; + myst_t *stp; + int i; + + memset(&af, 0, sizeof(af)); +#if 0 + if (limit < 0 || limit > 14) + limit = 14; + + for (i = 0; i < limit; i++) { + if (ttable[i].host == NULL) + break; + setaddr(&af, ttable[i].host); + printf("%d.%d.LOOKUP(%s)", pref, i, addrname(&af)); + rn = ipf_rx_match(rnh, &af); + stp = (myst_t *)rn; + printf(" = %s (%s/%d)\n", GNAME(rn), + rn ? addrname(&stp->dst) : "NULL", + rn ? rn->maskbitcount : 0); + } +#else + printf("%d.%d.LOOKUP(%s)", pref, -1, addrname(addr)); + rn = ipf_rx_match(rnh, addr); + stp = (myst_t *)rn; + printf(" = %s (%s/%d)\n", GNAME(rn), + rn ? addrname(&stp->dst) : "NULL", rn ? rn->maskbitcount : 0); +#endif +} + + +void +delete_addr(rnh, item) + ipf_rdx_head_t *rnh; + int item; +{ + ipf_rdx_node_t *rn; + addrfamily_t mask; + addrfamily_t af; + myst_t **pstp; + myst_t *stp; + + memset(&af, 0, sizeof(af)); + memset(&mask, 0, sizeof(mask)); + setaddr(&af, ttable[item].host); + mask.adf_family = af.adf_family; + setmask(&mask, ttable[item].mask); + + printf("DELETE(%s)\n", addrname(&af)); + rn = ipf_rx_delete(rnh, &af, &mask); + if (rn == NULL) { + printf("FAIL LOOKUP DELETE\n"); + checktree(rnh); + for (stp = myst_top; stp != NULL; stp = stp->next) + if (stp->printed != -1) + stp->printed = -2; + ipf_rx_walktree(rnh, nodeprinter, NULL); + dumptree(rnh); + abort(); + } + printf("%d.delete(%s) = %s\n", item, addrname(&af), GNAME(rn)); + + for (pstp = &myst_top; (stp = *pstp) != NULL; pstp = &stp->next) + if (stp == (myst_t *)rn) + break; + stp->printed = -1; + stp->nodes[0].parent = &stp->nodes[0]; + stp->nodes[1].parent = &stp->nodes[1]; + *pstp = stp->next; + free(stp); + nodecount--; + checktree(rnh); +} + + +void +add_addr(rnh, n, item) + ipf_rdx_head_t *rnh; + int n, item; +{ + ipf_rdx_node_t *rn; + myst_t *stp; + + stp = calloc(1, sizeof(*stp)); + rn = (ipf_rdx_node_t *)stp; + setaddr(&stp->dst, ttable[item].host); + stp->mask.adf_family = stp->dst.adf_family; + setmask(&stp->mask, ttable[item].mask); + stp->next = myst_top; + myst_top = stp; + (void) sprintf(rn[0].name, "_BORN.0"); + (void) sprintf(rn[1].name, "_BORN.1"); + rn = ipf_rx_addroute(rnh, &stp->dst, &stp->mask, stp->nodes); + (void) sprintf(rn[0].name, "%d_NODE.0", item); + (void) sprintf(rn[1].name, "%d_NODE.1", item); + printf("ADD %d/%d %s/%s\n", n, item, rn[0].name, rn[1].name); + nodecount++; + checktree(rnh); +} + + +void +checktree(ipf_rdx_head_t *head) +{ + myst_t *s1; + ipf_rdx_node_t *rn; + + if (nodecount <= 1) + return; + + for (s1 = myst_top; s1 != NULL; s1 = s1->next) { + int fault = 0; + if (s1->printed == -1) + continue; + rn = &s1->nodes[1]; + if (rn->right->parent != rn) + fault |= 1; + if (rn->left->parent != rn) + fault |= 2; + if (rn->parent->left != rn && rn->parent->right != rn) + fault |= 4; + if (fault != 0) { + printf("FAULT %#x %s\n", fault, rn->name); + dumptree(head); + ipf_rx_walktree(head, nodeprinter, NULL); + fflush(stdout); + fflush(stderr); + printf("--\n"); + abort(); + } + } +} + + +int * +randomize(int *pnitems) +{ + int *order; + int nitems; + int choice; + int j; + int i; + + nitems = sizeof(ttable) / sizeof(ttable[0]); + *pnitems = nitems; + order = calloc(nitems, sizeof(*order)); + srandom(getpid() * time(NULL)); + memset(order, 0xff, nitems * sizeof(*order)); + order[21] = 21; + for (i = 0; i < nitems - 1; i++) { + do { + choice = rand() % (nitems - 1); + for (j = 0; j < nitems; j++) + if (order[j] == choice) + break; + } while (j != nitems); + order[i] = choice; + } + + return order; +} + + +void +random_add(rnh) + ipf_rdx_head_t *rnh; +{ + int *order; + int nitems; + int i; + + order = randomize(&nitems); + + for (i = 0; i < nitems - 1; i++) { + add_addr(rnh, i, order[i]); + checktree(rnh); + } +} + + +void +random_delete(rnh) + ipf_rdx_head_t *rnh; +{ + int *order; + int nitems; + int i; + + order = randomize(&nitems); + + for (i = 0; i < nitems - 1; i++) { + delete_addr(rnh, i); + checktree(rnh); + } +} +#endif /* RDX_DEBUG */ diff --git a/contrib/ipfilter/radix_ipf.h b/contrib/ipfilter/radix_ipf.h index 11e4ba761aa..bbbf5592b99 100644 --- a/contrib/ipfilter/radix_ipf.h +++ b/contrib/ipfilter/radix_ipf.h @@ -1,214 +1,97 @@ /* $FreeBSD$ */ /* - * Copyright (c) 1988, 1989, 1993 - * The Regents of the University of California. All rights reserved. + * Copyright (C) 2012 by Darren Reed. * - * 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. - * - * 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. - * - * @(#)radix.h 8.2 (Berkeley) 10/31/94 + * See the IPFILTER.LICENCE file for details on licencing. */ +#ifndef __RADIX_IPF_H__ +#define __RADIX_IPF_H__ -#if !defined(_NET_RADIX_H_) && !defined(_RADIX_H_) -#define _NET_RADIX_H_ -#ifndef _RADIX_H_ -#define _RADIX_H_ -#endif /* _RADIX_H_ */ - -#ifndef __P -# ifdef __STDC__ -# define __P(x) x -# else -# define __P(x) () -# endif +#ifndef U_32_T +typedef unsigned int u_32_t; +# define U_32_T 1 #endif -#if defined(__sgi) || defined(__osf__) || defined(sun) -# define radix_mask ipf_radix_mask -# define radix_node ipf_radix_node -# define radix_node_head ipf_radix_node_head +typedef struct ipf_rdx_mask { + struct ipf_rdx_mask *next; + struct ipf_rdx_node *node; + u_32_t *mask; + int maskbitcount; +} ipf_rdx_mask_t; + +typedef struct ipf_rdx_node { + struct ipf_rdx_node *left; + struct ipf_rdx_node *right; + struct ipf_rdx_node *parent; + struct ipf_rdx_node *dupkey; + struct ipf_rdx_mask *masks; + struct ipf_rdx_mask *mymask; + u_32_t *addrkey; + u_32_t *maskkey; + u_32_t *addroff; + u_32_t *maskoff; + u_32_t lastmask; + u_32_t bitmask; + int offset; + int index; + int maskbitcount; + int root; +#ifdef RDX_DEBUG + char name[40]; #endif +} ipf_rdx_node_t; -/* - * Radix search tree node layout. - */ +struct ipf_rdx_head; -struct radix_node { - struct radix_mask *rn_mklist; /* list of masks contained in subtree */ - struct radix_node *rn_p; /* parent */ - short rn_b; /* bit offset; -1-index(netmask) */ - char rn_bmask; /* node: mask for bit test*/ - u_char rn_flags; /* enumerated next */ -#define RNF_NORMAL 1 /* leaf contains normal route */ -#define RNF_ROOT 2 /* leaf is root leaf for tree */ -#define RNF_ACTIVE 4 /* This node is alive (for rtfree) */ - union { - struct { /* leaf only data: */ - caddr_t rn_Key; /* object of search */ - caddr_t rn_Mask; /* netmask, if present */ - struct radix_node *rn_Dupedkey; - } rn_leaf; - struct { /* node only data: */ - int rn_Off; /* where to start compare */ - struct radix_node *rn_L;/* progeny */ - struct radix_node *rn_R;/* progeny */ - } rn_node; - } rn_u; -#ifdef RN_DEBUG - int rn_info; - struct radix_node *rn_twin; - struct radix_node *rn_ybro; -#endif -}; +typedef void (* radix_walk_func_t)(ipf_rdx_node_t *, void *); +typedef ipf_rdx_node_t *(* idx_hamn_func_t)(struct ipf_rdx_head *, + addrfamily_t *, addrfamily_t *, + ipf_rdx_node_t *); +typedef ipf_rdx_node_t *(* idx_ham_func_t)(struct ipf_rdx_head *, + addrfamily_t *, addrfamily_t *); +typedef ipf_rdx_node_t *(* idx_ha_func_t)(struct ipf_rdx_head *, + addrfamily_t *); +typedef void (* idx_walk_func_t)(struct ipf_rdx_head *, + radix_walk_func_t, void *); -#define rn_dupedkey rn_u.rn_leaf.rn_Dupedkey -#define rn_key rn_u.rn_leaf.rn_Key -#define rn_mask rn_u.rn_leaf.rn_Mask -#define rn_off rn_u.rn_node.rn_Off -#define rn_l rn_u.rn_node.rn_L -#define rn_r rn_u.rn_node.rn_R +typedef struct ipf_rdx_head { + ipf_rdx_node_t *root; + ipf_rdx_node_t nodes[3]; + ipfmutex_t lock; + idx_hamn_func_t addaddr; /* add addr/mask to tree */ + idx_ham_func_t deladdr; /* delete addr/mask from tree */ + idx_ham_func_t lookup; /* look for specific addr/mask */ + idx_ha_func_t matchaddr; /* search tree for address match */ + idx_walk_func_t walktree; /* walk entire tree */ +} ipf_rdx_head_t; -/* - * Annotations to tree concerning potential routes applying to subtrees. - */ +typedef struct radix_softc { + u_char *zeros; + u_char *ones; +} radix_softc_t; -struct radix_mask { - short rm_b; /* bit offset; -1-index(netmask) */ - char rm_unused; /* cf. rn_bmask */ - u_char rm_flags; /* cf. rn_flags */ - struct radix_mask *rm_mklist; /* more masks to try */ - union { - caddr_t rmu_mask; /* the mask */ - struct radix_node *rmu_leaf; /* for normal routes */ - } rm_rmu; - int rm_refs; /* # of references to this struct */ -}; - -#define rm_mask rm_rmu.rmu_mask -#define rm_leaf rm_rmu.rmu_leaf /* extra field would make 32 bytes */ - -#define MKGet(m) {\ - if (rn_mkfreelist) {\ - m = rn_mkfreelist; \ - rn_mkfreelist = (m)->rm_mklist; \ - } else \ - R_Malloc(m, struct radix_mask *, sizeof (*(m))); }\ - -#define MKFree(m) { (m)->rm_mklist = rn_mkfreelist; rn_mkfreelist = (m);} - -struct radix_node_head { - struct radix_node *rnh_treetop; - struct radix_node *rnh_leaflist; - u_long rnh_hits; - u_int rnh_number; - u_int rnh_ref; - int rnh_addrsize; /* permit, but not require fixed keys */ - int rnh_pktsize; /* permit, but not require fixed keys */ - struct radix_node *(*rnh_addaddr) /* add based on sockaddr */ - __P((void *v, void *mask, - struct radix_node_head *head, struct radix_node nodes[])); - struct radix_node *(*rnh_addpkt) /* add based on packet hdr */ - __P((void *v, void *mask, - struct radix_node_head *head, struct radix_node nodes[])); - struct radix_node *(*rnh_deladdr) /* remove based on sockaddr */ - __P((void *v, void *mask, struct radix_node_head *head)); - struct radix_node *(*rnh_delpkt) /* remove based on packet hdr */ - __P((void *v, void *mask, struct radix_node_head *head)); - struct radix_node *(*rnh_matchaddr) /* locate based on sockaddr */ - __P((void *v, struct radix_node_head *head)); - struct radix_node *(*rnh_lookup) /* locate based on sockaddr */ - __P((void *v, void *mask, struct radix_node_head *head)); - struct radix_node *(*rnh_matchpkt) /* locate based on packet hdr */ - __P((void *v, struct radix_node_head *head)); - int (*rnh_walktree) /* traverse tree */ - __P((struct radix_node_head *, - int (*)(struct radix_node *, void *), void *)); - struct radix_node rnh_nodes[3]; /* empty tree for common case */ -}; - - -#if defined(AIX) -# undef Bcmp -# undef Bzero -# undef R_Malloc -# undef Free -#endif -#define Bcmp(a, b, n) bcmp(((caddr_t)(a)), ((caddr_t)(b)), (unsigned)(n)) -#if defined(linux) && defined(_KERNEL) -# define Bcopy(a, b, n) memmove(((caddr_t)(b)), ((caddr_t)(a)), (unsigned)(n)) +#undef RADIX_NODE_HEAD_LOCK +#undef RADIX_NODE_HEAD_UNLOCK +#ifdef _KERNEL +# define RADIX_NODE_HEAD_LOCK(x) MUTEX_ENTER(&(x)->lock) +# define RADIX_NODE_HEAD_UNLOCK(x) MUTEX_UNLOCK(&(x)->lock) #else -# define Bcopy(a, b, n) bcopy(((caddr_t)(a)), ((caddr_t)(b)), (unsigned)(n)) -#endif -#define Bzero(p, n) bzero((caddr_t)(p), (unsigned)(n)); -#define R_Malloc(p, t, n) KMALLOCS(p, t, n) -#define FreeS(p, z) KFREES(p, z) -#define Free(p) KFREE(p) - -#if (defined(__osf__) || defined(AIX) || (IRIX >= 60516) || defined(sun)) && defined(_KERNEL) -# define rn_init ipf_rn_init -# define rn_fini ipf_rn_fini -# define rn_inithead ipf_rn_inithead -# define rn_freehead ipf_rn_freehead -# define rn_inithead0 ipf_rn_inithead0 -# define rn_refines ipf_rn_refines -# define rn_walktree ipf_rn_walktree -# define rn_addmask ipf_rn_addmask -# define rn_addroute ipf_rn_addroute -# define rn_delete ipf_rn_delete -# define rn_insert ipf_rn_insert -# define rn_lookup ipf_rn_lookup -# define rn_match ipf_rn_match -# define rn_newpair ipf_rn_newpair -# define rn_search ipf_rn_search -# define rn_search_m ipf_rn_search_m -# define max_keylen ipf_maxkeylen -# define rn_mkfreelist ipf_rn_mkfreelist -# define rn_zeros ipf_rn_zeros -# define rn_ones ipf_rn_ones -# define rn_satisfies_leaf ipf_rn_satisfies_leaf -# define rn_lexobetter ipf_rn_lexobetter -# define rn_new_radix_mask ipf_rn_new_radix_mask -# define rn_freenode ipf_rn_freenode +# define RADIX_NODE_HEAD_LOCK(x) +# define RADIX_NODE_HEAD_UNLOCK(x) #endif -void rn_init __P((void)); -void rn_fini __P((void)); -int rn_inithead __P((void **, int)); -void rn_freehead __P((struct radix_node_head *)); -int rn_inithead0 __P((struct radix_node_head *, int)); -int rn_refines __P((void *, void *)); -int rn_walktree __P((struct radix_node_head *, - int (*)(struct radix_node *, void *), void *)); -struct radix_node - *rn_addmask __P((void *, int, int)), - *rn_addroute __P((void *, void *, struct radix_node_head *, - struct radix_node [2])), - *rn_delete __P((void *, void *, struct radix_node_head *)), - *rn_insert __P((void *, struct radix_node_head *, int *, - struct radix_node [2])), - *rn_lookup __P((void *, void *, struct radix_node_head *)), - *rn_match __P((void *, struct radix_node_head *)), - *rn_newpair __P((void *, int, struct radix_node[2])), - *rn_search __P((void *, struct radix_node *)), - *rn_search_m __P((void *, struct radix_node *, void *)); +extern void *ipf_rx_create __P((void)); +extern int ipf_rx_init __P((void *)); +extern void ipf_rx_destroy __P((void *)); +extern int ipf_rx_inithead __P((radix_softc_t *, ipf_rdx_head_t **)); +extern void ipf_rx_freehead __P((ipf_rdx_head_t *)); +extern ipf_rdx_node_t *ipf_rx_addroute __P((ipf_rdx_head_t *, + addrfamily_t *, addrfamily_t *, + ipf_rdx_node_t *)); +extern ipf_rdx_node_t *ipf_rx_delete __P((ipf_rdx_head_t *, addrfamily_t *, + addrfamily_t *)); +extern void ipf_rx_walktree __P((ipf_rdx_head_t *, radix_walk_func_t, + void *)); -#endif /* _NET_RADIX_H_ */ +#endif /* __RADIX_IPF_H__ */ diff --git a/contrib/ipfilter/rules/.cvsignore b/contrib/ipfilter/rules/.cvsignore deleted file mode 100644 index 3e757656cf3..00000000000 --- a/contrib/ipfilter/rules/.cvsignore +++ /dev/null @@ -1 +0,0 @@ -new diff --git a/contrib/ipfilter/rules/BASIC_1.FW b/contrib/ipfilter/rules/BASIC_1.FW index d2bd60a3188..642dde061ef 100644 --- a/contrib/ipfilter/rules/BASIC_1.FW +++ b/contrib/ipfilter/rules/BASIC_1.FW @@ -22,10 +22,10 @@ block in log quick all with short # (especially for ed0) and needs to be further refined. # block in log on ppp0 all head 100 -block in log proto tcp all flags S/SA head 101 group 100 +block in log proto tcp all flags S/SA head 101 group 100 block out log on ppp0 all head 150 block in log on ed0 from w.x.y.z/24 to any head 200 -block in log proto tcp all flags S/SA head 201 group 200 +block in log proto tcp all flags S/SA head 201 group 200 block in log proto udp all head 202 group 200 block out log on ed0 all head 250 #------------------------------------------------------- diff --git a/contrib/ipfilter/rules/BASIC_2.FW b/contrib/ipfilter/rules/BASIC_2.FW index 46564f0ee41..1d4fd731767 100644 --- a/contrib/ipfilter/rules/BASIC_2.FW +++ b/contrib/ipfilter/rules/BASIC_2.FW @@ -56,7 +56,7 @@ pass out quick on lo0 all # # Allow all outgoing connections (SSH, TELNET, FTP, WWW, gopher, etc) # -pass in log quick proto tcp all flags S/SA keep state group 200 +pass in log quick proto tcp all flags S/SA keep state group 200 # # Support all UDP `connections' initiated from inside. # diff --git a/contrib/ipfilter/rules/firewall b/contrib/ipfilter/rules/firewall index 681a81da20b..f26b7150b98 100644 --- a/contrib/ipfilter/rules/firewall +++ b/contrib/ipfilter/rules/firewall @@ -31,7 +31,7 @@ where closest to your internal network in terms of network hops. * "int-net" is the internal network IP# subnet address range. This might - be something like 10.1.0.0/16, or 128.33.1.0/24 + be something like 10.1.0.0/16, or 128.33.1.0/24 * "ext-service" is the service to which you wish to connect or if it doesn't have a proper name, a number can be used. The translation of "ext-service" diff --git a/contrib/ipfilter/rules/ipmon.conf b/contrib/ipfilter/rules/ipmon.conf index 47b01463575..652afceb3ea 100644 --- a/contrib/ipfilter/rules/ipmon.conf +++ b/contrib/ipfilter/rules/ipmon.conf @@ -2,23 +2,24 @@ # # # -match { logtag = 10000 } - do { execute "/usr/bin/mail -s 'logtag 10000' root" }; -match { logtag = 2000, every 10 seconds } - do { execute "echo 'XXXXXXXX tag 2000 packet XXXXXXXX'" }; +match { logtag = 10000; } +do { execute("/usr/bin/mail -s 'logtag 10000' root"); }; # -match { protocol = udp, result = block } - do { execute "/usr/bin/mail -s 'blocked udp' root" -}; +match { logtag = 2000, every 10 seconds; } +do { execute("echo 'XXXXXXXX tag 2000 packet XXXXXXXX'"); }; # -match { - srcip = 10.1.0.0/16, dstip = 192.168.1.0/24 } - do { execute "/usr/bin/mail -s 'from 10.1 to 192.168.1' root" -}; +match { protocol = udp, result = block; } +do { file("file:///var/log/udp-block"); }; +# +match { protocol = tcp, result = block, dstport = 25; } +do { syslog("local0.info"), syslog("local1."), syslog(".warn"); }; +# +match { srcip = 10.1.0.0/16, dstip = 192.168.1.0/24; } +do { execute("/usr/bin/mail -s 'from 10.1 to 192.168.1' root"); }; + # match { rule = 12, logtag = 101, direction = in, result = block, - protocol = udp, srcip = 10.1.0.0/16, dstip = 192.168.1.0/24 } - do { execute "run shell command" -}; + protocol = udp, srcip = 10.1.0.0/16, dstip = 192.168.1.0/24; } +do { nothing; }; # diff --git a/contrib/ipfilter/rules/server b/contrib/ipfilter/rules/server index f2fb2041faf..de0e9bbd06d 100644 --- a/contrib/ipfilter/rules/server +++ b/contrib/ipfilter/rules/server @@ -3,7 +3,7 @@ # 128.1.2.1 (le1), we want to block all IP spoofing attacks. le1 is # connected to the majority of the network, whilst le0 is connected to a # leaf subnet. We're not concerned about filtering individual services -# or +# or # pass in quick on le0 from 128.1.40.0/24 to any block in log quick on le0 from any to any diff --git a/contrib/ipfilter/samples/.cvsignore b/contrib/ipfilter/samples/.cvsignore deleted file mode 100644 index 4d38251c2f4..00000000000 --- a/contrib/ipfilter/samples/.cvsignore +++ /dev/null @@ -1,4 +0,0 @@ -userauth -proxy -relay -trans_relay diff --git a/contrib/ipfilter/samples/proxy.c b/contrib/ipfilter/samples/proxy.c index 471cc736506..483c4b5d768 100644 --- a/contrib/ipfilter/samples/proxy.c +++ b/contrib/ipfilter/samples/proxy.c @@ -51,8 +51,8 @@ main(argc, argv) -int argc; -char *argv[]; + int argc; + char *argv[]; { struct sockaddr_in sin, sloc, sout; ipfobj_t obj; @@ -132,9 +132,9 @@ char *argv[]; #ifdef DO_NAT_OUT do_nat_out(in, out, fd, nlp, extif) -int fd; -natlookup_t *nlp; -char *extif; + int fd; + natlookup_t *nlp; + char *extif; { nat_save_t ns, *nsp = &ns; struct sockaddr_in usin; @@ -228,7 +228,7 @@ fflush(stdout); relay(in, out, net) -int in, out, net; + int in, out, net; { char netbuf[1024], outbuf[1024]; char *nwptr, *nrptr, *owptr, *orptr; diff --git a/contrib/ipfilter/samples/relay.c b/contrib/ipfilter/samples/relay.c index ac5c60243b8..11b76b07c50 100644 --- a/contrib/ipfilter/samples/relay.c +++ b/contrib/ipfilter/samples/relay.c @@ -29,7 +29,7 @@ char ibuff[RELAY_BUFSZ]; char obuff[RELAY_BUFSZ]; int relay(ifd, ofd, rfd) -int ifd, ofd, rfd; + int ifd, ofd, rfd; { fd_set rfds, wfds; char *irh, *irt, *rrh, *rrt; @@ -103,8 +103,8 @@ int ifd, ofd, rfd; } main(argc, argv) -int argc; -char *argv[]; + int argc; + char *argv[]; { struct sockaddr_in sin; ipfobj_t obj; diff --git a/contrib/ipfilter/snoop.h b/contrib/ipfilter/snoop.h index 3cb54b99cc3..74bc2470aef 100644 --- a/contrib/ipfilter/snoop.h +++ b/contrib/ipfilter/snoop.h @@ -1,7 +1,7 @@ /* $FreeBSD$ */ /* - * Copyright (C) 1993-2001 by Darren Reed. + * Copyright (C) 2012 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. */ @@ -11,7 +11,7 @@ /* * written to comply with the RFC (1761) from Sun. - * $Id: snoop.h,v 2.3 2001/06/09 17:09:23 darrenr Exp $ + * $Id$ */ struct snoophdr { char s_id[8]; diff --git a/contrib/ipfilter/sys/tree.h b/contrib/ipfilter/sys/tree.h new file mode 100644 index 00000000000..58558854495 --- /dev/null +++ b/contrib/ipfilter/sys/tree.h @@ -0,0 +1,750 @@ +/* $NetBSD: tree.h,v 1.8 2004/03/28 19:38:30 provos Exp $ */ +/* $OpenBSD: tree.h,v 1.7 2002/10/17 21:51:54 art Exp $ */ +/* $FreeBSD: src/sys/sys/tree.h,v 1.7 2007/12/28 07:03:26 jasone Exp $ */ + +/*- + * Copyright 2002 Niels Provos + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#ifndef _SYS_TREE_H_ +#define _SYS_TREE_H_ + +/* + * This file defines data structures for different types of trees: + * splay trees and red-black trees. + * + * A splay tree is a self-organizing data structure. Every operation + * on the tree causes a splay to happen. The splay moves the requested + * node to the root of the tree and partly rebalances it. + * + * This has the benefit that request locality causes faster lookups as + * the requested nodes move to the top of the tree. On the other hand, + * every lookup causes memory writes. + * + * The Balance Theorem bounds the total access time for m operations + * and n inserts on an initially empty tree as O((m + n)lg n). The + * amortized cost for a sequence of m accesses to a splay tree is O(lg n); + * + * A red-black tree is a binary search tree with the node color as an + * extra attribute. It fulfills a set of conditions: + * - every search path from the root to a leaf consists of the + * same number of black nodes, + * - each red node (except for the root) has a black parent, + * - each leaf node is black. + * + * Every operation on a red-black tree is bounded as O(lg n). + * The maximum height of a red-black tree is 2lg (n+1). + */ + +#define SPLAY_HEAD(name, type) \ +struct name { \ + struct type *sph_root; /* root of the tree */ \ +} + +#define SPLAY_INITIALIZER(root) \ + { NULL } + +#define SPLAY_INIT(root) do { \ + (root)->sph_root = NULL; \ +} while (/*CONSTCOND*/ 0) + +#define SPLAY_ENTRY(type) \ +struct { \ + struct type *spe_left; /* left element */ \ + struct type *spe_right; /* right element */ \ +} + +#define SPLAY_LEFT(elm, field) (elm)->field.spe_left +#define SPLAY_RIGHT(elm, field) (elm)->field.spe_right +#define SPLAY_ROOT(head) (head)->sph_root +#define SPLAY_EMPTY(head) (SPLAY_ROOT(head) == NULL) + +/* SPLAY_ROTATE_{LEFT,RIGHT} expect that tmp hold SPLAY_{RIGHT,LEFT} */ +#define SPLAY_ROTATE_RIGHT(head, tmp, field) do { \ + SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(tmp, field); \ + SPLAY_RIGHT(tmp, field) = (head)->sph_root; \ + (head)->sph_root = tmp; \ +} while (/*CONSTCOND*/ 0) + +#define SPLAY_ROTATE_LEFT(head, tmp, field) do { \ + SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(tmp, field); \ + SPLAY_LEFT(tmp, field) = (head)->sph_root; \ + (head)->sph_root = tmp; \ +} while (/*CONSTCOND*/ 0) + +#define SPLAY_LINKLEFT(head, tmp, field) do { \ + SPLAY_LEFT(tmp, field) = (head)->sph_root; \ + tmp = (head)->sph_root; \ + (head)->sph_root = SPLAY_LEFT((head)->sph_root, field); \ +} while (/*CONSTCOND*/ 0) + +#define SPLAY_LINKRIGHT(head, tmp, field) do { \ + SPLAY_RIGHT(tmp, field) = (head)->sph_root; \ + tmp = (head)->sph_root; \ + (head)->sph_root = SPLAY_RIGHT((head)->sph_root, field); \ +} while (/*CONSTCOND*/ 0) + +#define SPLAY_ASSEMBLE(head, node, left, right, field) do { \ + SPLAY_RIGHT(left, field) = SPLAY_LEFT((head)->sph_root, field); \ + SPLAY_LEFT(right, field) = SPLAY_RIGHT((head)->sph_root, field);\ + SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(node, field); \ + SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(node, field); \ +} while (/*CONSTCOND*/ 0) + +/* Generates prototypes and inline functions */ + +#define SPLAY_PROTOTYPE(name, type, field, cmp) \ +void name##_SPLAY(struct name *, struct type *); \ +void name##_SPLAY_MINMAX(struct name *, int); \ +struct type *name##_SPLAY_INSERT(struct name *, struct type *); \ +struct type *name##_SPLAY_REMOVE(struct name *, struct type *); \ + \ +/* Finds the node with the same key as elm */ \ +static __inline struct type * \ +name##_SPLAY_FIND(struct name *head, struct type *elm) \ +{ \ + if (SPLAY_EMPTY(head)) \ + return(NULL); \ + name##_SPLAY(head, elm); \ + if ((cmp)(elm, (head)->sph_root) == 0) \ + return (head->sph_root); \ + return (NULL); \ +} \ + \ +static __inline struct type * \ +name##_SPLAY_NEXT(struct name *head, struct type *elm) \ +{ \ + name##_SPLAY(head, elm); \ + if (SPLAY_RIGHT(elm, field) != NULL) { \ + elm = SPLAY_RIGHT(elm, field); \ + while (SPLAY_LEFT(elm, field) != NULL) { \ + elm = SPLAY_LEFT(elm, field); \ + } \ + } else \ + elm = NULL; \ + return (elm); \ +} \ + \ +static __inline struct type * \ +name##_SPLAY_MIN_MAX(struct name *head, int val) \ +{ \ + name##_SPLAY_MINMAX(head, val); \ + return (SPLAY_ROOT(head)); \ +} + +/* Main splay operation. + * Moves node close to the key of elm to top + */ +#define SPLAY_GENERATE(name, type, field, cmp) \ +struct type * \ +name##_SPLAY_INSERT(struct name *head, struct type *elm) \ +{ \ + if (SPLAY_EMPTY(head)) { \ + SPLAY_LEFT(elm, field) = SPLAY_RIGHT(elm, field) = NULL; \ + } else { \ + int __comp; \ + name##_SPLAY(head, elm); \ + __comp = (cmp)(elm, (head)->sph_root); \ + if(__comp < 0) { \ + SPLAY_LEFT(elm, field) = SPLAY_LEFT((head)->sph_root, field);\ + SPLAY_RIGHT(elm, field) = (head)->sph_root; \ + SPLAY_LEFT((head)->sph_root, field) = NULL; \ + } else if (__comp > 0) { \ + SPLAY_RIGHT(elm, field) = SPLAY_RIGHT((head)->sph_root, field);\ + SPLAY_LEFT(elm, field) = (head)->sph_root; \ + SPLAY_RIGHT((head)->sph_root, field) = NULL; \ + } else \ + return ((head)->sph_root); \ + } \ + (head)->sph_root = (elm); \ + return (NULL); \ +} \ + \ +struct type * \ +name##_SPLAY_REMOVE(struct name *head, struct type *elm) \ +{ \ + struct type *__tmp; \ + if (SPLAY_EMPTY(head)) \ + return (NULL); \ + name##_SPLAY(head, elm); \ + if ((cmp)(elm, (head)->sph_root) == 0) { \ + if (SPLAY_LEFT((head)->sph_root, field) == NULL) { \ + (head)->sph_root = SPLAY_RIGHT((head)->sph_root, field);\ + } else { \ + __tmp = SPLAY_RIGHT((head)->sph_root, field); \ + (head)->sph_root = SPLAY_LEFT((head)->sph_root, field);\ + name##_SPLAY(head, elm); \ + SPLAY_RIGHT((head)->sph_root, field) = __tmp; \ + } \ + return (elm); \ + } \ + return (NULL); \ +} \ + \ +void \ +name##_SPLAY(struct name *head, struct type *elm) \ +{ \ + struct type __node, *__left, *__right, *__tmp; \ + int __comp; \ +\ + SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL;\ + __left = __right = &__node; \ +\ + while ((__comp = (cmp)(elm, (head)->sph_root)) != 0) { \ + if (__comp < 0) { \ + __tmp = SPLAY_LEFT((head)->sph_root, field); \ + if (__tmp == NULL) \ + break; \ + if ((cmp)(elm, __tmp) < 0){ \ + SPLAY_ROTATE_RIGHT(head, __tmp, field); \ + if (SPLAY_LEFT((head)->sph_root, field) == NULL)\ + break; \ + } \ + SPLAY_LINKLEFT(head, __right, field); \ + } else if (__comp > 0) { \ + __tmp = SPLAY_RIGHT((head)->sph_root, field); \ + if (__tmp == NULL) \ + break; \ + if ((cmp)(elm, __tmp) > 0){ \ + SPLAY_ROTATE_LEFT(head, __tmp, field); \ + if (SPLAY_RIGHT((head)->sph_root, field) == NULL)\ + break; \ + } \ + SPLAY_LINKRIGHT(head, __left, field); \ + } \ + } \ + SPLAY_ASSEMBLE(head, &__node, __left, __right, field); \ +} \ + \ +/* Splay with either the minimum or the maximum element \ + * Used to find minimum or maximum element in tree. \ + */ \ +void name##_SPLAY_MINMAX(struct name *head, int __comp) \ +{ \ + struct type __node, *__left, *__right, *__tmp; \ +\ + SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL;\ + __left = __right = &__node; \ +\ + while (1) { \ + if (__comp < 0) { \ + __tmp = SPLAY_LEFT((head)->sph_root, field); \ + if (__tmp == NULL) \ + break; \ + if (__comp < 0){ \ + SPLAY_ROTATE_RIGHT(head, __tmp, field); \ + if (SPLAY_LEFT((head)->sph_root, field) == NULL)\ + break; \ + } \ + SPLAY_LINKLEFT(head, __right, field); \ + } else if (__comp > 0) { \ + __tmp = SPLAY_RIGHT((head)->sph_root, field); \ + if (__tmp == NULL) \ + break; \ + if (__comp > 0) { \ + SPLAY_ROTATE_LEFT(head, __tmp, field); \ + if (SPLAY_RIGHT((head)->sph_root, field) == NULL)\ + break; \ + } \ + SPLAY_LINKRIGHT(head, __left, field); \ + } \ + } \ + SPLAY_ASSEMBLE(head, &__node, __left, __right, field); \ +} + +#define SPLAY_NEGINF -1 +#define SPLAY_INF 1 + +#define SPLAY_INSERT(name, x, y) name##_SPLAY_INSERT(x, y) +#define SPLAY_REMOVE(name, x, y) name##_SPLAY_REMOVE(x, y) +#define SPLAY_FIND(name, x, y) name##_SPLAY_FIND(x, y) +#define SPLAY_NEXT(name, x, y) name##_SPLAY_NEXT(x, y) +#define SPLAY_MIN(name, x) (SPLAY_EMPTY(x) ? NULL \ + : name##_SPLAY_MIN_MAX(x, SPLAY_NEGINF)) +#define SPLAY_MAX(name, x) (SPLAY_EMPTY(x) ? NULL \ + : name##_SPLAY_MIN_MAX(x, SPLAY_INF)) + +#define SPLAY_FOREACH(x, name, head) \ + for ((x) = SPLAY_MIN(name, head); \ + (x) != NULL; \ + (x) = SPLAY_NEXT(name, head, x)) + +/* Macros that define a red-black tree */ +#define RB_HEAD(name, type) \ +struct name { \ + struct type *rbh_root; /* root of the tree */ \ +} + +#define RB_INITIALIZER(root) \ + { NULL } + +#define RB_INIT(root) do { \ + (root)->rbh_root = NULL; \ +} while (/*CONSTCOND*/ 0) + +/* + * Undef for Linux + */ +#undef RB_BLACK +#undef RB_RED +#undef RB_ROOT + +#define RB_BLACK 0 +#define RB_RED 1 +#define RB_ENTRY(type) \ +struct { \ + struct type *rbe_left; /* left element */ \ + struct type *rbe_right; /* right element */ \ + struct type *rbe_parent; /* parent element */ \ + int rbe_color; /* node color */ \ +} + +#define RB_LEFT(elm, field) (elm)->field.rbe_left +#define RB_RIGHT(elm, field) (elm)->field.rbe_right +#define RB_PARENT(elm, field) (elm)->field.rbe_parent +#define RB_COLOR(elm, field) (elm)->field.rbe_color +#define RB_ROOT(head) (head)->rbh_root +#define RB_EMPTY(head) (RB_ROOT(head) == NULL) + +#define RB_SET(elm, parent, field) do { \ + RB_PARENT(elm, field) = parent; \ + RB_LEFT(elm, field) = RB_RIGHT(elm, field) = NULL; \ + RB_COLOR(elm, field) = RB_RED; \ +} while (/*CONSTCOND*/ 0) + +#define RB_SET_BLACKRED(black, red, field) do { \ + RB_COLOR(black, field) = RB_BLACK; \ + RB_COLOR(red, field) = RB_RED; \ +} while (/*CONSTCOND*/ 0) + +#ifndef RB_AUGMENT +#define RB_AUGMENT(x) do {} while (0) +#endif + +#define RB_ROTATE_LEFT(head, elm, tmp, field) do { \ + (tmp) = RB_RIGHT(elm, field); \ + if ((RB_RIGHT(elm, field) = RB_LEFT(tmp, field)) != NULL) { \ + RB_PARENT(RB_LEFT(tmp, field), field) = (elm); \ + } \ + RB_AUGMENT(elm); \ + if ((RB_PARENT(tmp, field) = RB_PARENT(elm, field)) != NULL) { \ + if ((elm) == RB_LEFT(RB_PARENT(elm, field), field)) \ + RB_LEFT(RB_PARENT(elm, field), field) = (tmp); \ + else \ + RB_RIGHT(RB_PARENT(elm, field), field) = (tmp); \ + } else \ + (head)->rbh_root = (tmp); \ + RB_LEFT(tmp, field) = (elm); \ + RB_PARENT(elm, field) = (tmp); \ + RB_AUGMENT(tmp); \ + if ((RB_PARENT(tmp, field))) \ + RB_AUGMENT(RB_PARENT(tmp, field)); \ +} while (/*CONSTCOND*/ 0) + +#define RB_ROTATE_RIGHT(head, elm, tmp, field) do { \ + (tmp) = RB_LEFT(elm, field); \ + if ((RB_LEFT(elm, field) = RB_RIGHT(tmp, field)) != NULL) { \ + RB_PARENT(RB_RIGHT(tmp, field), field) = (elm); \ + } \ + RB_AUGMENT(elm); \ + if ((RB_PARENT(tmp, field) = RB_PARENT(elm, field)) != NULL) { \ + if ((elm) == RB_LEFT(RB_PARENT(elm, field), field)) \ + RB_LEFT(RB_PARENT(elm, field), field) = (tmp); \ + else \ + RB_RIGHT(RB_PARENT(elm, field), field) = (tmp); \ + } else \ + (head)->rbh_root = (tmp); \ + RB_RIGHT(tmp, field) = (elm); \ + RB_PARENT(elm, field) = (tmp); \ + RB_AUGMENT(tmp); \ + if ((RB_PARENT(tmp, field))) \ + RB_AUGMENT(RB_PARENT(tmp, field)); \ +} while (/*CONSTCOND*/ 0) + +/* Generates prototypes and inline functions */ +#define RB_PROTOTYPE(name, type, field, cmp) \ + RB_PROTOTYPE_INTERNAL(name, type, field, cmp,) +#define RB_PROTOTYPE_STATIC(name, type, field, cmp) \ + RB_PROTOTYPE_INTERNAL(name, type, field, cmp, __unused static) +#define RB_PROTOTYPE_INTERNAL(name, type, field, cmp, attr) \ +attr void name##_RB_INSERT_COLOR(struct name *, struct type *); \ +attr void name##_RB_REMOVE_COLOR(struct name *, struct type *, struct type *);\ +attr struct type *name##_RB_REMOVE(struct name *, struct type *); \ +attr struct type *name##_RB_INSERT(struct name *, struct type *); \ +attr struct type *name##_RB_FIND(struct name *, struct type *); \ +attr struct type *name##_RB_NFIND(struct name *, struct type *); \ +attr struct type *name##_RB_NEXT(struct type *); \ +attr struct type *name##_RB_PREV(struct type *); \ +attr struct type *name##_RB_MINMAX(struct name *, int); \ + \ + +/* Main rb operation. + * Moves node close to the key of elm to top + */ +#define RB_GENERATE(name, type, field, cmp) \ + RB_GENERATE_INTERNAL(name, type, field, cmp,) +#define RB_GENERATE_STATIC(name, type, field, cmp) \ + RB_GENERATE_INTERNAL(name, type, field, cmp, __unused static) +#define RB_GENERATE_INTERNAL(name, type, field, cmp, attr) \ +attr void \ +name##_RB_INSERT_COLOR(struct name *head, struct type *elm) \ +{ \ + struct type *parent, *gparent, *tmp; \ + while ((parent = RB_PARENT(elm, field)) != NULL && \ + RB_COLOR(parent, field) == RB_RED) { \ + gparent = RB_PARENT(parent, field); \ + if (parent == RB_LEFT(gparent, field)) { \ + tmp = RB_RIGHT(gparent, field); \ + if (tmp && RB_COLOR(tmp, field) == RB_RED) { \ + RB_COLOR(tmp, field) = RB_BLACK; \ + RB_SET_BLACKRED(parent, gparent, field);\ + elm = gparent; \ + continue; \ + } \ + if (RB_RIGHT(parent, field) == elm) { \ + RB_ROTATE_LEFT(head, parent, tmp, field);\ + tmp = parent; \ + parent = elm; \ + elm = tmp; \ + } \ + RB_SET_BLACKRED(parent, gparent, field); \ + RB_ROTATE_RIGHT(head, gparent, tmp, field); \ + } else { \ + tmp = RB_LEFT(gparent, field); \ + if (tmp && RB_COLOR(tmp, field) == RB_RED) { \ + RB_COLOR(tmp, field) = RB_BLACK; \ + RB_SET_BLACKRED(parent, gparent, field);\ + elm = gparent; \ + continue; \ + } \ + if (RB_LEFT(parent, field) == elm) { \ + RB_ROTATE_RIGHT(head, parent, tmp, field);\ + tmp = parent; \ + parent = elm; \ + elm = tmp; \ + } \ + RB_SET_BLACKRED(parent, gparent, field); \ + RB_ROTATE_LEFT(head, gparent, tmp, field); \ + } \ + } \ + RB_COLOR(head->rbh_root, field) = RB_BLACK; \ +} \ + \ +attr void \ +name##_RB_REMOVE_COLOR(struct name *head, struct type *parent, struct type *elm) \ +{ \ + struct type *tmp; \ + while ((elm == NULL || RB_COLOR(elm, field) == RB_BLACK) && \ + elm != RB_ROOT(head)) { \ + if (RB_LEFT(parent, field) == elm) { \ + tmp = RB_RIGHT(parent, field); \ + if (RB_COLOR(tmp, field) == RB_RED) { \ + RB_SET_BLACKRED(tmp, parent, field); \ + RB_ROTATE_LEFT(head, parent, tmp, field);\ + tmp = RB_RIGHT(parent, field); \ + } \ + if ((RB_LEFT(tmp, field) == NULL || \ + RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) &&\ + (RB_RIGHT(tmp, field) == NULL || \ + RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK)) {\ + RB_COLOR(tmp, field) = RB_RED; \ + elm = parent; \ + parent = RB_PARENT(elm, field); \ + } else { \ + if (RB_RIGHT(tmp, field) == NULL || \ + RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK) {\ + struct type *oleft; \ + if ((oleft = RB_LEFT(tmp, field)) \ + != NULL) \ + RB_COLOR(oleft, field) = RB_BLACK;\ + RB_COLOR(tmp, field) = RB_RED; \ + RB_ROTATE_RIGHT(head, tmp, oleft, field);\ + tmp = RB_RIGHT(parent, field); \ + } \ + RB_COLOR(tmp, field) = RB_COLOR(parent, field);\ + RB_COLOR(parent, field) = RB_BLACK; \ + if (RB_RIGHT(tmp, field)) \ + RB_COLOR(RB_RIGHT(tmp, field), field) = RB_BLACK;\ + RB_ROTATE_LEFT(head, parent, tmp, field);\ + elm = RB_ROOT(head); \ + break; \ + } \ + } else { \ + tmp = RB_LEFT(parent, field); \ + if (RB_COLOR(tmp, field) == RB_RED) { \ + RB_SET_BLACKRED(tmp, parent, field); \ + RB_ROTATE_RIGHT(head, parent, tmp, field);\ + tmp = RB_LEFT(parent, field); \ + } \ + if ((RB_LEFT(tmp, field) == NULL || \ + RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) &&\ + (RB_RIGHT(tmp, field) == NULL || \ + RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK)) {\ + RB_COLOR(tmp, field) = RB_RED; \ + elm = parent; \ + parent = RB_PARENT(elm, field); \ + } else { \ + if (RB_LEFT(tmp, field) == NULL || \ + RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) {\ + struct type *oright; \ + if ((oright = RB_RIGHT(tmp, field)) \ + != NULL) \ + RB_COLOR(oright, field) = RB_BLACK;\ + RB_COLOR(tmp, field) = RB_RED; \ + RB_ROTATE_LEFT(head, tmp, oright, field);\ + tmp = RB_LEFT(parent, field); \ + } \ + RB_COLOR(tmp, field) = RB_COLOR(parent, field);\ + RB_COLOR(parent, field) = RB_BLACK; \ + if (RB_LEFT(tmp, field)) \ + RB_COLOR(RB_LEFT(tmp, field), field) = RB_BLACK;\ + RB_ROTATE_RIGHT(head, parent, tmp, field);\ + elm = RB_ROOT(head); \ + break; \ + } \ + } \ + } \ + if (elm) \ + RB_COLOR(elm, field) = RB_BLACK; \ +} \ + \ +attr struct type * \ +name##_RB_REMOVE(struct name *head, struct type *elm) \ +{ \ + struct type *child, *parent, *old = elm; \ + int color; \ + if (RB_LEFT(elm, field) == NULL) \ + child = RB_RIGHT(elm, field); \ + else if (RB_RIGHT(elm, field) == NULL) \ + child = RB_LEFT(elm, field); \ + else { \ + struct type *left; \ + elm = RB_RIGHT(elm, field); \ + while ((left = RB_LEFT(elm, field)) != NULL) \ + elm = left; \ + child = RB_RIGHT(elm, field); \ + parent = RB_PARENT(elm, field); \ + color = RB_COLOR(elm, field); \ + if (child) \ + RB_PARENT(child, field) = parent; \ + if (parent) { \ + if (RB_LEFT(parent, field) == elm) \ + RB_LEFT(parent, field) = child; \ + else \ + RB_RIGHT(parent, field) = child; \ + RB_AUGMENT(parent); \ + } else \ + RB_ROOT(head) = child; \ + if (RB_PARENT(elm, field) == old) \ + parent = elm; \ + (elm)->field = (old)->field; \ + if (RB_PARENT(old, field)) { \ + if (RB_LEFT(RB_PARENT(old, field), field) == old)\ + RB_LEFT(RB_PARENT(old, field), field) = elm;\ + else \ + RB_RIGHT(RB_PARENT(old, field), field) = elm;\ + RB_AUGMENT(RB_PARENT(old, field)); \ + } else \ + RB_ROOT(head) = elm; \ + RB_PARENT(RB_LEFT(old, field), field) = elm; \ + if (RB_RIGHT(old, field)) \ + RB_PARENT(RB_RIGHT(old, field), field) = elm; \ + if (parent) { \ + left = parent; \ + do { \ + RB_AUGMENT(left); \ + } while ((left = RB_PARENT(left, field)) != NULL); \ + } \ + goto color; \ + } \ + parent = RB_PARENT(elm, field); \ + color = RB_COLOR(elm, field); \ + if (child) \ + RB_PARENT(child, field) = parent; \ + if (parent) { \ + if (RB_LEFT(parent, field) == elm) \ + RB_LEFT(parent, field) = child; \ + else \ + RB_RIGHT(parent, field) = child; \ + RB_AUGMENT(parent); \ + } else \ + RB_ROOT(head) = child; \ +color: \ + if (color == RB_BLACK) \ + name##_RB_REMOVE_COLOR(head, parent, child); \ + return (old); \ +} \ + \ +/* Inserts a node into the RB tree */ \ +attr struct type * \ +name##_RB_INSERT(struct name *head, struct type *elm) \ +{ \ + struct type *tmp; \ + struct type *parent = NULL; \ + int comp = 0; \ + tmp = RB_ROOT(head); \ + while (tmp) { \ + parent = tmp; \ + comp = (cmp)(elm, parent); \ + if (comp < 0) \ + tmp = RB_LEFT(tmp, field); \ + else if (comp > 0) \ + tmp = RB_RIGHT(tmp, field); \ + else \ + return (tmp); \ + } \ + RB_SET(elm, parent, field); \ + if (parent != NULL) { \ + if (comp < 0) \ + RB_LEFT(parent, field) = elm; \ + else \ + RB_RIGHT(parent, field) = elm; \ + RB_AUGMENT(parent); \ + } else \ + RB_ROOT(head) = elm; \ + name##_RB_INSERT_COLOR(head, elm); \ + return (NULL); \ +} \ + \ +/* Finds the node with the same key as elm */ \ +attr struct type * \ +name##_RB_FIND(struct name *head, struct type *elm) \ +{ \ + struct type *tmp = RB_ROOT(head); \ + int comp; \ + while (tmp) { \ + comp = cmp(elm, tmp); \ + if (comp < 0) \ + tmp = RB_LEFT(tmp, field); \ + else if (comp > 0) \ + tmp = RB_RIGHT(tmp, field); \ + else \ + return (tmp); \ + } \ + return (NULL); \ +} \ + \ +/* Finds the first node greater than or equal to the search key */ \ +attr struct type * \ +name##_RB_NFIND(struct name *head, struct type *elm) \ +{ \ + struct type *tmp = RB_ROOT(head); \ + struct type *res = NULL; \ + int comp; \ + while (tmp) { \ + comp = cmp(elm, tmp); \ + if (comp < 0) { \ + res = tmp; \ + tmp = RB_LEFT(tmp, field); \ + } \ + else if (comp > 0) \ + tmp = RB_RIGHT(tmp, field); \ + else \ + return (tmp); \ + } \ + return (res); \ +} \ + \ +/* ARGSUSED */ \ +attr struct type * \ +name##_RB_NEXT(struct type *elm) \ +{ \ + if (RB_RIGHT(elm, field)) { \ + elm = RB_RIGHT(elm, field); \ + while (RB_LEFT(elm, field)) \ + elm = RB_LEFT(elm, field); \ + } else { \ + if (RB_PARENT(elm, field) && \ + (elm == RB_LEFT(RB_PARENT(elm, field), field))) \ + elm = RB_PARENT(elm, field); \ + else { \ + while (RB_PARENT(elm, field) && \ + (elm == RB_RIGHT(RB_PARENT(elm, field), field)))\ + elm = RB_PARENT(elm, field); \ + elm = RB_PARENT(elm, field); \ + } \ + } \ + return (elm); \ +} \ + \ +/* ARGSUSED */ \ +attr struct type * \ +name##_RB_PREV(struct type *elm) \ +{ \ + if (RB_LEFT(elm, field)) { \ + elm = RB_LEFT(elm, field); \ + while (RB_RIGHT(elm, field)) \ + elm = RB_RIGHT(elm, field); \ + } else { \ + if (RB_PARENT(elm, field) && \ + (elm == RB_RIGHT(RB_PARENT(elm, field), field))) \ + elm = RB_PARENT(elm, field); \ + else { \ + while (RB_PARENT(elm, field) && \ + (elm == RB_LEFT(RB_PARENT(elm, field), field)))\ + elm = RB_PARENT(elm, field); \ + elm = RB_PARENT(elm, field); \ + } \ + } \ + return (elm); \ +} \ + \ +attr struct type * \ +name##_RB_MINMAX(struct name *head, int val) \ +{ \ + struct type *tmp = RB_ROOT(head); \ + struct type *parent = NULL; \ + while (tmp) { \ + parent = tmp; \ + if (val < 0) \ + tmp = RB_LEFT(tmp, field); \ + else \ + tmp = RB_RIGHT(tmp, field); \ + } \ + return (parent); \ +} + +#define RB_NEGINF -1 +#define RB_INF 1 + +#define RB_INSERT(name, x, y) name##_RB_INSERT(x, y) +#define RB_REMOVE(name, x, y) name##_RB_REMOVE(x, y) +#define RB_FIND(name, x, y) name##_RB_FIND(x, y) +#define RB_NFIND(name, x, y) name##_RB_NFIND(x, y) +#define RB_NEXT(name, x, y) name##_RB_NEXT(y) +#define RB_PREV(name, x, y) name##_RB_PREV(y) +#define RB_MIN(name, x) name##_RB_MINMAX(x, RB_NEGINF) +#define RB_MAX(name, x) name##_RB_MINMAX(x, RB_INF) + +#define RB_FOREACH(x, name, head) \ + for ((x) = RB_MIN(name, head); \ + (x) != NULL; \ + (x) = name##_RB_NEXT(x)) + +#define RB_FOREACH_REVERSE(x, name, head) \ + for ((x) = RB_MAX(name, head); \ + (x) != NULL; \ + (x) = name##_RB_PREV(x)) + +#endif /* _SYS_TREE_H_ */ diff --git a/contrib/ipfilter/test/.cvsignore b/contrib/ipfilter/test/.cvsignore deleted file mode 100644 index 5825abcde9c..00000000000 --- a/contrib/ipfilter/test/.cvsignore +++ /dev/null @@ -1,87 +0,0 @@ -results -1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -i1 -i2 -i3 -i4 -i5 -i6 -i7 -i8 -i9 -i10 -i11 -f1 -f2 -f3 -f4 -f5 -f6 -f7 -f8 -f9 -f10 -f11 -f12 -f13 -f14 -n1 -n2 -n3 -n4 -n5 -n6 -n7 -f15 -f16 -ipv6.1 -ipv6.2 -l1 -ni1 -ni2 -ni3 -ni4 -f17 -in1 -in2 -in3 -in4 -p1 -p2 -i12 -ip1 -p3 -i13 -ni5 -ni6 -i14 -in5 -ipv6.3 -n8 -n9 -n10 -n11 -ni7 -ni8 -ni9 -ni10 -ni11 -ni12 -n12 -in6 -i15 -ni13 -ni14 -ni15 -ni16 diff --git a/contrib/ipfilter/test/Makefile b/contrib/ipfilter/test/Makefile index b0462f3f388..89183117b4e 100644 --- a/contrib/ipfilter/test/Makefile +++ b/contrib/ipfilter/test/Makefile @@ -3,6 +3,9 @@ # # See the IPFILTER.LICENCE file for details on licencing. # +POOLDEP=../ip_lookup.c ../ip_lookup.h ../ip_pool.c ../ip_pool.h \ + ../ip_htable.c ../ip_htable.h ../ip_dstlist.c ../ip_dstlist.h \ + ../tools/ippool_y.y BINDEST=/usr/local/bin SBINDEST=/sbin MANDIR=/usr/share/man @@ -14,86 +17,504 @@ expected.d: results: mkdir -p results -tests: ipf nat logtests ipv6 pools bpf +tests: ipf nat logtests ipv6 pools -ipf: ftests ptests +ipf: patests ftests -nat: ntests nitests intests +nat: intests ntests nitests first: -mkdir -p results # Filtering tests -ftests: f1 f2 f3 f4 f5 f6 f7 f8 f9 f10 f11 f12 f13 f14 f15 f16 f17 f18 f19 f20 f24 +ftests: f1 f2 f3 f4 f5 f6 f7 f8 f9 f10 f11 f12 f13 f14 f15 f16 f17 f18 f19 f20 f21 f22 f24 f25 f26 f27 f28 f29 f30 # Rule parsing tests -ptests: i1 i2 i3 i4 i5 i6 i7 i8 i9 i10 i11 i12 i13 i14 i15 i16 i17 i18 i19 \ - i20 i21 +patests: i1 i2 i3 i4 i5 i6 i7 i8 i9 i10 i11 i12 i13 i14 i15 i16 i17 i18 i19 \ + i20 i21 i22 i23 -ntests: n1 n2 n3 n4 n5 n6 n7 n8 n9 n10 n11 n12 n13 n14 n16 +ntests: n1 n2 n3 n4 n5 n6 n7 n8 n9 n10 n11 n12 n13 n14 n15 n16 n17 n18 n100 n101 n102 n103 n104 n105 n106 n200 + +ntests6: n1_6 n2_6 n4_6 n5_6 n6_6 n7_6 n8_6 n9_6 n11_6 n12_6 n15_6 nitests: ni1 ni2 ni3 ni4 ni5 ni6 ni7 ni8 ni9 ni10 ni11 ni12 ni13 ni14 ni15 \ - ni16 ni19 ni20 ni21 ni23 + ni16 ni17 ni18 ni19 ni20 ni21 ni23 -intests: in1 in2 in3 in4 in5 in6 +intests: in1 in2 in3 in4 in5 in6 in7 in8 in100 in101 in102 logtests: l1 -pools: p1 p2 p3 p5 ip1 ip2 +pools: p1 p2 p3 p4 p5 p6 p7 p9 p10 p11 p12 p13 ip1 ip2 ip3 -ipv6: ipv6.1 ipv6.2 ipv6.3 ipv6.5 ipv6.6 +ipv6: ipv6.1 ipv6.2 ipv6.3 ipv6.4 ipv6.5 ipv6.6 ntests6 bpf: bpf1 bpf-f1 -f1 f2 f3 f4 f5 f6 f7 f8 f9 f10 f11 f12 f13 f14 f19: +f1: expected/f1 input/f1 regress/f1 @/bin/sh ./dotest `awk "/^$@ / { print; } " test.format` -f15 f16 f17 f18 f20 f24: +f2: expected/f2 input/f2 regress/f2 + @/bin/sh ./dotest `awk "/^$@ / { print; } " test.format` + +f3: expected/f3 input/f3 regress/f3 + @/bin/sh ./dotest `awk "/^$@ / { print; } " test.format` + +f4: expected/f4 input/f4 regress/f4 + @/bin/sh ./dotest `awk "/^$@ / { print; } " test.format` + +f5: expected/f5 input/f5 regress/f5 + @/bin/sh ./dotest `awk "/^$@ / { print; } " test.format` + +f6: expected/f6 input/f6 regress/f6 + @/bin/sh ./dotest `awk "/^$@ / { print; } " test.format` + +f7: expected/f7 input/f7 regress/f7 + @/bin/sh ./dotest `awk "/^$@ / { print; } " test.format` + +f8: expected/f8 input/f8 regress/f8 + @/bin/sh ./dotest `awk "/^$@ / { print; } " test.format` + +f9: expected/f9 input/f9 regress/f9 + @/bin/sh ./dotest `awk "/^$@ / { print; } " test.format` + +f10: expected/f10 input/f10 regress/f10 + @/bin/sh ./dotest `awk "/^$@ / { print; } " test.format` + +f11: expected/f11 input/f11 regress/f11 + @/bin/sh ./dotest `awk "/^$@ / { print; } " test.format` + +f12: expected/f12 input/f12 regress/f12 + @/bin/sh ./dotest `awk "/^$@ / { print; } " test.format` + +f13: expected/f13 input/f13 regress/f13 + @/bin/sh ./dotest `awk "/^$@ / { print; } " test.format` + +f14: expected/f14 input/f14 regress/f14 + @/bin/sh ./dotest `awk "/^$@ / { print; } " test.format` + +f19: expected/f15 input/f15 regress/f15 + @/bin/sh ./dotest `awk "/^$@ / { print; } " test.format` + +f15: expected/f15 input/f15 regress/f15 @/bin/sh ./mtest `awk "/^$@ / { print; } " test.format` -i1 i2 i3 i4 i5 i6 i7 i8 i9 i10 i11 i12 i13 i14 i15 i16 i17 i18 i19 i20 i21 bpf1: +f16: expected/f16 input/f16 regress/f16 + @/bin/sh ./mtest `awk "/^$@ / { print; } " test.format` + +f17: expected/f17 input/f17 regress/f17 + @/bin/sh ./mtest `awk "/^$@ / { print; } " test.format` + +f18: expected/f18 input/f18 regress/f18 + @/bin/sh ./mtest `awk "/^$@ / { print; } " test.format` + +f20: expected/f20 input/f20 regress/f20 + @/bin/sh ./mtest `awk "/^$@ / { print; } " test.format` + +f21: expected/f21 input/f21 regress/f21 + @/bin/sh ./mtest `awk "/^$@ / { print; } " test.format` + +f22: expected/f22 input/f22 regress/f22 + @/bin/sh ./mtest `awk "/^$@ / { print; } " test.format` + +f24: expected/f24 input/f24 regress/f24 + @/bin/sh ./mtest `awk "/^$@ / { print; } " test.format` + +f25: expected/f25 input/f25 regress/f25 + @/bin/sh ./mtest `awk "/^$@ / { print; } " test.format` + +f26: expected/f26 input/f26 regress/f26 + @/bin/sh ./dotest `awk "/^$@ / { print; } " test.format` + +f27: expected/f27 input/f27 regress/f27 + @/bin/sh ./dotest `awk "/^$@ / { print; } " test.format` + +f28: expected/f28 input/f28 regress/f28.ipf regress/f28.pool $(POOLDEP) + @/bin/sh ./ptest `awk "/^$@ / { print; } " test.format` + +f29: expected/f29 input/f29 regress/f29.ipf regress/f29.pool $(POOLDEP) + @/bin/sh ./ptest `awk "/^$@ / { print; } " test.format` + +f30: expected/f30 input/f30 regress/f30 + @/bin/sh ./dotest `awk "/^$@ / { print; } " test.format` + +i1: expected/i1 regress/i1 @/bin/sh ./itest `awk "/^$@ / { print; } " test.format` -n1 n2 n3 n4 n5 n6 n7 n8 n9 n10 n11 n12 n13 n14 n16: +i2: expected/i2 regress/i2 + @/bin/sh ./itest `awk "/^$@ / { print; } " test.format` + +i3: expected/i3 regress/i3 + @/bin/sh ./itest `awk "/^$@ / { print; } " test.format` + +i4: expected/i4 regress/i4 + @/bin/sh ./itest `awk "/^$@ / { print; } " test.format` + +i5: expected/i5 regress/i5 + @/bin/sh ./itest `awk "/^$@ / { print; } " test.format` + +i6: expected/i6 regress/i6 + @/bin/sh ./itest `awk "/^$@ / { print; } " test.format` + +i7: expected/i7 regress/i7 + @/bin/sh ./itest `awk "/^$@ / { print; } " test.format` + +i8: expected/i8 regress/i8 + @/bin/sh ./itest `awk "/^$@ / { print; } " test.format` + +i9: expected/i9 regress/i9 + @/bin/sh ./itest `awk "/^$@ / { print; } " test.format` + +i10: expected/i10 regress/i10 + @/bin/sh ./itest `awk "/^$@ / { print; } " test.format` + +i11: expected/i11 regress/i11 + @/bin/sh ./itest `awk "/^$@ / { print; } " test.format` + +i12: expected/i12 regress/i12 + @/bin/sh ./itest `awk "/^$@ / { print; } " test.format` + +i13: expected/i13 regress/i13 + @/bin/sh ./itest `awk "/^$@ / { print; } " test.format` + +i14: expected/i14 regress/i14 + @/bin/sh ./itest `awk "/^$@ / { print; } " test.format` + +i15: expected/i15 regress/i15 + @/bin/sh ./itest `awk "/^$@ / { print; } " test.format` + +i16: expected/i16 regress/i16 + @/bin/sh ./itest `awk "/^$@ / { print; } " test.format` + +i17: expected/i17 regress/i17 + @/bin/sh ./itest `awk "/^$@ / { print; } " test.format` + +i18: expected/i18 regress/i18 + @/bin/sh ./itest `awk "/^$@ / { print; } " test.format` + +i19: expected/i19 regress/i19 + @/bin/sh ./itest `awk "/^$@ / { print; } " test.format` + +i20: expected/i20 regress/i20 + @/bin/sh ./itest `awk "/^$@ / { print; } " test.format` + +i21: expected/i21 regress/i21 + @/bin/sh ./itest `awk "/^$@ / { print; } " test.format` + +i22: expected/i22 regress/i22 + @/bin/sh ./itest `awk "/^$@ / { print; } " test.format` + +i23: expected/i23 regress/i23 + @/bin/sh ./itest `awk "/^$@ / { print; } " test.format` + +bpf1: expected/bpf1 regress/bpf1 + @/bin/sh ./itest `awk "/^$@ / { print; } " test.format` + +n1: expected/n1 regress/n1 input/n1 @/bin/sh ./nattest `awk "/^$@ / { print; } " test.format` -ni2 ni3 ni4 ni5 ni7 ni8 ni9 ni10 ni11 ni12 ni13 ni14 ni15 ni16 ni19 ni20: +n2: expected/n2 regress/n2 input/n2 + @/bin/sh ./nattest `awk "/^$@ / { print; } " test.format` + +n3: expected/n3 regress/n3 input/n3 + @/bin/sh ./nattest `awk "/^$@ / { print; } " test.format` + +n4: expected/n4 regress/n4 input/n4 + @/bin/sh ./nattest `awk "/^$@ / { print; } " test.format` + +n5: expected/n5 regress/n5 input/n5 + @/bin/sh ./nattest `awk "/^$@ / { print; } " test.format` + +n6: expected/n6 regress/n6 input/n6 + @/bin/sh ./nattest `awk "/^$@ / { print; } " test.format` + +n7: expected/n7 regress/n7 input/n7 + @/bin/sh ./nattest `awk "/^$@ / { print; } " test.format` + +n8: expected/n8 regress/n8 input/n8 + @/bin/sh ./nattest `awk "/^$@ / { print; } " test.format` + +n9: expected/n9 regress/n9 input/n9 + @/bin/sh ./nattest `awk "/^$@ / { print; } " test.format` + +n10: expected/n10 regress/n10 input/n10 + @/bin/sh ./nattest `awk "/^$@ / { print; } " test.format` + +n11: expected/n11 regress/n11 input/n11 + @/bin/sh ./nattest `awk "/^$@ / { print; } " test.format` + +n12: expected/n12 regress/n12 input/n12 + @/bin/sh ./nattest `awk "/^$@ / { print; } " test.format` + +n13: expected/n13 regress/n13 input/n13 + @/bin/sh ./nattest `awk "/^$@ / { print; } " test.format` + +n14: expected/n14 regress/n14 input/n14 + @/bin/sh ./nattest `awk "/^$@ / { print; } " test.format` + +n15: expected/n15 regress/n15 input/n15 + @/bin/sh ./nattest `awk "/^$@ / { print; } " test.format` + +n16: expected/n16 regress/n16 input/n16 + @/bin/sh ./nattest `awk "/^$@ / { print; } " test.format` + +n17: expected/n17 regress/n17 input/n17 + @/bin/sh ./nattest `awk "/^$@ / { print; } " test.format` + +n18: expected/n18 regress/n18 input/n18 + @/bin/sh ./nattest `awk "/^$@ / { print; } " test.format` + +n100: expected/n100 regress/n100 input/n100 + @/bin/sh ./nattest `awk "/^$@ / { print; } " test.format` + +n101: expected/n101 regress/n101 input/n101 + @/bin/sh ./nattest `awk "/^$@ / { print; } " test.format` + +n102: expected/n102 regress/n102 input/n102 + @/bin/sh ./nattest `awk "/^$@ / { print; } " test.format` + +n103: expected/n103 regress/n103 input/n103 + @/bin/sh ./nattest `awk "/^$@ / { print; } " test.format` + +n104: expected/n104 regress/n104 input/n104 + @/bin/sh ./nattest `awk "/^$@ / { print; } " test.format` + +n105: expected/n105 regress/n105 input/n105 + @/bin/sh ./nattest `awk "/^$@ / { print; } " test.format` + +n106: expected/n106 regress/n106 input/n106 + @/bin/sh ./nattest `awk "/^$@ / { print; } " test.format` + +n200: expected/n200 regress/n200 input/n200 + @/bin/sh ./nattest `awk "/^$@ / { print; } " test.format` + +n1_6: expected/n1_6 regress/n1_6 input/n1_6 + @/bin/sh ./nattest `awk "/^$@ / { print; } " test.format` + +n2_6: expected/n2_6 regress/n2_6 input/n2_6 + @/bin/sh ./nattest `awk "/^$@ / { print; } " test.format` + +n4_6: expected/n4_6 regress/n4_6 input/n4_6 + @/bin/sh ./nattest `awk "/^$@ / { print; } " test.format` + +n5_6: expected/n5_6 regress/n5_6 input/n5_6 + @/bin/sh ./nattest `awk "/^$@ / { print; } " test.format` + +n6_6: expected/n6_6 regress/n6_6 input/n6_6 + @/bin/sh ./nattest `awk "/^$@ / { print; } " test.format` + +n7_6: expected/n7_6 regress/n7_6 input/n7_6 + @/bin/sh ./nattest `awk "/^$@ / { print; } " test.format` + +n8_6: expected/n8_6 regress/n8_6 input/n8_6 + @/bin/sh ./nattest `awk "/^$@ / { print; } " test.format` + +n9_6: expected/n9_6 regress/n9_6 input/n9_6 + @/bin/sh ./nattest `awk "/^$@ / { print; } " test.format` + +n11_6: expected/n11_6 regress/n11_6 input/n11_6 + @/bin/sh ./nattest `awk "/^$@ / { print; } " test.format` + +n12_6: expected/n12_6 regress/n12_6 input/n12_6 + @/bin/sh ./nattest `awk "/^$@ / { print; } " test.format` + +n15_6: expected/n15_6 regress/n15_6 input/n15_6 + @/bin/sh ./nattest `awk "/^$@ / { print; } " test.format` + +ni2: expected/ni2 input/ni2 regress/ni2.nat regress/ni2.ipf @/bin/sh ./natipftest single `awk "/^$@ / { print; } " test.format` -ni1 ni6 ni21 ni23: +ni3: expected/ni3 input/ni3 regress/ni3.nat regress/ni3.ipf + @/bin/sh ./natipftest single `awk "/^$@ / { print; } " test.format` + +ni4: expected/ni4 input/ni4 regress/ni4.nat regress/ni4.ipf + @/bin/sh ./natipftest single `awk "/^$@ / { print; } " test.format` + +ni5: expected/ni5 input/ni5 regress/ni5.nat regress/ni5.ipf + @/bin/sh ./natipftest single `awk "/^$@ / { print; } " test.format` + +ni7: expected/ni7 input/ni7 regress/ni7.nat regress/ni7.ipf + @/bin/sh ./natipftest single `awk "/^$@ / { print; } " test.format` + +ni8: expected/ni8 input/ni8 regress/ni8.nat regress/ni8.ipf + @/bin/sh ./natipftest single `awk "/^$@ / { print; } " test.format` + +ni9: expected/ni9 input/ni9 regress/ni9.nat regress/ni9.ipf + @/bin/sh ./natipftest single `awk "/^$@ / { print; } " test.format` + +ni10: expected/ni10 input/ni10 regress/ni10.nat regress/ni10.ipf + @/bin/sh ./natipftest single `awk "/^$@ / { print; } " test.format` + +ni11: expected/ni11 input/ni11 regress/ni11.nat regress/ni11.ipf + @/bin/sh ./natipftest single `awk "/^$@ / { print; } " test.format` + +ni12: expected/ni12 input/ni12 regress/ni12.nat regress/ni12.ipf + @/bin/sh ./natipftest single `awk "/^$@ / { print; } " test.format` + +ni13: expected/ni13 input/ni13 regress/ni13.nat regress/ni13.ipf + @/bin/sh ./natipftest single `awk "/^$@ / { print; } " test.format` + +ni14: expected/ni14 input/ni14 regress/ni14.nat regress/ni14.ipf + @/bin/sh ./natipftest single `awk "/^$@ / { print; } " test.format` + +ni15: expected/ni15 input/ni15 regress/ni15.nat regress/ni15.ipf + @/bin/sh ./natipftest single `awk "/^$@ / { print; } " test.format` + +ni16: expected/ni16 input/ni16 regress/ni16.nat regress/ni16.ipf + @/bin/sh ./natipftest single `awk "/^$@ / { print; } " test.format` + +ni19: expected/ni19 input/ni19 regress/ni19.nat regress/ni19.ipf + @/bin/sh ./natipftest single `awk "/^$@ / { print; } " test.format` + +ni20: expected/ni20 input/ni20 regress/ni20.nat regress/ni20.ipf + @/bin/sh ./natipftest single `awk "/^$@ / { print; } " test.format` + +ni1: expected/ni1 input/ni1 regress/ni1.nat regress/ni1.ipf @/bin/sh ./natipftest multi `awk "/^$@ / { print; } " test.format` -in1 in2 in3 in4 in5 in6: +ni6: expected/ni6 input/ni6 regress/ni6.nat regress/ni6.ipf + @/bin/sh ./natipftest multi `awk "/^$@ / { print; } " test.format` + +ni17: expected/ni17 input/ni17 regress/ni17.nat regress/ni17.ipf + @/bin/sh ./natipftest multi `awk "/^$@ / { print; } " test.format` + +ni18: expected/ni18 input/ni18 regress/ni18.nat regress/ni18.ipf + @/bin/sh ./natipftest multi `awk "/^$@ / { print; } " test.format` + +ni21: expected/ni21 input/ni21 regress/ni21.nat regress/ni21.ipf + @/bin/sh ./natipftest multi `awk "/^$@ / { print; } " test.format` + +ni23: expected/ni23 input/ni23 regress/ni23.nat regress/ni23.ipf + @/bin/sh ./natipftest multi `awk "/^$@ / { print; } " test.format` + +in1: expected/in1 regress/in1 + @/bin/sh ./intest `awk "/^$@ / { print; } " test.format` + +in2: expected/in2 regress/in2 + @/bin/sh ./intest `awk "/^$@ / { print; } " test.format` + +in3: expected/in3 regress/in3 + @/bin/sh ./intest `awk "/^$@ / { print; } " test.format` + +in4: expected/in4 regress/in4 + @/bin/sh ./intest `awk "/^$@ / { print; } " test.format` + +in5: expected/in5 regress/in5 + @/bin/sh ./intest `awk "/^$@ / { print; } " test.format` + +in6: expected/in6 regress/in6 + @/bin/sh ./intest `awk "/^$@ / { print; } " test.format` + +in7: expected/in7 regress/in7 + @/bin/sh ./intest `awk "/^$@ / { print; } " test.format` + +in8: expected/in8 regress/in8 + @/bin/sh ./intest `awk "/^$@ / { print; } " test.format` + +in100: expected/in100 regress/in100 + @/bin/sh ./intest `awk "/^$@ / { print; } " test.format` + +in101: expected/in101 regress/in101 + @/bin/sh ./intest `awk "/^$@ / { print; } " test.format` + +in102: expected/in102 regress/in102 @/bin/sh ./intest `awk "/^$@ / { print; } " test.format` l1: @/bin/sh ./logtest `awk "/^$@ / { print; } " test.format` -ipv6.1 ipv6.2 ipv6.3 ipv6.5 ipv6.6: +ipv6.1: expected/ipv6.1 input/ipv6.1 regress/ipv6.1 @/bin/sh ./dotest6 `awk "/^$@ / { print; } " test.format` -p1 p2 p3 p5: +ipv6.2: expected/ipv6.2 input/ipv6.2 regress/ipv6.2 + @/bin/sh ./dotest6 `awk "/^$@ / { print; } " test.format` + +ipv6.3: expected/ipv6.3 input/ipv6.3 regress/ipv6.3 + @/bin/sh ./dotest6 `awk "/^$@ / { print; } " test.format` + +ipv6.4: expected/ipv6.4 input/ipv6.4 regress/ipv6.4 + @/bin/sh ./dotest6 `awk "/^$@ / { print; } " test.format` + +ipv6.5: expected/ipv6.5 input/ipv6.5 regress/ipv6.5 + @/bin/sh ./dotest6 `awk "/^$@ / { print; } " test.format` + +ipv6.6: expected/ipv6.6 input/ipv6.6 regress/ipv6.6 + @/bin/sh ./dotest6 `awk "/^$@ / { print; } " test.format` + +p1: expected/p1 input/p1 regress/p1.ipf regress/p1.pool $(POOLDEP) @/bin/sh ./ptest `awk "/^$@ / { print; } " test.format` -ip1 ip2: +p2: expected/p2 input/p2 regress/p2.ipf $(POOLDEP) + @/bin/sh ./ptest `awk "/^$@ / { print; } " test.format` + +p3: expected/p3 input/p3 regress/p3.ipf regress/p3.pool $(POOLDEP) + @/bin/sh ./ptest `awk "/^$@ / { print; } " test.format` + +p4: expected/p4 input/p4 regress/p4.nat regress/p4.pool $(POOLDEP) + @/bin/sh ./ptest `awk "/^$@ / { print; } " test.format` + +p5: expected/p5 input/p5 regress/p5.ipf regress/p5.pool $(POOLDEP) + @/bin/sh ./ptest `awk "/^$@ / { print; } " test.format` + +p6: expected/p6 input/p6 regress/p6.ipf regress/p6.pool $(POOLDEP) + @/bin/sh ./ptest `awk "/^$@ / { print; } " test.format` + +p7: expected/p7 input/p7 regress/p7.nat regress/p7.pool $(POOLDEP) + @/bin/sh ./ptest `awk "/^$@ / { print; } " test.format` + +p9: expected/p9 input/p9 regress/p9.nat regress/p9.pool $(POOLDEP) + @/bin/sh ./ptest `awk "/^$@ / { print; } " test.format` + +p10: expected/p10 input/p10 regress/p10.nat regress/p10.pool $(POOLDEP) + @/bin/sh ./ptest `awk "/^$@ / { print; } " test.format` + +p11: expected/p11 input/p11 regress/p11.nat regress/p11.pool $(POOLDEP) + @/bin/sh ./ptest `awk "/^$@ / { print; } " test.format` + +p12: expected/p12 input/p12 regress/p12.nat regress/p12.pool $(POOLDEP) + @/bin/sh ./ptest `awk "/^$@ / { print; } " test.format` + +p13: expected/p13 input/p13 regress/p13.ipf regress/p13.pool $(POOLDEP) + @/bin/sh ./ptest `awk "/^$@ / { print; } " test.format` + +ip1: expected/ip1 regress/ip1 $(POOLDEP) @/bin/sh ./iptest `awk "/^$@ / { print; } " test.format` -bpf-f1: +ip2: expected/ip2 input/ip2.data regress/ip2 $(POOLDEP) + @/bin/sh ./iptest `awk "/^$@ / { print; } " test.format` + +ip3: expected/ip3 regress/ip3 $(POOLDEP) + @/bin/sh ./iptest `awk "/^$@ / { print; } " test.format` + +bpf-f1: expected/bpf-f1 regress/bpf-f1 /bin/sh ./bpftest `awk "/^$@ / { print; } " test.format` clean: - /bin/rm -f f1 f2 f3 f4 f5 f6 f7 f8 f9 f10 f11 f12 f13 f14 f15 f16 f17 f18 f19 f20 f24 - /bin/rm -f i1 i2 i3 i4 i5 i6 i7 i8 i9 i10 i11 i12 i13 i14 i15 i16 i17 i18 i19 i20 i21 - /bin/rm -f n1 n2 n3 n4 n5 n6 n7 n8 n9 n10 n11 n12 n13 n14 n16 + /bin/rm -f f1 f2 f3 f4 f5 f6 f7 f8 f9 f10 f11 f12 f13 f14 f15 f16 f17 + /bin/rm -f f18 f19 f20 f21 f22 f24 f25 f26 f27 f28 f29 + /bin/rm -f i1 i2 i3 i4 i5 i6 i7 i8 i9 i10 i11 i12 i13 i14 i15 i16 i17 + /bin/rm -f i18 i19 i20 i21 i22 i23 + /bin/rm -f n1 n2 n3 n4 n5 n6 n7 n8 n9 n10 n11 n12 n13 n14 n15 n16 n17 n18 n100 n101 n102 n103 n104 n105 n106 n200 + /bin/rm -f n1_6 n2_6 n4_6 n5_6 n6_6 n7_6 n8_6 n9_6 n11_6 n12_6 n15_6 /bin/rm -f ni1 ni2 ni3 ni4 ni5 ni6 ni7 ni8 ni9 - /bin/rm -f ni10 ni11 ni12 ni13 ni14 ni15 ni16 ni19 ni20 ni21 ni23 - /bin/rm -f in1 in2 in3 in4 in5 in6 - /bin/rm -f p1 p2 p3 p5 ip1 ip2 + /bin/rm -f ni10 ni11 ni12 ni13 ni14 ni15 ni16 ni17 ni18 ni19 ni20 ni21 ni23 + /bin/rm -f in1 in2 in3 in4 in5 in6 in7 in100 in101 in102 + /bin/rm -f p1 p2 p3 p4 p5 p6 p7 p9 p10 p11 p12 p13 ip1 ip2 ip3 /bin/rm -f l1 - /bin/rm -f ipv6.1 ipv6.2 ipv6.3 ipv6.5 ipv6.6 + /bin/rm -f ipv6.1 ipv6.2 ipv6.3 ipv6.4 ipv6.5 ipv6.6 /bin/rm -f bpf1 bpf-f1 /bin/rm -f results/* logout (cd expected; make clean) diffs: -cd expected; for i in *; do if [ -f $$i -a ! -f ../$$i -a -f ../results/$$i ] ; then diff -c $$i ../results/$$i >> ../diff.out; fi done + +n6s: + for i in 1 2 4 5 6 7 11 13 14 15; do \ + sh i4to6 < input/n$${i} > input/n$${i}_6; \ + sh e4to6 < regress/n$${i} > regress/n$${i}_6; \ + sh e4to6 < expected/n$${i} > expected/n$${i}_6; \ + done + for i in 8 9 10 12 17; do \ + sh e4to6 < regress/n$${i} > regress/n$${i}_6; \ + perl h4to6 < input/n$${i} > input/n$${i}_6; \ + done diff --git a/contrib/ipfilter/test/bpftest b/contrib/ipfilter/test/bpftest index b24c0f16ae4..5449658f820 100644 --- a/contrib/ipfilter/test/bpftest +++ b/contrib/ipfilter/test/bpftest @@ -1,28 +1,19 @@ #!/bin/sh -if [ -f /usr/ucb/touch ] ; then - TOUCH=/usr/ucb/touch -else - if [ -f /usr/bin/touch ] ; then - TOUCH=/usr/bin/touch - else - if [ -f /bin/touch ] ; then - TOUCH=/bin/touch - fi - fi -fi -echo "$1..."; -input=`expr $1 : 'bpf-\(.*\)'` -/bin/cp /dev/null results/$1 +name=$1 + +. ./ipflib.sh + +test_init + +echo "$name..."; +input=`expr $name : 'bpf-\(.*\)'` +/bin/cp /dev/null results/$name ( while read rule; do - echo "$rule" | ../ipftest -Rbr - -i input/$input >> results/$1; + echo "$rule" | ../ipftest -Rbr - -i input/$input >> results/$name if [ $? -ne 0 ] ; then exit 1; fi - echo "--------" >> results/$1 -done ) < regress/$1 -cmp expected/$1 results/$1 -status=$? -if [ $status = 0 ] ; then - $TOUCH $1 -fi + echo "--------" >> results/$name +done ) < regress/$name +check_results $name exit $status diff --git a/contrib/ipfilter/test/dotest b/contrib/ipfilter/test/dotest index 29891091da5..71c09b7b816 100644 --- a/contrib/ipfilter/test/dotest +++ b/contrib/ipfilter/test/dotest @@ -1,19 +1,13 @@ #!/bin/sh -thistest=$1 +name=$1 format=$2 output=$3 tuning=$4 -if [ -f /usr/ucb/touch ] ; then - TOUCH=/usr/ucb/touch -else - if [ -f /usr/bin/touch ] ; then - TOUCH=/usr/bin/touch - else - if [ -f /bin/touch ] ; then - TOUCH=/bin/touch - fi - fi -fi + +. ./ipflib.sh + +test_init + if [ "$tuning" != "" ] ; then case $tuning in -*) @@ -23,18 +17,17 @@ if [ "$tuning" != "" ] ; then ;; esac fi -echo "${thistest}..."; -/bin/cp /dev/null results/${thistest} +echo "${name}..."; +n=1 +/bin/cp /dev/null results/${name} ( while read rule; do - echo "$rule" | ../ipftest -F $format -Rbr - -i input/${thistest} $tuning>> results/${thistest}; - if [ $? -ne 0 ] ; then - exit 1; - fi - echo "--------" >> results/${thistest} -done ) < regress/${thistest} -cmp expected/${thistest} results/${thistest} -status=$? -if [ $status = 0 ] ; then - $TOUCH ${thistest} -fi + set_core $name $n + echo "$rule" | ../ipftest -F $format -Rbr - -i input/${name} $tuning>> results/${name} & + back=$! + wait $back + test_end_leak $? + n=`expr $n + 1` + echo "--------" >> results/${name} +done ) < regress/${name} +check_results $name exit $status diff --git a/contrib/ipfilter/test/e4to6 b/contrib/ipfilter/test/e4to6 new file mode 100644 index 00000000000..87558993b13 --- /dev/null +++ b/contrib/ipfilter/test/e4to6 @@ -0,0 +1,61 @@ +sed \ +-e 's/192.168.126.0/c0a8:7e00::/' \ +-e 's/\/32/\/128/g' \ +-e 's/\/24/\/112/g' \ +-e 's/\/16/\/32/g' \ +-e 's/10\.2\.0\.0/10::2:0:0/g' \ +-e 's/1\.\([0-9]\)\.\([0-9]\)\.\([0-9]\)/1:0:0:0:0:\1:\2:\3/g' \ +-e 's/2\.\([0-9]\)\.\([0-9]\)\.\([0-9]\)/2:0:0:0:0:\1:\2:\3/g' \ +-e 's/4\.\([0-9]\)\.\([0-9]\)\.\([0-9]\)/4:\1:\2:0:0:0:0:\3/g' \ +-e 's/3\.\([0-9]\)\.\([0-9]\)\.\([0-9]\)/3:0:\1:0:0:0:\2:\3/g' \ +-e 's/5\.\([0-9]\)\.\([0-9]\)\.\([0-9]\)/5:\1:0:0:0:0:\2:\3/g' \ +-e 's/9\.\([0-9]\)\.\([0-9]\)\.\([0-9]\)/9:\1:\2:0:0:0:0:\3/g' \ +-e 's/10\.1\.\([0-9]\)\.\([0-9]\)/10:1:\1:0:0:0:0:\2/g' \ +-e 's/10\.10\.\([0-9]*\)\.\([0-9]\)/10:10:\1:0:0:0:0:\2/g' \ +-e 's/10\.2\.\([0-9]\)\.\([0-9]\)/10:0:0:0:0:2:\1:\2/g' \ +-e 's/10\.4\.3\.\([0-9]\)/10:4:3:0:0:0:0:\1/g' \ +-e 's/10\.3\.4\.\([0-9]\)/10:0:0:0:0:3:4:\1/g' \ +-e 's/10\.3\.\([0-9]\)\.\([0-9]\)/10:3:\1:0:0:0:0:\2/g' \ +-e 's/0\.0\.0\.0/any/g' \ +-e 's/ 0\/0 / any /g' \ +-e 's/ip #0/ip6\/0/' \ +-e 's/40(20) 6 /20 0 6 /' \ +-e 's/28(20) 17 /8 0 17 /' \ +-e 's/20(20) 0 /1 0 41 /' \ +-e 's/48(20) 1 /88 0 58 /g' \ +-e 's/20(20) 34 /1 0 34 /g' \ +-e 's/20(20) 35 /1 0 35 /g' \ +-e 's/20(20) 255 /1 0 255 /g' \ +-e 's/ */ /g' | sed \ +-e '/use/s/:0:0:0:0:/::/g' \ +-e '/map/s/:0:0:0:0:/::/g' \ +-e '/rdr/s/:0:0:0:0:/::/g' \ +-e '/map/s/:0:0:0:/::/g' \ +-e '/rdr/s/:0:0:0:/::/g' \ +-e '/MAP/s/:0:0:0:0:0:/::/g' \ +-e '/RDR/s/:0:0:0:0:0:/::/g' \ +-e '/MAP/s/:0:0:0:0:/::/g' \ +-e '/RDR/s/:0:0:0:0:/::/g' \ +-e '/MAP/s/:0:0:0:/::/g' \ +-e '/RDR/s/:0:0:0:/::/g' \ +| sed \ +-e '/MAP/s/ \([0-9][0-9][0-9][0-9]\) / \1 /g' \ +-e '/MAP/s/ \([0-9][0-9][0-9]\) / \1 /g' \ +-e '/MAP/s/ \([0-9][0-9]\) / \1 /g' \ +-e '/RDR/s/ \([0-9][0-9][0-9][0-9]\) / \1 /g' \ +-e '/RDR/s/ \([0-9][0-9][0-9]\) / \1 /g' \ +-e '/RDR/s/ \([0-9][0-9]\) / \1 /g' \ +-e 's/::0:0\//::\//g' \ +-e 's/:0:0\//::\//g' \ +-e 's/::0\([^:0-9]\)/::\1/g' \ +-e 's/::0,/::,/g' \ +-e 's/::0:0 \([^>]\)/:: \1/g' \ +-e 's/:0:0 \([^>]\)/:: \1/g' \ +-e 's/::0 \([^>]\)/:: \1/g' \ +| sed \ +-e 's@::\([0-9]*\)::/16@::/16@g' \ +-e 's@::\([0-9]*\)::/32@::/32@g' \ +-e 's@::\([0-9]*\)::@::\1:0:0@g' \ +-e 's@::\([0-9]*\)::@::\1:0:0@g' \ +-e 's@::[:0-9]*\([^0-9:]\)/16@::/16@g' \ +-e 's@::[:0-9]*\([^0-9:]\)/32@::/32@g' diff --git a/contrib/ipfilter/test/expected/f11 b/contrib/ipfilter/test/expected/f11 index c1eb060a712..d7ab889dacd 100644 --- a/contrib/ipfilter/test/expected/f11 +++ b/contrib/ipfilter/test/expected/f11 @@ -24,6 +24,15 @@ List of configured pools List of configured hash tables List of groups configured (set 0) List of groups configured (set 1) +Rules configured (set 0, in) +1 pass in proto tcp from any to any port = 23 flags S/SA keep state +Rules configured (set 0, out) +Rules configured (set 1, in) +Rules configured (set 1, out) +Accounting rules configured (set 0, in) +Accounting rules configured (set 0, out) +Accounting rules configured (set 1, in) +Accounting rules configured (set 1, out) -------- block nomatch @@ -51,6 +60,15 @@ List of configured pools List of configured hash tables List of groups configured (set 0) List of groups configured (set 1) +Rules configured (set 0, in) +1 block in proto tcp from any to any port = 23 flags S/SA keep state +Rules configured (set 0, out) +Rules configured (set 1, in) +Rules configured (set 1, out) +Accounting rules configured (set 0, in) +Accounting rules configured (set 0, out) +Accounting rules configured (set 1, in) +Accounting rules configured (set 1, out) -------- nomatch nomatch @@ -78,6 +96,15 @@ List of configured pools List of configured hash tables List of groups configured (set 0) List of groups configured (set 1) +Rules configured (set 0, in) +2 pass in proto udp from any to any port = 53 keep frags +Rules configured (set 0, out) +Rules configured (set 1, in) +Rules configured (set 1, out) +Accounting rules configured (set 0, in) +Accounting rules configured (set 0, out) +Accounting rules configured (set 1, in) +Accounting rules configured (set 1, out) -------- nomatch nomatch @@ -105,6 +132,15 @@ List of configured pools List of configured hash tables List of groups configured (set 0) List of groups configured (set 1) +Rules configured (set 0, in) +2 block in proto udp from any to any port = 53 keep frags +Rules configured (set 0, out) +Rules configured (set 1, in) +Rules configured (set 1, out) +Accounting rules configured (set 0, in) +Accounting rules configured (set 0, out) +Accounting rules configured (set 1, in) +Accounting rules configured (set 1, out) -------- nomatch nomatch @@ -128,30 +164,31 @@ List of active sessions: Hostmap table: List of active state sessions: -2.2.2.2 -> 4.4.4.4 pass 0x40008402 pr 17 state 0/0 - tag 0 ttl 240 2 -> 53 - forward: pkts in 1 bytes in 28 pkts out 0 bytes out 0 - backward: pkts in 0 bytes in 0 pkts out 0 bytes out 0 - pass in keep state IPv4 - pkt_flags & 0(0) = 0, pkt_options & ffffffff = 0, ffffffff = 0 - pkt_security & ffff = 0, pkt_auth & ffff = 0 - is_flx 0x8001 0 0 0 +4:udp src:2.2.2.2,2 dst:4.4.4.4,53 240 + FWD: IN pkts 1 bytes 28 OUT pkts 0 bytes 0 + REV: IN pkts 0 bytes 0 OUT pkts 0 bytes 0 + tag 0 pass 0x2008402 = pass in keep state interfaces: in X[e1],X[] out X[],X[] Sync status: not synchronized -1.1.1.1 -> 4.4.4.4 pass 0x40008402 pr 17 state 0/0 - tag 0 ttl 24 1 -> 53 - forward: pkts in 1 bytes in 28 pkts out 0 bytes out 0 - backward: pkts in 1 bytes in 28 pkts out 0 bytes out 0 - pass in keep state IPv4 - pkt_flags & 0(0) = 0, pkt_options & ffffffff = 0, ffffffff = 0 - pkt_security & ffff = 0, pkt_auth & ffff = 0 - is_flx 0x8001 0x8001 0 0 +4:udp src:1.1.1.1,1 dst:4.4.4.4,53 24 + FWD: IN pkts 1 bytes 28 OUT pkts 0 bytes 0 + REV: IN pkts 1 bytes 28 OUT pkts 0 bytes 0 + tag 0 pass 0x2008402 = pass in keep state interfaces: in X[e1],X[e0] out X[],X[] Sync status: not synchronized List of configured pools List of configured hash tables List of groups configured (set 0) List of groups configured (set 1) +Rules configured (set 0, in) +2 pass in proto udp from any to any port = 53 keep state +Rules configured (set 0, out) +Rules configured (set 1, in) +Rules configured (set 1, out) +Accounting rules configured (set 0, in) +Accounting rules configured (set 0, out) +Accounting rules configured (set 1, in) +Accounting rules configured (set 1, out) -------- nomatch nomatch @@ -175,30 +212,31 @@ List of active sessions: Hostmap table: List of active state sessions: -2.2.2.2 -> 4.4.4.4 pass 0x40008401 pr 17 state 0/0 - tag 0 ttl 240 2 -> 53 - forward: pkts in 1 bytes in 28 pkts out 0 bytes out 0 - backward: pkts in 0 bytes in 0 pkts out 0 bytes out 0 - block in keep state IPv4 - pkt_flags & 0(0) = 0, pkt_options & ffffffff = 0, ffffffff = 0 - pkt_security & ffff = 0, pkt_auth & ffff = 0 - is_flx 0x8001 0 0 0 +4:udp src:2.2.2.2,2 dst:4.4.4.4,53 240 + FWD: IN pkts 1 bytes 28 OUT pkts 0 bytes 0 + REV: IN pkts 0 bytes 0 OUT pkts 0 bytes 0 + tag 0 pass 0x2008401 = block in keep state interfaces: in X[e1],X[] out X[],X[] Sync status: not synchronized -1.1.1.1 -> 4.4.4.4 pass 0x40008401 pr 17 state 0/0 - tag 0 ttl 24 1 -> 53 - forward: pkts in 1 bytes in 28 pkts out 0 bytes out 0 - backward: pkts in 1 bytes in 28 pkts out 0 bytes out 0 - block in keep state IPv4 - pkt_flags & 0(0) = 0, pkt_options & ffffffff = 0, ffffffff = 0 - pkt_security & ffff = 0, pkt_auth & ffff = 0 - is_flx 0x8001 0x8001 0 0 +4:udp src:1.1.1.1,1 dst:4.4.4.4,53 24 + FWD: IN pkts 1 bytes 28 OUT pkts 0 bytes 0 + REV: IN pkts 1 bytes 28 OUT pkts 0 bytes 0 + tag 0 pass 0x2008401 = block in keep state interfaces: in X[e1],X[e0] out X[],X[] Sync status: not synchronized List of configured pools List of configured hash tables List of groups configured (set 0) List of groups configured (set 1) +Rules configured (set 0, in) +2 block in proto udp from any to any port = 53 keep state +Rules configured (set 0, out) +Rules configured (set 1, in) +Rules configured (set 1, out) +Accounting rules configured (set 0, in) +Accounting rules configured (set 0, out) +Accounting rules configured (set 1, in) +Accounting rules configured (set 1, out) -------- nomatch nomatch @@ -222,22 +260,24 @@ List of active sessions: Hostmap table: List of active state sessions: -1.1.1.1 -> 2.1.2.2 pass 0x40008402 pr 6 state 3/4 - tag 0 ttl 864000 - 1 -> 25 2:66 4096<<0:16384<<0 - cmsk 0000 smsk 0000 s0 00000000/00000000 - FWD:ISN inc 0 sumd 0 - REV:ISN inc 0 sumd 0 - forward: pkts in 1 bytes in 40 pkts out 0 bytes out 0 - backward: pkts in 1 bytes in 40 pkts out 0 bytes out 0 - pass in keep state IPv4 - pkt_flags & 0(0) = 0, pkt_options & ffffffff = 0, ffffffff = 0 - pkt_security & ffff = 0, pkt_auth & ffff = 0 - is_flx 0x8001 0x8001 0 0 +4:tcp src:1.1.1.1,1 dst:2.1.2.2,25 state:3/4 864000 + 2:66 4096<<0:16384<<0 + FWD: IN pkts 1 bytes 40 OUT pkts 0 bytes 0 + REV: IN pkts 1 bytes 40 OUT pkts 0 bytes 0 + tag 0 pass 0x2008402 = pass in keep state interfaces: in X[e0],X[e1] out X[],X[] Sync status: not synchronized List of configured pools List of configured hash tables List of groups configured (set 0) List of groups configured (set 1) +Rules configured (set 0, in) +1 pass in on e0 proto tcp from any to any port = 25 keep state +Rules configured (set 0, out) +Rules configured (set 1, in) +Rules configured (set 1, out) +Accounting rules configured (set 0, in) +Accounting rules configured (set 0, out) +Accounting rules configured (set 1, in) +Accounting rules configured (set 1, out) -------- diff --git a/contrib/ipfilter/test/expected/f13 b/contrib/ipfilter/test/expected/f13 index 99c05651cea..ac7947bd292 100644 --- a/contrib/ipfilter/test/expected/f13 +++ b/contrib/ipfilter/test/expected/f13 @@ -154,7 +154,27 @@ nomatch nomatch nomatch pass +block +block +pass +-------- +block +bad-packet +nomatch +pass +bad-packet +nomatch +nomatch +bad-packet +nomatch +bad-packet +nomatch +nomatch +nomatch nomatch nomatch pass +pass +pass +pass -------- diff --git a/contrib/ipfilter/test/expected/f18 b/contrib/ipfilter/test/expected/f18 index 801abd36942..1af5de53cd0 100644 --- a/contrib/ipfilter/test/expected/f18 +++ b/contrib/ipfilter/test/expected/f18 @@ -2,4 +2,26 @@ pass pass pass pass +List of active MAP/Redirect filters: + +List of active sessions: + +Hostmap table: +List of active state sessions: +List of configured pools +List of configured hash tables +List of groups configured (set 0) +List of groups configured (set 1) +Rules configured (set 0, in) +2 pass in inet from 1.1.1.1/32 to any +Rules configured (set 0, out) +2 pass out inet from 2.2.2.2/32 to any +Rules configured (set 1, in) +Rules configured (set 1, out) +Accounting rules configured (set 0, in) +1 count in inet from 1.1.1.1/32 to 3.3.3.3/32 +Accounting rules configured (set 0, out) +Accounting rules configured (set 1, in) +1 count out inet from 2.2.2.2/32 to 4.4.4.4/32 +Accounting rules configured (set 1, out) -------- diff --git a/contrib/ipfilter/test/expected/f21 b/contrib/ipfilter/test/expected/f21 new file mode 100644 index 00000000000..525dacaae72 --- /dev/null +++ b/contrib/ipfilter/test/expected/f21 @@ -0,0 +1,5 @@ +pass +pass +nomatch +nomatch +-------- diff --git a/contrib/ipfilter/test/expected/f22 b/contrib/ipfilter/test/expected/f22 new file mode 100644 index 00000000000..525dacaae72 --- /dev/null +++ b/contrib/ipfilter/test/expected/f22 @@ -0,0 +1,5 @@ +pass +pass +nomatch +nomatch +-------- diff --git a/contrib/ipfilter/test/expected/f25 b/contrib/ipfilter/test/expected/f25 new file mode 100644 index 00000000000..a87b084cdad --- /dev/null +++ b/contrib/ipfilter/test/expected/f25 @@ -0,0 +1,35 @@ +pass +pass +pass +List of active MAP/Redirect filters: + +List of active sessions: + +Hostmap table: +List of active state sessions: +4:udp src:192.168.1.235,8008 dst:239.255.255.250,1900 240 + FWD: IN pkts 1 bytes 129 OUT pkts 0 bytes 0 + REV: IN pkts 0 bytes 0 OUT pkts 0 bytes 0 + tag 0 pass 0x2008402 = pass in keep state + interfaces: in X[hme0],X[] out X[],X[] + Sync status: not synchronized +4:udp src:192.168.1.235,8008 dst:192.168.1.254,1900 24 + FWD: IN pkts 1 bytes 129 OUT pkts 0 bytes 0 + REV: IN pkts 0 bytes 0 OUT pkts 1 bytes 264 + tag 0 pass 0x2008402 = pass in keep state + interfaces: in X[hme0],X[] out X[],X[hme0] + Sync status: not synchronized +List of configured pools +List of configured hash tables +List of groups configured (set 0) +List of groups configured (set 1) +Rules configured (set 0, in) +2 pass in on hme0 proto udp from any to any with mcast keep state +Rules configured (set 0, out) +Rules configured (set 1, in) +Rules configured (set 1, out) +Accounting rules configured (set 0, in) +Accounting rules configured (set 0, out) +Accounting rules configured (set 1, in) +Accounting rules configured (set 1, out) +-------- diff --git a/contrib/ipfilter/test/expected/f26 b/contrib/ipfilter/test/expected/f26 new file mode 100644 index 00000000000..9e4d62b5173 --- /dev/null +++ b/contrib/ipfilter/test/expected/f26 @@ -0,0 +1,84 @@ +pass +pass +pass +pass +pass +pass +nomatch +pass +pass +nomatch +pass +pass +nomatch +-------- +pass +nomatch +nomatch +nomatch +pass +pass +nomatch +nomatch +nomatch +nomatch +nomatch +nomatch +nomatch +-------- +pass +nomatch +nomatch +nomatch +nomatch +nomatch +nomatch +nomatch +nomatch +nomatch +nomatch +nomatch +nomatch +-------- +pass +pass +pass +pass +pass +pass +nomatch +pass +pass +nomatch +pass +pass +nomatch +-------- +pass +nomatch +nomatch +nomatch +pass +pass +nomatch +nomatch +nomatch +nomatch +nomatch +nomatch +nomatch +-------- +pass +nomatch +nomatch +nomatch +nomatch +nomatch +nomatch +nomatch +nomatch +nomatch +nomatch +nomatch +nomatch +-------- diff --git a/contrib/ipfilter/test/expected/f27 b/contrib/ipfilter/test/expected/f27 new file mode 100644 index 00000000000..c62f588e8b9 --- /dev/null +++ b/contrib/ipfilter/test/expected/f27 @@ -0,0 +1,90 @@ +pass +pass +pass +pass +pass +pass +nomatch +pass +pass +nomatch +pass +pass +nomatch +nomatch +-------- +pass +nomatch +nomatch +nomatch +pass +pass +nomatch +nomatch +nomatch +nomatch +nomatch +nomatch +nomatch +nomatch +-------- +pass +nomatch +nomatch +nomatch +nomatch +nomatch +nomatch +nomatch +nomatch +nomatch +nomatch +nomatch +nomatch +nomatch +-------- +pass +pass +pass +pass +pass +pass +nomatch +pass +pass +nomatch +pass +pass +nomatch +nomatch +-------- +pass +nomatch +nomatch +nomatch +pass +pass +nomatch +nomatch +nomatch +nomatch +nomatch +nomatch +nomatch +nomatch +-------- +pass +nomatch +nomatch +nomatch +nomatch +nomatch +nomatch +nomatch +nomatch +nomatch +nomatch +nomatch +nomatch +pass +-------- diff --git a/contrib/ipfilter/test/expected/f28 b/contrib/ipfilter/test/expected/f28 new file mode 100644 index 00000000000..e5867e69010 --- /dev/null +++ b/contrib/ipfilter/test/expected/f28 @@ -0,0 +1,32 @@ +block +block +block +> nic0 ip #0 20(20) 0 4.4.3.1 > 4.2.3.2 +pass +> nic1 ip #0 20(20) 0 4.4.1.1 > 4.2.1.2 +pass +> nic2 ip #0 20(20) 0 4.4.2.1 > 4.2.2.2 +pass +> nic3 ip #0 20(20) 0 4.4.3.1 > 4.2.3.2 +pass +List of active MAP/Redirect filters: + +List of active sessions: + +Hostmap table: +List of active state sessions: +List of configured pools +List of configured hash tables +List of groups configured (set 0) +List of groups configured (set 1) +Rules configured (set 0, in) +7 block in all +4 pass in on nic0 to dstlist/spread inet from 4.4.0.0/16 to any +Rules configured (set 0, out) +Rules configured (set 1, in) +Rules configured (set 1, out) +Accounting rules configured (set 0, in) +Accounting rules configured (set 0, out) +Accounting rules configured (set 1, in) +Accounting rules configured (set 1, out) +------------------------------- diff --git a/contrib/ipfilter/test/expected/f29 b/contrib/ipfilter/test/expected/f29 new file mode 100644 index 00000000000..a650c1b4528 --- /dev/null +++ b/contrib/ipfilter/test/expected/f29 @@ -0,0 +1,64 @@ +block +block +block +> nic0 ip #0 28(20) 17 4.4.3.1,1000 > 4.2.3.2,2000 +pass +> nic0 ip #0 28(20) 17 4.4.3.1,1000 > 4.2.3.2,2000 +pass +> nic1 ip #0 28(20) 17 4.4.1.1,1001 > 4.2.1.2,2001 +pass +> nic1 ip #0 28(20) 17 4.4.1.1,1001 > 4.2.1.2,2001 +pass +> nic2 ip #0 28(20) 17 4.4.2.1,1002 > 4.2.2.2,2002 +pass +> nic2 ip #0 28(20) 17 4.4.2.1,1002 > 4.2.2.2,2002 +pass +> nic3 ip #0 28(20) 17 4.4.3.1,1003 > 4.2.3.2,2003 +pass +> nic3 ip #0 28(20) 17 4.4.3.1,1003 > 4.2.3.2,2003 +pass +List of active MAP/Redirect filters: + +List of active sessions: + +Hostmap table: +List of active state sessions: +4:udp src:4.4.3.1,1003 dst:4.2.3.2,2003 240 + FWD: IN pkts 2 bytes 56 OUT pkts 2 bytes 56 + REV: IN pkts 0 bytes 0 OUT pkts 0 bytes 0 + tag 0 pass 0x2008402 = pass in keep state + interfaces: in X[nic0],X[] out X[nic3],X[] + Sync status: not synchronized +4:udp src:4.4.2.1,1002 dst:4.2.2.2,2002 240 + FWD: IN pkts 2 bytes 56 OUT pkts 2 bytes 56 + REV: IN pkts 0 bytes 0 OUT pkts 0 bytes 0 + tag 0 pass 0x2008402 = pass in keep state + interfaces: in X[nic0],X[] out X[nic2],X[] + Sync status: not synchronized +4:udp src:4.4.1.1,1001 dst:4.2.1.2,2001 240 + FWD: IN pkts 2 bytes 56 OUT pkts 2 bytes 56 + REV: IN pkts 0 bytes 0 OUT pkts 0 bytes 0 + tag 0 pass 0x2008402 = pass in keep state + interfaces: in X[nic0],X[] out X[nic1],X[] + Sync status: not synchronized +4:udp src:4.4.3.1,1000 dst:4.2.3.2,2000 240 + FWD: IN pkts 2 bytes 56 OUT pkts 2 bytes 56 + REV: IN pkts 0 bytes 0 OUT pkts 0 bytes 0 + tag 0 pass 0x2008402 = pass in keep state + interfaces: in X[nic0],X[] out X[nic0],X[] + Sync status: not synchronized +List of configured pools +List of configured hash tables +List of groups configured (set 0) +List of groups configured (set 1) +Rules configured (set 0, in) +7 block in all +4 pass in on nic0 to dstlist/spread inet from 4.4.0.0/16 to any keep state +Rules configured (set 0, out) +Rules configured (set 1, in) +Rules configured (set 1, out) +Accounting rules configured (set 0, in) +Accounting rules configured (set 0, out) +Accounting rules configured (set 1, in) +Accounting rules configured (set 1, out) +------------------------------- diff --git a/contrib/ipfilter/test/expected/f30 b/contrib/ipfilter/test/expected/f30 new file mode 100644 index 00000000000..30b9d405d0a --- /dev/null +++ b/contrib/ipfilter/test/expected/f30 @@ -0,0 +1,68 @@ +nomatch +nomatch +nomatch +pass +pass +nomatch +nomatch +nomatch +nomatch +nomatch +nomatch +nomatch +nomatch +nomatch +nomatch +nomatch +-------- +pass +pass +nomatch +nomatch +nomatch +nomatch +nomatch +nomatch +nomatch +nomatch +nomatch +nomatch +nomatch +nomatch +nomatch +nomatch +-------- +nomatch +nomatch +nomatch +nomatch +nomatch +pass +nomatch +pass +nomatch +nomatch +nomatch +nomatch +nomatch +nomatch +nomatch +nomatch +-------- +nomatch +nomatch +nomatch +nomatch +nomatch +nomatch +nomatch +nomatch +nomatch +nomatch +pass +nomatch +nomatch +pass +nomatch +nomatch +-------- diff --git a/contrib/ipfilter/test/expected/i1 b/contrib/ipfilter/test/expected/i1 index 74d0f309b61..19ae393e58e 100644 --- a/contrib/ipfilter/test/expected/i1 +++ b/contrib/ipfilter/test/expected/i1 @@ -5,8 +5,8 @@ log body in all count in from any to any pass in from !any to any pps 10 block in from any to !any -pass in on ed0(!) from 127.0.0.1/32 to 127.0.0.1/32 -pass in on ed0(!),vx0(!) from 127.0.0.1/32 to 127.0.0.1/32 +pass in on ed0(!) inet from 127.0.0.1/32 to 127.0.0.1/32 +pass in on ed0(!),vx0(!) inet from 127.0.0.1/32 to 127.0.0.1/32 block in log first on lo0(!) from any to any pass in log body or-block quick from any to any block return-rst in quick on le0(!) proto tcp from any to any @@ -14,4 +14,4 @@ block return-icmp in on qe0(!) from any to any block return-icmp(host-unr) in on qe0(!) from any to any block return-icmp-as-dest in on le0(!) from any to any block return-icmp-as-dest(port-unr) in on qe0(!) from any to any -pass out on longNICname0(!) from 254.220.186.152/32 to 254.220.186.152/32 +pass out on longNICname0(!) inet from 254.220.186.152/32 to 254.220.186.152/32 diff --git a/contrib/ipfilter/test/expected/i10 b/contrib/ipfilter/test/expected/i10 index 9e0a5d5ab8b..24137c1e29c 100644 --- a/contrib/ipfilter/test/expected/i10 +++ b/contrib/ipfilter/test/expected/i10 @@ -1,5 +1,5 @@ -pass in from 127.0.0.1/32 to 127.0.0.1/32 with opt sec -pass in from 127.0.0.1/32 to 127.0.0.1/32 with opt lsrr not opt sec -block in from any to any with not opt sec-class topsecret -block in from any to any with not opt sec-class topsecret,secret -pass in from any to any with opt sec-class topsecret,confid not opt sec-class unclass +pass in inet from 127.0.0.1/32 to 127.0.0.1/32 with opt sec +pass in inet from 127.0.0.1/32 to 127.0.0.1/32 with opt lsrr not opt sec +block in inet from any to any with not opt sec-class topsecret +block in inet from any to any with not opt sec-class topsecret,secret +pass in inet from any to any with opt sec-class topsecret,confid not opt sec-class unclass diff --git a/contrib/ipfilter/test/expected/i11 b/contrib/ipfilter/test/expected/i11 index 154f31e810b..d1d2cf6c574 100644 --- a/contrib/ipfilter/test/expected/i11 +++ b/contrib/ipfilter/test/expected/i11 @@ -1,11 +1,12 @@ -pass in on ed0(!) proto tcp from 127.0.0.1/32 to 127.0.0.1/32 port = 23 keep state # count 0 +pass in on ed0(!) inet proto tcp from 127.0.0.1/32 to 127.0.0.1/32 port = 23 keep state # count 0 block in log first on lo0(!) proto tcp/udp from any to any port = 7 keep state # count 0 -pass in proto udp from 127.0.0.1/32 to 127.0.0.1/32 port = 20499 keep frags -pass in proto udp from 127.0.0.1/32 to 127.0.0.1/32 port = 2049 keep frags (strict) -pass in proto udp from 127.0.0.1/32 to 127.0.0.1/32 port = 53 keep state keep frags # count 0 +pass in inet proto udp from 127.0.0.1/32 to 127.0.0.1/32 port = 20499 keep frags +pass in inet proto udp from 127.0.0.1/32 to 127.0.0.1/32 port = 2049 keep frags (strict) +pass in inet proto udp from 127.0.0.1/32 to 127.0.0.1/32 port = 53 keep state keep frags # count 0 pass in on ed0(!) out-via vx0(!) proto udp from any to any keep state # count 0 pass out on ppp0(!) in-via le0(!) proto tcp from any to any keep state # count 0 pass in on ed0(!),vx0(!) out-via vx0(!),ed0(!) proto udp from any to any keep state # count 0 -pass in proto tcp from any port > 1024 to 127.0.0.1/32 port = 1024 keep state # count 0 +pass in inet proto tcp from any port > 1024 to 127.0.0.1/32 port = 1024 keep state # count 0 pass in proto tcp from any to any flags S/FSRPAU keep state (limit 101,strict,newisn,no-icmp-err,age 600/600) # count 0 +pass in proto tcp from any to any flags S/FSRPAU keep state (limit 101,loose,newisn,no-icmp-err,age 600/600) # count 0 pass in proto udp from any to any keep state (sync,age 10/20) # count 0 diff --git a/contrib/ipfilter/test/expected/i12 b/contrib/ipfilter/test/expected/i12 index dadf597fc3d..6747d933890 100644 --- a/contrib/ipfilter/test/expected/i12 +++ b/contrib/ipfilter/test/expected/i12 @@ -1,39 +1,39 @@ -pass in from 1.1.1.1/32 to 2.2.2.2/32 -pass in from 2.2.2.0/24 to 4.4.4.4/32 -pass in from 3.3.3.3/32 to 4.4.4.4/32 -pass in from 2.2.2.0/24 to 5.5.5.5/32 -pass in from 3.3.3.3/32 to 5.5.5.5/32 -pass in from 2.2.2.0/24 to 6.6.6.6/32 -pass in from 3.3.3.3/32 to 6.6.6.6/32 -pass in from 2.2.2.0/24 to 5.5.5.5/32 port = 22 -pass in from 3.3.3.3/32 to 5.5.5.5/32 port = 22 -pass in from 2.2.2.0/24 to 6.6.6.6/32 port = 22 -pass in from 3.3.3.3/32 to 6.6.6.6/32 port = 22 -pass in from 2.2.2.0/24 to 5.5.5.5/32 port = 25 -pass in from 3.3.3.3/32 to 5.5.5.5/32 port = 25 -pass in from 2.2.2.0/24 to 6.6.6.6/32 port = 25 -pass in from 3.3.3.3/32 to 6.6.6.6/32 port = 25 -pass in proto tcp from 2.2.2.0/24 port = 53 to 5.5.5.5/32 -pass in proto tcp from 3.3.3.3/32 port = 53 to 5.5.5.5/32 -pass in proto tcp from 2.2.2.0/24 port = 9 to 5.5.5.5/32 -pass in proto tcp from 3.3.3.3/32 port = 9 to 5.5.5.5/32 -pass in proto tcp from 2.2.2.0/24 port = 53 to 6.6.6.6/32 -pass in proto tcp from 3.3.3.3/32 port = 53 to 6.6.6.6/32 -pass in proto tcp from 2.2.2.0/24 port = 9 to 6.6.6.6/32 -pass in proto tcp from 3.3.3.3/32 port = 9 to 6.6.6.6/32 -pass in proto udp from 2.2.2.0/24 to 5.5.5.5/32 port = 53 -pass in proto udp from 3.3.3.3/32 to 5.5.5.5/32 port = 53 -pass in proto udp from 2.2.2.0/24 to 6.6.6.6/32 port = 53 -pass in proto udp from 3.3.3.3/32 to 6.6.6.6/32 port = 53 -pass in proto udp from 2.2.2.0/24 to 5.5.5.5/32 port = 9 -pass in proto udp from 3.3.3.3/32 to 5.5.5.5/32 port = 9 -pass in proto udp from 2.2.2.0/24 to 6.6.6.6/32 port = 9 -pass in proto udp from 3.3.3.3/32 to 6.6.6.6/32 port = 9 -pass in from 10.10.10.10/32 to 11.11.11.11/32 -pass in from pool/101(!) to hash/202(!) -pass in from hash/303(!) to pool/404(!) -table role = ipf type = tree name = - { ! 1.1.1.1/32; 2.2.2.2/32; ! 2.2.0.0/16; }; -table role = ipf type = tree name = +pass in inet from 1.1.1.1/32 to 2.2.2.2/32 +pass in inet from 2.2.2.0/24 to 4.4.4.4/32 +pass in inet from 3.3.3.3/32 to 4.4.4.4/32 +pass in inet from 2.2.2.0/24 to 5.5.5.5/32 +pass in inet from 3.3.3.3/32 to 5.5.5.5/32 +pass in inet from 2.2.2.0/24 to 6.6.6.6/32 +pass in inet from 3.3.3.3/32 to 6.6.6.6/32 +pass in inet from 2.2.2.0/24 to 5.5.5.5/32 port = 22 +pass in inet from 3.3.3.3/32 to 5.5.5.5/32 port = 22 +pass in inet from 2.2.2.0/24 to 6.6.6.6/32 port = 22 +pass in inet from 3.3.3.3/32 to 6.6.6.6/32 port = 22 +pass in inet from 2.2.2.0/24 to 5.5.5.5/32 port = 25 +pass in inet from 3.3.3.3/32 to 5.5.5.5/32 port = 25 +pass in inet from 2.2.2.0/24 to 6.6.6.6/32 port = 25 +pass in inet from 3.3.3.3/32 to 6.6.6.6/32 port = 25 +pass in inet proto tcp from 2.2.2.0/24 port = 53 to 5.5.5.5/32 +pass in inet proto tcp from 3.3.3.3/32 port = 53 to 5.5.5.5/32 +pass in inet proto tcp from 2.2.2.0/24 port = 9 to 5.5.5.5/32 +pass in inet proto tcp from 3.3.3.3/32 port = 9 to 5.5.5.5/32 +pass in inet proto tcp from 2.2.2.0/24 port = 53 to 6.6.6.6/32 +pass in inet proto tcp from 3.3.3.3/32 port = 53 to 6.6.6.6/32 +pass in inet proto tcp from 2.2.2.0/24 port = 9 to 6.6.6.6/32 +pass in inet proto tcp from 3.3.3.3/32 port = 9 to 6.6.6.6/32 +pass in inet proto udp from 2.2.2.0/24 to 5.5.5.5/32 port = 53 +pass in inet proto udp from 3.3.3.3/32 to 5.5.5.5/32 port = 53 +pass in inet proto udp from 2.2.2.0/24 to 6.6.6.6/32 port = 53 +pass in inet proto udp from 3.3.3.3/32 to 6.6.6.6/32 port = 53 +pass in inet proto udp from 2.2.2.0/24 to 5.5.5.5/32 port = 9 +pass in inet proto udp from 3.3.3.3/32 to 5.5.5.5/32 port = 9 +pass in inet proto udp from 2.2.2.0/24 to 6.6.6.6/32 port = 9 +pass in inet proto udp from 3.3.3.3/32 to 6.6.6.6/32 port = 9 +pass in inet from 10.10.10.10/32 to 11.11.11.11/32 +pass in from pool/101 to hash/202 +pass in from hash/303 to pool/404 +table role=ipf type=tree number= + { ! 2.2.0.0/16; 2.2.2.2/32; ! 1.1.1.1/32; }; +table role=ipf type=tree number= { 1.1.0.0/16; }; -pass in from pool/0(!) to pool/0(!) +pass in from pool/0 to pool/0 diff --git a/contrib/ipfilter/test/expected/i14 b/contrib/ipfilter/test/expected/i14 index 08ba19ad558..bccdcac6dee 100644 --- a/contrib/ipfilter/test/expected/i14 +++ b/contrib/ipfilter/test/expected/i14 @@ -3,8 +3,10 @@ pass in on eri0(!) proto icmp from any to any group 1 pass out on ed0(!) all head 1000000 block out on ed0(!) proto udp from any to any group 1000000 block in on vm0(!) proto tcp/udp from any to any head 101 -pass in proto tcp/udp from 1.1.1.1/32 to 2.2.2.2/32 group 101 -pass in proto tcp from 1.0.0.1/32 to 2.0.0.2/32 group 101 -pass in proto udp from 2.0.0.2/32 to 3.0.0.3/32 group 101 +pass in inet proto tcp/udp from 1.1.1.1/32 to 2.2.2.2/32 group 101 +pass in inet proto tcp from 1.0.0.1/32 to 2.0.0.2/32 group 101 +pass in inet proto udp from 2.0.0.2/32 to 3.0.0.3/32 group 101 block in on vm0(!) proto tcp/udp from any to any head vm0-group -pass in proto tcp/udp from 1.1.1.1/32 to 2.2.2.2/32 group vm0-group +pass in inet proto tcp/udp from 1.1.1.1/32 to 2.2.2.2/32 group vm0-group +block in on vm0(!) proto tcp/udp from any to any head vm0-group +pass in inet proto tcp/udp from 1.1.1.1/32 to 2.2.2.2/32 group vm0-group diff --git a/contrib/ipfilter/test/expected/i17 b/contrib/ipfilter/test/expected/i17 index bcc4d2d544a..9e71cb10032 100644 --- a/contrib/ipfilter/test/expected/i17 +++ b/contrib/ipfilter/test/expected/i17 @@ -8,3 +8,22 @@ List of configured pools List of configured hash tables List of groups configured (set 0) List of groups configured (set 1) +Rules configured (set 0, in) +0 pass in inet from 1.1.1.1/32 to any +0 pass in all +0 pass in inet from 3.3.3.3/32 to any +0 pass in inet from any to 127.0.0.1/32 +0 pass in inet from 127.0.0.1/32 to any +0 100 pass in inet from 127.0.0.1/32 to any +0 100 pass in all +0 110 pass in proto udp from any to any +0 110 pass in inet from 2.2.2.2/32 to any +0 110 pass in inet from 127.0.0.1/32 to any +0 200 pass in proto tcp from any to any +Rules configured (set 0, out) +Rules configured (set 1, in) +Rules configured (set 1, out) +Accounting rules configured (set 0, in) +Accounting rules configured (set 0, out) +Accounting rules configured (set 1, in) +Accounting rules configured (set 1, out) diff --git a/contrib/ipfilter/test/expected/i18 b/contrib/ipfilter/test/expected/i18 index 88fca4744c4..2c7e4935d52 100644 --- a/contrib/ipfilter/test/expected/i18 +++ b/contrib/ipfilter/test/expected/i18 @@ -1,7 +1,7 @@ pass in tos 0x50 from any to any pass in tos 0x80 from any to any -pass in tos 0x80 from any to any -pass in tos 0x50 from any to any +pass out tos 0x80 from any to any +pass out tos 0x50 from any to any block in ttl 0 from any to any block in ttl 1 from any to any block in ttl 2 from any to any diff --git a/contrib/ipfilter/test/expected/i2 b/contrib/ipfilter/test/expected/i2 index 5ff18f4f924..17b9d07883f 100644 --- a/contrib/ipfilter/test/expected/i2 +++ b/contrib/ipfilter/test/expected/i2 @@ -1,8 +1,9 @@ log in proto tcp from any to any pass in proto tcp from any to any -pass in proto udp from 127.0.0.1/32 to 127.0.0.1/32 +pass in inet proto udp from 127.0.0.1/32 to 127.0.0.1/32 block in proto ipv6 from any to any block in proto udp from any to any block in proto 250 from any to any pass in proto tcp/udp from any to any block in proto tcp/udp from any to any +block in proto tcp/udp from any to any diff --git a/contrib/ipfilter/test/expected/i20 b/contrib/ipfilter/test/expected/i20 index 77eabdb55f0..25e35cd9f5c 100644 --- a/contrib/ipfilter/test/expected/i20 +++ b/contrib/ipfilter/test/expected/i20 @@ -1,4 +1,4 @@ -pass in on ppp0(!) from ppp0/peer to ppp0/32 -block in on hme0(!) from any to hme0/bcast -pass in on bge0(!) from bge0/net to bge0/32 -block in on eri0(!) from any to eri0/netmasked +pass in on ppp0(!) inet from ppp0/peer to ppp0/32 +block in on hme0(!) inet from any to hme0/bcast +pass in on bge0(!) inet from bge0/net to bge0/32 +block in on eri0(!) inet from any to eri0/netmasked diff --git a/contrib/ipfilter/test/expected/i22 b/contrib/ipfilter/test/expected/i22 new file mode 100644 index 00000000000..6e5a07d634b --- /dev/null +++ b/contrib/ipfilter/test/expected/i22 @@ -0,0 +1,5 @@ +pass in exp { "ip.src != 1.1.1.0/24; tcp.dport = 80;" } +pass in exp { "ip.addr = 1.2.3.4/32,5.6.7.8/32;" } +block out exp { "ip.dst = 127.0.0.0/8;" } +block in exp { "udp.sport = 53; udp.dport = 53;" } +pass out exp { "tcp.sport = 22; tcp.port = 25;" } diff --git a/contrib/ipfilter/test/expected/i23 b/contrib/ipfilter/test/expected/i23 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/contrib/ipfilter/test/expected/i3 b/contrib/ipfilter/test/expected/i3 index 6150c7e55bd..691ad257f38 100644 --- a/contrib/ipfilter/test/expected/i3 +++ b/contrib/ipfilter/test/expected/i3 @@ -1,11 +1,11 @@ log in all -pass in from 128.16.0.0/16 to 129.10.10.0/24 -pass in from 128.0.0.0/24 to 128.0.0.0/16 -pass in from 128.0.0.0/24 to 128.0.0.0/16 -pass in from 128.0.0.0/24 to 128.0.0.0/16 -pass in from 128.0.0.0/24 to 128.0.0.0/16 -pass in from 128.0.0.0/24 to 128.0.0.0/16 -pass in from 127.0.0.1/32 to 127.0.0.1/32 -block in log from any to any +pass in inet from 128.16.0.0/16 to 129.10.10.0/24 +pass in inet from 128.0.0.0/24 to 128.0.0.0/16 +pass in inet from 128.0.0.0/24 to 128.0.0.0/16 +pass in inet from 128.0.0.0/24 to 128.0.0.0/16 +pass in inet from 128.0.0.0/24 to 128.0.0.0/16 +pass in inet from 128.0.0.0/24 to 128.0.0.0/16 +pass in inet from 127.0.0.1/32 to 127.0.0.1/32 +block in log inet from any to any block in log level auth.info on hme0(!) all log level local5.warn out all diff --git a/contrib/ipfilter/test/expected/i4 b/contrib/ipfilter/test/expected/i4 index 49924555a27..1198714fb55 100644 --- a/contrib/ipfilter/test/expected/i4 +++ b/contrib/ipfilter/test/expected/i4 @@ -1,7 +1,7 @@ log in proto tcp from any port > 0 to any log in proto tcp from any to any port > 0 pass in proto tcp from any port != 0 to any port 0 >< 65535 -pass in proto udp from 127.0.0.1/32 port > 32000 to 127.0.0.1/32 port < 29000 +pass in inet proto udp from 127.0.0.1/32 port > 32000 to 127.0.0.1/32 port < 29000 block in proto udp from any port != 123 to any port < 7 block in proto tcp from any port = 25 to any port > 25 pass in proto tcp/udp from any port 1 >< 3 to any port 1 <> 3 diff --git a/contrib/ipfilter/test/expected/i5 b/contrib/ipfilter/test/expected/i5 index edf986558f2..0dbc859b9a4 100644 --- a/contrib/ipfilter/test/expected/i5 +++ b/contrib/ipfilter/test/expected/i5 @@ -1,9 +1,9 @@ log in all count in tos 0x80 from any to any -pass in on ed0(!) tos 0x40 from 127.0.0.1/32 to 127.0.0.1/32 +pass in on ed0(!) inet tos 0x40 from 127.0.0.1/32 to 127.0.0.1/32 block in log on lo0(!) ttl 0 from any to any pass in quick ttl 1 from any to any -skip 3 out from 127.0.0.1/32 to any +skip 3 out inet from 127.0.0.1/32 to any auth out on foo0(!) proto tcp from any to any port = 80 preauth out on foo0(!) proto tcp from any to any port = 22 nomatch out on foo0(!) proto tcp from any port < 1024 to any diff --git a/contrib/ipfilter/test/expected/i6 b/contrib/ipfilter/test/expected/i6 index e4b14c328cb..29c33a265f3 100644 --- a/contrib/ipfilter/test/expected/i6 +++ b/contrib/ipfilter/test/expected/i6 @@ -1,12 +1,12 @@ pass in on lo0(!) fastroute from any to any -pass in on lo0(!) to qe0(!) from 127.0.0.1/32 to 127.0.0.1/32 -pass in on le0(!) to qe0(!):127.0.0.1 from 127.0.0.1/32 to 127.0.0.1/32 -pass in on lo0(!) dup-to qe0(!) from 127.0.0.1/32 to 127.0.0.1/32 -pass in on le0(!) dup-to qe0(!):127.0.0.1 from 127.0.0.1/32 to 127.0.0.1/32 -pass in on le0(!) dup-to qe0(!):127.0.0.1 to hme0(!):10.1.1.1 from 127.0.0.1/32 to 127.0.0.1/32 +pass in on lo0(!) to qe0(!) inet from 127.0.0.1/32 to 127.0.0.1/32 +pass in on le0(!) to qe0:127.0.0.1 inet from 127.0.0.1/32 to 127.0.0.1/32 +pass in on lo0(!) dup-to qe0(!) inet from 127.0.0.1/32 to 127.0.0.1/32 +pass in on le0(!) dup-to qe0:127.0.0.1 inet from 127.0.0.1/32 to 127.0.0.1/32 +pass in on le0(!) to hme0:10.1.1.1 dup-to qe0:127.0.0.1 inet from 127.0.0.1/32 to 127.0.0.1/32 block in quick on qe0(!) to qe1(!) from any to any block in quick to qe1(!) from any to any pass out quick dup-to hme0(!) from any to any pass out quick on hme0(!) reply-to hme1(!) from any to any -pass in on le0(!) dup-to qe0(!):127.0.0.1 reply-to hme1(!):10.10.10.10 all +pass in on le0(!) dup-to qe0:127.0.0.1 reply-to hme1:10.10.10.10 inet all pass in quick fastroute all diff --git a/contrib/ipfilter/test/expected/i7 b/contrib/ipfilter/test/expected/i7 index 309cd28691b..552f7f811ef 100644 --- a/contrib/ipfilter/test/expected/i7 +++ b/contrib/ipfilter/test/expected/i7 @@ -1,4 +1,4 @@ -pass in on ed0(!) proto tcp from 127.0.0.1/32 to 127.0.0.1/32 port = 23 flags S/SA +pass in on ed0(!) inet proto tcp from 127.0.0.1/32 to 127.0.0.1/32 port = 23 flags S/SA block in on lo0(!) proto tcp from any to any flags A/FSRPAU pass in on lo0(!) proto tcp from any to any flags /SPA block in on lo0(!) proto tcp from any to any flags C/A @@ -7,3 +7,8 @@ block in on lo0(!) proto tcp from any to any flags S/SA pass in on lo0(!) proto tcp from any to any flags S/FSRPAU block in on lo0(!) proto tcp from any to any flags /A pass in on lo0(!) proto tcp from any to any flags S/SA +pass in on lo0(!) proto tcp from any to any flags S/SA +block in on lo0(!) proto tcp from any to any flags S/SA +pass in on lo0(!) proto tcp from any to any flags S/FSRPAU +block in on lo0(!) proto tcp from any to any flags /A +pass in on lo0(!) proto tcp from any to any flags S/SA diff --git a/contrib/ipfilter/test/expected/i8 b/contrib/ipfilter/test/expected/i8 index f033e6b8d89..a85f1deb270 100644 --- a/contrib/ipfilter/test/expected/i8 +++ b/contrib/ipfilter/test/expected/i8 @@ -1,35 +1,66 @@ -pass in proto icmp from 127.0.0.1/32 to 127.0.0.1/32 icmp-type timest -block in proto icmp from any to any icmp-type unreach code 1 -pass in proto icmp from any to any icmp-type unreach code 15 -pass in proto icmp from any to any icmp-type unreach code 13 -pass in proto icmp from any to any icmp-type unreach code 8 -pass in proto icmp from any to any icmp-type unreach code 4 -pass in proto icmp from any to any icmp-type unreach code 9 -pass in proto icmp from any to any icmp-type unreach code 11 -pass in proto icmp from any to any icmp-type unreach code 14 -pass in proto icmp from any to any icmp-type unreach code 10 -pass in proto icmp from any to any icmp-type unreach code 12 -pass in proto icmp from any to any icmp-type unreach code 7 -pass in proto icmp from any to any icmp-type unreach code 1 -pass in proto icmp from any to any icmp-type unreach code 6 -pass in proto icmp from any to any icmp-type unreach code 0 -pass in proto icmp from any to any icmp-type unreach code 3 -pass in proto icmp from any to any icmp-type unreach code 2 -pass in proto icmp from any to any icmp-type unreach code 5 -pass in proto icmp from any to any icmp-type echo -pass in proto icmp from any to any icmp-type echorep -pass in proto icmp from any to any icmp-type inforeq -pass in proto icmp from any to any icmp-type inforep -pass in proto icmp from any to any icmp-type maskrep -pass in proto icmp from any to any icmp-type maskreq -pass in proto icmp from any to any icmp-type paramprob -pass in proto icmp from any to any icmp-type redir -pass in proto icmp from any to any icmp-type unreach -pass in proto icmp from any to any icmp-type routerad -pass in proto icmp from any to any icmp-type routersol -pass in proto icmp from any to any icmp-type squench -pass in proto icmp from any to any icmp-type timest -pass in proto icmp from any to any icmp-type timestrep -pass in proto icmp from any to any icmp-type timex -pass in proto icmp from any to any icmp-type 254 -pass in proto icmp from any to any icmp-type 253 code 254 +pass in inet proto icmp from 127.0.0.1/32 to 127.0.0.1/32 icmp-type timest +block in inet proto icmp from any to any icmp-type unreach code 1 +pass in inet proto icmp from any to any icmp-type unreach code 15 +pass in inet proto icmp from any to any icmp-type unreach code 13 +pass in inet proto icmp from any to any icmp-type unreach code 8 +pass in inet proto icmp from any to any icmp-type unreach code 4 +pass in inet proto icmp from any to any icmp-type unreach code 9 +pass in inet proto icmp from any to any icmp-type unreach code 11 +pass in inet proto icmp from any to any icmp-type unreach code 14 +pass in inet proto icmp from any to any icmp-type unreach code 10 +pass in inet proto icmp from any to any icmp-type unreach code 12 +pass in inet proto icmp from any to any icmp-type unreach code 7 +pass in inet proto icmp from any to any icmp-type unreach code 1 +pass in inet proto icmp from any to any icmp-type unreach code 6 +pass in inet proto icmp from any to any icmp-type unreach code 0 +pass in inet proto icmp from any to any icmp-type unreach code 3 +pass in inet proto icmp from any to any icmp-type unreach code 2 +pass in inet proto icmp from any to any icmp-type unreach code 5 +pass in inet proto icmp from any to any icmp-type echo +pass in inet proto icmp from any to any icmp-type echorep +pass in inet proto icmp from any to any icmp-type inforeq +pass in inet proto icmp from any to any icmp-type inforep +pass in inet proto icmp from any to any icmp-type maskrep +pass in inet proto icmp from any to any icmp-type maskreq +pass in inet proto icmp from any to any icmp-type paramprob +pass in inet proto icmp from any to any icmp-type redir +pass in inet proto icmp from any to any icmp-type unreach +pass in inet proto icmp from any to any icmp-type routerad +pass in inet proto icmp from any to any icmp-type routersol +pass in inet proto icmp from any to any icmp-type squench +pass in inet proto icmp from any to any icmp-type timest +pass in inet proto icmp from any to any icmp-type timestrep +pass in inet proto icmp from any to any icmp-type timex +pass in inet proto icmp from any to any icmp-type 254 +pass in inet proto icmp from any to any icmp-type 253 code 254 +pass in inet proto icmp from any to any icmp-type unreach code 15 +pass in inet proto icmp from any to any icmp-type unreach code 13 +pass in inet proto icmp from any to any icmp-type unreach code 8 +pass in inet proto icmp from any to any icmp-type unreach code 4 +pass in inet proto icmp from any to any icmp-type unreach code 9 +pass in inet proto icmp from any to any icmp-type unreach code 11 +pass in inet proto icmp from any to any icmp-type unreach code 14 +pass in inet proto icmp from any to any icmp-type unreach code 10 +pass in inet proto icmp from any to any icmp-type unreach code 12 +pass in inet proto icmp from any to any icmp-type unreach code 7 +pass in inet proto icmp from any to any icmp-type unreach code 1 +pass in inet proto icmp from any to any icmp-type unreach code 6 +pass in inet proto icmp from any to any icmp-type unreach code 0 +pass in inet proto icmp from any to any icmp-type unreach code 3 +pass in inet proto icmp from any to any icmp-type unreach code 2 +pass in inet proto icmp from any to any icmp-type unreach code 5 +pass in inet proto icmp from any to any icmp-type echo +pass in inet proto icmp from any to any icmp-type echorep +pass in inet proto icmp from any to any icmp-type inforeq +pass in inet proto icmp from any to any icmp-type inforep +pass in inet proto icmp from any to any icmp-type maskrep +pass in inet proto icmp from any to any icmp-type maskreq +pass in inet proto icmp from any to any icmp-type paramprob +pass in inet proto icmp from any to any icmp-type redir +pass in inet proto icmp from any to any icmp-type unreach +pass in inet proto icmp from any to any icmp-type routerad +pass in inet proto icmp from any to any icmp-type routersol +pass in inet proto icmp from any to any icmp-type squench +pass in inet proto icmp from any to any icmp-type timest +pass in inet proto icmp from any to any icmp-type timestrep +pass in inet proto icmp from any to any icmp-type timex diff --git a/contrib/ipfilter/test/expected/i9 b/contrib/ipfilter/test/expected/i9 index b128f99d57a..deecd17d3a1 100644 --- a/contrib/ipfilter/test/expected/i9 +++ b/contrib/ipfilter/test/expected/i9 @@ -1,9 +1,9 @@ -pass in from 127.0.0.1/32 to 127.0.0.1/32 with short,frag +pass in inet from 127.0.0.1/32 to 127.0.0.1/32 with short,frag block in from any to any with ipopts -pass in from any to any with opt nop,rr,zsu -pass in from any to any with opt nop,rr,zsu not opt lsrr,ssrr -pass in from 127.0.0.1/32 to 127.0.0.1/32 with not frag -pass in from 127.0.0.1/32 to 127.0.0.1/32 with frag,frag-body +pass in inet from any to any with opt nop,rr,zsu +pass in inet from any to any with opt nop,rr,zsu not opt lsrr,ssrr +pass in inet from 127.0.0.1/32 to 127.0.0.1/32 with not frag +pass in inet from 127.0.0.1/32 to 127.0.0.1/32 with frag,frag-body pass in proto tcp from any to any flags S/FSRPAU with not oow keep state # count 0 block in proto tcp from any to any with oow pass in proto tcp from any to any flags S/FSRPAU with not bad,bad-src,bad-nat @@ -14,4 +14,4 @@ pass in quick from any to any with not frag-body block in quick from any to any with not lowttl pass in from any to any with not ipopts,mbcast,not bcast,mcast,not state block in from any to any with not mbcast,bcast,not mcast,state -pass in from any to any with opt mtup,mtur,encode,ts,tr,sec,e-sec,cipso,satid,ssrr,addext,visa,imitd,eip,finn,dps,sdb,nsapa,rtralrt,ump +pass in inet from any to any with opt mtup,mtur,encode,ts,tr,sec,e-sec,cipso,satid,ssrr,addext,visa,imitd,eip,finn,dps,sdb,nsapa,rtralrt,ump diff --git a/contrib/ipfilter/test/expected/in1 b/contrib/ipfilter/test/expected/in1 index 03436b65a4d..2f1cf31df00 100644 --- a/contrib/ipfilter/test/expected/in1 +++ b/contrib/ipfilter/test/expected/in1 @@ -1,31 +1,31 @@ -map le0 0.0.0.0/0 -> 0.0.0.0/32 +map le0 0/0 -> 0/32 map le0 0.0.0.1/32 -> 0.0.0.1/32 -map le0 128.0.0.0/1 -> 0.0.0.0/0 +map le0 128.0.0.0/1 -> 0/0 map le0 10.0.0.0/8 -> 1.2.3.0/24 map le0 10.0.0.0/8 -> 1.2.3.0/24 map le0 10.0.0.0/8 -> 1.2.3.0/24 map le0 0.0.0.5/0.0.0.255 -> 1.2.3.0/24 map le0 192.168.0.0/16 -> range 203.1.1.23-203.1.3.45 -map ppp0 192.168.0.0/16 -> 0.0.0.0/32 portmap tcp 10000:19999 -map ppp0 192.168.0.0/16 -> 0.0.0.0/32 portmap udp 20000:29999 -map ppp0 192.168.0.0/16 -> 0.0.0.0/32 portmap tcp/udp 30000:39999 -map ppp0 192.168.0.0/16 -> 0.0.0.0/32 portmap tcp auto -map ppp0 192.168.0.0/16 -> 0.0.0.0/32 portmap udp auto -map ppp0 192.168.0.0/16 -> 0.0.0.0/32 portmap tcp/udp auto -map ppp0 192.168.0.0/16 -> 0.0.0.0/32 proxy port 21 ftp/tcp -map ppp0 192.168.0.0/16 -> 0.0.0.0/32 proxy port 1010 ftp/tcp -map le0 0.0.0.0/0 -> 0.0.0.0/32 frag +map ppp0 192.168.0.0/16 -> 0/32 portmap tcp 10000:19999 +map ppp0 192.168.0.0/16 -> 0/32 portmap udp 20000:29999 +map ppp0 192.168.0.0/16 -> 0/32 portmap tcp/udp 30000:39999 +map ppp0 192.168.0.0/16 -> 0/32 portmap tcp auto +map ppp0 192.168.0.0/16 -> 0/32 portmap udp auto +map ppp0 192.168.0.0/16 -> 0/32 portmap tcp/udp auto +map ppp0 192.168.0.0/16 -> 0/32 proxy port 21 ftp/tcp +map ppp0 192.168.0.0/16 -> 0/32 proxy port 1010 ftp/tcp +map le0 0/0 -> 0/32 frag map le0 192.168.0.0/16 -> range 203.1.1.23-203.1.3.45 frag -map ppp0 192.168.0.0/16 -> 0.0.0.0/32 portmap tcp 10000:19999 frag -map ppp0 192.168.0.0/16 -> 0.0.0.0/32 proxy port 21 ftp/tcp frag -map le0 0.0.0.0/0 -> 0.0.0.0/32 age 10/10 +map ppp0 192.168.0.0/16 -> 0/32 portmap tcp 10000:19999 frag +map ppp0 192.168.0.0/16 -> 0/32 proxy port 21 ftp/tcp frag +map le0 0/0 -> 0/32 age 10/10 map le0 192.168.0.0/16 -> range 203.1.1.23-203.1.3.45 age 10/20 -map ppp0 192.168.0.0/16 -> 0.0.0.0/32 portmap tcp 10000:19999 age 30/30 -map le0 0.0.0.0/0 -> 0.0.0.0/32 frag age 10/10 +map ppp0 192.168.0.0/16 -> 0/32 portmap tcp 10000:19999 age 30/30 +map le0 0/0 -> 0/32 frag age 10/10 map le0 192.168.0.0/16 -> range 203.1.1.23-203.1.3.45 frag age 10/20 -map ppp0 192.168.0.0/16 -> 0.0.0.0/32 portmap tcp 10000:19999 frag age 30/30 -map fxp0 from 192.168.0.0/18 to any port = 21 -> 1.2.3.4/32 proxy port 21 ftp/tcp -map thisisalonginte 0.0.0.0/0 -> 0.0.0.0/32 mssclamp 1452 tag freddyliveshere -map bar0 0.0.0.0/0 -> 0.0.0.0/32 icmpidmap icmp 1000:2000 -map ppp0,adsl0 0.0.0.0/0 -> 0.0.0.0/32 -map ppp0 from 192.168.0.0/16 to any port = 123 -> 0.0.0.0/32 age 30/1 udp +map ppp0 192.168.0.0/16 -> 0/32 portmap tcp 10000:19999 frag age 30/30 +map fxp0 from 192.168.0.0/18 to 0/0 port = 21 -> 1.2.3.4/32 proxy port 21 ftp/tcp +map thisisalonginte 0/0 -> 0/32 mssclamp 1452 tag freddyliveshere +map bar0 0/0 -> 0/32 icmpidmap icmp 1000:2000 +map ppp0,adsl0 0/0 -> 0/32 +map ppp0 from 192.168.0.0/16 to 0/0 port = 123 -> 0/32 age 30/1 udp diff --git a/contrib/ipfilter/test/expected/in100 b/contrib/ipfilter/test/expected/in100 new file mode 100644 index 00000000000..dcf30975441 --- /dev/null +++ b/contrib/ipfilter/test/expected/in100 @@ -0,0 +1,3 @@ +rewrite in on bge0 from 1.1.1.1/32 to 2.2.2.2/32 -> src 3.3.3.3/32 dst 4.4.4.4/32; +rewrite out on bge0 from 1.1.1.1/32 to 2.2.2.2/32 -> src 3.3.3.0/24 dst 4.4.4.4/32; +rewrite in on bge0 from 1.1.1.1/32 to 2.2.2.2/32 -> src 3.3.3.0/24 dst 4.4.4.0/24; diff --git a/contrib/ipfilter/test/expected/in101 b/contrib/ipfilter/test/expected/in101 new file mode 100644 index 00000000000..04e234c2f16 --- /dev/null +++ b/contrib/ipfilter/test/expected/in101 @@ -0,0 +1,4 @@ +rewrite in on bge0 proto icmp from 1.1.1.1/32 to 2.2.2.2/32 -> src 3.3.3.3/32 dst 4.4.4.4/32; +rewrite in on bge0 proto udp from 1.1.1.1/32 to 2.2.2.2/32 -> src 3.3.3.3/32 dst 4.4.4.4/32; +rewrite out on bge0 proto tcp from 1.1.1.1/32 to 2.2.2.2/32 -> src 3.3.3.0/24 dst 4.4.4.4/32; +rewrite in on bge0 proto tcp/udp from 1.1.1.1/32 to 2.2.2.2/32 -> src 3.3.3.0/24,20202 dst 4.4.4.0/24,10101; diff --git a/contrib/ipfilter/test/expected/in102 b/contrib/ipfilter/test/expected/in102 new file mode 100644 index 00000000000..0a1b612d2f7 --- /dev/null +++ b/contrib/ipfilter/test/expected/in102 @@ -0,0 +1,5 @@ +rewrite in on bge0 proto tcp from 0/0 to 0/0 -> src 0/0 dst dstlist/a; +rewrite in on bge0 proto tcp from 1.1.1.1/32 to 0/0 -> src 0/0 dst dstlist/bee; +rewrite in on bge0 proto tcp from 1.1.1.1/32 to 2.2.2.2/32 -> src 0/0 dst dstlist/cat; +rewrite in on bge0 proto tcp from pool/a to 2.2.2.2/32 -> src 0/0 dst dstlist/bat; +rewrite in on bge0 proto tcp from pool/a to pool/1 -> src 0/0 dst dstlist/ant; diff --git a/contrib/ipfilter/test/expected/in2 b/contrib/ipfilter/test/expected/in2 index f1239b12213..dc8f4ac5ba2 100644 --- a/contrib/ipfilter/test/expected/in2 +++ b/contrib/ipfilter/test/expected/in2 @@ -1,71 +1,71 @@ -rdr le0 9.8.7.6/32 port 0 -> 1.1.1.1 port 0 tcp -rdr le0 9.8.7.6/32 -> 1.1.1.1 255 -rdr le0 9.8.7.6/32 port 80 -> 1.1.1.1 port 80 tcp -rdr le0 9.8.7.6/32 -> 1.1.1.1 ip -rdr le0 9.0.0.0/8 -> 1.1.1.1 ip -rdr le0 9.8.0.0/16 -> 1.1.1.1 ip -rdr le0 9.8.7.6/32 port 80 -> 1.1.1.1 port 80 tcp -rdr le0 9.8.7.6/32 port 80 -> 0.0.0.0/0 port 80 tcp -rdr le0 9.8.7.6/32 port 80 -> 1.1.1.1 port 80 udp -rdr le0 9.8.7.6/32 port 80 -> 1.1.1.1 port 80 tcp/udp -rdr le0 9.8.7.6/32 -> 1.1.1.1 icmp +rdr le0 9.8.7.6/32 port 0 -> 1.1.1.1/32 port 0 tcp +rdr le0 9.8.7.6/32 -> 1.1.1.1/32 255 +rdr le0 9.8.7.6/32 port 80 -> 1.1.1.1/32 port 80 tcp +rdr le0 9.8.7.6/32 -> 1.1.1.1/32 ip +rdr le0 9.0.0.0/8 -> 1.1.1.1/32 ip +rdr le0 9.8.0.0/16 -> 1.1.1.1/32 ip +rdr le0 9.8.7.6/32 port 80 -> 1.1.1.1/32 port 80 tcp +rdr le0 9.8.7.6/32 port 80 -> 0/0 port 80 tcp +rdr le0 9.8.7.6/32 port 80 -> 1.1.1.1/32 port 80 udp +rdr le0 9.8.7.6/32 port 80 -> 1.1.1.1/32 port 80 tcp/udp +rdr le0 9.8.7.6/32 -> 1.1.1.1/32 icmp rdr le0 9.8.7.6/32 port 80 -> 1.1.1.1,1.1.1.2 port 80 tcp -rdr le0 9.8.7.6/32 port 80 -> 1.1.1.1 port 80 tcp round-robin +rdr le0 9.8.7.6/32 port 80 -> 1.1.1.1/32 port 80 tcp round-robin rdr le0 9.8.7.6/32 port 80 -> 1.1.1.1,1.1.1.2 port 80 tcp round-robin -rdr le0 9.8.7.6/32 -> 1.1.1.1 ip frag -rdr le0 9.8.7.6/32 -> 1.1.1.1 icmp frag +rdr le0 9.8.7.6/32 -> 1.1.1.1/32 ip frag +rdr le0 9.8.7.6/32 -> 1.1.1.1/32 icmp frag rdr le0 9.8.7.6/32 port 80 -> 1.1.1.1,1.1.1.2 port 80 tcp/udp frag -rdr le0 9.8.7.6/32 port 80 -> 1.1.1.1 port 80 tcp round-robin frag +rdr le0 9.8.7.6/32 port 80 -> 1.1.1.1/32 port 80 tcp round-robin frag rdr le0 9.8.7.6/32 port 80 -> 1.1.1.1,1.1.1.2 port 80 tcp round-robin frag -rdr le0 9.8.7.6/32 -> 1.1.1.1 ip frag age 10/10 -rdr le0 9.8.7.6/32 -> 1.1.1.1 ip frag age 10/20 -rdr le0 9.8.7.6/32 -> 1.1.1.1 icmp frag age 10/10 +rdr le0 9.8.7.6/32 -> 1.1.1.1/32 ip frag age 10/10 +rdr le0 9.8.7.6/32 -> 1.1.1.1/32 ip frag age 10/20 +rdr le0 9.8.7.6/32 -> 1.1.1.1/32 icmp frag age 10/10 rdr le0 9.8.7.6/32 port 80 -> 1.1.1.1,1.1.1.2 port 80 tcp frag age 20/20 -rdr le0 9.8.7.6/32 port 80 -> 1.1.1.1 port 80 tcp round-robin frag age 30/30 +rdr le0 9.8.7.6/32 port 80 -> 1.1.1.1/32 port 80 tcp round-robin frag age 30/30 rdr le0 9.8.7.6/32 port 80 -> 1.1.1.1,1.1.1.2 port 80 tcp round-robin frag age 40/40 -rdr le0 9.8.7.6/32 -> 1.1.1.1 ip -rdr le0 9.8.7.6/32 -> 1.1.1.1 ip frag -rdr le0 9.8.7.6/32 -> 1.1.1.1 icmp frag +rdr le0 9.8.7.6/32 -> 1.1.1.1/32 ip +rdr le0 9.8.7.6/32 -> 1.1.1.1/32 ip frag +rdr le0 9.8.7.6/32 -> 1.1.1.1/32 icmp frag rdr le0 9.8.7.6/32 port 80 -> 1.1.1.1,1.1.1.2 port 80 tcp frag sticky -rdr le0 9.8.7.6/32 port 80 -> 1.1.1.1 port 80 tcp round-robin frag sticky +rdr le0 9.8.7.6/32 port 80 -> 1.1.1.1/32 port 80 tcp round-robin frag sticky rdr le0 9.8.7.6/32 port 80 -> 1.1.1.1,1.1.1.2 port 80 tcp round-robin frag sticky -rdr le0 9.8.7.6/32 -> 1.1.1.1 ip frag age 10/10 -rdr le0 9.8.7.6/32 -> 1.1.1.1 ip frag age 10/20 -rdr le0 9.8.7.6/32 -> 1.1.1.1 icmp frag age 10/10 +rdr le0 9.8.7.6/32 -> 1.1.1.1/32 ip frag age 10/10 +rdr le0 9.8.7.6/32 -> 1.1.1.1/32 ip frag age 10/20 +rdr le0 9.8.7.6/32 -> 1.1.1.1/32 icmp frag age 10/10 rdr le0 9.8.7.6/32 port 80 -> 1.1.1.1,1.1.1.2 port 80 tcp frag age 20/20 sticky -rdr le0 9.8.7.6/32 port 80 -> 1.1.1.1 port 80 tcp round-robin frag age 30/30 sticky +rdr le0 9.8.7.6/32 port 80 -> 1.1.1.1/32 port 80 tcp round-robin frag age 30/30 sticky rdr le0 9.8.7.6/32 port 80 -> 1.1.1.1,1.1.1.2 port 80 tcp round-robin frag age 40/40 sticky -rdr le0 9.8.7.6/32 -> 1.1.1.1 ip mssclamp 1000 -rdr le0 9.8.7.6/32 -> 1.1.1.1 ip mssclamp 1000 -rdr le0 9.8.7.6/32 -> 1.1.1.1 ip frag mssclamp 1000 -rdr le0 9.8.7.6/32 -> 1.1.1.1 icmp frag mssclamp 1000 +rdr le0 9.8.7.6/32 -> 1.1.1.1/32 ip mssclamp 1000 +rdr le0 9.8.7.6/32 -> 1.1.1.1/32 ip mssclamp 1000 +rdr le0 9.8.7.6/32 -> 1.1.1.1/32 ip frag mssclamp 1000 +rdr le0 9.8.7.6/32 -> 1.1.1.1/32 icmp frag mssclamp 1000 rdr le0 9.8.7.6/32 port 80 -> 1.1.1.1,1.1.1.2 port 80 tcp frag sticky mssclamp 1000 -rdr le0 9.8.7.6/32 port 80 -> 1.1.1.1 port 80 tcp round-robin frag sticky mssclamp 1000 +rdr le0 9.8.7.6/32 port 80 -> 1.1.1.1/32 port 80 tcp round-robin frag sticky mssclamp 1000 rdr le0 9.8.7.6/32 port 80 -> 1.1.1.1,1.1.1.2 port 80 tcp round-robin frag sticky mssclamp 1000 -rdr le0 9.8.7.6/32 -> 1.1.1.1 ip frag age 10/10 mssclamp 1000 -rdr le0 9.8.7.6/32 -> 1.1.1.1 ip frag age 10/20 mssclamp 1000 -rdr le0 9.8.7.6/32 -> 1.1.1.1 icmp frag age 10/10 mssclamp 1000 +rdr le0 9.8.7.6/32 -> 1.1.1.1/32 ip frag age 10/10 mssclamp 1000 +rdr le0 9.8.7.6/32 -> 1.1.1.1/32 ip frag age 10/20 mssclamp 1000 +rdr le0 9.8.7.6/32 -> 1.1.1.1/32 icmp frag age 10/10 mssclamp 1000 rdr le0 9.8.7.6/32 port 80 -> 1.1.1.1,1.1.1.2 port 80 tcp frag age 20/20 sticky mssclamp 1000 -rdr le0 9.8.7.6/32 port 80 -> 1.1.1.1 port 80 tcp round-robin frag age 30/30 sticky mssclamp 1000 +rdr le0 9.8.7.6/32 port 80 -> 1.1.1.1/32 port 80 tcp round-robin frag age 30/30 sticky mssclamp 1000 rdr le0 9.8.7.6/32 port 80 -> 1.1.1.1,1.1.1.2 port 80 tcp round-robin frag age 40/40 sticky mssclamp 1000 -rdr le0 9.8.7.6/32 -> 1.1.1.1 ip tag nattagcacheline -rdr le0 9.8.7.6/32 -> 1.1.1.1 ip mssclamp 1000 tag nattagcacheline -rdr le0 9.8.7.6/32 -> 1.1.1.1 ip mssclamp 1000 tag nattagcacheline -rdr le0 9.8.7.6/32 -> 1.1.1.1 ip frag mssclamp 1000 tag nattagcacheline -rdr le0 9.8.7.6/32 -> 1.1.1.1 icmp frag mssclamp 1000 tag nattagcacheline +rdr le0 9.8.7.6/32 -> 1.1.1.1/32 ip tag nattagcacheline +rdr le0 9.8.7.6/32 -> 1.1.1.1/32 ip mssclamp 1000 tag nattagcacheline +rdr le0 9.8.7.6/32 -> 1.1.1.1/32 ip mssclamp 1000 tag nattagcacheline +rdr le0 9.8.7.6/32 -> 1.1.1.1/32 ip frag mssclamp 1000 tag nattagcacheline +rdr le0 9.8.7.6/32 -> 1.1.1.1/32 icmp frag mssclamp 1000 tag nattagcacheline rdr le0 9.8.7.6/32 port 80 -> 1.1.1.1,1.1.1.2 port 80 tcp frag sticky mssclamp 1000 tag nattagcacheline -rdr le0 9.8.7.6/32 port 80 -> 1.1.1.1 port 80 tcp round-robin frag sticky mssclamp 1000 tag nattagcacheline +rdr le0 9.8.7.6/32 port 80 -> 1.1.1.1/32 port 80 tcp round-robin frag sticky mssclamp 1000 tag nattagcacheline rdr le0 9.8.7.6/32 port 80 -> 1.1.1.1,1.1.1.2 port 80 tcp round-robin frag sticky mssclamp 1000 tag nattagcacheline -rdr le0 9.8.7.6/32 -> 1.1.1.1 ip frag age 10/10 mssclamp 1000 tag nattagcacheline -rdr le0 9.8.7.6/32 -> 1.1.1.1 ip frag age 10/20 mssclamp 1000 tag nattagcacheline -rdr le0 9.8.7.6/32 -> 1.1.1.1 icmp frag age 10/10 mssclamp 1000 tag nattagcacheline +rdr le0 9.8.7.6/32 -> 1.1.1.1/32 ip frag age 10/10 mssclamp 1000 tag nattagcacheline +rdr le0 9.8.7.6/32 -> 1.1.1.1/32 ip frag age 10/20 mssclamp 1000 tag nattagcacheline +rdr le0 9.8.7.6/32 -> 1.1.1.1/32 icmp frag age 10/10 mssclamp 1000 tag nattagcacheline rdr le0 9.8.7.6/32 port 80 -> 1.1.1.1,1.1.1.2 port 80 tcp frag age 20/20 sticky mssclamp 1000 tag nattagcacheline -rdr le0 9.8.7.6/32 port 80 -> 1.1.1.1 port 80 tcp round-robin frag age 30/30 sticky mssclamp 1000 tag nattagcacheline +rdr le0 9.8.7.6/32 port 80 -> 1.1.1.1/32 port 80 tcp round-robin frag age 30/30 sticky mssclamp 1000 tag nattagcacheline rdr le0 9.8.7.6/32 port 80 -> 1.1.1.1,1.1.1.2 port 80 tcp round-robin frag age 40/40 sticky mssclamp 1000 tag nattagcacheline -rdr ge0 9.8.7.6/32 port 21 -> 1.1.1.1 port 21 tcp proxy ftp -rdr ge0 9.8.7.6/32 port 21 -> 1.1.1.1 port 21 tcp proxy ftp -rdr le0 9.8.7.6/32 port 1000-2000 -> 1.1.1.1 port 5555 tcp -rdr le0 9.8.7.6/32 port 1000-2000 -> 1.1.1.1 port = 5555 tcp -rdr le0 0.0.0.0/0 -> 254.220.186.152 ip -rdr le0 0.0.0.0/0 -> 254.220.186.152,254.220.186.152 ip -rdr adsl0,ppp0 0.0.0.0/0 port 25 -> 127.0.0.1 port 25 tcp +rdr ge0 9.8.7.6/32 port 21 -> 1.1.1.1/32 port 21 tcp proxy ftp +rdr ge0 9.8.7.6/32 port 21 -> 1.1.1.1/32 port 21 tcp proxy ftp +rdr le0 9.8.7.6/32 port 1000-2000 -> 1.1.1.1/32 port 5555 tcp +rdr le0 9.8.7.6/32 port 1000-2000 -> 1.1.1.1/32 port = 5555 tcp +rdr le0 0/0 -> 254.220.186.152/32 ip +rdr le0 0/0 -> 254.220.186.152,254.220.186.152 ip +rdr adsl0,ppp0 9.8.7.6/32 port 1000-2000 -> 1.1.1.1/32 port 5555-7777 tcp diff --git a/contrib/ipfilter/test/expected/in3 b/contrib/ipfilter/test/expected/in3 index b8a85bf9f38..dac97c7d9cc 100644 --- a/contrib/ipfilter/test/expected/in3 +++ b/contrib/ipfilter/test/expected/in3 @@ -1,5 +1,5 @@ -bimap le0 0.0.0.0/0 -> 0.0.0.0/32 +bimap le0 0/0 -> 0/32 bimap le0 0.0.0.1/32 -> 0.0.0.1/32 -bimap le0 128.0.0.0/1 -> 0.0.0.0/0 +bimap le0 128.0.0.0/1 -> 0/0 bimap le0 10.0.0.0/8 -> 1.2.3.0/24 bimap le0 10.0.5.0/24 -> 1.2.3.0/24 diff --git a/contrib/ipfilter/test/expected/in5 b/contrib/ipfilter/test/expected/in5 index e77de714a90..b7c6ef5d412 100644 --- a/contrib/ipfilter/test/expected/in5 +++ b/contrib/ipfilter/test/expected/in5 @@ -1,24 +1,24 @@ -map le0 from 9.8.7.6/32 port > 1024 to any -> 1.1.1.1/32 portmap tcp 10000:20000 +map le0 from 9.8.7.6/32 port > 1024 to 0/0 -> 1.1.1.1/32 portmap tcp 10000:20000 map le0 from 9.8.7.6/32 port > 1024 ! to 1.2.3.4/32 -> 1.1.1.1/32 portmap tcp 10000:20000 -rdr le0 from any to 9.8.7.6/32 port = 0 -> 1.1.1.1 port 0 tcp -rdr le0 from any to 9.8.7.6/32 -> 1.1.1.1 ip -rdr le0 ! from 1.2.3.4/32 to 9.8.7.6/32 port = 8888 -> 1.1.1.1 port 888 tcp -rdr le0 from any to 9.8.7.6/32 -> 1.1.1.1 ip -rdr le0 from any to 9.8.7.6/32 port = 8888 -> 1.1.1.1 port 888 tcp -rdr le0 from any to 9.8.7.6/32 port = 8888 -> 1.1.1.1 port 888 udp -rdr le0 from any to 9.8.7.6/32 port = 8888 -> 1.1.1.1 port 888 tcp/udp -rdr le0 from any to 9.8.7.6/32 -> 1.1.1.1 icmp -rdr le0 from any to 9.8.7.6/32 port = 8888 -> 1.1.1.1,1.1.1.2 port 888 tcp -rdr le0 from any to 9.8.7.6/32 port = 8888 -> 1.1.1.1 port 888 tcp round-robin -rdr le0 from any to 9.8.7.6/32 port = 8888 -> 1.1.1.1,1.1.1.2 port 888 tcp round-robin -rdr le0 from any to 9.8.7.6/32 -> 1.1.1.1 ip frag -rdr le0 from any to 9.8.7.6/32 -> 1.1.1.1 icmp frag -rdr le0 from any to 9.8.7.6/32 port = 8888 -> 1.1.1.1,1.1.1.2 port 888 tcp frag -rdr le0 from any to 9.8.7.6/32 port = 8888 -> 1.1.1.1 port 888 tcp round-robin frag -rdr le0 from any to 9.8.7.6/32 port = 8888 -> 1.1.1.1,1.1.1.2 port 888 tcp round-robin frag -rdr le0 from any to 9.8.7.6/32 -> 1.1.1.1 ip frag age 10/10 -rdr le0 from any to 9.8.7.6/32 -> 1.1.1.1 ip frag age 10/20 -rdr le0 from any to 9.8.7.6/32 -> 1.1.1.1 icmp frag age 10/10 -rdr le0 from any to 9.8.7.6/32 port = 8888 -> 1.1.1.1,1.1.1.2 port 888 tcp frag age 20/20 -rdr le0 from any to 9.8.7.6/32 port = 8888 -> 1.1.1.1 port 888 tcp round-robin frag age 30/30 -rdr le0 from any to 9.8.7.6/32 port = 8888 -> 1.1.1.1,1.1.1.2 port 888 tcp round-robin frag age 40/40 +rdr le0 from 0/0 to 9.8.7.6/32 port = 0 -> 1.1.1.1/32 port 0 tcp +rdr le0 from 0/0 to 9.8.7.6/32 -> 1.1.1.1/32 ip +rdr le0 ! from 1.2.3.4/32 to 9.8.7.6/32 port = 8888 -> 1.1.1.1/32 port 888 tcp +rdr le0 from 0/0 to 9.8.7.6/32 -> 1.1.1.1/32 ip +rdr le0 from 0/0 to 9.8.7.6/32 port = 8888 -> 1.1.1.1/32 port 888 tcp +rdr le0 from 0/0 to 9.8.7.6/32 port = 8888 -> 1.1.1.1/32 port 888 udp +rdr le0 from 0/0 to 9.8.7.6/32 port = 8888 -> 1.1.1.1/32 port 888 tcp/udp +rdr le0 from 0/0 to 9.8.7.6/32 -> 1.1.1.1/32 icmp +rdr le0 from 0/0 to 9.8.7.6/32 port = 8888 -> 1.1.1.1,1.1.1.2 port 888 tcp +rdr le0 from 0/0 to 9.8.7.6/32 port = 8888 -> 1.1.1.1/32 port 888 tcp round-robin +rdr le0 from 0/0 to 9.8.7.6/32 port = 8888 -> 1.1.1.1,1.1.1.2 port 888 tcp round-robin +rdr le0 from 0/0 to 9.8.7.6/32 -> 1.1.1.1/32 ip frag +rdr le0 from 0/0 to 9.8.7.6/32 -> 1.1.1.1/32 icmp frag +rdr le0 from 0/0 to 9.8.7.6/32 port = 8888 -> 1.1.1.1,1.1.1.2 port 888 tcp frag +rdr le0 from 0/0 to 9.8.7.6/32 port = 8888 -> 1.1.1.1/32 port 888 tcp round-robin frag +rdr le0 from 0/0 to 9.8.7.6/32 port = 8888 -> 1.1.1.1,1.1.1.2 port 888 tcp round-robin frag +rdr le0 from 0/0 to 9.8.7.6/32 -> 1.1.1.1/32 ip frag age 10/10 +rdr le0 from 0/0 to 9.8.7.6/32 -> 1.1.1.1/32 ip frag age 10/20 +rdr le0 from 0/0 to 9.8.7.6/32 -> 1.1.1.1/32 icmp frag age 10/10 +rdr le0 from 0/0 to 9.8.7.6/32 port = 8888 -> 1.1.1.1,1.1.1.2 port 888 tcp frag age 20/20 +rdr le0 from 0/0 to 9.8.7.6/32 port = 8888 -> 1.1.1.1/32 port 888 tcp round-robin frag age 30/30 +rdr le0 from 0/0 to 9.8.7.6/32 port = 8888 -> 1.1.1.1,1.1.1.2 port 888 tcp round-robin frag age 40/40 diff --git a/contrib/ipfilter/test/expected/in6 b/contrib/ipfilter/test/expected/in6 index 05426e7a8dc..fefc0522c7d 100644 --- a/contrib/ipfilter/test/expected/in6 +++ b/contrib/ipfilter/test/expected/in6 @@ -1,8 +1,8 @@ -map foo0 from any port = 1 to any port != 0 -> 0.0.0.0/32 udp -map foo0 from any port = 1 to any port != 0 -> 0.0.0.0/32 udp -map foo0 from any port < 1 to any port > 0 -> 0.0.0.0/32 tcp -map foo0 from any port < 1 to any port > 0 -> 0.0.0.0/32 tcp -map foo0 from any port <= 1 to any port >= 0 -> 0.0.0.0/32 tcp/udp -map foo0 from any port <= 1 to any port >= 0 -> 0.0.0.0/32 tcp/udp -map foo0 from any port 1 >< 20 to any port 20 <> 40 -> 0.0.0.0/32 tcp/udp -map foo0 from any port 10:20 to any port 30:40 -> 0.0.0.0/32 tcp/udp +map foo0 from 0/0 port = 1 to 0/0 port != 0 -> 0/32 udp +map foo0 from 0/0 port = 1 to 0/0 port != 0 -> 0/32 udp +map foo0 from 0/0 port < 1 to 0/0 port > 0 -> 0/32 tcp +map foo0 from 0/0 port < 1 to 0/0 port > 0 -> 0/32 tcp +map foo0 from 0/0 port <= 1 to 0/0 port >= 0 -> 0/32 tcp/udp +map foo0 from 0/0 port <= 1 to 0/0 port >= 0 -> 0/32 tcp/udp +map foo0 from 0/0 port 1 >< 20 to 0/0 port 20 <> 40 -> 0/32 tcp/udp +map foo0 from 0/0 port 10:20 to 0/0 port 30:40 -> 0/32 tcp/udp diff --git a/contrib/ipfilter/test/expected/in7 b/contrib/ipfilter/test/expected/in7 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/contrib/ipfilter/test/expected/ip1 b/contrib/ipfilter/test/expected/ip1 index b04fa9d1ad6..cee78319386 100644 --- a/contrib/ipfilter/test/expected/ip1 +++ b/contrib/ipfilter/test/expected/ip1 @@ -1,68 +1,68 @@ -table role = ipf type = tree number = 1 +table role=ipf type=tree number=1 {; }; -table role = ipf type = tree number = 100 - { 2.2.2.0/24; ! 2.2.0.0/16; 1.2.3.4/32; }; -table role = ipf type = tree number = 110 - { 2.2.2.0/24; ! 2.2.0.0/16; 1.2.3.4/32; }; -table role = ipf type = tree number = 120 - { 2.2.2.0/24; ! 2.2.0.0/16; 1.2.3.4/32; }; -table role = ipf type = tree number = 130 - { 2.2.2.0/24; ! 2.2.0.0/16; 1.2.3.4/32; }; -table role = ipf type = hash number = 2 size = 1 +table role=ipf type=tree number=100 + { 1.2.3.4/32; ! 2.2.0.0/16; 2.2.2.0/24; }; +table role=nat type=tree number=110 + { 1.2.3.4/32; ! 2.2.0.0/16; 2.2.2.0/24; }; +table role=auth type=tree number=120 + { 1.2.3.4/32; ! 2.2.0.0/16; 2.2.2.0/24; }; +table role=count type=tree number=130 + { 1.2.3.4/32; ! 2.2.0.0/16; 2.2.2.0/24; }; +table role=ipf type=hash number=2 size=1 {; }; -table role = ipf type = hash number = 200 size = 5 +table role=ipf type=hash number=200 size=5 { 0/0; 1/32; 1.2.3.4/32; }; -table role = nat type = hash number = 210 size = 5 +table role=nat type=hash number=210 size=5 { 0/0; 2/32; 1.2.3.4/32; }; -table role = auth type = hash number = 220 size = 5 +table role=auth type=hash number=220 size=5 { 0/0; 3/32; 1.2.3.4/32; }; -table role = count type = hash number = 230 size = 5 +table role=count type=hash number=230 size=5 { 0/0; 4/32; 1.2.3.4/32; }; -table role = ipf type = hash number = 240 size = 5 seed = 101 +table role=ipf type=hash number=240 size=5 seed=101 { 0/0; 1/32; 1.2.3.4/32; }; -table role = nat type = hash number = 250 size = 5 seed = 101 +table role=nat type=hash number=250 size=5 seed=101 { 0/0; 2/32; 1.2.3.4/32; }; -table role = auth type = hash number = 260 size = 5 seed = 101 +table role=auth type=hash number=260 size=5 seed=101 { 0/0; 3/32; 1.2.3.4/32; }; -table role = count type = hash number = 270 size = 5 seed = 101 +table role=count type=hash number=270 size=5 seed=101 { 0/0; 4/32; 1.2.3.4/32; }; -table role = ipf type = hash number = 2000 size = 1001 +table role=ipf type=hash number=2000 size=1001 { 0/0; 1/32; 1.2.3.4/32; }; -table role = nat type = hash number = 2000 size = 1001 +table role=nat type=hash number=2000 size=1001 { 0/0; 2/32; 1.2.3.4/32; }; -table role = auth type = hash number = 2000 size = 1001 +table role=auth type=hash number=2000 size=1001 { 0/0; 3/32; 1.2.3.4/32; }; -table role = count type = hash number = 2000 size = 1001 +table role=count type=hash number=2000 size=1001 { 0/0; 4/32; 1.2.3.4/32; }; -table role = ipf type = hash number = 100 size = 1001 seed = 101 +table role=ipf type=hash number=100 size=1001 seed=101 { 0/0; 1/32; 1.2.3.4/32; }; -table role = nat type = hash number = 100 size = 1001 seed = 101 +table role=nat type=hash number=100 size=1001 seed=101 { 0/0; 2/32; 1.2.3.4/32; }; -table role = auth type = hash number = 100 size = 1001 seed = 101 +table role=auth type=hash number=100 size=1001 seed=101 { 0/0; 3/32; 1.2.3.4/32; }; -table role = count type = hash number = 100 size = 1001 seed = 101 +table role=count type=hash number=100 size=1001 seed=101 { 0/0; 4/32; 1.2.3.4/32; }; -group-map in role = ipf number = 300 size = 5 - { 0/0, group = 303; 5/32, group = 303; 1.2.3.4/32, group = 303; }; -group-map in role = nat number = 300 size = 5 - { 0/0, group = 303; 6/32, group = 303; 1.2.3.4/32, group = 303; }; -group-map in role = auth number = 300 size = 5 - { 0/0, group = 303; 7/32, group = 303; 1.2.3.4/32, group = 303; }; -group-map in role = count number = 300 size = 5 - { 0/0, group = 303; 8/32, group = 303; 1.2.3.4/32, group = 303; }; -group-map out role = ipf number = 400 size = 5 - { 0/0, group = 303; 5/32, group = 303; 1.2.3.4/32, group = 606; }; -group-map out role = nat number = 400 size = 5 - { 0/0, group = 303; 6/32, group = 303; 1.2.3.4/32, group = 606; }; -group-map out role = auth number = 400 size = 5 - { 0/0, group = 303; 7/32, group = 303; 1.2.3.4/32, group = 606; }; -group-map out role = count number = 400 size = 5 - { 0/0, group = 303; 8/32, group = 303; 1.2.3.4/32, group = 606; }; -group-map in role = ipf number = 500 size = 5 - { 0/0, group = 10; 5/32, group = 800; 1.2.3.4/32, group = 606; }; -group-map in role = nat number = 500 size = 5 - { 0/0, group = 10; 6/32, group = 800; 1.2.3.4/32, group = 606; }; -group-map in role = auth number = 500 size = 5 - { 0/0, group = 10; 7/32, group = 800; 1.2.3.4/32, group = 606; }; -group-map in role = count number = 500 size = 5 - { 0/0, group = 10; 8/32, group = 800; 1.2.3.4/32, group = 606; }; +group-map in role=ipf number=300 size=5 + { 0/0, group=303; 5/32, group=303; 1.2.3.4/32, group=303; }; +group-map in role=nat number=300 size=5 + { 0/0, group=303; 6/32, group=303; 1.2.3.4/32, group=303; }; +group-map in role=auth number=300 size=5 + { 0/0, group=303; 7/32, group=303; 1.2.3.4/32, group=303; }; +group-map in role=count number=300 size=5 + { 0/0, group=303; 8/32, group=303; 1.2.3.4/32, group=303; }; +group-map out role=ipf number=400 size=5 + { 0/0, group=303; 5/32, group=303; 1.2.3.4/32, group=606; }; +group-map out role=nat number=400 size=5 + { 0/0, group=303; 6/32, group=303; 1.2.3.4/32, group=606; }; +group-map out role=auth number=400 size=5 + { 0/0, group=303; 7/32, group=303; 1.2.3.4/32, group=606; }; +group-map out role=count number=400 size=5 + { 0/0, group=303; 8/32, group=303; 1.2.3.4/32, group=606; }; +group-map in role=ipf number=500 size=5 + { 0/0, group=10; 5/32, group=800; 1.2.3.4/32, group=606; }; +group-map in role=nat number=500 size=5 + { 0/0, group=10; 6/32, group=800; 1.2.3.4/32, group=606; }; +group-map in role=auth number=500 size=5 + { 0/0, group=10; 7/32, group=800; 1.2.3.4/32, group=606; }; +group-map in role=count number=500 size=5 + { 0/0, group=10; 8/32, group=800; 1.2.3.4/32, group=606; }; diff --git a/contrib/ipfilter/test/expected/ip2 b/contrib/ipfilter/test/expected/ip2 index 9b0ed2babae..3de3c471fc9 100644 --- a/contrib/ipfilter/test/expected/ip2 +++ b/contrib/ipfilter/test/expected/ip2 @@ -1,2 +1,2 @@ -table role = ipf type = tree name = letters - { 2.2.2.0/24; ! 2.2.0.0/16; 1.1.1.1/32; }; +table role=ipf type=tree name=letters + { 1.1.1.1/32; ! 2.2.0.0/16; 2.2.2.0/24; }; diff --git a/contrib/ipfilter/test/expected/ip3 b/contrib/ipfilter/test/expected/ip3 new file mode 100644 index 00000000000..48dd074a4c0 --- /dev/null +++ b/contrib/ipfilter/test/expected/ip3 @@ -0,0 +1,14 @@ +pool ipf/dstlist (name fred; policy round-robin;) + { 3.3.3.3; }; +pool ipf/dstlist (name jack; policy weighting connection;) + { 4.4.4.4; bge0:5.5.5.5; }; +pool ipf/dstlist (name jill; policy random;) + { 1.1.1.1; bge0:2.2.2.2; }; +table role=nat type=hash name=noproxy size=3 + { 1.1.1.1/32; 2.2.2.2/32; }; +table role=nat type=tree name=raw + { 1.1.1.1/32; 2.2.2.2/32; }; +pool all/dstlist (name jill; policy random;) + { 1.1.1.1; bge0:2.2.2.2; }; +table role=all type=hash name=noproxy size=3 + { 1.1.1.1/32; 2.2.2.2/32; }; diff --git a/contrib/ipfilter/test/expected/ipv6.4 b/contrib/ipfilter/test/expected/ipv6.4 new file mode 100644 index 00000000000..e3ae842a747 --- /dev/null +++ b/contrib/ipfilter/test/expected/ipv6.4 @@ -0,0 +1,51 @@ +pass +pass +nomatch +nomatch +pass +pass +nomatch +nomatch +nomatch +nomatch +nomatch +nomatch +nomatch +nomatch +nomatch +nomatch +-------- +pass +pass +pass +pass +pass +pass +pass +pass +nomatch +nomatch +nomatch +nomatch +nomatch +nomatch +nomatch +nomatch +-------- +nomatch +nomatch +nomatch +nomatch +nomatch +nomatch +nomatch +nomatch +pass +pass +block +nomatch +nomatch +nomatch +pass +pass +-------- diff --git a/contrib/ipfilter/test/expected/ipv6.6 b/contrib/ipfilter/test/expected/ipv6.6 index abc0e87c691..efd04212578 100644 --- a/contrib/ipfilter/test/expected/ipv6.6 +++ b/contrib/ipfilter/test/expected/ipv6.6 @@ -1,3 +1,10 @@ pass pass +pass +pass +-------- +nomatch +nomatch +block +nomatch -------- diff --git a/contrib/ipfilter/test/expected/l1 b/contrib/ipfilter/test/expected/l1 index ba0de69b923..e4a081d8e82 100644 --- a/contrib/ipfilter/test/expected/l1 +++ b/contrib/ipfilter/test/expected/l1 @@ -1,11 +1,13 @@ log in all +missed 1 ipf log entries: 0 1 01/01/1970 00:00:00.000000 anon0 @-1:-1 L 1.1.1.1,1025 -> 2.2.2.2,25 PR tcp len 20 40 -S IN 01/01/1970 00:00:00.000000 anon0 @-1:-1 L 1.1.1.1,1025 -> 2.2.2.2,25 PR tcp len 20 40 -A IN 01/01/1970 00:00:00.000000 anon0 @-1:-1 L 2.2.2.2,25 -> 1.1.1.1,1025 PR tcp len 20 40 -AS IN 01/01/1970 00:00:00.000000 anon0 @-1:-1 L 1.1.1.1,1025 -> 2.2.2.2,25 PR tcp len 20 40 -AF IN 01/01/1970 00:00:00.000000 2x anon0 @-1:-1 L 1.1.1.1,1025 -> 2.2.2.2,25 PR tcp len 20 40 -A IN 01/01/1970 00:00:00.000000 anon0 @-1:-1 L 1.1.1.1,1 -> 4.4.4.4,53 PR udp len 20 40 IN -01/01/1970 00:00:00.000000 2x anon0 @-1:-1 L 2.2.2.2,1 -> 4.4.4.4,53 PR udp len 20 40 IN +01/01/1970 00:00:00.000000 anon0 @-1:-1 L 2.2.2.2,1 -> 4.4.4.4,53 PR udp len 20 40 IN +01/01/1970 00:00:00.000000 anon0 @-1:-1 L 2.2.2.2,1 -> 4.4.4.4,53 PR udp len 20 56 IN 01/01/1970 00:00:00.000000 anon0 @-1:-1 L 2.2.2.2 -> 4.4.4.4 PR ip len 20 (20) IN 01/01/1970 00:00:00.000000 anon0 @-1:-1 L 3.3.3.3,1023 -> 1.1.1.1,2049 PR udp len 20 28 IN 01/01/1970 00:00:00.000000 anon0 @-1:-1 L 1.1.1.1,2049 -> 3.3.3.3,1023 PR udp len 20 28 IN @@ -15,11 +17,14 @@ pass in on anon0 all head 100 pass in log quick from 3.3.3.3 to any group 100 -------- pass in log body quick from 2.2.2.2 to any +missed 1 ipf log entries: 0 1 01/01/1970 00:00:00.000000 anon0 @0:1 p 2.2.2.2,25 -> 1.1.1.1,1025 PR tcp len 20 40 -AS IN -01/01/1970 00:00:00.000000 2x anon0 @0:1 p 2.2.2.2,1 -> 4.4.4.4,53 PR udp len 20 40 IN +01/01/1970 00:00:00.000000 anon0 @0:1 p 2.2.2.2,1 -> 4.4.4.4,53 PR udp len 20 40 IN +01/01/1970 00:00:00.000000 anon0 @0:1 p 2.2.2.2,1 -> 4.4.4.4,53 PR udp len 20 56 IN 01/01/1970 00:00:00.000000 anon0 @0:1 p 2.2.2.2 -> 4.4.4.4 PR ip len 20 (20) IN -------- pass in log quick proto tcp from 1.1.1.1 to any flags S keep state +missed 1 ipf log entries: 0 1 01/01/1970 00:00:00.000000 anon0 @0:1 p 1.1.1.1,1025 -> 2.2.2.2,25 PR tcp len 20 40 -S K-S IN 01/01/1970 00:00:00.000000 anon0 @0:1 p 1.1.1.1,1025 -> 2.2.2.2,25 PR tcp len 20 40 -A K-S IN 01/01/1970 00:00:00.000000 anon0 @0:1 p 2.2.2.2,25 -> 1.1.1.1,1025 PR tcp len 20 40 -AS K-S IN @@ -27,8 +32,10 @@ pass in log quick proto tcp from 1.1.1.1 to any flags S keep state 01/01/1970 00:00:00.000000 anon0 @0:1 p 1.1.1.1,1025 -> 2.2.2.2,25 PR tcp len 20 40 -AF K-S IN -------- pass in log first quick proto tcp from 1.1.1.1 to any flags S keep state +missed 1 ipf log entries: 0 1 01/01/1970 00:00:00.000000 anon0 @0:1 p 1.1.1.1,1025 -> 2.2.2.2,25 PR tcp len 20 40 -S K-S IN -------- +missed 1 ipf log entries: 0 1 01/01/1970 00:00:00.000000 anon0 @-1:-1 L 1.1.1.1,1025 -> 2.2.2.2,25 PR tcp len 20 40 -S IN 01/01/1970 00:00:00.000000 anon0 @0:4 p 1.1.1.1,1025 -> 2.2.2.2,25 PR tcp len 20 40 -S K-S IN 01/01/1970 00:00:00.000000 anon0 @0:4 p 1.1.1.1,1025 -> 2.2.2.2,25 PR tcp len 20 40 -A K-S IN diff --git a/contrib/ipfilter/test/expected/l1.b b/contrib/ipfilter/test/expected/l1.b index c060086d8df..e06e486fcf3 100644 --- a/contrib/ipfilter/test/expected/l1.b +++ b/contrib/ipfilter/test/expected/l1.b @@ -1,29 +1,38 @@ +missed 1 ipf log entries: 0 1 01/01/1970 00:00:00.000000 anon0 @-1:-1 L 1.1.1.1,1025 -> 2.2.2.2,25 PR tcp len 20 40 -S IN 01/01/1970 00:00:00.000000 anon0 @-1:-1 L 1.1.1.1,1025 -> 2.2.2.2,25 PR tcp len 20 40 -A IN 01/01/1970 00:00:00.000000 anon0 @-1:-1 L 2.2.2.2,25 -> 1.1.1.1,1025 PR tcp len 20 40 -AS IN 01/01/1970 00:00:00.000000 anon0 @-1:-1 L 1.1.1.1,1025 -> 2.2.2.2,25 PR tcp len 20 40 -AF IN 01/01/1970 00:00:00.000000 2x anon0 @-1:-1 L 1.1.1.1,1025 -> 2.2.2.2,25 PR tcp len 20 40 -A IN 01/01/1970 00:00:00.000000 anon0 @-1:-1 L 1.1.1.1,1 -> 4.4.4.4,53 PR udp len 20 40 IN -01/01/1970 00:00:00.000000 2x anon0 @-1:-1 L 2.2.2.2,1 -> 4.4.4.4,53 PR udp len 20 40 IN +01/01/1970 00:00:00.000000 anon0 @-1:-1 L 2.2.2.2,1 -> 4.4.4.4,53 PR udp len 20 40 IN +01/01/1970 00:00:00.000000 anon0 @-1:-1 L 2.2.2.2,1 -> 4.4.4.4,53 PR udp len 20 56 IN 01/01/1970 00:00:00.000000 anon0 @-1:-1 L 2.2.2.2 -> 4.4.4.4 PR ip len 20 (20) IN 01/01/1970 00:00:00.000000 anon0 @-1:-1 L 3.3.3.3,1023 -> 1.1.1.1,2049 PR udp len 20 28 IN 01/01/1970 00:00:00.000000 anon0 @-1:-1 L 1.1.1.1,2049 -> 3.3.3.3,1023 PR udp len 20 28 IN -------- -------- -------- +missed 1 ipf log entries: 0 1 01/01/1970 00:00:00.000000 anon0 @0:1 p 2.2.2.2,25 -> 1.1.1.1,1025 PR tcp len 20 40 -AS IN -01/01/1970 00:00:00.000000 2x anon0 @0:1 p 2.2.2.2,1 -> 4.4.4.4,53 PR udp len 20 40 IN +01/01/1970 00:00:00.000000 anon0 @0:1 p 2.2.2.2,1 -> 4.4.4.4,53 PR udp len 20 40 IN 01 02 03 04 05 06 07 08 09 0a 0b 0d ............ +01/01/1970 00:00:00.000000 anon0 @0:1 p 2.2.2.2,1 -> 4.4.4.4,53 PR udp len 20 56 IN +01 02 03 04 05 06 07 08 09 0a 0b 0d 0e 0f 40 61 ..............@a +42 63 44 65 46 67 48 69 4a 6b 4c 6d BcDeFgHiJkLm 01/01/1970 00:00:00.000000 anon0 @0:1 p 2.2.2.2 -> 4.4.4.4 PR ip len 20 (20) IN -------- +missed 1 ipf log entries: 0 1 01/01/1970 00:00:00.000000 anon0 @0:1 p 1.1.1.1,1025 -> 2.2.2.2,25 PR tcp len 20 40 -S K-S IN 01/01/1970 00:00:00.000000 anon0 @0:1 p 1.1.1.1,1025 -> 2.2.2.2,25 PR tcp len 20 40 -A K-S IN 01/01/1970 00:00:00.000000 anon0 @0:1 p 2.2.2.2,25 -> 1.1.1.1,1025 PR tcp len 20 40 -AS K-S IN 01/01/1970 00:00:00.000000 e1 @0:1 p 2.2.2.2,25 -> 1.1.1.1,1025 PR tcp len 20 40 -A K-S OUT 01/01/1970 00:00:00.000000 anon0 @0:1 p 1.1.1.1,1025 -> 2.2.2.2,25 PR tcp len 20 40 -AF K-S IN -------- +missed 1 ipf log entries: 0 1 01/01/1970 00:00:00.000000 anon0 @0:1 p 1.1.1.1,1025 -> 2.2.2.2,25 PR tcp len 20 40 -S K-S IN -------- +missed 1 ipf log entries: 0 1 01/01/1970 00:00:00.000000 anon0 @-1:-1 L 1.1.1.1,1025 -> 2.2.2.2,25 PR tcp len 20 40 -S IN 01/01/1970 00:00:00.000000 anon0 @0:4 p 1.1.1.1,1025 -> 2.2.2.2,25 PR tcp len 20 40 -S K-S IN 01/01/1970 00:00:00.000000 anon0 @0:4 p 1.1.1.1,1025 -> 2.2.2.2,25 PR tcp len 20 40 -A K-S IN diff --git a/contrib/ipfilter/test/expected/n1 b/contrib/ipfilter/test/expected/n1 index 537f9bb6550..20eaedc8ea8 100644 --- a/contrib/ipfilter/test/expected/n1 +++ b/contrib/ipfilter/test/expected/n1 @@ -1,105 +1,197 @@ -ip #0 20(20) 255 10.1.1.0 > 10.1.1.2 -ip #0 20(20) 255 10.2.2.2 > 10.1.1.2 -ip #0 20(20) 255 10.1.1.2 > 10.1.1.1 -ip #0 40(20) 6 10.1.1.2,1025 > 10.1.1.1,1025 -ip #0 40(20) 6 10.1.1.2,1026 > 10.1.1.1,1025 -ip #0 20(20) 255 10.2.2.1 > 10.1.2.1 -ip #0 20(20) 255 10.2.2.2 > 10.1.2.1 -ip #0 20(20) 255 10.1.1.1 > 10.1.1.2 -ip #0 20(20) 255 10.1.1.2 > 10.1.1.1 -ip #0 20(20) 255 10.2.2.1 > 10.2.1.1 -ip #0 20(20) 255 10.2.2.2 > 10.2.1.1 -ip #0 20(20) 255 10.2.2.3 > 10.1.1.1 -ip #0 20(20) 255 10.2.3.4 > 10.2.2.2 -ip #0 20(20) 255 10.1.1.1 > 10.2.2.2 -ip #0 20(20) 255 10.1.1.2 > 10.1.1.1 -ip #0 20(20) 255 10.1.1.0 > 10.3.4.5 -ip #0 20(20) 255 10.1.1.1 > 10.3.4.5 -ip #0 20(20) 255 10.1.1.2 > 10.3.4.5 -ip #0 40(20) 6 10.1.1.1,1025 > 10.3.4.5,1025 -ip #0 48(20) 1 10.2.2.2 > 10.4.3.2 -ip #0 48(20) 1 10.4.3.2 > 10.1.1.1 -ip #0 48(20) 1 10.4.3.2 > 10.3.4.1 -ip #0 48(20) 1 10.4.3.2 > 10.3.4.2 -ip #0 48(20) 1 10.4.3.2 > 10.3.4.3 -ip #0 48(20) 1 10.4.3.2 > 10.3.4.4 -ip #0 48(20) 1 10.4.3.2 > 10.3.4.5 -ip #0 20(20) 34 10.1.1.2 > 10.4.3.2 -ip #0 20(20) 34 10.4.3.2 > 10.3.4.4 -ip #0 20(20) 34 10.1.1.2 > 10.4.3.4 -ip #0 20(20) 34 10.4.3.4 > 10.3.4.5 -ip #0 20(20) 34 10.1.1.3 > 10.4.3.4 -ip #0 20(20) 34 10.4.3.4 > 10.3.4.6 -ip #0 20(20) 35 10.1.1.3 > 10.4.3.4 -ip #0 20(20) 35 10.4.3.4 > 10.3.4.7 +> zx0 ip #0 20(20) 255 10.1.1.0 > 10.1.1.2 +> zx0 ip #0 20(20) 255 10.2.2.2 > 10.1.1.2 +> zx0 ip #0 20(20) 255 10.1.1.2 > 10.1.1.1 +> zx0 ip #0 40(20) 6 10.1.1.2,1025 > 10.1.1.1,1025 +> zx0 ip #0 40(20) 6 10.1.1.2,1026 > 10.1.1.1,1025 +> zx0 ip #0 20(20) 255 10.2.2.1 > 10.1.2.1 +> zx0 ip #0 20(20) 255 10.2.2.2 > 10.1.2.1 +< zx0 ip #0 20(20) 255 10.1.1.1 > 10.1.1.2 +< zx0 ip #0 20(20) 255 10.1.1.2 > 10.1.1.1 +< zx0 ip #0 20(20) 255 10.2.2.1 > 10.2.1.1 +< zx0 ip #0 20(20) 255 10.2.2.2 > 10.2.1.1 +< zx0 ip #0 20(20) 255 10.2.2.3 > 10.1.1.1 +< zx0 ip #0 20(20) 255 10.2.3.4 > 10.2.2.2 +< zx0 ip #0 20(20) 255 10.1.1.1 > 10.2.2.2 +< zx0 ip #0 20(20) 255 10.1.1.2 > 10.1.1.1 +< zx0 ip #0 20(20) 255 10.1.1.0 > 10.3.4.5 +< zx0 ip #0 20(20) 255 10.1.1.1 > 10.3.4.5 +< zx0 ip #0 20(20) 255 10.1.1.2 > 10.3.4.5 +< zx0 ip #0 40(20) 6 10.1.1.1,1025 > 10.3.4.5,1025 +> zx0 ip #0 48(20) 1 10.2.2.2 > 10.4.3.2 +< zx0 ip #0 48(20) 1 10.4.3.2 > 10.1.1.1 +< zx0 ip #0 48(20) 1 10.4.3.2 > 10.3.4.1 +< zx0 ip #0 48(20) 1 10.4.3.2 > 10.3.4.2 +< zx0 ip #0 48(20) 1 10.4.3.2 > 10.3.4.3 +< zx0 ip #0 48(20) 1 10.4.3.2 > 10.3.4.4 +< zx0 ip #0 48(20) 1 10.4.3.2 > 10.3.4.5 +> zx0 ip #0 20(20) 34 10.1.1.2 > 10.4.3.2 +< zx0 ip #0 20(20) 34 10.4.3.2 > 10.3.4.4 +> zx0 ip #0 20(20) 34 10.1.1.2 > 10.4.3.4 +< zx0 ip #0 20(20) 34 10.4.3.4 > 10.3.4.5 +> zx0 ip #0 20(20) 34 10.1.1.3 > 10.4.3.4 +< zx0 ip #0 20(20) 34 10.4.3.4 > 10.3.4.6 +> zx0 ip #0 20(20) 35 10.1.1.3 > 10.4.3.4 +< zx0 ip #0 20(20) 35 10.4.3.4 > 10.3.4.7 +List of active MAP/Redirect filters: +map zx0 10.1.1.1/32 -> 10.2.2.2/32 + +List of active sessions: +MAP 10.1.1.1 <- -> 10.2.2.2 [10.4.3.2] +MAP 10.1.1.1 <- -> 10.2.2.2 [10.1.1.2] + +Hostmap table: +10.1.1.1,10.4.3.2 -> 10.2.2.2,0.0.0.0 (use = 1) +10.1.1.1,10.1.1.2 -> 10.2.2.2,0.0.0.0 (use = 1) +List of active state sessions: +List of configured pools +List of configured hash tables +List of groups configured (set 0) +List of groups configured (set 1) +Rules configured (set 0, in) +Rules configured (set 0, out) +Rules configured (set 1, in) +Rules configured (set 1, out) +Accounting rules configured (set 0, in) +Accounting rules configured (set 0, out) +Accounting rules configured (set 1, in) +Accounting rules configured (set 1, out) ------------------------------- -ip #0 20(20) 255 10.3.4.5 > 10.1.1.2 -ip #0 20(20) 255 10.1.1.1 > 10.1.1.2 -ip #0 20(20) 255 10.3.4.5 > 10.1.1.1 -ip #0 40(20) 6 10.3.4.5,1025 > 10.1.1.1,1025 -ip #0 40(20) 6 10.3.4.5,1026 > 10.1.1.1,1025 -ip #0 20(20) 255 10.2.2.1 > 10.1.2.1 -ip #0 20(20) 255 10.2.2.2 > 10.1.2.1 -ip #0 20(20) 255 10.1.1.1 > 10.1.1.2 -ip #0 20(20) 255 10.1.1.2 > 10.1.1.1 -ip #0 20(20) 255 10.2.2.1 > 10.2.1.1 -ip #0 20(20) 255 10.2.2.2 > 10.2.1.1 -ip #0 20(20) 255 10.2.2.3 > 10.1.1.1 -ip #0 20(20) 255 10.2.3.4 > 10.2.2.2 -ip #0 20(20) 255 10.1.1.1 > 10.2.2.2 -ip #0 20(20) 255 10.1.1.2 > 10.2.2.2 -ip #0 20(20) 255 10.1.1.0 > 10.3.4.5 -ip #0 20(20) 255 10.1.1.1 > 10.1.1.2 -ip #0 20(20) 255 10.1.1.2 > 10.1.1.0 -ip #0 40(20) 6 10.1.1.1,1025 > 10.1.1.2,1025 -ip #0 48(20) 1 10.3.4.5 > 10.4.3.2 -ip #0 48(20) 1 10.4.3.2 > 10.2.2.2 -ip #0 48(20) 1 10.4.3.2 > 10.3.4.1 -ip #0 48(20) 1 10.4.3.2 > 10.3.4.2 -ip #0 48(20) 1 10.4.3.2 > 10.3.4.3 -ip #0 48(20) 1 10.4.3.2 > 10.3.4.4 -ip #0 48(20) 1 10.4.3.2 > 10.1.1.1 -ip #0 20(20) 34 10.3.4.5 > 10.4.3.2 -ip #0 20(20) 34 10.4.3.2 > 10.3.4.4 -ip #0 20(20) 34 10.3.4.5 > 10.4.3.4 -ip #0 20(20) 34 10.4.3.4 > 10.1.1.2 -ip #0 20(20) 34 10.1.1.3 > 10.4.3.4 -ip #0 20(20) 34 10.4.3.4 > 10.3.4.6 -ip #0 20(20) 35 10.3.4.5 > 10.4.3.4 -ip #0 20(20) 35 10.4.3.4 > 10.3.4.7 +> zx0 ip #0 20(20) 255 10.3.4.5 > 10.1.1.2 +15 +> zx0 ip #0 20(20) 255 10.3.4.5 > 10.1.1.1 +> zx0 ip #0 40(20) 6 10.3.4.5,1025 > 10.1.1.1,1025 +> zx0 ip #0 40(20) 6 10.3.4.5,1026 > 10.1.1.1,1025 +> zx0 ip #0 20(20) 255 10.2.2.1 > 10.1.2.1 +> zx0 ip #0 20(20) 255 10.2.2.2 > 10.1.2.1 +< zx0 ip #0 20(20) 255 10.1.1.1 > 10.1.1.2 +< zx0 ip #0 20(20) 255 10.1.1.2 > 10.1.1.1 +< zx0 ip #0 20(20) 255 10.2.2.1 > 10.2.1.1 +< zx0 ip #0 20(20) 255 10.2.2.2 > 10.2.1.1 +< zx0 ip #0 20(20) 255 10.2.2.3 > 10.1.1.1 +< zx0 ip #0 20(20) 255 10.2.3.4 > 10.2.2.2 +< zx0 ip #0 20(20) 255 10.1.1.1 > 10.2.2.2 +< zx0 ip #0 20(20) 255 10.1.1.2 > 10.2.2.2 +< zx0 ip #0 20(20) 255 10.1.1.0 > 10.3.4.5 +< zx0 ip #0 20(20) 255 10.1.1.1 > 10.1.1.2 +< zx0 ip #0 20(20) 255 10.1.1.2 > 10.1.1.0 +< zx0 ip #0 40(20) 6 10.1.1.1,1025 > 10.1.1.2,1025 +> zx0 ip #0 48(20) 1 10.3.4.5 > 10.4.3.2 +< zx0 ip #0 48(20) 1 10.4.3.2 > 10.2.2.2 +< zx0 ip #0 48(20) 1 10.4.3.2 > 10.3.4.1 +< zx0 ip #0 48(20) 1 10.4.3.2 > 10.3.4.2 +< zx0 ip #0 48(20) 1 10.4.3.2 > 10.3.4.3 +< zx0 ip #0 48(20) 1 10.4.3.2 > 10.3.4.4 +< zx0 ip #0 48(20) 1 10.4.3.2 > 10.1.1.1 +> zx0 ip #0 20(20) 34 10.3.4.5 > 10.4.3.2 +< zx0 ip #0 20(20) 34 10.4.3.2 > 10.3.4.4 +> zx0 ip #0 20(20) 34 10.3.4.5 > 10.4.3.4 +< zx0 ip #0 20(20) 34 10.4.3.4 > 10.1.1.2 +15 +< zx0 ip #0 20(20) 34 10.4.3.4 > 10.3.4.6 +> zx0 ip #0 20(20) 35 10.3.4.5 > 10.4.3.4 +< zx0 ip #0 20(20) 35 10.4.3.4 > 10.3.4.7 +List of active MAP/Redirect filters: +map zx0 10.1.1.0/24 -> 10.3.4.5/32 + +List of active sessions: +MAP 10.1.1.3 <- -> 10.3.4.5 [10.4.3.4] +MAP 10.1.1.2 <- -> 10.3.4.5 [10.4.3.4] +MAP 10.1.1.2 <- -> 10.3.4.5 [10.4.3.2] +MAP 10.1.1.1 <- -> 10.3.4.5 [10.4.3.2] +MAP 10.1.1.2 1026 <- -> 10.3.4.5 1026 [10.1.1.1 1025] +MAP 10.1.1.2 1025 <- -> 10.3.4.5 1025 [10.1.1.1 1025] +MAP 10.1.1.2 <- -> 10.3.4.5 [10.1.1.1] +MAP 10.1.1.0 <- -> 10.3.4.5 [10.1.1.2] + +Hostmap table: +10.1.1.3,10.4.3.4 -> 10.3.4.5,0.0.0.0 (use = 1) +10.1.1.2,10.4.3.4 -> 10.3.4.5,0.0.0.0 (use = 1) +10.1.1.2,10.4.3.2 -> 10.3.4.5,0.0.0.0 (use = 1) +10.1.1.1,10.4.3.2 -> 10.3.4.5,0.0.0.0 (use = 1) +10.1.1.2,10.1.1.1 -> 10.3.4.5,0.0.0.0 (use = 3) +10.1.1.0,10.1.1.2 -> 10.3.4.5,0.0.0.0 (use = 1) +List of active state sessions: +List of configured pools +List of configured hash tables +List of groups configured (set 0) +List of groups configured (set 1) +Rules configured (set 0, in) +Rules configured (set 0, out) +Rules configured (set 1, in) +Rules configured (set 1, out) +Accounting rules configured (set 0, in) +Accounting rules configured (set 0, out) +Accounting rules configured (set 1, in) +Accounting rules configured (set 1, out) ------------------------------- -ip #0 20(20) 255 10.3.4.1 > 10.1.1.2 -ip #0 20(20) 255 10.3.4.2 > 10.1.1.2 -ip #0 20(20) 255 10.3.4.3 > 10.1.1.1 -ip #0 40(20) 6 10.3.4.3,1025 > 10.1.1.1,1025 -ip #0 40(20) 6 10.3.4.3,1026 > 10.1.1.1,1025 -ip #0 20(20) 255 10.2.2.1 > 10.1.2.1 -ip #0 20(20) 255 10.2.2.2 > 10.1.2.1 -ip #0 20(20) 255 10.1.1.1 > 10.1.1.2 -ip #0 20(20) 255 10.1.1.2 > 10.1.1.1 -ip #0 20(20) 255 10.2.2.1 > 10.2.1.1 -ip #0 20(20) 255 10.2.2.2 > 10.2.1.1 -ip #0 20(20) 255 10.2.2.3 > 10.1.1.1 -ip #0 20(20) 255 10.2.3.4 > 10.2.2.2 -ip #0 20(20) 255 10.1.1.1 > 10.2.2.2 -ip #0 20(20) 255 10.1.1.2 > 10.2.2.2 -ip #0 20(20) 255 10.1.1.0 > 10.3.4.5 -ip #0 20(20) 255 10.1.1.1 > 10.3.4.5 -ip #0 20(20) 255 10.1.1.2 > 10.3.4.5 -ip #0 40(20) 6 10.1.1.1,1025 > 10.3.4.5,1025 -ip #0 48(20) 1 10.3.4.3 > 10.4.3.2 -ip #0 48(20) 1 10.4.3.2 > 10.2.2.2 -ip #0 48(20) 1 10.4.3.2 > 10.3.4.1 -ip #0 48(20) 1 10.4.3.2 > 10.3.4.2 -ip #0 48(20) 1 10.4.3.2 > 10.1.1.1 -ip #0 48(20) 1 10.4.3.2 > 10.3.4.4 -ip #0 48(20) 1 10.4.3.2 > 10.3.4.5 -ip #0 20(20) 34 10.3.4.3 > 10.4.3.2 -ip #0 20(20) 34 10.4.3.2 > 10.3.4.4 -ip #0 20(20) 34 10.3.4.3 > 10.4.3.4 -ip #0 20(20) 34 10.4.3.4 > 10.3.4.5 -ip #0 20(20) 34 10.3.4.4 > 10.4.3.4 -ip #0 20(20) 34 10.4.3.4 > 10.3.4.6 -ip #0 20(20) 35 10.3.4.4 > 10.4.3.4 -ip #0 20(20) 35 10.4.3.4 > 10.3.4.7 +> zx0 ip #0 20(20) 255 10.3.4.1 > 10.1.1.2 +> zx0 ip #0 20(20) 255 10.3.4.2 > 10.1.1.2 +> zx0 ip #0 20(20) 255 10.3.4.3 > 10.1.1.1 +> zx0 ip #0 40(20) 6 10.3.4.3,1025 > 10.1.1.1,1025 +> zx0 ip #0 40(20) 6 10.3.4.3,1026 > 10.1.1.1,1025 +> zx0 ip #0 20(20) 255 10.2.2.1 > 10.1.2.1 +> zx0 ip #0 20(20) 255 10.2.2.2 > 10.1.2.1 +< zx0 ip #0 20(20) 255 10.1.1.1 > 10.1.1.2 +< zx0 ip #0 20(20) 255 10.1.1.2 > 10.1.1.1 +< zx0 ip #0 20(20) 255 10.2.2.1 > 10.2.1.1 +< zx0 ip #0 20(20) 255 10.2.2.2 > 10.2.1.1 +< zx0 ip #0 20(20) 255 10.2.2.3 > 10.1.1.1 +< zx0 ip #0 20(20) 255 10.2.3.4 > 10.2.2.2 +< zx0 ip #0 20(20) 255 10.1.1.1 > 10.2.2.2 +< zx0 ip #0 20(20) 255 10.1.1.2 > 10.2.2.2 +< zx0 ip #0 20(20) 255 10.1.1.0 > 10.3.4.5 +< zx0 ip #0 20(20) 255 10.1.1.1 > 10.3.4.5 +< zx0 ip #0 20(20) 255 10.1.1.2 > 10.3.4.5 +< zx0 ip #0 40(20) 6 10.1.1.1,1025 > 10.3.4.5,1025 +> zx0 ip #0 48(20) 1 10.3.4.3 > 10.4.3.2 +< zx0 ip #0 48(20) 1 10.4.3.2 > 10.2.2.2 +< zx0 ip #0 48(20) 1 10.4.3.2 > 10.3.4.1 +< zx0 ip #0 48(20) 1 10.4.3.2 > 10.3.4.2 +< zx0 ip #0 48(20) 1 10.4.3.2 > 10.1.1.1 +< zx0 ip #0 48(20) 1 10.4.3.2 > 10.3.4.4 +< zx0 ip #0 48(20) 1 10.4.3.2 > 10.3.4.5 +> zx0 ip #0 20(20) 34 10.3.4.3 > 10.4.3.2 +< zx0 ip #0 20(20) 34 10.4.3.2 > 10.3.4.4 +> zx0 ip #0 20(20) 34 10.3.4.3 > 10.4.3.4 +< zx0 ip #0 20(20) 34 10.4.3.4 > 10.3.4.5 +> zx0 ip #0 20(20) 34 10.3.4.4 > 10.4.3.4 +< zx0 ip #0 20(20) 34 10.4.3.4 > 10.3.4.6 +> zx0 ip #0 20(20) 35 10.3.4.4 > 10.4.3.4 +< zx0 ip #0 20(20) 35 10.4.3.4 > 10.3.4.7 +List of active MAP/Redirect filters: +map zx0 10.1.1.0/24 -> 10.3.4.0/24 + +List of active sessions: +MAP 10.1.1.3 <- -> 10.3.4.4 [10.4.3.4] +MAP 10.1.1.3 <- -> 10.3.4.4 [10.4.3.4] +MAP 10.1.1.2 <- -> 10.3.4.3 [10.4.3.4] +MAP 10.1.1.2 <- -> 10.3.4.3 [10.4.3.2] +MAP 10.1.1.1 <- -> 10.3.4.3 [10.4.3.2] +MAP 10.1.1.2 1026 <- -> 10.3.4.3 1026 [10.1.1.1 1025] +MAP 10.1.1.2 1025 <- -> 10.3.4.3 1025 [10.1.1.1 1025] +MAP 10.1.1.2 <- -> 10.3.4.3 [10.1.1.1] +MAP 10.1.1.1 <- -> 10.3.4.2 [10.1.1.2] +MAP 10.1.1.0 <- -> 10.3.4.1 [10.1.1.2] + +Hostmap table: +10.1.1.3,10.4.3.4 -> 10.3.4.4,0.0.0.0 (use = 2) +10.1.1.2,10.4.3.4 -> 10.3.4.3,0.0.0.0 (use = 1) +10.1.1.2,10.4.3.2 -> 10.3.4.3,0.0.0.0 (use = 1) +10.1.1.1,10.4.3.2 -> 10.3.4.3,0.0.0.0 (use = 1) +10.1.1.2,10.1.1.1 -> 10.3.4.3,0.0.0.0 (use = 3) +10.1.1.1,10.1.1.2 -> 10.3.4.2,0.0.0.0 (use = 1) +10.1.1.0,10.1.1.2 -> 10.3.4.1,0.0.0.0 (use = 1) +List of active state sessions: +List of configured pools +List of configured hash tables +List of groups configured (set 0) +List of groups configured (set 1) +Rules configured (set 0, in) +Rules configured (set 0, out) +Rules configured (set 1, in) +Rules configured (set 1, out) +Accounting rules configured (set 0, in) +Accounting rules configured (set 0, out) +Accounting rules configured (set 1, in) +Accounting rules configured (set 1, out) ------------------------------- diff --git a/contrib/ipfilter/test/expected/n10 b/contrib/ipfilter/test/expected/n10 index ae541d15857..0c03ff088b7 100644 --- a/contrib/ipfilter/test/expected/n10 +++ b/contrib/ipfilter/test/expected/n10 @@ -1,9 +1,72 @@ 4500 002c 10c9 4000 ff06 5c9d cbcb cbcb 96cb e002 8032 0015 bd6b c9c8 0000 0000 6002 2238 655d 0000 0204 0064 +List of active MAP/Redirect filters: +map ppp0 0/0 -> 203.203.203.203/32 mssclamp 100 + +List of active sessions: +MAP 192.168.1.3 32818 <- -> 203.203.203.203 32818 [150.203.224.2 21] + +Hostmap table: +192.168.1.3,150.203.224.2 -> 203.203.203.203,0.0.0.0 (use = 1) +List of active state sessions: +List of configured pools +List of configured hash tables +List of groups configured (set 0) +List of groups configured (set 1) +Rules configured (set 0, in) +Rules configured (set 0, out) +Rules configured (set 1, in) +Rules configured (set 1, out) +Accounting rules configured (set 0, in) +Accounting rules configured (set 0, out) +Accounting rules configured (set 1, in) +Accounting rules configured (set 1, out) ------------------------------- 4500 002c 10c9 4000 ff06 5c9d cbcb cbcb 96cb e002 8032 0015 bd6b c9c8 0000 0000 6002 2238 61d9 0000 0204 03e8 +List of active MAP/Redirect filters: +map ppp0 0/0 -> 203.203.203.203/32 mssclamp 1000 + +List of active sessions: +MAP 192.168.1.3 32818 <- -> 203.203.203.203 32818 [150.203.224.2 21] + +Hostmap table: +192.168.1.3,150.203.224.2 -> 203.203.203.203,0.0.0.0 (use = 1) +List of active state sessions: +List of configured pools +List of configured hash tables +List of groups configured (set 0) +List of groups configured (set 1) +Rules configured (set 0, in) +Rules configured (set 0, out) +Rules configured (set 1, in) +Rules configured (set 1, out) +Accounting rules configured (set 0, in) +Accounting rules configured (set 0, out) +Accounting rules configured (set 1, in) +Accounting rules configured (set 1, out) ------------------------------- 4500 002c 10c9 4000 ff06 5c9d cbcb cbcb 96cb e002 8032 0015 bd6b c9c8 0000 0000 6002 2238 600d 0000 0204 05b4 +List of active MAP/Redirect filters: +map ppp0 0/0 -> 203.203.203.203/32 mssclamp 10000 + +List of active sessions: +MAP 192.168.1.3 32818 <- -> 203.203.203.203 32818 [150.203.224.2 21] + +Hostmap table: +192.168.1.3,150.203.224.2 -> 203.203.203.203,0.0.0.0 (use = 1) +List of active state sessions: +List of configured pools +List of configured hash tables +List of groups configured (set 0) +List of groups configured (set 1) +Rules configured (set 0, in) +Rules configured (set 0, out) +Rules configured (set 1, in) +Rules configured (set 1, out) +Accounting rules configured (set 0, in) +Accounting rules configured (set 0, out) +Accounting rules configured (set 1, in) +Accounting rules configured (set 1, out) ------------------------------- diff --git a/contrib/ipfilter/test/expected/n100 b/contrib/ipfilter/test/expected/n100 new file mode 100644 index 00000000000..80f00a178f3 --- /dev/null +++ b/contrib/ipfilter/test/expected/n100 @@ -0,0 +1,33 @@ +> zx0 ip #0 20(20) 255 1.1.1.1 > 2.3.2.3 +> zx0 ip #0 20(20) 255 4.4.4.4 > 6.6.0.1 +> zx0 ip #0 20(20) 255 4.4.4.4 > 6.6.0.2 +> zx0 ip #0 20(20) 255 4.4.4.4 > 6.6.0.3 +> zx0 ip #0 20(20) 255 4.4.4.4 > 6.6.0.4 +> zx0 ip #0 20(20) 255 4.4.4.4 > 6.6.0.1 +> zx0 ip #0 40(20) 6 1.1.1.1,101 > 2.3.2.3,203 +> zx0 ip #0 40(20) 6 4.4.4.4,101 > 6.6.0.5,203 +List of active MAP/Redirect filters: +rewrite out on zx0 from 0/0 to 2.2.0.0/16 -> src 4.4.4.4/32 dst 6.6.0.0/16; + +List of active sessions: +RWR-MAP 1.1.1.1 101 2.2.2.3 203 <- -> 4.4.4.4 101 6.6.0.5 203 +RWR-MAP 1.1.1.1 2.2.2.4 <- -> 4.4.4.4 6.6.0.4 +RWR-MAP 1.2.1.2 2.2.2.3 <- -> 4.4.4.4 6.6.0.3 +RWR-MAP 1.1.1.2 2.2.2.3 <- -> 4.4.4.4 6.6.0.2 +RWR-MAP 1.1.1.1 2.2.2.3 <- -> 4.4.4.4 6.6.0.1 + +Hostmap table: +List of active state sessions: +List of configured pools +List of configured hash tables +List of groups configured (set 0) +List of groups configured (set 1) +Rules configured (set 0, in) +Rules configured (set 0, out) +Rules configured (set 1, in) +Rules configured (set 1, out) +Accounting rules configured (set 0, in) +Accounting rules configured (set 0, out) +Accounting rules configured (set 1, in) +Accounting rules configured (set 1, out) +------------------------------- diff --git a/contrib/ipfilter/test/expected/n101 b/contrib/ipfilter/test/expected/n101 new file mode 100644 index 00000000000..ad0ad97bc3a --- /dev/null +++ b/contrib/ipfilter/test/expected/n101 @@ -0,0 +1,29 @@ +> zx0 ip #0 20(20) 255 1.1.1.1 > 2.3.2.3 +> zx0 ip #0 20(20) 255 1.1.1.1 > 2.2.2.3 +> zx0 ip #0 20(20) 255 1.1.1.2 > 2.2.2.3 +> zx0 ip #0 20(20) 255 1.2.1.2 > 2.2.2.3 +> zx0 ip #0 20(20) 255 1.1.1.1 > 2.2.2.4 +> zx0 ip #0 20(20) 255 1.1.1.1 > 2.2.2.3 +> zx0 ip #0 40(20) 6 1.1.1.1,101 > 2.3.2.3,203 +> zx0 ip #0 40(20) 6 4.4.4.4,101 > 6.6.0.1,203 +List of active MAP/Redirect filters: +rewrite out on zx0 proto tcp from 0/0 to 2.2.0.0/16 -> src 4.4.4.4/32 dst 6.6.0.0/16; + +List of active sessions: +RWR-MAP 1.1.1.1 101 2.2.2.3 203 <- -> 4.4.4.4 101 6.6.0.1 203 + +Hostmap table: +List of active state sessions: +List of configured pools +List of configured hash tables +List of groups configured (set 0) +List of groups configured (set 1) +Rules configured (set 0, in) +Rules configured (set 0, out) +Rules configured (set 1, in) +Rules configured (set 1, out) +Accounting rules configured (set 0, in) +Accounting rules configured (set 0, out) +Accounting rules configured (set 1, in) +Accounting rules configured (set 1, out) +------------------------------- diff --git a/contrib/ipfilter/test/expected/n102 b/contrib/ipfilter/test/expected/n102 new file mode 100644 index 00000000000..a2f130ee8a5 --- /dev/null +++ b/contrib/ipfilter/test/expected/n102 @@ -0,0 +1,29 @@ +> zx0 ip #0 20(20) 255 1.1.1.1 > 2.3.2.3 +> zx0 ip #0 20(20) 255 1.1.1.1 > 2.2.2.3 +> zx0 ip #0 20(20) 255 1.1.1.2 > 2.2.2.3 +> zx0 ip #0 20(20) 255 1.2.1.2 > 2.2.2.3 +> zx0 ip #0 20(20) 255 1.1.1.1 > 2.2.2.4 +> zx0 ip #0 20(20) 255 1.1.1.1 > 2.2.2.3 +> zx0 ip #0 40(20) 6 1.1.1.1,101 > 2.3.2.3,203 +> zx0 ip #0 40(20) 6 4.4.4.4,1000 > 6.6.0.1,203 +List of active MAP/Redirect filters: +rewrite out on zx0 proto tcp from 0/0 to 2.2.0.0/16 -> src 4.4.4.4/32,1000-2000 dst 6.6.0.0/16; + +List of active sessions: +RWR-MAP 1.1.1.1 101 2.2.2.3 203 <- -> 4.4.4.4 1000 6.6.0.1 203 + +Hostmap table: +List of active state sessions: +List of configured pools +List of configured hash tables +List of groups configured (set 0) +List of groups configured (set 1) +Rules configured (set 0, in) +Rules configured (set 0, out) +Rules configured (set 1, in) +Rules configured (set 1, out) +Accounting rules configured (set 0, in) +Accounting rules configured (set 0, out) +Accounting rules configured (set 1, in) +Accounting rules configured (set 1, out) +------------------------------- diff --git a/contrib/ipfilter/test/expected/n103 b/contrib/ipfilter/test/expected/n103 new file mode 100644 index 00000000000..31ed7404a06 --- /dev/null +++ b/contrib/ipfilter/test/expected/n103 @@ -0,0 +1,33 @@ +> zx0 ip #0 40(20) 6 1.1.1.1,101 > 2.3.2.3,203 +> zx0 ip #0 40(20) 6 4.4.4.4,1000 > 6.6.0.1,4000 +> zx0 ip #0 40(20) 6 4.4.4.4,1000 > 6.6.0.1,4000 +> zx0 ip #0 40(20) 6 4.4.4.4,1001 > 6.6.0.1,4000 +> zx0 ip #0 40(20) 6 4.4.4.4,1001 > 6.6.0.2,4000 +> zx0 ip #0 40(20) 6 4.4.4.4,1001 > 6.6.0.2,4001 +< zx0 ip #0 40(20) 6 2.2.2.3,4000 > 4.4.4.4,1000 +> zx0 ip #0 40(20) 6 4.4.4.4,1000 > 6.6.0.2,4001 +List of active MAP/Redirect filters: +rewrite out on zx0 proto tcp from 0/0 to 2.2.0.0/16 -> src 4.4.4.4/32,1000-1001 dst 6.6.0.0/16,4000-4001; + +List of active sessions: +RWR-MAP 7.7.7.7 101 2.2.2.3 203 <- -> 4.4.4.4 1000 6.6.0.2 4001 +RWR-MAP 5.5.5.5 101 2.2.2.3 203 <- -> 4.4.4.4 1001 6.6.0.2 4001 +RWR-MAP 10.10.10.10 101 2.2.2.3 203 <- -> 4.4.4.4 1001 6.6.0.2 4000 +RWR-MAP 1.1.1.2 101 2.2.2.3 203 <- -> 4.4.4.4 1001 6.6.0.1 4000 +RWR-MAP 1.1.1.1 101 2.2.2.3 203 <- -> 4.4.4.4 1000 6.6.0.1 4000 + +Hostmap table: +List of active state sessions: +List of configured pools +List of configured hash tables +List of groups configured (set 0) +List of groups configured (set 1) +Rules configured (set 0, in) +Rules configured (set 0, out) +Rules configured (set 1, in) +Rules configured (set 1, out) +Accounting rules configured (set 0, in) +Accounting rules configured (set 0, out) +Accounting rules configured (set 1, in) +Accounting rules configured (set 1, out) +------------------------------- diff --git a/contrib/ipfilter/test/expected/n104 b/contrib/ipfilter/test/expected/n104 new file mode 100644 index 00000000000..3b8a9de2e87 --- /dev/null +++ b/contrib/ipfilter/test/expected/n104 @@ -0,0 +1,50 @@ +4500 0028 0001 0000 ff06 b1c3 0404 0001 0606 0001 03e8 0fa0 0000 0001 1000 0001 5010 2000 623f 0000 + +4500 0028 0002 0000 ff06 b5c8 0202 0202 0101 0101 00cb 0065 0000 0001 1000 0001 5010 2000 789d 0000 + +4500 0028 0003 0000 ff06 b1c0 0404 0002 0606 0001 03e8 0fa0 0000 0001 1000 0001 5010 2000 623e 0000 + +4500 0028 0004 0000 ff06 b5c6 0202 0202 0101 0101 00cb 0066 0000 0001 1000 0001 5010 2000 789c 0000 + +4500 0028 0005 0000 ff06 b1be 0404 0002 0606 0001 03e9 0fa0 0000 0001 1000 0001 5010 2000 623d 0000 + +4500 0028 0006 0000 ff06 b5c4 0202 0202 0101 0101 00cb 0067 0000 0001 1000 0001 5010 2000 789b 0000 + +4500 0028 0007 0000 ff06 b1bb 0404 0002 0606 0002 03e9 0fa0 0000 0001 1000 0001 5010 2000 623c 0000 + +4500 0028 0008 0000 ff06 b5c2 0202 0202 0101 0101 00cb 0068 0000 0001 1000 0001 5010 2000 789a 0000 + +4500 0028 0009 0000 ff06 b1b9 0404 0002 0606 0002 03e9 0fa1 0000 0001 1000 0001 5010 2000 623b 0000 + +4500 0028 000a 0000 ff06 b5c0 0202 0202 0101 0101 00cb 0069 0000 0001 1000 0001 5010 2000 7899 0000 + +4500 0028 000b 0000 ff06 b1b6 0404 0003 0606 0002 03e9 0fa1 0000 0001 1000 0001 5010 2000 623a 0000 + +4500 0028 000c 0000 ff06 b5be 0202 0202 0101 0101 00cb 006a 0000 0001 1000 0001 5010 2000 7898 0000 + +List of active MAP/Redirect filters: +rewrite out on zx0 proto tcp from 0/0 to 2.2.0.0/16 -> src 4.4.0.0/24,1000-1001 dst 6.6.0.0/16,4000-4001; + +List of active sessions: +RWR-MAP 1.1.1.1 106 2.2.2.2 203 <- -> 4.4.0.3 1001 6.6.0.2 4001 +RWR-MAP 1.1.1.1 105 2.2.2.2 203 <- -> 4.4.0.2 1001 6.6.0.2 4001 +RWR-MAP 1.1.1.1 104 2.2.2.2 203 <- -> 4.4.0.2 1001 6.6.0.2 4000 +RWR-MAP 1.1.1.1 103 2.2.2.2 203 <- -> 4.4.0.2 1001 6.6.0.1 4000 +RWR-MAP 1.1.1.1 102 2.2.2.2 203 <- -> 4.4.0.2 1000 6.6.0.1 4000 +RWR-MAP 1.1.1.1 101 2.2.2.2 203 <- -> 4.4.0.1 1000 6.6.0.1 4000 + +Hostmap table: +List of active state sessions: +List of configured pools +List of configured hash tables +List of groups configured (set 0) +List of groups configured (set 1) +Rules configured (set 0, in) +Rules configured (set 0, out) +Rules configured (set 1, in) +Rules configured (set 1, out) +Accounting rules configured (set 0, in) +Accounting rules configured (set 0, out) +Accounting rules configured (set 1, in) +Accounting rules configured (set 1, out) +------------------------------- diff --git a/contrib/ipfilter/test/expected/n105 b/contrib/ipfilter/test/expected/n105 new file mode 100644 index 00000000000..d45a4af9840 --- /dev/null +++ b/contrib/ipfilter/test/expected/n105 @@ -0,0 +1,25 @@ +4500 0028 0001 0000 ff06 adc0 0404 0404 0606 0001 03e8 0c38 0000 0001 1000 0001 5010 2000 61a4 0000 + +4500 0028 0001 0000 ff06 b5c9 0202 0202 0101 0101 0050 0065 0000 0001 1000 0001 5010 2000 7918 0000 + +List of active MAP/Redirect filters: +rewrite in on zx0 proto tcp from 0/0 to 2.2.0.0/16 port = 80 -> src 4.4.4.4/32,1000-1001 dst 6.6.0.0/16,port = 3128; + +List of active sessions: +RWR-RDR 1.1.1.1 101 2.2.2.2 80 <- -> 4.4.4.4 1000 6.6.0.1 3128 + +Hostmap table: +List of active state sessions: +List of configured pools +List of configured hash tables +List of groups configured (set 0) +List of groups configured (set 1) +Rules configured (set 0, in) +Rules configured (set 0, out) +Rules configured (set 1, in) +Rules configured (set 1, out) +Accounting rules configured (set 0, in) +Accounting rules configured (set 0, out) +Accounting rules configured (set 1, in) +Accounting rules configured (set 1, out) +------------------------------- diff --git a/contrib/ipfilter/test/expected/n106 b/contrib/ipfilter/test/expected/n106 new file mode 100644 index 00000000000..d466e655335 --- /dev/null +++ b/contrib/ipfilter/test/expected/n106 @@ -0,0 +1,25 @@ +4500 0028 0001 0000 ff06 adc0 0404 0404 0606 0001 03e8 0c38 0000 0001 1000 0001 5010 2000 61a4 0000 + +4500 0028 0001 0000 ff06 b5c9 0202 0202 0101 0101 0050 0065 0000 0001 1000 0001 5010 2000 7918 0000 + +List of active MAP/Redirect filters: +rewrite out on zx0 proto tcp from 0/0 to 2.2.0.0/16 port = 80 -> src 4.4.4.4/32,1000-1001 dst 6.6.0.0/16,port = 3128; + +List of active sessions: +RWR-MAP 1.1.1.1 101 2.2.2.2 80 <- -> 4.4.4.4 1000 6.6.0.1 3128 + +Hostmap table: +List of active state sessions: +List of configured pools +List of configured hash tables +List of groups configured (set 0) +List of groups configured (set 1) +Rules configured (set 0, in) +Rules configured (set 0, out) +Rules configured (set 1, in) +Rules configured (set 1, out) +Accounting rules configured (set 0, in) +Accounting rules configured (set 0, out) +Accounting rules configured (set 1, in) +Accounting rules configured (set 1, out) +------------------------------- diff --git a/contrib/ipfilter/test/expected/n11 b/contrib/ipfilter/test/expected/n11 index 5257a64a1f0..ea11b93e7ca 100644 --- a/contrib/ipfilter/test/expected/n11 +++ b/contrib/ipfilter/test/expected/n11 @@ -1,51 +1,124 @@ -ip #0 20(20) 255 10.1.1.0 > 10.1.1.2 -ip #0 20(20) 255 1.6.7.8 > 10.1.1.2 -ip #0 20(20) 255 10.1.1.2 > 10.1.1.1 -ip #0 20(20) 255 10.2.2.1 > 10.1.2.1 -ip #0 20(20) 255 10.2.2.2 > 10.1.2.1 -ip #0 20(20) 255 10.1.1.1 > 10.1.1.2 -ip #0 20(20) 255 10.1.1.2 > 10.1.1.1 -ip #0 20(20) 255 10.2.2.1 > 10.2.1.1 -ip #0 20(20) 255 10.2.2.2 > 10.2.1.1 -ip #0 20(20) 255 10.2.2.3 > 10.1.1.1 -ip #0 20(20) 255 10.2.3.4 > 10.2.2.2 -ip #0 20(20) 255 10.1.1.1 > 10.2.2.2 -ip #0 20(20) 255 10.1.1.2 > 10.2.2.2 -ip #0 20(20) 255 10.1.1.0 > 10.3.4.5 -ip #0 20(20) 255 10.1.1.1 > 10.3.4.5 -ip #0 20(20) 255 10.1.1.2 > 10.3.4.5 +> zx0 ip #0 20(20) 255 10.1.1.0 > 10.1.1.2 +> zx0 ip #0 20(20) 255 1.6.7.8 > 10.1.1.2 +> zx0 ip #0 20(20) 255 10.1.1.2 > 10.1.1.1 +> zx0 ip #0 20(20) 255 10.2.2.1 > 10.1.2.1 +> zx0 ip #0 20(20) 255 10.2.2.2 > 10.1.2.1 +< zx0 ip #0 20(20) 255 10.1.1.1 > 10.1.1.2 +< zx0 ip #0 20(20) 255 10.1.1.2 > 10.1.1.1 +< zx0 ip #0 20(20) 255 10.2.2.1 > 10.2.1.1 +< zx0 ip #0 20(20) 255 10.2.2.2 > 10.2.1.1 +< zx0 ip #0 20(20) 255 10.2.2.3 > 10.1.1.1 +< zx0 ip #0 20(20) 255 10.2.3.4 > 10.2.2.2 +< zx0 ip #0 20(20) 255 10.1.1.1 > 10.2.2.2 +< zx0 ip #0 20(20) 255 10.1.1.2 > 10.2.2.2 +< zx0 ip #0 20(20) 255 10.1.1.0 > 10.3.4.5 +< zx0 ip #0 20(20) 255 10.1.1.1 > 10.3.4.5 +< zx0 ip #0 20(20) 255 10.1.1.2 > 10.3.4.5 +List of active MAP/Redirect filters: +bimap zx0 10.1.1.1/32 -> 1.6.7.8/32 + +List of active sessions: +BIMAP 10.1.1.1 <- -> 1.6.7.8 [10.1.1.2] + +Hostmap table: +10.1.1.1,10.1.1.2 -> 1.6.7.8,0.0.0.0 (use = 1) +List of active state sessions: +List of configured pools +List of configured hash tables +List of groups configured (set 0) +List of groups configured (set 1) +Rules configured (set 0, in) +Rules configured (set 0, out) +Rules configured (set 1, in) +Rules configured (set 1, out) +Accounting rules configured (set 0, in) +Accounting rules configured (set 0, out) +Accounting rules configured (set 1, in) +Accounting rules configured (set 1, out) ------------------------------- -ip #0 20(20) 255 10.2.2.2 > 10.1.1.2 -ip #0 20(20) 255 10.1.1.1 > 10.1.1.2 -ip #0 20(20) 255 10.2.2.2 > 10.1.1.1 -ip #0 20(20) 255 10.2.2.1 > 10.1.2.1 -ip #0 20(20) 255 10.2.2.2 > 10.1.2.1 -ip #0 20(20) 255 10.1.1.1 > 10.1.1.2 -ip #0 20(20) 255 10.1.1.2 > 10.1.1.1 -ip #0 20(20) 255 10.2.2.1 > 10.2.1.1 -ip #0 20(20) 255 10.2.2.2 > 10.2.1.1 -ip #0 20(20) 255 10.2.2.3 > 10.1.1.1 -ip #0 20(20) 255 10.2.3.4 > 10.1.1.0 -ip #0 20(20) 255 10.1.1.1 > 10.1.1.2 -ip #0 20(20) 255 10.1.1.2 > 10.1.1.0 -ip #0 20(20) 255 10.1.1.0 > 10.3.4.5 -ip #0 20(20) 255 10.1.1.1 > 10.3.4.5 -ip #0 20(20) 255 10.1.1.2 > 10.3.4.5 +> zx0 ip #0 20(20) 255 10.2.2.2 > 10.1.1.2 +15 +> zx0 ip #0 20(20) 255 10.2.2.2 > 10.1.1.1 +> zx0 ip #0 20(20) 255 10.2.2.1 > 10.1.2.1 +> zx0 ip #0 20(20) 255 10.2.2.2 > 10.1.2.1 +< zx0 ip #0 20(20) 255 10.1.1.1 > 10.1.1.2 +< zx0 ip #0 20(20) 255 10.1.1.2 > 10.1.1.1 +< zx0 ip #0 20(20) 255 10.2.2.1 > 10.2.1.1 +< zx0 ip #0 20(20) 255 10.2.2.2 > 10.2.1.1 +< zx0 ip #0 20(20) 255 10.2.2.3 > 10.1.1.1 +< zx0 ip #0 20(20) 255 10.2.3.4 > 10.1.1.0 +< zx0 ip #0 20(20) 255 10.1.1.1 > 10.1.1.2 +< zx0 ip #0 20(20) 255 10.1.1.2 > 10.1.1.0 +< zx0 ip #0 20(20) 255 10.1.1.0 > 10.3.4.5 +< zx0 ip #0 20(20) 255 10.1.1.1 > 10.3.4.5 +< zx0 ip #0 20(20) 255 10.1.1.2 > 10.3.4.5 +List of active MAP/Redirect filters: +bimap zx0 10.1.1.0/24 -> 10.2.2.2/32 + +List of active sessions: +BIMAP 10.1.1.0 <- -> 10.2.2.2 [10.2.3.4] +BIMAP 10.1.1.2 <- -> 10.2.2.2 [10.1.1.1] +BIMAP 10.1.1.0 <- -> 10.2.2.2 [10.1.1.2] + +Hostmap table: +10.1.1.2,10.1.1.1 -> 10.2.2.2,0.0.0.0 (use = 1) +10.1.1.0,10.1.1.2 -> 10.2.2.2,0.0.0.0 (use = 1) +List of active state sessions: +List of configured pools +List of configured hash tables +List of groups configured (set 0) +List of groups configured (set 1) +Rules configured (set 0, in) +Rules configured (set 0, out) +Rules configured (set 1, in) +Rules configured (set 1, out) +Accounting rules configured (set 0, in) +Accounting rules configured (set 0, out) +Accounting rules configured (set 1, in) +Accounting rules configured (set 1, out) ------------------------------- -ip #0 20(20) 255 10.3.4.0 > 10.1.1.2 -ip #0 20(20) 255 10.3.4.1 > 10.1.1.2 -ip #0 20(20) 255 10.3.4.2 > 10.1.1.1 -ip #0 20(20) 255 10.2.2.1 > 10.1.2.1 -ip #0 20(20) 255 10.2.2.2 > 10.1.2.1 -ip #0 20(20) 255 10.1.1.1 > 10.1.1.2 -ip #0 20(20) 255 10.1.1.2 > 10.1.1.1 -ip #0 20(20) 255 10.2.2.1 > 10.2.1.1 -ip #0 20(20) 255 10.2.2.2 > 10.2.1.1 -ip #0 20(20) 255 10.2.2.3 > 10.1.1.1 -ip #0 20(20) 255 10.2.3.4 > 10.2.2.2 -ip #0 20(20) 255 10.1.1.1 > 10.2.2.2 -ip #0 20(20) 255 10.1.1.2 > 10.2.2.2 -ip #0 20(20) 255 10.1.1.0 > 10.1.1.5 -ip #0 20(20) 255 10.1.1.1 > 10.1.1.5 -ip #0 20(20) 255 10.1.1.2 > 10.1.1.5 +> zx0 ip #0 20(20) 255 10.3.4.0 > 10.1.1.2 +> zx0 ip #0 20(20) 255 10.3.4.1 > 10.1.1.2 +> zx0 ip #0 20(20) 255 10.3.4.2 > 10.1.1.1 +> zx0 ip #0 20(20) 255 10.2.2.1 > 10.1.2.1 +> zx0 ip #0 20(20) 255 10.2.2.2 > 10.1.2.1 +< zx0 ip #0 20(20) 255 10.1.1.1 > 10.1.1.2 +< zx0 ip #0 20(20) 255 10.1.1.2 > 10.1.1.1 +< zx0 ip #0 20(20) 255 10.2.2.1 > 10.2.1.1 +< zx0 ip #0 20(20) 255 10.2.2.2 > 10.2.1.1 +< zx0 ip #0 20(20) 255 10.2.2.3 > 10.1.1.1 +< zx0 ip #0 20(20) 255 10.2.3.4 > 10.2.2.2 +< zx0 ip #0 20(20) 255 10.1.1.1 > 10.2.2.2 +< zx0 ip #0 20(20) 255 10.1.1.2 > 10.2.2.2 +< zx0 ip #0 20(20) 255 10.1.1.0 > 10.1.1.5 +< zx0 ip #0 20(20) 255 10.1.1.1 > 10.1.1.5 +< zx0 ip #0 20(20) 255 10.1.1.2 > 10.1.1.5 +List of active MAP/Redirect filters: +bimap zx0 10.1.1.0/24 -> 10.3.4.0/24 + +List of active sessions: +BIMAP 10.1.1.5 <- -> 10.3.4.5 [10.1.1.2] +BIMAP 10.1.1.5 <- -> 10.3.4.5 [10.1.1.1] +BIMAP 10.1.1.5 <- -> 10.3.4.5 [10.1.1.0] +BIMAP 10.1.1.2 <- -> 10.3.4.2 [10.1.1.1] +BIMAP 10.1.1.1 <- -> 10.3.4.1 [10.1.1.2] +BIMAP 10.1.1.0 <- -> 10.3.4.0 [10.1.1.2] + +Hostmap table: +10.1.1.2,10.1.1.1 -> 10.3.4.1,0.0.0.0 (use = 1) +10.1.1.1,10.1.1.2 -> 10.3.4.1,0.0.0.0 (use = 1) +10.1.1.0,10.1.1.2 -> 10.3.4.1,0.0.0.0 (use = 1) +List of active state sessions: +List of configured pools +List of configured hash tables +List of groups configured (set 0) +List of groups configured (set 1) +Rules configured (set 0, in) +Rules configured (set 0, out) +Rules configured (set 1, in) +Rules configured (set 1, out) +Accounting rules configured (set 0, in) +Accounting rules configured (set 0, out) +Accounting rules configured (set 1, in) +Accounting rules configured (set 1, out) ------------------------------- diff --git a/contrib/ipfilter/test/expected/n11_6 b/contrib/ipfilter/test/expected/n11_6 new file mode 100644 index 00000000000..f1c80de81ba --- /dev/null +++ b/contrib/ipfilter/test/expected/n11_6 @@ -0,0 +1,124 @@ +> zx0 ip6/0 1 0 255 10:1:1:0:0:0:0:0 > 10:1:1:0:0:0:0:2 +> zx0 ip6/0 1 0 255 1:0:0:0:0:6:7:8 > 10:1:1:0:0:0:0:2 +> zx0 ip6/0 1 0 255 10:1:1:0:0:0:0:2 > 10:1:1:0:0:0:0:1 +> zx0 ip6/0 1 0 255 10:0:0:0:0:2:2:1 > 10:1:2:0:0:0:0:1 +> zx0 ip6/0 1 0 255 10:0:0:0:0:2:2:2 > 10:1:2:0:0:0:0:1 +< zx0 ip6/0 1 0 255 10:1:1:0:0:0:0:1 > 10:1:1:0:0:0:0:2 +< zx0 ip6/0 1 0 255 10:1:1:0:0:0:0:2 > 10:1:1:0:0:0:0:1 +< zx0 ip6/0 1 0 255 10:0:0:0:0:2:2:1 > 10:0:0:0:0:2:1:1 +< zx0 ip6/0 1 0 255 10:0:0:0:0:2:2:2 > 10:0:0:0:0:2:1:1 +< zx0 ip6/0 1 0 255 10:0:0:0:0:2:2:3 > 10:1:1:0:0:0:0:1 +< zx0 ip6/0 1 0 255 10:0:0:0:0:2:3:4 > 10:0:0:0:0:2:2:2 +< zx0 ip6/0 1 0 255 10:1:1:0:0:0:0:1 > 10:0:0:0:0:2:2:2 +< zx0 ip6/0 1 0 255 10:1:1:0:0:0:0:2 > 10:0:0:0:0:2:2:2 +< zx0 ip6/0 1 0 255 10:1:1:0:0:0:0:0 > 10:0:0:0:0:3:4:5 +< zx0 ip6/0 1 0 255 10:1:1:0:0:0:0:1 > 10:0:0:0:0:3:4:5 +< zx0 ip6/0 1 0 255 10:1:1:0:0:0:0:2 > 10:0:0:0:0:3:4:5 +List of active MAP/Redirect filters: +bimap zx0 inet6 10:1:1::1/128 -> 1::6:7:8/128 + +List of active sessions: +BIMAP 10:1:1::1 <- -> 1::6:7:8 [10:1:1::2] + +Hostmap table: +10:1:1::1,10:1:1::2 -> 1::6:7:8,any (use = 1) +List of active state sessions: +List of configured pools +List of configured hash tables +List of groups configured (set 0) +List of groups configured (set 1) +Rules configured (set 0, in) +Rules configured (set 0, out) +Rules configured (set 1, in) +Rules configured (set 1, out) +Accounting rules configured (set 0, in) +Accounting rules configured (set 0, out) +Accounting rules configured (set 1, in) +Accounting rules configured (set 1, out) +------------------------------- +> zx0 ip6/0 1 0 255 10:0:0:0:0:2:2:2 > 10:1:1:0:0:0:0:2 +16 +> zx0 ip6/0 1 0 255 10:0:0:0:0:2:2:2 > 10:1:1:0:0:0:0:1 +> zx0 ip6/0 1 0 255 10:0:0:0:0:2:2:1 > 10:1:2:0:0:0:0:1 +> zx0 ip6/0 1 0 255 10:0:0:0:0:2:2:2 > 10:1:2:0:0:0:0:1 +< zx0 ip6/0 1 0 255 10:1:1:0:0:0:0:1 > 10:1:1:0:0:0:0:2 +< zx0 ip6/0 1 0 255 10:1:1:0:0:0:0:2 > 10:1:1:0:0:0:0:1 +< zx0 ip6/0 1 0 255 10:0:0:0:0:2:2:1 > 10:0:0:0:0:2:1:1 +< zx0 ip6/0 1 0 255 10:0:0:0:0:2:2:2 > 10:0:0:0:0:2:1:1 +< zx0 ip6/0 1 0 255 10:0:0:0:0:2:2:3 > 10:1:1:0:0:0:0:1 +< zx0 ip6/0 1 0 255 10:0:0:0:0:2:3:4 > 10:1:1:0:0:0:0:0 +< zx0 ip6/0 1 0 255 10:1:1:0:0:0:0:1 > 10:1:1:0:0:0:0:2 +< zx0 ip6/0 1 0 255 10:1:1:0:0:0:0:2 > 10:1:1:0:0:0:0:0 +< zx0 ip6/0 1 0 255 10:1:1:0:0:0:0:0 > 10:0:0:0:0:3:4:5 +< zx0 ip6/0 1 0 255 10:1:1:0:0:0:0:1 > 10:0:0:0:0:3:4:5 +< zx0 ip6/0 1 0 255 10:1:1:0:0:0:0:2 > 10:0:0:0:0:3:4:5 +List of active MAP/Redirect filters: +bimap zx0 inet6 10:1:1::/112 -> 10::2:2:2/128 + +List of active sessions: +BIMAP 10:1:1:: <- -> 10::2:2:2 [10::2:3:4] +BIMAP 10:1:1::2 <- -> 10::2:2:2 [10:1:1::1] +BIMAP 10:1:1:: <- -> 10::2:2:2 [10:1:1::2] + +Hostmap table: +10:1:1::2,10:1:1::1 -> 10::2:2:2,any (use = 1) +10:1:1::,10:1:1::2 -> 10::2:2:2,any (use = 1) +List of active state sessions: +List of configured pools +List of configured hash tables +List of groups configured (set 0) +List of groups configured (set 1) +Rules configured (set 0, in) +Rules configured (set 0, out) +Rules configured (set 1, in) +Rules configured (set 1, out) +Accounting rules configured (set 0, in) +Accounting rules configured (set 0, out) +Accounting rules configured (set 1, in) +Accounting rules configured (set 1, out) +------------------------------- +> zx0 ip6/0 1 0 255 10:0:0:0:0:3:4:0 > 10:1:1:0:0:0:0:2 +> zx0 ip6/0 1 0 255 10:0:0:0:0:3:4:1 > 10:1:1:0:0:0:0:2 +> zx0 ip6/0 1 0 255 10:0:0:0:0:3:4:2 > 10:1:1:0:0:0:0:1 +> zx0 ip6/0 1 0 255 10:0:0:0:0:2:2:1 > 10:1:2:0:0:0:0:1 +> zx0 ip6/0 1 0 255 10:0:0:0:0:2:2:2 > 10:1:2:0:0:0:0:1 +< zx0 ip6/0 1 0 255 10:1:1:0:0:0:0:1 > 10:1:1:0:0:0:0:2 +< zx0 ip6/0 1 0 255 10:1:1:0:0:0:0:2 > 10:1:1:0:0:0:0:1 +< zx0 ip6/0 1 0 255 10:0:0:0:0:2:2:1 > 10:0:0:0:0:2:1:1 +< zx0 ip6/0 1 0 255 10:0:0:0:0:2:2:2 > 10:0:0:0:0:2:1:1 +< zx0 ip6/0 1 0 255 10:0:0:0:0:2:2:3 > 10:1:1:0:0:0:0:1 +< zx0 ip6/0 1 0 255 10:0:0:0:0:2:3:4 > 10:0:0:0:0:2:2:2 +< zx0 ip6/0 1 0 255 10:1:1:0:0:0:0:1 > 10:0:0:0:0:2:2:2 +< zx0 ip6/0 1 0 255 10:1:1:0:0:0:0:2 > 10:0:0:0:0:2:2:2 +< zx0 ip6/0 1 0 255 10:1:1:0:0:0:0:0 > 10:1:1:0:0:0:0:5 +< zx0 ip6/0 1 0 255 10:1:1:0:0:0:0:1 > 10:1:1:0:0:0:0:5 +< zx0 ip6/0 1 0 255 10:1:1:0:0:0:0:2 > 10:1:1:0:0:0:0:5 +List of active MAP/Redirect filters: +bimap zx0 inet6 10:1:1::/112 -> 10::3:4:0/112 + +List of active sessions: +BIMAP 10:1:1::5 <- -> 10::3:4:5 [10:1:1::2] +BIMAP 10:1:1::5 <- -> 10::3:4:5 [10:1:1::1] +BIMAP 10:1:1::5 <- -> 10::3:4:5 [10:1:1::] +BIMAP 10:1:1::2 <- -> 10::3:4:2 [10:1:1::1] +BIMAP 10:1:1::1 <- -> 10::3:4:1 [10:1:1::2] +BIMAP 10:1:1:: <- -> 10::3:4:0 [10:1:1::2] + +Hostmap table: +10:1:1::2,10:1:1::1 -> 10::3:4:1,any (use = 1) +10:1:1::1,10:1:1::2 -> 10::3:4:1,any (use = 1) +10:1:1::,10:1:1::2 -> 10::3:4:1,any (use = 1) +List of active state sessions: +List of configured pools +List of configured hash tables +List of groups configured (set 0) +List of groups configured (set 1) +Rules configured (set 0, in) +Rules configured (set 0, out) +Rules configured (set 1, in) +Rules configured (set 1, out) +Accounting rules configured (set 0, in) +Accounting rules configured (set 0, out) +Accounting rules configured (set 1, in) +Accounting rules configured (set 1, out) +------------------------------- diff --git a/contrib/ipfilter/test/expected/n12 b/contrib/ipfilter/test/expected/n12 index 0d5cefbf7e7..56b3a781c9f 100644 --- a/contrib/ipfilter/test/expected/n12 +++ b/contrib/ipfilter/test/expected/n12 @@ -4,4 +4,25 @@ 4510 0034 493b 4000 4006 6b69 c0a8 01bc c0a8 0303 2710 0017 4e33 298f f674 e02d 8010 4000 f673 0000 0101 080a 0c72 549e 2c05 b797 +List of active MAP/Redirect filters: +map le0 192.168.126.0/24 -> 0/32 portmap tcp/udp 10000:20000 sequential + +List of active sessions: +MAP 192.168.126.83 4802 <- -> 192.168.1.188 10000 [192.168.3.3 23] + +Hostmap table: +192.168.126.83,192.168.3.3 -> 0.0.0.0,0.0.0.0 (use = 1) +List of active state sessions: +List of configured pools +List of configured hash tables +List of groups configured (set 0) +List of groups configured (set 1) +Rules configured (set 0, in) +Rules configured (set 0, out) +Rules configured (set 1, in) +Rules configured (set 1, out) +Accounting rules configured (set 0, in) +Accounting rules configured (set 0, out) +Accounting rules configured (set 1, in) +Accounting rules configured (set 1, out) ------------------------------- diff --git a/contrib/ipfilter/test/expected/n12_6 b/contrib/ipfilter/test/expected/n12_6 new file mode 100644 index 00000000000..9ef040aaafd --- /dev/null +++ b/contrib/ipfilter/test/expected/n12_6 @@ -0,0 +1,28 @@ +6000 0000 002c 0640 c0a8 0100 0000 0000 0000 0000 0000 00bc c0a8 0300 0000 0000 0000 0000 0000 0003 2710 0017 4e33 298e 0000 0000 b002 4000 6ff8 0000 0204 05b4 0101 0402 0103 0300 0101 080a 0c72 549e 0000 0000 + +6000 0000 0028 06fe c0a8 0300 0000 0000 0000 0000 0000 0003 c0a8 7e00 0000 0000 0000 0000 0000 0053 0017 12c2 f674 e02c 4e33 298f a012 2798 7ace 0000 0101 080a 2c05 b797 0c72 549e 0103 0300 0204 05b4 + +6000 0000 0020 0640 c0a8 0100 0000 0000 0000 0000 0000 00bc c0a8 0300 0000 0000 0000 0000 0000 0003 2710 0017 4e33 298f f674 e02d 8010 4000 f673 0000 0101 080a 0c72 549e 2c05 b797 + +List of active MAP/Redirect filters: +map le0 inet6 c0a8:7e00::/112 -> ::/128 portmap tcp/udp 10000:20000 + +List of active sessions: +MAP c0a8:7e00::53 4802 <- -> c0a8:100::bc 10000 [c0a8:300::3 23] + +Hostmap table: +c0a8:7e00::53,c0a8:300::3 -> any,any (use = 1) +List of active state sessions: +List of configured pools +List of configured hash tables +List of groups configured (set 0) +List of groups configured (set 1) +Rules configured (set 0, in) +Rules configured (set 0, out) +Rules configured (set 1, in) +Rules configured (set 1, out) +Accounting rules configured (set 0, in) +Accounting rules configured (set 0, out) +Accounting rules configured (set 1, in) +Accounting rules configured (set 1, out) +------------------------------- diff --git a/contrib/ipfilter/test/expected/n13 b/contrib/ipfilter/test/expected/n13 index bfe20188652..e6d26b2a548 100644 --- a/contrib/ipfilter/test/expected/n13 +++ b/contrib/ipfilter/test/expected/n13 @@ -1,5 +1,32 @@ -ip #0 20(20) 0 203.1.1.23 > 150.1.1.1 -ip #0 20(20) 0 203.1.1.23 > 150.1.1.2 -ip #0 20(20) 0 203.1.1.24 > 150.1.1.2 -ip #0 20(20) 0 203.1.1.25 > 150.1.1.1 +> le0 ip #0 20(20) 0 203.1.1.23 > 150.1.1.1 +> le0 ip #0 20(20) 0 203.1.1.23 > 150.1.1.2 +> le0 ip #0 20(20) 0 203.1.1.24 > 150.1.1.2 +> le0 ip #0 20(20) 0 203.1.1.25 > 150.1.1.1 +List of active MAP/Redirect filters: +map le0 192.168.0.0/16 -> range 203.1.1.23-203.1.3.45 + +List of active sessions: +MAP 192.168.1.3 <- -> 203.1.1.25 [150.1.1.1] +MAP 192.168.1.2 <- -> 203.1.1.24 [150.1.1.2] +MAP 192.168.1.1 <- -> 203.1.1.23 [150.1.1.2] +MAP 192.168.1.1 <- -> 203.1.1.23 [150.1.1.1] + +Hostmap table: +192.168.1.3,150.1.1.1 -> 203.1.1.25,0.0.0.0 (use = 1) +192.168.1.2,150.1.1.2 -> 203.1.1.24,0.0.0.0 (use = 1) +192.168.1.1,150.1.1.2 -> 203.1.1.23,0.0.0.0 (use = 1) +192.168.1.1,150.1.1.1 -> 203.1.1.23,0.0.0.0 (use = 1) +List of active state sessions: +List of configured pools +List of configured hash tables +List of groups configured (set 0) +List of groups configured (set 1) +Rules configured (set 0, in) +Rules configured (set 0, out) +Rules configured (set 1, in) +Rules configured (set 1, out) +Accounting rules configured (set 0, in) +Accounting rules configured (set 0, out) +Accounting rules configured (set 1, in) +Accounting rules configured (set 1, out) ------------------------------- diff --git a/contrib/ipfilter/test/expected/n13_6 b/contrib/ipfilter/test/expected/n13_6 new file mode 100644 index 00000000000..d3b5fe7fb99 --- /dev/null +++ b/contrib/ipfilter/test/expected/n13_6 @@ -0,0 +1,32 @@ +> le0 ip6/0 1 0 41 203:0:1:0:0:0:1:23 > 150.1.1.1 +> le0 ip6/0 1 0 41 203:0:1:0:0:0:1:23 > 150.1.1.2 +> le0 ip6/0 1 0 41 203:0:1:0:0:0:1:24 > 150.1.1.2 +> le0 ip6/0 1 0 41 203:0:1:0:0:0:1:25 > 150.1.1.1 +List of active MAP/Redirect filters: +map le0 inet6 192:168:0::0/48 -> range 203:0:1::1:23-203:0:1::3:45 + +List of active sessions: +MAP 192.168.1.3 <- -> 203:0:1::1:25 [150.1.1.1] +MAP 192.168.1.2 <- -> 203:0:1::1:24 [150.1.1.2] +MAP 192.168.1.1 <- -> 203:0:1::1:23 [150.1.1.2] +MAP 192.168.1.1 <- -> 203:0:1::1:23 [150.1.1.1] + +Hostmap table: +192.168.1.3,150.1.1.1 -> 203:0:1:0:0:0:1:25,any (use = 1) +192.168.1.2,150.1.1.2 -> 203:0:1:0:0:0:1:24,any (use = 1) +192.168.1.1,150.1.1.2 -> 203:0:1:0:0:0:1:23,any (use = 1) +192.168.1.1,150.1.1.1 -> 203:0:1:0:0:0:1:23,any (use = 1) +List of active state sessions: +List of configured pools +List of configured hash tables +List of groups configured (set 0) +List of groups configured (set 1) +Rules configured (set 0, in) +Rules configured (set 0, out) +Rules configured (set 1, in) +Rules configured (set 1, out) +Accounting rules configured (set 0, in) +Accounting rules configured (set 0, out) +Accounting rules configured (set 1, in) +Accounting rules configured (set 1, out) +------------------------------- diff --git a/contrib/ipfilter/test/expected/n14 b/contrib/ipfilter/test/expected/n14 index 46693001a44..7b1a19ea931 100644 --- a/contrib/ipfilter/test/expected/n14 +++ b/contrib/ipfilter/test/expected/n14 @@ -1,5 +1,30 @@ -ip #0 40(20) 6 10.2.2.5,2000 > 10.1.1.254,80 -ip #0 40(20) 6 10.2.2.6,2000 > 10.1.1.253,80 -ip #0 40(20) 6 10.2.2.7,2000 > 10.1.1.254,80 -ip #0 40(20) 6 10.2.2.5,2001 > 10.1.1.254,80 +< gre0 ip #0 40(20) 6 10.2.2.5,2000 > 10.1.1.254,80 +< gre0 ip #0 40(20) 6 10.2.2.6,2000 > 10.1.1.253,80 +< gre0 ip #0 40(20) 6 10.2.2.7,2000 > 10.1.1.254,80 +15 +List of active MAP/Redirect filters: +rdr gre0 0/0 port 80 -> 10.1.1.254,10.1.1.253 port 80 tcp sticky + +List of active sessions: +RDR 10.1.1.254 80 <- -> 203.1.1.1 80 [10.2.2.7 2000] +RDR 10.1.1.253 80 <- -> 203.1.1.1 80 [10.2.2.6 2000] +RDR 10.1.1.254 80 <- -> 203.1.1.1 80 [10.2.2.5 2000] + +Hostmap table: +10.2.2.7,203.1.1.1 -> 254.1.1.10,0.0.0.0 (use = 1) +10.2.2.6,203.1.1.1 -> 253.1.1.10,0.0.0.0 (use = 1) +10.2.2.5,203.1.1.1 -> 254.1.1.10,0.0.0.0 (use = 1) +List of active state sessions: +List of configured pools +List of configured hash tables +List of groups configured (set 0) +List of groups configured (set 1) +Rules configured (set 0, in) +Rules configured (set 0, out) +Rules configured (set 1, in) +Rules configured (set 1, out) +Accounting rules configured (set 0, in) +Accounting rules configured (set 0, out) +Accounting rules configured (set 1, in) +Accounting rules configured (set 1, out) ------------------------------- diff --git a/contrib/ipfilter/test/expected/n14_6 b/contrib/ipfilter/test/expected/n14_6 new file mode 100644 index 00000000000..b999ee971d8 --- /dev/null +++ b/contrib/ipfilter/test/expected/n14_6 @@ -0,0 +1,30 @@ +< gre0 ip6/0 20 0 6 10:0:0:0:0:2:2:5,2000 > 10:1:1:0:0:0:0:254,80 +< gre0 ip6/0 20 0 6 10:0:0:0:0:2:2:6,2000 > 10:1:1:0:0:0:0:253,80 +< gre0 ip6/0 20 0 6 10:0:0:0:0:2:2:7,2000 > 10:1:1:0:0:0:0:254,80 +< gre0 ip6/0 20 0 6 10:0:0:0:0:2:2:5,2001 > 203:0:1:0:0:0:1:1,80 +List of active MAP/Redirect filters: +rdr gre0 inet6 any port 80 -> 10:1:1::254,10:1:1::253 port 80 tcp sticky + +List of active sessions: +RDR 10:1:1::254 80 <- -> 203:0:1::1:1 80 [10::2:2:7 2000] +RDR 10:1:1::253 80 <- -> 203:0:1::1:1 80 [10::2:2:6 2000] +RDR 10:1:1::254 80 <- -> 203:0:1::1:1 80 [10::2:2:5 2000] + +Hostmap table: +10::2:2:7,203:0:1:0:0:0:1:1 -> 254:1:1::10,any (use = 1) +10::2:2:6,203:0:1:0:0:0:1:1 -> 253:0:1:0:0:0:1:10,any (use = 1) +10::2:2:5,203:0:1:0:0:0:1:1 -> 254:1:1::10,any (use = 3) +List of active state sessions: +List of configured pools +List of configured hash tables +List of groups configured (set 0) +List of groups configured (set 1) +Rules configured (set 0, in) +Rules configured (set 0, out) +Rules configured (set 1, in) +Rules configured (set 1, out) +Accounting rules configured (set 0, in) +Accounting rules configured (set 0, out) +Accounting rules configured (set 1, in) +Accounting rules configured (set 1, out) +------------------------------- diff --git a/contrib/ipfilter/test/expected/n15 b/contrib/ipfilter/test/expected/n15 new file mode 100644 index 00000000000..3889f82081d --- /dev/null +++ b/contrib/ipfilter/test/expected/n15 @@ -0,0 +1,47 @@ +< le0 ip #0 40(20) 6 9.9.9.9,10011 > 3.3.3.3,80 +15 +List of active MAP/Redirect filters: +rdr le0 0/0 port 80 -> 3.3.3.3/32 port 80 tcp + +List of active sessions: +RDR 3.3.3.3 80 <- -> 5.5.5.5 80 [9.9.9.9 10011] + +Hostmap table: +List of active state sessions: +List of configured pools +List of configured hash tables +List of groups configured (set 0) +List of groups configured (set 1) +Rules configured (set 0, in) +Rules configured (set 0, out) +Rules configured (set 1, in) +Rules configured (set 1, out) +Accounting rules configured (set 0, in) +Accounting rules configured (set 0, out) +Accounting rules configured (set 1, in) +Accounting rules configured (set 1, out) +------------------------------- +< le0 ip #0 40(20) 6 9.9.9.9,10011 > 3.3.3.3,80 +< le0 ip #0 40(20) 6 9.9.9.9,10011 > 3.3.3.3,81 +List of active MAP/Redirect filters: +rdr le0 0/0 port 80 -> 3.3.3.3/32 port 80-88 tcp + +List of active sessions: +RDR 3.3.3.3 81 <- -> 2.2.2.2 80 [9.9.9.9 10011] +RDR 3.3.3.3 80 <- -> 5.5.5.5 80 [9.9.9.9 10011] + +Hostmap table: +List of active state sessions: +List of configured pools +List of configured hash tables +List of groups configured (set 0) +List of groups configured (set 1) +Rules configured (set 0, in) +Rules configured (set 0, out) +Rules configured (set 1, in) +Rules configured (set 1, out) +Accounting rules configured (set 0, in) +Accounting rules configured (set 0, out) +Accounting rules configured (set 1, in) +Accounting rules configured (set 1, out) +------------------------------- diff --git a/contrib/ipfilter/test/expected/n15_6 b/contrib/ipfilter/test/expected/n15_6 new file mode 100644 index 00000000000..f01b72b6805 --- /dev/null +++ b/contrib/ipfilter/test/expected/n15_6 @@ -0,0 +1,47 @@ +< le0 ip6/0 20 0 6 9:9:9:0:0:0:0:9,10011 > 3:0:3:0:0:0:3:3,80 +16 +List of active MAP/Redirect filters: +rdr le0 inet6 any port 80 -> 3:0:3::3:3/128 port 80 tcp + +List of active sessions: +RDR 3:0:3::3:3 80 <- -> 5:5::5:5 80 [9:9:9::9 10011] + +Hostmap table: +List of active state sessions: +List of configured pools +List of configured hash tables +List of groups configured (set 0) +List of groups configured (set 1) +Rules configured (set 0, in) +Rules configured (set 0, out) +Rules configured (set 1, in) +Rules configured (set 1, out) +Accounting rules configured (set 0, in) +Accounting rules configured (set 0, out) +Accounting rules configured (set 1, in) +Accounting rules configured (set 1, out) +------------------------------- +< le0 ip6/0 20 0 6 9:9:9:0:0:0:0:9,10011 > 3:0:3:0:0:0:3:3,80 +< le0 ip6/0 20 0 6 9:9:9:0:0:0:0:9,10011 > 3:0:3:0:0:0:3:3,81 +List of active MAP/Redirect filters: +rdr le0 inet6 any port 80 -> 3:0:3::3:3/128 port 80-88 tcp + +List of active sessions: +RDR 3:0:3::3:3 81 <- -> 2::2:2:2 80 [9:9:9::9 10011] +RDR 3:0:3::3:3 80 <- -> 5:5::5:5 80 [9:9:9::9 10011] + +Hostmap table: +List of active state sessions: +List of configured pools +List of configured hash tables +List of groups configured (set 0) +List of groups configured (set 1) +Rules configured (set 0, in) +Rules configured (set 0, out) +Rules configured (set 1, in) +Rules configured (set 1, out) +Accounting rules configured (set 0, in) +Accounting rules configured (set 0, out) +Accounting rules configured (set 1, in) +Accounting rules configured (set 1, out) +------------------------------- diff --git a/contrib/ipfilter/test/expected/n16 b/contrib/ipfilter/test/expected/n16 index da617d9d360..0eb3954e857 100644 --- a/contrib/ipfilter/test/expected/n16 +++ b/contrib/ipfilter/test/expected/n16 @@ -7,7 +7,7 @@ 4500 0084 ee0f 0000 8001 4a21 45f8 4fc1 c05b ac33 0303 bf85 0000 0000 4520 0068 17e4 0000 6a11 3639 c05b ac33 45f8 4fc1 1194 94f8 0054 0000 a5a5 a5a5 a5a5 a5a5 a5a5 a5a5 a5a5 a5a5 a5a5 a5a5 a5a5 a5a5 a5a5 a5a5 a5a5 a5a5 a5a5 a5a5 a5a5 a5a5 a5a5 a5a5 a5a5 a5a5 a5a5 a5a5 a5a5 a5a5 a5a5 a5a5 a5a5 a5a5 a5a5 a5a5 a5a5 a5a5 a5a5 a5a5 List of active MAP/Redirect filters: -rdr vlan0 from any to 69.248.79.193/32 port = 38136 -> 172.31.83.24 port 2013 udp +rdr vlan0 from 0/0 to 69.248.79.193/32 port = 38136 -> 172.31.83.24/32 port 2013 udp List of active sessions: RDR 172.31.83.24 2013 <- -> 69.248.79.193 38136 [192.91.172.51 4500] @@ -18,4 +18,12 @@ List of configured pools List of configured hash tables List of groups configured (set 0) List of groups configured (set 1) +Rules configured (set 0, in) +Rules configured (set 0, out) +Rules configured (set 1, in) +Rules configured (set 1, out) +Accounting rules configured (set 0, in) +Accounting rules configured (set 0, out) +Accounting rules configured (set 1, in) +Accounting rules configured (set 1, out) ------------------------------- diff --git a/contrib/ipfilter/test/expected/n17 b/contrib/ipfilter/test/expected/n17 new file mode 100644 index 00000000000..f336bb0fb8d --- /dev/null +++ b/contrib/ipfilter/test/expected/n17 @@ -0,0 +1,24 @@ +4500 00a0 0000 0100 3f06 7555 0101 0101 0201 0101 0401 0019 0000 0000 0000 0000 5010 2000 86b7 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 + +4500 00a0 0000 0100 3f06 7553 0201 0101 0101 0103 0401 0019 0000 0000 0000 0000 5010 2000 86b7 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 + +List of active MAP/Redirect filters: +bimap zx0 0/0 -> 1.1.1.3/32 + +List of active sessions: + +Hostmap table: +List of active state sessions: +List of configured pools +List of configured hash tables +List of groups configured (set 0) +List of groups configured (set 1) +Rules configured (set 0, in) +Rules configured (set 0, out) +Rules configured (set 1, in) +Rules configured (set 1, out) +Accounting rules configured (set 0, in) +Accounting rules configured (set 0, out) +Accounting rules configured (set 1, in) +Accounting rules configured (set 1, out) +------------------------------- diff --git a/contrib/ipfilter/test/expected/n18 b/contrib/ipfilter/test/expected/n18 new file mode 100644 index 00000000000..c51c11c9a52 --- /dev/null +++ b/contrib/ipfilter/test/expected/n18 @@ -0,0 +1,111 @@ +> z0 ip #0 40(20) 6 1.1.1.1,1 > 3.3.3.3,30 +> z0 ip #0 40(20) 6 1.1.1.1,2 > 3.3.3.3,31 +> z0 ip #0 40(20) 6 1.1.1.1,3 > 3.3.3.3,32 +> z0 ip #0 40(20) 6 1.1.1.1,4 > 3.3.3.3,33 +> z0 ip #0 40(20) 6 1.1.1.1,1 > 3.3.3.3,34 +> z0 ip #0 40(20) 6 1.1.1.1,2 > 3.3.3.3,35 +> z0 ip #0 40(20) 6 1.1.1.1,3 > 3.3.3.3,36 +> z0 ip #0 40(20) 6 1.1.1.1,4 > 3.3.3.3,37 +List of active MAP/Redirect filters: +map z0 0/0 -> 1.1.1.1/32 portmap tcp/udp 1:4 sequential + +List of active sessions: +MAP 2.2.2.2 29 <- -> 1.1.1.1 4 [3.3.3.3 37] +MAP 2.2.2.2 28 <- -> 1.1.1.1 3 [3.3.3.3 36] +MAP 2.2.2.2 27 <- -> 1.1.1.1 2 [3.3.3.3 35] +MAP 2.2.2.2 26 <- -> 1.1.1.1 1 [3.3.3.3 34] +MAP 2.2.2.2 25 <- -> 1.1.1.1 4 [3.3.3.3 33] +MAP 2.2.2.2 24 <- -> 1.1.1.1 3 [3.3.3.3 32] +MAP 2.2.2.2 23 <- -> 1.1.1.1 2 [3.3.3.3 31] +MAP 2.2.2.2 22 <- -> 1.1.1.1 1 [3.3.3.3 30] + +Hostmap table: +2.2.2.2,3.3.3.3 -> 1.1.1.1,0.0.0.0 (use = 8) +List of active state sessions: +List of configured pools +List of configured hash tables +List of groups configured (set 0) +List of groups configured (set 1) +Rules configured (set 0, in) +Rules configured (set 0, out) +Rules configured (set 1, in) +Rules configured (set 1, out) +Accounting rules configured (set 0, in) +Accounting rules configured (set 0, out) +Accounting rules configured (set 1, in) +Accounting rules configured (set 1, out) +------------------------------- +> z0 ip #0 40(20) 6 1.1.1.1,1000 > 3.3.3.3,30 +> z0 ip #0 40(20) 6 1.1.1.1,1001 > 3.3.3.3,31 +> z0 ip #0 40(20) 6 1.1.1.1,1002 > 3.3.3.3,32 +> z0 ip #0 40(20) 6 1.1.1.1,1003 > 3.3.3.3,33 +> z0 ip #0 40(20) 6 1.1.1.1,1004 > 3.3.3.3,34 +> z0 ip #0 40(20) 6 1.1.1.1,1005 > 3.3.3.3,35 +> z0 ip #0 40(20) 6 1.1.1.1,1006 > 3.3.3.3,36 +> z0 ip #0 40(20) 6 1.1.1.1,1007 > 3.3.3.3,37 +List of active MAP/Redirect filters: +map z0 0/0 -> 1.1.1.1/32 portmap tcp/udp 1000:5000 sequential + +List of active sessions: +MAP 2.2.2.2 29 <- -> 1.1.1.1 1007 [3.3.3.3 37] +MAP 2.2.2.2 28 <- -> 1.1.1.1 1006 [3.3.3.3 36] +MAP 2.2.2.2 27 <- -> 1.1.1.1 1005 [3.3.3.3 35] +MAP 2.2.2.2 26 <- -> 1.1.1.1 1004 [3.3.3.3 34] +MAP 2.2.2.2 25 <- -> 1.1.1.1 1003 [3.3.3.3 33] +MAP 2.2.2.2 24 <- -> 1.1.1.1 1002 [3.3.3.3 32] +MAP 2.2.2.2 23 <- -> 1.1.1.1 1001 [3.3.3.3 31] +MAP 2.2.2.2 22 <- -> 1.1.1.1 1000 [3.3.3.3 30] + +Hostmap table: +2.2.2.2,3.3.3.3 -> 1.1.1.1,0.0.0.0 (use = 8) +List of active state sessions: +List of configured pools +List of configured hash tables +List of groups configured (set 0) +List of groups configured (set 1) +Rules configured (set 0, in) +Rules configured (set 0, out) +Rules configured (set 1, in) +Rules configured (set 1, out) +Accounting rules configured (set 0, in) +Accounting rules configured (set 0, out) +Accounting rules configured (set 1, in) +Accounting rules configured (set 1, out) +------------------------------- +> z0 ip #0 40(20) 6 1.1.1.1,1000 > 3.3.3.3,30 +> z0 ip #0 40(20) 6 1.1.1.1,1001 > 3.3.3.3,31 +> z0 ip #0 40(20) 6 1.1.1.1,1002 > 3.3.3.3,32 +> z0 ip #0 40(20) 6 1.1.1.1,1003 > 3.3.3.3,33 +> z0 ip #0 40(20) 6 1.1.1.1,1004 > 3.3.3.3,34 +> z0 ip #0 40(20) 6 1.1.1.1,1005 > 3.3.3.3,35 +> z0 ip #0 40(20) 6 1.1.1.1,1006 > 3.3.3.3,36 +> z0 ip #0 40(20) 6 1.1.1.1,1007 > 3.3.3.3,37 +List of active MAP/Redirect filters: +map z0 0/0 -> 1.1.1.1/32 portmap tcp/udp 1000:50000 sequential + +List of active sessions: +MAP 2.2.2.2 29 <- -> 1.1.1.1 1007 [3.3.3.3 37] +MAP 2.2.2.2 28 <- -> 1.1.1.1 1006 [3.3.3.3 36] +MAP 2.2.2.2 27 <- -> 1.1.1.1 1005 [3.3.3.3 35] +MAP 2.2.2.2 26 <- -> 1.1.1.1 1004 [3.3.3.3 34] +MAP 2.2.2.2 25 <- -> 1.1.1.1 1003 [3.3.3.3 33] +MAP 2.2.2.2 24 <- -> 1.1.1.1 1002 [3.3.3.3 32] +MAP 2.2.2.2 23 <- -> 1.1.1.1 1001 [3.3.3.3 31] +MAP 2.2.2.2 22 <- -> 1.1.1.1 1000 [3.3.3.3 30] + +Hostmap table: +2.2.2.2,3.3.3.3 -> 1.1.1.1,0.0.0.0 (use = 8) +List of active state sessions: +List of configured pools +List of configured hash tables +List of groups configured (set 0) +List of groups configured (set 1) +Rules configured (set 0, in) +Rules configured (set 0, out) +Rules configured (set 1, in) +Rules configured (set 1, out) +Accounting rules configured (set 0, in) +Accounting rules configured (set 0, out) +Accounting rules configured (set 1, in) +Accounting rules configured (set 1, out) +------------------------------- diff --git a/contrib/ipfilter/test/expected/n1_6 b/contrib/ipfilter/test/expected/n1_6 new file mode 100644 index 00000000000..347bf4aac4b --- /dev/null +++ b/contrib/ipfilter/test/expected/n1_6 @@ -0,0 +1,197 @@ +> zx0 ip6/0 1 0 255 10:1:1:0:0:0:0:0 > 10:1:1:0:0:0:0:2 +> zx0 ip6/0 1 0 255 10:0:0:0:0:2:2:2 > 10:1:1:0:0:0:0:2 +> zx0 ip6/0 1 0 255 10:1:1:0:0:0:0:2 > 10:1:1:0:0:0:0:1 +> zx0 ip6/0 20 0 6 10:1:1:0:0:0:0:2,1025 > 10:1:1:0:0:0:0:1,1025 +> zx0 ip6/0 20 0 6 10:1:1:0:0:0:0:2,1026 > 10:1:1:0:0:0:0:1,1025 +> zx0 ip6/0 1 0 255 10:0:0:0:0:2:2:1 > 10:1:2:0:0:0:0:1 +> zx0 ip6/0 1 0 255 10:0:0:0:0:2:2:2 > 10:1:2:0:0:0:0:1 +< zx0 ip6/0 1 0 255 10:1:1:0:0:0:0:1 > 10:1:1:0:0:0:0:2 +< zx0 ip6/0 1 0 255 10:1:1:0:0:0:0:2 > 10:1:1:0:0:0:0:1 +< zx0 ip6/0 1 0 255 10:0:0:0:0:2:2:1 > 10:0:0:0:0:2:1:1 +< zx0 ip6/0 1 0 255 10:0:0:0:0:2:2:2 > 10:0:0:0:0:2:1:1 +< zx0 ip6/0 1 0 255 10:0:0:0:0:2:2:3 > 10:1:1:0:0:0:0:1 +< zx0 ip6/0 1 0 255 10:0:0:0:0:2:3:4 > 10:0:0:0:0:2:2:2 +< zx0 ip6/0 1 0 255 10:1:1:0:0:0:0:1 > 10:0:0:0:0:2:2:2 +< zx0 ip6/0 1 0 255 10:1:1:0:0:0:0:2 > 10:1:1:0:0:0:0:1 +< zx0 ip6/0 1 0 255 10:1:1:0:0:0:0:0 > 10:0:0:0:0:3:4:5 +< zx0 ip6/0 1 0 255 10:1:1:0:0:0:0:1 > 10:0:0:0:0:3:4:5 +< zx0 ip6/0 1 0 255 10:1:1:0:0:0:0:2 > 10:0:0:0:0:3:4:5 +< zx0 ip6/0 20 0 6 10:1:1:0:0:0:0:1,1025 > 10:0:0:0:0:3:4:5,1025 +> zx0 ip6/0 88 0 58 10:0:0:0:0:2:2:2 > 10:4:3:0:0:0:0:2 +< zx0 ip6/0 88 0 58 10:4:3:0:0:0:0:2 > 10:1:1:0:0:0:0:1 +< zx0 ip6/0 88 0 58 10:4:3:0:0:0:0:2 > 10:0:0:0:0:3:4:1 +< zx0 ip6/0 88 0 58 10:4:3:0:0:0:0:2 > 10:0:0:0:0:3:4:2 +< zx0 ip6/0 88 0 58 10:4:3:0:0:0:0:2 > 10:0:0:0:0:3:4:3 +< zx0 ip6/0 88 0 58 10:4:3:0:0:0:0:2 > 10:0:0:0:0:3:4:4 +< zx0 ip6/0 88 0 58 10:4:3:0:0:0:0:2 > 10:0:0:0:0:3:4:5 +> zx0 ip6/0 1 0 34 10:1:1:0:0:0:0:2 > 10:4:3:0:0:0:0:2 +< zx0 ip6/0 1 0 34 10:4:3:0:0:0:0:2 > 10:0:0:0:0:3:4:4 +> zx0 ip6/0 1 0 34 10:1:1:0:0:0:0:2 > 10:4:3:0:0:0:0:4 +< zx0 ip6/0 1 0 34 10:4:3:0:0:0:0:4 > 10:0:0:0:0:3:4:5 +> zx0 ip6/0 1 0 34 10:1:1:0:0:0:0:3 > 10:4:3:0:0:0:0:4 +< zx0 ip6/0 1 0 34 10:4:3:0:0:0:0:4 > 10:0:0:0:0:3:4:6 +> zx0 ip6/0 1 0 35 10:1:1:0:0:0:0:3 > 10:4:3:0:0:0:0:4 +< zx0 ip6/0 1 0 35 10:4:3:0:0:0:0:4 > 10:0:0:0:0:3:4:7 +List of active MAP/Redirect filters: +map zx0 inet6 10:1:1::1/128 -> 10::2:2:2/128 + +List of active sessions: +MAP 10:1:1::1 <- -> 10::2:2:2 [10:4:3::2] +MAP 10:1:1::1 <- -> 10::2:2:2 [10:1:1::2] + +Hostmap table: +10:1:1::1,10:4:3::2 -> 10::2:2:2,any (use = 1) +10:1:1::1,10:1:1::2 -> 10::2:2:2,any (use = 1) +List of active state sessions: +List of configured pools +List of configured hash tables +List of groups configured (set 0) +List of groups configured (set 1) +Rules configured (set 0, in) +Rules configured (set 0, out) +Rules configured (set 1, in) +Rules configured (set 1, out) +Accounting rules configured (set 0, in) +Accounting rules configured (set 0, out) +Accounting rules configured (set 1, in) +Accounting rules configured (set 1, out) +------------------------------- +> zx0 ip6/0 1 0 255 10:0:0:0:0:3:4:5 > 10:1:1:0:0:0:0:2 +16 +> zx0 ip6/0 1 0 255 10:0:0:0:0:3:4:5 > 10:1:1:0:0:0:0:1 +> zx0 ip6/0 20 0 6 10:0:0:0:0:3:4:5,1025 > 10:1:1:0:0:0:0:1,1025 +> zx0 ip6/0 20 0 6 10:0:0:0:0:3:4:5,1026 > 10:1:1:0:0:0:0:1,1025 +> zx0 ip6/0 1 0 255 10:0:0:0:0:2:2:1 > 10:1:2:0:0:0:0:1 +> zx0 ip6/0 1 0 255 10:0:0:0:0:2:2:2 > 10:1:2:0:0:0:0:1 +< zx0 ip6/0 1 0 255 10:1:1:0:0:0:0:1 > 10:1:1:0:0:0:0:2 +< zx0 ip6/0 1 0 255 10:1:1:0:0:0:0:2 > 10:1:1:0:0:0:0:1 +< zx0 ip6/0 1 0 255 10:0:0:0:0:2:2:1 > 10:0:0:0:0:2:1:1 +< zx0 ip6/0 1 0 255 10:0:0:0:0:2:2:2 > 10:0:0:0:0:2:1:1 +< zx0 ip6/0 1 0 255 10:0:0:0:0:2:2:3 > 10:1:1:0:0:0:0:1 +< zx0 ip6/0 1 0 255 10:0:0:0:0:2:3:4 > 10:0:0:0:0:2:2:2 +< zx0 ip6/0 1 0 255 10:1:1:0:0:0:0:1 > 10:0:0:0:0:2:2:2 +< zx0 ip6/0 1 0 255 10:1:1:0:0:0:0:2 > 10:0:0:0:0:2:2:2 +< zx0 ip6/0 1 0 255 10:1:1:0:0:0:0:0 > 10:0:0:0:0:3:4:5 +< zx0 ip6/0 1 0 255 10:1:1:0:0:0:0:1 > 10:1:1:0:0:0:0:2 +< zx0 ip6/0 1 0 255 10:1:1:0:0:0:0:2 > 10:1:1:0:0:0:0:0 +< zx0 ip6/0 20 0 6 10:1:1:0:0:0:0:1,1025 > 10:1:1:0:0:0:0:2,1025 +> zx0 ip6/0 88 0 58 10:0:0:0:0:3:4:5 > 10:4:3:0:0:0:0:2 +< zx0 ip6/0 88 0 58 10:4:3:0:0:0:0:2 > 10:0:0:0:0:2:2:2 +< zx0 ip6/0 88 0 58 10:4:3:0:0:0:0:2 > 10:0:0:0:0:3:4:1 +< zx0 ip6/0 88 0 58 10:4:3:0:0:0:0:2 > 10:0:0:0:0:3:4:2 +< zx0 ip6/0 88 0 58 10:4:3:0:0:0:0:2 > 10:0:0:0:0:3:4:3 +< zx0 ip6/0 88 0 58 10:4:3:0:0:0:0:2 > 10:0:0:0:0:3:4:4 +< zx0 ip6/0 88 0 58 10:4:3:0:0:0:0:2 > 10:1:1:0:0:0:0:1 +> zx0 ip6/0 1 0 34 10:0:0:0:0:3:4:5 > 10:4:3:0:0:0:0:2 +< zx0 ip6/0 1 0 34 10:4:3:0:0:0:0:2 > 10:0:0:0:0:3:4:4 +> zx0 ip6/0 1 0 34 10:0:0:0:0:3:4:5 > 10:4:3:0:0:0:0:4 +< zx0 ip6/0 1 0 34 10:4:3:0:0:0:0:4 > 10:1:1:0:0:0:0:2 +16 +< zx0 ip6/0 1 0 34 10:4:3:0:0:0:0:4 > 10:0:0:0:0:3:4:6 +> zx0 ip6/0 1 0 35 10:0:0:0:0:3:4:5 > 10:4:3:0:0:0:0:4 +< zx0 ip6/0 1 0 35 10:4:3:0:0:0:0:4 > 10:0:0:0:0:3:4:7 +List of active MAP/Redirect filters: +map zx0 inet6 10:1:1::/112 -> 10::3:4:5/128 + +List of active sessions: +MAP 10:1:1::3 <- -> 10::3:4:5 [10:4:3::4] +MAP 10:1:1::2 <- -> 10::3:4:5 [10:4:3::4] +MAP 10:1:1::2 <- -> 10::3:4:5 [10:4:3::2] +MAP 10:1:1::1 <- -> 10::3:4:5 [10:4:3::2] +MAP 10:1:1::2 1026 <- -> 10::3:4:5 1026 [10:1:1::1 1025] +MAP 10:1:1::2 1025 <- -> 10::3:4:5 1025 [10:1:1::1 1025] +MAP 10:1:1::2 <- -> 10::3:4:5 [10:1:1::1] +MAP 10:1:1:: <- -> 10::3:4:5 [10:1:1::2] + +Hostmap table: +10:1:1::3,10:4:3::4 -> 10::3:4:5,any (use = 1) +10:1:1::2,10:4:3::4 -> 10::3:4:5,any (use = 1) +10:1:1::2,10:4:3::2 -> 10::3:4:5,any (use = 1) +10:1:1::1,10:4:3::2 -> 10::3:4:5,any (use = 1) +10:1:1::2,10:1:1::1 -> 10::3:4:5,any (use = 3) +10:1:1::,10:1:1::2 -> 10::3:4:5,any (use = 1) +List of active state sessions: +List of configured pools +List of configured hash tables +List of groups configured (set 0) +List of groups configured (set 1) +Rules configured (set 0, in) +Rules configured (set 0, out) +Rules configured (set 1, in) +Rules configured (set 1, out) +Accounting rules configured (set 0, in) +Accounting rules configured (set 0, out) +Accounting rules configured (set 1, in) +Accounting rules configured (set 1, out) +------------------------------- +> zx0 ip6/0 1 0 255 10:0:0:0:0:3:4:1 > 10:1:1:0:0:0:0:2 +> zx0 ip6/0 1 0 255 10:0:0:0:0:3:4:2 > 10:1:1:0:0:0:0:2 +> zx0 ip6/0 1 0 255 10:0:0:0:0:3:4:3 > 10:1:1:0:0:0:0:1 +> zx0 ip6/0 20 0 6 10:0:0:0:0:3:4:3,1025 > 10:1:1:0:0:0:0:1,1025 +> zx0 ip6/0 20 0 6 10:0:0:0:0:3:4:3,1026 > 10:1:1:0:0:0:0:1,1025 +> zx0 ip6/0 1 0 255 10:0:0:0:0:2:2:1 > 10:1:2:0:0:0:0:1 +> zx0 ip6/0 1 0 255 10:0:0:0:0:2:2:2 > 10:1:2:0:0:0:0:1 +< zx0 ip6/0 1 0 255 10:1:1:0:0:0:0:1 > 10:1:1:0:0:0:0:2 +< zx0 ip6/0 1 0 255 10:1:1:0:0:0:0:2 > 10:1:1:0:0:0:0:1 +< zx0 ip6/0 1 0 255 10:0:0:0:0:2:2:1 > 10:0:0:0:0:2:1:1 +< zx0 ip6/0 1 0 255 10:0:0:0:0:2:2:2 > 10:0:0:0:0:2:1:1 +< zx0 ip6/0 1 0 255 10:0:0:0:0:2:2:3 > 10:1:1:0:0:0:0:1 +< zx0 ip6/0 1 0 255 10:0:0:0:0:2:3:4 > 10:0:0:0:0:2:2:2 +< zx0 ip6/0 1 0 255 10:1:1:0:0:0:0:1 > 10:0:0:0:0:2:2:2 +< zx0 ip6/0 1 0 255 10:1:1:0:0:0:0:2 > 10:0:0:0:0:2:2:2 +< zx0 ip6/0 1 0 255 10:1:1:0:0:0:0:0 > 10:0:0:0:0:3:4:5 +< zx0 ip6/0 1 0 255 10:1:1:0:0:0:0:1 > 10:0:0:0:0:3:4:5 +< zx0 ip6/0 1 0 255 10:1:1:0:0:0:0:2 > 10:0:0:0:0:3:4:5 +< zx0 ip6/0 20 0 6 10:1:1:0:0:0:0:1,1025 > 10:0:0:0:0:3:4:5,1025 +> zx0 ip6/0 88 0 58 10:0:0:0:0:3:4:3 > 10:4:3:0:0:0:0:2 +< zx0 ip6/0 88 0 58 10:4:3:0:0:0:0:2 > 10:0:0:0:0:2:2:2 +< zx0 ip6/0 88 0 58 10:4:3:0:0:0:0:2 > 10:0:0:0:0:3:4:1 +< zx0 ip6/0 88 0 58 10:4:3:0:0:0:0:2 > 10:0:0:0:0:3:4:2 +< zx0 ip6/0 88 0 58 10:4:3:0:0:0:0:2 > 10:1:1:0:0:0:0:1 +< zx0 ip6/0 88 0 58 10:4:3:0:0:0:0:2 > 10:0:0:0:0:3:4:4 +< zx0 ip6/0 88 0 58 10:4:3:0:0:0:0:2 > 10:0:0:0:0:3:4:5 +> zx0 ip6/0 1 0 34 10:0:0:0:0:3:4:3 > 10:4:3:0:0:0:0:2 +< zx0 ip6/0 1 0 34 10:4:3:0:0:0:0:2 > 10:0:0:0:0:3:4:4 +> zx0 ip6/0 1 0 34 10:0:0:0:0:3:4:3 > 10:4:3:0:0:0:0:4 +< zx0 ip6/0 1 0 34 10:4:3:0:0:0:0:4 > 10:0:0:0:0:3:4:5 +> zx0 ip6/0 1 0 34 10:0:0:0:0:3:4:4 > 10:4:3:0:0:0:0:4 +< zx0 ip6/0 1 0 34 10:4:3:0:0:0:0:4 > 10:0:0:0:0:3:4:6 +> zx0 ip6/0 1 0 35 10:0:0:0:0:3:4:4 > 10:4:3:0:0:0:0:4 +< zx0 ip6/0 1 0 35 10:4:3:0:0:0:0:4 > 10:0:0:0:0:3:4:7 +List of active MAP/Redirect filters: +map zx0 inet6 10:1:1::/112 -> 10::3:4:0/112 + +List of active sessions: +MAP 10:1:1::3 <- -> 10::3:4:4 [10:4:3::4] +MAP 10:1:1::3 <- -> 10::3:4:4 [10:4:3::4] +MAP 10:1:1::2 <- -> 10::3:4:3 [10:4:3::4] +MAP 10:1:1::2 <- -> 10::3:4:3 [10:4:3::2] +MAP 10:1:1::1 <- -> 10::3:4:3 [10:4:3::2] +MAP 10:1:1::2 1026 <- -> 10::3:4:3 1026 [10:1:1::1 1025] +MAP 10:1:1::2 1025 <- -> 10::3:4:3 1025 [10:1:1::1 1025] +MAP 10:1:1::2 <- -> 10::3:4:3 [10:1:1::1] +MAP 10:1:1::1 <- -> 10::3:4:2 [10:1:1::2] +MAP 10:1:1:: <- -> 10::3:4:1 [10:1:1::2] + +Hostmap table: +10:1:1::3,10:4:3::4 -> 10::3:4:4,any (use = 2) +10:1:1::2,10:4:3::4 -> 10::3:4:3,any (use = 1) +10:1:1::2,10:4:3::2 -> 10::3:4:3,any (use = 1) +10:1:1::1,10:4:3::2 -> 10::3:4:3,any (use = 1) +10:1:1::2,10:1:1::1 -> 10::3:4:3,any (use = 3) +10:1:1::1,10:1:1::2 -> 10::3:4:2,any (use = 1) +10:1:1::,10:1:1::2 -> 10::3:4:1,any (use = 1) +List of active state sessions: +List of configured pools +List of configured hash tables +List of groups configured (set 0) +List of groups configured (set 1) +Rules configured (set 0, in) +Rules configured (set 0, out) +Rules configured (set 1, in) +Rules configured (set 1, out) +Accounting rules configured (set 0, in) +Accounting rules configured (set 0, out) +Accounting rules configured (set 1, in) +Accounting rules configured (set 1, out) +------------------------------- diff --git a/contrib/ipfilter/test/expected/n2 b/contrib/ipfilter/test/expected/n2 index 827272e9103..836608a46ad 100644 --- a/contrib/ipfilter/test/expected/n2 +++ b/contrib/ipfilter/test/expected/n2 @@ -1,80 +1,191 @@ -ip #0 40(20) 6 10.2.2.2,10000 > 10.1.1.1,1025 -ip #0 40(20) 6 10.2.2.2,10001 > 10.1.1.2,1025 -ip #0 20(20) 0 10.1.1.0 > 10.1.1.2 -ip #0 20(20) 0 10.1.1.1 > 10.1.2.1 -ip #0 40(20) 6 10.1.1.2,1025 > 10.1.1.1,1025 -ip #0 40(20) 6 10.1.1.2,1025 > 10.1.1.1,1025 -ip #0 40(20) 6 10.1.1.2,1026 > 10.1.1.1,1025 -ip #0 28(20) 17 10.1.1.2,1025 > 10.1.1.1,1025 -ip #0 40(20) 6 10.1.1.3,2000 > 10.1.2.1,80 -ip #0 40(20) 6 10.1.1.3,2001 > 10.1.3.1,80 -ip #0 40(20) 6 10.1.1.3,2002 > 10.1.4.1,80 -ip #0 40(20) 6 10.1.1.3,2003 > 10.1.4.1,80 -ip #0 20(20) 0 10.1.1.1 > 10.1.1.2 -ip #0 40(20) 6 10.1.1.1,1025 > 10.1.1.2,1025 -ip #0 20(20) 0 10.1.1.2 > 10.1.1.1 -ip #0 40(20) 6 10.1.1.1,1026 > 10.3.4.5,40000 -ip #0 40(20) 6 10.1.1.1,1025 > 10.3.4.5,40000 -ip #0 28(20) 17 10.1.1.2,1025 > 10.3.4.5,40001 -ip #0 40(20) 6 10.1.2.1,80 > 10.3.4.5,40001 +> zx0 ip #0 40(20) 6 10.2.2.2,10000 > 10.1.1.1,1025 +> zx0 ip #0 40(20) 6 10.2.2.2,10001 > 10.1.1.2,1025 +> zx0 ip #0 20(20) 0 10.1.1.0 > 10.1.1.2 +> zx0 ip #0 20(20) 0 10.1.1.1 > 10.1.2.1 +> zx0 ip #0 40(20) 6 10.1.1.2,1025 > 10.1.1.1,1025 +> zx0 ip #0 40(20) 6 10.1.1.2,1025 > 10.1.1.1,1025 +> zx0 ip #0 40(20) 6 10.1.1.2,1026 > 10.1.1.1,1025 +> zx0 ip #0 28(20) 17 10.1.1.2,1025 > 10.1.1.1,1025 +> zx0 ip #0 40(20) 6 10.1.1.3,2000 > 10.1.2.1,80 +> zx0 ip #0 40(20) 6 10.1.1.3,2001 > 10.1.3.1,80 +> zx0 ip #0 40(20) 6 10.1.1.3,2002 > 10.1.4.1,80 +> zx0 ip #0 40(20) 6 10.1.1.3,2003 > 10.1.4.1,80 +< zx0 ip #0 20(20) 0 10.1.1.1 > 10.1.1.2 +< zx0 ip #0 40(20) 6 10.1.1.1,1025 > 10.1.1.2,1025 +< zx0 ip #0 20(20) 0 10.1.1.2 > 10.1.1.1 +< zx0 ip #0 40(20) 6 10.1.1.1,1026 > 10.3.4.5,40000 +< zx0 ip #0 40(20) 6 10.1.1.1,1025 > 10.3.4.5,40000 +< zx0 ip #0 28(20) 17 10.1.1.2,1025 > 10.3.4.5,40001 +< zx0 ip #0 40(20) 6 10.1.2.1,80 > 10.3.4.5,40001 +List of active MAP/Redirect filters: +map zx0 10.1.1.1/32 -> 10.2.2.2/32 portmap tcp 10000:20000 sequential + +List of active sessions: +MAP 10.1.1.1 1025 <- -> 10.2.2.2 10001 [10.1.1.2 1025] +MAP 10.1.1.1 1025 <- -> 10.2.2.2 10000 [10.1.1.1 1025] + +Hostmap table: +10.1.1.1,10.1.1.2 -> 10.2.2.2,0.0.0.0 (use = 1) +10.1.1.1,10.1.1.1 -> 10.2.2.2,0.0.0.0 (use = 1) +List of active state sessions: +List of configured pools +List of configured hash tables +List of groups configured (set 0) +List of groups configured (set 1) +Rules configured (set 0, in) +Rules configured (set 0, out) +Rules configured (set 1, in) +Rules configured (set 1, out) +Accounting rules configured (set 0, in) +Accounting rules configured (set 0, out) +Accounting rules configured (set 1, in) +Accounting rules configured (set 1, out) ------------------------------- -ip #0 40(20) 6 10.1.1.1,1025 > 10.1.1.1,1025 -ip #0 40(20) 6 10.1.1.1,1025 > 10.1.1.2,1025 -ip #0 20(20) 0 10.1.1.0 > 10.1.1.2 -ip #0 20(20) 0 10.1.1.1 > 10.1.2.1 -ip #0 40(20) 6 10.1.1.2,1025 > 10.1.1.1,1025 -ip #0 40(20) 6 10.1.1.2,1025 > 10.1.1.1,1025 -ip #0 40(20) 6 10.1.1.2,1026 > 10.1.1.1,1025 -ip #0 28(20) 17 10.3.4.5,10000 > 10.1.1.1,1025 -ip #0 40(20) 6 10.1.1.3,2000 > 10.1.2.1,80 -ip #0 40(20) 6 10.1.1.3,2001 > 10.1.3.1,80 -ip #0 40(20) 6 10.1.1.3,2002 > 10.1.4.1,80 -ip #0 40(20) 6 10.1.1.3,2003 > 10.1.4.1,80 -ip #0 20(20) 0 10.1.1.1 > 10.1.1.2 -ip #0 40(20) 6 10.1.1.1,1025 > 10.1.1.2,1025 -ip #0 20(20) 0 10.1.1.2 > 10.1.1.1 -ip #0 40(20) 6 10.1.1.1,1026 > 10.3.4.5,40000 -ip #0 40(20) 6 10.1.1.1,1025 > 10.3.4.5,40000 -ip #0 28(20) 17 10.1.1.2,1025 > 10.3.4.5,40001 -ip #0 40(20) 6 10.1.2.1,80 > 10.3.4.5,40001 +> zx0 ip #0 40(20) 6 10.1.1.1,1025 > 10.1.1.1,1025 +> zx0 ip #0 40(20) 6 10.1.1.1,1025 > 10.1.1.2,1025 +> zx0 ip #0 20(20) 0 10.1.1.0 > 10.1.1.2 +> zx0 ip #0 20(20) 0 10.1.1.1 > 10.1.2.1 +> zx0 ip #0 40(20) 6 10.1.1.2,1025 > 10.1.1.1,1025 +> zx0 ip #0 40(20) 6 10.1.1.2,1025 > 10.1.1.1,1025 +> zx0 ip #0 40(20) 6 10.1.1.2,1026 > 10.1.1.1,1025 +> zx0 ip #0 28(20) 17 10.3.4.5,10000 > 10.1.1.1,1025 +> zx0 ip #0 40(20) 6 10.1.1.3,2000 > 10.1.2.1,80 +> zx0 ip #0 40(20) 6 10.1.1.3,2001 > 10.1.3.1,80 +> zx0 ip #0 40(20) 6 10.1.1.3,2002 > 10.1.4.1,80 +> zx0 ip #0 40(20) 6 10.1.1.3,2003 > 10.1.4.1,80 +< zx0 ip #0 20(20) 0 10.1.1.1 > 10.1.1.2 +< zx0 ip #0 40(20) 6 10.1.1.1,1025 > 10.1.1.2,1025 +< zx0 ip #0 20(20) 0 10.1.1.2 > 10.1.1.1 +< zx0 ip #0 40(20) 6 10.1.1.1,1026 > 10.3.4.5,40000 +< zx0 ip #0 40(20) 6 10.1.1.1,1025 > 10.3.4.5,40000 +< zx0 ip #0 28(20) 17 10.1.1.2,1025 > 10.3.4.5,40001 +< zx0 ip #0 40(20) 6 10.1.2.1,80 > 10.3.4.5,40001 +List of active MAP/Redirect filters: +map zx0 10.1.1.0/24 -> 10.3.4.5/32 portmap udp 10000:20000 sequential + +List of active sessions: +MAP 10.1.1.2 1025 <- -> 10.3.4.5 10000 [10.1.1.1 1025] + +Hostmap table: +10.1.1.2,10.1.1.1 -> 10.3.4.5,0.0.0.0 (use = 1) +List of active state sessions: +List of configured pools +List of configured hash tables +List of groups configured (set 0) +List of groups configured (set 1) +Rules configured (set 0, in) +Rules configured (set 0, out) +Rules configured (set 1, in) +Rules configured (set 1, out) +Accounting rules configured (set 0, in) +Accounting rules configured (set 0, out) +Accounting rules configured (set 1, in) +Accounting rules configured (set 1, out) ------------------------------- -ip #0 40(20) 6 10.3.4.1,10000 > 10.1.1.1,1025 -ip #0 40(20) 6 10.3.4.1,10001 > 10.1.1.2,1025 -ip #0 20(20) 0 10.1.1.0 > 10.1.1.2 -ip #0 20(20) 0 10.1.1.1 > 10.1.2.1 -ip #0 40(20) 6 10.3.4.1,10002 > 10.1.1.1,1025 -ip #0 40(20) 6 10.3.4.1,10002 > 10.1.1.1,1025 -ip #0 40(20) 6 10.3.4.1,10003 > 10.1.1.1,1025 -ip #0 28(20) 17 10.3.4.1,10004 > 10.1.1.1,1025 -ip #0 40(20) 6 10.3.4.1,10005 > 10.1.2.1,80 -ip #0 40(20) 6 10.3.4.1,10006 > 10.1.3.1,80 -ip #0 40(20) 6 10.3.4.1,10007 > 10.1.4.1,80 -ip #0 40(20) 6 10.3.4.1,10008 > 10.1.4.1,80 -ip #0 20(20) 0 10.1.1.1 > 10.1.1.2 -ip #0 40(20) 6 10.1.1.1,1025 > 10.1.1.2,1025 -ip #0 20(20) 0 10.1.1.2 > 10.1.1.1 -ip #0 40(20) 6 10.1.1.1,1026 > 10.3.4.5,40000 -ip #0 40(20) 6 10.1.1.1,1025 > 10.3.4.5,40000 -ip #0 28(20) 17 10.1.1.2,1025 > 10.3.4.5,40001 -ip #0 40(20) 6 10.1.2.1,80 > 10.3.4.5,40001 +> zx0 ip #0 40(20) 6 10.3.4.1,10000 > 10.1.1.1,1025 +> zx0 ip #0 40(20) 6 10.3.4.1,10001 > 10.1.1.2,1025 +> zx0 ip #0 20(20) 0 10.1.1.0 > 10.1.1.2 +> zx0 ip #0 20(20) 0 10.1.1.1 > 10.1.2.1 +> zx0 ip #0 40(20) 6 10.3.4.1,10002 > 10.1.1.1,1025 +> zx0 ip #0 40(20) 6 10.3.4.1,10002 > 10.1.1.1,1025 +> zx0 ip #0 40(20) 6 10.3.4.1,10003 > 10.1.1.1,1025 +> zx0 ip #0 28(20) 17 10.3.4.1,10004 > 10.1.1.1,1025 +> zx0 ip #0 40(20) 6 10.3.4.1,10005 > 10.1.2.1,80 +> zx0 ip #0 40(20) 6 10.3.4.1,10006 > 10.1.3.1,80 +> zx0 ip #0 40(20) 6 10.3.4.1,10007 > 10.1.4.1,80 +> zx0 ip #0 40(20) 6 10.3.4.1,10008 > 10.1.4.1,80 +< zx0 ip #0 20(20) 0 10.1.1.1 > 10.1.1.2 +< zx0 ip #0 40(20) 6 10.1.1.1,1025 > 10.1.1.2,1025 +< zx0 ip #0 20(20) 0 10.1.1.2 > 10.1.1.1 +< zx0 ip #0 40(20) 6 10.1.1.1,1026 > 10.3.4.5,40000 +< zx0 ip #0 40(20) 6 10.1.1.1,1025 > 10.3.4.5,40000 +< zx0 ip #0 28(20) 17 10.1.1.2,1025 > 10.3.4.5,40001 +< zx0 ip #0 40(20) 6 10.1.2.1,80 > 10.3.4.5,40001 +List of active MAP/Redirect filters: +map zx0 10.1.0.0/16 -> 10.3.4.0/24 portmap tcp/udp 10000:20000 sequential + +List of active sessions: +MAP 10.1.1.3 2003 <- -> 10.3.4.1 10008 [10.1.4.1 80] +MAP 10.1.1.3 2002 <- -> 10.3.4.1 10007 [10.1.4.1 80] +MAP 10.1.1.3 2001 <- -> 10.3.4.1 10006 [10.1.3.1 80] +MAP 10.1.1.3 2000 <- -> 10.3.4.1 10005 [10.1.2.1 80] +MAP 10.1.1.2 1025 <- -> 10.3.4.1 10004 [10.1.1.1 1025] +MAP 10.1.1.2 1026 <- -> 10.3.4.1 10003 [10.1.1.1 1025] +MAP 10.1.1.2 1025 <- -> 10.3.4.1 10002 [10.1.1.1 1025] +MAP 10.1.1.1 1025 <- -> 10.3.4.1 10001 [10.1.1.2 1025] +MAP 10.1.1.1 1025 <- -> 10.3.4.1 10000 [10.1.1.1 1025] + +Hostmap table: +10.1.1.3,10.1.4.1 -> 10.3.4.1,0.0.0.0 (use = 2) +10.1.1.3,10.1.3.1 -> 10.3.4.1,0.0.0.0 (use = 1) +10.1.1.3,10.1.2.1 -> 10.3.4.1,0.0.0.0 (use = 1) +10.1.1.2,10.1.1.1 -> 10.3.4.1,0.0.0.0 (use = 3) +10.1.1.1,10.1.1.2 -> 10.3.4.1,0.0.0.0 (use = 1) +10.1.1.1,10.1.1.1 -> 10.3.4.1,0.0.0.0 (use = 1) +List of active state sessions: +List of configured pools +List of configured hash tables +List of groups configured (set 0) +List of groups configured (set 1) +Rules configured (set 0, in) +Rules configured (set 0, out) +Rules configured (set 1, in) +Rules configured (set 1, out) +Accounting rules configured (set 0, in) +Accounting rules configured (set 0, out) +Accounting rules configured (set 1, in) +Accounting rules configured (set 1, out) ------------------------------- -ip #0 40(20) 6 10.3.4.5,40000 > 10.1.1.1,1025 -ip #0 40(20) 6 10.3.4.5,40001 > 10.1.1.2,1025 -ip #0 20(20) 0 10.1.1.0 > 10.1.1.2 -ip #0 20(20) 0 10.1.1.1 > 10.1.2.1 -ip #0 40(20) 6 10.3.4.5,40001 > 10.1.1.1,1025 -ip #0 40(20) 6 10.3.4.5,40001 > 10.1.1.1,1025 -ip #0 40(20) 6 10.1.1.2,1026 > 10.1.1.1,1025 -ip #0 28(20) 17 10.3.4.5,40000 > 10.1.1.1,1025 -ip #0 40(20) 6 10.3.4.5,40001 > 10.1.2.1,80 -ip #0 40(20) 6 10.3.4.5,40000 > 10.1.3.1,80 -ip #0 40(20) 6 10.3.4.5,40001 > 10.1.4.1,80 -ip #0 40(20) 6 10.3.4.5,40000 > 10.1.4.1,80 -ip #0 20(20) 0 10.1.1.1 > 10.1.1.2 -ip #0 40(20) 6 10.1.1.1,1025 > 10.1.1.2,1025 -ip #0 20(20) 0 10.1.1.2 > 10.1.1.1 -ip #0 40(20) 6 10.1.1.1,1026 > 10.3.4.5,40000 -ip #0 40(20) 6 10.1.1.1,1025 > 10.1.1.1,1025 -ip #0 28(20) 17 10.1.1.2,1025 > 10.3.4.5,40001 -ip #0 40(20) 6 10.1.2.1,80 > 10.1.1.3,2000 +> zx0 ip #0 40(20) 6 10.3.4.5,40000 > 10.1.1.1,1025 +> zx0 ip #0 40(20) 6 10.3.4.5,40001 > 10.1.1.2,1025 +> zx0 ip #0 20(20) 0 10.1.1.0 > 10.1.1.2 +> zx0 ip #0 20(20) 0 10.1.1.1 > 10.1.2.1 +> zx0 ip #0 40(20) 6 10.3.4.5,40001 > 10.1.1.1,1025 +> zx0 ip #0 40(20) 6 10.3.4.5,40001 > 10.1.1.1,1025 +15 +> zx0 ip #0 28(20) 17 10.3.4.5,40000 > 10.1.1.1,1025 +> zx0 ip #0 40(20) 6 10.3.4.5,40001 > 10.1.2.1,80 +> zx0 ip #0 40(20) 6 10.3.4.5,40000 > 10.1.3.1,80 +> zx0 ip #0 40(20) 6 10.3.4.5,40001 > 10.1.4.1,80 +> zx0 ip #0 40(20) 6 10.3.4.5,40000 > 10.1.4.1,80 +< zx0 ip #0 20(20) 0 10.1.1.1 > 10.1.1.2 +< zx0 ip #0 40(20) 6 10.1.1.1,1025 > 10.1.1.2,1025 +< zx0 ip #0 20(20) 0 10.1.1.2 > 10.1.1.1 +< zx0 ip #0 40(20) 6 10.1.1.1,1026 > 10.3.4.5,40000 +< zx0 ip #0 40(20) 6 10.1.1.1,1025 > 10.1.1.1,1025 +< zx0 ip #0 28(20) 17 10.1.1.2,1025 > 10.3.4.5,40001 +< zx0 ip #0 40(20) 6 10.1.2.1,80 > 10.1.1.3,2000 +List of active MAP/Redirect filters: +map zx0 10.1.1.0/24 -> 10.3.4.5/32 portmap tcp/udp 40000:40001 sequential + +List of active sessions: +MAP 10.1.1.3 2003 <- -> 10.3.4.5 40000 [10.1.4.1 80] +MAP 10.1.1.3 2002 <- -> 10.3.4.5 40001 [10.1.4.1 80] +MAP 10.1.1.3 2001 <- -> 10.3.4.5 40000 [10.1.3.1 80] +MAP 10.1.1.3 2000 <- -> 10.3.4.5 40001 [10.1.2.1 80] +MAP 10.1.1.2 1025 <- -> 10.3.4.5 40000 [10.1.1.1 1025] +MAP 10.1.1.2 1025 <- -> 10.3.4.5 40001 [10.1.1.1 1025] +MAP 10.1.1.1 1025 <- -> 10.3.4.5 40001 [10.1.1.2 1025] +MAP 10.1.1.1 1025 <- -> 10.3.4.5 40000 [10.1.1.1 1025] + +Hostmap table: +10.1.1.3,10.1.4.1 -> 10.3.4.5,0.0.0.0 (use = 2) +10.1.1.3,10.1.3.1 -> 10.3.4.5,0.0.0.0 (use = 1) +10.1.1.3,10.1.2.1 -> 10.3.4.5,0.0.0.0 (use = 1) +10.1.1.2,10.1.1.1 -> 10.3.4.5,0.0.0.0 (use = 2) +10.1.1.1,10.1.1.2 -> 10.3.4.5,0.0.0.0 (use = 1) +10.1.1.1,10.1.1.1 -> 10.3.4.5,0.0.0.0 (use = 1) +List of active state sessions: +List of configured pools +List of configured hash tables +List of groups configured (set 0) +List of groups configured (set 1) +Rules configured (set 0, in) +Rules configured (set 0, out) +Rules configured (set 1, in) +Rules configured (set 1, out) +Accounting rules configured (set 0, in) +Accounting rules configured (set 0, out) +Accounting rules configured (set 1, in) +Accounting rules configured (set 1, out) ------------------------------- diff --git a/contrib/ipfilter/test/expected/n200 b/contrib/ipfilter/test/expected/n200 new file mode 100644 index 00000000000..0f3c6a5adec --- /dev/null +++ b/contrib/ipfilter/test/expected/n200 @@ -0,0 +1,25 @@ +4500 0044 0000 0000 ff11 bda6 7f00 0001 7f00 0001 2775 2775 0030 0000 4500 0028 0000 0000 0006 435a 6363 6363 5858 5858 038d 0050 0000 0000 0000 0000 5000 1000 2491 0000 + +4500 0028 0000 0000 0006 435a 6363 6363 5858 5858 038d 0050 0000 0000 0000 0000 5000 1000 2491 0000 + +List of active MAP/Redirect filters: +divert in on bar0 proto tcp from 0/0 to 0/0 -> src 127.0.0.1/32,10101 dst 127.0.0.1/32,10101 udp; + +List of active sessions: +DIV-RDR 127.0.0.1 10101 <- -> 88.88.88.88 80 [99.99.99.99 909] + +Hostmap table: +List of active state sessions: +List of configured pools +List of configured hash tables +List of groups configured (set 0) +List of groups configured (set 1) +Rules configured (set 0, in) +Rules configured (set 0, out) +Rules configured (set 1, in) +Rules configured (set 1, out) +Accounting rules configured (set 0, in) +Accounting rules configured (set 0, out) +Accounting rules configured (set 1, in) +Accounting rules configured (set 1, out) +------------------------------- diff --git a/contrib/ipfilter/test/expected/n2_6 b/contrib/ipfilter/test/expected/n2_6 new file mode 100644 index 00000000000..08abc8f5b69 --- /dev/null +++ b/contrib/ipfilter/test/expected/n2_6 @@ -0,0 +1,191 @@ +> zx0 ip6/0 20 0 6 10:0:0:0:0:2:2:2,10000 > 10:1:1:0:0:0:0:1,1025 +> zx0 ip6/0 20 0 6 10:0:0:0:0:2:2:2,10001 > 10:1:1:0:0:0:0:2,1025 +> zx0 ip6/0 1 0 41 10:1:1:0:0:0:0:0 > 10:1:1:0:0:0:0:2 +> zx0 ip6/0 1 0 41 10:1:1:0:0:0:0:1 > 10:1:2:0:0:0:0:1 +> zx0 ip6/0 20 0 6 10:1:1:0:0:0:0:2,1025 > 10:1:1:0:0:0:0:1,1025 +> zx0 ip6/0 20 0 6 10:1:1:0:0:0:0:2,1025 > 10:1:1:0:0:0:0:1,1025 +> zx0 ip6/0 20 0 6 10:1:1:0:0:0:0:2,1026 > 10:1:1:0:0:0:0:1,1025 +> zx0 ip6/0 8 0 17 10:1:1:0:0:0:0:2,1025 > 10:1:1:0:0:0:0:1,1025 +> zx0 ip6/0 20 0 6 10:1:1:0:0:0:0:3,2000 > 10:1:2:0:0:0:0:1,80 +> zx0 ip6/0 20 0 6 10:1:1:0:0:0:0:3,2001 > 10:1:3:0:0:0:0:1,80 +> zx0 ip6/0 20 0 6 10:1:1:0:0:0:0:3,2002 > 10:1:4:0:0:0:0:1,80 +> zx0 ip6/0 20 0 6 10:1:1:0:0:0:0:3,2003 > 10:1:4:0:0:0:0:1,80 +< zx0 ip6/0 1 0 41 10:1:1:0:0:0:0:1 > 10:1:1:0:0:0:0:2 +< zx0 ip6/0 20 0 6 10:1:1:0:0:0:0:1,1025 > 10:1:1:0:0:0:0:2,1025 +< zx0 ip6/0 1 0 41 10:1:1:0:0:0:0:2 > 10:1:1:0:0:0:0:1 +< zx0 ip6/0 20 0 6 10:1:1:0:0:0:0:1,1026 > 10:0:0:0:0:3:4:5,40000 +< zx0 ip6/0 20 0 6 10:1:1:0:0:0:0:1,1025 > 10:0:0:0:0:3:4:5,40000 +< zx0 ip6/0 8 0 17 10:1:1:0:0:0:0:2,1025 > 10:0:0:0:0:3:4:5,40001 +< zx0 ip6/0 20 0 6 10:1:2:0:0:0:0:1,80 > 10:0:0:0:0:3:4:5,40001 +List of active MAP/Redirect filters: +map zx0 inet6 10:1:1::1/128 -> 10::2:2:2/128 portmap tcp 10000:20000 sequential + +List of active sessions: +MAP 10:1:1::1 1025 <- -> 10::2:2:2 10001 [10:1:1::2 1025] +MAP 10:1:1::1 1025 <- -> 10::2:2:2 10000 [10:1:1::1 1025] + +Hostmap table: +10:1:1::1,10:1:1::2 -> 10::2:2:2,any (use = 1) +10:1:1::1,10:1:1::1 -> 10::2:2:2,any (use = 1) +List of active state sessions: +List of configured pools +List of configured hash tables +List of groups configured (set 0) +List of groups configured (set 1) +Rules configured (set 0, in) +Rules configured (set 0, out) +Rules configured (set 1, in) +Rules configured (set 1, out) +Accounting rules configured (set 0, in) +Accounting rules configured (set 0, out) +Accounting rules configured (set 1, in) +Accounting rules configured (set 1, out) +------------------------------- +> zx0 ip6/0 20 0 6 10:1:1:0:0:0:0:1,1025 > 10:1:1:0:0:0:0:1,1025 +> zx0 ip6/0 20 0 6 10:1:1:0:0:0:0:1,1025 > 10:1:1:0:0:0:0:2,1025 +> zx0 ip6/0 1 0 41 10:1:1:0:0:0:0:0 > 10:1:1:0:0:0:0:2 +> zx0 ip6/0 1 0 41 10:1:1:0:0:0:0:1 > 10:1:2:0:0:0:0:1 +> zx0 ip6/0 20 0 6 10:1:1:0:0:0:0:2,1025 > 10:1:1:0:0:0:0:1,1025 +> zx0 ip6/0 20 0 6 10:1:1:0:0:0:0:2,1025 > 10:1:1:0:0:0:0:1,1025 +> zx0 ip6/0 20 0 6 10:1:1:0:0:0:0:2,1026 > 10:1:1:0:0:0:0:1,1025 +> zx0 ip6/0 8 0 17 10:0:0:0:0:3:4:5,10000 > 10:1:1:0:0:0:0:1,1025 +> zx0 ip6/0 20 0 6 10:1:1:0:0:0:0:3,2000 > 10:1:2:0:0:0:0:1,80 +> zx0 ip6/0 20 0 6 10:1:1:0:0:0:0:3,2001 > 10:1:3:0:0:0:0:1,80 +> zx0 ip6/0 20 0 6 10:1:1:0:0:0:0:3,2002 > 10:1:4:0:0:0:0:1,80 +> zx0 ip6/0 20 0 6 10:1:1:0:0:0:0:3,2003 > 10:1:4:0:0:0:0:1,80 +< zx0 ip6/0 1 0 41 10:1:1:0:0:0:0:1 > 10:1:1:0:0:0:0:2 +< zx0 ip6/0 20 0 6 10:1:1:0:0:0:0:1,1025 > 10:1:1:0:0:0:0:2,1025 +< zx0 ip6/0 1 0 41 10:1:1:0:0:0:0:2 > 10:1:1:0:0:0:0:1 +< zx0 ip6/0 20 0 6 10:1:1:0:0:0:0:1,1026 > 10:0:0:0:0:3:4:5,40000 +< zx0 ip6/0 20 0 6 10:1:1:0:0:0:0:1,1025 > 10:0:0:0:0:3:4:5,40000 +< zx0 ip6/0 8 0 17 10:1:1:0:0:0:0:2,1025 > 10:0:0:0:0:3:4:5,40001 +< zx0 ip6/0 20 0 6 10:1:2:0:0:0:0:1,80 > 10:0:0:0:0:3:4:5,40001 +List of active MAP/Redirect filters: +map zx0 inet6 10:1:1::/112 -> 10::3:4:5/128 portmap udp 10000:20000 sequential + +List of active sessions: +MAP 10:1:1::2 1025 <- -> 10::3:4:5 10000 [10:1:1::1 1025] + +Hostmap table: +10:1:1::2,10:1:1::1 -> 10::3:4:5,any (use = 1) +List of active state sessions: +List of configured pools +List of configured hash tables +List of groups configured (set 0) +List of groups configured (set 1) +Rules configured (set 0, in) +Rules configured (set 0, out) +Rules configured (set 1, in) +Rules configured (set 1, out) +Accounting rules configured (set 0, in) +Accounting rules configured (set 0, out) +Accounting rules configured (set 1, in) +Accounting rules configured (set 1, out) +------------------------------- +> zx0 ip6/0 20 0 6 10:0:0:0:0:3:4:1,10000 > 10:1:1:0:0:0:0:1,1025 +> zx0 ip6/0 20 0 6 10:0:0:0:0:3:4:1,10001 > 10:1:1:0:0:0:0:2,1025 +> zx0 ip6/0 1 0 41 10:1:1:0:0:0:0:0 > 10:1:1:0:0:0:0:2 +> zx0 ip6/0 1 0 41 10:1:1:0:0:0:0:1 > 10:1:2:0:0:0:0:1 +> zx0 ip6/0 20 0 6 10:0:0:0:0:3:4:1,10002 > 10:1:1:0:0:0:0:1,1025 +> zx0 ip6/0 20 0 6 10:0:0:0:0:3:4:1,10002 > 10:1:1:0:0:0:0:1,1025 +> zx0 ip6/0 20 0 6 10:0:0:0:0:3:4:1,10003 > 10:1:1:0:0:0:0:1,1025 +> zx0 ip6/0 8 0 17 10:0:0:0:0:3:4:1,10004 > 10:1:1:0:0:0:0:1,1025 +> zx0 ip6/0 20 0 6 10:0:0:0:0:3:4:1,10005 > 10:1:2:0:0:0:0:1,80 +> zx0 ip6/0 20 0 6 10:0:0:0:0:3:4:1,10006 > 10:1:3:0:0:0:0:1,80 +> zx0 ip6/0 20 0 6 10:0:0:0:0:3:4:1,10007 > 10:1:4:0:0:0:0:1,80 +> zx0 ip6/0 20 0 6 10:0:0:0:0:3:4:1,10008 > 10:1:4:0:0:0:0:1,80 +< zx0 ip6/0 1 0 41 10:1:1:0:0:0:0:1 > 10:1:1:0:0:0:0:2 +< zx0 ip6/0 20 0 6 10:1:1:0:0:0:0:1,1025 > 10:1:1:0:0:0:0:2,1025 +< zx0 ip6/0 1 0 41 10:1:1:0:0:0:0:2 > 10:1:1:0:0:0:0:1 +< zx0 ip6/0 20 0 6 10:1:1:0:0:0:0:1,1026 > 10:0:0:0:0:3:4:5,40000 +< zx0 ip6/0 20 0 6 10:1:1:0:0:0:0:1,1025 > 10:0:0:0:0:3:4:5,40000 +< zx0 ip6/0 8 0 17 10:1:1:0:0:0:0:2,1025 > 10:0:0:0:0:3:4:5,40001 +< zx0 ip6/0 20 0 6 10:1:2:0:0:0:0:1,80 > 10:0:0:0:0:3:4:5,40001 +List of active MAP/Redirect filters: +map zx0 inet6 10:1::/32 -> 10::3:4:0/112 portmap tcp/udp 10000:20000 sequential + +List of active sessions: +MAP 10:1:1::3 2003 <- -> 10::3:4:1 10008 [10:1:4::1 80] +MAP 10:1:1::3 2002 <- -> 10::3:4:1 10007 [10:1:4::1 80] +MAP 10:1:1::3 2001 <- -> 10::3:4:1 10006 [10:1:3::1 80] +MAP 10:1:1::3 2000 <- -> 10::3:4:1 10005 [10:1:2::1 80] +MAP 10:1:1::2 1025 <- -> 10::3:4:1 10004 [10:1:1::1 1025] +MAP 10:1:1::2 1026 <- -> 10::3:4:1 10003 [10:1:1::1 1025] +MAP 10:1:1::2 1025 <- -> 10::3:4:1 10002 [10:1:1::1 1025] +MAP 10:1:1::1 1025 <- -> 10::3:4:1 10001 [10:1:1::2 1025] +MAP 10:1:1::1 1025 <- -> 10::3:4:1 10000 [10:1:1::1 1025] + +Hostmap table: +10:1:1::3,10:1:4::1 -> 10::3:4:1,any (use = 2) +10:1:1::3,10:1:3::1 -> 10::3:4:1,any (use = 1) +10:1:1::3,10:1:2::1 -> 10::3:4:1,any (use = 1) +10:1:1::2,10:1:1::1 -> 10::3:4:1,any (use = 3) +10:1:1::1,10:1:1::2 -> 10::3:4:1,any (use = 1) +10:1:1::1,10:1:1::1 -> 10::3:4:1,any (use = 1) +List of active state sessions: +List of configured pools +List of configured hash tables +List of groups configured (set 0) +List of groups configured (set 1) +Rules configured (set 0, in) +Rules configured (set 0, out) +Rules configured (set 1, in) +Rules configured (set 1, out) +Accounting rules configured (set 0, in) +Accounting rules configured (set 0, out) +Accounting rules configured (set 1, in) +Accounting rules configured (set 1, out) +------------------------------- +> zx0 ip6/0 20 0 6 10:0:0:0:0:3:4:5,40000 > 10:1:1:0:0:0:0:1,1025 +> zx0 ip6/0 20 0 6 10:0:0:0:0:3:4:5,40001 > 10:1:1:0:0:0:0:2,1025 +> zx0 ip6/0 1 0 41 10:1:1:0:0:0:0:0 > 10:1:1:0:0:0:0:2 +> zx0 ip6/0 1 0 41 10:1:1:0:0:0:0:1 > 10:1:2:0:0:0:0:1 +> zx0 ip6/0 20 0 6 10:0:0:0:0:3:4:5,40001 > 10:1:1:0:0:0:0:1,1025 +> zx0 ip6/0 20 0 6 10:0:0:0:0:3:4:5,40001 > 10:1:1:0:0:0:0:1,1025 +16 +> zx0 ip6/0 8 0 17 10:0:0:0:0:3:4:5,40000 > 10:1:1:0:0:0:0:1,1025 +> zx0 ip6/0 20 0 6 10:0:0:0:0:3:4:5,40001 > 10:1:2:0:0:0:0:1,80 +> zx0 ip6/0 20 0 6 10:0:0:0:0:3:4:5,40000 > 10:1:3:0:0:0:0:1,80 +> zx0 ip6/0 20 0 6 10:0:0:0:0:3:4:5,40001 > 10:1:4:0:0:0:0:1,80 +> zx0 ip6/0 20 0 6 10:0:0:0:0:3:4:5,40000 > 10:1:4:0:0:0:0:1,80 +< zx0 ip6/0 1 0 41 10:1:1:0:0:0:0:1 > 10:1:1:0:0:0:0:2 +< zx0 ip6/0 20 0 6 10:1:1:0:0:0:0:1,1025 > 10:1:1:0:0:0:0:2,1025 +< zx0 ip6/0 1 0 41 10:1:1:0:0:0:0:2 > 10:1:1:0:0:0:0:1 +< zx0 ip6/0 20 0 6 10:1:1:0:0:0:0:1,1026 > 10:0:0:0:0:3:4:5,40000 +< zx0 ip6/0 20 0 6 10:1:1:0:0:0:0:1,1025 > 10:1:1:0:0:0:0:1,1025 +< zx0 ip6/0 8 0 17 10:1:1:0:0:0:0:2,1025 > 10:0:0:0:0:3:4:5,40001 +< zx0 ip6/0 20 0 6 10:1:2:0:0:0:0:1,80 > 10:1:1:0:0:0:0:3,2000 +List of active MAP/Redirect filters: +map zx0 inet6 10:1:1::/112 -> 10::3:4:5/128 portmap tcp/udp 40000:40001 sequential + +List of active sessions: +MAP 10:1:1::3 2003 <- -> 10::3:4:5 40000 [10:1:4::1 80] +MAP 10:1:1::3 2002 <- -> 10::3:4:5 40001 [10:1:4::1 80] +MAP 10:1:1::3 2001 <- -> 10::3:4:5 40000 [10:1:3::1 80] +MAP 10:1:1::3 2000 <- -> 10::3:4:5 40001 [10:1:2::1 80] +MAP 10:1:1::2 1025 <- -> 10::3:4:5 40000 [10:1:1::1 1025] +MAP 10:1:1::2 1025 <- -> 10::3:4:5 40001 [10:1:1::1 1025] +MAP 10:1:1::1 1025 <- -> 10::3:4:5 40001 [10:1:1::2 1025] +MAP 10:1:1::1 1025 <- -> 10::3:4:5 40000 [10:1:1::1 1025] + +Hostmap table: +10:1:1::3,10:1:4::1 -> 10::3:4:5,any (use = 2) +10:1:1::3,10:1:3::1 -> 10::3:4:5,any (use = 1) +10:1:1::3,10:1:2::1 -> 10::3:4:5,any (use = 1) +10:1:1::2,10:1:1::1 -> 10::3:4:5,any (use = 2) +10:1:1::1,10:1:1::2 -> 10::3:4:5,any (use = 1) +10:1:1::1,10:1:1::1 -> 10::3:4:5,any (use = 1) +List of active state sessions: +List of configured pools +List of configured hash tables +List of groups configured (set 0) +List of groups configured (set 1) +Rules configured (set 0, in) +Rules configured (set 0, out) +Rules configured (set 1, in) +Rules configured (set 1, out) +Accounting rules configured (set 0, in) +Accounting rules configured (set 0, out) +Accounting rules configured (set 1, in) +Accounting rules configured (set 1, out) +------------------------------- diff --git a/contrib/ipfilter/test/expected/n3 b/contrib/ipfilter/test/expected/n3 index 0e019aefb2b..66ada76e930 100644 --- a/contrib/ipfilter/test/expected/n3 +++ b/contrib/ipfilter/test/expected/n3 @@ -1,12 +1,66 @@ -ip #0 40(20) 6 192.168.2.1,1488 > 203.1.1.1,80 -ip #0 40(20) 6 192.168.2.1,1276 > 203.1.1.1,80 -ip #0 40(20) 6 192.168.2.1,1032 > 203.1.1.1,80 -ip #0 28(20) 17 192.168.2.1,1032 > 203.1.1.1,80 -ip #0 40(20) 6 192.168.2.1,65299 > 203.1.1.1,80 +> zz0 ip #0 40(20) 6 192.168.2.1,1488 > 203.1.1.1,80 +> zz0 ip #0 40(20) 6 192.168.2.1,1276 > 203.1.1.1,80 +> zz0 ip #0 40(20) 6 192.168.2.1,1032 > 203.1.1.1,80 +> zz0 ip #0 28(20) 17 192.168.2.1,1032 > 203.1.1.1,80 +> zz0 ip #0 40(20) 6 192.168.2.1,65299 > 203.1.1.1,80 +List of active MAP/Redirect filters: +map zz0 10.1.0.0/16 -> 192.168.2.0/24 portmap tcp/udp auto + +List of active sessions: +MAP 10.1.255.255 65535 <- -> 192.168.2.1 65299 [203.1.1.1 80] +MAP 10.1.0.0 32768 <- -> 192.168.2.1 1032 [203.1.1.1 80] +MAP 10.1.0.0 32768 <- -> 192.168.2.1 1032 [203.1.1.1 80] +MAP 10.1.1.1 252 <- -> 192.168.2.1 1276 [203.1.1.1 80] +MAP 10.1.1.1 5000 <- -> 192.168.2.1 1488 [203.1.1.1 80] + +Hostmap table: +10.1.255.255,203.1.1.1 -> 192.168.2.1,0.0.0.0 (use = 1) +10.1.0.0,203.1.1.1 -> 192.168.2.1,0.0.0.0 (use = 2) +10.1.1.1,203.1.1.1 -> 192.168.2.1,0.0.0.0 (use = 2) +List of active state sessions: +List of configured pools +List of configured hash tables +List of groups configured (set 0) +List of groups configured (set 1) +Rules configured (set 0, in) +Rules configured (set 0, out) +Rules configured (set 1, in) +Rules configured (set 1, out) +Accounting rules configured (set 0, in) +Accounting rules configured (set 0, out) +Accounting rules configured (set 1, in) +Accounting rules configured (set 1, out) ------------------------------- -ip #0 40(20) 6 192.168.1.1,1488 > 203.1.1.1,80 -ip #0 40(20) 6 192.168.1.1,1276 > 203.1.1.1,80 -ip #0 40(20) 6 192.168.1.0,1032 > 203.1.1.1,80 -ip #0 28(20) 17 192.168.1.0,1032 > 203.1.1.1,80 -ip #0 40(20) 6 192.168.1.255,65299 > 203.1.1.1,80 +> zz0 ip #0 40(20) 6 192.168.1.1,1488 > 203.1.1.1,80 +> zz0 ip #0 40(20) 6 192.168.1.1,1276 > 203.1.1.1,80 +> zz0 ip #0 40(20) 6 192.168.1.0,1032 > 203.1.1.1,80 +> zz0 ip #0 28(20) 17 192.168.1.0,1032 > 203.1.1.1,80 +> zz0 ip #0 40(20) 6 192.168.1.255,65299 > 203.1.1.1,80 +List of active MAP/Redirect filters: +map-block zz0 10.1.0.0/16 -> 192.168.1.0/24 ports 252 + +List of active sessions: +MAP-BLOCK 10.1.255.255 65535 <- -> 192.168.1.255 65299 [203.1.1.1 80] +MAP-BLOCK 10.1.0.0 32768 <- -> 192.168.1.0 1032 [203.1.1.1 80] +MAP-BLOCK 10.1.0.0 32768 <- -> 192.168.1.0 1032 [203.1.1.1 80] +MAP-BLOCK 10.1.1.1 252 <- -> 192.168.1.1 1276 [203.1.1.1 80] +MAP-BLOCK 10.1.1.1 5000 <- -> 192.168.1.1 1488 [203.1.1.1 80] + +Hostmap table: +10.1.255.255,203.1.1.1 -> 192.168.1.1,0.0.0.0 (use = 1) +10.1.0.0,203.1.1.1 -> 192.168.1.1,0.0.0.0 (use = 2) +10.1.1.1,203.1.1.1 -> 192.168.1.1,0.0.0.0 (use = 2) +List of active state sessions: +List of configured pools +List of configured hash tables +List of groups configured (set 0) +List of groups configured (set 1) +Rules configured (set 0, in) +Rules configured (set 0, out) +Rules configured (set 1, in) +Rules configured (set 1, out) +Accounting rules configured (set 0, in) +Accounting rules configured (set 0, out) +Accounting rules configured (set 1, in) +Accounting rules configured (set 1, out) ------------------------------- diff --git a/contrib/ipfilter/test/expected/n4 b/contrib/ipfilter/test/expected/n4 index 863217c1db7..746ef7e1858 100644 --- a/contrib/ipfilter/test/expected/n4 +++ b/contrib/ipfilter/test/expected/n4 @@ -1,66 +1,190 @@ -ip #0 40(20) 6 10.3.3.3,12345 > 10.2.2.1,10023 -ip #0 40(20) 6 10.1.1.1,23 > 10.3.3.3,12345 -ip #0 40(20) 6 10.3.3.3,12345 > 10.1.1.1,53 -ip #0 40(20) 6 10.2.2.1,10053 > 10.3.3.3,12345 -ip #0 40(20) 6 10.3.3.3,12346 > 10.1.0.0,23 -ip #0 40(20) 6 10.2.2.1,10023 > 10.3.3.3,12346 -ip #0 28(20) 17 10.3.3.3,12345 > 10.1.1.0,53 -ip #0 28(20) 17 10.2.2.1,10053 > 10.3.3.3,12345 -ip #0 40(20) 6 10.3.3.3,12345 > 10.1.1.0,53 -ip #0 40(20) 6 10.2.2.1,53 > 10.3.3.3,12345 +< zx0 ip #0 40(20) 6 10.3.3.3,12345 > 10.2.2.1,10023 +> zx0 ip #0 40(20) 6 10.1.1.1,23 > 10.3.3.3,12345 +< zx0 ip #0 40(20) 6 10.3.3.3,12345 > 10.1.1.1,53 +> zx0 ip #0 40(20) 6 10.2.2.1,10053 > 10.3.3.3,12345 +< zx0 ip #0 40(20) 6 10.3.3.3,12346 > 10.1.0.0,23 +> zx0 ip #0 40(20) 6 10.2.2.1,10023 > 10.3.3.3,12346 +< zx0 ip #0 28(20) 17 10.3.3.3,12345 > 10.1.1.0,53 +> zx0 ip #0 28(20) 17 10.2.2.1,10053 > 10.3.3.3,12345 +< zx0 ip #0 40(20) 6 10.3.3.3,12345 > 10.1.1.0,53 +> zx0 ip #0 40(20) 6 10.2.2.1,53 > 10.3.3.3,12345 +List of active MAP/Redirect filters: +rdr zx0 10.1.1.1/32 port 23 -> 10.2.2.1/32 port 10023 tcp + +List of active sessions: +RDR 10.2.2.1 10023 <- -> 10.1.1.1 23 [10.3.3.3 12345] + +Hostmap table: +List of active state sessions: +List of configured pools +List of configured hash tables +List of groups configured (set 0) +List of groups configured (set 1) +Rules configured (set 0, in) +Rules configured (set 0, out) +Rules configured (set 1, in) +Rules configured (set 1, out) +Accounting rules configured (set 0, in) +Accounting rules configured (set 0, out) +Accounting rules configured (set 1, in) +Accounting rules configured (set 1, out) ------------------------------- -ip #0 40(20) 6 10.3.3.3,12345 > 10.2.2.1,10023 -ip #0 40(20) 6 10.1.1.1,23 > 10.3.3.3,12345 -ip #0 40(20) 6 10.3.3.3,12345 > 10.1.1.1,53 -ip #0 40(20) 6 10.2.2.1,10053 > 10.3.3.3,12345 -ip #0 40(20) 6 10.3.3.3,12346 > 10.1.0.0,23 -ip #0 40(20) 6 10.2.2.1,10023 > 10.3.3.3,12346 -ip #0 28(20) 17 10.3.3.3,12345 > 10.1.1.0,53 -ip #0 28(20) 17 10.2.2.1,10053 > 10.3.3.3,12345 -ip #0 40(20) 6 10.3.3.3,12345 > 10.1.1.0,53 -ip #0 40(20) 6 10.2.2.1,53 > 10.3.3.3,12345 +< zx0 ip #0 40(20) 6 10.3.3.3,12345 > 10.2.2.1,10023 +> zx0 ip #0 40(20) 6 10.1.1.1,23 > 10.3.3.3,12345 +< zx0 ip #0 40(20) 6 10.3.3.3,12345 > 10.1.1.1,53 +> zx0 ip #0 40(20) 6 10.2.2.1,10053 > 10.3.3.3,12345 +< zx0 ip #0 40(20) 6 10.3.3.3,12346 > 10.1.0.0,23 +> zx0 ip #0 40(20) 6 10.2.2.1,10023 > 10.3.3.3,12346 +< zx0 ip #0 28(20) 17 10.3.3.3,12345 > 10.1.1.0,53 +> zx0 ip #0 28(20) 17 10.2.2.1,10053 > 10.3.3.3,12345 +< zx0 ip #0 40(20) 6 10.3.3.3,12345 > 10.1.1.0,53 +> zx0 ip #0 40(20) 6 10.2.2.1,53 > 10.3.3.3,12345 +List of active MAP/Redirect filters: +rdr zx0 10.1.1.0/24 port 23 -> 10.2.2.1/32 port 10023 tcp + +List of active sessions: +RDR 10.2.2.1 10023 <- -> 10.1.1.1 23 [10.3.3.3 12345] + +Hostmap table: +List of active state sessions: +List of configured pools +List of configured hash tables +List of groups configured (set 0) +List of groups configured (set 1) +Rules configured (set 0, in) +Rules configured (set 0, out) +Rules configured (set 1, in) +Rules configured (set 1, out) +Accounting rules configured (set 0, in) +Accounting rules configured (set 0, out) +Accounting rules configured (set 1, in) +Accounting rules configured (set 1, out) ------------------------------- -ip #0 40(20) 6 10.3.3.3,12345 > 10.2.2.1,10023 -ip #0 40(20) 6 10.1.1.1,23 > 10.3.3.3,12345 -ip #0 40(20) 6 10.3.3.3,12345 > 10.1.1.1,53 -ip #0 40(20) 6 10.2.2.1,10053 > 10.3.3.3,12345 -ip #0 40(20) 6 10.3.3.3,12346 > 10.2.2.1,10023 -ip #0 40(20) 6 10.1.0.0,23 > 10.3.3.3,12346 -ip #0 28(20) 17 10.3.3.3,12345 > 10.1.1.0,53 -ip #0 28(20) 17 10.2.2.1,10053 > 10.3.3.3,12345 -ip #0 40(20) 6 10.3.3.3,12345 > 10.1.1.0,53 -ip #0 40(20) 6 10.2.2.1,53 > 10.3.3.3,12345 +< zx0 ip #0 40(20) 6 10.3.3.3,12345 > 10.2.2.1,10023 +> zx0 ip #0 40(20) 6 10.1.1.1,23 > 10.3.3.3,12345 +< zx0 ip #0 40(20) 6 10.3.3.3,12345 > 10.1.1.1,53 +> zx0 ip #0 40(20) 6 10.2.2.1,10053 > 10.3.3.3,12345 +< zx0 ip #0 40(20) 6 10.3.3.3,12346 > 10.2.2.1,10023 +> zx0 ip #0 40(20) 6 10.1.0.0,23 > 10.3.3.3,12346 +< zx0 ip #0 28(20) 17 10.3.3.3,12345 > 10.1.1.0,53 +> zx0 ip #0 28(20) 17 10.2.2.1,10053 > 10.3.3.3,12345 +< zx0 ip #0 40(20) 6 10.3.3.3,12345 > 10.1.1.0,53 +> zx0 ip #0 40(20) 6 10.2.2.1,53 > 10.3.3.3,12345 +List of active MAP/Redirect filters: +rdr zx0 0/0 port 23 -> 10.2.2.1/32 port 10023 tcp + +List of active sessions: +RDR 10.2.2.1 10023 <- -> 10.1.0.0 23 [10.3.3.3 12346] +RDR 10.2.2.1 10023 <- -> 10.1.1.1 23 [10.3.3.3 12345] + +Hostmap table: +List of active state sessions: +List of configured pools +List of configured hash tables +List of groups configured (set 0) +List of groups configured (set 1) +Rules configured (set 0, in) +Rules configured (set 0, out) +Rules configured (set 1, in) +Rules configured (set 1, out) +Accounting rules configured (set 0, in) +Accounting rules configured (set 0, out) +Accounting rules configured (set 1, in) +Accounting rules configured (set 1, out) ------------------------------- -ip #0 40(20) 6 10.3.3.3,12345 > 10.1.1.1,23 -ip #0 40(20) 6 10.2.2.1,10023 > 10.3.3.3,12345 -ip #0 40(20) 6 10.3.3.3,12345 > 10.1.1.1,53 -ip #0 40(20) 6 10.2.2.1,10053 > 10.3.3.3,12345 -ip #0 40(20) 6 10.3.3.3,12346 > 10.1.0.0,23 -ip #0 40(20) 6 10.2.2.1,10023 > 10.3.3.3,12346 -ip #0 28(20) 17 10.3.3.3,12345 > 10.2.2.1,10053 -ip #0 28(20) 17 10.1.1.0,53 > 10.3.3.3,12345 -ip #0 40(20) 6 10.3.3.3,12345 > 10.1.1.0,53 -ip #0 40(20) 6 10.2.2.1,53 > 10.3.3.3,12345 +< zx0 ip #0 40(20) 6 10.3.3.3,12345 > 10.1.1.1,23 +> zx0 ip #0 40(20) 6 10.2.2.1,10023 > 10.3.3.3,12345 +< zx0 ip #0 40(20) 6 10.3.3.3,12345 > 10.1.1.1,53 +> zx0 ip #0 40(20) 6 10.2.2.1,10053 > 10.3.3.3,12345 +< zx0 ip #0 40(20) 6 10.3.3.3,12346 > 10.1.0.0,23 +> zx0 ip #0 40(20) 6 10.2.2.1,10023 > 10.3.3.3,12346 +< zx0 ip #0 28(20) 17 10.3.3.3,12345 > 10.2.2.1,10053 +> zx0 ip #0 28(20) 17 10.1.1.0,53 > 10.3.3.3,12345 +< zx0 ip #0 40(20) 6 10.3.3.3,12345 > 10.1.1.0,53 +> zx0 ip #0 40(20) 6 10.2.2.1,53 > 10.3.3.3,12345 +List of active MAP/Redirect filters: +rdr zx0 10.1.1.0/24 port 53 -> 10.2.2.1/32 port 10053 udp + +List of active sessions: +RDR 10.2.2.1 10053 <- -> 10.1.1.0 53 [10.3.3.3 12345] + +Hostmap table: +List of active state sessions: +List of configured pools +List of configured hash tables +List of groups configured (set 0) +List of groups configured (set 1) +Rules configured (set 0, in) +Rules configured (set 0, out) +Rules configured (set 1, in) +Rules configured (set 1, out) +Accounting rules configured (set 0, in) +Accounting rules configured (set 0, out) +Accounting rules configured (set 1, in) +Accounting rules configured (set 1, out) ------------------------------- -ip #0 40(20) 6 10.3.3.3,12345 > 10.2.2.1,23 -ip #0 40(20) 6 10.2.2.1,10023 > 10.3.3.3,12345 -ip #0 40(20) 6 10.3.3.3,12345 > 10.2.2.1,53 -ip #0 40(20) 6 10.2.2.1,10053 > 10.3.3.3,12345 -ip #0 40(20) 6 10.3.3.3,12346 > 10.1.0.0,23 -ip #0 40(20) 6 10.2.2.1,10023 > 10.3.3.3,12346 -ip #0 28(20) 17 10.3.3.3,12345 > 10.1.1.0,53 -ip #0 28(20) 17 10.2.2.1,10053 > 10.3.3.3,12345 -ip #0 40(20) 6 10.3.3.3,12345 > 10.1.1.0,53 -ip #0 40(20) 6 10.1.1.1,53 > 10.3.3.3,12345 +< zx0 ip #0 40(20) 6 10.3.3.3,12345 > 10.2.2.1,23 +> zx0 ip #0 40(20) 6 10.2.2.1,10023 > 10.3.3.3,12345 +< zx0 ip #0 40(20) 6 10.3.3.3,12345 > 10.2.2.1,53 +> zx0 ip #0 40(20) 6 10.2.2.1,10053 > 10.3.3.3,12345 +< zx0 ip #0 40(20) 6 10.3.3.3,12346 > 10.1.0.0,23 +> zx0 ip #0 40(20) 6 10.2.2.1,10023 > 10.3.3.3,12346 +< zx0 ip #0 28(20) 17 10.3.3.3,12345 > 10.1.1.0,53 +> zx0 ip #0 28(20) 17 10.2.2.1,10053 > 10.3.3.3,12345 +15 +> zx0 ip #0 40(20) 6 10.1.1.1,53 > 10.3.3.3,12345 +List of active MAP/Redirect filters: +rdr zx0 10.1.1.0/24 port 0 -> 10.2.2.1/32 port 0 tcp + +List of active sessions: +RDR 10.2.2.1 53 <- -> 10.1.1.1 53 [10.3.3.3 12345] +RDR 10.2.2.1 23 <- -> 10.1.1.1 23 [10.3.3.3 12345] + +Hostmap table: +List of active state sessions: +List of configured pools +List of configured hash tables +List of groups configured (set 0) +List of groups configured (set 1) +Rules configured (set 0, in) +Rules configured (set 0, out) +Rules configured (set 1, in) +Rules configured (set 1, out) +Accounting rules configured (set 0, in) +Accounting rules configured (set 0, out) +Accounting rules configured (set 1, in) +Accounting rules configured (set 1, out) ------------------------------- -ip #0 40(20) 6 10.3.3.3,12345 > 10.2.2.1,23 -ip #0 40(20) 6 10.2.2.1,10023 > 10.3.3.3,12345 -ip #0 40(20) 6 10.3.3.3,12345 > 10.2.2.1,53 -ip #0 40(20) 6 10.2.2.1,10053 > 10.3.3.3,12345 -ip #0 40(20) 6 10.3.3.3,12346 > 10.1.0.0,23 -ip #0 40(20) 6 10.2.2.1,10023 > 10.3.3.3,12346 -ip #0 28(20) 17 10.3.3.3,12345 > 10.2.2.1,53 -ip #0 28(20) 17 10.2.2.1,10053 > 10.3.3.3,12345 -ip #0 40(20) 6 10.3.3.3,12345 > 10.1.1.0,53 -ip #0 40(20) 6 10.1.1.1,53 > 10.3.3.3,12345 +< zx0 ip #0 40(20) 6 10.3.3.3,12345 > 10.2.2.1,23 +> zx0 ip #0 40(20) 6 10.2.2.1,10023 > 10.3.3.3,12345 +< zx0 ip #0 40(20) 6 10.3.3.3,12345 > 10.2.2.1,53 +> zx0 ip #0 40(20) 6 10.2.2.1,10053 > 10.3.3.3,12345 +< zx0 ip #0 40(20) 6 10.3.3.3,12346 > 10.1.0.0,23 +> zx0 ip #0 40(20) 6 10.2.2.1,10023 > 10.3.3.3,12346 +< zx0 ip #0 28(20) 17 10.3.3.3,12345 > 10.2.2.1,53 +> zx0 ip #0 28(20) 17 10.2.2.1,10053 > 10.3.3.3,12345 +15 +> zx0 ip #0 40(20) 6 10.1.1.1,53 > 10.3.3.3,12345 +List of active MAP/Redirect filters: +rdr zx0 10.1.1.0/24 -> 10.2.2.1/32 ip + +List of active sessions: +RDR 10.2.2.1 53 <- -> 10.1.1.0 53 [10.3.3.3 12345] +RDR 10.2.2.1 53 <- -> 10.1.1.1 53 [10.3.3.3 12345] +RDR 10.2.2.1 23 <- -> 10.1.1.1 23 [10.3.3.3 12345] + +Hostmap table: +List of active state sessions: +List of configured pools +List of configured hash tables +List of groups configured (set 0) +List of groups configured (set 1) +Rules configured (set 0, in) +Rules configured (set 0, out) +Rules configured (set 1, in) +Rules configured (set 1, out) +Accounting rules configured (set 0, in) +Accounting rules configured (set 0, out) +Accounting rules configured (set 1, in) +Accounting rules configured (set 1, out) ------------------------------- diff --git a/contrib/ipfilter/test/expected/n4_6 b/contrib/ipfilter/test/expected/n4_6 new file mode 100644 index 00000000000..e9a5ce34507 --- /dev/null +++ b/contrib/ipfilter/test/expected/n4_6 @@ -0,0 +1,190 @@ +< zx0 ip6/0 20 0 6 10:3:3:0:0:0:0:3,12345 > 10:0:0:0:0:2:2:1,10023 +> zx0 ip6/0 20 0 6 10:1:1:0:0:0:0:1,23 > 10:3:3:0:0:0:0:3,12345 +< zx0 ip6/0 20 0 6 10:3:3:0:0:0:0:3,12345 > 10:1:1:0:0:0:0:1,53 +> zx0 ip6/0 20 0 6 10:0:0:0:0:2:2:1,10053 > 10:3:3:0:0:0:0:3,12345 +< zx0 ip6/0 20 0 6 10:3:3:0:0:0:0:3,12346 > 10:1:0:0:0:0:0:0,23 +> zx0 ip6/0 20 0 6 10:0:0:0:0:2:2:1,10023 > 10:3:3:0:0:0:0:3,12346 +< zx0 ip6/0 8 0 17 10:3:3:0:0:0:0:3,12345 > 10:1:1:0:0:0:0:0,53 +> zx0 ip6/0 8 0 17 10:0:0:0:0:2:2:1,10053 > 10:3:3:0:0:0:0:3,12345 +< zx0 ip6/0 20 0 6 10:3:3:0:0:0:0:3,12345 > 10:1:1:0:0:0:0:0,53 +> zx0 ip6/0 20 0 6 10:0:0:0:0:2:2:1,53 > 10:3:3:0:0:0:0:3,12345 +List of active MAP/Redirect filters: +rdr zx0 inet6 10:1:1::1/128 port 23 -> 10::2:2:1/128 port 10023 tcp + +List of active sessions: +RDR 10::2:2:1 10023 <- -> 10:1:1::1 23 [10:3:3::3 12345] + +Hostmap table: +List of active state sessions: +List of configured pools +List of configured hash tables +List of groups configured (set 0) +List of groups configured (set 1) +Rules configured (set 0, in) +Rules configured (set 0, out) +Rules configured (set 1, in) +Rules configured (set 1, out) +Accounting rules configured (set 0, in) +Accounting rules configured (set 0, out) +Accounting rules configured (set 1, in) +Accounting rules configured (set 1, out) +------------------------------- +< zx0 ip6/0 20 0 6 10:3:3:0:0:0:0:3,12345 > 10:0:0:0:0:2:2:1,10023 +> zx0 ip6/0 20 0 6 10:1:1:0:0:0:0:1,23 > 10:3:3:0:0:0:0:3,12345 +< zx0 ip6/0 20 0 6 10:3:3:0:0:0:0:3,12345 > 10:1:1:0:0:0:0:1,53 +> zx0 ip6/0 20 0 6 10:0:0:0:0:2:2:1,10053 > 10:3:3:0:0:0:0:3,12345 +< zx0 ip6/0 20 0 6 10:3:3:0:0:0:0:3,12346 > 10:1:0:0:0:0:0:0,23 +> zx0 ip6/0 20 0 6 10:0:0:0:0:2:2:1,10023 > 10:3:3:0:0:0:0:3,12346 +< zx0 ip6/0 8 0 17 10:3:3:0:0:0:0:3,12345 > 10:1:1:0:0:0:0:0,53 +> zx0 ip6/0 8 0 17 10:0:0:0:0:2:2:1,10053 > 10:3:3:0:0:0:0:3,12345 +< zx0 ip6/0 20 0 6 10:3:3:0:0:0:0:3,12345 > 10:1:1:0:0:0:0:0,53 +> zx0 ip6/0 20 0 6 10:0:0:0:0:2:2:1,53 > 10:3:3:0:0:0:0:3,12345 +List of active MAP/Redirect filters: +rdr zx0 inet6 10:1:1::/112 port 23 -> 10::2:2:1/128 port 10023 tcp + +List of active sessions: +RDR 10::2:2:1 10023 <- -> 10:1:1::1 23 [10:3:3::3 12345] + +Hostmap table: +List of active state sessions: +List of configured pools +List of configured hash tables +List of groups configured (set 0) +List of groups configured (set 1) +Rules configured (set 0, in) +Rules configured (set 0, out) +Rules configured (set 1, in) +Rules configured (set 1, out) +Accounting rules configured (set 0, in) +Accounting rules configured (set 0, out) +Accounting rules configured (set 1, in) +Accounting rules configured (set 1, out) +------------------------------- +< zx0 ip6/0 20 0 6 10:3:3:0:0:0:0:3,12345 > 10:0:0:0:0:2:2:1,10023 +> zx0 ip6/0 20 0 6 10:1:1:0:0:0:0:1,23 > 10:3:3:0:0:0:0:3,12345 +< zx0 ip6/0 20 0 6 10:3:3:0:0:0:0:3,12345 > 10:1:1:0:0:0:0:1,53 +> zx0 ip6/0 20 0 6 10:0:0:0:0:2:2:1,10053 > 10:3:3:0:0:0:0:3,12345 +< zx0 ip6/0 20 0 6 10:3:3:0:0:0:0:3,12346 > 10:0:0:0:0:2:2:1,10023 +> zx0 ip6/0 20 0 6 10:1:0:0:0:0:0:0,23 > 10:3:3:0:0:0:0:3,12346 +< zx0 ip6/0 8 0 17 10:3:3:0:0:0:0:3,12345 > 10:1:1:0:0:0:0:0,53 +> zx0 ip6/0 8 0 17 10:0:0:0:0:2:2:1,10053 > 10:3:3:0:0:0:0:3,12345 +< zx0 ip6/0 20 0 6 10:3:3:0:0:0:0:3,12345 > 10:1:1:0:0:0:0:0,53 +> zx0 ip6/0 20 0 6 10:0:0:0:0:2:2:1,53 > 10:3:3:0:0:0:0:3,12345 +List of active MAP/Redirect filters: +rdr zx0 inet6 any port 23 -> 10::2:2:1/128 port 10023 tcp + +List of active sessions: +RDR 10::2:2:1 10023 <- -> 10:1:: 23 [10:3:3::3 12346] +RDR 10::2:2:1 10023 <- -> 10:1:1::1 23 [10:3:3::3 12345] + +Hostmap table: +List of active state sessions: +List of configured pools +List of configured hash tables +List of groups configured (set 0) +List of groups configured (set 1) +Rules configured (set 0, in) +Rules configured (set 0, out) +Rules configured (set 1, in) +Rules configured (set 1, out) +Accounting rules configured (set 0, in) +Accounting rules configured (set 0, out) +Accounting rules configured (set 1, in) +Accounting rules configured (set 1, out) +------------------------------- +< zx0 ip6/0 20 0 6 10:3:3:0:0:0:0:3,12345 > 10:1:1:0:0:0:0:1,23 +> zx0 ip6/0 20 0 6 10:0:0:0:0:2:2:1,10023 > 10:3:3:0:0:0:0:3,12345 +< zx0 ip6/0 20 0 6 10:3:3:0:0:0:0:3,12345 > 10:1:1:0:0:0:0:1,53 +> zx0 ip6/0 20 0 6 10:0:0:0:0:2:2:1,10053 > 10:3:3:0:0:0:0:3,12345 +< zx0 ip6/0 20 0 6 10:3:3:0:0:0:0:3,12346 > 10:1:0:0:0:0:0:0,23 +> zx0 ip6/0 20 0 6 10:0:0:0:0:2:2:1,10023 > 10:3:3:0:0:0:0:3,12346 +< zx0 ip6/0 8 0 17 10:3:3:0:0:0:0:3,12345 > 10:0:0:0:0:2:2:1,10053 +> zx0 ip6/0 8 0 17 10:1:1:0:0:0:0:0,53 > 10:3:3:0:0:0:0:3,12345 +< zx0 ip6/0 20 0 6 10:3:3:0:0:0:0:3,12345 > 10:1:1:0:0:0:0:0,53 +> zx0 ip6/0 20 0 6 10:0:0:0:0:2:2:1,53 > 10:3:3:0:0:0:0:3,12345 +List of active MAP/Redirect filters: +rdr zx0 inet6 10:1:1::/112 port 53 -> 10::2:2:1/128 port 10053 udp + +List of active sessions: +RDR 10::2:2:1 10053 <- -> 10:1:1:: 53 [10:3:3::3 12345] + +Hostmap table: +List of active state sessions: +List of configured pools +List of configured hash tables +List of groups configured (set 0) +List of groups configured (set 1) +Rules configured (set 0, in) +Rules configured (set 0, out) +Rules configured (set 1, in) +Rules configured (set 1, out) +Accounting rules configured (set 0, in) +Accounting rules configured (set 0, out) +Accounting rules configured (set 1, in) +Accounting rules configured (set 1, out) +------------------------------- +< zx0 ip6/0 20 0 6 10:3:3:0:0:0:0:3,12345 > 10:0:0:0:0:2:2:1,23 +> zx0 ip6/0 20 0 6 10:0:0:0:0:2:2:1,10023 > 10:3:3:0:0:0:0:3,12345 +< zx0 ip6/0 20 0 6 10:3:3:0:0:0:0:3,12345 > 10:0:0:0:0:2:2:1,53 +> zx0 ip6/0 20 0 6 10:0:0:0:0:2:2:1,10053 > 10:3:3:0:0:0:0:3,12345 +< zx0 ip6/0 20 0 6 10:3:3:0:0:0:0:3,12346 > 10:1:0:0:0:0:0:0,23 +> zx0 ip6/0 20 0 6 10:0:0:0:0:2:2:1,10023 > 10:3:3:0:0:0:0:3,12346 +< zx0 ip6/0 8 0 17 10:3:3:0:0:0:0:3,12345 > 10:1:1:0:0:0:0:0,53 +> zx0 ip6/0 8 0 17 10:0:0:0:0:2:2:1,10053 > 10:3:3:0:0:0:0:3,12345 +16 +> zx0 ip6/0 20 0 6 10:1:1:0:0:0:0:1,53 > 10:3:3:0:0:0:0:3,12345 +List of active MAP/Redirect filters: +rdr zx0 inet6 10:1:1::/112 port 0 -> 10::2:2:1/128 port 0 tcp + +List of active sessions: +RDR 10::2:2:1 53 <- -> 10:1:1::1 53 [10:3:3::3 12345] +RDR 10::2:2:1 23 <- -> 10:1:1::1 23 [10:3:3::3 12345] + +Hostmap table: +List of active state sessions: +List of configured pools +List of configured hash tables +List of groups configured (set 0) +List of groups configured (set 1) +Rules configured (set 0, in) +Rules configured (set 0, out) +Rules configured (set 1, in) +Rules configured (set 1, out) +Accounting rules configured (set 0, in) +Accounting rules configured (set 0, out) +Accounting rules configured (set 1, in) +Accounting rules configured (set 1, out) +------------------------------- +< zx0 ip6/0 20 0 6 10:3:3:0:0:0:0:3,12345 > 10:0:0:0:0:2:2:1,23 +> zx0 ip6/0 20 0 6 10:0:0:0:0:2:2:1,10023 > 10:3:3:0:0:0:0:3,12345 +< zx0 ip6/0 20 0 6 10:3:3:0:0:0:0:3,12345 > 10:0:0:0:0:2:2:1,53 +> zx0 ip6/0 20 0 6 10:0:0:0:0:2:2:1,10053 > 10:3:3:0:0:0:0:3,12345 +< zx0 ip6/0 20 0 6 10:3:3:0:0:0:0:3,12346 > 10:1:0:0:0:0:0:0,23 +> zx0 ip6/0 20 0 6 10:0:0:0:0:2:2:1,10023 > 10:3:3:0:0:0:0:3,12346 +< zx0 ip6/0 8 0 17 10:3:3:0:0:0:0:3,12345 > 10:0:0:0:0:2:2:1,53 +> zx0 ip6/0 8 0 17 10:0:0:0:0:2:2:1,10053 > 10:3:3:0:0:0:0:3,12345 +16 +> zx0 ip6/0 20 0 6 10:1:1:0:0:0:0:1,53 > 10:3:3:0:0:0:0:3,12345 +List of active MAP/Redirect filters: +rdr zx0 inet6 10:1:1::/112 -> 10::2:2:1/128 ip + +List of active sessions: +RDR 10::2:2:1 53 <- -> 10:1:1:: 53 [10:3:3::3 12345] +RDR 10::2:2:1 53 <- -> 10:1:1::1 53 [10:3:3::3 12345] +RDR 10::2:2:1 23 <- -> 10:1:1::1 23 [10:3:3::3 12345] + +Hostmap table: +List of active state sessions: +List of configured pools +List of configured hash tables +List of groups configured (set 0) +List of groups configured (set 1) +Rules configured (set 0, in) +Rules configured (set 0, out) +Rules configured (set 1, in) +Rules configured (set 1, out) +Accounting rules configured (set 0, in) +Accounting rules configured (set 0, out) +Accounting rules configured (set 1, in) +Accounting rules configured (set 1, out) +------------------------------- diff --git a/contrib/ipfilter/test/expected/n5 b/contrib/ipfilter/test/expected/n5 index 0e578b64bcf..423bf485008 100644 --- a/contrib/ipfilter/test/expected/n5 +++ b/contrib/ipfilter/test/expected/n5 @@ -1,330 +1,533 @@ -ip #0 20(20) 255 10.1.1.0 > 10.1.1.2 -ip #0 20(20) 255 10.2.2.2 > 10.1.1.2 -ip #0 20(20) 255 10.1.1.2 > 10.1.1.1 -ip #0 40(20) 6 10.1.1.2,1025 > 10.1.1.1,1025 -ip #0 40(20) 6 10.1.1.2,1026 > 10.1.1.1,1025 -ip #0 20(20) 255 10.2.2.1 > 10.1.2.1 -ip #0 20(20) 255 10.2.2.2 > 10.1.2.1 -ip #0 20(20) 255 10.1.1.1 > 10.1.1.2 -ip #0 20(20) 255 10.1.1.2 > 10.1.1.1 -ip #0 20(20) 255 10.2.2.1 > 10.2.1.1 -ip #0 20(20) 255 10.2.2.2 > 10.2.1.1 -ip #0 20(20) 255 10.2.2.3 > 10.1.1.1 -ip #0 20(20) 255 10.2.3.4 > 10.2.2.2 -ip #0 20(20) 255 10.1.1.1 > 10.2.2.2 -ip #0 20(20) 255 10.1.1.2 > 10.1.1.1 -ip #0 20(20) 255 10.1.1.0 > 10.3.4.5 -ip #0 20(20) 255 10.1.1.1 > 10.3.4.5 -ip #0 20(20) 255 10.1.1.2 > 10.3.4.5 -ip #0 40(20) 6 10.1.1.1,1025 > 10.3.4.5,1025 -ip #0 48(20) 1 10.2.2.2 > 10.4.3.2 -ip #0 48(20) 1 10.4.3.2 > 10.1.1.1 -ip #0 48(20) 1 10.4.3.2 > 10.3.4.3 -ip #0 48(20) 1 10.4.3.2 > 10.3.4.5 -ip #0 20(20) 34 10.1.1.2 > 10.4.3.2 -ip #0 20(20) 34 10.4.3.2 > 10.3.4.4 -ip #0 20(20) 34 10.1.1.2 > 10.4.3.4 -ip #0 20(20) 34 10.4.3.4 > 10.3.4.5 -ip #0 20(20) 34 10.1.1.3 > 10.4.3.4 -ip #0 20(20) 34 10.4.3.4 > 10.3.4.6 -ip #0 20(20) 35 10.1.1.3 > 10.4.3.4 -ip #0 20(20) 35 10.4.3.4 > 10.3.4.7 -ip #0 40(20) 6 10.2.2.2,1025 > 10.1.1.1,1025 -ip #0 40(20) 6 10.2.2.2,1025 > 10.1.1.2,1025 -ip #0 20(20) 0 10.1.1.0 > 10.1.1.2 -ip #0 20(20) 0 10.2.2.2 > 10.1.2.1 -ip #0 40(20) 6 10.1.1.2,1025 > 10.1.1.1,1025 -ip #0 40(20) 6 10.1.1.2,1025 > 10.1.1.1,1025 -ip #0 40(20) 6 10.1.1.2,1026 > 10.1.1.1,1025 -ip #0 28(20) 17 10.1.1.2,1025 > 10.1.1.1,1025 -ip #0 40(20) 6 10.1.1.3,2000 > 10.1.2.1,80 -ip #0 40(20) 6 10.1.1.3,2001 > 10.1.3.1,80 -ip #0 40(20) 6 10.1.1.3,2002 > 10.1.4.1,80 -ip #0 40(20) 6 10.1.1.3,2003 > 10.1.4.1,80 -ip #0 20(20) 0 10.1.1.1 > 10.1.1.2 -ip #0 40(20) 6 10.1.1.1,1025 > 10.1.1.2,1025 -ip #0 20(20) 0 10.1.1.2 > 10.1.1.1 -ip #0 40(20) 6 10.2.2.2,1026 > 10.3.4.5,40000 -ip #0 40(20) 6 10.1.1.1,1026 > 10.3.4.5,40000 -ip #0 40(20) 6 10.2.2.2,1025 > 10.3.4.5,40000 -ip #0 40(20) 6 10.1.1.1,1025 > 10.3.4.5,40000 -ip #0 28(20) 17 10.1.1.2,1025 > 10.3.4.5,40001 -ip #0 28(20) 17 10.1.1.2,1025 > 10.3.4.5,40001 -ip #0 40(20) 6 10.1.2.1,80 > 10.3.4.5,40001 -ip #0 40(20) 6 10.1.2.1,80 > 10.3.4.5,40001 +> zx0 ip #0 20(20) 255 10.1.1.0 > 10.1.1.2 +> zx0 ip #0 20(20) 255 10.2.2.2 > 10.1.1.2 +> zx0 ip #0 20(20) 255 10.1.1.2 > 10.1.1.1 +> zx0 ip #0 40(20) 6 10.1.1.2,1025 > 10.1.1.1,1025 +> zx0 ip #0 40(20) 6 10.1.1.2,1026 > 10.1.1.1,1025 +> zx0 ip #0 20(20) 255 10.2.2.1 > 10.1.2.1 +> zx0 ip #0 20(20) 255 10.2.2.2 > 10.1.2.1 +< zx0 ip #0 20(20) 255 10.1.1.1 > 10.1.1.2 +< zx0 ip #0 20(20) 255 10.1.1.2 > 10.1.1.1 +< zx0 ip #0 20(20) 255 10.2.2.1 > 10.2.1.1 +< zx0 ip #0 20(20) 255 10.2.2.2 > 10.2.1.1 +< zx0 ip #0 20(20) 255 10.2.2.3 > 10.1.1.1 +< zx0 ip #0 20(20) 255 10.2.3.4 > 10.2.2.2 +< zx0 ip #0 20(20) 255 10.1.1.1 > 10.2.2.2 +< zx0 ip #0 20(20) 255 10.1.1.2 > 10.1.1.1 +< zx0 ip #0 20(20) 255 10.1.1.0 > 10.3.4.5 +< zx0 ip #0 20(20) 255 10.1.1.1 > 10.3.4.5 +< zx0 ip #0 20(20) 255 10.1.1.2 > 10.3.4.5 +< zx0 ip #0 40(20) 6 10.1.1.1,1025 > 10.3.4.5,1025 +> zx0 ip #0 48(20) 1 10.2.2.2 > 10.4.3.2 +< zx0 ip #0 48(20) 1 10.4.3.2 > 10.1.1.1 +< zx0 ip #0 48(20) 1 10.4.3.2 > 10.3.4.3 +< zx0 ip #0 48(20) 1 10.4.3.2 > 10.3.4.5 +> zx0 ip #0 20(20) 34 10.1.1.2 > 10.4.3.2 +< zx0 ip #0 20(20) 34 10.4.3.2 > 10.3.4.4 +> zx0 ip #0 20(20) 34 10.1.1.2 > 10.4.3.4 +< zx0 ip #0 20(20) 34 10.4.3.4 > 10.3.4.5 +> zx0 ip #0 20(20) 34 10.1.1.3 > 10.4.3.4 +< zx0 ip #0 20(20) 34 10.4.3.4 > 10.3.4.6 +> zx0 ip #0 20(20) 35 10.1.1.3 > 10.4.3.4 +< zx0 ip #0 20(20) 35 10.4.3.4 > 10.3.4.7 +> zx0 ip #0 40(20) 6 10.2.2.2,1025 > 10.1.1.1,1025 +> zx0 ip #0 40(20) 6 10.2.2.2,1025 > 10.1.1.2,1025 +> zx0 ip #0 20(20) 0 10.1.1.0 > 10.1.1.2 +> zx0 ip #0 20(20) 0 10.2.2.2 > 10.1.2.1 +> zx0 ip #0 40(20) 6 10.1.1.2,1025 > 10.1.1.1,1025 +> zx0 ip #0 40(20) 6 10.1.1.2,1025 > 10.1.1.1,1025 +> zx0 ip #0 40(20) 6 10.1.1.2,1026 > 10.1.1.1,1025 +> zx0 ip #0 28(20) 17 10.1.1.2,1025 > 10.1.1.1,1025 +> zx0 ip #0 40(20) 6 10.1.1.3,2000 > 10.1.2.1,80 +> zx0 ip #0 40(20) 6 10.1.1.3,2001 > 10.1.3.1,80 +> zx0 ip #0 40(20) 6 10.1.1.3,2002 > 10.1.4.1,80 +> zx0 ip #0 40(20) 6 10.1.1.3,2003 > 10.1.4.1,80 +< zx0 ip #0 20(20) 0 10.1.1.1 > 10.1.1.2 +< zx0 ip #0 40(20) 6 10.1.1.1,1025 > 10.1.1.2,1025 +< zx0 ip #0 20(20) 0 10.1.1.2 > 10.1.1.1 +> zx0 ip #0 40(20) 6 10.2.2.2,1026 > 10.3.4.5,40000 +< zx0 ip #0 40(20) 6 10.1.1.1,1026 > 10.3.4.5,40000 +> zx0 ip #0 40(20) 6 10.2.2.2,1025 > 10.3.4.5,40000 +< zx0 ip #0 40(20) 6 10.1.1.1,1025 > 10.3.4.5,40000 +> zx0 ip #0 28(20) 17 10.1.1.2,1025 > 10.3.4.5,40001 +< zx0 ip #0 28(20) 17 10.1.1.2,1025 > 10.3.4.5,40001 +> zx0 ip #0 40(20) 6 10.1.2.1,80 > 10.3.4.5,40001 +< zx0 ip #0 40(20) 6 10.1.2.1,80 > 10.3.4.5,40001 +List of active MAP/Redirect filters: +map zx0 10.1.1.1/32 -> 10.2.2.2/32 + +List of active sessions: +MAP 10.1.1.1 1025 <- -> 10.2.2.2 1025 [10.3.4.5 40000] +MAP 10.1.1.1 1026 <- -> 10.2.2.2 1026 [10.3.4.5 40000] +MAP 10.1.1.1 <- -> 10.2.2.2 [10.1.2.1] +MAP 10.1.1.1 1025 <- -> 10.2.2.2 1025 [10.1.1.2 1025] +MAP 10.1.1.1 1025 <- -> 10.2.2.2 1025 [10.1.1.1 1025] +MAP 10.1.1.1 <- -> 10.2.2.2 [10.4.3.2] +MAP 10.1.1.1 <- -> 10.2.2.2 [10.1.1.2] + +Hostmap table: +10.1.1.1,10.3.4.5 -> 10.2.2.2,0.0.0.0 (use = 2) +10.1.1.1,10.1.2.1 -> 10.2.2.2,0.0.0.0 (use = 1) +10.1.1.1,10.1.1.1 -> 10.2.2.2,0.0.0.0 (use = 1) +10.1.1.1,10.4.3.2 -> 10.2.2.2,0.0.0.0 (use = 1) +10.1.1.1,10.1.1.2 -> 10.2.2.2,0.0.0.0 (use = 2) +List of active state sessions: +List of configured pools +List of configured hash tables +List of groups configured (set 0) +List of groups configured (set 1) +Rules configured (set 0, in) +Rules configured (set 0, out) +Rules configured (set 1, in) +Rules configured (set 1, out) +Accounting rules configured (set 0, in) +Accounting rules configured (set 0, out) +Accounting rules configured (set 1, in) +Accounting rules configured (set 1, out) ------------------------------- -ip #0 20(20) 255 10.3.4.5 > 10.1.1.2 -ip #0 20(20) 255 10.1.1.1 > 10.1.1.2 -ip #0 20(20) 255 10.3.4.5 > 10.1.1.1 -ip #0 40(20) 6 10.3.4.5,1025 > 10.1.1.1,1025 -ip #0 40(20) 6 10.3.4.5,1026 > 10.1.1.1,1025 -ip #0 20(20) 255 10.2.2.1 > 10.1.2.1 -ip #0 20(20) 255 10.2.2.2 > 10.1.2.1 -ip #0 20(20) 255 10.1.1.1 > 10.1.1.2 -ip #0 20(20) 255 10.1.1.2 > 10.1.1.1 -ip #0 20(20) 255 10.2.2.1 > 10.2.1.1 -ip #0 20(20) 255 10.2.2.2 > 10.2.1.1 -ip #0 20(20) 255 10.2.2.3 > 10.1.1.1 -ip #0 20(20) 255 10.2.3.4 > 10.2.2.2 -ip #0 20(20) 255 10.1.1.1 > 10.2.2.2 -ip #0 20(20) 255 10.1.1.2 > 10.2.2.2 -ip #0 20(20) 255 10.1.1.0 > 10.3.4.5 -ip #0 20(20) 255 10.1.1.1 > 10.1.1.2 -ip #0 20(20) 255 10.1.1.2 > 10.1.1.0 -ip #0 40(20) 6 10.1.1.1,1025 > 10.1.1.2,1025 -ip #0 48(20) 1 10.1.1.1 > 10.4.3.2 -ip #0 48(20) 1 10.4.3.2 > 10.2.2.2 -ip #0 48(20) 1 10.4.3.2 > 10.3.4.3 -ip #0 48(20) 1 10.4.3.2 > 10.3.4.5 -ip #0 20(20) 34 10.1.1.2 > 10.4.3.2 -ip #0 20(20) 34 10.4.3.2 > 10.3.4.4 -ip #0 20(20) 34 10.1.1.2 > 10.4.3.4 -ip #0 20(20) 34 10.4.3.4 > 10.3.4.5 -ip #0 20(20) 34 10.1.1.3 > 10.4.3.4 -ip #0 20(20) 34 10.4.3.4 > 10.3.4.6 -ip #0 20(20) 35 10.1.1.3 > 10.4.3.4 -ip #0 20(20) 35 10.4.3.4 > 10.3.4.7 -ip #0 40(20) 6 10.1.1.1,1025 > 10.1.1.1,1025 -ip #0 40(20) 6 10.3.4.5,1025 > 10.1.1.2,1025 -ip #0 20(20) 0 10.3.4.5 > 10.1.1.2 -ip #0 20(20) 0 10.3.4.5 > 10.1.2.1 -ip #0 40(20) 6 10.3.4.5,1025 > 10.1.1.1,1025 -ip #0 40(20) 6 10.3.4.5,1025 > 10.1.1.1,1025 -ip #0 40(20) 6 10.3.4.5,1026 > 10.1.1.1,1025 -ip #0 28(20) 17 10.3.4.5,1025 > 10.1.1.1,1025 -ip #0 40(20) 6 10.3.4.5,2000 > 10.1.2.1,80 -ip #0 40(20) 6 10.3.4.5,2001 > 10.1.3.1,80 -ip #0 40(20) 6 10.3.4.5,2002 > 10.1.4.1,80 -ip #0 40(20) 6 10.3.4.5,2003 > 10.1.4.1,80 -ip #0 20(20) 0 10.1.1.1 > 10.1.1.2 -ip #0 40(20) 6 10.1.1.1,1025 > 10.1.1.2,1025 -ip #0 20(20) 0 10.1.1.2 > 10.1.1.1 -ip #0 40(20) 6 10.1.1.1,1026 > 10.3.4.5,40000 -ip #0 40(20) 6 10.1.1.1,1026 > 10.3.4.5,40000 -ip #0 40(20) 6 10.1.1.1,1025 > 10.3.4.5,40000 -ip #0 40(20) 6 10.1.1.1,1025 > 10.3.4.5,40000 -ip #0 28(20) 17 10.1.1.2,1025 > 10.3.4.5,40001 -ip #0 28(20) 17 10.1.1.2,1025 > 10.3.4.5,40001 -ip #0 40(20) 6 10.1.2.1,80 > 10.3.4.5,40001 -ip #0 40(20) 6 10.1.2.1,80 > 10.3.4.5,40001 +> zx0 ip #0 20(20) 255 10.3.4.5 > 10.1.1.2 +15 +> zx0 ip #0 20(20) 255 10.3.4.5 > 10.1.1.1 +> zx0 ip #0 40(20) 6 10.3.4.5,1025 > 10.1.1.1,1025 +> zx0 ip #0 40(20) 6 10.3.4.5,1026 > 10.1.1.1,1025 +> zx0 ip #0 20(20) 255 10.2.2.1 > 10.1.2.1 +> zx0 ip #0 20(20) 255 10.2.2.2 > 10.1.2.1 +< zx0 ip #0 20(20) 255 10.1.1.1 > 10.1.1.2 +< zx0 ip #0 20(20) 255 10.1.1.2 > 10.1.1.1 +< zx0 ip #0 20(20) 255 10.2.2.1 > 10.2.1.1 +< zx0 ip #0 20(20) 255 10.2.2.2 > 10.2.1.1 +< zx0 ip #0 20(20) 255 10.2.2.3 > 10.1.1.1 +< zx0 ip #0 20(20) 255 10.2.3.4 > 10.2.2.2 +< zx0 ip #0 20(20) 255 10.1.1.1 > 10.2.2.2 +< zx0 ip #0 20(20) 255 10.1.1.2 > 10.2.2.2 +< zx0 ip #0 20(20) 255 10.1.1.0 > 10.3.4.5 +< zx0 ip #0 20(20) 255 10.1.1.1 > 10.1.1.2 +< zx0 ip #0 20(20) 255 10.1.1.2 > 10.1.1.0 +< zx0 ip #0 40(20) 6 10.1.1.1,1025 > 10.1.1.2,1025 +> zx0 ip #0 48(20) 1 10.1.1.1 > 10.4.3.2 +< zx0 ip #0 48(20) 1 10.4.3.2 > 10.2.2.2 +< zx0 ip #0 48(20) 1 10.4.3.2 > 10.3.4.3 +< zx0 ip #0 48(20) 1 10.4.3.2 > 10.3.4.5 +> zx0 ip #0 20(20) 34 10.1.1.2 > 10.4.3.2 +< zx0 ip #0 20(20) 34 10.4.3.2 > 10.3.4.4 +> zx0 ip #0 20(20) 34 10.1.1.2 > 10.4.3.4 +< zx0 ip #0 20(20) 34 10.4.3.4 > 10.3.4.5 +> zx0 ip #0 20(20) 34 10.1.1.3 > 10.4.3.4 +< zx0 ip #0 20(20) 34 10.4.3.4 > 10.3.4.6 +> zx0 ip #0 20(20) 35 10.1.1.3 > 10.4.3.4 +< zx0 ip #0 20(20) 35 10.4.3.4 > 10.3.4.7 +15 +> zx0 ip #0 40(20) 6 10.3.4.5,1025 > 10.1.1.2,1025 +> zx0 ip #0 20(20) 0 10.3.4.5 > 10.1.1.2 +> zx0 ip #0 20(20) 0 10.3.4.5 > 10.1.2.1 +> zx0 ip #0 40(20) 6 10.3.4.5,1025 > 10.1.1.1,1025 +> zx0 ip #0 40(20) 6 10.3.4.5,1025 > 10.1.1.1,1025 +> zx0 ip #0 40(20) 6 10.3.4.5,1026 > 10.1.1.1,1025 +> zx0 ip #0 28(20) 17 10.3.4.5,1025 > 10.1.1.1,1025 +> zx0 ip #0 40(20) 6 10.3.4.5,2000 > 10.1.2.1,80 +> zx0 ip #0 40(20) 6 10.3.4.5,2001 > 10.1.3.1,80 +> zx0 ip #0 40(20) 6 10.3.4.5,2002 > 10.1.4.1,80 +> zx0 ip #0 40(20) 6 10.3.4.5,2003 > 10.1.4.1,80 +< zx0 ip #0 20(20) 0 10.1.1.1 > 10.1.1.2 +< zx0 ip #0 40(20) 6 10.1.1.1,1025 > 10.1.1.2,1025 +< zx0 ip #0 20(20) 0 10.1.1.2 > 10.1.1.1 +> zx0 ip #0 40(20) 6 10.1.1.1,1026 > 10.3.4.5,40000 +< zx0 ip #0 40(20) 6 10.1.1.1,1026 > 10.3.4.5,40000 +> zx0 ip #0 40(20) 6 10.1.1.1,1025 > 10.3.4.5,40000 +< zx0 ip #0 40(20) 6 10.1.1.1,1025 > 10.3.4.5,40000 +> zx0 ip #0 28(20) 17 10.1.1.2,1025 > 10.3.4.5,40001 +< zx0 ip #0 28(20) 17 10.1.1.2,1025 > 10.3.4.5,40001 +> zx0 ip #0 40(20) 6 10.1.2.1,80 > 10.3.4.5,40001 +< zx0 ip #0 40(20) 6 10.1.2.1,80 > 10.3.4.5,40001 +List of active MAP/Redirect filters: +map zx0 from 10.1.1.0/24 to 10.1.0.0/16 -> 10.3.4.5/32 + +List of active sessions: +MAP 10.1.1.3 2003 <- -> 10.3.4.5 2003 [10.1.4.1 80] +MAP 10.1.1.3 2002 <- -> 10.3.4.5 2002 [10.1.4.1 80] +MAP 10.1.1.3 2001 <- -> 10.3.4.5 2001 [10.1.3.1 80] +MAP 10.1.1.3 2000 <- -> 10.3.4.5 2000 [10.1.2.1 80] +MAP 10.1.1.2 1025 <- -> 10.3.4.5 1025 [10.1.1.1 1025] +MAP 10.1.1.1 <- -> 10.3.4.5 [10.1.2.1] +MAP 10.1.1.0 <- -> 10.3.4.5 [10.1.1.2] +MAP 10.1.1.1 1025 <- -> 10.3.4.5 1025 [10.1.1.2 1025] +MAP 10.1.1.2 1026 <- -> 10.3.4.5 1026 [10.1.1.1 1025] +MAP 10.1.1.2 1025 <- -> 10.3.4.5 1025 [10.1.1.1 1025] +MAP 10.1.1.2 <- -> 10.3.4.5 [10.1.1.1] +MAP 10.1.1.0 <- -> 10.3.4.5 [10.1.1.2] + +Hostmap table: +10.1.1.3,10.1.4.1 -> 10.3.4.5,0.0.0.0 (use = 2) +10.1.1.3,10.1.3.1 -> 10.3.4.5,0.0.0.0 (use = 1) +10.1.1.3,10.1.2.1 -> 10.3.4.5,0.0.0.0 (use = 1) +10.1.1.1,10.1.2.1 -> 10.3.4.5,0.0.0.0 (use = 1) +10.1.1.1,10.1.1.2 -> 10.3.4.5,0.0.0.0 (use = 1) +10.1.1.2,10.1.1.1 -> 10.3.4.5,0.0.0.0 (use = 4) +10.1.1.0,10.1.1.2 -> 10.3.4.5,0.0.0.0 (use = 2) +List of active state sessions: +List of configured pools +List of configured hash tables +List of groups configured (set 0) +List of groups configured (set 1) +Rules configured (set 0, in) +Rules configured (set 0, out) +Rules configured (set 1, in) +Rules configured (set 1, out) +Accounting rules configured (set 0, in) +Accounting rules configured (set 0, out) +Accounting rules configured (set 1, in) +Accounting rules configured (set 1, out) ------------------------------- -ip #0 20(20) 255 10.1.1.0 > 10.1.1.2 -ip #0 20(20) 255 10.1.1.1 > 10.1.1.2 -ip #0 20(20) 255 10.1.1.2 > 10.1.1.1 -ip #0 40(20) 6 10.1.1.2,1025 > 10.1.1.1,1025 -ip #0 40(20) 6 10.1.1.2,1026 > 10.1.1.1,1025 -ip #0 20(20) 255 10.2.2.1 > 10.1.2.1 -ip #0 20(20) 255 10.2.2.2 > 10.1.2.1 -ip #0 20(20) 255 10.1.1.1 > 10.1.1.2 -ip #0 20(20) 255 10.1.1.2 > 10.1.1.1 -ip #0 20(20) 255 10.2.2.1 > 10.2.1.1 -ip #0 20(20) 255 10.2.2.2 > 10.2.1.1 -ip #0 20(20) 255 10.2.2.3 > 10.1.1.1 -ip #0 20(20) 255 10.2.3.4 > 10.2.2.2 -ip #0 20(20) 255 10.1.1.1 > 10.2.2.2 -ip #0 20(20) 255 10.1.1.2 > 10.2.2.2 -ip #0 20(20) 255 10.1.1.0 > 10.3.4.5 -ip #0 20(20) 255 10.1.1.1 > 10.3.4.5 -ip #0 20(20) 255 10.1.1.2 > 10.3.4.5 -ip #0 40(20) 6 10.1.1.1,1025 > 10.3.4.5,1025 -ip #0 48(20) 1 10.3.4.1 > 10.4.3.2 -ip #0 48(20) 1 10.4.3.2 > 10.2.2.2 -ip #0 48(20) 1 10.4.3.2 > 10.3.4.3 -ip #0 48(20) 1 10.4.3.2 > 10.3.4.5 -ip #0 20(20) 34 10.3.4.1 > 10.4.3.2 -ip #0 20(20) 34 10.4.3.2 > 10.3.4.4 -ip #0 20(20) 34 10.3.4.1 > 10.4.3.4 -ip #0 20(20) 34 10.4.3.4 > 10.3.4.5 -ip #0 20(20) 34 10.3.4.2 > 10.4.3.4 -ip #0 20(20) 34 10.4.3.4 > 10.3.4.6 -ip #0 20(20) 35 10.3.4.2 > 10.4.3.4 -ip #0 20(20) 35 10.4.3.4 > 10.3.4.7 -ip #0 40(20) 6 10.1.1.1,1025 > 10.1.1.1,1025 -ip #0 40(20) 6 10.1.1.1,1025 > 10.1.1.2,1025 -ip #0 20(20) 0 10.1.1.0 > 10.1.1.2 -ip #0 20(20) 0 10.1.1.1 > 10.1.2.1 -ip #0 40(20) 6 10.1.1.2,1025 > 10.1.1.1,1025 -ip #0 40(20) 6 10.1.1.2,1025 > 10.1.1.1,1025 -ip #0 40(20) 6 10.1.1.2,1026 > 10.1.1.1,1025 -ip #0 28(20) 17 10.1.1.2,1025 > 10.1.1.1,1025 -ip #0 40(20) 6 10.1.1.3,2000 > 10.1.2.1,80 -ip #0 40(20) 6 10.1.1.3,2001 > 10.1.3.1,80 -ip #0 40(20) 6 10.1.1.3,2002 > 10.1.4.1,80 -ip #0 40(20) 6 10.1.1.3,2003 > 10.1.4.1,80 -ip #0 20(20) 0 10.1.1.1 > 10.1.1.2 -ip #0 40(20) 6 10.1.1.1,1025 > 10.1.1.2,1025 -ip #0 20(20) 0 10.1.1.2 > 10.1.1.1 -ip #0 40(20) 6 10.3.4.3,1026 > 10.3.4.5,40000 -ip #0 40(20) 6 10.1.1.1,1026 > 10.3.4.5,40000 -ip #0 40(20) 6 10.3.4.3,1025 > 10.3.4.5,40000 -ip #0 40(20) 6 10.1.1.1,1025 > 10.3.4.5,40000 -ip #0 28(20) 17 10.3.4.3,1025 > 10.3.4.5,40001 -ip #0 28(20) 17 10.1.1.2,1025 > 10.3.4.5,40001 -ip #0 40(20) 6 10.1.2.1,80 > 10.3.4.5,40001 -ip #0 40(20) 6 10.1.2.1,80 > 10.3.4.5,40001 +> zx0 ip #0 20(20) 255 10.1.1.0 > 10.1.1.2 +> zx0 ip #0 20(20) 255 10.1.1.1 > 10.1.1.2 +> zx0 ip #0 20(20) 255 10.1.1.2 > 10.1.1.1 +> zx0 ip #0 40(20) 6 10.1.1.2,1025 > 10.1.1.1,1025 +> zx0 ip #0 40(20) 6 10.1.1.2,1026 > 10.1.1.1,1025 +> zx0 ip #0 20(20) 255 10.2.2.1 > 10.1.2.1 +> zx0 ip #0 20(20) 255 10.2.2.2 > 10.1.2.1 +< zx0 ip #0 20(20) 255 10.1.1.1 > 10.1.1.2 +< zx0 ip #0 20(20) 255 10.1.1.2 > 10.1.1.1 +< zx0 ip #0 20(20) 255 10.2.2.1 > 10.2.1.1 +< zx0 ip #0 20(20) 255 10.2.2.2 > 10.2.1.1 +< zx0 ip #0 20(20) 255 10.2.2.3 > 10.1.1.1 +< zx0 ip #0 20(20) 255 10.2.3.4 > 10.2.2.2 +< zx0 ip #0 20(20) 255 10.1.1.1 > 10.2.2.2 +< zx0 ip #0 20(20) 255 10.1.1.2 > 10.2.2.2 +< zx0 ip #0 20(20) 255 10.1.1.0 > 10.3.4.5 +< zx0 ip #0 20(20) 255 10.1.1.1 > 10.3.4.5 +< zx0 ip #0 20(20) 255 10.1.1.2 > 10.3.4.5 +< zx0 ip #0 40(20) 6 10.1.1.1,1025 > 10.3.4.5,1025 +> zx0 ip #0 48(20) 1 10.3.4.1 > 10.4.3.2 +< zx0 ip #0 48(20) 1 10.4.3.2 > 10.2.2.2 +< zx0 ip #0 48(20) 1 10.4.3.2 > 10.3.4.3 +< zx0 ip #0 48(20) 1 10.4.3.2 > 10.3.4.5 +> zx0 ip #0 20(20) 34 10.3.4.1 > 10.4.3.2 +< zx0 ip #0 20(20) 34 10.4.3.2 > 10.3.4.4 +> zx0 ip #0 20(20) 34 10.3.4.1 > 10.4.3.4 +< zx0 ip #0 20(20) 34 10.4.3.4 > 10.3.4.5 +> zx0 ip #0 20(20) 34 10.3.4.2 > 10.4.3.4 +< zx0 ip #0 20(20) 34 10.4.3.4 > 10.3.4.6 +> zx0 ip #0 20(20) 35 10.3.4.2 > 10.4.3.4 +< zx0 ip #0 20(20) 35 10.4.3.4 > 10.3.4.7 +> zx0 ip #0 40(20) 6 10.1.1.1,1025 > 10.1.1.1,1025 +> zx0 ip #0 40(20) 6 10.1.1.1,1025 > 10.1.1.2,1025 +> zx0 ip #0 20(20) 0 10.1.1.0 > 10.1.1.2 +> zx0 ip #0 20(20) 0 10.1.1.1 > 10.1.2.1 +> zx0 ip #0 40(20) 6 10.1.1.2,1025 > 10.1.1.1,1025 +> zx0 ip #0 40(20) 6 10.1.1.2,1025 > 10.1.1.1,1025 +> zx0 ip #0 40(20) 6 10.1.1.2,1026 > 10.1.1.1,1025 +> zx0 ip #0 28(20) 17 10.1.1.2,1025 > 10.1.1.1,1025 +> zx0 ip #0 40(20) 6 10.1.1.3,2000 > 10.1.2.1,80 +> zx0 ip #0 40(20) 6 10.1.1.3,2001 > 10.1.3.1,80 +> zx0 ip #0 40(20) 6 10.1.1.3,2002 > 10.1.4.1,80 +> zx0 ip #0 40(20) 6 10.1.1.3,2003 > 10.1.4.1,80 +< zx0 ip #0 20(20) 0 10.1.1.1 > 10.1.1.2 +< zx0 ip #0 40(20) 6 10.1.1.1,1025 > 10.1.1.2,1025 +< zx0 ip #0 20(20) 0 10.1.1.2 > 10.1.1.1 +> zx0 ip #0 40(20) 6 10.3.4.3,1026 > 10.3.4.5,40000 +< zx0 ip #0 40(20) 6 10.1.1.1,1026 > 10.3.4.5,40000 +> zx0 ip #0 40(20) 6 10.3.4.3,1025 > 10.3.4.5,40000 +< zx0 ip #0 40(20) 6 10.1.1.1,1025 > 10.3.4.5,40000 +> zx0 ip #0 28(20) 17 10.3.4.3,1025 > 10.3.4.5,40001 +< zx0 ip #0 28(20) 17 10.1.1.2,1025 > 10.3.4.5,40001 +> zx0 ip #0 40(20) 6 10.1.2.1,80 > 10.3.4.5,40001 +< zx0 ip #0 40(20) 6 10.1.2.1,80 > 10.3.4.5,40001 +List of active MAP/Redirect filters: +map zx0 from 10.1.1.0/24 ! to 10.1.0.0/16 -> 10.3.4.0/24 + +List of active sessions: +MAP 10.1.1.2 1025 <- -> 10.3.4.3 1025 [10.3.4.5 40001] +MAP 10.1.1.1 1025 <- -> 10.3.4.3 1025 [10.3.4.5 40000] +MAP 10.1.1.1 1026 <- -> 10.3.4.3 1026 [10.3.4.5 40000] +MAP 10.1.1.3 <- -> 10.3.4.2 [10.4.3.4] +MAP 10.1.1.3 <- -> 10.3.4.2 [10.4.3.4] +MAP 10.1.1.2 <- -> 10.3.4.1 [10.4.3.4] +MAP 10.1.1.2 <- -> 10.3.4.1 [10.4.3.2] +MAP 10.1.1.1 <- -> 10.3.4.1 [10.4.3.2] + +Hostmap table: +10.1.1.2,10.3.4.5 -> 10.3.4.3,0.0.0.0 (use = 1) +10.1.1.1,10.3.4.5 -> 10.3.4.3,0.0.0.0 (use = 2) +10.1.1.3,10.4.3.4 -> 10.3.4.2,0.0.0.0 (use = 2) +10.1.1.2,10.4.3.4 -> 10.3.4.1,0.0.0.0 (use = 1) +10.1.1.2,10.4.3.2 -> 10.3.4.1,0.0.0.0 (use = 1) +10.1.1.1,10.4.3.2 -> 10.3.4.1,0.0.0.0 (use = 1) +List of active state sessions: +List of configured pools +List of configured hash tables +List of groups configured (set 0) +List of groups configured (set 1) +Rules configured (set 0, in) +Rules configured (set 0, out) +Rules configured (set 1, in) +Rules configured (set 1, out) +Accounting rules configured (set 0, in) +Accounting rules configured (set 0, out) +Accounting rules configured (set 1, in) +Accounting rules configured (set 1, out) ------------------------------- -ip #0 20(20) 255 10.1.1.0 > 10.1.1.2 -ip #0 20(20) 255 10.1.1.1 > 10.1.1.2 -ip #0 20(20) 255 10.1.1.2 > 10.1.1.1 -ip #0 40(20) 6 10.1.1.2,1025 > 10.1.1.1,1025 -ip #0 40(20) 6 10.1.1.2,1026 > 10.1.1.1,1025 -ip #0 20(20) 255 10.2.2.1 > 10.1.2.1 -ip #0 20(20) 255 10.2.2.2 > 10.1.2.1 -ip #0 20(20) 255 10.1.1.1 > 10.1.1.2 -ip #0 20(20) 255 10.1.1.2 > 10.1.1.1 -ip #0 20(20) 255 10.2.2.1 > 10.2.1.1 -ip #0 20(20) 255 10.2.2.2 > 10.2.1.1 -ip #0 20(20) 255 10.2.2.3 > 10.1.1.1 -ip #0 20(20) 255 10.2.3.4 > 10.2.2.2 -ip #0 20(20) 255 10.1.1.1 > 10.2.2.2 -ip #0 20(20) 255 10.1.1.2 > 10.2.2.2 -ip #0 20(20) 255 10.1.1.0 > 10.3.4.5 -ip #0 20(20) 255 10.1.1.1 > 10.3.4.5 -ip #0 20(20) 255 10.1.1.2 > 10.3.4.5 -ip #0 40(20) 6 10.1.1.1,1025 > 10.3.4.5,1025 -ip #0 48(20) 1 10.1.1.1 > 10.4.3.2 -ip #0 48(20) 1 10.4.3.2 > 10.2.2.2 -ip #0 48(20) 1 10.4.3.2 > 10.3.4.3 -ip #0 48(20) 1 10.4.3.2 > 10.3.4.5 -ip #0 20(20) 34 10.1.1.2 > 10.4.3.2 -ip #0 20(20) 34 10.4.3.2 > 10.3.4.4 -ip #0 20(20) 34 10.1.1.2 > 10.4.3.4 -ip #0 20(20) 34 10.4.3.4 > 10.3.4.5 -ip #0 20(20) 34 10.1.1.3 > 10.4.3.4 -ip #0 20(20) 34 10.4.3.4 > 10.3.4.6 -ip #0 20(20) 35 10.1.1.3 > 10.4.3.4 -ip #0 20(20) 35 10.4.3.4 > 10.3.4.7 -ip #0 40(20) 6 10.1.1.1,1025 > 10.1.1.1,1025 -ip #0 40(20) 6 10.1.1.1,1025 > 10.1.1.2,1025 -ip #0 20(20) 0 10.1.1.0 > 10.1.1.2 -ip #0 20(20) 0 10.1.1.1 > 10.1.2.1 -ip #0 40(20) 6 10.1.1.2,1025 > 10.1.1.1,1025 -ip #0 40(20) 6 10.1.1.2,1025 > 10.1.1.1,1025 -ip #0 40(20) 6 10.1.1.2,1026 > 10.1.1.1,1025 -ip #0 28(20) 17 10.3.4.5,10000 > 10.1.1.1,1025 -ip #0 40(20) 6 10.1.1.3,2000 > 10.1.2.1,80 -ip #0 40(20) 6 10.1.1.3,2001 > 10.1.3.1,80 -ip #0 40(20) 6 10.1.1.3,2002 > 10.1.4.1,80 -ip #0 40(20) 6 10.1.1.3,2003 > 10.1.4.1,80 -ip #0 20(20) 0 10.1.1.1 > 10.1.1.2 -ip #0 40(20) 6 10.1.1.1,1025 > 10.1.1.2,1025 -ip #0 20(20) 0 10.1.1.2 > 10.1.1.1 -ip #0 40(20) 6 10.1.1.1,1026 > 10.3.4.5,40000 -ip #0 40(20) 6 10.1.1.1,1026 > 10.3.4.5,40000 -ip #0 40(20) 6 10.1.1.1,1025 > 10.3.4.5,40000 -ip #0 40(20) 6 10.1.1.1,1025 > 10.3.4.5,40000 -ip #0 28(20) 17 10.3.4.5,10001 > 10.3.4.5,40001 -ip #0 28(20) 17 10.1.1.2,1025 > 10.3.4.5,40001 -ip #0 40(20) 6 10.1.2.1,80 > 10.3.4.5,40001 -ip #0 40(20) 6 10.1.2.1,80 > 10.3.4.5,40001 +> zx0 ip #0 20(20) 255 10.1.1.0 > 10.1.1.2 +> zx0 ip #0 20(20) 255 10.1.1.1 > 10.1.1.2 +> zx0 ip #0 20(20) 255 10.1.1.2 > 10.1.1.1 +> zx0 ip #0 40(20) 6 10.1.1.2,1025 > 10.1.1.1,1025 +> zx0 ip #0 40(20) 6 10.1.1.2,1026 > 10.1.1.1,1025 +> zx0 ip #0 20(20) 255 10.2.2.1 > 10.1.2.1 +> zx0 ip #0 20(20) 255 10.2.2.2 > 10.1.2.1 +< zx0 ip #0 20(20) 255 10.1.1.1 > 10.1.1.2 +< zx0 ip #0 20(20) 255 10.1.1.2 > 10.1.1.1 +< zx0 ip #0 20(20) 255 10.2.2.1 > 10.2.1.1 +< zx0 ip #0 20(20) 255 10.2.2.2 > 10.2.1.1 +< zx0 ip #0 20(20) 255 10.2.2.3 > 10.1.1.1 +< zx0 ip #0 20(20) 255 10.2.3.4 > 10.2.2.2 +< zx0 ip #0 20(20) 255 10.1.1.1 > 10.2.2.2 +< zx0 ip #0 20(20) 255 10.1.1.2 > 10.2.2.2 +< zx0 ip #0 20(20) 255 10.1.1.0 > 10.3.4.5 +< zx0 ip #0 20(20) 255 10.1.1.1 > 10.3.4.5 +< zx0 ip #0 20(20) 255 10.1.1.2 > 10.3.4.5 +< zx0 ip #0 40(20) 6 10.1.1.1,1025 > 10.3.4.5,1025 +> zx0 ip #0 48(20) 1 10.1.1.1 > 10.4.3.2 +< zx0 ip #0 48(20) 1 10.4.3.2 > 10.2.2.2 +< zx0 ip #0 48(20) 1 10.4.3.2 > 10.3.4.3 +< zx0 ip #0 48(20) 1 10.4.3.2 > 10.3.4.5 +> zx0 ip #0 20(20) 34 10.1.1.2 > 10.4.3.2 +< zx0 ip #0 20(20) 34 10.4.3.2 > 10.3.4.4 +> zx0 ip #0 20(20) 34 10.1.1.2 > 10.4.3.4 +< zx0 ip #0 20(20) 34 10.4.3.4 > 10.3.4.5 +> zx0 ip #0 20(20) 34 10.1.1.3 > 10.4.3.4 +< zx0 ip #0 20(20) 34 10.4.3.4 > 10.3.4.6 +> zx0 ip #0 20(20) 35 10.1.1.3 > 10.4.3.4 +< zx0 ip #0 20(20) 35 10.4.3.4 > 10.3.4.7 +> zx0 ip #0 40(20) 6 10.1.1.1,1025 > 10.1.1.1,1025 +> zx0 ip #0 40(20) 6 10.1.1.1,1025 > 10.1.1.2,1025 +> zx0 ip #0 20(20) 0 10.1.1.0 > 10.1.1.2 +> zx0 ip #0 20(20) 0 10.1.1.1 > 10.1.2.1 +> zx0 ip #0 40(20) 6 10.1.1.2,1025 > 10.1.1.1,1025 +> zx0 ip #0 40(20) 6 10.1.1.2,1025 > 10.1.1.1,1025 +> zx0 ip #0 40(20) 6 10.1.1.2,1026 > 10.1.1.1,1025 +> zx0 ip #0 28(20) 17 10.3.4.5,10000 > 10.1.1.1,1025 +> zx0 ip #0 40(20) 6 10.1.1.3,2000 > 10.1.2.1,80 +> zx0 ip #0 40(20) 6 10.1.1.3,2001 > 10.1.3.1,80 +> zx0 ip #0 40(20) 6 10.1.1.3,2002 > 10.1.4.1,80 +> zx0 ip #0 40(20) 6 10.1.1.3,2003 > 10.1.4.1,80 +< zx0 ip #0 20(20) 0 10.1.1.1 > 10.1.1.2 +< zx0 ip #0 40(20) 6 10.1.1.1,1025 > 10.1.1.2,1025 +< zx0 ip #0 20(20) 0 10.1.1.2 > 10.1.1.1 +> zx0 ip #0 40(20) 6 10.1.1.1,1026 > 10.3.4.5,40000 +< zx0 ip #0 40(20) 6 10.1.1.1,1026 > 10.3.4.5,40000 +> zx0 ip #0 40(20) 6 10.1.1.1,1025 > 10.3.4.5,40000 +< zx0 ip #0 40(20) 6 10.1.1.1,1025 > 10.3.4.5,40000 +> zx0 ip #0 28(20) 17 10.3.4.5,10001 > 10.3.4.5,40001 +< zx0 ip #0 28(20) 17 10.1.1.2,1025 > 10.3.4.5,40001 +> zx0 ip #0 40(20) 6 10.1.2.1,80 > 10.3.4.5,40001 +< zx0 ip #0 40(20) 6 10.1.2.1,80 > 10.3.4.5,40001 +List of active MAP/Redirect filters: +map zx0 10.1.1.0/24 -> 10.3.4.5/32 portmap udp 10000:20000 sequential + +List of active sessions: +MAP 10.1.1.2 1025 <- -> 10.3.4.5 10001 [10.3.4.5 40001] +MAP 10.1.1.2 1025 <- -> 10.3.4.5 10000 [10.1.1.1 1025] + +Hostmap table: +10.1.1.2,10.3.4.5 -> 10.3.4.5,0.0.0.0 (use = 1) +10.1.1.2,10.1.1.1 -> 10.3.4.5,0.0.0.0 (use = 1) +List of active state sessions: +List of configured pools +List of configured hash tables +List of groups configured (set 0) +List of groups configured (set 1) +Rules configured (set 0, in) +Rules configured (set 0, out) +Rules configured (set 1, in) +Rules configured (set 1, out) +Accounting rules configured (set 0, in) +Accounting rules configured (set 0, out) +Accounting rules configured (set 1, in) +Accounting rules configured (set 1, out) ------------------------------- -ip #0 20(20) 255 10.1.1.0 > 10.1.1.2 -ip #0 20(20) 255 10.1.1.1 > 10.1.1.2 -ip #0 20(20) 255 10.1.1.2 > 10.1.1.1 -ip #0 40(20) 6 10.3.4.1,10000 > 10.1.1.1,1025 -ip #0 40(20) 6 10.3.4.1,10001 > 10.1.1.1,1025 -ip #0 20(20) 255 10.2.2.1 > 10.1.2.1 -ip #0 20(20) 255 10.2.2.2 > 10.1.2.1 -ip #0 20(20) 255 10.1.1.1 > 10.1.1.2 -ip #0 20(20) 255 10.1.1.2 > 10.1.1.1 -ip #0 20(20) 255 10.2.2.1 > 10.2.1.1 -ip #0 20(20) 255 10.2.2.2 > 10.2.1.1 -ip #0 20(20) 255 10.2.2.3 > 10.1.1.1 -ip #0 20(20) 255 10.2.3.4 > 10.2.2.2 -ip #0 20(20) 255 10.1.1.1 > 10.2.2.2 -ip #0 20(20) 255 10.1.1.2 > 10.2.2.2 -ip #0 20(20) 255 10.1.1.0 > 10.3.4.5 -ip #0 20(20) 255 10.1.1.1 > 10.3.4.5 -ip #0 20(20) 255 10.1.1.2 > 10.3.4.5 -ip #0 40(20) 6 10.1.1.1,1025 > 10.3.4.5,1025 -ip #0 48(20) 1 10.1.1.1 > 10.4.3.2 -ip #0 48(20) 1 10.4.3.2 > 10.2.2.2 -ip #0 48(20) 1 10.4.3.2 > 10.3.4.3 -ip #0 48(20) 1 10.4.3.2 > 10.3.4.5 -ip #0 20(20) 34 10.1.1.2 > 10.4.3.2 -ip #0 20(20) 34 10.4.3.2 > 10.3.4.4 -ip #0 20(20) 34 10.1.1.2 > 10.4.3.4 -ip #0 20(20) 34 10.4.3.4 > 10.3.4.5 -ip #0 20(20) 34 10.1.1.3 > 10.4.3.4 -ip #0 20(20) 34 10.4.3.4 > 10.3.4.6 -ip #0 20(20) 35 10.1.1.3 > 10.4.3.4 -ip #0 20(20) 35 10.4.3.4 > 10.3.4.7 -ip #0 40(20) 6 10.3.4.1,10002 > 10.1.1.1,1025 -ip #0 40(20) 6 10.3.4.1,10003 > 10.1.1.2,1025 -ip #0 20(20) 0 10.1.1.0 > 10.1.1.2 -ip #0 20(20) 0 10.1.1.1 > 10.1.2.1 -ip #0 40(20) 6 10.3.4.1,10000 > 10.1.1.1,1025 -ip #0 40(20) 6 10.3.4.1,10000 > 10.1.1.1,1025 -ip #0 40(20) 6 10.3.4.1,10001 > 10.1.1.1,1025 -ip #0 28(20) 17 10.3.4.1,10004 > 10.1.1.1,1025 -ip #0 40(20) 6 10.3.4.1,10005 > 10.1.2.1,80 -ip #0 40(20) 6 10.3.4.1,10006 > 10.1.3.1,80 -ip #0 40(20) 6 10.3.4.1,10007 > 10.1.4.1,80 -ip #0 40(20) 6 10.3.4.1,10008 > 10.1.4.1,80 -ip #0 20(20) 0 10.1.1.1 > 10.1.1.2 -ip #0 40(20) 6 10.1.1.1,1025 > 10.1.1.2,1025 -ip #0 20(20) 0 10.1.1.2 > 10.1.1.1 -ip #0 40(20) 6 10.3.4.1,10009 > 10.3.4.5,40000 -ip #0 40(20) 6 10.1.1.1,1026 > 10.3.4.5,40000 -ip #0 40(20) 6 10.3.4.1,10010 > 10.3.4.5,40000 -ip #0 40(20) 6 10.1.1.1,1025 > 10.3.4.5,40000 -ip #0 28(20) 17 10.3.4.1,10011 > 10.3.4.5,40001 -ip #0 28(20) 17 10.1.1.2,1025 > 10.3.4.5,40001 -ip #0 40(20) 6 10.3.4.1,10012 > 10.3.4.5,40001 -ip #0 40(20) 6 10.1.2.1,80 > 10.3.4.5,40001 +> zx0 ip #0 20(20) 255 10.1.1.0 > 10.1.1.2 +> zx0 ip #0 20(20) 255 10.1.1.1 > 10.1.1.2 +> zx0 ip #0 20(20) 255 10.1.1.2 > 10.1.1.1 +> zx0 ip #0 40(20) 6 10.3.4.1,10000 > 10.1.1.1,1025 +> zx0 ip #0 40(20) 6 10.3.4.1,10001 > 10.1.1.1,1025 +> zx0 ip #0 20(20) 255 10.2.2.1 > 10.1.2.1 +> zx0 ip #0 20(20) 255 10.2.2.2 > 10.1.2.1 +< zx0 ip #0 20(20) 255 10.1.1.1 > 10.1.1.2 +< zx0 ip #0 20(20) 255 10.1.1.2 > 10.1.1.1 +< zx0 ip #0 20(20) 255 10.2.2.1 > 10.2.1.1 +< zx0 ip #0 20(20) 255 10.2.2.2 > 10.2.1.1 +< zx0 ip #0 20(20) 255 10.2.2.3 > 10.1.1.1 +< zx0 ip #0 20(20) 255 10.2.3.4 > 10.2.2.2 +< zx0 ip #0 20(20) 255 10.1.1.1 > 10.2.2.2 +< zx0 ip #0 20(20) 255 10.1.1.2 > 10.2.2.2 +< zx0 ip #0 20(20) 255 10.1.1.0 > 10.3.4.5 +< zx0 ip #0 20(20) 255 10.1.1.1 > 10.3.4.5 +< zx0 ip #0 20(20) 255 10.1.1.2 > 10.3.4.5 +< zx0 ip #0 40(20) 6 10.1.1.1,1025 > 10.3.4.5,1025 +> zx0 ip #0 48(20) 1 10.1.1.1 > 10.4.3.2 +< zx0 ip #0 48(20) 1 10.4.3.2 > 10.2.2.2 +< zx0 ip #0 48(20) 1 10.4.3.2 > 10.3.4.3 +< zx0 ip #0 48(20) 1 10.4.3.2 > 10.3.4.5 +> zx0 ip #0 20(20) 34 10.1.1.2 > 10.4.3.2 +< zx0 ip #0 20(20) 34 10.4.3.2 > 10.3.4.4 +> zx0 ip #0 20(20) 34 10.1.1.2 > 10.4.3.4 +< zx0 ip #0 20(20) 34 10.4.3.4 > 10.3.4.5 +> zx0 ip #0 20(20) 34 10.1.1.3 > 10.4.3.4 +< zx0 ip #0 20(20) 34 10.4.3.4 > 10.3.4.6 +> zx0 ip #0 20(20) 35 10.1.1.3 > 10.4.3.4 +< zx0 ip #0 20(20) 35 10.4.3.4 > 10.3.4.7 +> zx0 ip #0 40(20) 6 10.3.4.1,10002 > 10.1.1.1,1025 +> zx0 ip #0 40(20) 6 10.3.4.1,10003 > 10.1.1.2,1025 +> zx0 ip #0 20(20) 0 10.1.1.0 > 10.1.1.2 +> zx0 ip #0 20(20) 0 10.1.1.1 > 10.1.2.1 +> zx0 ip #0 40(20) 6 10.3.4.1,10000 > 10.1.1.1,1025 +> zx0 ip #0 40(20) 6 10.3.4.1,10000 > 10.1.1.1,1025 +> zx0 ip #0 40(20) 6 10.3.4.1,10001 > 10.1.1.1,1025 +> zx0 ip #0 28(20) 17 10.3.4.1,10004 > 10.1.1.1,1025 +> zx0 ip #0 40(20) 6 10.3.4.1,10005 > 10.1.2.1,80 +> zx0 ip #0 40(20) 6 10.3.4.1,10006 > 10.1.3.1,80 +> zx0 ip #0 40(20) 6 10.3.4.1,10007 > 10.1.4.1,80 +> zx0 ip #0 40(20) 6 10.3.4.1,10008 > 10.1.4.1,80 +< zx0 ip #0 20(20) 0 10.1.1.1 > 10.1.1.2 +< zx0 ip #0 40(20) 6 10.1.1.1,1025 > 10.1.1.2,1025 +< zx0 ip #0 20(20) 0 10.1.1.2 > 10.1.1.1 +> zx0 ip #0 40(20) 6 10.3.4.1,10009 > 10.3.4.5,40000 +< zx0 ip #0 40(20) 6 10.1.1.1,1026 > 10.3.4.5,40000 +> zx0 ip #0 40(20) 6 10.3.4.1,10010 > 10.3.4.5,40000 +< zx0 ip #0 40(20) 6 10.1.1.1,1025 > 10.3.4.5,40000 +> zx0 ip #0 28(20) 17 10.3.4.1,10011 > 10.3.4.5,40001 +< zx0 ip #0 28(20) 17 10.1.1.2,1025 > 10.3.4.5,40001 +> zx0 ip #0 40(20) 6 10.3.4.1,10012 > 10.3.4.5,40001 +< zx0 ip #0 40(20) 6 10.1.2.1,80 > 10.3.4.5,40001 +List of active MAP/Redirect filters: +map zx0 10.1.0.0/16 -> 10.3.4.0/24 portmap tcp/udp 10000:20000 sequential + +List of active sessions: +MAP 10.1.2.1 80 <- -> 10.3.4.1 10012 [10.3.4.5 40001] +MAP 10.1.1.2 1025 <- -> 10.3.4.1 10011 [10.3.4.5 40001] +MAP 10.1.1.1 1025 <- -> 10.3.4.1 10010 [10.3.4.5 40000] +MAP 10.1.1.1 1026 <- -> 10.3.4.1 10009 [10.3.4.5 40000] +MAP 10.1.1.3 2003 <- -> 10.3.4.1 10008 [10.1.4.1 80] +MAP 10.1.1.3 2002 <- -> 10.3.4.1 10007 [10.1.4.1 80] +MAP 10.1.1.3 2001 <- -> 10.3.4.1 10006 [10.1.3.1 80] +MAP 10.1.1.3 2000 <- -> 10.3.4.1 10005 [10.1.2.1 80] +MAP 10.1.1.2 1025 <- -> 10.3.4.1 10004 [10.1.1.1 1025] +MAP 10.1.1.1 1025 <- -> 10.3.4.1 10003 [10.1.1.2 1025] +MAP 10.1.1.1 1025 <- -> 10.3.4.1 10002 [10.1.1.1 1025] +MAP 10.1.1.2 1026 <- -> 10.3.4.1 10001 [10.1.1.1 1025] +MAP 10.1.1.2 1025 <- -> 10.3.4.1 10000 [10.1.1.1 1025] + +Hostmap table: +10.1.2.1,10.3.4.5 -> 10.3.4.1,0.0.0.0 (use = 1) +10.1.1.2,10.3.4.5 -> 10.3.4.1,0.0.0.0 (use = 1) +10.1.1.1,10.3.4.5 -> 10.3.4.1,0.0.0.0 (use = 2) +10.1.1.3,10.1.4.1 -> 10.3.4.1,0.0.0.0 (use = 2) +10.1.1.3,10.1.3.1 -> 10.3.4.1,0.0.0.0 (use = 1) +10.1.1.3,10.1.2.1 -> 10.3.4.1,0.0.0.0 (use = 1) +10.1.1.1,10.1.1.2 -> 10.3.4.1,0.0.0.0 (use = 1) +10.1.1.1,10.1.1.1 -> 10.3.4.1,0.0.0.0 (use = 1) +10.1.1.2,10.1.1.1 -> 10.3.4.1,0.0.0.0 (use = 3) +List of active state sessions: +List of configured pools +List of configured hash tables +List of groups configured (set 0) +List of groups configured (set 1) +Rules configured (set 0, in) +Rules configured (set 0, out) +Rules configured (set 1, in) +Rules configured (set 1, out) +Accounting rules configured (set 0, in) +Accounting rules configured (set 0, out) +Accounting rules configured (set 1, in) +Accounting rules configured (set 1, out) ------------------------------- -ip #0 20(20) 255 10.1.1.0 > 10.1.1.2 -ip #0 20(20) 255 10.1.1.1 > 10.1.1.2 -ip #0 20(20) 255 10.1.1.2 > 10.1.1.1 -ip #0 40(20) 6 10.3.4.5,40000 > 10.1.1.1,1025 -ip #0 40(20) 6 10.3.4.5,40001 > 10.1.1.1,1025 -ip #0 20(20) 255 10.2.2.1 > 10.1.2.1 -ip #0 20(20) 255 10.2.2.2 > 10.1.2.1 -ip #0 20(20) 255 10.1.1.1 > 10.1.1.2 -ip #0 20(20) 255 10.1.1.2 > 10.1.1.1 -ip #0 20(20) 255 10.2.2.1 > 10.2.1.1 -ip #0 20(20) 255 10.2.2.2 > 10.2.1.1 -ip #0 20(20) 255 10.2.2.3 > 10.1.1.1 -ip #0 20(20) 255 10.2.3.4 > 10.2.2.2 -ip #0 20(20) 255 10.1.1.1 > 10.2.2.2 -ip #0 20(20) 255 10.1.1.2 > 10.2.2.2 -ip #0 20(20) 255 10.1.1.0 > 10.3.4.5 -ip #0 20(20) 255 10.1.1.1 > 10.3.4.5 -ip #0 20(20) 255 10.1.1.2 > 10.3.4.5 -ip #0 40(20) 6 10.1.1.1,1025 > 10.3.4.5,1025 -ip #0 48(20) 1 10.1.1.1 > 10.4.3.2 -ip #0 48(20) 1 10.4.3.2 > 10.2.2.2 -ip #0 48(20) 1 10.4.3.2 > 10.3.4.3 -ip #0 48(20) 1 10.4.3.2 > 10.3.4.5 -ip #0 20(20) 34 10.1.1.2 > 10.4.3.2 -ip #0 20(20) 34 10.4.3.2 > 10.3.4.4 -ip #0 20(20) 34 10.1.1.2 > 10.4.3.4 -ip #0 20(20) 34 10.4.3.4 > 10.3.4.5 -ip #0 20(20) 34 10.1.1.3 > 10.4.3.4 -ip #0 20(20) 34 10.4.3.4 > 10.3.4.6 -ip #0 20(20) 35 10.1.1.3 > 10.4.3.4 -ip #0 20(20) 35 10.4.3.4 > 10.3.4.7 -ip #0 40(20) 6 10.1.1.1,1025 > 10.1.1.1,1025 -ip #0 40(20) 6 10.3.4.5,40000 > 10.1.1.2,1025 -ip #0 20(20) 0 10.1.1.0 > 10.1.1.2 -ip #0 20(20) 0 10.1.1.1 > 10.1.2.1 -ip #0 40(20) 6 10.3.4.5,40000 > 10.1.1.1,1025 -ip #0 40(20) 6 10.3.4.5,40000 > 10.1.1.1,1025 -ip #0 40(20) 6 10.3.4.5,40001 > 10.1.1.1,1025 -ip #0 28(20) 17 10.3.4.5,40001 > 10.1.1.1,1025 -ip #0 40(20) 6 10.3.4.5,40000 > 10.1.2.1,80 -ip #0 40(20) 6 10.3.4.5,40001 > 10.1.3.1,80 -ip #0 40(20) 6 10.3.4.5,40000 > 10.1.4.1,80 -ip #0 40(20) 6 10.3.4.5,40001 > 10.1.4.1,80 -ip #0 20(20) 0 10.1.1.1 > 10.1.1.2 -ip #0 40(20) 6 10.1.1.1,1025 > 10.1.1.2,1025 -ip #0 20(20) 0 10.1.1.2 > 10.1.1.1 -ip #0 40(20) 6 10.3.4.5,40000 > 10.3.4.5,40000 -ip #0 40(20) 6 10.1.1.1,1026 > 10.3.4.5,40000 -ip #0 40(20) 6 10.3.4.5,40001 > 10.3.4.5,40000 -ip #0 40(20) 6 10.1.1.1,1025 > 10.1.1.2,1025 -ip #0 28(20) 17 10.3.4.5,40000 > 10.3.4.5,40001 -ip #0 28(20) 17 10.1.1.2,1025 > 10.3.4.5,40001 -ip #0 40(20) 6 10.1.2.1,80 > 10.3.4.5,40001 -ip #0 40(20) 6 10.1.2.1,80 > 10.3.4.5,40001 +> zx0 ip #0 20(20) 255 10.1.1.0 > 10.1.1.2 +> zx0 ip #0 20(20) 255 10.1.1.1 > 10.1.1.2 +> zx0 ip #0 20(20) 255 10.1.1.2 > 10.1.1.1 +> zx0 ip #0 40(20) 6 10.3.4.5,40000 > 10.1.1.1,1025 +> zx0 ip #0 40(20) 6 10.3.4.5,40001 > 10.1.1.1,1025 +> zx0 ip #0 20(20) 255 10.2.2.1 > 10.1.2.1 +> zx0 ip #0 20(20) 255 10.2.2.2 > 10.1.2.1 +< zx0 ip #0 20(20) 255 10.1.1.1 > 10.1.1.2 +< zx0 ip #0 20(20) 255 10.1.1.2 > 10.1.1.1 +< zx0 ip #0 20(20) 255 10.2.2.1 > 10.2.1.1 +< zx0 ip #0 20(20) 255 10.2.2.2 > 10.2.1.1 +< zx0 ip #0 20(20) 255 10.2.2.3 > 10.1.1.1 +< zx0 ip #0 20(20) 255 10.2.3.4 > 10.2.2.2 +< zx0 ip #0 20(20) 255 10.1.1.1 > 10.2.2.2 +< zx0 ip #0 20(20) 255 10.1.1.2 > 10.2.2.2 +< zx0 ip #0 20(20) 255 10.1.1.0 > 10.3.4.5 +< zx0 ip #0 20(20) 255 10.1.1.1 > 10.3.4.5 +< zx0 ip #0 20(20) 255 10.1.1.2 > 10.3.4.5 +< zx0 ip #0 40(20) 6 10.1.1.1,1025 > 10.3.4.5,1025 +> zx0 ip #0 48(20) 1 10.1.1.1 > 10.4.3.2 +< zx0 ip #0 48(20) 1 10.4.3.2 > 10.2.2.2 +< zx0 ip #0 48(20) 1 10.4.3.2 > 10.3.4.3 +< zx0 ip #0 48(20) 1 10.4.3.2 > 10.3.4.5 +> zx0 ip #0 20(20) 34 10.1.1.2 > 10.4.3.2 +< zx0 ip #0 20(20) 34 10.4.3.2 > 10.3.4.4 +> zx0 ip #0 20(20) 34 10.1.1.2 > 10.4.3.4 +< zx0 ip #0 20(20) 34 10.4.3.4 > 10.3.4.5 +> zx0 ip #0 20(20) 34 10.1.1.3 > 10.4.3.4 +< zx0 ip #0 20(20) 34 10.4.3.4 > 10.3.4.6 +> zx0 ip #0 20(20) 35 10.1.1.3 > 10.4.3.4 +< zx0 ip #0 20(20) 35 10.4.3.4 > 10.3.4.7 +15 +> zx0 ip #0 40(20) 6 10.3.4.5,40000 > 10.1.1.2,1025 +> zx0 ip #0 20(20) 0 10.1.1.0 > 10.1.1.2 +> zx0 ip #0 20(20) 0 10.1.1.1 > 10.1.2.1 +> zx0 ip #0 40(20) 6 10.3.4.5,40000 > 10.1.1.1,1025 +> zx0 ip #0 40(20) 6 10.3.4.5,40000 > 10.1.1.1,1025 +> zx0 ip #0 40(20) 6 10.3.4.5,40001 > 10.1.1.1,1025 +> zx0 ip #0 28(20) 17 10.3.4.5,40001 > 10.1.1.1,1025 +> zx0 ip #0 40(20) 6 10.3.4.5,40000 > 10.1.2.1,80 +> zx0 ip #0 40(20) 6 10.3.4.5,40001 > 10.1.3.1,80 +> zx0 ip #0 40(20) 6 10.3.4.5,40000 > 10.1.4.1,80 +> zx0 ip #0 40(20) 6 10.3.4.5,40001 > 10.1.4.1,80 +< zx0 ip #0 20(20) 0 10.1.1.1 > 10.1.1.2 +< zx0 ip #0 40(20) 6 10.1.1.1,1025 > 10.1.1.2,1025 +< zx0 ip #0 20(20) 0 10.1.1.2 > 10.1.1.1 +> zx0 ip #0 40(20) 6 10.3.4.5,40000 > 10.3.4.5,40000 +< zx0 ip #0 40(20) 6 10.1.1.1,1026 > 10.3.4.5,40000 +> zx0 ip #0 40(20) 6 10.3.4.5,40001 > 10.3.4.5,40000 +< zx0 ip #0 40(20) 6 10.1.1.1,1025 > 10.1.1.2,1025 +> zx0 ip #0 28(20) 17 10.3.4.5,40000 > 10.3.4.5,40001 +< zx0 ip #0 28(20) 17 10.1.1.2,1025 > 10.3.4.5,40001 +> zx0 ip #0 40(20) 6 10.1.2.1,80 > 10.3.4.5,40001 +< zx0 ip #0 40(20) 6 10.1.2.1,80 > 10.3.4.5,40001 +List of active MAP/Redirect filters: +map zx0 10.1.1.0/24 -> 10.3.4.5/32 portmap tcp/udp 40000:40001 sequential + +List of active sessions: +MAP 10.1.1.2 1025 <- -> 10.3.4.5 40000 [10.3.4.5 40001] +MAP 10.1.1.1 1025 <- -> 10.3.4.5 40001 [10.3.4.5 40000] +MAP 10.1.1.1 1026 <- -> 10.3.4.5 40000 [10.3.4.5 40000] +MAP 10.1.1.3 2003 <- -> 10.3.4.5 40001 [10.1.4.1 80] +MAP 10.1.1.3 2002 <- -> 10.3.4.5 40000 [10.1.4.1 80] +MAP 10.1.1.3 2001 <- -> 10.3.4.5 40001 [10.1.3.1 80] +MAP 10.1.1.3 2000 <- -> 10.3.4.5 40000 [10.1.2.1 80] +MAP 10.1.1.2 1025 <- -> 10.3.4.5 40001 [10.1.1.1 1025] +MAP 10.1.1.1 1025 <- -> 10.3.4.5 40000 [10.1.1.2 1025] +MAP 10.1.1.2 1026 <- -> 10.3.4.5 40001 [10.1.1.1 1025] +MAP 10.1.1.2 1025 <- -> 10.3.4.5 40000 [10.1.1.1 1025] + +Hostmap table: +10.1.1.2,10.3.4.5 -> 10.3.4.5,0.0.0.0 (use = 1) +10.1.1.1,10.3.4.5 -> 10.3.4.5,0.0.0.0 (use = 2) +10.1.1.3,10.1.4.1 -> 10.3.4.5,0.0.0.0 (use = 2) +10.1.1.3,10.1.3.1 -> 10.3.4.5,0.0.0.0 (use = 1) +10.1.1.3,10.1.2.1 -> 10.3.4.5,0.0.0.0 (use = 1) +10.1.1.1,10.1.1.2 -> 10.3.4.5,0.0.0.0 (use = 1) +10.1.1.2,10.1.1.1 -> 10.3.4.5,0.0.0.0 (use = 3) +List of active state sessions: +List of configured pools +List of configured hash tables +List of groups configured (set 0) +List of groups configured (set 1) +Rules configured (set 0, in) +Rules configured (set 0, out) +Rules configured (set 1, in) +Rules configured (set 1, out) +Accounting rules configured (set 0, in) +Accounting rules configured (set 0, out) +Accounting rules configured (set 1, in) +Accounting rules configured (set 1, out) ------------------------------- diff --git a/contrib/ipfilter/test/expected/n5_6 b/contrib/ipfilter/test/expected/n5_6 new file mode 100644 index 00000000000..1e7bc8eff5a --- /dev/null +++ b/contrib/ipfilter/test/expected/n5_6 @@ -0,0 +1,533 @@ +> zx0 ip6/0 1 0 255 10:1:1:0:0:0:0:0 > 10:1:1:0:0:0:0:2 +> zx0 ip6/0 1 0 255 10:0:0:0:0:2:2:2 > 10:1:1:0:0:0:0:2 +> zx0 ip6/0 1 0 255 10:1:1:0:0:0:0:2 > 10:1:1:0:0:0:0:1 +> zx0 ip6/0 20 0 6 10:1:1:0:0:0:0:2,1025 > 10:1:1:0:0:0:0:1,1025 +> zx0 ip6/0 20 0 6 10:1:1:0:0:0:0:2,1026 > 10:1:1:0:0:0:0:1,1025 +> zx0 ip6/0 1 0 255 10:0:0:0:0:2:2:1 > 10:1:2:0:0:0:0:1 +> zx0 ip6/0 1 0 255 10:0:0:0:0:2:2:2 > 10:1:2:0:0:0:0:1 +< zx0 ip6/0 1 0 255 10:1:1:0:0:0:0:1 > 10:1:1:0:0:0:0:2 +< zx0 ip6/0 1 0 255 10:1:1:0:0:0:0:2 > 10:1:1:0:0:0:0:1 +< zx0 ip6/0 1 0 255 10:0:0:0:0:2:2:1 > 10:0:0:0:0:2:1:1 +< zx0 ip6/0 1 0 255 10:0:0:0:0:2:2:2 > 10:0:0:0:0:2:1:1 +< zx0 ip6/0 1 0 255 10:0:0:0:0:2:2:3 > 10:1:1:0:0:0:0:1 +< zx0 ip6/0 1 0 255 10:0:0:0:0:2:3:4 > 10:0:0:0:0:2:2:2 +< zx0 ip6/0 1 0 255 10:1:1:0:0:0:0:1 > 10:0:0:0:0:2:2:2 +< zx0 ip6/0 1 0 255 10:1:1:0:0:0:0:2 > 10:1:1:0:0:0:0:1 +< zx0 ip6/0 1 0 255 10:1:1:0:0:0:0:0 > 10:0:0:0:0:3:4:5 +< zx0 ip6/0 1 0 255 10:1:1:0:0:0:0:1 > 10:0:0:0:0:3:4:5 +< zx0 ip6/0 1 0 255 10:1:1:0:0:0:0:2 > 10:0:0:0:0:3:4:5 +< zx0 ip6/0 20 0 6 10:1:1:0:0:0:0:1,1025 > 10:0:0:0:0:3:4:5,1025 +> zx0 ip6/0 88 0 58 10:0:0:0:0:2:2:2 > 10:4:3:0:0:0:0:2 +< zx0 ip6/0 88 0 58 10:4:3:0:0:0:0:2 > 10:1:1:0:0:0:0:1 +< zx0 ip6/0 88 0 58 10:4:3:0:0:0:0:2 > 10:0:0:0:0:3:4:3 +< zx0 ip6/0 88 0 58 10:4:3:0:0:0:0:2 > 10:0:0:0:0:3:4:5 +> zx0 ip6/0 1 0 34 10:1:1:0:0:0:0:2 > 10:4:3:0:0:0:0:2 +< zx0 ip6/0 1 0 34 10:4:3:0:0:0:0:2 > 10:0:0:0:0:3:4:4 +> zx0 ip6/0 1 0 34 10:1:1:0:0:0:0:2 > 10:4:3:0:0:0:0:4 +< zx0 ip6/0 1 0 34 10:4:3:0:0:0:0:4 > 10:0:0:0:0:3:4:5 +> zx0 ip6/0 1 0 34 10:1:1:0:0:0:0:3 > 10:4:3:0:0:0:0:4 +< zx0 ip6/0 1 0 34 10:4:3:0:0:0:0:4 > 10:0:0:0:0:3:4:6 +> zx0 ip6/0 1 0 35 10:1:1:0:0:0:0:3 > 10:4:3:0:0:0:0:4 +< zx0 ip6/0 1 0 35 10:4:3:0:0:0:0:4 > 10:0:0:0:0:3:4:7 +> zx0 ip6/0 20 0 6 10:0:0:0:0:2:2:2,1025 > 10:1:1:0:0:0:0:1,1025 +> zx0 ip6/0 20 0 6 10:0:0:0:0:2:2:2,1025 > 10:1:1:0:0:0:0:2,1025 +> zx0 ip6/0 1 0 41 10:1:1:0:0:0:0:0 > 10:1:1:0:0:0:0:2 +> zx0 ip6/0 1 0 41 10:0:0:0:0:2:2:2 > 10:1:2:0:0:0:0:1 +> zx0 ip6/0 20 0 6 10:1:1:0:0:0:0:2,1025 > 10:1:1:0:0:0:0:1,1025 +> zx0 ip6/0 20 0 6 10:1:1:0:0:0:0:2,1025 > 10:1:1:0:0:0:0:1,1025 +> zx0 ip6/0 20 0 6 10:1:1:0:0:0:0:2,1026 > 10:1:1:0:0:0:0:1,1025 +> zx0 ip6/0 8 0 17 10:1:1:0:0:0:0:2,1025 > 10:1:1:0:0:0:0:1,1025 +> zx0 ip6/0 20 0 6 10:1:1:0:0:0:0:3,2000 > 10:1:2:0:0:0:0:1,80 +> zx0 ip6/0 20 0 6 10:1:1:0:0:0:0:3,2001 > 10:1:3:0:0:0:0:1,80 +> zx0 ip6/0 20 0 6 10:1:1:0:0:0:0:3,2002 > 10:1:4:0:0:0:0:1,80 +> zx0 ip6/0 20 0 6 10:1:1:0:0:0:0:3,2003 > 10:1:4:0:0:0:0:1,80 +< zx0 ip6/0 1 0 41 10:1:1:0:0:0:0:1 > 10:1:1:0:0:0:0:2 +< zx0 ip6/0 20 0 6 10:1:1:0:0:0:0:1,1025 > 10:1:1:0:0:0:0:2,1025 +< zx0 ip6/0 1 0 41 10:1:1:0:0:0:0:2 > 10:1:1:0:0:0:0:1 +> zx0 ip6/0 20 0 6 10:0:0:0:0:2:2:2,1026 > 10:0:0:0:0:3:4:5,40000 +< zx0 ip6/0 20 0 6 10:1:1:0:0:0:0:1,1026 > 10:0:0:0:0:3:4:5,40000 +> zx0 ip6/0 20 0 6 10:0:0:0:0:2:2:2,1025 > 10:0:0:0:0:3:4:5,40000 +< zx0 ip6/0 20 0 6 10:1:1:0:0:0:0:1,1025 > 10:0:0:0:0:3:4:5,40000 +> zx0 ip6/0 8 0 17 10:1:1:0:0:0:0:2,1025 > 10:0:0:0:0:3:4:5,40001 +< zx0 ip6/0 8 0 17 10:1:1:0:0:0:0:2,1025 > 10:0:0:0:0:3:4:5,40001 +> zx0 ip6/0 20 0 6 10:1:2:0:0:0:0:1,80 > 10:0:0:0:0:3:4:5,40001 +< zx0 ip6/0 20 0 6 10:1:2:0:0:0:0:1,80 > 10:0:0:0:0:3:4:5,40001 +List of active MAP/Redirect filters: +map zx0 inet6 10:1:1::1/128 -> 10::2:2:2/128 + +List of active sessions: +MAP 10:1:1::1 1025 <- -> 10::2:2:2 1025 [10::3:4:5 40000] +MAP 10:1:1::1 1026 <- -> 10::2:2:2 1026 [10::3:4:5 40000] +MAP 10:1:1::1 <- -> 10::2:2:2 [10:1:2::1] +MAP 10:1:1::1 1025 <- -> 10::2:2:2 1025 [10:1:1::2 1025] +MAP 10:1:1::1 1025 <- -> 10::2:2:2 1025 [10:1:1::1 1025] +MAP 10:1:1::1 <- -> 10::2:2:2 [10:4:3::2] +MAP 10:1:1::1 <- -> 10::2:2:2 [10:1:1::2] + +Hostmap table: +10:1:1::1,10::3:4:5 -> 10::2:2:2,any (use = 2) +10:1:1::1,10:1:2::1 -> 10::2:2:2,any (use = 1) +10:1:1::1,10:1:1::1 -> 10::2:2:2,any (use = 1) +10:1:1::1,10:4:3::2 -> 10::2:2:2,any (use = 1) +10:1:1::1,10:1:1::2 -> 10::2:2:2,any (use = 2) +List of active state sessions: +List of configured pools +List of configured hash tables +List of groups configured (set 0) +List of groups configured (set 1) +Rules configured (set 0, in) +Rules configured (set 0, out) +Rules configured (set 1, in) +Rules configured (set 1, out) +Accounting rules configured (set 0, in) +Accounting rules configured (set 0, out) +Accounting rules configured (set 1, in) +Accounting rules configured (set 1, out) +------------------------------- +> zx0 ip6/0 1 0 255 10:0:0:0:0:3:4:5 > 10:1:1:0:0:0:0:2 +16 +> zx0 ip6/0 1 0 255 10:0:0:0:0:3:4:5 > 10:1:1:0:0:0:0:1 +> zx0 ip6/0 20 0 6 10:0:0:0:0:3:4:5,1025 > 10:1:1:0:0:0:0:1,1025 +> zx0 ip6/0 20 0 6 10:0:0:0:0:3:4:5,1026 > 10:1:1:0:0:0:0:1,1025 +> zx0 ip6/0 1 0 255 10:0:0:0:0:2:2:1 > 10:1:2:0:0:0:0:1 +> zx0 ip6/0 1 0 255 10:0:0:0:0:2:2:2 > 10:1:2:0:0:0:0:1 +< zx0 ip6/0 1 0 255 10:1:1:0:0:0:0:1 > 10:1:1:0:0:0:0:2 +< zx0 ip6/0 1 0 255 10:1:1:0:0:0:0:2 > 10:1:1:0:0:0:0:1 +< zx0 ip6/0 1 0 255 10:0:0:0:0:2:2:1 > 10:0:0:0:0:2:1:1 +< zx0 ip6/0 1 0 255 10:0:0:0:0:2:2:2 > 10:0:0:0:0:2:1:1 +< zx0 ip6/0 1 0 255 10:0:0:0:0:2:2:3 > 10:1:1:0:0:0:0:1 +< zx0 ip6/0 1 0 255 10:0:0:0:0:2:3:4 > 10:0:0:0:0:2:2:2 +< zx0 ip6/0 1 0 255 10:1:1:0:0:0:0:1 > 10:0:0:0:0:2:2:2 +< zx0 ip6/0 1 0 255 10:1:1:0:0:0:0:2 > 10:0:0:0:0:2:2:2 +< zx0 ip6/0 1 0 255 10:1:1:0:0:0:0:0 > 10:0:0:0:0:3:4:5 +< zx0 ip6/0 1 0 255 10:1:1:0:0:0:0:1 > 10:1:1:0:0:0:0:2 +< zx0 ip6/0 1 0 255 10:1:1:0:0:0:0:2 > 10:1:1:0:0:0:0:0 +< zx0 ip6/0 20 0 6 10:1:1:0:0:0:0:1,1025 > 10:1:1:0:0:0:0:2,1025 +> zx0 ip6/0 88 0 58 10:1:1:0:0:0:0:1 > 10:4:3:0:0:0:0:2 +< zx0 ip6/0 88 0 58 10:4:3:0:0:0:0:2 > 10:0:0:0:0:2:2:2 +< zx0 ip6/0 88 0 58 10:4:3:0:0:0:0:2 > 10:0:0:0:0:3:4:3 +< zx0 ip6/0 88 0 58 10:4:3:0:0:0:0:2 > 10:0:0:0:0:3:4:5 +> zx0 ip6/0 1 0 34 10:1:1:0:0:0:0:2 > 10:4:3:0:0:0:0:2 +< zx0 ip6/0 1 0 34 10:4:3:0:0:0:0:2 > 10:0:0:0:0:3:4:4 +> zx0 ip6/0 1 0 34 10:1:1:0:0:0:0:2 > 10:4:3:0:0:0:0:4 +< zx0 ip6/0 1 0 34 10:4:3:0:0:0:0:4 > 10:0:0:0:0:3:4:5 +> zx0 ip6/0 1 0 34 10:1:1:0:0:0:0:3 > 10:4:3:0:0:0:0:4 +< zx0 ip6/0 1 0 34 10:4:3:0:0:0:0:4 > 10:0:0:0:0:3:4:6 +> zx0 ip6/0 1 0 35 10:1:1:0:0:0:0:3 > 10:4:3:0:0:0:0:4 +< zx0 ip6/0 1 0 35 10:4:3:0:0:0:0:4 > 10:0:0:0:0:3:4:7 +16 +> zx0 ip6/0 20 0 6 10:0:0:0:0:3:4:5,1025 > 10:1:1:0:0:0:0:2,1025 +> zx0 ip6/0 1 0 41 10:0:0:0:0:3:4:5 > 10:1:1:0:0:0:0:2 +> zx0 ip6/0 1 0 41 10:0:0:0:0:3:4:5 > 10:1:2:0:0:0:0:1 +> zx0 ip6/0 20 0 6 10:0:0:0:0:3:4:5,1025 > 10:1:1:0:0:0:0:1,1025 +> zx0 ip6/0 20 0 6 10:0:0:0:0:3:4:5,1025 > 10:1:1:0:0:0:0:1,1025 +> zx0 ip6/0 20 0 6 10:0:0:0:0:3:4:5,1026 > 10:1:1:0:0:0:0:1,1025 +> zx0 ip6/0 8 0 17 10:0:0:0:0:3:4:5,1025 > 10:1:1:0:0:0:0:1,1025 +> zx0 ip6/0 20 0 6 10:0:0:0:0:3:4:5,2000 > 10:1:2:0:0:0:0:1,80 +> zx0 ip6/0 20 0 6 10:0:0:0:0:3:4:5,2001 > 10:1:3:0:0:0:0:1,80 +> zx0 ip6/0 20 0 6 10:0:0:0:0:3:4:5,2002 > 10:1:4:0:0:0:0:1,80 +> zx0 ip6/0 20 0 6 10:0:0:0:0:3:4:5,2003 > 10:1:4:0:0:0:0:1,80 +< zx0 ip6/0 1 0 41 10:1:1:0:0:0:0:1 > 10:1:1:0:0:0:0:2 +< zx0 ip6/0 20 0 6 10:1:1:0:0:0:0:1,1025 > 10:1:1:0:0:0:0:2,1025 +< zx0 ip6/0 1 0 41 10:1:1:0:0:0:0:2 > 10:1:1:0:0:0:0:1 +> zx0 ip6/0 20 0 6 10:1:1:0:0:0:0:1,1026 > 10:0:0:0:0:3:4:5,40000 +< zx0 ip6/0 20 0 6 10:1:1:0:0:0:0:1,1026 > 10:0:0:0:0:3:4:5,40000 +> zx0 ip6/0 20 0 6 10:1:1:0:0:0:0:1,1025 > 10:0:0:0:0:3:4:5,40000 +< zx0 ip6/0 20 0 6 10:1:1:0:0:0:0:1,1025 > 10:0:0:0:0:3:4:5,40000 +> zx0 ip6/0 8 0 17 10:1:1:0:0:0:0:2,1025 > 10:0:0:0:0:3:4:5,40001 +< zx0 ip6/0 8 0 17 10:1:1:0:0:0:0:2,1025 > 10:0:0:0:0:3:4:5,40001 +> zx0 ip6/0 20 0 6 10:1:2:0:0:0:0:1,80 > 10:0:0:0:0:3:4:5,40001 +< zx0 ip6/0 20 0 6 10:1:2:0:0:0:0:1,80 > 10:0:0:0:0:3:4:5,40001 +List of active MAP/Redirect filters: +map zx0 inet6 from 10:1:1::/112 to 10:1::/32 -> 10::3:4:5/128 + +List of active sessions: +MAP 10:1:1::3 2003 <- -> 10::3:4:5 2003 [10:1:4::1 80] +MAP 10:1:1::3 2002 <- -> 10::3:4:5 2002 [10:1:4::1 80] +MAP 10:1:1::3 2001 <- -> 10::3:4:5 2001 [10:1:3::1 80] +MAP 10:1:1::3 2000 <- -> 10::3:4:5 2000 [10:1:2::1 80] +MAP 10:1:1::2 1025 <- -> 10::3:4:5 1025 [10:1:1::1 1025] +MAP 10:1:1::1 <- -> 10::3:4:5 [10:1:2::1] +MAP 10:1:1:: <- -> 10::3:4:5 [10:1:1::2] +MAP 10:1:1::1 1025 <- -> 10::3:4:5 1025 [10:1:1::2 1025] +MAP 10:1:1::2 1026 <- -> 10::3:4:5 1026 [10:1:1::1 1025] +MAP 10:1:1::2 1025 <- -> 10::3:4:5 1025 [10:1:1::1 1025] +MAP 10:1:1::2 <- -> 10::3:4:5 [10:1:1::1] +MAP 10:1:1:: <- -> 10::3:4:5 [10:1:1::2] + +Hostmap table: +10:1:1::3,10:1:4::1 -> 10::3:4:5,any (use = 2) +10:1:1::3,10:1:3::1 -> 10::3:4:5,any (use = 1) +10:1:1::3,10:1:2::1 -> 10::3:4:5,any (use = 1) +10:1:1::1,10:1:2::1 -> 10::3:4:5,any (use = 1) +10:1:1::1,10:1:1::2 -> 10::3:4:5,any (use = 1) +10:1:1::2,10:1:1::1 -> 10::3:4:5,any (use = 4) +10:1:1::,10:1:1::2 -> 10::3:4:5,any (use = 2) +List of active state sessions: +List of configured pools +List of configured hash tables +List of groups configured (set 0) +List of groups configured (set 1) +Rules configured (set 0, in) +Rules configured (set 0, out) +Rules configured (set 1, in) +Rules configured (set 1, out) +Accounting rules configured (set 0, in) +Accounting rules configured (set 0, out) +Accounting rules configured (set 1, in) +Accounting rules configured (set 1, out) +------------------------------- +> zx0 ip6/0 1 0 255 10:1:1:0:0:0:0:0 > 10:1:1:0:0:0:0:2 +> zx0 ip6/0 1 0 255 10:1:1:0:0:0:0:1 > 10:1:1:0:0:0:0:2 +> zx0 ip6/0 1 0 255 10:1:1:0:0:0:0:2 > 10:1:1:0:0:0:0:1 +> zx0 ip6/0 20 0 6 10:1:1:0:0:0:0:2,1025 > 10:1:1:0:0:0:0:1,1025 +> zx0 ip6/0 20 0 6 10:1:1:0:0:0:0:2,1026 > 10:1:1:0:0:0:0:1,1025 +> zx0 ip6/0 1 0 255 10:0:0:0:0:2:2:1 > 10:1:2:0:0:0:0:1 +> zx0 ip6/0 1 0 255 10:0:0:0:0:2:2:2 > 10:1:2:0:0:0:0:1 +< zx0 ip6/0 1 0 255 10:1:1:0:0:0:0:1 > 10:1:1:0:0:0:0:2 +< zx0 ip6/0 1 0 255 10:1:1:0:0:0:0:2 > 10:1:1:0:0:0:0:1 +< zx0 ip6/0 1 0 255 10:0:0:0:0:2:2:1 > 10:0:0:0:0:2:1:1 +< zx0 ip6/0 1 0 255 10:0:0:0:0:2:2:2 > 10:0:0:0:0:2:1:1 +< zx0 ip6/0 1 0 255 10:0:0:0:0:2:2:3 > 10:1:1:0:0:0:0:1 +< zx0 ip6/0 1 0 255 10:0:0:0:0:2:3:4 > 10:0:0:0:0:2:2:2 +< zx0 ip6/0 1 0 255 10:1:1:0:0:0:0:1 > 10:0:0:0:0:2:2:2 +< zx0 ip6/0 1 0 255 10:1:1:0:0:0:0:2 > 10:0:0:0:0:2:2:2 +< zx0 ip6/0 1 0 255 10:1:1:0:0:0:0:0 > 10:0:0:0:0:3:4:5 +< zx0 ip6/0 1 0 255 10:1:1:0:0:0:0:1 > 10:0:0:0:0:3:4:5 +< zx0 ip6/0 1 0 255 10:1:1:0:0:0:0:2 > 10:0:0:0:0:3:4:5 +< zx0 ip6/0 20 0 6 10:1:1:0:0:0:0:1,1025 > 10:0:0:0:0:3:4:5,1025 +> zx0 ip6/0 88 0 58 10:0:0:0:0:3:4:1 > 10:4:3:0:0:0:0:2 +< zx0 ip6/0 88 0 58 10:4:3:0:0:0:0:2 > 10:0:0:0:0:2:2:2 +< zx0 ip6/0 88 0 58 10:4:3:0:0:0:0:2 > 10:0:0:0:0:3:4:3 +< zx0 ip6/0 88 0 58 10:4:3:0:0:0:0:2 > 10:0:0:0:0:3:4:5 +> zx0 ip6/0 1 0 34 10:0:0:0:0:3:4:1 > 10:4:3:0:0:0:0:2 +< zx0 ip6/0 1 0 34 10:4:3:0:0:0:0:2 > 10:0:0:0:0:3:4:4 +> zx0 ip6/0 1 0 34 10:0:0:0:0:3:4:1 > 10:4:3:0:0:0:0:4 +< zx0 ip6/0 1 0 34 10:4:3:0:0:0:0:4 > 10:0:0:0:0:3:4:5 +> zx0 ip6/0 1 0 34 10:0:0:0:0:3:4:2 > 10:4:3:0:0:0:0:4 +< zx0 ip6/0 1 0 34 10:4:3:0:0:0:0:4 > 10:0:0:0:0:3:4:6 +> zx0 ip6/0 1 0 35 10:0:0:0:0:3:4:2 > 10:4:3:0:0:0:0:4 +< zx0 ip6/0 1 0 35 10:4:3:0:0:0:0:4 > 10:0:0:0:0:3:4:7 +> zx0 ip6/0 20 0 6 10:1:1:0:0:0:0:1,1025 > 10:1:1:0:0:0:0:1,1025 +> zx0 ip6/0 20 0 6 10:1:1:0:0:0:0:1,1025 > 10:1:1:0:0:0:0:2,1025 +> zx0 ip6/0 1 0 41 10:1:1:0:0:0:0:0 > 10:1:1:0:0:0:0:2 +> zx0 ip6/0 1 0 41 10:1:1:0:0:0:0:1 > 10:1:2:0:0:0:0:1 +> zx0 ip6/0 20 0 6 10:1:1:0:0:0:0:2,1025 > 10:1:1:0:0:0:0:1,1025 +> zx0 ip6/0 20 0 6 10:1:1:0:0:0:0:2,1025 > 10:1:1:0:0:0:0:1,1025 +> zx0 ip6/0 20 0 6 10:1:1:0:0:0:0:2,1026 > 10:1:1:0:0:0:0:1,1025 +> zx0 ip6/0 8 0 17 10:1:1:0:0:0:0:2,1025 > 10:1:1:0:0:0:0:1,1025 +> zx0 ip6/0 20 0 6 10:1:1:0:0:0:0:3,2000 > 10:1:2:0:0:0:0:1,80 +> zx0 ip6/0 20 0 6 10:1:1:0:0:0:0:3,2001 > 10:1:3:0:0:0:0:1,80 +> zx0 ip6/0 20 0 6 10:1:1:0:0:0:0:3,2002 > 10:1:4:0:0:0:0:1,80 +> zx0 ip6/0 20 0 6 10:1:1:0:0:0:0:3,2003 > 10:1:4:0:0:0:0:1,80 +< zx0 ip6/0 1 0 41 10:1:1:0:0:0:0:1 > 10:1:1:0:0:0:0:2 +< zx0 ip6/0 20 0 6 10:1:1:0:0:0:0:1,1025 > 10:1:1:0:0:0:0:2,1025 +< zx0 ip6/0 1 0 41 10:1:1:0:0:0:0:2 > 10:1:1:0:0:0:0:1 +> zx0 ip6/0 20 0 6 10:0:0:0:0:3:4:3,1026 > 10:0:0:0:0:3:4:5,40000 +< zx0 ip6/0 20 0 6 10:1:1:0:0:0:0:1,1026 > 10:0:0:0:0:3:4:5,40000 +> zx0 ip6/0 20 0 6 10:0:0:0:0:3:4:3,1025 > 10:0:0:0:0:3:4:5,40000 +< zx0 ip6/0 20 0 6 10:1:1:0:0:0:0:1,1025 > 10:0:0:0:0:3:4:5,40000 +> zx0 ip6/0 8 0 17 10:0:0:0:0:3:4:3,1025 > 10:0:0:0:0:3:4:5,40001 +< zx0 ip6/0 8 0 17 10:1:1:0:0:0:0:2,1025 > 10:0:0:0:0:3:4:5,40001 +> zx0 ip6/0 20 0 6 10:1:2:0:0:0:0:1,80 > 10:0:0:0:0:3:4:5,40001 +< zx0 ip6/0 20 0 6 10:1:2:0:0:0:0:1,80 > 10:0:0:0:0:3:4:5,40001 +List of active MAP/Redirect filters: +map zx0 inet6 from 10:1:1::/112 ! to 10:1::/32 -> 10::3:4:0/112 + +List of active sessions: +MAP 10:1:1::2 1025 <- -> 10::3:4:3 1025 [10::3:4:5 40001] +MAP 10:1:1::1 1025 <- -> 10::3:4:3 1025 [10::3:4:5 40000] +MAP 10:1:1::1 1026 <- -> 10::3:4:3 1026 [10::3:4:5 40000] +MAP 10:1:1::3 <- -> 10::3:4:2 [10:4:3::4] +MAP 10:1:1::3 <- -> 10::3:4:2 [10:4:3::4] +MAP 10:1:1::2 <- -> 10::3:4:1 [10:4:3::4] +MAP 10:1:1::2 <- -> 10::3:4:1 [10:4:3::2] +MAP 10:1:1::1 <- -> 10::3:4:1 [10:4:3::2] + +Hostmap table: +10:1:1::2,10::3:4:5 -> 10::3:4:3,any (use = 1) +10:1:1::1,10::3:4:5 -> 10::3:4:3,any (use = 2) +10:1:1::3,10:4:3::4 -> 10::3:4:2,any (use = 2) +10:1:1::2,10:4:3::4 -> 10::3:4:1,any (use = 1) +10:1:1::2,10:4:3::2 -> 10::3:4:1,any (use = 1) +10:1:1::1,10:4:3::2 -> 10::3:4:1,any (use = 1) +List of active state sessions: +List of configured pools +List of configured hash tables +List of groups configured (set 0) +List of groups configured (set 1) +Rules configured (set 0, in) +Rules configured (set 0, out) +Rules configured (set 1, in) +Rules configured (set 1, out) +Accounting rules configured (set 0, in) +Accounting rules configured (set 0, out) +Accounting rules configured (set 1, in) +Accounting rules configured (set 1, out) +------------------------------- +> zx0 ip6/0 1 0 255 10:1:1:0:0:0:0:0 > 10:1:1:0:0:0:0:2 +> zx0 ip6/0 1 0 255 10:1:1:0:0:0:0:1 > 10:1:1:0:0:0:0:2 +> zx0 ip6/0 1 0 255 10:1:1:0:0:0:0:2 > 10:1:1:0:0:0:0:1 +> zx0 ip6/0 20 0 6 10:1:1:0:0:0:0:2,1025 > 10:1:1:0:0:0:0:1,1025 +> zx0 ip6/0 20 0 6 10:1:1:0:0:0:0:2,1026 > 10:1:1:0:0:0:0:1,1025 +> zx0 ip6/0 1 0 255 10:0:0:0:0:2:2:1 > 10:1:2:0:0:0:0:1 +> zx0 ip6/0 1 0 255 10:0:0:0:0:2:2:2 > 10:1:2:0:0:0:0:1 +< zx0 ip6/0 1 0 255 10:1:1:0:0:0:0:1 > 10:1:1:0:0:0:0:2 +< zx0 ip6/0 1 0 255 10:1:1:0:0:0:0:2 > 10:1:1:0:0:0:0:1 +< zx0 ip6/0 1 0 255 10:0:0:0:0:2:2:1 > 10:0:0:0:0:2:1:1 +< zx0 ip6/0 1 0 255 10:0:0:0:0:2:2:2 > 10:0:0:0:0:2:1:1 +< zx0 ip6/0 1 0 255 10:0:0:0:0:2:2:3 > 10:1:1:0:0:0:0:1 +< zx0 ip6/0 1 0 255 10:0:0:0:0:2:3:4 > 10:0:0:0:0:2:2:2 +< zx0 ip6/0 1 0 255 10:1:1:0:0:0:0:1 > 10:0:0:0:0:2:2:2 +< zx0 ip6/0 1 0 255 10:1:1:0:0:0:0:2 > 10:0:0:0:0:2:2:2 +< zx0 ip6/0 1 0 255 10:1:1:0:0:0:0:0 > 10:0:0:0:0:3:4:5 +< zx0 ip6/0 1 0 255 10:1:1:0:0:0:0:1 > 10:0:0:0:0:3:4:5 +< zx0 ip6/0 1 0 255 10:1:1:0:0:0:0:2 > 10:0:0:0:0:3:4:5 +< zx0 ip6/0 20 0 6 10:1:1:0:0:0:0:1,1025 > 10:0:0:0:0:3:4:5,1025 +> zx0 ip6/0 88 0 58 10:1:1:0:0:0:0:1 > 10:4:3:0:0:0:0:2 +< zx0 ip6/0 88 0 58 10:4:3:0:0:0:0:2 > 10:0:0:0:0:2:2:2 +< zx0 ip6/0 88 0 58 10:4:3:0:0:0:0:2 > 10:0:0:0:0:3:4:3 +< zx0 ip6/0 88 0 58 10:4:3:0:0:0:0:2 > 10:0:0:0:0:3:4:5 +> zx0 ip6/0 1 0 34 10:1:1:0:0:0:0:2 > 10:4:3:0:0:0:0:2 +< zx0 ip6/0 1 0 34 10:4:3:0:0:0:0:2 > 10:0:0:0:0:3:4:4 +> zx0 ip6/0 1 0 34 10:1:1:0:0:0:0:2 > 10:4:3:0:0:0:0:4 +< zx0 ip6/0 1 0 34 10:4:3:0:0:0:0:4 > 10:0:0:0:0:3:4:5 +> zx0 ip6/0 1 0 34 10:1:1:0:0:0:0:3 > 10:4:3:0:0:0:0:4 +< zx0 ip6/0 1 0 34 10:4:3:0:0:0:0:4 > 10:0:0:0:0:3:4:6 +> zx0 ip6/0 1 0 35 10:1:1:0:0:0:0:3 > 10:4:3:0:0:0:0:4 +< zx0 ip6/0 1 0 35 10:4:3:0:0:0:0:4 > 10:0:0:0:0:3:4:7 +> zx0 ip6/0 20 0 6 10:1:1:0:0:0:0:1,1025 > 10:1:1:0:0:0:0:1,1025 +> zx0 ip6/0 20 0 6 10:1:1:0:0:0:0:1,1025 > 10:1:1:0:0:0:0:2,1025 +> zx0 ip6/0 1 0 41 10:1:1:0:0:0:0:0 > 10:1:1:0:0:0:0:2 +> zx0 ip6/0 1 0 41 10:1:1:0:0:0:0:1 > 10:1:2:0:0:0:0:1 +> zx0 ip6/0 20 0 6 10:1:1:0:0:0:0:2,1025 > 10:1:1:0:0:0:0:1,1025 +> zx0 ip6/0 20 0 6 10:1:1:0:0:0:0:2,1025 > 10:1:1:0:0:0:0:1,1025 +> zx0 ip6/0 20 0 6 10:1:1:0:0:0:0:2,1026 > 10:1:1:0:0:0:0:1,1025 +> zx0 ip6/0 8 0 17 10:0:0:0:0:3:4:5,10000 > 10:1:1:0:0:0:0:1,1025 +> zx0 ip6/0 20 0 6 10:1:1:0:0:0:0:3,2000 > 10:1:2:0:0:0:0:1,80 +> zx0 ip6/0 20 0 6 10:1:1:0:0:0:0:3,2001 > 10:1:3:0:0:0:0:1,80 +> zx0 ip6/0 20 0 6 10:1:1:0:0:0:0:3,2002 > 10:1:4:0:0:0:0:1,80 +> zx0 ip6/0 20 0 6 10:1:1:0:0:0:0:3,2003 > 10:1:4:0:0:0:0:1,80 +< zx0 ip6/0 1 0 41 10:1:1:0:0:0:0:1 > 10:1:1:0:0:0:0:2 +< zx0 ip6/0 20 0 6 10:1:1:0:0:0:0:1,1025 > 10:1:1:0:0:0:0:2,1025 +< zx0 ip6/0 1 0 41 10:1:1:0:0:0:0:2 > 10:1:1:0:0:0:0:1 +> zx0 ip6/0 20 0 6 10:1:1:0:0:0:0:1,1026 > 10:0:0:0:0:3:4:5,40000 +< zx0 ip6/0 20 0 6 10:1:1:0:0:0:0:1,1026 > 10:0:0:0:0:3:4:5,40000 +> zx0 ip6/0 20 0 6 10:1:1:0:0:0:0:1,1025 > 10:0:0:0:0:3:4:5,40000 +< zx0 ip6/0 20 0 6 10:1:1:0:0:0:0:1,1025 > 10:0:0:0:0:3:4:5,40000 +> zx0 ip6/0 8 0 17 10:0:0:0:0:3:4:5,10001 > 10:0:0:0:0:3:4:5,40001 +< zx0 ip6/0 8 0 17 10:1:1:0:0:0:0:2,1025 > 10:0:0:0:0:3:4:5,40001 +> zx0 ip6/0 20 0 6 10:1:2:0:0:0:0:1,80 > 10:0:0:0:0:3:4:5,40001 +< zx0 ip6/0 20 0 6 10:1:2:0:0:0:0:1,80 > 10:0:0:0:0:3:4:5,40001 +List of active MAP/Redirect filters: +map zx0 inet6 10:1:1::/112 -> 10::3:4:5/128 portmap udp 10000:20000 sequential + +List of active sessions: +MAP 10:1:1::2 1025 <- -> 10::3:4:5 10001 [10::3:4:5 40001] +MAP 10:1:1::2 1025 <- -> 10::3:4:5 10000 [10:1:1::1 1025] + +Hostmap table: +10:1:1::2,10::3:4:5 -> 10::3:4:5,any (use = 1) +10:1:1::2,10:1:1::1 -> 10::3:4:5,any (use = 1) +List of active state sessions: +List of configured pools +List of configured hash tables +List of groups configured (set 0) +List of groups configured (set 1) +Rules configured (set 0, in) +Rules configured (set 0, out) +Rules configured (set 1, in) +Rules configured (set 1, out) +Accounting rules configured (set 0, in) +Accounting rules configured (set 0, out) +Accounting rules configured (set 1, in) +Accounting rules configured (set 1, out) +------------------------------- +> zx0 ip6/0 1 0 255 10:1:1:0:0:0:0:0 > 10:1:1:0:0:0:0:2 +> zx0 ip6/0 1 0 255 10:1:1:0:0:0:0:1 > 10:1:1:0:0:0:0:2 +> zx0 ip6/0 1 0 255 10:1:1:0:0:0:0:2 > 10:1:1:0:0:0:0:1 +> zx0 ip6/0 20 0 6 10:0:0:0:0:3:4:1,10000 > 10:1:1:0:0:0:0:1,1025 +> zx0 ip6/0 20 0 6 10:0:0:0:0:3:4:1,10001 > 10:1:1:0:0:0:0:1,1025 +> zx0 ip6/0 1 0 255 10:0:0:0:0:2:2:1 > 10:1:2:0:0:0:0:1 +> zx0 ip6/0 1 0 255 10:0:0:0:0:2:2:2 > 10:1:2:0:0:0:0:1 +< zx0 ip6/0 1 0 255 10:1:1:0:0:0:0:1 > 10:1:1:0:0:0:0:2 +< zx0 ip6/0 1 0 255 10:1:1:0:0:0:0:2 > 10:1:1:0:0:0:0:1 +< zx0 ip6/0 1 0 255 10:0:0:0:0:2:2:1 > 10:0:0:0:0:2:1:1 +< zx0 ip6/0 1 0 255 10:0:0:0:0:2:2:2 > 10:0:0:0:0:2:1:1 +< zx0 ip6/0 1 0 255 10:0:0:0:0:2:2:3 > 10:1:1:0:0:0:0:1 +< zx0 ip6/0 1 0 255 10:0:0:0:0:2:3:4 > 10:0:0:0:0:2:2:2 +< zx0 ip6/0 1 0 255 10:1:1:0:0:0:0:1 > 10:0:0:0:0:2:2:2 +< zx0 ip6/0 1 0 255 10:1:1:0:0:0:0:2 > 10:0:0:0:0:2:2:2 +< zx0 ip6/0 1 0 255 10:1:1:0:0:0:0:0 > 10:0:0:0:0:3:4:5 +< zx0 ip6/0 1 0 255 10:1:1:0:0:0:0:1 > 10:0:0:0:0:3:4:5 +< zx0 ip6/0 1 0 255 10:1:1:0:0:0:0:2 > 10:0:0:0:0:3:4:5 +< zx0 ip6/0 20 0 6 10:1:1:0:0:0:0:1,1025 > 10:0:0:0:0:3:4:5,1025 +> zx0 ip6/0 88 0 58 10:1:1:0:0:0:0:1 > 10:4:3:0:0:0:0:2 +< zx0 ip6/0 88 0 58 10:4:3:0:0:0:0:2 > 10:0:0:0:0:2:2:2 +< zx0 ip6/0 88 0 58 10:4:3:0:0:0:0:2 > 10:0:0:0:0:3:4:3 +< zx0 ip6/0 88 0 58 10:4:3:0:0:0:0:2 > 10:0:0:0:0:3:4:5 +> zx0 ip6/0 1 0 34 10:1:1:0:0:0:0:2 > 10:4:3:0:0:0:0:2 +< zx0 ip6/0 1 0 34 10:4:3:0:0:0:0:2 > 10:0:0:0:0:3:4:4 +> zx0 ip6/0 1 0 34 10:1:1:0:0:0:0:2 > 10:4:3:0:0:0:0:4 +< zx0 ip6/0 1 0 34 10:4:3:0:0:0:0:4 > 10:0:0:0:0:3:4:5 +> zx0 ip6/0 1 0 34 10:1:1:0:0:0:0:3 > 10:4:3:0:0:0:0:4 +< zx0 ip6/0 1 0 34 10:4:3:0:0:0:0:4 > 10:0:0:0:0:3:4:6 +> zx0 ip6/0 1 0 35 10:1:1:0:0:0:0:3 > 10:4:3:0:0:0:0:4 +< zx0 ip6/0 1 0 35 10:4:3:0:0:0:0:4 > 10:0:0:0:0:3:4:7 +> zx0 ip6/0 20 0 6 10:0:0:0:0:3:4:1,10002 > 10:1:1:0:0:0:0:1,1025 +> zx0 ip6/0 20 0 6 10:0:0:0:0:3:4:1,10003 > 10:1:1:0:0:0:0:2,1025 +> zx0 ip6/0 1 0 41 10:1:1:0:0:0:0:0 > 10:1:1:0:0:0:0:2 +> zx0 ip6/0 1 0 41 10:1:1:0:0:0:0:1 > 10:1:2:0:0:0:0:1 +> zx0 ip6/0 20 0 6 10:0:0:0:0:3:4:1,10000 > 10:1:1:0:0:0:0:1,1025 +> zx0 ip6/0 20 0 6 10:0:0:0:0:3:4:1,10000 > 10:1:1:0:0:0:0:1,1025 +> zx0 ip6/0 20 0 6 10:0:0:0:0:3:4:1,10001 > 10:1:1:0:0:0:0:1,1025 +> zx0 ip6/0 8 0 17 10:0:0:0:0:3:4:1,10004 > 10:1:1:0:0:0:0:1,1025 +> zx0 ip6/0 20 0 6 10:0:0:0:0:3:4:1,10005 > 10:1:2:0:0:0:0:1,80 +> zx0 ip6/0 20 0 6 10:0:0:0:0:3:4:1,10006 > 10:1:3:0:0:0:0:1,80 +> zx0 ip6/0 20 0 6 10:0:0:0:0:3:4:1,10007 > 10:1:4:0:0:0:0:1,80 +> zx0 ip6/0 20 0 6 10:0:0:0:0:3:4:1,10008 > 10:1:4:0:0:0:0:1,80 +< zx0 ip6/0 1 0 41 10:1:1:0:0:0:0:1 > 10:1:1:0:0:0:0:2 +< zx0 ip6/0 20 0 6 10:1:1:0:0:0:0:1,1025 > 10:1:1:0:0:0:0:2,1025 +< zx0 ip6/0 1 0 41 10:1:1:0:0:0:0:2 > 10:1:1:0:0:0:0:1 +> zx0 ip6/0 20 0 6 10:0:0:0:0:3:4:1,10009 > 10:0:0:0:0:3:4:5,40000 +< zx0 ip6/0 20 0 6 10:1:1:0:0:0:0:1,1026 > 10:0:0:0:0:3:4:5,40000 +> zx0 ip6/0 20 0 6 10:0:0:0:0:3:4:1,10010 > 10:0:0:0:0:3:4:5,40000 +< zx0 ip6/0 20 0 6 10:1:1:0:0:0:0:1,1025 > 10:0:0:0:0:3:4:5,40000 +> zx0 ip6/0 8 0 17 10:0:0:0:0:3:4:1,10011 > 10:0:0:0:0:3:4:5,40001 +< zx0 ip6/0 8 0 17 10:1:1:0:0:0:0:2,1025 > 10:0:0:0:0:3:4:5,40001 +> zx0 ip6/0 20 0 6 10:0:0:0:0:3:4:1,10012 > 10:0:0:0:0:3:4:5,40001 +< zx0 ip6/0 20 0 6 10:1:2:0:0:0:0:1,80 > 10:0:0:0:0:3:4:5,40001 +List of active MAP/Redirect filters: +map zx0 inet6 10:1::/32 -> 10::3:4:0/112 portmap tcp/udp 10000:20000 sequential + +List of active sessions: +MAP 10:1:2::1 80 <- -> 10::3:4:1 10012 [10::3:4:5 40001] +MAP 10:1:1::2 1025 <- -> 10::3:4:1 10011 [10::3:4:5 40001] +MAP 10:1:1::1 1025 <- -> 10::3:4:1 10010 [10::3:4:5 40000] +MAP 10:1:1::1 1026 <- -> 10::3:4:1 10009 [10::3:4:5 40000] +MAP 10:1:1::3 2003 <- -> 10::3:4:1 10008 [10:1:4::1 80] +MAP 10:1:1::3 2002 <- -> 10::3:4:1 10007 [10:1:4::1 80] +MAP 10:1:1::3 2001 <- -> 10::3:4:1 10006 [10:1:3::1 80] +MAP 10:1:1::3 2000 <- -> 10::3:4:1 10005 [10:1:2::1 80] +MAP 10:1:1::2 1025 <- -> 10::3:4:1 10004 [10:1:1::1 1025] +MAP 10:1:1::1 1025 <- -> 10::3:4:1 10003 [10:1:1::2 1025] +MAP 10:1:1::1 1025 <- -> 10::3:4:1 10002 [10:1:1::1 1025] +MAP 10:1:1::2 1026 <- -> 10::3:4:1 10001 [10:1:1::1 1025] +MAP 10:1:1::2 1025 <- -> 10::3:4:1 10000 [10:1:1::1 1025] + +Hostmap table: +10:1:2::1,10::3:4:5 -> 10::3:4:1,any (use = 1) +10:1:1::2,10::3:4:5 -> 10::3:4:1,any (use = 1) +10:1:1::1,10::3:4:5 -> 10::3:4:1,any (use = 2) +10:1:1::3,10:1:4::1 -> 10::3:4:1,any (use = 2) +10:1:1::3,10:1:3::1 -> 10::3:4:1,any (use = 1) +10:1:1::3,10:1:2::1 -> 10::3:4:1,any (use = 1) +10:1:1::1,10:1:1::2 -> 10::3:4:1,any (use = 1) +10:1:1::1,10:1:1::1 -> 10::3:4:1,any (use = 1) +10:1:1::2,10:1:1::1 -> 10::3:4:1,any (use = 3) +List of active state sessions: +List of configured pools +List of configured hash tables +List of groups configured (set 0) +List of groups configured (set 1) +Rules configured (set 0, in) +Rules configured (set 0, out) +Rules configured (set 1, in) +Rules configured (set 1, out) +Accounting rules configured (set 0, in) +Accounting rules configured (set 0, out) +Accounting rules configured (set 1, in) +Accounting rules configured (set 1, out) +------------------------------- +> zx0 ip6/0 1 0 255 10:1:1:0:0:0:0:0 > 10:1:1:0:0:0:0:2 +> zx0 ip6/0 1 0 255 10:1:1:0:0:0:0:1 > 10:1:1:0:0:0:0:2 +> zx0 ip6/0 1 0 255 10:1:1:0:0:0:0:2 > 10:1:1:0:0:0:0:1 +> zx0 ip6/0 20 0 6 10:0:0:0:0:3:4:5,40000 > 10:1:1:0:0:0:0:1,1025 +> zx0 ip6/0 20 0 6 10:0:0:0:0:3:4:5,40001 > 10:1:1:0:0:0:0:1,1025 +> zx0 ip6/0 1 0 255 10:0:0:0:0:2:2:1 > 10:1:2:0:0:0:0:1 +> zx0 ip6/0 1 0 255 10:0:0:0:0:2:2:2 > 10:1:2:0:0:0:0:1 +< zx0 ip6/0 1 0 255 10:1:1:0:0:0:0:1 > 10:1:1:0:0:0:0:2 +< zx0 ip6/0 1 0 255 10:1:1:0:0:0:0:2 > 10:1:1:0:0:0:0:1 +< zx0 ip6/0 1 0 255 10:0:0:0:0:2:2:1 > 10:0:0:0:0:2:1:1 +< zx0 ip6/0 1 0 255 10:0:0:0:0:2:2:2 > 10:0:0:0:0:2:1:1 +< zx0 ip6/0 1 0 255 10:0:0:0:0:2:2:3 > 10:1:1:0:0:0:0:1 +< zx0 ip6/0 1 0 255 10:0:0:0:0:2:3:4 > 10:0:0:0:0:2:2:2 +< zx0 ip6/0 1 0 255 10:1:1:0:0:0:0:1 > 10:0:0:0:0:2:2:2 +< zx0 ip6/0 1 0 255 10:1:1:0:0:0:0:2 > 10:0:0:0:0:2:2:2 +< zx0 ip6/0 1 0 255 10:1:1:0:0:0:0:0 > 10:0:0:0:0:3:4:5 +< zx0 ip6/0 1 0 255 10:1:1:0:0:0:0:1 > 10:0:0:0:0:3:4:5 +< zx0 ip6/0 1 0 255 10:1:1:0:0:0:0:2 > 10:0:0:0:0:3:4:5 +< zx0 ip6/0 20 0 6 10:1:1:0:0:0:0:1,1025 > 10:0:0:0:0:3:4:5,1025 +> zx0 ip6/0 88 0 58 10:1:1:0:0:0:0:1 > 10:4:3:0:0:0:0:2 +< zx0 ip6/0 88 0 58 10:4:3:0:0:0:0:2 > 10:0:0:0:0:2:2:2 +< zx0 ip6/0 88 0 58 10:4:3:0:0:0:0:2 > 10:0:0:0:0:3:4:3 +< zx0 ip6/0 88 0 58 10:4:3:0:0:0:0:2 > 10:0:0:0:0:3:4:5 +> zx0 ip6/0 1 0 34 10:1:1:0:0:0:0:2 > 10:4:3:0:0:0:0:2 +< zx0 ip6/0 1 0 34 10:4:3:0:0:0:0:2 > 10:0:0:0:0:3:4:4 +> zx0 ip6/0 1 0 34 10:1:1:0:0:0:0:2 > 10:4:3:0:0:0:0:4 +< zx0 ip6/0 1 0 34 10:4:3:0:0:0:0:4 > 10:0:0:0:0:3:4:5 +> zx0 ip6/0 1 0 34 10:1:1:0:0:0:0:3 > 10:4:3:0:0:0:0:4 +< zx0 ip6/0 1 0 34 10:4:3:0:0:0:0:4 > 10:0:0:0:0:3:4:6 +> zx0 ip6/0 1 0 35 10:1:1:0:0:0:0:3 > 10:4:3:0:0:0:0:4 +< zx0 ip6/0 1 0 35 10:4:3:0:0:0:0:4 > 10:0:0:0:0:3:4:7 +16 +> zx0 ip6/0 20 0 6 10:0:0:0:0:3:4:5,40000 > 10:1:1:0:0:0:0:2,1025 +> zx0 ip6/0 1 0 41 10:1:1:0:0:0:0:0 > 10:1:1:0:0:0:0:2 +> zx0 ip6/0 1 0 41 10:1:1:0:0:0:0:1 > 10:1:2:0:0:0:0:1 +> zx0 ip6/0 20 0 6 10:0:0:0:0:3:4:5,40000 > 10:1:1:0:0:0:0:1,1025 +> zx0 ip6/0 20 0 6 10:0:0:0:0:3:4:5,40000 > 10:1:1:0:0:0:0:1,1025 +> zx0 ip6/0 20 0 6 10:0:0:0:0:3:4:5,40001 > 10:1:1:0:0:0:0:1,1025 +> zx0 ip6/0 8 0 17 10:0:0:0:0:3:4:5,40001 > 10:1:1:0:0:0:0:1,1025 +> zx0 ip6/0 20 0 6 10:0:0:0:0:3:4:5,40000 > 10:1:2:0:0:0:0:1,80 +> zx0 ip6/0 20 0 6 10:0:0:0:0:3:4:5,40001 > 10:1:3:0:0:0:0:1,80 +> zx0 ip6/0 20 0 6 10:0:0:0:0:3:4:5,40000 > 10:1:4:0:0:0:0:1,80 +> zx0 ip6/0 20 0 6 10:0:0:0:0:3:4:5,40001 > 10:1:4:0:0:0:0:1,80 +< zx0 ip6/0 1 0 41 10:1:1:0:0:0:0:1 > 10:1:1:0:0:0:0:2 +< zx0 ip6/0 20 0 6 10:1:1:0:0:0:0:1,1025 > 10:1:1:0:0:0:0:2,1025 +< zx0 ip6/0 1 0 41 10:1:1:0:0:0:0:2 > 10:1:1:0:0:0:0:1 +> zx0 ip6/0 20 0 6 10:0:0:0:0:3:4:5,40000 > 10:0:0:0:0:3:4:5,40000 +< zx0 ip6/0 20 0 6 10:1:1:0:0:0:0:1,1026 > 10:0:0:0:0:3:4:5,40000 +> zx0 ip6/0 20 0 6 10:0:0:0:0:3:4:5,40001 > 10:0:0:0:0:3:4:5,40000 +< zx0 ip6/0 20 0 6 10:1:1:0:0:0:0:1,1025 > 10:1:1:0:0:0:0:2,1025 +> zx0 ip6/0 8 0 17 10:0:0:0:0:3:4:5,40000 > 10:0:0:0:0:3:4:5,40001 +< zx0 ip6/0 8 0 17 10:1:1:0:0:0:0:2,1025 > 10:0:0:0:0:3:4:5,40001 +> zx0 ip6/0 20 0 6 10:1:2:0:0:0:0:1,80 > 10:0:0:0:0:3:4:5,40001 +< zx0 ip6/0 20 0 6 10:1:2:0:0:0:0:1,80 > 10:0:0:0:0:3:4:5,40001 +List of active MAP/Redirect filters: +map zx0 inet6 10:1:1::/112 -> 10::3:4:5/128 portmap tcp/udp 40000:40001 sequential + +List of active sessions: +MAP 10:1:1::2 1025 <- -> 10::3:4:5 40000 [10::3:4:5 40001] +MAP 10:1:1::1 1025 <- -> 10::3:4:5 40001 [10::3:4:5 40000] +MAP 10:1:1::1 1026 <- -> 10::3:4:5 40000 [10::3:4:5 40000] +MAP 10:1:1::3 2003 <- -> 10::3:4:5 40001 [10:1:4::1 80] +MAP 10:1:1::3 2002 <- -> 10::3:4:5 40000 [10:1:4::1 80] +MAP 10:1:1::3 2001 <- -> 10::3:4:5 40001 [10:1:3::1 80] +MAP 10:1:1::3 2000 <- -> 10::3:4:5 40000 [10:1:2::1 80] +MAP 10:1:1::2 1025 <- -> 10::3:4:5 40001 [10:1:1::1 1025] +MAP 10:1:1::1 1025 <- -> 10::3:4:5 40000 [10:1:1::2 1025] +MAP 10:1:1::2 1026 <- -> 10::3:4:5 40001 [10:1:1::1 1025] +MAP 10:1:1::2 1025 <- -> 10::3:4:5 40000 [10:1:1::1 1025] + +Hostmap table: +10:1:1::2,10::3:4:5 -> 10::3:4:5,any (use = 1) +10:1:1::1,10::3:4:5 -> 10::3:4:5,any (use = 2) +10:1:1::3,10:1:4::1 -> 10::3:4:5,any (use = 2) +10:1:1::3,10:1:3::1 -> 10::3:4:5,any (use = 1) +10:1:1::3,10:1:2::1 -> 10::3:4:5,any (use = 1) +10:1:1::1,10:1:1::2 -> 10::3:4:5,any (use = 1) +10:1:1::2,10:1:1::1 -> 10::3:4:5,any (use = 3) +List of active state sessions: +List of configured pools +List of configured hash tables +List of groups configured (set 0) +List of groups configured (set 1) +Rules configured (set 0, in) +Rules configured (set 0, out) +Rules configured (set 1, in) +Rules configured (set 1, out) +Accounting rules configured (set 0, in) +Accounting rules configured (set 0, out) +Accounting rules configured (set 1, in) +Accounting rules configured (set 1, out) +------------------------------- diff --git a/contrib/ipfilter/test/expected/n6 b/contrib/ipfilter/test/expected/n6 index cbdad9f1388..1afd94e54e4 100644 --- a/contrib/ipfilter/test/expected/n6 +++ b/contrib/ipfilter/test/expected/n6 @@ -1,70 +1,173 @@ -ip #0 40(20) 6 10.2.2.2,12345 > 10.2.2.1,10023 -ip #0 40(20) 6 10.2.2.2,12345 > 10.1.1.2,23 -ip #0 40(20) 6 10.3.0.1,12345 > 10.1.2.2,23 -ip #0 40(20) 6 10.3.0.1,12345 > 10.2.2.2,23 -ip #0 40(20) 6 10.3.3.3,12345 > 10.2.2.1,10023 -ip #0 40(20) 6 10.2.2.2,12345 > 10.1.1.1,53 -ip #0 40(20) 6 10.3.3.3,12345 > 10.1.1.1,53 -ip #0 40(20) 6 10.2.2.2,12345 > 10.1.0.0,23 -ip #0 40(20) 6 10.3.3.3,12345 > 10.1.0.0,23 -ip #0 28(20) 17 10.2.2.2,12345 > 10.1.1.0,53 -ip #0 28(20) 17 10.3.3.3,12345 > 10.1.1.0,53 -ip #0 40(20) 6 10.2.2.2,12345 > 10.1.1.0,53 -ip #0 40(20) 6 10.3.3.3,12345 > 10.1.1.0,53 +< zx0 ip #0 40(20) 6 10.2.2.2,12345 > 10.2.2.1,10023 +< zx0 ip #0 40(20) 6 10.2.2.2,12345 > 10.1.1.2,23 +< zx0 ip #0 40(20) 6 10.3.0.1,12345 > 10.1.2.2,23 +< zx0 ip #0 40(20) 6 10.3.0.1,12345 > 10.2.2.2,23 +< zx0 ip #0 40(20) 6 10.3.3.3,12345 > 10.2.2.1,10023 +< zx0 ip #0 40(20) 6 10.2.2.2,12345 > 10.1.1.1,53 +< zx0 ip #0 40(20) 6 10.3.3.3,12345 > 10.1.1.1,53 +< zx0 ip #0 40(20) 6 10.2.2.2,12345 > 10.1.0.0,23 +< zx0 ip #0 40(20) 6 10.3.3.3,12345 > 10.1.0.0,23 +< zx0 ip #0 28(20) 17 10.2.2.2,12345 > 10.1.1.0,53 +< zx0 ip #0 28(20) 17 10.3.3.3,12345 > 10.1.1.0,53 +< zx0 ip #0 40(20) 6 10.2.2.2,12345 > 10.1.1.0,53 +< zx0 ip #0 40(20) 6 10.3.3.3,12345 > 10.1.1.0,53 +List of active MAP/Redirect filters: +rdr zx0 10.1.1.1/32 port 23 -> 10.2.2.1/32 port 10023 tcp + +List of active sessions: +RDR 10.2.2.1 10023 <- -> 10.1.1.1 23 [10.3.3.3 12345] +RDR 10.2.2.1 10023 <- -> 10.1.1.1 23 [10.2.2.2 12345] + +Hostmap table: +List of active state sessions: +List of configured pools +List of configured hash tables +List of groups configured (set 0) +List of groups configured (set 1) +Rules configured (set 0, in) +Rules configured (set 0, out) +Rules configured (set 1, in) +Rules configured (set 1, out) +Accounting rules configured (set 0, in) +Accounting rules configured (set 0, out) +Accounting rules configured (set 1, in) +Accounting rules configured (set 1, out) ------------------------------- -ip #0 40(20) 6 10.2.2.2,12345 > 10.2.2.1,10023 -ip #0 40(20) 6 10.2.2.2,12345 > 10.1.1.2,23 -ip #0 40(20) 6 10.3.0.1,12345 > 10.1.2.2,23 -ip #0 40(20) 6 10.3.0.1,12345 > 10.2.2.2,23 -ip #0 40(20) 6 10.3.3.3,12345 > 10.2.2.1,10023 -ip #0 40(20) 6 10.2.2.2,12345 > 10.1.1.1,53 -ip #0 40(20) 6 10.3.3.3,12345 > 10.1.1.1,53 -ip #0 40(20) 6 10.2.2.2,12345 > 10.1.0.0,23 -ip #0 40(20) 6 10.3.3.3,12345 > 10.1.0.0,23 -ip #0 28(20) 17 10.2.2.2,12345 > 10.1.1.0,53 -ip #0 28(20) 17 10.3.3.3,12345 > 10.1.1.0,53 -ip #0 40(20) 6 10.2.2.2,12345 > 10.1.1.0,53 -ip #0 40(20) 6 10.3.3.3,12345 > 10.1.1.0,53 +< zx0 ip #0 40(20) 6 10.2.2.2,12345 > 10.2.2.1,10023 +15 +< zx0 ip #0 40(20) 6 10.3.0.1,12345 > 10.1.2.2,23 +< zx0 ip #0 40(20) 6 10.3.0.1,12345 > 10.2.2.2,23 +< zx0 ip #0 40(20) 6 10.3.3.3,12345 > 10.2.2.1,10023 +< zx0 ip #0 40(20) 6 10.2.2.2,12345 > 10.1.1.1,53 +< zx0 ip #0 40(20) 6 10.3.3.3,12345 > 10.1.1.1,53 +< zx0 ip #0 40(20) 6 10.2.2.2,12345 > 10.1.0.0,23 +< zx0 ip #0 40(20) 6 10.3.3.3,12345 > 10.1.0.0,23 +< zx0 ip #0 28(20) 17 10.2.2.2,12345 > 10.1.1.0,53 +< zx0 ip #0 28(20) 17 10.3.3.3,12345 > 10.1.1.0,53 +< zx0 ip #0 40(20) 6 10.2.2.2,12345 > 10.1.1.0,53 +< zx0 ip #0 40(20) 6 10.3.3.3,12345 > 10.1.1.0,53 +List of active MAP/Redirect filters: +rdr zx0 from 0/0 to 10.1.1.0/24 port = 23 -> 10.2.2.1/32 port 10023 tcp + +List of active sessions: +RDR 10.2.2.1 10023 <- -> 10.1.1.1 23 [10.3.3.3 12345] +RDR 10.2.2.1 10023 <- -> 10.1.1.1 23 [10.2.2.2 12345] + +Hostmap table: +List of active state sessions: +List of configured pools +List of configured hash tables +List of groups configured (set 0) +List of groups configured (set 1) +Rules configured (set 0, in) +Rules configured (set 0, out) +Rules configured (set 1, in) +Rules configured (set 1, out) +Accounting rules configured (set 0, in) +Accounting rules configured (set 0, out) +Accounting rules configured (set 1, in) +Accounting rules configured (set 1, out) ------------------------------- -ip #0 40(20) 6 10.2.2.2,12345 > 10.2.2.1,10023 -ip #0 40(20) 6 10.2.2.2,12345 > 10.1.1.2,23 -ip #0 40(20) 6 10.3.0.1,12345 > 10.1.2.2,23 -ip #0 40(20) 6 10.3.0.1,12345 > 10.2.2.2,23 -ip #0 40(20) 6 10.3.3.3,12345 > 10.1.1.1,23 -ip #0 40(20) 6 10.2.2.2,12345 > 10.1.1.1,53 -ip #0 40(20) 6 10.3.3.3,12345 > 10.1.1.1,53 -ip #0 40(20) 6 10.2.2.2,12345 > 10.1.0.0,23 -ip #0 40(20) 6 10.3.3.3,12345 > 10.1.0.0,23 -ip #0 28(20) 17 10.2.2.2,12345 > 10.1.1.0,53 -ip #0 28(20) 17 10.3.3.3,12345 > 10.1.1.0,53 -ip #0 40(20) 6 10.2.2.2,12345 > 10.1.1.0,53 -ip #0 40(20) 6 10.3.3.3,12345 > 10.1.1.0,53 +< zx0 ip #0 40(20) 6 10.2.2.2,12345 > 10.2.2.1,10023 +15 +< zx0 ip #0 40(20) 6 10.3.0.1,12345 > 10.1.2.2,23 +< zx0 ip #0 40(20) 6 10.3.0.1,12345 > 10.2.2.2,23 +< zx0 ip #0 40(20) 6 10.3.3.3,12345 > 10.1.1.1,23 +< zx0 ip #0 40(20) 6 10.2.2.2,12345 > 10.1.1.1,53 +< zx0 ip #0 40(20) 6 10.3.3.3,12345 > 10.1.1.1,53 +< zx0 ip #0 40(20) 6 10.2.2.2,12345 > 10.1.0.0,23 +< zx0 ip #0 40(20) 6 10.3.3.3,12345 > 10.1.0.0,23 +< zx0 ip #0 28(20) 17 10.2.2.2,12345 > 10.1.1.0,53 +< zx0 ip #0 28(20) 17 10.3.3.3,12345 > 10.1.1.0,53 +< zx0 ip #0 40(20) 6 10.2.2.2,12345 > 10.1.1.0,53 +< zx0 ip #0 40(20) 6 10.3.3.3,12345 > 10.1.1.0,53 +List of active MAP/Redirect filters: +rdr zx0 from 10.2.0.0/16 to 10.1.1.0/24 port = 23 -> 10.2.2.1/32 port 10023 tcp + +List of active sessions: +RDR 10.2.2.1 10023 <- -> 10.1.1.1 23 [10.2.2.2 12345] + +Hostmap table: +List of active state sessions: +List of configured pools +List of configured hash tables +List of groups configured (set 0) +List of groups configured (set 1) +Rules configured (set 0, in) +Rules configured (set 0, out) +Rules configured (set 1, in) +Rules configured (set 1, out) +Accounting rules configured (set 0, in) +Accounting rules configured (set 0, out) +Accounting rules configured (set 1, in) +Accounting rules configured (set 1, out) ------------------------------- -ip #0 40(20) 6 10.2.2.2,12345 > 10.1.1.1,23 -ip #0 40(20) 6 10.2.2.2,12345 > 10.1.1.2,23 -ip #0 40(20) 6 10.3.0.1,12345 > 10.2.2.1,10023 -ip #0 40(20) 6 10.3.0.1,12345 > 10.2.2.2,23 -ip #0 40(20) 6 10.3.3.3,12345 > 10.2.2.1,10023 -ip #0 40(20) 6 10.2.2.2,12345 > 10.1.1.1,53 -ip #0 40(20) 6 10.3.3.3,12345 > 10.1.1.1,53 -ip #0 40(20) 6 10.2.2.2,12345 > 10.1.0.0,23 -ip #0 40(20) 6 10.3.3.3,12345 > 10.1.0.0,23 -ip #0 28(20) 17 10.2.2.2,12345 > 10.1.1.0,53 -ip #0 28(20) 17 10.3.3.3,12345 > 10.1.1.0,53 -ip #0 40(20) 6 10.2.2.2,12345 > 10.1.1.0,53 -ip #0 40(20) 6 10.3.3.3,12345 > 10.1.1.0,53 +< zx0 ip #0 40(20) 6 10.2.2.2,12345 > 10.1.1.1,23 +< zx0 ip #0 40(20) 6 10.2.2.2,12345 > 10.1.1.2,23 +< zx0 ip #0 40(20) 6 10.3.0.1,12345 > 10.2.2.1,10023 +< zx0 ip #0 40(20) 6 10.3.0.1,12345 > 10.2.2.2,23 +< zx0 ip #0 40(20) 6 10.3.3.3,12345 > 10.2.2.1,10023 +< zx0 ip #0 40(20) 6 10.2.2.2,12345 > 10.1.1.1,53 +< zx0 ip #0 40(20) 6 10.3.3.3,12345 > 10.1.1.1,53 +< zx0 ip #0 40(20) 6 10.2.2.2,12345 > 10.1.0.0,23 +15 +< zx0 ip #0 28(20) 17 10.2.2.2,12345 > 10.1.1.0,53 +< zx0 ip #0 28(20) 17 10.3.3.3,12345 > 10.1.1.0,53 +< zx0 ip #0 40(20) 6 10.2.2.2,12345 > 10.1.1.0,53 +< zx0 ip #0 40(20) 6 10.3.3.3,12345 > 10.1.1.0,53 +List of active MAP/Redirect filters: +rdr zx0 from 10.3.0.0/16 to 10.1.0.0/16 port = 23 -> 10.2.2.1/32 port 10023 tcp + +List of active sessions: +RDR 10.2.2.1 10023 <- -> 10.1.1.1 23 [10.3.3.3 12345] +RDR 10.2.2.1 10023 <- -> 10.1.2.2 23 [10.3.0.1 12345] + +Hostmap table: +List of active state sessions: +List of configured pools +List of configured hash tables +List of groups configured (set 0) +List of groups configured (set 1) +Rules configured (set 0, in) +Rules configured (set 0, out) +Rules configured (set 1, in) +Rules configured (set 1, out) +Accounting rules configured (set 0, in) +Accounting rules configured (set 0, out) +Accounting rules configured (set 1, in) +Accounting rules configured (set 1, out) ------------------------------- -ip #0 40(20) 6 10.2.2.2,12345 > 10.1.1.1,23 -ip #0 40(20) 6 10.2.2.2,12345 > 10.1.1.2,23 -ip #0 40(20) 6 10.3.0.1,12345 > 10.1.2.2,23 -ip #0 40(20) 6 10.3.0.1,12345 > 10.2.2.2,23 -ip #0 40(20) 6 10.3.3.3,12345 > 10.1.1.1,23 -ip #0 40(20) 6 10.2.2.2,12345 > 10.1.1.1,53 -ip #0 40(20) 6 10.3.3.3,12345 > 10.1.1.1,53 -ip #0 40(20) 6 10.2.2.2,12345 > 10.1.0.0,23 -ip #0 40(20) 6 10.3.3.3,12345 > 10.1.0.0,23 -ip #0 28(20) 17 10.2.2.2,12345 > 10.1.1.0,53 -ip #0 28(20) 17 10.3.3.3,12345 > 10.2.2.1,10053 -ip #0 40(20) 6 10.2.2.2,12345 > 10.1.1.0,53 -ip #0 40(20) 6 10.3.3.3,12345 > 10.1.1.0,53 +< zx0 ip #0 40(20) 6 10.2.2.2,12345 > 10.1.1.1,23 +< zx0 ip #0 40(20) 6 10.2.2.2,12345 > 10.1.1.2,23 +< zx0 ip #0 40(20) 6 10.3.0.1,12345 > 10.1.2.2,23 +< zx0 ip #0 40(20) 6 10.3.0.1,12345 > 10.2.2.2,23 +< zx0 ip #0 40(20) 6 10.3.3.3,12345 > 10.1.1.1,23 +< zx0 ip #0 40(20) 6 10.2.2.2,12345 > 10.1.1.1,53 +< zx0 ip #0 40(20) 6 10.3.3.3,12345 > 10.1.1.1,53 +< zx0 ip #0 40(20) 6 10.2.2.2,12345 > 10.1.0.0,23 +< zx0 ip #0 40(20) 6 10.3.3.3,12345 > 10.1.0.0,23 +< zx0 ip #0 28(20) 17 10.2.2.2,12345 > 10.1.1.0,53 +< zx0 ip #0 28(20) 17 10.3.3.3,12345 > 10.2.2.1,10053 +< zx0 ip #0 40(20) 6 10.2.2.2,12345 > 10.1.1.0,53 +< zx0 ip #0 40(20) 6 10.3.3.3,12345 > 10.1.1.0,53 +List of active MAP/Redirect filters: +rdr zx0 ! from 10.2.0.0/16 to 10.1.1.0/24 port = 53 -> 10.2.2.1/32 port 10053 udp + +List of active sessions: +RDR 10.2.2.1 10053 <- -> 10.1.1.0 53 [10.3.3.3 12345] + +Hostmap table: +List of active state sessions: +List of configured pools +List of configured hash tables +List of groups configured (set 0) +List of groups configured (set 1) +Rules configured (set 0, in) +Rules configured (set 0, out) +Rules configured (set 1, in) +Rules configured (set 1, out) +Accounting rules configured (set 0, in) +Accounting rules configured (set 0, out) +Accounting rules configured (set 1, in) +Accounting rules configured (set 1, out) ------------------------------- diff --git a/contrib/ipfilter/test/expected/n6_6 b/contrib/ipfilter/test/expected/n6_6 new file mode 100644 index 00000000000..e10f9bd8f43 --- /dev/null +++ b/contrib/ipfilter/test/expected/n6_6 @@ -0,0 +1,173 @@ +< zx0 ip6/0 20 0 6 10:0:0:0:0:2:2:2,12345 > 10:0:0:0:0:2:2:1,10023 +< zx0 ip6/0 20 0 6 10:0:0:0:0:2:2:2,12345 > 10:1:1:0:0:0:0:2,23 +< zx0 ip6/0 20 0 6 10:3:0:0:0:0:0:1,12345 > 10:1:2:0:0:0:0:2,23 +< zx0 ip6/0 20 0 6 10:3:0:0:0:0:0:1,12345 > 10:0:0:0:0:2:2:2,23 +< zx0 ip6/0 20 0 6 10:3:3:0:0:0:0:3,12345 > 10:0:0:0:0:2:2:1,10023 +< zx0 ip6/0 20 0 6 10:0:0:0:0:2:2:2,12345 > 10:1:1:0:0:0:0:1,53 +< zx0 ip6/0 20 0 6 10:3:3:0:0:0:0:3,12345 > 10:1:1:0:0:0:0:1,53 +< zx0 ip6/0 20 0 6 10:0:0:0:0:2:2:2,12345 > 10:1:0:0:0:0:0:0,23 +< zx0 ip6/0 20 0 6 10:3:3:0:0:0:0:3,12345 > 10:1:0:0:0:0:0:0,23 +< zx0 ip6/0 8 0 17 10:0:0:0:0:2:2:2,12345 > 10:1:1:0:0:0:0:0,53 +< zx0 ip6/0 8 0 17 10:3:3:0:0:0:0:3,12345 > 10:1:1:0:0:0:0:0,53 +< zx0 ip6/0 20 0 6 10:0:0:0:0:2:2:2,12345 > 10:1:1:0:0:0:0:0,53 +< zx0 ip6/0 20 0 6 10:3:3:0:0:0:0:3,12345 > 10:1:1:0:0:0:0:0,53 +List of active MAP/Redirect filters: +rdr zx0 inet6 10:1:1::1/128 port 23 -> 10::2:2:1/128 port 10023 tcp + +List of active sessions: +RDR 10::2:2:1 10023 <- -> 10:1:1::1 23 [10:3:3::3 12345] +RDR 10::2:2:1 10023 <- -> 10:1:1::1 23 [10::2:2:2 12345] + +Hostmap table: +List of active state sessions: +List of configured pools +List of configured hash tables +List of groups configured (set 0) +List of groups configured (set 1) +Rules configured (set 0, in) +Rules configured (set 0, out) +Rules configured (set 1, in) +Rules configured (set 1, out) +Accounting rules configured (set 0, in) +Accounting rules configured (set 0, out) +Accounting rules configured (set 1, in) +Accounting rules configured (set 1, out) +------------------------------- +< zx0 ip6/0 20 0 6 10:0:0:0:0:2:2:2,12345 > 10:0:0:0:0:2:2:1,10023 +16 +< zx0 ip6/0 20 0 6 10:3:0:0:0:0:0:1,12345 > 10:1:2:0:0:0:0:2,23 +< zx0 ip6/0 20 0 6 10:3:0:0:0:0:0:1,12345 > 10:0:0:0:0:2:2:2,23 +< zx0 ip6/0 20 0 6 10:3:3:0:0:0:0:3,12345 > 10:0:0:0:0:2:2:1,10023 +< zx0 ip6/0 20 0 6 10:0:0:0:0:2:2:2,12345 > 10:1:1:0:0:0:0:1,53 +< zx0 ip6/0 20 0 6 10:3:3:0:0:0:0:3,12345 > 10:1:1:0:0:0:0:1,53 +< zx0 ip6/0 20 0 6 10:0:0:0:0:2:2:2,12345 > 10:1:0:0:0:0:0:0,23 +< zx0 ip6/0 20 0 6 10:3:3:0:0:0:0:3,12345 > 10:1:0:0:0:0:0:0,23 +< zx0 ip6/0 8 0 17 10:0:0:0:0:2:2:2,12345 > 10:1:1:0:0:0:0:0,53 +< zx0 ip6/0 8 0 17 10:3:3:0:0:0:0:3,12345 > 10:1:1:0:0:0:0:0,53 +< zx0 ip6/0 20 0 6 10:0:0:0:0:2:2:2,12345 > 10:1:1:0:0:0:0:0,53 +< zx0 ip6/0 20 0 6 10:3:3:0:0:0:0:3,12345 > 10:1:1:0:0:0:0:0,53 +List of active MAP/Redirect filters: +rdr zx0 inet6 from any to 10:1:1::/112 port = 23 -> 10::2:2:1/128 port 10023 tcp + +List of active sessions: +RDR 10::2:2:1 10023 <- -> 10:1:1::1 23 [10:3:3::3 12345] +RDR 10::2:2:1 10023 <- -> 10:1:1::1 23 [10::2:2:2 12345] + +Hostmap table: +List of active state sessions: +List of configured pools +List of configured hash tables +List of groups configured (set 0) +List of groups configured (set 1) +Rules configured (set 0, in) +Rules configured (set 0, out) +Rules configured (set 1, in) +Rules configured (set 1, out) +Accounting rules configured (set 0, in) +Accounting rules configured (set 0, out) +Accounting rules configured (set 1, in) +Accounting rules configured (set 1, out) +------------------------------- +< zx0 ip6/0 20 0 6 10:0:0:0:0:2:2:2,12345 > 10:0:0:0:0:2:2:1,10023 +16 +< zx0 ip6/0 20 0 6 10:3:0:0:0:0:0:1,12345 > 10:1:2:0:0:0:0:2,23 +< zx0 ip6/0 20 0 6 10:3:0:0:0:0:0:1,12345 > 10:0:0:0:0:2:2:2,23 +< zx0 ip6/0 20 0 6 10:3:3:0:0:0:0:3,12345 > 10:1:1:0:0:0:0:1,23 +< zx0 ip6/0 20 0 6 10:0:0:0:0:2:2:2,12345 > 10:1:1:0:0:0:0:1,53 +< zx0 ip6/0 20 0 6 10:3:3:0:0:0:0:3,12345 > 10:1:1:0:0:0:0:1,53 +< zx0 ip6/0 20 0 6 10:0:0:0:0:2:2:2,12345 > 10:1:0:0:0:0:0:0,23 +< zx0 ip6/0 20 0 6 10:3:3:0:0:0:0:3,12345 > 10:1:0:0:0:0:0:0,23 +< zx0 ip6/0 8 0 17 10:0:0:0:0:2:2:2,12345 > 10:1:1:0:0:0:0:0,53 +< zx0 ip6/0 8 0 17 10:3:3:0:0:0:0:3,12345 > 10:1:1:0:0:0:0:0,53 +< zx0 ip6/0 20 0 6 10:0:0:0:0:2:2:2,12345 > 10:1:1:0:0:0:0:0,53 +< zx0 ip6/0 20 0 6 10:3:3:0:0:0:0:3,12345 > 10:1:1:0:0:0:0:0,53 +List of active MAP/Redirect filters: +rdr zx0 inet6 from 10::/32 to 10:1:1::/112 port = 23 -> 10::2:2:1/128 port 10023 tcp + +List of active sessions: +RDR 10::2:2:1 10023 <- -> 10:1:1::1 23 [10::2:2:2 12345] + +Hostmap table: +List of active state sessions: +List of configured pools +List of configured hash tables +List of groups configured (set 0) +List of groups configured (set 1) +Rules configured (set 0, in) +Rules configured (set 0, out) +Rules configured (set 1, in) +Rules configured (set 1, out) +Accounting rules configured (set 0, in) +Accounting rules configured (set 0, out) +Accounting rules configured (set 1, in) +Accounting rules configured (set 1, out) +------------------------------- +< zx0 ip6/0 20 0 6 10:0:0:0:0:2:2:2,12345 > 10:1:1:0:0:0:0:1,23 +< zx0 ip6/0 20 0 6 10:0:0:0:0:2:2:2,12345 > 10:1:1:0:0:0:0:2,23 +< zx0 ip6/0 20 0 6 10:3:0:0:0:0:0:1,12345 > 10:0:0:0:0:2:2:1,10023 +< zx0 ip6/0 20 0 6 10:3:0:0:0:0:0:1,12345 > 10:0:0:0:0:2:2:2,23 +< zx0 ip6/0 20 0 6 10:3:3:0:0:0:0:3,12345 > 10:0:0:0:0:2:2:1,10023 +< zx0 ip6/0 20 0 6 10:0:0:0:0:2:2:2,12345 > 10:1:1:0:0:0:0:1,53 +< zx0 ip6/0 20 0 6 10:3:3:0:0:0:0:3,12345 > 10:1:1:0:0:0:0:1,53 +< zx0 ip6/0 20 0 6 10:0:0:0:0:2:2:2,12345 > 10:1:0:0:0:0:0:0,23 +16 +< zx0 ip6/0 8 0 17 10:0:0:0:0:2:2:2,12345 > 10:1:1:0:0:0:0:0,53 +< zx0 ip6/0 8 0 17 10:3:3:0:0:0:0:3,12345 > 10:1:1:0:0:0:0:0,53 +< zx0 ip6/0 20 0 6 10:0:0:0:0:2:2:2,12345 > 10:1:1:0:0:0:0:0,53 +< zx0 ip6/0 20 0 6 10:3:3:0:0:0:0:3,12345 > 10:1:1:0:0:0:0:0,53 +List of active MAP/Redirect filters: +rdr zx0 inet6 from 10:3::/32 to 10:1::/32 port = 23 -> 10::2:2:1/128 port 10023 tcp + +List of active sessions: +RDR 10::2:2:1 10023 <- -> 10:1:1::1 23 [10:3:3::3 12345] +RDR 10::2:2:1 10023 <- -> 10:1:2::2 23 [10:3::1 12345] + +Hostmap table: +List of active state sessions: +List of configured pools +List of configured hash tables +List of groups configured (set 0) +List of groups configured (set 1) +Rules configured (set 0, in) +Rules configured (set 0, out) +Rules configured (set 1, in) +Rules configured (set 1, out) +Accounting rules configured (set 0, in) +Accounting rules configured (set 0, out) +Accounting rules configured (set 1, in) +Accounting rules configured (set 1, out) +------------------------------- +< zx0 ip6/0 20 0 6 10:0:0:0:0:2:2:2,12345 > 10:1:1:0:0:0:0:1,23 +< zx0 ip6/0 20 0 6 10:0:0:0:0:2:2:2,12345 > 10:1:1:0:0:0:0:2,23 +< zx0 ip6/0 20 0 6 10:3:0:0:0:0:0:1,12345 > 10:1:2:0:0:0:0:2,23 +< zx0 ip6/0 20 0 6 10:3:0:0:0:0:0:1,12345 > 10:0:0:0:0:2:2:2,23 +< zx0 ip6/0 20 0 6 10:3:3:0:0:0:0:3,12345 > 10:1:1:0:0:0:0:1,23 +< zx0 ip6/0 20 0 6 10:0:0:0:0:2:2:2,12345 > 10:1:1:0:0:0:0:1,53 +< zx0 ip6/0 20 0 6 10:3:3:0:0:0:0:3,12345 > 10:1:1:0:0:0:0:1,53 +< zx0 ip6/0 20 0 6 10:0:0:0:0:2:2:2,12345 > 10:1:0:0:0:0:0:0,23 +< zx0 ip6/0 20 0 6 10:3:3:0:0:0:0:3,12345 > 10:1:0:0:0:0:0:0,23 +< zx0 ip6/0 8 0 17 10:0:0:0:0:2:2:2,12345 > 10:1:1:0:0:0:0:0,53 +< zx0 ip6/0 8 0 17 10:3:3:0:0:0:0:3,12345 > 10:0:0:0:0:2:2:1,10053 +< zx0 ip6/0 20 0 6 10:0:0:0:0:2:2:2,12345 > 10:1:1:0:0:0:0:0,53 +< zx0 ip6/0 20 0 6 10:3:3:0:0:0:0:3,12345 > 10:1:1:0:0:0:0:0,53 +List of active MAP/Redirect filters: +rdr zx0 inet6 ! from 10::/32 to 10:1:1::/112 port = 53 -> 10::2:2:1/128 port 10053 udp + +List of active sessions: +RDR 10::2:2:1 10053 <- -> 10:1:1:: 53 [10:3:3::3 12345] + +Hostmap table: +List of active state sessions: +List of configured pools +List of configured hash tables +List of groups configured (set 0) +List of groups configured (set 1) +Rules configured (set 0, in) +Rules configured (set 0, out) +Rules configured (set 1, in) +Rules configured (set 1, out) +Accounting rules configured (set 0, in) +Accounting rules configured (set 0, out) +Accounting rules configured (set 1, in) +Accounting rules configured (set 1, out) +------------------------------- diff --git a/contrib/ipfilter/test/expected/n7 b/contrib/ipfilter/test/expected/n7 index eb23534d0b3..11b811538ba 100644 --- a/contrib/ipfilter/test/expected/n7 +++ b/contrib/ipfilter/test/expected/n7 @@ -1,30 +1,98 @@ -ip #0 40(20) 6 10.2.3.1,1230 > 10.1.1.1,22 -ip #0 40(20) 6 10.2.3.1,1231 > 10.2.2.1,10023 -ip #0 40(20) 6 10.2.3.1,1232 > 10.2.2.1,10050 -ip #0 40(20) 6 10.2.3.1,1233 > 10.2.2.1,10079 -ip #0 40(20) 6 10.2.3.1,1234 > 10.1.1.1,80 -ip #0 40(20) 6 10.2.3.1,1235 > 10.1.1.2,80 -ip #0 40(20) 6 10.2.3.1,1236 > 10.1.1.3,80 -ip #0 40(20) 6 10.2.3.1,1237 > 10.1.1.4,80 -ip #0 40(20) 6 10.2.3.1,1238 > 10.1.1.4,80 +< zx0 ip #0 40(20) 6 10.2.3.1,1230 > 10.1.1.1,22 +< zx0 ip #0 40(20) 6 10.2.3.1,1231 > 10.2.2.1,10023 +< zx0 ip #0 40(20) 6 10.2.3.1,1232 > 10.2.2.1,10050 +< zx0 ip #0 40(20) 6 10.2.3.1,1233 > 10.2.2.1,10079 +< zx0 ip #0 40(20) 6 10.2.3.1,1234 > 10.1.1.1,80 +< zx0 ip #0 40(20) 6 10.2.3.1,1235 > 10.1.1.2,80 +< zx0 ip #0 40(20) 6 10.2.3.1,1236 > 10.1.1.3,80 +< zx0 ip #0 40(20) 6 10.2.3.1,1237 > 10.1.1.4,80 +< zx0 ip #0 40(20) 6 10.2.3.1,1238 > 10.1.1.4,80 +List of active MAP/Redirect filters: +rdr zx0 10.1.1.1/32 port 23-79 -> 10.2.2.1/32 port 10023 tcp + +List of active sessions: +RDR 10.2.2.1 10079 <- -> 10.1.1.1 79 [10.2.3.1 1233] +RDR 10.2.2.1 10050 <- -> 10.1.1.1 50 [10.2.3.1 1232] +RDR 10.2.2.1 10023 <- -> 10.1.1.1 23 [10.2.3.1 1231] + +Hostmap table: +List of active state sessions: +List of configured pools +List of configured hash tables +List of groups configured (set 0) +List of groups configured (set 1) +Rules configured (set 0, in) +Rules configured (set 0, out) +Rules configured (set 1, in) +Rules configured (set 1, out) +Accounting rules configured (set 0, in) +Accounting rules configured (set 0, out) +Accounting rules configured (set 1, in) +Accounting rules configured (set 1, out) ------------------------------- -ip #0 40(20) 6 10.2.3.1,1230 > 10.1.1.1,22 -ip #0 40(20) 6 10.2.3.1,1231 > 10.2.2.1,10023 -ip #0 40(20) 6 10.2.3.1,1232 > 10.2.2.1,10023 -ip #0 40(20) 6 10.2.3.1,1233 > 10.2.2.1,10023 -ip #0 40(20) 6 10.2.3.1,1234 > 10.1.1.1,80 -ip #0 40(20) 6 10.2.3.1,1235 > 10.1.1.2,80 -ip #0 40(20) 6 10.2.3.1,1236 > 10.1.1.3,80 -ip #0 40(20) 6 10.2.3.1,1237 > 10.1.1.4,80 -ip #0 40(20) 6 10.2.3.1,1238 > 10.1.1.4,80 +< zx0 ip #0 40(20) 6 10.2.3.1,1230 > 10.1.1.1,22 +< zx0 ip #0 40(20) 6 10.2.3.1,1231 > 10.2.2.1,10023 +< zx0 ip #0 40(20) 6 10.2.3.1,1232 > 10.2.2.1,10023 +< zx0 ip #0 40(20) 6 10.2.3.1,1233 > 10.2.2.1,10023 +< zx0 ip #0 40(20) 6 10.2.3.1,1234 > 10.1.1.1,80 +< zx0 ip #0 40(20) 6 10.2.3.1,1235 > 10.1.1.2,80 +< zx0 ip #0 40(20) 6 10.2.3.1,1236 > 10.1.1.3,80 +< zx0 ip #0 40(20) 6 10.2.3.1,1237 > 10.1.1.4,80 +< zx0 ip #0 40(20) 6 10.2.3.1,1238 > 10.1.1.4,80 +List of active MAP/Redirect filters: +rdr zx0 10.1.1.1/32 port 23-79 -> 10.2.2.1/32 port = 10023 tcp + +List of active sessions: +RDR 10.2.2.1 10023 <- -> 10.1.1.1 79 [10.2.3.1 1233] +RDR 10.2.2.1 10023 <- -> 10.1.1.1 50 [10.2.3.1 1232] +RDR 10.2.2.1 10023 <- -> 10.1.1.1 23 [10.2.3.1 1231] + +Hostmap table: +List of active state sessions: +List of configured pools +List of configured hash tables +List of groups configured (set 0) +List of groups configured (set 1) +Rules configured (set 0, in) +Rules configured (set 0, out) +Rules configured (set 1, in) +Rules configured (set 1, out) +Accounting rules configured (set 0, in) +Accounting rules configured (set 0, out) +Accounting rules configured (set 1, in) +Accounting rules configured (set 1, out) ------------------------------- -ip #0 40(20) 6 10.2.3.1,1230 > 10.1.1.1,22 -ip #0 40(20) 6 10.2.3.1,1231 > 10.1.1.1,23 -ip #0 40(20) 6 10.2.3.1,1232 > 10.1.1.1,50 -ip #0 40(20) 6 10.2.3.1,1233 > 10.1.1.1,79 -ip #0 40(20) 6 10.2.3.1,1234 > 10.2.2.1,3128 -ip #0 40(20) 6 10.2.3.1,1235 > 1.2.2.129,3128 -ip #0 40(20) 6 10.2.3.1,1236 > 10.2.2.1,3128 -ip #0 40(20) 6 10.2.3.1,1237 > 1.2.2.129,3128 -ip #0 40(20) 6 10.2.3.1,1238 > 10.2.2.1,3128 +< zx0 ip #0 40(20) 6 10.2.3.1,1230 > 10.1.1.1,22 +< zx0 ip #0 40(20) 6 10.2.3.1,1231 > 10.1.1.1,23 +< zx0 ip #0 40(20) 6 10.2.3.1,1232 > 10.1.1.1,50 +< zx0 ip #0 40(20) 6 10.2.3.1,1233 > 10.1.1.1,79 +< zx0 ip #0 40(20) 6 10.2.3.1,1234 > 10.2.2.1,3128 +< zx0 ip #0 40(20) 6 10.2.3.1,1235 > 1.2.2.129,3128 +< zx0 ip #0 40(20) 6 10.2.3.1,1236 > 10.2.2.1,3128 +< zx0 ip #0 40(20) 6 10.2.3.1,1237 > 1.2.2.129,3128 +< zx0 ip #0 40(20) 6 10.2.3.1,1238 > 10.2.2.1,3128 +List of active MAP/Redirect filters: +rdr zx0 10.1.1.0/24 port 80 -> 10.2.2.1,1.2.2.129 port 3128 tcp + +List of active sessions: +RDR 10.2.2.1 3128 <- -> 10.1.1.4 80 [10.2.3.1 1238] +RDR 1.2.2.129 3128 <- -> 10.1.1.4 80 [10.2.3.1 1237] +RDR 10.2.2.1 3128 <- -> 10.1.1.3 80 [10.2.3.1 1236] +RDR 1.2.2.129 3128 <- -> 10.1.1.2 80 [10.2.3.1 1235] +RDR 10.2.2.1 3128 <- -> 10.1.1.1 80 [10.2.3.1 1234] + +Hostmap table: +List of active state sessions: +List of configured pools +List of configured hash tables +List of groups configured (set 0) +List of groups configured (set 1) +Rules configured (set 0, in) +Rules configured (set 0, out) +Rules configured (set 1, in) +Rules configured (set 1, out) +Accounting rules configured (set 0, in) +Accounting rules configured (set 0, out) +Accounting rules configured (set 1, in) +Accounting rules configured (set 1, out) ------------------------------- diff --git a/contrib/ipfilter/test/expected/n7_6 b/contrib/ipfilter/test/expected/n7_6 new file mode 100644 index 00000000000..25630334147 --- /dev/null +++ b/contrib/ipfilter/test/expected/n7_6 @@ -0,0 +1,98 @@ +< zx0 ip6/0 20 0 6 10:0:0:0:0:2:3:1,1230 > 10:1:1:0:0:0:0:1,22 +< zx0 ip6/0 20 0 6 10:0:0:0:0:2:3:1,1231 > 10:0:0:0:0:2:2:1,10023 +< zx0 ip6/0 20 0 6 10:0:0:0:0:2:3:1,1232 > 10:0:0:0:0:2:2:1,10050 +< zx0 ip6/0 20 0 6 10:0:0:0:0:2:3:1,1233 > 10:0:0:0:0:2:2:1,10079 +< zx0 ip6/0 20 0 6 10:0:0:0:0:2:3:1,1234 > 10:1:1:0:0:0:0:1,80 +< zx0 ip6/0 20 0 6 10:0:0:0:0:2:3:1,1235 > 10:1:1:0:0:0:0:2,80 +< zx0 ip6/0 20 0 6 10:0:0:0:0:2:3:1,1236 > 10:1:1:0:0:0:0:3,80 +< zx0 ip6/0 20 0 6 10:0:0:0:0:2:3:1,1237 > 10:1:1:0:0:0:0:4,80 +< zx0 ip6/0 20 0 6 10:0:0:0:0:2:3:1,1238 > 10:1:1:0:0:0:0:4,80 +List of active MAP/Redirect filters: +rdr zx0 inet6 10:1:1::1/128 port 23-79 -> 10::2:2:1/128 port 10023 tcp + +List of active sessions: +RDR 10::2:2:1 10079 <- -> 10:1:1::1 79 [10::2:3:1 1233] +RDR 10::2:2:1 10050 <- -> 10:1:1::1 50 [10::2:3:1 1232] +RDR 10::2:2:1 10023 <- -> 10:1:1::1 23 [10::2:3:1 1231] + +Hostmap table: +List of active state sessions: +List of configured pools +List of configured hash tables +List of groups configured (set 0) +List of groups configured (set 1) +Rules configured (set 0, in) +Rules configured (set 0, out) +Rules configured (set 1, in) +Rules configured (set 1, out) +Accounting rules configured (set 0, in) +Accounting rules configured (set 0, out) +Accounting rules configured (set 1, in) +Accounting rules configured (set 1, out) +------------------------------- +< zx0 ip6/0 20 0 6 10:0:0:0:0:2:3:1,1230 > 10:1:1:0:0:0:0:1,22 +< zx0 ip6/0 20 0 6 10:0:0:0:0:2:3:1,1231 > 10:0:0:0:0:2:2:1,10023 +< zx0 ip6/0 20 0 6 10:0:0:0:0:2:3:1,1232 > 10:0:0:0:0:2:2:1,10023 +< zx0 ip6/0 20 0 6 10:0:0:0:0:2:3:1,1233 > 10:0:0:0:0:2:2:1,10023 +< zx0 ip6/0 20 0 6 10:0:0:0:0:2:3:1,1234 > 10:1:1:0:0:0:0:1,80 +< zx0 ip6/0 20 0 6 10:0:0:0:0:2:3:1,1235 > 10:1:1:0:0:0:0:2,80 +< zx0 ip6/0 20 0 6 10:0:0:0:0:2:3:1,1236 > 10:1:1:0:0:0:0:3,80 +< zx0 ip6/0 20 0 6 10:0:0:0:0:2:3:1,1237 > 10:1:1:0:0:0:0:4,80 +< zx0 ip6/0 20 0 6 10:0:0:0:0:2:3:1,1238 > 10:1:1:0:0:0:0:4,80 +List of active MAP/Redirect filters: +rdr zx0 inet6 10:1:1::1/128 port 23-79 -> 10::2:2:1/128 port = 10023 tcp + +List of active sessions: +RDR 10::2:2:1 10023 <- -> 10:1:1::1 79 [10::2:3:1 1233] +RDR 10::2:2:1 10023 <- -> 10:1:1::1 50 [10::2:3:1 1232] +RDR 10::2:2:1 10023 <- -> 10:1:1::1 23 [10::2:3:1 1231] + +Hostmap table: +List of active state sessions: +List of configured pools +List of configured hash tables +List of groups configured (set 0) +List of groups configured (set 1) +Rules configured (set 0, in) +Rules configured (set 0, out) +Rules configured (set 1, in) +Rules configured (set 1, out) +Accounting rules configured (set 0, in) +Accounting rules configured (set 0, out) +Accounting rules configured (set 1, in) +Accounting rules configured (set 1, out) +------------------------------- +< zx0 ip6/0 20 0 6 10:0:0:0:0:2:3:1,1230 > 10:1:1:0:0:0:0:1,22 +< zx0 ip6/0 20 0 6 10:0:0:0:0:2:3:1,1231 > 10:1:1:0:0:0:0:1,23 +< zx0 ip6/0 20 0 6 10:0:0:0:0:2:3:1,1232 > 10:1:1:0:0:0:0:1,50 +< zx0 ip6/0 20 0 6 10:0:0:0:0:2:3:1,1233 > 10:1:1:0:0:0:0:1,79 +< zx0 ip6/0 20 0 6 10:0:0:0:0:2:3:1,1234 > 10:0:0:0:0:2:2:1,3128 +< zx0 ip6/0 20 0 6 10:0:0:0:0:2:3:1,1235 > 1:0:0:0:0:2:2:129,3128 +< zx0 ip6/0 20 0 6 10:0:0:0:0:2:3:1,1236 > 10:0:0:0:0:2:2:1,3128 +< zx0 ip6/0 20 0 6 10:0:0:0:0:2:3:1,1237 > 1:0:0:0:0:2:2:129,3128 +< zx0 ip6/0 20 0 6 10:0:0:0:0:2:3:1,1238 > 10:0:0:0:0:2:2:1,3128 +List of active MAP/Redirect filters: +rdr zx0 inet6 10:1:1::/112 port 80 -> 10::2:2:1,1::2:2:129 port 3128 tcp + +List of active sessions: +RDR 10::2:2:1 3128 <- -> 10:1:1::4 80 [10::2:3:1 1238] +RDR 1::2:2:129 3128 <- -> 10:1:1::4 80 [10::2:3:1 1237] +RDR 10::2:2:1 3128 <- -> 10:1:1::3 80 [10::2:3:1 1236] +RDR 1::2:2:129 3128 <- -> 10:1:1::2 80 [10::2:3:1 1235] +RDR 10::2:2:1 3128 <- -> 10:1:1::1 80 [10::2:3:1 1234] + +Hostmap table: +List of active state sessions: +List of configured pools +List of configured hash tables +List of groups configured (set 0) +List of groups configured (set 1) +Rules configured (set 0, in) +Rules configured (set 0, out) +Rules configured (set 1, in) +Rules configured (set 1, out) +Accounting rules configured (set 0, in) +Accounting rules configured (set 0, out) +Accounting rules configured (set 1, in) +Accounting rules configured (set 1, out) +------------------------------- diff --git a/contrib/ipfilter/test/expected/n8 b/contrib/ipfilter/test/expected/n8 index d3e061da974..a5e938f16ef 100644 --- a/contrib/ipfilter/test/expected/n8 +++ b/contrib/ipfilter/test/expected/n8 @@ -6,4 +6,25 @@ 4500 0054 3fd5 4000 ff01 2fc8 0404 0404 0202 0202 0000 f7de 6220 0001 3f6f 6e80 000b 0d02 0809 0a0b 0c0d 0e0f 1011 1213 1415 1617 1819 1a1b 1c1d 1e1f 2021 2223 2425 2627 2829 2a2b 2c2d 2e2f 3031 3233 3435 3637 +List of active MAP/Redirect filters: +map icmp0 2.2.2.0/24 -> 10.10.10.0/24 + +List of active sessions: +MAP 2.2.2.2 <- -> 10.10.10.1 [4.4.4.4] + +Hostmap table: +2.2.2.2,4.4.4.4 -> 10.10.10.1,0.0.0.0 (use = 1) +List of active state sessions: +List of configured pools +List of configured hash tables +List of groups configured (set 0) +List of groups configured (set 1) +Rules configured (set 0, in) +Rules configured (set 0, out) +Rules configured (set 1, in) +Rules configured (set 1, out) +Accounting rules configured (set 0, in) +Accounting rules configured (set 0, out) +Accounting rules configured (set 1, in) +Accounting rules configured (set 1, out) ------------------------------- diff --git a/contrib/ipfilter/test/expected/n8_6 b/contrib/ipfilter/test/expected/n8_6 new file mode 100644 index 00000000000..4d08efe37e2 --- /dev/null +++ b/contrib/ipfilter/test/expected/n8_6 @@ -0,0 +1,30 @@ +6000 0000 0040 3aff 0010 0010 0010 0000 0000 0000 0000 0001 0004 0004 0004 0000 0000 0000 0000 0004 8000 7724 6220 0000 3f6f 6e80 000b 0d02 0809 0a0b 0c0d 0e0f 1011 1213 1415 1617 1819 1a1b 1c1d 1e1f 2021 2223 2425 2627 2829 2a2b 2c2d 2e2f 3031 3233 3435 3637 + +6000 0000 0040 3aff 0004 0004 0004 0000 0000 0000 0000 0004 0002 0000 0000 0000 0000 0002 0002 0002 8100 764d 6220 0000 3f6f 6e80 000b 0d02 0809 0a0b 0c0d 0e0f 1011 1213 1415 1617 1819 1a1b 1c1d 1e1f 2021 2223 2425 2627 2829 2a2b 2c2d 2e2f 3031 3233 3435 3637 + +6000 0000 0040 3aff 0010 0010 0010 0000 0000 0000 0000 0001 0004 0004 0004 0000 0000 0000 0000 0004 8000 7723 6220 0001 3f6f 6e80 000b 0d02 0809 0a0b 0c0d 0e0f 1011 1213 1415 1617 1819 1a1b 1c1d 1e1f 2021 2223 2425 2627 2829 2a2b 2c2d 2e2f 3031 3233 3435 3637 + +6000 0000 0040 3aff 0004 0004 0004 0000 0000 0000 0000 0004 0002 0000 0000 0000 0000 0002 0002 0002 8100 764c 6220 0001 3f6f 6e80 000b 0d02 0809 0a0b 0c0d 0e0f 1011 1213 1415 1617 1819 1a1b 1c1d 1e1f 2021 2223 2425 2627 2829 2a2b 2c2d 2e2f 3031 3233 3435 3637 + +List of active MAP/Redirect filters: +map icmp0 inet6 2::2:2:0/112 -> 10:10:10::/112 + +List of active sessions: +MAP 2::2:2:2 <- -> 10:10:10::1 [4:4:4::4] + +Hostmap table: +2::2:2:2,4:4:4::4 -> 10:10:10::1,any (use = 1) +List of active state sessions: +List of configured pools +List of configured hash tables +List of groups configured (set 0) +List of groups configured (set 1) +Rules configured (set 0, in) +Rules configured (set 0, out) +Rules configured (set 1, in) +Rules configured (set 1, out) +Accounting rules configured (set 0, in) +Accounting rules configured (set 0, out) +Accounting rules configured (set 1, in) +Accounting rules configured (set 1, out) +------------------------------- diff --git a/contrib/ipfilter/test/expected/n9 b/contrib/ipfilter/test/expected/n9 index 917105f74ed..2c762be6af6 100644 --- a/contrib/ipfilter/test/expected/n9 +++ b/contrib/ipfilter/test/expected/n9 @@ -6,4 +6,24 @@ 4500 0054 3fd5 4000 ff01 2fc8 0404 0404 0202 0202 0000 f7de 6220 0001 3f6f 6e80 000b 0d02 0809 0a0b 0c0d 0e0f 1011 1213 1415 1617 1819 1a1b 1c1d 1e1f 2021 2223 2425 2627 2829 2a2b 2c2d 2e2f 3031 3233 3435 3637 +List of active MAP/Redirect filters: +rdr icmp0 4.4.4.0/24 -> 10.10.10.1/32 ip + +List of active sessions: +RDR 10.10.10.1 <- -> 4.4.4.4 [2.2.2.2] + +Hostmap table: +List of active state sessions: +List of configured pools +List of configured hash tables +List of groups configured (set 0) +List of groups configured (set 1) +Rules configured (set 0, in) +Rules configured (set 0, out) +Rules configured (set 1, in) +Rules configured (set 1, out) +Accounting rules configured (set 0, in) +Accounting rules configured (set 0, out) +Accounting rules configured (set 1, in) +Accounting rules configured (set 1, out) ------------------------------- diff --git a/contrib/ipfilter/test/expected/n9_6 b/contrib/ipfilter/test/expected/n9_6 new file mode 100644 index 00000000000..134d74c9fa4 --- /dev/null +++ b/contrib/ipfilter/test/expected/n9_6 @@ -0,0 +1,29 @@ +6000 0000 0040 3aff 0002 0000 0000 0000 0000 0002 0002 0002 0010 0010 0010 0000 0000 0000 0000 0001 8000 772c 6220 0000 3f6f 6e80 000b 0d02 0809 0a0b 0c0d 0e0f 1011 1213 1415 1617 1819 1a1b 1c1d 1e1f 2021 2223 2425 2627 2829 2a2b 2c2d 2e2f 3031 3233 3435 3637 + +6000 0000 0040 3aff 0004 0004 0004 0000 0000 0000 0000 0004 0002 0000 0000 0000 0000 0002 0002 0002 8100 764d 6220 0000 3f6f 6e80 000b 0d02 0809 0a0b 0c0d 0e0f 1011 1213 1415 1617 1819 1a1b 1c1d 1e1f 2021 2223 2425 2627 2829 2a2b 2c2d 2e2f 3031 3233 3435 3637 + +6000 0000 0040 3aff 0002 0000 0000 0000 0000 0002 0002 0002 0010 0010 0010 0000 0000 0000 0000 0001 8000 772b 6220 0001 3f6f 6e80 000b 0d02 0809 0a0b 0c0d 0e0f 1011 1213 1415 1617 1819 1a1b 1c1d 1e1f 2021 2223 2425 2627 2829 2a2b 2c2d 2e2f 3031 3233 3435 3637 + +6000 0000 0040 3aff 0004 0004 0004 0000 0000 0000 0000 0004 0002 0000 0000 0000 0000 0002 0002 0002 8100 764c 6220 0001 3f6f 6e80 000b 0d02 0809 0a0b 0c0d 0e0f 1011 1213 1415 1617 1819 1a1b 1c1d 1e1f 2021 2223 2425 2627 2829 2a2b 2c2d 2e2f 3031 3233 3435 3637 + +List of active MAP/Redirect filters: +rdr icmp0 inet6 4:4:4::/112 -> 10:10:10::1/128 ip + +List of active sessions: +RDR 10:10:10::1 <- -> 4:4:4::4 [2::2:2:2] + +Hostmap table: +List of active state sessions: +List of configured pools +List of configured hash tables +List of groups configured (set 0) +List of groups configured (set 1) +Rules configured (set 0, in) +Rules configured (set 0, out) +Rules configured (set 1, in) +Rules configured (set 1, out) +Accounting rules configured (set 0, in) +Accounting rules configured (set 0, out) +Accounting rules configured (set 1, in) +Accounting rules configured (set 1, out) +------------------------------- diff --git a/contrib/ipfilter/test/expected/ni10 b/contrib/ipfilter/test/expected/ni10 index 3ee63fb8ddd..050fb40725e 100644 --- a/contrib/ipfilter/test/expected/ni10 +++ b/contrib/ipfilter/test/expected/ni10 @@ -4,6 +4,5 @@ 4500 0058 0001 0000 ff01 af98 0202 0202 0404 0404 0303 0937 0000 0000 4500 003c 4706 4000 ff06 28aa 0404 0404 0202 0202 5000 0050 0000 0001 0000 0000 a002 16d0 d8e2 0000 0204 05b4 0402 080a 0047 fbb0 0000 0000 0103 0300 -4500 0038 809a 0000 ff01 2b1b 0303 0303 0505 0505 0303 acab 0000 0000 4500 003c 4706 4000 ff06 28ab 0404 0404 0202 0201 5000 0050 0000 0001 - +0 ------------------------------- diff --git a/contrib/ipfilter/test/expected/ni11 b/contrib/ipfilter/test/expected/ni11 index 88d6406e6ee..6ed8ecc8063 100644 --- a/contrib/ipfilter/test/expected/ni11 +++ b/contrib/ipfilter/test/expected/ni11 @@ -1,9 +1,8 @@ 4500 003c 4706 4000 ff06 2aac 0404 0404 0101 0101 5000 9d58 0000 0001 0000 0000 a002 16d0 3ddc 0000 0204 05b4 0402 080a 0047 fbb0 0000 0000 0103 0300 -4500 0038 0000 0000 ff01 a7b9 0a02 0202 0404 0404 0303 a7fb 0000 0000 4500 003c 4706 4000 ff06 20aa 0404 0404 0a02 0202 5000 0500 0000 0001 +4500 0038 0000 0000 ff01 a7b9 0a02 0202 0404 0404 0303 a7fc 0000 0000 4500 003c 4706 4000 ff06 20aa 0404 0404 0a02 0202 5000 0500 0000 0001 4500 0058 0001 0000 ff01 a798 0a02 0202 0404 0404 0303 1137 0000 0000 4500 003c 4706 4000 ff06 20aa 0404 0404 0a02 0202 5000 0500 0000 0001 0000 0000 a002 16d0 cc32 0000 0204 05b4 0402 080a 0047 fbb0 0000 0000 0103 0300 -4500 0038 809a 0000 ff01 2b1b 0303 0303 0505 0505 0303 0fa3 0000 0000 4500 003c 4706 4000 ff06 2aab 0404 0404 0101 0102 5000 9d58 0000 0001 - +0 ------------------------------- diff --git a/contrib/ipfilter/test/expected/ni12 b/contrib/ipfilter/test/expected/ni12 index 7d24a493fd3..590ec23087e 100644 --- a/contrib/ipfilter/test/expected/ni12 +++ b/contrib/ipfilter/test/expected/ni12 @@ -1,9 +1,8 @@ 4500 003c 4706 4000 ff06 2aac 0404 0404 0101 0101 5000 9c40 0000 0001 0000 0000 a002 16d0 3ef4 0000 0204 05b4 0402 080a 0047 fbb0 0000 0000 0103 0300 -4500 0038 809a 0000 ff01 2d1d 0303 0303 0404 0404 0303 0fa3 0000 0000 4500 003c 4706 4000 ff06 2aac 0404 0404 0101 0101 5000 9d58 0000 0001 +4500 0038 0000 0000 ff01 a7b9 0a02 0202 0404 0404 0303 a7fc 0000 0000 4500 003c 4706 4000 ff06 20aa 0404 0404 0a02 0202 5000 0500 0000 0001 -4500 0058 809a 0000 ff01 2cfd 0303 0303 0404 0404 0303 0735 0000 0000 4500 003c 4706 4000 ff06 2aac 0404 0404 0101 0101 5000 9d58 0000 0001 0000 0000 a002 16d0 3ddc 0000 0204 05b4 0402 080a 0047 fbb0 0000 0000 0103 0300 - -4500 0038 809a 0000 ff01 2b1b 0303 0303 0505 0505 0303 0fa3 0000 0000 4500 003c 4706 4000 ff06 2aab 0404 0404 0101 0102 5000 9d58 0000 0001 +4500 0058 0001 0000 ff01 a798 0a02 0202 0404 0404 0303 1137 0000 0000 4500 003c 4706 4000 ff06 20aa 0404 0404 0a02 0202 5000 0500 0000 0001 0000 0000 a002 16d0 cc32 0000 0204 05b4 0402 080a 0047 fbb0 0000 0000 0103 0300 +0 ------------------------------- diff --git a/contrib/ipfilter/test/expected/ni17 b/contrib/ipfilter/test/expected/ni17 new file mode 100644 index 00000000000..74eb4ddc5f9 --- /dev/null +++ b/contrib/ipfilter/test/expected/ni17 @@ -0,0 +1,7 @@ +< le0 ip #0 40(20) 6 10.2.2.5,2000 > 10.1.1.252,3128 +< le0 ip #0 40(20) 6 10.2.2.6,2000 > 10.1.2.252,3128 +< le0 ip #0 40(20) 6 10.2.2.7,2000 > 10.1.3.252,3128 +< le0 ip #0 40(20) 6 10.2.2.7,2001 > 10.1.3.252,3128 +< le0 ip #0 40(20) 6 10.2.2.8,2000 > 10.1.1.253,3128 +< le0 ip #0 40(20) 6 10.2.2.9,2000 > 10.1.2.253,3128 +------------------------------- diff --git a/contrib/ipfilter/test/expected/ni18 b/contrib/ipfilter/test/expected/ni18 new file mode 100644 index 00000000000..defc59cca97 --- /dev/null +++ b/contrib/ipfilter/test/expected/ni18 @@ -0,0 +1,5 @@ +< hme0 ip #0 40(20) 6 2.2.2.2,3000 > 1.1.1.1,80 +< hme0 ip #0 40(20) 6 2.2.2.2,3000 > 192.168.1.1,80 +> hme1 ip #0 40(20) 6 203.1.1.1,10000 > 4.5.6.7,80 +> hme1 ip #0 40(20) 6 10.1.1.2,5050 > 4.5.6.7,80 +------------------------------- diff --git a/contrib/ipfilter/test/expected/ni19 b/contrib/ipfilter/test/expected/ni19 index fa40771a0f1..e55c75dc383 100644 --- a/contrib/ipfilter/test/expected/ni19 +++ b/contrib/ipfilter/test/expected/ni19 @@ -34,16 +34,10 @@ 4500 0034 118c 4000 4006 ec87 0a01 0104 c0a8 7103 03ff 03f0 91d4 c8a7 66e5 b811 8011 05b4 d54e 0000 0101 080a 0039 dd6d 0000 0000 -4500 0028 e404 4000 4006 1a1b c0a8 7103 0a01 0104 03f1 0202 6523 90eb 915a a5cb 5010 8328 bcd3 0000 - -4500 0034 e405 4000 4006 1a0e c0a8 7103 0a01 0104 03f0 03ff 66e5 b811 91d4 c8a8 8010 8328 57d7 0000 0101 080a 0000 0004 0039 dd6c - -4500 0028 e40a 4000 4006 1a15 c0a8 7103 0a01 0104 03f1 0202 6523 90eb 915a a5cb 5011 832c bcce 0000 - -4500 0034 e40b 4000 4006 1a08 c0a8 7103 0a01 0104 03f0 03ff 66e5 b811 91d4 c8a8 8011 832c 57d2 0000 0101 080a 0000 0004 0039 dd6c - -4500 0028 0004 4000 4006 fe1b 0a01 0104 c0a8 7103 0202 03f1 915a a5cb 6523 90ec 5010 05b4 3a47 0000 - -4500 0034 118e 4000 4006 ec85 0a01 0104 c0a8 7103 03ff 03f0 91d4 c8a8 66e5 b812 8010 05b4 d548 0000 0101 080a 0039 dd6e 0000 0004 - +0 +0 +0 +0 +0 +0 ------------------------------- diff --git a/contrib/ipfilter/test/expected/ni2 b/contrib/ipfilter/test/expected/ni2 index e2a7eb89ffa..69a52724f0c 100644 --- a/contrib/ipfilter/test/expected/ni2 +++ b/contrib/ipfilter/test/expected/ni2 @@ -14,6 +14,6 @@ 4500 05dc e483 4000 7e06 44bb c0a8 0133 0a01 0201 0077 05f6 fbdf 1a75 a664 248c 5010 2232 9f2d 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 1111 2222 3333 0000 0000 0000 0000 0000 0000 1111 2222 3333 0000 0000 0000 0000 0000 0000 1111 2222 3333 0000 0000 0000 0000 0000 0000 1111 2222 3333 0000 0000 0000 0000 0000 0000 1111 2222 3333 0000 0000 0000 0000 0000 0000 1111 2222 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 1111 2222 3333 0000 0000 0000 0000 0000 0000 1111 2222 3333 0000 0000 0000 0000 0000 0000 1111 2222 3333 0000 0000 0000 0000 0000 0000 1111 2222 3333 0000 0000 0000 0000 0000 0000 1111 2222 3333 0000 0000 0000 0000 0000 0000 1111 2222 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 1111 2222 3333 0000 0000 0000 0000 0000 0000 1111 2222 3333 0000 0000 0000 0000 0000 0000 1111 2222 3333 0000 0000 0000 0000 0000 0000 1111 2222 3333 0000 0000 0000 0000 0000 0000 1111 2222 3333 0000 0000 0000 0000 0000 0000 1111 2222 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 1111 2222 3333 0000 0000 0000 0000 0000 0000 1111 2222 3333 0000 0000 0000 0000 0000 0000 1111 2222 3333 0000 0000 0000 0000 0000 0000 1111 2222 3333 0000 0000 0000 0000 0000 0000 1111 2222 3333 0000 0000 0000 0000 0000 0000 1111 2222 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 1111 2222 3333 0000 0000 0000 0000 0000 0000 1111 2222 3333 0000 0000 0000 0000 0000 0000 1111 2222 3333 0000 0000 0000 0000 0000 0000 1111 2222 3333 0000 0000 0000 0000 0000 0000 1111 2222 3333 0000 0000 0000 0000 0000 0000 1111 2222 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 1111 2222 3333 0000 0000 0000 0000 0000 0000 1111 2222 3333 0000 0000 0000 0000 0000 0000 1111 2222 3333 0000 0000 0000 0000 0000 0000 1111 2222 3333 0000 0000 0000 0000 0000 0000 1111 2222 3333 0000 0000 0000 0000 0000 0000 1111 2222 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 1111 2222 3331 0000 0000 0000 0000 0000 0000 1111 2222 3333 0000 0000 0000 0000 0000 0000 1111 2222 3333 0000 0000 0000 0000 0000 0000 1111 2222 3333 0000 0000 0000 0000 0000 0000 1111 2222 3333 0000 0000 0000 0000 0000 0000 1111 2222 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 1111 2222 3333 0000 0000 0000 0000 0000 0000 1111 2222 3333 0000 0000 0000 0000 0000 0000 1111 2222 3333 0000 0000 0000 0000 0000 0000 1111 2222 3333 0000 0000 0000 0000 0000 0000 1111 2222 3333 0000 0000 0000 0000 0000 0000 1111 2222 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 1111 2222 3333 0000 0000 0000 0000 0000 0000 1111 2222 3333 0000 0000 0000 0000 0000 0000 1111 2222 3333 0000 0000 0000 0000 0000 0000 1111 2222 3333 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 -4500 0038 0004 4000 4001 76e4 0101 0101 c0a8 0133 0304 9dea 0000 05a0 4500 05dc e483 4000 7e06 4ebb c0a8 0133 0101 0101 0077 9c40 fbdf 1a75 +4500 0038 0004 4000 4001 76e4 0101 0101 c0a8 0133 0304 444f 0000 05a0 4500 05dc e483 4000 7e06 4ebb c0a8 0133 0101 0101 0077 9c40 fbdf 1a75 ------------------------------- diff --git a/contrib/ipfilter/test/expected/ni20 b/contrib/ipfilter/test/expected/ni20 index 6001a5af9eb..913ef0b1e46 100644 --- a/contrib/ipfilter/test/expected/ni20 +++ b/contrib/ipfilter/test/expected/ni20 @@ -34,16 +34,36 @@ 4500 0034 118c 4000 4006 ec87 0a01 0104 c0a8 7103 03ff 03f0 91d4 c8a7 66e5 b811 8011 05b4 d54e 0000 0101 080a 0039 dd6d 0000 0000 -4500 0028 e404 4000 4006 f372 c0a8 7103 c0a8 7104 03f1 0202 6523 90eb 915a a5cb 5010 8328 962b 0000 +0 +0 +0 +0 +0 +0 +List of active MAP/Redirect filters: +rdr bge0 10.1.1.4/32 port 514 -> 192.168.113.4/32 port 514 tcp proxy rcmd -4500 0034 e405 4000 4006 f365 c0a8 7103 c0a8 7104 03f0 03ff 66e5 b811 91d4 c8a8 8010 8328 312f 0000 0101 080a 0000 0004 0039 dd6c - -4500 0028 e40a 4000 4006 f36c c0a8 7103 c0a8 7104 03f1 0202 6523 90eb 915a a5cb 5011 832c 9626 0000 - -4500 0034 e40b 4000 4006 f35f c0a8 7103 c0a8 7104 03f0 03ff 66e5 b811 91d4 c8a8 8011 832c 312a 0000 0101 080a 0000 0004 0039 dd6c - -4500 0028 0004 4000 4006 d773 c0a8 7104 c0a8 7103 0202 03f1 915a a5cb 6523 90ec 5010 05b4 139f 0000 - -4500 0034 118e 4000 4006 c5dd c0a8 7104 c0a8 7103 03ff 03f0 91d4 c8a8 66e5 b812 8010 05b4 aea0 0000 0101 080a 0039 dd6e 0000 0004 +List of active sessions: +MAP 192.168.113.4 1023 <- -> 10.1.1.4 1023 [192.168.113.3 1008] +RDR 192.168.113.4 514 <- -> 10.1.1.4 514 [192.168.113.3 1009] + proxy active +Hostmap table: +192.168.113.4,192.168.113.3 -> 10.1.1.4,0.0.0.0 (use = 1) +List of active state sessions: +List of configured pools +List of configured hash tables +List of groups configured (set 0) +List of groups configured (set 1) +Rules configured (set 0, in) +5 block in all +1 pass in quick on bge0 proto tcp from any to any port = 514 flags S/FSRPAU keep state +Rules configured (set 0, out) +2 block out all +Rules configured (set 1, in) +Rules configured (set 1, out) +Accounting rules configured (set 0, in) +Accounting rules configured (set 0, out) +Accounting rules configured (set 1, in) +Accounting rules configured (set 1, out) ------------------------------- diff --git a/contrib/ipfilter/test/expected/ni21 b/contrib/ipfilter/test/expected/ni21 index 349ae2391cc..53e64a5a1f9 100644 --- a/contrib/ipfilter/test/expected/ni21 +++ b/contrib/ipfilter/test/expected/ni21 @@ -1,4 +1,6 @@ -ip #0 20(20) 0 4.4.4.4 > 3.3.3.3 -ip #0 20(20) 0 3.3.3.3 > 2.2.2.2 -ip #0 20(20) 0 4.4.4.4 > 3.3.3.3 +> eri0 ip #0 20(20) 0 4.4.4.4 > 3.3.3.3 +0 +< lan0 ip #0 20(20) 0 3.3.3.3 > 2.2.2.2 +> eri0 ip #0 20(20) 0 4.4.4.4 > 3.3.3.3 +0 ------------------------------- diff --git a/contrib/ipfilter/test/expected/ni23 b/contrib/ipfilter/test/expected/ni23 index 24909b07f05..586373c070c 100644 --- a/contrib/ipfilter/test/expected/ni23 +++ b/contrib/ipfilter/test/expected/ni23 @@ -1,8 +1,9 @@ -ip #0 28(20) 17 4.4.4.4,6700 > 2.2.2.2,4500 -ip #0 28(20) 17 2.2.2.2,4500 > 3.3.3.1,6700 -ip #0 28(20) 17 1.1.2.3,4500 > 3.3.3.1,6700 +> ppp0 ip #0 28(20) 17 4.4.4.4,6700 > 2.2.2.2,4500 +0 +< hme0 ip #0 28(20) 17 2.2.2.2,4500 > 3.3.3.1,6700 +> bge0 ip #0 28(20) 17 1.1.2.3,4500 > 3.3.3.1,6700 List of active MAP/Redirect filters: -rdr le0,bge0 1.1.0.0/16 -> 2.2.2.2 ip +rdr le0,bge0 1.1.0.0/16 -> 2.2.2.2/32 ip map hme0,ppp0 3.3.3.0/24 -> 4.4.4.4/32 List of active sessions: @@ -10,20 +11,27 @@ MAP 3.3.3.1 6700 <- -> 4.4.4.4 6700 [2.2.2.2 4500] RDR 2.2.2.2 4500 <- -> 1.1.2.3 4500 [3.3.3.1 6700] Hostmap table: -3.3.3.1,2.2.2.2 -> 4.4.4.4 (use = 1 hv = 0) +3.3.3.1,2.2.2.2 -> 4.4.4.4,0.0.0.0 (use = 1) List of active state sessions: -3.3.3.1 -> 2.2.2.2 pass 0x40008402 pr 17 state 0/0 - tag 0 ttl 24 6700 -> 4500 - forward: pkts in 1 bytes in 28 pkts out 1 bytes out 28 - backward: pkts in 1 bytes in 28 pkts out 1 bytes out 28 - pass in keep state IPv4 - pkt_flags & 0(0) = 0, pkt_options & ffffffff = 0, ffffffff = 0 - pkt_security & ffff = 0, pkt_auth & ffff = 0 - is_flx 0x8001 0x8001 0x8001 0x1 +4:udp src:3.3.3.1,6700 dst:2.2.2.2,4500 24 + FWD: IN pkts 1 bytes 28 OUT pkts 1 bytes 28 + REV: IN pkts 1 bytes 28 OUT pkts 1 bytes 28 + tag 0 pass 0x2008402 = pass in keep state interfaces: in X[le0],X[hme0] out X[ppp0],X[bge0] Sync status: not synchronized List of configured pools List of configured hash tables List of groups configured (set 0) List of groups configured (set 1) +Rules configured (set 0, in) +1 block in all +1 pass in on le0,hme0 to ppp0:3.3.3.254 out-via ppp0,bge0 inet proto udp from any to any keep state +Rules configured (set 0, out) +0 block out all +Rules configured (set 1, in) +Rules configured (set 1, out) +Accounting rules configured (set 0, in) +Accounting rules configured (set 0, out) +Accounting rules configured (set 1, in) +Accounting rules configured (set 1, out) ------------------------------- diff --git a/contrib/ipfilter/test/expected/ni4 b/contrib/ipfilter/test/expected/ni4 index c9f7504d7ac..627aa1909da 100644 --- a/contrib/ipfilter/test/expected/ni4 +++ b/contrib/ipfilter/test/expected/ni4 @@ -1,6 +1,6 @@ 4500 003c 0000 4000 ff06 67a8 0606 0606 0404 0404 9c40 0050 0000 0001 0000 0000 a002 16d0 849a 0000 0204 05b4 0402 080a 0047 fbb0 0000 0000 0103 0300 -4500 0038 809a 0000 ff01 3121 0303 0303 0202 0202 0303 acab 0000 0000 4500 003c 4706 4000 ff06 28aa 0202 0202 0404 0404 5000 0050 0000 0001 +4500 0038 809a 0000 ff01 3121 0303 0303 0202 0202 0303 acac 0000 0000 4500 003c 4706 4000 ff06 28aa 0202 0202 0404 0404 5000 0050 0000 0001 4500 0058 809a 0000 ff01 3101 0303 0303 0202 0202 0303 0937 0000 0000 4500 003c 4706 4000 ff06 28aa 0202 0202 0404 0404 5000 0050 0000 0001 0000 0000 a002 16d0 d8e2 0000 0204 05b4 0402 080a 0047 fbb0 0000 0000 0103 0300 diff --git a/contrib/ipfilter/test/expected/ni5 b/contrib/ipfilter/test/expected/ni5 index e713cf28510..14d983759f6 100644 --- a/contrib/ipfilter/test/expected/ni5 +++ b/contrib/ipfilter/test/expected/ni5 @@ -4,6 +4,7 @@ 4500 0028 0001 4000 ff06 02ff 0101 0101 96cb e002 8032 0015 bd6b c9c9 3786 76c5 5010 269c 5aa0 0000 +ipf_p_ftp_servert_valid:i(0) < 5 4500 006f ffde 4000 ef06 5330 96cb e002 c0a8 0103 0015 8032 3786 76c5 bd6b c9c9 5018 269c 967e 0000 3232 302d 636f 6f6d 6273 2e61 6e75 2e65 6475 2e61 7520 4e63 4654 5064 2053 6572 7665 7220 2866 7265 6520 6564 7563 6174 696f 6e61 6c20 6c69 6365 6e73 6529 2072 6561 6479 2e0d 0a 4500 0028 0002 4000 ff06 02fe 0101 0101 96cb e002 8032 0015 bd6b c9c9 3786 770c 5010 269c 5a59 0000 @@ -22,6 +23,7 @@ 4500 0036 0006 4000 ff06 02ec 0101 0101 96cb e002 8032 0015 bd6b c9d9 3786 77ef 5018 269c 373f 0000 5041 5353 2061 7661 6c6f 6e40 0d0a +ipf_p_ftp_servert_valid:i(0) < 5 4500 005f ffe2 4000 ef06 533c 96cb e002 c0a8 0103 0015 8032 3786 77ef bd6b c9e7 5018 269c 895e 0000 3233 302d 596f 7520 6172 6520 7573 6572 2023 3420 6f66 2035 3020 7369 6d75 6c74 616e 656f 7573 2075 7365 7273 2061 6c6c 6f77 6564 2e0d 0a 4500 0028 0007 4000 ff06 02f9 0101 0101 96cb e002 8032 0015 bd6b c9e7 3786 7826 5010 269c 5921 0000 @@ -72,32 +74,23 @@ 4500 0028 ffec 4000 ef06 5369 96cb e002 c0a8 0103 0014 8034 d9f8 11d4 0000 0000 5010 2238 e90d 0000 -4500 0063 ffed 4000 ef06 532d 96cb e002 c0a8 0103 0014 8033 d9f8 11d5 bd78 5c13 5018 269c a315 0000 636f 6f6d 6273 7061 7065 7273 0d0a 6465 7074 730d 0a66 6f75 6e64 2d66 696c 6573 0d0a 696e 636f 6d69 6e67 0d0a 6e6c 632d 7465 7374 0d0a 7075 620d 0a - -4500 0028 0014 4000 ff06 02ec 0101 0101 96cb e002 8033 0014 bd78 5c13 d9f8 1210 5010 6348 4de0 0000 - -4500 0028 ffee 4000 ef06 5367 96cb e002 c0a8 0103 0014 8033 d9f8 1210 bd78 5c13 5011 269c cae1 0000 - -4500 0028 10dd 4000 ff06 3279 c0a8 0103 96cb e002 8033 0014 bd78 5c13 d9f8 1211 5010 6348 8e35 0000 - -4500 0028 10dd 4000 ff06 3279 c0a8 0103 96cb e002 8033 0014 bd78 5c13 d9f8 1211 5011 6348 8e34 0000 - -4500 0028 ffef 4000 ef06 5366 96cb e002 c0a8 0103 0014 8033 d9f8 1211 bd78 5c14 5010 269c cae0 0000 - +0 +0 +0 +0 +0 +0 4500 0040 fff0 4000 ef06 534d 96cb e002 c0a8 0103 0015 8032 3786 7903 bd6b ca3f 5018 269c 7c80 0000 3232 3620 4c69 7374 696e 6720 636f 6d70 6c65 7465 642e 0d0a -4500 0028 0015 4000 ff06 02eb 0101 0101 96cb e002 8032 0015 bd6b ca2f 3786 791b 5010 269c 57e4 0000 +4500 0028 0014 4000 ff06 02ec 0101 0101 96cb e002 8032 0015 bd6b ca2f 3786 791b 5010 269c 57e4 0000 -4500 002e 0016 4000 ff06 02e4 0101 0101 96cb e002 8032 0015 bd6b ca2f 3786 791b 5018 269c b022 0000 5155 4954 0d0a +4500 002e 0015 4000 ff06 02e5 0101 0101 96cb e002 8032 0015 bd6b ca2f 3786 791b 5018 269c b022 0000 5155 4954 0d0a 4500 0036 fff2 4000 ef06 5355 96cb e002 c0a8 0103 0015 8032 3786 791b bd6b ca45 5018 269c a936 0000 3232 3120 476f 6f64 6279 652e 0d0a -4500 0028 0017 4000 ff06 02e9 0101 0101 96cb e002 8032 0015 bd6b ca35 3786 7929 5011 269c 57cf 0000 - -4500 0028 fff3 4000 ef06 5362 96cb e002 c0a8 0103 0015 8032 3786 7929 bd6b ca45 5011 269c 9815 0000 - -4500 0028 10e3 4000 ff06 3273 c0a8 0103 96cb e002 8032 0015 bd6b ca3d 3786 792a 5010 269c 981d 0000 - -4500 0028 fff4 4000 ef06 5361 96cb e002 c0a8 0103 0015 8032 3786 792a bd6b ca46 5010 269c 9814 0000 +4500 0028 0016 4000 ff06 02ea 0101 0101 96cb e002 8032 0015 bd6b ca35 3786 7929 5011 269c 57cf 0000 +0 +0 +0 ------------------------------- diff --git a/contrib/ipfilter/test/expected/ni6 b/contrib/ipfilter/test/expected/ni6 index 0da034a781b..e70412b7f47 100644 --- a/contrib/ipfilter/test/expected/ni6 +++ b/contrib/ipfilter/test/expected/ni6 @@ -1,17 +1,63 @@ -4500 0054 cd8a 4000 ff11 1fbb c0a8 0601 c0a8 0701 8075 006f 0040 d26e 3e1d d249 0000 0000 0000 0002 0001 86a0 0000 0002 0000 0003 0000 0000 0000 0000 0000 0000 0000 0000 0001 86a3 0000 0003 0000 0011 0000 0000 +< nf0 ip #52618 84(20) 17 192.168.6.1,32885 > 192.168.7.1,111 +> qfe0 ip #0 84(20) 17 192.168.7.2,32885 > 192.168.7.1,111 +< qfe0 ip #52611 56(20) 17 192.168.7.1,111 > 192.168.6.1,32885 +> nf0 ip #1 56(20) 17 192.168.6.2,111 > 192.168.6.1,32885 +< nf0 ip #54694 68(20) 17 192.168.6.1,32991 > 192.168.7.1,2049 +> qfe0 ip #2 68(20) 17 192.168.7.2,32991 > 192.168.7.1,2049 +< qfe0 ip #0 52(20) 17 192.168.7.1,2049 > 192.168.6.1,32991 +> nf0 ip #3 52(20) 17 192.168.6.2,2049 > 192.168.6.1,32991 +List of active MAP/Redirect filters: +rdr nf0 192.168.6.2/32 port 111 -> 192.168.7.1/32 port 111 udp proxy rpcbu +rdr nf0 192.168.6.2/32 port 111 -> 192.168.7.1/32 port 111 tcp proxy rpcbt +map qfe0 192.168.6.0/24 -> 192.168.7.2/32 -4500 0054 0000 4000 ff11 ec44 c0a8 0702 c0a8 0701 8075 006f 0040 d16d 3e1d d249 0000 0000 0000 0002 0001 86a0 0000 0002 0000 0003 0000 0000 0000 0000 0000 0000 0000 0000 0001 86a3 0000 0003 0000 0011 0000 0000 - -4500 0038 cd83 4000 ff11 1fde c0a8 0701 c0a8 0601 006f 8075 0024 d805 3e1d d249 0000 0001 0000 0000 0000 0000 0000 0000 0000 0000 0000 0801 - -4500 0038 0001 4000 ff11 ee5f c0a8 0602 c0a8 0601 006f 8075 0024 d904 3e1d d249 0000 0001 0000 0000 0000 0000 0000 0000 0000 0000 0000 0801 - -4500 0044 d5a6 4000 ff11 17af c0a8 0601 c0a8 0701 80df 0801 0030 03f1 3e10 1fb1 0000 0000 0000 0002 0001 86a3 0000 0002 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 - -4500 0044 0002 4000 ff11 ec52 c0a8 0702 c0a8 0701 80df 0801 0030 02f0 3e10 1fb1 0000 0000 0000 0002 0001 86a3 0000 0002 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 - -4500 0034 0000 4000 fe11 ee65 c0a8 0701 c0a8 0601 0801 80df 0020 8ab8 3e10 1fb1 0000 0001 0000 0000 0000 0000 0000 0000 0000 0000 - -4500 0034 0003 4000 fe11 ef61 c0a8 0602 c0a8 0601 0801 80df 0020 0000 3e10 1fb1 0000 0001 0000 0000 0000 0000 0000 0000 0000 0000 +List of active sessions: +MAP 192.168.6.1 32991 <- -> 192.168.7.2 32991 [192.168.7.1 2049] +RDR 192.168.7.1 2049 <- -> 192.168.6.2 2049 [192.168.6.1 32991] +RDR CLONE 192.168.7.1 2049 <- -> 192.168.6.2 2049 [192.168.6.1 0] +MAP 192.168.6.1 32885 <- -> 192.168.7.2 32885 [192.168.7.1 111] +RDR 192.168.7.1 111 <- -> 192.168.6.2 111 [192.168.6.1 32885] + proxy active +Hostmap table: +192.168.6.1,192.168.7.1 -> 192.168.7.2,0.0.0.0 (use = 2) +List of active state sessions: +4:udp src:192.168.6.1,32991 dst:192.168.7.1,2049 24 + FWD: IN pkts 2 bytes 96 OUT pkts 1 bytes 68 + REV: IN pkts 1 bytes 52 OUT pkts 1 bytes 52 + tag 0 pass 0x502 = pass in quick keep state + interfaces: in X[nf0],X[qfe0] out X[qfe0],X[nf0] + Sync status: not synchronized +4:udp src:192.168.6.1,* dst:192.168.7.1,2049 240 CLONE + FWD: IN pkts 1 bytes 28 OUT pkts 0 bytes 0 + REV: IN pkts 0 bytes 0 OUT pkts 0 bytes 0 + tag 0 pass 0x502 = pass in quick keep state + interfaces: in X[nf0],X[] out X[],X[] + Sync status: not synchronized +4:udp src:192.168.6.1,32885 dst:192.168.7.1,111 24 + FWD: IN pkts 1 bytes 84 OUT pkts 1 bytes 84 + REV: IN pkts 1 bytes 56 OUT pkts 1 bytes 56 + tag 0 pass 0x2008502 = pass in quick keep state + interfaces: in X[nf0],X[qfe0] out X[qfe0],X[nf0] + Sync status: not synchronized +List of configured pools +List of configured hash tables +List of groups configured (set 0) +List of groups configured (set 1) +Rules configured (set 0, in) +0 pass in quick on nf0 proto tcp from any to any port = 111 flags S/FSRPAU keep state +1 pass in quick on nf0 proto udp from any to any port = 111 keep state +0 block return-rst in log quick on nf0 proto tcp from any to any +0 block in log quick on nf0 inet from 192.168.7.0/24 to any +0 block return-rst in log quick on qfe0 proto tcp from any to any +0 block in log quick on qfe0 inet from 192.168.6.0/24 to any +Rules configured (set 0, out) +0 block out log quick on qfe0 inet from 192.168.7.0/24 to any +0 block out log quick on nf0 inet from 192.168.6.0/24 to any +Rules configured (set 1, in) +Rules configured (set 1, out) +Accounting rules configured (set 0, in) +Accounting rules configured (set 0, out) +Accounting rules configured (set 1, in) +Accounting rules configured (set 1, out) ------------------------------- diff --git a/contrib/ipfilter/test/expected/ni8 b/contrib/ipfilter/test/expected/ni8 index 689ccaa87ea..e0d518270a1 100644 --- a/contrib/ipfilter/test/expected/ni8 +++ b/contrib/ipfilter/test/expected/ni8 @@ -1,6 +1,6 @@ 4500 003c 4706 4000 ff06 2aac 0404 0404 0101 0101 5000 9d58 0000 0001 0000 0000 a002 16d0 3ddc 0000 0204 05b4 0402 080a 0047 fbb0 0000 0000 0103 0300 -4500 0038 0000 0000 ff01 a7b9 0a02 0202 0404 0404 0303 a7fb 0000 0000 4500 003c 4706 4000 ff06 20aa 0404 0404 0a02 0202 5000 0500 0000 0001 +4500 0038 0000 0000 ff01 a7b9 0a02 0202 0404 0404 0303 a7fc 0000 0000 4500 003c 4706 4000 ff06 20aa 0404 0404 0a02 0202 5000 0500 0000 0001 4500 0058 0001 0000 ff01 a798 0a02 0202 0404 0404 0303 1137 0000 0000 4500 003c 4706 4000 ff06 20aa 0404 0404 0a02 0202 5000 0500 0000 0001 0000 0000 a002 16d0 cc32 0000 0204 05b4 0402 080a 0047 fbb0 0000 0000 0103 0300 diff --git a/contrib/ipfilter/test/expected/p1 b/contrib/ipfilter/test/expected/p1 index 9f02804439e..58dc6813f32 100644 --- a/contrib/ipfilter/test/expected/p1 +++ b/contrib/ipfilter/test/expected/p1 @@ -13,9 +13,18 @@ List of active sessions: Hostmap table: List of active state sessions: List of configured pools -table role = ipf type = tree number = 100 - { 1.1.1.1/32; ! 2.2.0.0/16; 2.2.2.0/24; }; +table role=ipf type=tree number=100 + { 1.1.1.1/32; ! 2.2.0.0/16; 2.2.2.0/24; ef00::5/128; }; List of configured hash tables List of groups configured (set 0) List of groups configured (set 1) +Rules configured (set 0, in) +2 pass in from pool/100 to any +Rules configured (set 0, out) +Rules configured (set 1, in) +Rules configured (set 1, out) +Accounting rules configured (set 0, in) +Accounting rules configured (set 0, out) +Accounting rules configured (set 1, in) +Accounting rules configured (set 1, out) ------------------------------- diff --git a/contrib/ipfilter/test/expected/p10 b/contrib/ipfilter/test/expected/p10 new file mode 100644 index 00000000000..9f09502d249 --- /dev/null +++ b/contrib/ipfilter/test/expected/p10 @@ -0,0 +1,40 @@ +< bge0 ip #0 40(20) 6 5.5.5.5,10000 > 1.1.1.2,80 +< bge0 ip #0 40(20) 6 5.5.5.6,10000 > 1.1.1.9,80 +< bge0 ip #0 40(20) 6 5.5.5.7,10000 > 1.1.1.2,80 +< bge0 ip #0 40(20) 6 5.5.5.8,10000 > 1.1.1.9,80 +< bge0 ip #0 40(20) 6 5.5.5.9,10000 > 1.1.1.4,80 +< bge0 ip #0 40(20) 6 5.5.6.5,10000 > 1.1.1.4,80 +< bge0 ip #0 40(20) 6 5.5.6.6,10000 > 1.1.1.9,80 +< bge0 ip #0 40(20) 6 5.5.6.7,10000 > 1.1.1.5,80 +< bge0 ip #0 40(20) 6 5.5.6.8,10000 > 1.1.1.5,80 +< bge0 ip #0 40(20) 6 5.5.6.9,10000 > 1.1.1.9,80 +List of active MAP/Redirect filters: +rewrite in on bge0 proto tcp from 0/0 to 0/0 port = 80 -> src 0/0 dst dstlist/servers; + +List of active sessions: +RWR-RDR 5.5.6.9 10000 9.9.9.9 80 <- -> 5.5.6.9 10000 1.1.1.9 80 +RWR-RDR 5.5.6.8 10000 9.9.9.9 80 <- -> 5.5.6.8 10000 1.1.1.5 80 +RWR-RDR 5.5.6.7 10000 9.9.9.9 80 <- -> 5.5.6.7 10000 1.1.1.5 80 +RWR-RDR 5.5.6.6 10000 9.9.9.9 80 <- -> 5.5.6.6 10000 1.1.1.9 80 +RWR-RDR 5.5.6.5 10000 9.9.9.9 80 <- -> 5.5.6.5 10000 1.1.1.4 80 +RWR-RDR 5.5.5.9 10000 9.9.9.9 80 <- -> 5.5.5.9 10000 1.1.1.4 80 +RWR-RDR 5.5.5.8 10000 9.9.9.9 80 <- -> 5.5.5.8 10000 1.1.1.9 80 +RWR-RDR 5.5.5.7 10000 9.9.9.9 80 <- -> 5.5.5.7 10000 1.1.1.2 80 +RWR-RDR 5.5.5.6 10000 9.9.9.9 80 <- -> 5.5.5.6 10000 1.1.1.9 80 +RWR-RDR 5.5.5.5 10000 9.9.9.9 80 <- -> 5.5.5.5 10000 1.1.1.2 80 + +Hostmap table: +List of active state sessions: +List of configured pools +List of configured hash tables +List of groups configured (set 0) +List of groups configured (set 1) +Rules configured (set 0, in) +Rules configured (set 0, out) +Rules configured (set 1, in) +Rules configured (set 1, out) +Accounting rules configured (set 0, in) +Accounting rules configured (set 0, out) +Accounting rules configured (set 1, in) +Accounting rules configured (set 1, out) +------------------------------- diff --git a/contrib/ipfilter/test/expected/p11 b/contrib/ipfilter/test/expected/p11 new file mode 100644 index 00000000000..e907fbb16ab --- /dev/null +++ b/contrib/ipfilter/test/expected/p11 @@ -0,0 +1,40 @@ +< bge0 ip #0 40(20) 6 5.5.5.5,10000 > 1.1.1.5,80 +< bge0 ip #0 40(20) 6 5.5.5.6,10000 > 1.1.1.5,80 +< bge0 ip #0 40(20) 6 5.5.5.7,10000 > 1.1.1.5,80 +< bge0 ip #0 40(20) 6 5.5.5.8,10000 > 1.1.1.5,80 +< bge0 ip #0 40(20) 6 5.5.5.9,10000 > 1.1.1.5,80 +< bge0 ip #0 40(20) 6 5.5.6.5,10000 > 1.1.1.5,80 +< bge0 ip #0 40(20) 6 5.5.6.6,10000 > 1.1.1.5,80 +< bge0 ip #0 40(20) 6 5.5.6.7,10000 > 1.1.1.5,80 +< bge0 ip #0 40(20) 6 5.5.6.8,10000 > 1.1.1.5,80 +< bge0 ip #0 40(20) 6 5.5.6.9,10000 > 1.1.1.5,80 +List of active MAP/Redirect filters: +rewrite in on bge0 proto tcp from 0/0 to 0/0 port = 80 -> src 0/0 dst dstlist/servers; + +List of active sessions: +RWR-RDR 5.5.6.9 10000 9.9.9.9 80 <- -> 5.5.6.9 10000 1.1.1.5 80 +RWR-RDR 5.5.6.8 10000 9.9.9.9 80 <- -> 5.5.6.8 10000 1.1.1.5 80 +RWR-RDR 5.5.6.7 10000 9.9.9.9 80 <- -> 5.5.6.7 10000 1.1.1.5 80 +RWR-RDR 5.5.6.6 10000 9.9.9.9 80 <- -> 5.5.6.6 10000 1.1.1.5 80 +RWR-RDR 5.5.6.5 10000 9.9.9.9 80 <- -> 5.5.6.5 10000 1.1.1.5 80 +RWR-RDR 5.5.5.9 10000 9.9.9.9 80 <- -> 5.5.5.9 10000 1.1.1.5 80 +RWR-RDR 5.5.5.8 10000 9.9.9.9 80 <- -> 5.5.5.8 10000 1.1.1.5 80 +RWR-RDR 5.5.5.7 10000 9.9.9.9 80 <- -> 5.5.5.7 10000 1.1.1.5 80 +RWR-RDR 5.5.5.6 10000 9.9.9.9 80 <- -> 5.5.5.6 10000 1.1.1.5 80 +RWR-RDR 5.5.5.5 10000 9.9.9.9 80 <- -> 5.5.5.5 10000 1.1.1.5 80 + +Hostmap table: +List of active state sessions: +List of configured pools +List of configured hash tables +List of groups configured (set 0) +List of groups configured (set 1) +Rules configured (set 0, in) +Rules configured (set 0, out) +Rules configured (set 1, in) +Rules configured (set 1, out) +Accounting rules configured (set 0, in) +Accounting rules configured (set 0, out) +Accounting rules configured (set 1, in) +Accounting rules configured (set 1, out) +------------------------------- diff --git a/contrib/ipfilter/test/expected/p12 b/contrib/ipfilter/test/expected/p12 new file mode 100644 index 00000000000..d097d5117b6 --- /dev/null +++ b/contrib/ipfilter/test/expected/p12 @@ -0,0 +1,40 @@ +< bge0 ip #0 40(20) 6 5.5.5.5,10000 > 1.1.1.2,80 +< bge0 ip #0 40(20) 6 5.5.5.6,10000 > 1.1.1.5,80 +< bge0 ip #0 40(20) 6 5.5.5.7,10000 > 1.1.1.4,80 +< bge0 ip #0 40(20) 6 5.5.5.8,10000 > 1.1.1.4,80 +< bge0 ip #0 40(20) 6 5.5.5.9,10000 > 1.1.1.9,80 +< bge0 ip #0 40(20) 6 5.5.6.5,10000 > 1.1.1.4,80 +< bge0 ip #0 40(20) 6 5.5.6.6,10000 > 1.1.1.4,80 +< bge0 ip #0 40(20) 6 5.5.6.7,10000 > 1.1.1.9,80 +< bge0 ip #0 40(20) 6 5.5.6.8,10000 > 1.1.1.9,80 +< bge0 ip #0 40(20) 6 5.5.6.9,10000 > 1.1.1.5,80 +List of active MAP/Redirect filters: +rewrite in on bge0 proto tcp from 0/0 to 0/0 port = 80 -> src 0/0 dst dstlist/servers; + +List of active sessions: +RWR-RDR 5.5.6.9 10000 9.9.9.9 80 <- -> 5.5.6.9 10000 1.1.1.5 80 +RWR-RDR 5.5.6.8 10000 9.9.9.9 80 <- -> 5.5.6.8 10000 1.1.1.9 80 +RWR-RDR 5.5.6.7 10000 9.9.9.9 80 <- -> 5.5.6.7 10000 1.1.1.9 80 +RWR-RDR 5.5.6.6 10000 9.9.9.9 80 <- -> 5.5.6.6 10000 1.1.1.4 80 +RWR-RDR 5.5.6.5 10000 9.9.9.9 80 <- -> 5.5.6.5 10000 1.1.1.4 80 +RWR-RDR 5.5.5.9 10000 9.9.9.9 80 <- -> 5.5.5.9 10000 1.1.1.9 80 +RWR-RDR 5.5.5.8 10000 9.9.9.9 80 <- -> 5.5.5.8 10000 1.1.1.4 80 +RWR-RDR 5.5.5.7 10000 9.9.9.9 80 <- -> 5.5.5.7 10000 1.1.1.4 80 +RWR-RDR 5.5.5.6 10000 9.9.9.9 80 <- -> 5.5.5.6 10000 1.1.1.5 80 +RWR-RDR 5.5.5.5 10000 9.9.9.9 80 <- -> 5.5.5.5 10000 1.1.1.2 80 + +Hostmap table: +List of active state sessions: +List of configured pools +List of configured hash tables +List of groups configured (set 0) +List of groups configured (set 1) +Rules configured (set 0, in) +Rules configured (set 0, out) +Rules configured (set 1, in) +Rules configured (set 1, out) +Accounting rules configured (set 0, in) +Accounting rules configured (set 0, out) +Accounting rules configured (set 1, in) +Accounting rules configured (set 1, out) +------------------------------- diff --git a/contrib/ipfilter/test/expected/p13 b/contrib/ipfilter/test/expected/p13 new file mode 100644 index 00000000000..aa529eada03 --- /dev/null +++ b/contrib/ipfilter/test/expected/p13 @@ -0,0 +1,30 @@ +nomatch +pass +nomatch +nomatch +nomatch +pass +nomatch +nomatch +List of active MAP/Redirect filters: + +List of active sessions: + +Hostmap table: +List of active state sessions: +List of configured pools +table role=all type=tree number=100 + { 1.1.1.1/32; ! 2.2.0.0/16; 2.2.2.0/24; ef00::5/128; }; +List of configured hash tables +List of groups configured (set 0) +List of groups configured (set 1) +Rules configured (set 0, in) +2 pass in from pool/100 to any +Rules configured (set 0, out) +Rules configured (set 1, in) +Rules configured (set 1, out) +Accounting rules configured (set 0, in) +Accounting rules configured (set 0, out) +Accounting rules configured (set 1, in) +Accounting rules configured (set 1, out) +------------------------------- diff --git a/contrib/ipfilter/test/expected/p2 b/contrib/ipfilter/test/expected/p2 index 67a7c3ea26f..53887422b21 100644 --- a/contrib/ipfilter/test/expected/p2 +++ b/contrib/ipfilter/test/expected/p2 @@ -14,12 +14,22 @@ Hostmap table: List of active state sessions: List of configured pools List of configured hash tables -# 'anonymous' table -table role = ipf type = hash number = 2147483650 size = 3 +# 'anonymous' table refs 2 +table role=ipf type=hash number=2147483650 size=3 { 127.0.0.1/32; 4.4.0.0/16; }; -# 'anonymous' table -table role = ipf type = hash number = 2147483649 size = 3 +# 'anonymous' table refs 2 +table role=ipf type=hash number=2147483649 size=3 { 127.0.0.1/32; 4.4.0.0/16; }; List of groups configured (set 0) List of groups configured (set 1) +Rules configured (set 0, in) +1 block in from hash/2147483650 to any +Rules configured (set 0, out) +2 pass out from hash/2147483649 to any +Rules configured (set 1, in) +Rules configured (set 1, out) +Accounting rules configured (set 0, in) +Accounting rules configured (set 0, out) +Accounting rules configured (set 1, in) +Accounting rules configured (set 1, out) ------------------------------- diff --git a/contrib/ipfilter/test/expected/p3 b/contrib/ipfilter/test/expected/p3 index 94fde9e701a..c1e03432538 100644 --- a/contrib/ipfilter/test/expected/p3 +++ b/contrib/ipfilter/test/expected/p3 @@ -18,18 +18,28 @@ Hostmap table: List of active state sessions: List of configured pools List of configured hash tables -group-map out role = ipf number = 2010 size = 5 - { 5.0.0.0/8, group = 2040; 4.4.0.0/16, group = 2020; 2.2.2.2/32, group = 2020; }; -group-map in role = ipf number = 1010 size = 3 - { 3.3.0.0/16, group = 1030; 1.1.1.1/32, group = 1020; }; +group-map out role=ipf number=2010 size=5 + { 2.2.2.2/32, group=2020; 4.4.0.0/16, group=2020; 5.0.0.0/8, group=2040; }; +group-map in role=ipf number=1010 size=3 + { 1.1.1.1/32, group=1020; 3.3.0.0/16, group=1030; }; List of groups configured (set 0) -Dev.0. Group 1020 Ref 1 Flags 0x8000 +Dev.0. Group 1020 Ref 2 Flags 0x8000 2 pass in all group 1020 -Dev.0. Group 1030 Ref 1 Flags 0x8000 +Dev.0. Group 1030 Ref 2 Flags 0x8000 2 block in all group 1030 -Dev.0. Group 2020 Ref 2 Flags 0x4000 +Dev.0. Group 2020 Ref 3 Flags 0x4000 4 pass out all group 2020 -Dev.0. Group 2040 Ref 1 Flags 0x4000 +Dev.0. Group 2040 Ref 2 Flags 0x4000 2 block out all group 2040 List of groups configured (set 1) +Rules configured (set 0, in) +6 call now srcgrpmap/1010 in all +Rules configured (set 0, out) +6 call now dstgrpmap/2010 out all +Rules configured (set 1, in) +Rules configured (set 1, out) +Accounting rules configured (set 0, in) +Accounting rules configured (set 0, out) +Accounting rules configured (set 1, in) +Accounting rules configured (set 1, out) ------------------------------- diff --git a/contrib/ipfilter/test/expected/p4 b/contrib/ipfilter/test/expected/p4 new file mode 100644 index 00000000000..e7aa73fda3d --- /dev/null +++ b/contrib/ipfilter/test/expected/p4 @@ -0,0 +1,38 @@ +< anon0 ip #0 20(20) 0 127.0.0.1 > 127.0.0.1 +< anon0 ip #0 20(20) 0 1.1.1.1 > 1.2.1.1 +> anon0 ip #0 20(20) 0 127.0.0.1 > 127.0.0.1 +> anon0 ip #0 20(20) 0 1.2.3.4 > 1.2.1.1 +< anon0 ip #0 20(20) 0 2.3.0.1 > 1.2.1.1 +< anon0 ip #0 20(20) 0 2.2.2.1 > 1.2.1.1 +< anon0 ip #0 20(20) 0 2.2.0.1 > 1.2.1.1 +15 +> anon0 ip #0 20(20) 0 1.2.3.4 > 1.2.1.2 +> anon0 ip #0 20(20) 0 2.2.0.1 > 1.2.1.1 +> anon0 ip #0 20(20) 0 2.2.0.1 > 1.2.1.3 +> anon0 ip #0 20(20) 0 4.4.1.1 > 1.2.1.1 +List of active MAP/Redirect filters: +map * from pool/100 to 0/0 -> 1.2.3.4/32 + +List of active sessions: +MAP 2.2.2.1 <- -> 1.2.3.4 [1.2.1.2] +MAP 1.1.1.1 <- -> 1.2.3.4 [1.2.1.1] + +Hostmap table: +2.2.2.1,1.2.1.2 -> 1.2.3.4,0.0.0.0 (use = 1) +1.1.1.1,1.2.1.1 -> 1.2.3.4,0.0.0.0 (use = 1) +List of active state sessions: +List of configured pools +table role=nat type=tree number=100 + { 1.1.1.1/32; ! 2.2.0.0/16; 2.2.2.0/24; }; +List of configured hash tables +List of groups configured (set 0) +List of groups configured (set 1) +Rules configured (set 0, in) +Rules configured (set 0, out) +Rules configured (set 1, in) +Rules configured (set 1, out) +Accounting rules configured (set 0, in) +Accounting rules configured (set 0, out) +Accounting rules configured (set 1, in) +Accounting rules configured (set 1, out) +------------------------------- diff --git a/contrib/ipfilter/test/expected/p5 b/contrib/ipfilter/test/expected/p5 index d8ea95c066a..b56c3bcb4e9 100644 --- a/contrib/ipfilter/test/expected/p5 +++ b/contrib/ipfilter/test/expected/p5 @@ -13,7 +13,7 @@ List of active sessions: Hostmap table: List of active state sessions: List of configured pools -table role = ipf type = tree name = letters +table role=ipf type=tree name=letters { 1.1.1.1/32; ! 2.2.0.0/16; 2.2.2.0/24; }; List of configured hash tables List of groups configured (set 0) diff --git a/contrib/ipfilter/test/expected/p6 b/contrib/ipfilter/test/expected/p6 new file mode 100644 index 00000000000..413f94ba74b --- /dev/null +++ b/contrib/ipfilter/test/expected/p6 @@ -0,0 +1,24 @@ +block +nomatch +List of active MAP/Redirect filters: + +List of active sessions: + +Hostmap table: +List of active state sessions: +List of configured pools +table role=ipf type=tree name=microsoft + { 131.107.0.0/16; 192.92.90.0/24; 198.105.232.0/22; 204.231.58.0/24; 204.140.77.0/24; 204.140.80.0/22; 199.60.28.0/24; 199.103.90.0/23; 199.103.122.0/24; 204.79.101.0/24; 192.237.67.0/24; 198.137.97.0/24; 204.79.135.0/24; 204.79.179.0/24; 204.79.180.0/23; 204.79.188.0/24; 204.79.7.0/24; 204.79.27.0/24; 198.180.74.0/23; 204.231.236.0/24; 205.163.63.0/24; 205.163.62.0/24; 205.163.144.0/20; 205.248.50.0/23; 205.248.72.0/24; 205.248.212.0/22; 205.248.228.0/24; 205.248.235.0/24; 204.231.76.0/24; 204.231.192.0/24; 207.78.80.0/24; 207.78.81.0/24; 207.78.82.0/24; 207.117.3.0/24; 207.18.117.0/24; 208.139.27.0/24; 209.28.213.0/24; 207.209.68.0/24; 204.95.96.0/20; 207.158.93.192/27; 207.240.123.192/27; 208.26.205.0/24; 192.197.157.0/24; 204.133.231.0/24; 216.72.96.0/22; 207.229.166.152/29; 204.95.149.0/24; 209.192.213.72/29; 206.73.203.0/24; 206.73.118.0/24; 208.45.54.16/29; 208.45.54.8/29; 206.73.31.0/24; 63.161.50.128/25; 63.161.50.0/25; 207.240.8.224/28; 208.45.89.248/29; 206.182.69.0/24; 206.182.240.0/24; 206.182.241.0/24; 206.73.67.0/24; 206.182.251.0/24; 206.182.247.0/24; 206.182.236.0/24; 63.236.198.64/29; 63.236.198.152/29; 165.121.253.232/29; 63.236.170.64/29; 63.236.186.64/29; 63.236.187.104/29; 63.236.187.128/29; 63.236.187.160/29; 199.2.137.0/24; 216.222.104.224/28; 63.151.87.64/29; 64.77.82.96/29; 64.77.93.80/28; 65.52.0.0/14; 207.46.0.0/16; 204.182.144.0/20; 206.107.34.0/24; 205.240.158.0/23; 204.79.252.0/24; 64.200.211.16/28; 12.178.163.0/27; 69.44.126.80/28; 63.173.42.128/25; 12.28.108.0/25; 65.170.29.0/29; 67.132.133.96/29; 8.6.176.0/24; 63.148.123.240/29; 64.41.193.0/24; 64.85.70.32/28; 64.85.81.96/29; 64.85.81.104/29; 216.32.168.224/27; 206.79.74.32/28; 216.32.175.224/27; 216.32.180.0/22; 216.33.229.224/27; 216.33.236.0/22; 216.33.240.0/22; 216.32.240.0/22; 216.34.51.0/24; 209.1.112.0/24; 209.1.113.0/24; 209.1.15.0/24; 216.34.53.176/28; 216.35.8.224/28; 209.185.128.0/22; 65.114.175.128/27; 64.15.229.96/27; 64.15.177.0/24; 64.15.170.192/29; 209.143.238.0/24; 64.15.178.0/24; 66.35.209.120/29; 66.35.211.128/26; 66.35.208.48/28; 216.33.148.0/22; 216.35.66.88/29; 12.230.32.160/29; 12.53.124.0/27; 12.232.18.96/27; 12.190.158.0/24; 12.71.196.32/28; 209.240.192.0/19; 70.37.128.0/23; 70.37.135.0/24; 12.49.87.192/26; 74.93.205.144/29; 74.93.205.152/29; 74.93.206.64/29; 70.89.139.120/29; 206.71.119.0/24; 206.71.117.0/24; 206.71.118.0/24; 209.154.155.112/29; 65.68.62.152/29; 67.39.208.168/29; 65.242.67.0/24; 204.71.191.0/24; 63.194.155.144/29; 66.136.85.192/29; 64.124.184.72/29; 216.200.206.0/24; 63.80.93.0/25; 67.192.225.208/28; 69.74.162.0/24; 65.221.5.0/24; 65.248.85.0/24; 199.243.157.192/27; 199.243.157.112/29; 65.194.210.224/27; 208.194.139.0/24; 208.204.49.128/25; 208.205.26.0/24; 208.217.184.0/22; 208.222.172.0/24; 208.224.200.64/27; 208.229.100.0/23; 208.241.19.0/28; 208.241.19.16/28; 208.241.9.224/28; 208.244.108.0/28; 208.245.16.0/27; 208.249.17.160/28; 63.104.216.0/25; 63.69.245.0/24; 68.90.141.72/29; 63.198.123.160/29; 68.248.48.64/29; 68.248.48.72/29; 99.49.8.248/29; 65.38.172.72/29; 65.38.172.96/28; 75.149.174.16/29; 75.151.100.240/28; 64.81.8.96/27; 67.112.255.144/29; 63.240.201.176/28; 206.16.209.208/28; 63.240.195.208/28; 206.16.204.64/28; 206.16.223.0/24; 63.240.216.0/22; 63.240.220.0/22; 206.16.246.24/29; 63.240.195.192/28; 206.16.224.160/27; 67.192.39.48/28; 72.32.240.160/28; 72.32.201.152/29; 67.39.81.152/29; 69.20.127.32/29; 216.52.28.0/24; 70.42.230.0/23; 63.251.97.0/24; 67.120.132.128/29; 67.120.132.152/29; 67.120.132.192/28; }; +List of configured hash tables +List of groups configured (set 0) +List of groups configured (set 1) +Rules configured (set 0, in) +1 block in from pool/microsoft to any +Rules configured (set 0, out) +Rules configured (set 1, in) +Rules configured (set 1, out) +Accounting rules configured (set 0, in) +Accounting rules configured (set 0, out) +Accounting rules configured (set 1, in) +Accounting rules configured (set 1, out) +------------------------------- diff --git a/contrib/ipfilter/test/expected/p7 b/contrib/ipfilter/test/expected/p7 new file mode 100644 index 00000000000..89bfc11301c --- /dev/null +++ b/contrib/ipfilter/test/expected/p7 @@ -0,0 +1,40 @@ +< bge0 ip #0 40(20) 6 5.5.5.5,10000 > 1.1.1.2,80 +< bge0 ip #0 40(20) 6 5.5.5.6,10000 > 1.1.1.4,80 +< bge0 ip #0 40(20) 6 5.5.5.7,10000 > 1.1.1.5,80 +< bge0 ip #0 40(20) 6 5.5.5.8,10000 > 1.1.1.9,80 +< bge0 ip #0 40(20) 6 5.5.5.9,10000 > 1.1.1.2,80 +< bge0 ip #0 40(20) 6 5.5.6.5,10000 > 1.1.1.4,80 +< bge0 ip #0 40(20) 6 5.5.6.6,10000 > 1.1.1.5,80 +< bge0 ip #0 40(20) 6 5.5.6.7,10000 > 1.1.1.9,80 +< bge0 ip #0 40(20) 6 5.5.6.8,10000 > 1.1.1.2,80 +< bge0 ip #0 40(20) 6 5.5.6.9,10000 > 1.1.1.4,80 +List of active MAP/Redirect filters: +rewrite in on bge0 proto tcp from 0/0 to 0/0 port = 80 -> src 0/0 dst dstlist/servers; + +List of active sessions: +RWR-RDR 5.5.6.9 10000 9.9.9.9 80 <- -> 5.5.6.9 10000 1.1.1.4 80 +RWR-RDR 5.5.6.8 10000 9.9.9.9 80 <- -> 5.5.6.8 10000 1.1.1.2 80 +RWR-RDR 5.5.6.7 10000 9.9.9.9 80 <- -> 5.5.6.7 10000 1.1.1.9 80 +RWR-RDR 5.5.6.6 10000 9.9.9.9 80 <- -> 5.5.6.6 10000 1.1.1.5 80 +RWR-RDR 5.5.6.5 10000 9.9.9.9 80 <- -> 5.5.6.5 10000 1.1.1.4 80 +RWR-RDR 5.5.5.9 10000 9.9.9.9 80 <- -> 5.5.5.9 10000 1.1.1.2 80 +RWR-RDR 5.5.5.8 10000 9.9.9.9 80 <- -> 5.5.5.8 10000 1.1.1.9 80 +RWR-RDR 5.5.5.7 10000 9.9.9.9 80 <- -> 5.5.5.7 10000 1.1.1.5 80 +RWR-RDR 5.5.5.6 10000 9.9.9.9 80 <- -> 5.5.5.6 10000 1.1.1.4 80 +RWR-RDR 5.5.5.5 10000 9.9.9.9 80 <- -> 5.5.5.5 10000 1.1.1.2 80 + +Hostmap table: +List of active state sessions: +List of configured pools +List of configured hash tables +List of groups configured (set 0) +List of groups configured (set 1) +Rules configured (set 0, in) +Rules configured (set 0, out) +Rules configured (set 1, in) +Rules configured (set 1, out) +Accounting rules configured (set 0, in) +Accounting rules configured (set 0, out) +Accounting rules configured (set 1, in) +Accounting rules configured (set 1, out) +------------------------------- diff --git a/contrib/ipfilter/test/expected/p9 b/contrib/ipfilter/test/expected/p9 new file mode 100644 index 00000000000..89bfc11301c --- /dev/null +++ b/contrib/ipfilter/test/expected/p9 @@ -0,0 +1,40 @@ +< bge0 ip #0 40(20) 6 5.5.5.5,10000 > 1.1.1.2,80 +< bge0 ip #0 40(20) 6 5.5.5.6,10000 > 1.1.1.4,80 +< bge0 ip #0 40(20) 6 5.5.5.7,10000 > 1.1.1.5,80 +< bge0 ip #0 40(20) 6 5.5.5.8,10000 > 1.1.1.9,80 +< bge0 ip #0 40(20) 6 5.5.5.9,10000 > 1.1.1.2,80 +< bge0 ip #0 40(20) 6 5.5.6.5,10000 > 1.1.1.4,80 +< bge0 ip #0 40(20) 6 5.5.6.6,10000 > 1.1.1.5,80 +< bge0 ip #0 40(20) 6 5.5.6.7,10000 > 1.1.1.9,80 +< bge0 ip #0 40(20) 6 5.5.6.8,10000 > 1.1.1.2,80 +< bge0 ip #0 40(20) 6 5.5.6.9,10000 > 1.1.1.4,80 +List of active MAP/Redirect filters: +rewrite in on bge0 proto tcp from 0/0 to 0/0 port = 80 -> src 0/0 dst dstlist/servers; + +List of active sessions: +RWR-RDR 5.5.6.9 10000 9.9.9.9 80 <- -> 5.5.6.9 10000 1.1.1.4 80 +RWR-RDR 5.5.6.8 10000 9.9.9.9 80 <- -> 5.5.6.8 10000 1.1.1.2 80 +RWR-RDR 5.5.6.7 10000 9.9.9.9 80 <- -> 5.5.6.7 10000 1.1.1.9 80 +RWR-RDR 5.5.6.6 10000 9.9.9.9 80 <- -> 5.5.6.6 10000 1.1.1.5 80 +RWR-RDR 5.5.6.5 10000 9.9.9.9 80 <- -> 5.5.6.5 10000 1.1.1.4 80 +RWR-RDR 5.5.5.9 10000 9.9.9.9 80 <- -> 5.5.5.9 10000 1.1.1.2 80 +RWR-RDR 5.5.5.8 10000 9.9.9.9 80 <- -> 5.5.5.8 10000 1.1.1.9 80 +RWR-RDR 5.5.5.7 10000 9.9.9.9 80 <- -> 5.5.5.7 10000 1.1.1.5 80 +RWR-RDR 5.5.5.6 10000 9.9.9.9 80 <- -> 5.5.5.6 10000 1.1.1.4 80 +RWR-RDR 5.5.5.5 10000 9.9.9.9 80 <- -> 5.5.5.5 10000 1.1.1.2 80 + +Hostmap table: +List of active state sessions: +List of configured pools +List of configured hash tables +List of groups configured (set 0) +List of groups configured (set 1) +Rules configured (set 0, in) +Rules configured (set 0, out) +Rules configured (set 1, in) +Rules configured (set 1, out) +Accounting rules configured (set 0, in) +Accounting rules configured (set 0, out) +Accounting rules configured (set 1, in) +Accounting rules configured (set 1, out) +------------------------------- diff --git a/contrib/ipfilter/test/h4to6 b/contrib/ipfilter/test/h4to6 new file mode 100644 index 00000000000..e31f7c4224b --- /dev/null +++ b/contrib/ipfilter/test/h4to6 @@ -0,0 +1,135 @@ +@P=(); +$line = 0; +while (<>) { + s/\=192.168.1.188/\=c0a8:0100::bc/g; + s/\=192.168.1.188/\=c0a8:0100::bc/g; + @F = split; + if (/^#/) { + @P[$nline++] = join(" ",@F); + next; + } + $line = 0 if (/^\[/); + if ($line == 1) { + $len = hex($F[1]) - 20; + $pr = hex($F[4]) & 0xff; + $pr = 58 if ($pr == 1); + $ttl = hex($F[4]) >> 8; + &replaceip($_, $len, $pr, $ttl); + $ipline = $nline; + $err = 0; + } elsif ($line == 2) { + if ($pr == 58) { + # + # Map the ICMP type codes from IPv4 to IPv6 + # and update the checksum to compensate. + # + if ($F[0] =~ /^0800/) { + $F[0] =~ s/^0800/8000/; + $d = 0x7800; + } + if ($F[0] =~ /^0000/) { + $F[0] =~ s/^0000/8100/; + $d = 0x8100; + } + if ($F[0] =~ /^0304/) { + $F[0] =~ s/^0304/0200/; + $d = 0xfefc; + $err = 1; + } + if ($F[0] =~ /^03/) { + $F[0] =~ s/^03/01/; + $d = 0xfe00; + $err = 1; + } + if ($F[0] =~ /^0b/) { + $F[0] =~ s/^0b/03/; + $d = 0xf800; + $err = 1; + } + if ($F[0] =~ /^0c/) { + $F[0] =~ s/^0c/04/; + $d = 0xf800; + $err = 1; + } + $F[1] = sprintf "%04x", hex($F[1]) - $d; + } + @P[$nline++] = join(" ",@F); + } elsif ($line == 3) { + if ($pr == 58 && $err == 1 && $F[0] =~ /^45/) { + local($l) = hex($F[1]) - 20; + local($p) = hex($F[4]) & 0xff; + $p = 58 if ($p == 1); + local($t) = hex($F[4]) >> 8; + &replaceip(join(" ", @F), $l, $p, $t); + @H = split(/ /, $P[$ipline]); + $H[2] += 20; + $P[$ipline] = join(" ",@H); + } else { + @P[$nline++] = join(" ",@F); + } + } else { + @P[$nline++] = join(" ",@F); + } + $line++; +} + +for ($li = 0; $li < $nline; $li++) { + print "$P[$li]\n"; +} + +exit(0); + +sub replaceip { + local(@G) = split(/\s/,$_[0]); + local($p) = ""; + + $p = sprintf "6000 0000 %04x %02x%02x", $_[1], $_[2], $_[3]; + if ($G[6] =~ /^c0a8/) { + $fmt = " %02x%02x %02x00 0000 0000 0000 0000 0000 00%02x"; + } else { + if ($G[6] =~ /^0[4a]../) { + $fmt = " 00%02x 00%02x 00%02x 0000 0000 0000 0000 00%02x"; + } else { + $fmt = " 00%02x 0000 0000 0000 0000 00%02x 00%02x 00%02x"; + } + if ($G[6] =~ /^0a/) { + $G[6] =~ s/^0a/10/; + } + if ($G[6] =~ /0a$/) { + $G[6] =~ s/0a$/10/; + } + if ($G[7] =~ /^0a/) { + $G[7] =~ s/^0a/10/; + } + if ($G[7] =~ /0a$/) { + $G[7] =~ s/0a$/10/; + } + } + $p = $p.sprintf $fmt, hex($G[6]) >> 8, hex($G[6]) & 0xff, + hex($G[7]) >> 8, hex($G[7]) & 0xff; + + if ($G[6] =~ /^c0a8/) { + $fmt = " %02x%02x %02x00 0000 0000 0000 0000 0000 00%02x"; + } else { + if ($G[8] =~ /^0[4a]../) { + $fmt = " 00%02x 00%02x 00%02x 0000 0000 0000 0000 00%02x"; + } else { + $fmt = " 00%02x 0000 0000 0000 0000 00%02x 00%02x 00%02x"; + } + if ($G[8] =~ /^0a/) { + $G[8] =~ s/^0a/10/; + } + if ($G[8] =~ /0a$/) { + $G[8] =~ s/0a$/10/; + } + if ($G[9] =~ /^0a/) { + $G[9] =~ s/^0a/10/; + } + if ($G[9] =~ /0a$/) { + $G[9] =~ s/0a$/10/; + } + } + $p = $p.sprintf $fmt, hex($G[8]) >> 8, hex($G[8]) & 0xff, + hex($G[9]) >> 8, hex($G[9]) & 0xff; + $P[$nline++] = $p; +} diff --git a/contrib/ipfilter/test/hextest b/contrib/ipfilter/test/hextest deleted file mode 100644 index b7b0b2c75e4..00000000000 --- a/contrib/ipfilter/test/hextest +++ /dev/null @@ -1,27 +0,0 @@ -#!/bin/sh -if [ -f /usr/ucb/touch ] ; then - TOUCH=/usr/ucb/touch -else - if [ -f /usr/bin/touch ] ; then - TOUCH=/usr/bin/touch - else - if [ -f /bin/touch ] ; then - TOUCH=/bin/touch - fi - fi -fi -echo "$1..."; -/bin/cp /dev/null results/$1 -( while read rule; do - echo "$rule" | ../ipftest -br - -F hex -i input/$1 >> results/$1; - if [ $? -ne 0 ] ; then - exit 1; - fi - echo "--------" >> results/$1 -done ) < regress/$1 -cmp expected/$1 results/$1 -status=$? -if [ $status = 0 ] ; then - $TOUCH $1 -fi -exit $status diff --git a/contrib/ipfilter/test/i4to6 b/contrib/ipfilter/test/i4to6 new file mode 100644 index 00000000000..b1208fd923d --- /dev/null +++ b/contrib/ipfilter/test/i4to6 @@ -0,0 +1,12 @@ +sed \ +-e 's/in /in6 /g' \ +-e 's/icmp/58/g' \ +-e 's/out /out6 /g' \ +-e 's/10\.4\.\([0-9]\)\.\([0-9]\)/10:4:\1::\2/g' \ +-e 's/10\.3\.4\.\([0-9]\)/10::3:4:\1/g' \ +-e 's/10\.3\.\([0-9]\)\.\([0-9]\)/10:3:\1::\2/g' \ +-e 's/10\.1\.\([0-9]\)\.\([0-9]\)/10:1:\1::\2/g' \ +-e 's/10\.2\.\([0-9]\)\.\([0-9]\)/10::2:\1:\2/g' \ +-e 's/9\.\([0-9]\)\.\([0-9]\)\.\([0-9]\)/9:\1:\2::\3/g' \ +-e 's/5\.\([0-9]\)\.\([0-9]\)\.\([0-9]\)/5:\1::\2:\3/g' \ +-e 's/2\.\([0-9]\)\.\([0-9]\)\.\([0-9]\)/2::\1:\2:\3/g' diff --git a/contrib/ipfilter/test/input/f13 b/contrib/ipfilter/test/input/f13 index 77e537e2b63..ccd370a7230 100644 --- a/contrib/ipfilter/test/input/f13 +++ b/contrib/ipfilter/test/input/f13 @@ -83,7 +83,7 @@ 4500 0028 0003 4000 3f06 36ca 0101 0101 0201 0101 0400 0019 0040 0000 0000 0000 5010 2000 8678 0000 -# 1.1.1.1,1024 -> 2.1.1.1,25 TTL=63 TCP DF ACK +# 1.1.1.1,1024 -> 2.1.1.1,25 TTL=63 TCP DF ACK (out-of-order) [in] 4500 0028 0003 4000 3f06 36ca 0101 0101 0201 0101 0400 0019 7000 0004 0000 0002 5010 2000 16b2 0000 diff --git a/contrib/ipfilter/test/input/f21 b/contrib/ipfilter/test/input/f21 new file mode 100644 index 00000000000..1135cbd01c4 --- /dev/null +++ b/contrib/ipfilter/test/input/f21 @@ -0,0 +1,31 @@ +# ICMP dest unreachable with 64 bits in payload (in reply to a TCP packet +# going out) +# IP 4.4.4.4 2.2.2.2 TCP(20480,80) +[out,df0] +4500 003c 4706 4000 ff06 28aa 0404 0404 +0202 0202 5000 0050 0000 0001 0000 0000 +a002 16d0 d8e2 0000 0204 05b4 0402 080a +0047 fbb0 0000 0000 0103 0300 + +# IP 3.3.3.3 -> 4.4.4.4 ICMP (IP(4.4.4.4,6.6.6.6) TCP(20480,80)) UNREACH +[in,df0] +4500 0038 809a 0000 ff01 2d1d 0303 0303 +0404 0404 0303 acab 0000 0000 4500 003c +4706 4000 ff06 28aa 0404 0404 0202 0202 +5000 0050 0000 0001 + +# IP 3.3.3.3 -> 4.4.4.4 ICMP (IP(4.4.4.4,6.6.6.6) TCP(20480,80)) REDIRECT +# ICMP dest unreachable with whole packet in payload (40 bytes = 320 bits) +[in,df0] +4500 0038 809a 0000 ff01 2d1d 0303 0303 +0404 0404 0501 9a9d 0808 0808 4500 003c +4706 4000 ff06 28aa 0404 0404 0202 0202 +5000 0050 0000 0001 + +# IP 3.3.3.3 -> 5.5.5.5 ICMP (IP(4.4.4.4,6.6.6.6) TCP(20480,80)) UNREACH +[in,df0] +4500 0038 809a 0000 ff01 2b1b 0303 0303 +0505 0505 0303 acab 0000 0000 4500 003c +4706 4000 ff06 28aa 0404 0404 0202 0202 +5000 0050 0000 0001 + diff --git a/contrib/ipfilter/test/input/f22 b/contrib/ipfilter/test/input/f22 new file mode 100644 index 00000000000..a5221c1abdb --- /dev/null +++ b/contrib/ipfilter/test/input/f22 @@ -0,0 +1,31 @@ +# ICMP dest unreachable with 64 bits in payload (in reply to a TCP packet +# going out) +# IP 4.4.4.4 2.2.2.2 TCP(20480,80) +[in,df0] +4500 003c 4706 4000 ff06 28aa 0404 0404 +0202 0202 5000 0050 0000 0001 0000 0000 +a002 16d0 d8e2 0000 0204 05b4 0402 080a +0047 fbb0 0000 0000 0103 0300 + +# IP 3.3.3.3 -> 4.4.4.4 ICMP (IP(4.4.4.4,6.6.6.6) TCP(20480,80)) UNREACH +[out,df0] +4500 0038 809a 0000 ff01 2d1d 0303 0303 +0404 0404 0303 acab 0000 0000 4500 003c +4706 4000 ff06 28aa 0404 0404 0202 0202 +5000 0050 0000 0001 + +# IP 3.3.3.3 -> 4.4.4.4 ICMP (IP(4.4.4.4,6.6.6.6) TCP(20480,80)) REDIRECT +# ICMP dest unreachable with whole packet in payload (40 bytes = 320 bits) +[out,df0] +4500 0038 809a 0000 ff01 2d1d 0303 0303 +0404 0404 0501 9a9d 0808 0808 4500 003c +4706 4000 ff06 28aa 0404 0404 0202 0202 +5000 0050 0000 0001 + +# IP 3.3.3.3 -> 5.5.5.5 ICMP (IP(4.4.4.4,6.6.6.6) TCP(20480,80)) UNREACH +[out,df0] +4500 0038 809a 0000 ff01 2b1b 0303 0303 +0505 0505 0303 acab 0000 0000 4500 003c +4706 4000 ff06 28aa 0404 0404 0202 0202 +5000 0050 0000 0001 + diff --git a/contrib/ipfilter/test/input/f24 b/contrib/ipfilter/test/input/f24 index 1d066822561..030772b427f 100644 --- a/contrib/ipfilter/test/input/f24 +++ b/contrib/ipfilter/test/input/f24 @@ -12,7 +12,7 @@ c0a8 0101 0035 eb22 00a9 d7b9 4a82 8180 0c00 0100 0100 0000 3c00 0496 [in,hme0] -4500 004c fc96 2006 4011 d9b4 c0a8 01fe +4500 004c fc96 2007 4011 d9b3 c0a8 01fe c0a8 0101 cbe7 50c0 1300 0200 0100 0078 8c00 0603 6e73 31c0 13c0 1300 0200 0100 0078 8c00 0e02 6e73 0861 6465 6c61 6964 diff --git a/contrib/ipfilter/test/input/f25 b/contrib/ipfilter/test/input/f25 new file mode 100644 index 00000000000..a4e31398e01 --- /dev/null +++ b/contrib/ipfilter/test/input/f25 @@ -0,0 +1,41 @@ +[in,hme0]+mcast +4500 0081 b02d 0000 0411 53b1 c0a8 01eb +efff fffa 1f48 076c 006d 1bd2 0000 0000 +0000 0000 0000 0000 0000 0000 0000 0000 +0000 0000 0000 0000 0000 0000 0000 0000 +0000 0000 0000 0000 0000 0000 0000 0000 +0000 0000 0000 0000 0000 0000 0000 0000 +0000 0000 0000 0000 0000 0000 0000 0000 +0000 0000 0000 0000 0000 0000 0000 0000 +0a + +[out,hme0] +4500 0108 7aca 0000 4011 79e1 c0a8 01fe +c0a8 01eb 076c 1f48 00f4 5218 0000 0000 +0000 0000 0000 0000 0000 0000 0000 0000 +0000 0000 0000 0000 0000 0000 0000 0000 +0000 0000 0000 0000 0000 0000 0000 0000 +0000 0000 0000 0000 0000 0000 0000 0000 +0000 0000 0000 0000 0000 0000 0000 0000 +0000 0000 0000 0000 0000 0000 0000 0000 +0000 0000 0000 0000 0000 0000 0000 0000 +0000 0000 0000 0000 0000 0000 0000 0000 +0000 0000 0000 0000 0000 0000 0000 0000 +0000 0000 0000 0000 0000 0000 0000 0000 +0000 0000 0000 0000 0000 0000 0000 0000 +0000 0000 0000 0000 0000 0000 0000 0000 +0000 0000 0000 0000 0000 0000 0000 0000 +0000 0000 0000 0000 0000 0000 0000 0000 +0000 0000 0000 0000 + +[in,hme0]+mcast +4500 0081 b02d 0000 0411 53b1 c0a8 01eb +efff fffa 1f48 076c 006d 1bd2 0000 0000 +0000 0000 0000 0000 0000 0000 0000 0000 +0000 0000 0000 0000 0000 0000 0000 0000 +0000 0000 0000 0000 0000 0000 0000 0000 +0000 0000 0000 0000 0000 0000 0000 0000 +0000 0000 0000 0000 0000 0000 0000 0000 +0000 0000 0000 0000 0000 0000 0000 0000 +0a + diff --git a/contrib/ipfilter/test/input/f26 b/contrib/ipfilter/test/input/f26 new file mode 100644 index 00000000000..2151f72e7cb --- /dev/null +++ b/contrib/ipfilter/test/input/f26 @@ -0,0 +1,13 @@ +in tcp 1.1.1.1,1001 2.2.2.2,22 S +in tcp 1.1.1.1,1002 2.2.2.2,22 S +in tcp 1.1.1.1,1003 2.2.2.2,22 S +in tcp 1.1.1.1,1004 2.2.2.2,22 S +in tcp 1.1.1.2,1002 2.2.2.2,22 S +in tcp 1.1.1.3,1003 2.2.2.2,22 S +in tcp 1.1.1.4,1004 2.2.2.2,22 S +in tcp 1.1.1.2,1005 2.2.2.2,22 S +in tcp 1.1.1.3,1006 2.2.2.2,22 S +in tcp 1.1.1.4,1007 2.2.2.2,22 S +in tcp 1.1.1.2,1008 2.2.2.2,22 S +in tcp 1.1.1.3,1009 2.2.2.2,22 S +in tcp 1.1.1.4,1010 2.2.2.2,22 S diff --git a/contrib/ipfilter/test/input/f27 b/contrib/ipfilter/test/input/f27 new file mode 100644 index 00000000000..f01bf7e6ee7 --- /dev/null +++ b/contrib/ipfilter/test/input/f27 @@ -0,0 +1,84 @@ +[in,hme0] +45000028 0000 0000 FF06 b5ca +01010101 02020202 +03e9 0016 00000000 00000000 5002 0000 +a5de 0000 + +[in,hme0] +45000028 0000 0000 FF06 b5ca +01010101 02020202 +03ea 0016 00000000 00000000 5002 0000 +a5dd 0000 + +[in,hme0] +45000028 0000 0000 FF06 b5ca +01010101 02020202 +03eb 0016 00000000 00000000 5002 0000 +a5dc 0000 + +[in,hme0] +45000028 0000 0000 FF06 b5ca +01010101 02020202 +03ec 0016 00000000 00000000 5002 0000 +a5db 0000 + +[in,hme0] +45000028 0000 0000 FF06 b5c9 +01010102 02020202 +03ed 0016 00000000 00000000 5002 0000 +a5d9 0000 + +[in,hme0] +45000028 0000 0000 FF06 b5c8 +01010103 02020202 +03ee 0016 00000000 00000000 5002 0000 +a5d7 0000 + +[in,hme0] +45000028 0000 0000 FF06 b5c7 +01010104 02020202 +03ef 0016 00000000 00000000 5002 0000 +a5d5 0000 + +[in,hme0] +45000028 0000 0000 FF06 b5c9 +01010102 02020202 +03f0 0016 00000000 00000000 5002 0000 +a5d6 0000 + +[in,hme0] +45000028 0000 0000 FF06 b5c8 +01010103 02020202 +03f1 0016 00000000 00000000 5002 0000 +a5d4 0000 + +[in,hme0] +45000028 0000 0000 FF06 b5c7 +01010104 02020202 +03f2 0016 00000000 00000000 5002 0000 +a5d2 0000 + +[in,hme0] +45000028 0000 0000 FF06 b5c9 +01010102 02020202 +03f3 0016 00000000 00000000 5002 0000 +a5d3 0000 + +[in,hme0] +45000028 0000 0000 FF06 b5c8 +01010103 02020202 +03f4 0016 00000000 00000000 5002 0000 +a5d1 0000 + +[in,hme0] +45000028 0000 0000 FF06 b5c7 +01010104 02020202 +03f5 0016 00000000 00000000 5002 0000 +a5cf 0000 + +[in,hme0] +6000 0000 0014 06FF +ef00 1001 2002 0001 0000 0000 0000 0070 +2001 1002 3333 0001 0000 0000 0000 0001 +03f6 0016 0000 0000 0000 0000 5002 0000 292a 0000 + diff --git a/contrib/ipfilter/test/input/f28 b/contrib/ipfilter/test/input/f28 new file mode 100644 index 00000000000..8849c140f1f --- /dev/null +++ b/contrib/ipfilter/test/input/f28 @@ -0,0 +1,7 @@ +in on nic1 4.4.0.1 4.2.0.2 +in on nic2 4.4.1.1 4.2.1.2 +in on nic3 4.4.2.1 4.2.2.2 +in on nic0 4.4.3.1 4.2.3.2 +in on nic0 4.4.1.1 4.2.1.2 +in on nic0 4.4.2.1 4.2.2.2 +in on nic0 4.4.3.1 4.2.3.2 diff --git a/contrib/ipfilter/test/input/f29 b/contrib/ipfilter/test/input/f29 new file mode 100644 index 00000000000..2e717af635b --- /dev/null +++ b/contrib/ipfilter/test/input/f29 @@ -0,0 +1,11 @@ +in on nic1 4.4.0.1 4.2.0.2 +in on nic2 4.4.1.1 4.2.1.2 +in on nic3 4.4.2.1 4.2.2.2 +in on nic0 udp 4.4.3.1,1000 4.2.3.2,2000 +in on nic0 udp 4.4.3.1,1000 4.2.3.2,2000 +in on nic0 udp 4.4.1.1,1001 4.2.1.2,2001 +in on nic0 udp 4.4.1.1,1001 4.2.1.2,2001 +in on nic0 udp 4.4.2.1,1002 4.2.2.2,2002 +in on nic0 udp 4.4.2.1,1002 4.2.2.2,2002 +in on nic0 udp 4.4.3.1,1003 4.2.3.2,2003 +in on nic0 udp 4.4.3.1,1003 4.2.3.2,2003 diff --git a/contrib/ipfilter/test/input/f30 b/contrib/ipfilter/test/input/f30 new file mode 100644 index 00000000000..ebf7dc06180 --- /dev/null +++ b/contrib/ipfilter/test/input/f30 @@ -0,0 +1,16 @@ +in on hme0 udp 1.1.1.1,53 2.1.1.1,53 opt lsrr +in on hme1 udp 2.1.1.1,53 1.1.1.1,53 opt ts,lsrr +in on hme1 udp 2.1.1.1,53 1.1.1.1,53 opt lsrr +in on hme0 udp 1.1.1.1,53 2.1.1.1,53 +in on hme1 udp 2.1.1.1,53 1.1.1.1,53 +in on hme0 tcp 1.1.1.1,12345 2.1.1.1,22 S opt rr +in on hme0 tcp 1.1.1.1,12345 2.1.1.1,22 S +in on hme1 tcp 2.1.1.1,22 1.1.1.1,12345 SA opt rr,ts +in on hme1 tcp 2.1.1.1,22 1.1.1.1,12345 SA opt rr +in on hme1 tcp 2.1.1.1,22 1.1.1.1,12345 SA +in on hme0 tcp 1.1.1.1,12346 2.1.1.1,22 S opt sec-class=secret +in on hme0 tcp 1.1.1.1,12346 2.1.1.1,22 S +in on hme1 tcp 2.1.1.1,22 1.1.1.1,12346 SA opt sec-class=topsecret +in on hme1 tcp 2.1.1.1,22 1.1.1.1,12346 SA opt ts,sec-class=secret +in on hme1 tcp 2.1.1.1,22 1.1.1.1,12346 SA opt sec-class=secret +in on hme1 tcp 2.1.1.1,22 1.1.1.1,12346 SA diff --git a/contrib/ipfilter/test/input/ipf6-1 b/contrib/ipfilter/test/input/ipf6-1 deleted file mode 100644 index 8cc2d175dc2..00000000000 --- a/contrib/ipfilter/test/input/ipf6-1 +++ /dev/null @@ -1,26 +0,0 @@ -[out,de0] -6000 0000 0020 3aff ef00 0000 0000 0000 -0000 0000 0001 0013 ff02 0000 0000 0000 -0000 0001 ff01 000b 8700 ea32 0000 0000 -ef00 0000 0000 0000 0000 0000 0001 000b -0101 0048 5487 5c6f - -[in,de0] -6000 0000 0020 3aff ef00 0000 0000 0000 -0000 0000 0001 000b ef00 0000 0000 0000 -0000 0000 0001 0013 8800 5322 6000 0000 -ef00 0000 0000 0000 0000 0000 0001 000b -0201 0800 2071 cce1 - -[out,de0] -6000 0000 0010 3a40 ef00 0000 0000 0000 -0000 0000 0001 0013 ef00 0000 0000 0000 -0000 0000 0001 000b 8000 3210 06ff 0002 -9ec3 3c3c 8a82 0300 - -[in,de0] -6000 0000 0010 3aff ef00 0000 0000 0000 -0000 0000 0001 000b ef00 0000 0000 0000 -0000 0000 0001 0013 8100 3110 06ff 0002 -9ec3 3c3c 8a82 0300 - diff --git a/contrib/ipfilter/test/input/ipv6.1 b/contrib/ipfilter/test/input/ipv6.1 index 3f0fd308102..6da8da0e4bb 100644 --- a/contrib/ipfilter/test/input/ipv6.1 +++ b/contrib/ipfilter/test/input/ipv6.1 @@ -1,3 +1,8 @@ +# +# traceroute simulation +# +# UDP +# [out,gif0] 6000 0000 0018 1101 ef00 1001 2002 0001 0000 0000 0000 0070 2001 1002 3333 0001 0000 0000 0000 0001 @@ -6,10 +11,14 @@ ef00 1001 2002 0001 0000 0000 0000 0070 f4c1 0000 0344 0000 0004 f8f1 9d3c ddba 0e00 +# +# ICMPV6 +# - Time exceeded +# [in,gif0] 6000 0000 0048 3a40 ef00 1001 0880 6cbf 0000 0000 0000 0001 ef00 1001 2002 0001 0000 0000 0000 0070 -0300 7d44 0000 0000 +0300 f86f 0000 0000 6000 0000 0018 1101 ef00 1001 2002 0001 0000 0000 0000 0070 2001 1002 3333 0001 0000 0000 0000 0001 @@ -18,14 +27,18 @@ ef00 1001 2002 0001 0000 0000 0000 0070 f427 0000 0344 0000 0004 f8f1 9d3c ddba 0e00 +# +# ICMPV6 +# - Time exceeded +# [in,gif0] 6000 0000 0048 3a40 ef00 1001 0880 6cbf 0000 0000 0000 0001 ef00 1001 2002 0001 0000 0000 0000 0070 -0300 7d44 0000 0000 +0300 7266 0000 0000 6000 0000 0018 1101 ef00 1001 2002 1001 0000 0000 0000 0070 2001 1002 3333 0001 0000 0000 0000 0001 -8083 829a +8083 f8a3 0018 f427 0000 0344 0000 0004 f8f1 9d3c ddba 0e00 diff --git a/contrib/ipfilter/test/input/ipv6.3 b/contrib/ipfilter/test/input/ipv6.3 index e8ad9f2f22d..3b2ef4d325b 100644 --- a/contrib/ipfilter/test/input/ipv6.3 +++ b/contrib/ipfilter/test/input/ipv6.3 @@ -7,19 +7,19 @@ [in,gif0] 6000 0000 0010 3a40 3ffe 8280 0000 2001 0000 0000 0000 4393 3ffe 8280 0000 2001 -0000 0000 0000 4395 8100 3e77 085c 0038 +0000 0000 0000 4395 8100 3e78 085c 0038 0c06 b73d 1b3d 0d00 [in,gif0] 6000 0000 0010 3a40 3ffe 8280 0000 2001 0000 0000 0000 4394 3ffe 8280 0000 2001 -0000 0000 0000 4395 8300 3e77 085c 0038 +0000 0000 0000 4395 8300 3c77 085c 0038 0c06 b73d 1b3d 0d00 [in,gif0] 6000 0000 0010 3a40 3ffe 8280 0000 2001 0000 0000 0000 4394 3ffe 8280 0000 2001 -0000 0000 0000 4395 8000 3e77 085c 0038 +0000 0000 0000 4395 8000 3f77 085c 0038 0c06 b73d 1b3d 0d00 [in,gif0] diff --git a/contrib/ipfilter/test/input/ipv6.4 b/contrib/ipfilter/test/input/ipv6.4 new file mode 100644 index 00000000000..eb986ae9e36 --- /dev/null +++ b/contrib/ipfilter/test/input/ipv6.4 @@ -0,0 +1,522 @@ +# fe80::20c:29ff:fe13:6899 > fe80::20c:29ff:fe21:5742: frag (0|1448) icmp6: echo request +[in,eth0] +6000 0000 05b0 2c40 fe80 0000 0000 0000 +020c 29ff fe13 6899 fe80 0000 0000 0000 +020c 29ff fe21 5742 3a00 0001 0000 0008 +8000 f400 2c0a 0001 fd38 4a42 9e59 0900 +0809 0a0b 0c0d 0e0f 1011 1213 1415 1617 +1819 1a1b 1c1d 1e1f 2021 2223 2425 2627 +2829 2a2b 2c2d 2e2f 3031 3233 3435 3637 +3839 3a3b 3c3d 3e3f 4041 4243 4445 4647 +4849 4a4b 4c4d 4e4f 5051 5253 5455 5657 +5859 5a5b 5c5d 5e5f 6061 6263 6465 6667 +6869 6a6b 6c6d 6e6f 7071 7273 7475 7677 +7879 7a7b 7c7d 7e7f 8081 8283 8485 8687 +8889 8a8b 8c8d 8e8f 9091 9293 9495 9697 +9899 9a9b 9c9d 9e9f a0a1 a2a3 a4a5 a6a7 +a8a9 aaab acad aeaf b0b1 b2b3 b4b5 b6b7 +b8b9 babb bcbd bebf c0c1 c2c3 c4c5 c6c7 +c8c9 cacb cccd cecf d0d1 d2d3 d4d5 d6d7 +d8d9 dadb dcdd dedf e0e1 e2e3 e4e5 e6e7 +e8e9 eaeb eced eeef f0f1 f2f3 f4f5 f6f7 +f8f9 fafb fcfd feff 0001 0203 0405 0607 +0809 0a0b 0c0d 0e0f 1011 1213 1415 1617 +1819 1a1b 1c1d 1e1f 2021 2223 2425 2627 +2829 2a2b 2c2d 2e2f 3031 3233 3435 3637 +3839 3a3b 3c3d 3e3f 4041 4243 4445 4647 +4849 4a4b 4c4d 4e4f 5051 5253 5455 5657 +5859 5a5b 5c5d 5e5f 6061 6263 6465 6667 +6869 6a6b 6c6d 6e6f 7071 7273 7475 7677 +7879 7a7b 7c7d 7e7f 8081 8283 8485 8687 +8889 8a8b 8c8d 8e8f 9091 9293 9495 9697 +9899 9a9b 9c9d 9e9f a0a1 a2a3 a4a5 a6a7 +a8a9 aaab acad aeaf b0b1 b2b3 b4b5 b6b7 +b8b9 babb bcbd bebf c0c1 c2c3 c4c5 c6c7 +c8c9 cacb cccd cecf d0d1 d2d3 d4d5 d6d7 +d8d9 dadb dcdd dedf e0e1 e2e3 e4e5 e6e7 +e8e9 eaeb eced eeef f0f1 f2f3 f4f5 f6f7 +f8f9 fafb fcfd feff 0001 0203 0405 0607 +0809 0a0b 0c0d 0e0f 1011 1213 1415 1617 +1819 1a1b 1c1d 1e1f 2021 2223 2425 2627 +2829 2a2b 2c2d 2e2f 3031 3233 3435 3637 +3839 3a3b 3c3d 3e3f 4041 4243 4445 4647 +4849 4a4b 4c4d 4e4f 5051 5253 5455 5657 +5859 5a5b 5c5d 5e5f 6061 6263 6465 6667 +6869 6a6b 6c6d 6e6f 7071 7273 7475 7677 +7879 7a7b 7c7d 7e7f 8081 8283 8485 8687 +8889 8a8b 8c8d 8e8f 9091 9293 9495 9697 +9899 9a9b 9c9d 9e9f a0a1 a2a3 a4a5 a6a7 +a8a9 aaab acad aeaf b0b1 b2b3 b4b5 b6b7 +b8b9 babb bcbd bebf c0c1 c2c3 c4c5 c6c7 +c8c9 cacb cccd cecf d0d1 d2d3 d4d5 d6d7 +d8d9 dadb dcdd dedf e0e1 e2e3 e4e5 e6e7 +e8e9 eaeb eced eeef f0f1 f2f3 f4f5 f6f7 +f8f9 fafb fcfd feff 0001 0203 0405 0607 +0809 0a0b 0c0d 0e0f 1011 1213 1415 1617 +1819 1a1b 1c1d 1e1f 2021 2223 2425 2627 +2829 2a2b 2c2d 2e2f 3031 3233 3435 3637 +3839 3a3b 3c3d 3e3f 4041 4243 4445 4647 +4849 4a4b 4c4d 4e4f 5051 5253 5455 5657 +5859 5a5b 5c5d 5e5f 6061 6263 6465 6667 +6869 6a6b 6c6d 6e6f 7071 7273 7475 7677 +7879 7a7b 7c7d 7e7f 8081 8283 8485 8687 +8889 8a8b 8c8d 8e8f 9091 9293 9495 9697 +9899 9a9b 9c9d 9e9f a0a1 a2a3 a4a5 a6a7 +a8a9 aaab acad aeaf b0b1 b2b3 b4b5 b6b7 +b8b9 babb bcbd bebf c0c1 c2c3 c4c5 c6c7 +c8c9 cacb cccd cecf d0d1 d2d3 d4d5 d6d7 +d8d9 dadb dcdd dedf e0e1 e2e3 e4e5 e6e7 +e8e9 eaeb eced eeef f0f1 f2f3 f4f5 f6f7 +f8f9 fafb fcfd feff 0001 0203 0405 0607 +0809 0a0b 0c0d 0e0f 1011 1213 1415 1617 +1819 1a1b 1c1d 1e1f 2021 2223 2425 2627 +2829 2a2b 2c2d 2e2f 3031 3233 3435 3637 +3839 3a3b 3c3d 3e3f 4041 4243 4445 4647 +4849 4a4b 4c4d 4e4f 5051 5253 5455 5657 +5859 5a5b 5c5d 5e5f 6061 6263 6465 6667 +6869 6a6b 6c6d 6e6f 7071 7273 7475 7677 +7879 7a7b 7c7d 7e7f 8081 8283 8485 8687 +8889 8a8b 8c8d 8e8f 9091 9293 9495 9697 +9899 9a9b 9c9d 9e9f a0a1 a2a3 a4a5 a6a7 +a8a9 aaab acad aeaf b0b1 b2b3 b4b5 b6b7 +b8b9 babb bcbd bebf c0c1 c2c3 c4c5 c6c7 +c8c9 cacb cccd cecf d0d1 d2d3 d4d5 d6d7 +d8d9 dadb dcdd dedf e0e1 e2e3 e4e5 e6e7 +e8e9 eaeb eced eeef f0f1 f2f3 f4f5 f6f7 +f8f9 fafb fcfd feff 0001 0203 0405 0607 +0809 0a0b 0c0d 0e0f 1011 1213 1415 1617 +1819 1a1b 1c1d 1e1f 2021 2223 2425 2627 +2829 2a2b 2c2d 2e2f 3031 3233 3435 3637 +3839 3a3b 3c3d 3e3f 4041 4243 4445 4647 +4849 4a4b 4c4d 4e4f 5051 5253 5455 5657 +5859 5a5b 5c5d 5e5f 6061 6263 6465 6667 +6869 6a6b 6c6d 6e6f 7071 7273 7475 7677 +7879 7a7b 7c7d 7e7f 8081 8283 8485 8687 +8889 8a8b 8c8d 8e8f 9091 9293 9495 9697 +9899 9a9b 9c9d 9e9f + +# fe80::20c:29ff:fe13:6899 > fe80::20c:29ff:fe21:5742: frag (1448|160) +[in,eth0] +6000 0000 00a8 2c40 fe80 0000 0000 0000 +020c 29ff fe13 6899 fe80 0000 0000 0000 +020c 29ff fe21 5742 3a00 05a8 0000 0008 +a0a1 a2a3 a4a5 a6a7 a8a9 aaab acad aeaf +b0b1 b2b3 b4b5 b6b7 b8b9 babb bcbd bebf +c0c1 c2c3 c4c5 c6c7 c8c9 cacb cccd cecf +d0d1 d2d3 d4d5 d6d7 d8d9 dadb dcdd dedf +e0e1 e2e3 e4e5 e6e7 e8e9 eaeb eced eeef +f0f1 f2f3 f4f5 f6f7 f8f9 fafb fcfd feff +0001 0203 0405 0607 0809 0a0b 0c0d 0e0f +1011 1213 1415 1617 1819 1a1b 1c1d 1e1f +2021 2223 2425 2627 2829 2a2b 2c2d 2e2f +3031 3233 3435 3637 3839 3a3b 3c3d 3e3f + +# fe80::20c:29ff:fe21:5742 > fe80::20c:29ff:fe13:6899: frag (0|1232) icmp6: echo reply +[out,eth0] +6000 0000 04d8 2c40 fe80 0000 0000 0000 +020c 29ff fe21 5742 fe80 0000 0000 0000 +020c 29ff fe13 6899 3a00 0001 9c56 86dd +8100 f300 2c0a 0001 fd38 4a42 9e59 0900 +0809 0a0b 0c0d 0e0f 1011 1213 1415 1617 +1819 1a1b 1c1d 1e1f 2021 2223 2425 2627 +2829 2a2b 2c2d 2e2f 3031 3233 3435 3637 +3839 3a3b 3c3d 3e3f 4041 4243 4445 4647 +4849 4a4b 4c4d 4e4f 5051 5253 5455 5657 +5859 5a5b 5c5d 5e5f 6061 6263 6465 6667 +6869 6a6b 6c6d 6e6f 7071 7273 7475 7677 +7879 7a7b 7c7d 7e7f 8081 8283 8485 8687 +8889 8a8b 8c8d 8e8f 9091 9293 9495 9697 +9899 9a9b 9c9d 9e9f a0a1 a2a3 a4a5 a6a7 +a8a9 aaab acad aeaf b0b1 b2b3 b4b5 b6b7 +b8b9 babb bcbd bebf c0c1 c2c3 c4c5 c6c7 +c8c9 cacb cccd cecf d0d1 d2d3 d4d5 d6d7 +d8d9 dadb dcdd dedf e0e1 e2e3 e4e5 e6e7 +e8e9 eaeb eced eeef f0f1 f2f3 f4f5 f6f7 +f8f9 fafb fcfd feff 0001 0203 0405 0607 +0809 0a0b 0c0d 0e0f 1011 1213 1415 1617 +1819 1a1b 1c1d 1e1f 2021 2223 2425 2627 +2829 2a2b 2c2d 2e2f 3031 3233 3435 3637 +3839 3a3b 3c3d 3e3f 4041 4243 4445 4647 +4849 4a4b 4c4d 4e4f 5051 5253 5455 5657 +5859 5a5b 5c5d 5e5f 6061 6263 6465 6667 +6869 6a6b 6c6d 6e6f 7071 7273 7475 7677 +7879 7a7b 7c7d 7e7f 8081 8283 8485 8687 +8889 8a8b 8c8d 8e8f 9091 9293 9495 9697 +9899 9a9b 9c9d 9e9f a0a1 a2a3 a4a5 a6a7 +a8a9 aaab acad aeaf b0b1 b2b3 b4b5 b6b7 +b8b9 babb bcbd bebf c0c1 c2c3 c4c5 c6c7 +c8c9 cacb cccd cecf d0d1 d2d3 d4d5 d6d7 +d8d9 dadb dcdd dedf e0e1 e2e3 e4e5 e6e7 +e8e9 eaeb eced eeef f0f1 f2f3 f4f5 f6f7 +f8f9 fafb fcfd feff 0001 0203 0405 0607 +0809 0a0b 0c0d 0e0f 1011 1213 1415 1617 +1819 1a1b 1c1d 1e1f 2021 2223 2425 2627 +2829 2a2b 2c2d 2e2f 3031 3233 3435 3637 +3839 3a3b 3c3d 3e3f 4041 4243 4445 4647 +4849 4a4b 4c4d 4e4f 5051 5253 5455 5657 +5859 5a5b 5c5d 5e5f 6061 6263 6465 6667 +6869 6a6b 6c6d 6e6f 7071 7273 7475 7677 +7879 7a7b 7c7d 7e7f 8081 8283 8485 8687 +8889 8a8b 8c8d 8e8f 9091 9293 9495 9697 +9899 9a9b 9c9d 9e9f a0a1 a2a3 a4a5 a6a7 +a8a9 aaab acad aeaf b0b1 b2b3 b4b5 b6b7 +b8b9 babb bcbd bebf c0c1 c2c3 c4c5 c6c7 +c8c9 cacb cccd cecf d0d1 d2d3 d4d5 d6d7 +d8d9 dadb dcdd dedf e0e1 e2e3 e4e5 e6e7 +e8e9 eaeb eced eeef f0f1 f2f3 f4f5 f6f7 +f8f9 fafb fcfd feff 0001 0203 0405 0607 +0809 0a0b 0c0d 0e0f 1011 1213 1415 1617 +1819 1a1b 1c1d 1e1f 2021 2223 2425 2627 +2829 2a2b 2c2d 2e2f 3031 3233 3435 3637 +3839 3a3b 3c3d 3e3f 4041 4243 4445 4647 +4849 4a4b 4c4d 4e4f 5051 5253 5455 5657 +5859 5a5b 5c5d 5e5f 6061 6263 6465 6667 +6869 6a6b 6c6d 6e6f 7071 7273 7475 7677 +7879 7a7b 7c7d 7e7f 8081 8283 8485 8687 +8889 8a8b 8c8d 8e8f 9091 9293 9495 9697 +9899 9a9b 9c9d 9e9f a0a1 a2a3 a4a5 a6a7 +a8a9 aaab acad aeaf b0b1 b2b3 b4b5 b6b7 +b8b9 babb bcbd bebf c0c1 c2c3 c4c5 c6c7 +c8c9 cacb cccd cecf d0d1 d2d3 d4d5 d6d7 +d8d9 dadb dcdd dedf e0e1 e2e3 e4e5 e6e7 +e8e9 eaeb eced eeef f0f1 f2f3 f4f5 f6f7 +f8f9 fafb fcfd feff 0001 0203 0405 0607 +0809 0a0b 0c0d 0e0f 1011 1213 1415 1617 +1819 1a1b 1c1d 1e1f 2021 2223 2425 2627 +2829 2a2b 2c2d 2e2f 3031 3233 3435 3637 +3839 3a3b 3c3d 3e3f 4041 4243 4445 4647 +4849 4a4b 4c4d 4e4f 5051 5253 5455 5657 +5859 5a5b 5c5d 5e5f 6061 6263 6465 6667 +6869 6a6b 6c6d 6e6f 7071 7273 7475 7677 +7879 7a7b 7c7d 7e7f 8081 8283 8485 8687 +8889 8a8b 8c8d 8e8f 9091 9293 9495 9697 +9899 9a9b 9c9d 9e9f a0a1 a2a3 a4a5 a6a7 +a8a9 aaab acad aeaf b0b1 b2b3 b4b5 b6b7 +b8b9 babb bcbd bebf c0c1 c2c3 c4c5 c6c7 + +# fe80::20c:29ff:fe21:5742 > fe80::20c:29ff:fe13:6899: frag (1232|376) +[out,eth0] +6000 0000 0180 2c40 fe80 0000 0000 0000 +020c 29ff fe21 5742 fe80 0000 0000 0000 +020c 29ff fe13 6899 3a00 04d0 9c56 86dd +c8c9 cacb cccd cecf d0d1 d2d3 d4d5 d6d7 +d8d9 dadb dcdd dedf e0e1 e2e3 e4e5 e6e7 +e8e9 eaeb eced eeef f0f1 f2f3 f4f5 f6f7 +f8f9 fafb fcfd feff 0001 0203 0405 0607 +0809 0a0b 0c0d 0e0f 1011 1213 1415 1617 +1819 1a1b 1c1d 1e1f 2021 2223 2425 2627 +2829 2a2b 2c2d 2e2f 3031 3233 3435 3637 +3839 3a3b 3c3d 3e3f 4041 4243 4445 4647 +4849 4a4b 4c4d 4e4f 5051 5253 5455 5657 +5859 5a5b 5c5d 5e5f 6061 6263 6465 6667 +6869 6a6b 6c6d 6e6f 7071 7273 7475 7677 +7879 7a7b 7c7d 7e7f 8081 8283 8485 8687 +8889 8a8b 8c8d 8e8f 9091 9293 9495 9697 +9899 9a9b 9c9d 9e9f a0a1 a2a3 a4a5 a6a7 +a8a9 aaab acad aeaf b0b1 b2b3 b4b5 b6b7 +b8b9 babb bcbd bebf c0c1 c2c3 c4c5 c6c7 +c8c9 cacb cccd cecf d0d1 d2d3 d4d5 d6d7 +d8d9 dadb dcdd dedf e0e1 e2e3 e4e5 e6e7 +e8e9 eaeb eced eeef f0f1 f2f3 f4f5 f6f7 +f8f9 fafb fcfd feff 0001 0203 0405 0607 +0809 0a0b 0c0d 0e0f 1011 1213 1415 1617 +1819 1a1b 1c1d 1e1f 2021 2223 2425 2627 +2829 2a2b 2c2d 2e2f 3031 3233 3435 3637 +3839 3a3b 3c3d 3e3f + +# fe80::20c:29ff:fe13:6899 > fe80::20c:29ff:fe21:5742: frag (0|1448) icmp6: echo request +[in,eth0] +6000 0000 05b0 2c40 fe80 0000 0000 0000 +020c 29ff fe13 6899 fe80 0000 0000 0000 +020c 29ff fe21 5742 3a00 0001 0000 0009 +8000 80fb 2c0a 0002 fe38 4a42 105e 0900 +0809 0a0b 0c0d 0e0f 1011 1213 1415 1617 +1819 1a1b 1c1d 1e1f 2021 2223 2425 2627 +2829 2a2b 2c2d 2e2f 3031 3233 3435 3637 +3839 3a3b 3c3d 3e3f 4041 4243 4445 4647 +4849 4a4b 4c4d 4e4f 5051 5253 5455 5657 +5859 5a5b 5c5d 5e5f 6061 6263 6465 6667 +6869 6a6b 6c6d 6e6f 7071 7273 7475 7677 +7879 7a7b 7c7d 7e7f 8081 8283 8485 8687 +8889 8a8b 8c8d 8e8f 9091 9293 9495 9697 +9899 9a9b 9c9d 9e9f a0a1 a2a3 a4a5 a6a7 +a8a9 aaab acad aeaf b0b1 b2b3 b4b5 b6b7 +b8b9 babb bcbd bebf c0c1 c2c3 c4c5 c6c7 +c8c9 cacb cccd cecf d0d1 d2d3 d4d5 d6d7 +d8d9 dadb dcdd dedf e0e1 e2e3 e4e5 e6e7 +e8e9 eaeb eced eeef f0f1 f2f3 f4f5 f6f7 +f8f9 fafb fcfd feff 0001 0203 0405 0607 +0809 0a0b 0c0d 0e0f 1011 1213 1415 1617 +1819 1a1b 1c1d 1e1f 2021 2223 2425 2627 +2829 2a2b 2c2d 2e2f 3031 3233 3435 3637 +3839 3a3b 3c3d 3e3f 4041 4243 4445 4647 +4849 4a4b 4c4d 4e4f 5051 5253 5455 5657 +5859 5a5b 5c5d 5e5f 6061 6263 6465 6667 +6869 6a6b 6c6d 6e6f 7071 7273 7475 7677 +7879 7a7b 7c7d 7e7f 8081 8283 8485 8687 +8889 8a8b 8c8d 8e8f 9091 9293 9495 9697 +9899 9a9b 9c9d 9e9f a0a1 a2a3 a4a5 a6a7 +a8a9 aaab acad aeaf b0b1 b2b3 b4b5 b6b7 +b8b9 babb bcbd bebf c0c1 c2c3 c4c5 c6c7 +c8c9 cacb cccd cecf d0d1 d2d3 d4d5 d6d7 +d8d9 dadb dcdd dedf e0e1 e2e3 e4e5 e6e7 +e8e9 eaeb eced eeef f0f1 f2f3 f4f5 f6f7 +f8f9 fafb fcfd feff 0001 0203 0405 0607 +0809 0a0b 0c0d 0e0f 1011 1213 1415 1617 +1819 1a1b 1c1d 1e1f 2021 2223 2425 2627 +2829 2a2b 2c2d 2e2f 3031 3233 3435 3637 +3839 3a3b 3c3d 3e3f 4041 4243 4445 4647 +4849 4a4b 4c4d 4e4f 5051 5253 5455 5657 +5859 5a5b 5c5d 5e5f 6061 6263 6465 6667 +6869 6a6b 6c6d 6e6f 7071 7273 7475 7677 +7879 7a7b 7c7d 7e7f 8081 8283 8485 8687 +8889 8a8b 8c8d 8e8f 9091 9293 9495 9697 +9899 9a9b 9c9d 9e9f a0a1 a2a3 a4a5 a6a7 +a8a9 aaab acad aeaf b0b1 b2b3 b4b5 b6b7 +b8b9 babb bcbd bebf c0c1 c2c3 c4c5 c6c7 +c8c9 cacb cccd cecf d0d1 d2d3 d4d5 d6d7 +d8d9 dadb dcdd dedf e0e1 e2e3 e4e5 e6e7 +e8e9 eaeb eced eeef f0f1 f2f3 f4f5 f6f7 +f8f9 fafb fcfd feff 0001 0203 0405 0607 +0809 0a0b 0c0d 0e0f 1011 1213 1415 1617 +1819 1a1b 1c1d 1e1f 2021 2223 2425 2627 +2829 2a2b 2c2d 2e2f 3031 3233 3435 3637 +3839 3a3b 3c3d 3e3f 4041 4243 4445 4647 +4849 4a4b 4c4d 4e4f 5051 5253 5455 5657 +5859 5a5b 5c5d 5e5f 6061 6263 6465 6667 +6869 6a6b 6c6d 6e6f 7071 7273 7475 7677 +7879 7a7b 7c7d 7e7f 8081 8283 8485 8687 +8889 8a8b 8c8d 8e8f 9091 9293 9495 9697 +9899 9a9b 9c9d 9e9f a0a1 a2a3 a4a5 a6a7 +a8a9 aaab acad aeaf b0b1 b2b3 b4b5 b6b7 +b8b9 babb bcbd bebf c0c1 c2c3 c4c5 c6c7 +c8c9 cacb cccd cecf d0d1 d2d3 d4d5 d6d7 +d8d9 dadb dcdd dedf e0e1 e2e3 e4e5 e6e7 +e8e9 eaeb eced eeef f0f1 f2f3 f4f5 f6f7 +f8f9 fafb fcfd feff 0001 0203 0405 0607 +0809 0a0b 0c0d 0e0f 1011 1213 1415 1617 +1819 1a1b 1c1d 1e1f 2021 2223 2425 2627 +2829 2a2b 2c2d 2e2f 3031 3233 3435 3637 +3839 3a3b 3c3d 3e3f 4041 4243 4445 4647 +4849 4a4b 4c4d 4e4f 5051 5253 5455 5657 +5859 5a5b 5c5d 5e5f 6061 6263 6465 6667 +6869 6a6b 6c6d 6e6f 7071 7273 7475 7677 +7879 7a7b 7c7d 7e7f 8081 8283 8485 8687 +8889 8a8b 8c8d 8e8f 9091 9293 9495 9697 +9899 9a9b 9c9d 9e9f a0a1 a2a3 a4a5 a6a7 +a8a9 aaab acad aeaf b0b1 b2b3 b4b5 b6b7 +b8b9 babb bcbd bebf c0c1 c2c3 c4c5 c6c7 +c8c9 cacb cccd cecf d0d1 d2d3 d4d5 d6d7 +d8d9 dadb dcdd dedf e0e1 e2e3 e4e5 e6e7 +e8e9 eaeb eced eeef f0f1 f2f3 f4f5 f6f7 +f8f9 fafb fcfd feff 0001 0203 0405 0607 +0809 0a0b 0c0d 0e0f 1011 1213 1415 1617 +1819 1a1b 1c1d 1e1f 2021 2223 2425 2627 +2829 2a2b 2c2d 2e2f 3031 3233 3435 3637 +3839 3a3b 3c3d 3e3f 4041 4243 4445 4647 +4849 4a4b 4c4d 4e4f 5051 5253 5455 5657 +5859 5a5b 5c5d 5e5f 6061 6263 6465 6667 +6869 6a6b 6c6d 6e6f 7071 7273 7475 7677 +7879 7a7b 7c7d 7e7f 8081 8283 8485 8687 +8889 8a8b 8c8d 8e8f 9091 9293 9495 9697 +9899 9a9b 9c9d 9e9f + +# fe80::20c:29ff:fe13:6899 > fe80::20c:29ff:fe21:5742: frag (1448|160) +[in,eth0] +6000 0000 00a8 2c40 fe80 0000 0000 0000 +020c 29ff fe13 6899 fe80 0000 0000 0000 +020c 29ff fe21 5742 3a00 05a8 0000 0009 +a0a1 a2a3 a4a5 a6a7 a8a9 aaab acad aeaf +b0b1 b2b3 b4b5 b6b7 b8b9 babb bcbd bebf +c0c1 c2c3 c4c5 c6c7 c8c9 cacb cccd cecf +d0d1 d2d3 d4d5 d6d7 d8d9 dadb dcdd dedf +e0e1 e2e3 e4e5 e6e7 e8e9 eaeb eced eeef +f0f1 f2f3 f4f5 f6f7 f8f9 fafb fcfd feff +0001 0203 0405 0607 0809 0a0b 0c0d 0e0f +1011 1213 1415 1617 1819 1a1b 1c1d 1e1f +2021 2223 2425 2627 2829 2a2b 2c2d 2e2f +3031 3233 3435 3637 3839 3a3b 3c3d 3e3f + +# fe80::20c:29ff:fe21:5742 > fe80::20c:29ff:fe13:6899: frag (0|1232) icmp6: echo reply +[out,eth0] +6000 0000 04d8 2c40 fe80 0000 0000 0000 +020c 29ff fe21 5742 fe80 0000 0000 0000 +020c 29ff fe13 6899 3a00 0001 9889 f4c1 +8100 7ffb 2c0a 0002 fe38 4a42 105e 0900 +0809 0a0b 0c0d 0e0f 1011 1213 1415 1617 +1819 1a1b 1c1d 1e1f 2021 2223 2425 2627 +2829 2a2b 2c2d 2e2f 3031 3233 3435 3637 +3839 3a3b 3c3d 3e3f 4041 4243 4445 4647 +4849 4a4b 4c4d 4e4f 5051 5253 5455 5657 +5859 5a5b 5c5d 5e5f 6061 6263 6465 6667 +6869 6a6b 6c6d 6e6f 7071 7273 7475 7677 +7879 7a7b 7c7d 7e7f 8081 8283 8485 8687 +8889 8a8b 8c8d 8e8f 9091 9293 9495 9697 +9899 9a9b 9c9d 9e9f a0a1 a2a3 a4a5 a6a7 +a8a9 aaab acad aeaf b0b1 b2b3 b4b5 b6b7 +b8b9 babb bcbd bebf c0c1 c2c3 c4c5 c6c7 +c8c9 cacb cccd cecf d0d1 d2d3 d4d5 d6d7 +d8d9 dadb dcdd dedf e0e1 e2e3 e4e5 e6e7 +e8e9 eaeb eced eeef f0f1 f2f3 f4f5 f6f7 +f8f9 fafb fcfd feff 0001 0203 0405 0607 +0809 0a0b 0c0d 0e0f 1011 1213 1415 1617 +1819 1a1b 1c1d 1e1f 2021 2223 2425 2627 +2829 2a2b 2c2d 2e2f 3031 3233 3435 3637 +3839 3a3b 3c3d 3e3f 4041 4243 4445 4647 +4849 4a4b 4c4d 4e4f 5051 5253 5455 5657 +5859 5a5b 5c5d 5e5f 6061 6263 6465 6667 +6869 6a6b 6c6d 6e6f 7071 7273 7475 7677 +7879 7a7b 7c7d 7e7f 8081 8283 8485 8687 +8889 8a8b 8c8d 8e8f 9091 9293 9495 9697 +9899 9a9b 9c9d 9e9f a0a1 a2a3 a4a5 a6a7 +a8a9 aaab acad aeaf b0b1 b2b3 b4b5 b6b7 +b8b9 babb bcbd bebf c0c1 c2c3 c4c5 c6c7 +c8c9 cacb cccd cecf d0d1 d2d3 d4d5 d6d7 +d8d9 dadb dcdd dedf e0e1 e2e3 e4e5 e6e7 +e8e9 eaeb eced eeef f0f1 f2f3 f4f5 f6f7 +f8f9 fafb fcfd feff 0001 0203 0405 0607 +0809 0a0b 0c0d 0e0f 1011 1213 1415 1617 +1819 1a1b 1c1d 1e1f 2021 2223 2425 2627 +2829 2a2b 2c2d 2e2f 3031 3233 3435 3637 +3839 3a3b 3c3d 3e3f 4041 4243 4445 4647 +4849 4a4b 4c4d 4e4f 5051 5253 5455 5657 +5859 5a5b 5c5d 5e5f 6061 6263 6465 6667 +6869 6a6b 6c6d 6e6f 7071 7273 7475 7677 +7879 7a7b 7c7d 7e7f 8081 8283 8485 8687 +8889 8a8b 8c8d 8e8f 9091 9293 9495 9697 +9899 9a9b 9c9d 9e9f a0a1 a2a3 a4a5 a6a7 +a8a9 aaab acad aeaf b0b1 b2b3 b4b5 b6b7 +b8b9 babb bcbd bebf c0c1 c2c3 c4c5 c6c7 +c8c9 cacb cccd cecf d0d1 d2d3 d4d5 d6d7 +d8d9 dadb dcdd dedf e0e1 e2e3 e4e5 e6e7 +e8e9 eaeb eced eeef f0f1 f2f3 f4f5 f6f7 +f8f9 fafb fcfd feff 0001 0203 0405 0607 +0809 0a0b 0c0d 0e0f 1011 1213 1415 1617 +1819 1a1b 1c1d 1e1f 2021 2223 2425 2627 +2829 2a2b 2c2d 2e2f 3031 3233 3435 3637 +3839 3a3b 3c3d 3e3f 4041 4243 4445 4647 +4849 4a4b 4c4d 4e4f 5051 5253 5455 5657 +5859 5a5b 5c5d 5e5f 6061 6263 6465 6667 +6869 6a6b 6c6d 6e6f 7071 7273 7475 7677 +7879 7a7b 7c7d 7e7f 8081 8283 8485 8687 +8889 8a8b 8c8d 8e8f 9091 9293 9495 9697 +9899 9a9b 9c9d 9e9f a0a1 a2a3 a4a5 a6a7 +a8a9 aaab acad aeaf b0b1 b2b3 b4b5 b6b7 +b8b9 babb bcbd bebf c0c1 c2c3 c4c5 c6c7 +c8c9 cacb cccd cecf d0d1 d2d3 d4d5 d6d7 +d8d9 dadb dcdd dedf e0e1 e2e3 e4e5 e6e7 +e8e9 eaeb eced eeef f0f1 f2f3 f4f5 f6f7 +f8f9 fafb fcfd feff 0001 0203 0405 0607 +0809 0a0b 0c0d 0e0f 1011 1213 1415 1617 +1819 1a1b 1c1d 1e1f 2021 2223 2425 2627 +2829 2a2b 2c2d 2e2f 3031 3233 3435 3637 +3839 3a3b 3c3d 3e3f 4041 4243 4445 4647 +4849 4a4b 4c4d 4e4f 5051 5253 5455 5657 +5859 5a5b 5c5d 5e5f 6061 6263 6465 6667 +6869 6a6b 6c6d 6e6f 7071 7273 7475 7677 +7879 7a7b 7c7d 7e7f 8081 8283 8485 8687 +8889 8a8b 8c8d 8e8f 9091 9293 9495 9697 +9899 9a9b 9c9d 9e9f a0a1 a2a3 a4a5 a6a7 +a8a9 aaab acad aeaf b0b1 b2b3 b4b5 b6b7 +b8b9 babb bcbd bebf c0c1 c2c3 c4c5 c6c7 + +# fe80::20c:29ff:fe21:5742 > fe80::20c:29ff:fe13:6899: frag (1232|376) +[out,eth0] +6000 0000 0180 2c40 fe80 0000 0000 0000 +020c 29ff fe21 5742 fe80 0000 0000 0000 +020c 29ff fe13 6899 3a00 04d0 9889 f4c1 +c8c9 cacb cccd cecf d0d1 d2d3 d4d5 d6d7 +d8d9 dadb dcdd dedf e0e1 e2e3 e4e5 e6e7 +e8e9 eaeb eced eeef f0f1 f2f3 f4f5 f6f7 +f8f9 fafb fcfd feff 0001 0203 0405 0607 +0809 0a0b 0c0d 0e0f 1011 1213 1415 1617 +1819 1a1b 1c1d 1e1f 2021 2223 2425 2627 +2829 2a2b 2c2d 2e2f 3031 3233 3435 3637 +3839 3a3b 3c3d 3e3f 4041 4243 4445 4647 +4849 4a4b 4c4d 4e4f 5051 5253 5455 5657 +5859 5a5b 5c5d 5e5f 6061 6263 6465 6667 +6869 6a6b 6c6d 6e6f 7071 7273 7475 7677 +7879 7a7b 7c7d 7e7f 8081 8283 8485 8687 +8889 8a8b 8c8d 8e8f 9091 9293 9495 9697 +9899 9a9b 9c9d 9e9f a0a1 a2a3 a4a5 a6a7 +a8a9 aaab acad aeaf b0b1 b2b3 b4b5 b6b7 +b8b9 babb bcbd bebf c0c1 c2c3 c4c5 c6c7 +c8c9 cacb cccd cecf d0d1 d2d3 d4d5 d6d7 +d8d9 dadb dcdd dedf e0e1 e2e3 e4e5 e6e7 +e8e9 eaeb eced eeef f0f1 f2f3 f4f5 f6f7 +f8f9 fafb fcfd feff 0001 0203 0405 0607 +0809 0a0b 0c0d 0e0f 1011 1213 1415 1617 +1819 1a1b 1c1d 1e1f 2021 2223 2425 2627 +2829 2a2b 2c2d 2e2f 3031 3233 3435 3637 +3839 3a3b 3c3d 3e3f + +# frag: [0-7:nh][8-15:res][16-31:off][32-64:id] +# Case 4: ipv6,fragment[id=10,off=0,m=1],tcp +[in,eth0] +600a af74 0038 2c40 +fe80 0000 0000 0000 020c 29ff fe21 5742 +fe80 0000 0000 0000 020c 29ff fe6e eb5a +0600 0001 0000 0010 +fff3 0017 52ac fbab 0000 0000 c002 8000 d36b 0000 +0204 05a0 0103 0300 0402 0101 0101 080a 0000 0000 0000 0000 0000 0000 + +# Case 5: ipv6,fragment[id=10,off=5,m=1],data +[in,eth0] +600a af74 0010 2c40 +fe80 0000 0000 0000 020c 29ff fe21 5742 +fe80 0000 0000 0000 020c 29ff fe6e eb5a +0600 0030 0000 0010 +0000 0000 0000 0000 + +# Case 3: ipv6,fragment[id=10,off=1,m=0],tcp +[in,eth0] +600a af74 0034 2c40 +fe80 0000 0000 0000 020c 29ff fe21 5742 +fe80 0000 0000 0000 020c 29ff fe6e eb5a +0600 0008 0000 0010 +0000 0000 b002 8000 d36b 0000 +0204 05a0 0103 0300 0402 0101 0101 080a 0000 0000 0000 0000 + +# Case 1: ipv6,fragment[id=11,off=0,m=1],hopopts,ah[next=dstopts] +[in,eth0] +600a af74 0020 2c40 +fe80 0000 0000 0000 020c 29ff fe21 5742 +fe80 0000 0000 0000 020c 29ff fe6e eb5a +0000 0001 0000 0011 +3300 0000 0000 0000 +3c01 0000 0000 0000 0000 0000 0000 0000 + +# Case 2: ipv6,fragment[id=11,off=3,m=0],dstopts,hop,tcp +[in,eth0] +600a af74 002c 2c40 +fe80 0000 0000 0000 020c 29ff fe21 5742 +fe80 0000 0000 0000 020c 29ff fe6e eb5a +3c00 0008 0000 0011 +0000 0000 0000 0000 +0600 0000 0000 0000 +fff3 0017 52ac fbab 0000 0000 5002 8000 d36b 0000 + +# Case 4: ipv6,fragment[id=10,off=0,m=1],tcp +[out,eth0] +6000 0000 001c 2c40 +fe80 0000 0000 0000 020c 29ff fe6e eb5a +fe80 0000 0000 0000 020c 29ff fe21 5742 +0600 0001 0000 0010 +0017 fff3 0000 0000 52ac fbac 5014 0000 cd26 0000 + +# Normal TCP Reset +[out,eth0] +6000 0000 0014 0640 +fe80 0000 0000 0000 020c 29ff fe6e eb5a +fe80 0000 0000 0000 020c 29ff fe21 5742 +0017 fff3 0000 0000 52ac fbac 5014 0000 cd26 0000 + +# Case 4: ipv6,fragment[id=12,off=0,m=1],tcp +[in,eth0] +600a af74 0038 2c40 +fe80 0000 0000 0000 020c 29ff fe21 5742 +fe80 0000 0000 0000 020c 29ff fe6e eb5a +0600 0001 0000 0012 +fff3 0017 52ac fbab 0000 0000 c002 8000 d36b 0000 +0204 05a0 0103 0300 0402 0101 0101 080a 0000 0000 0000 0000 0000 0000 + diff --git a/contrib/ipfilter/test/input/ipv6.6 b/contrib/ipfilter/test/input/ipv6.6 index 82efeac624d..fffbad28ccd 100644 --- a/contrib/ipfilter/test/input/ipv6.6 +++ b/contrib/ipfilter/test/input/ipv6.6 @@ -15,3 +15,17 @@ ef00 1001 2002 0001 0000 0000 0000 0070 1100 0008 0000 0001 0000 0000 0000 0000 0000 0000 0000 0000 +[out,gif0] +6000 0000 001e 2c01 +ef00 1001 2002 0001 0000 0000 0000 0070 +2001 1002 3333 0001 0000 0000 0000 0001 +1100 0001 0000 0001 +0000 0000 0000 0000 0000 0000 0000 + +[out,gif0] +6000 0000 0020 2c01 +ef00 1001 2002 0001 0000 0000 0000 0070 +2001 1002 3333 0001 0000 0000 0000 0001 +1100 001c 0000 0001 +0000 0000 0000 0000 0000 0000 0000 0000 + diff --git a/contrib/ipfilter/test/input/n10 b/contrib/ipfilter/test/input/n10 index 321ed0bf944..1e919cc4b34 100644 --- a/contrib/ipfilter/test/input/n10 +++ b/contrib/ipfilter/test/input/n10 @@ -1,6 +1,6 @@ # TCP SYN packet with an MSS option [out,ppp0] -4500 002c 10c9 4000 ff06 3289 c0a8 0103 -96cb e002 8032 0015 bd6b c9c8 0000 0000 +4500 002c 10c9 4000 ff06 3289 c0a8 0103 96cb e002 +8032 0015 bd6b c9c8 0000 0000 6002 2238 35f9 0000 0204 05b4 diff --git a/contrib/ipfilter/test/input/n100 b/contrib/ipfilter/test/input/n100 new file mode 100644 index 00000000000..94ff8c4be6e --- /dev/null +++ b/contrib/ipfilter/test/input/n100 @@ -0,0 +1,8 @@ +out on zx0 255 1.1.1.1 2.3.2.3 +out on zx0 255 1.1.1.1 2.2.2.3 +out on zx0 255 1.1.1.2 2.2.2.3 +out on zx0 255 1.2.1.2 2.2.2.3 +out on zx0 255 1.1.1.1 2.2.2.4 +out on zx0 255 1.1.1.1 2.2.2.3 +out on zx0 tcp 1.1.1.1,101 2.3.2.3,203 +out on zx0 tcp 1.1.1.1,101 2.2.2.3,203 diff --git a/contrib/ipfilter/test/input/n101 b/contrib/ipfilter/test/input/n101 new file mode 100644 index 00000000000..94ff8c4be6e --- /dev/null +++ b/contrib/ipfilter/test/input/n101 @@ -0,0 +1,8 @@ +out on zx0 255 1.1.1.1 2.3.2.3 +out on zx0 255 1.1.1.1 2.2.2.3 +out on zx0 255 1.1.1.2 2.2.2.3 +out on zx0 255 1.2.1.2 2.2.2.3 +out on zx0 255 1.1.1.1 2.2.2.4 +out on zx0 255 1.1.1.1 2.2.2.3 +out on zx0 tcp 1.1.1.1,101 2.3.2.3,203 +out on zx0 tcp 1.1.1.1,101 2.2.2.3,203 diff --git a/contrib/ipfilter/test/input/n102 b/contrib/ipfilter/test/input/n102 new file mode 100644 index 00000000000..94ff8c4be6e --- /dev/null +++ b/contrib/ipfilter/test/input/n102 @@ -0,0 +1,8 @@ +out on zx0 255 1.1.1.1 2.3.2.3 +out on zx0 255 1.1.1.1 2.2.2.3 +out on zx0 255 1.1.1.2 2.2.2.3 +out on zx0 255 1.2.1.2 2.2.2.3 +out on zx0 255 1.1.1.1 2.2.2.4 +out on zx0 255 1.1.1.1 2.2.2.3 +out on zx0 tcp 1.1.1.1,101 2.3.2.3,203 +out on zx0 tcp 1.1.1.1,101 2.2.2.3,203 diff --git a/contrib/ipfilter/test/input/n103 b/contrib/ipfilter/test/input/n103 new file mode 100644 index 00000000000..7957799d071 --- /dev/null +++ b/contrib/ipfilter/test/input/n103 @@ -0,0 +1,8 @@ +out on zx0 tcp 1.1.1.1,101 2.3.2.3,203 +out on zx0 tcp 1.1.1.1,101 2.2.2.3,203 +out on zx0 tcp 1.1.1.1,101 2.2.2.3,203 +out on zx0 tcp 1.1.1.2,101 2.2.2.3,203 +out on zx0 tcp 10.10.10.10,101 2.2.2.3,203 +out on zx0 tcp 5.5.5.5,101 2.2.2.3,203 +in on zx0 tcp 2.2.2.3,4000 4.4.4.4,1000 +out on zx0 tcp 7.7.7.7,101 2.2.2.3,203 diff --git a/contrib/ipfilter/test/input/n104 b/contrib/ipfilter/test/input/n104 new file mode 100644 index 00000000000..bb46b285608 --- /dev/null +++ b/contrib/ipfilter/test/input/n104 @@ -0,0 +1,48 @@ +[out,zx0] +4500 0028 0001 0000 ff06 b5c9 0101 0101 0202 0202 +0065 00cb 0000 0001 1000 0001 5010 2000 789d 0000 + +[in,zx0] +4500 0028 0002 0000 ff06 b1c2 0606 0001 0404 0001 +0fa0 03e8 0000 0001 1000 0001 5010 2000 623f 0000 + +[out,zx0] +4500 0028 0003 0000 ff06 b5c7 0101 0101 0202 0202 +0066 00cb 0000 0001 1000 0001 5010 2000 789c 0000 + +[in,zx0] +4500 0028 0004 0000 ff06 b1bf 0606 0001 0404 0002 +0fa0 03e8 0000 0001 1000 0001 5010 2000 623e 0000 + +[out,zx0] +4500 0028 0005 0000 ff06 b5c5 0101 0101 0202 0202 +0067 00cb 0000 0001 1000 0001 5010 2000 789b 0000 + +[in,zx0] +4500 0028 0006 0000 ff06 b1bd 0606 0001 0404 0002 +0fa0 03e9 0000 0001 1000 0001 5010 2000 623d 0000 + +[out,zx0] +4500 0028 0007 0000 ff06 b5c3 0101 0101 0202 0202 +0068 00cb 0000 0001 1000 0001 5010 2000 789a 0000 + +[in,zx0] +4500 0028 0008 0000 ff06 b1ba 0606 0002 0404 0002 +0fa0 03e9 0000 0001 1000 0001 5010 2000 623c 0000 + +[out,zx0] +4500 0028 0009 0000 ff06 b5c1 0101 0101 0202 0202 +0069 00cb 0000 0001 1000 0001 5010 2000 7899 0000 + +[in,zx0] +4500 0028 000a 0000 ff06 b1b8 0606 0002 0404 0002 +0fa1 03e9 0000 0001 1000 0001 5010 2000 623b 0000 + +[out,zx0] +4500 0028 000b 0000 ff06 b5bf 0101 0101 0202 0202 +006a 00cb 0000 0001 1000 0001 5010 2000 7898 0000 + +[in,zx0] +4500 0028 000c 0000 ff06 b1b5 0606 0002 0404 0003 +0fa1 03e9 0000 0001 1000 0001 5010 2000 623a 0000 + diff --git a/contrib/ipfilter/test/input/n105 b/contrib/ipfilter/test/input/n105 new file mode 100644 index 00000000000..63b68f0301c --- /dev/null +++ b/contrib/ipfilter/test/input/n105 @@ -0,0 +1,8 @@ +[in,zx0] +4500 0028 0001 0000 ff06 b5c9 0101 0101 0202 0202 +0065 0050 0000 0001 1000 0001 5010 2000 7918 0000 + +[out,zx0] +4500 0028 0001 0000 ff06 adc0 0606 0001 0404 0404 +0c38 03e8 0000 0001 1000 0001 5010 2000 61a4 0000 + diff --git a/contrib/ipfilter/test/input/n106 b/contrib/ipfilter/test/input/n106 new file mode 100644 index 00000000000..4e933785606 --- /dev/null +++ b/contrib/ipfilter/test/input/n106 @@ -0,0 +1,8 @@ +[out,zx0] +4500 0028 0001 0000 ff06 b5c9 0101 0101 0202 0202 +0065 0050 0000 0001 1000 0001 5010 2000 7918 0000 + +[in,zx0] +4500 0028 0001 0000 ff06 adc0 0606 0001 0404 0404 +0c38 03e8 0000 0001 1000 0001 5010 2000 61a4 0000 + diff --git a/contrib/ipfilter/test/input/n10_6 b/contrib/ipfilter/test/input/n10_6 new file mode 100644 index 00000000000..5c1f5af8596 --- /dev/null +++ b/contrib/ipfilter/test/input/n10_6 @@ -0,0 +1,6 @@ +# TCP SYN packet with an MSS option +[out,ppp0] +6000 0000 0018 06ff c0a8 0100 0000 0000 0000 0000 0000 0003 96cb e000 0000 0000 0000 0000 0000 0002 +8032 0015 bd6b c9c8 0000 0000 +6002 2238 35f9 0000 0204 05b4 + diff --git a/contrib/ipfilter/test/input/n11_6 b/contrib/ipfilter/test/input/n11_6 new file mode 100644 index 00000000000..128e45ae5c5 --- /dev/null +++ b/contrib/ipfilter/test/input/n11_6 @@ -0,0 +1,16 @@ +out6 on zx0 255 10:1:1::0 10:1:1::2 +out6 on zx0 255 10:1:1::1 10:1:1::2 +out6 on zx0 255 10:1:1::2 10:1:1::1 +out6 on zx0 255 10::2:2:1 10:1:2::1 +out6 on zx0 255 10::2:2:2 10:1:2::1 +in6 on zx0 255 10:1:1::1 10:1:1::2 +in6 on zx0 255 10:1:1::2 10:1:1::1 +in6 on zx0 255 10::2:2:1 10::2:1:1 +in6 on zx0 255 10::2:2:2 10::2:1:1 +in6 on zx0 255 10::2:2:3 10:1:1::1 +in6 on zx0 255 10::2:3:4 10::2:2:2 +in6 on zx0 255 10:1:1::1 10::2:2:2 +in6 on zx0 255 10:1:1::2 10::2:2:2 +in6 on zx0 255 10:1:1::0 10::3:4:5 +in6 on zx0 255 10:1:1::1 10::3:4:5 +in6 on zx0 255 10:1:1::2 10::3:4:5 diff --git a/contrib/ipfilter/test/input/n12 b/contrib/ipfilter/test/input/n12 index fb4d76de475..16e479ed196 100644 --- a/contrib/ipfilter/test/input/n12 +++ b/contrib/ipfilter/test/input/n12 @@ -1,18 +1,18 @@ [out,le0=192.168.1.188] -4510 0040 2020 4000 4006 17e1 c0a8 7e53 -c0a8 0303 12c2 0017 4e33 298e 0000 0000 +4510 0040 2020 4000 4006 17e1 c0a8 7e53 c0a8 0303 +12c2 0017 4e33 298e 0000 0000 b002 4000 07af 0000 0204 05b4 0101 0402 0103 0300 0101 080a 0c72 549e 0000 0000 [in,le0] -4500 003c 00b0 4000 fe06 f5fb c0a8 0303 -c0a8 01bc 0017 2710 f674 e02c 4e33 298f +4500 003c 00b0 4000 fe06 f5fb c0a8 0303 c0a8 01bc +0017 2710 f674 e02c 4e33 298f a012 2798 e317 0000 0101 080a 2c05 b797 0c72 549e 0103 0300 0204 05b4 [out,le0] -4510 0034 493b 4000 4006 eed1 c0a8 7e53 -c0a8 0303 12c2 0017 4e33 298f f674 e02d +4510 0034 493b 4000 4006 eed1 c0a8 7e53 c0a8 0303 +12c2 0017 4e33 298f f674 e02d 8010 4000 8e2a 0000 0101 080a 0c72 549e 2c05 b797 diff --git a/contrib/ipfilter/test/input/n12_6 b/contrib/ipfilter/test/input/n12_6 new file mode 100644 index 00000000000..8583acbd719 --- /dev/null +++ b/contrib/ipfilter/test/input/n12_6 @@ -0,0 +1,18 @@ +[out,le0=c0a8:0100::bc] +6000 0000 002c 0640 c0a8 7e00 0000 0000 0000 0000 0000 0053 c0a8 0300 0000 0000 0000 0000 0000 0003 +12c2 0017 4e33 298e 0000 0000 +b002 4000 07af 0000 0204 05b4 0101 0402 +0103 0300 0101 080a 0c72 549e 0000 0000 + +[in,le0] +6000 0000 0028 06fe c0a8 0300 0000 0000 0000 0000 0000 0003 c0a8 0100 0000 0000 0000 0000 0000 00bc +0017 2710 f674 e02c 4e33 298f +a012 2798 e317 0000 0101 080a 2c05 b797 +0c72 549e 0103 0300 0204 05b4 + +[out,le0] +6000 0000 0020 0640 c0a8 7e00 0000 0000 0000 0000 0000 0053 c0a8 0300 0000 0000 0000 0000 0000 0003 +12c2 0017 4e33 298f f674 e02d +8010 4000 8e2a 0000 0101 080a 0c72 549e +2c05 b797 + diff --git a/contrib/ipfilter/test/input/n13_6 b/contrib/ipfilter/test/input/n13_6 new file mode 100644 index 00000000000..54b262dc8ea --- /dev/null +++ b/contrib/ipfilter/test/input/n13_6 @@ -0,0 +1,4 @@ +out6 on le0 192:168:1::1 150:1:1::1 +out6 on le0 192:168:1::1 150:1:1::2 +out6 on le0 192:168:1::2 150:1:1::2 +out6 on le0 192:168:1::3 150:1:1::1 diff --git a/contrib/ipfilter/test/input/n14_6 b/contrib/ipfilter/test/input/n14_6 new file mode 100644 index 00000000000..f5dd5d359f6 --- /dev/null +++ b/contrib/ipfilter/test/input/n14_6 @@ -0,0 +1,4 @@ +in6 on gre0 tcp 10::2:2:5,2000 203:1:1::1,80 +in6 on gre0 tcp 10::2:2:6,2000 203:1:1::1,80 +in6 on gre0 tcp 10::2:2:7,2000 203:1:1::1,80 +in6 on gre0 tcp 10::2:2:5,2001 203:1:1::1,80 diff --git a/contrib/ipfilter/test/input/n15 b/contrib/ipfilter/test/input/n15 new file mode 100644 index 00000000000..715848e764d --- /dev/null +++ b/contrib/ipfilter/test/input/n15 @@ -0,0 +1,2 @@ +in on le0 tcp 9.9.9.9,10011 5.5.5.5,80 +in on le0 tcp 9.9.9.9,10011 2.2.2.2,80 diff --git a/contrib/ipfilter/test/input/n15_6 b/contrib/ipfilter/test/input/n15_6 new file mode 100644 index 00000000000..4a56138724b --- /dev/null +++ b/contrib/ipfilter/test/input/n15_6 @@ -0,0 +1,2 @@ +in6 on le0 tcp 9:9:9::9,10011 5:5::5:5,80 +in6 on le0 tcp 9:9:9::9,10011 2::2:2:2,80 diff --git a/contrib/ipfilter/test/input/n16 b/contrib/ipfilter/test/input/n16 index 2e77e40d767..ad09a45f673 100644 --- a/contrib/ipfilter/test/input/n16 +++ b/contrib/ipfilter/test/input/n16 @@ -8,33 +8,33 @@ a5a5 a5a5 a5a5 a5a5 a5a5 a5a5 a5a5 a5a5 a5a5 a5a5 a5a5 a5a5 a5a5 a5a5 [out,vlan2] -4520 0068 17e4 0000 6a11 ccba c05b ac33 -ac1f 5318 1194 07dd 0054 0000 a5a5 a5a5 +4520 0068 17e4 0000 6a11 ccba c05b ac33 ac1f 5318 +1194 07dd 0054 0000 a5a5 a5a5 a5a5 a5a5 a5a5 a5a5 a5a5 a5a5 a5a5 a5a5 a5a5 a5a5 a5a5 a5a5 a5a5 a5a5 a5a5 a5a5 a5a5 a5a5 a5a5 a5a5 a5a5 a5a5 a5a5 a5a5 a5a5 a5a5 a5a5 a5a5 a5a5 a5a5 -a5a5 a5a5 a5a5 a5a5 +a5a5 a5a5 a5a5 a5a5 a5a5 a5a5 [in,vlan2] -4500 0084 ee0f 0000 8001 e0a2 ac1f 5318 -c05b ac33 0303 4ca1 0000 0000 4520 0068 -17e4 0000 6a11 ccba c05b ac33 ac1f 5318 -1194 07dd 0054 0000 a5a5 a5a5 a5a5 a5a5 +4500 0084 ee0f 0000 8001 e0a2 ac1f 5318 c05b ac33 +0303 4ca1 0000 0000 +4520 0068 17e4 0000 6a11 ccba c05b ac33 ac1f 5318 +1194 07dd 0054 0000 a5a5 a5a5 a5a5 a5a5 a5a5 a5a5 a5a5 a5a5 a5a5 a5a5 a5a5 a5a5 a5a5 a5a5 a5a5 a5a5 a5a5 a5a5 a5a5 a5a5 a5a5 a5a5 a5a5 a5a5 a5a5 a5a5 a5a5 a5a5 a5a5 a5a5 a5a5 a5a5 -a5a5 a5a5 +a5a5 a5a5 a5a5 a5a5 a5a5 a5a5 [out,vlan0] -4500 0084 ee0f 0000 8001 e0a2 ac1f 5318 -c05b ac33 0303 4ca1 0000 0000 4520 0068 -17e4 0000 6a11 ccba c05b ac33 ac1f 5318 -1194 07dd 0054 0000 a5a5 a5a5 a5a5 a5a5 +4500 0084 ee0f 0000 8001 e0a2 ac1f 5318 c05b ac33 +0303 4ca1 0000 0000 +4520 0068 17e4 0000 6a11 ccba c05b ac33 ac1f 5318 +1194 07dd 0054 0000 a5a5 a5a5 a5a5 a5a5 a5a5 a5a5 a5a5 a5a5 a5a5 a5a5 a5a5 a5a5 a5a5 a5a5 a5a5 a5a5 a5a5 a5a5 a5a5 a5a5 a5a5 a5a5 a5a5 a5a5 a5a5 a5a5 a5a5 a5a5 a5a5 a5a5 a5a5 a5a5 -a5a5 a5a5 +a5a5 a5a5 a5a5 a5a5 a5a5 a5a5 diff --git a/contrib/ipfilter/test/input/n17 b/contrib/ipfilter/test/input/n17 new file mode 100644 index 00000000000..29709de53f6 --- /dev/null +++ b/contrib/ipfilter/test/input/n17 @@ -0,0 +1,24 @@ +[out,zx0] +4500 00a0 0000 0100 3f06 7555 0101 0101 0201 0101 +0401 0019 0000 0000 0000 0000 5010 2000 86b7 0000 +0000 0000 0000 0000 0000 0000 0000 0000 +0000 0000 0000 0000 0000 0000 0000 0000 +0000 0000 0000 0000 0000 0000 0000 0000 +0000 0000 0000 0000 0000 0000 0000 0000 +0000 0000 0000 0000 0000 0000 0000 0000 +0000 0000 0000 0000 0000 0000 0000 0000 +0000 0000 0000 0000 0000 0000 0000 0000 +0000 0000 0000 0000 + +[in,zx0] +4500 00a0 0000 0100 3f06 7553 0201 0101 0101 0103 +0401 0019 0000 0000 0000 0000 5010 2000 86b7 0000 +0000 0000 0000 0000 0000 0000 0000 0000 +0000 0000 0000 0000 0000 0000 0000 0000 +0000 0000 0000 0000 0000 0000 0000 0000 +0000 0000 0000 0000 0000 0000 0000 0000 +0000 0000 0000 0000 0000 0000 0000 0000 +0000 0000 0000 0000 0000 0000 0000 0000 +0000 0000 0000 0000 0000 0000 0000 0000 +0000 0000 0000 0000 + diff --git a/contrib/ipfilter/test/input/n17_6 b/contrib/ipfilter/test/input/n17_6 new file mode 100644 index 00000000000..a176c158ccc --- /dev/null +++ b/contrib/ipfilter/test/input/n17_6 @@ -0,0 +1,24 @@ +[out,zx0] +6000 0000 008c 063f 0001 0000 0000 0000 0000 0001 0001 0001 0002 0000 0000 0000 0000 0001 0001 0001 +0401 0019 0000 0000 0000 0000 5010 2000 86b7 0000 +0000 0000 0000 0000 0000 0000 0000 0000 +0000 0000 0000 0000 0000 0000 0000 0000 +0000 0000 0000 0000 0000 0000 0000 0000 +0000 0000 0000 0000 0000 0000 0000 0000 +0000 0000 0000 0000 0000 0000 0000 0000 +0000 0000 0000 0000 0000 0000 0000 0000 +0000 0000 0000 0000 0000 0000 0000 0000 +0000 0000 0000 0000 + +[in,zx0] +6000 0000 008c 063f 0002 0000 0000 0000 0000 0001 0001 0001 0001 0000 0000 0000 0000 0001 0001 0003 +0401 0019 0000 0000 0000 0000 5010 2000 86b7 0000 +0000 0000 0000 0000 0000 0000 0000 0000 +0000 0000 0000 0000 0000 0000 0000 0000 +0000 0000 0000 0000 0000 0000 0000 0000 +0000 0000 0000 0000 0000 0000 0000 0000 +0000 0000 0000 0000 0000 0000 0000 0000 +0000 0000 0000 0000 0000 0000 0000 0000 +0000 0000 0000 0000 0000 0000 0000 0000 +0000 0000 0000 0000 + diff --git a/contrib/ipfilter/test/input/n18 b/contrib/ipfilter/test/input/n18 new file mode 100644 index 00000000000..a7a610cf8fb --- /dev/null +++ b/contrib/ipfilter/test/input/n18 @@ -0,0 +1,8 @@ +out on z0 tcp 2.2.2.2,22 3.3.3.3,30 +out on z0 tcp 2.2.2.2,23 3.3.3.3,31 +out on z0 tcp 2.2.2.2,24 3.3.3.3,32 +out on z0 tcp 2.2.2.2,25 3.3.3.3,33 +out on z0 tcp 2.2.2.2,26 3.3.3.3,34 +out on z0 tcp 2.2.2.2,27 3.3.3.3,35 +out on z0 tcp 2.2.2.2,28 3.3.3.3,36 +out on z0 tcp 2.2.2.2,29 3.3.3.3,37 diff --git a/contrib/ipfilter/test/input/n1_6 b/contrib/ipfilter/test/input/n1_6 new file mode 100644 index 00000000000..c1badab9a9c --- /dev/null +++ b/contrib/ipfilter/test/input/n1_6 @@ -0,0 +1,34 @@ +out6 on zx0 255 10:1:1::0 10:1:1::2 +out6 on zx0 255 10:1:1::1 10:1:1::2 +out6 on zx0 255 10:1:1::2 10:1:1::1 +out6 on zx0 tcp 10:1:1::2,1025 10:1:1::1,1025 +out6 on zx0 tcp 10:1:1::2,1026 10:1:1::1,1025 +out6 on zx0 255 10::2:2:1 10:1:2::1 +out6 on zx0 255 10::2:2:2 10:1:2::1 +in6 on zx0 255 10:1:1::1 10:1:1::2 +in6 on zx0 255 10:1:1::2 10:1:1::1 +in6 on zx0 255 10::2:2:1 10::2:1:1 +in6 on zx0 255 10::2:2:2 10::2:1:1 +in6 on zx0 255 10::2:2:3 10:1:1::1 +in6 on zx0 255 10::2:3:4 10::2:2:2 +in6 on zx0 255 10:1:1::1 10::2:2:2 +in6 on zx0 255 10:1:1::2 10::2:2:2 +in6 on zx0 255 10:1:1::0 10::3:4:5 +in6 on zx0 255 10:1:1::1 10::3:4:5 +in6 on zx0 255 10:1:1::2 10::3:4:5 +in6 on zx0 tcp 10:1:1::1,1025 10::3:4:5,1025 +out6 on zx0 58 10:1:1::1 10:4:3::2 +in6 on zx0 58 10:4:3::2 10::2:2:2 +in6 on zx0 58 10:4:3::2 10::3:4:1 +in6 on zx0 58 10:4:3::2 10::3:4:2 +in6 on zx0 58 10:4:3::2 10::3:4:3 +in6 on zx0 58 10:4:3::2 10::3:4:4 +in6 on zx0 58 10:4:3::2 10::3:4:5 +out6 on zx0 34 10:1:1::2 10:4:3::2 +in6 on zx0 34 10:4:3::2 10::3:4:4 +out6 on zx0 34 10:1:1::2 10:4:3::4 +in6 on zx0 34 10:4:3::4 10::3:4:5 +out6 on zx0 34 10:1:1::3 10:4:3::4 +in6 on zx0 34 10:4:3::4 10::3:4:6 +out6 on zx0 35 10:1:1::3 10:4:3::4 +in6 on zx0 35 10:4:3::4 10::3:4:7 diff --git a/contrib/ipfilter/test/input/n200 b/contrib/ipfilter/test/input/n200 new file mode 100644 index 00000000000..9b021581e13 --- /dev/null +++ b/contrib/ipfilter/test/input/n200 @@ -0,0 +1,6 @@ +[in,bar0] +4500 0028 0000 0000 0006 435a 6363 6363 5858 5858 038d 0050 0000 0000 0000 0000 5000 1000 2491 0000 + +[out,bar0] +4500 0044 0000 0000 ff11 bda6 7f00 0001 7f00 0001 2775 2775 0030 0000 4500 0028 0000 0000 0006 435a 6363 6363 5858 5858 038d 0050 0000 0000 0000 0000 5000 1000 2491 0000 + diff --git a/contrib/ipfilter/test/input/n2_6 b/contrib/ipfilter/test/input/n2_6 new file mode 100644 index 00000000000..3ea74ff377a --- /dev/null +++ b/contrib/ipfilter/test/input/n2_6 @@ -0,0 +1,19 @@ +out6 on zx0 tcp 10:1:1::1,1025 10:1:1::1,1025 +out6 on zx0 tcp 10:1:1::1,1025 10:1:1::2,1025 +out6 on zx0 10:1:1::0 10:1:1::2 +out6 on zx0 10:1:1::1 10:1:2::1 +out6 on zx0 tcp 10:1:1::2,1025 10:1:1::1,1025 +out6 on zx0 tcp 10:1:1::2,1025 10:1:1::1,1025 +out6 on zx0 tcp 10:1:1::2,1026 10:1:1::1,1025 +out6 on zx0 udp 10:1:1::2,1025 10:1:1::1,1025 +out6 on zx0 tcp 10:1:1::3,2000 10:1:2::1,80 +out6 on zx0 tcp 10:1:1::3,2001 10:1:3::1,80 +out6 on zx0 tcp 10:1:1::3,2002 10:1:4::1,80 +out6 on zx0 tcp 10:1:1::3,2003 10:1:4::1,80 +in6 on zx0 10:1:1::1 10:1:1::2 +in6 on zx0 tcp 10:1:1::1,1025 10:1:1::2,1025 +in6 on zx0 10:1:1::2 10:1:1::1 +in6 on zx0 tcp 10:1:1::1,1026 10::3:4:5,40000 +in6 on zx0 tcp 10:1:1::1,1025 10::3:4:5,40000 +in6 on zx0 udp 10:1:1::2,1025 10::3:4:5,40001 +in6 on zx0 tcp 10:1:2::1,80 10::3:4:5,40001 diff --git a/contrib/ipfilter/test/input/n4_6 b/contrib/ipfilter/test/input/n4_6 new file mode 100644 index 00000000000..8f0f423bc27 --- /dev/null +++ b/contrib/ipfilter/test/input/n4_6 @@ -0,0 +1,10 @@ +in6 on zx0 tcp 10:3:3::3,12345 10:1:1::1,23 +out6 on zx0 tcp 10::2:2:1,10023 10:3:3::3,12345 +in6 on zx0 tcp 10:3:3::3,12345 10:1:1::1,53 +out6 on zx0 tcp 10::2:2:1,10053 10:3:3::3,12345 +in6 on zx0 tcp 10:3:3::3,12346 10:1:0::0,23 +out6 on zx0 tcp 10::2:2:1,10023 10:3:3::3,12346 +in6 on zx0 udp 10:3:3::3,12345 10:1:1::0,53 +out6 on zx0 udp 10::2:2:1,10053 10:3:3::3,12345 +in6 on zx0 tcp 10:3:3::3,12345 10:1:1::0,53 +out6 on zx0 tcp 10::2:2:1,53 10:3:3::3,12345 diff --git a/contrib/ipfilter/test/input/n5_6 b/contrib/ipfilter/test/input/n5_6 new file mode 100644 index 00000000000..9ac0c29c4a3 --- /dev/null +++ b/contrib/ipfilter/test/input/n5_6 @@ -0,0 +1,54 @@ +out6 on zx0 255 10:1:1::0 10:1:1::2 +out6 on zx0 255 10:1:1::1 10:1:1::2 +out6 on zx0 255 10:1:1::2 10:1:1::1 +out6 on zx0 tcp 10:1:1::2,1025 10:1:1::1,1025 +out6 on zx0 tcp 10:1:1::2,1026 10:1:1::1,1025 +out6 on zx0 255 10::2:2:1 10:1:2::1 +out6 on zx0 255 10::2:2:2 10:1:2::1 +in6 on zx0 255 10:1:1::1 10:1:1::2 +in6 on zx0 255 10:1:1::2 10:1:1::1 +in6 on zx0 255 10::2:2:1 10::2:1:1 +in6 on zx0 255 10::2:2:2 10::2:1:1 +in6 on zx0 255 10::2:2:3 10:1:1::1 +in6 on zx0 255 10::2:3:4 10::2:2:2 +in6 on zx0 255 10:1:1::1 10::2:2:2 +in6 on zx0 255 10:1:1::2 10::2:2:2 +in6 on zx0 255 10:1:1::0 10::3:4:5 +in6 on zx0 255 10:1:1::1 10::3:4:5 +in6 on zx0 255 10:1:1::2 10::3:4:5 +in6 on zx0 tcp 10:1:1::1,1025 10::3:4:5,1025 +out6 on zx0 58 10:1:1::1 10:4:3::2 +in6 on zx0 58 10:4:3::2 10::2:2:2 +in6 on zx0 58 10:4:3::2 10::3:4:3 +in6 on zx0 58 10:4:3::2 10::3:4:5 +out6 on zx0 34 10:1:1::2 10:4:3::2 +in6 on zx0 34 10:4:3::2 10::3:4:4 +out6 on zx0 34 10:1:1::2 10:4:3::4 +in6 on zx0 34 10:4:3::4 10::3:4:5 +out6 on zx0 34 10:1:1::3 10:4:3::4 +in6 on zx0 34 10:4:3::4 10::3:4:6 +out6 on zx0 35 10:1:1::3 10:4:3::4 +in6 on zx0 35 10:4:3::4 10::3:4:7 +out6 on zx0 tcp 10:1:1::1,1025 10:1:1::1,1025 +out6 on zx0 tcp 10:1:1::1,1025 10:1:1::2,1025 +out6 on zx0 10:1:1::0 10:1:1::2 +out6 on zx0 10:1:1::1 10:1:2::1 +out6 on zx0 tcp 10:1:1::2,1025 10:1:1::1,1025 +out6 on zx0 tcp 10:1:1::2,1025 10:1:1::1,1025 +out6 on zx0 tcp 10:1:1::2,1026 10:1:1::1,1025 +out6 on zx0 udp 10:1:1::2,1025 10:1:1::1,1025 +out6 on zx0 tcp 10:1:1::3,2000 10:1:2::1,80 +out6 on zx0 tcp 10:1:1::3,2001 10:1:3::1,80 +out6 on zx0 tcp 10:1:1::3,2002 10:1:4::1,80 +out6 on zx0 tcp 10:1:1::3,2003 10:1:4::1,80 +in6 on zx0 10:1:1::1 10:1:1::2 +in6 on zx0 tcp 10:1:1::1,1025 10:1:1::2,1025 +in6 on zx0 10:1:1::2 10:1:1::1 +out6 on zx0 tcp 10:1:1::1,1026 10::3:4:5,40000 +in6 on zx0 tcp 10:1:1::1,1026 10::3:4:5,40000 +out6 on zx0 tcp 10:1:1::1,1025 10::3:4:5,40000 +in6 on zx0 tcp 10:1:1::1,1025 10::3:4:5,40000 +out6 on zx0 udp 10:1:1::2,1025 10::3:4:5,40001 +in6 on zx0 udp 10:1:1::2,1025 10::3:4:5,40001 +out6 on zx0 tcp 10:1:2::1,80 10::3:4:5,40001 +in6 on zx0 tcp 10:1:2::1,80 10::3:4:5,40001 diff --git a/contrib/ipfilter/test/input/n6_6 b/contrib/ipfilter/test/input/n6_6 new file mode 100644 index 00000000000..18300cd1455 --- /dev/null +++ b/contrib/ipfilter/test/input/n6_6 @@ -0,0 +1,13 @@ +in6 on zx0 tcp 10::2:2:2,12345 10:1:1::1,23 +in6 on zx0 tcp 10::2:2:2,12345 10:1:1::2,23 +in6 on zx0 tcp 10:3:0::1,12345 10:1:2::2,23 +in6 on zx0 tcp 10:3:0::1,12345 10::2:2:2,23 +in6 on zx0 tcp 10:3:3::3,12345 10:1:1::1,23 +in6 on zx0 tcp 10::2:2:2,12345 10:1:1::1,53 +in6 on zx0 tcp 10:3:3::3,12345 10:1:1::1,53 +in6 on zx0 tcp 10::2:2:2,12345 10:1:0::0,23 +in6 on zx0 tcp 10:3:3::3,12345 10:1:0::0,23 +in6 on zx0 udp 10::2:2:2,12345 10:1:1::0,53 +in6 on zx0 udp 10:3:3::3,12345 10:1:1::0,53 +in6 on zx0 tcp 10::2:2:2,12345 10:1:1::0,53 +in6 on zx0 tcp 10:3:3::3,12345 10:1:1::0,53 diff --git a/contrib/ipfilter/test/input/n7_6 b/contrib/ipfilter/test/input/n7_6 new file mode 100644 index 00000000000..b31a1def4f4 --- /dev/null +++ b/contrib/ipfilter/test/input/n7_6 @@ -0,0 +1,9 @@ +in6 on zx0 tcp 10::2:3:1,1230 10:1:1::1,22 +in6 on zx0 tcp 10::2:3:1,1231 10:1:1::1,23 +in6 on zx0 tcp 10::2:3:1,1232 10:1:1::1,50 +in6 on zx0 tcp 10::2:3:1,1233 10:1:1::1,79 +in6 on zx0 tcp 10::2:3:1,1234 10:1:1::1,80 +in6 on zx0 tcp 10::2:3:1,1235 10:1:1::2,80 +in6 on zx0 tcp 10::2:3:1,1236 10:1:1::3,80 +in6 on zx0 tcp 10::2:3:1,1237 10:1:1::4,80 +in6 on zx0 tcp 10::2:3:1,1238 10:1:1::4,80 diff --git a/contrib/ipfilter/test/input/n8 b/contrib/ipfilter/test/input/n8 index 1f5b2130ba2..c0a5b3fd7dd 100644 --- a/contrib/ipfilter/test/input/n8 +++ b/contrib/ipfilter/test/input/n8 @@ -1,27 +1,31 @@ #v tos len id off ttl p sum src dst # ICMP ECHO (ping) exchange -[out,icmp0] 4500 0054 8bc1 0000 ff01 23dc 0202 0202 0404 0404 +[out,icmp0] +4500 0054 8bc1 0000 ff01 23dc 0202 0202 0404 0404 0800 efdf 6220 0000 3f6f 6e80 000b 0d02 0809 0a0b 0c0d 0e0f 1011 1213 1415 1617 1819 1a1b 1c1d 1e1f 2021 2223 2425 2627 2829 2a2b 2c2d 2e2f 3031 3233 3435 3637 -[in,icmp0] 4500 0054 3fd5 4000 ff01 1fc1 0404 0404 0a0a 0a01 +[in,icmp0] +4500 0054 3fd5 4000 ff01 1fc1 0404 0404 0a0a 0a01 0000 f7df 6220 0000 3f6f 6e80 000b 0d02 0809 0a0b 0c0d 0e0f 1011 1213 1415 1617 1819 1a1b 1c1d 1e1f 2021 2223 2425 2627 2829 2a2b 2c2d 2e2f 3031 3233 3435 3637 -[out,icmp0] 4500 0054 8bc1 0000 ff01 23dc 0202 0202 0404 0404 +[out,icmp0] +4500 0054 8bc1 0000 ff01 23dc 0202 0202 0404 0404 0800 efde 6220 0001 3f6f 6e80 000b 0d02 0809 0a0b 0c0d 0e0f 1011 1213 1415 1617 1819 1a1b 1c1d 1e1f 2021 2223 2425 2627 2829 2a2b 2c2d 2e2f 3031 3233 3435 3637 -[in,icmp0] 4500 0054 3fd5 4000 ff01 1fc1 0404 0404 0a0a 0a01 +[in,icmp0] +4500 0054 3fd5 4000 ff01 1fc1 0404 0404 0a0a 0a01 0000 f7de 6220 0001 3f6f 6e80 000b 0d02 0809 0a0b 0c0d 0e0f 1011 1213 1415 1617 1819 1a1b 1c1d 1e1f 2021 2223 2425 diff --git a/contrib/ipfilter/test/input/n8_6 b/contrib/ipfilter/test/input/n8_6 new file mode 100644 index 00000000000..8039f78df63 --- /dev/null +++ b/contrib/ipfilter/test/input/n8_6 @@ -0,0 +1,37 @@ +#v tos len id off ttl p sum src dst +# ICMP ECHO (ping) exchange +[out,icmp0] +6000 0000 0040 3aff 0002 0000 0000 0000 0000 0002 0002 0002 0004 0004 0004 0000 0000 0000 0000 0004 +8000 774d 6220 0000 3f6f 6e80 000b +0d02 0809 0a0b 0c0d 0e0f 1011 1213 1415 +1617 1819 1a1b 1c1d 1e1f 2021 2223 2425 +2627 2829 2a2b 2c2d 2e2f 3031 3233 3435 +3637 + +# ECHO reply +[in,icmp0] +6000 0000 0040 3aff 0004 0004 0004 0000 0000 0000 0000 0004 0010 0010 0010 0000 0000 0000 0000 0001 +8100 7624 6220 0000 3f6f 6e80 000b +0d02 0809 0a0b 0c0d 0e0f 1011 1213 1415 +1617 1819 1a1b 1c1d 1e1f 2021 2223 2425 +2627 2829 2a2b 2c2d 2e2f 3031 3233 3435 +3637 + +# ECHO request +[out,icmp0] +6000 0000 0040 3aff 0002 0000 0000 0000 0000 0002 0002 0002 0004 0004 0004 0000 0000 0000 0000 0004 +8000 774c 6220 0001 3f6f 6e80 000b +0d02 0809 0a0b 0c0d 0e0f 1011 1213 1415 +1617 1819 1a1b 1c1d 1e1f 2021 2223 2425 +2627 2829 2a2b 2c2d 2e2f 3031 3233 3435 +3637 + +# ECHO reply +[in,icmp0] +6000 0000 0040 3aff 0004 0004 0004 0000 0000 0000 0000 0004 0010 0010 0010 0000 0000 0000 0000 0001 +8100 7623 6220 0001 3f6f 6e80 000b +0d02 0809 0a0b 0c0d 0e0f 1011 1213 1415 +1617 1819 1a1b 1c1d 1e1f 2021 2223 2425 +2627 2829 2a2b 2c2d 2e2f 3031 3233 3435 +3637 + diff --git a/contrib/ipfilter/test/input/n9 b/contrib/ipfilter/test/input/n9 index c4aada8f2c7..5c2d3c7dfeb 100644 --- a/contrib/ipfilter/test/input/n9 +++ b/contrib/ipfilter/test/input/n9 @@ -1,27 +1,31 @@ #v tos len id off ttl p sum src dst # ICMP ECHO (ping) exchange -[in,icmp0] 4500 0054 8bc1 0000 ff01 23dc 0202 0202 0404 0404 +[in,icmp0] +4500 0054 8bc1 0000 ff01 23dc 0202 0202 0404 0404 0800 efdf 6220 0000 3f6f 6e80 000b 0d02 0809 0a0b 0c0d 0e0f 1011 1213 1415 1617 1819 1a1b 1c1d 1e1f 2021 2223 2425 2627 2829 2a2b 2c2d 2e2f 3031 3233 3435 3637 -[out,icmp0] 4500 0054 3fd5 4000 ff01 23c5 0a0a 0a01 0202 0202 +[out,icmp0] +4500 0054 3fd5 4000 ff01 23c5 0a0a 0a01 0202 0202 0000 f7df 6220 0000 3f6f 6e80 000b 0d02 0809 0a0b 0c0d 0e0f 1011 1213 1415 1617 1819 1a1b 1c1d 1e1f 2021 2223 2425 2627 2829 2a2b 2c2d 2e2f 3031 3233 3435 3637 -[in,icmp0] 4500 0054 8bc1 0000 ff01 23dc 0202 0202 0404 0404 +[in,icmp0] +4500 0054 8bc1 0000 ff01 23dc 0202 0202 0404 0404 0800 efde 6220 0001 3f6f 6e80 000b 0d02 0809 0a0b 0c0d 0e0f 1011 1213 1415 1617 1819 1a1b 1c1d 1e1f 2021 2223 2425 2627 2829 2a2b 2c2d 2e2f 3031 3233 3435 3637 -[out,icmp0] 4500 0054 3fd5 4000 ff01 23c5 0a0a 0a01 0202 0202 +[out,icmp0] +4500 0054 3fd5 4000 ff01 23c5 0a0a 0a01 0202 0202 0000 f7de 6220 0001 3f6f 6e80 000b 0d02 0809 0a0b 0c0d 0e0f 1011 1213 1415 1617 1819 1a1b 1c1d 1e1f 2021 2223 2425 diff --git a/contrib/ipfilter/test/input/n9_6 b/contrib/ipfilter/test/input/n9_6 new file mode 100644 index 00000000000..42db09dd365 --- /dev/null +++ b/contrib/ipfilter/test/input/n9_6 @@ -0,0 +1,34 @@ +#v tos len id off ttl p sum src dst +# ICMP ECHO (ping) exchange +[in,icmp0] +6000 0000 0040 3aff 0002 0000 0000 0000 0000 0002 0002 0002 0004 0004 0004 0000 0000 0000 0000 0004 +8000 774d 6220 0000 3f6f 6e80 000b +0d02 0809 0a0b 0c0d 0e0f 1011 1213 1415 +1617 1819 1a1b 1c1d 1e1f 2021 2223 2425 +2627 2829 2a2b 2c2d 2e2f 3031 3233 3435 +3637 + +[out,icmp0] +6000 0000 0040 3aff 0010 0010 0010 0000 0000 0000 0000 0001 0002 0000 0000 0000 0000 0002 0002 0002 +8100 762c 6220 0000 3f6f 6e80 000b +0d02 0809 0a0b 0c0d 0e0f 1011 1213 1415 +1617 1819 1a1b 1c1d 1e1f 2021 2223 2425 +2627 2829 2a2b 2c2d 2e2f 3031 3233 3435 +3637 + +[in,icmp0] +6000 0000 0040 3aff 0002 0000 0000 0000 0000 0002 0002 0002 0004 0004 0004 0000 0000 0000 0000 0004 +8000 774c 6220 0001 3f6f 6e80 000b +0d02 0809 0a0b 0c0d 0e0f 1011 1213 1415 +1617 1819 1a1b 1c1d 1e1f 2021 2223 2425 +2627 2829 2a2b 2c2d 2e2f 3031 3233 3435 +3637 + +[out,icmp0] +6000 0000 0040 3aff 0010 0010 0010 0000 0000 0000 0000 0001 0002 0000 0000 0000 0000 0002 0002 0002 +8100 762b 6220 0001 3f6f 6e80 000b +0d02 0809 0a0b 0c0d 0e0f 1011 1213 1415 +1617 1819 1a1b 1c1d 1e1f 2021 2223 2425 +2627 2829 2a2b 2c2d 2e2f 3031 3233 3435 +3637 + diff --git a/contrib/ipfilter/test/input/ni1 b/contrib/ipfilter/test/input/ni1 index fb6b0b63e5f..519325fc56b 100644 --- a/contrib/ipfilter/test/input/ni1 +++ b/contrib/ipfilter/test/input/ni1 @@ -1,55 +1,58 @@ #v tos len id off ttl p sum src dst # ICMP timeout exceeded in reply to a ICMP packet going out. +# 2.2.2.2,44489 -> 4.4.4.4,33438 [out,df0] -4500 0028 4706 4000 0111 26b4 0202 0202 -0404 0404 afc9 829e 0014 6b10 0402 0000 -3be5 468d 000a cfc3 +4500 0028 4706 4000 0111 26b4 0202 0202 0404 0404 +afc9 829e 0014 6b10 +0402 0000 3be5 468d 000a cfc3 [in,df0] -4500 0038 809a 0000 ff01 2919 0303 0303 -0606 0606 0b00 5f7b 0000 0000 +4500 0038 809a 0000 ff01 2919 0303 0303 0606 0606 +0b00 5f7b 0000 0000 4500 0028 0000 4000 0111 65b2 0606 0606 0404 0404 afc9 829e 0014 6308 [in,df0] -4500 0044 809a 0000 ff01 290d 0303 0303 -0606 0606 0b00 0939 0000 0000 +4500 0044 809a 0000 ff01 290d 0303 0303 0606 0606 +0b00 0939 0000 0000 4500 0028 0000 4000 0111 65b2 0606 0606 0404 0404 afc9 829e 0014 6308 0402 0000 3be5 468d 000a cfc3 +# 2.2.2.2,2048 -> 4.4.4.4,33438 [out,df0] -4500 0028 4706 4000 0111 26b4 0202 0202 -0404 0404 0800 829e 0014 12da 0402 0000 -3be5 468d 000a cfc3 +4500 0028 4706 4000 0111 26b4 0202 0202 0404 0404 +0800 829e 0014 12da +0402 0000 3be5 468d 000a cfc3 [in,df0] -4500 0038 809a 0000 ff01 2918 0303 0303 -0606 0607 0b00 5f7c 0000 0000 +4500 0038 809a 0000 ff01 2918 0303 0303 0606 0607 +0b00 5f7c 0000 0000 4500 0028 0000 4000 0111 65b1 0606 0607 0404 0404 4e20 829e 0014 c4b0 [in,df0] -4500 0044 809a 0000 ff01 290c 0303 0303 -0606 0607 0b00 093a 0000 0000 +4500 0044 809a 0000 ff01 290c 0303 0303 0606 0607 +0b00 093a 0000 0000 4500 0028 0000 4000 0111 65b1 0606 0607 0404 0404 4e20 829e 0014 c4b0 0402 0000 3be5 468d 000a cfc3 +# 2.2.2.2,20480 -> 4.4.4.4,33438 [out,df0] -4500 0028 4706 4000 0111 26b4 0202 0202 -0404 0404 5000 829e 0014 cad9 0402 0000 -3be5 468d 000a cfc3 +4500 0028 4706 4000 0111 26b4 0202 0202 0404 0404 +5000 829e 0014 cad9 +0402 0000 3be5 468d 000a cfc3 [in,df0] -4500 0038 809a 0000 ff01 2917 0303 0303 -0606 0608 0b00 0775 0000 0000 +4500 0038 809a 0000 ff01 2917 0303 0303 0606 0608 +0b00 0775 0000 0000 4500 0028 0000 4000 0111 65b0 0606 0608 0404 0404 07d0 829e 0014 6308 [in,df0] -4500 0044 809a 0000 ff01 290b 0303 0303 -0606 0608 0b00 093b 0000 0000 +4500 0044 809a 0000 ff01 290b 0303 0303 0606 0608 +0b00 093b 0000 0000 4500 0028 0000 4000 0111 65b0 0606 0608 0404 0404 07d0 829e 0014 0b00 0402 0000 3be5 468d 000a cfc3 diff --git a/contrib/ipfilter/test/input/ni10 b/contrib/ipfilter/test/input/ni10 index 48ac2257028..636c4f1b2d6 100644 --- a/contrib/ipfilter/test/input/ni10 +++ b/contrib/ipfilter/test/input/ni10 @@ -2,7 +2,9 @@ # ICMP dest unreachable with 64 bits in payload (in reply to a TCP packet # going out) # IP 4.4.4.4 2.2.2.2 TCP(20480,80) -[in,df0] 45 00 00 3c 47 06 40 00 ff 06 28 aa 04 04 04 04 02 02 02 02 50 00 00 50 00 00 00 01 00 00 00 00 a0 02 16 d0 d8 e2 00 00 02 04 05 b4 04 02 08 0a 00 47 fb b0 00 00 00 00 01 03 03 00 +[in,df0] +4500 003c 4706 4000 ff06 28aa 0404 0404 0202 0202 +5000 0050 0000 0001 0000 0000 a002 16d0 d8e2 0000 0204 05b4 0402 080a 0047 fbb0 0000 0000 0103 0300 # IP 3.3.3.3 -> 4.4.4.4 ICMP (IP(4.4.4.4,6.6.6.6) TCP(20480,80)) [out,df0] @@ -13,7 +15,11 @@ # IP 3.3.3.3 -> 4.4.4.4 ICMP (IP(4.4.4.4,6.6.6.6) TCP(20480,80)) # ICMP dest unreachable with whole packet in payload (40 bytes = 320 bits) -[out,df0] 45 00 00 58 80 9a 00 00 ff 01 2c fd 03 03 03 03 04 04 04 04 03 03 11 3f 00 00 00 00 45 00 00 3c 47 06 40 00 ff 06 20 a2 04 04 04 04 06 06 06 06 50 00 00 50 00 00 00 01 00 00 00 00 a0 02 16 d0 d0 da 00 00 02 04 05 b4 04 02 08 0a 00 47 fb b0 00 00 00 00 01 03 03 00 +[out,df0] +4500 0058 809a 0000 ff01 2cfd 0303 0303 0404 0404 +0303 113f 0000 0000 +4500 003c 4706 4000 ff06 20a2 0404 0404 0606 0606 +5000 0050 0000 0001 0000 0000 a002 16d0 d0da 0000 0204 05b4 0402 080a 0047 fbb0 0000 0000 0103 0300 # IP 3.3.3.3 -> 4.4.4.4 ICMP (IP(4.4.4.4,6.6.6.6) TCP(20480,80)) [out,df0] diff --git a/contrib/ipfilter/test/input/ni11 b/contrib/ipfilter/test/input/ni11 index 788e6036c40..0650abb7034 100644 --- a/contrib/ipfilter/test/input/ni11 +++ b/contrib/ipfilter/test/input/ni11 @@ -1,7 +1,9 @@ #v tos len id off ttl p sum src dst # ICMP dest unreachable with 64 bits in payload (in reply to a TCP packet # going out) -[in,df0] 45 00 00 3c 47 06 40 00 ff 06 20 aa 04 04 04 04 0a 02 02 02 50 00 05 00 00 00 00 01 00 00 00 00 a0 02 16 d0 cc 32 00 00 02 04 05 b4 04 02 08 0a 00 47 fb b0 00 00 00 00 01 03 03 00 +[in,df0] +4500 003c 4706 4000 ff06 20aa 0404 0404 0a02 0202 +5000 0500 0000 0001 0000 0000 a002 16d0 cc32 0000 0204 05b4 0402 080a 0047 fbb0 0000 0000 0103 0300 [out,df0] 4500 0038 809a 0000 ff01 2d1d 0303 0303 0404 0404 diff --git a/contrib/ipfilter/test/input/ni12 b/contrib/ipfilter/test/input/ni12 index 788e6036c40..c44aacce6da 100644 --- a/contrib/ipfilter/test/input/ni12 +++ b/contrib/ipfilter/test/input/ni12 @@ -1,24 +1,26 @@ #v tos len id off ttl p sum src dst # ICMP dest unreachable with 64 bits in payload (in reply to a TCP packet # going out) -[in,df0] 45 00 00 3c 47 06 40 00 ff 06 20 aa 04 04 04 04 0a 02 02 02 50 00 05 00 00 00 00 01 00 00 00 00 a0 02 16 d0 cc 32 00 00 02 04 05 b4 04 02 08 0a 00 47 fb b0 00 00 00 00 01 03 03 00 +[in,df0] +4500 003c 4706 4000 ff06 20aa 0404 0404 0a02 0202 +5000 0500 0000 0001 0000 0000 a002 16d0 cc32 0000 0204 05b4 0402 080a 0047 fbb0 0000 0000 0103 0300 [out,df0] 4500 0038 809a 0000 ff01 2d1d 0303 0303 0404 0404 -0303 0fa3 0000 0000 +0303 10bb 0000 0000 4500 003c 4706 4000 ff06 2aac 0404 0404 0101 0101 -5000 9d58 0000 0001 +5000 9c40 0000 0001 # ICMP dest unreachable with whole packet in payload (40 bytes = 320 bits) [out,df0] 4500 0058 809a 0000 ff01 2cfd 0303 0303 0404 0404 0303 0735 0000 0000 4500 003c 4706 4000 ff06 2aac 0404 0404 0101 0101 -5000 9d58 0000 0001 0000 0000 a002 16d0 3ddc 0000 +5000 9c40 0000 0001 0000 0000 a002 16d0 3ef4 0000 0204 05b4 0402 080a 0047 fbb0 0000 0000 0103 0300 [out,df0] 4500 0038 809a 0000 ff01 2b1b 0303 0303 0505 0505 -0303 0fa3 0000 0000 -4500 003c 4706 4000 ff06 2aab 0404 0404 0101 0102 5000 9d58 0000 0001 +0303 10bb 0000 0000 +4500 003c 4706 4000 ff06 2aab 0404 0404 0101 0102 5000 9c40 0000 0001 diff --git a/contrib/ipfilter/test/input/ni13 b/contrib/ipfilter/test/input/ni13 index 77569eead70..70c19526ba9 100644 --- a/contrib/ipfilter/test/input/ni13 +++ b/contrib/ipfilter/test/input/ni13 @@ -1,19 +1,17 @@ # 23:18:36.130424 192.168.113.1.1511 > 192.168.113.3.1723: S 2884651685:2884651685(0) win 64240 (DF) [in,pcn1=192.168.113.3] -4500 0030 5e11 4000 8006 3961 c0a8 7101 -c0a8 7103 05e7 06bb abf0 4aa5 0000 0000 -7002 faf0 21a1 0000 0204 05b4 0101 0402 +4500 0030 5e11 4000 8006 3961 c0a8 7101 c0a8 7103 +05e7 06bb abf0 4aa5 0000 0000 7002 faf0 21a1 0000 0204 05b4 0101 0402 # 23:18:36.130778 192.168.113.3.1723 > 192.168.113.1.1511: S 2774821082:2774821082(0) ack 2884651686 win 32768 (DF) [out,pcn1] -4500 002c 69a6 4000 4006 6dd0 c0a8 7103 -c0a8 7101 06bb 05e7 a564 68da abf0 4aa6 -6012 8000 a348 0000 0204 05b4 +4500 002c 69a6 4000 4006 6dd0 c0a8 7103 c0a8 7101 +06bb 05e7 a564 68da abf0 4aa6 6012 8000 a348 0000 0204 05b4 # 23:18:36.130784 192.168.113.1.1511 > 192.168.113.3.1723: P 1:157(156) ack 1 win 64240: pptp CTRL_MSGTYPE=SCCRQ PROTO_VER(1.0) FRAME_CAP(A) BEARER_CAP(A) MAX_CHAN(0) FIRM_REV(2600) HOSTNAME() VENDOR(Microsoft Windows NT) (DF) [in,pcn1] -4500 00c4 5e12 4000 8006 38cc c0a8 7101 -c0a8 7103 05e7 06bb abf0 4aa6 a564 68db +4500 00c4 5e12 4000 8006 38cc c0a8 7101 c0a8 7103 +05e7 06bb abf0 4aa6 a564 68db 5018 faf0 e2a0 0000 009c 0001 1a2b 3c4d 0001 0000 0100 0000 0000 0001 0000 0001 0000 0a28 0000 0000 0000 0000 0000 0000 @@ -28,8 +26,8 @@ c0a8 7103 05e7 06bb abf0 4aa6 a564 68db # 23:18:36.260235 192.168.113.3.1723 > 192.168.113.1.1511: P 1:157(156) ack 157 win 33580: pptp CTRL_MSGTYPE=SCCRP PROTO_VER(1.0) RESULT_CODE(1) ERR_CODE(0) FRAME_CAP() BEARER_CAP() MAX_CHAN(1) FIRM_REV(1) HOSTNAME(local) VENDOR(linux) (DF) [out,pcn1] -4500 00c4 69a7 4000 4006 6d37 c0a8 7103 -c0a8 7101 06bb 05e7 a564 68db abf0 4b42 +4500 00c4 69a7 4000 4006 6d37 c0a8 7103 c0a8 7101 +06bb 05e7 a564 68db abf0 4b42 5018 832c cecf 0000 009c 0001 1a2b 3c4d 0002 0000 0100 0100 0000 0000 0000 0000 0001 0001 6c6f 6361 6c00 0000 0000 0000 @@ -44,8 +42,8 @@ c0a8 7101 06bb 05e7 a564 68db abf0 4b42 # 23:18:36.260252 192.168.113.1.1511 > 192.168.113.3.1723: P 157:325(168) ack 157 win 64084: pptp CTRL_MSGTYPE=OCRQ CALL_ID(16384) CALL_SER_NUM(4913) MIN_BPS(300) MAX_BPS(100000000) BEARER_TYPE(Any) FRAME_TYPE(E) RECV_WIN(64) PROC_DELAY(0) PHONE_NO_LEN(0) PHONE_NO() SUB_ADDR() (DF) [in,pcn1] -4500 00d0 5e13 4000 8006 38bf c0a8 7101 -c0a8 7103 05e7 06bb abf0 4b42 a564 6977 +4500 00d0 5e13 4000 8006 38bf c0a8 7101 c0a8 7103 +05e7 06bb abf0 4b42 a564 6977 5018 fa54 ac07 0000 00a8 0001 1a2b 3c4d 0007 0000 4000 1331 0000 012c 05f5 e100 0000 0003 0000 0003 0040 0000 0000 0000 @@ -60,176 +58,174 @@ c0a8 7103 05e7 06bb abf0 4b42 a564 6977 # 23:18:36.272856 192.168.113.3.1723 > 192.168.113.1.1511: P 157:189(32) ack 325 win 33580: pptp CTRL_MSGTYPE=OCRP CALL_ID(0) PEER_CALL_ID(16384) RESULT_CODE(1) ERR_CODE(0) CAUSE_CODE(0) CONN_SPEED(100000000) RECV_WIN(64) PROC_DELAY(0) PHY_CHAN_ID(0) (DF) [out,pcn1] -4500 0048 69a8 4000 4006 6db2 c0a8 7103 -c0a8 7101 06bb 05e7 a564 6977 abf0 4bea +4500 0048 69a8 4000 4006 6db2 c0a8 7103 c0a8 7101 +06bb 05e7 a564 6977 abf0 4bea 5018 832c 36fa 0000 0020 0001 1a2b 3c4d 0008 0000 0000 4000 0100 0000 05f5 e100 0040 0000 0000 0000 # 23:18:36.321819 192.168.113.1.1511 > 192.168.113.3.1723: P 325:349(24) ack 189 win 64052: pptp CTRL_MSGTYPE=SLI PEER_CALL_ID(0) SEND_ACCM(0xffffffff) RECV_ACCM(0xffffffff) (DF) [in,pcn1] -4500 0040 5e14 4000 8006 394e c0a8 7101 -c0a8 7103 05e7 06bb abf0 4bea a564 6997 +4500 0040 5e14 4000 8006 394e c0a8 7101 c0a8 7103 +05e7 06bb abf0 4bea a564 6997 5018 fa34 e810 0000 0018 0001 1a2b 3c4d 000f 0000 0000 0000 ffff ffff ffff ffff # 23:18:36.349759 192.168.113.1 > 192.168.113.3: gre [KSv1] ID:0000 S:0 ppp: LCP 25: Conf-Req(0), MRU=1400, Magic-Num=577f7c5b, PFC, ACFC, Call-Back CBCP [in,pcn1] -4500 0039 5e15 0000 802f 792b c0a8 7101 -c0a8 7103 3001 880b 0019 0000 0000 0000 +4500 0039 5e15 0000 802f 792b c0a8 7101 c0a8 7103 +3001 880b 0019 0000 0000 0000 ff03 c021 0100 0015 0104 0578 0506 577f 7c5b 0702 0802 0d03 06 # 23:18:36.389970 192.168.113.3 > 192.168.113.1: gre [KAv1] ID:4000 A:4294967295 [|gre] [out,pcn1] -4500 0020 69a9 0000 ff2f eeaf c0a8 7103 -c0a8 7101 2081 880b 0000 4000 ffff ffff +4500 0020 69a9 0000 ff2f eeaf c0a8 7103 c0a8 7101 +2081 880b 0000 4000 ffff ffff # 23:18:36.518426 192.168.113.3.1723 > 192.168.113.1.1511: . ack 349 win 33580 (DF) [out,pcn1] -4500 0028 69aa 4000 4006 6dd0 c0a8 7103 -c0a8 7101 06bb 05e7 a564 6997 abf0 4c02 -5010 832c b5c1 0000 +4500 0028 69aa 4000 4006 6dd0 c0a8 7103 c0a8 7101 +06bb 05e7 a564 6997 abf0 4c02 5010 832c b5c1 0000 # 23:18:36.555363 192.168.113.3 > 192.168.113.1: gre [KSv1] ID:4000 S:0 ppp: LCP 24: Conf-Req(1), ACCM=00000000, Magic-Num=22d90cfa, PFC, ACFC [out,pcn1] -4500 0038 69ab 0000 ff2f ee95 c0a8 7103 -c0a8 7101 3001 880b 0018 4000 0000 0000 +4500 0038 69ab 0000 ff2f ee95 c0a8 7103 c0a8 7101 +3001 880b 0018 4000 0000 0000 ff03 c021 0101 0014 0206 0000 0000 0506 22d9 0cfa 0702 0802 # 23:18:36.556030 192.168.113.3 > 192.168.113.1: gre [KSAv1] ID:4000 S:1 A:0 ppp: LCP 11: Conf-Rej(0), Call-Back CBCP [out,pcn1] -4500 002f 69ac 0000 ff2f ee9d c0a8 7103 -c0a8 7101 3081 880b 000b 4000 0000 0001 -0000 0000 ff03 c021 0400 0007 0d03 06 +4500 002f 69ac 0000 ff2f ee9d c0a8 7103 c0a8 7101 +3081 880b 000b 4000 0000 0001 0000 0000 ff03 c021 0400 0007 0d03 06 # 23:18:36.557166 192.168.113.1 > 192.168.113.3: gre [KSAv1] ID:0000 S:1 A:1 ppp: LCP 24: Conf-Ack(1), ACCM=00000000, Magic-Num=22d90cfa, PFC, ACFC [in,pcn1] -4500 003c 5e16 0000 802f 7927 c0a8 7101 -c0a8 7103 3081 880b 0018 0000 0000 0001 +4500 003c 5e16 0000 802f 7927 c0a8 7101 c0a8 7103 +3081 880b 0018 0000 0000 0001 0000 0001 ff03 c021 0201 0014 0206 0000 0000 0506 22d9 0cfa 0702 0802 # 23:18:36.557764 192.168.113.1 > 192.168.113.3: gre [KSv1] ID:0000 S:2 ppp: LCP 22: Conf-Req(1), MRU=1400, Magic-Num=577f7c5b, PFC, ACFC [in,pcn1] -4500 0036 5e17 0000 802f 792c c0a8 7101 -c0a8 7103 3001 880b 0016 0000 0000 0002 +4500 0036 5e17 0000 802f 792c c0a8 7101 c0a8 7103 +3001 880b 0016 0000 0000 0002 ff03 c021 0101 0012 0104 0578 0506 577f 7c5b 0702 0802 # 23:18:36.564658 192.168.113.3 > 192.168.113.1: gre [KSAv1] ID:4000 S:2 A:2 ppp: LCP 22: Conf-Ack(1), MRU=1400, Magic-Num=577f7c5b, PFC, ACFC [out,pcn1] -4500 003a 69ad 0000 ff2f ee91 c0a8 7103 -c0a8 7101 3081 880b 0016 4000 0000 0002 +4500 003a 69ad 0000 ff2f ee91 c0a8 7103 c0a8 7101 +3081 880b 0016 4000 0000 0002 0000 0002 ff03 c021 0201 0012 0104 0578 0506 577f 7c5b 0702 0802 # 23:18:36.564803 192.168.113.3 > 192.168.113.1: gre [KSv1] ID:4000 S:3 ppp: IPCP 18: Conf-Req(1), IP-Addr=192.168.0.1, IP-Comp VJ-Comp [out,pcn1] -4500 0032 69ae 0000 ff2f ee98 c0a8 7103 -c0a8 7101 3001 880b 0012 4000 0000 0003 +4500 0032 69ae 0000 ff2f ee98 c0a8 7103 c0a8 7101 +3001 880b 0012 4000 0000 0003 8021 0101 0010 0306 c0a8 0001 0206 002d 0f01 # 23:18:36.570395 192.168.113.1.1511 > 192.168.113.3.1723: P 349:373(24) ack 189 win 64052: pptp CTRL_MSGTYPE=SLI PEER_CALL_ID(0) SEND_ACCM(0x00000000) RECV_ACCM(0xffffffff) (DF) [in,pcn1] -4500 0040 5e18 4000 8006 394a c0a8 7101 -c0a8 7103 05e7 06bb abf0 4c02 a564 6997 +4500 0040 5e18 4000 8006 394a c0a8 7101 c0a8 7103 +05e7 06bb abf0 4c02 a564 6997 5018 fa34 e7f8 0000 0018 0001 1a2b 3c4d 000f 0000 0000 0000 0000 0000 ffff ffff # 23:18:36.573307 192.168.113.1 > 192.168.113.3: gre [KSAv1] ID:0000 S:3 A:3 ppp: LCP 20: Ident(2), Magic-Num=577f7c5b [in,pcn1] -4500 0038 5e19 0000 802f 7928 c0a8 7101 -c0a8 7103 3081 880b 0014 0000 0000 0003 +4500 0038 5e19 0000 802f 7928 c0a8 7101 c0a8 7103 +3081 880b 0014 0000 0000 0003 0000 0003 c021 0c02 0012 577f 7c5b 4d53 5241 5356 352e 3130 # 23:18:36.573856 192.168.113.3 > 192.168.113.1: gre [KSAv1] ID:4000 S:4 A:3 ppp: LCP 26: Code-Rej(2) [out,pcn1] -4500 003e 69af 0000 ff2f ee8b c0a8 7103 -c0a8 7101 3081 880b 001a 4000 0000 0004 +4500 003e 69af 0000 ff2f ee8b c0a8 7103 c0a8 7101 +3081 880b 001a 4000 0000 0004 0000 0003 ff03 c021 0702 0016 0c02 0012 577f 7c5b 4d53 5241 5356 352e 3130 # 23:18:36.584936 192.168.113.1 > 192.168.113.3: gre [KSAv1] ID:0000 S:4 A:4 ppp: LCP 26: Ident(3), Magic-Num=577f7c5b [in,pcn1] -4500 003e 5e1a 0000 802f 7921 c0a8 7101 -c0a8 7103 3081 880b 001a 0000 0000 0004 +4500 003e 5e1a 0000 802f 7921 c0a8 7101 c0a8 7103 +3081 880b 001a 0000 0000 0004 0000 0004 c021 0c03 0018 577f 7c5b 4d53 5241 532d 302d 434c 4159 4d4f 4f52 # 23:18:36.585562 192.168.113.3 > 192.168.113.1: gre [KSAv1] ID:4000 S:5 A:4 ppp: LCP 32: Code-Rej(3) [out,pcn1] -4500 0044 69b0 0000 ff2f ee84 c0a8 7103 -c0a8 7101 3081 880b 0020 4000 0000 0005 +4500 0044 69b0 0000 ff2f ee84 c0a8 7103 c0a8 7101 +3081 880b 0020 4000 0000 0005 0000 0004 ff03 c021 0703 001c 0c03 0018 577f 7c5b 4d53 5241 532d 302d 434c 4159 4d4f 4f52 # 23:18:36.588721 192.168.113.1 > 192.168.113.3: gre [KSAv1] ID:0000 S:5 A:5 ppp: CCP 12: Conf-Req(4), MPPC [in,pcn1] -4500 0030 5e1b 0000 802f 792e c0a8 7101 -c0a8 7103 3081 880b 000c 0000 0000 0005 +4500 0030 5e1b 0000 802f 792e c0a8 7101 c0a8 7103 +3081 880b 000c 0000 0000 0005 0000 0005 80fd 0104 000a 1206 0100 0001 # 23:18:36.589445 192.168.113.3 > 192.168.113.1: gre [KSAv1] ID:4000 S:6 A:5 ppp: CCP 6: Conf-Req(1) [out,pcn1] -4500 002a 69b1 0000 ff2f ee9d c0a8 7103 -c0a8 7101 3081 880b 0006 4000 0000 0006 +4500 002a 69b1 0000 ff2f ee9d c0a8 7103 c0a8 7101 +3081 880b 0006 4000 0000 0006 0000 0005 80fd 0101 0004 # 23:18:36.589540 192.168.113.3 > 192.168.113.1: gre [KSv1] ID:4000 S:7 ppp: CCP 12: Conf-Rej(4), MPPC [out,pcn1] -4500 002c 69b2 0000 ff2f ee9a c0a8 7103 -c0a8 7101 3001 880b 000c 4000 0000 0007 +4500 002c 69b2 0000 ff2f ee9a c0a8 7103 c0a8 7101 +3001 880b 000c 4000 0000 0007 80fd 0404 000a 1206 0100 0001 # 23:18:36.590023 192.168.113.1 > 192.168.113.3: gre [KSAv1] ID:0000 S:6 A:7 ppp: IPCP 36: Conf-Req(5), IP-Addr=0.0.0.0, Pri-DNS=0.0.0.0, Pri-NBNS=0.0.0.0, Sec-DNS=0.0.0.0, Sec-NBNS=0.0.0.0 [in,pcn1] -4500 0048 5e1c 0000 802f 7915 c0a8 7101 -c0a8 7103 3081 880b 0024 0000 0000 0006 +4500 0048 5e1c 0000 802f 7915 c0a8 7101 c0a8 7103 +3081 880b 0024 0000 0000 0006 0000 0007 8021 0105 0022 0306 0000 0000 8106 0000 0000 8206 0000 0000 8306 0000 0000 8406 0000 0000 # 23:18:36.590489 192.168.113.3 > 192.168.113.1: gre [KSAv1] ID:4000 S:8 A:6 ppp: IPCP 30: Conf-Rej(5), Pri-DNS=0.0.0.0, Pri-NBNS=0.0.0.0, Sec-DNS=0.0.0.0, Sec-NBNS=0.0.0.0 [out,pcn1] -4500 0042 69b3 0000 ff2f ee83 c0a8 7103 -c0a8 7101 3081 880b 001e 4000 0000 0008 +4500 0042 69b3 0000 ff2f ee83 c0a8 7103 c0a8 7101 +3081 880b 001e 4000 0000 0008 0000 0006 8021 0405 001c 8106 0000 0000 8206 0000 0000 8306 0000 0000 8406 0000 0000 # 23:18:36.591003 192.168.113.1 > 192.168.113.3: gre [KSAv1] ID:0000 S:7 A:8 ppp: IPCP 12: Conf-Rej(1), IP-Comp VJ-Comp [in,pcn1] -4500 0030 5e1d 0000 802f 792c c0a8 7101 -c0a8 7103 3081 880b 000c 0000 0000 0007 +4500 0030 5e1d 0000 802f 792c c0a8 7101 c0a8 7103 +3081 880b 000c 0000 0000 0007 0000 0008 8021 0401 000a 0206 002d 0f01 # 23:18:36.593819 192.168.113.3 > 192.168.113.1: gre [KSAv1] ID:4000 S:9 A:7 ppp: IPCP 12: Conf-Req(2), IP-Addr=192.168.0.1 [out,pcn1] -4500 0030 69b4 0000 ff2f ee94 c0a8 7103 -c0a8 7101 3081 880b 000c 4000 0000 0009 +4500 0030 69b4 0000 ff2f ee94 c0a8 7103 c0a8 7101 +3081 880b 000c 4000 0000 0009 0000 0007 8021 0102 000a 0306 c0a8 0001 # 23:18:36.594840 192.168.113.1 > 192.168.113.3: gre [KSAv1] ID:0000 S:8 A:9 ppp: CCP 6: Conf-Ack(1) [in,pcn1] -4500 002a 5e1e 0000 802f 7931 c0a8 7101 -c0a8 7103 3081 880b 0006 0000 0000 0008 -0000 0009 80fd 0201 0004 0000 0000 +4500 002a 5e1e 0000 802f 7931 c0a8 7101 c0a8 7103 +3081 880b 0006 0000 0000 0008 +0000 0009 80fd 0201 0004 # 23:18:36.595525 192.168.113.1 > 192.168.113.3: gre [KSv1] ID:0000 S:9 ppp: CCP 18: Term-Req(6) [in,pcn1] -4500 0032 5e1f 0000 802f 7928 c0a8 7101 -c0a8 7103 3001 880b 0012 0000 0000 0009 +4500 0032 5e1f 0000 802f 7928 c0a8 7101 c0a8 7103 +3001 880b 0012 0000 0000 0009 80fd 0506 0010 577f 7c5b 003c cd74 0000 02dc # 23:18:36.595937 192.168.113.3 > 192.168.113.1: gre [KSAv1] ID:4000 S:10 A:9 ppp: CCP 6: Term-Ack(6) [out,pcn1] -4500 002a 69b5 0000 ff2f ee99 c0a8 7103 -c0a8 7101 3081 880b 0006 4000 0000 000a +4500 002a 69b5 0000 ff2f ee99 c0a8 7103 c0a8 7101 +3081 880b 0006 4000 0000 000a 0000 0009 80fd 0606 0004 diff --git a/contrib/ipfilter/test/input/ni14 b/contrib/ipfilter/test/input/ni14 index 681132120e2..6bc127610f2 100644 --- a/contrib/ipfilter/test/input/ni14 +++ b/contrib/ipfilter/test/input/ni14 @@ -1,19 +1,19 @@ # 23:18:36.130424 192.168.113.1.1511 > 192.168.113.3.1723: S 2884651685:2884651685(0) win 64240 (DF) [in,pcn1=192.168.113.3] -4500 0030 5e11 4000 8006 3961 c0a8 7101 -c0a8 7103 05e7 06bb abf0 4aa5 0000 0000 +4500 0030 5e11 4000 8006 3961 c0a8 7101 c0a8 7103 +05e7 06bb abf0 4aa5 0000 0000 7002 faf0 21a1 0000 0204 05b4 0101 0402 # 23:18:36.130778 192.168.113.3.1723 > 192.168.113.1.1511: S 2774821082:2774821082(0) ack 2884651686 win 32768 (DF) [out,pcn1] -4500 002c 69a6 4000 4006 207b 7f00 0001 -c0a8 7101 06bb 05e7 a564 68da abf0 4aa6 +4500 002c 69a6 4000 4006 207b 7f00 0001 c0a8 7101 +06bb 05e7 a564 68da abf0 4aa6 6012 8000 55f3 0000 0204 05b4 # 23:18:36.130784 192.168.113.1.1511 > 192.168.113.3.1723: P 1:157(156) ack 1 win 64240: pptp CTRL_MSGTYPE=SCCRQ PROTO_VER(1.0) FRAME_CAP(A) BEARER_CAP(A) MAX_CHAN(0) FIRM_REV(2600) HOSTNAME() VENDOR(Microsoft Windows NT) (DF) [in,pcn1] -4500 00c4 5e12 4000 8006 38cc c0a8 7101 -c0a8 7103 05e7 06bb abf0 4aa6 a564 68db +4500 00c4 5e12 4000 8006 38cc c0a8 7101 c0a8 7103 +05e7 06bb abf0 4aa6 a564 68db 5018 faf0 e2a0 0000 009c 0001 1a2b 3c4d 0001 0000 0100 0000 0000 0001 0000 0001 0000 0a28 0000 0000 0000 0000 0000 0000 @@ -28,8 +28,8 @@ c0a8 7103 05e7 06bb abf0 4aa6 a564 68db # 23:18:36.260235 192.168.113.3.1723 > 192.168.113.1.1511: P 1:157(156) ack 157 win 33580: pptp CTRL_MSGTYPE=SCCRP PROTO_VER(1.0) RESULT_CODE(1) ERR_CODE(0) FRAME_CAP() BEARER_CAP() MAX_CHAN(1) FIRM_REV(1) HOSTNAME(local) VENDOR(linux) (DF) [out,pcn1] -4500 00c4 69a7 4000 4006 1fe2 7f00 0001 -c0a8 7101 06bb 05e7 a564 68db abf0 4b42 +4500 00c4 69a7 4000 4006 1fe2 7f00 0001 c0a8 7101 +06bb 05e7 a564 68db abf0 4b42 5018 832c 817a 0000 009c 0001 1a2b 3c4d 0002 0000 0100 0100 0000 0000 0000 0000 0001 0001 6c6f 6361 6c00 0000 0000 0000 @@ -44,8 +44,8 @@ c0a8 7101 06bb 05e7 a564 68db abf0 4b42 # 23:18:36.260252 192.168.113.1.1511 > 192.168.113.3.1723: P 157:325(168) ack 157 win 64084: pptp CTRL_MSGTYPE=OCRQ CALL_ID(16384) CALL_SER_NUM(4913) MIN_BPS(300) MAX_BPS(100000000) BEARER_TYPE(Any) FRAME_TYPE(E) RECV_WIN(64) PROC_DELAY(0) PHONE_NO_LEN(0) PHONE_NO() SUB_ADDR() (DF) [in,pcn1] -4500 00d0 5e13 4000 8006 38bf c0a8 7101 -c0a8 7103 05e7 06bb abf0 4b42 a564 6977 +4500 00d0 5e13 4000 8006 38bf c0a8 7101 c0a8 7103 +05e7 06bb abf0 4b42 a564 6977 5018 fa54 ac07 0000 00a8 0001 1a2b 3c4d 0007 0000 4000 1331 0000 012c 05f5 e100 0000 0003 0000 0003 0040 0000 0000 0000 @@ -60,176 +60,176 @@ c0a8 7103 05e7 06bb abf0 4b42 a564 6977 # 23:18:36.272856 192.168.113.3.1723 > 192.168.113.1.1511: P 157:189(32) ack 325 win 33580: pptp CTRL_MSGTYPE=OCRP CALL_ID(0) PEER_CALL_ID(16384) RESULT_CODE(1) ERR_CODE(0) CAUSE_CODE(0) CONN_SPEED(100000000) RECV_WIN(64) PROC_DELAY(0) PHY_CHAN_ID(0) (DF) [out,pcn1] -4500 0048 69a8 4000 4006 205d 7f00 0001 -c0a8 7101 06bb 05e7 a564 6977 abf0 4bea +4500 0048 69a8 4000 4006 205d 7f00 0001 c0a8 7101 +06bb 05e7 a564 6977 abf0 4bea 5018 832c e9a4 0000 0020 0001 1a2b 3c4d 0008 0000 0000 4000 0100 0000 05f5 e100 0040 0000 0000 0000 # 23:18:36.321819 192.168.113.1.1511 > 192.168.113.3.1723: P 325:349(24) ack 189 win 64052: pptp CTRL_MSGTYPE=SLI PEER_CALL_ID(0) SEND_ACCM(0xffffffff) RECV_ACCM(0xffffffff) (DF) [in,pcn1] -4500 0040 5e14 4000 8006 394e c0a8 7101 -c0a8 7103 05e7 06bb abf0 4bea a564 6997 +4500 0040 5e14 4000 8006 394e c0a8 7101 c0a8 7103 +05e7 06bb abf0 4bea a564 6997 5018 fa34 e810 0000 0018 0001 1a2b 3c4d 000f 0000 0000 0000 ffff ffff ffff ffff # 23:18:36.349759 192.168.113.1 > 192.168.113.3: gre [KSv1] ID:0000 S:0 ppp: LCP 25: Conf-Req(0), MRU=1400, Magic-Num=577f7c5b, PFC, ACFC, Call-Back CBCP [in,pcn1] -4500 0039 5e15 0000 802f 792b c0a8 7101 -c0a8 7103 3001 880b 0019 0000 0000 0000 +4500 0039 5e15 0000 802f 792b c0a8 7101 c0a8 7103 +3001 880b 0019 0000 0000 0000 ff03 c021 0100 0015 0104 0578 0506 577f 7c5b 0702 0802 0d03 06 # 23:18:36.389970 192.168.113.3 > 192.168.113.1: gre [KAv1] ID:4000 A:4294967295 [|gre] [out,pcn1] -4500 0020 69a9 0000 ff2f a15a 7f00 0001 -c0a8 7101 2081 880b 0000 4000 ffff ffff +4500 0020 69a9 0000 ff2f a15a 7f00 0001 c0a8 7101 +2081 880b 0000 4000 ffff ffff # 23:18:36.518426 192.168.113.3.1723 > 192.168.113.1.1511: . ack 349 win 33580 (DF) [out,pcn1] -4500 0028 69aa 4000 4006 207b 7f00 0001 -c0a8 7101 06bb 05e7 a564 6997 abf0 4c02 +4500 0028 69aa 4000 4006 207b 7f00 0001 c0a8 7101 +06bb 05e7 a564 6997 abf0 4c02 5010 832c 686c 0000 # 23:18:36.555363 192.168.113.3 > 192.168.113.1: gre [KSv1] ID:4000 S:0 ppp: LCP 24: Conf-Req(1), ACCM=00000000, Magic-Num=22d90cfa, PFC, ACFC [out,pcn1] -4500 0038 69ab 0000 ff2f a140 7f00 0001 -c0a8 7101 3001 880b 0018 4000 0000 0000 +4500 0038 69ab 0000 ff2f a140 7f00 0001 c0a8 7101 +3001 880b 0018 4000 0000 0000 ff03 c021 0101 0014 0206 0000 0000 0506 22d9 0cfa 0702 0802 # 23:18:36.556030 192.168.113.3 > 192.168.113.1: gre [KSAv1] ID:4000 S:1 A:0 ppp: LCP 11: Conf-Rej(0), Call-Back CBCP [out,pcn1] -4500 002f 69ac 0000 ff2f a148 7f00 0001 -c0a8 7101 3081 880b 000b 4000 0000 0001 +4500 002f 69ac 0000 ff2f a148 7f00 0001 c0a8 7101 +3081 880b 000b 4000 0000 0001 0000 0000 ff03 c021 0400 0007 0d03 06 # 23:18:36.557166 192.168.113.1 > 192.168.113.3: gre [KSAv1] ID:0000 S:1 A:1 ppp: LCP 24: Conf-Ack(1), ACCM=00000000, Magic-Num=22d90cfa, PFC, ACFC [in,pcn1] -4500 003c 5e16 0000 802f 7927 c0a8 7101 -c0a8 7103 3081 880b 0018 0000 0000 0001 +4500 003c 5e16 0000 802f 7927 c0a8 7101 c0a8 7103 +3081 880b 0018 0000 0000 0001 0000 0001 ff03 c021 0201 0014 0206 0000 0000 0506 22d9 0cfa 0702 0802 # 23:18:36.557764 192.168.113.1 > 192.168.113.3: gre [KSv1] ID:0000 S:2 ppp: LCP 22: Conf-Req(1), MRU=1400, Magic-Num=577f7c5b, PFC, ACFC [in,pcn1] -4500 0036 5e17 0000 802f 792c c0a8 7101 -c0a8 7103 3001 880b 0016 0000 0000 0002 +4500 0036 5e17 0000 802f 792c c0a8 7101 c0a8 7103 +3001 880b 0016 0000 0000 0002 ff03 c021 0101 0012 0104 0578 0506 577f 7c5b 0702 0802 # 23:18:36.564658 192.168.113.3 > 192.168.113.1: gre [KSAv1] ID:4000 S:2 A:2 ppp: LCP 22: Conf-Ack(1), MRU=1400, Magic-Num=577f7c5b, PFC, ACFC [out,pcn1] -4500 003a 69ad 0000 ff2f a13c 7f00 0001 -c0a8 7101 3081 880b 0016 4000 0000 0002 +4500 003a 69ad 0000 ff2f a13c 7f00 0001 c0a8 7101 +3081 880b 0016 4000 0000 0002 0000 0002 ff03 c021 0201 0012 0104 0578 0506 577f 7c5b 0702 0802 # 23:18:36.564803 192.168.113.3 > 192.168.113.1: gre [KSv1] ID:4000 S:3 ppp: IPCP 18: Conf-Req(1), IP-Addr=192.168.0.1, IP-Comp VJ-Comp [out,pcn1] -4500 0032 69ae 0000 ff2f a143 7f00 0001 -c0a8 7101 3001 880b 0012 4000 0000 0003 +4500 0032 69ae 0000 ff2f a143 7f00 0001 c0a8 7101 +3001 880b 0012 4000 0000 0003 8021 0101 0010 0306 c0a8 0001 0206 002d 0f01 # 23:18:36.570395 192.168.113.1.1511 > 192.168.113.3.1723: P 349:373(24) ack 189 win 64052: pptp CTRL_MSGTYPE=SLI PEER_CALL_ID(0) SEND_ACCM(0x00000000) RECV_ACCM(0xffffffff) (DF) [in,pcn1] -4500 0040 5e18 4000 8006 394a c0a8 7101 -c0a8 7103 05e7 06bb abf0 4c02 a564 6997 +4500 0040 5e18 4000 8006 394a c0a8 7101 c0a8 7103 +05e7 06bb abf0 4c02 a564 6997 5018 fa34 e7f8 0000 0018 0001 1a2b 3c4d 000f 0000 0000 0000 0000 0000 ffff ffff # 23:18:36.573307 192.168.113.1 > 192.168.113.3: gre [KSAv1] ID:0000 S:3 A:3 ppp: LCP 20: Ident(2), Magic-Num=577f7c5b [in,pcn1] -4500 0038 5e19 0000 802f 7928 c0a8 7101 -c0a8 7103 3081 880b 0014 0000 0000 0003 +4500 0038 5e19 0000 802f 7928 c0a8 7101 c0a8 7103 +3081 880b 0014 0000 0000 0003 0000 0003 c021 0c02 0012 577f 7c5b 4d53 5241 5356 352e 3130 # 23:18:36.573856 192.168.113.3 > 192.168.113.1: gre [KSAv1] ID:4000 S:4 A:3 ppp: LCP 26: Code-Rej(2) [out,pcn1] -4500 003e 69af 0000 ff2f a136 7f00 0001 -c0a8 7101 3081 880b 001a 4000 0000 0004 +4500 003e 69af 0000 ff2f a136 7f00 0001 c0a8 7101 +3081 880b 001a 4000 0000 0004 0000 0003 ff03 c021 0702 0016 0c02 0012 577f 7c5b 4d53 5241 5356 352e 3130 # 23:18:36.584936 192.168.113.1 > 192.168.113.3: gre [KSAv1] ID:0000 S:4 A:4 ppp: LCP 26: Ident(3), Magic-Num=577f7c5b [in,pcn1] -4500 003e 5e1a 0000 802f 7921 c0a8 7101 -c0a8 7103 3081 880b 001a 0000 0000 0004 +4500 003e 5e1a 0000 802f 7921 c0a8 7101 c0a8 7103 +3081 880b 001a 0000 0000 0004 0000 0004 c021 0c03 0018 577f 7c5b 4d53 5241 532d 302d 434c 4159 4d4f 4f52 # 23:18:36.585562 192.168.113.3 > 192.168.113.1: gre [KSAv1] ID:4000 S:5 A:4 ppp: LCP 32: Code-Rej(3) [out,pcn1] -4500 0044 69b0 0000 ff2f a12f 7f00 0001 -c0a8 7101 3081 880b 0020 4000 0000 0005 +4500 0044 69b0 0000 ff2f a12f 7f00 0001 c0a8 7101 +3081 880b 0020 4000 0000 0005 0000 0004 ff03 c021 0703 001c 0c03 0018 577f 7c5b 4d53 5241 532d 302d 434c 4159 4d4f 4f52 # 23:18:36.588721 192.168.113.1 > 192.168.113.3: gre [KSAv1] ID:0000 S:5 A:5 ppp: CCP 12: Conf-Req(4), MPPC [in,pcn1] -4500 0030 5e1b 0000 802f 792e c0a8 7101 -c0a8 7103 3081 880b 000c 0000 0000 0005 +4500 0030 5e1b 0000 802f 792e c0a8 7101 c0a8 7103 +3081 880b 000c 0000 0000 0005 0000 0005 80fd 0104 000a 1206 0100 0001 # 23:18:36.589445 192.168.113.3 > 192.168.113.1: gre [KSAv1] ID:4000 S:6 A:5 ppp: CCP 6: Conf-Req(1) [out,pcn1] -4500 002a 69b1 0000 ff2f a148 7f00 0001 -c0a8 7101 3081 880b 0006 4000 0000 0006 +4500 002a 69b1 0000 ff2f a148 7f00 0001 c0a8 7101 +3081 880b 0006 4000 0000 0006 0000 0005 80fd 0101 0004 # 23:18:36.589540 192.168.113.3 > 192.168.113.1: gre [KSv1] ID:4000 S:7 ppp: CCP 12: Conf-Rej(4), MPPC [out,pcn1] -4500 002c 69b2 0000 ff2f a145 7f00 0001 -c0a8 7101 3001 880b 000c 4000 0000 0007 +4500 002c 69b2 0000 ff2f a145 7f00 0001 c0a8 7101 +3001 880b 000c 4000 0000 0007 80fd 0404 000a 1206 0100 0001 # 23:18:36.590023 192.168.113.1 > 192.168.113.3: gre [KSAv1] ID:0000 S:6 A:7 ppp: IPCP 36: Conf-Req(5), IP-Addr=0.0.0.0, Pri-DNS=0.0.0.0, Pri-NBNS=0.0.0.0, Sec-DNS=0.0.0.0, Sec-NBNS=0.0.0.0 [in,pcn1] -4500 0048 5e1c 0000 802f 7915 c0a8 7101 -c0a8 7103 3081 880b 0024 0000 0000 0006 +4500 0048 5e1c 0000 802f 7915 c0a8 7101 c0a8 7103 +3081 880b 0024 0000 0000 0006 0000 0007 8021 0105 0022 0306 0000 0000 8106 0000 0000 8206 0000 0000 8306 0000 0000 8406 0000 0000 # 23:18:36.590489 192.168.113.3 > 192.168.113.1: gre [KSAv1] ID:4000 S:8 A:6 ppp: IPCP 30: Conf-Rej(5), Pri-DNS=0.0.0.0, Pri-NBNS=0.0.0.0, Sec-DNS=0.0.0.0, Sec-NBNS=0.0.0.0 [out,pcn1] -4500 0042 69b3 0000 ff2f a12e 7f00 0001 -c0a8 7101 3081 880b 001e 4000 0000 0008 +4500 0042 69b3 0000 ff2f a12e 7f00 0001 c0a8 7101 +3081 880b 001e 4000 0000 0008 0000 0006 8021 0405 001c 8106 0000 0000 8206 0000 0000 8306 0000 0000 8406 0000 0000 # 23:18:36.591003 192.168.113.1 > 192.168.113.3: gre [KSAv1] ID:0000 S:7 A:8 ppp: IPCP 12: Conf-Rej(1), IP-Comp VJ-Comp [in,pcn1] -4500 0030 5e1d 0000 802f 792c c0a8 7101 -c0a8 7103 3081 880b 000c 0000 0000 0007 +4500 0030 5e1d 0000 802f 792c c0a8 7101 c0a8 7103 +3081 880b 000c 0000 0000 0007 0000 0008 8021 0401 000a 0206 002d 0f01 # 23:18:36.593819 192.168.113.3 > 192.168.113.1: gre [KSAv1] ID:4000 S:9 A:7 ppp: IPCP 12: Conf-Req(2), IP-Addr=192.168.0.1 [out,pcn1] -4500 0030 69b4 0000 ff2f a13f 7f00 0001 -c0a8 7101 3081 880b 000c 4000 0000 0009 +4500 0030 69b4 0000 ff2f a13f 7f00 0001 c0a8 7101 +3081 880b 000c 4000 0000 0009 0000 0007 8021 0102 000a 0306 c0a8 0001 # 23:18:36.594840 192.168.113.1 > 192.168.113.3: gre [KSAv1] ID:0000 S:8 A:9 ppp: CCP 6: Conf-Ack(1) [in,pcn1] -4500 002a 5e1e 0000 802f 7931 c0a8 7101 -c0a8 7103 3081 880b 0006 0000 0000 0008 -0000 0009 80fd 0201 0004 0000 0000 +4500 002a 5e1e 0000 802f 7931 c0a8 7101 c0a8 7103 +3081 880b 0006 0000 0000 0008 +0000 0009 80fd 0201 0004 # 23:18:36.595525 192.168.113.1 > 192.168.113.3: gre [KSv1] ID:0000 S:9 ppp: CCP 18: Term-Req(6) [in,pcn1] -4500 0032 5e1f 0000 802f 7928 c0a8 7101 -c0a8 7103 3001 880b 0012 0000 0000 0009 +4500 0032 5e1f 0000 802f 7928 c0a8 7101 c0a8 7103 +3001 880b 0012 0000 0000 0009 80fd 0506 0010 577f 7c5b 003c cd74 0000 02dc # 23:18:36.595937 192.168.113.3 > 192.168.113.1: gre [KSAv1] ID:4000 S:10 A:9 ppp: CCP 6: Term-Ack(6) [out,pcn1] -4500 002a 69b5 0000 ff2f a144 7f00 0001 -c0a8 7101 3081 880b 0006 4000 0000 000a +4500 002a 69b5 0000 ff2f a144 7f00 0001 c0a8 7101 +3081 880b 0006 4000 0000 000a 0000 0009 80fd 0606 0004 diff --git a/contrib/ipfilter/test/input/ni15 b/contrib/ipfilter/test/input/ni15 index fb445bb93c6..7e7aabde158 100644 --- a/contrib/ipfilter/test/input/ni15 +++ b/contrib/ipfilter/test/input/ni15 @@ -218,7 +218,7 @@ c0a8 7101 3081 880b 000c 4000 0000 0009 [out,pcn1] 4500 002a 5e1e 0000 802f 7931 c0a8 7101 c0a8 7103 3081 880b 0006 0000 0000 0008 -0000 0009 80fd 0201 0004 0000 0000 +0000 0009 80fd 0201 0004 # 23:18:36.595525 192.168.113.1 > 192.168.113.3: gre [KSv1] ID:0000 S:9 ppp: CCP 18: Term-Req(6) [out,pcn1] diff --git a/contrib/ipfilter/test/input/ni16 b/contrib/ipfilter/test/input/ni16 index 24bfcfc3835..362b98d09c4 100644 --- a/contrib/ipfilter/test/input/ni16 +++ b/contrib/ipfilter/test/input/ni16 @@ -218,7 +218,7 @@ c0a8 7101 3081 880b 000c 4000 0000 0009 [out,pcn1] 4500 002a 5e1e 0000 802f 9ed7 0a02 0202 c0a8 7103 3081 880b 0006 0000 0000 0008 -0000 0009 80fd 0201 0004 0000 0000 +0000 0009 80fd 0201 0004 # 23:18:36.595525 192.168.113.1 > 192.168.113.3: gre [KSv1] ID:0000 S:9 ppp: CCP 18: Term-Req(6) [out,pcn1] diff --git a/contrib/ipfilter/test/input/ni18 b/contrib/ipfilter/test/input/ni18 new file mode 100644 index 00000000000..4e06f790844 --- /dev/null +++ b/contrib/ipfilter/test/input/ni18 @@ -0,0 +1,4 @@ +in on hme0 tcp 2.2.2.2,3000 192.168.1.2,80 +in on hme0 tcp 2.2.2.2,3000 192.168.1.1,80 +out on hme1 tcp 10.1.2.2,5050 4.5.6.7,80; +out on hme1 tcp 10.1.1.2,5050 4.5.6.7,80; diff --git a/contrib/ipfilter/test/input/ni19 b/contrib/ipfilter/test/input/ni19 index d95e68afc7b..3ea706fcbcd 100644 --- a/contrib/ipfilter/test/input/ni19 +++ b/contrib/ipfilter/test/input/ni19 @@ -28,7 +28,7 @@ b002 8000 7d87 0000 0204 05b4 0103 0300 [in,bge0] 4500 0028 7ce5 4000 4006 a7e4 0a01 0104 0a01 0101 0202 03f1 915a a5c5 6523 90b8 -5010 05b4 612b 0000 0000 0000 0000 +5010 05b4 612b 0000 # 10.1.1.4.1023 > 10.1.1.1.1008: SYN win 5840 [in,bge0] @@ -61,7 +61,7 @@ b012 8000 1e85 0000 0204 05b4 0103 0300 [in,bge0] 4500 0028 7ce7 4000 4006 a7e2 0a01 0104 0a01 0101 0202 03f1 915a a5c5 6523 90c0 -5010 05b4 6123 0000 0000 0000 0000 +5010 05b4 6123 0000 # 192.168.113.3.1009 > 10.1.1.4.shell [out,bge0] @@ -76,13 +76,13 @@ b012 8000 1e85 0000 0204 05b4 0103 0300 [in,bge0] 4500 0028 7ce9 4000 4006 a7e0 0a01 0104 0a01 0101 0202 03f1 915a a5c5 6523 90eb -5010 05b4 60f8 0000 0000 0000 0000 +5010 05b4 60f8 0000 # 10.1.1.4.shell > 10.1.1.1.1009 [in,bge0] 4500 0029 7ceb 4000 4006 a7dd 0a01 0104 0a01 0101 0202 03f1 915a a5c5 6523 90eb -5018 05b4 60ef 0000 0000 0000 0000 +5018 05b4 60ef 0000 00 # 192.168.113.3.1009 > 10.1.1.4.shell [out,bge0] @@ -94,7 +94,7 @@ b012 8000 1e85 0000 0204 05b4 0103 0300 [in,bge0] 4500 002c 7ced 4000 4006 a7d8 0a01 0104 0a01 0101 0202 03f1 915a a5c6 6523 90eb -5018 05b4 8b71 0000 666f 6f0a 0000 +5018 05b4 8b71 0000 666f 6f0a # 10.1.1.4.1023 > 10.1.1.1.1008 [in,bge0] @@ -107,7 +107,7 @@ b012 8000 1e85 0000 0204 05b4 0103 0300 [in,bge0] 4500 0028 7cef 4000 4006 a7da 0a01 0104 0a01 0101 0202 03f1 915a a5ca 6523 90eb -5011 05b4 60f2 0000 0000 0000 0000 +5011 05b4 60f2 0000 # 10.1.1.4.1023 > 10.1.1.1.1008 [in,bge0] @@ -146,7 +146,7 @@ b012 8000 1e85 0000 0204 05b4 0103 0300 [in,bge0] 4500 0028 0004 4000 4006 24c6 0a01 0104 0a01 0101 0202 03f1 915a a5cb 6523 90ec -5010 05b4 60f1 0000 0000 0000 0000 +5010 05b4 60f1 0000 # 10.1.1.4.1023 > 10.1.1.1.1008 [in,bge0] diff --git a/contrib/ipfilter/test/input/ni2 b/contrib/ipfilter/test/input/ni2 index 30458212bb0..6dcedb7f0ff 100644 --- a/contrib/ipfilter/test/input/ni2 +++ b/contrib/ipfilter/test/input/ni2 @@ -1,29 +1,21 @@ # Test of fragmentation required coming from the inside. [out,xl0] -4510 002c bd0d 4000 3e06 b1d1 -0a01 0201 -c0a8 0133 +4510 002c bd0d 4000 3e06 b1d1 0a01 0201 c0a8 0133 05f6 0077 a664 2485 0000 0000 6002 4000 b8f2 0000 0204 05b4 [in,xl0] -4500 002c ce83 4000 7e06 606b -c0a8 0133 -0a01 0201 +4500 002c ce83 4000 7e06 606b c0a8 0133 0a01 0201 0077 05f6 fbdf 1a21 a664 2486 -6012 2238 c0a8 0000 0204 05b4 0000 +6012 2238 c0a8 0000 0204 05b4 [out,xl0] -4510 0028 bd0e 4000 3e06 b1d4 -0a01 0201 -c0a8 0133 +4510 0028 bd0e 4000 3e06 b1d4 0a01 0201 c0a8 0133 05f6 0077 a664 2486 fbdf 1a22 5010 4470 b62d 0000 [in,xl0] -4500 005b cf83 4000 7e06 5f3c -c0a8 0133 -0a01 0201 +4500 005b cf83 4000 7e06 5f3c c0a8 0133 0a01 0201 0077 05f6 fbdf 1a22 a664 2486 5018 2238 ce2a 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 @@ -31,32 +23,24 @@ c0a8 0133 0000 0000 0000 0000 0000 0a [out,xl0] -4510 0028 bd18 4000 3e06 b1ca -0a01 0201 -c0a8 0133 +4510 0028 bd18 4000 3e06 b1ca 0a01 0201 c0a8 0133 05f6 0077 a664 2486 fbdf 1a55 5010 4470 b5fa 0000 [out,xl0] -4510 002e bd1e 4000 3e06 b1be -0a01 0201 -c0a8 0133 +4510 002e bd1e 4000 3e06 b1be 0a01 0201 c0a8 0133 05f6 0077 a664 2486 fbdf 1a55 5018 4470 a8e2 0000 0000 0000 0d0a [in,xl0] -4500 0048 e383 4000 7e06 4b4f -c0a8 0133 -0a01 0201 +4500 0048 e383 4000 7e06 4b4f c0a8 0133 0a01 0201 0077 05f6 fbdf 1a55 a664 248c 5018 2232 d80a 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 [in,xl0] -4500 05dc e483 4000 7e06 44bb -c0a8 0133 -0a01 0201 +4500 05dc e483 4000 7e06 44bb c0a8 0133 0a01 0201 0077 05f6 fbdf 1a75 a664 248c 5010 2232 9f2d 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 @@ -152,10 +136,8 @@ c0a8 0133 0000 0000 0000 0000 0000 0000 [out,xl0] -4500 0038 d71d 4000 4001 7d22 -c0a8 6401 -c0a8 0133 -0304 3435 0000 05a0 +4500 0038 d71d 4000 4001 7d22 c0a8 6401 c0a8 0133 +0304 da99 0000 05a0 4500 05dc e483 4000 7e06 44bb c0a8 0133 0a01 0201 -0077 05f6 fbdf 1a75 a664 +0077 05f6 fbdf 1a75 diff --git a/contrib/ipfilter/test/input/ni20 b/contrib/ipfilter/test/input/ni20 index 4c2b87e4de3..065ed27ba79 100644 --- a/contrib/ipfilter/test/input/ni20 +++ b/contrib/ipfilter/test/input/ni20 @@ -28,7 +28,7 @@ c0a8 7103 0202 03f1 915a a5c4 6523 90b3 [out,bge0] 4500 0028 7ce5 4000 4006 5a92 c0a8 7104 c0a8 7103 0202 03f1 915a a5c5 6523 90b8 -5010 05b4 13d9 0000 0000 0000 0000 +5010 05b4 13d9 0000 # 192.168.113.4.1023 > 192.168.113.3.1008: SYN win 5840 [out,bge0] @@ -44,26 +44,26 @@ a002 16d0 9218 0000 0204 05b4 0402 080a b012 8000 1e85 0000 0204 05b4 0103 0300 0101 080a 0000 0000 0039 d924 0402 0101 -# 192.168.113.4.1023 > 192.168.113.3.1008 +# 192.168.113.4.1023 > 192.168.113.3.1008 ACK [out,bge0] 4500 0034 1188 4000 4006 c5e3 c0a8 7104 c0a8 7103 03ff 03f0 91d4 c8a3 66e5 b811 8010 05b4 b2f3 0000 0101 080a 0039 d925 0000 0000 -# 192.168.113.3.1009 > 10.1.1.4.shell +# 192.168.113.3.1009 > 10.1.1.4.shell PUSH+ACK [in,bge0] 4500 0030 e400 4000 4006 1a17 c0a8 7103 0a01 0104 03f1 0202 6523 90b8 915a a5c5 5018 832c 0eb6 0000 6461 7272 656e 7200 -# 192.168.113.4.shell > 192.168.113.3.1009 +# 192.168.113.4.shell > 192.168.113.3.1009 ACK [out,bge0] 4500 0028 7ce7 4000 4006 5a90 c0a8 7104 c0a8 7103 0202 03f1 915a a5c5 6523 90c0 -5010 05b4 13d1 0000 0000 0000 0000 +5010 05b4 13d1 0000 -# 192.168.113.3.1009 > 10.1.1.4.shell +# 192.168.113.3.1009 > 10.1.1.4.shell PUSH+ACK [in,bge0] 4500 0053 e401 4000 4006 19f3 c0a8 7103 0a01 0104 03f1 0202 6523 90c0 915a a5c5 @@ -72,83 +72,83 @@ c0a8 7103 0202 03f1 915a a5c5 6523 90c0 3e26 313b 2065 6368 6f20 6261 7220 3e26 3222 00 -# 192.168.113.4.shell > 192.168.113.3.1009 +# 192.168.113.4.shell > 192.168.113.3.1009 ACK [out,bge0] 4500 0028 7ce9 4000 4006 5a8e c0a8 7104 c0a8 7103 0202 03f1 915a a5c5 6523 90eb -5010 05b4 13a6 0000 0000 0000 0000 +5010 05b4 13a6 0000 -# 192.168.113.4.shell > 192.168.113.3.1009 +# 192.168.113.4.shell > 192.168.113.3.1009 PUSH+ACK [out,bge0] 4500 0029 7ceb 4000 4006 5a8b c0a8 7104 c0a8 7103 0202 03f1 915a a5c5 6523 90eb -5018 05b4 139d 0000 0000 0000 0000 +5018 05b4 139d 0000 00 -# 192.168.113.3.1009 > 10.1.1.4.shell +# 192.168.113.3.1009 > 10.1.1.4.shell ACK [in,bge0] 4500 0028 e403 4000 4006 1a1c c0a8 7103 0a01 0104 03f1 0202 6523 90eb 915a a5c6 5010 832c bcd4 0000 -# 192.168.113.4.shell > 192.168.113.3.1009 +# 192.168.113.4.shell > 192.168.113.3.1009 PUSH+ACK [out,bge0] 4500 002c 7ced 4000 4006 5a86 c0a8 7104 c0a8 7103 0202 03f1 915a a5c6 6523 90eb -5018 05b4 3e1f 0000 666f 6f0a 0000 +5018 05b4 3e1f 0000 666f 6f0a -# 192.168.113.4.1023 > 192.168.113.3.1008 +# 192.168.113.4.1023 > 192.168.113.3.1008 PUSH+ACK [out,bge0] 4500 0038 118a 4000 4006 c5dd c0a8 7104 c0a8 7103 03ff 03f0 91d4 c8a3 66e5 b811 8018 05b4 da34 0000 0101 080a 0039 dd6c 0000 0000 6261 720a -# 192.168.113.4.shell > 192.168.113.3.1009 +# 192.168.113.4.shell > 192.168.113.3.1009 FIN+ACK [out,bge0] 4500 0028 7cef 4000 4006 5a88 c0a8 7104 c0a8 7103 0202 03f1 915a a5ca 6523 90eb -5011 05b4 13a0 0000 0000 0000 0000 +5011 05b4 13a0 0000 -# 192.168.113.4.1023 > 192.168.113.3.1008 +# 192.168.113.4.1023 > 192.168.113.3.1008 FIN+ACK [out,bge0] 4500 0034 118c 4000 4006 c5df c0a8 7104 c0a8 7103 03ff 03f0 91d4 c8a7 66e5 b811 8011 05b4 aea6 0000 0101 080a 0039 dd6d 0000 0000 -# 192.168.113.3.1009 > 10.1.1.4.shell +# 192.168.113.3.1009 > 10.1.1.4.shell ACK [in,bge0] 4500 0028 e404 4000 4006 1a1b c0a8 7103 0a01 0104 03f1 0202 6523 90eb 915a a5cb 5010 8328 bcd3 0000 -# 192.168.113.3.1008 > 10.1.1.4.1023 +# 192.168.113.3.1008 > 10.1.1.4.1023 ACK [in,bge0] 4500 0034 e405 4000 4006 1a0e c0a8 7103 0a01 0104 03f0 03ff 66e5 b811 91d4 c8a8 8010 8328 57d7 0000 0101 080a 0000 0004 0039 dd6c -# 192.168.113.3.1009 > 10.1.1.4.shell +# 192.168.113.3.1009 > 10.1.1.4.shell FIN+ACK [in,bge0] 4500 0028 e40a 4000 4006 1a15 c0a8 7103 0a01 0104 03f1 0202 6523 90eb 915a a5cb 5011 832c bcce 0000 -# 192.168.113.3.1008 > 10.1.1.4.1023 +# 192.168.113.3.1008 > 10.1.1.4.1023 FIN+ACK [in,bge0] 4500 0034 e40b 4000 4006 1a08 c0a8 7103 0a01 0104 03f0 03ff 66e5 b811 91d4 c8a8 8011 832c 57d2 0000 0101 080a 0000 0004 0039 dd6c -# 192.168.113.4.shell > 192.168.113.3.1009 +# 192.168.113.4.shell > 192.168.113.3.1009 ACK [out,bge0] 4500 0028 0004 4000 4006 d773 c0a8 7104 c0a8 7103 0202 03f1 915a a5cb 6523 90ec -5010 05b4 139f 0000 0000 0000 0000 +5010 05b4 139f 0000 -# 192.168.113.4.1023 > 192.168.113.3.1008 +# 192.168.113.4.1023 > 192.168.113.3.1008 ACK [out,bge0] 4500 0034 118e 4000 4006 c5dd c0a8 7104 c0a8 7103 03ff 03f0 91d4 c8a8 66e5 b812 diff --git a/contrib/ipfilter/test/input/ni3 b/contrib/ipfilter/test/input/ni3 index 66b22a6d429..e4d12fe1dee 100644 --- a/contrib/ipfilter/test/input/ni3 +++ b/contrib/ipfilter/test/input/ni3 @@ -1,10 +1,20 @@ #v tos len id off ttl p sum src dst # ICMP dest unreachable with 64 bits in payload (in reply to a TCP packet # going out) -[out,df0] 45 00 00 3c 47 06 40 00 ff 06 28 aa 02 02 02 02 04 04 04 04 50 00 00 50 00 00 00 01 00 00 00 00 a0 02 16 d0 d8 e2 00 00 02 04 05 b4 04 02 08 0a 00 47 fb b0 00 00 00 00 01 03 03 00 +[out,df0] +4500 003c 4706 4000 ff06 28aa 0202 0202 0404 0404 +5000 0050 0000 0001 0000 0000 a002 16d0 d8e2 0000 0204 05b4 0402 080a 0047 fbb0 0000 0000 0103 0300 -[in,df0] 45 00 00 38 80 9a 00 00 ff 01 29 19 03 03 03 03 06 06 06 06 03 03 ac ab 00 00 00 00 45 00 00 3c 47 06 40 00 ff 06 20 a2 06 06 06 06 04 04 04 04 50 00 00 50 00 00 00 01 +[in,df0] +4500 0038 809a 0000 ff01 2919 0303 0303 0606 0606 +0303 acab 0000 0000 +4500 003c 4706 4000 ff06 20a2 0606 0606 0404 0404 +5000 0050 0000 0001 # ICMP dest unreachable with whole packet in payload (40 bytes = 320 bits) -[in,df0] 45 00 00 58 80 9a 00 00 ff 01 28 f9 03 03 03 03 06 06 06 06 03 03 11 3f 00 00 00 00 45 00 00 3c 47 06 40 00 ff 06 20 a2 06 06 06 06 04 04 04 04 50 00 00 50 00 00 00 01 00 00 00 00 a0 02 16 d0 d0 da 00 00 02 04 05 b4 04 02 08 0a 00 47 fb b0 00 00 00 00 01 03 03 00 +[in,df0] +4500 0058 809a 0000 ff01 28f9 0303 0303 0606 0606 +0303 113f 0000 0000 +4500 003c 4706 4000 ff06 20a2 0606 0606 0404 0404 +5000 0050 0000 0001 0000 0000 a002 16d0 d0da 0000 0204 05b4 0402 080a 0047 fbb0 0000 0000 0103 0300 diff --git a/contrib/ipfilter/test/input/ni4 b/contrib/ipfilter/test/input/ni4 index ad5575f9531..dac9f53b8e6 100644 --- a/contrib/ipfilter/test/input/ni4 +++ b/contrib/ipfilter/test/input/ni4 @@ -1,10 +1,18 @@ #v tos len id off ttl p sum src dst # ICMP dest unreachable with 64 bits in payload (in reply to a TCP packet # going out) -[out,df0] 45 00 00 3c 47 06 40 00 ff 06 28 aa 02 02 02 02 04 04 04 04 50 00 00 50 00 00 00 01 00 00 00 00 a0 02 16 d0 d8 e2 00 00 02 04 05 b4 04 02 08 0a 00 47 fb b0 00 00 00 00 01 03 03 00 +[out,df0] +4500 003c 4706 4000 ff06 28aa 0202 0202 0404 0404 +5000 0050 0000 0001 0000 0000 a002 16d0 d8e2 0000 0204 05b4 0402 080a 0047 fbb0 0000 0000 0103 0300 -[in,df0] 45 00 00 38 80 9a 00 00 ff 01 29 19 03 03 03 03 06 06 06 06 03 03 60 6b 00 00 00 00 45 00 00 3c 47 06 40 00 ff 06 20 a2 06 06 06 06 04 04 04 04 9c 40 00 50 00 00 00 01 +[in,df0] +4500 0038 809a 0000 ff01 2919 0303 0303 0606 0606 +0303 606b 0000 0000 4500 003c 4706 4000 ff06 20a2 0606 0606 0404 0404 9c40 0050 0000 0001 # ICMP dest unreachable with whole packet in payload (40 bytes = 320 bits) -[in,df0] 45 00 00 58 80 9a 00 00 ff 01 28 f9 03 03 03 03 06 06 06 06 03 03 11 3f 00 00 00 00 45 00 00 3c 47 06 40 00 ff 06 20 a2 06 06 06 06 04 04 04 04 9c 40 00 50 00 00 00 01 00 00 00 00 a0 02 16 d0 84 9a 00 00 02 04 05 b4 04 02 08 0a 00 47 fb b0 00 00 00 00 01 03 03 00 +[in,df0] +4500 0058 809a 0000 ff01 28f9 0303 0303 0606 0606 +0303 113f 0000 0000 +4500 003c 4706 4000 ff06 20a2 0606 0606 0404 0404 +9c40 0050 0000 0001 0000 0000 a002 16d0 849a 0000 0204 05b4 0402 080a 0047 fbb0 0000 0000 0103 0300 diff --git a/contrib/ipfilter/test/input/ni5 b/contrib/ipfilter/test/input/ni5 index c45be54266f..4b32e49d091 100644 --- a/contrib/ipfilter/test/input/ni5 +++ b/contrib/ipfilter/test/input/ni5 @@ -203,7 +203,7 @@ 0101 0101 0014 8033 d9f8 11d5 bd78 5c13 5010 269c 8ac7 0000 -# 21,32819 ACK "150 Opening ASCII mode data connection for /bin/ls.\r\n" +# 21,32818 ACK "150 Opening ASCII mode data connection for /bin/ls.\r\n" [in,ppp0] 4500 005d ffe9 4000 ef06 12e1 96cb e002 0101 0101 0015 8032 3786 78d5 bd6b ca16 diff --git a/contrib/ipfilter/test/input/ni7 b/contrib/ipfilter/test/input/ni7 index 30f247d39ca..8d07937c126 100644 --- a/contrib/ipfilter/test/input/ni7 +++ b/contrib/ipfilter/test/input/ni7 @@ -1,13 +1,13 @@ #v tos len id off ttl p sum src dst # ICMP timeout exceeded in reply to a ICMP packet coming in. [in,df0] -4500 0028 4706 4000 0111 26b4 0404 0404 -0202 0202 afc9 829e 0014 6b10 0402 0000 +4500 0028 4706 4000 0111 26b4 0404 0404 0202 0202 +afc9 829e 0014 6b10 0402 0000 3be5 468d 000a cfc3 [out,df0] -4500 0038 809a 0000 ff01 2d1d 0303 0303 -0404 0404 0b00 0125 0000 0000 4500 0028 -4706 4000 0111 1eac 0404 0404 0606 0606 +4500 0038 809a 0000 ff01 2d1d 0303 0303 0404 0404 +0b00 0125 0000 0000 +4500 0028 4706 4000 0111 1eac 0404 0404 0606 0606 afc9 829e 0014 c15e diff --git a/contrib/ipfilter/test/input/ni8 b/contrib/ipfilter/test/input/ni8 index 788e6036c40..72205ee9a79 100644 --- a/contrib/ipfilter/test/input/ni8 +++ b/contrib/ipfilter/test/input/ni8 @@ -1,7 +1,7 @@ #v tos len id off ttl p sum src dst -# ICMP dest unreachable with 64 bits in payload (in reply to a TCP packet -# going out) -[in,df0] 45 00 00 3c 47 06 40 00 ff 06 20 aa 04 04 04 04 0a 02 02 02 50 00 05 00 00 00 00 01 00 00 00 00 a0 02 16 d0 cc 32 00 00 02 04 05 b4 04 02 08 0a 00 47 fb b0 00 00 00 00 01 03 03 00 +[in,df0] +4500 003c 4706 4000 ff06 20aa 0404 0404 0a02 0202 +5000 0500 0000 0001 0000 0000 a002 16d0 cc32 0000 0204 05b4 0402 080a 0047 fbb0 0000 0000 0103 0300 [out,df0] 4500 0038 809a 0000 ff01 2d1d 0303 0303 0404 0404 @@ -17,8 +17,11 @@ 5000 9d58 0000 0001 0000 0000 a002 16d0 3ddc 0000 0204 05b4 0402 080a 0047 fbb0 0000 0000 0103 0300 +# ICMP dest unreachable with 64 bits in payload (in reply to a TCP packet +# going in) [out,df0] 4500 0038 809a 0000 ff01 2b1b 0303 0303 0505 0505 0303 0fa3 0000 0000 -4500 003c 4706 4000 ff06 2aab 0404 0404 0101 0102 5000 9d58 0000 0001 +4500 003c 4706 4000 ff06 2aab 0404 0404 0101 0102 +5000 9d58 0000 0001 diff --git a/contrib/ipfilter/test/input/ni9 b/contrib/ipfilter/test/input/ni9 index 788e6036c40..b8f45991f08 100644 --- a/contrib/ipfilter/test/input/ni9 +++ b/contrib/ipfilter/test/input/ni9 @@ -1,7 +1,9 @@ #v tos len id off ttl p sum src dst # ICMP dest unreachable with 64 bits in payload (in reply to a TCP packet # going out) -[in,df0] 45 00 00 3c 47 06 40 00 ff 06 20 aa 04 04 04 04 0a 02 02 02 50 00 05 00 00 00 00 01 00 00 00 00 a0 02 16 d0 cc 32 00 00 02 04 05 b4 04 02 08 0a 00 47 fb b0 00 00 00 00 01 03 03 00 +[in,df0] +4500 003c 4706 4000 ff06 20aa 0404 0404 0a02 0202 +5000 0500 0000 0001 0000 0000 a002 16d0 cc32 0000 0204 05b4 0402 080a 0047 fbb0 0000 0000 0103 0300 [out,df0] 4500 0038 809a 0000 ff01 2d1d 0303 0303 0404 0404 @@ -20,5 +22,6 @@ [out,df0] 4500 0038 809a 0000 ff01 2b1b 0303 0303 0505 0505 0303 0fa3 0000 0000 -4500 003c 4706 4000 ff06 2aab 0404 0404 0101 0102 5000 9d58 0000 0001 +4500 003c 4706 4000 ff06 2aab 0404 0404 0101 0102 +5000 9d58 0000 0001 diff --git a/contrib/ipfilter/test/input/p10 b/contrib/ipfilter/test/input/p10 new file mode 100644 index 00000000000..f8162e807b0 --- /dev/null +++ b/contrib/ipfilter/test/input/p10 @@ -0,0 +1,10 @@ +in on bge0 tcp 5.5.5.5,10000 9.9.9.9,80 +in on bge0 tcp 5.5.5.6,10000 9.9.9.9,80 +in on bge0 tcp 5.5.5.7,10000 9.9.9.9,80 +in on bge0 tcp 5.5.5.8,10000 9.9.9.9,80 +in on bge0 tcp 5.5.5.9,10000 9.9.9.9,80 +in on bge0 tcp 5.5.6.5,10000 9.9.9.9,80 +in on bge0 tcp 5.5.6.6,10000 9.9.9.9,80 +in on bge0 tcp 5.5.6.7,10000 9.9.9.9,80 +in on bge0 tcp 5.5.6.8,10000 9.9.9.9,80 +in on bge0 tcp 5.5.6.9,10000 9.9.9.9,80 diff --git a/contrib/ipfilter/test/input/p11 b/contrib/ipfilter/test/input/p11 new file mode 100644 index 00000000000..f8162e807b0 --- /dev/null +++ b/contrib/ipfilter/test/input/p11 @@ -0,0 +1,10 @@ +in on bge0 tcp 5.5.5.5,10000 9.9.9.9,80 +in on bge0 tcp 5.5.5.6,10000 9.9.9.9,80 +in on bge0 tcp 5.5.5.7,10000 9.9.9.9,80 +in on bge0 tcp 5.5.5.8,10000 9.9.9.9,80 +in on bge0 tcp 5.5.5.9,10000 9.9.9.9,80 +in on bge0 tcp 5.5.6.5,10000 9.9.9.9,80 +in on bge0 tcp 5.5.6.6,10000 9.9.9.9,80 +in on bge0 tcp 5.5.6.7,10000 9.9.9.9,80 +in on bge0 tcp 5.5.6.8,10000 9.9.9.9,80 +in on bge0 tcp 5.5.6.9,10000 9.9.9.9,80 diff --git a/contrib/ipfilter/test/input/p12 b/contrib/ipfilter/test/input/p12 new file mode 100644 index 00000000000..f8162e807b0 --- /dev/null +++ b/contrib/ipfilter/test/input/p12 @@ -0,0 +1,10 @@ +in on bge0 tcp 5.5.5.5,10000 9.9.9.9,80 +in on bge0 tcp 5.5.5.6,10000 9.9.9.9,80 +in on bge0 tcp 5.5.5.7,10000 9.9.9.9,80 +in on bge0 tcp 5.5.5.8,10000 9.9.9.9,80 +in on bge0 tcp 5.5.5.9,10000 9.9.9.9,80 +in on bge0 tcp 5.5.6.5,10000 9.9.9.9,80 +in on bge0 tcp 5.5.6.6,10000 9.9.9.9,80 +in on bge0 tcp 5.5.6.7,10000 9.9.9.9,80 +in on bge0 tcp 5.5.6.8,10000 9.9.9.9,80 +in on bge0 tcp 5.5.6.9,10000 9.9.9.9,80 diff --git a/contrib/ipfilter/test/input/p13 b/contrib/ipfilter/test/input/p13 new file mode 100644 index 00000000000..f6753fac426 --- /dev/null +++ b/contrib/ipfilter/test/input/p13 @@ -0,0 +1,8 @@ +in 127.0.0.1 127.0.0.1 +in 1.1.1.1 1.2.1.1 +out 127.0.0.1 127.0.0.1 +out 1.1.1.1 1.2.1.1 +in 2.3.0.1 1.2.1.1 +in 2.2.2.1 1.2.1.1 +in 2.2.0.1 1.2.1.1 +out 4.4.1.1 1.2.1.1 diff --git a/contrib/ipfilter/test/input/p4 b/contrib/ipfilter/test/input/p4 new file mode 100644 index 00000000000..46c0998cb4e --- /dev/null +++ b/contrib/ipfilter/test/input/p4 @@ -0,0 +1,12 @@ +in 127.0.0.1 127.0.0.1 +in 1.1.1.1 1.2.1.1 +out 127.0.0.1 127.0.0.1 +out 1.1.1.1 1.2.1.1 +in 2.3.0.1 1.2.1.1 +in 2.2.2.1 1.2.1.1 +in 2.2.0.1 1.2.1.1 +out 2.2.2.1 1.2.1.1 +out 2.2.2.1 1.2.1.2 +out 2.2.0.1 1.2.1.1 +out 2.2.0.1 1.2.1.3 +out 4.4.1.1 1.2.1.1 diff --git a/contrib/ipfilter/test/input/p6 b/contrib/ipfilter/test/input/p6 new file mode 100644 index 00000000000..37c26ce3442 --- /dev/null +++ b/contrib/ipfilter/test/input/p6 @@ -0,0 +1,2 @@ +in 131.107.1.1 10.1.1.1 +out 10.1.1.1 131.107.1.1 diff --git a/contrib/ipfilter/test/input/p7 b/contrib/ipfilter/test/input/p7 new file mode 100644 index 00000000000..f8162e807b0 --- /dev/null +++ b/contrib/ipfilter/test/input/p7 @@ -0,0 +1,10 @@ +in on bge0 tcp 5.5.5.5,10000 9.9.9.9,80 +in on bge0 tcp 5.5.5.6,10000 9.9.9.9,80 +in on bge0 tcp 5.5.5.7,10000 9.9.9.9,80 +in on bge0 tcp 5.5.5.8,10000 9.9.9.9,80 +in on bge0 tcp 5.5.5.9,10000 9.9.9.9,80 +in on bge0 tcp 5.5.6.5,10000 9.9.9.9,80 +in on bge0 tcp 5.5.6.6,10000 9.9.9.9,80 +in on bge0 tcp 5.5.6.7,10000 9.9.9.9,80 +in on bge0 tcp 5.5.6.8,10000 9.9.9.9,80 +in on bge0 tcp 5.5.6.9,10000 9.9.9.9,80 diff --git a/contrib/ipfilter/test/input/p9 b/contrib/ipfilter/test/input/p9 new file mode 100644 index 00000000000..f8162e807b0 --- /dev/null +++ b/contrib/ipfilter/test/input/p9 @@ -0,0 +1,10 @@ +in on bge0 tcp 5.5.5.5,10000 9.9.9.9,80 +in on bge0 tcp 5.5.5.6,10000 9.9.9.9,80 +in on bge0 tcp 5.5.5.7,10000 9.9.9.9,80 +in on bge0 tcp 5.5.5.8,10000 9.9.9.9,80 +in on bge0 tcp 5.5.5.9,10000 9.9.9.9,80 +in on bge0 tcp 5.5.6.5,10000 9.9.9.9,80 +in on bge0 tcp 5.5.6.6,10000 9.9.9.9,80 +in on bge0 tcp 5.5.6.7,10000 9.9.9.9,80 +in on bge0 tcp 5.5.6.8,10000 9.9.9.9,80 +in on bge0 tcp 5.5.6.9,10000 9.9.9.9,80 diff --git a/contrib/ipfilter/test/intest b/contrib/ipfilter/test/intest index e94ca0894e5..bcafe76d0dc 100755 --- a/contrib/ipfilter/test/intest +++ b/contrib/ipfilter/test/intest @@ -1,22 +1,12 @@ #!/bin/sh -mkdir -p results -if [ -f /usr/ucb/touch ] ; then - TOUCH=/usr/ucb/touch -else - if [ -f /usr/bin/touch ] ; then - TOUCH=/usr/bin/touch - else - if [ -f /bin/touch ] ; then - TOUCH=/bin/touch - fi - fi -fi -echo "$1..."; -/bin/cp /dev/null results/$1 -../ipnat -Rnvf regress/$1 2>/dev/null > results/$1 -cmp expected/$1 results/$1 -status=$? -if [ $status = 0 ] ; then - $TOUCH $1 -fi +name=$1 + +. ./ipflib.sh + +test_init + +echo "$name..."; +/bin/cp /dev/null results/$name +../ipnat -Rnvf regress/$name 2>/dev/null > results/$name +check_results $name exit $status diff --git a/contrib/ipfilter/test/ipflib.sh b/contrib/ipfilter/test/ipflib.sh new file mode 100644 index 00000000000..82d473d0d36 --- /dev/null +++ b/contrib/ipfilter/test/ipflib.sh @@ -0,0 +1,59 @@ +#!/bin/sh +# +# (C)opyright 2012 by Darren Reed. +# +# See the IPFILTER.LICENCE file for details on licencing. +# +test_init() { + mkdir -p results + find_touch + set_core $name 1 +} + +set_core() { + if [ -n "${FINDLEAKS}" -a -x /bin/mdb ] ; then + _findleaks=1 + else + _findleaks=0 + fi + if [ -x /bin/coreadm ] ; then + _cn="$1.$2.core" + coreadm -p "${PWD}/$_cn" + else + _cn= + fi +} + +test_end_leak() { + if [ $1 -ne 0 ] ; then + if [ ${_findleaks} = 1 -a -f $_cn ] ; then + echo "==== ${name}:${n} ====" >> leaktest + echo '::findleaks' | mdb ../i86/ipftest $_cn >> leaktest + rm $_cn + else + exit 2; + fi + fi +} + +check_results() { + cmp expected/$1 results/$1 + status=$? + if [ $status = 0 ] ; then + $TOUCH $1 + fi +} + +find_touch() { + if [ -f /bin/touch ] ; then + TOUCH=/bin/touch + else + if [ -f /usr/bin/touch ] ; then + TOUCH=/usr/bin/touch + else + if [ -f /usr/ucb/touch ] ; then + TOUCH=/usr/ucb/touch + fi + fi + fi +} diff --git a/contrib/ipfilter/test/iptest b/contrib/ipfilter/test/iptest index bb3ab5ea86f..70fd9d89c72 100644 --- a/contrib/ipfilter/test/iptest +++ b/contrib/ipfilter/test/iptest @@ -1,22 +1,12 @@ #!/bin/sh -mkdir -p results -if [ -f /usr/ucb/touch ] ; then - TOUCH=/usr/ucb/touch -else - if [ -f /usr/bin/touch ] ; then - TOUCH=/usr/bin/touch - else - if [ -f /bin/touch ] ; then - TOUCH=/bin/touch - fi - fi -fi -echo "$1..."; -/bin/cp /dev/null results/$1 -../ippool -f regress/$1 -nRv 2>/dev/null > results/$1 -cmp expected/$1 results/$1 -status=$? -if [ $status = 0 ] ; then - $TOUCH $1 -fi +name=$1 + +. ./ipflib.sh + +test_init + +echo "$name..."; +/bin/cp /dev/null results/$name +../ippool -f regress/$name -nRv 2>/dev/null > results/$name +check_results $name exit $status diff --git a/contrib/ipfilter/test/itest b/contrib/ipfilter/test/itest index 8fefc634bfb..84b045454ff 100644 --- a/contrib/ipfilter/test/itest +++ b/contrib/ipfilter/test/itest @@ -1,29 +1,30 @@ #!/bin/sh -mkdir -p results -if [ -f /usr/ucb/touch ] ; then - TOUCH=/usr/ucb/touch -else - if [ -f /usr/bin/touch ] ; then - TOUCH=/usr/bin/touch - else - if [ -f /bin/touch ] ; then - TOUCH=/bin/touch - fi - fi -fi -echo "$1..."; -/bin/cp /dev/null results/$1 +name=$1 + +. ./ipflib.sh + +test_init + +echo "$name..."; +/bin/cp /dev/null results/$name case $3 in ipf) - ../ipf -Rnvf regress/$1 2>/dev/null > results/$1 + ../ipf -Rnvf regress/$name 2>/dev/null > results/$name + status=$? + if [ $status -ne 0 ] ; then + echo "ERROR: ../ipf -Rnvf regress/$name" + fi ;; ipftest) - ../ipftest -D -r regress/$1 -i /dev/null > results/$1 + unset FINDLEAKS + ../ipftest -D -r regress/$name -i /dev/null > results/$name + status=$? + if [ $status -ne 0 ] ; then + echo "ERROR: ../ipftest -D -r regress/$name" + fi ;; esac -cmp expected/$1 results/$1 -status=$? -if [ $status = 0 ] ; then - $TOUCH $1 +if [ $status -eq 0 ] ; then + check_results $name fi exit $status diff --git a/contrib/ipfilter/test/logtest b/contrib/ipfilter/test/logtest index 1c8ac5bca2c..a3a96716079 100755 --- a/contrib/ipfilter/test/logtest +++ b/contrib/ipfilter/test/logtest @@ -1,19 +1,13 @@ #!/bin/sh # $FreeBSD$ +name=$1 format=$2 -mkdir -p results -if [ -f /usr/ucb/touch ] ; then - TOUCH=/usr/ucb/touch -else - if [ -f /usr/bin/touch ] ; then - TOUCH=/usr/bin/touch - else - if [ -f /bin/touch ] ; then - TOUCH=/bin/touch - fi - fi -fi -echo "$1..."; + +. ./ipflib.sh + +test_init + +echo "$name..."; case `uname -s` in OSF1) @@ -24,16 +18,20 @@ OSF1) ;; esac -/bin/cp /dev/null results/$1 -/bin/cp /dev/null results/$1.b +n=1 +/bin/cp /dev/null results/$name +/bin/cp /dev/null results/$name.b ( while read rule; do - echo $rule >> results/$1 - echo $rule | ../ipftest -br - -F $format -i input/$1 -l logout > /dev/null - if [ $? -ne 0 ] ; then - /bin/rm -f logout - exit 1 - fi + /bin/rm -f logout + set_core $name $n + echo $rule >> results/$name + echo $rule | ../ipftest -br - -F $format -i input/$name -l logout > /dev/null & + back=$! + wait $back + test_end_leak $? + n=`expr $n + 1` + TZ=$GMT ../ipmon -P /dev/null -f logout >> results/$1 echo "--------" >> results/$1 TZ=$GMT ../ipmon -P /dev/null -bf logout >> results/$1.b diff --git a/contrib/ipfilter/test/mhtest b/contrib/ipfilter/test/mhtest deleted file mode 100755 index a4d48d69347..00000000000 --- a/contrib/ipfilter/test/mhtest +++ /dev/null @@ -1,36 +0,0 @@ -#!/bin/sh -# multiple rules at the same time - -if [ -f /usr/ucb/touch ] ; then - TOUCH=/usr/ucb/touch -else - if [ -f /usr/bin/touch ] ; then - TOUCH=/usr/bin/touch - else - if [ -f /bin/touch ] ; then - TOUCH=/bin/touch - fi - fi -fi -echo "$1..."; - -/bin/cp /dev/null results/$1 - -../ipftest -br regress/$1 -F hex -i input/$1 > results/$1 -if [ $? -ne 0 ] ; then - exit 1 -fi -echo "--------" >> results/$1 - -cmp expected/$1 results/$1 -status=$? -if [ $status -ne 0 ] ; then - exit $status -fi -cmp expected/$1 results/$1 -status=$? -if [ $status -ne 0 ] ; then - exit $status -fi -$TOUCH $1 -exit 0 diff --git a/contrib/ipfilter/test/mtest b/contrib/ipfilter/test/mtest index 2a3ed38b34f..aed9fb98424 100755 --- a/contrib/ipfilter/test/mtest +++ b/contrib/ipfilter/test/mtest @@ -1,38 +1,20 @@ #!/bin/sh +name=$1 format=$2 -mkdir -p results -# multiple rules at the same time -if [ -f /usr/ucb/touch ] ; then - TOUCH=/usr/ucb/touch -else - if [ -f /usr/bin/touch ] ; then - TOUCH=/usr/bin/touch - else - if [ -f /bin/touch ] ; then - TOUCH=/bin/touch - fi - fi -fi -echo "$1..."; +. ./ipflib.sh + +test_init + +echo "$name..."; /bin/cp /dev/null results/$1 -../ipftest -F $format -Rbr regress/$1 -i input/$1 > results/$1 -if [ $? -ne 0 ] ; then - exit 1 -fi -echo "--------" >> results/$1 +../ipftest -F $format $4 -Rbr regress/$name -i input/$name > results/$name & +back=$! +wait $back +test_end_leak $? +echo "--------" >> results/$name -cmp expected/$1 results/$1 -status=$? -if [ $status -ne 0 ] ; then - exit $status -fi -cmp expected/$1 results/$1 -status=$? -if [ $status -ne 0 ] ; then - exit $status -fi -$TOUCH $1 -exit 0 +check_results $name +exit $status diff --git a/contrib/ipfilter/test/natipftest b/contrib/ipfilter/test/natipftest index 5776b4202c9..493f18b43a8 100755 --- a/contrib/ipfilter/test/natipftest +++ b/contrib/ipfilter/test/natipftest @@ -3,6 +3,12 @@ mode=$1 name=$2 input=$3 output=$4 +n=1 + +. ./ipflib.sh + +test_init + shift if [ $output = hex ] ; then format="-xF $input" @@ -21,51 +27,33 @@ while [ $# -ge 1 ] ; do fi shift done -mkdir -p results -if [ -f /usr/ucb/touch ] ; then - TOUCH=/usr/ucb/touch -else - if [ -f /usr/bin/touch ] ; then - TOUCH=/usr/bin/touch - else - if [ -f /bin/touch ] ; then - TOUCH=/bin/touch - fi - fi -fi case $mode in single) echo "$name..."; /bin/cp /dev/null results/$name ( while read rule; do + set_core $name $n echo "$rule" | ../ipftest -R $format -b -r regress/$name.ipf -N - -i input/$name >> \ - results/$name; - if [ $? -ne 0 ] ; then - exit 1; - fi + results/$name & + back=$! + wait $back + test_end_leak $? + n=`expr $n + 1` echo "-------------------------------" >> results/$name done ) < regress/$name.nat - cmp expected/$name results/$name - status=$? - if [ $status = 0 ] ; then - $TOUCH $name - fi + check_results $name ;; multi) echo "$name..."; /bin/cp /dev/null results/$name ../ipftest -R $format -b -r regress/$name.ipf -N regress/$name.nat \ - -i input/$name >> results/$name; - if [ $? -ne 0 ] ; then - exit 2; - fi + -i input/$name >> results/$name & + back=$! + wait $back + test_end_leak $? echo "-------------------------------" >> results/$name - cmp expected/$name results/$name - status=$? - if [ $status = 0 ] ; then - $TOUCH $name - fi + check_results $name ;; esac exit $status diff --git a/contrib/ipfilter/test/nattest b/contrib/ipfilter/test/nattest index fece276a6a7..c9708771719 100755 --- a/contrib/ipfilter/test/nattest +++ b/contrib/ipfilter/test/nattest @@ -1,4 +1,10 @@ #!/bin/sh +name=$1 + +. ./ipflib.sh + +test_init + if [ $3 = hex ] ; then format="-xF $2" else @@ -14,29 +20,18 @@ if [ "$4" != "" ] ; then ;; esac fi -if [ -f /usr/ucb/touch ] ; then - TOUCH=/usr/ucb/touch -else - if [ -f /usr/bin/touch ] ; then - TOUCH=/usr/bin/touch - else - if [ -f /bin/touch ] ; then - TOUCH=/bin/touch - fi - fi -fi -echo "$1..."; -/bin/cp /dev/null results/$1 + +echo "$name..."; +n=1 +/bin/cp /dev/null results/$name ( while read rule; do - echo "$rule" | ../ipftest $format -RbN - -i input/$1 >> results/$1; - if [ $? -ne 0 ] ; then - exit 1; - fi - echo "-------------------------------" >> results/$1 -done ) < regress/$1 -cmp expected/$1 results/$1 -status=$? -if [ $status = 0 ] ; then - $TOUCH $1 -fi + set_core $name $n + echo "$rule" | ../ipftest $format -DRbN - -i input/$name >>results/$name & + back=$! + wait $back + test_end_leak $? + n=`expr $n + 1` + echo "-------------------------------" >> results/$name +done ) < regress/$name +check_results $name exit $status diff --git a/contrib/ipfilter/test/ptest b/contrib/ipfilter/test/ptest index 7deccd3354f..87daacc8072 100644 --- a/contrib/ipfilter/test/ptest +++ b/contrib/ipfilter/test/ptest @@ -1,31 +1,24 @@ #!/bin/sh -mkdir -p results -if [ -f /usr/ucb/touch ] ; then - TOUCH=/usr/ucb/touch +name=$1 + +. ./ipflib.sh + +test_init + +echo "$name..."; +/bin/cp /dev/null results/$name +if [ -f regress/$name.pool -a -f regress/$name.ipf ] ; then + ../ipftest -RD -b -P regress/$name.pool -r regress/$name.ipf -i input/$name >> \ + results/$name & +elif [ -f regress/$name.pool -a -f regress/$name.nat ] ; then + ../ipftest -RD -b -P regress/$name.pool -N regress/$name.nat -i input/$name >> \ + results/$name & else - if [ -f /usr/bin/touch ] ; then - TOUCH=/usr/bin/touch - else - if [ -f /bin/touch ] ; then - TOUCH=/bin/touch - fi - fi -fi -echo "$1..."; -/bin/cp /dev/null results/$1 -if [ -f regress/$1.pool ] ; then - ../ipftest -RD -b -P regress/$1.pool -r regress/$1.ipf -i input/$1 >> \ - results/$1 -else - ../ipftest -RD -b -r regress/$1.ipf -i input/$1 >> results/$1 -fi -if [ $? -ne 0 ] ; then - exit 1; -fi -echo "-------------------------------" >> results/$1 -cmp expected/$1 results/$1 -status=$? -if [ $status = 0 ] ; then - $TOUCH $1 + ../ipftest -RD -b -r regress/$name.ipf -i input/$name >> results/$name & fi +back=$! +wait $back +test_end_leak $? +echo "-------------------------------" >> results/$name +check_results $name exit $status diff --git a/contrib/ipfilter/test/regress/f13 b/contrib/ipfilter/test/regress/f13 index 8106419f3e0..393a65e9eff 100644 --- a/contrib/ipfilter/test/regress/f13 +++ b/contrib/ipfilter/test/regress/f13 @@ -6,3 +6,4 @@ pass in proto tcp from any to any port = 25 flags S/SA keep state keep frags block in proto tcp from any to any port = 25 flags S/SA keep state keep frags pass in proto udp from any to any port = 53 keep frags(strict) pass in proto tcp from any to any port = 25 keep state(strict) +pass in proto tcp from any to any port = 25 keep state(loose) diff --git a/contrib/ipfilter/test/regress/f21 b/contrib/ipfilter/test/regress/f21 new file mode 100644 index 00000000000..26ffa87f238 --- /dev/null +++ b/contrib/ipfilter/test/regress/f21 @@ -0,0 +1,2 @@ +pass out proto tcp all flags S keep state(icmp-head icmpredir) +block in proto icmp all icmp-type redir group icmpredir diff --git a/contrib/ipfilter/test/regress/f22 b/contrib/ipfilter/test/regress/f22 new file mode 100644 index 00000000000..10765db18f0 --- /dev/null +++ b/contrib/ipfilter/test/regress/f22 @@ -0,0 +1,2 @@ +pass in proto tcp all flags S keep state(icmp-head icmpredir) +block out proto icmp all icmp-type redir group icmpredir diff --git a/contrib/ipfilter/test/regress/f25 b/contrib/ipfilter/test/regress/f25 new file mode 100644 index 00000000000..c018b498dd3 --- /dev/null +++ b/contrib/ipfilter/test/regress/f25 @@ -0,0 +1 @@ +pass in on hme0 proto udp all with mcast keep state diff --git a/contrib/ipfilter/test/regress/f26 b/contrib/ipfilter/test/regress/f26 new file mode 100644 index 00000000000..22357a475ca --- /dev/null +++ b/contrib/ipfilter/test/regress/f26 @@ -0,0 +1,6 @@ +pass in quick proto tcp from 1.1.1.0/24 to any port = 22 flags S keep state(max-srcs 3) +pass in quick proto tcp from 1.1.1.0/24 to any port = 22 flags S keep state(max-srcs 3, max-per-src 1/32) +pass in quick proto tcp from 1.1.1.0/24 to any port = 22 flags S keep state(max-srcs 3, max-per-src 1/16) +pass in quick proto tcp all flags S keep state(max-srcs 3) +pass in quick proto tcp all flags S keep state(max-srcs 3, max-per-src 1/32) +pass in quick proto tcp all flags S keep state(max-srcs 3, max-per-src 1/16) diff --git a/contrib/ipfilter/test/regress/f27 b/contrib/ipfilter/test/regress/f27 new file mode 100644 index 00000000000..22357a475ca --- /dev/null +++ b/contrib/ipfilter/test/regress/f27 @@ -0,0 +1,6 @@ +pass in quick proto tcp from 1.1.1.0/24 to any port = 22 flags S keep state(max-srcs 3) +pass in quick proto tcp from 1.1.1.0/24 to any port = 22 flags S keep state(max-srcs 3, max-per-src 1/32) +pass in quick proto tcp from 1.1.1.0/24 to any port = 22 flags S keep state(max-srcs 3, max-per-src 1/16) +pass in quick proto tcp all flags S keep state(max-srcs 3) +pass in quick proto tcp all flags S keep state(max-srcs 3, max-per-src 1/32) +pass in quick proto tcp all flags S keep state(max-srcs 3, max-per-src 1/16) diff --git a/contrib/ipfilter/test/regress/f28.ipf b/contrib/ipfilter/test/regress/f28.ipf new file mode 100644 index 00000000000..ca427715ac1 --- /dev/null +++ b/contrib/ipfilter/test/regress/f28.ipf @@ -0,0 +1,2 @@ +block in all +pass in on nic0 to dstlist/spread from 4.4.0.0/16 to any diff --git a/contrib/ipfilter/test/regress/f28.pool b/contrib/ipfilter/test/regress/f28.pool new file mode 100644 index 00000000000..499b6034466 --- /dev/null +++ b/contrib/ipfilter/test/regress/f28.pool @@ -0,0 +1,2 @@ +pool ipf/dstlist (name spread; policy round-robin;) + { nic0:1.1.0.2; nic1:1.1.1.2; nic2:1.1.2.2; nic3:1.1.3.2; }; diff --git a/contrib/ipfilter/test/regress/f29.ipf b/contrib/ipfilter/test/regress/f29.ipf new file mode 100644 index 00000000000..e4634cc1fa4 --- /dev/null +++ b/contrib/ipfilter/test/regress/f29.ipf @@ -0,0 +1,2 @@ +block in all +pass in on nic0 to dstlist/spread from 4.4.0.0/16 to any keep state diff --git a/contrib/ipfilter/test/regress/f29.pool b/contrib/ipfilter/test/regress/f29.pool new file mode 100644 index 00000000000..499b6034466 --- /dev/null +++ b/contrib/ipfilter/test/regress/f29.pool @@ -0,0 +1,2 @@ +pool ipf/dstlist (name spread; policy round-robin;) + { nic0:1.1.0.2; nic1:1.1.1.2; nic2:1.1.2.2; nic3:1.1.3.2; }; diff --git a/contrib/ipfilter/test/regress/f30 b/contrib/ipfilter/test/regress/f30 new file mode 100644 index 00000000000..84a8970eb86 --- /dev/null +++ b/contrib/ipfilter/test/regress/f30 @@ -0,0 +1,4 @@ +pass in on hme0 proto udp all with not ipopts keep state +pass in on hme0 proto udp all with ipopts keep state +pass in on hme0 proto tcp all flags S with opt rr keep state +pass in on hme0 proto tcp all flags S with opt sec-class secret keep state diff --git a/contrib/ipfilter/test/regress/i11 b/contrib/ipfilter/test/regress/i11 index cb7d6838999..ca65da313b0 100644 --- a/contrib/ipfilter/test/regress/i11 +++ b/contrib/ipfilter/test/regress/i11 @@ -8,4 +8,5 @@ pass out on ppp0 in-via le0 proto tcp from any to any keep state pass in on ed0,vx0 out-via vx0,ed0 proto udp from any to any keep state pass in proto tcp from any port gt 1024 to localhost port eq 1024 keep state pass in proto tcp all flags S keep state(strict,newisn,no-icmp-err,limit 101,age 600) +pass in proto tcp all flags S keep state(loose,newisn,no-icmp-err,limit 101,age 600) pass in proto udp all keep state(age 10/20,sync) diff --git a/contrib/ipfilter/test/regress/i12 b/contrib/ipfilter/test/regress/i12 index 5342702353e..f42c2d522b5 100644 --- a/contrib/ipfilter/test/regress/i12 +++ b/contrib/ipfilter/test/regress/i12 @@ -1,9 +1,9 @@ pass in from 1.1.1.1/32 to 2.2.2.2/32 -pass in from (2.2.2.2/24,3.3.3.3/32) to 4.4.4.4/32 -pass in from (2.2.2.2/24,3.3.3.3/32) to (5.5.5.5/32,6.6.6.6/32) -pass in from (2.2.2.2/24,3.3.3.3/32) to (5.5.5.5/32,6.6.6.6/32) port = (22,25) -pass in proto tcp from (2.2.2.2/24,3.3.3.3/32) port = (53,9) to (5.5.5.5/32,6.6.6.6/32) -pass in proto udp from (2.2.2.2/24,3.3.3.3/32) to (5.5.5.5/32,6.6.6.6/32) port = (53,9) +pass in from {2.2.2.2/24,3.3.3.3/32} to 4.4.4.4/32 +pass in from {2.2.2.2/24,3.3.3.3/32} to {5.5.5.5/32,6.6.6.6/32} +pass in from {2.2.2.2/24,3.3.3.3/32} to {5.5.5.5/32,6.6.6.6/32} port = {22,25} +pass in proto tcp from {2.2.2.2/24,3.3.3.3/32} port = {53,9} to {5.5.5.5/32,6.6.6.6/32} +pass in proto udp from {2.2.2.2/24,3.3.3.3/32} to {5.5.5.5/32,6.6.6.6/32} port = {53,9} pass in from 10.10.10.10 to 11.11.11.11 pass in from pool/101 to hash/202 pass in from hash/303 to pool/404 diff --git a/contrib/ipfilter/test/regress/i14 b/contrib/ipfilter/test/regress/i14 index 2cd26130640..54613a51670 100644 --- a/contrib/ipfilter/test/regress/i14 +++ b/contrib/ipfilter/test/regress/i14 @@ -8,3 +8,5 @@ pass in proto tcp from 1.0.0.1 to 2.0.0.2 group 101 pass in proto udp from 2.0.0.2 to 3.0.0.3 group 101 block in on vm0 proto tcp/udp all head vm0-group pass in from 1.1.1.1 to 2.2.2.2 group vm0-group +block in on vm0 proto tcp/udp all head vm0-group +pass in from 1.1.1.1 to 2.2.2.2 group vm0-group diff --git a/contrib/ipfilter/test/regress/i17 b/contrib/ipfilter/test/regress/i17 index e399248222a..139b86a8d84 100644 --- a/contrib/ipfilter/test/regress/i17 +++ b/contrib/ipfilter/test/regress/i17 @@ -9,5 +9,5 @@ pass in from localhost to any @0 pass in from 1.1.1.1 to any @1 110 pass in from 2.2.2.2 to any @2 pass in from 3.3.3.3 to any -call fr_srcgrpmap/100 out from 10.1.0.0/16 to any -call now fr_dstgrpmap/200 in from 10.2.0.0/16 to any +call srcgrpmap/100 out from 10.1.0.0/16 to any +call now dstgrpmap/200 in from 10.2.0.0/16 to any diff --git a/contrib/ipfilter/test/regress/i18 b/contrib/ipfilter/test/regress/i18 index 03ce713b4a5..b55b11a01fe 100644 --- a/contrib/ipfilter/test/regress/i18 +++ b/contrib/ipfilter/test/regress/i18 @@ -1,3 +1,3 @@ -pass in tos (80,0x80) all -pass in tos (0x80,80) all -block in ttl (0,1,2,3,4,5,6) all +pass in tos {80,0x80} all +pass out tos {0x80,80} all +block in ttl {0,1,2,3,4,5,6} all diff --git a/contrib/ipfilter/test/regress/i2 b/contrib/ipfilter/test/regress/i2 index 50f610750bc..f69e28edb13 100644 --- a/contrib/ipfilter/test/regress/i2 +++ b/contrib/ipfilter/test/regress/i2 @@ -6,3 +6,4 @@ block in proto 17 from any to any block in proto 250 from any to any pass in proto tcp/udp from any to any block in proto tcp-udp from any to any +block in proto tcp-udp from any to any diff --git a/contrib/ipfilter/test/regress/i21 b/contrib/ipfilter/test/regress/i21 index 9d583ab0894..237f8fadb64 100644 --- a/contrib/ipfilter/test/regress/i21 +++ b/contrib/ipfilter/test/regress/i21 @@ -2,6 +2,6 @@ pass in from port = 10101 pass out from any to port != 22 block in from port 20:21 block out from any to port 10 <> 100 -pass out from any to port = (3,5,7,9) -block in from port = (20,25) -pass in from any port = (11:12, 21:22) to any port = (1:2, 4:5, 8:9) +pass out from any to port = {3,5,7,9} +block in from port = {20,25} +pass in from any port = {11:12, 21:22} to any port = {1:2, 4:5, 8:9} diff --git a/contrib/ipfilter/test/regress/i22 b/contrib/ipfilter/test/regress/i22 new file mode 100644 index 00000000000..1ac8d12ba35 --- /dev/null +++ b/contrib/ipfilter/test/regress/i22 @@ -0,0 +1,5 @@ +pass in exp { "ip.src != 1.1.1.0/24; tcp.dport = 80;" } +pass in exp { "ip.addr = 1.2.3.4,5.6.7.8;" } +block out exp { "ip.dst= 127.0.0.0/8;" } +block in exp { "udp.sport=53;udp.dport=53;" } +pass out exp { "tcp.sport=22; tcp.port=25;" } diff --git a/contrib/ipfilter/test/regress/i23 b/contrib/ipfilter/test/regress/i23 new file mode 100644 index 00000000000..792d6005489 --- /dev/null +++ b/contrib/ipfilter/test/regress/i23 @@ -0,0 +1 @@ +# diff --git a/contrib/ipfilter/test/regress/i7 b/contrib/ipfilter/test/regress/i7 index 1a82940c6c8..15b88a56dc5 100644 --- a/contrib/ipfilter/test/regress/i7 +++ b/contrib/ipfilter/test/regress/i7 @@ -7,3 +7,8 @@ block in on lo0 proto tcp from any to any flags 2/18 pass in on lo0 proto tcp from any to any flags 2 block in on lo0 proto tcp from any to any flags /16 pass in on lo0 proto tcp from any to any flags 2/SA +pass in on lo0 proto tcp from any to any flags S/18 +block in on lo0 proto tcp from any to any flags 2/18 +pass in on lo0 proto tcp from any to any flags 2 +block in on lo0 proto tcp from any to any flags /16 +pass in on lo0 proto tcp from any to any flags 2/SA diff --git a/contrib/ipfilter/test/regress/i8 b/contrib/ipfilter/test/regress/i8 index c30f8bdbd90..abf69d9d30b 100644 --- a/contrib/ipfilter/test/regress/i8 +++ b/contrib/ipfilter/test/regress/i8 @@ -11,11 +11,11 @@ pass in proto icmp all icmp-type unreach code host-prohib pass in proto icmp all icmp-type unreach code host-tos pass in proto icmp all icmp-type unreach code host-unk pass in proto icmp all icmp-type unreach code host-unr -pass in proto icmp all icmp-type unreach code (net-unk,net-unr) +pass in proto icmp all icmp-type unreach code {net-unk,net-unr} pass in proto icmp all icmp-type unreach code port-unr pass in proto icmp all icmp-type unreach code proto-unr pass in proto icmp all icmp-type unreach code srcfail -pass in proto icmp all icmp-type (echo,echorep) +pass in proto icmp all icmp-type {echo,echorep} pass in proto icmp all icmp-type inforeq pass in proto icmp all icmp-type inforep pass in proto icmp all icmp-type maskrep @@ -31,3 +31,32 @@ pass in proto icmp all icmp-type timestrep pass in proto icmp all icmp-type timex pass in proto icmp all icmp-type 254 pass in proto icmp all icmp-type 253 code 254 +pass in proto icmp all icmp-type unreach code cutoff-preced +pass in proto icmp all icmp-type unreach code filter-prohib +pass in proto icmp all icmp-type unreach code isolate +pass in proto icmp all icmp-type unreach code needfrag +pass in proto icmp all icmp-type unreach code net-prohib +pass in proto icmp all icmp-type unreach code net-tos +pass in proto icmp all icmp-type unreach code host-preced +pass in proto icmp all icmp-type unreach code host-prohib +pass in proto icmp all icmp-type unreach code host-tos +pass in proto icmp all icmp-type unreach code host-unk +pass in proto icmp all icmp-type unreach code host-unr +pass in proto icmp all icmp-type unreach code {net-unk,net-unr} +pass in proto icmp all icmp-type unreach code port-unr +pass in proto icmp all icmp-type unreach code proto-unr +pass in proto icmp all icmp-type unreach code srcfail +pass in proto icmp all icmp-type {echo,echorep} +pass in proto icmp all icmp-type inforeq +pass in proto icmp all icmp-type inforep +pass in proto icmp all icmp-type maskrep +pass in proto icmp all icmp-type maskreq +pass in proto icmp all icmp-type paramprob +pass in proto icmp all icmp-type redir +pass in proto icmp all icmp-type unreach +pass in proto icmp all icmp-type routerad +pass in proto icmp all icmp-type routersol +pass in proto icmp all icmp-type squench +pass in proto icmp all icmp-type timest +pass in proto icmp all icmp-type timestrep +pass in proto icmp all icmp-type timex diff --git a/contrib/ipfilter/test/regress/in100 b/contrib/ipfilter/test/regress/in100 new file mode 100644 index 00000000000..5e2ab6c9173 --- /dev/null +++ b/contrib/ipfilter/test/regress/in100 @@ -0,0 +1,3 @@ +rewrite in on bge0 from 1.1.1.1 to 2.2.2.2 -> src 3.3.3.3 dst 4.4.4.4; +rewrite out on bge0 from 1.1.1.1/32 to 2.2.2.2 -> src 3.3.3.0/24 dst 4.4.4.4; +rewrite in on bge0 from 1.1.1.1/32 to 2.2.2.2/32 -> src 3.3.3.0/24 dst 4.4.4.0/24; diff --git a/contrib/ipfilter/test/regress/in101 b/contrib/ipfilter/test/regress/in101 new file mode 100644 index 00000000000..afef53bc9ab --- /dev/null +++ b/contrib/ipfilter/test/regress/in101 @@ -0,0 +1,4 @@ +rewrite in on bge0 proto icmp from 1.1.1.1 to 2.2.2.2 -> src 3.3.3.3 dst 4.4.4.4; +rewrite in on bge0 proto udp from 1.1.1.1 to 2.2.2.2 -> src 3.3.3.3 dst 4.4.4.4; +rewrite out on bge0 proto tcp from 1.1.1.1/32 to 2.2.2.2 -> src 3.3.3.0/24 dst 4.4.4.4; +rewrite in on bge0 proto tcp/udp from 1.1.1.1/32 to 2.2.2.2/32 -> src 3.3.3.0/24,20202 dst 4.4.4.0/24,10101; diff --git a/contrib/ipfilter/test/regress/in102 b/contrib/ipfilter/test/regress/in102 new file mode 100644 index 00000000000..57f364566f0 --- /dev/null +++ b/contrib/ipfilter/test/regress/in102 @@ -0,0 +1,5 @@ +rewrite in on bge0 proto tcp from any to any -> src 0/0 dst dstlist/a; +rewrite in on bge0 proto tcp from 1.1.1.1 to any -> src 0/0 dst dstlist/bee; +rewrite in on bge0 proto tcp from 1.1.1.1 to 2.2.2.2 -> src 0/0 dst dstlist/cat; +rewrite in on bge0 proto tcp from pool/a to 2.2.2.2 -> src 0/0 dst dstlist/bat; +rewrite in on bge0 proto tcp from pool/a to pool/1 -> src 0/0 dst dstlist/ant; diff --git a/contrib/ipfilter/test/regress/in2 b/contrib/ipfilter/test/regress/in2 index 83a2ca5acc3..58556c04854 100644 --- a/contrib/ipfilter/test/regress/in2 +++ b/contrib/ipfilter/test/regress/in2 @@ -67,5 +67,5 @@ rdr ge0 9.8.7.6/32 port 21 -> 1.1.1.1 port 21 tcp proxy ftp rdr le0 9.8.7.6/32 port 1000-2000 -> 1.1.1.1 port 5555 tcp rdr le0 9.8.7.6/32 port 1000-2000 -> 1.1.1.1 port = 5555 tcp rdr le0 0/0 -> test.host.dots -rdr le0 0/0 -> test.host.dots,test.host.dots -rdr adsl0,ppp0 0/0 port 25 -> 127.0.0.1 port 25 +rdr le0 any -> test.host.dots,test.host.dots +rdr adsl0,ppp0 9.8.7.6/32 port 1000-2000 -> 1.1.1.1 port 5555-7777 tcp diff --git a/contrib/ipfilter/test/regress/in7 b/contrib/ipfilter/test/regress/in7 new file mode 100644 index 00000000000..792d6005489 --- /dev/null +++ b/contrib/ipfilter/test/regress/in7 @@ -0,0 +1 @@ +# diff --git a/contrib/ipfilter/test/regress/ip3 b/contrib/ipfilter/test/regress/ip3 new file mode 100644 index 00000000000..98d2b0b9455 --- /dev/null +++ b/contrib/ipfilter/test/regress/ip3 @@ -0,0 +1,14 @@ +pool ipf/dstlist (name fred; policy round-robin;) + { 3.3.3.3; }; +pool ipf/dstlist (name jack; policy weighted connection;) + { 4.4.4.4; bge0:5.5.5.5;}; +pool ipf/dstlist (name jill; policy random;) + { 1.1.1.1; bge0:2.2.2.2;}; +pool nat/hash (name noproxy; size 17;) + { 1.1.1.1; 2.2.2.2;}; +pool nat/tree (name raw;) + { 1.1.1.1; 2.2.2.2;}; +pool all/dstlist (name jill; policy random;) + { 1.1.1.1; bge0:2.2.2.2;}; +pool all/hash (name noproxy; size 17;) + { 1.1.1.1; 2.2.2.2;}; diff --git a/contrib/ipfilter/test/regress/ipv6.4 b/contrib/ipfilter/test/regress/ipv6.4 new file mode 100644 index 00000000000..b2217446d39 --- /dev/null +++ b/contrib/ipfilter/test/regress/ipv6.4 @@ -0,0 +1,3 @@ +pass in proto ipv6-icmp all icmp-type echo keep frags +pass in proto ipv6-icmp all icmp-type echo keep frags keep state +pass in proto tcp all keep frags keep state diff --git a/contrib/ipfilter/test/regress/ipv6.5 b/contrib/ipfilter/test/regress/ipv6.5 index ba8cabb501e..d9ae23b2fbd 100644 --- a/contrib/ipfilter/test/regress/ipv6.5 +++ b/contrib/ipfilter/test/regress/ipv6.5 @@ -1,2 +1,2 @@ -pass out all with v6hdrs routing -block out proto tcp all with v6hdrs routing +pass out family inet6 all with v6hdr routing +block out family inet6 proto tcp all with v6hdr routing diff --git a/contrib/ipfilter/test/regress/ipv6.6 b/contrib/ipfilter/test/regress/ipv6.6 index f1f904b4eb6..19a4df97233 100644 --- a/contrib/ipfilter/test/regress/ipv6.6 +++ b/contrib/ipfilter/test/regress/ipv6.6 @@ -1 +1,2 @@ pass out on gif0 proto udp all keep frag +block out all with bad diff --git a/contrib/ipfilter/test/regress/n100 b/contrib/ipfilter/test/regress/n100 new file mode 100644 index 00000000000..a8b6deeb52e --- /dev/null +++ b/contrib/ipfilter/test/regress/n100 @@ -0,0 +1 @@ +rewrite out on zx0 from 0/0 to 2.2.0.0/16 -> src 4.4.4.4/32 dst 6.6.0.0/16; diff --git a/contrib/ipfilter/test/regress/n101 b/contrib/ipfilter/test/regress/n101 new file mode 100644 index 00000000000..2f5fcd98c9c --- /dev/null +++ b/contrib/ipfilter/test/regress/n101 @@ -0,0 +1 @@ +rewrite out on zx0 proto tcp from 0/0 to 2.2.0.0/16 -> src 4.4.4.4/32 dst 6.6.0.0/16; diff --git a/contrib/ipfilter/test/regress/n102 b/contrib/ipfilter/test/regress/n102 new file mode 100644 index 00000000000..f056633dd23 --- /dev/null +++ b/contrib/ipfilter/test/regress/n102 @@ -0,0 +1 @@ +rewrite out on zx0 proto tcp from 0/0 to 2.2.0.0/16 -> src 4.4.4.4/32,1000:2000 dst 6.6.0.0/16; diff --git a/contrib/ipfilter/test/regress/n103 b/contrib/ipfilter/test/regress/n103 new file mode 100644 index 00000000000..c3c27d60dd8 --- /dev/null +++ b/contrib/ipfilter/test/regress/n103 @@ -0,0 +1 @@ +rewrite out on zx0 proto tcp from 0/0 to 2.2.0.0/16 -> src 4.4.4.4/32,1000-1001 dst 6.6.0.0/16,4000:4001; diff --git a/contrib/ipfilter/test/regress/n104 b/contrib/ipfilter/test/regress/n104 new file mode 100644 index 00000000000..785f0ada244 --- /dev/null +++ b/contrib/ipfilter/test/regress/n104 @@ -0,0 +1 @@ +rewrite out on zx0 proto tcp from 0/0 to 2.2.0.0/16 -> src 4.4.0.0/24,1000-1001 dst 6.6.0.0/16,4000:4001; diff --git a/contrib/ipfilter/test/regress/n105 b/contrib/ipfilter/test/regress/n105 new file mode 100644 index 00000000000..afe89660a72 --- /dev/null +++ b/contrib/ipfilter/test/regress/n105 @@ -0,0 +1 @@ +rewrite in on zx0 proto tcp from 0/0 to 2.2.0.0/16 port = 80 -> src 4.4.4.4/32,1000-1001 dst 6.6.0.0/16 port = 3128; diff --git a/contrib/ipfilter/test/regress/n106 b/contrib/ipfilter/test/regress/n106 new file mode 100644 index 00000000000..6074ab0b4cc --- /dev/null +++ b/contrib/ipfilter/test/regress/n106 @@ -0,0 +1 @@ +rewrite out on zx0 proto tcp from 0/0 to 2.2.0.0/16 port = 80 -> src 4.4.4.4/32,1000-1001 dst 6.6.0.0/16 port = 3128; diff --git a/contrib/ipfilter/test/regress/n10_6 b/contrib/ipfilter/test/regress/n10_6 new file mode 100644 index 00000000000..738152df62a --- /dev/null +++ b/contrib/ipfilter/test/regress/n10_6 @@ -0,0 +1,3 @@ +map ppp0 any -> 203.203.203.203/128 mssclamp 100 +map ppp0 any -> 203.203.203.203/128 mssclamp 1000 +map ppp0 any -> 203.203.203.203/128 mssclamp 10000 diff --git a/contrib/ipfilter/test/regress/n11_6 b/contrib/ipfilter/test/regress/n11_6 new file mode 100644 index 00000000000..7b428cc2392 --- /dev/null +++ b/contrib/ipfilter/test/regress/n11_6 @@ -0,0 +1,3 @@ +bimap zx0 10:1:1::1/128 -> 1::6:7:8/128 +bimap zx0 10:1:1::/112 -> 10::2:2:2/128 +bimap zx0 10:1:1::/112 -> 10::3:4:5/112 diff --git a/contrib/ipfilter/test/regress/n12_6 b/contrib/ipfilter/test/regress/n12_6 new file mode 100644 index 00000000000..bf218489d05 --- /dev/null +++ b/contrib/ipfilter/test/regress/n12_6 @@ -0,0 +1 @@ +map le0 c0a8:7e00::/112 -> 0/128 portmap tcp/udp 10000:20000 diff --git a/contrib/ipfilter/test/regress/n13_6 b/contrib/ipfilter/test/regress/n13_6 new file mode 100644 index 00000000000..c1d1646d2dd --- /dev/null +++ b/contrib/ipfilter/test/regress/n13_6 @@ -0,0 +1 @@ +map le0 192:168:1::0/48 -> range 203:0:1::1:23-203:0:1::3:45 diff --git a/contrib/ipfilter/test/regress/n14_6 b/contrib/ipfilter/test/regress/n14_6 new file mode 100644 index 00000000000..64e88ee4c7f --- /dev/null +++ b/contrib/ipfilter/test/regress/n14_6 @@ -0,0 +1 @@ +rdr gre0 any port 80 -> 10:1:1::254,10:1:1::253 port 80 tcp sticky diff --git a/contrib/ipfilter/test/regress/n15 b/contrib/ipfilter/test/regress/n15 new file mode 100644 index 00000000000..062b766c9ab --- /dev/null +++ b/contrib/ipfilter/test/regress/n15 @@ -0,0 +1,2 @@ +rdr le0 0/0 port 80 -> 3.3.3.3 port 80 tcp +rdr le0 0/0 port 80 -> 3.3.3.3 port 80-88 tcp diff --git a/contrib/ipfilter/test/regress/n15_6 b/contrib/ipfilter/test/regress/n15_6 new file mode 100644 index 00000000000..e82dd823276 --- /dev/null +++ b/contrib/ipfilter/test/regress/n15_6 @@ -0,0 +1,2 @@ +rdr le0 any port 80 -> 3:0:3::3:3 port 80 tcp +rdr le0 any port 80 -> 3:0:3::3:3 port 80-88 tcp diff --git a/contrib/ipfilter/test/regress/n16_6 b/contrib/ipfilter/test/regress/n16_6 new file mode 100644 index 00000000000..ff8958cad86 --- /dev/null +++ b/contrib/ipfilter/test/regress/n16_6 @@ -0,0 +1 @@ +rdr vlan0 from any to 69.248.79.193 port = 38136 -> 172.31.83.24 port 2013 udp diff --git a/contrib/ipfilter/test/regress/n17 b/contrib/ipfilter/test/regress/n17 new file mode 100644 index 00000000000..213f51f079b --- /dev/null +++ b/contrib/ipfilter/test/regress/n17 @@ -0,0 +1 @@ +bimap zx0 0/0 -> 1.1.1.3 diff --git a/contrib/ipfilter/test/regress/n17_6 b/contrib/ipfilter/test/regress/n17_6 new file mode 100644 index 00000000000..08ef77a4884 --- /dev/null +++ b/contrib/ipfilter/test/regress/n17_6 @@ -0,0 +1 @@ +bimap zx0 any -> 1::1:1:3 diff --git a/contrib/ipfilter/test/regress/n18 b/contrib/ipfilter/test/regress/n18 new file mode 100644 index 00000000000..792f136b76c --- /dev/null +++ b/contrib/ipfilter/test/regress/n18 @@ -0,0 +1,3 @@ +map z0 0/0 -> 1.1.1.1/32 portmap tcp/udp 1:4 sequential +map z0 0/0 -> 1.1.1.1/32 portmap tcp/udp 1000:5000 sequential +map z0 0/0 -> 1.1.1.1/32 portmap tcp/udp 1000:50000 sequential diff --git a/contrib/ipfilter/test/regress/n1_6 b/contrib/ipfilter/test/regress/n1_6 new file mode 100644 index 00000000000..341f13651b9 --- /dev/null +++ b/contrib/ipfilter/test/regress/n1_6 @@ -0,0 +1,3 @@ +map zx0 10:1:1::1/128 -> 10::2:2:2/128 +map zx0 10:1:1::/112 -> 10::3:4:5/128 +map zx0 10:1:1::/112 -> 10::3:4:0/112 diff --git a/contrib/ipfilter/test/regress/n200 b/contrib/ipfilter/test/regress/n200 new file mode 100644 index 00000000000..c792e540675 --- /dev/null +++ b/contrib/ipfilter/test/regress/n200 @@ -0,0 +1 @@ +divert in on bar0 from any to any -> src 127.0.0.1,10101 dst 127.0.0.1,10101 udp; diff --git a/contrib/ipfilter/test/regress/n2_6 b/contrib/ipfilter/test/regress/n2_6 new file mode 100644 index 00000000000..3a04f33ae74 --- /dev/null +++ b/contrib/ipfilter/test/regress/n2_6 @@ -0,0 +1,4 @@ +map zx0 10:1:1::1/128 -> 10::2:2:2/128 portmap tcp 10000:20000 sequential +map zx0 10:1:1::/112 -> 10::3:4:5/128 portmap udp 10000:20000 sequential +map zx0 10:1::/32 -> 10::3:4:0/112 portmap tcp/udp 10000:20000 sequential +map zx0 10:1:1::/112 -> 10::3:4:5/128 portmap tcp/udp 40000:40001 sequential diff --git a/contrib/ipfilter/test/regress/n4_6 b/contrib/ipfilter/test/regress/n4_6 new file mode 100644 index 00000000000..72dad4c1121 --- /dev/null +++ b/contrib/ipfilter/test/regress/n4_6 @@ -0,0 +1,6 @@ +rdr zx0 10:1:1::1/128 port 23 -> 10::2:2:1 port 10023 tcp +rdr zx0 10:1:1::/112 port 23 -> 10::2:2:1 port 10023 tcp +rdr zx0 any port 23 -> 10::2:2:1 port 10023 tcp +rdr zx0 10:1:1::/112 port 53 -> 10::2:2:1 port 10053 udp +rdr zx0 10:1:1::/112 port 0 -> 10::2:2:1 port 0 tcp +rdr zx0 10:1:1::/112 port 0 -> 10::2:2:1 port 0 ip diff --git a/contrib/ipfilter/test/regress/n5_6 b/contrib/ipfilter/test/regress/n5_6 new file mode 100644 index 00000000000..acefd7b18a7 --- /dev/null +++ b/contrib/ipfilter/test/regress/n5_6 @@ -0,0 +1,6 @@ +map zx0 10:1:1::1/128 -> 10::2:2:2/128 +map zx0 from 10:1:1::/112 to 10:1::/32 -> 10::3:4:5/128 +map zx0 from 10:1:1::/112 ! to 10:1::/32 -> 10::3:4:0/112 +map zx0 10:1:1::/112 -> 10::3:4:5/128 portmap udp 10000:20000 sequential +map zx0 10:1::/32 -> 10::3:4:0/112 portmap tcp/udp 10000:20000 sequential +map zx0 10:1:1::/112 -> 10::3:4:5/128 portmap tcp/udp 40000:40001 sequential diff --git a/contrib/ipfilter/test/regress/n6_6 b/contrib/ipfilter/test/regress/n6_6 new file mode 100644 index 00000000000..3491c6b3e03 --- /dev/null +++ b/contrib/ipfilter/test/regress/n6_6 @@ -0,0 +1,5 @@ +rdr zx0 10:1:1::1/128 port 23 -> 10::2:2:1 port 10023 tcp +rdr zx0 from any to 10:1:1::/112 port = 23 -> 10::2:2:1 port 10023 tcp +rdr zx0 from 10::/32 to 10:1:1::/112 port = 23 -> 10::2:2:1 port 10023 tcp +rdr zx0 from 10:3::/32 to 10:1::/32 port = 23 -> 10::2:2:1 port 10023 tcp +rdr zx0 ! from 10::/32 to 10:1:1::/112 port = 53 -> 10::2:2:1 port 10053 udp diff --git a/contrib/ipfilter/test/regress/n7_6 b/contrib/ipfilter/test/regress/n7_6 new file mode 100644 index 00000000000..88055f69ab3 --- /dev/null +++ b/contrib/ipfilter/test/regress/n7_6 @@ -0,0 +1,3 @@ +rdr zx0 10:1:1::1/128 port 23-79 -> 10::2:2:1 port 10023 tcp +rdr zx0 10:1:1::1/128 port 23-79 -> 10::2:2:1 port = 10023 tcp +rdr zx0 10:1:1::/112 port 80 -> 10::2:2:1,1::2:2:129 port 3128 tcp diff --git a/contrib/ipfilter/test/regress/n8_6 b/contrib/ipfilter/test/regress/n8_6 new file mode 100644 index 00000000000..2f96be05d00 --- /dev/null +++ b/contrib/ipfilter/test/regress/n8_6 @@ -0,0 +1 @@ +map icmp0 2::2:2:0/112 -> 10:10:10::/112 diff --git a/contrib/ipfilter/test/regress/n9_6 b/contrib/ipfilter/test/regress/n9_6 new file mode 100644 index 00000000000..31e4615f132 --- /dev/null +++ b/contrib/ipfilter/test/regress/n9_6 @@ -0,0 +1 @@ +rdr icmp0 4:4:4::/112 port 0 -> 10:10:10::1 port 0 ip diff --git a/contrib/ipfilter/test/regress/ni13.nat b/contrib/ipfilter/test/regress/ni13.nat index 7a879d8cd85..ac2be49d61f 100644 --- a/contrib/ipfilter/test/regress/ni13.nat +++ b/contrib/ipfilter/test/regress/ni13.nat @@ -1 +1 @@ -rdr pcn1 192.168.113.3/32 port 1723 -> 0.0.0.0 port 1723 proxy pptp +rdr pcn1 192.168.113.3/32 port 1723 -> 0.0.0.0 port 1723 tcp proxy pptp diff --git a/contrib/ipfilter/test/regress/ni14.nat b/contrib/ipfilter/test/regress/ni14.nat index c546e99029d..72a8a4a2759 100644 --- a/contrib/ipfilter/test/regress/ni14.nat +++ b/contrib/ipfilter/test/regress/ni14.nat @@ -1 +1 @@ -rdr pcn1 192.168.113.3/32 port 1723 -> 127.0.0.1 port 1723 proxy pptp +rdr pcn1 192.168.113.3/32 port 1723 -> 127.0.0.1 port 1723 tcp proxy pptp diff --git a/contrib/ipfilter/test/regress/ni17.ipf b/contrib/ipfilter/test/regress/ni17.ipf new file mode 100644 index 00000000000..e69de29bb2d diff --git a/contrib/ipfilter/test/regress/ni18.ipf b/contrib/ipfilter/test/regress/ni18.ipf new file mode 100644 index 00000000000..e69de29bb2d diff --git a/contrib/ipfilter/test/regress/ni18.nat b/contrib/ipfilter/test/regress/ni18.nat new file mode 100644 index 00000000000..40113c1c32c --- /dev/null +++ b/contrib/ipfilter/test/regress/ni18.nat @@ -0,0 +1,4 @@ +rdr hme0 192.168.1.0/24 port 80 -> 1.1.1.1 port 80 tcp; +no rdr hme0 192.168.1.1 port 80 tcp; +map hme1 10.1.0.0/16 -> 203.1.1.1/32 portmap tcp/udp 10000:20000 +no map hme1 10.1.1.0/24 tcp; diff --git a/contrib/ipfilter/test/regress/p1.pool b/contrib/ipfilter/test/regress/p1.pool index 14ae3a3d40d..aa262a7704c 100644 --- a/contrib/ipfilter/test/regress/p1.pool +++ b/contrib/ipfilter/test/regress/p1.pool @@ -1,2 +1,2 @@ table role = ipf type = tree number = 100 - { 1.1.1.1/32; !2.2.0.0/16; 2.2.2.0/24; }; + { 1.1.1.1/32; !2.2.0.0/16; 2.2.2.0/24; ef00::5/128; }; diff --git a/contrib/ipfilter/test/regress/p10.nat b/contrib/ipfilter/test/regress/p10.nat new file mode 100644 index 00000000000..3c3fa7c1874 --- /dev/null +++ b/contrib/ipfilter/test/regress/p10.nat @@ -0,0 +1 @@ +rewrite in on bge0 proto tcp from any to any port = 80 -> src 0/0 dst dstlist/servers; diff --git a/contrib/ipfilter/test/regress/p10.pool b/contrib/ipfilter/test/regress/p10.pool new file mode 100644 index 00000000000..2be554aadc8 --- /dev/null +++ b/contrib/ipfilter/test/regress/p10.pool @@ -0,0 +1,2 @@ +pool nat/dstlist (name servers; policy hash;) + { 1.1.1.2; 1.1.1.4; 1.1.1.5; 1.1.1.9; }; diff --git a/contrib/ipfilter/test/regress/p11.nat b/contrib/ipfilter/test/regress/p11.nat new file mode 100644 index 00000000000..3c3fa7c1874 --- /dev/null +++ b/contrib/ipfilter/test/regress/p11.nat @@ -0,0 +1 @@ +rewrite in on bge0 proto tcp from any to any port = 80 -> src 0/0 dst dstlist/servers; diff --git a/contrib/ipfilter/test/regress/p11.pool b/contrib/ipfilter/test/regress/p11.pool new file mode 100644 index 00000000000..a79d9ea5809 --- /dev/null +++ b/contrib/ipfilter/test/regress/p11.pool @@ -0,0 +1,2 @@ +pool nat/dstlist (name servers; policy dst-hash;) + { 1.1.1.2; 1.1.1.4; 1.1.1.5; 1.1.1.9; }; diff --git a/contrib/ipfilter/test/regress/p12.nat b/contrib/ipfilter/test/regress/p12.nat new file mode 100644 index 00000000000..3c3fa7c1874 --- /dev/null +++ b/contrib/ipfilter/test/regress/p12.nat @@ -0,0 +1 @@ +rewrite in on bge0 proto tcp from any to any port = 80 -> src 0/0 dst dstlist/servers; diff --git a/contrib/ipfilter/test/regress/p12.pool b/contrib/ipfilter/test/regress/p12.pool new file mode 100644 index 00000000000..c9afcda840c --- /dev/null +++ b/contrib/ipfilter/test/regress/p12.pool @@ -0,0 +1,2 @@ +pool nat/dstlist (name servers; policy src-hash;) + { 1.1.1.2; 1.1.1.4; 1.1.1.5; 1.1.1.9; }; diff --git a/contrib/ipfilter/test/regress/p13.ipf b/contrib/ipfilter/test/regress/p13.ipf new file mode 100644 index 00000000000..acaf639ac1f --- /dev/null +++ b/contrib/ipfilter/test/regress/p13.ipf @@ -0,0 +1 @@ +pass in from pool/100 to any diff --git a/contrib/ipfilter/test/regress/p13.pool b/contrib/ipfilter/test/regress/p13.pool new file mode 100644 index 00000000000..de80f72d5b8 --- /dev/null +++ b/contrib/ipfilter/test/regress/p13.pool @@ -0,0 +1,2 @@ +table role = all type = tree number = 100 + { 1.1.1.1/32; !2.2.0.0/16; 2.2.2.0/24; ef00::5/128; }; diff --git a/contrib/ipfilter/test/regress/p3.ipf b/contrib/ipfilter/test/regress/p3.ipf index aad7cb358b5..a598d88168c 100644 --- a/contrib/ipfilter/test/regress/p3.ipf +++ b/contrib/ipfilter/test/regress/p3.ipf @@ -1,5 +1,5 @@ -call now fr_srcgrpmap/1010 in all -call now fr_dstgrpmap/2010 out all +call now srcgrpmap/1010 in all +call now dstgrpmap/2010 out all pass in all group 1020 block in all group 1030 pass out all group 2020 diff --git a/contrib/ipfilter/test/regress/p4.nat b/contrib/ipfilter/test/regress/p4.nat new file mode 100644 index 00000000000..d504ac90c46 --- /dev/null +++ b/contrib/ipfilter/test/regress/p4.nat @@ -0,0 +1 @@ +map * from pool/100 to any -> 1.2.3.4/32 diff --git a/contrib/ipfilter/test/regress/p4.pool b/contrib/ipfilter/test/regress/p4.pool new file mode 100644 index 00000000000..6ed0e499ac5 --- /dev/null +++ b/contrib/ipfilter/test/regress/p4.pool @@ -0,0 +1,2 @@ +table role = nat type = tree number = 100 + { 1.1.1.1/32; !2.2.0.0/16; 2.2.2.0/24; }; diff --git a/contrib/ipfilter/test/regress/p6.ipf b/contrib/ipfilter/test/regress/p6.ipf new file mode 100644 index 00000000000..b9b89374eed --- /dev/null +++ b/contrib/ipfilter/test/regress/p6.ipf @@ -0,0 +1 @@ +block in from pool/microsoft to any diff --git a/contrib/ipfilter/test/regress/p6.pool b/contrib/ipfilter/test/regress/p6.pool new file mode 100644 index 00000000000..83e818ced5b --- /dev/null +++ b/contrib/ipfilter/test/regress/p6.pool @@ -0,0 +1 @@ +pool ipf/tree (name microsoft;) { whois file "regress/p6.whois"; }; diff --git a/contrib/ipfilter/test/regress/p6.whois b/contrib/ipfilter/test/regress/p6.whois new file mode 100644 index 00000000000..284244e11b8 --- /dev/null +++ b/contrib/ipfilter/test/regress/p6.whois @@ -0,0 +1,241 @@ +# This query resulted in more than 256 records. Remaining results +# have been truncated. For more specific results, go to +# http://ws.arin.net/whois for help in refining your query. +Microsoft Corp (MSFT) +Microsoft Corp., MSN Operations (MCMO) +MICROSOFT CORPORATION (MICRO-101) +MICROSOFT CORPORATION (MICRO-97) +MICROSOFT CORPORATION (MICRO-100) +Microsoft Corporation (MICRO-111) +MICROSOFT CORPORATION (MICRO-117) +Microsoft Corporation (ZM23-ARIN) noc@microsoft.com +1-425-882-8080 +Microsoft (ZM39-ARIN) noc@microsoft.com +1-425-882-8080 +Microsoft Corp (AS8068) MICROSOFT-CORP---MSN-AS-BLOCK 8068 - 8075 +Microsoft Corp (AS13811) MSLI 13811 +Microsoft Corp (AS14719) MICROSOFT-CORP-BCENTRAL 14719 +Microsoft Corp (AS3598) MICROSOFT-CORP-AS 3598 +Microsoft Corp (AS5761) MICROSOFT-CORP---MSN-AS---SATURN 5761 +Microsoft Corp (AS6182) MICROSOFT-CORP--MSN-AS-4 6182 +Microsoft Corp (AS6194) MICROSOFT-CORP--MSN-AS-3 6194 +Microsoft Corp (AS6291) MICROSOFT-CORP---MSN-AS 6291 +Microsoft Corp (AS13399) MICROSOFT-CORP---MSN-AS-2 13399 +Microsoft Corp (AS23468) MICROSOFT-CORP-XBOX-ONLINE 23468 +Microsoft Corp MICROSOFT (NET-131-107-0-0-1) 131.107.0.0 - 131.107.255.255 +Microsoft Corp MICROSOFT-VEXCEL (NET-192-92-90-0-1) 192.92.90.0 - 192.92.90.255 +Microsoft Corp NETBLK-MSOFT-NET (NET-198-105-232-0-1) 198.105.232.0 - 198.105.235.255 +Microsoft Corp MICROSOFT19-NET58 (NET-204-231-58-0-1) 204.231.58.0 - 204.231.58.255 +Microsoft Corp MICROSOFT15 (NET-204-140-77-0-1) 204.140.77.0 - 204.140.77.255 +Microsoft Corp MICROSOFT16 (NET-204-140-80-0-1) 204.140.80.0 - 204.140.83.255 +Microsoft Corp MICROSOFT-CORP-MSN-1 (NET-199-60-28-0-1) 199.60.28.0 - 199.60.28.255 +Microsoft Corp MICROSOFT-1 (NET-199-103-90-0-1) 199.103.90.0 - 199.103.91.255 +Microsoft Corp MICROSOFT-CORP-MSN-3 (NET-199-103-122-0-1) 199.103.122.0 - 199.103.122.255 +Microsoft Corp MICROSOFT8 (NET-204-79-101-0-1) 204.79.101.0 - 204.79.101.255 +Microsoft Corp MICROSOFT18 (NET-192-237-67-0-1) 192.237.67.0 - 192.237.67.255 +Microsoft Corp MICROSOFT19 (NET-198-137-97-0-1) 198.137.97.0 - 198.137.97.255 +Microsoft Corp MICROSOFT-HK (NET-204-79-135-0-1) 204.79.135.0 - 204.79.135.255 +Microsoft Corp MICROSOFT-PLACEWARE-1 (NET-204-79-179-0-1) 204.79.179.0 - 204.79.179.255 +Microsoft Corp MICROSOFT11 (NET-204-79-180-0-1) 204.79.180.0 - 204.79.181.255 +Microsoft Corp MICROSOFT-PLACEWARE-2 (NET-204-79-188-0-1) 204.79.188.0 - 204.79.188.255 +Microsoft Corp MICROSOFT13 (NET-204-79-195-0-1) 204.79.195.0 - 204.79.197.255 +Microsoft Corp MICROSOFT17 (NET-199-6-92-0-1) 199.6.92.0 - 199.6.94.255 +Microsoft Corp MICROSOFT-2 (NET-204-79-7-0-1) 204.79.7.0 - 204.79.7.255 +Microsoft Corp MICROSOFT-NET1 (NET-204-79-27-0-1) 204.79.27.0 - 204.79.27.255 +Microsoft Corp MICROSOFT2 (NET-198-180-74-0-1) 198.180.74.0 - 198.180.75.255 +Microsoft Corp MICROSOFT3 (NET-198-180-95-0-1) 198.180.95.0 - 198.180.97.255 +Microsoft Corp MICROSOFT28 (NET-204-231-236-0-1) 204.231.236.0 - 204.231.236.255 +Microsoft Corp MICROSOFT29 (NET-205-248-10-0-1) 205.248.10.0 - 205.248.15.255 +Microsoft Corp SPRINT-CDA33F (NET-205-163-63-0-1) 205.163.63.0 - 205.163.63.255 +Microsoft Corp SPRINT-CDA33E (NET-205-163-62-0-1) 205.163.62.0 - 205.163.62.255 +Microsoft Corp SPRINT-CDA39F (NET-205-163-144-0-1) 205.163.144.0 - 205.163.159.255 +Microsoft Corp MICROSOFT30 (NET-205-248-41-0-1) 205.248.41.0 - 205.248.43.255 +Microsoft Corp MICROSOFT31 (NET-205-248-50-0-1) 205.248.50.0 - 205.248.51.255 +Microsoft Corp MICROSOFT32 (NET-205-248-61-0-1) 205.248.61.0 - 205.248.63.255 +Microsoft Corp MICROSOFT34 (NET-205-248-72-0-1) 205.248.72.0 - 205.248.72.255 +Microsoft Corp MICROSOFT35 (NET-205-248-212-0-1) 205.248.212.0 - 205.248.215.255 +Microsoft Corp MICROSOFT36 (NET-205-248-228-0-1) 205.248.228.0 - 205.248.228.255 +Microsoft Corp MICROSOFT37 (NET-205-248-235-0-1) 205.248.235.0 - 205.248.235.255 +Microsoft Corp MICROSOFT20 (NET-204-231-76-0-1) 204.231.76.0 - 204.231.76.255 +Microsoft Corp MICROSOFT26 (NET-204-231-192-0-1) 204.231.192.0 - 204.231.192.255 +Microsoft Corp MICROSOFT27 (NET-204-231-194-0-1) 204.231.194.0 - 204.231.223.255 +Microsoft Corp SOCRATIC (NET-207-78-80-0-1) 207.78.80.0 - 207.78.80.255 +Microsoft Corp DAVELADD (NET-207-78-81-0-1) 207.78.81.0 - 207.78.81.255 +Microsoft Corp RSEGAL (NET-207-78-82-0-1) 207.78.82.0 - 207.78.82.255 +Microsoft Corp MICROSOFT44 (NET-205-248-243-0-1) 205.248.243.0 - 205.248.244.255 +Microsoft Corp MICROSOFT48 (NET-207-117-3-0-1) 207.117.3.0 - 207.117.3.255 +Microsoft Corp UU-207-18-117 (NET-207-18-117-0-1) 207.18.117.0 - 207.18.117.255 +Microsoft Corp CW-208-139-27-B (NET-208-139-27-0-1) 208.139.27.0 - 208.139.27.255 +Microsoft Corp MICROSOFT55 (NET-209-28-213-0-1) 209.28.213.0 - 209.28.213.255 +Microsoft Corp MICROSOFT50 (NET-207-209-68-0-1) 207.209.68.0 - 207.209.68.255 +Microsoft Corp SPRINT-CC5F6F (NET-204-95-96-0-1) 204.95.96.0 - 204.95.111.255 +Microsoft Corp CYBR-LCCLAB (NET-207-158-93-192-1) 207.158.93.192 - 207.158.93.223 +Microsoft Corp MSBPN-2 (NET-207-240-123-192-1) 207.240.123.192 - 207.240.123.223 +Microsoft Corp SPRINT-D01ACD (NET-208-26-205-0-1) 208.26.205.0 - 208.26.205.255 +Microsoft Corp MICROSOFT-CORP-MSN-2 (NET-192-197-157-0-1) 192.197.157.0 - 192.197.157.255 +Microsoft Corp MICROSOFTDENVER (NET-204-133-231-0-1) 204.133.231.0 - 204.133.231.255 +Microsoft Corp MICROSOFTG1-COM (NET-216-72-96-0-1) 216.72.96.0 - 216.72.99.255 +Microsoft Corp EACT-CUST-JLEZNEK (NET-207-229-166-152-1) 207.229.166.152 - 207.229.166.159 +Microsoft Corp SPRINT-CC5F95-8 (NET-204-95-149-0-1) 204.95.149.0 - 204.95.149.255 +Microsoft Corp NET-CSAMSI (NET-209-192-213-72-1) 209.192.213.72 - 209.192.213.79 +Microsoft Corp MICROSOFT57 (NET-206-73-203-0-1) 206.73.203.0 - 206.73.203.255 +Microsoft Corp MICROSOFT56 (NET-206-73-118-0-1) 206.73.118.0 - 206.73.118.255 +Microsoft Corp QWEST-208-45-54-16 (NET-208-45-54-16-1) 208.45.54.16 - 208.45.54.23 +Microsoft Corp QWEST-208-45-54-8 (NET-208-45-54-8-1) 208.45.54.8 - 208.45.54.15 +Microsoft Corp MICROSOFT58 (NET-206-73-31-0-1) 206.73.31.0 - 206.73.31.255 +Microsoft Corp SPRINT-3FA132 (NET-63-161-50-128-1) 63.161.50.128 - 63.161.50.255 +Microsoft Corp SPRINT-3FA132-6 (NET-63-161-50-0-1) 63.161.50.0 - 63.161.50.127 +Microsoft Corp MICROSOFT-8-18 (NET-207-240-8-224-1) 207.240.8.224 - 207.240.8.239 +Microsoft Corp MICROSOFT-BBLK (NET-157-54-0-0-1) 157.54.0.0 - 157.60.255.255 +Microsoft Corp QWEST-208-45-89-248A (NET-208-45-89-248-1) 208.45.89.248 - 208.45.89.255 +Microsoft Corp MICROSOFT61 (NET-206-182-69-0-1) 206.182.69.0 - 206.182.69.255 +Microsoft Corp MICROSOFT63 (NET-206-182-240-0-1) 206.182.240.0 - 206.182.240.255 +Microsoft Corp MICROSOFT64 (NET-206-182-241-0-1) 206.182.241.0 - 206.182.241.255 +Microsoft Corp MICROSOFT59 (NET-206-73-67-0-1) 206.73.67.0 - 206.73.67.255 +Microsoft Corp MICROSOFT66 (NET-206-182-251-0-1) 206.182.251.0 - 206.182.251.255 +Microsoft Corp MICROSOFT65 (NET-206-182-247-0-1) 206.182.247.0 - 206.182.247.255 +Microsoft Corp MICROSOFT62 (NET-206-182-236-0-1) 206.182.236.0 - 206.182.236.255 +Microsoft Corp QWEST-63-236-198-64 (NET-63-236-198-64-1) 63.236.198.64 - 63.236.198.71 +Microsoft Corp QWEST-63-236-198-152 (NET-63-236-198-152-1) 63.236.198.152 - 63.236.198.159 +Microsoft Corp ERMS-6799349 (NET-165-121-253-232-1) 165.121.253.232 - 165.121.253.239 +Microsoft Corp QWEST-63-236-170-64 (NET-63-236-170-64-1) 63.236.170.64 - 63.236.170.71 +Microsoft Corp QWEST-63-236-186-64 (NET-63-236-186-64-1) 63.236.186.64 - 63.236.186.71 +Microsoft Corp QWEST-63-236-187-104 (NET-63-236-187-104-1) 63.236.187.104 - 63.236.187.111 +Microsoft Corp QWEST-63-236-187-128 (NET-63-236-187-128-1) 63.236.187.128 - 63.236.187.135 +Microsoft Corp QWEST-63-236-187-160 (NET-63-236-187-160-1) 63.236.187.160 - 63.236.187.167 +Microsoft Corp FON-3338832128690 (NET-199-2-137-0-1) 199.2.137.0 - 199.2.137.255 +Microsoft Corp CUST-86-24614 (NET-216-222-104-224-1) 216.222.104.224 - 216.222.104.239 +Microsoft Corp QWEST-63-151-87-64 (NET-63-151-87-64-1) 63.151.87.64 - 63.151.87.71 +Microsoft Corp HP-64-77-82-96 (NET-64-77-82-96-1) 64.77.82.96 - 64.77.82.103 +Microsoft Corp HP-64-77-93-80 (NET-64-77-93-80-1) 64.77.93.80 - 64.77.93.95 +Microsoft Corp MICROSOFT-1BLK (NET-65-52-0-0-1) 65.52.0.0 - 65.55.255.255 +Microsoft Corp MICROSOFT-GLOBAL-NET (NET-207-46-0-0-1) 207.46.0.0 - 207.46.255.255 +Microsoft Corp MICROSOFT-CORP-MSN-BLK (NET-207-68-128-0-1) 207.68.128.0 - 207.68.207.255 +Microsoft Corp FON-343451648081865 (NET-204-182-144-0-1) 204.182.144.0 - 204.182.159.255 +Microsoft Corp FON-346312755281299 (NET-206-107-34-0-1) 206.107.34.0 - 206.107.34.255 +Microsoft Corp FON-34550983681918 (NET-205-240-158-0-1) 205.240.158.0 - 205.240.159.255 +Microsoft Corp MICROSOFT-PLACEWARE-2 (NET-204-79-252-0-1) 204.79.252.0 - 204.79.252.255 +Microsoft Corp WLCO-TWC1057147-MICROSOFT (NET-64-200-211-16-1) 64.200.211.16 - 64.200.211.31 +Microsoft Corp MICROSOF81-163-0 (NET-12-178-163-0-1) 12.178.163.0 - 12.178.163.31 +Microsoft Corp WLCO-TWC1057147-MICROSOFT-1 (NET-69-44-126-80-1) 69.44.126.80 - 69.44.126.95 +Microsoft Corp SPRINTLINK (NET-63-173-42-128-1) 63.173.42.128 - 63.173.42.255 +Microsoft Corp MICROSOF33-108-0 (NET-12-28-108-0-1) 12.28.108.0 - 12.28.108.127 +Microsoft Corp SPRINTLINK (NET-65-170-29-0-1) 65.170.29.0 - 65.170.29.7 +Microsoft Corp Q0903-67-132-133-96 (NET-67-132-133-96-1) 67.132.133.96 - 67.132.133.103 +Microsoft Corp MICROSOFT-IPV6-BLK (NET6-2001-4898-1) 2001:4898:0000:0000:0000:0000:0000:0000 - 2001:4898:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF +Microsoft Corp LVLT-MSFT-8-6-176 (NET-8-6-176-0-1) 8.6.176.0 - 8.6.176.255 +Microsoft Corp MICROSOFT33 (NET-205-248-80-0-1) 205.248.80.0 - 205.248.129.255 +Microsoft Corp Q0523-63-148-123-240 (NET-63-148-123-240-1) 63.148.123.240 - 63.148.123.247 +Microsoft Corp SAVV-S233608-1 (NET-64-41-193-0-1) 64.41.193.0 - 64.41.193.255 +Microsoft Corp SAVV-S233053-1 (NET-64-85-70-32-1) 64.85.70.32 - 64.85.70.47 +Microsoft Corp SAVV-S233053-2 (NET-64-85-81-96-1) 64.85.81.96 - 64.85.81.103 +Microsoft Corp SAVV-S233053-3 (NET-64-85-81-104-1) 64.85.81.104 - 64.85.81.111 +Microsoft Corp SAVV-S233053-7 (NET-216-32-168-224-1) 216.32.168.224 - 216.32.168.255 +Microsoft Corp SAVV-S233053-6 (NET-206-79-74-32-1) 206.79.74.32 - 206.79.74.47 +Microsoft Corp SAVV-S233053-8 (NET-216-32-175-224-1) 216.32.175.224 - 216.32.175.255 +Microsoft Corp SAVV-S233053-9 (NET-216-32-180-0-1) 216.32.180.0 - 216.32.183.255 +Microsoft Corp SAVV-S233053-11 (NET-216-33-229-224-1) 216.33.229.224 - 216.33.229.255 +Microsoft Corp SAVV-S233053-12 (NET-216-33-236-0-1) 216.33.236.0 - 216.33.239.255 +Microsoft Corp SAVV-S233053-13 (NET-216-33-240-0-1) 216.33.240.0 - 216.33.243.255 +Microsoft Corp SAVV-S233053-10 (NET-216-32-240-0-1) 216.32.240.0 - 216.32.243.255 +Microsoft Corp SAVV-S233608-3 (NET-216-34-51-0-1) 216.34.51.0 - 216.34.51.255 +Microsoft Corp SAVV-S233053-4 (NET-209-1-112-0-1) 209.1.112.0 - 209.1.112.255 +Microsoft Corp SAVV-S233053-5 (NET-209-1-113-0-1) 209.1.113.0 - 209.1.113.255 +Microsoft Corp SAVV-S233608-2 (NET-209-1-15-0-1) 209.1.15.0 - 209.1.15.255 +Microsoft Corp SAVV-S233608-4 (NET-216-34-53-176-1) 216.34.53.176 - 216.34.53.191 +Microsoft Corp SAVV-S233608-5 (NET-216-35-8-224-1) 216.35.8.224 - 216.35.8.239 +Microsoft Corp SAVV-S233053-14 (NET-209-185-128-0-1) 209.185.128.0 - 209.185.131.255 +Microsoft Corp Q0112-65-114-175-128 (NET-65-114-175-128-1) 65.114.175.128 - 65.114.175.159 +Microsoft Corp SAVV-S233053-15 (NET-64-15-229-96-1) 64.15.229.96 - 64.15.229.127 +Microsoft Corp SAVV-S233050-5 (NET-64-15-177-0-1) 64.15.177.0 - 64.15.177.255 +Microsoft Corp SAVV-S233050-4 (NET-64-15-170-192-1) 64.15.170.192 - 64.15.170.199 +Microsoft Corp SAVV-S233050-2 (NET-209-143-238-0-1) 209.143.238.0 - 209.143.238.255 +Microsoft Corp SAVV-S233050-6 (NET-64-15-178-0-1) 64.15.178.0 - 64.15.178.255 +Microsoft Corp SAVV-S232995-2 (NET-66-35-209-120-1) 66.35.209.120 - 66.35.209.127 +Microsoft Corp SAVV-S232995-3 (NET-66-35-211-128-1) 66.35.211.128 - 66.35.211.191 +Microsoft Corp SAVV-S232995-1 (NET-66-35-208-48-1) 66.35.208.48 - 66.35.208.63 +Microsoft Corp SAVV-S233053-16 (NET-216-33-148-0-1) 216.33.148.0 - 216.33.151.255 +Microsoft Corp., MSN Operations SAVV-S233052-4 (NET-216-35-66-88-1) 216.35.66.88 - 216.35.66.95 +MICROSOFT CORPORATION MICROSOF32-32-160 (NET-12-230-32-160-1) 12.230.32.160 - 12.230.32.167 +MICROSOFT CORPORATION MICROSOF43-124-0 (NET-12-53-124-0-1) 12.53.124.0 - 12.53.124.31 +MICROSOFT CORPORATION MICROSOF82-18-96 (NET-12-232-18-96-1) 12.232.18.96 - 12.232.18.127 +MICROSOFT CORPORATION MICROSOF25-158 (NET-12-190-158-0-1) 12.190.158.0 - 12.190.158.255 +MICROSOFT CORPORATION MICROSOF61-196-32 (NET-12-71-196-32-1) 12.71.196.32 - 12.71.196.47 +Microsoft Corporation MICROSOFT-ONLINE-SERVICES (NET-209-240-192-0-1) 209.240.192.0 - 209.240.223.255 +Microsoft Corporation MICROSOFT-DYNAMIC-HOSTING (NET-70-37-0-0-1) 70.37.0.0 - 70.37.191.255 +Microsoft Corporation MS-ONLINE-SERVICES-NJ (NET-70-37-128-0-1) 70.37.128.0 - 70.37.129.255 +Microsoft Corporation MS-GLOBAL-ONLINE-SERVICES (NET-70-37-135-0-1) 70.37.135.0 - 70.37.135.255 +MICROSOFT CORPORATION MICROSOF82-87-192 (NET-12-49-87-192-1) 12.49.87.192 - 12.49.87.255 +Microsoft MICROSOFT (NET-74-93-205-144-1) 74.93.205.144 - 74.93.205.151 +Microsoft MICROSOFT (NET-74-93-205-152-1) 74.93.205.152 - 74.93.205.159 +Microsoft MICROSOFT (NET-74-93-206-64-1) 74.93.206.64 - 74.93.206.71 +Microsoft MICROSOFT (NET-70-89-139-120-1) 70.89.139.120 - 70.89.139.127 +Microsoft DIRECP-NET1-206-71-11 (NET-206-71-119-0-1) 206.71.119.0 - 206.71.119.255 +Microsoft DIRECP-NET1-117 (NET-206-71-117-0-1) 206.71.117.0 - 206.71.117.255 +Microsoft DIRECP-NET1-118 (NET-206-71-118-0-1) 206.71.118.0 - 206.71.118.255 +Microsoft UUHIL-BLK1-C155-112 (NET-209-154-155-112-1) 209.154.155.112 - 209.154.155.119 +Microsoft SBCIS-101411-164355 (NET-65-68-62-152-1) 65.68.62.152 - 65.68.62.159 +MICROSOFT SBC067039208168020503 (NET-67-39-208-168-1) 67.39.208.168 - 67.39.208.175 +Microsoft UU-65-242-67 (NET-65-242-67-0-1) 65.242.67.0 - 65.242.67.255 +Microsoft CW-204-71-191-0 (NET-204-71-191-0-1) 204.71.191.0 - 204.71.191.255 +Microsoft SBC063194155144021023 (NET-63-194-155-144-1) 63.194.155.144 - 63.194.155.151 +Microsoft SBC066136085192030113 (NET-66-136-85-192-1) 66.136.85.192 - 66.136.85.199 +MICROSOFT MFN-T280-64-124-184-72-29 (NET-64-124-184-72-1) 64.124.184.72 - 64.124.184.79 +MICROSOFT MFN-T133-216-200-206-0-24 (NET-216-200-206-0-1) 216.200.206.0 - 216.200.206.255 +Microsoft UU-63-80-93-D4 (NET-63-80-93-0-1) 63.80.93.0 - 63.80.93.127 +Microsoft RSPC-1218167167199384 (NET-67-192-225-208-1) 67.192.225.208 - 67.192.225.223 +Microsoft CVNET-454AA20 (NET-69-74-162-0-1) 69.74.162.0 - 69.74.162.255 +Microsoft UU-65-221-5 (NET-65-221-5-0-1) 65.221.5.0 - 65.221.5.255 +Microsoft - Partner Campaign Builder (PCB) MICROSOFT-PARTNER-CAMPAIGN-BUILDER-PCB (NET-216-182-89-192-1) 216.182.89.192 - 216.182.89.207 +Microsoft - Partner Campaign Builder (PCB) MICROSOFT-PARTNER-CAMPAIGN-BUILDER-PCB (NET-216-182-89-48-1) 216.182.89.48 - 216.182.89.63 +MICROSOFT AUSTIN-STO UU-65-248-85-D4 (NET-65-248-85-0-1) 65.248.85.0 - 65.248.85.255 +Microsoft Canada MIC0923-CA (NET-199-243-157-192-1) 199.243.157.192 - 199.243.157.223 +Microsoft Canada MIC0702-CA (NET-199-243-157-112-1) 199.243.157.112 - 199.243.157.119 +Microsoft Corp UU-65-194-210-224 (NET-65-194-210-224-1) 65.194.210.224 - 65.194.210.255 +Microsoft Corp UU-208-194-139 (NET-208-194-139-0-1) 208.194.139.0 - 208.194.139.255 +Microsoft Corp UU-208-204-49-128-B (NET-208-204-49-128-1) 208.204.49.128 - 208.204.49.255 +Microsoft Corp UU-208-205-26 (NET-208-205-26-0-1) 208.205.26.0 - 208.205.26.255 +Microsoft Corp UU-208-217-184-D1 (NET-208-217-184-0-1) 208.217.184.0 - 208.217.187.255 +Microsoft Corp UU-208-222-172 (NET-208-222-172-0-1) 208.222.172.0 - 208.222.172.255 +Microsoft Corp UU-208-224-200-64 (NET-208-224-200-64-1) 208.224.200.64 - 208.224.200.95 +Microsoft Corp UU-208-229-100-D1 (NET-208-229-100-0-1) 208.229.100.0 - 208.229.101.255 +Microsoft Corp UU-208-241-19 (NET-208-241-19-0-1) 208.241.19.0 - 208.241.19.15 +Microsoft Corp UU-208-241-19-16 (NET-208-241-19-16-1) 208.241.19.16 - 208.241.19.31 +Microsoft Corp UU-208-241-9-224 (NET-208-241-9-224-1) 208.241.9.224 - 208.241.9.239 +Microsoft Corp UU-208-244-108-D2 (NET-208-244-108-0-1) 208.244.108.0 - 208.244.108.15 +Microsoft Corp UU-208-245-16 (NET-208-245-16-0-1) 208.245.16.0 - 208.245.16.31 +Microsoft Corp UU-208-249-17-160 (NET-208-249-17-160-1) 208.249.17.160 - 208.249.17.175 +Microsoft Corp UU-63-104-216-D2 (NET-63-104-216-0-1) 63.104.216.0 - 63.104.216.127 +Microsoft Corp UU-63-69-245 (NET-63-69-245-0-1) 63.69.245.0 - 63.69.245.255 +Microsoft Corp SBC068090141072031030 (NET-68-90-141-72-1) 68.90.141.72 - 68.90.141.79 +Microsoft Corp 10825385 SBC06319812316029040317151513 (NET-63-198-123-160-1) 63.198.123.160 - 63.198.123.167 +MICROSOFT CORP-040821020257 SBC06824804806429040821020303 (NET-68-248-48-64-1) 68.248.48.64 - 68.248.48.71 +MICROSOFT CORP-040821020338 SBC06824804807229040821020347 (NET-68-248-48-72-1) 68.248.48.72 - 68.248.48.79 +MICROSOFT CORP-081024181821 SBC-99-49-8-248-29-0810241850 (NET-99-49-8-248-1) 99.49.8.248 - 99.49.8.255 +Microsoft Corp. HUGE-65-38-172-72-29 (NET-65-38-172-72-1) 65.38.172.72 - 65.38.172.79 +Microsoft Corp. HUGE-65-38-172-96-28 (NET-65-38-172-96-1) 65.38.172.96 - 65.38.172.111 +Microsoft Corporation MICROSOFT-CORPORATION (NET-75-149-174-16-1) 75.149.174.16 - 75.149.174.23 +Microsoft Corporation MICROSOFT-CORPORATION (NET-75-151-100-240-1) 75.151.100.240 - 75.151.100.255 +Microsoft Corporation SPEK-647057-0 (NET-64-81-8-96-1) 64.81.8.96 - 64.81.8.127 +Microsoft Corporation SBC067112255144030130 (NET-67-112-255-144-1) 67.112.255.144 - 67.112.255.151 +Microsoft Corporation ATTENS-010075-004522 (NET-63-240-201-176-1) 63.240.201.176 - 63.240.201.191 +Microsoft Corporation ATTENS-010075-004523 (NET-206-16-209-208-1) 206.16.209.208 - 206.16.209.223 +Microsoft Corporation ATTENS-010075-004525 (NET-63-240-195-208-1) 63.240.195.208 - 63.240.195.223 +Microsoft Corporation ATTENS-010075-004526 (NET-206-16-204-64-1) 206.16.204.64 - 206.16.204.79 +Microsoft Corporation ATTENS-010075-004450 (NET-206-16-223-0-1) 206.16.223.0 - 206.16.223.255 +Microsoft Corporation ATTENS-010075-005028 (NET-63-240-216-0-1) 63.240.216.0 - 63.240.219.255 +Microsoft Corporation ATTENS-010075-005057 (NET-63-240-220-0-1) 63.240.220.0 - 63.240.223.255 +Microsoft Corporation ATTENS-010075-005135 (NET-206-16-246-24-1) 206.16.246.24 - 206.16.246.31 +Microsoft Corporation ATTENS-010075-004524 (NET-63-240-195-192-1) 63.240.195.192 - 63.240.195.207 +Microsoft Corporation ATTENS-010075-005880 (NET-206-16-224-160-1) 206.16.224.160 - 206.16.224.191 +Microsoft Corporation (managed segment) RSPC-1229444888833780 (NET-98-129-187-144-1) 98.129.187.144 - 98.129.187.151 +Microsoft Corporation - Secure Dimensions ( RSPC-33955-12072007 (NET-67-192-39-48-1) 67.192.39.48 - 67.192.39.63 +Microsoft Corporation - Whale RSPC-108457-1170047010 (NET-72-32-240-160-1) 72.32.240.160 - 72.32.240.175 +Microsoft Corporation - Whale RSPC-108456-1173386392 (NET-72-32-201-152-1) 72.32.201.152 - 72.32.201.159 +MICROSOFT CROP SBC067039081152020503 (NET-67-39-81-152-1) 67.39.81.152 - 67.39.81.159 +Microsoft Education Programs RSPC-48725-1096578571 (NET-69-20-127-32-1) 69.20.127.32 - 69.20.127.39 +Microsoft License PNAP-SFJ-MSLI-RM-01 (NET-216-52-28-0-1) 216.52.28.0 - 216.52.28.255 +Microsoft License INAP-PHX003-MSLICENSE-25271 (NET-70-42-230-0-1) 70.42.230.0 - 70.42.231.255 +Microsoft License INAP-SFJ-MSLICENSE-13982 (NET-63-251-97-0-1) 63.251.97.0 - 63.251.97.255 +Microsoft Licensing SBC067120132128020815 (NET-67-120-132-128-1) 67.120.132.128 - 67.120.132.135 +Microsoft Licensing SBC067120132152020815 (NET-67-120-132-152-1) 67.120.132.152 - 67.120.132.159 +Microsoft Licensing SBC067120132192020816 (NET-67-120-132-192-1) 67.120.132.192 - 67.120.132.207 +Microsoft Licensing SBC0671201322080208 diff --git a/contrib/ipfilter/test/regress/p7.nat b/contrib/ipfilter/test/regress/p7.nat new file mode 100644 index 00000000000..3c3fa7c1874 --- /dev/null +++ b/contrib/ipfilter/test/regress/p7.nat @@ -0,0 +1 @@ +rewrite in on bge0 proto tcp from any to any port = 80 -> src 0/0 dst dstlist/servers; diff --git a/contrib/ipfilter/test/regress/p7.pool b/contrib/ipfilter/test/regress/p7.pool new file mode 100644 index 00000000000..451b374dfd1 --- /dev/null +++ b/contrib/ipfilter/test/regress/p7.pool @@ -0,0 +1,2 @@ +pool nat/dstlist (name servers; policy weighted connection;) + { 1.1.1.2; 1.1.1.4; 1.1.1.5; 1.1.1.9; }; diff --git a/contrib/ipfilter/test/regress/p9.nat b/contrib/ipfilter/test/regress/p9.nat new file mode 100644 index 00000000000..3c3fa7c1874 --- /dev/null +++ b/contrib/ipfilter/test/regress/p9.nat @@ -0,0 +1 @@ +rewrite in on bge0 proto tcp from any to any port = 80 -> src 0/0 dst dstlist/servers; diff --git a/contrib/ipfilter/test/regress/p9.pool b/contrib/ipfilter/test/regress/p9.pool new file mode 100644 index 00000000000..c452ffc681b --- /dev/null +++ b/contrib/ipfilter/test/regress/p9.pool @@ -0,0 +1,2 @@ +pool nat/dstlist (name servers; policy round-robin;) + { 1.1.1.2; 1.1.1.4; 1.1.1.5; 1.1.1.9; }; diff --git a/contrib/ipfilter/test/test.format b/contrib/ipfilter/test/test.format index dfc3f35b619..64f7d9b3202 100644 --- a/contrib/ipfilter/test/test.format +++ b/contrib/ipfilter/test/test.format @@ -1,6 +1,6 @@ -#test input-format output-format +#test input-format output-format options bpf-f1 text text -bpf1 text ipf +bpf1 text text f1 text text f2 text text f3 text text @@ -18,9 +18,11 @@ f14 text text f15 text text f16 text text f17 hex hex -f18 text text -f19 text text fr_statemax=3 +f18 text text -D +f19 text text state_max=3 f20 text text +f21 hex text +f22 hex text i1 text ipf i2 text ipf i3 text ipf @@ -42,17 +44,26 @@ i18 text ipf i19 text ipf i20 text ipf i21 text ipf +i22 text ipf +i23 text ipf in1 text text in2 text text in3 text text in4 text text in5 text text in6 text text +in7 text text +in8 text text +in100 text text +in101 text text +in102 text text ip1 text text ip2 text text +ip3 text text ipv6.1 hex hex ipv6.2 hex hex ipv6.3 hex hex +ipv6.4 hex hex ipv6.5 hex hex l1 hex hex n1 text text @@ -62,31 +73,40 @@ n4 text text n5 text text n6 text text n7 text text -n8 hex hex fr_update_ipid=0 -n9 hex hex fr_update_ipid=0 -n10 hex hex fr_update_ipid=0 +n8 hex hex update_ipid=0 +n9 hex hex update_ipid=0 +n10 hex hex update_ipid=0 n11 text text -n12 hex hex fr_update_ipid=0 -n13 text text -n14 text text -ni1 hex hex fr_update_ipid=1 -ni2 hex hex fr_update_ipid=1 -ni3 hex hex fr_update_ipid=1 -ni4 hex hex fr_update_ipid=1 -ni5 hex hex fr_update_ipid=1 -ni6 hex hex fr_update_ipid=1 -ni7 hex hex fr_update_ipid=1 -ni8 hex hex fr_update_ipid=1 -ni9 hex hex fr_update_ipid=1 -ni10 hex hex fr_update_ipid=1 -ni11 hex hex fr_update_ipid=1 -ni12 hex hex fr_update_ipid=1 -ni13 hex hex fr_update_ipid=1 -ni14 hex hex fr_update_ipid=1 -ni15 hex hex fr_update_ipid=1 -ni16 hex hex fr_update_ipid=1 -ni19 hex hex fr_update_ipid=0 -ni20 hex hex fr_update_ipid=0 +n12 hex hex update_ipid=0 -v +n15 text text update_ipid=0 +n100 text text +n101 text text +n102 text text +n103 text text +n104 hex hex update_ipid=0 +n105 hex hex update_ipid=0 +n106 hex hex update_ipid=0 +n200 hex hex update_ipid=0 +ni1 hex hex update_ipid=1 +ni2 hex hex update_ipid=1 +ni3 hex hex update_ipid=1 +ni4 hex hex update_ipid=1 +ni5 hex hex update_ipid=1 +ni6 hex text update_ipid=1 -D +ni7 hex hex update_ipid=1 +ni8 hex hex update_ipid=1 +ni9 hex hex update_ipid=1 +ni10 hex hex update_ipid=1 +ni11 hex hex update_ipid=1 +ni12 hex hex update_ipid=1 +ni13 hex hex update_ipid=1 +ni14 hex hex update_ipid=1 +ni15 hex hex update_ipid=1 +ni16 hex hex update_ipid=1 +ni17 text text +ni18 text text +ni19 hex hex update_ipid=0 +ni20 hex hex update_ipid=0 -D ni21 text text ni23 text text -D p1 text text @@ -94,6 +114,35 @@ p2 text text p3 text text p4 text text p5 text text +p6 text text +p7 text text +p9 text text +p10 text text +p11 text text +p12 text text +p13 text text n16 hex hex -D +n17 hex hex -D f24 hex text ipv6.6 hex text +f25 hex text -D +f26 text text +f27 hex text +n1_6 text text -6 +n2_6 text text -6 +n4_6 text text -6 +n5_6 text text -6 +n6_6 text text -6 +n7_6 text text -6 +n8_6 hex hex -6D +n9_6 hex hex -6D +n11_6 text text -6 +n12_6 hex hex -D6 +n15_6 text text -6 +n17_6 hex hex -6 +n13 text text +n14 text text +n18 text text -D +f28 text text +f29 text text +f30 text text diff --git a/contrib/ipfilter/test/vfycksum.pl b/contrib/ipfilter/test/vfycksum.pl index b3a20be0cf2..0272e4b867c 100755 --- a/contrib/ipfilter/test/vfycksum.pl +++ b/contrib/ipfilter/test/vfycksum.pl @@ -19,82 +19,71 @@ sub dosum { local($lsum) = $seed; for ($idx = $start, $lsum = $seed; $idx < $max; $idx++) { +#printf "%#x += %#x\n", $lsum, $bytes[$idx]; $lsum += $bytes[$idx]; } - $lsum = ($lsum & 0xffff) + ($lsum >> 16); + while ($lsum > 0xffff) { + $lsum = ($lsum & 0xffff) + ($lsum >> 16); + } $lsum = ~$lsum & 0xffff; return $lsum; } -sub ipv4check { - local($base) = $_[0]; - $hl = $bytes[$base] / 256; - return if (($hl >> 4) != 4); # IPv4 ? - $hl &= 0xf; - $hl <<= 1; # get the header length in 16bit words - $hs = &dosum(0, $base, $base + $hl); - $osum = $bytes[$base + 5]; +sub ipv4addrsum { + local($b) = $_[0]; + local($as) = 0; - if ($hs != 0) { - $bytes[$base + 5] = 0; - $hs2 = &dosum(0, $base, $base + $hl); - $bytes[$base + 5] = $osum; - printf " IP: ($hl,%x) %x != %x", $hs, $osum, $hs2; - } else { - print " IP($base): ok "; - } - - # - # Recognise TCP & UDP and calculate checksums for each of these. - # - if (($bytes[$base + 4] & 0xff) == 6) { - &tcpcheck($base); - } - - if (($bytes[$base + 4] & 0xff) == 17) { - &udpcheck($base); - } - - if (($bytes[$base + 4] & 0xff) == 1) { - &icmpcheck($base); - } - if ($base == 0) { - print "\n"; - } + $as += $bytes[$b + 6]; # source address + $as += $bytes[$b + 7]; + $as += $bytes[$b + 8]; # destination address + $as += $bytes[$b + 9]; + return ($as); } -sub tcpcheck { +sub ipv6addrsum { + local($b) = $_[0]; + local($as) = 0; + + $as += $bytes[$b + 4]; # source address + $as += $bytes[$b + 5]; + $as += $bytes[$b + 6]; + $as += $bytes[$b + 7]; + $as += $bytes[$b + 8]; + $as += $bytes[$b + 9]; + $as += $bytes[$b + 10]; + $as += $bytes[$b + 11]; + $as += $bytes[$b + 12]; # destination address + $as += $bytes[$b + 13]; + $as += $bytes[$b + 14]; + $as += $bytes[$b + 15]; + $as += $bytes[$b + 16]; + $as += $bytes[$b + 17]; + $as += $bytes[$b + 18]; + $as += $bytes[$b + 19]; + return ($as); +} + +sub tcpcommon { local($base) = $_[0]; - local($hl) = $bytes[$base] / 256; - return if (($hl >> 4) != 4); - return if ($bytes[$base + 3] & 0x1fff); - $hl &= 0xf; - $hl <<= 1; + local($hl) = $_[1]; + local($hs) = $_[2]; + local($lenoffset) = $_[3]; - local($hs2); - local($hs) = 6; # TCP - local($len) = $bytes[$base + 1] - ($hl << 1); - $hs += $len; - $hs += $bytes[$base + 6]; # source address - $hs += $bytes[$base + 7]; - $hs += $bytes[$base + 8]; # destination address - $hs += $bytes[$base + 9]; - local($tcpsum) = $hs; - - local($thl) = $bytes[$base + $hl + 6] >> 8; + local($thl) = $bytes[$base + $hl + 6]; $thl &= 0xf0; $thl >>= 2; - $x = $bytes[$base + 1]; - $y = ($cnt - $base) * 2; - $z = 0; - if ($bytes[$base + 1] > ($cnt - $base) * 2) { + local($x) = $bytes[$base + $lenoffset]; + local($y) = ($cnt - $base) * 2; + local($z) = 0; + + if ($bytes[$base + $lenoffset] > ($cnt - $base) * 2) { print "[cnt=$cnt base=$base]"; - $x = $bytes[$base + 1]; + $x = $bytes[$base + $lenoffset]; $y = ($cnt - $base) * 2; $z = 1; - } elsif (($cnt - $base) * 2 < $hl + 20) { + } elsif (($cnt - $base) * 2 < $hl + $hl) { $x = ($cnt - $base) * 2; $y = $hl + 20; $z = 2; @@ -106,6 +95,10 @@ sub tcpcheck { $x = ($cnt - $base) * 2; $y = $len; $z = 4; + } elsif (($cnt - $base) * 2 < 20) { + $x = ($cnt - $base) * 2; + $y = $len; + $z = 5; } if ($z) { @@ -115,11 +108,11 @@ sub tcpcheck { } local($tcpat) = $base + $hl; - $hs = &dosum($tcpsum, $tcpat, $cnt); + $hs = &dosum($_[2], $tcpat, $cnt); if ($hs != 0) { local($osum) = $bytes[$tcpat + 8]; $bytes[$base + $hl + 8] = 0; - $hs2 = &dosum($tcpsum, $tcpat, $cnt); + local($hs2) = &dosum($_[2], $tcpat, $cnt); $bytes[$tcpat + 8] = $osum; printf " TCP: (%x) %x != %x", $hs, $osum, $hs2; } else { @@ -127,23 +120,10 @@ sub tcpcheck { } } -sub udpcheck { +sub udpcommon { local($base) = $_[0]; - local($hl) = $bytes[0] / 256; - return if (($hl >> 4) != 4); - return if ($bytes[3] & 0x1fff); - $hl &= 0xf; - $hl <<= 1; - - local($hs2); - local($hs) = 17; # UDP - local($len) = $bytes[$base + 1] - ($hl << 1); - $hs += $len; - $hs += $bytes[$base + 6]; # source address - $hs += $bytes[$base + 7]; - $hs += $bytes[$base + 8]; # destination address - $hs += $bytes[$base + 9]; - local($udpsum) = $hs; + local($hl) = $_[1]; + local($hs) = $_[2]; if ($bytes[$base + 1] > ($cnt - $base) * 2) { print " UDP: missing data(1)"; @@ -168,7 +148,7 @@ sub udpcheck { printf " UDP: => %x", $hs; } elsif ($hs != 0) { $bytes[$udpat + 3] = 0; - $hs2 = &dosum($udpsum, $udpat, $cnt); + local($hs2) = &dosum($udpsum, $udpat, $cnt); $bytes[$udpat + 3] = $osum; printf " UDP: (%x) %x != %x", $hs, $osum, $hs2; } else { @@ -176,6 +156,156 @@ sub udpcheck { } } +sub ipv6check { + local($base) = $_[0]; + $hl = $bytes[$base] / 256; + return if (($hl >> 4) != 6); # IPv4 ? + $hl = 40; + + print " IPv6($base): ok "; + + if (($bytes[$base + 3] >> 8) == 6) { + &tcpcheck6($base); + } elsif (($bytes[$base + 3] >> 8) == 58) { + &icmpcheck6($base); + } + print "\n"; +} + +sub tcpcheck6 { + local($base) = $_[0]; + local($hl) = $bytes[$base] / 256; + return if (($hl >> 4) != 6); + $hl = 20; + + local($hs) = 6; # TCP + local($len) = $bytes[$base + 2]; + $hs += $len; + $hs += &ipv6addrsum($base); + + &tcpcommon($base, $hl, $hs, 2); +} + +sub icmpcheck6 { + local($base) = $_[0]; + local($hl) = 20; + + local($hs) = 58; # ICMP6 + local($len) = $bytes[$base + 2]; + $hs += $len; + $hs += &ipv6addrsum($base); + + local($len) = $bytes[$base + 1] - ($hl << 1); + + if ($bytes[$base + 2] > ($cnt - $base) * 2) { + print " ICMPv6: missing data(1)"; + return; + } elsif ($bytes[$base + 2] < 8) { + print " ICMPv6: missing data(2)"; + return; + } + + local($osum) = $bytes[$base + $hl + 1]; + $bytes[$base + $hl + 1] = 0; + local($hs2) = &dosum($hs, $base + $hl, $cnt); + $bytes[$base + $hl + 1] = $osum; + + if ($osum != $hs2) { + printf " ICMPv6: (%x) %x != %x", $hs, $osum, $hs2; + } else { + print " ICMPv6: ok"; + } +# if ($base == 0) { +# $type = $bytes[$hl] >> 8; +# if ($type == 3 || $type == 4 || $type == 5 || +# $type == 11 || $type == 12) { +# &ipv4check($hl + 4); +# } +# } +} + +sub ipv4check { + local($base) = $_[0]; + $hl = $bytes[$base] / 256; + if (($hl >> 4) == 6) { + &ipv6check($_[0]); + } + return if (($hl >> 4) != 4); # IPv4 ? + $hl &= 0xf; + $hl <<= 1; # get the header length in 16bit words + + $hs = &dosum(0, $base, $base + $hl); + $osum = $bytes[$base + 5]; + + if ($hs != 0) { + $bytes[$base + 5] = 0; + $hs2 = &dosum(0, $base, $base + $hl); + $bytes[$base + 5] = $osum; + printf " IPv4: ($hl,%x) %x != %x", $hs, $osum, $hs2; + } else { + print " IPv4($base): ok "; + } + + # + # Recognise TCP & UDP and calculate checksums for each of these. + # + if (($bytes[$base + 4] & 0xff) == 4) { + &ipv4check($hl); + } + if (($bytes[$base + 4] & 0xff) == 6) { + &tcpcheck($base); + } + + if (($bytes[$base + 4] & 0xff) == 17) { + &udpcheck($base); + } + + if (($bytes[$base + 4] & 0xff) == 1) { + &icmpcheck($base); + } + if ($base == 0) { + print "\n"; + } +} + +sub tcpcheck { + local($base) = $_[0]; + local($hl) = $bytes[$base] / 256; + return if (($hl >> 4) != 4); + if ($bytes[$base + 3] & 0x3fff) { + print " TCP: fragment"; + return; + } + $hl &= 0xf; + $hl <<= 1; + + local($hs) = 6; # TCP + local($len) = $bytes[$base + 1] - ($hl << 1); + $hs += $len; + $hs += &ipv4addrsum($base); + + &tcpcommon($base, $hl, $hs, 1); +} + +sub udpcheck { + local($base) = $_[0]; + local($hl) = $bytes[0] / 256; + return if (($hl >> 4) != 4); + if ($bytes[$base + 3] & 0x3fff) { + print " UDP: fragment"; + return; + } + $hl &= 0xf; + $hl <<= 1; + + local($hs) = 17; # UDP + local($len) = $bytes[$base + 1] - ($hl << 1); + $hs += $len; + $hs += &ipv4addrsum($base); + local($udpsum) = $hs; + &udpcommon($base, $hl, $hs); +} + sub icmpcheck { local($base) = $_[0]; local($hl) = $bytes[$base + 0] / 256; diff --git a/contrib/ipfilter/todo b/contrib/ipfilter/todo index 5b2c0590521..3f558d1f603 100644 --- a/contrib/ipfilter/todo +++ b/contrib/ipfilter/todo @@ -21,14 +21,6 @@ time permitting: * record buffering for TCP/UDP -* modular application proxying --done - -* allow multiple ip addresses in a source route list for ipsend - -* port IP Filter to Linux -Not in this century. - * document bimap * document NAT rule order processing @@ -56,14 +48,14 @@ I would also love to see a more extensive NAT. It can choose to do rdr and map based on saddr, daddr, sport and dport. (Does the kernel module already have functionality for that and it just needs support in the userland ipnat?) --sort of done +-done - * intrusion detection - detection of port scans + * intrusion detection + detection of port scans detection of multiple connection attempts - + * support for multiple log files - i.e. all connections to ftp and telnet logged to + i.e. all connections to ftp and telnet logged to a seperate log file * multiple levels of log severity with E-mail notification diff --git a/contrib/ipfilter/tools/BNF.ipf b/contrib/ipfilter/tools/BNF.ipf index 0e8433291d1..0740c5855af 100644 --- a/contrib/ipfilter/tools/BNF.ipf +++ b/contrib/ipfilter/tools/BNF.ipf @@ -66,7 +66,7 @@ facility = "kern" | "user" | "mail" | "daemon" | "auth" | "syslog" | "audit" | "logalert" | "local0" | "local1" | "local2" | "local3" | "local4" | "local5" | "local6" | "local7" . priority = "emerg" | "alert" | "crit" | "err" | "warn" | "notice" | - "info" | "debug" . + "info" | "debug" . hexnumber = "0" "x" hexstring . hexstring = hexdigit [ hexstring ] . diff --git a/contrib/ipfilter/tools/Makefile b/contrib/ipfilter/tools/Makefile index 43ec1a897b8..ce1ab0e6fc2 100644 --- a/contrib/ipfilter/tools/Makefile +++ b/contrib/ipfilter/tools/Makefile @@ -1,8 +1,5 @@ -# -# Copyright (C) 1993-2001 by Darren Reed. -# -# See the IPFILTER.LICENCE file for details on licencing. -# +YACC=yacc -v + DEST=. all: $(DEST)/ipf_y.c $(DEST)/ipf_y.h $(DEST)/ipf_l.c \ @@ -16,7 +13,7 @@ all: $(DEST)/ipf_y.c $(DEST)/ipf_y.h $(DEST)/ipf_l.c \ $(DEST)/ipf_y.h: $(DEST)/ipf_y.c $(DEST)/ipf_y.c: ipf_y.y - yacc -d ipf_y.y + $(YACC) -d ipf_y.y sed -e 's/yy/ipf_yy/g' -e 's/y.tab.h/ipf_y.c/' \ -e 's/"ipf_y.y"/"..\/tools\/ipf_y.y"/' \ y.tab.c > $(DEST)/ipf_y.c @@ -30,7 +27,7 @@ $(DEST)/ipf_l.c: lexer.c $(DEST)/ipmon_y.n: $(DEST)/ipmon_y.c $(DEST)/ipmon_y.c $(DEST)/ipmon_y.h: ipmon_y.y - yacc -d ipmon_y.y + $(YACC) -d ipmon_y.y sed -e 's/yy/ipmon_yy/g' -e 's/"ipmon_y.y"/"..\/tools\/ipmon_y.y"/' \ y.tab.c > $(DEST)/ipmon_y.c sed -e 's/yy/ipmon_yy/g' y.tab.h > $(DEST)/ipmon_y.h @@ -43,7 +40,7 @@ $(DEST)/ipmon_l.c: lexer.c $(DEST)/ipscan_y.h: $(DEST)/ipscan_y.c $(DEST)/ipscan_y.c $(DEST)/ipscan_y.h: ipscan_y.y - yacc -d ipscan_y.y + $(YACC) -d ipscan_y.y sed -e 's/yy/ipscan_yy/g' \ -e 's/"ipscan_y.y"/"..\/tools\/ipscan_y.y"/' \ y.tab.c > $(DEST)/ipscan_y.c @@ -57,7 +54,7 @@ $(DEST)/ipscan_l.c: lexer.c $(DEST)/ippool_y.h: $(DEST)/ippool_y.c $(DEST)/ippool_y.c $(DEST)/ippool_y.h: ippool_y.y - yacc -d ippool_y.y + $(YACC) -d ippool_y.y sed -e 's/yy/ippool_yy/g' -e 's/"ippool_y.y"/"..\/tools\/ippool_y.y"/' \ y.tab.c > $(DEST)/ippool_y.c sed -e 's/yy/ippool_yy/g' y.tab.h > $(DEST)/ippool_y.h @@ -70,7 +67,7 @@ $(DEST)/ippool_l.c: lexer.c $(DEST)/ipnat_y.h: $(DEST)/ipnat_y.c $(DEST)/ipnat_y.c $(DEST)/ipnat_y.h: ipnat_y.y - yacc -d ipnat_y.y + $(YACC) -d ipnat_y.y sed -e 's/yy/ipnat_yy/g' -e 's/y.tab.c/ipnat_y.c/' \ -e s/\"ipnat_y.y\"/\"..\\/tools\\/ipnat_y.y\"/ \ y.tab.c > $(DEST)/ipnat_y.c diff --git a/contrib/ipfilter/tools/ipf.c b/contrib/ipfilter/tools/ipf.c index fe9fec2581e..dd601426deb 100644 --- a/contrib/ipfilter/tools/ipf.c +++ b/contrib/ipfilter/tools/ipf.c @@ -1,7 +1,7 @@ /* $FreeBSD$ */ /* - * Copyright (C) 2001-2006 by Darren Reed. + * Copyright (C) 2012 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. */ @@ -16,12 +16,13 @@ #endif #include "ipf.h" #include +#include #include #include "netinet/ipl.h" #if !defined(lint) static const char sccsid[] = "@(#)ipf.c 1.23 6/5/96 (C) 1993-2000 Darren Reed"; -static const char rcsid[] = "@(#)$Id: ipf.c,v 1.35.2.8 2007/05/10 06:12:01 darrenr Exp $"; +static const char rcsid[] = "@(#)$Id$"; #endif #if !defined(__SVR4) && defined(__GNUC__) @@ -40,23 +41,30 @@ int main __P((int, char *[])); int opts = 0; int outputc = 0; int use_inet6 = 0; +int exitstatus = 0; -static void procfile __P((char *, char *)), flushfilter __P((char *)); -static void set_state __P((u_int)), showstats __P((friostat_t *)); -static void packetlogon __P((char *)), swapactive __P((void)); +static void procfile __P((char *)); +static void flushfilter __P((char *, int *)); +static void set_state __P((u_int)); +static void showstats __P((friostat_t *)); +static void packetlogon __P((char *)); +static void swapactive __P((void)); static int opendevice __P((char *, int)); static void closedevice __P((void)); static char *ipfname = IPL_NAME; static void usage __P((void)); static int showversion __P((void)); static int get_flags __P((void)); -static void ipf_interceptadd __P((int, ioctlfunc_t, void *)); +static int ipf_interceptadd __P((int, ioctlfunc_t, void *)); static int fd = -1; static ioctlfunc_t iocfunctions[IPL_LOGSIZE] = { ioctl, ioctl, ioctl, ioctl, ioctl, ioctl, ioctl, ioctl }; +/* XXX The following was added to satisfy a rescue/rescue/ build + XXX requirement. */ +int nohdrfields; static void usage() { @@ -68,25 +76,28 @@ static void usage() int main(argc,argv) -int argc; -char *argv[]; + int argc; + char *argv[]; { - int c; + int c, *filter = NULL; if (argc < 2) usage(); - while ((c = getopt(argc, argv, "6Ac:dDEf:F:Il:noPrRsT:vVyzZ")) != -1) { + assigndefined(getenv("IPF_PREDEFINED")); + + while ((c = getopt(argc, argv, "46Ac:dDEf:F:Il:m:noPrRsT:vVyzZ")) != -1) { switch (c) { case '?' : usage(); break; -#ifdef USE_INET6 + case '4' : + use_inet6 = -1; + break; case '6' : use_inet6 = 1; break; -#endif case 'A' : opts &= ~OPT_INACTIVE; break; @@ -104,10 +115,10 @@ char *argv[]; opts ^= OPT_DEBUG; break; case 'f' : - procfile(argv[0], optarg); + procfile(optarg); break; case 'F' : - flushfilter(optarg); + flushfilter(optarg, filter); break; case 'I' : opts ^= OPT_INACTIVE; @@ -115,8 +126,11 @@ char *argv[]; case 'l' : packetlogon(optarg); break; + case 'm' : + filter = parseipfexpr(optarg, NULL); + break; case 'n' : - opts ^= OPT_DONOTHING; + opts ^= OPT_DONOTHING|OPT_DONTOPEN; break; case 'o' : break; @@ -161,14 +175,14 @@ char *argv[]; if (fd != -1) (void) close(fd); - return(0); + return(exitstatus); /* NOTREACHED */ } static int opendevice(ipfdev, check) -char *ipfdev; -int check; + char *ipfdev; + int check; { if (opts & OPT_DONOTHING) return -2; @@ -184,7 +198,7 @@ int check; if (fd == -1) if ((fd = open(ipfdev, O_RDWR)) == -1) if ((fd = open(ipfdev, O_RDONLY)) == -1) - perror("open device"); + ipferror(fd, "open device"); return fd; } @@ -202,7 +216,7 @@ static int get_flags() if ((opendevice(ipfname, 1) != -2) && (ioctl(fd, SIOCGETFF, &i) == -1)) { - perror("SIOCGETFF"); + ipferror(fd, "SIOCGETFF"); return 0; } return i; @@ -210,22 +224,24 @@ static int get_flags() static void set_state(enable) -u_int enable; + u_int enable; { - if (opendevice(ipfname, 0) != -2) + if (opendevice(ipfname, 0) != -2) { if (ioctl(fd, SIOCFRENB, &enable) == -1) { - if (errno == EBUSY) + if (errno == EBUSY) { fprintf(stderr, "IP FIlter: already initialized\n"); - else - perror("SIOCFRENB"); + } else { + ipferror(fd, "SIOCFRENB"); + } } + } return; } -static void procfile(name, file) -char *name, *file; +static void procfile(file) + char *file; { (void) opendevice(ipfname, 1); @@ -241,20 +257,22 @@ char *name, *file; } -static void ipf_interceptadd(fd, ioctlfunc, ptr) -int fd; -ioctlfunc_t ioctlfunc; -void *ptr; +static int ipf_interceptadd(fd, ioctlfunc, ptr) + int fd; + ioctlfunc_t ioctlfunc; + void *ptr; { if (outputc) printc(ptr); - ipf_addrule(fd, ioctlfunc, ptr); + if (ipf_addrule(fd, ioctlfunc, ptr) != 0) + exitstatus = 1; + return 0; } static void packetlogon(opt) -char *opt; + char *opt; { int flag, xfd, logopt, change = 0; @@ -293,7 +311,7 @@ char *opt; if (change == 1) { if (opendevice(ipfname, 1) != -2 && (ioctl(fd, SIOCSETFF, &flag) != 0)) - perror("ioctl(SIOCSETFF)"); + ipferror(fd, "ioctl(SIOCSETFF)"); } if ((opts & (OPT_DONOTHING|OPT_VERBOSE)) == OPT_VERBOSE) { @@ -308,11 +326,11 @@ char *opt; if (xfd >= 0) { logopt = 0; if (ioctl(xfd, SIOCGETLG, &logopt)) - perror("ioctl(SIOCGETLG)"); + ipferror(fd, "ioctl(SIOCGETLG)"); else { logopt = 1 - logopt; if (ioctl(xfd, SIOCSETLG, &logopt)) - perror("ioctl(SIOCSETLG)"); + ipferror(xfd, "ioctl(SIOCSETLG)"); } close(xfd); } @@ -325,11 +343,11 @@ char *opt; if (xfd >= 0) { logopt = 0; if (ioctl(xfd, SIOCGETLG, &logopt)) - perror("ioctl(SIOCGETLG)"); + ipferror(xfd, "ioctl(SIOCGETLG)"); else { logopt = 1 - logopt; if (ioctl(xfd, SIOCSETLG, &logopt)) - perror("ioctl(SIOCSETLG)"); + ipferror(xfd, "ioctl(SIOCSETLG)"); } close(xfd); } @@ -337,8 +355,9 @@ char *opt; } -static void flushfilter(arg) -char *arg; +static void flushfilter(arg, filter) + char *arg; + int *filter; { int fl = 0, rem; @@ -359,20 +378,33 @@ char *arg; if (!(opts & OPT_DONOTHING)) { if (use_inet6) { - if (ioctl(fd, SIOCIPFL6, &fl) == -1) { - perror("ioctl(SIOCIPFL6)"); - exit(1); + fprintf(stderr, + "IPv6 rules are no longer seperate\n"); + } else if (filter != NULL) { + ipfobj_t obj; + + obj.ipfo_rev = IPFILTER_VERSION; + obj.ipfo_size = filter[0] * sizeof(int); + obj.ipfo_type = IPFOBJ_IPFEXPR; + obj.ipfo_ptr = filter; + if (ioctl(fd, SIOCMATCHFLUSH, &obj) == -1) { + ipferror(fd, "ioctl(SIOCMATCHFLUSH)"); + fl = -1; + } else { + fl = obj.ipfo_retval; } } else { if (ioctl(fd, SIOCIPFFL, &fl) == -1) { - perror("ioctl(SIOCIPFFL)"); + ipferror(fd, "ioctl(SIOCIPFFL)"); exit(1); } } } - if ((opts & (OPT_DONOTHING|OPT_VERBOSE)) == OPT_VERBOSE) { + if ((opts & (OPT_DONOTHING|OPT_DEBUG)) == OPT_DEBUG) { printf("remove flags %s (%d)\n", arg, rem); - printf("removed %d entries\n", fl); + } + if ((opts & (OPT_DONOTHING|OPT_VERBOSE)) == OPT_VERBOSE) { + printf("%d state entries removed\n", fl); } closedevice(); return; @@ -388,7 +420,7 @@ char *arg; perror("open(IPL_AUTH)"); else { if (ioctl(fd, SIOCIPFFA, &fl) == -1) - perror("ioctl(SIOCIPFFA)"); + ipferror(fd, "ioctl(SIOCIPFFA)"); } closedevice(); return; @@ -411,21 +443,23 @@ char *arg; if (!(opts & OPT_DONOTHING)) { if (use_inet6) { if (ioctl(fd, SIOCIPFL6, &fl) == -1) { - perror("ioctl(SIOCIPFL6)"); + ipferror(fd, "ioctl(SIOCIPFL6)"); exit(1); } } else { if (ioctl(fd, SIOCIPFFL, &fl) == -1) { - perror("ioctl(SIOCIPFFL)"); + ipferror(fd, "ioctl(SIOCIPFFL)"); exit(1); } } } - if ((opts & (OPT_DONOTHING|OPT_VERBOSE)) == OPT_VERBOSE) { + if ((opts & (OPT_DONOTHING|OPT_DEBUG)) == OPT_DEBUG) { printf("remove flags %s%s (%d)\n", (rem & FR_INQUE) ? "I" : "", (rem & FR_OUTQUE) ? "O" : "", rem); - printf("removed %d filter rules\n", fl); + } + if ((opts & (OPT_DONOTHING|OPT_VERBOSE)) == OPT_VERBOSE) { + printf("%d filter rules removed\n", fl); } return; } @@ -436,7 +470,7 @@ static void swapactive() int in = 2; if (opendevice(ipfname, 1) != -2 && ioctl(fd, SIOCSWAPA, &in) == -1) - perror("ioctl(SIOCSWAPA)"); + ipferror(fd, "ioctl(SIOCSWAPA)"); else printf("Set %d now inactive\n", in); } @@ -447,7 +481,7 @@ void ipf_frsync() int frsyn = 0; if (opendevice(ipfname, 1) != -2 && ioctl(fd, SIOCFRSYN, &frsyn) == -1) - perror("SIOCFRSYN"); + ipferror(fd, "SIOCFRSYN"); else printf("filter sync'd\n"); } @@ -466,7 +500,7 @@ void zerostats() if (opendevice(ipfname, 1) != -2) { if (ioctl(fd, SIOCFRZST, &obj) == -1) { - perror("ioctl(SIOCFRZST)"); + ipferror(fd, "ioctl(SIOCFRZST)"); exit(-1); } showstats(&fio); @@ -479,7 +513,7 @@ void zerostats() * read the kernel stats for packets blocked and passed */ static void showstats(fp) -friostat_t *fp; + friostat_t *fp; { printf("bad packets:\t\tin %lu\tout %lu\n", fp->f_st[0].fr_bad, fp->f_st[1].fr_bad); @@ -495,9 +529,6 @@ friostat_t *fp; fp->f_st[0].fr_bpkl, fp->f_st[0].fr_ppkl); printf("output packets logged:\tblocked %lu passed %lu\n", fp->f_st[1].fr_bpkl, fp->f_st[1].fr_ppkl); - printf(" packets logged:\tinput %lu-%lu output %lu-%lu\n", - fp->f_st[0].fr_pkl, fp->f_st[0].fr_skip, - fp->f_st[1].fr_pkl, fp->f_st[1].fr_skip); } @@ -523,7 +554,7 @@ static int showversion() } if (ioctl(vfd, SIOCGETFS, &ipfo)) { - perror("ioctl(SIOCGETFS)"); + ipferror(vfd, "ioctl(SIOCGETFS)"); close(vfd); return 1; } diff --git a/contrib/ipfilter/tools/ipf_y.y b/contrib/ipfilter/tools/ipf_y.y index 82307dec032..822e9a5ab0c 100644 --- a/contrib/ipfilter/tools/ipf_y.y +++ b/contrib/ipfilter/tools/ipf_y.y @@ -1,7 +1,7 @@ /* $FreeBSD$ */ /* - * Copyright (C) 2001-2006 by Darren Reed. + * Copyright (C) 2012 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. */ @@ -28,18 +28,29 @@ extern int yydebug; extern FILE *yyin; extern int yylineNum; -static void newrule __P((void)); -static void setipftype __P((void)); -static u_32_t lookuphost __P((char *)); +static int addname __P((frentry_t **, char *)); +static frentry_t *addrule __P((void)); +static frentry_t *allocfr __P((void)); +static void build_dstaddr_af __P((frentry_t *, void *)); +static void build_srcaddr_af __P((frentry_t *, void *)); static void dobpf __P((int, char *)); -static void resetaddr __P((void)); -static struct alist_s *newalist __P((struct alist_s *)); +static void doipfexpr __P((char *)); +static void do_tuneint __P((char *, int)); +static void do_tunestr __P((char *, char *)); +static void fillgroup __P((frentry_t *)); +static int lookuphost __P((char *, i6addr_t *)); static u_int makehash __P((struct alist_s *)); static int makepool __P((struct alist_s *)); -static frentry_t *addrule __P((void)); +static struct alist_s *newalist __P((struct alist_s *)); +static void newrule __P((void)); +static void resetaddr __P((void)); +static void setgroup __P((frentry_t **, char *)); +static void setgrhead __P((frentry_t **, char *)); +static void seticmphead __P((frentry_t **, char *)); +static void setifname __P((frentry_t **, int, char *)); +static void setipftype __P((void)); static void setsyslog __P((void)); static void unsetsyslog __P((void)); -static void fillgroup __P((frentry_t *)); frentry_t *fr = NULL, *frc = NULL, *frtop = NULL, *frold = NULL; @@ -52,52 +63,54 @@ static int nrules = 0; static int newlist = 0; static int added = 0; static int ipffd = -1; -static int *yycont = 0; -static ioctlfunc_t ipfioctl[IPL_LOGSIZE]; +static int *yycont = NULL; +static ioctlfunc_t ipfioctls[IPL_LOGSIZE]; static addfunc_t ipfaddfunc = NULL; -static struct wordtab ipfwords[95]; -static struct wordtab addrwords[4]; -static struct wordtab maskwords[5]; -static struct wordtab icmpcodewords[17]; -static struct wordtab icmptypewords[16]; -static struct wordtab ipv4optwords[25]; -static struct wordtab ipv4secwords[9]; -static struct wordtab ipv6optwords[9]; -static struct wordtab logwords[33]; %} %union { char *str; u_32_t num; - struct in_addr ipa; frentry_t fr; frtuc_t *frt; struct alist_s *alist; u_short port; + struct in_addr ip4; struct { u_short p1; u_short p2; int pc; } pc; - struct { + struct ipp_s { + int type; + int ifpos; + int f; + int v; + int lif; union i6addr a; union i6addr m; + char *name; } ipp; - union i6addr ip6; + struct { + i6addr_t adr; + int f; + } adr; + i6addr_t ip6; struct { char *if1; char *if2; } ifs; + char gname[FR_GROUPLEN]; }; %type portnum %type facility priority icmpcode seclevel secname icmptype %type opt compare range opttype flagset optlist ipv6hdrlist ipv6hdr -%type portc porteq -%type hostname ipv4 ipv4mask ipv4_16 ipv4_24 -%type ipv6mask +%type portc porteq ipmask maskopts +%type ipv4 ipv4_16 ipv4_24 +%type hostname %type addr ipaddr -%type servicename name interfacename +%type servicename name interfacename groupname %type portrange portcomp %type addrlist poollist %type onname @@ -109,30 +122,32 @@ static struct wordtab logwords[33]; %token YY_RANGE_OUT YY_RANGE_IN %token YY_IPV6 +%token IPFY_SET %token IPFY_PASS IPFY_BLOCK IPFY_COUNT IPFY_CALL IPFY_NOMATCH %token IPFY_RETICMP IPFY_RETRST IPFY_RETICMPASDST %token IPFY_IN IPFY_OUT %token IPFY_QUICK IPFY_ON IPFY_OUTVIA IPFY_INVIA %token IPFY_DUPTO IPFY_TO IPFY_FROUTE IPFY_REPLY_TO IPFY_ROUTETO -%token IPFY_TOS IPFY_TTL IPFY_PROTO +%token IPFY_TOS IPFY_TTL IPFY_PROTO IPFY_INET IPFY_INET6 %token IPFY_HEAD IPFY_GROUP %token IPFY_AUTH IPFY_PREAUTH -%token IPFY_LOG IPFY_BODY IPFY_FIRST IPFY_LEVEL IPFY_ORBLOCK -%token IPFY_LOGTAG IPFY_MATCHTAG IPFY_SETTAG IPFY_SKIP +%token IPFY_LOG IPFY_BODY IPFY_FIRST IPFY_LEVEL IPFY_ORBLOCK IPFY_L5AS +%token IPFY_LOGTAG IPFY_MATCHTAG IPFY_SETTAG IPFY_SKIP IPFY_DECAPS %token IPFY_FROM IPFY_ALL IPFY_ANY IPFY_BPFV4 IPFY_BPFV6 IPFY_POOL IPFY_HASH -%token IPFY_PPS +%token IPFY_IPFEXPR IPFY_PPS IPFY_FAMILY IPFY_DSTLIST %token IPFY_ESP IPFY_AH %token IPFY_WITH IPFY_AND IPFY_NOT IPFY_NO IPFY_OPT %token IPFY_TCPUDP IPFY_TCP IPFY_UDP %token IPFY_FLAGS IPFY_MULTICAST %token IPFY_MASK IPFY_BROADCAST IPFY_NETWORK IPFY_NETMASKED IPFY_PEER -%token IPFY_PORT -%token IPFY_NOW +%token IPFY_RPC IPFY_PORT +%token IPFY_NOW IPFY_COMMENT IPFY_RULETTL %token IPFY_ICMP IPFY_ICMPTYPE IPFY_ICMPCODE %token IPFY_IPOPTS IPFY_SHORT IPFY_NAT IPFY_BADSRC IPFY_LOWTTL IPFY_FRAG %token IPFY_MBCAST IPFY_BAD IPFY_BADNAT IPFY_OOW IPFY_NEWISN IPFY_NOICMPERR %token IPFY_KEEP IPFY_STATE IPFY_FRAGS IPFY_LIMIT IPFY_STRICT IPFY_AGE -%token IPFY_SYNC IPFY_FRAGBODY +%token IPFY_SYNC IPFY_FRAGBODY IPFY_ICMPHEAD IPFY_NOLOG IPFY_LOOSE +%token IPFY_MAX_SRCS IPFY_MAX_PER_SRC %token IPFY_IPOPT_NOP IPFY_IPOPT_RR IPFY_IPOPT_ZSU IPFY_IPOPT_MTUP %token IPFY_IPOPT_MTUR IPFY_IPOPT_ENCODE IPFY_IPOPT_TS IPFY_IPOPT_TR %token IPFY_IPOPT_SEC IPFY_IPOPT_LSRR IPFY_IPOPT_ESEC IPFY_IPOPT_CIPSO @@ -140,10 +155,10 @@ static struct wordtab logwords[33]; %token IPFY_IPOPT_IMITD IPFY_IPOPT_EIP IPFY_IPOPT_FINN IPFY_IPOPT_DPS %token IPFY_IPOPT_SDB IPFY_IPOPT_NSAPA IPFY_IPOPT_RTRALRT IPFY_IPOPT_UMP %token IPFY_SECCLASS IPFY_SEC_UNC IPFY_SEC_CONF IPFY_SEC_RSV1 IPFY_SEC_RSV2 -%token IPFY_SEC_RSV4 IPFY_SEC_SEC IPFY_SEC_TS IPFY_SEC_RSV3 +%token IPFY_SEC_RSV4 IPFY_SEC_SEC IPFY_SEC_TS IPFY_SEC_RSV3 IPFY_DOI -%token IPF6_V6HDRS IPFY_IPV6OPT IPFY_IPV6OPT_DSTOPTS IPFY_IPV6OPT_HOPOPTS -%token IPFY_IPV6OPT_IPV6 IPFY_IPV6OPT_NONE IPFY_IPV6OPT_ROUTING +%token IPFY_V6HDRS IPFY_IPV6OPT IPFY_IPV6OPT_DSTOPTS IPFY_IPV6OPT_HOPOPTS +%token IPFY_IPV6OPT_IPV6 IPFY_IPV6OPT_NONE IPFY_IPV6OPT_ROUTING IPFY_V6HDR %token IPFY_IPV6OPT_MOBILITY IPFY_IPV6OPT_ESP IPFY_IPV6OPT_FRAG %token IPFY_ICMPT_UNR IPFY_ICMPT_ECHO IPFY_ICMPT_ECHOR IPFY_ICMPT_SQUENCH @@ -168,16 +183,36 @@ static struct wordtab logwords[33]; %token IPFY_PRI_EMERG IPFY_PRI_ALERT IPFY_PRI_CRIT IPFY_PRI_ERR IPFY_PRI_WARN %token IPFY_PRI_NOTICE IPFY_PRI_INFO IPFY_PRI_DEBUG %% -file: line +file: settings rules + | rules + ; + +settings: + YY_COMMENT + | setting + | settings setting + ; + +rules: line | assign - | file line - | file assign + | rules line + | rules assign + ; + +setting: + IPFY_SET YY_STR YY_NUMBER ';' { do_tuneint($2, $3); } + | IPFY_SET YY_STR YY_HEX ';' { do_tuneint($2, $3); } + | IPFY_SET YY_STR YY_STR ';' { do_tunestr($2, $3); } ; line: rule { while ((fr = frtop) != NULL) { frtop = fr->fr_next; fr->fr_next = NULL; - (*ipfaddfunc)(ipffd, ipfioctl[IPL_LOGIPF], fr); + if ((fr->fr_type == FR_T_IPF) && + (fr->fr_ip.fi_v == 0)) + fr->fr_mip.fi_v = 0; + /* XXX validate ? */ + (*ipfaddfunc)(ipffd, ipfioctls[IPL_LOGIPF], fr); fr->fr_next = frold; frold = fr; } @@ -231,10 +266,37 @@ markout: rulemain: ipfrule | bpfrule + | exprrule ; ipfrule: - tos ttl proto ip + family tos ttl proto ip + ; + +family: | IPFY_FAMILY IPFY_INET { if (use_inet6 == 1) { + YYERROR; + } else { + frc->fr_family = AF_INET; + } + } + | IPFY_INET { if (use_inet6 == 1) { + YYERROR; + } else { + frc->fr_family = AF_INET; + } + } + | IPFY_FAMILY IPFY_INET6 { if (use_inet6 == -1) { + YYERROR; + } else { + frc->fr_family = AF_INET6; + } + } + | IPFY_INET6 { if (use_inet6 == -1) { + YYERROR; + } else { + frc->fr_family = AF_INET6; + } + } ; bpfrule: @@ -242,12 +304,16 @@ bpfrule: | IPFY_BPFV6 '{' YY_STR '}' { dobpf(6, $3); free($3); } ; +exprrule: + IPFY_IPFEXPR '{' YY_STR '}' { doipfexpr($3); } + ; + ruletail: with keep head group ; ruletail2: - pps age new + pps age new rulettl comment ; intag: settagin matchtagin @@ -269,6 +335,7 @@ action: block | IPFY_NOMATCH { fr->fr_flags |= FR_NOMATCH; } | log | IPFY_COUNT { fr->fr_flags |= FR_ACCOUNT; } + | decaps { fr->fr_flags |= FR_DECAPSULATE; } | auth | IPFY_SKIP YY_NUMBER { fr->fr_flags |= FR_SKIP; fr->fr_arg = $2; } @@ -291,6 +358,11 @@ blockreturn: | IPFY_RETRST { fr->fr_flags |= FR_RETRST; } ; +decaps: IPFY_DECAPS + | IPFY_DECAPS IPFY_L5AS '(' YY_STR ')' + { fr->fr_icode = atoi($4); } + ; + log: IPFY_LOG { fr->fr_flags |= FR_LOG; } | IPFY_LOG logoptions { fr->fr_flags |= FR_LOG; } ; @@ -300,10 +372,11 @@ auth: IPFY_AUTH { fr->fr_flags |= FR_AUTH; } | IPFY_PREAUTH { fr->fr_flags |= FR_PREAUTH; } ; -func: YY_STR '/' YY_NUMBER { fr->fr_func = nametokva($1, - ipfioctl[IPL_LOGIPF]); - fr->fr_arg = $3; - free($1); } +func: YY_STR '/' YY_NUMBER + { fr->fr_func = nametokva($1, ipfioctls[IPL_LOGIPF]); + fr->fr_arg = $3; + free($1); + } ; inopts: @@ -330,6 +403,7 @@ outopt: | on | dup | proute + | froute | replyto ; @@ -346,7 +420,7 @@ toslist: | YY_HEX { DOREM(fr->fr_tos = $1; fr->fr_mtos = 0xff;) } | toslist lmore YY_NUMBER { DOREM(fr->fr_tos = $3; fr->fr_mtos = 0xff;) } - | toslist lmore YY_HEX + | toslist lmore YY_HEX { DOREM(fr->fr_tos = $3; fr->fr_mtos = 0xff;) } ; @@ -355,10 +429,10 @@ ttl: | setttl YY_NUMBER | setttl lstart ttllist lend ; -lstart: '(' { newlist = 1; fr = frc; added = 0; } +lstart: '{' { newlist = 1; fr = frc; added = 0; } ; -lend: ')' { nrules += added; } +lend: '}' { nrules += added; } ; lmore: lanother { if (newlist == 1) { @@ -394,20 +468,25 @@ protox: IPFY_PROTO { setipftype(); ip: srcdst flags icmp ; -group: | IPFY_GROUP YY_STR { DOALL(strncpy(fr->fr_group, $2, \ - FR_GROUPLEN); \ - fillgroup(fr);); - free($2); } - | IPFY_GROUP YY_NUMBER { DOALL(sprintf(fr->fr_group, "%d", \ - $2); \ - fillgroup(fr);) } +group: | IPFY_GROUP groupname { DOALL(setgroup(&fr, $2); \ + fillgroup(fr);); + free($2); + } ; -head: | IPFY_HEAD YY_STR { DOALL(strncpy(fr->fr_grhead, $2, \ - FR_GROUPLEN);); - free($2); } - | IPFY_HEAD YY_NUMBER { DOALL(sprintf(fr->fr_grhead, "%d", \ - $2);) } +head: | IPFY_HEAD groupname { DOALL(setgrhead(&fr, $2);); + free($2); + } + ; + +groupname: + YY_STR { $$ = $1; + if (strlen($$) >= FR_GROUPLEN) + $$[FR_GROUPLEN - 1] = '\0'; + } + | YY_NUMBER { $$ = malloc(16); + sprintf($$, "%d", $1); + } ; settagin: @@ -461,6 +540,15 @@ pps: | IPFY_PPS YY_NUMBER { DOALL(fr->fr_pps = $2;) } new: | savegroup file restoregroup ; +rulettl: + | IPFY_RULETTL YY_NUMBER { DOALL(fr->fr_die = $2;) } + ; + +comment: + | IPFY_COMMENT YY_STR { DOALL(fr->fr_comment = addname(&fr, \ + $2);) } + ; + savegroup: '{' ; @@ -472,76 +560,92 @@ restoregroup: logopt: log ; -quick: - IPFY_QUICK { fr->fr_flags |= FR_QUICK; } +quick: IPFY_QUICK { fr->fr_flags |= FR_QUICK; } ; -on: IPFY_ON onname +on: IPFY_ON onname { setifname(&fr, 0, $2.if1); + free($2.if1); + if ($2.if2 != NULL) { + setifname(&fr, 1, + $2.if2); + free($2.if2); + } + } | IPFY_ON lstart onlist lend - | IPFY_ON onname IPFY_INVIA vianame - | IPFY_ON onname IPFY_OUTVIA vianame + | IPFY_ON onname IPFY_INVIA vianame { setifname(&fr, 0, $2.if1); + free($2.if1); + if ($2.if2 != NULL) { + setifname(&fr, 1, + $2.if2); + free($2.if2); + } + } + | IPFY_ON onname IPFY_OUTVIA vianame { setifname(&fr, 0, $2.if1); + free($2.if1); + if ($2.if2 != NULL) { + setifname(&fr, 1, + $2.if2); + free($2.if2); + } + } ; -onlist: onname { DOREM(strncpy(fr->fr_ifnames[0], $1.if1, \ - sizeof(fr->fr_ifnames[0])); \ - if ($1.if2 != NULL) { \ - strncpy(fr->fr_ifnames[1], \ - $1.if2, \ - sizeof(fr->fr_ifnames[1]));\ - } \ - ) } - | onlist lmore onname { DOREM(strncpy(fr->fr_ifnames[0], $3.if1, \ - sizeof(fr->fr_ifnames[0])); \ - if ($3.if2 != NULL) { \ - strncpy(fr->fr_ifnames[1], \ - $3.if2, \ - sizeof(fr->fr_ifnames[1]));\ - } \ - ) } +onlist: onname { DOREM(setifname(&fr, 0, $1.if1); \ + if ($1.if2 != NULL) \ + setifname(&fr, 1, $1.if2); \ + ) + free($1.if1); + if ($1.if2 != NULL) + free($1.if2); + } + | onlist lmore onname { DOREM(setifname(&fr, 0, $3.if1); \ + if ($3.if2 != NULL) \ + setifname(&fr, 1, $3.if2); \ + ) + free($3.if1); + if ($3.if2 != NULL) + free($3.if2); + } ; -onname: interfacename - { strncpy(fr->fr_ifnames[0], $1, sizeof(fr->fr_ifnames[0])); - $$.if1 = fr->fr_ifnames[0]; - $$.if2 = NULL; - free($1); - } +onname: interfacename { $$.if1 = $1; + $$.if2 = NULL; + } | interfacename ',' interfacename - { strncpy(fr->fr_ifnames[0], $1, sizeof(fr->fr_ifnames[0])); - $$.if1 = fr->fr_ifnames[0]; - free($1); - strncpy(fr->fr_ifnames[1], $3, sizeof(fr->fr_ifnames[1])); - $$.if1 = fr->fr_ifnames[1]; - free($3); - } + { $$.if1 = $1; + $$.if2 = $3; + } ; vianame: - name - { strncpy(fr->fr_ifnames[2], $1, sizeof(fr->fr_ifnames[2])); - free($1); - } - | name ',' name - { strncpy(fr->fr_ifnames[2], $1, sizeof(fr->fr_ifnames[2])); - free($1); - strncpy(fr->fr_ifnames[3], $3, sizeof(fr->fr_ifnames[3])); - free($3); - } + name { setifname(&fr, 2, $1); + free($1); + } + | name ',' name { setifname(&fr, 2, $1); + free($1); + setifname(&fr, 3, $3); + free($3); + } ; dup: IPFY_DUPTO name - { strncpy(fr->fr_dif.fd_ifname, $2, sizeof(fr->fr_dif.fd_ifname)); + { int idx = addname(&fr, $2); + fr->fr_dif.fd_name = idx; free($2); } + | IPFY_DUPTO IPFY_DSTLIST '/' name + { int idx = addname(&fr, $4); + fr->fr_dif.fd_name = idx; + fr->fr_dif.fd_type = FRD_DSTLIST; + free($4); + } | IPFY_DUPTO name duptoseparator hostname - { strncpy(fr->fr_dif.fd_ifname, $2, sizeof(fr->fr_dif.fd_ifname)); - fr->fr_dif.fd_ip = $4; - yyexpectaddr = 0; - free($2); - } - | IPFY_DUPTO name duptoseparator YY_IPV6 - { strncpy(fr->fr_dif.fd_ifname, $2, sizeof(fr->fr_dif.fd_ifname)); - bcopy(&$4, &fr->fr_dif.fd_ip6, sizeof(fr->fr_dif.fd_ip6)); + { int idx = addname(&fr, $2); + fr->fr_dif.fd_name = idx; + fr->fr_dif.fd_ptr = (void *)-1; + fr->fr_dif.fd_ip6 = $4.adr; + if (fr->fr_family == AF_UNSPEC && $4.f != AF_UNSPEC) + fr->fr_family = $4.f; yyexpectaddr = 0; free($2); } @@ -555,18 +659,23 @@ froute: IPFY_FROUTE { fr->fr_flags |= FR_FASTROUTE; } ; proute: routeto name - { strncpy(fr->fr_tif.fd_ifname, $2, sizeof(fr->fr_tif.fd_ifname)); + { int idx = addname(&fr, $2); + fr->fr_tif.fd_name = idx; free($2); } + | routeto IPFY_DSTLIST '/' name + { int idx = addname(&fr, $4); + fr->fr_tif.fd_name = idx; + fr->fr_tif.fd_type = FRD_DSTLIST; + free($4); + } | routeto name duptoseparator hostname - { strncpy(fr->fr_tif.fd_ifname, $2, sizeof(fr->fr_tif.fd_ifname)); - fr->fr_tif.fd_ip = $4; - yyexpectaddr = 0; - free($2); - } - | routeto name duptoseparator YY_IPV6 - { strncpy(fr->fr_tif.fd_ifname, $2, sizeof(fr->fr_tif.fd_ifname)); - bcopy(&$4, &fr->fr_tif.fd_ip6, sizeof(fr->fr_tif.fd_ip6)); + { int idx = addname(&fr, $2); + fr->fr_tif.fd_name = idx; + fr->fr_tif.fd_ptr = (void *)-1; + fr->fr_tif.fd_ip6 = $4.adr; + if (fr->fr_family == AF_UNSPEC && $4.f != AF_UNSPEC) + fr->fr_family = $4.f; yyexpectaddr = 0; free($2); } @@ -579,12 +688,22 @@ routeto: replyto: IPFY_REPLY_TO name - { strncpy(fr->fr_rif.fd_ifname, $2, sizeof(fr->fr_rif.fd_ifname)); + { int idx = addname(&fr, $2); + fr->fr_rif.fd_name = idx; free($2); } + | IPFY_REPLY_TO IPFY_DSTLIST '/' name + { fr->fr_rif.fd_name = addname(&fr, $4); + fr->fr_rif.fd_type = FRD_DSTLIST; + free($4); + } | IPFY_REPLY_TO name duptoseparator hostname - { strncpy(fr->fr_rif.fd_ifname, $2, sizeof(fr->fr_rif.fd_ifname)); - fr->fr_rif.fd_ip = $4; + { int idx = addname(&fr, $2); + fr->fr_rif.fd_name = idx; + fr->fr_rif.fd_ptr = (void *)-1; + fr->fr_rif.fd_ip6 = $4.adr; + if (fr->fr_family == AF_UNSPEC && $4.f != AF_UNSPEC) + fr->fr_family = $4.f; free($2); } ; @@ -614,27 +733,29 @@ srcdst: | IPFY_ALL ; protocol: - YY_NUMBER { DOREM(fr->fr_proto = $1; \ - fr->fr_mproto = 0xff;) } + YY_NUMBER { DOALL(fr->fr_proto = $1; \ + fr->fr_mproto = 0xff;) + } | YY_STR { if (!strcmp($1, "tcp-udp")) { - DOREM(fr->fr_flx |= FI_TCPUDP; \ + DOALL(fr->fr_flx |= FI_TCPUDP; \ fr->fr_mflx |= FI_TCPUDP;) } else { int p = getproto($1); if (p == -1) yyerror("protocol unknown"); - DOREM(fr->fr_proto = p; \ + DOALL(fr->fr_proto = p; \ fr->fr_mproto = 0xff;) } free($1); - } + } | YY_STR nextstring YY_STR { if (!strcmp($1, "tcp") && !strcmp($3, "udp")) { DOREM(fr->fr_flx |= FI_TCPUDP; \ fr->fr_mflx |= FI_TCPUDP;) - } else + } else { YYERROR; + } free($1); free($3); } @@ -667,7 +788,8 @@ to: IPFY_TO { if (fr == NULL) printf("set yyexpectaddr\n"); yycont = &yyexpectaddr; yysetdict(addrwords); - resetaddr(); } + resetaddr(); + } ; with: | andwith withlist @@ -678,7 +800,7 @@ andwith: | IPFY_AND { nowith = 0; setipftype(); } ; -flags: | startflags flagset +flags: | startflags flagset { DOALL(fr->fr_tcpf = $2; fr->fr_tcpfm = FR_TCPFMAX;) } | startflags flagset '/' flagset { DOALL(fr->fr_tcpf = $2; fr->fr_tcpfm = $4;) } @@ -717,35 +839,14 @@ srcobject: ; srcaddr: - addr { DOREM(bcopy(&($1.a), &fr->fr_ip.fi_src, sizeof($1.a)); \ - bcopy(&($1.m), &fr->fr_mip.fi_src, sizeof($1.m)); \ - if (dynamic != -1) { \ - fr->fr_satype = ifpflag; \ - fr->fr_ipf->fri_sifpidx = dynamic; \ - } else if (pooled || hashed) \ - fr->fr_satype = FRI_LOOKUP;) - } + addr { build_srcaddr_af(fr, &$1); } | lstart srcaddrlist lend ; srcaddrlist: - addr { DOREM(bcopy(&($1.a), &fr->fr_ip.fi_src, sizeof($1.a)); \ - bcopy(&($1.m), &fr->fr_mip.fi_src, sizeof($1.m)); \ - if (dynamic != -1) { \ - fr->fr_satype = ifpflag; \ - fr->fr_ipf->fri_sifpidx = dynamic; \ - } else if (pooled || hashed) \ - fr->fr_satype = FRI_LOOKUP;) - } + addr { build_srcaddr_af(fr, &$1); } | srcaddrlist lmore addr - { DOREM(bcopy(&($3.a), &fr->fr_ip.fi_src, sizeof($3.a)); \ - bcopy(&($3.m), &fr->fr_mip.fi_src, sizeof($3.m)); \ - if (dynamic != -1) { \ - fr->fr_satype = ifpflag; \ - fr->fr_ipf->fri_sifpidx = dynamic; \ - } else if (pooled || hashed) \ - fr->fr_satype = FRI_LOOKUP;) - } + { build_srcaddr_af(fr, &$3); } ; srcport: @@ -770,10 +871,10 @@ fromport: srcportlist: portnum { DOREM(fr->fr_scmp = FR_EQUAL; fr->fr_sport = $1;) } - | portnum ':' portnum + | portnum ':' portnum { DOREM(fr->fr_scmp = FR_INCRANGE; fr->fr_sport = $1; \ fr->fr_stop = $3;) } - | portnum YY_RANGE_IN portnum + | portnum YY_RANGE_IN portnum { DOREM(fr->fr_scmp = FR_INRANGE; fr->fr_sport = $1; \ fr->fr_stop = $3;) } | srcportlist lmore portnum @@ -794,34 +895,25 @@ dstobject: ; dstaddr: - addr { DOREM(bcopy(&($1.a), &fr->fr_ip.fi_dst, sizeof($1.a)); \ - bcopy(&($1.m), &fr->fr_mip.fi_dst, sizeof($1.m)); \ - if (dynamic != -1) { \ - fr->fr_datype = ifpflag; \ - fr->fr_ipf->fri_difpidx = dynamic; \ - } else if (pooled || hashed) \ - fr->fr_datype = FRI_LOOKUP;) + addr { if (($1.f != AF_UNSPEC) && (frc->fr_family != AF_UNSPEC) && + ($1.f != frc->fr_family)) + yyerror("1.src/dst address family mismatch"); + build_dstaddr_af(fr, &$1); } | lstart dstaddrlist lend ; dstaddrlist: - addr { DOREM(bcopy(&($1.a), &fr->fr_ip.fi_dst, sizeof($1.a)); \ - bcopy(&($1.m), &fr->fr_mip.fi_dst, sizeof($1.m)); \ - if (dynamic != -1) { \ - fr->fr_datype = ifpflag; \ - fr->fr_ipf->fri_difpidx = dynamic; \ - } else if (pooled || hashed) \ - fr->fr_datype = FRI_LOOKUP;) + addr { if (($1.f != AF_UNSPEC) && (frc->fr_family != AF_UNSPEC) && + ($1.f != frc->fr_family)) + yyerror("2.src/dst address family mismatch"); + build_dstaddr_af(fr, &$1); } | dstaddrlist lmore addr - { DOREM(bcopy(&($3.a), &fr->fr_ip.fi_dst, sizeof($3.a)); \ - bcopy(&($3.m), &fr->fr_mip.fi_dst, sizeof($3.m)); \ - if (dynamic != -1) { \ - fr->fr_datype = ifpflag; \ - fr->fr_ipf->fri_difpidx = dynamic; \ - } else if (pooled || hashed) \ - fr->fr_datype = FRI_LOOKUP;) + { if (($3.f != AF_UNSPEC) && (frc->fr_family != AF_UNSPEC) && + ($3.f != frc->fr_family)) + yyerror("3.src/dst address family mismatch"); + build_dstaddr_af(fr, &$3); } ; @@ -848,10 +940,10 @@ toport: dstportlist: portnum { DOREM(fr->fr_dcmp = FR_EQUAL; fr->fr_dport = $1;) } - | portnum ':' portnum + | portnum ':' portnum { DOREM(fr->fr_dcmp = FR_INCRANGE; fr->fr_dport = $1; \ fr->fr_dtop = $3;) } - | portnum YY_RANGE_IN portnum + | portnum YY_RANGE_IN portnum { DOREM(fr->fr_dcmp = FR_INRANGE; fr->fr_dport = $1; \ fr->fr_dtop = $3;) } | dstportlist lmore portnum @@ -865,141 +957,234 @@ dstportlist: ; addr: pool '/' YY_NUMBER { pooled = 1; + yyexpectaddr = 0; + $$.type = FRI_LOOKUP; + $$.v = 0; + $$.ifpos = -1; + $$.f = AF_UNSPEC; $$.a.iplookuptype = IPLT_POOL; $$.a.iplookupsubtype = 0; $$.a.iplookupnum = $3; } | pool '/' YY_STR { pooled = 1; + $$.ifpos = -1; + $$.f = AF_UNSPEC; + $$.type = FRI_LOOKUP; $$.a.iplookuptype = IPLT_POOL; $$.a.iplookupsubtype = 1; - strncpy($$.a.iplookupname, $3, - sizeof($$.a.iplookupname)); + $$.a.iplookupname = addname(&fr, $3); } - | pool '=' '(' poollist ')' { pooled = 1; + | pool '=' '(' { yyexpectaddr = 1; + pooled = 1; + } + poollist ')' { yyexpectaddr = 0; + $$.v = 0; + $$.ifpos = -1; + $$.f = AF_UNSPEC; + $$.type = FRI_LOOKUP; $$.a.iplookuptype = IPLT_POOL; $$.a.iplookupsubtype = 0; - $$.a.iplookupnum = makepool($4); } + $$.a.iplookupnum = makepool($5); + } | hash '/' YY_NUMBER { hashed = 1; + yyexpectaddr = 0; + $$.v = 0; + $$.ifpos = -1; + $$.f = AF_UNSPEC; + $$.type = FRI_LOOKUP; $$.a.iplookuptype = IPLT_HASH; $$.a.iplookupsubtype = 0; - $$.a.iplookupnum = $3; } - | hash '/' YY_STR { pooled = 1; + $$.a.iplookupnum = $3; + } + | hash '/' YY_STR { hashed = 1; + $$.type = FRI_LOOKUP; + $$.v = 0; + $$.ifpos = -1; + $$.f = AF_UNSPEC; $$.a.iplookuptype = IPLT_HASH; $$.a.iplookupsubtype = 1; - strncpy($$.a.iplookupname, $3, - sizeof($$.a.iplookupname)); + $$.a.iplookupname = addname(&fr, $3); } - | hash '=' '(' addrlist ')' { hashed = 1; + | hash '=' '(' { hashed = 1; + yyexpectaddr = 1; + } + addrlist ')' { yyexpectaddr = 0; + $$.v = 0; + $$.ifpos = -1; + $$.f = AF_UNSPEC; + $$.type = FRI_LOOKUP; $$.a.iplookuptype = IPLT_HASH; $$.a.iplookupsubtype = 0; - $$.a.iplookupnum = makehash($4); } - | ipaddr { bcopy(&$1, &$$, sizeof($$)); + $$.a.iplookupnum = makehash($5); + } + | ipaddr { $$ = $1; yyexpectaddr = 0; } ; ipaddr: IPFY_ANY { bzero(&($$), sizeof($$)); + $$.type = FRI_NORMAL; + $$.ifpos = -1; + yyexpectaddr = 0; + } + | hostname { $$.a = $1.adr; + $$.f = $1.f; + if ($1.f == AF_INET6) + fill6bits(128, $$.m.i6); + else if ($1.f == AF_INET) + fill6bits(32, $$.m.i6); + $$.v = ftov($1.f); + $$.ifpos = dynamic; + $$.type = FRI_NORMAL; + } + | hostname { yyresetdict(); } + maskspace { yysetdict(maskwords); + yyexpectaddr = 2; } + ipmask { ntomask($1.f, $5, $$.m.i6); + $$.a = $1.adr; + $$.a.i6[0] &= $$.m.i6[0]; + $$.a.i6[1] &= $$.m.i6[1]; + $$.a.i6[2] &= $$.m.i6[2]; + $$.a.i6[3] &= $$.m.i6[3]; + $$.f = $1.f; + $$.v = ftov($1.f); + $$.type = ifpflag; + $$.ifpos = dynamic; + if (ifpflag != 0 && $$.v == 0) { + if (frc->fr_family == AF_INET6){ + $$.v = 6; + $$.f = AF_INET6; + } else { + $$.v = 4; + $$.f = AF_INET; + } + } yyresetdict(); - yyexpectaddr = 0; } - | hostname { $$.a.in4 = $1; - $$.m.in4_addr = 0xffffffff; - yyexpectaddr = 0; } - | hostname { yyresetdict(); - $$.a.in4_addr = $1.s_addr; } - maskspace { yysetdict(maskwords); } - ipv4mask { $$.m.in4_addr = $5.s_addr; - $$.a.in4_addr &= $5.s_addr; + yyexpectaddr = 0; + } + | '(' YY_STR ')' { $$.type = FRI_DYNAMIC; + ifpflag = FRI_DYNAMIC; + $$.ifpos = addname(&fr, $2); + $$.lif = 0; + } + | '(' YY_STR ')' '/' + { ifpflag = FRI_DYNAMIC; yysetdict(maskwords); } + maskopts + { $$.type = ifpflag; + $$.ifpos = addname(&fr, $2); + $$.lif = 0; + if (frc->fr_family == AF_UNSPEC) + frc->fr_family = AF_INET; + if (ifpflag == FRI_DYNAMIC) { + ntomask(frc->fr_family, + $6, $$.m.i6); + } yyresetdict(); - yyexpectaddr = 0; } - | YY_IPV6 { bcopy(&$1, &$$.a, sizeof($$.a)); - fill6bits(128, (u_32_t *)&$$.m); + yyexpectaddr = 0; + } + | '(' YY_STR ':' YY_NUMBER ')' '/' + { ifpflag = FRI_DYNAMIC; yysetdict(maskwords); } + maskopts + { $$.type = ifpflag; + $$.ifpos = addname(&fr, $2); + $$.lif = $4; + if (frc->fr_family == AF_UNSPEC) + frc->fr_family = AF_INET; + if (ifpflag == FRI_DYNAMIC) { + ntomask(frc->fr_family, + $8, $$.m.i6); + } yyresetdict(); - yyexpectaddr = 0; } - | YY_IPV6 { yyresetdict(); - bcopy(&$1, &$$.a, sizeof($$.a)); } - maskspace { yysetdict(maskwords); } - ipv6mask { bcopy(&$5, &$$.m, sizeof($$.m)); - yyresetdict(); - yyexpectaddr = 0; } + yyexpectaddr = 0; + } ; + maskspace: '/' | IPFY_MASK ; -ipv4mask: - ipv4 { $$ = $1; } - | YY_HEX { $$.s_addr = htonl($1); } - | YY_NUMBER { ntomask(4, $1, (u_32_t *)&$$); } - | IPFY_BROADCAST { if (ifpflag == FRI_DYNAMIC) { - $$.s_addr = 0; - ifpflag = FRI_BROADCAST; - } else - YYERROR; - } - | IPFY_NETWORK { if (ifpflag == FRI_DYNAMIC) { - $$.s_addr = 0; - ifpflag = FRI_NETWORK; - } else - YYERROR; - } - | IPFY_NETMASKED { if (ifpflag == FRI_DYNAMIC) { - $$.s_addr = 0; - ifpflag = FRI_NETMASKED; - } else - YYERROR; - } - | IPFY_PEER { if (ifpflag == FRI_DYNAMIC) { - $$.s_addr = 0; - ifpflag = FRI_PEERADDR; - } else - YYERROR; - } +ipmask: ipv4 { $$ = count4bits($1.s_addr); } + | YY_HEX { $$ = count4bits(htonl($1)); } + | YY_NUMBER { $$ = $1; } + | YY_IPV6 { $$ = count6bits($1.i6); } + | maskopts { $$ = $1; } ; -ipv6mask: - YY_NUMBER { ntomask(6, $1, $$.i6); } - | IPFY_BROADCAST { if (ifpflag == FRI_DYNAMIC) { - bzero(&$$, sizeof($$)); +maskopts: + IPFY_BROADCAST { if (ifpflag == FRI_DYNAMIC) { ifpflag = FRI_BROADCAST; - } else + } else { YYERROR; + } + $$ = 0; } | IPFY_NETWORK { if (ifpflag == FRI_DYNAMIC) { - bzero(&$$, sizeof($$)); - ifpflag = FRI_BROADCAST; - } else + ifpflag = FRI_NETWORK; + } else { YYERROR; + } + $$ = 0; } | IPFY_NETMASKED { if (ifpflag == FRI_DYNAMIC) { - bzero(&$$, sizeof($$)); - ifpflag = FRI_BROADCAST; - } else + ifpflag = FRI_NETMASKED; + } else { YYERROR; + } + $$ = 0; } | IPFY_PEER { if (ifpflag == FRI_DYNAMIC) { - bzero(&$$, sizeof($$)); - ifpflag = FRI_BROADCAST; - } else + ifpflag = FRI_PEERADDR; + } else { YYERROR; + } + $$ = 0; } + | YY_NUMBER { $$ = $1; } ; hostname: - ipv4 { $$ = $1; } - | YY_NUMBER { $$.s_addr = $1; } - | YY_HEX { $$.s_addr = $1; } - | YY_STR { $$.s_addr = lookuphost($1); + ipv4 { $$.adr.in4 = $1; + if (frc->fr_family == AF_INET6) + YYERROR; + $$.f = AF_INET; + yyexpectaddr = 2; + } + | YY_NUMBER { if (frc->fr_family == AF_INET6) + YYERROR; + $$.adr.in4_addr = $1; + $$.f = AF_INET; + yyexpectaddr = 2; + } + | YY_HEX { if (frc->fr_family == AF_INET6) + YYERROR; + $$.adr.in4_addr = $1; + $$.f = AF_INET; + yyexpectaddr = 2; + } + | YY_STR { if (lookuphost($1, &$$.adr) == 0) + $$.f = AF_INET; free($1); + yyexpectaddr = 2; + } + | YY_IPV6 { if (frc->fr_family == AF_INET) + YYERROR; + $$.adr = $1; + $$.f = AF_INET6; + yyexpectaddr = 2; } ; addrlist: ipaddr { $$ = newalist(NULL); - bcopy(&($1.a), &($$->al_i6addr), sizeof($1.a)); - bcopy(&($1.m), &($$->al_i6mask), sizeof($1.m)); } - | addrlist ',' ipaddr - { $$ = newalist($1); - bcopy(&($3.a), &($$->al_i6addr), sizeof($3.a)); - bcopy(&($3.m), &($$->al_i6mask), sizeof($3.m)); } + $$->al_family = $1.f; + $$->al_i6addr = $1.a; + $$->al_i6mask = $1.m; + } + | ipaddr ',' { yyexpectaddr = 1; } addrlist + { $$ = newalist($4); + $$->al_family = $1.f; + $$->al_i6addr = $1.a; + $$->al_i6mask = $1.m; + } ; pool: IPFY_POOL { yyexpectaddr = 0; yycont = NULL; yyresetdict(); } @@ -1010,53 +1195,70 @@ hash: IPFY_HASH { yyexpectaddr = 0; yycont = NULL; yyresetdict(); } poollist: ipaddr { $$ = newalist(NULL); - bcopy(&($1.a), &($$->al_i6addr), sizeof($1.a)); - bcopy(&($1.m), &($$->al_i6mask), sizeof($1.m)); } + $$->al_family = $1.f; + $$->al_i6addr = $1.a; + $$->al_i6mask = $1.m; + } | '!' ipaddr { $$ = newalist(NULL); $$->al_not = 1; - bcopy(&($2.a), &($$->al_i6addr), sizeof($2.a)); - bcopy(&($2.m), &($$->al_i6mask), sizeof($2.m)); } + $$->al_family = $2.f; + $$->al_i6addr = $2.a; + $$->al_i6mask = $2.m; + } | poollist ',' ipaddr { $$ = newalist($1); - bcopy(&($3.a), &($$->al_i6addr), sizeof($3.a)); - bcopy(&($3.m), &($$->al_i6mask), sizeof($3.m)); } + $$->al_family = $3.f; + $$->al_i6addr = $3.a; + $$->al_i6mask = $3.m; + } | poollist ',' '!' ipaddr { $$ = newalist($1); $$->al_not = 1; - bcopy(&($4.a), &($$->al_i6addr), sizeof($4.a)); - bcopy(&($4.m), &($$->al_i6mask), sizeof($4.m)); } + $$->al_family = $4.f; + $$->al_i6addr = $4.a; + $$->al_i6mask = $4.m; + } ; port: IPFY_PORT { yyexpectaddr = 0; yycont = NULL; + if (frc->fr_proto != 0 && + frc->fr_proto != IPPROTO_UDP && + frc->fr_proto != IPPROTO_TCP) + yyerror("port use incorrect"); } ; portc: port compare { $$ = $2; - yysetdict(NULL); } + yysetdict(NULL); + } | porteq { $$ = $1; } ; porteq: port '=' { $$ = FR_EQUAL; - yysetdict(NULL); } + yysetdict(NULL); + } ; portr: IPFY_PORT { yyexpectaddr = 0; yycont = NULL; - yysetdict(NULL); } + yysetdict(NULL); + } ; portcomp: portc portnum { $$.pc = $1; $$.p1 = $2; - yyresetdict(); } + yyresetdict(); + } ; portrange: portr portnum range portnum { $$.p1 = $2; $$.pc = $3; $$.p2 = $4; - yyresetdict(); } + yyresetdict(); + } ; icmp: | itype icode @@ -1070,8 +1272,30 @@ itype: seticmptype icmptype ; seticmptype: - IPFY_ICMPTYPE { setipftype(); - yysetdict(icmptypewords); } + IPFY_ICMPTYPE { if (frc->fr_family == AF_UNSPEC) + frc->fr_family = AF_INET; + if (frc->fr_family == AF_INET && + frc->fr_type == FR_T_IPF && + frc->fr_proto != IPPROTO_ICMP) { + yyerror("proto not icmp"); + } + if (frc->fr_family == AF_INET6 && + frc->fr_type == FR_T_IPF && + frc->fr_proto != IPPROTO_ICMPV6) { + yyerror("proto not ipv6-icmp"); + } + setipftype(); + DOALL(if (fr->fr_family == AF_INET) { \ + fr->fr_ip.fi_v = 4; \ + fr->fr_mip.fi_v = 0xf; \ + } + if (fr->fr_family == AF_INET6) { \ + fr->fr_ip.fi_v = 6; \ + fr->fr_mip.fi_v = 0xf; \ + } + ) + yysetdict(NULL); + } ; icode: | seticmpcode icmpcode @@ -1146,9 +1370,18 @@ stateopt: IPFY_LIMIT YY_NUMBER { DOALL(fr->fr_statemax = $2;) } | IPFY_STRICT { DOALL(if (fr->fr_proto != IPPROTO_TCP) { \ YYERROR; \ - } else \ + } else if (fr->fr_flags & FR_STLOOSE) {\ + YYERROR; \ + } else \ fr->fr_flags |= FR_STSTRICT;) } + | IPFY_LOOSE { DOALL(if (fr->fr_proto != IPPROTO_TCP) { \ + YYERROR; \ + } else if (fr->fr_flags & FR_STSTRICT){\ + YYERROR; \ + } else \ + fr->fr_flags |= FR_STLOOSE;) + } | IPFY_NEWISN { DOALL(if (fr->fr_proto != IPPROTO_TCP) { \ YYERROR; \ } else \ @@ -1162,10 +1395,32 @@ stateopt: | IPFY_AGE YY_NUMBER '/' YY_NUMBER { DOALL(fr->fr_age[0] = $2; \ fr->fr_age[1] = $4;) } + | IPFY_ICMPHEAD groupname + { DOALL(seticmphead(&fr, $2);) + free($2); + } + | IPFY_NOLOG + { DOALL(fr->fr_nostatelog = 1;) } + | IPFY_RPC + { DOALL(fr->fr_rpc = 1;) } + | IPFY_RPC IPFY_IN YY_STR + { DOALL(fr->fr_rpc = 1;) } + | IPFY_MAX_SRCS YY_NUMBER + { DOALL(fr->fr_srctrack.ht_max_nodes = $2;) } + | IPFY_MAX_PER_SRC YY_NUMBER + { DOALL(fr->fr_srctrack.ht_max_per_node = $2; \ + fr->fr_srctrack.ht_netmask = \ + fr->fr_family == AF_INET ? 32: 128;) + } + | IPFY_MAX_PER_SRC YY_NUMBER '/' YY_NUMBER + { DOALL(fr->fr_srctrack.ht_max_per_node = $2; \ + fr->fr_srctrack.ht_netmask = $4;) + } ; portnum: - servicename { if (getport(frc, $1, &($$)) == -1) + servicename { if (getport(frc, $1, + &($$), NULL) == -1) yyerror("service unknown"); $$ = ntohs($$); free($1); @@ -1188,14 +1443,14 @@ withopt: | notwith opttype { DOALL(fr->fr_mflx |= $2;) } | ipopt ipopts { yyresetdict(); } | notwith ipopt ipopts { yyresetdict(); } - | startv6hdrs ipv6hdrs { yyresetdict(); } + | startv6hdr ipv6hdrs { yyresetdict(); } ; ipopt: IPFY_OPT { yysetdict(ipv4optwords); } ; -startv6hdrs: - IPF6_V6HDRS { if (use_inet6 == 0) +startv6hdr: + IPFY_V6HDR { if (frc->fr_family != AF_INET6) yyerror("only available with IPv6"); yysetdict(ipv6optwords); } @@ -1222,9 +1477,18 @@ opttype: | IPFY_BROADCAST { $$ = FI_BROADCAST; } | IPFY_STATE { $$ = FI_STATE; } | IPFY_OOW { $$ = FI_OOW; } + | IPFY_AH { $$ = FI_AH; } + | IPFY_V6HDRS { $$ = FI_V6EXTHDR; } ; ipopts: optlist { DOALL(fr->fr_mip.fi_optmsk |= $1; + if (fr->fr_family == AF_UNSPEC) { + fr->fr_family = AF_INET; + fr->fr_ip.fi_v = 4; + fr->fr_mip.fi_v = 0xf; + } else if (fr->fr_family != AF_INET) { + YYERROR; + } if (!nowith) fr->fr_ip.fi_optmsk |= $1;) } @@ -1264,22 +1528,11 @@ seclevel: ; icmptype: - YY_NUMBER { $$ = $1; } - | IPFY_ICMPT_UNR { $$ = ICMP_UNREACH; } - | IPFY_ICMPT_ECHO { $$ = ICMP_ECHO; } - | IPFY_ICMPT_ECHOR { $$ = ICMP_ECHOREPLY; } - | IPFY_ICMPT_SQUENCH { $$ = ICMP_SOURCEQUENCH; } - | IPFY_ICMPT_REDIR { $$ = ICMP_REDIRECT; } - | IPFY_ICMPT_TIMEX { $$ = ICMP_TIMXCEED; } - | IPFY_ICMPT_PARAMP { $$ = ICMP_PARAMPROB; } - | IPFY_ICMPT_TIMEST { $$ = ICMP_TSTAMP; } - | IPFY_ICMPT_TIMESTREP { $$ = ICMP_TSTAMPREPLY; } - | IPFY_ICMPT_INFOREQ { $$ = ICMP_IREQ; } - | IPFY_ICMPT_INFOREP { $$ = ICMP_IREQREPLY; } - | IPFY_ICMPT_MASKREQ { $$ = ICMP_MASKREQ; } - | IPFY_ICMPT_MASKREP { $$ = ICMP_MASKREPLY; } - | IPFY_ICMPT_ROUTERAD { $$ = ICMP_ROUTERADVERT; } - | IPFY_ICMPT_ROUTERSOL { $$ = ICMP_ROUTERSOLICIT; } + YY_NUMBER { $$ = $1; } + | YY_STR { $$ = geticmptype(frc->fr_family, $1); + if ($$ == -1) + yyerror("unrecognised icmp type"); + } ; icmpcode: @@ -1314,7 +1567,8 @@ opt: | IPFY_IPOPT_SEC { $$ = getoptbyvalue(IPOPT_SECURITY); } | IPFY_IPOPT_LSRR { $$ = getoptbyvalue(IPOPT_LSRR); } | IPFY_IPOPT_ESEC { $$ = getoptbyvalue(IPOPT_E_SEC); } - | IPFY_IPOPT_CIPSO { $$ = getoptbyvalue(IPOPT_CIPSO); } + | IPFY_IPOPT_CIPSO { $$ = getoptbyvalue(IPOPT_CIPSO); } + | IPFY_IPOPT_CIPSO doi { $$ = getoptbyvalue(IPOPT_CIPSO); } | IPFY_IPOPT_SATID { $$ = getoptbyvalue(IPOPT_SATID); } | IPFY_IPOPT_SSRR { $$ = getoptbyvalue(IPOPT_SSRR); } | IPFY_IPOPT_ADDEXT { $$ = getoptbyvalue(IPOPT_ADDEXT); } @@ -1329,6 +1583,13 @@ opt: | IPFY_IPOPT_UMP { $$ = getoptbyvalue(IPOPT_UMP); } | setsecclass secname { DOALL(fr->fr_mip.fi_secmsk |= $2; + if (fr->fr_family == AF_UNSPEC) { + fr->fr_family = AF_INET; + fr->fr_ip.fi_v = 4; + fr->fr_mip.fi_v = 0xf; + } else if (fr->fr_family != AF_INET) { + YYERROR; + } if (!nowith) fr->fr_ip.fi_secmsk |= $2;) $$ = 0; @@ -1337,7 +1598,15 @@ opt: ; setsecclass: - IPFY_SECCLASS { yysetdict(ipv4secwords); } + IPFY_SECCLASS { yysetdict(ipv4secwords); } + ; + +doi: IPFY_DOI YY_NUMBER { DOALL(fr->fr_doimask = 0xffffffff; \ + if (!nowith) \ + fr->fr_doi = $2;) } + | IPFY_DOI YY_HEX { DOALL(fr->fr_doimask = 0xffffffff; \ + if (!nowith) \ + fr->fr_doi = $2;) } ; ipv6hdr: @@ -1463,7 +1732,7 @@ ipv4: ipv4_24 '.' YY_NUMBER %% -static struct wordtab ipfwords[95] = { +static struct wordtab ipfwords[] = { { "age", IPFY_AGE }, { "ah", IPFY_AH }, { "all", IPFY_ALL }, @@ -1481,10 +1750,16 @@ static struct wordtab ipfwords[95] = { #endif { "call", IPFY_CALL }, { "code", IPFY_ICMPCODE }, + { "comment", IPFY_COMMENT }, { "count", IPFY_COUNT }, + { "decapsulate", IPFY_DECAPS }, + { "dstlist", IPFY_DSTLIST }, + { "doi", IPFY_DOI }, { "dup-to", IPFY_DUPTO }, { "eq", YY_CMP_EQ }, { "esp", IPFY_ESP }, + { "exp", IPFY_IPFEXPR }, + { "family", IPFY_FAMILY }, { "fastroute", IPFY_FROUTE }, { "first", IPFY_FIRST }, { "flags", IPFY_FLAGS }, @@ -1497,20 +1772,27 @@ static struct wordtab ipfwords[95] = { { "gt", YY_CMP_GT }, { "head", IPFY_HEAD }, { "icmp", IPFY_ICMP }, + { "icmp-head", IPFY_ICMPHEAD }, { "icmp-type", IPFY_ICMPTYPE }, { "in", IPFY_IN }, { "in-via", IPFY_INVIA }, + { "inet", IPFY_INET }, + { "inet6", IPFY_INET6 }, { "ipopt", IPFY_IPOPTS }, { "ipopts", IPFY_IPOPTS }, { "keep", IPFY_KEEP }, + { "l5-as", IPFY_L5AS }, { "le", YY_CMP_LE }, { "level", IPFY_LEVEL }, { "limit", IPFY_LIMIT }, { "log", IPFY_LOG }, + { "loose", IPFY_LOOSE }, { "lowttl", IPFY_LOWTTL }, { "lt", YY_CMP_LT }, { "mask", IPFY_MASK }, { "match-tag", IPFY_MATCHTAG }, + { "max-per-src", IPFY_MAX_PER_SRC }, + { "max-srcs", IPFY_MAX_SRCS }, { "mbcast", IPFY_MBCAST }, { "mcast", IPFY_MULTICAST }, { "multicast", IPFY_MULTICAST }, @@ -1520,6 +1802,7 @@ static struct wordtab ipfwords[95] = { { "newisn", IPFY_NEWISN }, { "no", IPFY_NO }, { "no-icmp-err", IPFY_NOICMPERR }, + { "nolog", IPFY_NOLOG }, { "nomatch", IPFY_NOMATCH }, { "now", IPFY_NOW }, { "not", IPFY_NOT }, @@ -1540,7 +1823,10 @@ static struct wordtab ipfwords[95] = { { "return-icmp-as-dest", IPFY_RETICMPASDST }, { "return-rst", IPFY_RETRST }, { "route-to", IPFY_ROUTETO }, + { "rule-ttl", IPFY_RULETTL }, + { "rpc", IPFY_RPC }, { "sec-class", IPFY_SECCLASS }, + { "set", IPFY_SET }, { "set-tag", IPFY_SETTAG }, { "skip", IPFY_SKIP }, { "short", IPFY_SHORT }, @@ -1554,19 +1840,20 @@ static struct wordtab ipfwords[95] = { { "to", IPFY_TO }, { "ttl", IPFY_TTL }, { "udp", IPFY_UDP }, - { "v6hdrs", IPF6_V6HDRS }, + { "v6hdr", IPFY_V6HDR }, + { "v6hdrs", IPFY_V6HDRS }, { "with", IPFY_WITH }, { NULL, 0 } }; -static struct wordtab addrwords[4] = { +static struct wordtab addrwords[] = { { "any", IPFY_ANY }, { "hash", IPFY_HASH }, { "pool", IPFY_POOL }, { NULL, 0 } }; -static struct wordtab maskwords[5] = { +static struct wordtab maskwords[] = { { "broadcast", IPFY_BROADCAST }, { "netmasked", IPFY_NETMASKED }, { "network", IPFY_NETWORK }, @@ -1574,26 +1861,7 @@ static struct wordtab maskwords[5] = { { NULL, 0 } }; -static struct wordtab icmptypewords[16] = { - { "echo", IPFY_ICMPT_ECHO }, - { "echorep", IPFY_ICMPT_ECHOR }, - { "inforeq", IPFY_ICMPT_INFOREQ }, - { "inforep", IPFY_ICMPT_INFOREP }, - { "maskrep", IPFY_ICMPT_MASKREP }, - { "maskreq", IPFY_ICMPT_MASKREQ }, - { "paramprob", IPFY_ICMPT_PARAMP }, - { "redir", IPFY_ICMPT_REDIR }, - { "unreach", IPFY_ICMPT_UNR }, - { "routerad", IPFY_ICMPT_ROUTERAD }, - { "routersol", IPFY_ICMPT_ROUTERSOL }, - { "squench", IPFY_ICMPT_SQUENCH }, - { "timest", IPFY_ICMPT_TIMEST }, - { "timestrep", IPFY_ICMPT_TIMESTREP }, - { "timex", IPFY_ICMPT_TIMEX }, - { NULL, 0 }, -}; - -static struct wordtab icmpcodewords[17] = { +static struct wordtab icmpcodewords[] = { { "cutoff-preced", IPFY_ICMPC_CUTPRE }, { "filter-prohib", IPFY_ICMPC_FLTPRO }, { "isolate", IPFY_ICMPC_ISOLATE }, @@ -1613,7 +1881,7 @@ static struct wordtab icmpcodewords[17] = { { NULL, 0 }, }; -static struct wordtab ipv4optwords[25] = { +static struct wordtab ipv4optwords[] = { { "addext", IPFY_IPOPT_ADDEXT }, { "cipso", IPFY_IPOPT_CIPSO }, { "dps", IPFY_IPOPT_DPS }, @@ -1641,7 +1909,7 @@ static struct wordtab ipv4optwords[25] = { { NULL, 0 }, }; -static struct wordtab ipv4secwords[9] = { +static struct wordtab ipv4secwords[] = { { "confid", IPFY_SEC_CONF }, { "reserv-1", IPFY_SEC_RSV1 }, { "reserv-2", IPFY_SEC_RSV2 }, @@ -1653,7 +1921,7 @@ static struct wordtab ipv4secwords[9] = { { NULL, 0 }, }; -static struct wordtab ipv6optwords[9] = { +static struct wordtab ipv6optwords[] = { { "dstopts", IPFY_IPV6OPT_DSTOPTS }, { "esp", IPFY_IPV6OPT_ESP }, { "frag", IPFY_IPV6OPT_FRAG }, @@ -1665,7 +1933,7 @@ static struct wordtab ipv6optwords[9] = { { NULL, 0 }, }; -static struct wordtab logwords[33] = { +static struct wordtab logwords[] = { { "kern", IPFY_FAC_KERN }, { "user", IPFY_FAC_USER }, { "mail", IPFY_FAC_MAIL }, @@ -1751,7 +2019,7 @@ FILE *fp; ipffd = fd; for (i = 0; i <= IPL_LOGMAX; i++) - ipfioctl[i] = iocfuncs[i]; + ipfioctls[i] = iocfuncs[i]; ipfaddfunc = addfunc; if (feof(fp)) @@ -1779,23 +2047,29 @@ static void newrule() { frentry_t *frn; - frn = (frentry_t *)calloc(1, sizeof(frentry_t)); + frn = allocfr(); for (fr = frtop; fr != NULL && fr->fr_next != NULL; fr = fr->fr_next) ; - if (fr != NULL) + if (fr != NULL) { fr->fr_next = frn; - if (frtop == NULL) + frn->fr_pnext = &fr->fr_next; + } + if (frtop == NULL) { frtop = frn; + frn->fr_pnext = &frtop; + } fr = frn; frc = frn; fr->fr_loglevel = 0xffff; fr->fr_isc = (void *)-1; fr->fr_logtag = FR_NOLOGTAG; fr->fr_type = FR_T_NONE; - if (use_inet6 != 0) - fr->fr_v = 6; - else - fr->fr_v = 4; + fr->fr_flineno = yylineNum; + + if (use_inet6 == 1) + fr->fr_family = AF_INET6; + else if (use_inet6 == -1) + fr->fr_family = AF_INET; nrules = 1; } @@ -1808,7 +2082,13 @@ static void setipftype() fr->fr_type = FR_T_IPF; fr->fr_data = (void *)calloc(sizeof(fripf_t), 1); fr->fr_dsize = sizeof(fripf_t); - fr->fr_ip.fi_v = frc->fr_v; + fr->fr_family = frc->fr_family; + if (fr->fr_family == AF_INET) { + fr->fr_ip.fi_v = 4; + } + else if (fr->fr_family == AF_INET6) { + fr->fr_ip.fi_v = 6; + } fr->fr_mip.fi_v = 0xf; fr->fr_ipf->fri_sifpidx = -1; fr->fr_ipf->fri_difpidx = -1; @@ -1831,10 +2111,13 @@ static frentry_t *addrule() count = nrules; f = f2; for (f1 = frc; count > 0; count--, f1 = f1->fr_next) { - f->fr_next = (frentry_t *)calloc(sizeof(*f), 1); + f->fr_next = allocfr(); + if (f->fr_next == NULL) + return NULL; + f->fr_next->fr_pnext = &f->fr_next; added++; f = f->fr_next; - bcopy(f1, f, sizeof(*f)); + *f = *f1; f->fr_next = NULL; if (f->fr_caddr != NULL) { f->fr_caddr = malloc(f->fr_dsize); @@ -1846,10 +2129,11 @@ static frentry_t *addrule() } -static u_32_t lookuphost(name) -char *name; +static int +lookuphost(name, addrp) + char *name; + i6addr_t *addrp; { - u_32_t addr; int i; hashed = 0; @@ -1857,19 +2141,20 @@ char *name; dynamic = -1; for (i = 0; i < 4; i++) { - if (strncmp(name, frc->fr_ifnames[i], - sizeof(frc->fr_ifnames[i])) == 0) { + if (fr->fr_ifnames[i] == -1) + continue; + if (strcmp(name, fr->fr_names + fr->fr_ifnames[i]) == 0) { ifpflag = FRI_DYNAMIC; - dynamic = i; - return 0; + dynamic = addname(&fr, name); + return 1; } } - if (gethost(name, &addr) == -1) { + if (gethost(AF_INET, name, addrp) == -1) { fprintf(stderr, "unknown name \"%s\"\n", name); - return 0; + return -1; } - return addr; + return 0; } @@ -1891,7 +2176,7 @@ char *phrase; fprintf(stderr, "cannot mix IPF and BPF matching\n"); return; } - fr->fr_v = v; + fr->fr_family = vtof(v); fr->fr_type = FR_T_BPFOPC; if (!strncmp(phrase, "0x", 2)) { @@ -1986,8 +2271,9 @@ alist_t *ptr; } -static int makepool(list) -alist_t *list; +static int +makepool(list) + alist_t *list; { ip_pool_node_t *n, *top; ip_pool_t pool; @@ -1999,10 +2285,30 @@ alist_t *list; top = calloc(1, sizeof(*top)); if (top == NULL) return 0; - + for (n = top, a = list; (n != NULL) && (a != NULL); a = a->al_next) { - n->ipn_addr.adf_addr.in4.s_addr = a->al_1; - n->ipn_mask.adf_addr.in4.s_addr = a->al_2; + if (use_inet6 == 1) { +#ifdef AF_INET6 + n->ipn_addr.adf_family = AF_INET6; + n->ipn_addr.adf_addr = a->al_i6addr; + n->ipn_addr.adf_len = offsetof(addrfamily_t, + adf_addr) + 16; + n->ipn_mask.adf_family = AF_INET6; + n->ipn_mask.adf_addr = a->al_i6mask; + n->ipn_mask.adf_len = offsetof(addrfamily_t, + adf_addr) + 16; + +#endif + } else { + n->ipn_addr.adf_family = AF_INET; + n->ipn_addr.adf_addr.in4.s_addr = a->al_1; + n->ipn_addr.adf_len = offsetof(addrfamily_t, + adf_addr) + 4; + n->ipn_mask.adf_family = AF_INET; + n->ipn_mask.adf_addr.in4.s_addr = a->al_2; + n->ipn_mask.adf_len = offsetof(addrfamily_t, + adf_addr) + 4; + } n->ipn_info = a->al_not; if (a->al_next != NULL) { n->ipn_next = calloc(1, sizeof(*n)); @@ -2013,7 +2319,7 @@ alist_t *list; bzero((char *)&pool, sizeof(pool)); pool.ipo_unit = IPL_LOGIPF; pool.ipo_list = top; - num = load_pool(&pool, ipfioctl[IPL_LOGLOOKUP]); + num = load_pool(&pool, ipfioctls[IPL_LOGLOOKUP]); while ((n = top) != NULL) { top = n->ipn_next; @@ -2036,10 +2342,17 @@ alist_t *list; top = calloc(1, sizeof(*top)); if (top == NULL) return 0; - + for (n = top, a = list; (n != NULL) && (a != NULL); a = a->al_next) { - n->ipe_addr.in4_addr = a->al_1; - n->ipe_mask.in4_addr = a->al_2; + if (a->al_family == AF_INET6) { + n->ipe_family = AF_INET6; + n->ipe_addr = a->al_i6addr; + n->ipe_mask = a->al_i6mask; + } else { + n->ipe_family = AF_INET; + n->ipe_addr.in4_addr = a->al_1; + n->ipe_mask.in4_addr = a->al_2; + } n->ipe_value = 0; if (a->al_next != NULL) { n->ipe_next = calloc(1, sizeof(*n)); @@ -2052,7 +2365,7 @@ alist_t *list; iph.iph_type = IPHASH_LOOKUP; *iph.iph_name = '\0'; - if (load_hash(&iph, top, ipfioctl[IPL_LOGLOOKUP]) == 0) + if (load_hash(&iph, top, ipfioctls[IPL_LOGLOOKUP]) == 0) sscanf(iph.iph_name, "%u", &num); else num = 0; @@ -2065,7 +2378,7 @@ alist_t *list; } -void ipf_addrule(fd, ioctlfunc, ptr) +int ipf_addrule(fd, ioctlfunc, ptr) int fd; ioctlfunc_t ioctlfunc; void *ptr; @@ -2075,7 +2388,7 @@ void *ptr; ipfobj_t obj; if (ptr == NULL) - return; + return 0; fr = ptr; add = 0; @@ -2083,7 +2396,7 @@ void *ptr; bzero((char *)&obj, sizeof(obj)); obj.ipfo_rev = IPFILTER_VERSION; - obj.ipfo_size = sizeof(*fr); + obj.ipfo_size = fr->fr_size; obj.ipfo_type = IPFOBJ_FRENTRY; obj.ipfo_ptr = ptr; @@ -2118,8 +2431,11 @@ void *ptr; if ((opts & OPT_ZERORULEST) != 0) { if ((*ioctlfunc)(fd, add, (void *)&obj) == -1) { if ((opts & OPT_DONOTHING) == 0) { - fprintf(stderr, "%d:", yylineNum); - perror("ioctl(SIOCZRLST)"); + char msg[80]; + + sprintf(msg, "%d:ioctl(zero rule)", + fr->fr_flineno); + return ipf_perror_fd(fd, ioctlfunc, msg); } } else { #ifdef USE_QUAD_T @@ -2134,19 +2450,26 @@ void *ptr; } } else if ((opts & OPT_REMOVE) != 0) { if ((*ioctlfunc)(fd, del, (void *)&obj) == -1) { - if ((opts & OPT_DONOTHING) != 0) { - fprintf(stderr, "%d:", yylineNum); - perror("ioctl(delete rule)"); + if ((opts & OPT_DONOTHING) == 0) { + char msg[80]; + + sprintf(msg, "%d:ioctl(delete rule)", + fr->fr_flineno); + return ipf_perror_fd(fd, ioctlfunc, msg); } } } else { if ((*ioctlfunc)(fd, add, (void *)&obj) == -1) { - if (!(opts & OPT_DONOTHING)) { - fprintf(stderr, "%d:", yylineNum); - perror("ioctl(add/insert rule)"); + if ((opts & OPT_DONOTHING) == 0) { + char msg[80]; + + sprintf(msg, "%d:ioctl(add/insert rule)", + fr->fr_flineno); + return ipf_perror_fd(fd, ioctlfunc, msg); } } } + return 0; } static void setsyslog() @@ -2168,9 +2491,16 @@ frentry_t *fr; { frentry_t *f; - for (f = frold; f != NULL; f = f->fr_next) - if (strncmp(f->fr_grhead, fr->fr_group, FR_GROUPLEN) == 0) + for (f = frold; f != NULL; f = f->fr_next) { + if (f->fr_grhead == -1 && fr->fr_group == -1) break; + if (f->fr_grhead == -1 || fr->fr_group == -1) + continue; + if (strcmp(f->fr_names + f->fr_grhead, + fr->fr_names + fr->fr_group) == 0) + break; + } + if (f == NULL) return; @@ -2183,8 +2513,8 @@ frentry_t *fr; if (f->fr_type != fr->fr_type || f->fr_type != FR_T_IPF) return; - if (fr->fr_v == 0 && f->fr_v != 0) - fr->fr_v = f->fr_v; + if (fr->fr_family == 0 && f->fr_family != 0) + fr->fr_family = f->fr_family; if (fr->fr_mproto == 0 && f->fr_mproto != 0) fr->fr_mproto = f->fr_mproto; @@ -2192,6 +2522,218 @@ frentry_t *fr; fr->fr_proto = f->fr_proto; if ((fr->fr_mproto == 0) && ((fr->fr_flx & FI_TCPUDP) == 0) && - ((f->fr_flx & FI_TCPUDP) != 0)) + ((f->fr_flx & FI_TCPUDP) != 0)) { fr->fr_flx |= FI_TCPUDP; + fr->fr_mflx |= FI_TCPUDP; + } +} + + +static void doipfexpr(line) +char *line; +{ + int *array; + char *error; + + array = parseipfexpr(line, &error); + if (array == NULL) { + fprintf(stderr, "%s:", error); + yyerror("error parsing ipf matching expression"); + return; + } + + fr->fr_type = FR_T_IPFEXPR; + fr->fr_data = array; + fr->fr_dsize = array[0] * sizeof(*array); +} + + +static void do_tuneint(varname, value) +char *varname; +int value; +{ + char buffer[80]; + + strncpy(buffer, varname, 60); + buffer[59] = '\0'; + strcat(buffer, "="); + sprintf(buffer, "%u", value); + ipf_dotuning(ipffd, buffer, ioctl); +} + + +static void do_tunestr(varname, value) +char *varname, *value; +{ + + if (!strcasecmp(value, "true")) { + do_tuneint(varname, 1); + } else if (!strcasecmp(value, "false")) { + do_tuneint(varname, 0); + } else { + yyerror("did not find true/false where expected"); + } +} + + +static void setifname(frp, idx, name) +frentry_t **frp; +int idx; +char *name; +{ + int pos; + + pos = addname(frp, name); + if (pos == -1) + return; + (*frp)->fr_ifnames[idx] = pos; +} + + +static int addname(frp, name) +frentry_t **frp; +char *name; +{ + frentry_t *f; + int nlen; + int pos; + + nlen = strlen(name) + 1; + f = realloc(*frp, (*frp)->fr_size + nlen); + if (*frp == frc) + frc = f; + *frp = f; + if (f == NULL) + return -1; + if (f->fr_pnext != NULL) + *f->fr_pnext = f; + f->fr_size += nlen; + pos = f->fr_namelen; + f->fr_namelen += nlen; + strcpy(f->fr_names + pos, name); + f->fr_names[f->fr_namelen] = '\0'; + return pos; +} + + +static frentry_t *allocfr() +{ + frentry_t *fr; + + fr = calloc(1, sizeof(*fr)); + if (fr != NULL) { + fr->fr_size = sizeof(*fr); + fr->fr_comment = -1; + fr->fr_group = -1; + fr->fr_grhead = -1; + fr->fr_icmphead = -1; + fr->fr_ifnames[0] = -1; + fr->fr_ifnames[1] = -1; + fr->fr_ifnames[2] = -1; + fr->fr_ifnames[3] = -1; + fr->fr_tif.fd_name = -1; + fr->fr_rif.fd_name = -1; + fr->fr_dif.fd_name = -1; + } + return fr; +} + + +static void setgroup(frp, name) +frentry_t **frp; +char *name; +{ + int pos; + + pos = addname(frp, name); + if (pos == -1) + return; + (*frp)->fr_group = pos; +} + + +static void setgrhead(frp, name) +frentry_t **frp; +char *name; +{ + int pos; + + pos = addname(frp, name); + if (pos == -1) + return; + (*frp)->fr_grhead = pos; +} + + +static void seticmphead(frp, name) +frentry_t **frp; +char *name; +{ + int pos; + + pos = addname(frp, name); + if (pos == -1) + return; + (*frp)->fr_icmphead = pos; +} + + +static void +build_dstaddr_af(fp, ptr) + frentry_t *fp; + void *ptr; +{ + struct ipp_s *ipp = ptr; + frentry_t *f = fp; + + if (f->fr_family != AF_UNSPEC && ipp->f == AF_UNSPEC) { + ipp->f = f->fr_family; + ipp->v = f->fr_ip.fi_v; + } + if (ipp->f == AF_INET) + ipp->v = 4; + else if (ipp->f == AF_INET6) + ipp->v = 6; + + for (; f != NULL; f = f->fr_next) { + f->fr_ip.fi_dst = ipp->a; + f->fr_mip.fi_dst = ipp->m; + f->fr_family = ipp->f; + f->fr_ip.fi_v = ipp->v; + f->fr_mip.fi_v = 0xf; + f->fr_datype = ipp->type; + if (ipp->ifpos != -1) + f->fr_ipf->fri_difpidx = ipp->ifpos; + } + fr = NULL; +} + + +static void +build_srcaddr_af(fp, ptr) + frentry_t *fp; + void *ptr; +{ + struct ipp_s *ipp = ptr; + frentry_t *f = fp; + + if (f->fr_family != AF_UNSPEC && ipp->f == AF_UNSPEC) { + ipp->f = f->fr_family; + ipp->v = f->fr_ip.fi_v; + } + if (ipp->f == AF_INET) + ipp->v = 4; + else if (ipp->f == AF_INET6) + ipp->v = 6; + + for (; f != NULL; f = f->fr_next) { + f->fr_ip.fi_src = ipp->a; + f->fr_mip.fi_src = ipp->m; + f->fr_family = ipp->f; + f->fr_ip.fi_v = ipp->v; + f->fr_mip.fi_v = 0xf; + f->fr_satype = ipp->type; + f->fr_ipf->fri_sifpidx = ipp->ifpos; + } + fr = NULL; } diff --git a/contrib/ipfilter/tools/ipfcomp.c b/contrib/ipfilter/tools/ipfcomp.c index e00fe84a9af..eba28ceb1d5 100644 --- a/contrib/ipfilter/tools/ipfcomp.c +++ b/contrib/ipfilter/tools/ipfcomp.c @@ -1,13 +1,13 @@ /* $FreeBSD$ */ /* - * Copyright (C) 2001-2005 by Darren Reed. + * Copyright (C) 2012 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. */ #if !defined(lint) static const char sccsid[] = "@(#)ip_fil.c 2.41 6/5/96 (C) 1993-2000 Darren Reed"; -static const char rcsid[] = "@(#)$Id: ipfcomp.c,v 1.24.2.7 2007/05/01 22:15:00 darrenr Exp $"; +static const char rcsid[] = "@(#)$Id$"; #endif #include "ipf.h" @@ -63,7 +63,7 @@ static FILE *cfile = NULL; * required. */ void printc(fr) -frentry_t *fr; + frentry_t *fr; { fripf_t *ipf; u_long *ulp; @@ -71,7 +71,7 @@ frentry_t *fr; FILE *fp; int i; - if (fr->fr_v != 4) + if (fr->fr_family == 6) return; if ((fr->fr_type != FR_T_IPF) && (fr->fr_type != FR_T_NONE)) return; @@ -87,7 +87,7 @@ frentry_t *fr; fp = cfile; if (count == 0) { fprintf(fp, "/*\n"); - fprintf(fp, "* Copyright (C) 1993-2000 by Darren Reed.\n"); + fprintf(fp, "* Copyright (C) 2012 by Darren Reed.\n"); fprintf(fp, "*\n"); fprintf(fp, "* Redistribution and use in source and binary forms are permitted\n"); fprintf(fp, "* provided that this notice is preserved and due credit is given\n"); @@ -136,6 +136,9 @@ frentry_t *fr; fprintf(fp, "#endif /* _KERNEL */\n"); fprintf(fp, "\n"); fprintf(fp, "#ifdef IPFILTER_COMPILED\n"); + fprintf(fp, "\n"); + fprintf(fp, "extern ipf_main_softc_t ipfmain;\n"); + fprintf(fp, "\n"); } addrule(fp, fr); @@ -162,12 +165,14 @@ static frgroup_t *groups = NULL; static void addrule(fp, fr) -FILE *fp; -frentry_t *fr; + FILE *fp; + frentry_t *fr; { frentry_t *f, **fpp; frgroup_t *g; u_long *ulp; + char *ghead; + char *gname; char *and; int i; @@ -180,8 +185,10 @@ frentry_t *fr; } f->fr_next = NULL; + gname = FR_NAME(fr, fr_group); + for (g = groups; g != NULL; g = g->fg_next) - if ((strncmp(g->fg_name, f->fr_group, FR_GROUPLEN) == 0) && + if ((strncmp(g->fg_name, gname, FR_GROUPLEN) == 0) && (g->fg_flags == (f->fr_flags & FR_INOUT))) break; @@ -190,7 +197,7 @@ frentry_t *fr; g->fg_next = groups; groups = g; g->fg_head = f; - bcopy(f->fr_group, g->fg_name, FR_GROUPLEN); + strncpy(g->fg_name, gname, FR_GROUPLEN); g->fg_ref = 0; g->fg_flags = f->fr_flags & FR_INOUT; } @@ -219,10 +226,10 @@ static u_long ipf%s_rule_data_%s_%u[] = {\n", g->fg_ref++; - if (f->fr_grhead != 0) { + if (f->fr_grhead != -1) { + ghead = FR_NAME(f, fr_grhead); for (g = groups; g != NULL; g = g->fg_next) - if ((strncmp(g->fg_name, f->fr_grhead, - FR_GROUPLEN) == 0) && + if ((strncmp(g->fg_name, ghead, FR_GROUPLEN) == 0) && g->fg_flags == (f->fr_flags & FR_INOUT)) break; if (g == NULL) { @@ -230,7 +237,7 @@ static u_long ipf%s_rule_data_%s_%u[] = {\n", g->fg_next = groups; groups = g; g->fg_head = f; - bcopy(f->fr_grhead, g->fg_name, FR_GROUPLEN); + strncpy(g->fg_name, ghead, FR_GROUPLEN); g->fg_ref = 0; g->fg_flags = f->fr_flags & FR_INOUT; } @@ -239,7 +246,7 @@ static u_long ipf%s_rule_data_%s_%u[] = {\n", int intcmp(c1, c2) -const void *c1, *c2; + const void *c1, *c2; { const mc_t *i1 = (const mc_t *)c1, *i2 = (const mc_t *)c2; @@ -251,17 +258,17 @@ const void *c1, *c2; static void indent(fp, in) -FILE *fp; -int in; + FILE *fp; + int in; { for (; in; in--) fputc('\t', fp); } static void printeq(fp, var, m, max, v) -FILE *fp; -char *var; -int m, max, v; + FILE *fp; + char *var; + int m, max, v; { if (m == max) fprintf(fp, "%s == %#x) {\n", var, v); @@ -276,9 +283,9 @@ int m, max, v; * v - required address */ static void printipeq(fp, var, fl, m, v) -FILE *fp; -char *var; -int fl, m, v; + FILE *fp; + char *var; + int fl, m, v; { if (m == 0xffffffff) fprintf(fp, "%s ", var); @@ -290,9 +297,9 @@ int fl, m, v; void emit(num, dir, v, fr) -int num, dir; -void *v; -frentry_t *fr; + int num, dir; + void *v; + frentry_t *fr; { u_int incnt, outcnt; frgroup_t *g; @@ -342,8 +349,8 @@ frentry_t *fr; static void emitheader(grp, incount, outcount) -frgroup_t *grp; -u_int incount, outcount; + frgroup_t *grp; + u_int incount, outcount; { static FILE *fph = NULL; frgroup_t *g; @@ -434,11 +441,11 @@ int ipfrule_remove()\n\ static void emitGroup(num, dir, v, fr, group, incount, outcount) -int num, dir; -void *v; -frentry_t *fr; -char *group; -u_int incount, outcount; + int num, dir; + void *v; + frentry_t *fr; + char *group; + u_int incount, outcount; { static FILE *fp = NULL; static int header[2] = { 0, 0 }; @@ -514,9 +521,8 @@ u_int incount, outcount; if ((i & 1) == 0) { fprintf(fp, "\n\t"); } - fprintf(fp, - "(frentry_t *)&in_rule_%s_%d", - f->fr_group, i); + fprintf(fp, "(frentry_t *)&in_rule_%s_%d", + FR_NAME(f, fr_group), i); if (i + 1 < incount) fprintf(fp, ", "); i++; @@ -534,9 +540,8 @@ u_int incount, outcount; if ((i & 1) == 0) { fprintf(fp, "\n\t"); } - fprintf(fp, - "(frentry_t *)&out_rule_%s_%d", - f->fr_group, i); + fprintf(fp, "(frentry_t *)&out_rule_%s_%d", + FR_NAME(f, fr_group), i); if (i + 1 < outcount) fprintf(fp, ", "); i++; @@ -586,7 +591,7 @@ u_int incount, outcount; switch(m[i].c) { case FRC_IFN : - if (*fr->fr_ifname) + if (fr->fr_ifnames[0] != -1) m[i].s = 1; break; case FRC_V : @@ -940,11 +945,11 @@ u_int incount, outcount; if (fr->fr_flags & FR_QUICK) { fprintf(fp, "return (frentry_t *)&%s_rule_%s_%d;\n", fr->fr_flags & FR_INQUE ? "in" : "out", - fr->fr_group, num); + FR_NAME(fr, fr_group), num); } else { fprintf(fp, "fr = (frentry_t *)&%s_rule_%s_%d;\n", fr->fr_flags & FR_INQUE ? "in" : "out", - fr->fr_group, num); + FR_NAME(fr, fr_group), num); } if (n == NULL) n = (mc_t *)malloc(sizeof(*n) * FRC_MAX); @@ -954,7 +959,7 @@ u_int incount, outcount; void printC(dir) -int dir; + int dir; { static mc_t *m = NULL; frgroup_t *g; @@ -977,10 +982,10 @@ int dir; * Now print out code to implement all of the rules. */ static void printCgroup(dir, top, m, group) -int dir; -frentry_t *top; -mc_t *m; -char *group; + int dir; + frentry_t *top; + mc_t *m; + char *group; { frentry_t *fr, *fr1; int i, n, rn; @@ -1027,13 +1032,14 @@ char *group; continue; if ((n & 0x0001) && - !strcmp(fr1->fr_ifname, fr->fr_ifname)) { + !strcmp(fr1->fr_names + fr1->fr_ifnames[0], + fr->fr_names + fr->fr_ifnames[0])) { m[FRC_IFN].e++; m[FRC_IFN].n++; } else n &= ~0x0001; - if ((n & 0x0002) && (fr1->fr_v == fr->fr_v)) { + if ((n & 0x0002) && (fr1->fr_family == fr->fr_family)) { m[FRC_V].e++; m[FRC_V].n++; } else @@ -1226,10 +1232,10 @@ char *group; } static void printhooks(fp, in, out, grp) -FILE *fp; -int in; -int out; -frgroup_t *grp; + FILE *fp; + int in; + int out; + frgroup_t *grp; { frentry_t *fr; char *group; @@ -1237,7 +1243,7 @@ frgroup_t *grp; char *instr; group = grp->fg_name; - dogrp = *group ? 1 : 0; + dogrp = 0; if (in && out) { fprintf(stderr, @@ -1283,18 +1289,24 @@ int ipfrule_add_%s_%s()\n", instr, group); fprintf(fp, "\ for (j = i + 1; j < max; j++)\n\ - if (strncmp(fp->fr_group,\n\ + if (strncmp(fp->fr_names + fp->fr_group,\n\ + ipf_rules_%s_%s[j]->fr_names +\n\ ipf_rules_%s_%s[j]->fr_group,\n\ FR_GROUPLEN) == 0) {\n\ + if (ipf_rules_%s_%s[j] != NULL)\n\ + ipf_rules_%s_%s[j]->fr_pnext =\n\ + &fp->fr_next;\n\ + fp->fr_pnext = &ipf_rules_%s_%s[j];\n\ fp->fr_next = ipf_rules_%s_%s[j];\n\ break;\n\ - }\n", instr, group, instr, group); + }\n", instr, group, instr, group, instr, group, + instr, group, instr, group, instr, group); if (dogrp) fprintf(fp, "\ \n\ - if (fp->fr_grhead != 0) {\n\ - fg = fr_addgroup(fp->fr_grhead, fp, FR_INQUE,\n\ - IPL_LOGIPF, 0);\n\ + if (fp->fr_grhead != -1) {\n\ + fg = fr_addgroup(fp->fr_names + fp->fr_grhead,\n\ + fp, FR_INQUE, IPL_LOGIPF, 0);\n\ if (fg != NULL)\n\ fp->fr_grp = &fg->fg_start;\n\ }\n"); @@ -1304,7 +1316,7 @@ int ipfrule_add_%s_%s()\n", instr, group); fp = &ipfrule_%s_%s;\n", instr, group); fprintf(fp, "\ bzero((char *)fp, sizeof(*fp));\n\ - fp->fr_type = FR_T_CALLFUNC|FR_T_BUILTIN;\n\ + fp->fr_type = FR_T_CALLFUNC_BUILTIN;\n\ fp->fr_flags = FR_%sQUE|FR_NOMATCH;\n\ fp->fr_data = (void *)ipf_rules_%s_%s[0];\n", (in != 0) ? "IN" : "OUT", instr, group); @@ -1313,9 +1325,10 @@ int ipfrule_add_%s_%s()\n", instr, group); instr, group); fprintf(fp, "\ - fp->fr_v = 4;\n\ + fp->fr_family = AF_INET;\n\ fp->fr_func = (ipfunc_t)ipfrule_match_%s_%s;\n\ - err = frrequest(IPL_LOGIPF, SIOCADDFR, (caddr_t)fp, fr_active, 0);\n", + err = frrequest(&ipfmain, IPL_LOGIPF, SIOCADDFR, (caddr_t)fp,\n\ + ipfmain.ipf_active, 0);\n", instr, group); fprintf(fp, "\treturn err;\n}\n"); @@ -1348,8 +1361,9 @@ int ipfrule_remove_%s_%s()\n", instr, group); }\n\ }\n\ if (err == 0)\n\ - err = frrequest(IPL_LOGIPF, SIOCDELFR,\n\ - (caddr_t)&ipfrule_%s_%s, fr_active, 0);\n", + err = frrequest(&ipfmain, IPL_LOGIPF, SIOCDELFR,\n\ + (caddr_t)&ipfrule_%s_%s,\n\ + ipfmain.ipf_active, 0);\n", instr, group, instr, group, instr, group); fprintf(fp, "\ if (err)\n\ diff --git a/contrib/ipfilter/tools/ipfs.c b/contrib/ipfilter/tools/ipfs.c index eab650a7ee3..b5484be2193 100644 --- a/contrib/ipfilter/tools/ipfs.c +++ b/contrib/ipfilter/tools/ipfs.c @@ -1,7 +1,7 @@ /* $FreeBSD$ */ /* - * Copyright (C) 2001-2006 by Darren Reed. + * Copyright (C) 2012 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. */ @@ -44,7 +44,7 @@ #include "netinet/ipl.h" #if !defined(lint) -static const char rcsid[] = "@(#)Id: ipfs.c,v 1.12 2003/12/01 01:56:53 darrenr Exp"; +static const char rcsid[] = "@(#)$Id$"; #endif #ifndef IPF_SAVEDIR @@ -100,7 +100,7 @@ void usage() * Change interface names in state information saved out to disk. */ int changestateif(ifs, fname) -char *ifs, *fname; + char *ifs, *fname; { int fd, olen, nlen, rw; ipstate_save_t ips; @@ -163,7 +163,7 @@ char *ifs, *fname; * Change interface names in NAT information saved out to disk. */ int changenatif(ifs, fname) -char *ifs, *fname; + char *ifs, *fname; { int fd, olen, nlen, rw; nat_save_t ipn; @@ -198,14 +198,6 @@ char *ifs, *fname; strcpy(nat->nat_ifnames[1], s); rw = 1; } - if (!strncmp(nat->nat_ifnames[2], ifs, olen + 1)) { - strcpy(nat->nat_ifnames[2], s); - rw = 1; - } - if (!strncmp(nat->nat_ifnames[3], ifs, olen + 1)) { - strcpy(nat->nat_ifnames[3], s); - rw = 1; - } if (rw == 1) { if (lseek(fd, pos, SEEK_SET) != pos) { perror("lseek"); @@ -225,8 +217,8 @@ char *ifs, *fname; int main(argc,argv) -int argc; -char *argv[]; + int argc; + char *argv[]; { int c, lock = -1, devfd = -1, err = 0, rw = -1, ns = -1, set = 0; char *dirname = NULL, *filename = NULL, *ifs = NULL; @@ -356,7 +348,7 @@ char *argv[]; int opendevice(ipfdev) -char *ipfdev; + char *ipfdev; { int fd = -1; @@ -374,14 +366,14 @@ char *ipfdev; void closedevice(fd) -int fd; + int fd; { close(fd); } int setlock(fd, lock) -int fd, lock; + int fd, lock; { if (opts & OPT_VERBOSE) printf("Turn lock %s\n", lock ? "on" : "off"); @@ -398,8 +390,8 @@ int fd, lock; int writestate(fd, file) -int fd; -char *file; + int fd; + char *file; { ipstate_save_t ips, *ipsp; ipfobj_t obj; @@ -450,8 +442,8 @@ char *file; int readstate(fd, file) -int fd; -char *file; + int fd; + char *file; { ipstate_save_t ips, *is, *ipshead = NULL, *is1, *ipstail = NULL; int sfd = -1, i; @@ -567,8 +559,8 @@ freeipshead: int readnat(fd, file) -int fd; -char *file; + int fd; + char *file; { nat_save_t ipn, *in, *ipnhead = NULL, *in1, *ipntail = NULL; ipfobj_t obj; @@ -714,8 +706,8 @@ freenathead: int writenat(fd, file) -int fd; -char *file; + int fd; + char *file; { nat_save_t *ipnp = NULL, *next = NULL; ipfobj_t obj; @@ -798,7 +790,7 @@ char *file; int writeall(dirname) -char *dirname; + char *dirname; { int fd, devfd; @@ -849,7 +841,7 @@ bad: int readall(dirname) -char *dirname; + char *dirname; { int fd, devfd; diff --git a/contrib/ipfilter/tools/ipfstat.c b/contrib/ipfilter/tools/ipfstat.c index 3c5bfdd50ac..3261cef8e4d 100644 --- a/contrib/ipfilter/tools/ipfstat.c +++ b/contrib/ipfilter/tools/ipfstat.c @@ -1,7 +1,7 @@ /* $FreeBSD$ */ /* - * Copyright (C) 2002-2006 by Darren Reed. + * Copyright (C) 2012 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. */ @@ -15,6 +15,7 @@ # endif #endif #include +#include #include #ifdef linux # include @@ -71,7 +72,7 @@ #if !defined(lint) static const char sccsid[] = "@(#)fils.c 1.21 4/20/96 (C) 1993-2000 Darren Reed"; -static const char rcsid[] = "@(#)$Id: ipfstat.c,v 1.44.2.25 2007/06/30 09:48:50 darrenr Exp $"; +static const char rcsid[] = "@(#)$Id$"; #endif #ifdef __hpux @@ -87,7 +88,9 @@ extern int opterr; static char *filters[4] = { "ipfilter(in)", "ipfilter(out)", "ipacct(in)", "ipacct(out)" }; static int state_logging = -1; +static wordtab_t *state_fields = NULL; +int nohdrfields = 0; int opts = 0; int use_inet6 = 0; int live_kernel = 1; @@ -98,6 +101,26 @@ int nat_fd = -1; frgroup_t *grtop = NULL; frgroup_t *grtail = NULL; +char *blockreasons[FRB_MAX_VALUE + 1] = { + "packet blocked", + "log rule failure", + "pps rate exceeded", + "jumbogram", + "makefrip failed", + "cannot add state", + "IP ID update failed", + "log-or-block failed", + "decapsulate failure", + "cannot create new auth entry", + "packet queued for auth", + "buffer coalesce failure", + "buffer pullup failure", + "auth feedback", + "bad fragment", + "IPv4 NAT failure", + "IPv6 NAT failure" +}; + #ifdef STATETOP #define STSTRSIZE 80 #define STGROWSIZE 16 @@ -135,22 +158,27 @@ static int fetchfrag __P((int, int, ipfr_t *)); static void showstats __P((friostat_t *, u_32_t)); static void showfrstates __P((ipfrstat_t *, u_long)); static void showlist __P((friostat_t *)); -static void showipstates __P((ips_stat_t *)); -static void showauthstates __P((fr_authstat_t *)); +static void showstatestats __P((ips_stat_t *)); +static void showipstates __P((ips_stat_t *, int *)); +static void showauthstates __P((ipf_authstat_t *)); +static void showtqtable_live __P((int)); static void showgroups __P((friostat_t *)); static void usage __P((char *)); -static void showtqtable_live __P((int)); -static void printlivelist __P((int, int, frentry_t *, char *, char *)); -static void printdeadlist __P((int, int, frentry_t *, char *, char *)); +static int state_matcharray __P((ipstate_t *, int *)); +static int printlivelist __P((friostat_t *, int, int, frentry_t *, + char *, char *)); +static void printdeadlist __P((friostat_t *, int, int, frentry_t *, + char *, char *)); +static void printside __P((char *, ipf_statistics_t *)); static void parse_ipportstr __P((const char *, i6addr_t *, int *)); static void ipfstate_live __P((char *, friostat_t **, ips_stat_t **, - ipfrstat_t **, fr_authstat_t **, u_32_t *)); + ipfrstat_t **, ipf_authstat_t **, u_32_t *)); static void ipfstate_dead __P((char *, friostat_t **, ips_stat_t **, - ipfrstat_t **, fr_authstat_t **, u_32_t *)); + ipfrstat_t **, ipf_authstat_t **, u_32_t *)); static ipstate_t *fetchstate __P((ipstate_t *, ipstate_t *)); #ifdef STATETOP static void topipstates __P((i6addr_t, i6addr_t, int, int, int, - int, int, int)); + int, int, int, int *)); static void sig_break __P((int)); static void sig_resize __P((int)); static char *getip __P((int, i6addr_t *)); @@ -167,7 +195,7 @@ static int sort_dstpt __P((const void *, const void *)); static void usage(name) -char *name; + char *name; { #ifdef USE_INET6 fprintf(stderr, "Usage: %s [-6aAdfghIilnoRsv]\n", name); @@ -186,20 +214,23 @@ char *name; int main(argc,argv) -int argc; -char *argv[]; + int argc; + char *argv[]; { - fr_authstat_t frauthst; - fr_authstat_t *frauthstp = &frauthst; + ipf_authstat_t frauthst; + ipf_authstat_t *frauthstp = &frauthst; friostat_t fio; friostat_t *fiop = &fio; ips_stat_t ipsst; ips_stat_t *ipsstp = &ipsst; ipfrstat_t ifrst; ipfrstat_t *ifrstp = &ifrst; - char *memf = NULL; - char *options, *kern = NULL; - int c, myoptind; + char *options; + char *kern = NULL; + char *memf = NULL; + int c; + int myoptind; + int *filter = NULL; int protocol = -1; /* -1 = wild card for any protocol */ int refreshtime = 1; /* default update time */ @@ -210,9 +241,9 @@ char *argv[]; u_32_t frf; #ifdef USE_INET6 - options = "6aACdfghIilnostvD:M:N:P:RS:T:"; + options = "6aACdfghIilnostvD:m:M:N:O:P:RS:T:"; #else - options = "aACdfghIilnostvD:M:N:P:RS:T:"; + options = "aACdfghIilnostvD:m:M:N:O:P:RS:T:"; #endif saddr.in4.s_addr = INADDR_ANY; /* default any v4 source addr */ @@ -324,6 +355,14 @@ char *argv[]; case 'l' : opts |= OPT_SHOWLIST; break; + case 'm' : + filter = parseipfexpr(optarg, NULL); + if (filter == NULL) { + fprintf(stderr, "Error parseing '%s'\n", + optarg); + exit(1); + } + break; case 'M' : break; case 'N' : @@ -334,6 +373,9 @@ char *argv[]; case 'o' : opts |= OPT_OUTQUE|OPT_SHOWLIST; break; + case 'O' : + state_fields = parsefields(statefields, optarg); + break; case 'P' : protocol = getproto(optarg); if (protocol == -1) { @@ -386,11 +428,12 @@ char *argv[]; ipfstate_live(IPL_NAME, &fiop, &ipsstp, &ifrstp, &frauthstp, &frf); - } else + } else { ipfstate_dead(kern, &fiop, &ipsstp, &ifrstp, &frauthstp, &frf); + } if (opts & OPT_IPSTATES) { - showipstates(ipsstp); + showipstates(ipsstp, filter); } else if (opts & OPT_SHOWLIST) { showlist(fiop); if ((opts & OPT_OUTQUE) && (opts & OPT_INQUE)){ @@ -402,7 +445,7 @@ char *argv[]; #ifdef STATETOP else if (opts & OPT_STATETOP) topipstates(saddr, daddr, sport, dport, protocol, - use_inet6 ? 6 : 4, refreshtime, topclosed); + use_inet6 ? 6 : 4, refreshtime, topclosed, filter); #endif else if (opts & OPT_AUTHSTATS) showauthstates(frauthstp); @@ -420,12 +463,12 @@ char *argv[]; * of ioctl's and copying directly from kernel memory. */ static void ipfstate_live(device, fiopp, ipsstpp, ifrstpp, frauthstpp, frfp) -char *device; -friostat_t **fiopp; -ips_stat_t **ipsstpp; -ipfrstat_t **ifrstpp; -fr_authstat_t **frauthstpp; -u_32_t *frfp; + char *device; + friostat_t **fiopp; + ips_stat_t **ipsstpp; + ipfrstat_t **ifrstpp; + ipf_authstat_t **frauthstpp; + u_32_t *frfp; { ipfobj_t ipfo; @@ -442,12 +485,12 @@ u_32_t *frfp; ipfo.ipfo_ptr = (void *)*fiopp; if (ioctl(ipf_fd, SIOCGETFS, &ipfo) == -1) { - perror("ioctl(ipf:SIOCGETFS)"); + ipferror(ipf_fd, "ioctl(ipf:SIOCGETFS)"); exit(-1); } if (ioctl(ipf_fd, SIOCGETFF, frfp) == -1) - perror("ioctl(SIOCGETFF)"); + ipferror(ipf_fd, "ioctl(SIOCGETFF)"); } if ((opts & OPT_IPSTATES) != 0) { @@ -459,11 +502,11 @@ u_32_t *frfp; ipfo.ipfo_ptr = (void *)*ipsstpp; if ((ioctl(state_fd, SIOCGETFS, &ipfo) == -1)) { - perror("ioctl(state:SIOCGETFS)"); + ipferror(state_fd, "ioctl(state:SIOCGETFS)"); exit(-1); } if (ioctl(state_fd, SIOCGETLG, &state_logging) == -1) { - perror("ioctl(state:SIOCGETLG)"); + ipferror(state_fd, "ioctl(state:SIOCGETLG)"); exit(-1); } } @@ -474,9 +517,9 @@ u_32_t *frfp; ipfo.ipfo_type = IPFOBJ_FRAGSTAT; ipfo.ipfo_size = sizeof(ipfrstat_t); ipfo.ipfo_ptr = (void *)*ifrstpp; - + if (ioctl(ipf_fd, SIOCGFRST, &ipfo) == -1) { - perror("ioctl(SIOCGFRST)"); + ipferror(ipf_fd, "ioctl(SIOCGFRST)"); exit(-1); } } @@ -488,11 +531,11 @@ u_32_t *frfp; bzero((caddr_t)&ipfo, sizeof(ipfo)); ipfo.ipfo_rev = IPFILTER_VERSION; ipfo.ipfo_type = IPFOBJ_AUTHSTAT; - ipfo.ipfo_size = sizeof(fr_authstat_t); + ipfo.ipfo_size = sizeof(ipf_authstat_t); ipfo.ipfo_ptr = (void *)*frauthstpp; if (ioctl(auth_fd, SIOCATHST, &ipfo) == -1) { - perror("ioctl(SIOCATHST)"); + ipferror(auth_fd, "ioctl(SIOCATHST)"); exit(-1); } } @@ -505,66 +548,64 @@ u_32_t *frfp; * just won't work any more. */ static void ipfstate_dead(kernel, fiopp, ipsstpp, ifrstpp, frauthstpp, frfp) -char *kernel; -friostat_t **fiopp; -ips_stat_t **ipsstpp; -ipfrstat_t **ifrstpp; -fr_authstat_t **frauthstpp; -u_32_t *frfp; + char *kernel; + friostat_t **fiopp; + ips_stat_t **ipsstpp; + ipfrstat_t **ifrstpp; + ipf_authstat_t **frauthstpp; + u_32_t *frfp; { - static fr_authstat_t frauthst, *frauthstp; + static ipf_authstat_t frauthst, *frauthstp; + static ipftq_t ipstcptab[IPF_TCP_NSTATES]; static ips_stat_t ipsst, *ipsstp; static ipfrstat_t ifrst, *ifrstp; static friostat_t fio, *fiop; - static ipftq_t ipssttab[IPF_TCP_NSTATES]; int temp; void *rules[2][2]; struct nlist deadlist[44] = { - { "fr_authstats" }, /* 0 */ - { "fae_list" }, - { "ipauth" }, - { "fr_authlist" }, - { "fr_authstart" }, - { "fr_authend" }, /* 5 */ - { "fr_authnext" }, - { "fr_auth" }, - { "fr_authused" }, - { "fr_authsize" }, - { "fr_defaultauthage" }, /* 10 */ - { "fr_authpkts" }, - { "fr_auth_lock" }, - { "frstats" }, - { "ips_stats" }, - { "ips_num" }, /* 15 */ - { "ips_wild" }, - { "ips_list" }, - { "ips_table" }, - { "fr_statemax" }, - { "fr_statesize" }, /* 20 */ - { "fr_state_doflush" }, - { "fr_state_lock" }, - { "ipfr_heads" }, - { "ipfr_nattab" }, - { "ipfr_stats" }, /* 25 */ - { "ipfr_inuse" }, - { "fr_ipfrttl" }, - { "fr_frag_lock" }, - { "ipfr_timer_id" }, - { "fr_nat_lock" }, /* 30 */ - { "ipfilter" }, - { "ipfilter6" }, - { "ipacct" }, - { "ipacct6" }, - { "ipl_frouteok" }, /* 35 */ - { "fr_running" }, - { "ipfgroups" }, - { "fr_active" }, - { "fr_pass" }, - { "fr_flags" }, /* 40 */ - { "ipstate_logging" }, - { "ips_tqtqb" }, - { NULL } + { "ipf_auth_stats", 0, 0, 0, 0 }, /* 0 */ + { "fae_list", 0, 0, 0, 0 }, + { "ipauth", 0, 0, 0, 0 }, + { "ipf_auth_list", 0, 0, 0, 0 }, + { "ipf_auth_start", 0, 0, 0, 0 }, + { "ipf_auth_end", 0, 0, 0, 0 }, /* 5 */ + { "ipf_auth_next", 0, 0, 0, 0 }, + { "ipf_auth", 0, 0, 0, 0 }, + { "ipf_auth_used", 0, 0, 0, 0 }, + { "ipf_auth_size", 0, 0, 0, 0 }, + { "ipf_auth_defaultage", 0, 0, 0, 0 }, /* 10 */ + { "ipf_auth_pkts", 0, 0, 0, 0 }, + { "ipf_auth_lock", 0, 0, 0, 0 }, + { "frstats", 0, 0, 0, 0 }, + { "ips_stats", 0, 0, 0, 0 }, + { "ips_num", 0, 0, 0, 0 }, /* 15 */ + { "ips_wild", 0, 0, 0, 0 }, + { "ips_list", 0, 0, 0, 0 }, + { "ips_table", 0, 0, 0, 0 }, + { "ipf_state_max", 0, 0, 0, 0 }, + { "ipf_state_size", 0, 0, 0, 0 }, /* 20 */ + { "ipf_state_doflush", 0, 0, 0, 0 }, + { "ipf_state_lock", 0, 0, 0, 0 }, + { "ipfr_heads", 0, 0, 0, 0 }, + { "ipfr_nattab", 0, 0, 0, 0 }, + { "ipfr_stats", 0, 0, 0, 0 }, /* 25 */ + { "ipfr_inuse", 0, 0, 0, 0 }, + { "ipf_ipfrttl", 0, 0, 0, 0 }, + { "ipf_frag_lock", 0, 0, 0, 0 }, + { "ipfr_timer_id", 0, 0, 0, 0 }, + { "ipf_nat_lock", 0, 0, 0, 0 }, /* 30 */ + { "ipf_rules", 0, 0, 0, 0 }, + { "ipf_acct", 0, 0, 0, 0 }, + { "ipl_frouteok", 0, 0, 0, 0 }, + { "ipf_running", 0, 0, 0, 0 }, + { "ipf_groups", 0, 0, 0, 0 }, /* 35 */ + { "ipf_active", 0, 0, 0, 0 }, + { "ipf_pass", 0, 0, 0, 0 }, + { "ipf_flags", 0, 0, 0, 0 }, + { "ipf_state_logging", 0, 0, 0, 0 }, + { "ips_tqtqb", 0, 0, 0, 0 }, /* 40 */ + { NULL, 0, 0, 0, 0 } }; @@ -617,23 +658,6 @@ u_32_t *frfp; fiop->f_fout[0] = rules[1][0]; fiop->f_fout[1] = rules[1][1]; - /* - * Same for IPv6, except make them null if support for it is not - * being compiled in. - */ -#ifdef USE_INET6 - kmemcpy((char *)&rules, (u_long)deadlist[32].n_value, sizeof(rules)); - fiop->f_fin6[0] = rules[0][0]; - fiop->f_fin6[1] = rules[0][1]; - fiop->f_fout6[0] = rules[1][0]; - fiop->f_fout6[1] = rules[1][1]; -#else - fiop->f_fin6[0] = NULL; - fiop->f_fin6[1] = NULL; - fiop->f_fout6[0] = NULL; - fiop->f_fout6[1] = NULL; -#endif - /* * Now get accounting rules pointers. */ @@ -643,32 +667,19 @@ u_32_t *frfp; fiop->f_acctout[0] = rules[1][0]; fiop->f_acctout[1] = rules[1][1]; -#ifdef USE_INET6 - kmemcpy((char *)&rules, (u_long)deadlist[34].n_value, sizeof(rules)); - fiop->f_acctin6[0] = rules[0][0]; - fiop->f_acctin6[1] = rules[0][1]; - fiop->f_acctout6[0] = rules[1][0]; - fiop->f_acctout6[1] = rules[1][1]; -#else - fiop->f_acctin6[0] = NULL; - fiop->f_acctin6[1] = NULL; - fiop->f_acctout6[0] = NULL; - fiop->f_acctout6[1] = NULL; -#endif - /* * A collection of "global" variables used inside the kernel which * are all collected in friostat_t via ioctl. */ - kmemcpy((char *)&fiop->f_froute, (u_long)deadlist[35].n_value, + kmemcpy((char *)&fiop->f_froute, (u_long)deadlist[33].n_value, sizeof(fiop->f_froute)); - kmemcpy((char *)&fiop->f_running, (u_long)deadlist[36].n_value, + kmemcpy((char *)&fiop->f_running, (u_long)deadlist[34].n_value, sizeof(fiop->f_running)); - kmemcpy((char *)&fiop->f_groups, (u_long)deadlist[37].n_value, + kmemcpy((char *)&fiop->f_groups, (u_long)deadlist[35].n_value, sizeof(fiop->f_groups)); - kmemcpy((char *)&fiop->f_active, (u_long)deadlist[38].n_value, + kmemcpy((char *)&fiop->f_active, (u_long)deadlist[36].n_value, sizeof(fiop->f_active)); - kmemcpy((char *)&fiop->f_defpass, (u_long)deadlist[39].n_value, + kmemcpy((char *)&fiop->f_defpass, (u_long)deadlist[37].n_value, sizeof(fiop->f_defpass)); /* @@ -676,12 +687,12 @@ u_32_t *frfp; */ kmemcpy((char *)ipsstp, (u_long)deadlist[14].n_value, sizeof(*ipsstp)); kmemcpy((char *)&temp, (u_long)deadlist[15].n_value, sizeof(temp)); - kmemcpy((char *)ipssttab, (u_long)deadlist[42].n_value, - sizeof(ipssttab)); + kmemcpy((char *)ipstcptab, (u_long)deadlist[40].n_value, + sizeof(ipstcptab)); ipsstp->iss_active = temp; ipsstp->iss_table = (void *)deadlist[18].n_value; ipsstp->iss_list = (void *)deadlist[17].n_value; - ipsstp->iss_tcptab = ipssttab; + ipsstp->iss_tcptab = ipstcptab; /* * Build up the authentiation information stats structure. @@ -708,65 +719,62 @@ u_32_t *frfp; } +static void printside(side, frs) + char *side; + ipf_statistics_t *frs; +{ + int i; + + PRINTF("%lu\t%s bad packets\n", frs->fr_bad, side); +#ifdef USE_INET6 + PRINTF("%lu\t%s IPv6 packets\n", frs->fr_ipv6, side); +#endif + PRINTF("%lu\t%s packets blocked\n", frs->fr_block, side); + PRINTF("%lu\t%s packets passed\n", frs->fr_pass, side); + PRINTF("%lu\t%s packets not matched\n", frs->fr_nom, side); + PRINTF("%lu\t%s packets counted\n", frs->fr_acct, side); + PRINTF("%lu\t%s packets short\n", frs->fr_short, side); + PRINTF("%lu\t%s packets logged and blocked\n", frs->fr_bpkl, side); + PRINTF("%lu\t%s packets logged and passed\n", frs->fr_ppkl, side); + PRINTF("%lu\t%s fragment state kept\n", frs->fr_nfr, side); + PRINTF("%lu\t%s fragment state lost\n", frs->fr_bnfr, side); + PRINTF("%lu\t%s packet state kept\n", frs->fr_ads, side); + PRINTF("%lu\t%s packet state lost\n", frs->fr_bads, side); + PRINTF("%lu\t%s invalid source\n", frs->fr_v4_badsrc, side); + PRINTF("%lu\t%s cache hits\n", frs->fr_chit, side); + PRINTF("%lu\t%s cache misses\n", frs->fr_cmiss, side); + PRINTF("%lu\t%s bad coalesces\n", frs->fr_badcoalesces, side); + PRINTF("%lu\t%s pullups succeeded\n", frs->fr_pull[0], side); + PRINTF("%lu\t%s pullups failed\n", frs->fr_pull[1], side); + PRINTF("%lu\t%s TCP checksum failures\n", frs->fr_tcpbad, side); + for (i = 0; i <= FRB_MAX_VALUE; i++) + PRINTF("%lu\t%s block reason %s\n", + frs->fr_blocked[i], side, blockreasons[i]); +} + + /* * Display the kernel stats for packets blocked and passed and other * associated running totals which are kept. */ static void showstats(fp, frf) -struct friostat *fp; -u_32_t frf; + struct friostat *fp; + u_32_t frf; { + printside("input", &fp->f_st[0]); + printside("output", &fp->f_st[1]); - PRINTF("bad packets:\t\tin %lu\tout %lu\n", - fp->f_st[0].fr_bad, fp->f_st[1].fr_bad); -#ifdef USE_INET6 - PRINTF(" IPv6 packets:\t\tin %lu out %lu\n", - fp->f_st[0].fr_ipv6, fp->f_st[1].fr_ipv6); -#endif - PRINTF(" input packets:\t\tblocked %lu passed %lu nomatch %lu", - fp->f_st[0].fr_block, fp->f_st[0].fr_pass, - fp->f_st[0].fr_nom); - PRINTF(" counted %lu short %lu\n", - fp->f_st[0].fr_acct, fp->f_st[0].fr_short); - PRINTF("output packets:\t\tblocked %lu passed %lu nomatch %lu", - fp->f_st[1].fr_block, fp->f_st[1].fr_pass, - fp->f_st[1].fr_nom); - PRINTF(" counted %lu short %lu\n", - fp->f_st[1].fr_acct, fp->f_st[1].fr_short); - PRINTF(" input packets logged:\tblocked %lu passed %lu\n", - fp->f_st[0].fr_bpkl, fp->f_st[0].fr_ppkl); - PRINTF("output packets logged:\tblocked %lu passed %lu\n", - fp->f_st[1].fr_bpkl, fp->f_st[1].fr_ppkl); - PRINTF(" packets logged:\tinput %lu output %lu\n", - fp->f_st[0].fr_pkl, fp->f_st[1].fr_pkl); - PRINTF(" log failures:\t\tinput %lu output %lu\n", - fp->f_st[0].fr_skip, fp->f_st[1].fr_skip); - PRINTF("fragment state(in):\tkept %lu\tlost %lu\tnot fragmented %lu\n", - fp->f_st[0].fr_nfr, fp->f_st[0].fr_bnfr, - fp->f_st[0].fr_cfr); - PRINTF("fragment state(out):\tkept %lu\tlost %lu\tnot fragmented %lu\n", - fp->f_st[1].fr_nfr, fp->f_st[1].fr_bnfr, - fp->f_st[0].fr_cfr); - PRINTF("packet state(in):\tkept %lu\tlost %lu\n", - fp->f_st[0].fr_ads, fp->f_st[0].fr_bads); - PRINTF("packet state(out):\tkept %lu\tlost %lu\n", - fp->f_st[1].fr_ads, fp->f_st[1].fr_bads); - PRINTF("ICMP replies:\t%lu\tTCP RSTs sent:\t%lu\n", - fp->f_st[0].fr_ret, fp->f_st[1].fr_ret); - PRINTF("Invalid source(in):\t%lu\n", fp->f_st[0].fr_badsrc); - PRINTF("Result cache hits(in):\t%lu\t(out):\t%lu\n", - fp->f_st[0].fr_chit, fp->f_st[1].fr_chit); - PRINTF("IN Pullups succeeded:\t%lu\tfailed:\t%lu\n", - fp->f_st[0].fr_pull[0], fp->f_st[0].fr_pull[1]); - PRINTF("OUT Pullups succeeded:\t%lu\tfailed:\t%lu\n", - fp->f_st[1].fr_pull[0], fp->f_st[1].fr_pull[1]); - PRINTF("Fastroute successes:\t%lu\tfailures:\t%lu\n", - fp->f_froute[0], fp->f_froute[1]); - PRINTF("TCP cksum fails(in):\t%lu\t(out):\t%lu\n", - fp->f_st[0].fr_tcpbad, fp->f_st[1].fr_tcpbad); - PRINTF("IPF Ticks:\t%lu\n", fp->f_ticks); + PRINTF("%lu\tpackets logged\n", fp->f_log_ok); + PRINTF("%lu\tlog failures\n", fp->f_log_fail); + PRINTF("%lu\tred-black no memory\n", fp->f_rb_no_mem); + PRINTF("%lu\tred-black node maximum\n", fp->f_rb_node_max); + PRINTF("%lu\tICMP replies sent\n", fp->f_st[0].fr_ret); + PRINTF("%lu\tTCP RSTs sent\n", fp->f_st[1].fr_ret); + PRINTF("%lu\tfastroute successes\n", fp->f_froute[0]); + PRINTF("%lu\tfastroute failures\n", fp->f_froute[1]); + PRINTF("%u\tIPF Ticks\n", fp->f_ticks); - PRINTF("Packet log flags set: (%#x)\n", frf); + PRINTF("%x\tPacket log flags set:\n", frf); if (frf & FF_LOGPASS) PRINTF("\tpackets passed through filter\n"); if (frf & FF_LOGBLOCK) @@ -781,30 +789,27 @@ u_32_t frf; /* * Print out a list of rules from the kernel, starting at the one passed. */ -static void printlivelist(out, set, fp, group, comment) -int out, set; -frentry_t *fp; -char *group, *comment; +static int +printlivelist(fiop, out, set, fp, group, comment) + struct friostat *fiop; + int out, set; + frentry_t *fp; + char *group, *comment; { struct frentry fb; ipfruleiter_t rule; frentry_t zero; frgroup_t *g; ipfobj_t obj; - int n; + int rules; + int num; - if (use_inet6 == 1) - fb.fr_v = 6; - else - fb.fr_v = 4; - fb.fr_next = fp; - n = 0; + rules = 0; rule.iri_inout = out; rule.iri_active = set; rule.iri_rule = &fb; rule.iri_nrules = 1; - rule.iri_v = use_inet6 ? 6 : 4; if (group != NULL) strncpy(rule.iri_group, group, FR_GROUPLEN); else @@ -818,49 +823,65 @@ char *group, *comment; obj.ipfo_size = sizeof(rule); obj.ipfo_ptr = &rule; - do { + while (rule.iri_rule != NULL) { u_long array[1000]; memset(array, 0xff, sizeof(array)); fp = (frentry_t *)array; rule.iri_rule = fp; if (ioctl(ipf_fd, SIOCIPFITER, &obj) == -1) { - perror("ioctl(SIOCIPFITER)"); - n = IPFGENITER_IPF; - ioctl(ipf_fd, SIOCIPFDELTOK, &n); - return; + ipferror(ipf_fd, "ioctl(SIOCIPFITER)"); + num = IPFGENITER_IPF; + (void) ioctl(ipf_fd,SIOCIPFDELTOK, &num); + return rules; } if (bcmp(fp, &zero, sizeof(zero)) == 0) break; + if (rule.iri_rule == NULL) + break; +#ifdef USE_INET6 + if (use_inet6 != 0) { + if (fp->fr_family != 0 && fp->fr_family != AF_INET6) + continue; + } else +#endif + { + if (fp->fr_family != 0 && fp->fr_family != AF_INET) + continue; + } if (fp->fr_data != NULL) - fp->fr_data = (char *)fp + sizeof(*fp); + fp->fr_data = (char *)fp + fp->fr_size; - n++; + rules++; - if (opts & (OPT_HITS|OPT_VERBOSE)) + if (opts & (OPT_HITS|OPT_DEBUG)) #ifdef USE_QUAD_T - PRINTF("%qu ", (unsigned long long) fp->fr_hits); + PRINTF("%"PRIu64" ", (unsigned long long) fp->fr_hits); #else PRINTF("%lu ", fp->fr_hits); #endif - if (opts & (OPT_ACCNT|OPT_VERBOSE)) + if (opts & (OPT_ACCNT|OPT_DEBUG)) #ifdef USE_QUAD_T - PRINTF("%qu ", (unsigned long long) fp->fr_bytes); + PRINTF("%"PRIu64" ", (unsigned long long) fp->fr_bytes); #else PRINTF("%lu ", fp->fr_bytes); #endif if (opts & OPT_SHOWLINENO) - PRINTF("@%d ", n); + PRINTF("@%d ", rules); + + if (fp->fr_die != 0) + fp->fr_die -= fiop->f_ticks; printfr(fp, ioctl); if (opts & OPT_DEBUG) { - binprint(fp, sizeof(*fp)); + binprint(fp, fp->fr_size); if (fp->fr_data != NULL && fp->fr_dsize > 0) binprint(fp->fr_data, fp->fr_dsize); } - if (fp->fr_grhead[0] != '\0') { + if (fp->fr_grhead != -1) { for (g = grtop; g != NULL; g = g->fg_next) { - if (!strncmp(fp->fr_grhead, g->fg_name, + if (!strncmp(fp->fr_names + fp->fr_grhead, + g->fg_name, FR_GROUPLEN)) break; } @@ -868,7 +889,8 @@ char *group, *comment; g = calloc(1, sizeof(*g)); if (g != NULL) { - strncpy(g->fg_name, fp->fr_grhead, + strncpy(g->fg_name, + fp->fr_names + fp->fr_grhead, FR_GROUPLEN); if (grtop == NULL) { grtop = g; @@ -881,29 +903,23 @@ char *group, *comment; } } if (fp->fr_type == FR_T_CALLFUNC) { - printlivelist(out, set, fp->fr_data, group, - "# callfunc: "); - } - } while (fp->fr_next != NULL); - - n = IPFGENITER_IPF; - ioctl(ipf_fd, SIOCIPFDELTOK, &n); - - if (group == NULL) { - while ((g = grtop) != NULL) { - printf("# Group %s\n", g->fg_name); - printlivelist(out, set, NULL, g->fg_name, comment); - grtop = g->fg_next; - free(g); + rules += printlivelist(fiop, out, set, fp->fr_data, + group, "# callfunc: "); } } + + num = IPFGENITER_IPF; + (void) ioctl(ipf_fd,SIOCIPFDELTOK, &num); + + return rules; } -static void printdeadlist(out, set, fp, group, comment) -int out, set; -frentry_t *fp; -char *group, *comment; +static void printdeadlist(fiop, out, set, fp, group, comment) + friostat_t *fiop; + int out, set; + frentry_t *fp; + char *group, *comment; { frgroup_t *grtop, *grtail, *g; struct frentry fb; @@ -916,13 +932,20 @@ char *group, *comment; grtop = NULL; grtail = NULL; - do { - fp = fb.fr_next; + for (n = 1; fp; fp = fb.fr_next, n++) { if (kmemcpy((char *)&fb, (u_long)fb.fr_next, - sizeof(fb)) == -1) { + fb.fr_size) == -1) { perror("kmemcpy"); return; } + fp = &fb; + if (use_inet6 != 0) { + if (fp->fr_family != 0 && fp->fr_family != 6) + continue; + } else { + if (fp->fr_family != 0 && fp->fr_family != 4) + continue; + } data = NULL; type = fb.fr_type & ~FR_T_BUILTIN; @@ -939,17 +962,15 @@ char *group, *comment; } } - n++; - - if (opts & (OPT_HITS|OPT_VERBOSE)) + if (opts & OPT_HITS) #ifdef USE_QUAD_T - PRINTF("%qu ", (unsigned long long) fb.fr_hits); + PRINTF("%"PRIu64" ", (unsigned long long) fb.fr_hits); #else PRINTF("%lu ", fb.fr_hits); #endif - if (opts & (OPT_ACCNT|OPT_VERBOSE)) + if (opts & OPT_ACCNT) #ifdef USE_QUAD_T - PRINTF("%qu ", (unsigned long long) fb.fr_bytes); + PRINTF("%"PRIu64" ", (unsigned long long) fb.fr_bytes); #else PRINTF("%lu ", fb.fr_bytes); #endif @@ -958,17 +979,17 @@ char *group, *comment; printfr(fp, ioctl); if (opts & OPT_DEBUG) { - binprint(fp, sizeof(*fp)); + binprint(fp, fp->fr_size); if (fb.fr_data != NULL && fb.fr_dsize > 0) binprint(fb.fr_data, fb.fr_dsize); } if (data != NULL) free(data); - if (fb.fr_grhead[0] != '\0') { + if (fb.fr_grhead != -1) { g = calloc(1, sizeof(*g)); if (g != NULL) { - strncpy(g->fg_name, fb.fr_grhead, + strncpy(g->fg_name, fb.fr_names + fb.fr_grhead, FR_GROUPLEN); if (grtop == NULL) { grtop = g; @@ -980,13 +1001,13 @@ char *group, *comment; } } if (type == FR_T_CALLFUNC) { - printdeadlist(out, set, fb.fr_data, group, + printdeadlist(fiop, out, set, fb.fr_data, group, "# callfunc: "); } - } while (fb.fr_next != NULL); + } while ((g = grtop) != NULL) { - printdeadlist(out, set, NULL, g->fg_name, comment); + printdeadlist(fiop, out, set, NULL, g->fg_name, comment); grtop = g->fg_next; free(g); } @@ -997,7 +1018,7 @@ char *group, *comment; * the base from which to get the pointers. */ static void showlist(fiop) -struct friostat *fiop; + struct friostat *fiop; { struct frentry *fp = NULL; int i, set; @@ -1006,15 +1027,6 @@ struct friostat *fiop; if (opts & OPT_INACTIVE) set = 1 - set; if (opts & OPT_ACCNT) { -#ifdef USE_INET6 - if ((use_inet6) && (opts & OPT_OUTQUE)) { - i = F_ACOUT; - fp = (struct frentry *)fiop->f_acctout6[set]; - } else if ((use_inet6) && (opts & OPT_INQUE)) { - i = F_ACIN; - fp = (struct frentry *)fiop->f_acctin6[set]; - } else -#endif if (opts & OPT_OUTQUE) { i = F_ACOUT; fp = (struct frentry *)fiop->f_acctout[set]; @@ -1026,15 +1038,6 @@ struct friostat *fiop; return; } } else { -#ifdef USE_INET6 - if ((use_inet6) && (opts & OPT_OUTQUE)) { - i = F_OUT; - fp = (struct frentry *)fiop->f_fout6[set]; - } else if ((use_inet6) && (opts & OPT_INQUE)) { - i = F_IN; - fp = (struct frentry *)fiop->f_fin6[set]; - } else -#endif if (opts & OPT_OUTQUE) { i = F_OUT; fp = (struct frentry *)fiop->f_fout[set]; @@ -1049,139 +1052,243 @@ struct friostat *fiop; if (opts & OPT_DEBUG) PRINTF("fp %p set %d\n", fp, set); - if (!fp) { - FPRINTF(stderr, "empty list for %s%s\n", - (opts & OPT_INACTIVE) ? "inactive " : "", filters[i]); - return; + + if (live_kernel == 1) { + int printed; + + printed = printlivelist(fiop, i, set, fp, NULL, NULL); + if (printed == 0) { + FPRINTF(stderr, "# empty list for %s%s\n", + (opts & OPT_INACTIVE) ? "inactive " : "", + filters[i]); + } + } else { + if (!fp) { + FPRINTF(stderr, "# empty list for %s%s\n", + (opts & OPT_INACTIVE) ? "inactive " : "", + filters[i]); + } else { + printdeadlist(fiop, i, set, fp, NULL, NULL); + } } - if (live_kernel == 1) - printlivelist(i, set, fp, NULL, NULL); - else - printdeadlist(i, set, fp, NULL, NULL); } /* * Display ipfilter stateful filtering information */ -static void showipstates(ipsp) -ips_stat_t *ipsp; +static void showipstates(ipsp, filter) + ips_stat_t *ipsp; + int *filter; { - u_long minlen, maxlen, totallen, *buckets; + ipstate_t *is; + int i; + + /* + * If a list of states hasn't been asked for, only print out stats + */ + if (!(opts & OPT_SHOWLIST)) { + showstatestats(ipsp); + return; + } + + if ((state_fields != NULL) && (nohdrfields == 0)) { + for (i = 0; state_fields[i].w_value != 0; i++) { + printfieldhdr(statefields, state_fields + i); + if (state_fields[i + 1].w_value != 0) + printf("\t"); + } + printf("\n"); + } + + /* + * Print out all the state information currently held in the kernel. + */ + for (is = ipsp->iss_list; is != NULL; ) { + ipstate_t ips; + + is = fetchstate(is, &ips); + + if (is == NULL) + break; + + is = ips.is_next; + if ((filter != NULL) && + (state_matcharray(&ips, filter) == 0)) { + continue; + } + if (state_fields != NULL) { + for (i = 0; state_fields[i].w_value != 0; i++) { + printstatefield(&ips, state_fields[i].w_value); + if (state_fields[i + 1].w_value != 0) + printf("\t"); + } + printf("\n"); + } else { + printstate(&ips, opts, ipsp->iss_ticks); + } + } +} + + +static void showstatestats(ipsp) + ips_stat_t *ipsp; +{ + int minlen, maxlen, totallen; ipftable_t table; + u_int *buckets; ipfobj_t obj; int i, sz; /* * If a list of states hasn't been asked for, only print out stats */ - if (!(opts & OPT_SHOWLIST)) { - sz = sizeof(*buckets) * ipsp->iss_statesize; - buckets = (u_long *)malloc(sz); + sz = sizeof(*buckets) * ipsp->iss_state_size; + buckets = (u_int *)malloc(sz); - obj.ipfo_rev = IPFILTER_VERSION; - obj.ipfo_type = IPFOBJ_GTABLE; - obj.ipfo_size = sizeof(table); - obj.ipfo_ptr = &table; + obj.ipfo_rev = IPFILTER_VERSION; + obj.ipfo_type = IPFOBJ_GTABLE; + obj.ipfo_size = sizeof(table); + obj.ipfo_ptr = &table; - table.ita_type = IPFTABLE_BUCKETS; - table.ita_table = buckets; + table.ita_type = IPFTABLE_BUCKETS; + table.ita_table = buckets; - if (live_kernel == 1) { - if (ioctl(state_fd, SIOCGTABL, &obj) != 0) { - free(buckets); - return; - } - } else { - if (kmemcpy((char *)buckets, - (u_long)ipsp->iss_bucketlen, sz)) { - free(buckets); - return; - } + if (live_kernel == 1) { + if (ioctl(state_fd, SIOCGTABL, &obj) != 0) { + free(buckets); + return; } - - PRINTF("IP states added:\n\t%lu TCP\n\t%lu UDP\n\t%lu ICMP\n", - ipsp->iss_tcp, ipsp->iss_udp, ipsp->iss_icmp); - PRINTF("\t%lu hits\n\t%lu misses\n", ipsp->iss_hits, - ipsp->iss_miss); - PRINTF("\t%lu bucket full\n", ipsp->iss_bucketfull); - PRINTF("\t%lu maximum rule references\n", ipsp->iss_maxref); - PRINTF("\t%lu maximum\n\t%lu no memory\n\t%lu bkts in use\n", - ipsp->iss_max, ipsp->iss_nomem, ipsp->iss_inuse); - PRINTF("\t%lu active\n\t%lu expired\n\t%lu closed\n", - ipsp->iss_active, ipsp->iss_expire, ipsp->iss_fin); - - PRINTF("State logging %sabled\n", - state_logging ? "en" : "dis"); - - PRINTF("\nState table bucket statistics:\n"); - PRINTF("\t%lu in use\t\n", ipsp->iss_inuse); - PRINTF("\t%u%% hash efficiency\n", ipsp->iss_active ? - (u_int)(ipsp->iss_inuse * 100 / ipsp->iss_active) : 0); - - minlen = ipsp->iss_inuse; - totallen = 0; - maxlen = 0; - - for (i = 0; i < ipsp->iss_statesize; i++) { - if (buckets[i] > maxlen) - maxlen = buckets[i]; - if (buckets[i] < minlen) - minlen = buckets[i]; - totallen += buckets[i]; + } else { + if (kmemcpy((char *)buckets, + (u_long)ipsp->iss_bucketlen, sz)) { + free(buckets); + return; } + } - PRINTF("\t%2.2f%% bucket usage\n\t%lu minimal length\n", - ((float)ipsp->iss_inuse / ipsp->iss_statesize) * 100.0, - minlen); - PRINTF("\t%lu maximal length\n\t%.3f average length\n", - maxlen, - ipsp->iss_inuse ? (float) totallen/ ipsp->iss_inuse : - 0.0); + PRINTF("%u\tactive state table entries\n",ipsp->iss_active); + PRINTF("%lu\tadd bad\n", ipsp->iss_add_bad); + PRINTF("%lu\tadd duplicate\n", ipsp->iss_add_dup); + PRINTF("%lu\tadd locked\n", ipsp->iss_add_locked); + PRINTF("%lu\tadd oow\n", ipsp->iss_add_oow); + PRINTF("%lu\tbucket full\n", ipsp->iss_bucket_full); + PRINTF("%lu\tcheck bad\n", ipsp->iss_check_bad); + PRINTF("%lu\tcheck miss\n", ipsp->iss_check_miss); + PRINTF("%lu\tcheck nattag\n", ipsp->iss_check_nattag); + PRINTF("%lu\tclone nomem\n", ipsp->iss_clone_nomem); + PRINTF("%lu\tcheck notag\n", ipsp->iss_check_notag); + PRINTF("%lu\tcheck success\n", ipsp->iss_hits); + PRINTF("%lu\tcloned\n", ipsp->iss_cloned); + PRINTF("%lu\texpired\n", ipsp->iss_expire); + PRINTF("%lu\tflush all\n", ipsp->iss_flush_all); + PRINTF("%lu\tflush closing\n", ipsp->iss_flush_closing); + PRINTF("%lu\tflush queue\n", ipsp->iss_flush_queue); + PRINTF("%lu\tflush state\n", ipsp->iss_flush_state); + PRINTF("%lu\tflush timeout\n", ipsp->iss_flush_timeout); + PRINTF("%u\thash buckets in use\n", ipsp->iss_inuse); + PRINTF("%lu\tICMP bad\n", ipsp->iss_icmp_bad); + PRINTF("%lu\tICMP banned\n", ipsp->iss_icmp_banned); + PRINTF("%lu\tICMP errors\n", ipsp->iss_icmp_icmperr); + PRINTF("%lu\tICMP head block\n", ipsp->iss_icmp_headblock); + PRINTF("%lu\tICMP hits\n", ipsp->iss_icmp_hits); + PRINTF("%lu\tICMP not query\n", ipsp->iss_icmp_notquery); + PRINTF("%lu\tICMP short\n", ipsp->iss_icmp_short); + PRINTF("%lu\tICMP too many\n", ipsp->iss_icmp_toomany); + PRINTF("%lu\tICMPv6 errors\n", ipsp->iss_icmp6_icmperr); + PRINTF("%lu\tICMPv6 miss\n", ipsp->iss_icmp6_miss); + PRINTF("%lu\tICMPv6 not info\n", ipsp->iss_icmp6_notinfo); + PRINTF("%lu\tICMPv6 not query\n", ipsp->iss_icmp6_notquery); + PRINTF("%lu\tlog fail\n", ipsp->iss_log_fail); + PRINTF("%lu\tlog ok\n", ipsp->iss_log_ok); + PRINTF("%lu\tlookup interface mismatch\n", ipsp->iss_lookup_badifp); + PRINTF("%lu\tlookup mask mismatch\n", ipsp->iss_miss_mask); + PRINTF("%lu\tlookup port mismatch\n", ipsp->iss_lookup_badport); + PRINTF("%lu\tlookup miss\n", ipsp->iss_lookup_miss); + PRINTF("%lu\tmaximum rule references\n", ipsp->iss_max_ref); + PRINTF("%lu\tmaximum hosts per rule\n", ipsp->iss_max_track); + PRINTF("%lu\tno memory\n", ipsp->iss_nomem); + PRINTF("%lu\tout of window\n", ipsp->iss_oow); + PRINTF("%lu\torphans\n", ipsp->iss_orphan); + PRINTF("%lu\tscan block\n", ipsp->iss_scan_block); + PRINTF("%lu\tstate table maximum reached\n", ipsp->iss_max); + PRINTF("%lu\tTCP closing\n", ipsp->iss_tcp_closing); + PRINTF("%lu\tTCP OOW\n", ipsp->iss_tcp_oow); + PRINTF("%lu\tTCP RST add\n", ipsp->iss_tcp_rstadd); + PRINTF("%lu\tTCP too small\n", ipsp->iss_tcp_toosmall); + PRINTF("%lu\tTCP bad options\n", ipsp->iss_tcp_badopt); + PRINTF("%lu\tTCP removed\n", ipsp->iss_fin); + PRINTF("%lu\tTCP FSM\n", ipsp->iss_tcp_fsm); + PRINTF("%lu\tTCP strict\n", ipsp->iss_tcp_strict); + PRINTF("%lu\tTCP wild\n", ipsp->iss_wild); + PRINTF("%lu\tMicrosoft Windows SACK\n", ipsp->iss_winsack); + + PRINTF("State logging %sabled\n", state_logging ? "en" : "dis"); + + PRINTF("IP states added:\n"); + for (i = 0; i < 256; i++) { + if (ipsp->iss_proto[i] != 0) { + struct protoent *proto; + + proto = getprotobynumber(i); + PRINTF("%lu", ipsp->iss_proto[i]); + if (proto != NULL) + PRINTF("\t%s\n", proto->p_name); + else + PRINTF("\t%d\n", i); + } + } + + PRINTF("\nState table bucket statistics:\n"); + PRINTF("%u\tin use\n", ipsp->iss_inuse); + + minlen = ipsp->iss_max; + totallen = 0; + maxlen = 0; + + for (i = 0; i < ipsp->iss_state_size; i++) { + if (buckets[i] > maxlen) + maxlen = buckets[i]; + if (buckets[i] < minlen) + minlen = buckets[i]; + totallen += buckets[i]; + } + + PRINTF("%d\thash efficiency\n", + totallen ? ipsp->iss_inuse * 100 / totallen : 0); + PRINTF("%2.2f%%\tbucket usage\n%u\tminimal length\n", + ((float)ipsp->iss_inuse / ipsp->iss_state_size) * 100.0, + minlen); + PRINTF("%u\tmaximal length\n%.3f\taverage length\n", + maxlen, + ipsp->iss_inuse ? (float) totallen/ ipsp->iss_inuse : + 0.0); #define ENTRIES_PER_LINE 5 - if (opts & OPT_VERBOSE) { - PRINTF("\nCurrent bucket sizes :\n"); - for (i = 0; i < ipsp->iss_statesize; i++) { - if ((i % ENTRIES_PER_LINE) == 0) - PRINTF("\t"); - PRINTF("%4d -> %4lu", i, buckets[i]); - if ((i % ENTRIES_PER_LINE) == - (ENTRIES_PER_LINE - 1)) - PRINTF("\n"); - else - PRINTF(" "); - } - PRINTF("\n"); + if (opts & OPT_VERBOSE) { + PRINTF("\nCurrent bucket sizes :\n"); + for (i = 0; i < ipsp->iss_state_size; i++) { + if ((i % ENTRIES_PER_LINE) == 0) + PRINTF("\t"); + PRINTF("%4d -> %4u", i, buckets[i]); + if ((i % ENTRIES_PER_LINE) == + (ENTRIES_PER_LINE - 1)) + PRINTF("\n"); + else + PRINTF(" "); } PRINTF("\n"); - - free(buckets); - - if (live_kernel == 1) { - showtqtable_live(state_fd); - } else { - printtqtable(ipsp->iss_tcptab); - } - - return; - } + PRINTF("\n"); - /* - * Print out all the state information currently held in the kernel. - */ - while (ipsp->iss_list != NULL) { - ipstate_t ips; + free(buckets); - ipsp->iss_list = fetchstate(ipsp->iss_list, &ips); - - if (ipsp->iss_list != NULL) { - ipsp->iss_list = ips.is_next; - printstate(&ips, opts, ipsp->iss_ticks); - } + if (live_kernel == 1) { + showtqtable_live(state_fd); + } else { + printtqtable(ipsp->iss_tcptab); } } @@ -1190,21 +1297,23 @@ ips_stat_t *ipsp; static int handle_resize = 0, handle_break = 0; static void topipstates(saddr, daddr, sport, dport, protocol, ver, - refreshtime, topclosed) -i6addr_t saddr; -i6addr_t daddr; -int sport; -int dport; -int protocol; -int ver; -int refreshtime; -int topclosed; + refreshtime, topclosed, filter) + i6addr_t saddr; + i6addr_t daddr; + int sport; + int dport; + int protocol; + int ver; + int refreshtime; + int topclosed; + int *filter; { char str1[STSTRSIZE], str2[STSTRSIZE], str3[STSTRSIZE], str4[STSTRSIZE]; int maxtsentries = 0, reverse = 0, sorting = STSORT_DEFAULT; int i, j, winy, tsentry, maxx, maxy, redraw = 0, ret = 0; int len, srclen, dstlen, forward = 1, c = 0; ips_stat_t ipsst, *ipsstp = &ipsst; + int token_type = IPFGENITER_STATE; statetop_t *tstable = NULL, *tp; const char *errstr = ""; ipstate_t ips; @@ -1267,6 +1376,10 @@ int topclosed; if (ips.is_v != ver) continue; + if ((filter != NULL) && + (state_matcharray(&ips, filter) == 0)) + continue; + /* check v4 src/dest addresses */ if (ips.is_v == 4) { if ((saddr.in4.s_addr != INADDR_ANY && @@ -1348,6 +1461,7 @@ int topclosed; } } + (void) ioctl(state_fd, SIOCIPFDELTOK, &token_type); /* sort the array */ if (tsentry != -1) { @@ -1485,14 +1599,14 @@ int topclosed; printw("Src: %s, Dest: %s, Proto: %s, Sorted by: %s\n\n", str1, str2, str3, str4); - /* + /* * For an IPv4 IP address we need at most 15 characters, * 4 tuples of 3 digits, separated by 3 dots. Enforce this * length, so the colums do not change positions based * on the size of the IP address. This length makes the - * output fit in a 80 column terminal. + * output fit in a 80 column terminal. * We are lacking a good solution for IPv6 addresses (that - * can be longer that 15 characters), so we do not enforce + * can be longer that 15 characters), so we do not enforce * a maximum on the IP field size. */ if (srclen < 15) @@ -1629,8 +1743,8 @@ out: * Show fragment cache information that's held in the kernel. */ static void showfrstates(ifsp, ticks) -ipfrstat_t *ifsp; -u_long ticks; + ipfrstat_t *ifsp; + u_long ticks; { struct ipfr *ipfrtab[IPFT_SIZE], ifr; int i; @@ -1638,13 +1752,13 @@ u_long ticks; /* * print out the numeric statistics */ - PRINTF("IP fragment states:\n\t%lu new\n\t%lu expired\n\t%lu hits\n", + PRINTF("IP fragment states:\n%lu\tnew\n%lu\texpired\n%lu\thits\n", ifsp->ifs_new, ifsp->ifs_expire, ifsp->ifs_hits); - PRINTF("\t%lu retrans\n\t%lu too short\n", + PRINTF("%lu\tretrans\n%lu\ttoo short\n", ifsp->ifs_retrans0, ifsp->ifs_short); - PRINTF("\t%lu no memory\n\t%lu already exist\n", + PRINTF("%lu\tno memory\n%lu\talready exist\n", ifsp->ifs_nomem, ifsp->ifs_exists); - PRINTF("\t%lu inuse\n", ifsp->ifs_inuse); + PRINTF("%lu\tinuse\n", ifsp->ifs_inuse); PRINTF("\n"); if (live_kernel == 0) { @@ -1664,7 +1778,7 @@ u_long ticks; break; ifr.ipfr_ttl -= ticks; printfraginfo("", &ifr); - } while (1); + } while (ifr.ipfr_next != NULL); } else { for (i = 0; i < IPFT_SIZE; i++) while (ipfrtab[i] != NULL) { @@ -1693,7 +1807,7 @@ u_long ticks; break; ifr.ipfr_ttl -= ticks; printfraginfo("NAT: ", &ifr); - } while (1); + } while (ifr.ipfr_next != NULL); } else { for (i = 0; i < IPFT_SIZE; i++) while (ipfrtab[i] != NULL) { @@ -1711,7 +1825,7 @@ u_long ticks; * Show stats on how auth within IPFilter has been used */ static void showauthstates(asp) -fr_authstat_t *asp; + ipf_authstat_t *asp; { frauthent_t *frap, fra; ipfgeniter_t auth; @@ -1727,7 +1841,7 @@ fr_authstat_t *asp; auth.igi_data = &fra; #ifdef USE_QUAD_T - printf("Authorisation hits: %qu\tmisses %qu\n", + printf("Authorisation hits: %"PRIu64"\tmisses %"PRIu64"\n", (unsigned long long) asp->fas_hits, (unsigned long long) asp->fas_miss); #else @@ -1762,7 +1876,7 @@ fr_authstat_t *asp; * authentication, separately. */ static void showgroups(fiop) -struct friostat *fiop; + struct friostat *fiop; { static char *gnames[3] = { "Filter", "Accounting", "Authentication" }; static int gnums[3] = { IPL_LOGIPF, IPL_LOGCOUNT, IPL_LOGAUTH }; @@ -1790,10 +1904,11 @@ struct friostat *fiop; } } + static void parse_ipportstr(argument, ip, port) -const char *argument; -i6addr_t *ip; -int *port; + const char *argument; + i6addr_t *ip; + int *port; { char *s, *comma; int ok = 0; @@ -1845,20 +1960,20 @@ int *port; #ifdef STATETOP static void sig_resize(s) -int s; + int s; { handle_resize = 1; } static void sig_break(s) -int s; + int s; { handle_break = 1; } static char *getip(v, addr) -int v; -i6addr_t *addr; + int v; + i6addr_t *addr; { #ifdef USE_INET6 static char hostbuf[MAXHOSTNAMELEN+1]; @@ -1878,7 +1993,7 @@ i6addr_t *addr; static char *ttl_to_string(ttl) -long int ttl; + long int ttl; { static char ttlbuf[STSTRSIZE]; int hours, minutes, seconds; @@ -1900,8 +2015,8 @@ long int ttl; static int sort_pkts(a, b) -const void *a; -const void *b; + const void *a; + const void *b; { register const statetop_t *ap = a; @@ -1916,8 +2031,8 @@ const void *b; static int sort_bytes(a, b) -const void *a; -const void *b; + const void *a; + const void *b; { register const statetop_t *ap = a; register const statetop_t *bp = b; @@ -1931,8 +2046,8 @@ const void *b; static int sort_p(a, b) -const void *a; -const void *b; + const void *a; + const void *b; { register const statetop_t *ap = a; register const statetop_t *bp = b; @@ -1946,8 +2061,8 @@ const void *b; static int sort_ttl(a, b) -const void *a; -const void *b; + const void *a; + const void *b; { register const statetop_t *ap = a; register const statetop_t *bp = b; @@ -1960,8 +2075,8 @@ const void *b; } static int sort_srcip(a, b) -const void *a; -const void *b; + const void *a; + const void *b; { register const statetop_t *ap = a; register const statetop_t *bp = b; @@ -1986,8 +2101,8 @@ const void *b; } static int sort_srcpt(a, b) -const void *a; -const void *b; + const void *a; + const void *b; { register const statetop_t *ap = a; register const statetop_t *bp = b; @@ -2000,8 +2115,8 @@ const void *b; } static int sort_dstip(a, b) -const void *a; -const void *b; + const void *a; + const void *b; { register const statetop_t *ap = a; register const statetop_t *bp = b; @@ -2026,8 +2141,8 @@ const void *b; } static int sort_dstpt(a, b) -const void *a; -const void *b; + const void *a; + const void *b; { register const statetop_t *ap = a; register const statetop_t *bp = b; @@ -2043,9 +2158,8 @@ const void *b; ipstate_t *fetchstate(src, dst) -ipstate_t *src, *dst; + ipstate_t *src, *dst; { - int i; if (live_kernel == 1) { ipfgeniter_t state; @@ -2063,8 +2177,8 @@ ipstate_t *src, *dst; if (ioctl(state_fd, SIOCGENITER, &obj) != 0) return NULL; if (dst->is_next == NULL) { - i = IPFGENITER_STATE; - ioctl(state_fd, SIOCIPFDELTOK, &i); + int n = IPFGENITER_STATE; + (void) ioctl(ipf_fd,SIOCIPFDELTOK, &n); } } else { if (kmemcpy((char *)dst, (u_long)src, sizeof(*dst))) @@ -2075,8 +2189,8 @@ ipstate_t *src, *dst; static int fetchfrag(fd, type, frp) -int fd, type; -ipfr_t *frp; + int fd, type; + ipfr_t *frp; { ipfgeniter_t frag; ipfobj_t obj; @@ -2096,8 +2210,155 @@ ipfr_t *frp; } +static int state_matcharray(stp, array) + ipstate_t *stp; + int *array; +{ + int i, n, *x, rv, p; + ipfexp_t *e; + + rv = 0; + + for (n = array[0], x = array + 1; n > 0; x += e->ipfe_size) { + e = (ipfexp_t *)x; + if (e->ipfe_cmd == IPF_EXP_END) + break; + n -= e->ipfe_size; + + rv = 0; + /* + * The upper 16 bits currently store the protocol value. + * This is currently used with TCP and UDP port compares and + * allows "tcp.port = 80" without requiring an explicit + " "ip.pr = tcp" first. + */ + p = e->ipfe_cmd >> 16; + if ((p != 0) && (p != stp->is_p)) + break; + + switch (e->ipfe_cmd) + { + case IPF_EXP_IP_PR : + for (i = 0; !rv && i < e->ipfe_narg; i++) { + rv |= (stp->is_p == e->ipfe_arg0[i]); + } + break; + + case IPF_EXP_IP_SRCADDR : + if (stp->is_v != 4) + break; + for (i = 0; !rv && i < e->ipfe_narg; i++) { + rv |= ((stp->is_saddr & + e->ipfe_arg0[i * 2 + 1]) == + e->ipfe_arg0[i * 2]); + } + break; + + case IPF_EXP_IP_DSTADDR : + if (stp->is_v != 4) + break; + for (i = 0; !rv && i < e->ipfe_narg; i++) { + rv |= ((stp->is_daddr & + e->ipfe_arg0[i * 2 + 1]) == + e->ipfe_arg0[i * 2]); + } + break; + + case IPF_EXP_IP_ADDR : + if (stp->is_v != 4) + break; + for (i = 0; !rv && i < e->ipfe_narg; i++) { + rv |= ((stp->is_saddr & + e->ipfe_arg0[i * 2 + 1]) == + e->ipfe_arg0[i * 2]) || + ((stp->is_daddr & + e->ipfe_arg0[i * 2 + 1]) == + e->ipfe_arg0[i * 2]); + } + break; + +#ifdef USE_INET6 + case IPF_EXP_IP6_SRCADDR : + if (stp->is_v != 6) + break; + for (i = 0; !rv && i < e->ipfe_narg; i++) { + rv |= IP6_MASKEQ(&stp->is_src, + &e->ipfe_arg0[i * 8 + 4], + &e->ipfe_arg0[i * 8]); + } + break; + + case IPF_EXP_IP6_DSTADDR : + if (stp->is_v != 6) + break; + for (i = 0; !rv && i < e->ipfe_narg; i++) { + rv |= IP6_MASKEQ(&stp->is_dst, + &e->ipfe_arg0[i * 8 + 4], + &e->ipfe_arg0[i * 8]); + } + break; + + case IPF_EXP_IP6_ADDR : + if (stp->is_v != 6) + break; + for (i = 0; !rv && i < e->ipfe_narg; i++) { + rv |= IP6_MASKEQ(&stp->is_src, + &e->ipfe_arg0[i * 8 + 4], + &e->ipfe_arg0[i * 8]) || + IP6_MASKEQ(&stp->is_dst, + &e->ipfe_arg0[i * 8 + 4], + &e->ipfe_arg0[i * 8]); + } + break; +#endif + + case IPF_EXP_UDP_PORT : + case IPF_EXP_TCP_PORT : + for (i = 0; !rv && i < e->ipfe_narg; i++) { + rv |= (stp->is_sport == e->ipfe_arg0[i]) || + (stp->is_dport == e->ipfe_arg0[i]); + } + break; + + case IPF_EXP_UDP_SPORT : + case IPF_EXP_TCP_SPORT : + for (i = 0; !rv && i < e->ipfe_narg; i++) { + rv |= (stp->is_sport == e->ipfe_arg0[i]); + } + break; + + case IPF_EXP_UDP_DPORT : + case IPF_EXP_TCP_DPORT : + for (i = 0; !rv && i < e->ipfe_narg; i++) { + rv |= (stp->is_dport == e->ipfe_arg0[i]); + } + break; + + case IPF_EXP_IDLE_GT : + for (i = 0; !rv && i < e->ipfe_narg; i++) { + rv |= (stp->is_die < e->ipfe_arg0[i]); + } + break; + + case IPF_EXP_TCP_STATE : + for (i = 0; !rv && i < e->ipfe_narg; i++) { + rv |= (stp->is_state[0] == e->ipfe_arg0[i]) || + (stp->is_state[1] == e->ipfe_arg0[i]); + } + break; + } + rv ^= e->ipfe_not; + + if (rv == 0) + break; + } + + return rv; +} + + static void showtqtable_live(fd) -int fd; + int fd; { ipftq_t table[IPF_TCP_NSTATES]; ipfobj_t obj; diff --git a/contrib/ipfilter/tools/ipfsyncd.c b/contrib/ipfilter/tools/ipfsyncd.c new file mode 100644 index 00000000000..d4671e409d6 --- /dev/null +++ b/contrib/ipfilter/tools/ipfsyncd.c @@ -0,0 +1,671 @@ +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + */ +#if !defined(lint) +static const char sccsid[] = "@(#)ip_fil.c 2.41 6/5/96 (C) 1993-2000 Darren Reed"; +static const char rcsid[] = "@(#)$Id: ipfsyncd.c,v 1.1.2.2 2012/07/22 08:04:24 darren_r Exp $"; +#endif +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "ipf.h" +#include "opts.h" + + +#define R_IO_ERROR -1 +#define R_OKAY 0 +#define R_MORE 1 +#define R_SKIP 2 +#if defined(sun) && !defined(SOLARIS2) +# define STRERROR(x) sys_errlist[x] +extern char *sys_errlist[]; +#else +# define STRERROR(x) strerror(x) +#endif + + +int main __P((int, char *[])); +void usage __P((char *)); +void printsynchdr __P((synchdr_t *)); +void printtable __P((int)); +void printsmcproto __P((char *)); +void printcommand __P((int)); +int do_kbuff __P((int, char *, int *)); +int do_packet __P((int, char *)); +int buildsocket __P((char *, struct sockaddr_in *)); +void do_io __P((void)); +void handleterm __P((int)); + +int terminate = 0; +int igmpfd = -1; +int nfd = -1; +int lfd = -1; +int opts = 0; + +void +usage(progname) + char *progname; +{ + fprintf(stderr, + "Usage: %s [-d] [-p port] [-i address] -I \n", + progname); +} + +void +handleterm(sig) + int sig; +{ + terminate = sig; +} + + +/* should be large enough to hold header + any datatype */ +#define BUFFERLEN 1400 + +int +main(argc, argv) + int argc; + char *argv[]; +{ + struct sockaddr_in sin; + char *interface; + char *progname; + int opt, tries; + + progname = strrchr(argv[0], '/'); + if (progname) { + progname++; + } else { + progname = argv[0]; + } + + opts = 0; + tries = 0; + interface = NULL; + + bzero((char *)&sin, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_port = htons(0xaf6c); + sin.sin_addr.s_addr = htonl(INADDR_UNSPEC_GROUP | 0x697066); + + while ((opt = getopt(argc, argv, "di:I:p:")) != -1) + switch (opt) + { + case 'd' : + debuglevel++; + break; + case 'I' : + interface = optarg; + break; + case 'i' : + sin.sin_addr.s_addr = inet_addr(optarg); + break; + case 'p' : + sin.sin_port = htons(atoi(optarg)); + break; + } + + if (interface == NULL) { + usage(progname); + exit(1); + } + + if (!debuglevel) { + +#if BSD >= 199306 + daemon(0, 0); +#else + int fd = open("/dev/null", O_RDWR); + + switch (fork()) + { + case 0 : + break; + + case -1 : + fprintf(stderr, "%s: fork() failed: %s\n", + argv[0], STRERROR(errno)); + exit(1); + /* NOTREACHED */ + + default : + exit(0); + /* NOTREACHED */ + } + + dup2(fd, 0); + dup2(fd, 1); + dup2(fd, 2); + close(fd); + + setsid(); +#endif + } + + signal(SIGHUP, handleterm); + signal(SIGINT, handleterm); + signal(SIGTERM, handleterm); + + openlog(progname, LOG_PID, LOG_SECURITY); + + while (!terminate) { + if (lfd != -1) { + close(lfd); + lfd = -1; + } + if (nfd != -1) { + close(nfd); + nfd = -1; + } + if (igmpfd != -1) { + close(igmpfd); + igmpfd = -1; + } + + if (buildsocket(interface, &sin) == -1) + goto tryagain; + + lfd = open(IPSYNC_NAME, O_RDWR); + if (lfd == -1) { + syslog(LOG_ERR, "open(%s):%m", IPSYNC_NAME); + debug(1, "open(%s): %s\n", IPSYNC_NAME, + STRERROR(errno)); + goto tryagain; + } + + tries = -1; + do_io(); +tryagain: + tries++; + syslog(LOG_INFO, "retry in %d seconds", 1 << tries); + debug(1, "wait %d seconds\n", 1 << tries); + sleep(1 << tries); + } + + + /* terminate */ + if (lfd != -1) + close(lfd); + if (nfd != -1) + close(nfd); + + syslog(LOG_ERR, "signal %d received, exiting...", terminate); + debug(1, "signal %d received, exiting...", terminate); + + exit(1); +} + + +void +do_io() +{ + char nbuff[BUFFERLEN]; + char buff[BUFFERLEN]; + fd_set mrd, rd; + int maxfd; + int inbuf; + int n1; + int left; + + FD_ZERO(&mrd); + FD_SET(lfd, &mrd); + FD_SET(nfd, &mrd); + maxfd = nfd; + if (lfd > maxfd) + maxfd = lfd; + debug(2, "nfd %d lfd %d maxfd %d\n", nfd, lfd, maxfd); + + inbuf = 0; + /* + * A threaded approach to this loop would have one thread + * work on reading lfd (only) all the time and another thread + * working on reading nfd all the time. + */ + while (!terminate) { + int n; + + rd = mrd; + + n = select(maxfd + 1, &rd, NULL, NULL, NULL); + if (n < 0) { + switch (errno) + { + case EINTR : + continue; + default : + syslog(LOG_ERR, "select error: %m"); + debug(1, "select error: %s\n", STRERROR(errno)); + return; + } + } + + if (FD_ISSET(lfd, &rd)) { + n1 = read(lfd, buff+inbuf, BUFFERLEN-inbuf); + + debug(3, "read(K):%d\n", n1); + + if (n1 <= 0) { + syslog(LOG_ERR, "read error (k-header): %m"); + debug(1, "read error (k-header): %s\n", + STRERROR(errno)); + return; + } + + left = 0; + + switch (do_kbuff(n1, buff, &left)) + { + case R_IO_ERROR : + return; + case R_MORE : + inbuf += left; + break; + default : + inbuf = 0; + break; + } + } + + if (FD_ISSET(nfd, &rd)) { + n1 = recv(nfd, nbuff, sizeof(nbuff), 0); + + debug(3, "read(N):%d\n", n1); + + if (n1 <= 0) { + syslog(LOG_ERR, "read error (n-header): %m"); + debug(1, "read error (n-header): %s\n", + STRERROR(errno)); + return; + } + + switch (do_packet(n1, nbuff)) + { + case R_IO_ERROR : + return; + default : + break; + } + } + } +} + + +int +buildsocket(nicname, sinp) + char *nicname; + struct sockaddr_in *sinp; +{ + struct sockaddr_in *reqip; + struct ifreq req; + char opt; + + debug(2, "binding to %s:%s\n", nicname, inet_ntoa(sinp->sin_addr)); + + if (IN_MULTICAST(ntohl(sinp->sin_addr.s_addr))) { + struct in_addr addr; + struct ip_mreq mreq; + + igmpfd = socket(AF_INET, SOCK_RAW, IPPROTO_IGMP); + if (igmpfd == -1) { + syslog(LOG_ERR, "socket:%m"); + debug(1, "socket:%s\n", STRERROR(errno)); + return -1; + } + + bzero((char *)&req, sizeof(req)); + strncpy(req.ifr_name, nicname, sizeof(req.ifr_name)); + req.ifr_name[sizeof(req.ifr_name) - 1] = '\0'; + if (ioctl(igmpfd, SIOCGIFADDR, &req) == -1) { + syslog(LOG_ERR, "ioctl(SIOCGIFADDR):%m"); + debug(1, "ioctl(SIOCGIFADDR):%s\n", STRERROR(errno)); + close(igmpfd); + igmpfd = -1; + return -1; + } + reqip = (struct sockaddr_in *)&req.ifr_addr; + + addr = reqip->sin_addr; + if (setsockopt(igmpfd, IPPROTO_IP, IP_MULTICAST_IF, + (char *)&addr, sizeof(addr)) == -1) { + syslog(LOG_ERR, "setsockopt(IP_MULTICAST_IF(%s)):%m", + inet_ntoa(addr)); + debug(1, "setsockopt(IP_MULTICAST_IF(%s)):%s\n", + inet_ntoa(addr), STRERROR(errno)); + close(igmpfd); + igmpfd = -1; + return -1; + } + + opt = 0; + if (setsockopt(igmpfd, IPPROTO_IP, IP_MULTICAST_LOOP, + (char *)&opt, sizeof(opt)) == -1) { + syslog(LOG_ERR, "setsockopt(IP_MULTICAST_LOOP=0):%m"); + debug(1, "setsockopt(IP_MULTICAST_LOOP=0):%s\n", + STRERROR(errno)); + close(igmpfd); + igmpfd = -1; + return -1; + } + + opt = 63; + if (setsockopt(igmpfd, IPPROTO_IP, IP_MULTICAST_TTL, + (char *)&opt, sizeof(opt)) == -1) { + syslog(LOG_ERR, "setsockopt(IP_MULTICAST_TTL=%d):%m", + opt); + debug(1, "setsockopt(IP_MULTICAST_TTL=%d):%s\n", opt, + STRERROR(errno)); + close(igmpfd); + igmpfd = -1; + return -1; + } + + mreq.imr_multiaddr.s_addr = sinp->sin_addr.s_addr; + mreq.imr_interface.s_addr = reqip->sin_addr.s_addr; + + if (setsockopt(igmpfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, + (char *)&mreq, sizeof(mreq)) == -1) { + char buffer[80]; + + sprintf(buffer, "%s,", inet_ntoa(sinp->sin_addr)); + strcat(buffer, inet_ntoa(reqip->sin_addr)); + + syslog(LOG_ERR, + "setsockpt(IP_ADD_MEMBERSHIP,%s):%m", buffer); + debug(1, "setsockpt(IP_ADD_MEMBERSHIP,%s):%s\n", + buffer, STRERROR(errno)); + close(igmpfd); + igmpfd = -1; + return -1; + } + } + nfd = socket(AF_INET, SOCK_DGRAM, 0); + if (nfd == -1) { + syslog(LOG_ERR, "socket:%m"); + if (igmpfd != -1) { + close(igmpfd); + igmpfd = -1; + } + return -1; + } + bzero((char *)&req, sizeof(req)); + strncpy(req.ifr_name, nicname, sizeof(req.ifr_name)); + req.ifr_name[sizeof(req.ifr_name) - 1] = '\0'; + if (ioctl(nfd, SIOCGIFADDR, &req) == -1) { + syslog(LOG_ERR, "ioctl(SIOCGIFADDR):%m"); + debug(1, "ioctl(SIOCGIFADDR):%s\n", STRERROR(errno)); + close(igmpfd); + igmpfd = -1; + return -1; + } + + if (bind(nfd, (struct sockaddr *)&req.ifr_addr, + sizeof(req.ifr_addr)) == -1) { + syslog(LOG_ERR, "bind:%m"); + debug(1, "bind:%s\n", STRERROR(errno)); + close(nfd); + if (igmpfd != -1) { + close(igmpfd); + igmpfd = -1; + } + nfd = -1; + return -1; + } + + if (connect(nfd, (struct sockaddr *)sinp, sizeof(*sinp)) == -1) { + syslog(LOG_ERR, "connect:%m"); + debug(1, "connect:%s\n", STRERROR(errno)); + close(nfd); + if (igmpfd != -1) { + close(igmpfd); + igmpfd = -1; + } + nfd = -1; + return -1; + } + syslog(LOG_INFO, "Sending data to %s", inet_ntoa(sinp->sin_addr)); + debug(3, "Sending data to %s\n", inet_ntoa(sinp->sin_addr)); + + return nfd; +} + + +int +do_packet(pklen, buff) + int pklen; + char *buff; +{ + synchdr_t *sh; + u_32_t magic; + int len; + int n2; + int n3; + + while (pklen > 0) { + if (pklen < sizeof(*sh)) { + syslog(LOG_ERR, "packet length too short:%d", pklen); + debug(2, "packet length too short:%d\n", pklen); + return R_SKIP; + } + + sh = (synchdr_t *)buff; + len = ntohl(sh->sm_len); + magic = ntohl(sh->sm_magic); + + if (magic != SYNHDRMAGIC) { + syslog(LOG_ERR, "invalid header magic %x", magic); + debug(2, "invalid header magic %x\n", magic); + return R_SKIP; + } + + if (pklen < len + sizeof(*sh)) { + syslog(LOG_ERR, "packet length too short:%d", pklen); + debug(2, "packet length too short:%d\n", pklen); + return R_SKIP; + } + + if (debuglevel > 3) { + printsynchdr(sh); + printcommand(sh->sm_cmd); + printtable(sh->sm_table); + printsmcproto(buff); + } + + n2 = sizeof(*sh) + len; + + do { + n3 = write(lfd, buff, n2); + if (n3 <= 0) { + syslog(LOG_ERR, "write error: %m"); + debug(1, "write error: %s\n", STRERROR(errno)); + return R_IO_ERROR; + } + + n2 -= n3; + buff += n3; + pklen -= n3; + } while (n3 != 0); + } + + return R_OKAY; +} + + + +int +do_kbuff(inbuf, buf, left) + int inbuf, *left; + char *buf; +{ + synchdr_t *sh; + u_32_t magic; + int complete; + int sendlen; + int error; + int bytes; + int len; + int n2; + int n3; + + sendlen = 0; + bytes = inbuf; + error = R_OKAY; + sh = (synchdr_t *)buf; + + for (complete = 0; bytes > 0; complete++) { + len = ntohl(sh->sm_len); + magic = ntohl(sh->sm_magic); + + if (magic != SYNHDRMAGIC) { + syslog(LOG_ERR, + "read invalid header magic 0x%x, flushing", + magic); + debug(2, "read invalid header magic 0x%x, flushing\n", + magic); + n2 = SMC_RLOG; + (void) ioctl(lfd, SIOCIPFFL, &n2); + break; + } + + if (debuglevel > 3) { + printsynchdr(sh); + printcommand(sh->sm_cmd); + printtable(sh->sm_table); + putchar('\n'); + } + + if (bytes < sizeof(*sh) + len) { + debug(3, "Not enough bytes %d < %d\n", bytes, + sizeof(*sh) + len); + error = R_MORE; + break; + } + + if (debuglevel > 3) { + printsmcproto(buf); + } + + sendlen += len + sizeof(*sh); + sh = (synchdr_t *)(buf + sendlen); + bytes -= sendlen; + } + + if (complete) { + n3 = send(nfd, buf, sendlen, 0); + if (n3 <= 0) { + syslog(LOG_ERR, "write error: %m"); + debug(1, "write error: %s\n", STRERROR(errno)); + return R_IO_ERROR; + } + debug(3, "send on %d len %d = %d\n", nfd, sendlen, n3); + error = R_OKAY; + } + + /* move buffer to the front,we might need to make + * this more efficient, by using a rolling pointer + * over the buffer and only copying it, when + * we are reaching the end + */ + if (bytes > 0) { + bcopy(buf + bytes, buf, bytes); + error = R_MORE; + } + debug(4, "complete %d bytes %d error %d\n", complete, bytes, error); + + *left = bytes; + + return error; +} + + +void +printcommand(cmd) + int cmd; +{ + + switch (cmd) + { + case SMC_CREATE : + printf(" cmd:CREATE"); + break; + case SMC_UPDATE : + printf(" cmd:UPDATE"); + break; + default : + printf(" cmd:Unknown(%d)", cmd); + break; + } +} + + +void +printtable(table) + int table; +{ + switch (table) + { + case SMC_NAT : + printf(" table:NAT"); + break; + case SMC_STATE : + printf(" table:STATE"); + break; + default : + printf(" table:Unknown(%d)", table); + break; + } +} + + +void +printsmcproto(buff) + char *buff; +{ + syncupdent_t *su; + synchdr_t *sh; + + sh = (synchdr_t *)buff; + + if (sh->sm_cmd == SMC_CREATE) { + ; + + } else if (sh->sm_cmd == SMC_UPDATE) { + su = (syncupdent_t *)buff; + if (sh->sm_p == IPPROTO_TCP) { + printf(" TCP Update: age %lu state %d/%d\n", + su->sup_tcp.stu_age, + su->sup_tcp.stu_state[0], + su->sup_tcp.stu_state[1]); + } + } else { + printf("Unknown command\n"); + } +} + + +void +printsynchdr(sh) + synchdr_t *sh; +{ + + printf("v:%d p:%d num:%d len:%d magic:%x", sh->sm_v, sh->sm_p, + ntohl(sh->sm_num), ntohl(sh->sm_len), ntohl(sh->sm_magic)); +} diff --git a/contrib/ipfilter/tools/ipftest.c b/contrib/ipfilter/tools/ipftest.c index 963ed197cb9..a475828b2e8 100644 --- a/contrib/ipfilter/tools/ipftest.c +++ b/contrib/ipfilter/tools/ipftest.c @@ -1,7 +1,7 @@ /* $FreeBSD$ */ /* - * Copyright (C) 2002-2006 by Darren Reed. + * Copyright (C) 2012 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. */ @@ -12,24 +12,23 @@ #if !defined(lint) static const char sccsid[] = "@(#)ipt.c 1.19 6/3/96 (C) 1993-2000 Darren Reed"; -static const char rcsid[] = "@(#)$Id: ipftest.c,v 1.44.2.13 2006/12/12 16:13:01 darrenr Exp $"; +static const char rcsid[] = "@(#)$Id$"; #endif extern char *optarg; -extern struct frentry *ipfilter[2][2]; -extern struct ipread snoop, etherf, tcpd, pcap, iptext, iphex; +extern struct ipread pcap, iptext, iphex; extern struct ifnet *get_unit __P((char *, int)); extern void init_ifp __P((void)); extern ipnat_t *natparse __P((char *, int)); -extern int fr_running; extern hostmap_t **ipf_hm_maptable; extern hostmap_t *ipf_hm_maplist; -ipfmutex_t ipl_mutex, ipf_authmx, ipf_rw, ipf_stinsert; +ipfmutex_t ipl_mutex, ipf_auth_mx, ipf_rw, ipf_stinsert; ipfmutex_t ipf_nat_new, ipf_natio, ipf_timeoutlock; ipfrwlock_t ipf_mutex, ipf_global, ipf_ipidfrag, ip_poolrw, ipf_frcache; -ipfrwlock_t ipf_frag, ipf_state, ipf_nat, ipf_natfrag, ipf_auth, ipf_tokens; -int opts = OPT_DONOTHING; +ipfrwlock_t ipf_frag, ipf_state, ipf_nat, ipf_natfrag, ipf_authlk; +ipfrwlock_t ipf_tokens; +int opts = OPT_DONTOPEN; int use_inet6 = 0; int docksum = 0; int pfil_delayed_copy = 0; @@ -37,10 +36,10 @@ int main __P((int, char *[])); int loadrules __P((char *, int)); int kmemcpy __P((char *, long, int)); int kstrncpy __P((char *, long, int n)); -void dumpnat __P((void)); -void dumpstate __P((void)); -void dumplookups __P((void)); -void dumpgroups __P((void)); +int blockreason; +void dumpnat __P((void *)); +void dumpgroups __P((ipf_main_softc_t *)); +void dumprules __P((frentry_t *)); void drain_log __P((char *)); void fixv4sums __P((mb_t *, ip_t *)); @@ -72,18 +71,20 @@ static ioctlfunc_t iocfunctions[IPL_LOGSIZE] = { ipftestioctl, ipscantestioctl, ipooltestioctl, NULL }; +static ipf_main_softc_t *softc = NULL; -int main(argc,argv) -int argc; -char *argv[]; +int +main(argc,argv) + int argc; + char *argv[]; { char *datain, *iface, *ifname, *logout; int fd, i, dir, c, loaded, dump, hlen; struct in_addr sip; struct ifnet *ifp; struct ipread *r; - mb_t mb, *m; + mb_t mb, *m, *n; ip_t *ip; m = &mb; @@ -98,18 +99,20 @@ char *argv[]; sip.s_addr = 0; ifname = "anon0"; - MUTEX_INIT(&ipf_rw, "ipf rw mutex"); - MUTEX_INIT(&ipf_timeoutlock, "ipf timeout lock"); - RWLOCK_INIT(&ipf_global, "ipf filter load/unload mutex"); - RWLOCK_INIT(&ipf_mutex, "ipf filter rwlock"); - RWLOCK_INIT(&ipf_ipidfrag, "ipf IP NAT-Frag rwlock"); - RWLOCK_INIT(&ipf_frcache, "ipf filter cache"); - RWLOCK_INIT(&ipf_tokens, "ipf token rwlock"); - initparse(); - if (fr_initialise() == -1) - abort(); - fr_running = 1; + + ipf_load_all(); + + softc = ipf_create_all(NULL); + if (softc == NULL) + exit(1); + + if (ipf_init_all(softc) == -1) + exit(1); + + i = 1; + if (ipftestioctl(IPL_LOGIPF, SIOCFRENB, &i) != 0) + exit(1); while ((c = getopt(argc, argv, "6bCdDF:i:I:l:N:P:or:RS:T:vxX")) != -1) switch (c) @@ -137,12 +140,6 @@ char *argv[]; case 'F' : if (strcasecmp(optarg, "pcap") == 0) r = &pcap; - else if (strcasecmp(optarg, "etherfind") == 0) - r = ðerf; - else if (strcasecmp(optarg, "snoop") == 0) - r = &snoop; - else if (strcasecmp(optarg, "tcpdump") == 0) - r = &tcpd; else if (strcasecmp(optarg, "hex") == 0) r = &iphex; else if (strcasecmp(optarg, "text") == 0) @@ -208,18 +205,21 @@ char *argv[]; else fd = (*r->r_open)("-"); - if (fd < 0) + if (fd < 0) { + perror("error opening input"); exit(-1); + } + + m->m_data = (char *)m->mb_buf; + while ((i = (*r->r_readip)(m, &iface, &dir)) > 0) { - ip = MTOD(m, ip_t *); - while ((i = (*r->r_readip)(MTOD(m, char *), sizeof(m->mb_buf), - &iface, &dir)) > 0) { if ((iface == NULL) || (*iface == '\0')) iface = ifname; + + ip = MTOD(m, ip_t *); ifp = get_unit(iface, IP_V(ip)); - if (!use_inet6) { - ip->ip_off = ntohs(ip->ip_off); - ip->ip_len = ntohs(ip->ip_len); + + if (IP_V(ip) == 4) { if ((r->r_flags & R_DO_CKSUM) || docksum) fixv4sums(m, ip); hlen = IP_HL(ip) << 2; @@ -231,9 +231,11 @@ char *argv[]; hlen = sizeof(ip6_t); #endif /* ipfr_slowtimer(); */ + blockreason = 0; m = &mb; + m->mb_ifp = ifp; m->mb_len = i; - i = fr_check(ip, hlen, ifp, dir, &m); + i = ipf_check(softc, ip, hlen, ifp, dir, &m); if ((opts & OPT_NAT) == 0) switch (i) { @@ -271,17 +273,24 @@ char *argv[]; (void)printf("recognised return %#x\n", i); break; } - if (!use_inet6) { - ip->ip_off = htons(ip->ip_off); - ip->ip_len = htons(ip->ip_len); - } if (!(opts & OPT_BRIEF)) { putchar(' '); - printpacket(ip); + if (m != NULL) + printpacket(dir, m); + else + printpacket(dir, &mb); printf("--------------"); - } else if ((opts & (OPT_BRIEF|OPT_NAT)) == (OPT_NAT|OPT_BRIEF)) - printpacket(ip); + } else if ((opts & (OPT_BRIEF|OPT_NAT)) == + (OPT_NAT|OPT_BRIEF)) { + if (m != NULL) + printpacket(dir, m); + else + PRINTF("%d\n", blockreason); + } + + ipf_state_flush(softc, 1, 0); + if (dir && (ifp != NULL) && IP_V(ip) && (m != NULL)) #if defined(__sgi) && (IRIX < 60500) (*ifp->if_output)(ifp, (void *)m, NULL); @@ -292,6 +301,13 @@ char *argv[]; (*ifp->if_output)(ifp, (void *)m, NULL, 0); # endif #endif + + while ((m != NULL) && (m != &mb)) { + n = m->mb_next; + freembt(m); + m = n; + } + if ((opts & (OPT_BRIEF|OPT_NAT)) != (OPT_NAT|OPT_BRIEF)) putchar('\n'); dir = 0; @@ -300,6 +316,7 @@ char *argv[]; iface = ifname; } m = &mb; + m->mb_data = (char *)m->mb_buf; } if (i != 0) @@ -311,14 +328,25 @@ char *argv[]; } if (dump == 1) { - dumpnat(); - dumpstate(); - dumplookups(); - dumpgroups(); + dumpnat(softc->ipf_nat_soft); + ipf_state_dump(softc, softc->ipf_state_soft); + ipf_lookup_dump(softc, softc->ipf_state_soft); + dumpgroups(softc); } - fr_deinitialise(); + ipf_fini_all(softc); + ipf_destroy_all(softc); + + ipf_unload_all(); + + ipf_mutex_clean(); + ipf_rwlock_clean(); + + if (getenv("FINDLEAKS")) { + fflush(stdout); + abort(); + } return 0; } @@ -332,14 +360,15 @@ int ipftestioctl(int dev, ioctlcmd_t cmd, ...) va_list ap; int i; + dev = dev; /* gcc -Wextra */ va_start(ap, cmd); data = va_arg(ap, caddr_t); va_end(ap); - i = iplioctl(IPL_LOGIPF, cmd, data, FWRITE|FREAD); + i = ipfioctl(softc, IPL_LOGIPF, cmd, data, FWRITE|FREAD); if (opts & OPT_DEBUG) - fprintf(stderr, "iplioctl(IPF,%#x,%p) = %d\n", - (u_int)cmd, data, i); + fprintf(stderr, "ipfioctl(IPF,%#x,%p) = %d (%d)\n", + (u_int)cmd, data, i, softc->ipf_interror); if (i != 0) { errno = i; return -1; @@ -354,13 +383,14 @@ int ipnattestioctl(int dev, ioctlcmd_t cmd, ...) va_list ap; int i; + dev = dev; /* gcc -Wextra */ va_start(ap, cmd); data = va_arg(ap, caddr_t); va_end(ap); - i = iplioctl(IPL_LOGNAT, cmd, data, FWRITE|FREAD); + i = ipfioctl(softc, IPL_LOGNAT, cmd, data, FWRITE|FREAD); if (opts & OPT_DEBUG) - fprintf(stderr, "iplioctl(NAT,%#x,%p) = %d\n", + fprintf(stderr, "ipfioctl(NAT,%#x,%p) = %d\n", (u_int)cmd, data, i); if (i != 0) { errno = i; @@ -376,13 +406,14 @@ int ipstatetestioctl(int dev, ioctlcmd_t cmd, ...) va_list ap; int i; + dev = dev; /* gcc -Wextra */ va_start(ap, cmd); data = va_arg(ap, caddr_t); va_end(ap); - i = iplioctl(IPL_LOGSTATE, cmd, data, FWRITE|FREAD); + i = ipfioctl(softc, IPL_LOGSTATE, cmd, data, FWRITE|FREAD); if ((opts & OPT_DEBUG) || (i != 0)) - fprintf(stderr, "iplioctl(STATE,%#x,%p) = %d\n", + fprintf(stderr, "ipfioctl(STATE,%#x,%p) = %d\n", (u_int)cmd, data, i); if (i != 0) { errno = i; @@ -398,13 +429,14 @@ int ipauthtestioctl(int dev, ioctlcmd_t cmd, ...) va_list ap; int i; + dev = dev; /* gcc -Wextra */ va_start(ap, cmd); data = va_arg(ap, caddr_t); va_end(ap); - i = iplioctl(IPL_LOGAUTH, cmd, data, FWRITE|FREAD); + i = ipfioctl(softc, IPL_LOGAUTH, cmd, data, FWRITE|FREAD); if ((opts & OPT_DEBUG) || (i != 0)) - fprintf(stderr, "iplioctl(AUTH,%#x,%p) = %d\n", + fprintf(stderr, "ipfioctl(AUTH,%#x,%p) = %d\n", (u_int)cmd, data, i); if (i != 0) { errno = i; @@ -420,13 +452,14 @@ int ipscantestioctl(int dev, ioctlcmd_t cmd, ...) va_list ap; int i; + dev = dev; /* gcc -Wextra */ va_start(ap, cmd); data = va_arg(ap, caddr_t); va_end(ap); - i = iplioctl(IPL_LOGSCAN, cmd, data, FWRITE|FREAD); + i = ipfioctl(softc, IPL_LOGSCAN, cmd, data, FWRITE|FREAD); if ((opts & OPT_DEBUG) || (i != 0)) - fprintf(stderr, "iplioctl(SCAN,%#x,%p) = %d\n", + fprintf(stderr, "ipfioctl(SCAN,%#x,%p) = %d\n", (u_int)cmd, data, i); if (i != 0) { errno = i; @@ -442,13 +475,14 @@ int ipsynctestioctl(int dev, ioctlcmd_t cmd, ...) va_list ap; int i; + dev = dev; /* gcc -Wextra */ va_start(ap, cmd); data = va_arg(ap, caddr_t); va_end(ap); - i = iplioctl(IPL_LOGSYNC, cmd, data, FWRITE|FREAD); + i = ipfioctl(softc, IPL_LOGSYNC, cmd, data, FWRITE|FREAD); if ((opts & OPT_DEBUG) || (i != 0)) - fprintf(stderr, "iplioctl(SYNC,%#x,%p) = %d\n", + fprintf(stderr, "ipfioctl(SYNC,%#x,%p) = %d\n", (u_int)cmd, data, i); if (i != 0) { errno = i; @@ -464,14 +498,15 @@ int ipooltestioctl(int dev, ioctlcmd_t cmd, ...) va_list ap; int i; + dev = dev; /* gcc -Wextra */ va_start(ap, cmd); data = va_arg(ap, caddr_t); va_end(ap); - i = iplioctl(IPL_LOGLOOKUP, cmd, data, FWRITE|FREAD); + i = ipfioctl(softc, IPL_LOGLOOKUP, cmd, data, FWRITE|FREAD); if ((opts & OPT_DEBUG) || (i != 0)) - fprintf(stderr, "iplioctl(POOL,%#x,%p) = %d\n", - (u_int)cmd, data, i); + fprintf(stderr, "ipfioctl(POOL,%#x,%p) = %d (%d)\n", + (u_int)cmd, data, i, softc->ipf_interror); if (i != 0) { errno = i; return -1; @@ -480,15 +515,17 @@ int ipooltestioctl(int dev, ioctlcmd_t cmd, ...) } #else int ipftestioctl(dev, cmd, data) -dev_t dev; -ioctlcmd_t cmd; -void *data; + dev_t dev; + ioctlcmd_t cmd; + void *data; { int i; - i = iplioctl(IPL_LOGIPF, cmd, data, FWRITE|FREAD); + dev = dev; /* gcc -Wextra */ + i = ipfioctl(softc, IPL_LOGIPF, cmd, data, FWRITE|FREAD); if ((opts & OPT_DEBUG) || (i != 0)) - fprintf(stderr, "iplioctl(IPF,%#x,%p) = %d\n", cmd, data, i); + fprintf(stderr, "ipfioctl(IPF,%#x,%p) = %d (%d)\n", + cmd, data, i, softc->ipf_interror); if (i != 0) { errno = i; return -1; @@ -498,15 +535,16 @@ void *data; int ipnattestioctl(dev, cmd, data) -dev_t dev; -ioctlcmd_t cmd; -void *data; + dev_t dev; + ioctlcmd_t cmd; + void *data; { int i; - i = iplioctl(IPL_LOGNAT, cmd, data, FWRITE|FREAD); + dev = dev; /* gcc -Wextra */ + i = ipfioctl(softc, IPL_LOGNAT, cmd, data, FWRITE|FREAD); if ((opts & OPT_DEBUG) || (i != 0)) - fprintf(stderr, "iplioctl(NAT,%#x,%p) = %d\n", cmd, data, i); + fprintf(stderr, "ipfioctl(NAT,%#x,%p) = %d\n", cmd, data, i); if (i != 0) { errno = i; return -1; @@ -516,15 +554,16 @@ void *data; int ipstatetestioctl(dev, cmd, data) -dev_t dev; -ioctlcmd_t cmd; -void *data; + dev_t dev; + ioctlcmd_t cmd; + void *data; { int i; - i = iplioctl(IPL_LOGSTATE, cmd, data, FWRITE|FREAD); + dev = dev; /* gcc -Wextra */ + i = ipfioctl(softc, IPL_LOGSTATE, cmd, data, FWRITE|FREAD); if ((opts & OPT_DEBUG) || (i != 0)) - fprintf(stderr, "iplioctl(STATE,%#x,%p) = %d\n", cmd, data, i); + fprintf(stderr, "ipfioctl(STATE,%#x,%p) = %d\n", cmd, data, i); if (i != 0) { errno = i; return -1; @@ -534,15 +573,16 @@ void *data; int ipauthtestioctl(dev, cmd, data) -dev_t dev; -ioctlcmd_t cmd; -void *data; + dev_t dev; + ioctlcmd_t cmd; + void *data; { int i; - i = iplioctl(IPL_LOGAUTH, cmd, data, FWRITE|FREAD); + dev = dev; /* gcc -Wextra */ + i = ipfioctl(softc, IPL_LOGAUTH, cmd, data, FWRITE|FREAD); if ((opts & OPT_DEBUG) || (i != 0)) - fprintf(stderr, "iplioctl(AUTH,%#x,%p) = %d\n", cmd, data, i); + fprintf(stderr, "ipfioctl(AUTH,%#x,%p) = %d\n", cmd, data, i); if (i != 0) { errno = i; return -1; @@ -552,15 +592,16 @@ void *data; int ipsynctestioctl(dev, cmd, data) -dev_t dev; -ioctlcmd_t cmd; -void *data; + dev_t dev; + ioctlcmd_t cmd; + void *data; { int i; - i = iplioctl(IPL_LOGSYNC, cmd, data, FWRITE|FREAD); + dev = dev; /* gcc -Wextra */ + i = ipfioctl(softc, IPL_LOGSYNC, cmd, data, FWRITE|FREAD); if ((opts & OPT_DEBUG) || (i != 0)) - fprintf(stderr, "iplioctl(SYNC,%#x,%p) = %d\n", cmd, data, i); + fprintf(stderr, "ipfioctl(SYNC,%#x,%p) = %d\n", cmd, data, i); if (i != 0) { errno = i; return -1; @@ -570,15 +611,16 @@ void *data; int ipscantestioctl(dev, cmd, data) -dev_t dev; -ioctlcmd_t cmd; -void *data; + dev_t dev; + ioctlcmd_t cmd; + void *data; { int i; - i = iplioctl(IPL_LOGSCAN, cmd, data, FWRITE|FREAD); + dev = dev; /* gcc -Wextra */ + i = ipfioctl(softc, IPL_LOGSCAN, cmd, data, FWRITE|FREAD); if ((opts & OPT_DEBUG) || (i != 0)) - fprintf(stderr, "iplioctl(SCAN,%#x,%p) = %d\n", cmd, data, i); + fprintf(stderr, "ipfioctl(SCAN,%#x,%p) = %d\n", cmd, data, i); if (i != 0) { errno = i; return -1; @@ -588,15 +630,17 @@ void *data; int ipooltestioctl(dev, cmd, data) -dev_t dev; -ioctlcmd_t cmd; -void *data; + dev_t dev; + ioctlcmd_t cmd; + void *data; { int i; - i = iplioctl(IPL_LOGLOOKUP, cmd, data, FWRITE|FREAD); + dev = dev; /* gcc -Wextra */ + i = ipfioctl(softc, IPL_LOGLOOKUP, cmd, data, FWRITE|FREAD); if (opts & OPT_DEBUG) - fprintf(stderr, "iplioctl(POOL,%#x,%p) = %d\n", cmd, data, i); + fprintf(stderr, "ipfioctl(POOL,%#x,%p) = %d (%d)\n", + cmd, data, i, softc->ipf_interror); if (i != 0) { errno = i; return -1; @@ -607,9 +651,9 @@ void *data; int kmemcpy(addr, offset, size) -char *addr; -long offset; -int size; + char *addr; + long offset; + int size; { bcopy((char *)offset, addr, size); return 0; @@ -617,9 +661,9 @@ int size; int kstrncpy(buf, pos, n) -char *buf; -long pos; -int n; + char *buf; + long pos; + int n; { char *ptr; @@ -634,100 +678,91 @@ int n; /* * Display the built up NAT table rules and mapping entries. */ -void dumpnat() +void dumpnat(arg) + void *arg; { + ipf_nat_softc_t *softn = arg; hostmap_t *hm; ipnat_t *ipn; nat_t *nat; printf("List of active MAP/Redirect filters:\n"); - for (ipn = nat_list; ipn != NULL; ipn = ipn->in_next) + for (ipn = softn->ipf_nat_list; ipn != NULL; ipn = ipn->in_next) printnat(ipn, opts & (OPT_DEBUG|OPT_VERBOSE)); printf("\nList of active sessions:\n"); - for (nat = nat_instances; nat; nat = nat->nat_next) { - printactivenat(nat, opts, 0, 0); + for (nat = softn->ipf_nat_instances; nat; nat = nat->nat_next) { + printactivenat(nat, opts, 0); if (nat->nat_aps) - printaps(nat->nat_aps, opts); + printf("\tproxy active\n"); } printf("\nHostmap table:\n"); - for (hm = ipf_hm_maplist; hm != NULL; hm = hm->hm_next) - printhostmap(hm, 0); + for (hm = softn->ipf_hm_maplist; hm != NULL; hm = hm->hm_next) + printhostmap(hm, hm->hm_hv); } -/* - * Display the built up state table rules and mapping entries. - */ -void dumpstate() -{ - ipstate_t *ips; - - printf("List of active state sessions:\n"); - for (ips = ips_list; ips != NULL; ) - ips = printstate(ips, opts & (OPT_DEBUG|OPT_VERBOSE), - fr_ticks); -} - - -void dumplookups() -{ - iphtable_t *iph; - ip_pool_t *ipl; - int i; - - printf("List of configured pools\n"); - for (i = 0; i < IPL_LOGSIZE; i++) - for (ipl = ip_pool_list[i]; ipl != NULL; ipl = ipl->ipo_next) - printpool(ipl, bcopywrap, NULL, opts); - - printf("List of configured hash tables\n"); - for (i = 0; i < IPL_LOGSIZE; i++) - for (iph = ipf_htables[i]; iph != NULL; iph = iph->iph_next) - printhash(iph, bcopywrap, NULL, opts); -} - - -void dumpgroups() +void dumpgroups(softc) + ipf_main_softc_t *softc; { frgroup_t *fg; - frentry_t *fr; int i; printf("List of groups configured (set 0)\n"); for (i = 0; i < IPL_LOGSIZE; i++) - for (fg = ipfgroups[i][0]; fg != NULL; fg = fg->fg_next) { + for (fg = softc->ipf_groups[i][0]; fg != NULL; + fg = fg->fg_next) { printf("Dev.%d. Group %s Ref %d Flags %#x\n", i, fg->fg_name, fg->fg_ref, fg->fg_flags); - for (fr = fg->fg_start; fr != NULL; fr = fr->fr_next) { -#ifdef USE_QUAD_T - printf("%qu ",(unsigned long long)fr->fr_hits); -#else - printf("%ld ", fr->fr_hits); -#endif - printfr(fr, ipftestioctl); - } + dumprules(fg->fg_start); } printf("List of groups configured (set 1)\n"); for (i = 0; i < IPL_LOGSIZE; i++) - for (fg = ipfgroups[i][1]; fg != NULL; fg = fg->fg_next) { + for (fg = softc->ipf_groups[i][1]; fg != NULL; + fg = fg->fg_next) { printf("Dev.%d. Group %s Ref %d Flags %#x\n", i, fg->fg_name, fg->fg_ref, fg->fg_flags); - for (fr = fg->fg_start; fr != NULL; fr = fr->fr_next) { -#ifdef USE_QUAD_T - printf("%qu ",(unsigned long long)fr->fr_hits); -#else - printf("%ld ", fr->fr_hits); -#endif - printfr(fr, ipftestioctl); - } + dumprules(fg->fg_start); } + + printf("Rules configured (set 0, in)\n"); + dumprules(softc->ipf_rules[0][0]); + printf("Rules configured (set 0, out)\n"); + dumprules(softc->ipf_rules[1][0]); + printf("Rules configured (set 1, in)\n"); + dumprules(softc->ipf_rules[0][1]); + printf("Rules configured (set 1, out)\n"); + dumprules(softc->ipf_rules[1][1]); + + printf("Accounting rules configured (set 0, in)\n"); + dumprules(softc->ipf_acct[0][0]); + printf("Accounting rules configured (set 0, out)\n"); + dumprules(softc->ipf_acct[0][1]); + printf("Accounting rules configured (set 1, in)\n"); + dumprules(softc->ipf_acct[1][0]); + printf("Accounting rules configured (set 1, out)\n"); + dumprules(softc->ipf_acct[1][1]); +} + +void dumprules(rulehead) + frentry_t *rulehead; +{ + frentry_t *fr; + + for (fr = rulehead; fr != NULL; fr = fr->fr_next) { +#ifdef USE_QUAD_T + printf("%"PRIu64" ",(unsigned long long)fr->fr_hits); +#else + printf("%ld ", fr->fr_hits); +#endif + printfr(fr, ipftestioctl); + } } void drain_log(filename) -char *filename; + char *filename; { char buffer[DEFAULT_IPFLOGSIZE]; struct iovec iov; @@ -753,7 +788,7 @@ char *filename; uio.uio_resid = iov.iov_len; resid = uio.uio_resid; - if (ipflog_read(i, &uio) == 0) { + if (ipf_log_read(softc, i, &uio) == 0) { /* * If nothing was read then break out. */ @@ -769,18 +804,38 @@ char *filename; void fixv4sums(m, ip) -mb_t *m; -ip_t *ip; + mb_t *m; + ip_t *ip; { - u_char *csump, *hdr; + u_char *csump, *hdr, p; + fr_info_t tmp; + int len; - ip->ip_sum = 0; - ip->ip_sum = ipf_cksum((u_short *)ip, IP_HL(ip) << 2); + p = 0; + len = 0; + bzero((char *)&tmp, sizeof(tmp)); csump = (u_char *)ip; - csump += IP_HL(ip) << 2; + if (IP_V(ip) == 4) { + ip->ip_sum = 0; + ip->ip_sum = ipf_cksum((u_short *)ip, IP_HL(ip) << 2); + tmp.fin_hlen = IP_HL(ip) << 2; + csump += IP_HL(ip) << 2; + p = ip->ip_p; + len = ntohs(ip->ip_len); +#ifdef USE_INET6 + } else if (IP_V(ip) == 6) { + tmp.fin_hlen = sizeof(ip6_t); + csump += sizeof(ip6_t); + p = ((ip6_t *)ip)->ip6_nxt; + len = ntohs(((ip6_t *)ip)->ip6_plen); + len += sizeof(ip6_t); +#endif + } + tmp.fin_plen = len; + tmp.fin_dlen = len - tmp.fin_hlen; - switch (ip->ip_p) + switch (p) { case IPPROTO_TCP : hdr = csump; @@ -800,7 +855,12 @@ ip_t *ip; break; } if (hdr != NULL) { + tmp.fin_m = m; + tmp.fin_mp = &m; + tmp.fin_dp = hdr; + tmp.fin_ip = ip; + tmp.fin_plen = len; *csump = 0; - *(u_short *)csump = fr_cksum(m, ip, ip->ip_p, hdr, ip->ip_len); + *(u_short *)csump = fr_cksum(&tmp, ip, p, hdr); } } diff --git a/contrib/ipfilter/tools/ipmon.c b/contrib/ipfilter/tools/ipmon.c index ceaed82cb4b..1c52e7fd87a 100644 --- a/contrib/ipfilter/tools/ipmon.c +++ b/contrib/ipfilter/tools/ipmon.c @@ -1,84 +1,22 @@ /* $FreeBSD$ */ /* - * Copyright (C) 2001-2006 by Darren Reed. + * Copyright (C) 2012 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. */ -#ifndef SOLARIS -#define SOLARIS (defined(__SVR4) || defined(__svr4__)) && defined(sun) -#endif - -#include -#include -#include -#include -#include -#define _KERNEL -#include -#undef _KERNEL -#include -#include - -#include -#include -#include -#include -#include -#include -#if !defined(__SVR4) && !defined(__svr4__) -# if (__FreeBSD_version >= 300000) -# include -# else -# include -# endif -#else -# include -# include -#endif -#if !defined(__hpux) && (!defined(__SVR4) && !defined(__GNUC__)) -# include -#endif -#include -#include -#include -#include -#include -#include -#include -#if !defined(__hpux) && !defined(linux) -# include -#endif -#include -#include -#include -#ifdef __hpux -# undef NOERROR -#endif -#include - -#if !defined(linux) -# include -# include -#endif - -#include -#include - -#include -#include - -#include "netinet/ip_compat.h" -#include -#include "netinet/ip_fil.h" -#include "netinet/ip_nat.h" -#include "netinet/ip_state.h" -#include "netinet/ip_proxy.h" +#include "ipf.h" #include "ipmon.h" +#include +#include +#include +#include +#include +#include #if !defined(lint) static const char sccsid[] = "@(#)ipmon.c 1.21 6/5/96 (C)1993-2000 Darren Reed"; -static const char rcsid[] = "@(#)$Id: ipmon.c,v 1.33.2.20 2007/09/20 12:51:56 darrenr Exp $"; +static const char rcsid[] = "@(#)$Id$"; #endif @@ -89,12 +27,41 @@ extern char *sys_errlist[]; #define STRERROR(x) strerror(x) #endif +extern int optind; +extern char *optarg; + +extern ipmon_saver_t executesaver; +extern ipmon_saver_t filesaver; +extern ipmon_saver_t nothingsaver; +extern ipmon_saver_t snmpv1saver; +extern ipmon_saver_t snmpv2saver; +extern ipmon_saver_t syslogsaver; + struct flags { int value; char flag; }; +typedef struct logsource { + int fd; + int logtype; + char *file; + int regular; + size_t size; +} logsource_t; + +typedef struct config { + int opts; + int maxfd; + logsource_t logsrc[3]; + fd_set fdmr; + FILE *blog; + char *bfile; + FILE *log; + char *file; + char *cfile; +} config_t; typedef struct icmp_subtype { int ist_val; @@ -124,6 +91,28 @@ struct flags tcpfl[] = { { 0, '\0' } }; +char *reasons[] = { + "filter-rule", + "log-or-block_1", + "pps-rate", + "jumbogram", + "makefrip-fail", + "state_add-fail", + "updateipid-fail", + "log-or-block_2", + "decap-fail", + "auth_new-fail", + "auth_captured", + "coalesce-fail", + "pullup-fail", + "auth-feedback", + "bad-frag", + "natv4_out-fail", + "natv4_in-fail", + "natv6_out-fail", + "natv6_in-fail", +}; + #ifdef MENTAT static char *pidfile = "/etc/opt/ipf/ipmon.pid"; #else @@ -135,18 +124,14 @@ static char *pidfile = "/etc/ipmon.pid"; #endif static char line[2048]; -static int opts = 0; -static char *logfile = NULL; -static FILE *binarylog = NULL; -static char *binarylogfile = NULL; static int donehup = 0; static void usage __P((char *)); static void handlehup __P((int)); static void flushlogs __P((char *, FILE *)); -static void print_log __P((int, FILE *, char *, int)); -static void print_ipflog __P((FILE *, char *, int)); -static void print_natlog __P((FILE *, char *, int)); -static void print_statelog __P((FILE *, char *, int)); +static void print_log __P((config_t *, logsource_t *, char *, int)); +static void print_ipflog __P((config_t *, char *, int)); +static void print_natlog __P((config_t *, char *, int)); +static void print_statelog __P((config_t *, char *, int)); static int read_log __P((int, int *, char *, int)); static void write_pid __P((char *)); static char *icmpname __P((u_int, u_int)); @@ -159,39 +144,30 @@ static struct tm *get_tm __P((u_32_t)); static struct tm *get_tm __P((time_t)); #endif -char *hostname __P((int, int, u_32_t *)); -char *portname __P((int, char *, u_int)); +char *portlocalname __P((int, char *, u_int)); int main __P((int, char *[])); static void logopts __P((int, char *)); static void init_tabs __P((void)); -static char *getproto __P((u_int)); +static char *getlocalproto __P((u_int)); +static void openlogs __P((config_t *conf)); +static int read_loginfo __P((config_t *conf)); +static void initconfig __P((config_t *conf)); static char **protocols = NULL; static char **udp_ports = NULL; static char **tcp_ports = NULL; -static char *conf_file = NULL; -#define OPT_SYSLOG 0x001 -#define OPT_RESOLVE 0x002 -#define OPT_HEXBODY 0x004 -#define OPT_VERBOSE 0x008 -#define OPT_HEXHDR 0x010 -#define OPT_TAIL 0x020 -#define OPT_NAT 0x080 -#define OPT_STATE 0x100 -#define OPT_FILTER 0x200 -#define OPT_PORTNUM 0x400 -#define OPT_LOGALL (OPT_NAT|OPT_STATE|OPT_FILTER) -#define OPT_LOGBODY 0x800 - -#define HOSTNAME_V4(a,b) hostname((a), 4, (u_32_t *)&(b)) +#define HOSTNAMEV4(b) hostname(AF_INET, (u_32_t *)&(b)) #ifndef LOGFAC #define LOGFAC LOG_LOCAL0 #endif int logfac = LOGFAC; +int ipmonopts = 0; +int opts = OPT_NORESOLVE; +int use_inet6 = 0; static icmp_subtype_t icmpunreachnames[] = { @@ -233,7 +209,7 @@ static icmp_subtype_t paramnames[] = { { -2, NULL } }; -static icmp_type_t icmptypes[] = { +static icmp_type_t icmptypes4[] = { { ICMP_ECHOREPLY, NULL, 0, "echoreply" }, { -1, NULL, 0, NULL }, { -1, NULL, 0, NULL }, @@ -338,9 +314,9 @@ static icmp_type_t icmptypes6[] = { }; static icmp_subtype_t *find_icmpsubtype(type, table, tablesz) -int type; -icmp_subtype_t *table; -size_t tablesz; + int type; + icmp_subtype_t *table; + size_t tablesz; { icmp_subtype_t *ist; int i; @@ -363,9 +339,9 @@ size_t tablesz; static icmp_type_t *find_icmptype(type, table, tablesz) -int type; -icmp_type_t *table; -size_t tablesz; + int type; + icmp_type_t *table; + size_t tablesz; { icmp_type_t *it; int i; @@ -388,7 +364,7 @@ size_t tablesz; static void handlehup(sig) -int sig; + int sig; { signal(SIGHUP, handlehup); donehup = 1; @@ -421,12 +397,12 @@ static void init_tabs() p->p_name != NULL && protocols[p->p_proto] == NULL) protocols[p->p_proto] = strdup(p->p_name); endprotoent(); -#if defined(_AIX51) if (protocols[0]) free(protocols[0]); + protocols[0] = strdup("ip"); +#if defined(_AIX51) if (protocols[252]) free(protocols[252]); - protocols[0] = "ip"; protocols[252] = NULL; #endif } @@ -480,8 +456,8 @@ static void init_tabs() } -static char *getproto(p) -u_int p; +static char *getlocalproto(p) + u_int p; { static char pnum[4]; char *s; @@ -497,11 +473,14 @@ u_int p; static int read_log(fd, lenp, buf, bufsize) -int fd, bufsize, *lenp; -char *buf; + int fd, bufsize, *lenp; + char *buf; { int nr; + if (bufsize > IPFILTER_LOGSIZE) + bufsize = IPFILTER_LOGSIZE; + nr = read(fd, buf, bufsize); if (!nr) return 2; @@ -512,51 +491,18 @@ char *buf; } -char *hostname(res, v, ip) -int res, v; -u_32_t *ip; +char *portlocalname(res, proto, port) + int res; + char *proto; + u_int port; { -# define MAX_INETA 16 - static char hname[MAXHOSTNAMELEN + MAX_INETA + 3]; -#ifdef USE_INET6 - static char hostbuf[MAXHOSTNAMELEN+1]; -#endif - struct hostent *hp; - struct in_addr ipa; - - if (v == 4) { - ipa.s_addr = *ip; - if (!res) - return inet_ntoa(ipa); - hp = gethostbyaddr((char *)ip, sizeof(*ip), AF_INET); - if (!hp) - return inet_ntoa(ipa); - sprintf(hname, "%.*s[%s]", MAXHOSTNAMELEN, hp->h_name, - inet_ntoa(ipa)); - return hname; - } -#ifdef USE_INET6 - (void) inet_ntop(AF_INET6, ip, hostbuf, sizeof(hostbuf) - 1); - hostbuf[MAXHOSTNAMELEN] = '\0'; - return hostbuf; -#else - return "IPv6"; -#endif -} - - -char *portname(res, proto, port) -int res; -char *proto; -u_int port; -{ - static char pname[8]; - char *s; + static char pname[8]; + char *s; port = ntohs(port); port &= 0xffff; - (void) sprintf(pname, "%u", port); - if (!res || (opts & OPT_PORTNUM)) + sprintf(pname, "%u", port); + if (!res || (ipmonopts & IPMON_PORTNUM)) return pname; s = NULL; if (!strcmp(proto, "tcp")) @@ -569,9 +515,9 @@ u_int port; } -static char *icmpname(type, code) -u_int type; -u_int code; +static char *icmpname(type, code) + u_int type; + u_int code; { static char name[80]; icmp_subtype_t *ist; @@ -579,7 +525,7 @@ u_int code; char *s; s = NULL; - it = find_icmptype(type, icmptypes, sizeof(icmptypes) / sizeof(*it)); + it = find_icmptype(type, icmptypes4, sizeof(icmptypes4) / sizeof(*it)); if (it != NULL) s = it->it_name; @@ -600,9 +546,9 @@ u_int code; return name; } -static char *icmpname6(type, code) -u_int type; -u_int code; +static char *icmpname6(type, code) + u_int type; + u_int code; { static char name[80]; icmp_subtype_t *ist; @@ -632,11 +578,11 @@ u_int code; } -void dumphex(log, dopts, buf, len) -FILE *log; -int dopts; -char *buf; -int len; +void dumphex(log, dopts, buf, len) + FILE *log; + int dopts; + char *buf; + int len; { char hline[80]; int i, j, k; @@ -651,7 +597,7 @@ int len; if (j && !(j & 0xf)) { *t++ = '\n'; *t = '\0'; - if ((dopts & OPT_SYSLOG)) + if ((dopts & IPMON_SYSLOG)) syslog(LOG_INFO, "%s", hline); else if (log != NULL) fputs(hline, log); @@ -665,10 +611,10 @@ int len; sprintf((char *)t, " "); t += 8; for (k = 16; k; k--, s++) - *t++ = (ISPRINT(*s) ? *s : '.'); + *t++ = (isprint(*s) ? *s : '.'); s--; } - + if ((j + 1) & 0xf) *t++ = ' ';; } @@ -683,11 +629,11 @@ int len; t += 7; s -= j & 0xf; for (k = j & 0xf; k; k--, s++) - *t++ = (ISPRINT(*s) ? *s : '.'); + *t++ = (isprint(*s) ? *s : '.'); *t++ = '\n'; *t = '\0'; } - if ((dopts & OPT_SYSLOG) != 0) + if ((dopts & IPMON_SYSLOG) != 0) syslog(LOG_INFO, "%s", hline); else if (log != NULL) { fputs(hline, log); @@ -696,11 +642,11 @@ int len; } -static struct tm *get_tm(sec) +static struct tm *get_tm(sec) #ifdef __hpux -u_32_t sec; + u_32_t sec; #else -time_t sec; + time_t sec; #endif { struct tm *tm; @@ -711,23 +657,44 @@ time_t sec; return tm; } -static void print_natlog(log, buf, blen) -FILE *log; -char *buf; -int blen; +static void print_natlog(conf, buf, blen) + config_t *conf; + char *buf; + int blen; { - struct natlog *nl; - iplog_t *ipl = (iplog_t *)buf; - char *t = line; - struct tm *tm; - int res, i, len; - char *proto; + static u_32_t seqnum = 0; + int res, i, len, family; + struct natlog *nl; + struct tm *tm; + iplog_t *ipl; + char *proto; + int simple; + char *t; + + t = line; + simple = 0; + ipl = (iplog_t *)buf; + if (ipl->ipl_seqnum != seqnum) { + if ((ipmonopts & IPMON_SYSLOG) != 0) { + syslog(LOG_WARNING, + "missed %u NAT log entries: %u %u", + ipl->ipl_seqnum - seqnum, seqnum, + ipl->ipl_seqnum); + } else { + (void) fprintf(conf->log, + "missed %u NAT log entries: %u %u\n", + ipl->ipl_seqnum - seqnum, seqnum, + ipl->ipl_seqnum); + } + } + seqnum = ipl->ipl_seqnum + ipl->ipl_count; nl = (struct natlog *)((char *)ipl + sizeof(*ipl)); - res = (opts & OPT_RESOLVE) ? 1 : 0; + res = (ipmonopts & IPMON_RESOLVE) ? 1 : 0; tm = get_tm(ipl->ipl_sec); len = sizeof(line); - if (!(opts & OPT_SYSLOG)) { + + if (!(ipmonopts & IPMON_SYSLOG)) { (void) strftime(t, len, "%d/%m/%Y ", tm); i = strlen(t); len -= i; @@ -735,81 +702,184 @@ int blen; } (void) strftime(t, len, "%T", tm); t += strlen(t); - (void) sprintf(t, ".%-.6ld @%hd ", ipl->ipl_usec, nl->nl_rule + 1); + sprintf(t, ".%-.6ld @%hd ", (long)ipl->ipl_usec, nl->nl_rule + 1); t += strlen(t); - if (nl->nl_type == NL_NEWMAP) - strcpy(t, "NAT:MAP "); - else if (nl->nl_type == NL_NEWRDR) - strcpy(t, "NAT:RDR "); - else if (nl->nl_type == NL_FLUSH) - strcpy(t, "NAT:FLUSH "); - else if (nl->nl_type == NL_EXPIRE) - strcpy(t, "NAT:EXPIRE "); - else if (nl->nl_type == NL_NEWBIMAP) - strcpy(t, "NAT:BIMAP "); - else if (nl->nl_type == NL_NEWBLOCK) - strcpy(t, "NAT:MAPBLOCK "); - else if (nl->nl_type == NL_CLONE) - strcpy(t, "NAT:CLONE "); - else if (nl->nl_type == NL_DESTROY) - strcpy(t, "NAT:DESTROY "); - else - sprintf(t, "Type: %d ", nl->nl_type); + switch (nl->nl_action) + { + case NL_NEW : + strcpy(t, "NAT:NEW"); + break; + + case NL_FLUSH : + strcpy(t, "NAT:FLUSH"); + break; + + case NL_CLONE : + strcpy(t, "NAT:CLONE"); + break; + + case NL_EXPIRE : + strcpy(t, "NAT:EXPIRE"); + break; + + case NL_DESTROY : + strcpy(t, "NAT:DESTROY"); + break; + + case NL_PURGE : + strcpy(t, "NAT:PURGE"); + break; + + default : + sprintf(t, "NAT:Action(%d)", nl->nl_action); + break; + } t += strlen(t); - proto = getproto(nl->nl_p); - (void) sprintf(t, "%s,%s <- -> ", HOSTNAME_V4(res, nl->nl_inip), - portname(res, proto, (u_int)nl->nl_inport)); + switch (nl->nl_type) + { + case NAT_MAP : + strcpy(t, "-MAP "); + simple = 1; + break; + + case NAT_REDIRECT : + strcpy(t, "-RDR "); + simple = 1; + break; + + case NAT_BIMAP : + strcpy(t, "-BIMAP "); + simple = 1; + break; + + case NAT_MAPBLK : + strcpy(t, "-MAPBLOCK "); + simple = 1; + break; + + case NAT_REWRITE|NAT_MAP : + strcpy(t, "-RWR_MAP "); + break; + + case NAT_REWRITE|NAT_REDIRECT : + strcpy(t, "-RWR_RDR "); + break; + + case NAT_ENCAP|NAT_MAP : + strcpy(t, "-ENC_MAP "); + break; + + case NAT_ENCAP|NAT_REDIRECT : + strcpy(t, "-ENC_RDR "); + break; + + case NAT_DIVERTUDP|NAT_MAP : + strcpy(t, "-DIV_MAP "); + break; + + case NAT_DIVERTUDP|NAT_REDIRECT : + strcpy(t, "-DIV_RDR "); + break; + + default : + sprintf(t, "-Type(%d) ", nl->nl_type); + break; + } t += strlen(t); - (void) sprintf(t, "%s,%s ", HOSTNAME_V4(res, nl->nl_outip), - portname(res, proto, (u_int)nl->nl_outport)); + + proto = getlocalproto(nl->nl_p[0]); + + family = vtof(nl->nl_v[0]); + + if (simple == 1) { + sprintf(t, "%s,%s <- -> ", hostname(family, nl->nl_osrcip.i6), + portlocalname(res, proto, (u_int)nl->nl_osrcport)); + t += strlen(t); + sprintf(t, "%s,%s ", hostname(family, nl->nl_nsrcip.i6), + portlocalname(res, proto, (u_int)nl->nl_nsrcport)); + t += strlen(t); + sprintf(t, "[%s,%s] ", hostname(family, nl->nl_odstip.i6), + portlocalname(res, proto, (u_int)nl->nl_odstport)); + } else { + sprintf(t, "%s,%s ", hostname(family, nl->nl_osrcip.i6), + portlocalname(res, proto, (u_int)nl->nl_osrcport)); + t += strlen(t); + sprintf(t, "%s,%s <- -> ", hostname(family, nl->nl_odstip.i6), + portlocalname(res, proto, (u_int)nl->nl_odstport)); + t += strlen(t); + sprintf(t, "%s,%s ", hostname(family, nl->nl_nsrcip.i6), + portlocalname(res, proto, (u_int)nl->nl_nsrcport)); + t += strlen(t); + sprintf(t, "%s,%s ", hostname(family, nl->nl_ndstip.i6), + portlocalname(res, proto, (u_int)nl->nl_ndstport)); + } t += strlen(t); - (void) sprintf(t, "[%s,%s PR %s]", HOSTNAME_V4(res, nl->nl_origip), - portname(res, proto, (u_int)nl->nl_origport), - getproto(nl->nl_p)); + + strcpy(t, getlocalproto(nl->nl_p[0])); t += strlen(t); - if (nl->nl_type == NL_EXPIRE) { + + if (nl->nl_action == NL_EXPIRE || nl->nl_action == NL_FLUSH) { #ifdef USE_QUAD_T - (void) sprintf(t, " Pkts %qd/%qd Bytes %qd/%qd", - (long long)nl->nl_pkts[0], - (long long)nl->nl_pkts[1], - (long long)nl->nl_bytes[0], - (long long)nl->nl_bytes[1]); +# ifdef PRId64 + sprintf(t, " Pkts %" PRId64 "/%" PRId64 " Bytes %" PRId64 "/%" + PRId64, +# else + sprintf(t, " Pkts %qd/%qd Bytes %qd/%qd", +# endif #else - (void) sprintf(t, " Pkts %ld/%ld Bytes %ld/%ld", + sprintf(t, " Pkts %ld/%ld Bytes %ld/%ld", +#endif nl->nl_pkts[0], nl->nl_pkts[1], nl->nl_bytes[0], nl->nl_bytes[1]); -#endif t += strlen(t); } *t++ = '\n'; *t++ = '\0'; - if (opts & OPT_SYSLOG) + if (ipmonopts & IPMON_SYSLOG) syslog(LOG_INFO, "%s", line); - else if (log != NULL) - (void) fprintf(log, "%s", line); + else if (conf->log != NULL) + (void) fprintf(conf->log, "%s", line); } -static void print_statelog(log, buf, blen) -FILE *log; -char *buf; -int blen; +static void print_statelog(conf, buf, blen) + config_t *conf; + char *buf; + int blen; { - struct ipslog *sl; - iplog_t *ipl = (iplog_t *)buf; - char *t = line, *proto; - struct tm *tm; - int res, i, len; + static u_32_t seqnum = 0; + int res, i, len, family; + struct ipslog *sl; + char *t, *proto; + struct tm *tm; + iplog_t *ipl; + + t = line; + ipl = (iplog_t *)buf; + if (ipl->ipl_seqnum != seqnum) { + if ((ipmonopts & IPMON_SYSLOG) != 0) { + syslog(LOG_WARNING, + "missed %u state log entries: %u %u", + ipl->ipl_seqnum - seqnum, seqnum, + ipl->ipl_seqnum); + } else { + (void) fprintf(conf->log, + "missed %u state log entries: %u %u\n", + ipl->ipl_seqnum - seqnum, seqnum, + ipl->ipl_seqnum); + } + } + seqnum = ipl->ipl_seqnum + ipl->ipl_count; sl = (struct ipslog *)((char *)ipl + sizeof(*ipl)); - res = (opts & OPT_RESOLVE) ? 1 : 0; + res = (ipmonopts & IPMON_RESOLVE) ? 1 : 0; tm = get_tm(ipl->ipl_sec); len = sizeof(line); - if (!(opts & OPT_SYSLOG)) { + if (!(ipmonopts & IPMON_SYSLOG)) { (void) strftime(t, len, "%d/%m/%Y ", tm); i = strlen(t); len -= i; @@ -817,9 +887,11 @@ int blen; } (void) strftime(t, len, "%T", tm); t += strlen(t); - (void) sprintf(t, ".%-.6ld ", ipl->ipl_usec); + sprintf(t, ".%-.6ld ", (long)ipl->ipl_usec); t += strlen(t); + family = vtof(sl->isl_v); + switch (sl->isl_type) { case ISL_NEW : @@ -865,41 +937,37 @@ int blen; } t += strlen(t); - proto = getproto(sl->isl_p); + proto = getlocalproto(sl->isl_p); if (sl->isl_p == IPPROTO_TCP || sl->isl_p == IPPROTO_UDP) { - (void) sprintf(t, "%s,%s -> ", - hostname(res, sl->isl_v, (u_32_t *)&sl->isl_src), - portname(res, proto, (u_int)sl->isl_sport)); + sprintf(t, "%s,%s -> ", + hostname(family, (u_32_t *)&sl->isl_src), + portlocalname(res, proto, (u_int)sl->isl_sport)); t += strlen(t); - (void) sprintf(t, "%s,%s PR %s", - hostname(res, sl->isl_v, (u_32_t *)&sl->isl_dst), - portname(res, proto, (u_int)sl->isl_dport), proto); + sprintf(t, "%s,%s PR %s", + hostname(family, (u_32_t *)&sl->isl_dst), + portlocalname(res, proto, (u_int)sl->isl_dport), proto); } else if (sl->isl_p == IPPROTO_ICMP) { - (void) sprintf(t, "%s -> ", hostname(res, sl->isl_v, - (u_32_t *)&sl->isl_src)); + sprintf(t, "%s -> ", hostname(family, (u_32_t *)&sl->isl_src)); t += strlen(t); - (void) sprintf(t, "%s PR icmp %d", - hostname(res, sl->isl_v, (u_32_t *)&sl->isl_dst), + sprintf(t, "%s PR icmp %d", + hostname(family, (u_32_t *)&sl->isl_dst), sl->isl_itype); } else if (sl->isl_p == IPPROTO_ICMPV6) { - (void) sprintf(t, "%s -> ", hostname(res, sl->isl_v, - (u_32_t *)&sl->isl_src)); + sprintf(t, "%s -> ", hostname(family, (u_32_t *)&sl->isl_src)); t += strlen(t); - (void) sprintf(t, "%s PR icmpv6 %d", - hostname(res, sl->isl_v, (u_32_t *)&sl->isl_dst), + sprintf(t, "%s PR icmpv6 %d", + hostname(family, (u_32_t *)&sl->isl_dst), sl->isl_itype); } else { - (void) sprintf(t, "%s -> ", - hostname(res, sl->isl_v, (u_32_t *)&sl->isl_src)); + sprintf(t, "%s -> ", hostname(family, (u_32_t *)&sl->isl_src)); t += strlen(t); - (void) sprintf(t, "%s PR %s", - hostname(res, sl->isl_v, (u_32_t *)&sl->isl_dst), - proto); + sprintf(t, "%s PR %s", + hostname(family, (u_32_t *)&sl->isl_dst), proto); } t += strlen(t); if (sl->isl_tag != FR_NOLOGTAG) { - (void) sprintf(t, " tag %u", sl->isl_tag); + sprintf(t, " tag %u", sl->isl_tag); t += strlen(t); } if (sl->isl_type != ISL_NEW) { @@ -926,22 +994,26 @@ int blen; *t++ = '\n'; *t++ = '\0'; - if (opts & OPT_SYSLOG) + if (ipmonopts & IPMON_SYSLOG) syslog(LOG_INFO, "%s", line); - else if (log != NULL) - (void) fprintf(log, "%s", line); + else if (conf->log != NULL) + (void) fprintf(conf->log, "%s", line); } -static void print_log(logtype, log, buf, blen) -FILE *log; -char *buf; -int logtype, blen; +static void print_log(conf, log, buf, blen) + config_t *conf; + logsource_t *log; + char *buf; + int blen; { + char *bp, *bpo; iplog_t *ipl; - char *bp = NULL, *bpo = NULL; int psize; + bp = NULL; + bpo = NULL; + while (blen > 0) { ipl = (iplog_t *)buf; if ((u_long)ipl & (sizeof(long)-1)) { @@ -961,22 +1033,22 @@ int logtype, blen; if (psize > blen) break; - if (binarylog) { - fwrite(buf, psize, 1, binarylog); - fflush(binarylog); + if (conf->blog != NULL) { + fwrite(buf, psize, 1, conf->blog); + fflush(conf->blog); } - if (logtype == IPL_LOGIPF) { + if (log->logtype == IPL_LOGIPF) { if (ipl->ipl_magic == IPL_MAGIC) - print_ipflog(log, buf, psize); + print_ipflog(conf, buf, psize); - } else if (logtype == IPL_LOGNAT) { + } else if (log->logtype == IPL_LOGNAT) { if (ipl->ipl_magic == IPL_MAGIC_NAT) - print_natlog(log, buf, psize); + print_natlog(conf, buf, psize); - } else if (logtype == IPL_LOGSTATE) { + } else if (log->logtype == IPL_LOGSTATE) { if (ipl->ipl_magic == IPL_MAGIC_STATE) - print_statelog(log, buf, psize); + print_statelog(conf, buf, psize); } blen -= psize; @@ -988,22 +1060,23 @@ int logtype, blen; } -static void print_ipflog(log, buf, blen) -FILE *log; -char *buf; -int blen; +static void print_ipflog(conf, buf, blen) + config_t *conf; + char *buf; + int blen; { - tcphdr_t *tp; - struct icmp *ic; - struct icmp *icmp; - struct tm *tm; - char *t, *proto; - int i, v, lvl, res, len, off, plen, ipoff, defaction; - ip_t *ipc, *ip; - u_32_t *s, *d; - u_short hl, p; + static u_32_t seqnum = 0; + int i, f, lvl, res, len, off, plen, ipoff, defaction; + struct icmp *icmp; + struct icmp *ic; + char *t, *proto; + ip_t *ipc, *ip; + struct tm *tm; + u_32_t *s, *d; + u_short hl, p; ipflog_t *ipf; - iplog_t *ipl; + iplog_t *ipl; + tcphdr_t *tp; #ifdef USE_INET6 struct ip6_ext *ehp; u_short ehl; @@ -1012,16 +1085,31 @@ int blen; #endif ipl = (iplog_t *)buf; + if (ipl->ipl_seqnum != seqnum) { + if ((ipmonopts & IPMON_SYSLOG) != 0) { + syslog(LOG_WARNING, + "missed %u ipf log entries: %u %u", + ipl->ipl_seqnum - seqnum, seqnum, + ipl->ipl_seqnum); + } else { + (void) fprintf(conf->log, + "missed %u ipf log entries: %u %u\n", + ipl->ipl_seqnum - seqnum, seqnum, + ipl->ipl_seqnum); + } + } + seqnum = ipl->ipl_seqnum + ipl->ipl_count; + ipf = (ipflog_t *)((char *)buf + sizeof(*ipl)); ip = (ip_t *)((char *)ipf + sizeof(*ipf)); - v = IP_V(ip); - res = (opts & OPT_RESOLVE) ? 1 : 0; + f = ipf->fl_family; + res = (ipmonopts & IPMON_RESOLVE) ? 1 : 0; t = line; *t = '\0'; tm = get_tm(ipl->ipl_sec); len = sizeof(line); - if (!(opts & OPT_SYSLOG)) { + if (!(ipmonopts & IPMON_SYSLOG)) { (void) strftime(t, len, "%d/%m/%Y ", tm); i = strlen(t); len -= i; @@ -1029,10 +1117,10 @@ int blen; } (void) strftime(t, len, "%T", tm); t += strlen(t); - (void) sprintf(t, ".%-.6ld ", ipl->ipl_usec); + sprintf(t, ".%-.6ld ", (long)ipl->ipl_usec); t += strlen(t); if (ipl->ipl_count > 1) { - (void) sprintf(t, "%dx ", ipl->ipl_count); + sprintf(t, "%dx ", ipl->ipl_count); t += strlen(t); } #if (defined(MENTAT) || \ @@ -1044,13 +1132,19 @@ int blen; strncpy(ifname, ipf->fl_ifname, sizeof(ipf->fl_ifname)); ifname[sizeof(ipf->fl_ifname)] = '\0'; - (void) sprintf(t, "%s", ifname); + sprintf(t, "%s", ifname); t += strlen(t); # if defined(MENTAT) || defined(linux) - if (ISALPHA(*(t - 1))) { - sprintf(t, "%d", ipf->fl_unit); - t += strlen(t); - } +# if defined(linux) + /* + * On Linux, the loopback interface is just "lo", not "lo0". + */ + if (strcmp(ifname, "lo") != 0) +# endif + if (ISALPHA(*(t - 1))) { + sprintf(t, "%d", ipf->fl_unit); + t += strlen(t); + } # endif } #else @@ -1059,7 +1153,7 @@ int blen; break; if (ipf->fl_ifname[len]) len++; - (void) sprintf(t, "%*.*s%u", len, len, ipf->fl_ifname, ipf->fl_unit); + sprintf(t, "%*.*s%u", len, len, ipf->fl_ifname, ipf->fl_unit); t += strlen(t); #endif if ((ipf->fl_group[0] == (char)~0) && (ipf->fl_group[1] == '\0')) @@ -1067,12 +1161,12 @@ int blen; else if (ipf->fl_group[0] == '\0') (void) strcpy(t, " @0:"); else - (void) sprintf(t, " @%s:", ipf->fl_group); + sprintf(t, " @%s:", ipf->fl_group); t += strlen(t); if (ipf->fl_rule == 0xffffffff) strcat(t, "-1 "); else - (void) sprintf(t, "%u ", ipf->fl_rule + 1); + sprintf(t, "%u ", ipf->fl_rule + 1); t += strlen(t); lvl = LOG_NOTICE; @@ -1107,8 +1201,17 @@ int blen; *t++ = ' '; *t = '\0'; - if (v == 6) { + if (f == AF_INET) { + hl = IP_HL(ip) << 2; + ipoff = ntohs(ip->ip_off); + off = ipoff & IP_OFFMASK; + p = (u_short)ip->ip_p; + s = (u_32_t *)&ip->ip_src; + d = (u_32_t *)&ip->ip_dst; + plen = ntohs(ip->ip_len); + } else #ifdef USE_INET6 + if (f == AF_INET6) { off = 0; ipoff = 0; hl = sizeof(ip6_t); @@ -1140,32 +1243,22 @@ int blen; break; } } -#else - sprintf(t, "ipv6"); - goto printipflog; + } else #endif - } else if (v == 4) { - hl = IP_HL(ip) << 2; - ipoff = ip->ip_off; - off = ipoff & IP_OFFMASK; - p = (u_short)ip->ip_p; - s = (u_32_t *)&ip->ip_src; - d = (u_32_t *)&ip->ip_dst; - plen = ip->ip_len; - } else { + { goto printipflog; } - proto = getproto(p); + proto = getlocalproto(p); if ((p == IPPROTO_TCP || p == IPPROTO_UDP) && !off) { tp = (tcphdr_t *)((char *)ip + hl); if (!(ipf->fl_lflags & FI_SHORT)) { - (void) sprintf(t, "%s,%s -> ", hostname(res, v, s), - portname(res, proto, (u_int)tp->th_sport)); + sprintf(t, "%s,%s -> ", hostname(f, s), + portlocalname(res, proto, (u_int)tp->th_sport)); t += strlen(t); - (void) sprintf(t, "%s,%s PR %s len %hu %hu", - hostname(res, v, d), - portname(res, proto, (u_int)tp->th_dport), + sprintf(t, "%s,%s PR %s len %hu %hu", + hostname(f, d), + portlocalname(res, proto, (u_int)tp->th_dport), proto, hl, plen); t += strlen(t); @@ -1175,8 +1268,8 @@ int blen; for (i = 0; tcpfl[i].value; i++) if (tp->th_flags & tcpfl[i].value) *t++ = tcpfl[i].flag; - if (opts & OPT_VERBOSE) { - (void) sprintf(t, " %lu %lu %hu", + if (ipmonopts & IPMON_VERBOSE) { + sprintf(t, " %lu %lu %hu", (u_long)(ntohl(tp->th_seq)), (u_long)(ntohl(tp->th_ack)), ntohs(tp->th_win)); @@ -1185,24 +1278,26 @@ int blen; } *t = '\0'; } else { - (void) sprintf(t, "%s -> ", hostname(res, v, s)); + sprintf(t, "%s -> ", hostname(f, s)); t += strlen(t); - (void) sprintf(t, "%s PR %s len %hu %hu", - hostname(res, v, d), proto, hl, plen); + sprintf(t, "%s PR %s len %hu %hu", + hostname(f, d), proto, hl, plen); } - } else if ((p == IPPROTO_ICMPV6) && !off && (v == 6)) { +#if defined(AF_INET6) && defined(IPPROTO_ICMPV6) + } else if ((p == IPPROTO_ICMPV6) && !off && (f == AF_INET6)) { ic = (struct icmp *)((char *)ip + hl); - (void) sprintf(t, "%s -> ", hostname(res, v, s)); + sprintf(t, "%s -> ", hostname(f, s)); t += strlen(t); - (void) sprintf(t, "%s PR icmpv6 len %hu %hu icmpv6 %s", - hostname(res, v, d), hl, plen, + sprintf(t, "%s PR icmpv6 len %hu %hu icmpv6 %s", + hostname(f, d), hl, plen, icmpname6(ic->icmp_type, ic->icmp_code)); - } else if ((p == IPPROTO_ICMP) && !off && (v == 4)) { +#endif + } else if ((p == IPPROTO_ICMP) && !off && (f == AF_INET)) { ic = (struct icmp *)((char *)ip + hl); - (void) sprintf(t, "%s -> ", hostname(res, v, s)); + sprintf(t, "%s -> ", hostname(f, s)); t += strlen(t); - (void) sprintf(t, "%s PR icmp len %hu %hu icmp %s", - hostname(res, v, d), hl, plen, + sprintf(t, "%s PR icmp len %hu %hu icmp %s", + hostname(f, d), hl, plen, icmpname(ic->icmp_type, ic->icmp_code)); if (ic->icmp_type == ICMP_UNREACH || ic->icmp_type == ICMP_SOURCEQUENCH || @@ -1218,21 +1313,21 @@ int blen; if (i > 1500) i = ipc->ip_len; ipoff = ntohs(ipc->ip_off); - proto = getproto(ipc->ip_p); + proto = getlocalproto(ipc->ip_p); if (!(ipoff & IP_OFFMASK) && ((ipc->ip_p == IPPROTO_TCP) || (ipc->ip_p == IPPROTO_UDP))) { tp = (tcphdr_t *)((char *)ipc + hl); t += strlen(t); - (void) sprintf(t, " for %s,%s -", - HOSTNAME_V4(res, ipc->ip_src), - portname(res, proto, + sprintf(t, " for %s,%s -", + HOSTNAMEV4(ipc->ip_src), + portlocalname(res, proto, (u_int)tp->th_sport)); t += strlen(t); - (void) sprintf(t, " %s,%s PR %s len %hu %hu", - HOSTNAME_V4(res, ipc->ip_dst), - portname(res, proto, + sprintf(t, " %s,%s PR %s len %hu %hu", + HOSTNAMEV4(ipc->ip_dst), + portlocalname(res, proto, (u_int)tp->th_dport), proto, IP_HL(ipc) << 2, i); } else if (!(ipoff & IP_OFFMASK) && @@ -1240,26 +1335,25 @@ int blen; icmp = (icmphdr_t *)((char *)ipc + hl); t += strlen(t); - (void) sprintf(t, " for %s -", - HOSTNAME_V4(res, ipc->ip_src)); + sprintf(t, " for %s -", + HOSTNAMEV4(ipc->ip_src)); t += strlen(t); - (void) sprintf(t, + sprintf(t, " %s PR icmp len %hu %hu icmp %d/%d", - HOSTNAME_V4(res, ipc->ip_dst), + HOSTNAMEV4(ipc->ip_dst), IP_HL(ipc) << 2, i, icmp->icmp_type, icmp->icmp_code); } else { t += strlen(t); - (void) sprintf(t, " for %s -", - HOSTNAME_V4(res, ipc->ip_src)); + sprintf(t, " for %s -", + HOSTNAMEV4(ipc->ip_src)); t += strlen(t); - (void) sprintf(t, " %s PR %s len %hu (%hu)", - HOSTNAME_V4(res, ipc->ip_dst), proto, + sprintf(t, " %s PR %s len %hu (%hu)", + HOSTNAMEV4(ipc->ip_dst), proto, IP_HL(ipc) << 2, i); t += strlen(t); if (ipoff & IP_OFFMASK) { - (void) sprintf(t, - "(frag %d:%hu@%hu%s%s)", + sprintf(t, "(frag %d:%hu@%hu%s%s)", ntohs(ipc->ip_id), i - (IP_HL(ipc) << 2), (ipoff & IP_OFFMASK) << 3, @@ -1270,13 +1364,13 @@ int blen; } } else { - (void) sprintf(t, "%s -> ", hostname(res, v, s)); + sprintf(t, "%s -> ", hostname(f, s)); t += strlen(t); - (void) sprintf(t, "%s PR %s len %hu (%hu)", - hostname(res, v, d), proto, hl, plen); + sprintf(t, "%s PR %s len %hu (%hu)", + hostname(f, d), proto, hl, plen); t += strlen(t); if (off & IP_OFFMASK) - (void) sprintf(t, " (frag %d:%hu@%hu%s%s)", + sprintf(t, " (frag %d:%hu@%hu%s%s)", ntohs(ip->ip_id), plen - hl, (off & IP_OFFMASK) << 3, ipoff & IP_MF ? "+" : "", @@ -1347,32 +1441,43 @@ printipflog: strcpy(t, " mbcast"); t += 7; } + if (ipf->fl_breason != 0) { + strcpy(t, " reason:"); + t += 8; + strcpy(t, reasons[ipf->fl_breason]); + t += strlen(reasons[ipf->fl_breason]); + } *t++ = '\n'; *t++ = '\0'; defaction = 0; - if (conf_file != NULL) - defaction = check_action(buf, line, opts, lvl); - if (defaction == 0) { - if (opts & OPT_SYSLOG) - syslog(lvl, "%s", line); - else if (log != NULL) - (void) fprintf(log, "%s", line); + if (conf->cfile != NULL) + defaction = check_action(buf, line, ipmonopts, lvl); - if (opts & OPT_HEXHDR) - dumphex(log, opts, buf, + if (defaction == 0) { + if (ipmonopts & IPMON_SYSLOG) { + syslog(lvl, "%s", line); + } else if (conf->log != NULL) { + (void) fprintf(conf->log, "%s", line); + } + + if (ipmonopts & IPMON_HEXHDR) { + dumphex(conf->log, ipmonopts, buf, sizeof(iplog_t) + sizeof(*ipf)); - if (opts & OPT_HEXBODY) - dumphex(log, opts, (char *)ip, + } + if (ipmonopts & IPMON_HEXBODY) { + dumphex(conf->log, ipmonopts, (char *)ip, ipf->fl_plen + ipf->fl_hlen); - else if ((opts & OPT_LOGBODY) && (ipf->fl_flags & FR_LOGBODY)) - dumphex(log, opts, (char *)ip + ipf->fl_hlen, + } else if ((ipmonopts & IPMON_LOGBODY) && + (ipf->fl_flags & FR_LOGBODY)) { + dumphex(conf->log, ipmonopts, (char *)ip + ipf->fl_hlen, ipf->fl_plen); + } } } static void usage(prog) -char *prog; + char *prog; { fprintf(stderr, "%s: [-NFhstvxX] [-f ]\n", prog); exit(1); @@ -1380,7 +1485,7 @@ char *prog; static void write_pid(file) -char *file; + char *file; { FILE *fp = NULL; int fd; @@ -1400,8 +1505,8 @@ char *file; static void flushlogs(file, log) -char *file; -FILE *log; + char *file; + FILE *log; { int fd, flushed = 0; @@ -1416,11 +1521,11 @@ FILE *log; flushed); fflush(stdout); } else - perror("SIOCIPFFB"); + ipferror(fd, "SIOCIPFFB"); (void) close(fd); if (flushed) { - if (opts & OPT_SYSLOG) { + if (ipmonopts & IPMON_SYSLOG) { syslog(LOG_INFO, "%d bytes flushed from log\n", flushed); } else if ((log != stdout) && (log != NULL)) { @@ -1431,8 +1536,8 @@ FILE *log; static void logopts(turnon, options) -int turnon; -char *options; + int turnon; + char *options; { int flags = 0; char *s; @@ -1442,13 +1547,13 @@ char *options; switch (*s) { case 'N' : - flags |= OPT_NAT; + flags |= IPMON_NAT; break; case 'S' : - flags |= OPT_STATE; + flags |= IPMON_STATE; break; case 'I' : - flags |= OPT_FILTER; + flags |= IPMON_FILTER; break; default : fprintf(stderr, "Unknown log option %c\n", *s); @@ -1457,64 +1562,87 @@ char *options; } if (turnon) - opts |= flags; + ipmonopts |= flags; else - opts &= ~(flags); + ipmonopts &= ~(flags); +} + +static void initconfig(config_t *conf) +{ + int i; + + memset(conf, 0, sizeof(*conf)); + + conf->log = stdout; + conf->maxfd = -1; + + for (i = 0; i < 3; i++) { + conf->logsrc[i].fd = -1; + conf->logsrc[i].logtype = -1; + conf->logsrc[i].regular = -1; + } + + conf->logsrc[0].file = IPL_NAME; + conf->logsrc[1].file = IPNAT_NAME; + conf->logsrc[2].file = IPSTATE_NAME; + + add_doing(&executesaver); + add_doing(&snmpv1saver); + add_doing(&snmpv2saver); + add_doing(&syslogsaver); + add_doing(&filesaver); + add_doing(¬hingsaver); } int main(argc, argv) -int argc; -char *argv[]; + int argc; + char *argv[]; { - struct stat sb; - FILE *log = stdout; - FILE *fp; - int fd[3], doread, n, i; - int tr, nr, regular[3], c; - int fdt[3], devices = 0, make_daemon = 0; - char buf[DEFAULT_IPFLOGSIZE], *iplfile[3], *s; - extern int optind; - extern char *optarg; + int doread, c, make_daemon = 0; + char *prog; + config_t config; - fd[0] = fd[1] = fd[2] = -1; - fdt[0] = fdt[1] = fdt[2] = -1; - iplfile[0] = IPL_NAME; - iplfile[1] = IPNAT_NAME; - iplfile[2] = IPSTATE_NAME; + prog = strrchr(argv[0], '/'); + if (prog == NULL) + prog = argv[0]; + else + prog++; + + initconfig(&config); while ((c = getopt(argc, argv, "?abB:C:Df:FhL:nN:o:O:pP:sS:tvxX")) != -1) switch (c) { case 'a' : - opts |= OPT_LOGALL; - fdt[0] = IPL_LOGIPF; - fdt[1] = IPL_LOGNAT; - fdt[2] = IPL_LOGSTATE; + ipmonopts |= IPMON_LOGALL; + config.logsrc[0].logtype = IPL_LOGIPF; + config.logsrc[1].logtype = IPL_LOGNAT; + config.logsrc[2].logtype = IPL_LOGSTATE; break; case 'b' : - opts |= OPT_LOGBODY; + ipmonopts |= IPMON_LOGBODY; break; case 'B' : - binarylogfile = optarg; - binarylog = fopen(optarg, "a"); + config.bfile = optarg; + config.blog = fopen(optarg, "a"); break; case 'C' : - conf_file = optarg; + config.cfile = optarg; break; case 'D' : make_daemon = 1; break; case 'f' : case 'I' : - opts |= OPT_FILTER; - fdt[0] = IPL_LOGIPF; - iplfile[0] = optarg; + ipmonopts |= IPMON_FILTER; + config.logsrc[0].logtype = IPL_LOGIPF; + config.logsrc[0].file = optarg; break; case 'F' : - flushlogs(iplfile[0], log); - flushlogs(iplfile[1], log); - flushlogs(iplfile[2], log); + flushlogs(config.logsrc[0].file, config.log); + flushlogs(config.logsrc[1].file, config.log); + flushlogs(config.logsrc[2].file, config.log); break; case 'L' : logfac = fac_findname(optarg); @@ -1526,56 +1654,49 @@ char *argv[]; } break; case 'n' : - opts |= OPT_RESOLVE; + ipmonopts |= IPMON_RESOLVE; + opts &= ~OPT_NORESOLVE; break; case 'N' : - opts |= OPT_NAT; - fdt[1] = IPL_LOGNAT; - iplfile[1] = optarg; + ipmonopts |= IPMON_NAT; + config.logsrc[1].logtype = IPL_LOGNAT; + config.logsrc[1].file = optarg; break; case 'o' : case 'O' : logopts(c == 'o', optarg); - fdt[0] = fdt[1] = fdt[2] = -1; - if (opts & OPT_FILTER) - fdt[0] = IPL_LOGIPF; - if (opts & OPT_NAT) - fdt[1] = IPL_LOGNAT; - if (opts & OPT_STATE) - fdt[2] = IPL_LOGSTATE; + if (ipmonopts & IPMON_FILTER) + config.logsrc[0].logtype = IPL_LOGIPF; + if (ipmonopts & IPMON_NAT) + config.logsrc[1].logtype = IPL_LOGNAT; + if (ipmonopts & IPMON_STATE) + config.logsrc[2].logtype = IPL_LOGSTATE; break; case 'p' : - opts |= OPT_PORTNUM; + ipmonopts |= IPMON_PORTNUM; break; case 'P' : pidfile = optarg; break; case 's' : - s = strrchr(argv[0], '/'); - if (s == NULL) - s = argv[0]; - else - s++; - openlog(s, LOG_NDELAY|LOG_PID, logfac); - s = NULL; - opts |= OPT_SYSLOG; - log = NULL; + ipmonopts |= IPMON_SYSLOG; + config.log = NULL; break; case 'S' : - opts |= OPT_STATE; - fdt[2] = IPL_LOGSTATE; - iplfile[2] = optarg; + ipmonopts |= IPMON_STATE; + config.logsrc[2].logtype = IPL_LOGSTATE; + config.logsrc[2].file = optarg; break; case 't' : - opts |= OPT_TAIL; + ipmonopts |= IPMON_TAIL; break; case 'v' : - opts |= OPT_VERBOSE; + ipmonopts |= IPMON_VERBOSE; break; case 'x' : - opts |= OPT_HEXBODY; + ipmonopts |= IPMON_HEXBODY; break; case 'X' : - opts |= OPT_HEXHDR; + ipmonopts |= IPMON_HEXHDR; break; default : case 'h' : @@ -1583,69 +1704,62 @@ char *argv[]; usage(argv[0]); } + if (ipmonopts & IPMON_SYSLOG) + openlog(prog, LOG_NDELAY|LOG_PID, logfac); + init_tabs(); - if (conf_file) - if (load_config(conf_file) == -1) + if (config.cfile) + if (load_config(config.cfile) == -1) { + unload_config(); exit(1); + } /* * Default action is to only open the filter log file. */ - if ((fdt[0] == -1) && (fdt[1] == -1) && (fdt[2] == -1)) - fdt[0] = IPL_LOGIPF; + if ((config.logsrc[0].logtype == -1) && + (config.logsrc[0].logtype == -1) && + (config.logsrc[0].logtype == -1)) + config.logsrc[0].logtype = IPL_LOGIPF; - for (i = 0; i < 3; i++) { - if (fdt[i] == -1) - continue; - if (!strcmp(iplfile[i], "-")) - fd[i] = 0; - else { - if ((fd[i] = open(iplfile[i], O_RDONLY)) == -1) { - (void) fprintf(stderr, - "%s: open: %s\n", iplfile[i], - STRERROR(errno)); - exit(1); - /* NOTREACHED */ - } - if (fstat(fd[i], &sb) == -1) { - (void) fprintf(stderr, "%d: fstat: %s\n", - fd[i], STRERROR(errno)); - exit(1); - /* NOTREACHED */ - } - if (!(regular[i] = !S_ISCHR(sb.st_mode))) - devices++; - } - } + openlogs(&config); - if (!(opts & OPT_SYSLOG)) { - logfile = argv[optind]; - log = logfile ? fopen(logfile, "a") : stdout; - if (log == NULL) { + if (!(ipmonopts & IPMON_SYSLOG)) { + config.file = argv[optind]; + config.log = config.file ? fopen(config.file, "a") : stdout; + if (config.log == NULL) { (void) fprintf(stderr, "%s: fopen: %s\n", argv[optind], STRERROR(errno)); exit(1); /* NOTREACHED */ } - setvbuf(log, NULL, _IONBF, 0); - } else - log = NULL; + setvbuf(config.log, NULL, _IONBF, 0); + } else { + config.log = NULL; + } - if (make_daemon && ((log != stdout) || (opts & OPT_SYSLOG))) { + if (make_daemon && + ((config.log != stdout) || (ipmonopts & IPMON_SYSLOG))) { #if BSD >= 199306 - daemon(0, !(opts & OPT_SYSLOG)); + daemon(0, !(ipmonopts & IPMON_SYSLOG)); #else int pid; - if ((pid = fork()) > 0) - exit(0); - if (pid < 0) { + + switch (fork()) + { + case -1 : (void) fprintf(stderr, "%s: fork() failed: %s\n", argv[0], STRERROR(errno)); exit(1); /* NOTREACHED */ + case 0 : + break; + default : + exit(0); } + setsid(); - if ((opts & OPT_SYSLOG)) + if ((ipmonopts & IPMON_SYSLOG)) close(2); #endif /* !BSD */ close(0); @@ -1655,80 +1769,142 @@ char *argv[]; signal(SIGHUP, handlehup); - for (doread = 1; doread; ) { - nr = 0; + for (doread = 1; doread; ) + doread = read_loginfo(&config); - for (i = 0; i < 3; i++) { - tr = 0; - if (fdt[i] == -1) - continue; - if (!regular[i]) { - if (ioctl(fd[i], FIONREAD, &tr) == -1) { - if (opts & OPT_SYSLOG) - syslog(LOG_CRIT, - "ioctl(FIONREAD): %m"); - else - perror("ioctl(FIONREAD)"); - exit(1); - /* NOTREACHED */ - } - } else { - tr = (lseek(fd[i], 0, SEEK_CUR) < sb.st_size); - if (!tr && !(opts & OPT_TAIL)) - doread = 0; - } - if (!tr) - continue; - nr += tr; - n = 0; + unload_config(); - tr = read_log(fd[i], &n, buf, sizeof(buf)); - if (donehup) { - if (logfile && (fp = fopen(logfile, "a"))) { - fclose(log); - log = fp; - } - if (binarylogfile && - (fp = fopen(binarylogfile, "a"))) { - fclose(binarylog); - binarylog = fp; - } - init_tabs(); - if (conf_file != NULL) - load_config(conf_file); - donehup = 0; - } - - switch (tr) - { - case -1 : - if (opts & OPT_SYSLOG) - syslog(LOG_CRIT, "read: %m\n"); - else - perror("read"); - doread = 0; - break; - case 1 : - if (opts & OPT_SYSLOG) - syslog(LOG_CRIT, "aborting logging\n"); - else if (log != NULL) - fprintf(log, "aborting logging\n"); - doread = 0; - break; - case 2 : - break; - case 0 : - if (n > 0) { - print_log(fdt[i], log, buf, n); - if (!(opts & OPT_SYSLOG)) - fflush(log); - } - break; - } - } - if (!nr && ((opts & OPT_TAIL) || devices)) - sleep(1); - } return(0); /* NOTREACHED */ } + + +static void openlogs(config_t *conf) +{ + logsource_t *l; + struct stat sb; + int i; + + for (i = 0; i < 3; i++) { + l = &conf->logsrc[i]; + if (l->logtype == -1) + continue; + if (!strcmp(l->file, "-")) + l->fd = 0; + else { + if ((l->fd= open(l->file, O_RDONLY)) == -1) { + (void) fprintf(stderr, + "%s: open: %s\n", l->file, + STRERROR(errno)); + exit(1); + /* NOTREACHED */ + } + + if (fstat(l->fd, &sb) == -1) { + (void) fprintf(stderr, "%d: fstat: %s\n", + l->fd, STRERROR(errno)); + exit(1); + /* NOTREACHED */ + } + + l->regular = !S_ISCHR(sb.st_mode); + if (l->regular) + l->size = sb.st_size; + + FD_SET(l->fd, &conf->fdmr); + if (l->fd > conf->maxfd) + conf->maxfd = l->fd; + } + } +} + + +static int read_loginfo(config_t *conf) +{ + iplog_t buf[DEFAULT_IPFLOGSIZE/sizeof(iplog_t)+1]; + int n, tr, nr, i; + logsource_t *l; + fd_set fdr; + + fdr = conf->fdmr; + + n = select(conf->maxfd + 1, &fdr, NULL, NULL, NULL); + if (n == 0) + return 1; + if (n == -1) { + if (errno == EINTR) + return 1; + return -1; + } + + for (i = 0, nr = 0; i < 3; i++) { + l = &conf->logsrc[i]; + + if ((l->logtype == -1) || !FD_ISSET(l->fd, &fdr)) + continue; + + tr = 0; + if (l->regular) { + tr = (lseek(l->fd, 0, SEEK_CUR) < l->size); + if (!tr && !(ipmonopts & IPMON_TAIL)) + return 0; + } + + n = 0; + tr = read_log(l->fd, &n, (char *)buf, sizeof(buf)); + if (donehup) { + if (conf->file != NULL) { + if (conf->log != NULL) { + fclose(conf->log); + conf->log = NULL; + } + conf->log = fopen(conf->file, "a"); + } + + if (conf->bfile != NULL) { + if (conf->blog != NULL) { + fclose(conf->blog); + conf->blog = NULL; + } + conf->blog = fopen(conf->bfile, "a"); + } + + init_tabs(); + if (conf->cfile != NULL) + load_config(conf->cfile); + donehup = 0; + } + + switch (tr) + { + case -1 : + if (ipmonopts & IPMON_SYSLOG) + syslog(LOG_CRIT, "read: %m\n"); + else { + ipferror(l->fd, "read"); + } + return 0; + case 1 : + if (ipmonopts & IPMON_SYSLOG) + syslog(LOG_CRIT, "aborting logging\n"); + else if (conf->log != NULL) + fprintf(conf->log, "aborting logging\n"); + return 0; + case 2 : + break; + case 0 : + nr += tr; + if (n > 0) { + print_log(conf, l, (char *)buf, n); + if (!(ipmonopts & IPMON_SYSLOG)) + fflush(conf->log); + } + break; + } + } + + if (!nr && (ipmonopts & IPMON_TAIL)) + sleep(1); + + return 1; +} diff --git a/contrib/ipfilter/tools/ipmon_y.y b/contrib/ipfilter/tools/ipmon_y.y index 98042d88022..f14180d6a9e 100644 --- a/contrib/ipfilter/tools/ipmon_y.y +++ b/contrib/ipfilter/tools/ipmon_y.y @@ -1,7 +1,7 @@ /* $FreeBSD$ */ /* - * Copyright (C) 2001-2004 by Darren Reed. + * Copyright (C) 2012 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. */ @@ -13,6 +13,8 @@ #include "ipmon_l.h" #include "ipmon.h" +#include + #define YYDEBUG 1 extern void yyerror __P((char *)); @@ -21,69 +23,88 @@ extern int yylex __P((void)); extern int yydebug; extern FILE *yyin; extern int yylineNum; +extern int ipmonopts; -typedef struct opt { - struct opt *o_next; +typedef struct opt_s { + struct opt_s *o_next; int o_line; int o_type; int o_num; char *o_str; struct in_addr o_ip; + int o_logfac; + int o_logpri; } opt_t; -static void build_action __P((struct opt *)); +static void build_action __P((opt_t *, ipmon_doing_t *)); static opt_t *new_opt __P((int)); static void free_action __P((ipmon_action_t *)); +static void print_action __P((ipmon_action_t *)); +static int find_doing __P((char *)); +static ipmon_doing_t *build_doing __P((char *, char *)); +static void print_match __P((ipmon_action_t *)); +static int install_saver __P((char *, char *)); static ipmon_action_t *alist = NULL; + +ipmon_saver_int_t *saverlist = NULL; %} %union { char *str; u_32_t num; struct in_addr addr; - struct opt *opt; + struct opt_s *opt; union i6addr ip6; + struct ipmon_doing_s *ipmd; } %token YY_NUMBER YY_HEX %token YY_STR %token YY_IPV6 -%token YY_COMMENT +%token YY_COMMENT %token YY_CMP_EQ YY_CMP_NE YY_CMP_LE YY_CMP_GE YY_CMP_LT YY_CMP_GT %token YY_RANGE_OUT YY_RANGE_IN %token IPM_MATCH IPM_BODY IPM_COMMENT IPM_DIRECTION IPM_DSTIP IPM_DSTPORT -%token IPM_EVERY IPM_EXECUTE IPM_GROUP IPM_INTERFACE IPM_IN IPM_NO IPM_OUT +%token IPM_EVERY IPM_GROUP IPM_INTERFACE IPM_IN IPM_NO IPM_OUT IPM_LOADACTION %token IPM_PACKET IPM_PACKETS IPM_POOL IPM_PROTOCOL IPM_RESULT IPM_RULE %token IPM_SECOND IPM_SECONDS IPM_SRCIP IPM_SRCPORT IPM_LOGTAG IPM_WITH -%token IPM_DO IPM_SAVE IPM_SYSLOG IPM_NOTHING IPM_RAW IPM_TYPE IPM_NAT +%token IPM_DO IPM_DOING IPM_TYPE IPM_NAT %token IPM_STATE IPM_NATTAG IPM_IPF %type ipv4 -%type direction dstip dstport every execute group interface +%type direction dstip dstport every group interface %type protocol result rule srcip srcport logtag matching -%type matchopt nattag type doopt doing save syslog nothing -%type saveopts saveopt typeopt +%type matchopt nattag type +%type typeopt +%type doopt doing %% -file: line - | assign - | file line - | file assign +file: action + | file action ; -line: IPM_MATCH '{' matching '}' IPM_DO '{' doing '}' ';' - { build_action($3); resetlexer(); } +action: line ';' + | assign ';' | IPM_COMMENT | YY_COMMENT ; -assign: YY_STR assigning YY_STR ';' { set_variable($1, $3); +line: IPM_MATCH '{' matching ';' '}' IPM_DO '{' doing ';' '}' + { build_action($3, $8); + resetlexer(); + } + | IPM_LOADACTION YY_STR YY_STR { if (install_saver($2, $3)) + yyerror("install saver"); + } + ; + +assign: YY_STR assigning YY_STR { set_variable($1, $3); resetlexer(); free($1); free($3); yyvarnext = 0; - } + } ; assigning: @@ -114,14 +135,20 @@ matchopt: doing: doopt { $$ = $1; } - | doopt ',' doing { $1->o_next = $3; $$ = $1; } + | doopt ',' doing { $1->ipmd_next = $3; $$ = $1; } ; doopt: - execute { $$ = $1; } - | save { $$ = $1; } - | syslog { $$ = $1; } - | nothing { $$ = $1; } + YY_STR { if (find_doing($1) != IPM_DOING) + yyerror("unknown action"); + } + '(' YY_STR ')' { $$ = build_doing($1, $4); + if ($$ == NULL) + yyerror("action building"); + } + | YY_STR { if (find_doing($1) == IPM_DOING) + $$ = build_doing($1, NULL); + } ; direction: @@ -211,31 +238,7 @@ typeopt: | IPM_STATE { $$ = IPL_MAGIC_STATE; } ; -execute: - IPM_EXECUTE YY_STR { $$ = new_opt(IPM_EXECUTE); - $$->o_str = $2; } - ; -save: IPM_SAVE saveopts YY_STR { $$ = new_opt(IPM_SAVE); - $$->o_num = $2; - $$->o_str = $3; } - ; - -saveopts: { $$ = 0; } - | saveopt { $$ = $1; } - | saveopt ',' saveopts { $$ = $1 | $3; } - ; - -saveopt: - IPM_RAW { $$ = IPMDO_SAVERAW; } - ; - -syslog: IPM_SYSLOG { $$ = new_opt(IPM_SYSLOG); } - ; - -nothing: - IPM_NOTHING { $$ = 0; } - ; ipv4: YY_NUMBER '.' YY_NUMBER '.' YY_NUMBER '.' YY_NUMBER { if ($1 > 255 || $3 > 255 || $5 > 255 || $7 > 255) { @@ -253,30 +256,27 @@ static struct wordtab yywords[] = { { "dstip", IPM_DSTIP }, { "dstport", IPM_DSTPORT }, { "every", IPM_EVERY }, - { "execute", IPM_EXECUTE }, { "group", IPM_GROUP }, { "in", IPM_IN }, { "interface", IPM_INTERFACE }, { "ipf", IPM_IPF }, + { "load_action",IPM_LOADACTION }, { "logtag", IPM_LOGTAG }, { "match", IPM_MATCH }, { "nat", IPM_NAT }, { "nattag", IPM_NATTAG }, { "no", IPM_NO }, - { "nothing", IPM_NOTHING }, { "out", IPM_OUT }, { "packet", IPM_PACKET }, { "packets", IPM_PACKETS }, { "protocol", IPM_PROTOCOL }, { "result", IPM_RESULT }, { "rule", IPM_RULE }, - { "save", IPM_SAVE }, { "second", IPM_SECOND }, { "seconds", IPM_SECONDS }, { "srcip", IPM_SRCIP }, { "srcport", IPM_SRCPORT }, { "state", IPM_STATE }, - { "syslog", IPM_SYSLOG }, { "with", IPM_WITH }, { NULL, 0 } }; @@ -301,31 +301,33 @@ static int macflags[17][2] = { { 0, 0 } }; -static opt_t *new_opt(type) -int type; +static opt_t * +new_opt(type) + int type; { opt_t *o; - o = (opt_t *)malloc(sizeof(*o)); + o = (opt_t *)calloc(1, sizeof(*o)); o->o_type = type; o->o_line = yylineNum; - o->o_num = 0; - o->o_str = (char *)0; - o->o_next = NULL; + o->o_logfac = -1; + o->o_logpri = -1; return o; } -static void build_action(olist) -opt_t *olist; +static void +build_action(olist, todo) + opt_t *olist; + ipmon_doing_t *todo; { ipmon_action_t *a; opt_t *o; - char c; int i; a = (ipmon_action_t *)calloc(1, sizeof(*a)); if (a == NULL) return; + while ((o = olist) != NULL) { /* * Check to see if the same comparator is being used more than @@ -358,24 +360,11 @@ opt_t *olist; case IPM_DSTPORT : a->ac_dport = htons(o->o_num); break; - case IPM_EXECUTE : - a->ac_exec = o->o_str; - c = *o->o_str; - if (c== '"'|| c == '\'') { - if (o->o_str[strlen(o->o_str) - 1] == c) { - a->ac_run = strdup(o->o_str + 1); - a->ac_run[strlen(a->ac_run) - 1] ='\0'; - } else - a->ac_run = o->o_str; - } else - a->ac_run = o->o_str; - o->o_str = NULL; - break; case IPM_INTERFACE : a->ac_iface = o->o_str; o->o_str = NULL; break; - case IPM_GROUP : + case IPM_GROUP : if (o->o_str != NULL) strncpy(a->ac_group, o->o_str, FR_GROUPLEN); else @@ -416,24 +405,6 @@ opt_t *olist; case IPM_SRCPORT : a->ac_sport = htons(o->o_num); break; - case IPM_SAVE : - if (a->ac_savefile != NULL) { - fprintf(stderr, "%s redfined on line %d\n", - yykeytostr(o->o_type), yylineNum); - break; - } - a->ac_savefile = strdup(o->o_str); - a->ac_savefp = fopen(o->o_str, "a"); - a->ac_dflag |= o->o_num & IPMDO_SAVERAW; - break; - case IPM_SYSLOG : - if (a->ac_syslog != 0) { - fprintf(stderr, "%s redfined on line %d\n", - yykeytostr(o->o_type), yylineNum); - break; - } - a->ac_syslog = 1; - break; case IPM_TYPE : a->ac_type = o->o_num; break; @@ -448,17 +419,25 @@ opt_t *olist; free(o->o_str); free(o); } + + a->ac_doing = todo; a->ac_next = alist; alist = a; + + if (ipmonopts & IPMON_VERBOSE) + print_action(a); } -int check_action(buf, log, opts, lvl) -char *buf, *log; -int opts, lvl; +int +check_action(buf, log, opts, lvl) + char *buf, *log; + int opts, lvl; { ipmon_action_t *a; struct timeval tv; + ipmon_doing_t *d; + ipmon_msg_t msg; ipflog_t *ipf; tcphdr_t *tcp; iplog_t *ipl; @@ -472,19 +451,33 @@ int opts, lvl; ip = (ip_t *)(ipf + 1); tcp = (tcphdr_t *)((char *)ip + (IP_HL(ip) << 2)); + msg.imm_data = ipl; + msg.imm_dsize = ipl->ipl_dsize; + msg.imm_when = ipl->ipl_time.tv_sec; + msg.imm_msg = log; + msg.imm_msglen = strlen(log); + msg.imm_loglevel = lvl; + for (a = alist; a != NULL; a = a->ac_next) { + verbose(0, "== checking config rule\n"); if ((a->ac_mflag & IPMAC_DIRECTION) != 0) { if (a->ac_direction == IPM_IN) { - if ((ipf->fl_flags & FR_INQUE) == 0) + if ((ipf->fl_flags & FR_INQUE) == 0) { + verbose(8, "-- direction not in\n"); continue; + } } else if (a->ac_direction == IPM_OUT) { - if ((ipf->fl_flags & FR_OUTQUE) == 0) + if ((ipf->fl_flags & FR_OUTQUE) == 0) { + verbose(8, "-- direction not out\n"); continue; + } } } - if ((a->ac_type != 0) && (a->ac_type != ipl->ipl_magic)) + if ((a->ac_type != 0) && (a->ac_type != ipl->ipl_magic)) { + verbose(8, "-- type mismatch\n"); continue; + } if ((a->ac_mflag & IPMAC_EVERY) != 0) { gettimeofday(&tv, NULL); @@ -492,8 +485,10 @@ int opts, lvl; if (tv.tv_usec <= a->ac_lastusec) t1--; if (a->ac_second != 0) { - if (t1 < a->ac_second) + if (t1 < a->ac_second) { + verbose(8, "-- too soon\n"); continue; + } a->ac_lastsec = tv.tv_sec; a->ac_lastusec = tv.tv_usec; } @@ -503,159 +498,149 @@ int opts, lvl; a->ac_pktcnt++; else if (a->ac_pktcnt == a->ac_packet) { a->ac_pktcnt = 0; + verbose(8, "-- packet count\n"); continue; } else { a->ac_pktcnt++; + verbose(8, "-- packet count\n"); continue; } } } if ((a->ac_mflag & IPMAC_DSTIP) != 0) { - if ((ip->ip_dst.s_addr & a->ac_dmsk) != a->ac_dip) + if ((ip->ip_dst.s_addr & a->ac_dmsk) != a->ac_dip) { + verbose(8, "-- dstip wrong\n"); continue; + } } if ((a->ac_mflag & IPMAC_DSTPORT) != 0) { - if (ip->ip_p != IPPROTO_UDP && ip->ip_p != IPPROTO_TCP) + if (ip->ip_p != IPPROTO_UDP && + ip->ip_p != IPPROTO_TCP) { + verbose(8, "-- not port protocol\n"); continue; - if (tcp->th_dport != a->ac_dport) + } + if (tcp->th_dport != a->ac_dport) { + verbose(8, "-- dport mismatch\n"); continue; + } } if ((a->ac_mflag & IPMAC_GROUP) != 0) { if (strncmp(a->ac_group, ipf->fl_group, - FR_GROUPLEN) != 0) + FR_GROUPLEN) != 0) { + verbose(8, "-- group mismatch\n"); continue; + } } if ((a->ac_mflag & IPMAC_INTERFACE) != 0) { - if (strcmp(a->ac_iface, ipf->fl_ifname)) + if (strcmp(a->ac_iface, ipf->fl_ifname)) { + verbose(8, "-- ifname mismatch\n"); continue; + } } if ((a->ac_mflag & IPMAC_PROTOCOL) != 0) { - if (a->ac_proto != ip->ip_p) + if (a->ac_proto != ip->ip_p) { + verbose(8, "-- protocol mismatch\n"); continue; + } } if ((a->ac_mflag & IPMAC_RESULT) != 0) { if ((ipf->fl_flags & FF_LOGNOMATCH) != 0) { - if (a->ac_result != IPMR_NOMATCH) + if (a->ac_result != IPMR_NOMATCH) { + verbose(8, "-- ff-flags mismatch\n"); continue; + } } else if (FR_ISPASS(ipf->fl_flags)) { - if (a->ac_result != IPMR_PASS) + if (a->ac_result != IPMR_PASS) { + verbose(8, "-- pass mismatch\n"); continue; + } } else if (FR_ISBLOCK(ipf->fl_flags)) { - if (a->ac_result != IPMR_BLOCK) + if (a->ac_result != IPMR_BLOCK) { + verbose(8, "-- block mismatch\n"); continue; + } } else { /* Log only */ - if (a->ac_result != IPMR_LOG) + if (a->ac_result != IPMR_LOG) { + verbose(8, "-- log mismatch\n"); continue; + } } } if ((a->ac_mflag & IPMAC_RULE) != 0) { - if (a->ac_rule != ipf->fl_rule) + if (a->ac_rule != ipf->fl_rule) { + verbose(8, "-- rule mismatch\n"); continue; + } } if ((a->ac_mflag & IPMAC_SRCIP) != 0) { - if ((ip->ip_src.s_addr & a->ac_smsk) != a->ac_sip) + if ((ip->ip_src.s_addr & a->ac_smsk) != a->ac_sip) { + verbose(8, "-- srcip mismatch\n"); continue; + } } if ((a->ac_mflag & IPMAC_SRCPORT) != 0) { - if (ip->ip_p != IPPROTO_UDP && ip->ip_p != IPPROTO_TCP) + if (ip->ip_p != IPPROTO_UDP && + ip->ip_p != IPPROTO_TCP) { + verbose(8, "-- port protocol mismatch\n"); continue; - if (tcp->th_sport != a->ac_sport) + } + if (tcp->th_sport != a->ac_sport) { + verbose(8, "-- sport mismatch\n"); continue; + } } if ((a->ac_mflag & IPMAC_LOGTAG) != 0) { - if (a->ac_logtag != ipf->fl_logtag) + if (a->ac_logtag != ipf->fl_logtag) { + verbose(8, "-- logtag %d != %d\n", + a->ac_logtag, ipf->fl_logtag); continue; + } } if ((a->ac_mflag & IPMAC_NATTAG) != 0) { if (strncmp(a->ac_nattag, ipf->fl_nattag.ipt_tag, - IPFTAG_LEN) != 0) + IPFTAG_LEN) != 0) { + verbose(8, "-- nattag mismatch\n"); continue; + } } matched = 1; + verbose(8, "++ matched\n"); /* - * It matched so now execute the command + * It matched so now perform the saves */ - if (a->ac_syslog != 0) { - syslog(lvl, "%s", log); - } - - if (a->ac_savefp != NULL) { - if (a->ac_dflag & IPMDO_SAVERAW) - fwrite(ipl, 1, ipl->ipl_dsize, a->ac_savefp); - else - fputs(log, a->ac_savefp); - } - - if (a->ac_exec != NULL) { - switch (fork()) - { - case 0 : - { - FILE *pi; - - pi = popen(a->ac_run, "w"); - if (pi != NULL) { - fprintf(pi, "%s\n", log); - if ((opts & OPT_HEXHDR) != 0) { - dumphex(pi, 0, buf, - sizeof(*ipl) + - sizeof(*ipf)); - } - if ((opts & OPT_HEXBODY) != 0) { - dumphex(pi, 0, (char *)ip, - ipf->fl_hlen + - ipf->fl_plen); - } - pclose(pi); - } - exit(1); - } - case -1 : - break; - default : - break; - } - } + for (d = a->ac_doing; d != NULL; d = d->ipmd_next) + (*d->ipmd_store)(d->ipmd_token, &msg); } return matched; } -static void free_action(a) -ipmon_action_t *a; +static void +free_action(a) + ipmon_action_t *a; { - if (a->ac_savefile != NULL) { - free(a->ac_savefile); - a->ac_savefile = NULL; - } - if (a->ac_savefp != NULL) { - fclose(a->ac_savefp); - a->ac_savefp = NULL; - } - if (a->ac_exec != NULL) { - free(a->ac_exec); - if (a->ac_run == a->ac_exec) - a->ac_run = NULL; - a->ac_exec = NULL; - } - if (a->ac_run != NULL) { - free(a->ac_run); - a->ac_run = NULL; + ipmon_doing_t *d; + + while ((d = a->ac_doing) != NULL) { + a->ac_doing = d->ipmd_next; + (*d->ipmd_saver->ims_destroy)(d->ipmd_token); + free(d); } + if (a->ac_iface != NULL) { free(a->ac_iface); a->ac_iface = NULL; @@ -665,24 +650,21 @@ ipmon_action_t *a; } -int load_config(file) -char *file; +int +load_config(file) + char *file; { - ipmon_action_t *a; FILE *fp; char *s; + unload_config(); + s = getenv("YYDEBUG"); if (s != NULL) yydebug = atoi(s); else yydebug = 0; - while ((a = alist) != NULL) { - alist = a->ac_next; - free_action(a); - } - yylineNum = 1; (void) yysettab(yywords); @@ -698,3 +680,373 @@ char *file; fclose(fp); return 0; } + + +void +unload_config() +{ + ipmon_saver_int_t *sav, **imsip; + ipmon_saver_t *is; + ipmon_action_t *a; + + while ((a = alist) != NULL) { + alist = a->ac_next; + free_action(a); + } + + /* + * Look for savers that have been added in dynamically from the + * configuration file. + */ + for (imsip = &saverlist; (sav = *imsip) != NULL; ) { + if (sav->imsi_handle == NULL) + imsip = &sav->imsi_next; + else { + dlclose(sav->imsi_handle); + + *imsip = sav->imsi_next; + is = sav->imsi_stor; + free(sav); + + free(is->ims_name); + free(is); + } + } +} + + +void +dump_config() +{ + ipmon_action_t *a; + + for (a = alist; a != NULL; a = a->ac_next) { + print_action(a); + + printf("#\n"); + } +} + + +static void +print_action(a) + ipmon_action_t *a; +{ + ipmon_doing_t *d; + + printf("match { "); + print_match(a); + printf("; }\n"); + printf("do {"); + for (d = a->ac_doing; d != NULL; d = d->ipmd_next) { + printf("%s", d->ipmd_saver->ims_name); + if (d->ipmd_saver->ims_print != NULL) { + printf("(\""); + (*d->ipmd_saver->ims_print)(d->ipmd_token); + printf("\")"); + } + printf(";"); + } + printf("};\n"); +} + + +void * +add_doing(saver) + ipmon_saver_t *saver; +{ + ipmon_saver_int_t *it; + + if (find_doing(saver->ims_name) == IPM_DOING) + return NULL; + + it = calloc(1, sizeof(*it)); + if (it == NULL) + return NULL; + it->imsi_stor = saver; + it->imsi_next = saverlist; + saverlist = it; + return it; +} + + +static int +find_doing(string) + char *string; +{ + ipmon_saver_int_t *it; + + for (it = saverlist; it != NULL; it = it->imsi_next) { + if (!strcmp(it->imsi_stor->ims_name, string)) + return IPM_DOING; + } + return 0; +} + + +static ipmon_doing_t * +build_doing(target, options) + char *target; + char *options; +{ + ipmon_saver_int_t *it; + char *strarray[2]; + ipmon_doing_t *d, *d1; + ipmon_action_t *a; + ipmon_saver_t *save; + + d = calloc(1, sizeof(*d)); + if (d == NULL) + return NULL; + + for (it = saverlist; it != NULL; it = it->imsi_next) { + if (!strcmp(it->imsi_stor->ims_name, target)) + break; + } + if (it == NULL) { + free(d); + return NULL; + } + + strarray[0] = options; + strarray[1] = NULL; + + d->ipmd_token = (*it->imsi_stor->ims_parse)(strarray); + if (d->ipmd_token == NULL) { + free(d); + return NULL; + } + + save = it->imsi_stor; + d->ipmd_saver = save; + d->ipmd_store = it->imsi_stor->ims_store; + + /* + * Look for duplicate do-things that need to be dup'd + */ + for (a = alist; a != NULL; a = a->ac_next) { + for (d1 = a->ac_doing; d1 != NULL; d1 = d1->ipmd_next) { + if (save != d1->ipmd_saver) + continue; + if (save->ims_match == NULL || save->ims_dup == NULL) + continue; + if ((*save->ims_match)(d->ipmd_token, d1->ipmd_token)) + continue; + + (*d->ipmd_saver->ims_destroy)(d->ipmd_token); + d->ipmd_token = (*save->ims_dup)(d1->ipmd_token); + break; + } + } + + return d; +} + + +static void +print_match(a) + ipmon_action_t *a; +{ + char *coma = ""; + + if ((a->ac_mflag & IPMAC_DIRECTION) != 0) { + printf("direction = "); + if (a->ac_direction == IPM_IN) + printf("in"); + else if (a->ac_direction == IPM_OUT) + printf("out"); + coma = ", "; + } + + if ((a->ac_mflag & IPMAC_DSTIP) != 0) { + printf("%sdstip = ", coma); + printhostmask(AF_INET, &a->ac_dip, &a->ac_dmsk); + coma = ", "; + } + + if ((a->ac_mflag & IPMAC_DSTPORT) != 0) { + printf("%sdstport = %hu", coma, ntohs(a->ac_dport)); + coma = ", "; + } + + if ((a->ac_mflag & IPMAC_GROUP) != 0) { + char group[FR_GROUPLEN+1]; + + strncpy(group, a->ac_group, FR_GROUPLEN); + group[FR_GROUPLEN] = '\0'; + printf("%sgroup = %s", coma, group); + coma = ", "; + } + + if ((a->ac_mflag & IPMAC_INTERFACE) != 0) { + printf("%siface = %s", coma, a->ac_iface); + coma = ", "; + } + + if ((a->ac_mflag & IPMAC_LOGTAG) != 0) { + printf("%slogtag = %u", coma, a->ac_logtag); + coma = ", "; + } + + if ((a->ac_mflag & IPMAC_NATTAG) != 0) { + char tag[17]; + + strncpy(tag, a->ac_nattag, 16); + tag[16] = '\0'; + printf("%snattag = %s", coma, tag); + coma = ", "; + } + + if ((a->ac_mflag & IPMAC_PROTOCOL) != 0) { + printf("%sprotocol = %u", coma, a->ac_proto); + coma = ", "; + } + + if ((a->ac_mflag & IPMAC_RESULT) != 0) { + printf("%sresult = ", coma); + switch (a->ac_result) + { + case IPMR_LOG : + printf("log"); + break; + case IPMR_PASS : + printf("pass"); + break; + case IPMR_BLOCK : + printf("block"); + break; + case IPMR_NOMATCH : + printf("nomatch"); + break; + } + coma = ", "; + } + + if ((a->ac_mflag & IPMAC_RULE) != 0) { + printf("%srule = %u", coma, a->ac_rule); + coma = ", "; + } + + if ((a->ac_mflag & IPMAC_EVERY) != 0) { + if (a->ac_packet > 1) { + printf("%severy %d packets", coma, a->ac_packet); + coma = ", "; + } else if (a->ac_packet == 1) { + printf("%severy packet", coma); + coma = ", "; + } + if (a->ac_second > 1) { + printf("%severy %d seconds", coma, a->ac_second); + coma = ", "; + } else if (a->ac_second == 1) { + printf("%severy second", coma); + coma = ", "; + } + } + + if ((a->ac_mflag & IPMAC_SRCIP) != 0) { + printf("%ssrcip = ", coma); + printhostmask(AF_INET, &a->ac_sip, &a->ac_smsk); + coma = ", "; + } + + if ((a->ac_mflag & IPMAC_SRCPORT) != 0) { + printf("%ssrcport = %hu", coma, ntohs(a->ac_sport)); + coma = ", "; + } + + if ((a->ac_mflag & IPMAC_TYPE) != 0) { + printf("%stype = ", coma); + switch (a->ac_type) + { + case IPL_LOGIPF : + printf("ipf"); + break; + case IPL_LOGSTATE : + printf("state"); + break; + case IPL_LOGNAT : + printf("nat"); + break; + } + coma = ", "; + } + + if ((a->ac_mflag & IPMAC_WITH) != 0) { + printf("%swith ", coma); + coma = ", "; + } +} + + +static int +install_saver(name, path) + char *name, *path; +{ + ipmon_saver_int_t *isi; + ipmon_saver_t *is; + char nbuf[80]; + + if (find_doing(name) == IPM_DOING) + return -1; + + isi = calloc(1, sizeof(*isi)); + if (isi == NULL) + return -1; + + is = calloc(1, sizeof(*is)); + if (is == NULL) + goto loaderror; + + is->ims_name = name; + +#ifdef RTLD_LAZY + isi->imsi_handle = dlopen(path, RTLD_LAZY); +#endif +#ifdef DL_LAZY + isi->imsi_handle = dlopen(path, DL_LAZY); +#endif + + if (isi->imsi_handle == NULL) + goto loaderror; + + snprintf(nbuf, sizeof(nbuf), "%sdup", name); + is->ims_dup = (ims_dup_func_t)dlsym(isi->imsi_handle, nbuf); + + snprintf(nbuf, sizeof(nbuf), "%sdestroy", name); + is->ims_destroy = (ims_destroy_func_t)dlsym(isi->imsi_handle, nbuf); + if (is->ims_destroy == NULL) + goto loaderror; + + snprintf(nbuf, sizeof(nbuf), "%smatch", name); + is->ims_match = (ims_match_func_t)dlsym(isi->imsi_handle, nbuf); + + snprintf(nbuf, sizeof(nbuf), "%sparse", name); + is->ims_parse = (ims_parse_func_t)dlsym(isi->imsi_handle, nbuf); + if (is->ims_parse == NULL) + goto loaderror; + + snprintf(nbuf, sizeof(nbuf), "%sprint", name); + is->ims_print = (ims_print_func_t)dlsym(isi->imsi_handle, nbuf); + if (is->ims_print == NULL) + goto loaderror; + + snprintf(nbuf, sizeof(nbuf), "%sstore", name); + is->ims_store = (ims_store_func_t)dlsym(isi->imsi_handle, nbuf); + if (is->ims_store == NULL) + goto loaderror; + + isi->imsi_stor = is; + isi->imsi_next = saverlist; + saverlist = isi; + + return 0; + +loaderror: + if (isi->imsi_handle != NULL) + dlclose(isi->imsi_handle); + free(isi); + if (is != NULL) + free(is); + return -1; +} diff --git a/contrib/ipfilter/tools/ipnat.c b/contrib/ipfilter/tools/ipnat.c index 28e29ec7419..448c1c0d2d2 100644 --- a/contrib/ipfilter/tools/ipnat.c +++ b/contrib/ipfilter/tools/ipnat.c @@ -1,7 +1,7 @@ /* $FreeBSD$ */ /* - * Copyright (C) 2001-2006 by Darren Reed. + * Copyright (C) 2012 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. * @@ -67,7 +67,7 @@ extern char *sys_errlist[]; #if !defined(lint) static const char sccsid[] ="@(#)ipnat.c 1.9 6/5/96 (C) 1993 Darren Reed"; -static const char rcsid[] = "@(#)$Id: ipnat.c,v 1.24.2.11 2007/09/25 08:27:34 darrenr Exp $"; +static const char rcsid[] = "@(#)$Id$"; #endif @@ -79,23 +79,25 @@ char thishost[MAXHOSTNAMELEN]; extern char *optarg; -void dostats __P((int, natstat_t *, int, int)); -void dotable __P((natstat_t *, int, int)); -void flushtable __P((int, int)); +void dostats __P((int, natstat_t *, int, int, int *)); +void dotable __P((natstat_t *, int, int, int, char *)); +void flushtable __P((int, int, int *)); void usage __P((char *)); int main __P((int, char*[])); void showhostmap __P((natstat_t *nsp)); void natstat_dead __P((natstat_t *, char *)); -void dostats_live __P((int, natstat_t *, int)); +void dostats_live __P((int, natstat_t *, int, int *)); void showhostmap_dead __P((natstat_t *)); void showhostmap_live __P((int, natstat_t *)); -void dostats_dead __P((natstat_t *, int)); -void showtqtable_live __P((int)); +void dostats_dead __P((natstat_t *, int, int *)); +int nat_matcharray __P((nat_t *, int *)); -int opts; +int opts; +int nohdrfields = 0; +wordtab_t *nat_fields = NULL; void usage(name) -char *name; + char *name; { fprintf(stderr, "Usage: %s [-CFhlnrRsv] [-f filename]\n", name); exit(1); @@ -103,12 +105,12 @@ char *name; int main(argc, argv) -int argc; -char *argv[]; + int argc; + char *argv[]; { + int fd, c, mode, *natfilter; char *file, *core, *kernel; natstat_t ns, *nsp; - int fd, c, mode; ipfobj_t obj; fd = -1; @@ -118,8 +120,11 @@ char *argv[]; core = NULL; kernel = NULL; mode = O_RDWR; + natfilter = NULL; - while ((c = getopt(argc, argv, "CdFf:hlM:N:nrRsv")) != -1) + assigndefined(getenv("IPNAT_PREDEFINED")); + + while ((c = getopt(argc, argv, "CdFf:hlm:M:N:nO:prRsv")) != -1) switch (c) { case 'C' : @@ -141,6 +146,9 @@ char *argv[]; opts |= OPT_LIST; mode = O_RDONLY; break; + case 'm' : + natfilter = parseipfexpr(optarg, NULL); + break; case 'M' : core = optarg; break; @@ -148,9 +156,15 @@ char *argv[]; kernel = optarg; break; case 'n' : - opts |= OPT_DONOTHING; + opts |= OPT_DONOTHING|OPT_DONTOPEN; mode = O_RDONLY; break; + case 'O' : + nat_fields = parsefields(natfields, optarg); + break; + case 'p' : + opts |= OPT_PURGE; + break; case 'R' : opts |= OPT_NORESOLVE; break; @@ -168,6 +182,12 @@ char *argv[]; usage(argv[0]); } + if (((opts & OPT_PURGE) != 0) && ((opts & OPT_REMOVE) == 0)) { + (void) fprintf(stderr, "%s: -p must be used with -r\n", + argv[0]); + exit(1); + } + initparse(); if ((kernel != NULL) || (core != NULL)) { @@ -200,7 +220,7 @@ char *argv[]; obj.ipfo_size = sizeof(*nsp); obj.ipfo_ptr = (void *)nsp; if (ioctl(fd, SIOCGNATS, &obj) == -1) { - perror("ioctl(SIOCGNATS)"); + ipferror(fd, "ioctl(SIOCGNATS)"); exit(1); } (void) setgid(getgid()); @@ -211,17 +231,17 @@ char *argv[]; natstat_dead(nsp, kernel); if (opts & (OPT_LIST|OPT_STAT)) - dostats(fd, nsp, opts, 0); + dostats(fd, nsp, opts, 0, natfilter); exit(0); } if (opts & (OPT_FLUSH|OPT_CLEAR)) - flushtable(fd, opts); + flushtable(fd, opts, natfilter); if (file) { - ipnat_parsefile(fd, ipnat_addrule, ioctl, file); + return ipnat_parsefile(fd, ipnat_addrule, ioctl, file); } if (opts & (OPT_LIST|OPT_STAT)) - dostats(fd, nsp, opts, 1); + dostats(fd, nsp, opts, 1, natfilter); return 0; } @@ -231,8 +251,8 @@ char *argv[]; * rather than doing ioctl's. */ void natstat_dead(nsp, kernel) -natstat_t *nsp; -char *kernel; + natstat_t *nsp; + char *kernel; { struct nlist nat_nlist[10] = { { "nat_table" }, /* 0 */ @@ -243,7 +263,6 @@ char *kernel; { "ipf_rdrrules_sz" }, /* 5 */ { "ipf_hostmap_sz" }, { "nat_instances" }, - { "ap_sess_list" }, { NULL } }; void *tables[2]; @@ -259,8 +278,8 @@ char *kernel; * one in individually. */ kmemcpy((char *)&tables, nat_nlist[0].n_value, sizeof(tables)); - nsp->ns_table[0] = tables[0]; - nsp->ns_table[1] = tables[1]; + nsp->ns_side[0].ns_table = tables[0]; + nsp->ns_side[1].ns_table = tables[1]; kmemcpy((char *)&nsp->ns_list, nat_nlist[1].n_value, sizeof(nsp->ns_list)); @@ -276,8 +295,6 @@ char *kernel; sizeof(nsp->ns_hostmap_sz)); kmemcpy((char *)&nsp->ns_instances, nat_nlist[7].n_value, sizeof(nsp->ns_instances)); - kmemcpy((char *)&nsp->ns_apslist, nat_nlist[8].n_value, - sizeof(nsp->ns_apslist)); } @@ -285,23 +302,40 @@ char *kernel; * Issue an ioctl to flush either the NAT rules table or the active mapping * table or both. */ -void flushtable(fd, opts) -int fd, opts; +void flushtable(fd, opts, match) + int fd, opts, *match; { int n = 0; if (opts & OPT_FLUSH) { n = 0; - if (!(opts & OPT_DONOTHING) && ioctl(fd, SIOCIPFFL, &n) == -1) - perror("ioctl(SIOCFLNAT)"); - else + if (!(opts & OPT_DONOTHING)) { + if (match != NULL) { + ipfobj_t obj; + + obj.ipfo_rev = IPFILTER_VERSION; + obj.ipfo_size = match[0] * sizeof(int); + obj.ipfo_type = IPFOBJ_IPFEXPR; + obj.ipfo_ptr = match; + if (ioctl(fd, SIOCMATCHFLUSH, &obj) == -1) { + ipferror(fd, "ioctl(SIOCMATCHFLUSH)"); + n = -1; + } else { + n = obj.ipfo_retval; + } + } else if (ioctl(fd, SIOCIPFFL, &n) == -1) { + ipferror(fd, "ioctl(SIOCIPFFL)"); + n = -1; + } + } + if (n >= 0) printf("%d entries flushed from NAT table\n", n); } if (opts & OPT_CLEAR) { n = 1; if (!(opts & OPT_DONOTHING) && ioctl(fd, SIOCIPFFL, &n) == -1) - perror("ioctl(SIOCCNATL)"); + ipferror(fd, "ioctl(SIOCCNATL)"); else printf("%d entries flushed from NAT list\n", n); } @@ -311,34 +345,65 @@ int fd, opts; /* * Display NAT statistics. */ -void dostats_dead(nsp, opts) -natstat_t *nsp; -int opts; +void dostats_dead(nsp, opts, filter) + natstat_t *nsp; + int opts, *filter; { nat_t *np, nat; ipnat_t ipn; + int i; - printf("List of active MAP/Redirect filters:\n"); - while (nsp->ns_list) { - if (kmemcpy((char *)&ipn, (long)nsp->ns_list, - sizeof(ipn))) { - perror("kmemcpy"); - break; + if (nat_fields == NULL) { + printf("List of active MAP/Redirect filters:\n"); + while (nsp->ns_list) { + if (kmemcpy((char *)&ipn, (long)nsp->ns_list, + sizeof(ipn))) { + perror("kmemcpy"); + break; + } + if (opts & OPT_HITS) + printf("%lu ", ipn.in_hits); + printnat(&ipn, opts & (OPT_DEBUG|OPT_VERBOSE)); + nsp->ns_list = ipn.in_next; } - if (opts & OPT_HITS) - printf("%lu ", ipn.in_hits); - printnat(&ipn, opts & (OPT_DEBUG|OPT_VERBOSE)); - nsp->ns_list = ipn.in_next; } - printf("\nList of active sessions:\n"); + if (nat_fields == NULL) { + printf("\nList of active sessions:\n"); + + } else if (nohdrfields == 0) { + for (i = 0; nat_fields[i].w_value != 0; i++) { + printfieldhdr(natfields, nat_fields + i); + if (nat_fields[i + 1].w_value != 0) + printf("\t"); + } + printf("\n"); + } for (np = nsp->ns_instances; np; np = nat.nat_next) { if (kmemcpy((char *)&nat, (long)np, sizeof(nat))) break; - printactivenat(&nat, opts, 0, nsp->ns_ticks); - if (nat.nat_aps) - printaps(nat.nat_aps, opts); + if ((filter != NULL) && (nat_matcharray(&nat, filter) == 0)) + continue; + if (nat_fields != NULL) { + for (i = 0; nat_fields[i].w_value != 0; i++) { + printnatfield(&nat, nat_fields[i].w_value); + if (nat_fields[i + 1].w_value != 0) + printf("\t"); + } + printf("\n"); + } else { + printactivenat(&nat, opts, nsp->ns_ticks); + if (nat.nat_aps) { + int proto; + + if (nat.nat_dir & NAT_OUTBOUND) + proto = nat.nat_pr[1]; + else + proto = nat.nat_pr[0]; + printaps(nat.nat_aps, opts, proto); + } + } } if (opts & OPT_VERBOSE) @@ -346,62 +411,39 @@ int opts; } -void dostats(fd, nsp, opts, alive) -natstat_t *nsp; -int fd, opts, alive; +void dotable(nsp, fd, alive, which, side) + natstat_t *nsp; + int fd, alive, which; + char *side; { - /* - * Show statistics ? - */ - if (opts & OPT_STAT) { - printf("mapped\tin\t%lu\tout\t%lu\n", - nsp->ns_mapped[0], nsp->ns_mapped[1]); - printf("added\t%lu\texpired\t%lu\n", - nsp->ns_added, nsp->ns_expire); - printf("no memory\t%lu\tbad nat\t%lu\n", - nsp->ns_memfail, nsp->ns_badnat); - printf("inuse\t%lu\norphans\t%u\nrules\t%lu\n", - nsp->ns_inuse, nsp->ns_orphans, nsp->ns_rules); - printf("wilds\t%u\n", nsp->ns_wilds); - dotable(nsp, fd, alive); - if (opts & OPT_VERBOSE) - printf("table %p list %p\n", - nsp->ns_table, nsp->ns_list); - if (alive) - showtqtable_live(fd); - } - - if (opts & OPT_LIST) { - if (alive) - dostats_live(fd, nsp, opts); - else - dostats_dead(nsp, opts); - } -} - - -void dotable(nsp, fd, alive) -natstat_t *nsp; -int fd, alive; -{ - int sz, i, used, totallen, maxlen, minlen; + int sz, i, used, maxlen, minlen, totallen; ipftable_t table; - u_long *buckets; + u_int *buckets; ipfobj_t obj; sz = sizeof(*buckets) * nsp->ns_nattab_sz; - buckets = (u_long *)malloc(sz); + buckets = (u_int *)malloc(sz); + if (buckets == NULL) { + fprintf(stderr, + "cannot allocate memory (%d) for buckets\n", sz); + return; + } obj.ipfo_rev = IPFILTER_VERSION; obj.ipfo_type = IPFOBJ_GTABLE; obj.ipfo_size = sizeof(table); obj.ipfo_ptr = &table; - table.ita_type = IPFTABLE_BUCKETS_NATIN; + if (which == 0) { + table.ita_type = IPFTABLE_BUCKETS_NATIN; + } else if (which == 1) { + table.ita_type = IPFTABLE_BUCKETS_NATOUT; + } table.ita_table = buckets; if (alive) { if (ioctl(fd, SIOCGTABL, &obj) != 0) { + ipferror(fd, "SIOCFTABL"); free(buckets); return; } @@ -412,9 +454,9 @@ int fd, alive; } } + minlen = nsp->ns_side[which].ns_inuse; totallen = 0; maxlen = 0; - minlen = nsp->ns_inuse; used = 0; for (i = 0; i < nsp->ns_nattab_sz; i++) { @@ -427,27 +469,84 @@ int fd, alive; totallen += buckets[i]; } - printf("hash efficiency\t%2.2f%%\n", - totallen ? ((float)used / totallen) * 100.0 : 0.0); - printf("bucket usage\t%2.2f%%\n", - ((float)used / nsp->ns_nattab_sz) * 100.0); - printf("minimal length\t%d\n", minlen); - printf("maximal length\t%d\n", maxlen); - printf("average length\t%.3f\n", used ? (float)totallen / used : 0.0); + printf("%d%%\thash efficiency %s\n", + totallen ? used * 100 / totallen : 0, side); + printf("%2.2f%%\tbucket usage %s\n", + ((float)used / nsp->ns_nattab_sz) * 100.0, side); + printf("%d\tminimal length %s\n", minlen, side); + printf("%d\tmaximal length %s\n", maxlen, side); + printf("%.3f\taverage length %s\n", + used ? ((float)totallen / used) : 0.0, side); + + free(buckets); +} + + +void dostats(fd, nsp, opts, alive, filter) + natstat_t *nsp; + int fd, opts, alive, *filter; +{ + /* + * Show statistics ? + */ + if (opts & OPT_STAT) { + printnatside("in", &nsp->ns_side[0]); + dotable(nsp, fd, alive, 0, "in"); + + printnatside("out", &nsp->ns_side[1]); + dotable(nsp, fd, alive, 1, "out"); + + printf("%lu\tlog successes\n", nsp->ns_side[0].ns_log); + printf("%lu\tlog failures\n", nsp->ns_side[1].ns_log); + printf("%lu\tadded in\n%lu\tadded out\n", + nsp->ns_side[0].ns_added, + nsp->ns_side[1].ns_added); + printf("%u\tactive\n", nsp->ns_active); + printf("%lu\ttransparent adds\n", nsp->ns_addtrpnt); + printf("%lu\tdivert build\n", nsp->ns_divert_build); + printf("%lu\texpired\n", nsp->ns_expire); + printf("%lu\tflush all\n", nsp->ns_flush_all); + printf("%lu\tflush closing\n", nsp->ns_flush_closing); + printf("%lu\tflush queue\n", nsp->ns_flush_queue); + printf("%lu\tflush state\n", nsp->ns_flush_state); + printf("%lu\tflush timeout\n", nsp->ns_flush_timeout); + printf("%lu\thostmap new\n", nsp->ns_hm_new); + printf("%lu\thostmap fails\n", nsp->ns_hm_newfail); + printf("%lu\thostmap add\n", nsp->ns_hm_addref); + printf("%lu\thostmap NULL rule\n", nsp->ns_hm_nullnp); + printf("%lu\tlog ok\n", nsp->ns_log_ok); + printf("%lu\tlog fail\n", nsp->ns_log_fail); + printf("%u\torphan count\n", nsp->ns_orphans); + printf("%u\trule count\n", nsp->ns_rules); + printf("%u\tmap rules\n", nsp->ns_rules_map); + printf("%u\trdr rules\n", nsp->ns_rules_rdr); + printf("%u\twilds\n", nsp->ns_wilds); + if (opts & OPT_VERBOSE) + printf("list %p\n", nsp->ns_list); + } + + if (opts & OPT_LIST) { + if (alive) + dostats_live(fd, nsp, opts, filter); + else + dostats_dead(nsp, opts, filter); + } } /* * Display NAT statistics. */ -void dostats_live(fd, nsp, opts) -natstat_t *nsp; -int fd, opts; +void dostats_live(fd, nsp, opts, filter) + natstat_t *nsp; + int fd, opts, *filter; { ipfgeniter_t iter; + char buffer[2000]; ipfobj_t obj; - ipnat_t ipn; + ipnat_t *ipn; nat_t nat; + int i; bzero((char *)&obj, sizeof(obj)); obj.ipfo_rev = IPFILTER_VERSION; @@ -457,22 +556,39 @@ int fd, opts; iter.igi_type = IPFGENITER_IPNAT; iter.igi_nitems = 1; - iter.igi_data = &ipn; + iter.igi_data = buffer; + ipn = (ipnat_t *)buffer; /* * Show list of NAT rules and NAT sessions ? */ - printf("List of active MAP/Redirect filters:\n"); - while (nsp->ns_list) { - if (ioctl(fd, SIOCGENITER, &obj) == -1) - break; - if (opts & OPT_HITS) - printf("%lu ", ipn.in_hits); - printnat(&ipn, opts & (OPT_DEBUG|OPT_VERBOSE)); - nsp->ns_list = ipn.in_next; + if (nat_fields == NULL) { + printf("List of active MAP/Redirect filters:\n"); + while (nsp->ns_list) { + if (ioctl(fd, SIOCGENITER, &obj) == -1) + break; + if (opts & OPT_HITS) + printf("%lu ", ipn->in_hits); + printnat(ipn, opts & (OPT_DEBUG|OPT_VERBOSE)); + nsp->ns_list = ipn->in_next; + } } - printf("\nList of active sessions:\n"); + if (nat_fields == NULL) { + printf("\nList of active sessions:\n"); + + } else if (nohdrfields == 0) { + for (i = 0; nat_fields[i].w_value != 0; i++) { + printfieldhdr(natfields, nat_fields + i); + if (nat_fields[i + 1].w_value != 0) + printf("\t"); + } + printf("\n"); + } + + i = IPFGENITER_IPNAT; + (void) ioctl(fd,SIOCIPFDELTOK, &i); + iter.igi_type = IPFGENITER_NAT; iter.igi_nitems = 1; @@ -481,14 +597,35 @@ int fd, opts; while (nsp->ns_instances != NULL) { if (ioctl(fd, SIOCGENITER, &obj) == -1) break; - printactivenat(&nat, opts, 1, nsp->ns_ticks); - if (nat.nat_aps) - printaps(nat.nat_aps, opts); + if ((filter != NULL) && (nat_matcharray(&nat, filter) == 0)) + continue; + if (nat_fields != NULL) { + for (i = 0; nat_fields[i].w_value != 0; i++) { + printnatfield(&nat, nat_fields[i].w_value); + if (nat_fields[i + 1].w_value != 0) + printf("\t"); + } + printf("\n"); + } else { + printactivenat(&nat, opts, nsp->ns_ticks); + if (nat.nat_aps) { + int proto; + + if (nat.nat_dir & NAT_OUTBOUND) + proto = nat.nat_pr[1]; + else + proto = nat.nat_pr[0]; + printaps(nat.nat_aps, opts, proto); + } + } nsp->ns_instances = nat.nat_next; } if (opts & OPT_VERBOSE) showhostmap_live(fd, nsp); + + i = IPFGENITER_NAT; + (void) ioctl(fd,SIOCIPFDELTOK, &i); } @@ -496,7 +633,7 @@ int fd, opts; * Display the active host mapping table. */ void showhostmap_dead(nsp) -natstat_t *nsp; + natstat_t *nsp; { hostmap_t hm, *hmp, **maptable; u_int hv; @@ -532,12 +669,13 @@ natstat_t *nsp; * Display the active host mapping table. */ void showhostmap_live(fd, nsp) -int fd; -natstat_t *nsp; + int fd; + natstat_t *nsp; { ipfgeniter_t iter; hostmap_t hm; ipfobj_t obj; + int i; bzero((char *)&obj, sizeof(obj)); obj.ipfo_rev = IPFILTER_VERSION; @@ -554,25 +692,167 @@ natstat_t *nsp; while (nsp->ns_maplist != NULL) { if (ioctl(fd, SIOCGENITER, &obj) == -1) break; - printhostmap(&hm, 0); + printhostmap(&hm, hm.hm_hv); nsp->ns_maplist = hm.hm_next; } + + i = IPFGENITER_HOSTMAP; + (void) ioctl(fd,SIOCIPFDELTOK, &i); } -void showtqtable_live(fd) -int fd; +int nat_matcharray(nat, array) + nat_t *nat; + int *array; { - ipftq_t table[IPF_TCP_NSTATES]; - ipfobj_t obj; + int i, n, *x, rv, p; + ipfexp_t *e; - bzero((char *)&obj, sizeof(obj)); - obj.ipfo_rev = IPFILTER_VERSION; - obj.ipfo_size = sizeof(table); - obj.ipfo_ptr = (void *)table; - obj.ipfo_type = IPFOBJ_STATETQTAB; + rv = 0; + n = array[0]; + x = array + 1; - if (ioctl(fd, SIOCGTQTAB, &obj) == 0) { - printtqtable(table); + for (; n > 0; x += 3 + x[3], rv = 0) { + e = (ipfexp_t *)x; + if (e->ipfe_cmd == IPF_EXP_END) + break; + n -= e->ipfe_size; + + p = e->ipfe_cmd >> 16; + if ((p != 0) && (p != nat->nat_pr[1])) + break; + + switch (e->ipfe_cmd) + { + case IPF_EXP_IP_PR : + for (i = 0; !rv && i < e->ipfe_narg; i++) { + rv |= (nat->nat_pr[1] == e->ipfe_arg0[i]); + } + break; + + case IPF_EXP_IP_SRCADDR : + if (nat->nat_v[0] != 4) + break; + for (i = 0; !rv && i < e->ipfe_narg; i++) { + rv |= ((nat->nat_osrcaddr & + e->ipfe_arg0[i * 2 + 1]) == + e->ipfe_arg0[i * 2]) || + ((nat->nat_nsrcaddr & + e->ipfe_arg0[i * 2 + 1]) == + e->ipfe_arg0[i * 2]); + } + break; + + case IPF_EXP_IP_DSTADDR : + if (nat->nat_v[0] != 4) + break; + for (i = 0; !rv && i < e->ipfe_narg; i++) { + rv |= ((nat->nat_odstaddr & + e->ipfe_arg0[i * 2 + 1]) == + e->ipfe_arg0[i * 2]) || + ((nat->nat_ndstaddr & + e->ipfe_arg0[i * 2 + 1]) == + e->ipfe_arg0[i * 2]); + } + break; + + case IPF_EXP_IP_ADDR : + if (nat->nat_v[0] != 4) + break; + for (i = 0; !rv && i < e->ipfe_narg; i++) { + rv |= ((nat->nat_osrcaddr & + e->ipfe_arg0[i * 2 + 1]) == + e->ipfe_arg0[i * 2]) || + ((nat->nat_nsrcaddr & + e->ipfe_arg0[i * 2 + 1]) == + e->ipfe_arg0[i * 2]) || + ((nat->nat_odstaddr & + e->ipfe_arg0[i * 2 + 1]) == + e->ipfe_arg0[i * 2]) || + ((nat->nat_ndstaddr & + e->ipfe_arg0[i * 2 + 1]) == + e->ipfe_arg0[i * 2]); + } + break; + +#ifdef USE_INET6 + case IPF_EXP_IP6_SRCADDR : + if (nat->nat_v[0] != 6) + break; + for (i = 0; !rv && i < e->ipfe_narg; i++) { + rv |= IP6_MASKEQ(&nat->nat_osrc6, + &e->ipfe_arg0[i * 8 + 4], + &e->ipfe_arg0[i * 8]) || + IP6_MASKEQ(&nat->nat_nsrc6, + &e->ipfe_arg0[i * 8 + 4], + &e->ipfe_arg0[i * 8]); + } + break; + + case IPF_EXP_IP6_DSTADDR : + if (nat->nat_v[0] != 6) + break; + for (i = 0; !rv && i < e->ipfe_narg; i++) { + rv |= IP6_MASKEQ(&nat->nat_odst6, + &e->ipfe_arg0[i * 8 + 4], + &e->ipfe_arg0[i * 8]) || + IP6_MASKEQ(&nat->nat_ndst6, + &e->ipfe_arg0[i * 8 + 4], + &e->ipfe_arg0[i * 8]); + } + break; + + case IPF_EXP_IP6_ADDR : + if (nat->nat_v[0] != 6) + break; + for (i = 0; !rv && i < e->ipfe_narg; i++) { + rv |= IP6_MASKEQ(&nat->nat_osrc6, + &e->ipfe_arg0[i * 8 + 4], + &e->ipfe_arg0[i * 8]) || + IP6_MASKEQ(&nat->nat_nsrc6, + &e->ipfe_arg0[i * 8 + 4], + &e->ipfe_arg0[i * 8]) || + IP6_MASKEQ(&nat->nat_odst6, + &e->ipfe_arg0[i * 8 + 4], + &e->ipfe_arg0[i * 8]) || + IP6_MASKEQ(&nat->nat_ndst6, + &e->ipfe_arg0[i * 8 + 4], + &e->ipfe_arg0[i * 8]); + } + break; +#endif + + case IPF_EXP_UDP_PORT : + case IPF_EXP_TCP_PORT : + for (i = 0; !rv && i < e->ipfe_narg; i++) { + rv |= (nat->nat_osport == e->ipfe_arg0[i]) || + (nat->nat_nsport == e->ipfe_arg0[i]) || + (nat->nat_odport == e->ipfe_arg0[i]) || + (nat->nat_ndport == e->ipfe_arg0[i]); + } + break; + + case IPF_EXP_UDP_SPORT : + case IPF_EXP_TCP_SPORT : + for (i = 0; !rv && i < e->ipfe_narg; i++) { + rv |= (nat->nat_osport == e->ipfe_arg0[i]) || + (nat->nat_nsport == e->ipfe_arg0[i]); + } + break; + + case IPF_EXP_UDP_DPORT : + case IPF_EXP_TCP_DPORT : + for (i = 0; !rv && i < e->ipfe_narg; i++) { + rv |= (nat->nat_odport == e->ipfe_arg0[i]) || + (nat->nat_ndport == e->ipfe_arg0[i]); + } + break; + } + rv ^= e->ipfe_not; + + if (rv == 0) + break; } + + return rv; } diff --git a/contrib/ipfilter/tools/ipnat_y.y b/contrib/ipfilter/tools/ipnat_y.y index 7109f60ec71..71fb8ee3710 100644 --- a/contrib/ipfilter/tools/ipnat_y.y +++ b/contrib/ipfilter/tools/ipnat_y.y @@ -1,7 +1,7 @@ /* $FreeBSD$ */ /* - * Copyright (C) 2001-2006 by Darren Reed. + * Copyright (C) 2012 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. */ @@ -60,33 +60,58 @@ static int natfd = -1; static ioctlfunc_t natioctlfunc = NULL; static addfunc_t nataddfunc = NULL; static int suggest_port = 0; +static proxyrule_t *prules = NULL; +static int parser_error = 0; static void newnatrule __P((void)); static void setnatproto __P((int)); - +static void setmapifnames __P((void)); +static void setrdrifnames __P((void)); +static void proxy_setconfig __P((int)); +static void proxy_unsetconfig __P((void)); +static namelist_t *proxy_dns_add_pass __P((char *, char *)); +static namelist_t *proxy_dns_add_block __P((char *, char *)); +static void proxy_addconfig __P((char *, int, char *, namelist_t *)); +static void proxy_loadconfig __P((int, ioctlfunc_t, char *, int, + char *, namelist_t *)); +static void proxy_loadrules __P((int, ioctlfunc_t, proxyrule_t *)); +static void setmapifnames __P((void)); +static void setrdrifnames __P((void)); +static void setifname __P((ipnat_t **, int, char *)); +static int addname __P((ipnat_t **, char *)); %} %union { char *str; u_32_t num; - struct in_addr ipa; + struct { + i6addr_t a; + int f; + } ipa; frentry_t fr; frtuc_t *frt; u_short port; struct { - u_short p1; - u_short p2; + int p1; + int p2; int pc; } pc; struct { - struct in_addr a; - struct in_addr m; + i6addr_t a; + i6addr_t m; + int t; /* Address type */ + int u; + int f; /* Family */ + int v; /* IP version */ + int s; /* 0 = number, 1 = text */ + int n; /* number */ } ipp; union i6addr ip6; + namelist_t *names; }; %token YY_NUMBER YY_HEX %token YY_STR -%token YY_COMMENT +%token YY_COMMENT %token YY_CMP_EQ YY_CMP_NE YY_CMP_LE YY_CMP_GE YY_CMP_LT YY_CMP_GT %token YY_RANGE_OUT YY_RANGE_IN %token YY_IPV6 @@ -95,23 +120,42 @@ static void setnatproto __P((int)); %token IPNY_MAP IPNY_BIMAP IPNY_FROM IPNY_TO IPNY_MASK IPNY_PORTMAP IPNY_ANY %token IPNY_ROUNDROBIN IPNY_FRAG IPNY_AGE IPNY_ICMPIDMAP IPNY_PROXY %token IPNY_TCP IPNY_UDP IPNY_TCPUDP IPNY_STICKY IPNY_MSSCLAMP IPNY_TAG -%token IPNY_TLATE IPNY_SEQUENTIAL +%token IPNY_TLATE IPNY_POOL IPNY_HASH IPNY_NO IPNY_REWRITE IPNY_PROTO +%token IPNY_ON IPNY_SRC IPNY_DST IPNY_IN IPNY_OUT IPNY_DIVERT +%token IPNY_CONFIG IPNY_ALLOW IPNY_DENY IPNY_DNS IPNY_INET IPNY_INET6 +%token IPNY_SEQUENTIAL IPNY_DSTLIST IPNY_PURGE %type portspec %type hexnumber compare range proto -%type hostname ipv4 -%type addr nummask rhaddr -%type portstuff +%type saddr daddr sobject dobject mapfrom rdrfrom dip +%type hostname ipv4 ipaddr +%type addr rhsaddr rhdaddr erhdaddr +%type portstuff portpair comaports srcports dstports +%type dnslines dnsline %% file: line | assign | file line | file assign + | file pconf ';' ; -line: xx rule { while ((nat = nattop) != NULL) { +line: xx rule { int err; + while ((nat = nattop) != NULL) { + if (nat->in_v[0] == 0) + nat->in_v[0] = 4; + if (nat->in_v[1] == 0) + nat->in_v[1] = nat->in_v[0]; nattop = nat->in_next; - (*nataddfunc)(natfd, natioctlfunc, nat); + err = (*nataddfunc)(natfd, natioctlfunc, nat); free(nat); + if (err != 0) { + parser_error = err; + break; + } + } + if (parser_error == 0 && prules != NULL) { + proxy_loadrules(natfd, natioctlfunc, prules); + prules = NULL; } resetlexer(); } @@ -136,206 +180,541 @@ xx: { newnatrule(); } rule: map eol | mapblock eol | redir eol + | rewrite ';' + | divert ';' + ; + +no: IPNY_NO { nat->in_flags |= IPN_NO; } ; eol: | ';' ; -map: mapit ifnames addr IPNY_TLATE rhaddr proxy mapoptions - { nat->in_v = 4; - nat->in_inip = $3.a.s_addr; - nat->in_inmsk = $3.m.s_addr; - nat->in_outip = $5.a.s_addr; - nat->in_outmsk = $5.m.s_addr; - if (nat->in_ifnames[1][0] == '\0') - strncpy(nat->in_ifnames[1], - nat->in_ifnames[0], - sizeof(nat->in_ifnames[0])); - if ((nat->in_flags & IPN_TCPUDP) == 0) - setnatproto(nat->in_p); - if (((nat->in_redir & NAT_MAPBLK) != 0) || - ((nat->in_flags & IPN_AUTOPORTMAP) != 0)) - nat_setgroupmap(nat); +map: mapit ifnames addr tlate rhsaddr proxy mapoptions + { if ($3.f != 0 && $3.f != $5.f && $5.f != 0) + yyerror("3.address family mismatch"); + if (nat->in_v[0] == 0 && $5.v != 0) + nat->in_v[0] = $5.v; + else if (nat->in_v[0] == 0 && $3.v != 0) + nat->in_v[0] = $3.v; + if (nat->in_v[1] == 0 && $5.v != 0) + nat->in_v[1] = $5.v; + else if (nat->in_v[1] == 0 && $3.v != 0) + nat->in_v[1] = $3.v; + nat->in_osrcatype = $3.t; + bcopy(&$3.a, &nat->in_osrc.na_addr[0], + sizeof($3.a)); + bcopy(&$3.m, &nat->in_osrc.na_addr[1], + sizeof($3.a)); + nat->in_nsrcatype = $5.t; + nat->in_nsrcafunc = $5.u; + bcopy(&$5.a, &nat->in_nsrc.na_addr[0], + sizeof($5.a)); + bcopy(&$5.m, &nat->in_nsrc.na_addr[1], + sizeof($5.a)); + + setmapifnames(); } - | mapit ifnames addr IPNY_TLATE rhaddr mapport mapoptions - { nat->in_v = 4; - nat->in_inip = $3.a.s_addr; - nat->in_inmsk = $3.m.s_addr; - nat->in_outip = $5.a.s_addr; - nat->in_outmsk = $5.m.s_addr; - if (nat->in_ifnames[1][0] == '\0') - strncpy(nat->in_ifnames[1], - nat->in_ifnames[0], - sizeof(nat->in_ifnames[0])); - if (((nat->in_redir & NAT_MAPBLK) != 0) || - ((nat->in_flags & IPN_AUTOPORTMAP) != 0)) - nat_setgroupmap(nat); + | mapit ifnames addr tlate rhsaddr mapport mapoptions + { if ($3.f != $5.f && $3.f != 0 && $5.f != 0) + yyerror("4.address family mismatch"); + if (nat->in_v[1] == 0 && $5.v != 0) + nat->in_v[1] = $5.v; + else if (nat->in_v[0] == 0 && $3.v != 0) + nat->in_v[0] = $3.v; + if (nat->in_v[0] == 0 && $5.v != 0) + nat->in_v[0] = $5.v; + else if (nat->in_v[1] == 0 && $3.v != 0) + nat->in_v[1] = $3.v; + nat->in_osrcatype = $3.t; + bcopy(&$3.a, &nat->in_osrc.na_addr[0], + sizeof($3.a)); + bcopy(&$3.m, &nat->in_osrc.na_addr[1], + sizeof($3.a)); + nat->in_nsrcatype = $5.t; + nat->in_nsrcafunc = $5.u; + bcopy(&$5.a, &nat->in_nsrc.na_addr[0], + sizeof($5.a)); + bcopy(&$5.m, &nat->in_nsrc.na_addr[1], + sizeof($5.a)); + + setmapifnames(); } - | mapit ifnames mapfrom IPNY_TLATE rhaddr proxy mapoptions - { nat->in_v = 4; - nat->in_outip = $5.a.s_addr; - nat->in_outmsk = $5.m.s_addr; - if (nat->in_ifnames[1][0] == '\0') - strncpy(nat->in_ifnames[1], - nat->in_ifnames[0], - sizeof(nat->in_ifnames[0])); - if ((suggest_port == 1) && - (nat->in_flags & IPN_TCPUDP) == 0) - nat->in_flags |= IPN_TCPUDP; - if ((nat->in_flags & IPN_TCPUDP) == 0) - setnatproto(nat->in_p); - if (((nat->in_redir & NAT_MAPBLK) != 0) || - ((nat->in_flags & IPN_AUTOPORTMAP) != 0)) - nat_setgroupmap(nat); + | no mapit ifnames addr setproto ';' + { if (nat->in_v[0] == 0) + nat->in_v[0] = $4.v; + nat->in_osrcatype = $4.t; + bcopy(&$4.a, &nat->in_osrc.na_addr[0], + sizeof($4.a)); + bcopy(&$4.m, &nat->in_osrc.na_addr[1], + sizeof($4.a)); + + setmapifnames(); } - | mapit ifnames mapfrom IPNY_TLATE rhaddr mapport mapoptions - { nat->in_v = 4; - nat->in_outip = $5.a.s_addr; - nat->in_outmsk = $5.m.s_addr; - if (nat->in_ifnames[1][0] == '\0') - strncpy(nat->in_ifnames[1], - nat->in_ifnames[0], - sizeof(nat->in_ifnames[0])); - if ((suggest_port == 1) && - (nat->in_flags & IPN_TCPUDP) == 0) - nat->in_flags |= IPN_TCPUDP; - if (((nat->in_redir & NAT_MAPBLK) != 0) || - ((nat->in_flags & IPN_AUTOPORTMAP) != 0)) - nat_setgroupmap(nat); + | mapit ifnames mapfrom tlate rhsaddr proxy mapoptions + { if ($3 != 0 && $5.f != 0 && $3 != $5.f) + yyerror("5.address family mismatch"); + if (nat->in_v[0] == 0 && $5.v != 0) + nat->in_v[0] = $5.v; + else if (nat->in_v[0] == 0 && $3 != 0) + nat->in_v[0] = ftov($3); + if (nat->in_v[1] == 0 && $5.v != 0) + nat->in_v[1] = $5.v; + else if (nat->in_v[1] == 0 && $3 != 0) + nat->in_v[1] = ftov($3); + nat->in_nsrcatype = $5.t; + nat->in_nsrcafunc = $5.u; + bcopy(&$5.a, &nat->in_nsrc.na_addr[0], + sizeof($5.a)); + bcopy(&$5.m, &nat->in_nsrc.na_addr[1], + sizeof($5.a)); + + setmapifnames(); + } + | no mapit ifnames mapfrom setproto ';' + { nat->in_v[0] = ftov($4); + setmapifnames(); + } + | mapit ifnames mapfrom tlate rhsaddr mapport mapoptions + { if ($3 != 0 && $5.f != 0 && $3 != $5.f) + yyerror("6.address family mismatch"); + if (nat->in_v[0] == 0 && $5.v != 0) + nat->in_v[0] = $5.v; + else if (nat->in_v[0] == 0 && $3 != 0) + nat->in_v[0] = ftov($3); + if (nat->in_v[1] == 0 && $5.v != 0) + nat->in_v[1] = $5.v; + else if (nat->in_v[1] == 0 && $3 != 0) + nat->in_v[1] = ftov($3); + nat->in_nsrcatype = $5.t; + nat->in_nsrcafunc = $5.u; + bcopy(&$5.a, &nat->in_nsrc.na_addr[0], + sizeof($5.a)); + bcopy(&$5.m, &nat->in_nsrc.na_addr[1], + sizeof($5.a)); + + setmapifnames(); } ; mapblock: - mapblockit ifnames addr IPNY_TLATE addr ports mapoptions - { nat->in_v = 4; - nat->in_inip = $3.a.s_addr; - nat->in_inmsk = $3.m.s_addr; - nat->in_outip = $5.a.s_addr; - nat->in_outmsk = $5.m.s_addr; - if (nat->in_ifnames[1][0] == '\0') - strncpy(nat->in_ifnames[1], - nat->in_ifnames[0], - sizeof(nat->in_ifnames[0])); - if ((nat->in_flags & IPN_TCPUDP) == 0) - setnatproto(nat->in_p); - if (((nat->in_redir & NAT_MAPBLK) != 0) || - ((nat->in_flags & IPN_AUTOPORTMAP) != 0)) - nat_setgroupmap(nat); + mapblockit ifnames addr tlate addr ports mapoptions + { if ($3.f != 0 && $5.f != 0 && $3.f != $5.f) + yyerror("7.address family mismatch"); + if (nat->in_v[0] == 0 && $5.v != 0) + nat->in_v[0] = $5.v; + else if (nat->in_v[0] == 0 && $3.v != 0) + nat->in_v[0] = $3.v; + if (nat->in_v[1] == 0 && $5.v != 0) + nat->in_v[1] = $5.v; + else if (nat->in_v[1] == 0 && $3.v != 0) + nat->in_v[1] = $3.v; + nat->in_osrcatype = $3.t; + bcopy(&$3.a, &nat->in_osrc.na_addr[0], + sizeof($3.a)); + bcopy(&$3.m, &nat->in_osrc.na_addr[1], + sizeof($3.a)); + nat->in_nsrcatype = $5.t; + nat->in_nsrcafunc = $5.u; + bcopy(&$5.a, &nat->in_nsrc.na_addr[0], + sizeof($5.a)); + bcopy(&$5.m, &nat->in_nsrc.na_addr[1], + sizeof($5.a)); + + setmapifnames(); + } + | no mapblockit ifnames { yyexpectaddr = 1; } addr setproto ';' + { if (nat->in_v[0] == 0) + nat->in_v[0] = $5.v; + if (nat->in_v[1] == 0) + nat->in_v[1] = $5.v; + nat->in_osrcatype = $5.t; + bcopy(&$5.a, &nat->in_osrc.na_addr[0], + sizeof($5.a)); + bcopy(&$5.m, &nat->in_osrc.na_addr[1], + sizeof($5.a)); + + setmapifnames(); } ; -redir: rdrit ifnames addr dport IPNY_TLATE dip nport setproto rdroptions - { nat->in_v = 4; - nat->in_outip = $3.a.s_addr; - nat->in_outmsk = $3.m.s_addr; - if (nat->in_ifnames[1][0] == '\0') - strncpy(nat->in_ifnames[1], - nat->in_ifnames[0], - sizeof(nat->in_ifnames[0])); - if ((nat->in_p == 0) && - ((nat->in_flags & IPN_TCPUDP) == 0) && - (nat->in_pmin != 0 || - nat->in_pmax != 0 || - nat->in_pnext != 0)) - setnatproto(IPPROTO_TCP); +redir: rdrit ifnames addr dport tlate dip nport setproto rdroptions + { if ($6 != 0 && $3.f != 0 && $6 != $3.f) + yyerror("21.address family mismatch"); + if (nat->in_v[0] == 0) { + if ($3.v != AF_UNSPEC) + nat->in_v[0] = ftov($3.f); + else + nat->in_v[0] = ftov($6); + } + nat->in_odstatype = $3.t; + bcopy(&$3.a, &nat->in_odst.na_addr[0], + sizeof($3.a)); + bcopy(&$3.m, &nat->in_odst.na_addr[1], + sizeof($3.a)); + + setrdrifnames(); } - | rdrit ifnames rdrfrom IPNY_TLATE dip nport setproto rdroptions - { nat->in_v = 4; - if ((nat->in_p == 0) && - ((nat->in_flags & IPN_TCPUDP) == 0) && - (nat->in_pmin != 0 || - nat->in_pmax != 0 || - nat->in_pnext != 0)) - setnatproto(IPPROTO_TCP); - if ((suggest_port == 1) && - (nat->in_flags & IPN_TCPUDP) == 0) - nat->in_flags |= IPN_TCPUDP; - if (nat->in_ifnames[1][0] == '\0') - strncpy(nat->in_ifnames[1], - nat->in_ifnames[0], - sizeof(nat->in_ifnames[0])); + | no rdrit ifnames addr dport setproto ';' + { if (nat->in_v[0] == 0) + nat->in_v[0] = ftov($4.f); + nat->in_odstatype = $4.t; + bcopy(&$4.a, &nat->in_odst.na_addr[0], + sizeof($4.a)); + bcopy(&$4.m, &nat->in_odst.na_addr[1], + sizeof($4.a)); + + setrdrifnames(); } - | rdrit ifnames addr IPNY_TLATE dip setproto rdroptions - { nat->in_v = 4; - nat->in_outip = $3.a.s_addr; - nat->in_outmsk = $3.m.s_addr; - if (nat->in_ifnames[1][0] == '\0') - strncpy(nat->in_ifnames[1], - nat->in_ifnames[0], - sizeof(nat->in_ifnames[0])); + | rdrit ifnames rdrfrom tlate dip nport setproto rdroptions + { if ($5 != 0 && $3 != 0 && $5 != $3) + yyerror("20.address family mismatch"); + if (nat->in_v[0] == 0) { + if ($3 != AF_UNSPEC) + nat->in_v[0] = ftov($3); + else + nat->in_v[0] = ftov($5); + } + setrdrifnames(); } - | rdrit ifnames rdrfrom IPNY_TLATE dip setproto rdroptions - { nat->in_v = 4; - if ((suggest_port == 1) && - (nat->in_flags & IPN_TCPUDP) == 0) - nat->in_flags |= IPN_TCPUDP; - if (nat->in_ifnames[1][0] == '\0') - strncpy(nat->in_ifnames[1], - nat->in_ifnames[0], - sizeof(nat->in_ifnames[0])); + | no rdrit ifnames rdrfrom setproto ';' + { nat->in_v[0] = ftov($4); + + setrdrifnames(); + } + ; + +rewrite: + IPNY_REWRITE oninout rwrproto mapfrom tlate newdst newopts + { if (nat->in_v[0] == 0) + nat->in_v[0] = ftov($4); + if (nat->in_redir & NAT_MAP) + setmapifnames(); + else + setrdrifnames(); + nat->in_redir |= NAT_REWRITE; + } + ; + +divert: IPNY_DIVERT oninout rwrproto mapfrom tlate divdst newopts + { if (nat->in_v[0] == 0) + nat->in_v[0] = ftov($4); + if (nat->in_redir & NAT_MAP) { + setmapifnames(); + nat->in_pr[0] = IPPROTO_UDP; + } else { + setrdrifnames(); + nat->in_pr[1] = IPPROTO_UDP; + } + nat->in_flags &= ~IPN_TCP; + } + ; + +tlate: IPNY_TLATE { yyexpectaddr = 1; } + ; + +pconf: IPNY_PROXY { yysetdict(proxies); } + IPNY_DNS '/' proto IPNY_CONFIG YY_STR '{' + { proxy_setconfig(IPNY_DNS); } + dnslines ';' '}' + { proxy_addconfig("dns", $5, $7, $10); + proxy_unsetconfig(); + } + ; + +dnslines: + dnsline { $$ = $1; } + | dnslines ';' dnsline { $$ = $1; $1->na_next = $3; } + ; + +dnsline: + IPNY_ALLOW YY_STR { $$ = proxy_dns_add_pass(NULL, $2); } + | IPNY_DENY YY_STR { $$ = proxy_dns_add_block(NULL, $2); } + | IPNY_ALLOW '.' YY_STR { $$ = proxy_dns_add_pass(".", $3); } + | IPNY_DENY '.' YY_STR { $$ = proxy_dns_add_block(".", $3); } + ; + +oninout: + inout IPNY_ON ifnames { ; } + ; + +inout: IPNY_IN { nat->in_redir = NAT_REDIRECT; } + | IPNY_OUT { nat->in_redir = NAT_MAP; } + ; + +rwrproto: + | IPNY_PROTO setproto + ; + +newdst: src rhsaddr srcports dst erhdaddr dstports + { nat->in_nsrc.na_addr[0] = $2.a; + nat->in_nsrc.na_addr[1] = $2.m; + nat->in_nsrc.na_atype = $2.t; + if ($2.t == FRI_LOOKUP) { + nat->in_nsrc.na_type = $2.u; + nat->in_nsrc.na_subtype = $2.s; + nat->in_nsrc.na_num = $2.n; + } + nat->in_nsports[0] = $3.p1; + nat->in_nsports[1] = $3.p2; + nat->in_ndst.na_addr[0] = $5.a; + nat->in_ndst.na_addr[1] = $5.m; + nat->in_ndst.na_atype = $5.t; + if ($5.t == FRI_LOOKUP) { + nat->in_ndst.na_type = $5.u; + nat->in_ndst.na_subtype = $5.s; + nat->in_ndst.na_num = $5.n; + } + nat->in_ndports[0] = $6.p1; + nat->in_ndports[1] = $6.p2; + } + ; + +divdst: src addr ',' portspec dst addr ',' portspec IPNY_UDP + { nat->in_nsrc.na_addr[0] = $2.a; + if ($2.m.in4.s_addr != 0xffffffff) + yyerror("divert must have /32 dest"); + nat->in_nsrc.na_addr[1] = $2.m; + nat->in_nsports[0] = $4; + nat->in_nsports[1] = $4; + + nat->in_ndst.na_addr[0] = $6.a; + nat->in_ndst.na_addr[1] = $6.m; + if ($6.m.in4.s_addr != 0xffffffff) + yyerror("divert must have /32 dest"); + nat->in_ndports[0] = $8; + nat->in_ndports[1] = $8; + + nat->in_redir |= NAT_DIVERTUDP; + } + ; + +src: IPNY_SRC { yyexpectaddr = 1; } + ; + +dst: IPNY_DST { yyexpectaddr = 1; } + ; + +srcports: + comaports { $$.p1 = $1.p1; + $$.p2 = $1.p2; + } + | IPNY_PORT '=' portspec + { $$.p1 = $3; + $$.p2 = $3; + nat->in_flags |= IPN_FIXEDSPORT; + } + ; + +dstports: + comaports { $$.p1 = $1.p1; + $$.p2 = $1.p2; + } + | IPNY_PORT '=' portspec + { $$.p1 = $3; + $$.p2 = $3; + nat->in_flags |= IPN_FIXEDDPORT; + } + ; + +comaports: + { $$.p1 = 0; + $$.p2 = 0; + } + | ',' { if (!(nat->in_flags & IPN_TCPUDP)) + yyerror("must be TCP/UDP for ports"); + } + portpair { $$.p1 = $3.p1; + $$.p2 = $3.p2; } ; proxy: | IPNY_PROXY port portspec YY_STR '/' proto - { strncpy(nat->in_plabel, $4, sizeof(nat->in_plabel)); + { int pos; + pos = addname(&nat, $4); + nat->in_plabel = pos; if (nat->in_dcmp == 0) { - nat->in_dport = htons($3); - } else if ($3 != nat->in_dport) { + nat->in_odport = $3; + } else if ($3 != nat->in_odport) { yyerror("proxy port numbers not consistant"); } + nat->in_ndport = $3; setnatproto($6); free($4); } | IPNY_PROXY port YY_STR YY_STR '/' proto - { int pnum; - strncpy(nat->in_plabel, $4, sizeof(nat->in_plabel)); + { int pnum, pos; + pos = addname(&nat, $4); + nat->in_plabel = pos; pnum = getportproto($3, $6); if (pnum == -1) yyerror("invalid port number"); - nat->in_dport = pnum; + nat->in_odport = ntohs(pnum); + nat->in_ndport = ntohs(pnum); setnatproto($6); free($3); free($4); } + | IPNY_PROXY port portspec YY_STR '/' proto IPNY_CONFIG YY_STR + { int pos; + pos = addname(&nat, $4); + nat->in_plabel = pos; + if (nat->in_dcmp == 0) { + nat->in_odport = $3; + } else if ($3 != nat->in_odport) { + yyerror("proxy port numbers not consistant"); + } + nat->in_ndport = $3; + setnatproto($6); + nat->in_pconfig = addname(&nat, $8); + free($4); + free($8); + } + | IPNY_PROXY port YY_STR YY_STR '/' proto IPNY_CONFIG YY_STR + { int pnum, pos; + pos = addname(&nat, $4); + nat->in_plabel = pos; + pnum = getportproto($3, $6); + if (pnum == -1) + yyerror("invalid port number"); + nat->in_odport = ntohs(pnum); + nat->in_ndport = ntohs(pnum); + setnatproto($6); + pos = addname(&nat, $8); + nat->in_pconfig = pos; + free($3); + free($4); + free($8); + } ; - setproto: - | proto { if (nat->in_p != 0 || + | proto { if (nat->in_pr[0] != 0 || + nat->in_pr[1] != 0 || nat->in_flags & IPN_TCPUDP) yyerror("protocol set twice"); setnatproto($1); } - | IPNY_TCPUDP { if (nat->in_p != 0 || + | IPNY_TCPUDP { if (nat->in_pr[0] != 0 || + nat->in_pr[1] != 0 || nat->in_flags & IPN_TCPUDP) yyerror("protocol set twice"); nat->in_flags |= IPN_TCPUDP; - nat->in_p = 0; + nat->in_pr[0] = 0; + nat->in_pr[1] = 0; } - | IPNY_TCP '/' IPNY_UDP { if (nat->in_p != 0 || + | IPNY_TCP '/' IPNY_UDP { if (nat->in_pr[0] != 0 || + nat->in_pr[1] != 0 || nat->in_flags & IPN_TCPUDP) yyerror("protocol set twice"); nat->in_flags |= IPN_TCPUDP; - nat->in_p = 0; + nat->in_pr[0] = 0; + nat->in_pr[1] = 0; } ; -rhaddr: addr { $$.a = $1.a; $$.m = $1.m; } - | IPNY_RANGE ipv4 '-' ipv4 - { $$.a = $2; $$.m = $4; - nat->in_flags |= IPN_IPRANGE; } +rhsaddr: + addr { $$ = $1; + yyexpectaddr = 0; + } + | hostname '-' { yyexpectaddr = 1; } hostname + { $$.t = FRI_RANGE; + if ($1.f != $4.f) + yyerror("8.address family " + "mismatch"); + $$.f = $1.f; + $$.v = ftov($1.f); + $$.a = $1.a; + $$.m = $4.a; + nat->in_flags |= IPN_SIPRANGE; + yyexpectaddr = 0; + } + | IPNY_RANGE hostname '-' { yyexpectaddr = 1; } hostname + { $$.t = FRI_RANGE; + if ($2.f != $5.f) + yyerror("9.address family " + "mismatch"); + $$.f = $2.f; + $$.v = ftov($2.f); + $$.a = $2.a; + $$.m = $5.a; + nat->in_flags |= IPN_SIPRANGE; + yyexpectaddr = 0; + } ; dip: - hostname { nat->in_inip = $1.s_addr; - nat->in_inmsk = 0xffffffff; } - | hostname '/' YY_NUMBER { if ($3 != 0 || $1.s_addr != 0) - yyerror("Only 0/0 supported"); - nat->in_inip = 0; - nat->in_inmsk = 0; + hostname ',' { yyexpectaddr = 1; } hostname + { nat->in_flags |= IPN_SPLIT; + if ($1.f != $4.f) + yyerror("10.address family " + "mismatch"); + $$ = $1.f; + nat->in_ndstip6 = $1.a; + nat->in_ndstmsk6 = $4.a; + nat->in_ndstatype = FRI_SPLIT; + yyexpectaddr = 0; + } + | rhdaddr { int bits; + nat->in_ndstip6 = $1.a; + nat->in_ndstmsk6 = $1.m; + nat->in_ndst.na_atype = $1.t; + yyexpectaddr = 0; + if ($1.f == AF_INET) + bits = count4bits($1.m.in4.s_addr); + else + bits = count6bits($1.m.i6); + if (($1.f == AF_INET) && (bits != 0) && + (bits != 32)) { + yyerror("dest ip bitmask not /32"); + } else if (($1.f == AF_INET6) && + (bits != 0) && (bits != 128)) { + yyerror("dest ip bitmask not /128"); + } + $$ = $1.f; + } + ; + +rhdaddr: + addr { $$ = $1; + yyexpectaddr = 0; + } + | hostname '-' hostname { bzero(&$$, sizeof($$)); + $$.t = FRI_RANGE; + if ($1.f != 0 && $3.f != 0 && + $1.f != $3.f) + yyerror("11.address family " + "mismatch"); + $$.a = $1.a; + $$.m = $3.a; + nat->in_flags |= IPN_DIPRANGE; + yyexpectaddr = 0; + } + | IPNY_RANGE hostname '-' hostname + { bzero(&$$, sizeof($$)); + $$.t = FRI_RANGE; + if ($2.f != 0 && $4.f != 0 && + $2.f != $4.f) + yyerror("12.address family " + "mismatch"); + $$.a = $2.a; + $$.m = $4.a; + nat->in_flags |= IPN_DIPRANGE; + yyexpectaddr = 0; + } + ; + +erhdaddr: + rhdaddr { $$ = $1; } + | IPNY_DSTLIST '/' YY_NUMBER { $$.t = FRI_LOOKUP; + $$.u = IPLT_DSTLIST; + $$.s = 0; + $$.n = $3; + } + | IPNY_DSTLIST '/' YY_STR { $$.t = FRI_LOOKUP; + $$.u = IPLT_DSTLIST; + $$.s = 1; + $$.n = addname(&nat, $3); } - | hostname ',' hostname { nat->in_flags |= IPN_SPLIT; - nat->in_inip = $1.s_addr; - nat->in_inmsk = $3.s_addr; } ; port: IPNY_PORT { suggest_port = 1; } @@ -347,27 +726,44 @@ portspec: else $$ = $1; } - | YY_STR { if (getport(NULL, $1, &($$)) == -1) + | YY_STR { if (getport(NULL, $1, + &($$), NULL) == -1) yyerror("invalid port number"); $$ = ntohs($$); } ; -dport: | port portspec { nat->in_pmin = htons($2); - nat->in_pmax = htons($2); } - | port portspec '-' portspec { nat->in_pmin = htons($2); - nat->in_pmax = htons($4); } - | port portspec ':' portspec { nat->in_pmin = htons($2); - nat->in_pmax = htons($4); } +portpair: + portspec { $$.p1 = $1; $$.p2 = $1; } + | portspec '-' portspec { $$.p1 = $1; $$.p2 = $3; } + | portspec ':' portspec { $$.p1 = $1; $$.p2 = $3; } ; -nport: port portspec { nat->in_pnext = htons($2); } - | port '=' portspec { nat->in_pnext = htons($3); +dport: | port portpair { nat->in_odport = $2.p1; + if ($2.p2 == 0) + nat->in_dtop = $2.p1; + else + nat->in_dtop = $2.p2; + } + ; + +nport: | port portpair { nat->in_dpmin = $2.p1; + nat->in_dpnext = $2.p1; + nat->in_dpmax = $2.p2; + nat->in_ndport = $2.p1; + if (nat->in_dtop == 0) + nat->in_dtop = $2.p2; + } + | port '=' portspec { nat->in_dpmin = $3; + nat->in_dpnext = $3; + nat->in_ndport = $3; + if (nat->in_dtop == 0) + nat->in_dtop = nat->in_odport; nat->in_flags |= IPN_FIXEDDPORT; } ; -ports: | IPNY_PORTS YY_NUMBER { nat->in_pmin = $2; } +ports: | IPNY_PORTS YY_NUMBER { nat->in_spmin = $2; } | IPNY_PORTS IPNY_AUTO { nat->in_flags |= IPN_AUTOPORTMAP; } ; @@ -383,128 +779,282 @@ mapblockit: ; mapfrom: - from sobject IPNY_TO dobject - | from sobject '!' IPNY_TO dobject - { nat->in_flags |= IPN_NOTDST; } - | from sobject IPNY_TO '!' dobject - { nat->in_flags |= IPN_NOTDST; } + from sobject to dobject { if ($2 != 0 && $4 != 0 && $2 != $4) + yyerror("13.address family " + "mismatch"); + $$ = $2; + } + | from sobject '!' to dobject + { if ($2 != 0 && $5 != 0 && $2 != $5) + yyerror("14.address family " + "mismatch"); + nat->in_flags |= IPN_NOTDST; + $$ = $2; + } + | from sobject to '!' dobject + { if ($2 != 0 && $5 != 0 && $2 != $5) + yyerror("15.address family " + "mismatch"); + nat->in_flags |= IPN_NOTDST; + $$ = $2; + } ; rdrfrom: - from sobject IPNY_TO dobject - | '!' from sobject IPNY_TO dobject - { nat->in_flags |= IPN_NOTSRC; } - | from '!' sobject IPNY_TO dobject - { nat->in_flags |= IPN_NOTSRC; } + from sobject to dobject { if ($2 != 0 && $4 != 0 && $2 != $4) + yyerror("16.address family " + "mismatch"); + $$ = $2; + } + | '!' from sobject to dobject + { if ($3 != 0 && $5 != 0 && $3 != $5) + yyerror("17.address family " + "mismatch"); + nat->in_flags |= IPN_NOTSRC; + $$ = $3; + } + | from '!' sobject to dobject + { if ($3 != 0 && $5 != 0 && $3 != $5) + yyerror("18.address family " + "mismatch"); + nat->in_flags |= IPN_NOTSRC; + $$ = $3; + } ; -from: IPNY_FROM { nat->in_flags |= IPN_FILTER; } +from: IPNY_FROM { nat->in_flags |= IPN_FILTER; + yyexpectaddr = 1; + } + ; + +to: IPNY_TO { yyexpectaddr = 1; } ; ifnames: - ifname - | ifname ',' otherifname + ifname family { yyexpectaddr = 1; } + | ifname ',' otherifname family { yyexpectaddr = 1; } ; -ifname: YY_STR { strncpy(nat->in_ifnames[0], $1, - sizeof(nat->in_ifnames[0])); - nat->in_ifnames[0][LIFNAMSIZ - 1] = '\0'; - free($1); - } +ifname: YY_STR { setifname(&nat, 0, $1); + free($1); + } + ; + +family: | IPNY_INET { nat->in_v[0] = 4; nat->in_v[1] = 4; } + | IPNY_INET6 { nat->in_v[0] = 6; nat->in_v[1] = 6; } ; otherifname: - YY_STR { strncpy(nat->in_ifnames[1], $1, - sizeof(nat->in_ifnames[1])); - nat->in_ifnames[1][LIFNAMSIZ - 1] = '\0'; - free($1); - } + YY_STR { setifname(&nat, 1, $1); + free($1); + } ; mapport: - IPNY_PORTMAP tcpudp portspec ':' portspec randport - { nat->in_pmin = htons($3); - nat->in_pmax = htons($5); - } - | IPNY_PORTMAP tcpudp IPNY_AUTO randport - { nat->in_flags |= IPN_AUTOPORTMAP; - nat->in_pmin = htons(1024); - nat->in_pmax = htons(65535); - } - | IPNY_ICMPIDMAP YY_STR YY_NUMBER ':' YY_NUMBER - { if (strcmp($2, "icmp") != 0) { + IPNY_PORTMAP tcpudp portpair sequential + { nat->in_spmin = $3.p1; + nat->in_spmax = $3.p2; + } + | IPNY_PORTMAP portpair tcpudp sequential + { nat->in_spmin = $2.p1; + nat->in_spmax = $2.p2; + } + | IPNY_PORTMAP tcpudp IPNY_AUTO sequential + { nat->in_flags |= IPN_AUTOPORTMAP; + nat->in_spmin = 1024; + nat->in_spmax = 65535; + } + | IPNY_ICMPIDMAP YY_STR portpair sequential + { if (strcmp($2, "icmp") != 0 && + strcmp($2, "ipv6-icmp") != 0) { yyerror("icmpidmap not followed by icmp"); } free($2); - if ($3 < 0 || $3 > 65535) + if ($3.p1 < 0 || $3.p1 > 65535) yyerror("invalid ICMP Id number"); - if ($5 < 0 || $5 > 65535) + if ($3.p2 < 0 || $3.p2 > 65535) yyerror("invalid ICMP Id number"); + if (strcmp($2, "ipv6-icmp") == 0) { + nat->in_pr[0] = IPPROTO_ICMPV6; + nat->in_pr[1] = IPPROTO_ICMPV6; + } else { + nat->in_pr[0] = IPPROTO_ICMP; + nat->in_pr[1] = IPPROTO_ICMP; + } nat->in_flags = IPN_ICMPQUERY; - nat->in_pmin = htons($3); - nat->in_pmax = htons($5); + nat->in_spmin = $3.p1; + nat->in_spmax = $3.p2; } ; -randport: - | IPNY_SEQUENTIAL { nat->in_flags |= IPN_SEQUENTIAL; } - ; - sobject: - saddr - | saddr port portstuff { nat->in_sport = $3.p1; + saddr { $$ = $1; } + | saddr port portstuff { nat->in_osport = $3.p1; nat->in_stop = $3.p2; - nat->in_scmp = $3.pc; } + nat->in_scmp = $3.pc; + $$ = $1; + } ; -saddr: addr { if (nat->in_redir == NAT_REDIRECT) { - nat->in_srcip = $1.a.s_addr; - nat->in_srcmsk = $1.m.s_addr; - } else { - nat->in_inip = $1.a.s_addr; - nat->in_inmsk = $1.m.s_addr; - } +saddr: addr { nat->in_osrcatype = $1.t; + bcopy(&$1.a, + &nat->in_osrc.na_addr[0], + sizeof($1.a)); + bcopy(&$1.m, + &nat->in_osrc.na_addr[1], + sizeof($1.m)); + $$ = $1.f; } ; dobject: - daddr - | daddr port portstuff { nat->in_dport = $3.p1; + daddr { $$ = $1; } + | daddr port portstuff { nat->in_odport = $3.p1; nat->in_dtop = $3.p2; nat->in_dcmp = $3.pc; - if (nat->in_redir == NAT_REDIRECT) - nat->in_pmin = htons($3.p1); + $$ = $1; } ; -daddr: addr { if (nat->in_redir == NAT_REDIRECT) { - nat->in_outip = $1.a.s_addr; - nat->in_outmsk = $1.m.s_addr; - } else { - nat->in_srcip = $1.a.s_addr; - nat->in_srcmsk = $1.m.s_addr; +daddr: addr { nat->in_odstatype = $1.t; + bcopy(&$1.a, + &nat->in_odst.na_addr[0], + sizeof($1.a)); + bcopy(&$1.m, + &nat->in_odst.na_addr[1], + sizeof($1.m)); + $$ = $1.f; + } + ; + +addr: IPNY_ANY { yyexpectaddr = 0; + bzero(&$$, sizeof($$)); + $$.t = FRI_NORMAL; + } + | hostname { bzero(&$$, sizeof($$)); + $$.a = $1.a; + $$.t = FRI_NORMAL; + $$.v = ftov($1.f); + $$.f = $1.f; + if ($$.f == AF_INET) { + $$.m.in4.s_addr = 0xffffffff; + } else if ($$.f == AF_INET6) { + $$.m.i6[0] = 0xffffffff; + $$.m.i6[1] = 0xffffffff; + $$.m.i6[2] = 0xffffffff; + $$.m.i6[3] = 0xffffffff; } + yyexpectaddr = 0; + } + | hostname slash YY_NUMBER + { bzero(&$$, sizeof($$)); + $$.a = $1.a; + $$.f = $1.f; + $$.v = ftov($1.f); + $$.t = FRI_NORMAL; + ntomask($$.f, $3, (u_32_t *)&$$.m); + $$.a.i6[0] &= $$.m.i6[0]; + $$.a.i6[1] &= $$.m.i6[1]; + $$.a.i6[2] &= $$.m.i6[2]; + $$.a.i6[3] &= $$.m.i6[3]; + yyexpectaddr = 0; + } + | hostname slash ipaddr { bzero(&$$, sizeof($$)); + if ($1.f != $3.f) { + yyerror("1.address family " + "mismatch"); + } + $$.a = $1.a; + $$.m = $3.a; + $$.t = FRI_NORMAL; + $$.a.i6[0] &= $$.m.i6[0]; + $$.a.i6[1] &= $$.m.i6[1]; + $$.a.i6[2] &= $$.m.i6[2]; + $$.a.i6[3] &= $$.m.i6[3]; + $$.f = $1.f; + $$.v = ftov($1.f); + yyexpectaddr = 0; + } + | hostname slash hexnumber { bzero(&$$, sizeof($$)); + $$.a = $1.a; + $$.m.in4.s_addr = htonl($3); + $$.t = FRI_NORMAL; + $$.a.in4.s_addr &= $$.m.in4.s_addr; + $$.f = $1.f; + $$.v = ftov($1.f); + if ($$.f == AF_INET6) + yyerror("incorrect inet6 mask"); + } + | hostname mask ipaddr { bzero(&$$, sizeof($$)); + if ($1.f != $3.f) { + yyerror("2.address family " + "mismatch"); + } + $$.a = $1.a; + $$.m = $3.a; + $$.t = FRI_NORMAL; + $$.a.i6[0] &= $$.m.i6[0]; + $$.a.i6[1] &= $$.m.i6[1]; + $$.a.i6[2] &= $$.m.i6[2]; + $$.a.i6[3] &= $$.m.i6[3]; + $$.f = $1.f; + $$.v = ftov($1.f); + yyexpectaddr = 0; + } + | hostname mask hexnumber { bzero(&$$, sizeof($$)); + $$.a = $1.a; + $$.m.in4.s_addr = htonl($3); + $$.t = FRI_NORMAL; + $$.a.in4.s_addr &= $$.m.in4.s_addr; + $$.f = AF_INET; + $$.v = 4; + } + | pool slash YY_NUMBER { bzero(&$$, sizeof($$)); + $$.a.iplookupnum = $3; + $$.a.iplookuptype = IPLT_POOL; + $$.a.iplookupsubtype = 0; + $$.t = FRI_LOOKUP; + } + | pool slash YY_STR { bzero(&$$, sizeof($$)); + $$.a.iplookupname = addname(&nat,$3); + $$.a.iplookuptype = IPLT_POOL; + $$.a.iplookupsubtype = 1; + $$.t = FRI_LOOKUP; + } + | hash slash YY_NUMBER { bzero(&$$, sizeof($$)); + $$.a.iplookupnum = $3; + $$.a.iplookuptype = IPLT_HASH; + $$.a.iplookupsubtype = 0; + $$.t = FRI_LOOKUP; + } + | hash slash YY_STR { bzero(&$$, sizeof($$)); + $$.a.iplookupname = addname(&nat,$3); + $$.a.iplookuptype = IPLT_HASH; + $$.a.iplookupsubtype = 1; + $$.t = FRI_LOOKUP; } ; -addr: IPNY_ANY { $$.a.s_addr = 0; $$.m.s_addr = 0; } - | nummask { $$.a = $1.a; $$.m = $1.m; - $$.a.s_addr &= $$.m.s_addr; } - | hostname '/' ipv4 { $$.a = $1; $$.m = $3; - $$.a.s_addr &= $$.m.s_addr; } - | hostname '/' hexnumber { $$.a = $1; $$.m.s_addr = htonl($3); - $$.a.s_addr &= $$.m.s_addr; } - | hostname IPNY_MASK ipv4 { $$.a = $1; $$.m = $3; - $$.a.s_addr &= $$.m.s_addr; } - | hostname IPNY_MASK hexnumber { $$.a = $1; $$.m.s_addr = htonl($3); - $$.a.s_addr &= $$.m.s_addr; } +slash: '/' { yyexpectaddr = 0; } ; -nummask: - hostname { $$.a = $1; - $$.m.s_addr = 0xffffffff; } - | hostname '/' YY_NUMBER { $$.a = $1; - ntomask(4, $3, &$$.m.s_addr); } +mask: IPNY_MASK { yyexpectaddr = 0; } + ; + +pool: IPNY_POOL { if (!(nat->in_flags & IPN_FILTER)) { + yyerror("Can only use pool with from/to rules\n"); + } + yyexpectaddr = 0; + yyresetdict(); + } + ; + +hash: IPNY_HASH { if (!(nat->in_flags & IPN_FILTER)) { + yyerror("Can only use hash with from/to rules\n"); + } + yyexpectaddr = 0; + yyresetdict(); + } ; portstuff: @@ -513,17 +1063,16 @@ portstuff: ; mapoptions: - rr frag age mssclamp nattag setproto + rr frag age mssclamp nattag setproto purge ; rdroptions: - rr frag age sticky mssclamp rdrproxy nattag + rr frag age sticky mssclamp rdrproxy nattag purge ; nattag: | IPNY_TAG YY_STR { strncpy(nat->in_tag.ipt_tag, $2, sizeof(nat->in_tag.ipt_tag)); } - rr: | IPNY_ROUNDROBIN { nat->in_flags |= IPN_ROUNDR; } ; @@ -536,9 +1085,9 @@ age: | IPNY_AGE YY_NUMBER { nat->in_age[0] = $2; nat->in_age[1] = $4; } ; -sticky: | IPNY_STICKY { if (!(nat->in_flags & IPN_ROUNDR) && +sticky: | IPNY_STICKY { if (!(nat->in_flags & IPN_ROUNDR) && !(nat->in_flags & IPN_SPLIT)) { - fprintf(stderr, + FPRINTF(stderr, "'sticky' for use with round-robin/IP splitting only\n"); } else nat->in_flags |= IPN_STICKY; @@ -549,30 +1098,47 @@ mssclamp: | IPNY_MSSCLAMP YY_NUMBER { nat->in_mssclamp = $2; } ; -tcpudp: | IPNY_TCP { setnatproto(IPPROTO_TCP); } +tcpudp: IPNY_TCP { setnatproto(IPPROTO_TCP); } | IPNY_UDP { setnatproto(IPPROTO_UDP); } | IPNY_TCPUDP { nat->in_flags |= IPN_TCPUDP; - nat->in_p = 0; + nat->in_pr[0] = 0; + nat->in_pr[1] = 0; } | IPNY_TCP '/' IPNY_UDP { nat->in_flags |= IPN_TCPUDP; - nat->in_p = 0; + nat->in_pr[0] = 0; + nat->in_pr[1] = 0; } ; +sequential: + | IPNY_SEQUENTIAL { nat->in_flags |= IPN_SEQUENTIAL; } + ; + +purge: + | IPNY_PURGE { nat->in_flags |= IPN_PURGE; } + ; + rdrproxy: IPNY_PROXY YY_STR - { strncpy(nat->in_plabel, $2, - sizeof(nat->in_plabel)); - nat->in_dport = nat->in_pnext; - nat->in_dport = htons(nat->in_dport); + { int pos; + pos = addname(&nat, $2); + nat->in_plabel = pos; + nat->in_odport = nat->in_dpnext; + nat->in_dtop = nat->in_odport; free($2); } - | proxy { if (nat->in_plabel[0] != '\0') { - nat->in_pmin = nat->in_dport; - nat->in_pmax = nat->in_pmin; - nat->in_pnext = nat->in_pmin; - } - } + | proxy { if (nat->in_plabel != -1) { + nat->in_ndport = nat->in_odport; + nat->in_dpmin = nat->in_odport; + nat->in_dpmax = nat->in_dpmin; + nat->in_dtop = nat->in_dpmin; + nat->in_dpnext = nat->in_dpmin; + } + } + ; + +newopts: + | IPNY_PURGE { nat->in_flags |= IPN_PURGE; } ; proto: YY_NUMBER { $$ = $1; @@ -582,7 +1148,10 @@ proto: YY_NUMBER { $$ = $1; } | IPNY_TCP { $$ = IPPROTO_TCP; } | IPNY_UDP { $$ = IPPROTO_UDP; } - | YY_STR { $$ = getproto($1); free($1); + | YY_STR { $$ = getproto($1); + free($1); + if ($$ == -1) + yyerror("unknwon protocol"); if ($$ != IPPROTO_TCP && $$ != IPPROTO_UDP) suggest_port = 0; @@ -594,14 +1163,39 @@ hexnumber: ; hostname: - YY_STR { if (gethost($1, &$$.s_addr) == -1) - fprintf(stderr, + YY_STR { i6addr_t addr; + + bzero(&$$, sizeof($$)); + if (gethost(AF_INET, $1, + &addr) == 0) { + $$.a = addr; + $$.f = AF_INET; + } else + if (gethost(AF_INET6, $1, + &addr) == 0) { + $$.a = addr; + $$.f = AF_INET6; + } else { + FPRINTF(stderr, "Unknown host '%s'\n", $1); + } free($1); } - | YY_NUMBER { $$.s_addr = htonl($1); } - | ipv4 { $$.s_addr = $1.s_addr; } + | YY_NUMBER { bzero(&$$, sizeof($$)); + $$.a.in4.s_addr = htonl($1); + if ($$.a.in4.s_addr != 0) + $$.f = AF_INET; + } + | ipv4 { $$ = $1; } + | YY_IPV6 { bzero(&$$, sizeof($$)); + $$.a = $1; + $$.f = AF_INET6; + } + | YY_NUMBER YY_IPV6 { bzero(&$$, sizeof($$)); + $$.a = $2; + $$.f = AF_INET6; + } ; compare: @@ -619,40 +1213,77 @@ range: | ':' { $$ = FR_INCRANGE; } ; +ipaddr: ipv4 { $$ = $1; } + | YY_IPV6 { $$.a = $1; + $$.f = AF_INET6; + } + ; + ipv4: YY_NUMBER '.' YY_NUMBER '.' YY_NUMBER '.' YY_NUMBER { if ($1 > 255 || $3 > 255 || $5 > 255 || $7 > 255) { yyerror("Invalid octet string for IP address"); return 0; } - $$.s_addr = ($1 << 24) | ($3 << 16) | ($5 << 8) | $7; - $$.s_addr = htonl($$.s_addr); + bzero((char *)&$$, sizeof($$)); + $$.a.in4.s_addr = ($1 << 24) | ($3 << 16) | ($5 << 8) | $7; + $$.a.in4.s_addr = htonl($$.a.in4.s_addr); + $$.f = AF_INET; } ; %% +static wordtab_t proxies[] = { + { "dns", IPNY_DNS } +}; + +static wordtab_t dnswords[] = { + { "allow", IPNY_ALLOW }, + { "block", IPNY_DENY }, + { "deny", IPNY_DENY }, + { "drop", IPNY_DENY }, + { "pass", IPNY_ALLOW }, + +}; + static wordtab_t yywords[] = { { "age", IPNY_AGE }, { "any", IPNY_ANY }, { "auto", IPNY_AUTO }, { "bimap", IPNY_BIMAP }, + { "config", IPNY_CONFIG }, + { "divert", IPNY_DIVERT }, + { "dst", IPNY_DST }, + { "dstlist", IPNY_DSTLIST }, { "frag", IPNY_FRAG }, { "from", IPNY_FROM }, + { "hash", IPNY_HASH }, { "icmpidmap", IPNY_ICMPIDMAP }, + { "in", IPNY_IN }, + { "inet", IPNY_INET }, + { "inet6", IPNY_INET6 }, { "mask", IPNY_MASK }, { "map", IPNY_MAP }, { "map-block", IPNY_MAPBLOCK }, { "mssclamp", IPNY_MSSCLAMP }, { "netmask", IPNY_MASK }, + { "no", IPNY_NO }, + { "on", IPNY_ON }, + { "out", IPNY_OUT }, + { "pool", IPNY_POOL }, { "port", IPNY_PORT }, { "portmap", IPNY_PORTMAP }, { "ports", IPNY_PORTS }, + { "proto", IPNY_PROTO }, { "proxy", IPNY_PROXY }, + { "purge", IPNY_PURGE }, { "range", IPNY_RANGE }, + { "rewrite", IPNY_REWRITE }, { "rdr", IPNY_RDR }, { "round-robin",IPNY_ROUNDROBIN }, { "sequential", IPNY_SEQUENTIAL }, + { "src", IPNY_SRC }, { "sticky", IPNY_STICKY }, { "tag", IPNY_TAG }, { "tcp", IPNY_TCP }, @@ -671,15 +1302,19 @@ static wordtab_t yywords[] = { }; -int ipnat_parsefile(fd, addfunc, ioctlfunc, filename) -int fd; -addfunc_t addfunc; -ioctlfunc_t ioctlfunc; -char *filename; +int +ipnat_parsefile(fd, addfunc, ioctlfunc, filename) + int fd; + addfunc_t addfunc; + ioctlfunc_t ioctlfunc; + char *filename; { FILE *fp = NULL; + int rval; char *s; + yylineNum = 1; + (void) yysettab(yywords); s = getenv("YYDEBUG"); @@ -691,45 +1326,49 @@ char *filename; if (strcmp(filename, "-")) { fp = fopen(filename, "r"); if (!fp) { - fprintf(stderr, "fopen(%s) failed: %s\n", filename, + FPRINTF(stderr, "fopen(%s) failed: %s\n", filename, STRERROR(errno)); return -1; } } else fp = stdin; - while (ipnat_parsesome(fd, addfunc, ioctlfunc, fp) == 1) + while ((rval = ipnat_parsesome(fd, addfunc, ioctlfunc, fp)) == 0) ; if (fp != NULL) fclose(fp); - return 0; + if (rval == -1) + rval = 0; + else if (rval != 0) + rval = 1; + return rval; } -int ipnat_parsesome(fd, addfunc, ioctlfunc, fp) -int fd; -addfunc_t addfunc; -ioctlfunc_t ioctlfunc; -FILE *fp; +int +ipnat_parsesome(fd, addfunc, ioctlfunc, fp) + int fd; + addfunc_t addfunc; + ioctlfunc_t ioctlfunc; + FILE *fp; { char *s; int i; - yylineNum = 1; - natfd = fd; + parser_error = 0; nataddfunc = addfunc; natioctlfunc = ioctlfunc; if (feof(fp)) - return 0; + return -1; i = fgetc(fp); if (i == EOF) - return 0; + return -1; if (ungetc(i, fp) == EOF) - return 0; + return -1; if (feof(fp)) - return 0; + return -1; s = getenv("YYDEBUG"); if (s) yydebug = atoi(s); @@ -738,11 +1377,12 @@ FILE *fp; yyin = fp; yyparse(); - return 1; + return parser_error; } -static void newnatrule() +static void +newnatrule() { ipnat_t *n; @@ -750,21 +1390,32 @@ static void newnatrule() if (n == NULL) return; - if (nat == NULL) + if (nat == NULL) { nattop = nat = n; - else { + n->in_pnext = &nattop; + } else { nat->in_next = n; + n->in_pnext = &nat->in_next; nat = n; } + n->in_flineno = yylineNum; + n->in_ifnames[0] = -1; + n->in_ifnames[1] = -1; + n->in_plabel = -1; + n->in_pconfig = -1; + n->in_size = sizeof(*n); + suggest_port = 0; } -static void setnatproto(p) -int p; +static void +setnatproto(p) + int p; { - nat->in_p = p; + nat->in_pr[0] = p; + nat->in_pr[1] = p; switch (p) { @@ -778,12 +1429,16 @@ int p; break; case IPPROTO_ICMP : nat->in_flags &= ~IPN_TCPUDP; - if (!(nat->in_flags & IPN_ICMPQUERY)) { + if (!(nat->in_flags & IPN_ICMPQUERY) && + !(nat->in_redir & NAT_DIVERTUDP)) { nat->in_dcmp = 0; nat->in_scmp = 0; - nat->in_pmin = 0; - nat->in_pmax = 0; - nat->in_pnext = 0; + nat->in_dpmin = 0; + nat->in_dpmax = 0; + nat->in_dpnext = 0; + nat->in_spmin = 0; + nat->in_spmax = 0; + nat->in_spnext = 0; } break; default : @@ -791,22 +1446,36 @@ int p; nat->in_flags &= ~IPN_TCPUDP; nat->in_dcmp = 0; nat->in_scmp = 0; - nat->in_pmin = 0; - nat->in_pmax = 0; - nat->in_pnext = 0; + nat->in_dpmin = 0; + nat->in_dpmax = 0; + nat->in_dpnext = 0; + nat->in_spmin = 0; + nat->in_spmax = 0; + nat->in_spnext = 0; } break; } + if ((nat->in_flags & (IPN_TCP|IPN_UDP)) == 0) { + nat->in_stop = 0; + nat->in_dtop = 0; + nat->in_osport = 0; + nat->in_odport = 0; + nat->in_stop = 0; + nat->in_osport = 0; + nat->in_dtop = 0; + nat->in_odport = 0; + } if ((nat->in_flags & (IPN_TCPUDP|IPN_FIXEDDPORT)) == IPN_FIXEDDPORT) nat->in_flags &= ~IPN_FIXEDDPORT; } -void ipnat_addrule(fd, ioctlfunc, ptr) -int fd; -ioctlfunc_t ioctlfunc; -void *ptr; +int +ipnat_addrule(fd, ioctlfunc, ptr) + int fd; + ioctlfunc_t ioctlfunc; + void *ptr; { ioctlcmd_t add, del; ipfobj_t obj; @@ -815,20 +1484,19 @@ void *ptr; ipn = ptr; bzero((char *)&obj, sizeof(obj)); obj.ipfo_rev = IPFILTER_VERSION; - obj.ipfo_size = sizeof(ipnat_t); + obj.ipfo_size = ipn->in_size; obj.ipfo_type = IPFOBJ_IPNAT; obj.ipfo_ptr = ptr; - add = 0; - del = 0; if ((opts & OPT_DONOTHING) != 0) fd = -1; if (opts & OPT_ZERORULEST) { add = SIOCZRLST; - } else if (opts & OPT_INACTIVE) { - add = SIOCADNAT; - del = SIOCRMNAT; + del = 0; + } else if (opts & OPT_PURGE) { + add = 0; + del = SIOCPURGENAT; } else { add = SIOCADNAT; del = SIOCRMNAT; @@ -843,37 +1511,269 @@ void *ptr; if ((opts & OPT_ZERORULEST) != 0) { if ((*ioctlfunc)(fd, add, (void *)&obj) == -1) { if ((opts & OPT_DONOTHING) == 0) { - fprintf(stderr, "%d:", yylineNum); - perror("ioctl(SIOCZRLST)"); + char msg[80]; + + sprintf(msg, "%d:ioctl(zero nat rule)", + ipn->in_flineno); + return ipf_perror_fd(fd, ioctlfunc, msg); } } else { -#ifdef USE_QUAD_T -/* - printf("hits %qd bytes %qd ", - (long long)fr->fr_hits, - (long long)fr->fr_bytes); -*/ + PRINTF("hits %lu ", ipn->in_hits); +#ifdef USE_QUAD_T + PRINTF("bytes %"PRIu64" ", + ipn->in_bytes[0] + ipn->in_bytes[1]); #else -/* - printf("hits %ld bytes %ld ", - fr->fr_hits, fr->fr_bytes); -*/ + PRINTF("bytes %lu ", + ipn->in_bytes[0] + ipn->in_bytes[1]); #endif printnat(ipn, opts); } } else if ((opts & OPT_REMOVE) != 0) { if ((*ioctlfunc)(fd, del, (void *)&obj) == -1) { if ((opts & OPT_DONOTHING) == 0) { - fprintf(stderr, "%d:", yylineNum); - perror("ioctl(delete nat rule)"); + char msg[80]; + + sprintf(msg, "%d:ioctl(delete nat rule)", + ipn->in_flineno); + return ipf_perror_fd(fd, ioctlfunc, msg); } } } else { if ((*ioctlfunc)(fd, add, (void *)&obj) == -1) { if ((opts & OPT_DONOTHING) == 0) { - fprintf(stderr, "%d:", yylineNum); - perror("ioctl(add/insert nat rule)"); + char msg[80]; + + sprintf(msg, "%d:ioctl(add/insert nat rule)", + ipn->in_flineno); + if (errno == EEXIST) { + sprintf(msg + strlen(msg), "(line %d)", + ipn->in_flineno); + } + return ipf_perror_fd(fd, ioctlfunc, msg); } } } + return 0; +} + + +static void +setmapifnames() +{ + if (nat->in_ifnames[1] == -1) + nat->in_ifnames[1] = nat->in_ifnames[0]; + + if ((suggest_port == 1) && (nat->in_flags & IPN_TCPUDP) == 0) + nat->in_flags |= IPN_TCPUDP; + + if ((nat->in_flags & IPN_TCPUDP) == 0) + setnatproto(nat->in_pr[1]); + + if (((nat->in_redir & NAT_MAPBLK) != 0) || + ((nat->in_flags & IPN_AUTOPORTMAP) != 0)) + nat_setgroupmap(nat); +} + + +static void +setrdrifnames() +{ + if ((suggest_port == 1) && (nat->in_flags & IPN_TCPUDP) == 0) + nat->in_flags |= IPN_TCPUDP; + + if ((nat->in_pr[0] == 0) && ((nat->in_flags & IPN_TCPUDP) == 0) && + (nat->in_dpmin != 0 || nat->in_dpmax != 0 || nat->in_dpnext != 0)) + setnatproto(IPPROTO_TCP); + + if (nat->in_ifnames[1] == -1) + nat->in_ifnames[1] = nat->in_ifnames[0]; +} + + +static void +proxy_setconfig(proxy) + int proxy; +{ + if (proxy == IPNY_DNS) { + yysetfixeddict(dnswords); + } +} + + +static void +proxy_unsetconfig() +{ + yyresetdict(); +} + + +static namelist_t * +proxy_dns_add_pass(prefix, name) + char *prefix, *name; +{ + namelist_t *n; + + n = calloc(1, sizeof(*n)); + if (n != NULL) { + if (prefix == NULL || *prefix == '\0') { + n->na_name = strdup(name); + } else { + n->na_name = malloc(strlen(name) + strlen(prefix) + 1); + strcpy(n->na_name, prefix); + strcat(n->na_name, name); + } + } + return n; +} + + +static namelist_t * +proxy_dns_add_block(prefix, name) + char *prefix, *name; +{ + namelist_t *n; + + n = calloc(1, sizeof(*n)); + if (n != NULL) { + if (prefix == NULL || *prefix == '\0') { + n->na_name = strdup(name); + } else { + n->na_name = malloc(strlen(name) + strlen(prefix) + 1); + strcpy(n->na_name, prefix); + strcat(n->na_name, name); + } + n->na_value = 1; + } + return n; +} + + +static void +proxy_addconfig(proxy, proto, conf, list) + char *proxy, *conf; + int proto; + namelist_t *list; +{ + proxyrule_t *pr; + + pr = calloc(1, sizeof(*pr)); + if (pr != NULL) { + pr->pr_proto = proto; + pr->pr_proxy = proxy; + pr->pr_conf = conf; + pr->pr_names = list; + pr->pr_next = prules; + prules = pr; + } +} + + +static void +proxy_loadrules(fd, ioctlfunc, rules) + int fd; + ioctlfunc_t ioctlfunc; + proxyrule_t *rules; +{ + proxyrule_t *pr; + + while ((pr = rules) != NULL) { + proxy_loadconfig(fd, ioctlfunc, pr->pr_proxy, pr->pr_proto, + pr->pr_conf, pr->pr_names); + rules = pr->pr_next; + free(pr->pr_conf); + free(pr); + } +} + + +static void +proxy_loadconfig(fd, ioctlfunc, proxy, proto, conf, list) + int fd; + ioctlfunc_t ioctlfunc; + char *proxy, *conf; + int proto; + namelist_t *list; +{ + namelist_t *na; + ipfobj_t obj; + ap_ctl_t pcmd; + + obj.ipfo_rev = IPFILTER_VERSION; + obj.ipfo_type = IPFOBJ_PROXYCTL; + obj.ipfo_size = sizeof(pcmd); + obj.ipfo_ptr = &pcmd; + + while ((na = list) != NULL) { + if ((opts & OPT_REMOVE) != 0) + pcmd.apc_cmd = APC_CMD_DEL; + else + pcmd.apc_cmd = APC_CMD_ADD; + pcmd.apc_dsize = strlen(na->na_name) + 1; + pcmd.apc_data = na->na_name; + pcmd.apc_arg = na->na_value; + pcmd.apc_p = proto; + + strncpy(pcmd.apc_label, proxy, APR_LABELLEN); + pcmd.apc_label[APR_LABELLEN - 1] = '\0'; + + strncpy(pcmd.apc_config, conf, APR_LABELLEN); + pcmd.apc_config[APR_LABELLEN - 1] = '\0'; + + if ((*ioctlfunc)(fd, SIOCPROXY, (void *)&obj) == -1) { + if ((opts & OPT_DONOTHING) == 0) { + char msg[80]; + + sprintf(msg, "%d:ioctl(add/remove proxy rule)", + yylineNum); + ipf_perror_fd(fd, ioctlfunc, msg); + return; + } + } + + list = na->na_next; + free(na->na_name); + free(na); + } +} + + +static void +setifname(np, idx, name) + ipnat_t **np; + int idx; + char *name; +{ + int pos; + + pos = addname(np, name); + if (pos == -1) + return; + (*np)->in_ifnames[idx] = pos; +} + + +static int +addname(np, name) + ipnat_t **np; + char *name; +{ + ipnat_t *n; + int nlen; + int pos; + + nlen = strlen(name) + 1; + n = realloc(*np, (*np)->in_size + nlen); + if (*np == nattop) + nattop = n; + *np = n; + if (n == NULL) + return -1; + if (n->in_pnext != NULL) + *n->in_pnext = n; + n->in_size += nlen; + pos = n->in_namelen; + n->in_namelen += nlen; + strcpy(n->in_names + pos, name); + n->in_names[n->in_namelen] = '\0'; + return pos; } diff --git a/contrib/ipfilter/tools/ippool.c b/contrib/ipfilter/tools/ippool.c index 8b709605073..49cf7dae63d 100644 --- a/contrib/ipfilter/tools/ippool.c +++ b/contrib/ipfilter/tools/ippool.c @@ -1,7 +1,7 @@ /* $FreeBSD$ */ /* - * Copyright (C) 2002-2006 by Darren Reed. + * Copyright (C) 2012 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. */ @@ -61,41 +61,48 @@ int poolflush __P((int, char *[])); int poolstats __P((int, char *[])); int gettype __P((char *, u_int *)); int getrole __P((char *)); -int setnodeaddr __P((ip_pool_node_t *node, char *arg)); -void showpools_live __P((int, int, ip_pool_stat_t *, char *)); +int setnodeaddr __P((int, int, void *ptr, char *arg)); +void showpools_live __P((int, int, ipf_pool_stat_t *, char *)); void showhashs_live __P((int, int, iphtstat_t *, char *)); +void showdstls_live __P((int, int, ipf_dstl_stat_t *, char *)); int opts = 0; int fd = -1; int use_inet6 = 0; +wordtab_t *pool_fields = NULL; +int nohdrfields = 0; -void usage(prog) -char *prog; +void +usage(prog) + char *prog; { fprintf(stderr, "Usage:\t%s\n", prog); - fprintf(stderr, "\t\t\t-a [-dnv] [-m ] [-o ] -i [/netmask]\n"); - fprintf(stderr, "\t\t\t-A [-dnv] [-m ] [-o ] [-S ] [-t ]\n"); - fprintf(stderr, "\t\t\t-f [-dnuv]\n"); - fprintf(stderr, "\t\t\t-F [-dv] [-o ] [-t ]\n"); - fprintf(stderr, "\t\t\t-l [-dv] [-m ] [-t ]\n"); - fprintf(stderr, "\t\t\t-r [-dnv] [-m ] [-o ] -i [/netmask]\n"); - fprintf(stderr, "\t\t\t-R [-dnv] [-m ] [-o ] [-t ]\n"); - fprintf(stderr, "\t\t\t-s [-dtv] [-M ] [-N ]\n"); + fprintf(stderr, "\t-a [-dnv] [-m ] [-o ] [-t type] [-T ttl] -i [/netmask]\n"); + fprintf(stderr, "\t-A [-dnv] [-m ] [-o ] [-S ] [-t ]\n"); + fprintf(stderr, "\t-f [-dnuv]\n"); + fprintf(stderr, "\t-F [-dv] [-o ] [-t ]\n"); + fprintf(stderr, "\t-l [-dv] [-m ] [-t ] [-O ]\n"); + fprintf(stderr, "\t-r [-dnv] [-m ] [-o ] [-t type] -i [/netmask]\n"); + fprintf(stderr, "\t-R [-dnv] [-m ] [-o ] [-t ]\n"); + fprintf(stderr, "\t-s [-dtv] [-M ] [-N ]\n"); exit(1); } -int main(argc, argv) -int argc; -char *argv[]; +int +main(argc, argv) + int argc; + char *argv[]; { - int err; + int err = 1; if (argc < 2) usage(argv[0]); - switch (getopt(argc, argv, "aAf:FlrRs")) + assigndefined(getenv("IPPOOL_PREDEFINED")); + + switch (getopt(argc, argv, "aAf:FlnrRsv")) { case 'a' : err = poolnodecommand(0, argc, argv); @@ -112,6 +119,9 @@ char *argv[]; case 'l' : err = poollist(argc, argv); break; + case 'n' : + opts |= OPT_DONOTHING|OPT_DONTOPEN; + break; case 'r' : err = poolnodecommand(1, argc, argv); break; @@ -121,6 +131,9 @@ char *argv[]; case 's' : err = poolstats(argc, argv); break; + case 'v' : + opts |= OPT_VERBOSE; + break; default : exit(1); } @@ -131,19 +144,23 @@ char *argv[]; } -int poolnodecommand(remove, argc, argv) -int remove, argc; -char *argv[]; +int +poolnodecommand(remove, argc, argv) + int remove, argc; + char *argv[]; { - int err, c, ipset, role; + int err = 0, c, ipset, role, type = IPLT_POOL, ttl = 0; char *poolname = NULL; - ip_pool_node_t node; + ip_pool_node_t pnode; + iphtent_t hnode; + void *ptr = &pnode; ipset = 0; role = IPL_LOGIPF; - bzero((char *)&node, sizeof(node)); + bzero((char *)&pnode, sizeof(pnode)); + bzero((char *)&hnode, sizeof(hnode)); - while ((c = getopt(argc, argv, "di:m:no:Rv")) != -1) + while ((c = getopt(argc, argv, "di:m:no:Rt:T:v")) != -1) switch (c) { case 'd' : @@ -151,16 +168,21 @@ char *argv[]; ippool_yydebug++; break; case 'i' : - if (setnodeaddr(&node, optarg) == 0) + if (setnodeaddr(type, role, ptr, optarg) == 0) ipset = 1; break; case 'm' : poolname = optarg; break; case 'n' : - opts |= OPT_DONOTHING; + opts |= OPT_DONOTHING|OPT_DONTOPEN; break; case 'o' : + if (ipset == 1) { + fprintf(stderr, + "cannot set role after ip address\n"); + return -1; + } role = getrole(optarg); if (role == IPL_LOGNONE) return -1; @@ -168,13 +190,39 @@ char *argv[]; case 'R' : opts |= OPT_NORESOLVE; break; + case 't' : + if (ipset == 1) { + fprintf(stderr, + "cannot set type after ip address\n"); + return -1; + } + type = gettype(optarg, NULL); + switch (type) { + case IPLT_NONE : + fprintf(stderr, "unknown type '%s'\n", optarg); + return -1; + case IPLT_HASH : + ptr = &hnode; + break; + case IPLT_POOL : + default : + break; + } + break; + case 'T' : + ttl = atoi(optarg); + if (ttl < 0) { + fprintf(stderr, "cannot set negative ttl\n"); + return -1; + } + break; case 'v' : opts |= OPT_VERBOSE; break; } if (argv[optind] != NULL && ipset == 0) { - if (setnodeaddr(&node, argv[optind]) == 0) + if (setnodeaddr(type, role, ptr, argv[optind]) == 0) ipset = 1; } @@ -191,17 +239,30 @@ char *argv[]; return -1; } - if (remove == 0) - err = load_poolnode(0, poolname, &node, ioctl); - else - err = remove_poolnode(0, poolname, &node, ioctl); + switch (type) { + case IPLT_POOL : + if (remove == 0) + err = load_poolnode(role, poolname, &pnode, ttl, ioctl); + else + err = remove_poolnode(role, poolname, &pnode, ioctl); + break; + case IPLT_HASH : + if (remove == 0) + err = load_hashnode(role, poolname, &hnode, ttl, ioctl); + else + err = remove_hashnode(role, poolname, &hnode, ioctl); + break; + default : + break; + } return err; } -int poolcommand(remove, argc, argv) -int remove, argc; -char *argv[]; +int +poolcommand(remove, argc, argv) + int remove, argc; + char *argv[]; { int type, role, c, err; char *poolname; @@ -216,7 +277,7 @@ char *argv[]; bzero((char *)&iph, sizeof(iph)); bzero((char *)&pool, sizeof(pool)); - while ((c = getopt(argc, argv, "dm:no:RSt:v")) != -1) + while ((c = getopt(argc, argv, "dm:no:RSv")) != -1) switch (c) { case 'd' : @@ -227,7 +288,7 @@ char *argv[]; poolname = optarg; break; case 'n' : - opts |= OPT_DONOTHING; + opts |= OPT_DONOTHING|OPT_DONTOPEN; break; case 'o' : role = getrole(optarg); @@ -242,13 +303,6 @@ char *argv[]; case 'S' : iph.iph_seed = atoi(optarg); break; - case 't' : - type = gettype(optarg, &iph.iph_type); - if (type == IPLT_NONE) { - fprintf(stderr, "unknown type '%s'\n", optarg); - return -1; - } - break; case 'v' : opts |= OPT_VERBOSE; break; @@ -262,6 +316,12 @@ char *argv[]; return -1; } + type = gettype(argv[optind], &iph.iph_type); + if (type == IPLT_NONE) { + fprintf(stderr, "unknown type '%s'\n", argv[optind]); + return -1; + } + if (type == IPLT_HASH) { strncpy(iph.iph_name, poolname, sizeof(iph.iph_name)); iph.iph_name[sizeof(iph.iph_name) - 1] = '\0'; @@ -297,9 +357,10 @@ char *argv[]; } -int loadpoolfile(argc, argv, infile) -int argc; -char *argv[], *infile; +int +loadpoolfile(argc, argv, infile) + int argc; + char *argv[], *infile; { int c; @@ -313,7 +374,7 @@ char *argv[], *infile; ippool_yydebug++; break; case 'n' : - opts |= OPT_DONOTHING; + opts |= OPT_DONOTHING|OPT_DONTOPEN; break; case 'R' : opts |= OPT_NORESOLVE; @@ -329,7 +390,7 @@ char *argv[], *infile; if (opts & OPT_DEBUG) fprintf(stderr, "loadpoolfile: opts = %#x\n", opts); - if (!(opts & OPT_DONOTHING) && (fd == -1)) { + if (!(opts & (OPT_DONOTHING|OPT_DONTOPEN)) && (fd == -1)) { fd = open(IPLOOKUP_NAME, O_RDWR); if (fd == -1) { perror("open(IPLOOKUP_NAME)"); @@ -343,12 +404,14 @@ char *argv[], *infile; } -int poolstats(argc, argv) -int argc; -char *argv[]; +int +poolstats(argc, argv) + int argc; + char *argv[]; { int c, type, role, live_kernel; - ip_pool_stat_t plstat; + ipf_pool_stat_t plstat; + ipf_dstl_stat_t dlstat; char *kernel, *core; iphtstat_t htstat; iplookupop_t op; @@ -398,7 +461,7 @@ char *argv[]; if (opts & OPT_DEBUG) fprintf(stderr, "poolstats: opts = %#x\n", opts); - if (!(opts & OPT_DONOTHING) && (fd == -1)) { + if (!(opts & (OPT_DONOTHING|OPT_DONTOPEN)) && (fd == -1)) { fd = open(IPLOOKUP_NAME, O_RDWR); if (fd == -1) { perror("open(IPLOOKUP_NAME)"); @@ -410,14 +473,14 @@ char *argv[]; op.iplo_type = IPLT_POOL; op.iplo_struct = &plstat; op.iplo_size = sizeof(plstat); - if (!(opts & OPT_DONOTHING)) { + if (!(opts & (OPT_DONOTHING|OPT_DONTOPEN))) { c = ioctl(fd, SIOCLOOKUPSTAT, &op); if (c == -1) { - perror("ioctl(SIOCLOOKUPSTAT)"); + ipferror(fd, "ioctl(S0IOCLOOKUPSTAT)"); return -1; } - printf("Pools:\t%lu\n", plstat.ipls_pools); - printf("Nodes:\t%lu\n", plstat.ipls_nodes); + printf("%lu\taddress pools\n", plstat.ipls_pools); + printf("%lu\taddress pool nodes\n", plstat.ipls_nodes); } } @@ -425,24 +488,49 @@ char *argv[]; op.iplo_type = IPLT_HASH; op.iplo_struct = &htstat; op.iplo_size = sizeof(htstat); - if (!(opts & OPT_DONOTHING)) { + if (!(opts & (OPT_DONOTHING|OPT_DONTOPEN))) { c = ioctl(fd, SIOCLOOKUPSTAT, &op); if (c == -1) { - perror("ioctl(SIOCLOOKUPSTAT)"); + ipferror(fd, "ioctl(SIOCLOOKUPSTAT)"); return -1; } - printf("Hash Tables:\t%lu\n", htstat.iphs_numtables); - printf("Nodes:\t%lu\n", htstat.iphs_numnodes); - printf("Out of Memory:\t%lu\n", htstat.iphs_nomem); + printf("%lu\thash tables\n", htstat.iphs_numtables); + printf("%lu\thash table nodes\n", htstat.iphs_numnodes); + printf("%lu\thash table no memory \n", + htstat.iphs_nomem); + } + } + + if (type == IPLT_ALL || type == IPLT_DSTLIST) { + op.iplo_type = IPLT_DSTLIST; + op.iplo_struct = &dlstat; + op.iplo_size = sizeof(dlstat); + if (!(opts & (OPT_DONOTHING|OPT_DONTOPEN))) { + c = ioctl(fd, SIOCLOOKUPSTAT, &op); + if (c == -1) { + ipferror(fd, "ioctl(SIOCLOOKUPSTAT)"); + return -1; + } + printf("%u\tdestination lists\n", + dlstat.ipls_numlists); + printf("%u\tdestination list nodes\n", + dlstat.ipls_numnodes); + printf("%lu\tdestination list no memory\n", + dlstat.ipls_nomem); + printf("%u\tdestination list zombies\n", + dlstat.ipls_numdereflists); + printf("%u\tdesetination list node zombies\n", + dlstat.ipls_numderefnodes); } } return 0; } -int poolflush(argc, argv) -int argc; -char *argv[]; +int +poolflush(argc, argv) + int argc; + char *argv[]; { int c, role, type, arg; iplookupflush_t flush; @@ -479,7 +567,7 @@ char *argv[]; if (opts & OPT_DEBUG) fprintf(stderr, "poolflush: opts = %#x\n", opts); - if (!(opts & OPT_DONOTHING) && (fd == -1)) { + if (!(opts & (OPT_DONOTHING|OPT_DONTOPEN)) && (fd == -1)) { fd = open(IPLOOKUP_NAME, O_RDWR); if (fd == -1) { perror("open(IPLOOKUP_NAME)"); @@ -492,22 +580,23 @@ char *argv[]; flush.iplf_unit = role; flush.iplf_arg = arg; - if (!(opts & OPT_DONOTHING)) { + if (!(opts & (OPT_DONOTHING|OPT_DONTOPEN))) { if (ioctl(fd, SIOCLOOKUPFLUSH, &flush) == -1) { - perror("ioctl(SIOCLOOKUPFLUSH)"); + ipferror(fd, "ioctl(SIOCLOOKUPFLUSH)"); exit(1); } } - printf("%zd object%s flushed\n", flush.iplf_count, + printf("%u object%s flushed\n", flush.iplf_count, (flush.iplf_count == 1) ? "" : "s"); return 0; } -int getrole(rolename) -char *rolename; +int +getrole(rolename) + char *rolename; { int role; @@ -537,19 +626,20 @@ char *rolename; } -int gettype(typename, minor) -char *typename; -u_int *minor; +int +gettype(typename, minor) + char *typename; + u_int *minor; { int type; - if (!strcasecmp(optarg, "tree") || !strcasecmp(optarg, "pool")) { + if (!strcasecmp(typename, "tree") || !strcasecmp(typename, "pool")) { type = IPLT_POOL; - } else if (!strcasecmp(optarg, "hash")) { + } else if (!strcasecmp(typename, "hash")) { type = IPLT_HASH; if (minor != NULL) *minor = IPHASH_LOOKUP; - } else if (!strcasecmp(optarg, "group-map")) { + } else if (!strcasecmp(typename, "group-map")) { type = IPLT_HASH; if (minor != NULL) *minor = IPHASH_GROUPMAP; @@ -560,9 +650,10 @@ u_int *minor; } -int poollist(argc, argv) -int argc; -char *argv[]; +int +poollist(argc, argv) + int argc; + char *argv[]; { char *kernel, *core, *poolname; int c, role, type, live_kernel; @@ -599,6 +690,9 @@ char *argv[]; return -1; } break; + case 'O' : + pool_fields = parsefields(poolfields, optarg); + break; case 'R' : opts |= OPT_NORESOLVE; break; @@ -617,7 +711,7 @@ char *argv[]; if (opts & OPT_DEBUG) fprintf(stderr, "poollist: opts = %#x\n", opts); - if (!(opts & OPT_DONOTHING) && (fd == -1)) { + if (!(opts & (OPT_DONOTHING|OPT_DONTOPEN)) && (fd == -1)) { fd = open(IPLOOKUP_NAME, O_RDWR); if (fd == -1) { perror("open(IPLOOKUP_NAME)"); @@ -640,9 +734,10 @@ char *argv[]; } -void poollist_dead(role, poolname, type, kernel, core) -int role, type; -char *poolname, *kernel, *core; +void +poollist_dead(role, poolname, type, kernel, core) + int role, type; + char *poolname, *kernel, *core; { iphtable_t *hptr; ip_pool_t *ptr; @@ -665,14 +760,15 @@ char *poolname, *kernel, *core; ptr = pools[role]; while (ptr != NULL) { ptr = printpool(ptr, kmemcpywrap, poolname, - opts); + opts, pool_fields); } } else { for (role = 0; role <= IPL_LOGMAX; role++) { ptr = pools[role]; while (ptr != NULL) { ptr = printpool(ptr, kmemcpywrap, - poolname, opts); + poolname, opts, + pool_fields); } } role = IPL_LOGALL; @@ -693,14 +789,15 @@ char *poolname, *kernel, *core; hptr = tables[role]; while (hptr != NULL) { hptr = printhash(hptr, kmemcpywrap, - poolname, opts); + poolname, opts, pool_fields); } } else { for (role = 0; role <= IPL_LOGMAX; role++) { hptr = tables[role]; while (hptr != NULL) { hptr = printhash(hptr, kmemcpywrap, - poolname, opts); + poolname, opts, + pool_fields); } } } @@ -708,12 +805,12 @@ char *poolname, *kernel, *core; } -void poollist_live(role, poolname, type, fd) -int role, type, fd; -char *poolname; +void +poollist_live(role, poolname, type, fd) + int role, type, fd; + char *poolname; { - ip_pool_stat_t plstat; - iphtstat_t htstat; + ipf_pool_stat_t plstat; iplookupop_t op; int c; @@ -729,18 +826,18 @@ char *poolname; c = ioctl(fd, SIOCLOOKUPSTAT, &op); if (c == -1) { - perror("ioctl(SIOCLOOKUPSTAT)"); + ipferror(fd, "ioctl(SIOCLOOKUPSTAT)"); return; } showpools_live(fd, role, &plstat, poolname); } else { - for (role = 0; role <= IPL_LOGMAX; role++) { + for (role = -1; role <= IPL_LOGMAX; role++) { op.iplo_unit = role; c = ioctl(fd, SIOCLOOKUPSTAT, &op); if (c == -1) { - perror("ioctl(SIOCLOOKUPSTAT)"); + ipferror(fd, "ioctl(SIOCLOOKUPSTAT)"); return; } @@ -752,6 +849,8 @@ char *poolname; } if (type == IPLT_ALL || type == IPLT_HASH) { + iphtstat_t htstat; + op.iplo_type = IPLT_HASH; op.iplo_size = sizeof(htstat); op.iplo_struct = &htstat; @@ -763,7 +862,7 @@ char *poolname; c = ioctl(fd, SIOCLOOKUPSTAT, &op); if (c == -1) { - perror("ioctl(SIOCLOOKUPSTAT)"); + ipferror(fd, "ioctl(SIOCLOOKUPSTAT)"); return; } showhashs_live(fd, role, &htstat, poolname); @@ -773,21 +872,57 @@ char *poolname; op.iplo_unit = role; c = ioctl(fd, SIOCLOOKUPSTAT, &op); if (c == -1) { - perror("ioctl(SIOCLOOKUPSTAT)"); + ipferror(fd, "ioctl(SIOCLOOKUPSTAT)"); return; } showhashs_live(fd, role, &htstat, poolname); } + role = IPL_LOGALL; + } + } + + if (type == IPLT_ALL || type == IPLT_DSTLIST) { + ipf_dstl_stat_t dlstat; + + op.iplo_type = IPLT_DSTLIST; + op.iplo_size = sizeof(dlstat); + op.iplo_struct = &dlstat; + op.iplo_name[0] = '\0'; + op.iplo_arg = 0; + + if (role != IPL_LOGALL) { + op.iplo_unit = role; + + c = ioctl(fd, SIOCLOOKUPSTAT, &op); + if (c == -1) { + ipferror(fd, "ioctl(SIOCLOOKUPSTAT)"); + return; + } + showdstls_live(fd, role, &dlstat, poolname); + } else { + for (role = 0; role <= IPL_LOGMAX; role++) { + + op.iplo_unit = role; + c = ioctl(fd, SIOCLOOKUPSTAT, &op); + if (c == -1) { + ipferror(fd, "ioctl(SIOCLOOKUPSTAT)"); + return; + } + + showdstls_live(fd, role, &dlstat, poolname); + } + role = IPL_LOGALL; } } } -void showpools_live(fd, role, plstp, poolname) -int fd, role; -ip_pool_stat_t *plstp; -char *poolname; +void +showpools_live(fd, role, plstp, poolname) + int fd, role; + ipf_pool_stat_t *plstp; + char *poolname; { ipflookupiter_t iter; ip_pool_t pool; @@ -806,22 +941,27 @@ char *poolname; iter.ili_unit = role; *iter.ili_name = '\0'; - while (plstp->ipls_list[role] != NULL) { + bzero((char *)&pool, sizeof(pool)); + + while (plstp->ipls_list[role + 1] != NULL) { if (ioctl(fd, SIOCLOOKUPITER, &obj)) { - perror("ioctl(SIOCLOOKUPITER)"); + ipferror(fd, "ioctl(SIOCLOOKUPITER)"); break; } - printpool_live(&pool, fd, poolname, opts); + if (((pool.ipo_flags & IPOOL_DELETE) == 0) || + ((opts & OPT_DEBUG) != 0)) + printpool_live(&pool, fd, poolname, opts, pool_fields); - plstp->ipls_list[role] = pool.ipo_next; + plstp->ipls_list[role + 1] = pool.ipo_next; } } -void showhashs_live(fd, role, htstp, poolname) -int fd, role; -iphtstat_t *htstp; -char *poolname; +void +showhashs_live(fd, role, htstp, poolname) + int fd, role; + iphtstat_t *htstp; + char *poolname; { ipflookupiter_t iter; iphtable_t table; @@ -842,18 +982,55 @@ char *poolname; while (htstp->iphs_tables != NULL) { if (ioctl(fd, SIOCLOOKUPITER, &obj)) { - perror("ioctl(SIOCLOOKUPITER)"); + ipferror(fd, "ioctl(SIOCLOOKUPITER)"); break; } - printhash_live(&table, fd, poolname, opts); + printhash_live(&table, fd, poolname, opts, pool_fields); htstp->iphs_tables = table.iph_next; } } -int setnodeaddr(ip_pool_node_t *node, char *arg) +void +showdstls_live(fd, role, dlstp, poolname) + int fd, role; + ipf_dstl_stat_t *dlstp; + char *poolname; +{ + ipflookupiter_t iter; + ippool_dst_t table; + ipfobj_t obj; + + obj.ipfo_rev = IPFILTER_VERSION; + obj.ipfo_type = IPFOBJ_LOOKUPITER; + obj.ipfo_size = sizeof(iter); + obj.ipfo_ptr = &iter; + + iter.ili_type = IPLT_DSTLIST; + iter.ili_otype = IPFLOOKUPITER_LIST; + iter.ili_ival = IPFGENITER_LOOKUP; + iter.ili_nitems = 1; + iter.ili_data = &table; + iter.ili_unit = role; + *iter.ili_name = '\0'; + + while (dlstp->ipls_list[role] != NULL) { + if (ioctl(fd, SIOCLOOKUPITER, &obj)) { + ipferror(fd, "ioctl(SIOCLOOKUPITER)"); + break; + } + + printdstl_live(&table, fd, poolname, opts, pool_fields); + + dlstp->ipls_list[role] = table.ipld_next; + } +} + + +int +setnodeaddr(int type, int role, void *ptr, char *arg) { struct in_addr mask; char *s; @@ -862,17 +1039,38 @@ int setnodeaddr(ip_pool_node_t *node, char *arg) if (s == NULL) mask.s_addr = 0xffffffff; else if (strchr(s, '.') == NULL) { - if (ntomask(4, atoi(s + 1), &mask.s_addr) != 0) + if (ntomask(AF_INET, atoi(s + 1), &mask.s_addr) != 0) return -1; } else { mask.s_addr = inet_addr(s + 1); } if (s != NULL) *s = '\0'; - node->ipn_addr.adf_len = sizeof(node->ipn_addr); - node->ipn_addr.adf_addr.in4.s_addr = inet_addr(arg); - node->ipn_mask.adf_len = sizeof(node->ipn_mask); - node->ipn_mask.adf_addr.in4.s_addr = mask.s_addr; + + if (type == IPLT_POOL) { + ip_pool_node_t *node = ptr; + + if (node->ipn_addr.adf_family == AF_INET) + node->ipn_addr.adf_len = offsetof(addrfamily_t, + adf_addr) + + sizeof(struct in_addr); +#ifdef USE_INET6 + else + node->ipn_addr.adf_len = offsetof(addrfamily_t, + adf_addr) + + sizeof(struct in6_addr); +#endif + node->ipn_addr.adf_addr.in4.s_addr = inet_addr(arg); + node->ipn_mask.adf_len = node->ipn_addr.adf_len; + node->ipn_mask.adf_addr.in4.s_addr = mask.s_addr; + } else if (type == IPLT_HASH) { + iphtent_t *node = ptr; + + node->ipe_addr.in4.s_addr = inet_addr(arg); + node->ipe_mask.in4.s_addr = mask.s_addr; + node->ipe_family = AF_INET; + node->ipe_unit = role; + } return 0; } diff --git a/contrib/ipfilter/tools/ippool_y.y b/contrib/ipfilter/tools/ippool_y.y index 24f683bd1c1..34987452c65 100644 --- a/contrib/ipfilter/tools/ippool_y.y +++ b/contrib/ipfilter/tools/ippool_y.y @@ -1,7 +1,7 @@ /* $FreeBSD$ */ /* - * Copyright (C) 2001-2006 by Darren Reed. + * Copyright (C) 2012 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. */ @@ -35,6 +35,7 @@ #include "netinet/ip_lookup.h" #include "netinet/ip_pool.h" #include "netinet/ip_htable.h" +#include "netinet/ip_dstlist.h" #include "ippool_l.h" #include "kmem.h" @@ -48,43 +49,52 @@ extern FILE *yyin; static iphtable_t ipht; static iphtent_t iphte; static ip_pool_t iplo; +static ippool_dst_t ipld; static ioctlfunc_t poolioctl = NULL; static char poolname[FR_GROUPLEN]; static iphtent_t *add_htablehosts __P((char *)); static ip_pool_node_t *add_poolhosts __P((char *)); +static ip_pool_node_t *read_whoisfile __P((char *)); +static void setadflen __P((addrfamily_t *)); %} %union { char *str; u_32_t num; - struct in_addr addr; + struct in_addr ip4; struct alist_s *alist; - struct in_addr adrmsk[2]; + addrfamily_t adrmsk[2]; iphtent_t *ipe; ip_pool_node_t *ipp; - union i6addr ip6; + ipf_dstnode_t *ipd; + addrfamily_t ipa; + i6addr_t ip6; } -%token YY_NUMBER YY_HEX -%token YY_STR -%token YY_COMMENT -%token YY_CMP_EQ YY_CMP_NE YY_CMP_LE YY_CMP_GE YY_CMP_LT YY_CMP_GT -%token YY_RANGE_OUT YY_RANGE_IN -%token YY_IPV6 - -%token IPT_IPF IPT_NAT IPT_COUNT IPT_AUTH IPT_IN IPT_OUT -%token IPT_TABLE IPT_GROUPMAP IPT_HASH +%token YY_NUMBER YY_HEX +%token YY_STR +%token YY_IPV6 +%token YY_COMMENT +%token YY_CMP_EQ YY_CMP_NE YY_CMP_LE YY_CMP_GE YY_CMP_LT YY_CMP_GT +%token YY_RANGE_OUT YY_RANGE_IN +%token IPT_IPF IPT_NAT IPT_COUNT IPT_AUTH IPT_IN IPT_OUT IPT_ALL +%token IPT_TABLE IPT_GROUPMAP IPT_HASH IPT_SRCHASH IPT_DSTHASH %token IPT_ROLE IPT_TYPE IPT_TREE -%token IPT_GROUP IPT_SIZE IPT_SEED IPT_NUM IPT_NAME -%type role table inout +%token IPT_GROUP IPT_SIZE IPT_SEED IPT_NUM IPT_NAME IPT_POLICY +%token IPT_POOL IPT_DSTLIST IPT_ROUNDROBIN +%token IPT_WEIGHTED IPT_RANDOM IPT_CONNECTION +%token IPT_WHOIS IPT_FILE +%type role table inout unit dstopts weighting %type ipftree range addrlist %type addrmask %type ipfgroup ipfhash hashlist hashentry %type groupentry setgrouplist grouplist -%type ipaddr mask ipv4 -%type number setgroup +%type ipaddr mask +%type ipv4 +%type number setgroup name +%type dstentry dstentries dstlist %% file: line @@ -93,25 +103,44 @@ file: line | file assign ; -line: table role ipftree eol { iplo.ipo_unit = $2; +line: table role ipftree eol { ip_pool_node_t *n; + iplo.ipo_unit = $2; iplo.ipo_list = $3; load_pool(&iplo, poolioctl); + while ((n = $3) != NULL) { + $3 = n->ipn_next; + free(n); + } resetlexer(); + use_inet6 = 0; } - | table role ipfhash eol { ipht.iph_unit = $2; + | table role ipfhash eol { iphtent_t *h; + ipht.iph_unit = $2; ipht.iph_type = IPHASH_LOOKUP; load_hash(&ipht, $3, poolioctl); + while ((h = $3) != NULL) { + $3 = h->ipe_next; + free(h); + } resetlexer(); + use_inet6 = 0; } | groupmap role number ipfgroup eol - { ipht.iph_unit = $2; + { iphtent_t *h; + ipht.iph_unit = $2; strncpy(ipht.iph_name, $3, sizeof(ipht.iph_name)); ipht.iph_type = IPHASH_GROUPMAP; load_hash(&ipht, $4, poolioctl); + while ((h = $4) != NULL) { + $4 = h->ipe_next; + free(h); + } resetlexer(); + use_inet6 = 0; } | YY_COMMENT + | poolline eol ; eol: ';' @@ -132,6 +161,7 @@ assigning: table: IPT_TABLE { bzero((char *)&ipht, sizeof(ipht)); bzero((char *)&iphte, sizeof(iphte)); bzero((char *)&iplo, sizeof(iplo)); + bzero((char *)&ipld, sizeof(ipld)); *ipht.iph_name = '\0'; iplo.ipo_flags = IPHASH_ANON; iplo.ipo_name[0] = '\0'; @@ -150,11 +180,15 @@ groupmap: inout: IPT_IN { $$ = FR_INQUE; } | IPT_OUT { $$ = FR_OUTQUE; } ; -role: - IPT_ROLE '=' IPT_IPF { $$ = IPL_LOGIPF; } - | IPT_ROLE '=' IPT_NAT { $$ = IPL_LOGNAT; } - | IPT_ROLE '=' IPT_AUTH { $$ = IPL_LOGAUTH; } - | IPT_ROLE '=' IPT_COUNT { $$ = IPL_LOGCOUNT; } + +role: IPT_ROLE '=' unit { $$ = $3; } + ; + +unit: IPT_IPF { $$ = IPL_LOGIPF; } + | IPT_NAT { $$ = IPL_LOGNAT; } + | IPT_AUTH { $$ = IPL_LOGAUTH; } + | IPT_COUNT { $$ = IPL_LOGCOUNT; } + | IPT_ALL { $$ = IPL_LOGALL; } ; ipftree: @@ -183,14 +217,21 @@ ipfgroup: $1, FR_GROUPLEN); $$ = $4; + free($1); } - | hashopts start setgrouplist end { $$ = $3; } + | hashopts start setgrouplist end + { $$ = $3; } ; number: IPT_NUM '=' YY_NUMBER { sprintf(poolname, "%u", $3); $$ = poolname; } - | IPT_NAME '=' YY_STR { $$ = $3; } + | IPT_NAME '=' YY_STR { strncpy(poolname, $3, + FR_GROUPLEN); + poolname[FR_GROUPLEN-1]='\0'; + free($3); + $$ = poolname; + } | { $$ = ""; } ; @@ -198,6 +239,7 @@ setgroup: IPT_GROUP '=' YY_STR { char tmp[FR_GROUPLEN+1]; strncpy(tmp, $3, FR_GROUPLEN); $$ = strdup(tmp); + free($3); } | IPT_GROUP '=' YY_NUMBER { char tmp[FR_GROUPLEN+1]; sprintf(tmp, "%u", $3); @@ -212,119 +254,162 @@ hashopts: ; addrlist: - next { $$ = NULL; } - | range next addrlist { $1->ipn_next = $3; $$ = $1; } + ';' { $$ = NULL; } + | range next addrlist { $$ = $1; + while ($1->ipn_next != NULL) + $1 = $1->ipn_next; + $1->ipn_next = $3; + } | range next { $$ = $1; } ; grouplist: - next { $$ = NULL; } + ';' { $$ = NULL; } | groupentry next grouplist { $$ = $1; $1->ipe_next = $3; } | addrmask next grouplist { $$ = calloc(1, sizeof(iphtent_t)); - bcopy((char *)&($1[0]), - (char *)&($$->ipe_addr), - sizeof($$->ipe_addr)); - bcopy((char *)&($1[1]), - (char *)&($$->ipe_mask), - sizeof($$->ipe_mask)); + $$->ipe_addr = $1[0].adf_addr; + $$->ipe_mask = $1[1].adf_addr; + $$->ipe_family = $1[0].adf_family; $$->ipe_next = $3; } | groupentry next { $$ = $1; } | addrmask next { $$ = calloc(1, sizeof(iphtent_t)); - bcopy((char *)&($1[0]), - (char *)&($$->ipe_addr), - sizeof($$->ipe_addr)); - bcopy((char *)&($1[1]), - (char *)&($$->ipe_mask), - sizeof($$->ipe_mask)); + $$->ipe_addr = $1[0].adf_addr; + $$->ipe_mask = $1[1].adf_addr; +#ifdef AF_INET6 + if (use_inet6) + $$->ipe_family = AF_INET6; + else +#endif + $$->ipe_family = AF_INET; + } + | YY_STR { $$ = add_htablehosts($1); + free($1); } ; setgrouplist: - next { $$ = NULL; } + ';' { $$ = NULL; } | groupentry next { $$ = $1; } | groupentry next setgrouplist { $1->ipe_next = $3; $$ = $1; } ; groupentry: addrmask ',' setgroup { $$ = calloc(1, sizeof(iphtent_t)); - bcopy((char *)&($1[0]), - (char *)&($$->ipe_addr), - sizeof($$->ipe_addr)); - bcopy((char *)&($1[1]), - (char *)&($$->ipe_mask), - sizeof($$->ipe_mask)); + $$->ipe_addr = $1[0].adf_addr; + $$->ipe_mask = $1[1].adf_addr; strncpy($$->ipe_group, $3, FR_GROUPLEN); +#ifdef AF_INET6 + if (use_inet6) + $$->ipe_family = AF_INET6; + else +#endif + $$->ipe_family = AF_INET; free($3); } - | YY_STR { $$ = add_htablehosts($1); } ; -range: addrmask { $$ = calloc(1, sizeof(*$$)); - $$->ipn_info = 0; - $$->ipn_addr.adf_len = sizeof($$->ipn_addr); - $$->ipn_addr.adf_addr.in4.s_addr = $1[0].s_addr; - $$->ipn_mask.adf_len = sizeof($$->ipn_mask); - $$->ipn_mask.adf_addr.in4.s_addr = $1[1].s_addr; - } - | '!' addrmask { $$ = calloc(1, sizeof(*$$)); - $$->ipn_info = 1; - $$->ipn_addr.adf_len = sizeof($$->ipn_addr); - $$->ipn_addr.adf_addr.in4.s_addr = $2[0].s_addr; - $$->ipn_mask.adf_len = sizeof($$->ipn_mask); - $$->ipn_mask.adf_addr.in4.s_addr = $2[1].s_addr; - } - | YY_STR { $$ = add_poolhosts($1); } +range: addrmask { $$ = calloc(1, sizeof(*$$)); + $$->ipn_info = 0; + $$->ipn_addr = $1[0]; + $$->ipn_mask = $1[1]; + } + | '!' addrmask { $$ = calloc(1, sizeof(*$$)); + $$->ipn_info = 1; + $$->ipn_addr = $2[0]; + $$->ipn_mask = $2[1]; + } + | YY_STR { $$ = add_poolhosts($1); + free($1); + } + | IPT_WHOIS IPT_FILE YY_STR { $$ = read_whoisfile($3); + free($3); + } + ; hashlist: - next { $$ = NULL; } + ';' { $$ = NULL; } | hashentry next { $$ = $1; } | hashentry next hashlist { $1->ipe_next = $3; $$ = $1; } ; hashentry: - addrmask { $$ = calloc(1, sizeof(iphtent_t)); - bcopy((char *)&($1[0]), - (char *)&($$->ipe_addr), - sizeof($$->ipe_addr)); - bcopy((char *)&($1[1]), - (char *)&($$->ipe_mask), - sizeof($$->ipe_mask)); - } - | YY_STR { $$ = add_htablehosts($1); } + addrmask { $$ = calloc(1, sizeof(iphtent_t)); + $$->ipe_addr = $1[0].adf_addr; + $$->ipe_mask = $1[1].adf_addr; +#ifdef USE_INET6 + if (use_inet6) + $$->ipe_family = AF_INET6; + else +#endif + $$->ipe_family = AF_INET; + } + | YY_STR { $$ = add_htablehosts($1); + free($1); + } ; addrmask: - ipaddr '/' mask { $$[0] = $1; $$[1].s_addr = $3.s_addr; - yyexpectaddr = 0; + ipaddr '/' mask { $$[0] = $1; + setadflen(&$$[0]); + $$[1] = $3; + $$[1].adf_len = $$[0].adf_len; } - | ipaddr { $$[0] = $1; $$[1].s_addr = 0xffffffff; - yyexpectaddr = 0; + | ipaddr { $$[0] = $1; + setadflen(&$$[1]); + $$[1].adf_len = $$[0].adf_len; +#ifdef USE_INET6 + if (use_inet6) + memset(&$$[1].adf_addr, 0xff, + sizeof($$[1].adf_addr.in6)); + else +#endif + memset(&$$[1].adf_addr, 0xff, + sizeof($$[1].adf_addr.in4)); } ; -ipaddr: ipv4 { $$ = $1; } - | YY_NUMBER { $$.s_addr = htonl($1); } +ipaddr: ipv4 { $$.adf_addr.in4 = $1; + $$.adf_family = AF_INET; + setadflen(&$$); + use_inet6 = 0; + } + | YY_NUMBER { $$.adf_addr.in4.s_addr = htonl($1); + $$.adf_family = AF_INET; + setadflen(&$$); + use_inet6 = 0; + } + | YY_IPV6 { $$.adf_addr = $1; + $$.adf_family = AF_INET6; + setadflen(&$$); + use_inet6 = 1; + } ; -mask: YY_NUMBER { ntomask(4, $1, (u_32_t *)&$$.s_addr); } - | ipv4 { $$ = $1; } +mask: YY_NUMBER { bzero(&$$, sizeof($$)); + if (use_inet6) { + if (ntomask(AF_INET6, $1, + (u_32_t *)&$$.adf_addr) == -1) + yyerror("bad bitmask"); + } else { + if (ntomask(AF_INET, $1, + (u_32_t *)&$$.adf_addr.in4) == -1) + yyerror("bad bitmask"); + } + } + | ipv4 { bzero(&$$, sizeof($$)); + $$.adf_addr.in4 = $1; + } + | YY_IPV6 { bzero(&$$, sizeof($$)); + $$.adf_addr = $1; + } ; -start: '{' { yyexpectaddr = 1; } +size: IPT_SIZE '=' YY_NUMBER { ipht.iph_size = $3; } ; -end: '}' { yyexpectaddr = 0; } - ; - -next: ';' { yyexpectaddr = 1; } - ; - -size: IPT_SIZE '=' YY_NUMBER { ipht.iph_size = $3; } - ; - -seed: IPT_SEED '=' YY_NUMBER { ipht.iph_seed = $3; } +seed: IPT_SEED '=' YY_NUMBER { ipht.iph_seed = $3; } ; ipv4: YY_NUMBER '.' YY_NUMBER '.' YY_NUMBER '.' YY_NUMBER @@ -336,26 +421,180 @@ ipv4: YY_NUMBER '.' YY_NUMBER '.' YY_NUMBER '.' YY_NUMBER $$.s_addr = htonl($$.s_addr); } ; + +next: ';' { yyexpectaddr = 1; } + ; + +start: '{' { yyexpectaddr = 1; } + ; + +end: '}' { yyexpectaddr = 0; } + ; + +poolline: + IPT_POOL unit '/' IPT_DSTLIST '(' name ';' dstopts ')' + start dstlist end + { bzero((char *)&ipld, sizeof(ipld)); + strncpy(ipld.ipld_name, $6, + sizeof(ipld.ipld_name)); + ipld.ipld_unit = $2; + ipld.ipld_policy = $8; + load_dstlist(&ipld, poolioctl, $11); + resetlexer(); + use_inet6 = 0; + free($6); + } + | IPT_POOL unit '/' IPT_TREE '(' name ';' ')' + start addrlist end + { bzero((char *)&iplo, sizeof(iplo)); + strncpy(iplo.ipo_name, $6, + sizeof(iplo.ipo_name)); + iplo.ipo_list = $10; + iplo.ipo_unit = $2; + load_pool(&iplo, poolioctl); + resetlexer(); + use_inet6 = 0; + free($6); + } + | IPT_POOL '(' name ';' ')' start addrlist end + { bzero((char *)&iplo, sizeof(iplo)); + strncpy(iplo.ipo_name, $3, + sizeof(iplo.ipo_name)); + iplo.ipo_list = $7; + iplo.ipo_unit = IPL_LOGALL; + load_pool(&iplo, poolioctl); + resetlexer(); + use_inet6 = 0; + free($3); + } + | IPT_POOL unit '/' IPT_HASH '(' name ';' hashoptlist ')' + start hashlist end + { iphtent_t *h; + bzero((char *)&ipht, sizeof(ipht)); + strncpy(ipht.iph_name, $6, + sizeof(ipht.iph_name)); + ipht.iph_unit = $2; + load_hash(&ipht, $11, poolioctl); + while ((h = ipht.iph_list) != NULL) { + ipht.iph_list = h->ipe_next; + free(h); + } + resetlexer(); + use_inet6 = 0; + free($6); + } + | IPT_GROUPMAP '(' name ';' inout ';' ')' + start setgrouplist end + { iphtent_t *h; + bzero((char *)&ipht, sizeof(ipht)); + strncpy(ipht.iph_name, $3, + sizeof(ipht.iph_name)); + ipht.iph_type = IPHASH_GROUPMAP; + ipht.iph_unit = IPL_LOGIPF; + ipht.iph_flags = $5; + load_hash(&ipht, $9, poolioctl); + while ((h = ipht.iph_list) != NULL) { + ipht.iph_list = h->ipe_next; + free(h); + } + resetlexer(); + use_inet6 = 0; + free($3); + } + ; + +name: IPT_NAME YY_STR { $$ = $2; } + | IPT_NUM YY_NUMBER { char name[80]; + sprintf(name, "%d", $2); + $$ = strdup(name); + } + ; + +hashoptlist: + | hashopt ';' + | hashoptlist ';' hashopt ';' + ; +hashopt: + IPT_SIZE YY_NUMBER + | IPT_SEED YY_NUMBER + ; + +dstlist: + dstentries { $$ = $1; } + | ';' { $$ = NULL; } + ; + +dstentries: + dstentry next { $$ = $1; } + | dstentry next dstentries { $1->ipfd_next = $3; $$ = $1; } + ; + +dstentry: + YY_STR ':' ipaddr { int size = sizeof(*$$) + strlen($1) + 1; + $$ = calloc(1, size); + if ($$ != NULL) { + $$->ipfd_dest.fd_name = strlen($1) + 1; + bcopy($1, $$->ipfd_names, + $$->ipfd_dest.fd_name); + $$->ipfd_dest.fd_addr = $3; + $$->ipfd_size = size; + } + free($1); + } + | ipaddr { $$ = calloc(1, sizeof(*$$)); + if ($$ != NULL) { + $$->ipfd_dest.fd_name = -1; + $$->ipfd_dest.fd_addr = $1; + $$->ipfd_size = sizeof(*$$); + } + } + ; + +dstopts: + { $$ = IPLDP_NONE; } + | IPT_POLICY IPT_ROUNDROBIN ';' { $$ = IPLDP_ROUNDROBIN; } + | IPT_POLICY IPT_WEIGHTED weighting ';' { $$ = $3; } + | IPT_POLICY IPT_RANDOM ';' { $$ = IPLDP_RANDOM; } + | IPT_POLICY IPT_HASH ';' { $$ = IPLDP_HASHED; } + | IPT_POLICY IPT_SRCHASH ';' { $$ = IPLDP_SRCHASH; } + | IPT_POLICY IPT_DSTHASH ';' { $$ = IPLDP_DSTHASH; } + ; + +weighting: + IPT_CONNECTION { $$ = IPLDP_CONNECTION; } + ; %% static wordtab_t yywords[] = { - { "auth", IPT_AUTH }, - { "count", IPT_COUNT }, - { "group", IPT_GROUP }, - { "group-map", IPT_GROUPMAP }, - { "hash", IPT_HASH }, - { "in", IPT_IN }, - { "ipf", IPT_IPF }, - { "name", IPT_NAME }, - { "nat", IPT_NAT }, - { "number", IPT_NUM }, - { "out", IPT_OUT }, - { "role", IPT_ROLE }, - { "seed", IPT_SEED }, - { "size", IPT_SIZE }, - { "table", IPT_TABLE }, - { "tree", IPT_TREE }, - { "type", IPT_TYPE }, - { NULL, 0 } + { "all", IPT_ALL }, + { "auth", IPT_AUTH }, + { "connection", IPT_CONNECTION }, + { "count", IPT_COUNT }, + { "dst-hash", IPT_DSTHASH }, + { "dstlist", IPT_DSTLIST }, + { "file", IPT_FILE }, + { "group", IPT_GROUP }, + { "group-map", IPT_GROUPMAP }, + { "hash", IPT_HASH }, + { "in", IPT_IN }, + { "ipf", IPT_IPF }, + { "name", IPT_NAME }, + { "nat", IPT_NAT }, + { "number", IPT_NUM }, + { "out", IPT_OUT }, + { "policy", IPT_POLICY }, + { "pool", IPT_POOL }, + { "random", IPT_RANDOM }, + { "round-robin", IPT_ROUNDROBIN }, + { "role", IPT_ROLE }, + { "seed", IPT_SEED }, + { "size", IPT_SIZE }, + { "src-hash", IPT_SRCHASH }, + { "table", IPT_TABLE }, + { "tree", IPT_TREE }, + { "type", IPT_TYPE }, + { "weighted", IPT_WEIGHTED }, + { "whois", IPT_WHOIS }, + { NULL, 0 } }; @@ -441,8 +680,9 @@ char *url; if (hlist == NULL) return NULL; - if (gethost(url, &hlist->al_addr) == -1) + if (gethost(hlist->al_family, url, &hlist->al_i6addr) == -1) { yyerror("Unknown hostname"); + } } hbot = NULL; @@ -453,10 +693,9 @@ char *url; if (h == NULL) break; - bcopy((char *)&a->al_addr, (char *)&h->ipe_addr, - sizeof(h->ipe_addr)); - bcopy((char *)&a->al_mask, (char *)&h->ipe_mask, - sizeof(h->ipe_mask)); + h->ipe_family = a->al_family; + h->ipe_addr = a->al_i6addr; + h->ipe_mask = a->al_i6mask; if (hbot != NULL) hbot->ipe_next = h; @@ -487,8 +726,9 @@ char *url; if (hlist == NULL) return NULL; - if (gethost(url, &hlist->al_addr) == -1) + if (gethost(hlist->al_family, url, &hlist->al_i6addr) == -1) { yyerror("Unknown hostname"); + } } pbot = NULL; @@ -498,16 +738,19 @@ char *url; p = calloc(1, sizeof(*p)); if (p == NULL) break; + p->ipn_mask.adf_addr = a->al_i6mask; - p->ipn_addr.adf_len = 8; - p->ipn_mask.adf_len = 8; - + if (a->al_family == AF_INET) { + p->ipn_addr.adf_family = AF_INET; +#ifdef USE_INET6 + } else if (a->al_family == AF_INET6) { + p->ipn_addr.adf_family = AF_INET6; +#endif + } + setadflen(&p->ipn_addr); + p->ipn_addr.adf_addr = a->al_i6addr; p->ipn_info = a->al_not; - - bcopy((char *)&a->al_addr, (char *)&p->ipn_addr.adf_addr, - sizeof(p->ipn_addr.adf_addr)); - bcopy((char *)&a->al_mask, (char *)&p->ipn_mask.adf_addr, - sizeof(p->ipn_mask.adf_addr)); + p->ipn_mask.adf_len = p->ipn_addr.adf_len; if (pbot != NULL) pbot->ipn_next = p; @@ -520,3 +763,59 @@ char *url; return ptop; } + + +ip_pool_node_t * +read_whoisfile(file) + char *file; +{ + ip_pool_node_t *ntop, *ipn, node, *last; + char line[1024]; + FILE *fp; + + fp = fopen(file, "r"); + if (fp == NULL) + return NULL; + + last = NULL; + ntop = NULL; + while (fgets(line, sizeof(line) - 1, fp) != NULL) { + line[sizeof(line) - 1] = '\0'; + + if (parsewhoisline(line, &node.ipn_addr, &node.ipn_mask)) + continue; + ipn = calloc(1, sizeof(*ipn)); + if (ipn == NULL) + continue; + ipn->ipn_addr = node.ipn_addr; + ipn->ipn_mask = node.ipn_mask; + if (last == NULL) + ntop = ipn; + else + last->ipn_next = ipn; + last = ipn; + } + fclose(fp); + return ntop; +} + + +static void +setadflen(afp) + addrfamily_t *afp; +{ + afp->adf_len = offsetof(addrfamily_t, adf_addr); + switch (afp->adf_family) + { + case AF_INET : + afp->adf_len += sizeof(struct in_addr); + break; +#ifdef USE_INET6 + case AF_INET6 : + afp->adf_len += sizeof(struct in6_addr); + break; +#endif + default : + break; + } +} diff --git a/contrib/ipfilter/tools/ipscan_y.y b/contrib/ipfilter/tools/ipscan_y.y index 5dbefd6ab5a..d323f054bd2 100644 --- a/contrib/ipfilter/tools/ipscan_y.y +++ b/contrib/ipfilter/tools/ipscan_y.y @@ -1,7 +1,7 @@ /* $FreeBSD$ */ /* - * Copyright (C) 2001-2004 by Darren Reed. + * Copyright (C) 2012 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. */ @@ -13,6 +13,7 @@ #include "kmem.h" #include "ipscan_l.h" #include "netinet/ip_scan.h" +#include #define YYDEBUG 1 @@ -60,7 +61,7 @@ int fd = -1; %token YY_NUMBER YY_HEX %token YY_STR -%token YY_COMMENT +%token YY_COMMENT %token YY_CMP_EQ YY_CMP_NE YY_CMP_LE YY_CMP_GE YY_CMP_LT YY_CMP_GT %token YY_RANGE_OUT YY_RANGE_IN %token YY_IPV6 diff --git a/contrib/ipfilter/tools/ipsyncm.c b/contrib/ipfilter/tools/ipsyncm.c index 600d39ad0d6..41513fae3bc 100644 --- a/contrib/ipfilter/tools/ipsyncm.c +++ b/contrib/ipfilter/tools/ipsyncm.c @@ -1,13 +1,13 @@ /* $FreeBSD$ */ /* - * Copyright (C) 2001-2006 by Darren Reed. + * Copyright (C) 2012 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. */ #if !defined(lint) static const char sccsid[] = "@(#)ip_fil.c 2.41 6/5/96 (C) 1993-2000 Darren Reed"; -static const char rcsid[] = "@(#)$Id: ipsyncm.c,v 1.4.2.5 2006/08/26 11:21:14 darrenr Exp $"; +static const char rcsid[] = "@(#)$Id$"; #endif #include #include @@ -49,13 +49,13 @@ static void handleterm(int sig) } #endif - + /* should be large enough to hold header + any datatype */ #define BUFFERLEN 1400 int main(argc, argv) -int argc; -char *argv[]; + int argc; + char *argv[]; { struct sockaddr_in sin; char buff[BUFFERLEN]; @@ -66,14 +66,14 @@ char *argv[]; u_32_t magic; synchdr_t *sh; char *progname; - + progname = strrchr(argv[0], '/'); if (progname) { progname++; } else { progname = argv[0]; } - + if (argc < 2) { usage(progname); @@ -108,13 +108,13 @@ char *argv[]; syslog(LOG_ERR, "Opening %s :%m", IPSYNC_NAME); goto tryagain; } - + nfd = socket(AF_INET, SOCK_DGRAM, 0); if (nfd == -1) { syslog(LOG_ERR, "Socket :%m"); goto tryagain; } - + if (connect(nfd, (struct sockaddr *)&sin, sizeof(sin)) == -1) { syslog(LOG_ERR, "Connect: %m"); goto tryagain; @@ -122,15 +122,15 @@ char *argv[]; syslog(LOG_INFO, "Sending data to %s", inet_ntoa(sin.sin_addr)); - - inbuf = 0; + + inbuf = 0; while (1) { n1 = read(lfd, buff+inbuf, BUFFERLEN-inbuf); - + printf("header : %d bytes read (header = %d bytes)\n", - n1, sizeof(*sh)); - + n1, (int) sizeof(*sh)); + if (n1 < 0) { syslog(LOG_ERR, "Read error (header): %m"); goto tryagain; @@ -143,8 +143,8 @@ char *argv[]; sleep(1); continue; } - - inbuf += n1; + + inbuf += n1; moreinbuf: if (inbuf < sizeof(*sh)) { @@ -153,7 +153,7 @@ moreinbuf: sh = (synchdr_t *)buff; len = ntohl(sh->sm_len); - magic = ntohl(sh->sm_magic); + magic = ntohl(sh->sm_magic); if (magic != SYNHDRMAGIC) { syslog(LOG_ERR, @@ -181,8 +181,8 @@ moreinbuf: printf(" table:Unknown(%d)", sh->sm_table); printf(" num:%d\n", (u_32_t)ntohl(sh->sm_num)); -#endif - +#endif + if (inbuf < sizeof(*sh) + len) { continue; /* need more data */ goto tryagain; @@ -195,9 +195,9 @@ moreinbuf: } else if (sh->sm_cmd == SMC_UPDATE) { su = (syncupdent_t *)buff; if (sh->sm_p == IPPROTO_TCP) { - printf(" TCP Update: age %lu state %d/%d\n", + printf(" TCP Update: age %lu state %d/%d\n", su->sup_tcp.stu_age, - su->sup_tcp.stu_state[0], + su->sup_tcp.stu_state[0], su->sup_tcp.stu_state[1]); } } else { @@ -212,7 +212,7 @@ moreinbuf: goto tryagain; } - + if (n3 != n2) { syslog(LOG_ERR, "Incomplete write (%d/%d)", n3, n2); @@ -226,7 +226,7 @@ moreinbuf: /* move buffer to the front,we might need to make * this more efficient, by using a rolling pointer * over the buffer and only copying it, when - * we are reaching the end + * we are reaching the end */ inbuf -= n2; if (inbuf) { diff --git a/contrib/ipfilter/tools/ipsyncs.c b/contrib/ipfilter/tools/ipsyncs.c index 887eeab310c..43692cd6a0d 100644 --- a/contrib/ipfilter/tools/ipsyncs.c +++ b/contrib/ipfilter/tools/ipsyncs.c @@ -1,13 +1,13 @@ /* $FreeBSD$ */ /* - * Copyright (C) 2001-2006 by Darren Reed. + * Copyright (C) 2012 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. */ #if !defined(lint) static const char sccsid[] = "@(#)ip_fil.c 2.41 6/5/96 (C) 1993-2000 Darren Reed"; -static const char rcsid[] = "@(#)$Id: ipsyncs.c,v 1.5.2.4 2006/08/26 11:21:15 darrenr Exp $"; +static const char rcsid[] = "@(#)$Id$"; #endif #include #include @@ -54,10 +54,10 @@ static void handleterm(int sig) #define BUFFERLEN 1400 int main(argc, argv) -int argc; -char *argv[]; + int argc; + char *argv[]; { - int nfd = -1 , lfd = -1; + int nfd = -1 , lfd = -1; int n1, n2, n3, magic, len, inbuf; struct sockaddr_in sin; struct sockaddr_in in; @@ -66,14 +66,14 @@ char *argv[]; syncupdent_t *su; synchdr_t *sh; char *progname; - + progname = strrchr(argv[0], '/'); if (progname) { progname++; } else { progname = argv[0]; } - + if (argc < 2) { usage(progname); exit(1); @@ -86,7 +86,7 @@ char *argv[]; #endif openlog(progname, LOG_PID, LOG_SECURITY); - + lfd = open(IPSYNC_NAME, O_WRONLY); if (lfd == -1) { syslog(LOG_ERR, "Opening %s :%m", IPSYNC_NAME); @@ -101,14 +101,14 @@ char *argv[]; sin.sin_port = htons(atoi(argv[2])); else sin.sin_port = htons(43434); - if (argc > 3) + if (argc > 3) in.sin_addr.s_addr = inet_addr(argv[3]); else in.sin_addr.s_addr = 0; in.sin_port = 0; while(1) { - + if (lfd != -1) close(lfd); if (nfd != -1) @@ -119,7 +119,7 @@ char *argv[]; syslog(LOG_ERR, "Opening %s :%m", IPSYNC_NAME); goto tryagain; } - + nfd = socket(AF_INET, SOCK_DGRAM, 0); if (nfd == -1) { syslog(LOG_ERR, "Socket :%m"); @@ -135,20 +135,20 @@ char *argv[]; } syslog(LOG_INFO, "Listening to %s", inet_ntoa(sin.sin_addr)); - - inbuf = 0; + + inbuf = 0; while (1) { - /* + /* * XXX currently we do not check the source address * of a datagram, this can be a security risk */ n1 = read(nfd, buff+inbuf, BUFFERLEN-inbuf); - + printf("header : %d bytes read (header = %d bytes)\n", - n1, sizeof(*sh)); - + n1, (int) sizeof(*sh)); + if (n1 < 0) { syslog(LOG_ERR, "Read error (header): %m"); goto tryagain; @@ -161,8 +161,8 @@ char *argv[]; sleep(1); continue; } - - inbuf += n1; + + inbuf += n1; moreinbuf: if (inbuf < sizeof(*sh)) { @@ -171,7 +171,7 @@ moreinbuf: sh = (synchdr_t *)buff; len = ntohl(sh->sm_len); - magic = ntohl(sh->sm_magic); + magic = ntohl(sh->sm_magic); if (magic != SYNHDRMAGIC) { syslog(LOG_ERR, "Invalid header magic %x", @@ -199,8 +199,8 @@ moreinbuf: printf(" table:Unknown(%d)", sh->sm_table); printf(" num:%d\n", (u_32_t)ntohl(sh->sm_num)); -#endif - +#endif + if (inbuf < sizeof(*sh) + len) { continue; /* need more data */ goto tryagain; @@ -213,9 +213,9 @@ moreinbuf: } else if (sh->sm_cmd == SMC_UPDATE) { su = (syncupdent_t *)buff; if (sh->sm_p == IPPROTO_TCP) { - printf(" TCP Update: age %lu state %d/%d\n", + printf(" TCP Update: age %lu state %d/%d\n", su->sup_tcp.stu_age, - su->sup_tcp.stu_state[0], + su->sup_tcp.stu_state[0], su->sup_tcp.stu_state[1]); } } else { @@ -231,7 +231,7 @@ moreinbuf: goto tryagain; } - + if (n3 != n2) { syslog(LOG_ERR, "%s: Incomplete write (%d/%d)", IPSYNC_NAME, n3, n2); @@ -245,7 +245,7 @@ moreinbuf: /* move buffer to the front,we might need to make * this more efficient, by using a rolling pointer * over the buffer and only copying it, when - * we are reaching the end + * we are reaching the end */ inbuf -= n2; if (inbuf) { diff --git a/contrib/ipfilter/tools/lex_var.h b/contrib/ipfilter/tools/lex_var.h index 78c5efc4263..eb59f5887cb 100644 --- a/contrib/ipfilter/tools/lex_var.h +++ b/contrib/ipfilter/tools/lex_var.h @@ -1,7 +1,7 @@ /* $FreeBSD$ */ /* - * Copyright (C) 2002 by Darren Reed. + * Copyright (C) 2012 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. */ diff --git a/contrib/ipfilter/tools/lexer.c b/contrib/ipfilter/tools/lexer.c index 989643c966b..41b7896090a 100644 --- a/contrib/ipfilter/tools/lexer.c +++ b/contrib/ipfilter/tools/lexer.c @@ -1,7 +1,7 @@ /* $FreeBSD$ */ /* - * Copyright (C) 2002-2006 by Darren Reed. + * Copyright (C) 2012 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. */ @@ -42,6 +42,7 @@ char yychars[YYBUFSIZ+1]; int yylineNum = 1; int yypos = 0; int yylast = -1; +int yydictfixed = 0; int yyexpectaddr = 0; int yybreakondot = 0; int yyvarnext = 0; @@ -60,7 +61,7 @@ static void yystrtotext __P((char *)); static char *yytexttochar __P((void)); static int yygetc(docont) -int docont; + int docont; { int c; @@ -98,7 +99,7 @@ int docont; static void yyunputc(c) -int c; + int c; { if (c == '\n') yylineNum--; @@ -107,7 +108,7 @@ int c; static int yyswallow(last) -int last; + int last; { int c; @@ -134,7 +135,7 @@ static char *yytexttochar() static void yystrtotext(str) -char *str; + char *str; { int len; char *s; @@ -150,7 +151,7 @@ char *str; static char *yytexttostr(offset, max) -int offset, max; + int offset, max; { char *str; int i; @@ -175,8 +176,11 @@ int offset, max; int yylex() { + static int prior = 0; + static int priornum = 0; int c, n, isbuilding, rval, lnext, nokey = 0; char *name; + int triedv6 = 0; isbuilding = 0; lnext = 0; @@ -190,7 +194,8 @@ int yylex() nextchar: c = yygetc(0); if (yydebug > 1) - printf("yygetc = (%x) %c [%*.*s]\n", c, c, yypos, yypos, yytexttochar()); + printf("yygetc = (%x) %c [%*.*s]\n", + c, c, yypos, yypos, yytexttochar()); switch (c) { @@ -209,6 +214,8 @@ nextchar: sizeof(yytext[0]) * (yylast - yypos + 1)); } yylast -= yypos; + if (yyexpectaddr == 2) + yyexpectaddr = 0; yypos = 0; lnext = 0; nokey = 0; @@ -232,6 +239,7 @@ nextchar: if (lnext == 1) { lnext = 0; if ((isbuilding == 0) && !ISALNUM(c)) { + prior = c; return c; } goto nextchar; @@ -246,7 +254,7 @@ nextchar: } yyswallow('\n'); rval = YY_COMMENT; - goto nextchar; + goto done; case '$' : if (isbuilding == 1) { @@ -320,6 +328,9 @@ nextchar: yybreakondot = 0; yyvarnext = 0; yytokentype = 0; + if (yydebug) + fprintf(stderr, "reset at EOF\n"); + prior = 0; return 0; } @@ -344,16 +355,21 @@ nextchar: switch (c) { case '-' : - if (yyexpectaddr) - break; - if (isbuilding == 1) - break; n = yygetc(0); if (n == '>') { isbuilding = 1; goto done; } yyunputc(n); + if (yyexpectaddr) { + if (isbuilding == 1) + yyunputc(c); + else + rval = '-'; + goto done; + } + if (isbuilding == 1) + break; rval = '-'; goto done; @@ -420,14 +436,21 @@ nextchar: * 0000:0000:0000:0000:0000:0000:0000:0000 */ #ifdef USE_INET6 - if (yyexpectaddr == 1 && isbuilding == 0 && (ishex(c) || c == ':')) { + if (yyexpectaddr != 0 && isbuilding == 0 && + (ishex(c) || isdigit(c) || c == ':')) { char ipv6buf[45 + 1], *s, oc; int start; +buildipv6: start = yypos; s = ipv6buf; oc = c; + if (prior == YY_NUMBER && c == ':') { + sprintf(s, "%d", priornum); + s += strlen(s); + } + /* * Perhaps we should implement stricter controls on what we * swallow up here, but surely it would just be duplicating @@ -451,7 +474,25 @@ nextchar: } #endif - if (c == ':') { + if ((c == ':') && (rval != YY_IPV6) && (triedv6 == 0)) { +#ifdef USE_INET6 + yystr = yytexttostr(0, yypos - 1); + if (yystr != NULL) { + char *s; + + for (s = yystr; *s && ishex(*s); s++) + ; + if (!*s && *yystr) { + isbuilding = 0; + c = *yystr; + free(yystr); + triedv6 = 1; + yypos = 1; + goto buildipv6; + } + free(yystr); + } +#endif if (isbuilding == 1) { yyunputc(c); goto done; @@ -492,8 +533,8 @@ done: yystr = yytexttostr(0, yypos); if (yydebug) - printf("isbuilding %d yyvarnext %d nokey %d\n", - isbuilding, yyvarnext, nokey); + printf("isbuilding %d yyvarnext %d nokey %d fixed %d addr %d\n", + isbuilding, yyvarnext, nokey, yydictfixed, yyexpectaddr); if (isbuilding == 1) { wordtab_t *w; @@ -502,7 +543,7 @@ done: if ((yyvarnext == 0) && (nokey == 0)) { w = yyfindkey(yystr); - if (w == NULL && yywordtab != NULL) { + if (w == NULL && yywordtab != NULL && !yydictfixed) { yyresetdict(); w = yyfindkey(yystr); } @@ -514,14 +555,19 @@ done: rval = YY_STR; } - if (rval == YY_STR && yysavedepth > 0) - yyresetdict(); + if (rval == YY_STR) { + if (yysavedepth > 0 && !yydictfixed) + yyresetdict(); + if (yyexpectaddr != 0) + yyexpectaddr = 0; + } yytokentype = rval; if (yydebug) - printf("lexed(%s) [%d,%d,%d] => %d @%d\n", yystr, string_start, - string_end, pos, rval, yysavedepth); + printf("lexed(%s) %d,%d,%d [%d,%d,%d] => %d @%d\n", + yystr, isbuilding, yyexpectaddr, yysavedepth, + string_start, string_end, pos, rval, yysavedepth); switch (rval) { @@ -548,12 +594,15 @@ done: yypos = 0; } + if (rval == YY_NUMBER) + priornum = yylval.num; + prior = rval; return rval; } static wordtab_t *yyfindkey(key) -char *key; + char *key; { wordtab_t *w; @@ -568,7 +617,7 @@ char *key; char *yykeytostr(num) -int num; + int num; { wordtab_t *w; @@ -583,7 +632,7 @@ int num; wordtab_t *yysettab(words) -wordtab_t *words; + wordtab_t *words; { wordtab_t *save; @@ -594,7 +643,7 @@ wordtab_t *words; void yyerror(msg) -char *msg; + char *msg; { char *txt, letter[2]; int freetxt = 0; @@ -620,9 +669,31 @@ char *msg; } -void yysetdict(newdict) -wordtab_t *newdict; +void yysetfixeddict(newdict) + wordtab_t *newdict; { + if (yydebug) + printf("yysetfixeddict(%lx)\n", (u_long)newdict); + + if (yysavedepth == sizeof(yysavewords)/sizeof(yysavewords[0])) { + fprintf(stderr, "%d: at maximum dictionary depth\n", + yylineNum); + return; + } + + yysavewords[yysavedepth++] = yysettab(newdict); + if (yydebug) + printf("yysavedepth++ => %d\n", yysavedepth); + yydictfixed = 1; +} + + +void yysetdict(newdict) + wordtab_t *newdict; +{ + if (yydebug) + printf("yysetdict(%lx)\n", (u_long)newdict); + if (yysavedepth == sizeof(yysavewords)/sizeof(yysavewords[0])) { fprintf(stderr, "%d: at maximum dictionary depth\n", yylineNum); @@ -643,14 +714,15 @@ void yyresetdict() if (yydebug) printf("yysavedepth-- => %d\n", yysavedepth); } + yydictfixed = 0; } #ifdef TEST_LEXER int main(argc, argv) -int argc; -char *argv[]; + int argc; + char *argv[]; { int n; diff --git a/contrib/ipfilter/tools/lexer.h b/contrib/ipfilter/tools/lexer.h index d973ea42ae3..cff24b4eb40 100644 --- a/contrib/ipfilter/tools/lexer.h +++ b/contrib/ipfilter/tools/lexer.h @@ -1,16 +1,11 @@ /* $FreeBSD$ */ /* - * Copyright (C) 2002-2004 by Darren Reed. + * Copyright (C) 2012 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. */ -typedef struct wordtab { - char *w_word; - int w_value; -} wordtab_t; - #ifdef NO_YACC #define YY_COMMENT 1000 #define YY_CMP_NE 1001 @@ -29,6 +24,7 @@ typedef struct wordtab { extern wordtab_t *yysettab __P((wordtab_t *)); extern void yysetdict __P((wordtab_t *)); +extern void yysetfixeddict __P((wordtab_t *)); extern int yylex __P((void)); extern void yyerror __P((char *)); extern char *yykeytostr __P((int)); diff --git a/contrib/ldns-host/Makefile b/contrib/ldns-host/Makefile new file mode 100644 index 00000000000..9d07a581fdd --- /dev/null +++ b/contrib/ldns-host/Makefile @@ -0,0 +1,23 @@ +PROG=ldns-host +SRC=ldns-host.c +MAN=ldns-host.1 + +LOCALBASE?=/usr/local +PREFIX?=${LOCALBASE} +MANDIR?=${PREFIX}/man + +XCFLAGS=${CFLAGS} -I${LOCALBASE}/include +XLDFLAGS=${LDFLAGS} -L${LOCALBASE}/lib -lldns + +${PROG}: ${SRC} + ${CC} -o $@ ${XCFLAGS} ${XLDFLAGS} ${SRC} + +clean: + rm -f ${PROG} + +install: ${PROG} + cp ${PROG} ${PREFIX}/bin/ + cp ${MAN} ${MANDIR}/man1/ + +deinstall: + rm -f ${PREFIX}/bin/${PROG} ${MANDIR}/man1/${MAN} diff --git a/contrib/ldns-host/ldns-host.1 b/contrib/ldns-host/ldns-host.1 new file mode 100644 index 00000000000..3a85ac41001 --- /dev/null +++ b/contrib/ldns-host/ldns-host.1 @@ -0,0 +1,246 @@ +.\" (c) Magerya Vitaly +.\" +.\" Copying and distribution of this file, with or without modification, +.\" are permitted in any medium without royalty provided the copyright +.\" notice and this notice are preserved. This file is offered as-is, +.\" without any warranty. +.Dd Aug 27, 2012 +.Dt LDNS-HOST 1 +.Os +.Sh NAME +.Nm ldns-host +.Nd DNS lookup utility +.Sh SYNOPSIS +.Nm +.Op Fl aCdilrsTvw46 +.Op Fl c Ar class +.Op Fl N Ar ndots +.Op Fl R Ar number +.Op Fl t Ar type +.Op Fl W Ar wait +.Ar name +.Op Ar server +.Sh DESCRIPTION +.Nm +is a simple utility for performing DNS lookups. It is normally +used to convert names to IP addresses and vice versa. +.Pp +.Ar name +is the domain name that is to be looked up. It can also be a +dotted-decimal IPv4 address or a colon-delimited IPv6 address, +in which case +.Nm +will by default perform a reverse lookup for that address. +.Pp +When +.Ar name +is not provided, +.Nm +prints a short summary of it's usage. +.Pp +.Ar server +is an optional argument which is either a domain name or an IP +address of the name server that +.Nm +should query instead of the server or servers listed in +.Pa /etc/resolv.conf . +When +.Ar server +is a domain name, system resolver is used to obtain it's address. +.Pp +Supported options: +.Bl -tag -width indent +.It Fl a +Make a verbose query of type +.Cm ANY . +Equivalent to +.Fl v Fl t Cm ANY . +.It Fl C +Query for +.Cm SOA +records for zone +.Ar name +from all of it's authoritative name servers. The list of name +servers is obtained via +.Cm NS +query for +.Ar name . +.It Fl c Ar class +Perform DNS query of class +.Ar class . +Recognized classes are +.Cm IN Pq Internet , +.Cm CH Pq Chaosnet , +.Cm HS Pq Hesiod , +.Cm NONE , +.Cm ANY +and +.Cm CLASS Ns Ar N +(where +.Ar N +is a number from 1 to 255). Default is +.Cm IN . +.It Fl d +Produce verbose output. This is a synonym for +.Fl v , +and is provided for backward compatibility. +.It Fl i +Use IP6.INT domain for reverse lookups of IPv6 addresses (as +defined in RFC1886; note that RFC4159 deprecates IP6.INT). +By default IP6.ARPA is used. +.It Fl l +List all +.Cm NS, PTR, A +and +.Cm AAAA +records in zone +.Ar name +by performing a zone transfer +.Pq Cm AXFR . +You can combine this option with +.Fl a +to print all records, or with +.Fl t +to only print specific ones. +.It Fl N Ar ndots +Consider names with at least this many dots as absolute. That +is, try to resolve them directly before consulting +.Ic domain +or +.Ic search +options from +.Pa /etc/resolv.conf . +.It Fl r +Perform non-recursive query to the name server by clearing RD +.Pq Dq recursion desired +bit of the query. +.It Fl R Ar number +Retry this many times when a query does not receive an answer +in time. The default is 1 retry. If +.Ar number +is negative or zero, 1 is used instead. +.It Fl s +Report SERVFAIL responses as they are, do not ignore them. +.It Fl T +Query name server over TCP. By default UDP is used, except for +.Cm AXFR +and +.Cm IXFR +queries, which require TCP. +.Nm +will also retry UDP queries in TCP mode if the UDP response was +truncated (i.e. had TC bit set). +.It Fl t Ar type +Perform DNS query of type +.Ar type , +which can be any standard query type name +.Pq Cm A , CNAME , MX , TXT , No etc , +a wildcard query +.Pq Cm ANY , +or +.Cm TYPE Ns Ar N , +where +.Ar N +is a number from 1 to 65535. For +.Cm IXFR Pq incremental zone transfer +queries the starting serial number can be specified by appending +an equal sign followed by the number +.Pq e.g. Fl t Cm IXFR Ns =12345678 . +.Pp +The default is to query for +.Cm A , AAAA , No and Cm MX +records, unless +.Fl C +or +.Fl l +options are given (in which case +.Cm SOA +or +.Cm AXFR +queries are made) or +.Ar name +is a valid IP address +(in which case reverse lookup using +.Cm PTR +query is performed). +.It Fl v +Produce verbose output. +.It Fl w +Wait forever (or for a very long time) for response from the +name server. +.It Fl W Ar wait +Wait this many seconds for a reply from name server before timing +out. If +.Ar wait +is negative or zero, value of 1 is used. The default is to wait +10 seconds for TCP connections, and 5 seconds for UDP (both are +subject to retries, see option +.Fl R ) . +.It Fl 4 +Only use IPv4 transport. +.It Fl 6 +Only use IPv6 transport. +.El +.Sh FILES +.Pa /etc/resolv.conf +.Sh SEE ALSO +.Xr drill 1 , +.Xr resolv.conf 5 +.Sh COMPATIBILITY +.Nm +aims to be reasonably compatible with +.Sq host +utility from BIND9 distribution, both in supported options and +in produced output. Here is a list of known notable differences: +.Bl -bullet +.It +Debugging options +.Pq Fl D No and Fl m +are not supported. +.It +Query class +.Cm CLASS0 +and type +.Cm TYPE0 +are not supported. +.It +Backslashes in domain names are treated especially. +.It +The maximum of 255 retries (option +.Fl R ) +are supported. +.It +Some resource records are formatted differently. For example, +.Cm RRSIG +and +.Cm DNSKEY +records are displayed without spaces in them. +.It +When parsing +.Pa /etc/resolv.conf +commands +.Ic sortlist +and +.Ic options +are ignored. When multiple +.Ic search +and/or +.Ic domain +commands are present, +.Nm +first uses the last +.Ic domain +command, and then all of +.Ic search +commands, while +.Sq host +from BIND9 uses whatever command was specified last. +.It +Multi-packet zone transfers are not supported; only the first +response packet is printed. +.It +.Sq Pseudosection TSIG +is missing from verbose packet output. +.El +.Sh AUTHORS +.An Vitaly Magerya Aq magv@tx97.net diff --git a/contrib/ldns-host/ldns-host.c b/contrib/ldns-host/ldns-host.c new file mode 100644 index 00000000000..85c7dcbe2e4 --- /dev/null +++ b/contrib/ldns-host/ldns-host.c @@ -0,0 +1,884 @@ +/*- + * (c) Magerya Vitaly + * + * Copying and distribution of this file, with or without modification, + * are permitted in any medium without royalty provided the copyright + * notice and this notice are preserved. This file is offered as-is, + * without any warranty. + */ + +#include + +#include +#include +#include +#include +#include + +#include + +/* General utilities. + */ + +static char *progname; + +#define countof(array) (sizeof(array)/sizeof(*(array))) + +static void +die(int code, const char *fmt, ...) { + va_list args; + + va_start(args, fmt); + fprintf(stderr, "%s: ", progname); + vfprintf(stderr, fmt, args); + fprintf(stderr, "\n"); + va_end(args); + exit(code); +} + +static int +ndots(const char *name) { + int n; + + for (n = 0; (name = strchr(name, '.')); n++, name++); + return n; +} + +/* General LDNS-specific utilities. + */ + +static ldns_status +ldns_resolver_new_default(ldns_resolver **res) { + if (ldns_resolver_new_frm_file(res, NULL) == LDNS_STATUS_OK || + (*res = ldns_resolver_new()) != NULL) + return LDNS_STATUS_OK; + return LDNS_STATUS_MEM_ERR; +} + +static ldns_status +ldns_resolver_push_default_servers(ldns_resolver *res) { + ldns_status status; + ldns_rdf *addr; + + if ((status = ldns_str2rdf_a(&addr, "127.0.0.1")) != LDNS_STATUS_OK || + (status = ldns_resolver_push_nameserver(res, addr)) != LDNS_STATUS_OK) + return ldns_rdf_deep_free(addr), status; + ldns_rdf_deep_free(addr); + if ((status = ldns_str2rdf_aaaa(&addr, "::1")) != LDNS_STATUS_OK || + (status = ldns_resolver_push_nameserver(res, addr)) != LDNS_STATUS_OK) + return ldns_rdf_deep_free(addr), status; + ldns_rdf_deep_free(addr); + return LDNS_STATUS_OK; +} + +static ldns_rdf * +ldns_rdf_new_addr_frm_str(const char *str) { + ldns_rdf *addr; + + if ((addr = ldns_rdf_new_frm_str(LDNS_RDF_TYPE_A, str)) == NULL) + addr = ldns_rdf_new_frm_str(LDNS_RDF_TYPE_AAAA, str); + return addr; +} + +static void +ldns_resolver_remove_nameservers(ldns_resolver *res) { + while (ldns_resolver_nameserver_count(res) > 0) + ldns_rdf_deep_free(ldns_resolver_pop_nameserver(res)); +} + +static ldns_rdf * +ldns_rdf_reverse_a(ldns_rdf *addr, const char *base) { + char *buf; + int i, len; + + len = strlen(base); + buf = alloca(LDNS_IP4ADDRLEN*4 + len + 1); + for (len = i = 0; i < LDNS_IP4ADDRLEN; i++) + len += sprintf(&buf[len], "%d.", + (int)ldns_rdf_data(addr)[LDNS_IP4ADDRLEN - i - 1]); + sprintf(&buf[len], "%s", base); + return ldns_dname_new_frm_str(buf); +} + +static ldns_rdf * +ldns_rdf_reverse_aaaa(ldns_rdf *addr, const char *base) { + char *buf; + int i, len; + + len = strlen(base); + buf = alloca(LDNS_IP6ADDRLEN*4 + len + 1); + for (i = 0; i < LDNS_IP6ADDRLEN; i++) { + uint8_t byte = ldns_rdf_data(addr)[LDNS_IP6ADDRLEN - i - 1]; + sprintf(&buf[i*4], "%x.%x.", byte & 0x0F, byte >> 4); + } + sprintf(&buf[LDNS_IP6ADDRLEN*4], "%s", base); + return ldns_dname_new_frm_str(buf); +} + +static ldns_status +ldns_pkt_push_rr_soa(ldns_pkt *pkt, ldns_pkt_section sec, + const ldns_rdf *name, ldns_rr_class c, uint32_t serial) { + ldns_rdf *rdf; + ldns_rr *rr; + uint32_t n; + + if ((rr = ldns_rr_new_frm_type(LDNS_RR_TYPE_SOA)) == NULL) + return LDNS_STATUS_MEM_ERR; + ldns_rr_set_class(rr, c); + ldns_rr_set_owner(rr, ldns_rdf_clone(name)); + ldns_rr_set_ttl(rr, 0); + + n = 0; + if ((rdf = ldns_rdf_new_frm_data(LDNS_RDF_TYPE_DNAME, 1, &n)) == NULL) + goto memerr; + ldns_rr_set_rdf(rr, rdf, 0); + ldns_rr_set_rdf(rr, ldns_rdf_clone(rdf), 1); + + n = htonl(serial); + if ((rdf = ldns_rdf_new_frm_data(LDNS_RDF_TYPE_INT32, 4, &n)) == NULL) + goto memerr; + ldns_rr_set_rdf(rr, rdf, 2); + + n = 0; + if ((rdf = ldns_rdf_new_frm_data(LDNS_RDF_TYPE_PERIOD, 4, &n)) == NULL) + goto memerr; + ldns_rr_set_rdf(rr, rdf, 3); + ldns_rr_set_rdf(rr, ldns_rdf_clone(rdf), 4); + ldns_rr_set_rdf(rr, ldns_rdf_clone(rdf), 5); + ldns_rr_set_rdf(rr, ldns_rdf_clone(rdf), 6); + + if (ldns_rr_rdf(rr, 1) == NULL || ldns_rr_rdf(rr, 4) == NULL || + ldns_rr_rdf(rr, 5) == NULL || ldns_rr_rdf(rr, 6) == NULL || + !ldns_pkt_push_rr(pkt, sec, rr)) + goto memerr; + return LDNS_STATUS_OK; + +memerr: + ldns_rr_free(rr); + return LDNS_STATUS_MEM_ERR; +} + +static ldns_status +ldns_resolver_send_to(ldns_pkt **answer, ldns_resolver *res, + const ldns_rdf *name, ldns_rr_type t, ldns_rr_class c, + uint16_t flags, uint32_t ixfr_serial, int nameserver) { + ldns_status status; + ldns_pkt *qpkt; + + int nscnt = ldns_resolver_nameserver_count(res); + ldns_rdf **ns = ldns_resolver_nameservers(res); + size_t *rtt = ldns_resolver_rtt(res); + + ldns_resolver_set_nameservers(res, &ns[nameserver]); + ldns_resolver_set_rtt(res, &rtt[nameserver]); + ldns_resolver_set_nameserver_count(res, 1); + + status = ldns_resolver_prepare_query_pkt(&qpkt, res, name, t, c, flags); + if (status == LDNS_STATUS_OK && t == LDNS_RR_TYPE_IXFR) + status = ldns_pkt_push_rr_soa(qpkt, + LDNS_SECTION_AUTHORITY, name, c, ixfr_serial); + if (status == LDNS_STATUS_OK) + status = ldns_resolver_send_pkt(answer, res, qpkt); + ldns_pkt_free(qpkt); + + ldns_resolver_set_nameservers(res, ns); + ldns_resolver_set_rtt(res, rtt); + ldns_resolver_set_nameserver_count(res, nscnt); + return status; +} + +static void +ldns_pkt_filter_answer(ldns_pkt *pkt, ldns_rr_type type) { + int i, j, cnt; + ldns_rr_list *rrlist; + ldns_rr *rr; + ldns_rr_type rrtype; + + rrlist = ldns_pkt_answer(pkt); + cnt = ldns_rr_list_rr_count(rrlist); + for (i = j = 0; i < cnt; i++) { + rr = ldns_rr_list_rr(rrlist, i); + rrtype = ldns_rr_get_type(rr); + if (type == LDNS_RR_TYPE_ANY || + type == rrtype || + (type == LDNS_RR_TYPE_AXFR && + (rrtype == LDNS_RR_TYPE_A || + rrtype == LDNS_RR_TYPE_AAAA || + rrtype == LDNS_RR_TYPE_NS || + rrtype == LDNS_RR_TYPE_PTR))) + ldns_rr_list_set_rr(rrlist, rr, j++); + } + ldns_rr_list_set_rr_count(rrlist, j); +} + +/* Packet content printing. + */ + +static struct { + ldns_rr_type type; + const char *text; +} rr_types[] = { + {LDNS_RR_TYPE_A, "has address"}, + {LDNS_RR_TYPE_NS, "name server"}, + {LDNS_RR_TYPE_CNAME, "is an alias for"}, + {LDNS_RR_TYPE_WKS, "has well known services"}, + {LDNS_RR_TYPE_PTR, "domain name pointer"}, + {LDNS_RR_TYPE_HINFO, "host information"}, + {LDNS_RR_TYPE_MX, "mail is handled by"}, + {LDNS_RR_TYPE_TXT, "descriptive text"}, + {LDNS_RR_TYPE_X25, "x25 address"}, + {LDNS_RR_TYPE_ISDN, "ISDN address"}, + {LDNS_RR_TYPE_SIG, "has signature"}, + {LDNS_RR_TYPE_KEY, "has key"}, + {LDNS_RR_TYPE_AAAA, "has IPv6 address"}, + {LDNS_RR_TYPE_LOC, "location"}, +}; + +static void +print_opcode(ldns_pkt_opcode opcode) { + ldns_lookup_table *lt = ldns_lookup_by_id(ldns_opcodes, opcode); + + if (lt && lt->name) + printf("%s", lt->name); + else + printf("RESERVED%d", opcode); +} + +static void +print_rcode(uint8_t rcode) { + ldns_lookup_table *lt = ldns_lookup_by_id(ldns_rcodes, rcode); + + if (lt && lt->name) + printf("%s", lt->name); + else + printf("RESERVED%d", rcode); +} + +static int +print_rr_type(ldns_rr_type type) { + char *str; + int n; + + str = ldns_rr_type2str(type); + n = printf("%s", str); + free(str); + return n; +} + +static int +print_rr_class(ldns_rr_class cls) { + char *str; + int n; + + str = ldns_rr_class2str(cls); + n = printf("%s", str); + free(str); + return n; +} + +static int +print_rdf(ldns_rdf *rdf) { + char *str; + int n; + + str = ldns_rdf2str(rdf); + n = printf("%s", str); + free(str); + return n; +} + +static int +print_rdf_nodot(ldns_rdf *rdf) { + char *str; + int len, n; + + str = ldns_rdf2str(rdf); + len = strlen(str); + n = printf("%.*s", str[len-1] == '.' ? len-1 : len, str); + free(str); + return n; +} + +static int +print_padding(int fromcol, int tocol) { + int col = fromcol, nextcol = fromcol + 8 - fromcol%8; + + if (fromcol + 1 > tocol) tocol = fromcol + 1; + for (; nextcol <= tocol; col = nextcol, nextcol += 8) + printf("\t"); + for (; col < tocol; col++) + printf(" "); + return col - fromcol; +} + +static void +print_rr_verbose(ldns_rr *rr) { + bool isq = ldns_rr_is_question(rr); + int rdcnt = ldns_rr_rd_count(rr); + int i, n; + + /* bind9-host does not count the initial ';' here */ + n = isq ? printf(";") : 0; + n = print_rdf(ldns_rr_owner(rr)); + if (!isq) { + n += print_padding(n, 24); + n += printf("%d", ldns_rr_ttl(rr)); + } + n += print_padding(n, 32); + n += print_rr_class(ldns_rr_get_class(rr)); + n += print_padding(n, 40); + n += print_rr_type(ldns_rr_get_type(rr)); + for (i = 0; i < rdcnt; i++) { + if (i == 0) print_padding(n, 48); + else printf(" "); + print_rdf(ldns_rr_rdf(rr, i)); + } + printf("\n"); +} + +static void +print_pkt_section_verbose(const char *name, ldns_rr_list *rrlist) { + int i, cnt = ldns_rr_list_rr_count(rrlist); + + if (cnt == 0) + return; + printf(";; %s SECTION:\n", name); + for (i = 0; i < cnt; i++) + print_rr_verbose(ldns_rr_list_rr(rrlist, i)); + printf("\n"); +} + +static void +print_pkt_verbose(ldns_pkt *pkt) { + int got_flags = 0; + + printf(";; ->>HEADER<<- opcode: "); + print_opcode(ldns_pkt_get_opcode(pkt)); + printf(", status: "); + print_rcode(ldns_pkt_get_rcode(pkt)); + printf(", id: %u\n", ldns_pkt_id(pkt)); + printf(";; flags:"); + if (ldns_pkt_qr(pkt)) printf(" qr"), got_flags = 1; + if (ldns_pkt_aa(pkt)) printf(" aa"), got_flags = 1; + if (ldns_pkt_tc(pkt)) printf(" tc"), got_flags = 1; + if (ldns_pkt_rd(pkt)) printf(" rd"), got_flags = 1; + if (ldns_pkt_ra(pkt)) printf(" ra"), got_flags = 1; + if (ldns_pkt_ad(pkt)) printf(" ad"), got_flags = 1; + if (ldns_pkt_cd(pkt)) printf(" cd"), got_flags = 1; + if (!got_flags) printf(" "); + printf("; QUERY: %u, ANSWER: %u, AUTHORITY: %u, ADDITIONAL: %u\n", + ldns_pkt_qdcount(pkt), ldns_pkt_ancount(pkt), + ldns_pkt_nscount(pkt), ldns_pkt_arcount(pkt)); + if (ldns_pkt_edns(pkt)) + printf(";; EDNS: version: %u, udp=%u\n", + ldns_pkt_edns_version(pkt), ldns_pkt_edns_udp_size(pkt)); + printf("\n"); + print_pkt_section_verbose("QUESTION", ldns_pkt_question(pkt)); + print_pkt_section_verbose("ANSWER", ldns_pkt_answer(pkt)); + print_pkt_section_verbose("AUTHORITY", ldns_pkt_authority(pkt)); + print_pkt_section_verbose("ADDITIONAL", ldns_pkt_additional(pkt)); +} + +static void +print_rr_short(ldns_rr *rr) { + ldns_rr_type type = ldns_rr_get_type(rr); + size_t i, rdcnt = ldns_rr_rd_count(rr); + + print_rdf_nodot(ldns_rr_owner(rr)); + printf(" "); + for (i = 0; i < countof(rr_types); i++) { + if (rr_types[i].type == type) { + printf("%s", rr_types[i].text); + goto found; + } + } + + printf("has "); + print_rr_type(type); + printf(" record"); + +found: + for (i = 0; i < rdcnt; i++) { + printf(" "); + print_rdf(ldns_rr_rdf(rr, i)); + } + printf("\n"); +} + +static void +print_pkt_short(ldns_pkt *pkt, bool print_rr_server) { + ldns_rr_list *rrlist = ldns_pkt_answer(pkt); + size_t i; + + for (i = 0; i < ldns_rr_list_rr_count(rrlist); i++) { + if (print_rr_server) { + printf("Nameserver "); + print_rdf(ldns_pkt_answerfrom(pkt)); + printf(":\n\t"); + } + print_rr_short(ldns_rr_list_rr(rrlist, i)); + } +} + +static void +print_received_line(ldns_resolver *res, ldns_pkt *pkt) { + char *from = ldns_rdf2str(ldns_pkt_answerfrom(pkt)); + + printf("Received %zu bytes from %s#%d in %d ms\n", + ldns_pkt_size(pkt), from, ldns_resolver_port(res), + ldns_pkt_querytime(pkt)); + free(from); +} + +/* Main program. + * + * Note that no memory is freed below this line by intention. + */ + +#define DEFAULT_TCP_TIMEOUT 10 +#define DEFAULT_UDP_TIMEOUT 5 + +enum operation_mode { M_AXFR, M_DEFAULT_Q, M_SINGLE_Q, M_SOA }; + +static enum operation_mode o_mode = M_DEFAULT_Q; +static bool o_ignore_servfail = true; +static bool o_ip6_int = false; +static bool o_print_pkt_server = false; +static bool o_print_rr_server = false; +static bool o_recursive = true; +static bool o_tcp = false; +static bool o_verbose = false; +static char *o_name = NULL; +static char *o_server = NULL; +static int o_ipversion = LDNS_RESOLV_INETANY; +static int o_ndots = 1; +static int o_retries = 1; +static ldns_rr_class o_rrclass = LDNS_RR_CLASS_IN; +static ldns_rr_type o_rrtype = LDNS_RR_TYPE_A; +static time_t o_timeout = 0; +static uint32_t o_ixfr_serial = 0; + +static void +usage(void) { + fputs( + "Usage: host [-aCdilrsTvw46] [-c class] [-N ndots] [-R number]\n" + " [-t type] [-W wait] name [server]\n" + "\t-a same as -v -t ANY\n" + "\t-C query SOA records from all authoritative name servers\n" + "\t-c use this query class (IN, CH, HS, etc)\n" + "\t-d produce verbose output, same as -v\n" + "\t-i use IP6.INT for IPv6 reverse lookups\n" + "\t-l list records in a zone via AXFR\n" + "\t-N consider names with at least this many dots as absolute\n" + "\t-R retry UDP queries this many times\n" + "\t-r disable recursive query\n" + "\t-s do not ignore SERVFAIL responses\n" + "\t-T send query via TCP\n" + "\t-t use this query type (A, AAAA, MX, etc)\n" + "\t-v produce verbose output\n" + "\t-w wait forever for a server reply\n" + "\t-W wait this many seconds for a reply\n" + "\t-4 use IPv4 only\n" + "\t-6 use IPv6 only\n", + stderr); + exit(1); +} + +static void +parse_args(int argc, char *argv[]) { + int ch; + + progname = argv[0]; + while ((ch = getopt(argc, argv, "aCdilrsTvw46c:N:R:t:W:")) != -1) { + switch (ch) { + case 'a': + if (o_mode != M_AXFR) + o_mode = M_SINGLE_Q; + o_rrtype = LDNS_RR_TYPE_ANY; + o_verbose = true; + break; + case 'C': + o_mode = M_SOA; + o_print_rr_server = true; + o_rrclass = LDNS_RR_CLASS_IN; + o_rrtype = LDNS_RR_TYPE_NS; + break; + case 'c': + /* bind9-host sets o_mode to M_SINGLE_Q here */ + o_rrclass = ldns_get_rr_class_by_name(optarg); + if (o_rrclass <= 0) + die(2, "invalid class: %s\n", optarg); + break; + case 'd': o_verbose = true; break; + case 'i': o_ip6_int = true; break; + case 'l': + o_mode = M_AXFR; + o_rrtype = LDNS_RR_TYPE_AXFR; + o_tcp = true; + break; + case 'N': + o_ndots = atoi(optarg); + if (o_ndots < 0) o_ndots = 0; + break; + case 'n': + /* bind9-host accepts and ignores this option */ + break; + case 'r': o_recursive = 0; break; + case 'R': + o_retries = atoi(optarg); + if (o_retries <= 0) o_retries = 1; + if (o_retries > 255) o_retries = 255; + break; + case 's': o_ignore_servfail = false; break; + case 'T': o_tcp = true; break; + case 't': + if (o_mode != M_AXFR) + o_mode = M_SINGLE_Q; + if (strncasecmp(optarg, "ixfr=", 5) == 0) { + o_rrtype = LDNS_RR_TYPE_IXFR; + o_ixfr_serial = atol(optarg + 5); + } else { + o_rrtype = ldns_get_rr_type_by_name(optarg); + if (o_rrtype <= 0) + die(2, "invalid type: %s\n", optarg); + } + if (o_rrtype == LDNS_RR_TYPE_AXFR || o_rrtype == LDNS_RR_TYPE_IXFR) + o_tcp = true; + if (o_rrtype == LDNS_RR_TYPE_AXFR) { + o_mode = M_AXFR; + o_rrtype = LDNS_RR_TYPE_ANY; + o_verbose = true; + } + break; + case 'v': o_verbose = true; break; + case 'w': + o_timeout = (time_t)INT_MAX; + break; + case 'W': + o_timeout = atol(optarg); + if (o_timeout <= 0) o_timeout = 1; + break; + case '4': o_ipversion = LDNS_RESOLV_INET; break; + case '6': o_ipversion = LDNS_RESOLV_INET6; break; + default: + usage(); + } + } + argc -= optind; + argv += optind; + /* bind9-host ignores arguments after the 2-nd one */ + if (argc < 1) + usage(); + o_name = argv[0]; + if (argc > 1) { + o_server = argv[1]; + o_print_pkt_server = true; + } +} + +static ldns_rdf* +safe_str2rdf_dname(const char *name) { + ldns_rdf *dname; + ldns_status status; + + if ((status = ldns_str2rdf_dname(&dname, name)) != LDNS_STATUS_OK) { + die(1, "'%s' is not a legal name (%s)", + name, ldns_get_errorstr_by_id(status)); + } + return dname; +} + +static ldns_rdf* +safe_dname_cat_clone(const ldns_rdf *rd1, const ldns_rdf *rd2) { + ldns_rdf *result = ldns_dname_cat_clone(rd1, rd2); + + if (!result) + die(1, "not enought memory for a domain name"); + /* Why doesn't ldns_dname_cat_clone check this condition? */ + if (ldns_rdf_size(result) > LDNS_MAX_DOMAINLEN) + die(1, "'%s' is not a legal name (%s)\n", ldns_rdf2str(result), + ldns_get_errorstr_by_id(LDNS_STATUS_DOMAINNAME_OVERFLOW)); + return result; +} + +static bool +query(ldns_resolver *res, ldns_rdf *domain, ldns_pkt **pkt) { + ldns_status status; + ldns_pkt_rcode rcode; + int i, cnt; + + if (o_verbose) { + printf("Trying \""); + print_rdf_nodot(domain); + printf("\"\n"); + } + for (cnt = ldns_resolver_nameserver_count(res), i = 0; i < cnt; i++) { + status = ldns_resolver_send_to(pkt, res, domain, o_rrtype, + o_rrclass, o_recursive ? LDNS_RD : 0, o_ixfr_serial, i); + if (status != LDNS_STATUS_OK) { + *pkt = NULL; + continue; + } + if (ldns_pkt_tc(*pkt) && !ldns_resolver_usevc(res)) { + if (o_verbose) + printf(";; Truncated, retrying in TCP mode.\n"); + ldns_resolver_set_usevc(res, true); + status = ldns_resolver_send_to(pkt, res, domain, o_rrtype, + o_rrclass, o_recursive ? LDNS_RD : 0, o_ixfr_serial, i); + ldns_resolver_set_usevc(res, false); + if (status != LDNS_STATUS_OK) + continue; + } + rcode = ldns_pkt_get_rcode(*pkt); + if (o_ignore_servfail && rcode == LDNS_RCODE_SERVFAIL && cnt > 1) + continue; + return rcode == LDNS_RCODE_NOERROR; + } + if (*pkt == NULL) { + printf(";; connection timed out; no servers could be reached\n"); + exit(1); + } + return false; +} + +static ldns_rdf * +search(ldns_resolver *res, ldns_rdf *domain, ldns_pkt **pkt, bool absolute) { + ldns_rdf *dname, **searchlist; + int i, n; + + if (absolute && query(res, domain, pkt)) + return domain; + + if ((dname = ldns_resolver_domain(res)) != NULL) { + dname = safe_dname_cat_clone(domain, dname); + if (query(res, dname, pkt)) + return dname; + } + + searchlist = ldns_resolver_searchlist(res); + n = ldns_resolver_searchlist_count(res); + for (i = 0; i < n; i++) { + dname = safe_dname_cat_clone(domain, searchlist[i]); + if (query(res, dname, pkt)) + return dname; + } + + if (!absolute && query(res, domain, pkt)) + return domain; + + return NULL; +} + +static void +report(ldns_resolver *res, ldns_rdf *domain, ldns_pkt *pkt) { + ldns_pkt_rcode rcode; + + if (o_print_pkt_server) { + printf("Using domain server:\nName: %s\nAddress: ", o_server); + print_rdf(ldns_pkt_answerfrom(pkt)); + printf("#%d\nAliases: \n\n", ldns_resolver_port(res)); + o_print_pkt_server = false; + } + rcode = ldns_pkt_get_rcode(pkt); + if (rcode != LDNS_RCODE_NOERROR) { + printf("Host "); + print_rdf_nodot(domain); + printf(" not found: %d(", rcode); + print_rcode(rcode); + printf(")\n"); + } else { + if (o_verbose) { + print_pkt_verbose(pkt); + } else { + print_pkt_short(pkt, o_print_rr_server); + if (o_mode != M_DEFAULT_Q && + ldns_rr_list_rr_count(ldns_pkt_answer(pkt)) == 0) { + print_rdf_nodot(domain); + printf(" has no "); + print_rr_type(o_rrtype); + printf(" record\n"); + } + } + } + if (o_verbose) + print_received_line(res, pkt); +} + +static bool +doquery(ldns_resolver *res, ldns_rdf *domain) { + ldns_pkt *pkt; + bool q; + + q = query(res, domain, &pkt); + report(res, domain, pkt); + return q; +} + +static bool +doquery_filtered(ldns_resolver *res, ldns_rdf *domain) { + ldns_pkt *pkt; + bool q; + + q = query(res, domain, &pkt); + ldns_pkt_filter_answer(pkt, o_rrtype); + report(res, domain, pkt); + return q; +} + +static bool +dosearch(ldns_resolver *res, ldns_rdf *domain, bool absolute) { + ldns_pkt *pkt; + ldns_rdf *dname; + + dname = search(res, domain, &pkt, absolute); + report(res, dname != NULL ? dname : domain, pkt); + return o_mode != M_DEFAULT_Q ? (dname != NULL) : + (dname != NULL) && + (o_rrtype = LDNS_RR_TYPE_AAAA, doquery_filtered(res, dname)) && + (o_rrtype = LDNS_RR_TYPE_MX, doquery_filtered(res, dname)); +} + +static bool +doaxfr(ldns_resolver *res, ldns_rdf *domain, bool absolute) { + ldns_pkt *pkt; + ldns_rdf *dname; + ldns_rr_type rrtype; + + rrtype = o_rrtype; + o_rrtype = LDNS_RR_TYPE_AXFR; + dname = search(res, domain, &pkt, absolute); + ldns_pkt_filter_answer(pkt, rrtype); + report(res, dname != NULL ? dname : domain, pkt); + return dname != NULL; +} + +static bool +dosoa(ldns_resolver *res, ldns_rdf *domain, bool absolute) { + ldns_rr_list *answer, **nsaddrs; + ldns_rdf *dname, *addr; + ldns_pkt *pkt; + ldns_rr *rr; + size_t i, j, n, cnt; + + if ((dname = search(res, domain, &pkt, absolute)) == NULL) + return false; + + answer = ldns_pkt_answer(pkt); + cnt = ldns_rr_list_rr_count(answer); + nsaddrs = alloca(cnt*sizeof(*nsaddrs)); + for (n = 0, i = 0; i < cnt; i++) + if ((addr = ldns_rr_ns_nsdname(ldns_rr_list_rr(answer, i))) != NULL) + nsaddrs[n++] = ldns_get_rr_list_addr_by_name(res, + addr, LDNS_RR_CLASS_IN, 0); + + o_print_pkt_server = false; + o_recursive = false; + o_rrtype = LDNS_RR_TYPE_SOA; + for (i = 0; i < n; i++) { + cnt = ldns_rr_list_rr_count(nsaddrs[i]); + for (j = 0; j < cnt; j++) { + ldns_resolver_remove_nameservers(res); + rr = ldns_rr_list_rr(nsaddrs[i], j); + if ((ldns_resolver_ip6(res) == LDNS_RESOLV_INET && + ldns_rr_get_type(rr) == LDNS_RR_TYPE_AAAA) || + (ldns_resolver_ip6(res) == LDNS_RESOLV_INET6 && + ldns_rr_get_type(rr) == LDNS_RR_TYPE_A)) + continue; + if (ldns_resolver_push_nameserver_rr(res, rr) == LDNS_STATUS_OK) + /* bind9-host queries for domain, not dname here */ + doquery(res, dname); + } + } + return 0; +} + +static void +resolver_set_nameserver_hostname(ldns_resolver *res, const char *server) { + struct addrinfo hints, *ailist, *ai; + ldns_status status; + ldns_rdf *rdf; + int err; + + memset(&hints, 0, sizeof hints); + switch (ldns_resolver_ip6(res)) { + case LDNS_RESOLV_INET: hints.ai_family = PF_INET; break; + case LDNS_RESOLV_INET6: hints.ai_family = PF_INET6; break; + default: hints.ai_family = PF_UNSPEC; break; + } + hints.ai_socktype = SOCK_STREAM; + do err = getaddrinfo(server, NULL, &hints, &ailist); + while (err == EAI_AGAIN); + if (err != 0) + die(1, "couldn't get address for '%s': %s", server, gai_strerror(err)); + for (ai = ailist; ai != NULL; ai = ai->ai_next) { + if ((rdf = ldns_sockaddr_storage2rdf((void*)ai->ai_addr, NULL)) == NULL) + die(1, "couldn't allocate an rdf: %s", + ldns_get_errorstr_by_id(LDNS_STATUS_MEM_ERR)); + status = ldns_resolver_push_nameserver(res, rdf); + if (status != LDNS_STATUS_OK) + die(1, "couldn't push a nameserver address: %s", + ldns_get_errorstr_by_id(status)); + } +} + +static void +resolver_set_nameserver_str(ldns_resolver *res, const char *server) { + ldns_rdf *addr; + + ldns_resolver_remove_nameservers(res); + addr = ldns_rdf_new_addr_frm_str(server); + if (addr) { + if (ldns_resolver_push_nameserver(res, addr) != LDNS_STATUS_OK) + die(1, "couldn't push a nameserver address"); + } else + resolver_set_nameserver_hostname(res, server); +} + +int +main(int argc, char *argv[]) { + ldns_rdf *addr, *dname; + ldns_resolver *res; + ldns_status status; + struct timeval restimeout; + + parse_args(argc, argv); + + status = ldns_resolver_new_default(&res); + if (status != LDNS_STATUS_OK) + die(1, "error creating resolver: %s", ldns_get_errorstr_by_id(status)); + if (ldns_resolver_nameserver_count(res) == 0) + ldns_resolver_push_default_servers(res); + + ldns_resolver_set_usevc(res, o_tcp); + restimeout.tv_sec = o_timeout > 0 ? o_timeout : + o_tcp ? DEFAULT_TCP_TIMEOUT : DEFAULT_UDP_TIMEOUT; + restimeout.tv_usec = 0; + ldns_resolver_set_timeout(res, restimeout); + ldns_resolver_set_retry(res, o_retries+1); + ldns_resolver_set_ip6(res, o_ipversion); + ldns_resolver_set_defnames(res, false); + ldns_resolver_set_fallback(res, false); + + if (o_server) + resolver_set_nameserver_str(res, o_server); + + if (ldns_str2rdf_a(&addr, o_name) == LDNS_STATUS_OK) { + dname = ldns_rdf_reverse_a(addr, "in-addr.arpa"); + if (dname == NULL) + die(1, "can't reverse '%s': %s", o_name, + ldns_get_errorstr_by_id(LDNS_STATUS_MEM_ERR)); + o_mode = M_SINGLE_Q; + o_rrtype = LDNS_RR_TYPE_PTR; + return !doquery(res, dname); + } else if (ldns_str2rdf_aaaa(&addr, o_name) == LDNS_STATUS_OK) { + dname = ldns_rdf_reverse_aaaa(addr, o_ip6_int ? "ip6.int" : "ip6.arpa"); + if (dname == NULL) + die(1, "can't reverse '%s': %s", o_name, + ldns_get_errorstr_by_id(LDNS_STATUS_MEM_ERR)); + o_mode = M_SINGLE_Q; + o_rrtype = LDNS_RR_TYPE_PTR; + return !doquery(res, dname); + } + return !(o_mode == M_SOA ? dosoa : o_mode == M_AXFR ? doaxfr : dosearch) + (res, safe_str2rdf_dname(o_name), ndots(o_name) >= o_ndots); +} diff --git a/contrib/ldns/ldns/dnssec_verify.h b/contrib/ldns/ldns/dnssec_verify.h index 0c41e8c11b6..e8b1a910267 100644 --- a/contrib/ldns/ldns/dnssec_verify.h +++ b/contrib/ldns/ldns/dnssec_verify.h @@ -294,23 +294,6 @@ void ldns_dnssec_derive_trust_tree_dnskey_rrset_time( ldns_rr *cur_rr, ldns_rr *cur_sig_rr, time_t check_time); - -/** - * Sub function for derive_trust_tree that is used for DNSKEY rrsets - * - * \param[in] new_tree The trust tree that we are building - * \param[in] data_chain The data chain containing the data for the trust tree - * \param[in] cur_rr The currently relevant DNSKEY RR - * \param[in] cur_sig_rr The currently relevant signature - * \param[in] check_time the time for which the validation is performed - */ -void ldns_dnssec_derive_trust_tree_dnskey_rrset_time( - ldns_dnssec_trust_tree *new_tree, - ldns_dnssec_data_chain *data_chain, - ldns_rr *cur_rr, ldns_rr *cur_sig_rr, - time_t check_time); - - /** * Sub function for derive_trust_tree that is used for DS rrsets * diff --git a/contrib/ldns/ldns/util.h b/contrib/ldns/ldns/util.h index bb639b6a85e..b30fc374c76 100644 --- a/contrib/ldns/ldns/util.h +++ b/contrib/ldns/ldns/util.h @@ -72,7 +72,7 @@ ldns_read_uint16(const void *src) #ifdef ALLOW_UNALIGNED_ACCESSES return ntohs(*(uint16_t *) src); #else - uint8_t *p = (uint8_t *) src; + const uint8_t *p = (const uint8_t *) src; return ((uint16_t) p[0] << 8) | (uint16_t) p[1]; #endif } @@ -83,7 +83,7 @@ ldns_read_uint32(const void *src) #ifdef ALLOW_UNALIGNED_ACCESSES return ntohl(*(uint32_t *) src); #else - uint8_t *p = (uint8_t *) src; + const uint8_t *p = (const uint8_t *) src; return ( ((uint32_t) p[0] << 24) | ((uint32_t) p[1] << 16) | ((uint32_t) p[2] << 8) diff --git a/contrib/libcxxrt/exception.cc b/contrib/libcxxrt/exception.cc index c1cb243b105..d8489059961 100644 --- a/contrib/libcxxrt/exception.cc +++ b/contrib/libcxxrt/exception.cc @@ -715,7 +715,9 @@ static void report_failure(_Unwind_Reason_Code err, __cxa_exception *thrown_exce if (status == 0) { free(demangled); } // Print a back trace if no handler is found. // TODO: Make this optional +#ifndef __arm__ _Unwind_Backtrace(trace, 0); +#endif break; } std::terminate(); diff --git a/contrib/libcxxrt/unwind-itanium.h b/contrib/libcxxrt/unwind-itanium.h index 16b3eed6d70..a5c0e755ad0 100644 --- a/contrib/libcxxrt/unwind-itanium.h +++ b/contrib/libcxxrt/unwind-itanium.h @@ -80,7 +80,7 @@ struct _Unwind_Exception _Unwind_Exception_Cleanup_Fn exception_cleanup; unsigned long private_1; unsigned long private_2; - } __attribute__((__aligned__)); + }; extern _Unwind_Reason_Code _Unwind_RaiseException (struct _Unwind_Exception *); extern _Unwind_Reason_Code _Unwind_ForcedUnwind (struct _Unwind_Exception *, diff --git a/contrib/libexecinfo/backtrace.3 b/contrib/libexecinfo/backtrace.3 new file mode 100644 index 00000000000..fdd8cec5653 --- /dev/null +++ b/contrib/libexecinfo/backtrace.3 @@ -0,0 +1,155 @@ +.\" $NetBSD: backtrace.3,v 1.5 2013/08/22 17:08:43 christos Exp $ +.\" $FreeBSD$ +.\" +.\" Copyright (c) 2012 The NetBSD Foundation, Inc. +.\" All rights reserved. +.\" +.\" This code is derived from software contributed to The NetBSD Foundation +.\" by Christos Zoulas +.\" +.\" 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. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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. +.\" +.Dd August 23, 2013 +.Dt BACKTRACE 3 +.Os +.Sh NAME +.Nm backtrace +.Nd fill in the backtrace of the currently executing thread +.Sh LIBRARY +.Lb libexecinfo +.Sh SYNOPSIS +.In execinfo.h +.Ft size_t +.Fn backtrace "void **addrlist" "size_t len" +.Ft "char **" +.Fn backtrace_symbols "void * const *addrlist" "size_t len" +.Ft int +.Fn backtrace_symbols_fd "void * const *addrlist" "size_t len" "int fd" +.Ft "char **" +.Fn backtrace_symbols_fmt "void * const *addrlist" "size_t len" "const char *fmt" +.Ft int +.Fn backtrace_symbols_fmt_fd "void * const *addrlist" "size_t len" "const char *fmt" "int fd" +.Sh DESCRIPTION +The +.Fn backtrace +function places into the array pointed by +.Fa addrlist +the array of the values of the program counter for each frame called up to +.Fa len +frames. +The number of frames found (which can be fewer than +.Fa len ) +is returned. +.Pp +The +.Fn backtrace_symbols_fmt +function takes an array of previously filled addresses from +.Fn backtrace +in +.Fa addrlist +of +.Fa len +elements, and uses +.Fa fmt +to format them. +The formatting characters available are: +.Bl -tag -width a -offset indent +.It Dv a +The numeric address of each element as would be printed using %p. +.It Dv n +The name of the nearest function symbol (smaller than the address element) +as determined by +.Xr dladdr 3 +if the symbol was dynamic, or looked up in the executable if static and +the /proc filesystem is available to determine the executable path. +.It Dv d +The difference of the symbol address and the address element printed +using 0x%tx. +.It Dv D +The difference of the symbol addresss and the address element printed using ++0x%tx if non-zero, or nothing if zero. +.It Dv f +The filename of the symbol as determined by +.Xr dladdr 3 . +.El +.Pp +The array of formatted strings is returned as a contiguous memory address which +can be freed by a single +.Xr free 3 . +.Pp +The +.Fn backtrace_symbols +function is equivalent of calling +.Fn backtrace_symbols_fmt +with a format argument of +.Dv "%a <%n%D> at %f" +.Pp +The +.Fn backtrace_symbols_fd +and +.Fn backtrace_symbols_fmt_fd +are similar to the non _fd named functions, only instead of returning +an array or strings, they print a new-line separated array of strings in +fd, and return +.Dv 0 +on success and +.Dv \-1 +on failure. +.Sh RETURN VALUES +The +.Fn backtrace +function returns the number of elements that were filled in the backtrace. +The +.Fn backtrace_symbols +and +.Fn backtrace_symbols_fmt +return a string array on success, and +.Dv NULL +on failure, setting +.Va errno . +Diagnostic output may also be produced by the ELF symbol lookup functions. +.Sh SEE ALSO +.Xr dladdr 3 , +.Xr elf 3 +.Sh HISTORY +The +.Fn backtrace +library of functions first appeared in +.Nx 7.0 +and +.Fx 10.0 . +.Sh BUGS +.Bl -enum +.It +Errors should not be printed but communicated to the caller differently. +.It +Because these functions use +.Xr elf 3 +this is a separate library instead of being part of libc/libutil +so that no library dependencies are introduced. +.It +The Linux versions of the functions (there are no _fmt variants) use +.Ft int +instead of +.Ft size_t +arguments. +.El diff --git a/contrib/libexecinfo/backtrace.c b/contrib/libexecinfo/backtrace.c new file mode 100644 index 00000000000..756a9825561 --- /dev/null +++ b/contrib/libexecinfo/backtrace.c @@ -0,0 +1,250 @@ +/* $NetBSD: backtrace.c,v 1.3 2013/08/29 14:58:56 christos Exp $ */ + +/*- + * Copyright (c) 2012 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Christos Zoulas. + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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 +__RCSID("$NetBSD: backtrace.c,v 1.3 2013/08/29 14:58:56 christos Exp $"); + +#include +#include +#define _WITH_DPRINTF +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "execinfo.h" +#include "symtab.h" + +#ifdef __linux__ +#define SELF "/proc/self/exe" +#else +#include +#define SELF "/proc/curproc/file" +#endif + +static int +open_self(int flags) +{ + const char *pathname = SELF; +#ifdef KERN_PROC_PATHNAME + static const int name[] = { + CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1, + }; + char path[MAXPATHLEN]; + size_t len; + + len = sizeof(path); + if (sysctl(name, 4, path, &len, NULL, 0) != -1) + pathname = path; +#endif + return open(pathname, flags); +} + + +static int __printflike(4, 5) +rasprintf(char **buf, size_t *bufsiz, size_t offs, const char *fmt, ...) +{ + for (;;) { + size_t nbufsiz; + char *nbuf; + + if (*buf && offs < *bufsiz) { + va_list ap; + int len; + + va_start(ap, fmt); + len = vsnprintf(*buf + offs, *bufsiz - offs, fmt, ap); + va_end(ap); + + if (len < 0 || (size_t)len < *bufsiz - offs) + return len; + nbufsiz = MAX(*bufsiz + 512, (size_t)len + 1); + } else + nbufsiz = MAX(offs, *bufsiz) + 512; + + nbuf = realloc(*buf, nbufsiz); + if (nbuf == NULL) + return -1; + *buf = nbuf; + *bufsiz = nbufsiz; + } +} + +/* + * format specifiers: + * %a = address + * %n = symbol_name + * %d = symbol_address - address + * %D = if symbol_address == address "" else +%d + * %f = filename + */ +static ssize_t +format_string(char **buf, size_t *bufsiz, size_t offs, const char *fmt, + Dl_info *dli, const void *addr) +{ + ptrdiff_t diff = (const char *)addr - (const char *)dli->dli_saddr; + size_t o = offs; + int len; + + for (; *fmt; fmt++) { + if (*fmt != '%') + goto printone; + switch (*++fmt) { + case 'a': + len = rasprintf(buf, bufsiz, o, "%p", addr); + break; + case 'n': + len = rasprintf(buf, bufsiz, o, "%s", dli->dli_sname); + break; + case 'D': + if (diff) + len = rasprintf(buf, bufsiz, o, "+0x%tx", diff); + else + len = 0; + break; + case 'd': + len = rasprintf(buf, bufsiz, o, "0x%tx", diff); + break; + case 'f': + len = rasprintf(buf, bufsiz, o, "%s", dli->dli_fname); + break; + default: + printone: + len = rasprintf(buf, bufsiz, o, "%c", *fmt); + break; + } + if (len == -1) + return -1; + o += len; + } + return o - offs; +} + +static ssize_t +format_address(symtab_t *st, char **buf, size_t *bufsiz, size_t offs, + const char *fmt, const void *addr) +{ + Dl_info dli; + + memset(&dli, 0, sizeof(dli)); + (void)dladdr(addr, &dli); + if (st) + symtab_find(st, addr, &dli); + + if (dli.dli_sname == NULL) + dli.dli_sname = "???"; + if (dli.dli_fname == NULL) + dli.dli_fname = "???"; + if (dli.dli_saddr == NULL) + dli.dli_saddr = (void *)(intptr_t)addr; + + return format_string(buf, bufsiz, offs, fmt, &dli, addr); +} + +char ** +backtrace_symbols_fmt(void *const *trace, size_t len, const char *fmt) +{ + + static const size_t slen = sizeof(char *) + 64; /* estimate */ + char *ptr; + symtab_t *st; + int fd; + + if ((fd = open_self(O_RDONLY)) != -1) + st = symtab_create(fd, -1, STT_FUNC); + else + st = NULL; + + if ((ptr = calloc(len, slen)) == NULL) + goto out; + + size_t psize = len * slen; + size_t offs = len * sizeof(char *); + + /* We store only offsets in the first pass because of realloc */ + for (size_t i = 0; i < len; i++) { + ssize_t x; + ((char **)(void *)ptr)[i] = (void *)offs; + x = format_address(st, &ptr, &psize, offs, fmt, trace[i]); + if (x == -1) { + free(ptr); + ptr = NULL; + goto out; + } + offs += x; + ptr[offs++] = '\0'; + assert(offs < psize); + } + + /* Change offsets to pointers */ + for (size_t j = 0; j < len; j++) + ((char **)(void *)ptr)[j] += (intptr_t)ptr; + +out: + symtab_destroy(st); + if (fd != -1) + (void)close(fd); + + return (void *)ptr; +} + +int +backtrace_symbols_fd_fmt(void *const *trace, size_t len, int fd, + const char *fmt) +{ + char **s = backtrace_symbols_fmt(trace, len, fmt); + if (s == NULL) + return -1; + for (size_t i = 0; i < len; i++) + if (dprintf(fd, "%s\n", s[i]) < 0) + break; + free(s); + return 0; +} + +static const char fmt[] = "%a <%n%D> at %f"; + +char ** +backtrace_symbols(void *const *trace, size_t len) +{ + return backtrace_symbols_fmt(trace, len, fmt); +} + +int +backtrace_symbols_fd(void *const *trace, size_t len, int fd) +{ + return backtrace_symbols_fd_fmt(trace, len, fd, fmt); +} diff --git a/contrib/libexecinfo/builtin.c b/contrib/libexecinfo/builtin.c new file mode 100644 index 00000000000..3fc861958d1 --- /dev/null +++ b/contrib/libexecinfo/builtin.c @@ -0,0 +1,68 @@ +/* $NetBSD: builtin.c,v 1.1 2012/05/26 22:02:29 christos Exp $ */ + +/*- + * Copyright (c) 2012 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Christos Zoulas. + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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 +__RCSID("$NetBSD: builtin.c,v 1.1 2012/05/26 22:02:29 christos Exp $"); + +#include +#include + +#include "execinfo.h" + +#ifdef __MACHINE_STACK_GROWS_UP +#define BELOW > +#else +#define BELOW < +#endif + +#ifdef __lint__ +#define __builtin_frame_address(a) ((void *)a) +#endif + +struct frameinfo { + struct frameinfo *next; + void *return_address; +}; + +size_t +backtrace(void **trace, size_t len) +{ + const struct frameinfo *frame = __builtin_frame_address(0); + void *stack = &stack; + + for (size_t i = 0; i < len; i++) { + if ((const void *)frame BELOW stack) + return i; + trace[i] = frame->return_address; + frame = frame->next; + } + + return len; +} diff --git a/contrib/libexecinfo/execinfo.h b/contrib/libexecinfo/execinfo.h new file mode 100644 index 00000000000..22460967e83 --- /dev/null +++ b/contrib/libexecinfo/execinfo.h @@ -0,0 +1,45 @@ +/* $NetBSD: execinfo.h,v 1.2 2012/06/09 21:22:17 christos Exp $ */ +/* $FreeBSD$ */ + +/*- + * Copyright (c) 2012 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Christos Zoulas. + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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. + */ +#ifndef _EXECINFO_H_ +#define _EXECINFO_H_ + +#include + +__BEGIN_DECLS +size_t backtrace(void **, size_t); +char **backtrace_symbols(void *const *, size_t); +int backtrace_symbols_fd(void *const *, size_t, int); +char **backtrace_symbols_fmt(void *const *, size_t, const char *); +int backtrace_symbols_fd_fmt(void *const *, size_t, int, const char *); +__END_DECLS + +#endif /* _EXECINFO_H_ */ diff --git a/contrib/libexecinfo/symtab.c b/contrib/libexecinfo/symtab.c new file mode 100644 index 00000000000..92d92dc20bc --- /dev/null +++ b/contrib/libexecinfo/symtab.c @@ -0,0 +1,193 @@ +/* $NetBSD: symtab.c,v 1.2 2013/08/29 15:01:57 christos Exp $ */ + +/*- + * Copyright (c) 2012 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Christos Zoulas. + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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 +__RCSID("$NetBSD: symtab.c,v 1.2 2013/08/29 15:01:57 christos Exp $"); + +#include +#include +#include +#include +#include +#include + +#include +#include +#ifndef ELF_ST_BIND +#define ELF_ST_BIND(x) ((x) >> 4) +#endif +#ifndef ELF_ST_TYPE +#define ELF_ST_TYPE(x) (((unsigned int)x) & 0xf) +#endif + + +#include "symtab.h" + +struct symbol { + char *st_name; + uintptr_t st_value; + uintptr_t st_info; +}; + +struct symtab { + size_t nsymbols; + struct symbol *symbols; +}; + +static int +address_compare(const void *a, const void *b) +{ + const struct symbol *sa = a; + const struct symbol *sb = b; + return (int)(intmax_t)(sa->st_value - sb->st_value); +} + +void +symtab_destroy(symtab_t *s) +{ + if (s == NULL) + return; + for (size_t i = 0; i < s->nsymbols; i++) + free(s->symbols[i].st_name); + free(s->symbols); + free(s); +} + +symtab_t * +symtab_create(int fd, int bind, int type) +{ + Elf *elf; + symtab_t *st; + Elf_Scn *scn = NULL; + + if (elf_version(EV_CURRENT) == EV_NONE) { + warnx("Elf Library is out of date."); + return NULL; + } + + elf = elf_begin(fd, ELF_C_READ, NULL); + if (elf == NULL) { + warnx("Error opening elf file: %s", elf_errmsg(elf_errno())); + return NULL; + } + st = calloc(1, sizeof(*st)); + if (st == NULL) { + warnx("Error allocating symbol table"); + elf_end(elf); + return NULL; + } + + while ((scn = elf_nextscn(elf, scn)) != NULL) { + GElf_Shdr shdr; + Elf_Data *edata; + size_t ns; + struct symbol *s; + + gelf_getshdr(scn, &shdr); + if(shdr.sh_type != SHT_SYMTAB) + continue; + + edata = elf_getdata(scn, NULL); + ns = shdr.sh_size / shdr.sh_entsize; + s = calloc(ns, sizeof(*s)); + if (s == NULL) { + warn("Cannot allocate %zu symbols", ns); + goto out; + } + st->symbols = s; + + for (size_t i = 0; i < ns; i++) { + GElf_Sym sym; + gelf_getsym(edata, (int)i, &sym); + + if (bind != -1 && + (unsigned)bind != ELF_ST_BIND(sym.st_info)) + continue; + + if (type != -1 && + (unsigned)type != ELF_ST_TYPE(sym.st_info)) + continue; + + s->st_value = sym.st_value; + s->st_info = sym.st_info; + s->st_name = strdup( + elf_strptr(elf, shdr.sh_link, sym.st_name)); + if (s->st_name == NULL) + goto out; + s++; + } + st->nsymbols = s - st->symbols; + if (st->nsymbols == 0) { + warnx("No symbols found"); + goto out; + } + qsort(st->symbols, st->nsymbols, sizeof(*st->symbols), + address_compare); + elf_end(elf); + return st; + } +out: + symtab_destroy(st); + elf_end(elf); + return NULL; +} + + +int +symtab_find(const symtab_t *st, const void *p, Dl_info *dli) +{ + struct symbol *s = st->symbols; + size_t ns = st->nsymbols; + size_t hi = ns; + size_t lo = 0; + size_t mid = ns / 2; + uintptr_t dd, sd, me = (uintptr_t)p; + + for (;;) { + if (s[mid].st_value < me) + lo = mid; + else if (s[mid].st_value > me) + hi = mid; + else + break; + if (hi - lo == 1) { + mid = lo; + break; + } + mid = (hi + lo) / 2; + } + dd = me - (uintptr_t)dli->dli_saddr; + sd = me - s[mid].st_value; + if (dd > sd) { + dli->dli_saddr = (void *)s[mid].st_value; + dli->dli_sname = s[mid].st_name; + } + return 1; +} diff --git a/contrib/libexecinfo/symtab.h b/contrib/libexecinfo/symtab.h new file mode 100644 index 00000000000..d527a3cc472 --- /dev/null +++ b/contrib/libexecinfo/symtab.h @@ -0,0 +1,42 @@ +/* $NetBSD: symtab.h,v 1.1 2012/05/26 22:02:29 christos Exp $ */ + +/*- + * Copyright (c) 2012 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Christos Zoulas. + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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. + */ +#ifndef _SYMTAB_H_ +#define _SYMTAB_H_ + +__BEGIN_DECLS +typedef struct symtab symtab_t; + +void symtab_destroy(symtab_t *); +symtab_t * symtab_create(int, int, int); +int symtab_find(const symtab_t *, const void *, Dl_info *); +__END_DECLS + +#endif /* _SYMTAB_H_ */ diff --git a/contrib/libexecinfo/unwind.c b/contrib/libexecinfo/unwind.c new file mode 100644 index 00000000000..b9b785ff602 --- /dev/null +++ b/contrib/libexecinfo/unwind.c @@ -0,0 +1,72 @@ +/* $NetBSD: unwind.c,v 1.1 2012/05/26 22:02:29 christos Exp $ */ + +/*- + * Copyright (c) 2012 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Christos Zoulas. + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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 +#include +#include + +#include "unwind.h" +#include "execinfo.h" + +struct tracer_context { + void **arr; + size_t len; + size_t n; +}; + +static _Unwind_Reason_Code +tracer(struct _Unwind_Context *ctx, void *arg) +{ + struct tracer_context *t = arg; + if (t->n == (size_t)~0) { + /* Skip backtrace frame */ + t->n = 0; + return 0; + } + if (t->n < t->len) + t->arr[t->n++] = _Unwind_GetIP(ctx); + return 0; +} + +size_t +backtrace(void **arr, size_t len) +{ + struct tracer_context ctx; + + ctx.arr = arr; + ctx.len = len; + ctx.n = (size_t)~0; + + _Unwind_Backtrace(tracer, &ctx); + if (ctx.n != (size_t)~0 && ctx.n > 0) + ctx.arr[--ctx.n] = NULL; /* Skip frame below __start */ + + return ctx.n; +} diff --git a/contrib/libexecinfo/unwind.h b/contrib/libexecinfo/unwind.h new file mode 100644 index 00000000000..c9978b3d5df --- /dev/null +++ b/contrib/libexecinfo/unwind.h @@ -0,0 +1,68 @@ +/* $NetBSD: unwind.h,v 1.1 2012/05/26 22:02:29 christos Exp $ */ + +/*- + * Copyright (c) 2012 The NetBSD Foundation, Inc. + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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. + */ +#ifndef _UNWIND_H_ +#define _UNWIND_H_ + +__BEGIN_DECLS +struct _Unwind_Context; +struct _Unwind_Exception; +typedef int _Unwind_Reason_Code; +typedef void *_Unwind_Ptr; +typedef long _Unwind_Word; + +typedef _Unwind_Reason_Code + (*_Unwind_Trace_Fn)(struct _Unwind_Context *, void *); +#ifdef notyet +typedef _Unwind_Reason_Code + (*_Unwind_Stop_Fn)(struct _Unwind_Context *, void *); +#endif + +_Unwind_Reason_Code _Unwind_Backtrace(_Unwind_Trace_Fn, void *); +void _Unwind_DeleteException(struct _Unwind_Exception *); +void *_Unwind_FindEnclosingFunction(void *); +#ifdef notyet +_Unwind_Reason_Code _Unwind_ForcedUnwind(struct _Unwind_Exception *, + _Unwind_Stop_fn, void *); +#endif +_Unwind_Word _Unwind_GetCFA(struct _Unwind_Context *); +_Unwind_Ptr _Unwind_GetDataRelBase(struct _Unwind_Context *); +_Unwind_Word _Unwind_GetGR(struct _Unwind_Context *, int); +_Unwind_Ptr _Unwind_GetIP(struct _Unwind_Context *); +_Unwind_Ptr _Unwind_GetIPInfo(struct _Unwind_Context *, int *); +void *_Unwind_GetLanguageSpecificData( + struct _Unwind_Context *); +_Unwind_Ptr _Unwind_GetRegionStart(struct _Unwind_Context *); +_Unwind_Ptr _Unwind_GetTextRelBase(struct _Unwind_Context *); +_Unwind_Reason_Code _Unwind_RaiseException(struct _Unwind_Exception *); +void _Unwind_Resume(struct _Unwind_Exception *); +_Unwind_Reason_Code _Unwind_Resume_or_Rethrow(struct _Unwind_Exception *); +void _Unwind_SetGR(struct _Unwind_Context *, int, + _Unwind_Ptr); +void _Unwind_SetIP(struct _Unwind_Context *, _Unwind_Ptr); +__END_DECLS +#endif /* _UNWIND_H_ */ diff --git a/contrib/libexecinfo/unwind_arm_ehabi_stub.c b/contrib/libexecinfo/unwind_arm_ehabi_stub.c new file mode 100644 index 00000000000..6fe97161edf --- /dev/null +++ b/contrib/libexecinfo/unwind_arm_ehabi_stub.c @@ -0,0 +1,61 @@ +/*- + * Copyright (c) 2013 The NetBSD Foundation, Inc. + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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 +#include +#include "unwind.h" + +void _Unwind_VRS_Get(struct _Unwind_Context *, int, _Unwind_Word, int, void *); +void _Unwind_VRS_Set(struct _Unwind_Context *, int, _Unwind_Word, int, void *); + +_Unwind_Word +_Unwind_GetGR(struct _Unwind_Context *context, int regno) +{ + _Unwind_Word val; + _Unwind_VRS_Get(context, 0 /*_UVRSC_CORE*/, regno, 0 /*_UVRSD_UINT32*/, + &val); + + return val; +} + +_Unwind_Ptr +_Unwind_GetIP(struct _Unwind_Context *context) +{ + return (_Unwind_Ptr)(_Unwind_GetGR (context, 15) & ~(_Unwind_Word)1); +} + +_Unwind_Ptr +_Unwind_GetIPInfo(struct _Unwind_Context *context, int *p) +{ + *p = 0; + return _Unwind_GetIP(context); +} + +void +_Unwind_SetGR(struct _Unwind_Context *context, int reg, _Unwind_Ptr val) +{ + _Unwind_VRS_Set(context, 0 /*_UVRSC_CORE*/, reg, 0 /*_UVRSD_UINT32*/, + &val); +} diff --git a/contrib/llvm/include/llvm/MC/MCInstPrinter.h b/contrib/llvm/include/llvm/MC/MCInstPrinter.h index a18cbd94bbb..cb7225f81a8 100644 --- a/contrib/llvm/include/llvm/MC/MCInstPrinter.h +++ b/contrib/llvm/include/llvm/MC/MCInstPrinter.h @@ -21,6 +21,13 @@ class MCInstrInfo; class MCRegisterInfo; class StringRef; +namespace HexStyle { + enum Style { + C, ///< 0xff + Asm ///< 0ffh + }; +} + /// MCInstPrinter - This is an instance of a target assembly language printer /// that converts an MCInst to valid target assembly syntax. class MCInstPrinter { @@ -42,13 +49,16 @@ protected: /// True if we are printing immediates as hex. bool PrintImmHex; + /// Which style to use for printing hexadecimal values. + HexStyle::Style PrintHexStyle; + /// Utility function for printing annotations. void printAnnotation(raw_ostream &OS, StringRef Annot); public: MCInstPrinter(const MCAsmInfo &mai, const MCInstrInfo &mii, const MCRegisterInfo &mri) : CommentStream(0), MAI(mai), MII(mii), MRI(mri), AvailableFeatures(0), - UseMarkup(0), PrintImmHex(0) {} + UseMarkup(0), PrintImmHex(0), PrintHexStyle(HexStyle::C) {} virtual ~MCInstPrinter(); @@ -80,8 +90,16 @@ public: bool getPrintImmHex() const { return PrintImmHex; } void setPrintImmHex(bool Value) { PrintImmHex = Value; } + HexStyle::Style getPrintHexStyleHex() const { return PrintHexStyle; } + void setPrintImmHex(HexStyle::Style Value) { PrintHexStyle = Value; } + /// Utility function to print immediates in decimal or hex. - format_object1 formatImm(const int64_t Value) const; + format_object1 formatImm(const int64_t Value) const { return PrintImmHex ? formatHex(Value) : formatDec(Value); } + + /// Utility functions to print decimal/hexadecimal values. + format_object1 formatDec(const int64_t Value) const; + format_object1 formatHex(const int64_t Value) const; + format_object1 formatHex(const uint64_t Value) const; }; } // namespace llvm diff --git a/contrib/llvm/lib/MC/MCInstPrinter.cpp b/contrib/llvm/lib/MC/MCInstPrinter.cpp index 73f30ffb52a..c729d498044 100644 --- a/contrib/llvm/lib/MC/MCInstPrinter.cpp +++ b/contrib/llvm/lib/MC/MCInstPrinter.cpp @@ -52,10 +52,53 @@ StringRef MCInstPrinter::markup(StringRef a, StringRef b) const { return b; } -/// Utility function to print immediates in decimal or hex. -format_object1 MCInstPrinter::formatImm(const int64_t Value) const { - if (getPrintImmHex()) - return format("0x%" PRIx64, Value); - else - return format("%" PRId64, Value); +// For asm-style hex (e.g. 0ffh) the first digit always has to be a number. +static bool needsLeadingZero(uint64_t Value) +{ + while(Value) + { + uint64_t digit = (Value >> 60) & 0xf; + if (digit != 0) + return (digit >= 0xa); + Value <<= 4; + } + return false; +} + +format_object1 MCInstPrinter::formatDec(const int64_t Value) const { + return format("%" PRId64, Value); +} + +format_object1 MCInstPrinter::formatHex(const int64_t Value) const { + switch(PrintHexStyle) { + case HexStyle::C: + if (Value < 0) + return format("-0x%" PRIx64, -Value); + else + return format("0x%" PRIx64, Value); + case HexStyle::Asm: + if (Value < 0) { + if (needsLeadingZero((uint64_t)(-Value))) + return format("-0%" PRIx64 "h", -Value); + else + return format("-%" PRIx64 "h", -Value); + } else { + if (needsLeadingZero((uint64_t)(Value))) + return format("0%" PRIx64 "h", Value); + else + return format("%" PRIx64 "h", Value); + } + } +} + +format_object1 MCInstPrinter::formatHex(const uint64_t Value) const { + switch(PrintHexStyle) { + case HexStyle::C: + return format("0x%" PRIx64, Value); + case HexStyle::Asm: + if (needsLeadingZero(Value)) + return format("0%" PRIx64 "h", Value); + else + return format("%" PRIx64 "h", Value); + } } diff --git a/contrib/llvm/lib/Target/X86/InstPrinter/X86ATTInstPrinter.cpp b/contrib/llvm/lib/Target/X86/InstPrinter/X86ATTInstPrinter.cpp index e357710b20e..8195b7e4377 100644 --- a/contrib/llvm/lib/Target/X86/InstPrinter/X86ATTInstPrinter.cpp +++ b/contrib/llvm/lib/Target/X86/InstPrinter/X86ATTInstPrinter.cpp @@ -139,8 +139,7 @@ void X86ATTInstPrinter::printPCRelImm(const MCInst *MI, unsigned OpNo, const MCConstantExpr *BranchTarget = dyn_cast(Op.getExpr()); int64_t Address; if (BranchTarget && BranchTarget->EvaluateAsAbsolute(Address)) { - O << "0x"; - O.write_hex(Address); + O << formatHex((uint64_t)Address); } else { // Otherwise, just print the expression. diff --git a/contrib/llvm/lib/Target/X86/InstPrinter/X86IntelInstPrinter.cpp b/contrib/llvm/lib/Target/X86/InstPrinter/X86IntelInstPrinter.cpp index 141f4a4dd85..2228e6c0cf6 100644 --- a/contrib/llvm/lib/Target/X86/InstPrinter/X86IntelInstPrinter.cpp +++ b/contrib/llvm/lib/Target/X86/InstPrinter/X86IntelInstPrinter.cpp @@ -119,7 +119,7 @@ void X86IntelInstPrinter::printPCRelImm(const MCInst *MI, unsigned OpNo, raw_ostream &O) { const MCOperand &Op = MI->getOperand(OpNo); if (Op.isImm()) - O << Op.getImm(); + O << formatImm(Op.getImm()); else { assert(Op.isExpr() && "unknown pcrel immediate operand"); // If a symbolic branch target was added as a constant expression then print @@ -127,8 +127,7 @@ void X86IntelInstPrinter::printPCRelImm(const MCInst *MI, unsigned OpNo, const MCConstantExpr *BranchTarget = dyn_cast(Op.getExpr()); int64_t Address; if (BranchTarget && BranchTarget->EvaluateAsAbsolute(Address)) { - O << "0x"; - O.write_hex(Address); + O << formatHex((uint64_t)Address); } else { // Otherwise, just print the expression. @@ -148,7 +147,7 @@ void X86IntelInstPrinter::printOperand(const MCInst *MI, unsigned OpNo, if (Op.isReg()) { PrintRegName(O, getRegisterName(Op.getReg())); } else if (Op.isImm()) { - O << Op.getImm(); + O << formatImm((int64_t)Op.getImm()); } else { assert(Op.isExpr() && "unknown operand kind in printOperand"); O << *Op.getExpr(); @@ -200,7 +199,7 @@ void X86IntelInstPrinter::printMemReference(const MCInst *MI, unsigned Op, DispVal = -DispVal; } } - O << DispVal; + O << formatImm(DispVal); } } diff --git a/contrib/llvm/lib/Transforms/InstCombine/InstCombineSimplifyDemanded.cpp b/contrib/llvm/lib/Transforms/InstCombine/InstCombineSimplifyDemanded.cpp index 8add1ea618d..60d672bb332 100644 --- a/contrib/llvm/lib/Transforms/InstCombine/InstCombineSimplifyDemanded.cpp +++ b/contrib/llvm/lib/Transforms/InstCombine/InstCombineSimplifyDemanded.cpp @@ -845,21 +845,26 @@ Value *InstCombiner::SimplifyDemandedUseBits(Value *V, APInt DemandedMask, Value *InstCombiner::SimplifyShrShlDemandedBits(Instruction *Shr, Instruction *Shl, APInt DemandedMask, APInt &KnownZero, APInt &KnownOne) { - unsigned ShlAmt = cast(Shl->getOperand(1))->getZExtValue(); - unsigned ShrAmt = cast(Shr->getOperand(1))->getZExtValue(); + const APInt &ShlOp1 = cast(Shl->getOperand(1))->getValue(); + const APInt &ShrOp1 = cast(Shr->getOperand(1))->getValue(); + if (!ShlOp1 || !ShrOp1) + return 0; // Noop. + + Value *VarX = Shr->getOperand(0); + Type *Ty = VarX->getType(); + unsigned BitWidth = Ty->getIntegerBitWidth(); + if (ShlOp1.uge(BitWidth) || ShrOp1.uge(BitWidth)) + return 0; // Undef. + + unsigned ShlAmt = ShlOp1.getZExtValue(); + unsigned ShrAmt = ShrOp1.getZExtValue(); KnownOne.clearAllBits(); KnownZero = APInt::getBitsSet(KnownZero.getBitWidth(), 0, ShlAmt-1); KnownZero &= DemandedMask; - if (ShlAmt == 0 || ShrAmt == 0) - return 0; - - Value *VarX = Shr->getOperand(0); - Type *Ty = VarX->getType(); - - APInt BitMask1(APInt::getAllOnesValue(Ty->getIntegerBitWidth())); - APInt BitMask2(APInt::getAllOnesValue(Ty->getIntegerBitWidth())); + APInt BitMask1(APInt::getAllOnesValue(BitWidth)); + APInt BitMask2(APInt::getAllOnesValue(BitWidth)); bool isLshr = (Shr->getOpcode() == Instruction::LShr); BitMask1 = isLshr ? (BitMask1.lshr(ShrAmt) << ShlAmt) : diff --git a/contrib/llvm/tools/clang/lib/Driver/ToolChains.cpp b/contrib/llvm/tools/clang/lib/Driver/ToolChains.cpp index fffba0e4e50..d8d580b8b5c 100644 --- a/contrib/llvm/tools/clang/lib/Driver/ToolChains.cpp +++ b/contrib/llvm/tools/clang/lib/Driver/ToolChains.cpp @@ -1851,6 +1851,38 @@ bool FreeBSD::UseSjLjExceptions() const { } } +ToolChain::CXXStdlibType +FreeBSD::GetCXXStdlibType(const ArgList &Args) const { + if (Arg *A = Args.getLastArg(options::OPT_stdlib_EQ)) { + StringRef Value = A->getValue(); + if (Value == "libc++") + return ToolChain::CST_Libcxx; + if (Value == "libstdc++") + return ToolChain::CST_Libstdcxx; + getDriver().Diag(diag::err_drv_invalid_stdlib_name) + << A->getAsString(Args); + } + + return getTriple().getOSMajorVersion() >= 10 ? ToolChain::CST_Libcxx : + ToolChain::CST_Libstdcxx; +} + +void FreeBSD::AddClangCXXStdlibIncludeArgs(const ArgList &DriverArgs, + ArgStringList &CC1Args) const { + if (DriverArgs.hasArg(options::OPT_nostdlibinc) || + DriverArgs.hasArg(options::OPT_nostdincxx)) + return; + + if (GetCXXStdlibType(DriverArgs) == ToolChain::CST_Libcxx) + addSystemInclude(DriverArgs, CC1Args, + getDriver().SysRoot + "/usr/include/c++/v1"); + else + addSystemInclude(DriverArgs, CC1Args, + getDriver().SysRoot + "/usr/include/c++/4.2"); + return; + +} + /// NetBSD - NetBSD tool chain which can call as(1) and ld(1) directly. NetBSD::NetBSD(const Driver &D, const llvm::Triple& Triple, const ArgList &Args) diff --git a/contrib/llvm/tools/clang/lib/Driver/ToolChains.h b/contrib/llvm/tools/clang/lib/Driver/ToolChains.h index 3afd8dd228b..2b140c1cd0a 100644 --- a/contrib/llvm/tools/clang/lib/Driver/ToolChains.h +++ b/contrib/llvm/tools/clang/lib/Driver/ToolChains.h @@ -458,9 +458,14 @@ class LLVM_LIBRARY_VISIBILITY FreeBSD : public Generic_ELF { public: FreeBSD(const Driver &D, const llvm::Triple& Triple, const ArgList &Args); + virtual CXXStdlibType GetCXXStdlibType(const ArgList &Args) const; + virtual bool IsMathErrnoDefault() const { return false; } virtual bool IsObjCNonFragileABIDefault() const { return true; } + virtual void AddClangCXXStdlibIncludeArgs(const ArgList &DriverArgs, + ArgStringList &CC1Args) const; + virtual bool UseSjLjExceptions() const; protected: virtual Tool *buildAssembler() const; diff --git a/contrib/llvm/tools/clang/lib/Sema/SemaExpr.cpp b/contrib/llvm/tools/clang/lib/Sema/SemaExpr.cpp index dd05b822364..a9179fd4889 100644 --- a/contrib/llvm/tools/clang/lib/Sema/SemaExpr.cpp +++ b/contrib/llvm/tools/clang/lib/Sema/SemaExpr.cpp @@ -8884,14 +8884,16 @@ EmitDiagnosticForLogicalAndInLogicalOr(Sema &Self, SourceLocation OpLoc, /// 'true'. static bool EvaluatesAsTrue(Sema &S, Expr *E) { bool Res; - return E->EvaluateAsBooleanCondition(Res, S.getASTContext()) && Res; + return !E->isValueDependent() && + E->EvaluateAsBooleanCondition(Res, S.getASTContext()) && Res; } /// \brief Returns true if the given expression can be evaluated as a constant /// 'false'. static bool EvaluatesAsFalse(Sema &S, Expr *E) { bool Res; - return E->EvaluateAsBooleanCondition(Res, S.getASTContext()) && !Res; + return !E->isValueDependent() && + E->EvaluateAsBooleanCondition(Res, S.getASTContext()) && !Res; } /// \brief Look for '&&' in the left hand of a '||' expr. diff --git a/contrib/llvm/tools/clang/lib/Sema/SemaInit.cpp b/contrib/llvm/tools/clang/lib/Sema/SemaInit.cpp index 9e8936e17b8..a632f022f5d 100644 --- a/contrib/llvm/tools/clang/lib/Sema/SemaInit.cpp +++ b/contrib/llvm/tools/clang/lib/Sema/SemaInit.cpp @@ -774,6 +774,11 @@ void InitListChecker::CheckSubElementType(const InitializedEntity &Entity, InitListExpr *StructuredList, unsigned &StructuredIndex) { Expr *expr = IList->getInit(Index); + + if (ElemType->isReferenceType()) + return CheckReferenceType(Entity, IList, ElemType, Index, + StructuredList, StructuredIndex); + if (InitListExpr *SubInitList = dyn_cast(expr)) { if (!ElemType->isRecordType() || ElemType->isAggregateType()) { unsigned newIndex = 0; @@ -793,13 +798,13 @@ void InitListChecker::CheckSubElementType(const InitializedEntity &Entity, // C++ initialization is handled later. } - if (ElemType->isScalarType()) { + // FIXME: Need to handle atomic aggregate types with implicit init lists. + if (ElemType->isScalarType() || ElemType->isAtomicType()) return CheckScalarType(Entity, IList, ElemType, Index, StructuredList, StructuredIndex); - } else if (ElemType->isReferenceType()) { - return CheckReferenceType(Entity, IList, ElemType, Index, - StructuredList, StructuredIndex); - } + + assert((ElemType->isRecordType() || ElemType->isVectorType() || + ElemType->isArrayType()) && "Unexpected type"); if (const ArrayType *arrayType = SemaRef.Context.getAsArrayType(ElemType)) { // arrayType can be incomplete if we're initializing a flexible diff --git a/contrib/llvm/tools/lldb/LICENSE.TXT b/contrib/llvm/tools/lldb/LICENSE.TXT new file mode 100644 index 00000000000..1f0309419d7 --- /dev/null +++ b/contrib/llvm/tools/lldb/LICENSE.TXT @@ -0,0 +1,38 @@ +University of Illinois/NCSA +Open Source License + +Copyright (c) 2010 Apple Inc. +All rights reserved. + +Developed by: + + LLDB Team + + http://lldb.llvm.org/ + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal with +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: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimers. + + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimers in the + documentation and/or other materials provided with the distribution. + + * Neither the names of the LLDB Team, copyright holders, nor the names of + its contributors may be used to endorse or promote products derived from + this Software without specific prior written permission. + +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 +CONTRIBUTORS 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 WITH THE +SOFTWARE. + diff --git a/contrib/llvm/tools/lldb/docs/lldb.1 b/contrib/llvm/tools/lldb/docs/lldb.1 new file mode 100644 index 00000000000..4929e91f937 --- /dev/null +++ b/contrib/llvm/tools/lldb/docs/lldb.1 @@ -0,0 +1,119 @@ +.Dd 7 June, 2012 \" DATE +.Dt LLDB 1 \" Program name and manual section number +.Os Darwin \" Operating System +.Sh NAME \" Section Header - required - don't modify +.Nm lldb +.Nd The debugger +.Sh SYNOPSIS \" Section Header - required - don't modify +.Nm lldb +.Op Fl hvdexw +.Op Fl a Ar arch +.Op Fl l Ar script-language +.Op Fl s Ar lldb-commands +.Op Fl n Ar process-name +.Op Fl p Ar pid +.Ar [[--] ...] +.Sh DESCRIPTION \" Section Header - required - don't modify +.Nm +is the command line interface for the LLDB debugger library. +.Nm +can debug C, C++, Objective-C, and Objective-C++ programs. +.Pp +The following options are available: +.Bl -tag -width indent +.It Fl h, -help +Prints out the usage information for the +.Nm +debugger. The \fB\-\-help\fR text may be more up-to-date and +authoritative than the command line options described in this man +page. +.It Fl v, -version +Prints out the version number of the +.Nm +debugger. +.It Fl a, -arch Ar arch +Specifies which architecture +.Nm +will use when launching the specified program (assuming the provided +executable is built for multiple architectures.) +.It Fl f, -file Ar filename +Specifies the executable file that +.nm +will be launching / attaching to. +.It Fl n, -attach-name Ar process-name +Specifies the name of a currently-running process to attach to. +(or the name of a process to wait for if \fB\-w\fR is used.) +.It Fl w, -wait-for +When used in concert with \&\fB\-n process-name\-E\fR, indicates that +.Nm +should wait for a new process of that name to be started -- and attach +to it as early in the process-launch as possible. +.It Fl p, -attach-pid Ar pid +Specifies a currently running process that +.Nm +should attach to. +.It Fl l, -script-language Ar language +Tells the debugger to use the specified scripting language for +user-defined scripts, rather than the default. Valid scripting +languages that can be specified include Python, Perl, Ruby and Tcl. +Currently only the Python extensions have been implemented. +.It Fl d, -debug +Tells the debugger to print out extra information for debugging itself. +.It Fl s, -source Ar filename +Tells +.Nm +to read in and execute the file "\fBfilename\fR", which +should contain +.Nm +commands. +.It Fl e, -editor +Instructs +.Nm +to open source files using the host's "external editor" mechanism. +.It Fl x, -no-lldbinit +Do not automatically parse any '.lldbinit' files. +.Pp +(If you don't provide -f then the first argument will be the file to be debugged +so 'lldb -- [ []]' also works. +Remember to end the options with "--" if any of your arguments have a "-" in them.) +.El +.Sh USING LLDB +In +.Nm +there is a \fBhelp\fR command which can be used to find descriptions and examples of +all +.Nm +commands. To get help on "\fBbreakpoint set\fR" you would type "\fBhelp breakpoint set\fR". +.Pp +There is also an \fBapropos\fR command which will search the help text of all commands +for a given term -- this is useful for locating a command by topic. For instance, "\fBapropos breakpoint\fR" +will list any command that has the word \fBbreakpoint\fR in its help text. +.Sh FILES +.Nm +will read settings/aliases/commands from three files at startup, if they exist. +.Pp +First, it will read a \fB~/.lldbinit-\fIdebugger\fR command file. If you are using the +.Nm +command line interface, this is \fB~/.lldbinit-lldb\fR. If you are using +.Nm +inside a GUI debugger like +.Nm Xcode +this will be \fB~/.lldbinit-Xcode\fR. This is a useful place to put settings that you +want to apply only when a given +.Nm +command interpreter is used. +.Pp +Second, \fB~/.lldbinit\fR is read. +.Pp +Third, an \fR.lldbinit\fR file in the current working directory (where +.Nm +is started) will be read. +.Sh SEE ALSO +The LLDB project page http://lldb.llvm.org/ has many different resources for +.Nm +users -- the gdb/lldb command equivalence page http://lldb.llvm.org/lldb-gdb.html can +be especially helpful for users coming from gdb. +.Sh BUGS +To report bugs, please visit http://llvm.org/bugs/ +.Sh AUTHOR +Maintained by the LLDB Team, http://lldb.llvm.org/ diff --git a/contrib/llvm/tools/lldb/include/lldb/API/LLDB.h b/contrib/llvm/tools/lldb/include/lldb/API/LLDB.h new file mode 100644 index 00000000000..93bc3bc121e --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/API/LLDB.h @@ -0,0 +1,54 @@ +//===-- LLDB.h --------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_LLDB_h_ +#define LLDB_LLDB_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/API/SBDefines.h" +#include "lldb/API/SBAddress.h" +#include "lldb/API/SBBlock.h" +#include "lldb/API/SBBreakpoint.h" +#include "lldb/API/SBBreakpointLocation.h" +#include "lldb/API/SBBroadcaster.h" +#include "lldb/API/SBCommandInterpreter.h" +#include "lldb/API/SBCommandReturnObject.h" +#include "lldb/API/SBCommunication.h" +#include "lldb/API/SBCompileUnit.h" +#include "lldb/API/SBData.h" +#include "lldb/API/SBDebugger.h" +#include "lldb/API/SBDeclaration.h" +#include "lldb/API/SBError.h" +#include "lldb/API/SBEvent.h" +#include "lldb/API/SBFileSpec.h" +#include "lldb/API/SBFrame.h" +#include "lldb/API/SBFunction.h" +#include "lldb/API/SBHostOS.h" +#include "lldb/API/SBInputReader.h" +#include "lldb/API/SBInstruction.h" +#include "lldb/API/SBInstructionList.h" +#include "lldb/API/SBLineEntry.h" +#include "lldb/API/SBListener.h" +#include "lldb/API/SBModule.h" +#include "lldb/API/SBProcess.h" +#include "lldb/API/SBSourceManager.h" +#include "lldb/API/SBStream.h" +#include "lldb/API/SBStringList.h" +#include "lldb/API/SBSymbol.h" +#include "lldb/API/SBSymbolContext.h" +#include "lldb/API/SBTarget.h" +#include "lldb/API/SBThread.h" +#include "lldb/API/SBType.h" +#include "lldb/API/SBValue.h" +#include "lldb/API/SBValueList.h" + +#endif // LLDB_LLDB_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/API/SBAddress.h b/contrib/llvm/tools/lldb/include/lldb/API/SBAddress.h new file mode 100644 index 00000000000..c5e8cc685a4 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/API/SBAddress.h @@ -0,0 +1,150 @@ +//===-- SBAddress.h ---------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SBAddress_h_ +#define LLDB_SBAddress_h_ + +#include "lldb/API/SBDefines.h" +#include "lldb/API/SBModule.h" + +namespace lldb { + +class SBAddress +{ +public: + + SBAddress (); + + SBAddress (const lldb::SBAddress &rhs); + + SBAddress (lldb::SBSection section, lldb::addr_t offset); + + // Create an address by resolving a load address using the supplied target + SBAddress (lldb::addr_t load_addr, lldb::SBTarget &target); + + ~SBAddress (); + + const lldb::SBAddress & + operator = (const lldb::SBAddress &rhs); + + bool + IsValid () const; + + void + Clear (); + + addr_t + GetFileAddress () const; + + addr_t + GetLoadAddress (const lldb::SBTarget &target) const; + + void + SetAddress (lldb::SBSection section, lldb::addr_t offset); + + void + SetLoadAddress (lldb::addr_t load_addr, + lldb::SBTarget &target); + bool + OffsetAddress (addr_t offset); + + bool + GetDescription (lldb::SBStream &description); + + // The following queries can lookup symbol information for a given address. + // An address might refer to code or data from an existing module, or it + // might refer to something on the stack or heap. The following functions + // will only return valid values if the address has been resolved to a code + // or data address using "void SBAddress::SetLoadAddress(...)" or + // "lldb::SBAddress SBTarget::ResolveLoadAddress (...)". + lldb::SBSymbolContext + GetSymbolContext (uint32_t resolve_scope); + + + // The following functions grab individual objects for a given address and + // are less efficient if you want more than one symbol related objects. + // Use one of the following when you want multiple debug symbol related + // objects for an address: + // lldb::SBSymbolContext SBAddress::GetSymbolContext (uint32_t resolve_scope); + // lldb::SBSymbolContext SBTarget::ResolveSymbolContextForAddress (const SBAddress &addr, uint32_t resolve_scope); + // One or more bits from the SymbolContextItem enumerations can be logically + // OR'ed together to more efficiently retrieve multiple symbol objects. + + lldb::SBSection + GetSection (); + + lldb::addr_t + GetOffset (); + + lldb::SBModule + GetModule (); + + lldb::SBCompileUnit + GetCompileUnit (); + + lldb::SBFunction + GetFunction (); + + lldb::SBBlock + GetBlock (); + + lldb::SBSymbol + GetSymbol (); + + lldb::SBLineEntry + GetLineEntry (); + + lldb::AddressClass + GetAddressClass (); + +protected: + + friend class SBBlock; + friend class SBBreakpointLocation; + friend class SBFrame; + friend class SBFunction; + friend class SBLineEntry; + friend class SBInstruction; + friend class SBModule; + friend class SBSection; + friend class SBSymbol; + friend class SBSymbolContext; + friend class SBTarget; + friend class SBThread; + friend class SBValue; + + lldb_private::Address * + operator->(); + + const lldb_private::Address * + operator->() const; + + lldb_private::Address * + get (); + + lldb_private::Address & + ref(); + + const lldb_private::Address & + ref() const; + + SBAddress (const lldb_private::Address *lldb_object_ptr); + + void + SetAddress (const lldb_private::Address *lldb_object_ptr); + +private: + + std::unique_ptr m_opaque_ap; +}; + + +} // namespace lldb + +#endif // LLDB_SBAddress_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/API/SBBlock.h b/contrib/llvm/tools/lldb/include/lldb/API/SBBlock.h new file mode 100644 index 00000000000..b8e61fc6eb2 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/API/SBBlock.h @@ -0,0 +1,123 @@ +//===-- SBBlock.h -----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SBBlock_h_ +#define LLDB_SBBlock_h_ + +#include "lldb/API/SBDefines.h" +#include "lldb/API/SBFrame.h" +#include "lldb/API/SBTarget.h" +#include "lldb/API/SBValueList.h" + +namespace lldb { + +class SBBlock +{ +public: + + SBBlock (); + + SBBlock (const lldb::SBBlock &rhs); + + ~SBBlock (); + + const lldb::SBBlock & + operator = (const lldb::SBBlock &rhs); + + bool + IsInlined () const; + + bool + IsValid () const; + + const char * + GetInlinedName () const; + + lldb::SBFileSpec + GetInlinedCallSiteFile () const; + + uint32_t + GetInlinedCallSiteLine () const; + + uint32_t + GetInlinedCallSiteColumn () const; + + lldb::SBBlock + GetParent (); + + lldb::SBBlock + GetSibling (); + + lldb::SBBlock + GetFirstChild (); + + uint32_t + GetNumRanges (); + + lldb::SBAddress + GetRangeStartAddress (uint32_t idx); + + lldb::SBAddress + GetRangeEndAddress (uint32_t idx); + + uint32_t + GetRangeIndexForBlockAddress (lldb::SBAddress block_addr); + + lldb::SBValueList + GetVariables (lldb::SBFrame& frame, + bool arguments, + bool locals, + bool statics, + lldb::DynamicValueType use_dynamic); + + lldb::SBValueList + GetVariables (lldb::SBTarget& target, + bool arguments, + bool locals, + bool statics); + //------------------------------------------------------------------ + /// Get the inlined block that contains this block. + /// + /// @return + /// If this block is inlined, it will return this block, else + /// parent blocks will be searched to see if any contain this + /// block and are themselves inlined. An invalid SBBlock will + /// be returned if this block nor any parent blocks are inlined + /// function blocks. + //------------------------------------------------------------------ + lldb::SBBlock + GetContainingInlinedBlock (); + + bool + GetDescription (lldb::SBStream &description); + +private: + friend class SBAddress; + friend class SBFrame; + friend class SBFunction; + friend class SBSymbolContext; + + lldb_private::Block * + GetPtr (); + + void + SetPtr (lldb_private::Block *lldb_object_ptr); + + SBBlock (lldb_private::Block *lldb_object_ptr); + + void + AppendVariables (bool can_create, bool get_parent_variables, lldb_private::VariableList *var_list); + + lldb_private::Block *m_opaque_ptr; +}; + + +} // namespace lldb + +#endif // LLDB_SBBlock_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/API/SBBreakpoint.h b/contrib/llvm/tools/lldb/include/lldb/API/SBBreakpoint.h new file mode 100644 index 00000000000..be9c499798e --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/API/SBBreakpoint.h @@ -0,0 +1,175 @@ +//===-- SBBreakpoint.h ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SBBreakpoint_h_ +#define LLDB_SBBreakpoint_h_ + +#include "lldb/API/SBDefines.h" + +namespace lldb { + +class SBBreakpoint +{ +public: + + typedef bool (*BreakpointHitCallback) (void *baton, + SBProcess &process, + SBThread &thread, + lldb::SBBreakpointLocation &location); + + SBBreakpoint (); + + SBBreakpoint (const lldb::SBBreakpoint& rhs); + + ~SBBreakpoint(); + + const lldb::SBBreakpoint & + operator = (const lldb::SBBreakpoint& rhs); + + // Tests to see if the opaque breakpoint object in this object matches the + // opaque breakpoint object in "rhs". + bool + operator == (const lldb::SBBreakpoint& rhs); + + bool + operator != (const lldb::SBBreakpoint& rhs); + + break_id_t + GetID () const; + + bool + IsValid() const; + + void + ClearAllBreakpointSites (); + + lldb::SBBreakpointLocation + FindLocationByAddress (lldb::addr_t vm_addr); + + lldb::break_id_t + FindLocationIDByAddress (lldb::addr_t vm_addr); + + lldb::SBBreakpointLocation + FindLocationByID (lldb::break_id_t bp_loc_id); + + lldb::SBBreakpointLocation + GetLocationAtIndex (uint32_t index); + + void + SetEnabled (bool enable); + + bool + IsEnabled (); + + void + SetOneShot (bool one_shot); + + bool + IsOneShot () const; + + bool + IsInternal (); + + uint32_t + GetHitCount () const; + + void + SetIgnoreCount (uint32_t count); + + uint32_t + GetIgnoreCount () const; + + void + SetCondition (const char *condition); + + const char * + GetCondition (); + + void + SetThreadID (lldb::tid_t sb_thread_id); + + lldb::tid_t + GetThreadID (); + + void + SetThreadIndex (uint32_t index); + + uint32_t + GetThreadIndex() const; + + void + SetThreadName (const char *thread_name); + + const char * + GetThreadName () const; + + void + SetQueueName (const char *queue_name); + + const char * + GetQueueName () const; + + void + SetCallback (BreakpointHitCallback callback, void *baton); + + size_t + GetNumResolvedLocations() const; + + size_t + GetNumLocations() const; + + bool + GetDescription (lldb::SBStream &description); + + static bool + EventIsBreakpointEvent (const lldb::SBEvent &event); + + static lldb::BreakpointEventType + GetBreakpointEventTypeFromEvent (const lldb::SBEvent& event); + + static lldb::SBBreakpoint + GetBreakpointFromEvent (const lldb::SBEvent& event); + + static lldb::SBBreakpointLocation + GetBreakpointLocationAtIndexFromEvent (const lldb::SBEvent& event, uint32_t loc_idx); + + static uint32_t + GetNumBreakpointLocationsFromEvent (const lldb::SBEvent &event_sp); + + +private: + friend class SBBreakpointLocation; + friend class SBTarget; + + SBBreakpoint (const lldb::BreakpointSP &bp_sp); + + lldb_private::Breakpoint * + operator->() const; + + lldb_private::Breakpoint * + get() const; + + lldb::BreakpointSP & + operator *(); + + const lldb::BreakpointSP & + operator *() const; + + static bool + PrivateBreakpointHitCallback (void *baton, + lldb_private::StoppointCallbackContext *context, + lldb::user_id_t break_id, + lldb::user_id_t break_loc_id); + + lldb::BreakpointSP m_opaque_sp; +}; + +} // namespace lldb + +#endif // LLDB_SBBreakpoint_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/API/SBBreakpointLocation.h b/contrib/llvm/tools/lldb/include/lldb/API/SBBreakpointLocation.h new file mode 100644 index 00000000000..3b2ca2cf88e --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/API/SBBreakpointLocation.h @@ -0,0 +1,110 @@ +//===-- SBBreakpointLocation.h ----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SBBreakpointLocation_h_ +#define LLDB_SBBreakpointLocation_h_ + +#include "lldb/API/SBDefines.h" +#include "lldb/API/SBBreakpoint.h" + +namespace lldb { + +class SBBreakpointLocation +{ +public: + + SBBreakpointLocation (); + + SBBreakpointLocation (const lldb::SBBreakpointLocation &rhs); + + ~SBBreakpointLocation (); + + const lldb::SBBreakpointLocation & + operator = (const lldb::SBBreakpointLocation &rhs); + + break_id_t + GetID (); + + bool + IsValid() const; + + lldb::SBAddress + GetAddress (); + + lldb::addr_t + GetLoadAddress (); + + void + SetEnabled(bool enabled); + + bool + IsEnabled (); + + uint32_t + GetIgnoreCount (); + + void + SetIgnoreCount (uint32_t n); + + void + SetCondition (const char *condition); + + const char * + GetCondition (); + + void + SetThreadID (lldb::tid_t sb_thread_id); + + lldb::tid_t + GetThreadID (); + + void + SetThreadIndex (uint32_t index); + + uint32_t + GetThreadIndex() const; + + void + SetThreadName (const char *thread_name); + + const char * + GetThreadName () const; + + void + SetQueueName (const char *queue_name); + + const char * + GetQueueName () const; + + bool + IsResolved (); + + bool + GetDescription (lldb::SBStream &description, DescriptionLevel level); + + SBBreakpoint + GetBreakpoint (); + + SBBreakpointLocation (const lldb::BreakpointLocationSP &break_loc_sp); + +private: + friend class SBBreakpoint; +#ifndef LLDB_DISABLE_PYTHON + friend class lldb_private::ScriptInterpreterPython; +#endif + void + SetLocation (const lldb::BreakpointLocationSP &break_loc_sp); + + lldb::BreakpointLocationSP m_opaque_sp; + +}; + +} // namespace lldb + +#endif // LLDB_SBBreakpointLocation_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/API/SBBroadcaster.h b/contrib/llvm/tools/lldb/include/lldb/API/SBBroadcaster.h new file mode 100644 index 00000000000..7b32d85faa0 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/API/SBBroadcaster.h @@ -0,0 +1,97 @@ +//===-- SBBroadcaster.h -----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SBBroadcaster_h_ +#define LLDB_SBBroadcaster_h_ + +#include "lldb/API/SBDefines.h" + +namespace lldb { + +class SBBroadcaster +{ +public: + SBBroadcaster (); + + SBBroadcaster (const char *name); + + SBBroadcaster (const SBBroadcaster &rhs); + + const SBBroadcaster & + operator = (const SBBroadcaster &rhs); + + ~SBBroadcaster(); + + bool + IsValid () const; + + void + Clear (); + + void + BroadcastEventByType (uint32_t event_type, bool unique = false); + + void + BroadcastEvent (const lldb::SBEvent &event, bool unique = false); + + void + AddInitialEventsToListener (const lldb::SBListener &listener, uint32_t requested_events); + + uint32_t + AddListener (const lldb::SBListener &listener, uint32_t event_mask); + + const char * + GetName () const; + + bool + EventTypeHasListeners (uint32_t event_type); + + bool + RemoveListener (const lldb::SBListener &listener, uint32_t event_mask = UINT32_MAX); + + // This comparison is checking if the internal opaque pointer value + // is equal to that in "rhs". + bool + operator == (const lldb::SBBroadcaster &rhs) const; + + // This comparison is checking if the internal opaque pointer value + // is not equal to that in "rhs". + bool + operator != (const lldb::SBBroadcaster &rhs) const; + + // This comparison is checking if the internal opaque pointer value + // is less than that in "rhs" so SBBroadcaster objects can be contained + // in ordered containers. + bool + operator < (const lldb::SBBroadcaster &rhs) const; + +protected: + friend class SBCommandInterpreter; + friend class SBCommunication; + friend class SBEvent; + friend class SBListener; + friend class SBProcess; + friend class SBTarget; + + SBBroadcaster (lldb_private::Broadcaster *broadcaster, bool owns); + + lldb_private::Broadcaster * + get () const; + + void + reset (lldb_private::Broadcaster *broadcaster, bool owns); + +private: + lldb::BroadcasterSP m_opaque_sp; + lldb_private::Broadcaster *m_opaque_ptr; +}; + +} // namespace lldb + +#endif // LLDB_SBBroadcaster_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/API/SBCommandInterpreter.h b/contrib/llvm/tools/lldb/include/lldb/API/SBCommandInterpreter.h new file mode 100644 index 00000000000..9b2583cd85c --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/API/SBCommandInterpreter.h @@ -0,0 +1,193 @@ +//===-- SBCommandInterpreter.h ----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SBCommandInterpreter_h_ +#define LLDB_SBCommandInterpreter_h_ + +#include "lldb/API/SBDefines.h" +#include "lldb/API/SBDebugger.h" + +namespace lldb { + +class SBCommandInterpreter +{ +public: + enum + { + eBroadcastBitThreadShouldExit = (1 << 0), + eBroadcastBitResetPrompt = (1 << 1), + eBroadcastBitQuitCommandReceived = (1 << 2), // User entered quit + eBroadcastBitAsynchronousOutputData = (1 << 3), + eBroadcastBitAsynchronousErrorData = (1 << 4) + }; + + SBCommandInterpreter (const lldb::SBCommandInterpreter &rhs); + + const lldb::SBCommandInterpreter & + operator = (const lldb::SBCommandInterpreter &rhs); + + ~SBCommandInterpreter (); + + static const char * + GetArgumentTypeAsCString (const lldb::CommandArgumentType arg_type); + + static const char * + GetArgumentDescriptionAsCString (const lldb::CommandArgumentType arg_type); + + bool + IsValid() const; + + bool + CommandExists (const char *cmd); + + bool + AliasExists (const char *cmd); + + lldb::SBBroadcaster + GetBroadcaster (); + + static const char * + GetBroadcasterClass (); + + bool + HasCommands (); + + bool + HasAliases (); + + bool + HasAliasOptions (); + + lldb::SBProcess + GetProcess (); + + lldb::SBDebugger + GetDebugger (); + + lldb::SBCommand + AddMultiwordCommand (const char* name, const char* help); + + lldb::SBCommand + AddCommand (const char* name, lldb::SBCommandPluginInterface *impl, const char* help); + + void + SourceInitFileInHomeDirectory (lldb::SBCommandReturnObject &result); + + void + SourceInitFileInCurrentWorkingDirectory (lldb::SBCommandReturnObject &result); + + lldb::ReturnStatus + HandleCommand (const char *command_line, lldb::SBCommandReturnObject &result, bool add_to_history = false); + + // The pointer based interface is not useful in SWIG, since the cursor & last_char arguments are string pointers INTO current_line + // and you can't do that in a scripting language interface in general... + + // In either case, the way this works is that the you give it a line and cursor position in the line. The function + // will return the number of completions. The matches list will contain number_of_completions + 1 elements. The first + // element is the common substring after the cursor position for all the matches. The rest of the elements are the + // matches. The first element is useful if you are emulating the common shell behavior where the tab completes + // to the string that is common among all the matches, then you should first check if the first element is non-empty, + // and if so just insert it and move the cursor to the end of the insertion. The next tab will return an empty + // common substring, and a list of choices (if any), at which point you should display the choices and let the user + // type further to disambiguate. + + int + HandleCompletion (const char *current_line, + const char *cursor, + const char *last_char, + int match_start_point, + int max_return_elements, + lldb::SBStringList &matches); + + int + HandleCompletion (const char *current_line, + uint32_t cursor_pos, + int match_start_point, + int max_return_elements, + lldb::SBStringList &matches); + + // Catch commands before they execute by registering a callback that will + // get called when the command gets executed. This allows GUI or command + // line interfaces to intercept a command and stop it from happening + bool + SetCommandOverrideCallback (const char *command_name, + lldb::CommandOverrideCallback callback, + void *baton); + + SBCommandInterpreter (lldb_private::CommandInterpreter *interpreter_ptr = NULL); // Access using SBDebugger::GetCommandInterpreter(); + +protected: + + lldb_private::CommandInterpreter & + ref (); + + lldb_private::CommandInterpreter * + get (); + + void + reset (lldb_private::CommandInterpreter *); +private: + friend class SBDebugger; + + static void + InitializeSWIG (); + + lldb_private::CommandInterpreter *m_opaque_ptr; +}; + +class SBCommandPluginInterface +{ +public: + virtual bool + DoExecute (lldb::SBDebugger debugger, + char** command, + lldb::SBCommandReturnObject &result) + { + return false; + } + + virtual + ~SBCommandPluginInterface () + {} +}; + +class SBCommand +{ +public: + + SBCommand (); + + bool + IsValid (); + + const char* + GetName (); + + const char* + GetHelp (); + + lldb::SBCommand + AddMultiwordCommand (const char* name, const char* help = NULL); + + lldb::SBCommand + AddCommand (const char* name, lldb::SBCommandPluginInterface* impl, const char* help = NULL); + +private: + + friend class SBDebugger; + friend class SBCommandInterpreter; + + SBCommand (lldb::CommandObjectSP cmd_sp); + + lldb::CommandObjectSP m_opaque_sp; +}; + +} // namespace lldb + +#endif // LLDB_SBCommandInterpreter_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/API/SBCommandReturnObject.h b/contrib/llvm/tools/lldb/include/lldb/API/SBCommandReturnObject.h new file mode 100644 index 00000000000..f2d27480233 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/API/SBCommandReturnObject.h @@ -0,0 +1,133 @@ +//===-- SBCommandReturnObject.h ---------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SBCommandReturnObject_h_ +#define LLDB_SBCommandReturnObject_h_ + +#include + +#include "lldb/API/SBDefines.h" + +namespace lldb { + +class SBCommandReturnObject +{ +public: + + SBCommandReturnObject (); + + SBCommandReturnObject (const lldb::SBCommandReturnObject &rhs); + + const lldb::SBCommandReturnObject & + operator = (const lldb::SBCommandReturnObject &rhs); + + + SBCommandReturnObject (lldb_private::CommandReturnObject *ptr); + + lldb_private::CommandReturnObject * + Release (); + + ~SBCommandReturnObject (); + + bool + IsValid() const; + + const char * + GetOutput (); + + const char * + GetError (); + + size_t + PutOutput (FILE *fh); + + size_t + GetOutputSize (); + + size_t + GetErrorSize (); + + size_t + PutError (FILE *fh); + + void + Clear(); + + lldb::ReturnStatus + GetStatus(); + + void + SetStatus (lldb::ReturnStatus status); + + bool + Succeeded (); + + bool + HasResult (); + + void + AppendMessage (const char *message); + + void + AppendWarning (const char *message); + + bool + GetDescription (lldb::SBStream &description); + + void + SetImmediateOutputFile (FILE *fh); + + void + SetImmediateErrorFile (FILE *fh); + + void + PutCString(const char* string, int len = -1); + + size_t + Printf(const char* format, ...) __attribute__ ((format (printf, 2, 3))); + + const char * + GetOutput (bool only_if_no_immediate); + + const char * + GetError (bool only_if_no_immediate); + + void + SetError (lldb::SBError &error, + const char *fallback_error_cstr = NULL); + + void + SetError (const char* error_cstr); + +protected: + friend class SBCommandInterpreter; + friend class SBOptions; + + lldb_private::CommandReturnObject * + operator->() const; + + lldb_private::CommandReturnObject * + get() const; + + lldb_private::CommandReturnObject & + operator*() const; + + lldb_private::CommandReturnObject & + ref() const; + + void + SetLLDBObjectPtr (lldb_private::CommandReturnObject *ptr); + + private: + std::unique_ptr m_opaque_ap; +}; + +} // namespace lldb + +#endif // LLDB_SBCommandReturnObject_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/API/SBCommunication.h b/contrib/llvm/tools/lldb/include/lldb/API/SBCommunication.h new file mode 100644 index 00000000000..ecaaa3523c9 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/API/SBCommunication.h @@ -0,0 +1,99 @@ +//===-- SBCommunication.h ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SBCommunication_h_ +#define LLDB_SBCommunication_h_ + +#include "lldb/API/SBDefines.h" +#include "lldb/API/SBError.h" + +namespace lldb { + +class SBCommunication +{ +public: + enum { + eBroadcastBitDisconnected = (1 << 0), ///< Sent when the communications connection is lost. + eBroadcastBitReadThreadGotBytes = (1 << 1), ///< Sent by the read thread when bytes become available. + eBroadcastBitReadThreadDidExit = (1 << 2), ///< Sent by the read thread when it exits to inform clients. + eBroadcastBitReadThreadShouldExit = (1 << 3), ///< Sent by clients that need to cancel the read thread. + eBroadcastBitPacketAvailable = (1 << 4), ///< Sent when data received makes a complete packet. + eAllEventBits = 0xffffffff + }; + + typedef void (*ReadThreadBytesReceived) (void *baton, const void *src, size_t src_len); + + SBCommunication (); + SBCommunication (const char * broadcaster_name); + ~SBCommunication (); + + + bool + IsValid () const; + + lldb::SBBroadcaster + GetBroadcaster (); + + static const char *GetBroadcasterClass(); + + lldb::ConnectionStatus + AdoptFileDesriptor (int fd, bool owns_fd); + + lldb::ConnectionStatus + Connect (const char *url); + + lldb::ConnectionStatus + Disconnect (); + + bool + IsConnected () const; + + bool + GetCloseOnEOF (); + + void + SetCloseOnEOF (bool b); + + size_t + Read (void *dst, + size_t dst_len, + uint32_t timeout_usec, + lldb::ConnectionStatus &status); + + size_t + Write (const void *src, + size_t src_len, + lldb::ConnectionStatus &status); + + bool + ReadThreadStart (); + + bool + ReadThreadStop (); + + bool + ReadThreadIsRunning (); + + bool + SetReadThreadBytesReceivedCallback (ReadThreadBytesReceived callback, + void *callback_baton); + + +private: + + DISALLOW_COPY_AND_ASSIGN (SBCommunication); + + lldb_private::Communication *m_opaque; + bool m_opaque_owned; +}; + + +} // namespace lldb + +#endif // LLDB_SBCommunication_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/API/SBCompileUnit.h b/contrib/llvm/tools/lldb/include/lldb/API/SBCompileUnit.h new file mode 100644 index 00000000000..95af3d4722c --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/API/SBCompileUnit.h @@ -0,0 +1,116 @@ +//===-- SBCompileUnit.h -----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SBCompileUnit_h_ +#define LLDB_SBCompileUnit_h_ + +#include "lldb/API/SBDefines.h" +#include "lldb/API/SBFileSpec.h" + +namespace lldb { + +class SBCompileUnit +{ +public: + + SBCompileUnit (); + + SBCompileUnit (const lldb::SBCompileUnit &rhs); + + ~SBCompileUnit (); + + const lldb::SBCompileUnit & + operator = (const lldb::SBCompileUnit &rhs); + + bool + IsValid () const; + + lldb::SBFileSpec + GetFileSpec () const; + + uint32_t + GetNumLineEntries () const; + + lldb::SBLineEntry + GetLineEntryAtIndex (uint32_t idx) const; + + uint32_t + FindLineEntryIndex (uint32_t start_idx, + uint32_t line, + lldb::SBFileSpec *inline_file_spec) const; + + uint32_t + FindLineEntryIndex (uint32_t start_idx, + uint32_t line, + lldb::SBFileSpec *inline_file_spec, + bool exact) const; + + SBFileSpec + GetSupportFileAtIndex (uint32_t idx) const; + + uint32_t + GetNumSupportFiles () const; + + uint32_t + FindSupportFileIndex (uint32_t start_idx, const SBFileSpec &sb_file, bool full); + + //------------------------------------------------------------------ + /// Get all types matching \a type_mask from debug info in this + /// compile unit. + /// + /// @param[in] type_mask + /// A bitfield that consists of one or more bits logically OR'ed + /// together from the lldb::TypeClass enumeration. This allows + /// you to request only structure types, or only class, struct + /// and union types. Passing in lldb::eTypeClassAny will return + /// all types found in the debug information for this compile + /// unit. + /// + /// @return + /// A list of types in this compile unit that match \a type_mask + //------------------------------------------------------------------ + lldb::SBTypeList + GetTypes (uint32_t type_mask = lldb::eTypeClassAny); + + bool + operator == (const lldb::SBCompileUnit &rhs) const; + + bool + operator != (const lldb::SBCompileUnit &rhs) const; + + bool + GetDescription (lldb::SBStream &description); + +private: + friend class SBAddress; + friend class SBFrame; + friend class SBSymbolContext; + friend class SBModule; + + SBCompileUnit (lldb_private::CompileUnit *lldb_object_ptr); + + const lldb_private::CompileUnit * + operator->() const; + + const lldb_private::CompileUnit & + operator*() const; + + lldb_private::CompileUnit * + get (); + + void + reset (lldb_private::CompileUnit *lldb_object_ptr); + + lldb_private::CompileUnit *m_opaque_ptr; +}; + + +} // namespace lldb + +#endif // LLDB_SBCompileUnit_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/API/SBData.h b/contrib/llvm/tools/lldb/include/lldb/API/SBData.h new file mode 100644 index 00000000000..10c00224727 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/API/SBData.h @@ -0,0 +1,180 @@ +//===-- SBData.h -----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SBData_h_ +#define LLDB_SBData_h_ + +#include "lldb/API/SBDefines.h" + +namespace lldb { + +class SBData +{ +public: + + SBData (); + + SBData (const SBData &rhs); + + const SBData & + operator = (const SBData &rhs); + + ~SBData (); + + uint8_t + GetAddressByteSize (); + + void + SetAddressByteSize (uint8_t addr_byte_size); + + void + Clear (); + + bool + IsValid(); + + size_t + GetByteSize (); + + lldb::ByteOrder + GetByteOrder(); + + void + SetByteOrder (lldb::ByteOrder endian); + + float + GetFloat (lldb::SBError& error, lldb::offset_t offset); + + double + GetDouble (lldb::SBError& error, lldb::offset_t offset); + + long double + GetLongDouble (lldb::SBError& error, lldb::offset_t offset); + + lldb::addr_t + GetAddress (lldb::SBError& error, lldb::offset_t offset); + + uint8_t + GetUnsignedInt8 (lldb::SBError& error, lldb::offset_t offset); + + uint16_t + GetUnsignedInt16 (lldb::SBError& error, lldb::offset_t offset); + + uint32_t + GetUnsignedInt32 (lldb::SBError& error, lldb::offset_t offset); + + uint64_t + GetUnsignedInt64 (lldb::SBError& error, lldb::offset_t offset); + + int8_t + GetSignedInt8 (lldb::SBError& error, lldb::offset_t offset); + + int16_t + GetSignedInt16 (lldb::SBError& error, lldb::offset_t offset); + + int32_t + GetSignedInt32 (lldb::SBError& error, lldb::offset_t offset); + + int64_t + GetSignedInt64 (lldb::SBError& error, lldb::offset_t offset); + + const char* + GetString (lldb::SBError& error, lldb::offset_t offset); + + size_t + ReadRawData (lldb::SBError& error, + lldb::offset_t offset, + void *buf, + size_t size); + + bool + GetDescription (lldb::SBStream &description, lldb::addr_t base_addr = LLDB_INVALID_ADDRESS); + + // it would be nice to have SetData(SBError, const void*, size_t) when endianness and address size can be + // inferred from the existing DataExtractor, but having two SetData() signatures triggers a SWIG bug where + // the typemap isn't applied before resolving the overload, and thus the right function never gets called + void + SetData (lldb::SBError& error, const void *buf, size_t size, lldb::ByteOrder endian, uint8_t addr_size); + + // see SetData() for why we don't have Append(const void* buf, size_t size) + bool + Append (const SBData& rhs); + + static lldb::SBData + CreateDataFromCString (lldb::ByteOrder endian, uint32_t addr_byte_size, const char* data); + + // in the following CreateData*() and SetData*() prototypes, the two parameters array and array_len + // should not be renamed or rearranged, because doing so will break the SWIG typemap + static lldb::SBData + CreateDataFromUInt64Array (lldb::ByteOrder endian, uint32_t addr_byte_size, uint64_t* array, size_t array_len); + + static lldb::SBData + CreateDataFromUInt32Array (lldb::ByteOrder endian, uint32_t addr_byte_size, uint32_t* array, size_t array_len); + + static lldb::SBData + CreateDataFromSInt64Array (lldb::ByteOrder endian, uint32_t addr_byte_size, int64_t* array, size_t array_len); + + static lldb::SBData + CreateDataFromSInt32Array (lldb::ByteOrder endian, uint32_t addr_byte_size, int32_t* array, size_t array_len); + + static lldb::SBData + CreateDataFromDoubleArray (lldb::ByteOrder endian, uint32_t addr_byte_size, double* array, size_t array_len); + + bool + SetDataFromCString (const char* data); + + bool + SetDataFromUInt64Array (uint64_t* array, size_t array_len); + + bool + SetDataFromUInt32Array (uint32_t* array, size_t array_len); + + bool + SetDataFromSInt64Array (int64_t* array, size_t array_len); + + bool + SetDataFromSInt32Array (int32_t* array, size_t array_len); + + bool + SetDataFromDoubleArray (double* array, size_t array_len); + + +protected: + + // Mimic shared pointer... + lldb_private::DataExtractor * + get() const; + + lldb_private::DataExtractor * + operator->() const; + + lldb::DataExtractorSP & + operator*(); + + const lldb::DataExtractorSP & + operator*() const; + + SBData (const lldb::DataExtractorSP &data_sp); + + void + SetOpaque (const lldb::DataExtractorSP &data_sp); + +private: + friend class SBInstruction; + friend class SBProcess; + friend class SBSection; + friend class SBValue; + + lldb::DataExtractorSP m_opaque_sp; +}; + + +} // namespace lldb + +#endif // LLDB_SBData_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/API/SBDebugger.h b/contrib/llvm/tools/lldb/include/lldb/API/SBDebugger.h new file mode 100644 index 00000000000..518cbf67c93 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/API/SBDebugger.h @@ -0,0 +1,339 @@ +//===-- SBDebugger.h --------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SBDebugger_h_ +#define LLDB_SBDebugger_h_ + +#include "lldb/API/SBDefines.h" +#include + +namespace lldb { + +class SBDebugger +{ +public: + + static void + Initialize(); + + static void + Terminate(); + + // Deprecated, use the one that takes a source_init_files bool. + static lldb::SBDebugger + Create(); + + static lldb::SBDebugger + Create(bool source_init_files); + + static lldb::SBDebugger + Create(bool source_init_files, lldb::LogOutputCallback log_callback, void *baton); + + static void + Destroy (lldb::SBDebugger &debugger); + + static void + MemoryPressureDetected (); + + SBDebugger(); + + SBDebugger(const lldb::SBDebugger &rhs); + + SBDebugger(const lldb::DebuggerSP &debugger_sp); + + lldb::SBDebugger & + operator = (const lldb::SBDebugger &rhs); + + ~SBDebugger(); + + bool + IsValid() const; + + void + Clear (); + + void + SetAsync (bool b); + + bool + GetAsync (); + + void + SkipLLDBInitFiles (bool b); + + void + SkipAppInitFiles (bool b); + + void + SetInputFileHandle (FILE *f, bool transfer_ownership); + + void + SetOutputFileHandle (FILE *f, bool transfer_ownership); + + void + SetErrorFileHandle (FILE *f, bool transfer_ownership); + + FILE * + GetInputFileHandle (); + + FILE * + GetOutputFileHandle (); + + FILE * + GetErrorFileHandle (); + + void + SaveInputTerminalState(); + + void + RestoreInputTerminalState(); + + lldb::SBCommandInterpreter + GetCommandInterpreter (); + + void + HandleCommand (const char *command); + + lldb::SBListener + GetListener (); + + void + HandleProcessEvent (const lldb::SBProcess &process, + const lldb::SBEvent &event, + FILE *out, + FILE *err); + + lldb::SBTarget + CreateTarget (const char *filename, + const char *target_triple, + const char *platform_name, + bool add_dependent_modules, + lldb::SBError& error); + + lldb::SBTarget + CreateTargetWithFileAndTargetTriple (const char *filename, + const char *target_triple); + + lldb::SBTarget + CreateTargetWithFileAndArch (const char *filename, + const char *archname); + + lldb::SBTarget + CreateTarget (const char *filename); + + // Return true if target is deleted from the target list of the debugger. + bool + DeleteTarget (lldb::SBTarget &target); + + lldb::SBTarget + GetTargetAtIndex (uint32_t idx); + + uint32_t + GetIndexOfTarget (lldb::SBTarget target); + + lldb::SBTarget + FindTargetWithProcessID (pid_t pid); + + lldb::SBTarget + FindTargetWithFileAndArch (const char *filename, + const char *arch); + + uint32_t + GetNumTargets (); + + lldb::SBTarget + GetSelectedTarget (); + + void + SetSelectedTarget (SBTarget& target); + + lldb::SBSourceManager + GetSourceManager (); + + // REMOVE: just for a quick fix, need to expose platforms through + // SBPlatform from this class. + lldb::SBError + SetCurrentPlatform (const char *platform_name); + + bool + SetCurrentPlatformSDKRoot (const char *sysroot); + + // FIXME: Once we get the set show stuff in place, the driver won't need + // an interface to the Set/Get UseExternalEditor. + bool + SetUseExternalEditor (bool input); + + bool + GetUseExternalEditor (); + + bool + SetUseColor (bool use_color); + + bool + GetUseColor () const; + + static bool + GetDefaultArchitecture (char *arch_name, size_t arch_name_len); + + static bool + SetDefaultArchitecture (const char *arch_name); + + lldb::ScriptLanguage + GetScriptingLanguage (const char *script_language_name); + + static const char * + GetVersionString (); + + static const char * + StateAsCString (lldb::StateType state); + + static bool + StateIsRunningState (lldb::StateType state); + + static bool + StateIsStoppedState (lldb::StateType state); + + bool + EnableLog (const char *channel, const char **categories); + + void + SetLoggingCallback (lldb::LogOutputCallback log_callback, void *baton); + + // DEPRECATED + void + DispatchInput (void* baton, + const void* data, + size_t data_len); + + void + DispatchInput (const void *data, size_t data_len); + + void + DispatchInputInterrupt (); + + void + DispatchInputEndOfFile (); + + void + PushInputReader (lldb::SBInputReader &reader); + + void + NotifyTopInputReader (lldb::InputReaderAction notification); + + bool + InputReaderIsTopReader (const lldb::SBInputReader &reader); + + const char * + GetInstanceName (); + + static SBDebugger + FindDebuggerWithID (int id); + + static lldb::SBError + SetInternalVariable (const char *var_name, const char *value, const char *debugger_instance_name); + + static lldb::SBStringList + GetInternalVariableValue (const char *var_name, const char *debugger_instance_name); + + bool + GetDescription (lldb::SBStream &description); + + uint32_t + GetTerminalWidth () const; + + void + SetTerminalWidth (uint32_t term_width); + + lldb::user_id_t + GetID (); + + const char * + GetPrompt() const; + + void + SetPrompt (const char *prompt); + + lldb::ScriptLanguage + GetScriptLanguage() const; + + void + SetScriptLanguage (lldb::ScriptLanguage script_lang); + + bool + GetCloseInputOnEOF () const; + + void + SetCloseInputOnEOF (bool b); + + SBTypeCategory + GetCategory (const char* category_name); + + SBTypeCategory + CreateCategory (const char* category_name); + + bool + DeleteCategory (const char* category_name); + + uint32_t + GetNumCategories (); + + SBTypeCategory + GetCategoryAtIndex (uint32_t); + + SBTypeCategory + GetDefaultCategory(); + + SBTypeFormat + GetFormatForType (SBTypeNameSpecifier); + +#ifndef LLDB_DISABLE_PYTHON + SBTypeSummary + GetSummaryForType (SBTypeNameSpecifier); +#endif + + SBTypeFilter + GetFilterForType (SBTypeNameSpecifier); + +#ifndef LLDB_DISABLE_PYTHON + SBTypeSynthetic + GetSyntheticForType (SBTypeNameSpecifier); +#endif + +private: + + friend class SBCommandInterpreter; + friend class SBInputReader; + friend class SBListener; + friend class SBProcess; + friend class SBSourceManager; + friend class SBTarget; + + lldb::SBTarget + FindTargetWithLLDBProcess (const lldb::ProcessSP &processSP); + + void + reset (const lldb::DebuggerSP &debugger_sp); + + lldb_private::Debugger * + get () const; + + lldb_private::Debugger & + ref () const; + + const lldb::DebuggerSP & + get_sp () const; + + lldb::DebuggerSP m_opaque_sp; + +}; // class SBDebugger + + +} // namespace lldb + +#endif // LLDB_SBDebugger_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/API/SBDeclaration.h b/contrib/llvm/tools/lldb/include/lldb/API/SBDeclaration.h new file mode 100644 index 00000000000..190026c0d2d --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/API/SBDeclaration.h @@ -0,0 +1,89 @@ +//===-- SBDeclaration.h -------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SBDeclaration_h_ +#define LLDB_SBDeclaration_h_ + +#include "lldb/API/SBDefines.h" +#include "lldb/API/SBFileSpec.h" + +namespace lldb { + + class SBDeclaration + { + public: + + SBDeclaration (); + + SBDeclaration (const lldb::SBDeclaration &rhs); + + ~SBDeclaration (); + + const lldb::SBDeclaration & + operator = (const lldb::SBDeclaration &rhs); + + bool + IsValid () const; + + lldb::SBFileSpec + GetFileSpec () const; + + uint32_t + GetLine () const; + + uint32_t + GetColumn () const; + + void + SetFileSpec (lldb::SBFileSpec filespec); + + void + SetLine (uint32_t line); + + void + SetColumn (uint32_t column); + + bool + operator == (const lldb::SBDeclaration &rhs) const; + + bool + operator != (const lldb::SBDeclaration &rhs) const; + + bool + GetDescription (lldb::SBStream &description); + + protected: + + lldb_private::Declaration * + get (); + + private: + friend class SBValue; + + const lldb_private::Declaration * + operator->() const; + + lldb_private::Declaration & + ref(); + + const lldb_private::Declaration & + ref() const; + + SBDeclaration (const lldb_private::Declaration *lldb_object_ptr); + + void + SetDeclaration (const lldb_private::Declaration &lldb_object_ref); + + std::unique_ptr m_opaque_ap; + }; + + +} // namespace lldb + +#endif // LLDB_SBDeclaration_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/API/SBDefines.h b/contrib/llvm/tools/lldb/include/lldb/API/SBDefines.h new file mode 100644 index 00000000000..2cdf92170d8 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/API/SBDefines.h @@ -0,0 +1,84 @@ +//===-- SBDefines.h ---------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SBDefines_h_ +#define LLDB_SBDefines_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes + +#include "lldb/lldb-defines.h" +#include "lldb/lldb-enumerations.h" +#include "lldb/lldb-forward.h" +#include "lldb/lldb-types.h" +#include "lldb/lldb-versioning.h" + +// Forward Declarations + +namespace lldb { + +class SBAddress; +class SBBlock; +class SBBreakpoint; +class SBBreakpointLocation; +class SBBroadcaster; +class SBCommand; +class SBCommandInterpreter; +class SBCommandPluginInterface; +class SBCommandReturnObject; +class SBCommunication; +class SBCompileUnit; +class SBData; +class SBDebugger; +class SBDeclaration; +class SBError; +class SBEvent; +class SBEventList; +class SBExpressionOptions; +class SBFileSpec; +class SBFileSpecList; +class SBFrame; +class SBFunction; +class SBHostOS; +class SBInputReader; +class SBInstruction; +class SBInstructionList; +class SBLineEntry; +class SBListener; +class SBModule; +class SBModuleSpec; +class SBModuleSpecList; +class SBProcess; +class SBSourceManager; +class SBStream; +class SBStringList; +class SBSymbol; +class SBSymbolContext; +class SBSymbolContextList; +class SBTarget; +class SBThread; +class SBType; +class SBTypeCategory; +class SBTypeFilter; +class SBTypeFormat; +class SBTypeNameSpecifier; +class SBTypeSummary; +#ifndef LLDB_DISABLE_PYTHON +class SBTypeSynthetic; +#endif +class SBTypeList; +class SBValue; +class SBValueList; +class SBWatchpoint; + +} + +#endif // LLDB_SBDefines_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/API/SBError.h b/contrib/llvm/tools/lldb/include/lldb/API/SBError.h new file mode 100644 index 00000000000..a6d3dacb454 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/API/SBError.h @@ -0,0 +1,106 @@ +//===-- SBError.h -----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SBError_h_ +#define LLDB_SBError_h_ + +#include "lldb/API/SBDefines.h" + +namespace lldb { + +class SBError { +public: + SBError (); + + SBError (const lldb::SBError &rhs); + + ~SBError(); + + const SBError & + operator =(const lldb::SBError &rhs); + + const char * + GetCString () const; + + void + Clear (); + + bool + Fail () const; + + bool + Success () const; + + uint32_t + GetError () const; + + lldb::ErrorType + GetType () const; + + void + SetError (uint32_t err, lldb::ErrorType type); + + void + SetErrorToErrno (); + + void + SetErrorToGenericError (); + + void + SetErrorString (const char *err_str); + + int + SetErrorStringWithFormat (const char *format, ...) __attribute__ ((format (printf, 2, 3))); + + bool + IsValid () const; + + bool + GetDescription (lldb::SBStream &description); + +protected: + + friend class SBCommandReturnObject; + friend class SBData; + friend class SBDebugger; + friend class SBCommunication; + friend class SBHostOS; + friend class SBInputReader; + friend class SBProcess; + friend class SBThread; + friend class SBTarget; + friend class SBValue; + friend class SBWatchpoint; + + lldb_private::Error * + get(); + + lldb_private::Error * + operator->(); + + const lldb_private::Error & + operator*() const; + + lldb_private::Error & + ref(); + + void + SetError (const lldb_private::Error &lldb_error); + +private: + std::unique_ptr m_opaque_ap; + + void + CreateIfNeeded (); +}; + + +} // namespace lldb + +#endif // LLDB_SBError_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/API/SBEvent.h b/contrib/llvm/tools/lldb/include/lldb/API/SBEvent.h new file mode 100644 index 00000000000..6cb975a1ff7 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/API/SBEvent.h @@ -0,0 +1,102 @@ +//===-- SBEvent.h -----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SBEvent_h_ +#define LLDB_SBEvent_h_ + +#include "lldb/API/SBDefines.h" + +#include +#include + + +namespace lldb { + +class SBBroadcaster; + +class SBEvent +{ +public: + SBEvent(); + + SBEvent (const lldb::SBEvent &rhs); + + // Make an event that contains a C string. + SBEvent (uint32_t event, const char *cstr, uint32_t cstr_len); + + ~SBEvent(); + + const SBEvent & + operator = (const lldb::SBEvent &rhs); + + bool + IsValid() const; + + const char * + GetDataFlavor (); + + uint32_t + GetType () const; + + lldb::SBBroadcaster + GetBroadcaster () const; + + const char * + GetBroadcasterClass () const; + + bool + BroadcasterMatchesPtr (const lldb::SBBroadcaster *broadcaster); + + bool + BroadcasterMatchesRef (const lldb::SBBroadcaster &broadcaster); + + void + Clear(); + + static const char * + GetCStringFromEvent (const lldb::SBEvent &event); + + bool + GetDescription (lldb::SBStream &description); + + bool + GetDescription (lldb::SBStream &description) const; + +protected: + friend class SBListener; + friend class SBBroadcaster; + friend class SBBreakpoint; + friend class SBDebugger; + friend class SBProcess; + friend class SBThread; + friend class SBWatchpoint; + + SBEvent (lldb::EventSP &event_sp); + + lldb::EventSP & + GetSP () const; + + void + reset (lldb::EventSP &event_sp); + + void + reset (lldb_private::Event* event); + + lldb_private::Event * + get () const; + +private: + + mutable lldb::EventSP m_event_sp; + mutable lldb_private::Event *m_opaque_ptr; +}; + +} // namespace lldb + +#endif // LLDB_SBEvent_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/API/SBExpressionOptions.h b/contrib/llvm/tools/lldb/include/lldb/API/SBExpressionOptions.h new file mode 100644 index 00000000000..eed9ed528be --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/API/SBExpressionOptions.h @@ -0,0 +1,89 @@ +//===-- SBEvent.h -----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SBExpressionOptions_h_ +#define LLDB_SBExpressionOptions_h_ + +#include "lldb/API/SBDefines.h" + +#include + +namespace lldb { + + +class SBExpressionOptions +{ +public: + SBExpressionOptions(); + + SBExpressionOptions (const lldb::SBExpressionOptions &rhs); + + ~SBExpressionOptions(); + + const SBExpressionOptions & + operator = (const lldb::SBExpressionOptions &rhs); + + bool + GetCoerceResultToId () const; + + void + SetCoerceResultToId (bool coerce = true); + + bool + GetUnwindOnError () const; + + void + SetUnwindOnError (bool unwind = true); + + bool + GetIgnoreBreakpoints () const; + + void + SetIgnoreBreakpoints (bool ignore = true); + + lldb::DynamicValueType + GetFetchDynamicValue () const; + + void + SetFetchDynamicValue (lldb::DynamicValueType dynamic = lldb::eDynamicCanRunTarget); + + uint32_t + GetTimeoutInMicroSeconds () const; + + void + SetTimeoutInMicroSeconds (uint32_t timeout = 0); + + bool + GetTryAllThreads () const; + + void + SetTryAllThreads (bool run_others = true); + +protected: + + SBExpressionOptions (lldb_private::EvaluateExpressionOptions &expression_options); + + lldb_private::EvaluateExpressionOptions * + get () const; + + lldb_private::EvaluateExpressionOptions & + ref () const; + + friend class SBFrame; + friend class SBValue; + friend class SBTarget; + +private: + // This auto_pointer is made in the constructor and is always valid. + mutable std::unique_ptr m_opaque_ap; +}; + +} // namespace lldb + +#endif // LLDB_SBExpressionOptions_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/API/SBFileSpec.h b/contrib/llvm/tools/lldb/include/lldb/API/SBFileSpec.h new file mode 100644 index 00000000000..e44abe4759c --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/API/SBFileSpec.h @@ -0,0 +1,96 @@ +//===-- SBFileSpec.h --------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SBFileSpec_h_ +#define LLDB_SBFileSpec_h_ + +#include "lldb/API/SBDefines.h" + +namespace lldb { + +class SBFileSpec +{ +public: + SBFileSpec (); + + SBFileSpec (const lldb::SBFileSpec &rhs); + + SBFileSpec (const char *path);// Deprected, use SBFileSpec (const char *path, bool resolve) + + SBFileSpec (const char *path, bool resolve); + + ~SBFileSpec (); + + const SBFileSpec & + operator = (const lldb::SBFileSpec &rhs); + + bool + IsValid() const; + + bool + Exists () const; + + bool + ResolveExecutableLocation (); + + const char * + GetFilename() const; + + const char * + GetDirectory() const; + + uint32_t + GetPath (char *dst_path, size_t dst_len) const; + + static int + ResolvePath (const char *src_path, char *dst_path, size_t dst_len); + + bool + GetDescription (lldb::SBStream &description) const; + +private: + friend class SBAttachInfo; + friend class SBBlock; + friend class SBCompileUnit; + friend class SBDeclaration; + friend class SBFileSpecList; + friend class SBHostOS; + friend class SBLaunchInfo; + friend class SBLineEntry; + friend class SBModule; + friend class SBModuleSpec; + friend class SBProcess; + friend class SBSourceManager; + friend class SBThread; + friend class SBTarget; + + SBFileSpec (const lldb_private::FileSpec& fspec); + + void + SetFileSpec (const lldb_private::FileSpec& fspec); + + const lldb_private::FileSpec * + operator->() const; + + const lldb_private::FileSpec * + get() const; + + const lldb_private::FileSpec & + operator*() const; + + const lldb_private::FileSpec & + ref() const; + + std::unique_ptr m_opaque_ap; +}; + + +} // namespace lldb + +#endif // LLDB_SBFileSpec_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/API/SBFileSpecList.h b/contrib/llvm/tools/lldb/include/lldb/API/SBFileSpecList.h new file mode 100644 index 00000000000..734e7d4d35c --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/API/SBFileSpecList.h @@ -0,0 +1,72 @@ +//===-- SBFileSpecList.h --------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SBFileSpecList_h_ +#define LLDB_SBFileSpecList_h_ + +#include "lldb/API/SBDefines.h" + +namespace lldb { + +class SBFileSpecList +{ +public: + SBFileSpecList (); + + SBFileSpecList (const lldb::SBFileSpecList &rhs); + + ~SBFileSpecList (); + + const SBFileSpecList & + operator = (const lldb::SBFileSpecList &rhs); + + uint32_t + GetSize () const; + + bool + GetDescription (SBStream &description) const; + + void + Append (const SBFileSpec &sb_file); + + bool + AppendIfUnique (const SBFileSpec &sb_file); + + void + Clear(); + + uint32_t + FindFileIndex (uint32_t idx, const SBFileSpec &sb_file, bool full); + + const SBFileSpec + GetFileSpecAtIndex (uint32_t idx) const; + +private: + +friend class SBTarget; + + const lldb_private::FileSpecList * + operator->() const; + + const lldb_private::FileSpecList * + get() const; + + const lldb_private::FileSpecList & + operator*() const; + + const lldb_private::FileSpecList & + ref() const; + + std::unique_ptr m_opaque_ap; +}; + + +} // namespace lldb + +#endif // LLDB_SBFileSpecList_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/API/SBFrame.h b/contrib/llvm/tools/lldb/include/lldb/API/SBFrame.h new file mode 100644 index 00000000000..4ae38c13bed --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/API/SBFrame.h @@ -0,0 +1,242 @@ +//===-- SBFrame.h -----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SBFrame_h_ +#define LLDB_SBFrame_h_ + +#include "lldb/API/SBDefines.h" +#include "lldb/API/SBValueList.h" + +namespace lldb { + +class SBFrame +{ +public: + SBFrame (); + + SBFrame (const lldb::SBFrame &rhs); + + const lldb::SBFrame & + operator =(const lldb::SBFrame &rhs); + + ~SBFrame(); + + bool + IsEqual (const lldb::SBFrame &that) const; + + bool + IsValid() const; + + uint32_t + GetFrameID () const; + + lldb::addr_t + GetPC () const; + + bool + SetPC (lldb::addr_t new_pc); + + lldb::addr_t + GetSP () const; + + lldb::addr_t + GetFP () const; + + lldb::SBAddress + GetPCAddress () const; + + lldb::SBSymbolContext + GetSymbolContext (uint32_t resolve_scope) const; + + lldb::SBModule + GetModule () const; + + lldb::SBCompileUnit + GetCompileUnit () const; + + lldb::SBFunction + GetFunction () const; + + lldb::SBSymbol + GetSymbol () const; + + /// Gets the deepest block that contains the frame PC. + /// + /// See also GetFrameBlock(). + lldb::SBBlock + GetBlock () const; + + /// Get the appropriate function name for this frame. Inlined functions in + /// LLDB are represented by Blocks that have inlined function information, so + /// just looking at the SBFunction or SBSymbol for a frame isn't enough. + /// This function will return the appriopriate function, symbol or inlined + /// function name for the frame. + /// + /// This function returns: + /// - the name of the inlined function (if there is one) + /// - the name of the concrete function (if there is one) + /// - the name of the symbol (if there is one) + /// - NULL + /// + /// See also IsInlined(). + const char * + GetFunctionName(); + + /// Return true if this frame represents an inlined function. + /// + /// See also GetFunctionName(). + bool + IsInlined(); + + /// The version that doesn't supply a 'use_dynamic' value will use the + /// target's default. + lldb::SBValue + EvaluateExpression (const char *expr); + + lldb::SBValue + EvaluateExpression (const char *expr, lldb::DynamicValueType use_dynamic); + + lldb::SBValue + EvaluateExpression (const char *expr, lldb::DynamicValueType use_dynamic, bool unwind_on_error); + + lldb::SBValue + EvaluateExpression (const char *expr, const SBExpressionOptions &options); + + /// Gets the lexical block that defines the stack frame. Another way to think + /// of this is it will return the block that contains all of the variables + /// for a stack frame. Inlined functions are represented as SBBlock objects + /// that have inlined function information: the name of the inlined function, + /// where it was called from. The block that is returned will be the first + /// block at or above the block for the PC (SBFrame::GetBlock()) that defines + /// the scope of the frame. When a function contains no inlined functions, + /// this will be the top most lexical block that defines the function. + /// When a function has inlined functions and the PC is currently + /// in one of those inlined functions, this method will return the inlined + /// block that defines this frame. If the PC isn't currently in an inlined + /// function, the lexical block that defines the function is returned. + lldb::SBBlock + GetFrameBlock () const; + + lldb::SBLineEntry + GetLineEntry () const; + + lldb::SBThread + GetThread () const; + + const char * + Disassemble () const; + + void + Clear(); + + bool + operator == (const lldb::SBFrame &rhs) const; + + bool + operator != (const lldb::SBFrame &rhs) const; + + /// The version that doesn't supply a 'use_dynamic' value will use the + /// target's default. + lldb::SBValueList + GetVariables (bool arguments, + bool locals, + bool statics, + bool in_scope_only); + + lldb::SBValueList + GetVariables (bool arguments, + bool locals, + bool statics, + bool in_scope_only, + lldb::DynamicValueType use_dynamic); + + lldb::SBValueList + GetRegisters (); + + lldb::SBValue + FindRegister (const char *name); + + /// The version that doesn't supply a 'use_dynamic' value will use the + /// target's default. + lldb::SBValue + FindVariable (const char *var_name); + + lldb::SBValue + FindVariable (const char *var_name, lldb::DynamicValueType use_dynamic); + + // Find a value for a variable expression path like "rect.origin.x" or + // "pt_ptr->x", "*self", "*this->obj_ptr". The returned value is _not_ + // and expression result and is not a constant object like + // SBFrame::EvaluateExpression(...) returns, but a child object of + // the variable value. + lldb::SBValue + GetValueForVariablePath (const char *var_expr_cstr, + DynamicValueType use_dynamic); + + /// The version that doesn't supply a 'use_dynamic' value will use the + /// target's default. + lldb::SBValue + GetValueForVariablePath (const char *var_path); + + /// Find variables, register sets, registers, or persistent variables using + /// the frame as the scope. + /// + /// NB. This function does not look up ivars in the function object pointer. + /// To do that use GetValueForVariablePath. + /// + /// The version that doesn't supply a 'use_dynamic' value will use the + /// target's default. + lldb::SBValue + FindValue (const char *name, ValueType value_type); + + lldb::SBValue + FindValue (const char *name, ValueType value_type, lldb::DynamicValueType use_dynamic); + + /// Find and watch a variable using the frame as the scope. + /// It returns an SBValue, similar to FindValue() method, if find-and-watch + /// operation succeeds. Otherwise, an invalid SBValue is returned. + /// You can use LLDB_WATCH_TYPE_READ | LLDB_WATCH_TYPE_WRITE for 'rw' watch. + lldb::SBValue + WatchValue (const char *name, ValueType value_type, uint32_t watch_type); + + /// Find and watch the location pointed to by a variable using the frame as + /// the scope. + /// It returns an SBValue, similar to FindValue() method, if find-and-watch + /// operation succeeds. Otherwise, an invalid SBValue is returned. + /// You can use LLDB_WATCH_TYPE_READ | LLDB_WATCH_TYPE_WRITE for 'rw' watch. + lldb::SBValue + WatchLocation (const char *name, ValueType value_type, uint32_t watch_type, size_t size); + + bool + GetDescription (lldb::SBStream &description); + + SBFrame (const lldb::StackFrameSP &lldb_object_sp); + +protected: + + friend class SBBlock; + friend class SBInstruction; + friend class SBThread; + friend class SBValue; +#ifndef LLDB_DISABLE_PYTHON + friend class lldb_private::ScriptInterpreterPython; +#endif + + lldb::StackFrameSP + GetFrameSP() const; + + void + SetFrameSP (const lldb::StackFrameSP &lldb_object_sp); + + lldb::ExecutionContextRefSP m_opaque_sp; +}; + +} // namespace lldb + +#endif // LLDB_SBFrame_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/API/SBFunction.h b/contrib/llvm/tools/lldb/include/lldb/API/SBFunction.h new file mode 100644 index 00000000000..49a3847efbe --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/API/SBFunction.h @@ -0,0 +1,93 @@ +//===-- SBFunction.h --------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SBFunction_h_ +#define LLDB_SBFunction_h_ + +#include "lldb/API/SBDefines.h" +#include "lldb/API/SBAddress.h" +#include "lldb/API/SBInstructionList.h" + +namespace lldb { + +class SBFunction +{ +public: + + SBFunction (); + + SBFunction (const lldb::SBFunction &rhs); + + const lldb::SBFunction & + operator = (const lldb::SBFunction &rhs); + + ~SBFunction (); + + bool + IsValid () const; + + const char * + GetName() const; + + const char * + GetMangledName () const; + + lldb::SBInstructionList + GetInstructions (lldb::SBTarget target); + + lldb::SBInstructionList + GetInstructions (lldb::SBTarget target, const char *flavor); + + lldb::SBAddress + GetStartAddress (); + + lldb::SBAddress + GetEndAddress (); + + uint32_t + GetPrologueByteSize (); + + lldb::SBType + GetType (); + + lldb::SBBlock + GetBlock (); + + bool + operator == (const lldb::SBFunction &rhs) const; + + bool + operator != (const lldb::SBFunction &rhs) const; + + bool + GetDescription (lldb::SBStream &description); + +protected: + + lldb_private::Function * + get (); + + void + reset (lldb_private::Function *lldb_object_ptr); + +private: + friend class SBAddress; + friend class SBFrame; + friend class SBSymbolContext; + + SBFunction (lldb_private::Function *lldb_object_ptr); + + + lldb_private::Function *m_opaque_ptr; +}; + + +} // namespace lldb + +#endif // LLDB_SBFunction_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/API/SBHostOS.h b/contrib/llvm/tools/lldb/include/lldb/API/SBHostOS.h new file mode 100644 index 00000000000..52754ea4e82 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/API/SBHostOS.h @@ -0,0 +1,57 @@ +//===-- SBHostOS.h ----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SBHostOS_h_ +#define LLDB_SBHostOS_h_ + +#include "lldb/API/SBDefines.h" +#include "lldb/API/SBFileSpec.h" + +namespace lldb { + +class SBHostOS +{ +public: + + static lldb::SBFileSpec + GetProgramFileSpec (); + + static lldb::SBFileSpec + GetLLDBPythonPath (); + + static void + ThreadCreated (const char *name); + + static lldb::thread_t + ThreadCreate (const char *name, + void *(*thread_function)(void *), + void *thread_arg, + lldb::SBError *err); + + static bool + ThreadCancel (lldb::thread_t thread, + lldb::SBError *err); + + static bool + ThreadDetach (lldb::thread_t thread, + lldb::SBError *err); + static bool + ThreadJoin (lldb::thread_t thread, + void **result, + lldb::SBError *err); + + +private: + +}; + + +} // namespace lldb + +#endif // LLDB_SBHostOS_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/API/SBInputReader.h b/contrib/llvm/tools/lldb/include/lldb/API/SBInputReader.h new file mode 100644 index 00000000000..61f7de4fde4 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/API/SBInputReader.h @@ -0,0 +1,97 @@ +//===-- SBInputReader.h -----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SBInputReader_h_ +#define LLDB_SBInputReader_h_ + +#include "lldb/API/SBDefines.h" + +namespace lldb { + +class SBInputReader +{ +public: + + typedef size_t (*Callback) (void *baton, + SBInputReader *reader, + InputReaderAction notification, + const char *bytes, + size_t bytes_len); + + SBInputReader (); + + SBInputReader (const lldb::InputReaderSP &reader_sp); + + SBInputReader (const lldb::SBInputReader &rhs); + + ~SBInputReader (); + + + SBError + Initialize (SBDebugger &debugger, + Callback callback, + void *callback_baton, + lldb::InputReaderGranularity granularity, + const char *end_token, + const char *prompt, + bool echo); + + bool + IsValid () const; + + const lldb::SBInputReader & + operator = (const lldb::SBInputReader &rhs); + + bool + IsActive () const; + + bool + IsDone () const; + + void + SetIsDone (bool value); + + InputReaderGranularity + GetGranularity (); + +protected: + friend class SBDebugger; + + lldb_private::InputReader * + operator->() const; + + lldb::InputReaderSP & + operator *(); + + const lldb::InputReaderSP & + operator *() const; + + lldb_private::InputReader * + get() const; + + lldb_private::InputReader & + ref() const; + +private: + + static size_t + PrivateCallback (void *baton, + lldb_private::InputReader &reader, + lldb::InputReaderAction notification, + const char *bytes, + size_t bytes_len); + + lldb::InputReaderSP m_opaque_sp; + Callback m_callback_function; + void *m_callback_baton; +}; + +} // namespace lldb + +#endif // LLDB_SBInputReader_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/API/SBInstruction.h b/contrib/llvm/tools/lldb/include/lldb/API/SBInstruction.h new file mode 100644 index 00000000000..aad2d87f4f3 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/API/SBInstruction.h @@ -0,0 +1,94 @@ +//===-- SBInstruction.h -----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SBInstruction_h_ +#define LLDB_SBInstruction_h_ + +#include "lldb/API/SBDefines.h" +#include "lldb/API/SBData.h" + +#include + +// There's a lot to be fixed here, but need to wait for underlying insn implementation +// to be revised & settle down first. + +namespace lldb { + +class SBInstruction +{ +public: + + SBInstruction (); + + SBInstruction (const SBInstruction &rhs); + + const SBInstruction & + operator = (const SBInstruction &rhs); + + ~SBInstruction (); + + bool + IsValid(); + + SBAddress + GetAddress(); + + lldb::AddressClass + GetAddressClass (); + + const char * + GetMnemonic (lldb::SBTarget target); + + const char * + GetOperands (lldb::SBTarget target); + + const char * + GetComment (lldb::SBTarget target); + + lldb::SBData + GetData (lldb::SBTarget target); + + size_t + GetByteSize (); + + bool + DoesBranch (); + + void + Print (FILE *out); + + bool + GetDescription (lldb::SBStream &description); + + bool + EmulateWithFrame (lldb::SBFrame &frame, uint32_t evaluate_options); + + bool + DumpEmulation (const char * triple); // triple is to specify the architecture, e.g. 'armv6' or 'armv7-apple-ios' + + bool + TestEmulation (lldb::SBStream &output_stream, const char *test_file); + +protected: + friend class SBInstructionList; + + SBInstruction (const lldb::InstructionSP &inst_sp); + + void + SetOpaque (const lldb::InstructionSP &inst_sp); + +private: + + lldb::InstructionSP m_opaque_sp; +}; + + +} // namespace lldb + +#endif // LLDB_SBInstruction_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/API/SBInstructionList.h b/contrib/llvm/tools/lldb/include/lldb/API/SBInstructionList.h new file mode 100644 index 00000000000..944e144a148 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/API/SBInstructionList.h @@ -0,0 +1,71 @@ +//===-- SBInstructionList.h -------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SBInstructionList_h_ +#define LLDB_SBInstructionList_h_ + +#include "lldb/API/SBDefines.h" + +#include + +namespace lldb { + +class SBInstructionList +{ +public: + + SBInstructionList (); + + SBInstructionList (const SBInstructionList &rhs); + + const SBInstructionList & + operator = (const SBInstructionList &rhs); + + ~SBInstructionList (); + + bool + IsValid () const; + + size_t + GetSize (); + + lldb::SBInstruction + GetInstructionAtIndex (uint32_t idx); + + void + Clear (); + + void + AppendInstruction (lldb::SBInstruction inst); + + void + Print (FILE *out); + + bool + GetDescription (lldb::SBStream &description); + + bool + DumpEmulationForAllInstructions (const char *triple); + +protected: + friend class SBFunction; + friend class SBSymbol; + friend class SBTarget; + + void + SetDisassembler (const lldb::DisassemblerSP &opaque_sp); + +private: + lldb::DisassemblerSP m_opaque_sp; +}; + + +} // namespace lldb + +#endif // LLDB_SBInstructionList_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/API/SBLineEntry.h b/contrib/llvm/tools/lldb/include/lldb/API/SBLineEntry.h new file mode 100644 index 00000000000..2d099a29798 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/API/SBLineEntry.h @@ -0,0 +1,99 @@ +//===-- SBLineEntry.h -------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SBLineEntry_h_ +#define LLDB_SBLineEntry_h_ + +#include "lldb/API/SBDefines.h" +#include "lldb/API/SBAddress.h" +#include "lldb/API/SBFileSpec.h" + +namespace lldb { + +class SBLineEntry +{ +public: + + SBLineEntry (); + + SBLineEntry (const lldb::SBLineEntry &rhs); + + ~SBLineEntry (); + + const lldb::SBLineEntry & + operator = (const lldb::SBLineEntry &rhs); + + lldb::SBAddress + GetStartAddress () const; + + lldb::SBAddress + GetEndAddress () const; + + bool + IsValid () const; + + lldb::SBFileSpec + GetFileSpec () const; + + uint32_t + GetLine () const; + + uint32_t + GetColumn () const; + + void + SetFileSpec (lldb::SBFileSpec filespec); + + void + SetLine (uint32_t line); + + void + SetColumn (uint32_t column); + + bool + operator == (const lldb::SBLineEntry &rhs) const; + + bool + operator != (const lldb::SBLineEntry &rhs) const; + + bool + GetDescription (lldb::SBStream &description); + +protected: + + lldb_private::LineEntry * + get (); + +private: + friend class SBAddress; + friend class SBCompileUnit; + friend class SBFrame; + friend class SBSymbolContext; + + const lldb_private::LineEntry * + operator->() const; + + lldb_private::LineEntry & + ref(); + + const lldb_private::LineEntry & + ref() const; + + SBLineEntry (const lldb_private::LineEntry *lldb_object_ptr); + + void + SetLineEntry (const lldb_private::LineEntry &lldb_object_ref); + + std::unique_ptr m_opaque_ap; +}; + + +} // namespace lldb + +#endif // LLDB_SBLineEntry_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/API/SBListener.h b/contrib/llvm/tools/lldb/include/lldb/API/SBListener.h new file mode 100644 index 00000000000..c5a04734174 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/API/SBListener.h @@ -0,0 +1,135 @@ +//===-- SBListener.h --------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SBListener_h_ +#define LLDB_SBListener_h_ + +#include "lldb/API/SBDefines.h" + +namespace lldb { + +class SBListener +{ +public: + SBListener (); + + SBListener (const char *name); + + SBListener (const SBListener &rhs); + + ~SBListener (); + + const lldb::SBListener & + operator = (const lldb::SBListener &rhs); + + void + AddEvent (const lldb::SBEvent &event); + + void + Clear (); + + bool + IsValid () const; + + uint32_t + StartListeningForEventClass (SBDebugger &debugger, + const char *broadcaster_class, + uint32_t event_mask); + + bool + StopListeningForEventClass (SBDebugger &debugger, + const char *broadcaster_class, + uint32_t event_mask); + + uint32_t + StartListeningForEvents (const lldb::SBBroadcaster& broadcaster, + uint32_t event_mask); + + bool + StopListeningForEvents (const lldb::SBBroadcaster& broadcaster, + uint32_t event_mask); + + // Returns true if an event was recieved, false if we timed out. + bool + WaitForEvent (uint32_t num_seconds, + lldb::SBEvent &event); + + bool + WaitForEventForBroadcaster (uint32_t num_seconds, + const lldb::SBBroadcaster &broadcaster, + lldb::SBEvent &sb_event); + + bool + WaitForEventForBroadcasterWithType (uint32_t num_seconds, + const lldb::SBBroadcaster &broadcaster, + uint32_t event_type_mask, + lldb::SBEvent &sb_event); + + bool + PeekAtNextEvent (lldb::SBEvent &sb_event); + + bool + PeekAtNextEventForBroadcaster (const lldb::SBBroadcaster &broadcaster, + lldb::SBEvent &sb_event); + + bool + PeekAtNextEventForBroadcasterWithType (const lldb::SBBroadcaster &broadcaster, + uint32_t event_type_mask, + lldb::SBEvent &sb_event); + + bool + GetNextEvent (lldb::SBEvent &sb_event); + + bool + GetNextEventForBroadcaster (const lldb::SBBroadcaster &broadcaster, + lldb::SBEvent &sb_event); + + bool + GetNextEventForBroadcasterWithType (const lldb::SBBroadcaster &broadcaster, + uint32_t event_type_mask, + lldb::SBEvent &sb_event); + + bool + HandleBroadcastEvent (const lldb::SBEvent &event); + +protected: + friend class SBBroadcaster; + friend class SBCommandInterpreter; + friend class SBDebugger; + friend class SBTarget; + + SBListener (lldb_private::Listener &listener); + +private: + + lldb_private::Listener * + operator->() const; + + lldb_private::Listener * + get() const; + + lldb_private::Listener & + ref() const; + + lldb_private::Listener & + operator *(); + + const lldb_private::Listener & + operator *() const; + + void + reset(lldb_private::Listener *listener, bool transfer_ownership); + + lldb::ListenerSP m_opaque_sp; + lldb_private::Listener *m_opaque_ptr; +}; + +} // namespace lldb + +#endif // LLDB_SBListener_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/API/SBModule.h b/contrib/llvm/tools/lldb/include/lldb/API/SBModule.h new file mode 100644 index 00000000000..a3c4879aa2f --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/API/SBModule.h @@ -0,0 +1,287 @@ +//===-- SBModule.h ----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SBModule_h_ +#define LLDB_SBModule_h_ + +#include "lldb/API/SBDefines.h" +#include "lldb/API/SBError.h" +#include "lldb/API/SBSection.h" +#include "lldb/API/SBSymbolContext.h" +#include "lldb/API/SBValueList.h" + +namespace lldb { + +class SBModule +{ +public: + + SBModule (); + + SBModule (const SBModule &rhs); + + SBModule (const SBModuleSpec &module_spec); + + const SBModule & + operator = (const SBModule &rhs); + + SBModule (lldb::SBProcess &process, + lldb::addr_t header_addr); + + ~SBModule (); + + bool + IsValid () const; + + void + Clear(); + + //------------------------------------------------------------------ + /// Get const accessor for the module file specification. + /// + /// This function returns the file for the module on the host system + /// that is running LLDB. This can differ from the path on the + /// platform since we might be doing remote debugging. + /// + /// @return + /// A const reference to the file specification object. + //------------------------------------------------------------------ + lldb::SBFileSpec + GetFileSpec () const; + + //------------------------------------------------------------------ + /// Get accessor for the module platform file specification. + /// + /// Platform file refers to the path of the module as it is known on + /// the remote system on which it is being debugged. For local + /// debugging this is always the same as Module::GetFileSpec(). But + /// remote debugging might mention a file '/usr/lib/liba.dylib' + /// which might be locally downloaded and cached. In this case the + /// platform file could be something like: + /// '/tmp/lldb/platform-cache/remote.host.computer/usr/lib/liba.dylib' + /// The file could also be cached in a local developer kit directory. + /// + /// @return + /// A const reference to the file specification object. + //------------------------------------------------------------------ + lldb::SBFileSpec + GetPlatformFileSpec () const; + + bool + SetPlatformFileSpec (const lldb::SBFileSpec &platform_file); + + lldb::ByteOrder + GetByteOrder (); + + uint32_t + GetAddressByteSize(); + + const char * + GetTriple (); + + const uint8_t * + GetUUIDBytes () const; + + const char * + GetUUIDString () const; + + bool + operator == (const lldb::SBModule &rhs) const; + + bool + operator != (const lldb::SBModule &rhs) const; + + lldb::SBSection + FindSection (const char *sect_name); + + lldb::SBAddress + ResolveFileAddress (lldb::addr_t vm_addr); + + lldb::SBSymbolContext + ResolveSymbolContextForAddress (const lldb::SBAddress& addr, + uint32_t resolve_scope); + + bool + GetDescription (lldb::SBStream &description); + + uint32_t + GetNumCompileUnits(); + + lldb::SBCompileUnit + GetCompileUnitAtIndex (uint32_t); + + size_t + GetNumSymbols (); + + lldb::SBSymbol + GetSymbolAtIndex (size_t idx); + + lldb::SBSymbol + FindSymbol (const char *name, + lldb::SymbolType type = eSymbolTypeAny); + + lldb::SBSymbolContextList + FindSymbols (const char *name, + lldb::SymbolType type = eSymbolTypeAny); + + size_t + GetNumSections (); + + lldb::SBSection + GetSectionAtIndex (size_t idx); + //------------------------------------------------------------------ + /// Find functions by name. + /// + /// @param[in] name + /// The name of the function we are looking for. + /// + /// @param[in] name_type_mask + /// A logical OR of one or more FunctionNameType enum bits that + /// indicate what kind of names should be used when doing the + /// lookup. Bits include fully qualified names, base names, + /// C++ methods, or ObjC selectors. + /// See FunctionNameType for more details. + /// + /// @return + /// A lldb::SBSymbolContextList that gets filled in with all of + /// the symbol contexts for all the matches. + //------------------------------------------------------------------ + lldb::SBSymbolContextList + FindFunctions (const char *name, + uint32_t name_type_mask = lldb::eFunctionNameTypeAny); + + //------------------------------------------------------------------ + /// Find global and static variables by name. + /// + /// @param[in] target + /// A valid SBTarget instance representing the debuggee. + /// + /// @param[in] name + /// The name of the global or static variable we are looking + /// for. + /// + /// @param[in] max_matches + /// Allow the number of matches to be limited to \a max_matches. + /// + /// @return + /// A list of matched variables in an SBValueList. + //------------------------------------------------------------------ + lldb::SBValueList + FindGlobalVariables (lldb::SBTarget &target, + const char *name, + uint32_t max_matches); + + //------------------------------------------------------------------ + /// Find the first global (or static) variable by name. + /// + /// @param[in] target + /// A valid SBTarget instance representing the debuggee. + /// + /// @param[in] name + /// The name of the global or static variable we are looking + /// for. + /// + /// @return + /// An SBValue that gets filled in with the found variable (if any). + //------------------------------------------------------------------ + lldb::SBValue + FindFirstGlobalVariable (lldb::SBTarget &target, const char *name); + + lldb::SBType + FindFirstType (const char* name); + + lldb::SBTypeList + FindTypes (const char* type); + + lldb::SBType + GetBasicType(lldb::BasicType type); + + //------------------------------------------------------------------ + /// Get all types matching \a type_mask from debug info in this + /// module. + /// + /// @param[in] type_mask + /// A bitfield that consists of one or more bits logically OR'ed + /// together from the lldb::TypeClass enumeration. This allows + /// you to request only structure types, or only class, struct + /// and union types. Passing in lldb::eTypeClassAny will return + /// all types found in the debug information for this module. + /// + /// @return + /// A list of types in this module that match \a type_mask + //------------------------------------------------------------------ + lldb::SBTypeList + GetTypes (uint32_t type_mask = lldb::eTypeClassAny); + + //------------------------------------------------------------------ + /// Get the module version numbers. + /// + /// Many object files have a set of version numbers that describe + /// the version of the executable or shared library. Typically there + /// are major, minor and build, but there may be more. This function + /// will extract the versions from object files if they are available. + /// + /// If \a versions is NULL, or if \a num_versions is 0, the return + /// value will indicate how many version numbers are available in + /// this object file. Then a subsequent call can be made to this + /// function with a value of \a versions and \a num_versions that + /// has enough storage to store some or all version numbers. + /// + /// @param[out] versions + /// A pointer to an array of uint32_t types that is \a num_versions + /// long. If this value is NULL, the return value will indicate + /// how many version numbers are required for a subsequent call + /// to this function so that all versions can be retrieved. If + /// the value is non-NULL, then at most \a num_versions of the + /// existing versions numbers will be filled into \a versions. + /// If there is no version information available, \a versions + /// will be filled with \a num_versions UINT32_MAX values + /// and zero will be returned. + /// + /// @param[in] num_versions + /// The maximum number of entries to fill into \a versions. If + /// this value is zero, then the return value will indicate + /// how many version numbers there are in total so another call + /// to this function can be make with adequate storage in + /// \a versions to get all of the version numbers. If \a + /// num_versions is less than the actual number of version + /// numbers in this object file, only \a num_versions will be + /// filled into \a versions (if \a versions is non-NULL). + /// + /// @return + /// This function always returns the number of version numbers + /// that this object file has regardless of the number of + /// version numbers that were copied into \a versions. + //------------------------------------------------------------------ + uint32_t + GetVersion (uint32_t *versions, + uint32_t num_versions); + +private: + friend class SBAddress; + friend class SBFrame; + friend class SBSection; + friend class SBSymbolContext; + friend class SBTarget; + + explicit SBModule (const lldb::ModuleSP& module_sp); + + ModuleSP + GetSP () const; + + void + SetSP (const ModuleSP &module_sp); + + lldb::ModuleSP m_opaque_sp; +}; + + +} // namespace lldb + +#endif // LLDB_SBModule_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/API/SBModuleSpec.h b/contrib/llvm/tools/lldb/include/lldb/API/SBModuleSpec.h new file mode 100644 index 00000000000..a615e017cbc --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/API/SBModuleSpec.h @@ -0,0 +1,154 @@ +//===-- SBModuleSpec.h ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SBModuleSpec_h_ +#define LLDB_SBModuleSpec_h_ + +#include "lldb/API/SBDefines.h" +#include "lldb/API/SBFileSpec.h" + +namespace lldb { + +class SBModuleSpec +{ +public: + + SBModuleSpec (); + + SBModuleSpec (const SBModuleSpec &rhs); + + ~SBModuleSpec (); + + const SBModuleSpec & + operator = (const SBModuleSpec &rhs); + + bool + IsValid () const; + + void + Clear(); + + //------------------------------------------------------------------ + /// Get const accessor for the module file. + /// + /// This function returns the file for the module on the host system + /// that is running LLDB. This can differ from the path on the + /// platform since we might be doing remote debugging. + /// + /// @return + /// A const reference to the file specification object. + //------------------------------------------------------------------ + lldb::SBFileSpec + GetFileSpec (); + + void + SetFileSpec (const lldb::SBFileSpec &fspec); + + //------------------------------------------------------------------ + /// Get accessor for the module platform file. + /// + /// Platform file refers to the path of the module as it is known on + /// the remote system on which it is being debugged. For local + /// debugging this is always the same as Module::GetFileSpec(). But + /// remote debugging might mention a file '/usr/lib/liba.dylib' + /// which might be locally downloaded and cached. In this case the + /// platform file could be something like: + /// '/tmp/lldb/platform-cache/remote.host.computer/usr/lib/liba.dylib' + /// The file could also be cached in a local developer kit directory. + /// + /// @return + /// A const reference to the file specification object. + //------------------------------------------------------------------ + lldb::SBFileSpec + GetPlatformFileSpec (); + + void + SetPlatformFileSpec (const lldb::SBFileSpec &fspec); + + lldb::SBFileSpec + GetSymbolFileSpec (); + + void + SetSymbolFileSpec (const lldb::SBFileSpec &fspec); + + const char * + GetObjectName (); + + void + SetObjectName (const char *name); + + const char * + GetTriple (); + + void + SetTriple (const char *triple); + + const uint8_t * + GetUUIDBytes (); + + size_t + GetUUIDLength (); + + bool + SetUUIDBytes (const uint8_t *uuid, size_t uuid_len); + + bool + GetDescription (lldb::SBStream &description); + +private: + friend class SBModuleSpecList; + friend class SBModule; + friend class SBTarget; + + std::unique_ptr m_opaque_ap; +}; + +class SBModuleSpecList +{ +public: + SBModuleSpecList(); + + SBModuleSpecList (const SBModuleSpecList &rhs); + + ~SBModuleSpecList(); + + SBModuleSpecList & + operator = (const SBModuleSpecList &rhs); + + static SBModuleSpecList + GetModuleSpecifications (const char *path); + + void + Append (const SBModuleSpec &spec); + + void + Append (const SBModuleSpecList &spec_list); + + SBModuleSpec + FindFirstMatchingSpec (const SBModuleSpec &match_spec); + + SBModuleSpecList + FindMatchingSpecs (const SBModuleSpec &match_spec); + + size_t + GetSize(); + + SBModuleSpec + GetSpecAtIndex (size_t i); + + bool + GetDescription (lldb::SBStream &description); + +private: + std::unique_ptr m_opaque_ap; +}; + +} // namespace lldb + +#endif // LLDB_SBModuleSpec_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/API/SBProcess.h b/contrib/llvm/tools/lldb/include/lldb/API/SBProcess.h new file mode 100644 index 00000000000..784f362122a --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/API/SBProcess.h @@ -0,0 +1,295 @@ +//===-- SBProcess.h ---------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SBProcess_h_ +#define LLDB_SBProcess_h_ + +#include "lldb/API/SBDefines.h" +#include "lldb/API/SBError.h" +#include "lldb/API/SBTarget.h" +#include + +namespace lldb { + +class SBEvent; + +class SBProcess +{ +public: + //------------------------------------------------------------------ + /// Broadcaster event bits definitions. + //------------------------------------------------------------------ + enum + { + eBroadcastBitStateChanged = (1 << 0), + eBroadcastBitInterrupt = (1 << 1), + eBroadcastBitSTDOUT = (1 << 2), + eBroadcastBitSTDERR = (1 << 3), + eBroadcastBitProfileData = (1 << 4) + }; + + SBProcess (); + + SBProcess (const lldb::SBProcess& rhs); + + const lldb::SBProcess& + operator = (const lldb::SBProcess& rhs); + + SBProcess (const lldb::ProcessSP &process_sp); + + ~SBProcess(); + + static const char * + GetBroadcasterClassName (); + + const char * + GetPluginName (); + + // DEPRECATED: use GetPluginName() + const char * + GetShortPluginName (); + + void + Clear (); + + bool + IsValid() const; + + lldb::SBTarget + GetTarget() const; + + lldb::ByteOrder + GetByteOrder() const; + + size_t + PutSTDIN (const char *src, size_t src_len); + + size_t + GetSTDOUT (char *dst, size_t dst_len) const; + + size_t + GetSTDERR (char *dst, size_t dst_len) const; + + size_t + GetAsyncProfileData(char *dst, size_t dst_len) const; + + void + ReportEventState (const lldb::SBEvent &event, FILE *out) const; + + void + AppendEventStateReport (const lldb::SBEvent &event, lldb::SBCommandReturnObject &result); + + //------------------------------------------------------------------ + /// Remote connection related functions. These will fail if the + /// process is not in eStateConnected. They are intended for use + /// when connecting to an externally managed debugserver instance. + //------------------------------------------------------------------ + bool + RemoteAttachToProcessWithID (lldb::pid_t pid, + lldb::SBError& error); + + bool + RemoteLaunch (char const **argv, + char const **envp, + const char *stdin_path, + const char *stdout_path, + const char *stderr_path, + const char *working_directory, + uint32_t launch_flags, + bool stop_at_entry, + lldb::SBError& error); + + //------------------------------------------------------------------ + // Thread related functions + //------------------------------------------------------------------ + uint32_t + GetNumThreads (); + + lldb::SBThread + GetThreadAtIndex (size_t index); + + lldb::SBThread + GetThreadByID (lldb::tid_t sb_thread_id); + + lldb::SBThread + GetThreadByIndexID (uint32_t index_id); + + lldb::SBThread + GetSelectedThread () const; + + //------------------------------------------------------------------ + // Function for lazily creating a thread using the current OS + // plug-in. This function will be removed in the future when there + // are APIs to create SBThread objects through the interface and add + // them to the process through the SBProcess API. + //------------------------------------------------------------------ + lldb::SBThread + CreateOSPluginThread (lldb::tid_t tid, lldb::addr_t context); + + bool + SetSelectedThread (const lldb::SBThread &thread); + + bool + SetSelectedThreadByID (lldb::tid_t tid); + + bool + SetSelectedThreadByIndexID (uint32_t index_id); + + //------------------------------------------------------------------ + // Stepping related functions + //------------------------------------------------------------------ + + lldb::StateType + GetState (); + + int + GetExitStatus (); + + const char * + GetExitDescription (); + + //------------------------------------------------------------------ + /// Gets the process ID + /// + /// Returns the process identifier for the process as it is known + /// on the system on which the process is running. For unix systems + /// this is typically the same as if you called "getpid()" in the + /// process. + /// + /// @return + /// Returns LLDB_INVALID_PROCESS_ID if this object does not + /// contain a valid process object, or if the process has not + /// been launched. Returns a valid process ID if the process is + /// valid. + //------------------------------------------------------------------ + lldb::pid_t + GetProcessID (); + + //------------------------------------------------------------------ + /// Gets the unique ID associated with this process object + /// + /// Unique IDs start at 1 and increment up with each new process + /// instance. Since starting a process on a system might always + /// create a process with the same process ID, there needs to be a + /// way to tell two process instances apart. + /// + /// @return + /// Returns a non-zero integer ID if this object contains a + /// valid process object, zero if this object does not contain + /// a valid process object. + //------------------------------------------------------------------ + uint32_t + GetUniqueID(); + + uint32_t + GetAddressByteSize() const; + + lldb::SBError + Destroy (); + + lldb::SBError + Continue (); + + lldb::SBError + Stop (); + + lldb::SBError + Kill (); + + lldb::SBError + Detach (); + + lldb::SBError + Detach (bool keep_stopped); + + lldb::SBError + Signal (int signal); + + void + SendAsyncInterrupt(); + + uint32_t + GetStopID(bool include_expression_stops = false); + + size_t + ReadMemory (addr_t addr, void *buf, size_t size, lldb::SBError &error); + + size_t + WriteMemory (addr_t addr, const void *buf, size_t size, lldb::SBError &error); + + size_t + ReadCStringFromMemory (addr_t addr, void *buf, size_t size, lldb::SBError &error); + + uint64_t + ReadUnsignedFromMemory (addr_t addr, uint32_t byte_size, lldb::SBError &error); + + lldb::addr_t + ReadPointerFromMemory (addr_t addr, lldb::SBError &error); + + // Events + static lldb::StateType + GetStateFromEvent (const lldb::SBEvent &event); + + static bool + GetRestartedFromEvent (const lldb::SBEvent &event); + + static size_t + GetNumRestartedReasonsFromEvent (const lldb::SBEvent &event); + + static const char * + GetRestartedReasonAtIndexFromEvent (const lldb::SBEvent &event, size_t idx); + + static lldb::SBProcess + GetProcessFromEvent (const lldb::SBEvent &event); + + static bool + EventIsProcessEvent (const lldb::SBEvent &event); + + lldb::SBBroadcaster + GetBroadcaster () const; + + static const char * + GetBroadcasterClass (); + + bool + GetDescription (lldb::SBStream &description); + + uint32_t + GetNumSupportedHardwareWatchpoints (lldb::SBError &error) const; + + uint32_t + LoadImage (lldb::SBFileSpec &image_spec, lldb::SBError &error); + + lldb::SBError + UnloadImage (uint32_t image_token); + +protected: + friend class SBAddress; + friend class SBBreakpoint; + friend class SBBreakpointLocation; + friend class SBCommandInterpreter; + friend class SBDebugger; + friend class SBFunction; + friend class SBModule; + friend class SBTarget; + friend class SBThread; + friend class SBValue; + + lldb::ProcessSP + GetSP() const; + + void + SetSP (const lldb::ProcessSP &process_sp); + + lldb::ProcessWP m_opaque_wp; +}; + +} // namespace lldb + +#endif // LLDB_SBProcess_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/API/SBSection.h b/contrib/llvm/tools/lldb/include/lldb/API/SBSection.h new file mode 100644 index 00000000000..3386484f649 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/API/SBSection.h @@ -0,0 +1,104 @@ +//===-- SBSection.h ---------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SBSection_h_ +#define LLDB_SBSection_h_ + +#include "lldb/API/SBDefines.h" +#include "lldb/API/SBData.h" + +namespace lldb { + +class SBSection +{ +public: + + SBSection (); + + SBSection (const lldb::SBSection &rhs); + + ~SBSection (); + + const lldb::SBSection & + operator = (const lldb::SBSection &rhs); + + bool + IsValid () const; + + const char * + GetName (); + + lldb::SBSection + GetParent(); + + lldb::SBSection + FindSubSection (const char *sect_name); + + size_t + GetNumSubSections (); + + lldb::SBSection + GetSubSectionAtIndex (size_t idx); + + lldb::addr_t + GetFileAddress (); + + lldb::addr_t + GetLoadAddress (lldb::SBTarget &target); + + lldb::addr_t + GetByteSize (); + + uint64_t + GetFileOffset (); + + uint64_t + GetFileByteSize (); + + lldb::SBData + GetSectionData (); + + lldb::SBData + GetSectionData (uint64_t offset, + uint64_t size); + + SectionType + GetSectionType (); + + bool + operator == (const lldb::SBSection &rhs); + + bool + operator != (const lldb::SBSection &rhs); + + bool + GetDescription (lldb::SBStream &description); + + +private: + + friend class SBAddress; + friend class SBModule; + friend class SBTarget; + + SBSection (const lldb::SectionSP §ion_sp); + + lldb::SectionSP + GetSP() const; + + void + SetSP(const lldb::SectionSP §ion_sp); + + lldb::SectionWP m_opaque_wp; +}; + + +} // namespace lldb + +#endif // LLDB_SBSection_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/API/SBSourceManager.h b/contrib/llvm/tools/lldb/include/lldb/API/SBSourceManager.h new file mode 100644 index 00000000000..5b52c49ff3e --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/API/SBSourceManager.h @@ -0,0 +1,53 @@ +//===-- SBSourceManager.h ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SBSourceManager_h_ +#define LLDB_SBSourceManager_h_ + +#include "lldb/API/SBDefines.h" + +#include + +namespace lldb { + +class SBSourceManager +{ +public: + SBSourceManager (const SBDebugger &debugger); + SBSourceManager (const SBTarget &target); + SBSourceManager (const SBSourceManager &rhs); + + ~SBSourceManager(); + + const lldb::SBSourceManager & + operator = (const lldb::SBSourceManager &rhs); + + size_t + DisplaySourceLinesWithLineNumbers (const lldb::SBFileSpec &file, + uint32_t line, + uint32_t context_before, + uint32_t context_after, + const char* current_line_cstr, + lldb::SBStream &s); + + +protected: + friend class SBCommandInterpreter; + friend class SBDebugger; + + SBSourceManager(lldb_private::SourceManager *source_manager); + +private: + + std::unique_ptr m_opaque_ap; +}; + +} // namespace lldb + +#endif // LLDB_SBSourceManager_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/API/SBStream.h b/contrib/llvm/tools/lldb/include/lldb/API/SBStream.h new file mode 100644 index 00000000000..038adf68542 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/API/SBStream.h @@ -0,0 +1,111 @@ +//===-- SBStream.h ----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SBStream_h_ +#define LLDB_SBStream_h_ + +#include + +#include "lldb/API/SBDefines.h" + +namespace lldb { + +class SBStream +{ +public: + + SBStream (); + + ~SBStream (); + + bool + IsValid() const; + + // If this stream is not redirected to a file, it will maintain a local + // cache for the stream data which can be accessed using this accessor. + const char * + GetData (); + + // If this stream is not redirected to a file, it will maintain a local + // cache for the stream output whose length can be accessed using this + // accessor. + size_t + GetSize(); + + void + Printf (const char *format, ...) __attribute__ ((format (printf, 2, 3))); + + void + RedirectToFile (const char *path, bool append); + + void + RedirectToFileHandle (FILE *fh, bool transfer_fh_ownership); + + void + RedirectToFileDescriptor (int fd, bool transfer_fh_ownership); + + // If the stream is redirected to a file, forget about the file and if + // ownership of the file was transfered to this object, close the file. + // If the stream is backed by a local cache, clear this cache. + void + Clear (); + +protected: + friend class SBAddress; + friend class SBBlock; + friend class SBBreakpoint; + friend class SBBreakpointLocation; + friend class SBCommandReturnObject; + friend class SBCompileUnit; + friend class SBData; + friend class SBDebugger; + friend class SBDeclaration; + friend class SBEvent; + friend class SBFileSpec; + friend class SBFileSpecList; + friend class SBFrame; + friend class SBFunction; + friend class SBInstruction; + friend class SBInstructionList; + friend class SBLineEntry; + friend class SBModule; + friend class SBModuleSpec; + friend class SBModuleSpecList; + friend class SBProcess; + friend class SBSection; + friend class SBSourceManager; + friend class SBSymbol; + friend class SBSymbolContext; + friend class SBSymbolContextList; + friend class SBTarget; + friend class SBThread; + friend class SBType; + friend class SBTypeMember; + friend class SBValue; + friend class SBWatchpoint; + + lldb_private::Stream * + operator->(); + + lldb_private::Stream * + get(); + + lldb_private::Stream & + ref(); + +private: + + DISALLOW_COPY_AND_ASSIGN (SBStream); + std::unique_ptr m_opaque_ap; + bool m_is_file; +}; + +} // namespace lldb + +#endif // LLDB_SBStream_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/API/SBStringList.h b/contrib/llvm/tools/lldb/include/lldb/API/SBStringList.h new file mode 100644 index 00000000000..9d0be6e8a74 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/API/SBStringList.h @@ -0,0 +1,71 @@ +//===-- SBStringList.h ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SBStringList_h_ +#define LLDB_SBStringList_h_ + +#include "lldb/API/SBDefines.h" + +namespace lldb { + +class SBStringList +{ +public: + + SBStringList (); + + SBStringList (const lldb::SBStringList &rhs); + + const SBStringList & + operator = (const SBStringList &rhs); + + ~SBStringList (); + + bool + IsValid() const; + + void + AppendString (const char *str); + + void + AppendList (const char **strv, int strc); + + void + AppendList (const lldb::SBStringList &strings); + + uint32_t + GetSize () const; + + const char * + GetStringAtIndex (size_t idx); + + void + Clear (); + +protected: + friend class SBCommandInterpreter; + friend class SBDebugger; + + SBStringList (const lldb_private::StringList *lldb_strings); + + const lldb_private::StringList * + operator->() const; + + const lldb_private::StringList & + operator*() const; + +private: + + std::unique_ptr m_opaque_ap; + +}; + +} // namespace lldb + +#endif // LLDB_SBStringList_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/API/SBSymbol.h b/contrib/llvm/tools/lldb/include/lldb/API/SBSymbol.h new file mode 100644 index 00000000000..0a528a9ac96 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/API/SBSymbol.h @@ -0,0 +1,109 @@ +//===-- SBSymbol.h ----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SBSymbol_h_ +#define LLDB_SBSymbol_h_ + +#include "lldb/API/SBDefines.h" +#include "lldb/API/SBAddress.h" +#include "lldb/API/SBInstructionList.h" +#include "lldb/API/SBTarget.h" + +namespace lldb { + +class SBSymbol +{ +public: + + SBSymbol (); + + ~SBSymbol (); + + SBSymbol (const lldb::SBSymbol &rhs); + + const lldb::SBSymbol & + operator = (const lldb::SBSymbol &rhs); + + bool + IsValid () const; + + + const char * + GetName() const; + + const char * + GetMangledName () const; + + lldb::SBInstructionList + GetInstructions (lldb::SBTarget target); + + lldb::SBInstructionList + GetInstructions (lldb::SBTarget target, const char *flavor_string); + + SBAddress + GetStartAddress (); + + SBAddress + GetEndAddress (); + + uint32_t + GetPrologueByteSize (); + + SymbolType + GetType (); + + bool + operator == (const lldb::SBSymbol &rhs) const; + + bool + operator != (const lldb::SBSymbol &rhs) const; + + bool + GetDescription (lldb::SBStream &description); + + //---------------------------------------------------------------------- + // Returns true if the symbol is externally visible in the module that + // it is defined in + //---------------------------------------------------------------------- + bool + IsExternal(); + + //---------------------------------------------------------------------- + // Returns true if the symbol was synthetically generated from something + // other than the actual symbol table itself in the object file. + //---------------------------------------------------------------------- + bool + IsSynthetic(); + +protected: + + lldb_private::Symbol * + get (); + + void + reset (lldb_private::Symbol *); + +private: + friend class SBAddress; + friend class SBFrame; + friend class SBModule; + friend class SBSymbolContext; + + SBSymbol (lldb_private::Symbol *lldb_object_ptr); + + void + SetSymbol (lldb_private::Symbol *lldb_object_ptr); + + lldb_private::Symbol *m_opaque_ptr; +}; + + +} // namespace lldb + +#endif // LLDB_SBSymbol_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/API/SBSymbolContext.h b/contrib/llvm/tools/lldb/include/lldb/API/SBSymbolContext.h new file mode 100644 index 00000000000..fee2d19179d --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/API/SBSymbolContext.h @@ -0,0 +1,94 @@ +//===-- SBSymbolContext.h ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SBSymbolContext_h_ +#define LLDB_SBSymbolContext_h_ + +#include "lldb/API/SBDefines.h" +#include "lldb/API/SBBlock.h" +#include "lldb/API/SBCompileUnit.h" +#include "lldb/API/SBFunction.h" +#include "lldb/API/SBLineEntry.h" +#include "lldb/API/SBModule.h" +#include "lldb/API/SBSymbol.h" + +namespace lldb { + +class SBSymbolContext +{ +public: + SBSymbolContext (); + + SBSymbolContext (const lldb::SBSymbolContext& rhs); + + ~SBSymbolContext (); + + bool + IsValid () const; + + const lldb::SBSymbolContext & + operator = (const lldb::SBSymbolContext &rhs); + + lldb::SBModule GetModule (); + lldb::SBCompileUnit GetCompileUnit (); + lldb::SBFunction GetFunction (); + lldb::SBBlock GetBlock (); + lldb::SBLineEntry GetLineEntry (); + lldb::SBSymbol GetSymbol (); + + void SetModule (lldb::SBModule module); + void SetCompileUnit (lldb::SBCompileUnit compile_unit); + void SetFunction (lldb::SBFunction function); + void SetBlock (lldb::SBBlock block); + void SetLineEntry (lldb::SBLineEntry line_entry); + void SetSymbol (lldb::SBSymbol symbol); + + SBSymbolContext + GetParentOfInlinedScope (const SBAddress &curr_frame_pc, + SBAddress &parent_frame_addr) const; + + bool + GetDescription (lldb::SBStream &description); + +protected: + friend class SBAddress; + friend class SBFrame; + friend class SBModule; + friend class SBThread; + friend class SBTarget; + friend class SBSymbolContextList; + + lldb_private::SymbolContext* + operator->() const; + + lldb_private::SymbolContext& + operator*(); + + lldb_private::SymbolContext& + ref(); + + const lldb_private::SymbolContext& + operator*() const; + + lldb_private::SymbolContext * + get() const; + + SBSymbolContext (const lldb_private::SymbolContext *sc_ptr); + + void + SetSymbolContext (const lldb_private::SymbolContext *sc_ptr); + +private: + std::unique_ptr m_opaque_ap; +}; + + +} // namespace lldb + +#endif // LLDB_SBSymbolContext_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/API/SBSymbolContextList.h b/contrib/llvm/tools/lldb/include/lldb/API/SBSymbolContextList.h new file mode 100644 index 00000000000..6cc78e472c6 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/API/SBSymbolContextList.h @@ -0,0 +1,69 @@ +//===-- SBSymbolContextList.h -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SBSymbolContextList_h_ +#define LLDB_SBSymbolContextList_h_ + +#include "lldb/API/SBDefines.h" +#include "lldb/API/SBSymbolContext.h" + +namespace lldb { + +class SBSymbolContextList +{ +public: + SBSymbolContextList (); + + SBSymbolContextList (const lldb::SBSymbolContextList& rhs); + + ~SBSymbolContextList (); + + const lldb::SBSymbolContextList & + operator = (const lldb::SBSymbolContextList &rhs); + + bool + IsValid () const; + + uint32_t + GetSize() const; + + lldb::SBSymbolContext + GetContextAtIndex (uint32_t idx); + + bool + GetDescription (lldb::SBStream &description); + + void + Append (lldb::SBSymbolContext &sc); + + void + Append (lldb::SBSymbolContextList &sc_list); + + void + Clear(); + +protected: + + friend class SBModule; + friend class SBTarget; + + lldb_private::SymbolContextList* + operator->() const; + + lldb_private::SymbolContextList& + operator*() const; + +private: + std::unique_ptr m_opaque_ap; +}; + + +} // namespace lldb + +#endif // LLDB_SBSymbolContextList_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/API/SBTarget.h b/contrib/llvm/tools/lldb/include/lldb/API/SBTarget.h new file mode 100644 index 00000000000..15aeed4b600 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/API/SBTarget.h @@ -0,0 +1,828 @@ +//===-- SBTarget.h ----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SBTarget_h_ +#define LLDB_SBTarget_h_ + +#include "lldb/API/SBDefines.h" +#include "lldb/API/SBAddress.h" +#include "lldb/API/SBBroadcaster.h" +#include "lldb/API/SBFileSpec.h" +#include "lldb/API/SBFileSpecList.h" +#include "lldb/API/SBSymbolContextList.h" +#include "lldb/API/SBType.h" +#include "lldb/API/SBValue.h" +#include "lldb/API/SBWatchpoint.h" + +namespace lldb { + +class SBLaunchInfo +{ +public: + SBLaunchInfo (const char **argv); + + ~SBLaunchInfo(); + + uint32_t + GetUserID(); + + uint32_t + GetGroupID(); + + bool + UserIDIsValid (); + + bool + GroupIDIsValid (); + + void + SetUserID (uint32_t uid); + + void + SetGroupID (uint32_t gid); + + uint32_t + GetNumArguments (); + + const char * + GetArgumentAtIndex (uint32_t idx); + + void + SetArguments (const char **argv, bool append); + + uint32_t + GetNumEnvironmentEntries (); + + const char * + GetEnvironmentEntryAtIndex (uint32_t idx); + + void + SetEnvironmentEntries (const char **envp, bool append); + + void + Clear (); + + const char * + GetWorkingDirectory () const; + + void + SetWorkingDirectory (const char *working_dir); + + uint32_t + GetLaunchFlags (); + + void + SetLaunchFlags (uint32_t flags); + + const char * + GetProcessPluginName (); + + void + SetProcessPluginName (const char *plugin_name); + + const char * + GetShell (); + + void + SetShell (const char * path); + + uint32_t + GetResumeCount (); + + void + SetResumeCount (uint32_t c); + + bool + AddCloseFileAction (int fd); + + bool + AddDuplicateFileAction (int fd, int dup_fd); + + bool + AddOpenFileAction (int fd, const char *path, bool read, bool write); + + bool + AddSuppressFileAction (int fd, bool read, bool write); + +protected: + friend class SBTarget; + + lldb_private::ProcessLaunchInfo & + ref (); + + ProcessLaunchInfoSP m_opaque_sp; +}; + +class SBAttachInfo +{ +public: + SBAttachInfo (); + + SBAttachInfo (lldb::pid_t pid); + + SBAttachInfo (const char *path, bool wait_for); + + SBAttachInfo (const SBAttachInfo &rhs); + + ~SBAttachInfo(); + + SBAttachInfo & + operator = (const SBAttachInfo &rhs); + + lldb::pid_t + GetProcessID (); + + void + SetProcessID (lldb::pid_t pid); + + void + SetExecutable (const char *path); + + void + SetExecutable (lldb::SBFileSpec exe_file); + + bool + GetWaitForLaunch (); + + void + SetWaitForLaunch (bool b); + + bool + GetIgnoreExisting (); + + void + SetIgnoreExisting (bool b); + + uint32_t + GetResumeCount (); + + void + SetResumeCount (uint32_t c); + + const char * + GetProcessPluginName (); + + void + SetProcessPluginName (const char *plugin_name); + + uint32_t + GetUserID(); + + uint32_t + GetGroupID(); + + bool + UserIDIsValid (); + + bool + GroupIDIsValid (); + + void + SetUserID (uint32_t uid); + + void + SetGroupID (uint32_t gid); + + uint32_t + GetEffectiveUserID(); + + uint32_t + GetEffectiveGroupID(); + + bool + EffectiveUserIDIsValid (); + + bool + EffectiveGroupIDIsValid (); + + void + SetEffectiveUserID (uint32_t uid); + + void + SetEffectiveGroupID (uint32_t gid); + + lldb::pid_t + GetParentProcessID (); + + void + SetParentProcessID (lldb::pid_t pid); + + bool + ParentProcessIDIsValid(); + + +protected: + friend class SBTarget; + + lldb_private::ProcessAttachInfo & + ref (); + + ProcessAttachInfoSP m_opaque_sp; +}; + +class SBTarget +{ +public: + //------------------------------------------------------------------ + // Broadcaster bits. + //------------------------------------------------------------------ + enum + { + eBroadcastBitBreakpointChanged = (1 << 0), + eBroadcastBitModulesLoaded = (1 << 1), + eBroadcastBitModulesUnloaded = (1 << 2), + eBroadcastBitWatchpointChanged = (1 << 3), + eBroadcastBitSymbolsLoaded = (1 << 4) + }; + + //------------------------------------------------------------------ + // Constructors + //------------------------------------------------------------------ + SBTarget (); + + SBTarget (const lldb::SBTarget& rhs); + + SBTarget (const lldb::TargetSP& target_sp); + + const lldb::SBTarget& + operator = (const lldb::SBTarget& rhs); + + //------------------------------------------------------------------ + // Destructor + //------------------------------------------------------------------ + ~SBTarget(); + + bool + IsValid() const; + + static const char * + GetBroadcasterClassName (); + + lldb::SBProcess + GetProcess (); + + //------------------------------------------------------------------ + /// Launch a new process. + /// + /// Launch a new process by spawning a new process using the + /// target object's executable module's file as the file to launch. + /// Arguments are given in \a argv, and the environment variables + /// are in \a envp. Standard input and output files can be + /// optionally re-directed to \a stdin_path, \a stdout_path, and + /// \a stderr_path. + /// + /// @param[in] listener + /// An optional listener that will receive all process events. + /// If \a listener is valid then \a listener will listen to all + /// process events. If not valid, then this target's debugger + /// (SBTarget::GetDebugger()) will listen to all process events. + /// + /// @param[in] argv + /// The argument array. + /// + /// @param[in] envp + /// The environment array. + /// + /// @param[in] launch_flags + /// Flags to modify the launch (@see lldb::LaunchFlags) + /// + /// @param[in] stdin_path + /// The path to use when re-directing the STDIN of the new + /// process. If all stdXX_path arguments are NULL, a pseudo + /// terminal will be used. + /// + /// @param[in] stdout_path + /// The path to use when re-directing the STDOUT of the new + /// process. If all stdXX_path arguments are NULL, a pseudo + /// terminal will be used. + /// + /// @param[in] stderr_path + /// The path to use when re-directing the STDERR of the new + /// process. If all stdXX_path arguments are NULL, a pseudo + /// terminal will be used. + /// + /// @param[in] working_directory + /// The working directory to have the child process run in + /// + /// @param[in] launch_flags + /// Some launch options specified by logical OR'ing + /// lldb::LaunchFlags enumeration values together. + /// + /// @param[in] stop_at_endtry + /// If false do not stop the inferior at the entry point. + /// + /// @param[out] + /// An error object. Contains the reason if there is some failure. + /// + /// @return + /// A process object for the newly created process. + //------------------------------------------------------------------ + lldb::SBProcess + Launch (SBListener &listener, + char const **argv, + char const **envp, + const char *stdin_path, + const char *stdout_path, + const char *stderr_path, + const char *working_directory, + uint32_t launch_flags, // See LaunchFlags + bool stop_at_entry, + lldb::SBError& error); + + + //------------------------------------------------------------------ + /// Launch a new process with sensible defaults. + /// + /// @param[in] argv + /// The argument array. + /// + /// @param[in] envp + /// The environment array. + /// + /// @param[in] working_directory + /// The working directory to have the child process run in + /// + /// Default: listener + /// Set to the target's debugger (SBTarget::GetDebugger()) + /// + /// Default: launch_flags + /// Empty launch flags + /// + /// Default: stdin_path + /// Default: stdout_path + /// Default: stderr_path + /// A pseudo terminal will be used. + /// + /// @return + /// A process object for the newly created process. + //------------------------------------------------------------------ + SBProcess + LaunchSimple (const char **argv, + const char **envp, + const char *working_directory); + + SBProcess + Launch (SBLaunchInfo &launch_info, SBError& error); + + SBProcess + LoadCore (const char *core_file); + + SBProcess + Attach (SBAttachInfo &attach_info, SBError& error); + + //------------------------------------------------------------------ + /// Attach to process with pid. + /// + /// @param[in] listener + /// An optional listener that will receive all process events. + /// If \a listener is valid then \a listener will listen to all + /// process events. If not valid, then this target's debugger + /// (SBTarget::GetDebugger()) will listen to all process events. + /// + /// @param[in] pid + /// The process ID to attach to. + /// + /// @param[out] + /// An error explaining what went wrong if attach fails. + /// + /// @return + /// A process object for the attached process. + //------------------------------------------------------------------ + lldb::SBProcess + AttachToProcessWithID (SBListener &listener, + lldb::pid_t pid, + lldb::SBError& error); + +#if defined(__APPLE__) + // We need to keep this around for a build or two since Xcode links + // to the 32 bit version of this function. We will take it out soon. + lldb::SBProcess + AttachToProcessWithID (SBListener &listener, + ::pid_t pid, // 32 bit int process ID + lldb::SBError& error); // DEPRECATED +#endif + //------------------------------------------------------------------ + /// Attach to process with name. + /// + /// @param[in] listener + /// An optional listener that will receive all process events. + /// If \a listener is valid then \a listener will listen to all + /// process events. If not valid, then this target's debugger + /// (SBTarget::GetDebugger()) will listen to all process events. + /// + /// @param[in] name + /// Basename of process to attach to. + /// + /// @param[in] wait_for + /// If true wait for a new instance of 'name' to be launched. + /// + /// @param[out] + /// An error explaining what went wrong if attach fails. + /// + /// @return + /// A process object for the attached process. + //------------------------------------------------------------------ + lldb::SBProcess + AttachToProcessWithName (SBListener &listener, + const char *name, + bool wait_for, + lldb::SBError& error); + + //------------------------------------------------------------------ + /// Connect to a remote debug server with url. + /// + /// @param[in] listener + /// An optional listener that will receive all process events. + /// If \a listener is valid then \a listener will listen to all + /// process events. If not valid, then this target's debugger + /// (SBTarget::GetDebugger()) will listen to all process events. + /// + /// @param[in] url + /// The url to connect to, e.g., 'connect://localhost:12345'. + /// + /// @param[in] plugin_name + /// The plugin name to be used; can be NULL. + /// + /// @param[out] + /// An error explaining what went wrong if the connect fails. + /// + /// @return + /// A process object for the connected process. + //------------------------------------------------------------------ + lldb::SBProcess + ConnectRemote (SBListener &listener, + const char *url, + const char *plugin_name, + SBError& error); + + lldb::SBFileSpec + GetExecutable (); + + bool + AddModule (lldb::SBModule &module); + + lldb::SBModule + AddModule (const char *path, + const char *triple, + const char *uuid); + + lldb::SBModule + AddModule (const char *path, + const char *triple, + const char *uuid_cstr, + const char *symfile); + + lldb::SBModule + AddModule (const SBModuleSpec &module_spec); + + uint32_t + GetNumModules () const; + + lldb::SBModule + GetModuleAtIndex (uint32_t idx); + + bool + RemoveModule (lldb::SBModule module); + + lldb::SBDebugger + GetDebugger() const; + + lldb::SBModule + FindModule (const lldb::SBFileSpec &file_spec); + + lldb::ByteOrder + GetByteOrder (); + + uint32_t + GetAddressByteSize(); + + const char * + GetTriple (); + + //------------------------------------------------------------------ + /// Set the base load address for a module section. + /// + /// @param[in] section + /// The section whose base load address will be set within this + /// target. + /// + /// @param[in] section_base_addr + /// The base address for the section. + /// + /// @return + /// An error to indicate success, fail, and any reason for + /// failure. + //------------------------------------------------------------------ + lldb::SBError + SetSectionLoadAddress (lldb::SBSection section, + lldb::addr_t section_base_addr); + + //------------------------------------------------------------------ + /// Clear the base load address for a module section. + /// + /// @param[in] section + /// The section whose base load address will be cleared within + /// this target. + /// + /// @return + /// An error to indicate success, fail, and any reason for + /// failure. + //------------------------------------------------------------------ + lldb::SBError + ClearSectionLoadAddress (lldb::SBSection section); + + //------------------------------------------------------------------ + /// Slide all file addresses for all module sections so that \a module + /// appears to loaded at these slide addresses. + /// + /// When you need all sections within a module to be loaded at a + /// rigid slide from the addresses found in the module object file, + /// this function will allow you to easily and quickly slide all + /// module sections. + /// + /// @param[in] module + /// The module to load. + /// + /// @param[in] sections_offset + /// An offset that will be applied to all section file addresses + /// (the virtual addresses found in the object file itself). + /// + /// @return + /// An error to indicate success, fail, and any reason for + /// failure. + //------------------------------------------------------------------ + lldb::SBError + SetModuleLoadAddress (lldb::SBModule module, + int64_t sections_offset); + + + //------------------------------------------------------------------ + /// The the section base load addresses for all sections in a module. + /// + /// @param[in] module + /// The module to unload. + /// + /// @return + /// An error to indicate success, fail, and any reason for + /// failure. + //------------------------------------------------------------------ + lldb::SBError + ClearModuleLoadAddress (lldb::SBModule module); + + //------------------------------------------------------------------ + /// Find functions by name. + /// + /// @param[in] name + /// The name of the function we are looking for. + /// + /// @param[in] name_type_mask + /// A logical OR of one or more FunctionNameType enum bits that + /// indicate what kind of names should be used when doing the + /// lookup. Bits include fully qualified names, base names, + /// C++ methods, or ObjC selectors. + /// See FunctionNameType for more details. + /// + /// @return + /// A lldb::SBSymbolContextList that gets filled in with all of + /// the symbol contexts for all the matches. + //------------------------------------------------------------------ + lldb::SBSymbolContextList + FindFunctions (const char *name, + uint32_t name_type_mask = lldb::eFunctionNameTypeAny); + + //------------------------------------------------------------------ + /// Find global and static variables by name. + /// + /// @param[in] name + /// The name of the global or static variable we are looking + /// for. + /// + /// @param[in] max_matches + /// Allow the number of matches to be limited to \a max_matches. + /// + /// @return + /// A list of matched variables in an SBValueList. + //------------------------------------------------------------------ + lldb::SBValueList + FindGlobalVariables (const char *name, + uint32_t max_matches); + + //------------------------------------------------------------------ + /// Find the first global (or static) variable by name. + /// + /// @param[in] name + /// The name of the global or static variable we are looking + /// for. + /// + /// @return + /// An SBValue that gets filled in with the found variable (if any). + //------------------------------------------------------------------ + lldb::SBValue + FindFirstGlobalVariable (const char* name); + + void + Clear (); + + lldb::SBAddress + ResolveLoadAddress (lldb::addr_t vm_addr); + + SBSymbolContext + ResolveSymbolContextForAddress (const SBAddress& addr, + uint32_t resolve_scope); + + lldb::SBBreakpoint + BreakpointCreateByLocation (const char *file, uint32_t line); + + lldb::SBBreakpoint + BreakpointCreateByLocation (const lldb::SBFileSpec &file_spec, uint32_t line); + + lldb::SBBreakpoint + BreakpointCreateByName (const char *symbol_name, const char *module_name = NULL); + + // This version uses name_type_mask = eFunctionNameTypeAuto + lldb::SBBreakpoint + BreakpointCreateByName (const char *symbol_name, + const SBFileSpecList &module_list, + const SBFileSpecList &comp_unit_list); + + lldb::SBBreakpoint + BreakpointCreateByName (const char *symbol_name, + uint32_t name_type_mask, // Logical OR one or more FunctionNameType enum bits + const SBFileSpecList &module_list, + const SBFileSpecList &comp_unit_list); + + lldb::SBBreakpoint + BreakpointCreateByNames (const char *symbol_name[], + uint32_t num_names, + uint32_t name_type_mask, // Logical OR one or more FunctionNameType enum bits + const SBFileSpecList &module_list, + const SBFileSpecList &comp_unit_list); + + lldb::SBBreakpoint + BreakpointCreateByRegex (const char *symbol_name_regex, const char *module_name = NULL); + + lldb::SBBreakpoint + BreakpointCreateByRegex (const char *symbol_name_regex, + const SBFileSpecList &module_list, + const SBFileSpecList &comp_unit_list); + + lldb::SBBreakpoint + BreakpointCreateBySourceRegex (const char *source_regex, + const lldb::SBFileSpec &source_file, + const char *module_name = NULL); + + lldb::SBBreakpoint + BreakpointCreateBySourceRegex (const char *source_regex, + const SBFileSpecList &module_list, + const lldb::SBFileSpecList &source_file); + + lldb::SBBreakpoint + BreakpointCreateForException (lldb::LanguageType language, + bool catch_bp, + bool throw_bp); + + lldb::SBBreakpoint + BreakpointCreateByAddress (addr_t address); + + uint32_t + GetNumBreakpoints () const; + + lldb::SBBreakpoint + GetBreakpointAtIndex (uint32_t idx) const; + + bool + BreakpointDelete (break_id_t break_id); + + lldb::SBBreakpoint + FindBreakpointByID (break_id_t break_id); + + bool + EnableAllBreakpoints (); + + bool + DisableAllBreakpoints (); + + bool + DeleteAllBreakpoints (); + + uint32_t + GetNumWatchpoints () const; + + lldb::SBWatchpoint + GetWatchpointAtIndex (uint32_t idx) const; + + bool + DeleteWatchpoint (lldb::watch_id_t watch_id); + + lldb::SBWatchpoint + FindWatchpointByID (lldb::watch_id_t watch_id); + + lldb::SBWatchpoint + WatchAddress (lldb::addr_t addr, size_t size, bool read, bool write, SBError& error); + + bool + EnableAllWatchpoints (); + + bool + DisableAllWatchpoints (); + + bool + DeleteAllWatchpoints (); + + lldb::SBBroadcaster + GetBroadcaster () const; + + lldb::SBType + FindFirstType (const char* type); + + lldb::SBTypeList + FindTypes (const char* type); + + lldb::SBType + GetBasicType(lldb::BasicType type); + + SBSourceManager + GetSourceManager(); + + lldb::SBInstructionList + ReadInstructions (lldb::SBAddress base_addr, uint32_t count); + + lldb::SBInstructionList + ReadInstructions (lldb::SBAddress base_addr, uint32_t count, const char *flavor_string); + + lldb::SBInstructionList + GetInstructions (lldb::SBAddress base_addr, const void *buf, size_t size); + + // The "WithFlavor" is necessary to keep SWIG from getting confused about overloaded arguments when + // using the buf + size -> Python Object magic. + + lldb::SBInstructionList + GetInstructionsWithFlavor (lldb::SBAddress base_addr, const char *flavor_string, const void *buf, size_t size); + + lldb::SBInstructionList + GetInstructions (lldb::addr_t base_addr, const void *buf, size_t size); + + lldb::SBInstructionList + GetInstructionsWithFlavor (lldb::addr_t base_addr, const char *flavor_string, const void *buf, size_t size); + + lldb::SBSymbolContextList + FindSymbols (const char *name, + lldb::SymbolType type = eSymbolTypeAny); + + bool + operator == (const lldb::SBTarget &rhs) const; + + bool + operator != (const lldb::SBTarget &rhs) const; + + bool + GetDescription (lldb::SBStream &description, lldb::DescriptionLevel description_level); + + lldb::SBValue + EvaluateExpression (const char *expr, const SBExpressionOptions &options); + + lldb::addr_t + GetStackRedZoneSize(); + +protected: + friend class SBAddress; + friend class SBBlock; + friend class SBDebugger; + friend class SBFunction; + friend class SBInstruction; + friend class SBModule; + friend class SBProcess; + friend class SBSection; + friend class SBSourceManager; + friend class SBSymbol; + friend class SBValue; + + //------------------------------------------------------------------ + // Constructors are private, use static Target::Create function to + // create an instance of this class. + //------------------------------------------------------------------ + + lldb::TargetSP + GetSP () const; + + void + SetSP (const lldb::TargetSP& target_sp); + + +private: + //------------------------------------------------------------------ + // For Target only + //------------------------------------------------------------------ + + lldb::TargetSP m_opaque_sp; +}; + +} // namespace lldb + +#endif // LLDB_SBTarget_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/API/SBThread.h b/contrib/llvm/tools/lldb/include/lldb/API/SBThread.h new file mode 100644 index 00000000000..9645f925035 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/API/SBThread.h @@ -0,0 +1,220 @@ +//===-- SBThread.h ----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SBThread_h_ +#define LLDB_SBThread_h_ + +#include "lldb/API/SBDefines.h" + +#include + +namespace lldb { + +class SBFrame; + +class SBThread +{ +public: + enum + { + eBroadcastBitStackChanged = (1 << 0), + eBroadcastBitThreadSuspended = (1 << 1), + eBroadcastBitThreadResumed = (1 << 2), + eBroadcastBitSelectedFrameChanged = (1 << 3), + eBroadcastBitThreadSelected = (1 << 4) + }; + + static const char * + GetBroadcasterClassName (); + + SBThread (); + + SBThread (const lldb::SBThread &thread); + + SBThread (const lldb::ThreadSP& lldb_object_sp); + + ~SBThread(); + + bool + IsValid() const; + + void + Clear (); + + lldb::StopReason + GetStopReason(); + + /// Get the number of words associated with the stop reason. + /// See also GetStopReasonDataAtIndex(). + size_t + GetStopReasonDataCount(); + + //-------------------------------------------------------------------------- + /// Get information associated with a stop reason. + /// + /// Breakpoint stop reasons will have data that consists of pairs of + /// breakpoint IDs followed by the breakpoint location IDs (they always come + /// in pairs). + /// + /// Stop Reason Count Data Type + /// ======================== ===== ========================================= + /// eStopReasonNone 0 + /// eStopReasonTrace 0 + /// eStopReasonBreakpoint N duple: {breakpoint id, location id} + /// eStopReasonWatchpoint 1 watchpoint id + /// eStopReasonSignal 1 unix signal number + /// eStopReasonException N exception data + /// eStopReasonExec 0 + /// eStopReasonPlanComplete 0 + //-------------------------------------------------------------------------- + uint64_t + GetStopReasonDataAtIndex(uint32_t idx); + + size_t + GetStopDescription (char *dst, size_t dst_len); + + SBValue + GetStopReturnValue (); + + lldb::tid_t + GetThreadID () const; + + uint32_t + GetIndexID () const; + + const char * + GetName () const; + + const char * + GetQueueName() const; + + void + StepOver (lldb::RunMode stop_other_threads = lldb::eOnlyDuringStepping); + + void + StepInto (lldb::RunMode stop_other_threads = lldb::eOnlyDuringStepping); + + void + StepInto (const char *target_name, lldb::RunMode stop_other_threads = lldb::eOnlyDuringStepping); + + void + StepOut (); + + void + StepOutOfFrame (lldb::SBFrame &frame); + + void + StepInstruction(bool step_over); + + SBError + StepOverUntil (lldb::SBFrame &frame, + lldb::SBFileSpec &file_spec, + uint32_t line); + + void + RunToAddress (lldb::addr_t addr); + + SBError + ReturnFromFrame (SBFrame &frame, SBValue &return_value); + + //-------------------------------------------------------------------------- + /// LLDB currently supports process centric debugging which means when any + /// thread in a process stops, all other threads are stopped. The Suspend() + /// call here tells our process to suspend a thread and not let it run when + /// the other threads in a process are allowed to run. So when + /// SBProcess::Continue() is called, any threads that aren't suspended will + /// be allowed to run. If any of the SBThread functions for stepping are + /// called (StepOver, StepInto, StepOut, StepInstruction, RunToAddres), the + /// thread will not be allowed to run and these funtions will simply return. + /// + /// Eventually we plan to add support for thread centric debugging where + /// each thread is controlled individually and each thread would broadcast + /// its state, but we haven't implemented this yet. + /// + /// Likewise the SBThread::Resume() call will again allow the thread to run + /// when the process is continued. + /// + /// Suspend() and Resume() functions are not currently reference counted, if + /// anyone has the need for them to be reference counted, please let us + /// know. + //-------------------------------------------------------------------------- + bool + Suspend(); + + bool + Resume (); + + bool + IsSuspended(); + + bool + IsStopped(); + + uint32_t + GetNumFrames (); + + lldb::SBFrame + GetFrameAtIndex (uint32_t idx); + + lldb::SBFrame + GetSelectedFrame (); + + lldb::SBFrame + SetSelectedFrame (uint32_t frame_idx); + + static bool + EventIsThreadEvent (const SBEvent &event); + + static SBFrame + GetStackFrameFromEvent (const SBEvent &event); + + static SBThread + GetThreadFromEvent (const SBEvent &event); + + lldb::SBProcess + GetProcess (); + + const lldb::SBThread & + operator = (const lldb::SBThread &rhs); + + bool + operator == (const lldb::SBThread &rhs) const; + + bool + operator != (const lldb::SBThread &rhs) const; + + bool + GetDescription (lldb::SBStream &description) const; + + bool + GetStatus (lldb::SBStream &status) const; + +protected: + friend class SBBreakpoint; + friend class SBBreakpointLocation; + friend class SBFrame; + friend class SBProcess; + friend class SBDebugger; + friend class SBValue; + + void + SetThread (const lldb::ThreadSP& lldb_object_sp); + +#ifndef SWIG + SBError + ResumeNewPlan (lldb_private::ExecutionContext &exe_ctx, lldb_private::ThreadPlan *new_plan); +#endif + +private: + lldb::ExecutionContextRefSP m_opaque_sp; +}; + +} // namespace lldb + +#endif // LLDB_SBThread_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/API/SBType.h b/contrib/llvm/tools/lldb/include/lldb/API/SBType.h new file mode 100644 index 00000000000..3729b2f84b9 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/API/SBType.h @@ -0,0 +1,244 @@ +//===-- SBType.h ------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SBType_h_ +#define LLDB_SBType_h_ + +#include "lldb/API/SBDefines.h" + +namespace lldb { + +class SBTypeList; + +class SBTypeMember +{ +public: + SBTypeMember (); + + SBTypeMember (const lldb::SBTypeMember& rhs); + + ~SBTypeMember(); + + lldb::SBTypeMember& + operator = (const lldb::SBTypeMember& rhs); + + bool + IsValid() const; + + const char * + GetName (); + + lldb::SBType + GetType (); + + uint64_t + GetOffsetInBytes(); + + uint64_t + GetOffsetInBits(); + + bool + IsBitfield(); + + uint32_t + GetBitfieldSizeInBits(); + + bool + GetDescription (lldb::SBStream &description, + lldb::DescriptionLevel description_level); + +protected: + friend class SBType; + + void + reset (lldb_private::TypeMemberImpl *); + + lldb_private::TypeMemberImpl & + ref (); + + const lldb_private::TypeMemberImpl & + ref () const; + + std::unique_ptr m_opaque_ap; +}; + +class SBType +{ +public: + + SBType(); + + SBType (const lldb::SBType &rhs); + + ~SBType (); + + bool + IsValid() const; + + uint64_t + GetByteSize(); + + bool + IsPointerType(); + + bool + IsReferenceType(); + + bool + IsFunctionType (); + + bool + IsPolymorphicClass (); + + lldb::SBType + GetPointerType(); + + lldb::SBType + GetPointeeType(); + + lldb::SBType + GetReferenceType(); + + lldb::SBType + GetDereferencedType(); + + lldb::SBType + GetUnqualifiedType(); + + lldb::SBType + GetCanonicalType(); + // Get the "lldb::BasicType" enumeration for a type. If a type is not a basic + // type eBasicTypeInvalid will be returned + lldb::BasicType + GetBasicType(); + + // The call below confusing and should really be renamed to "CreateBasicType" + lldb::SBType + GetBasicType(lldb::BasicType type); + + uint32_t + GetNumberOfFields (); + + uint32_t + GetNumberOfDirectBaseClasses (); + + uint32_t + GetNumberOfVirtualBaseClasses (); + + lldb::SBTypeMember + GetFieldAtIndex (uint32_t idx); + + lldb::SBTypeMember + GetDirectBaseClassAtIndex (uint32_t idx); + + lldb::SBTypeMember + GetVirtualBaseClassAtIndex (uint32_t idx); + + uint32_t + GetNumberOfTemplateArguments (); + + lldb::SBType + GetTemplateArgumentType (uint32_t idx); + + lldb::TemplateArgumentKind + GetTemplateArgumentKind (uint32_t idx); + + lldb::SBType + GetFunctionReturnType (); + + lldb::SBTypeList + GetFunctionArgumentTypes (); + + const char* + GetName(); + + lldb::TypeClass + GetTypeClass (); + + bool + IsTypeComplete (); + + bool + GetDescription (lldb::SBStream &description, + lldb::DescriptionLevel description_level); + + lldb::SBType & + operator = (const lldb::SBType &rhs); + + bool + operator == (lldb::SBType &rhs); + + bool + operator != (lldb::SBType &rhs); + +protected: + + lldb_private::TypeImpl & + ref (); + + const lldb_private::TypeImpl & + ref () const; + + lldb::TypeImplSP + GetSP (); + + void + SetSP (const lldb::TypeImplSP &type_impl_sp); + + lldb::TypeImplSP m_opaque_sp; + + friend class SBFunction; + friend class SBModule; + friend class SBTarget; + friend class SBTypeNameSpecifier; + friend class SBTypeMember; + friend class SBTypeList; + friend class SBValue; + + SBType (const lldb_private::ClangASTType &); + SBType (const lldb::TypeSP &); + SBType (const lldb::TypeImplSP &); + +}; + +class SBTypeList +{ +public: + SBTypeList(); + + SBTypeList(const lldb::SBTypeList& rhs); + + ~SBTypeList(); + + lldb::SBTypeList& + operator = (const lldb::SBTypeList& rhs); + + bool + IsValid(); + + void + Append (lldb::SBType type); + + lldb::SBType + GetTypeAtIndex (uint32_t index); + + uint32_t + GetSize(); + + +private: + std::unique_ptr m_opaque_ap; + friend class SBModule; + friend class SBCompileUnit; +}; + + +} // namespace lldb + +#endif // LLDB_SBType_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/API/SBTypeCategory.h b/contrib/llvm/tools/lldb/include/lldb/API/SBTypeCategory.h new file mode 100644 index 00000000000..f123e931e17 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/API/SBTypeCategory.h @@ -0,0 +1,168 @@ +//===-- SBTypeCategory.h --------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SBTypeCategory_h_ +#define LLDB_SBTypeCategory_h_ + +#include "lldb/API/SBDefines.h" + +namespace lldb { + + class SBTypeCategory + { + public: + + SBTypeCategory(); + + SBTypeCategory (const lldb::SBTypeCategory &rhs); + + ~SBTypeCategory (); + + bool + IsValid() const; + + bool + GetEnabled (); + + void + SetEnabled (bool); + + const char* + GetName(); + + bool + GetDescription (lldb::SBStream &description, + lldb::DescriptionLevel description_level); + + uint32_t + GetNumFormats (); + + uint32_t + GetNumSummaries (); + + uint32_t + GetNumFilters (); + +#ifndef LLDB_DISABLE_PYTHON + uint32_t + GetNumSynthetics (); +#endif + + SBTypeNameSpecifier + GetTypeNameSpecifierForFilterAtIndex (uint32_t); + + SBTypeNameSpecifier + GetTypeNameSpecifierForFormatAtIndex (uint32_t); + + SBTypeNameSpecifier + GetTypeNameSpecifierForSummaryAtIndex (uint32_t); + +#ifndef LLDB_DISABLE_PYTHON + SBTypeNameSpecifier + GetTypeNameSpecifierForSyntheticAtIndex (uint32_t); +#endif + + SBTypeFilter + GetFilterForType (SBTypeNameSpecifier); + + SBTypeFormat + GetFormatForType (SBTypeNameSpecifier); + +#ifndef LLDB_DISABLE_PYTHON + SBTypeSummary + GetSummaryForType (SBTypeNameSpecifier); +#endif + +#ifndef LLDB_DISABLE_PYTHON + SBTypeSynthetic + GetSyntheticForType (SBTypeNameSpecifier); +#endif + +#ifndef LLDB_DISABLE_PYTHON + SBTypeFilter + GetFilterAtIndex (uint32_t); +#endif + + SBTypeFormat + GetFormatAtIndex (uint32_t); + +#ifndef LLDB_DISABLE_PYTHON + SBTypeSummary + GetSummaryAtIndex (uint32_t); +#endif + +#ifndef LLDB_DISABLE_PYTHON + SBTypeSynthetic + GetSyntheticAtIndex (uint32_t); +#endif + + bool + AddTypeFormat (SBTypeNameSpecifier, + SBTypeFormat); + + bool + DeleteTypeFormat (SBTypeNameSpecifier); + +#ifndef LLDB_DISABLE_PYTHON + bool + AddTypeSummary (SBTypeNameSpecifier, + SBTypeSummary); +#endif + + bool + DeleteTypeSummary (SBTypeNameSpecifier); + + bool + AddTypeFilter (SBTypeNameSpecifier, + SBTypeFilter); + + bool + DeleteTypeFilter (SBTypeNameSpecifier); + +#ifndef LLDB_DISABLE_PYTHON + bool + AddTypeSynthetic (SBTypeNameSpecifier, + SBTypeSynthetic); + + bool + DeleteTypeSynthetic (SBTypeNameSpecifier); +#endif + + lldb::SBTypeCategory & + operator = (const lldb::SBTypeCategory &rhs); + + bool + operator == (lldb::SBTypeCategory &rhs); + + bool + operator != (lldb::SBTypeCategory &rhs); + + protected: + friend class SBDebugger; + + lldb::TypeCategoryImplSP + GetSP (); + + void + SetSP (const lldb::TypeCategoryImplSP &typecategory_impl_sp); + + TypeCategoryImplSP m_opaque_sp; + + SBTypeCategory (const lldb::TypeCategoryImplSP &); + + SBTypeCategory (const char*); + + bool + IsDefaultCategory(); + + }; + +} // namespace lldb + +#endif // LLDB_SBTypeCategory_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/API/SBTypeFilter.h b/contrib/llvm/tools/lldb/include/lldb/API/SBTypeFilter.h new file mode 100644 index 00000000000..016954943e2 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/API/SBTypeFilter.h @@ -0,0 +1,92 @@ +//===-- SBTypeFilter.h --------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SBTypeFilter_h_ +#define LLDB_SBTypeFilter_h_ + +#include "lldb/API/SBDefines.h" + +namespace lldb { + + class SBTypeFilter + { + public: + + SBTypeFilter(); + + SBTypeFilter (uint32_t options); // see lldb::eTypeOption values + + SBTypeFilter (const lldb::SBTypeFilter &rhs); + + ~SBTypeFilter (); + + bool + IsValid() const; + + uint32_t + GetNumberOfExpressionPaths (); + + const char* + GetExpressionPathAtIndex (uint32_t i); + + bool + ReplaceExpressionPathAtIndex (uint32_t i, const char* item); + + void + AppendExpressionPath (const char* item); + + void + Clear(); + + uint32_t + GetOptions(); + + void + SetOptions (uint32_t); + + bool + GetDescription (lldb::SBStream &description, + lldb::DescriptionLevel description_level); + + lldb::SBTypeFilter & + operator = (const lldb::SBTypeFilter &rhs); + + bool + IsEqualTo (lldb::SBTypeFilter &rhs); + + bool + operator == (lldb::SBTypeFilter &rhs); + + bool + operator != (lldb::SBTypeFilter &rhs); + + protected: + friend class SBDebugger; + friend class SBTypeCategory; + friend class SBValue; + + lldb::TypeFilterImplSP + GetSP (); + + void + SetSP (const lldb::TypeFilterImplSP &typefilter_impl_sp); + + lldb::TypeFilterImplSP m_opaque_sp; + + SBTypeFilter (const lldb::TypeFilterImplSP &); + + bool + CopyOnWrite_Impl(); + + }; + + +} // namespace lldb + +#endif // LLDB_SBTypeFilter_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/API/SBTypeFormat.h b/contrib/llvm/tools/lldb/include/lldb/API/SBTypeFormat.h new file mode 100644 index 00000000000..cd6345fbe6f --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/API/SBTypeFormat.h @@ -0,0 +1,84 @@ +//===-- SBTypeFormat.h --------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SBTypeFormat_h_ +#define LLDB_SBTypeFormat_h_ + +#include "lldb/API/SBDefines.h" + +namespace lldb { + +class SBTypeFormat +{ +public: + + SBTypeFormat(); + + SBTypeFormat (lldb::Format format, + uint32_t options = 0); // see lldb::eTypeOption values + + SBTypeFormat (const lldb::SBTypeFormat &rhs); + + ~SBTypeFormat (); + + bool + IsValid() const; + + lldb::Format + GetFormat (); + + uint32_t + GetOptions(); + + void + SetFormat (lldb::Format); + + void + SetOptions (uint32_t); + + bool + GetDescription (lldb::SBStream &description, + lldb::DescriptionLevel description_level); + + lldb::SBTypeFormat & + operator = (const lldb::SBTypeFormat &rhs); + + bool + IsEqualTo (lldb::SBTypeFormat &rhs); + + bool + operator == (lldb::SBTypeFormat &rhs); + + bool + operator != (lldb::SBTypeFormat &rhs); + +protected: + friend class SBDebugger; + friend class SBTypeCategory; + friend class SBValue; + + lldb::TypeFormatImplSP + GetSP (); + + void + SetSP (const lldb::TypeFormatImplSP &typeformat_impl_sp); + + lldb::TypeFormatImplSP m_opaque_sp; + + SBTypeFormat (const lldb::TypeFormatImplSP &); + + bool + CopyOnWrite_Impl(); + +}; + + +} // namespace lldb + +#endif // LLDB_SBTypeFormat_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/API/SBTypeNameSpecifier.h b/contrib/llvm/tools/lldb/include/lldb/API/SBTypeNameSpecifier.h new file mode 100644 index 00000000000..19d1988aa0c --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/API/SBTypeNameSpecifier.h @@ -0,0 +1,77 @@ +//===-- SBTypeNameSpecifier.h --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SBTypeNameSpecifier_h_ +#define LLDB_SBTypeNameSpecifier_h_ + +#include "lldb/API/SBDefines.h" + +namespace lldb { + + class SBTypeNameSpecifier + { + public: + + SBTypeNameSpecifier(); + + SBTypeNameSpecifier (const char* name, + bool is_regex = false); + + SBTypeNameSpecifier (SBType type); + + SBTypeNameSpecifier (const lldb::SBTypeNameSpecifier &rhs); + + ~SBTypeNameSpecifier (); + + bool + IsValid() const; + + const char* + GetName(); + + SBType + GetType (); + + bool + IsRegex(); + + bool + GetDescription (lldb::SBStream &description, + lldb::DescriptionLevel description_level); + + lldb::SBTypeNameSpecifier & + operator = (const lldb::SBTypeNameSpecifier &rhs); + + bool + IsEqualTo (lldb::SBTypeNameSpecifier &rhs); + + bool + operator == (lldb::SBTypeNameSpecifier &rhs); + + bool + operator != (lldb::SBTypeNameSpecifier &rhs); + + protected: + friend class SBDebugger; + friend class SBTypeCategory; + + lldb::TypeNameSpecifierImplSP + GetSP (); + + void + SetSP (const lldb::TypeNameSpecifierImplSP &type_namespec_sp); + + lldb::TypeNameSpecifierImplSP m_opaque_sp; + + SBTypeNameSpecifier (const lldb::TypeNameSpecifierImplSP &); + }; + +} // namespace lldb + +#endif // LLDB_SBTypeNameSpecifier_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/API/SBTypeSummary.h b/contrib/llvm/tools/lldb/include/lldb/API/SBTypeSummary.h new file mode 100644 index 00000000000..67a8607511c --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/API/SBTypeSummary.h @@ -0,0 +1,115 @@ +//===-- SBTypeSummary.h -------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SBTypeSummary_h_ +#define LLDB_SBTypeSummary_h_ + +#include "lldb/API/SBDefines.h" + +#ifndef LLDB_DISABLE_PYTHON + +namespace lldb { + + class SBTypeSummary + { + public: + + SBTypeSummary(); + + static SBTypeSummary + CreateWithSummaryString (const char* data, + uint32_t options = 0); // see lldb::eTypeOption values + + static SBTypeSummary + CreateWithFunctionName (const char* data, + uint32_t options = 0); // see lldb::eTypeOption values + + static SBTypeSummary + CreateWithScriptCode (const char* data, + uint32_t options = 0); // see lldb::eTypeOption values + + SBTypeSummary (const lldb::SBTypeSummary &rhs); + + ~SBTypeSummary (); + + bool + IsValid() const; + + bool + IsFunctionCode(); + + bool + IsFunctionName(); + + bool + IsSummaryString(); + + const char* + GetData (); + + void + SetSummaryString (const char* data); + + void + SetFunctionName (const char* data); + + void + SetFunctionCode (const char* data); + + uint32_t + GetOptions (); + + void + SetOptions (uint32_t); + + bool + GetDescription (lldb::SBStream &description, + lldb::DescriptionLevel description_level); + + lldb::SBTypeSummary & + operator = (const lldb::SBTypeSummary &rhs); + + bool + IsEqualTo (lldb::SBTypeSummary &rhs); + + bool + operator == (lldb::SBTypeSummary &rhs); + + bool + operator != (lldb::SBTypeSummary &rhs); + + protected: + friend class SBDebugger; + friend class SBTypeCategory; + friend class SBValue; + + lldb::TypeSummaryImplSP + GetSP (); + + void + SetSP (const lldb::TypeSummaryImplSP &typefilter_impl_sp); + + lldb::TypeSummaryImplSP m_opaque_sp; + + SBTypeSummary (const lldb::TypeSummaryImplSP &); + + bool + CopyOnWrite_Impl(); + + bool + ChangeSummaryType (bool want_script); + + }; + + +} // namespace lldb + +#endif // LLDB_DISABLE_PYTHON + +#endif // LLDB_SBTypeSummary_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/API/SBTypeSynthetic.h b/contrib/llvm/tools/lldb/include/lldb/API/SBTypeSynthetic.h new file mode 100644 index 00000000000..e77cbfef598 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/API/SBTypeSynthetic.h @@ -0,0 +1,102 @@ +//===-- SBTypeSynthetic.h -----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SBTypeSynthetic_h_ +#define LLDB_SBTypeSynthetic_h_ + +#include "lldb/API/SBDefines.h" + +#ifndef LLDB_DISABLE_PYTHON + +namespace lldb { + + class SBTypeSynthetic + { + public: + + SBTypeSynthetic(); + + static SBTypeSynthetic + CreateWithClassName (const char* data, + uint32_t options = 0); // see lldb::eTypeOption values + + static SBTypeSynthetic + CreateWithScriptCode (const char* data, + uint32_t options = 0); // see lldb::eTypeOption values + + SBTypeSynthetic (const lldb::SBTypeSynthetic &rhs); + + ~SBTypeSynthetic (); + + bool + IsValid() const; + + bool + IsClassCode(); + + bool + IsClassName(); + + const char* + GetData (); + + void + SetClassName (const char* data); + + void + SetClassCode (const char* data); + + uint32_t + GetOptions (); + + void + SetOptions (uint32_t); + + bool + GetDescription (lldb::SBStream &description, + lldb::DescriptionLevel description_level); + + lldb::SBTypeSynthetic & + operator = (const lldb::SBTypeSynthetic &rhs); + + bool + IsEqualTo (lldb::SBTypeSynthetic &rhs); + + bool + operator == (lldb::SBTypeSynthetic &rhs); + + bool + operator != (lldb::SBTypeSynthetic &rhs); + + protected: + friend class SBDebugger; + friend class SBTypeCategory; + friend class SBValue; + + lldb::ScriptedSyntheticChildrenSP + GetSP (); + + void + SetSP (const lldb::ScriptedSyntheticChildrenSP &typefilter_impl_sp); + + lldb::ScriptedSyntheticChildrenSP m_opaque_sp; + + SBTypeSynthetic (const lldb::ScriptedSyntheticChildrenSP &); + + bool + CopyOnWrite_Impl(); + + }; + + +} // namespace lldb + +#endif // LLDB_DISABLE_PYTHON + +#endif // LLDB_SBTypeSynthetic_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/API/SBValue.h b/contrib/llvm/tools/lldb/include/lldb/API/SBValue.h new file mode 100644 index 00000000000..2b9a344b930 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/API/SBValue.h @@ -0,0 +1,488 @@ +//===-- SBValue.h -----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SBValue_h_ +#define LLDB_SBValue_h_ + +#include "lldb/API/SBData.h" +#include "lldb/API/SBDefines.h" +#include "lldb/API/SBType.h" + +class ValueImpl; +class ValueLocker; + +namespace lldb { + +class SBValue +{ +friend class ValueLocker; + +public: + SBValue (); + + SBValue (const lldb::SBValue &rhs); + + lldb::SBValue & + operator =(const lldb::SBValue &rhs); + + ~SBValue (); + + bool + IsValid(); + + void + Clear(); + + SBError + GetError(); + + lldb::user_id_t + GetID (); + + const char * + GetName(); + + const char * + GetTypeName (); + + size_t + GetByteSize (); + + bool + IsInScope (); + + lldb::Format + GetFormat (); + + void + SetFormat (lldb::Format format); + + const char * + GetValue (); + + int64_t + GetValueAsSigned (lldb::SBError& error, int64_t fail_value=0); + + uint64_t + GetValueAsUnsigned (lldb::SBError& error, uint64_t fail_value=0); + + int64_t + GetValueAsSigned(int64_t fail_value=0); + + uint64_t + GetValueAsUnsigned(uint64_t fail_value=0); + + ValueType + GetValueType (); + + bool + GetValueDidChange (); + + const char * + GetSummary (); + + const char * + GetObjectDescription (); + + lldb::SBValue + GetDynamicValue (lldb::DynamicValueType use_dynamic); + + lldb::SBValue + GetStaticValue (); + + lldb::SBValue + GetNonSyntheticValue (); + + lldb::DynamicValueType + GetPreferDynamicValue (); + + void + SetPreferDynamicValue (lldb::DynamicValueType use_dynamic); + + bool + GetPreferSyntheticValue (); + + void + SetPreferSyntheticValue (bool use_synthetic); + + bool + IsDynamic (); + + bool + IsSynthetic (); + + const char * + GetLocation (); + + // Deprecated - use the one that takes SBError& + bool + SetValueFromCString (const char *value_str); + + bool + SetValueFromCString (const char *value_str, lldb::SBError& error); + + lldb::SBTypeFormat + GetTypeFormat (); + +#ifndef LLDB_DISABLE_PYTHON + lldb::SBTypeSummary + GetTypeSummary (); +#endif + + lldb::SBTypeFilter + GetTypeFilter (); + +#ifndef LLDB_DISABLE_PYTHON + lldb::SBTypeSynthetic + GetTypeSynthetic (); +#endif + + lldb::SBValue + GetChildAtIndex (uint32_t idx); + + lldb::SBValue + CreateChildAtOffset (const char *name, uint32_t offset, lldb::SBType type); + + lldb::SBValue + Cast (lldb::SBType type); + + lldb::SBValue + CreateValueFromExpression (const char *name, const char* expression); + + lldb::SBValue + CreateValueFromExpression (const char *name, const char* expression, SBExpressionOptions &options); + + lldb::SBValue + CreateValueFromAddress (const char* name, + lldb::addr_t address, + lldb::SBType type); + + // this has no address! GetAddress() and GetLoadAddress() as well as AddressOf() + // on the return of this call all return invalid + lldb::SBValue + CreateValueFromData (const char* name, + lldb::SBData data, + lldb::SBType type); + + //------------------------------------------------------------------ + /// Get a child value by index from a value. + /// + /// Structs, unions, classes, arrays and and pointers have child + /// values that can be access by index. + /// + /// Structs and unions access child members using a zero based index + /// for each child member. For + /// + /// Classes reserve the first indexes for base classes that have + /// members (empty base classes are omitted), and all members of the + /// current class will then follow the base classes. + /// + /// Pointers differ depending on what they point to. If the pointer + /// points to a simple type, the child at index zero + /// is the only child value available, unless \a synthetic_allowed + /// is \b true, in which case the pointer will be used as an array + /// and can create 'synthetic' child values using positive or + /// negative indexes. If the pointer points to an aggregate type + /// (an array, class, union, struct), then the pointee is + /// transparently skipped and any children are going to be the indexes + /// of the child values within the aggregate type. For example if + /// we have a 'Point' type and we have a SBValue that contains a + /// pointer to a 'Point' type, then the child at index zero will be + /// the 'x' member, and the child at index 1 will be the 'y' member + /// (the child at index zero won't be a 'Point' instance). + /// + /// Arrays have a preset number of children that can be accessed by + /// index and will returns invalid child values for indexes that are + /// out of bounds unless the \a synthetic_allowed is \b true. In this + /// case the array can create 'synthetic' child values for indexes + /// that aren't in the array bounds using positive or negative + /// indexes. + /// + /// @param[in] idx + /// The index of the child value to get + /// + /// @param[in] use_dynamic + /// An enumeration that specifies wether to get dynamic values, + /// and also if the target can be run to figure out the dynamic + /// type of the child value. + /// + /// @param[in] synthetic_allowed + /// If \b true, then allow child values to be created by index + /// for pointers and arrays for indexes that normally wouldn't + /// be allowed. + /// + /// @return + /// A new SBValue object that represents the child member value. + //------------------------------------------------------------------ + lldb::SBValue + GetChildAtIndex (uint32_t idx, + lldb::DynamicValueType use_dynamic, + bool can_create_synthetic); + + // Matches children of this object only and will match base classes and + // member names if this is a clang typed object. + uint32_t + GetIndexOfChildWithName (const char *name); + + // Matches child members of this object and child members of any base + // classes. + lldb::SBValue + GetChildMemberWithName (const char *name); + + // Matches child members of this object and child members of any base + // classes. + lldb::SBValue + GetChildMemberWithName (const char *name, lldb::DynamicValueType use_dynamic); + + // Expands nested expressions like .a->b[0].c[1]->d + lldb::SBValue + GetValueForExpressionPath(const char* expr_path); + + lldb::SBValue + AddressOf(); + + lldb::addr_t + GetLoadAddress(); + + lldb::SBAddress + GetAddress(); + + //------------------------------------------------------------------ + /// Get an SBData wrapping what this SBValue points to. + /// + /// This method will dereference the current SBValue, if its + /// data type is a T* or T[], and extract item_count elements + /// of type T from it, copying their contents in an SBData. + /// + /// @param[in] item_idx + /// The index of the first item to retrieve. For an array + /// this is equivalent to array[item_idx], for a pointer + /// to *(pointer + item_idx). In either case, the measurement + /// unit for item_idx is the sizeof(T) rather than the byte + /// + /// @param[in] item_count + /// How many items should be copied into the output. By default + /// only one item is copied, but more can be asked for. + /// + /// @return + /// An SBData with the contents of the copied items, on success. + /// An empty SBData otherwise. + //------------------------------------------------------------------ + lldb::SBData + GetPointeeData (uint32_t item_idx = 0, + uint32_t item_count = 1); + + //------------------------------------------------------------------ + /// Get an SBData wrapping the contents of this SBValue. + /// + /// This method will read the contents of this object in memory + /// and copy them into an SBData for future use. + /// + /// @return + /// An SBData with the contents of this SBValue, on success. + /// An empty SBData otherwise. + //------------------------------------------------------------------ + lldb::SBData + GetData (); + + bool + SetData (lldb::SBData &data, lldb::SBError& error); + + lldb::SBDeclaration + GetDeclaration (); + + //------------------------------------------------------------------ + /// Find out if a SBValue might have children. + /// + /// This call is much more efficient than GetNumChildren() as it + /// doesn't need to complete the underlying type. This is designed + /// to be used in a UI environment in order to detect if the + /// disclosure triangle should be displayed or not. + /// + /// This function returns true for class, union, structure, + /// pointers, references, arrays and more. Again, it does so without + /// doing any expensive type completion. + /// + /// @return + /// Returns \b true if the SBValue might have children, or \b + /// false otherwise. + //------------------------------------------------------------------ + bool + MightHaveChildren (); + + uint32_t + GetNumChildren (); + + void * + GetOpaqueType(); + + lldb::SBTarget + GetTarget(); + + lldb::SBProcess + GetProcess(); + + lldb::SBThread + GetThread(); + + lldb::SBFrame + GetFrame(); + + lldb::SBValue + Dereference (); + + bool + TypeIsPointerType (); + + lldb::SBType + GetType(); + + bool + GetDescription (lldb::SBStream &description); + + bool + GetExpressionPath (lldb::SBStream &description); + + bool + GetExpressionPath (lldb::SBStream &description, + bool qualify_cxx_base_classes); + + SBValue (const lldb::ValueObjectSP &value_sp); + + //------------------------------------------------------------------ + /// Watch this value if it resides in memory. + /// + /// Sets a watchpoint on the value. + /// + /// @param[in] resolve_location + /// Resolve the location of this value once and watch its address. + /// This value must currently be set to \b true as watching all + /// locations of a variable or a variable path is not yet supported, + /// though we plan to support it in the future. + /// + /// @param[in] read + /// Stop when this value is accessed. + /// + /// @param[in] write + /// Stop when this value is modified + /// + /// @param[out] + /// An error object. Contains the reason if there is some failure. + /// + /// @return + /// An SBWatchpoint object. This object might not be valid upon + /// return due to a value not being contained in memory, too + /// large, or watchpoint resources are not available or all in + /// use. + //------------------------------------------------------------------ + lldb::SBWatchpoint + Watch (bool resolve_location, bool read, bool write, SBError &error); + + // Backward compatibility fix in the interim. + lldb::SBWatchpoint + Watch (bool resolve_location, bool read, bool write); + + //------------------------------------------------------------------ + /// Watch this value that this value points to in memory + /// + /// Sets a watchpoint on the value. + /// + /// @param[in] resolve_location + /// Resolve the location of this value once and watch its address. + /// This value must currently be set to \b true as watching all + /// locations of a variable or a variable path is not yet supported, + /// though we plan to support it in the future. + /// + /// @param[in] read + /// Stop when this value is accessed. + /// + /// @param[in] write + /// Stop when this value is modified + /// + /// @param[out] + /// An error object. Contains the reason if there is some failure. + /// + /// @return + /// An SBWatchpoint object. This object might not be valid upon + /// return due to a value not being contained in memory, too + /// large, or watchpoint resources are not available or all in + /// use. + //------------------------------------------------------------------ + lldb::SBWatchpoint + WatchPointee (bool resolve_location, bool read, bool write, SBError &error); + + //------------------------------------------------------------------ + /// Same as the protected version of GetSP that takes a locker, except that we make the + /// locker locally in the function. Since the Target API mutex is recursive, and the + /// StopLocker is a read lock, you can call this function even if you are already + /// holding the two above-mentioned locks. + /// + /// @return + /// A ValueObjectSP of the best kind (static, dynamic or synthetic) we + /// can cons up, in accordance with the SBValue's settings. + //------------------------------------------------------------------ + lldb::ValueObjectSP + GetSP () const; + +protected: + friend class SBBlock; + friend class SBFrame; + friend class SBTarget; + friend class SBThread; + friend class SBValueList; + + //------------------------------------------------------------------ + /// Get the appropriate ValueObjectSP from this SBValue, consulting the + /// use_dynamic and use_synthetic options passed in to SetSP when the + /// SBValue's contents were set. Since this often requires examining memory, + /// and maybe even running code, it needs to acquire the Target API and Process StopLock. + /// Those are held in an opaque class ValueLocker which is currently local to SBValue.cpp. + /// So you don't have to get these yourself just default construct a ValueLocker, and pass it into this. + /// If we need to make a ValueLocker and use it in some other .cpp file, we'll have to move it to + /// ValueObject.h/cpp or somewhere else convenient. We haven't needed to so far. + /// + /// @param[in] value_locker + /// An object that will hold the Target API, and Process RunLocks, and + /// auto-destroy them when it goes out of scope. Currently this is only useful in + /// SBValue.cpp. + /// + /// @return + /// A ValueObjectSP of the best kind (static, dynamic or synthetic) we + /// can cons up, in accordance with the SBValue's settings. + //------------------------------------------------------------------ + lldb::ValueObjectSP + GetSP (ValueLocker &value_locker) const; + + // these calls do the right thing WRT adjusting their settings according to the target's preferences + void + SetSP (const lldb::ValueObjectSP &sp); + + void + SetSP (const lldb::ValueObjectSP &sp, bool use_synthetic); + + void + SetSP (const lldb::ValueObjectSP &sp, lldb::DynamicValueType use_dynamic); + + void + SetSP (const lldb::ValueObjectSP &sp, lldb::DynamicValueType use_dynamic, bool use_synthetic); + + void + SetSP (const lldb::ValueObjectSP &sp, lldb::DynamicValueType use_dynamic, bool use_synthetic, const char *name); + +private: + typedef std::shared_ptr ValueImplSP; + ValueImplSP m_opaque_sp; + + void + SetSP (ValueImplSP impl_sp); +}; + +} // namespace lldb + +#endif // LLDB_SBValue_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/API/SBValueList.h b/contrib/llvm/tools/lldb/include/lldb/API/SBValueList.h new file mode 100644 index 00000000000..b9a6aedea3c --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/API/SBValueList.h @@ -0,0 +1,93 @@ +//===-- SBValueList.h -------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SBValueList_h_ +#define LLDB_SBValueList_h_ + +#include "lldb/API/SBDefines.h" + +class ValueListImpl; + +namespace lldb { + +class SBValueList +{ +public: + + SBValueList (); + + SBValueList (const lldb::SBValueList &rhs); + + ~SBValueList(); + + bool + IsValid() const; + + void + Clear(); + + void + Append (const lldb::SBValue &val_obj); + + void + Append (const lldb::SBValueList& value_list); + + uint32_t + GetSize() const; + + lldb::SBValue + GetValueAtIndex (uint32_t idx) const; + + lldb::SBValue + FindValueObjectByUID (lldb::user_id_t uid); + + const lldb::SBValueList & + operator = (const lldb::SBValueList &rhs); + +protected: + + // only useful for visualizing the pointer or comparing two SBValueLists + // to see if they are backed by the same underlying Impl. + void * + opaque_ptr (); + +private: + friend class SBFrame; + + SBValueList (const ValueListImpl *lldb_object_ptr); + + void + Append (lldb::ValueObjectSP& val_obj_sp); + + void + CreateIfNeeded (); + + ValueListImpl * + operator -> (); + + ValueListImpl & + operator* (); + + const ValueListImpl * + operator -> () const; + + const ValueListImpl & + operator* () const; + + + ValueListImpl & + ref (); + + std::unique_ptr m_opaque_ap; +}; + + +} // namespace lldb + +#endif // LLDB_SBValueList_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/API/SBWatchpoint.h b/contrib/llvm/tools/lldb/include/lldb/API/SBWatchpoint.h new file mode 100644 index 00000000000..9bf51fd1ad0 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/API/SBWatchpoint.h @@ -0,0 +1,104 @@ +//===-- SBWatchpoint.h ----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SBWatchpoint_h_ +#define LLDB_SBWatchpoint_h_ + +#include "lldb/API/SBDefines.h" + +namespace lldb { + +class SBWatchpoint +{ +public: + + SBWatchpoint (); + + SBWatchpoint (const lldb::SBWatchpoint &rhs); + + SBWatchpoint (const lldb::WatchpointSP &wp_sp); + + ~SBWatchpoint (); + + const lldb::SBWatchpoint & + operator = (const lldb::SBWatchpoint &rhs); + + bool + IsValid() const; + + SBError + GetError(); + + watch_id_t + GetID (); + + /// With -1 representing an invalid hardware index. + int32_t + GetHardwareIndex (); + + lldb::addr_t + GetWatchAddress (); + + size_t + GetWatchSize(); + + void + SetEnabled(bool enabled); + + bool + IsEnabled (); + + uint32_t + GetHitCount (); + + uint32_t + GetIgnoreCount (); + + void + SetIgnoreCount (uint32_t n); + + const char * + GetCondition (); + + void + SetCondition (const char *condition); + + bool + GetDescription (lldb::SBStream &description, DescriptionLevel level); + + void + Clear (); + + lldb::WatchpointSP + GetSP () const; + + void + SetSP (const lldb::WatchpointSP &sp); + + static bool + EventIsWatchpointEvent (const lldb::SBEvent &event); + + static lldb::WatchpointEventType + GetWatchpointEventTypeFromEvent (const lldb::SBEvent& event); + + static lldb::SBWatchpoint + GetWatchpointFromEvent (const lldb::SBEvent& event); + +private: + friend class SBTarget; + friend class SBValue; + + + lldb::WatchpointSP m_opaque_sp; + +}; + +} // namespace lldb + +#endif // LLDB_SBWatchpoint_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Breakpoint/Breakpoint.h b/contrib/llvm/tools/lldb/include/lldb/Breakpoint/Breakpoint.h new file mode 100644 index 00000000000..bd11a1c91e2 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Breakpoint/Breakpoint.h @@ -0,0 +1,630 @@ +//===-- Breakpoint.h --------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_Breakpoint_h_ +#define liblldb_Breakpoint_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Breakpoint/BreakpointLocationList.h" +#include "lldb/Breakpoint/BreakpointOptions.h" +#include "lldb/Breakpoint/BreakpointLocationCollection.h" +#include "lldb/Breakpoint/Stoppoint.h" +#include "lldb/Core/SearchFilter.h" +#include "lldb/Core/Event.h" +#include "lldb/Core/StringList.h" + +namespace lldb_private { + +//---------------------------------------------------------------------- +/// @class Breakpoint Breakpoint.h "lldb/Breakpoint/Breakpoint.h" +/// @brief Class that manages logical breakpoint setting. +//---------------------------------------------------------------------- + +//---------------------------------------------------------------------- +/// General Outline: +/// A breakpoint has four main parts, a filter, a resolver, the list of breakpoint +/// locations that have been determined for the filter/resolver pair, and finally +/// a set of options for the breakpoint. +/// +/// \b Filter: +/// This is an object derived from SearchFilter. It manages the search +/// for breakpoint location matches through the symbols in the module list of the target +/// that owns it. It also filters out locations based on whatever logic it wants. +/// +/// \b Resolver: +/// This is an object derived from BreakpointResolver. It provides a +/// callback to the filter that will find breakpoint locations. How it does this is +/// determined by what kind of resolver it is. +/// +/// The Breakpoint class also provides constructors for the common breakpoint cases +/// which make the appropriate filter and resolver for you. +/// +/// \b Location List: +/// This stores the breakpoint locations that have been determined +/// to date. For a given breakpoint, there will be only one location with a given +/// address. Adding a location at an already taken address will just return the location +/// already at that address. Locations can be looked up by ID, or by address. +/// +/// \b Options: +/// This includes: +/// \b Enabled/Disabled +/// \b Ignore Count +/// \b Callback +/// \b Condition +/// Note, these options can be set on the breakpoint, and they can also be set on the +/// individual locations. The options set on the breakpoint take precedence over the +/// options set on the individual location. +/// So for instance disabling the breakpoint will cause NONE of the locations to get hit. +/// But if the breakpoint is enabled, then the location's enabled state will be checked +/// to determine whether to insert that breakpoint location. +/// Similarly, if the breakpoint condition says "stop", we won't check the location's condition. +/// But if the breakpoint condition says "continue", then we will check the location for whether +/// to actually stop or not. +/// One subtle point worth observing here is that you don't actually stop at a Breakpoint, you +/// always stop at one of its locations. So the "should stop" tests are done by the location, +/// not by the breakpoint. +//---------------------------------------------------------------------- +class Breakpoint: + public std::enable_shared_from_this, + public Stoppoint +{ +public: + + static const ConstString & + GetEventIdentifier (); + + + //------------------------------------------------------------------ + /// An enum specifying the match style for breakpoint settings. At + /// present only used for function name style breakpoints. + //------------------------------------------------------------------ + typedef enum + { + Exact, + Regexp, + Glob + } MatchType; + + class BreakpointEventData : + public EventData + { + public: + + static const ConstString & + GetFlavorString (); + + virtual const ConstString & + GetFlavor () const; + + BreakpointEventData (lldb::BreakpointEventType sub_type, + const lldb::BreakpointSP &new_breakpoint_sp); + + virtual + ~BreakpointEventData(); + + lldb::BreakpointEventType + GetBreakpointEventType () const; + + lldb::BreakpointSP & + GetBreakpoint (); + + BreakpointLocationCollection & + GetBreakpointLocationCollection() + { + return m_locations; + } + + + virtual void + Dump (Stream *s) const; + + static lldb::BreakpointEventType + GetBreakpointEventTypeFromEvent (const lldb::EventSP &event_sp); + + static lldb::BreakpointSP + GetBreakpointFromEvent (const lldb::EventSP &event_sp); + + static lldb::BreakpointLocationSP + GetBreakpointLocationAtIndexFromEvent (const lldb::EventSP &event_sp, uint32_t loc_idx); + + static size_t + GetNumBreakpointLocationsFromEvent (const lldb::EventSP &event_sp); + + static const BreakpointEventData * + GetEventDataFromEvent (const Event *event_sp); + + private: + + lldb::BreakpointEventType m_breakpoint_event; + lldb::BreakpointSP m_new_breakpoint_sp; + BreakpointLocationCollection m_locations; + + DISALLOW_COPY_AND_ASSIGN (BreakpointEventData); + }; + + + //------------------------------------------------------------------ + /// Destructor. + /// + /// The destructor is not virtual since there should be no reason to subclass + /// breakpoints. The varieties of breakpoints are specified instead by + /// providing different resolvers & filters. + //------------------------------------------------------------------ + ~Breakpoint(); + + //------------------------------------------------------------------ + // Methods + //------------------------------------------------------------------ + + //------------------------------------------------------------------ + /// Tell whether this breakpoint is an "internal" breakpoint. + /// @return + /// Returns \b true if this is an internal breakpoint, \b false otherwise. + //------------------------------------------------------------------ + bool + IsInternal () const; + + //------------------------------------------------------------------ + /// Standard "Dump" method. At present it does nothing. + //------------------------------------------------------------------ + void + Dump (Stream *s); + + //------------------------------------------------------------------ + // The next set of methods provide ways to tell the breakpoint to update + // it's location list - usually done when modules appear or disappear. + //------------------------------------------------------------------ + + + //------------------------------------------------------------------ + /// Tell this breakpoint to clear all its breakpoint sites. Done + /// when the process holding the breakpoint sites is destroyed. + //------------------------------------------------------------------ + void + ClearAllBreakpointSites (); + + //------------------------------------------------------------------ + /// Tell this breakpoint to scan it's target's module list and resolve any + /// new locations that match the breakpoint's specifications. + //------------------------------------------------------------------ + void + ResolveBreakpoint (); + + //------------------------------------------------------------------ + /// Tell this breakpoint to scan a given module list and resolve any + /// new locations that match the breakpoint's specifications. + /// + /// @param[in] changed_modules + /// The list of modules to look in for new locations. + //------------------------------------------------------------------ + void + ResolveBreakpointInModules (ModuleList &changed_modules); + + + //------------------------------------------------------------------ + /// Like ResolveBreakpointInModules, but allows for "unload" events, in + /// which case we will remove any locations that are in modules that got + /// unloaded. + /// + /// @param[in] changedModules + /// The list of modules to look in for new locations. + /// @param[in] load_event + /// If \b true then the modules were loaded, if \b false, unloaded. + /// @param[in] delete_locations + /// If \b true then the modules were unloaded delete any locations in the changed modules. + //------------------------------------------------------------------ + void + ModulesChanged (ModuleList &changed_modules, + bool load_event, + bool delete_locations = false); + + + //------------------------------------------------------------------ + /// Tells the breakpoint the old module \a old_module_sp has been + /// replaced by new_module_sp (usually because the underlying file has been + /// rebuilt, and the old version is gone.) + /// + /// @param[in] old_module_sp + /// The old module that is going away. + /// @param[in] new_module_sp + /// The new module that is replacing it. + //------------------------------------------------------------------ + void + ModuleReplaced (lldb::ModuleSP old_module_sp, lldb::ModuleSP new_module_sp); + + //------------------------------------------------------------------ + // The next set of methods provide access to the breakpoint locations + // for this breakpoint. + //------------------------------------------------------------------ + + //------------------------------------------------------------------ + /// Add a location to the breakpoint's location list. This is only meant + /// to be called by the breakpoint's resolver. FIXME: how do I ensure that? + /// + /// @param[in] addr + /// The Address specifying the new location. + /// @param[out] new_location + /// Set to \b true if a new location was created, to \b false if there + /// already was a location at this Address. + /// @return + /// Returns a pointer to the new location. + //------------------------------------------------------------------ + lldb::BreakpointLocationSP + AddLocation (const Address &addr, + bool *new_location = NULL); + + //------------------------------------------------------------------ + /// Find a breakpoint location by Address. + /// + /// @param[in] addr + /// The Address specifying the location. + /// @return + /// Returns a shared pointer to the location at \a addr. The pointer + /// in the shared pointer will be NULL if there is no location at that address. + //------------------------------------------------------------------ + lldb::BreakpointLocationSP + FindLocationByAddress (const Address &addr); + + //------------------------------------------------------------------ + /// Find a breakpoint location ID by Address. + /// + /// @param[in] addr + /// The Address specifying the location. + /// @return + /// Returns the UID of the location at \a addr, or \b LLDB_INVALID_ID if + /// there is no breakpoint location at that address. + //------------------------------------------------------------------ + lldb::break_id_t + FindLocationIDByAddress (const Address &addr); + + //------------------------------------------------------------------ + /// Find a breakpoint location for a given breakpoint location ID. + /// + /// @param[in] bp_loc_id + /// The ID specifying the location. + /// @return + /// Returns a shared pointer to the location with ID \a bp_loc_id. The pointer + /// in the shared pointer will be NULL if there is no location with that ID. + //------------------------------------------------------------------ + lldb::BreakpointLocationSP + FindLocationByID (lldb::break_id_t bp_loc_id); + + //------------------------------------------------------------------ + /// Get breakpoint locations by index. + /// + /// @param[in] index + /// The location index. + /// + /// @return + /// Returns a shared pointer to the location with index \a + /// index. The shared pointer might contain NULL if \a index is + /// greater than then number of actual locations. + //------------------------------------------------------------------ + lldb::BreakpointLocationSP + GetLocationAtIndex (size_t index); + + //------------------------------------------------------------------ + // The next section deals with various breakpoint options. + //------------------------------------------------------------------ + + //------------------------------------------------------------------ + /// If \a enable is \b true, enable the breakpoint, if \b false disable it. + //------------------------------------------------------------------ + void + SetEnabled (bool enable); + + //------------------------------------------------------------------ + /// Check the Enable/Disable state. + /// @return + /// \b true if the breakpoint is enabled, \b false if disabled. + //------------------------------------------------------------------ + bool + IsEnabled (); + + //------------------------------------------------------------------ + /// Set the breakpoint to ignore the next \a count breakpoint hits. + /// @param[in] count + /// The number of breakpoint hits to ignore. + //------------------------------------------------------------------ + void + SetIgnoreCount (uint32_t count); + + //------------------------------------------------------------------ + /// Return the current ignore count/ + /// @return + /// The number of breakpoint hits to be ignored. + //------------------------------------------------------------------ + uint32_t + GetIgnoreCount () const; + + //------------------------------------------------------------------ + /// Return the current hit count for all locations. + /// @return + /// The current hit count for all locations. + //------------------------------------------------------------------ + uint32_t + GetHitCount () const; + + + //------------------------------------------------------------------ + /// If \a one_shot is \b true, breakpoint will be deleted on first hit. + //------------------------------------------------------------------ + void + SetOneShot (bool one_shot); + + //------------------------------------------------------------------ + /// Check the OneShot state. + /// @return + /// \b true if the breakpoint is one shot, \b false otherwise. + //------------------------------------------------------------------ + bool + IsOneShot () const; + + //------------------------------------------------------------------ + /// Set the valid thread to be checked when the breakpoint is hit. + /// @param[in] thread_id + /// If this thread hits the breakpoint, we stop, otherwise not. + //------------------------------------------------------------------ + void + SetThreadID (lldb::tid_t thread_id); + + //------------------------------------------------------------------ + /// Return the current stop thread value. + /// @return + /// The thread id for which the breakpoint hit will stop, LLDB_INVALID_THREAD_ID for all threads. + //------------------------------------------------------------------ + lldb::tid_t + GetThreadID () const; + + void + SetThreadIndex (uint32_t index); + + uint32_t + GetThreadIndex() const; + + void + SetThreadName (const char *thread_name); + + const char * + GetThreadName () const; + + void + SetQueueName (const char *queue_name); + + const char * + GetQueueName () const; + + //------------------------------------------------------------------ + /// Set the callback action invoked when the breakpoint is hit. + /// + /// @param[in] callback + /// The method that will get called when the breakpoint is hit. + /// @param[in] baton + /// A void * pointer that will get passed back to the callback function. + /// @param[in] is_synchronous + /// If \b true the callback will be run on the private event thread + /// before the stop event gets reported. If false, the callback will get + /// handled on the public event thead after the stop has been posted. + /// + /// @return + /// \b true if the process should stop when you hit the breakpoint. + /// \b false if it should continue. + //------------------------------------------------------------------ + void + SetCallback (BreakpointHitCallback callback, + void *baton, + bool is_synchronous = false); + + void + SetCallback (BreakpointHitCallback callback, + const lldb::BatonSP &callback_baton_sp, + bool is_synchronous = false); + + void + ClearCallback (); + + //------------------------------------------------------------------ + /// Set the breakpoint's condition. + /// + /// @param[in] condition + /// The condition expression to evaluate when the breakpoint is hit. + /// Pass in NULL to clear the condition. + //------------------------------------------------------------------ + void SetCondition (const char *condition); + + //------------------------------------------------------------------ + /// Return a pointer to the text of the condition expression. + /// + /// @return + /// A pointer to the condition expression text, or NULL if no + // condition has been set. + //------------------------------------------------------------------ + const char *GetConditionText () const; + + //------------------------------------------------------------------ + // The next section are various utility functions. + //------------------------------------------------------------------ + + //------------------------------------------------------------------ + /// Return the number of breakpoint locations that have resolved to + /// actual breakpoint sites. + /// + /// @return + /// The number locations resolved breakpoint sites. + //------------------------------------------------------------------ + size_t + GetNumResolvedLocations() const; + + //------------------------------------------------------------------ + /// Return the number of breakpoint locations. + /// + /// @return + /// The number breakpoint locations. + //------------------------------------------------------------------ + size_t + GetNumLocations() const; + + //------------------------------------------------------------------ + /// Put a description of this breakpoint into the stream \a s. + /// + /// @param[in] s + /// Stream into which to dump the description. + /// + /// @param[in] level + /// The description level that indicates the detail level to + /// provide. + /// + /// @see lldb::DescriptionLevel + //------------------------------------------------------------------ + void + GetDescription (Stream *s, lldb::DescriptionLevel level, bool show_locations = false); + + //------------------------------------------------------------------ + /// Set the "kind" description for a breakpoint. If the breakpoint is hit + /// the stop info will show this "kind" description instead of the breakpoint + /// number. Mostly useful for internal breakpoints, where the breakpoint number + /// doesn't have meaning to the user. + /// + /// @param[in] kind + /// New "kind" description. + //------------------------------------------------------------------ + void + SetBreakpointKind (const char *kind) + { + m_kind_description.assign (kind); + } + + //------------------------------------------------------------------ + /// Return the "kind" description for a breakpoint. + /// + /// @return + /// The breakpoint kind, or NULL if none is set. + //------------------------------------------------------------------ + const char *GetBreakpointKind () const + { + return m_kind_description.c_str(); + } + + //------------------------------------------------------------------ + /// Accessor for the breakpoint Target. + /// @return + /// This breakpoint's Target. + //------------------------------------------------------------------ + Target & + GetTarget (); + + const Target & + GetTarget () const; + + void + GetResolverDescription (Stream *s); + + //------------------------------------------------------------------ + /// Find breakpoint locations which match the (filename, line_number) description. + /// The breakpoint location collection is to be filled with the matching locations. + /// It should be initialized with 0 size by the API client. + /// + /// @return + /// True if there is a match + /// + /// The locations which match the filename and line_number in loc_coll. If its + /// size is 0 and true is returned, it means the breakpoint fully matches the + /// description. + //------------------------------------------------------------------ + bool GetMatchingFileLine(const ConstString &filename, uint32_t line_number, + BreakpointLocationCollection &loc_coll); + + void + GetFilterDescription (Stream *s); + + //------------------------------------------------------------------ + /// Returns the BreakpointOptions structure set at the breakpoint level. + /// + /// Meant to be used by the BreakpointLocation class. + /// + /// @return + /// A pointer to this breakpoint's BreakpointOptions. + //------------------------------------------------------------------ + BreakpointOptions * + GetOptions (); + + + //------------------------------------------------------------------ + /// Invoke the callback action when the breakpoint is hit. + /// + /// Meant to be used by the BreakpointLocation class. + /// + /// @param[in] context + /// Described the breakpoint event. + /// + /// @param[in] bp_loc_id + /// Which breakpoint location hit this breakpoint. + /// + /// @return + /// \b true if the target should stop at this breakpoint and \b false not. + //------------------------------------------------------------------ + bool + InvokeCallback (StoppointCallbackContext *context, + lldb::break_id_t bp_loc_id); + +protected: + friend class Target; + //------------------------------------------------------------------ + // Protected Methods + //------------------------------------------------------------------ + + + //------------------------------------------------------------------ + /// Constructors and Destructors + /// Only the Target can make a breakpoint, and it owns the breakpoint lifespans. + /// The constructor takes a filter and a resolver. Up in Target there are convenience + /// variants that make breakpoints for some common cases. + //------------------------------------------------------------------ + // This is the generic constructor + Breakpoint(Target &target, lldb::SearchFilterSP &filter_sp, lldb::BreakpointResolverSP &resolver_sp); + + friend class BreakpointLocation; // To call the following two when determining whether to stop. + + void + DecrementIgnoreCount(); + + // BreakpointLocation::IgnoreCountShouldStop & Breakpoint::IgnoreCountShouldStop can only be called once per stop, + // and BreakpointLocation::IgnoreCountShouldStop should be tested first, and if it returns false we should + // continue, otherwise we should test Breakpoint::IgnoreCountShouldStop. + + bool + IgnoreCountShouldStop (); + +private: + //------------------------------------------------------------------ + // For Breakpoint only + //------------------------------------------------------------------ + bool m_being_created; + Target &m_target; // The target that holds this breakpoint. + lldb::SearchFilterSP m_filter_sp; // The filter that constrains the breakpoint's domain. + lldb::BreakpointResolverSP m_resolver_sp; // The resolver that defines this breakpoint. + BreakpointOptions m_options; // Settable breakpoint options + BreakpointLocationList m_locations; // The list of locations currently found for this breakpoint. + std::string m_kind_description; + + void + SendBreakpointChangedEvent (lldb::BreakpointEventType eventKind); + + void + SendBreakpointChangedEvent (BreakpointEventData *data); + + DISALLOW_COPY_AND_ASSIGN(Breakpoint); +}; + +} // namespace lldb_private + +#endif // liblldb_Breakpoint_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Breakpoint/BreakpointID.h b/contrib/llvm/tools/lldb/include/lldb/Breakpoint/BreakpointID.h new file mode 100644 index 00000000000..9e352100b9e --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Breakpoint/BreakpointID.h @@ -0,0 +1,117 @@ +//===-- BreakpointID.h ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_BreakpointID_h_ +#define liblldb_BreakpointID_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes + +#include "lldb/lldb-private.h" + +namespace lldb_private { + +//---------------------------------------------------------------------- +// class BreakpointID +//---------------------------------------------------------------------- + +class BreakpointID +{ +public: + + BreakpointID (lldb::break_id_t bp_id = LLDB_INVALID_BREAK_ID, + lldb::break_id_t loc_id = LLDB_INVALID_BREAK_ID); + + virtual + ~BreakpointID (); + + lldb::break_id_t + GetBreakpointID () + { + return m_break_id; + } + + lldb::break_id_t + GetLocationID () + { + return m_location_id; + } + + void + SetID (lldb::break_id_t bp_id, lldb::break_id_t loc_id) + { + m_break_id = bp_id; + m_location_id = loc_id; + } + + void + SetBreakpointID (lldb::break_id_t bp_id) + { + m_break_id = bp_id; + } + + void + SetBreakpointLocationID (lldb::break_id_t loc_id) + { + m_location_id = loc_id; + } + + void + GetDescription (Stream *s, lldb::DescriptionLevel level); + + static bool + IsRangeIdentifier (const char *str); + + static bool + IsValidIDExpression (const char *str); + + static const char *g_range_specifiers[]; + + //------------------------------------------------------------------ + /// Takes an input string containing the description of a breakpoint or breakpoint and location + /// and returns the breakpoint ID and the breakpoint location id. + /// + /// @param[in] input + /// A string containing JUST the breakpoint description. + /// @param[out] break_id + /// This is the break id. + /// @param[out] break_loc_id + /// This is breakpoint location id, or LLDB_INVALID_BREAK_ID is no location was specified. + /// @return + /// \b true if the call was able to extract a breakpoint location from the string. \b false otherwise. + //------------------------------------------------------------------ + static bool + ParseCanonicalReference (const char *input, lldb::break_id_t *break_id, lldb::break_id_t *break_loc_id); + + + //------------------------------------------------------------------ + /// Takes a breakpoint ID and the breakpoint location id and returns + /// a string containing the canonical description for the breakpoint + /// or breakpoint location. + /// + /// @param[out] break_id + /// This is the break id. + /// + /// @param[out] break_loc_id + /// This is breakpoint location id, or LLDB_INVALID_BREAK_ID is no + /// location is to be specified. + //------------------------------------------------------------------ + static void + GetCanonicalReference (Stream *s, lldb::break_id_t break_id, lldb::break_id_t break_loc_id); + +protected: + lldb::break_id_t m_break_id; + lldb::break_id_t m_location_id; +}; + +} // namespace lldb_private + +#endif // liblldb_BreakpointID_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Breakpoint/BreakpointIDList.h b/contrib/llvm/tools/lldb/include/lldb/Breakpoint/BreakpointIDList.h new file mode 100644 index 00000000000..c9fcef0a783 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Breakpoint/BreakpointIDList.h @@ -0,0 +1,82 @@ +//===-- BreakpointIDList.h --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_BreakpointIDList_h_ +#define liblldb_BreakpointIDList_h_ + +// C Includes +// C++ Includes +#include + +// Other libraries and framework includes +// Project includes + +#include "lldb/lldb-private.h" +#include "lldb/Breakpoint/BreakpointID.h" + +namespace lldb_private { + +//---------------------------------------------------------------------- +// class BreakpointIDList +//---------------------------------------------------------------------- + + +class BreakpointIDList +{ +public: + typedef std::vector BreakpointIDArray; + + BreakpointIDList (); + + virtual + ~BreakpointIDList (); + + size_t + GetSize(); + + BreakpointID & + GetBreakpointIDAtIndex (size_t index); + + bool + RemoveBreakpointIDAtIndex (size_t index); + + void + Clear(); + + bool + AddBreakpointID (BreakpointID bp_id); + + bool + AddBreakpointID (const char *bp_id); + + bool + FindBreakpointID (BreakpointID &bp_id, size_t *position); + + bool + FindBreakpointID (const char *bp_id, size_t *position); + + void + InsertStringArray (const char **string_array, size_t array_size, CommandReturnObject &result); + + static bool + StringContainsIDRangeExpression (const char *in_string, size_t *range_start_len, size_t *range_end_pos); + + static void + FindAndReplaceIDRanges (Args &old_args, Target *target, CommandReturnObject &result, Args &new_args); + +private: + BreakpointIDArray m_breakpoint_ids; + BreakpointID m_invalid_id; + + DISALLOW_COPY_AND_ASSIGN(BreakpointIDList); +}; + +} // namespace lldb_private + +#endif // liblldb_BreakpointIDList_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Breakpoint/BreakpointList.h b/contrib/llvm/tools/lldb/include/lldb/Breakpoint/BreakpointList.h new file mode 100644 index 00000000000..97eb2b46bc0 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Breakpoint/BreakpointList.h @@ -0,0 +1,193 @@ +//===-- BreakpointList.h ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_BreakpointList_h_ +#define liblldb_BreakpointList_h_ + +// C Includes +// C++ Includes +#include +// Other libraries and framework includes +// Project includes +#include "lldb/Breakpoint/Breakpoint.h" +#include "lldb/Host/Mutex.h" + +namespace lldb_private { + +//---------------------------------------------------------------------- +/// @class BreakpointList BreakpointList.h "lldb/Breakpoint/BreakpointList.h" +/// @brief This class manages a list of breakpoints. +//---------------------------------------------------------------------- + +//---------------------------------------------------------------------- +/// General Outline: +/// Allows adding and removing breakpoints and find by ID and index. +//---------------------------------------------------------------------- + +class BreakpointList +{ +public: + BreakpointList (bool is_internal); + + ~BreakpointList(); + + //------------------------------------------------------------------ + /// Add the breakpoint \a bp_sp to the list. + /// + /// @param[in] bp_sp + /// Shared pointer to the breakpoint that will get added to the list. + /// + /// @result + /// Returns breakpoint id. + //------------------------------------------------------------------ + lldb::break_id_t + Add (lldb::BreakpointSP& bp_sp, bool notify); + + //------------------------------------------------------------------ + /// Standard "Dump" method. At present it does nothing. + //------------------------------------------------------------------ + void + Dump (Stream *s) const; + + //------------------------------------------------------------------ + /// Returns a shared pointer to the breakpoint with id \a breakID. + /// + /// @param[in] breakID + /// The breakpoint ID to seek for. + /// + /// @result + /// A shared pointer to the breakpoint. May contain a NULL pointer if the + /// breakpoint doesn't exist. + //------------------------------------------------------------------ + lldb::BreakpointSP + FindBreakpointByID (lldb::break_id_t breakID); + + //------------------------------------------------------------------ + /// Returns a shared pointer to the breakpoint with id \a breakID. Const version. + /// + /// @param[in] breakID + /// The breakpoint ID to seek for. + /// + /// @result + /// A shared pointer to the breakpoint. May contain a NULL pointer if the + /// breakpoint doesn't exist. + //------------------------------------------------------------------ + const lldb::BreakpointSP + FindBreakpointByID (lldb::break_id_t breakID) const; + + //------------------------------------------------------------------ + /// Returns a shared pointer to the breakpoint with index \a i. + /// + /// @param[in] i + /// The breakpoint index to seek for. + /// + /// @result + /// A shared pointer to the breakpoint. May contain a NULL pointer if the + /// breakpoint doesn't exist. + //------------------------------------------------------------------ + lldb::BreakpointSP + GetBreakpointAtIndex (size_t i); + + //------------------------------------------------------------------ + /// Returns a shared pointer to the breakpoint with index \a i, const version + /// + /// @param[in] i + /// The breakpoint index to seek for. + /// + /// @result + /// A shared pointer to the breakpoint. May contain a NULL pointer if the + /// breakpoint doesn't exist. + //------------------------------------------------------------------ + const lldb::BreakpointSP + GetBreakpointAtIndex (size_t i) const; + + //------------------------------------------------------------------ + /// Returns the number of elements in this breakpoint list. + /// + /// @result + /// The number of elements. + //------------------------------------------------------------------ + size_t + GetSize() const + { + Mutex::Locker locker(m_mutex); + return m_breakpoints.size(); + } + + //------------------------------------------------------------------ + /// Removes the breakpoint given by \b breakID from this list. + /// + /// @param[in] breakID + /// The breakpoint index to remove. + /// + /// @result + /// \b true if the breakpoint \a breakID was in the list. + //------------------------------------------------------------------ + bool + Remove (lldb::break_id_t breakID, bool notify); + + void + SetEnabledAll (bool enabled); + + //------------------------------------------------------------------ + /// Removes all the breakpoints from this list. + //------------------------------------------------------------------ + void + RemoveAll (bool notify); + + //------------------------------------------------------------------ + /// Tell all the breakpoints to update themselves due to a change in the + /// modules in \a module_list. \a added says whether the module was loaded + /// or unloaded. + /// + /// @param[in] module_list + /// The module list that has changed. + /// + /// @param[in] added + /// \b true if the modules are loaded, \b false if unloaded. + //------------------------------------------------------------------ + void + UpdateBreakpoints (ModuleList &module_list, bool added); + + void + UpdateBreakpointsWhenModuleIsReplaced (lldb::ModuleSP old_module_sp, lldb::ModuleSP new_module_sp); + + void + ClearAllBreakpointSites (); + + //------------------------------------------------------------------ + /// Sets the passed in Locker to hold the Breakpoint List mutex. + /// + /// @param[in] locker + /// The locker object that is set. + //------------------------------------------------------------------ + void + GetListMutex (lldb_private::Mutex::Locker &locker); + +protected: + typedef std::list bp_collection; + + bp_collection::iterator + GetBreakpointIDIterator(lldb::break_id_t breakID); + + bp_collection::const_iterator + GetBreakpointIDConstIterator(lldb::break_id_t breakID) const; + + mutable Mutex m_mutex; + bp_collection m_breakpoints; // The breakpoint list, currently a list. + lldb::break_id_t m_next_break_id; + bool m_is_internal; + +private: + DISALLOW_COPY_AND_ASSIGN (BreakpointList); +}; + +} // namespace lldb_private + +#endif // liblldb_BreakpointList_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Breakpoint/BreakpointLocation.h b/contrib/llvm/tools/lldb/include/lldb/Breakpoint/BreakpointLocation.h new file mode 100644 index 00000000000..9ab0a79c684 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Breakpoint/BreakpointLocation.h @@ -0,0 +1,401 @@ +//===-- BreakpointLocation.h ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_BreakpointLocation_h_ +#define liblldb_BreakpointLocation_h_ + +// C Includes + +// C++ Includes +#include + +// Other libraries and framework includes + +// Project includes +#include "lldb/lldb-private.h" +#include "lldb/Breakpoint/StoppointLocation.h" +#include "lldb/Core/Address.h" +#include "lldb/Core/StringList.h" +#include "lldb/Core/UserID.h" +#include "lldb/Host/Mutex.h" +#include "lldb/Target/Process.h" +#include "lldb/Expression/ClangUserExpression.h" + +namespace lldb_private { + +//---------------------------------------------------------------------- +/// @class BreakpointLocation BreakpointLocation.h "lldb/Breakpoint/BreakpointLocation.h" +/// @brief Class that manages one unique (by address) instance of a logical breakpoint. +//---------------------------------------------------------------------- + +//---------------------------------------------------------------------- +/// General Outline: +/// A breakpoint location is defined by the breakpoint that produces it, +/// and the address that resulted in this particular instantiation. +/// Each breakpoint location also may have a breakpoint site if its +/// address has been loaded into the program. +/// Finally it has a settable options object. +/// +/// FIXME: Should we also store some fingerprint for the location, so +/// we can map one location to the "equivalent location" on rerun? This +/// would be useful if you've set options on the locations. +//---------------------------------------------------------------------- + +class BreakpointLocation : + public std::enable_shared_from_this, + public StoppointLocation +{ +public: + + ~BreakpointLocation (); + + //------------------------------------------------------------------ + /// Gets the load address for this breakpoint location + /// @return + /// Returns breakpoint location load address, \b + /// LLDB_INVALID_ADDRESS if not yet set. + //------------------------------------------------------------------ + lldb::addr_t + GetLoadAddress () const; + + //------------------------------------------------------------------ + /// Gets the Address for this breakpoint location + /// @return + /// Returns breakpoint location Address. + //------------------------------------------------------------------ + Address & + GetAddress (); + //------------------------------------------------------------------ + /// Gets the Breakpoint that created this breakpoint location + /// @return + /// Returns the owning breakpoint. + //------------------------------------------------------------------ + Breakpoint & + GetBreakpoint (); + + //------------------------------------------------------------------ + /// Determines whether we should stop due to a hit at this + /// breakpoint location. + /// + /// Side Effects: This may evaluate the breakpoint condition, and + /// run the callback. So this command may do a considerable amount + /// of work. + /// + /// @return + /// \b true if this breakpoint location thinks we should stop, + /// \b false otherwise. + //------------------------------------------------------------------ + bool + ShouldStop (StoppointCallbackContext *context); + + //------------------------------------------------------------------ + // The next section deals with various breakpoint options. + //------------------------------------------------------------------ + + //------------------------------------------------------------------ + /// If \a enable is \b true, enable the breakpoint, if \b false + /// disable it. + //------------------------------------------------------------------ + void + SetEnabled(bool enabled); + + //------------------------------------------------------------------ + /// Check the Enable/Disable state. + /// + /// @return + /// \b true if the breakpoint is enabled, \b false if disabled. + //------------------------------------------------------------------ + bool + IsEnabled () const; + + //------------------------------------------------------------------ + /// Return the current Ignore Count. + /// + /// @return + /// The number of breakpoint hits to be ignored. + //------------------------------------------------------------------ + uint32_t + GetIgnoreCount (); + + //------------------------------------------------------------------ + /// Set the breakpoint to ignore the next \a count breakpoint hits. + /// + /// @param[in] count + /// The number of breakpoint hits to ignore. + //------------------------------------------------------------------ + void + SetIgnoreCount (uint32_t n); + + //------------------------------------------------------------------ + /// Set the callback action invoked when the breakpoint is hit. + /// + /// The callback will return a bool indicating whether the target + /// should stop at this breakpoint or not. + /// + /// @param[in] callback + /// The method that will get called when the breakpoint is hit. + /// + /// @param[in] callback_baton_sp + /// A shared pointer to a Baton that provides the void * needed + /// for the callback. + /// + /// @see lldb_private::Baton + //------------------------------------------------------------------ + void + SetCallback (BreakpointHitCallback callback, + const lldb::BatonSP &callback_baton_sp, + bool is_synchronous); + + void + SetCallback (BreakpointHitCallback callback, + void *baton, + bool is_synchronous); + + void + ClearCallback (); + + //------------------------------------------------------------------ + /// Set the breakpoint location's condition. + /// + /// @param[in] condition + /// The condition expression to evaluate when the breakpoint is hit. + //------------------------------------------------------------------ + void + SetCondition (const char *condition); + + //------------------------------------------------------------------ + /// Return a pointer to the text of the condition expression. + /// + /// @return + /// A pointer to the condition expression text, or NULL if no + // condition has been set. + //------------------------------------------------------------------ + const char * + GetConditionText (size_t *hash = NULL) const; + + bool + ConditionSaysStop (ExecutionContext &exe_ctx, Error &error); + + + //------------------------------------------------------------------ + /// Set the valid thread to be checked when the breakpoint is hit. + /// + /// @param[in] thread_id + /// If this thread hits the breakpoint, we stop, otherwise not. + //------------------------------------------------------------------ + void + SetThreadID (lldb::tid_t thread_id); + + lldb::tid_t + GetThreadID (); + + void + SetThreadIndex (uint32_t index); + + uint32_t + GetThreadIndex() const; + + void + SetThreadName (const char *thread_name); + + const char * + GetThreadName () const; + + void + SetQueueName (const char *queue_name); + + const char * + GetQueueName () const; + + //------------------------------------------------------------------ + // The next section deals with this location's breakpoint sites. + //------------------------------------------------------------------ + + //------------------------------------------------------------------ + /// Try to resolve the breakpoint site for this location. + /// + /// @return + /// \b true if we were successful at setting a breakpoint site, + /// \b false otherwise. + //------------------------------------------------------------------ + bool + ResolveBreakpointSite (); + + //------------------------------------------------------------------ + /// Clear this breakpoint location's breakpoint site - for instance + /// when disabling the breakpoint. + /// + /// @return + /// \b true if there was a breakpoint site to be cleared, \b false + /// otherwise. + //------------------------------------------------------------------ + bool + ClearBreakpointSite (); + + //------------------------------------------------------------------ + /// Return whether this breakpoint location has a breakpoint site. + /// @return + /// \b true if there was a breakpoint site for this breakpoint + /// location, \b false otherwise. + //------------------------------------------------------------------ + bool + IsResolved () const; + + lldb::BreakpointSiteSP + GetBreakpointSite() const; + + //------------------------------------------------------------------ + // The next section are generic report functions. + //------------------------------------------------------------------ + + //------------------------------------------------------------------ + /// Print a description of this breakpoint location to the stream + /// \a s. + /// + /// @param[in] s + /// The stream to which to print the description. + /// + /// @param[in] level + /// The description level that indicates the detail level to + /// provide. + /// + /// @see lldb::DescriptionLevel + //------------------------------------------------------------------ + void + GetDescription (Stream *s, lldb::DescriptionLevel level); + + //------------------------------------------------------------------ + /// Standard "Dump" method. At present it does nothing. + //------------------------------------------------------------------ + void + Dump (Stream *s) const; + + //------------------------------------------------------------------ + /// Use this to set location specific breakpoint options. + /// + /// It will create a copy of the containing breakpoint's options if + /// that hasn't been done already + /// + /// @return + /// A pointer to the breakpoint options. + //------------------------------------------------------------------ + BreakpointOptions * + GetLocationOptions (); + + //------------------------------------------------------------------ + /// Use this to access breakpoint options from this breakpoint location. + /// This will point to the owning breakpoint's options unless options have + /// been set specifically on this location. + /// + /// @return + /// A pointer to the containing breakpoint's options if this + /// location doesn't have its own copy. + //------------------------------------------------------------------ + const BreakpointOptions * + GetOptionsNoCreate () const; + + bool + ValidForThisThread (Thread *thread); + + + //------------------------------------------------------------------ + /// Invoke the callback action when the breakpoint is hit. + /// + /// Meant to be used by the BreakpointLocation class. + /// + /// @param[in] context + /// Described the breakpoint event. + /// + /// @param[in] bp_loc_id + /// Which breakpoint location hit this breakpoint. + /// + /// @return + /// \b true if the target should stop at this breakpoint and \b + /// false not. + //------------------------------------------------------------------ + bool + InvokeCallback (StoppointCallbackContext *context); + +protected: + friend class BreakpointLocationList; + friend class CommandObjectBreakpointCommandAdd; + friend class Process; + + //------------------------------------------------------------------ + /// Set the breakpoint site for this location to \a bp_site_sp. + /// + /// @param[in] bp_site_sp + /// The breakpoint site we are setting for this location. + /// + /// @return + /// \b true if we were successful at setting the breakpoint site, + /// \b false otherwise. + //------------------------------------------------------------------ + bool + SetBreakpointSite (lldb::BreakpointSiteSP& bp_site_sp); + + void + DecrementIgnoreCount(); + + bool + IgnoreCountShouldStop(); + +private: + + //------------------------------------------------------------------ + // Constructors and Destructors + // + // Only the Breakpoint can make breakpoint locations, and it owns + // them. + //------------------------------------------------------------------ + + //------------------------------------------------------------------ + /// Constructor. + /// + /// @param[in] owner + /// A back pointer to the breakpoint that owns this location. + /// + /// @param[in] addr + /// The Address defining this location. + /// + /// @param[in] tid + /// The thread for which this breakpoint location is valid, or + /// LLDB_INVALID_THREAD_ID if it is valid for all threads. + /// + /// @param[in] hardware + /// \b true if a hardware breakpoint is requested. + //------------------------------------------------------------------ + + BreakpointLocation (lldb::break_id_t bid, + Breakpoint &owner, + const Address &addr, + lldb::tid_t tid = LLDB_INVALID_THREAD_ID, + bool hardware = false); + + //------------------------------------------------------------------ + // Data members: + //------------------------------------------------------------------ + bool m_being_created; + Address m_address; ///< The address defining this location. + Breakpoint &m_owner; ///< The breakpoint that produced this object. + std::unique_ptr m_options_ap; ///< Breakpoint options pointer, NULL if we're using our breakpoint's options. + lldb::BreakpointSiteSP m_bp_site_sp; ///< Our breakpoint site (it may be shared by more than one location.) + ClangUserExpression::ClangUserExpressionSP m_user_expression_sp; ///< The compiled expression to use in testing our condition. + Mutex m_condition_mutex; ///< Guards parsing and evaluation of the condition, which could be evaluated by multiple processes. + size_t m_condition_hash; ///< For testing whether the condition source code changed. + + void + SendBreakpointLocationChangedEvent (lldb::BreakpointEventType eventKind); + + DISALLOW_COPY_AND_ASSIGN (BreakpointLocation); +}; + +} // namespace lldb_private + +#endif // liblldb_BreakpointLocation_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Breakpoint/BreakpointLocationCollection.h b/contrib/llvm/tools/lldb/include/lldb/Breakpoint/BreakpointLocationCollection.h new file mode 100644 index 00000000000..7f6a659323b --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Breakpoint/BreakpointLocationCollection.h @@ -0,0 +1,209 @@ +//===-- BreakpointLocationCollection.h --------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_BreakpointLocationCollection_h_ +#define liblldb_BreakpointLocationCollection_h_ + +// C Includes +// C++ Includes +#include +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private.h" + +namespace lldb_private { + +class BreakpointLocationCollection +{ +public: + BreakpointLocationCollection(); + + ~BreakpointLocationCollection(); + + //------------------------------------------------------------------ + /// Add the breakpoint \a bp_loc_sp to the list. + /// + /// @param[in] bp_sp + /// Shared pointer to the breakpoint location that will get added + /// to the list. + /// + /// @result + /// Returns breakpoint location id. + //------------------------------------------------------------------ + void + Add (const lldb::BreakpointLocationSP& bp_loc_sp); + + //------------------------------------------------------------------ + /// Removes the breakpoint location given by \b breakID from this + /// list. + /// + /// @param[in] break_id + /// The breakpoint index to remove. + /// + /// @param[in] break_loc_id + /// The breakpoint location index in break_id to remove. + /// + /// @result + /// \b true if the breakpoint was in the list. + //------------------------------------------------------------------ + bool + Remove (lldb::break_id_t break_id, lldb::break_id_t break_loc_id); + + //------------------------------------------------------------------ + /// Returns a shared pointer to the breakpoint location with id \a + /// breakID. + /// + /// @param[in] break_id + /// The breakpoint ID to seek for. + /// + /// @param[in] break_loc_id + /// The breakpoint location ID in \a break_id to seek for. + /// + /// @result + /// A shared pointer to the breakpoint. May contain a NULL + /// pointer if the breakpoint doesn't exist. + //------------------------------------------------------------------ + lldb::BreakpointLocationSP + FindByIDPair (lldb::break_id_t break_id, lldb::break_id_t break_loc_id); + + //------------------------------------------------------------------ + /// Returns a shared pointer to the breakpoint location with id \a + /// breakID, const version. + /// + /// @param[in] breakID + /// The breakpoint location ID to seek for. + /// + /// @param[in] break_loc_id + /// The breakpoint location ID in \a break_id to seek for. + /// + /// @result + /// A shared pointer to the breakpoint. May contain a NULL + /// pointer if the breakpoint doesn't exist. + //------------------------------------------------------------------ + const lldb::BreakpointLocationSP + FindByIDPair (lldb::break_id_t break_id, lldb::break_id_t break_loc_id) const; + + //------------------------------------------------------------------ + /// Returns a shared pointer to the breakpoint location with index + /// \a i. + /// + /// @param[in] i + /// The breakpoint location index to seek for. + /// + /// @result + /// A shared pointer to the breakpoint. May contain a NULL + /// pointer if the breakpoint doesn't exist. + //------------------------------------------------------------------ + lldb::BreakpointLocationSP + GetByIndex (size_t i); + + //------------------------------------------------------------------ + /// Returns a shared pointer to the breakpoint location with index + /// \a i, const version. + /// + /// @param[in] i + /// The breakpoint location index to seek for. + /// + /// @result + /// A shared pointer to the breakpoint. May contain a NULL + /// pointer if the breakpoint doesn't exist. + //------------------------------------------------------------------ + const lldb::BreakpointLocationSP + GetByIndex (size_t i) const; + + //------------------------------------------------------------------ + /// Returns the number of elements in this breakpoint location list. + /// + /// @result + /// The number of elements. + //------------------------------------------------------------------ + size_t + GetSize() const { return m_break_loc_collection.size(); } + + //------------------------------------------------------------------ + /// Enquires of all the breakpoint locations in this list whether + /// we should stop at a hit at \a breakID. + /// + /// @param[in] context + /// This contains the information about this stop. + /// + /// @param[in] breakID + /// This break ID that we hit. + /// + /// @return + /// \b true if we should stop, \b false otherwise. + //------------------------------------------------------------------ + bool + ShouldStop (StoppointCallbackContext *context); + + //------------------------------------------------------------------ + /// Print a description of the breakpoint locations in this list + /// to the stream \a s. + /// + /// @param[in] s + /// The stream to which to print the description. + /// + /// @param[in] level + /// The description level that indicates the detail level to + /// provide. + /// + /// @see lldb::DescriptionLevel + //------------------------------------------------------------------ + void GetDescription (Stream *s, lldb::DescriptionLevel level); + + //------------------------------------------------------------------ + /// Check whether this collection of breakpoint locations have any + /// thread specifiers, and if yes, is \a thread_id contained in any + /// of these specifiers. + /// + /// @param[in] thread + /// The thread against which to test. + /// + /// return + /// \b true if the collection contains at least one location that + /// would be valid for this thread, false otherwise. + //------------------------------------------------------------------ + bool ValidForThisThread (Thread *thread); + + //------------------------------------------------------------------ + /// Tell whether ALL the breakpoints in the location collection are internal. + /// + /// @result + /// \b true if all breakpoint locations are owned by internal breakpoints, + /// \b false otherwise. + //------------------------------------------------------------------ + bool IsInternal() const; + + +protected: + //------------------------------------------------------------------ + // Classes that inherit from BreakpointLocationCollection can see + // and modify these + //------------------------------------------------------------------ + +private: + //------------------------------------------------------------------ + // For BreakpointLocationCollection only + //------------------------------------------------------------------ + + typedef std::vector collection; + + collection::iterator + GetIDPairIterator(lldb::break_id_t break_id, lldb::break_id_t break_loc_id); + + collection::const_iterator + GetIDPairConstIterator(lldb::break_id_t break_id, lldb::break_id_t break_loc_id) const; + + collection m_break_loc_collection; + +}; + +} // namespace lldb_private + +#endif // liblldb_BreakpointLocationCollection_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Breakpoint/BreakpointLocationList.h b/contrib/llvm/tools/lldb/include/lldb/Breakpoint/BreakpointLocationList.h new file mode 100644 index 00000000000..1cba23d9118 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Breakpoint/BreakpointLocationList.h @@ -0,0 +1,269 @@ +//===-- BreakpointLocationList.h --------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_BreakpointLocationList_h_ +#define liblldb_BreakpointLocationList_h_ + +// C Includes +// C++ Includes +#include +#include +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private.h" +#include "lldb/Core/Address.h" +#include "lldb/Host/Mutex.h" + +namespace lldb_private { + +//---------------------------------------------------------------------- +/// @class BreakpointLocationList BreakpointLocationList.h "lldb/Breakpoint/BreakpointLocationList.h" +/// @brief This class is used by Breakpoint to manage a list of breakpoint locations, +// each breakpoint location in the list +/// has a unique ID, and is unique by Address as well. +//---------------------------------------------------------------------- + +class BreakpointLocationList +{ +// Only Breakpoints can make the location list, or add elements to it. +// This is not just some random collection of locations. Rather, the act of adding the location +// to this list sets its ID, and implicitly all the locations have the same breakpoint ID as +// well. If you need a generic container for breakpoint locations, use BreakpointLocationCollection. +friend class Breakpoint; + +public: + virtual + ~BreakpointLocationList(); + + //------------------------------------------------------------------ + /// Standard "Dump" method. At present it does nothing. + //------------------------------------------------------------------ + void + Dump (Stream *s) const; + + //------------------------------------------------------------------ + /// Returns a shared pointer to the breakpoint location at address + /// \a addr - const version. + /// + /// @param[in] addr + /// The address to look for. + /// + /// @result + /// A shared pointer to the breakpoint. May contain a NULL + /// pointer if the breakpoint doesn't exist. + //------------------------------------------------------------------ + const lldb::BreakpointLocationSP + FindByAddress (const Address &addr) const; + + //------------------------------------------------------------------ + /// Returns a shared pointer to the breakpoint location with id + /// \a breakID, const version. + /// + /// @param[in] breakID + /// The breakpoint location ID to seek for. + /// + /// @result + /// A shared pointer to the breakpoint. May contain a NULL + /// pointer if the breakpoint doesn't exist. + //------------------------------------------------------------------ + lldb::BreakpointLocationSP + FindByID (lldb::break_id_t breakID) const; + + //------------------------------------------------------------------ + /// Returns the breakpoint location id to the breakpoint location + /// at address \a addr. + /// + /// @param[in] addr + /// The address to match. + /// + /// @result + /// The ID of the breakpoint location, or LLDB_INVALID_BREAK_ID. + //------------------------------------------------------------------ + lldb::break_id_t + FindIDByAddress (const Address &addr); + + //------------------------------------------------------------------ + /// Returns a breakpoint location list of the breakpoint locations + /// in the module \a module. This list is allocated, and owned by + /// the caller. + /// + /// @param[in] module + /// The module to seek in. + /// + /// @param[in] + /// A breakpoint collection that gets any breakpoint locations + /// that match \a module appended to. + /// + /// @result + /// The number of matches + //------------------------------------------------------------------ + size_t + FindInModule (Module *module, + BreakpointLocationCollection& bp_loc_list); + + //------------------------------------------------------------------ + /// Returns a shared pointer to the breakpoint location with + /// index \a i. + /// + /// @param[in] i + /// The breakpoint location index to seek for. + /// + /// @result + /// A shared pointer to the breakpoint. May contain a NULL + /// pointer if the breakpoint doesn't exist. + //------------------------------------------------------------------ + lldb::BreakpointLocationSP + GetByIndex (size_t i); + + //------------------------------------------------------------------ + /// Returns a shared pointer to the breakpoint location with index + /// \a i, const version. + /// + /// @param[in] i + /// The breakpoint location index to seek for. + /// + /// @result + /// A shared pointer to the breakpoint. May contain a NULL + /// pointer if the breakpoint doesn't exist. + //------------------------------------------------------------------ + const lldb::BreakpointLocationSP + GetByIndex (size_t i) const; + + //------------------------------------------------------------------ + /// Removes all the locations in this list from their breakpoint site + /// owners list. + //------------------------------------------------------------------ + void + ClearAllBreakpointSites (); + + //------------------------------------------------------------------ + /// Tells all the breakopint locations in this list to attempt to + /// resolve any possible breakpoint sites. + //------------------------------------------------------------------ + void + ResolveAllBreakpointSites (); + + //------------------------------------------------------------------ + /// Returns the number of breakpoint locations in this list with + /// resolved breakpoints. + /// + /// @result + /// Number of qualifying breakpoint locations. + //------------------------------------------------------------------ + size_t + GetNumResolvedLocations() const; + + //------------------------------------------------------------------ + /// Returns the number hit count of all locations in this list. + /// + /// @result + /// Hit count of all locations in this list. + //------------------------------------------------------------------ + uint32_t + GetHitCount () const; + + //------------------------------------------------------------------ + /// Enquires of the breakpoint location in this list with ID \a + /// breakID whether we should stop. + /// + /// @param[in] context + /// This contains the information about this stop. + /// + /// @param[in] breakID + /// This break ID that we hit. + /// + /// @return + /// \b true if we should stop, \b false otherwise. + //------------------------------------------------------------------ + bool + ShouldStop (StoppointCallbackContext *context, + lldb::break_id_t breakID); + + //------------------------------------------------------------------ + /// Returns the number of elements in this breakpoint location list. + /// + /// @result + /// The number of elements. + //------------------------------------------------------------------ + size_t + GetSize() const + { + return m_locations.size(); + } + + //------------------------------------------------------------------ + /// Print a description of the breakpoint locations in this list to + /// the stream \a s. + /// + /// @param[in] s + /// The stream to which to print the description. + /// + /// @param[in] level + /// The description level that indicates the detail level to + /// provide. + /// + /// @see lldb::DescriptionLevel + //------------------------------------------------------------------ + void + GetDescription (Stream *s, + lldb::DescriptionLevel level); + +protected: + + //------------------------------------------------------------------ + /// This is the standard constructor. + /// + /// It creates an empty breakpoint location list. It is protected + /// here because only Breakpoints are allowed to create the + /// breakpoint location list. + //------------------------------------------------------------------ + BreakpointLocationList(Breakpoint &owner); + + //------------------------------------------------------------------ + /// Add the breakpoint \a bp_loc_sp to the list. + /// + /// @param[in] bp_sp + /// Shared pointer to the breakpoint location that will get + /// added to the list. + /// + /// @result + /// Returns breakpoint location id. + //------------------------------------------------------------------ + lldb::BreakpointLocationSP + Create (const Address &addr); + + void + StartRecordingNewLocations(BreakpointLocationCollection &new_locations); + + void + StopRecordingNewLocations(); + + lldb::BreakpointLocationSP + AddLocation (const Address &addr, + bool *new_location = NULL); + + bool + RemoveLocation (const lldb::BreakpointLocationSP &bp_loc_sp); + + typedef std::vector collection; + typedef std::map addr_map; + + Breakpoint &m_owner; + collection m_locations; + addr_map m_address_to_location; + mutable Mutex m_mutex; + lldb::break_id_t m_next_id; + BreakpointLocationCollection *m_new_location_recorder; +}; + +} // namespace lldb_private + +#endif // liblldb_BreakpointLocationList_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Breakpoint/BreakpointOptions.h b/contrib/llvm/tools/lldb/include/lldb/Breakpoint/BreakpointOptions.h new file mode 100644 index 00000000000..728f5932fa0 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Breakpoint/BreakpointOptions.h @@ -0,0 +1,358 @@ +//===-- BreakpointOptions.h -------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_BreakpointOptions_h_ +#define liblldb_BreakpointOptions_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private.h" +#include "lldb/Core/Baton.h" +#include "lldb/Core/StringList.h" + +namespace lldb_private { + +//---------------------------------------------------------------------- +/// @class BreakpointOptions BreakpointOptions.h "lldb/Breakpoint/BreakpointOptions.h" +/// @brief Class that manages the options on a breakpoint or breakpoint location. +//---------------------------------------------------------------------- + +class BreakpointOptions +{ +public: + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + //------------------------------------------------------------------ + /// Default constructor. The breakpoint is enabled, and has no condition, + /// callback, ignore count, etc... + //------------------------------------------------------------------ + BreakpointOptions(); + BreakpointOptions(const BreakpointOptions& rhs); + + static BreakpointOptions * + CopyOptionsNoCallback (BreakpointOptions &rhs); + //------------------------------------------------------------------ + /// This constructor allows you to specify all the breakpoint options. + /// + /// @param[in] condition + /// The expression which if it evaluates to \b true if we are to stop + /// + /// @param[in] callback + /// This is the plugin for some code that gets run, returns \b true if we are to stop. + /// + /// @param[in] baton + /// Client data that will get passed to the callback. + /// + /// @param[in] enabled + /// Is this breakpoint enabled. + /// + /// @param[in] ignore + /// How many breakpoint hits we should ignore before stopping. + /// + /// @param[in] thread_id + /// Only stop if \a thread_id hits the breakpoint. + //------------------------------------------------------------------ + BreakpointOptions(void *condition, + BreakpointHitCallback callback, + void *baton, + bool enabled = true, + int32_t ignore = 0, + lldb::tid_t thread_id = LLDB_INVALID_THREAD_ID, + bool one_shot = false); + + virtual ~BreakpointOptions(); + + //------------------------------------------------------------------ + // Operators + //------------------------------------------------------------------ + const BreakpointOptions& + operator=(const BreakpointOptions& rhs); + + //------------------------------------------------------------------ + // Callbacks + // + // Breakpoint callbacks come in two forms, synchronous and asynchronous. Synchronous callbacks will get + // run before any of the thread plans are consulted, and if they return false the target will continue + // "under the radar" of the thread plans. There are a couple of restrictions to synchronous callbacks: + // 1) They should NOT resume the target themselves. Just return false if you want the target to restart. + // 2) Breakpoints with synchronous callbacks can't have conditions (or rather, they can have them, but they + // won't do anything. Ditto with ignore counts, etc... You are supposed to control that all through the + // callback. + // Asynchronous callbacks get run as part of the "ShouldStop" logic in the thread plan. The logic there is: + // a) If the breakpoint is thread specific and not for this thread, continue w/o running the callback. + // b) If the ignore count says we shouldn't stop, then ditto. + // c) If the condition says we shouldn't stop, then ditto. + // d) Otherwise, the callback will get run, and if it returns true we will stop, and if false we won't. + // The asynchronous callback can run the target itself, but at present that should be the last action the + // callback does. We will relax this condition at some point, but it will take a bit of plumbing to get + // that to work. + // + //------------------------------------------------------------------ + + //------------------------------------------------------------------ + /// Adds a callback to the breakpoint option set. + /// + /// @param[in] callback + /// The function to be called when the breakpoint gets hit. + /// + /// @param[in] baton_sp + /// A baton which will get passed back to the callback when it is invoked. + /// + /// @param[in] synchronous + /// Whether this is a synchronous or asynchronous callback. See discussion above. + //------------------------------------------------------------------ + void SetCallback (BreakpointHitCallback callback, const lldb::BatonSP &baton_sp, bool synchronous = false); + + + //------------------------------------------------------------------ + /// Remove the callback from this option set. + //------------------------------------------------------------------ + void ClearCallback (); + + // The rest of these functions are meant to be used only within the breakpoint handling mechanism. + + //------------------------------------------------------------------ + /// Use this function to invoke the callback for a specific stop. + /// + /// @param[in] context + /// The context in which the callback is to be invoked. This includes the stop event, the + /// execution context of the stop (since you might hit the same breakpoint on multiple threads) and + /// whether we are currently executing synchronous or asynchronous callbacks. + /// + /// @param[in] break_id + /// The breakpoint ID that owns this option set. + /// + /// @param[in] break_loc_id + /// The breakpoint location ID that owns this option set. + /// + /// @return + /// The callback return value. + //------------------------------------------------------------------ + bool InvokeCallback (StoppointCallbackContext *context, lldb::user_id_t break_id, lldb::user_id_t break_loc_id); + + //------------------------------------------------------------------ + /// Used in InvokeCallback to tell whether it is the right time to run this kind of callback. + /// + /// @return + /// The synchronicity of our callback. + //------------------------------------------------------------------ + bool IsCallbackSynchronous () { + return m_callback_is_synchronous; + } + + //------------------------------------------------------------------ + /// Fetch the baton from the callback. + /// + /// @return + /// The baton. + //------------------------------------------------------------------ + Baton *GetBaton (); + + //------------------------------------------------------------------ + /// Fetch a const version of the baton from the callback. + /// + /// @return + /// The baton. + //------------------------------------------------------------------ + const Baton *GetBaton () const; + + //------------------------------------------------------------------ + // Condition + //------------------------------------------------------------------ + //------------------------------------------------------------------ + /// Set the breakpoint option's condition. + /// + /// @param[in] condition + /// The condition expression to evaluate when the breakpoint is hit. + //------------------------------------------------------------------ + void SetCondition (const char *condition); + + //------------------------------------------------------------------ + /// Return a pointer to the text of the condition expression. + /// + /// @return + /// A pointer to the condition expression text, or NULL if no + // condition has been set. + //------------------------------------------------------------------ + const char *GetConditionText (size_t *hash = NULL) const; + + //------------------------------------------------------------------ + // Enabled/Ignore Count + //------------------------------------------------------------------ + + //------------------------------------------------------------------ + /// Check the Enable/Disable state. + /// @return + /// \b true if the breakpoint is enabled, \b false if disabled. + //------------------------------------------------------------------ + bool + IsEnabled () const + { + return m_enabled; + } + + //------------------------------------------------------------------ + /// If \a enable is \b true, enable the breakpoint, if \b false disable it. + //------------------------------------------------------------------ + void + SetEnabled (bool enabled) + { + m_enabled = enabled; + } + + //------------------------------------------------------------------ + /// Check the One-shot state. + /// @return + /// \b true if the breakpoint is one-shot, \b false otherwise. + //------------------------------------------------------------------ + bool + IsOneShot () const + { + return m_one_shot; + } + + //------------------------------------------------------------------ + /// If \a enable is \b true, enable the breakpoint, if \b false disable it. + //------------------------------------------------------------------ + void + SetOneShot (bool one_shot) + { + m_one_shot = one_shot; + } + + //------------------------------------------------------------------ + /// Set the breakpoint to ignore the next \a count breakpoint hits. + /// @param[in] count + /// The number of breakpoint hits to ignore. + //------------------------------------------------------------------ + + void + SetIgnoreCount (uint32_t n) + { + m_ignore_count = n; + } + + //------------------------------------------------------------------ + /// Return the current Ignore Count. + /// @return + /// The number of breakpoint hits to be ignored. + //------------------------------------------------------------------ + uint32_t + GetIgnoreCount () const + { + return m_ignore_count; + } + + //------------------------------------------------------------------ + /// Return the current thread spec for this option. This will return NULL if the no thread + /// specifications have been set for this Option yet. + /// @return + /// The thread specification pointer for this option, or NULL if none has + /// been set yet. + //------------------------------------------------------------------ + const ThreadSpec * + GetThreadSpecNoCreate () const; + + //------------------------------------------------------------------ + /// Returns a pointer to the ThreadSpec for this option, creating it. + /// if it hasn't been created already. This API is used for setting the + /// ThreadSpec items for this option. + //------------------------------------------------------------------ + ThreadSpec * + GetThreadSpec (); + + void + SetThreadID(lldb::tid_t thread_id); + + void + GetDescription (Stream *s, lldb::DescriptionLevel level) const; + + //------------------------------------------------------------------ + /// Returns true if the breakpoint option has a callback set. + //------------------------------------------------------------------ + bool + HasCallback(); + + //------------------------------------------------------------------ + /// This is the default empty callback. + /// @return + /// The thread id for which the breakpoint hit will stop, + /// LLDB_INVALID_THREAD_ID for all threads. + //------------------------------------------------------------------ + static bool + NullCallback (void *baton, + StoppointCallbackContext *context, + lldb::user_id_t break_id, + lldb::user_id_t break_loc_id); + + + struct CommandData + { + CommandData () : + user_source(), + script_source(), + stop_on_error(true) + { + } + + ~CommandData () + { + } + + StringList user_source; + std::string script_source; + bool stop_on_error; + }; + + class CommandBaton : public Baton + { + public: + CommandBaton (CommandData *data) : + Baton (data) + { + } + + virtual + ~CommandBaton () + { + delete ((CommandData *)m_data); + m_data = NULL; + } + + virtual void + GetDescription (Stream *s, lldb::DescriptionLevel level) const; + + }; + +protected: + //------------------------------------------------------------------ + // Classes that inherit from BreakpointOptions can see and modify these + //------------------------------------------------------------------ + +private: + //------------------------------------------------------------------ + // For BreakpointOptions only + //------------------------------------------------------------------ + BreakpointHitCallback m_callback; // This is the callback function pointer + lldb::BatonSP m_callback_baton_sp; // This is the client data for the callback + bool m_callback_is_synchronous; + bool m_enabled; + bool m_one_shot; + uint32_t m_ignore_count; // Number of times to ignore this breakpoint + std::unique_ptr m_thread_spec_ap; // Thread for which this breakpoint will take + std::string m_condition_text; // The condition to test. + size_t m_condition_text_hash; // Its hash, so that locations know when the condition is updated. +}; + +} // namespace lldb_private + +#endif // liblldb_BreakpointOptions_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Breakpoint/BreakpointResolver.h b/contrib/llvm/tools/lldb/include/lldb/Breakpoint/BreakpointResolver.h new file mode 100644 index 00000000000..3db3795453e --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Breakpoint/BreakpointResolver.h @@ -0,0 +1,147 @@ +//===-- BreakpointResolver.h ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_BreakpointResolver_h_ +#define liblldb_BreakpointResolver_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private.h" +#include "lldb/Core/Address.h" +#include "lldb/Breakpoint/Breakpoint.h" +#include "lldb/Breakpoint/BreakpointResolver.h" +#include "lldb/Host/FileSpec.h" +#include "lldb/Core/RegularExpression.h" +#include "lldb/Core/SearchFilter.h" +#include "lldb/Core/ConstString.h" + +namespace lldb_private { + +//---------------------------------------------------------------------- +/// @class BreakpointResolver BreakpointResolver.h "lldb/Breakpoint/BreakpointResolver.h" +/// @brief This class works with SearchFilter to resolve logical breakpoints to their +/// of concrete breakpoint locations. +//---------------------------------------------------------------------- + +//---------------------------------------------------------------------- +/// General Outline: +/// The BreakpointResolver is a Searcher. In that protocol, +/// the SearchFilter asks the question "At what depth of the symbol context +/// descent do you want your callback to get called?" of the filter. The resolver +/// answers this question (in the GetDepth method) and provides the resolution callback. +/// Each Breakpoint has a BreakpointResolver, and it calls either ResolveBreakpoint +/// or ResolveBreakpointInModules to tell it to look for new breakpoint locations. +//---------------------------------------------------------------------- + +class BreakpointResolver : + public Searcher +{ +public: + //------------------------------------------------------------------ + /// The breakpoint resolver need to have a breakpoint for "ResolveBreakpoint + /// to make sense. It can be constructed without a breakpoint, but you have to + /// call SetBreakpoint before ResolveBreakpoint. + /// + /// @param[in] bkpt + /// The breakpoint that owns this resolver. + /// @param[in] resolverType + /// The concrete breakpoint resolver type for this breakpoint. + /// + /// @result + /// Returns breakpoint location id. + //------------------------------------------------------------------ + BreakpointResolver (Breakpoint *bkpt, unsigned char resolverType); + + //------------------------------------------------------------------ + /// The Destructor is virtual, all significant breakpoint resolvers derive + /// from this class. + //------------------------------------------------------------------ + virtual + ~BreakpointResolver (); + + //------------------------------------------------------------------ + /// This sets the breakpoint for this resolver. + /// + /// @param[in] bkpt + /// The breakpoint that owns this resolver. + //------------------------------------------------------------------ + void + SetBreakpoint (Breakpoint *bkpt); + + //------------------------------------------------------------------ + /// In response to this method the resolver scans all the modules in the breakpoint's + /// target, and adds any new locations it finds. + /// + /// @param[in] filter + /// The filter that will manage the search for this resolver. + //------------------------------------------------------------------ + virtual void + ResolveBreakpoint (SearchFilter &filter); + + //------------------------------------------------------------------ + /// In response to this method the resolver scans the modules in the module list + /// \a modules, and adds any new locations it finds. + /// + /// @param[in] filter + /// The filter that will manage the search for this resolver. + //------------------------------------------------------------------ + virtual void + ResolveBreakpointInModules (SearchFilter &filter, + ModuleList &modules); + + //------------------------------------------------------------------ + /// Prints a canonical description for the breakpoint to the stream \a s. + /// + /// @param[in] s + /// Stream to which the output is copied. + //------------------------------------------------------------------ + virtual void + GetDescription (Stream *s) = 0; + + //------------------------------------------------------------------ + /// Standard "Dump" method. At present it does nothing. + //------------------------------------------------------------------ + virtual void + Dump (Stream *s) const = 0; + + //------------------------------------------------------------------ + /// An enumeration for keeping track of the concrete subclass that + /// is actually instantiated. Values of this enumeration are kept in the + /// BreakpointResolver's SubclassID field. They are used for concrete type + /// identification. + enum ResolverTy { + FileLineResolver, // This is an instance of BreakpointResolverFileLine + AddressResolver, // This is an instance of BreakpointResolverAddress + NameResolver, // This is an instance of BreakpointResolverName + FileRegexResolver, + ExceptionResolver + }; + + //------------------------------------------------------------------ + /// getResolverID - Return an ID for the concrete type of this object. This + /// is used to implement the LLVM classof checks. This should not be used + /// for any other purpose, as the values may change as LLDB evolves. + unsigned getResolverID() const { + return SubclassID; + } + +protected: + Breakpoint *m_breakpoint; // This is the breakpoint we add locations to. + +private: + // Subclass identifier (for llvm isa/dyn_cast) + const unsigned char SubclassID; + DISALLOW_COPY_AND_ASSIGN(BreakpointResolver); +}; + +} // namespace lldb_private + +#endif // liblldb_BreakpointResolver_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Breakpoint/BreakpointResolverAddress.h b/contrib/llvm/tools/lldb/include/lldb/Breakpoint/BreakpointResolverAddress.h new file mode 100644 index 00000000000..4ca4a405957 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Breakpoint/BreakpointResolverAddress.h @@ -0,0 +1,74 @@ +//===-- BreakpointResolverAddress.h -----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_BreakpointResolverAddress_h_ +#define liblldb_BreakpointResolverAddress_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Breakpoint/BreakpointResolver.h" + +namespace lldb_private { + +//---------------------------------------------------------------------- +/// @class BreakpointResolverAddress BreakpointResolverAddress.h "lldb/Breakpoint/BreakpointResolverAddress.h" +/// @brief This class sets breakpoints on a given Address. This breakpoint only takes +/// once, and then it won't attempt to reset itself. +//---------------------------------------------------------------------- + +class BreakpointResolverAddress: + public BreakpointResolver +{ +public: + BreakpointResolverAddress (Breakpoint *bkpt, + const Address &addr); + + virtual + ~BreakpointResolverAddress (); + + virtual void + ResolveBreakpoint (SearchFilter &filter); + + virtual void + ResolveBreakpointInModules (SearchFilter &filter, + ModuleList &modules); + + virtual Searcher::CallbackReturn + SearchCallback (SearchFilter &filter, + SymbolContext &context, + Address *addr, + bool containing); + + virtual Searcher::Depth + GetDepth (); + + virtual void + GetDescription (Stream *s); + + virtual void + Dump (Stream *s) const; + + /// Methods for support type inquiry through isa, cast, and dyn_cast: + static inline bool classof(const BreakpointResolverAddress *) { return true; } + static inline bool classof(const BreakpointResolver *V) { + return V->getResolverID() == BreakpointResolver::AddressResolver; + } + +protected: + Address m_addr; + +private: + DISALLOW_COPY_AND_ASSIGN(BreakpointResolverAddress); +}; + +} // namespace lldb_private + +#endif // liblldb_BreakpointResolverAddress_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Breakpoint/BreakpointResolverFileLine.h b/contrib/llvm/tools/lldb/include/lldb/Breakpoint/BreakpointResolverFileLine.h new file mode 100644 index 00000000000..cc1633ce170 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Breakpoint/BreakpointResolverFileLine.h @@ -0,0 +1,74 @@ +//===-- BreakpointResolverFileLine.h ----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_BreakpointResolverFileLine_h_ +#define liblldb_BreakpointResolverFileLine_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Breakpoint/BreakpointResolver.h" + +namespace lldb_private { + +//---------------------------------------------------------------------- +/// @class BreakpointResolverFileLine BreakpointResolverFileLine.h "lldb/Breakpoint/BreakpointResolverFileLine.h" +/// @brief This class sets breakpoints by file and line. Optionally, it will look for inlined +/// instances of the file and line specification. +//---------------------------------------------------------------------- + +class BreakpointResolverFileLine : + public BreakpointResolver +{ +public: + BreakpointResolverFileLine (Breakpoint *bkpt, + const FileSpec &resolver, + uint32_t line_no, + bool check_inlines, + bool skip_prologue); + + virtual + ~BreakpointResolverFileLine (); + + virtual Searcher::CallbackReturn + SearchCallback (SearchFilter &filter, + SymbolContext &context, + Address *addr, + bool containing); + + virtual Searcher::Depth + GetDepth (); + + virtual void + GetDescription (Stream *s); + + virtual void + Dump (Stream *s) const; + + /// Methods for support type inquiry through isa, cast, and dyn_cast: + static inline bool classof(const BreakpointResolverFileLine *) { return true; } + static inline bool classof(const BreakpointResolver *V) { + return V->getResolverID() == BreakpointResolver::FileLineResolver; + } + +protected: + friend class Breakpoint; + FileSpec m_file_spec; // This is the file spec we are looking for. + uint32_t m_line_number; // This is the line number that we are looking for. + bool m_inlines; // This determines whether the resolver looks for inlined functions or not. + bool m_skip_prologue; + +private: + DISALLOW_COPY_AND_ASSIGN(BreakpointResolverFileLine); +}; + +} // namespace lldb_private + +#endif // liblldb_BreakpointResolverFileLine_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Breakpoint/BreakpointResolverFileRegex.h b/contrib/llvm/tools/lldb/include/lldb/Breakpoint/BreakpointResolverFileRegex.h new file mode 100644 index 00000000000..f1c2b1409e9 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Breakpoint/BreakpointResolverFileRegex.h @@ -0,0 +1,68 @@ +//===-- BreakpointResolverFileRegex.h ----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_BreakpointResolverFileRegex_h_ +#define liblldb_BreakpointResolverFileRegex_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Breakpoint/BreakpointResolver.h" + +namespace lldb_private { + +//---------------------------------------------------------------------- +/// @class BreakpointResolverFileRegex BreakpointResolverFileRegex.h "lldb/Breakpoint/BreakpointResolverFileRegex.h" +/// @brief This class sets breakpoints by file and line. Optionally, it will look for inlined +/// instances of the file and line specification. +//---------------------------------------------------------------------- + +class BreakpointResolverFileRegex : + public BreakpointResolver +{ +public: + BreakpointResolverFileRegex (Breakpoint *bkpt, + RegularExpression ®ex); + + virtual + ~BreakpointResolverFileRegex (); + + virtual Searcher::CallbackReturn + SearchCallback (SearchFilter &filter, + SymbolContext &context, + Address *addr, + bool containing); + + virtual Searcher::Depth + GetDepth (); + + virtual void + GetDescription (Stream *s); + + virtual void + Dump (Stream *s) const; + + /// Methods for support type inquiry through isa, cast, and dyn_cast: + static inline bool classof(const BreakpointResolverFileRegex *) { return true; } + static inline bool classof(const BreakpointResolver *V) { + return V->getResolverID() == BreakpointResolver::FileRegexResolver; + } + +protected: + friend class Breakpoint; + RegularExpression m_regex; // This is the line expression that we are looking for. + +private: + DISALLOW_COPY_AND_ASSIGN(BreakpointResolverFileRegex); +}; + +} // namespace lldb_private + +#endif // liblldb_BreakpointResolverFileRegex_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Breakpoint/BreakpointResolverName.h b/contrib/llvm/tools/lldb/include/lldb/Breakpoint/BreakpointResolverName.h new file mode 100644 index 00000000000..f481aa9c533 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Breakpoint/BreakpointResolverName.h @@ -0,0 +1,122 @@ +//===-- BreakpointResolverName.h --------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_BreakpointResolverName_h_ +#define liblldb_BreakpointResolverName_h_ + +// C Includes +// C++ Includes +#include +#include +// Other libraries and framework includes +// Project includes +#include "lldb/Breakpoint/BreakpointResolver.h" + +namespace lldb_private { + +//---------------------------------------------------------------------- +/// @class BreakpointResolverName BreakpointResolverName.h "lldb/Breakpoint/BreakpointResolverName.h" +/// @brief This class sets breakpoints on a given function name, either by exact match +/// or by regular expression. +//---------------------------------------------------------------------- + +class BreakpointResolverName: + public BreakpointResolver +{ +public: + + BreakpointResolverName (Breakpoint *bkpt, + const char *name, + uint32_t name_type_mask, + Breakpoint::MatchType type, + bool skip_prologue); + + // This one takes an array of names. It is always MatchType = Exact. + BreakpointResolverName (Breakpoint *bkpt, + const char *names[], + size_t num_names, + uint32_t name_type_mask, + bool skip_prologue); + + // This one takes a C++ array of names. It is always MatchType = Exact. + BreakpointResolverName (Breakpoint *bkpt, + std::vector names, + uint32_t name_type_mask, + bool skip_prologue); + + // Creates a function breakpoint by regular expression. Takes over control of the lifespan of func_regex. + BreakpointResolverName (Breakpoint *bkpt, + RegularExpression &func_regex, + bool skip_prologue); + + BreakpointResolverName (Breakpoint *bkpt, + const char *class_name, + const char *method, + Breakpoint::MatchType type, + bool skip_prologue); + + virtual + ~BreakpointResolverName (); + + virtual Searcher::CallbackReturn + SearchCallback (SearchFilter &filter, + SymbolContext &context, + Address *addr, + bool containing); + + virtual Searcher::Depth + GetDepth (); + + virtual void + GetDescription (Stream *s); + + virtual void + Dump (Stream *s) const; + + /// Methods for support type inquiry through isa, cast, and dyn_cast: + static inline bool classof(const BreakpointResolverName *) { return true; } + static inline bool classof(const BreakpointResolver *V) { + return V->getResolverID() == BreakpointResolver::NameResolver; + } + +protected: + struct LookupInfo + { + ConstString name; + ConstString lookup_name; + uint32_t name_type_mask; // See FunctionNameType + bool match_name_after_lookup; + + LookupInfo () : + name(), + lookup_name(), + name_type_mask (0), + match_name_after_lookup (false) + { + } + + void + Prune (SymbolContextList &sc_list, + size_t start_idx) const; + }; + std::vector m_lookups; + ConstString m_class_name; + RegularExpression m_regex; + Breakpoint::MatchType m_match_type; + bool m_skip_prologue; + + void + AddNameLookup (const ConstString &name, uint32_t name_type_mask); +private: + DISALLOW_COPY_AND_ASSIGN(BreakpointResolverName); +}; + +} // namespace lldb_private + +#endif // liblldb_BreakpointResolverName_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Breakpoint/BreakpointSite.h b/contrib/llvm/tools/lldb/include/lldb/Breakpoint/BreakpointSite.h new file mode 100644 index 00000000000..271a23c2e45 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Breakpoint/BreakpointSite.h @@ -0,0 +1,295 @@ +//===-- BreakpointSite.h ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_BreakpointSite_h_ +#define liblldb_BreakpointSite_h_ + +// C Includes + +// C++ Includes +#include + +// Other libraries and framework includes + +// Project includes +#include "lldb/lldb-private.h" +#include "lldb/Core/UserID.h" +#include "lldb/Breakpoint/StoppointLocation.h" +#include "lldb/Breakpoint/BreakpointLocationCollection.h" + +namespace lldb_private { + +//---------------------------------------------------------------------- +/// @class BreakpointSite BreakpointSite.h "lldb/Breakpoint/BreakpointSite.h" +/// @brief Class that manages the actual breakpoint that will be inserted +/// into the running program. +/// +/// The BreakpointSite class handles the physical breakpoint that is +/// actually inserted in the target program. As such, it is also the +/// one that gets hit, when the program stops. It keeps a list of all +/// BreakpointLocations that share this phsyical site. When the +/// breakpoint is hit, all the locations are informed by the breakpoint +/// site. Breakpoint sites are owned by the process. +//---------------------------------------------------------------------- + +class BreakpointSite : + public std::enable_shared_from_this, + public StoppointLocation +{ +public: + + enum Type + { + eSoftware, // Breakpoint opcode has been written to memory and m_saved_opcode + // and m_trap_opcode contain the saved and written opcode. + eHardware, // Breakpoint site is set as a hardware breakpoint + eExternal // Breakpoint site is managed by an external debug nub or + // debug interface where memory reads trasparently will not + // display any breakpoint opcodes. + }; + + virtual ~BreakpointSite (); + + //---------------------------------------------------------------------- + // This section manages the breakpoint traps + //---------------------------------------------------------------------- + + //------------------------------------------------------------------ + /// Returns the Opcode Bytes for this breakpoint + //------------------------------------------------------------------ + uint8_t * + GetTrapOpcodeBytes (); + + //------------------------------------------------------------------ + /// Returns the Opcode Bytes for this breakpoint - const version + //------------------------------------------------------------------ + const uint8_t * + GetTrapOpcodeBytes () const; + + //------------------------------------------------------------------ + /// Get the size of the trap opcode for this address + //------------------------------------------------------------------ + size_t + GetTrapOpcodeMaxByteSize () const; + + //------------------------------------------------------------------ + /// Sets the trap opcode + //------------------------------------------------------------------ + bool + SetTrapOpcode (const uint8_t *trap_opcode, + uint32_t trap_opcode_size); + + //------------------------------------------------------------------ + /// Gets the original instruction bytes that were overwritten by the trap + //------------------------------------------------------------------ + uint8_t * + GetSavedOpcodeBytes (); + + //------------------------------------------------------------------ + /// Gets the original instruction bytes that were overwritten by the trap const version + //------------------------------------------------------------------ + const uint8_t * + GetSavedOpcodeBytes () const; + + //------------------------------------------------------------------ + /// Says whether \a addr and size \a size intersects with the address \a intersect_addr + //------------------------------------------------------------------ + bool + IntersectsRange (lldb::addr_t addr, + size_t size, + lldb::addr_t *intersect_addr, + size_t *intersect_size, + size_t *opcode_offset) const; + + //------------------------------------------------------------------ + /// Tells whether the current breakpoint site is enabled or not + /// + /// This is a low-level enable bit for the breakpoint sites. If a + /// breakpoint site has no enabled owners, it should just get + /// removed. This enable/disable is for the low-level target code + /// to enable and disable breakpoint sites when single stepping, + /// etc. + //------------------------------------------------------------------ + bool + IsEnabled () const; + + //------------------------------------------------------------------ + /// Sets whether the current breakpoint site is enabled or not + /// + /// @param[in] enabled + /// \b true if the breakoint is enabled, \b false otherwise. + //------------------------------------------------------------------ + void + SetEnabled (bool enabled); + + //------------------------------------------------------------------ + /// Enquires of the breakpoint locations that produced this breakpoint site whether + /// we should stop at this location. + /// + /// @param[in] context + /// This contains the information about this stop. + /// + /// @return + /// \b true if we should stop, \b false otherwise. + //------------------------------------------------------------------ + virtual bool + ShouldStop (StoppointCallbackContext *context); + + //------------------------------------------------------------------ + /// Standard Dump method + /// + /// @param[in] context + /// The stream to dump this output. + //------------------------------------------------------------------ + void + Dump (Stream *s) const; + + //------------------------------------------------------------------ + /// The "Owners" are the breakpoint locations that share this + /// breakpoint site. The method adds the \a owner to this breakpoint + /// site's owner list. + /// + /// @param[in] context + /// \a owner is the Breakpoint Location to add. + //------------------------------------------------------------------ + void + AddOwner (const lldb::BreakpointLocationSP &owner); + + //------------------------------------------------------------------ + /// This method returns the number of breakpoint locations currently + /// located at this breakpoint site. + /// + /// @return + /// The number of owners. + //------------------------------------------------------------------ + size_t + GetNumberOfOwners (); + + //------------------------------------------------------------------ + /// This method returns the the breakpoint location at index \a index + /// located at this breakpoint site. The owners are listed ordinally + /// from 0 to GetNumberOfOwners() - 1 so you can use this method to iterate + /// over the owners + /// + /// @param[in] index + /// The index in the list of owners for which you wish the owner location. + /// @return + /// A shared pointer to the breakpoint location at that index. + //------------------------------------------------------------------ + lldb::BreakpointLocationSP + GetOwnerAtIndex (size_t idx); + + //------------------------------------------------------------------ + /// Check whether the owners of this breakpoint site have any + /// thread specifiers, and if yes, is \a thread contained in any + /// of these specifiers. + /// + /// @param[in] thread + /// The thread against which to test. + /// + /// return + /// \b true if the collection contains at least one location that + /// would be valid for this thread, false otherwise. + //------------------------------------------------------------------ + bool + ValidForThisThread (Thread *thread); + + + //------------------------------------------------------------------ + /// Print a description of this breakpoint site to the stream \a s. + /// GetDescription tells you about the breakpoint site's owners. + /// Use BreakpointSite::Dump(Stream *) to get information about the + /// breakpoint site itself. + /// + /// @param[in] s + /// The stream to which to print the description. + /// + /// @param[in] level + /// The description level that indicates the detail level to + /// provide. + /// + /// @see lldb::DescriptionLevel + //------------------------------------------------------------------ + void + GetDescription (Stream *s, + lldb::DescriptionLevel level); + + //------------------------------------------------------------------ + /// Tell whether a breakpoint has a location at this site. + /// + /// @param[in] bp_id + /// The breakpoint id to query. + /// + /// @result + /// \b true if bp_id has a location that is at this site, + /// \b false otherwise. + //------------------------------------------------------------------ + bool + IsBreakpointAtThisSite (lldb::break_id_t bp_id); + + //------------------------------------------------------------------ + /// Tell whether ALL the breakpoints in the location collection are internal. + /// + /// @result + /// \b true if all breakpoint locations are owned by internal breakpoints, + /// \b false otherwise. + //------------------------------------------------------------------ + bool + IsInternal () const; + + BreakpointSite::Type + GetType () const + { + return m_type; + } + + void + SetType (BreakpointSite::Type type) + { + m_type = type; + } + +private: + friend class Process; + + //------------------------------------------------------------------ + /// The method removes the owner at \a break_loc_id from this breakpoint list. + /// + /// @param[in] context + /// \a break_loc_id is the Breakpoint Location to remove. + //------------------------------------------------------------------ + size_t + RemoveOwner (lldb::break_id_t break_id, + lldb::break_id_t break_loc_id); + + BreakpointSite::Type m_type;///< The type of this breakpoint site. + uint8_t m_saved_opcode[8]; ///< The saved opcode bytes if this breakpoint site uses trap opcodes. + uint8_t m_trap_opcode[8]; ///< The opcode that was used to create the breakpoint if it is a software breakpoint site. + bool m_enabled; ///< Boolean indicating if this breakpoint site enabled or not. + + // Consider adding an optimization where if there is only one + // owner, we don't store a list. The usual case will be only one owner... + BreakpointLocationCollection m_owners; ///< This has the BreakpointLocations that share this breakpoint site. + + static lldb::break_id_t + GetNextID(); + + // Only the Process can create breakpoint sites in + // Process::CreateBreakpointSite (lldb::BreakpointLocationSP &, bool). + BreakpointSite (BreakpointSiteList *list, + const lldb::BreakpointLocationSP& owner, + lldb::addr_t m_addr, + bool use_hardware); + + DISALLOW_COPY_AND_ASSIGN(BreakpointSite); +}; + +} // namespace lldb_private + +#endif // liblldb_BreakpointSite_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Breakpoint/BreakpointSiteList.h b/contrib/llvm/tools/lldb/include/lldb/Breakpoint/BreakpointSiteList.h new file mode 100644 index 00000000000..0d4dafc4baa --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Breakpoint/BreakpointSiteList.h @@ -0,0 +1,216 @@ +//===-- BreakpointSiteList.h ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_BreakpointSiteList_h_ +#define liblldb_BreakpointSiteList_h_ + +// C Includes +// C++ Includes +#include +// Other libraries and framework includes +// Project includes +#include "lldb/Breakpoint/BreakpointSite.h" +#include "lldb/Host/Mutex.h" + +namespace lldb_private { + +//---------------------------------------------------------------------- +/// @class BreakpointSiteList BreakpointSiteList.h "lldb/Breakpoint/BreakpointSiteList.h" +/// @brief Class that manages lists of BreakpointSite shared pointers. +//---------------------------------------------------------------------- +class BreakpointSiteList +{ +// At present Process directly accesses the map of BreakpointSites so it can +// do quick lookups into the map (using GetMap). +// FIXME: Find a better interface for this. +friend class Process; + +public: + //------------------------------------------------------------------ + /// Default constructor makes an empty list. + //------------------------------------------------------------------ + BreakpointSiteList(); + + //------------------------------------------------------------------ + /// Destructor, currently does nothing. + //------------------------------------------------------------------ + ~BreakpointSiteList(); + + //------------------------------------------------------------------ + /// Add a BreakpointSite to the list. + /// + /// @param[in] bp_site_sp + /// A shared pointer to a breakpoint site being added to the list. + /// + /// @return + /// The ID of the BreakpointSite in the list. + //------------------------------------------------------------------ + lldb::break_id_t + Add (const lldb::BreakpointSiteSP& bp_site_sp); + + //------------------------------------------------------------------ + /// Standard Dump routine, doesn't do anything at present. + /// @param[in] s + /// Stream into which to dump the description. + //------------------------------------------------------------------ + void + Dump (Stream *s) const; + + //------------------------------------------------------------------ + /// Returns a shared pointer to the breakpoint site at address + /// \a addr. + /// + /// @param[in] addr + /// The address to look for. + /// + /// @result + /// A shared pointer to the breakpoint site. May contain a NULL + /// pointer if no breakpoint site exists with a matching address. + //------------------------------------------------------------------ + lldb::BreakpointSiteSP + FindByAddress (lldb::addr_t addr); + + //------------------------------------------------------------------ + /// Returns a shared pointer to the breakpoint site with id \a breakID. + /// + /// @param[in] breakID + /// The breakpoint site ID to seek for. + /// + /// @result + /// A shared pointer to the breakpoint site. May contain a NULL pointer if the + /// breakpoint doesn't exist. + //------------------------------------------------------------------ + lldb::BreakpointSiteSP + FindByID (lldb::break_id_t breakID); + + //------------------------------------------------------------------ + /// Returns a shared pointer to the breakpoint site with id \a breakID - const version. + /// + /// @param[in] breakID + /// The breakpoint site ID to seek for. + /// + /// @result + /// A shared pointer to the breakpoint site. May contain a NULL pointer if the + /// breakpoint doesn't exist. + //------------------------------------------------------------------ + const lldb::BreakpointSiteSP + FindByID (lldb::break_id_t breakID) const; + + //------------------------------------------------------------------ + /// Returns the breakpoint site id to the breakpoint site at address \a addr. + /// + /// @param[in] addr + /// The address to match. + /// + /// @result + /// The ID of the breakpoint site, or LLDB_INVALID_BREAK_ID. + //------------------------------------------------------------------ + lldb::break_id_t + FindIDByAddress (lldb::addr_t addr); + + //------------------------------------------------------------------ + /// Returns whether the breakpoint site \a bp_site_id has \a bp_id + // as one of its owners. + /// + /// @param[in] bp_site_id + /// The breakpoint site id to query. + /// + /// @param[in] bp_id + /// The breakpoint id to look for in \a bp_site_id. + /// + /// @result + /// True if \a bp_site_id exists in the site list AND \a bp_id is one of the + /// owners of that site. + //------------------------------------------------------------------ + bool + BreakpointSiteContainsBreakpoint (lldb::break_id_t bp_site_id, lldb::break_id_t bp_id); + + void + ForEach (std::function const &callback); + + //------------------------------------------------------------------ + /// Removes the breakpoint site given by \b breakID from this list. + /// + /// @param[in] breakID + /// The breakpoint site index to remove. + /// + /// @result + /// \b true if the breakpoint site \a breakID was in the list. + //------------------------------------------------------------------ + bool + Remove (lldb::break_id_t breakID); + + //------------------------------------------------------------------ + /// Removes the breakpoint site at address \a addr from this list. + /// + /// @param[in] addr + /// The address from which to remove a breakpoint site. + /// + /// @result + /// \b true if \a addr had a breakpoint site to remove from the list. + //------------------------------------------------------------------ + bool + RemoveByAddress (lldb::addr_t addr); + + bool + FindInRange (lldb::addr_t lower_bound, lldb::addr_t upper_bound, BreakpointSiteList &bp_site_list) const; + + typedef void (*BreakpointSiteSPMapFunc) (lldb::BreakpointSiteSP &bp, void *baton); + + //------------------------------------------------------------------ + /// Enquires of the breakpoint site on in this list with ID \a breakID whether + /// we should stop for the breakpoint or not. + /// + /// @param[in] context + /// This contains the information about this stop. + /// + /// @param[in] breakID + /// This break ID that we hit. + /// + /// @return + /// \b true if we should stop, \b false otherwise. + //------------------------------------------------------------------ + bool + ShouldStop (StoppointCallbackContext *context, lldb::break_id_t breakID); + + //------------------------------------------------------------------ + /// Returns the number of elements in the list. + /// + /// @result + /// The number of elements. + //------------------------------------------------------------------ + size_t + GetSize() const + { + Mutex::Locker locker(m_mutex); + return m_bp_site_list.size(); + } + + bool + IsEmpty() const + { + Mutex::Locker locker(m_mutex); + return m_bp_site_list.empty(); + } +protected: + typedef std::map collection; + + collection::iterator + GetIDIterator(lldb::break_id_t breakID); + + collection::const_iterator + GetIDConstIterator(lldb::break_id_t breakID) const; + + mutable Mutex m_mutex; + collection m_bp_site_list; // The breakpoint site list. +}; + +} // namespace lldb_private + +#endif // liblldb_BreakpointSiteList_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Breakpoint/Stoppoint.h b/contrib/llvm/tools/lldb/include/lldb/Breakpoint/Stoppoint.h new file mode 100644 index 00000000000..c294830f15e --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Breakpoint/Stoppoint.h @@ -0,0 +1,63 @@ +//===-- Stoppoint.h ---------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_Stoppoint_h_ +#define liblldb_Stoppoint_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private.h" +#include "lldb/Core/UserID.h" + +namespace lldb_private { + +class Stoppoint +{ +public: + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + Stoppoint(); + + virtual + ~Stoppoint(); + + //------------------------------------------------------------------ + // Methods + //------------------------------------------------------------------ + virtual void + Dump (Stream *) = 0; + + virtual bool + IsEnabled () = 0; + + virtual void + SetEnabled (bool enable) = 0; + + lldb::break_id_t + GetID () const; + + void + SetID (lldb::break_id_t bid); + +protected: + lldb::break_id_t m_bid; + +private: + //------------------------------------------------------------------ + // For Stoppoint only + //------------------------------------------------------------------ + DISALLOW_COPY_AND_ASSIGN (Stoppoint); +}; + +} // namespace lldb_private + +#endif // liblldb_Stoppoint_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Breakpoint/StoppointCallbackContext.h b/contrib/llvm/tools/lldb/include/lldb/Breakpoint/StoppointCallbackContext.h new file mode 100644 index 00000000000..78327e29134 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Breakpoint/StoppointCallbackContext.h @@ -0,0 +1,58 @@ +//===-- StoppointCallbackContext.h ------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_StoppointCallbackContext_h_ +#define liblldb_StoppointCallbackContext_h_ + +#include "lldb/lldb-private.h" +#include "lldb/Target/ExecutionContext.h" + +namespace lldb_private { + +//---------------------------------------------------------------------- +/// @class StoppointCallbackContext StoppointCallbackContext.h "lldb/Breakpoint/StoppointCallbackContext.h" +/// @brief Class holds the information that a breakpoint callback needs to evaluate this stop. +//---------------------------------------------------------------------- + +//---------------------------------------------------------------------- +/// General Outline: +/// When we hit a breakpoint we need to package up whatever information is needed +/// to evaluate breakpoint commands and conditions. This class is the container of +/// that information. +//---------------------------------------------------------------------- + +class StoppointCallbackContext +{ +public: + StoppointCallbackContext(); + + StoppointCallbackContext(Event *event, const ExecutionContext &exe_ctx, bool synchronously = false); + + //------------------------------------------------------------------ + /// Clear the object's state. + /// + /// Sets the event, process and thread to NULL, and the frame index to an + /// invalid value. + //------------------------------------------------------------------ + void + Clear (); + + //------------------------------------------------------------------ + // Member variables + //------------------------------------------------------------------ + Event *event; // This is the event, the callback can modify this to indicate + // the meaning of the breakpoint hit + ExecutionContextRef exe_ctx_ref; // This tells us where we have stopped, what thread. + bool is_synchronous; // Is the callback being executed synchronously with the breakpoint, + // or asynchronously as the event is retrieved? +}; + +} // namespace lldb_private + +#endif // liblldb_StoppointCallbackContext_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Breakpoint/StoppointLocation.h b/contrib/llvm/tools/lldb/include/lldb/Breakpoint/StoppointLocation.h new file mode 100644 index 00000000000..ccedc511951 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Breakpoint/StoppointLocation.h @@ -0,0 +1,147 @@ +//===-- StoppointLocation.h -------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_StoppointLocation_h_ +#define liblldb_StoppointLocation_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private.h" +#include "lldb/Core/UserID.h" +// #include "lldb/Breakpoint/BreakpointOptions.h" + +namespace lldb_private { + +class StoppointLocation +{ +public: + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + StoppointLocation (lldb::break_id_t bid, + lldb::addr_t m_addr, + bool hardware); + + StoppointLocation (lldb::break_id_t bid, + lldb::addr_t m_addr, + uint32_t byte_size, + bool hardware); + + virtual + ~StoppointLocation (); + + //------------------------------------------------------------------ + // Operators + //------------------------------------------------------------------ + + //------------------------------------------------------------------ + // Methods + //------------------------------------------------------------------ + virtual lldb::addr_t + GetLoadAddress() const + { + return m_addr; + } + + virtual void + SetLoadAddress (lldb::addr_t addr) + { + m_addr = addr; + } + + uint32_t + GetByteSize () const + { + return m_byte_size; + } + + uint32_t + GetHitCount () const + { + return m_hit_count; + } + + uint32_t + GetHardwareIndex () const + { + return m_hw_index; + } + + + bool + HardwarePreferred () const + { + return m_hw_preferred; + } + + virtual bool + IsHardware () const + { + return m_hw_index != LLDB_INVALID_INDEX32; + } + + + virtual bool + ShouldStop (StoppointCallbackContext *context) + { + return true; + } + + virtual void + Dump (Stream *stream) const + { + } + + void + SetHardwareIndex (uint32_t index) + { + m_hw_index = index; + } + + + lldb::break_id_t + GetID () const + { + return m_loc_id; + } + +protected: + //------------------------------------------------------------------ + // Classes that inherit from StoppointLocation can see and modify these + //------------------------------------------------------------------ + lldb::break_id_t m_loc_id; // Stoppoint location ID + lldb::addr_t m_addr; // The load address of this stop point. The base Stoppoint doesn't + // store a full Address since that's not needed for the breakpoint sites. + bool m_hw_preferred; // 1 if this point has been requested to be set using hardware (which may fail due to lack of resources) + uint32_t m_hw_index; // The hardware resource index for this breakpoint/watchpoint + uint32_t m_byte_size; // The size in bytes of stop location. e.g. the length of the trap opcode for + // software breakpoints, or the optional length in bytes for + // hardware breakpoints, or the length of the watchpoint. + uint32_t m_hit_count; // Number of times this breakpoint/watchpoint has been hit + + // If you override this, be sure to call the base class to increment the internal counter. + void + IncrementHitCount () + { + ++m_hit_count; + } + +private: + //------------------------------------------------------------------ + // For StoppointLocation only + //------------------------------------------------------------------ + DISALLOW_COPY_AND_ASSIGN(StoppointLocation); + StoppointLocation(); // Disallow default constructor +}; + +} // namespace lldb_private + +#endif // liblldb_StoppointLocation_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Breakpoint/Watchpoint.h b/contrib/llvm/tools/lldb/include/lldb/Breakpoint/Watchpoint.h new file mode 100644 index 00000000000..5dbed03d540 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Breakpoint/Watchpoint.h @@ -0,0 +1,252 @@ +//===-- Watchpoint.h --------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_Watchpoint_h_ +#define liblldb_Watchpoint_h_ + +// C Includes + +// C++ Includes +#include +#include + +// Other libraries and framework includes + +// Project includes +#include "lldb/lldb-private.h" +#include "lldb/Target/Target.h" +#include "lldb/Core/UserID.h" +#include "lldb/Breakpoint/WatchpointOptions.h" +#include "lldb/Breakpoint/StoppointLocation.h" + +namespace lldb_private { + +class Watchpoint : + public std::enable_shared_from_this, + public StoppointLocation +{ +public: + + class WatchpointEventData : + public EventData + { + public: + + static const ConstString & + GetFlavorString (); + + virtual const ConstString & + GetFlavor () const; + + WatchpointEventData (lldb::WatchpointEventType sub_type, + const lldb::WatchpointSP &new_watchpoint_sp); + + virtual + ~WatchpointEventData(); + + lldb::WatchpointEventType + GetWatchpointEventType () const; + + lldb::WatchpointSP & + GetWatchpoint (); + + virtual void + Dump (Stream *s) const; + + static lldb::WatchpointEventType + GetWatchpointEventTypeFromEvent (const lldb::EventSP &event_sp); + + static lldb::WatchpointSP + GetWatchpointFromEvent (const lldb::EventSP &event_sp); + + static const WatchpointEventData * + GetEventDataFromEvent (const Event *event_sp); + + private: + + lldb::WatchpointEventType m_watchpoint_event; + lldb::WatchpointSP m_new_watchpoint_sp; + + DISALLOW_COPY_AND_ASSIGN (WatchpointEventData); + }; + + Watchpoint (Target& target, lldb::addr_t addr, uint32_t size, const ClangASTType *type, bool hardware = true); + ~Watchpoint (); + + void + IncrementFalseAlarmsAndReviseHitCount(); + + bool + IsEnabled () const; + + void + SetEnabled (bool enabled, bool notify = true); + + virtual bool + IsHardware () const; + + virtual bool + ShouldStop (StoppointCallbackContext *context); + + bool WatchpointRead () const; + bool WatchpointWrite () const; + uint32_t GetIgnoreCount () const; + void SetIgnoreCount (uint32_t n); + void SetWatchpointType (uint32_t type, bool notify = true); + void SetDeclInfo (const std::string &str); + std::string GetWatchSpec(); + void SetWatchSpec (const std::string &str); + + // Snapshot management interface. + bool IsWatchVariable() const; + void SetWatchVariable(bool val); + bool CaptureWatchedValue (const ExecutionContext &exe_ctx); + + void GetDescription (Stream *s, lldb::DescriptionLevel level); + void Dump (Stream *s) const; + void DumpSnapshots (Stream *s, const char * prefix = NULL) const; + void DumpWithLevel (Stream *s, lldb::DescriptionLevel description_level) const; + Target &GetTarget() { return m_target; } + const Error &GetError() { return m_error; } + + //------------------------------------------------------------------ + /// Returns the WatchpointOptions structure set for this watchpoint. + /// + /// @return + /// A pointer to this watchpoint's WatchpointOptions. + //------------------------------------------------------------------ + WatchpointOptions * + GetOptions () { return &m_options; } + + //------------------------------------------------------------------ + /// Set the callback action invoked when the watchpoint is hit. + /// + /// @param[in] callback + /// The method that will get called when the watchpoint is hit. + /// @param[in] callback_baton + /// A void * pointer that will get passed back to the callback function. + /// @param[in] is_synchronous + /// If \b true the callback will be run on the private event thread + /// before the stop event gets reported. If false, the callback will get + /// handled on the public event thead after the stop has been posted. + /// + /// @return + /// \b true if the process should stop when you hit the watchpoint. + /// \b false if it should continue. + //------------------------------------------------------------------ + void + SetCallback (WatchpointHitCallback callback, + void *callback_baton, + bool is_synchronous = false); + + void + SetCallback (WatchpointHitCallback callback, + const lldb::BatonSP &callback_baton_sp, + bool is_synchronous = false); + + void ClearCallback(); + + //------------------------------------------------------------------ + /// Invoke the callback action when the watchpoint is hit. + /// + /// @param[in] context + /// Described the watchpoint event. + /// + /// @return + /// \b true if the target should stop at this watchpoint and \b false not. + //------------------------------------------------------------------ + bool + InvokeCallback (StoppointCallbackContext *context); + + //------------------------------------------------------------------ + // Condition + //------------------------------------------------------------------ + //------------------------------------------------------------------ + /// Set the watchpoint's condition. + /// + /// @param[in] condition + /// The condition expression to evaluate when the watchpoint is hit. + /// Pass in NULL to clear the condition. + //------------------------------------------------------------------ + void SetCondition (const char *condition); + + //------------------------------------------------------------------ + /// Return a pointer to the text of the condition expression. + /// + /// @return + /// A pointer to the condition expression text, or NULL if no + // condition has been set. + //------------------------------------------------------------------ + const char *GetConditionText () const; + + void + TurnOnEphemeralMode(); + + void + TurnOffEphemeralMode(); + + bool + IsDisabledDuringEphemeralMode(); + + const ClangASTType & + GetClangASTType() + { + return m_type; + } + + +private: + friend class Target; + friend class WatchpointList; + + void ResetHitCount() { m_hit_count = 0; } + + Target &m_target; + bool m_enabled; // Is this watchpoint enabled + bool m_is_hardware; // Is this a hardware watchpoint + bool m_is_watch_variable; // True if set via 'watchpoint set variable'. + bool m_is_ephemeral; // True if the watchpoint is in the ephemeral mode, meaning that it is + // undergoing a pair of temporary disable/enable actions to avoid recursively + // triggering further watchpoint events. + uint32_t m_disabled_count; // Keep track of the count that the watchpoint is disabled while in ephemeral mode. + // At the end of the ephemeral mode when the watchpoint is to be enabled agian, + // we check the count, if it is more than 1, it means the user-supplied actions + // actually want the watchpoint to be disabled! + uint32_t m_watch_read:1, // 1 if we stop when the watched data is read from + m_watch_write:1, // 1 if we stop when the watched data is written to + m_watch_was_read:1, // Set to 1 when watchpoint is hit for a read access + m_watch_was_written:1; // Set to 1 when watchpoint is hit for a write access + uint32_t m_ignore_count; // Number of times to ignore this watchpoint + uint32_t m_false_alarms; // Number of false alarms. + std::string m_decl_str; // Declaration information, if any. + std::string m_watch_spec_str; // Spec for the watchpoint. + lldb::ValueObjectSP m_old_value_sp; + lldb::ValueObjectSP m_new_value_sp; + ClangASTType m_type; + Error m_error; // An error object describing errors associated with this watchpoint. + WatchpointOptions m_options; // Settable watchpoint options, which is a delegate to handle + // the callback machinery. + bool m_being_created; + + std::unique_ptr m_condition_ap; // The condition to test. + + void SetID(lldb::watch_id_t id) { m_loc_id = id; } + + void + SendWatchpointChangedEvent (lldb::WatchpointEventType eventKind); + + void + SendWatchpointChangedEvent (WatchpointEventData *data); + + DISALLOW_COPY_AND_ASSIGN (Watchpoint); +}; + +} // namespace lldb_private + +#endif // liblldb_Watchpoint_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Breakpoint/WatchpointList.h b/contrib/llvm/tools/lldb/include/lldb/Breakpoint/WatchpointList.h new file mode 100644 index 00000000000..d16cb25e3b7 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Breakpoint/WatchpointList.h @@ -0,0 +1,276 @@ +//===-- WatchpointList.h ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_WatchpointList_h_ +#define liblldb_WatchpointList_h_ + +// C Includes +// C++ Includes +#include +#include +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private.h" +#include "lldb/Core/Address.h" +#include "lldb/Host/Mutex.h" + +namespace lldb_private { + +//---------------------------------------------------------------------- +/// @class WatchpointList WatchpointList.h "lldb/Breakpoint/WatchpointList.h" +/// @brief This class is used by Watchpoint to manage a list of watchpoints, +// each watchpoint in the list has a unique ID, and is unique by Address as +// well. +//---------------------------------------------------------------------- + +class WatchpointList +{ +// Only Target can make the watchpoint list, or add elements to it. +// This is not just some random collection of watchpoints. Rather, the act of +// adding the watchpoint to this list sets its ID. +friend class Watchpoint; +friend class Target; + +public: + //------------------------------------------------------------------ + /// Default constructor makes an empty list. + //------------------------------------------------------------------ + WatchpointList(); + + //------------------------------------------------------------------ + /// Destructor, currently does nothing. + //------------------------------------------------------------------ + ~WatchpointList(); + + //------------------------------------------------------------------ + /// Add a Watchpoint to the list. + /// + /// @param[in] wp_sp + /// A shared pointer to a watchpoint being added to the list. + /// + /// @return + /// The ID of the Watchpoint in the list. + //------------------------------------------------------------------ + lldb::watch_id_t + Add (const lldb::WatchpointSP& wp_sp, bool notify); + + //------------------------------------------------------------------ + /// Standard "Dump" method. + //------------------------------------------------------------------ + void + Dump (Stream *s) const; + + //------------------------------------------------------------------ + /// Dump with lldb::DescriptionLevel. + //------------------------------------------------------------------ + void + DumpWithLevel (Stream *s, lldb::DescriptionLevel description_level) const; + + //------------------------------------------------------------------ + /// Returns a shared pointer to the watchpoint at address + /// \a addr - + /// const version. + /// + /// @param[in] addr + /// The address to look for. + /// + /// @result + /// A shared pointer to the watchpoint. May contain a NULL + /// pointer if the watchpoint doesn't exist. + //------------------------------------------------------------------ + const lldb::WatchpointSP + FindByAddress (lldb::addr_t addr) const; + + //------------------------------------------------------------------ + /// Returns a shared pointer to the watchpoint with watchpoint spec + /// \a spec - + /// const version. + /// + /// @param[in] spec + /// The watchpoint spec to look for. + /// + /// @result + /// A shared pointer to the watchpoint. May contain a NULL + /// pointer if the watchpoint doesn't exist. + //------------------------------------------------------------------ + const lldb::WatchpointSP + FindBySpec (std::string spec) const; + + //------------------------------------------------------------------ + /// Returns a shared pointer to the watchpoint with id + /// \a watchID, const + /// version. + /// + /// @param[in] watchID + /// The watchpoint location ID to seek for. + /// + /// @result + /// A shared pointer to the watchpoint. May contain a NULL + /// pointer if the watchpoint doesn't exist. + //------------------------------------------------------------------ + lldb::WatchpointSP + FindByID (lldb::watch_id_t watchID) const; + + //------------------------------------------------------------------ + /// Returns the watchpoint id to the watchpoint + /// at address \a addr. + /// + /// @param[in] addr + /// The address to match. + /// + /// @result + /// The ID of the watchpoint, or LLDB_INVALID_WATCH_ID. + //------------------------------------------------------------------ + lldb::watch_id_t + FindIDByAddress (lldb::addr_t addr); + + //------------------------------------------------------------------ + /// Returns the watchpoint id to the watchpoint + /// with watchpoint spec \a spec. + /// + /// @param[in] spec + /// The watchpoint spec to match. + /// + /// @result + /// The ID of the watchpoint, or LLDB_INVALID_WATCH_ID. + //------------------------------------------------------------------ + lldb::watch_id_t + FindIDBySpec (std::string spec); + + //------------------------------------------------------------------ + /// Returns a shared pointer to the watchpoint with index \a i. + /// + /// @param[in] i + /// The watchpoint index to seek for. + /// + /// @result + /// A shared pointer to the watchpoint. May contain a NULL pointer if + /// the watchpoint doesn't exist. + //------------------------------------------------------------------ + lldb::WatchpointSP + GetByIndex (uint32_t i); + + //------------------------------------------------------------------ + /// Returns a shared pointer to the watchpoint with index \a i, const + /// version. + /// + /// @param[in] i + /// The watchpoint index to seek for. + /// + /// @result + /// A shared pointer to the watchpoint. May contain a NULL pointer if + /// the watchpoint location doesn't exist. + //------------------------------------------------------------------ + const lldb::WatchpointSP + GetByIndex (uint32_t i) const; + + //------------------------------------------------------------------ + /// Removes the watchpoint given by \b watchID from this list. + /// + /// @param[in] watchID + /// The watchpoint ID to remove. + /// + /// @result + /// \b true if the watchpoint \a watchID was in the list. + //------------------------------------------------------------------ + bool + Remove (lldb::watch_id_t watchID, bool notify); + + //------------------------------------------------------------------ + /// Returns the number hit count of all watchpoints in this list. + /// + /// @result + /// Hit count of all watchpoints in this list. + //------------------------------------------------------------------ + uint32_t + GetHitCount () const; + + //------------------------------------------------------------------ + /// Enquires of the watchpoint in this list with ID \a watchID whether we + /// should stop. + /// + /// @param[in] context + /// This contains the information about this stop. + /// + /// @param[in] watchID + /// This watch ID that we hit. + /// + /// @return + /// \b true if we should stop, \b false otherwise. + //------------------------------------------------------------------ + bool + ShouldStop (StoppointCallbackContext *context, + lldb::watch_id_t watchID); + + //------------------------------------------------------------------ + /// Returns the number of elements in this watchpoint list. + /// + /// @result + /// The number of elements. + //------------------------------------------------------------------ + size_t + GetSize() const + { + Mutex::Locker locker(m_mutex); + return m_watchpoints.size(); + } + + //------------------------------------------------------------------ + /// Print a description of the watchpoints in this list to the stream \a s. + /// + /// @param[in] s + /// The stream to which to print the description. + /// + /// @param[in] level + /// The description level that indicates the detail level to + /// provide. + /// + /// @see lldb::DescriptionLevel + //------------------------------------------------------------------ + void + GetDescription (Stream *s, + lldb::DescriptionLevel level); + + void + SetEnabledAll (bool enabled); + + void + RemoveAll (bool notify); + + //------------------------------------------------------------------ + /// Sets the passed in Locker to hold the Watchpoint List mutex. + /// + /// @param[in] locker + /// The locker object that is set. + //------------------------------------------------------------------ + void + GetListMutex (lldb_private::Mutex::Locker &locker); + +protected: + typedef std::list wp_collection; + typedef std::vector id_vector; + + id_vector + GetWatchpointIDs() const; + + wp_collection::iterator + GetIDIterator(lldb::watch_id_t watchID); + + wp_collection::const_iterator + GetIDConstIterator(lldb::watch_id_t watchID) const; + + wp_collection m_watchpoints; + mutable Mutex m_mutex; + + lldb::watch_id_t m_next_wp_id; +}; + +} // namespace lldb_private + +#endif // liblldb_WatchpointList_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Breakpoint/WatchpointOptions.h b/contrib/llvm/tools/lldb/include/lldb/Breakpoint/WatchpointOptions.h new file mode 100644 index 00000000000..64c65f92b44 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Breakpoint/WatchpointOptions.h @@ -0,0 +1,255 @@ +//===-- WatchpointOptions.h -------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_WatchpointOptions_h_ +#define liblldb_WatchpointOptions_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private.h" +#include "lldb/Core/Baton.h" +#include "lldb/Core/StringList.h" + +namespace lldb_private { + +//---------------------------------------------------------------------- +/// @class WatchpointOptions WatchpointOptions.h "lldb/Breakpoint/WatchpointOptions.h" +/// @brief Class that manages the options on a watchpoint. +//---------------------------------------------------------------------- + +class WatchpointOptions +{ +public: + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + //------------------------------------------------------------------ + /// Default constructor. The watchpoint is enabled, and has no condition, + /// callback, ignore count, etc... + //------------------------------------------------------------------ + WatchpointOptions(); + WatchpointOptions(const WatchpointOptions& rhs); + + static WatchpointOptions * + CopyOptionsNoCallback (WatchpointOptions &rhs); + //------------------------------------------------------------------ + /// This constructor allows you to specify all the watchpoint options. + /// + /// @param[in] callback + /// This is the plugin for some code that gets run, returns \b true if we are to stop. + /// + /// @param[in] baton + /// Client data that will get passed to the callback. + /// + /// @param[in] thread_id + /// Only stop if \a thread_id hits the watchpoint. + //------------------------------------------------------------------ + WatchpointOptions(WatchpointHitCallback callback, + void *baton, + lldb::tid_t thread_id = LLDB_INVALID_THREAD_ID); + + virtual ~WatchpointOptions(); + + //------------------------------------------------------------------ + // Operators + //------------------------------------------------------------------ + const WatchpointOptions& + operator=(const WatchpointOptions& rhs); + + //------------------------------------------------------------------ + // Callbacks + // + // Watchpoint callbacks come in two forms, synchronous and asynchronous. Synchronous callbacks will get + // run before any of the thread plans are consulted, and if they return false the target will continue + // "under the radar" of the thread plans. There are a couple of restrictions to synchronous callbacks: + // 1) They should NOT resume the target themselves. Just return false if you want the target to restart. + // 2) Watchpoints with synchronous callbacks can't have conditions (or rather, they can have them, but they + // won't do anything. Ditto with ignore counts, etc... You are supposed to control that all through the + // callback. + // Asynchronous callbacks get run as part of the "ShouldStop" logic in the thread plan. The logic there is: + // a) If the watchpoint is thread specific and not for this thread, continue w/o running the callback. + // b) If the ignore count says we shouldn't stop, then ditto. + // c) If the condition says we shouldn't stop, then ditto. + // d) Otherwise, the callback will get run, and if it returns true we will stop, and if false we won't. + // The asynchronous callback can run the target itself, but at present that should be the last action the + // callback does. We will relax this condition at some point, but it will take a bit of plumbing to get + // that to work. + // + //------------------------------------------------------------------ + + //------------------------------------------------------------------ + /// Adds a callback to the watchpoint option set. + /// + /// @param[in] callback + /// The function to be called when the watchpoint gets hit. + /// + /// @param[in] baton_sp + /// A baton which will get passed back to the callback when it is invoked. + /// + /// @param[in] synchronous + /// Whether this is a synchronous or asynchronous callback. See discussion above. + //------------------------------------------------------------------ + void SetCallback (WatchpointHitCallback callback, const lldb::BatonSP &baton_sp, bool synchronous = false); + + + //------------------------------------------------------------------ + /// Remove the callback from this option set. + //------------------------------------------------------------------ + void ClearCallback (); + + // The rest of these functions are meant to be used only within the watchpoint handling mechanism. + + //------------------------------------------------------------------ + /// Use this function to invoke the callback for a specific stop. + /// + /// @param[in] context + /// The context in which the callback is to be invoked. This includes the stop event, the + /// execution context of the stop (since you might hit the same watchpoint on multiple threads) and + /// whether we are currently executing synchronous or asynchronous callbacks. + /// + /// @param[in] watch_id + /// The watchpoint ID that owns this option set. + /// + /// @return + /// The callback return value. + //------------------------------------------------------------------ + bool InvokeCallback (StoppointCallbackContext *context, lldb::user_id_t watch_id); + + //------------------------------------------------------------------ + /// Used in InvokeCallback to tell whether it is the right time to run this kind of callback. + /// + /// @return + /// The synchronicity of our callback. + //------------------------------------------------------------------ + bool IsCallbackSynchronous () { + return m_callback_is_synchronous; + } + + //------------------------------------------------------------------ + /// Fetch the baton from the callback. + /// + /// @return + /// The baton. + //------------------------------------------------------------------ + Baton *GetBaton (); + + //------------------------------------------------------------------ + /// Fetch a const version of the baton from the callback. + /// + /// @return + /// The baton. + //------------------------------------------------------------------ + const Baton *GetBaton () const; + + //------------------------------------------------------------------ + /// Return the current thread spec for this option. This will return NULL if the no thread + /// specifications have been set for this Option yet. + /// @return + /// The thread specification pointer for this option, or NULL if none has + /// been set yet. + //------------------------------------------------------------------ + const ThreadSpec * + GetThreadSpecNoCreate () const; + + //------------------------------------------------------------------ + /// Returns a pointer to the ThreadSpec for this option, creating it. + /// if it hasn't been created already. This API is used for setting the + /// ThreadSpec items for this option. + //------------------------------------------------------------------ + ThreadSpec * + GetThreadSpec (); + + void + SetThreadID(lldb::tid_t thread_id); + + void + GetDescription (Stream *s, lldb::DescriptionLevel level) const; + + //------------------------------------------------------------------ + /// Get description for callback only. + //------------------------------------------------------------------ + void + GetCallbackDescription (Stream *s, lldb::DescriptionLevel level) const; + + //------------------------------------------------------------------ + /// Returns true if the watchpoint option has a callback set. + //------------------------------------------------------------------ + bool + HasCallback(); + + //------------------------------------------------------------------ + /// This is the default empty callback. + /// @return + /// The thread id for which the watchpoint hit will stop, + /// LLDB_INVALID_THREAD_ID for all threads. + //------------------------------------------------------------------ + static bool + NullCallback (void *baton, + StoppointCallbackContext *context, + lldb::user_id_t watch_id); + + + struct CommandData + { + CommandData () : + user_source(), + script_source(), + stop_on_error(true) + { + } + + ~CommandData () + { + } + + StringList user_source; + std::string script_source; + bool stop_on_error; + }; + + class CommandBaton : public Baton + { + public: + CommandBaton (CommandData *data) : + Baton (data) + { + } + + virtual + ~CommandBaton () + { + delete ((CommandData *)m_data); + m_data = NULL; + } + + virtual void + GetDescription (Stream *s, lldb::DescriptionLevel level) const; + + }; + +protected: + //------------------------------------------------------------------ + // Classes that inherit from WatchpointOptions can see and modify these + //------------------------------------------------------------------ + +private: + //------------------------------------------------------------------ + // For WatchpointOptions only + //------------------------------------------------------------------ + WatchpointHitCallback m_callback; // This is the callback function pointer + lldb::BatonSP m_callback_baton_sp; // This is the client data for the callback + bool m_callback_is_synchronous; + std::unique_ptr m_thread_spec_ap; // Thread for which this watchpoint will take +}; + +} // namespace lldb_private + +#endif // liblldb_WatchpointOptions_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Core/Address.h b/contrib/llvm/tools/lldb/include/lldb/Core/Address.h new file mode 100644 index 00000000000..60cd4a86bd4 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Core/Address.h @@ -0,0 +1,570 @@ +//===-- Address.h -----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_Address_h_ +#define liblldb_Address_h_ + +// C Includes +// C++ Includes +#include +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private.h" +#include "lldb/Symbol/SymbolContextScope.h" + +namespace lldb_private { + +//---------------------------------------------------------------------- +/// @class Address Address.h "lldb/Core/Address.h" +/// @brief A section + offset based address class. +/// +/// The Address class allows addresses to be relative to a section +/// that can move during runtime due to images (executables, shared +/// libraries, bundles, frameworks) being loaded at different +/// addresses than the addresses found in the object file that +/// represents them on disk. There are currently two types of addresses +/// for a section: +/// @li file addresses +/// @li load addresses +/// +/// File addresses represent the virtual addresses that are in the "on +/// disk" object files. These virtual addresses are converted to be +/// relative to unique sections scoped to the object file so that +/// when/if the addresses slide when the images are loaded/unloaded +/// in memory, we can easily track these changes without having to +/// update every object (compile unit ranges, line tables, function +/// address ranges, lexical block and inlined subroutine address +/// ranges, global and static variables) each time an image is loaded or +/// unloaded. +/// +/// Load addresses represent the virtual addresses where each section +/// ends up getting loaded at runtime. Before executing a program, it +/// is common for all of the load addresses to be unresolved. When a +/// DynamicLoader plug-in receives notification that shared libraries +/// have been loaded/unloaded, the load addresses of the main executable +/// and any images (shared libraries) will be resolved/unresolved. When +/// this happens, breakpoints that are in one of these sections can be +/// set/cleared. +//---------------------------------------------------------------------- +class Address +{ +public: + //------------------------------------------------------------------ + /// Dump styles allow the Address::Dump(Stream *,DumpStyle) const + /// function to display Address contents in a variety of ways. + //------------------------------------------------------------------ + typedef enum { + DumpStyleInvalid, ///< Invalid dump style + DumpStyleSectionNameOffset, ///< Display as the section name + offset. + ///< \code + /// // address for printf in libSystem.B.dylib as a section name + offset + /// libSystem.B.dylib.__TEXT.__text + 0x0005cfdf + /// \endcode + DumpStyleSectionPointerOffset, ///< Display as the section pointer + offset (debug output). + ///< \code + /// // address for printf in libSystem.B.dylib as a section pointer + offset + /// (lldb::Section *)0x35cc50 + 0x000000000005cfdf \endcode + DumpStyleFileAddress, ///< Display as the file address (if any). + ///< \code + /// // address for printf in libSystem.B.dylib as a file address + /// 0x000000000005dcff \endcode + DumpStyleModuleWithFileAddress, ///< Display as the file address with the module name prepended (if any). + ///< \code + /// // address for printf in libSystem.B.dylib as a file address + /// libSystem.B.dylib[0x000000000005dcff] \endcode + DumpStyleLoadAddress, ///< Display as the load address (if resolved). + ///< \code + /// // address for printf in libSystem.B.dylib as a load address + /// 0x00007fff8306bcff \endcode + DumpStyleResolvedDescription, ///< Display the details about what an address resolves to. This can + ///< be anything from a symbol context summary (module, function/symbol, + ///< and file and line), to information about what the pointer points to + ///< if the address is in a section (section of pointers, c strings, etc). + DumpStyleResolvedDescriptionNoModule, + DumpStyleDetailedSymbolContext, ///< Detailed symbol context information for an address for all symbol + ///< context members. + DumpStyleResolvedPointerDescription ///< Dereference a pointer at the current address and then lookup the + ///< dereferenced address using DumpStyleResolvedDescription + } DumpStyle; + + //------------------------------------------------------------------ + /// Default constructor. + /// + /// Initialize with a invalid section (NULL) and an invalid + /// offset (LLDB_INVALID_ADDRESS). + //------------------------------------------------------------------ + Address () : + m_section_wp (), + m_offset (LLDB_INVALID_ADDRESS) + { + } + + + //------------------------------------------------------------------ + /// Copy constructor + /// + /// Makes a copy of the another Address object \a rhs. + /// + /// @param[in] rhs + /// A const Address object reference to copy. + //------------------------------------------------------------------ + Address (const Address& rhs) : + m_section_wp (rhs.m_section_wp), + m_offset(rhs.m_offset.load()) + { + } + + //------------------------------------------------------------------ + /// Construct with a section pointer and offset. + /// + /// Initialize the address with the supplied \a section and \a + /// offset. + /// + /// @param[in] section + /// A section pointer to a valid lldb::Section, or NULL if the + /// address doesn't have a section or will get resolved later. + /// + /// @param[in] offset + /// The offset in bytes into \a section. + //------------------------------------------------------------------ + Address (const lldb::SectionSP §ion_sp, lldb::addr_t offset) : + m_section_wp (), // Don't init with section_sp in case section_sp is invalid (the weak_ptr will throw) + m_offset (offset) + { + if (section_sp) + m_section_wp = section_sp; + } + + //------------------------------------------------------------------ + /// Construct with a virtual address and section list. + /// + /// Initialize and resolve the address with the supplied virtual + /// address \a file_addr. + /// + /// @param[in] file_addr + /// A virtual file address. + /// + /// @param[in] section_list + /// A list of sections, one of which may contain the \a file_addr. + //------------------------------------------------------------------ + Address (lldb::addr_t file_addr, const SectionList * section_list); + + Address (lldb::addr_t abs_addr); + + //------------------------------------------------------------------ + /// Assignment operator. + /// + /// Copies the address value from another Address object \a rhs + /// into \a this object. + /// + /// @param[in] rhs + /// A const Address object reference to copy. + /// + /// @return + /// A const Address object reference to \a this. + //------------------------------------------------------------------ +#ifndef SWIG + const Address& + operator= (const Address& rhs); +#endif + //------------------------------------------------------------------ + /// Clear the object's state. + /// + /// Sets the section to an invalid value (NULL) and an invalid + /// offset (LLDB_INVALID_ADDRESS). + //------------------------------------------------------------------ + void + Clear () + { + m_section_wp.reset(); + m_offset = LLDB_INVALID_ADDRESS; + } + + //------------------------------------------------------------------ + /// Compare two Address objects. + /// + /// @param[in] lhs + /// The Left Hand Side const Address object reference. + /// + /// @param[in] rhs + /// The Right Hand Side const Address object reference. + /// + /// @return + /// @li -1 if lhs < rhs + /// @li 0 if lhs == rhs + /// @li 1 if lhs > rhs + //------------------------------------------------------------------ + static int + CompareFileAddress (const Address& lhs, const Address& rhs); + + static int + CompareLoadAddress (const Address& lhs, const Address& rhs, Target *target); + + static int + CompareModulePointerAndOffset (const Address& lhs, const Address& rhs); + + // For use with std::map, std::multi_map + class ModulePointerAndOffsetLessThanFunctionObject + { + public: + ModulePointerAndOffsetLessThanFunctionObject () {} + + bool + operator() (const Address& a, const Address& b) const + { + return Address::CompareModulePointerAndOffset(a, b) < 0; + } + }; + + //------------------------------------------------------------------ + /// Dump a description of this object to a Stream. + /// + /// Dump a description of the contents of this object to the + /// supplied stream \a s. There are many ways to display a section + /// offset based address, and \a style lets the user choose. + /// + /// @param[in] s + /// The stream to which to dump the object descripton. + /// + /// @param[in] style + /// The display style for the address. + /// + /// @param[in] fallback_style + /// The display style for the address. + /// + /// @return + /// Returns \b true if the address was able to be displayed. + /// File and load addresses may be unresolved and it may not be + /// possible to display a valid value, \b false will be returned + /// in such cases. + /// + /// @see Address::DumpStyle + //------------------------------------------------------------------ + bool + Dump (Stream *s, + ExecutionContextScope *exe_scope, + DumpStyle style, + DumpStyle fallback_style = DumpStyleInvalid, + uint32_t addr_byte_size = UINT32_MAX) const; + + lldb::AddressClass + GetAddressClass () const; + + //------------------------------------------------------------------ + /// Get the file address. + /// + /// If an address comes from a file on disk that has section + /// relative addresses, then it has a virtual address that is + /// relative to unique section in the object file. + /// + /// @return + /// The valid file virtual address, or LLDB_INVALID_ADDRESS if + /// the address doesn't have a file virtual address (image is + /// from memory only with no representation on disk). + //------------------------------------------------------------------ + lldb::addr_t + GetFileAddress () const; + + //------------------------------------------------------------------ + /// Get the load address. + /// + /// If an address comes from a file on disk that has section + /// relative addresses, then it has a virtual address that is + /// relative to unique section in the object file. Sections get + /// resolved at runtime by DynamicLoader plug-ins as images + /// (executables and shared libraries) get loaded/unloaded. If a + /// section is loaded, then the load address can be resolved. + /// + /// @return + /// The valid load virtual address, or LLDB_INVALID_ADDRESS if + /// the address is currently not loaded. + //------------------------------------------------------------------ + lldb::addr_t + GetLoadAddress (Target *target) const; + + //------------------------------------------------------------------ + /// Get the load address as a callable code load address. + /// + /// This function will first resolve its address to a load address. + /// Then, if the address turns out to be in code address, return the + /// load address that would be required to call or return to. The + /// address might have extra bits set (bit zero will be set to Thumb + /// functions for an ARM target) that are required when changing the + /// program counter to setting a return address. + /// + /// @return + /// The valid load virtual address, or LLDB_INVALID_ADDRESS if + /// the address is currently not loaded. + //------------------------------------------------------------------ + lldb::addr_t + GetCallableLoadAddress (Target *target, bool is_indirect = false) const; + + //------------------------------------------------------------------ + /// Get the load address as an opcode load address. + /// + /// This function will first resolve its address to a load address. + /// Then, if the address turns out to be in code address, return the + /// load address for a an opcode. This address object might have + /// extra bits set (bit zero will be set to Thumb functions for an + /// ARM target) that are required for changing the program counter + /// and this function will remove any bits that are intended for + /// these special purposes. The result of this function can be used + /// to safely write a software breakpoint trap to memory. + /// + /// @return + /// The valid load virtual address with extra callable bits + /// removed, or LLDB_INVALID_ADDRESS if the address is currently + /// not loaded. + //------------------------------------------------------------------ + lldb::addr_t + GetOpcodeLoadAddress (Target *target) const; + + //------------------------------------------------------------------ + /// Get the section relative offset value. + /// + /// @return + /// The current offset, or LLDB_INVALID_ADDRESS if this address + /// doesn't contain a valid offset. + //------------------------------------------------------------------ + lldb::addr_t + GetOffset () const { return m_offset; } + + //------------------------------------------------------------------ + /// Check if an address is section offset. + /// + /// When converting a virtual file or load address into a section + /// offset based address, we often need to know if, given a section + /// list, if the address was able to be converted to section offset. + /// This function returns true if the current value contained in + /// this object is section offset based. + /// + /// @return + /// Returns \b true if the address has a valid section and + /// offset, \b false otherwise. + //------------------------------------------------------------------ + bool + IsSectionOffset() const + { + return IsValid() && (GetSection().get() != NULL); + } + + //------------------------------------------------------------------ + /// Check if the object state is valid. + /// + /// A valid Address object contains either a section pointer and + /// and offset (for section offset based addresses), or just a valid + /// offset (for absolute addresses that have no section). + /// + /// @return + /// Returns \b true if the the offset is valid, \b false + /// otherwise. + //------------------------------------------------------------------ + bool + IsValid() const + { + return m_offset != LLDB_INVALID_ADDRESS; + } + + + //------------------------------------------------------------------ + /// Get the memory cost of this object. + /// + /// @return + /// The number of bytes that this object occupies in memory. + //------------------------------------------------------------------ + size_t + MemorySize () const; + + //------------------------------------------------------------------ + /// Resolve a file virtual address using a section list. + /// + /// Given a list of sections, attempt to resolve \a addr as a + /// an offset into one of the file sections. + /// + /// @return + /// Returns \b true if \a addr was able to be resolved, \b false + /// otherwise. + //------------------------------------------------------------------ + bool + ResolveAddressUsingFileSections (lldb::addr_t addr, const SectionList *sections); + + //------------------------------------------------------------------ + /// Set the address to represent \a load_addr. + /// + /// The address will attempt to find a loaded section within + /// \a target that contains \a load_addr. If successful, this + /// address object will have a valid section and offset. Else this + /// address object will have no section (NULL) and the offset will + /// be \a load_addr. + /// + /// @param[in] load_addr + /// A load address from a current process. + /// + /// @param[in] target + /// The target to use when trying resolve the address into + /// a section + offset. The Target's SectionLoadList object + /// is used to resolve the address. + /// + /// @return + /// Returns \b true if the load address was resolved to be + /// section/offset, \b false otherwise. It is often ok for an + /// address no not resolve to a section in a module, this often + /// happens for JIT'ed code, or any load addresses on the stack + /// or heap. + //------------------------------------------------------------------ + bool + SetLoadAddress (lldb::addr_t load_addr, Target *target); + + bool + SetOpcodeLoadAddress (lldb::addr_t load_addr, Target *target); + + bool + SetCallableLoadAddress (lldb::addr_t load_addr, Target *target); + + //------------------------------------------------------------------ + /// Get accessor for the module for this address. + /// + /// @return + /// Returns the Module pointer that this address is an offset + /// in, or NULL if this address doesn't belong in a module, or + /// isn't resolved yet. + //------------------------------------------------------------------ + lldb::ModuleSP + GetModule () const; + + //------------------------------------------------------------------ + /// Get const accessor for the section. + /// + /// @return + /// Returns the const lldb::Section pointer that this address is an + /// offset in, or NULL if this address is absolute. + //------------------------------------------------------------------ + lldb::SectionSP + GetSection () const { return m_section_wp.lock(); } + + //------------------------------------------------------------------ + /// Set accessor for the offset. + /// + /// @param[in] offset + /// A new offset value for this object. + /// + /// @return + /// Returns \b true if the offset changed, \b false otherwise. + //------------------------------------------------------------------ + bool + SetOffset (lldb::addr_t offset) + { + bool changed = m_offset != offset; + m_offset = offset; + return changed; + } + + void + SetRawAddress (lldb::addr_t addr) + { + m_section_wp.reset(); + m_offset = addr; + } + + bool + Slide (int64_t offset) + { + if (m_offset != LLDB_INVALID_ADDRESS) + { + m_offset += offset; + return true; + } + return false; + } + + //------------------------------------------------------------------ + /// Set accessor for the section. + /// + /// @param[in] section + /// A new lldb::Section pointer to use as the section base. Can + /// be NULL for absolute addresses that are not relative to + /// any section. + //------------------------------------------------------------------ + void + SetSection (const lldb::SectionSP §ion_sp) + { + m_section_wp = section_sp; + } + + void + ClearSection () + { + m_section_wp.reset(); + } + //------------------------------------------------------------------ + /// Reconstruct a symbol context from an address. + /// + /// This class doesn't inherit from SymbolContextScope because many + /// address objects have short lifespans. Address objects that are + /// section offset can reconstruct their symbol context by looking + /// up the address in the module found in the section. + /// + /// @see SymbolContextScope::CalculateSymbolContext(SymbolContext*) + //------------------------------------------------------------------ + uint32_t + CalculateSymbolContext (SymbolContext *sc, + uint32_t resolve_scope = lldb::eSymbolContextEverything) const; + + lldb::ModuleSP + CalculateSymbolContextModule () const; + + CompileUnit * + CalculateSymbolContextCompileUnit () const; + + Function * + CalculateSymbolContextFunction () const; + + Block * + CalculateSymbolContextBlock () const; + + Symbol * + CalculateSymbolContextSymbol () const; + + bool + CalculateSymbolContextLineEntry (LineEntry &line_entry) const; + +protected: + //------------------------------------------------------------------ + // Member variables. + //------------------------------------------------------------------ + lldb::SectionWP m_section_wp; ///< The section for the address, can be NULL. + std::atomic m_offset; ///< Offset into section if \a m_section_wp is valid... +}; + + +//---------------------------------------------------------------------- +// NOTE: Be careful using this operator. It can correctly compare two +// addresses from the same Module correctly. It can't compare two +// addresses from different modules in any meaningful way, but it will +// compare the module pointers. +// +// To sum things up: +// - works great for addresses within the same module +// - it works for addresses across multiple modules, but don't expect the +// address results to make much sense +// +// This basically lets Address objects be used in ordered collection +// classes. +//---------------------------------------------------------------------- +bool operator< (const Address& lhs, const Address& rhs); +bool operator> (const Address& lhs, const Address& rhs); + + + +bool operator== (const Address& lhs, const Address& rhs); +bool operator!= (const Address& lhs, const Address& rhs); + +} // namespace lldb_private + +#endif // liblldb_Address_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Core/AddressRange.h b/contrib/llvm/tools/lldb/include/lldb/Core/AddressRange.h new file mode 100644 index 00000000000..bd3ab2ab5da --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Core/AddressRange.h @@ -0,0 +1,284 @@ +//===-- AddressRange.h ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_AddressRange_h_ +#define liblldb_AddressRange_h_ + +#include "lldb/Core/Address.h" + +namespace lldb_private { + +//---------------------------------------------------------------------- +/// @class AddressRange AddressRange.h "lldb/Core/AddressRange.h" +/// @brief A section + offset based address range class. +//---------------------------------------------------------------------- +class AddressRange +{ +public: + //------------------------------------------------------------------ + /// Default constructor. + /// + /// Initialize with a invalid section (NULL), an invalid + /// offset (LLDB_INVALID_ADDRESS), and zero byte size. + //------------------------------------------------------------------ + AddressRange (); + + //------------------------------------------------------------------ + /// Construct with a section pointer, offset, and byte_size. + /// + /// Initialize the address with the supplied \a section, \a + /// offset and \a byte_size. + /// + /// @param[in] section + /// A section pointer to a valid lldb::Section, or NULL if the + /// address doesn't have a section or will get resolved later. + /// + /// @param[in] offset + /// The offset in bytes into \a section. + /// + /// @param[in] byte_size + /// The size in bytes of the address range. + //------------------------------------------------------------------ + AddressRange (const lldb::SectionSP §ion, lldb::addr_t offset, lldb::addr_t byte_size); + + //------------------------------------------------------------------ + /// Construct with a virtual address, section list and byte size. + /// + /// Initialize and resolve the address with the supplied virtual + /// address \a file_addr, and byte size \a byte_size. + /// + /// @param[in] file_addr + /// A virtual address. + /// + /// @param[in] byte_size + /// The size in bytes of the address range. + /// + /// @param[in] section_list + /// A list of sections, one of which may contain the \a vaddr. + //------------------------------------------------------------------ + AddressRange (lldb::addr_t file_addr, lldb::addr_t byte_size, const SectionList *section_list = NULL); + + //------------------------------------------------------------------ + /// Construct with a Address object address and byte size. + /// + /// Initialize by copying the section offset address in \a so_addr, + /// and setting the byte size to \a byte_size. + /// + /// @param[in] so_addr + /// A section offset address object. + /// + /// @param[in] byte_size + /// The size in bytes of the address range. + //------------------------------------------------------------------ + AddressRange (const Address& so_addr, lldb::addr_t byte_size); + + //------------------------------------------------------------------ + /// Destructor. + /// + /// The destructor is virtual in case this class is subclassed. + //------------------------------------------------------------------ + ~AddressRange (); + + //------------------------------------------------------------------ + /// Clear the object's state. + /// + /// Sets the section to an invalid value (NULL), an invalid offset + /// (LLDB_INVALID_ADDRESS) and a zero byte size. + //------------------------------------------------------------------ + void + Clear (); + + //------------------------------------------------------------------ + /// Check if a section offset address is contained in this range. + /// + /// @param[in] so_addr + /// A section offset address object reference. + /// + /// @return + /// Returns \b true if \a so_addr is contained in this range, + /// \b false otherwise. + //------------------------------------------------------------------ +// bool +// Contains (const Address &so_addr) const; + + //------------------------------------------------------------------ + /// Check if a section offset address is contained in this range. + /// + /// @param[in] so_addr_ptr + /// A section offset address object pointer. + /// + /// @return + /// Returns \b true if \a so_addr is contained in this range, + /// \b false otherwise. + //------------------------------------------------------------------ +// bool +// Contains (const Address *so_addr_ptr) const; + + //------------------------------------------------------------------ + /// Check if a section offset \a so_addr when represented as a file + /// address is contained within this object's file address range. + /// + /// @param[in] so_addr + /// A section offset address object reference. + /// + /// @return + /// Returns \b true if both \a this and \a so_addr have + /// resolvable file address values and \a so_addr is contained + /// in the address range, \b false otherwise. + //------------------------------------------------------------------ + bool + ContainsFileAddress (const Address &so_addr) const; + + //------------------------------------------------------------------ + /// Check if the resolved file address \a file_addr is contained + /// within this object's file address range. + /// + /// @param[in] so_addr + /// A section offset address object reference. + /// + /// @return + /// Returns \b true if both \a this has a resolvable file + /// address value and \a so_addr is contained in the address + /// range, \b false otherwise. + //------------------------------------------------------------------ + bool + ContainsFileAddress (lldb::addr_t file_addr) const; + + //------------------------------------------------------------------ + /// Check if a section offset \a so_addr when represented as a load + /// address is contained within this object's load address range. + /// + /// @param[in] so_addr + /// A section offset address object reference. + /// + /// @return + /// Returns \b true if both \a this and \a so_addr have + /// resolvable load address values and \a so_addr is contained + /// in the address range, \b false otherwise. + //------------------------------------------------------------------ + bool + ContainsLoadAddress (const Address &so_addr, Target *target) const; + + //------------------------------------------------------------------ + /// Check if the resolved load address \a load_addr is contained + /// within this object's load address range. + /// + /// @param[in] so_addr + /// A section offset address object reference. + /// + /// @return + /// Returns \b true if both \a this has a resolvable load + /// address value and \a so_addr is contained in the address + /// range, \b false otherwise. + //------------------------------------------------------------------ + bool + ContainsLoadAddress (lldb::addr_t load_addr, Target *target) const; + + //------------------------------------------------------------------ + /// Dump a description of this object to a Stream. + /// + /// Dump a description of the contents of this object to the + /// supplied stream \a s. There are many ways to display a section + /// offset based address range, and \a style lets the user choose + /// how the base address gets displayed. + /// + /// @param[in] s + /// The stream to which to dump the object descripton. + /// + /// @param[in] style + /// The display style for the address. + /// + /// @return + /// Returns \b true if the address was able to be displayed. + /// File and load addresses may be unresolved and it may not be + /// possible to display a valid value, \b false will be returned + /// in such cases. + /// + /// @see Address::DumpStyle + //------------------------------------------------------------------ + bool + Dump (Stream *s, Target *target, Address::DumpStyle style, Address::DumpStyle fallback_style = Address::DumpStyleInvalid) const; + + //------------------------------------------------------------------ + /// Dump a debug description of this object to a Stream. + /// + /// Dump a debug description of the contents of this object to the + /// supplied stream \a s. + /// + /// The debug description contains verbose internal state such + /// and pointer values, reference counts, etc. + /// + /// @param[in] s + /// The stream to which to dump the object descripton. + //------------------------------------------------------------------ + void + DumpDebug (Stream *s) const; + + //------------------------------------------------------------------ + /// Get accessor for the base address of the range. + /// + /// @return + /// A reference to the base address object. + //------------------------------------------------------------------ + Address & + GetBaseAddress() { return m_base_addr; } + + //------------------------------------------------------------------ + /// Get const accessor for the base address of the range. + /// + /// @return + /// A const reference to the base address object. + //------------------------------------------------------------------ + const Address & + GetBaseAddress() const { return m_base_addr; } + + //------------------------------------------------------------------ + /// Get accessor for the byte size of this range. + /// + /// @return + /// The size in bytes of this address range. + //------------------------------------------------------------------ + lldb::addr_t + GetByteSize () const { return m_byte_size; } + + //------------------------------------------------------------------ + /// Get the memory cost of this object. + /// + /// @return + /// The number of bytes that this object occupies in memory. + //------------------------------------------------------------------ + size_t + MemorySize () const { + // Noting special for the memory size of a single AddressRange object, + // it is just the size of itself. + return sizeof(AddressRange); + } + + //------------------------------------------------------------------ + /// Set accessor for the byte size of this range. + /// + /// @param[in] byte_size + /// The new size in bytes of this address range. + //------------------------------------------------------------------ + void + SetByteSize (lldb::addr_t byte_size) { m_byte_size = byte_size; } + +protected: + //------------------------------------------------------------------ + // Member variables + //------------------------------------------------------------------ + Address m_base_addr; ///< The section offset base address of this range. + lldb::addr_t m_byte_size; ///< The size in bytes of this address range. +}; + +//bool operator== (const AddressRange& lhs, const AddressRange& rhs); + +} // namespace lldb_private + +#endif // liblldb_AddressRange_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Core/AddressResolver.h b/contrib/llvm/tools/lldb/include/lldb/Core/AddressResolver.h new file mode 100644 index 00000000000..e5fe276e3fb --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Core/AddressResolver.h @@ -0,0 +1,89 @@ +//===-- AddressResolver.h ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_AddressResolver_h_ +#define liblldb_AddressResolver_h_ + +#include + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private.h" +#include "lldb/Core/Address.h" +#include "lldb/Core/AddressRange.h" +#include "lldb/Host/FileSpec.h" +#include "lldb/Core/SearchFilter.h" +#include "lldb/Core/ConstString.h" + +namespace lldb_private { + +//---------------------------------------------------------------------- +/// @class AddressResolver AddressResolver.h "lldb/Core/AddressResolver.h" +/// @brief This class works with SearchFilter to resolve function names and +/// source file locations to their concrete addresses. +//---------------------------------------------------------------------- + +//---------------------------------------------------------------------- +/// General Outline: +/// The AddressResolver is a Searcher. In that protocol, +/// the SearchFilter asks the question "At what depth of the symbol context +/// descent do you want your callback to get called?" of the filter. The resolver +/// answers this question (in the GetDepth method) and provides the resolution callback. +//---------------------------------------------------------------------- + +class AddressResolver : + public Searcher +{ +public: + + typedef enum + { + Exact, + Regexp, + Glob + } MatchType; + + + AddressResolver (); + + virtual + ~AddressResolver (); + + virtual void + ResolveAddress (SearchFilter &filter); + + virtual void + ResolveAddressInModules (SearchFilter &filter, + ModuleList &modules); + + virtual void + GetDescription (Stream *s) = 0; + + std::vector & + GetAddressRanges (); + + size_t + GetNumberOfAddresses (); + + AddressRange & + GetAddressRangeAtIndex (size_t idx); + +protected: + + std::vector m_address_ranges; + +private: + DISALLOW_COPY_AND_ASSIGN(AddressResolver); +}; + +} // namespace lldb_private + +#endif // liblldb_AddressResolver_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Core/AddressResolverFileLine.h b/contrib/llvm/tools/lldb/include/lldb/Core/AddressResolverFileLine.h new file mode 100644 index 00000000000..ddeb0e0301d --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Core/AddressResolverFileLine.h @@ -0,0 +1,59 @@ +//===-- AddressResolverFileLine.h -------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_AddressResolverFileLine_h_ +#define liblldb_AddressResolverFileLine_h_ + +// Project includes +#include "lldb/Core/AddressResolver.h" + +namespace lldb_private { + +//---------------------------------------------------------------------- +/// @class AddressResolverFileLine AddressResolverFileLine.h "lldb/Core/AddressResolverFileLine.h" +/// @brief This class finds address for source file and line. Optionally, it will look for inlined +/// instances of the file and line specification. +//---------------------------------------------------------------------- + +class AddressResolverFileLine : + public AddressResolver +{ +public: + + AddressResolverFileLine (const FileSpec &resolver, + uint32_t line_no, + bool check_inlines); + + virtual + ~AddressResolverFileLine (); + + virtual Searcher::CallbackReturn + SearchCallback (SearchFilter &filter, + SymbolContext &context, + Address *addr, + bool containing); + + virtual Searcher::Depth + GetDepth (); + + virtual void + GetDescription (Stream *s); + +protected: + FileSpec m_file_spec; // This is the file spec we are looking for. + uint32_t m_line_number; // This is the line number that we are looking for. + bool m_inlines; // This determines whether the resolver looks for inlined functions or not. + +private: + DISALLOW_COPY_AND_ASSIGN(AddressResolverFileLine); +}; + +} // namespace lldb_private + +#endif // liblldb_AddressResolverFileLine_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Core/AddressResolverName.h b/contrib/llvm/tools/lldb/include/lldb/Core/AddressResolverName.h new file mode 100644 index 00000000000..afde675a89b --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Core/AddressResolverName.h @@ -0,0 +1,68 @@ +//===-- AddressResolverName.h -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_AddressResolverName_h_ +#define liblldb_AddressResolverName_h_ + +// Project includes + +#include "lldb/Core/AddressResolver.h" +#include "lldb/Core/RegularExpression.h" + +namespace lldb_private { + +//---------------------------------------------------------------------- +/// @class AddressResolverName AddressResolverName.h "lldb/Core/AddressResolverName.h" +/// @brief This class finds addresses for a given function name, either by exact match +/// or by regular expression. +//---------------------------------------------------------------------- + +class AddressResolverName: + public AddressResolver +{ +public: + + AddressResolverName (const char *func_name, + AddressResolver::MatchType type = Exact); + + // Creates a function breakpoint by regular expression. Takes over control of the lifespan of func_regex. + AddressResolverName (RegularExpression &func_regex); + + AddressResolverName (const char *class_name, + const char *method, + AddressResolver::MatchType type); + + virtual + ~AddressResolverName (); + + virtual Searcher::CallbackReturn + SearchCallback (SearchFilter &filter, + SymbolContext &context, + Address *addr, + bool containing); + + virtual Searcher::Depth + GetDepth (); + + virtual void + GetDescription (Stream *s); + +protected: + ConstString m_func_name; + ConstString m_class_name; // FIXME: Not used yet. The idea would be to stop on methods of this class. + RegularExpression m_regex; + AddressResolver::MatchType m_match_type; + +private: + DISALLOW_COPY_AND_ASSIGN(AddressResolverName); +}; + +} // namespace lldb_private + +#endif // liblldb_AddressResolverName_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Core/ArchSpec.h b/contrib/llvm/tools/lldb/include/lldb/Core/ArchSpec.h new file mode 100644 index 00000000000..3bfa96be0ce --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Core/ArchSpec.h @@ -0,0 +1,422 @@ +//===-- ArchSpec.h ----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ArchSpec_h_ +#define liblldb_ArchSpec_h_ + +#if defined(__cplusplus) + +#include "lldb/lldb-private.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/Triple.h" + +namespace lldb_private { + +struct CoreDefinition; + +//---------------------------------------------------------------------- +/// @class ArchSpec ArchSpec.h "lldb/Core/ArchSpec.h" +/// @brief An architecture specification class. +/// +/// A class designed to be created from a cpu type and subtype, a +/// string representation, or an llvm::Triple. Keeping all of the +/// conversions of strings to architecture enumeration values confined +/// to this class allows new architecture support to be added easily. +//---------------------------------------------------------------------- +class ArchSpec +{ +public: + enum Core + { + eCore_arm_generic, + eCore_arm_armv4, + eCore_arm_armv4t, + eCore_arm_armv5, + eCore_arm_armv5e, + eCore_arm_armv5t, + eCore_arm_armv6, + eCore_arm_armv7, + eCore_arm_armv7f, + eCore_arm_armv7s, + eCore_arm_armv7k, + eCore_arm_armv7m, + eCore_arm_armv7em, + eCore_arm_xscale, + eCore_thumb, + eCore_thumbv4t, + eCore_thumbv5, + eCore_thumbv5e, + eCore_thumbv6, + eCore_thumbv7, + eCore_thumbv7f, + eCore_thumbv7s, + eCore_thumbv7k, + eCore_thumbv7m, + eCore_thumbv7em, + + eCore_ppc_generic, + eCore_ppc_ppc601, + eCore_ppc_ppc602, + eCore_ppc_ppc603, + eCore_ppc_ppc603e, + eCore_ppc_ppc603ev, + eCore_ppc_ppc604, + eCore_ppc_ppc604e, + eCore_ppc_ppc620, + eCore_ppc_ppc750, + eCore_ppc_ppc7400, + eCore_ppc_ppc7450, + eCore_ppc_ppc970, + + eCore_ppc64_generic, + eCore_ppc64_ppc970_64, + + eCore_sparc_generic, + + eCore_sparc9_generic, + + eCore_x86_32_i386, + eCore_x86_32_i486, + eCore_x86_32_i486sx, + + eCore_x86_64_x86_64, + eCore_uknownMach32, + eCore_uknownMach64, + kNumCores, + + kCore_invalid, + // The following constants are used for wildcard matching only + kCore_any, + kCore_arm_any, + kCore_ppc_any, + kCore_ppc64_any, + kCore_x86_32_any, + + kCore_arm_first = eCore_arm_generic, + kCore_arm_last = eCore_arm_xscale, + + kCore_thumb_first = eCore_thumb, + kCore_thumb_last = eCore_thumbv7em, + + kCore_ppc_first = eCore_ppc_generic, + kCore_ppc_last = eCore_ppc_ppc970, + + kCore_ppc64_first = eCore_ppc64_generic, + kCore_ppc64_last = eCore_ppc64_ppc970_64, + + kCore_x86_32_first = eCore_x86_32_i386, + kCore_x86_32_last = eCore_x86_32_i486sx + }; + + //------------------------------------------------------------------ + /// Default constructor. + /// + /// Default constructor that initializes the object with invalid + /// cpu type and subtype values. + //------------------------------------------------------------------ + ArchSpec (); + + //------------------------------------------------------------------ + /// Constructor over triple. + /// + /// Constructs an ArchSpec with properties consistent with the given + /// Triple. + //------------------------------------------------------------------ + explicit + ArchSpec (const llvm::Triple &triple); + explicit + ArchSpec (const char *triple_cstr); + explicit + ArchSpec (const char *triple_cstr, Platform *platform); + //------------------------------------------------------------------ + /// Constructor over architecture name. + /// + /// Constructs an ArchSpec with properties consistent with the given + /// object type and architecture name. + //------------------------------------------------------------------ + explicit + ArchSpec (ArchitectureType arch_type, + uint32_t cpu_type, + uint32_t cpu_subtype); + + //------------------------------------------------------------------ + /// Destructor. + //------------------------------------------------------------------ + ~ArchSpec (); + + //------------------------------------------------------------------ + /// Assignment operator. + /// + /// @param[in] rhs another ArchSpec object to copy. + /// + /// @return A const reference to this object. + //------------------------------------------------------------------ + const ArchSpec& + operator= (const ArchSpec& rhs); + + static size_t + AutoComplete (const char *name, + StringList &matches); + + //------------------------------------------------------------------ + /// Returns a static string representing the current architecture. + /// + /// @return A static string correcponding to the current + /// architecture. + //------------------------------------------------------------------ + const char * + GetArchitectureName () const; + + //------------------------------------------------------------------ + /// Clears the object state. + /// + /// Clears the object state back to a default invalid state. + //------------------------------------------------------------------ + void + Clear (); + + //------------------------------------------------------------------ + /// Returns the size in bytes of an address of the current + /// architecture. + /// + /// @return The byte size of an address of the current architecture. + //------------------------------------------------------------------ + uint32_t + GetAddressByteSize () const; + + //------------------------------------------------------------------ + /// Returns a machine family for the current architecture. + /// + /// @return An LLVM arch type. + //------------------------------------------------------------------ + llvm::Triple::ArchType + GetMachine () const; + + //------------------------------------------------------------------ + /// Tests if this ArchSpec is valid. + /// + /// @return True if the current architecture is valid, false + /// otherwise. + //------------------------------------------------------------------ + bool + IsValid () const + { + return m_core >= eCore_arm_generic && m_core < kNumCores; + } + + bool + TripleVendorWasSpecified() const + { + return !m_triple.getVendorName().empty(); + } + + bool + TripleOSWasSpecified() const + { + return !m_triple.getOSName().empty(); + } + + //------------------------------------------------------------------ + /// Sets this ArchSpec according to the given architecture name. + /// + /// The architecture name can be one of the generic system default + /// values: + /// + /// @li \c LLDB_ARCH_DEFAULT - The arch the current system defaults + /// to when a program is launched without any extra + /// attributes or settings. + /// @li \c LLDB_ARCH_DEFAULT_32BIT - The default host architecture + /// for 32 bit (if any). + /// @li \c LLDB_ARCH_DEFAULT_64BIT - The default host architecture + /// for 64 bit (if any). + /// + /// Alternatively, if the object type of this ArchSpec has been + /// configured, a concrete architecture can be specified to set + /// the CPU type ("x86_64" for example). + /// + /// Finally, an encoded object and archetecture format is accepted. + /// The format contains an object type (like "macho" or "elf"), + /// followed by a platform dependent encoding of CPU type and + /// subtype. For example: + /// + /// "macho" : Specifies an object type of MachO. + /// "macho-16-6" : MachO specific encoding for ARMv6. + /// "elf-43 : ELF specific encoding for Sparc V9. + /// + /// @param[in] arch_name The name of an architecture. + /// + /// @return True if @p arch_name was successfully translated, false + /// otherwise. + //------------------------------------------------------------------ +// bool +// SetArchitecture (const llvm::StringRef& arch_name); +// +// bool +// SetArchitecture (const char *arch_name); + + //------------------------------------------------------------------ + /// Change the architecture object type and CPU type. + /// + /// @param[in] arch_type The object type of this ArchSpec. + /// + /// @param[in] cpu The required CPU type. + /// + /// @return True if the object and CPU type were sucessfully set. + //------------------------------------------------------------------ + bool + SetArchitecture (ArchitectureType arch_type, + uint32_t cpu, + uint32_t sub); + + //------------------------------------------------------------------ + /// Returns the byte order for the architecture specification. + /// + /// @return The endian enumeration for the current endianness of + /// the architecture specification + //------------------------------------------------------------------ + lldb::ByteOrder + GetByteOrder () const; + + //------------------------------------------------------------------ + /// Sets this ArchSpec's byte order. + /// + /// In the common case there is no need to call this method as the + /// byte order can almost always be determined by the architecture. + /// However, many CPU's are bi-endian (ARM, Alpha, PowerPC, etc) + /// and the default/assumed byte order may be incorrect. + //------------------------------------------------------------------ + void + SetByteOrder (lldb::ByteOrder byte_order) + { + m_byte_order = byte_order; + } + + uint32_t + GetMinimumOpcodeByteSize() const; + + uint32_t + GetMaximumOpcodeByteSize() const; + + Core + GetCore () const + { + return m_core; + } + + uint32_t + GetMachOCPUType () const; + + uint32_t + GetMachOCPUSubType () const; + + //------------------------------------------------------------------ + /// Architecture tripple accessor. + /// + /// @return A triple describing this ArchSpec. + //------------------------------------------------------------------ + llvm::Triple & + GetTriple () + { + return m_triple; + } + + //------------------------------------------------------------------ + /// Architecture tripple accessor. + /// + /// @return A triple describing this ArchSpec. + //------------------------------------------------------------------ + const llvm::Triple & + GetTriple () const + { + return m_triple; + } + + //------------------------------------------------------------------ + /// Architecture tripple setter. + /// + /// Configures this ArchSpec according to the given triple. If the + /// triple has unknown components in all of the vendor, OS, and + /// the optional environment field (i.e. "i386-unknown-unknown") + /// then default values are taken from the host. Architecture and + /// environment components are used to further resolve the CPU type + /// and subtype, endian characteristics, etc. + /// + /// @return A triple describing this ArchSpec. + //------------------------------------------------------------------ + bool + SetTriple (const llvm::Triple &triple); + + bool + SetTriple (const char *triple_cstr); + + bool + SetTriple (const char *triple_cstr, + Platform *platform); + + //------------------------------------------------------------------ + /// Returns the default endianness of the architecture. + /// + /// @return The endian enumeration for the default endianness of + /// the architecture. + //------------------------------------------------------------------ + lldb::ByteOrder + GetDefaultEndian () const; + + //------------------------------------------------------------------ + /// Compare an ArchSpec to another ArchSpec, requiring an exact cpu + /// type match between them. + /// e.g. armv7s is not an exact match with armv7 - this would return false + /// + /// @return true if the two ArchSpecs match. + //------------------------------------------------------------------ + bool + IsExactMatch (const ArchSpec& rhs) const; + + //------------------------------------------------------------------ + /// Compare an ArchSpec to another ArchSpec, requiring a compatible + /// cpu type match between them. + /// e.g. armv7s is compatible with armv7 - this method would return true + /// + /// @return true if the two ArchSpecs are compatible + //------------------------------------------------------------------ + bool + IsCompatibleMatch (const ArchSpec& rhs) const; + +protected: + bool + IsEqualTo (const ArchSpec& rhs, bool exact_match) const; + + llvm::Triple m_triple; + Core m_core; + lldb::ByteOrder m_byte_order; + + // Called when m_def or m_entry are changed. Fills in all remaining + // members with default values. + void + CoreUpdated (bool update_triple); +}; + +//------------------------------------------------------------------ +/// @fn bool operator< (const ArchSpec& lhs, const ArchSpec& rhs) +/// @brief Less than operator. +/// +/// Tests two ArchSpec objects to see if \a lhs is less than \a +/// rhs. +/// +/// @param[in] lhs The Left Hand Side ArchSpec object to compare. +/// @param[in] rhs The Left Hand Side ArchSpec object to compare. +/// +/// @return true if \a lhs is less than \a rhs +//------------------------------------------------------------------ +bool operator< (const ArchSpec& lhs, const ArchSpec& rhs); + +} // namespace lldb_private + +#endif // #if defined(__cplusplus) +#endif // #ifndef liblldb_ArchSpec_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Core/Baton.h b/contrib/llvm/tools/lldb/include/lldb/Core/Baton.h new file mode 100644 index 00000000000..627e7203a4a --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Core/Baton.h @@ -0,0 +1,62 @@ +//===-- Baton.h -------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef lldb_Baton_h_ +#define lldb_Baton_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-public.h" + +namespace lldb_private { + +//---------------------------------------------------------------------- +/// @class Baton Baton.h "lldb/Core/Baton.h" +/// @brief A class designed to wrap callback batons so they can cleanup +/// any acquired resources +/// +/// This class is designed to be used by any objects that have a +/// callback function that takes a baton where the baton might need to +/// free/delete/close itself. +/// +/// The default behavior is to not free anything. Subclasses can +/// free any needed resources in their destructors. +//---------------------------------------------------------------------- +class Baton +{ +public: + explicit Baton(void *p) : + m_data (p) + { + } + + virtual + ~Baton() + { + // The default destructor for a baton does NOT attempt to clean up + // anything in m_baton + } + + virtual void + GetDescription (Stream *s, lldb::DescriptionLevel level) const; + + void *m_data; // Leave baton public for easy access + +private: + //------------------------------------------------------------------ + // For Baton only + //------------------------------------------------------------------ + DISALLOW_COPY_AND_ASSIGN (Baton); +}; + +} // namespace lldb_private + +#endif // lldb_Baton_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Core/Broadcaster.h b/contrib/llvm/tools/lldb/include/lldb/Core/Broadcaster.h new file mode 100644 index 00000000000..64b12ca8a93 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Core/Broadcaster.h @@ -0,0 +1,475 @@ +//===-- Broadcaster.h -------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_Broadcaster_h_ +#define liblldb_Broadcaster_h_ + +// C Includes +// C++ Includes +#include +#include +#include + +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private.h" +//#include "lldb/Core/Flags.h" +#include "lldb/Core/ConstString.h" +#include "lldb/Core/Listener.h" + +namespace lldb_private { + +//---------------------------------------------------------------------- +// lldb::BroadcastEventSpec +// +// This class is used to specify a kind of event to register for. The Debugger +// maintains a list of BroadcastEventSpec's and when it is made +//---------------------------------------------------------------------- +class BroadcastEventSpec +{ +public: + BroadcastEventSpec (const ConstString &broadcaster_class, uint32_t event_bits) : + m_broadcaster_class (broadcaster_class), + m_event_bits (event_bits) + { + } + + BroadcastEventSpec (const BroadcastEventSpec &rhs); + + ~BroadcastEventSpec() {} + + const ConstString &GetBroadcasterClass() const + { + return m_broadcaster_class; + } + + uint32_t GetEventBits () const + { + return m_event_bits; + } + + // Tell whether this BroadcastEventSpec is contained in in_spec. + // That is: + // (a) the two spec's share the same broadcaster class + // (b) the event bits of this spec are wholly contained in those of in_spec. + bool IsContainedIn (BroadcastEventSpec in_spec) const + { + if (m_broadcaster_class != in_spec.GetBroadcasterClass()) + return false; + uint32_t in_bits = in_spec.GetEventBits(); + if (in_bits == m_event_bits) + return true; + else + { + if ((m_event_bits & in_bits) != 0 + && (m_event_bits & ~in_bits) == 0) + return true; + } + return false; + } + + bool operator< (const BroadcastEventSpec &rhs) const; + const BroadcastEventSpec &operator= (const BroadcastEventSpec &rhs); + +private: + ConstString m_broadcaster_class; + uint32_t m_event_bits; +}; + +class BroadcasterManager +{ +public: + friend class Listener; + + BroadcasterManager (); + + ~BroadcasterManager () {} + + uint32_t + RegisterListenerForEvents (Listener &listener, BroadcastEventSpec event_spec); + + bool + UnregisterListenerForEvents (Listener &listener, BroadcastEventSpec event_spec); + + Listener * + GetListenerForEventSpec (BroadcastEventSpec event_spec) const; + + void + SignUpListenersForBroadcaster (Broadcaster &broadcaster); + + void + RemoveListener (Listener &Listener); + +protected: + void Clear(); + +private: + typedef std::pair event_listener_key; + typedef std::map collection; + typedef std::set listener_collection; + collection m_event_map; + listener_collection m_listeners; + + Mutex m_manager_mutex; + + // A couple of comparator classes for find_if: + + class BroadcasterClassMatches + { + public: + BroadcasterClassMatches (const ConstString &broadcaster_class) : + m_broadcaster_class (broadcaster_class) + { + } + + ~BroadcasterClassMatches () {} + + bool operator() (const event_listener_key input) const + { + return (input.first.GetBroadcasterClass() == m_broadcaster_class); + } + + private: + ConstString m_broadcaster_class; + }; + + class BroadcastEventSpecMatches + { + public: + BroadcastEventSpecMatches (BroadcastEventSpec broadcaster_spec) : + m_broadcaster_spec (broadcaster_spec) + { + } + + ~BroadcastEventSpecMatches () {} + + bool operator() (const event_listener_key input) const + { + return (input.first.IsContainedIn (m_broadcaster_spec)); + } + + private: + BroadcastEventSpec m_broadcaster_spec; + }; + + class ListenerMatchesAndSharedBits + { + public: + ListenerMatchesAndSharedBits (BroadcastEventSpec broadcaster_spec, + const Listener &listener) : + m_broadcaster_spec (broadcaster_spec), + m_listener (&listener) + { + } + + ~ListenerMatchesAndSharedBits () {} + + bool operator() (const event_listener_key input) const + { + return (input.first.GetBroadcasterClass() == m_broadcaster_spec.GetBroadcasterClass() + && (input.first.GetEventBits() & m_broadcaster_spec.GetEventBits()) != 0 + && input.second == m_listener); + } + + private: + BroadcastEventSpec m_broadcaster_spec; + const Listener *m_listener; + }; + + class ListenerMatches + { + public: + ListenerMatches (const Listener &in_listener) : + m_listener (&in_listener) + { + } + + ~ListenerMatches() {} + + bool operator () (const event_listener_key input) const + { + if (input.second == m_listener) + return true; + else + return false; + } + + private: + const Listener *m_listener; + + }; + +}; + +//---------------------------------------------------------------------- +/// @class Broadcaster Broadcaster.h "lldb/Core/Broadcaster.h" +/// @brief An event broadcasting class. +/// +/// The Broadcaster class is designed to be subclassed by objects that +/// wish to vend events in a multi-threaded environment. Broadcaster +/// objects can each vend 32 events. Each event is represented by a bit +/// in a 32 bit value and these bits can be set: +/// @see Broadcaster::SetEventBits(uint32_t) +/// or cleared: +/// @see Broadcaster::ResetEventBits(uint32_t) +/// When an event gets set the Broadcaster object will notify the +/// Listener object that is listening for the event (if there is one). +/// +/// Subclasses should provide broadcast bit definitions for any events +/// they vend, typically using an enumeration: +/// \code +/// class Foo : public Broadcaster +/// { +/// public: +/// //---------------------------------------------------------- +/// // Broadcaster event bits definitions. +/// //---------------------------------------------------------- +/// enum +/// { +/// eBroadcastBitStateChanged = (1 << 0), +/// eBroadcastBitInterrupt = (1 << 1), +/// eBroadcastBitSTDOUT = (1 << 2), +/// eBroadcastBitSTDERR = (1 << 3), +/// eBroadcastBitProfileData = (1 << 4) +/// }; +/// \endcode +//---------------------------------------------------------------------- +class Broadcaster +{ +public: + //------------------------------------------------------------------ + /// Construct with a broadcaster with a name. + /// + /// @param[in] name + /// A NULL terminated C string that contains the name of the + /// broadcaster object. + //------------------------------------------------------------------ + Broadcaster (BroadcasterManager *manager, const char *name); + + //------------------------------------------------------------------ + /// Destructor. + /// + /// The destructor is virtual since this class gets subclassed. + //------------------------------------------------------------------ + virtual + ~Broadcaster(); + + void + CheckInWithManager (); + + //------------------------------------------------------------------ + /// Broadcast an event which has no associated data. + /// + /// @param[in] event_type + /// The element from the enum defining this broadcaster's events + /// that is being broadcast. + /// + /// @param[in] event_data + /// User event data that will be owned by the lldb::Event that + /// is created internally. + /// + /// @param[in] unique + /// If true, then only add an event of this type if there isn't + /// one already in the queue. + /// + //------------------------------------------------------------------ + void + BroadcastEvent (lldb::EventSP &event_sp); + + void + BroadcastEventIfUnique (lldb::EventSP &event_sp); + + void + BroadcastEvent (uint32_t event_type, EventData *event_data = NULL); + + void + BroadcastEventIfUnique (uint32_t event_type, EventData *event_data = NULL); + + void + Clear(); + + virtual void + AddInitialEventsToListener (Listener *listener, uint32_t requested_events); + + //------------------------------------------------------------------ + /// Listen for any events specified by \a event_mask. + /// + /// Only one listener can listen to each event bit in a given + /// Broadcaster. Once a listener has acquired an event bit, no + /// other broadcaster will have access to it until it is + /// relinquished by the first listener that gets it. The actual + /// event bits that get acquired by \a listener may be different + /// from what is requested in \a event_mask, and to track this the + /// actual event bits that are acquired get returned. + /// + /// @param[in] listener + /// The Listener object that wants to monitor the events that + /// get broadcast by this object. + /// + /// @param[in] event_mask + /// A bit mask that indicates which events the listener is + /// asking to monitor. + /// + /// @return + /// The actual event bits that were acquired by \a listener. + //------------------------------------------------------------------ + uint32_t + AddListener (Listener* listener, uint32_t event_mask); + + //------------------------------------------------------------------ + /// Get the NULL terminated C string name of this Broadcaster + /// object. + /// + /// @return + /// The NULL terminated C string name of this Broadcaster. + //------------------------------------------------------------------ + const ConstString & + GetBroadcasterName (); + + + //------------------------------------------------------------------ + /// Get the event name(s) for one or more event bits. + /// + /// @param[in] event_mask + /// A bit mask that indicates which events to get names for. + /// + /// @return + /// The NULL terminated C string name of this Broadcaster. + //------------------------------------------------------------------ + bool + GetEventNames (Stream &s, const uint32_t event_mask, bool prefix_with_broadcaster_name) const; + + //------------------------------------------------------------------ + /// Set the name for an event bit. + /// + /// @param[in] event_mask + /// A bit mask that indicates which events the listener is + /// asking to monitor. + /// + /// @return + /// The NULL terminated C string name of this Broadcaster. + //------------------------------------------------------------------ + void + SetEventName (uint32_t event_mask, const char *name) + { + m_event_names[event_mask] = name; + } + + const char * + GetEventName (uint32_t event_mask) const + { + event_names_map::const_iterator pos = m_event_names.find (event_mask); + if (pos != m_event_names.end()) + return pos->second.c_str(); + return NULL; + } + + bool + EventTypeHasListeners (uint32_t event_type); + + //------------------------------------------------------------------ + /// Removes a Listener from this broadcasters list and frees the + /// event bits specified by \a event_mask that were previously + /// acquired by \a listener (assuming \a listener was listening to + /// this object) for other listener objects to use. + /// + /// @param[in] listener + /// A Listener object that previously called AddListener. + /// + /// @param[in] event_mask + /// The event bits \a listener wishes to relinquish. + /// + /// @return + /// \b True if the listener was listening to this broadcaster + /// and was removed, \b false otherwise. + /// + /// @see uint32_t Broadcaster::AddListener (Listener*, uint32_t) + //------------------------------------------------------------------ + bool + RemoveListener (Listener* listener, uint32_t event_mask = UINT32_MAX); + + //------------------------------------------------------------------ + /// Provides a simple mechanism to temporarily redirect events from + /// broadcaster. When you call this function passing in a listener and + /// event type mask, all events from the broadcaster matching the mask + /// will now go to the hijacking listener. + /// Only one hijack can occur at a time. If we need more than this we + /// will have to implement a Listener stack. + /// + /// @param[in] listener + /// A Listener object. You do not need to call StartListeningForEvents + /// for this broadcaster (that would fail anyway since the event bits + /// would most likely be taken by the listener(s) you are usurping. + /// + /// @param[in] event_mask + /// The event bits \a listener wishes to hijack. + /// + /// @return + /// \b True if the event mask could be hijacked, \b false otherwise. + /// + /// @see uint32_t Broadcaster::AddListener (Listener*, uint32_t) + //------------------------------------------------------------------ + bool + HijackBroadcaster (Listener *listener, uint32_t event_mask = UINT32_MAX); + + bool + IsHijackedForEvent (uint32_t event_mask) + { + if (m_hijacking_listeners.size() > 0) + return (event_mask & m_hijacking_masks.back()) != 0; + return false; + } + + //------------------------------------------------------------------ + /// Restore the state of the Broadcaster from a previous hijack attempt. + /// + //------------------------------------------------------------------ + void + RestoreBroadcaster (); + + // This needs to be filled in if you are going to register the broadcaster with the broadcaster + // manager and do broadcaster class matching. + // FIXME: Probably should make a ManagedBroadcaster subclass with all the bits needed to work + // with the BroadcasterManager, so that it is clearer how to add one. + virtual ConstString &GetBroadcasterClass() const; + + BroadcasterManager *GetManager(); + +protected: + + + void + PrivateBroadcastEvent (lldb::EventSP &event_sp, bool unique); + + //------------------------------------------------------------------ + // Classes that inherit from Broadcaster can see and modify these + //------------------------------------------------------------------ + typedef std::vector< std::pair > collection; + typedef std::map event_names_map; + // Prefix the name of our member variables with "m_broadcaster_" + // since this is a class that gets subclassed. + const ConstString m_broadcaster_name; ///< The name of this broadcaster object. + event_names_map m_event_names; ///< Optionally define event names for readability and logging for each event bit + collection m_listeners; ///< A list of Listener / event_mask pairs that are listening to this broadcaster. + Mutex m_listeners_mutex; ///< A mutex that protects \a m_listeners. + std::vector m_hijacking_listeners; // A simple mechanism to intercept events from a broadcaster + std::vector m_hijacking_masks; // At some point we may want to have a stack or Listener + // collections, but for now this is just for private hijacking. + BroadcasterManager *m_manager; + +private: + //------------------------------------------------------------------ + // For Broadcaster only + //------------------------------------------------------------------ + DISALLOW_COPY_AND_ASSIGN (Broadcaster); +}; + +} // namespace lldb_private + +#endif // liblldb_Broadcaster_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Core/ClangForward.h b/contrib/llvm/tools/lldb/include/lldb/Core/ClangForward.h new file mode 100644 index 00000000000..0b3f13a1660 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Core/ClangForward.h @@ -0,0 +1,136 @@ +//===-- ClangForward.h ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ClangForward_h_ +#define liblldb_ClangForward_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes + +#if defined(__cplusplus) + +namespace clang +{ + namespace Builtin + { + class Context; + } + + class Action; + class ASTConsumer; + class ASTContext; + class ASTRecordLayout; + class AddrLabelExpr; + class AnalyzerOptions; + class BinaryOperator; + class ClassTemplateDecl; + class ClassTemplateSpecializationDecl; + class CodeGenOptions; + class CodeGenerator; + class CompilerInstance; + class CompoundStmt; + class CXXBaseSpecifier; + class CXXBoolLiteralExpr; + class CXXFunctionalCastExpr; + class CXXMethodDecl; + class CXXNamedCastExpr; + class CXXRecordDecl; + class CXXThisExpr; + class CharacterLiteral; + class CompoundAssignOperator; + class Decl; + class DeclarationName; + class DeclaratorDecl; + class DeclContext; + class DeclRefExpr; + class DeclStmt; + class DependencyOutputOptions; + class Diagnostic; + class DiagnosticConsumer; + class DiagnosticsEngine; + class DiagnosticOptions; + class EnumDecl; + class Expr; + class ExternalASTSource; + class ExtVectorElementExpr; + class FieldDecl; + class FileManager; + class FileSystemOptions; + class FloatingLiteral; + class FrontendOptions; + class FunctionDecl; + class FunctionTemplateDecl; + class FunctionTemplateSpecializationInfo; + class GotoStmt; + class HeaderSearchOptions; + class IdentifierTable; + class IntegerLiteral; + class LabelStmt; + class LangOptions; + class MemberExpr; + class NamedDecl; + class NamespaceDecl; + class NonTypeTemplateParmDecl; + class ObjCEncodeExpr; + class ObjCImplicitSetterGetterRefExpr; + class ObjCInterfaceDecl; + class ObjCIvarDecl; + class ObjCIvarRefExpr; + class ObjCMessageExpr; + class ObjCMethodDecl; + class ObjCPropertyRefExpr; + class ObjCProtocolDecl; + class ObjCProtocolExpr; + class ObjCSelectorExpr; + class ObjCSuperExpr; + class ParenExpr; + class ParmVarDecl; + class PredefinedExpr; + class PreprocessorOptions; + class PreprocessorOutputOptions; + class QualType; + class QualifiedNameType; + class RecordDecl; + class SelectorTable; + class SizeOfAlignOfExpr; + class SourceLocation; + class SourceManager; + class Stmt; + class StmtIteratorBase; + class StringLiteral; + class TagDecl; + class TargetInfo; + class TargetOptions; + class TemplateArgument; + class TemplateDecl; + class TemplateParameterList; + class TemplateTemplateParmDecl; + class TemplateTypeParmDecl; + class TextDiagnosticBuffer; + class TranslationUnitDecl; + class Type; + class TypeDecl; + class TypedefDecl; + class TypesCompatibleExpr; + class UnaryOperator; + class ValueDecl; + class VarDecl; + struct PrintingPolicy; +} + +namespace llvm +{ + class LLVMContext; + class ExecutionEngine; +} + +#endif // #if defined(__cplusplus) +#endif // liblldb_ClangForward_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Core/Communication.h b/contrib/llvm/tools/lldb/include/lldb/Core/Communication.h new file mode 100644 index 00000000000..98d4dfd011b --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Core/Communication.h @@ -0,0 +1,413 @@ +//===-- Communication.h -----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_Communication_h_ +#define liblldb_Communication_h_ + +// C Includes +// C++ Includes +#include + +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private.h" +#include "lldb/Core/Broadcaster.h" +#include "lldb/Core/Error.h" +#include "lldb/Host/Mutex.h" +#include "lldb/lldb-private.h" + +namespace lldb_private { + +//---------------------------------------------------------------------- +/// @class Communication Communication.h "lldb/Core/Communication.h" +/// @brief An abstract communications class. +/// +/// Communication is an class that handles data communication +/// between two data sources. It uses a Connection class to do the +/// real communication. This approach has a couple of advantages: it +/// allows a single instance of this class to be used even though its +/// connection can change. Connections could negotiate for different +/// connections based on abilities like starting with Bluetooth and +/// negotiating up to WiFi if available. It also allows this class to be +/// subclassed by any interfaces that don't want to give bytes but want +/// to validate and give out packets. This can be done by overriding: +/// +/// AppendBytesToCache (const uint8_t *src, size_t src_len, bool broadcast); +/// +/// Communication inherits from Broadcaster which means it can be +/// used in conjunction with Listener to wait for multiple broadcaster +/// objects and multiple events from each of those objects. +/// Communication defines a set of pre-defined event bits (see +/// enumerations definitions that start with "eBroadcastBit" below). +/// +/// There are two modes in which communications can occur: +/// @li single-threaded +/// @li multi-threaded +/// +/// In single-threaded mode, all reads and writes happen synchronously +/// on the calling thread. +/// +/// In multi-threaded mode, a read thread is spawned that continually +/// reads data and caches any received bytes. To start the read thread +/// clients call: +/// +/// bool Communication::StartReadThread (Error *); +/// +/// If true is returned a read thead has been spawned that will +/// continually execute a call to the pure virtual DoRead function: +/// +/// size_t Communication::ReadFromConnection (void *, size_t, uint32_t); +/// +/// When bytes are received the data gets cached in \a m_bytes and this +/// class will broadcast a \b eBroadcastBitReadThreadGotBytes event. +/// Clients that want packet based communication should override +/// AppendBytesToCache. The subclasses can choose to call the +/// built in AppendBytesToCache with the \a broadcast parameter set to +/// false. This will cause the \b eBroadcastBitReadThreadGotBytes event +/// not get broadcast, and then the subclass can post a \b +/// eBroadcastBitPacketAvailable event when a full packet of data has +/// been received. +/// +/// If the connection is disconnected a \b eBroadcastBitDisconnected +/// event gets broadcast. If the read thread exits a \b +/// eBroadcastBitReadThreadDidExit event will be broadcast. Clients +/// can also post a \b eBroadcastBitReadThreadShouldExit event to this +/// object which will cause the read thread to exit. +//---------------------------------------------------------------------- +class Communication : public Broadcaster +{ +public: + enum { + eBroadcastBitDisconnected = (1 << 0), ///< Sent when the communications connection is lost. + eBroadcastBitReadThreadGotBytes = (1 << 1), ///< Sent by the read thread when bytes become available. + eBroadcastBitReadThreadDidExit = (1 << 2), ///< Sent by the read thread when it exits to inform clients. + eBroadcastBitReadThreadShouldExit = (1 << 3), ///< Sent by clients that need to cancel the read thread. + eBroadcastBitPacketAvailable = (1 << 4), ///< Sent when data received makes a complete packet. + kLoUserBroadcastBit = (1 << 16),///< Subclasses can used bits 31:16 for any needed events. + kHiUserBroadcastBit = (1 << 31), + eAllEventBits = 0xffffffff + }; + + typedef void (*ReadThreadBytesReceived) (void *baton, const void *src, size_t src_len); + + + //------------------------------------------------------------------ + /// Construct the Communication object with the specified name for + /// the Broadcaster that this object inherits from. + /// + /// @param[in] broadcaster_name + /// The name of the broadcaster object. This name should be as + /// complete as possible to uniquely identify this object. The + /// broadcaster name can be updated after the connect function + /// is called. + //------------------------------------------------------------------ + Communication(const char * broadcaster_name); + + //------------------------------------------------------------------ + /// Destructor. + /// + /// The destructor is virtual since this class gets subclassed. + //------------------------------------------------------------------ + virtual + ~Communication(); + + void + Clear (); + + //------------------------------------------------------------------ + /// Connect using the current connection by passing \a url to its + /// connect function. + /// string. + /// + /// @param[in] url + /// A string that contains all information needed by the + /// subclass to connect to another client. + /// + /// @return + /// \b True if the connect succeeded, \b false otherwise. The + /// internal error object should be filled in with an + /// appropriate value based on the result of this function. + /// + /// @see Error& Communication::GetError (); + /// @see bool Connection::Connect (const char *url); + //------------------------------------------------------------------ + lldb::ConnectionStatus + Connect (const char *url, Error *error_ptr); + + //------------------------------------------------------------------ + /// Disconnect the communications connection if one is currently + /// connected. + /// + /// @return + /// \b True if the disconnect succeeded, \b false otherwise. The + /// internal error object should be filled in with an + /// appropriate value based on the result of this function. + /// + /// @see Error& Communication::GetError (); + /// @see bool Connection::Disconnect (); + //------------------------------------------------------------------ + lldb::ConnectionStatus + Disconnect (Error *error_ptr = NULL); + + //------------------------------------------------------------------ + /// Check if the connection is valid. + /// + /// @return + /// \b True if this object is currently connected, \b false + /// otherwise. + //------------------------------------------------------------------ + bool + IsConnected () const; + + bool + HasConnection () const; + + lldb_private::Connection * + GetConnection () + { + return m_connection_sp.get(); + } + //------------------------------------------------------------------ + /// Read bytes from the current connection. + /// + /// If no read thread is running, this function call the + /// connection's Connection::Read(...) function to get any available. + /// + /// If a read thread has been started, this function will check for + /// any cached bytes that have already been read and return any + /// currently available bytes. If no bytes are cached, it will wait + /// for the bytes to become available by listening for the \a + /// eBroadcastBitReadThreadGotBytes event. If this function consumes + /// all of the bytes in the cache, it will reset the + /// \a eBroadcastBitReadThreadGotBytes event bit. + /// + /// @param[in] dst + /// A destination buffer that must be at least \a dst_len bytes + /// long. + /// + /// @param[in] dst_len + /// The number of bytes to attempt to read, and also the max + /// number of bytes that can be placed into \a dst. + /// + /// @param[in] timeout_usec + /// A timeout value in micro-seconds. + /// + /// @return + /// The number of bytes actually read. + /// + /// @see size_t Connection::Read (void *, size_t); + //------------------------------------------------------------------ + size_t + Read (void *dst, + size_t dst_len, + uint32_t timeout_usec, + lldb::ConnectionStatus &status, + Error *error_ptr); + + //------------------------------------------------------------------ + /// The actual write function that attempts to write to the + /// communications protocol. + /// + /// Subclasses must override this function. + /// + /// @param[in] src + /// A source buffer that must be at least \a src_len bytes + /// long. + /// + /// @param[in] src_len + /// The number of bytes to attempt to write, and also the + /// number of bytes are currently available in \a src. + /// + /// @return + /// The number of bytes actually Written. + //------------------------------------------------------------------ + size_t + Write (const void *src, + size_t src_len, + lldb::ConnectionStatus &status, + Error *error_ptr); + + //------------------------------------------------------------------ + /// Sets the connection that it to be used by this class. + /// + /// By making a communication class that uses different connections + /// it allows a single communication interface to negotiate and + /// change its connection without any interruption to the client. + /// It also allows the Communication class to be subclassed for + /// packet based communication. + /// + /// @param[in] connection + /// A connection that this class will own and destroy. + /// + /// @see + /// class Connection + //------------------------------------------------------------------ + void + SetConnection (Connection *connection); + + //------------------------------------------------------------------ + /// Starts a read thread whose sole purpose it to read bytes from + /// the current connection. This function will call connection's + /// read function: + /// + /// size_t Connection::Read (void *, size_t); + /// + /// When bytes are read and cached, this function will call: + /// + /// Communication::AppendBytesToCache (const uint8_t * bytes, size_t len, bool broadcast); + /// + /// Subclasses should override this function if they wish to override + /// the default action of caching the bytes and broadcasting a \b + /// eBroadcastBitReadThreadGotBytes event. + /// + /// @return + /// \b True if the read thread was successfully started, \b + /// false otherwise. + /// + /// @see size_t Connection::Read (void *, size_t); + /// @see void Communication::AppendBytesToCache (const uint8_t * bytes, size_t len, bool broadcast); + //------------------------------------------------------------------ + virtual bool + StartReadThread (Error *error_ptr = NULL); + + //------------------------------------------------------------------ + /// Stops the read thread by cancelling it. + /// + /// @return + /// \b True if the read thread was successfully canceled, \b + /// false otherwise. + //------------------------------------------------------------------ + virtual bool + StopReadThread (Error *error_ptr = NULL); + + //------------------------------------------------------------------ + /// Checks if there is a currently running read thread. + /// + /// @return + /// \b True if the read thread is running, \b false otherwise. + //------------------------------------------------------------------ + bool + ReadThreadIsRunning (); + + //------------------------------------------------------------------ + /// The static read thread function. This function will call + /// the "DoRead" function continuously and wait for data to become + /// avaialble. When data is received it will append the available + /// data to the internal cache and broadcast a + /// \b eBroadcastBitReadThreadGotBytes event. + /// + /// @param[in] comm_ptr + /// A pointer to an instance of this class. + /// + /// @return + /// \b NULL. + /// + /// @see void Communication::ReadThreadGotBytes (const uint8_t *, size_t); + //------------------------------------------------------------------ + static lldb::thread_result_t + ReadThread (lldb::thread_arg_t comm_ptr); + + void + SetReadThreadBytesReceivedCallback (ReadThreadBytesReceived callback, + void *callback_baton); + + static const char * + ConnectionStatusAsCString (lldb::ConnectionStatus status); + + bool + GetCloseOnEOF () const + { + return m_close_on_eof; + } + + void + SetCloseOnEOF (bool b) + { + m_close_on_eof = b; + } + + static ConstString &GetStaticBroadcasterClass (); + + virtual ConstString &GetBroadcasterClass() const + { + return GetStaticBroadcasterClass(); + } + +private: + //------------------------------------------------------------------ + // For Communication only + //------------------------------------------------------------------ + DISALLOW_COPY_AND_ASSIGN (Communication); + + +protected: + lldb::ConnectionSP m_connection_sp; ///< The connection that is current in use by this communications class. + lldb::thread_t m_read_thread; ///< The read thread handle in case we need to cancel the thread. + bool m_read_thread_enabled; + std::string m_bytes; ///< A buffer to cache bytes read in the ReadThread function. + Mutex m_bytes_mutex; ///< A mutex to protect multi-threaded access to the cached bytes. + Mutex m_write_mutex; ///< Don't let multiple threads write at the same time... + ReadThreadBytesReceived m_callback; + void *m_callback_baton; + bool m_close_on_eof; + + size_t + ReadFromConnection (void *dst, + size_t dst_len, + uint32_t timeout_usec, + lldb::ConnectionStatus &status, + Error *error_ptr); + //------------------------------------------------------------------ + /// Append new bytes that get read from the read thread into the + /// internal object byte cache. This will cause a \b + /// eBroadcastBitReadThreadGotBytes event to be broadcast if \a + /// broadcast is true. + /// + /// Subclasses can override this function in order to inspect the + /// received data and check if a packet is available. + /// + /// Subclasses can also still call this function from the + /// overridden method to allow the caching to correctly happen and + /// suppress the broadcasting of the \a eBroadcastBitReadThreadGotBytes + /// event by setting \a broadcast to false. + /// + /// @param[in] src + /// A source buffer that must be at least \a src_len bytes + /// long. + /// + /// @param[in] src_len + /// The number of bytes to append to the cache. + //------------------------------------------------------------------ + virtual void + AppendBytesToCache (const uint8_t *src, size_t src_len, bool broadcast, lldb::ConnectionStatus status); + + //------------------------------------------------------------------ + /// Get any available bytes from our data cache. If this call + /// empties the data cache, the \b eBroadcastBitReadThreadGotBytes event + /// will be reset to signify no more bytes are available. + /// + /// @param[in] dst + /// A destination buffer that must be at least \a dst_len bytes + /// long. + /// + /// @param[in] dst_len + /// The number of bytes to attempt to read from the cache, + /// and also the max number of bytes that can be placed into + /// \a dst. + /// + /// @return + /// The number of bytes extracted from the data cache. + //------------------------------------------------------------------ + size_t + GetCachedBytes (void *dst, size_t dst_len); +}; + +} // namespace lldb_private + +#endif // liblldb_Communication_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Core/Connection.h b/contrib/llvm/tools/lldb/include/lldb/Core/Connection.h new file mode 100644 index 00000000000..dde3c4b1022 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Core/Connection.h @@ -0,0 +1,162 @@ +//===-- Connection.h --------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_Connection_h_ +#define liblldb_Connection_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private.h" + +namespace lldb_private { + +//---------------------------------------------------------------------- +/// @class Connection Connection.h "lldb/Core/Connection.h" +/// @brief A communication connection class. +/// +/// A class that implements that actual communication functions for +/// connecting/disconnecting, reading/writing, and waiting for bytes +/// to become available from a two way communication connection. +/// +/// This class is designed to only do very simple communication +/// functions. Instances can be instantiated and given to a +/// Communication class to perform communications where clients can +/// listen for broadcasts, and perform other higher level communications. +//---------------------------------------------------------------------- +class Connection +{ +public: + //------------------------------------------------------------------ + /// Default constructor + //------------------------------------------------------------------ + Connection (); + + //------------------------------------------------------------------ + /// Virtual destructor since this class gets subclassed and handed + /// to a Communication object. + //------------------------------------------------------------------ + virtual + ~Connection (); + + //------------------------------------------------------------------ + /// Connect using the connect string \a url. + /// + /// @param[in] url + /// A string that contains all information needed by the + /// subclass to connect to another client. + /// + /// @param[out] error_ptr + /// A pointer to an error object that should be given an + /// approriate error value if this method returns false. This + /// value can be NULL if the error value should be ignored. + /// + /// @return + /// \b True if the connect succeeded, \b false otherwise. The + /// internal error object should be filled in with an + /// appropriate value based on the result of this function. + /// + /// @see Error& Communication::GetError (); + //------------------------------------------------------------------ + virtual lldb::ConnectionStatus + Connect (const char *url, Error *error_ptr) = 0; + + //------------------------------------------------------------------ + /// Disconnect the communications connection if one is currently + /// connected. + /// + /// @param[out] error_ptr + /// A pointer to an error object that should be given an + /// approriate error value if this method returns false. This + /// value can be NULL if the error value should be ignored. + /// + /// @return + /// \b True if the disconnect succeeded, \b false otherwise. The + /// internal error object should be filled in with an + /// appropriate value based on the result of this function. + /// + /// @see Error& Communication::GetError (); + //------------------------------------------------------------------ + virtual lldb::ConnectionStatus + Disconnect (Error *error_ptr) = 0; + + //------------------------------------------------------------------ + /// Check if the connection is valid. + /// + /// @return + /// \b True if this object is currently connected, \b false + /// otherwise. + //------------------------------------------------------------------ + virtual bool + IsConnected () const = 0; + + //------------------------------------------------------------------ + /// The read function that attempts to read from the connection. + /// + /// @param[in] dst + /// A destination buffer that must be at least \a dst_len bytes + /// long. + /// + /// @param[in] dst_len + /// The number of bytes to attempt to read, and also the max + /// number of bytes that can be placed into \a dst. + /// + /// @param[out] error_ptr + /// A pointer to an error object that should be given an + /// approriate error value if this method returns zero. This + /// value can be NULL if the error value should be ignored. + /// + /// @return + /// The number of bytes actually read. + /// + /// @see size_t Communication::Read (void *, size_t, uint32_t); + //------------------------------------------------------------------ + virtual size_t + Read (void *dst, + size_t dst_len, + uint32_t timeout_usec, + lldb::ConnectionStatus &status, + Error *error_ptr) = 0; + + //------------------------------------------------------------------ + /// The actual write function that attempts to write to the + /// communications protocol. + /// + /// Subclasses must override this function. + /// + /// @param[in] src + /// A source buffer that must be at least \a src_len bytes + /// long. + /// + /// @param[in] src_len + /// The number of bytes to attempt to write, and also the + /// number of bytes are currently available in \a src. + /// + /// @param[out] error_ptr + /// A pointer to an error object that should be given an + /// approriate error value if this method returns zero. This + /// value can be NULL if the error value should be ignored. + /// + /// @return + /// The number of bytes actually Written. + //------------------------------------------------------------------ + virtual size_t + Write (const void *buffer, size_t length, lldb::ConnectionStatus &status, Error *error_ptr) = 0; + +private: + //------------------------------------------------------------------ + // For Connection only + //------------------------------------------------------------------ + DISALLOW_COPY_AND_ASSIGN (Connection); +}; + +} // namespace lldb_private + +#endif // liblldb_Connection_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Core/ConnectionFileDescriptor.h b/contrib/llvm/tools/lldb/include/lldb/Core/ConnectionFileDescriptor.h new file mode 100644 index 00000000000..fe704d4cadf --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Core/ConnectionFileDescriptor.h @@ -0,0 +1,139 @@ +//===-- ConnectionFileDescriptor.h ------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ConnectionFileDescriptor_h_ +#define liblldb_ConnectionFileDescriptor_h_ + +// C Includes +#include +#include +#include + +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/Connection.h" +#include "lldb/Host/Mutex.h" +#include "lldb/Host/Predicate.h" +#include "lldb/Host/SocketAddress.h" + +namespace lldb_private { + +class ConnectionFileDescriptor : + public Connection +{ +public: + + ConnectionFileDescriptor (); + + ConnectionFileDescriptor (int fd, bool owns_fd); + + virtual + ~ConnectionFileDescriptor (); + + virtual bool + IsConnected () const; + + virtual lldb::ConnectionStatus + Connect (const char *s, Error *error_ptr); + + virtual lldb::ConnectionStatus + Disconnect (Error *error_ptr); + + virtual size_t + Read (void *dst, + size_t dst_len, + uint32_t timeout_usec, + lldb::ConnectionStatus &status, + Error *error_ptr); + + virtual size_t + Write (const void *src, + size_t src_len, + lldb::ConnectionStatus &status, + Error *error_ptr); + + // If the read file descriptor is a socket, then return + // the port number that is being used by the socket. + in_port_t + GetReadPort () const; + + // If the write file descriptor is a socket, then return + // the port number that is being used by the socket. + in_port_t + GetWritePort () const; + +protected: + + void + OpenCommandPipe (); + + void + CloseCommandPipe (); + + lldb::ConnectionStatus + BytesAvailable (uint32_t timeout_usec, Error *error_ptr); + + lldb::ConnectionStatus + SocketListen (uint16_t listen_port_num, Error *error_ptr); + + lldb::ConnectionStatus + ConnectTCP (const char *host_and_port, Error *error_ptr); + + lldb::ConnectionStatus + ConnectUDP (const char *args, Error *error_ptr); + + lldb::ConnectionStatus + NamedSocketAccept (const char *socket_name, Error *error_ptr); + + lldb::ConnectionStatus + NamedSocketConnect (const char *socket_name, Error *error_ptr); + + lldb::ConnectionStatus + Close (int& fd, Error *error); + + typedef enum + { + eFDTypeFile, // Other FD requireing read/write + eFDTypeSocket, // Socket requiring send/recv + eFDTypeSocketUDP // Unconnected UDP socket requiring sendto/recvfrom + } FDType; + + int m_fd_send; + int m_fd_recv; + FDType m_fd_send_type; + FDType m_fd_recv_type; + SocketAddress m_udp_send_sockaddr; + bool m_should_close_fd; // True if this class should close the file descriptor when it goes away. + uint32_t m_socket_timeout_usec; + int m_pipe_read; // A pipe that we select on the reading end of along with + int m_pipe_write; // m_fd_recv so we can force ourselves out of the select. + Mutex m_mutex; + bool m_shutting_down; // This marks that we are shutting down so if we get woken up from BytesAvailable + // to disconnect, we won't try to read again. + + static in_port_t + GetSocketPort (int fd); + + static int + GetSocketOption(int fd, int level, int option_name, int &option_value); + + static int + SetSocketOption(int fd, int level, int option_name, int option_value); + + bool + SetSocketReceiveTimeout (uint32_t timeout_usec); + +private: + DISALLOW_COPY_AND_ASSIGN (ConnectionFileDescriptor); +}; + +} // namespace lldb_private + +#endif // liblldb_ConnectionFileDescriptor_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Core/ConnectionMachPort.h b/contrib/llvm/tools/lldb/include/lldb/Core/ConnectionMachPort.h new file mode 100644 index 00000000000..5613e7ee800 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Core/ConnectionMachPort.h @@ -0,0 +1,92 @@ +//===-- ConnectionMachPort.h --------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +#if defined(__APPLE__) + +#ifndef liblldb_ConnectionMachPort_h_ +#define liblldb_ConnectionMachPort_h_ + +// C Includes +#include + +// C++ Includes +#include + +// Other libraries and framework includes +// Project includes +#include "lldb/Core/Connection.h" + +class ConnectionMachPort : + public lldb_private::Connection +{ +public: + ConnectionMachPort (); + + virtual + ~ConnectionMachPort (); + + virtual bool + IsConnected () const; + + virtual lldb::ConnectionStatus + BytesAvailable (uint32_t timeout_usec, lldb_private::Error *error_ptr); + + virtual lldb::ConnectionStatus + Connect (const char *s, lldb_private::Error *error_ptr); + + virtual lldb::ConnectionStatus + Disconnect (lldb_private::Error *error_ptr); + + virtual size_t + Read (void *dst, + size_t dst_len, + uint32_t timeout_usec, + lldb::ConnectionStatus &status, + lldb_private::Error *error_ptr); + + virtual size_t + Write (const void *src, + size_t src_len, + lldb::ConnectionStatus &status, + lldb_private::Error *error_ptr); + + lldb::ConnectionStatus + BootstrapCheckIn (const char *port_name, + lldb_private::Error *error_ptr); + + lldb::ConnectionStatus + BootstrapLookup (const char *port_name, + lldb_private::Error *error_ptr); + + struct PayloadType + { + uint32_t command; + uint32_t data_length; + uint8_t data[32]; + }; + + kern_return_t + Send (const PayloadType &payload); + + kern_return_t + Receive (PayloadType &payload); + + +protected: + mach_port_t m_task; + mach_port_t m_port; + +private: + + + DISALLOW_COPY_AND_ASSIGN (ConnectionMachPort); +}; + +#endif // liblldb_ConnectionMachPort_h_ + +#endif // #if defined(__APPLE__) diff --git a/contrib/llvm/tools/lldb/include/lldb/Core/ConnectionSharedMemory.h b/contrib/llvm/tools/lldb/include/lldb/Core/ConnectionSharedMemory.h new file mode 100644 index 00000000000..0f9cdcb8a92 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Core/ConnectionSharedMemory.h @@ -0,0 +1,70 @@ +//===-- ConnectionSharedMemory.h --------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ConnectionSharedMemory_h_ +#define liblldb_ConnectionSharedMemory_h_ + +// C Includes +// C++ Includes +#include + +// Other libraries and framework includes +// Project includes +#include "lldb/Core/Connection.h" +#include "lldb/Core/DataBufferMemoryMap.h" + +namespace lldb_private { + +class ConnectionSharedMemory : + public Connection +{ +public: + + ConnectionSharedMemory (); + + virtual + ~ConnectionSharedMemory (); + + virtual bool + IsConnected () const; + + virtual lldb::ConnectionStatus + BytesAvailable (uint32_t timeout_usec, Error *error_ptr); + + virtual lldb::ConnectionStatus + Connect (const char *s, Error *error_ptr); + + virtual lldb::ConnectionStatus + Disconnect (Error *error_ptr); + + virtual size_t + Read (void *dst, + size_t dst_len, + uint32_t timeout_usec, + lldb::ConnectionStatus &status, + Error *error_ptr); + + virtual size_t + Write (const void *src, size_t src_len, lldb::ConnectionStatus &status, Error *error_ptr); + + lldb::ConnectionStatus + Open (bool create, const char *name, size_t size, Error *error_ptr); + +protected: + + std::string m_name; + int m_fd; // One buffer that contains all we need + DataBufferMemoryMap m_mmap; +private: + DISALLOW_COPY_AND_ASSIGN (ConnectionSharedMemory); +}; + +} // namespace lldb_private + +#endif // liblldb_ConnectionSharedMemory_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Core/ConstString.h b/contrib/llvm/tools/lldb/include/lldb/Core/ConstString.h new file mode 100644 index 00000000000..e692d3b96e5 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Core/ConstString.h @@ -0,0 +1,507 @@ +//===-- ConstString.h -------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ConstString_h_ +#define liblldb_ConstString_h_ +#if defined(__cplusplus) + +#include + +#include "lldb/lldb-private.h" +#include "llvm/ADT/StringRef.h" + + +namespace lldb_private { + +//---------------------------------------------------------------------- +/// @class ConstString ConstString.h "lldb/Core/ConstString.h" +/// @brief A uniqued constant string class. +/// +/// Provides an efficient way to store strings as uniqued strings. After +/// the strings are uniqued, finding strings that are equal to one +/// another is very fast as just the pointers need to be compared. It +/// also allows for many common strings from many different sources to +/// be shared to keep the memory footprint low. +/// +/// No reference counting is done on strings that are added to the +/// string pool, once strings are added they are in the string pool for +/// the life of the program. +//---------------------------------------------------------------------- +class ConstString +{ +public: + //------------------------------------------------------------------ + /// Default constructor + /// + /// Initializes the string to an empty string. + //------------------------------------------------------------------ + ConstString (): + m_string (NULL) + { + } + + + //------------------------------------------------------------------ + /// Copy constructor + /// + /// Copies the string value in \a rhs into this object. + /// + /// @param[in] rhs + /// Another string object to copy. + //------------------------------------------------------------------ + ConstString (const ConstString& rhs) : + m_string (rhs.m_string) + { + } + + explicit ConstString (const llvm::StringRef &s); + + //------------------------------------------------------------------ + /// Construct with C String value + /// + /// Constructs this object with a C string by looking to see if the + /// C string already exists in the global string pool. If it doesn't + /// exist, it is added to the string pool. + /// + /// @param[in] cstr + /// A NULL terminated C string to add to the string pool. + //------------------------------------------------------------------ + explicit ConstString (const char *cstr); + + //------------------------------------------------------------------ + /// Construct with C String value with max length + /// + /// Constructs this object with a C string with a length. If + /// \a max_cstr_len is greater than the actual length of the string, + /// the string length will be truncated. This allows substrings to + /// be created without the need to NULL terminate the string as it + /// is passed into this function. + /// + /// @param[in] cstr + /// A pointer to the first character in the C string. The C + /// string can be NULL terminated in a buffer that contains + /// more characters than the length of the stirng, or the + /// string can be part of another string and a new substring + /// can be created. + /// + /// @param[in] max_cstr_len + /// The max length of \a cstr. If the string length of \a cstr + /// is less than \a max_cstr_len, then the string will be + /// truncated. If the string length of \a cstr is greater than + /// \a max_cstr_len, then only max_cstr_len bytes will be used + /// from \a cstr. + //------------------------------------------------------------------ + explicit ConstString (const char *cstr, size_t max_cstr_len); + + //------------------------------------------------------------------ + /// Destructor + /// + /// Since constant string values are currently not reference counted, + /// there isn't much to do here. + //------------------------------------------------------------------ + ~ConstString () + { + } + + + //---------------------------------------------------------------------- + /// C string equality binary predicate function object for ConstString + /// objects. + //---------------------------------------------------------------------- + struct StringIsEqual + { + //-------------------------------------------------------------- + /// C equality test. + /// + /// Two C strings are equal when they are contained in ConstString + /// objects when their pointer values are equal to each other. + /// + /// @return + /// Returns \b true if the C string in \a lhs is equal to + /// the C string value in \a rhs, \b false otherwise. + //-------------------------------------------------------------- + bool operator()(const char* lhs, const char* rhs) const + { + return lhs == rhs; + } + }; + + //------------------------------------------------------------------ + /// Convert to bool operator. + /// + /// This allows code to check a ConstString object to see if it + /// contains a valid string using code such as: + /// + /// @code + /// ConstString str(...); + /// if (str) + /// { ... + /// @endcode + /// + /// @return + /// /b True this object contains a valid non-empty C string, \b + /// false otherwise. + //------------------------------------------------------------------ + operator bool() const + { + return m_string && m_string[0]; + } + + //------------------------------------------------------------------ + /// Assignment operator + /// + /// Assigns the string in this object with the value from \a rhs. + /// + /// @param[in] rhs + /// Another string object to copy into this object. + /// + /// @return + /// A const reference to this object. + //------------------------------------------------------------------ + const ConstString& + operator = (const ConstString& rhs) + { + m_string = rhs.m_string; + return *this; + } + + //------------------------------------------------------------------ + /// Equal to operator + /// + /// Returns true if this string is equal to the string in \a rhs. + /// This operation is very fast as it results in a pointer + /// comparison since all strings are in a uniqued in a global string + /// pool. + /// + /// @param[in] rhs + /// Another string object to compare this object to. + /// + /// @return + /// @li \b true if this object is equal to \a rhs. + /// @li \b false if this object is not equal to \a rhs. + //------------------------------------------------------------------ + bool + operator == (const ConstString& rhs) const + { + // We can do a pointer compare to compare these strings since they + // must come from the same pool in order to be equal. + return m_string == rhs.m_string; + } + + //------------------------------------------------------------------ + /// Not equal to operator + /// + /// Returns true if this string is not equal to the string in \a rhs. + /// This operation is very fast as it results in a pointer + /// comparison since all strings are in a uniqued in a global string + /// pool. + /// + /// @param[in] rhs + /// Another string object to compare this object to. + /// + /// @return + /// @li \b true if this object is not equal to \a rhs. + /// @li \b false if this object is equal to \a rhs. + //------------------------------------------------------------------ + bool + operator != (const ConstString& rhs) const + { + return m_string != rhs.m_string; + } + + bool + operator < (const ConstString& rhs) const; + + //------------------------------------------------------------------ + /// Get the string value as a C string. + /// + /// Get the value of the contained string as a NULL terminated C + /// string value. + /// + /// If \a value_if_empty is NULL, then NULL will be returned. + /// + /// @return + /// Returns \a value_if_empty if the string is empty, otherwise + /// the C string value contained in this object. + //------------------------------------------------------------------ + const char * + AsCString(const char *value_if_empty = NULL) const + { + if (m_string == NULL) + return value_if_empty; + return m_string; + } + + //------------------------------------------------------------------ + /// Get the string value as a llvm::StringRef + /// + /// @return + /// Returns a new llvm::StringRef object filled in with the + /// needed data. + //------------------------------------------------------------------ + llvm::StringRef + GetStringRef () const + { + return llvm::StringRef (m_string, GetLength()); + } + + //------------------------------------------------------------------ + /// Get the string value as a C string. + /// + /// Get the value of the contained string as a NULL terminated C + /// string value. Similar to the ConstString::AsCString() function, + /// yet this function will always return NULL if the string is not + /// valid. So this function is a direct accessor to the string + /// pointer value. + /// + /// @return + /// Returns NULL the string is invalid, otherwise the C string + /// value contained in this object. + //------------------------------------------------------------------ + const char * + GetCString () const + { + return m_string; + } + + + //------------------------------------------------------------------ + /// Get the length in bytes of string value. + /// + /// The string pool stores the length of the string, so we can avoid + /// calling strlen() on the pointer value with this function. + /// + /// @return + /// Returns the number of bytes that this string occupies in + /// memory, not including the NULL termination byte. + //------------------------------------------------------------------ + size_t + GetLength () const; + + //------------------------------------------------------------------ + /// Clear this object's state. + /// + /// Clear any contained string and reset the value to the an empty + /// string value. + //------------------------------------------------------------------ + void + Clear () + { + m_string = NULL; + } + + //------------------------------------------------------------------ + /// Compare two string objects. + /// + /// Compares the C string values contained in \a lhs and \a rhs and + /// returns an integer result. + /// + /// NOTE: only call this function when you want a true string + /// comparision. If you want string equality use the, use the == + /// operator as it is much more efficient. Also if you want string + /// inequality, use the != operator for the same reasons. + /// + /// @param[in] lhs + /// The Left Hand Side const ConstString object reference. + /// + /// @param[in] rhs + /// The Right Hand Side const ConstString object reference. + /// + /// @return + /// @li -1 if lhs < rhs + /// @li 0 if lhs == rhs + /// @li 1 if lhs > rhs + //------------------------------------------------------------------ + static int + Compare (const ConstString& lhs, const ConstString& rhs); + + //------------------------------------------------------------------ + /// Dump the object description to a stream. + /// + /// Dump the string value to the stream \a s. If the contained string + /// is empty, print \a value_if_empty to the stream instead. If + /// \a value_if_empty is NULL, then nothing will be dumped to the + /// stream. + /// + /// @param[in] s + /// The stream that will be used to dump the object description. + /// + /// @param[in] value_if_empty + /// The value to dump if the string is empty. If NULL, nothing + /// will be output to the stream. + //------------------------------------------------------------------ + void + Dump (Stream *s, const char *value_if_empty = NULL) const; + + //------------------------------------------------------------------ + /// Dump the object debug description to a stream. + /// + /// @param[in] s + /// The stream that will be used to dump the object description. + //------------------------------------------------------------------ + void + DumpDebug (Stream *s) const; + + //------------------------------------------------------------------ + /// Test for empty string. + /// + /// @return + /// @li \b true if the contained string is empty. + /// @li \b false if the contained string is not empty. + //------------------------------------------------------------------ + bool + IsEmpty () const + { + return m_string == NULL || m_string[0] == '\0'; + } + + //------------------------------------------------------------------ + /// Set the C string value. + /// + /// Set the string value in the object by uniquing the \a cstr + /// string value in our global string pool. + /// + /// If the C string already exists in the global string pool, it + /// finds the current entry and returns the existing value. If it + /// doesn't exist, it is added to the string pool. + /// + /// @param[in] cstr + /// A NULL terminated C string to add to the string pool. + //------------------------------------------------------------------ + void + SetCString (const char *cstr); + + void + SetString (const llvm::StringRef &s); + + //------------------------------------------------------------------ + /// Set the C string value and its mangled counterpart. + /// + /// Object files and debug sybmols often use mangled string to + /// represent the linkage name for a symbol, function or global. + /// The string pool can efficiently store these values and their + /// counterparts so when we run into another instance of a mangled + /// name, we can avoid calling the name demangler over and over on + /// the same strings and then trying to unique them. + /// + /// @param[in] demangled + /// The demangled C string to correlate with the \a mangled + /// name. + /// + /// @param[in] mangled + /// The already uniqued mangled ConstString to correlate the + /// soon to be uniqued version of \a demangled. + //------------------------------------------------------------------ + void + SetCStringWithMangledCounterpart (const char *demangled, + const ConstString &mangled); + + //------------------------------------------------------------------ + /// Retrieve the mangled or demangled counterpart for a mangled + /// or demangled ConstString. + /// + /// Object files and debug sybmols often use mangled string to + /// represent the linkage name for a symbol, function or global. + /// The string pool can efficiently store these values and their + /// counterparts so when we run into another instance of a mangled + /// name, we can avoid calling the name demangler over and over on + /// the same strings and then trying to unique them. + /// + /// @param[in] counterpart + /// A reference to a ConstString object that might get filled in + /// with the demangled/mangled counterpart. + /// + /// @return + /// /b True if \a counterpart was filled in with the counterpart + /// /b false otherwise. + //------------------------------------------------------------------ + bool + GetMangledCounterpart (ConstString &counterpart) const; + + //------------------------------------------------------------------ + /// Set the C string value with length. + /// + /// Set the string value in the object by uniquing \a cstr_len bytes + /// starting at the \a cstr string value in our global string pool. + /// If trim is true, then \a cstr_len indicates a maximum length of + /// the CString and if the actual length of the string is less, then + /// it will be trimmed. + /// + /// If the C string already exists in the global string pool, it + /// finds the current entry and returns the existing value. If it + /// doesn't exist, it is added to the string pool. + /// + /// @param[in] cstr + /// A NULL terminated C string to add to the string pool. + /// + /// @param[in] cstr_len + /// The maximum length of the C string. + //------------------------------------------------------------------ + void + SetCStringWithLength (const char *cstr, size_t cstr_len); + + //------------------------------------------------------------------ + /// Set the C string value with the minimum length between + /// \a fixed_cstr_len and the actual length of the C string. This + /// can be used for data structures that have a fixed length to + /// store a C string where the string might not be NULL terminated + /// if the string takes the entire buffer. + //------------------------------------------------------------------ + void + SetTrimmedCStringWithLength (const char *cstr, size_t fixed_cstr_len); + + //------------------------------------------------------------------ + /// Get the memory cost of this object. + /// + /// Return the size in bytes that this object takes in memory. This + /// returns the size in bytes of this object, which does not include + /// any the shared string values it may refer to. + /// + /// @return + /// The number of bytes that this object occupies in memory. + /// + /// @see ConstString::StaticMemorySize () + //------------------------------------------------------------------ + size_t + MemorySize () const + { + return sizeof(ConstString); + } + + + //------------------------------------------------------------------ + /// Get the size in bytes of the current global string pool. + /// + /// Reports the the size in bytes of all shared C string values, + /// containers and any other values as a byte size for the + /// entire string pool. + /// + /// @return + /// The number of bytes that the global string pool occupies + /// in memory. + //------------------------------------------------------------------ + static size_t + StaticMemorySize (); + +protected: + //------------------------------------------------------------------ + // Member variables + //------------------------------------------------------------------ + const char *m_string; +}; + +//------------------------------------------------------------------ +/// Stream the string value \a str to the stream \a s +//------------------------------------------------------------------ +Stream& operator << (Stream& s, const ConstString& str); + +} // namespace lldb_private + +#endif // #if defined(__cplusplus) +#endif // liblldb_ConstString_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Core/DataBuffer.h b/contrib/llvm/tools/lldb/include/lldb/Core/DataBuffer.h new file mode 100644 index 00000000000..e64245dead3 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Core/DataBuffer.h @@ -0,0 +1,94 @@ +//===-- DataBuffer.h --------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_DataBuffer_h_ +#define liblldb_DataBuffer_h_ +#if defined(__cplusplus) + +#include +#include + +namespace lldb_private { + +//---------------------------------------------------------------------- +/// @class DataBuffer DataBuffer.h "lldb/Core/DataBuffer.h" +/// @brief A pure virtual protocol class for abstracted data buffers. +/// +/// DataBuffer is an abtract class that gets packaged into a shared pointer +/// that can use to implement various ways to store data (on the heap, +/// memory mapped, cached inferior memory). It gets used by DataExtractor +/// so many DataExtractor objects can share the same data and sub-ranges +/// of that shared data, and the last object that contains a reference +/// to the shared data will free it. +/// +/// Subclasses can implement as many different constructors or member +/// functions that allow data to be stored in the object's buffer prior +/// to handing the shared data to clients that use these buffers. +/// +/// All subclasses must override all of the pure virtual functions as +/// they are used by clients to access the data. Having a common +/// interface allows different ways of storing data, yet using it in +/// one common way. +/// +/// This class currently expects all data to be available without any +/// extra calls being made, but we can modify it to optionally get +/// data on demand with some extra function calls to load the data +/// before it gets accessed. +//---------------------------------------------------------------------- +class DataBuffer +{ +public: + //------------------------------------------------------------------ + /// Destructor + /// + /// The destructor is virtual as other classes will inherit from + /// this class and be downcast to the DataBuffer pure virtual + /// interface. The virtual destructor ensures that destructing the + /// base class will destruct the class that inherited from it + /// correctly. + //------------------------------------------------------------------ + virtual + ~DataBuffer() + { + } + + //------------------------------------------------------------------ + /// Get a pointer to the data. + /// + /// @return + /// A pointer to the bytes owned by this object, or NULL if the + /// object contains no bytes. + //------------------------------------------------------------------ + virtual uint8_t * + GetBytes () = 0; + + //------------------------------------------------------------------ + /// Get a const pointer to the data. + /// + /// @return + /// A const pointer to the bytes owned by this object, or NULL + /// if the object contains no bytes. + //------------------------------------------------------------------ + virtual const uint8_t * + GetBytes () const = 0; + + //------------------------------------------------------------------ + /// Get the number of bytes in the data buffer. + /// + /// @return + /// The number of bytes this object currently contains. + //------------------------------------------------------------------ + virtual lldb::offset_t + GetByteSize() const = 0; +}; + +} // namespace lldb_private + +#endif /// #if defined(__cplusplus) +#endif /// lldb_DataBuffer_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Core/DataBufferHeap.h b/contrib/llvm/tools/lldb/include/lldb/Core/DataBufferHeap.h new file mode 100644 index 00000000000..dac9a28befb --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Core/DataBufferHeap.h @@ -0,0 +1,139 @@ +//===-- DataBufferHeap.h ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_DataBufferHeap_h_ +#define liblldb_DataBufferHeap_h_ +#if defined(__cplusplus) + +#include + +#include "lldb/lldb-private.h" +#include "lldb/Core/DataBuffer.h" + +namespace lldb_private { + +//---------------------------------------------------------------------- +/// @class DataBufferHeap DataBufferHeap.h "lldb/Core/DataBufferHeap.h" +/// @brief A subclass of DataBuffer that stores a data buffer on the heap. +/// +/// This class keeps its data in a heap based buffer that is owned by +/// the object. This class is best used to store chunks of data that +/// are created or read from sources that can't intelligently and lazily +/// fault new data pages in. Large amounts of data that comes from files +/// should probably use the DataBufferMemoryMap class. +//---------------------------------------------------------------------- +class DataBufferHeap : public DataBuffer +{ +public: + //------------------------------------------------------------------ + /// Default constructor + /// + /// Initializes the heap based buffer with no bytes. + //------------------------------------------------------------------ + DataBufferHeap (); + + //------------------------------------------------------------------ + /// Construct with size \a n and fill with \a ch. + /// + /// Initialize this class with \a n bytes and fills the buffer with + /// \a ch. + /// + /// @param[in] n + /// The number of bytes that heap based buffer should contain. + /// + /// @param[in] ch + /// The character to use when filling the buffer initially. + //------------------------------------------------------------------ + DataBufferHeap (lldb::offset_t n, uint8_t ch); + + //------------------------------------------------------------------ + /// Construct by making a copy of \a src_len bytes from \a src. + /// + /// @param[in] src + /// A pointer to the data to copy. + /// + /// @param[in] src_len + /// The number of bytes in \a src to copy. + //------------------------------------------------------------------ + DataBufferHeap (const void *src, lldb::offset_t src_len); + + //------------------------------------------------------------------ + /// Destructor. + /// + /// Virtual destructor since this class inherits from a pure virtual + /// base class #DataBuffer. + //------------------------------------------------------------------ + virtual + ~DataBufferHeap(); + + //------------------------------------------------------------------ + /// @copydoc DataBuffer::GetBytes() + //------------------------------------------------------------------ + virtual uint8_t * + GetBytes (); + + //------------------------------------------------------------------ + /// @copydoc DataBuffer::GetBytes() const + //------------------------------------------------------------------ + virtual const uint8_t * + GetBytes () const; + + //------------------------------------------------------------------ + /// @copydoc DataBuffer::GetByteSize() const + //------------------------------------------------------------------ + virtual lldb::offset_t + GetByteSize () const; + + //------------------------------------------------------------------ + /// Set the number of bytes in the data buffer. + /// + /// Sets the number of bytes that this object should be able to + /// contain. This can be used prior to copying data into the buffer. + /// + /// @param[in] byte_size + /// The new size in bytes that this data buffer should attempt + /// to resize itself to. + /// + /// @return + /// The size in bytes after that this heap buffer was + /// successfully resized to. + //------------------------------------------------------------------ + lldb::offset_t + SetByteSize (lldb::offset_t byte_size); + + //------------------------------------------------------------------ + /// Makes a copy of the \a src_len bytes in \a src. + /// + /// Copies the data in \a src into an internal buffer. + /// + /// @param[in] src + /// A pointer to the data to copy. + /// + /// @param[in] src_len + /// The number of bytes in \a src to copy. + //------------------------------------------------------------------ + void + CopyData (const void *src, lldb::offset_t src_len); + + void + Clear(); + +private: + //------------------------------------------------------------------ + // This object uses a std::vector to store its data. This + // takes care of free the data when the object is deleted. + //------------------------------------------------------------------ + typedef std::vector buffer_t; ///< Buffer type + buffer_t m_data; ///< The heap based buffer where data is stored +}; + +} // namespace lldb_private + +#endif // #if defined(__cplusplus) +#endif // liblldb_DataBufferHeap_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Core/DataBufferMemoryMap.h b/contrib/llvm/tools/lldb/include/lldb/Core/DataBufferMemoryMap.h new file mode 100644 index 00000000000..d4a448a5df5 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Core/DataBufferMemoryMap.h @@ -0,0 +1,160 @@ +//===-- DataBufferMemoryMap.h -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_DataBufferMemoryMap_h_ +#define liblldb_DataBufferMemoryMap_h_ +#if defined(__cplusplus) + + +#include "lldb/lldb-private.h" +#include "lldb/Core/DataBuffer.h" +#include "lldb/Core/Error.h" +#include + +namespace lldb_private { + +//---------------------------------------------------------------------- +/// @class DataBufferMemoryMap DataBufferMemoryMap.h "lldb/Core/DataBufferMemoryMap.h" +/// @brief A subclass of DataBuffer that memory maps data. +/// +/// This class memory maps data and stores any needed data for the +/// memory mapping in its internal state. Memory map requests are not +/// required to have any alignment or size constraints, this class will +/// work around any host OS issues regarding such things. +/// +/// This class is designed to allow pages to be faulted in as needed and +/// works well data from large files that won't be accessed all at once. +//---------------------------------------------------------------------- +class DataBufferMemoryMap : public DataBuffer +{ +public: + //------------------------------------------------------------------ + /// Default Constructor + //------------------------------------------------------------------ + DataBufferMemoryMap (); + + //------------------------------------------------------------------ + /// Destructor. + /// + /// Virtual destructor since this class inherits from a pure virtual + /// base class #DataBuffer. + //------------------------------------------------------------------ + virtual + ~DataBufferMemoryMap (); + + //------------------------------------------------------------------ + /// Reverts this object to an empty state by unmapping any memory + /// that is currently owned. + //------------------------------------------------------------------ + void + Clear (); + + //------------------------------------------------------------------ + /// @copydoc DataBuffer::GetBytes() + //------------------------------------------------------------------ + virtual uint8_t * + GetBytes (); + + //------------------------------------------------------------------ + /// @copydoc DataBuffer::GetBytes() const + //------------------------------------------------------------------ + virtual const uint8_t * + GetBytes () const; + + //------------------------------------------------------------------ + /// @copydoc DataBuffer::GetByteSize() const + //------------------------------------------------------------------ + virtual lldb::offset_t + GetByteSize () const; + + //------------------------------------------------------------------ + /// Error get accessor. + /// + /// @return + /// A const reference to Error object in case memory mapping + /// fails. + //------------------------------------------------------------------ + const Error & + GetError() const; + + //------------------------------------------------------------------ + /// Memory map all or part of a file. + /// + /// Memory map \a length bytes from \a file starting \a offset + /// bytes into the file. If \a length is set to \c SIZE_MAX, + /// then map as many bytes as possible. + /// + /// @param[in] file + /// The file specification from which to map data. + /// + /// @param[in] offset + /// The offset in bytes from the beginning of the file where + /// memory mapping should begin. + /// + /// @param[in] length + /// The size in bytes that should be mapped starting \a offset + /// bytes into the file. If \a length is \c SIZE_MAX, map + /// as many bytes as possible. + /// + /// @return + /// The number of bytes mapped starting from the \a offset. + //------------------------------------------------------------------ + size_t + MemoryMapFromFileSpec (const FileSpec* file, + lldb::offset_t offset = 0, + lldb::offset_t length = SIZE_MAX, + bool writeable = false); + + //------------------------------------------------------------------ + /// Memory map all or part of a file. + /// + /// Memory map \a length bytes from an opened file descriptor \a fd + /// starting \a offset bytes into the file. If \a length is set to + /// \c SIZE_MAX, then map as many bytes as possible. + /// + /// @param[in] fd + /// The posix file descriptor for an already opened file + /// from which to map data. + /// + /// @param[in] offset + /// The offset in bytes from the beginning of the file where + /// memory mapping should begin. + /// + /// @param[in] length + /// The size in bytes that should be mapped starting \a offset + /// bytes into the file. If \a length is \c SIZE_MAX, map + /// as many bytes as possible. + /// + /// @return + /// The number of bytes mapped starting from the \a offset. + //------------------------------------------------------------------ + size_t + MemoryMapFromFileDescriptor (int fd, + lldb::offset_t offset, + lldb::offset_t length, + bool write, + bool fd_is_file); + +protected: + //------------------------------------------------------------------ + // Classes that inherit from DataBufferMemoryMap can see and modify these + //------------------------------------------------------------------ + uint8_t * m_mmap_addr; ///< The actual pointer that was returned from \c mmap() + size_t m_mmap_size; ///< The actual number of bytes that were mapped when \c mmap() was called + uint8_t *m_data; ///< The data the user requested somewhere within the memory mapped data. + lldb::offset_t m_size; ///< The size of the data the user got when data was requested + +private: + DISALLOW_COPY_AND_ASSIGN (DataBufferMemoryMap); +}; + +} // namespace lldb_private + +#endif // #if defined(__cplusplus) +#endif // liblldb_DataBufferMemoryMap_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Core/DataEncoder.h b/contrib/llvm/tools/lldb/include/lldb/Core/DataEncoder.h new file mode 100644 index 00000000000..658cce0d2b4 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Core/DataEncoder.h @@ -0,0 +1,459 @@ +//===-- DataEncoder.h -------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_DataEncoder_h_ +#define liblldb_DataEncoder_h_ + +#if defined (__cplusplus) + +#include "lldb/lldb-private.h" +#include +#include + +namespace lldb_private { + +//---------------------------------------------------------------------- +/// @class DataEncoder DataEncoder.h "lldb/Core/DataEncoder.h" +/// @brief An binary data encoding class. +/// +/// DataEncoder is a class that can encode binary data (swapping if needed) +/// to a data buffer. The data buffer can be caller owned, or can be +/// shared data that can be shared between multiple DataEncoder or +/// DataEncoder instances. +/// +/// @see DataBuffer +//---------------------------------------------------------------------- +class DataEncoder +{ +public: + //------------------------------------------------------------------ + /// Default constructor. + /// + /// Initialize all members to a default empty state. + //------------------------------------------------------------------ + DataEncoder (); + + //------------------------------------------------------------------ + /// Construct with a buffer that is owned by the caller. + /// + /// This constructor allows us to use data that is owned by the + /// caller. The data must stay around as long as this object is + /// valid. + /// + /// @param[in] data + /// A pointer to caller owned data. + /// + /// @param[in] data_length + /// The length in bytes of \a data. + /// + /// @param[in] byte_order + /// A byte order of the data that we are extracting from. + /// + /// @param[in] addr_size + /// A new address byte size value. + //------------------------------------------------------------------ + DataEncoder (void* data, uint32_t data_length, lldb::ByteOrder byte_order, uint8_t addr_size); + + //------------------------------------------------------------------ + /// Construct with shared data. + /// + /// Copies the data shared pointer which adds a reference to the + /// contained in \a data_sp. The shared data reference is reference + /// counted to ensure the data lives as long as anyone still has a + /// valid shared pointer to the data in \a data_sp. + /// + /// @param[in] data_sp + /// A shared pointer to data. + /// + /// @param[in] byte_order + /// A byte order of the data that we are extracting from. + /// + /// @param[in] addr_size + /// A new address byte size value. + //------------------------------------------------------------------ + DataEncoder (const lldb::DataBufferSP& data_sp, lldb::ByteOrder byte_order, uint8_t addr_size); + + //------------------------------------------------------------------ + /// Destructor + /// + /// If this object contains a valid shared data reference, the + /// reference count on the data will be decremented, and if zero, + /// the data will be freed. + //------------------------------------------------------------------ + ~DataEncoder (); + + //------------------------------------------------------------------ + /// Clears the object state. + /// + /// Clears the object contents back to a default invalid state, and + /// release any references to shared data that this object may + /// contain. + //------------------------------------------------------------------ + void + Clear (); + + //------------------------------------------------------------------ + /// Get the current address size. + /// + /// Return the size in bytes of any address values this object will + /// extract. + /// + /// @return + /// The size in bytes of address values that will be extracted. + //------------------------------------------------------------------ + uint8_t + GetAddressByteSize () const + { + return m_addr_size; + } + + + //------------------------------------------------------------------ + /// Get the number of bytes contained in this object. + /// + /// @return + /// The total number of bytes of data this object refers to. + //------------------------------------------------------------------ + size_t + GetByteSize () const + { + return m_end - m_start; + } + + //------------------------------------------------------------------ + /// Get the data end pointer. + /// + /// @return + /// Returns a pointer to the next byte contained in this + /// object's data, or NULL of there is no data in this object. + //------------------------------------------------------------------ + uint8_t * + GetDataEnd () + { + return m_end; + } + + const uint8_t * + GetDataEnd () const + { + return m_end; + } + + //------------------------------------------------------------------ + /// Get the shared data offset. + /// + /// Get the offset of the first byte of data in the shared data (if + /// any). + /// + /// @return + /// If this object contains shared data, this function returns + /// the offset in bytes into that shared data, zero otherwise. + //------------------------------------------------------------------ + size_t + GetSharedDataOffset () const; + + + //------------------------------------------------------------------ + /// Get the current byte order value. + /// + /// @return + /// The current byte order value from this object's internal + /// state. + //------------------------------------------------------------------ + lldb::ByteOrder + GetByteOrder() const + { + return m_byte_order; + } + + //------------------------------------------------------------------ + /// Get a the data start pointer. + /// + /// @return + /// Returns a pointer to the first byte contained in this + /// object's data, or NULL of there is no data in this object. + //------------------------------------------------------------------ + uint8_t * + GetDataStart () + { + return m_start; + } + + const uint8_t * + GetDataStart () const + { + return m_start; + } + + //------------------------------------------------------------------ + /// Encode unsigned integer values into the data at \a offset. + /// + /// @param[in] offset + /// The offset within the contained data at which to put the + /// data. + /// + /// @param[in] value + /// The value to encode into the data. + /// + /// @return + /// The next offset in the bytes of this data if the data + /// was successfully encoded, UINT32_MAX if the encoding failed. + //------------------------------------------------------------------ + uint32_t + PutU8 (uint32_t offset, uint8_t value); + + uint32_t + PutU16 (uint32_t offset, uint16_t value); + + uint32_t + PutU32 (uint32_t offset, uint32_t value); + + uint32_t + PutU64 (uint32_t offset, uint64_t value); + + //------------------------------------------------------------------ + /// Encode an unsigned integer of size \a byte_size to \a offset. + /// + /// Encode a single integer value at \a offset and return the offset + /// that follows the newly encoded integer when the data is successfully + /// encoded into the existing data. There must be enough room in the + /// data, else UINT32_MAX will be returned to indicate that encoding + /// failed. + /// + /// @param[in] offset + /// The offset within the contained data at which to put the + /// encoded integer. + /// + /// @param[in] byte_size + /// The size in byte of the integer to encode. + /// + /// @param[in] value + /// The integer value to write. The least significate bytes of + /// the integer value will be written if the size is less than + /// 8 bytes. + /// + /// @return + /// The next offset in the bytes of this data if the integer + /// was successfully encoded, UINT32_MAX if the encoding failed. + //------------------------------------------------------------------ + uint32_t + PutMaxU64 (uint32_t offset, uint32_t byte_size, uint64_t value); + + //------------------------------------------------------------------ + /// Encode an arbitrary number of bytes. + /// + /// @param[in] offset + /// The offset in bytes into the contained data at which to + /// start encoding. + /// + /// @param[int] src + /// The buffer that contains the the bytes to encode. + /// + /// @param[in] src_len + /// The number of bytes to encode. + /// + /// @return + /// The next valid offset within data if the put operation + /// was successful, else UINT32_MAX to indicate the put failed. + //------------------------------------------------------------------ + uint32_t + PutData (uint32_t offset, + const void *src, + uint32_t src_len); + + //------------------------------------------------------------------ + /// Encode an address in the existing buffer at \a offset bytes into + /// the buffer. + /// + /// Encode a single address (honoring the m_addr_size member) to + /// the data and return the next offset where subsequent data would + /// go. + /// pointed to by \a offset_ptr. The size of the extracted address + /// comes from the \a m_addr_size member variable and should be + /// set correctly prior to extracting any address values. + /// + /// @param[in,out] offset_ptr + /// A pointer to an offset within the data that will be advanced + /// by the appropriate number of bytes if the value is extracted + /// correctly. If the offset is out of bounds or there are not + /// enough bytes to extract this value, the offset will be left + /// unmodified. + /// + /// @return + /// The next valid offset within data if the put operation + /// was successful, else UINT32_MAX to indicate the put failed. + //------------------------------------------------------------------ + uint32_t + PutAddress (uint32_t offset, lldb::addr_t addr); + + //------------------------------------------------------------------ + /// Put a C string to \a offset. + /// + /// Encodes a C string into the existing data including the + /// terminating + /// + /// @param[in,out] offset_ptr + /// A pointer to an offset within the data that will be advanced + /// by the appropriate number of bytes if the value is extracted + /// correctly. If the offset is out of bounds or there are not + /// enough bytes to extract this value, the offset will be left + /// unmodified. + /// + /// @return + /// A pointer to the C string value in the data. If the offset + /// pointed to by \a offset_ptr is out of bounds, or if the + /// offset plus the length of the C string is out of bounds, + /// NULL will be returned. + //------------------------------------------------------------------ + uint32_t + PutCString (uint32_t offset_ptr, const char *cstr); + + lldb::DataBufferSP & + GetSharedDataBuffer () + { + return m_data_sp; + } + + //------------------------------------------------------------------ + /// Set the address byte size. + /// + /// Set the size in bytes that will be used when extracting any + /// address and pointer values from data contained in this object. + /// + /// @param[in] addr_size + /// The size in bytes to use when extracting addresses. + //------------------------------------------------------------------ + void + SetAddressByteSize (uint8_t addr_size) + { + m_addr_size = addr_size; + } + + //------------------------------------------------------------------ + /// Set data with a buffer that is caller owned. + /// + /// Use data that is owned by the caller when extracting values. + /// The data must stay around as long as this object, or any object + /// that copies a subset of this object's data, is valid. If \a + /// bytes is NULL, or \a length is zero, this object will contain + /// no data. + /// + /// @param[in] bytes + /// A pointer to caller owned data. + /// + /// @param[in] length + /// The length in bytes of \a bytes. + /// + /// @param[in] byte_order + /// A byte order of the data that we are extracting from. + /// + /// @return + /// The number of bytes that this object now contains. + //------------------------------------------------------------------ + uint32_t + SetData (const void *bytes, uint32_t length, lldb::ByteOrder byte_order); + + //------------------------------------------------------------------ + /// Adopt a subset of shared data in \a data_sp. + /// + /// Copies the data shared pointer which adds a reference to the + /// contained in \a data_sp. The shared data reference is reference + /// counted to ensure the data lives as long as anyone still has a + /// valid shared pointer to the data in \a data_sp. The byte order + /// and address byte size settings remain the same. If + /// \a offset is not a valid offset in \a data_sp, then no reference + /// to the shared data will be added. If there are not \a length + /// bytes available in \a data starting at \a offset, the length + /// will be truncated to contains as many bytes as possible. + /// + /// @param[in] data_sp + /// A shared pointer to data. + /// + /// @param[in] offset + /// The offset into \a data_sp at which the subset starts. + /// + /// @param[in] length + /// The length in bytes of the subset of \a data_sp. + /// + /// @return + /// The number of bytes that this object now contains. + //------------------------------------------------------------------ + uint32_t + SetData (const lldb::DataBufferSP& data_sp, uint32_t offset = 0, uint32_t length = UINT32_MAX); + + //------------------------------------------------------------------ + /// Set the byte_order value. + /// + /// Sets the byte order of the data to extract. Extracted values + /// will be swapped if necessary when decoding. + /// + /// @param[in] byte_order + /// The byte order value to use when extracting data. + //------------------------------------------------------------------ + void + SetByteOrder (lldb::ByteOrder byte_order) + { + m_byte_order = byte_order; + } + + + //------------------------------------------------------------------ + /// Test the validity of \a offset. + /// + /// @return + /// \b true if \a offset is a valid offset into the data in this + /// object, \b false otherwise. + //------------------------------------------------------------------ + bool + ValidOffset (uint32_t offset) const + { + return offset < GetByteSize(); + } + + //------------------------------------------------------------------ + /// Test the availability of \a length bytes of data from \a offset. + /// + /// @return + /// \b true if \a offset is a valid offset and there are \a + /// length bytes available at that offset, \b false otherwise. + //------------------------------------------------------------------ + bool + ValidOffsetForDataOfSize (uint32_t offset, uint32_t length) const + { + return length <= BytesLeft (offset); + } + + uint32_t + BytesLeft (uint32_t offset) const + { + const uint32_t size = GetByteSize(); + if (size > offset) + return size - offset; + return 0; + } + +protected: + //------------------------------------------------------------------ + // Member variables + //------------------------------------------------------------------ + uint8_t *m_start; ///< A pointer to the first byte of data. + uint8_t *m_end; ///< A pointer to the byte that is past the end of the data. + lldb::ByteOrder m_byte_order; ///< The byte order of the data we are extracting from. + uint8_t m_addr_size; ///< The address size to use when extracting pointers or addresses + mutable lldb::DataBufferSP m_data_sp; ///< The shared pointer to data that can be shared among multilple instances + +private: + DISALLOW_COPY_AND_ASSIGN (DataEncoder); + +}; + +} // namespace lldb_private + +#endif // #if defined (__cplusplus) +#endif // #ifndef liblldb_DataEncoder_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Core/DataExtractor.h b/contrib/llvm/tools/lldb/include/lldb/Core/DataExtractor.h new file mode 100644 index 00000000000..a8593043cb1 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Core/DataExtractor.h @@ -0,0 +1,1298 @@ +//===-- DataExtractor.h -----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_DataExtractor_h_ +#define liblldb_DataExtractor_h_ +#if defined (__cplusplus) + + +#include "lldb/lldb-private.h" +#include +#include +#include + +namespace lldb_private { + +//---------------------------------------------------------------------- +/// @class DataExtractor DataExtractor.h "lldb/Core/DataExtractor.h" +/// @brief An data extractor class. +/// +/// DataExtractor is a class that can extract data (swapping if needed) +/// from a data buffer. The data buffer can be caller owned, or can be +/// shared data that can be shared between multiple DataExtractor +/// instances. Multiple DataExtractor objects can share the same data, +/// yet extract values in different address sizes and byte order modes. +/// Each object can have a unique position in the shared data and extract +/// data from different offsets. +/// +/// @see DataBuffer +//---------------------------------------------------------------------- +class DataExtractor +{ +public: + //------------------------------------------------------------------ + /// @typedef DataExtractor::Type + /// @brief Type enumerations used in the dump routines. + /// @see DataExtractor::Dump() + /// @see DataExtractor::DumpRawHexBytes() + //------------------------------------------------------------------ + typedef enum + { + TypeUInt8, ///< Format output as unsigned 8 bit integers + TypeChar, ///< Format output as characters + TypeUInt16, ///< Format output as unsigned 16 bit integers + TypeUInt32, ///< Format output as unsigned 32 bit integers + TypeUInt64, ///< Format output as unsigned 64 bit integers + TypePointer, ///< Format output as pointers + TypeULEB128, ///< Format output as ULEB128 numbers + TypeSLEB128 ///< Format output as SLEB128 numbers + } Type; + + static void + DumpHexBytes (Stream *s, + const void *src, + size_t src_len, + uint32_t bytes_per_line, + lldb::addr_t base_addr); // Pass LLDB_INVALID_ADDRESS to not show address at start of line + //------------------------------------------------------------------ + /// Default constructor. + /// + /// Initialize all members to a default empty state. + //------------------------------------------------------------------ + DataExtractor (); + + //------------------------------------------------------------------ + /// Construct with a buffer that is owned by the caller. + /// + /// This constructor allows us to use data that is owned by the + /// caller. The data must stay around as long as this object is + /// valid. + /// + /// @param[in] data + /// A pointer to caller owned data. + /// + /// @param[in] data_length + /// The length in bytes of \a data. + /// + /// @param[in] byte_order + /// A byte order of the data that we are extracting from. + /// + /// @param[in] addr_size + /// A new address byte size value. + //------------------------------------------------------------------ + DataExtractor (const void* data, lldb::offset_t data_length, lldb::ByteOrder byte_order, uint32_t addr_size); + + //------------------------------------------------------------------ + /// Construct with shared data. + /// + /// Copies the data shared pointer which adds a reference to the + /// contained in \a data_sp. The shared data reference is reference + /// counted to ensure the data lives as long as anyone still has a + /// valid shared pointer to the data in \a data_sp. + /// + /// @param[in] data_sp + /// A shared pointer to data. + /// + /// @param[in] byte_order + /// A byte order of the data that we are extracting from. + /// + /// @param[in] addr_size + /// A new address byte size value. + //------------------------------------------------------------------ + DataExtractor (const lldb::DataBufferSP& data_sp, lldb::ByteOrder byte_order, uint32_t addr_size); + + //------------------------------------------------------------------ + /// Construct with a subset of \a data. + /// + /// Initialize this object with a subset of the data bytes in \a + /// data. If \a data contains shared data, then a reference to the + /// shared data will be added to ensure the shared data stays around + /// as long as any objects have references to the shared data. The + /// byte order value and the address size settings are copied from \a + /// data. If \a offset is not a valid offset in \a data, then no + /// reference to the shared data will be added. If there are not + /// \a length bytes available in \a data starting at \a offset, + /// the length will be truncated to contain as many bytes as + /// possible. + /// + /// @param[in] data + /// Another DataExtractor object that contains data. + /// + /// @param[in] offset + /// The offset into \a data at which the subset starts. + /// + /// @param[in] length + /// The length in bytes of the subset of data. + //------------------------------------------------------------------ + DataExtractor (const DataExtractor& data, lldb::offset_t offset, lldb::offset_t length); + + DataExtractor (const DataExtractor& rhs); + //------------------------------------------------------------------ + /// Assignment operator. + /// + /// Copies all data, byte order and address size settings from \a rhs into + /// this object. If \a rhs contains shared data, a reference to that + /// shared data will be added. + /// + /// @param[in] rhs + /// Another DataExtractor object to copy. + /// + /// @return + /// A const reference to this object. + //------------------------------------------------------------------ + const DataExtractor& + operator= (const DataExtractor& rhs); + + //------------------------------------------------------------------ + /// Destructor + /// + /// If this object contains a valid shared data reference, the + /// reference count on the data will be decremented, and if zero, + /// the data will be freed. + //------------------------------------------------------------------ + ~DataExtractor (); + + //------------------------------------------------------------------ + /// Clears the object state. + /// + /// Clears the object contents back to a default invalid state, and + /// release any references to shared data that this object may + /// contain. + //------------------------------------------------------------------ + void + Clear (); + + //------------------------------------------------------------------ + /// Dumps the binary data as \a type objects to stream \a s (or to + /// Log() if \a s is NULL) starting \a offset bytes into the data + /// and stopping after dumping \a length bytes. The offset into the + /// data is displayed at the beginning of each line and can be + /// offset by base address \a base_addr. \a num_per_line objects + /// will be displayed on each line. + /// + /// @param[in] s + /// The stream to dump the output to. If NULL the output will + /// be dumped to Log(). + /// + /// @param[in] offset + /// The offset into the data at which to start dumping. + /// + /// @param[in] length + /// The number of bytes to dump. + /// + /// @param[in] base_addr + /// The base address that gets added to the offset displayed on + /// each line. + /// + /// @param[in] num_per_line + /// The number of \a type objects to display on each line. + /// + /// @param[in] type + /// The type of objects to use when dumping data from this + /// object. See DataExtractor::Type. + /// + /// @param[in] type_format + /// The optional format to use for the \a type objects. If this + /// is NULL, the default format for the \a type will be used. + /// + /// @return + /// The offset at which dumping ended. + //------------------------------------------------------------------ + lldb::offset_t + PutToLog (Log *log, + lldb::offset_t offset, + lldb::offset_t length, + uint64_t base_addr, + uint32_t num_per_line, + Type type, + const char *type_format = NULL) const; + + //------------------------------------------------------------------ + /// Dumps \a item_count objects into the stream \a s. + /// + /// Dumps \a item_count objects using \a item_format, each of which + /// are \a item_byte_size bytes long starting at offset \a offset + /// bytes into the contained data, into the stream \a s. \a + /// num_per_line objects will be dumped on each line before a new + /// line will be output. If \a base_addr is a valid address, then + /// each new line of output will be prededed by the address value + /// plus appropriate offset, and a colon and space. Bitfield values + /// can be dumped by calling this function multiple times with the + /// same start offset, format and size, yet differing \a + /// item_bit_size and \a item_bit_offset values. + /// + /// @param[in] s + /// The stream to dump the output to. This value can not be NULL. + /// + /// @param[in] offset + /// The offset into the data at which to start dumping. + /// + /// @param[in] item_format + /// The format to use when dumping each item. + /// + /// @param[in] item_byte_size + /// The byte size of each item. + /// + /// @param[in] item_count + /// The number of items to dump. + /// + /// @param[in] num_per_line + /// The number of items to display on each line. + /// + /// @param[in] base_addr + /// The base address that gets added to the offset displayed on + /// each line if the value is valid. Is \a base_addr is + /// LLDB_INVALID_ADDRESS then no address values will be prepended + /// to any lines. + /// + /// @param[in] item_bit_size + /// If the value to display is a bitfield, this value should + /// be the number of bits that the bitfield item has within the + /// item's byte size value. This function will need to be called + /// multiple times with identical \a offset and \a item_byte_size + /// values in order to display multiple bitfield values that + /// exist within the same integer value. If the items being + /// displayed are not bitfields, this value should be zero. + /// + /// @param[in] item_bit_offset + /// If the value to display is a bitfield, this value should + /// be the offset in bits, or shift right amount, that the + /// bitfield item occupies within the item's byte size value. + /// This function will need to be called multiple times with + /// identical \a offset and \a item_byte_size values in order + /// to display multiple bitfield values that exist within the + /// same integer value. If the items being displayed are not + /// bitfields, this value should be zero. + /// + /// @return + /// The offset at which dumping ended. + //------------------------------------------------------------------ + lldb::offset_t + Dump (Stream *s, + lldb::offset_t offset, + lldb::Format item_format, + size_t item_byte_size, + size_t item_count, + size_t num_per_line, + uint64_t base_addr, + uint32_t item_bit_size, + uint32_t item_bit_offset, + ExecutionContextScope *exe_scope = NULL) const; + + //------------------------------------------------------------------ + /// Dump a UUID value at \a offset. + /// + /// Dump a UUID starting at \a offset bytes into this object's data. + /// If the stream \a s is NULL, the output will be sent to Log(). + /// + /// @param[in] s + /// The stream to dump the output to. If NULL the output will + /// be dumped to Log(). + /// + /// @param[in] offset + /// The offset into the data at which to extract and dump a + /// UUID value. + //------------------------------------------------------------------ + void + DumpUUID (Stream *s, lldb::offset_t offset) const; + + //------------------------------------------------------------------ + /// Extract an arbitrary number of bytes in the specified byte + /// order. + /// + /// Attemps to extract \a length bytes starting at \a offset bytes + /// into this data in the requested byte order (\a dst_byte_order) + /// and place the results in \a dst. \a dst must be at least \a + /// length bytes long. + /// + /// @param[in] offset + /// The offset in bytes into the contained data at which to + /// start extracting. + /// + /// @param[in] length + /// The number of bytes to extract. + /// + /// @param[in] dst_byte_order + /// A byte order of the data that we want when the value in + /// copied to \a dst. + /// + /// @param[out] dst + /// The buffer that will receive the extracted value if there + /// are enough bytes available in the current data. + /// + /// @return + /// The number of bytes that were extracted which will be \a + /// length when the value is successfully extracted, or zero + /// if there aren't enough bytes at the specified offset. + //------------------------------------------------------------------ + size_t + ExtractBytes (lldb::offset_t offset, lldb::offset_t length, lldb::ByteOrder dst_byte_order, void *dst) const; + + //------------------------------------------------------------------ + /// Extract an address from \a *offset_ptr. + /// + /// Extract a single address from the data and update the offset + /// pointed to by \a offset_ptr. The size of the extracted address + /// comes from the \a m_addr_size member variable and should be + /// set correctly prior to extracting any address values. + /// + /// @param[in,out] offset_ptr + /// A pointer to an offset within the data that will be advanced + /// by the appropriate number of bytes if the value is extracted + /// correctly. If the offset is out of bounds or there are not + /// enough bytes to extract this value, the offset will be left + /// unmodified. + /// + /// @return + /// The extracted address value. + //------------------------------------------------------------------ + uint64_t + GetAddress (lldb::offset_t *offset_ptr) const; + + uint64_t + GetAddress_unchecked (lldb::offset_t *offset_ptr) const; + + //------------------------------------------------------------------ + /// Get the current address size. + /// + /// Return the size in bytes of any address values this object will + /// extract. + /// + /// @return + /// The size in bytes of address values that will be extracted. + //------------------------------------------------------------------ + uint32_t + GetAddressByteSize () const + { + return m_addr_size; + } + + //------------------------------------------------------------------ + /// Get the number of bytes contained in this object. + /// + /// @return + /// The total number of bytes of data this object refers to. + //------------------------------------------------------------------ + uint64_t + GetByteSize () const + { + return m_end - m_start; + } + + //------------------------------------------------------------------ + /// Extract a C string from \a *offset_ptr. + /// + /// Returns a pointer to a C String from the data at the offset + /// pointed to by \a offset_ptr. A variable length NULL terminated C + /// string will be extracted and the \a offset_ptr will be + /// updated with the offset of the byte that follows the NULL + /// terminator byte. + /// + /// @param[in,out] offset_ptr + /// A pointer to an offset within the data that will be advanced + /// by the appropriate number of bytes if the value is extracted + /// correctly. If the offset is out of bounds or there are not + /// enough bytes to extract this value, the offset will be left + /// unmodified. + /// + /// @return + /// A pointer to the C string value in the data. If the offset + /// pointed to by \a offset_ptr is out of bounds, or if the + /// offset plus the length of the C string is out of bounds, + /// NULL will be returned. + //------------------------------------------------------------------ + const char * + GetCStr (lldb::offset_t *offset_ptr) const; + + //------------------------------------------------------------------ + /// Extract a C string from \a *offset_ptr with field size \a len. + /// + /// Returns a pointer to a C String from the data at the offset + /// pointed to by \a offset_ptr, with a field length of \a len. + /// A NULL terminated C string will be extracted and the \a offset_ptr + /// will be updated with the offset of the byte that follows the fixed + /// length field. + /// + /// @param[in,out] offset_ptr + /// A pointer to an offset within the data that will be advanced + /// by the appropriate number of bytes if the value is extracted + /// correctly. If the offset is out of bounds or there are not + /// enough bytes to extract this value, the offset will be left + /// unmodified. + /// + /// @return + /// A pointer to the C string value in the data. If the offset + /// pointed to by \a offset_ptr is out of bounds, or if the + /// offset plus the length of the field is out of bounds, or if + /// the field does not contain a NULL terminator byte, NULL will + /// be returned. + const char * + GetCStr (lldb::offset_t *offset_ptr, lldb::offset_t len) const; + + //------------------------------------------------------------------ + /// Extract \a length bytes from \a *offset_ptr. + /// + /// Returns a pointer to a bytes in this object's data at the offset + /// pointed to by \a offset_ptr. If \a length is zero or too large, + /// then the offset pointed to by \a offset_ptr will not be updated + /// and NULL will be returned. + /// + /// @param[in,out] offset_ptr + /// A pointer to an offset within the data that will be advanced + /// by the appropriate number of bytes if the value is extracted + /// correctly. If the offset is out of bounds or there are not + /// enough bytes to extract this value, the offset will be left + /// unmodified. + /// + /// @param[in] length + /// The optional length of a string to extract. If the value is + /// zero, a NULL terminated C string will be extracted. + /// + /// @return + /// A pointer to the bytes in this object's data if the offset + /// and length are valid, or NULL otherwise. + //------------------------------------------------------------------ + const void* + GetData (lldb::offset_t *offset_ptr, lldb::offset_t length) const + { + const uint8_t *ptr = PeekData (*offset_ptr, length); + if (ptr) + *offset_ptr += length; + return ptr; + } + + //------------------------------------------------------------------ + /// Copy \a dst_len bytes from \a *offset_ptr and ensure the copied + /// data is treated as a value that can be swapped to match the + /// specified byte order. + /// + /// For values that are larger than the supported integer sizes, + /// this function can be used to extract data in a specified byte + /// order. It can also be used to copy a smaller integer value from + /// to a larger value. The extra bytes left over will be padded + /// correctly according to the byte order of this object and the + /// \a dst_byte_order. This can be very handy when say copying a + /// partial data value into a register. + /// + /// @param[in] src_offset + /// The offset into this data from which to start copying an + /// endian entity + /// + /// @param[in] src_len + /// The length of the endian data to copy from this object + /// into the \a dst object + /// + /// @param[out] dst + /// The buffer where to place the endian data. The data might + /// need to be byte swapped (and appropriately padded with + /// zeroes if \a src_len != \a dst_len) if \a dst_byte_order + /// does not match the byte order in this object. + /// + /// @param[in] dst_len + /// The length number of bytes that the endian value will + /// occupy is \a dst. + /// + /// @param[in] byte_order + /// The byte order that the endian value should be in the \a dst + /// buffer. + /// + /// @return + /// Returns the number of bytes that were copied, or zero if + /// anything goes wrong. + //------------------------------------------------------------------ + lldb::offset_t + CopyByteOrderedData (lldb::offset_t src_offset, + lldb::offset_t src_len, + void *dst, + lldb::offset_t dst_len, + lldb::ByteOrder dst_byte_order) const; + + //------------------------------------------------------------------ + /// Get the data end pointer. + /// + /// @return + /// Returns a pointer to the next byte contained in this + /// object's data, or NULL of there is no data in this object. + //------------------------------------------------------------------ + const uint8_t * + GetDataEnd () const + { + return m_end; + } + + //------------------------------------------------------------------ + /// Get the shared data offset. + /// + /// Get the offset of the first byte of data in the shared data (if + /// any). + /// + /// @return + /// If this object contains shared data, this function returns + /// the offset in bytes into that shared data, zero otherwise. + //------------------------------------------------------------------ + size_t + GetSharedDataOffset () const; + + //------------------------------------------------------------------ + /// Get a the data start pointer. + /// + /// @return + /// Returns a pointer to the first byte contained in this + /// object's data, or NULL of there is no data in this object. + //------------------------------------------------------------------ + const uint8_t * + GetDataStart () const + { + return m_start; + } + + + //------------------------------------------------------------------ + /// Extract a float from \a *offset_ptr. + /// + /// Extract a single float value. + /// + /// @param[in,out] offset_ptr + /// A pointer to an offset within the data that will be advanced + /// by the appropriate number of bytes if the value is extracted + /// correctly. If the offset is out of bounds or there are not + /// enough bytes to extract this value, the offset will be left + /// unmodified. + /// + /// @return + /// The floating value that was extracted, or zero on failure. + //------------------------------------------------------------------ + float + GetFloat (lldb::offset_t *offset_ptr) const; + + double + GetDouble (lldb::offset_t *offset_ptr) const; + + long double + GetLongDouble (lldb::offset_t *offset_ptr) const; + + //------------------------------------------------------------------ + /// Extract a GNU encoded pointer value from \a *offset_ptr. + /// + /// @param[in,out] offset_ptr + /// A pointer to an offset within the data that will be advanced + /// by the appropriate number of bytes if the value is extracted + /// correctly. If the offset is out of bounds or there are not + /// enough bytes to extract this value, the offset will be left + /// unmodified. + /// + /// @param[in] eh_ptr_enc + /// The GNU pointer encoding type. + /// + /// @param[in] pc_rel_addr + /// The PC relative address to use when the encoding is + /// \c DW_GNU_EH_PE_pcrel. + /// + /// @param[in] text_addr + /// The text (code) relative address to use when the encoding is + /// \c DW_GNU_EH_PE_textrel. + /// + /// @param[in] data_addr + /// The data relative address to use when the encoding is + /// \c DW_GNU_EH_PE_datarel. + /// + /// @return + /// The extracted GNU encoded pointer value. + //------------------------------------------------------------------ + uint64_t + GetGNUEHPointer (lldb::offset_t *offset_ptr, + uint32_t eh_ptr_enc, + lldb::addr_t pc_rel_addr, + lldb::addr_t text_addr, + lldb::addr_t data_addr); + + //------------------------------------------------------------------ + /// Extract an integer of size \a byte_size from \a *offset_ptr. + /// + /// Extract a single integer value and update the offset pointed to + /// by \a offset_ptr. The size of the extracted integer is specified + /// by the \a byte_size argument. \a byte_size should have a value + /// >= 1 and <= 4 since the return value is only 32 bits wide. Any + /// \a byte_size values less than 1 or greater than 4 will result in + /// nothing being extracted, and zero being returned. + /// + /// @param[in,out] offset_ptr + /// A pointer to an offset within the data that will be advanced + /// by the appropriate number of bytes if the value is extracted + /// correctly. If the offset is out of bounds or there are not + /// enough bytes to extract this value, the offset will be left + /// unmodified. + /// + /// @param[in] byte_size + /// The size in byte of the integer to extract. + /// + /// @return + /// The integer value that was extracted, or zero on failure. + //------------------------------------------------------------------ + uint32_t + GetMaxU32 (lldb::offset_t *offset_ptr, size_t byte_size) const; + + //------------------------------------------------------------------ + /// Extract an unsigned integer of size \a byte_size from \a + /// *offset_ptr. + /// + /// Extract a single unsigned integer value and update the offset + /// pointed to by \a offset_ptr. The size of the extracted integer + /// is specified by the \a byte_size argument. \a byte_size should + /// have a value greater than or equal to one and less than or equal + /// to eight since the return value is 64 bits wide. Any + /// \a byte_size values less than 1 or greater than 8 will result in + /// nothing being extracted, and zero being returned. + /// + /// @param[in,out] offset_ptr + /// A pointer to an offset within the data that will be advanced + /// by the appropriate number of bytes if the value is extracted + /// correctly. If the offset is out of bounds or there are not + /// enough bytes to extract this value, the offset will be left + /// unmodified. + /// + /// @param[in] byte_size + /// The size in byte of the integer to extract. + /// + /// @return + /// The unsigned integer value that was extracted, or zero on + /// failure. + //------------------------------------------------------------------ + uint64_t + GetMaxU64 (lldb::offset_t *offset_ptr, size_t byte_size) const; + + uint64_t + GetMaxU64_unchecked (lldb::offset_t *offset_ptr, size_t byte_size) const; + + //------------------------------------------------------------------ + /// Extract an signed integer of size \a byte_size from \a *offset_ptr. + /// + /// Extract a single signed integer value (sign extending if required) + /// and update the offset pointed to by \a offset_ptr. The size of + /// the extracted integer is specified by the \a byte_size argument. + /// \a byte_size should have a value greater than or equal to one + /// and less than or equal to eight since the return value is 64 + /// bits wide. Any \a byte_size values less than 1 or greater than + /// 8 will result in nothing being extracted, and zero being returned. + /// + /// @param[in,out] offset_ptr + /// A pointer to an offset within the data that will be advanced + /// by the appropriate number of bytes if the value is extracted + /// correctly. If the offset is out of bounds or there are not + /// enough bytes to extract this value, the offset will be left + /// unmodified. + /// + /// @param[in] byte_size + /// The size in byte of the integer to extract. + /// + /// @return + /// The sign extended signed integer value that was extracted, + /// or zero on failure. + //------------------------------------------------------------------ + int64_t + GetMaxS64 (lldb::offset_t *offset_ptr, size_t size) const; + + //------------------------------------------------------------------ + /// Extract an unsigned integer of size \a byte_size from \a + /// *offset_ptr, then extract the bitfield from this value if + /// \a bitfield_bit_size is non-zero. + /// + /// Extract a single unsigned integer value and update the offset + /// pointed to by \a offset_ptr. The size of the extracted integer + /// is specified by the \a byte_size argument. \a byte_size should + /// have a value greater than or equal to one and less than or equal + /// to 8 since the return value is 64 bits wide. Any + /// \a byte_size values less than 1 or greater than 8 will result in + /// nothing being extracted, and zero being returned. + /// + /// @param[in,out] offset_ptr + /// A pointer to an offset within the data that will be advanced + /// by the appropriate number of bytes if the value is extracted + /// correctly. If the offset is out of bounds or there are not + /// enough bytes to extract this value, the offset will be left + /// unmodified. + /// + /// @param[in] byte_size + /// The size in byte of the integer to extract. + /// + /// @param[in] bitfield_bit_size + /// The size in bits of the bitfield value to extract, or zero + /// to just extract the entire integer value. + /// + /// @param[in] bitfield_bit_offset + /// The bit offset of the bitfield value in the extracted + /// integer (the number of bits to shift the integer to the + /// right). + /// + /// @return + /// The unsigned bitfield integer value that was extracted, or + /// zero on failure. + //------------------------------------------------------------------ + uint64_t + GetMaxU64Bitfield (lldb::offset_t *offset_ptr, + size_t size, + uint32_t bitfield_bit_size, + uint32_t bitfield_bit_offset) const; + + //------------------------------------------------------------------ + /// Extract an signed integer of size \a byte_size from \a + /// *offset_ptr, then extract and signe extend the bitfield from + /// this value if \a bitfield_bit_size is non-zero. + /// + /// Extract a single signed integer value (sign extending if required) + /// and update the offset pointed to by \a offset_ptr. The size of + /// the extracted integer is specified by the \a byte_size argument. + /// \a byte_size should have a value greater than or equal to one + /// and less than or equal to eight since the return value is 64 + /// bits wide. Any \a byte_size values less than 1 or greater than + /// 8 will result in nothing being extracted, and zero being returned. + /// + /// @param[in,out] offset_ptr + /// A pointer to an offset within the data that will be advanced + /// by the appropriate number of bytes if the value is extracted + /// correctly. If the offset is out of bounds or there are not + /// enough bytes to extract this value, the offset will be left + /// unmodified. + /// + /// @param[in] byte_size + /// The size in bytes of the integer to extract. + /// + /// @param[in] bitfield_bit_size + /// The size in bits of the bitfield value to extract, or zero + /// to just extract the entire integer value. + /// + /// @param[in] bitfield_bit_offset + /// The bit offset of the bitfield value in the extracted + /// integer (the number of bits to shift the integer to the + /// right). + /// + /// @return + /// The signed bitfield integer value that was extracted, or + /// zero on failure. + //------------------------------------------------------------------ + int64_t + GetMaxS64Bitfield (lldb::offset_t *offset_ptr, + size_t size, + uint32_t bitfield_bit_size, + uint32_t bitfield_bit_offset) const; + + //------------------------------------------------------------------ + /// Extract an pointer from \a *offset_ptr. + /// + /// Extract a single pointer from the data and update the offset + /// pointed to by \a offset_ptr. The size of the extracted pointer + /// comes from the \a m_addr_size member variable and should be + /// set correctly prior to extracting any pointer values. + /// + /// @param[in,out] offset_ptr + /// A pointer to an offset within the data that will be advanced + /// by the appropriate number of bytes if the value is extracted + /// correctly. If the offset is out of bounds or there are not + /// enough bytes to extract this value, the offset will be left + /// unmodified. + /// + /// @return + /// The extracted pointer value as a 64 integer. + //------------------------------------------------------------------ + uint64_t + GetPointer (lldb::offset_t *offset_ptr) const; + + //------------------------------------------------------------------ + /// Get the current byte order value. + /// + /// @return + /// The current byte order value from this object's internal + /// state. + //------------------------------------------------------------------ + lldb::ByteOrder + GetByteOrder() const + { + return m_byte_order; + } + + //------------------------------------------------------------------ + /// Extract a uint8_t value from \a *offset_ptr. + /// + /// Extract a single uint8_t from the binary data at the offset + /// pointed to by \a offset_ptr, and advance the offset on success. + /// + /// @param[in,out] offset_ptr + /// A pointer to an offset within the data that will be advanced + /// by the appropriate number of bytes if the value is extracted + /// correctly. If the offset is out of bounds or there are not + /// enough bytes to extract this value, the offset will be left + /// unmodified. + /// + /// @return + /// The extracted uint8_t value. + //------------------------------------------------------------------ + uint8_t + GetU8 ( lldb::offset_t *offset_ptr) const; + + uint8_t + GetU8_unchecked (lldb::offset_t *offset_ptr) const + { + uint8_t val = m_start[*offset_ptr]; + *offset_ptr += 1; + return val; + } + + uint16_t + GetU16_unchecked (lldb::offset_t *offset_ptr) const; + + uint32_t + GetU32_unchecked (lldb::offset_t *offset_ptr) const; + + uint64_t + GetU64_unchecked (lldb::offset_t *offset_ptr) const; + //------------------------------------------------------------------ + /// Extract \a count uint8_t values from \a *offset_ptr. + /// + /// Extract \a count uint8_t values from the binary data at the + /// offset pointed to by \a offset_ptr, and advance the offset on + /// success. The extracted values are copied into \a dst. + /// + /// @param[in,out] offset_ptr + /// A pointer to an offset within the data that will be advanced + /// by the appropriate number of bytes if the value is extracted + /// correctly. If the offset is out of bounds or there are not + /// enough bytes to extract this value, the offset will be left + /// unmodified. + /// + /// @param[out] dst + /// A buffer to copy \a count uint8_t values into. \a dst must + /// be large enough to hold all requested data. + /// + /// @param[in] count + /// The number of uint8_t values to extract. + /// + /// @return + /// \a dst if all values were properly extracted and copied, + /// NULL otherise. + //------------------------------------------------------------------ + void * + GetU8 (lldb::offset_t *offset_ptr, void *dst, uint32_t count) const; + + //------------------------------------------------------------------ + /// Extract a uint16_t value from \a *offset_ptr. + /// + /// Extract a single uint16_t from the binary data at the offset + /// pointed to by \a offset_ptr, and update the offset on success. + /// + /// @param[in,out] offset_ptr + /// A pointer to an offset within the data that will be advanced + /// by the appropriate number of bytes if the value is extracted + /// correctly. If the offset is out of bounds or there are not + /// enough bytes to extract this value, the offset will be left + /// unmodified. + /// + /// @return + /// The extracted uint16_t value. + //------------------------------------------------------------------ + uint16_t + GetU16 (lldb::offset_t *offset_ptr) const; + + //------------------------------------------------------------------ + /// Extract \a count uint16_t values from \a *offset_ptr. + /// + /// Extract \a count uint16_t values from the binary data at the + /// offset pointed to by \a offset_ptr, and advance the offset on + /// success. The extracted values are copied into \a dst. + /// + /// @param[in,out] offset_ptr + /// A pointer to an offset within the data that will be advanced + /// by the appropriate number of bytes if the value is extracted + /// correctly. If the offset is out of bounds or there are not + /// enough bytes to extract this value, the offset will be left + /// unmodified. + /// + /// @param[out] dst + /// A buffer to copy \a count uint16_t values into. \a dst must + /// be large enough to hold all requested data. + /// + /// @param[in] count + /// The number of uint16_t values to extract. + /// + /// @return + /// \a dst if all values were properly extracted and copied, + /// NULL otherise. + //------------------------------------------------------------------ + void * + GetU16 (lldb::offset_t *offset_ptr, void *dst, uint32_t count) const; + + //------------------------------------------------------------------ + /// Extract a uint32_t value from \a *offset_ptr. + /// + /// Extract a single uint32_t from the binary data at the offset + /// pointed to by \a offset_ptr, and update the offset on success. + /// + /// @param[in,out] offset_ptr + /// A pointer to an offset within the data that will be advanced + /// by the appropriate number of bytes if the value is extracted + /// correctly. If the offset is out of bounds or there are not + /// enough bytes to extract this value, the offset will be left + /// unmodified. + /// + /// @return + /// The extracted uint32_t value. + //------------------------------------------------------------------ + uint32_t + GetU32 (lldb::offset_t *offset_ptr) const; + + //------------------------------------------------------------------ + /// Extract \a count uint32_t values from \a *offset_ptr. + /// + /// Extract \a count uint32_t values from the binary data at the + /// offset pointed to by \a offset_ptr, and advance the offset on + /// success. The extracted values are copied into \a dst. + /// + /// @param[in,out] offset_ptr + /// A pointer to an offset within the data that will be advanced + /// by the appropriate number of bytes if the value is extracted + /// correctly. If the offset is out of bounds or there are not + /// enough bytes to extract this value, the offset will be left + /// unmodified. + /// + /// @param[out] dst + /// A buffer to copy \a count uint32_t values into. \a dst must + /// be large enough to hold all requested data. + /// + /// @param[in] count + /// The number of uint32_t values to extract. + /// + /// @return + /// \a dst if all values were properly extracted and copied, + /// NULL otherise. + //------------------------------------------------------------------ + void * + GetU32 (lldb::offset_t *offset_ptr, void *dst, uint32_t count) const; + + //------------------------------------------------------------------ + /// Extract a uint64_t value from \a *offset_ptr. + /// + /// Extract a single uint64_t from the binary data at the offset + /// pointed to by \a offset_ptr, and update the offset on success. + /// + /// @param[in,out] offset_ptr + /// A pointer to an offset within the data that will be advanced + /// by the appropriate number of bytes if the value is extracted + /// correctly. If the offset is out of bounds or there are not + /// enough bytes to extract this value, the offset will be left + /// unmodified. + /// + /// @return + /// The extracted uint64_t value. + //------------------------------------------------------------------ + uint64_t + GetU64 (lldb::offset_t *offset_ptr) const; + + //------------------------------------------------------------------ + /// Extract \a count uint64_t values from \a *offset_ptr. + /// + /// Extract \a count uint64_t values from the binary data at the + /// offset pointed to by \a offset_ptr, and advance the offset on + /// success. The extracted values are copied into \a dst. + /// + /// @param[in,out] offset_ptr + /// A pointer to an offset within the data that will be advanced + /// by the appropriate number of bytes if the value is extracted + /// correctly. If the offset is out of bounds or there are not + /// enough bytes to extract this value, the offset will be left + /// unmodified. + /// + /// @param[out] dst + /// A buffer to copy \a count uint64_t values into. \a dst must + /// be large enough to hold all requested data. + /// + /// @param[in] count + /// The number of uint64_t values to extract. + /// + /// @return + /// \a dst if all values were properly extracted and copied, + /// NULL otherise. + //------------------------------------------------------------------ + void * + GetU64 ( lldb::offset_t *offset_ptr, void *dst, uint32_t count) const; + + //------------------------------------------------------------------ + /// Extract a signed LEB128 value from \a *offset_ptr. + /// + /// Extracts an signed LEB128 number from this object's data + /// starting at the offset pointed to by \a offset_ptr. The offset + /// pointed to by \a offset_ptr will be updated with the offset of + /// the byte following the last extracted byte. + /// + /// @param[in,out] offset_ptr + /// A pointer to an offset within the data that will be advanced + /// by the appropriate number of bytes if the value is extracted + /// correctly. If the offset is out of bounds or there are not + /// enough bytes to extract this value, the offset will be left + /// unmodified. + /// + /// @return + /// The extracted signed integer value. + //------------------------------------------------------------------ + int64_t + GetSLEB128 (lldb::offset_t *offset_ptr) const; + + //------------------------------------------------------------------ + /// Extract a unsigned LEB128 value from \a *offset_ptr. + /// + /// Extracts an unsigned LEB128 number from this object's data + /// starting at the offset pointed to by \a offset_ptr. The offset + /// pointed to by \a offset_ptr will be updated with the offset of + /// the byte following the last extracted byte. + /// + /// @param[in,out] offset_ptr + /// A pointer to an offset within the data that will be advanced + /// by the appropriate number of bytes if the value is extracted + /// correctly. If the offset is out of bounds or there are not + /// enough bytes to extract this value, the offset will be left + /// unmodified. + /// + /// @return + /// The extracted unsigned integer value. + //------------------------------------------------------------------ + uint64_t + GetULEB128 (lldb::offset_t *offset_ptr) const; + + lldb::DataBufferSP & + GetSharedDataBuffer () + { + return m_data_sp; + } + + //------------------------------------------------------------------ + /// Peek at a C string at \a offset. + /// + /// Peeks at a string in the contained data. No verification is done + /// to make sure the entire string lies within the bounds of this + /// object's data, only \a offset is verified to be a valid offset. + /// + /// @param[in] offset + /// An offset into the data. + /// + /// @return + /// A non-NULL C string pointer if \a offset is a valid offset, + /// NULL otherwise. + //------------------------------------------------------------------ + const char * + PeekCStr (lldb::offset_t offset) const; + + //------------------------------------------------------------------ + /// Peek at a bytes at \a offset. + /// + /// Returns a pointer to \a length bytes at \a offset as long as + /// there are \a length bytes available starting at \a offset. + /// + /// @return + /// A non-NULL data pointer if \a offset is a valid offset and + /// there are \a length bytes available at that offset, NULL + /// otherwise. + //------------------------------------------------------------------ + const uint8_t* + PeekData (lldb::offset_t offset, lldb::offset_t length) const + { + if (length > 0 && ValidOffsetForDataOfSize(offset, length)) + return m_start + offset; + return NULL; + } + + //------------------------------------------------------------------ + /// Set the address byte size. + /// + /// Set the size in bytes that will be used when extracting any + /// address and pointer values from data contained in this object. + /// + /// @param[in] addr_size + /// The size in bytes to use when extracting addresses. + //------------------------------------------------------------------ + void + SetAddressByteSize (uint32_t addr_size) + { + m_addr_size = addr_size; + } + + //------------------------------------------------------------------ + /// Set data with a buffer that is caller owned. + /// + /// Use data that is owned by the caller when extracting values. + /// The data must stay around as long as this object, or any object + /// that copies a subset of this object's data, is valid. If \a + /// bytes is NULL, or \a length is zero, this object will contain + /// no data. + /// + /// @param[in] bytes + /// A pointer to caller owned data. + /// + /// @param[in] length + /// The length in bytes of \a bytes. + /// + /// @param[in] byte_order + /// A byte order of the data that we are extracting from. + /// + /// @return + /// The number of bytes that this object now contains. + //------------------------------------------------------------------ + lldb::offset_t + SetData (const void *bytes, lldb::offset_t length, lldb::ByteOrder byte_order); + + //------------------------------------------------------------------ + /// Adopt a subset of \a data. + /// + /// Set this object's data to be a subset of the data bytes in \a + /// data. If \a data contains shared data, then a reference to the + /// shared data will be added to ensure the shared data stays around + /// as long as any objects have references to the shared data. The + /// byte order and the address size settings are copied from \a + /// data. If \a offset is not a valid offset in \a data, then no + /// reference to the shared data will be added. If there are not + /// \a length bytes available in \a data starting at \a offset, + /// the length will be truncated to contains as many bytes as + /// possible. + /// + /// @param[in] data + /// Another DataExtractor object that contains data. + /// + /// @param[in] offset + /// The offset into \a data at which the subset starts. + /// + /// @param[in] length + /// The length in bytes of the subset of \a data. + /// + /// @return + /// The number of bytes that this object now contains. + //------------------------------------------------------------------ + lldb::offset_t + SetData (const DataExtractor& data, lldb::offset_t offset, lldb::offset_t length); + + //------------------------------------------------------------------ + /// Adopt a subset of shared data in \a data_sp. + /// + /// Copies the data shared pointer which adds a reference to the + /// contained in \a data_sp. The shared data reference is reference + /// counted to ensure the data lives as long as anyone still has a + /// valid shared pointer to the data in \a data_sp. The byte order + /// and address byte size settings remain the same. If + /// \a offset is not a valid offset in \a data_sp, then no reference + /// to the shared data will be added. If there are not \a length + /// bytes available in \a data starting at \a offset, the length + /// will be truncated to contains as many bytes as possible. + /// + /// @param[in] data_sp + /// A shared pointer to data. + /// + /// @param[in] offset + /// The offset into \a data_sp at which the subset starts. + /// + /// @param[in] length + /// The length in bytes of the subset of \a data_sp. + /// + /// @return + /// The number of bytes that this object now contains. + //------------------------------------------------------------------ + lldb::offset_t + SetData (const lldb::DataBufferSP& data_sp, lldb::offset_t offset = 0, lldb::offset_t length = LLDB_INVALID_OFFSET); + + //------------------------------------------------------------------ + /// Set the byte_order value. + /// + /// Sets the byte order of the data to extract. Extracted values + /// will be swapped if necessary when decoding. + /// + /// @param[in] byte_order + /// The byte order value to use when extracting data. + //------------------------------------------------------------------ + void + SetByteOrder (lldb::ByteOrder byte_order) + { + m_byte_order = byte_order; + } + + //------------------------------------------------------------------ + /// Skip an LEB128 number at \a *offset_ptr. + /// + /// Skips a LEB128 number (signed or unsigned) from this object's + /// data starting at the offset pointed to by \a offset_ptr. The + /// offset pointed to by \a offset_ptr will be updated with the + /// offset of the byte following the last extracted byte. + /// + /// @param[in,out] offset_ptr + /// A pointer to an offset within the data that will be advanced + /// by the appropriate number of bytes if the value is extracted + /// correctly. If the offset is out of bounds or there are not + /// enough bytes to extract this value, the offset will be left + /// unmodified. + /// + /// @return + // The number of bytes consumed during the extraction. + //------------------------------------------------------------------ + uint32_t + Skip_LEB128 (lldb::offset_t *offset_ptr) const; + + //------------------------------------------------------------------ + /// Test the validity of \a offset. + /// + /// @return + /// \b true if \a offset is a valid offset into the data in this + /// object, \b false otherwise. + //------------------------------------------------------------------ + bool + ValidOffset (lldb::offset_t offset) const + { + return offset < GetByteSize(); + } + + //------------------------------------------------------------------ + /// Test the availability of \a length bytes of data from \a offset. + /// + /// @return + /// \b true if \a offset is a valid offset and there are \a + /// length bytes available at that offset, \b false otherwise. + //------------------------------------------------------------------ + bool + ValidOffsetForDataOfSize (lldb::offset_t offset, lldb::offset_t length) const + { + return length <= BytesLeft (offset); + } + + size_t + Copy (DataExtractor& dest_data) const; + + bool + Append (DataExtractor& rhs); + + bool + Append (void* bytes, lldb::offset_t length); + + lldb::offset_t + BytesLeft (lldb::offset_t offset) const + { + const lldb::offset_t size = GetByteSize(); + if (size > offset) + return size - offset; + return 0; + } + +protected: + + //------------------------------------------------------------------ + // Member variables + //------------------------------------------------------------------ + const uint8_t * m_start; ///< A pointer to the first byte of data. + const uint8_t * m_end; ///< A pointer to the byte that is past the end of the data. + lldb::ByteOrder m_byte_order; ///< The byte order of the data we are extracting from. + uint32_t m_addr_size; ///< The address size to use when extracting pointers or addresses + mutable lldb::DataBufferSP m_data_sp; ///< The shared pointer to data that can be shared among multilple instances +}; + +} // namespace lldb_private + +#endif // #if defined (__cplusplus) +#endif // #ifndef liblldb_DataExtractor_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Core/Debugger.h b/contrib/llvm/tools/lldb/include/lldb/Core/Debugger.h new file mode 100644 index 00000000000..bed93fe0252 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Core/Debugger.h @@ -0,0 +1,397 @@ +//===-- Debugger.h ----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_Debugger_h_ +#define liblldb_Debugger_h_ +#if defined(__cplusplus) + + +#include +#include + +#include + +#include "lldb/lldb-public.h" + +#include "lldb/API/SBDefines.h" + +#include "lldb/Core/Broadcaster.h" +#include "lldb/Core/Communication.h" +#include "lldb/Core/InputReaderStack.h" +#include "lldb/Core/Listener.h" +#include "lldb/Core/StreamFile.h" +#include "lldb/Core/SourceManager.h" +#include "lldb/Core/UserID.h" +#include "lldb/Core/UserSettingsController.h" +#include "lldb/DataFormatters/FormatManager.h" +#include "lldb/Host/Terminal.h" +#include "lldb/Interpreter/OptionValueProperties.h" +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/Platform.h" +#include "lldb/Target/TargetList.h" + +namespace lldb_private { + +//---------------------------------------------------------------------- +/// @class Debugger Debugger.h "lldb/Core/Debugger.h" +/// @brief A class to manage flag bits. +/// +/// Provides a global root objects for the debugger core. +//---------------------------------------------------------------------- + + +class Debugger : + public std::enable_shared_from_this, + public UserID, + public Properties, + public BroadcasterManager +{ +friend class SourceManager; // For GetSourceFileCache. + +public: + + static lldb::DebuggerSP + CreateInstance (lldb::LogOutputCallback log_callback = NULL, void *baton = NULL); + + static lldb::TargetSP + FindTargetWithProcessID (lldb::pid_t pid); + + static lldb::TargetSP + FindTargetWithProcess (Process *process); + + static void + Initialize (); + + static void + Terminate (); + + static void + SettingsInitialize (); + + static void + SettingsTerminate (); + + static void + Destroy (lldb::DebuggerSP &debugger_sp); + + virtual + ~Debugger (); + + void Clear(); + + bool + GetAsyncExecution (); + + void + SetAsyncExecution (bool async); + + File & + GetInputFile () + { + return m_input_file.GetFile(); + } + + File & + GetOutputFile () + { + return m_output_file.GetFile(); + } + + File & + GetErrorFile () + { + return m_error_file.GetFile(); + } + + void + SetInputFileHandle (FILE *fh, bool tranfer_ownership); + + void + SetOutputFileHandle (FILE *fh, bool tranfer_ownership); + + void + SetErrorFileHandle (FILE *fh, bool tranfer_ownership); + + void + SaveInputTerminalState(); + + void + RestoreInputTerminalState(); + + Stream& + GetOutputStream () + { + return m_output_file; + } + + Stream& + GetErrorStream () + { + return m_error_file; + } + + lldb::StreamSP + GetAsyncOutputStream (); + + lldb::StreamSP + GetAsyncErrorStream (); + + CommandInterpreter & + GetCommandInterpreter () + { + assert (m_command_interpreter_ap.get()); + return *m_command_interpreter_ap; + } + + Listener & + GetListener () + { + return m_listener; + } + + // This returns the Debugger's scratch source manager. It won't be able to look up files in debug + // information, but it can look up files by absolute path and display them to you. + // To get the target's source manager, call GetSourceManager on the target instead. + SourceManager & + GetSourceManager (); + +public: + + lldb::TargetSP + GetSelectedTarget () + { + return m_target_list.GetSelectedTarget (); + } + + ExecutionContext + GetSelectedExecutionContext(); + //------------------------------------------------------------------ + /// Get accessor for the target list. + /// + /// The target list is part of the global debugger object. This + /// the single debugger shared instance to control where targets + /// get created and to allow for tracking and searching for targets + /// based on certain criteria. + /// + /// @return + /// A global shared target list. + //------------------------------------------------------------------ + TargetList & + GetTargetList () + { + return m_target_list; + } + + PlatformList & + GetPlatformList () + { + return m_platform_list; + } + + void + DispatchInputInterrupt (); + + void + DispatchInputEndOfFile (); + + void + DispatchInput (const char *bytes, size_t bytes_len); + + void + WriteToDefaultReader (const char *bytes, size_t bytes_len); + + void + PushInputReader (const lldb::InputReaderSP& reader_sp); + + bool + PopInputReader (const lldb::InputReaderSP& reader_sp); + + void + NotifyTopInputReader (lldb::InputReaderAction notification); + + bool + InputReaderIsTopReader (const lldb::InputReaderSP& reader_sp); + + static lldb::DebuggerSP + FindDebuggerWithID (lldb::user_id_t id); + + static lldb::DebuggerSP + FindDebuggerWithInstanceName (const ConstString &instance_name); + + static size_t + GetNumDebuggers(); + + static lldb::DebuggerSP + GetDebuggerAtIndex (size_t index); + + static bool + FormatPrompt (const char *format, + const SymbolContext *sc, + const ExecutionContext *exe_ctx, + const Address *addr, + Stream &s, + ValueObject* valobj = NULL); + + + void + CleanUpInputReaders (); + + static int + TestDebuggerRefCount (); + + bool + GetCloseInputOnEOF () const; + + void + SetCloseInputOnEOF (bool b); + + bool + EnableLog (const char *channel, const char **categories, const char *log_file, uint32_t log_options, Stream &error_stream); + + void + SetLoggingCallback (lldb::LogOutputCallback log_callback, void *baton); + + + //---------------------------------------------------------------------- + // Properties Functions + //---------------------------------------------------------------------- + enum StopDisassemblyType + { + eStopDisassemblyTypeNever = 0, + eStopDisassemblyTypeNoSource, + eStopDisassemblyTypeAlways + }; + + virtual Error + SetPropertyValue (const ExecutionContext *exe_ctx, + VarSetOperationType op, + const char *property_path, + const char *value); + + bool + GetAutoConfirm () const; + + const char * + GetFrameFormat() const; + + const char * + GetThreadFormat() const; + + lldb::ScriptLanguage + GetScriptLanguage() const; + + bool + SetScriptLanguage (lldb::ScriptLanguage script_lang); + + uint32_t + GetTerminalWidth () const; + + bool + SetTerminalWidth (uint32_t term_width); + + const char * + GetPrompt() const; + + void + SetPrompt(const char *p); + + bool + GetUseExternalEditor () const; + + bool + SetUseExternalEditor (bool use_external_editor_p); + + bool + GetUseColor () const; + + bool + SetUseColor (bool use_color); + + uint32_t + GetStopSourceLineCount (bool before) const; + + StopDisassemblyType + GetStopDisassemblyDisplay () const; + + uint32_t + GetDisassemblyLineCount () const; + + bool + GetNotifyVoid () const; + + + const ConstString & + GetInstanceName() + { + return m_instance_name; + } + + typedef bool (*LLDBCommandPluginInit) (lldb::SBDebugger& debugger); + + bool + LoadPlugin (const FileSpec& spec, Error& error); + +protected: + + static void + DispatchInputCallback (void *baton, const void *bytes, size_t bytes_len); + + lldb::InputReaderSP + GetCurrentInputReader (); + + void + ActivateInputReader (const lldb::InputReaderSP &reader_sp); + + bool + CheckIfTopInputReaderIsDone (); + + SourceManager::SourceFileCache & + GetSourceFileCache () + { + return m_source_file_cache; + } + Communication m_input_comm; + StreamFile m_input_file; + StreamFile m_output_file; + StreamFile m_error_file; + TerminalState m_terminal_state; + TargetList m_target_list; + PlatformList m_platform_list; + Listener m_listener; + std::unique_ptr m_source_manager_ap; // This is a scratch source manager that we return if we have no targets. + SourceManager::SourceFileCache m_source_file_cache; // All the source managers for targets created in this debugger used this shared + // source file cache. + std::unique_ptr m_command_interpreter_ap; + + InputReaderStack m_input_reader_stack; + std::string m_input_reader_data; + typedef std::map LogStreamMap; + LogStreamMap m_log_streams; + lldb::StreamSP m_log_callback_stream_sp; + ConstString m_instance_name; + typedef std::vector LoadedPluginsList; + LoadedPluginsList m_loaded_plugins; + + void + InstanceInitialize (); + +private: + + // Use Debugger::CreateInstance() to get a shared pointer to a new + // debugger object + Debugger (lldb::LogOutputCallback m_log_callback, void *baton); + + DISALLOW_COPY_AND_ASSIGN (Debugger); + +}; + +} // namespace lldb_private + +#endif // #if defined(__cplusplus) +#endif // liblldb_Debugger_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Core/Disassembler.h b/contrib/llvm/tools/lldb/include/lldb/Core/Disassembler.h new file mode 100644 index 00000000000..d6e90071dc5 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Core/Disassembler.h @@ -0,0 +1,422 @@ +//===-- Disassembler.h ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_Disassembler_h_ +#define liblldb_Disassembler_h_ + +// C Includes +// C++ Includes +#include +#include + +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private.h" +#include "lldb/Core/Address.h" +#include "lldb/Core/ArchSpec.h" +#include "lldb/Core/EmulateInstruction.h" +#include "lldb/Core/Opcode.h" +#include "lldb/Core/PluginInterface.h" +#include "lldb/Interpreter/OptionValue.h" + +namespace lldb_private { + +class Instruction +{ +public: + Instruction (const Address &address, + lldb::AddressClass addr_class = lldb::eAddressClassInvalid); + + virtual + ~Instruction(); + + const Address & + GetAddress () const + { + return m_address; + } + + const char * + GetMnemonic (const ExecutionContext* exe_ctx) + { + CalculateMnemonicOperandsAndCommentIfNeeded (exe_ctx); + return m_opcode_name.c_str(); + } + const char * + GetOperands (const ExecutionContext* exe_ctx) + { + CalculateMnemonicOperandsAndCommentIfNeeded (exe_ctx); + return m_mnemonics.c_str(); + } + + const char * + GetComment (const ExecutionContext* exe_ctx) + { + CalculateMnemonicOperandsAndCommentIfNeeded (exe_ctx); + return m_comment.c_str(); + } + + virtual void + CalculateMnemonicOperandsAndComment (const ExecutionContext* exe_ctx) = 0; + + lldb::AddressClass + GetAddressClass (); + + void + SetAddress (const Address &addr) + { + // Invalidate the address class to lazily discover + // it if we need to. + m_address_class = lldb::eAddressClassInvalid; + m_address = addr; + } + + virtual void + Dump (Stream *s, + uint32_t max_opcode_byte_size, + bool show_address, + bool show_bytes, + const ExecutionContext* exe_ctx); + + virtual bool + DoesBranch () = 0; + + virtual size_t + Decode (const Disassembler &disassembler, + const DataExtractor& data, + lldb::offset_t data_offset) = 0; + + virtual void + SetDescription (const char *) {} // May be overridden in sub-classes that have descriptions. + + lldb::OptionValueSP + ReadArray (FILE *in_file, Stream *out_stream, OptionValue::Type data_type); + + lldb::OptionValueSP + ReadDictionary (FILE *in_file, Stream *out_stream); + + bool + DumpEmulation (const ArchSpec &arch); + + virtual bool + TestEmulation (Stream *stream, const char *test_file_name); + + bool + Emulate (const ArchSpec &arch, + uint32_t evaluate_options, + void *baton, + EmulateInstruction::ReadMemoryCallback read_mem_callback, + EmulateInstruction::WriteMemoryCallback write_mem_calback, + EmulateInstruction::ReadRegisterCallback read_reg_callback, + EmulateInstruction::WriteRegisterCallback write_reg_callback); + + const Opcode & + GetOpcode () const + { + return m_opcode; + } + + uint32_t + GetData (DataExtractor &data); + +protected: + Address m_address; // The section offset address of this instruction + // We include an address class in the Instruction class to + // allow the instruction specify the eAddressClassCodeAlternateISA + // (currently used for thumb), and also to specify data (eAddressClassData). + // The usual value will be eAddressClassCode, but often when + // disassembling memory, you might run into data. This can + // help us to disassemble appropriately. +private: + lldb::AddressClass m_address_class; // Use GetAddressClass () accessor function! +protected: + Opcode m_opcode; // The opcode for this instruction + std::string m_opcode_name; + std::string m_mnemonics; + std::string m_comment; + bool m_calculated_strings; + + void + CalculateMnemonicOperandsAndCommentIfNeeded (const ExecutionContext* exe_ctx) + { + if (!m_calculated_strings) + { + m_calculated_strings = true; + CalculateMnemonicOperandsAndComment(exe_ctx); + } + } +}; + + +class InstructionList +{ +public: + InstructionList(); + ~InstructionList(); + + size_t + GetSize() const; + + uint32_t + GetMaxOpcocdeByteSize () const; + + lldb::InstructionSP + GetInstructionAtIndex (size_t idx) const; + + uint32_t + GetIndexOfNextBranchInstruction(uint32_t start) const; + + uint32_t + GetIndexOfInstructionAtLoadAddress (lldb::addr_t load_addr, Target &target); + + void + Clear(); + + void + Append (lldb::InstructionSP &inst_sp); + + void + Dump (Stream *s, + bool show_address, + bool show_bytes, + const ExecutionContext* exe_ctx); + +private: + typedef std::vector collection; + typedef collection::iterator iterator; + typedef collection::const_iterator const_iterator; + + collection m_instructions; +}; + +class PseudoInstruction : + public Instruction +{ +public: + + PseudoInstruction (); + + virtual + ~PseudoInstruction (); + + virtual bool + DoesBranch (); + + virtual void + CalculateMnemonicOperandsAndComment (const ExecutionContext* exe_ctx) + { + // TODO: fill this in and put opcode name into Instruction::m_opcode_name, + // mnemonic into Instruction::m_mnemonics, and any comment into + // Instruction::m_comment + } + + virtual size_t + Decode (const Disassembler &disassembler, + const DataExtractor &data, + lldb::offset_t data_offset); + + void + SetOpcode (size_t opcode_size, void *opcode_data); + + virtual void + SetDescription (const char *description); + +protected: + std::string m_description; + + DISALLOW_COPY_AND_ASSIGN (PseudoInstruction); +}; + +class Disassembler : + public std::enable_shared_from_this, + public PluginInterface +{ +public: + + enum + { + eOptionNone = 0u, + eOptionShowBytes = (1u << 0), + eOptionRawOuput = (1u << 1), + eOptionMarkPCSourceLine = (1u << 2), // Mark the source line that contains the current PC (mixed mode only) + eOptionMarkPCAddress = (1u << 3) // Mark the disassembly line the contains the PC + }; + + enum HexImmediateStyle + { + eHexStyleC, + eHexStyleAsm, + }; + + // FindPlugin should be lax about the flavor string (it is too annoying to have various internal uses of the + // disassembler fail because the global flavor string gets set wrong. Instead, if you get a flavor string you + // don't understand, use the default. Folks who care to check can use the FlavorValidForArchSpec method on the + // disassembler they got back. + static lldb::DisassemblerSP + FindPlugin (const ArchSpec &arch, const char *flavor, const char *plugin_name); + + // This version will use the value in the Target settings if flavor is NULL; + static lldb::DisassemblerSP + FindPluginForTarget(const lldb::TargetSP target_sp, const ArchSpec &arch, const char *flavor, const char *plugin_name); + + static lldb::DisassemblerSP + DisassembleRange (const ArchSpec &arch, + const char *plugin_name, + const char *flavor, + const ExecutionContext &exe_ctx, + const AddressRange &disasm_range); + + static lldb::DisassemblerSP + DisassembleBytes (const ArchSpec &arch, + const char *plugin_name, + const char *flavor, + const Address &start, + const void *bytes, + size_t length, + uint32_t max_num_instructions, + bool data_from_file); + + static bool + Disassemble (Debugger &debugger, + const ArchSpec &arch, + const char *plugin_name, + const char *flavor, + const ExecutionContext &exe_ctx, + const AddressRange &range, + uint32_t num_instructions, + uint32_t num_mixed_context_lines, + uint32_t options, + Stream &strm); + + static bool + Disassemble (Debugger &debugger, + const ArchSpec &arch, + const char *plugin_name, + const char *flavor, + const ExecutionContext &exe_ctx, + const Address &start, + uint32_t num_instructions, + uint32_t num_mixed_context_lines, + uint32_t options, + Stream &strm); + + static size_t + Disassemble (Debugger &debugger, + const ArchSpec &arch, + const char *plugin_name, + const char *flavor, + const ExecutionContext &exe_ctx, + SymbolContextList &sc_list, + uint32_t num_instructions, + uint32_t num_mixed_context_lines, + uint32_t options, + Stream &strm); + + static bool + Disassemble (Debugger &debugger, + const ArchSpec &arch, + const char *plugin_name, + const char *flavor, + const ExecutionContext &exe_ctx, + const ConstString &name, + Module *module, + uint32_t num_instructions, + uint32_t num_mixed_context_lines, + uint32_t options, + Stream &strm); + + static bool + Disassemble (Debugger &debugger, + const ArchSpec &arch, + const char *plugin_name, + const char *flavor, + const ExecutionContext &exe_ctx, + uint32_t num_instructions, + uint32_t num_mixed_context_lines, + uint32_t options, + Stream &strm); + + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + Disassembler(const ArchSpec &arch, const char *flavor); + virtual ~Disassembler(); + + typedef const char * (*SummaryCallback)(const Instruction& inst, ExecutionContext *exe_context, void *user_data); + + static bool + PrintInstructions (Disassembler *disasm_ptr, + Debugger &debugger, + const ArchSpec &arch, + const ExecutionContext &exe_ctx, + uint32_t num_instructions, + uint32_t num_mixed_context_lines, + uint32_t options, + Stream &strm); + + size_t + ParseInstructions (const ExecutionContext *exe_ctx, + const AddressRange &range, + Stream *error_strm_ptr, + bool prefer_file_cache); + + size_t + ParseInstructions (const ExecutionContext *exe_ctx, + const Address &range, + uint32_t num_instructions, + bool prefer_file_cache); + + virtual size_t + DecodeInstructions (const Address &base_addr, + const DataExtractor& data, + lldb::offset_t data_offset, + size_t num_instructions, + bool append, + bool data_from_file) = 0; + + InstructionList & + GetInstructionList (); + + const InstructionList & + GetInstructionList () const; + + const ArchSpec & + GetArchitecture () const + { + return m_arch; + } + + const char * + GetFlavor () const + { + return m_flavor.c_str(); + } + + virtual bool + FlavorValidForArchSpec (const lldb_private::ArchSpec &arch, const char *flavor) = 0; + +protected: + //------------------------------------------------------------------ + // Classes that inherit from Disassembler can see and modify these + //------------------------------------------------------------------ + const ArchSpec m_arch; + InstructionList m_instruction_list; + lldb::addr_t m_base_addr; + std::string m_flavor; + +private: + //------------------------------------------------------------------ + // For Disassembler only + //------------------------------------------------------------------ + DISALLOW_COPY_AND_ASSIGN (Disassembler); +}; + +} // namespace lldb_private + +#endif // liblldb_Disassembler_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Core/EmulateInstruction.h b/contrib/llvm/tools/lldb/include/lldb/Core/EmulateInstruction.h new file mode 100644 index 00000000000..08b97e42491 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Core/EmulateInstruction.h @@ -0,0 +1,641 @@ +//===-- EmulateInstruction.h ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef lldb_EmulateInstruction_h_ +#define lldb_EmulateInstruction_h_ + +#include + +#include "lldb/lldb-private.h" +#include "lldb/lldb-public.h" +#include "lldb/Core/ArchSpec.h" +#include "lldb/Core/PluginInterface.h" +#include "lldb/Core/Opcode.h" +#include "lldb/Core/RegisterValue.h" + +//---------------------------------------------------------------------- +/// @class EmulateInstruction EmulateInstruction.h "lldb/Core/EmulateInstruction.h" +/// @brief A class that allows emulation of CPU opcodes. +/// +/// This class is a plug-in interface that is accessed through the +/// standard static FindPlugin function call in the EmulateInstruction +/// class. The FindPlugin takes a target triple and returns a new object +/// if there is a plug-in that supports the architecture and OS. Four +/// callbacks and a baton are provided. The four callbacks are read +/// register, write register, read memory and write memory. +/// +/// This class is currently designed for these main use cases: +/// - Auto generation of Call Frame Information (CFI) from assembly code +/// - Predicting single step breakpoint locations +/// - Emulating instructions for breakpoint traps +/// +/// Objects can be asked to read an instruction which will cause a call +/// to the read register callback to get the PC, followed by a read +/// memory call to read the opcode. If ReadInstruction () returns true, +/// then a call to EmulateInstruction::EvaluateInstruction () can be +/// made. At this point the EmulateInstruction subclass will use all of +/// the callbacks to emulate an instruction. +/// +/// Clients that provide the callbacks can either do the read/write +/// registers/memory to actually emulate the instruction on a real or +/// virtual CPU, or watch for the EmulateInstruction::Context which +/// is context for the read/write register/memory which explains why +/// the callback is being called. Examples of a context are: +/// "pushing register 3 onto the stack at offset -12", or "adjusting +/// stack pointer by -16". This extra context allows the generation of +/// CFI information from assembly code without having to actually do +/// the read/write register/memory. +/// +/// Clients must be prepared that not all instructions for an +/// Instruction Set Architecture (ISA) will be emulated. +/// +/// Subclasses at the very least should implement the instructions that +/// save and restore registers onto the stack and adjustment to the stack +/// pointer. By just implementing a few instructions for an ISA that are +/// the typical prologue opcodes, you can then generate CFI using a +/// class that will soon be available. +/// +/// Implementing all of the instructions that affect the PC can then +/// allow single step prediction support. +/// +/// Implementing all of the instructions allows for emulation of opcodes +/// for breakpoint traps and will pave the way for "thread centric" +/// debugging. The current debugging model is "process centric" where +/// all threads must be stopped when any thread is stopped; when +/// hitting software breakpoints we must disable the breakpoint by +/// restoring the original breakpoint opcde, single stepping and +/// restoring the breakpoint trap. If all threads were allowed to run +/// then other threads could miss the breakpoint. +/// +/// This class centralizes the code that usually is done in separate +/// code paths in a debugger (single step prediction, finding save +/// restore locations of registers for unwinding stack frame variables) +/// and emulating the intruction is just a bonus. +//---------------------------------------------------------------------- + +namespace lldb_private { + +class EmulateInstruction : + public PluginInterface +{ +public: + + static EmulateInstruction* + FindPlugin (const ArchSpec &arch, + InstructionType supported_inst_type, + const char *plugin_name); + + enum ContextType + { + eContextInvalid = 0, + // Read an instruciton opcode from memory + eContextReadOpcode, + + // Usually used for writing a register value whose source value is an + // immediate + eContextImmediate, + + // Exclusively used when saving a register to the stack as part of the + // prologue + eContextPushRegisterOnStack, + + // Exclusively used when restoring a register off the stack as part of + // the epilogue + eContextPopRegisterOffStack, + + // Add or subtract a value from the stack + eContextAdjustStackPointer, + + // Adjust the frame pointer for the current frame + eContextSetFramePointer, + + // Add or subtract a value from a base address register (other than SP) + eContextAdjustBaseRegister, + + // Add or subtract a value from the PC or store a value to the PC. + eContextAdjustPC, + + // Used in WriteRegister callbacks to indicate where the + eContextRegisterPlusOffset, + + // Used in WriteMemory callback to indicate where the data came from + eContextRegisterStore, + + eContextRegisterLoad, + + // Used when performing a PC-relative branch where the + eContextRelativeBranchImmediate, + + // Used when performing an absolute branch where the + eContextAbsoluteBranchRegister, + + // Used when performing a supervisor call to an operating system to + // provide a service: + eContextSupervisorCall, + + // Used when performing a MemU operation to read the PC-relative offset + // from an address. + eContextTableBranchReadMemory, + + // Used when random bits are written into a register + eContextWriteRegisterRandomBits, + + // Used when random bits are written to memory + eContextWriteMemoryRandomBits, + + eContextArithmetic, + + eContextAdvancePC, + + eContextReturnFromException + }; + + enum InfoType { + eInfoTypeRegisterPlusOffset, + eInfoTypeRegisterPlusIndirectOffset, + eInfoTypeRegisterToRegisterPlusOffset, + eInfoTypeRegisterToRegisterPlusIndirectOffset, + eInfoTypeRegisterRegisterOperands, + eInfoTypeOffset, + eInfoTypeRegister, + eInfoTypeImmediate, + eInfoTypeImmediateSigned, + eInfoTypeAddress, + eInfoTypeISAAndImmediate, + eInfoTypeISAAndImmediateSigned, + eInfoTypeISA, + eInfoTypeNoArgs + } InfoType; + + struct Context + { + ContextType type; + enum InfoType info_type; + union + { + struct RegisterPlusOffset + { + RegisterInfo reg; // base register + int64_t signed_offset; // signed offset added to base register + } RegisterPlusOffset; + + struct RegisterPlusIndirectOffset + { + RegisterInfo base_reg; // base register number + RegisterInfo offset_reg; // offset register kind + } RegisterPlusIndirectOffset; + + struct RegisterToRegisterPlusOffset + { + RegisterInfo data_reg; // source/target register for data + RegisterInfo base_reg; // base register for address calculation + int64_t offset; // offset for address calculation + } RegisterToRegisterPlusOffset; + + struct RegisterToRegisterPlusIndirectOffset + { + RegisterInfo base_reg; // base register for address calculation + RegisterInfo offset_reg; // offset register for address calculation + RegisterInfo data_reg; // source/target register for data + } RegisterToRegisterPlusIndirectOffset; + + struct RegisterRegisterOperands + { + RegisterInfo operand1; // register containing first operand for binary op + RegisterInfo operand2; // register containing second operand for binary op + } RegisterRegisterOperands; + + int64_t signed_offset; // signed offset by which to adjust self (for registers only) + + RegisterInfo reg; // plain register + + uint64_t unsigned_immediate;// unsigned immediate value + int64_t signed_immediate; // signed immediate value + + lldb::addr_t address; // direct address + + struct ISAAndImmediate + { + uint32_t isa; + uint32_t unsigned_data32; // immdiate data + } ISAAndImmediate; + + struct ISAAndImmediateSigned + { + uint32_t isa; + int32_t signed_data32; // signed immdiate data + } ISAAndImmediateSigned; + + uint32_t isa; + + } info; + + Context () : + type (eContextInvalid), + info_type (eInfoTypeNoArgs) + { + } + + void + SetRegisterPlusOffset (RegisterInfo base_reg, + int64_t signed_offset) + { + info_type = eInfoTypeRegisterPlusOffset; + info.RegisterPlusOffset.reg = base_reg; + info.RegisterPlusOffset.signed_offset = signed_offset; + } + + void + SetRegisterPlusIndirectOffset (RegisterInfo base_reg, + RegisterInfo offset_reg) + { + info_type = eInfoTypeRegisterPlusIndirectOffset; + info.RegisterPlusIndirectOffset.base_reg = base_reg; + info.RegisterPlusIndirectOffset.offset_reg = offset_reg; + } + + void + SetRegisterToRegisterPlusOffset (RegisterInfo data_reg, + RegisterInfo base_reg, + int64_t offset) + { + info_type = eInfoTypeRegisterToRegisterPlusOffset; + info.RegisterToRegisterPlusOffset.data_reg = data_reg; + info.RegisterToRegisterPlusOffset.base_reg = base_reg; + info.RegisterToRegisterPlusOffset.offset = offset; + } + + void + SetRegisterToRegisterPlusIndirectOffset (RegisterInfo base_reg, + RegisterInfo offset_reg, + RegisterInfo data_reg) + { + info_type = eInfoTypeRegisterToRegisterPlusIndirectOffset; + info.RegisterToRegisterPlusIndirectOffset.base_reg = base_reg; + info.RegisterToRegisterPlusIndirectOffset.offset_reg = offset_reg; + info.RegisterToRegisterPlusIndirectOffset.data_reg = data_reg; + } + + void + SetRegisterRegisterOperands (RegisterInfo op1_reg, + RegisterInfo op2_reg) + { + info_type = eInfoTypeRegisterRegisterOperands; + info.RegisterRegisterOperands.operand1 = op1_reg; + info.RegisterRegisterOperands.operand2 = op2_reg; + } + + void + SetOffset (int64_t signed_offset) + { + info_type = eInfoTypeOffset; + info.signed_offset = signed_offset; + } + + void + SetRegister (RegisterInfo reg) + { + info_type = eInfoTypeRegister; + info.reg = reg; + } + + void + SetImmediate (uint64_t immediate) + { + info_type = eInfoTypeImmediate; + info.unsigned_immediate = immediate; + } + + void + SetImmediateSigned (int64_t signed_immediate) + { + info_type = eInfoTypeImmediateSigned; + info.signed_immediate = signed_immediate; + } + + void + SetAddress (lldb::addr_t address) + { + info_type = eInfoTypeAddress; + info.address = address; + } + void + SetISAAndImmediate (uint32_t isa, uint32_t data) + { + info_type = eInfoTypeISAAndImmediate; + info.ISAAndImmediate.isa = isa; + info.ISAAndImmediate.unsigned_data32 = data; + } + + void + SetISAAndImmediateSigned (uint32_t isa, int32_t data) + { + info_type = eInfoTypeISAAndImmediateSigned; + info.ISAAndImmediateSigned.isa = isa; + info.ISAAndImmediateSigned.signed_data32 = data; + } + + void + SetISA (uint32_t isa) + { + info_type = eInfoTypeISA; + info.isa = isa; + } + + void + SetNoArgs () + { + info_type = eInfoTypeNoArgs; + } + + void + Dump (Stream &s, + EmulateInstruction *instruction) const; + + }; + + typedef size_t (*ReadMemoryCallback) (EmulateInstruction *instruction, + void *baton, + const Context &context, + lldb::addr_t addr, + void *dst, + size_t length); + + typedef size_t (*WriteMemoryCallback) (EmulateInstruction *instruction, + void *baton, + const Context &context, + lldb::addr_t addr, + const void *dst, + size_t length); + + typedef bool (*ReadRegisterCallback) (EmulateInstruction *instruction, + void *baton, + const RegisterInfo *reg_info, + RegisterValue ®_value); + + typedef bool (*WriteRegisterCallback) (EmulateInstruction *instruction, + void *baton, + const Context &context, + const RegisterInfo *reg_info, + const RegisterValue ®_value); + + EmulateInstruction (const ArchSpec &arch); + + virtual ~EmulateInstruction() + { + } + //---------------------------------------------------------------------- + // Mandatory overrides + //---------------------------------------------------------------------- + virtual bool + SupportsEmulatingIntructionsOfType (InstructionType inst_type) = 0; + + virtual bool + SetTargetTriple (const ArchSpec &arch) = 0; + + virtual bool + ReadInstruction () = 0; + + virtual bool + EvaluateInstruction (uint32_t evaluate_options) = 0; + + virtual bool + TestEmulation (Stream *out_stream, ArchSpec &arch, OptionValueDictionary *test_data) = 0; + + virtual bool + GetRegisterInfo (uint32_t reg_kind, uint32_t reg_num, RegisterInfo ®_info) = 0; + + //---------------------------------------------------------------------- + // Optional overrides + //---------------------------------------------------------------------- + virtual bool + SetInstruction (const Opcode &insn_opcode, const Address &inst_addr, Target *target); + + virtual bool + CreateFunctionEntryUnwind (UnwindPlan &unwind_plan); + + static const char * + TranslateRegister (uint32_t reg_kind, uint32_t reg_num, std::string ®_name); + + //---------------------------------------------------------------------- + // RegisterInfo variants + //---------------------------------------------------------------------- + bool + ReadRegister (const RegisterInfo *reg_info, + RegisterValue& reg_value); + + uint64_t + ReadRegisterUnsigned (const RegisterInfo *reg_info, + uint64_t fail_value, + bool *success_ptr); + + bool + WriteRegister (const Context &context, + const RegisterInfo *ref_info, + const RegisterValue& reg_value); + + bool + WriteRegisterUnsigned (const Context &context, + const RegisterInfo *reg_info, + uint64_t reg_value); + + //---------------------------------------------------------------------- + // Register kind and number variants + //---------------------------------------------------------------------- + bool + ReadRegister (uint32_t reg_kind, + uint32_t reg_num, + RegisterValue& reg_value); + + bool + WriteRegister (const Context &context, + uint32_t reg_kind, + uint32_t reg_num, + const RegisterValue& reg_value); + + uint64_t + ReadRegisterUnsigned (uint32_t reg_kind, + uint32_t reg_num, + uint64_t fail_value, + bool *success_ptr); + + bool + WriteRegisterUnsigned (const Context &context, + uint32_t reg_kind, + uint32_t reg_num, + uint64_t reg_value); + + + size_t + ReadMemory (const Context &context, + lldb::addr_t addr, + void *dst, + size_t dst_len); + + uint64_t + ReadMemoryUnsigned (const Context &context, + lldb::addr_t addr, + size_t byte_size, + uint64_t fail_value, + bool *success_ptr); + + bool + WriteMemory (const Context &context, + lldb::addr_t addr, + const void *src, + size_t src_len); + + bool + WriteMemoryUnsigned (const Context &context, + lldb::addr_t addr, + uint64_t uval, + size_t uval_byte_size); + + uint32_t + GetAddressByteSize () const + { + return m_arch.GetAddressByteSize(); + } + + lldb::ByteOrder + GetByteOrder () const + { + return m_arch.GetByteOrder(); + } + + const Opcode & + GetOpcode () const + { + return m_opcode; + } + + lldb::addr_t + GetAddress () const + { + return m_addr; + } + + const ArchSpec & + GetArchitecture () const + { + return m_arch; + } + + + static size_t + ReadMemoryFrame (EmulateInstruction *instruction, + void *baton, + const Context &context, + lldb::addr_t addr, + void *dst, + size_t length); + + static size_t + WriteMemoryFrame (EmulateInstruction *instruction, + void *baton, + const Context &context, + lldb::addr_t addr, + const void *dst, + size_t length); + + static bool + ReadRegisterFrame (EmulateInstruction *instruction, + void *baton, + const RegisterInfo *reg_info, + RegisterValue ®_value); + + + static bool + WriteRegisterFrame (EmulateInstruction *instruction, + void *baton, + const Context &context, + const RegisterInfo *reg_info, + const RegisterValue ®_value); + + static size_t + ReadMemoryDefault (EmulateInstruction *instruction, + void *baton, + const Context &context, + lldb::addr_t addr, + void *dst, + size_t length); + + static size_t + WriteMemoryDefault (EmulateInstruction *instruction, + void *baton, + const Context &context, + lldb::addr_t addr, + const void *dst, + size_t length); + + static bool + ReadRegisterDefault (EmulateInstruction *instruction, + void *baton, + const RegisterInfo *reg_info, + RegisterValue ®_value); + + + static bool + WriteRegisterDefault (EmulateInstruction *instruction, + void *baton, + const Context &context, + const RegisterInfo *reg_info, + const RegisterValue ®_value); + + void + SetBaton (void *baton); + + void + SetCallbacks (ReadMemoryCallback read_mem_callback, + WriteMemoryCallback write_mem_callback, + ReadRegisterCallback read_reg_callback, + WriteRegisterCallback write_reg_callback); + + void + SetReadMemCallback (ReadMemoryCallback read_mem_callback); + + void + SetWriteMemCallback (WriteMemoryCallback write_mem_callback); + + void + SetReadRegCallback (ReadRegisterCallback read_reg_callback); + + void + SetWriteRegCallback (WriteRegisterCallback write_reg_callback); + + static bool + GetBestRegisterKindAndNumber (const RegisterInfo *reg_info, + uint32_t ®_kind, + uint32_t ®_num); + + static uint32_t + GetInternalRegisterNumber (RegisterContext *reg_ctx, + const RegisterInfo ®_info); + +protected: + ArchSpec m_arch; + void * m_baton; + ReadMemoryCallback m_read_mem_callback; + WriteMemoryCallback m_write_mem_callback; + ReadRegisterCallback m_read_reg_callback; + WriteRegisterCallback m_write_reg_callback; + lldb::addr_t m_addr; + Opcode m_opcode; + + +private: + //------------------------------------------------------------------ + // For EmulateInstruction only + //------------------------------------------------------------------ + DISALLOW_COPY_AND_ASSIGN (EmulateInstruction); +}; + +} // namespace lldb_private + +#endif // lldb_EmulateInstruction_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Core/Error.h b/contrib/llvm/tools/lldb/include/lldb/Core/Error.h new file mode 100644 index 00000000000..9e45d5f555d --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Core/Error.h @@ -0,0 +1,312 @@ +//===-- Error.h -------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef __DCError_h__ +#define __DCError_h__ +#if defined(__cplusplus) + +#if defined (__APPLE__) +#include +#endif +#include +#include +#include + +#include "lldb/lldb-private.h" + +namespace lldb_private { + +class Log; + +//---------------------------------------------------------------------- +/// @class Error Error.h "lldb/Core/Error.h" +/// @brief An error handling class. +/// +/// This class is designed to be able to hold any error code that can be +/// encountered on a given platform. The errors are stored as a value +/// of type Error::ValueType. This value should be large enough to hold +/// any and all errors that the class supports. Each error has an +/// associated type that is of type lldb::ErrorType. New types +/// can be added to support new error types, and architecture specific +/// types can be enabled. In the future we may wish to switch to a +/// registration mechanism where new error types can be registered at +/// runtime instead of a hard coded scheme. +/// +/// All errors in this class also know how to generate a string +/// representation of themselves for printing results and error codes. +/// The string value will be fetched on demand and its string value will +/// be cached until the error is cleared of the value of the error +/// changes. +//---------------------------------------------------------------------- +class Error +{ +public: + //------------------------------------------------------------------ + /// Every error value that this object can contain needs to be able + /// to fit into ValueType. + //------------------------------------------------------------------ + typedef uint32_t ValueType; + + //------------------------------------------------------------------ + /// Default constructor. + /// + /// Initialize the error object with a generic success value. + /// + /// @param[in] err + /// An error code. + /// + /// @param[in] type + /// The type for \a err. + //------------------------------------------------------------------ + Error (); + + explicit + Error (ValueType err, lldb::ErrorType type = lldb::eErrorTypeGeneric); + + explicit + Error (const char* err_str); + + Error (const Error &rhs); + //------------------------------------------------------------------ + /// Assignment operator. + /// + /// @param[in] err + /// An error code. + /// + /// @return + /// A const reference to this object. + //------------------------------------------------------------------ + const Error& + operator = (const Error& rhs); + + + //------------------------------------------------------------------ + /// Assignment operator from a kern_return_t. + /// + /// Sets the type to \c MachKernel and the error code to \a err. + /// + /// @param[in] err + /// A mach error code. + /// + /// @return + /// A const reference to this object. + //------------------------------------------------------------------ + const Error& + operator = (uint32_t err); + + ~Error(); + + //------------------------------------------------------------------ + /// Get the error string associated with the current error. + // + /// Gets the error value as a NULL terminated C string. The error + /// string will be fetched and cached on demand. The error string + /// will be retrieved from a callback that is appropriate for the + /// type of the error and will be cached until the error value is + /// changed or cleared. + /// + /// @return + /// The error as a NULL terminated C string value if the error + /// is valid and is able to be converted to a string value, + /// NULL otherwise. + //------------------------------------------------------------------ + const char * + AsCString (const char *default_error_str = "unknown error") const; + + //------------------------------------------------------------------ + /// Clear the object state. + /// + /// Reverts the state of this object to contain a generic success + /// value and frees any cached error string value. + //------------------------------------------------------------------ + void + Clear (); + + //------------------------------------------------------------------ + /// Test for error condition. + /// + /// @return + /// \b true if this object contains an error, \b false + /// otherwise. + //------------------------------------------------------------------ + bool + Fail () const; + + //------------------------------------------------------------------ + /// Access the error value. + /// + /// @return + /// The error value. + //------------------------------------------------------------------ + ValueType + GetError () const; + + //------------------------------------------------------------------ + /// Access the error type. + /// + /// @return + /// The error type enumeration value. + //------------------------------------------------------------------ + lldb::ErrorType + GetType () const; + + //------------------------------------------------------------------ + /// Log an error to Log(). + /// + /// Log the error given a formatted string \a format. If the this + /// object contains an error code, update the error string to + /// contain the prefix "error: ", followed by the formatted string, + /// followed by the error value and any string that describes the + /// error value. This allows more context to be given to an error + /// string that remains cached in this object. Logging always occurs + /// even when the error code contains a non-error value. + /// + /// @param[in] format + /// A printf style format string. + /// + /// @param[in] ... + /// Variable arguments that are needed for the printf style + /// format string \a format. + //------------------------------------------------------------------ + void + PutToLog (Log *log, const char *format, ...) __attribute__ ((format (printf, 3, 4))); + + //------------------------------------------------------------------ + /// Log an error to Log() if the error value is an error. + /// + /// Log the error given a formatted string \a format only if the + /// error value in this object describes an error condition. If the + /// this object contains an error, update the error string to + /// contain the prefix "error: " followed by the formatted string, + /// followed by the error value and any string that describes the + /// error value. This allows more context to be given to an error + /// string that remains cached in this object. + /// + /// @param[in] format + /// A printf style format string. + /// + /// @param[in] ... + /// Variable arguments that are needed for the printf style + /// format string \a format. + //------------------------------------------------------------------ + void + LogIfError (Log *log, const char *format, ...) __attribute__ ((format (printf, 3, 4))); + + //------------------------------------------------------------------ + /// Set accessor from a kern_return_t. + /// + /// Set accesssor for the error value to \a err and the error type + /// to \c MachKernel. + /// + /// @param[in] err + /// A mach error code. + //------------------------------------------------------------------ + void + SetMachError (uint32_t err); + + //------------------------------------------------------------------ + /// Set accesssor with an error value and type. + /// + /// Set accesssor for the error value to \a err and the error type + /// to \a type. + /// + /// @param[in] err + /// A mach error code. + /// + /// @param[in] type + /// The type for \a err. + //------------------------------------------------------------------ + void + SetError (ValueType err, lldb::ErrorType type); + + //------------------------------------------------------------------ + /// Set the current error to errno. + /// + /// Update the error value to be \c errno and update the type to + /// be \c Error::POSIX. + //------------------------------------------------------------------ + void + SetErrorToErrno (); + + //------------------------------------------------------------------ + /// Set the current error to a generic error. + /// + /// Update the error value to be \c LLDB_GENERIC_ERROR and update the + /// type to be \c Error::Generic. + //------------------------------------------------------------------ + void + SetErrorToGenericError (); + + //------------------------------------------------------------------ + /// Set the current error string to \a err_str. + /// + /// Set accessor for the error string value for a generic errors, + /// or to supply additional details above and beyond the standard + /// error strings that the standard type callbacks typically + /// provide. This allows custom strings to be supplied as an + /// error explanation. The error string value will remain until the + /// error value is cleared or a new error value/type is assigned. + /// + /// @param err_str + /// The new custom error string to copy and cache. + //------------------------------------------------------------------ + void + SetErrorString (const char *err_str); + + //------------------------------------------------------------------ + /// Set the current error string to a formatted error string. + /// + /// @param format + /// A printf style format string + //------------------------------------------------------------------ + int + SetErrorStringWithFormat (const char *format, ...) __attribute__ ((format (printf, 2, 3))); + + int + SetErrorStringWithVarArg (const char *format, va_list args); + + //------------------------------------------------------------------ + /// Test for success condition. + /// + /// Returns true if the error code in this object is considered a + /// successful return value. + /// + /// @return + /// \b true if this object contains an value that describes + /// success (non-erro), \b false otherwise. + //------------------------------------------------------------------ + bool + Success () const; + + //------------------------------------------------------------------ + /// Test for a failure due to a generic interrupt. + /// + /// Returns true if the error code in this object was caused by an interrupt. + /// At present only supports Posix EINTR. + /// + /// @return + /// \b true if this object contains an value that describes + /// failure due to interrupt, \b false otherwise. + //------------------------------------------------------------------ + bool + WasInterrupted() const; + +protected: + //------------------------------------------------------------------ + /// Member variables + //------------------------------------------------------------------ + ValueType m_code; ///< Error code as an integer value. + lldb::ErrorType m_type; ///< The type of the above error code. + mutable std::string m_string; ///< A string representation of the error code. +}; + +} // namespace lldb_private + +#endif // #if defined(__cplusplus) +#endif // #ifndef __DCError_h__ diff --git a/contrib/llvm/tools/lldb/include/lldb/Core/Event.h b/contrib/llvm/tools/lldb/include/lldb/Core/Event.h new file mode 100644 index 00000000000..1c3eec0359c --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Core/Event.h @@ -0,0 +1,217 @@ +//===-- Event.h -------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_Event_h_ +#define liblldb_Event_h_ + +// C Includes +// C++ Includes +#include +#include + +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private.h" +#include "lldb/Core/ConstString.h" +#include "lldb/Host/Predicate.h" + +namespace lldb_private { + +//---------------------------------------------------------------------- +// lldb::EventData +//---------------------------------------------------------------------- +class EventData +{ + friend class Event; + +public: + EventData (); + + virtual + ~EventData(); + + virtual const ConstString & + GetFlavor () const = 0; + + virtual void + Dump (Stream *s) const; + +private: + virtual void + DoOnRemoval (Event *event_ptr) + { + } + + DISALLOW_COPY_AND_ASSIGN (EventData); + +}; + +//---------------------------------------------------------------------- +// lldb::EventDataBytes +//---------------------------------------------------------------------- +class EventDataBytes : public EventData +{ +public: + //------------------------------------------------------------------ + // Constructors + //------------------------------------------------------------------ + EventDataBytes (); + + EventDataBytes (const char *cstr); + + EventDataBytes (const void *src, size_t src_len); + + virtual + ~EventDataBytes(); + + //------------------------------------------------------------------ + // Member functions + //------------------------------------------------------------------ + virtual const ConstString & + GetFlavor () const; + + virtual void + Dump (Stream *s) const; + + const void * + GetBytes() const; + + size_t + GetByteSize() const; + + void + SetBytes (const void *src, size_t src_len); + + void + SwapBytes (std::string &new_bytes); + + void + SetBytesFromCString (const char *cstr); + + //------------------------------------------------------------------ + // Static functions + //------------------------------------------------------------------ + static const EventDataBytes * + GetEventDataFromEvent (const Event *event_ptr); + + static const void * + GetBytesFromEvent (const Event *event_ptr); + + static size_t + GetByteSizeFromEvent (const Event *event_ptr); + + static const ConstString & + GetFlavorString (); + +private: + std::string m_bytes; + + DISALLOW_COPY_AND_ASSIGN (EventDataBytes); + +}; + +//---------------------------------------------------------------------- +// lldb::Event +//---------------------------------------------------------------------- +class Event +{ + friend class Broadcaster; + friend class Listener; + friend class EventData; + +public: + + Event (Broadcaster *broadcaster, uint32_t event_type, EventData *data = NULL); + + Event (uint32_t event_type, EventData *data = NULL); + + ~Event (); + + void + Dump (Stream *s) const; + + EventData * + GetData () + { + return m_data_ap.get(); + } + + const EventData * + GetData () const + { + return m_data_ap.get(); + } + + void + SetData (EventData *new_data) + { + m_data_ap.reset (new_data); + } + + uint32_t + GetType () const + { + return m_type; + } + + void + SetType (uint32_t new_type) + { + m_type = new_type; + } + + Broadcaster * + GetBroadcaster () const + { + return m_broadcaster; + } + + bool + BroadcasterIs (Broadcaster *broadcaster) + { + return broadcaster == m_broadcaster; + } + + void + Clear() + { + m_data_ap.reset(); + } + + +private: + // This is only called by Listener when it pops an event off the queue for + // the listener. It calls the Event Data's DoOnRemoval() method, which is + // virtual and can be overridden by the specific data classes. + + void + DoOnRemoval (); + + // Called by Broadcaster::BroadcastEvent prior to letting all the listeners + // know about it update the contained broadcaster so that events can be + // popped off one queue and re-broadcast to others. + void + SetBroadcaster (Broadcaster *broadcaster) + { + m_broadcaster = broadcaster; + } + + + Broadcaster * m_broadcaster; // The broadcaster that sent this event + uint32_t m_type; // The bit describing this event + std::unique_ptr m_data_ap; // User specific data for this event + + + DISALLOW_COPY_AND_ASSIGN (Event); + Event(); // Disallow default constructor +}; + +} // namespace lldb_private + +#endif // liblldb_Event_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Core/FileLineResolver.h b/contrib/llvm/tools/lldb/include/lldb/Core/FileLineResolver.h new file mode 100644 index 00000000000..e1928f1b063 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Core/FileLineResolver.h @@ -0,0 +1,81 @@ +//===-- FileLineResolver.h --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_FileLineResolver_h_ +#define liblldb_FileLineResolver_h_ + +// Project includes +#include "lldb/Core/AddressResolver.h" +#include "lldb/Symbol/SymbolContext.h" + +namespace lldb_private { + +//---------------------------------------------------------------------- +/// @class FileLineResolver FileLineResolver.h "lldb/Core/FileLineResolver.h" +/// @brief This class finds address for source file and line. Optionally, it will look for inlined +/// instances of the file and line specification. +//---------------------------------------------------------------------- + +class FileLineResolver : + public Searcher +{ +public: + FileLineResolver () : + m_file_spec(), + m_line_number(UINT32_MAX), // Set this to zero for all lines in a file + m_sc_list (), + m_inlines (true) + { + } + + FileLineResolver (const FileSpec &resolver, + uint32_t line_no, + bool check_inlines); + + virtual + ~FileLineResolver (); + + virtual Searcher::CallbackReturn + SearchCallback (SearchFilter &filter, + SymbolContext &context, + Address *addr, + bool containing); + + virtual Searcher::Depth + GetDepth (); + + virtual void + GetDescription (Stream *s); + + const SymbolContextList & + GetFileLineMatches() + { + return m_sc_list; + } + + void + Clear(); + + void + Reset (const FileSpec &file_spec, + uint32_t line, + bool check_inlines); +protected: + FileSpec m_file_spec; // This is the file spec we are looking for. + uint32_t m_line_number; // This is the line number that we are looking for. + SymbolContextList m_sc_list; + bool m_inlines; // This determines whether the resolver looks for inlined functions or not. + +private: + DISALLOW_COPY_AND_ASSIGN(FileLineResolver); +}; + +} // namespace lldb_private + +#endif // liblldb_FileLineResolver_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Core/FileSpecList.h b/contrib/llvm/tools/lldb/include/lldb/Core/FileSpecList.h new file mode 100644 index 00000000000..f94bdae83c0 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Core/FileSpecList.h @@ -0,0 +1,243 @@ +//===-- FileSpecList.h ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_FileSpecList_h_ +#define liblldb_FileSpecList_h_ +#if defined(__cplusplus) + +#include "lldb/lldb-private.h" +#include "lldb/Host/FileSpec.h" +#include + +namespace lldb_private { + +//---------------------------------------------------------------------- +/// @class FileSpecList FileSpecList.h "lldb/Core/FileSpecList.h" +/// @brief A file collection class. +/// +/// A class that contains a mutable list of FileSpec objects. +//---------------------------------------------------------------------- +class FileSpecList +{ +public: + //------------------------------------------------------------------ + /// Default constructor. + /// + /// Initialize this object with an empty file list. + //------------------------------------------------------------------ + FileSpecList (); + + //------------------------------------------------------------------ + /// Copy constructor. + /// + /// Initialize this object with a copy of the file list from \a rhs. + /// + /// @param[in] rhs + /// A const reference to another file list object. + //------------------------------------------------------------------ + FileSpecList (const FileSpecList &rhs); + + //------------------------------------------------------------------ + /// Destructor. + //------------------------------------------------------------------ + ~FileSpecList (); + + //------------------------------------------------------------------ + /// Assignment operator. + /// + /// Replace the file list in this object with the file list from + /// \a rhs. + /// + /// @param[in] rhs + /// A file list object to copy. + /// + /// @return + /// A const reference to this object. + //------------------------------------------------------------------ + const FileSpecList& + operator= (const FileSpecList &rhs); + + //------------------------------------------------------------------ + /// Append a FileSpec object to the list. + /// + /// Appends \a file to the end of the file list. + /// + /// @param[in] file + /// A new file to append to this file list. + //------------------------------------------------------------------ + void + Append (const FileSpec &file); + + //------------------------------------------------------------------ + /// Append a FileSpec object if unique. + /// + /// Appends \a file to the end of the file list if it doesn't + /// already exist in the file list. + /// + /// @param[in] file + /// A new file to append to this file list. + /// + /// @return + /// \b true if the file was appended, \b false otherwise. + //------------------------------------------------------------------ + bool + AppendIfUnique (const FileSpec &file); + + //------------------------------------------------------------------ + /// Clears the file list. + //------------------------------------------------------------------ + void + Clear (); + + //------------------------------------------------------------------ + /// Dumps the file list to the supplied stream pointer "s". + /// + /// @param[in] s + /// The stream that will be used to dump the object description. + //------------------------------------------------------------------ + void + Dump (Stream *s, const char *separator_cstr = "\n") const; + + //------------------------------------------------------------------ + /// Find a file index. + /// + /// Find the index of the file in the file spec list that matches + /// \a file starting \a idx entries into the file spec list. + /// + /// @param[in] idx + /// An index into the file list. + /// + /// @param[in] file + /// The file specification to search for. + /// + /// @param[in] full + /// Should FileSpec::Equal be called with "full" true or false. + /// + /// @return + /// The index of the file that matches \a file if it is found, + /// else UINT32_MAX is returned. + //------------------------------------------------------------------ + size_t + FindFileIndex (size_t idx, const FileSpec &file, bool full) const; + + //------------------------------------------------------------------ + /// Get file at index. + /// + /// Gets a file from the file list. If \a idx is not a valid index, + /// an empty FileSpec object will be returned. The file objects + /// that are returned can be tested using + /// FileSpec::operator void*(). + /// + /// @param[in] idx + /// An index into the file list. + /// + /// @return + /// A copy of the FileSpec object at index \a idx. If \a idx + /// is out of range, then an empty FileSpec object will be + /// returned. + //------------------------------------------------------------------ + const FileSpec & + GetFileSpecAtIndex (size_t idx) const; + + //------------------------------------------------------------------ + /// Get file specification pointer at index. + /// + /// Gets a file from the file list. The file objects that are + /// returned can be tested using FileSpec::operator void*(). + /// + /// @param[in] idx + /// An index into the file list. + /// + /// @return + /// A pointer to a contained FileSpec object at index \a idx. + /// If \a idx is out of range, then an NULL is returned. + //------------------------------------------------------------------ + const FileSpec * + GetFileSpecPointerAtIndex (size_t idx) const; + + //------------------------------------------------------------------ + /// Get the memory cost of this object. + /// + /// Return the size in bytes that this object takes in memory. This + /// returns the size in bytes of this object, not any shared string + /// values it may refer to. + /// + /// @return + /// The number of bytes that this object occupies in memory. + /// + /// @see ConstString::StaticMemorySize () + //------------------------------------------------------------------ + size_t + MemorySize () const; + + bool + IsEmpty() const + { + return m_files.empty(); + } + + //------------------------------------------------------------------ + /// Get the number of files in the file list. + /// + /// @return + /// The number of files in the file spec list. + //------------------------------------------------------------------ + size_t + GetSize () const; + + bool + Insert (size_t idx, const FileSpec &file) + { + if (idx < m_files.size()) + { + m_files.insert(m_files.begin() + idx, file); + return true; + } + else if (idx == m_files.size()) + { + m_files.push_back(file); + return true; + } + return false; + } + + bool + Replace (size_t idx, const FileSpec &file) + { + if (idx < m_files.size()) + { + m_files[idx] = file; + return true; + } + return false; + } + + bool + Remove (size_t idx) + { + if (idx < m_files.size()) + { + m_files.erase(m_files.begin() + idx); + return true; + } + return false; + } + + static size_t GetFilesMatchingPartialPath (const char *path, bool dir_okay, FileSpecList &matches); + +protected: + typedef std::vector collection; ///< The collection type for the file list. + collection m_files; ///< A collection of FileSpec objects. +}; + +} // namespace lldb_private + + +#endif // #if defined(__cplusplus) +#endif // liblldb_FileSpecList_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Core/Flags.h b/contrib/llvm/tools/lldb/include/lldb/Core/Flags.h new file mode 100644 index 00000000000..233f098ead2 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Core/Flags.h @@ -0,0 +1,253 @@ +//===-- Flags.h -------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_Flags_h_ +#define liblldb_Flags_h_ +#if defined(__cplusplus) + + +#include +#include + +namespace lldb_private { + +//---------------------------------------------------------------------- +/// @class Flags Flags.h "lldb/Core/Flags.h" +/// @brief A class to manage flags. +/// +/// The Flags class managed flag bits and allows testing and +/// modification of individual or multiple flag bits. +//---------------------------------------------------------------------- +class Flags +{ +public: + //---------------------------------------------------------------------- + /// The value type for flags is a 32 bit unsigned integer type. + //---------------------------------------------------------------------- + typedef uint32_t ValueType; + + //---------------------------------------------------------------------- + /// Construct with initial flag bit values. + /// + /// Constructs this object with \a mask as the initial value for all + /// of the flags. + /// + /// @param[in] mask + /// The initial value for all flags. + //---------------------------------------------------------------------- + Flags (ValueType flags = 0) : + m_flags (flags) + { + } + + //---------------------------------------------------------------------- + /// Copy constructor. + /// + /// Construct and copy the flags from \a rhs. + /// + /// @param[in] rhs + /// A const Flags object reference to copy. + //---------------------------------------------------------------------- + Flags (const Flags& rhs) : + m_flags(rhs.m_flags) + { + } + + //---------------------------------------------------------------------- + /// Destructor. + //---------------------------------------------------------------------- + ~Flags () + { + } + + //---------------------------------------------------------------------- + /// Get accessor for all flags. + /// + /// @return + /// Returns all of the flags as a Flags::ValueType. + //---------------------------------------------------------------------- + ValueType + Get () const + { + return m_flags; + } + + //---------------------------------------------------------------------- + /// Return the number of flags that can be represented in this + /// object. + /// + /// @return + /// The maximum number bits in this flag object. + //---------------------------------------------------------------------- + size_t + GetBitSize() const + { + return sizeof (ValueType) * 8; + } + + //---------------------------------------------------------------------- + /// Set accessor for all flags. + /// + /// @param[in] flags + /// The bits with which to replace all of the current flags. + //---------------------------------------------------------------------- + void + Reset (ValueType flags) + { + m_flags = flags; + } + + //---------------------------------------------------------------------- + /// Clear one or more flags. + /// + /// @param[in] mask + /// A bitfield containing one or more flags. + /// + /// @return + /// The new flags after clearing all bits from \a mask. + //---------------------------------------------------------------------- + ValueType + Clear (ValueType mask = ~(ValueType)0) + { + m_flags &= ~mask; + return m_flags; + } + + + //---------------------------------------------------------------------- + /// Set one or more flags by logical OR'ing \a mask with the current + /// flags. + /// + /// @param[in] mask + /// A bitfield containing one or more flags. + /// + /// @return + /// The new flags after setting all bits from \a mask. + //---------------------------------------------------------------------- + ValueType + Set (ValueType mask) + { + m_flags |= mask; + return m_flags; + } + + + //---------------------------------------------------------------------- + /// Test if all bits in \a mask are 1 in the current flags + /// + /// @return + /// \b true if all flags in \a mask are 1, \b false + /// otherwise. + //---------------------------------------------------------------------- + bool + AllSet (ValueType mask) const + { + return (m_flags & mask) == mask; + } + + //---------------------------------------------------------------------- + /// Test one or more flags. + /// + /// @return + /// \b true if any flags in \a mask are 1, \b false + /// otherwise. + //---------------------------------------------------------------------- + bool + AnySet (ValueType mask) const + { + return (m_flags & mask) != 0; + } + + //---------------------------------------------------------------------- + /// Test a single flag bit. + /// + /// @return + /// \b true if \a bit is set, \b false otherwise. + //---------------------------------------------------------------------- + bool + Test (ValueType bit) const + { + return (m_flags & bit) != 0; + } + + //---------------------------------------------------------------------- + /// Test if all bits in \a mask are clear. + /// + /// @return + /// \b true if \b all flags in \a mask are clear, \b false + /// otherwise. + //---------------------------------------------------------------------- + bool + AllClear (ValueType mask) const + { + return (m_flags & mask) == 0; + } + + bool + AnyClear (ValueType mask) const + { + return (m_flags & mask) != mask; + } + + //---------------------------------------------------------------------- + /// Test a single flag bit to see if it is clear (zero). + /// + /// @return + /// \b true if \a bit is 0, \b false otherwise. + //---------------------------------------------------------------------- + bool + IsClear (ValueType bit) const + { + return (m_flags & bit) == 0; + } + + //---------------------------------------------------------------------- + /// Get the number of zero bits in \a m_flags. + /// + /// @return + /// The number of bits that are set to 0 in the current flags. + //---------------------------------------------------------------------- + size_t + ClearCount () const + { + size_t count = 0; + for (ValueType shift = 0; shift < sizeof(ValueType)*8; ++shift) + { + if ((m_flags & (1u << shift)) == 0) + ++count; + } + return count; + } + + //---------------------------------------------------------------------- + /// Get the number of one bits in \a m_flags. + /// + /// @return + /// The number of bits that are set to 1 in the current flags. + //---------------------------------------------------------------------- + size_t + SetCount () const + { + size_t count = 0; + for (ValueType mask = m_flags; mask; mask >>= 1) + { + if (mask & 1u) + ++count; + } + return count; + } + +protected: + ValueType m_flags; ///< The flags. +}; + +} // namespace lldb_private + +#endif // #if defined(__cplusplus) +#endif // liblldb_Flags_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Core/History.h b/contrib/llvm/tools/lldb/include/lldb/Core/History.h new file mode 100644 index 00000000000..b3626882f84 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Core/History.h @@ -0,0 +1,177 @@ +//===-- History.h -----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef lldb_History_h_ +#define lldb_History_h_ + +// C Includes +#include + +// C++ Includes +#include +#include + +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-public.h" +#include "lldb/Host/Mutex.h" + +namespace lldb_private { + +//---------------------------------------------------------------------- +/// @class HistorySource History.h "lldb/Core/History.h" +/// @brief A class that defines history events. +//---------------------------------------------------------------------- + +class HistorySource +{ +public: + typedef const void * HistoryEvent; + + HistorySource () : + m_mutex (Mutex::eMutexTypeRecursive), + m_events () + { + } + + virtual + ~HistorySource() + { + } + + // Create a new history event. Subclasses should use any data or members + // in the subclass of this class to produce a history event and push it + // onto the end of the history stack. + + virtual HistoryEvent + CreateHistoryEvent () = 0; + + virtual void + DeleteHistoryEvent (HistoryEvent event) = 0; + + virtual void + DumpHistoryEvent (Stream &strm, HistoryEvent event) = 0; + + virtual size_t + GetHistoryEventCount() = 0; + + virtual HistoryEvent + GetHistoryEventAtIndex (uint32_t idx) = 0; + + virtual HistoryEvent + GetCurrentHistoryEvent () = 0; + + // Return 0 when lhs == rhs, 1 if lhs > rhs, or -1 if lhs < rhs. + virtual int + CompareHistoryEvents (const HistoryEvent lhs, + const HistoryEvent rhs) = 0; + + virtual bool + IsCurrentHistoryEvent (const HistoryEvent event) = 0; + +private: + typedef std::stack collection; + + Mutex m_mutex; + collection m_events; + + DISALLOW_COPY_AND_ASSIGN (HistorySource); + +}; + +//---------------------------------------------------------------------- +/// @class HistorySourceUInt History.h "lldb/Core/History.h" +/// @brief A class that defines history events that are represented by +/// unsigned integers. +/// +/// Any history event that is defined by a unique monotonically +/// increasing unsigned integer +//---------------------------------------------------------------------- + +class HistorySourceUInt : public HistorySource +{ + HistorySourceUInt (const char *id_name, uintptr_t start_value = 0u) : + HistorySource(), + m_name (id_name), + m_curr_id (start_value) + { + } + + virtual + ~HistorySourceUInt() + { + } + + // Create a new history event. Subclasses should use any data or members + // in the subclass of this class to produce a history event and push it + // onto the end of the history stack. + + virtual HistoryEvent + CreateHistoryEvent () + { + ++m_curr_id; + return (HistoryEvent)m_curr_id; + } + + virtual void + DeleteHistoryEvent (HistoryEvent event) + { + // Nothing to delete, the event contains the integer + } + + virtual void + DumpHistoryEvent (Stream &strm, HistoryEvent event); + + virtual size_t + GetHistoryEventCount() + { + return m_curr_id; + } + + virtual HistoryEvent + GetHistoryEventAtIndex (uint32_t idx) + { + return (HistoryEvent)((uintptr_t)idx); + } + + virtual HistoryEvent + GetCurrentHistoryEvent () + { + return (HistoryEvent)m_curr_id; + } + + // Return 0 when lhs == rhs, 1 if lhs > rhs, or -1 if lhs < rhs. + virtual int + CompareHistoryEvents (const HistoryEvent lhs, + const HistoryEvent rhs) + { + uintptr_t lhs_uint = (uintptr_t)lhs; + uintptr_t rhs_uint = (uintptr_t)rhs; + if (lhs_uint < rhs_uint) + return -1; + if (lhs_uint > rhs_uint) + return +1; + return 0; + } + + virtual bool + IsCurrentHistoryEvent (const HistoryEvent event) + { + return (uintptr_t)event == m_curr_id; + } + +protected: + std::string m_name; // The name of the history unsigned integer + uintptr_t m_curr_id; // The current value of the history unsigned unteger +}; + + +} // namespace lldb_private + +#endif // lldb_History_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Core/IOStreamMacros.h b/contrib/llvm/tools/lldb/include/lldb/Core/IOStreamMacros.h new file mode 100644 index 00000000000..cc4eeb0e050 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Core/IOStreamMacros.h @@ -0,0 +1,38 @@ +//===-- IOStreamMacros.h ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_IOStreamMacros_h_ +#define liblldb_IOStreamMacros_h_ +#if defined(__cplusplus) + +#include + +#define RAW_HEXBASE std::setfill('0') << std::hex << std::right +#define HEXBASE '0' << 'x' << RAW_HEXBASE +#define RAWHEX8(x) RAW_HEXBASE << std::setw(2) << ((uint32_t)(x)) +#define RAWHEX16 RAW_HEXBASE << std::setw(4) +#define RAWHEX32 RAW_HEXBASE << std::setw(8) +#define RAWHEX64 RAW_HEXBASE << std::setw(16) +#define HEX8(x) HEXBASE << std::setw(2) << ((uint32_t)(x)) +#define HEX16 HEXBASE << std::setw(4) +#define HEX32 HEXBASE << std::setw(8) +#define HEX64 HEXBASE << std::setw(16) +#define RAW_HEX(x) RAW_HEXBASE << std::setw(sizeof(x)*2) << (x) +#define HEX(x) HEXBASE << std::setw(sizeof(x)*2) << (x) +#define HEX_SIZE(x, sz) HEXBASE << std::setw((sz)) << (x) +#define STRING_WIDTH(w) std::setfill(' ') << std::setw(w) +#define LEFT_STRING_WIDTH(s, w) std::left << std::setfill(' ') << std::setw(w) << (s) << std::right +#define DECIMAL std::dec << std::setfill(' ') +#define DECIMAL_WIDTH(w) DECIMAL << std::setw(w) +//#define FLOAT(n, d) std::setfill(' ') << std::setw((n)+(d)+1) << std::setprecision(d) << std::showpoint << std::fixed +#define INDENT_WITH_SPACES(iword_idx) std::setfill(' ') << std::setw((iword_idx)) << "" +#define INDENT_WITH_TABS(iword_idx) std::setfill('\t') << std::setw((iword_idx)) << "" + +#endif // #if defined(__cplusplus) +#endif // liblldb_IOStreamMacros_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Core/InputReader.h b/contrib/llvm/tools/lldb/include/lldb/Core/InputReader.h new file mode 100644 index 00000000000..fa86e9a39e2 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Core/InputReader.h @@ -0,0 +1,274 @@ +//===-- InputReader.h -------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_InputReader_h_ +#define liblldb_InputReader_h_ + +#include + +#include "lldb/lldb-public.h" +#include "lldb/lldb-enumerations.h" +#include "lldb/Core/Error.h" +#include "lldb/Core/StringList.h" +#include "lldb/Host/Predicate.h" + + +namespace lldb_private { + +class InputReader +{ +public: + + typedef size_t (*Callback) (void *baton, + InputReader &reader, + lldb::InputReaderAction notification, + const char *bytes, + size_t bytes_len); + + struct HandlerData + { + InputReader& reader; + const char *bytes; + size_t bytes_len; + void* baton; + + HandlerData(InputReader& r, + const char* b, + size_t l, + void* t) : + reader(r), + bytes(b), + bytes_len(l), + baton(t) + { + } + + lldb::StreamSP + GetOutStream(); + + bool + GetBatchMode(); + }; + + struct InitializationParameters + { + private: + void* m_baton; + lldb::InputReaderGranularity m_token_size; + char* m_end_token; + char* m_prompt; + bool m_echo; + bool m_save_user_input; + public: + InitializationParameters() : + m_baton(NULL), + m_token_size(lldb::eInputReaderGranularityLine), + m_echo(true), + m_save_user_input(false) + { + SetEndToken("DONE"); + SetPrompt("> "); + } + + InitializationParameters& + SetEcho(bool e) + { + m_echo = e; + return *this; + } + + InitializationParameters& + SetSaveUserInput(bool s) + { + m_save_user_input = s; + return *this; + } + + InitializationParameters& + SetBaton(void* b) + { + m_baton = b; + return *this; + } + + InitializationParameters& + SetGranularity(lldb::InputReaderGranularity g) + { + m_token_size = g; + return *this; + } + + InitializationParameters& + SetEndToken(const char* e) + { + m_end_token = new char[strlen(e)+1]; + ::strcpy(m_end_token,e); + return *this; + } + + InitializationParameters& + SetPrompt(const char* p) + { + m_prompt = new char[strlen(p)+1]; + ::strcpy(m_prompt,p); + return *this; + } + + friend class InputReaderEZ; + + }; + + InputReader (Debugger &debugger); + + virtual + ~InputReader (); + + virtual Error + Initialize (Callback callback, + void *baton, + lldb::InputReaderGranularity token_size, + const char *end_token, + const char *prompt, + bool echo); + + virtual Error Initialize(void* baton, + lldb::InputReaderGranularity token_size = lldb::eInputReaderGranularityLine, + const char* end_token = "DONE", + const char *prompt = "> ", + bool echo = true) + { + return Error("unimplemented"); + } + + virtual Error + Initialize(InitializationParameters& params) + { + return Error("unimplemented"); + } + + // to use these handlers instead of the Callback function, you must subclass + // InputReaderEZ, and redefine the handlers for the events you care about + virtual void + ActivateHandler(HandlerData&) {} + + virtual void + DeactivateHandler(HandlerData&) {} + + virtual void + ReactivateHandler(HandlerData&) {} + + virtual void + AsynchronousOutputWrittenHandler(HandlerData&) {} + + virtual void + GotTokenHandler(HandlerData&) {} + + virtual void + InterruptHandler(HandlerData&) {} + + virtual void + EOFHandler(HandlerData&) {} + + virtual void + DoneHandler(HandlerData&) {} + + bool + IsDone () const + { + return m_done; + } + + void + SetIsDone (bool b) + { + m_done = b; + } + + lldb::InputReaderGranularity + GetGranularity () const + { + return m_granularity; + } + + bool + GetEcho () const + { + return m_echo; + } + + StringList& + GetUserInput() + { + return m_user_input; + } + + virtual bool + GetSaveUserInput() + { + return false; + } + + // Subclasses _can_ override this function to get input as it comes in + // without any granularity + virtual size_t + HandleRawBytes (const char *bytes, size_t bytes_len); + + Debugger & + GetDebugger() + { + return m_debugger; + } + + bool + IsActive () const + { + return m_active; + } + + const char * + GetPrompt () const; + + void + RefreshPrompt(); + + // If you want to read from an input reader synchronously, then just initialize the + // reader and then call WaitOnReaderIsDone, which will return when the reader is popped. + void + WaitOnReaderIsDone (); + + static const char * + GranularityAsCString (lldb::InputReaderGranularity granularity); + +protected: + friend class Debugger; + + void + Notify (lldb::InputReaderAction notification); + + Debugger &m_debugger; + Callback m_callback; + void *m_callback_baton; + std::string m_end_token; + std::string m_prompt; + lldb::InputReaderGranularity m_granularity; + bool m_done; + bool m_echo; + bool m_active; + Predicate m_reader_done; + StringList m_user_input; + bool m_save_user_input; + +private: + DISALLOW_COPY_AND_ASSIGN (InputReader); + +}; + +} // namespace lldb_private + +#endif // #ifndef liblldb_InputReader_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Core/InputReaderEZ.h b/contrib/llvm/tools/lldb/include/lldb/Core/InputReaderEZ.h new file mode 100644 index 00000000000..85561b6f0a9 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Core/InputReaderEZ.h @@ -0,0 +1,87 @@ +//===-- InputReaderEZ.h -----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_InputReaderEZ_h_ +#define liblldb_InputReaderEZ_h_ + +#include "lldb/Core/InputReader.h" + +namespace lldb_private { + +class InputReaderEZ : public InputReader +{ + +private: + + static size_t Callback_Impl(void *baton, + InputReader &reader, + lldb::InputReaderAction notification, + const char *bytes, + size_t bytes_len); +public: + + InputReaderEZ (Debugger &debugger) : + InputReader(debugger) + {} + + virtual + ~InputReaderEZ (); + + using InputReader::Initialize; + virtual Error + Initialize(void* baton, + lldb::InputReaderGranularity token_size = lldb::eInputReaderGranularityLine, + const char* end_token = "DONE", + const char *prompt = "> ", + bool echo = true); + + virtual Error + Initialize(InitializationParameters& params); + + virtual void + ActivateHandler(HandlerData&) {} + + virtual void + DeactivateHandler(HandlerData&) {} + + virtual void + ReactivateHandler(HandlerData&) {} + + virtual void + AsynchronousOutputWrittenHandler(HandlerData&) {} + + virtual void + GotTokenHandler(HandlerData&) {} + + virtual void + InterruptHandler(HandlerData&) {} + + virtual void + EOFHandler(HandlerData&) {} + + virtual void + DoneHandler(HandlerData&) {} + + virtual bool + GetSaveUserInput() + { + return m_save_user_input; + } + +protected: + friend class Debugger; + +private: + DISALLOW_COPY_AND_ASSIGN (InputReaderEZ); + +}; + +} // namespace lldb_private + +#endif // #ifndef liblldb_InputReaderEZ_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Core/InputReaderStack.h b/contrib/llvm/tools/lldb/include/lldb/Core/InputReaderStack.h new file mode 100644 index 00000000000..a73b97cad57 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Core/InputReaderStack.h @@ -0,0 +1,58 @@ +//===-- InputReaderStack.h --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_InputReaderStack_h_ +#define liblldb_InputReaderStack_h_ + +#include + +#include "lldb/lldb-private.h" +#include "lldb/Host/Mutex.h" + +namespace lldb_private { + +class InputReaderStack +{ +public: + + InputReaderStack (); + + ~InputReaderStack (); + + size_t + GetSize () const; + + void + Push (const lldb::InputReaderSP& reader_sp); + + bool + IsEmpty () const; + + lldb::InputReaderSP + Top (); + + void + Pop (); + + Mutex & + GetStackMutex (); + +protected: + + std::stack m_input_readers; + mutable Mutex m_input_readers_mutex; + +private: + + DISALLOW_COPY_AND_ASSIGN (InputReaderStack); +}; + +} // namespace lldb_private + +#endif // liblldb_InputReaderStack_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Core/Language.h b/contrib/llvm/tools/lldb/include/lldb/Core/Language.h new file mode 100644 index 00000000000..670c6aa695e --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Core/Language.h @@ -0,0 +1,117 @@ +//===-- Language.h ----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_Language_h_ +#define liblldb_Language_h_ + +#include "lldb/lldb-private.h" + +namespace lldb_private { + +//---------------------------------------------------------------------- +/// @class Language Language.h "lldb/Core/Language.h" +/// @brief Encapsulates the programming language for an lldb object. +/// +/// Languages are represented by an enumeration value. +/// +/// The enumeration values used when describing the programming language +/// are the same values as the latest DWARF specification. +//---------------------------------------------------------------------- +class Language +{ +public: + //------------------------------------------------------------------ + /// Construct with optional language enumeration. + //------------------------------------------------------------------ + Language(lldb::LanguageType language = lldb::eLanguageTypeUnknown); + + //------------------------------------------------------------------ + /// Destructor. + /// + /// The destructor is virtual in case this class is subclassed. + //------------------------------------------------------------------ + virtual + ~Language(); + + //------------------------------------------------------------------ + /// Get the language value as a NULL termianted C string. + /// + /// @return + /// The C string representation of the language. The returned + /// string does not need to be freed as it comes from constant + /// strings. NULL can be returned when the language is set to + /// a value that doesn't match of of the lldb::LanguageType + /// enumerations. + //------------------------------------------------------------------ + const char * + AsCString (lldb::DescriptionLevel level = lldb::eDescriptionLevelBrief) const; + + void + Clear(); + + void + GetDescription (Stream *s, lldb::DescriptionLevel level) const; + + //------------------------------------------------------------------ + /// Dump the language value to the stream \a s. + /// + /// @param[in] s + /// The stream to which to dump the language description. + //------------------------------------------------------------------ + void + Dump(Stream *s) const; + + //------------------------------------------------------------------ + /// Get accessor for the language. + /// + /// @return + /// The enumeration value that describes the programming + /// language that an object is associated with. + //------------------------------------------------------------------ + virtual lldb::LanguageType + GetLanguage() const; + + //------------------------------------------------------------------ + /// Set accessor for the language. + /// + /// @param[in] language + /// The new enumeration value that describes the programming + /// language that an object is associated with. + //------------------------------------------------------------------ + void + SetLanguage(lldb::LanguageType language); + + //------------------------------------------------------------------ + /// Set accessor for the language. + /// + /// @param[in] language_cstr + /// The language name as a C string. + //------------------------------------------------------------------ + bool + SetLanguageFromCString(const char *language_cstr); + + +protected: + //------------------------------------------------------------------ + // Member variables + //------------------------------------------------------------------ + lldb::LanguageType m_language; ///< The programming language enumeration value. + ///< The enumeration values are the same as the + ///< latest DWARF specification. +}; + +//-------------------------------------------------------------- +/// Stream the language enumeration as a string object to a +/// Stream. +//-------------------------------------------------------------- +Stream& operator << (Stream& s, const Language& language); + +} // namespace lldb_private + +#endif // liblldb_Language_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Core/Listener.h b/contrib/llvm/tools/lldb/include/lldb/Core/Listener.h new file mode 100644 index 00000000000..a12a65d705d --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Core/Listener.h @@ -0,0 +1,194 @@ +//===-- Listener.h ----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_Select_h_ +#define liblldb_Select_h_ + +// C Includes +// C++ Includes +#include +#include +#include +#include +#include + + +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private.h" +#include "lldb/Host/Predicate.h" +#include "lldb/Core/Event.h" + +namespace lldb_private { + +class Listener +{ +public: + typedef bool (*HandleBroadcastCallback) (lldb::EventSP &event_sp, void *baton); + + friend class Broadcaster; + friend class BroadcasterManager; + + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + Listener (const char *name); + + ~Listener (); + + void + AddEvent (lldb::EventSP &event); + + void + Clear (); + + const char * + GetName () + { + return m_name.c_str(); + } + + uint32_t + StartListeningForEventSpec (BroadcasterManager &manager, + const BroadcastEventSpec &event_spec); + + bool + StopListeningForEventSpec (BroadcasterManager &manager, + const BroadcastEventSpec &event_spec); + + uint32_t + StartListeningForEvents (Broadcaster* broadcaster, + uint32_t event_mask); + + uint32_t + StartListeningForEvents (Broadcaster* broadcaster, + uint32_t event_mask, + HandleBroadcastCallback callback, + void *callback_user_data); + + bool + StopListeningForEvents (Broadcaster* broadcaster, + uint32_t event_mask); + + // Returns true if an event was recieved, false if we timed out. + bool + WaitForEvent (const TimeValue *timeout, + lldb::EventSP &event_sp); + + bool + WaitForEventForBroadcaster (const TimeValue *timeout, + Broadcaster *broadcaster, + lldb::EventSP &event_sp); + + bool + WaitForEventForBroadcasterWithType (const TimeValue *timeout, + Broadcaster *broadcaster, + uint32_t event_type_mask, + lldb::EventSP &event_sp); + + Event * + PeekAtNextEvent (); + + Event * + PeekAtNextEventForBroadcaster (Broadcaster *broadcaster); + + Event * + PeekAtNextEventForBroadcasterWithType (Broadcaster *broadcaster, + uint32_t event_type_mask); + + bool + GetNextEvent (lldb::EventSP &event_sp); + + bool + GetNextEventForBroadcaster (Broadcaster *broadcaster, + lldb::EventSP &event_sp); + + bool + GetNextEventForBroadcasterWithType (Broadcaster *broadcaster, + uint32_t event_type_mask, + lldb::EventSP &event_sp); + + size_t + HandleBroadcastEvent (lldb::EventSP &event_sp); + +private: + + //------------------------------------------------------------------ + // Classes that inherit from Listener can see and modify these + //------------------------------------------------------------------ + struct BroadcasterInfo + { + BroadcasterInfo(uint32_t mask, HandleBroadcastCallback cb = NULL, void *ud = NULL) : + event_mask (mask), + callback (cb), + callback_user_data (ud) + { + } + + uint32_t event_mask; + HandleBroadcastCallback callback; + void *callback_user_data; + }; + + typedef std::multimap broadcaster_collection; + typedef std::list event_collection; + typedef std::vector broadcaster_manager_collection; + + bool + FindNextEventInternal (Broadcaster *broadcaster, // NULL for any broadcaster + const ConstString *sources, // NULL for any event + uint32_t num_sources, + uint32_t event_type_mask, + lldb::EventSP &event_sp, + bool remove); + + bool + GetNextEventInternal (Broadcaster *broadcaster, // NULL for any broadcaster + const ConstString *sources, // NULL for any event + uint32_t num_sources, + uint32_t event_type_mask, + lldb::EventSP &event_sp); + + bool + WaitForEventsInternal (const TimeValue *timeout, + Broadcaster *broadcaster, // NULL for any broadcaster + const ConstString *sources, // NULL for any event + uint32_t num_sources, + uint32_t event_type_mask, + lldb::EventSP &event_sp); + + std::string m_name; + broadcaster_collection m_broadcasters; + Mutex m_broadcasters_mutex; // Protects m_broadcasters + event_collection m_events; + Mutex m_events_mutex; // Protects m_broadcasters and m_events + Predicate m_cond_wait; + broadcaster_manager_collection m_broadcaster_managers; + + void + BroadcasterWillDestruct (Broadcaster *); + + void + BroadcasterManagerWillDestruct (BroadcasterManager *manager); + + +// broadcaster_collection::iterator +// FindBroadcasterWithMask (Broadcaster *broadcaster, +// uint32_t event_mask, +// bool exact); + + //------------------------------------------------------------------ + // For Listener only + //------------------------------------------------------------------ + DISALLOW_COPY_AND_ASSIGN (Listener); +}; + +} // namespace lldb_private + +#endif // liblldb_Select_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Core/Log.h b/contrib/llvm/tools/lldb/include/lldb/Core/Log.h new file mode 100644 index 00000000000..ced6f2565d9 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Core/Log.h @@ -0,0 +1,236 @@ +//===-- Log.h ---------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_Log_h_ +#define liblldb_Log_h_ + +// C Includes +#include +#include +#include +#include +#include + +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private.h" +#include "lldb/Core/ConstString.h" +#include "lldb/Core/Flags.h" +#include "lldb/Core/PluginInterface.h" + +//---------------------------------------------------------------------- +// Logging types +//---------------------------------------------------------------------- +#define LLDB_LOG_FLAG_STDOUT (1u << 0) +#define LLDB_LOG_FLAG_STDERR (1u << 1) +#define LLDB_LOG_FLAG_FATAL (1u << 2) +#define LLDB_LOG_FLAG_ERROR (1u << 3) +#define LLDB_LOG_FLAG_WARNING (1u << 4) +#define LLDB_LOG_FLAG_DEBUG (1u << 5) +#define LLDB_LOG_FLAG_VERBOSE (1u << 6) + +//---------------------------------------------------------------------- +// Logging Options +//---------------------------------------------------------------------- +#define LLDB_LOG_OPTION_THREADSAFE (1u << 0) +#define LLDB_LOG_OPTION_VERBOSE (1u << 1) +#define LLDB_LOG_OPTION_DEBUG (1u << 2) +#define LLDB_LOG_OPTION_PREPEND_SEQUENCE (1u << 3) +#define LLDB_LOG_OPTION_PREPEND_TIMESTAMP (1u << 4) +#define LLDB_LOG_OPTION_PREPEND_PROC_AND_THREAD (1u << 5) +#define LLDB_LOG_OPTION_PREPEND_THREAD_NAME (1U << 6) +#define LLDB_LOG_OPTION_BACKTRACE (1U << 7) + +//---------------------------------------------------------------------- +// Logging Functions +//---------------------------------------------------------------------- +namespace lldb_private { + +class Log +{ +public: + + //------------------------------------------------------------------ + // Callback definitions for abstracted plug-in log access. + //------------------------------------------------------------------ + typedef void (*DisableCallback) (const char **categories, Stream *feedback_strm); + typedef Log * (*EnableCallback) (lldb::StreamSP &log_stream_sp, + uint32_t log_options, + const char **categories, + Stream *feedback_strm); + typedef void (*ListCategoriesCallback) (Stream *strm); + + struct Callbacks + { + DisableCallback disable; + EnableCallback enable; + ListCategoriesCallback list_categories; + }; + + //------------------------------------------------------------------ + // Static accessors for logging channels + //------------------------------------------------------------------ + static void + RegisterLogChannel (const ConstString &channel, + const Log::Callbacks &log_callbacks); + + static bool + UnregisterLogChannel (const ConstString &channel); + + static bool + GetLogChannelCallbacks (const ConstString &channel, + Log::Callbacks &log_callbacks); + + + static void + EnableAllLogChannels (lldb::StreamSP &log_stream_sp, + uint32_t log_options, + const char **categories, + Stream *feedback_strm); + + static void + DisableAllLogChannels (Stream *feedback_strm); + + static void + ListAllLogChannels (Stream *strm); + + static void + Initialize (); + + static void + Terminate (); + + //------------------------------------------------------------------ + // Auto completion + //------------------------------------------------------------------ + static void + AutoCompleteChannelName (const char *channel_name, + StringList &matches); + + //------------------------------------------------------------------ + // Member functions + //------------------------------------------------------------------ + Log (); + + Log (const lldb::StreamSP &stream_sp); + + ~Log (); + + void + PutCString (const char *cstr); + + void + Printf (const char *format, ...) __attribute__ ((format (printf, 2, 3))); + + void + VAPrintf (const char *format, va_list args); + + void + PrintfWithFlags( uint32_t flags, const char *format, ...) __attribute__ ((format (printf, 3, 4))); + + void + LogIf (uint32_t mask, const char *fmt, ...) __attribute__ ((format (printf, 3, 4))); + + void + Debug (const char *fmt, ...) __attribute__ ((format (printf, 2, 3))); + + void + DebugVerbose (const char *fmt, ...) __attribute__ ((format (printf, 2, 3))); + + void + Error (const char *fmt, ...) __attribute__ ((format (printf, 2, 3))); + + void + FatalError (int err, const char *fmt, ...) __attribute__ ((format (printf, 3, 4))); + + void + Verbose (const char *fmt, ...) __attribute__ ((format (printf, 2, 3))); + + void + Warning (const char *fmt, ...) __attribute__ ((format (printf, 2, 3))); + + void + WarningVerbose (const char *fmt, ...) __attribute__ ((format (printf, 2, 3))); + + Flags & + GetOptions(); + + const Flags & + GetOptions() const; + + Flags & + GetMask(); + + const Flags & + GetMask() const; + + bool + GetVerbose() const; + + bool + GetDebug() const; + + void + SetStream (const lldb::StreamSP &stream_sp) + { + m_stream_sp = stream_sp; + } + +protected: + //------------------------------------------------------------------ + // Member variables + //------------------------------------------------------------------ + lldb::StreamSP m_stream_sp; + Flags m_options; + Flags m_mask_bits; + + void + PrintfWithFlagsVarArg (uint32_t flags, const char *format, va_list args); + +private: + DISALLOW_COPY_AND_ASSIGN (Log); +}; + + +class LogChannel : public PluginInterface +{ +public: + LogChannel (); + + virtual + ~LogChannel (); + + static lldb::LogChannelSP + FindPlugin (const char *plugin_name); + + // categories is a an array of chars that ends with a NULL element. + virtual void + Disable (const char **categories, Stream *feedback_strm) = 0; + + virtual bool + Enable (lldb::StreamSP &log_stream_sp, + uint32_t log_options, + Stream *feedback_strm, // Feedback stream for argument errors etc + const char **categories) = 0;// The categories to enable within this logging stream, if empty, enable default set + + virtual void + ListCategories (Stream *strm) = 0; + +protected: + std::unique_ptr m_log_ap; + +private: + DISALLOW_COPY_AND_ASSIGN (LogChannel); +}; + + +} // namespace lldb_private + +#endif // liblldb_Log_H_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Core/Mangled.h b/contrib/llvm/tools/lldb/include/lldb/Core/Mangled.h new file mode 100644 index 00000000000..8732dc00270 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Core/Mangled.h @@ -0,0 +1,306 @@ +//===-- Mangled.h -----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_Mangled_h_ +#define liblldb_Mangled_h_ +#if defined(__cplusplus) + + +#include "lldb/lldb-private.h" +#include "lldb/Core/ConstString.h" +#include + +namespace lldb_private { + +//---------------------------------------------------------------------- +/// @class Mangled Mangled.h "lldb/Core/Mangled.h" +/// @brief A class that handles mangled names. +/// +/// Designed to handle mangled names. The demangled version of any names +/// will be computed when the demangled name is accessed through the +/// Demangled() acccessor. This class can also tokenize the demangled +/// version of the name for powerful searches. Functions and symbols +/// could make instances of this class for their mangled names. Uniqued +/// string pools are used for the mangled, demangled, and token string +/// values to allow for faster comparisons and for efficient memory use. +//---------------------------------------------------------------------- +class Mangled +{ +public: + + enum NamePreference + { + ePreferMangled, + ePreferDemangled + }; + + //---------------------------------------------------------------------- + /// Default constructor. + /// + /// Initialize with both mangled and demangled names empty. + //---------------------------------------------------------------------- + Mangled (); + + //---------------------------------------------------------------------- + /// Construct with name. + /// + /// Constructor with an optional string and a boolean indicating if it is + /// the mangled version. + /// + /// @param[in] name + /// The already const name to copy into this object. + /// + /// @param[in] is_mangled + /// If \b true then \a name is a mangled name, if \b false then + /// \a name is demangled. + //---------------------------------------------------------------------- + explicit + Mangled (const ConstString &name, bool is_mangled); + + //---------------------------------------------------------------------- + /// Construct with name. + /// + /// Constructor with an optional string and auto-detect if \a name is + /// mangled or not. + /// + /// @param[in] name + /// The already const name to copy into this object. + //---------------------------------------------------------------------- + explicit + Mangled (const ConstString &name); + + //---------------------------------------------------------------------- + /// Destructor + /// + /// Releases its ref counts on the mangled and demangled strings that + /// live in the global string pool. + //---------------------------------------------------------------------- + ~Mangled (); + + //---------------------------------------------------------------------- + /// Convert to pointer operator. + /// + /// This allows code to check a Mangled object to see if it contains + /// a valid mangled name using code such as: + /// + /// @code + /// Mangled mangled(...); + /// if (mangled) + /// { ... + /// @endcode + /// + /// @return + /// A pointer to this object if either the mangled or unmangled + /// name is set, NULL otherwise. + //---------------------------------------------------------------------- + operator + void*() const; + + //---------------------------------------------------------------------- + /// Logical NOT operator. + /// + /// This allows code to check a Mangled object to see if it contains + /// an empty mangled name using code such as: + /// + /// @code + /// Mangled mangled(...); + /// if (!mangled) + /// { ... + /// @endcode + /// + /// @return + /// Returns \b true if the object has an empty mangled and + /// unmangled name, \b false otherwise. + //---------------------------------------------------------------------- + bool + operator!() const; + + //---------------------------------------------------------------------- + /// Clear the mangled and demangled values. + //---------------------------------------------------------------------- + void + Clear (); + + //---------------------------------------------------------------------- + /// Compare the mangled string values + /// + /// Compares the Mangled::GetName() string in \a lhs and \a rhs. + /// + /// @param[in] lhs + /// A const reference to the Left Hand Side object to compare. + /// + /// @param[in] rhs + /// A const reference to the Right Hand Side object to compare. + /// + /// @return + /// @li -1 if \a lhs is less than \a rhs + /// @li 0 if \a lhs is equal to \a rhs + /// @li 1 if \a lhs is greater than \a rhs + //---------------------------------------------------------------------- + static int + Compare (const Mangled& lhs, const Mangled& rhs); + + //---------------------------------------------------------------------- + /// Dump a description of this object to a Stream \a s. + /// + /// Dump a Mangled object to stream \a s. We don't force our + /// demangled name to be computed currently (we don't use the accessor). + /// + /// @param[in] s + /// The stream to which to dump the object descripton. + //---------------------------------------------------------------------- + void + Dump (Stream *s) const; + + //---------------------------------------------------------------------- + /// Dump a debug description of this object to a Stream \a s. + /// + /// @param[in] s + /// The stream to which to dump the object descripton. + //---------------------------------------------------------------------- + void + DumpDebug (Stream *s) const; + + //---------------------------------------------------------------------- + /// Demangled name get accessor. + /// + /// @return + /// A const reference to the demangled name string object. + //---------------------------------------------------------------------- + const ConstString& + GetDemangledName () const; + + void + SetDemangledName (const ConstString &name) + { + m_demangled = name; + } + + void + SetMangledName (const ConstString &name) + { + m_mangled = name; + } + + //---------------------------------------------------------------------- + /// Mangled name get accessor. + /// + /// @return + /// A reference to the mangled name string object. + //---------------------------------------------------------------------- + ConstString& + GetMangledName () + { + return m_mangled; + } + + //---------------------------------------------------------------------- + /// Mangled name get accessor. + /// + /// @return + /// A const reference to the mangled name string object. + //---------------------------------------------------------------------- + const ConstString& + GetMangledName () const + { + return m_mangled; + } + + //---------------------------------------------------------------------- + /// Best name get accessor. + /// + /// @param[in] preference + /// Which name would you prefer to get? + /// + /// @return + /// A const reference to the the preferred name string object if this + /// object has a valid name of that kind, else a const reference to the + /// other name is returned. + //---------------------------------------------------------------------- + const ConstString& + GetName (NamePreference preference = ePreferDemangled) const; + + //---------------------------------------------------------------------- + /// Check if "name" matches either the mangled or demangled name. + /// + /// @param[in] name + /// A name to match against both strings. + /// + /// @return + /// \b True if \a name matches either name, \b false otherwise. + //---------------------------------------------------------------------- + bool + NameMatches (const ConstString &name) const + { + if (m_mangled == name) + return true; + return GetDemangledName () == name; + } + + bool + NameMatches (const RegularExpression& regex) const; + + //---------------------------------------------------------------------- + /// Get the memory cost of this object. + /// + /// Return the size in bytes that this object takes in memory. This + /// returns the size in bytes of this object, not any shared string + /// values it may refer to. + /// + /// @return + /// The number of bytes that this object occupies in memory. + /// + /// @see ConstString::StaticMemorySize () + //---------------------------------------------------------------------- + size_t + MemorySize () const; + + //---------------------------------------------------------------------- + /// Set the string value in this object. + /// + /// If \a is_mangled is \b true, then the mangled named is set to \a + /// name, else the demangled name is set to \a name. + /// + /// @param[in] name + /// The already const version of the name for this object. + /// + /// @param[in] is_mangled + /// If \b true then \a name is a mangled name, if \b false then + /// \a name is demangled. + //---------------------------------------------------------------------- + void + SetValue (const ConstString &name, bool is_mangled); + + //---------------------------------------------------------------------- + /// Set the string value in this object. + /// + /// This version auto detects if the string is mangled by inspecting the + /// string value and looking for common mangling prefixes. + /// + /// @param[in] name + /// The already const version of the name for this object. + //---------------------------------------------------------------------- + void + SetValue (const ConstString &name); + +private: + //---------------------------------------------------------------------- + /// Mangled member variables. + //---------------------------------------------------------------------- + ConstString m_mangled; ///< The mangled version of the name + mutable ConstString m_demangled; ///< Mutable so we can get it on demand with a const version of this object +}; + + +Stream& operator << (Stream& s, const Mangled& obj); + +} // namespace lldb_private + +#endif // #if defined(__cplusplus) +#endif // liblldb_Mangled_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Core/MappedHash.h b/contrib/llvm/tools/lldb/include/lldb/Core/MappedHash.h new file mode 100644 index 00000000000..80d249d4cc9 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Core/MappedHash.h @@ -0,0 +1,552 @@ +// +// MappedHash.h +// + +#ifndef liblldb_MappedHash_h_ +#define liblldb_MappedHash_h_ + +#include +#include + +#include +#include + +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/Stream.h" + +class MappedHash +{ +public: + + enum HashFunctionType + { + eHashFunctionDJB = 0u // Daniel J Bernstein hash function that is also used by the ELF GNU_HASH sections + }; + + + static uint32_t + HashStringUsingDJB (const char *s) + { + uint32_t h = 5381; + + for (unsigned char c = *s; c; c = *++s) + h = ((h << 5) + h) + c; + + return h; + } + + static uint32_t + HashString (uint32_t hash_function, const char *s) + { + switch (hash_function) + { + case MappedHash::eHashFunctionDJB: + return HashStringUsingDJB (s); + + default: + break; + } + assert (!"Invalid hash function index"); + return 0; + } + + + static const uint32_t HASH_MAGIC = 0x48415348u; + static const uint32_t HASH_CIGAM = 0x48534148u; + + template + struct Header + { + typedef T HeaderData; + + uint32_t magic; // HASH_MAGIC or HASH_CIGAM magic value to allow endian detection + uint16_t version; // Version number + uint16_t hash_function; // The hash function enumeration that was used + uint32_t bucket_count; // The number of buckets in this hash table + uint32_t hashes_count; // The total number of unique hash values and hash data offsets in this table + uint32_t header_data_len; // The size in bytes of the "header_data" template member below + HeaderData header_data; // + + Header () : + magic (HASH_MAGIC), + version (1), + hash_function (eHashFunctionDJB), + bucket_count (0), + hashes_count (0), + header_data_len (sizeof(T)), + header_data () + { + } + + virtual + ~Header() + { + } + + size_t + GetByteSize() const + { + return sizeof(magic) + + sizeof(version) + + sizeof(hash_function) + + sizeof(bucket_count) + + sizeof(hashes_count) + + sizeof(header_data_len) + + header_data_len; + } + + virtual size_t + GetByteSize (const HeaderData &header_data) = 0; + + void + SetHeaderDataByteSize (uint32_t header_data_byte_size) + { + header_data_len = header_data_byte_size; + } + + void + Dump (lldb_private::Stream &s) + { + s.Printf ("header.magic = 0x%8.8x\n", magic); + s.Printf ("header.version = 0x%4.4x\n", version); + s.Printf ("header.hash_function = 0x%4.4x\n", hash_function); + s.Printf ("header.bucket_count = 0x%8.8x %u\n", bucket_count, bucket_count); + s.Printf ("header.hashes_count = 0x%8.8x %u\n", hashes_count, hashes_count); + s.Printf ("header.header_data_len = 0x%8.8x %u\n", header_data_len, header_data_len); + } + + virtual lldb::offset_t + Read (lldb_private::DataExtractor &data, lldb::offset_t offset) + { + if (data.ValidOffsetForDataOfSize (offset, + sizeof (magic) + + sizeof (version) + + sizeof (hash_function) + + sizeof (bucket_count) + + sizeof (hashes_count) + + sizeof (header_data_len))) + { + magic = data.GetU32 (&offset); + if (magic != HASH_MAGIC) + { + if (magic == HASH_CIGAM) + { + switch (data.GetByteOrder()) + { + case lldb::eByteOrderBig: + data.SetByteOrder(lldb::eByteOrderLittle); + break; + case lldb::eByteOrderLittle: + data.SetByteOrder(lldb::eByteOrderBig); + break; + default: + return LLDB_INVALID_OFFSET; + } + } + else + { + // Magic bytes didn't match + version = 0; + return LLDB_INVALID_OFFSET; + } + } + + version = data.GetU16 (&offset); + if (version != 1) + { + // Unsupported version + return LLDB_INVALID_OFFSET; + } + hash_function = data.GetU16 (&offset); + if (hash_function == 4) + hash_function = 0; // Deal with pre-release version of this table... + bucket_count = data.GetU32 (&offset); + hashes_count = data.GetU32 (&offset); + header_data_len = data.GetU32 (&offset); + return offset; + } + return LLDB_INVALID_OFFSET; + } +// +// // Returns a buffer that contains a serialized version of this table +// // that must be freed with free(). +// virtual void * +// Write (int fd); + }; + + template + class ExportTable + { + public: + typedef __HeaderDataType HeaderDataType; + typedef Header HeaderType; + typedef __KeyType KeyType; + typedef __ValueType ValueType; + + struct Entry + { + uint32_t hash; + KeyType key; + ValueType value; + }; + + typedef std::vector ValueArrayType; + + typedef std::map HashData; + // Map a name hash to one or more name infos + typedef std::map HashToHashData; + + virtual KeyType + GetKeyForStringType (const char *cstr) const = 0; + + virtual size_t + GetByteSize (const HashData &key_to_key_values) = 0; + + virtual bool + WriteHashData (const HashData &hash_data, + lldb_private::Stream &ostrm) = 0; +// + void + AddEntry (const char *cstr, const ValueType &value) + { + Entry entry; + entry.hash = MappedHash::HashString (eHashFunctionDJB, cstr); + entry.key = GetKeyForStringType (cstr); + entry.value = value; + m_entries.push_back (entry); + } + + void + Save (const HeaderDataType &header_data, + lldb_private::Stream &ostrm) + { + if (m_entries.empty()) + return; + + const uint32_t num_entries = m_entries.size(); + uint32_t i = 0; + + HeaderType header; + + header.magic = HASH_MAGIC; + header.version = 1; + header.hash_function = eHashFunctionDJB; + header.bucket_count = 0; + header.hashes_count = 0; + header.prologue_length = header_data.GetByteSize(); + + // We need to figure out the number of unique hashes first before we can + // calculate the number of buckets we want to use. + typedef std::vector hash_coll; + hash_coll unique_hashes; + unique_hashes.resize (num_entries); + for (i=0; i 1024) + header.bucket_count = num_unique_hashes/4; + else if (num_unique_hashes > 16) + header.bucket_count = num_unique_hashes/2; + else + header.bucket_count = num_unique_hashes; + if (header.bucket_count == 0) + header.bucket_count = 1; + + + std::vector hash_buckets; + std::vector hash_indexes (header.bucket_count, 0); + std::vector hash_values; + std::vector hash_offsets; + hash_buckets.resize (header.bucket_count); + uint32_t bucket_entry_empties = 0; + //StreamString hash_file_data(Stream::eBinary, dwarf->GetObjectFile()->GetAddressByteSize(), dwarf->GetObjectFile()->GetByteSize()); + + // Push all of the hashes into their buckets and create all bucket + // entries all populated with data. + for (i=0; ifirst); + hash_offsets.push_back (GetByteSize (pos->second)); + ++hash_count; + } + + hash_indexes[i] = hash_value_index; + } + } + header.hashes_count = hash_values.size(); + + // Write the header out now that we have the hash_count + header.Write (ostrm); + + // Now for each bucket we write the start index of the hashes + // for the current bucket, or UINT32_MAX if the bucket is empty + for (i=0; isecond); + } + } + } + } + protected: + typedef std::vector collection; + collection m_entries; + }; + // A class for reading and using a saved hash table from a block of data + // in memory + template + class MemoryTable + { + public: + typedef __HeaderType HeaderType; + typedef __KeyType KeyType; + typedef __HashData HashData; + + enum Result + { + eResultKeyMatch = 0u, // The entry was found, key matched and "pair" was filled in successfully + eResultKeyMismatch = 1u, // Bucket hash data collision, but key didn't match + eResultEndOfHashData = 2u, // The chain of items for this hash data in this bucket is terminated, search no more + eResultError = 3u // Error parsing the hash data, abort + }; + + struct Pair + { + KeyType key; + HashData value; + }; + + MemoryTable (lldb_private::DataExtractor &data) : + m_header (), + m_hash_indexes (NULL), + m_hash_values (NULL), + m_hash_offsets (NULL) + { + lldb::offset_t offset = m_header.Read (data, 0); + if (offset != LLDB_INVALID_OFFSET && IsValid ()) + { + m_hash_indexes = (uint32_t *)data.GetData (&offset, m_header.bucket_count * sizeof(uint32_t)); + m_hash_values = (uint32_t *)data.GetData (&offset, m_header.hashes_count * sizeof(uint32_t)); + m_hash_offsets = (uint32_t *)data.GetData (&offset, m_header.hashes_count * sizeof(uint32_t)); + } + } + + virtual + ~MemoryTable () + { + } + + bool + IsValid () const + { + return m_header.version == 1 && + m_header.hash_function == eHashFunctionDJB && + m_header.bucket_count > 0 && + m_header.hashes_count > 0; + } + + uint32_t + GetHashIndex (uint32_t bucket_idx) const + { + if (m_hash_indexes && bucket_idx < m_header.bucket_count) + return m_hash_indexes[bucket_idx]; + return UINT32_MAX; + } + + uint32_t + GetHashValue (uint32_t hash_idx) const + { + if (m_hash_values && hash_idx < m_header.hashes_count) + return m_hash_values[hash_idx]; + return UINT32_MAX; + } + + uint32_t + GetHashDataOffset (uint32_t hash_idx) const + { + if (m_hash_offsets && hash_idx < m_header.hashes_count) + return m_hash_offsets[hash_idx]; + return UINT32_MAX; + } + + bool + Find (const char *name, Pair &pair) const + { + if (IsValid ()) + { + const uint32_t bucket_count = m_header.bucket_count; + const uint32_t hash_count = m_header.hashes_count; + const uint32_t hash_value = MappedHash::HashString (m_header.hash_function, name); + const uint32_t bucket_idx = hash_value % bucket_count; + uint32_t hash_idx = GetHashIndex (bucket_idx); + if (hash_idx < hash_count) + { + for (; hash_idx < hash_count; ++hash_idx) + { + const uint32_t curr_hash_value = GetHashValue (hash_idx); + if (curr_hash_value == hash_value) + { + lldb::offset_t hash_data_offset = GetHashDataOffset (hash_idx); + while (hash_data_offset != UINT32_MAX) + { + const lldb::offset_t prev_hash_data_offset = hash_data_offset; + Result hash_result = GetHashDataForName (name, &hash_data_offset, pair); + // Check the result of getting our hash data + switch (hash_result) + { + case eResultKeyMatch: + return true; + + case eResultKeyMismatch: + if (prev_hash_data_offset == hash_data_offset) + return false; + break; + + case eResultEndOfHashData: + // The last HashData for this key has been reached, stop searching + return false; + case eResultError: + // Error parsing the hash data, abort + return false; + } + } + } + if ((curr_hash_value % bucket_count) != bucket_idx) + break; + } + } + } + return false; + } + + // This method must be implemented in any subclasses. + // The KeyType is user specified and must somehow result in a string + // value. For example, the KeyType might be a string offset in a string + // table and subclasses can store their string table as a member of the + // subclass and return a valie "const char *" given a "key". The value + // could also be a C string pointer, in which case just returning "key" + // will suffice. + + virtual const char * + GetStringForKeyType (KeyType key) const = 0; + + virtual bool + ReadHashData (uint32_t hash_data_offset, + HashData &hash_data) const = 0; + + // This method must be implemented in any subclasses and it must try to + // read one "Pair" at the offset pointed to by the "hash_data_offset_ptr" + // parameter. This offset should be updated as bytes are consumed and + // a value "Result" enum should be returned. If the "name" matches the + // full name for the "pair.key" (which must be filled in by this call), + // then the HashData in the pair ("pair.value") should be extracted and + // filled in and "eResultKeyMatch" should be returned. If "name" doesn't + // match this string for the key, then "eResultKeyMismatch" should be + // returned and all data for the current HashData must be consumed or + // skipped and the "hash_data_offset_ptr" offset needs to be updated to + // point to the next HashData. If the end of the HashData objects for + // a given hash value have been reached, then "eResultEndOfHashData" + // should be returned. If anything else goes wrong during parsing, + // return "eResultError" and the corresponding "Find()" function will + // be canceled and return false. + + virtual Result + GetHashDataForName (const char *name, + lldb::offset_t* hash_data_offset_ptr, + Pair &pair) const = 0; + + const HeaderType & + GetHeader() + { + return m_header; + } + + + void + ForEach (std::function const &callback) const + { + const size_t num_hash_offsets = m_header.hashes_count; + for (size_t i=0; i, + public SymbolContextScope +{ +public: + // Static functions that can track the lifetime of module objects. + // This is handy because we might have Module objects that are in + // shared pointers that aren't in the global module list (from + // ModuleList). If this is the case we need to know about it. + // The modules in the global list maintained by these functions + // can be viewed using the "target modules list" command using the + // "--global" (-g for short). + static size_t + GetNumberAllocatedModules (); + + static Module * + GetAllocatedModuleAtIndex (size_t idx); + + static Mutex * + GetAllocationModuleCollectionMutex(); + + //------------------------------------------------------------------ + /// Construct with file specification and architecture. + /// + /// Clients that wish to share modules with other targets should + /// use ModuleList::GetSharedModule(). + /// + /// @param[in] file_spec + /// The file specification for the on disk repesentation of + /// this executable image. + /// + /// @param[in] arch + /// The architecture to set as the current architecture in + /// this module. + /// + /// @param[in] object_name + /// The name of an object in a module used to extract a module + /// within a module (.a files and modules that contain multiple + /// architectures). + /// + /// @param[in] object_offset + /// The offset within an existing module used to extract a + /// module within a module (.a files and modules that contain + /// multiple architectures). + //------------------------------------------------------------------ + Module (const FileSpec& file_spec, + const ArchSpec& arch, + const ConstString *object_name = NULL, + off_t object_offset = 0, + const TimeValue *object_mod_time_ptr = NULL); + + Module (const ModuleSpec &module_spec); + //------------------------------------------------------------------ + /// Destructor. + //------------------------------------------------------------------ + virtual + ~Module (); + + bool + MatchesModuleSpec (const ModuleSpec &module_ref); + + //------------------------------------------------------------------ + /// Set the load address for all sections in a module to be the + /// file address plus \a slide. + /// + /// Many times a module will be loaded in a target with a constant + /// offset applied to all top level sections. This function can + /// set the load address for all top level sections to be the + /// section file address + offset. + /// + /// @param[in] target + /// The target in which to apply the section load addresses. + /// + /// @param[in] offset + /// The offset to apply to all file addresses for all top + /// level sections in the object file as each section load + /// address is being set. + /// + /// @param[out] changed + /// If any section load addresses were changed in \a target, + /// then \a changed will be set to \b true. Else \a changed + /// will be set to false. This allows this function to be + /// called multiple times on the same module for the same + /// target. If the module hasn't moved, then \a changed will + /// be false and no module updated notification will need to + /// be sent out. + /// + /// @return + /// /b True if any sections were successfully loaded in \a target, + /// /b false otherwise. + //------------------------------------------------------------------ + bool + SetLoadAddress (Target &target, + lldb::addr_t offset, + bool &changed); + + //------------------------------------------------------------------ + /// @copydoc SymbolContextScope::CalculateSymbolContext(SymbolContext*) + /// + /// @see SymbolContextScope + //------------------------------------------------------------------ + virtual void + CalculateSymbolContext (SymbolContext* sc); + + virtual lldb::ModuleSP + CalculateSymbolContextModule (); + + void + GetDescription (Stream *s, + lldb::DescriptionLevel level = lldb::eDescriptionLevelFull); + + //------------------------------------------------------------------ + /// Get the module path and object name. + /// + /// Modules can refer to object files. In this case the specification + /// is simple and would return the path to the file: + /// + /// "/usr/lib/foo.dylib" + /// + /// Modules can be .o files inside of a BSD archive (.a file). In + /// this case, the object specification will look like: + /// + /// "/usr/lib/foo.a(bar.o)" + /// + /// There are many places where logging wants to log this fully + /// qualified specification, so we centralize this functionality + /// here. + /// + /// @return + /// The object path + object name if there is one. + //------------------------------------------------------------------ + std::string + GetSpecificationDescription () const; + + //------------------------------------------------------------------ + /// Dump a description of this object to a Stream. + /// + /// Dump a description of the contents of this object to the + /// supplied stream \a s. The dumped content will be only what has + /// been loaded or parsed up to this point at which this function + /// is called, so this is a good way to see what has been parsed + /// in a module. + /// + /// @param[in] s + /// The stream to which to dump the object descripton. + //------------------------------------------------------------------ + void + Dump (Stream *s); + + //------------------------------------------------------------------ + /// @copydoc SymbolContextScope::DumpSymbolContext(Stream*) + /// + /// @see SymbolContextScope + //------------------------------------------------------------------ + virtual void + DumpSymbolContext (Stream *s); + + + //------------------------------------------------------------------ + /// Find a symbol in the object file's symbol table. + /// + /// @param[in] name + /// The name of the symbol that we are looking for. + /// + /// @param[in] symbol_type + /// If set to eSymbolTypeAny, find a symbol of any type that + /// has a name that matches \a name. If set to any other valid + /// SymbolType enumeration value, then search only for + /// symbols that match \a symbol_type. + /// + /// @return + /// Returns a valid symbol pointer if a symbol was found, + /// NULL otherwise. + //------------------------------------------------------------------ + const Symbol * + FindFirstSymbolWithNameAndType (const ConstString &name, + lldb::SymbolType symbol_type = lldb::eSymbolTypeAny); + + size_t + FindSymbolsWithNameAndType (const ConstString &name, + lldb::SymbolType symbol_type, + SymbolContextList &sc_list); + + size_t + FindSymbolsMatchingRegExAndType (const RegularExpression ®ex, + lldb::SymbolType symbol_type, + SymbolContextList &sc_list); + + //------------------------------------------------------------------ + /// Find a funciton symbols in the object file's symbol table. + /// + /// @param[in] name + /// The name of the symbol that we are looking for. + /// + /// @param[in] name_type_mask + /// A mask that has one or more bitwise OR'ed values from the + /// lldb::FunctionNameType enumeration type that indicate what + /// kind of names we are looking for. + /// + /// @param[out] sc_list + /// A list to append any matching symbol contexts to. + /// + /// @return + /// The number of symbol contexts that were added to \a sc_list + //------------------------------------------------------------------ + size_t + FindFunctionSymbols (const ConstString &name, + uint32_t name_type_mask, + SymbolContextList& sc_list); + + //------------------------------------------------------------------ + /// Find compile units by partial or full path. + /// + /// Finds all compile units that match \a path in all of the modules + /// and returns the results in \a sc_list. + /// + /// @param[in] path + /// The name of the function we are looking for. + /// + /// @param[in] append + /// If \b true, then append any compile units that were found + /// to \a sc_list. If \b false, then the \a sc_list is cleared + /// and the contents of \a sc_list are replaced. + /// + /// @param[out] sc_list + /// A symbol context list that gets filled in with all of the + /// matches. + /// + /// @return + /// The number of matches added to \a sc_list. + //------------------------------------------------------------------ + size_t + FindCompileUnits (const FileSpec &path, + bool append, + SymbolContextList &sc_list); + + + //------------------------------------------------------------------ + /// Find functions by name. + /// + /// If the function is an inlined function, it will have a block, + /// representing the inlined function, and the function will be the + /// containing function. If it is not inlined, then the block will + /// be NULL. + /// + /// @param[in] name + /// The name of the compile unit we are looking for. + /// + /// @param[in] namespace_decl + /// If valid, a namespace to search in. + /// + /// @param[in] name_type_mask + /// A bit mask of bits that indicate what kind of names should + /// be used when doing the lookup. Bits include fully qualified + /// names, base names, C++ methods, or ObjC selectors. + /// See FunctionNameType for more details. + /// + /// @param[in] append + /// If \b true, any matches will be appended to \a sc_list, else + /// matches replace the contents of \a sc_list. + /// + /// @param[out] sc_list + /// A symbol context list that gets filled in with all of the + /// matches. + /// + /// @return + /// The number of matches added to \a sc_list. + //------------------------------------------------------------------ + size_t + FindFunctions (const ConstString &name, + const ClangNamespaceDecl *namespace_decl, + uint32_t name_type_mask, + bool symbols_ok, + bool inlines_ok, + bool append, + SymbolContextList& sc_list); + + //------------------------------------------------------------------ + /// Find functions by name. + /// + /// If the function is an inlined function, it will have a block, + /// representing the inlined function, and the function will be the + /// containing function. If it is not inlined, then the block will + /// be NULL. + /// + /// @param[in] regex + /// A regular expression to use when matching the name. + /// + /// @param[in] append + /// If \b true, any matches will be appended to \a sc_list, else + /// matches replace the contents of \a sc_list. + /// + /// @param[out] sc_list + /// A symbol context list that gets filled in with all of the + /// matches. + /// + /// @return + /// The number of matches added to \a sc_list. + //------------------------------------------------------------------ + size_t + FindFunctions (const RegularExpression& regex, + bool symbols_ok, + bool inlines_ok, + bool append, + SymbolContextList& sc_list); + + //------------------------------------------------------------------ + /// Find global and static variables by name. + /// + /// @param[in] name + /// The name of the global or static variable we are looking + /// for. + /// + /// @param[in] namespace_decl + /// If valid, a namespace to search in. + /// + /// @param[in] append + /// If \b true, any matches will be appended to \a + /// variable_list, else matches replace the contents of + /// \a variable_list. + /// + /// @param[in] max_matches + /// Allow the number of matches to be limited to \a + /// max_matches. Specify UINT32_MAX to get all possible matches. + /// + /// @param[in] variable_list + /// A list of variables that gets the matches appended to (if + /// \a append it \b true), or replace (if \a append is \b false). + /// + /// @return + /// The number of matches added to \a variable_list. + //------------------------------------------------------------------ + size_t + FindGlobalVariables (const ConstString &name, + const ClangNamespaceDecl *namespace_decl, + bool append, + size_t max_matches, + VariableList& variable_list); + + //------------------------------------------------------------------ + /// Find global and static variables by regular exression. + /// + /// @param[in] regex + /// A regular expression to use when matching the name. + /// + /// @param[in] append + /// If \b true, any matches will be appended to \a + /// variable_list, else matches replace the contents of + /// \a variable_list. + /// + /// @param[in] max_matches + /// Allow the number of matches to be limited to \a + /// max_matches. Specify UINT32_MAX to get all possible matches. + /// + /// @param[in] variable_list + /// A list of variables that gets the matches appended to (if + /// \a append it \b true), or replace (if \a append is \b false). + /// + /// @return + /// The number of matches added to \a variable_list. + //------------------------------------------------------------------ + size_t + FindGlobalVariables (const RegularExpression& regex, + bool append, + size_t max_matches, + VariableList& variable_list); + + //------------------------------------------------------------------ + /// Find types by name. + /// + /// Type lookups in modules go through the SymbolVendor (which will + /// use one or more SymbolFile subclasses). The SymbolFile needs to + /// be able to lookup types by basename and not the fully qualified + /// typename. This allows the type accelerator tables to stay small, + /// even with heavily templatized C++. The type search will then + /// narrow down the search results. If "exact_match" is true, then + /// the type search will only match exact type name matches. If + /// "exact_match" is false, the type will match as long as the base + /// typename matches and as long as any immediate containing + /// namespaces/class scopes that are specified match. So to search + /// for a type "d" in "b::c", the name "b::c::d" can be specified + /// and it will match any class/namespace "b" which contains a + /// class/namespace "c" which contains type "d". We do this to + /// allow users to not always have to specify complete scoping on + /// all expressions, but it also allows for exact matching when + /// required. + /// + /// @param[in] sc + /// A symbol context that scopes where to extract a type list + /// from. + /// + /// @param[in] type_name + /// The name of the type we are looking for that is a fully + /// or partially qualfieid type name. + /// + /// @param[in] exact_match + /// If \b true, \a type_name is fully qualifed and must match + /// exactly. If \b false, \a type_name is a partially qualfied + /// name where the leading namespaces or classes can be + /// omitted to make finding types that a user may type + /// easier. + /// + /// @param[out] type_list + /// A type list gets populated with any matches. + /// + /// @return + /// The number of matches added to \a type_list. + //------------------------------------------------------------------ + size_t + FindTypes (const SymbolContext& sc, + const ConstString &type_name, + bool exact_match, + size_t max_matches, + TypeList& types); + + lldb::TypeSP + FindFirstType (const SymbolContext& sc, + const ConstString &type_name, + bool exact_match); + + //------------------------------------------------------------------ + /// Find types by name that are in a namespace. This function is + /// used by the expression parser when searches need to happen in + /// an exact namespace scope. + /// + /// @param[in] sc + /// A symbol context that scopes where to extract a type list + /// from. + /// + /// @param[in] type_name + /// The name of a type within a namespace that should not include + /// any qualifying namespaces (just a type basename). + /// + /// @param[in] namespace_decl + /// The namespace declaration that this type must exist in. + /// + /// @param[out] type_list + /// A type list gets populated with any matches. + /// + /// @return + /// The number of matches added to \a type_list. + //------------------------------------------------------------------ + size_t + FindTypesInNamespace (const SymbolContext& sc, + const ConstString &type_name, + const ClangNamespaceDecl *namespace_decl, + size_t max_matches, + TypeList& type_list); + + //------------------------------------------------------------------ + /// Get const accessor for the module architecture. + /// + /// @return + /// A const reference to the architecture object. + //------------------------------------------------------------------ + const ArchSpec& + GetArchitecture () const; + + //------------------------------------------------------------------ + /// Get const accessor for the module file specification. + /// + /// This function returns the file for the module on the host system + /// that is running LLDB. This can differ from the path on the + /// platform since we might be doing remote debugging. + /// + /// @return + /// A const reference to the file specification object. + //------------------------------------------------------------------ + const FileSpec & + GetFileSpec () const + { + return m_file; + } + + //------------------------------------------------------------------ + /// Get accessor for the module platform file specification. + /// + /// Platform file refers to the path of the module as it is known on + /// the remote system on which it is being debugged. For local + /// debugging this is always the same as Module::GetFileSpec(). But + /// remote debugging might mention a file "/usr/lib/liba.dylib" + /// which might be locally downloaded and cached. In this case the + /// platform file could be something like: + /// "/tmp/lldb/platform-cache/remote.host.computer/usr/lib/liba.dylib" + /// The file could also be cached in a local developer kit directory. + /// + /// @return + /// A const reference to the file specification object. + //------------------------------------------------------------------ + const FileSpec & + GetPlatformFileSpec () const + { + if (m_platform_file) + return m_platform_file; + return m_file; + } + + void + SetPlatformFileSpec (const FileSpec &file) + { + m_platform_file = file; + } + + const FileSpec & + GetSymbolFileFileSpec () const + { + return m_symfile_spec; + } + + void + SetSymbolFileFileSpec (const FileSpec &file); + + const TimeValue & + GetModificationTime () const + { + return m_mod_time; + } + + const TimeValue & + GetObjectModificationTime () const + { + return m_object_mod_time; + } + + void + SetObjectModificationTime (const TimeValue &mod_time) + { + m_mod_time = mod_time; + } + + //------------------------------------------------------------------ + /// Tells whether this module is capable of being the main executable + /// for a process. + /// + /// @return + /// \b true if it is, \b false otherwise. + //------------------------------------------------------------------ + bool + IsExecutable (); + + //------------------------------------------------------------------ + /// Tells whether this module has been loaded in the target passed in. + /// This call doesn't distinguish between whether the module is loaded + /// by the dynamic loader, or by a "target module add" type call. + /// + /// @param[in] target + /// The target to check whether this is loaded in. + /// + /// @return + /// \b true if it is, \b false otherwise. + //------------------------------------------------------------------ + bool + IsLoadedInTarget (Target *target); + + bool + LoadScriptingResourceInTarget (Target *target, + Error& error, + Stream* feedback_stream = NULL); + + //------------------------------------------------------------------ + /// Get the number of compile units for this module. + /// + /// @return + /// The number of compile units that the symbol vendor plug-in + /// finds. + //------------------------------------------------------------------ + size_t + GetNumCompileUnits(); + + lldb::CompUnitSP + GetCompileUnitAtIndex (size_t idx); + + const ConstString & + GetObjectName() const; + + uint64_t + GetObjectOffset() const + { + return m_object_offset; + } + + //------------------------------------------------------------------ + /// Get the object file representation for the current architecture. + /// + /// If the object file has not been located or parsed yet, this + /// function will find the best ObjectFile plug-in that can parse + /// Module::m_file. + /// + /// @return + /// If Module::m_file does not exist, or no plug-in was found + /// that can parse the file, or the object file doesn't contain + /// the current architecture in Module::m_arch, NULL will be + /// returned, else a valid object file interface will be + /// returned. The returned pointer is owned by this object and + /// remains valid as long as the object is around. + //------------------------------------------------------------------ + virtual ObjectFile * + GetObjectFile (); + + //------------------------------------------------------------------ + /// Get the unified section list for the module. This is the section + /// list created by the module's object file and any debug info and + /// symbol files created by the symbol vendor. + /// + /// If the symbol vendor has not been loaded yet, this function + /// will return the section list for the object file. + /// + /// @return + /// Unified module section list. + //------------------------------------------------------------------ + virtual SectionList * + GetSectionList (); + + uint32_t + GetVersion (uint32_t *versions, uint32_t num_versions); + + // Load an object file from memory. + ObjectFile * + GetMemoryObjectFile (const lldb::ProcessSP &process_sp, + lldb::addr_t header_addr, + Error &error); + //------------------------------------------------------------------ + /// Get the symbol vendor interface for the current architecture. + /// + /// If the symbol vendor file has not been located yet, this + /// function will find the best SymbolVendor plug-in that can + /// use the current object file. + /// + /// @return + /// If this module does not have a valid object file, or no + /// plug-in can be found that can use the object file, NULL will + /// be returned, else a valid symbol vendor plug-in interface + /// will be returned. The returned pointer is owned by this + /// object and remains valid as long as the object is around. + //------------------------------------------------------------------ + virtual SymbolVendor* + GetSymbolVendor(bool can_create = true, + lldb_private::Stream *feedback_strm = NULL); + + //------------------------------------------------------------------ + /// Get accessor the type list for this module. + /// + /// @return + /// A valid type list pointer, or NULL if there is no valid + /// symbol vendor for this module. + //------------------------------------------------------------------ + TypeList* + GetTypeList (); + + //------------------------------------------------------------------ + /// Get a pointer to the UUID value contained in this object. + /// + /// If the executable image file doesn't not have a UUID value built + /// into the file format, an MD5 checksum of the entire file, or + /// slice of the file for the current architecture should be used. + /// + /// @return + /// A const pointer to the internal copy of the UUID value in + /// this module if this module has a valid UUID value, NULL + /// otherwise. + //------------------------------------------------------------------ + const lldb_private::UUID & + GetUUID (); + + //------------------------------------------------------------------ + /// A debugging function that will cause everything in a module to + /// be parsed. + /// + /// All compile units will be pasred, along with all globals and + /// static variables and all functions for those compile units. + /// All types, scopes, local variables, static variables, global + /// variables, and line tables will be parsed. This can be used + /// prior to dumping a module to see a complete list of the + /// resuling debug information that gets parsed, or as a debug + /// function to ensure that the module can consume all of the + /// debug data the symbol vendor provides. + //------------------------------------------------------------------ + void + ParseAllDebugSymbols(); + + bool + ResolveFileAddress (lldb::addr_t vm_addr, Address& so_addr); + + uint32_t + ResolveSymbolContextForAddress (const Address& so_addr, uint32_t resolve_scope, SymbolContext& sc); + + //------------------------------------------------------------------ + /// Resolve items in the symbol context for a given file and line. + /// + /// Tries to resolve \a file_path and \a line to a list of matching + /// symbol contexts. + /// + /// The line table entries contains addresses that can be used to + /// further resolve the values in each match: the function, block, + /// symbol. Care should be taken to minimize the amount of + /// information that is requested to only what is needed -- + /// typically the module, compile unit, line table and line table + /// entry are sufficient. + /// + /// @param[in] file_path + /// A path to a source file to match. If \a file_path does not + /// specify a directory, then this query will match all files + /// whose base filename matches. If \a file_path does specify + /// a directory, the fullpath to the file must match. + /// + /// @param[in] line + /// The source line to match, or zero if just the compile unit + /// should be resolved. + /// + /// @param[in] check_inlines + /// Check for inline file and line number matches. This option + /// should be used sparingly as it will cause all line tables + /// for every compile unit to be parsed and searched for + /// matching inline file entries. + /// + /// @param[in] resolve_scope + /// The scope that should be resolved (see + /// SymbolContext::Scope). + /// + /// @param[out] sc_list + /// A symbol context list that gets matching symbols contexts + /// appended to. + /// + /// @return + /// The number of matches that were added to \a sc_list. + /// + /// @see SymbolContext::Scope + //------------------------------------------------------------------ + uint32_t + ResolveSymbolContextForFilePath (const char *file_path, uint32_t line, bool check_inlines, uint32_t resolve_scope, SymbolContextList& sc_list); + + //------------------------------------------------------------------ + /// Resolve items in the symbol context for a given file and line. + /// + /// Tries to resolve \a file_spec and \a line to a list of matching + /// symbol contexts. + /// + /// The line table entries contains addresses that can be used to + /// further resolve the values in each match: the function, block, + /// symbol. Care should be taken to minimize the amount of + /// information that is requested to only what is needed -- + /// typically the module, compile unit, line table and line table + /// entry are sufficient. + /// + /// @param[in] file_spec + /// A file spec to a source file to match. If \a file_path does + /// not specify a directory, then this query will match all + /// files whose base filename matches. If \a file_path does + /// specify a directory, the fullpath to the file must match. + /// + /// @param[in] line + /// The source line to match, or zero if just the compile unit + /// should be resolved. + /// + /// @param[in] check_inlines + /// Check for inline file and line number matches. This option + /// should be used sparingly as it will cause all line tables + /// for every compile unit to be parsed and searched for + /// matching inline file entries. + /// + /// @param[in] resolve_scope + /// The scope that should be resolved (see + /// SymbolContext::Scope). + /// + /// @param[out] sc_list + /// A symbol context list that gets filled in with all of the + /// matches. + /// + /// @return + /// A integer that contains SymbolContext::Scope bits set for + /// each item that was successfully resolved. + /// + /// @see SymbolContext::Scope + //------------------------------------------------------------------ + uint32_t + ResolveSymbolContextsForFileSpec (const FileSpec &file_spec, uint32_t line, bool check_inlines, uint32_t resolve_scope, SymbolContextList& sc_list); + + + void + SetFileSpecAndObjectName (const FileSpec &file, + const ConstString &object_name); + + bool + GetIsDynamicLinkEditor () const + { + return m_is_dynamic_loader_module; + } + + void + SetIsDynamicLinkEditor (bool b) + { + m_is_dynamic_loader_module = b; + } + + ClangASTContext & + GetClangASTContext (); + + // Special error functions that can do printf style formatting that will prepend the message with + // something appropriate for this module (like the architecture, path and object name (if any)). + // This centralizes code so that everyone doesn't need to format their error and log messages on + // their own and keeps the output a bit more consistent. + void + LogMessage (Log *log, const char *format, ...) __attribute__ ((format (printf, 3, 4))); + + void + LogMessageVerboseBacktrace (Log *log, const char *format, ...) __attribute__ ((format (printf, 3, 4))); + + void + ReportWarning (const char *format, ...) __attribute__ ((format (printf, 2, 3))); + + void + ReportError (const char *format, ...) __attribute__ ((format (printf, 2, 3))); + + // Only report an error once when the module is first detected to be modified + // so we don't spam the console with many messages. + void + ReportErrorIfModifyDetected (const char *format, ...) __attribute__ ((format (printf, 2, 3))); + + //------------------------------------------------------------------ + // Return true if the file backing this module has changed since the + // module was originally created since we saved the intial file + // modification time when the module first gets created. + //------------------------------------------------------------------ + bool + FileHasChanged () const; + + //------------------------------------------------------------------ + // SymbolVendor, SymbolFile and ObjectFile member objects should + // lock the module mutex to avoid deadlocks. + //------------------------------------------------------------------ + Mutex & + GetMutex () const + { + return m_mutex; + } + + PathMappingList & + GetSourceMappingList () + { + return m_source_mappings; + } + + const PathMappingList & + GetSourceMappingList () const + { + return m_source_mappings; + } + + //------------------------------------------------------------------ + /// Finds a source file given a file spec using the module source + /// path remappings (if any). + /// + /// Tries to resolve \a orig_spec by checking the module source path + /// remappings. It makes sure the file exists, so this call can be + /// expensive if the remappings are on a network file system, so + /// use this function sparingly (not in a tight debug info parsing + /// loop). + /// + /// @param[in] orig_spec + /// The original source file path to try and remap. + /// + /// @param[out] new_spec + /// The newly remapped filespec that is guaranteed to exist. + /// + /// @return + /// /b true if \a orig_spec was successfully located and + /// \a new_spec is filled in with an existing file spec, + /// \b false otherwise. + //------------------------------------------------------------------ + bool + FindSourceFile (const FileSpec &orig_spec, FileSpec &new_spec) const; + + //------------------------------------------------------------------ + /// Remaps a source file given \a path into \a new_path. + /// + /// Remaps \a path if any source remappings match. This function + /// does NOT stat the file system so it can be used in tight loops + /// where debug info is being parsed. + /// + /// @param[in] path + /// The original source file path to try and remap. + /// + /// @param[out] new_path + /// The newly remapped filespec that is may or may not exist. + /// + /// @return + /// /b true if \a path was successfully located and \a new_path + /// is filled in with a new source path, \b false otherwise. + //------------------------------------------------------------------ + bool + RemapSourceFile (const char *path, std::string &new_path) const; + + + //------------------------------------------------------------------ + /// Prepare to do a function name lookup. + /// + /// Looking up functions by name can be a tricky thing. LLDB requires + /// that accelerator tables contain full names for functions as well + /// as function basenames which include functions, class methods and + /// class functions. When the user requests that an action use a + /// function by name, we are sometimes asked to automatically figure + /// out what a name could possibly map to. A user might request a + /// breakpoint be set on "count". If no options are supplied to limit + /// the scope of where to search for count, we will by default match + /// any function names named "count", all class and instance methods + /// named "count" (no matter what the namespace or contained context) + /// and any selectors named "count". If a user specifies "a::b" we + /// will search for the basename "b", and then prune the results that + /// don't match "a::b" (note that "c::a::b" and "d::e::a::b" will + /// match a query of "a::b". + /// + /// @param[in] name + /// The user supplied name to use in the lookup + /// + /// @param[in] name_type_mask + /// The mask of bits from lldb::FunctionNameType enumerations + /// that tell us what kind of name we are looking for. + /// + /// @param[out] lookup_name + /// The actual name that will be used when calling + /// SymbolVendor::FindFunctions() or Symtab::FindFunctionSymbols() + /// + /// @param[out] lookup_name_type_mask + /// The actual name mask that should be used in the calls to + /// SymbolVendor::FindFunctions() or Symtab::FindFunctionSymbols() + /// + /// @param[out] match_name_after_lookup + /// A boolean that indicates if we need to iterate through any + /// match results obtained from SymbolVendor::FindFunctions() or + /// Symtab::FindFunctionSymbols() to see if the name contains + /// \a name. For example if \a name is "a::b", this function will + /// return a \a lookup_name of "b", with \a match_name_after_lookup + /// set to true to indicate any matches will need to be checked + /// to make sure they contain \a name. + //------------------------------------------------------------------ + static void + PrepareForFunctionNameLookup (const ConstString &name, + uint32_t name_type_mask, + ConstString &lookup_name, + uint32_t &lookup_name_type_mask, + bool &match_name_after_lookup); + +protected: + //------------------------------------------------------------------ + // Member Variables + //------------------------------------------------------------------ + mutable Mutex m_mutex; ///< A mutex to keep this object happy in multi-threaded environments. + TimeValue m_mod_time; ///< The modification time for this module when it was created. + ArchSpec m_arch; ///< The architecture for this module. + lldb_private::UUID m_uuid; ///< Each module is assumed to have a unique identifier to help match it up to debug symbols. + FileSpec m_file; ///< The file representation on disk for this module (if there is one). + FileSpec m_platform_file;///< The path to the module on the platform on which it is being debugged + FileSpec m_symfile_spec; ///< If this path is valid, then this is the file that _will_ be used as the symbol file for this module + ConstString m_object_name; ///< The name an object within this module that is selected, or empty of the module is represented by \a m_file. + uint64_t m_object_offset; + TimeValue m_object_mod_time; + lldb::ObjectFileSP m_objfile_sp; ///< A shared pointer to the object file parser for this module as it may or may not be shared with the SymbolFile + std::unique_ptr m_symfile_ap; ///< A pointer to the symbol vendor for this module. + ClangASTContext m_ast; ///< The AST context for this module. + PathMappingList m_source_mappings; ///< Module specific source remappings for when you have debug info for a module that doesn't match where the sources currently are + std::unique_ptr m_sections_ap; ///< Unified section list for module that is used by the ObjectFile and and ObjectFile instances for the debug info + + bool m_did_load_objfile:1, + m_did_load_symbol_vendor:1, + m_did_parse_uuid:1, + m_did_init_ast:1, + m_is_dynamic_loader_module:1; + mutable bool m_file_has_changed:1, + m_first_file_changed_log:1; /// See if the module was modified after it was initially opened. + + //------------------------------------------------------------------ + /// Resolve a file or load virtual address. + /// + /// Tries to resolve \a vm_addr as a file address (if \a + /// vm_addr_is_file_addr is true) or as a load address if \a + /// vm_addr_is_file_addr is false) in the symbol vendor. + /// \a resolve_scope indicates what clients wish to resolve + /// and can be used to limit the scope of what is parsed. + /// + /// @param[in] vm_addr + /// The load virtual address to resolve. + /// + /// @param[in] vm_addr_is_file_addr + /// If \b true, \a vm_addr is a file address, else \a vm_addr + /// if a load address. + /// + /// @param[in] resolve_scope + /// The scope that should be resolved (see + /// SymbolContext::Scope). + /// + /// @param[out] so_addr + /// The section offset based address that got resolved if + /// any bits are returned. + /// + /// @param[out] sc + // The symbol context that has objects filled in. Each bit + /// in the \a resolve_scope pertains to a member in the \a sc. + /// + /// @return + /// A integer that contains SymbolContext::Scope bits set for + /// each item that was successfully resolved. + /// + /// @see SymbolContext::Scope + //------------------------------------------------------------------ + uint32_t + ResolveSymbolContextForAddress (lldb::addr_t vm_addr, + bool vm_addr_is_file_addr, + uint32_t resolve_scope, + Address& so_addr, + SymbolContext& sc); + + void + SymbolIndicesToSymbolContextList (Symtab *symtab, + std::vector &symbol_indexes, + SymbolContextList &sc_list); + + bool + SetArchitecture (const ArchSpec &new_arch); + + SectionList * + GetUnifiedSectionList(); + + friend class ModuleList; + friend class ObjectFile; + friend class SymbolFile; + +private: + + size_t + FindTypes_Impl (const SymbolContext& sc, + const ConstString &name, + const ClangNamespaceDecl *namespace_decl, + bool append, + size_t max_matches, + TypeList& types); + + + DISALLOW_COPY_AND_ASSIGN (Module); +}; + +} // namespace lldb_private + +#endif // liblldb_Module_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Core/ModuleChild.h b/contrib/llvm/tools/lldb/include/lldb/Core/ModuleChild.h new file mode 100644 index 00000000000..d2a6fb0c3bc --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Core/ModuleChild.h @@ -0,0 +1,90 @@ +//===-- ModuleChild.h -------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ModuleChild_h_ +#define liblldb_ModuleChild_h_ + +#include "lldb/lldb-private.h" + +namespace lldb_private { + +//---------------------------------------------------------------------- +/// @class ModuleChild ModuleChild.h "lldb/Core/ModuleChild.h" +/// @brief A mix in class that contains a pointer back to the module +/// that owns the object which inherits from it. +//---------------------------------------------------------------------- +class ModuleChild +{ +public: + //------------------------------------------------------------------ + /// Construct with owning module. + /// + /// @param[in] module + /// The module that owns the object that inherits from this + /// class. + //------------------------------------------------------------------ + ModuleChild (const lldb::ModuleSP &module_sp); + + //------------------------------------------------------------------ + /// Copy constructor. + /// + /// @param[in] rhs + /// A const ModuleChild class reference to copy. + //------------------------------------------------------------------ + ModuleChild (const ModuleChild& rhs); + + //------------------------------------------------------------------ + /// Destructor. + //------------------------------------------------------------------ + ~ModuleChild(); + + //------------------------------------------------------------------ + /// Assignment operator. + /// + /// @param[in] rhs + /// A const ModuleChild class reference to copy. + /// + /// @return + /// A const reference to this object. + //------------------------------------------------------------------ + const ModuleChild& + operator= (const ModuleChild& rhs); + + //------------------------------------------------------------------ + /// Get const accessor for the module pointer. + /// + /// @return + /// A const pointer to the module that owns the object that + /// inherits from this class. + //------------------------------------------------------------------ + lldb::ModuleSP + GetModule () const; + + //------------------------------------------------------------------ + /// Set accessor for the module pointer. + /// + /// @param[in] module + /// A new module that owns the object that inherits from this + /// class. + //------------------------------------------------------------------ + void + SetModule (const lldb::ModuleSP &module_sp); + +protected: + //------------------------------------------------------------------ + // Member variables + //------------------------------------------------------------------ + lldb::ModuleWP m_module_wp; ///< The Module that owns the object that inherits + ///< from this class. +}; + +} // namespace lldb_private + + +#endif // liblldb_ModuleChild_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Core/ModuleList.h b/contrib/llvm/tools/lldb/include/lldb/Core/ModuleList.h new file mode 100644 index 00000000000..1198e419648 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Core/ModuleList.h @@ -0,0 +1,556 @@ +//===-- ModuleList.h --------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ModuleList_h_ +#define liblldb_ModuleList_h_ + +#include +#include + +#include "lldb/lldb-private.h" +#include "lldb/Host/Mutex.h" + +namespace lldb_private { + +//---------------------------------------------------------------------- +/// @class ModuleList ModuleList.h "lldb/Core/ModuleList.h" +/// @brief A collection class for Module objects. +/// +/// Modules in the module collection class are stored as reference +/// counted shared pointers to Module objects. +//---------------------------------------------------------------------- +class ModuleList +{ +public: + + class Notifier + { + public: + virtual void + ModuleAdded (const ModuleList& module_list, const lldb::ModuleSP& module_sp) = 0; + virtual void + ModuleRemoved (const ModuleList& module_list, const lldb::ModuleSP& module_sp) = 0; + virtual void + ModuleUpdated (const ModuleList& module_list, const lldb::ModuleSP& old_module_sp, + const lldb::ModuleSP& new_module_sp) = 0; + virtual void + WillClearList (const ModuleList& module_list) = 0; + + virtual + ~Notifier () + {} + }; + + //------------------------------------------------------------------ + /// Default constructor. + /// + /// Creates an empty list of Module objects. + //------------------------------------------------------------------ + ModuleList (); + + //------------------------------------------------------------------ + /// Copy Constructor. + /// + /// Creates a new module list object with a copy of the modules from + /// \a rhs. + /// + /// @param[in] rhs + /// Another module list object. + //------------------------------------------------------------------ + ModuleList (const ModuleList& rhs); + + ModuleList (ModuleList::Notifier* notifier); + + //------------------------------------------------------------------ + /// Destructor. + //------------------------------------------------------------------ + ~ModuleList (); + + //------------------------------------------------------------------ + /// Assignment operator. + /// + /// Copies the module list from \a rhs into this list. + /// + /// @param[in] rhs + /// Another module list object. + /// + /// @return + /// A const reference to this object. + //------------------------------------------------------------------ + const ModuleList& + operator= (const ModuleList& rhs); + + //------------------------------------------------------------------ + /// Append a module to the module list. + /// + /// Appends the module to the collection. + /// + /// @param[in] module_sp + /// A shared pointer to a module to add to this collection. + //------------------------------------------------------------------ + void + Append (const lldb::ModuleSP &module_sp); + + //------------------------------------------------------------------ + /// Append a module to the module list and remove any equivalent + /// modules. Equivalent modules are ones whose file, platform file + /// and architecture matches. + /// + /// Replaces the module to the collection. + /// + /// @param[in] module_sp + /// A shared pointer to a module to replace in this collection. + //------------------------------------------------------------------ + void + ReplaceEquivalent (const lldb::ModuleSP &module_sp); + + bool + AppendIfNeeded (const lldb::ModuleSP &module_sp); + + void + Append (const ModuleList& module_list); + + bool + AppendIfNeeded (const ModuleList& module_list); + + bool + ReplaceModule (const lldb::ModuleSP &old_module_sp, const lldb::ModuleSP &new_module_sp); + + //------------------------------------------------------------------ + /// Clear the object's state. + /// + /// Clears the list of modules and releases a reference to each + /// module object and if the reference count goes to zero, the + /// module will be deleted. + //------------------------------------------------------------------ + void + Clear (); + + //------------------------------------------------------------------ + /// Clear the object's state. + /// + /// Clears the list of modules and releases a reference to each + /// module object and if the reference count goes to zero, the + /// module will be deleted. Also relese all memory that might be + /// held by any collection classes (like std::vector) + //------------------------------------------------------------------ + void + Destroy(); + //------------------------------------------------------------------ + /// Dump the description of each module contained in this list. + /// + /// Dump the description of each module contained in this list to + /// the supplied stream \a s. + /// + /// @param[in] s + /// The stream to which to dump the object descripton. + /// + /// @see Module::Dump(Stream *) const + //------------------------------------------------------------------ + void + Dump (Stream *s) const; + + void + LogUUIDAndPaths (Log *log, const char *prefix_cstr); + + Mutex & + GetMutex () const + { + return m_modules_mutex; + } + + size_t + GetIndexForModule (const Module *module) const; + + //------------------------------------------------------------------ + /// Get the module shared pointer for the module at index \a idx. + /// + /// @param[in] idx + /// An index into this module collection. + /// + /// @return + /// A shared pointer to a Module which can contain NULL if + /// \a idx is out of range. + /// + /// @see ModuleList::GetSize() + //------------------------------------------------------------------ + lldb::ModuleSP + GetModuleAtIndex (size_t idx) const; + + //------------------------------------------------------------------ + /// Get the module shared pointer for the module at index \a idx without + /// acquiring the ModuleList mutex. This MUST already have been + /// acquired with ModuleList::GetMutex and locked for this call to be safe. + /// + /// @param[in] idx + /// An index into this module collection. + /// + /// @return + /// A shared pointer to a Module which can contain NULL if + /// \a idx is out of range. + /// + /// @see ModuleList::GetSize() + //------------------------------------------------------------------ + lldb::ModuleSP + GetModuleAtIndexUnlocked (size_t idx) const; + + //------------------------------------------------------------------ + /// Get the module pointer for the module at index \a idx. + /// + /// @param[in] idx + /// An index into this module collection. + /// + /// @return + /// A pointer to a Module which can by NULL if \a idx is out + /// of range. + /// + /// @see ModuleList::GetSize() + //------------------------------------------------------------------ + Module* + GetModulePointerAtIndex (size_t idx) const; + + //------------------------------------------------------------------ + /// Get the module pointer for the module at index \a idx without + /// acquiring the ModuleList mutex. This MUST already have been + /// acquired with ModuleList::GetMutex and locked for this call to be safe. + /// + /// @param[in] idx + /// An index into this module collection. + /// + /// @return + /// A pointer to a Module which can by NULL if \a idx is out + /// of range. + /// + /// @see ModuleList::GetSize() + //------------------------------------------------------------------ + Module* + GetModulePointerAtIndexUnlocked (size_t idx) const; + + //------------------------------------------------------------------ + /// Find compile units by partial or full path. + /// + /// Finds all compile units that match \a path in all of the modules + /// and returns the results in \a sc_list. + /// + /// @param[in] path + /// The name of the compile unit we are looking for. + /// + /// @param[in] append + /// If \b true, then append any compile units that were found + /// to \a sc_list. If \b false, then the \a sc_list is cleared + /// and the contents of \a sc_list are replaced. + /// + /// @param[out] sc_list + /// A symbol context list that gets filled in with all of the + /// matches. + /// + /// @return + /// The number of matches added to \a sc_list. + //------------------------------------------------------------------ + size_t + FindCompileUnits (const FileSpec &path, + bool append, + SymbolContextList &sc_list) const; + + //------------------------------------------------------------------ + /// @see Module::FindFunctions () + //------------------------------------------------------------------ + size_t + FindFunctions (const ConstString &name, + uint32_t name_type_mask, + bool include_symbols, + bool include_inlines, + bool append, + SymbolContextList &sc_list) const; + + //------------------------------------------------------------------ + /// @see Module::FindFunctionSymbols () + //------------------------------------------------------------------ + size_t + FindFunctionSymbols (const ConstString &name, + uint32_t name_type_mask, + SymbolContextList& sc_list); + + //------------------------------------------------------------------ + /// Find global and static variables by name. + /// + /// @param[in] name + /// The name of the global or static variable we are looking + /// for. + /// + /// @param[in] append + /// If \b true, any matches will be appended to \a + /// variable_list, else matches replace the contents of + /// \a variable_list. + /// + /// @param[in] max_matches + /// Allow the number of matches to be limited to \a + /// max_matches. Specify UINT32_MAX to get all possible matches. + /// + /// @param[in] variable_list + /// A list of variables that gets the matches appended to (if + /// \a append it \b true), or replace (if \a append is \b false). + /// + /// @return + /// The number of matches added to \a variable_list. + //------------------------------------------------------------------ + size_t + FindGlobalVariables (const ConstString &name, + bool append, + size_t max_matches, + VariableList& variable_list) const; + + //------------------------------------------------------------------ + /// Find global and static variables by regular exression. + /// + /// @param[in] regex + /// A regular expression to use when matching the name. + /// + /// @param[in] append + /// If \b true, any matches will be appended to \a + /// variable_list, else matches replace the contents of + /// \a variable_list. + /// + /// @param[in] max_matches + /// Allow the number of matches to be limited to \a + /// max_matches. Specify UINT32_MAX to get all possible matches. + /// + /// @param[in] variable_list + /// A list of variables that gets the matches appended to (if + /// \a append it \b true), or replace (if \a append is \b false). + /// + /// @return + /// The number of matches added to \a variable_list. + //------------------------------------------------------------------ + size_t + FindGlobalVariables (const RegularExpression& regex, + bool append, + size_t max_matches, + VariableList& variable_list) const; + + //------------------------------------------------------------------ + /// Finds the first module whose file specification matches \a + /// file_spec. + /// + /// @param[in] file_spec_ptr + /// A file specification object to match against the Module's + /// file specifications. If \a file_spec does not have + /// directory information, matches will occur by matching only + /// the basename of any modules in this list. If this value is + /// NULL, then file specifications won't be compared when + /// searching for matching modules. + /// + /// @param[in] arch_ptr + /// The architecture to search for if non-NULL. If this value + /// is NULL no architecture matching will be performed. + /// + /// @param[in] uuid_ptr + /// The uuid to search for if non-NULL. If this value is NULL + /// no uuid matching will be performed. + /// + /// @param[in] object_name + /// An optional object name that must match as well. This value + /// can be NULL. + /// + /// @param[out] matching_module_list + /// A module list that gets filled in with any modules that + /// match the search criteria. + /// + /// @return + /// The number of matching modules found by the search. + //------------------------------------------------------------------ + size_t + FindModules (const ModuleSpec &module_spec, + ModuleList& matching_module_list) const; + + lldb::ModuleSP + FindModule (const Module *module_ptr) const; + + //------------------------------------------------------------------ + // Find a module by UUID + // + // The UUID value for a module is extracted from the ObjectFile and + // is the MD5 checksum, or a smarter object file equivalent, so + // finding modules by UUID values is very efficient and accurate. + //------------------------------------------------------------------ + lldb::ModuleSP + FindModule (const UUID &uuid) const; + + lldb::ModuleSP + FindFirstModule (const ModuleSpec &module_spec) const; + + size_t + FindSymbolsWithNameAndType (const ConstString &name, + lldb::SymbolType symbol_type, + SymbolContextList &sc_list, + bool append = false) const; + + size_t + FindSymbolsMatchingRegExAndType (const RegularExpression ®ex, + lldb::SymbolType symbol_type, + SymbolContextList &sc_list, + bool append = false) const; + + //------------------------------------------------------------------ + /// Find types by name. + /// + /// @param[in] sc + /// A symbol context that scopes where to extract a type list + /// from. + /// + /// @param[in] name + /// The name of the type we are looking for. + /// + /// @param[in] append + /// If \b true, any matches will be appended to \a + /// variable_list, else matches replace the contents of + /// \a variable_list. + /// + /// @param[in] max_matches + /// Allow the number of matches to be limited to \a + /// max_matches. Specify UINT32_MAX to get all possible matches. + /// + /// @param[in] encoding + /// Limit the search to specific types, or get all types if + /// set to Type::invalid. + /// + /// @param[in] udt_name + /// If the encoding is a user defined type, specify the name + /// of the user defined type ("struct", "union", "class", etc). + /// + /// @param[out] type_list + /// A type list gets populated with any matches. + /// + /// @return + /// The number of matches added to \a type_list. + //------------------------------------------------------------------ + size_t + FindTypes (const SymbolContext& sc, + const ConstString &name, + bool name_is_fully_qualified, + size_t max_matches, + TypeList& types) const; + + bool + FindSourceFile (const FileSpec &orig_spec, FileSpec &new_spec) const; + + bool + Remove (const lldb::ModuleSP &module_sp); + + size_t + Remove (ModuleList &module_list); + + bool + RemoveIfOrphaned (const Module *module_ptr); + + size_t + RemoveOrphans (bool mandatory); + + bool + ResolveFileAddress (lldb::addr_t vm_addr, + Address& so_addr) const; + + //------------------------------------------------------------------ + /// @copydoc Module::ResolveSymbolContextForAddress (const Address &,uint32_t,SymbolContext&) + //------------------------------------------------------------------ + uint32_t + ResolveSymbolContextForAddress (const Address& so_addr, + uint32_t resolve_scope, + SymbolContext& sc) const; + + //------------------------------------------------------------------ + /// @copydoc Module::ResolveSymbolContextForFilePath (const char *,uint32_t,bool,uint32_t,SymbolContextList&) + //------------------------------------------------------------------ + uint32_t + ResolveSymbolContextForFilePath (const char *file_path, + uint32_t line, + bool check_inlines, + uint32_t resolve_scope, + SymbolContextList& sc_list) const; + + //------------------------------------------------------------------ + /// @copydoc Module::ResolveSymbolContextsForFileSpec (const FileSpec &,uint32_t,bool,uint32_t,SymbolContextList&) + //------------------------------------------------------------------ + uint32_t + ResolveSymbolContextsForFileSpec (const FileSpec &file_spec, + uint32_t line, + bool check_inlines, + uint32_t resolve_scope, + SymbolContextList& sc_list) const; + + //------------------------------------------------------------------ + /// Gets the size of the module list. + /// + /// @return + /// The number of modules in the module list. + //------------------------------------------------------------------ + size_t + GetSize () const; + + bool + LoadScriptingResourcesInTarget (Target *target, + std::list& errors, + Stream* feedback_stream = NULL, + bool continue_on_error = true); + + static bool + ModuleIsInCache (const Module *module_ptr); + + static Error + GetSharedModule (const ModuleSpec &module_spec, + lldb::ModuleSP &module_sp, + const FileSpecList *module_search_paths_ptr, + lldb::ModuleSP *old_module_sp_ptr, + bool *did_create_ptr, + bool always_create = false); + + static bool + RemoveSharedModule (lldb::ModuleSP &module_sp); + + static size_t + FindSharedModules (const ModuleSpec &module_spec, + ModuleList &matching_module_list); + + static size_t + RemoveOrphanSharedModules (bool mandatory); + + static bool + RemoveSharedModuleIfOrphaned (const Module *module_ptr); + +protected: + //------------------------------------------------------------------ + // Class typedefs. + //------------------------------------------------------------------ + typedef std::vector collection; ///< The module collection type. + + void + AppendImpl (const lldb::ModuleSP &module_sp, bool use_notifier = true); + + bool + RemoveImpl (const lldb::ModuleSP &module_sp, bool use_notifier = true); + + collection::iterator + RemoveImpl (collection::iterator pos, bool use_notifier = true); + + void + ClearImpl (bool use_notifier = true); + + //------------------------------------------------------------------ + // Member variables. + //------------------------------------------------------------------ + collection m_modules; ///< The collection of modules. + mutable Mutex m_modules_mutex; + + Notifier* m_notifier; + +}; + +} // namespace lldb_private + +#endif // liblldb_ModuleList_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Core/ModuleSpec.h b/contrib/llvm/tools/lldb/include/lldb/Core/ModuleSpec.h new file mode 100644 index 00000000000..10e1ea9f17a --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Core/ModuleSpec.h @@ -0,0 +1,594 @@ +//===-- ModuleSpec.h --------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ModuleSpec_h_ +#define liblldb_ModuleSpec_h_ + +#include "lldb/Core/ArchSpec.h" +#include "lldb/Core/Stream.h" +#include "lldb/Core/UUID.h" +#include "lldb/Host/FileSpec.h" +#include "lldb/Target/PathMappingList.h" + +namespace lldb_private { + +class ModuleSpec +{ +public: + ModuleSpec () : + m_file (), + m_platform_file (), + m_symbol_file (), + m_arch (), + m_uuid (), + m_object_name (), + m_object_offset (0), + m_object_mod_time (), + m_source_mappings () + { + } + + ModuleSpec (const FileSpec &file_spec) : + m_file (file_spec), + m_platform_file (), + m_symbol_file (), + m_arch (), + m_uuid (), + m_object_name (), + m_object_offset (0), + m_object_mod_time (), + m_source_mappings () + { + } + + ModuleSpec (const FileSpec &file_spec, const ArchSpec &arch) : + m_file (file_spec), + m_platform_file (), + m_symbol_file (), + m_arch (arch), + m_uuid (), + m_object_name (), + m_object_offset (0), + m_object_mod_time (), + m_source_mappings () + { + } + + ModuleSpec (const ModuleSpec &rhs) : + m_file (rhs.m_file), + m_platform_file (rhs.m_platform_file), + m_symbol_file (rhs.m_symbol_file), + m_arch (rhs.m_arch), + m_uuid (rhs.m_uuid), + m_object_name (rhs.m_object_name), + m_object_offset (rhs.m_object_offset), + m_object_mod_time (rhs.m_object_mod_time), + m_source_mappings (rhs.m_source_mappings) + { + } + + ModuleSpec & + operator = (const ModuleSpec &rhs) + { + if (this != &rhs) + { + m_file = rhs.m_file; + m_platform_file = rhs.m_platform_file; + m_symbol_file = rhs.m_symbol_file; + m_arch = rhs.m_arch; + m_uuid = rhs.m_uuid; + m_object_name = rhs.m_object_name; + m_object_offset = rhs.m_object_offset; + m_object_mod_time = rhs.m_object_mod_time; + m_source_mappings = rhs.m_source_mappings; + } + return *this; + } + + FileSpec * + GetFileSpecPtr () + { + if (m_file) + return &m_file; + return NULL; + } + + const FileSpec * + GetFileSpecPtr () const + { + if (m_file) + return &m_file; + return NULL; + } + + FileSpec & + GetFileSpec () + { + return m_file; + } + const FileSpec & + GetFileSpec () const + { + return m_file; + } + + FileSpec * + GetPlatformFileSpecPtr () + { + if (m_platform_file) + return &m_platform_file; + return NULL; + } + + const FileSpec * + GetPlatformFileSpecPtr () const + { + if (m_platform_file) + return &m_platform_file; + return NULL; + } + + FileSpec & + GetPlatformFileSpec () + { + return m_platform_file; + } + + const FileSpec & + GetPlatformFileSpec () const + { + return m_platform_file; + } + + FileSpec * + GetSymbolFileSpecPtr () + { + if (m_symbol_file) + return &m_symbol_file; + return NULL; + } + + const FileSpec * + GetSymbolFileSpecPtr () const + { + if (m_symbol_file) + return &m_symbol_file; + return NULL; + } + + FileSpec & + GetSymbolFileSpec () + { + return m_symbol_file; + } + + const FileSpec & + GetSymbolFileSpec () const + { + return m_symbol_file; + } + + + ArchSpec * + GetArchitecturePtr () + { + if (m_arch.IsValid()) + return &m_arch; + return NULL; + } + + const ArchSpec * + GetArchitecturePtr () const + { + if (m_arch.IsValid()) + return &m_arch; + return NULL; + } + + ArchSpec & + GetArchitecture () + { + return m_arch; + } + + const ArchSpec & + GetArchitecture () const + { + return m_arch; + } + + UUID * + GetUUIDPtr () + { + if (m_uuid.IsValid()) + return &m_uuid; + return NULL; + } + + const UUID * + GetUUIDPtr () const + { + if (m_uuid.IsValid()) + return &m_uuid; + return NULL; + } + + UUID & + GetUUID () + { + return m_uuid; + } + + const UUID & + GetUUID () const + { + return m_uuid; + } + + ConstString & + GetObjectName () + { + return m_object_name; + } + + const ConstString & + GetObjectName () const + { + return m_object_name; + } + + uint64_t + GetObjectOffset () const + { + return m_object_offset; + } + + void + SetObjectOffset (uint64_t object_offset) + { + m_object_offset = object_offset; + } + + TimeValue & + GetObjectModificationTime () + { + return m_object_mod_time; + } + + const TimeValue & + GetObjectModificationTime () const + { + return m_object_mod_time; + } + + PathMappingList & + GetSourceMappingList () const + { + return m_source_mappings; + } + + void + Clear () + { + m_file.Clear(); + m_platform_file.Clear(); + m_symbol_file.Clear(); + m_arch.Clear(); + m_uuid.Clear(); + m_object_name.Clear(); + m_object_offset = 0; + m_source_mappings.Clear(false); + m_object_mod_time.Clear(); + } + + + operator bool () const + { + if (m_file) + return true; + if (m_platform_file) + return true; + if (m_symbol_file) + return true; + if (m_arch.IsValid()) + return true; + if (m_uuid.IsValid()) + return true; + if (m_object_name) + return true; + if (m_object_mod_time.IsValid()) + return true; + return false; + } + + void + Dump (Stream &strm) + { + bool dumped_something = false; + if (m_file) + { + strm.PutCString("file = '"); + strm << m_file; + strm.PutCString("'"); + dumped_something = true; + } + if (m_platform_file) + { + if (dumped_something) + strm.PutCString(", "); + strm.PutCString("platform_file = '"); + strm << m_platform_file; + strm.PutCString("'"); + dumped_something = true; + } + if (m_symbol_file) + { + if (dumped_something) + strm.PutCString(", "); + strm.PutCString("symbol_file = '"); + strm << m_symbol_file; + strm.PutCString("'"); + dumped_something = true; + } + if (m_arch.IsValid()) + { + if (dumped_something) + strm.PutCString(", "); + strm.Printf("arch = %s", m_arch.GetTriple().str().c_str()); + dumped_something = true; + } + if (m_uuid.IsValid()) + { + if (dumped_something) + strm.PutCString(", "); + strm.PutCString("uuid = "); + m_uuid.Dump(&strm); + dumped_something = true; + } + if (m_object_name) + { + if (dumped_something) + strm.PutCString(", "); + strm.Printf("object_name = %s", m_object_name.GetCString()); + dumped_something = true; + } + if (m_object_offset > 0) + { + if (dumped_something) + strm.PutCString(", "); + strm.Printf("object_offset = 0x%" PRIx64, m_object_offset); + dumped_something = true; + } + if (m_object_mod_time.IsValid()) + { + if (dumped_something) + strm.PutCString(", "); + strm.Printf("object_mod_time = 0x%" PRIx64, m_object_mod_time.GetAsSecondsSinceJan1_1970()); + dumped_something = true; + } + } + + bool + Matches (const ModuleSpec &match_module_spec, bool exact_arch_match) const + { + if (match_module_spec.GetUUIDPtr() && match_module_spec.GetUUID() != GetUUID()) + return false; + if (match_module_spec.GetObjectName() && match_module_spec.GetObjectName() != GetObjectName()) + return false; + if (match_module_spec.GetFileSpecPtr()) + { + const FileSpec &fspec = match_module_spec.GetFileSpec(); + if (!FileSpec::Equal(fspec, GetFileSpec(), fspec.GetDirectory().IsEmpty() == false)) + return false; + } + if (match_module_spec.GetPlatformFileSpecPtr()) + { + const FileSpec &fspec = match_module_spec.GetPlatformFileSpec(); + if (!FileSpec::Equal(fspec, GetPlatformFileSpec(), fspec.GetDirectory().IsEmpty() == false)) + return false; + + } + if (match_module_spec.GetSymbolFileSpecPtr()) + { + const FileSpec &fspec = match_module_spec.GetSymbolFileSpec(); + if (!FileSpec::Equal(fspec, GetSymbolFileSpec(), fspec.GetDirectory().IsEmpty() == false)) + return false; + + } + if (match_module_spec.GetArchitecturePtr()) + { + if (exact_arch_match) + { + if (!GetArchitecture().IsExactMatch(match_module_spec.GetArchitecture())) + return false; + } + else + { + if (!GetArchitecture().IsCompatibleMatch(match_module_spec.GetArchitecture())) + return false; + } + } + return true; + } + +protected: + FileSpec m_file; + FileSpec m_platform_file; + FileSpec m_symbol_file; + ArchSpec m_arch; + UUID m_uuid; + ConstString m_object_name; + uint64_t m_object_offset; + TimeValue m_object_mod_time; + mutable PathMappingList m_source_mappings; +}; + +class ModuleSpecList +{ +public: + ModuleSpecList () : + m_specs(), + m_mutex(Mutex::eMutexTypeRecursive) + { + } + + ModuleSpecList (const ModuleSpecList &rhs) : + m_specs(), + m_mutex(Mutex::eMutexTypeRecursive) + { + Mutex::Locker lhs_locker(m_mutex); + Mutex::Locker rhs_locker(rhs.m_mutex); + m_specs = rhs.m_specs; + } + + ~ModuleSpecList () + { + } + + ModuleSpecList & + operator = (const ModuleSpecList &rhs) + { + if (this != &rhs) + { + Mutex::Locker lhs_locker(m_mutex); + Mutex::Locker rhs_locker(rhs.m_mutex); + m_specs = rhs.m_specs; + } + return *this; + } + + size_t + GetSize() const + { + Mutex::Locker locker(m_mutex); + return m_specs.size(); + } + + void + Clear () + { + Mutex::Locker locker(m_mutex); + m_specs.clear(); + } + + void + Append (const ModuleSpec &spec) + { + Mutex::Locker locker(m_mutex); + m_specs.push_back (spec); + } + + void + Append (const ModuleSpecList &rhs) + { + Mutex::Locker lhs_locker(m_mutex); + Mutex::Locker rhs_locker(rhs.m_mutex); + m_specs.insert(m_specs.end(), rhs.m_specs.begin(), rhs.m_specs.end()); + } + + // The index "i" must be valid and this can't be used in + // multi-threaded code as no mutex lock is taken. + ModuleSpec & + GetModuleSpecRefAtIndex (size_t i) + { + return m_specs[i]; + } + bool + GetModuleSpecAtIndex (size_t i, ModuleSpec &module_spec) const + { + Mutex::Locker locker(m_mutex); + if (i < m_specs.size()) + { + module_spec = m_specs[i]; + return true; + } + module_spec.Clear(); + return false; + } + + + bool + FindMatchingModuleSpec (const ModuleSpec &module_spec, ModuleSpec &match_module_spec) const + { + Mutex::Locker locker(m_mutex); + bool exact_arch_match = true; + for (auto spec: m_specs) + { + if (spec.Matches(module_spec, exact_arch_match)) + { + match_module_spec = spec; + return true; + } + } + + // If there was an architecture, retry with a compatible arch + if (module_spec.GetArchitecturePtr()) + { + exact_arch_match = false; + for (auto spec: m_specs) + { + if (spec.Matches(module_spec, exact_arch_match)) + { + match_module_spec = spec; + return true; + } + } + } + match_module_spec.Clear(); + return false; + } + + size_t + FindMatchingModuleSpecs (const ModuleSpec &module_spec, ModuleSpecList &matching_list) const + { + Mutex::Locker locker(m_mutex); + bool exact_arch_match = true; + const size_t initial_match_count = matching_list.GetSize(); + for (auto spec: m_specs) + { + if (spec.Matches(module_spec, exact_arch_match)) + matching_list.Append (spec); + } + + // If there was an architecture, retry with a compatible arch if no matches were found + if (module_spec.GetArchitecturePtr() && (initial_match_count == matching_list.GetSize())) + { + exact_arch_match = false; + for (auto spec: m_specs) + { + if (spec.Matches(module_spec, exact_arch_match)) + matching_list.Append (spec); + } + } + return matching_list.GetSize() - initial_match_count; + } + + void + Dump (Stream &strm) + { + Mutex::Locker locker(m_mutex); + uint32_t idx = 0; + for (auto spec: m_specs) + { + strm.Printf("[%u] ", idx); + spec.Dump (strm); + strm.EOL(); + ++idx; + } + } + +protected: + typedef std::vector collection; ///< The module collection type. + collection m_specs; ///< The collection of modules. + mutable Mutex m_mutex; +}; + +} // namespace lldb_private + +#endif // liblldb_ModuleSpec_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Core/Opcode.h b/contrib/llvm/tools/lldb/include/lldb/Core/Opcode.h new file mode 100644 index 00000000000..c07193b6205 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Core/Opcode.h @@ -0,0 +1,270 @@ +//===-- Opcode.h ------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef lldb_Opcode_h +#define lldb_Opcode_h + +// C Includes +#include + +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-public.h" + +namespace lldb +{ + class SBInstruction; +} + +namespace lldb_private { + + class Opcode + { + public: + enum Type + { + eTypeInvalid, + eType8, + eType16, + eType16_2, // a 32-bit Thumb instruction, made up of two words + eType32, + eType64, + eTypeBytes + }; + + Opcode () : m_type (eTypeInvalid) + { + } + + Opcode (uint8_t inst) : m_type (eType8) + { + m_data.inst8 = inst; + } + + Opcode (uint16_t inst) : m_type (eType16) + { + m_data.inst16 = inst; + } + + Opcode (uint32_t inst) : m_type (eType32) + { + m_data.inst32 = inst; + } + + Opcode (uint64_t inst) : m_type (eType64) + { + m_data.inst64 = inst; + } + + Opcode (uint8_t *bytes, size_t length) + { + SetOpcodeBytes (bytes, length); + } + + void + Clear() + { + m_type = Opcode::eTypeInvalid; + } + Opcode::Type + GetType () const + { + return m_type; + } + + uint8_t + GetOpcode8 (uint8_t invalid_opcode = UINT8_MAX) const + { + switch (m_type) + { + case Opcode::eTypeInvalid: break; + case Opcode::eType8: return m_data.inst8; + case Opcode::eType16: break; + case Opcode::eType16_2: break; + case Opcode::eType32: break; + case Opcode::eType64: break; + case Opcode::eTypeBytes: break; + break; + } + return invalid_opcode; + } + + uint16_t + GetOpcode16 (uint16_t invalid_opcode = UINT16_MAX) const + { + switch (m_type) + { + case Opcode::eTypeInvalid: break; + case Opcode::eType8: return m_data.inst8; + case Opcode::eType16: return m_data.inst16; + case Opcode::eType16_2: break; + case Opcode::eType32: break; + case Opcode::eType64: break; + case Opcode::eTypeBytes: break; + } + return invalid_opcode; + } + + uint32_t + GetOpcode32 (uint32_t invalid_opcode = UINT32_MAX) const + { + switch (m_type) + { + case Opcode::eTypeInvalid: break; + case Opcode::eType8: return m_data.inst8; + case Opcode::eType16: return m_data.inst16; + case Opcode::eType16_2: // passthrough + case Opcode::eType32: return m_data.inst32; + case Opcode::eType64: break; + case Opcode::eTypeBytes: break; + } + return invalid_opcode; + } + + uint64_t + GetOpcode64 (uint64_t invalid_opcode = UINT64_MAX) const + { + switch (m_type) + { + case Opcode::eTypeInvalid: break; + case Opcode::eType8: return m_data.inst8; + case Opcode::eType16: return m_data.inst16; + case Opcode::eType16_2: // passthrough + case Opcode::eType32: return m_data.inst32; + case Opcode::eType64: return m_data.inst64; + case Opcode::eTypeBytes: break; + } + return invalid_opcode; + } + + void + SetOpcode8 (uint8_t inst) + { + m_type = eType8; + m_data.inst8 = inst; + } + + void + SetOpcode16 (uint16_t inst) + { + m_type = eType16; + m_data.inst16 = inst; + } + + void + SetOpcode16_2 (uint32_t inst) + { + m_type = eType16_2; + m_data.inst32 = inst; + } + + void + SetOpcode32 (uint32_t inst) + { + m_type = eType32; + m_data.inst32 = inst; + } + + void + SetOpcode64 (uint64_t inst) + { + m_type = eType64; + m_data.inst64 = inst; + } + + void + SetOpcodeBytes (const void *bytes, size_t length) + { + if (bytes && length > 0) + { + m_type = eTypeBytes; + m_data.inst.length = length; + assert (length < sizeof (m_data.inst.bytes)); + memcpy (m_data.inst.bytes, bytes, length); + } + else + { + m_type = eTypeInvalid; + m_data.inst.length = 0; + } + } + + int + Dump (Stream *s, uint32_t min_byte_width); + + const void * + GetOpcodeBytes () const + { + if (m_type == Opcode::eTypeBytes) + return m_data.inst.bytes; + return NULL; + } + + uint32_t + GetByteSize () const + { + switch (m_type) + { + case Opcode::eTypeInvalid: break; + case Opcode::eType8: return sizeof(m_data.inst8); + case Opcode::eType16: return sizeof(m_data.inst16); + case Opcode::eType16_2: // passthrough + case Opcode::eType32: return sizeof(m_data.inst32); + case Opcode::eType64: return sizeof(m_data.inst64); + case Opcode::eTypeBytes: return m_data.inst.length; + } + return 0; + } + + // Get the opcode exactly as it would be laid out in memory. + uint32_t + GetData (DataExtractor &data) const; + + protected: + + friend class lldb::SBInstruction; + + const void * + GetOpcodeDataBytes () const + { + switch (m_type) + { + case Opcode::eTypeInvalid: break; + case Opcode::eType8: return &m_data.inst8; + case Opcode::eType16: return &m_data.inst16; + case Opcode::eType16_2: // passthrough + case Opcode::eType32: return &m_data.inst32; + case Opcode::eType64: return &m_data.inst64; + case Opcode::eTypeBytes: return m_data.inst.bytes; + } + return NULL; + } + + lldb::ByteOrder + GetDataByteOrder () const; + + Opcode::Type m_type; + union + { + uint8_t inst8; + uint16_t inst16; + uint32_t inst32; + uint64_t inst64; + struct + { + uint8_t bytes[16]; // This must be big enough to handle any opcode for any supported target. + uint8_t length; + } inst; + } m_data; + }; + +} // namespace lldb_private + +#endif // lldb_Opcode_h diff --git a/contrib/llvm/tools/lldb/include/lldb/Core/PluginInterface.h b/contrib/llvm/tools/lldb/include/lldb/Core/PluginInterface.h new file mode 100644 index 00000000000..19371ca9860 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Core/PluginInterface.h @@ -0,0 +1,37 @@ +//===-- PluginInterface.h ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_PluginInterface_h_ +#define liblldb_PluginInterface_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private.h" + +namespace lldb_private { + +class PluginInterface +{ +public: + virtual + ~PluginInterface () {} + + virtual ConstString + GetPluginName() = 0; + + virtual uint32_t + GetPluginVersion() = 0; + +}; + +} // namespace lldb_private + +#endif // liblldb_PluginInterface_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Core/PluginManager.h b/contrib/llvm/tools/lldb/include/lldb/Core/PluginManager.h new file mode 100644 index 00000000000..91f8fbb997f --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Core/PluginManager.h @@ -0,0 +1,352 @@ +//===-- PluginManager.h -----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + + +#ifndef liblldb_PluginManager_h_ +#define liblldb_PluginManager_h_ + +#include "lldb/lldb-private.h" +#include "lldb/Host/FileSpec.h" + +namespace lldb_private { + +class PluginManager +{ +public: + static void + Initialize (); + + static void + Terminate (); + + //------------------------------------------------------------------ + // ABI + //------------------------------------------------------------------ + static bool + RegisterPlugin (const ConstString &name, + const char *description, + ABICreateInstance create_callback); + + static bool + UnregisterPlugin (ABICreateInstance create_callback); + + static ABICreateInstance + GetABICreateCallbackAtIndex (uint32_t idx); + + static ABICreateInstance + GetABICreateCallbackForPluginName (const ConstString &name); + + + //------------------------------------------------------------------ + // Disassembler + //------------------------------------------------------------------ + static bool + RegisterPlugin (const ConstString &name, + const char *description, + DisassemblerCreateInstance create_callback); + + static bool + UnregisterPlugin (DisassemblerCreateInstance create_callback); + + static DisassemblerCreateInstance + GetDisassemblerCreateCallbackAtIndex (uint32_t idx); + + static DisassemblerCreateInstance + GetDisassemblerCreateCallbackForPluginName (const ConstString &name); + + + //------------------------------------------------------------------ + // DynamicLoader + //------------------------------------------------------------------ + static bool + RegisterPlugin (const ConstString &name, + const char *description, + DynamicLoaderCreateInstance create_callback, + DebuggerInitializeCallback debugger_init_callback = NULL); + + static bool + UnregisterPlugin (DynamicLoaderCreateInstance create_callback); + + static DynamicLoaderCreateInstance + GetDynamicLoaderCreateCallbackAtIndex (uint32_t idx); + + static DynamicLoaderCreateInstance + GetDynamicLoaderCreateCallbackForPluginName (const ConstString &name); + + //------------------------------------------------------------------ + // EmulateInstruction + //------------------------------------------------------------------ + static bool + RegisterPlugin (const ConstString &name, + const char *description, + EmulateInstructionCreateInstance create_callback); + + static bool + UnregisterPlugin (EmulateInstructionCreateInstance create_callback); + + static EmulateInstructionCreateInstance + GetEmulateInstructionCreateCallbackAtIndex (uint32_t idx); + + static EmulateInstructionCreateInstance + GetEmulateInstructionCreateCallbackForPluginName (const ConstString &name); + + //------------------------------------------------------------------ + // OperatingSystem + //------------------------------------------------------------------ + static bool + RegisterPlugin (const ConstString &name, + const char *description, + OperatingSystemCreateInstance create_callback); + + static bool + UnregisterPlugin (OperatingSystemCreateInstance create_callback); + + static OperatingSystemCreateInstance + GetOperatingSystemCreateCallbackAtIndex (uint32_t idx); + + static OperatingSystemCreateInstance + GetOperatingSystemCreateCallbackForPluginName (const ConstString &name); + + //------------------------------------------------------------------ + // LanguageRuntime + //------------------------------------------------------------------ + static bool + RegisterPlugin (const ConstString &name, + const char *description, + LanguageRuntimeCreateInstance create_callback); + + static bool + UnregisterPlugin (LanguageRuntimeCreateInstance create_callback); + + static LanguageRuntimeCreateInstance + GetLanguageRuntimeCreateCallbackAtIndex (uint32_t idx); + + static LanguageRuntimeCreateInstance + GetLanguageRuntimeCreateCallbackForPluginName (const ConstString &name); + + + //------------------------------------------------------------------ + // ObjectFile + //------------------------------------------------------------------ + static bool + RegisterPlugin (const ConstString &name, + const char *description, + ObjectFileCreateInstance create_callback, + ObjectFileCreateMemoryInstance create_memory_callback, + ObjectFileGetModuleSpecifications get_module_specifications); + + static bool + UnregisterPlugin (ObjectFileCreateInstance create_callback); + + static ObjectFileCreateInstance + GetObjectFileCreateCallbackAtIndex (uint32_t idx); + + static ObjectFileCreateMemoryInstance + GetObjectFileCreateMemoryCallbackAtIndex (uint32_t idx); + + static ObjectFileGetModuleSpecifications + GetObjectFileGetModuleSpecificationsCallbackAtIndex (uint32_t idx); + + static ObjectFileCreateInstance + GetObjectFileCreateCallbackForPluginName (const ConstString &name); + + static ObjectFileCreateMemoryInstance + GetObjectFileCreateMemoryCallbackForPluginName (const ConstString &name); + + + //------------------------------------------------------------------ + // ObjectContainer + //------------------------------------------------------------------ + static bool + RegisterPlugin (const ConstString &name, + const char *description, + ObjectContainerCreateInstance create_callback, + ObjectFileGetModuleSpecifications get_module_specifications); + + static bool + UnregisterPlugin (ObjectContainerCreateInstance create_callback); + + static ObjectContainerCreateInstance + GetObjectContainerCreateCallbackAtIndex (uint32_t idx); + + static ObjectContainerCreateInstance + GetObjectContainerCreateCallbackForPluginName (const ConstString &name); + + static ObjectFileGetModuleSpecifications + GetObjectContainerGetModuleSpecificationsCallbackAtIndex (uint32_t idx); + + //------------------------------------------------------------------ + // LogChannel + //------------------------------------------------------------------ + static bool + RegisterPlugin (const ConstString &name, + const char *description, + LogChannelCreateInstance create_callback); + + static bool + UnregisterPlugin (LogChannelCreateInstance create_callback); + + static LogChannelCreateInstance + GetLogChannelCreateCallbackAtIndex (uint32_t idx); + + static LogChannelCreateInstance + GetLogChannelCreateCallbackForPluginName (const ConstString &name); + + static const char * + GetLogChannelCreateNameAtIndex (uint32_t idx); + + //------------------------------------------------------------------ + // Platform + //------------------------------------------------------------------ + static bool + RegisterPlugin (const ConstString &name, + const char *description, + PlatformCreateInstance create_callback, + DebuggerInitializeCallback debugger_init_callback = NULL); + + static bool + UnregisterPlugin (PlatformCreateInstance create_callback); + + static PlatformCreateInstance + GetPlatformCreateCallbackAtIndex (uint32_t idx); + + static PlatformCreateInstance + GetPlatformCreateCallbackForPluginName (const ConstString &name); + + static const char * + GetPlatformPluginNameAtIndex (uint32_t idx); + + static const char * + GetPlatformPluginDescriptionAtIndex (uint32_t idx); + + static size_t + AutoCompletePlatformName (const char *partial_name, + StringList &matches); + //------------------------------------------------------------------ + // Process + //------------------------------------------------------------------ + static bool + RegisterPlugin (const ConstString &name, + const char *description, + ProcessCreateInstance create_callback, + DebuggerInitializeCallback debugger_init_callback = NULL); + + static bool + UnregisterPlugin (ProcessCreateInstance create_callback); + + static ProcessCreateInstance + GetProcessCreateCallbackAtIndex (uint32_t idx); + + static ProcessCreateInstance + GetProcessCreateCallbackForPluginName (const ConstString &name); + + static const char * + GetProcessPluginNameAtIndex (uint32_t idx); + + static const char * + GetProcessPluginDescriptionAtIndex (uint32_t idx); + + //------------------------------------------------------------------ + // SymbolFile + //------------------------------------------------------------------ + static bool + RegisterPlugin (const ConstString &name, + const char *description, + SymbolFileCreateInstance create_callback); + + static bool + UnregisterPlugin (SymbolFileCreateInstance create_callback); + + static SymbolFileCreateInstance + GetSymbolFileCreateCallbackAtIndex (uint32_t idx); + + static SymbolFileCreateInstance + GetSymbolFileCreateCallbackForPluginName (const ConstString &name); + + + //------------------------------------------------------------------ + // SymbolVendor + //------------------------------------------------------------------ + static bool + RegisterPlugin (const ConstString &name, + const char *description, + SymbolVendorCreateInstance create_callback); + + static bool + UnregisterPlugin (SymbolVendorCreateInstance create_callback); + + static SymbolVendorCreateInstance + GetSymbolVendorCreateCallbackAtIndex (uint32_t idx); + + static SymbolVendorCreateInstance + GetSymbolVendorCreateCallbackForPluginName (const ConstString &name); + + //------------------------------------------------------------------ + // UnwindAssembly + //------------------------------------------------------------------ + static bool + RegisterPlugin (const ConstString &name, + const char *description, + UnwindAssemblyCreateInstance create_callback); + + static bool + UnregisterPlugin (UnwindAssemblyCreateInstance create_callback); + + static UnwindAssemblyCreateInstance + GetUnwindAssemblyCreateCallbackAtIndex (uint32_t idx); + + static UnwindAssemblyCreateInstance + GetUnwindAssemblyCreateCallbackForPluginName (const ConstString &name); + + //------------------------------------------------------------------ + // Some plug-ins might register a DebuggerInitializeCallback + // callback when registering the plug-in. After a new Debugger + // instance is created, this DebuggerInitialize function will get + // called. This allows plug-ins to install Properties and do any + // other initialization that requires a debugger instance. + //------------------------------------------------------------------ + static void + DebuggerInitialize (Debugger &debugger); + + static lldb::OptionValuePropertiesSP + GetSettingForDynamicLoaderPlugin (Debugger &debugger, + const ConstString &setting_name); + + static bool + CreateSettingForDynamicLoaderPlugin (Debugger &debugger, + const lldb::OptionValuePropertiesSP &properties_sp, + const ConstString &description, + bool is_global_property); + + static lldb::OptionValuePropertiesSP + GetSettingForPlatformPlugin (Debugger &debugger, + const ConstString &setting_name); + + static bool + CreateSettingForPlatformPlugin (Debugger &debugger, + const lldb::OptionValuePropertiesSP &properties_sp, + const ConstString &description, + bool is_global_property); + + static lldb::OptionValuePropertiesSP + GetSettingForProcessPlugin (Debugger &debugger, + const ConstString &setting_name); + + static bool + CreateSettingForProcessPlugin (Debugger &debugger, + const lldb::OptionValuePropertiesSP &properties_sp, + const ConstString &description, + bool is_global_property); + +}; + + +} // namespace lldb_private + +#endif // liblldb_PluginManager_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Core/RangeMap.h b/contrib/llvm/tools/lldb/include/lldb/Core/RangeMap.h new file mode 100644 index 00000000000..ee42467c18b --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Core/RangeMap.h @@ -0,0 +1,1543 @@ +//===-- RangeMap.h ----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_RangeMap_h_ +#define liblldb_RangeMap_h_ + +#include + +#include "lldb/lldb-private.h" +#include "llvm/ADT/SmallVector.h" + +// Uncomment to make sure all Range objects are sorted when needed +//#define ASSERT_RANGEMAP_ARE_SORTED + +namespace lldb_private { + + + //---------------------------------------------------------------------- + // Templatized classes for dealing with generic ranges and also + // collections of ranges, or collections of ranges that have associated + // data. + //---------------------------------------------------------------------- + + //---------------------------------------------------------------------- + // A simple range class where you get to define the type of the range + // base "B", and the type used for the range byte size "S". + //---------------------------------------------------------------------- + template + struct Range + { + typedef B BaseType; + typedef S SizeType; + + BaseType base; + SizeType size; + + Range () : + base (0), + size (0) + { + } + + Range (BaseType b, SizeType s) : + base (b), + size (s) + { + } + + void + Clear (BaseType b = 0) + { + base = b; + size = 0; + } + + // Set the start value for the range, and keep the same size + BaseType + GetRangeBase () const + { + return base; + } + + void + SetRangeBase (BaseType b) + { + base = b; + } + + void + Slide (BaseType slide) + { + base += slide; + } + + BaseType + GetRangeEnd () const + { + return base + size; + } + + void + SetRangeEnd (BaseType end) + { + if (end > base) + size = end - base; + else + size = 0; + } + + SizeType + GetByteSize () const + { + return size; + } + + void + SetByteSize (SizeType s) + { + size = s; + } + + bool + IsValid() const + { + return size > 0; + } + + bool + Contains (BaseType r) const + { + return (GetRangeBase() <= r) && (r < GetRangeEnd()); + } + + bool + ContainsEndInclusive (BaseType r) const + { + return (GetRangeBase() <= r) && (r <= GetRangeEnd()); + } + + bool + Contains (const Range& range) const + { + return Contains(range.GetRangeBase()) && ContainsEndInclusive(range.GetRangeEnd()); + } + + bool + Overlap (const Range &rhs) const + { + const BaseType lhs_base = this->GetRangeBase(); + const BaseType rhs_base = rhs.GetRangeBase(); + const BaseType lhs_end = this->GetRangeEnd(); + const BaseType rhs_end = rhs.GetRangeEnd(); + bool result = (lhs_base <= rhs_end) && (lhs_end >= rhs_base); + return result; + } + + bool + operator < (const Range &rhs) const + { + if (base == rhs.base) + return size < rhs.size; + return base < rhs.base; + } + + bool + operator == (const Range &rhs) const + { + return base == rhs.base && size == rhs.size; + } + + bool + operator != (const Range &rhs) const + { + return base != rhs.base || size != rhs.size; + } + }; + + //---------------------------------------------------------------------- + // A range array class where you get to define the type of the ranges + // that the collection contains. + //---------------------------------------------------------------------- + + template + class RangeArray + { + public: + typedef B BaseType; + typedef S SizeType; + typedef Range Entry; + typedef llvm::SmallVector Collection; + + RangeArray () : + m_entries () + { + } + + ~RangeArray() + { + } + + void + Append (const Entry &entry) + { + m_entries.push_back (entry); + } + + bool + RemoveEntrtAtIndex (uint32_t idx) + { + if (idx < m_entries.size()) + { + m_entries.erase (m_entries.begin() + idx); + return true; + } + return false; + } + + void + Sort () + { + if (m_entries.size() > 1) + std::stable_sort (m_entries.begin(), m_entries.end()); + } + +#ifdef ASSERT_RANGEMAP_ARE_SORTED + bool + IsSorted () const + { + typename Collection::const_iterator pos, end, prev; + // First we determine if we can combine any of the Entry objects so we + // don't end up allocating and making a new collection for no reason + for (pos = m_entries.begin(), end = m_entries.end(), prev = end; pos != end; prev = pos++) + { + if (prev != end && *pos < *prev) + return false; + } + return true; + } +#endif + void + CombineConsecutiveRanges () + { +#ifdef ASSERT_RANGEMAP_ARE_SORTED + assert (IsSorted()); +#endif + // Can't combine if ranges if we have zero or one range + if (m_entries.size() > 1) + { + // The list should be sorted prior to calling this function + typename Collection::iterator pos; + typename Collection::iterator end; + typename Collection::iterator prev; + bool can_combine = false; + // First we determine if we can combine any of the Entry objects so we + // don't end up allocating and making a new collection for no reason + for (pos = m_entries.begin(), end = m_entries.end(), prev = end; pos != end; prev = pos++) + { + if (prev != end && prev->Overlap(*pos)) + { + can_combine = true; + break; + } + } + + // We we can combine at least one entry, then we make a new collection + // and populate it accordingly, and then swap it into place. + if (can_combine) + { + Collection minimal_ranges; + for (pos = m_entries.begin(), end = m_entries.end(), prev = end; pos != end; prev = pos++) + { + if (prev != end && prev->Overlap(*pos)) + minimal_ranges.back().SetRangeEnd (std::max(prev->GetRangeEnd(), pos->GetRangeEnd())); + else + minimal_ranges.push_back (*pos); + } + // Use the swap technique in case our new vector is much smaller. + // We must swap when using the STL because std::vector objects never + // release or reduce the memory once it has been allocated/reserved. + m_entries.swap (minimal_ranges); + } + } + } + + + BaseType + GetMinRangeBase (BaseType fail_value) const + { +#ifdef ASSERT_RANGEMAP_ARE_SORTED + assert (IsSorted()); +#endif + if (m_entries.empty()) + return fail_value; + // m_entries must be sorted, so if we aren't empty, we grab the + // first range's base + return m_entries.front().GetRangeBase(); + } + + BaseType + GetMaxRangeEnd (BaseType fail_value) const + { +#ifdef ASSERT_RANGEMAP_ARE_SORTED + assert (IsSorted()); +#endif + if (m_entries.empty()) + return fail_value; + // m_entries must be sorted, so if we aren't empty, we grab the + // last range's end + return m_entries.back().GetRangeEnd(); + } + + void + Slide (BaseType slide) + { + typename Collection::iterator pos, end; + for (pos = m_entries.begin(), end = m_entries.end(); pos != end; ++pos) + pos->Slide (slide); + } + + void + Clear () + { + m_entries.clear(); + } + + bool + IsEmpty () const + { + return m_entries.empty(); + } + + size_t + GetSize () const + { + return m_entries.size(); + } + + const Entry * + GetEntryAtIndex (size_t i) const + { + if (iContains(addr)) + { + return std::distance (begin, pos); + } + else if (pos != begin) + { + --pos; + if (pos->Contains(addr)) + return std::distance (begin, pos); + } + } + return UINT32_MAX; + } + + const Entry * + FindEntryThatContains (B addr) const + { +#ifdef ASSERT_RANGEMAP_ARE_SORTED + assert (IsSorted()); +#endif + if (!m_entries.empty()) + { + Entry entry (addr, 1); + typename Collection::const_iterator begin = m_entries.begin(); + typename Collection::const_iterator end = m_entries.end(); + typename Collection::const_iterator pos = std::lower_bound (begin, end, entry, BaseLessThan); + + if (pos != end && pos->Contains(addr)) + { + return &(*pos); + } + else if (pos != begin) + { + --pos; + if (pos->Contains(addr)) + { + return &(*pos); + } + } + } + return NULL; + } + + const Entry * + FindEntryThatContains (const Entry &range) const + { +#ifdef ASSERT_RANGEMAP_ARE_SORTED + assert (IsSorted()); +#endif + if (!m_entries.empty()) + { + typename Collection::const_iterator begin = m_entries.begin(); + typename Collection::const_iterator end = m_entries.end(); + typename Collection::const_iterator pos = std::lower_bound (begin, end, range, BaseLessThan); + + if (pos != end && pos->Contains(range)) + { + return &(*pos); + } + else if (pos != begin) + { + --pos; + if (pos->Contains(range)) + { + return &(*pos); + } + } + } + return NULL; + } + + protected: + Collection m_entries; + }; + + template + class RangeVector + { + public: + typedef B BaseType; + typedef S SizeType; + typedef Range Entry; + typedef std::vector Collection; + + RangeVector () : + m_entries () + { + } + + ~RangeVector() + { + } + + void + Append (const Entry &entry) + { + m_entries.push_back (entry); + } + + bool + RemoveEntrtAtIndex (uint32_t idx) + { + if (idx < m_entries.size()) + { + m_entries.erase (m_entries.begin() + idx); + return true; + } + return false; + } + + void + Sort () + { + if (m_entries.size() > 1) + std::stable_sort (m_entries.begin(), m_entries.end()); + } + +#ifdef ASSERT_RANGEMAP_ARE_SORTED + bool + IsSorted () const + { + typename Collection::const_iterator pos, end, prev; + // First we determine if we can combine any of the Entry objects so we + // don't end up allocating and making a new collection for no reason + for (pos = m_entries.begin(), end = m_entries.end(), prev = end; pos != end; prev = pos++) + { + if (prev != end && *pos < *prev) + return false; + } + return true; + } +#endif + void + CombineConsecutiveRanges () + { +#ifdef ASSERT_RANGEMAP_ARE_SORTED + assert (IsSorted()); +#endif + // Can't combine if ranges if we have zero or one range + if (m_entries.size() > 1) + { + // The list should be sorted prior to calling this function + typename Collection::iterator pos; + typename Collection::iterator end; + typename Collection::iterator prev; + bool can_combine = false; + // First we determine if we can combine any of the Entry objects so we + // don't end up allocating and making a new collection for no reason + for (pos = m_entries.begin(), end = m_entries.end(), prev = end; pos != end; prev = pos++) + { + if (prev != end && prev->Overlap(*pos)) + { + can_combine = true; + break; + } + } + + // We we can combine at least one entry, then we make a new collection + // and populate it accordingly, and then swap it into place. + if (can_combine) + { + Collection minimal_ranges; + for (pos = m_entries.begin(), end = m_entries.end(), prev = end; pos != end; prev = pos++) + { + if (prev != end && prev->Overlap(*pos)) + minimal_ranges.back().SetRangeEnd (std::max(prev->GetRangeEnd(), pos->GetRangeEnd())); + else + minimal_ranges.push_back (*pos); + } + // Use the swap technique in case our new vector is much smaller. + // We must swap when using the STL because std::vector objects never + // release or reduce the memory once it has been allocated/reserved. + m_entries.swap (minimal_ranges); + } + } + } + + + BaseType + GetMinRangeBase (BaseType fail_value) const + { +#ifdef ASSERT_RANGEMAP_ARE_SORTED + assert (IsSorted()); +#endif + if (m_entries.empty()) + return fail_value; + // m_entries must be sorted, so if we aren't empty, we grab the + // first range's base + return m_entries.front().GetRangeBase(); + } + + BaseType + GetMaxRangeEnd (BaseType fail_value) const + { +#ifdef ASSERT_RANGEMAP_ARE_SORTED + assert (IsSorted()); +#endif + if (m_entries.empty()) + return fail_value; + // m_entries must be sorted, so if we aren't empty, we grab the + // last range's end + return m_entries.back().GetRangeEnd(); + } + + void + Slide (BaseType slide) + { + typename Collection::iterator pos, end; + for (pos = m_entries.begin(), end = m_entries.end(); pos != end; ++pos) + pos->Slide (slide); + } + + void + Clear () + { + m_entries.clear(); + } + + void + Reserve (typename Collection::size_type size) + { + m_entries.resize (size); + } + + bool + IsEmpty () const + { + return m_entries.empty(); + } + + size_t + GetSize () const + { + return m_entries.size(); + } + + const Entry * + GetEntryAtIndex (size_t i) const + { + if (iContains(addr)) + { + return std::distance (begin, pos); + } + else if (pos != begin) + { + --pos; + if (pos->Contains(addr)) + return std::distance (begin, pos); + } + } + return UINT32_MAX; + } + + const Entry * + FindEntryThatContains (B addr) const + { +#ifdef ASSERT_RANGEMAP_ARE_SORTED + assert (IsSorted()); +#endif + if (!m_entries.empty()) + { + Entry entry (addr, 1); + typename Collection::const_iterator begin = m_entries.begin(); + typename Collection::const_iterator end = m_entries.end(); + typename Collection::const_iterator pos = std::lower_bound (begin, end, entry, BaseLessThan); + + if (pos != end && pos->Contains(addr)) + { + return &(*pos); + } + else if (pos != begin) + { + --pos; + if (pos->Contains(addr)) + { + return &(*pos); + } + } + } + return NULL; + } + + const Entry * + FindEntryThatContains (const Entry &range) const + { +#ifdef ASSERT_RANGEMAP_ARE_SORTED + assert (IsSorted()); +#endif + if (!m_entries.empty()) + { + typename Collection::const_iterator begin = m_entries.begin(); + typename Collection::const_iterator end = m_entries.end(); + typename Collection::const_iterator pos = std::lower_bound (begin, end, range, BaseLessThan); + + if (pos != end && pos->Contains(range)) + { + return &(*pos); + } + else if (pos != begin) + { + --pos; + if (pos->Contains(range)) + { + return &(*pos); + } + } + } + return NULL; + } + + protected: + Collection m_entries; + }; + + //---------------------------------------------------------------------- + // A simple range with data class where you get to define the type of + // the range base "B", the type used for the range byte size "S", and + // the type for the associated data "T". + //---------------------------------------------------------------------- + template + struct RangeData : public Range + { + typedef T DataType; + + DataType data; + + RangeData () : + Range (), + data () + { + } + + RangeData (B base, S size) : + Range (base, size), + data () + { + } + + RangeData (B base, S size, DataType d) : + Range (base, size), + data (d) + { + } + + bool + operator < (const RangeData &rhs) const + { + if (this->base == rhs.base) + { + if (this->size == rhs.size) + return this->data < rhs.data; + else + return this->size < rhs.size; + } + return this->base < rhs.base; + } + + bool + operator == (const RangeData &rhs) const + { + return this->GetRangeBase() == rhs.GetRangeBase() && + this->GetByteSize() == rhs.GetByteSize() && + this->data == rhs.data; + } + + bool + operator != (const RangeData &rhs) const + { + return this->GetRangeBase() != rhs.GetRangeBase() || + this->GetByteSize() != rhs.GetByteSize() || + this->data != rhs.data; + } + }; + + template + class RangeDataArray + { + public: + typedef RangeData Entry; + typedef llvm::SmallVector Collection; + + + RangeDataArray () + { + } + + ~RangeDataArray() + { + } + + void + Append (const Entry &entry) + { + m_entries.push_back (entry); + } + + void + Sort () + { + if (m_entries.size() > 1) + std::stable_sort (m_entries.begin(), m_entries.end()); + } + +#ifdef ASSERT_RANGEMAP_ARE_SORTED + bool + IsSorted () const + { + typename Collection::const_iterator pos, end, prev; + // First we determine if we can combine any of the Entry objects so we + // don't end up allocating and making a new collection for no reason + for (pos = m_entries.begin(), end = m_entries.end(), prev = end; pos != end; prev = pos++) + { + if (prev != end && *pos < *prev) + return false; + } + return true; + } +#endif + + void + CombineConsecutiveEntriesWithEqualData () + { +#ifdef ASSERT_RANGEMAP_ARE_SORTED + assert (IsSorted()); +#endif + typename Collection::iterator pos; + typename Collection::iterator end; + typename Collection::iterator prev; + bool can_combine = false; + // First we determine if we can combine any of the Entry objects so we + // don't end up allocating and making a new collection for no reason + for (pos = m_entries.begin(), end = m_entries.end(), prev = end; pos != end; prev = pos++) + { + if (prev != end && prev->data == pos->data) + { + can_combine = true; + break; + } + } + + // We we can combine at least one entry, then we make a new collection + // and populate it accordingly, and then swap it into place. + if (can_combine) + { + Collection minimal_ranges; + for (pos = m_entries.begin(), end = m_entries.end(), prev = end; pos != end; prev = pos++) + { + if (prev != end && prev->data == pos->data) + minimal_ranges.back().SetRangeEnd (pos->GetRangeEnd()); + else + minimal_ranges.push_back (*pos); + } + // Use the swap technique in case our new vector is much smaller. + // We must swap when using the STL because std::vector objects never + // release or reduce the memory once it has been allocated/reserved. + m_entries.swap (minimal_ranges); + } + } + + void + Clear () + { + m_entries.clear(); + } + + bool + IsEmpty () const + { + return m_entries.empty(); + } + + size_t + GetSize () const + { + return m_entries.size(); + } + + const Entry * + GetEntryAtIndex (size_t i) const + { + if (iContains(addr)) + { + return std::distance (begin, pos); + } + else if (pos != begin) + { + --pos; + if (pos->Contains(addr)) + return std::distance (begin, pos); + } + } + return UINT32_MAX; + } + + Entry * + FindEntryThatContains (B addr) + { +#ifdef ASSERT_RANGEMAP_ARE_SORTED + assert (IsSorted()); +#endif + if ( !m_entries.empty() ) + { + Entry entry; + entry.SetRangeBase(addr); + entry.SetByteSize(1); + typename Collection::iterator begin = m_entries.begin(); + typename Collection::iterator end = m_entries.end(); + typename Collection::iterator pos = std::lower_bound (begin, end, entry, BaseLessThan); + + if (pos != end && pos->Contains(addr)) + { + return &(*pos); + } + else if (pos != begin) + { + --pos; + if (pos->Contains(addr)) + { + return &(*pos); + } + } + } + return NULL; + } + const Entry * + FindEntryThatContains (B addr) const + { +#ifdef ASSERT_RANGEMAP_ARE_SORTED + assert (IsSorted()); +#endif + if ( !m_entries.empty() ) + { + Entry entry; + entry.SetRangeBase(addr); + entry.SetByteSize(1); + typename Collection::const_iterator begin = m_entries.begin(); + typename Collection::const_iterator end = m_entries.end(); + typename Collection::const_iterator pos = std::lower_bound (begin, end, entry, BaseLessThan); + + if (pos != end && pos->Contains(addr)) + { + return &(*pos); + } + else if (pos != begin) + { + --pos; + if (pos->Contains(addr)) + { + return &(*pos); + } + } + } + return NULL; + } + + const Entry * + FindEntryThatContains (const Entry &range) const + { +#ifdef ASSERT_RANGEMAP_ARE_SORTED + assert (IsSorted()); +#endif + if ( !m_entries.empty() ) + { + typename Collection::const_iterator begin = m_entries.begin(); + typename Collection::const_iterator end = m_entries.end(); + typename Collection::const_iterator pos = std::lower_bound (begin, end, range, BaseLessThan); + + if (pos != end && pos->Contains(range)) + { + return &(*pos); + } + else if (pos != begin) + { + --pos; + if (pos->Contains(range)) + { + return &(*pos); + } + } + } + return NULL; + } + + Entry * + Back() + { + if (!m_entries.empty()) + return &m_entries.back(); + return NULL; + } + + const Entry * + Back() const + { + if (!m_entries.empty()) + return &m_entries.back(); + return NULL; + } + + protected: + Collection m_entries; + }; + + // Same as RangeDataArray, but uses std::vector as to not + // require static storage of N items in the class itself + template + class RangeDataVector + { + public: + typedef RangeData Entry; + typedef std::vector Collection; + + RangeDataVector () + { + } + + ~RangeDataVector() + { + } + + void + Append (const Entry &entry) + { + m_entries.push_back (entry); + } + + void + Sort () + { + if (m_entries.size() > 1) + std::stable_sort (m_entries.begin(), m_entries.end()); + } + +#ifdef ASSERT_RANGEMAP_ARE_SORTED + bool + IsSorted () const + { + typename Collection::const_iterator pos, end, prev; + // First we determine if we can combine any of the Entry objects so we + // don't end up allocating and making a new collection for no reason + for (pos = m_entries.begin(), end = m_entries.end(), prev = end; pos != end; prev = pos++) + { + if (prev != end && *pos < *prev) + return false; + } + return true; + } +#endif + + void + CombineConsecutiveEntriesWithEqualData () + { +#ifdef ASSERT_RANGEMAP_ARE_SORTED + assert (IsSorted()); +#endif + typename Collection::iterator pos; + typename Collection::iterator end; + typename Collection::iterator prev; + bool can_combine = false; + // First we determine if we can combine any of the Entry objects so we + // don't end up allocating and making a new collection for no reason + for (pos = m_entries.begin(), end = m_entries.end(), prev = end; pos != end; prev = pos++) + { + if (prev != end && prev->data == pos->data) + { + can_combine = true; + break; + } + } + + // We we can combine at least one entry, then we make a new collection + // and populate it accordingly, and then swap it into place. + if (can_combine) + { + Collection minimal_ranges; + for (pos = m_entries.begin(), end = m_entries.end(), prev = end; pos != end; prev = pos++) + { + if (prev != end && prev->data == pos->data) + minimal_ranges.back().SetRangeEnd (pos->GetRangeEnd()); + else + minimal_ranges.push_back (*pos); + } + // Use the swap technique in case our new vector is much smaller. + // We must swap when using the STL because std::vector objects never + // release or reduce the memory once it has been allocated/reserved. + m_entries.swap (minimal_ranges); + } + } + + // Calculate the byte size of ranges with zero byte sizes by finding + // the next entry with a base address > the current base address + void + CalculateSizesOfZeroByteSizeRanges () + { +#ifdef ASSERT_RANGEMAP_ARE_SORTED + assert (IsSorted()); +#endif + typename Collection::iterator pos; + typename Collection::iterator end; + typename Collection::iterator next; + for (pos = m_entries.begin(), end = m_entries.end(); pos != end; ++pos) + { + if (pos->GetByteSize() == 0) + { + // Watch out for multiple entries with same address and make sure + // we find an entry that is greater than the current base address + // before we use that for the size + auto curr_base = pos->GetRangeBase(); + for (next = pos + 1; next != end; ++next) + { + auto next_base = next->GetRangeBase(); + if (next_base > curr_base) + { + pos->SetByteSize (next_base - curr_base); + break; + } + } + } + } + + } + + void + Clear () + { + m_entries.clear(); + } + + void + Reserve (typename Collection::size_type size) + { + m_entries.resize (size); + } + + bool + IsEmpty () const + { + return m_entries.empty(); + } + + size_t + GetSize () const + { + return m_entries.size(); + } + + const Entry * + GetEntryAtIndex (size_t i) const + { + if (iContains(addr)) + { + return std::distance (begin, pos); + } + else if (pos != begin) + { + --pos; + if (pos->Contains(addr)) + return std::distance (begin, pos); + } + } + return UINT32_MAX; + } + + Entry * + FindEntryThatContains (B addr) + { +#ifdef ASSERT_RANGEMAP_ARE_SORTED + assert (IsSorted()); +#endif + if ( !m_entries.empty() ) + { + Entry entry; + entry.SetRangeBase(addr); + entry.SetByteSize(1); + typename Collection::iterator begin = m_entries.begin(); + typename Collection::iterator end = m_entries.end(); + typename Collection::iterator pos = std::lower_bound (begin, end, entry, BaseLessThan); + + if (pos != end && pos->Contains(addr)) + { + return &(*pos); + } + else if (pos != begin) + { + --pos; + if (pos->Contains(addr)) + { + return &(*pos); + } + } + } + return NULL; + } + const Entry * + FindEntryThatContains (B addr) const + { +#ifdef ASSERT_RANGEMAP_ARE_SORTED + assert (IsSorted()); +#endif + if ( !m_entries.empty() ) + { + Entry entry; + entry.SetRangeBase(addr); + entry.SetByteSize(1); + typename Collection::const_iterator begin = m_entries.begin(); + typename Collection::const_iterator end = m_entries.end(); + typename Collection::const_iterator pos = std::lower_bound (begin, end, entry, BaseLessThan); + + if (pos != end && pos->Contains(addr)) + { + return &(*pos); + } + else if (pos != begin) + { + --pos; + if (pos->Contains(addr)) + { + return &(*pos); + } + } + } + return NULL; + } + + const Entry * + FindEntryThatContains (const Entry &range) const + { +#ifdef ASSERT_RANGEMAP_ARE_SORTED + assert (IsSorted()); +#endif + if ( !m_entries.empty() ) + { + typename Collection::const_iterator begin = m_entries.begin(); + typename Collection::const_iterator end = m_entries.end(); + typename Collection::const_iterator pos = std::lower_bound (begin, end, range, BaseLessThan); + + if (pos != end && pos->Contains(range)) + { + return &(*pos); + } + else if (pos != begin) + { + --pos; + if (pos->Contains(range)) + { + return &(*pos); + } + } + } + return NULL; + } + + Entry * + Back() + { + if (!m_entries.empty()) + return &m_entries.back(); + return NULL; + } + + const Entry * + Back() const + { + if (!m_entries.empty()) + return &m_entries.back(); + return NULL; + } + + protected: + Collection m_entries; + }; + + + //---------------------------------------------------------------------- + // A simple range with data class where you get to define the type of + // the range base "B", the type used for the range byte size "S", and + // the type for the associated data "T". + //---------------------------------------------------------------------- + template + struct AddressData + { + typedef B BaseType; + typedef T DataType; + + BaseType addr; + DataType data; + + AddressData () : + addr (), + data () + { + } + + AddressData (B a, DataType d) : + addr (a), + data (d) + { + } + + bool + operator < (const AddressData &rhs) const + { + if (this->addr == rhs.addr) + return this->data < rhs.data; + return this->addr < rhs.addr; + } + + bool + operator == (const AddressData &rhs) const + { + return this->addr == rhs.addr && + this->data == rhs.data; + } + + bool + operator != (const AddressData &rhs) const + { + return this->addr != rhs.addr || + this->data == rhs.data; + } + }; + + + template + class AddressDataArray + { + public: + typedef AddressData Entry; + typedef llvm::SmallVector Collection; + + + AddressDataArray () + { + } + + ~AddressDataArray() + { + } + + void + Append (const Entry &entry) + { + m_entries.push_back (entry); + } + + void + Sort () + { + if (m_entries.size() > 1) + std::stable_sort (m_entries.begin(), m_entries.end()); + } + +#ifdef ASSERT_RANGEMAP_ARE_SORTED + bool + IsSorted () const + { + typename Collection::const_iterator pos, end, prev; + // First we determine if we can combine any of the Entry objects so we + // don't end up allocating and making a new collection for no reason + for (pos = m_entries.begin(), end = m_entries.end(), prev = end; pos != end; prev = pos++) + { + if (prev != end && *pos < *prev) + return false; + } + return true; + } +#endif + + void + Clear () + { + m_entries.clear(); + } + + bool + IsEmpty () const + { + return m_entries.empty(); + } + + size_t + GetSize () const + { + return m_entries.size(); + } + + const Entry * + GetEntryAtIndex (size_t i) const + { + if (iaddr == addr || !exact_match_only) + return &(*pos); + } + } + return NULL; + } + + const Entry * + FindNextEntry (const Entry *entry) + { + if (entry >= &*m_entries.begin() && entry + 1 < &*m_entries.end()) + return entry + 1; + return NULL; + } + + Entry * + Back() + { + if (!m_entries.empty()) + return &m_entries.back(); + return NULL; + } + + const Entry * + Back() const + { + if (!m_entries.empty()) + return &m_entries.back(); + return NULL; + } + + protected: + Collection m_entries; + }; + +} // namespace lldb_private + +#endif // liblldb_RangeMap_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Core/RegisterValue.h b/contrib/llvm/tools/lldb/include/lldb/Core/RegisterValue.h new file mode 100644 index 00000000000..cf29cea46d3 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Core/RegisterValue.h @@ -0,0 +1,406 @@ +//===-- RegisterValue.h ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef lldb_RegisterValue_h +#define lldb_RegisterValue_h + +// C Includes +#include + +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-public.h" +#include "lldb/lldb-private.h" +#include "lldb/Host/Endian.h" + +//#define ENABLE_128_BIT_SUPPORT 1 +namespace lldb_private { + + class RegisterValue + { + public: + enum + { + kMaxRegisterByteSize = 32u + }; + enum Type + { + eTypeInvalid, + eTypeUInt8, + eTypeUInt16, + eTypeUInt32, + eTypeUInt64, +#if defined (ENABLE_128_BIT_SUPPORT) + eTypeUInt128, +#endif + eTypeFloat, + eTypeDouble, + eTypeLongDouble, + eTypeBytes + }; + + RegisterValue () : + m_type (eTypeInvalid) + { + } + + explicit + RegisterValue (uint8_t inst) : + m_type (eTypeUInt8) + { + m_data.uint8 = inst; + } + + explicit + RegisterValue (uint16_t inst) : + m_type (eTypeUInt16) + { + m_data.uint16 = inst; + } + + explicit + RegisterValue (uint32_t inst) : + m_type (eTypeUInt32) + { + m_data.uint32 = inst; + } + + explicit + RegisterValue (uint64_t inst) : + m_type (eTypeUInt64) + { + m_data.uint64 = inst; + } + +#if defined (ENABLE_128_BIT_SUPPORT) + explicit + RegisterValue (__uint128_t inst) : + m_type (eTypeUInt128) + { + m_data.uint128 = inst; + } +#endif + explicit + RegisterValue (float value) : + m_type (eTypeFloat) + { + m_data.ieee_float = value; + } + + explicit + RegisterValue (double value) : + m_type (eTypeDouble) + { + m_data.ieee_double = value; + } + + explicit + RegisterValue (long double value) : + m_type (eTypeLongDouble) + { + m_data.ieee_long_double = value; + } + + explicit + RegisterValue (uint8_t *bytes, size_t length, lldb::ByteOrder byte_order) + { + SetBytes (bytes, length, byte_order); + } + + RegisterValue::Type + GetType () const + { + return m_type; + } + + bool + CopyValue (const RegisterValue &rhs); + + void + SetType (RegisterValue::Type type) + { + m_type = type; + } + + RegisterValue::Type + SetType (const RegisterInfo *reg_info); + + bool + GetData (DataExtractor &data) const; + + // Copy the register value from this object into a buffer in "dst" + // and obey the "dst_byte_order" when copying the data. Also watch out + // in case "dst_len" is longer or shorter than the register value + // described by "reg_info" and only copy the least significant bytes + // of the register value, or pad the destination with zeroes if the + // register byte size is shorter that "dst_len" (all while correctly + // abiding the "dst_byte_order"). Returns the number of bytes copied + // into "dst". + uint32_t + GetAsMemoryData (const RegisterInfo *reg_info, + void *dst, + uint32_t dst_len, + lldb::ByteOrder dst_byte_order, + Error &error) const; + + uint32_t + SetFromMemoryData (const RegisterInfo *reg_info, + const void *src, + uint32_t src_len, + lldb::ByteOrder src_byte_order, + Error &error); + + bool + GetScalarValue (Scalar &scalar) const; + + uint8_t + GetAsUInt8 (uint8_t fail_value = UINT8_MAX, bool *success_ptr = NULL) const + { + if (m_type == eTypeUInt8) + { + if (success_ptr) + *success_ptr = true; + return m_data.uint8; + } + if (success_ptr) + *success_ptr = true; + return fail_value; + } + + uint16_t + GetAsUInt16 (uint16_t fail_value = UINT16_MAX, bool *success_ptr = NULL) const; + + uint32_t + GetAsUInt32 (uint32_t fail_value = UINT32_MAX, bool *success_ptr = NULL) const; + + uint64_t + GetAsUInt64 (uint64_t fail_value = UINT64_MAX, bool *success_ptr = NULL) const; + +#if defined (ENABLE_128_BIT_SUPPORT) + __uint128_t + GetAsUInt128 (__uint128_t fail_value = ~((__uint128_t)0), bool *success_ptr = NULL) const; +#endif + + float + GetAsFloat (float fail_value = 0.0f, bool *success_ptr = NULL) const; + + double + GetAsDouble (double fail_value = 0.0, bool *success_ptr = NULL) const; + + long double + GetAsLongDouble (long double fail_value = 0.0, bool *success_ptr = NULL) const; + + void + SetValueToInvalid () + { + m_type = eTypeInvalid; + } + + bool + ClearBit (uint32_t bit); + + bool + SetBit (uint32_t bit); + + bool + operator == (const RegisterValue &rhs) const; + + bool + operator != (const RegisterValue &rhs) const; + + void + operator = (uint8_t uint) + { + m_type = eTypeUInt8; + m_data.uint8 = uint; + } + + void + operator = (uint16_t uint) + { + m_type = eTypeUInt16; + m_data.uint16 = uint; + } + + void + operator = (uint32_t uint) + { + m_type = eTypeUInt32; + m_data.uint32 = uint; + } + + void + operator = (uint64_t uint) + { + m_type = eTypeUInt64; + m_data.uint64 = uint; + } + +#if defined (ENABLE_128_BIT_SUPPORT) + void + operator = (__uint128_t uint) + { + m_type = eTypeUInt128; + m_data.uint128 = uint; + } +#endif + void + operator = (float f) + { + m_type = eTypeFloat; + m_data.ieee_float = f; + } + + void + operator = (double f) + { + m_type = eTypeDouble; + m_data.ieee_double = f; + } + + void + operator = (long double f) + { + m_type = eTypeLongDouble; + m_data.ieee_long_double = f; + } + + void + SetUInt8 (uint8_t uint) + { + m_type = eTypeUInt8; + m_data.uint8 = uint; + } + + void + SetUInt16 (uint16_t uint) + { + m_type = eTypeUInt16; + m_data.uint16 = uint; + } + + void + SetUInt32 (uint32_t uint, Type t = eTypeUInt32) + { + m_type = t; + m_data.uint32 = uint; + } + + void + SetUInt64 (uint64_t uint, Type t = eTypeUInt64) + { + m_type = t; + m_data.uint64 = uint; + } + +#if defined (ENABLE_128_BIT_SUPPORT) + void + SetUInt128 (__uint128_t uint) + { + m_type = eTypeUInt128; + m_data.uint128 = uint; + } +#endif + bool + SetUInt (uint64_t uint, uint32_t byte_size); + + void + SetFloat (float f) + { + m_type = eTypeFloat; + m_data.ieee_float = f; + } + + void + SetDouble (double f) + { + m_type = eTypeDouble; + m_data.ieee_double = f; + } + + void + SetLongDouble (long double f) + { + m_type = eTypeLongDouble; + m_data.ieee_long_double = f; + } + + void + SetBytes (const void *bytes, size_t length, lldb::ByteOrder byte_order); + + bool + SignExtend (uint32_t sign_bitpos); + + Error + SetValueFromCString (const RegisterInfo *reg_info, + const char *value_str); + + Error + SetValueFromData (const RegisterInfo *reg_info, + DataExtractor &data, + lldb::offset_t offset, + bool partial_data_ok); + + // The default value of 0 for reg_name_right_align_at means no alignment at all. + bool + Dump (Stream *s, + const RegisterInfo *reg_info, + bool prefix_with_name, + bool prefix_with_alt_name, + lldb::Format format, + uint32_t reg_name_right_align_at = 0) const; + + void * + GetBytes (); + + const void * + GetBytes () const; + + lldb::ByteOrder + GetByteOrder () const + { + if (m_type == eTypeBytes) + return m_data.buffer.byte_order; + return lldb::endian::InlHostByteOrder(); + } + + uint32_t + GetByteSize () const; + + void + Clear(); + + protected: + + RegisterValue::Type m_type; + union + { + uint8_t uint8; + uint16_t uint16; + uint32_t uint32; + uint64_t uint64; +#if defined (ENABLE_128_BIT_SUPPORT) + __uint128_t uint128; +#endif + float ieee_float; + double ieee_double; + long double ieee_long_double; + struct + { + uint8_t bytes[kMaxRegisterByteSize]; // This must be big enough to hold any register for any supported target. + uint8_t length; + lldb::ByteOrder byte_order; + } buffer; + } m_data; + }; + +} // namespace lldb_private + +#endif // lldb_RegisterValue_h diff --git a/contrib/llvm/tools/lldb/include/lldb/Core/RegularExpression.h b/contrib/llvm/tools/lldb/include/lldb/Core/RegularExpression.h new file mode 100644 index 00000000000..eeeb914bfa9 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Core/RegularExpression.h @@ -0,0 +1,253 @@ +//===-- RegularExpression.h -------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_DBRegex_h_ +#define liblldb_DBRegex_h_ +#if defined(__cplusplus) + +#include +#include + +#include +#include + +namespace llvm +{ + class StringRef; +} + +namespace lldb_private { + +//---------------------------------------------------------------------- +/// @class RegularExpression RegularExpression.h "lldb/Core/RegularExpression.h" +/// @brief A C++ wrapper class for regex. +/// +/// This regular expression class wraps the posix regex functions +/// \c regcomp(), \c regerror(), \c regexec(), and \c regfree() from +/// the header file in \c /usr/include/regex\.h. +//---------------------------------------------------------------------- +class RegularExpression +{ +public: + class Match + { + public: + Match (uint32_t max_matches) : + m_matches () + { + if (max_matches > 0) + m_matches.resize(max_matches + 1); + } + + void + Clear() + { + const size_t num_matches = m_matches.size(); + regmatch_t invalid_match = { -1, -1 }; + for (size_t i=0; i m_matches; ///< Where parenthesized subexpressions results are stored + }; + //------------------------------------------------------------------ + /// Default constructor. + /// + /// The default constructor that initializes the object state such + /// that it contains no compiled regular expression. + //------------------------------------------------------------------ + RegularExpression (); + + //------------------------------------------------------------------ + /// Constructor that takes a regulare expression with flags. + /// + /// Constructor that compiles \a re using \a flags and stores the + /// resulting compiled regular expression into this object. + /// + /// @param[in] re + /// A c string that represents the regular expression to + /// compile. + /// + /// @param[in] flags + /// Flags that are passed the the \c regcomp() function. + //------------------------------------------------------------------ + explicit + RegularExpression (const char* re, int flags); + + // This one uses flags = REG_EXTENDED. + explicit + RegularExpression (const char* re); + + //------------------------------------------------------------------ + /// Destructor. + /// + /// Any previosuly compiled regular expression contained in this + /// object will be freed. + //------------------------------------------------------------------ + ~RegularExpression (); + + RegularExpression (const RegularExpression &rhs); + + const RegularExpression & operator=(const RegularExpression &rhs); + + //------------------------------------------------------------------ + /// Compile a regular expression. + /// + /// Compile a regular expression using the supplied regular + /// expression text and flags. The compied regular expression lives + /// in this object so that it can be readily used for regular + /// expression matches. Execute() can be called after the regular + /// expression is compiled. Any previosuly compiled regular + /// expression contained in this object will be freed. + /// + /// @param[in] re + /// A NULL terminated C string that represents the regular + /// expression to compile. + /// + /// @param[in] flags + /// Flags that are passed the the \c regcomp() function. + /// + /// @return + /// \b true if the regular expression compiles successfully, + /// \b false otherwise. + //------------------------------------------------------------------ + bool + Compile (const char* re); + + bool + Compile (const char* re, int flags); + + //------------------------------------------------------------------ + /// Executes a regular expression. + /// + /// Execute a regular expression match using the compiled regular + /// expression that is already in this object against the match + /// string \a s. If any parens are used for regular expression + /// matches \a match_count should indicate the number of regmatch_t + /// values that are present in \a match_ptr. The regular expression + /// will be executed using the \a execute_flags + /// + /// @param[in] string + /// The string to match against the compile regular expression. + /// + /// @param[in] match + /// A pointer to a RegularExpression::Match structure that was + /// properly initialized with the desired number of maximum + /// matches, or NULL if no parenthesized matching is needed. + /// + /// @param[in] execute_flags + /// Flags to pass to the \c regexec() function. + /// + /// @return + /// \b true if \a string matches the compiled regular + /// expression, \b false otherwise. + //------------------------------------------------------------------ + bool + Execute (const char* string, Match *match = NULL, int execute_flags = 0) const; + + size_t + GetErrorAsCString (char *err_str, size_t err_str_max_len) const; + + //------------------------------------------------------------------ + /// Free the compiled regular expression. + /// + /// If this object contains a valid compiled regular expression, + /// this function will free any resources it was consuming. + //------------------------------------------------------------------ + void + Free (); + + //------------------------------------------------------------------ + /// Access the regular expression text. + /// + /// Returns the text that was used to compile the current regular + /// expression. + /// + /// @return + /// The NULL terminated C string that was used to compile the + /// current regular expression + //------------------------------------------------------------------ + const char* + GetText () const; + + int + GetCompileFlags () const + { + return m_compile_flags; + } + + //------------------------------------------------------------------ + /// Test if valid. + /// + /// Test if this object contains a valid regular expression. + /// + /// @return + /// \b true if the regular expression compiled and is ready + /// for execution, \b false otherwise. + //------------------------------------------------------------------ + bool + IsValid () const; + + void + Clear () + { + Free(); + m_re.clear(); + m_compile_flags = 0; + m_comp_err = 1; + } + + int + GetErrorCode() const + { + return m_comp_err; + } + + bool + operator < (const RegularExpression& rhs) const; + +private: + //------------------------------------------------------------------ + // Member variables + //------------------------------------------------------------------ + std::string m_re; ///< A copy of the original regular expression text + int m_comp_err; ///< Error code for the regular expression compilation + regex_t m_preg; ///< The compiled regular expression + int m_compile_flags; ///< Stores the flags from the last compile. +}; + +} // namespace lldb_private + +#endif // #if defined(__cplusplus) +#endif // liblldb_DBRegex_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Core/STLUtils.h b/contrib/llvm/tools/lldb/include/lldb/Core/STLUtils.h new file mode 100644 index 00000000000..9321e057a39 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Core/STLUtils.h @@ -0,0 +1,94 @@ +//===-- STLUtils.h ----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_STLUtils_h_ +#define liblldb_STLUtils_h_ +#if defined(__cplusplus) + +#include + +#include +#include +#include + +//---------------------------------------------------------------------- +// C string less than compare function object +//---------------------------------------------------------------------- +struct CStringCompareFunctionObject +{ + bool operator() (const char* s1, const char* s2) const + { + return strcmp(s1, s2) < 0; + } +}; + + +//---------------------------------------------------------------------- +// C string equality function object (binary predicate). +//---------------------------------------------------------------------- +struct CStringEqualBinaryPredicate +{ + bool operator()(const char* s1, const char* s2) const + { + return strcmp(s1, s2) == 0; + } +}; + + +//---------------------------------------------------------------------- +// Templated type for finding an entry in a std::map whose value +// is equal to something +//---------------------------------------------------------------------- +template +class ValueEquals +{ +private: + S second_value; + +public: + ValueEquals (const S& val) : second_value(val) + {} + // Compare the second item + bool operator() (std::pair elem) + { + return elem.second == second_value; + } +}; + +template +inline void PrintAllCollectionElements (std::ostream &s, const T& coll, const char* header_cstr=NULL, const char* separator_cstr=" ") +{ + typename T::const_iterator pos; + + if (header_cstr) + s << header_cstr; + for (pos=coll.begin(); pos!=coll.end(); ++pos) { + s << *pos << separator_cstr; + } + s << std::endl; +} + +// The function object below can be used to delete a STL container that +// contains C++ object pointers. +// +// Usage: std::for_each(vector.begin(), vector.end(), for_each_delete()); + +struct for_each_cplusplus_delete +{ + template + void operator()(T *ptr){ delete ptr;} +}; + +typedef std::vector STLStringArray; +typedef std::vector CStringArray; + + + +#endif // #if defined(__cplusplus) +#endif // liblldb_STLUtils_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Core/Scalar.h b/contrib/llvm/tools/lldb/include/lldb/Core/Scalar.h new file mode 100644 index 00000000000..821a0fb1ae2 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Core/Scalar.h @@ -0,0 +1,341 @@ +//===-- Scalar.h ------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_Scalar_h_ +#define liblldb_Scalar_h_ + +#include "lldb/lldb-private.h" + +namespace lldb_private { + +//---------------------------------------------------------------------- +// A class designed to hold onto values and their corresponding types. +// Operators are defined and Scalar objects will correctly promote +// their types and values before performing these operations. Type +// promotion currently follows the ANSI C type promotion rules. +//---------------------------------------------------------------------- +class Scalar +{ +public: + enum Type + { + e_void = 0, + e_sint, + e_uint, + e_slong, + e_ulong, + e_slonglong, + e_ulonglong, + e_float, + e_double, + e_long_double + }; + + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + Scalar(); + Scalar(int v) : m_type(e_sint), m_data() { m_data.sint = v; } + Scalar(unsigned int v) : m_type(e_uint), m_data() { m_data.uint = v; } + Scalar(long v) : m_type(e_slong), m_data() { m_data.slong = v; } + Scalar(unsigned long v) : m_type(e_ulong), m_data() { m_data.ulong = v; } + Scalar(long long v) : m_type(e_slonglong), m_data() { m_data.slonglong = v; } + Scalar(unsigned long long v): m_type(e_ulonglong), m_data() { m_data.ulonglong = v; } + Scalar(float v) : m_type(e_float), m_data() { m_data.flt = v; } + Scalar(double v) : m_type(e_double), m_data() { m_data.dbl = v; } + Scalar(long double v) : m_type(e_long_double), m_data() { m_data.ldbl = v; } + Scalar(const Scalar& rhs); + //Scalar(const RegisterValue& reg_value); + virtual ~Scalar(); + + bool + SignExtend (uint32_t bit_pos); + + bool + ExtractBitfield (uint32_t bit_size, + uint32_t bit_offset); + + size_t + GetByteSize() const; + + static size_t + GetMaxByteSize() + { + return sizeof(ValueData); + } + + bool + GetData (DataExtractor &data, size_t limit_byte_size = UINT32_MAX) const; + + size_t + GetAsMemoryData (void *dst, + size_t dst_len, + lldb::ByteOrder dst_byte_order, + Error &error) const; + + bool + IsZero() const; + + void + Clear() { m_type = e_void; m_data.ulonglong = 0; } + + const char * + GetTypeAsCString() const; + + void + GetValue (Stream *s, bool show_type) const; + + bool + IsValid() const + { + return (m_type >= e_sint) && (m_type <= e_long_double); + } + + bool + Promote(Scalar::Type type); + + bool + Cast (Scalar::Type type); + + bool + MakeSigned (); + + static const char * + GetValueTypeAsCString (Scalar::Type value_type); + + static Scalar::Type + GetValueTypeForSignedIntegerWithByteSize (size_t byte_size); + + static Scalar::Type + GetValueTypeForUnsignedIntegerWithByteSize (size_t byte_size); + + static Scalar::Type + GetValueTypeForFloatWithByteSize (size_t byte_size); + + //---------------------------------------------------------------------- + // All operators can benefits from the implicit conversions that will + // happen automagically by the compiler, so no temporary objects will + // need to be created. As a result, we currently don't need a variety of + // overloaded set value accessors. + //---------------------------------------------------------------------- + Scalar& operator= (const int i); + Scalar& operator= (unsigned int v); + Scalar& operator= (long v); + Scalar& operator= (unsigned long v); + Scalar& operator= (long long v); + Scalar& operator= (unsigned long long v); + Scalar& operator= (float v); + Scalar& operator= (double v); + Scalar& operator= (long double v); + Scalar& operator= (const Scalar& rhs); // Assignment operator + Scalar& operator+= (const Scalar& rhs); + Scalar& operator<<= (const Scalar& rhs); // Shift left + Scalar& operator>>= (const Scalar& rhs); // Shift right (arithmetic) + Scalar& operator&= (const Scalar& rhs); + + //---------------------------------------------------------------------- + // Shifts the current value to the right without maintaining the current + // sign of the value (if it is signed). + //---------------------------------------------------------------------- + bool + ShiftRightLogical(const Scalar& rhs); // Returns true on success + + //---------------------------------------------------------------------- + // Takes the absolute value of the current value if it is signed, else + // the value remains unchanged. + // Returns false if the contained value has a void type. + //---------------------------------------------------------------------- + bool + AbsoluteValue(); // Returns true on success + //---------------------------------------------------------------------- + // Negates the current value (even for unsigned values). + // Returns false if the contained value has a void type. + //---------------------------------------------------------------------- + bool + UnaryNegate(); // Returns true on success + //---------------------------------------------------------------------- + // Inverts all bits in the current value as long as it isn't void or + // a float/double/long double type. + // Returns false if the contained value has a void/float/double/long + // double type, else the value is inverted and true is returned. + //---------------------------------------------------------------------- + bool + OnesComplement(); // Returns true on success + + //---------------------------------------------------------------------- + // Access the type of the current value. + //---------------------------------------------------------------------- + Scalar::Type + GetType() const { return m_type; } + + //---------------------------------------------------------------------- + // Returns a casted value of the current contained data without + // modifying the current value. FAIL_VALUE will be returned if the type + // of the value is void or invalid. + //---------------------------------------------------------------------- + int + SInt(int fail_value = 0) const; + + // Return the raw unsigned integer without any casting or conversion + unsigned int + RawUInt () const; + + // Return the raw unsigned long without any casting or conversion + unsigned long + RawULong () const; + + // Return the raw unsigned long long without any casting or conversion + unsigned long long + RawULongLong () const; + + unsigned int + UInt(unsigned int fail_value = 0) const; + + long + SLong(long fail_value = 0) const; + + unsigned long + ULong(unsigned long fail_value = 0) const; + + long long + SLongLong(long long fail_value = 0) const; + + unsigned long long + ULongLong(unsigned long long fail_value = 0) const; + + float + Float(float fail_value = 0.0f) const; + + double + Double(double fail_value = 0.0) const; + + long double + LongDouble(long double fail_value = 0.0) const; + + uint64_t + GetRawBits64 (uint64_t fail_value) const; + + Error + SetValueFromCString (const char *s, lldb::Encoding encoding, size_t byte_size); + + Error + SetValueFromData (DataExtractor &data, lldb::Encoding encoding, size_t byte_size); + + static bool + UIntValueIsValidForSize (uint64_t uval64, size_t total_byte_size) + { + if (total_byte_size > 8) + return false; + + if (total_byte_size == 8) + return true; + + const uint64_t max = ((uint64_t)1 << (uint64_t)(total_byte_size * 8)) - 1; + return uval64 <= max; + } + + static bool + SIntValueIsValidForSize (int64_t sval64, size_t total_byte_size) + { + if (total_byte_size > 8) + return false; + + if (total_byte_size == 8) + return true; + + const int64_t max = ((int64_t)1 << (uint64_t)(total_byte_size * 8 - 1)) - 1; + const int64_t min = ~(max); + return min <= sval64 && sval64 <= max; + } + +protected: + typedef int sint_t; + typedef unsigned int uint_t; + typedef long slong_t; + typedef unsigned long ulong_t; + typedef long long slonglong_t; + typedef unsigned long long ulonglong_t; + typedef float float_t; + typedef double double_t; + typedef long double long_double_t; + + union ValueData + { + int sint; + unsigned int uint; + long slong; + unsigned long ulong; + long long slonglong; + unsigned long long ulonglong; + float flt; + double dbl; + long double ldbl; + }; + + //------------------------------------------------------------------ + // Classes that inherit from Scalar can see and modify these + //------------------------------------------------------------------ + Scalar::Type m_type; + ValueData m_data; + +private: + friend const Scalar operator+ (const Scalar& lhs, const Scalar& rhs); + friend const Scalar operator- (const Scalar& lhs, const Scalar& rhs); + friend const Scalar operator/ (const Scalar& lhs, const Scalar& rhs); + friend const Scalar operator* (const Scalar& lhs, const Scalar& rhs); + friend const Scalar operator& (const Scalar& lhs, const Scalar& rhs); + friend const Scalar operator| (const Scalar& lhs, const Scalar& rhs); + friend const Scalar operator% (const Scalar& lhs, const Scalar& rhs); + friend const Scalar operator^ (const Scalar& lhs, const Scalar& rhs); + friend const Scalar operator<< (const Scalar& lhs, const Scalar& rhs); + friend const Scalar operator>> (const Scalar& lhs, const Scalar& rhs); + friend bool operator== (const Scalar& lhs, const Scalar& rhs); + friend bool operator!= (const Scalar& lhs, const Scalar& rhs); + friend bool operator< (const Scalar& lhs, const Scalar& rhs); + friend bool operator<= (const Scalar& lhs, const Scalar& rhs); + friend bool operator> (const Scalar& lhs, const Scalar& rhs); + friend bool operator>= (const Scalar& lhs, const Scalar& rhs); + +}; + +//---------------------------------------------------------------------- +// Split out the operators into a format where the compiler will be able +// to implicitly convert numbers into Scalar objects. +// +// This allows code like: +// Scalar two(2); +// Scalar four = two * 2; +// Scalar eight = 2 * four; // This would cause an error if the +// // operator* was implemented as a +// // member function. +// SEE: +// Item 19 of "Effective C++ Second Edition" by Scott Meyers +// Differentiate among members functions, non-member functions, and +// friend functions +//---------------------------------------------------------------------- +const Scalar operator+ (const Scalar& lhs, const Scalar& rhs); +const Scalar operator- (const Scalar& lhs, const Scalar& rhs); +const Scalar operator/ (const Scalar& lhs, const Scalar& rhs); +const Scalar operator* (const Scalar& lhs, const Scalar& rhs); +const Scalar operator& (const Scalar& lhs, const Scalar& rhs); +const Scalar operator| (const Scalar& lhs, const Scalar& rhs); +const Scalar operator% (const Scalar& lhs, const Scalar& rhs); +const Scalar operator^ (const Scalar& lhs, const Scalar& rhs); +const Scalar operator<< (const Scalar& lhs, const Scalar& rhs); +const Scalar operator>> (const Scalar& lhs, const Scalar& rhs); +bool operator== (const Scalar& lhs, const Scalar& rhs); +bool operator!= (const Scalar& lhs, const Scalar& rhs); +bool operator< (const Scalar& lhs, const Scalar& rhs); +bool operator<= (const Scalar& lhs, const Scalar& rhs); +bool operator> (const Scalar& lhs, const Scalar& rhs); +bool operator>= (const Scalar& lhs, const Scalar& rhs); + +} // namespace lldb_private + +#endif // liblldb_Scalar_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Core/SearchFilter.h b/contrib/llvm/tools/lldb/include/lldb/Core/SearchFilter.h new file mode 100644 index 00000000000..57f1b9c1a0a --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Core/SearchFilter.h @@ -0,0 +1,447 @@ +//===-- SearchFilter.h ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_SearchFilter_h_ +#define liblldb_SearchFilter_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private.h" +#include "lldb/Core/FileSpecList.h" + +namespace lldb_private { + +//---------------------------------------------------------------------- +/// @class Searcher SearchFilter.h "lldb/Core/SearchFilter.h" +/// @brief Class that is driven by the SearchFilter to search the +/// SymbolContext space of the target program. +//---------------------------------------------------------------------- + +//---------------------------------------------------------------------- +/// General Outline: +/// Provides the callback and search depth for the SearchFilter search. +//---------------------------------------------------------------------- + +class Searcher +{ +public: + typedef enum { + eCallbackReturnStop = 0, // Stop the iteration + eCallbackReturnContinue, // Continue the iteration + eCallbackReturnPop // Pop one level up and continue iterating + } CallbackReturn; + + typedef enum { + eDepthTarget, + eDepthModule, + eDepthCompUnit, + eDepthFunction, + eDepthBlock, + eDepthAddress + } Depth; + + Searcher (); + + virtual ~Searcher (); + + virtual CallbackReturn + SearchCallback (SearchFilter &filter, + SymbolContext &context, + Address *addr, + bool complete) = 0; + + virtual Depth + GetDepth () = 0; + + //------------------------------------------------------------------ + /// Prints a canonical description for the searcher to the stream \a s. + /// + /// @param[in] s + /// Stream to which the output is copied. + //------------------------------------------------------------------ + virtual void + GetDescription(Stream *s); +}; + +//---------------------------------------------------------------------- +/// @class SearchFilter SearchFilter.h "lldb/Core/SearchFilter.h" +/// @brief Class descends through the SymbolContext space of the target, +/// applying a filter at each stage till it reaches the depth specified by +/// the GetDepth method of the searcher, and calls its callback at that point. +//---------------------------------------------------------------------- + +//---------------------------------------------------------------------- +/// General Outline: +/// Provides the callback and search depth for the SearchFilter search. +/// +/// The search is done by cooperation between the search filter and the searcher. +/// The search filter does the heavy work of recursing through the SymbolContext +/// space of the target program's symbol space. The Searcher specifies the depth +/// at which it wants its callback to be invoked. Note that since the resolution +/// of the Searcher may be greater than that of the SearchFilter, before the +/// Searcher qualifies an address it should pass it to "AddressPasses." +/// The default implementation is "Everything Passes." +//---------------------------------------------------------------------- + +class SearchFilter +{ +public: + + //------------------------------------------------------------------ + /// The basic constructor takes a Target, which gives the space to search. + /// + /// @param[in] target + /// The Target that provides the module list to search. + //------------------------------------------------------------------ + SearchFilter (const lldb::TargetSP &target_sp); + + SearchFilter (const SearchFilter& rhs); + + virtual + ~SearchFilter (); + + const SearchFilter& + operator=(const SearchFilter& rhs); + + //------------------------------------------------------------------ + /// Call this method with a file spec to see if that spec passes the filter. + /// + /// @param[in] spec + /// The file spec to check against the filter. + /// @return + /// \b true if \a spec passes, and \b false otherwise. + //------------------------------------------------------------------ + virtual bool + ModulePasses (const FileSpec &spec); + + //------------------------------------------------------------------ + /// Call this method with a Module to see if that module passes the filter. + /// + /// @param[in] module + /// The Module to check against the filter. + /// + /// @return + /// \b true if \a module passes, and \b false otherwise. + //------------------------------------------------------------------ + virtual bool + ModulePasses (const lldb::ModuleSP &module_sp); + + //------------------------------------------------------------------ + /// Call this method with a Address to see if \a address passes the filter. + /// + /// @param[in] addr + /// The address to check against the filter. + /// + /// @return + /// \b true if \a address passes, and \b false otherwise. + //------------------------------------------------------------------ + virtual bool + AddressPasses (Address &addr); + + //------------------------------------------------------------------ + /// Call this method with a FileSpec to see if \a file spec passes the filter + /// as the name of a compilation unit. + /// + /// @param[in] fileSpec + /// The file spec to check against the filter. + /// + /// @return + /// \b true if \a file spec passes, and \b false otherwise. + //------------------------------------------------------------------ + virtual bool + CompUnitPasses (FileSpec &fileSpec); + + //------------------------------------------------------------------ + /// Call this method with a CompileUnit to see if \a comp unit passes the filter. + /// + /// @param[in] compUnit + /// The CompileUnit to check against the filter. + /// + /// @return + /// \b true if \a Comp Unit passes, and \b false otherwise. + //------------------------------------------------------------------ + virtual bool + CompUnitPasses (CompileUnit &compUnit); + + //------------------------------------------------------------------ + /// Call this method to do the search using the Searcher. + /// + /// @param[in] searcher + /// The searcher to drive with this search. + /// + //------------------------------------------------------------------ + virtual void + Search (Searcher &searcher); + + //------------------------------------------------------------------ + /// Call this method to do the search using the Searcher in the module list + /// \a modules. + /// + /// @param[in] searcher + /// The searcher to drive with this search. + /// + /// @param[in] modules + /// The module list within which to restrict the search. + /// + //------------------------------------------------------------------ + virtual void + SearchInModuleList (Searcher &searcher, ModuleList &modules); + + //------------------------------------------------------------------ + /// This determines which items are REQUIRED for the filter to pass. + /// For instance, if you are filtering by Compilation Unit, obviously + /// symbols that have no compilation unit can't pass So return eSymbolContextCU + /// and search callbacks can then short cut the search to avoid looking at + /// things that obviously won't pass. + /// + /// @return + /// The required elements for the search, which is an or'ed together + /// set of lldb:SearchContextItem enum's. + /// + //------------------------------------------------------------------ + virtual uint32_t + GetFilterRequiredItems (); + + //------------------------------------------------------------------ + /// Prints a canonical description for the search filter to the stream \a s. + /// + /// @param[in] s + /// Stream to which the output is copied. + //------------------------------------------------------------------ + virtual void + GetDescription(Stream *s); + + //------------------------------------------------------------------ + /// Standard "Dump" method. At present it does nothing. + //------------------------------------------------------------------ + virtual void + Dump (Stream *s) const; + +protected: + + // These are utility functions to assist with the search iteration. They are used by the + // default Search method. + + Searcher::CallbackReturn + DoModuleIteration (const SymbolContext &context, + Searcher &searcher); + + Searcher::CallbackReturn + DoModuleIteration (const lldb::ModuleSP& module_sp, + Searcher &searcher); + + Searcher::CallbackReturn + DoCUIteration (const lldb::ModuleSP& module_sp, + const SymbolContext &context, + Searcher &searcher); + + Searcher::CallbackReturn + DoFunctionIteration (Function *function, + const SymbolContext &context, + Searcher &searcher); + + lldb::TargetSP m_target_sp; // Every filter has to be associated with a target for + // now since you need a starting place for the search. +}; + +//---------------------------------------------------------------------- +/// @class SearchFilterForNonModuleSpecificSearches SearchFilter.h "lldb/Core/SearchFilter.h" +/// @brief This is a SearchFilter that searches through all modules. It also consults the Target::ModuleIsExcludedForNonModuleSpecificSearches. +//---------------------------------------------------------------------- +class SearchFilterForNonModuleSpecificSearches : + public SearchFilter +{ +public: + SearchFilterForNonModuleSpecificSearches (const lldb::TargetSP &targetSP) : SearchFilter(targetSP) {} + ~SearchFilterForNonModuleSpecificSearches () {} + + virtual bool + ModulePasses (const FileSpec &module_spec); + + virtual bool + ModulePasses (const lldb::ModuleSP &module_sp); +}; + +//---------------------------------------------------------------------- +/// @class SearchFilterByModule SearchFilter.h "lldb/Core/SearchFilter.h" +/// @brief This is a SearchFilter that restricts the search to a given module. +//---------------------------------------------------------------------- + +class SearchFilterByModule : + public SearchFilter +{ +public: + + //------------------------------------------------------------------ + /// The basic constructor takes a Target, which gives the space to search, + /// and the module to restrict the search to. + /// + /// @param[in] target + /// The Target that provides the module list to search. + /// + /// @param[in] module + /// The Module that limits the search. + //------------------------------------------------------------------ + SearchFilterByModule (const lldb::TargetSP &targetSP, + const FileSpec &module); + + SearchFilterByModule (const SearchFilterByModule& rhs); + + virtual + ~SearchFilterByModule (); + + const SearchFilterByModule& + operator=(const SearchFilterByModule& rhs); + + virtual bool + ModulePasses (const lldb::ModuleSP &module_sp); + + virtual bool + ModulePasses (const FileSpec &spec); + + virtual bool + AddressPasses (Address &address); + + virtual bool + CompUnitPasses (FileSpec &fileSpec); + + virtual bool + CompUnitPasses (CompileUnit &compUnit); + + virtual void + GetDescription(Stream *s); + + virtual uint32_t + GetFilterRequiredItems (); + + virtual void + Dump (Stream *s) const; + + virtual void + Search (Searcher &searcher); + +private: + FileSpec m_module_spec; +}; + +class SearchFilterByModuleList : + public SearchFilter +{ +public: + + //------------------------------------------------------------------ + /// The basic constructor takes a Target, which gives the space to search, + /// and the module list to restrict the search to. + /// + /// @param[in] target + /// The Target that provides the module list to search. + /// + /// @param[in] module + /// The Module that limits the search. + //------------------------------------------------------------------ + SearchFilterByModuleList (const lldb::TargetSP &targetSP, + const FileSpecList &module_list); + + SearchFilterByModuleList (const SearchFilterByModuleList& rhs); + + virtual + ~SearchFilterByModuleList (); + + const SearchFilterByModuleList& + operator=(const SearchFilterByModuleList& rhs); + + virtual bool + ModulePasses (const lldb::ModuleSP &module_sp); + + virtual bool + ModulePasses (const FileSpec &spec); + + virtual bool + AddressPasses (Address &address); + + virtual bool + CompUnitPasses (FileSpec &fileSpec); + + virtual bool + CompUnitPasses (CompileUnit &compUnit); + + virtual void + GetDescription(Stream *s); + + virtual uint32_t + GetFilterRequiredItems (); + + virtual void + Dump (Stream *s) const; + + virtual void + Search (Searcher &searcher); + +private: + FileSpecList m_module_spec_list; +}; + +class SearchFilterByModuleListAndCU : + public SearchFilterByModuleList +{ +public: + + //------------------------------------------------------------------ + /// The basic constructor takes a Target, which gives the space to search, + /// and the module list to restrict the search to. + /// + /// @param[in] target + /// The Target that provides the module list to search. + /// + /// @param[in] module + /// The Module that limits the search. + //------------------------------------------------------------------ + SearchFilterByModuleListAndCU (const lldb::TargetSP &targetSP, + const FileSpecList &module_list, + const FileSpecList &cu_list); + + SearchFilterByModuleListAndCU (const SearchFilterByModuleListAndCU& rhs); + + virtual + ~SearchFilterByModuleListAndCU (); + + const SearchFilterByModuleListAndCU& + operator=(const SearchFilterByModuleListAndCU& rhs); + + virtual bool + AddressPasses (Address &address); + + virtual bool + CompUnitPasses (FileSpec &fileSpec); + + virtual bool + CompUnitPasses (CompileUnit &compUnit); + + virtual void + GetDescription(Stream *s); + + virtual uint32_t + GetFilterRequiredItems (); + + virtual void + Dump (Stream *s) const; + + virtual void + Search (Searcher &searcher); + +private: + FileSpecList m_module_spec_list; + FileSpecList m_cu_spec_list; +}; + +} // namespace lldb_private + +#endif // liblldb_SearchFilter_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Core/Section.h b/contrib/llvm/tools/lldb/include/lldb/Core/Section.h new file mode 100644 index 00000000000..437eaf59b9c --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Core/Section.h @@ -0,0 +1,313 @@ +//===-- Section.h -----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_Section_h_ +#define liblldb_Section_h_ + +#include "lldb/lldb-private.h" +#include "lldb/Core/AddressRange.h" +#include "lldb/Core/Flags.h" +#include "lldb/Core/ModuleChild.h" +#include "lldb/Core/ConstString.h" +#include "lldb/Core/RangeMap.h" +#include "lldb/Core/UserID.h" +#include "lldb/Core/VMRange.h" +#include "lldb/Symbol/ObjectFile.h" +#include + +namespace lldb_private { + +class SectionList +{ +public: + typedef std::vector collection; + typedef collection::iterator iterator; + typedef collection::const_iterator const_iterator; + + SectionList(); + + ~SectionList(); + + SectionList & + operator =(const SectionList& rhs); + + size_t + AddSection (const lldb::SectionSP& section_sp); + + size_t + AddUniqueSection (const lldb::SectionSP& section_sp); + + size_t + FindSectionIndex (const Section* sect); + + bool + ContainsSection(lldb::user_id_t sect_id) const; + + void + Dump (Stream *s, Target *target, bool show_header, uint32_t depth) const; + + lldb::SectionSP + FindSectionByName (const ConstString §ion_dstr) const; + + lldb::SectionSP + FindSectionByID (lldb::user_id_t sect_id) const; + + lldb::SectionSP + FindSectionByType (lldb::SectionType sect_type, bool check_children, size_t start_idx = 0) const; + + lldb::SectionSP + FindSectionContainingFileAddress (lldb::addr_t addr, uint32_t depth = UINT32_MAX) const; + + bool + GetSectionData (const DataExtractor& module_data, DataExtractor& section_data) const; + + // Get the number of sections in this list only + size_t + GetSize () const + { + return m_sections.size(); + } + + // Get the number of sections in this list, and any contained child sections + size_t + GetNumSections (uint32_t depth) const; + + bool + ReplaceSection (lldb::user_id_t sect_id, const lldb::SectionSP& section_sp, uint32_t depth = UINT32_MAX); + + // Warning, this can be slow as it's removing items from a std::vector. + bool + DeleteSection (size_t idx); + + lldb::SectionSP + GetSectionAtIndex (size_t idx) const; + + size_t + Slide (lldb::addr_t slide_amount, bool slide_children); + + void + Clear () + { + m_sections.clear(); + } + +protected: + collection m_sections; +}; + + +class Section : + public std::enable_shared_from_this
, + public ModuleChild, + public UserID, + public Flags +{ +public: + // Create a root section (one that has no parent) + Section (const lldb::ModuleSP &module_sp, + ObjectFile *obj_file, + lldb::user_id_t sect_id, + const ConstString &name, + lldb::SectionType sect_type, + lldb::addr_t file_vm_addr, + lldb::addr_t vm_size, + lldb::offset_t file_offset, + lldb::offset_t file_size, + uint32_t flags); + + // Create a section that is a child of parent_section_sp + Section (const lldb::SectionSP &parent_section_sp, // NULL for top level sections, non-NULL for child sections + const lldb::ModuleSP &module_sp, + ObjectFile *obj_file, + lldb::user_id_t sect_id, + const ConstString &name, + lldb::SectionType sect_type, + lldb::addr_t file_vm_addr, + lldb::addr_t vm_size, + lldb::offset_t file_offset, + lldb::offset_t file_size, + uint32_t flags); + + ~Section (); + + static int + Compare (const Section& a, const Section& b); + + bool + ContainsFileAddress (lldb::addr_t vm_addr) const; + + SectionList& + GetChildren () + { + return m_children; + } + + const SectionList& + GetChildren () const + { + return m_children; + } + + void + Dump (Stream *s, Target *target, uint32_t depth) const; + + void + DumpName (Stream *s) const; + + lldb::addr_t + GetLoadBaseAddress (Target *target) const; + + bool + ResolveContainedAddress (lldb::addr_t offset, Address &so_addr) const; + + lldb::offset_t + GetFileOffset () const + { + return m_file_offset; + } + + void + SetFileOffset (lldb::offset_t file_offset) + { + m_file_offset = file_offset; + } + + lldb::offset_t + GetFileSize () const + { + return m_file_size; + } + + void + SetFileSize (lldb::offset_t file_size) + { + m_file_size = file_size; + } + + lldb::addr_t + GetFileAddress () const; + + bool + SetFileAddress (lldb::addr_t file_addr); + + lldb::addr_t + GetOffset () const; + + + lldb::addr_t + GetByteSize () const + { + return m_byte_size; + } + + void + SetByteSize (lldb::addr_t byte_size) + { + m_byte_size = byte_size; + } + + bool + IsFake() const + { + return m_fake; + } + + void + SetIsFake(bool fake) + { + m_fake = fake; + } + + bool + IsEncrypted () const + { + return m_encrypted; + } + + void + SetIsEncrypted (bool b) + { + m_encrypted = b; + } + + bool + IsDescendant (const Section *section); + + const ConstString& + GetName () const + { + return m_name; + } + + bool + Slide (lldb::addr_t slide_amount, bool slide_children); + + + lldb::SectionType + GetType () const + { + return m_type; + } + + lldb::SectionSP + GetParent () const + { + return m_parent_wp.lock(); + } + + bool + IsThreadSpecific () const + { + return m_thread_specific; + } + + void + SetIsThreadSpecific (bool b) + { + m_thread_specific = b; + } + + ObjectFile * + GetObjectFile () + { + return m_obj_file; + } + const ObjectFile * + GetObjectFile () const + { + return m_obj_file; + } + + +protected: + + ObjectFile *m_obj_file; // The object file that data for this section should be read from + lldb::SectionType m_type; // The type of this section + lldb::SectionWP m_parent_wp; // Weak pointer to parent section + ConstString m_name; // Name of this section + lldb::addr_t m_file_addr; // The absolute file virtual address range of this section if m_parent == NULL, + // offset from parent file virtual address if m_parent != NULL + lldb::addr_t m_byte_size; // Size in bytes that this section will occupy in memory at runtime + lldb::offset_t m_file_offset; // Object file offset (if any) + lldb::offset_t m_file_size; // Object file size (can be smaller than m_byte_size for zero filled sections...) + SectionList m_children; // Child sections + bool m_fake:1, // If true, then this section only can contain the address if one of its + // children contains an address. This allows for gaps between the children + // that are contained in the address range for this section, but do not produce + // hits unless the children contain the address. + m_encrypted:1, // Set to true if the contents are encrypted + m_thread_specific:1;// This section is thread specific +private: + DISALLOW_COPY_AND_ASSIGN (Section); +}; + + +} // namespace lldb_private + +#endif // liblldb_Section_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Core/SourceManager.h b/contrib/llvm/tools/lldb/include/lldb/Core/SourceManager.h new file mode 100644 index 00000000000..b850df774ba --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Core/SourceManager.h @@ -0,0 +1,196 @@ +//===-- SourceManager.h -----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_SourceManager_h_ +#define liblldb_SourceManager_h_ + +// C Includes +// C++ Includes +#include +#include + +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private.h" +#include "lldb/Host/FileSpec.h" + +namespace lldb_private { + +class SourceManager +{ +public: +#ifndef SWIG + + class File + { + friend bool operator== (const SourceManager::File &lhs, const SourceManager::File &rhs); + public: + + File (const FileSpec &file_spec, Target *target); + ~File(); + + size_t + DisplaySourceLines (uint32_t line, + uint32_t context_before, + uint32_t context_after, + Stream *s); + void + FindLinesMatchingRegex (RegularExpression& regex, + uint32_t start_line, + uint32_t end_line, + std::vector &match_lines); + + bool + GetLine (uint32_t line_no, std::string &buffer); + + uint32_t + GetLineOffset (uint32_t line); + + bool + LineIsValid (uint32_t line); + + bool + FileSpecMatches (const FileSpec &file_spec); + + const FileSpec & + GetFileSpec () + { + return m_file_spec; + } + + uint32_t + GetSourceMapModificationID() const + { + return m_source_map_mod_id; + } + + protected: + + bool + CalculateLineOffsets (uint32_t line = UINT32_MAX); + + FileSpec m_file_spec_orig; // The original file spec that was used (can be different from m_file_spec) + FileSpec m_file_spec; // The actualy file spec being used (if the target has source mappings, this might be different from m_file_spec_orig) + TimeValue m_mod_time; // Keep the modification time that this file data is valid for + uint32_t m_source_map_mod_id; // If the target uses path remappings, be sure to clear our notion of a source file if the path modification ID changes + lldb::DataBufferSP m_data_sp; + typedef std::vector LineOffsets; + LineOffsets m_offsets; + }; + +#endif // SWIG + + typedef std::shared_ptr FileSP; + +#ifndef SWIG + + // The SourceFileCache class separates the source manager from the cache of source files, so the + // cache can be stored in the Debugger, but the source managers can be per target. + class SourceFileCache + { + public: + SourceFileCache () {} + ~SourceFileCache() {} + + void AddSourceFile (const FileSP &file_sp); + FileSP FindSourceFile (const FileSpec &file_spec) const; + + protected: + typedef std::map FileCache; + FileCache m_file_cache; + }; +#endif + + + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + // A source manager can be made with a non-null target, in which case it can use the path remappings to find + // source files that are not in their build locations. With no target it won't be able to do this. + SourceManager (const lldb::DebuggerSP &debugger_sp); + SourceManager (const lldb::TargetSP &target_sp); + + ~SourceManager(); + + + FileSP + GetLastFile () + { + return m_last_file_sp; + } + + size_t + DisplaySourceLinesWithLineNumbers (const FileSpec &file, + uint32_t line, + uint32_t context_before, + uint32_t context_after, + const char* current_line_cstr, + Stream *s, + const SymbolContextList *bp_locs = NULL); + + // This variant uses the last file we visited. + size_t + DisplaySourceLinesWithLineNumbersUsingLastFile (uint32_t start_line, + uint32_t count, + uint32_t curr_line, + const char* current_line_cstr, + Stream *s, + const SymbolContextList *bp_locs = NULL); + + size_t + DisplayMoreWithLineNumbers (Stream *s, + uint32_t count, + bool reverse, + const SymbolContextList *bp_locs = NULL); + + bool + SetDefaultFileAndLine (const FileSpec &file_spec, uint32_t line); + + bool + GetDefaultFileAndLine (FileSpec &file_spec, uint32_t &line); + + bool + DefaultFileAndLineSet () + { + return (m_last_file_sp.get() != NULL); + } + + void + FindLinesMatchingRegex (FileSpec &file_spec, + RegularExpression& regex, + uint32_t start_line, + uint32_t end_line, + std::vector &match_lines); + +protected: + + FileSP + GetFile (const FileSpec &file_spec); + + //------------------------------------------------------------------ + // Classes that inherit from SourceManager can see and modify these + //------------------------------------------------------------------ + FileSP m_last_file_sp; + uint32_t m_last_line; + uint32_t m_last_count; + bool m_default_set; + lldb::TargetWP m_target_wp; + lldb::DebuggerWP m_debugger_wp; + +private: + //------------------------------------------------------------------ + // For SourceManager only + //------------------------------------------------------------------ + DISALLOW_COPY_AND_ASSIGN (SourceManager); +}; + +bool operator== (const SourceManager::File &lhs, const SourceManager::File &rhs); +} // namespace lldb_private + +#endif // liblldb_SourceManager_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Core/State.h b/contrib/llvm/tools/lldb/include/lldb/Core/State.h new file mode 100644 index 00000000000..8057b3e0584 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Core/State.h @@ -0,0 +1,78 @@ +//===-- State.h -------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_State_h_ +#define liblldb_State_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private.h" + +namespace lldb_private { + +//------------------------------------------------------------------ +/// Converts a StateType to a C string. +/// +/// @param[in] state +/// The StateType object to convert. +/// +/// @return +/// A NULL terminated C string that describes \a state. The +/// returned string comes from constant string buffers and does +/// not need to be freed. +//------------------------------------------------------------------ +const char * +StateAsCString (lldb::StateType state); + +//------------------------------------------------------------------ +/// Check if a state represents a state where the process or thread +/// is running. +/// +/// @param[in] state +/// The StateType enumeration value +/// +/// @return +/// \b true if the state represents a process or thread state +/// where the process or thread is running, \b false otherwise. +//------------------------------------------------------------------ +bool +StateIsRunningState (lldb::StateType state); + +//------------------------------------------------------------------ +/// Check if a state represents a state where the process or thread +/// is stopped. Stopped can mean stopped when the process is still +/// around, or stopped when the process has exited or doesn't exist +/// yet. The \a must_exist argument tells us which of these cases is +/// desired. +/// +/// @param[in] state +/// The StateType enumeration value +/// +/// @param[in] must_exist +/// A boolean that indicates the thread must also be alive +/// so states like unloaded or exited won't return true. +/// +/// @return +/// \b true if the state represents a process or thread state +/// where the process or thread is stopped. If \a must_exist is +/// \b true, then the process can't be exited or unloaded, +/// otherwise exited and unloaded or other states where the +/// process no longer exists are considered to be stopped. +//------------------------------------------------------------------ +bool +StateIsStoppedState (lldb::StateType state, bool must_exist); + +const char * +GetPermissionsAsCString (uint32_t permissions); + +} // namespace lldb_private + +#endif // liblldb_State_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Core/Stream.h b/contrib/llvm/tools/lldb/include/lldb/Core/Stream.h new file mode 100644 index 00000000000..0fd4aac041a --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Core/Stream.h @@ -0,0 +1,612 @@ +//===-- Stream.h ------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_Stream_h_ +#define liblldb_Stream_h_ +#if defined(__cplusplus) + +#include "lldb/lldb-private.h" +#include "lldb/Core/Flags.h" +#include + +namespace lldb_private { + +//---------------------------------------------------------------------- +/// @class Stream Stream.h "lldb/Core/Stream.h" +/// @brief A stream class that can stream formatted output to a file. +//---------------------------------------------------------------------- +class Stream +{ +public: + //------------------------------------------------------------------ + /// \a m_flags bit values. + //------------------------------------------------------------------ + enum + { + eVerbose = (1 << 0), ///< If set, verbose logging is enabled + eDebug = (1 << 1), ///< If set, debug logging is enabled + eAddPrefix = (1 << 2), ///< Add number prefixes for binary, octal and hex when eBinary is clear + eBinary = (1 << 3) ///< Get and put data as binary instead of as the default string mode. + }; + + //------------------------------------------------------------------ + /// Construct with flags and address size and byte order. + /// + /// Construct with dump flags \a flags and the default address + /// size. \a flags can be any of the above enumeration logical OR'ed + /// together. + //------------------------------------------------------------------ + Stream (uint32_t flags, + uint32_t addr_size, + lldb::ByteOrder byte_order); + + //------------------------------------------------------------------ + /// Construct a default Stream, not binary, host byte order and + /// host addr size. + /// + //------------------------------------------------------------------ + Stream (); + + //------------------------------------------------------------------ + /// Destructor + //------------------------------------------------------------------ + virtual + ~Stream (); + + //------------------------------------------------------------------ + // Subclasses must override these methods + //------------------------------------------------------------------ + + //------------------------------------------------------------------ + /// Flush the stream. + /// + /// Subclasses should flush the stream to make any output appear + /// if the stream has any buffering. + //------------------------------------------------------------------ + virtual void + Flush () = 0; + + //------------------------------------------------------------------ + /// Output character bytes to the stream. + /// + /// Appends \a src_len characters from the buffer \a src to the + /// stream. + /// + /// @param[in] src + /// A buffer containing at least \a src_len bytes of data. + /// + /// @param[in] src_len + /// A number of bytes to append to the stream. + /// + /// @return + /// The number of bytes that were appended to the stream. + //------------------------------------------------------------------ + virtual size_t + Write (const void *src, size_t src_len) = 0; + + //------------------------------------------------------------------ + // Member functions + //------------------------------------------------------------------ + size_t + PutChar (char ch); + + //------------------------------------------------------------------ + /// Set the byte_order value. + /// + /// Sets the byte order of the data to extract. Extracted values + /// will be swapped if necessary when decoding. + /// + /// @param[in] byte_order + /// The byte order value to use when extracting data. + /// + /// @return + /// The old byte order value. + //------------------------------------------------------------------ + lldb::ByteOrder + SetByteOrder (lldb::ByteOrder byte_order); + + //------------------------------------------------------------------ + /// Format a C string from a printf style format and variable + /// arguments and encode and append the resulting C string as hex + /// bytes. + /// + /// @param[in] format + /// A printf style format string. + /// + /// @param[in] ... + /// Any additional arguments needed for the printf format string. + /// + /// @return + /// The number of bytes that were appended to the stream. + //------------------------------------------------------------------ + size_t + PrintfAsRawHex8 (const char *format, ...) __attribute__ ((format (printf, 2, 3))); + + //------------------------------------------------------------------ + /// Format a C string from a printf style format and variable + /// arguments and encode and append the resulting C string as hex + /// bytes. + /// + /// @param[in] format + /// A printf style format string. + /// + /// @param[in] ... + /// Any additional arguments needed for the printf format string. + /// + /// @return + /// The number of bytes that were appended to the stream. + //------------------------------------------------------------------ + size_t + PutHex8 (uint8_t uvalue); + + size_t + PutNHex8 (size_t n, uint8_t uvalue); + + size_t + PutHex16 (uint16_t uvalue, + lldb::ByteOrder byte_order = lldb::eByteOrderInvalid); + + size_t + PutHex32 (uint32_t uvalue, + lldb::ByteOrder byte_order = lldb::eByteOrderInvalid); + + size_t + PutHex64 (uint64_t uvalue, + lldb::ByteOrder byte_order = lldb::eByteOrderInvalid); + + size_t + PutMaxHex64 (uint64_t uvalue, + size_t byte_size, + lldb::ByteOrder byte_order = lldb::eByteOrderInvalid); + size_t + PutFloat (float f, + lldb::ByteOrder byte_order = lldb::eByteOrderInvalid); + + size_t + PutDouble (double d, + lldb::ByteOrder byte_order = lldb::eByteOrderInvalid); + + size_t + PutLongDouble (long double ld, + lldb::ByteOrder byte_order = lldb::eByteOrderInvalid); + + size_t + PutPointer (void *ptr); + + // Append \a src_len bytes from \a src to the stream as hex characters + // (two ascii characters per byte of input data) + size_t + PutBytesAsRawHex8 (const void *src, + size_t src_len, + lldb::ByteOrder src_byte_order = lldb::eByteOrderInvalid, + lldb::ByteOrder dst_byte_order = lldb::eByteOrderInvalid); + + // Append \a src_len bytes from \a s to the stream as binary data. + size_t + PutRawBytes (const void *s, + size_t src_len, + lldb::ByteOrder src_byte_order = lldb::eByteOrderInvalid, + lldb::ByteOrder dst_byte_order = lldb::eByteOrderInvalid); + + size_t + PutCStringAsRawHex8 (const char *s); + + //------------------------------------------------------------------ + /// Output a NULL terminated C string \a cstr to the stream \a s. + /// + /// @param[in] cstr + /// A NULL terminated C string. + /// + /// @return + /// A reference to this class so multiple things can be streamed + /// in one statement. + //------------------------------------------------------------------ + Stream& + operator<< (const char *cstr); + + //------------------------------------------------------------------ + /// Output a pointer value \a p to the stream \a s. + /// + /// @param[in] p + /// A void pointer. + /// + /// @return + /// A reference to this class so multiple things can be streamed + /// in one statement. + //------------------------------------------------------------------ + Stream& + operator<< (void *p); + + //------------------------------------------------------------------ + /// Output a character \a ch to the stream \a s. + /// + /// @param[in] ch + /// A printable character value. + /// + /// @return + /// A reference to this class so multiple things can be streamed + /// in one statement. + //------------------------------------------------------------------ + Stream& + operator<< (char ch); + + //------------------------------------------------------------------ + /// Output a uint8_t \a uval to the stream \a s. + /// + /// @param[in] uval + /// A uint8_t value. + /// + /// @return + /// A reference to this class so multiple things can be streamed + /// in one statement. + //------------------------------------------------------------------ + Stream& + operator<< (uint8_t uval); + + //------------------------------------------------------------------ + /// Output a uint16_t \a uval to the stream \a s. + /// + /// @param[in] uval + /// A uint16_t value. + /// + /// @return + /// A reference to this class so multiple things can be streamed + /// in one statement. + //------------------------------------------------------------------ + Stream& + operator<< (uint16_t uval); + + //------------------------------------------------------------------ + /// Output a uint32_t \a uval to the stream \a s. + /// + /// @param[in] uval + /// A uint32_t value. + /// + /// @return + /// A reference to this class so multiple things can be streamed + /// in one statement. + //------------------------------------------------------------------ + Stream& + operator<< (uint32_t uval); + + //------------------------------------------------------------------ + /// Output a uint64_t \a uval to the stream \a s. + /// + /// @param[in] uval + /// A uint64_t value. + /// + /// @return + /// A reference to this class so multiple things can be streamed + /// in one statement. + //------------------------------------------------------------------ + Stream& + operator<< (uint64_t uval); + + //------------------------------------------------------------------ + /// Output a int8_t \a sval to the stream \a s. + /// + /// @param[in] sval + /// A int8_t value. + /// + /// @return + /// A reference to this class so multiple things can be streamed + /// in one statement. + //------------------------------------------------------------------ + Stream& + operator<< (int8_t sval); + + //------------------------------------------------------------------ + /// Output a int16_t \a sval to the stream \a s. + /// + /// @param[in] sval + /// A int16_t value. + /// + /// @return + /// A reference to this class so multiple things can be streamed + /// in one statement. + //------------------------------------------------------------------ + Stream& + operator<< (int16_t sval); + + //------------------------------------------------------------------ + /// Output a int32_t \a sval to the stream \a s. + /// + /// @param[in] sval + /// A int32_t value. + /// + /// @return + /// A reference to this class so multiple things can be streamed + /// in one statement. + //------------------------------------------------------------------ + Stream& + operator<< (int32_t sval); + + //------------------------------------------------------------------ + /// Output a int64_t \a sval to the stream \a s. + /// + /// @param[in] sval + /// A int64_t value. + /// + /// @return + /// A reference to this class so multiple things can be streamed + /// in one statement. + //------------------------------------------------------------------ + Stream& + operator<< (int64_t sval); + + //------------------------------------------------------------------ + /// Output an address value to this stream. + /// + /// Put an address \a addr out to the stream with optional \a prefix + /// and \a suffix strings. + /// + /// @param[in] addr + /// An address value. + /// + /// @param[in] addr_size + /// Size in bytes of the address, used for formatting. + /// + /// @param[in] prefix + /// A prefix C string. If NULL, no prefix will be output. + /// + /// @param[in] suffix + /// A suffix C string. If NULL, no suffix will be output. + //------------------------------------------------------------------ + void + Address (uint64_t addr, uint32_t addr_size, const char *prefix = NULL, const char *suffix = NULL); + + //------------------------------------------------------------------ + /// Output an address range to this stream. + /// + /// Put an address range \a lo_addr - \a hi_addr out to the stream + /// with optional \a prefix and \a suffix strings. + /// + /// @param[in] lo_addr + /// The start address of the address range. + /// + /// @param[in] hi_addr + /// The end address of the address range. + /// + /// @param[in] addr_size + /// Size in bytes of the address, used for formatting. + /// + /// @param[in] prefix + /// A prefix C string. If NULL, no prefix will be output. + /// + /// @param[in] suffix + /// A suffix C string. If NULL, no suffix will be output. + //------------------------------------------------------------------ + void + AddressRange(uint64_t lo_addr, uint64_t hi_addr, uint32_t addr_size, const char *prefix = NULL, const char *suffix = NULL); + + //------------------------------------------------------------------ + /// Output a C string to the stream. + /// + /// Print a C string \a cstr to the stream. + /// + /// @param[in] cstr + /// The string to be output to the stream. + //------------------------------------------------------------------ + size_t + PutCString (const char *cstr); + + //------------------------------------------------------------------ + /// Output and End of Line character to the stream. + //------------------------------------------------------------------ + size_t + EOL(); + + //------------------------------------------------------------------ + /// Get the address size in bytes. + /// + /// @return + /// The size of an address in bytes that is used when outputting + /// address and pointer values to the stream. + //------------------------------------------------------------------ + uint32_t + GetAddressByteSize () const; + + //------------------------------------------------------------------ + /// Test if debug logging is enabled. + /// + /// @return + // \b true if the debug flag bit is set in this stream, \b + // false otherwise. + //------------------------------------------------------------------ + bool + GetDebug() const; + + //------------------------------------------------------------------ + /// The flags accessor. + /// + /// @return + /// A reference to the Flags member variable. + //------------------------------------------------------------------ + Flags& + GetFlags(); + + //------------------------------------------------------------------ + /// The flags const accessor. + /// + /// @return + /// A const reference to the Flags member variable. + //------------------------------------------------------------------ + const Flags& + GetFlags() const; + + //------------------------------------------------------------------ + //// The byte order accessor. + //// + //// @return + //// The byte order. + //------------------------------------------------------------------ + lldb::ByteOrder + GetByteOrder() const; + + //------------------------------------------------------------------ + /// Get the current indentation level. + /// + /// @return + /// The current indentation level as an integer. + //------------------------------------------------------------------ + int + GetIndentLevel () const; + + //------------------------------------------------------------------ + /// Test if verbose logging is enabled. + /// + /// @return + // \b true if the verbose flag bit is set in this stream, \b + // false otherwise. + //------------------------------------------------------------------ + bool + GetVerbose() const; + + //------------------------------------------------------------------ + /// Indent the current line in the stream. + /// + /// Indent the current line using the current indentation level and + /// print an optional string following the idenatation spaces. + /// + /// @param[in] s + /// A C string to print following the indentation. If NULL, just + /// output the indentation characters. + //------------------------------------------------------------------ + size_t + Indent(const char *s = NULL); + + //------------------------------------------------------------------ + /// Decrement the current indentation level. + //------------------------------------------------------------------ + void + IndentLess (int amount = 2); + + //------------------------------------------------------------------ + /// Increment the current indentation level. + //------------------------------------------------------------------ + void + IndentMore (int amount = 2); + + //------------------------------------------------------------------ + /// Output an offset value. + /// + /// Put an offset \a uval out to the stream using the printf format + /// in \a format. + /// + /// @param[in] offset + /// The offset value. + /// + /// @param[in] format + /// The printf style format to use when outputting the offset. + //------------------------------------------------------------------ + void + Offset (uint32_t offset, const char *format = "0x%8.8x: "); + + //------------------------------------------------------------------ + /// Output printf formatted output to the stream. + /// + /// Print some formatted output to the stream. + /// + /// @param[in] format + /// A printf style format string. + /// + /// @param[in] ... + /// Variable arguments that are needed for the printf style + /// format string \a format. + //------------------------------------------------------------------ + size_t + Printf (const char *format, ...) __attribute__ ((format (printf, 2, 3))); + + size_t + PrintfVarArg(const char *format, va_list args); + + //------------------------------------------------------------------ + /// Output a quoted C string value to the stream. + /// + /// Print a double quoted NULL terminated C string to the stream + /// using the printf format in \a format. + /// + /// @param[in] cstr + /// A NULL terminated C string value. + /// + /// @param[in] format + /// The optional C string format that can be overridden. + //------------------------------------------------------------------ + void + QuotedCString (const char *cstr, const char *format = "\"%s\""); + + //------------------------------------------------------------------ + /// Set the address size in bytes. + /// + /// @param[in] addr_size + /// The new size in bytes of an address to use when outputting + /// address and pointer values. + //------------------------------------------------------------------ + void + SetAddressByteSize (uint32_t addr_size); + + //------------------------------------------------------------------ + /// Set the current indentation level. + /// + /// @param[in] level + /// The new indentation level. + //------------------------------------------------------------------ + void + SetIndentLevel (int level); + + //------------------------------------------------------------------ + /// Output a SLEB128 number to the stream. + /// + /// Put an SLEB128 \a uval out to the stream using the printf format + /// in \a format. + /// + /// @param[in] uval + /// A uint64_t value that was extracted as a SLEB128 value. + /// + /// @param[in] format + /// The optional printf format that can be overridden. + //------------------------------------------------------------------ + size_t + PutSLEB128 (int64_t uval); + + //------------------------------------------------------------------ + /// Output a ULEB128 number to the stream. + /// + /// Put an ULEB128 \a uval out to the stream using the printf format + /// in \a format. + /// + /// @param[in] uval + /// A uint64_t value that was extracted as a ULEB128 value. + /// + /// @param[in] format + /// The optional printf format that can be overridden. + //------------------------------------------------------------------ + size_t + PutULEB128 (uint64_t uval); + + static void + UnitTest(Stream *s); + +protected: + //------------------------------------------------------------------ + // Member variables + //------------------------------------------------------------------ + Flags m_flags; ///< Dump flags. + uint32_t m_addr_size; ///< Size of an address in bytes. + lldb::ByteOrder m_byte_order; ///< Byte order to use when encoding scalar types. + int m_indent_level; ///< Indention level. + + size_t _PutHex8 (uint8_t uvalue, bool add_prefix); +}; + +} // namespace lldb_private + +#endif // #if defined(__cplusplus) +#endif // liblldb_Stream_h_ + diff --git a/contrib/llvm/tools/lldb/include/lldb/Core/StreamAsynchronousIO.h b/contrib/llvm/tools/lldb/include/lldb/Core/StreamAsynchronousIO.h new file mode 100644 index 00000000000..0e3e9ee9bcf --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Core/StreamAsynchronousIO.h @@ -0,0 +1,42 @@ +//===-- StreamAsynchronousIO.h -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_StreamAsynchronousIO_h_ +#define liblldb_StreamAsynchronousIO_h_ + +#include + +#include "lldb/Core/Stream.h" +#include "lldb/Core/StreamString.h" + +namespace lldb_private { + +class StreamAsynchronousIO : + public Stream +{ +public: + StreamAsynchronousIO (Broadcaster &broadcaster, uint32_t broadcast_event_type); + + virtual ~StreamAsynchronousIO (); + + virtual void + Flush (); + + virtual size_t + Write (const void *src, size_t src_len); + + +private: + Broadcaster &m_broadcaster; + uint32_t m_broadcast_event_type; + StreamString m_accumulated_data; +}; + +} // namespace lldb_private +#endif // #ifndef liblldb_StreamAsynchronousIO_h diff --git a/contrib/llvm/tools/lldb/include/lldb/Core/StreamBuffer.h b/contrib/llvm/tools/lldb/include/lldb/Core/StreamBuffer.h new file mode 100644 index 00000000000..9d25e842ecc --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Core/StreamBuffer.h @@ -0,0 +1,87 @@ +//===-- StreamBuffer.h ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_StreamBuffer_h_ +#define liblldb_StreamBuffer_h_ + +#include +#include +#include "llvm/ADT/SmallVector.h" +#include "lldb/Core/Stream.h" + +namespace lldb_private { + +template +class StreamBuffer : public Stream +{ +public: + StreamBuffer () : + Stream (0, 4, lldb::eByteOrderBig), + m_packet () + { + } + + + StreamBuffer (uint32_t flags, + uint32_t addr_size, + lldb::ByteOrder byte_order) : + Stream (flags, addr_size, byte_order), + m_packet () + { + } + + virtual + ~StreamBuffer () + { + } + + virtual void + Flush () + { + // Nothing to do when flushing a buffer based stream... + } + + virtual size_t + Write (const void *s, size_t length) + { + if (s && length) + m_packet.append ((const char *)s, ((const char *)s) + length); + return length; + } + + void + Clear() + { + m_packet.clear(); + } + + // Beware, this might not be NULL terminated as you can expect from + // StringString as there may be random bits in the llvm::SmallVector. If + // you are using this class to create a C string, be sure the call PutChar ('\0') + // after you have created your string, or use StreamString. + const char * + GetData () const + { + return m_packet.data(); + } + + size_t + GetSize() const + { + return m_packet.size(); + } + +protected: + llvm::SmallVector m_packet; + +}; + +} // namespace lldb_private + +#endif // #ifndef liblldb_StreamBuffer_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Core/StreamCallback.h b/contrib/llvm/tools/lldb/include/lldb/Core/StreamCallback.h new file mode 100644 index 00000000000..b5fb91c6ce0 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Core/StreamCallback.h @@ -0,0 +1,47 @@ +//===-- StreamCallback.h -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_StreamCallback_h_ +#define liblldb_StreamCallback_h_ + +#include + +#include "lldb/Core/Stream.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Host/Mutex.h" + +namespace lldb_private { + +class StreamCallback : + public Stream +{ +public: + StreamCallback (lldb::LogOutputCallback callback, void *baton); + + virtual ~StreamCallback (); + + virtual void + Flush (); + + virtual size_t + Write (const void *src, size_t src_len); + + +private: + typedef std::map collection; + lldb::LogOutputCallback m_callback; + void *m_baton; + collection m_accumulated_data; + Mutex m_collection_mutex; + + StreamString &FindStreamForThread(lldb::tid_t cur_tid); +}; + +} // namespace lldb_private +#endif // #ifndef liblldb_StreamCallback_h diff --git a/contrib/llvm/tools/lldb/include/lldb/Core/StreamFile.h b/contrib/llvm/tools/lldb/include/lldb/Core/StreamFile.h new file mode 100644 index 00000000000..d032c0b21e6 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Core/StreamFile.h @@ -0,0 +1,75 @@ +//===-- StreamFile.h --------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_StreamFile_h_ +#define liblldb_StreamFile_h_ + +// C Includes +// C++ Includes + +#include + +// Other libraries and framework includes +// Project includes + +#include "lldb/Core/Stream.h" +#include "lldb/Host/File.h" + +namespace lldb_private { + +class StreamFile : public Stream +{ +public: + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + StreamFile (); + + StreamFile (uint32_t flags, uint32_t addr_size, lldb::ByteOrder byte_order); + + StreamFile (int fd, bool transfer_ownership); + + StreamFile (const char *path); + + StreamFile (FILE *fh, bool transfer_ownership); + + virtual + ~StreamFile(); + + File & + GetFile () + { + return m_file; + } + + const File & + GetFile () const + { + return m_file; + } + + virtual void + Flush (); + + virtual size_t + Write (const void *s, size_t length); + +protected: + //------------------------------------------------------------------ + // Classes that inherit from StreamFile can see and modify these + //------------------------------------------------------------------ + File m_file; + +private: + DISALLOW_COPY_AND_ASSIGN (StreamFile); +}; + +} // namespace lldb_private + +#endif // liblldb_StreamFile_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Core/StreamString.h b/contrib/llvm/tools/lldb/include/lldb/Core/StreamString.h new file mode 100644 index 00000000000..a26ad2d16a0 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Core/StreamString.h @@ -0,0 +1,64 @@ +//===-- StreamString.h ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_StreamString_h_ +#define liblldb_StreamString_h_ + +#include + +#include "lldb/Core/Stream.h" + +namespace lldb_private { + +class StreamString : public Stream +{ +public: + StreamString (); + + StreamString (uint32_t flags, + uint32_t addr_size, + lldb::ByteOrder byte_order); + + virtual + ~StreamString (); + + virtual void + Flush (); + + virtual size_t + Write (const void *s, size_t length); + + void + Clear(); + + bool + Empty() const; + + const char * + GetData () const; + + size_t + GetSize() const; + + std::string & + GetString(); + + const std::string & + GetString() const; + + void + FillLastLineToColumn (uint32_t column, char fill_char); + +protected: + std::string m_packet; + +}; + +} // namespace lldb_private +#endif // #ifndef liblldb_StreamString_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Core/StreamTee.h b/contrib/llvm/tools/lldb/include/lldb/Core/StreamTee.h new file mode 100644 index 00000000000..e2a29a37455 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Core/StreamTee.h @@ -0,0 +1,175 @@ +//===-- StreamTee.h ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_StreamTee_h_ +#define liblldb_StreamTee_h_ + +#include + +#include "lldb/Core/Stream.h" +#include "lldb/Host/Mutex.h" + +namespace lldb_private { + +class StreamTee : public Stream +{ +public: + StreamTee () : + Stream (), + m_streams_mutex (Mutex::eMutexTypeRecursive), + m_streams () + { + } + + StreamTee (lldb::StreamSP &stream_sp): + Stream (), + m_streams_mutex (Mutex::eMutexTypeRecursive), + m_streams () + { + // No need to lock mutex during construction + if (stream_sp) + m_streams.push_back (stream_sp); + } + + + StreamTee (lldb::StreamSP &stream_sp, lldb::StreamSP &stream_2_sp) : + Stream (), + m_streams_mutex (Mutex::eMutexTypeRecursive), + m_streams () + { + // No need to lock mutex during construction + if (stream_sp) + m_streams.push_back (stream_sp); + if (stream_2_sp) + m_streams.push_back (stream_2_sp); + } + + StreamTee (const StreamTee &rhs) : + Stream (rhs), + m_streams_mutex (Mutex::eMutexTypeRecursive), + m_streams() // Don't copy until we lock down "rhs" + { + Mutex::Locker locker (rhs.m_streams_mutex); + m_streams = rhs.m_streams; + } + + virtual + ~StreamTee () + { + } + + StreamTee & + operator = (const StreamTee &rhs) + { + if (this != &rhs) { + Stream::operator=(rhs); + Mutex::Locker lhs_locker (m_streams_mutex); + Mutex::Locker rhs_locker (rhs.m_streams_mutex); + m_streams = rhs.m_streams; + } + return *this; + } + + virtual void + Flush () + { + Mutex::Locker locker (m_streams_mutex); + collection::iterator pos, end; + for (pos = m_streams.begin(), end = m_streams.end(); pos != end; ++pos) + { + // Allow for our collection to contain NULL streams. This allows + // the StreamTee to be used with hard coded indexes for clients + // that might want N total streams with only a few that are set + // to valid values. + Stream *strm = pos->get(); + if (strm) + strm->Flush (); + } + } + + virtual size_t + Write (const void *s, size_t length) + { + Mutex::Locker locker (m_streams_mutex); + if (m_streams.empty()) + return 0; + + size_t min_bytes_written = SIZE_MAX; + collection::iterator pos, end; + for (pos = m_streams.begin(), end = m_streams.end(); pos != end; ++pos) + { + // Allow for our collection to contain NULL streams. This allows + // the StreamTee to be used with hard coded indexes for clients + // that might want N total streams with only a few that are set + // to valid values. + Stream *strm = pos->get(); + if (strm) + { + const size_t bytes_written = strm->Write (s, length); + if (min_bytes_written > bytes_written) + min_bytes_written = bytes_written; + } + } + if (min_bytes_written == SIZE_MAX) + return 0; + return min_bytes_written; + } + + size_t + AppendStream (const lldb::StreamSP &stream_sp) + { + size_t new_idx = m_streams.size(); + Mutex::Locker locker (m_streams_mutex); + m_streams.push_back (stream_sp); + return new_idx; + } + + size_t + GetNumStreams () const + { + size_t result = 0; + { + Mutex::Locker locker (m_streams_mutex); + result = m_streams.size(); + } + return result; + } + + lldb::StreamSP + GetStreamAtIndex (uint32_t idx) + { + lldb::StreamSP stream_sp; + Mutex::Locker locker (m_streams_mutex); + if (idx < m_streams.size()) + stream_sp = m_streams[idx]; + return stream_sp; + } + + void + SetStreamAtIndex (uint32_t idx, const lldb::StreamSP& stream_sp) + { + Mutex::Locker locker (m_streams_mutex); + // Resize our stream vector as necessary to fit as many streams + // as needed. This also allows this class to be used with hard + // coded indexes that can be used contain many streams, not all + // of which are valid. + if (idx >= m_streams.size()) + m_streams.resize(idx + 1); + m_streams[idx] = stream_sp; + } + + +protected: + typedef std::vector collection; + mutable Mutex m_streams_mutex; + collection m_streams; +}; + +} // namespace lldb_private +#endif // #ifndef liblldb_StreamTee_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Core/StringList.h b/contrib/llvm/tools/lldb/include/lldb/Core/StringList.h new file mode 100644 index 00000000000..5503274173c --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Core/StringList.h @@ -0,0 +1,107 @@ +//===-- StringList.h --------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_StringList_h_ +#define liblldb_StringList_h_ + +#include + +#include "lldb/Core/STLUtils.h" +#include "lldb/lldb-forward.h" + +namespace lldb_private { + +class StringList +{ +public: + + StringList (); + + StringList (const char *str); + + StringList (const char **strv, int strc); + + virtual + ~StringList (); + + void + AppendString (const std::string &s); + + void + AppendString (const char *str); + + void + AppendString (const char *str, size_t str_len); + + void + AppendList (const char ** strv, int strc); + + void + AppendList (StringList strings); + + bool + ReadFileLines (FileSpec &input_file); + + size_t + GetSize () const; + + const char * + GetStringAtIndex (size_t idx) const; + + void + Join (const char *separator, Stream &strm); + + void + Clear (); + + void + LongestCommonPrefix (std::string &common_prefix); + + void + InsertStringAtIndex (size_t id, const char *str); + + void + DeleteStringAtIndex (size_t id); + + void + RemoveBlankLines (); + + size_t + SplitIntoLines (const char *lines, size_t len); + + std::string + CopyList(const char* item_preamble = NULL, + const char* items_sep = "\n"); + + StringList& + operator << (const char* str); + + StringList& + operator << (StringList strings); + + // This string list contains a list of valid auto completion + // strings, and the "s" is passed in. "matches" is filled in + // with zero or more string values that start with "s", and + // the first string to exactly match one of the string + // values in this collection, will have "exact_matches_idx" + // filled in to match the index, or "exact_matches_idx" will + // have SIZE_MAX + size_t + AutoComplete (const char *s, + StringList &matches, + size_t &exact_matches_idx) const; + +private: + + STLStringArray m_strings; +}; + +} // namespace lldb_private + +#endif // liblldb_StringList_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Core/ThreadSafeSTLMap.h b/contrib/llvm/tools/lldb/include/lldb/Core/ThreadSafeSTLMap.h new file mode 100644 index 00000000000..703ce481f63 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Core/ThreadSafeSTLMap.h @@ -0,0 +1,184 @@ +//===-- ThreadSafeSTLMap.h --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ThreadSafeSTLMap_h_ +#define liblldb_ThreadSafeSTLMap_h_ + +// C Includes +// C++ Includes +#include + +// Other libraries and framework includes +// Project includes +#include "lldb/Host/Mutex.h" + +namespace lldb_private { + +template +class ThreadSafeSTLMap +{ +public: + typedef std::map<_Key,_Tp> collection; + typedef typename collection::iterator iterator; + typedef typename collection::const_iterator const_iterator; + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + ThreadSafeSTLMap() : + m_collection (), + m_mutex (Mutex::eMutexTypeRecursive) + { + } + + ~ThreadSafeSTLMap() + { + } + + bool + IsEmpty() const + { + Mutex::Locker locker(m_mutex); + return m_collection.empty(); + } + + void + Clear() + { + Mutex::Locker locker(m_mutex); + return m_collection.clear(); + } + + size_t + Erase (const _Key& key) + { + Mutex::Locker locker(m_mutex); + return EraseNoLock (key); + } + + size_t + EraseNoLock (const _Key& key) + { + return m_collection.erase (key); + } + + bool + GetValueForKey (const _Key& key, _Tp &value) const + { + Mutex::Locker locker(m_mutex); + return GetValueForKeyNoLock (key, value); + } + + // Call this if you have already manually locked the mutex using the + // GetMutex() accessor + bool + GetValueForKeyNoLock (const _Key& key, _Tp &value) const + { + const_iterator pos = m_collection.find(key); + if (pos != m_collection.end()) + { + value = pos->second; + return true; + } + return false; + } + + bool + GetFirstKeyForValue (const _Tp &value, _Key& key) const + { + Mutex::Locker locker(m_mutex); + return GetFirstKeyForValueNoLock (value, key); + } + + bool + GetFirstKeyForValueNoLock (const _Tp &value, _Key& key) const + { + const_iterator pos, end = m_collection.end(); + for (pos = m_collection.begin(); pos != end; ++pos) + { + if (pos->second == value) + { + key = pos->first; + return true; + } + } + return false; + } + + bool + LowerBound (const _Key& key, + _Key& match_key, + _Tp &match_value, + bool decrement_if_not_equal) const + { + Mutex::Locker locker(m_mutex); + return LowerBoundNoLock (key, match_key, match_value, decrement_if_not_equal); + } + + bool + LowerBoundNoLock (const _Key& key, + _Key& match_key, + _Tp &match_value, + bool decrement_if_not_equal) const + { + const_iterator pos = m_collection.lower_bound (key); + if (pos != m_collection.end()) + { + match_key = pos->first; + if (decrement_if_not_equal && key != match_key && pos != m_collection.begin()) + { + --pos; + match_key = pos->first; + } + match_value = pos->second; + return true; + } + return false; + } + + iterator + lower_bound_unsafe (const _Key& key) + { + return m_collection.lower_bound (key); + } + + void + SetValueForKey (const _Key& key, const _Tp &value) + { + Mutex::Locker locker(m_mutex); + SetValueForKeyNoLock (key, value); + } + + // Call this if you have already manually locked the mutex using the + // GetMutex() accessor + void + SetValueForKeyNoLock (const _Key& key, const _Tp &value) + { + m_collection[key] = value; + } + + Mutex & + GetMutex () + { + return m_mutex; + } + +private: + collection m_collection; + mutable Mutex m_mutex; + + //------------------------------------------------------------------ + // For ThreadSafeSTLMap only + //------------------------------------------------------------------ + DISALLOW_COPY_AND_ASSIGN (ThreadSafeSTLMap); +}; + + +} // namespace lldb_private + +#endif // liblldb_ThreadSafeSTLMap_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Core/ThreadSafeValue.h b/contrib/llvm/tools/lldb/include/lldb/Core/ThreadSafeValue.h new file mode 100644 index 00000000000..42a5a5c6725 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Core/ThreadSafeValue.h @@ -0,0 +1,96 @@ +//===-- ThreadSafeValue.h ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ThreadSafeValue_h_ +#define liblldb_ThreadSafeValue_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Host/Mutex.h" + +namespace lldb_private { + +template +class ThreadSafeValue +{ +public: + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + ThreadSafeValue() : + m_value (), + m_mutex (Mutex::eMutexTypeRecursive) + { + } + + ThreadSafeValue(const T& value) : + m_value (value), + m_mutex (Mutex::eMutexTypeRecursive) + { + } + + ~ThreadSafeValue() + { + } + + T + GetValue () const + { + T value; + { + Mutex::Locker locker(m_mutex); + value = m_value; + } + return value; + } + + // Call this if you have already manually locked the mutex using the + // GetMutex() accessor + const T& + GetValueNoLock () const + { + return m_value; + } + + void + SetValue (const T& value) + { + Mutex::Locker locker(m_mutex); + m_value = value; + } + + // Call this if you have already manually locked the mutex using the + // GetMutex() accessor + void + SetValueNoLock (const T& value) + { + m_value = value; + } + + Mutex & + GetMutex () + { + return m_mutex; + } + +private: + T m_value; + mutable Mutex m_mutex; + + //------------------------------------------------------------------ + // For ThreadSafeValue only + //------------------------------------------------------------------ + DISALLOW_COPY_AND_ASSIGN (ThreadSafeValue); +}; + + +} // namespace lldb_private +#endif // liblldb_ThreadSafeValue_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Core/Timer.h b/contrib/llvm/tools/lldb/include/lldb/Core/Timer.h new file mode 100644 index 00000000000..e354d91be44 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Core/Timer.h @@ -0,0 +1,160 @@ +//===-- Timer.h -------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_Timer_h_ +#define liblldb_Timer_h_ +#if defined(__cplusplus) + +#include +#include +#include +#include "lldb/lldb-private.h" +#include "lldb/Host/TimeValue.h" + +namespace lldb_private { + +//---------------------------------------------------------------------- +/// @class Timer Timer.h "lldb/Core/Timer.h" +/// @brief A timer class that simplifies common timing metrics. +/// +/// A scoped timer class that allows a variety of pthread mutex +/// objects to have a mutex locked when a Timer::Locker +/// object is created, and unlocked when it goes out of scope or +/// when the Timer::Locker::Reset(pthread_mutex_t *) +/// is called. This provides an exception safe way to lock a mutex +/// in a scope. +//---------------------------------------------------------------------- + +class Timer +{ +public: + static void + Initialize (); + + //-------------------------------------------------------------- + /// Default constructor. + //-------------------------------------------------------------- + Timer(const char *category, const char *format, ...) __attribute__ ((format (printf, 3, 4))); + + //-------------------------------------------------------------- + /// Desstructor + //-------------------------------------------------------------- + ~Timer(); + + void + Dump (); + + static void + SetDisplayDepth (uint32_t depth); + + static void + SetQuiet (bool value); + + static void + DumpCategoryTimes (Stream *s); + + static void + ResetCategoryTimes (); + +protected: + + void + ChildStarted (const TimeValue& time); + + void + ChildStopped (const TimeValue& time); + + uint64_t + GetTotalElapsedNanoSeconds(); + + uint64_t + GetTimerElapsedNanoSeconds(); + + //-------------------------------------------------------------- + /// Member variables + //-------------------------------------------------------------- + const char *m_category; + TimeValue m_total_start; + TimeValue m_timer_start; + uint64_t m_total_ticks; // Total running time for this timer including when other timers below this are running + uint64_t m_timer_ticks; // Ticks for this timer that do not include when other timers below this one are running + static uint32_t g_depth; + static uint32_t g_display_depth; + static FILE * g_file; +private: + Timer(); + DISALLOW_COPY_AND_ASSIGN (Timer); +}; + +class IntervalTimer +{ +public: + IntervalTimer() : + m_start (TimeValue::Now()) + { + } + + ~IntervalTimer() + { + } + + uint64_t + GetElapsedNanoSeconds() const + { + return TimeValue::Now() - m_start; + } + + void + Reset () + { + m_start = TimeValue::Now(); + } + + int + PrintfElapsed (const char *format, ...) __attribute__ ((format (printf, 2, 3))) + { + TimeValue now (TimeValue::Now()); + const uint64_t elapsed_nsec = now - m_start; + const char *unit = NULL; + float elapsed_value; + if (elapsed_nsec < 1000) + { + unit = "ns"; + elapsed_value = (float)elapsed_nsec; + } + else if (elapsed_nsec < 1000000) + { + unit = "us"; + elapsed_value = (float)elapsed_nsec/1000.0f; + } + else if (elapsed_nsec < 1000000000) + { + unit = "ms"; + elapsed_value = (float)elapsed_nsec/1000000.0f; + } + else + { + unit = "sec"; + elapsed_value = (float)elapsed_nsec/1000000000.0f; + } + int result = printf ("%3.2f %s: ", elapsed_value, unit); + va_list args; + va_start (args, format); + result += vprintf (format, args); + va_end (args); + return result; + } +protected: + TimeValue m_start; +}; + +} // namespace lldb_private + +#endif // #if defined(__cplusplus) +#endif // #ifndef liblldb_Timer_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Core/UUID.h b/contrib/llvm/tools/lldb/include/lldb/Core/UUID.h new file mode 100644 index 00000000000..fe72b8eb0c7 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Core/UUID.h @@ -0,0 +1,109 @@ +//===-- UUID.h --------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_UUID_h_ +#define liblldb_UUID_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes + +#include "lldb/lldb-private.h" + +namespace lldb_private { + +class UUID +{ +public: + // Most UUIDs are 16 bytes, but some Linux build-ids (SHA1) are 20. + typedef uint8_t ValueType[20]; + + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + UUID (); + UUID (const UUID& rhs); + UUID (const void *uuid_bytes, uint32_t num_uuid_bytes); + + ~UUID (); + + const UUID& + operator=(const UUID& rhs); + + void + Clear (); + + void + Dump (Stream *s) const; + + const void * + GetBytes() const; + + size_t + GetByteSize(); + + bool + IsValid () const; + + bool + SetBytes (const void *uuid_bytes, uint32_t num_uuid_bytes = 16); + + std::string + GetAsString (const char *separator = NULL) const; + + size_t + SetFromCString (const char *c_str, uint32_t num_uuid_bytes = 16); + + // Decode as many UUID bytes (up to 16) as possible from the C string "cstr" + // This is used for auto completion where a partial UUID might have been + // typed in. It + //------------------------------------------------------------------ + /// Decode as many UUID bytes (up to 16) as possible from the C + /// string \a cstr. + /// + /// @param[in] cstr + /// A NULL terminate C string that points at a UUID string value + /// (no leading spaces). The string must contain only hex + /// characters and optionally can contain the '-' sepearators. + /// + /// @param[in] uuid_bytes + /// A buffer of bytes that will contain a full or patially + /// decoded UUID. + /// + /// @param[out] end + /// If \a end is not NULL, it will be filled in with the a + /// pointer to the character after the last successfully decoded + /// byte. + /// + /// @return + /// Returns the number of bytes that were successfully decoded + /// which should be 16 if a full UUID value was properly decoded. + //------------------------------------------------------------------ + static size_t + DecodeUUIDBytesFromCString (const char *cstr, ValueType &uuid_bytes, const char **end, uint32_t num_uuid_bytes = 16); + +protected: + //------------------------------------------------------------------ + // Classes that inherit from UUID can see and modify these + //------------------------------------------------------------------ + uint32_t m_num_uuid_bytes; // Should be 16 or 20 + ValueType m_uuid; +}; + +bool operator == (const UUID &lhs, const UUID &rhs); +bool operator != (const UUID &lhs, const UUID &rhs); +bool operator < (const UUID &lhs, const UUID &rhs); +bool operator <= (const UUID &lhs, const UUID &rhs); +bool operator > (const UUID &lhs, const UUID &rhs); +bool operator >= (const UUID &lhs, const UUID &rhs); + +} // namespace lldb_private + +#endif // liblldb_UUID_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Core/UniqueCStringMap.h b/contrib/llvm/tools/lldb/include/lldb/Core/UniqueCStringMap.h new file mode 100644 index 00000000000..972c0d53ea9 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Core/UniqueCStringMap.h @@ -0,0 +1,361 @@ +//===-- UniqueCStringMap.h --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_UniqueCStringMap_h_ +#define liblldb_UniqueCStringMap_h_ +#if defined(__cplusplus) + +#include +#include +#include + +#include "lldb/Core/RegularExpression.h" + +namespace lldb_private { + + + +//---------------------------------------------------------------------- +// Templatized uniqued string map. +// +// This map is useful for mapping unique C string names to values of +// type T. Each "const char *" name added must be unique for a given +// C string value. ConstString::GetCString() can provide such strings. +// Any other string table that has guaranteed unique values can also +// be used. +//---------------------------------------------------------------------- +template +class UniqueCStringMap +{ +public: + struct Entry + { + Entry () : + cstring(NULL), + value() + { + } + + Entry (const char *cstr) : + cstring(cstr), + value() + { + } + + Entry (const char *cstr, const T&v) : + cstring(cstr), + value(v) + { + } + + bool + operator < (const Entry& rhs) const + { + return cstring < rhs.cstring; + } + + const char* cstring; + T value; + }; + + //------------------------------------------------------------------ + // Call this function multiple times to add a bunch of entries to + // this map, then later call UniqueCStringMap::Sort() before doing + // any searches by name. + //------------------------------------------------------------------ + void + Append (const char *unique_cstr, const T& value) + { + m_map.push_back (typename UniqueCStringMap::Entry(unique_cstr, value)); + } + + void + Append (const Entry &e) + { + m_map.push_back (e); + } + + void + Clear () + { + m_map.clear(); + } + + //------------------------------------------------------------------ + // Call this function to always keep the map sorted when putting + // entries into the map. + //------------------------------------------------------------------ + void + Insert (const char *unique_cstr, const T& value) + { + typename UniqueCStringMap::Entry e(unique_cstr, value); + m_map.insert (std::upper_bound (m_map.begin(), m_map.end(), e), e); + } + + void + Insert (const Entry &e) + { + m_map.insert (std::upper_bound (m_map.begin(), m_map.end(), e), e); + } + + //------------------------------------------------------------------ + // Get an entries by index in a variety of forms. + // + // The caller is responsible for ensuring that the collection does + // not change during while using the returned values. + //------------------------------------------------------------------ + bool + GetValueAtIndex (uint32_t idx, T &value) const + { + if (idx < m_map.size()) + { + value = m_map[idx].value; + return true; + } + return false; + } + + const char * + GetCStringAtIndexUnchecked (uint32_t idx) const + { + return m_map[idx].cstring; + } + + // Use this function if you have simple types in your map that you + // can easily copy when accessing values by index. + T + GetValueAtIndexUnchecked (uint32_t idx) const + { + return m_map[idx].value; + } + + // Use this function if you have complex types in your map that you + // don't want to copy when accessing values by index. + const T & + GetValueRefAtIndexUnchecked (uint32_t idx) const + { + return m_map[idx].value; + } + + const char * + GetCStringAtIndex (uint32_t idx) const + { + if (idx < m_map.size()) + return m_map[idx].cstring; + return NULL; + } + + //------------------------------------------------------------------ + // Find the value for the unique string in the map. + // + // Return the value for \a unique_cstr if one is found, return + // \a fail_value otherwise. This method works well for simple type + // T values and only if there is a sensible failure value that can + // be returned and that won't match any existing values. + //------------------------------------------------------------------ + T + Find (const char *unique_cstr, T fail_value) const + { + Entry search_entry (unique_cstr); + const_iterator end = m_map.end(); + const_iterator pos = std::lower_bound (m_map.begin(), end, search_entry); + if (pos != end) + { + if (pos->cstring == unique_cstr) + return pos->value; + } + return fail_value; + } + //------------------------------------------------------------------ + // Get a pointer to the first entry that matches "name". NULL will + // be returned if there is no entry that matches "name". + // + // The caller is responsible for ensuring that the collection does + // not change during while using the returned pointer. + //------------------------------------------------------------------ + const Entry * + FindFirstValueForName (const char *unique_cstr) const + { + Entry search_entry (unique_cstr); + const_iterator end = m_map.end(); + const_iterator pos = std::lower_bound (m_map.begin(), end, search_entry); + if (pos != end) + { + const char *pos_cstr = pos->cstring; + if (pos_cstr == unique_cstr) + return &(*pos); + } + return NULL; + } + + //------------------------------------------------------------------ + // Get a pointer to the next entry that matches "name" from a + // previously returned Entry pointer. NULL will be returned if there + // is no subsequent entry that matches "name". + // + // The caller is responsible for ensuring that the collection does + // not change during while using the returned pointer. + //------------------------------------------------------------------ + const Entry * + FindNextValueForName (const Entry *entry_ptr) const + { + if (!m_map.empty()) + { + const Entry *first_entry = &m_map[0]; + const Entry *after_last_entry = first_entry + m_map.size(); + const Entry *next_entry = entry_ptr + 1; + if (first_entry <= next_entry && next_entry < after_last_entry) + { + if (next_entry->cstring == entry_ptr->cstring) + return next_entry; + } + } + return NULL; + } + + size_t + GetValues (const char *unique_cstr, std::vector &values) const + { + const size_t start_size = values.size(); + + Entry search_entry (unique_cstr); + const_iterator pos, end = m_map.end(); + for (pos = std::lower_bound (m_map.begin(), end, search_entry); pos != end; ++pos) + { + if (pos->cstring == unique_cstr) + values.push_back (pos->value); + else + break; + } + + return values.size() - start_size; + } + + size_t + GetValues (const RegularExpression& regex, std::vector &values) const + { + const size_t start_size = values.size(); + + const_iterator pos, end = m_map.end(); + for (pos = m_map.begin(); pos != end; ++pos) + { + if (regex.Execute(pos->cstring)) + values.push_back (pos->value); + } + + return values.size() - start_size; + } + + //------------------------------------------------------------------ + // Get the total number of entries in this map. + //------------------------------------------------------------------ + size_t + GetSize () const + { + return m_map.size(); + } + + + //------------------------------------------------------------------ + // Returns true if this map is empty. + //------------------------------------------------------------------ + bool + IsEmpty() const + { + return m_map.empty(); + } + + //------------------------------------------------------------------ + // Reserve memory for at least "n" entries in the map. This is + // useful to call when you know you will be adding a lot of entries + // using UniqueCStringMap::Append() (which should be followed by a + // call to UniqueCStringMap::Sort()) or to UniqueCStringMap::Insert(). + //------------------------------------------------------------------ + void + Reserve (size_t n) + { + m_map.reserve (n); + } + + //------------------------------------------------------------------ + // Sort the unsorted contents in this map. A typical code flow would + // be: + // size_t approximate_num_entries = .... + // UniqueCStringMap my_map; + // my_map.Reserve (approximate_num_entries); + // for (...) + // { + // my_map.Append (UniqueCStringMap::Entry(GetName(...), GetValue(...))); + // } + // my_map.Sort(); + //------------------------------------------------------------------ + void + Sort () + { + std::sort (m_map.begin(), m_map.end()); + } + + //------------------------------------------------------------------ + // Since we are using a vector to contain our items it will always + // double its memory consumption as things are added to the vector, + // so if you intend to keep a UniqueCStringMap around and have + // a lot of entries in the map, you will want to call this function + // to create a new vector and copy _only_ the exact size needed as + // part of the finalization of the string map. + //------------------------------------------------------------------ + void + SizeToFit () + { + if (m_map.size() < m_map.capacity()) + { + collection temp (m_map.begin(), m_map.end()); + m_map.swap(temp); + } + } + + size_t + Erase (const char *unique_cstr) + { + size_t num_removed = 0; + Entry search_entry (unique_cstr); + iterator end = m_map.end(); + iterator begin = m_map.begin(); + iterator lower_pos = std::lower_bound (begin, end, search_entry); + if (lower_pos != end) + { + if (lower_pos->cstring == unique_cstr) + { + iterator upper_pos = std::upper_bound (lower_pos, end, search_entry); + if (lower_pos == upper_pos) + { + m_map.erase (lower_pos); + num_removed = 1; + } + else + { + num_removed = std::distance (lower_pos, upper_pos); + m_map.erase (lower_pos, upper_pos); + } + } + } + return num_removed; + } +protected: + typedef std::vector collection; + typedef typename collection::iterator iterator; + typedef typename collection::const_iterator const_iterator; + collection m_map; +}; + + + +} // namespace lldb_private + +#endif // #if defined(__cplusplus) +#endif // liblldb_UniqueCStringMap_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Core/UserID.h b/contrib/llvm/tools/lldb/include/lldb/Core/UserID.h new file mode 100644 index 00000000000..ea6af74759b --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Core/UserID.h @@ -0,0 +1,130 @@ +//===-- UserID.h ------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + + +#ifndef liblldb_UserID_h_ +#define liblldb_UserID_h_ + +#include "lldb/lldb-private.h" + +namespace lldb_private { + +//---------------------------------------------------------------------- +/// @class UserID UserID.h "lldb/Core/UserID.h" +/// @brief A mix in class that contains a generic user ID. +/// +/// UserID is desinged as a mix in class that can contain an integer +/// based unique identifier for a varietly of objects in lldb. +/// +/// The value for this identifier is chosen by each parser plug-in. A +/// value should be chosen that makes sense for each kind of object +/// should and allows quick access to further and more in depth parsing. +/// +/// Symbol table entries can use this to store the original symbol table +/// index, functions can use it to store the symbol table index or the +/// DWARF offset. +//---------------------------------------------------------------------- +struct UserID +{ + //------------------------------------------------------------------ + /// Construct with optional user ID. + //------------------------------------------------------------------ + UserID (lldb::user_id_t uid = LLDB_INVALID_UID) : m_uid(uid) {} + + //------------------------------------------------------------------ + /// Destructor. + //------------------------------------------------------------------ + ~UserID () + { + } + + //------------------------------------------------------------------ + /// Clears the object state. + /// + /// Clears the object contents back to a default invalid state. + //------------------------------------------------------------------ + void + Clear () { m_uid = LLDB_INVALID_UID; } + + //------------------------------------------------------------------ + /// Get accessor for the user ID. + /// + /// @return + /// The user ID. + //------------------------------------------------------------------ + lldb::user_id_t + GetID () const { return m_uid; } + + //------------------------------------------------------------------ + /// Set accessor for the user ID. + /// + /// @param[in] uid + /// The new user ID. + //------------------------------------------------------------------ + void + SetID (lldb::user_id_t uid) { m_uid = uid; } + + //------------------------------------------------------------------ + /// Unary predicate function object that can search for a matching + /// user ID. + /// + /// Function object that can be used on any class that inherits + /// from UserID: + /// \code + /// iterator pos; + /// pos = std::find_if (coll.begin(), coll.end(), UserID::IDMatches(blockID)); + /// \endcode + //------------------------------------------------------------------ + class IDMatches + { + public: + //-------------------------------------------------------------- + /// Construct with the user ID to look for. + //-------------------------------------------------------------- + IDMatches (lldb::user_id_t uid) : m_uid(uid) {} + + //-------------------------------------------------------------- + /// Unary predicate function object callback. + //-------------------------------------------------------------- + bool + operator () (const UserID& rhs) const { return m_uid == rhs.GetID(); } + + private: + //-------------------------------------------------------------- + // Member variables. + //-------------------------------------------------------------- + const lldb::user_id_t m_uid; ///< The user ID we are looking for + }; + + +protected: + //------------------------------------------------------------------ + // Member variables. + //------------------------------------------------------------------ + lldb::user_id_t m_uid; ///< The user ID that uniquely identifies an object. +}; + +inline bool operator== (const UserID& lhs, const UserID& rhs) +{ + return lhs.GetID() == rhs.GetID(); +} + +inline bool operator!= (const UserID& lhs, const UserID& rhs) +{ + return lhs.GetID() != rhs.GetID(); +} + +//-------------------------------------------------------------- +/// Stream the UserID object to a Stream. +//-------------------------------------------------------------- +Stream& operator << (Stream& strm, const UserID& uid); + +} // namespace lldb_private + +#endif // liblldb_UserID_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Core/UserSettingsController.h b/contrib/llvm/tools/lldb/include/lldb/Core/UserSettingsController.h new file mode 100644 index 00000000000..7e72b89ad8e --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Core/UserSettingsController.h @@ -0,0 +1,98 @@ +//====-- UserSettingsController.h --------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_UserSettingsController_h_ +#define liblldb_UserSettingsController_h_ + +// C Includes +// C++ Includes + +#include +#include + +// Other libraries and framework includes +// Project includes + +#include "lldb/lldb-private.h" +#include "lldb/Core/ConstString.h" +#include "lldb/Core/StringList.h" +#include "lldb/Core/Stream.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Host/Mutex.h" +#include "lldb/Interpreter/OptionValue.h" + +namespace lldb_private { + +class Properties +{ +public: + Properties () : + m_collection_sp () + { + } + + Properties (const lldb::OptionValuePropertiesSP &collection_sp) : + m_collection_sp (collection_sp) + { + } + + virtual + ~Properties() + { + } + + virtual lldb::OptionValuePropertiesSP + GetValueProperties () const + { + // This function is virtual in case subclasses want to lazily + // implement creating the properties. + return m_collection_sp; + } + + virtual lldb::OptionValueSP + GetPropertyValue (const ExecutionContext *exe_ctx, + const char *property_path, + bool will_modify, + Error &error) const; + + virtual Error + SetPropertyValue (const ExecutionContext *exe_ctx, + VarSetOperationType op, + const char *property_path, + const char *value); + + virtual Error + DumpPropertyValue (const ExecutionContext *exe_ctx, + Stream &strm, + const char *property_path, + uint32_t dump_mask); + + virtual void + DumpAllPropertyValues (const ExecutionContext *exe_ctx, + Stream &strm, + uint32_t dump_mask); + + virtual void + DumpAllDescriptions (CommandInterpreter &interpreter, + Stream &strm) const; + + size_t + Apropos (const char *keyword, + std::vector &matching_properties) const; + + lldb::OptionValuePropertiesSP + GetSubProperty (const ExecutionContext *exe_ctx, + const ConstString &name); +protected: + lldb::OptionValuePropertiesSP m_collection_sp; +}; + +} // namespace lldb_private + +#endif // liblldb_UserSettingsController_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Core/VMRange.h b/contrib/llvm/tools/lldb/include/lldb/Core/VMRange.h new file mode 100644 index 00000000000..94c83e730e9 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Core/VMRange.h @@ -0,0 +1,181 @@ +//===-- VMRange.h -----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_VMRange_h_ +#define liblldb_VMRange_h_ + +#include "lldb/lldb-private.h" +#include + +namespace lldb_private { + +//---------------------------------------------------------------------- +// A vm address range. These can represent offsets ranges or actual +// addresses. +//---------------------------------------------------------------------- +class VMRange +{ +public: + + typedef std::vector collection; + typedef collection::iterator iterator; + typedef collection::const_iterator const_iterator; + + VMRange() : + m_base_addr(0), + m_byte_size(0) + { + } + + VMRange(lldb::addr_t start_addr, lldb::addr_t end_addr) : + m_base_addr(start_addr), + m_byte_size(end_addr > start_addr ? end_addr - start_addr : 0) + { + } + + ~VMRange() + { + } + + void + Clear () + { + m_base_addr = 0; + m_byte_size = 0; + } + + // Set the start and end values + void + Reset (lldb::addr_t start_addr, lldb::addr_t end_addr) + { + SetBaseAddress (start_addr); + SetEndAddress (end_addr); + } + + // Set the start value for the range, and keep the same size + void + SetBaseAddress (lldb::addr_t base_addr) + { + m_base_addr = base_addr; + } + + void + SetEndAddress (lldb::addr_t end_addr) + { + const lldb::addr_t base_addr = GetBaseAddress(); + if (end_addr > base_addr) + m_byte_size = end_addr - base_addr; + else + m_byte_size = 0; + } + + lldb::addr_t + GetByteSize () const + { + return m_byte_size; + } + + void + SetByteSize (lldb::addr_t byte_size) + { + m_byte_size = byte_size; + } + + lldb::addr_t + GetBaseAddress () const + { + return m_base_addr; + } + + lldb::addr_t + GetEndAddress () const + { + return GetBaseAddress() + m_byte_size; + } + + bool + IsValid() const + { + return m_byte_size > 0; + } + + bool + Contains (lldb::addr_t addr) const + { + return (GetBaseAddress() <= addr) && (addr < GetEndAddress()); + } + + bool + Contains (const VMRange& range) const + { + if (Contains(range.GetBaseAddress())) + { + lldb::addr_t range_end = range.GetEndAddress(); + return (GetBaseAddress() <= range_end) && (range_end <= GetEndAddress()); + } + return false; + } + + void + Dump (Stream *s, lldb::addr_t base_addr = 0, uint32_t addr_width = 8) const; + + class ValueInRangeUnaryPredicate + { + public: + ValueInRangeUnaryPredicate(lldb::addr_t value) : + _value(value) + { + } + bool operator()(const VMRange& range) const + { + return range.Contains(_value); + } + lldb::addr_t _value; + }; + + class RangeInRangeUnaryPredicate + { + public: + RangeInRangeUnaryPredicate(VMRange range) : + _range(range) + { + } + bool operator()(const VMRange& range) const + { + return range.Contains(_range); + } + const VMRange& _range; + }; + + static bool + ContainsValue(const VMRange::collection& coll, lldb::addr_t value); + + static bool + ContainsRange(const VMRange::collection& coll, const VMRange& range); + + // Returns a valid index into coll when a match is found, else UINT32_MAX + // is returned + static size_t + FindRangeIndexThatContainsValue (const VMRange::collection& coll, lldb::addr_t value); + +protected: + lldb::addr_t m_base_addr; + lldb::addr_t m_byte_size; +}; + +bool operator== (const VMRange& lhs, const VMRange& rhs); +bool operator!= (const VMRange& lhs, const VMRange& rhs); +bool operator< (const VMRange& lhs, const VMRange& rhs); +bool operator<= (const VMRange& lhs, const VMRange& rhs); +bool operator> (const VMRange& lhs, const VMRange& rhs); +bool operator>= (const VMRange& lhs, const VMRange& rhs); + +} // namespace lldb_private + +#endif // liblldb_VMRange_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Core/Value.h b/contrib/llvm/tools/lldb/include/lldb/Core/Value.h new file mode 100644 index 00000000000..5461ca73d08 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Core/Value.h @@ -0,0 +1,314 @@ +//===-- Value.h -------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_Value_h_ +#define liblldb_Value_h_ + +// C Includes +// C++ Includes +#include +#include +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private.h" +#include "lldb/Core/ClangForward.h" +#include "lldb/Core/DataBufferHeap.h" +#include "lldb/Core/Error.h" +#include "lldb/Core/Scalar.h" +#include "lldb/Symbol/ClangASTType.h" + +namespace lldb_private { + +class Value +{ +public: + + // Values Less than zero are an error, greater than or equal to zero + // returns what the Scalar result is. + enum ValueType + { + // m_value contains... + // ============================ + eValueTypeScalar, // raw scalar value + eValueTypeVector, // byte array of m_vector.length with endianness of m_vector.byte_order + eValueTypeFileAddress, // file address value + eValueTypeLoadAddress, // load address value + eValueTypeHostAddress // host address value (for memory in the process that is using liblldb) + }; + + enum ContextType // Type that describes Value::m_context + { + // m_context contains... + // ==================== + eContextTypeInvalid, // undefined + eContextTypeRegisterInfo, // RegisterInfo * (can be a scalar or a vector register) + eContextTypeLLDBType, // lldb_private::Type * + eContextTypeVariable // lldb_private::Variable * + }; + + const static size_t kMaxByteSize = 32u; + + struct Vector + { + // The byte array must be big enough to hold vector registers for any supported target. + uint8_t bytes[kMaxByteSize]; + size_t length; + lldb::ByteOrder byte_order; + + Vector() : + length(0), + byte_order(lldb::eByteOrderInvalid) + { + } + + Vector(const Vector& vector) + { *this = vector; + } + const Vector& + operator=(const Vector& vector) + { + SetBytes(vector.bytes, vector.length, vector.byte_order); + return *this; + } + + void + Clear () + { + length = 0; + } + + bool + SetBytes(const void *bytes, size_t length, lldb::ByteOrder byte_order) + { + this->length = length; + this->byte_order = byte_order; + if (length) + ::memcpy(this->bytes, bytes, length < kMaxByteSize ? length : kMaxByteSize); + return IsValid(); + } + + bool + IsValid() const + { + return (length > 0 && length < kMaxByteSize && byte_order != lldb::eByteOrderInvalid); + } + // Casts a vector, if valid, to an unsigned int of matching or largest supported size. + // Truncates to the beginning of the vector if required. + // Returns a default constructed Scalar if the Vector data is internally inconsistent. + Scalar + GetAsScalar() const + { + Scalar scalar; + if (IsValid()) + { + if (length == 1) scalar = *(const uint8_t *)bytes; + else if (length == 2) scalar = *(const uint16_t *)bytes; + else if (length == 4) scalar = *(const uint32_t *)bytes; + else if (length == 8) scalar = *(const uint64_t *)bytes; +#if defined (ENABLE_128_BIT_SUPPORT) + else if (length >= 16) scalar = *(const __uint128_t *)bytes; +#else + else if (length >= 16) scalar = *(const __uint64_t *)bytes; +#endif + } + return scalar; + } + }; + + Value(); + Value(const Scalar& scalar); + Value(const Vector& vector); + Value(const uint8_t *bytes, int len); + Value(const Value &rhs); + + Value & + operator=(const Value &rhs); + + const ClangASTType & + GetClangType(); + + void + SetClangType (const ClangASTType &clang_type); + + ValueType + GetValueType() const; + + AddressType + GetValueAddressType () const; + + ContextType + GetContextType() const + { + return m_context_type; + } + + void + SetValueType (ValueType value_type) + { + m_value_type = value_type; + } + + void + ClearContext () + { + m_context = NULL; + m_context_type = eContextTypeInvalid; + } + + void + SetContext (ContextType context_type, void *p) + { + m_context_type = context_type; + m_context = p; + if (m_context_type == eContextTypeRegisterInfo) { + RegisterInfo *reg_info = GetRegisterInfo(); + if (reg_info->encoding == lldb::eEncodingVector) + SetValueType(eValueTypeVector); + else + SetValueType(eValueTypeScalar); + } + } + + RegisterInfo * + GetRegisterInfo() const; + + Type * + GetType(); + + Scalar & + ResolveValue (ExecutionContext *exe_ctx); + + const Scalar & + GetScalar() const + { + return m_value; + } + + const Vector & + GetVector() const + { + return m_vector; + } + + Scalar & + GetScalar() + { + return m_value; + } + + Vector & + GetVector() + { + return m_vector; + } + + bool + SetVectorBytes(const Vector& vector) + { + m_vector = vector; + return m_vector.IsValid(); + } + + bool + SetVectorBytes(uint8_t *bytes, size_t length, lldb::ByteOrder byte_order) + { + return m_vector.SetBytes(bytes, length, byte_order); + } + + bool + SetScalarFromVector() + { + if (m_vector.IsValid()) + { + m_value = m_vector.GetAsScalar(); + return true; + } + return false; + } + + void + ResizeData(size_t len); + + bool + ValueOf(ExecutionContext *exe_ctx); + + Variable * + GetVariable(); + + void + Dump (Stream* strm); + + lldb::Format + GetValueDefaultFormat (); + + uint64_t + GetValueByteSize (Error *error_ptr); + + Error + GetValueAsData (ExecutionContext *exe_ctx, + DataExtractor &data, + uint32_t data_offset, + Module *module); // Can be NULL + + static const char * + GetValueTypeAsCString (ValueType context_type); + + static const char * + GetContextTypeAsCString (ContextType context_type); + + bool + GetData (DataExtractor &data); + + void + Clear(); + +protected: + Scalar m_value; + Vector m_vector; + ClangASTType m_clang_type; + void * m_context; + ValueType m_value_type; + ContextType m_context_type; + DataBufferHeap m_data_buffer; +}; + +class ValueList +{ +public: + ValueList () : + m_values() + { + } + + ValueList (const ValueList &rhs); + + ~ValueList () + { + } + + const ValueList & operator= (const ValueList &rhs); + + // void InsertValue (Value *value, size_t idx); + void PushValue (const Value &value); + + size_t GetSize (); + Value *GetValueAtIndex(size_t idx); + void Clear(); + +protected: + +private: + typedef std::vector collection; + + collection m_values; +}; + +} // namespace lldb_private + +#endif // liblldb_Value_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Core/ValueObject.h b/contrib/llvm/tools/lldb/include/lldb/Core/ValueObject.h new file mode 100644 index 00000000000..0d965d6ccc0 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Core/ValueObject.h @@ -0,0 +1,1375 @@ +//===-- ValueObject.h -------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ValueObject_h_ +#define liblldb_ValueObject_h_ + +// C Includes +// C++ Includes +#include +#include +#include +// Other libraries and framework includes +// Project includes + +#include "lldb/lldb-private.h" +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/Error.h" +#include "lldb/Core/Flags.h" +#include "lldb/Core/ConstString.h" +#include "lldb/Core/UserID.h" +#include "lldb/Core/Value.h" +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/ExecutionContextScope.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/StackID.h" +#include "lldb/Utility/SharedCluster.h" + +namespace lldb_private { + +/// ValueObject: +/// +/// This abstract class provides an interface to a particular value, be it a register, a local or global variable, +/// that is evaluated in some particular scope. The ValueObject also has the capibility of being the "child" of +/// some other variable object, and in turn of having children. +/// If a ValueObject is a root variable object - having no parent - then it must be constructed with respect to some +/// particular ExecutionContextScope. If it is a child, it inherits the ExecutionContextScope from its parent. +/// The ValueObject will update itself if necessary before fetching its value, summary, object description, etc. +/// But it will always update itself in the ExecutionContextScope with which it was originally created. + +/// A brief note on life cycle management for ValueObjects. This is a little tricky because a ValueObject can contain +/// various other ValueObjects - the Dynamic Value, its children, the dereference value, etc. Any one of these can be +/// handed out as a shared pointer, but for that contained value object to be valid, the root object and potentially other +/// of the value objects need to stay around. +/// We solve this problem by handing out shared pointers to the Value Object and any of its dependents using a shared +/// ClusterManager. This treats each shared pointer handed out for the entire cluster as a reference to the whole +/// cluster. The whole cluster will stay around until the last reference is released. +/// +/// The ValueObject mostly handle this automatically, if a value object is made with a Parent ValueObject, then it adds +/// itself to the ClusterManager of the parent. + +/// It does mean that external to the ValueObjects we should only ever make available ValueObjectSP's, never ValueObjects +/// or pointers to them. So all the "Root level" ValueObject derived constructors should be private, and +/// should implement a Create function that new's up object and returns a Shared Pointer that it gets from the GetSP() method. +/// +/// However, if you are making an derived ValueObject that will be contained in a parent value object, you should just +/// hold onto a pointer to it internally, and by virtue of passing the parent ValueObject into its constructor, it will +/// be added to the ClusterManager for the parent. Then if you ever hand out a Shared Pointer to the contained ValueObject, +/// just do so by calling GetSP() on the contained object. + +class ValueObject : public UserID +{ +public: + + enum GetExpressionPathFormat + { + eGetExpressionPathFormatDereferencePointers = 1, + eGetExpressionPathFormatHonorPointers + }; + + enum ValueObjectRepresentationStyle + { + eValueObjectRepresentationStyleValue = 1, + eValueObjectRepresentationStyleSummary, + eValueObjectRepresentationStyleLanguageSpecific, + eValueObjectRepresentationStyleLocation, + eValueObjectRepresentationStyleChildrenCount, + eValueObjectRepresentationStyleType, + eValueObjectRepresentationStyleName, + eValueObjectRepresentationStyleExpressionPath + }; + + enum ExpressionPathScanEndReason + { + eExpressionPathScanEndReasonEndOfString = 1, // out of data to parse + eExpressionPathScanEndReasonNoSuchChild, // child element not found + eExpressionPathScanEndReasonEmptyRangeNotAllowed, // [] only allowed for arrays + eExpressionPathScanEndReasonDotInsteadOfArrow, // . used when -> should be used + eExpressionPathScanEndReasonArrowInsteadOfDot, // -> used when . should be used + eExpressionPathScanEndReasonFragileIVarNotAllowed, // ObjC ivar expansion not allowed + eExpressionPathScanEndReasonRangeOperatorNotAllowed, // [] not allowed by options + eExpressionPathScanEndReasonRangeOperatorInvalid, // [] not valid on objects other than scalars, pointers or arrays + eExpressionPathScanEndReasonArrayRangeOperatorMet, // [] is good for arrays, but I cannot parse it + eExpressionPathScanEndReasonBitfieldRangeOperatorMet, // [] is good for bitfields, but I cannot parse after it + eExpressionPathScanEndReasonUnexpectedSymbol, // something is malformed in the expression + eExpressionPathScanEndReasonTakingAddressFailed, // impossible to apply & operator + eExpressionPathScanEndReasonDereferencingFailed, // impossible to apply * operator + eExpressionPathScanEndReasonRangeOperatorExpanded, // [] was expanded into a VOList + eExpressionPathScanEndReasonSyntheticValueMissing, // getting the synthetic children failed + eExpressionPathScanEndReasonUnknown = 0xFFFF + }; + + enum ExpressionPathEndResultType + { + eExpressionPathEndResultTypePlain = 1, // anything but... + eExpressionPathEndResultTypeBitfield, // a bitfield + eExpressionPathEndResultTypeBoundedRange, // a range [low-high] + eExpressionPathEndResultTypeUnboundedRange, // a range [] + eExpressionPathEndResultTypeValueObjectList, // several items in a VOList + eExpressionPathEndResultTypeInvalid = 0xFFFF + }; + + enum ExpressionPathAftermath + { + eExpressionPathAftermathNothing = 1, // just return it + eExpressionPathAftermathDereference, // dereference the target + eExpressionPathAftermathTakeAddress // take target's address + }; + + enum ClearUserVisibleDataItems + { + eClearUserVisibleDataItemsNothing = 1u << 0, + eClearUserVisibleDataItemsValue = 1u << 1, + eClearUserVisibleDataItemsSummary = 1u << 2, + eClearUserVisibleDataItemsLocation = 1u << 3, + eClearUserVisibleDataItemsDescription = 1u << 4, + eClearUserVisibleDataItemsSyntheticChildren = 1u << 5, + eClearUserVisibleDataItemsAllStrings = eClearUserVisibleDataItemsValue | eClearUserVisibleDataItemsSummary | eClearUserVisibleDataItemsLocation | eClearUserVisibleDataItemsDescription, + eClearUserVisibleDataItemsAll = 0xFFFF + }; + + struct GetValueForExpressionPathOptions + { + bool m_check_dot_vs_arrow_syntax; + bool m_no_fragile_ivar; + bool m_allow_bitfields_syntax; + bool m_no_synthetic_children; + + GetValueForExpressionPathOptions(bool dot = false, + bool no_ivar = false, + bool bitfield = true, + bool no_synth = false) : + m_check_dot_vs_arrow_syntax(dot), + m_no_fragile_ivar(no_ivar), + m_allow_bitfields_syntax(bitfield), + m_no_synthetic_children(no_synth) + { + } + + GetValueForExpressionPathOptions& + DoCheckDotVsArrowSyntax() + { + m_check_dot_vs_arrow_syntax = true; + return *this; + } + + GetValueForExpressionPathOptions& + DontCheckDotVsArrowSyntax() + { + m_check_dot_vs_arrow_syntax = false; + return *this; + } + + GetValueForExpressionPathOptions& + DoAllowFragileIVar() + { + m_no_fragile_ivar = false; + return *this; + } + + GetValueForExpressionPathOptions& + DontAllowFragileIVar() + { + m_no_fragile_ivar = true; + return *this; + } + + GetValueForExpressionPathOptions& + DoAllowBitfieldSyntax() + { + m_allow_bitfields_syntax = true; + return *this; + } + + GetValueForExpressionPathOptions& + DontAllowBitfieldSyntax() + { + m_allow_bitfields_syntax = false; + return *this; + } + + GetValueForExpressionPathOptions& + DoAllowSyntheticChildren() + { + m_no_synthetic_children = false; + return *this; + } + + GetValueForExpressionPathOptions& + DontAllowSyntheticChildren() + { + m_no_synthetic_children = true; + return *this; + } + + static const GetValueForExpressionPathOptions + DefaultOptions() + { + static GetValueForExpressionPathOptions g_default_options; + + return g_default_options; + } + + }; + + struct DumpValueObjectOptions + { + uint32_t m_max_ptr_depth; + uint32_t m_max_depth; + bool m_show_types; + bool m_show_location; + bool m_use_objc; + lldb::DynamicValueType m_use_dynamic; + bool m_use_synthetic; + bool m_scope_already_checked; + bool m_flat_output; + uint32_t m_omit_summary_depth; + bool m_ignore_cap; + lldb::Format m_format; + lldb::TypeSummaryImplSP m_summary_sp; + std::string m_root_valobj_name; + bool m_hide_root_type; + bool m_hide_name; + bool m_hide_value; + + DumpValueObjectOptions() : + m_max_ptr_depth(0), + m_max_depth(UINT32_MAX), + m_show_types(false), + m_show_location(false), + m_use_objc(false), + m_use_dynamic(lldb::eNoDynamicValues), + m_use_synthetic(true), + m_scope_already_checked(false), + m_flat_output(false), + m_omit_summary_depth(0), + m_ignore_cap(false), + m_format (lldb::eFormatDefault), + m_summary_sp(), + m_root_valobj_name(), + m_hide_root_type(false), // provide a special compact display for "po" + m_hide_name(false), // provide a special compact display for "po" + m_hide_value(false) // provide a special compact display for "po" + {} + + static const DumpValueObjectOptions + DefaultOptions() + { + static DumpValueObjectOptions g_default_options; + + return g_default_options; + } + + DumpValueObjectOptions (const DumpValueObjectOptions& rhs) : + m_max_ptr_depth(rhs.m_max_ptr_depth), + m_max_depth(rhs.m_max_depth), + m_show_types(rhs.m_show_types), + m_show_location(rhs.m_show_location), + m_use_objc(rhs.m_use_objc), + m_use_dynamic(rhs.m_use_dynamic), + m_use_synthetic(rhs.m_use_synthetic), + m_scope_already_checked(rhs.m_scope_already_checked), + m_flat_output(rhs.m_flat_output), + m_omit_summary_depth(rhs.m_omit_summary_depth), + m_ignore_cap(rhs.m_ignore_cap), + m_format(rhs.m_format), + m_summary_sp(rhs.m_summary_sp), + m_root_valobj_name(rhs.m_root_valobj_name), + m_hide_root_type(rhs.m_hide_root_type), + m_hide_name(rhs.m_hide_name), + m_hide_value(rhs.m_hide_value) + {} + + DumpValueObjectOptions& + SetMaximumPointerDepth(uint32_t depth = 0) + { + m_max_ptr_depth = depth; + return *this; + } + + DumpValueObjectOptions& + SetMaximumDepth(uint32_t depth = 0) + { + m_max_depth = depth; + return *this; + } + + DumpValueObjectOptions& + SetShowTypes(bool show = false) + { + m_show_types = show; + return *this; + } + + DumpValueObjectOptions& + SetShowLocation(bool show = false) + { + m_show_location = show; + return *this; + } + + DumpValueObjectOptions& + SetUseObjectiveC(bool use = false) + { + m_use_objc = use; + return *this; + } + + DumpValueObjectOptions& + SetShowSummary(bool show = true) + { + if (show == false) + SetOmitSummaryDepth(UINT32_MAX); + else + SetOmitSummaryDepth(0); + return *this; + } + + DumpValueObjectOptions& + SetUseDynamicType(lldb::DynamicValueType dyn = lldb::eNoDynamicValues) + { + m_use_dynamic = dyn; + return *this; + } + + DumpValueObjectOptions& + SetUseSyntheticValue(bool use_synthetic = true) + { + m_use_synthetic = use_synthetic; + return *this; + } + + DumpValueObjectOptions& + SetScopeChecked(bool check = true) + { + m_scope_already_checked = check; + return *this; + } + + DumpValueObjectOptions& + SetFlatOutput(bool flat = false) + { + m_flat_output = flat; + return *this; + } + + DumpValueObjectOptions& + SetOmitSummaryDepth(uint32_t depth = 0) + { + m_omit_summary_depth = depth; + return *this; + } + + DumpValueObjectOptions& + SetIgnoreCap(bool ignore = false) + { + m_ignore_cap = ignore; + return *this; + } + + DumpValueObjectOptions& + SetRawDisplay(bool raw = false) + { + if (raw) + { + SetUseSyntheticValue(false); + SetOmitSummaryDepth(UINT32_MAX); + SetIgnoreCap(true); + SetHideName(false); + SetHideValue(false); + } + else + { + SetUseSyntheticValue(true); + SetOmitSummaryDepth(0); + SetIgnoreCap(false); + SetHideName(false); + SetHideValue(false); + } + return *this; + } + + DumpValueObjectOptions& + SetFormat (lldb::Format format = lldb::eFormatDefault) + { + m_format = format; + return *this; + } + + DumpValueObjectOptions& + SetSummary (lldb::TypeSummaryImplSP summary = lldb::TypeSummaryImplSP()) + { + m_summary_sp = summary; + return *this; + } + + DumpValueObjectOptions& + SetRootValueObjectName (const char* name = NULL) + { + if (name) + m_root_valobj_name.assign(name); + else + m_root_valobj_name.clear(); + return *this; + } + + DumpValueObjectOptions& + SetHideRootType (bool hide_root_type = false) + { + m_hide_root_type = hide_root_type; + return *this; + } + + DumpValueObjectOptions& + SetHideName (bool hide_name = false) + { + m_hide_name = hide_name; + return *this; + } + + DumpValueObjectOptions& + SetHideValue (bool hide_value = false) + { + m_hide_value = hide_value; + return *this; + } + }; + + class EvaluationPoint + { + public: + + EvaluationPoint (); + + EvaluationPoint (ExecutionContextScope *exe_scope, bool use_selected = false); + + EvaluationPoint (const EvaluationPoint &rhs); + + ~EvaluationPoint (); + + const ExecutionContextRef & + GetExecutionContextRef() const + { + return m_exe_ctx_ref; + } + + // Set the EvaluationPoint to the values in exe_scope, + // Return true if the Evaluation Point changed. + // Since the ExecutionContextScope is always going to be valid currently, + // the Updated Context will also always be valid. + +// bool +// SetContext (ExecutionContextScope *exe_scope); + + void + SetIsConstant () + { + SetUpdated(); + m_mod_id.SetInvalid(); + } + + bool + IsConstant () const + { + return !m_mod_id.IsValid(); + } + + ProcessModID + GetModID () const + { + return m_mod_id; + } + + void + SetUpdateID (ProcessModID new_id) + { + m_mod_id = new_id; + } + + bool + IsFirstEvaluation () const + { + return m_first_update; + } + + void + SetNeedsUpdate () + { + m_needs_update = true; + } + + void + SetUpdated (); + + bool + NeedsUpdating() + { + SyncWithProcessState(); + return m_needs_update; + } + + bool + IsValid () + { + if (!m_mod_id.IsValid()) + return false; + else if (SyncWithProcessState ()) + { + if (!m_mod_id.IsValid()) + return false; + } + return true; + } + + void + SetInvalid () + { + // Use the stop id to mark us as invalid, leave the thread id and the stack id around for logging and + // history purposes. + m_mod_id.SetInvalid(); + + // Can't update an invalid state. + m_needs_update = false; + + } + + private: + bool + SyncWithProcessState (); + + ProcessModID m_mod_id; // This is the stop id when this ValueObject was last evaluated. + ExecutionContextRef m_exe_ctx_ref; + bool m_needs_update; + bool m_first_update; + }; + + const EvaluationPoint & + GetUpdatePoint () const + { + return m_update_point; + } + + EvaluationPoint & + GetUpdatePoint () + { + return m_update_point; + } + + const ExecutionContextRef & + GetExecutionContextRef() const + { + return m_update_point.GetExecutionContextRef(); + } + + lldb::TargetSP + GetTargetSP() const + { + return m_update_point.GetExecutionContextRef().GetTargetSP(); + } + + lldb::ProcessSP + GetProcessSP() const + { + return m_update_point.GetExecutionContextRef().GetProcessSP(); + } + + lldb::ThreadSP + GetThreadSP() const + { + return m_update_point.GetExecutionContextRef().GetThreadSP(); + } + + lldb::StackFrameSP + GetFrameSP() const + { + return m_update_point.GetExecutionContextRef().GetFrameSP(); + } + + void + SetNeedsUpdate (); + + virtual ~ValueObject(); + + ClangASTType + GetClangType (); + + //------------------------------------------------------------------ + // Sublasses must implement the functions below. + //------------------------------------------------------------------ + virtual uint64_t + GetByteSize() = 0; + + virtual lldb::ValueType + GetValueType() const = 0; + + //------------------------------------------------------------------ + // Sublasses can implement the functions below. + //------------------------------------------------------------------ + virtual ConstString + GetTypeName(); + + virtual ConstString + GetQualifiedTypeName(); + + virtual lldb::LanguageType + GetObjectRuntimeLanguage(); + + virtual uint32_t + GetTypeInfo (ClangASTType *pointee_or_element_clang_type = NULL); + + virtual bool + IsPointerType (); + + virtual bool + IsArrayType (); + + virtual bool + IsScalarType (); + + virtual bool + IsPointerOrReferenceType (); + + virtual bool + IsPossibleDynamicType (); + + virtual bool + IsObjCNil (); + + virtual bool + IsBaseClass () + { + return false; + } + + virtual bool + IsDereferenceOfParent () + { + return false; + } + + bool + IsIntegerType (bool &is_signed); + + virtual bool + GetBaseClassPath (Stream &s); + + virtual void + GetExpressionPath (Stream &s, bool qualify_cxx_base_classes, GetExpressionPathFormat = eGetExpressionPathFormatDereferencePointers); + + lldb::ValueObjectSP + GetValueForExpressionPath(const char* expression, + const char** first_unparsed = NULL, + ExpressionPathScanEndReason* reason_to_stop = NULL, + ExpressionPathEndResultType* final_value_type = NULL, + const GetValueForExpressionPathOptions& options = GetValueForExpressionPathOptions::DefaultOptions(), + ExpressionPathAftermath* final_task_on_target = NULL); + + int + GetValuesForExpressionPath(const char* expression, + lldb::ValueObjectListSP& list, + const char** first_unparsed = NULL, + ExpressionPathScanEndReason* reason_to_stop = NULL, + ExpressionPathEndResultType* final_value_type = NULL, + const GetValueForExpressionPathOptions& options = GetValueForExpressionPathOptions::DefaultOptions(), + ExpressionPathAftermath* final_task_on_target = NULL); + + virtual bool + IsInScope () + { + return true; + } + + virtual off_t + GetByteOffset() + { + return 0; + } + + virtual uint32_t + GetBitfieldBitSize () + { + return 0; + } + + virtual uint32_t + GetBitfieldBitOffset () + { + return 0; + } + + bool + IsBitfield () + { + return (GetBitfieldBitSize() != 0) || (GetBitfieldBitOffset() != 0); + } + + virtual bool + IsArrayItemForPointer() + { + return m_is_array_item_for_pointer; + } + + virtual const char * + GetValueAsCString (); + + virtual bool + GetValueAsCString (lldb::Format format, + std::string& destination); + + virtual uint64_t + GetValueAsUnsigned (uint64_t fail_value, bool *success = NULL); + + virtual bool + SetValueFromCString (const char *value_str, Error& error); + + // Return the module associated with this value object in case the + // value is from an executable file and might have its data in + // sections of the file. This can be used for variables. + virtual lldb::ModuleSP + GetModule(); + + virtual ValueObject* + GetRoot (); + + virtual bool + GetDeclaration (Declaration &decl); + + //------------------------------------------------------------------ + // The functions below should NOT be modified by sublasses + //------------------------------------------------------------------ + const Error & + GetError(); + + const ConstString & + GetName() const; + + virtual lldb::ValueObjectSP + GetChildAtIndex (size_t idx, bool can_create); + + // this will always create the children if necessary + lldb::ValueObjectSP + GetChildAtIndexPath (const std::initializer_list &idxs, + size_t* index_of_error = NULL); + + lldb::ValueObjectSP + GetChildAtIndexPath (const std::vector &idxs, + size_t* index_of_error = NULL); + + lldb::ValueObjectSP + GetChildAtIndexPath (const std::initializer_list< std::pair > &idxs, + size_t* index_of_error = NULL); + + lldb::ValueObjectSP + GetChildAtIndexPath (const std::vector< std::pair > &idxs, + size_t* index_of_error = NULL); + + virtual lldb::ValueObjectSP + GetChildMemberWithName (const ConstString &name, bool can_create); + + virtual size_t + GetIndexOfChildWithName (const ConstString &name); + + size_t + GetNumChildren (); + + const Value & + GetValue() const; + + Value & + GetValue(); + + virtual bool + ResolveValue (Scalar &scalar); + + virtual const char * + GetLocationAsCString (); + + const char * + GetSummaryAsCString (); + + bool + GetSummaryAsCString (TypeSummaryImpl* summary_ptr, + std::string& destination); + + const char * + GetObjectDescription (); + + bool + HasSpecialPrintableRepresentation (ValueObjectRepresentationStyle val_obj_display, + lldb::Format custom_format); + + enum PrintableRepresentationSpecialCases + { + ePrintableRepresentationSpecialCasesDisable = 0, + ePrintableRepresentationSpecialCasesAllow = 1, + ePrintableRepresentationSpecialCasesOnly = 3 + }; + + bool + DumpPrintableRepresentation (Stream& s, + ValueObjectRepresentationStyle val_obj_display = eValueObjectRepresentationStyleSummary, + lldb::Format custom_format = lldb::eFormatInvalid, + PrintableRepresentationSpecialCases special = ePrintableRepresentationSpecialCasesAllow); + bool + GetValueIsValid () const; + + bool + GetValueDidChange (); + + bool + UpdateValueIfNeeded (bool update_format = true); + + bool + UpdateFormatsIfNeeded(); + + lldb::ValueObjectSP + GetSP () + { + return m_manager->GetSharedPointer(this); + } + + void + SetName (const ConstString &name); + + virtual lldb::addr_t + GetAddressOf (bool scalar_is_load_address = true, + AddressType *address_type = NULL); + + lldb::addr_t + GetPointerValue (AddressType *address_type = NULL); + + lldb::ValueObjectSP + GetSyntheticChild (const ConstString &key) const; + + lldb::ValueObjectSP + GetSyntheticArrayMember (size_t index, bool can_create); + + lldb::ValueObjectSP + GetSyntheticArrayMemberFromPointer (size_t index, bool can_create); + + lldb::ValueObjectSP + GetSyntheticArrayMemberFromArray (size_t index, bool can_create); + + lldb::ValueObjectSP + GetSyntheticBitFieldChild (uint32_t from, uint32_t to, bool can_create); + + lldb::ValueObjectSP + GetSyntheticExpressionPathChild(const char* expression, bool can_create); + + virtual lldb::ValueObjectSP + GetSyntheticChildAtOffset(uint32_t offset, const ClangASTType& type, bool can_create); + + virtual lldb::ValueObjectSP + GetDynamicValue (lldb::DynamicValueType valueType); + + lldb::DynamicValueType + GetDynamicValueType (); + + virtual lldb::ValueObjectSP + GetStaticValue (); + + virtual lldb::ValueObjectSP + GetNonSyntheticValue (); + + lldb::ValueObjectSP + GetSyntheticValue (bool use_synthetic = true); + + virtual bool + HasSyntheticValue(); + + virtual bool + IsSynthetic() { return false; } + + virtual lldb::ValueObjectSP + CreateConstantValue (const ConstString &name); + + virtual lldb::ValueObjectSP + Dereference (Error &error); + + virtual lldb::ValueObjectSP + AddressOf (Error &error); + + virtual lldb::addr_t + GetLiveAddress() + { + return LLDB_INVALID_ADDRESS; + } + + virtual void + SetLiveAddress(lldb::addr_t addr = LLDB_INVALID_ADDRESS, + AddressType address_type = eAddressTypeLoad) + { + } + + virtual lldb::ValueObjectSP + Cast (const ClangASTType &clang_ast_type); + + virtual lldb::ValueObjectSP + CastPointerType (const char *name, + ClangASTType &ast_type); + + virtual lldb::ValueObjectSP + CastPointerType (const char *name, + lldb::TypeSP &type_sp); + + // The backing bits of this value object were updated, clear any + // descriptive string, so we know we have to refetch them + virtual void + ValueUpdated () + { + ClearUserVisibleData(eClearUserVisibleDataItemsValue | + eClearUserVisibleDataItemsSummary | + eClearUserVisibleDataItemsDescription); + } + + virtual bool + IsDynamic () + { + return false; + } + + virtual SymbolContextScope * + GetSymbolContextScope(); + + static void + DumpValueObject (Stream &s, + ValueObject *valobj); + static void + DumpValueObject (Stream &s, + ValueObject *valobj, + const DumpValueObjectOptions& options); + + static lldb::ValueObjectSP + CreateValueObjectFromExpression (const char* name, + const char* expression, + const ExecutionContext& exe_ctx); + + static lldb::ValueObjectSP + CreateValueObjectFromAddress (const char* name, + uint64_t address, + const ExecutionContext& exe_ctx, + ClangASTType type); + + static lldb::ValueObjectSP + CreateValueObjectFromData (const char* name, + DataExtractor& data, + const ExecutionContext& exe_ctx, + ClangASTType type); + + static void + LogValueObject (Log *log, + ValueObject *valobj); + + static void + LogValueObject (Log *log, + ValueObject *valobj, + const DumpValueObjectOptions& options); + + + // returns true if this is a char* or a char[] + // if it is a char* and check_pointer is true, + // it also checks that the pointer is valid + bool + IsCStringContainer (bool check_pointer = false); + + size_t + ReadPointedString (Stream& s, + Error& error, + uint32_t max_length = 0, + bool honor_array = true, + lldb::Format item_format = lldb::eFormatCharArray); + + virtual size_t + GetPointeeData (DataExtractor& data, + uint32_t item_idx = 0, + uint32_t item_count = 1); + + virtual uint64_t + GetData (DataExtractor& data); + + virtual bool + SetData (DataExtractor &data, Error &error); + + bool + GetIsConstant () const + { + return m_update_point.IsConstant(); + } + + void + SetIsConstant () + { + m_update_point.SetIsConstant(); + } + + lldb::Format + GetFormat () const; + + void + SetFormat (lldb::Format format) + { + if (format != m_format) + ClearUserVisibleData(eClearUserVisibleDataItemsValue); + m_format = format; + } + + lldb::TypeSummaryImplSP + GetSummaryFormat() + { + UpdateFormatsIfNeeded(); + return m_type_summary_sp; + } + + void + SetSummaryFormat(lldb::TypeSummaryImplSP format) + { + m_type_summary_sp = format; + ClearUserVisibleData(eClearUserVisibleDataItemsSummary); + } + + void + SetValueFormat(lldb::TypeFormatImplSP format) + { + m_type_format_sp = format; + ClearUserVisibleData(eClearUserVisibleDataItemsValue); + } + + lldb::TypeFormatImplSP + GetValueFormat() + { + UpdateFormatsIfNeeded(); + return m_type_format_sp; + } + + void + SetSyntheticChildren(const lldb::SyntheticChildrenSP &synth_sp) + { + if (synth_sp.get() == m_synthetic_children_sp.get()) + return; + ClearUserVisibleData(eClearUserVisibleDataItemsSyntheticChildren); + m_synthetic_children_sp = synth_sp; + } + + lldb::SyntheticChildrenSP + GetSyntheticChildren() + { + UpdateFormatsIfNeeded(); + return m_synthetic_children_sp; + } + + // Use GetParent for display purposes, but if you want to tell the parent to update itself + // then use m_parent. The ValueObjectDynamicValue's parent is not the correct parent for + // displaying, they are really siblings, so for display it needs to route through to its grandparent. + virtual ValueObject * + GetParent() + { + return m_parent; + } + + virtual const ValueObject * + GetParent() const + { + return m_parent; + } + + ValueObject * + GetNonBaseClassParent(); + + void + SetAddressTypeOfChildren(AddressType at) + { + m_address_type_of_ptr_or_ref_children = at; + } + + AddressType + GetAddressTypeOfChildren(); + + void + SetHasCompleteType() + { + m_did_calculate_complete_objc_class_type = true; + } + + //------------------------------------------------------------------ + /// Find out if a ValueObject might have children. + /// + /// This call is much more efficient than CalculateNumChildren() as + /// it doesn't need to complete the underlying type. This is designed + /// to be used in a UI environment in order to detect if the + /// disclosure triangle should be displayed or not. + /// + /// This function returns true for class, union, structure, + /// pointers, references, arrays and more. Again, it does so without + /// doing any expensive type completion. + /// + /// @return + /// Returns \b true if the ValueObject might have children, or \b + /// false otherwise. + //------------------------------------------------------------------ + virtual bool + MightHaveChildren(); + +protected: + typedef ClusterManager ValueObjectManager; + + class ChildrenManager + { + public: + ChildrenManager() : + m_mutex(Mutex::eMutexTypeRecursive), + m_children(), + m_children_count(0) + {} + + bool + HasChildAtIndex (size_t idx) + { + Mutex::Locker locker(m_mutex); + ChildrenIterator iter = m_children.find(idx); + ChildrenIterator end = m_children.end(); + return (iter != end); + } + + ValueObject* + GetChildAtIndex (size_t idx) + { + Mutex::Locker locker(m_mutex); + ChildrenIterator iter = m_children.find(idx); + ChildrenIterator end = m_children.end(); + if (iter == end) + return NULL; + else + return iter->second; + } + + void + SetChildAtIndex (size_t idx, ValueObject* valobj) + { + ChildrenPair pair(idx,valobj); // we do not need to be mutex-protected to make a pair + Mutex::Locker locker(m_mutex); + m_children.insert(pair); + } + + void + SetChildrenCount (size_t count) + { + m_children_count = count; + } + + size_t + GetChildrenCount () + { + return m_children_count; + } + + void + Clear() + { + m_children_count = 0; + Mutex::Locker locker(m_mutex); + m_children.clear(); + } + + private: + typedef std::map ChildrenMap; + typedef ChildrenMap::iterator ChildrenIterator; + typedef ChildrenMap::value_type ChildrenPair; + Mutex m_mutex; + ChildrenMap m_children; + size_t m_children_count; + }; + + //------------------------------------------------------------------ + // Classes that inherit from ValueObject can see and modify these + //------------------------------------------------------------------ + ValueObject * m_parent; // The parent value object, or NULL if this has no parent + ValueObject * m_root; // The root of the hierarchy for this ValueObject (or NULL if never calculated) + EvaluationPoint m_update_point; // Stores both the stop id and the full context at which this value was last + // updated. When we are asked to update the value object, we check whether + // the context & stop id are the same before updating. + ConstString m_name; // The name of this object + DataExtractor m_data; // A data extractor that can be used to extract the value. + Value m_value; + Error m_error; // An error object that can describe any errors that occur when updating values. + std::string m_value_str; // Cached value string that will get cleared if/when the value is updated. + std::string m_old_value_str;// Cached old value string from the last time the value was gotten + std::string m_location_str; // Cached location string that will get cleared if/when the value is updated. + std::string m_summary_str; // Cached summary string that will get cleared if/when the value is updated. + std::string m_object_desc_str; // Cached result of the "object printer". This differs from the summary + // in that the summary is consed up by us, the object_desc_string is builtin. + + ClangASTType m_override_type;// If the type of the value object should be overridden, the type to impose. + + ValueObjectManager *m_manager; // This object is managed by the root object (any ValueObject that gets created + // without a parent.) The manager gets passed through all the generations of + // dependent objects, and will keep the whole cluster of objects alive as long + // as a shared pointer to any of them has been handed out. Shared pointers to + // value objects must always be made with the GetSP method. + + ChildrenManager m_children; + std::map m_synthetic_children; + + ValueObject* m_dynamic_value; + ValueObject* m_synthetic_value; + ValueObject* m_deref_valobj; + + lldb::ValueObjectSP m_addr_of_valobj_sp; // We have to hold onto a shared pointer to this one because it is created + // as an independent ValueObjectConstResult, which isn't managed by us. + + lldb::Format m_format; + lldb::Format m_last_format; + uint32_t m_last_format_mgr_revision; + lldb::TypeSummaryImplSP m_type_summary_sp; + lldb::TypeFormatImplSP m_type_format_sp; + lldb::SyntheticChildrenSP m_synthetic_children_sp; + ProcessModID m_user_id_of_forced_summary; + AddressType m_address_type_of_ptr_or_ref_children; + + bool m_value_is_valid:1, + m_value_did_change:1, + m_children_count_valid:1, + m_old_value_valid:1, + m_is_deref_of_parent:1, + m_is_array_item_for_pointer:1, + m_is_bitfield_for_scalar:1, + m_is_child_at_offset:1, + m_is_getting_summary:1, + m_did_calculate_complete_objc_class_type:1; + + friend class ClangExpressionDeclMap; // For GetValue + friend class ClangExpressionVariable; // For SetName + friend class Target; // For SetName + friend class ValueObjectConstResultImpl; + + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + + // Use the no-argument constructor to make a constant variable object (with no ExecutionContextScope.) + + ValueObject(); + + // Use this constructor to create a "root variable object". The ValueObject will be locked to this context + // through-out its lifespan. + + ValueObject (ExecutionContextScope *exe_scope, + AddressType child_ptr_or_ref_addr_type = eAddressTypeLoad); + + // Use this constructor to create a ValueObject owned by another ValueObject. It will inherit the ExecutionContext + // of its parent. + + ValueObject (ValueObject &parent); + + ValueObjectManager * + GetManager() + { + return m_manager; + } + + virtual bool + UpdateValue () = 0; + + virtual void + CalculateDynamicValue (lldb::DynamicValueType use_dynamic); + + virtual lldb::DynamicValueType + GetDynamicValueTypeImpl () + { + return lldb::eNoDynamicValues; + } + + virtual bool + HasDynamicValueTypeInfo () + { + return false; + } + + virtual void + CalculateSyntheticValue (bool use_synthetic = true); + + // Should only be called by ValueObject::GetChildAtIndex() + // Returns a ValueObject managed by this ValueObject's manager. + virtual ValueObject * + CreateChildAtIndex (size_t idx, bool synthetic_array_member, int32_t synthetic_index); + + // Should only be called by ValueObject::GetNumChildren() + virtual size_t + CalculateNumChildren() = 0; + + void + SetNumChildren (size_t num_children); + + void + SetValueDidChange (bool value_changed); + + void + SetValueIsValid (bool valid); + + void + ClearUserVisibleData(uint32_t items = ValueObject::eClearUserVisibleDataItemsAllStrings); + + void + AddSyntheticChild (const ConstString &key, + ValueObject *valobj); + + DataExtractor & + GetDataExtractor (); + + void + ClearDynamicTypeInformation (); + + //------------------------------------------------------------------ + // Sublasses must implement the functions below. + //------------------------------------------------------------------ + + virtual ClangASTType + GetClangTypeImpl () = 0; + + const char * + GetLocationAsCStringImpl (const Value& value, + const DataExtractor& data); + +private: + //------------------------------------------------------------------ + // For ValueObject only + //------------------------------------------------------------------ + + virtual ClangASTType + MaybeCalculateCompleteType (); + + lldb::ValueObjectSP + GetValueForExpressionPath_Impl(const char* expression_cstr, + const char** first_unparsed, + ExpressionPathScanEndReason* reason_to_stop, + ExpressionPathEndResultType* final_value_type, + const GetValueForExpressionPathOptions& options, + ExpressionPathAftermath* final_task_on_target); + + // this method will ONLY expand [] expressions into a VOList and return + // the number of elements it added to the VOList + // it will NOT loop through expanding the follow-up of the expression_cstr + // for all objects in the list + int + ExpandArraySliceExpression(const char* expression_cstr, + const char** first_unparsed, + lldb::ValueObjectSP root, + lldb::ValueObjectListSP& list, + ExpressionPathScanEndReason* reason_to_stop, + ExpressionPathEndResultType* final_value_type, + const GetValueForExpressionPathOptions& options, + ExpressionPathAftermath* final_task_on_target); + + + DISALLOW_COPY_AND_ASSIGN (ValueObject); + +}; + +} // namespace lldb_private + +#endif // liblldb_ValueObject_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Core/ValueObjectCast.h b/contrib/llvm/tools/lldb/include/lldb/Core/ValueObjectCast.h new file mode 100644 index 00000000000..1538d7a5563 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Core/ValueObjectCast.h @@ -0,0 +1,87 @@ +//===-- ValueObjectDynamicValue.h -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ValueObjectCast_h_ +#define liblldb_ValueObjectCast_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/ValueObject.h" + +namespace lldb_private { + +//--------------------------------------------------------------------------------- +// A ValueObject that represents a given value represented as a different type. +//--------------------------------------------------------------------------------- +class ValueObjectCast : public ValueObject +{ +public: + static lldb::ValueObjectSP + Create (ValueObject &parent, + const ConstString &name, + const ClangASTType &cast_type); + + virtual + ~ValueObjectCast(); + + virtual uint64_t + GetByteSize(); + + virtual size_t + CalculateNumChildren(); + + virtual lldb::ValueType + GetValueType() const; + + virtual bool + IsInScope (); + + virtual ValueObject * + GetParent() + { + if (m_parent) + return m_parent->GetParent(); + else + return NULL; + } + + virtual const ValueObject * + GetParent() const + { + if (m_parent) + return m_parent->GetParent(); + else + return NULL; + } + +protected: + virtual bool + UpdateValue (); + + virtual ClangASTType + GetClangTypeImpl (); + + ClangASTType m_cast_type; + +private: + ValueObjectCast (ValueObject &parent, + const ConstString &name, + const ClangASTType &cast_type); + + //------------------------------------------------------------------ + // For ValueObject only + //------------------------------------------------------------------ + DISALLOW_COPY_AND_ASSIGN (ValueObjectCast); +}; + +} // namespace lldb_private + +#endif // liblldb_ValueObjectCast_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Core/ValueObjectChild.h b/contrib/llvm/tools/lldb/include/lldb/Core/ValueObjectChild.h new file mode 100644 index 00000000000..780529a4af1 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Core/ValueObjectChild.h @@ -0,0 +1,122 @@ +//===-- ValueObjectChild.h --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ValueObjectChild_h_ +#define liblldb_ValueObjectChild_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/ValueObject.h" + +namespace lldb_private { + +//---------------------------------------------------------------------- +// A child of another ValueObject. +//---------------------------------------------------------------------- +class ValueObjectChild : public ValueObject +{ +public: + virtual ~ValueObjectChild(); + + virtual uint64_t + GetByteSize() + { + return m_byte_size; + } + + virtual off_t + GetByteOffset() + { + return m_byte_offset; + } + + virtual uint32_t + GetBitfieldBitSize() + { + return m_bitfield_bit_size; + } + + virtual uint32_t + GetBitfieldBitOffset() + { + return m_bitfield_bit_offset; + } + + virtual lldb::ValueType + GetValueType() const; + + virtual size_t + CalculateNumChildren(); + + virtual ConstString + GetTypeName(); + + virtual ConstString + GetQualifiedTypeName(); + + virtual bool + IsInScope (); + + virtual bool + IsBaseClass () + { + return m_is_base_class; + } + + virtual bool + IsDereferenceOfParent () + { + return m_is_deref_of_parent; + } + +protected: + virtual bool + UpdateValue (); + + virtual ClangASTType + GetClangTypeImpl () + { + return m_clang_type; + } + + ClangASTType m_clang_type; + ConstString m_type_name; + uint64_t m_byte_size; + int32_t m_byte_offset; + uint8_t m_bitfield_bit_size; + uint8_t m_bitfield_bit_offset; + bool m_is_base_class; + bool m_is_deref_of_parent; + +// +// void +// ReadValueFromMemory (ValueObject* parent, lldb::addr_t address); + +protected: + friend class ValueObject; + friend class ValueObjectConstResult; + ValueObjectChild (ValueObject &parent, + const ClangASTType &clang_type, + const ConstString &name, + uint64_t byte_size, + int32_t byte_offset, + uint32_t bitfield_bit_size, + uint32_t bitfield_bit_offset, + bool is_base_class, + bool is_deref_of_parent, + AddressType child_ptr_or_ref_addr_type); + + DISALLOW_COPY_AND_ASSIGN (ValueObjectChild); +}; + +} // namespace lldb_private + +#endif // liblldb_ValueObjectChild_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Core/ValueObjectConstResult.h b/contrib/llvm/tools/lldb/include/lldb/Core/ValueObjectConstResult.h new file mode 100644 index 00000000000..4964d0589a0 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Core/ValueObjectConstResult.h @@ -0,0 +1,179 @@ +//===-- ValueObjectConstResult.h --------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ValueObjectConstResult_h_ +#define liblldb_ValueObjectConstResult_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/ValueObject.h" + +#include "lldb/Core/ValueObjectConstResultImpl.h" + +namespace lldb_private { + +//---------------------------------------------------------------------- +// A frozen ValueObject copied into host memory +//---------------------------------------------------------------------- +class ValueObjectConstResult : public ValueObject +{ +public: + static lldb::ValueObjectSP + Create (ExecutionContextScope *exe_scope, + lldb::ByteOrder byte_order, + uint32_t addr_byte_size, + lldb::addr_t address = LLDB_INVALID_ADDRESS); + + static lldb::ValueObjectSP + Create (ExecutionContextScope *exe_scope, + const ClangASTType &clang_type, + const ConstString &name, + const DataExtractor &data, + lldb::addr_t address = LLDB_INVALID_ADDRESS); + + static lldb::ValueObjectSP + Create (ExecutionContextScope *exe_scope, + const ClangASTType &clang_type, + const ConstString &name, + const lldb::DataBufferSP &result_data_sp, + lldb::ByteOrder byte_order, + uint32_t addr_size, + lldb::addr_t address = LLDB_INVALID_ADDRESS); + + static lldb::ValueObjectSP + Create (ExecutionContextScope *exe_scope, + const ClangASTType &clang_type, + const ConstString &name, + lldb::addr_t address, + AddressType address_type, + uint32_t addr_byte_size); + + static lldb::ValueObjectSP + Create (ExecutionContextScope *exe_scope, + Value &value, + const ConstString &name); + + // When an expression fails to evaluate, we return an error + static lldb::ValueObjectSP + Create (ExecutionContextScope *exe_scope, + const Error& error); + + virtual ~ValueObjectConstResult(); + + virtual uint64_t + GetByteSize(); + + virtual lldb::ValueType + GetValueType() const; + + virtual size_t + CalculateNumChildren(); + + virtual ConstString + GetTypeName(); + + virtual bool + IsInScope (); + + void + SetByteSize (size_t size); + + virtual lldb::ValueObjectSP + Dereference (Error &error); + + virtual ValueObject * + CreateChildAtIndex (size_t idx, bool synthetic_array_member, int32_t synthetic_index); + + virtual lldb::ValueObjectSP + GetSyntheticChildAtOffset(uint32_t offset, const ClangASTType& type, bool can_create); + + virtual lldb::ValueObjectSP + AddressOf (Error &error); + + virtual lldb::addr_t + GetAddressOf (bool scalar_is_load_address = true, + AddressType *address_type = NULL); + + virtual size_t + GetPointeeData (DataExtractor& data, + uint32_t item_idx = 0, + uint32_t item_count = 1); + + virtual lldb::addr_t + GetLiveAddress() + { + return m_impl.GetLiveAddress(); + } + + virtual void + SetLiveAddress(lldb::addr_t addr = LLDB_INVALID_ADDRESS, + AddressType address_type = eAddressTypeLoad) + { + m_impl.SetLiveAddress(addr, + address_type); + } + + virtual lldb::ValueObjectSP + GetDynamicValue (lldb::DynamicValueType valueType); + +protected: + virtual bool + UpdateValue (); + + virtual ClangASTType + GetClangTypeImpl (); + + ConstString m_type_name; + uint64_t m_byte_size; + + ValueObjectConstResultImpl m_impl; + +private: + friend class ValueObjectConstResultImpl; + ValueObjectConstResult (ExecutionContextScope *exe_scope, + lldb::ByteOrder byte_order, + uint32_t addr_byte_size, + lldb::addr_t address); + + ValueObjectConstResult (ExecutionContextScope *exe_scope, + const ClangASTType &clang_type, + const ConstString &name, + const DataExtractor &data, + lldb::addr_t address); + + ValueObjectConstResult (ExecutionContextScope *exe_scope, + const ClangASTType &clang_type, + const ConstString &name, + const lldb::DataBufferSP &result_data_sp, + lldb::ByteOrder byte_order, + uint32_t addr_size, + lldb::addr_t address); + + ValueObjectConstResult (ExecutionContextScope *exe_scope, + const ClangASTType &clang_type, + const ConstString &name, + lldb::addr_t address, + AddressType address_type, + uint32_t addr_byte_size); + + ValueObjectConstResult (ExecutionContextScope *exe_scope, + const Value &value, + const ConstString &name); + + ValueObjectConstResult (ExecutionContextScope *exe_scope, + const Error& error); + + DISALLOW_COPY_AND_ASSIGN (ValueObjectConstResult); +}; + +} // namespace lldb_private + +#endif // liblldb_ValueObjectConstResult_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Core/ValueObjectConstResultChild.h b/contrib/llvm/tools/lldb/include/lldb/Core/ValueObjectConstResultChild.h new file mode 100644 index 00000000000..9063276b019 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Core/ValueObjectConstResultChild.h @@ -0,0 +1,77 @@ +//===-- ValueObjectConstResultChild.h -------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ValueObjectConstResultChild_h_ +#define liblldb_ValueObjectConstResultChild_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/ValueObjectChild.h" +#include "lldb/Core/ValueObjectConstResultImpl.h" + +namespace lldb_private { + +//---------------------------------------------------------------------- +// A child of a ValueObjectConstResult. +//---------------------------------------------------------------------- +class ValueObjectConstResultChild : public ValueObjectChild +{ +public: + + ValueObjectConstResultChild (ValueObject &parent, + const ClangASTType &clang_type, + const ConstString &name, + uint32_t byte_size, + int32_t byte_offset, + uint32_t bitfield_bit_size, + uint32_t bitfield_bit_offset, + bool is_base_class, + bool is_deref_of_parent); + + virtual ~ValueObjectConstResultChild(); + + virtual lldb::ValueObjectSP + Dereference (Error &error); + + virtual ValueObject * + CreateChildAtIndex (size_t idx, bool synthetic_array_member, int32_t synthetic_index); + + virtual ClangASTType + GetClangType () + { + return ValueObjectChild::GetClangType(); + } + + virtual lldb::ValueObjectSP + GetSyntheticChildAtOffset(uint32_t offset, const ClangASTType& type, bool can_create); + + virtual lldb::ValueObjectSP + AddressOf (Error &error); + + virtual size_t + GetPointeeData (DataExtractor& data, + uint32_t item_idx = 0, + uint32_t item_count = 1); + +protected: + ValueObjectConstResultImpl m_impl; + +private: + friend class ValueObject; + friend class ValueObjectConstResult; + friend class ValueObjectConstResultImpl; + + DISALLOW_COPY_AND_ASSIGN (ValueObjectConstResultChild); +}; + +} // namespace lldb_private + +#endif // liblldb_ValueObjectConstResultChild_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Core/ValueObjectConstResultImpl.h b/contrib/llvm/tools/lldb/include/lldb/Core/ValueObjectConstResultImpl.h new file mode 100644 index 00000000000..271b9382356 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Core/ValueObjectConstResultImpl.h @@ -0,0 +1,96 @@ +//===-- ValueObjectConstResultImpl.h -----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ValueObjectConstResultImpl_h_ +#define liblldb_ValueObjectConstResultImpl_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/ValueObject.h" + +namespace lldb_private { + +//---------------------------------------------------------------------- +// A class wrapping common implementation details for operations in +// ValueObjectConstResult ( & Child ) that may need to jump from the host +// memory space into the target's memory space +//---------------------------------------------------------------------- +class ValueObjectConstResultImpl +{ +public: + + ValueObjectConstResultImpl (ValueObject* valobj, + lldb::addr_t live_address = LLDB_INVALID_ADDRESS); + + virtual + ~ValueObjectConstResultImpl() + { + } + + lldb::ValueObjectSP + Dereference (Error &error); + + ValueObject * + CreateChildAtIndex (size_t idx, bool synthetic_array_member, int32_t synthetic_index); + + lldb::ValueObjectSP + GetSyntheticChildAtOffset (uint32_t offset, const ClangASTType& type, bool can_create); + + lldb::ValueObjectSP + AddressOf (Error &error); + + bool + NeedsDerefOnTarget() + { + m_impl_backend->UpdateValueIfNeeded(false); + return (m_impl_backend->GetValue().GetValueType() == Value::eValueTypeHostAddress); + } + + lldb::addr_t + GetLiveAddress() + { + return m_live_address; + } + + void + SetLiveAddress(lldb::addr_t addr = LLDB_INVALID_ADDRESS, + AddressType address_type = eAddressTypeLoad) + { + m_live_address = addr; + m_live_address_type = address_type; + } + + lldb::ValueObjectSP + DerefOnTarget(); + + virtual lldb::addr_t + GetAddressOf (bool scalar_is_load_address = true, + AddressType *address_type = NULL); + + virtual size_t + GetPointeeData (DataExtractor& data, + uint32_t item_idx = 0, + uint32_t item_count = 1); + +private: + + ValueObject *m_impl_backend; + lldb::addr_t m_live_address; + AddressType m_live_address_type; + lldb::ValueObjectSP m_load_addr_backend; + lldb::ValueObjectSP m_address_of_backend; + + DISALLOW_COPY_AND_ASSIGN (ValueObjectConstResultImpl); +}; + +} // namespace lldb_private + +#endif // liblldb_ValueObjectConstResultImpl_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Core/ValueObjectDynamicValue.h b/contrib/llvm/tools/lldb/include/lldb/Core/ValueObjectDynamicValue.h new file mode 100644 index 00000000000..c0f6baade3f --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Core/ValueObjectDynamicValue.h @@ -0,0 +1,133 @@ +//===-- ValueObjectDynamicValue.h -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ValueObjectDynamicValue_h_ +#define liblldb_ValueObjectDynamicValue_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/ValueObject.h" +#include "lldb/Symbol/Type.h" + +namespace lldb_private { + +//---------------------------------------------------------------------- +// A ValueObject that represents memory at a given address, viewed as some +// set lldb type. +//---------------------------------------------------------------------- +class ValueObjectDynamicValue : public ValueObject +{ +public: + virtual + ~ValueObjectDynamicValue(); + + virtual uint64_t + GetByteSize(); + + virtual ConstString + GetTypeName(); + + virtual ConstString + GetQualifiedTypeName(); + + virtual size_t + CalculateNumChildren(); + + virtual lldb::ValueType + GetValueType() const; + + virtual bool + IsInScope (); + + virtual bool + IsDynamic () + { + return true; + } + + virtual ValueObject * + GetParent() + { + if (m_parent) + return m_parent->GetParent(); + else + return NULL; + } + + virtual const ValueObject * + GetParent() const + { + if (m_parent) + return m_parent->GetParent(); + else + return NULL; + } + + virtual lldb::ValueObjectSP + GetStaticValue () + { + return m_parent->GetSP(); + } + + void + SetOwningSP (lldb::ValueObjectSP &owning_sp) + { + if (m_owning_valobj_sp == owning_sp) + return; + + assert (m_owning_valobj_sp.get() == NULL); + m_owning_valobj_sp = owning_sp; + } + + virtual bool + SetValueFromCString (const char *value_str, Error& error); + + virtual bool + SetData (DataExtractor &data, Error &error); + +protected: + virtual bool + UpdateValue (); + + virtual lldb::DynamicValueType + GetDynamicValueTypeImpl () + { + return m_use_dynamic; + } + + virtual bool + HasDynamicValueTypeInfo () + { + return true; + } + + virtual ClangASTType + GetClangTypeImpl (); + + Address m_address; ///< The variable that this value object is based upon + TypeAndOrName m_dynamic_type_info; // We can have a type_sp or just a name + lldb::ValueObjectSP m_owning_valobj_sp; + lldb::DynamicValueType m_use_dynamic; + +private: + friend class ValueObject; + friend class ValueObjectConstResult; + ValueObjectDynamicValue (ValueObject &parent, lldb::DynamicValueType use_dynamic); + + //------------------------------------------------------------------ + // For ValueObject only + //------------------------------------------------------------------ + DISALLOW_COPY_AND_ASSIGN (ValueObjectDynamicValue); +}; + +} // namespace lldb_private + +#endif // liblldb_ValueObjectDynamicValue_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Core/ValueObjectList.h b/contrib/llvm/tools/lldb/include/lldb/Core/ValueObjectList.h new file mode 100644 index 00000000000..5bfe40b2e95 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Core/ValueObjectList.h @@ -0,0 +1,90 @@ +//===-- ValueObjectList.h ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ValueObjectList_h_ +#define liblldb_ValueObjectList_h_ + +// C Includes +// C++ Includes +#include + +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private.h" +#include "lldb/Core/ClangForward.h" +#include "lldb/Core/UserID.h" +#include "lldb/Target/ExecutionContextScope.h" + +namespace lldb_private { + +//---------------------------------------------------------------------- +// A collection of ValueObject values that +//---------------------------------------------------------------------- +class ValueObjectList +{ +public: + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + ValueObjectList (); + + ValueObjectList (const ValueObjectList &rhs); + + ~ValueObjectList(); + + const ValueObjectList & + operator = (const ValueObjectList &rhs); + + void + Append (const lldb::ValueObjectSP &val_obj_sp); + + void + Append (const ValueObjectList &valobj_list); + + lldb::ValueObjectSP + FindValueObjectByPointer (ValueObject *valobj); + + size_t + GetSize () const; + + void + Resize (size_t size); + + lldb::ValueObjectSP + GetValueObjectAtIndex (size_t idx); + + lldb::ValueObjectSP + RemoveValueObjectAtIndex (size_t idx); + + void + SetValueObjectAtIndex (size_t idx, + const lldb::ValueObjectSP &valobj_sp); + + lldb::ValueObjectSP + FindValueObjectByValueName (const char *name); + + lldb::ValueObjectSP + FindValueObjectByUID (lldb::user_id_t uid); + + void + Swap (ValueObjectList &value_object_list); + +protected: + typedef std::vector collection; + //------------------------------------------------------------------ + // Classes that inherit from ValueObjectList can see and modify these + //------------------------------------------------------------------ + collection m_value_objects; + +}; + + +} // namespace lldb_private + +#endif // liblldb_ValueObjectList_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Core/ValueObjectMemory.h b/contrib/llvm/tools/lldb/include/lldb/Core/ValueObjectMemory.h new file mode 100644 index 00000000000..627d73eb4b2 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Core/ValueObjectMemory.h @@ -0,0 +1,91 @@ +//===-- ValueObjectMemory.h -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ValueObjectMemory_h_ +#define liblldb_ValueObjectMemory_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/ValueObject.h" +#include "lldb/Symbol/ClangASTType.h" + +namespace lldb_private { + +//---------------------------------------------------------------------- +// A ValueObject that represents memory at a given address, viewed as some +// set lldb type. +//---------------------------------------------------------------------- +class ValueObjectMemory : public ValueObject +{ +public: + static lldb::ValueObjectSP + Create (ExecutionContextScope *exe_scope, + const char *name, + const Address &address, + lldb::TypeSP &type_sp); + + static lldb::ValueObjectSP + Create (ExecutionContextScope *exe_scope, + const char *name, + const Address &address, + const ClangASTType &ast_type); + + virtual + ~ValueObjectMemory(); + + virtual uint64_t + GetByteSize(); + + virtual ConstString + GetTypeName(); + + virtual size_t + CalculateNumChildren(); + + virtual lldb::ValueType + GetValueType() const; + + virtual bool + IsInScope (); + + virtual lldb::ModuleSP + GetModule(); + +protected: + virtual bool + UpdateValue (); + + virtual ClangASTType + GetClangTypeImpl (); + + Address m_address; ///< The variable that this value object is based upon + lldb::TypeSP m_type_sp; + ClangASTType m_clang_type; + +private: + ValueObjectMemory (ExecutionContextScope *exe_scope, + const char *name, + const Address &address, + lldb::TypeSP &type_sp); + + ValueObjectMemory (ExecutionContextScope *exe_scope, + const char *name, + const Address &address, + const ClangASTType &ast_type); + //------------------------------------------------------------------ + // For ValueObject only + //------------------------------------------------------------------ + DISALLOW_COPY_AND_ASSIGN (ValueObjectMemory); +}; + +} // namespace lldb_private + +#endif // liblldb_ValueObjectMemory_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Core/ValueObjectRegister.h b/contrib/llvm/tools/lldb/include/lldb/Core/ValueObjectRegister.h new file mode 100644 index 00000000000..6820629f08e --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Core/ValueObjectRegister.h @@ -0,0 +1,195 @@ +//===-- ValueObjectRegister.h -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ValueObjectRegister_h_ +#define liblldb_ValueObjectRegister_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private.h" +#include "lldb/Core/RegisterValue.h" +#include "lldb/Core/ValueObject.h" + +namespace lldb_private { + +//---------------------------------------------------------------------- +// A ValueObject that contains a root variable that may or may not +// have children. +//---------------------------------------------------------------------- +class ValueObjectRegisterContext : public ValueObject +{ +public: + + virtual + ~ValueObjectRegisterContext(); + + virtual uint64_t + GetByteSize(); + + virtual lldb::ValueType + GetValueType () const + { + return lldb::eValueTypeRegisterSet; + } + + virtual ConstString + GetTypeName(); + + virtual ConstString + GetQualifiedTypeName(); + + virtual size_t + CalculateNumChildren(); + + virtual ValueObject * + CreateChildAtIndex (size_t idx, bool synthetic_array_member, int32_t synthetic_index); + +protected: + virtual bool + UpdateValue (); + + virtual ClangASTType + GetClangTypeImpl (); + + lldb::RegisterContextSP m_reg_ctx_sp; + +private: + ValueObjectRegisterContext (ValueObject &parent, lldb::RegisterContextSP ®_ctx_sp); + //------------------------------------------------------------------ + // For ValueObject only + //------------------------------------------------------------------ + DISALLOW_COPY_AND_ASSIGN (ValueObjectRegisterContext); +}; + +class ValueObjectRegisterSet : public ValueObject +{ +public: + static lldb::ValueObjectSP + Create (ExecutionContextScope *exe_scope, lldb::RegisterContextSP ®_ctx_sp, uint32_t set_idx); + + virtual + ~ValueObjectRegisterSet(); + + virtual uint64_t + GetByteSize(); + + virtual lldb::ValueType + GetValueType () const + { + return lldb::eValueTypeRegisterSet; + } + + virtual ConstString + GetTypeName(); + + virtual ConstString + GetQualifiedTypeName(); + + virtual size_t + CalculateNumChildren(); + + virtual ValueObject * + CreateChildAtIndex (size_t idx, bool synthetic_array_member, int32_t synthetic_index); + + virtual lldb::ValueObjectSP + GetChildMemberWithName (const ConstString &name, bool can_create); + + virtual size_t + GetIndexOfChildWithName (const ConstString &name); + + +protected: + virtual bool + UpdateValue (); + + virtual ClangASTType + GetClangTypeImpl (); + + lldb::RegisterContextSP m_reg_ctx_sp; + const RegisterSet *m_reg_set; + uint32_t m_reg_set_idx; + +private: + friend class ValueObjectRegisterContext; + ValueObjectRegisterSet (ExecutionContextScope *exe_scope, lldb::RegisterContextSP ®_ctx_sp, uint32_t set_idx); + + //------------------------------------------------------------------ + // For ValueObject only + //------------------------------------------------------------------ + DISALLOW_COPY_AND_ASSIGN (ValueObjectRegisterSet); +}; + +class ValueObjectRegister : public ValueObject +{ +public: + static lldb::ValueObjectSP + Create (ExecutionContextScope *exe_scope, lldb::RegisterContextSP ®_ctx_sp, uint32_t reg_num); + + virtual + ~ValueObjectRegister(); + + virtual uint64_t + GetByteSize(); + + virtual lldb::ValueType + GetValueType () const + { + return lldb::eValueTypeRegister; + } + + virtual ConstString + GetTypeName(); + + virtual size_t + CalculateNumChildren(); + + virtual bool + SetValueFromCString (const char *value_str, Error& error); + + virtual bool + SetData (DataExtractor &data, Error &error); + + virtual bool + ResolveValue (Scalar &scalar); + + virtual void + GetExpressionPath (Stream &s, bool qualify_cxx_base_classes, GetExpressionPathFormat epformat = eGetExpressionPathFormatDereferencePointers); + +protected: + virtual bool + UpdateValue (); + + virtual ClangASTType + GetClangTypeImpl (); + + lldb::RegisterContextSP m_reg_ctx_sp; + RegisterInfo m_reg_info; + RegisterValue m_reg_value; + ConstString m_type_name; + ClangASTType m_clang_type; + +private: + void + ConstructObject (uint32_t reg_num); + + friend class ValueObjectRegisterSet; + ValueObjectRegister (ValueObject &parent, lldb::RegisterContextSP ®_ctx_sp, uint32_t reg_num); + ValueObjectRegister (ExecutionContextScope *exe_scope, lldb::RegisterContextSP ®_ctx_sp, uint32_t reg_num); + + //------------------------------------------------------------------ + // For ValueObject only + //------------------------------------------------------------------ + DISALLOW_COPY_AND_ASSIGN (ValueObjectRegister); +}; + +} // namespace lldb_private + +#endif // liblldb_ValueObjectRegister_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Core/ValueObjectSyntheticFilter.h b/contrib/llvm/tools/lldb/include/lldb/Core/ValueObjectSyntheticFilter.h new file mode 100644 index 00000000000..f1d8c885c25 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Core/ValueObjectSyntheticFilter.h @@ -0,0 +1,182 @@ +//===-- ValueObjectSyntheticFilter.h -------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ValueObjectSyntheticFilter_h_ +#define liblldb_ValueObjectSyntheticFilter_h_ + +// C Includes +// C++ Includes +#include +#include +// Other libraries and framework includes +// Project includes +#include "lldb/Core/ValueObject.h" + +namespace lldb_private { + +//---------------------------------------------------------------------- +// A ValueObject that obtains its children from some source other than +// real information +// This is currently used to implement Python-based children and filters +// but you can bind it to any source of synthetic information and have +// it behave accordingly +//---------------------------------------------------------------------- +class ValueObjectSynthetic : public ValueObject +{ +public: + virtual + ~ValueObjectSynthetic(); + + virtual uint64_t + GetByteSize(); + + virtual ConstString + GetTypeName(); + + virtual ConstString + GetQualifiedTypeName(); + + virtual bool + MightHaveChildren(); + + virtual size_t + CalculateNumChildren(); + + virtual lldb::ValueType + GetValueType() const; + + virtual lldb::ValueObjectSP + GetChildAtIndex (size_t idx, bool can_create); + + virtual lldb::ValueObjectSP + GetChildMemberWithName (const ConstString &name, bool can_create); + + virtual size_t + GetIndexOfChildWithName (const ConstString &name); + + virtual lldb::ValueObjectSP + GetDynamicValue (lldb::DynamicValueType valueType); + + virtual bool + IsInScope (); + + virtual bool + HasSyntheticValue() + { + return false; + } + + virtual bool + IsSynthetic() { return true; } + + virtual void + CalculateSyntheticValue (bool use_synthetic) + { + } + + virtual bool + IsDynamic () + { + if (m_parent) + return m_parent->IsDynamic(); + else + return false; + } + + virtual lldb::ValueObjectSP + GetStaticValue () + { + if (m_parent) + return m_parent->GetStaticValue(); + else + return GetSP(); + } + + virtual lldb::DynamicValueType + GetDynamicValueType () + { + if (m_parent) + return m_parent->GetDynamicValueType(); + else + return lldb::eNoDynamicValues; + } + + virtual ValueObject * + GetParent() + { + if (m_parent) + return m_parent->GetParent(); + else + return NULL; + } + + virtual const ValueObject * + GetParent() const + { + if (m_parent) + return m_parent->GetParent(); + else + return NULL; + } + + virtual lldb::ValueObjectSP + GetNonSyntheticValue (); + + virtual bool + ResolveValue (Scalar &scalar) + { + if (m_parent) + return m_parent->ResolveValue(scalar); + return false; + } + +protected: + virtual bool + UpdateValue (); + + virtual ClangASTType + GetClangTypeImpl (); + + virtual void + CreateSynthFilter (); + + // we need to hold on to the SyntheticChildren because someone might delete the type binding while we are alive + lldb::SyntheticChildrenSP m_synth_sp; + std::unique_ptr m_synth_filter_ap; + + typedef std::map ByIndexMap; + typedef std::map NameToIndexMap; + + typedef ByIndexMap::iterator ByIndexIterator; + typedef NameToIndexMap::iterator NameToIndexIterator; + + ByIndexMap m_children_byindex; + NameToIndexMap m_name_toindex; + uint32_t m_synthetic_children_count; // FIXME use the ValueObject's ChildrenManager instead of a special purpose solution + + ConstString m_parent_type_name; + + LazyBool m_might_have_children; + +private: + friend class ValueObject; + ValueObjectSynthetic (ValueObject &parent, lldb::SyntheticChildrenSP filter); + + void + CopyParentData (); + + //------------------------------------------------------------------ + // For ValueObject only + //------------------------------------------------------------------ + DISALLOW_COPY_AND_ASSIGN (ValueObjectSynthetic); +}; + +} // namespace lldb_private + +#endif // liblldb_ValueObjectSyntheticFilter_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Core/ValueObjectVariable.h b/contrib/llvm/tools/lldb/include/lldb/Core/ValueObjectVariable.h new file mode 100644 index 00000000000..8a30b00f6bb --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Core/ValueObjectVariable.h @@ -0,0 +1,90 @@ +//===-- ValueObjectVariable.h -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ValueObjectVariable_h_ +#define liblldb_ValueObjectVariable_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/ValueObject.h" + +namespace lldb_private { + +//---------------------------------------------------------------------- +// A ValueObject that contains a root variable that may or may not +// have children. +//---------------------------------------------------------------------- +class ValueObjectVariable : public ValueObject +{ +public: + static lldb::ValueObjectSP + Create (ExecutionContextScope *exe_scope, const lldb::VariableSP &var_sp); + + virtual + ~ValueObjectVariable(); + + virtual uint64_t + GetByteSize(); + + virtual ConstString + GetTypeName(); + + virtual ConstString + GetQualifiedTypeName(); + + virtual size_t + CalculateNumChildren(); + + virtual lldb::ValueType + GetValueType() const; + + virtual bool + IsInScope (); + + virtual lldb::ModuleSP + GetModule(); + + virtual SymbolContextScope * + GetSymbolContextScope(); + + virtual bool + GetDeclaration (Declaration &decl); + + virtual const char * + GetLocationAsCString (); + + virtual bool + SetValueFromCString (const char *value_str, Error& error); + + virtual bool + SetData (DataExtractor &data, Error &error); + +protected: + virtual bool + UpdateValue (); + + virtual ClangASTType + GetClangTypeImpl (); + + lldb::VariableSP m_variable_sp; ///< The variable that this value object is based upon + Value m_resolved_value; ///< The value that DWARFExpression resolves this variable to before we patch it up + +private: + ValueObjectVariable (ExecutionContextScope *exe_scope, const lldb::VariableSP &var_sp); + //------------------------------------------------------------------ + // For ValueObject only + //------------------------------------------------------------------ + DISALLOW_COPY_AND_ASSIGN (ValueObjectVariable); +}; + +} // namespace lldb_private + +#endif // liblldb_ValueObjectVariable_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Core/dwarf.h b/contrib/llvm/tools/lldb/include/lldb/Core/dwarf.h new file mode 100644 index 00000000000..bf77125d86a --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Core/dwarf.h @@ -0,0 +1,64 @@ +//===-- dwarf.h -------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef DebugBase_dwarf_h_ +#define DebugBase_dwarf_h_ + +#include +#include + +// Get the DWARF constant defintions from llvm +#include "llvm/Support/Dwarf.h" +// and stuff them in our default namespace +using namespace llvm::dwarf; + +typedef uint32_t dw_uleb128_t; +typedef int32_t dw_sleb128_t; +typedef uint16_t dw_attr_t; +typedef uint8_t dw_form_t; +typedef uint16_t dw_tag_t; +typedef uint64_t dw_addr_t; // Dwarf address define that must be big enough for any addresses in the compile units that get parsed + +#ifdef DWARFUTILS_DWARF64 +#define DWARF_REF_ADDR_SIZE 8 +typedef uint64_t dw_offset_t; // Dwarf Debug Information Entry offset for any offset into the file +#else +#define DWARF_REF_ADDR_SIZE 4 +typedef uint32_t dw_offset_t; // Dwarf Debug Information Entry offset for any offset into the file +#endif + +/* Constants */ +#define DW_INVALID_OFFSET (~(dw_offset_t)0) +#define DW_INVALID_INDEX 0xFFFFFFFFul + +// #define DW_ADDR_none 0x0 + +#define DW_EH_PE_MASK_ENCODING 0x0F + +//// The following are used only internally within lldb - don't +//// document them in the llvm Dwarf.h header file, we won't see +//// them in executable files anywhere. +//// These constants fit between DW_OP_lo_user (0xe0) and DW_OP_hi_user (0xff). +// +//#define DW_OP_APPLE_array_ref 0xEE // first pops index, then pops array; pushes array[index] +//#define DW_OP_APPLE_extern 0xEF // ULEB128 index of external object (i.e., an entity from the program that was used in the expression) +#define DW_OP_APPLE_uninit 0xF0 // This is actually generated by some apple compilers in locations lists +//#define DW_OP_APPLE_assign 0xF1 // pops value off and assigns it to second item on stack (2nd item must have assignable context) +//#define DW_OP_APPLE_address_of 0xF2 // gets the address of the top stack item (top item must be a variable, or have value_type that is an address already) +//#define DW_OP_APPLE_value_of 0xF3 // pops the value off the stack and pushes the value of that object (top item must be a variable, or expression local) +//#define DW_OP_APPLE_deref_type 0xF4 // gets the address of the top stack item (top item must be a variable, or a clang type) +//#define DW_OP_APPLE_expr_local 0xF5 // ULEB128 expression local index +//#define DW_OP_APPLE_constf 0xF6 // 1 byte float size, followed by constant float data +//#define DW_OP_APPLE_scalar_cast 0xF7 // Cast top of stack to 2nd in stack's type leaving all items in place +//#define DW_OP_APPLE_clang_cast 0xF8 // pointer size clang::Type * off the stack and cast top stack item to this type +//#define DW_OP_APPLE_clear 0xFE // clears the entire expression stack, ok if the stack is empty +//#define DW_OP_APPLE_error 0xFF // Stops expression evaluation and returns an error (no args) + + +#endif // DebugBase_dwarf_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/DataFormatters/CXXFormatterFunctions.h b/contrib/llvm/tools/lldb/include/lldb/DataFormatters/CXXFormatterFunctions.h new file mode 100644 index 00000000000..2f56c56810a --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/DataFormatters/CXXFormatterFunctions.h @@ -0,0 +1,874 @@ +//===-- CXXFormatterFunctions.h------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_CXXFormatterFunctions_h_ +#define liblldb_CXXFormatterFunctions_h_ + +#include +#include + +#include "lldb/lldb-forward.h" + +#include "lldb/Core/ConstString.h" +#include "lldb/DataFormatters/FormatClasses.h" +#include "lldb/Target/Target.h" + +#include "clang/AST/ASTContext.h" + +namespace lldb_private { + namespace formatters + { + bool + ExtractValueFromObjCExpression (ValueObject &valobj, + const char* target_type, + const char* selector, + uint64_t &value); + + bool + ExtractSummaryFromObjCExpression (ValueObject &valobj, + const char* target_type, + const char* selector, + Stream &stream); + + lldb::ValueObjectSP + CallSelectorOnObject (ValueObject &valobj, + const char* return_type, + const char* selector, + uint64_t index); + + lldb::ValueObjectSP + CallSelectorOnObject (ValueObject &valobj, + const char* return_type, + const char* selector, + const char* key); + + size_t + ExtractIndexFromString (const char* item_name); + + time_t + GetOSXEpoch (); + + bool + Char16StringSummaryProvider (ValueObject& valobj, Stream& stream); // char16_t* and unichar* + + bool + Char32StringSummaryProvider (ValueObject& valobj, Stream& stream); // char32_t* + + bool + WCharStringSummaryProvider (ValueObject& valobj, Stream& stream); // wchar_t* + + bool + Char16SummaryProvider (ValueObject& valobj, Stream& stream); // char16_t and unichar + + bool + Char32SummaryProvider (ValueObject& valobj, Stream& stream); // char32_t + + bool + WCharSummaryProvider (ValueObject& valobj, Stream& stream); // wchar_t + + bool + LibcxxStringSummaryProvider (ValueObject& valobj, Stream& stream); // libc++ std::string + + bool + LibcxxWStringSummaryProvider (ValueObject& valobj, Stream& stream); // libc++ std::wstring + + bool + ObjCClassSummaryProvider (ValueObject& valobj, Stream& stream); + + SyntheticChildrenFrontEnd* ObjCClassSyntheticFrontEndCreator (CXXSyntheticChildren*, lldb::ValueObjectSP); + + template + bool + NSDictionarySummaryProvider (ValueObject& valobj, Stream& stream); + + bool + NSIndexSetSummaryProvider (ValueObject& valobj, Stream& stream); + + bool + NSArraySummaryProvider (ValueObject& valobj, Stream& stream); + + template + bool + NSSetSummaryProvider (ValueObject& valobj, Stream& stream); + + template + bool + NSDataSummaryProvider (ValueObject& valobj, Stream& stream); + + bool + NSNumberSummaryProvider (ValueObject& valobj, Stream& stream); + + bool + NSNotificationSummaryProvider (ValueObject& valobj, Stream& stream); + + bool + NSTimeZoneSummaryProvider (ValueObject& valobj, Stream& stream); + + bool + NSMachPortSummaryProvider (ValueObject& valobj, Stream& stream); + + bool + CFBagSummaryProvider (ValueObject& valobj, Stream& stream); + + bool + CFBinaryHeapSummaryProvider (ValueObject& valobj, Stream& stream); + + bool + CFBitVectorSummaryProvider (ValueObject& valobj, Stream& stream); + + bool + NSDateSummaryProvider (ValueObject& valobj, Stream& stream); + + bool + CFAbsoluteTimeSummaryProvider (ValueObject& valobj, Stream& stream); + + bool + NSBundleSummaryProvider (ValueObject& valobj, Stream& stream); + + bool + NSStringSummaryProvider (ValueObject& valobj, Stream& stream); + + bool + NSAttributedStringSummaryProvider (ValueObject& valobj, Stream& stream); + + bool + NSMutableAttributedStringSummaryProvider (ValueObject& valobj, Stream& stream); + + bool + NSURLSummaryProvider (ValueObject& valobj, Stream& stream); + + bool + ObjCBOOLSummaryProvider (ValueObject& valobj, Stream& stream); + + template + bool + ObjCSELSummaryProvider (ValueObject& valobj, Stream& stream); + + bool + RuntimeSpecificDescriptionSummaryProvider (ValueObject& valobj, Stream& stream); + + extern template bool + NSDictionarySummaryProvider (ValueObject&, Stream&) ; + + extern template bool + NSDictionarySummaryProvider (ValueObject&, Stream&) ; + + extern template bool + NSDataSummaryProvider (ValueObject&, Stream&) ; + + extern template bool + NSDataSummaryProvider (ValueObject&, Stream&) ; + + extern template bool + ObjCSELSummaryProvider (ValueObject&, Stream&); + + extern template bool + ObjCSELSummaryProvider (ValueObject&, Stream&); + + class NSArrayMSyntheticFrontEnd : public SyntheticChildrenFrontEnd + { + private: + struct DataDescriptor_32 + { + uint32_t _used; + uint32_t _priv1 : 2 ; + uint32_t _size : 30; + uint32_t _priv2 : 2; + uint32_t offset : 30; + uint32_t _priv3; + uint32_t _data; + }; + struct DataDescriptor_64 + { + uint64_t _used; + uint64_t _priv1 : 2 ; + uint64_t _size : 62; + uint64_t _priv2 : 2; + uint64_t offset : 62; + uint32_t _priv3; + uint64_t _data; + }; + public: + NSArrayMSyntheticFrontEnd (lldb::ValueObjectSP valobj_sp); + + virtual size_t + CalculateNumChildren (); + + virtual lldb::ValueObjectSP + GetChildAtIndex (size_t idx); + + virtual bool + Update(); + + virtual bool + MightHaveChildren (); + + virtual size_t + GetIndexOfChildWithName (const ConstString &name); + + virtual + ~NSArrayMSyntheticFrontEnd (); + private: + ExecutionContextRef m_exe_ctx_ref; + uint8_t m_ptr_size; + DataDescriptor_32 *m_data_32; + DataDescriptor_64 *m_data_64; + ClangASTType m_id_type; + std::vector m_children; + }; + + class NSArrayISyntheticFrontEnd : public SyntheticChildrenFrontEnd + { + public: + NSArrayISyntheticFrontEnd (lldb::ValueObjectSP valobj_sp); + + virtual size_t + CalculateNumChildren (); + + virtual lldb::ValueObjectSP + GetChildAtIndex (size_t idx); + + virtual bool + Update(); + + virtual bool + MightHaveChildren (); + + virtual size_t + GetIndexOfChildWithName (const ConstString &name); + + virtual + ~NSArrayISyntheticFrontEnd (); + private: + ExecutionContextRef m_exe_ctx_ref; + uint8_t m_ptr_size; + uint64_t m_items; + lldb::addr_t m_data_ptr; + ClangASTType m_id_type; + std::vector m_children; + }; + + class NSArrayCodeRunningSyntheticFrontEnd : public SyntheticChildrenFrontEnd + { + public: + NSArrayCodeRunningSyntheticFrontEnd (lldb::ValueObjectSP valobj_sp); + + virtual size_t + CalculateNumChildren (); + + virtual lldb::ValueObjectSP + GetChildAtIndex (size_t idx); + + virtual bool + Update(); + + virtual bool + MightHaveChildren (); + + virtual size_t + GetIndexOfChildWithName (const ConstString &name); + + virtual + ~NSArrayCodeRunningSyntheticFrontEnd (); + }; + + SyntheticChildrenFrontEnd* NSArraySyntheticFrontEndCreator (CXXSyntheticChildren*, lldb::ValueObjectSP); + + class NSDictionaryISyntheticFrontEnd : public SyntheticChildrenFrontEnd + { + private: + struct DataDescriptor_32 + { + uint32_t _used : 26; + uint32_t _szidx : 6; + }; + struct DataDescriptor_64 + { + uint64_t _used : 58; + uint32_t _szidx : 6; + }; + + struct DictionaryItemDescriptor + { + lldb::addr_t key_ptr; + lldb::addr_t val_ptr; + lldb::ValueObjectSP valobj_sp; + }; + + public: + NSDictionaryISyntheticFrontEnd (lldb::ValueObjectSP valobj_sp); + + virtual size_t + CalculateNumChildren (); + + virtual lldb::ValueObjectSP + GetChildAtIndex (size_t idx); + + virtual bool + Update(); + + virtual bool + MightHaveChildren (); + + virtual size_t + GetIndexOfChildWithName (const ConstString &name); + + virtual + ~NSDictionaryISyntheticFrontEnd (); + private: + ExecutionContextRef m_exe_ctx_ref; + uint8_t m_ptr_size; + lldb::ByteOrder m_order; + DataDescriptor_32 *m_data_32; + DataDescriptor_64 *m_data_64; + lldb::addr_t m_data_ptr; + ClangASTType m_pair_type; + std::vector m_children; + }; + + class NSDictionaryMSyntheticFrontEnd : public SyntheticChildrenFrontEnd + { + private: + struct DataDescriptor_32 + { + uint32_t _used : 26; + uint32_t _kvo : 1; + uint32_t _size; + uint32_t _mutations; + uint32_t _objs_addr; + uint32_t _keys_addr; + }; + struct DataDescriptor_64 + { + uint64_t _used : 58; + uint32_t _kvo : 1; + uint64_t _size; + uint64_t _mutations; + uint64_t _objs_addr; + uint64_t _keys_addr; + }; + struct DictionaryItemDescriptor + { + lldb::addr_t key_ptr; + lldb::addr_t val_ptr; + lldb::ValueObjectSP valobj_sp; + }; + public: + NSDictionaryMSyntheticFrontEnd (lldb::ValueObjectSP valobj_sp); + + virtual size_t + CalculateNumChildren (); + + virtual lldb::ValueObjectSP + GetChildAtIndex (size_t idx); + + virtual bool + Update(); + + virtual bool + MightHaveChildren (); + + virtual size_t + GetIndexOfChildWithName (const ConstString &name); + + virtual + ~NSDictionaryMSyntheticFrontEnd (); + private: + ExecutionContextRef m_exe_ctx_ref; + uint8_t m_ptr_size; + lldb::ByteOrder m_order; + DataDescriptor_32 *m_data_32; + DataDescriptor_64 *m_data_64; + ClangASTType m_pair_type; + std::vector m_children; + }; + + class NSDictionaryCodeRunningSyntheticFrontEnd : public SyntheticChildrenFrontEnd + { + public: + NSDictionaryCodeRunningSyntheticFrontEnd (lldb::ValueObjectSP valobj_sp); + + virtual size_t + CalculateNumChildren (); + + virtual lldb::ValueObjectSP + GetChildAtIndex (size_t idx); + + virtual bool + Update(); + + virtual bool + MightHaveChildren (); + + virtual size_t + GetIndexOfChildWithName (const ConstString &name); + + virtual + ~NSDictionaryCodeRunningSyntheticFrontEnd (); + }; + + SyntheticChildrenFrontEnd* NSDictionarySyntheticFrontEndCreator (CXXSyntheticChildren*, lldb::ValueObjectSP); + + class NSSetISyntheticFrontEnd : public SyntheticChildrenFrontEnd + { + private: + struct DataDescriptor_32 + { + uint32_t _used : 26; + uint32_t _szidx : 6; + }; + struct DataDescriptor_64 + { + uint64_t _used : 58; + uint32_t _szidx : 6; + }; + + struct SetItemDescriptor + { + lldb::addr_t item_ptr; + lldb::ValueObjectSP valobj_sp; + }; + + public: + NSSetISyntheticFrontEnd (lldb::ValueObjectSP valobj_sp); + + virtual size_t + CalculateNumChildren (); + + virtual lldb::ValueObjectSP + GetChildAtIndex (size_t idx); + + virtual bool + Update(); + + virtual bool + MightHaveChildren (); + + virtual size_t + GetIndexOfChildWithName (const ConstString &name); + + virtual + ~NSSetISyntheticFrontEnd (); + private: + ExecutionContextRef m_exe_ctx_ref; + uint8_t m_ptr_size; + DataDescriptor_32 *m_data_32; + DataDescriptor_64 *m_data_64; + lldb::addr_t m_data_ptr; + std::vector m_children; + }; + + class NSOrderedSetSyntheticFrontEnd : public SyntheticChildrenFrontEnd + { + private: + + public: + NSOrderedSetSyntheticFrontEnd (lldb::ValueObjectSP valobj_sp); + + virtual size_t + CalculateNumChildren (); + + virtual lldb::ValueObjectSP + GetChildAtIndex (size_t idx); + + virtual bool + Update(); + + virtual bool + MightHaveChildren (); + + virtual size_t + GetIndexOfChildWithName (const ConstString &name); + + virtual + ~NSOrderedSetSyntheticFrontEnd (); + private: + uint32_t m_count; + std::map m_children; + }; + + class NSSetMSyntheticFrontEnd : public SyntheticChildrenFrontEnd + { + private: + struct DataDescriptor_32 + { + uint32_t _used : 26; + uint32_t _size; + uint32_t _mutations; + uint32_t _objs_addr; + }; + struct DataDescriptor_64 + { + uint64_t _used : 58; + uint64_t _size; + uint64_t _mutations; + uint64_t _objs_addr; + }; + struct SetItemDescriptor + { + lldb::addr_t item_ptr; + lldb::ValueObjectSP valobj_sp; + }; + public: + NSSetMSyntheticFrontEnd (lldb::ValueObjectSP valobj_sp); + + virtual size_t + CalculateNumChildren (); + + virtual lldb::ValueObjectSP + GetChildAtIndex (size_t idx); + + virtual bool + Update(); + + virtual bool + MightHaveChildren (); + + virtual size_t + GetIndexOfChildWithName (const ConstString &name); + + virtual + ~NSSetMSyntheticFrontEnd (); + private: + ExecutionContextRef m_exe_ctx_ref; + uint8_t m_ptr_size; + DataDescriptor_32 *m_data_32; + DataDescriptor_64 *m_data_64; + std::vector m_children; + }; + + class NSSetCodeRunningSyntheticFrontEnd : public SyntheticChildrenFrontEnd + { + public: + NSSetCodeRunningSyntheticFrontEnd (lldb::ValueObjectSP valobj_sp); + + virtual size_t + CalculateNumChildren (); + + virtual lldb::ValueObjectSP + GetChildAtIndex (size_t idx); + + virtual bool + Update(); + + virtual bool + MightHaveChildren (); + + virtual size_t + GetIndexOfChildWithName (const ConstString &name); + + virtual + ~NSSetCodeRunningSyntheticFrontEnd (); + }; + + SyntheticChildrenFrontEnd* NSSetSyntheticFrontEndCreator (CXXSyntheticChildren*, lldb::ValueObjectSP); + + class LibcxxVectorBoolSyntheticFrontEnd : public SyntheticChildrenFrontEnd + { + public: + LibcxxVectorBoolSyntheticFrontEnd (lldb::ValueObjectSP valobj_sp); + + virtual size_t + CalculateNumChildren (); + + virtual lldb::ValueObjectSP + GetChildAtIndex (size_t idx); + + virtual bool + Update(); + + virtual bool + MightHaveChildren (); + + virtual size_t + GetIndexOfChildWithName (const ConstString &name); + + virtual + ~LibcxxVectorBoolSyntheticFrontEnd (); + private: + ExecutionContextRef m_exe_ctx_ref; + uint64_t m_count; + lldb::addr_t m_base_data_address; + EvaluateExpressionOptions m_options; + }; + + SyntheticChildrenFrontEnd* LibcxxVectorBoolSyntheticFrontEndCreator (CXXSyntheticChildren*, lldb::ValueObjectSP); + + bool + LibcxxContainerSummaryProvider (ValueObject& valobj, Stream& stream); + + class LibstdcppVectorBoolSyntheticFrontEnd : public SyntheticChildrenFrontEnd + { + public: + LibstdcppVectorBoolSyntheticFrontEnd (lldb::ValueObjectSP valobj_sp); + + virtual size_t + CalculateNumChildren (); + + virtual lldb::ValueObjectSP + GetChildAtIndex (size_t idx); + + virtual bool + Update(); + + virtual bool + MightHaveChildren (); + + virtual size_t + GetIndexOfChildWithName (const ConstString &name); + + virtual + ~LibstdcppVectorBoolSyntheticFrontEnd (); + private: + ExecutionContextRef m_exe_ctx_ref; + uint64_t m_count; + lldb::addr_t m_base_data_address; + EvaluateExpressionOptions m_options; + }; + + SyntheticChildrenFrontEnd* LibstdcppVectorBoolSyntheticFrontEndCreator (CXXSyntheticChildren*, lldb::ValueObjectSP); + + class LibstdcppMapIteratorSyntheticFrontEnd : public SyntheticChildrenFrontEnd + { + public: + LibstdcppMapIteratorSyntheticFrontEnd (lldb::ValueObjectSP valobj_sp); + + virtual size_t + CalculateNumChildren (); + + virtual lldb::ValueObjectSP + GetChildAtIndex (size_t idx); + + virtual bool + Update(); + + virtual bool + MightHaveChildren (); + + virtual size_t + GetIndexOfChildWithName (const ConstString &name); + + virtual + ~LibstdcppMapIteratorSyntheticFrontEnd (); + private: + ExecutionContextRef m_exe_ctx_ref; + lldb::addr_t m_pair_address; + ClangASTType m_pair_type; + EvaluateExpressionOptions m_options; + lldb::ValueObjectSP m_pair_sp; + }; + + SyntheticChildrenFrontEnd* LibstdcppMapIteratorSyntheticFrontEndCreator (CXXSyntheticChildren*, lldb::ValueObjectSP); + + class LibCxxMapIteratorSyntheticFrontEnd : public SyntheticChildrenFrontEnd + { + public: + LibCxxMapIteratorSyntheticFrontEnd (lldb::ValueObjectSP valobj_sp); + + virtual size_t + CalculateNumChildren (); + + virtual lldb::ValueObjectSP + GetChildAtIndex (size_t idx); + + virtual bool + Update(); + + virtual bool + MightHaveChildren (); + + virtual size_t + GetIndexOfChildWithName (const ConstString &name); + + virtual + ~LibCxxMapIteratorSyntheticFrontEnd (); + private: + ValueObject *m_pair_ptr; + }; + + SyntheticChildrenFrontEnd* LibCxxMapIteratorSyntheticFrontEndCreator (CXXSyntheticChildren*, lldb::ValueObjectSP); + + class VectorIteratorSyntheticFrontEnd : public SyntheticChildrenFrontEnd + { + public: + VectorIteratorSyntheticFrontEnd (lldb::ValueObjectSP valobj_sp, + ConstString item_name); + + virtual size_t + CalculateNumChildren (); + + virtual lldb::ValueObjectSP + GetChildAtIndex (size_t idx); + + virtual bool + Update(); + + virtual bool + MightHaveChildren (); + + virtual size_t + GetIndexOfChildWithName (const ConstString &name); + + virtual + ~VectorIteratorSyntheticFrontEnd (); + private: + ExecutionContextRef m_exe_ctx_ref; + ConstString m_item_name; + lldb::ValueObjectSP m_item_sp; + }; + + SyntheticChildrenFrontEnd* LibCxxVectorIteratorSyntheticFrontEndCreator (CXXSyntheticChildren*, lldb::ValueObjectSP); + + SyntheticChildrenFrontEnd* LibStdcppVectorIteratorSyntheticFrontEndCreator (CXXSyntheticChildren*, lldb::ValueObjectSP); + + class LibcxxSharedPtrSyntheticFrontEnd : public SyntheticChildrenFrontEnd + { + public: + LibcxxSharedPtrSyntheticFrontEnd (lldb::ValueObjectSP valobj_sp); + + virtual size_t + CalculateNumChildren (); + + virtual lldb::ValueObjectSP + GetChildAtIndex (size_t idx); + + virtual bool + Update(); + + virtual bool + MightHaveChildren (); + + virtual size_t + GetIndexOfChildWithName (const ConstString &name); + + virtual + ~LibcxxSharedPtrSyntheticFrontEnd (); + private: + ValueObject* m_cntrl; + lldb::ValueObjectSP m_count_sp; + lldb::ValueObjectSP m_weak_count_sp; + uint8_t m_ptr_size; + lldb::ByteOrder m_byte_order; + }; + + SyntheticChildrenFrontEnd* LibcxxSharedPtrSyntheticFrontEndCreator (CXXSyntheticChildren*, lldb::ValueObjectSP); + + class LibcxxStdVectorSyntheticFrontEnd : public SyntheticChildrenFrontEnd + { + public: + LibcxxStdVectorSyntheticFrontEnd (lldb::ValueObjectSP valobj_sp); + + virtual size_t + CalculateNumChildren (); + + virtual lldb::ValueObjectSP + GetChildAtIndex (size_t idx); + + virtual bool + Update(); + + virtual bool + MightHaveChildren (); + + virtual size_t + GetIndexOfChildWithName (const ConstString &name); + + virtual + ~LibcxxStdVectorSyntheticFrontEnd (); + private: + ValueObject* m_start; + ValueObject* m_finish; + ClangASTType m_element_type; + uint32_t m_element_size; + std::map m_children; + }; + + SyntheticChildrenFrontEnd* LibcxxStdVectorSyntheticFrontEndCreator (CXXSyntheticChildren*, lldb::ValueObjectSP); + + class LibcxxStdListSyntheticFrontEnd : public SyntheticChildrenFrontEnd + { + public: + LibcxxStdListSyntheticFrontEnd (lldb::ValueObjectSP valobj_sp); + + virtual size_t + CalculateNumChildren (); + + virtual lldb::ValueObjectSP + GetChildAtIndex (size_t idx); + + virtual bool + Update(); + + virtual bool + MightHaveChildren (); + + virtual size_t + GetIndexOfChildWithName (const ConstString &name); + + virtual + ~LibcxxStdListSyntheticFrontEnd (); + private: + bool + HasLoop(); + + size_t m_list_capping_size; + static const bool g_use_loop_detect = true; + lldb::addr_t m_node_address; + ValueObject* m_head; + ValueObject* m_tail; + ClangASTType m_element_type; + size_t m_count; + std::map m_children; + }; + + SyntheticChildrenFrontEnd* LibcxxStdListSyntheticFrontEndCreator (CXXSyntheticChildren*, lldb::ValueObjectSP); + + class LibcxxStdMapSyntheticFrontEnd : public SyntheticChildrenFrontEnd + { + public: + LibcxxStdMapSyntheticFrontEnd (lldb::ValueObjectSP valobj_sp); + + virtual size_t + CalculateNumChildren (); + + virtual lldb::ValueObjectSP + GetChildAtIndex (size_t idx); + + virtual bool + Update(); + + virtual bool + MightHaveChildren (); + + virtual size_t + GetIndexOfChildWithName (const ConstString &name); + + virtual + ~LibcxxStdMapSyntheticFrontEnd (); + private: + bool + GetDataType(); + + void + GetValueOffset (const lldb::ValueObjectSP& node); + + ValueObject* m_tree; + ValueObject* m_root_node; + ClangASTType m_element_type; + uint32_t m_skip_size; + size_t m_count; + std::map m_children; + }; + + SyntheticChildrenFrontEnd* LibcxxStdMapSyntheticFrontEndCreator (CXXSyntheticChildren*, lldb::ValueObjectSP); + + } // namespace formatters +} // namespace lldb_private + +#endif // liblldb_CXXFormatterFunctions_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/DataFormatters/DataVisualization.h b/contrib/llvm/tools/lldb/include/lldb/DataFormatters/DataVisualization.h new file mode 100644 index 00000000000..499e0fe14d9 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/DataFormatters/DataVisualization.h @@ -0,0 +1,174 @@ +//===-- DataVisualization.h ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef lldb_DataVisualization_h_ +#define lldb_DataVisualization_h_ + +// C Includes +// C++ Includes + +// Other libraries and framework includes +// Project includes +#include "lldb/Core/ConstString.h" +#include "lldb/DataFormatters/FormatClasses.h" +#include "lldb/DataFormatters/FormatManager.h" + +namespace lldb_private { + +// this class is the high-level front-end of LLDB Data Visualization +// code in FormatManager.h/cpp is the low-level implementation of this feature +// clients should refer to this class as the entry-point into the data formatters +// unless they have a good reason to bypass this and go to the backend +class DataVisualization +{ +public: + + // use this call to force the FM to consider itself updated even when there is no apparent reason for that + static void + ForceUpdate(); + + static uint32_t + GetCurrentRevision (); + + class ValueFormats + { + public: + static lldb::TypeFormatImplSP + GetFormat (ValueObject& valobj, lldb::DynamicValueType use_dynamic); + + static lldb::TypeFormatImplSP + GetFormat (const ConstString &type); + + static void + Add (const ConstString &type, const lldb::TypeFormatImplSP &entry); + + static bool + Delete (const ConstString &type); + + static void + Clear (); + + static void + LoopThrough (TypeFormatImpl::ValueCallback callback, void* callback_baton); + + static size_t + GetCount (); + + static lldb::TypeNameSpecifierImplSP + GetTypeNameSpecifierForFormatAtIndex (size_t); + + static lldb::TypeFormatImplSP + GetFormatAtIndex (size_t); + }; + + static lldb::TypeSummaryImplSP + GetSummaryFormat(ValueObject& valobj, + lldb::DynamicValueType use_dynamic); + + static lldb::TypeSummaryImplSP + GetSummaryForType (lldb::TypeNameSpecifierImplSP type_sp); + +#ifndef LLDB_DISABLE_PYTHON + static lldb::SyntheticChildrenSP + GetSyntheticChildrenForType (lldb::TypeNameSpecifierImplSP type_sp); +#endif + + static lldb::TypeFilterImplSP + GetFilterForType (lldb::TypeNameSpecifierImplSP type_sp); + +#ifndef LLDB_DISABLE_PYTHON + static lldb::ScriptedSyntheticChildrenSP + GetSyntheticForType (lldb::TypeNameSpecifierImplSP type_sp); +#endif + +#ifndef LLDB_DISABLE_PYTHON + static lldb::SyntheticChildrenSP + GetSyntheticChildren(ValueObject& valobj, + lldb::DynamicValueType use_dynamic); +#endif + + static bool + AnyMatches(ConstString type_name, + TypeCategoryImpl::FormatCategoryItems items = TypeCategoryImpl::ALL_ITEM_TYPES, + bool only_enabled = true, + const char** matching_category = NULL, + TypeCategoryImpl::FormatCategoryItems* matching_type = NULL); + + class NamedSummaryFormats + { + public: + static bool + GetSummaryFormat (const ConstString &type, lldb::TypeSummaryImplSP &entry); + + static void + Add (const ConstString &type, const lldb::TypeSummaryImplSP &entry); + + static bool + Delete (const ConstString &type); + + static void + Clear (); + + static void + LoopThrough (TypeSummaryImpl::SummaryCallback callback, void* callback_baton); + + static uint32_t + GetCount (); + }; + + class Categories + { + public: + + static bool + GetCategory (const ConstString &category, + lldb::TypeCategoryImplSP &entry, + bool allow_create = true); + + static void + Add (const ConstString &category); + + static bool + Delete (const ConstString &category); + + static void + Clear (); + + static void + Clear (const ConstString &category); + + static void + Enable (const ConstString& category, + TypeCategoryMap::Position = TypeCategoryMap::Default); + + static void + Disable (const ConstString& category); + + static void + Enable (const lldb::TypeCategoryImplSP& category, + TypeCategoryMap::Position = TypeCategoryMap::Default); + + static void + Disable (const lldb::TypeCategoryImplSP& category); + + static void + LoopThrough (FormatManager::CategoryCallback callback, void* callback_baton); + + static uint32_t + GetCount (); + + static lldb::TypeCategoryImplSP + GetCategoryAtIndex (size_t); + }; +}; + + +} // namespace lldb_private + +#endif // lldb_DataVisualization_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/DataFormatters/FormatCache.h b/contrib/llvm/tools/lldb/include/lldb/DataFormatters/FormatCache.h new file mode 100644 index 00000000000..941b96c1fac --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/DataFormatters/FormatCache.h @@ -0,0 +1,101 @@ +//===-- FormatCache.h ---------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef lldb_FormatCache_h_ +#define lldb_FormatCache_h_ + +// C Includes +// C++ Includes +#include + +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-public.h" +#include "lldb/Core/ConstString.h" +#include "lldb/DataFormatters/FormatClasses.h" + +namespace lldb_private { +class FormatCache +{ +private: + struct Entry + { + private: + bool m_summary_cached : 1; + bool m_synthetic_cached : 1; + + lldb::TypeSummaryImplSP m_summary_sp; + lldb::SyntheticChildrenSP m_synthetic_sp; + public: + Entry (); + Entry (lldb::TypeSummaryImplSP); + Entry (lldb::SyntheticChildrenSP); + Entry (lldb::TypeSummaryImplSP,lldb::SyntheticChildrenSP); + + bool + IsSummaryCached (); + + bool + IsSyntheticCached (); + + lldb::TypeSummaryImplSP + GetSummary (); + + lldb::SyntheticChildrenSP + GetSynthetic (); + + void + SetSummary (lldb::TypeSummaryImplSP); + + void + SetSynthetic (lldb::SyntheticChildrenSP); + }; + typedef std::map CacheMap; + CacheMap m_map; + Mutex m_mutex; + + uint64_t m_cache_hits; + uint64_t m_cache_misses; + + Entry& + GetEntry (const ConstString& type); + +public: + FormatCache (); + + bool + GetSummary (const ConstString& type,lldb::TypeSummaryImplSP& summary_sp); + + bool + GetSynthetic (const ConstString& type,lldb::SyntheticChildrenSP& synthetic_sp); + + void + SetSummary (const ConstString& type,lldb::TypeSummaryImplSP& summary_sp); + + void + SetSynthetic (const ConstString& type,lldb::SyntheticChildrenSP& synthetic_sp); + + void + Clear (); + + uint64_t + GetCacheHits () + { + return m_cache_hits; + } + + uint64_t + GetCacheMisses () + { + return m_cache_misses; + } +}; +} // namespace lldb_private + +#endif // lldb_FormatCache_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/DataFormatters/FormatClasses.h b/contrib/llvm/tools/lldb/include/lldb/DataFormatters/FormatClasses.h new file mode 100644 index 00000000000..48a8eda4ad4 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/DataFormatters/FormatClasses.h @@ -0,0 +1,128 @@ +//===-- FormatClasses.h -----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef lldb_FormatClasses_h_ +#define lldb_FormatClasses_h_ + +// C Includes +#include +#include + +// C++ Includes +#include +#include + +// Other libraries and framework includes + +// Project includes +#include "lldb/lldb-public.h" +#include "lldb/lldb-enumerations.h" + +#include "lldb/Core/ValueObject.h" +#include "lldb/Interpreter/ScriptInterpreterPython.h" +#include "lldb/Symbol/ClangASTType.h" +#include "lldb/Symbol/Type.h" + +#include "lldb/DataFormatters/TypeFormat.h" +#include "lldb/DataFormatters/TypeSummary.h" +#include "lldb/DataFormatters/TypeSynthetic.h" + +namespace lldb_private { + +class TypeNameSpecifierImpl +{ +public: + TypeNameSpecifierImpl() : + m_is_regex(false), + m_type() + { + } + + TypeNameSpecifierImpl (const char* name, bool is_regex) : + m_is_regex(is_regex), + m_type() + { + if (name) + m_type.m_type_name.assign(name); + } + + // if constructing with a given type, is_regex cannot be true since we are + // giving an exact type to match + TypeNameSpecifierImpl (lldb::TypeSP type) : + m_is_regex(false), + m_type() + { + if (type) + { + m_type.m_type_name.assign(type->GetName().GetCString()); + m_type.m_typeimpl_sp = lldb::TypeImplSP(new TypeImpl(type)); + } + } + + TypeNameSpecifierImpl (ClangASTType type) : + m_is_regex(false), + m_type() + { + if (type.IsValid()) + { + m_type.m_type_name.assign(type.GetConstTypeName().GetCString()); + m_type.m_typeimpl_sp = lldb::TypeImplSP(new TypeImpl(type)); + } + } + + const char* + GetName() + { + if (m_type.m_type_name.size()) + return m_type.m_type_name.c_str(); + return NULL; + } + + lldb::TypeSP + GetTypeSP () + { + if (m_type.m_typeimpl_sp && m_type.m_typeimpl_sp->IsValid()) + return m_type.m_typeimpl_sp->GetTypeSP(); + return lldb::TypeSP(); + } + + ClangASTType + GetClangASTType () + { + if (m_type.m_typeimpl_sp && m_type.m_typeimpl_sp->IsValid()) + return m_type.m_typeimpl_sp->GetClangASTType(); + return ClangASTType(); + } + + bool + IsRegex() + { + return m_is_regex; + } + +private: + bool m_is_regex; + // this works better than TypeAndOrName because the latter only wraps a TypeSP + // whereas TypeImplSP can also be backed by a ClangASTType which is more commonly + // used in LLDB. moreover, TypeImplSP is also what is currently backing SBType + struct TypeOrName + { + std::string m_type_name; + lldb::TypeImplSP m_typeimpl_sp; + }; + TypeOrName m_type; + + +private: + DISALLOW_COPY_AND_ASSIGN(TypeNameSpecifierImpl); +}; + +} // namespace lldb_private + +#endif // lldb_FormatClasses_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/DataFormatters/FormatManager.h b/contrib/llvm/tools/lldb/include/lldb/DataFormatters/FormatManager.h new file mode 100644 index 00000000000..162e25143f2 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/DataFormatters/FormatManager.h @@ -0,0 +1,251 @@ +//===-- FormatManager.h -------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef lldb_FormatManager_h_ +#define lldb_FormatManager_h_ + +// C Includes +// C++ Includes + +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-public.h" +#include "lldb/lldb-enumerations.h" + +#include "lldb/DataFormatters/FormatCache.h" +#include "lldb/DataFormatters/FormatNavigator.h" +#include "lldb/DataFormatters/TypeCategory.h" +#include "lldb/DataFormatters/TypeCategoryMap.h" + +namespace lldb_private { + +// this file (and its. cpp) contain the low-level implementation of LLDB Data Visualization +// class DataVisualization is the high-level front-end of this feature +// clients should refer to that class as the entry-point into the data formatters +// unless they have a good reason to bypass it and prefer to use this file's objects directly + +class FormatManager : public IFormatChangeListener +{ + typedef FormatNavigator ValueNavigator; + typedef ValueNavigator::MapType ValueMap; + typedef FormatMap NamedSummariesMap; + typedef TypeCategoryMap::MapType::iterator CategoryMapIterator; +public: + + typedef TypeCategoryMap::CallbackType CategoryCallback; + + FormatManager (); + + ValueNavigator& + GetValueNavigator () + { + return m_value_nav; + } + + NamedSummariesMap& + GetNamedSummaryNavigator () + { + return m_named_summaries_map; + } + + void + EnableCategory (const ConstString& category_name, + TypeCategoryMap::Position pos = TypeCategoryMap::Default) + { + m_categories_map.Enable(category_name, + pos); + } + + void + DisableCategory (const ConstString& category_name) + { + m_categories_map.Disable(category_name); + } + + void + EnableCategory (const lldb::TypeCategoryImplSP& category, + TypeCategoryMap::Position pos = TypeCategoryMap::Default) + { + m_categories_map.Enable(category, + pos); + } + + void + DisableCategory (const lldb::TypeCategoryImplSP& category) + { + m_categories_map.Disable(category); + } + + bool + DeleteCategory (const ConstString& category_name) + { + return m_categories_map.Delete(category_name); + } + + void + ClearCategories () + { + return m_categories_map.Clear(); + } + + uint32_t + GetCategoriesCount () + { + return m_categories_map.GetCount(); + } + + lldb::TypeCategoryImplSP + GetCategoryAtIndex (size_t index) + { + return m_categories_map.GetAtIndex(index); + } + + void + LoopThroughCategories (CategoryCallback callback, void* param) + { + m_categories_map.LoopThrough(callback, param); + } + + lldb::TypeCategoryImplSP + GetCategory (const char* category_name = NULL, + bool can_create = true) + { + if (!category_name) + return GetCategory(m_default_category_name); + return GetCategory(ConstString(category_name)); + } + + lldb::TypeCategoryImplSP + GetCategory (const ConstString& category_name, + bool can_create = true); + + lldb::TypeSummaryImplSP + GetSummaryForType (lldb::TypeNameSpecifierImplSP type_sp); + + lldb::TypeFilterImplSP + GetFilterForType (lldb::TypeNameSpecifierImplSP type_sp); + +#ifndef LLDB_DISABLE_PYTHON + lldb::ScriptedSyntheticChildrenSP + GetSyntheticForType (lldb::TypeNameSpecifierImplSP type_sp); +#endif + +#ifndef LLDB_DISABLE_PYTHON + lldb::SyntheticChildrenSP + GetSyntheticChildrenForType (lldb::TypeNameSpecifierImplSP type_sp); +#endif + + lldb::TypeSummaryImplSP + GetSummaryFormat (ValueObject& valobj, + lldb::DynamicValueType use_dynamic); + +#ifndef LLDB_DISABLE_PYTHON + lldb::SyntheticChildrenSP + GetSyntheticChildren (ValueObject& valobj, + lldb::DynamicValueType use_dynamic); +#endif + + bool + AnyMatches (ConstString type_name, + TypeCategoryImpl::FormatCategoryItems items = TypeCategoryImpl::ALL_ITEM_TYPES, + bool only_enabled = true, + const char** matching_category = NULL, + TypeCategoryImpl::FormatCategoryItems* matching_type = NULL) + { + return m_categories_map.AnyMatches(type_name, + items, + only_enabled, + matching_category, + matching_type); + } + + static bool + GetFormatFromCString (const char *format_cstr, + bool partial_match_ok, + lldb::Format &format); + + static char + GetFormatAsFormatChar (lldb::Format format); + + static const char * + GetFormatAsCString (lldb::Format format); + + // if the user tries to add formatters for, say, "struct Foo" + // those will not match any type because of the way we strip qualifiers from typenames + // this method looks for the case where the user is adding a "class","struct","enum" or "union" Foo + // and strips the unnecessary qualifier + static ConstString + GetValidTypeName (const ConstString& type); + + // when DataExtractor dumps a vectorOfT, it uses a predefined format for each item + // this method returns it, or eFormatInvalid if vector_format is not a vectorOf + static lldb::Format + GetSingleItemFormat (lldb::Format vector_format); + + void + Changed () + { + __sync_add_and_fetch(&m_last_revision, +1); + m_format_cache.Clear (); + } + + uint32_t + GetCurrentRevision () + { + return m_last_revision; + } + + ~FormatManager () + { + } + +private: + FormatCache m_format_cache; + ValueNavigator m_value_nav; + NamedSummariesMap m_named_summaries_map; + uint32_t m_last_revision; + TypeCategoryMap m_categories_map; + + ConstString m_default_category_name; + ConstString m_system_category_name; + ConstString m_gnu_cpp_category_name; + ConstString m_libcxx_category_name; + ConstString m_objc_category_name; + ConstString m_corefoundation_category_name; + ConstString m_coregraphics_category_name; + ConstString m_coreservices_category_name; + ConstString m_vectortypes_category_name; + ConstString m_appkit_category_name; + + TypeCategoryMap& + GetCategories () + { + return m_categories_map; + } + + // WARNING: these are temporary functions that setup formatters + // while a few of these actually should be globally available and setup by LLDB itself + // most would actually belong to the users' lldbinit file or to some other form of configurable + // storage + void + LoadLibStdcppFormatters (); + + void + LoadLibcxxFormatters (); + + void + LoadSystemFormatters (); + + void + LoadObjCFormatters (); +}; + +} // namespace lldb_private + +#endif // lldb_FormatManager_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/DataFormatters/FormatNavigator.h b/contrib/llvm/tools/lldb/include/lldb/DataFormatters/FormatNavigator.h new file mode 100644 index 00000000000..a738cfd069e --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/DataFormatters/FormatNavigator.h @@ -0,0 +1,690 @@ +//===-- FormatNavigator.h ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef lldb_FormatNavigator_h_ +#define lldb_FormatNavigator_h_ + +// C Includes +// C++ Includes + +// Other libraries and framework includes +#include "clang/AST/DeclCXX.h" +#include "clang/AST/Type.h" +#include "clang/AST/DeclObjC.h" + +// Project includes +#include "lldb/lldb-public.h" + +#include "lldb/Core/Log.h" +#include "lldb/Core/RegularExpression.h" +#include "lldb/Core/ValueObject.h" + +#include "lldb/DataFormatters/FormatClasses.h" + +#include "lldb/Symbol/ClangASTContext.h" +#include "lldb/Symbol/ClangASTType.h" + +#include "lldb/Target/ObjCLanguageRuntime.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/StackFrame.h" +#include "lldb/Target/TargetList.h" + +namespace lldb_private { + +// this file (and its. cpp) contain the low-level implementation of LLDB Data Visualization +// class DataVisualization is the high-level front-end of this feature +// clients should refer to that class as the entry-point into the data formatters +// unless they have a good reason to bypass it and prefer to use this file's objects directly +class IFormatChangeListener +{ +public: + virtual void + Changed () = 0; + + virtual + ~IFormatChangeListener () {} + + virtual uint32_t + GetCurrentRevision () = 0; + +}; + +static inline bool +IsWhitespace (char c) +{ + return ( (c == ' ') || (c == '\t') || (c == '\v') || (c == '\f') ); +} + +static inline bool +HasPrefix (const char* str1, const char* str2) +{ + return ( ::strstr(str1, str2) == str1 ); +} + +// if the user tries to add formatters for, say, "struct Foo" +// those will not match any type because of the way we strip qualifiers from typenames +// this method looks for the case where the user is adding a "class","struct","enum" or "union" Foo +// and strips the unnecessary qualifier +static inline ConstString +GetValidTypeName_Impl (const ConstString& type) +{ + int strip_len = 0; + + if (type == false) + return type; + + const char* type_cstr = type.AsCString(); + + if ( HasPrefix(type_cstr, "class ") ) + strip_len = 6; + else if ( HasPrefix(type_cstr, "enum ") ) + strip_len = 5; + else if ( HasPrefix(type_cstr, "struct ") ) + strip_len = 7; + else if ( HasPrefix(type_cstr, "union ") ) + strip_len = 6; + + if (strip_len == 0) + return type; + + type_cstr += strip_len; + while (IsWhitespace(*type_cstr) && ++type_cstr) + ; + + return ConstString(type_cstr); +} + +template +class FormatNavigator; + +template +class FormatMap +{ +public: + + typedef typename ValueType::SharedPointer ValueSP; + typedef std::map MapType; + typedef typename MapType::iterator MapIterator; + typedef bool(*CallbackType)(void*, KeyType, const ValueSP&); + + FormatMap(IFormatChangeListener* lst) : + m_map(), + m_map_mutex(Mutex::eMutexTypeRecursive), + listener(lst) + { + } + + void + Add(KeyType name, + const ValueSP& entry) + { + if (listener) + entry->GetRevision() = listener->GetCurrentRevision(); + else + entry->GetRevision() = 0; + + Mutex::Locker locker(m_map_mutex); + m_map[name] = entry; + if (listener) + listener->Changed(); + } + + bool + Delete (KeyType name) + { + Mutex::Locker locker(m_map_mutex); + MapIterator iter = m_map.find(name); + if (iter == m_map.end()) + return false; + m_map.erase(name); + if (listener) + listener->Changed(); + return true; + } + + void + Clear () + { + Mutex::Locker locker(m_map_mutex); + m_map.clear(); + if (listener) + listener->Changed(); + } + + bool + Get(KeyType name, + ValueSP& entry) + { + Mutex::Locker locker(m_map_mutex); + MapIterator iter = m_map.find(name); + if (iter == m_map.end()) + return false; + entry = iter->second; + return true; + } + + void + LoopThrough (CallbackType callback, void* param) + { + if (callback) + { + Mutex::Locker locker(m_map_mutex); + MapIterator pos, end = m_map.end(); + for (pos = m_map.begin(); pos != end; pos++) + { + KeyType type = pos->first; + if (!callback(param, type, pos->second)) + break; + } + } + } + + uint32_t + GetCount () + { + return m_map.size(); + } + + ValueSP + GetValueAtIndex (size_t index) + { + Mutex::Locker locker(m_map_mutex); + MapIterator iter = m_map.begin(); + MapIterator end = m_map.end(); + while (index > 0) + { + iter++; + index--; + if (end == iter) + return ValueSP(); + } + return iter->second; + } + + KeyType + GetKeyAtIndex (size_t index) + { + Mutex::Locker locker(m_map_mutex); + MapIterator iter = m_map.begin(); + MapIterator end = m_map.end(); + while (index > 0) + { + iter++; + index--; + if (end == iter) + return KeyType(); + } + return iter->first; + } + +protected: + MapType m_map; + Mutex m_map_mutex; + IFormatChangeListener* listener; + + MapType& + map () + { + return m_map; + } + + Mutex& + mutex () + { + return m_map_mutex; + } + + friend class FormatNavigator; + friend class FormatManager; + +}; + +template +class FormatNavigator +{ +protected: + typedef FormatMap BackEndType; + +public: + typedef typename BackEndType::MapType MapType; + typedef typename MapType::iterator MapIterator; + typedef typename MapType::key_type MapKeyType; + typedef typename MapType::mapped_type MapValueType; + typedef typename BackEndType::CallbackType CallbackType; + typedef typename std::shared_ptr > SharedPointer; + + friend class TypeCategoryImpl; + + FormatNavigator(std::string name, + IFormatChangeListener* lst) : + m_format_map(lst), + m_name(name), + m_id_cs(ConstString("id")) + { + } + + void + Add (const MapKeyType &type, const MapValueType& entry) + { + Add_Impl(type, entry, (KeyType*)NULL); + } + + bool + Delete (ConstString type) + { + return Delete_Impl(type, (KeyType*)NULL); + } + + bool + Get(ValueObject& valobj, + MapValueType& entry, + lldb::DynamicValueType use_dynamic, + uint32_t* why = NULL) + { + uint32_t value = lldb_private::eFormatterChoiceCriterionDirectChoice; + ClangASTType ast_type(valobj.GetClangType()); + bool ret = Get(valobj, ast_type, entry, use_dynamic, value); + if (ret) + entry = MapValueType(entry); + else + entry = MapValueType(); + if (why) + *why = value; + return ret; + } + + bool + Get (ConstString type, MapValueType& entry) + { + return Get_Impl(type, entry, (KeyType*)NULL); + } + + bool + GetExact (ConstString type, MapValueType& entry) + { + return GetExact_Impl(type, entry, (KeyType*)NULL); + } + + MapValueType + GetAtIndex (size_t index) + { + return m_format_map.GetValueAtIndex(index); + } + + lldb::TypeNameSpecifierImplSP + GetTypeNameSpecifierAtIndex (size_t index) + { + return GetTypeNameSpecifierAtIndex_Impl(index, (KeyType*)NULL); + } + + void + Clear () + { + m_format_map.Clear(); + } + + void + LoopThrough (CallbackType callback, void* param) + { + m_format_map.LoopThrough(callback,param); + } + + uint32_t + GetCount () + { + return m_format_map.GetCount(); + } + +protected: + + BackEndType m_format_map; + + std::string m_name; + + DISALLOW_COPY_AND_ASSIGN(FormatNavigator); + + ConstString m_id_cs; + + void + Add_Impl (const MapKeyType &type, const MapValueType& entry, lldb::RegularExpressionSP *dummy) + { + m_format_map.Add(type,entry); + } + + void Add_Impl (const ConstString &type, const MapValueType& entry, ConstString *dummy) + { + m_format_map.Add(GetValidTypeName_Impl(type), entry); + } + + bool + Delete_Impl (ConstString type, ConstString *dummy) + { + return m_format_map.Delete(type); + } + + bool + Delete_Impl (ConstString type, lldb::RegularExpressionSP *dummy) + { + Mutex& x_mutex = m_format_map.mutex(); + lldb_private::Mutex::Locker locker(x_mutex); + MapIterator pos, end = m_format_map.map().end(); + for (pos = m_format_map.map().begin(); pos != end; pos++) + { + lldb::RegularExpressionSP regex = pos->first; + if ( ::strcmp(type.AsCString(),regex->GetText()) == 0) + { + m_format_map.map().erase(pos); + if (m_format_map.listener) + m_format_map.listener->Changed(); + return true; + } + } + return false; + } + + bool + Get_Impl (ConstString type, MapValueType& entry, ConstString *dummy) + { + return m_format_map.Get(type, entry); + } + + bool + GetExact_Impl (ConstString type, MapValueType& entry, ConstString *dummy) + { + return Get_Impl(type,entry, (KeyType*)0); + } + + lldb::TypeNameSpecifierImplSP + GetTypeNameSpecifierAtIndex_Impl (size_t index, ConstString *dummy) + { + ConstString key = m_format_map.GetKeyAtIndex(index); + if (key) + return lldb::TypeNameSpecifierImplSP(new TypeNameSpecifierImpl(key.AsCString(), + false)); + else + return lldb::TypeNameSpecifierImplSP(); + } + + lldb::TypeNameSpecifierImplSP + GetTypeNameSpecifierAtIndex_Impl (size_t index, lldb::RegularExpressionSP *dummy) + { + lldb::RegularExpressionSP regex = m_format_map.GetKeyAtIndex(index); + if (regex.get() == NULL) + return lldb::TypeNameSpecifierImplSP(); + return lldb::TypeNameSpecifierImplSP(new TypeNameSpecifierImpl(regex->GetText(), + true)); + } + + bool + Get_Impl (ConstString key, MapValueType& value, lldb::RegularExpressionSP *dummy) + { + const char* key_cstr = key.AsCString(); + if (!key_cstr) + return false; + Mutex& x_mutex = m_format_map.mutex(); + lldb_private::Mutex::Locker locker(x_mutex); + MapIterator pos, end = m_format_map.map().end(); + for (pos = m_format_map.map().begin(); pos != end; pos++) + { + lldb::RegularExpressionSP regex = pos->first; + if (regex->Execute(key_cstr)) + { + value = pos->second; + return true; + } + } + return false; + } + + bool + GetExact_Impl (ConstString key, MapValueType& value, lldb::RegularExpressionSP *dummy) + { + Mutex& x_mutex = m_format_map.mutex(); + lldb_private::Mutex::Locker locker(x_mutex); + MapIterator pos, end = m_format_map.map().end(); + for (pos = m_format_map.map().begin(); pos != end; pos++) + { + lldb::RegularExpressionSP regex = pos->first; + if (strcmp(regex->GetText(),key.AsCString()) == 0) + { + value = pos->second; + return true; + } + } + return false; + } + + bool + Get_BitfieldMatch (ValueObject& valobj, + ConstString typeName, + MapValueType& entry, + uint32_t& reason) + { + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_TYPES)); + // for bitfields, append size to the typename so one can custom format them + StreamString sstring; + sstring.Printf("%s:%d",typeName.AsCString(),valobj.GetBitfieldBitSize()); + ConstString bitfieldname = ConstString(sstring.GetData()); + if (log) + log->Printf("[Get_BitfieldMatch] appended bitfield info, final result is %s", bitfieldname.GetCString()); + if (Get(bitfieldname, entry)) + { + if (log) + log->Printf("[Get_BitfieldMatch] bitfield direct match found, returning"); + return true; + } + else + { + reason |= lldb_private::eFormatterChoiceCriterionStrippedBitField; + if (log) + log->Printf("[Get_BitfieldMatch] no bitfield direct match"); + return false; + } + } + + bool Get_ObjC (ValueObject& valobj, + MapValueType& entry) + { + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_TYPES)); + lldb::ProcessSP process_sp = valobj.GetProcessSP(); + ObjCLanguageRuntime* runtime = process_sp->GetObjCLanguageRuntime(); + if (runtime == NULL) + { + if (log) + log->Printf("[Get_ObjC] no valid ObjC runtime, skipping dynamic"); + return false; + } + ObjCLanguageRuntime::ClassDescriptorSP objc_class_sp (runtime->GetClassDescriptor(valobj)); + if (!objc_class_sp) + { + if (log) + log->Printf("[Get_ObjC] invalid ISA, skipping dynamic"); + return false; + } + ConstString name (objc_class_sp->GetClassName()); + if (log) + log->Printf("[Get_ObjC] dynamic type inferred is %s - looking for direct dynamic match", name.GetCString()); + if (Get(name, entry)) + { + if (log) + log->Printf("[Get_ObjC] direct dynamic match found, returning"); + return true; + } + if (log) + log->Printf("[Get_ObjC] no dynamic match"); + return false; + } + + bool + Get_Impl (ValueObject& valobj, + ClangASTType clang_type, + MapValueType& entry, + lldb::DynamicValueType use_dynamic, + uint32_t& reason) + { + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_TYPES)); + + if (!clang_type.IsValid()) + { + if (log) + log->Printf("[Get_Impl] type is invalid, returning"); + return false; + } + + clang_type = clang_type.RemoveFastQualifiers(); + + ConstString typeName(clang_type.GetConstTypeName()); + + if (valobj.GetBitfieldBitSize() > 0) + { + if (Get_BitfieldMatch(valobj, typeName, entry, reason)) + return true; + } + + if (log) + log->Printf("[Get_Impl] trying to get %s for VO name %s of type %s", + m_name.c_str(), + valobj.GetName().AsCString(), + typeName.AsCString()); + + if (Get(typeName, entry)) + { + if (log) + log->Printf("[Get] direct match found, returning"); + return true; + } + if (log) + log->Printf("[Get_Impl] no direct match"); + + // strip pointers and references and see if that helps + if (clang_type.IsReferenceType()) + { + if (log) + log->Printf("[Get_Impl] stripping reference"); + if (Get_Impl(valobj, clang_type.GetNonReferenceType(), entry, use_dynamic, reason) && !entry->SkipsReferences()) + { + reason |= lldb_private::eFormatterChoiceCriterionStrippedPointerReference; + return true; + } + } + else if (clang_type.IsPointerType()) + { + if (log) + log->Printf("[Get_Impl] stripping pointer"); + if (Get_Impl(valobj, clang_type.GetPointeeType(), entry, use_dynamic, reason) && !entry->SkipsPointers()) + { + reason |= lldb_private::eFormatterChoiceCriterionStrippedPointerReference; + return true; + } + } + + bool canBeObjCDynamic = valobj.GetClangType().IsPossibleDynamicType (NULL, + false, // no C++ + true); // yes ObjC + + if (canBeObjCDynamic) + { + if (use_dynamic != lldb::eNoDynamicValues) + { + if (log) + log->Printf("[Get_Impl] allowed to figure out dynamic ObjC type"); + if (Get_ObjC(valobj,entry)) + { + reason |= lldb_private::eFormatterChoiceCriterionDynamicObjCDiscovery; + return true; + } + } + if (log) + log->Printf("[Get_Impl] dynamic disabled or failed - stripping ObjC pointer"); + if (Get_Impl(valobj, clang_type.GetPointeeType(), entry, use_dynamic, reason) && !entry->SkipsPointers()) + { + reason |= lldb_private::eFormatterChoiceCriterionStrippedPointerReference; + return true; + } + } + + // try to strip typedef chains + if (clang_type.IsTypedefType()) + { + if (log) + log->Printf("[Get_Impl] stripping typedef"); + if ((Get_Impl(valobj, clang_type.GetTypedefedType(), entry, use_dynamic, reason)) && entry->Cascades()) + { + reason |= lldb_private::eFormatterChoiceCriterionNavigatedTypedefs; + return true; + } + } + + // out of luck here + return false; + } + + // we are separately passing in valobj and type because the valobj is fixed (and is used for ObjC discovery and bitfield size) + // but the type can change (e.g. stripping pointers, ...) + bool Get (ValueObject& valobj, + ClangASTType clang_type, + MapValueType& entry, + lldb::DynamicValueType use_dynamic, + uint32_t& reason) + { + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_TYPES)); + + if (Get_Impl (valobj, clang_type, entry, use_dynamic, reason)) + return true; + + // try going to the unqualified type + do { + if (log) + log->Printf("[Get] trying the unqualified type"); + if (!clang_type.IsValid()) + break; + + ClangASTType unqual_clang_ast_type = clang_type.GetFullyUnqualifiedType(); + if (!unqual_clang_ast_type.IsValid()) + { + if (log) + log->Printf("[Get] could not get the unqual_clang_ast_type"); + break; + } + if (unqual_clang_ast_type.GetOpaqueQualType() != clang_type.GetOpaqueQualType()) + { + if (log) + log->Printf("[Get] unqualified type is there and is not the same, let's try"); + if (Get_Impl (valobj, unqual_clang_ast_type,entry, use_dynamic, reason)) + return true; + } + else if (log) + log->Printf("[Get] unqualified type same as original type"); + } while(false); + + // if all else fails, go to static type + if (valobj.IsDynamic()) + { + if (log) + log->Printf("[Get] going to static value"); + lldb::ValueObjectSP static_value_sp(valobj.GetStaticValue()); + if (static_value_sp) + { + if (log) + log->Printf("[Get] has a static value - actually use it"); + if (Get(*static_value_sp.get(), static_value_sp->GetClangType(), entry, use_dynamic, reason)) + { + reason |= lldb_private::eFormatterChoiceCriterionWentToStaticValue; + return true; + } + } + } + + return false; + } +}; + +} // namespace lldb_private + +#endif // lldb_FormatNavigator_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/DataFormatters/TypeCategory.h b/contrib/llvm/tools/lldb/include/lldb/DataFormatters/TypeCategory.h new file mode 100644 index 00000000000..b76d84f4772 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/DataFormatters/TypeCategory.h @@ -0,0 +1,230 @@ +//===-- TypeCategory.h -------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef lldb_TypeCategory_h_ +#define lldb_TypeCategory_h_ + +// C Includes +// C++ Includes + +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-public.h" +#include "lldb/lldb-enumerations.h" + +#include "lldb/DataFormatters/FormatNavigator.h" + +namespace lldb_private { + class TypeCategoryImpl + { + private: + + typedef FormatNavigator SummaryNavigator; + typedef FormatNavigator RegexSummaryNavigator; + + typedef FormatNavigator FilterNavigator; + typedef FormatNavigator RegexFilterNavigator; + +#ifndef LLDB_DISABLE_PYTHON + typedef FormatNavigator SynthNavigator; + typedef FormatNavigator RegexSynthNavigator; +#endif // #ifndef LLDB_DISABLE_PYTHON + + typedef SummaryNavigator::MapType SummaryMap; + typedef RegexSummaryNavigator::MapType RegexSummaryMap; + typedef FilterNavigator::MapType FilterMap; + typedef RegexFilterNavigator::MapType RegexFilterMap; +#ifndef LLDB_DISABLE_PYTHON + typedef SynthNavigator::MapType SynthMap; + typedef RegexSynthNavigator::MapType RegexSynthMap; +#endif // #ifndef LLDB_DISABLE_PYTHON + + public: + + typedef uint16_t FormatCategoryItems; + static const uint16_t ALL_ITEM_TYPES = UINT16_MAX; + + typedef SummaryNavigator::SharedPointer SummaryNavigatorSP; + typedef RegexSummaryNavigator::SharedPointer RegexSummaryNavigatorSP; + typedef FilterNavigator::SharedPointer FilterNavigatorSP; + typedef RegexFilterNavigator::SharedPointer RegexFilterNavigatorSP; +#ifndef LLDB_DISABLE_PYTHON + typedef SynthNavigator::SharedPointer SynthNavigatorSP; + typedef RegexSynthNavigator::SharedPointer RegexSynthNavigatorSP; +#endif // #ifndef LLDB_DISABLE_PYTHON + + TypeCategoryImpl (IFormatChangeListener* clist, + ConstString name); + + SummaryNavigatorSP + GetSummaryNavigator () + { + return SummaryNavigatorSP(m_summary_nav); + } + + RegexSummaryNavigatorSP + GetRegexSummaryNavigator () + { + return RegexSummaryNavigatorSP(m_regex_summary_nav); + } + + FilterNavigatorSP + GetFilterNavigator () + { + return FilterNavigatorSP(m_filter_nav); + } + + RegexFilterNavigatorSP + GetRegexFilterNavigator () + { + return RegexFilterNavigatorSP(m_regex_filter_nav); + } + + SummaryNavigator::MapValueType + GetSummaryForType (lldb::TypeNameSpecifierImplSP type_sp); + + FilterNavigator::MapValueType + GetFilterForType (lldb::TypeNameSpecifierImplSP type_sp); + +#ifndef LLDB_DISABLE_PYTHON + SynthNavigator::MapValueType + GetSyntheticForType (lldb::TypeNameSpecifierImplSP type_sp); +#endif + + lldb::TypeNameSpecifierImplSP + GetTypeNameSpecifierForSummaryAtIndex (size_t index); + + SummaryNavigator::MapValueType + GetSummaryAtIndex (size_t index); + + FilterNavigator::MapValueType + GetFilterAtIndex (size_t index); + + lldb::TypeNameSpecifierImplSP + GetTypeNameSpecifierForFilterAtIndex (size_t index); + +#ifndef LLDB_DISABLE_PYTHON + SynthNavigatorSP + GetSyntheticNavigator () + { + return SynthNavigatorSP(m_synth_nav); + } + + RegexSynthNavigatorSP + GetRegexSyntheticNavigator () + { + return RegexSynthNavigatorSP(m_regex_synth_nav); + } + + SynthNavigator::MapValueType + GetSyntheticAtIndex (size_t index); + + lldb::TypeNameSpecifierImplSP + GetTypeNameSpecifierForSyntheticAtIndex (size_t index); + +#endif // #ifndef LLDB_DISABLE_PYTHON + + bool + IsEnabled () const + { + return m_enabled; + } + + uint32_t + GetEnabledPosition() + { + if (m_enabled == false) + return UINT32_MAX; + else + return m_enabled_position; + } + + bool + Get (ValueObject& valobj, + lldb::TypeSummaryImplSP& entry, + lldb::DynamicValueType use_dynamic, + uint32_t* reason = NULL); + + bool + Get (ValueObject& valobj, + lldb::SyntheticChildrenSP& entry, + lldb::DynamicValueType use_dynamic, + uint32_t* reason = NULL); + + void + Clear (FormatCategoryItems items = ALL_ITEM_TYPES); + + bool + Delete (ConstString name, + FormatCategoryItems items = ALL_ITEM_TYPES); + + uint32_t + GetCount (FormatCategoryItems items = ALL_ITEM_TYPES); + + const char* + GetName () + { + return m_name.GetCString(); + } + + bool + AnyMatches (ConstString type_name, + FormatCategoryItems items = ALL_ITEM_TYPES, + bool only_enabled = true, + const char** matching_category = NULL, + FormatCategoryItems* matching_type = NULL); + + typedef std::shared_ptr SharedPointer; + + private: + SummaryNavigator::SharedPointer m_summary_nav; + RegexSummaryNavigator::SharedPointer m_regex_summary_nav; + FilterNavigator::SharedPointer m_filter_nav; + RegexFilterNavigator::SharedPointer m_regex_filter_nav; +#ifndef LLDB_DISABLE_PYTHON + SynthNavigator::SharedPointer m_synth_nav; + RegexSynthNavigator::SharedPointer m_regex_synth_nav; +#endif // #ifndef LLDB_DISABLE_PYTHON + + bool m_enabled; + + IFormatChangeListener* m_change_listener; + + Mutex m_mutex; + + ConstString m_name; + + uint32_t m_enabled_position; + + void + Enable (bool value, uint32_t position); + + void + Disable () + { + Enable(false, UINT32_MAX); + } + + friend class TypeCategoryMap; + + friend class FormatNavigator; + friend class FormatNavigator; + + friend class FormatNavigator; + friend class FormatNavigator; + +#ifndef LLDB_DISABLE_PYTHON + friend class FormatNavigator; + friend class FormatNavigator; +#endif // #ifndef LLDB_DISABLE_PYTHON + }; + +} // namespace lldb_private + +#endif // lldb_TypeCategory_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/DataFormatters/TypeCategoryMap.h b/contrib/llvm/tools/lldb/include/lldb/DataFormatters/TypeCategoryMap.h new file mode 100644 index 00000000000..c2465ad13aa --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/DataFormatters/TypeCategoryMap.h @@ -0,0 +1,148 @@ +//===-- TypeCategoryMap.h ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef lldb_TypeCategoryMap_h_ +#define lldb_TypeCategoryMap_h_ + +// C Includes +// C++ Includes + +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-public.h" +#include "lldb/lldb-enumerations.h" + +#include "lldb/DataFormatters/FormatNavigator.h" +#include "lldb/DataFormatters/TypeCategory.h" + +namespace lldb_private { + class TypeCategoryMap + { + private: + typedef ConstString KeyType; + typedef TypeCategoryImpl ValueType; + typedef ValueType::SharedPointer ValueSP; + typedef std::list ActiveCategoriesList; + typedef ActiveCategoriesList::iterator ActiveCategoriesIterator; + + public: + typedef std::map MapType; + typedef MapType::iterator MapIterator; + typedef bool(*CallbackType)(void*, const ValueSP&); + typedef uint32_t Position; + + static const Position First = 0; + static const Position Default = 1; + static const Position Last = UINT32_MAX; + + TypeCategoryMap (IFormatChangeListener* lst); + + void + Add (KeyType name, + const ValueSP& entry); + + bool + Delete (KeyType name); + + bool + Enable (KeyType category_name, + Position pos = Default); + + bool + Disable (KeyType category_name); + + bool + Enable (ValueSP category, + Position pos = Default); + + bool + Disable (ValueSP category); + + void + Clear (); + + bool + Get (KeyType name, + ValueSP& entry); + + bool + Get (uint32_t pos, + ValueSP& entry); + + void + LoopThrough (CallbackType callback, void* param); + + lldb::TypeCategoryImplSP + GetAtIndex (uint32_t); + + bool + AnyMatches (ConstString type_name, + TypeCategoryImpl::FormatCategoryItems items = TypeCategoryImpl::ALL_ITEM_TYPES, + bool only_enabled = true, + const char** matching_category = NULL, + TypeCategoryImpl::FormatCategoryItems* matching_type = NULL); + + uint32_t + GetCount () + { + return m_map.size(); + } + + lldb::TypeSummaryImplSP + GetSummaryFormat (ValueObject& valobj, + lldb::DynamicValueType use_dynamic); + +#ifndef LLDB_DISABLE_PYTHON + lldb::SyntheticChildrenSP + GetSyntheticChildren (ValueObject& valobj, + lldb::DynamicValueType use_dynamic); +#endif + + private: + + class delete_matching_categories + { + lldb::TypeCategoryImplSP ptr; + public: + delete_matching_categories(lldb::TypeCategoryImplSP p) : ptr(p) + {} + + bool operator()(const lldb::TypeCategoryImplSP& other) + { + return ptr.get() == other.get(); + } + }; + + Mutex m_map_mutex; + IFormatChangeListener* listener; + + MapType m_map; + ActiveCategoriesList m_active_categories; + + MapType& map () + { + return m_map; + } + + ActiveCategoriesList& active_list () + { + return m_active_categories; + } + + Mutex& mutex () + { + return m_map_mutex; + } + + friend class FormatNavigator; + friend class FormatManager; + }; +} // namespace lldb_private + +#endif // lldb_TypeCategoryMap_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/DataFormatters/TypeFormat.h b/contrib/llvm/tools/lldb/include/lldb/DataFormatters/TypeFormat.h new file mode 100644 index 00000000000..77135c448ed --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/DataFormatters/TypeFormat.h @@ -0,0 +1,220 @@ +//===-- TypeFormat.h ----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef lldb_TypeFormat_h_ +#define lldb_TypeFormat_h_ + +// C Includes + +// C++ Includes +#include + +// Other libraries and framework includes + +// Project includes +#include "lldb/lldb-public.h" +#include "lldb/lldb-enumerations.h" + +#include "lldb/Core/ValueObject.h" + +namespace lldb_private { + class TypeFormatImpl + { + public: + class Flags + { + public: + + Flags () : + m_flags (lldb::eTypeOptionCascade) + {} + + Flags (const Flags& other) : + m_flags (other.m_flags) + {} + + Flags (uint32_t value) : + m_flags (value) + {} + + Flags& + operator = (const Flags& rhs) + { + if (&rhs != this) + m_flags = rhs.m_flags; + + return *this; + } + + Flags& + operator = (const uint32_t& rhs) + { + m_flags = rhs; + return *this; + } + + Flags& + Clear() + { + m_flags = 0; + return *this; + } + + bool + GetCascades () const + { + return (m_flags & lldb::eTypeOptionCascade) == lldb::eTypeOptionCascade; + } + + Flags& + SetCascades (bool value = true) + { + if (value) + m_flags |= lldb::eTypeOptionCascade; + else + m_flags &= ~lldb::eTypeOptionCascade; + return *this; + } + + bool + GetSkipPointers () const + { + return (m_flags & lldb::eTypeOptionSkipPointers) == lldb::eTypeOptionSkipPointers; + } + + Flags& + SetSkipPointers (bool value = true) + { + if (value) + m_flags |= lldb::eTypeOptionSkipPointers; + else + m_flags &= ~lldb::eTypeOptionSkipPointers; + return *this; + } + + bool + GetSkipReferences () const + { + return (m_flags & lldb::eTypeOptionSkipReferences) == lldb::eTypeOptionSkipReferences; + } + + Flags& + SetSkipReferences (bool value = true) + { + if (value) + m_flags |= lldb::eTypeOptionSkipReferences; + else + m_flags &= ~lldb::eTypeOptionSkipReferences; + return *this; + } + + uint32_t + GetValue () + { + return m_flags; + } + + void + SetValue (uint32_t value) + { + m_flags = value; + } + + private: + uint32_t m_flags; + }; + + TypeFormatImpl (lldb::Format f = lldb::eFormatInvalid, + const Flags& flags = Flags()); + + typedef std::shared_ptr SharedPointer; + typedef bool(*ValueCallback)(void*, ConstString, const lldb::TypeFormatImplSP&); + + ~TypeFormatImpl () + { + } + + bool + Cascades () const + { + return m_flags.GetCascades(); + } + bool + SkipsPointers () const + { + return m_flags.GetSkipPointers(); + } + bool + SkipsReferences () const + { + return m_flags.GetSkipReferences(); + } + + void + SetCascades (bool value) + { + m_flags.SetCascades(value); + } + + void + SetSkipsPointers (bool value) + { + m_flags.SetSkipPointers(value); + } + + void + SetSkipsReferences (bool value) + { + m_flags.SetSkipReferences(value); + } + + lldb::Format + GetFormat () const + { + return m_format; + } + + void + SetFormat (lldb::Format fmt) + { + m_format = fmt; + } + + uint32_t + GetOptions () + { + return m_flags.GetValue(); + } + + void + SetOptions (uint32_t value) + { + m_flags.SetValue(value); + } + + uint32_t& + GetRevision () + { + return m_my_revision; + } + + std::string + GetDescription(); + + protected: + Flags m_flags; + lldb::Format m_format; + uint32_t m_my_revision; + + private: + DISALLOW_COPY_AND_ASSIGN(TypeFormatImpl); + }; +} // namespace lldb_private + +#endif // lldb_TypeFormat_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/DataFormatters/TypeSummary.h b/contrib/llvm/tools/lldb/include/lldb/DataFormatters/TypeSummary.h new file mode 100644 index 00000000000..2183384b9d6 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/DataFormatters/TypeSummary.h @@ -0,0 +1,547 @@ +//===-- TypeSummary.h --------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef lldb_TypeSummary_h_ +#define lldb_TypeSummary_h_ + +// C Includes +#include +#include + +// C++ Includes +#include +#include + +// Other libraries and framework includes + +// Project includes +#include "lldb/lldb-public.h" +#include "lldb/lldb-enumerations.h" + +#include "lldb/Core/ValueObject.h" +#include "lldb/Interpreter/ScriptInterpreterPython.h" +#include "lldb/Symbol/Type.h" + +namespace lldb_private { + + class TypeSummaryImpl + { + public: + class Flags + { + public: + + Flags () : + m_flags (lldb::eTypeOptionCascade) + {} + + Flags (const Flags& other) : + m_flags (other.m_flags) + {} + + Flags (uint32_t value) : + m_flags (value) + {} + + Flags& + operator = (const Flags& rhs) + { + if (&rhs != this) + m_flags = rhs.m_flags; + + return *this; + } + + Flags& + operator = (const uint32_t& rhs) + { + m_flags = rhs; + return *this; + } + + Flags& + Clear() + { + m_flags = 0; + return *this; + } + + bool + GetCascades () const + { + return (m_flags & lldb::eTypeOptionCascade) == lldb::eTypeOptionCascade; + } + + Flags& + SetCascades (bool value = true) + { + if (value) + m_flags |= lldb::eTypeOptionCascade; + else + m_flags &= ~lldb::eTypeOptionCascade; + return *this; + } + + bool + GetSkipPointers () const + { + return (m_flags & lldb::eTypeOptionSkipPointers) == lldb::eTypeOptionSkipPointers; + } + + Flags& + SetSkipPointers (bool value = true) + { + if (value) + m_flags |= lldb::eTypeOptionSkipPointers; + else + m_flags &= ~lldb::eTypeOptionSkipPointers; + return *this; + } + + bool + GetSkipReferences () const + { + return (m_flags & lldb::eTypeOptionSkipReferences) == lldb::eTypeOptionSkipReferences; + } + + Flags& + SetSkipReferences (bool value = true) + { + if (value) + m_flags |= lldb::eTypeOptionSkipReferences; + else + m_flags &= ~lldb::eTypeOptionSkipReferences; + return *this; + } + + bool + GetDontShowChildren () const + { + return (m_flags & lldb::eTypeOptionHideChildren) == lldb::eTypeOptionHideChildren; + } + + Flags& + SetDontShowChildren (bool value = true) + { + if (value) + m_flags |= lldb::eTypeOptionHideChildren; + else + m_flags &= ~lldb::eTypeOptionHideChildren; + return *this; + } + + bool + GetDontShowValue () const + { + return (m_flags & lldb::eTypeOptionHideValue) == lldb::eTypeOptionHideValue; + } + + Flags& + SetDontShowValue (bool value = true) + { + if (value) + m_flags |= lldb::eTypeOptionHideValue; + else + m_flags &= ~lldb::eTypeOptionHideValue; + return *this; + } + + bool + GetShowMembersOneLiner () const + { + return (m_flags & lldb::eTypeOptionShowOneLiner) == lldb::eTypeOptionShowOneLiner; + } + + Flags& + SetShowMembersOneLiner (bool value = true) + { + if (value) + m_flags |= lldb::eTypeOptionShowOneLiner; + else + m_flags &= ~lldb::eTypeOptionShowOneLiner; + return *this; + } + + bool + GetHideItemNames () const + { + return (m_flags & lldb::eTypeOptionHideNames) == lldb::eTypeOptionHideNames; + } + + Flags& + SetHideItemNames (bool value = true) + { + if (value) + m_flags |= lldb::eTypeOptionHideNames; + else + m_flags &= ~lldb::eTypeOptionHideNames; + return *this; + } + + uint32_t + GetValue () + { + return m_flags; + } + + void + SetValue (uint32_t value) + { + m_flags = value; + } + + private: + uint32_t m_flags; + }; + + typedef enum Type + { + eTypeUnknown, + eTypeString, + eTypeScript, + eTypeCallback + } Type; + + TypeSummaryImpl (const TypeSummaryImpl::Flags& flags); + + bool + Cascades () const + { + return m_flags.GetCascades(); + } + bool + SkipsPointers () const + { + return m_flags.GetSkipPointers(); + } + bool + SkipsReferences () const + { + return m_flags.GetSkipReferences(); + } + + bool + DoesPrintChildren () const + { + return !m_flags.GetDontShowChildren(); + } + + bool + DoesPrintValue () const + { + return !m_flags.GetDontShowValue(); + } + + bool + IsOneliner () const + { + return m_flags.GetShowMembersOneLiner(); + } + + bool + HideNames () const + { + return m_flags.GetHideItemNames(); + } + + void + SetCascades (bool value) + { + m_flags.SetCascades(value); + } + + void + SetSkipsPointers (bool value) + { + m_flags.SetSkipPointers(value); + } + + void + SetSkipsReferences (bool value) + { + m_flags.SetSkipReferences(value); + } + + void + SetDoesPrintChildren (bool value) + { + m_flags.SetDontShowChildren(!value); + } + + void + SetDoesPrintValue (bool value) + { + m_flags.SetDontShowValue(!value); + } + + void + SetIsOneliner (bool value) + { + m_flags.SetShowMembersOneLiner(value); + } + + void + SetHideNames (bool value) + { + m_flags.SetHideItemNames(value); + } + + uint32_t + GetOptions () + { + return m_flags.GetValue(); + } + + void + SetOptions (uint32_t value) + { + m_flags.SetValue(value); + } + + virtual + ~TypeSummaryImpl () + { + } + + // we are using a ValueObject* instead of a ValueObjectSP because we do not need to hold on to this for + // extended periods of time and we trust the ValueObject to stay around for as long as it is required + // for us to generate its summary + virtual bool + FormatObject (ValueObject *valobj, + std::string& dest) = 0; + + virtual std::string + GetDescription () = 0; + + virtual bool + IsScripted () = 0; + + virtual Type + GetType () = 0; + + uint32_t& + GetRevision () + { + return m_my_revision; + } + + typedef std::shared_ptr SharedPointer; + typedef bool(*SummaryCallback)(void*, ConstString, const lldb::TypeSummaryImplSP&); + typedef bool(*RegexSummaryCallback)(void*, lldb::RegularExpressionSP, const lldb::TypeSummaryImplSP&); + + protected: + uint32_t m_my_revision; + Flags m_flags; + + private: + DISALLOW_COPY_AND_ASSIGN(TypeSummaryImpl); + }; + + // simple string-based summaries, using ${var to show data + struct StringSummaryFormat : public TypeSummaryImpl + { + std::string m_format; + + StringSummaryFormat(const TypeSummaryImpl::Flags& flags, + const char* f); + + const char* + GetSummaryString () const + { + return m_format.c_str(); + } + + void + SetSummaryString (const char* data) + { + if (data) + m_format.assign(data); + else + m_format.clear(); + } + + virtual + ~StringSummaryFormat() + { + } + + virtual bool + FormatObject(ValueObject *valobj, + std::string& dest); + + virtual std::string + GetDescription(); + + virtual bool + IsScripted () + { + return false; + } + + + virtual Type + GetType () + { + return TypeSummaryImpl::eTypeString; + } + + private: + DISALLOW_COPY_AND_ASSIGN(StringSummaryFormat); + }; + + // summaries implemented via a C++ function + struct CXXFunctionSummaryFormat : public TypeSummaryImpl + { + + // we should convert these to SBValue and SBStream if we ever cross + // the boundary towards the external world + typedef bool (*Callback)(ValueObject& valobj, Stream& dest); + + Callback m_impl; + std::string m_description; + + CXXFunctionSummaryFormat (const TypeSummaryImpl::Flags& flags, + Callback impl, + const char* description); + + Callback + GetBackendFunction () const + { + return m_impl; + } + + const char* + GetTextualInfo () const + { + return m_description.c_str(); + } + + void + SetBackendFunction (Callback cb_func) + { + m_impl = cb_func; + } + + void + SetTextualInfo (const char* descr) + { + if (descr) + m_description.assign(descr); + else + m_description.clear(); + } + + virtual + ~CXXFunctionSummaryFormat () + { + } + + virtual bool + FormatObject (ValueObject *valobj, + std::string& dest); + + virtual std::string + GetDescription (); + + virtual bool + IsScripted () + { + return false; + } + + virtual Type + GetType () + { + return TypeSummaryImpl::eTypeCallback; + } + + typedef std::shared_ptr SharedPointer; + + private: + DISALLOW_COPY_AND_ASSIGN(CXXFunctionSummaryFormat); + }; + +#ifndef LLDB_DISABLE_PYTHON + + // Python-based summaries, running script code to show data + struct ScriptSummaryFormat : public TypeSummaryImpl + { + std::string m_function_name; + std::string m_python_script; + lldb::ScriptInterpreterObjectSP m_script_function_sp; + + ScriptSummaryFormat(const TypeSummaryImpl::Flags& flags, + const char *function_name, + const char* python_script = NULL); + + const char* + GetFunctionName () const + { + return m_function_name.c_str(); + } + + const char* + GetPythonScript () const + { + return m_python_script.c_str(); + } + + void + SetFunctionName (const char* function_name) + { + if (function_name) + m_function_name.assign(function_name); + else + m_function_name.clear(); + m_python_script.clear(); + } + + void + SetPythonScript (const char* script) + { + if (script) + m_python_script.assign(script); + else + m_python_script.clear(); + } + + virtual + ~ScriptSummaryFormat () + { + } + + virtual bool + FormatObject (ValueObject *valobj, + std::string& dest); + + virtual std::string + GetDescription (); + + virtual bool + IsScripted () + { + return true; + } + + virtual Type + GetType () + { + return TypeSummaryImpl::eTypeScript; + } + + typedef std::shared_ptr SharedPointer; + + + private: + DISALLOW_COPY_AND_ASSIGN(ScriptSummaryFormat); + }; +#endif +} // namespace lldb_private + +#endif // lldb_TypeSummary_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/DataFormatters/TypeSynthetic.h b/contrib/llvm/tools/lldb/include/lldb/DataFormatters/TypeSynthetic.h new file mode 100644 index 00000000000..a32f4b76117 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/DataFormatters/TypeSynthetic.h @@ -0,0 +1,594 @@ +//===-- TypeSynthetic.h -------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef lldb_TypeSynthetic_h_ +#define lldb_TypeSynthetic_h_ + +// C Includes +#include +#include + +// C++ Includes +#include +#include + +// Other libraries and framework includes + +// Project includes +#include "lldb/lldb-public.h" +#include "lldb/lldb-enumerations.h" + +#include "lldb/Core/ValueObject.h" +#include "lldb/Interpreter/ScriptInterpreterPython.h" +#include "lldb/Symbol/Type.h" + +namespace lldb_private { + class SyntheticChildrenFrontEnd + { + protected: + ValueObject &m_backend; + public: + + SyntheticChildrenFrontEnd (ValueObject &backend) : + m_backend(backend) + {} + + virtual + ~SyntheticChildrenFrontEnd () + { + } + + virtual size_t + CalculateNumChildren () = 0; + + virtual lldb::ValueObjectSP + GetChildAtIndex (size_t idx) = 0; + + virtual size_t + GetIndexOfChildWithName (const ConstString &name) = 0; + + // this function is assumed to always succeed and it if fails, the front-end should know to deal + // with it in the correct way (most probably, by refusing to return any children) + // the return value of Update() should actually be interpreted as "ValueObjectSyntheticFilter cache is good/bad" + // if =true, ValueObjectSyntheticFilter is allowed to use the children it fetched previously and cached + // if =false, ValueObjectSyntheticFilter must throw away its cache, and query again for children + virtual bool + Update () = 0; + + // if this function returns false, then CalculateNumChildren() MUST return 0 since UI frontends + // might validly decide not to inquire for children given a false return value from this call + // if it returns true, then CalculateNumChildren() can return any number >= 0 (0 being valid) + // it should if at all possible be more efficient than CalculateNumChildren() + virtual bool + MightHaveChildren () = 0; + + typedef std::shared_ptr SharedPointer; + typedef std::unique_ptr AutoPointer; + + private: + DISALLOW_COPY_AND_ASSIGN(SyntheticChildrenFrontEnd); + }; + + class SyntheticChildren + { + public: + + class Flags + { + public: + + Flags () : + m_flags (lldb::eTypeOptionCascade) + {} + + Flags (const Flags& other) : + m_flags (other.m_flags) + {} + + Flags (uint32_t value) : + m_flags (value) + {} + + Flags& + operator = (const Flags& rhs) + { + if (&rhs != this) + m_flags = rhs.m_flags; + + return *this; + } + + Flags& + operator = (const uint32_t& rhs) + { + m_flags = rhs; + return *this; + } + + Flags& + Clear() + { + m_flags = 0; + return *this; + } + + bool + GetCascades () const + { + return (m_flags & lldb::eTypeOptionCascade) == lldb::eTypeOptionCascade; + } + + Flags& + SetCascades (bool value = true) + { + if (value) + m_flags |= lldb::eTypeOptionCascade; + else + m_flags &= ~lldb::eTypeOptionCascade; + return *this; + } + + bool + GetSkipPointers () const + { + return (m_flags & lldb::eTypeOptionSkipPointers) == lldb::eTypeOptionSkipPointers; + } + + Flags& + SetSkipPointers (bool value = true) + { + if (value) + m_flags |= lldb::eTypeOptionSkipPointers; + else + m_flags &= ~lldb::eTypeOptionSkipPointers; + return *this; + } + + bool + GetSkipReferences () const + { + return (m_flags & lldb::eTypeOptionSkipReferences) == lldb::eTypeOptionSkipReferences; + } + + Flags& + SetSkipReferences (bool value = true) + { + if (value) + m_flags |= lldb::eTypeOptionSkipReferences; + else + m_flags &= ~lldb::eTypeOptionSkipReferences; + return *this; + } + + uint32_t + GetValue () + { + return m_flags; + } + + void + SetValue (uint32_t value) + { + m_flags = value; + } + + private: + uint32_t m_flags; + }; + + SyntheticChildren (const Flags& flags) : + m_flags(flags) + { + } + + virtual + ~SyntheticChildren () + { + } + + bool + Cascades () const + { + return m_flags.GetCascades(); + } + bool + SkipsPointers () const + { + return m_flags.GetSkipPointers(); + } + bool + SkipsReferences () const + { + return m_flags.GetSkipReferences(); + } + + void + SetCascades (bool value) + { + m_flags.SetCascades(value); + } + + void + SetSkipsPointers (bool value) + { + m_flags.SetSkipPointers(value); + } + + void + SetSkipsReferences (bool value) + { + m_flags.SetSkipReferences(value); + } + + uint32_t + GetOptions () + { + return m_flags.GetValue(); + } + + void + SetOptions (uint32_t value) + { + m_flags.SetValue(value); + } + + virtual bool + IsScripted () = 0; + + virtual std::string + GetDescription () = 0; + + virtual SyntheticChildrenFrontEnd::AutoPointer + GetFrontEnd (ValueObject &backend) = 0; + + typedef std::shared_ptr SharedPointer; + typedef bool(*SyntheticChildrenCallback)(void*, ConstString, const SyntheticChildren::SharedPointer&); + + uint32_t& + GetRevision () + { + return m_my_revision; + } + + protected: + uint32_t m_my_revision; + Flags m_flags; + + private: + DISALLOW_COPY_AND_ASSIGN(SyntheticChildren); + }; + + class TypeFilterImpl : public SyntheticChildren + { + std::vector m_expression_paths; + public: + TypeFilterImpl(const SyntheticChildren::Flags& flags) : + SyntheticChildren(flags), + m_expression_paths() + { + } + + TypeFilterImpl(const SyntheticChildren::Flags& flags, + const std::initializer_list items) : + SyntheticChildren(flags), + m_expression_paths() + { + for (auto path : items) + AddExpressionPath (path); + } + + void + AddExpressionPath (const char* path) + { + AddExpressionPath(std::string(path)); + } + + void + Clear() + { + m_expression_paths.clear(); + } + + size_t + GetCount() const + { + return m_expression_paths.size(); + } + + const char* + GetExpressionPathAtIndex(size_t i) const + { + return m_expression_paths[i].c_str(); + } + + bool + SetExpressionPathAtIndex (size_t i, const char* path) + { + return SetExpressionPathAtIndex(i, std::string(path)); + } + + void + AddExpressionPath (const std::string& path) + { + bool need_add_dot = true; + if (path[0] == '.' || + (path[0] == '-' && path[1] == '>') || + path[0] == '[') + need_add_dot = false; + // add a '.' symbol to help forgetful users + if(!need_add_dot) + m_expression_paths.push_back(path); + else + m_expression_paths.push_back(std::string(".") + path); + } + + bool + SetExpressionPathAtIndex (size_t i, const std::string& path) + { + if (i >= GetCount()) + return false; + bool need_add_dot = true; + if (path[0] == '.' || + (path[0] == '-' && path[1] == '>') || + path[0] == '[') + need_add_dot = false; + // add a '.' symbol to help forgetful users + if(!need_add_dot) + m_expression_paths[i] = path; + else + m_expression_paths[i] = std::string(".") + path; + return true; + } + + bool + IsScripted () + { + return false; + } + + std::string + GetDescription (); + + class FrontEnd : public SyntheticChildrenFrontEnd + { + private: + TypeFilterImpl* filter; + public: + + FrontEnd(TypeFilterImpl* flt, + ValueObject &backend) : + SyntheticChildrenFrontEnd(backend), + filter(flt) + {} + + virtual + ~FrontEnd () + { + } + + virtual size_t + CalculateNumChildren () + { + return filter->GetCount(); + } + + virtual lldb::ValueObjectSP + GetChildAtIndex (size_t idx) + { + if (idx >= filter->GetCount()) + return lldb::ValueObjectSP(); + return m_backend.GetSyntheticExpressionPathChild(filter->GetExpressionPathAtIndex(idx), true); + } + + virtual bool + Update() { return false; } + + virtual bool + MightHaveChildren () + { + return filter->GetCount() > 0; + } + + virtual size_t + GetIndexOfChildWithName (const ConstString &name) + { + const char* name_cstr = name.GetCString(); + for (size_t i = 0; i < filter->GetCount(); i++) + { + const char* expr_cstr = filter->GetExpressionPathAtIndex(i); + if (expr_cstr) + { + if (*expr_cstr == '.') + expr_cstr++; + else if (*expr_cstr == '-' && *(expr_cstr+1) == '>') + expr_cstr += 2; + } + if (!::strcmp(name_cstr, expr_cstr)) + return i; + } + return UINT32_MAX; + } + + typedef std::shared_ptr SharedPointer; + + private: + DISALLOW_COPY_AND_ASSIGN(FrontEnd); + }; + + virtual SyntheticChildrenFrontEnd::AutoPointer + GetFrontEnd(ValueObject &backend) + { + return SyntheticChildrenFrontEnd::AutoPointer(new FrontEnd(this, backend)); + } + + private: + DISALLOW_COPY_AND_ASSIGN(TypeFilterImpl); + }; + + class CXXSyntheticChildren : public SyntheticChildren + { + public: + typedef SyntheticChildrenFrontEnd* (*CreateFrontEndCallback) (CXXSyntheticChildren*, lldb::ValueObjectSP); + protected: + CreateFrontEndCallback m_create_callback; + std::string m_description; + public: + CXXSyntheticChildren (const SyntheticChildren::Flags& flags, + const char* description, + CreateFrontEndCallback callback) : + SyntheticChildren(flags), + m_create_callback(callback), + m_description(description ? description : "") + { + } + + bool + IsScripted () + { + return false; + } + + std::string + GetDescription (); + + virtual SyntheticChildrenFrontEnd::AutoPointer + GetFrontEnd (ValueObject &backend) + { + return SyntheticChildrenFrontEnd::AutoPointer(m_create_callback(this, backend.GetSP())); + } + + private: + DISALLOW_COPY_AND_ASSIGN(CXXSyntheticChildren); + }; + +#ifndef LLDB_DISABLE_PYTHON + + class ScriptedSyntheticChildren : public SyntheticChildren + { + std::string m_python_class; + std::string m_python_code; + public: + + ScriptedSyntheticChildren (const SyntheticChildren::Flags& flags, + const char* pclass, + const char* pcode = NULL) : + SyntheticChildren(flags), + m_python_class(), + m_python_code() + { + if (pclass) + m_python_class = pclass; + if (pcode) + m_python_code = pcode; + } + + const char* + GetPythonClassName () + { + return m_python_class.c_str(); + } + + const char* + GetPythonCode () + { + return m_python_code.c_str(); + } + + void + SetPythonClassName (const char* fname) + { + m_python_class.assign(fname); + m_python_code.clear(); + } + + void + SetPythonCode (const char* script) + { + m_python_code.assign(script); + } + + std::string + GetDescription (); + + bool + IsScripted () + { + return true; + } + + class FrontEnd : public SyntheticChildrenFrontEnd + { + private: + std::string m_python_class; + lldb::ScriptInterpreterObjectSP m_wrapper_sp; + ScriptInterpreter *m_interpreter; + public: + + FrontEnd (std::string pclass, + ValueObject &backend); + + virtual + ~FrontEnd (); + + virtual size_t + CalculateNumChildren () + { + if (!m_wrapper_sp || m_interpreter == NULL) + return 0; + return m_interpreter->CalculateNumChildren(m_wrapper_sp); + } + + virtual lldb::ValueObjectSP + GetChildAtIndex (size_t idx); + + virtual bool + Update () + { + if (!m_wrapper_sp || m_interpreter == NULL) + return false; + + return m_interpreter->UpdateSynthProviderInstance(m_wrapper_sp); + } + + virtual bool + MightHaveChildren () + { + if (!m_wrapper_sp || m_interpreter == NULL) + return false; + + return m_interpreter->MightHaveChildrenSynthProviderInstance(m_wrapper_sp); + } + + virtual size_t + GetIndexOfChildWithName (const ConstString &name) + { + if (!m_wrapper_sp || m_interpreter == NULL) + return UINT32_MAX; + return m_interpreter->GetIndexOfChildWithName(m_wrapper_sp, name.GetCString()); + } + + typedef std::shared_ptr SharedPointer; + + private: + DISALLOW_COPY_AND_ASSIGN(FrontEnd); + }; + + virtual SyntheticChildrenFrontEnd::AutoPointer + GetFrontEnd(ValueObject &backend) + { + return SyntheticChildrenFrontEnd::AutoPointer(new FrontEnd(m_python_class, backend)); + } + + private: + DISALLOW_COPY_AND_ASSIGN(ScriptedSyntheticChildren); + }; +#endif +} // namespace lldb_private + +#endif // lldb_TypeSynthetic_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Expression/ASTDumper.h b/contrib/llvm/tools/lldb/include/lldb/Expression/ASTDumper.h new file mode 100644 index 00000000000..47f7ea460b8 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Expression/ASTDumper.h @@ -0,0 +1,43 @@ +//===-- ASTDumper.h ---------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ASTDumper_h_ +#define liblldb_ASTDumper_h_ + +#include "clang/AST/DeclVisitor.h" +#include "clang/AST/StmtVisitor.h" +#include "clang/AST/TypeVisitor.h" + +#include "lldb/Core/Stream.h" +#include "llvm/ADT/DenseSet.h" + +namespace lldb_private +{ + +class ASTDumper +{ +public: + ASTDumper (clang::Decl *decl); + ASTDumper (clang::DeclContext *decl_ctx); + ASTDumper (const clang::Type *type); + ASTDumper (clang::QualType type); + ASTDumper (lldb::clang_type_t type); + ASTDumper (const ClangASTType &clang_type); + + const char *GetCString(); + void ToSTDERR(); + void ToLog(Log *log, const char *prefix); + void ToStream(lldb::StreamSP &stream); +private: + std::string m_dump; +}; + +} // namespace lldb_private + +#endif diff --git a/contrib/llvm/tools/lldb/include/lldb/Expression/ASTResultSynthesizer.h b/contrib/llvm/tools/lldb/include/lldb/Expression/ASTResultSynthesizer.h new file mode 100644 index 00000000000..79709de3546 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Expression/ASTResultSynthesizer.h @@ -0,0 +1,184 @@ +//===-- ASTResultSynthesizer.h ----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ASTResultSynthesizer_h_ +#define liblldb_ASTResultSynthesizer_h_ + +#include "clang/Sema/SemaConsumer.h" +#include "lldb/Core/ClangForward.h" +#include "lldb/Symbol/TaggedASTType.h" + +namespace lldb_private { + +//---------------------------------------------------------------------- +/// @class ASTResultSynthesizer ASTResultSynthesizer.h "lldb/Expression/ASTResultSynthesizer.h" +/// @brief Adds a result variable declaration to the ASTs for an expression. +/// +/// Users expect the expression "i + 3" to return a result, even if a result +/// variable wasn't specifically declared. To fulfil this requirement, LLDB adds +/// a result variable to the expression, transforming it to +/// "int $__lldb_expr_result = i + 3." The IR transformers ensure that the +/// resulting variable is mapped to the right piece of memory. +/// ASTResultSynthesizer's job is to add the variable and its initialization to +/// the ASTs for the expression, and it does so by acting as a SemaConsumer for +/// Clang. +//---------------------------------------------------------------------- +class ASTResultSynthesizer : public clang::SemaConsumer +{ +public: + //---------------------------------------------------------------------- + /// Constructor + /// + /// @param[in] passthrough + /// Since the ASTs must typically go through to the Clang code generator + /// in order to produce LLVM IR, this SemaConsumer must allow them to + /// pass to the next step in the chain after processing. Passthrough is + /// the next ASTConsumer, or NULL if none is required. + /// + /// @param[in] target + /// The target, which contains the persistent variable store and the + /// AST importer. + //---------------------------------------------------------------------- + ASTResultSynthesizer(clang::ASTConsumer *passthrough, + Target &target); + + //---------------------------------------------------------------------- + /// Destructor + //---------------------------------------------------------------------- + ~ASTResultSynthesizer(); + + //---------------------------------------------------------------------- + /// Link this consumer with a particular AST context + /// + /// @param[in] Context + /// This AST context will be used for types and identifiers, and also + /// forwarded to the passthrough consumer, if one exists. + //---------------------------------------------------------------------- + void Initialize(clang::ASTContext &Context); + + //---------------------------------------------------------------------- + /// Examine a list of Decls to find the function $__lldb_expr and + /// transform its code + /// + /// @param[in] D + /// The list of Decls to search. These may contain LinkageSpecDecls, + /// which need to be searched recursively. That job falls to + /// TransformTopLevelDecl. + //---------------------------------------------------------------------- + bool HandleTopLevelDecl(clang::DeclGroupRef D); + + //---------------------------------------------------------------------- + /// Passthrough stub + //---------------------------------------------------------------------- + void HandleTranslationUnit(clang::ASTContext &Ctx); + + //---------------------------------------------------------------------- + /// Passthrough stub + //---------------------------------------------------------------------- + void HandleTagDeclDefinition(clang::TagDecl *D); + + //---------------------------------------------------------------------- + /// Passthrough stub + //---------------------------------------------------------------------- + void CompleteTentativeDefinition(clang::VarDecl *D); + + //---------------------------------------------------------------------- + /// Passthrough stub + //---------------------------------------------------------------------- + void HandleVTable(clang::CXXRecordDecl *RD, bool DefinitionRequired); + + //---------------------------------------------------------------------- + /// Passthrough stub + //---------------------------------------------------------------------- + void PrintStats(); + + //---------------------------------------------------------------------- + /// Set the Sema object to use when performing transforms, and pass it on + /// + /// @param[in] S + /// The Sema to use. Because Sema isn't externally visible, this class + /// casts it to an Action for actual use. + //---------------------------------------------------------------------- + void InitializeSema(clang::Sema &S); + + //---------------------------------------------------------------------- + /// Reset the Sema to NULL now that transformations are done + //---------------------------------------------------------------------- + void ForgetSema(); +private: + //---------------------------------------------------------------------- + /// Hunt the given Decl for FunctionDecls named $__lldb_expr, recursing + /// as necessary through LinkageSpecDecls, and calling SynthesizeResult on + /// anything that was found + /// + /// @param[in] D + /// The Decl to hunt. + //---------------------------------------------------------------------- + void TransformTopLevelDecl(clang::Decl *D); + + //---------------------------------------------------------------------- + /// Process an Objective-C method and produce the result variable and + /// initialization + /// + /// @param[in] MethodDecl + /// The method to process. + //---------------------------------------------------------------------- + bool SynthesizeObjCMethodResult(clang::ObjCMethodDecl *MethodDecl); + + //---------------------------------------------------------------------- + /// Process a function and produce the result variable and initialization + /// + /// @param[in] FunDecl + /// The function to process. + //---------------------------------------------------------------------- + bool SynthesizeFunctionResult(clang::FunctionDecl *FunDecl); + + //---------------------------------------------------------------------- + /// Process a function body and produce the result variable and + /// initialization + /// + /// @param[in] Body + /// The body of the function. + /// + /// @param[in] DC + /// The DeclContext of the function, into which the result variable + /// is inserted. + //---------------------------------------------------------------------- + bool SynthesizeBodyResult(clang::CompoundStmt *Body, + clang::DeclContext *DC); + + //---------------------------------------------------------------------- + /// Given a DeclContext for a function or method, find all types + /// declared in the context and record any persistent types found. + /// + /// @param[in] FunDeclCtx + /// The context for the function to process. + //---------------------------------------------------------------------- + void RecordPersistentTypes(clang::DeclContext *FunDeclCtx); + + //---------------------------------------------------------------------- + /// Given a TypeDecl, if it declares a type whose name starts with a + /// dollar sign, register it as a pointer type in the target's scratch + /// AST context. + /// + /// @param[in] Body + /// The body of the function. + //---------------------------------------------------------------------- + void MaybeRecordPersistentType(clang::TypeDecl *D); + + clang::ASTContext *m_ast_context; ///< The AST context to use for identifiers and types. + clang::ASTConsumer *m_passthrough; ///< The ASTConsumer down the chain, for passthrough. NULL if it's a SemaConsumer. + clang::SemaConsumer *m_passthrough_sema; ///< The SemaConsumer down the chain, for passthrough. NULL if it's an ASTConsumer. + Target &m_target; ///< The target, which contains the persistent variable store and the + clang::Sema *m_sema; ///< The Sema to use. +}; + +} + +#endif diff --git a/contrib/llvm/tools/lldb/include/lldb/Expression/ASTStructExtractor.h b/contrib/llvm/tools/lldb/include/lldb/Expression/ASTStructExtractor.h new file mode 100644 index 00000000000..a1518de83d6 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Expression/ASTStructExtractor.h @@ -0,0 +1,156 @@ +//===-- ASTStructExtractor.h ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ASTStructExtractor_h_ +#define liblldb_ASTStructExtractor_h_ + +#include "clang/Sema/SemaConsumer.h" +#include "lldb/Core/ClangForward.h" +#include "lldb/Expression/ClangExpressionVariable.h" +#include "lldb/Expression/ClangFunction.h" + +namespace lldb_private { + +//---------------------------------------------------------------------- +/// @class ASTStructExtractor ASTStructExtractor.h "lldb/Expression/ASTStructExtractor.h" +/// @brief Extracts and describes the argument structure for a wrapped function. +/// +/// This pass integrates with ClangFunction, which calls functions with custom +/// sets of arguments. To avoid having to implement the full calling convention +/// for the target's architecture, ClangFunction writes a simple wrapper +/// function that takes a pointer to an argument structure that contains room +/// for the address of the function to be called, the values of all its +/// arguments, and room for the function's return value. +/// +/// The definition of this struct is itself in the body of the wrapper function, +/// so Clang does the structure layout itself. ASTStructExtractor reads through +/// the AST for the wrapper funtion and finds the struct. +//---------------------------------------------------------------------- +class ASTStructExtractor : public clang::SemaConsumer +{ +public: + //---------------------------------------------------------------------- + /// Constructor + /// + /// @param[in] passthrough + /// Since the ASTs must typically go through to the Clang code generator + /// in order to produce LLVM IR, this SemaConsumer must allow them to + /// pass to the next step in the chain after processing. Passthrough is + /// the next ASTConsumer, or NULL if none is required. + /// + /// @param[in] struct_name + /// The name of the structure to extract from the wrapper function. + /// + /// @param[in] function + /// The caller object whose members should be populated with information + /// about the argument struct. ClangFunction friends ASTStructExtractor + /// for this purpose. + //---------------------------------------------------------------------- + ASTStructExtractor(clang::ASTConsumer *passthrough, + const char *struct_name, + ClangFunction &function); + + //---------------------------------------------------------------------- + /// Destructor + //---------------------------------------------------------------------- + virtual ~ASTStructExtractor(); + + //---------------------------------------------------------------------- + /// Link this consumer with a particular AST context + /// + /// @param[in] Context + /// This AST context will be used for types and identifiers, and also + /// forwarded to the passthrough consumer, if one exists. + //---------------------------------------------------------------------- + void Initialize(clang::ASTContext &Context); + + //---------------------------------------------------------------------- + /// Examine a list of Decls to find the function $__lldb_expr and + /// transform its code + /// + /// @param[in] D + /// The list of Decls to search. These may contain LinkageSpecDecls, + /// which need to be searched recursively. That job falls to + /// TransformTopLevelDecl. + //---------------------------------------------------------------------- + bool HandleTopLevelDecl(clang::DeclGroupRef D); + + //---------------------------------------------------------------------- + /// Passthrough stub + //---------------------------------------------------------------------- + void HandleTranslationUnit(clang::ASTContext &Ctx); + + //---------------------------------------------------------------------- + /// Passthrough stub + //---------------------------------------------------------------------- + void HandleTagDeclDefinition(clang::TagDecl *D); + + //---------------------------------------------------------------------- + /// Passthrough stub + //---------------------------------------------------------------------- + void CompleteTentativeDefinition(clang::VarDecl *D); + + //---------------------------------------------------------------------- + /// Passthrough stub + //---------------------------------------------------------------------- + void HandleVTable(clang::CXXRecordDecl *RD, bool DefinitionRequired); + + //---------------------------------------------------------------------- + /// Passthrough stub + //---------------------------------------------------------------------- + void PrintStats(); + + //---------------------------------------------------------------------- + /// Set the Sema object to use when performing transforms, and pass it on + /// + /// @param[in] S + /// The Sema to use. Because Sema isn't externally visible, this class + /// casts it to an Action for actual use. + //---------------------------------------------------------------------- + void InitializeSema(clang::Sema &S); + + //---------------------------------------------------------------------- + /// Reset the Sema to NULL now that transformations are done + //---------------------------------------------------------------------- + void ForgetSema(); +private: + //---------------------------------------------------------------------- + /// Hunt the given FunctionDecl for the argument struct and place + /// information about it into m_function + /// + /// @param[in] F + /// The FunctionDecl to hunt. + //---------------------------------------------------------------------- + void + ExtractFromFunctionDecl(clang::FunctionDecl* F); + + //---------------------------------------------------------------------- + /// Hunt the given Decl for FunctionDecls named the same as the wrapper + /// function name, recursing as necessary through LinkageSpecDecls, and + /// calling ExtractFromFunctionDecl on anything that was found + /// + /// @param[in] D + /// The Decl to hunt. + //---------------------------------------------------------------------- + void + ExtractFromTopLevelDecl(clang::Decl* D); + + clang::ASTContext *m_ast_context; ///< The AST context to use for identifiers and types. + clang::ASTConsumer *m_passthrough; ///< The ASTConsumer down the chain, for passthrough. NULL if it's a SemaConsumer. + clang::SemaConsumer *m_passthrough_sema; ///< The SemaConsumer down the chain, for passthrough. NULL if it's an ASTConsumer. + clang::Sema *m_sema; ///< The Sema to use. + clang::Action *m_action; ///< The Sema to use, cast to an Action so it's usable. + + ClangFunction &m_function; ///< The function to populate with information about the argument structure. + std::string m_struct_name; ///< The name of the structure to extract. +}; + +} + +#endif diff --git a/contrib/llvm/tools/lldb/include/lldb/Expression/ClangASTSource.h b/contrib/llvm/tools/lldb/include/lldb/Expression/ClangASTSource.h new file mode 100644 index 00000000000..3e41a9e69e9 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Expression/ClangASTSource.h @@ -0,0 +1,530 @@ +//===-- ClangASTSource.h ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ClangASTSource_h_ +#define liblldb_ClangASTSource_h_ + +#include + +#include "clang/Basic/IdentifierTable.h" +#include "lldb/Symbol/ClangExternalASTSourceCommon.h" +#include "lldb/Symbol/ClangASTImporter.h" +#include "lldb/Target/Target.h" + +#include "llvm/ADT/SmallSet.h" + +namespace lldb_private { + +//---------------------------------------------------------------------- +/// @class ClangASTSource ClangASTSource.h "lldb/Expression/ClangASTSource.h" +/// @brief Provider for named objects defined in the debug info for Clang +/// +/// As Clang parses an expression, it may encounter names that are not +/// defined inside the expression, including variables, functions, and +/// types. Clang knows the name it is looking for, but nothing else. +/// The ExternalSemaSource class provides Decls (VarDecl, FunDecl, TypeDecl) +/// to Clang for these names, consulting the ClangExpressionDeclMap to do +/// the actual lookups. +//---------------------------------------------------------------------- +class ClangASTSource : + public ClangExternalASTSourceCommon, + public ClangASTImporter::MapCompleter +{ +public: + //------------------------------------------------------------------ + /// Constructor + /// + /// Initializes class variables. + /// + /// @param[in] declMap + /// A reference to the LLDB object that handles entity lookup. + //------------------------------------------------------------------ + ClangASTSource (const lldb::TargetSP &target) : + m_import_in_progress (false), + m_lookups_enabled (false), + m_target (target), + m_ast_context (NULL), + m_active_lookups () + { + m_ast_importer = m_target->GetClangASTImporter(); + } + + //------------------------------------------------------------------ + /// Destructor + //------------------------------------------------------------------ + ~ClangASTSource(); + + //------------------------------------------------------------------ + /// Interface stubs. + //------------------------------------------------------------------ + clang::Decl *GetExternalDecl (uint32_t) { return NULL; } + clang::Stmt *GetExternalDeclStmt (uint64_t) { return NULL; } + clang::Selector GetExternalSelector (uint32_t) { return clang::Selector(); } + uint32_t GetNumExternalSelectors () { return 0; } + clang::CXXBaseSpecifier *GetExternalCXXBaseSpecifiers (uint64_t Offset) + { return NULL; } + void MaterializeVisibleDecls (const clang::DeclContext *DC) + { return; } + + void InstallASTContext (clang::ASTContext *ast_context) + { + m_ast_context = ast_context; + m_ast_importer->InstallMapCompleter(ast_context, *this); + } + + // + // APIs for ExternalASTSource + // + + //------------------------------------------------------------------ + /// Look up all Decls that match a particular name. Only handles + /// Identifiers and DeclContexts that are either NamespaceDecls or + /// TranslationUnitDecls. Calls SetExternalVisibleDeclsForName with + /// the result. + /// + /// The work for this function is done by + /// void FindExternalVisibleDecls (NameSearchContext &); + /// + /// @param[in] DC + /// The DeclContext to register the found Decls in. + /// + /// @param[in] Name + /// The name to find entries for. + /// + /// @return + /// Whatever SetExternalVisibleDeclsForName returns. + //------------------------------------------------------------------ + bool + FindExternalVisibleDeclsByName (const clang::DeclContext *DC, + clang::DeclarationName Name); + + //------------------------------------------------------------------ + /// Enumerate all Decls in a given lexical context. + /// + /// @param[in] DC + /// The DeclContext being searched. + /// + /// @param[in] isKindWeWant + /// If non-NULL, a callback function that returns true given the + /// DeclKinds of desired Decls, and false otherwise. + /// + /// @param[in] Decls + /// A vector that is filled in with matching Decls. + //------------------------------------------------------------------ + clang::ExternalLoadResult + FindExternalLexicalDecls (const clang::DeclContext *DC, + bool (*isKindWeWant)(clang::Decl::Kind), + llvm::SmallVectorImpl &Decls); + + //------------------------------------------------------------------ + /// Specify the layout of the contents of a RecordDecl. + /// + /// @param[in] Record + /// The record (in the parser's AST context) that needs to be + /// laid out. + /// + /// @param[out] Size + /// The total size of the record in bits. + /// + /// @param[out] Alignment + /// The alignment of the record in bits. + /// + /// @param[in] FieldOffsets + /// A map that must be populated with pairs of the record's + /// fields (in the parser's AST context) and their offsets + /// (measured in bits). + /// + /// @param[in] BaseOffsets + /// A map that must be populated with pairs of the record's + /// C++ concrete base classes (in the parser's AST context, + /// and only if the record is a CXXRecordDecl and has base + /// classes) and their offsets (measured in bytes). + /// + /// @param[in] VirtualBaseOffsets + /// A map that must be populated with pairs of the record's + /// C++ virtual base classes (in the parser's AST context, + /// and only if the record is a CXXRecordDecl and has base + /// classes) and their offsets (measured in bytes). + /// + /// @return + /// True <=> the layout is valid. + //----------------------------------------------------------------- + bool + layoutRecordType(const clang::RecordDecl *Record, + uint64_t &Size, + uint64_t &Alignment, + llvm::DenseMap &FieldOffsets, + llvm::DenseMap &BaseOffsets, + llvm::DenseMap &VirtualBaseOffsets); + + //------------------------------------------------------------------ + /// Complete a TagDecl. + /// + /// @param[in] Tag + /// The Decl to be completed in place. + //------------------------------------------------------------------ + virtual void + CompleteType (clang::TagDecl *Tag); + + //------------------------------------------------------------------ + /// Complete an ObjCInterfaceDecl. + /// + /// @param[in] Class + /// The Decl to be completed in place. + //------------------------------------------------------------------ + virtual void + CompleteType (clang::ObjCInterfaceDecl *Class); + + //------------------------------------------------------------------ + /// Called on entering a translation unit. Tells Clang by calling + /// setHasExternalVisibleStorage() and setHasExternalLexicalStorage() + /// that this object has something to say about undefined names. + /// + /// @param[in] ASTConsumer + /// Unused. + //------------------------------------------------------------------ + void StartTranslationUnit (clang::ASTConsumer *Consumer); + + // + // APIs for NamespaceMapCompleter + // + + //------------------------------------------------------------------ + /// Look up the modules containing a given namespace and put the + /// appropriate entries in the namespace map. + /// + /// @param[in] namespace_map + /// The map to be completed. + /// + /// @param[in] name + /// The name of the namespace to be found. + /// + /// @param[in] parent_map + /// The map for the namespace's parent namespace, if there is + /// one. + //------------------------------------------------------------------ + void CompleteNamespaceMap (ClangASTImporter::NamespaceMapSP &namespace_map, + const ConstString &name, + ClangASTImporter::NamespaceMapSP &parent_map) const; + + // + // Helper APIs + // + + clang::NamespaceDecl * + AddNamespace (NameSearchContext &context, + ClangASTImporter::NamespaceMapSP &namespace_decls); + + //------------------------------------------------------------------ + /// The worker function for FindExternalVisibleDeclsByName. + /// + /// @param[in] context + /// The NameSearchContext to use when filing results. + //------------------------------------------------------------------ + virtual void FindExternalVisibleDecls (NameSearchContext &context); + + void SetImportInProgress (bool import_in_progress) { m_import_in_progress = import_in_progress; } + bool GetImportInProgress () { return m_import_in_progress; } + + void SetLookupsEnabled (bool lookups_enabled) { m_lookups_enabled = lookups_enabled; } + bool GetLookupsEnabled () { return m_lookups_enabled; } + + //---------------------------------------------------------------------- + /// @class ClangASTSourceProxy ClangASTSource.h "lldb/Expression/ClangASTSource.h" + /// @brief Proxy for ClangASTSource + /// + /// Clang AST contexts like to own their AST sources, so this is a + /// state-free proxy object. + //---------------------------------------------------------------------- + class ClangASTSourceProxy : public ClangExternalASTSourceCommon + { + public: + ClangASTSourceProxy (ClangASTSource &original) : + m_original(original) + { + } + + bool + FindExternalVisibleDeclsByName (const clang::DeclContext *DC, + clang::DeclarationName Name) + { + return m_original.FindExternalVisibleDeclsByName(DC, Name); + } + + clang::ExternalLoadResult + FindExternalLexicalDecls (const clang::DeclContext *DC, + bool (*isKindWeWant)(clang::Decl::Kind), + llvm::SmallVectorImpl &Decls) + { + return m_original.FindExternalLexicalDecls(DC, isKindWeWant, Decls); + } + + void + CompleteType (clang::TagDecl *Tag) + { + return m_original.CompleteType(Tag); + } + + void + CompleteType (clang::ObjCInterfaceDecl *Class) + { + return m_original.CompleteType(Class); + } + + bool + layoutRecordType(const clang::RecordDecl *Record, + uint64_t &Size, + uint64_t &Alignment, + llvm::DenseMap &FieldOffsets, + llvm::DenseMap &BaseOffsets, + llvm::DenseMap &VirtualBaseOffsets) + { + return m_original.layoutRecordType(Record, + Size, + Alignment, + FieldOffsets, + BaseOffsets, + VirtualBaseOffsets); + } + + void StartTranslationUnit (clang::ASTConsumer *Consumer) + { + return m_original.StartTranslationUnit(Consumer); + } + + ClangASTMetadata * + GetMetadata(const void * object) + { + return m_original.GetMetadata(object); + } + + void + SetMetadata(const void * object, ClangASTMetadata &metadata) + { + return m_original.SetMetadata(object, metadata); + } + + bool + HasMetadata(const void * object) + { + return m_original.HasMetadata(object); + } + private: + ClangASTSource &m_original; + }; + + clang::ExternalASTSource *CreateProxy() + { + return new ClangASTSourceProxy(*this); + } + +protected: + //------------------------------------------------------------------ + /// Look for the complete version of an Objective-C interface, and + /// return it if found. + /// + /// @param[in] interface_decl + /// An ObjCInterfaceDecl that may not be the complete one. + /// + /// @return + /// NULL if the complete interface couldn't be found; + /// the complete interface otherwise. + //------------------------------------------------------------------ + clang::ObjCInterfaceDecl * + GetCompleteObjCInterface (clang::ObjCInterfaceDecl *interface_decl); + + //------------------------------------------------------------------ + /// Find all entities matching a given name in a given module, + /// using a NameSearchContext to make Decls for them. + /// + /// @param[in] context + /// The NameSearchContext that can construct Decls for this name. + /// + /// @param[in] module + /// If non-NULL, the module to query. + /// + /// @param[in] namespace_decl + /// If valid and module is non-NULL, the parent namespace. + /// + /// @param[in] current_id + /// The ID for the current FindExternalVisibleDecls invocation, + /// for logging purposes. + /// + /// @return + /// True on success; false otherwise. + //------------------------------------------------------------------ + void + FindExternalVisibleDecls (NameSearchContext &context, + lldb::ModuleSP module, + ClangNamespaceDecl &namespace_decl, + unsigned int current_id); + + //------------------------------------------------------------------ + /// Find all Objective-C methods matching a given selector. + /// + /// @param[in] context + /// The NameSearchContext that can construct Decls for this name. + /// Its m_decl_name contains the selector and its m_decl_context + /// is the containing object. + //------------------------------------------------------------------ + void + FindObjCMethodDecls (NameSearchContext &context); + + //------------------------------------------------------------------ + /// Find all Objective-C properties and ivars with a given name. + /// + /// @param[in] context + /// The NameSearchContext that can construct Decls for this name. + /// Its m_decl_name contains the name and its m_decl_context + /// is the containing object. + //------------------------------------------------------------------ + void + FindObjCPropertyAndIvarDecls (NameSearchContext &context); + + //------------------------------------------------------------------ + /// A wrapper for ClangASTContext::CopyType that sets a flag that + /// indicates that we should not respond to queries during import. + /// + /// @param[in] dest_context + /// The target AST context, typically the parser's AST context. + /// + /// @param[in] source_context + /// The source AST context, typically the AST context of whatever + /// symbol file the type was found in. + /// + /// @param[in] clang_type + /// The source type. + /// + /// @return + /// The imported type. + //------------------------------------------------------------------ + ClangASTType + GuardedCopyType (const ClangASTType &src_type); + + friend struct NameSearchContext; + + bool m_import_in_progress; + bool m_lookups_enabled; + + const lldb::TargetSP m_target; ///< The target to use in finding variables and types. + clang::ASTContext *m_ast_context; ///< The AST context requests are coming in for. + ClangASTImporter *m_ast_importer; ///< The target's AST importer. + std::set m_active_lookups; +}; + +//---------------------------------------------------------------------- +/// @class NameSearchContext ClangASTSource.h "lldb/Expression/ClangASTSource.h" +/// @brief Container for all objects relevant to a single name lookup +/// +/// LLDB needs to create Decls for entities it finds. This class communicates +/// what name is being searched for and provides helper functions to construct +/// Decls given appropriate type information. +//---------------------------------------------------------------------- +struct NameSearchContext { + ClangASTSource &m_ast_source; ///< The AST source making the request + llvm::SmallVectorImpl &m_decls; ///< The list of declarations already constructed + ClangASTImporter::NamespaceMapSP m_namespace_map; ///< The mapping of all namespaces found for this request back to their modules + const clang::DeclarationName &m_decl_name; ///< The name being looked for + const clang::DeclContext *m_decl_context; ///< The DeclContext to put declarations into + llvm::SmallSet m_function_types; ///< All the types of functions that have been reported, so we don't report conflicts + + struct { + bool variable : 1; + bool function_with_type_info : 1; + bool function : 1; + } m_found; + + //------------------------------------------------------------------ + /// Constructor + /// + /// Initializes class variables. + /// + /// @param[in] astSource + /// A reference to the AST source making a request. + /// + /// @param[in] decls + /// A reference to a list into which new Decls will be placed. This + /// list is typically empty when the function is called. + /// + /// @param[in] name + /// The name being searched for (always an Identifier). + /// + /// @param[in] dc + /// The DeclContext to register Decls in. + //------------------------------------------------------------------ + NameSearchContext (ClangASTSource &astSource, + llvm::SmallVectorImpl &decls, + clang::DeclarationName &name, + const clang::DeclContext *dc) : + m_ast_source(astSource), + m_decls(decls), + m_decl_name(name), + m_decl_context(dc) + { + memset(&m_found, 0, sizeof(m_found)); + } + + //------------------------------------------------------------------ + /// Create a VarDecl with the name being searched for and the provided + /// type and register it in the right places. + /// + /// @param[in] type + /// The opaque QualType for the VarDecl being registered. + //------------------------------------------------------------------ + clang::NamedDecl *AddVarDecl(const ClangASTType &type); + + //------------------------------------------------------------------ + /// Create a FunDecl with the name being searched for and the provided + /// type and register it in the right places. + /// + /// @param[in] type + /// The opaque QualType for the FunDecl being registered. + //------------------------------------------------------------------ + clang::NamedDecl *AddFunDecl(const ClangASTType &type); + + //------------------------------------------------------------------ + /// Create a FunDecl with the name being searched for and generic + /// type (i.e. intptr_t NAME_GOES_HERE(...)) and register it in the + /// right places. + //------------------------------------------------------------------ + clang::NamedDecl *AddGenericFunDecl(); + + //------------------------------------------------------------------ + /// Create a TypeDecl with the name being searched for and the provided + /// type and register it in the right places. + /// + /// @param[in] type + /// The opaque QualType for the TypeDecl being registered. + //------------------------------------------------------------------ + clang::NamedDecl *AddTypeDecl(const ClangASTType &clang_type); + + + //------------------------------------------------------------------ + /// Add Decls from the provided DeclContextLookupResult to the list + /// of results. + /// + /// @param[in] result + /// The DeclContextLookupResult, usually returned as the result + /// of querying a DeclContext. + //------------------------------------------------------------------ + void AddLookupResult (clang::DeclContextLookupConstResult result); + + //------------------------------------------------------------------ + /// Add a NamedDecl to the list of results. + /// + /// @param[in] decl + /// The NamedDecl, usually returned as the result + /// of querying a DeclContext. + //------------------------------------------------------------------ + void AddNamedDecl (clang::NamedDecl *decl); +}; + +} + +#endif diff --git a/contrib/llvm/tools/lldb/include/lldb/Expression/ClangExpression.h b/contrib/llvm/tools/lldb/include/lldb/Expression/ClangExpression.h new file mode 100644 index 00000000000..6e831e4471e --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Expression/ClangExpression.h @@ -0,0 +1,153 @@ +//===-- ClangExpression.h ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ClangExpression_h_ +#define liblldb_ClangExpression_h_ + +// C Includes +// C++ Includes +#include +#include +#include + +// Other libraries and framework includes +// Project includes + +#include "lldb/lldb-forward.h" +#include "lldb/lldb-private.h" +#include "lldb/Core/ClangForward.h" +#include "lldb/Target/Process.h" + +namespace lldb_private { + +class RecordingMemoryManager; + +//---------------------------------------------------------------------- +/// @class ClangExpression ClangExpression.h "lldb/Expression/ClangExpression.h" +/// @brief Encapsulates a single expression for use with Clang +/// +/// LLDB uses expressions for various purposes, notably to call functions +/// and as a backend for the expr command. ClangExpression encapsulates +/// the objects needed to parse and interpret or JIT an expression. It +/// uses the Clang parser to produce LLVM IR from the expression. +//---------------------------------------------------------------------- +class ClangExpression +{ +public: + enum ResultType { + eResultTypeAny, + eResultTypeId + }; + + ClangExpression () : + m_jit_process_wp(), + m_jit_start_addr (LLDB_INVALID_ADDRESS), + m_jit_end_addr (LLDB_INVALID_ADDRESS) + { + } + + //------------------------------------------------------------------ + /// Destructor + //------------------------------------------------------------------ + virtual ~ClangExpression () + { + } + + //------------------------------------------------------------------ + /// Return the string that the parser should parse. Must be a full + /// translation unit. + //------------------------------------------------------------------ + virtual const char * + Text () = 0; + + //------------------------------------------------------------------ + /// Return the function name that should be used for executing the + /// expression. Text() should contain the definition of this + /// function. + //------------------------------------------------------------------ + virtual const char * + FunctionName () = 0; + + //------------------------------------------------------------------ + /// Return the language that should be used when parsing. To use + /// the default, return eLanguageTypeUnknown. + //------------------------------------------------------------------ + virtual lldb::LanguageType + Language () + { + return lldb::eLanguageTypeUnknown; + } + + //------------------------------------------------------------------ + /// Return the object that the parser should use when resolving external + /// values. May be NULL if everything should be self-contained. + //------------------------------------------------------------------ + virtual ClangExpressionDeclMap * + DeclMap () = 0; + + //------------------------------------------------------------------ + /// Return the object that the parser should allow to access ASTs. + /// May be NULL if the ASTs do not need to be transformed. + /// + /// @param[in] passthrough + /// The ASTConsumer that the returned transformer should send + /// the ASTs to after transformation. + //------------------------------------------------------------------ + virtual clang::ASTConsumer * + ASTTransformer (clang::ASTConsumer *passthrough) = 0; + + //------------------------------------------------------------------ + /// Return the desired result type of the function, or + /// eResultTypeAny if indifferent. + //------------------------------------------------------------------ + virtual ResultType + DesiredResultType () + { + return eResultTypeAny; + } + + //------------------------------------------------------------------ + /// Flags + //------------------------------------------------------------------ + + //------------------------------------------------------------------ + /// Return true if validation code should be inserted into the + /// expression. + //------------------------------------------------------------------ + virtual bool + NeedsValidation () = 0; + + //------------------------------------------------------------------ + /// Return true if external variables in the expression should be + /// resolved. + //------------------------------------------------------------------ + virtual bool + NeedsVariableResolution () = 0; + + //------------------------------------------------------------------ + /// Return the address of the function's JIT-compiled code, or + /// LLDB_INVALID_ADDRESS if the function is not JIT compiled + //------------------------------------------------------------------ + lldb::addr_t + StartAddress () + { + return m_jit_start_addr; + } + +protected: + + lldb::ProcessWP m_jit_process_wp; + lldb::addr_t m_jit_start_addr; ///< The address of the JITted function within the JIT allocation. LLDB_INVALID_ADDRESS if invalid. + lldb::addr_t m_jit_end_addr; ///< The address of the JITted function within the JIT allocation. LLDB_INVALID_ADDRESS if invalid. + +}; + +} // namespace lldb_private + +#endif // liblldb_ClangExpression_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Expression/ClangExpressionDeclMap.h b/contrib/llvm/tools/lldb/include/lldb/Expression/ClangExpressionDeclMap.h new file mode 100644 index 00000000000..b2a43e0ac75 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Expression/ClangExpressionDeclMap.h @@ -0,0 +1,698 @@ +//===-- ClangExpressionDeclMap.h --------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ClangExpressionDeclMap_h_ +#define liblldb_ClangExpressionDeclMap_h_ + +// C Includes +#include +#include + +// C++ Includes +#include + +// Other libraries and framework includes +// Project includes +#include "llvm/ADT/APInt.h" +#include "llvm/ADT/DenseMap.h" +#include "clang/AST/Decl.h" +#include "lldb/lldb-public.h" +#include "lldb/Core/ClangForward.h" +#include "lldb/Core/Value.h" +#include "lldb/Expression/ClangASTSource.h" +#include "lldb/Expression/ClangExpressionVariable.h" +#include "lldb/Expression/Materializer.h" +#include "lldb/Symbol/TaggedASTType.h" +#include "lldb/Symbol/SymbolContext.h" +#include "lldb/Target/ExecutionContext.h" + +namespace lldb_private { + +//---------------------------------------------------------------------- +/// @class ClangExpressionDeclMap ClangExpressionDeclMap.h "lldb/Expression/ClangExpressionDeclMap.h" +/// @brief Manages named entities that are defined in LLDB's debug information. +/// +/// The Clang parser uses the ClangASTSource as an interface to request named +/// entities from outside an expression. The ClangASTSource reports back, listing +/// all possible objects corresponding to a particular name. But it in turn +/// relies on ClangExpressionDeclMap, which performs several important functions. +/// +/// First, it records what variables and functions were looked up and what Decls +/// were returned for them. +/// +/// Second, it constructs a struct on behalf of IRForTarget, recording which +/// variables should be placed where and relaying this information back so that +/// IRForTarget can generate context-independent code. +/// +/// Third, it "materializes" this struct on behalf of the expression command, +/// finding the current values of each variable and placing them into the +/// struct so that it can be passed to the JITted version of the IR. +/// +/// Fourth and finally, it "dematerializes" the struct after the JITted code has +/// has executed, placing the new values back where it found the old ones. +//---------------------------------------------------------------------- +class ClangExpressionDeclMap : + public ClangASTSource +{ +public: + //------------------------------------------------------------------ + /// Constructor + /// + /// Initializes class variables. + /// + /// @param[in] keep_result_in_memory + /// If true, inhibits the normal deallocation of the memory for + /// the result persistent variable, and instead marks the variable + /// as persisting. + /// + /// @param[in] exe_ctx + /// The execution context to use when parsing. + //------------------------------------------------------------------ + ClangExpressionDeclMap (bool keep_result_in_memory, + ExecutionContext &exe_ctx); + + //------------------------------------------------------------------ + /// Destructor + //------------------------------------------------------------------ + ~ClangExpressionDeclMap (); + + //------------------------------------------------------------------ + /// Enable the state needed for parsing and IR transformation. + /// + /// @param[in] exe_ctx + /// The execution context to use when finding types for variables. + /// Also used to find a "scratch" AST context to store result types. + /// + /// @param[in] materializer + /// If non-NULL, the materializer to populate with information about + /// the variables to use + /// + /// @return + /// True if parsing is possible; false if it is unsafe to continue. + //------------------------------------------------------------------ + bool + WillParse (ExecutionContext &exe_ctx, + Materializer *materializer); + + //------------------------------------------------------------------ + /// [Used by ClangExpressionParser] For each variable that had an unknown + /// type at the beginning of parsing, determine its final type now. + /// + /// @return + /// True on success; false otherwise. + //------------------------------------------------------------------ + bool + ResolveUnknownTypes(); + + //------------------------------------------------------------------ + /// Disable the state needed for parsing and IR transformation. + //------------------------------------------------------------------ + void + DidParse (); + + //------------------------------------------------------------------ + /// [Used by IRForTarget] Add a variable to the list of persistent + /// variables for the process. + /// + /// @param[in] decl + /// The Clang declaration for the persistent variable, used for + /// lookup during parsing. + /// + /// @param[in] name + /// The name of the persistent variable, usually $something. + /// + /// @param[in] type + /// The type of the variable, in the Clang parser's context. + /// + /// @return + /// True on success; false otherwise. + //------------------------------------------------------------------ + bool + AddPersistentVariable (const clang::NamedDecl *decl, + const ConstString &name, + TypeFromParser type, + bool is_result, + bool is_lvalue); + + //------------------------------------------------------------------ + /// [Used by IRForTarget] Add a variable to the struct that needs to + /// be materialized each time the expression runs. + /// + /// @param[in] decl + /// The Clang declaration for the variable. + /// + /// @param[in] name + /// The name of the variable. + /// + /// @param[in] value + /// The LLVM IR value for this variable. + /// + /// @param[in] size + /// The size of the variable in bytes. + /// + /// @param[in] alignment + /// The required alignment of the variable in bytes. + /// + /// @return + /// True on success; false otherwise. + //------------------------------------------------------------------ + bool + AddValueToStruct (const clang::NamedDecl *decl, + const ConstString &name, + llvm::Value *value, + size_t size, + off_t alignment); + + //------------------------------------------------------------------ + /// [Used by IRForTarget] Finalize the struct, laying out the position + /// of each object in it. + /// + /// @return + /// True on success; false otherwise. + //------------------------------------------------------------------ + bool + DoStructLayout (); + + //------------------------------------------------------------------ + /// [Used by IRForTarget] Get general information about the laid-out + /// struct after DoStructLayout() has been called. + /// + /// @param[out] num_elements + /// The number of elements in the struct. + /// + /// @param[out] size + /// The size of the struct, in bytes. + /// + /// @param[out] alignment + /// The alignment of the struct, in bytes. + /// + /// @return + /// True if the information could be retrieved; false otherwise. + //------------------------------------------------------------------ + bool + GetStructInfo (uint32_t &num_elements, + size_t &size, + off_t &alignment); + + //------------------------------------------------------------------ + /// [Used by IRForTarget] Get specific information about one field + /// of the laid-out struct after DoStructLayout() has been called. + /// + /// @param[out] decl + /// The parsed Decl for the field, as generated by ClangASTSource + /// on ClangExpressionDeclMap's behalf. In the case of the result + /// value, this will have the name $__lldb_result even if the + /// result value ends up having the name $1. This is an + /// implementation detail of IRForTarget. + /// + /// @param[out] value + /// The IR value for the field (usually a GlobalVariable). In + /// the case of the result value, this will have the correct + /// name ($1, for instance). This is an implementation detail + /// of IRForTarget. + /// + /// @param[out] offset + /// The offset of the field from the beginning of the struct. + /// As long as the struct is aligned according to its required + /// alignment, this offset will align the field correctly. + /// + /// @param[out] name + /// The name of the field as used in materialization. + /// + /// @param[in] index + /// The index of the field about which information is requested. + /// + /// @return + /// True if the information could be retrieved; false otherwise. + //------------------------------------------------------------------ + bool + GetStructElement (const clang::NamedDecl *&decl, + llvm::Value *&value, + off_t &offset, + ConstString &name, + uint32_t index); + + //------------------------------------------------------------------ + /// [Used by IRForTarget] Get information about a function given its + /// Decl. + /// + /// @param[in] decl + /// The parsed Decl for the Function, as generated by ClangASTSource + /// on ClangExpressionDeclMap's behalf. + /// + /// @param[out] ptr + /// The absolute address of the function in the target. + /// + /// @return + /// True if the information could be retrieved; false otherwise. + //------------------------------------------------------------------ + bool + GetFunctionInfo (const clang::NamedDecl *decl, + uint64_t &ptr); + + //------------------------------------------------------------------ + /// [Used by IRForTarget] Get the address of a function given nothing + /// but its name. Some functions are needed but didn't get Decls made + /// during parsing -- specifically, sel_registerName is never called + /// in the generated IR but we need to call it nonetheless. + /// + /// @param[in] name + /// The name of the function. + /// + /// @param[out] ptr + /// The absolute address of the function in the target. + /// + /// @return + /// True if the address could be retrieved; false otherwise. + //------------------------------------------------------------------ + bool + GetFunctionAddress (const ConstString &name, + uint64_t &ptr); + + //------------------------------------------------------------------ + /// [Used by IRForTarget] Get the address of a symbol given nothing + /// but its name. + /// + /// @param[in] target + /// The target to find the symbol in. If not provided, + /// then the current parsing context's Target. + /// + /// @param[in] process + /// The process to use. For Objective-C symbols, the process's + /// Objective-C language runtime may be queried if the process + /// is non-NULL. + /// + /// @param[in] name + /// The name of the symbol. + /// + /// @return + /// Valid load address for the symbol + //------------------------------------------------------------------ + lldb::addr_t + GetSymbolAddress (Target &target, + Process *process, + const ConstString &name, + lldb::SymbolType symbol_type); + + lldb::addr_t + GetSymbolAddress (const ConstString &name, + lldb::SymbolType symbol_type); + + //------------------------------------------------------------------ + /// [Used by IRInterpreter] Get basic target information. + /// + /// @param[out] byte_order + /// The byte order of the target. + /// + /// @param[out] address_byte_size + /// The size of a pointer in bytes. + /// + /// @return + /// True if the information could be determined; false + /// otherwise. + //------------------------------------------------------------------ + struct TargetInfo + { + lldb::ByteOrder byte_order; + size_t address_byte_size; + + TargetInfo() : + byte_order(lldb::eByteOrderInvalid), + address_byte_size(0) + { + } + + bool IsValid() + { + return (byte_order != lldb::eByteOrderInvalid && + address_byte_size != 0); + } + }; + TargetInfo GetTargetInfo(); + + //------------------------------------------------------------------ + /// [Used by ClangASTSource] Find all entities matching a given name, + /// using a NameSearchContext to make Decls for them. + /// + /// @param[in] context + /// The NameSearchContext that can construct Decls for this name. + /// + /// @return + /// True on success; false otherwise. + //------------------------------------------------------------------ + void + FindExternalVisibleDecls (NameSearchContext &context); + + //------------------------------------------------------------------ + /// Find all entities matching a given name in a given module/namespace, + /// using a NameSearchContext to make Decls for them. + /// + /// @param[in] context + /// The NameSearchContext that can construct Decls for this name. + /// + /// @param[in] module + /// If non-NULL, the module to query. + /// + /// @param[in] namespace_decl + /// If valid and module is non-NULL, the parent namespace. + /// + /// @param[in] name + /// The name as a plain C string. The NameSearchContext contains + /// a DeclarationName for the name so at first the name may seem + /// redundant, but ClangExpressionDeclMap operates in RTTI land so + /// it can't access DeclarationName. + /// + /// @param[in] current_id + /// The ID for the current FindExternalVisibleDecls invocation, + /// for logging purposes. + /// + /// @return + /// True on success; false otherwise. + //------------------------------------------------------------------ + void + FindExternalVisibleDecls (NameSearchContext &context, + lldb::ModuleSP module, + ClangNamespaceDecl &namespace_decl, + unsigned int current_id); +private: + ClangExpressionVariableList m_found_entities; ///< All entities that were looked up for the parser. + ClangExpressionVariableList m_struct_members; ///< All entities that need to be placed in the struct. + bool m_keep_result_in_memory; ///< True if result persistent variables generated by this expression should stay in memory. + + //---------------------------------------------------------------------- + /// The following values should not live beyond parsing + //---------------------------------------------------------------------- + class ParserVars + { + public: + ParserVars(ClangExpressionDeclMap &decl_map) : + m_exe_ctx(), + m_sym_ctx(), + m_persistent_vars(NULL), + m_enable_lookups(false), + m_materializer(NULL), + m_decl_map(decl_map) + { + } + + Target * + GetTarget() + { + if (m_exe_ctx.GetTargetPtr()) + return m_exe_ctx.GetTargetPtr(); + else if (m_sym_ctx.target_sp) + m_sym_ctx.target_sp.get(); + return NULL; + } + + ExecutionContext m_exe_ctx; ///< The execution context to use when parsing. + SymbolContext m_sym_ctx; ///< The symbol context to use in finding variables and types. + ClangPersistentVariables *m_persistent_vars; ///< The persistent variables for the process. + bool m_enable_lookups; ///< Set to true during parsing if we have found the first "$__lldb" name. + TargetInfo m_target_info; ///< Basic information about the target. + Materializer *m_materializer; ///< If non-NULL, the materializer to use when reporting used variables. + private: + ClangExpressionDeclMap &m_decl_map; + DISALLOW_COPY_AND_ASSIGN (ParserVars); + }; + + std::unique_ptr m_parser_vars; + + //---------------------------------------------------------------------- + /// Activate parser-specific variables + //---------------------------------------------------------------------- + void + EnableParserVars() + { + if (!m_parser_vars.get()) + m_parser_vars.reset(new ParserVars(*this)); + } + + //---------------------------------------------------------------------- + /// Deallocate parser-specific variables + //---------------------------------------------------------------------- + void + DisableParserVars() + { + m_parser_vars.reset(); + } + + //---------------------------------------------------------------------- + /// The following values contain layout information for the materialized + /// struct, but are not specific to a single materialization + //---------------------------------------------------------------------- + struct StructVars { + StructVars() : + m_struct_alignment(0), + m_struct_size(0), + m_struct_laid_out(false), + m_result_name(), + m_object_pointer_type(NULL, NULL) + { + } + + off_t m_struct_alignment; ///< The alignment of the struct in bytes. + size_t m_struct_size; ///< The size of the struct in bytes. + bool m_struct_laid_out; ///< True if the struct has been laid out and the layout is valid (that is, no new fields have been added since). + ConstString m_result_name; ///< The name of the result variable ($1, for example) + TypeFromUser m_object_pointer_type; ///< The type of the "this" variable, if one exists + }; + + std::unique_ptr m_struct_vars; + + //---------------------------------------------------------------------- + /// Activate struct variables + //---------------------------------------------------------------------- + void + EnableStructVars() + { + if (!m_struct_vars.get()) + m_struct_vars.reset(new struct StructVars); + } + + //---------------------------------------------------------------------- + /// Deallocate struct variables + //---------------------------------------------------------------------- + void + DisableStructVars() + { + m_struct_vars.reset(); + } + + //---------------------------------------------------------------------- + /// Get this parser's ID for use in extracting parser- and JIT-specific + /// data from persistent variables. + //---------------------------------------------------------------------- + uint64_t + GetParserID() + { + return (uint64_t)this; + } + + //------------------------------------------------------------------ + /// Given a target, find a data symbol that has the given name. + /// + /// @param[in] target + /// The target to use as the basis for the search. + /// + /// @param[in] name + /// The name as a plain C string. + /// + /// @return + /// The LLDB Symbol found, or NULL if none was found. + //--------------------------------------------------------- + const Symbol * + FindGlobalDataSymbol (Target &target, + const ConstString &name); + + //------------------------------------------------------------------ + /// Given a target, find a variable that matches the given name and + /// type. + /// + /// @param[in] target + /// The target to use as a basis for finding the variable. + /// + /// @param[in] module + /// If non-NULL, the module to search. + /// + /// @param[in] name + /// The name as a plain C string. + /// + /// @param[in] namespace_decl + /// If non-NULL and module is non-NULL, the parent namespace. + /// + /// @param[in] type + /// The required type for the variable. This function may be called + /// during parsing, in which case we don't know its type; hence the + /// default. + /// + /// @return + /// The LLDB Variable found, or NULL if none was found. + //------------------------------------------------------------------ + lldb::VariableSP + FindGlobalVariable (Target &target, + lldb::ModuleSP &module, + const ConstString &name, + ClangNamespaceDecl *namespace_decl, + TypeFromUser *type = NULL); + + //------------------------------------------------------------------ + /// Get the value of a variable in a given execution context and return + /// the associated Types if needed. + /// + /// @param[in] var + /// The variable to evaluate. + /// + /// @param[out] var_location + /// The variable location value to fill in + /// + /// @param[out] found_type + /// The type of the found value, as it was found in the user process. + /// This is only useful when the variable is being inspected on behalf + /// of the parser, hence the default. + /// + /// @param[out] parser_type + /// The type of the found value, as it was copied into the parser's + /// AST context. This is only useful when the variable is being + /// inspected on behalf of the parser, hence the default. + /// + /// @param[in] decl + /// The Decl to be looked up. + /// + /// @return + /// Return true if the value was successfully filled in. + //------------------------------------------------------------------ + bool + GetVariableValue (lldb::VariableSP &var, + lldb_private::Value &var_location, + TypeFromUser *found_type = NULL, + TypeFromParser *parser_type = NULL); + + //------------------------------------------------------------------ + /// Use the NameSearchContext to generate a Decl for the given LLDB + /// Variable, and put it in the Tuple list. + /// + /// @param[in] context + /// The NameSearchContext to use when constructing the Decl. + /// + /// @param[in] var + /// The LLDB Variable that needs a Decl. + /// + /// @param[in] valobj + /// The LLDB ValueObject for that variable. + //------------------------------------------------------------------ + void + AddOneVariable (NameSearchContext &context, + lldb::VariableSP var, + lldb::ValueObjectSP valobj, + unsigned int current_id); + + //------------------------------------------------------------------ + /// Use the NameSearchContext to generate a Decl for the given + /// persistent variable, and put it in the list of found entities. + /// + /// @param[in] context + /// The NameSearchContext to use when constructing the Decl. + /// + /// @param[in] pvar + /// The persistent variable that needs a Decl. + /// + /// @param[in] current_id + /// The ID of the current invocation of FindExternalVisibleDecls + /// for logging purposes. + //------------------------------------------------------------------ + void + AddOneVariable (NameSearchContext &context, + lldb::ClangExpressionVariableSP &pvar_sp, + unsigned int current_id); + + //------------------------------------------------------------------ + /// Use the NameSearchContext to generate a Decl for the given LLDB + /// symbol (treated as a variable), and put it in the list of found + /// entities. + /// + /// @param[in] context + /// The NameSearchContext to use when constructing the Decl. + /// + /// @param[in] var + /// The LLDB Variable that needs a Decl. + //------------------------------------------------------------------ + void + AddOneGenericVariable (NameSearchContext &context, + const Symbol &symbol, + unsigned int current_id); + + //------------------------------------------------------------------ + /// Use the NameSearchContext to generate a Decl for the given + /// function. (Functions are not placed in the Tuple list.) Can + /// handle both fully typed functions and generic functions. + /// + /// @param[in] context + /// The NameSearchContext to use when constructing the Decl. + /// + /// @param[in] fun + /// The Function that needs to be created. If non-NULL, this is + /// a fully-typed function. + /// + /// @param[in] sym + /// The Symbol that corresponds to a function that needs to be + /// created with generic type (unitptr_t foo(...)). + //------------------------------------------------------------------ + void + AddOneFunction (NameSearchContext &context, + Function *fun, + Symbol *sym, + unsigned int current_id); + + //------------------------------------------------------------------ + /// Use the NameSearchContext to generate a Decl for the given + /// register. + /// + /// @param[in] context + /// The NameSearchContext to use when constructing the Decl. + /// + /// @param[in] reg_info + /// The information corresponding to that register. + //------------------------------------------------------------------ + void + AddOneRegister (NameSearchContext &context, + const RegisterInfo *reg_info, + unsigned int current_id); + + //------------------------------------------------------------------ + /// Use the NameSearchContext to generate a Decl for the given + /// type. (Types are not placed in the Tuple list.) + /// + /// @param[in] context + /// The NameSearchContext to use when constructing the Decl. + /// + /// @param[in] type + /// The type that needs to be created. + //------------------------------------------------------------------ + void + AddOneType (NameSearchContext &context, + TypeFromUser &type, + unsigned int current_id); + + //------------------------------------------------------------------ + /// Copy a C++ class type into the parser's AST context and add a + /// member function declaration to it for the expression. + /// + /// @param[in] type + /// The type that needs to be created. + //------------------------------------------------------------------ + + TypeFromParser + CopyClassType(TypeFromUser &type, + unsigned int current_id); +}; + +} // namespace lldb_private + +#endif // liblldb_ClangExpressionDeclMap_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Expression/ClangExpressionParser.h b/contrib/llvm/tools/lldb/include/lldb/Expression/ClangExpressionParser.h new file mode 100644 index 00000000000..3247f2094ba --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Expression/ClangExpressionParser.h @@ -0,0 +1,151 @@ +//===-- ClangExpressionParser.h ---------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ClangExpressionParser_h_ +#define liblldb_ClangExpressionParser_h_ + +#include "lldb/lldb-public.h" +#include "lldb/Core/ArchSpec.h" +#include "lldb/Core/ClangForward.h" +#include "lldb/Core/Error.h" +#include "lldb/Expression/IRForTarget.h" + +#include +#include + +namespace lldb_private +{ + +class IRExecutionUnit; + +//---------------------------------------------------------------------- +/// @class ClangExpressionParser ClangExpressionParser.h "lldb/Expression/ClangExpressionParser.h" +/// @brief Encapsulates an instance of Clang that can parse expressions. +/// +/// ClangExpressionParser is responsible for preparing an instance of +/// ClangExpression for execution. ClangExpressionParser uses ClangExpression +/// as a glorified parameter list, performing the required parsing and +/// conversion to formats (DWARF bytecode, or JIT compiled machine code) +/// that can be executed. +//---------------------------------------------------------------------- +class ClangExpressionParser +{ +public: + //------------------------------------------------------------------ + /// Constructor + /// + /// Initializes class variabes. + /// + /// @param[in] exe_scope, + /// If non-NULL, an execution context scope that can help to + /// correctly create an expression with a valid process for + /// optional tuning Objective-C runtime support. Can be NULL. + /// + /// @param[in] expr + /// The expression to be parsed. + //------------------------------------------------------------------ + ClangExpressionParser (ExecutionContextScope *exe_scope, + ClangExpression &expr); + + //------------------------------------------------------------------ + /// Destructor + //------------------------------------------------------------------ + ~ClangExpressionParser (); + + //------------------------------------------------------------------ + /// Parse a single expression and convert it to IR using Clang. Don't + /// wrap the expression in anything at all. + /// + /// @param[in] stream + /// The stream to print errors to. + /// + /// @return + /// The number of errors encountered during parsing. 0 means + /// success. + //------------------------------------------------------------------ + unsigned + Parse (Stream &stream); + + //------------------------------------------------------------------ + /// Ready an already-parsed expression for execution, possibly + /// evaluating it statically. + /// + /// @param[out] func_addr + /// The address to which the function has been written. + /// + /// @param[out] func_end + /// The end of the function's allocated memory region. (func_addr + /// and func_end do not delimit an allocated region; the allocated + /// region may begin before func_addr.) + /// + /// @param[in] execution_unit_ap + /// After parsing, ownership of the execution unit for + /// for the expression is handed to this unique pointer. + /// + /// @param[in] exe_ctx + /// The execution context to write the function into. + /// + /// @param[out] evaluated_statically + /// Set to true if the expression could be interpreted statically; + /// untouched otherwise. + /// + /// @param[out] const_result + /// If the result of the expression is constant, and the + /// expression has no side effects, this is set to the result of the + /// expression. + /// + /// @param[in] execution_policy + /// Determines whether the expression must be JIT-compiled, must be + /// evaluated statically, or whether this decision may be made + /// opportunistically. + /// + /// @return + /// An error code indicating the success or failure of the operation. + /// Test with Success(). + //------------------------------------------------------------------ + Error + PrepareForExecution (lldb::addr_t &func_addr, + lldb::addr_t &func_end, + std::unique_ptr &execution_unit_ap, + ExecutionContext &exe_ctx, + bool &can_interpret, + lldb_private::ExecutionPolicy execution_policy); + + //------------------------------------------------------------------ + /// Disassemble the machine code for a JITted function from the target + /// process's memory and print the result to a stream. + /// + /// @param[in] stream + /// The stream to print disassembly to. + /// + /// @param[in] exc_context + /// The execution context to get the machine code from. + /// + /// @return + /// The error generated. If .Success() is true, disassembly succeeded. + //------------------------------------------------------------------ + Error + DisassembleFunction (Stream &stream, + ExecutionContext &exe_ctx); + +private: + ClangExpression & m_expr; ///< The expression to be parsed + std::unique_ptr m_llvm_context; ///< The LLVM context to generate IR into + std::unique_ptr m_file_manager; ///< The Clang file manager object used by the compiler + std::unique_ptr m_compiler; ///< The Clang compiler used to parse expressions into IR + std::unique_ptr m_builtin_context; ///< Context for Clang built-ins + std::unique_ptr m_selector_table; ///< Selector table for Objective-C methods + std::unique_ptr m_ast_context; ///< The AST context used to hold types and names for the parser + std::unique_ptr m_code_generator; ///< The Clang object that generates IR + std::unique_ptr m_execution_unit; ///< The container for the finished Module +}; + +} + +#endif // liblldb_ClangExpressionParser_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Expression/ClangExpressionVariable.h b/contrib/llvm/tools/lldb/include/lldb/Expression/ClangExpressionVariable.h new file mode 100644 index 00000000000..620e604fb18 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Expression/ClangExpressionVariable.h @@ -0,0 +1,451 @@ +//===-- ClangExpressionVariable.h -------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ClangExpressionVariable_h_ +#define liblldb_ClangExpressionVariable_h_ + +// C Includes +#include +#include +#include + +// C++ Includes +#include +#include +#include + +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-public.h" +#include "lldb/Core/ClangForward.h" +#include "lldb/Core/ConstString.h" +#include "lldb/Core/Value.h" +#include "lldb/Symbol/TaggedASTType.h" + +namespace llvm { + class Value; +} + +namespace lldb_private { + +class ClangExpressionVariableList; +class ValueObjectConstResult; + +//---------------------------------------------------------------------- +/// @class ClangExpressionVariable ClangExpressionVariable.h "lldb/Expression/ClangExpressionVariable.h" +/// @brief Encapsulates one variable for the expression parser. +/// +/// The expression parser uses variables in three different contexts: +/// +/// First, it stores persistent variables along with the process for use +/// in expressions. These persistent variables contain their own data +/// and are typed. +/// +/// Second, in an interpreted expression, it stores the local variables +/// for the expression along with the expression. These variables +/// contain their own data and are typed. +/// +/// Third, in a JIT-compiled expression, it stores the variables that +/// the expression needs to have materialized and dematerialized at each +/// execution. These do not contain their own data but are named and +/// typed. +/// +/// This class supports all of these use cases using simple type +/// polymorphism, and provides necessary support methods. Its interface +/// is RTTI-neutral. +//---------------------------------------------------------------------- +class ClangExpressionVariable +{ +public: + ClangExpressionVariable(ExecutionContextScope *exe_scope, lldb::ByteOrder byte_order, uint32_t addr_byte_size); + + ClangExpressionVariable(const lldb::ValueObjectSP &valobj_sp); + + //---------------------------------------------------------------------- + /// If the variable contains its own data, make a Value point at it. + /// If \a exe_ctx in not NULL, the value will be resolved in with + /// that execution context. + /// + /// @param[in] value + /// The value to point at the data. + /// + /// @param[in] exe_ctx + /// The execution context to use to resolve \a value. + /// + /// @return + /// True on success; false otherwise (in particular, if this variable + /// does not contain its own data). + //---------------------------------------------------------------------- + bool + PointValueAtData(Value &value, ExecutionContext *exe_ctx); + + lldb::ValueObjectSP + GetValueObject(); + + //---------------------------------------------------------------------- + /// The following values should not live beyond parsing + //---------------------------------------------------------------------- + class ParserVars + { + public: + + ParserVars() : + m_parser_type(), + m_named_decl (NULL), + m_llvm_value (NULL), + m_lldb_value (), + m_lldb_var (), + m_lldb_sym (NULL) + { + } + + TypeFromParser m_parser_type; ///< The type of the variable according to the parser + const clang::NamedDecl *m_named_decl; ///< The Decl corresponding to this variable + llvm::Value *m_llvm_value; ///< The IR value corresponding to this variable; usually a GlobalValue + lldb_private::Value m_lldb_value; ///< The value found in LLDB for this variable + lldb::VariableSP m_lldb_var; ///< The original variable for this variable + const lldb_private::Symbol *m_lldb_sym; ///< The original symbol for this variable, if it was a symbol + }; + +private: + typedef std::map ParserVarMap; + ParserVarMap m_parser_vars; + +public: + //---------------------------------------------------------------------- + /// Make this variable usable by the parser by allocating space for + /// parser-specific variables + //---------------------------------------------------------------------- + void + EnableParserVars(uint64_t parser_id) + { + m_parser_vars.insert(std::make_pair(parser_id, ParserVars())); + } + + //---------------------------------------------------------------------- + /// Deallocate parser-specific variables + //---------------------------------------------------------------------- + void + DisableParserVars(uint64_t parser_id) + { + m_parser_vars.erase(parser_id); + } + + //---------------------------------------------------------------------- + /// Access parser-specific variables + //---------------------------------------------------------------------- + ParserVars * + GetParserVars(uint64_t parser_id) + { + ParserVarMap::iterator i = m_parser_vars.find(parser_id); + + if (i == m_parser_vars.end()) + return NULL; + else + return &i->second; + } + + //---------------------------------------------------------------------- + /// The following values are valid if the variable is used by JIT code + //---------------------------------------------------------------------- + struct JITVars { + JITVars () : + m_alignment (0), + m_size (0), + m_offset (0) + { + } + + off_t m_alignment; ///< The required alignment of the variable, in bytes + size_t m_size; ///< The space required for the variable, in bytes + off_t m_offset; ///< The offset of the variable in the struct, in bytes + }; + +private: + typedef std::map JITVarMap; + JITVarMap m_jit_vars; + +public: + //---------------------------------------------------------------------- + /// Make this variable usable for materializing for the JIT by allocating + /// space for JIT-specific variables + //---------------------------------------------------------------------- + void + EnableJITVars(uint64_t parser_id) + { + m_jit_vars.insert(std::make_pair(parser_id, JITVars())); + } + + //---------------------------------------------------------------------- + /// Deallocate JIT-specific variables + //---------------------------------------------------------------------- + void + DisableJITVars(uint64_t parser_id) + { + m_jit_vars.erase(parser_id); + } + + JITVars *GetJITVars(uint64_t parser_id) + { + JITVarMap::iterator i = m_jit_vars.find(parser_id); + + if (i == m_jit_vars.end()) + return NULL; + else + return &i->second; + } + + //---------------------------------------------------------------------- + /// Return the variable's size in bytes + //---------------------------------------------------------------------- + size_t + GetByteSize (); + + const ConstString & + GetName(); + + RegisterInfo * + GetRegisterInfo(); + + void + SetRegisterInfo (const RegisterInfo *reg_info); + + ClangASTType + GetClangType (); + + void + SetClangType (const ClangASTType &clang_type); + + TypeFromUser + GetTypeFromUser (); + + uint8_t * + GetValueBytes (); + + void + SetName (const ConstString &name); + + void + ValueUpdated (); + + // this function is used to copy the address-of m_live_sp into m_frozen_sp + // this is necessary because the results of certain cast and pointer-arithmetic + // operations (such as those described in bugzilla issues 11588 and 11618) generate + // frozen objcts that do not have a valid address-of, which can be troublesome when + // using synthetic children providers. transferring the address-of the live object + // solves these issues and provides the expected user-level behavior + void + TransferAddress (bool force = false); + + typedef std::shared_ptr ValueObjectConstResultSP; + + //---------------------------------------------------------------------- + /// Members + //---------------------------------------------------------------------- + enum Flags + { + EVNone = 0, + EVIsLLDBAllocated = 1 << 0, ///< This variable is resident in a location specifically allocated for it by LLDB in the target process + EVIsProgramReference = 1 << 1, ///< This variable is a reference to a (possibly invalid) area managed by the target program + EVNeedsAllocation = 1 << 2, ///< Space for this variable has yet to be allocated in the target process + EVIsFreezeDried = 1 << 3, ///< This variable's authoritative version is in m_frozen_sp (for example, for statically-computed results) + EVNeedsFreezeDry = 1 << 4, ///< Copy from m_live_sp to m_frozen_sp during dematerialization + EVKeepInTarget = 1 << 5, ///< Keep the allocation after the expression is complete rather than freeze drying its contents and freeing it + EVTypeIsReference = 1 << 6, ///< The original type of this variable is a reference, so materialize the value rather than the location + EVUnknownType = 1 << 7, ///< This is a symbol of unknown type, and the type must be resolved after parsing is complete + EVBareRegister = 1 << 8 ///< This variable is a direct reference to $pc or some other entity. + }; + + typedef uint16_t FlagType; + + FlagType m_flags; // takes elements of Flags + + lldb::ValueObjectSP m_frozen_sp; + lldb::ValueObjectSP m_live_sp; + + DISALLOW_COPY_AND_ASSIGN (ClangExpressionVariable); +}; + +//---------------------------------------------------------------------- +/// @class ClangExpressionVariableListBase ClangExpressionVariable.h "lldb/Expression/ClangExpressionVariable.h" +/// @brief A list of variable references. +/// +/// This class stores variables internally, acting as the permanent store. +//---------------------------------------------------------------------- +class ClangExpressionVariableList +{ +public: + //---------------------------------------------------------------------- + /// Implementation of methods in ClangExpressionVariableListBase + //---------------------------------------------------------------------- + size_t + GetSize() + { + return m_variables.size(); + } + + lldb::ClangExpressionVariableSP + GetVariableAtIndex(size_t index) + { + lldb::ClangExpressionVariableSP var_sp; + if (index < m_variables.size()) + var_sp = m_variables[index]; + return var_sp; + } + + size_t + AddVariable (const lldb::ClangExpressionVariableSP &var_sp) + { + m_variables.push_back(var_sp); + return m_variables.size() - 1; + } + + bool + ContainsVariable (const lldb::ClangExpressionVariableSP &var_sp) + { + const size_t size = m_variables.size(); + for (size_t index = 0; index < size; ++index) + { + if (m_variables[index].get() == var_sp.get()) + return true; + } + return false; + } + + //---------------------------------------------------------------------- + /// Finds a variable by name in the list. + /// + /// @param[in] name + /// The name of the requested variable. + /// + /// @return + /// The variable requested, or NULL if that variable is not in the list. + //---------------------------------------------------------------------- + lldb::ClangExpressionVariableSP + GetVariable (const ConstString &name) + { + lldb::ClangExpressionVariableSP var_sp; + for (size_t index = 0, size = GetSize(); index < size; ++index) + { + var_sp = GetVariableAtIndex(index); + if (var_sp->GetName() == name) + return var_sp; + } + var_sp.reset(); + return var_sp; + } + + lldb::ClangExpressionVariableSP + GetVariable (const char *name) + { + lldb::ClangExpressionVariableSP var_sp; + if (name && name[0]) + { + for (size_t index = 0, size = GetSize(); index < size; ++index) + { + var_sp = GetVariableAtIndex(index); + const char *var_name_cstr = var_sp->GetName().GetCString(); + if (!var_name_cstr || !name) + continue; + if (::strcmp (var_name_cstr, name) == 0) + return var_sp; + } + var_sp.reset(); + } + return var_sp; + } + + //---------------------------------------------------------------------- + /// Finds a variable by NamedDecl in the list. + /// + /// @param[in] name + /// The name of the requested variable. + /// + /// @return + /// The variable requested, or NULL if that variable is not in the list. + //---------------------------------------------------------------------- + lldb::ClangExpressionVariableSP + GetVariable (const clang::NamedDecl *decl, uint64_t parser_id) + { + lldb::ClangExpressionVariableSP var_sp; + for (size_t index = 0, size = GetSize(); index < size; ++index) + { + var_sp = GetVariableAtIndex(index); + + ClangExpressionVariable::ParserVars *parser_vars = var_sp->GetParserVars(parser_id); + + if (parser_vars && parser_vars->m_named_decl == decl) + return var_sp; + } + var_sp.reset(); + return var_sp; + } + + //---------------------------------------------------------------------- + /// Create a new variable in the list and return its index + //---------------------------------------------------------------------- + lldb::ClangExpressionVariableSP + CreateVariable (ExecutionContextScope *exe_scope, lldb::ByteOrder byte_order, uint32_t addr_byte_size) + { + lldb::ClangExpressionVariableSP var_sp(new ClangExpressionVariable(exe_scope, byte_order, addr_byte_size)); + m_variables.push_back(var_sp); + return var_sp; + } + + lldb::ClangExpressionVariableSP + CreateVariable(const lldb::ValueObjectSP &valobj_sp) + { + lldb::ClangExpressionVariableSP var_sp(new ClangExpressionVariable(valobj_sp)); + m_variables.push_back(var_sp); + return var_sp; + } + + lldb::ClangExpressionVariableSP + CreateVariable (ExecutionContextScope *exe_scope, + const ConstString &name, + const TypeFromUser& user_type, + lldb::ByteOrder byte_order, + uint32_t addr_byte_size) + { + lldb::ClangExpressionVariableSP var_sp(new ClangExpressionVariable(exe_scope, byte_order, addr_byte_size)); + var_sp->SetName (name); + var_sp->SetClangType (user_type); + m_variables.push_back(var_sp); + return var_sp; + } + + void + RemoveVariable (lldb::ClangExpressionVariableSP var_sp) + { + for (std::vector::iterator vi = m_variables.begin(), ve = m_variables.end(); + vi != ve; + ++vi) + { + if (vi->get() == var_sp.get()) + { + m_variables.erase(vi); + return; + } + } + } + + void + Clear() + { + m_variables.clear(); + } + +private: + std::vector m_variables; +}; + + +} // namespace lldb_private + +#endif // liblldb_ClangExpressionVariable_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Expression/ClangFunction.h b/contrib/llvm/tools/lldb/include/lldb/Expression/ClangFunction.h new file mode 100644 index 00000000000..3f96f7bd311 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Expression/ClangFunction.h @@ -0,0 +1,652 @@ +//===-- ClangFunction.h -----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef lldb_ClangFunction_h_ +#define lldb_ClangFunction_h_ + +// C Includes +// C++ Includes +#include +#include +// Other libraries and framework includes +// Project includes +#include "lldb/Core/ClangForward.h" +#include "lldb/Core/Address.h" +#include "lldb/Core/ArchSpec.h" +#include "lldb/Core/Value.h" +#include "lldb/Core/ValueObjectList.h" +#include "lldb/Expression/ClangExpression.h" +#include "lldb/Target/Process.h" + +namespace lldb_private +{ + +class ASTStructExtractor; +class ClangExpressionParser; + +//---------------------------------------------------------------------- +/// @class ClangFunction ClangFunction.h "lldb/Expression/ClangFunction.h" +/// @brief Encapsulates a function that can be called. +/// +/// A given ClangFunction object can handle a single function signature. +/// Once constructed, it can set up any number of concurrent calls to +/// functions with that signature. +/// +/// It performs the call by synthesizing a structure that contains the pointer +/// to the function and the arguments that should be passed to that function, +/// and producing a special-purpose JIT-compiled function that accepts a void* +/// pointing to this struct as its only argument and calls the function in the +/// struct with the written arguments. This method lets Clang handle the +/// vagaries of function calling conventions. +/// +/// The simplest use of the ClangFunction is to construct it with a +/// function representative of the signature you want to use, then call +/// ExecuteFunction(ExecutionContext &, Stream &, Value &). +/// +/// If you need to reuse the arguments for several calls, you can call +/// InsertFunction() followed by WriteFunctionArguments(), which will return +/// the location of the args struct for the wrapper function in args_addr_ref. +/// +/// If you need to call the function on the thread plan stack, you can also +/// call InsertFunction() followed by GetThreadPlanToCallFunction(). +/// +/// Any of the methods that take arg_addr_ptr or arg_addr_ref can be passed +/// a pointer set to LLDB_INVALID_ADDRESS and new structure will be allocated +/// and its address returned in that variable. +/// +/// Any of the methods that take arg_addr_ptr can be passed NULL, and the +/// argument space will be managed for you. +//---------------------------------------------------------------------- +class ClangFunction : public ClangExpression +{ + friend class ASTStructExtractor; +public: + //------------------------------------------------------------------ + /// Constructor + /// + /// @param[in] exe_scope + /// An execution context scope that gets us at least a target and + /// process. + /// + /// @param[in] function_ptr + /// The default function to be called. Can be overridden using + /// WriteFunctionArguments(). + /// + /// @param[in] ast_context + /// The AST context to evaluate argument types in. + /// + /// @param[in] arg_value_list + /// The default values to use when calling this function. Can + /// be overridden using WriteFunctionArguments(). + //------------------------------------------------------------------ + ClangFunction (ExecutionContextScope &exe_scope, + Function &function_ptr, + ClangASTContext *ast_context, + const ValueList &arg_value_list); + + //------------------------------------------------------------------ + /// Constructor + /// + /// @param[in] exe_scope + /// An execution context scope that gets us at least a target and + /// process. + /// + /// @param[in] ast_context + /// The AST context to evaluate argument types in. + /// + /// @param[in] return_qualtype + /// An opaque Clang QualType for the function result. Should be + /// defined in ast_context. + /// + /// @param[in] function_address + /// The address of the function to call. + /// + /// @param[in] arg_value_list + /// The default values to use when calling this function. Can + /// be overridden using WriteFunctionArguments(). + //------------------------------------------------------------------ + ClangFunction (ExecutionContextScope &exe_scope, + const ClangASTType &return_type, + const Address& function_address, + const ValueList &arg_value_list); + + //------------------------------------------------------------------ + /// Destructor + //------------------------------------------------------------------ + virtual + ~ClangFunction(); + + //------------------------------------------------------------------ + /// Compile the wrapper function + /// + /// @param[in] errors + /// The stream to print parser errors to. + /// + /// @return + /// The number of errors. + //------------------------------------------------------------------ + unsigned + CompileFunction (Stream &errors); + + //------------------------------------------------------------------ + /// Insert the default function wrapper and its default argument struct + /// + /// @param[in] exe_ctx + /// The execution context to insert the function and its arguments + /// into. + /// + /// @param[in,out] args_addr_ref + /// The address of the structure to write the arguments into. May + /// be LLDB_INVALID_ADDRESS; if it is, a new structure is allocated + /// and args_addr_ref is pointed to it. + /// + /// @param[in] errors + /// The stream to write errors to. + /// + /// @return + /// True on success; false otherwise. + //------------------------------------------------------------------ + bool + InsertFunction (ExecutionContext &exe_ctx, + lldb::addr_t &args_addr_ref, + Stream &errors); + + //------------------------------------------------------------------ + /// Insert the default function wrapper (using the JIT) + /// + /// @param[in] exe_ctx + /// The execution context to insert the function and its arguments + /// into. + /// + /// @param[in] errors + /// The stream to write errors to. + /// + /// @return + /// True on success; false otherwise. + //------------------------------------------------------------------ + bool WriteFunctionWrapper (ExecutionContext &exe_ctx, + Stream &errors); + + //------------------------------------------------------------------ + /// Insert the default function argument struct + /// + /// @param[in] exe_ctx + /// The execution context to insert the function and its arguments + /// into. + /// + /// @param[in,out] args_addr_ref + /// The address of the structure to write the arguments into. May + /// be LLDB_INVALID_ADDRESS; if it is, a new structure is allocated + /// and args_addr_ref is pointed to it. + /// + /// @param[in] errors + /// The stream to write errors to. + /// + /// @return + /// True on success; false otherwise. + //------------------------------------------------------------------ + bool WriteFunctionArguments (ExecutionContext &exe_ctx, + lldb::addr_t &args_addr_ref, + Stream &errors); + + //------------------------------------------------------------------ + /// Insert an argument struct with a non-default function address and + /// non-default argument values + /// + /// @param[in] exe_ctx + /// The execution context to insert the function and its arguments + /// into. + /// + /// @param[in,out] args_addr_ref + /// The address of the structure to write the arguments into. May + /// be LLDB_INVALID_ADDRESS; if it is, a new structure is allocated + /// and args_addr_ref is pointed to it. + /// + /// @param[in] function_address + /// The address of the function to call. + /// + /// @param[in] arg_values + /// The values of the function's arguments. + /// + /// @param[in] errors + /// The stream to write errors to. + /// + /// @return + /// True on success; false otherwise. + //------------------------------------------------------------------ + bool WriteFunctionArguments (ExecutionContext &exe_ctx, + lldb::addr_t &args_addr_ref, + Address function_address, + ValueList &arg_values, + Stream &errors); + + //------------------------------------------------------------------ + /// [Static] Execute a function, passing it a single void* parameter. + /// ClangFunction uses this to call the wrapper function. + /// + /// @param[in] exe_ctx + /// The execution context to insert the function and its arguments + /// into. + /// + /// @param[in] function_address + /// The address of the function in the target process. + /// + /// @param[in] void_arg + /// The value of the void* parameter. + /// + /// @param[in] stop_others + /// True if other threads should pause during execution. + /// + /// @param[in] try_all_threads + /// If the timeout expires, true if other threads should run. If + /// the function may try to take locks, this is useful. + /// + /// @param[in] unwind_on_error + /// If true, and the execution stops before completion, we unwind the + /// function call, and return the program state to what it was before the + /// execution. If false, we leave the program in the stopped state. + /// + /// @param[in] timeout_usec + /// Timeout value (0 for no timeout). If try_all_threads is true, then we + /// will try on one thread for the lesser of .25 sec and half the total timeout. + /// then switch to running all threads, otherwise this will be the total timeout. + /// + /// @param[in] errors + /// The stream to write errors to. + /// + /// @param[in] this_arg + /// If non-NULL, the function is invoked like a C++ method, with the + /// value pointed to by the pointer as its 'this' argument. + /// + /// @return + /// Returns one of the ExecutionResults enum indicating function call status. + //------------------------------------------------------------------ + static ExecutionResults + ExecuteFunction (ExecutionContext &exe_ctx, + lldb::addr_t function_address, + lldb::addr_t &void_arg, + bool stop_others, + bool try_all_threads, + bool unwind_on_error, + bool ignore_breakpoints, + uint32_t timeout_usec, + Stream &errors, + lldb::addr_t* this_arg = 0); + + //------------------------------------------------------------------ + /// Run the function this ClangFunction was created with. + /// + /// This simple version will run the function stopping other threads + /// for a fixed timeout period (1000 usec) and if it does not complete, + /// we halt the process and try with all threads running. + /// + /// @param[in] exe_ctx + /// The thread & process in which this function will run. + /// + /// @param[in] errors + /// Errors will be written here if there are any. + /// + /// @param[out] results + /// The result value will be put here after running the function. + /// + /// @return + /// Returns one of the ExecutionResults enum indicating function call status. + //------------------------------------------------------------------ + ExecutionResults + ExecuteFunction(ExecutionContext &exe_ctx, + Stream &errors, + Value &results); + + //------------------------------------------------------------------ + /// Run the function this ClangFunction was created with. + /// + /// This simple version will run the function obeying the stop_others + /// argument. There is no timeout. + /// + /// @param[in] exe_ctx + /// The thread & process in which this function will run. + /// + /// @param[in] errors + /// Errors will be written here if there are any. + /// + /// @param[in] stop_others + /// If \b true, run only this thread, if \b false let all threads run. + /// + /// @param[out] results + /// The result value will be put here after running the function. + /// + /// @return + /// Returns one of the ExecutionResults enum indicating function call status. + //------------------------------------------------------------------ + ExecutionResults + ExecuteFunction(ExecutionContext &exe_ctx, + Stream &errors, bool stop_others, + Value &results); + + //------------------------------------------------------------------ + /// Run the function this ClangFunction was created with. + /// + /// This simple version will run the function on one thread. If \a timeout_usec + /// is not zero, we time out after that timeout. If \a try_all_threads is true, then we will + /// resume with all threads on, otherwise we halt the process, and eExecutionInterrupted will be returned. + /// + /// @param[in] exe_ctx + /// The thread & process in which this function will run. + /// + /// @param[in] errors + /// Errors will be written here if there are any. + /// + /// @param[in] timeout_usec + /// Timeout value (0 for no timeout). If try_all_threads is true, then we + /// will try on one thread for the lesser of .25 sec and half the total timeout. + /// then switch to running all threads, otherwise this will be the total timeout. + /// + /// @param[in] try_all_threads + /// If \b true, run only this thread, if \b false let all threads run. + /// + /// @param[out] results + /// The result value will be put here after running the function. + /// + /// @return + /// Returns one of the ExecutionResults enum indicating function call status. + //------------------------------------------------------------------ + ExecutionResults + ExecuteFunction(ExecutionContext &exe_ctx, + Stream &errors, + uint32_t single_thread_timeout_usec, + bool try_all_threads, + Value &results); + + //------------------------------------------------------------------ + /// Run the function this ClangFunction was created with. + /// + /// This is the full version. + /// + /// @param[in] exe_ctx + /// The thread & process in which this function will run. + /// + /// @param[in] args_addr_ptr + /// If NULL, the function will take care of allocating & deallocating the wrapper + /// args structure. Otherwise, if set to LLDB_INVALID_ADDRESS, a new structure + /// will be allocated, filled and the address returned to you. You are responsible + /// for deallocating it. And if passed in with a value other than LLDB_INVALID_ADDRESS, + /// this should point to an already allocated structure with the values already written. + /// + /// @param[in] errors + /// Errors will be written here if there are any. + /// + /// @param[in] stop_others + /// If \b true, run only this thread, if \b false let all threads run. + /// + /// @param[in] timeout_usec + /// Timeout value (0 for no timeout). If try_all_threads is true, then we + /// will try on one thread for the lesser of .25 sec and half the total timeout. + /// then switch to running all threads, otherwise this will be the total timeout. + /// + /// + /// @param[in] try_all_threads + /// If \b true, run only this thread, if \b false let all threads run. + /// + /// @param[out] results + /// The result value will be put here after running the function. + /// + /// @return + /// Returns one of the ExecutionResults enum indicating function call status. + //------------------------------------------------------------------ + ExecutionResults + ExecuteFunction(ExecutionContext &exe_ctx, + lldb::addr_t *args_addr_ptr, + Stream &errors, + bool stop_others, + uint32_t timeout_usec, + bool try_all_threads, + bool unwind_on_error, + bool ignore_breakpoints, + Value &results); + + //------------------------------------------------------------------ + /// [static] Get a thread plan to run a function. + /// + /// @param[in] exe_ctx + /// The execution context to insert the function and its arguments + /// into. + /// + /// @param[in] func_addr + /// The address of the function in the target process. + /// + /// @param[in] args_addr_ref + /// The value of the void* parameter. + /// + /// @param[in] errors + /// The stream to write errors to. + /// + /// @param[in] stop_others + /// True if other threads should pause during execution. + /// + /// @param[in] unwind_on_error + /// True if the thread plan may simply be discarded if an error occurs. + /// + /// @param[in] ignore_breakpoints + /// True if the expression execution will ignore breakpoint hits and continue executing. + /// + /// @param[in] this_arg + /// If non-NULL (and cmd_arg is NULL), the function is invoked like a C++ + /// method, with the value pointed to by the pointer as its 'this' + /// argument. + /// + /// @param[in] cmd_arg + /// If non-NULL, the function is invoked like an Objective-C method, with + /// this_arg in the 'self' slot and cmd_arg in the '_cmd' slot + /// + /// @return + /// A ThreadPlan for executing the function. + //------------------------------------------------------------------ + static ThreadPlan * + GetThreadPlanToCallFunction (ExecutionContext &exe_ctx, + lldb::addr_t func_addr, + lldb::addr_t &args_addr_ref, + Stream &errors, + bool stop_others, + bool unwind_on_error, + bool ignore_breakpoints, + lldb::addr_t *this_arg = 0, + lldb::addr_t *cmd_arg = 0); + + //------------------------------------------------------------------ + /// Get a thread plan to run the function this ClangFunction was created with. + /// + /// @param[in] exe_ctx + /// The execution context to insert the function and its arguments + /// into. + /// + /// @param[in] func_addr + /// The address of the function in the target process. + /// + /// @param[in] args_addr_ref + /// The value of the void* parameter. + /// + /// @param[in] errors + /// The stream to write errors to. + /// + /// @param[in] stop_others + /// True if other threads should pause during execution. + /// + /// @param[in] unwind_on_error + /// True if the thread plan may simply be discarded if an error occurs. + /// + /// @return + /// A ThreadPlan for executing the function. + //------------------------------------------------------------------ + ThreadPlan * + GetThreadPlanToCallFunction (ExecutionContext &exe_ctx, + lldb::addr_t &args_addr_ref, + Stream &errors, + bool stop_others, + bool unwind_on_error = true, + bool ignore_breakpoints = true) + { + return ClangFunction::GetThreadPlanToCallFunction (exe_ctx, + m_jit_start_addr, + args_addr_ref, + errors, + stop_others, + unwind_on_error, + ignore_breakpoints); + } + + //------------------------------------------------------------------ + /// Get the result of the function from its struct + /// + /// @param[in] exe_ctx + /// The execution context to retrieve the result from. + /// + /// @param[in] args_addr + /// The address of the argument struct. + /// + /// @param[in] ret_value + /// The value returned by the function. + /// + /// @return + /// True on success; false otherwise. + //------------------------------------------------------------------ + bool FetchFunctionResults (ExecutionContext &exe_ctx, + lldb::addr_t args_addr, + Value &ret_value); + + //------------------------------------------------------------------ + /// Deallocate the arguments structure + /// + /// @param[in] exe_ctx + /// The execution context to insert the function and its arguments + /// into. + /// + /// @param[in] args_addr + /// The address of the argument struct. + //------------------------------------------------------------------ + void DeallocateFunctionResults (ExecutionContext &exe_ctx, + lldb::addr_t args_addr); + + //------------------------------------------------------------------ + /// Interface for ClangExpression + //------------------------------------------------------------------ + + //------------------------------------------------------------------ + /// Return the string that the parser should parse. Must be a full + /// translation unit. + //------------------------------------------------------------------ + const char * + Text () + { + return m_wrapper_function_text.c_str(); + } + + //------------------------------------------------------------------ + /// Return the function name that should be used for executing the + /// expression. Text() should contain the definition of this + /// function. + //------------------------------------------------------------------ + const char * + FunctionName () + { + return m_wrapper_function_name.c_str(); + } + + //------------------------------------------------------------------ + /// Return the object that the parser should use when resolving external + /// values. May be NULL if everything should be self-contained. + //------------------------------------------------------------------ + ClangExpressionDeclMap * + DeclMap () + { + return NULL; + } + + //------------------------------------------------------------------ + /// Return the object that the parser should use when registering + /// local variables. May be NULL if the Expression doesn't care. + //------------------------------------------------------------------ + ClangExpressionVariableList * + LocalVariables () + { + return NULL; + } + + //------------------------------------------------------------------ + /// Return the object that the parser should allow to access ASTs. + /// May be NULL if the ASTs do not need to be transformed. + /// + /// @param[in] passthrough + /// The ASTConsumer that the returned transformer should send + /// the ASTs to after transformation. + //------------------------------------------------------------------ + clang::ASTConsumer * + ASTTransformer (clang::ASTConsumer *passthrough); + + //------------------------------------------------------------------ + /// Return true if validation code should be inserted into the + /// expression. + //------------------------------------------------------------------ + bool + NeedsValidation () + { + return false; + } + + //------------------------------------------------------------------ + /// Return true if external variables in the expression should be + /// resolved. + //------------------------------------------------------------------ + bool + NeedsVariableResolution () + { + return false; + } + + ValueList + GetArgumentValues () const + { + return m_arg_values; + } +private: + //------------------------------------------------------------------ + // For ClangFunction only + //------------------------------------------------------------------ + + std::unique_ptr m_parser; ///< The parser responsible for compiling the function. + std::unique_ptr m_execution_unit_ap; + + Function *m_function_ptr; ///< The function we're going to call. May be NULL if we don't have debug info for the function. + Address m_function_addr; ///< If we don't have the FunctionSP, we at least need the address & return type. + ClangASTType m_function_return_type; ///< The opaque clang qual type for the function return type. + ClangASTContext *m_clang_ast_context; ///< This is the clang_ast_context that we're getting types from the and value, and the function return the function pointer is NULL. + + std::string m_wrapper_function_name; ///< The name of the wrapper function. + std::string m_wrapper_function_text; ///< The contents of the wrapper function. + std::string m_wrapper_struct_name; ///< The name of the struct that contains the target function address, arguments, and result. + std::list m_wrapper_args_addrs; ///< The addresses of the arguments to the wrapper function. + + bool m_struct_valid; ///< True if the ASTStructExtractor has populated the variables below. + + //------------------------------------------------------------------ + /// These values are populated by the ASTStructExtractor + size_t m_struct_size; ///< The size of the argument struct, in bytes. + std::vector m_member_offsets; ///< The offset of each member in the struct, in bytes. + uint64_t m_return_size; ///< The size of the result variable, in bytes. + uint64_t m_return_offset; ///< The offset of the result variable in the struct, in bytes. + //------------------------------------------------------------------ + + ValueList m_arg_values; ///< The default values of the arguments. + + bool m_compiled; ///< True if the wrapper function has already been parsed. + bool m_JITted; ///< True if the wrapper function has already been JIT-compiled. +}; + +} // Namespace lldb_private + +#endif // lldb_ClangFunction_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Expression/ClangPersistentVariables.h b/contrib/llvm/tools/lldb/include/lldb/Expression/ClangPersistentVariables.h new file mode 100644 index 00000000000..6d9dae95473 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Expression/ClangPersistentVariables.h @@ -0,0 +1,75 @@ +//===-- ClangPersistentVariables.h ------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ClangPersistentVariables_h_ +#define liblldb_ClangPersistentVariables_h_ + +#include "lldb/Expression/ClangExpressionVariable.h" +#include "llvm/ADT/DenseMap.h" + +namespace lldb_private +{ + +//---------------------------------------------------------------------- +/// @class ClangPersistentVariables ClangPersistentVariables.h "lldb/Expression/ClangPersistentVariables.h" +/// @brief Manages persistent values that need to be preserved between expression invocations. +/// +/// A list of variables that can be accessed and updated by any expression. See +/// ClangPersistentVariable for more discussion. Also provides an increasing, +/// 0-based counter for naming result variables. +//---------------------------------------------------------------------- +class ClangPersistentVariables : public ClangExpressionVariableList +{ +public: + + //---------------------------------------------------------------------- + /// Constructor + //---------------------------------------------------------------------- + ClangPersistentVariables (); + + lldb::ClangExpressionVariableSP + CreatePersistentVariable (const lldb::ValueObjectSP &valobj_sp); + + lldb::ClangExpressionVariableSP + CreatePersistentVariable (ExecutionContextScope *exe_scope, + const ConstString &name, + const TypeFromUser& user_type, + lldb::ByteOrder byte_order, + uint32_t addr_byte_size); + + //---------------------------------------------------------------------- + /// Return the next entry in the sequence of strings "$0", "$1", ... for + /// use naming persistent expression convenience variables. + /// + /// @return + /// A string that contains the next persistent variable name. + //---------------------------------------------------------------------- + ConstString + GetNextPersistentVariableName (); + + void + RemovePersistentVariable (lldb::ClangExpressionVariableSP variable); + + void + RegisterPersistentType (const ConstString &name, + clang::TypeDecl *tag_decl); + + clang::TypeDecl * + GetPersistentType (const ConstString &name); + +private: + uint32_t m_next_persistent_variable_id; ///< The counter used by GetNextResultName(). + + typedef llvm::DenseMap PersistentTypeMap; + PersistentTypeMap m_persistent_types; ///< The persistent types declared by the user. +}; + +} + +#endif diff --git a/contrib/llvm/tools/lldb/include/lldb/Expression/ClangUserExpression.h b/contrib/llvm/tools/lldb/include/lldb/Expression/ClangUserExpression.h new file mode 100644 index 00000000000..47bfebb4664 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Expression/ClangUserExpression.h @@ -0,0 +1,432 @@ +//===-- ClangUserExpression.h -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ClangUserExpression_h_ +#define liblldb_ClangUserExpression_h_ + +// C Includes +// C++ Includes +#include +#include +#include + +// Other libraries and framework includes +// Project includes + +#include "lldb/lldb-forward.h" +#include "lldb/lldb-private.h" +#include "lldb/Core/Address.h" +#include "lldb/Core/ClangForward.h" +#include "lldb/Expression/ClangExpression.h" +#include "lldb/Expression/ClangExpressionVariable.h" +#include "lldb/Expression/IRForTarget.h" +#include "lldb/Expression/Materializer.h" +#include "lldb/Symbol/TaggedASTType.h" +#include "lldb/Target/ExecutionContext.h" + +#include "llvm/ExecutionEngine/JITMemoryManager.h" + +namespace lldb_private +{ + +//---------------------------------------------------------------------- +/// @class ClangUserExpression ClangUserExpression.h "lldb/Expression/ClangUserExpression.h" +/// @brief Encapsulates a single expression for use with Clang +/// +/// LLDB uses expressions for various purposes, notably to call functions +/// and as a backend for the expr command. ClangUserExpression encapsulates +/// the objects needed to parse and interpret or JIT an expression. It +/// uses the Clang parser to produce LLVM IR from the expression. +//---------------------------------------------------------------------- +class ClangUserExpression : public ClangExpression +{ +public: + typedef std::shared_ptr ClangUserExpressionSP; + + enum { kDefaultTimeout = 500000u }; + //------------------------------------------------------------------ + /// Constructor + /// + /// @param[in] expr + /// The expression to parse. + /// + /// @param[in] expr_prefix + /// If non-NULL, a C string containing translation-unit level + /// definitions to be included when the expression is parsed. + /// + /// @param[in] language + /// If not eLanguageTypeUnknown, a language to use when parsing + /// the expression. Currently restricted to those languages + /// supported by Clang. + /// + /// @param[in] desired_type + /// If not eResultTypeAny, the type to use for the expression + /// result. + //------------------------------------------------------------------ + ClangUserExpression (const char *expr, + const char *expr_prefix, + lldb::LanguageType language, + ResultType desired_type); + + //------------------------------------------------------------------ + /// Destructor + //------------------------------------------------------------------ + virtual + ~ClangUserExpression (); + + //------------------------------------------------------------------ + /// Parse the expression + /// + /// @param[in] error_stream + /// A stream to print parse errors and warnings to. + /// + /// @param[in] exe_ctx + /// The execution context to use when looking up entities that + /// are needed for parsing (locations of functions, types of + /// variables, persistent variables, etc.) + /// + /// @param[in] execution_policy + /// Determines whether interpretation is possible or mandatory. + /// + /// @param[in] keep_result_in_memory + /// True if the resulting persistent variable should reside in + /// target memory, if applicable. + /// + /// @return + /// True on success (no errors); false otherwise. + //------------------------------------------------------------------ + bool + Parse (Stream &error_stream, + ExecutionContext &exe_ctx, + lldb_private::ExecutionPolicy execution_policy, + bool keep_result_in_memory); + + bool + CanInterpret () + { + return m_can_interpret; + } + + bool + MatchesContext (ExecutionContext &exe_ctx); + + //------------------------------------------------------------------ + /// Execute the parsed expression + /// + /// @param[in] error_stream + /// A stream to print errors to. + /// + /// @param[in] exe_ctx + /// The execution context to use when looking up entities that + /// are needed for parsing (locations of variables, etc.) + /// + /// @param[in] unwind_on_error + /// If true, and the execution stops before completion, we unwind the + /// function call, and return the program state to what it was before the + /// execution. If false, we leave the program in the stopped state. + /// + /// @param[in] ignore_breakpoints + /// If true, ignore breakpoints while executing the expression. + /// + /// @param[in] shared_ptr_to_me + /// This is a shared pointer to this ClangUserExpression. This is + /// needed because Execute can push a thread plan that will hold onto + /// the ClangUserExpression for an unbounded period of time. So you + /// need to give the thread plan a reference to this object that can + /// keep it alive. + /// + /// @param[in] result + /// A pointer to direct at the persistent variable in which the + /// expression's result is stored. + /// + /// @param[in] try_all_threads + /// If true, then we will try to run all threads if the function doesn't complete on + /// one thread. See timeout_usec for the interaction of this variable and + /// the timeout. + /// + /// @param[in] timeout_usec + /// Timeout value (0 for no timeout). If try_all_threads is true, then we + /// will try on one thread for the lesser of .25 sec and half the total timeout. + /// then switch to running all threads, otherwise this will be the total timeout. + /// + /// + /// @return + /// A Process::Execution results value. + //------------------------------------------------------------------ + ExecutionResults + Execute (Stream &error_stream, + ExecutionContext &exe_ctx, + bool unwind_on_error, + bool ignore_breakpoints, + ClangUserExpressionSP &shared_ptr_to_me, + lldb::ClangExpressionVariableSP &result, + bool try_all_threads, + uint32_t timeout_usec); + + ThreadPlan * + GetThreadPlanToExecuteJITExpression (Stream &error_stream, + ExecutionContext &exe_ctx); + + //------------------------------------------------------------------ + /// Apply the side effects of the function to program state. + /// + /// @param[in] error_stream + /// A stream to print errors to. + /// + /// @param[in] exe_ctx + /// The execution context to use when looking up entities that + /// are needed for parsing (locations of variables, etc.) + /// + /// @param[in] result + /// A pointer to direct at the persistent variable in which the + /// expression's result is stored. + /// + /// @param[in] function_stack_pointer + /// A pointer to the base of the function's stack frame. This + /// is used to determine whether the expession result resides in + /// memory that will still be valid, or whether it needs to be + /// treated as homeless for the purpose of future expressions. + /// + /// @return + /// A Process::Execution results value. + //------------------------------------------------------------------ + bool + FinalizeJITExecution (Stream &error_stream, + ExecutionContext &exe_ctx, + lldb::ClangExpressionVariableSP &result, + lldb::addr_t function_stack_bottom = LLDB_INVALID_ADDRESS, + lldb::addr_t function_stack_top = LLDB_INVALID_ADDRESS); + + //------------------------------------------------------------------ + /// Return the string that the parser should parse. Must be a full + /// translation unit. + //------------------------------------------------------------------ + const char * + Text () + { + return m_transformed_text.c_str(); + } + + //------------------------------------------------------------------ + /// Return the string that the user typed. + //------------------------------------------------------------------ + const char * + GetUserText () + { + return m_expr_text.c_str(); + } + + //------------------------------------------------------------------ + /// Return the function name that should be used for executing the + /// expression. Text() should contain the definition of this + /// function. + //------------------------------------------------------------------ + const char * + FunctionName () + { + return "$__lldb_expr"; + } + + //------------------------------------------------------------------ + /// Return the language that should be used when parsing. To use + /// the default, return eLanguageTypeUnknown. + //------------------------------------------------------------------ + virtual lldb::LanguageType + Language () + { + return m_language; + } + + //------------------------------------------------------------------ + /// Return the object that the parser should use when resolving external + /// values. May be NULL if everything should be self-contained. + //------------------------------------------------------------------ + ClangExpressionDeclMap * + DeclMap () + { + return m_expr_decl_map.get(); + } + + //------------------------------------------------------------------ + /// Return the object that the parser should allow to access ASTs. + /// May be NULL if the ASTs do not need to be transformed. + /// + /// @param[in] passthrough + /// The ASTConsumer that the returned transformer should send + /// the ASTs to after transformation. + //------------------------------------------------------------------ + clang::ASTConsumer * + ASTTransformer (clang::ASTConsumer *passthrough); + + //------------------------------------------------------------------ + /// Return the desired result type of the function, or + /// eResultTypeAny if indifferent. + //------------------------------------------------------------------ + virtual ResultType + DesiredResultType () + { + return m_desired_type; + } + + //------------------------------------------------------------------ + /// Return true if validation code should be inserted into the + /// expression. + //------------------------------------------------------------------ + bool + NeedsValidation () + { + return true; + } + + //------------------------------------------------------------------ + /// Return true if external variables in the expression should be + /// resolved. + //------------------------------------------------------------------ + bool + NeedsVariableResolution () + { + return true; + } + + //------------------------------------------------------------------ + /// Evaluate one expression and return its result. + /// + /// @param[in] exe_ctx + /// The execution context to use when evaluating the expression. + /// + /// @param[in] execution_policy + /// Determines whether or not to try using the IR interpreter to + /// avoid running the expression on the parser. + /// + /// @param[in] language + /// If not eLanguageTypeUnknown, a language to use when parsing + /// the expression. Currently restricted to those languages + /// supported by Clang. + /// + /// @param[in] unwind_on_error + /// True if the thread's state should be restored in the case + /// of an error. + /// + /// @param[in] ignore_breakpoints + /// If true, ignore breakpoints while executing the expression. + /// + /// @param[in] result_type + /// If not eResultTypeAny, the type of the desired result. Will + /// result in parse errors if impossible. + /// + /// @param[in] expr_cstr + /// A C string containing the expression to be evaluated. + /// + /// @param[in] expr_prefix + /// If non-NULL, a C string containing translation-unit level + /// definitions to be included when the expression is parsed. + /// + /// @param[in/out] result_valobj_sp + /// If execution is successful, the result valobj is placed here. + /// + /// @param[in] try_all_threads + /// If true, then we will try to run all threads if the function doesn't complete on + /// one thread. See timeout_usec for the interaction of this variable and + /// the timeout. + /// + /// @param[in] timeout_usec + /// Timeout value (0 for no timeout). If try_all_threads is true, then we + /// will try on one thread for the lesser of .25 sec and half the total timeout. + /// then switch to running all threads, otherwise this will be the total timeout. + /// + /// @result + /// A Process::ExecutionResults value. eExecutionCompleted for success. + //------------------------------------------------------------------ + static ExecutionResults + Evaluate (ExecutionContext &exe_ctx, + lldb_private::ExecutionPolicy execution_policy, + lldb::LanguageType language, + ResultType desired_type, + bool unwind_on_error, + bool ignore_breakpoints, + const char *expr_cstr, + const char *expr_prefix, + lldb::ValueObjectSP &result_valobj_sp, + bool try_all_threads, + uint32_t timeout_usec); + + static ExecutionResults + EvaluateWithError (ExecutionContext &exe_ctx, + lldb_private::ExecutionPolicy execution_policy, + lldb::LanguageType language, + ResultType desired_type, + bool unwind_on_error, + bool ignore_breakpoints, + const char *expr_cstr, + const char *expr_prefix, + lldb::ValueObjectSP &result_valobj_sp, + Error &error, + bool try_all_threads, + uint32_t timeout_usec); + + static const Error::ValueType kNoResult = 0x1001; ///< ValueObject::GetError() returns this if there is no result from the expression. +private: + //------------------------------------------------------------------ + /// Populate m_cplusplus and m_objetivec based on the environment. + //------------------------------------------------------------------ + + void + ScanContext (ExecutionContext &exe_ctx, + lldb_private::Error &err); + + bool + PrepareToExecuteJITExpression (Stream &error_stream, + ExecutionContext &exe_ctx, + lldb::addr_t &struct_address, + lldb::addr_t &object_ptr, + lldb::addr_t &cmd_ptr); + + void + InstallContext (ExecutionContext &exe_ctx); + + bool + LockAndCheckContext (ExecutionContext &exe_ctx, + lldb::TargetSP &target_sp, + lldb::ProcessSP &process_sp, + lldb::StackFrameSP &frame_sp); + + lldb::ProcessWP m_process_wp; ///< The process used as the context for the expression. + Address m_address; ///< The address the process is stopped in. + lldb::addr_t m_stack_frame_bottom; ///< The bottom of the allocated stack frame. + lldb::addr_t m_stack_frame_top; ///< The top of the allocated stack frame. + + std::string m_expr_text; ///< The text of the expression, as typed by the user + std::string m_expr_prefix; ///< The text of the translation-level definitions, as provided by the user + lldb::LanguageType m_language; ///< The language to use when parsing (eLanguageTypeUnknown means use defaults) + bool m_allow_cxx; ///< True if the language allows C++. + bool m_allow_objc; ///< True if the language allows Objective-C. + std::string m_transformed_text; ///< The text of the expression, as send to the parser + ResultType m_desired_type; ///< The type to coerce the expression's result to. If eResultTypeAny, inferred from the expression. + + std::unique_ptr m_expr_decl_map; ///< The map to use when parsing the expression. + std::unique_ptr m_execution_unit_ap; ///< The execution unit the expression is stored in. + std::unique_ptr m_materializer_ap; ///< The materializer to use when running the expression. + std::unique_ptr m_result_synthesizer; ///< The result synthesizer, if one is needed. + + bool m_enforce_valid_object; ///< True if the expression parser should enforce the presence of a valid class pointer in order to generate the expression as a method. + bool m_cplusplus; ///< True if the expression is compiled as a C++ member function (true if it was parsed when exe_ctx was in a C++ method). + bool m_objectivec; ///< True if the expression is compiled as an Objective-C method (true if it was parsed when exe_ctx was in an Objective-C method). + bool m_static_method; ///< True if the expression is compiled as a static (or class) method (currently true if it was parsed when exe_ctx was in an Objective-C class method). + bool m_needs_object_ptr; ///< True if "this" or "self" must be looked up and passed in. False if the expression doesn't really use them and they can be NULL. + bool m_const_object; ///< True if "this" is const. + Target *m_target; ///< The target for storing persistent data like types and variables. + + bool m_can_interpret; ///< True if the expression could be evaluated statically; false otherwise. + lldb::addr_t m_materialized_address; ///< The address at which the arguments to the expression have been materialized. + Materializer::DematerializerSP m_dematerializer_sp; ///< The dematerializer. +}; + +} // namespace lldb_private + +#endif // liblldb_ClangUserExpression_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Expression/ClangUtilityFunction.h b/contrib/llvm/tools/lldb/include/lldb/Expression/ClangUtilityFunction.h new file mode 100644 index 00000000000..6da8e5ec3a8 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Expression/ClangUtilityFunction.h @@ -0,0 +1,179 @@ +//===-- ClangUtilityFunction.h ----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ClangUtilityFunction_h_ +#define liblldb_ClangUtilityFunction_h_ + +// C Includes +// C++ Includes +#include +#include +#include + +// Other libraries and framework includes +// Project includes + +#include "lldb/lldb-forward.h" +#include "lldb/lldb-private.h" +#include "lldb/Core/ClangForward.h" +#include "lldb/Expression/ClangExpression.h" + +namespace lldb_private +{ + +//---------------------------------------------------------------------- +/// @class ClangUtilityFunction ClangUtilityFunction.h "lldb/Expression/ClangUtilityFunction.h" +/// @brief Encapsulates a single expression for use with Clang +/// +/// LLDB uses expressions for various purposes, notably to call functions +/// and as a backend for the expr command. ClangUtilityFunction encapsulates +/// a self-contained function meant to be used from other code. Utility +/// functions can perform error-checking for ClangUserExpressions, +//---------------------------------------------------------------------- +class ClangUtilityFunction : public ClangExpression +{ +public: + //------------------------------------------------------------------ + /// Constructor + /// + /// @param[in] text + /// The text of the function. Must be a full translation unit. + /// + /// @param[in] name + /// The name of the function, as used in the text. + //------------------------------------------------------------------ + ClangUtilityFunction (const char *text, + const char *name); + + virtual + ~ClangUtilityFunction (); + + //------------------------------------------------------------------ + /// Install the utility function into a process + /// + /// @param[in] error_stream + /// A stream to print parse errors and warnings to. + /// + /// @param[in] exe_ctx + /// The execution context to install the utility function to. + /// + /// @return + /// True on success (no errors); false otherwise. + //------------------------------------------------------------------ + bool + Install (Stream &error_stream, ExecutionContext &exe_ctx); + + //------------------------------------------------------------------ + /// Check whether the given PC is inside the function + /// + /// Especially useful if the function dereferences NULL to indicate a failed + /// assert. + /// + /// @param[in] pc + /// The program counter to check. + /// + /// @return + /// True if the program counter falls within the function's bounds; + /// false if not (or the function is not JIT compiled) + //------------------------------------------------------------------ + bool + ContainsAddress (lldb::addr_t address) + { + // nothing is both >= LLDB_INVALID_ADDRESS and < LLDB_INVALID_ADDRESS, + // so this always returns false if the function is not JIT compiled yet + return (address >= m_jit_start_addr && address < m_jit_end_addr); + } + + + //------------------------------------------------------------------ + /// Return the string that the parser should parse. Must be a full + /// translation unit. + //------------------------------------------------------------------ + const char * + Text () + { + return m_function_text.c_str(); + } + + //------------------------------------------------------------------ + /// Return the function name that should be used for executing the + /// expression. Text() should contain the definition of this + /// function. + //------------------------------------------------------------------ + const char * + FunctionName () + { + return m_function_name.c_str(); + } + + //------------------------------------------------------------------ + /// Return the object that the parser should use when resolving external + /// values. May be NULL if everything should be self-contained. + //------------------------------------------------------------------ + ClangExpressionDeclMap * + DeclMap () + { + return m_expr_decl_map.get(); + } + + //------------------------------------------------------------------ + /// Return the object that the parser should use when registering + /// local variables. May be NULL if the Expression doesn't care. + //------------------------------------------------------------------ + ClangExpressionVariableList * + LocalVariables () + { + return NULL; + } + + //------------------------------------------------------------------ + /// Return the object that the parser should allow to access ASTs. + /// May be NULL if the ASTs do not need to be transformed. + /// + /// @param[in] passthrough + /// The ASTConsumer that the returned transformer should send + /// the ASTs to after transformation. + //------------------------------------------------------------------ + clang::ASTConsumer * + ASTTransformer (clang::ASTConsumer *passthrough) + { + return NULL; + } + + //------------------------------------------------------------------ + /// Return true if validation code should be inserted into the + /// expression. + //------------------------------------------------------------------ + bool + NeedsValidation () + { + return false; + } + + //------------------------------------------------------------------ + /// Return true if external variables in the expression should be + /// resolved. + //------------------------------------------------------------------ + bool + NeedsVariableResolution () + { + return false; + } + +private: + std::unique_ptr m_expr_decl_map; ///< The map to use when parsing and materializing the expression. + std::unique_ptr m_execution_unit_ap; + + std::string m_function_text; ///< The text of the function. Must be a well-formed translation unit. + std::string m_function_name; ///< The name of the function. +}; + +} // namespace lldb_private + +#endif // liblldb_ClangUtilityFunction_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Expression/DWARFExpression.h b/contrib/llvm/tools/lldb/include/lldb/Expression/DWARFExpression.h new file mode 100644 index 00000000000..2692831ecc8 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Expression/DWARFExpression.h @@ -0,0 +1,424 @@ +//===-- DWARFExpression.h ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_DWARFExpression_h_ +#define liblldb_DWARFExpression_h_ + +#include "lldb/lldb-private.h" +#include "lldb/Core/ClangForward.h" +#include "lldb/Core/Address.h" +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/Error.h" +#include "lldb/Core/Scalar.h" + +namespace lldb_private { + +class ClangExpressionVariable; +class ClangExpressionVariableList; + +class ClangExpressionDeclMap; + +//---------------------------------------------------------------------- +/// @class DWARFExpression DWARFExpression.h "lldb/Expression/DWARFExpression.h" +/// @brief Encapsulates a DWARF location expression and interprets it. +/// +/// DWARF location expressions are used in two ways by LLDB. The first +/// use is to find entities specified in the debug information, since +/// their locations are specified in precisely this language. The second +/// is to interpret expressions without having to run the target in cases +/// where the overhead from copying JIT-compiled code into the target is +/// too high or where the target cannot be run. This class encapsulates +/// a single DWARF location expression or a location list and interprets +/// it. +//---------------------------------------------------------------------- +class DWARFExpression +{ +public: + //------------------------------------------------------------------ + /// Constructor + //------------------------------------------------------------------ + DWARFExpression(); + + //------------------------------------------------------------------ + /// Constructor + /// + /// @param[in] data + /// A data extractor configured to read the DWARF location expression's + /// bytecode. + /// + /// @param[in] data_offset + /// The offset of the location expression in the extractor. + /// + /// @param[in] data_length + /// The byte length of the location expression. + //------------------------------------------------------------------ + DWARFExpression(const DataExtractor& data, + lldb::offset_t data_offset, + lldb::offset_t data_length); + + //------------------------------------------------------------------ + /// Copy constructor + //------------------------------------------------------------------ + DWARFExpression(const DWARFExpression& rhs); + + //------------------------------------------------------------------ + /// Destructor + //------------------------------------------------------------------ + virtual + ~DWARFExpression(); + + //------------------------------------------------------------------ + /// Print the description of the expression to a stream + /// + /// @param[in] s + /// The stream to print to. + /// + /// @param[in] level + /// The level of verbosity to use. + /// + /// @param[in] location_list_base_addr + /// If this is a location list based expression, this is the + /// address of the object that owns it. NOTE: this value is + /// different from the DWARF version of the location list base + /// address which is compile unit relative. This base address + /// is the address of the object that owns the location list. + /// + /// @param[in] abi + /// An optional ABI plug-in that can be used to resolve register + /// names. + //------------------------------------------------------------------ + void + GetDescription (Stream *s, + lldb::DescriptionLevel level, + lldb::addr_t location_list_base_addr, + ABI *abi) const; + + //------------------------------------------------------------------ + /// Return true if the location expression contains data + //------------------------------------------------------------------ + bool + IsValid() const; + + //------------------------------------------------------------------ + /// Return true if a location list was provided + //------------------------------------------------------------------ + bool + IsLocationList() const; + + //------------------------------------------------------------------ + /// Search for a load address in the location list + /// + /// @param[in] process + /// The process to use when resolving the load address + /// + /// @param[in] addr + /// The address to resolve + /// + /// @return + /// True if IsLocationList() is true and the address was found; + /// false otherwise. + //------------------------------------------------------------------ +// bool +// LocationListContainsLoadAddress (Process* process, const Address &addr) const; +// + bool + LocationListContainsAddress (lldb::addr_t loclist_base_addr, lldb::addr_t addr) const; + + //------------------------------------------------------------------ + /// If a location is not a location list, return true if the location + /// contains a DW_OP_addr () opcode in the stream that matches \a + /// file_addr. If file_addr is LLDB_INVALID_ADDRESS, the this + /// function will return true if the variable there is any DW_OP_addr + /// in a location that (yet still is NOT a location list). This helps + /// us detect if a variable is a global or static variable since + /// there is no other indication from DWARF debug info. + /// + /// @param[in] op_addr_idx + /// The DW_OP_addr index to retrieve in case there is more than + /// one DW_OP_addr opcode in the location byte stream. + /// + /// @param[out] error + /// If the location stream contains unknown DW_OP opcodes or the + /// data is missing, \a error will be set to \b true. + /// + /// @return + /// LLDB_INVALID_ADDRESS if the location doesn't contain a + /// DW_OP_addr for \a op_addr_idx, otherwise a valid file address + //------------------------------------------------------------------ + lldb::addr_t + GetLocation_DW_OP_addr (uint32_t op_addr_idx, bool &error) const; + + bool + Update_DW_OP_addr (lldb::addr_t file_addr); + + //------------------------------------------------------------------ + /// Make the expression parser read its location information from a + /// given data source. Does not change the offset and length + /// + /// @param[in] data + /// A data extractor configured to read the DWARF location expression's + /// bytecode. + //------------------------------------------------------------------ + void + SetOpcodeData(const DataExtractor& data); + + //------------------------------------------------------------------ + /// Make the expression parser read its location information from a + /// given data source + /// + /// @param[in] data + /// A data extractor configured to read the DWARF location expression's + /// bytecode. + /// + /// @param[in] data_offset + /// The offset of the location expression in the extractor. + /// + /// @param[in] data_length + /// The byte length of the location expression. + //------------------------------------------------------------------ + void + SetOpcodeData(const DataExtractor& data, lldb::offset_t data_offset, lldb::offset_t data_length); + + //------------------------------------------------------------------ + /// Copy the DWARF location expression into a local buffer. + /// + /// It is a good idea to copy the data so we don't keep the entire + /// object file worth of data around just for a few bytes of location + /// expression. LLDB typically will mmap the entire contents of debug + /// information files, and if we use SetOpcodeData, it will get a + /// shared reference to all of this data for the and cause the object + /// file to have to stay around. Even worse, a very very large ".a" + /// that contains one or more .o files could end up being referenced. + /// Location lists are typically small so even though we are copying + /// the data, it shouldn't amount to that much for the variables we + /// end up parsing. + /// + /// @param[in] data + /// A data extractor configured to read and copy the DWARF + /// location expression's bytecode. + /// + /// @param[in] data_offset + /// The offset of the location expression in the extractor. + /// + /// @param[in] data_length + /// The byte length of the location expression. + //------------------------------------------------------------------ + void + CopyOpcodeData (const DataExtractor& data, + lldb::offset_t data_offset, + lldb::offset_t data_length); + + + //------------------------------------------------------------------ + /// Tells the expression that it refers to a location list. + /// + /// @param[in] slide + /// This value should be a slide that is applied to any values + /// in the location list data so the values become zero based + /// offsets into the object that owns the location list. We need + /// to make location lists relative to the objects that own them + /// so we can relink addresses on the fly. + //------------------------------------------------------------------ + void + SetLocationListSlide (lldb::addr_t slide); + + //------------------------------------------------------------------ + /// Return the call-frame-info style register kind + //------------------------------------------------------------------ + int + GetRegisterKind (); + + //------------------------------------------------------------------ + /// Set the call-frame-info style register kind + /// + /// @param[in] reg_kind + /// The register kind. + //------------------------------------------------------------------ + void + SetRegisterKind (lldb::RegisterKind reg_kind); + + //------------------------------------------------------------------ + /// Wrapper for the static evaluate function that accepts an + /// ExecutionContextScope instead of an ExecutionContext and uses + /// member variables to populate many operands + //------------------------------------------------------------------ + bool + Evaluate (ExecutionContextScope *exe_scope, + ClangExpressionVariableList *expr_locals, + ClangExpressionDeclMap *decl_map, + lldb::addr_t loclist_base_load_addr, + const Value* initial_value_ptr, + Value& result, + Error *error_ptr) const; + + //------------------------------------------------------------------ + /// Wrapper for the static evaluate function that uses member + /// variables to populate many operands + //------------------------------------------------------------------ + bool + Evaluate (ExecutionContext *exe_ctx, + ClangExpressionVariableList *expr_locals, + ClangExpressionDeclMap *decl_map, + RegisterContext *reg_ctx, + lldb::addr_t loclist_base_load_addr, + const Value* initial_value_ptr, + Value& result, + Error *error_ptr) const; + + //------------------------------------------------------------------ + /// Evaluate a DWARF location expression in a particular context + /// + /// @param[in] exe_ctx + /// The execution context in which to evaluate the location + /// expression. The location expression may access the target's + /// memory, especially if it comes from the expression parser. + /// + /// @param[in] opcodes + /// This is a static method so the opcodes need to be provided + /// explicitly. + /// + /// @param[in] expr_locals + /// If the location expression was produced by the expression parser, + /// the list of local variables referenced by the DWARF expression. + /// This list should already have been populated during parsing; + /// the DWARF expression refers to variables by index. Can be NULL if + /// the location expression uses no locals. + /// + /// @param[in] decl_map + /// If the location expression was produced by the expression parser, + /// the list of external variables referenced by the location + /// expression. Can be NULL if the location expression uses no + /// external variables. + /// + /// @param[in] reg_ctx + /// An optional parameter which provides a RegisterContext for use + /// when evaluating the expression (i.e. for fetching register values). + /// Normally this will come from the ExecutionContext's StackFrame but + /// in the case where an expression needs to be evaluated while building + /// the stack frame list, this short-cut is available. + /// + /// @param[in] offset + /// The offset of the location expression in the data extractor. + /// + /// @param[in] length + /// The length in bytes of the location expression. + /// + /// @param[in] reg_set + /// The call-frame-info style register kind. + /// + /// @param[in] initial_value_ptr + /// A value to put on top of the interpreter stack before evaluating + /// the expression, if the expression is parametrized. Can be NULL. + /// + /// @param[in] result + /// A value into which the result of evaluating the expression is + /// to be placed. + /// + /// @param[in] error_ptr + /// If non-NULL, used to report errors in expression evaluation. + /// + /// @return + /// True on success; false otherwise. If error_ptr is non-NULL, + /// details of the failure are provided through it. + //------------------------------------------------------------------ + static bool + Evaluate (ExecutionContext *exe_ctx, + ClangExpressionVariableList *expr_locals, + ClangExpressionDeclMap *decl_map, + RegisterContext *reg_ctx, + const DataExtractor& opcodes, + const lldb::offset_t offset, + const lldb::offset_t length, + const uint32_t reg_set, + const Value* initial_value_ptr, + Value& result, + Error *error_ptr); + + //------------------------------------------------------------------ + /// Loads a ClangExpressionVariableList into the object + /// + /// @param[in] locals + /// If non-NULL, the list of locals used by this expression. + /// See Evaluate(). + //------------------------------------------------------------------ + void + SetExpressionLocalVariableList (ClangExpressionVariableList *locals); + + //------------------------------------------------------------------ + /// Loads a ClangExpressionDeclMap into the object + /// + /// @param[in] locals + /// If non-NULL, the list of external variables used by this + /// expression. See Evaluate(). + //------------------------------------------------------------------ + void + SetExpressionDeclMap (ClangExpressionDeclMap *decl_map); + + bool + GetExpressionData (DataExtractor &data) const + { + data = m_data; + return data.GetByteSize() > 0; + } + + bool + DumpLocationForAddress (Stream *s, + lldb::DescriptionLevel level, + lldb::addr_t loclist_base_load_addr, + lldb::addr_t address, + ABI *abi); + +protected: + //------------------------------------------------------------------ + /// Pretty-prints the location expression to a stream + /// + /// @param[in] stream + /// The stream to use for pretty-printing. + /// + /// @param[in] offset + /// The offset into the data buffer of the opcodes to be printed. + /// + /// @param[in] length + /// The length in bytes of the opcodes to be printed. + /// + /// @param[in] level + /// The level of detail to use in pretty-printing. + /// + /// @param[in] abi + /// An optional ABI plug-in that can be used to resolve register + /// names. + //------------------------------------------------------------------ + void + DumpLocation(Stream *s, + lldb::offset_t offset, + lldb::offset_t length, + lldb::DescriptionLevel level, + ABI *abi) const; + + bool + GetLocation (lldb::addr_t base_addr, + lldb::addr_t pc, + lldb::offset_t &offset, + lldb::offset_t &len); + + //------------------------------------------------------------------ + /// Classes that inherit from DWARFExpression can see and modify these + //------------------------------------------------------------------ + + DataExtractor m_data; ///< A data extractor capable of reading opcode bytes + lldb::RegisterKind m_reg_kind; ///< One of the defines that starts with LLDB_REGKIND_ + lldb::addr_t m_loclist_slide; ///< A value used to slide the location list offsets so that + ///< they are relative to the object that owns the location list + ///< (the function for frame base and variable location lists) + +}; + +} // namespace lldb_private + +#endif // liblldb_DWARFExpression_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Expression/ExpressionSourceCode.h b/contrib/llvm/tools/lldb/include/lldb/Expression/ExpressionSourceCode.h new file mode 100644 index 00000000000..be1014ae304 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Expression/ExpressionSourceCode.h @@ -0,0 +1,78 @@ +//===-- ExpressionSourceCode.h ----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ExpressionSourceCode_h +#define liblldb_ExpressionSourceCode_h + +#include "lldb/lldb-enumerations.h" + +#include + +namespace lldb_private +{ + +class ExpressionSourceCode +{ +public: + static const char * g_expression_prefix; + + static ExpressionSourceCode *CreateWrapped (const char *prefix, + const char *body) + { + return new ExpressionSourceCode ("$__lldb_expr", + prefix, + body, + true); + } + + static ExpressionSourceCode *CreateUnwrapped (const char *name, + const char *body) + { + return new ExpressionSourceCode (name, + "", + body, + false); + } + + bool NeedsWrapping () const + { + return m_wrap; + } + + const char *GetName () const + { + return m_name.c_str(); + } + + bool GetText (std::string &text, + lldb::LanguageType wrapping_language, + bool const_object, + bool static_method) const; + +private: + ExpressionSourceCode (const char *name, + const char *prefix, + const char *body, + bool wrap) : + m_name(name), + m_prefix(prefix), + m_body(body), + m_wrap(wrap) + { + } + + std::string m_name; + std::string m_prefix; + std::string m_body; + bool m_wrap; +}; + +} // namespace lldb_private + +#endif diff --git a/contrib/llvm/tools/lldb/include/lldb/Expression/IRDynamicChecks.h b/contrib/llvm/tools/lldb/include/lldb/Expression/IRDynamicChecks.h new file mode 100644 index 00000000000..226f5c94e98 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Expression/IRDynamicChecks.h @@ -0,0 +1,169 @@ +//===-- IRDynamicChecks.h ---------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_IRDynamicChecks_h_ +#define liblldb_IRDynamicChecks_h_ + +#include "lldb/lldb-types.h" +#include "llvm/Pass.h" + +namespace llvm { + class BasicBlock; + class CallInst; + class Constant; + class Function; + class Instruction; + class Module; + class DataLayout; + class Value; +} + +namespace lldb_private +{ + +class ClangExpressionDeclMap; +class ClangUtilityFunction; +class ExecutionContext; +class Stream; + +//---------------------------------------------------------------------- +/// @class DynamicCheckerFunctions IRDynamicChecks.h "lldb/Expression/IRDynamicChecks.h" +/// @brief Encapsulates dynamic check functions used by expressions. +/// +/// Each of the utility functions encapsulated in this class is responsible +/// for validating some data that an expression is about to use. Examples are: +/// +/// a = *b; // check that b is a valid pointer +/// [b init]; // check that b is a valid object to send "init" to +/// +/// The class installs each checker function into the target process and +/// makes it available to IRDynamicChecks to use. +//---------------------------------------------------------------------- +class DynamicCheckerFunctions +{ +public: + //------------------------------------------------------------------ + /// Constructor + //------------------------------------------------------------------ + DynamicCheckerFunctions (); + + //------------------------------------------------------------------ + /// Destructor + //------------------------------------------------------------------ + ~DynamicCheckerFunctions (); + + //------------------------------------------------------------------ + /// Install the utility functions into a process. This binds the + /// instance of DynamicCheckerFunctions to that process. + /// + /// @param[in] error_stream + /// A stream to print errors on. + /// + /// @param[in] exe_ctx + /// The execution context to install the functions into. + /// + /// @return + /// True on success; false on failure, or if the functions have + /// already been installed. + //------------------------------------------------------------------ + bool Install (Stream &error_stream, + ExecutionContext &exe_ctx); + + bool DoCheckersExplainStop (lldb::addr_t addr, Stream &message); + + std::unique_ptr m_valid_pointer_check; + std::unique_ptr m_objc_object_check; +}; + +//---------------------------------------------------------------------- +/// @class IRDynamicChecks IRDynamicChecks.h "lldb/Expression/IRDynamicChecks.h" +/// @brief Adds dynamic checks to a user-entered expression to reduce its likelihood of crashing +/// +/// When an IR function is executed in the target process, it may cause +/// crashes or hangs by dereferencing NULL pointers, trying to call Objective-C +/// methods on objects that do not respond to them, and so forth. +/// +/// IRDynamicChecks adds calls to the functions in DynamicCheckerFunctions +/// to appropriate locations in an expression's IR. +//---------------------------------------------------------------------- +class IRDynamicChecks : public llvm::ModulePass +{ +public: + //------------------------------------------------------------------ + /// Constructor + /// + /// @param[in] checker_functions + /// The checker functions for the target process. + /// + /// @param[in] func_name + /// The name of the function to prepare for execution in the target. + /// + /// @param[in] decl_map + /// The mapping used to look up entities in the target process. In + /// this case, used to find objc_msgSend + //------------------------------------------------------------------ + IRDynamicChecks (DynamicCheckerFunctions &checker_functions, + const char* func_name = "$__lldb_expr"); + + //------------------------------------------------------------------ + /// Destructor + //------------------------------------------------------------------ + virtual ~IRDynamicChecks(); + + //------------------------------------------------------------------ + /// Run this IR transformer on a single module + /// + /// @param[in] M + /// The module to run on. This module is searched for the function + /// $__lldb_expr, and that function is passed to the passes one by + /// one. + /// + /// @return + /// True on success; false otherwise + //------------------------------------------------------------------ + bool runOnModule(llvm::Module &M); + + //------------------------------------------------------------------ + /// Interface stub + //------------------------------------------------------------------ + void assignPassManager(llvm::PMStack &PMS, + llvm::PassManagerType T = llvm::PMT_ModulePassManager); + + //------------------------------------------------------------------ + /// Returns PMT_ModulePassManager + //------------------------------------------------------------------ + llvm::PassManagerType getPotentialPassManagerType() const; +private: + //------------------------------------------------------------------ + /// A basic block-level pass to find all pointer dereferences and + /// validate them before use. + //------------------------------------------------------------------ + + //------------------------------------------------------------------ + /// The top-level pass implementation + /// + /// @param[in] M + /// The module currently being processed. + /// + /// @param[in] BB + /// The basic block currently being processed. + /// + /// @return + /// True on success; false otherwise + //------------------------------------------------------------------ + bool FindDataLoads(llvm::Module &M, + llvm::BasicBlock &BB); + + std::string m_func_name; ///< The name of the function to add checks to + DynamicCheckerFunctions &m_checker_functions; ///< The checker functions for the process +}; + +} + +#endif diff --git a/contrib/llvm/tools/lldb/include/lldb/Expression/IRExecutionUnit.h b/contrib/llvm/tools/lldb/include/lldb/Expression/IRExecutionUnit.h new file mode 100644 index 00000000000..9bc55920474 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Expression/IRExecutionUnit.h @@ -0,0 +1,521 @@ +//===-- IRExecutionUnit.h ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef lldb_IRExecutionUnit_h_ +#define lldb_IRExecutionUnit_h_ + +// C Includes +// C++ Includes +#include +#include +#include +#include + +// Other libraries and framework includes +#include "llvm/IR/Module.h" + +// Project includes +#include "lldb/lldb-forward.h" +#include "lldb/lldb-private.h" +#include "lldb/Core/ClangForward.h" +#include "lldb/Core/DataBufferHeap.h" +#include "llvm/ExecutionEngine/JITMemoryManager.h" +#include "lldb/Expression/ClangExpression.h" +#include "lldb/Expression/ClangExpressionParser.h" +#include "lldb/Expression/IRMemoryMap.h" +#include "lldb/Host/Mutex.h" + +namespace llvm { + +class Module; +class ExecutionEngine; + +} + +namespace lldb_private { + +class Error; + +//---------------------------------------------------------------------- +/// @class IRExecutionUnit IRExecutionUnit.h "lldb/Expression/IRExecutionUnit.h" +/// @brief Contains the IR and, optionally, JIT-compiled code for a module. +/// +/// This class encapsulates the compiled version of an expression, in IR +/// form (for interpretation purposes) and in raw machine code form (for +/// execution in the target). +/// +/// This object wraps an IR module that comes from the expression parser, +/// and knows how to use the JIT to make it into executable code. It can +/// then be used as input to the IR interpreter, or the address of the +/// executable code can be passed to a thread plan to run in the target. +/// +/// This class creates a subclass of LLVM's JITMemoryManager, because that is +/// how the JIT emits code. Because LLDB needs to move JIT-compiled code +/// into the target process, the IRExecutionUnit knows how to copy the +/// emitted code into the target process. +//---------------------------------------------------------------------- +class IRExecutionUnit : public IRMemoryMap +{ +public: + //------------------------------------------------------------------ + /// Constructor + //------------------------------------------------------------------ + IRExecutionUnit (std::unique_ptr &context_ap, + std::unique_ptr &module_ap, + ConstString &name, + const lldb::TargetSP &target_sp, + std::vector &cpu_features); + + //------------------------------------------------------------------ + /// Destructor + //------------------------------------------------------------------ + ~IRExecutionUnit(); + + llvm::Module *GetModule() + { + return m_module; + } + + llvm::Function *GetFunction() + { + if (m_module) + return m_module->getFunction (m_name.AsCString()); + else + return NULL; + } + + void GetRunnableInfo(Error &error, + lldb::addr_t &func_addr, + lldb::addr_t &func_end); + + //------------------------------------------------------------------ + /// Accessors for IRForTarget and other clients that may want binary + /// data placed on their behalf. The binary data is owned by the + /// IRExecutionUnit unless the client explicitly chooses to free it. + //------------------------------------------------------------------ + + lldb::addr_t WriteNow(const uint8_t *bytes, + size_t size, + Error &error); + + void FreeNow(lldb::addr_t allocation); + +private: + //------------------------------------------------------------------ + /// Look up the object in m_address_map that contains a given address, + /// find where it was copied to, and return the remote address at the + /// same offset into the copied entity + /// + /// @param[in] local_address + /// The address in the debugger. + /// + /// @return + /// The address in the target process. + //------------------------------------------------------------------ + lldb::addr_t + GetRemoteAddressForLocal (lldb::addr_t local_address); + + //------------------------------------------------------------------ + /// Look up the object in m_address_map that contains a given address, + /// find where it was copied to, and return its address range in the + /// target process + /// + /// @param[in] local_address + /// The address in the debugger. + /// + /// @return + /// The range of the containing object in the target process. + //------------------------------------------------------------------ + typedef std::pair AddrRange; + AddrRange + GetRemoteRangeForLocal (lldb::addr_t local_address); + + //------------------------------------------------------------------ + /// Commit all allocations to the process and record where they were stored. + /// + /// @param[in] process + /// The process to allocate memory in. + /// + /// @return + /// True <=> all allocations were performed successfully. + /// This method will attempt to free allocated memory if the + /// operation fails. + //------------------------------------------------------------------ + bool + CommitAllocations (lldb::ProcessSP &process_sp); + + //------------------------------------------------------------------ + /// Report all committed allocations to the execution engine. + /// + /// @param[in] engine + /// The execution engine to notify. + //------------------------------------------------------------------ + void + ReportAllocations (llvm::ExecutionEngine &engine); + + //------------------------------------------------------------------ + /// Write the contents of all allocations to the process. + /// + /// @param[in] local_address + /// The process containing the allocations. + /// + /// @return + /// True <=> all allocations were performed successfully. + //------------------------------------------------------------------ + bool + WriteData (lldb::ProcessSP &process_sp); + + Error + DisassembleFunction (Stream &stream, + lldb::ProcessSP &process_sp); + + class MemoryManager : public llvm::JITMemoryManager + { + public: + MemoryManager (IRExecutionUnit &parent); + + //------------------------------------------------------------------ + /// Passthrough interface stub + //------------------------------------------------------------------ + virtual void setMemoryWritable (); + + //------------------------------------------------------------------ + /// Passthrough interface stub + //------------------------------------------------------------------ + virtual void setMemoryExecutable (); + + //------------------------------------------------------------------ + /// Passthrough interface stub + //------------------------------------------------------------------ + virtual void setPoisonMemory (bool poison) + { + m_default_mm_ap->setPoisonMemory (poison); + } + + //------------------------------------------------------------------ + /// Passthrough interface stub + //------------------------------------------------------------------ + virtual void AllocateGOT() + { + m_default_mm_ap->AllocateGOT(); + } + + //------------------------------------------------------------------ + /// Passthrough interface stub + //------------------------------------------------------------------ + virtual uint8_t *getGOTBase() const + { + return m_default_mm_ap->getGOTBase(); + } + + //------------------------------------------------------------------ + /// Passthrough interface stub + //------------------------------------------------------------------ + virtual uint8_t *startFunctionBody(const llvm::Function *F, + uintptr_t &ActualSize); + + //------------------------------------------------------------------ + /// Allocate room for a dyld stub for a lazy-referenced function, + /// and add it to the m_stubs map + /// + /// @param[in] F + /// The function being referenced. + /// + /// @param[in] StubSize + /// The size of the stub. + /// + /// @param[in] Alignment + /// The required alignment of the stub. + /// + /// @return + /// Allocated space for the stub. + //------------------------------------------------------------------ + virtual uint8_t *allocateStub(const llvm::GlobalValue* F, + unsigned StubSize, + unsigned Alignment); + + //------------------------------------------------------------------ + /// Complete the body of a function, and add it to the m_functions map + /// + /// @param[in] F + /// The function being completed. + /// + /// @param[in] FunctionStart + /// The first instruction of the function. + /// + /// @param[in] FunctionEnd + /// The last byte of the last instruction of the function. + //------------------------------------------------------------------ + virtual void endFunctionBody(const llvm::Function *F, + uint8_t *FunctionStart, + uint8_t *FunctionEnd); + //------------------------------------------------------------------ + /// Allocate space for an unspecified purpose, and add it to the + /// m_spaceBlocks map + /// + /// @param[in] Size + /// The size of the area. + /// + /// @param[in] Alignment + /// The required alignment of the area. + /// + /// @return + /// Allocated space. + //------------------------------------------------------------------ + virtual uint8_t *allocateSpace(intptr_t Size, unsigned Alignment); + + //------------------------------------------------------------------ + /// Allocate space for executable code, and add it to the + /// m_spaceBlocks map + /// + /// @param[in] Size + /// The size of the area. + /// + /// @param[in] Alignment + /// The required alignment of the area. + /// + /// @param[in] SectionID + /// A unique identifier for the section. + /// + /// @return + /// Allocated space. + //------------------------------------------------------------------ + virtual uint8_t *allocateCodeSection(uintptr_t Size, unsigned Alignment, + unsigned SectionID); + + //------------------------------------------------------------------ + /// Allocate space for data, and add it to the m_spaceBlocks map + /// + /// @param[in] Size + /// The size of the area. + /// + /// @param[in] Alignment + /// The required alignment of the area. + /// + /// @param[in] SectionID + /// A unique identifier for the section. + /// + /// @param[in] IsReadOnly + /// Flag indicating the section is read-only. + /// + /// @return + /// Allocated space. + //------------------------------------------------------------------ + virtual uint8_t *allocateDataSection(uintptr_t Size, unsigned Alignment, + unsigned SectionID, bool IsReadOnly); + + //------------------------------------------------------------------ + /// Allocate space for a global variable, and add it to the + /// m_spaceBlocks map + /// + /// @param[in] Size + /// The size of the variable. + /// + /// @param[in] Alignment + /// The required alignment of the variable. + /// + /// @return + /// Allocated space for the global. + //------------------------------------------------------------------ + virtual uint8_t *allocateGlobal(uintptr_t Size, + unsigned Alignment); + + //------------------------------------------------------------------ + /// Called when object loading is complete and section page + /// permissions can be applied. Currently unimplemented for LLDB. + /// + /// @param[out] ErrMsg + /// The error that prevented the page protection from succeeding. + /// + /// @return + /// True in case of failure, false in case of success. + //------------------------------------------------------------------ + bool applyPermissions(std::string *ErrMsg) { return false; } + + //------------------------------------------------------------------ + /// Passthrough interface stub + //------------------------------------------------------------------ + virtual void deallocateFunctionBody(void *Body); + + //------------------------------------------------------------------ + /// Passthrough interface stub + //------------------------------------------------------------------ + virtual uint8_t* startExceptionTable(const llvm::Function* F, + uintptr_t &ActualSize); + + //------------------------------------------------------------------ + /// Complete the exception table for a function, and add it to the + /// m_exception_tables map + /// + /// @param[in] F + /// The function whose exception table is being written. + /// + /// @param[in] TableStart + /// The first byte of the exception table. + /// + /// @param[in] TableEnd + /// The last byte of the exception table. + /// + /// @param[in] FrameRegister + /// I don't know what this does, but it's passed through. + //------------------------------------------------------------------ + virtual void endExceptionTable(const llvm::Function *F, + uint8_t *TableStart, + uint8_t *TableEnd, + uint8_t* FrameRegister); + + //------------------------------------------------------------------ + /// Passthrough interface stub + //------------------------------------------------------------------ + virtual void deallocateExceptionTable(void *ET); + + //------------------------------------------------------------------ + /// Passthrough interface stub + //------------------------------------------------------------------ + virtual size_t GetDefaultCodeSlabSize() { + return m_default_mm_ap->GetDefaultCodeSlabSize(); + } + + //------------------------------------------------------------------ + /// Passthrough interface stub + //------------------------------------------------------------------ + virtual size_t GetDefaultDataSlabSize() { + return m_default_mm_ap->GetDefaultDataSlabSize(); + } + + virtual size_t GetDefaultStubSlabSize() { + return m_default_mm_ap->GetDefaultStubSlabSize(); + } + + //------------------------------------------------------------------ + /// Passthrough interface stub + //------------------------------------------------------------------ + virtual unsigned GetNumCodeSlabs() { + return m_default_mm_ap->GetNumCodeSlabs(); + } + + //------------------------------------------------------------------ + /// Passthrough interface stub + //------------------------------------------------------------------ + virtual unsigned GetNumDataSlabs() { + return m_default_mm_ap->GetNumDataSlabs(); + } + + //------------------------------------------------------------------ + /// Passthrough interface stub + //------------------------------------------------------------------ + virtual unsigned GetNumStubSlabs() { + return m_default_mm_ap->GetNumStubSlabs(); + } + + //------------------------------------------------------------------ + /// Passthrough interface stub + //------------------------------------------------------------------ + virtual void *getPointerToNamedFunction(const std::string &Name, + bool AbortOnFailure = true) { + return m_default_mm_ap->getPointerToNamedFunction(Name, AbortOnFailure); + } + private: + std::unique_ptr m_default_mm_ap; ///< The memory allocator to use in actually creating space. All calls are passed through to it. + IRExecutionUnit &m_parent; ///< The execution unit this is a proxy for. + }; + + //---------------------------------------------------------------------- + /// @class JittedFunction IRExecutionUnit.h "lldb/Expression/IRExecutionUnit.h" + /// @brief Encapsulates a single function that has been generated by the JIT. + /// + /// Functions that have been generated by the JIT are first resident in the + /// local process, and then placed in the target process. JittedFunction + /// represents a function possibly resident in both. + //---------------------------------------------------------------------- + struct JittedFunction { + std::string m_name; ///< The function's name + lldb::addr_t m_local_addr; ///< The address of the function in LLDB's memory + lldb::addr_t m_remote_addr; ///< The address of the function in the target's memory + + //------------------------------------------------------------------ + /// Constructor + /// + /// Initializes class variabes. + /// + /// @param[in] name + /// The name of the function. + /// + /// @param[in] local_addr + /// The address of the function in LLDB, or LLDB_INVALID_ADDRESS if + /// it is not present in LLDB's memory. + /// + /// @param[in] remote_addr + /// The address of the function in the target, or LLDB_INVALID_ADDRESS + /// if it is not present in the target's memory. + //------------------------------------------------------------------ + JittedFunction (const char *name, + lldb::addr_t local_addr = LLDB_INVALID_ADDRESS, + lldb::addr_t remote_addr = LLDB_INVALID_ADDRESS) : + m_name (name), + m_local_addr (local_addr), + m_remote_addr (remote_addr) + { + } + }; + + static const unsigned eSectionIDInvalid = (unsigned)-1; + + //---------------------------------------------------------------------- + /// @class AllocationRecord IRExecutionUnit.h "lldb/Expression/IRExecutionUnit.h" + /// @brief Enacpsulates a single allocation request made by the JIT. + /// + /// Allocations made by the JIT are first queued up and then applied in + /// bulk to the underlying process. + //---------------------------------------------------------------------- + struct AllocationRecord { + lldb::addr_t m_process_address; + uintptr_t m_host_address; + uint32_t m_permissions; + size_t m_size; + unsigned m_alignment; + unsigned m_section_id; + + AllocationRecord (uintptr_t host_address, + uint32_t permissions, + size_t size, + unsigned alignment, + unsigned section_id = eSectionIDInvalid) : + m_process_address(LLDB_INVALID_ADDRESS), + m_host_address(host_address), + m_permissions(permissions), + m_size(size), + m_alignment(alignment), + m_section_id(section_id) + { + } + + void dump (Log *log); + }; + + typedef std::vector RecordVector; + RecordVector m_records; + + std::unique_ptr m_context_ap; + std::unique_ptr m_execution_engine_ap; + std::unique_ptr m_module_ap; ///< Holder for the module until it's been handed off + llvm::Module *m_module; ///< Owned by the execution engine + std::vector m_cpu_features; + llvm::SmallVector m_jitted_functions; ///< A vector of all functions that have been JITted into machine code + const ConstString m_name; + + std::atomic m_did_jit; + + lldb::addr_t m_function_load_addr; + lldb::addr_t m_function_end_load_addr; +}; + +} // namespace lldb_private + +#endif // lldb_IRExecutionUnit_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Expression/IRForTarget.h b/contrib/llvm/tools/lldb/include/lldb/Expression/IRForTarget.h new file mode 100644 index 00000000000..151bf2ab477 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Expression/IRForTarget.h @@ -0,0 +1,733 @@ +//===-- IRForTarget.h ---------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_IRForTarget_h_ +#define liblldb_IRForTarget_h_ + +#include "lldb/lldb-public.h" +#include "lldb/Core/ConstString.h" +#include "lldb/Core/Error.h" +#include "lldb/Core/Stream.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Symbol/TaggedASTType.h" +#include "llvm/Pass.h" + +#include + +namespace llvm { + class BasicBlock; + class CallInst; + class Constant; + class ConstantInt; + class Function; + class GlobalValue; + class GlobalVariable; + class Instruction; + class Module; + class StoreInst; + class DataLayout; + class Type; + class Value; +} + +namespace lldb_private { + class ClangExpressionDeclMap; + class IRExecutionUnit; + class IRMemoryMap; +} + +//---------------------------------------------------------------------- +/// @class IRForTarget IRForTarget.h "lldb/Expression/IRForTarget.h" +/// @brief Transforms the IR for a function to run in the target +/// +/// Once an expression has been parsed and converted to IR, it can run +/// in two contexts: interpreted by LLDB as a DWARF location expression, +/// or compiled by the JIT and inserted into the target process for +/// execution. +/// +/// IRForTarget makes the second possible, by applying a series of +/// transformations to the IR which make it relocatable. These +/// transformations are discussed in more detail next to their relevant +/// functions. +//---------------------------------------------------------------------- +class IRForTarget : public llvm::ModulePass +{ +public: + //------------------------------------------------------------------ + /// Constructor + /// + /// @param[in] decl_map + /// The list of externally-referenced variables for the expression, + /// for use in looking up globals and allocating the argument + /// struct. See the documentation for ClangExpressionDeclMap. + /// + /// @param[in] resolve_vars + /// True if the external variable references (including persistent + /// variables) should be resolved. If not, only external functions + /// are resolved. + /// + /// @param[in] execution_policy + /// Determines whether an IR interpreter can be used to statically + /// evaluate the expression. + /// + /// @param[in] const_result + /// This variable is populated with the statically-computed result + /// of the function, if it has no side-effects and the result can + /// be computed statically. + /// + /// @param[in] execution_unit + /// The holder for raw data associated with the expression. + /// + /// @param[in] error_stream + /// If non-NULL, a stream on which errors can be printed. + /// + /// @param[in] func_name + /// The name of the function to prepare for execution in the target. + //------------------------------------------------------------------ + IRForTarget(lldb_private::ClangExpressionDeclMap *decl_map, + bool resolve_vars, + lldb_private::IRExecutionUnit &execution_unit, + lldb_private::Stream *error_stream, + const char* func_name = "$__lldb_expr"); + + //------------------------------------------------------------------ + /// Destructor + //------------------------------------------------------------------ + virtual ~IRForTarget(); + + //------------------------------------------------------------------ + /// Run this IR transformer on a single module + /// + /// Implementation of the llvm::ModulePass::runOnModule() function. + /// + /// @param[in] llvm_module + /// The module to run on. This module is searched for the function + /// $__lldb_expr, and that function is passed to the passes one by + /// one. + /// + /// @param[in] interpreter_error + /// An error. If the expression fails to be interpreted, this error + /// is set to a reason why. + /// + /// @return + /// True on success; false otherwise + //------------------------------------------------------------------ + virtual bool + runOnModule (llvm::Module &llvm_module); + + //------------------------------------------------------------------ + /// Interface stub + /// + /// Implementation of the llvm::ModulePass::assignPassManager() + /// function. + //------------------------------------------------------------------ + virtual void + assignPassManager (llvm::PMStack &pass_mgr_stack, + llvm::PassManagerType pass_mgr_type = llvm::PMT_ModulePassManager); + + //------------------------------------------------------------------ + /// Returns PMT_ModulePassManager + /// + /// Implementation of the llvm::ModulePass::getPotentialPassManagerType() + /// function. + //------------------------------------------------------------------ + virtual llvm::PassManagerType + getPotentialPassManagerType() const; + +private: + //------------------------------------------------------------------ + /// Ensures that the current function's linkage is set to external. + /// Otherwise the JIT may not return an address for it. + /// + /// @param[in] llvm_function + /// The function whose linkage is to be fixed. + /// + /// @return + /// True on success; false otherwise. + //------------------------------------------------------------------ + bool + FixFunctionLinkage (llvm::Function &llvm_function); + + //------------------------------------------------------------------ + /// A module-level pass to replace all function pointers with their + /// integer equivalents. + //------------------------------------------------------------------ + + //------------------------------------------------------------------ + /// The top-level pass implementation + /// + /// @param[in] llvm_module + /// The module currently being processed. + /// + /// @param[in] llvm_function + /// The function currently being processed. + /// + /// @return + /// True on success; false otherwise. + //------------------------------------------------------------------ + bool + HasSideEffects (llvm::Function &llvm_function); + + //------------------------------------------------------------------ + /// A function-level pass to check whether the function has side + /// effects. + //------------------------------------------------------------------ + + //------------------------------------------------------------------ + /// Get the address of a fuction, and a location to put the complete + /// Value of the function if one is available. + /// + /// @param[in] function + /// The function to find the location of. + /// + /// @param[out] ptr + /// The location of the function in the target. + /// + /// @param[out] name + /// The resolved name of the function (matters for intrinsics). + /// + /// @param[out] value_ptr + /// A variable to put the function's completed Value* in, or NULL + /// if the Value* shouldn't be stored anywhere. + /// + /// @return + /// The pointer. + //------------------------------------------------------------------ + bool + GetFunctionAddress (llvm::Function *function, + uint64_t &ptr, + lldb_private::ConstString &name, + llvm::Constant **&value_ptr); + + //------------------------------------------------------------------ + /// Build a function pointer given a type and a raw pointer. + /// + /// @param[in] type + /// The type of the function pointer to be built. + /// + /// @param[in] ptr + /// The value of the pointer. + /// + /// @return + /// The pointer. + //------------------------------------------------------------------ + llvm::Constant * + BuildFunctionPointer (llvm::Type *type, + uint64_t ptr); + + void + RegisterFunctionMetadata (llvm::LLVMContext &context, + llvm::Value *function_ptr, + const char *name); + + //------------------------------------------------------------------ + /// The top-level pass implementation + /// + /// @param[in] llvm_function + /// The function currently being processed. + /// + /// @return + /// True if the function has side effects (or if this cannot + /// be determined); false otherwise. + //------------------------------------------------------------------ + bool + ResolveFunctionPointers (llvm::Module &llvm_module); + + //------------------------------------------------------------------ + /// A function-level pass to take the generated global value + /// $__lldb_expr_result and make it into a persistent variable. + /// Also see ASTResultSynthesizer. + //------------------------------------------------------------------ + + //------------------------------------------------------------------ + /// Find the NamedDecl corresponding to a Value. This interface is + /// exposed for the IR interpreter. + /// + /// @param[in] module + /// The module containing metadata to search + /// + /// @param[in] global + /// The global entity to search for + /// + /// @return + /// The corresponding variable declaration + //------------------------------------------------------------------ +public: + static clang::NamedDecl * + DeclForGlobal (const llvm::GlobalValue *global_val, llvm::Module *module); +private: + clang::NamedDecl * + DeclForGlobal (llvm::GlobalValue *global); + + //------------------------------------------------------------------ + /// Set the constant result variable m_const_result to the provided + /// constant, assuming it can be evaluated. The result variable + /// will be reset to NULL later if the expression has side effects. + /// + /// @param[in] initializer + /// The constant initializer for the variable. + /// + /// @param[in] name + /// The name of the result variable. + /// + /// @param[in] type + /// The Clang type of the result variable. + //------------------------------------------------------------------ + void + MaybeSetConstantResult (llvm::Constant *initializer, + const lldb_private::ConstString &name, + lldb_private::TypeFromParser type); + + //------------------------------------------------------------------ + /// If the IR represents a cast of a variable, set m_const_result + /// to the result of the cast. The result variable will be reset to + /// NULL latger if the expression has side effects. + /// + /// @param[in] type + /// The Clang type of the result variable. + //------------------------------------------------------------------ + void + MaybeSetCastResult (lldb_private::TypeFromParser type); + + //------------------------------------------------------------------ + /// The top-level pass implementation + /// + /// @param[in] llvm_function + /// The function currently being processed. + /// + /// @return + /// True on success; false otherwise + //------------------------------------------------------------------ + bool + CreateResultVariable (llvm::Function &llvm_function); + + //------------------------------------------------------------------ + /// A module-level pass to find Objective-C constant strings and + /// transform them to calls to CFStringCreateWithBytes. + //------------------------------------------------------------------ + + //------------------------------------------------------------------ + /// Rewrite a single Objective-C constant string. + /// + /// @param[in] NSStr + /// The constant NSString to be transformed + /// + /// @param[in] CStr + /// The constant C string inside the NSString. This will be + /// passed as the bytes argument to CFStringCreateWithBytes. + /// + /// @return + /// True on success; false otherwise + //------------------------------------------------------------------ + bool + RewriteObjCConstString (llvm::GlobalVariable *NSStr, + llvm::GlobalVariable *CStr); + + //------------------------------------------------------------------ + /// The top-level pass implementation + /// + /// @return + /// True on success; false otherwise + //------------------------------------------------------------------ + bool + RewriteObjCConstStrings (); + + //------------------------------------------------------------------ + /// A basic block-level pass to find all Objective-C method calls and + /// rewrite them to use sel_registerName instead of statically allocated + /// selectors. The reason is that the selectors are created on the + /// assumption that the Objective-C runtime will scan the appropriate + /// section and prepare them. This doesn't happen when code is copied + /// into the target, though, and there's no easy way to induce the + /// runtime to scan them. So instead we get our selectors from + /// sel_registerName. + //------------------------------------------------------------------ + + //------------------------------------------------------------------ + /// Replace a single selector reference + /// + /// @param[in] selector_load + /// The load of the statically-allocated selector. + /// + /// @return + /// True on success; false otherwise + //------------------------------------------------------------------ + bool + RewriteObjCSelector (llvm::Instruction* selector_load); + + //------------------------------------------------------------------ + /// The top-level pass implementation + /// + /// @param[in] basic_block + /// The basic block currently being processed. + /// + /// @return + /// True on success; false otherwise + //------------------------------------------------------------------ + bool + RewriteObjCSelectors (llvm::BasicBlock &basic_block); + + //------------------------------------------------------------------ + /// A basic block-level pass to find all newly-declared persistent + /// variables and register them with the ClangExprDeclMap. This + /// allows them to be materialized and dematerialized like normal + /// external variables. Before transformation, these persistent + /// variables look like normal locals, so they have an allocation. + /// This pass excises these allocations and makes references look + /// like external references where they will be resolved -- like all + /// other external references -- by ResolveExternals(). + //------------------------------------------------------------------ + + //------------------------------------------------------------------ + /// Handle a single allocation of a persistent variable + /// + /// @param[in] persistent_alloc + /// The allocation of the persistent variable. + /// + /// @return + /// True on success; false otherwise + //------------------------------------------------------------------ + bool + RewritePersistentAlloc (llvm::Instruction *persistent_alloc); + + //------------------------------------------------------------------ + /// The top-level pass implementation + /// + /// @param[in] basic_block + /// The basic block currently being processed. + //------------------------------------------------------------------ + bool + RewritePersistentAllocs (llvm::BasicBlock &basic_block); + + //------------------------------------------------------------------ + /// A function-level pass to find all external variables and functions + /// used in the IR. Each found external variable is added to the + /// struct, and each external function is resolved in place, its call + /// replaced with a call to a function pointer whose value is the + /// address of the function in the target process. + //------------------------------------------------------------------ + + //------------------------------------------------------------------ + /// Write an initializer to a memory array of assumed sufficient + /// size. + /// + /// @param[in] data + /// A pointer to the data to write to. + /// + /// @param[in] initializer + /// The initializer itself. + /// + /// @return + /// True on success; false otherwise + //------------------------------------------------------------------ + bool + MaterializeInitializer (uint8_t *data, llvm::Constant *initializer); + + //------------------------------------------------------------------ + /// Move an internal variable into the static allocation section. + /// + /// @param[in] global_variable + /// The variable. + /// + /// @return + /// True on success; false otherwise + //------------------------------------------------------------------ + bool + MaterializeInternalVariable (llvm::GlobalVariable *global_variable); + + //------------------------------------------------------------------ + /// Handle a single externally-defined variable + /// + /// @param[in] value + /// The variable. + /// + /// @return + /// True on success; false otherwise + //------------------------------------------------------------------ + bool + MaybeHandleVariable (llvm::Value *value); + + //------------------------------------------------------------------ + /// Handle a single externally-defined symbol + /// + /// @param[in] symbol + /// The symbol. + /// + /// @return + /// True on success; false otherwise + //------------------------------------------------------------------ + bool + HandleSymbol (llvm::Value *symbol); + + //------------------------------------------------------------------ + /// Handle a single externally-defined Objective-C class + /// + /// @param[in] classlist_reference + /// The reference, usually "01L_OBJC_CLASSLIST_REFERENCES_$_n" + /// where n (if present) is an index. + /// + /// @return + /// True on success; false otherwise + //------------------------------------------------------------------ + bool + HandleObjCClass(llvm::Value *classlist_reference); + + //------------------------------------------------------------------ + /// Handle all the arguments to a function call + /// + /// @param[in] C + /// The call instruction. + /// + /// @return + /// True on success; false otherwise + //------------------------------------------------------------------ + bool + MaybeHandleCallArguments (llvm::CallInst *call_inst); + + //------------------------------------------------------------------ + /// Resolve variable references in calls to external functions + /// + /// @param[in] basic_block + /// The basic block currently being processed. + /// + /// @return + /// True on success; false otherwise + //------------------------------------------------------------------ + bool + ResolveCalls (llvm::BasicBlock &basic_block); + + //------------------------------------------------------------------ + /// Remove calls to __cxa_atexit, which should never be generated by + /// expressions. + /// + /// @param[in] call_inst + /// The call instruction. + /// + /// @return + /// True if the scan was successful; false if some operation + /// failed + //------------------------------------------------------------------ + bool + RemoveCXAAtExit (llvm::BasicBlock &basic_block); + + //------------------------------------------------------------------ + /// The top-level pass implementation + /// + /// @param[in] basic_block + /// The function currently being processed. + /// + /// @return + /// True on success; false otherwise + //------------------------------------------------------------------ + bool + ResolveExternals (llvm::Function &llvm_function); + + //------------------------------------------------------------------ + /// A basic block-level pass to excise guard variables from the code. + /// The result for the function is passed through Clang as a static + /// variable. Static variables normally have guard variables to + /// ensure that they are only initialized once. + //------------------------------------------------------------------ + + //------------------------------------------------------------------ + /// Rewrite a load to a guard variable to return constant 0. + /// + /// @param[in] guard_load + /// The load instruction to zero out. + //------------------------------------------------------------------ + void + TurnGuardLoadIntoZero(llvm::Instruction* guard_load); + + //------------------------------------------------------------------ + /// The top-level pass implementation + /// + /// @param[in] basic_block + /// The basic block currently being processed. + /// + /// @return + /// True on success; false otherwise + //------------------------------------------------------------------ + bool + RemoveGuards (llvm::BasicBlock &basic_block); + + //------------------------------------------------------------------ + /// A module-level pass to allocate all string literals in a separate + /// allocation and redirect references to them. + //------------------------------------------------------------------ + + //------------------------------------------------------------------ + /// The top-level pass implementation + /// + /// @return + /// True on success; false otherwise + //------------------------------------------------------------------ + bool + ReplaceStrings (); + + //------------------------------------------------------------------ + /// A basick block-level pass to find all literals that will be + /// allocated as statics by the JIT (in contrast to the Strings, + /// which already are statics) and synthesize loads for them. + //------------------------------------------------------------------ + + //------------------------------------------------------------------ + /// The top-level pass implementation + /// + /// @param[in] basic_block + /// The basic block currently being processed. + /// + /// @return + /// True on success; false otherwise + //------------------------------------------------------------------ + bool + ReplaceStaticLiterals (llvm::BasicBlock &basic_block); + + //------------------------------------------------------------------ + /// A function-level pass to make all external variable references + /// point at the correct offsets from the void* passed into the + /// function. ClangExpressionDeclMap::DoStructLayout() must be called + /// beforehand, so that the offsets are valid. + //------------------------------------------------------------------ + + //------------------------------------------------------------------ + /// The top-level pass implementation + /// + /// @param[in] llvm_function + /// The function currently being processed. + /// + /// @return + /// True on success; false otherwise + //------------------------------------------------------------------ + bool + ReplaceVariables (llvm::Function &llvm_function); + + //------------------------------------------------------------------ + /// A module-level pass to remove all global variables from the + /// module since it no longer should export or import any symbols. + //------------------------------------------------------------------ + + //------------------------------------------------------------------ + /// The top-level pass implementation + /// + /// @param[in] llvm_module + /// The module currently being processed. + /// + /// @return + /// True on success; false otherwise + //------------------------------------------------------------------ + bool + StripAllGVs (llvm::Module &llvm_module); + + class StaticDataAllocator { + public: + StaticDataAllocator(lldb_private::IRExecutionUnit &execution_unit); + lldb_private::StreamString &GetStream() + { + return m_stream_string; + } + lldb::addr_t Allocate(); + private: + lldb_private::IRExecutionUnit &m_execution_unit; + lldb_private::StreamString m_stream_string; + lldb::addr_t m_allocation; + }; + + /// Flags + bool m_resolve_vars; ///< True if external variable references and persistent variable references should be resolved + std::string m_func_name; ///< The name of the function to translate + lldb_private::ConstString m_result_name; ///< The name of the result variable ($0, $1, ...) + lldb_private::TypeFromParser m_result_type; ///< The type of the result variable. + llvm::Module *m_module; ///< The module being processed, or NULL if that has not been determined yet. + std::unique_ptr m_target_data; ///< The target data for the module being processed, or NULL if there is no module. + lldb_private::ClangExpressionDeclMap *m_decl_map; ///< The DeclMap containing the Decls + StaticDataAllocator m_data_allocator; ///< The allocator to use for constant strings + llvm::Constant *m_CFStringCreateWithBytes; ///< The address of the function CFStringCreateWithBytes, cast to the appropriate function pointer type + llvm::Constant *m_sel_registerName; ///< The address of the function sel_registerName, cast to the appropriate function pointer type + lldb_private::Stream *m_error_stream; ///< If non-NULL, the stream on which errors should be printed + + llvm::StoreInst *m_result_store; ///< If non-NULL, the store instruction that writes to the result variable. If m_has_side_effects is true, this is NULL. + bool m_result_is_pointer; ///< True if the function's result in the AST is a pointer (see comments in ASTResultSynthesizer::SynthesizeBodyResult) + + llvm::GlobalVariable *m_reloc_placeholder; ///< A placeholder that will be replaced by a pointer to the final location of the static allocation. + + //------------------------------------------------------------------ + /// UnfoldConstant operates on a constant [Old] which has just been + /// replaced with a value [New]. We assume that new_value has + /// been properly placed early in the function, in front of the + /// first instruction in the entry basic block + /// [FirstEntryInstruction]. + /// + /// UnfoldConstant reads through the uses of Old and replaces Old + /// in those uses with New. Where those uses are constants, the + /// function generates new instructions to compute the result of the + /// new, non-constant expression and places them before + /// FirstEntryInstruction. These instructions replace the constant + /// uses, so UnfoldConstant calls itself recursively for those. + /// + /// @param[in] llvm_function + /// The function currently being processed. + /// + /// @return + /// True on success; false otherwise + //------------------------------------------------------------------ + + class FunctionValueCache { + public: + typedef std::function Maker; + + FunctionValueCache (Maker const &maker); + ~FunctionValueCache (); + llvm::Value *GetValue (llvm::Function *function); + private: + Maker const m_maker; + typedef std::map FunctionValueMap; + FunctionValueMap m_values; + }; + + FunctionValueCache m_entry_instruction_finder; + + static bool + UnfoldConstant (llvm::Constant *old_constant, + FunctionValueCache &value_maker, + FunctionValueCache &entry_instruction_finder); + + //------------------------------------------------------------------ + /// Construct a reference to m_reloc_placeholder with a given type + /// and offset. This typically happens after inserting data into + /// m_data_allocator. + /// + /// @param[in] type + /// The type of the value being loaded. + /// + /// @param[in] offset + /// The offset of the value from the base of m_data_allocator. + /// + /// @return + /// The Constant for the reference, usually a ConstantExpr. + //------------------------------------------------------------------ + llvm::Constant * + BuildRelocation(llvm::Type *type, + uint64_t offset); + + //------------------------------------------------------------------ + /// Commit the allocation in m_data_allocator and use its final + /// location to replace m_reloc_placeholder. + /// + /// @param[in] module + /// The module that m_data_allocator resides in + /// + /// @return + /// True on success; false otherwise + //------------------------------------------------------------------ + bool + CompleteDataAllocation (); + +}; + +#endif diff --git a/contrib/llvm/tools/lldb/include/lldb/Expression/IRInterpreter.h b/contrib/llvm/tools/lldb/include/lldb/Expression/IRInterpreter.h new file mode 100644 index 00000000000..5defa8dd202 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Expression/IRInterpreter.h @@ -0,0 +1,64 @@ +//===-- IRInterpreter.h -----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_IRInterpreter_h_ +#define liblldb_IRInterpreter_h_ + +#include "lldb/lldb-public.h" +#include "lldb/Core/ConstString.h" +#include "lldb/Core/Stream.h" +#include "lldb/Symbol/TaggedASTType.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/Pass.h" + +namespace llvm { + class Function; + class Module; +} + +namespace lldb_private { + +class ClangExpressionDeclMap; +class IRMemoryMap; + +} + +//---------------------------------------------------------------------- +/// @class IRInterpreter IRInterpreter.h "lldb/Expression/IRInterpreter.h" +/// @brief Attempt to interpret the function's code if it does not require +/// running the target. +/// +/// In some cases, the IR for an expression can be evaluated entirely +/// in the debugger, manipulating variables but not executing any code +/// in the target. The IRInterpreter attempts to do this. +//---------------------------------------------------------------------- +class IRInterpreter +{ +public: + static bool + CanInterpret (llvm::Module &module, + llvm::Function &function, + lldb_private::Error &error); + + static bool + Interpret (llvm::Module &module, + llvm::Function &function, + llvm::ArrayRef args, + lldb_private::IRMemoryMap &memory_map, + lldb_private::Error &error, + lldb::addr_t stack_frame_bottom, + lldb::addr_t stack_frame_top); + +private: + static bool + supportsFunction (llvm::Function &llvm_function, + lldb_private::Error &err); +}; + +#endif diff --git a/contrib/llvm/tools/lldb/include/lldb/Expression/IRMemoryMap.h b/contrib/llvm/tools/lldb/include/lldb/Expression/IRMemoryMap.h new file mode 100644 index 00000000000..affe19350e3 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Expression/IRMemoryMap.h @@ -0,0 +1,126 @@ +//===-- IRExecutionUnit.h ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef lldb_IRMemoryMap_h_ +#define lldb_IRMemoryMap_h_ + +#include "lldb/lldb-public.h" +#include "lldb/Core/DataBufferHeap.h" +#include "lldb/Core/UserID.h" + +#include + +namespace lldb_private +{ + +//---------------------------------------------------------------------- +/// @class IRMemoryMap IRMemoryMap.h "lldb/Expression/IRMemoryMap.h" +/// @brief Encapsulates memory that may exist in the process but must +/// also be available in the host process. +/// +/// This class encapsulates a group of memory objects that must be readable +/// or writable from the host process regardless of whether the process +/// exists. This allows the IR interpreter as well as JITted code to access +/// the same memory. +/// +/// Point queries against this group of memory objects can be made by the +/// address in the tar at which they reside. If the inferior does not +/// exist, allocations still get made-up addresses. If an inferior appears +/// at some point, then those addresses need to be re-mapped. +//---------------------------------------------------------------------- +class IRMemoryMap +{ +public: + IRMemoryMap (lldb::TargetSP target_sp); + ~IRMemoryMap (); + + enum AllocationPolicy { + eAllocationPolicyInvalid = 0, ///< It is an error for an allocation to have this policy. + eAllocationPolicyHostOnly, ///< This allocation was created in the host and will never make it into the process. + ///< It is an error to create other types of allocations while such allocations exist. + eAllocationPolicyMirror, ///< The intent is that this allocation exist both in the host and the process and have + ///< the same content in both. + eAllocationPolicyProcessOnly ///< The intent is that this allocation exist only in the process. + }; + + lldb::addr_t Malloc (size_t size, uint8_t alignment, uint32_t permissions, AllocationPolicy policy, Error &error); + void Leak (lldb::addr_t process_address, Error &error); + void Free (lldb::addr_t process_address, Error &error); + + void WriteMemory (lldb::addr_t process_address, const uint8_t *bytes, size_t size, Error &error); + void WriteScalarToMemory (lldb::addr_t process_address, Scalar &scalar, size_t size, Error &error); + void WritePointerToMemory (lldb::addr_t process_address, lldb::addr_t address, Error &error); + void ReadMemory (uint8_t *bytes, lldb::addr_t process_address, size_t size, Error &error); + void ReadScalarFromMemory (Scalar &scalar, lldb::addr_t process_address, size_t size, Error &error); + void ReadPointerFromMemory (lldb::addr_t *address, lldb::addr_t process_address, Error &error); + + void GetMemoryData (DataExtractor &extractor, lldb::addr_t process_address, size_t size, Error &error); + + lldb::ByteOrder GetByteOrder(); + uint32_t GetAddressByteSize(); + + // This function can return NULL. + ExecutionContextScope *GetBestExecutionContextScope(); + +protected: + // This function should only be used if you know you are using the JIT. + // Any other cases should use GetBestExecutionContextScope(). + lldb::ProcessWP GetProcessWP () + { + return m_process_wp; + } + +private: + struct Allocation + { + lldb::addr_t m_process_alloc; ///< The (unaligned) base for the remote allocation + lldb::addr_t m_process_start; ///< The base address of the allocation in the process + size_t m_size; ///< The size of the requested allocation + uint32_t m_permissions; ///< The access permissions on the memory in the process. In the host, the memory is always read/write. + uint8_t m_alignment; ///< The alignment of the requested allocation + DataBufferHeap m_data; + + ///< Flags + AllocationPolicy m_policy; + bool m_leak; + public: + Allocation (lldb::addr_t process_alloc, + lldb::addr_t process_start, + size_t size, + uint32_t permissions, + uint8_t alignment, + AllocationPolicy m_policy); + + Allocation () : + m_process_alloc (LLDB_INVALID_ADDRESS), + m_process_start (LLDB_INVALID_ADDRESS), + m_size (0), + m_permissions (0), + m_alignment (0), + m_data (), + m_policy (eAllocationPolicyInvalid), + m_leak (false) + { + } + }; + + lldb::ProcessWP m_process_wp; + lldb::TargetWP m_target_wp; + typedef std::map AllocationMap; + AllocationMap m_allocations; + + lldb::addr_t FindSpace (size_t size); + bool ContainsHostOnlyAllocations (); + AllocationMap::iterator FindAllocation (lldb::addr_t addr, size_t size); + bool IntersectsAllocation (lldb::addr_t addr, size_t size); +}; + +} + +#endif diff --git a/contrib/llvm/tools/lldb/include/lldb/Expression/IRToDWARF.h b/contrib/llvm/tools/lldb/include/lldb/Expression/IRToDWARF.h new file mode 100644 index 00000000000..43dc99d6d47 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Expression/IRToDWARF.h @@ -0,0 +1,111 @@ +//===-- IRToDWARF.h ---------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_IRToDWARF_h_ +#define liblldb_IRToDWARF_h_ + +#include "llvm/Pass.h" +#include "llvm/PassManager.h" + +#include "lldb/lldb-public.h" + +class Relocator; +//---------------------------------------------------------------------- +/// @class IRToDWARF IRToDWARF.h "lldb/Expression/IRToDWARF.h" +/// @brief Transforms the IR for a function into a DWARF location expression +/// +/// Once an expression has been parsed and converted to IR, it can run +/// in two contexts: interpreted by LLDB as a DWARF location expression, +/// or compiled by the JIT and inserted into the target process for +/// execution. +/// +/// IRToDWARF makes the first possible, by traversing the control flow +/// graph and writing the code for each basic block out as location +/// expression bytecode. To ensure that the links between the basic blocks +/// remain intact, it uses a relocator that records the location of every +/// location expression instruction that has a relocatable operand, the +/// target of that operand (as a basic block), and the mapping of each basic +/// block to an actual location. After all code has been written out, the +/// relocator post-processes it and performs all necessary relocations. +//---------------------------------------------------------------------- +class IRToDWARF : public llvm::ModulePass +{ +public: + //------------------------------------------------------------------ + /// Constructor + /// + /// @param[in] local_vars + /// A list of variables to populate with the local variables this + /// expression uses. + /// + /// @param[in] decl_map + /// The list of externally-referenced variables for the expression, + /// for use in looking up globals. + /// + /// @param[in] stream + /// The stream to dump DWARF bytecode onto. + /// + /// @param[in] func_name + /// The name of the function to translate to DWARF. + //------------------------------------------------------------------ + IRToDWARF(lldb_private::ClangExpressionVariableList &local_vars, + lldb_private::ClangExpressionDeclMap *decl_map, + lldb_private::StreamString &strm, + const char* func_name = "$__lldb_expr"); + + //------------------------------------------------------------------ + /// Destructor + //------------------------------------------------------------------ + virtual ~IRToDWARF(); + + //------------------------------------------------------------------ + /// Run this IR transformer on a single module + /// + /// @param[in] M + /// The module to run on. This module is searched for the function + /// $__lldb_expr, and that function is converted to a location + /// expression. + /// + /// @return + /// True on success; false otherwise + //------------------------------------------------------------------ + bool runOnModule(llvm::Module &M); + + //------------------------------------------------------------------ + /// Interface stub + //------------------------------------------------------------------ + void assignPassManager(llvm::PMStack &PMS, + llvm::PassManagerType T = llvm::PMT_ModulePassManager); + + //------------------------------------------------------------------ + /// Returns PMT_ModulePassManager + //------------------------------------------------------------------ + llvm::PassManagerType getPotentialPassManagerType() const; +private: + //------------------------------------------------------------------ + /// Run this IR transformer on a single basic block + /// + /// @param[in] BB + /// The basic block to transform. + /// + /// @param[in] Relocator + /// The relocator to use when registering branches. + /// + /// @return + /// True on success; false otherwise + //------------------------------------------------------------------ + bool runOnBasicBlock(llvm::BasicBlock &BB, Relocator &Relocator); + + std::string m_func_name; ///< The name of the function to translate + lldb_private::ClangExpressionVariableList &m_local_vars; ///< The list of local variables to populate while transforming + lldb_private::ClangExpressionDeclMap *m_decl_map; ///< The list of external variables + lldb_private::StreamString &m_strm; ///< The stream to write bytecode to +}; + +#endif diff --git a/contrib/llvm/tools/lldb/include/lldb/Expression/Materializer.h b/contrib/llvm/tools/lldb/include/lldb/Expression/Materializer.h new file mode 100644 index 00000000000..208a0813392 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Expression/Materializer.h @@ -0,0 +1,173 @@ +//===-- Materializer.h ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef lldb_Materializer_h +#define lldb_Materializer_h + +#include "lldb/lldb-private-types.h" +#include "lldb/Core/Error.h" +#include "lldb/Expression/IRMemoryMap.h" +#include "lldb/Host/Mutex.h" +#include "lldb/Symbol/SymbolContext.h" +#include "lldb/Target/StackFrame.h" + +#include + +namespace lldb_private +{ + +class Materializer +{ +public: + Materializer (); + ~Materializer (); + + class Dematerializer + { + public: + Dematerializer () : + m_materializer(NULL), + m_map(NULL), + m_process_address(LLDB_INVALID_ADDRESS) + { + } + + ~Dematerializer () + { + Wipe (); + } + + void Dematerialize (Error &err, + lldb::ClangExpressionVariableSP &result_sp, + lldb::addr_t frame_top, + lldb::addr_t frame_bottom); + + void Wipe (); + + bool IsValid () + { + return m_materializer && m_map && (m_process_address != LLDB_INVALID_ADDRESS); + } + private: + friend class Materializer; + + Dematerializer (Materializer &materializer, + lldb::StackFrameSP &frame_sp, + IRMemoryMap &map, + lldb::addr_t process_address) : + m_materializer(&materializer), + m_map(&map), + m_process_address(process_address) + { + if (frame_sp) + { + m_thread_wp = frame_sp->GetThread(); + m_stack_id = frame_sp->GetStackID(); + } + } + + Materializer *m_materializer; + lldb::ThreadWP m_thread_wp; + StackID m_stack_id; + IRMemoryMap *m_map; + lldb::addr_t m_process_address; + }; + + typedef std::shared_ptr DematerializerSP; + typedef std::weak_ptr DematerializerWP; + + DematerializerSP Materialize (lldb::StackFrameSP &frame_sp, IRMemoryMap &map, lldb::addr_t process_address, Error &err); + + uint32_t AddPersistentVariable (lldb::ClangExpressionVariableSP &persistent_variable_sp, Error &err); + uint32_t AddVariable (lldb::VariableSP &variable_sp, Error &err); + uint32_t AddResultVariable (const TypeFromUser &type, bool is_lvalue, bool keep_in_memory, Error &err); + uint32_t AddSymbol (const Symbol &symbol_sp, Error &err); + uint32_t AddRegister (const RegisterInfo ®ister_info, Error &err); + + uint32_t GetStructAlignment () + { + return m_struct_alignment; + } + + uint32_t GetStructByteSize () + { + return m_current_offset; + } + + uint32_t GetResultOffset () + { + if (m_result_entity) + return m_result_entity->GetOffset(); + else + return UINT32_MAX; + } + + class Entity + { + public: + Entity () : + m_alignment(1), + m_size(0), + m_offset(0) + { + } + + virtual ~Entity () + { + } + + virtual void Materialize (lldb::StackFrameSP &frame_sp, IRMemoryMap &map, lldb::addr_t process_address, Error &err) = 0; + virtual void Dematerialize (lldb::StackFrameSP &frame_sp, IRMemoryMap &map, lldb::addr_t process_address, + lldb::addr_t frame_top, lldb::addr_t frame_bottom, Error &err) = 0; + virtual void DumpToLog (IRMemoryMap &map, lldb::addr_t process_address, Log *log) = 0; + virtual void Wipe (IRMemoryMap &map, lldb::addr_t process_address) = 0; + + uint32_t GetAlignment () + { + return m_alignment; + } + + uint32_t GetSize () + { + return m_size; + } + + uint32_t GetOffset () + { + return m_offset; + } + + void SetOffset (uint32_t offset) + { + m_offset = offset; + } + protected: + void SetSizeAndAlignmentFromType (ClangASTType &type); + + uint32_t m_alignment; + uint32_t m_size; + uint32_t m_offset; + }; + +private: + uint32_t AddStructMember (Entity &entity); + + typedef std::unique_ptr EntityUP; + typedef std::vector EntityVector; + + DematerializerWP m_dematerializer_wp; + EntityVector m_entities; + Entity *m_result_entity; + uint32_t m_current_offset; + uint32_t m_struct_alignment; +}; + +} + +#endif diff --git a/contrib/llvm/tools/lldb/include/lldb/Host/Condition.h b/contrib/llvm/tools/lldb/include/lldb/Host/Condition.h new file mode 100644 index 00000000000..98439ee2ebd --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Host/Condition.h @@ -0,0 +1,124 @@ +//===-- Condition.h ---------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_DBCondition_h_ +#define liblldb_DBCondition_h_ +#if defined(__cplusplus) + + +#include +#include "lldb/Host/Mutex.h" + +namespace lldb_private { + +class TimeValue; + +//---------------------------------------------------------------------- +/// @class Condition Condition.h "lldb/Host/Condition.h" +/// @brief A C++ wrapper class for pthread condition variables. +/// +/// A class that wraps up a pthread condition (pthread_cond_t). The +/// class will create a pthread condition when an instance is +/// constructed, and detroy it when it is destructed. It also provides +/// access to the standard pthread condition calls. +//---------------------------------------------------------------------- +class Condition +{ +public: + + //------------------------------------------------------------------ + /// Default constructor + /// + /// The default constructor will initialize a new pthread condition + /// and maintain the condition in the object state. + //------------------------------------------------------------------ + Condition (); + + //------------------------------------------------------------------ + /// Destructor + /// + /// Destroys the pthread condition that the object owns. + //------------------------------------------------------------------ + ~Condition (); + + //------------------------------------------------------------------ + /// Unblock all threads waiting for a condition variable + /// + /// @return + /// The return value from \c pthread_cond_broadcast() + //------------------------------------------------------------------ + int + Broadcast (); + + //------------------------------------------------------------------ + /// Unblocks one thread waiting for the condition variable + /// + /// @return + /// The return value from \c pthread_cond_signal() + //------------------------------------------------------------------ + int + Signal (); + + //------------------------------------------------------------------ + /// Wait for the condition variable to be signaled. + /// + /// The Wait() function atomically blocks the current thread + /// waiting on this object's condition variable, and unblocks + /// \a mutex. The waiting thread unblocks only after another thread + /// signals or broadcasts this object's condition variable. + /// + /// If \a abstime is non-NULL, this function will return when the + /// system time reaches the time specified in \a abstime if the + /// condition variable doesn't get unblocked. If \a abstime is NULL + /// this function will wait for an infinite amount of time for the + /// condition variable to be unblocked. + /// + /// The current thread re-acquires the lock on \a mutex following + /// the wait. + /// + /// @param[in] mutex + /// The mutex to use in the \c pthread_cond_timedwait() or + /// \c pthread_cond_wait() calls. + /// + /// @param[in] abstime + /// An absolute time at which to stop waiting if non-NULL, else + /// wait an infinite amount of time for the condition variable + /// toget signaled. + /// + /// @param[out] timed_out + /// If not NULL, will be set to true if the wait timed out, and + // false otherwise. + /// + /// @see Condition::Broadcast() + /// @see Condition::Signal() + //------------------------------------------------------------------ + int + Wait (Mutex &mutex, const TimeValue *abstime = NULL, bool *timed_out = NULL); + +protected: + //------------------------------------------------------------------ + // Member variables + //------------------------------------------------------------------ + pthread_cond_t m_condition; ///< The condition variable. + + //------------------------------------------------------------------ + /// Get accessor to the pthread condition object. + /// + /// @return + /// A pointer to the condition variable owned by this object. + //------------------------------------------------------------------ + pthread_cond_t * + GetCondition (); +}; + +} // namespace lldb_private + +#endif // #if defined(__cplusplus) +#endif + diff --git a/contrib/llvm/tools/lldb/include/lldb/Host/Config.h b/contrib/llvm/tools/lldb/include/lldb/Host/Config.h new file mode 100644 index 00000000000..2d5d39baac3 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Host/Config.h @@ -0,0 +1,35 @@ +//===-- Config.h ------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_Config_h_ +#define liblldb_Config_h_ + +#if defined(__APPLE__) + +#include "lldb/Host/macosx/Config.h" + +#elif defined(__linux__) + +#include "lldb/Host/linux/Config.h" + +#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__OpenBSD__) + +#include "lldb/Host/freebsd/Config.h" + +#elif defined(__MINGW__) || defined (__MINGW32__) + +#include "lldb/Host/mingw/Config.h" + +#else + +#error undefined platform + +#endif + +#endif // #ifndef liblldb_Config_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Host/DynamicLibrary.h b/contrib/llvm/tools/lldb/include/lldb/Host/DynamicLibrary.h new file mode 100644 index 00000000000..1fcc7d1883c --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Host/DynamicLibrary.h @@ -0,0 +1,51 @@ +//===-- DynamicLibrary.h -------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_DynamicLibrary_h_ +#define liblldb_DynamicLibrary_h_ + +#include "lldb/Host/FileSpec.h" +#include "lldb/Host/Host.h" + +namespace lldb_private { + +class DynamicLibrary +{ +public: + DynamicLibrary (const FileSpec& spec, uint32_t options = Host::eDynamicLibraryOpenOptionLazy | + Host::eDynamicLibraryOpenOptionLocal | + Host::eDynamicLibraryOpenOptionLimitGetSymbol); + + ~DynamicLibrary (); + + template + T GetSymbol (const char* name) + { + Error err; + if (!m_handle) + return (T)NULL; + void* symbol = Host::DynamicLibraryGetSymbol (m_handle, name, err); + if (!symbol) + return (T)NULL; + return (T)symbol; + } + + bool + IsValid (); + +private: + lldb_private::FileSpec m_filespec; + void* m_handle; + + DISALLOW_COPY_AND_ASSIGN (DynamicLibrary); +}; + +} // namespace lldb_private + +#endif // liblldb_DynamicLibrary_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Host/Endian.h b/contrib/llvm/tools/lldb/include/lldb/Host/Endian.h new file mode 100644 index 00000000000..610f3ce95c4 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Host/Endian.h @@ -0,0 +1,33 @@ +//===-- Endian.h ------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_host_endian_h_ +#define liblldb_host_endian_h_ + +#include "lldb/lldb-enumerations.h" + +namespace lldb { + +namespace endian { + + static union EndianTest + { + uint32_t num; + uint8_t bytes[sizeof(uint32_t)]; + } const endianTest = { (uint16_t)0x01020304 }; + + inline ByteOrder InlHostByteOrder() { return (ByteOrder)endianTest.bytes[0]; } + +// ByteOrder const InlHostByteOrder = (ByteOrder)endianTest.bytes[0]; +} + +} + +#endif // liblldb_host_endian_h_ + diff --git a/contrib/llvm/tools/lldb/include/lldb/Host/File.h b/contrib/llvm/tools/lldb/include/lldb/Host/File.h new file mode 100644 index 00000000000..df7fe92cccb --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Host/File.h @@ -0,0 +1,500 @@ +//===-- File.h --------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_File_h_ +#define liblldb_File_h_ +#if defined(__cplusplus) + +#include + +#include "lldb/lldb-private.h" + +namespace lldb_private { + +//---------------------------------------------------------------------- +/// @class File File.h "lldb/Host/File.h" +/// @brief A file class. +/// +/// A file class that divides abstracts the LLDB core from host file +/// functionality. +//---------------------------------------------------------------------- +class File +{ +public: + static int kInvalidDescriptor; + static FILE * kInvalidStream; + + enum OpenOptions + { + eOpenOptionRead = (1u << 0), // Open file for reading + eOpenOptionWrite = (1u << 1), // Open file for writing + eOpenOptionAppend = (1u << 2), // Don't truncate file when opening, append to end of file + eOpenOptionTruncate = (1u << 3), // Truncate file when opening + eOpenOptionNonBlocking = (1u << 4), // File reads + eOpenOptionCanCreate = (1u << 5), // Create file if doesn't already exist + eOpenOptionCanCreateNewOnly = (1u << 6) // Can create file only if it doesn't already exist + }; + + enum Permissions + { + ePermissionsUserRead = (1u << 0), + ePermissionsUserWrite = (1u << 1), + ePermissionsUserExecute = (1u << 2), + ePermissionsGroupRead = (1u << 3), + ePermissionsGroupWrite = (1u << 4), + ePermissionsGroupExecute = (1u << 5), + ePermissionsWorldRead = (1u << 6), + ePermissionsWorldWrite = (1u << 7), + ePermissionsWorldExecute = (1u << 8), + + ePermissionsUserRW = (ePermissionsUserRead | ePermissionsUserWrite | 0 ), + ePermissionsUserRX = (ePermissionsUserRead | 0 | ePermissionsUserExecute ), + ePermissionsUserRWX = (ePermissionsUserRead | ePermissionsUserWrite | ePermissionsUserExecute ), + + ePermissionsGroupRW = (ePermissionsGroupRead | ePermissionsGroupWrite | 0 ), + ePermissionsGroupRX = (ePermissionsGroupRead | 0 | ePermissionsGroupExecute ), + ePermissionsGroupRWX = (ePermissionsGroupRead | ePermissionsGroupWrite | ePermissionsGroupExecute ), + + ePermissionsWorldRW = (ePermissionsWorldRead | ePermissionsWorldWrite | 0 ), + ePermissionsWorldRX = (ePermissionsWorldRead | 0 | ePermissionsWorldExecute ), + ePermissionsWorldRWX = (ePermissionsWorldRead | ePermissionsWorldWrite | ePermissionsWorldExecute ), + + ePermissionsEveryoneR = (ePermissionsUserRead | ePermissionsGroupRead | ePermissionsWorldRead ), + ePermissionsEveryoneW = (ePermissionsUserWrite | ePermissionsGroupWrite | ePermissionsWorldWrite ), + ePermissionsEveryoneX = (ePermissionsUserExecute | ePermissionsGroupExecute | ePermissionsWorldExecute ), + + ePermissionsEveryoneRW = (ePermissionsEveryoneR | ePermissionsEveryoneW | 0 ), + ePermissionsEveryoneRX = (ePermissionsEveryoneR | 0 | ePermissionsEveryoneX ), + ePermissionsEveryoneRWX = (ePermissionsEveryoneR | ePermissionsEveryoneW | ePermissionsEveryoneX ), + ePermissionsDefault = (ePermissionsUserRW | ePermissionsGroupRead) + }; + + File() : + m_descriptor (kInvalidDescriptor), + m_stream (kInvalidStream), + m_options (0), + m_owned (false) + { + } + + File (FILE *fh, bool transfer_ownership) : + m_descriptor (kInvalidDescriptor), + m_stream (fh), + m_options (0), + m_owned (transfer_ownership) + { + } + + File (const File &rhs); + + File & + operator= (const File &rhs); + //------------------------------------------------------------------ + /// Constructor with path. + /// + /// Takes a path to a file which can be just a filename, or a full + /// path. If \a path is not NULL or empty, this function will call + /// File::Open (const char *path, uint32_t options, uint32_t permissions). + /// + /// @param[in] path + /// The full or partial path to a file. + /// + /// @param[in] options + /// Options to use when opening (see File::OpenOptions) + /// + /// @param[in] permissions + /// Options to use when opening (see File::Permissions) + /// + /// @see File::Open (const char *path, uint32_t options, uint32_t permissions) + //------------------------------------------------------------------ + File (const char *path, + uint32_t options, + uint32_t permissions = ePermissionsDefault); + + + File (int fd, bool tranfer_ownership) : + m_descriptor (fd), + m_stream (kInvalidStream), + m_options (0), + m_owned (tranfer_ownership) + { + } + //------------------------------------------------------------------ + /// Destructor. + /// + /// The destructor is virtual in case this class is subclassed. + //------------------------------------------------------------------ + virtual + ~File (); + + bool + IsValid () const + { + return DescriptorIsValid() || StreamIsValid(); + } + + //------------------------------------------------------------------ + /// Convert to pointer operator. + /// + /// This allows code to check a File object to see if it + /// contains anything valid using code such as: + /// + /// @code + /// File file(...); + /// if (file) + /// { ... + /// @endcode + /// + /// @return + /// A pointer to this object if either the directory or filename + /// is valid, NULL otherwise. + //------------------------------------------------------------------ + operator + bool () const + { + return DescriptorIsValid() || StreamIsValid(); + } + + //------------------------------------------------------------------ + /// Logical NOT operator. + /// + /// This allows code to check a File object to see if it is + /// invalid using code such as: + /// + /// @code + /// File file(...); + /// if (!file) + /// { ... + /// @endcode + /// + /// @return + /// Returns \b true if the object has an empty directory and + /// filename, \b false otherwise. + //------------------------------------------------------------------ + bool + operator! () const + { + return !DescriptorIsValid() && !StreamIsValid(); + } + + //------------------------------------------------------------------ + /// Get the file spec for this file. + /// + /// @return + /// A reference to the file specification object. + //------------------------------------------------------------------ + Error + GetFileSpec (FileSpec &file_spec) const; + + //------------------------------------------------------------------ + /// Open a file for read/writing with the specified options. + /// + /// Takes a path to a file which can be just a filename, or a full + /// path. + /// + /// @param[in] path + /// The full or partial path to a file. + /// + /// @param[in] options + /// Options to use when opening (see File::OpenOptions) + /// + /// @param[in] permissions + /// Options to use when opening (see File::Permissions) + //------------------------------------------------------------------ + Error + Open (const char *path, + uint32_t options, + uint32_t permissions = ePermissionsDefault); + + Error + Close (); + + Error + Duplicate (const File &rhs); + + int + GetDescriptor() const; + + void + SetDescriptor(int fd, bool transfer_ownership); + + FILE * + GetStream (); + + void + SetStream (FILE *fh, bool transfer_ownership); + + //------------------------------------------------------------------ + /// Read bytes from a file from the current file position. + /// + /// NOTE: This function is NOT thread safe. Use the read function + /// that takes an "off_t &offset" to ensure correct operation in + /// multi-threaded environments. + /// + /// @param[in] buf + /// A buffer where to put the bytes that are read. + /// + /// @param[in/out] num_bytes + /// The number of bytes to read form the current file position + /// which gets modified with the number of bytes that were read. + /// + /// @return + /// An error object that indicates success or the reason for + /// failure. + //------------------------------------------------------------------ + Error + Read (void *buf, size_t &num_bytes); + + //------------------------------------------------------------------ + /// Write bytes to a file at the current file position. + /// + /// NOTE: This function is NOT thread safe. Use the write function + /// that takes an "off_t &offset" to ensure correct operation in + /// multi-threaded environments. + /// + /// @param[in] buf + /// A buffer where to put the bytes that are read. + /// + /// @param[in/out] num_bytes + /// The number of bytes to write to the current file position + /// which gets modified with the number of bytes that were + /// written. + /// + /// @return + /// An error object that indicates success or the reason for + /// failure. + //------------------------------------------------------------------ + Error + Write (const void *buf, size_t &num_bytes); + + //------------------------------------------------------------------ + /// Seek to an offset relative to the beginning of the file. + /// + /// NOTE: This function is NOT thread safe, other threads that + /// access this object might also change the current file position. + /// For thread safe reads and writes see the following functions: + /// @see File::Read (void *, size_t, off_t &) + /// @see File::Write (const void *, size_t, off_t &) + /// + /// @param[in] offset + /// The offset to seek to within the file relative to the + /// beginning of the file. + /// + /// @param[in] error_ptr + /// A pointer to a lldb_private::Error object that will be + /// filled in if non-NULL. + /// + /// @return + /// The resulting seek offset, or -1 on error. + //------------------------------------------------------------------ + off_t + SeekFromStart (off_t offset, Error *error_ptr = NULL); + + //------------------------------------------------------------------ + /// Seek to an offset relative to the current file position. + /// + /// NOTE: This function is NOT thread safe, other threads that + /// access this object might also change the current file position. + /// For thread safe reads and writes see the following functions: + /// @see File::Read (void *, size_t, off_t &) + /// @see File::Write (const void *, size_t, off_t &) + /// + /// @param[in] offset + /// The offset to seek to within the file relative to the + /// current file position. + /// + /// @param[in] error_ptr + /// A pointer to a lldb_private::Error object that will be + /// filled in if non-NULL. + /// + /// @return + /// The resulting seek offset, or -1 on error. + //------------------------------------------------------------------ + off_t + SeekFromCurrent (off_t offset, Error *error_ptr = NULL); + + //------------------------------------------------------------------ + /// Seek to an offset relative to the end of the file. + /// + /// NOTE: This function is NOT thread safe, other threads that + /// access this object might also change the current file position. + /// For thread safe reads and writes see the following functions: + /// @see File::Read (void *, size_t, off_t &) + /// @see File::Write (const void *, size_t, off_t &) + /// + /// @param[in/out] offset + /// The offset to seek to within the file relative to the + /// end of the file which gets filled in the the resulting + /// absolute file offset. + /// + /// @param[in] error_ptr + /// A pointer to a lldb_private::Error object that will be + /// filled in if non-NULL. + /// + /// @return + /// The resulting seek offset, or -1 on error. + //------------------------------------------------------------------ + off_t + SeekFromEnd (off_t offset, Error *error_ptr = NULL); + + //------------------------------------------------------------------ + /// Read bytes from a file from the specified file offset. + /// + /// NOTE: This function is thread safe in that clients manager their + /// own file position markers and reads on other threads won't mess + /// up the current read. + /// + /// @param[in] buf + /// A buffer where to put the bytes that are read. + /// + /// @param[in/out] num_bytes + /// The number of bytes to read form the current file position + /// which gets modified with the number of bytes that were read. + /// + /// @param[in/out] offset + /// The offset within the file from which to read \a num_bytes + /// bytes. This offset gets incremented by the number of bytes + /// that were read. + /// + /// @return + /// An error object that indicates success or the reason for + /// failure. + //------------------------------------------------------------------ + Error + Read (void *dst, size_t &num_bytes, off_t &offset); + + //------------------------------------------------------------------ + /// Read bytes from a file from the specified file offset. + /// + /// NOTE: This function is thread safe in that clients manager their + /// own file position markers and reads on other threads won't mess + /// up the current read. + /// + /// @param[in/out] num_bytes + /// The number of bytes to read form the current file position + /// which gets modified with the number of bytes that were read. + /// + /// @param[in/out] offset + /// The offset within the file from which to read \a num_bytes + /// bytes. This offset gets incremented by the number of bytes + /// that were read. + /// + /// @param[in] null_terminate + /// Ensure that the data that is read is terminated with a NULL + /// character so that the data can be used as a C string. + /// + /// @param[out] data_buffer_sp + /// A data buffer to create and fill in that will contain any + /// data that is read from the file. This buffer will be reset + /// if an error occurs. + /// + /// @return + /// An error object that indicates success or the reason for + /// failure. + //------------------------------------------------------------------ + Error + Read (size_t &num_bytes, + off_t &offset, + bool null_terminate, + lldb::DataBufferSP &data_buffer_sp); + + //------------------------------------------------------------------ + /// Write bytes to a file at the specified file offset. + /// + /// NOTE: This function is thread safe in that clients manager their + /// own file position markers, though clients will need to implement + /// their own locking externally to avoid multiple people writing + /// to the file at the same time. + /// + /// @param[in] buf + /// A buffer containing the bytes to write. + /// + /// @param[in/out] num_bytes + /// The number of bytes to write to the file at offset \a offset. + /// \a num_bytes gets modified with the number of bytes that + /// were read. + /// + /// @param[in/out] offset + /// The offset within the file at which to write \a num_bytes + /// bytes. This offset gets incremented by the number of bytes + /// that were written. + /// + /// @return + /// An error object that indicates success or the reason for + /// failure. + //------------------------------------------------------------------ + Error + Write (const void *src, size_t &num_bytes, off_t &offset); + + //------------------------------------------------------------------ + /// Flush the current stream + /// + /// @return + /// An error object that indicates success or the reason for + /// failure. + //------------------------------------------------------------------ + Error + Flush (); + + //------------------------------------------------------------------ + /// Sync to disk. + /// + /// @return + /// An error object that indicates success or the reason for + /// failure. + //------------------------------------------------------------------ + Error + Sync (); + + //------------------------------------------------------------------ + /// Output printf formatted output to the stream. + /// + /// Print some formatted output to the stream. + /// + /// @param[in] format + /// A printf style format string. + /// + /// @param[in] ... + /// Variable arguments that are needed for the printf style + /// format string \a format. + //------------------------------------------------------------------ + size_t + Printf (const char *format, ...) __attribute__ ((format (printf, 2, 3))); + + size_t + PrintfVarArg(const char *format, va_list args); + +protected: + + + bool + DescriptorIsValid () const + { + return m_descriptor >= 0; + } + + bool + StreamIsValid () const + { + return m_stream != kInvalidStream; + } + + //------------------------------------------------------------------ + // Member variables + //------------------------------------------------------------------ + int m_descriptor; + FILE *m_stream; + uint32_t m_options; + bool m_owned; +}; + +} // namespace lldb_private + +#endif // #if defined(__cplusplus) +#endif // liblldb_File_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Host/FileSpec.h b/contrib/llvm/tools/lldb/include/lldb/Host/FileSpec.h new file mode 100644 index 00000000000..c58be9ec09d --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Host/FileSpec.h @@ -0,0 +1,692 @@ +//===-- FileSpec.h ----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_FileSpec_h_ +#define liblldb_FileSpec_h_ +#if defined(__cplusplus) + +#include "lldb/lldb-private.h" +#include "lldb/Core/ConstString.h" +#include "lldb/Core/STLUtils.h" +#include "lldb/Host/TimeValue.h" + +namespace lldb_private { + +//---------------------------------------------------------------------- +/// @class FileSpec FileSpec.h "lldb/Host/FileSpec.h" +/// @brief A file utility class. +/// +/// A file specification class that divides paths up into a directory +/// and basename. These string values of the paths are put into uniqued +/// string pools for fast comparisons and efficient memory usage. +/// +/// Another reason the paths are split into the directory and basename +/// is to allow efficient debugger searching. Often in a debugger the +/// user types in the basename of the file, for example setting a +/// breakpoint by file and line, or specifying a module (shared library) +/// to limit the scope in which to execute a command. The user rarely +/// types in a full path. When the paths are already split up, it makes +/// it easy for us to compare only the basenames of a lot of file +/// specifications without having to split up the file path each time +/// to get to the basename. +//---------------------------------------------------------------------- +class FileSpec +{ +public: + typedef enum FileType + { + eFileTypeInvalid = -1, + eFileTypeUnknown = 0, + eFileTypeDirectory, + eFileTypePipe, + eFileTypeRegular, + eFileTypeSocket, + eFileTypeSymbolicLink, + eFileTypeOther + } FileType; + + FileSpec(); + + //------------------------------------------------------------------ + /// Constructor with path. + /// + /// Takes a path to a file which can be just a filename, or a full + /// path. If \a path is not NULL or empty, this function will call + /// FileSpec::SetFile (const char *path, bool resolve). + /// + /// @param[in] path + /// The full or partial path to a file. + /// + /// @param[in] resolve_path + /// If \b true, then we resolve the path with realpath, + /// if \b false we trust the path is in canonical form already. + /// + /// @see FileSpec::SetFile (const char *path, bool resolve) + //------------------------------------------------------------------ + explicit FileSpec (const char *path, bool resolve_path); + + //------------------------------------------------------------------ + /// Copy constructor + /// + /// Makes a copy of the uniqued directory and filename strings from + /// \a rhs. + /// + /// @param[in] rhs + /// A const FileSpec object reference to copy. + //------------------------------------------------------------------ + FileSpec (const FileSpec& rhs); + + //------------------------------------------------------------------ + /// Copy constructor + /// + /// Makes a copy of the uniqued directory and filename strings from + /// \a rhs if it is not NULL. + /// + /// @param[in] rhs + /// A const FileSpec object pointer to copy if non-NULL. + //------------------------------------------------------------------ + FileSpec (const FileSpec* rhs); + + //------------------------------------------------------------------ + /// Destructor. + //------------------------------------------------------------------ + ~FileSpec (); + + //------------------------------------------------------------------ + /// Assignment operator. + /// + /// Makes a copy of the uniqued directory and filename strings from + /// \a rhs. + /// + /// @param[in] rhs + /// A const FileSpec object reference to assign to this object. + /// + /// @return + /// A const reference to this object. + //------------------------------------------------------------------ + const FileSpec& + operator= (const FileSpec& rhs); + + //------------------------------------------------------------------ + /// Equal to operator + /// + /// Tests if this object is equal to \a rhs. + /// + /// @param[in] rhs + /// A const FileSpec object reference to compare this object + /// to. + /// + /// @return + /// \b true if this object is equal to \a rhs, \b false + /// otherwise. + //------------------------------------------------------------------ + bool + operator== (const FileSpec& rhs) const; + + //------------------------------------------------------------------ + /// Not equal to operator + /// + /// Tests if this object is not equal to \a rhs. + /// + /// @param[in] rhs + /// A const FileSpec object reference to compare this object + /// to. + /// + /// @return + /// \b true if this object is equal to \a rhs, \b false + /// otherwise. + //------------------------------------------------------------------ + bool + operator!= (const FileSpec& rhs) const; + + //------------------------------------------------------------------ + /// Less than to operator + /// + /// Tests if this object is less than \a rhs. + /// + /// @param[in] rhs + /// A const FileSpec object reference to compare this object + /// to. + /// + /// @return + /// \b true if this object is less than \a rhs, \b false + /// otherwise. + //------------------------------------------------------------------ + bool + operator< (const FileSpec& rhs) const; + + //------------------------------------------------------------------ + /// Convert to pointer operator. + /// + /// This allows code to check a FileSpec object to see if it + /// contains anything valid using code such as: + /// + /// @code + /// FileSpec file_spec(...); + /// if (file_spec) + /// { ... + /// @endcode + /// + /// @return + /// A pointer to this object if either the directory or filename + /// is valid, NULL otherwise. + //------------------------------------------------------------------ + operator bool() const; + + //------------------------------------------------------------------ + /// Logical NOT operator. + /// + /// This allows code to check a FileSpec object to see if it is + /// invalid using code such as: + /// + /// @code + /// FileSpec file_spec(...); + /// if (!file_spec) + /// { ... + /// @endcode + /// + /// @return + /// Returns \b true if the object has an empty directory and + /// filename, \b false otherwise. + //------------------------------------------------------------------ + bool + operator! () const; + + //------------------------------------------------------------------ + /// Clears the object state. + /// + /// Clear this object by releasing both the directory and filename + /// string values and reverting them to empty strings. + //------------------------------------------------------------------ + void + Clear (); + + //------------------------------------------------------------------ + /// Compare two FileSpec objects. + /// + /// If \a full is true, then both the directory and the filename + /// must match. If \a full is false, then the directory names for + /// \a lhs and \a rhs are only compared if they are both not empty. + /// This allows a FileSpec object to only contain a filename + /// and it can match FileSpec objects that have matching + /// filenames with different paths. + /// + /// @param[in] lhs + /// A const reference to the Left Hand Side object to compare. + /// + /// @param[in] rhs + /// A const reference to the Right Hand Side object to compare. + /// + /// @param[in] full + /// If true, then both the directory and filenames will have to + /// match for a compare to return zero (equal to). If false + /// and either directory from \a lhs or \a rhs is empty, then + /// only the filename will be compared, else a full comparison + /// is done. + /// + /// @return + /// @li -1 if \a lhs is less than \a rhs + /// @li 0 if \a lhs is equal to \a rhs + /// @li 1 if \a lhs is greater than \a rhs + //------------------------------------------------------------------ + static int + Compare (const FileSpec& lhs, const FileSpec& rhs, bool full); + + static bool + Equal (const FileSpec& a, const FileSpec& b, bool full); + + //------------------------------------------------------------------ + /// Dump this object to a Stream. + /// + /// Dump the object to the supplied stream \a s. If the object + /// contains a valid directory name, it will be displayed followed + /// by a directory delimiter, and the filename. + /// + /// @param[in] s + /// The stream to which to dump the object descripton. + //------------------------------------------------------------------ + void + Dump (Stream *s) const; + + //------------------------------------------------------------------ + /// Existence test. + /// + /// @return + /// \b true if the file exists on disk, \b false otherwise. + //------------------------------------------------------------------ + bool + Exists () const; + + + //------------------------------------------------------------------ + /// Expanded existence test. + /// + /// Call into the Host to see if it can help find the file (e.g. by + /// searching paths set in the environment, etc.). + /// + /// If found, sets the value of m_directory to the directory where + /// the file was found. + /// + /// @return + /// \b true if was able to find the file using expanded search + /// methods, \b false otherwise. + //------------------------------------------------------------------ + bool + ResolveExecutableLocation (); + + //------------------------------------------------------------------ + /// Canonicalize this file path (basically running the static + /// FileSpec::Resolve method on it). Useful if you asked us not to + /// resolve the file path when you set the file. + //------------------------------------------------------------------ + bool + ResolvePath (); + + uint64_t + GetByteSize() const; + + //------------------------------------------------------------------ + /// Directory string get accessor. + /// + /// @return + /// A reference to the directory string object. + //------------------------------------------------------------------ + ConstString & + GetDirectory (); + + //------------------------------------------------------------------ + /// Directory string const get accessor. + /// + /// @return + /// A const reference to the directory string object. + //------------------------------------------------------------------ + const ConstString & + GetDirectory () const; + + //------------------------------------------------------------------ + /// Filename string get accessor. + /// + /// @return + /// A reference to the filename string object. + //------------------------------------------------------------------ + ConstString & + GetFilename (); + + //------------------------------------------------------------------ + /// Filename string const get accessor. + /// + /// @return + /// A const reference to the filename string object. + //------------------------------------------------------------------ + const ConstString & + GetFilename () const; + + //------------------------------------------------------------------ + /// Returns true if the filespec represents an implementation source + /// file (files with a ".c", ".cpp", ".m", ".mm" (many more) + /// extension). + /// + /// @return + /// \b true if the filespec represents an implementation source + /// file, \b false otherwise. + //------------------------------------------------------------------ + bool + IsSourceImplementationFile () const; + + //------------------------------------------------------------------ + /// Returns true if the filespec represents path that is relative + /// path to the current working directory. + /// + /// @return + /// \b true if the filespec represents a current working + /// directory relative path, \b false otherwise. + //------------------------------------------------------------------ + bool + IsRelativeToCurrentWorkingDirectory () const; + + TimeValue + GetModificationTime () const; + + //------------------------------------------------------------------ + /// Extract the full path to the file. + /// + /// Extract the directory and path into a fixed buffer. This is + /// needed as the directory and path are stored in separate string + /// values. + /// + /// @param[out] path + /// The buffer in which to place the extracted full path. + /// + /// @param[in] max_path_length + /// The maximum length of \a path. + /// + /// @return + /// Returns the number of characters that would be needed to + /// properly copy the full path into \a path. If the returned + /// number is less than \a max_path_length, then the path is + /// properly copied and terminated. If the return value is + /// >= \a max_path_length, then the path was truncated (but is + /// still NULL terminated). + //------------------------------------------------------------------ + size_t + GetPath (char *path, size_t max_path_length) const; + + //------------------------------------------------------------------ + /// Extract the full path to the file. + /// + /// Extract the directory and path into a std::string, which is returned. + /// + /// @return + /// Returns a std::string with the directory and filename + /// concatenated. + //------------------------------------------------------------------ + std::string + GetPath () const; + + //------------------------------------------------------------------ + /// Extract the extension of the file. + /// + /// Returns a ConstString that represents the extension of the filename + /// for this FileSpec object. If this object does not represent a file, + /// or the filename has no extension, ConstString(NULL) is returned. + /// The dot ('.') character is not returned as part of the extension + /// + /// @return + /// Returns the extension of the file as a ConstString object. + //------------------------------------------------------------------ + ConstString + GetFileNameExtension () const; + + //------------------------------------------------------------------ + /// Return the filename without the extension part + /// + /// Returns a ConstString that represents the filename of this object + /// without the extension part (e.g. for a file named "foo.bar", "foo" + /// is returned) + /// + /// @return + /// Returns the filename without extension + /// as a ConstString object. + //------------------------------------------------------------------ + ConstString + GetFileNameStrippingExtension () const; + + FileType + GetFileType () const; + + bool + IsDirectory () const + { + return GetFileType() == FileSpec::eFileTypeDirectory; + } + + bool + IsPipe () const + { + return GetFileType() == FileSpec::eFileTypePipe; + } + + bool + IsRegularFile () const + { + return GetFileType() == FileSpec::eFileTypeRegular; + } + + bool + IsSocket () const + { + return GetFileType() == FileSpec::eFileTypeSocket; + } + + bool + IsSymbolicLink () const + { + return GetFileType() == FileSpec::eFileTypeSymbolicLink; + } + + //------------------------------------------------------------------ + /// Get the memory cost of this object. + /// + /// Return the size in bytes that this object takes in memory. This + /// returns the size in bytes of this object, not any shared string + /// values it may refer to. + /// + /// @return + /// The number of bytes that this object occupies in memory. + /// + /// @see ConstString::StaticMemorySize () + //------------------------------------------------------------------ + size_t + MemorySize () const; + + //------------------------------------------------------------------ + /// Memory map part of, or the entire contents of, a file. + /// + /// Returns a shared pointer to a data buffer that contains all or + /// part of the contents of a file. The data is memory mapped and + /// will lazily page in data from the file as memory is accessed. + /// The data that is mappped will start \a offset bytes into the + /// file, and \a length bytes will be mapped. If \a length is + /// greater than the number of bytes available in the file starting + /// at \a offset, the number of bytes will be appropriately + /// truncated. The final number of bytes that get mapped can be + /// verified using the DataBuffer::GetByteSize() function on the return + /// shared data pointer object contents. + /// + /// @param[in] offset + /// The offset in bytes from the beginning of the file where + /// memory mapping should begin. + /// + /// @param[in] length + /// The size in bytes that should be mapped starting \a offset + /// bytes into the file. If \a length is \c SIZE_MAX, map + /// as many bytes as possible. + /// + /// @return + /// A shared pointer to the memeory mapped data. This shared + /// pointer can contain a NULL DataBuffer pointer, so the contained + /// pointer must be checked prior to using it. + //------------------------------------------------------------------ + lldb::DataBufferSP + MemoryMapFileContents (off_t offset = 0, size_t length = SIZE_MAX) const; + + //------------------------------------------------------------------ + /// Read part of, or the entire contents of, a file into a heap based data buffer. + /// + /// Returns a shared pointer to a data buffer that contains all or + /// part of the contents of a file. The data copies into a heap based + /// buffer that lives in the DataBuffer shared pointer object returned. + /// The data that is cached will start \a offset bytes into the + /// file, and \a length bytes will be mapped. If \a length is + /// greater than the number of bytes available in the file starting + /// at \a offset, the number of bytes will be appropriately + /// truncated. The final number of bytes that get mapped can be + /// verified using the DataBuffer::GetByteSize() function. + /// + /// @param[in] offset + /// The offset in bytes from the beginning of the file where + /// memory mapping should begin. + /// + /// @param[in] length + /// The size in bytes that should be mapped starting \a offset + /// bytes into the file. If \a length is \c SIZE_MAX, map + /// as many bytes as possible. + /// + /// @return + /// A shared pointer to the memeory mapped data. This shared + /// pointer can contain a NULL DataBuffer pointer, so the contained + /// pointer must be checked prior to using it. + //------------------------------------------------------------------ + lldb::DataBufferSP + ReadFileContents (off_t offset = 0, size_t length = SIZE_MAX, Error *error_ptr = NULL) const; + + size_t + ReadFileContents (off_t file_offset, void *dst, size_t dst_len, Error *error_ptr) const; + + + //------------------------------------------------------------------ + /// Read the entire contents of a file as data that can be used + /// as a C string. + /// + /// Read the entire contents of a file and ensure that the data + /// is NULL terminated so it can be used as a C string. + /// + /// @return + /// A shared pointer to the data. This shared pointer can + /// contain a NULL DataBuffer pointer, so the contained pointer + /// must be checked prior to using it. + //------------------------------------------------------------------ + lldb::DataBufferSP + ReadFileContentsAsCString(Error *error_ptr = NULL); + //------------------------------------------------------------------ + /// Change the file specificed with a new path. + /// + /// Update the contents of this object with a new path. The path will + /// be split up into a directory and filename and stored as uniqued + /// string values for quick comparison and efficient memory usage. + /// + /// @param[in] path + /// A full, partial, or relative path to a file. + /// + /// @param[in] resolve_path + /// If \b true, then we will try to resolve links the path using + /// the static FileSpec::Resolve. + //------------------------------------------------------------------ + void + SetFile (const char *path, bool resolve_path); + + bool + IsResolved () const + { + return m_is_resolved; + } + + //------------------------------------------------------------------ + /// Set if the file path has been resolved or not. + /// + /// If you know a file path is already resolved and avoided passing + /// a \b true parameter for any functions that take a "bool + /// resolve_path" parameter, you can set the value manually using + /// this call to make sure we don't try and resolve it later, or try + /// and resolve a path that has already been resolved. + /// + /// @param[in] is_resolved + /// A boolean value that will replace the current value that + /// indicates if the paths in this object have been resolved. + //------------------------------------------------------------------ + void + SetIsResolved (bool is_resolved) + { + m_is_resolved = is_resolved; + } + //------------------------------------------------------------------ + /// Read the file into an array of strings, one per line. + /// + /// Opens and reads the file in this object into an array of strings, + /// one string per line of the file. Returns a boolean indicating + /// success or failure. + /// + /// @param[out] lines + /// The string array into which to read the file. + /// + /// @result + /// Returns the number of lines that were read from the file. + //------------------------------------------------------------------ + size_t + ReadFileLines (STLStringArray &lines); + + //------------------------------------------------------------------ + /// Resolves user name and links in \a src_path, and writes the output + /// to \a dst_path. Note if the path pointed to by \a src_path does not + /// exist, the contents of \a src_path will be copied to \a dst_path + /// unchanged. + /// + /// @param[in] src_path + /// Input path to be resolved. + /// + /// @param[in] dst_path + /// Buffer to store the resolved path. + /// + /// @param[in] dst_len + /// Size of the buffer pointed to by dst_path. + /// + /// @result + /// The number of characters required to write the resolved path. If the + /// resolved path doesn't fit in dst_len, dst_len-1 characters will + /// be written to \a dst_path, but the actual required length will still be returned. + //------------------------------------------------------------------ + static size_t + Resolve (const char *src_path, char *dst_path, size_t dst_len); + + //------------------------------------------------------------------ + /// Resolves the user name at the beginning of \a src_path, and writes the output + /// to \a dst_path. Note, \a src_path can contain other path components after the + /// user name, they will be copied over, and if the path doesn't start with "~" it + /// will also be copied over to \a dst_path. + /// + /// @param[in] src_path + /// Input path to be resolved. + /// + /// @param[in] dst_path + /// Buffer to store the resolved path. + /// + /// @param[in] dst_len + /// Size of the buffer pointed to by dst_path. + /// + /// @result + /// The number of characters required to write the resolved path, or 0 if + /// the user name could not be found. If the + /// resolved path doesn't fit in dst_len, dst_len-1 characters will + /// be written to \a dst_path, but the actual required length will still be returned. + //------------------------------------------------------------------ + static size_t + ResolveUsername (const char *src_path, char *dst_path, size_t dst_len); + + static size_t + ResolvePartialUsername (const char *partial_name, StringList &matches); + + enum EnumerateDirectoryResult + { + eEnumerateDirectoryResultNext, // Enumerate next entry in the current directory + eEnumerateDirectoryResultEnter, // Recurse into the current entry if it is a directory or symlink, or next if not + eEnumerateDirectoryResultExit, // Exit from the current directory at the current level. + eEnumerateDirectoryResultQuit // Stop directory enumerations at any level + }; + + typedef EnumerateDirectoryResult (*EnumerateDirectoryCallbackType) (void *baton, + FileType file_type, + const FileSpec &spec +); + + static EnumerateDirectoryResult + EnumerateDirectory (const char *dir_path, + bool find_directories, + bool find_files, + bool find_other, + EnumerateDirectoryCallbackType callback, + void *callback_baton); + +protected: + //------------------------------------------------------------------ + // Member variables + //------------------------------------------------------------------ + ConstString m_directory; ///< The uniqued directory path + ConstString m_filename; ///< The uniqued filename path + mutable bool m_is_resolved; ///< True if this path has been resolved. +}; + +//---------------------------------------------------------------------- +/// Dump a FileSpec object to a stream +//---------------------------------------------------------------------- +Stream& operator << (Stream& s, const FileSpec& f); + +} // namespace lldb_private + +#endif // #if defined(__cplusplus) +#endif // liblldb_FileSpec_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Host/Host.h b/contrib/llvm/tools/lldb/include/lldb/Host/Host.h new file mode 100644 index 00000000000..547bdd5d637 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Host/Host.h @@ -0,0 +1,502 @@ +//===-- Host.h --------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_Host_h_ +#define liblldb_Host_h_ +#if defined(__cplusplus) + +#include + +#include +#include + +#include "lldb/lldb-private.h" +#include "lldb/Core/StringList.h" + +namespace lldb_private { + +//---------------------------------------------------------------------- +/// @class Host Host.h "lldb/Host/Host.h" +/// @brief A class that provides host computer information. +/// +/// Host is a class that answers information about the host operating +/// system. +//---------------------------------------------------------------------- +class Host +{ +public: + typedef bool (*MonitorChildProcessCallback) (void *callback_baton, + lldb::pid_t pid, + bool exited, + int signal, // Zero for no signal + int status); // Exit value of process if signal is zero + + //------------------------------------------------------------------ + /// Start monitoring a child process. + /// + /// Allows easy monitoring of child processes. \a callback will be + /// called when the child process exits or if it gets a signal. The + /// callback will only be called with signals if \a monitor_signals + /// is \b true. \a callback will usually be called from another + /// thread so the callback function must be thread safe. + /// + /// When the callback gets called, the return value indicates if + /// minotoring should stop. If \b true is returned from \a callback + /// the information will be removed. If \b false is returned then + /// monitoring will continue. If the child process exits, the + /// monitoring will automatically stop after the callback returned + /// ragardless of the callback return value. + /// + /// @param[in] callback + /// A function callback to call when a child receives a signal + /// (if \a monitor_signals is true) or a child exits. + /// + /// @param[in] callback_baton + /// A void * of user data that will be pass back when + /// \a callback is called. + /// + /// @param[in] pid + /// The process ID of a child process to monitor, -1 for all + /// processes. + /// + /// @param[in] monitor_signals + /// If \b true the callback will get called when the child + /// process gets a signal. If \b false, the callback will only + /// get called if the child process exits. + /// + /// @return + /// A thread handle that can be used to cancel the thread that + /// was spawned to monitor \a pid. + /// + /// @see static void Host::StopMonitoringChildProcess (uint32_t) + //------------------------------------------------------------------ + static lldb::thread_t + StartMonitoringChildProcess (MonitorChildProcessCallback callback, + void *callback_baton, + lldb::pid_t pid, + bool monitor_signals); + + //------------------------------------------------------------------ + /// Get the host page size. + /// + /// @return + /// The size in bytes of a VM page on the host system. + //------------------------------------------------------------------ + static size_t + GetPageSize(); + + //------------------------------------------------------------------ + /// Returns the endianness of the host system. + /// + /// @return + /// Returns the endianness of the host system as a lldb::ByteOrder + /// enumeration. + //------------------------------------------------------------------ + static lldb::ByteOrder + GetByteOrder (); + + //------------------------------------------------------------------ + /// Returns the number of CPUs on this current host. + /// + /// @return + /// Number of CPUs on this current host, or zero if the number + /// of CPUs can't be determined on this host. + //------------------------------------------------------------------ + static uint32_t + GetNumberCPUS (); + + static bool + GetOSVersion (uint32_t &major, + uint32_t &minor, + uint32_t &update); + + static bool + GetOSBuildString (std::string &s); + + static bool + GetOSKernelDescription (std::string &s); + + static bool + GetHostname (std::string &s); + + static const char * + GetUserName (uint32_t uid, std::string &user_name); + + static const char * + GetGroupName (uint32_t gid, std::string &group_name); + + static uint32_t + GetUserID (); + + static uint32_t + GetGroupID (); + + static uint32_t + GetEffectiveUserID (); + + static uint32_t + GetEffectiveGroupID (); + + + enum SystemLogType + { + eSystemLogWarning, + eSystemLogError + }; + + static void + SystemLog (SystemLogType type, const char *format, ...) __attribute__ ((format (printf, 2, 3))); + + static void + SystemLog (SystemLogType type, const char *format, va_list args); + + //------------------------------------------------------------------ + /// Gets the host architecture. + /// + /// @return + /// A const architecture object that represents the host + /// architecture. + //------------------------------------------------------------------ + enum SystemDefaultArchitecture + { + eSystemDefaultArchitecture, // The overall default architecture that applications will run on this host + eSystemDefaultArchitecture32, // If this host supports 32 bit programs, return the default 32 bit arch + eSystemDefaultArchitecture64 // If this host supports 64 bit programs, return the default 64 bit arch + }; + + static const ArchSpec & + GetArchitecture (SystemDefaultArchitecture arch_kind = eSystemDefaultArchitecture); + + //------------------------------------------------------------------ + /// Gets the host vendor string. + /// + /// @return + /// A const string object containing the host vendor name. + //------------------------------------------------------------------ + static const ConstString & + GetVendorString (); + + //------------------------------------------------------------------ + /// Gets the host Operating System (OS) string. + /// + /// @return + /// A const string object containing the host OS name. + //------------------------------------------------------------------ + static const ConstString & + GetOSString (); + + //------------------------------------------------------------------ + /// Gets the host target triple as a const string. + /// + /// @return + /// A const string object containing the host target triple. + //------------------------------------------------------------------ + static const ConstString & + GetTargetTriple (); + + //------------------------------------------------------------------ + /// Get the process ID for the calling process. + /// + /// @return + /// The process ID for the current process. + //------------------------------------------------------------------ + static lldb::pid_t + GetCurrentProcessID (); + + //------------------------------------------------------------------ + /// Get the thread ID for the calling thread in the current process. + /// + /// @return + /// The thread ID for the calling thread in the current process. + //------------------------------------------------------------------ + static lldb::tid_t + GetCurrentThreadID (); + + //------------------------------------------------------------------ + /// Get the thread token (the one returned by ThreadCreate when the thread was created) for the + /// calling thread in the current process. + /// + /// @return + /// The thread token for the calling thread in the current process. + //------------------------------------------------------------------ + static lldb::thread_t + GetCurrentThread (); + + static const char * + GetSignalAsCString (int signo); + + static void + WillTerminate (); + //------------------------------------------------------------------ + /// Host specific thread created function call. + /// + /// This function call lets the current host OS do any thread + /// specific initialization that it needs, including naming the + /// thread. No cleanup routine is exptected to be called + /// + /// @param[in] name + /// The current thread's name in the current process. + //------------------------------------------------------------------ + static void + ThreadCreated (const char *name); + + static lldb::thread_t + ThreadCreate (const char *name, + lldb::thread_func_t function, + lldb::thread_arg_t thread_arg, + Error *err); + + static bool + ThreadCancel (lldb::thread_t thread, + Error *error); + + static bool + ThreadDetach (lldb::thread_t thread, + Error *error); + static bool + ThreadJoin (lldb::thread_t thread, + lldb::thread_result_t *thread_result_ptr, + Error *error); + + //------------------------------------------------------------------ + /// Gets the name of a thread in a process. + /// + /// This function will name a thread in a process using it's own + /// thread name pool, and also will attempt to set a thread name + /// using any supported host OS APIs. + /// + /// @param[in] pid + /// The process ID in which we are trying to get the name of + /// a thread. + /// + /// @param[in] tid + /// The thread ID for which we are trying retrieve the name of. + /// + /// @return + /// A std::string containing the thread name. + //------------------------------------------------------------------ + static std::string + GetThreadName (lldb::pid_t pid, lldb::tid_t tid); + + //------------------------------------------------------------------ + /// Sets the name of a thread in the current process. + /// + /// @param[in] pid + /// The process ID in which we are trying to name a thread. + /// + /// @param[in] tid + /// The thread ID which we are trying to name. + /// + /// @param[in] name + /// The current thread's name in the current process to \a name. + /// + /// @return + /// \b true if the thread name was able to be set, \b false + /// otherwise. + //------------------------------------------------------------------ + static bool + SetThreadName (lldb::pid_t pid, lldb::tid_t tid, const char *name); + + //------------------------------------------------------------------ + /// Sets a shortened name of a thread in the current process. + /// + /// @param[in] pid + /// The process ID in which we are trying to name a thread. + /// + /// @param[in] tid + /// The thread ID which we are trying to name. + /// + /// @param[in] name + /// The current thread's name in the current process to \a name. + /// + /// @param[in] len + /// The maximum length for the thread's shortened name. + /// + /// @return + /// \b true if the thread name was able to be set, \b false + /// otherwise. + static bool + SetShortThreadName (lldb::pid_t pid, lldb::tid_t tid, const char *name, size_t len); + + //------------------------------------------------------------------ + /// Gets the FileSpec of the current process (the process that + /// that is running the LLDB code). + /// + /// @return + /// \b A file spec with the program name. + //------------------------------------------------------------------ + static FileSpec + GetProgramFileSpec (); + + //------------------------------------------------------------------ + /// Given an address in the current process (the process that + /// is running the LLDB code), return the name of the module that + /// it comes from. This can be useful when you need to know the + /// path to the shared library that your code is running in for + /// loading resources that are relative to your binary. + /// + /// @param[in] host_addr + /// The pointer to some code in the current process. + /// + /// @return + /// \b A file spec with the module that contains \a host_addr, + /// which may be invalid if \a host_addr doesn't fall into + /// any valid module address range. + //------------------------------------------------------------------ + static FileSpec + GetModuleFileSpecForHostAddress (const void *host_addr); + + + + //------------------------------------------------------------------ + /// If you have an executable that is in a bundle and want to get + /// back to the bundle directory from the path itself, this + /// function will change a path to a file within a bundle to the + /// bundle directory itself. + /// + /// @param[in] file + /// A file spec that might point to a file in a bundle. + /// + /// @param[out] bundle_directory + /// An object will be filled in with the bundle directory for + /// the bundle when \b true is returned. Otherwise \a file is + /// left untouched and \b false is returned. + /// + /// @return + /// \b true if \a file was resolved in \a bundle_directory, + /// \b false otherwise. + //------------------------------------------------------------------ + static bool + GetBundleDirectory (const FileSpec &file, FileSpec &bundle_directory); + + //------------------------------------------------------------------ + /// When executable files may live within a directory, where the + /// directory represents an executable bundle (like the MacOSX + /// app bundles), the locate the executable within the containing + /// bundle. + /// + /// @param[in,out] file + /// A file spec that currently points to the bundle that will + /// be filled in with the executable path within the bundle + /// if \b true is returned. Otherwise \a file is left untouched. + /// + /// @return + /// \b true if \a file was resolved, \b false if this function + /// was not able to resolve the path. + //------------------------------------------------------------------ + static bool + ResolveExecutableInBundle (FileSpec &file); + + //------------------------------------------------------------------ + /// Find a resource files that are related to LLDB. + /// + /// Operating systems have different ways of storing shared + /// libraries and related resources. This function abstracts the + /// access to these paths. + /// + /// @param[in] path_type + /// The type of LLDB resource path you are looking for. If the + /// enumeration ends with "Dir", then only the \a file_spec's + /// directory member gets filled in. + /// + /// @param[in] file_spec + /// A file spec that gets filled in with the appriopriate path. + /// + /// @return + /// \b true if \a resource_path was resolved, \a false otherwise. + //------------------------------------------------------------------ + static bool + GetLLDBPath (PathType path_type, + FileSpec &file_spec); + + //------------------------------------------------------------------ + /// Set a string that can be displayed if host application crashes. + /// + /// Some operating systems have the ability to print a description + /// for shared libraries when a program crashes. If the host OS + /// supports such a mechanism, it should be implemented to help + /// with crash triage. + /// + /// @param[in] format + /// A printf format that will be used to form a new crash + /// description string. + //------------------------------------------------------------------ + static void + SetCrashDescriptionWithFormat (const char *format, ...) __attribute__ ((format (printf, 1, 2))); + + static void + SetCrashDescription (const char *description); + + static uint32_t + FindProcesses (const ProcessInstanceInfoMatch &match_info, + ProcessInstanceInfoList &proc_infos); + + typedef std::map TidMap; + typedef std::pair TidPair; + static bool + FindProcessThreads (const lldb::pid_t pid, TidMap &tids_to_attach); + + static bool + GetProcessInfo (lldb::pid_t pid, ProcessInstanceInfo &proc_info); + + static lldb::pid_t + LaunchApplication (const FileSpec &app_file_spec); + + static Error + LaunchProcess (ProcessLaunchInfo &launch_info); + + static Error + RunShellCommand (const char *command, // Shouldn't be NULL + const char *working_dir, // Pass NULL to use the current working directory + int *status_ptr, // Pass NULL if you don't want the process exit status + int *signo_ptr, // Pass NULL if you don't want the signal that caused the process to exit + std::string *command_output, // Pass NULL if you don't want the command output + uint32_t timeout_sec, + const char *shell = "/bin/bash"); + + static lldb::DataBufferSP + GetAuxvData (lldb_private::Process *process); + + static lldb::TargetSP + GetDummyTarget (Debugger &debugger); + + static bool + OpenFileInExternalEditor (const FileSpec &file_spec, + uint32_t line_no); + + static void + Backtrace (Stream &strm, uint32_t max_frames); + + static size_t + GetEnvironment (StringList &env); + + enum DynamicLibraryOpenOptions + { + eDynamicLibraryOpenOptionLazy = (1u << 0), // Lazily resolve symbols in this dynamic library + eDynamicLibraryOpenOptionLocal = (1u << 1), // Only open a shared library with local access (hide it from the global symbol namespace) + eDynamicLibraryOpenOptionLimitGetSymbol = (1u << 2) // DynamicLibraryGetSymbol calls on this handle will only return matches from this shared library + }; + static void * + DynamicLibraryOpen (const FileSpec &file_spec, + uint32_t options, + Error &error); + + static Error + DynamicLibraryClose (void *dynamic_library_handle); + + static void * + DynamicLibraryGetSymbol (void *dynamic_library_handle, + const char *symbol_name, + Error &error); +}; + +} // namespace lldb_private + +#endif // #if defined(__cplusplus) +#endif // liblldb_Host_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Host/Mutex.h b/contrib/llvm/tools/lldb/include/lldb/Host/Mutex.h new file mode 100644 index 00000000000..63f759efe36 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Host/Mutex.h @@ -0,0 +1,312 @@ +//===-- Mutex.h -------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_Mutex_h_ +#define liblldb_Mutex_h_ +#if defined(__cplusplus) + +#include +#include + +#ifdef LLDB_CONFIGURATION_DEBUG +#include +#endif + +namespace lldb_private { + +//---------------------------------------------------------------------- +/// @class Mutex Mutex.h "lldb/Host/Mutex.h" +/// @brief A C++ wrapper class for pthread mutexes. +//---------------------------------------------------------------------- +class Mutex +{ +public: + friend class Locker; + friend class Condition; + + enum Type + { + eMutexTypeNormal, ///< Mutex that can't recursively entered by the same thread + eMutexTypeRecursive ///< Mutex can be recursively entered by the same thread + }; + + //------------------------------------------------------------------ + /// @class Mutex::Locker + /// + /// A scoped locking class that allows a variety of pthread mutex + /// objects to have a mutex locked when an Mutex::Locker + /// object is created, and unlocked when it goes out of scope or + /// when the Mutex::Locker::Reset(pthread_mutex_t *) + /// is called. This provides an exception safe way to lock a mutex + /// in a scope. + //------------------------------------------------------------------ + class Locker + { + public: + //-------------------------------------------------------------- + /// Default constructor. + /// + /// This will create a scoped mutex locking object that doesn't + /// have a mutex to lock. One will need to be provided using the + /// Mutex::Locker::Reset(pthread_mutex_t *) method. + /// + /// @see Mutex::Locker::Reset(pthread_mutex_t *) + //-------------------------------------------------------------- + Locker(); + + //-------------------------------------------------------------- + /// Constructor with a Mutex object. + /// + /// This will create a scoped mutex locking object that extracts + /// the mutex owned by \a m and locks it. + /// + /// @param[in] m + /// An instance of a Mutex object that contains a + /// valid mutex object. + //-------------------------------------------------------------- + Locker(Mutex& m); + + //-------------------------------------------------------------- + /// Constructor with a Mutex object pointer. + /// + /// This will create a scoped mutex locking object that extracts + /// the mutex owned by a m and locks it. + /// + /// @param[in] m + /// A pointer to instance of a Mutex object that + /// contains a valid mutex object. + //-------------------------------------------------------------- + Locker(Mutex* m); + + //-------------------------------------------------------------- + /// Desstructor + /// + /// Unlocks any valid pthread_mutex_t that this object may + /// contain. + //-------------------------------------------------------------- + ~Locker(); + + //-------------------------------------------------------------- + /// Change the contained mutex. + /// + /// Unlock the current mutex in this object (if it contains a + /// valid mutex) and lock the new \a mutex object if it is + /// non-NULL. + //-------------------------------------------------------------- + void + Lock (Mutex &mutex); + + void + Lock (Mutex *mutex) + { + if (mutex) + Lock(*mutex); + } + + //-------------------------------------------------------------- + /// Change the contained mutex only if the mutex can be locked. + /// + /// Unlock the current mutex in this object (if it contains a + /// valid mutex) and try to lock \a mutex. If \a mutex can be + /// locked this object will take ownership of the lock and will + /// unlock it when it goes out of scope or Reset or TryLock are + /// called again. If the mutex is already locked, this object + /// will not take ownership of the mutex. + /// + /// @return + /// Returns \b true if the lock was aquired and the this + /// object will unlock the mutex when it goes out of scope, + /// returns \b false otherwise. + //-------------------------------------------------------------- + bool + TryLock (Mutex &mutex, const char *failure_message = NULL); + + bool + TryLock (Mutex *mutex, const char *failure_message = NULL) + { + if (mutex) + return TryLock(*mutex, failure_message); + else + return false; + } + + void + Unlock (); + + protected: + //-------------------------------------------------------------- + /// Member variables + //-------------------------------------------------------------- + Mutex *m_mutex_ptr; + + private: + Locker(const Locker&); + const Locker& operator=(const Locker&); + }; + + + //------------------------------------------------------------------ + /// Default constructor. + /// + /// Creates a pthread mutex with no attributes. + //------------------------------------------------------------------ + Mutex(); + + //------------------------------------------------------------------ + /// Default constructor. + /// + /// Creates a pthread mutex with \a type as the mutex type. + /// Valid values for \a type include: + /// @li Mutex::Type::eMutexTypeNormal + /// @li Mutex::Type::eMutexTypeRecursive + /// + /// @param[in] type + /// The type of the mutex. + /// + /// @see ::pthread_mutexattr_settype() + //------------------------------------------------------------------ + Mutex(Mutex::Type type); + + //------------------------------------------------------------------ + /// Destructor. + /// + /// Destroys the mutex owned by this object. + //------------------------------------------------------------------ +#ifdef LLDB_CONFIGURATION_DEBUG + virtual +#endif + ~Mutex(); + + //------------------------------------------------------------------ + /// Lock the mutex. + /// + /// Locks the mutex owned by this object. If the mutex is already + /// locked, the calling thread will block until the mutex becomes + /// available. + /// + /// @return + /// The error code from \c pthread_mutex_lock(). + //------------------------------------------------------------------ +#ifdef LLDB_CONFIGURATION_DEBUG + virtual +#endif + int + Lock(); + + //------------------------------------------------------------------ + /// Try to lock the mutex. + /// + /// Attempts to lock the mutex owned by this object without blocking. + /// If the mutex is already locked, TryLock() will not block waiting + /// for the mutex, but will return an error condition. + /// + /// @return + /// The error code from \c pthread_mutex_trylock(). + //------------------------------------------------------------------ +#ifdef LLDB_CONFIGURATION_DEBUG + virtual +#endif + int + TryLock(const char *failure_message = NULL); + + //------------------------------------------------------------------ + /// Unlock the mutex. + /// + /// If the current thread holds the lock on the owned mutex, then + /// Unlock() will unlock the mutex. Calling Unlock() on this object + /// when the calling thread does not hold the lock will result in + /// undefined behavior. + /// + /// @return + /// The error code from \c pthread_mutex_unlock(). + //------------------------------------------------------------------ +#ifdef LLDB_CONFIGURATION_DEBUG + virtual +#endif + int + Unlock(); + +protected: + //------------------------------------------------------------------ + // Member variables + //------------------------------------------------------------------ + // TODO: Hide the mutex in the implementation file in case we ever need to port to an + // architecture that doesn't have pthread mutexes. + pthread_mutex_t m_mutex; ///< The pthread mutex object. + +private: + //------------------------------------------------------------------ + /// Mutex get accessor. + /// + /// @return + /// A pointer to the pthread mutex object owned by this object. + //------------------------------------------------------------------ + pthread_mutex_t * + GetMutex(); + + Mutex(const Mutex&); + const Mutex& operator=(const Mutex&); +}; + +#ifdef LLDB_CONFIGURATION_DEBUG +class TrackingMutex : public Mutex +{ +public: + TrackingMutex() : Mutex() {} + TrackingMutex(Mutex::Type type) : Mutex (type) {} + + virtual + ~TrackingMutex() {} + + virtual int + Unlock (); + + virtual int + TryLock (const char *failure_message = NULL) + { + int return_value = Mutex::TryLock(); + if (return_value != 0 && failure_message != NULL) + { + m_failure_message.assign(failure_message); + m_thread_that_tried = pthread_self(); + } + return return_value; + } + +protected: + pthread_t m_thread_that_tried; + std::string m_failure_message; +}; + +class LoggingMutex : public Mutex +{ +public: + LoggingMutex() : Mutex(),m_locked(false) {} + LoggingMutex(Mutex::Type type) : Mutex (type),m_locked(false) {} + + virtual + ~LoggingMutex() {} + + virtual int + Lock (); + + virtual int + Unlock (); + + virtual int + TryLock (const char *failure_message = NULL); +protected: + bool m_locked; +}; +#endif + +} // namespace lldb_private + +#endif // #if defined(__cplusplus) +#endif diff --git a/contrib/llvm/tools/lldb/include/lldb/Host/Predicate.h b/contrib/llvm/tools/lldb/include/lldb/Host/Predicate.h new file mode 100644 index 00000000000..6ddf20b67c6 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Host/Predicate.h @@ -0,0 +1,509 @@ +//===-- Predicate.h ---------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_Predicate_h_ +#define liblldb_Predicate_h_ +#if defined(__cplusplus) + +#include "lldb/Host/Mutex.h" +#include "lldb/Host/Condition.h" +#include +#include + +//#define DB_PTHREAD_LOG_EVENTS + +//---------------------------------------------------------------------- +/// Enumerations for broadcasting. +//---------------------------------------------------------------------- +namespace lldb_private { + +typedef enum +{ + eBroadcastNever, ///< No broadcast will be sent when the value is modified. + eBroadcastAlways, ///< Always send a broadcast when the value is modified. + eBroadcastOnChange ///< Only broadcast if the value changes when the value is modified. + +} PredicateBroadcastType; + +//---------------------------------------------------------------------- +/// @class Predicate Predicate.h "lldb/Host/Predicate.h" +/// @brief A C++ wrapper class for providing threaded access to a value +/// of type T. +/// +/// A templatized class that provides multi-threaded access to a value +/// of type T. Threads can efficiently wait for bits within T to be set +/// or reset, or wait for T to be set to be equal/not equal to a +/// specified values. +//---------------------------------------------------------------------- +template +class Predicate +{ +public: + + //------------------------------------------------------------------ + /// Default constructor. + /// + /// Initializes the mutex, condition and value with their default + /// constructors. + //------------------------------------------------------------------ + Predicate () : + m_value(), + m_mutex(), + m_condition() + { + } + + //------------------------------------------------------------------ + /// Construct with initial T value \a initial_value. + /// + /// Initializes the mutex and condition with their default + /// constructors, and initializes the value with \a initial_value. + /// + /// @param[in] initial_value + /// The initial value for our T object. + //------------------------------------------------------------------ + Predicate (T initial_value) : + m_value(initial_value), + m_mutex(), + m_condition() + { + } + + //------------------------------------------------------------------ + /// Destructor. + /// + /// Destrory the condition, mutex, and T objects. + //------------------------------------------------------------------ + ~Predicate () + { + } + + + //------------------------------------------------------------------ + /// Value get accessor. + /// + /// Copies the current \a m_value in a thread safe manor and returns + /// the copied value. + /// + /// @return + /// A copy of the current value. + //------------------------------------------------------------------ + T + GetValue () const + { + Mutex::Locker locker(m_mutex); + T value = m_value; + return value; + } + + //------------------------------------------------------------------ + /// Value set accessor. + /// + /// Set the contained \a m_value to \a new_value in a thread safe + /// way and broadcast if needed. + /// + /// @param[in] value + /// The new value to set. + /// + /// @param[in] broadcast_type + /// A value indicating when and if to broadast. See the + /// PredicateBroadcastType enumeration for details. + /// + /// @see Predicate::Broadcast() + //------------------------------------------------------------------ + void + SetValue (T value, PredicateBroadcastType broadcast_type) + { + Mutex::Locker locker(m_mutex); +#ifdef DB_PTHREAD_LOG_EVENTS + printf("%s (value = 0x%8.8x, broadcast_type = %i)\n", __FUNCTION__, value, broadcast_type); +#endif + const T old_value = m_value; + m_value = value; + + Broadcast(old_value, broadcast_type); + } + + //------------------------------------------------------------------ + /// Set some bits in \a m_value. + /// + /// Logically set the bits \a bits in the contained \a m_value in a + /// thread safe way and broadcast if needed. + /// + /// @param[in] bits + /// The bits to set in \a m_value. + /// + /// @param[in] broadcast_type + /// A value indicating when and if to broadast. See the + /// PredicateBroadcastType enumeration for details. + /// + /// @see Predicate::Broadcast() + //------------------------------------------------------------------ + void + SetValueBits (T bits, PredicateBroadcastType broadcast_type) + { + Mutex::Locker locker(m_mutex); +#ifdef DB_PTHREAD_LOG_EVENTS + printf("%s (bits = 0x%8.8x, broadcast_type = %i)\n", __FUNCTION__, bits, broadcast_type); +#endif + const T old_value = m_value; + m_value |= bits; + + Broadcast(old_value, broadcast_type); + } + + //------------------------------------------------------------------ + /// Reset some bits in \a m_value. + /// + /// Logically reset (clear) the bits \a bits in the contained + /// \a m_value in a thread safe way and broadcast if needed. + /// + /// @param[in] bits + /// The bits to clear in \a m_value. + /// + /// @param[in] broadcast_type + /// A value indicating when and if to broadast. See the + /// PredicateBroadcastType enumeration for details. + /// + /// @see Predicate::Broadcast() + //------------------------------------------------------------------ + void + ResetValueBits (T bits, PredicateBroadcastType broadcast_type) + { + Mutex::Locker locker(m_mutex); +#ifdef DB_PTHREAD_LOG_EVENTS + printf("%s (bits = 0x%8.8x, broadcast_type = %i)\n", __FUNCTION__, bits, broadcast_type); +#endif + const T old_value = m_value; + m_value &= ~bits; + + Broadcast(old_value, broadcast_type); + } + + //------------------------------------------------------------------ + /// Wait for bits to be set in \a m_value. + /// + /// Waits in a thread safe way for any bits in \a bits to get + /// logically set in \a m_value. If any bits are already set in + /// \a m_value, this function will return without waiting. + /// + /// It is possible for the value to be changed between the time + /// the bits are set and the time the waiting thread wakes up. + /// If the bits are no longer set when the waiting thread wakes + /// up, it will go back into a wait state. It may be necessary + /// for the calling code to use additional thread synchronization + /// methods to detect transitory states. + /// + /// @param[in] bits + /// The bits we are waiting to be set in \a m_value. + /// + /// @param[in] abstime + /// If non-NULL, the absolute time at which we should stop + /// waiting, else wait an infinite amount of time. + /// + /// @return + /// Any bits of the requested bits that actually were set within + /// the time specified. Zero if a timeout or unrecoverable error + /// occurred. + //------------------------------------------------------------------ + T + WaitForSetValueBits (T bits, const TimeValue *abstime = NULL) + { + int err = 0; + // pthread_cond_timedwait() or pthread_cond_wait() will atomically + // unlock the mutex and wait for the condition to be set. When either + // function returns, they will re-lock the mutex. We use an auto lock/unlock + // class (Mutex::Locker) to allow us to return at any point in this + // function and not have to worry about unlocking the mutex. + Mutex::Locker locker(m_mutex); +#ifdef DB_PTHREAD_LOG_EVENTS + printf("%s (bits = 0x%8.8x, abstime = %p), m_value = 0x%8.8x\n", __FUNCTION__, bits, abstime, m_value); +#endif + while (err == 0 && ((m_value & bits) == 0)) + { + err = m_condition.Wait (m_mutex, abstime); + } +#ifdef DB_PTHREAD_LOG_EVENTS + printf("%s (bits = 0x%8.8x), m_value = 0x%8.8x, returning 0x%8.8x\n", __FUNCTION__, bits, m_value, m_value & bits); +#endif + + return m_value & bits; + } + + //------------------------------------------------------------------ + /// Wait for bits to be reset in \a m_value. + /// + /// Waits in a thread safe way for any bits in \a bits to get + /// logically reset in \a m_value. If all bits are already reset in + /// \a m_value, this function will return without waiting. + /// + /// It is possible for the value to be changed between the time + /// the bits are reset and the time the waiting thread wakes up. + /// If the bits are no set when the waiting thread wakes up, it will + /// go back into a wait state. It may be necessary for the calling + /// code to use additional thread synchronization methods to detect + /// transitory states. + /// + /// @param[in] bits + /// The bits we are waiting to be reset in \a m_value. + /// + /// @param[in] abstime + /// If non-NULL, the absolute time at which we should stop + /// waiting, else wait an infinite amount of time. + /// + /// @return + /// Zero on successful waits, or non-zero if a timeout or + /// unrecoverable error occurs. + //------------------------------------------------------------------ + T + WaitForResetValueBits (T bits, const TimeValue *abstime = NULL) + { + int err = 0; + + // pthread_cond_timedwait() or pthread_cond_wait() will atomically + // unlock the mutex and wait for the condition to be set. When either + // function returns, they will re-lock the mutex. We use an auto lock/unlock + // class (Mutex::Locker) to allow us to return at any point in this + // function and not have to worry about unlocking the mutex. + Mutex::Locker locker(m_mutex); + +#ifdef DB_PTHREAD_LOG_EVENTS + printf("%s (bits = 0x%8.8x, abstime = %p), m_value = 0x%8.8x\n", __FUNCTION__, bits, abstime, m_value); +#endif + while (err == 0 && ((m_value & bits) != 0)) + { + err = m_condition.Wait (m_mutex, abstime); + } + +#ifdef DB_PTHREAD_LOG_EVENTS + printf("%s (bits = 0x%8.8x), m_value = 0x%8.8x, returning 0x%8.8x\n", __FUNCTION__, bits, m_value, m_value & bits); +#endif + return m_value & bits; + } + + //------------------------------------------------------------------ + /// Wait for \a m_value to be equal to \a value. + /// + /// Waits in a thread safe way for \a m_value to be equal to \a + /// value. If \a m_value is already equal to \a value, this + /// function will return without waiting. + /// + /// It is possible for the value to be changed between the time + /// the value is set and the time the waiting thread wakes up. + /// If the value no longer matches the requested value when the + /// waiting thread wakes up, it will go back into a wait state. It + /// may be necessary for the calling code to use additional thread + /// synchronization methods to detect transitory states. + /// + /// @param[in] value + /// The value we want \a m_value to be equal to. + /// + /// @param[in] abstime + /// If non-NULL, the absolute time at which we should stop + /// waiting, else wait an infinite amount of time. + /// + /// @param[out] timed_out + /// If not null, set to true if we return because of a time out, + /// and false if the value was set. + /// + /// @return + /// @li \b true if the \a m_value is equal to \a value + /// @li \b false otherwise + //------------------------------------------------------------------ + bool + WaitForValueEqualTo (T value, const TimeValue *abstime = NULL, bool *timed_out = NULL) + { + int err = 0; + // pthread_cond_timedwait() or pthread_cond_wait() will atomically + // unlock the mutex and wait for the condition to be set. When either + // function returns, they will re-lock the mutex. We use an auto lock/unlock + // class (Mutex::Locker) to allow us to return at any point in this + // function and not have to worry about unlocking the mutex. + Mutex::Locker locker(m_mutex); + +#ifdef DB_PTHREAD_LOG_EVENTS + printf("%s (value = 0x%8.8x, abstime = %p), m_value = 0x%8.8x\n", __FUNCTION__, value, abstime, m_value); +#endif + if (timed_out) + *timed_out = false; + + while (err == 0 && m_value != value) + { + err = m_condition.Wait (m_mutex, abstime, timed_out); + } + + return m_value == value; + } + + //------------------------------------------------------------------ + /// Wait for \a m_value to be equal to \a value and then set it to + /// a new value. + /// + /// Waits in a thread safe way for \a m_value to be equal to \a + /// value and then sets \a m_value to \a new_value. If \a m_value + /// is already equal to \a value, this function will immediately + /// set \a m_value to \a new_value and return without waiting. + /// + /// It is possible for the value to be changed between the time + /// the value is set and the time the waiting thread wakes up. + /// If the value no longer matches the requested value when the + /// waiting thread wakes up, it will go back into a wait state. It + /// may be necessary for the calling code to use additional thread + /// synchronization methods to detect transitory states. + /// + /// @param[in] value + /// The value we want \a m_value to be equal to. + /// + /// @param[in] new_value + /// The value to which \a m_value will be set if \b true is + /// returned. + /// + /// @param[in] abstime + /// If non-NULL, the absolute time at which we should stop + /// waiting, else wait an infinite amount of time. + /// + /// @param[out] timed_out + /// If not null, set to true if we return because of a time out, + /// and false if the value was set. + /// + /// @return + /// @li \b true if the \a m_value became equal to \a value + /// @li \b false otherwise + //------------------------------------------------------------------ + bool + WaitForValueEqualToAndSetValueTo (T wait_value, T new_value, const TimeValue *abstime = NULL, bool *timed_out = NULL) + { + int err = 0; + // pthread_cond_timedwait() or pthread_cond_wait() will atomically + // unlock the mutex and wait for the condition to be set. When either + // function returns, they will re-lock the mutex. We use an auto lock/unlock + // class (Mutex::Locker) to allow us to return at any point in this + // function and not have to worry about unlocking the mutex. + Mutex::Locker locker(m_mutex); + +#ifdef DB_PTHREAD_LOG_EVENTS + printf("%s (wait_value = 0x%8.8x, new_value = 0x%8.8x, abstime = %p), m_value = 0x%8.8x\n", __FUNCTION__, wait_value, new_value, abstime, m_value); +#endif + if (timed_out) + *timed_out = false; + + while (err == 0 && m_value != wait_value) + { + err = m_condition.Wait (m_mutex, abstime, timed_out); + } + + if (m_value == wait_value) + { + m_value = new_value; + return true; + } + + return false; + } + + + //------------------------------------------------------------------ + /// Wait for \a m_value to not be equal to \a value. + /// + /// Waits in a thread safe way for \a m_value to not be equal to \a + /// value. If \a m_value is already not equal to \a value, this + /// function will return without waiting. + /// + /// It is possible for the value to be changed between the time + /// the value is set and the time the waiting thread wakes up. + /// If the value is equal to the test value when the waiting thread + /// wakes up, it will go back into a wait state. It may be + /// necessary for the calling code to use additional thread + /// synchronization methods to detect transitory states. + /// + /// @param[in] value + /// The value we want \a m_value to not be equal to. + /// + /// @param[out] new_value + /// The new value if \b true is returned. + /// + /// @param[in] abstime + /// If non-NULL, the absolute time at which we should stop + /// waiting, else wait an infinite amount of time. + /// + /// @return + /// @li \b true if the \a m_value is equal to \a value + /// @li \b false otherwise + //------------------------------------------------------------------ + bool + WaitForValueNotEqualTo (T value, T &new_value, const TimeValue *abstime = NULL) + { + int err = 0; + // pthread_cond_timedwait() or pthread_cond_wait() will atomically + // unlock the mutex and wait for the condition to be set. When either + // function returns, they will re-lock the mutex. We use an auto lock/unlock + // class (Mutex::Locker) to allow us to return at any point in this + // function and not have to worry about unlocking the mutex. + Mutex::Locker locker(m_mutex); +#ifdef DB_PTHREAD_LOG_EVENTS + printf("%s (value = 0x%8.8x, abstime = %p), m_value = 0x%8.8x\n", __FUNCTION__, value, abstime, m_value); +#endif + while (err == 0 && m_value == value) + { + err = m_condition.Wait (m_mutex, abstime); + } + + if (m_value != value) + { + new_value = m_value; + return true; + } + return false; + } + +protected: + //---------------------------------------------------------------------- + // pthread condition and mutex variable to controll access and allow + // blocking between the main thread and the spotlight index thread. + //---------------------------------------------------------------------- + T m_value; ///< The templatized value T that we are protecting access to + mutable Mutex m_mutex; ///< The mutex to use when accessing the data + Condition m_condition; ///< The pthread condition variable to use for signaling that data available or changed. + +private: + + //------------------------------------------------------------------ + /// Broadcast if needed. + /// + /// Check to see if we need to broadcast to our condition variable + /// depedning on the \a old_value and on the \a broadcast_type. + /// + /// If \a broadcast_type is eBroadcastNever, no broadcast will be + /// sent. + /// + /// If \a broadcast_type is eBroadcastAlways, the condition variable + /// will always be broadcast. + /// + /// If \a broadcast_type is eBroadcastOnChange, the condition + /// variable be broadcast if the owned value changes. + //------------------------------------------------------------------ + void + Broadcast (T old_value, PredicateBroadcastType broadcast_type) + { + bool broadcast = (broadcast_type == eBroadcastAlways) || ((broadcast_type == eBroadcastOnChange) && old_value != m_value); +#ifdef DB_PTHREAD_LOG_EVENTS + printf("%s (old_value = 0x%8.8x, broadcast_type = %i) m_value = 0x%8.8x, broadcast = %u\n", __FUNCTION__, old_value, broadcast_type, m_value, broadcast); +#endif + if (broadcast) + m_condition.Broadcast(); + } + + + DISALLOW_COPY_AND_ASSIGN(Predicate); +}; + +} // namespace lldb_private + +#endif // #if defined(__cplusplus) +#endif // #ifndef liblldb_Predicate_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Host/ProcessRunLock.h b/contrib/llvm/tools/lldb/include/lldb/Host/ProcessRunLock.h new file mode 100644 index 00000000000..f563be73fce --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Host/ProcessRunLock.h @@ -0,0 +1,165 @@ +//===-- ProcessRunLock.h ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ProcessRunLock_h_ +#define liblldb_ProcessRunLock_h_ +#if defined(__cplusplus) + +#include "lldb/Host/Mutex.h" +#include "lldb/Host/Condition.h" +#include +#include +#include + +//---------------------------------------------------------------------- +/// Enumerations for broadcasting. +//---------------------------------------------------------------------- +namespace lldb_private { + +//---------------------------------------------------------------------- +/// @class ProcessRunLock ProcessRunLock.h "lldb/Host/ProcessRunLock.h" +/// @brief A class used to prevent the process from starting while other +/// threads are accessing its data, and prevent access to its data while +/// it is running. +//---------------------------------------------------------------------- + +class ProcessRunLock +{ +public: + ProcessRunLock () : + m_rwlock(), + m_running(false) + { + int err = ::pthread_rwlock_init(&m_rwlock, NULL); (void)err; +//#if LLDB_CONFIGURATION_DEBUG +// assert(err == 0); +//#endif + } + + ~ProcessRunLock () + { + int err = ::pthread_rwlock_destroy (&m_rwlock); (void)err; +//#if LLDB_CONFIGURATION_DEBUG +// assert(err == 0); +//#endif + } + + bool + ReadTryLock () + { + ::pthread_rwlock_rdlock (&m_rwlock); + if (m_running == false) + { + return true; + } + ::pthread_rwlock_unlock (&m_rwlock); + return false; + } + + bool + ReadUnlock () + { + return ::pthread_rwlock_unlock (&m_rwlock) == 0; + } + + bool + SetRunning() + { + ::pthread_rwlock_wrlock (&m_rwlock); + m_running = true; + ::pthread_rwlock_unlock (&m_rwlock); + return true; + } + + bool + TrySetRunning() + { + bool r; + + if (::pthread_rwlock_trywrlock (&m_rwlock) == 0) + { + r = !m_running; + m_running = true; + ::pthread_rwlock_unlock (&m_rwlock); + return r; + } + return false; + } + + bool + SetStopped () + { + ::pthread_rwlock_wrlock (&m_rwlock); + m_running = false; + ::pthread_rwlock_unlock (&m_rwlock); + return true; + } + + class ProcessRunLocker + { + public: + ProcessRunLocker () : + m_lock (NULL) + { + } + + ~ProcessRunLocker() + { + Unlock(); + } + + // Try to lock the read lock, but only do so if there are no writers. + bool + TryLock (ProcessRunLock *lock) + { + if (m_lock) + { + if (m_lock == lock) + return true; // We already have this lock locked + else + Unlock(); + } + if (lock) + { + if (lock->ReadTryLock()) + { + m_lock = lock; + return true; + } + } + return false; + } + + protected: + void + Unlock () + { + if (m_lock) + { + m_lock->ReadUnlock(); + m_lock = NULL; + } + } + + ProcessRunLock *m_lock; + private: + DISALLOW_COPY_AND_ASSIGN(ProcessRunLocker); + }; + +protected: + pthread_rwlock_t m_rwlock; + bool m_running; +private: + DISALLOW_COPY_AND_ASSIGN(ProcessRunLock); +}; + +} // namespace lldb_private + +#endif // #if defined(__cplusplus) +#endif // #ifndef liblldb_ProcessRunLock_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Host/SocketAddress.h b/contrib/llvm/tools/lldb/include/lldb/Host/SocketAddress.h new file mode 100644 index 00000000000..e63b238c799 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Host/SocketAddress.h @@ -0,0 +1,256 @@ +//===-- SocketAddress.h -----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_SocketAddress_h_ +#define liblldb_SocketAddress_h_ + +// C Includes +#include +#include +#include +#include + +#if defined(__FreeBSD__) +#include +#endif + +// C++ Includes +// Other libraries and framework includes +// Project includes + +namespace lldb_private { + +class SocketAddress +{ +public: + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + SocketAddress (); + SocketAddress (const struct sockaddr &s); + SocketAddress (const struct sockaddr_in &s); + SocketAddress (const struct sockaddr_in6 &s); + SocketAddress (const struct sockaddr_storage &s); + SocketAddress (const SocketAddress& rhs); + ~SocketAddress (); + + //------------------------------------------------------------------ + // Operators + //------------------------------------------------------------------ + const SocketAddress& + operator=(const SocketAddress& rhs); + + const SocketAddress& + operator=(const struct addrinfo *addr_info); + + const SocketAddress& + operator=(const struct sockaddr &s); + + const SocketAddress& + operator=(const struct sockaddr_in &s); + + const SocketAddress& + operator=(const struct sockaddr_in6 &s); + + const SocketAddress& + operator=(const struct sockaddr_storage &s); + + //------------------------------------------------------------------ + // Clear the contents of this socket address + //------------------------------------------------------------------ + void + Clear (); + + //------------------------------------------------------------------ + // Get the length for the current socket address family + //------------------------------------------------------------------ + socklen_t + GetLength () const; + + //------------------------------------------------------------------ + // Get the mex length for the the largest socket address supported. + //------------------------------------------------------------------ + static socklen_t + GetMaxLength (); + + //------------------------------------------------------------------ + // Get the socket address family + //------------------------------------------------------------------ + sa_family_t + GetFamily () const; + + //------------------------------------------------------------------ + // Set the socket address family + //------------------------------------------------------------------ + void + SetFamily (sa_family_t family); + + //------------------------------------------------------------------ + // Get the port if the socket address for the family has a port + //------------------------------------------------------------------ + in_port_t + GetPort () const; + + //------------------------------------------------------------------ + // Set the port if the socket address for the family has a port. + // The family must be set correctly prior to calling this function. + //------------------------------------------------------------------ + bool + SetPort (in_port_t port); + + //------------------------------------------------------------------ + // Set the socket address according to the first match from a call + // to getaddrinfo() (or equivalent functions for systems that don't + // have getaddrinfo(). If "addr_info_ptr" is not NULL, it will get + // filled in with the match that was used to populate this socket + // address. + //------------------------------------------------------------------ + bool + SetAddress (const struct addrinfo *hints_ptr, // Optional hints where the family, protocol and other things can be specified. + const char *host, // Hostname ("foo.bar.com" or "foo" or IP address string ("123.234.12.1" or "2001:0db8:85a3:0000:0000:8a2e:0370:7334") + const char *service, // Protocol name ("tcp", "http", etc) or a raw port number string ("81") + struct addrinfo *addr_info_ptr); // If non-NULL, this will get filled in with the match + + //------------------------------------------------------------------ + // Quick way to set the SocketAddress to localhost given the family. + // Returns true if successful, false if "family" doesn't support + // localhost or if "family" is not supported by this class. + //------------------------------------------------------------------ + bool + SetToLocalhost (sa_family_t family, + in_port_t port); + + //------------------------------------------------------------------ + // Returns true if there is a valid socket address in this object. + //------------------------------------------------------------------ + bool + IsValid () const; + + //------------------------------------------------------------------ + // Direct access to all of the sockaddr structures + //------------------------------------------------------------------ + struct sockaddr & + sockaddr () + { + return m_socket_addr.sa; + } + + const struct sockaddr & + sockaddr () const + { + return m_socket_addr.sa; + } + + struct sockaddr_in & + sockaddr_in () + { + return m_socket_addr.sa_ipv4; + } + + const struct sockaddr_in & + sockaddr_in () const + { + return m_socket_addr.sa_ipv4; + } + + struct sockaddr_in6 & + sockaddr_in6 () + { + return m_socket_addr.sa_ipv6; + } + + const struct sockaddr_in6 & + sockaddr_in6 () const + { + return m_socket_addr.sa_ipv6; + } + + struct sockaddr_storage & + sockaddr_storage () + { + return m_socket_addr.sa_storage; + } + + + const struct sockaddr_storage & + sockaddr_storage () const + { + return m_socket_addr.sa_storage; + } + + + //------------------------------------------------------------------ + // Conversion operators to allow getting the contents of this class + // as a pointer to the appropriate structure. This allows an instance + // of this class to be used in calls that take one of the sockaddr + // structure variants without having to manally use the correct + // accessor function. + //------------------------------------------------------------------ + + operator struct sockaddr * () + { + return &m_socket_addr.sa; + } + + operator const struct sockaddr * () const + { + return &m_socket_addr.sa; + } + + operator struct sockaddr_in * () + { + return &m_socket_addr.sa_ipv4; + } + + operator const struct sockaddr_in * () const + { + return &m_socket_addr.sa_ipv4; + } + + operator struct sockaddr_in6 * () + { + return &m_socket_addr.sa_ipv6; + } + + operator const struct sockaddr_in6 * () const + { + return &m_socket_addr.sa_ipv6; + } + + operator const struct sockaddr_storage * () const + { + return &m_socket_addr.sa_storage; + } + + operator struct sockaddr_storage * () + { + return &m_socket_addr.sa_storage; + } + + +protected: + typedef union sockaddr_tag + { + struct sockaddr sa; + struct sockaddr_in sa_ipv4; + struct sockaddr_in6 sa_ipv6; + struct sockaddr_storage sa_storage; + } sockaddr_t; + + //------------------------------------------------------------------ + // Classes that inherit from SocketAddress can see and modify these + //------------------------------------------------------------------ + sockaddr_t m_socket_addr; +}; + + +} // namespace lldb_private + + +#endif // liblldb_SocketAddress_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Host/Symbols.h b/contrib/llvm/tools/lldb/include/lldb/Host/Symbols.h new file mode 100644 index 00000000000..9db68e1ecf1 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Host/Symbols.h @@ -0,0 +1,69 @@ +//===-- Symbols.h -----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_Symbols_h_ +#define liblldb_Symbols_h_ + +// C Includes +#include +#include + +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Host/FileSpec.h" + +namespace lldb_private { + +class Symbols +{ +public: + //---------------------------------------------------------------------- + // Locate the executable file given a module specification. + // + // Locating the file should happen only on the local computer or using + // the current computers global settings. + //---------------------------------------------------------------------- + static FileSpec + LocateExecutableObjectFile (const ModuleSpec &module_spec); + + //---------------------------------------------------------------------- + // Locate the symbol file given a module specification. + // + // Locating the file should happen only on the local computer or using + // the current computers global settings. + //---------------------------------------------------------------------- + static FileSpec + LocateExecutableSymbolFile (const ModuleSpec &module_spec); + + static FileSpec + FindSymbolFileInBundle (const FileSpec& dsym_bundle_fspec, + const lldb_private::UUID *uuid, + const ArchSpec *arch); + + //---------------------------------------------------------------------- + // Locate the object and symbol file given a module specification. + // + // Locating the file can try to download the file from a corporate build + // respository, or using any other means necessary to locate both the + // unstripped object file and the debug symbols. + // The force_lookup argument controls whether the external program is called + // unconditionally to find the symbol file, or if the user's settings are + // checked to see if they've enabled the external program before calling. + // + //---------------------------------------------------------------------- + static bool + DownloadObjectAndSymbolFile (ModuleSpec &module_spec, bool force_lookup = true); + +}; + +} // namespace lldb_private + + +#endif // liblldb_Symbols_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Host/Terminal.h b/contrib/llvm/tools/lldb/include/lldb/Host/Terminal.h new file mode 100644 index 00000000000..b334717c796 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Host/Terminal.h @@ -0,0 +1,254 @@ +//===-- Terminal.h ----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_Terminal_h_ +#define liblldb_Terminal_h_ +#if defined(__cplusplus) + +#include "lldb/lldb-private.h" + +struct termios; + +namespace lldb_private { + +class Terminal +{ +public: + + Terminal (int fd = -1) : + m_fd (fd) + { + } + + ~Terminal () + { + } + + bool + IsATerminal () const; + + int + GetFileDescriptor () const + { + return m_fd; + } + + void + SetFileDescriptor (int fd) + { + m_fd = fd; + } + + bool + FileDescriptorIsValid () const + { + return m_fd != -1; + } + + void + Clear () + { + m_fd = -1; + } + + bool + SetEcho (bool enabled); + + bool + SetCanonical (bool enabled); + +protected: + int m_fd; // This may or may not be a terminal file descriptor +}; + + +//---------------------------------------------------------------------- +/// @class State Terminal.h "lldb/Host/Terminal.h" +/// @brief A terminal state saving/restoring class. +/// +/// This class can be used to remember the terminal state for a file +/// descriptor and later restore that state as it originally was. +//---------------------------------------------------------------------- +class TerminalState +{ +public: + //------------------------------------------------------------------ + /// Default constructor + //------------------------------------------------------------------ + TerminalState(); + + //------------------------------------------------------------------ + /// Destructor + //------------------------------------------------------------------ + ~TerminalState(); + + //------------------------------------------------------------------ + /// Save the TTY state for \a fd. + /// + /// Save the current state of the TTY for the file descriptor "fd" + /// and if "save_process_group" is true, attempt to save the process + /// group info for the TTY. + /// + /// @param[in] fd + /// The file descriptor to save the state of. + /// + /// @param[in] save_process_group + /// If \b true, save the process group settings, else do not + /// save the process group setttings for a TTY. + /// + /// @return + /// Returns \b true if \a fd describes a TTY and if the state + /// was able to be saved, \b false otherwise. + //------------------------------------------------------------------ + bool + Save (int fd, bool save_process_group); + + //------------------------------------------------------------------ + /// Restore the TTY state to the cached state. + /// + /// Restore the state of the TTY using the cached values from a + /// previous call to TerminalState::Save(int,bool). + /// + /// @return + /// Returns \b true if the TTY state was successfully restored, + /// \b false otherwise. + //------------------------------------------------------------------ + bool + Restore () const; + + //------------------------------------------------------------------ + /// Test for valid cached TTY state information. + /// + /// @return + /// Returns \b true if this object has valid saved TTY state + /// settings that can be used to restore a previous state, + /// \b false otherwise. + //------------------------------------------------------------------ + bool + IsValid() const; + + void + Clear (); + +protected: + + //------------------------------------------------------------------ + /// Test if tflags is valid. + /// + /// @return + /// Returns \b true if \a m_tflags is valid and can be restored, + /// \b false otherwise. + //------------------------------------------------------------------ + bool + TFlagsIsValid() const; + + //------------------------------------------------------------------ + /// Test if ttystate is valid. + /// + /// @return + /// Returns \b true if \a m_ttystate is valid and can be + /// restored, \b false otherwise. + //------------------------------------------------------------------ + bool + TTYStateIsValid() const; + + //------------------------------------------------------------------ + /// Test if the process group information is valid. + /// + /// @return + /// Returns \b true if \a m_process_group is valid and can be + /// restored, \b false otherwise. + //------------------------------------------------------------------ + bool + ProcessGroupIsValid() const; + + //------------------------------------------------------------------ + // Member variables + //------------------------------------------------------------------ + Terminal m_tty; ///< A terminal + int m_tflags; ///< Cached tflags information. + std::unique_ptr m_termios_ap; ///< Cached terminal state information. + lldb::pid_t m_process_group;///< Cached process group information. + +}; + +//---------------------------------------------------------------------- +/// @class TerminalStateSwitcher Terminal.h "lldb/Host/Terminal.h" +/// @brief A TTY state switching class. +/// +/// This class can be used to remember 2 TTY states for a given file +/// descriptor and switch between the two states. +//---------------------------------------------------------------------- +class TerminalStateSwitcher +{ +public: + //------------------------------------------------------------------ + /// Constructor + //------------------------------------------------------------------ + TerminalStateSwitcher(); + + //------------------------------------------------------------------ + /// Destructor + //------------------------------------------------------------------ + ~TerminalStateSwitcher(); + + //------------------------------------------------------------------ + /// Get the number of possible states to save. + /// + /// @return + /// The number of states that this TTY switcher object contains. + //------------------------------------------------------------------ + uint32_t + GetNumberOfStates() const; + + //------------------------------------------------------------------ + /// Restore the TTY state for state at index \a idx. + /// + /// @return + /// Returns \b true if the TTY state was successfully restored, + /// \b false otherwise. + //------------------------------------------------------------------ + bool + Restore (uint32_t idx) const; + + //------------------------------------------------------------------ + /// Save the TTY state information for the state at index \a idx. + /// The TTY state is saved for the file descriptor \a fd and + /// the process group information will also be saved if requested + /// by \a save_process_group. + /// + /// @param[in] idx + /// The index into the state array where the state should be + /// saved. + /// + /// @param[in] fd + /// The file descriptor for which to save the settings. + /// + /// @param[in] save_process_group + /// If \b true, save the process group information for the TTY. + /// + /// @return + /// Returns \b true if the save was successful, \b false + /// otherwise. + //------------------------------------------------------------------ + bool + Save (uint32_t idx, int fd, bool save_process_group); + +protected: + //------------------------------------------------------------------ + // Member variables + //------------------------------------------------------------------ + mutable uint32_t m_currentState; ///< The currently active TTY state index. + TerminalState m_ttystates[2]; ///< The array of TTY states that holds saved TTY info. +}; + +} // namespace lldb_private + +#endif // #if defined(__cplusplus) +#endif // #ifndef liblldb_Terminal_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Host/TimeValue.h b/contrib/llvm/tools/lldb/include/lldb/Host/TimeValue.h new file mode 100644 index 00000000000..8c43d6d0e5f --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Host/TimeValue.h @@ -0,0 +1,107 @@ +//===-- TimeValue.h ---------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_TimeValue_h_ +#define liblldb_TimeValue_h_ + +// C Includes +#include +#include + +// BEGIN: MinGW work around +#if !defined(_STRUCT_TIMESPEC) && !defined(HAVE_STRUCT_TIMESPEC) +#include +#endif +// END: MinGW work around + +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private.h" + +namespace lldb_private { + +class TimeValue +{ +public: + static const uint64_t MicroSecPerSec = 1000000UL; + static const uint64_t NanoSecPerSec = 1000000000UL; + static const uint64_t NanoSecPerMicroSec = 1000U; + + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + TimeValue(); + TimeValue(const TimeValue& rhs); + TimeValue(const struct timespec& ts); + TimeValue(const struct timeval& tv); + ~TimeValue(); + + //------------------------------------------------------------------ + // Operators + //------------------------------------------------------------------ + const TimeValue& + operator=(const TimeValue& rhs); + + void + Clear (); + + uint64_t + GetAsNanoSecondsSinceJan1_1970() const; + + uint64_t + GetAsMicroSecondsSinceJan1_1970() const; + + uint64_t + GetAsSecondsSinceJan1_1970() const; + + struct timespec + GetAsTimeSpec () const; + + struct timeval + GetAsTimeVal () const; + + bool + IsValid () const; + + void + OffsetWithSeconds (uint64_t sec); + + void + OffsetWithMicroSeconds (uint64_t usec); + + void + OffsetWithNanoSeconds (uint64_t nsec); + + static TimeValue + Now(); + + void + Dump (Stream *s, uint32_t width = 0) const; + +protected: + //------------------------------------------------------------------ + // Classes that inherit from TimeValue can see and modify these + //------------------------------------------------------------------ + uint64_t m_nano_seconds; +}; + +bool operator == (const TimeValue &lhs, const TimeValue &rhs); +bool operator != (const TimeValue &lhs, const TimeValue &rhs); +bool operator < (const TimeValue &lhs, const TimeValue &rhs); +bool operator <= (const TimeValue &lhs, const TimeValue &rhs); +bool operator > (const TimeValue &lhs, const TimeValue &rhs); +bool operator >= (const TimeValue &lhs, const TimeValue &rhs); + +uint64_t operator -(const TimeValue &lhs, const TimeValue &rhs); + +} // namespace lldb_private + + +#endif // liblldb_TimeValue_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Host/freebsd/Config.h b/contrib/llvm/tools/lldb/include/lldb/Host/freebsd/Config.h new file mode 100644 index 00000000000..49d97dd5793 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Host/freebsd/Config.h @@ -0,0 +1,28 @@ +//===-- Config.h -----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +//---------------------------------------------------------------------- +// LLDB currently doesn't have a dynamic configuration mechanism, so we +// are going to hardcode things for now. Eventually these files will +// be auto generated by some configuration script that can detect +// platform functionality availability. +//---------------------------------------------------------------------- + +#ifndef liblldb_Platform_Config_h_ +#define liblldb_Platform_Config_h_ + +#define LLDB_CONFIG_TERMIOS_SUPPORTED 1 + +#define LLDB_CONFIG_TILDE_RESOLVES_TO_USER 1 + +//#define LLDB_CONFIG_DLOPEN_RTLD_FIRST_SUPPORTED 1 + +//#define LLDB_CONFIG_FCNTL_GETPATH_SUPPORTED 1 + +#endif // #ifndef liblldb_Platform_Config_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Interpreter/Args.h b/contrib/llvm/tools/lldb/include/lldb/Interpreter/Args.h new file mode 100644 index 00000000000..d06c3e56aec --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Interpreter/Args.h @@ -0,0 +1,467 @@ +//===-- Args.h --------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_Command_h_ +#define liblldb_Command_h_ + +// C Includes +#include + +// C++ Includes +#include +#include +#include +#include + +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private-types.h" +#include "lldb/lldb-types.h" +#include "lldb/Core/Error.h" + +namespace lldb_private { + +typedef std::pair OptionArgValue; +typedef std::pair OptionArgPair; +typedef std::vector OptionArgVector; +typedef std::shared_ptr OptionArgVectorSP; + +struct OptionArgElement +{ + enum { + eUnrecognizedArg = -1, + eBareDash = -2, + eBareDoubleDash = -3 + }; + + OptionArgElement (int defs_index, int pos, int arg_pos) : + opt_defs_index(defs_index), + opt_pos (pos), + opt_arg_pos (arg_pos) + { + } + + int opt_defs_index; + int opt_pos; + int opt_arg_pos; +}; + +typedef std::vector OptionElementVector; + +//---------------------------------------------------------------------- +/// @class Args Args.h "lldb/Interpreter/Args.h" +/// @brief A command line argument class. +/// +/// The Args class is designed to be fed a command line. The +/// command line is copied into an internal buffer and then split up +/// into arguments. Arguments are space delimited if there are no quotes +/// (single, double, or backtick quotes) surrounding the argument. Spaces +/// can be escaped using a \ character to avoid having to surround an +/// argument that contains a space with quotes. +//---------------------------------------------------------------------- +class Args +{ +public: + + //------------------------------------------------------------------ + /// Construct with an option command string. + /// + /// @param[in] command + /// A NULL terminated command that will be copied and split up + /// into arguments. + /// + /// @see Args::SetCommandString(const char *) + //------------------------------------------------------------------ + Args (const char *command = NULL); + + Args (const char *command, size_t len); + + Args (const Args &rhs); + + const Args & + operator= (const Args &rhs); + + //------------------------------------------------------------------ + /// Destructor. + //------------------------------------------------------------------ + ~Args(); + + //------------------------------------------------------------------ + /// Dump all arguments to the stream \a s. + /// + /// @param[in] s + /// The stream to which to dump all arguments in the argument + /// vector. + //------------------------------------------------------------------ + void + Dump (Stream *s); + + //------------------------------------------------------------------ + /// Sets the command string contained by this object. + /// + /// The command string will be copied and split up into arguments + /// that can be accessed via the accessor functions. + /// + /// @param[in] command + /// A NULL terminated command that will be copied and split up + /// into arguments. + /// + /// @see Args::GetArgumentCount() const + /// @see Args::GetArgumentAtIndex (size_t) const + /// @see Args::GetArgumentVector () + /// @see Args::Shift () + /// @see Args::Unshift (const char *) + //------------------------------------------------------------------ + void + SetCommandString (const char *command); + + void + SetCommandString (const char *command, size_t len); + + bool + GetCommandString (std::string &command) const; + + bool + GetQuotedCommandString (std::string &command) const; + + //------------------------------------------------------------------ + /// Gets the number of arguments left in this command object. + /// + /// @return + /// The number or arguments in this object. + //------------------------------------------------------------------ + size_t + GetArgumentCount () const; + + //------------------------------------------------------------------ + /// Gets the NULL terminated C string argument pointer for the + /// argument at index \a idx. + /// + /// @return + /// The NULL terminated C string argument pointer if \a idx is a + /// valid argument index, NULL otherwise. + //------------------------------------------------------------------ + const char * + GetArgumentAtIndex (size_t idx) const; + + char + GetArgumentQuoteCharAtIndex (size_t idx) const; + + //------------------------------------------------------------------ + /// Gets the argument vector. + /// + /// The value returned by this function can be used by any function + /// that takes and vector. The return value is just like \a argv + /// in the standard C entry point function: + /// \code + /// int main (int argc, const char **argv); + /// \endcode + /// + /// @return + /// An array of NULL terminated C string argument pointers that + /// also has a terminating NULL C string pointer + //------------------------------------------------------------------ + char ** + GetArgumentVector (); + + //------------------------------------------------------------------ + /// Gets the argument vector. + /// + /// The value returned by this function can be used by any function + /// that takes and vector. The return value is just like \a argv + /// in the standard C entry point function: + /// \code + /// int main (int argc, const char **argv); + /// \endcode + /// + /// @return + /// An array of NULL terminate C string argument pointers that + /// also has a terminating NULL C string pointer + //------------------------------------------------------------------ + const char ** + GetConstArgumentVector () const; + + + //------------------------------------------------------------------ + /// Appends a new argument to the end of the list argument list. + /// + /// @param[in] arg_cstr + /// The new argument as a NULL terminated C string. + /// + /// @param[in] quote_char + /// If the argument was originally quoted, put in the quote char here. + /// + /// @return + /// The NULL terminated C string of the copy of \a arg_cstr. + //------------------------------------------------------------------ + const char * + AppendArgument (const char *arg_cstr, char quote_char = '\0'); + + void + AppendArguments (const Args &rhs); + + void + AppendArguments (const char **argv); + + //------------------------------------------------------------------ + /// Insert the argument value at index \a idx to \a arg_cstr. + /// + /// @param[in] idx + /// The index of where to insert the argument. + /// + /// @param[in] arg_cstr + /// The new argument as a NULL terminated C string. + /// + /// @param[in] quote_char + /// If the argument was originally quoted, put in the quote char here. + /// + /// @return + /// The NULL terminated C string of the copy of \a arg_cstr. + //------------------------------------------------------------------ + const char * + InsertArgumentAtIndex (size_t idx, const char *arg_cstr, char quote_char = '\0'); + + //------------------------------------------------------------------ + /// Replaces the argument value at index \a idx to \a arg_cstr + /// if \a idx is a valid argument index. + /// + /// @param[in] idx + /// The index of the argument that will have its value replaced. + /// + /// @param[in] arg_cstr + /// The new argument as a NULL terminated C string. + /// + /// @param[in] quote_char + /// If the argument was originally quoted, put in the quote char here. + /// + /// @return + /// The NULL terminated C string of the copy of \a arg_cstr if + /// \a idx was a valid index, NULL otherwise. + //------------------------------------------------------------------ + const char * + ReplaceArgumentAtIndex (size_t idx, const char *arg_cstr, char quote_char = '\0'); + + //------------------------------------------------------------------ + /// Deletes the argument value at index + /// if \a idx is a valid argument index. + /// + /// @param[in] idx + /// The index of the argument that will have its value replaced. + /// + //------------------------------------------------------------------ + void + DeleteArgumentAtIndex (size_t idx); + + //------------------------------------------------------------------ + /// Sets the argument vector value, optionally copying all + /// arguments into an internal buffer. + /// + /// Sets the arguments to match those found in \a argv. All argument + /// strings will be copied into an internal buffers. + // + // FIXME: Handle the quote character somehow. + //------------------------------------------------------------------ + void + SetArguments (size_t argc, const char **argv); + + void + SetArguments (const char **argv); + + //------------------------------------------------------------------ + /// Shifts the first argument C string value of the array off the + /// argument array. + /// + /// The string value will be freed, so a copy of the string should + /// be made by calling Args::GetArgumentAtIndex (size_t) const + /// first and copying the returned value before calling + /// Args::Shift(). + /// + /// @see Args::GetArgumentAtIndex (size_t) const + //------------------------------------------------------------------ + void + Shift (); + + //------------------------------------------------------------------ + /// Inserts a class owned copy of \a arg_cstr at the beginning of + /// the argument vector. + /// + /// A copy \a arg_cstr will be made. + /// + /// @param[in] arg_cstr + /// The argument to push on the front the the argument stack. + /// + /// @param[in] quote_char + /// If the argument was originally quoted, put in the quote char here. + /// + /// @return + /// A pointer to the copy of \a arg_cstr that was made. + //------------------------------------------------------------------ + const char * + Unshift (const char *arg_cstr, char quote_char = '\0'); + + //------------------------------------------------------------------ + /// Parse the arguments in the contained arguments. + /// + /// The arguments that are consumed by the argument parsing process + /// will be removed from the argument vector. The arguements that + /// get processed start at the second argument. The first argument + /// is assumed to be the command and will not be touched. + /// + /// @see class Options + //------------------------------------------------------------------ + Error + ParseOptions (Options &options); + + size_t + FindArgumentIndexForOption (struct option *long_options, int long_options_index); + + bool + IsPositionalArgument (const char *arg); + + // The following works almost identically to ParseOptions, except that no option is required to have arguments, + // and it builds up the option_arg_vector as it parses the options. + + void + ParseAliasOptions (Options &options, CommandReturnObject &result, OptionArgVector *option_arg_vector, + std::string &raw_input_line); + + void + ParseArgsForCompletion (Options &options, OptionElementVector &option_element_vector, uint32_t cursor_index); + + //------------------------------------------------------------------ + // Clear the arguments. + // + // For re-setting or blanking out the list of arguments. + //------------------------------------------------------------------ + void + Clear (); + + static const char * + StripSpaces (std::string &s, + bool leading = true, + bool trailing = true, + bool return_null_if_empty = true); + + static int32_t + StringToSInt32 (const char *s, int32_t fail_value = 0, int base = 0, bool *success_ptr = NULL); + + static uint32_t + StringToUInt32 (const char *s, uint32_t fail_value = 0, int base = 0, bool *success_ptr = NULL); + + static int64_t + StringToSInt64 (const char *s, int64_t fail_value = 0, int base = 0, bool *success_ptr = NULL); + + static uint64_t + StringToUInt64 (const char *s, uint64_t fail_value = 0, int base = 0, bool *success_ptr = NULL); + + static bool + UInt64ValueIsValidForByteSize (uint64_t uval64, size_t total_byte_size) + { + if (total_byte_size > 8) + return false; + + if (total_byte_size == 8) + return true; + + const uint64_t max = ((uint64_t)1 << (uint64_t)(total_byte_size * 8)) - 1; + return uval64 <= max; + } + + static bool + SInt64ValueIsValidForByteSize (int64_t sval64, size_t total_byte_size) + { + if (total_byte_size > 8) + return false; + + if (total_byte_size == 8) + return true; + + const int64_t max = ((int64_t)1 << (uint64_t)(total_byte_size * 8 - 1)) - 1; + const int64_t min = ~(max); + return min <= sval64 && sval64 <= max; + } + + static lldb::addr_t + StringToAddress (const ExecutionContext *exe_ctx, + const char *s, + lldb::addr_t fail_value, + Error *error); + + static bool + StringToBoolean (const char *s, bool fail_value, bool *success_ptr); + + static int64_t + StringToOptionEnum (const char *s, OptionEnumValueElement *enum_values, int32_t fail_value, Error &error); + + static lldb::ScriptLanguage + StringToScriptLanguage (const char *s, lldb::ScriptLanguage fail_value, bool *success_ptr); + + static Error + StringToFormat (const char *s, + lldb::Format &format, + size_t *byte_size_ptr); // If non-NULL, then a byte size can precede the format character + + static lldb::Encoding + StringToEncoding (const char *s, + lldb::Encoding fail_value = lldb::eEncodingInvalid); + + static uint32_t + StringToGenericRegister (const char *s); + + static const char * + StringToVersion (const char *s, uint32_t &major, uint32_t &minor, uint32_t &update); + + static const char * + GetShellSafeArgument (const char *unsafe_arg, std::string &safe_arg); + + // EncodeEscapeSequences will change the textual representation of common + // escape sequences like "\n" (two characters) into a single '\n'. It does + // this for all of the supported escaped sequences and for the \0ooo (octal) + // and \xXX (hex). The resulting "dst" string will contain the character + // versions of all supported escape sequences. The common supported escape + // sequences are: "\a", "\b", "\f", "\n", "\r", "\t", "\v", "\'", "\"", "\\". + + static void + EncodeEscapeSequences (const char *src, std::string &dst); + + // ExpandEscapeSequences will change a string of possibly non-printable + // characters and expand them into text. So '\n' will turn into two chracters + // like "\n" which is suitable for human reading. When a character is not + // printable and isn't one of the common in escape sequences listed in the + // help for EncodeEscapeSequences, then it will be encoded as octal. Printable + // characters are left alone. + static void + ExpandEscapedCharacters (const char *src, std::string &dst); + + // This one isn't really relevant to Arguments per se, but we're using the Args as a + // general strings container, so... + void + LongestCommonPrefix (std::string &common_prefix); + +protected: + //------------------------------------------------------------------ + // Classes that inherit from Args can see and modify these + //------------------------------------------------------------------ + typedef std::list arg_sstr_collection; + typedef std::vector arg_cstr_collection; + typedef std::vector arg_quote_char_collection; + arg_sstr_collection m_args; + arg_cstr_collection m_argv; ///< The current argument vector. + arg_quote_char_collection m_args_quote_char; + + void + UpdateArgsAfterOptionParsing (); + + void + UpdateArgvFromArgs (); +}; + +} // namespace lldb_private + +#endif // liblldb_Command_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Interpreter/CommandCompletions.h b/contrib/llvm/tools/lldb/include/lldb/Interpreter/CommandCompletions.h new file mode 100644 index 00000000000..c4ab1b61ade --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Interpreter/CommandCompletions.h @@ -0,0 +1,307 @@ +//===-- CommandCompletions.h ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef lldb_CommandCompletions_h_ +#define lldb_CommandCompletions_h_ + +// C Includes +// C++ Includes +#include + +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private.h" +#include "lldb/Core/SearchFilter.h" +#include "lldb/Core/FileSpecList.h" +#include "lldb/Core/RegularExpression.h" + +namespace lldb_private +{ +class CommandCompletions +{ +public: + + //---------------------------------------------------------------------- + // This is the command completion callback that is used to complete the argument of the option + // it is bound to (in the OptionDefinition table below). Return the total number of matches. + //---------------------------------------------------------------------- + typedef int (*CompletionCallback) (CommandInterpreter &interpreter, + const char *completion_str, // This is the argument we are completing + int match_start_point, // This is the point in the list of matches that you should start returning elements + int max_return_elements, // This is the number of matches requested. + lldb_private::SearchFilter *searcher,// A search filter to limit the search... + bool &word_complete, + lldb_private::StringList &matches); // The array of matches we return. + typedef enum + { + eNoCompletion = 0u, + eSourceFileCompletion = (1u << 0), + eDiskFileCompletion = (1u << 1), + eDiskDirectoryCompletion = (1u << 2), + eSymbolCompletion = (1u << 3), + eModuleCompletion = (1u << 4), + eSettingsNameCompletion = (1u << 5), + ePlatformPluginCompletion = (1u << 6), + eArchitectureCompletion = (1u << 7), + eVariablePathCompletion = (1u << 8), + // This item serves two purposes. It is the last element in the enum, + // so you can add custom enums starting from here in your Option class. + // Also if you & in this bit the base code will not process the option. + eCustomCompletion = (1u << 9) + + } CommonCompletionTypes; + + struct CommonCompletionElement + { + uint32_t type; + CompletionCallback callback; + }; + + static bool InvokeCommonCompletionCallbacks (CommandInterpreter &interpreter, + uint32_t completion_mask, + const char *completion_str, + int match_start_point, + int max_return_elements, + SearchFilter *searcher, + bool &word_complete, + StringList &matches); + + //---------------------------------------------------------------------- + // These are the generic completer functions: + //---------------------------------------------------------------------- + static int + DiskFiles (CommandInterpreter &interpreter, + const char *partial_file_name, + int match_start_point, + int max_return_elements, + SearchFilter *searcher, + bool &word_complete, + StringList &matches); + static int + DiskDirectories (CommandInterpreter &interpreter, + const char *partial_file_name, + int match_start_point, + int max_return_elements, + SearchFilter *searcher, + bool &word_complete, + StringList &matches); + + static int + SourceFiles (CommandInterpreter &interpreter, + const char *partial_file_name, + int match_start_point, + int max_return_elements, + SearchFilter *searcher, + bool &word_complete, + StringList &matches); + + static int + Modules (CommandInterpreter &interpreter, + const char *partial_file_name, + int match_start_point, + int max_return_elements, + SearchFilter *searcher, + bool &word_complete, + lldb_private::StringList &matches); + + static int + Symbols (CommandInterpreter &interpreter, + const char *partial_file_name, + int match_start_point, + int max_return_elements, + SearchFilter *searcher, + bool &word_complete, + lldb_private::StringList &matches); + + static int + SettingsNames (CommandInterpreter &interpreter, + const char *partial_file_name, + int match_start_point, + int max_return_elements, + SearchFilter *searcher, + bool &word_complete, + lldb_private::StringList &matches); + + static int + PlatformPluginNames (CommandInterpreter &interpreter, + const char *partial_file_name, + int match_start_point, + int max_return_elements, + SearchFilter *searcher, + bool &word_complete, + lldb_private::StringList &matches); + + + static int + ArchitectureNames (CommandInterpreter &interpreter, + const char *partial_file_name, + int match_start_point, + int max_return_elements, + SearchFilter *searcher, + bool &word_complete, + lldb_private::StringList &matches); + + static int + VariablePath (CommandInterpreter &interpreter, + const char *partial_file_name, + int match_start_point, + int max_return_elements, + SearchFilter *searcher, + bool &word_complete, + lldb_private::StringList &matches); + + //---------------------------------------------------------------------- + // The Completer class is a convenient base class for building searchers + // that go along with the SearchFilter passed to the standard Completer + // functions. + //---------------------------------------------------------------------- + class Completer : public Searcher + { + public: + Completer (CommandInterpreter &interpreter, + const char *completion_str, + int match_start_point, + int max_return_elements, + StringList &matches); + + virtual ~Completer (); + + virtual CallbackReturn + SearchCallback (SearchFilter &filter, + SymbolContext &context, + Address *addr, + bool complete) = 0; + + virtual Depth + GetDepth () = 0; + + virtual size_t + DoCompletion (SearchFilter *filter) = 0; + + protected: + CommandInterpreter &m_interpreter; + std::string m_completion_str; + int m_match_start_point; + int m_max_return_elements; + StringList &m_matches; + private: + DISALLOW_COPY_AND_ASSIGN (Completer); + }; + + //---------------------------------------------------------------------- + // SouceFileCompleter implements the source file completer + //---------------------------------------------------------------------- + class SourceFileCompleter : public Completer + { + public: + + SourceFileCompleter (CommandInterpreter &interpreter, + bool include_support_files, + const char *completion_str, + int match_start_point, + int max_return_elements, + StringList &matches); + + virtual Searcher::Depth GetDepth (); + + virtual Searcher::CallbackReturn + SearchCallback (SearchFilter &filter, + SymbolContext &context, + Address *addr, + bool complete); + + size_t + DoCompletion (SearchFilter *filter); + + private: + bool m_include_support_files; + FileSpecList m_matching_files; + const char *m_file_name; + const char *m_dir_name; + DISALLOW_COPY_AND_ASSIGN (SourceFileCompleter); + + }; + + //---------------------------------------------------------------------- + // ModuleCompleter implements the module completer + //---------------------------------------------------------------------- + class ModuleCompleter : public Completer + { + public: + + ModuleCompleter (CommandInterpreter &interpreter, + const char *completion_str, + int match_start_point, + int max_return_elements, + StringList &matches); + + virtual Searcher::Depth GetDepth (); + + virtual Searcher::CallbackReturn + SearchCallback (SearchFilter &filter, + SymbolContext &context, + Address *addr, + bool complete); + + size_t + DoCompletion (SearchFilter *filter); + + private: + const char *m_file_name; + const char *m_dir_name; + DISALLOW_COPY_AND_ASSIGN (ModuleCompleter); + + }; + + //---------------------------------------------------------------------- + // SymbolCompleter implements the symbol completer + //---------------------------------------------------------------------- + class SymbolCompleter : public Completer + { + public: + + SymbolCompleter (CommandInterpreter &interpreter, + const char *completion_str, + int match_start_point, + int max_return_elements, + StringList &matches); + + virtual Searcher::Depth GetDepth (); + + virtual Searcher::CallbackReturn + SearchCallback (SearchFilter &filter, + SymbolContext &context, + Address *addr, + bool complete); + + size_t + DoCompletion (SearchFilter *filter); + + private: +// struct NameCmp { +// bool operator() (const ConstString& lhs, const ConstString& rhs) const +// { +// return lhs < rhs; +// } +// }; + + RegularExpression m_regex; + typedef std::set collection; + collection m_match_set; + DISALLOW_COPY_AND_ASSIGN (SymbolCompleter); + + }; + +private: + static CommonCompletionElement g_common_completions[]; + +}; + +} // namespace lldb_private +#endif // lldb_CommandCompletions_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Interpreter/CommandHistory.h b/contrib/llvm/tools/lldb/include/lldb/Interpreter/CommandHistory.h new file mode 100644 index 00000000000..dbe6e99bb5b --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Interpreter/CommandHistory.h @@ -0,0 +1,76 @@ +//===-- CommandHistory.h ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_CommandHistory_h_ +#define liblldb_CommandHistory_h_ + +// C Includes +// C++ Includes +#include +#include + +// Other libraries and framework includes +// Project includes + +#include "lldb/lldb-private.h" +#include "lldb/Core/Stream.h" +#include "lldb/Host/Mutex.h" + +namespace lldb_private { + +class CommandHistory +{ +public: + CommandHistory (); + + ~CommandHistory (); + + size_t + GetSize () const; + + bool + IsEmpty () const; + + const char* + FindString (const char* input_str) const; + + const char* + GetStringAtIndex (size_t idx) const; + + const char* + operator [] (size_t idx) const; + + const char* + GetRecentmostString () const; + + void + AppendString (const std::string& str, + bool reject_if_dupe = true); + + void + Clear (); + + void + Dump (Stream& stream, + size_t start_idx = 0, + size_t stop_idx = SIZE_MAX) const; + + static const char g_repeat_char = '!'; + +private: + DISALLOW_COPY_AND_ASSIGN(CommandHistory); + + typedef std::vector History; + mutable Mutex m_mutex; + History m_history; +}; + +} // namespace lldb_private + +#endif // liblldb_CommandHistory_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Interpreter/CommandInterpreter.h b/contrib/llvm/tools/lldb/include/lldb/Interpreter/CommandInterpreter.h new file mode 100644 index 00000000000..31fcc38eed9 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Interpreter/CommandInterpreter.h @@ -0,0 +1,486 @@ +//===-- CommandInterpreter.h ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_CommandInterpreter_h_ +#define liblldb_CommandInterpreter_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private.h" +#include "lldb/Core/Broadcaster.h" +#include "lldb/Core/Debugger.h" +#include "lldb/Core/Log.h" +#include "lldb/Interpreter/CommandHistory.h" +#include "lldb/Interpreter/CommandObject.h" +#include "lldb/Interpreter/ScriptInterpreter.h" +#include "lldb/Core/Event.h" +#include "lldb/Interpreter/Args.h" +#include "lldb/Core/StringList.h" + +namespace lldb_private { + +class CommandInterpreter : + public Broadcaster, + public Properties +{ +public: + typedef std::map OptionArgMap; + + enum + { + eBroadcastBitThreadShouldExit = (1 << 0), + eBroadcastBitResetPrompt = (1 << 1), + eBroadcastBitQuitCommandReceived = (1 << 2), // User entered quit + eBroadcastBitAsynchronousOutputData = (1 << 3), + eBroadcastBitAsynchronousErrorData = (1 << 4) + }; + + enum ChildrenTruncatedWarningStatus // tristate boolean to manage children truncation warning + { + eNoTruncation = 0, // never truncated + eUnwarnedTruncation = 1, // truncated but did not notify + eWarnedTruncation = 2 // truncated and notified + }; + + enum CommandTypes + { + eCommandTypesBuiltin = 0x0001, // native commands such as "frame" + eCommandTypesUserDef = 0x0002, // scripted commands + eCommandTypesAliases = 0x0004, // aliases such as "po" + eCommandTypesAllThem = 0xFFFF // all commands + }; + + // These two functions fill out the Broadcaster interface: + + static ConstString &GetStaticBroadcasterClass (); + + virtual ConstString &GetBroadcasterClass() const + { + return GetStaticBroadcasterClass(); + } + + void + SourceInitFile (bool in_cwd, + CommandReturnObject &result); + + CommandInterpreter (Debugger &debugger, + lldb::ScriptLanguage script_language, + bool synchronous_execution); + + virtual + ~CommandInterpreter (); + + bool + AddCommand (const char *name, + const lldb::CommandObjectSP &cmd_sp, + bool can_replace); + + bool + AddUserCommand (std::string name, + const lldb::CommandObjectSP &cmd_sp, + bool can_replace); + + lldb::CommandObjectSP + GetCommandSPExact (const char *cmd, + bool include_aliases); + + CommandObject * + GetCommandObjectExact (const char *cmd_cstr, + bool include_aliases); + + CommandObject * + GetCommandObject (const char *cmd, + StringList *matches = NULL); + + bool + CommandExists (const char *cmd); + + bool + AliasExists (const char *cmd); + + bool + UserCommandExists (const char *cmd); + + void + AddAlias (const char *alias_name, + lldb::CommandObjectSP& command_obj_sp); + + bool + RemoveAlias (const char *alias_name); + + bool + GetAliasFullName (const char *cmd, std::string &full_name); + + bool + RemoveUser (const char *alias_name); + + void + RemoveAllUser () + { + m_user_dict.clear(); + } + + OptionArgVectorSP + GetAliasOptions (const char *alias_name); + + + bool + ProcessAliasOptionsArgs (lldb::CommandObjectSP &cmd_obj_sp, + const char *options_args, + OptionArgVectorSP &option_arg_vector_sp); + + void + RemoveAliasOptions (const char *alias_name); + + void + AddOrReplaceAliasOptions (const char *alias_name, + OptionArgVectorSP &option_arg_vector_sp); + + CommandObject * + BuildAliasResult (const char *alias_name, + std::string &raw_input_string, + std::string &alias_result, + CommandReturnObject &result); + + bool + HandleCommand (const char *command_line, + LazyBool add_to_history, + CommandReturnObject &result, + ExecutionContext *override_context = NULL, + bool repeat_on_empty_command = true, + bool no_context_switching = false); + + //------------------------------------------------------------------ + /// Execute a list of commands in sequence. + /// + /// @param[in] commands + /// The list of commands to execute. + /// @param[in/out] context + /// The execution context in which to run the commands. Can be NULL in which case the default + /// context will be used. + /// @param[in] stop_on_continue + /// If \b true execution will end on the first command that causes the process in the + /// execution context to continue. If \false, we won't check the execution status. + /// @param[in] stop_on_error + /// If \b true execution will end on the first command that causes an error. + /// @param[in] echo_commands + /// If \b true echo the command before executing it. If \false, execute silently. + /// @param[in] print_results + /// If \b true print the results of the command after executing it. If \false, execute silently. + /// @param[out] result + /// This is marked as succeeding with no output if all commands execute safely, + /// and failed with some explanation if we aborted executing the commands at some point. + //------------------------------------------------------------------ + void + HandleCommands (const StringList &commands, + ExecutionContext *context, + bool stop_on_continue, + bool stop_on_error, + bool echo_commands, + bool print_results, + LazyBool add_to_history, + CommandReturnObject &result); + + //------------------------------------------------------------------ + /// Execute a list of commands from a file. + /// + /// @param[in] file + /// The file from which to read in commands. + /// @param[in/out] context + /// The execution context in which to run the commands. Can be NULL in which case the default + /// context will be used. + /// @param[in] stop_on_continue + /// If \b true execution will end on the first command that causes the process in the + /// execution context to continue. If \false, we won't check the execution status. + /// @param[in] stop_on_error + /// If \b true execution will end on the first command that causes an error. + /// @param[in] echo_commands + /// If \b true echo the command before executing it. If \false, execute silently. + /// @param[in] print_results + /// If \b true print the results of the command after executing it. If \false, execute silently. + /// @param[out] result + /// This is marked as succeeding with no output if all commands execute safely, + /// and failed with some explanation if we aborted executing the commands at some point. + //------------------------------------------------------------------ + void + HandleCommandsFromFile (FileSpec &file, + ExecutionContext *context, + bool stop_on_continue, + bool stop_on_error, + bool echo_commands, + bool print_results, + LazyBool add_to_history, + CommandReturnObject &result); + + CommandObject * + GetCommandObjectForCommand (std::string &command_line); + + // This handles command line completion. You are given a pointer to the command string buffer, to the current cursor, + // and to the end of the string (in case it is not NULL terminated). + // You also passed in an StringList object to fill with the returns. + // The first element of the array will be filled with the string that you would need to insert at + // the cursor point to complete the cursor point to the longest common matching prefix. + // If you want to limit the number of elements returned, set max_return_elements to the number of elements + // you want returned. Otherwise set max_return_elements to -1. + // If you want to start some way into the match list, then set match_start_point to the desired start + // point. + // Returns: + // -1 if the completion character should be inserted + // -2 if the entire command line should be deleted and replaced with matches.GetStringAtIndex(0) + // INT_MAX if the number of matches is > max_return_elements, but it is expensive to compute. + // Otherwise, returns the number of matches. + // + // FIXME: Only max_return_elements == -1 is supported at present. + + int + HandleCompletion (const char *current_line, + const char *cursor, + const char *last_char, + int match_start_point, + int max_return_elements, + StringList &matches); + + // This version just returns matches, and doesn't compute the substring. It is here so the + // Help command can call it for the first argument. + // word_complete tells whether a the completions are considered a "complete" response (so the + // completer should complete the quote & put a space after the word. + + int + HandleCompletionMatches (Args &input, + int &cursor_index, + int &cursor_char_position, + int match_start_point, + int max_return_elements, + bool &word_complete, + StringList &matches); + + + int + GetCommandNamesMatchingPartialString (const char *cmd_cstr, + bool include_aliases, + StringList &matches); + + void + GetHelp (CommandReturnObject &result, + uint32_t types = eCommandTypesAllThem); + + void + GetAliasHelp (const char *alias_name, + const char *command_name, + StreamString &help_string); + + void + OutputFormattedHelpText (Stream &stream, + const char *command_word, + const char *separator, + const char *help_text, + size_t max_word_len); + + // this mimics OutputFormattedHelpText but it does perform a much simpler + // formatting, basically ensuring line alignment. This is only good if you have + // some complicated layout for your help text and want as little help as reasonable + // in properly displaying it. Most of the times, you simply want to type some text + // and have it printed in a reasonable way on screen. If so, use OutputFormattedHelpText + void + OutputHelpText (Stream &stream, + const char *command_word, + const char *separator, + const char *help_text, + uint32_t max_word_len); + + Debugger & + GetDebugger () + { + return m_debugger; + } + + ExecutionContext + GetExecutionContext() + { + return m_exe_ctx_ref.Lock(); + } + + void + UpdateExecutionContext (ExecutionContext *override_context); + + lldb::PlatformSP + GetPlatform (bool prefer_target_platform); + + const char * + ProcessEmbeddedScriptCommands (const char *arg); + + const char * + GetPrompt (); + + void + SetPrompt (const char *); + + bool Confirm (const char *message, bool default_answer); + + static size_t + GetConfirmationInputReaderCallback (void *baton, + InputReader &reader, + lldb::InputReaderAction action, + const char *bytes, + size_t bytes_len); + + void + LoadCommandDictionary (); + + void + Initialize (); + + void + SetScriptLanguage (lldb::ScriptLanguage lang); + + + bool + HasCommands (); + + bool + HasAliases (); + + bool + HasUserCommands (); + + bool + HasAliasOptions (); + + void + BuildAliasCommandArgs (CommandObject *alias_cmd_obj, + const char *alias_name, + Args &cmd_args, + std::string &raw_input_string, + CommandReturnObject &result); + + int + GetOptionArgumentPosition (const char *in_string); + + ScriptInterpreter * + GetScriptInterpreter (bool can_create = true); + + void + SkipLLDBInitFiles (bool skip_lldbinit_files) + { + m_skip_lldbinit_files = skip_lldbinit_files; + } + + void + SkipAppInitFiles (bool skip_app_init_files) + { + m_skip_app_init_files = m_skip_lldbinit_files; + } + + bool + GetSynchronous (); + + size_t + FindLongestCommandWord (CommandObject::CommandMap &dict); + + void + FindCommandsForApropos (const char *word, + StringList &commands_found, + StringList &commands_help, + bool search_builtin_commands, + bool search_user_commands); + + bool + GetBatchCommandMode () { return m_batch_command_mode; } + + void + SetBatchCommandMode (bool value) { m_batch_command_mode = value; } + + void + ChildrenTruncated () + { + if (m_truncation_warning == eNoTruncation) + m_truncation_warning = eUnwarnedTruncation; + } + + bool + TruncationWarningNecessary () + { + return (m_truncation_warning == eUnwarnedTruncation); + } + + void + TruncationWarningGiven () + { + m_truncation_warning = eWarnedTruncation; + } + + const char * + TruncationWarningText () + { + return "*** Some of your variables have more members than the debugger will show by default. To show all of them, you can either use the --show-all-children option to %s or raise the limit by changing the target.max-children-count setting.\n"; + } + + const CommandHistory& + GetCommandHistory () const + { + return m_command_history; + } + + CommandHistory& + GetCommandHistory () + { + return m_command_history; + } + + //------------------------------------------------------------------ + // Properties + //------------------------------------------------------------------ + bool + GetExpandRegexAliases () const; + + bool + GetPromptOnQuit () const; + + bool + GetStopCmdSourceOnError () const; + +protected: + friend class Debugger; + + void + SetSynchronous (bool value); + + lldb::CommandObjectSP + GetCommandSP (const char *cmd, bool include_aliases = true, bool exact = true, StringList *matches = NULL); + +private: + + Error + PreprocessCommand (std::string &command); + + Debugger &m_debugger; // The debugger session that this interpreter is associated with + ExecutionContextRef m_exe_ctx_ref; // The current execution context to use when handling commands + bool m_synchronous_execution; + bool m_skip_lldbinit_files; + bool m_skip_app_init_files; + CommandObject::CommandMap m_command_dict; // Stores basic built-in commands (they cannot be deleted, removed or overwritten). + CommandObject::CommandMap m_alias_dict; // Stores user aliases/abbreviations for commands + CommandObject::CommandMap m_user_dict; // Stores user-defined commands + OptionArgMap m_alias_options; // Stores any options (with or without arguments) that go with any alias. + CommandHistory m_command_history; + std::string m_repeat_command; // Stores the command that will be executed for an empty command string. + std::unique_ptr m_script_interpreter_ap; + char m_comment_char; + bool m_batch_command_mode; + ChildrenTruncatedWarningStatus m_truncation_warning; // Whether we truncated children and whether the user has been told + uint32_t m_command_source_depth; + +}; + + +} // namespace lldb_private + +#endif // liblldb_CommandInterpreter_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Interpreter/CommandObject.h b/contrib/llvm/tools/lldb/include/lldb/Interpreter/CommandObject.h new file mode 100644 index 00000000000..2bfab0a8ecc --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Interpreter/CommandObject.h @@ -0,0 +1,608 @@ +//===-- CommandObject.h -----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_CommandObject_h_ +#define liblldb_CommandObject_h_ + +#include +#include +#include +#include + +#include "lldb/lldb-private.h" +#include "lldb/Interpreter/Args.h" +#include "lldb/Interpreter/CommandCompletions.h" +#include "lldb/Core/StringList.h" +#include "lldb/Core/Flags.h" +#include "lldb/Host/Mutex.h" +#include "lldb/Target/ExecutionContext.h" + +namespace lldb_private { + +class CommandObject +{ +public: + + typedef const char *(ArgumentHelpCallbackFunction) (); + + struct ArgumentHelpCallback + { + ArgumentHelpCallbackFunction *help_callback; + bool self_formatting; + + const char* + operator () () const + { + return (*help_callback)(); + } + + operator bool() const + { + return (help_callback != NULL); + } + + }; + + struct ArgumentTableEntry // Entries in the main argument information table + { + lldb::CommandArgumentType arg_type; + const char *arg_name; + CommandCompletions::CommonCompletionTypes completion_type; + ArgumentHelpCallback help_function; + const char *help_text; + }; + + struct CommandArgumentData // Used to build individual command argument lists + { + lldb::CommandArgumentType arg_type; + ArgumentRepetitionType arg_repetition; + uint32_t arg_opt_set_association; // This arg might be associated only with some particular option set(s). + CommandArgumentData(): + arg_type(lldb::eArgTypeNone), + arg_repetition(eArgRepeatPlain), + arg_opt_set_association(LLDB_OPT_SET_ALL) // By default, the arg associates to all option sets. + {} + }; + + typedef std::vector CommandArgumentEntry; // Used to build individual command argument lists + + static ArgumentTableEntry g_arguments_data[lldb::eArgTypeLastArg]; // Main argument information table + + typedef std::map CommandMap; + + CommandObject (CommandInterpreter &interpreter, + const char *name, + const char *help = NULL, + const char *syntax = NULL, + uint32_t flags = 0); + + virtual + ~CommandObject (); + + + static const char * + GetArgumentTypeAsCString (const lldb::CommandArgumentType arg_type); + + static const char * + GetArgumentDescriptionAsCString (const lldb::CommandArgumentType arg_type); + + CommandInterpreter & + GetCommandInterpreter () + { + return m_interpreter; + } + + const char * + GetHelp (); + + virtual const char * + GetHelpLong (); + + const char * + GetSyntax (); + + const char * + GetCommandName (); + + void + SetHelp (const char * str); + + void + SetHelpLong (const char * str); + + void + SetHelpLong (std::string str); + + void + SetSyntax (const char *str); + + // override this to return true if you want to enable the user to delete + // the Command object from the Command dictionary (aliases have their own + // deletion scheme, so they do not need to care about this) + virtual bool + IsRemovable () const { return false; } + + bool + IsAlias () { return m_is_alias; } + + void + SetIsAlias (bool value) { m_is_alias = value; } + + virtual bool + IsMultiwordObject () { return false; } + + virtual lldb::CommandObjectSP + GetSubcommandSP (const char *sub_cmd, StringList *matches = NULL) + { + return lldb::CommandObjectSP(); + } + + virtual CommandObject * + GetSubcommandObject (const char *sub_cmd, StringList *matches = NULL) + { + return NULL; + } + + virtual void + AproposAllSubCommands (const char *prefix, + const char *search_word, + StringList &commands_found, + StringList &commands_help) + { + } + + void + GenerateHelpText (CommandReturnObject &result); + + virtual void + GenerateHelpText (Stream &result); + + // this is needed in order to allow the SBCommand class to + // transparently try and load subcommands - it will fail on + // anything but a multiword command, but it avoids us doing + // type checkings and casts + virtual bool + LoadSubCommand (const char *cmd_name, + const lldb::CommandObjectSP& command_obj) + { + return false; + } + + virtual bool + WantsRawCommandString() = 0; + + // By default, WantsCompletion = !WantsRawCommandString. + // Subclasses who want raw command string but desire, for example, + // argument completion should override this method to return true. + virtual bool + WantsCompletion() { return !WantsRawCommandString(); } + + virtual Options * + GetOptions (); + + static const ArgumentTableEntry* + GetArgumentTable (); + + static lldb::CommandArgumentType + LookupArgumentName (const char *arg_name); + + static ArgumentTableEntry * + FindArgumentDataByType (lldb::CommandArgumentType arg_type); + + int + GetNumArgumentEntries (); + + CommandArgumentEntry * + GetArgumentEntryAtIndex (int idx); + + static void + GetArgumentHelp (Stream &str, lldb::CommandArgumentType arg_type, CommandInterpreter &interpreter); + + static const char * + GetArgumentName (lldb::CommandArgumentType arg_type); + + // Generates a nicely formatted command args string for help command output. + // By default, all possible args are taken into account, for example, + // ''. This can be refined by passing a second arg + // specifying which option set(s) we are interested, which could then, for + // example, produce either '' or ''. + void + GetFormattedCommandArguments (Stream &str, uint32_t opt_set_mask = LLDB_OPT_SET_ALL); + + bool + IsPairType (ArgumentRepetitionType arg_repeat_type); + + enum + { + //---------------------------------------------------------------------- + // eFlagRequiresTarget + // + // Ensures a valid target is contained in m_exe_ctx prior to executing + // the command. If a target doesn't exist or is invalid, the command + // will fail and CommandObject::GetInvalidTargetDescription() will be + // returned as the error. CommandObject subclasses can override the + // virtual function for GetInvalidTargetDescription() to provide custom + // strings when needed. + //---------------------------------------------------------------------- + eFlagRequiresTarget = (1u << 0), + //---------------------------------------------------------------------- + // eFlagRequiresProcess + // + // Ensures a valid process is contained in m_exe_ctx prior to executing + // the command. If a process doesn't exist or is invalid, the command + // will fail and CommandObject::GetInvalidProcessDescription() will be + // returned as the error. CommandObject subclasses can override the + // virtual function for GetInvalidProcessDescription() to provide custom + // strings when needed. + //---------------------------------------------------------------------- + eFlagRequiresProcess = (1u << 1), + //---------------------------------------------------------------------- + // eFlagRequiresThread + // + // Ensures a valid thread is contained in m_exe_ctx prior to executing + // the command. If a thread doesn't exist or is invalid, the command + // will fail and CommandObject::GetInvalidThreadDescription() will be + // returned as the error. CommandObject subclasses can override the + // virtual function for GetInvalidThreadDescription() to provide custom + // strings when needed. + //---------------------------------------------------------------------- + eFlagRequiresThread = (1u << 2), + //---------------------------------------------------------------------- + // eFlagRequiresFrame + // + // Ensures a valid frame is contained in m_exe_ctx prior to executing + // the command. If a frame doesn't exist or is invalid, the command + // will fail and CommandObject::GetInvalidFrameDescription() will be + // returned as the error. CommandObject subclasses can override the + // virtual function for GetInvalidFrameDescription() to provide custom + // strings when needed. + //---------------------------------------------------------------------- + eFlagRequiresFrame = (1u << 3), + //---------------------------------------------------------------------- + // eFlagRequiresRegContext + // + // Ensures a valid register context (from the selected frame if there + // is a frame in m_exe_ctx, or from the selected thread from m_exe_ctx) + // is availble from m_exe_ctx prior to executing the command. If a + // target doesn't exist or is invalid, the command will fail and + // CommandObject::GetInvalidRegContextDescription() will be returned as + // the error. CommandObject subclasses can override the virtual function + // for GetInvalidRegContextDescription() to provide custom strings when + // needed. + //---------------------------------------------------------------------- + eFlagRequiresRegContext = (1u << 4), + //---------------------------------------------------------------------- + // eFlagTryTargetAPILock + // + // Attempts to acquire the target lock if a target is selected in the + // command interpreter. If the command object fails to acquire the API + // lock, the command will fail with an appropriate error message. + //---------------------------------------------------------------------- + eFlagTryTargetAPILock = (1u << 5), + //---------------------------------------------------------------------- + // eFlagProcessMustBeLaunched + // + // Verifies that there is a launched process in m_exe_ctx, if there + // isn't, the command will fail with an appropriate error message. + //---------------------------------------------------------------------- + eFlagProcessMustBeLaunched = (1u << 6), + //---------------------------------------------------------------------- + // eFlagProcessMustBePaused + // + // Verifies that there is a paused process in m_exe_ctx, if there + // isn't, the command will fail with an appropriate error message. + //---------------------------------------------------------------------- + eFlagProcessMustBePaused = (1u << 7) + }; + + bool + ParseOptions (Args& args, CommandReturnObject &result); + + void + SetCommandName (const char *name); + + // This function really deals with CommandObjectLists, but we didn't make a + // CommandObjectList class, so I'm sticking it here. But we really should have + // such a class. Anyway, it looks up the commands in the map that match the partial + // string cmd_str, inserts the matches into matches, and returns the number added. + + static int + AddNamesMatchingPartialString (CommandMap &in_map, const char *cmd_str, StringList &matches); + + //------------------------------------------------------------------ + /// The input array contains a parsed version of the line. The insertion + /// point is given by cursor_index (the index in input of the word containing + /// the cursor) and cursor_char_position (the position of the cursor in that word.) + /// This default version handles calling option argument completions and then calls + /// HandleArgumentCompletion if the cursor is on an argument, not an option. + /// Don't override this method, override HandleArgumentCompletion instead unless + /// you have special reasons. + /// + /// @param[in] interpreter + /// The command interpreter doing the completion. + /// + /// @param[in] input + /// The command line parsed into words + /// + /// @param[in] cursor_index + /// The index in \ainput of the word in which the cursor lies. + /// + /// @param[in] cursor_char_pos + /// The character position of the cursor in its argument word. + /// + /// @param[in] match_start_point + /// @param[in] match_return_elements + /// FIXME: Not yet implemented... If there is a match that is expensive to compute, these are + /// here to allow you to compute the completions in batches. Start the completion from \amatch_start_point, + /// and return \amatch_return_elements elements. + /// + /// @param[out] word_complete + /// \btrue if this is a complete option value (a space will be inserted after the + /// completion.) \bfalse otherwise. + /// + /// @param[out] matches + /// The array of matches returned. + /// + /// FIXME: This is the wrong return value, since we also need to make a distinction between + /// total number of matches, and the window the user wants returned. + /// + /// @return + /// \btrue if we were in an option, \bfalse otherwise. + //------------------------------------------------------------------ + virtual int + HandleCompletion (Args &input, + int &cursor_index, + int &cursor_char_position, + int match_start_point, + int max_return_elements, + bool &word_complete, + StringList &matches); + + //------------------------------------------------------------------ + /// The input array contains a parsed version of the line. The insertion + /// point is given by cursor_index (the index in input of the word containing + /// the cursor) and cursor_char_position (the position of the cursor in that word.) + /// We've constructed the map of options and their arguments as well if that is + /// helpful for the completion. + /// + /// @param[in] interpreter + /// The command interpreter doing the completion. + /// + /// @param[in] input + /// The command line parsed into words + /// + /// @param[in] cursor_index + /// The index in \ainput of the word in which the cursor lies. + /// + /// @param[in] cursor_char_pos + /// The character position of the cursor in its argument word. + /// + /// @param[in] opt_element_vector + /// The results of the options parse of \a input. + /// + /// @param[in] match_start_point + /// @param[in] match_return_elements + /// See CommandObject::HandleCompletions for a description of how these work. + /// + /// @param[out] word_complete + /// \btrue if this is a complete option value (a space will be inserted after the + /// completion.) \bfalse otherwise. + /// + /// @param[out] matches + /// The array of matches returned. + /// + /// FIXME: This is the wrong return value, since we also need to make a distinction between + /// total number of matches, and the window the user wants returned. + /// + /// @return + /// The number of completions. + //------------------------------------------------------------------ + + virtual int + HandleArgumentCompletion (Args &input, + int &cursor_index, + int &cursor_char_position, + OptionElementVector &opt_element_vector, + int match_start_point, + int max_return_elements, + bool &word_complete, + StringList &matches) + { + return 0; + } + + bool + HelpTextContainsWord (const char *search_word); + + //------------------------------------------------------------------ + /// The flags accessor. + /// + /// @return + /// A reference to the Flags member variable. + //------------------------------------------------------------------ + Flags& + GetFlags() + { + return m_flags; + } + + //------------------------------------------------------------------ + /// The flags const accessor. + /// + /// @return + /// A const reference to the Flags member variable. + //------------------------------------------------------------------ + const Flags& + GetFlags() const + { + return m_flags; + } + + //------------------------------------------------------------------ + /// Get the command that appropriate for a "repeat" of the current command. + /// + /// @param[in] current_command_line + /// The complete current command line. + /// + /// @return + /// NULL if there is no special repeat command - it will use the current command line. + /// Otherwise a pointer to the command to be repeated. + /// If the returned string is the empty string, the command won't be repeated. + //------------------------------------------------------------------ + virtual const char *GetRepeatCommand (Args ¤t_command_args, uint32_t index) + { + return NULL; + } + + CommandOverrideCallback + GetOverrideCallback () const + { + return m_command_override_callback; + } + + void * + GetOverrideCallbackBaton () const + { + return m_command_override_baton; + } + + void + SetOverrideCallback (CommandOverrideCallback callback, void *baton) + { + m_command_override_callback = callback; + m_command_override_baton = baton; + } + + virtual bool + Execute (const char *args_string, CommandReturnObject &result) = 0; + +protected: + virtual const char * + GetInvalidTargetDescription() + { + return "invalid target, create a target using the 'target create' command"; + } + + virtual const char * + GetInvalidProcessDescription() + { + return "invalid process"; + } + + virtual const char * + GetInvalidThreadDescription() + { + return "invalid thread"; + } + + virtual const char * + GetInvalidFrameDescription() + { + return "invalid frame"; + } + + virtual const char * + GetInvalidRegContextDescription () + { + return "invalid frame, no registers"; + } + + //------------------------------------------------------------------ + /// Check the command to make sure anything required by this + /// command is available. + /// + /// @param[out] result + /// A command result object, if it is not okay to run the command + /// this will be filled in with a suitable error. + /// + /// @return + /// \b true if it is okay to run this command, \b false otherwise. + //------------------------------------------------------------------ + bool + CheckRequirements (CommandReturnObject &result); + + void + Cleanup (); + + CommandInterpreter &m_interpreter; + ExecutionContext m_exe_ctx; + Mutex::Locker m_api_locker; + std::string m_cmd_name; + std::string m_cmd_help_short; + std::string m_cmd_help_long; + std::string m_cmd_syntax; + bool m_is_alias; + Flags m_flags; + std::vector m_arguments; + CommandOverrideCallback m_command_override_callback; + void * m_command_override_baton; + + // Helper function to populate IDs or ID ranges as the command argument data + // to the specified command argument entry. + static void + AddIDsArgumentData(CommandArgumentEntry &arg, lldb::CommandArgumentType ID, lldb::CommandArgumentType IDRange); + +}; + +class CommandObjectParsed : public CommandObject +{ +public: + + CommandObjectParsed (CommandInterpreter &interpreter, + const char *name, + const char *help = NULL, + const char *syntax = NULL, + uint32_t flags = 0) : + CommandObject (interpreter, name, help, syntax, flags) {} + + virtual + ~CommandObjectParsed () {}; + + virtual bool + Execute (const char *args_string, CommandReturnObject &result); + +protected: + virtual bool + DoExecute (Args& command, + CommandReturnObject &result) = 0; + + virtual bool + WantsRawCommandString() { return false; }; +}; + +class CommandObjectRaw : public CommandObject +{ +public: + + CommandObjectRaw (CommandInterpreter &interpreter, + const char *name, + const char *help = NULL, + const char *syntax = NULL, + uint32_t flags = 0) : + CommandObject (interpreter, name, help, syntax, flags) {} + + virtual + ~CommandObjectRaw () {}; + + virtual bool + Execute (const char *args_string, CommandReturnObject &result); + +protected: + virtual bool + DoExecute (const char *command, CommandReturnObject &result) = 0; + + virtual bool + WantsRawCommandString() { return true; }; +}; + + +} // namespace lldb_private + + +#endif // liblldb_CommandObject_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Interpreter/CommandObjectMultiword.h b/contrib/llvm/tools/lldb/include/lldb/Interpreter/CommandObjectMultiword.h new file mode 100644 index 00000000000..491d43c4bd9 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Interpreter/CommandObjectMultiword.h @@ -0,0 +1,187 @@ +//===-- CommandObjectMultiword.h --------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_CommandObjectMultiword_h_ +#define liblldb_CommandObjectMultiword_h_ + +// C Includes +// C++ Includes +#include + +// Other libraries and framework includes +// Project includes +#include "lldb/Interpreter/CommandObject.h" + +namespace lldb_private { + +//------------------------------------------------------------------------- +// CommandObjectMultiword +//------------------------------------------------------------------------- + +class CommandObjectMultiword : public CommandObject +{ +// These two want to iterate over the subcommand dictionary. +friend class CommandInterpreter; +friend class CommandObjectSyntax; +public: + CommandObjectMultiword (CommandInterpreter &interpreter, + const char *name, + const char *help = NULL, + const char *syntax = NULL, + uint32_t flags = 0); + + virtual + ~CommandObjectMultiword (); + + virtual bool + IsMultiwordObject () { return true; } + + virtual bool + LoadSubCommand (const char *cmd_name, + const lldb::CommandObjectSP& command_obj); + + virtual void + GenerateHelpText (Stream &output_stream); + + virtual lldb::CommandObjectSP + GetSubcommandSP (const char *sub_cmd, StringList *matches = NULL); + + virtual CommandObject * + GetSubcommandObject (const char *sub_cmd, StringList *matches = NULL); + + virtual void + AproposAllSubCommands (const char *prefix, + const char *search_word, + StringList &commands_found, + StringList &commands_help); + + virtual bool + WantsRawCommandString() { return false; }; + + virtual int + HandleCompletion (Args &input, + int &cursor_index, + int &cursor_char_position, + int match_start_point, + int max_return_elements, + bool &word_complete, + StringList &matches); + + virtual const char *GetRepeatCommand (Args ¤t_command_args, uint32_t index); + + virtual bool + Execute (const char *args_string, + CommandReturnObject &result); + + virtual bool + IsRemovable() const { return m_can_be_removed; } + + void + SetRemovable (bool removable) + { + m_can_be_removed = removable; + } + +protected: + + CommandObject::CommandMap m_subcommand_dict; + bool m_can_be_removed; +}; + + +class CommandObjectProxy : public CommandObject +{ +public: + CommandObjectProxy (CommandInterpreter &interpreter, + const char *name, + const char *help = NULL, + const char *syntax = NULL, + uint32_t flags = 0); + + virtual + ~CommandObjectProxy (); + + // Subclasses must provide a command object that will be transparently + // used for this object. + virtual CommandObject * + GetProxyCommandObject() = 0; + + virtual const char * + GetHelpLong (); + + virtual bool + IsRemovable() const; + + virtual bool + IsMultiwordObject (); + + virtual lldb::CommandObjectSP + GetSubcommandSP (const char *sub_cmd, StringList *matches = NULL); + + virtual CommandObject * + GetSubcommandObject (const char *sub_cmd, StringList *matches = NULL); + + virtual void + AproposAllSubCommands (const char *prefix, + const char *search_word, + StringList &commands_found, + StringList &commands_help); + + virtual bool + LoadSubCommand (const char *cmd_name, + const lldb::CommandObjectSP& command_obj); + + virtual bool + WantsRawCommandString(); + + virtual bool + WantsCompletion(); + + virtual Options * + GetOptions (); + + + virtual int + HandleCompletion (Args &input, + int &cursor_index, + int &cursor_char_position, + int match_start_point, + int max_return_elements, + bool &word_complete, + StringList &matches); + + virtual int + HandleArgumentCompletion (Args &input, + int &cursor_index, + int &cursor_char_position, + OptionElementVector &opt_element_vector, + int match_start_point, + int max_return_elements, + bool &word_complete, + StringList &matches); + + virtual const char * + GetRepeatCommand (Args ¤t_command_args, + uint32_t index); + + virtual bool + Execute (const char *args_string, + CommandReturnObject &result); + +protected: + + // These two want to iterate over the subcommand dictionary. + friend class CommandInterpreter; + friend class CommandObjectSyntax; + +}; + +} // namespace lldb_private + +#endif // liblldb_CommandObjectMultiword_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Interpreter/CommandObjectRegexCommand.h b/contrib/llvm/tools/lldb/include/lldb/Interpreter/CommandObjectRegexCommand.h new file mode 100644 index 00000000000..8855680d5f8 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Interpreter/CommandObjectRegexCommand.h @@ -0,0 +1,81 @@ +//===-- CommandObjectRegexCommand.h -----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_CommandObjectRegexCommand_h_ +#define liblldb_CommandObjectRegexCommand_h_ + +// C Includes +// C++ Includes +#include + +// Other libraries and framework includes +// Project includes +#include "lldb/Core/RegularExpression.h" +#include "lldb/Interpreter/CommandObject.h" + +namespace lldb_private { + +//------------------------------------------------------------------------- +// CommandObjectRegexCommand +//------------------------------------------------------------------------- + +class CommandObjectRegexCommand : public CommandObjectRaw +{ +public: + + CommandObjectRegexCommand (CommandInterpreter &interpreter, + const char *name, + const char *help, + const char *syntax, + uint32_t max_matches, + uint32_t completion_type_mask = 0); + + virtual + ~CommandObjectRegexCommand (); + + bool + AddRegexCommand (const char *re_cstr, const char *command_cstr); + + bool + HasRegexEntries () const + { + return !m_entries.empty(); + } + + virtual int + HandleCompletion (Args &input, + int &cursor_index, + int &cursor_char_position, + int match_start_point, + int max_return_elements, + bool &word_complete, + StringList &matches); + +protected: + virtual bool + DoExecute (const char *command, CommandReturnObject &result); + + struct Entry + { + RegularExpression regex; + std::string command; + }; + + typedef std::list EntryCollection; + const uint32_t m_max_matches; + const uint32_t m_completion_type_mask; + EntryCollection m_entries; + +private: + DISALLOW_COPY_AND_ASSIGN (CommandObjectRegexCommand); +}; + +} // namespace lldb_private + +#endif // liblldb_CommandObjectRegexCommand_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Interpreter/CommandReturnObject.h b/contrib/llvm/tools/lldb/include/lldb/Interpreter/CommandReturnObject.h new file mode 100644 index 00000000000..acd03992e5e --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Interpreter/CommandReturnObject.h @@ -0,0 +1,183 @@ +//===-- CommandReturnObject.h -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_CommandReturnObject_h_ +#define liblldb_CommandReturnObject_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private.h" +#include "lldb/Core/STLUtils.h" +#include "lldb/Core/StreamFile.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Core/StreamTee.h" + +namespace lldb_private { + + +class CommandReturnObject +{ +public: + + CommandReturnObject (); + + ~CommandReturnObject (); + + const char * + GetOutputData () + { + lldb::StreamSP stream_sp (m_out_stream.GetStreamAtIndex (eStreamStringIndex)); + if (stream_sp) + return static_cast(stream_sp.get())->GetData(); + return ""; + } + + const char * + GetErrorData () + { + lldb::StreamSP stream_sp (m_err_stream.GetStreamAtIndex (eStreamStringIndex)); + if (stream_sp) + return static_cast(stream_sp.get())->GetData(); + else + return ""; + } + + Stream & + GetOutputStream () + { + // Make sure we at least have our normal string stream output stream + lldb::StreamSP stream_sp (m_out_stream.GetStreamAtIndex (eStreamStringIndex)); + if (!stream_sp) + { + stream_sp.reset (new StreamString()); + m_out_stream.SetStreamAtIndex (eStreamStringIndex, stream_sp); + } + return m_out_stream; + } + + Stream & + GetErrorStream () + { + // Make sure we at least have our normal string stream output stream + lldb::StreamSP stream_sp (m_err_stream.GetStreamAtIndex (eStreamStringIndex)); + if (!stream_sp) + { + stream_sp.reset (new StreamString()); + m_err_stream.SetStreamAtIndex (eStreamStringIndex, stream_sp); + } + return m_err_stream; + } + + void + SetImmediateOutputFile (FILE *fh, bool transfer_fh_ownership = false) + { + lldb::StreamSP stream_sp (new StreamFile (fh, transfer_fh_ownership)); + m_out_stream.SetStreamAtIndex (eImmediateStreamIndex, stream_sp); + } + + void + SetImmediateErrorFile (FILE *fh, bool transfer_fh_ownership = false) + { + lldb::StreamSP stream_sp (new StreamFile (fh, transfer_fh_ownership)); + m_err_stream.SetStreamAtIndex (eImmediateStreamIndex, stream_sp); + } + + void + SetImmediateOutputStream (const lldb::StreamSP &stream_sp) + { + m_out_stream.SetStreamAtIndex (eImmediateStreamIndex, stream_sp); + } + + void + SetImmediateErrorStream (const lldb::StreamSP &stream_sp) + { + m_err_stream.SetStreamAtIndex (eImmediateStreamIndex, stream_sp); + } + + lldb::StreamSP + GetImmediateOutputStream () + { + return m_out_stream.GetStreamAtIndex (eImmediateStreamIndex); + } + + lldb::StreamSP + GetImmediateErrorStream () + { + return m_err_stream.GetStreamAtIndex (eImmediateStreamIndex); + } + + void + Clear(); + + void + AppendMessage (const char *in_string); + + void + AppendMessageWithFormat (const char *format, ...) __attribute__ ((format (printf, 2, 3))); + + void + AppendRawWarning (const char *in_string); + + void + AppendWarning (const char *in_string); + + void + AppendWarningWithFormat (const char *format, ...) __attribute__ ((format (printf, 2, 3))); + + void + AppendError (const char *in_string); + + void + AppendRawError (const char *in_string); + + void + AppendErrorWithFormat (const char *format, ...) __attribute__ ((format (printf, 2, 3))); + + void + SetError (const Error &error, + const char *fallback_error_cstr = NULL); + + void + SetError (const char *error_cstr); + + lldb::ReturnStatus + GetStatus(); + + void + SetStatus (lldb::ReturnStatus status); + + bool + Succeeded (); + + bool + HasResult (); + + bool GetDidChangeProcessState (); + + void SetDidChangeProcessState (bool b); + +private: + enum + { + eStreamStringIndex = 0, + eImmediateStreamIndex = 1 + }; + + StreamTee m_out_stream; + StreamTee m_err_stream; + + lldb::ReturnStatus m_status; + bool m_did_change_process_state; +}; + +} // namespace lldb_private + +#endif // liblldb_CommandReturnObject_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Interpreter/OptionGroupArchitecture.h b/contrib/llvm/tools/lldb/include/lldb/Interpreter/OptionGroupArchitecture.h new file mode 100644 index 00000000000..7cd1ca3d710 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Interpreter/OptionGroupArchitecture.h @@ -0,0 +1,73 @@ +//===-- OptionGroupArchitecture.h -------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_OptionGroupArchitecture_h_ +#define liblldb_OptionGroupArchitecture_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Interpreter/Options.h" +#include "lldb/Core/ArchSpec.h" + +namespace lldb_private { + +//------------------------------------------------------------------------- +// OptionGroupArchitecture +//------------------------------------------------------------------------- + +class OptionGroupArchitecture : public OptionGroup +{ +public: + + OptionGroupArchitecture (); + + virtual + ~OptionGroupArchitecture (); + + + virtual uint32_t + GetNumDefinitions (); + + virtual const OptionDefinition* + GetDefinitions (); + + virtual Error + SetOptionValue (CommandInterpreter &interpreter, + uint32_t option_idx, + const char *option_value); + + virtual void + OptionParsingStarting (CommandInterpreter &interpreter); + + bool + GetArchitecture (Platform *platform, ArchSpec &arch); + + bool + ArchitectureWasSpecified () const + { + return !m_arch_str.empty(); + } + const char * + GetArchitectureName () + { + if (m_arch_str.empty()) + return NULL; + return m_arch_str.c_str(); + } + +protected: + + std::string m_arch_str; // Save the arch triple in case a platform is specified after the architecture +}; + +} // namespace lldb_private + +#endif // liblldb_OptionGroupArchitecture_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Interpreter/OptionGroupBoolean.h b/contrib/llvm/tools/lldb/include/lldb/Interpreter/OptionGroupBoolean.h new file mode 100644 index 00000000000..0d861b24169 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Interpreter/OptionGroupBoolean.h @@ -0,0 +1,83 @@ +//===-- OptionGroupBoolean.h ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_OptionGroupBoolean_h_ +#define liblldb_OptionGroupBoolean_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Interpreter/Options.h" +#include "lldb/Interpreter/OptionValueBoolean.h" + +namespace lldb_private { + //------------------------------------------------------------------------- + // OptionGroupBoolean + //------------------------------------------------------------------------- + + class OptionGroupBoolean : public OptionGroup + { + public: + // When 'no_argument_toggle_default' is true, then setting the option + // value does NOT require an argument, it sets the boolean value to the + // inverse of the default value + OptionGroupBoolean (uint32_t usage_mask, + bool required, + const char *long_option, + int short_option, + const char *usage_text, + bool default_value, + bool no_argument_toggle_default); + + virtual + ~OptionGroupBoolean (); + + + virtual uint32_t + GetNumDefinitions () + { + return 1; + } + + virtual const OptionDefinition* + GetDefinitions () + { + return &m_option_definition; + } + + virtual Error + SetOptionValue (CommandInterpreter &interpreter, + uint32_t option_idx, + const char *option_value); + + virtual void + OptionParsingStarting (CommandInterpreter &interpreter); + + OptionValueBoolean & + GetOptionValue () + { + return m_value; + } + + const OptionValueBoolean & + GetOptionValue () const + { + return m_value; + } + + protected: + OptionValueBoolean m_value; + OptionDefinition m_option_definition; + + }; + +} // namespace lldb_private + +#endif // liblldb_OptionGroupBoolean_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Interpreter/OptionGroupFile.h b/contrib/llvm/tools/lldb/include/lldb/Interpreter/OptionGroupFile.h new file mode 100644 index 00000000000..632a2dbdf22 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Interpreter/OptionGroupFile.h @@ -0,0 +1,142 @@ +//===-- OptionGroupFile.h -------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_OptionGroupFile_h_ +#define liblldb_OptionGroupFile_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Interpreter/Options.h" +#include "lldb/Interpreter/OptionValueFileSpec.h" +#include "lldb/Interpreter/OptionValueFileSpecList.h" + +namespace lldb_private { + +//------------------------------------------------------------------------- +// OptionGroupFile +//------------------------------------------------------------------------- + +class OptionGroupFile : public OptionGroup +{ +public: + + OptionGroupFile (uint32_t usage_mask, + bool required, + const char *long_option, + int short_option, + uint32_t completion_type, + lldb::CommandArgumentType argument_type, + const char *usage_text); + + virtual + ~OptionGroupFile (); + + + virtual uint32_t + GetNumDefinitions () + { + return 1; + } + + virtual const OptionDefinition* + GetDefinitions () + { + return &m_option_definition; + } + + virtual Error + SetOptionValue (CommandInterpreter &interpreter, + uint32_t option_idx, + const char *option_value); + + virtual void + OptionParsingStarting (CommandInterpreter &interpreter); + + OptionValueFileSpec & + GetOptionValue () + { + return m_file; + } + + const OptionValueFileSpec & + GetOptionValue () const + { + return m_file; + } + +protected: + OptionValueFileSpec m_file; + OptionDefinition m_option_definition; + +}; + +//------------------------------------------------------------------------- +// OptionGroupFileList +//------------------------------------------------------------------------- + +class OptionGroupFileList : public OptionGroup +{ +public: + + OptionGroupFileList (uint32_t usage_mask, + bool required, + const char *long_option, + int short_option, + uint32_t completion_type, + lldb::CommandArgumentType argument_type, + const char *usage_text); + + virtual + ~OptionGroupFileList (); + + + virtual uint32_t + GetNumDefinitions () + { + return 1; + } + + virtual const OptionDefinition* + GetDefinitions () + { + return &m_option_definition; + } + + virtual Error + SetOptionValue (CommandInterpreter &interpreter, + uint32_t option_idx, + const char *option_value); + + virtual void + OptionParsingStarting (CommandInterpreter &interpreter); + + + OptionValueFileSpecList & + GetOptionValue () + { + return m_file_list; + } + + const OptionValueFileSpecList & + GetOptionValue () const + { + return m_file_list; + } + +protected: + OptionValueFileSpecList m_file_list; + OptionDefinition m_option_definition; + +}; + +} // namespace lldb_private + +#endif // liblldb_OptionGroupFile_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Interpreter/OptionGroupFormat.h b/contrib/llvm/tools/lldb/include/lldb/Interpreter/OptionGroupFormat.h new file mode 100644 index 00000000000..7419b049666 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Interpreter/OptionGroupFormat.h @@ -0,0 +1,133 @@ +//===-- OptionGroupFormat.h -------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_OptionGroupFormat_h_ +#define liblldb_OptionGroupFormat_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Interpreter/Options.h" +#include "lldb/Interpreter/OptionValueFormat.h" +#include "lldb/Interpreter/OptionValueSInt64.h" +#include "lldb/Interpreter/OptionValueUInt64.h" + +namespace lldb_private { + +//------------------------------------------------------------------------- +// OptionGroupFormat +//------------------------------------------------------------------------- + +class OptionGroupFormat : public OptionGroup +{ +public: + static const uint32_t OPTION_GROUP_FORMAT = LLDB_OPT_SET_1; + static const uint32_t OPTION_GROUP_GDB_FMT = LLDB_OPT_SET_2; + static const uint32_t OPTION_GROUP_SIZE = LLDB_OPT_SET_3; + static const uint32_t OPTION_GROUP_COUNT = LLDB_OPT_SET_4; + + OptionGroupFormat (lldb::Format default_format, + uint64_t default_byte_size = UINT64_MAX, // Pass UINT64_MAX to disable the "--size" option + uint64_t default_count = UINT64_MAX); // Pass UINT64_MAX to disable the "--count" option + + virtual + ~OptionGroupFormat (); + + + virtual uint32_t + GetNumDefinitions (); + + virtual const OptionDefinition* + GetDefinitions (); + + virtual Error + SetOptionValue (CommandInterpreter &interpreter, + uint32_t option_idx, + const char *option_value); + + virtual void + OptionParsingStarting (CommandInterpreter &interpreter); + + lldb::Format + GetFormat () const + { + return m_format.GetCurrentValue(); + } + + OptionValueFormat & + GetFormatValue() + { + return m_format; + } + + const OptionValueFormat & + GetFormatValue() const + { + return m_format; + } + + OptionValueUInt64 & + GetByteSizeValue() + { + return m_byte_size; + } + + const OptionValueUInt64 & + GetByteSizeValue() const + { + return m_byte_size; + } + + OptionValueUInt64 & + GetCountValue() + { + return m_count; + } + + const OptionValueUInt64 & + GetCountValue() const + { + return m_count; + } + + bool + HasGDBFormat () const + { + return m_has_gdb_format; + } + + bool + AnyOptionWasSet () const + { + return m_format.OptionWasSet() || + m_byte_size.OptionWasSet() || + m_count.OptionWasSet(); + } + +protected: + + bool + ParserGDBFormatLetter (CommandInterpreter &interpreter, + char format_letter, + lldb::Format &format, + uint32_t &byte_size); + + OptionValueFormat m_format; + OptionValueUInt64 m_byte_size; + OptionValueUInt64 m_count; + char m_prev_gdb_format; + char m_prev_gdb_size; + + bool m_has_gdb_format; +}; + +} // namespace lldb_private + +#endif // liblldb_OptionGroupFormat_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Interpreter/OptionGroupOutputFile.h b/contrib/llvm/tools/lldb/include/lldb/Interpreter/OptionGroupOutputFile.h new file mode 100644 index 00000000000..533cd6ee8eb --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Interpreter/OptionGroupOutputFile.h @@ -0,0 +1,76 @@ +//===-- OptionGroupOutputFile.h -------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_OptionGroupOutputFile_h_ +#define liblldb_OptionGroupOutputFile_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Interpreter/Options.h" +#include "lldb/Interpreter/OptionValueBoolean.h" +#include "lldb/Interpreter/OptionValueFileSpec.h" + +namespace lldb_private { +//------------------------------------------------------------------------- +// OptionGroupOutputFile +//------------------------------------------------------------------------- + +class OptionGroupOutputFile : public OptionGroup +{ +public: + + OptionGroupOutputFile (); + + virtual + ~OptionGroupOutputFile (); + + + virtual uint32_t + GetNumDefinitions (); + + virtual const OptionDefinition* + GetDefinitions (); + + virtual Error + SetOptionValue (CommandInterpreter &interpreter, + uint32_t option_idx, + const char *option_value); + + virtual void + OptionParsingStarting (CommandInterpreter &interpreter); + + const OptionValueFileSpec & + GetFile () + { + return m_file; + } + + const OptionValueBoolean & + GetAppend () + { + return m_append; + } + + bool + AnyOptionWasSet () const + { + return m_file.OptionWasSet() || m_append.OptionWasSet(); + } + +protected: + OptionValueFileSpec m_file; + OptionValueBoolean m_append; + +}; + +} // namespace lldb_private + +#endif // liblldb_OptionGroupOutputFile_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Interpreter/OptionGroupPlatform.h b/contrib/llvm/tools/lldb/include/lldb/Interpreter/OptionGroupPlatform.h new file mode 100644 index 00000000000..970ad328ccb --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Interpreter/OptionGroupPlatform.h @@ -0,0 +1,120 @@ +//===-- OptionGroupPlatform.h -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_OptionGroupPlatform_h_ +#define liblldb_OptionGroupPlatform_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/ConstString.h" +#include "lldb/Interpreter/Options.h" + +namespace lldb_private { + +//------------------------------------------------------------------------- +// PlatformOptionGroup +// +// Make platform options available to any commands that need the settings. +//------------------------------------------------------------------------- +class OptionGroupPlatform : public OptionGroup +{ +public: + + OptionGroupPlatform (bool include_platform_option) : + OptionGroup(), + m_platform_name (), + m_sdk_sysroot (), + m_os_version_major (UINT32_MAX), + m_os_version_minor (UINT32_MAX), + m_os_version_update (UINT32_MAX), + m_include_platform_option (include_platform_option) + { + } + + virtual + ~OptionGroupPlatform () + { + } + + virtual uint32_t + GetNumDefinitions (); + + virtual const OptionDefinition* + GetDefinitions (); + + virtual Error + SetOptionValue (CommandInterpreter &interpreter, + uint32_t option_idx, + const char *option_value); + + virtual void + OptionParsingStarting (CommandInterpreter &interpreter); + + lldb::PlatformSP + CreatePlatformWithOptions (CommandInterpreter &interpreter, + const ArchSpec &arch, + bool make_selected, + Error& error, + ArchSpec &platform_arch) const; + + bool + PlatformWasSpecified () const + { + return !m_platform_name.empty(); + } + + void + SetPlatformName (const char *platform_name) + { + if (platform_name && platform_name[0]) + m_platform_name.assign (platform_name); + else + m_platform_name.clear(); + } + + const ConstString & + GetSDKRootDirectory () const + { + return m_sdk_sysroot; + } + + void + SetSDKRootDirectory (const ConstString &sdk_root_directory) + { + m_sdk_sysroot = sdk_root_directory; + } + + const ConstString & + GetSDKBuild () const + { + return m_sdk_build; + } + + void + SetSDKBuild (const ConstString &sdk_build) + { + m_sdk_build = sdk_build; + } + + +protected: + std::string m_platform_name; + ConstString m_sdk_sysroot; + ConstString m_sdk_build; + uint32_t m_os_version_major; + uint32_t m_os_version_minor; + uint32_t m_os_version_update; + bool m_include_platform_option; +}; + +} // namespace lldb_private + +#endif // liblldb_OptionGroupPlatform_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Interpreter/OptionGroupString.h b/contrib/llvm/tools/lldb/include/lldb/Interpreter/OptionGroupString.h new file mode 100644 index 00000000000..e62a81bc411 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Interpreter/OptionGroupString.h @@ -0,0 +1,82 @@ +//===-- OptionGroupString.h ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_OptionGroupString_h_ +#define liblldb_OptionGroupString_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Interpreter/Options.h" +#include "lldb/Interpreter/OptionValueString.h" + +namespace lldb_private { + //------------------------------------------------------------------------- + // OptionGroupString + //------------------------------------------------------------------------- + + class OptionGroupString : public OptionGroup + { + public: + + OptionGroupString (uint32_t usage_mask, + bool required, + const char *long_option, + int short_option, + uint32_t completion_type, + lldb::CommandArgumentType argument_type, + const char *usage_text, + const char *default_value); + + virtual + ~OptionGroupString (); + + + virtual uint32_t + GetNumDefinitions () + { + return 1; + } + + virtual const OptionDefinition* + GetDefinitions () + { + return &m_option_definition; + } + + virtual Error + SetOptionValue (CommandInterpreter &interpreter, + uint32_t option_idx, + const char *option_value); + + virtual void + OptionParsingStarting (CommandInterpreter &interpreter); + + OptionValueString & + GetOptionValue () + { + return m_value; + } + + const OptionValueString & + GetOptionValue () const + { + return m_value; + } + + protected: + OptionValueString m_value; + OptionDefinition m_option_definition; + + }; + +} // namespace lldb_private + +#endif // liblldb_OptionGroupString_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Interpreter/OptionGroupUInt64.h b/contrib/llvm/tools/lldb/include/lldb/Interpreter/OptionGroupUInt64.h new file mode 100644 index 00000000000..c5f9e85d2f8 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Interpreter/OptionGroupUInt64.h @@ -0,0 +1,82 @@ +//===-- OptionGroupUInt64.h ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_OptionGroupUInt64_h_ +#define liblldb_OptionGroupUInt64_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Interpreter/Options.h" +#include "lldb/Interpreter/OptionValueUInt64.h" + +namespace lldb_private { + //------------------------------------------------------------------------- + // OptionGroupUInt64 + //------------------------------------------------------------------------- + + class OptionGroupUInt64 : public OptionGroup + { + public: + + OptionGroupUInt64 (uint32_t usage_mask, + bool required, + const char *long_option, + int short_option, + uint32_t completion_type, + lldb::CommandArgumentType argument_type, + const char *usage_text, + uint64_t default_value); + + virtual + ~OptionGroupUInt64 (); + + + virtual uint32_t + GetNumDefinitions () + { + return 1; + } + + virtual const OptionDefinition* + GetDefinitions () + { + return &m_option_definition; + } + + virtual Error + SetOptionValue (CommandInterpreter &interpreter, + uint32_t option_idx, + const char *option_value); + + virtual void + OptionParsingStarting (CommandInterpreter &interpreter); + + OptionValueUInt64 & + GetOptionValue () + { + return m_value; + } + + const OptionValueUInt64 & + GetOptionValue () const + { + return m_value; + } + + protected: + OptionValueUInt64 m_value; + OptionDefinition m_option_definition; + + }; + +} // namespace lldb_private + +#endif // liblldb_OptionGroupUInt64_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Interpreter/OptionGroupUUID.h b/contrib/llvm/tools/lldb/include/lldb/Interpreter/OptionGroupUUID.h new file mode 100644 index 00000000000..ea968d73796 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Interpreter/OptionGroupUUID.h @@ -0,0 +1,61 @@ +//===-- OptionGroupUUID.h ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_OptionGroupUUID_h_ +#define liblldb_OptionGroupUUID_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Interpreter/Options.h" +#include "lldb/Interpreter/OptionValueUUID.h" + +namespace lldb_private { +//------------------------------------------------------------------------- +// OptionGroupUUID +//------------------------------------------------------------------------- + +class OptionGroupUUID : public OptionGroup +{ +public: + + OptionGroupUUID (); + + virtual + ~OptionGroupUUID (); + + + virtual uint32_t + GetNumDefinitions (); + + virtual const OptionDefinition* + GetDefinitions (); + + virtual Error + SetOptionValue (CommandInterpreter &interpreter, + uint32_t option_idx, + const char *option_value); + + virtual void + OptionParsingStarting (CommandInterpreter &interpreter); + + const OptionValueUUID & + GetOptionValue () const + { + return m_uuid; + } + +protected: + OptionValueUUID m_uuid; +}; + +} // namespace lldb_private + +#endif // liblldb_OptionGroupUUID_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Interpreter/OptionGroupValueObjectDisplay.h b/contrib/llvm/tools/lldb/include/lldb/Interpreter/OptionGroupValueObjectDisplay.h new file mode 100644 index 00000000000..da05e127d5d --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Interpreter/OptionGroupValueObjectDisplay.h @@ -0,0 +1,85 @@ +//===-- OptionGroupValueObjectDisplay.h -------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_OptionGroupValueObjectDisplay_h_ +#define liblldb_OptionGroupValueObjectDisplay_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/ValueObject.h" +#include "lldb/Interpreter/Options.h" + +namespace lldb_private { + +//------------------------------------------------------------------------- +// OptionGroupValueObjectDisplay +//------------------------------------------------------------------------- + +class OptionGroupValueObjectDisplay : public OptionGroup +{ +public: + + OptionGroupValueObjectDisplay (); + + virtual + ~OptionGroupValueObjectDisplay (); + + + virtual uint32_t + GetNumDefinitions (); + + virtual const OptionDefinition* + GetDefinitions (); + + virtual Error + SetOptionValue (CommandInterpreter &interpreter, + uint32_t option_idx, + const char *option_value); + + virtual void + OptionParsingStarting (CommandInterpreter &interpreter); + + bool + AnyOptionWasSet () const + { + return show_types == true || + no_summary_depth != 0 || + show_location == true || + flat_output == true || + use_objc == true || + max_depth != UINT32_MAX || + ptr_depth != 0 || + use_synth == false || + be_raw == true || + ignore_cap == true; + } + + ValueObject::DumpValueObjectOptions + GetAsDumpOptions (bool objc_is_compact = false, + lldb::Format format = lldb::eFormatDefault, + lldb::TypeSummaryImplSP summary_sp = lldb::TypeSummaryImplSP()); + + bool show_types; + uint32_t no_summary_depth; + bool show_location; + bool flat_output; + bool use_objc; + uint32_t max_depth; + uint32_t ptr_depth; + lldb::DynamicValueType use_dynamic; + bool use_synth; + bool be_raw; + bool ignore_cap; +}; + +} // namespace lldb_private + +#endif // liblldb_OptionGroupValueObjectDisplay_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Interpreter/OptionGroupVariable.h b/contrib/llvm/tools/lldb/include/lldb/Interpreter/OptionGroupVariable.h new file mode 100644 index 00000000000..40f4d436bc6 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Interpreter/OptionGroupVariable.h @@ -0,0 +1,65 @@ +//===-- OptionGroupVariable.h -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_OptionGroupVariable_h_ +#define liblldb_OptionGroupVariable_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Interpreter/OptionValueString.h" +#include "lldb/Interpreter/Options.h" + +namespace lldb_private { + +//------------------------------------------------------------------------- +// OptionGroupVariable +//------------------------------------------------------------------------- + + class OptionGroupVariable : public OptionGroup + { + public: + + OptionGroupVariable (bool show_frame_options); + + virtual + ~OptionGroupVariable (); + + virtual uint32_t + GetNumDefinitions (); + + virtual const OptionDefinition* + GetDefinitions (); + + virtual Error + SetOptionValue (CommandInterpreter &interpreter, + uint32_t option_idx, + const char *option_arg); + + virtual void + OptionParsingStarting (CommandInterpreter &interpreter); + + bool include_frame_options:1, + show_args:1, // Frame option only (include_frame_options == true) + show_locals:1, // Frame option only (include_frame_options == true) + show_globals:1, // Frame option only (include_frame_options == true) + use_regex:1, + show_scope:1, + show_decl:1; + OptionValueString summary; // the name of a named summary + OptionValueString summary_string; // a summary string + + private: + DISALLOW_COPY_AND_ASSIGN(OptionGroupVariable); + }; + +} // namespace lldb_private + +#endif // liblldb_OptionGroupVariable_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Interpreter/OptionGroupWatchpoint.h b/contrib/llvm/tools/lldb/include/lldb/Interpreter/OptionGroupWatchpoint.h new file mode 100644 index 00000000000..1298da80750 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Interpreter/OptionGroupWatchpoint.h @@ -0,0 +1,71 @@ +//===-- OptionGroupWatchpoint.h ---------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_OptionGroupWatchpoint_h_ +#define liblldb_OptionGroupWatchpoint_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Interpreter/Options.h" + +namespace lldb_private { + +//------------------------------------------------------------------------- +// OptionGroupWatchpoint +//------------------------------------------------------------------------- + + class OptionGroupWatchpoint : public OptionGroup + { + public: + + static bool + IsWatchSizeSupported(uint32_t watch_size); + + OptionGroupWatchpoint (); + + virtual + ~OptionGroupWatchpoint (); + + virtual uint32_t + GetNumDefinitions (); + + virtual const OptionDefinition* + GetDefinitions (); + + virtual Error + SetOptionValue (CommandInterpreter &interpreter, + uint32_t option_idx, + const char *option_arg); + + virtual void + OptionParsingStarting (CommandInterpreter &interpreter); + + // Note: + // eWatchRead == LLDB_WATCH_TYPE_READ; and + // eWatchWrite == LLDB_WATCH_TYPE_WRITE + typedef enum WatchType { + eWatchInvalid = 0, + eWatchRead, + eWatchWrite, + eWatchReadWrite + } WatchType; + + WatchType watch_type; + uint32_t watch_size; + bool watch_type_specified; + + private: + DISALLOW_COPY_AND_ASSIGN(OptionGroupWatchpoint); + }; + +} // namespace lldb_private + +#endif // liblldb_OptionGroupWatchpoint_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Interpreter/OptionValue.h b/contrib/llvm/tools/lldb/include/lldb/Interpreter/OptionValue.h new file mode 100644 index 00000000000..33e7fc5f818 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Interpreter/OptionValue.h @@ -0,0 +1,384 @@ +//===-- OptionValue.h --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_OptionValue_h_ +#define liblldb_OptionValue_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-defines.h" +#include "lldb/Core/ConstString.h" +#include "lldb/Core/Error.h" + +namespace lldb_private { + + //--------------------------------------------------------------------- + // OptionValue + //--------------------------------------------------------------------- + class OptionValue + { + public: + typedef enum { + eTypeInvalid = 0, + eTypeArch, + eTypeArgs, + eTypeArray, + eTypeBoolean, + eTypeDictionary, + eTypeEnum, + eTypeFileSpec, + eTypeFileSpecList, + eTypeFormat, + eTypePathMap, + eTypeProperties, + eTypeRegex, + eTypeSInt64, + eTypeString, + eTypeUInt64, + eTypeUUID + } Type; + + enum { + eDumpOptionName = (1u << 0), + eDumpOptionType = (1u << 1), + eDumpOptionValue = (1u << 2), + eDumpOptionDescription = (1u << 3), + eDumpOptionRaw = (1u << 4), + eDumpGroupValue = (eDumpOptionName | eDumpOptionType | eDumpOptionValue), + eDumpGroupHelp = (eDumpOptionName | eDumpOptionType | eDumpOptionDescription) + }; + + + OptionValue () : + m_value_was_set (false) + { + } + + OptionValue (const OptionValue &rhs) : + m_value_was_set (rhs.m_value_was_set) + { + } + + virtual ~OptionValue () + { + } + //----------------------------------------------------------------- + // Subclasses should override these functions + //----------------------------------------------------------------- + virtual Type + GetType () const = 0; + + // If this value is always hidden, the avoid showing any info on this + // value, just show the info for the child values. + virtual bool + ValueIsTransparent () const + { + return GetType() == eTypeProperties; + } + + virtual const char * + GetTypeAsCString () const + { + return GetBuiltinTypeAsCString(GetType()); + } + + + static const char * + GetBuiltinTypeAsCString (Type t); + + virtual void + DumpValue (const ExecutionContext *exe_ctx, Stream &strm, uint32_t dump_mask) = 0; + + virtual Error + SetValueFromCString (const char *value, VarSetOperationType op = eVarSetOperationAssign); + + virtual bool + Clear () = 0; + + virtual lldb::OptionValueSP + DeepCopy () const = 0; + + virtual size_t + AutoComplete (CommandInterpreter &interpreter, + const char *s, + int match_start_point, + int max_return_elements, + bool &word_complete, + StringList &matches); + + //----------------------------------------------------------------- + // Subclasses can override these functions + //----------------------------------------------------------------- + virtual lldb::OptionValueSP + GetSubValue (const ExecutionContext *exe_ctx, + const char *name, + bool will_modify, + Error &error) const + { + error.SetErrorStringWithFormat("'%s' is not a value subvalue", name); + return lldb::OptionValueSP(); + } + + virtual Error + SetSubValue (const ExecutionContext *exe_ctx, + VarSetOperationType op, + const char *name, + const char *value); + + virtual bool + IsAggregateValue () const + { + return false; + } + + virtual ConstString + GetName() const + { + return ConstString(); + } + + virtual bool + DumpQualifiedName (Stream &strm) const; + //----------------------------------------------------------------- + // Subclasses should NOT override these functions as they use the + // above functions to implement functionality + //----------------------------------------------------------------- + uint32_t + GetTypeAsMask () + { + return 1u << GetType(); + } + + static uint32_t + ConvertTypeToMask (OptionValue::Type type) + { + return 1u << type; + } + + static OptionValue::Type + ConvertTypeMaskToType (uint32_t type_mask) + { + // If only one bit is set, then return an appropriate enumeration + switch (type_mask) + { + case 1u << eTypeArch: return eTypeArch; + case 1u << eTypeArgs: return eTypeArgs; + case 1u << eTypeArray: return eTypeArray; + case 1u << eTypeBoolean: return eTypeBoolean; + case 1u << eTypeDictionary: return eTypeDictionary; + case 1u << eTypeEnum: return eTypeEnum; + case 1u << eTypeFileSpec: return eTypeFileSpec; + case 1u << eTypeFileSpecList: return eTypeFileSpecList; + case 1u << eTypeFormat: return eTypeFormat; + case 1u << eTypePathMap: return eTypePathMap; + case 1u << eTypeProperties: return eTypeProperties; + case 1u << eTypeRegex: return eTypeRegex; + case 1u << eTypeSInt64: return eTypeSInt64; + case 1u << eTypeString: return eTypeString; + case 1u << eTypeUInt64: return eTypeUInt64; + case 1u << eTypeUUID: return eTypeUUID; + } + // Else return invalid + return eTypeInvalid; + } + + static lldb::OptionValueSP + CreateValueFromCStringForTypeMask (const char *value_cstr, + uint32_t type_mask, + Error &error); + + // Get this value as a uint64_t value if it is encoded as a boolean, + // uint64_t or int64_t. Other types will cause "fail_value" to be + // returned + uint64_t + GetUInt64Value (uint64_t fail_value, bool *success_ptr); + + OptionValueArch * + GetAsArch (); + + const OptionValueArch * + GetAsArch () const; + + OptionValueArray * + GetAsArray (); + + const OptionValueArray * + GetAsArray () const; + + OptionValueArgs * + GetAsArgs (); + + const OptionValueArgs * + GetAsArgs () const; + + OptionValueBoolean * + GetAsBoolean (); + + const OptionValueBoolean * + GetAsBoolean () const; + + OptionValueDictionary * + GetAsDictionary (); + + const OptionValueDictionary * + GetAsDictionary () const; + + OptionValueEnumeration * + GetAsEnumeration (); + + const OptionValueEnumeration * + GetAsEnumeration () const; + + OptionValueFileSpec * + GetAsFileSpec (); + + const OptionValueFileSpec * + GetAsFileSpec () const; + + OptionValueFileSpecList * + GetAsFileSpecList (); + + const OptionValueFileSpecList * + GetAsFileSpecList () const; + + OptionValueFormat * + GetAsFormat (); + + const OptionValueFormat * + GetAsFormat () const; + + OptionValuePathMappings * + GetAsPathMappings (); + + const OptionValuePathMappings * + GetAsPathMappings () const; + + OptionValueProperties * + GetAsProperties (); + + const OptionValueProperties * + GetAsProperties () const; + + OptionValueRegex * + GetAsRegex (); + + const OptionValueRegex * + GetAsRegex () const; + + OptionValueSInt64 * + GetAsSInt64 (); + + const OptionValueSInt64 * + GetAsSInt64 () const; + + OptionValueString * + GetAsString (); + + const OptionValueString * + GetAsString () const; + + OptionValueUInt64 * + GetAsUInt64 (); + + const OptionValueUInt64 * + GetAsUInt64 () const; + + OptionValueUUID * + GetAsUUID (); + + const OptionValueUUID * + GetAsUUID () const; + + bool + GetBooleanValue (bool fail_value = false) const; + + bool + SetBooleanValue (bool new_value); + + int64_t + GetEnumerationValue (int64_t fail_value = -1) const; + + bool + SetEnumerationValue (int64_t value); + + FileSpec + GetFileSpecValue () const; + + bool + SetFileSpecValue (const FileSpec &file_spec); + + FileSpecList + GetFileSpecListValue () const; + + lldb::Format + GetFormatValue (lldb::Format fail_value = lldb::eFormatDefault) const; + + bool + SetFormatValue (lldb::Format new_value); + + const RegularExpression * + GetRegexValue () const; + + int64_t + GetSInt64Value (int64_t fail_value = 0) const; + + bool + SetSInt64Value (int64_t new_value); + + const char * + GetStringValue (const char *fail_value = NULL) const; + + bool + SetStringValue (const char *new_value); + + uint64_t + GetUInt64Value (uint64_t fail_value = 0) const; + + bool + SetUInt64Value (uint64_t new_value); + + UUID + GetUUIDValue () const; + + bool + SetUUIDValue (const UUID &uuid); + + bool + OptionWasSet () const + { + return m_value_was_set; + } + + void + SetOptionWasSet () + { + m_value_was_set = true; + } + + void + SetParent (const lldb::OptionValueSP &parent_sp) + { + m_parent_wp = parent_sp; + } + protected: + lldb::OptionValueWP m_parent_wp; + bool m_value_was_set; // This can be used to see if a value has been set + // by a call to SetValueFromCString(). It is often + // handy to know if an option value was set from + // the command line or as a setting, versus if we + // just have the default value that was already + // populated in the option value. + + }; + +} // namespace lldb_private + +#endif // liblldb_OptionValue_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Interpreter/OptionValueArch.h b/contrib/llvm/tools/lldb/include/lldb/Interpreter/OptionValueArch.h new file mode 100644 index 00000000000..662e1ec9f62 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Interpreter/OptionValueArch.h @@ -0,0 +1,139 @@ +//===-- OptionValueArch.h -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_OptionValueArch_h_ +#define liblldb_OptionValueArch_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/ArchSpec.h" +#include "lldb/Interpreter/OptionValue.h" + +namespace lldb_private { + +class OptionValueArch : public OptionValue +{ +public: + OptionValueArch () : + OptionValue(), + m_current_value (), + m_default_value () + { + } + + OptionValueArch (const char *triple) : + OptionValue(), + m_current_value (triple), + m_default_value () + { + m_default_value = m_current_value; + } + + OptionValueArch (const ArchSpec &value) : + OptionValue(), + m_current_value (value), + m_default_value (value) + { + } + + OptionValueArch (const ArchSpec ¤t_value, + const ArchSpec &default_value) : + OptionValue(), + m_current_value (current_value), + m_default_value (default_value) + { + } + + virtual + ~OptionValueArch() + { + } + + //--------------------------------------------------------------------- + // Virtual subclass pure virtual overrides + //--------------------------------------------------------------------- + + virtual OptionValue::Type + GetType () const + { + return eTypeArch; + } + + virtual void + DumpValue (const ExecutionContext *exe_ctx, Stream &strm, uint32_t dump_mask); + + virtual Error + SetValueFromCString (const char *value, + VarSetOperationType op = eVarSetOperationAssign); + + virtual bool + Clear () + { + m_current_value = m_default_value; + m_value_was_set = false; + return true; + } + + virtual lldb::OptionValueSP + DeepCopy () const; + + virtual size_t + AutoComplete (CommandInterpreter &interpreter, + const char *s, + int match_start_point, + int max_return_elements, + bool &word_complete, + StringList &matches); + + //--------------------------------------------------------------------- + // Subclass specific functions + //--------------------------------------------------------------------- + + ArchSpec & + GetCurrentValue() + { + return m_current_value; + } + + const ArchSpec & + GetCurrentValue() const + { + return m_current_value; + } + + const ArchSpec & + GetDefaultValue() const + { + return m_default_value; + } + + void + SetCurrentValue (const ArchSpec &value, bool set_value_was_set) + { + m_current_value = value; + if (set_value_was_set) + m_value_was_set = true; + } + + void + SetDefaultValue (const ArchSpec &value) + { + m_default_value = value; + } + +protected: + ArchSpec m_current_value; + ArchSpec m_default_value; +}; + +} // namespace lldb_private + +#endif // liblldb_OptionValueArch_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Interpreter/OptionValueArgs.h b/contrib/llvm/tools/lldb/include/lldb/Interpreter/OptionValueArgs.h new file mode 100644 index 00000000000..365a52a8b39 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Interpreter/OptionValueArgs.h @@ -0,0 +1,46 @@ +//===-- OptionValueArgs.h --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_OptionValueArgs_h_ +#define liblldb_OptionValueArgs_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Interpreter/OptionValueArray.h" + +namespace lldb_private { + +class OptionValueArgs : public OptionValueArray +{ +public: + OptionValueArgs () : + OptionValueArray (OptionValue::ConvertTypeToMask (OptionValue::eTypeString)) + { + } + + virtual + ~OptionValueArgs() + { + } + + size_t + GetArgs (Args &args); + + virtual Type + GetType() const + { + return eTypeArgs; + } +}; + +} // namespace lldb_private + +#endif // liblldb_OptionValueArgs_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Interpreter/OptionValueArray.h b/contrib/llvm/tools/lldb/include/lldb/Interpreter/OptionValueArray.h new file mode 100644 index 00000000000..39ae2f6f43d --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Interpreter/OptionValueArray.h @@ -0,0 +1,178 @@ +//===-- OptionValueArray.h --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_OptionValueArray_h_ +#define liblldb_OptionValueArray_h_ + +// C Includes +// C++ Includes +#include + +// Other libraries and framework includes +// Project includes +#include "lldb/Interpreter/OptionValue.h" + +namespace lldb_private { + +class OptionValueArray : public OptionValue +{ +public: + OptionValueArray (uint32_t type_mask = UINT32_MAX, bool raw_value_dump = false) : + m_type_mask (type_mask), + m_values (), + m_raw_value_dump(raw_value_dump) + { + } + + virtual + ~OptionValueArray() + { + } + + //--------------------------------------------------------------------- + // Virtual subclass pure virtual overrides + //--------------------------------------------------------------------- + + virtual OptionValue::Type + GetType () const + { + return eTypeArray; + } + + virtual void + DumpValue (const ExecutionContext *exe_ctx, Stream &strm, uint32_t dump_mask); + + virtual Error + SetValueFromCString (const char *value, + VarSetOperationType op = eVarSetOperationAssign); + + virtual bool + Clear () + { + m_values.clear(); + m_value_was_set = false; + return true; + } + + virtual lldb::OptionValueSP + DeepCopy () const; + + virtual bool + IsAggregateValue () const + { + return true; + } + + virtual lldb::OptionValueSP + GetSubValue (const ExecutionContext *exe_ctx, + const char *name, + bool will_modify, + Error &error) const; + + //--------------------------------------------------------------------- + // Subclass specific functions + //--------------------------------------------------------------------- + + size_t + GetSize () const + { + return m_values.size(); + } + + lldb::OptionValueSP + operator[](size_t idx) const + { + lldb::OptionValueSP value_sp; + if (idx < m_values.size()) + value_sp = m_values[idx]; + return value_sp; + } + + lldb::OptionValueSP + GetValueAtIndex (size_t idx) const + { + lldb::OptionValueSP value_sp; + if (idx < m_values.size()) + value_sp = m_values[idx]; + return value_sp; + } + + bool + AppendValue (const lldb::OptionValueSP &value_sp) + { + // Make sure the value_sp object is allowed to contain + // values of the type passed in... + if (value_sp && (m_type_mask & value_sp->GetTypeAsMask())) + { + m_values.push_back(value_sp); + return true; + } + return false; + } + + bool + InsertValue (size_t idx, const lldb::OptionValueSP &value_sp) + { + // Make sure the value_sp object is allowed to contain + // values of the type passed in... + if (value_sp && (m_type_mask & value_sp->GetTypeAsMask())) + { + if (idx < m_values.size()) + m_values.insert(m_values.begin() + idx, value_sp); + else + m_values.push_back(value_sp); + return true; + } + return false; + } + + bool + ReplaceValue (size_t idx, const lldb::OptionValueSP &value_sp) + { + // Make sure the value_sp object is allowed to contain + // values of the type passed in... + if (value_sp && (m_type_mask & value_sp->GetTypeAsMask())) + { + if (idx < m_values.size()) + { + m_values[idx] = value_sp; + return true; + } + } + return false; + } + + bool + DeleteValue (size_t idx) + { + if (idx < m_values.size()) + { + m_values.erase (m_values.begin() + idx); + return true; + } + return false; + } + + size_t + GetArgs (Args &args) const; + + Error + SetArgs (const Args &args, VarSetOperationType op); + +protected: + typedef std::vector collection; + + uint32_t m_type_mask; + collection m_values; + bool m_raw_value_dump; +}; + +} // namespace lldb_private + +#endif // liblldb_OptionValueArray_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Interpreter/OptionValueBoolean.h b/contrib/llvm/tools/lldb/include/lldb/Interpreter/OptionValueBoolean.h new file mode 100644 index 00000000000..2b935e9e03e --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Interpreter/OptionValueBoolean.h @@ -0,0 +1,141 @@ +//===-- OptionValueBoolean.h ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_OptionValueBoolean_h_ +#define liblldb_OptionValueBoolean_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Interpreter/OptionValue.h" + +namespace lldb_private { + +class OptionValueBoolean : public OptionValue +{ +public: + OptionValueBoolean (bool value) : + OptionValue(), + m_current_value (value), + m_default_value (value) + { + } + OptionValueBoolean (bool current_value, + bool default_value) : + OptionValue(), + m_current_value (current_value), + m_default_value (default_value) + { + } + + virtual + ~OptionValueBoolean() + { + } + + //--------------------------------------------------------------------- + // Virtual subclass pure virtual overrides + //--------------------------------------------------------------------- + + virtual OptionValue::Type + GetType () const + { + return eTypeBoolean; + } + + virtual void + DumpValue (const ExecutionContext *exe_ctx, Stream &strm, uint32_t dump_mask); + + virtual Error + SetValueFromCString (const char *value, + VarSetOperationType op = eVarSetOperationAssign); + + virtual bool + Clear () + { + m_current_value = m_default_value; + m_value_was_set = false; + return true; + } + + virtual size_t + AutoComplete (CommandInterpreter &interpreter, + const char *s, + int match_start_point, + int max_return_elements, + bool &word_complete, + StringList &matches); + + //--------------------------------------------------------------------- + // Subclass specific functions + //--------------------------------------------------------------------- + + //------------------------------------------------------------------ + /// Convert to bool operator. + /// + /// This allows code to check a OptionValueBoolean in conditions. + /// + /// @code + /// OptionValueBoolean bool_value(...); + /// if (bool_value) + /// { ... + /// @endcode + /// + /// @return + /// /b True this object contains a valid namespace decl, \b + /// false otherwise. + //------------------------------------------------------------------ + operator bool() const + { + return m_current_value; + } + + const bool & + operator = (bool b) + { + m_current_value = b; + return m_current_value; + } + + bool + GetCurrentValue() const + { + return m_current_value; + } + + bool + GetDefaultValue() const + { + return m_default_value; + } + + void + SetCurrentValue (bool value) + { + m_current_value = value; + } + + void + SetDefaultValue (bool value) + { + m_default_value = value; + } + + virtual lldb::OptionValueSP + DeepCopy () const; + +protected: + bool m_current_value; + bool m_default_value; +}; + +} // namespace lldb_private + +#endif // liblldb_OptionValueBoolean_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Interpreter/OptionValueDictionary.h b/contrib/llvm/tools/lldb/include/lldb/Interpreter/OptionValueDictionary.h new file mode 100644 index 00000000000..5fb698b9f22 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Interpreter/OptionValueDictionary.h @@ -0,0 +1,139 @@ +//===-- OptionValueDictionary.h ---------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_OptionValueDictionary_h_ +#define liblldb_OptionValueDictionary_h_ + +// C Includes +// C++ Includes +#include + +// Other libraries and framework includes +// Project includes +#include "lldb/Interpreter/OptionValue.h" + +namespace lldb_private { + +class OptionValueDictionary : public OptionValue +{ +public: + OptionValueDictionary (uint32_t type_mask = UINT32_MAX, bool raw_value_dump = true) : + OptionValue(), + m_type_mask (type_mask), + m_values (), + m_raw_value_dump (raw_value_dump) + { + } + + virtual + ~OptionValueDictionary() + { + } + + //--------------------------------------------------------------------- + // Virtual subclass pure virtual overrides + //--------------------------------------------------------------------- + + virtual OptionValue::Type + GetType () const + { + return eTypeDictionary; + } + + virtual void + DumpValue (const ExecutionContext *exe_ctx, Stream &strm, uint32_t dump_mask); + + virtual Error + SetValueFromCString (const char *value, + VarSetOperationType op = eVarSetOperationAssign); + + virtual bool + Clear () + { + m_values.clear(); + m_value_was_set = false; + return true; + } + + virtual lldb::OptionValueSP + DeepCopy () const; + + virtual bool + IsAggregateValue () const + { + return true; + } + + bool + IsHomogenous() const + { + return ConvertTypeMaskToType (m_type_mask) != eTypeInvalid; + } + + //--------------------------------------------------------------------- + // Subclass specific functions + //--------------------------------------------------------------------- + + size_t + GetNumValues() const + { + return m_values.size(); + } + + lldb::OptionValueSP + GetValueForKey (const ConstString &key) const; + + virtual lldb::OptionValueSP + GetSubValue (const ExecutionContext *exe_ctx, + const char *name, + bool will_modify, + Error &error) const; + + virtual Error + SetSubValue (const ExecutionContext *exe_ctx, + VarSetOperationType op, + const char *name, + const char *value); + + //--------------------------------------------------------------------- + // String value getters and setters + //--------------------------------------------------------------------- + const char * + GetStringValueForKey (const ConstString &key); + + bool + SetStringValueForKey (const ConstString &key, + const char *value, + bool can_replace = true); + + + bool + SetValueForKey (const ConstString &key, + const lldb::OptionValueSP &value_sp, + bool can_replace = true); + + bool + DeleteValueForKey (const ConstString &key); + + size_t + GetArgs (Args &args) const; + + Error + SetArgs (const Args &args, VarSetOperationType op); + +protected: + typedef std::map collection; + uint32_t m_type_mask; + collection m_values; + bool m_raw_value_dump; +}; + +} // namespace lldb_private + +#endif // liblldb_OptionValueDictionary_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Interpreter/OptionValueEnumeration.h b/contrib/llvm/tools/lldb/include/lldb/Interpreter/OptionValueEnumeration.h new file mode 100644 index 00000000000..012eeb68ac0 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Interpreter/OptionValueEnumeration.h @@ -0,0 +1,126 @@ +//===-- OptionValueEnumeration.h --------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_OptionValueEnumeration_h_ +#define liblldb_OptionValueEnumeration_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/ConstString.h" +#include "lldb/Core/Error.h" +#include "lldb/Core/Stream.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Core/UniqueCStringMap.h" +#include "lldb/Interpreter/OptionValue.h" + +namespace lldb_private { + + +class OptionValueEnumeration : public OptionValue +{ +public: + typedef int64_t enum_type; + struct EnumeratorInfo + { + enum_type value; + const char *description; + }; + typedef UniqueCStringMap EnumerationMap; + typedef typename EnumerationMap::Entry EnumerationMapEntry; + + OptionValueEnumeration (const OptionEnumValueElement *enumerators, enum_type value); + + virtual + ~OptionValueEnumeration(); + + //--------------------------------------------------------------------- + // Virtual subclass pure virtual overrides + //--------------------------------------------------------------------- + + virtual OptionValue::Type + GetType () const + { + return eTypeEnum; + } + + virtual void + DumpValue (const ExecutionContext *exe_ctx, Stream &strm, uint32_t dump_mask); + + virtual Error + SetValueFromCString (const char *value, + VarSetOperationType op = eVarSetOperationAssign); + + virtual bool + Clear () + { + m_current_value = m_default_value; + m_value_was_set = false; + return true; + } + + virtual lldb::OptionValueSP + DeepCopy () const; + + virtual size_t + AutoComplete (CommandInterpreter &interpreter, + const char *s, + int match_start_point, + int max_return_elements, + bool &word_complete, + StringList &matches); + + //--------------------------------------------------------------------- + // Subclass specific functions + //--------------------------------------------------------------------- + + enum_type + operator = (enum_type value) + { + m_current_value = value; + return m_current_value; + } + + enum_type + GetCurrentValue() const + { + return m_current_value; + } + + enum_type + GetDefaultValue() const + { + return m_default_value; + } + + void + SetCurrentValue (enum_type value) + { + m_current_value = value; + } + + void + SetDefaultValue (enum_type value) + { + m_default_value = value; + } + +protected: + void + SetEnumerations (const OptionEnumValueElement *enumerators); + + enum_type m_current_value; + enum_type m_default_value; + EnumerationMap m_enumerations; +}; + +} // namespace lldb_private + +#endif // liblldb_OptionValueEnumeration_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Interpreter/OptionValueFileSpec.h b/contrib/llvm/tools/lldb/include/lldb/Interpreter/OptionValueFileSpec.h new file mode 100644 index 00000000000..7e74b605660 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Interpreter/OptionValueFileSpec.h @@ -0,0 +1,129 @@ +//===-- OptionValueFileSpec.h -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_OptionValueFileSpec_h_ +#define liblldb_OptionValueFileSpec_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Host/FileSpec.h" +#include "lldb/Interpreter/OptionValue.h" + +namespace lldb_private { + +class OptionValueFileSpec : public OptionValue +{ +public: + OptionValueFileSpec (); + + OptionValueFileSpec (const FileSpec &value); + + OptionValueFileSpec (const FileSpec ¤t_value, + const FileSpec &default_value); + + virtual + ~OptionValueFileSpec() + { + } + + //--------------------------------------------------------------------- + // Virtual subclass pure virtual overrides + //--------------------------------------------------------------------- + + virtual OptionValue::Type + GetType () const + { + return eTypeFileSpec; + } + + virtual void + DumpValue (const ExecutionContext *exe_ctx, Stream &strm, uint32_t dump_mask); + + virtual Error + SetValueFromCString (const char *value, + VarSetOperationType op = eVarSetOperationAssign); + + virtual bool + Clear () + { + m_current_value = m_default_value; + m_value_was_set = false; + m_data_sp.reset(); + return true; + } + + virtual lldb::OptionValueSP + DeepCopy () const; + + virtual size_t + AutoComplete (CommandInterpreter &interpreter, + const char *s, + int match_start_point, + int max_return_elements, + bool &word_complete, + StringList &matches); + + //--------------------------------------------------------------------- + // Subclass specific functions + //--------------------------------------------------------------------- + + FileSpec & + GetCurrentValue() + { + return m_current_value; + } + + const FileSpec & + GetCurrentValue() const + { + return m_current_value; + } + + const FileSpec & + GetDefaultValue() const + { + return m_default_value; + } + + void + SetCurrentValue (const FileSpec &value, bool set_value_was_set) + { + m_current_value = value; + if (set_value_was_set) + m_value_was_set = true; + m_data_sp.reset(); + } + + void + SetDefaultValue (const FileSpec &value) + { + m_default_value = value; + } + + const lldb::DataBufferSP & + GetFileContents(bool null_terminate); + + void + SetCompletionMask (uint32_t mask) + { + m_completion_mask = mask; + } + +protected: + FileSpec m_current_value; + FileSpec m_default_value; + lldb::DataBufferSP m_data_sp; + uint32_t m_completion_mask; +}; + +} // namespace lldb_private + +#endif // liblldb_OptionValueFileSpec_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Interpreter/OptionValueFileSpecList.h b/contrib/llvm/tools/lldb/include/lldb/Interpreter/OptionValueFileSpecList.h new file mode 100644 index 00000000000..792de4e23af --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Interpreter/OptionValueFileSpecList.h @@ -0,0 +1,105 @@ +//===-- OptionValueFileSpecList.h -------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_OptionValueFileSpecList_h_ +#define liblldb_OptionValueFileSpecList_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/FileSpecList.h" +#include "lldb/Interpreter/OptionValue.h" + +namespace lldb_private { + +class OptionValueFileSpecList : public OptionValue +{ +public: + OptionValueFileSpecList () : + OptionValue(), + m_current_value () + { + } + + OptionValueFileSpecList (const FileSpecList ¤t_value) : + OptionValue(), + m_current_value (current_value) + { + } + + + virtual + ~OptionValueFileSpecList() + { + } + + //--------------------------------------------------------------------- + // Virtual subclass pure virtual overrides + //--------------------------------------------------------------------- + + virtual OptionValue::Type + GetType () const + { + return eTypeFileSpecList; + } + + virtual void + DumpValue (const ExecutionContext *exe_ctx, Stream &strm, uint32_t dump_mask); + + virtual Error + SetValueFromCString (const char *value, + VarSetOperationType op = eVarSetOperationAssign); + + virtual bool + Clear () + { + m_current_value.Clear(); + m_value_was_set = false; + return true; + } + + virtual lldb::OptionValueSP + DeepCopy () const; + + virtual bool + IsAggregateValue () const + { + return true; + } + + //--------------------------------------------------------------------- + // Subclass specific functions + //--------------------------------------------------------------------- + + FileSpecList & + GetCurrentValue() + { + return m_current_value; + } + + const FileSpecList & + GetCurrentValue() const + { + return m_current_value; + } + + void + SetCurrentValue (const FileSpecList &value) + { + m_current_value = value; + } + +protected: + FileSpecList m_current_value; +}; + +} // namespace lldb_private + +#endif // liblldb_OptionValueFileSpecList_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Interpreter/OptionValueFormat.h b/contrib/llvm/tools/lldb/include/lldb/Interpreter/OptionValueFormat.h new file mode 100644 index 00000000000..245b2eeb5af --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Interpreter/OptionValueFormat.h @@ -0,0 +1,107 @@ +//===-- OptionValueFormat.h -------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_OptionValueFormat_h_ +#define liblldb_OptionValueFormat_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Interpreter/OptionValue.h" + +namespace lldb_private { + +class OptionValueFormat : public OptionValue +{ +public: + OptionValueFormat (lldb::Format value) : + OptionValue(), + m_current_value (value), + m_default_value (value) + { + } + + OptionValueFormat (lldb::Format current_value, + lldb::Format default_value) : + OptionValue(), + m_current_value (current_value), + m_default_value (default_value) + { + } + + virtual + ~OptionValueFormat() + { + } + + //--------------------------------------------------------------------- + // Virtual subclass pure virtual overrides + //--------------------------------------------------------------------- + + virtual OptionValue::Type + GetType () const + { + return eTypeFormat; + } + + virtual void + DumpValue (const ExecutionContext *exe_ctx, Stream &strm, uint32_t dump_mask); + + virtual Error + SetValueFromCString (const char *value, + VarSetOperationType op = eVarSetOperationAssign); + + virtual bool + Clear () + { + m_current_value = m_default_value; + m_value_was_set = false; + return true; + } + + virtual lldb::OptionValueSP + DeepCopy () const; + + //--------------------------------------------------------------------- + // Subclass specific functions + //--------------------------------------------------------------------- + + lldb::Format + GetCurrentValue() const + { + return m_current_value; + } + + lldb::Format + GetDefaultValue() const + { + return m_default_value; + } + + void + SetCurrentValue (lldb::Format value) + { + m_current_value = value; + } + + void + SetDefaultValue (lldb::Format value) + { + m_default_value = value; + } + +protected: + lldb::Format m_current_value; + lldb::Format m_default_value; +}; + +} // namespace lldb_private + +#endif // liblldb_OptionValueFormat_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Interpreter/OptionValuePathMappings.h b/contrib/llvm/tools/lldb/include/lldb/Interpreter/OptionValuePathMappings.h new file mode 100644 index 00000000000..7ebf4947c6a --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Interpreter/OptionValuePathMappings.h @@ -0,0 +1,94 @@ +//===-- OptionValuePathMappings.h -------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_OptionValuePathMappings_h_ +#define liblldb_OptionValuePathMappings_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Target/PathMappingList.h" +#include "lldb/Interpreter/OptionValue.h" + +namespace lldb_private { + +class OptionValuePathMappings : public OptionValue +{ +public: + OptionValuePathMappings (bool notify_changes) : + OptionValue(), + m_path_mappings (), + m_notify_changes (notify_changes) + { + } + + virtual + ~OptionValuePathMappings() + { + } + + //--------------------------------------------------------------------- + // Virtual subclass pure virtual overrides + //--------------------------------------------------------------------- + + virtual OptionValue::Type + GetType () const + { + return eTypePathMap; + } + + virtual void + DumpValue (const ExecutionContext *exe_ctx, Stream &strm, uint32_t dump_mask); + + virtual Error + SetValueFromCString (const char *value, + VarSetOperationType op = eVarSetOperationAssign); + + virtual bool + Clear () + { + m_path_mappings.Clear(m_notify_changes); + m_value_was_set = false; + return true; + } + + virtual lldb::OptionValueSP + DeepCopy () const; + + virtual bool + IsAggregateValue () const + { + return true; + } + + //--------------------------------------------------------------------- + // Subclass specific functions + //--------------------------------------------------------------------- + + PathMappingList & + GetCurrentValue() + { + return m_path_mappings; + } + + const PathMappingList & + GetCurrentValue() const + { + return m_path_mappings; + } + +protected: + PathMappingList m_path_mappings; + bool m_notify_changes; +}; + +} // namespace lldb_private + +#endif // liblldb_OptionValuePathMappings_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Interpreter/OptionValueProperties.h b/contrib/llvm/tools/lldb/include/lldb/Interpreter/OptionValueProperties.h new file mode 100644 index 00000000000..0024f20e2ff --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Interpreter/OptionValueProperties.h @@ -0,0 +1,265 @@ +//===-- OptionValueProperties.h --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_OptionValueProperties_h_ +#define liblldb_OptionValueProperties_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/ConstString.h" +#include "lldb/Core/UniqueCStringMap.h" +#include "lldb/Interpreter/OptionValue.h" +#include "lldb/Interpreter/Property.h" + +namespace lldb_private { + +class OptionValueProperties : + public OptionValue, + public std::enable_shared_from_this +{ +public: + + //--------------------------------------------------------------------- + // OptionValueProperties + //--------------------------------------------------------------------- + OptionValueProperties () : + OptionValue(), + m_name (), + m_properties (), + m_name_to_index () + { + } + + OptionValueProperties (const ConstString &name); + + OptionValueProperties (const OptionValueProperties &global_properties); + + virtual + ~OptionValueProperties() + { + } + + virtual Type + GetType () const + { + return eTypeProperties; + } + + virtual bool + Clear (); + + virtual lldb::OptionValueSP + DeepCopy () const; + + virtual Error + SetValueFromCString (const char *value, VarSetOperationType op = eVarSetOperationAssign); + + virtual void + DumpValue (const ExecutionContext *exe_ctx, + Stream &strm, + uint32_t dump_mask); + + virtual ConstString + GetName () const + { + return m_name; + } + + virtual Error + DumpPropertyValue (const ExecutionContext *exe_ctx, + Stream &strm, + const char *property_path, + uint32_t dump_mask); + + virtual void + DumpAllDescriptions (CommandInterpreter &interpreter, + Stream &strm) const; + + void + Apropos (const char *keyword, + std::vector &matching_properties) const; + + void + Initialize (const PropertyDefinition *setting_definitions); + +// bool +// GetQualifiedName (Stream &strm); + + //--------------------------------------------------------------------- + // Subclass specific functions + //--------------------------------------------------------------------- + + virtual size_t + GetNumProperties() const; + + virtual ConstString + GetPropertyNameAtIndex (uint32_t idx) const; + + virtual const char * + GetPropertyDescriptionAtIndex (uint32_t idx) const; + + //--------------------------------------------------------------------- + // Get the index of a property given its exact name in this property + // collection, "name" can't be a path to a property path that refers + // to a property within a property + //--------------------------------------------------------------------- + virtual uint32_t + GetPropertyIndex (const ConstString &name) const; + + //--------------------------------------------------------------------- + // Get a property by exact name exists in this property collection, name + // can not be a path to a property path that refers to a property within + // a property + //--------------------------------------------------------------------- + virtual const Property * + GetProperty (const ExecutionContext *exe_ctx, + bool will_modify, + const ConstString &name) const; + + virtual const Property * + GetPropertyAtIndex (const ExecutionContext *exe_ctx, + bool will_modify, + uint32_t idx) const; + + //--------------------------------------------------------------------- + // Property can be be a property path like "target.process.extra-startup-command" + //--------------------------------------------------------------------- + virtual const Property * + GetPropertyAtPath (const ExecutionContext *exe_ctx, + bool will_modify, + const char *property_path) const; + + virtual lldb::OptionValueSP + GetPropertyValueAtIndex (const ExecutionContext *exe_ctx, + bool will_modify, + uint32_t idx) const; + + virtual lldb::OptionValueSP + GetValueForKey (const ExecutionContext *exe_ctx, + const ConstString &key, + bool value_will_be_modified) const; + + lldb::OptionValueSP + GetSubValue (const ExecutionContext *exe_ctx, + const char *name, + bool value_will_be_modified, + Error &error) const; + + virtual Error + SetSubValue (const ExecutionContext *exe_ctx, + VarSetOperationType op, + const char *path, + const char *value); + + virtual bool + PredicateMatches (const ExecutionContext *exe_ctx, + const char *predicate) const + { + return false; + } + + + OptionValueArch * + GetPropertyAtIndexAsOptionValueArch (const ExecutionContext *exe_ctx, uint32_t idx) const; + + bool + GetPropertyAtIndexAsArgs (const ExecutionContext *exe_ctx, uint32_t idx, Args &args) const; + + bool + SetPropertyAtIndexFromArgs (const ExecutionContext *exe_ctx, uint32_t idx, const Args &args); + + bool + GetPropertyAtIndexAsBoolean (const ExecutionContext *exe_ctx, uint32_t idx, bool fail_value) const; + + bool + SetPropertyAtIndexAsBoolean (const ExecutionContext *exe_ctx, uint32_t idx, bool new_value); + + OptionValueDictionary * + GetPropertyAtIndexAsOptionValueDictionary (const ExecutionContext *exe_ctx, uint32_t idx) const; + + int64_t + GetPropertyAtIndexAsEnumeration (const ExecutionContext *exe_ctx, uint32_t idx, int64_t fail_value) const; + + bool + SetPropertyAtIndexAsEnumeration (const ExecutionContext *exe_ctx, uint32_t idx, int64_t new_value); + + const RegularExpression * + GetPropertyAtIndexAsOptionValueRegex (const ExecutionContext *exe_ctx, uint32_t idx) const; + + OptionValueSInt64 * + GetPropertyAtIndexAsOptionValueSInt64 (const ExecutionContext *exe_ctx, uint32_t idx) const; + + int64_t + GetPropertyAtIndexAsSInt64 (const ExecutionContext *exe_ctx, uint32_t idx, int64_t fail_value) const; + + bool + SetPropertyAtIndexAsSInt64 (const ExecutionContext *exe_ctx, uint32_t idx, int64_t new_value); + + uint64_t + GetPropertyAtIndexAsUInt64 (const ExecutionContext *exe_ctx, uint32_t idx, uint64_t fail_value) const; + + bool + SetPropertyAtIndexAsUInt64 (const ExecutionContext *exe_ctx, uint32_t idx, uint64_t new_value); + + const char * + GetPropertyAtIndexAsString (const ExecutionContext *exe_ctx, uint32_t idx, const char *fail_value) const; + + bool + SetPropertyAtIndexAsString (const ExecutionContext *exe_ctx, uint32_t idx, const char *new_value); + + OptionValueString * + GetPropertyAtIndexAsOptionValueString (const ExecutionContext *exe_ctx, bool will_modify, uint32_t idx) const; + + OptionValueFileSpec * + GetPropertyAtIndexAsOptionValueFileSpec (const ExecutionContext *exe_ctx, bool will_modify, uint32_t idx) const; + + FileSpec + GetPropertyAtIndexAsFileSpec (const ExecutionContext *exe_ctx, uint32_t idx) const; + + bool + SetPropertyAtIndexAsFileSpec (const ExecutionContext *exe_ctx, uint32_t idx, const FileSpec &file_spec); + + OptionValuePathMappings * + GetPropertyAtIndexAsOptionValuePathMappings (const ExecutionContext *exe_ctx, bool will_modify, uint32_t idx) const; + + OptionValueFileSpecList * + GetPropertyAtIndexAsOptionValueFileSpecList (const ExecutionContext *exe_ctx, bool will_modify, uint32_t idx) const; + + void + AppendProperty(const ConstString &name, + const ConstString &desc, + bool is_global, + const lldb::OptionValueSP &value_sp); + + lldb::OptionValuePropertiesSP + GetSubProperty (const ExecutionContext *exe_ctx, + const ConstString &name); + +protected: + + const Property * + ProtectedGetPropertyAtIndex (uint32_t idx) const + { + if (idx < m_properties.size()) + return &m_properties[idx]; + return NULL; + } + + typedef UniqueCStringMap NameToIndex; + + ConstString m_name; + std::vector m_properties; + NameToIndex m_name_to_index; +}; + +} // namespace lldb_private + +#endif // liblldb_OptionValueProperties_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Interpreter/OptionValueRegex.h b/contrib/llvm/tools/lldb/include/lldb/Interpreter/OptionValueRegex.h new file mode 100644 index 00000000000..bb8c4588e22 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Interpreter/OptionValueRegex.h @@ -0,0 +1,98 @@ +//===-- OptionValueRegex.h --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_OptionValueRegex_h_ +#define liblldb_OptionValueRegex_h_ + +// C Includes +// C++ Includes +#include + +// Other libraries and framework includes +// Project includes +#include "lldb/Core/RegularExpression.h" +#include "lldb/Interpreter/OptionValue.h" + +namespace lldb_private { + +class OptionValueRegex : public OptionValue +{ +public: + OptionValueRegex (const char *value = NULL, uint32_t regex_flags = 0) : + OptionValue(), + m_regex (value, regex_flags) + { + } + + virtual + ~OptionValueRegex() + { + } + + //--------------------------------------------------------------------- + // Virtual subclass pure virtual overrides + //--------------------------------------------------------------------- + + virtual OptionValue::Type + GetType () const + { + return eTypeRegex; + } + + virtual void + DumpValue (const ExecutionContext *exe_ctx, Stream &strm, uint32_t dump_mask); + + virtual Error + SetValueFromCString (const char *value, + VarSetOperationType op = eVarSetOperationAssign); + + virtual bool + Clear () + { + m_regex.Clear(); + m_value_was_set = false; + return true; + } + + virtual lldb::OptionValueSP + DeepCopy () const; + + //--------------------------------------------------------------------- + // Subclass specific functions + //--------------------------------------------------------------------- + const RegularExpression * + GetCurrentValue() const + { + if (m_regex.IsValid()) + return &m_regex; + return NULL; + } + + void + SetCurrentValue (const char *value, uint32_t regex_flags) + { + if (value && value[0]) + m_regex.Compile (value, regex_flags); + else + m_regex.Clear(); + } + + bool + IsValid () const + { + return m_regex.IsValid(); + } + +protected: + RegularExpression m_regex; +}; + +} // namespace lldb_private + +#endif // liblldb_OptionValueRegex_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Interpreter/OptionValueSInt64.h b/contrib/llvm/tools/lldb/include/lldb/Interpreter/OptionValueSInt64.h new file mode 100644 index 00000000000..8bc8fb2da2d --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Interpreter/OptionValueSInt64.h @@ -0,0 +1,172 @@ +//===-- OptionValueSInt64.h --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_OptionValueSInt64_h_ +#define liblldb_OptionValueSInt64_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Interpreter/OptionValue.h" + +namespace lldb_private { + +class OptionValueSInt64 : public OptionValue +{ +public: + OptionValueSInt64 () : + OptionValue(), + m_current_value (0), + m_default_value (0), + m_min_value (INT64_MIN), + m_max_value (INT64_MAX) + { + } + + OptionValueSInt64 (int64_t value) : + OptionValue(), + m_current_value (value), + m_default_value (value), + m_min_value (INT64_MIN), + m_max_value (INT64_MAX) + { + } + + OptionValueSInt64 (int64_t current_value, + int64_t default_value) : + OptionValue(), + m_current_value (current_value), + m_default_value (default_value), + m_min_value (INT64_MIN), + m_max_value (INT64_MAX) + { + } + + OptionValueSInt64 (const OptionValueSInt64 &rhs) : + OptionValue(rhs), + m_current_value (rhs.m_current_value), + m_default_value (rhs.m_default_value), + m_min_value (rhs.m_min_value), + m_max_value (rhs.m_max_value) + { + } + + virtual + ~OptionValueSInt64() + { + } + + //--------------------------------------------------------------------- + // Virtual subclass pure virtual overrides + //--------------------------------------------------------------------- + + virtual OptionValue::Type + GetType () const + { + return eTypeSInt64; + } + + virtual void + DumpValue (const ExecutionContext *exe_ctx, Stream &strm, uint32_t dump_mask); + + virtual Error + SetValueFromCString (const char *value, + VarSetOperationType op = eVarSetOperationAssign); + + virtual bool + Clear () + { + m_current_value = m_default_value; + m_value_was_set = false; + return true; + } + + virtual lldb::OptionValueSP + DeepCopy () const; + + //--------------------------------------------------------------------- + // Subclass specific functions + //--------------------------------------------------------------------- + + const int64_t & + operator = (int64_t value) + { + m_current_value = value; + return m_current_value; + } + + int64_t + GetCurrentValue() const + { + return m_current_value; + } + + int64_t + GetDefaultValue() const + { + return m_default_value; + } + + bool + SetCurrentValue (int64_t value) + { + if (value >= m_min_value && value <= m_max_value) + { + m_current_value = value; + return true; + } + return false; + } + + bool + SetDefaultValue (int64_t value) + { + if (value >= m_min_value && value <= m_max_value) + { + m_default_value = value; + return true; + } + return false; + } + + void + SetMinimumValue (int64_t v) + { + m_min_value = v; + } + + int64_t + GetMinimumValue () const + { + return m_min_value; + } + + void + SetMaximumValue (int64_t v) + { + m_max_value = v; + } + + int64_t + GetMaximumValue () const + { + return m_max_value; + } + +protected: + int64_t m_current_value; + int64_t m_default_value; + int64_t m_min_value; + int64_t m_max_value; +}; + +} // namespace lldb_private + +#endif // liblldb_OptionValueSInt64_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Interpreter/OptionValueString.h b/contrib/llvm/tools/lldb/include/lldb/Interpreter/OptionValueString.h new file mode 100644 index 00000000000..a82e1403b74 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Interpreter/OptionValueString.h @@ -0,0 +1,227 @@ +//===-- OptionValueString.h --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_OptionValueString_h_ +#define liblldb_OptionValueString_h_ + +// C Includes +// C++ Includes +#include + +// Other libraries and framework includes +// Project includes +#include "lldb/Core/Flags.h" +#include "lldb/Interpreter/OptionValue.h" + +namespace lldb_private { + +class OptionValueString : public OptionValue +{ +public: + + typedef Error (*ValidatorCallback) (const char* string, + void* baton); + + enum Options + { + eOptionEncodeCharacterEscapeSequences = (1u << 0) + }; + + OptionValueString () : + OptionValue(), + m_current_value (), + m_default_value (), + m_options(), + m_validator(), + m_validator_baton() + { + } + + OptionValueString (ValidatorCallback validator, + void* baton = NULL) : + OptionValue(), + m_current_value (), + m_default_value (), + m_options(), + m_validator(validator), + m_validator_baton(baton) + { + } + + OptionValueString (const char *value) : + OptionValue(), + m_current_value (), + m_default_value (), + m_options(), + m_validator(), + m_validator_baton() + { + if (value && value[0]) + { + m_current_value.assign (value); + m_default_value.assign (value); + } + } + + OptionValueString (const char *current_value, + const char *default_value) : + OptionValue(), + m_current_value (), + m_default_value (), + m_options(), + m_validator(), + m_validator_baton() + { + if (current_value && current_value[0]) + m_current_value.assign (current_value); + if (default_value && default_value[0]) + m_default_value.assign (default_value); + } + + OptionValueString (const char *value, + ValidatorCallback validator, + void* baton = NULL) : + OptionValue(), + m_current_value (), + m_default_value (), + m_options(), + m_validator(validator), + m_validator_baton(baton) + { + if (value && value[0]) + { + m_current_value.assign (value); + m_default_value.assign (value); + } + } + + OptionValueString (const char *current_value, + const char *default_value, + ValidatorCallback validator, + void* baton = NULL) : + OptionValue(), + m_current_value (), + m_default_value (), + m_options(), + m_validator(validator), + m_validator_baton(baton) + { + if (current_value && current_value[0]) + m_current_value.assign (current_value); + if (default_value && default_value[0]) + m_default_value.assign (default_value); + } + + virtual + ~OptionValueString() + { + } + + //--------------------------------------------------------------------- + // Virtual subclass pure virtual overrides + //--------------------------------------------------------------------- + + virtual OptionValue::Type + GetType () const + { + return eTypeString; + } + + virtual void + DumpValue (const ExecutionContext *exe_ctx, Stream &strm, uint32_t dump_mask); + + virtual Error + SetValueFromCString (const char *value, + VarSetOperationType op = eVarSetOperationAssign); + + virtual bool + Clear () + { + m_current_value = m_default_value; + m_value_was_set = false; + return true; + } + + virtual lldb::OptionValueSP + DeepCopy () const; + + //--------------------------------------------------------------------- + // Subclass specific functions + //--------------------------------------------------------------------- + + Flags & + GetOptions () + { + return m_options; + } + + const Flags & + GetOptions () const + { + return m_options; + } + + const char * + operator = (const char *value) + { + SetCurrentValue(value); + return m_current_value.c_str(); + } + + const char * + GetCurrentValue() const + { + return m_current_value.c_str(); + } + + const char * + GetDefaultValue() const + { + return m_default_value.c_str(); + } + + Error + SetCurrentValue (const char *value); + + Error + AppendToCurrentValue (const char *value); + + void + SetDefaultValue (const char *value) + { + if (value && value[0]) + m_default_value.assign (value); + else + m_default_value.clear(); + } + + bool + IsCurrentValueEmpty () const + { + return m_current_value.empty(); + } + + bool + IsDefaultValueEmpty () const + { + return m_default_value.empty(); + } + + +protected: + std::string m_current_value; + std::string m_default_value; + Flags m_options; + ValidatorCallback m_validator; + void* m_validator_baton; +}; + +} // namespace lldb_private + +#endif // liblldb_OptionValueString_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Interpreter/OptionValueUInt64.h b/contrib/llvm/tools/lldb/include/lldb/Interpreter/OptionValueUInt64.h new file mode 100644 index 00000000000..9b5496f9835 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Interpreter/OptionValueUInt64.h @@ -0,0 +1,134 @@ +//===-- OptionValueUInt64.h --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_OptionValueUInt64_h_ +#define liblldb_OptionValueUInt64_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Interpreter/OptionValue.h" + +namespace lldb_private { + +class OptionValueUInt64 : public OptionValue +{ +public: + OptionValueUInt64 () : + OptionValue(), + m_current_value (0), + m_default_value (0) + { + } + + OptionValueUInt64 (uint64_t value) : + OptionValue(), + m_current_value (value), + m_default_value (value) + { + } + + OptionValueUInt64 (uint64_t current_value, + uint64_t default_value) : + OptionValue(), + m_current_value (current_value), + m_default_value (default_value) + { + } + + virtual + ~OptionValueUInt64() + { + } + + //--------------------------------------------------------------------- + // Decode a uint64_t from "value_cstr" return a OptionValueUInt64 object + // inside of a lldb::OptionValueSP object if all goes well. If the + // string isn't a uint64_t value or any other error occurs, return an + // empty lldb::OptionValueSP and fill error in with the correct stuff. + //--------------------------------------------------------------------- + static lldb::OptionValueSP + Create (const char *value_cstr, Error &error); + //--------------------------------------------------------------------- + // Virtual subclass pure virtual overrides + //--------------------------------------------------------------------- + + virtual OptionValue::Type + GetType () const + { + return eTypeUInt64; + } + + virtual void + DumpValue (const ExecutionContext *exe_ctx, Stream &strm, uint32_t dump_mask); + + virtual Error + SetValueFromCString (const char *value, + VarSetOperationType op = eVarSetOperationAssign); + + virtual bool + Clear () + { + m_current_value = m_default_value; + m_value_was_set = false; + return true; + } + + virtual lldb::OptionValueSP + DeepCopy () const; + + //--------------------------------------------------------------------- + // Subclass specific functions + //--------------------------------------------------------------------- + + const uint64_t & + operator = (uint64_t value) + { + m_current_value = value; + return m_current_value; + } + + operator uint64_t () const + { + return m_current_value; + } + + uint64_t + GetCurrentValue() const + { + return m_current_value; + } + + uint64_t + GetDefaultValue() const + { + return m_default_value; + } + + void + SetCurrentValue (uint64_t value) + { + m_current_value = value; + } + + void + SetDefaultValue (uint64_t value) + { + m_default_value = value; + } + +protected: + uint64_t m_current_value; + uint64_t m_default_value; +}; + +} // namespace lldb_private + +#endif // liblldb_OptionValueUInt64_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Interpreter/OptionValueUUID.h b/contrib/llvm/tools/lldb/include/lldb/Interpreter/OptionValueUUID.h new file mode 100644 index 00000000000..caf436e576f --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Interpreter/OptionValueUUID.h @@ -0,0 +1,106 @@ +//===-- OptionValueUUID.h --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_OptionValueUUID_h_ +#define liblldb_OptionValueUUID_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/UUID.h" +#include "lldb/Interpreter/OptionValue.h" + +namespace lldb_private { + +class OptionValueUUID : public OptionValue +{ +public: + OptionValueUUID () : + OptionValue(), + m_uuid () + { + } + + OptionValueUUID (const UUID &uuid) : + OptionValue(), + m_uuid (uuid) + { + } + + virtual + ~OptionValueUUID() + { + } + + //--------------------------------------------------------------------- + // Virtual subclass pure virtual overrides + //--------------------------------------------------------------------- + + virtual OptionValue::Type + GetType () const + { + return eTypeUUID; + } + + virtual void + DumpValue (const ExecutionContext *exe_ctx, Stream &strm, uint32_t dump_mask); + + virtual Error + SetValueFromCString (const char *value, + VarSetOperationType op = eVarSetOperationAssign); + + virtual bool + Clear () + { + m_uuid.Clear(); + m_value_was_set = false; + return true; + } + + virtual lldb::OptionValueSP + DeepCopy () const; + + //--------------------------------------------------------------------- + // Subclass specific functions + //--------------------------------------------------------------------- + + UUID & + GetCurrentValue() + { + return m_uuid; + } + + const UUID & + GetCurrentValue() const + { + return m_uuid; + } + + void + SetCurrentValue (const UUID &value) + { + m_uuid = value; + } + + virtual size_t + AutoComplete (CommandInterpreter &interpreter, + const char *s, + int match_start_point, + int max_return_elements, + bool &word_complete, + StringList &matches); + +protected: + UUID m_uuid; +}; + +} // namespace lldb_private + +#endif // liblldb_OptionValueUUID_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Interpreter/OptionValues.h b/contrib/llvm/tools/lldb/include/lldb/Interpreter/OptionValues.h new file mode 100644 index 00000000000..41b9d2e351f --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Interpreter/OptionValues.h @@ -0,0 +1,31 @@ +//===-- OptionValues.h ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_OptionValues_h_ +#define liblldb_OptionValues_h_ + +#include "lldb/Interpreter/OptionValue.h" +#include "lldb/Interpreter/OptionValueArch.h" +#include "lldb/Interpreter/OptionValueArgs.h" +#include "lldb/Interpreter/OptionValueArray.h" +#include "lldb/Interpreter/OptionValueBoolean.h" +#include "lldb/Interpreter/OptionValueDictionary.h" +#include "lldb/Interpreter/OptionValueEnumeration.h" +#include "lldb/Interpreter/OptionValueFileSpec.h" +#include "lldb/Interpreter/OptionValueFileSpecList.h" +#include "lldb/Interpreter/OptionValueFormat.h" +#include "lldb/Interpreter/OptionValuePathMappings.h" +#include "lldb/Interpreter/OptionValueProperties.h" +#include "lldb/Interpreter/OptionValueRegex.h" +#include "lldb/Interpreter/OptionValueSInt64.h" +#include "lldb/Interpreter/OptionValueString.h" +#include "lldb/Interpreter/OptionValueUInt64.h" +#include "lldb/Interpreter/OptionValueUUID.h" + +#endif // liblldb_OptionValues_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Interpreter/Options.h b/contrib/llvm/tools/lldb/include/lldb/Interpreter/Options.h new file mode 100644 index 00000000000..ac4daa8f579 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Interpreter/Options.h @@ -0,0 +1,487 @@ +//===-- Options.h -----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_Options_h_ +#define liblldb_Options_h_ + +// C Includes +#include + +// C++ Includes +#include +#include + +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private.h" +#include "lldb/lldb-defines.h" +#include "lldb/Interpreter/Args.h" + +namespace lldb_private { + + static inline bool + isprint8 (int ch) + { + if (ch & 0xffffff00u) + return false; + return isprint(ch); + } + + +//---------------------------------------------------------------------- +/// @class Options Options.h "lldb/Interpreter/Options.h" +/// @brief A command line option parsing protocol class. +/// +/// Options is designed to be subclassed to contain all needed +/// options for a given command. The options can be parsed by calling: +/// \code +/// Error Args::ParseOptions (Options &); +/// \endcode +/// +/// The options are specified using the format defined for the libc +/// options parsing function getopt_long_only: +/// \code +/// #include +/// int getopt_long_only(int argc, char * const *argv, const char *optstring, const struct option *longopts, int *longindex); +/// \endcode +/// +/// Example code: +/// \code +/// #include +/// #include +/// +/// class CommandOptions : public Options +/// { +/// public: +/// virtual struct option * +/// GetLongOptions() { +/// return g_options; +/// } +/// +/// virtual Error +/// SetOptionValue (uint32_t option_idx, int option_val, const char *option_arg) +/// { +/// Error error; +/// switch (option_val) +/// { +/// case 'g': debug = true; break; +/// case 'v': verbose = true; break; +/// case 'l': log_file = option_arg; break; +/// case 'f': log_flags = strtoull(option_arg, NULL, 0); break; +/// default: +/// error.SetErrorStringWithFormat("unrecognized short option %c", option_val); +/// break; +/// } +/// +/// return error; +/// } +/// +/// CommandOptions (CommandInterpreter &interpreter) : debug (true), verbose (false), log_file (), log_flags (0) +/// {} +/// +/// bool debug; +/// bool verbose; +/// std::string log_file; +/// uint32_t log_flags; +/// +/// static struct option g_options[]; +/// +/// }; +/// +/// struct option CommandOptions::g_options[] = +/// { +/// { "debug", no_argument, NULL, 'g' }, +/// { "log-file", required_argument, NULL, 'l' }, +/// { "log-flags", required_argument, NULL, 'f' }, +/// { "verbose", no_argument, NULL, 'v' }, +/// { NULL, 0, NULL, 0 } +/// }; +/// +/// int main (int argc, const char **argv, const char **envp) +/// { +/// CommandOptions options; +/// Args main_command; +/// main_command.SetArguments(argc, argv, false); +/// main_command.ParseOptions(options); +/// +/// if (options.verbose) +/// { +/// std::cout << "verbose is on" << std::endl; +/// } +/// } +/// \endcode +//---------------------------------------------------------------------- +class Options +{ +public: + + Options (CommandInterpreter &interpreter); + + virtual + ~Options (); + + void + BuildGetoptTable (); + + void + BuildValidOptionSets (); + + uint32_t + NumCommandOptions (); + + //------------------------------------------------------------------ + /// Get the option definitions to use when parsing Args options. + /// + /// @see Args::ParseOptions (Options&) + /// @see man getopt_long_only + //------------------------------------------------------------------ + struct option * + GetLongOptions (); + + // This gets passed the short option as an integer... + void + OptionSeen (int short_option); + + bool + VerifyOptions (CommandReturnObject &result); + + // Verify that the options given are in the options table and can + // be used together, but there may be some required options that are + // missing (used to verify options that get folded into command aliases). + + bool + VerifyPartialOptions (CommandReturnObject &result); + + void + OutputFormattedUsageText (Stream &strm, + const char *text, + uint32_t output_max_columns); + + void + GenerateOptionUsage (Stream &strm, + CommandObject *cmd); + + bool + SupportsLongOption (const char *long_option); + + // The following two pure virtual functions must be defined by every + // class that inherits from this class. + + virtual const OptionDefinition* + GetDefinitions () { return NULL; } + + // Call this prior to parsing any options. This call will call the + // subclass OptionParsingStarting() and will avoid the need for all + // OptionParsingStarting() function instances from having to call the + // Option::OptionParsingStarting() like they did before. This was error + // prone and subclasses shouldn't have to do it. + void + NotifyOptionParsingStarting (); + + Error + NotifyOptionParsingFinished (); + + //------------------------------------------------------------------ + /// Set the value of an option. + /// + /// @param[in] option_idx + /// The index into the "struct option" array that was returned + /// by Options::GetLongOptions(). + /// + /// @param[in] option_arg + /// The argument value for the option that the user entered, or + /// NULL if there is no argument for the current option. + /// + /// + /// @see Args::ParseOptions (Options&) + /// @see man getopt_long_only + //------------------------------------------------------------------ + virtual Error + SetOptionValue (uint32_t option_idx, const char *option_arg) = 0; + + //------------------------------------------------------------------ + /// Handles the generic bits of figuring out whether we are in an + /// option, and if so completing it. + /// + /// @param[in] input + /// The command line parsed into words + /// + /// @param[in] cursor_index + /// The index in \ainput of the word in which the cursor lies. + /// + /// @param[in] char_pos + /// The character position of the cursor in its argument word. + /// + /// @param[in] match_start_point + /// @param[in] match_return_elements + /// See CommandObject::HandleCompletions for a description of + /// how these work. + /// + /// @param[in] interpreter + /// The interpreter that's doing the completing. + /// + /// @param[out] word_complete + /// \btrue if this is a complete option value (a space will be + /// inserted after the completion.) \b false otherwise. + /// + /// @param[out] matches + /// The array of matches returned. + /// + /// FIXME: This is the wrong return value, since we also need to + /// make a distinction between total number of matches, and the + /// window the user wants returned. + /// + /// @return + /// \btrue if we were in an option, \bfalse otherwise. + //------------------------------------------------------------------ + bool + HandleOptionCompletion (Args &input, + OptionElementVector &option_map, + int cursor_index, + int char_pos, + int match_start_point, + int max_return_elements, + bool &word_complete, + lldb_private::StringList &matches); + + //------------------------------------------------------------------ + /// Handles the generic bits of figuring out whether we are in an + /// option, and if so completing it. + /// + /// @param[in] interpreter + /// The command interpreter doing the completion. + /// + /// @param[in] input + /// The command line parsed into words + /// + /// @param[in] cursor_index + /// The index in \ainput of the word in which the cursor lies. + /// + /// @param[in] char_pos + /// The character position of the cursor in its argument word. + /// + /// @param[in] opt_element_vector + /// The results of the options parse of \a input. + /// + /// @param[in] opt_element_index + /// The position in \a opt_element_vector of the word in \a + /// input containing the cursor. + /// + /// @param[in] match_start_point + /// @param[in] match_return_elements + /// See CommandObject::HandleCompletions for a description of + /// how these work. + /// + /// @param[out] word_complete + /// \btrue if this is a complete option value (a space will + /// be inserted after the completion.) \bfalse otherwise. + /// + /// @param[out] matches + /// The array of matches returned. + /// + /// FIXME: This is the wrong return value, since we also need to + /// make a distinction between total number of matches, and the + /// window the user wants returned. + /// + /// @return + /// \btrue if we were in an option, \bfalse otherwise. + //------------------------------------------------------------------ + virtual bool + HandleOptionArgumentCompletion (Args &input, + int cursor_index, + int char_pos, + OptionElementVector &opt_element_vector, + int opt_element_index, + int match_start_point, + int max_return_elements, + bool &word_complete, + StringList &matches); + +protected: + // This is a set of options expressed as indexes into the options table for this Option. + typedef std::set OptionSet; + typedef std::vector OptionSetVector; + + CommandInterpreter &m_interpreter; + std::vector m_getopt_table; + OptionSet m_seen_options; + OptionSetVector m_required_options; + OptionSetVector m_optional_options; + + OptionSetVector &GetRequiredOptions () + { + BuildValidOptionSets(); + return m_required_options; + } + + OptionSetVector &GetOptionalOptions () + { + BuildValidOptionSets(); + return m_optional_options; + } + + bool + IsASubset (const OptionSet& set_a, const OptionSet& set_b); + + size_t + OptionsSetDiff (const OptionSet &set_a, const OptionSet &set_b, OptionSet &diffs); + + void + OptionsSetUnion (const OptionSet &set_a, const OptionSet &set_b, OptionSet &union_set); + + // Subclasses must reset their option values prior to starting a new + // option parse. Each subclass must override this function and revert + // all option settings to default values. + virtual void + OptionParsingStarting () = 0; + + virtual Error + OptionParsingFinished () + { + // If subclasses need to know when the options are done being parsed + // they can implement this function to do extra checking + Error error; + return error; + } +}; + + class OptionGroup + { + public: + OptionGroup () + { + } + + virtual + ~OptionGroup () + { + } + + virtual uint32_t + GetNumDefinitions () = 0; + + virtual const OptionDefinition* + GetDefinitions () = 0; + + virtual Error + SetOptionValue (CommandInterpreter &interpreter, + uint32_t option_idx, + const char *option_value) = 0; + + virtual void + OptionParsingStarting (CommandInterpreter &interpreter) = 0; + + virtual Error + OptionParsingFinished (CommandInterpreter &interpreter) + { + // If subclasses need to know when the options are done being parsed + // they can implement this function to do extra checking + Error error; + return error; + } + }; + + class OptionGroupOptions : public Options + { + public: + + OptionGroupOptions (CommandInterpreter &interpreter) : + Options (interpreter), + m_option_defs (), + m_option_infos (), + m_did_finalize (false) + { + } + + virtual + ~OptionGroupOptions () + { + } + + + //---------------------------------------------------------------------- + /// Append options from a OptionGroup class. + /// + /// Append all options from \a group using the exact same option groups + /// that each option is defined with. + /// + /// @param[in] group + /// A group of options to take option values from and copy their + /// definitions into this class. + //---------------------------------------------------------------------- + void + Append (OptionGroup* group); + + //---------------------------------------------------------------------- + /// Append options from a OptionGroup class. + /// + /// Append options from \a group that have a usage mask that has any bits + /// in "src_mask" set. After the option definition is copied into the + /// options definitions in this class, set the usage_mask to "dst_mask". + /// + /// @param[in] group + /// A group of options to take option values from and copy their + /// definitions into this class. + /// + /// @param[in] src_mask + /// When copying options from \a group, you might only want some of + /// the options to be appended to this group. This mask allows you + /// to control which options from \a group get added. It also allows + /// you to specify the same options from \a group multiple times + /// for different option sets. + /// + /// @param[in] dst_mask + /// Set the usage mask for any copied options to \a dst_mask after + /// copying the option definition. + //---------------------------------------------------------------------- + void + Append (OptionGroup* group, + uint32_t src_mask, + uint32_t dst_mask); + + void + Finalize (); + + virtual Error + SetOptionValue (uint32_t option_idx, + const char *option_arg); + + virtual void + OptionParsingStarting (); + + virtual Error + OptionParsingFinished (); + + const OptionDefinition* + GetDefinitions () + { + assert (m_did_finalize); + return &m_option_defs[0]; + } + struct OptionInfo + { + OptionInfo (OptionGroup* g, uint32_t i) : + option_group (g), + option_index (i) + { + } + OptionGroup* option_group; // The group that this option came from + uint32_t option_index; // The original option index from the OptionGroup + }; + typedef std::vector OptionInfos; + + std::vector m_option_defs; + OptionInfos m_option_infos; + bool m_did_finalize; + }; + + +} // namespace lldb_private + +#endif // liblldb_Options_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Interpreter/Property.h b/contrib/llvm/tools/lldb/include/lldb/Interpreter/Property.h new file mode 100644 index 00000000000..b192758cbc4 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Interpreter/Property.h @@ -0,0 +1,109 @@ +//===-- Property.h ----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_Property_h_ +#define liblldb_Property_h_ + +// C Includes +// C++ Includes +#include + +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-defines.h" +#include "lldb/Core/ConstString.h" +#include "lldb/Core/Flags.h" +#include "lldb/Interpreter/OptionValue.h" + +namespace lldb_private { + + // A structure that can be used to create a global table for all properties. + // Property class instances can be constructed using one of these. + struct PropertyDefinition + { + const char *name; + OptionValue::Type type; + bool global; + uintptr_t default_uint_value; + const char *default_cstr_value; + OptionEnumValueElement *enum_values; + const char *description; + }; + + class Property + { + public: + Property (const PropertyDefinition &definition); + + Property (const ConstString &name, + const ConstString &desc, + bool is_global, + const lldb::OptionValueSP &value_sp); + + const ConstString & + GetName() const + { + return m_name; + } + + const char * + GetDescription () const + { + return m_description.GetCString(); + } + + const lldb::OptionValueSP & + GetValue() const + { + return m_value_sp; + } + + void + SetOptionValue (const lldb::OptionValueSP &value_sp) + { + m_value_sp = value_sp; + } + + + bool + IsValid() const + { + return (bool)m_value_sp; + } + + bool + IsGlobal () const + { + return m_is_global; + } + + void + Dump (const ExecutionContext *exe_ctx, + Stream &strm, + uint32_t dump_mask) const; + + bool + DumpQualifiedName(Stream &strm) const; + + void + DumpDescription (CommandInterpreter &interpreter, + Stream &strm, + uint32_t output_width, + bool display_qualified_name) const; + + protected: + ConstString m_name; + ConstString m_description; + lldb::OptionValueSP m_value_sp; + bool m_is_global; + }; + +} // namespace lldb_private + +#endif // liblldb_Property_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Interpreter/PythonDataObjects.h b/contrib/llvm/tools/lldb/include/lldb/Interpreter/PythonDataObjects.h new file mode 100644 index 00000000000..b2c9240db09 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Interpreter/PythonDataObjects.h @@ -0,0 +1,233 @@ +//===-- PythonDataObjects.h----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_PythonDataObjects_h_ +#define liblldb_PythonDataObjects_h_ + +// C Includes +// C++ Includes + +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-defines.h" +#include "lldb/Core/ConstString.h" +#include "lldb/Core/Flags.h" +#include "lldb/Interpreter/OptionValue.h" +#if defined (__APPLE__) +#include +#else +#include +#endif + +namespace lldb_private { + + class PythonObject + { + public: + PythonObject () : + m_py_obj(NULL) + { + } + + PythonObject (PyObject* py_obj) : + m_py_obj(NULL) + { + Reset (py_obj); + } + + PythonObject (const PythonObject &rhs) : + m_py_obj(NULL) + { + Reset (rhs.m_py_obj); + } + + PythonObject (const lldb::ScriptInterpreterObjectSP &script_object_sp); + + virtual + ~PythonObject () + { + Reset (NULL); + } + + const PythonObject & + operator = (const PythonObject &rhs) + { + if (this != &rhs) + Reset (rhs.m_py_obj); + return *this; + } + + bool + Reset (const PythonObject &object) + { + return Reset(object.GetPythonObject()); + } + + virtual bool + Reset (PyObject* py_obj = NULL) + { + if (py_obj != m_py_obj) + { + Py_XDECREF(m_py_obj); + m_py_obj = py_obj; + Py_XINCREF(m_py_obj); + } + return true; + } + + void + Dump () const + { + if (m_py_obj) + _PyObject_Dump (m_py_obj); + else + puts ("NULL"); + } + + void + Dump (Stream &strm) const; + + PyObject* + GetPythonObject () const + { + return m_py_obj; + } + + PythonString + Repr (); + + PythonString + Str (); + + operator bool () const + { + return m_py_obj != NULL; + } + + protected: + PyObject* m_py_obj; + }; + + class PythonString: public PythonObject + { + public: + + PythonString (); + PythonString (PyObject *o); + PythonString (const PythonObject &object); + PythonString (const lldb::ScriptInterpreterObjectSP &script_object_sp); + PythonString (const char* string); + virtual ~PythonString (); + + virtual bool + Reset (PyObject* py_obj = NULL); + + const char* + GetString() const; + + size_t + GetSize() const; + + void + SetString (const char* string); + }; + + class PythonInteger: public PythonObject + { + public: + + PythonInteger (); + PythonInteger (PyObject* py_obj); + PythonInteger (const PythonObject &object); + PythonInteger (const lldb::ScriptInterpreterObjectSP &script_object_sp); + PythonInteger (int64_t value); + virtual ~PythonInteger (); + + virtual bool + Reset (PyObject* py_obj = NULL); + + int64_t + GetInteger(); + + void + SetInteger (int64_t value); + }; + + class PythonList: public PythonObject + { + public: + + PythonList (); + PythonList (PyObject* py_obj); + PythonList (const PythonObject &object); + PythonList (const lldb::ScriptInterpreterObjectSP &script_object_sp); + PythonList (uint32_t count); + virtual ~PythonList (); + + virtual bool + Reset (PyObject* py_obj = NULL); + + uint32_t + GetSize(); + + PythonObject + GetItemAtIndex (uint32_t index); + + void + SetItemAtIndex (uint32_t index, const PythonObject &object); + + void + AppendItem (const PythonObject &object); + }; + + class PythonDictionary: public PythonObject + { + public: + + PythonDictionary (); + PythonDictionary (PyObject* object); + PythonDictionary (const PythonObject &object); + PythonDictionary (const lldb::ScriptInterpreterObjectSP &script_object_sp); + virtual ~PythonDictionary (); + + virtual bool + Reset (PyObject* object = NULL); + + uint32_t GetSize(); + + PythonObject + GetItemForKey (const PythonString &key) const; + + const char * + GetItemForKeyAsString (const PythonString &key, const char *fail_value = NULL) const; + + int64_t + GetItemForKeyAsInteger (const PythonString &key, int64_t fail_value = 0) const; + + PythonObject + GetItemForKey (const char *key) const; + + typedef bool (*DictionaryIteratorCallback)(PythonString* key, PythonDictionary* dict); + + PythonList + GetKeys () const; + + PythonString + GetKeyAtPosition (uint32_t pos) const; + + PythonObject + GetValueAtPosition (uint32_t pos) const; + + void + SetItemForKey (const PythonString &key, const PythonObject& value); + }; + +} // namespace lldb_private + +#endif // liblldb_PythonDataObjects_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Interpreter/ScriptInterpreter.h b/contrib/llvm/tools/lldb/include/lldb/Interpreter/ScriptInterpreter.h new file mode 100644 index 00000000000..9a66c775d47 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Interpreter/ScriptInterpreter.h @@ -0,0 +1,519 @@ +//===-- ScriptInterpreter.h -------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ScriptInterpreter_h_ +#define liblldb_ScriptInterpreter_h_ + +#include "lldb/lldb-private.h" + +#include "lldb/Core/Broadcaster.h" +#include "lldb/Core/Error.h" + +#include "lldb/Utility/PseudoTerminal.h" + + +namespace lldb_private { + +class ScriptInterpreterObject +{ +public: + ScriptInterpreterObject() : + m_object(NULL) + {} + + ScriptInterpreterObject(void* obj) : + m_object(obj) + {} + + ScriptInterpreterObject(const ScriptInterpreterObject& rhs) + : m_object(rhs.m_object) + {} + + virtual void* + GetObject() + { + return m_object; + } + + operator bool () + { + return m_object != NULL; + } + + ScriptInterpreterObject& + operator = (const ScriptInterpreterObject& rhs) + { + if (this != &rhs) + m_object = rhs.m_object; + return *this; + } + + virtual + ~ScriptInterpreterObject() + {} + +protected: + void* m_object; +}; + +class ScriptInterpreterLocker +{ +public: + + ScriptInterpreterLocker () + { + } + + virtual ~ScriptInterpreterLocker () + { + } +private: + DISALLOW_COPY_AND_ASSIGN (ScriptInterpreterLocker); +}; + + +class ScriptInterpreter +{ +public: + + typedef void (*SWIGInitCallback) (void); + + typedef bool (*SWIGBreakpointCallbackFunction) (const char *python_function_name, + const char *session_dictionary_name, + const lldb::StackFrameSP& frame_sp, + const lldb::BreakpointLocationSP &bp_loc_sp); + + typedef bool (*SWIGWatchpointCallbackFunction) (const char *python_function_name, + const char *session_dictionary_name, + const lldb::StackFrameSP& frame_sp, + const lldb::WatchpointSP &wp_sp); + + typedef bool (*SWIGPythonTypeScriptCallbackFunction) (const char *python_function_name, + void *session_dictionary, + const lldb::ValueObjectSP& valobj_sp, + void** pyfunct_wrapper, + std::string& retval); + + typedef void* (*SWIGPythonCreateSyntheticProvider) (const char *python_class_name, + const char *session_dictionary_name, + const lldb::ValueObjectSP& valobj_sp); + + typedef void* (*SWIGPythonCreateOSPlugin) (const char *python_class_name, + const char *session_dictionary_name, + const lldb::ProcessSP& process_sp); + + typedef uint32_t (*SWIGPythonCalculateNumChildren) (void *implementor); + typedef void* (*SWIGPythonGetChildAtIndex) (void *implementor, uint32_t idx); + typedef int (*SWIGPythonGetIndexOfChildWithName) (void *implementor, const char* child_name); + typedef void* (*SWIGPythonCastPyObjectToSBValue) (void* data); + typedef bool (*SWIGPythonUpdateSynthProviderInstance) (void* data); + typedef bool (*SWIGPythonMightHaveChildrenSynthProviderInstance) (void* data); + + + typedef bool (*SWIGPythonCallCommand) (const char *python_function_name, + const char *session_dictionary_name, + lldb::DebuggerSP& debugger, + const char* args, + lldb_private::CommandReturnObject& cmd_retobj); + + typedef bool (*SWIGPythonCallModuleInit) (const char *python_module_name, + const char *session_dictionary_name, + lldb::DebuggerSP& debugger); + + typedef bool (*SWIGPythonScriptKeyword_Process) (const char* python_function_name, + const char* session_dictionary_name, + lldb::ProcessSP& process, + std::string& output); + typedef bool (*SWIGPythonScriptKeyword_Thread) (const char* python_function_name, + const char* session_dictionary_name, + lldb::ThreadSP& thread, + std::string& output); + + typedef bool (*SWIGPythonScriptKeyword_Target) (const char* python_function_name, + const char* session_dictionary_name, + lldb::TargetSP& target, + std::string& output); + + typedef bool (*SWIGPythonScriptKeyword_Frame) (const char* python_function_name, + const char* session_dictionary_name, + lldb::StackFrameSP& frame, + std::string& output); + + + + typedef enum + { + eScriptReturnTypeCharPtr, + eScriptReturnTypeBool, + eScriptReturnTypeShortInt, + eScriptReturnTypeShortIntUnsigned, + eScriptReturnTypeInt, + eScriptReturnTypeIntUnsigned, + eScriptReturnTypeLongInt, + eScriptReturnTypeLongIntUnsigned, + eScriptReturnTypeLongLong, + eScriptReturnTypeLongLongUnsigned, + eScriptReturnTypeFloat, + eScriptReturnTypeDouble, + eScriptReturnTypeChar, + eScriptReturnTypeCharStrOrNone + } ScriptReturnType; + + ScriptInterpreter (CommandInterpreter &interpreter, lldb::ScriptLanguage script_lang); + + virtual ~ScriptInterpreter (); + + struct ExecuteScriptOptions + { + public: + ExecuteScriptOptions () : + m_enable_io(true), + m_set_lldb_globals(true), + m_maskout_errors(true) + { + } + + bool + GetEnableIO () const + { + return m_enable_io; + } + + bool + GetSetLLDBGlobals () const + { + return m_set_lldb_globals; + } + + bool + GetMaskoutErrors () const + { + return m_maskout_errors; + } + + ExecuteScriptOptions& + SetEnableIO (bool enable) + { + m_enable_io = enable; + return *this; + } + + ExecuteScriptOptions& + SetSetLLDBGlobals (bool set) + { + m_set_lldb_globals = set; + return *this; + } + + ExecuteScriptOptions& + SetMaskoutErrors (bool maskout) + { + m_maskout_errors = maskout; + return *this; + } + + private: + bool m_enable_io; + bool m_set_lldb_globals; + bool m_maskout_errors; + }; + + virtual bool + ExecuteOneLine (const char *command, + CommandReturnObject *result, + const ExecuteScriptOptions &options = ExecuteScriptOptions()) = 0; + + virtual void + ExecuteInterpreterLoop () = 0; + + virtual bool + ExecuteOneLineWithReturn (const char *in_string, + ScriptReturnType return_type, + void *ret_value, + const ExecuteScriptOptions &options = ExecuteScriptOptions()) + { + return true; + } + + virtual bool + ExecuteMultipleLines (const char *in_string, + const ExecuteScriptOptions &options = ExecuteScriptOptions()) + { + return true; + } + + virtual bool + ExportFunctionDefinitionToInterpreter (StringList &function_def) + { + return false; + } + + virtual bool + GenerateBreakpointCommandCallbackData (StringList &input, std::string& output) + { + return false; + } + + virtual bool + GenerateWatchpointCommandCallbackData (StringList &input, std::string& output) + { + return false; + } + + virtual bool + GenerateTypeScriptFunction (const char* oneliner, std::string& output, void* name_token = NULL) + { + return false; + } + + virtual bool + GenerateTypeScriptFunction (StringList &input, std::string& output, void* name_token = NULL) + { + return false; + } + + virtual bool + GenerateScriptAliasFunction (StringList &input, std::string& output) + { + return false; + } + + virtual bool + GenerateTypeSynthClass (StringList &input, std::string& output, void* name_token = NULL) + { + return false; + } + + virtual bool + GenerateTypeSynthClass (const char* oneliner, std::string& output, void* name_token = NULL) + { + return false; + } + + virtual lldb::ScriptInterpreterObjectSP + CreateSyntheticScriptedProvider (const char *class_name, + lldb::ValueObjectSP valobj) + { + return lldb::ScriptInterpreterObjectSP(); + } + + virtual lldb::ScriptInterpreterObjectSP + OSPlugin_CreatePluginObject (const char *class_name, + lldb::ProcessSP process_sp) + { + return lldb::ScriptInterpreterObjectSP(); + } + + virtual lldb::ScriptInterpreterObjectSP + OSPlugin_RegisterInfo (lldb::ScriptInterpreterObjectSP os_plugin_object_sp) + { + return lldb::ScriptInterpreterObjectSP(); + } + + virtual lldb::ScriptInterpreterObjectSP + OSPlugin_ThreadsInfo (lldb::ScriptInterpreterObjectSP os_plugin_object_sp) + { + return lldb::ScriptInterpreterObjectSP(); + } + + virtual lldb::ScriptInterpreterObjectSP + OSPlugin_RegisterContextData (lldb::ScriptInterpreterObjectSP os_plugin_object_sp, + lldb::tid_t thread_id) + { + return lldb::ScriptInterpreterObjectSP(); + } + + virtual lldb::ScriptInterpreterObjectSP + OSPlugin_CreateThread (lldb::ScriptInterpreterObjectSP os_plugin_object_sp, + lldb::tid_t tid, + lldb::addr_t context) + { + return lldb::ScriptInterpreterObjectSP(); + } + + virtual bool + GenerateFunction(const char *signature, const StringList &input) + { + return false; + } + + virtual void + CollectDataForBreakpointCommandCallback (BreakpointOptions *bp_options, + CommandReturnObject &result); + + virtual void + CollectDataForWatchpointCommandCallback (WatchpointOptions *wp_options, + CommandReturnObject &result); + + /// Set a one-liner as the callback for the breakpoint. + virtual void + SetBreakpointCommandCallback (BreakpointOptions *bp_options, + const char *oneliner) + { + return; + } + + /// Set a one-liner as the callback for the watchpoint. + virtual void + SetWatchpointCommandCallback (WatchpointOptions *wp_options, + const char *oneliner) + { + return; + } + + virtual bool + GetScriptedSummary (const char *function_name, + lldb::ValueObjectSP valobj, + lldb::ScriptInterpreterObjectSP& callee_wrapper_sp, + std::string& retval) + { + return false; + } + + virtual size_t + CalculateNumChildren (const lldb::ScriptInterpreterObjectSP& implementor) + { + return 0; + } + + virtual lldb::ValueObjectSP + GetChildAtIndex (const lldb::ScriptInterpreterObjectSP& implementor, uint32_t idx) + { + return lldb::ValueObjectSP(); + } + + virtual int + GetIndexOfChildWithName (const lldb::ScriptInterpreterObjectSP& implementor, const char* child_name) + { + return UINT32_MAX; + } + + virtual bool + UpdateSynthProviderInstance (const lldb::ScriptInterpreterObjectSP& implementor) + { + return false; + } + + virtual bool + MightHaveChildrenSynthProviderInstance (const lldb::ScriptInterpreterObjectSP& implementor) + { + return true; + } + + virtual bool + RunScriptBasedCommand (const char* impl_function, + const char* args, + ScriptedCommandSynchronicity synchronicity, + lldb_private::CommandReturnObject& cmd_retobj, + Error& error) + { + return false; + } + + virtual bool + RunScriptFormatKeyword (const char* impl_function, + Process* process, + std::string& output, + Error& error) + { + error.SetErrorString("unimplemented"); + return false; + } + + virtual bool + RunScriptFormatKeyword (const char* impl_function, + Thread* thread, + std::string& output, + Error& error) + { + error.SetErrorString("unimplemented"); + return false; + } + + virtual bool + RunScriptFormatKeyword (const char* impl_function, + Target* target, + std::string& output, + Error& error) + { + error.SetErrorString("unimplemented"); + return false; + } + + virtual bool + RunScriptFormatKeyword (const char* impl_function, + StackFrame* frame, + std::string& output, + Error& error) + { + error.SetErrorString("unimplemented"); + return false; + } + + virtual bool + GetDocumentationForItem (const char* item, std::string& dest) + { + dest.clear(); + return false; + } + + virtual bool + CheckObjectExists (const char* name) + { + return false; + } + + virtual bool + LoadScriptingModule (const char* filename, + bool can_reload, + bool init_session, + lldb_private::Error& error) + { + error.SetErrorString("loading unimplemented"); + return false; + } + + virtual lldb::ScriptInterpreterObjectSP + MakeScriptObject (void* object) + { + return lldb::ScriptInterpreterObjectSP(new ScriptInterpreterObject(object)); + } + + virtual std::unique_ptr + AcquireInterpreterLock (); + + const char * + GetScriptInterpreterPtyName (); + + int + GetMasterFileDescriptor (); + + CommandInterpreter & + GetCommandInterpreter (); + + static std::string + LanguageToString (lldb::ScriptLanguage language); + + static void + InitializeInterpreter (SWIGInitCallback python_swig_init_callback); + + static void + TerminateInterpreter (); + + virtual void + ResetOutputFileHandle (FILE *new_fh) { } //By default, do nothing. + +protected: + CommandInterpreter &m_interpreter; + lldb::ScriptLanguage m_script_lang; +}; + +} // namespace lldb_private + +#endif // #ifndef liblldb_ScriptInterpreter_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Interpreter/ScriptInterpreterNone.h b/contrib/llvm/tools/lldb/include/lldb/Interpreter/ScriptInterpreterNone.h new file mode 100644 index 00000000000..6c82b60b0bc --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Interpreter/ScriptInterpreterNone.h @@ -0,0 +1,35 @@ +//===-- ScriptInterpreterNone.h ---------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ScriptInterpreterNone_h_ +#define liblldb_ScriptInterpreterNone_h_ + +#include "lldb/Interpreter/ScriptInterpreter.h" + +namespace lldb_private { + +class ScriptInterpreterNone : public ScriptInterpreter +{ +public: + + ScriptInterpreterNone (CommandInterpreter &interpreter); + + ~ScriptInterpreterNone (); + + bool + ExecuteOneLine (const char *command, CommandReturnObject *result, const ExecuteScriptOptions &options = ExecuteScriptOptions()); + + void + ExecuteInterpreterLoop (); + +}; + +} // namespace lldb_private + +#endif // #ifndef liblldb_ScriptInterpreterNone_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Interpreter/ScriptInterpreterPython.h b/contrib/llvm/tools/lldb/include/lldb/Interpreter/ScriptInterpreterPython.h new file mode 100644 index 00000000000..2616f575d20 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Interpreter/ScriptInterpreterPython.h @@ -0,0 +1,407 @@ +//===-- ScriptInterpreterPython.h -------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + + +#ifndef liblldb_ScriptInterpreterPython_h_ +#define liblldb_ScriptInterpreterPython_h_ + +#ifdef LLDB_DISABLE_PYTHON + +// Python is disabled in this build + +#else + +#if defined (__APPLE__) +#include +#else +#include +#endif + +#include "lldb/lldb-private.h" +#include "lldb/Interpreter/ScriptInterpreter.h" +#include "lldb/Core/InputReader.h" +#include "lldb/Host/Terminal.h" + +namespace lldb_private { + +class ScriptInterpreterPython : public ScriptInterpreter +{ +public: + + ScriptInterpreterPython (CommandInterpreter &interpreter); + + ~ScriptInterpreterPython (); + + bool + ExecuteOneLine (const char *command, + CommandReturnObject *result, + const ExecuteScriptOptions &options = ExecuteScriptOptions()); + + void + ExecuteInterpreterLoop (); + + bool + ExecuteOneLineWithReturn (const char *in_string, + ScriptInterpreter::ScriptReturnType return_type, + void *ret_value, + const ExecuteScriptOptions &options = ExecuteScriptOptions()); + + bool + ExecuteMultipleLines (const char *in_string, + const ExecuteScriptOptions &options = ExecuteScriptOptions()); + + bool + ExportFunctionDefinitionToInterpreter (StringList &function_def); + + bool + GenerateTypeScriptFunction (StringList &input, std::string& output, void* name_token = NULL); + + bool + GenerateTypeSynthClass (StringList &input, std::string& output, void* name_token = NULL); + + bool + GenerateTypeSynthClass (const char* oneliner, std::string& output, void* name_token = NULL); + + // use this if the function code is just a one-liner script + bool + GenerateTypeScriptFunction (const char* oneliner, std::string& output, void* name_token = NULL); + + virtual bool + GenerateScriptAliasFunction (StringList &input, std::string& output); + + lldb::ScriptInterpreterObjectSP + CreateSyntheticScriptedProvider (const char *class_name, + lldb::ValueObjectSP valobj); + + virtual lldb::ScriptInterpreterObjectSP + OSPlugin_CreatePluginObject (const char *class_name, + lldb::ProcessSP process_sp); + + virtual lldb::ScriptInterpreterObjectSP + OSPlugin_RegisterInfo (lldb::ScriptInterpreterObjectSP os_plugin_object_sp); + + virtual lldb::ScriptInterpreterObjectSP + OSPlugin_ThreadsInfo (lldb::ScriptInterpreterObjectSP os_plugin_object_sp); + + virtual lldb::ScriptInterpreterObjectSP + OSPlugin_RegisterContextData (lldb::ScriptInterpreterObjectSP os_plugin_object_sp, + lldb::tid_t thread_id); + + virtual lldb::ScriptInterpreterObjectSP + OSPlugin_CreateThread (lldb::ScriptInterpreterObjectSP os_plugin_object_sp, + lldb::tid_t tid, + lldb::addr_t context); + + virtual size_t + CalculateNumChildren (const lldb::ScriptInterpreterObjectSP& implementor); + + virtual lldb::ValueObjectSP + GetChildAtIndex (const lldb::ScriptInterpreterObjectSP& implementor, uint32_t idx); + + virtual int + GetIndexOfChildWithName (const lldb::ScriptInterpreterObjectSP& implementor, const char* child_name); + + virtual bool + UpdateSynthProviderInstance (const lldb::ScriptInterpreterObjectSP& implementor); + + virtual bool + MightHaveChildrenSynthProviderInstance (const lldb::ScriptInterpreterObjectSP& implementor); + + virtual bool + RunScriptBasedCommand(const char* impl_function, + const char* args, + ScriptedCommandSynchronicity synchronicity, + lldb_private::CommandReturnObject& cmd_retobj, + Error& error); + + bool + GenerateFunction(const char *signature, const StringList &input); + + bool + GenerateBreakpointCommandCallbackData (StringList &input, std::string& output); + + bool + GenerateWatchpointCommandCallbackData (StringList &input, std::string& output); + + static size_t + GenerateBreakpointOptionsCommandCallback (void *baton, + InputReader &reader, + lldb::InputReaderAction notification, + const char *bytes, + size_t bytes_len); + + static size_t + GenerateWatchpointOptionsCommandCallback (void *baton, + InputReader &reader, + lldb::InputReaderAction notification, + const char *bytes, + size_t bytes_len); + + static bool + BreakpointCallbackFunction (void *baton, + StoppointCallbackContext *context, + lldb::user_id_t break_id, + lldb::user_id_t break_loc_id); + + static bool + WatchpointCallbackFunction (void *baton, + StoppointCallbackContext *context, + lldb::user_id_t watch_id); + + virtual bool + GetScriptedSummary (const char *function_name, + lldb::ValueObjectSP valobj, + lldb::ScriptInterpreterObjectSP& callee_wrapper_sp, + std::string& retval); + + virtual bool + GetDocumentationForItem (const char* item, std::string& dest); + + virtual bool + CheckObjectExists (const char* name) + { + if (!name || !name[0]) + return false; + std::string temp; + return GetDocumentationForItem (name,temp); + } + + virtual bool + RunScriptFormatKeyword (const char* impl_function, + Process* process, + std::string& output, + Error& error); + + virtual bool + RunScriptFormatKeyword (const char* impl_function, + Thread* thread, + std::string& output, + Error& error); + + virtual bool + RunScriptFormatKeyword (const char* impl_function, + Target* target, + std::string& output, + Error& error); + + virtual bool + RunScriptFormatKeyword (const char* impl_function, + StackFrame* frame, + std::string& output, + Error& error); + + virtual bool + LoadScriptingModule (const char* filename, + bool can_reload, + bool init_session, + lldb_private::Error& error); + + virtual lldb::ScriptInterpreterObjectSP + MakeScriptObject (void* object); + + virtual std::unique_ptr + AcquireInterpreterLock (); + + void + CollectDataForBreakpointCommandCallback (BreakpointOptions *bp_options, + CommandReturnObject &result); + + void + CollectDataForWatchpointCommandCallback (WatchpointOptions *wp_options, + CommandReturnObject &result); + + /// Set a Python one-liner as the callback for the breakpoint. + void + SetBreakpointCommandCallback (BreakpointOptions *bp_options, + const char *oneliner); + + /// Set a one-liner as the callback for the watchpoint. + void + SetWatchpointCommandCallback (WatchpointOptions *wp_options, + const char *oneliner); + + StringList + ReadCommandInputFromUser (FILE *in_file); + + virtual void + ResetOutputFileHandle (FILE *new_fh); + + static lldb::thread_result_t + RunEmbeddedPythonInterpreter (lldb::thread_arg_t baton); + + static void + InitializePrivate (); + + static void + InitializeInterpreter (SWIGInitCallback python_swig_init_callback); + +protected: + + bool + EnterSession (bool init_lldb_globals); + + void + LeaveSession (); + + void + SaveTerminalState (int fd); + + void + RestoreTerminalState (); + +private: + + class SynchronicityHandler + { + private: + lldb::DebuggerSP m_debugger_sp; + ScriptedCommandSynchronicity m_synch_wanted; + bool m_old_asynch; + public: + SynchronicityHandler(lldb::DebuggerSP, + ScriptedCommandSynchronicity); + ~SynchronicityHandler(); + }; + + class ScriptInterpreterPythonObject : public ScriptInterpreterObject + { + public: + ScriptInterpreterPythonObject() : + ScriptInterpreterObject() + {} + + ScriptInterpreterPythonObject(void* obj) : + ScriptInterpreterObject(obj) + { + Py_XINCREF(m_object); + } + + operator bool () + { + return m_object && m_object != Py_None; + } + + + virtual + ~ScriptInterpreterPythonObject() + { + Py_XDECREF(m_object); + m_object = NULL; + } + private: + DISALLOW_COPY_AND_ASSIGN (ScriptInterpreterPythonObject); + }; + + class Locker : public ScriptInterpreterLocker + { + public: + + enum OnEntry + { + AcquireLock = 0x0001, + InitSession = 0x0002, + InitGlobals = 0x0004 + }; + + enum OnLeave + { + FreeLock = 0x0001, + FreeAcquiredLock = 0x0002, // do not free the lock if we already held it when calling constructor + TearDownSession = 0x0004 + }; + + Locker (ScriptInterpreterPython *py_interpreter = NULL, + uint16_t on_entry = AcquireLock | InitSession, + uint16_t on_leave = FreeLock | TearDownSession, + FILE* wait_msg_handle = NULL); + + ~Locker (); + + private: + + bool + DoAcquireLock (); + + bool + DoInitSession (bool init_lldb_globals); + + bool + DoFreeLock (); + + bool + DoTearDownSession (); + + static void + ReleasePythonLock (); + + bool m_teardown_session; + ScriptInterpreterPython *m_python_interpreter; + FILE* m_tmp_fh; + PyGILState_STATE m_GILState; + }; + + class PythonInputReaderManager + { + public: + PythonInputReaderManager (ScriptInterpreterPython *interpreter); + + operator bool() + { + return m_error; + } + + ~PythonInputReaderManager(); + + private: + + static size_t + InputReaderCallback (void *baton, + InputReader &reader, + lldb::InputReaderAction notification, + const char *bytes, + size_t bytes_len); + + static lldb::thread_result_t + RunPythonInputReader (lldb::thread_arg_t baton); + + ScriptInterpreterPython *m_interpreter; + lldb::DebuggerSP m_debugger_sp; + lldb::InputReaderSP m_reader_sp; + bool m_error; + }; + + static size_t + InputReaderCallback (void *baton, + InputReader &reader, + lldb::InputReaderAction notification, + const char *bytes, + size_t bytes_len); + + + lldb_utility::PseudoTerminal m_embedded_thread_pty; + lldb_utility::PseudoTerminal m_embedded_python_pty; + lldb::InputReaderSP m_embedded_thread_input_reader_sp; + lldb::InputReaderSP m_embedded_python_input_reader_sp; + FILE *m_dbg_stdout; + PyObject *m_new_sysout; + PyObject *m_old_sysout; + PyObject *m_old_syserr; + PyObject *m_run_one_line; + std::string m_dictionary_name; + TerminalState m_terminal_state; + bool m_session_is_active; + bool m_pty_slave_is_open; + bool m_valid_session; + PyThreadState *m_command_thread_state; +}; +} // namespace lldb_private + +#endif // #ifdef LLDB_DISABLE_PYTHON + +#endif // #ifndef liblldb_ScriptInterpreterPython_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Symbol/Block.h b/contrib/llvm/tools/lldb/include/lldb/Symbol/Block.h new file mode 100644 index 00000000000..a2d703b9069 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Symbol/Block.h @@ -0,0 +1,496 @@ +//===-- Block.h -------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_Block_h_ +#define liblldb_Block_h_ + +#include "lldb/lldb-private.h" +#include "lldb/Core/AddressRange.h" +#include "lldb/Core/RangeMap.h" +#include "lldb/Core/Stream.h" +#include "lldb/Core/UserID.h" +#include "lldb/Symbol/LineEntry.h" +#include "lldb/Symbol/SymbolContext.h" + +namespace lldb_private { + +//---------------------------------------------------------------------- +/// @class Block Block.h "lldb/Symbol/Block.h" +/// @brief A class that describes a single lexical block. +/// +/// A Function object owns a BlockList object which owns one or more +/// Block objects. The BlockList object contains a section offset +/// address range, and Block objects contain one or more ranges +/// which are offsets into that range. Blocks are can have discontiguous +/// ranges within the BlockList adress range, and each block can +/// contain child blocks each with their own sets of ranges. +/// +/// Each block has a variable list that represents local, argument, and +/// static variables that are scoped to the block. +/// +/// Inlined functions are representated by attaching a +/// InlineFunctionInfo shared pointer object to a block. Inlined +/// functions are represented as named blocks. +//---------------------------------------------------------------------- +class Block : + public UserID, + public SymbolContextScope +{ +public: + typedef RangeArray RangeList; + typedef RangeList::Entry Range; + + //------------------------------------------------------------------ + /// Construct with a User ID \a uid, \a depth. + /// + /// Initialize this block with the specified UID \a uid. The + /// \a depth in the \a block_list is used to represent the parent, + /// sibling, and child block information and also allows for partial + /// parsing at the block level. + /// + /// @param[in] uid + /// The UID for a given block. This value is given by the + /// SymbolFile plug-in and can be any value that helps the + /// SymbolFile plug-in to match this block back to the debug + /// information data that it parses for further or more in + /// depth parsing. Common values would be the index into a + /// table, or an offset into the debug information. + /// + /// @param[in] depth + /// The integer depth of this block in the block list hierarchy. + /// + /// @param[in] block_list + /// The block list that this object belongs to. + /// + /// @see BlockList + //------------------------------------------------------------------ + Block (lldb::user_id_t uid); + + //------------------------------------------------------------------ + /// Destructor. + //------------------------------------------------------------------ + virtual ~Block (); + + //------------------------------------------------------------------ + /// Add a child to this object. + /// + /// @param[in] child_block_sp + /// A shared pointer to a child block that will get added to + /// this block. + //------------------------------------------------------------------ + void + AddChild (const lldb::BlockSP &child_block_sp); + + //------------------------------------------------------------------ + /// Add a new offset range to this block. + /// + /// @param[in] start_offset + /// An offset into this Function's address range that + /// describes the start address of a range for this block. + /// + /// @param[in] end_offset + /// An offset into this Function's address range that + /// describes the end address of a range for this block. + //------------------------------------------------------------------ + void + AddRange (const Range& range); + + void + FinalizeRanges (); + + //------------------------------------------------------------------ + /// @copydoc SymbolContextScope::CalculateSymbolContext(SymbolContext*) + /// + /// @see SymbolContextScope + //------------------------------------------------------------------ + virtual void + CalculateSymbolContext(SymbolContext* sc); + + virtual lldb::ModuleSP + CalculateSymbolContextModule (); + + virtual CompileUnit * + CalculateSymbolContextCompileUnit (); + + virtual Function * + CalculateSymbolContextFunction (); + + virtual Block * + CalculateSymbolContextBlock (); + + //------------------------------------------------------------------ + /// Check if an offset is in one of the block offset ranges. + /// + /// @param[in] range_offset + /// An offset into the Function's address range. + /// + /// @return + /// Returns \b true if \a range_offset falls in one of this + /// block's ranges, \b false otherwise. + //------------------------------------------------------------------ + bool + Contains (lldb::addr_t range_offset) const; + + //------------------------------------------------------------------ + /// Check if a offset range is in one of the block offset ranges. + /// + /// @param[in] range + /// An offset range into the Function's address range. + /// + /// @return + /// Returns \b true if \a range falls in one of this + /// block's ranges, \b false otherwise. + //------------------------------------------------------------------ + bool + Contains (const Range& range) const; + + //------------------------------------------------------------------ + /// Check if this object contains "block" as a child block at any + /// depth. + /// + /// @param[in] block + /// A potential child block. + /// + /// @return + /// Returns \b true if \a block is a child of this block, \b + /// false otherwise. + //------------------------------------------------------------------ + bool + Contains (const Block *block) const; + + //------------------------------------------------------------------ + /// Dump the block contents. + /// + /// @param[in] s + /// The stream to which to dump the object descripton. + /// + /// @param[in] base_addr + /// The resolved start address of the Function's address + /// range. This should be resolved as the file or load address + /// prior to passing the value into this function for dumping. + /// + /// @param[in] depth + /// Limit the number of levels deep that this function should + /// print as this block can contain child blocks. Specify + /// INT_MAX to dump all child blocks. + /// + /// @param[in] show_context + /// If \b true, variables will dump their context information. + //------------------------------------------------------------------ + void + Dump (Stream *s, lldb::addr_t base_addr, int32_t depth, bool show_context) const; + + //------------------------------------------------------------------ + /// @copydoc SymbolContextScope::DumpSymbolContext(Stream*) + /// + /// @see SymbolContextScope + //------------------------------------------------------------------ + virtual void + DumpSymbolContext(Stream *s); + + void + DumpAddressRanges (Stream *s, + lldb::addr_t base_addr); + + void + GetDescription (Stream *s, + Function *function, + lldb::DescriptionLevel level, + Target *target) const; + + //------------------------------------------------------------------ + /// Get the parent block. + /// + /// @return + /// The parent block pointer, or NULL if this block has no + /// parent. + //------------------------------------------------------------------ + Block * + GetParent () const; + + + //------------------------------------------------------------------ + /// Get the inlined block that contains this block. + /// + /// @return + /// If this block contains inlined function info, it will return + /// this block, else parent blocks will be searched to see if + /// any contain this block. NULL will be returned if this block + /// nor any parent blocks are inlined function blocks. + //------------------------------------------------------------------ + Block * + GetContainingInlinedBlock (); + + //------------------------------------------------------------------ + /// Get the inlined parent block for this block. + /// + /// @return + /// The parent block pointer, or NULL if this block has no + /// parent. + //------------------------------------------------------------------ + Block * + GetInlinedParent (); + + //------------------------------------------------------------------ + /// Get the sibling block for this block. + /// + /// @return + /// The sibling block pointer, or NULL if this block has no + /// sibling. + //------------------------------------------------------------------ + Block * + GetSibling () const; + + //------------------------------------------------------------------ + /// Get the first child block. + /// + /// @return + /// The first child block pointer, or NULL if this block has no + /// children. + //------------------------------------------------------------------ + Block * + GetFirstChild () const + { + if (m_children.empty()) + return NULL; + return m_children.front().get(); + } + + //------------------------------------------------------------------ + /// Get the variable list for this block only. + /// + /// @param[in] can_create + /// If \b true, the variables can be parsed if they already + /// haven't been, else the current state of the block will be + /// returned. + /// + /// @return + /// A variable list shared pointer that contains all variables + /// for this block. + //------------------------------------------------------------------ + lldb::VariableListSP + GetBlockVariableList (bool can_create); + + + //------------------------------------------------------------------ + /// Get the variable list for this block and optionally all child + /// blocks if \a get_child_variables is \b true. + /// + /// @param[in] get_child_variables + /// If \b true, all variables from all child blocks will be + /// added to the variable list. + /// + /// @param[in] can_create + /// If \b true, the variables can be parsed if they already + /// haven't been, else the current state of the block will be + /// returned. Passing \b true for this parameter can be used + /// to see the current state of what has been parsed up to this + /// point. + /// + /// @param[in] add_inline_child_block_variables + /// If this is \b false, no child variables of child blocks + /// that are inlined functions will be gotten. If \b true then + /// all child variables will be added regardless of whether they + /// come from inlined functions or not. + /// + /// @return + /// A variable list shared pointer that contains all variables + /// for this block. + //------------------------------------------------------------------ + uint32_t + AppendBlockVariables (bool can_create, + bool get_child_block_variables, + bool stop_if_child_block_is_inlined_function, + VariableList *variable_list); + + //------------------------------------------------------------------ + /// Appends the variables from this block, and optionally from all + /// parent blocks, to \a variable_list. + /// + /// @param[in] can_create + /// If \b true, the variables can be parsed if they already + /// haven't been, else the current state of the block will be + /// returned. Passing \b true for this parameter can be used + /// to see the current state of what has been parsed up to this + /// point. + /// + /// @param[in] get_parent_variables + /// If \b true, all variables from all parent blocks will be + /// added to the variable list. + /// + /// @param[in] stop_if_block_is_inlined_function + /// If \b true, all variables from all parent blocks will be + /// added to the variable list until there are no parent blocks + /// or the parent block has inlined function info. + /// + /// @param[in/out] variable_list + /// All variables in this block, and optionally all parent + /// blocks will be added to this list. + /// + /// @return + /// The number of variable that were appended to \a + /// variable_list. + //------------------------------------------------------------------ + uint32_t + AppendVariables (bool can_create, + bool get_parent_variables, + bool stop_if_block_is_inlined_function, + VariableList *variable_list); + + //------------------------------------------------------------------ + /// Get const accessor for any inlined function information. + /// + /// @return + /// A comst pointer to any inlined function information, or NULL + /// if this is a regular block. + //------------------------------------------------------------------ + const InlineFunctionInfo* + GetInlinedFunctionInfo () const + { + return m_inlineInfoSP.get(); + } + + clang::DeclContext * + GetClangDeclContext(); + + //------------------------------------------------------------------ + /// Get the memory cost of this object. + /// + /// Returns the cost of this object plus any owned objects from the + /// ranges, variables, and inline function information. + /// + /// @return + /// The number of bytes that this object occupies in memory. + //------------------------------------------------------------------ + size_t + MemorySize() const; + + //------------------------------------------------------------------ + /// Set accessor for any inlined function information. + /// + /// @param[in] name + /// The method name for the inlined function. This value should + /// not be NULL. + /// + /// @param[in] mangled + /// The mangled method name for the inlined function. This can + /// be NULL if there is no mangled name for an inlined function + /// or if the name is the same as \a name. + /// + /// @param[in] decl_ptr + /// A optional pointer to declaration information for the + /// inlined function information. This value can be NULL to + /// indicate that no declaration information is available. + /// + /// @param[in] call_decl_ptr + /// Optional calling location declaration information that + /// describes from where this inlined function was called. + //------------------------------------------------------------------ + void + SetInlinedFunctionInfo (const char *name, + const char *mangled, + const Declaration *decl_ptr, + const Declaration *call_decl_ptr); + + + void + SetParentScope (SymbolContextScope *parent_scope) + { + m_parent_scope = parent_scope; + } + + //------------------------------------------------------------------ + /// Set accessor for the variable list. + /// + /// Called by the SymbolFile plug-ins after they have parsed the + /// variable lists and are ready to hand ownership of the list over + /// to this object. + /// + /// @param[in] variable_list_sp + /// A shared pointer to a VariableList. + //------------------------------------------------------------------ + void + SetVariableList (lldb::VariableListSP& variable_list_sp) + { + m_variable_list_sp = variable_list_sp; + } + + + + bool + BlockInfoHasBeenParsed() const + { + return m_parsed_block_info; + } + + void + SetBlockInfoHasBeenParsed (bool b, bool set_children); + + Block * + FindBlockByID (lldb::user_id_t block_id); + + size_t + GetNumRanges () const + { + return m_ranges.GetSize(); + } + + bool + GetRangeContainingOffset (const lldb::addr_t offset, Range &range); + + bool + GetRangeContainingAddress (const Address& addr, AddressRange &range); + + bool + GetRangeContainingLoadAddress (lldb::addr_t load_addr, Target &target, AddressRange &range); + + uint32_t + GetRangeIndexContainingAddress (const Address& addr); + + //------------------------------------------------------------------ + // Since blocks might have multiple discontiguous addresss ranges, + // we need to be able to get at any of the address ranges in a block. + //------------------------------------------------------------------ + bool + GetRangeAtIndex (uint32_t range_idx, + AddressRange &range); + + bool + GetStartAddress (Address &addr); + + void + SetDidParseVariables (bool b, bool set_children); + +protected: + typedef std::vector collection; + //------------------------------------------------------------------ + // Member variables. + //------------------------------------------------------------------ + SymbolContextScope *m_parent_scope; + collection m_children; + RangeList m_ranges; + lldb::InlineFunctionInfoSP m_inlineInfoSP; ///< Inlined function information. + lldb::VariableListSP m_variable_list_sp; ///< The variable list for all local, static and paramter variables scoped to this block. + bool m_parsed_block_info:1, ///< Set to true if this block and it's children have all been parsed + m_parsed_block_variables:1, + m_parsed_child_blocks:1; + + // A parent of child blocks can be asked to find a sibling block given + // one of its child blocks + Block * + GetSiblingForChild (const Block *child_block) const; + +private: + DISALLOW_COPY_AND_ASSIGN (Block); +}; + + +} // namespace lldb_private + +#endif // liblldb_Block_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Symbol/ClangASTContext.h b/contrib/llvm/tools/lldb/include/lldb/Symbol/ClangASTContext.h new file mode 100644 index 00000000000..75fc07b480e --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Symbol/ClangASTContext.h @@ -0,0 +1,441 @@ +//===-- ClangASTContext.h ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ClangASTContext_h_ +#define liblldb_ClangASTContext_h_ + +// C Includes +#include + +// C++ Includes +#include +#include + +// Other libraries and framework includes +#include "llvm/ADT/OwningPtr.h" +#include "llvm/ADT/SmallVector.h" +#include "clang/AST/TemplateBase.h" + + +// Project includes +#include "lldb/lldb-enumerations.h" +#include "lldb/Core/ClangForward.h" +#include "lldb/Symbol/ClangASTType.h" + +namespace lldb_private { + +class Declaration; + +class ClangASTContext +{ +public: + typedef void (*CompleteTagDeclCallback)(void *baton, clang::TagDecl *); + typedef void (*CompleteObjCInterfaceDeclCallback)(void *baton, clang::ObjCInterfaceDecl *); + + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + ClangASTContext (const char *triple = NULL); + + ~ClangASTContext(); + + clang::ASTContext * + getASTContext(); + + clang::Builtin::Context * + getBuiltinContext(); + + clang::IdentifierTable * + getIdentifierTable(); + + clang::LangOptions * + getLanguageOptions(); + + clang::SelectorTable * + getSelectorTable(); + + clang::FileManager * + getFileManager(); + + clang::SourceManager * + getSourceManager(); + + clang::DiagnosticsEngine * + getDiagnosticsEngine(); + + clang::DiagnosticConsumer * + getDiagnosticConsumer(); + + clang::TargetOptions * + getTargetOptions(); + + clang::TargetInfo * + getTargetInfo(); + + void + Clear(); + + const char * + GetTargetTriple (); + + void + SetTargetTriple (const char *target_triple); + + void + SetArchitecture (const ArchSpec &arch); + + bool + HasExternalSource (); + + void + SetExternalSource (llvm::OwningPtr &ast_source_ap); + + void + RemoveExternalSource (); + + bool + GetCompleteDecl (clang::Decl *decl) + { + return ClangASTContext::GetCompleteDecl(getASTContext(), decl); + } + + static bool + GetCompleteDecl (clang::ASTContext *ast, + clang::Decl *decl); + + void SetMetadataAsUserID (const void *object, + lldb::user_id_t user_id); + + void SetMetadata (const void *object, + ClangASTMetadata &meta_data) + { + SetMetadata(getASTContext(), object, meta_data); + } + + static void + SetMetadata (clang::ASTContext *ast, + const void *object, + ClangASTMetadata &meta_data); + + ClangASTMetadata * + GetMetadata (const void *object) + { + return GetMetadata(getASTContext(), object); + } + + static ClangASTMetadata * + GetMetadata (clang::ASTContext *ast, + const void *object); + + //------------------------------------------------------------------ + // Basic Types + //------------------------------------------------------------------ + ClangASTType + GetBuiltinTypeForEncodingAndBitSize (lldb::Encoding encoding, + uint32_t bit_size); + + static ClangASTType + GetBuiltinTypeForEncodingAndBitSize (clang::ASTContext *ast, + lldb::Encoding encoding, + uint32_t bit_size); + + ClangASTType + GetBasicType (lldb::BasicType type); + + static ClangASTType + GetBasicType (clang::ASTContext *ast, lldb::BasicType type); + + static ClangASTType + GetBasicType (clang::ASTContext *ast, const ConstString &name); + + static lldb::BasicType + GetBasicTypeEnumeration (const ConstString &name); + + ClangASTType + GetBuiltinTypeForDWARFEncodingAndBitSize ( + const char *type_name, + uint32_t dw_ate, + uint32_t bit_size); + + ClangASTType + GetCStringType(bool is_const); + + static ClangASTType + GetUnknownAnyType(clang::ASTContext *ast); + + ClangASTType + GetUnknownAnyType() + { + return ClangASTContext::GetUnknownAnyType(getASTContext()); + } + + uint32_t + GetPointerByteSize (); + + static clang::DeclContext * + GetTranslationUnitDecl (clang::ASTContext *ast); + + clang::DeclContext * + GetTranslationUnitDecl () + { + return GetTranslationUnitDecl (getASTContext()); + } + + static bool + GetClassMethodInfoForDeclContext (clang::DeclContext *decl_ctx, + lldb::LanguageType &language, + bool &is_instance_method, + ConstString &language_object_name); + + static ClangASTType + CopyType(clang::ASTContext *dest_context, + ClangASTType source_type); + + static clang::Decl * + CopyDecl (clang::ASTContext *dest_context, + clang::ASTContext *source_context, + clang::Decl *source_decl); + + static bool + AreTypesSame(ClangASTType type1, + ClangASTType type2, + bool ignore_qualifiers = false); + + ClangASTType + GetTypeForDecl (clang::TagDecl *decl); + + ClangASTType + GetTypeForDecl (clang::ObjCInterfaceDecl *objc_decl); + + //------------------------------------------------------------------ + // Structure, Unions, Classes + //------------------------------------------------------------------ + + static clang::AccessSpecifier + ConvertAccessTypeToAccessSpecifier (lldb::AccessType access); + + static clang::AccessSpecifier + UnifyAccessSpecifiers (clang::AccessSpecifier lhs, clang::AccessSpecifier rhs); + + static uint32_t + GetNumBaseClasses (const clang::CXXRecordDecl *cxx_record_decl, + bool omit_empty_base_classes); + + static uint32_t + GetIndexForRecordBase (const clang::RecordDecl *record_decl, + const clang::CXXBaseSpecifier *base_spec, + bool omit_empty_base_classes); + + ClangASTType + CreateRecordType (clang::DeclContext *decl_ctx, + lldb::AccessType access_type, + const char *name, + int kind, + lldb::LanguageType language, + ClangASTMetadata *metadata = NULL); + + class TemplateParameterInfos + { + public: + bool + IsValid() const + { + if (args.empty()) + return false; + return args.size() == names.size(); + } + + size_t + GetSize () const + { + if (IsValid()) + return args.size(); + return 0; + } + + llvm::SmallVector names; + llvm::SmallVector args; + }; + + clang::FunctionTemplateDecl * + CreateFunctionTemplateDecl (clang::DeclContext *decl_ctx, + clang::FunctionDecl *func_decl, + const char *name, + const TemplateParameterInfos &infos); + + void + CreateFunctionTemplateSpecializationInfo (clang::FunctionDecl *func_decl, + clang::FunctionTemplateDecl *Template, + const TemplateParameterInfos &infos); + + clang::ClassTemplateDecl * + CreateClassTemplateDecl (clang::DeclContext *decl_ctx, + lldb::AccessType access_type, + const char *class_name, + int kind, + const TemplateParameterInfos &infos); + + clang::ClassTemplateSpecializationDecl * + CreateClassTemplateSpecializationDecl (clang::DeclContext *decl_ctx, + clang::ClassTemplateDecl *class_template_decl, + int kind, + const TemplateParameterInfos &infos); + + ClangASTType + CreateClassTemplateSpecializationType (clang::ClassTemplateSpecializationDecl *class_template_specialization_decl); + + static clang::DeclContext * + GetAsDeclContext (clang::CXXMethodDecl *cxx_method_decl); + + static clang::DeclContext * + GetAsDeclContext (clang::ObjCMethodDecl *objc_method_decl); + + + static bool + CheckOverloadedOperatorKindParameterCount (uint32_t op_kind, + uint32_t num_params); + + bool + FieldIsBitfield (clang::FieldDecl* field, + uint32_t& bitfield_bit_size); + + static bool + FieldIsBitfield (clang::ASTContext *ast, + clang::FieldDecl* field, + uint32_t& bitfield_bit_size); + + static bool + RecordHasFields (const clang::RecordDecl *record_decl); + + + ClangASTType + CreateObjCClass (const char *name, + clang::DeclContext *decl_ctx, + bool isForwardDecl, + bool isInternal, + ClangASTMetadata *metadata = NULL); + + // Returns a mask containing bits from the ClangASTContext::eTypeXXX enumerations + + + //------------------------------------------------------------------ + // Namespace Declarations + //------------------------------------------------------------------ + + clang::NamespaceDecl * + GetUniqueNamespaceDeclaration (const char *name, + clang::DeclContext *decl_ctx); + + //------------------------------------------------------------------ + // Function Types + //------------------------------------------------------------------ + + clang::FunctionDecl * + CreateFunctionDeclaration (clang::DeclContext *decl_ctx, + const char *name, + const ClangASTType &function_Type, + int storage, + bool is_inline); + + static ClangASTType + CreateFunctionType (clang::ASTContext *ast, + const ClangASTType &result_type, + const ClangASTType *args, + unsigned num_args, + bool is_variadic, + unsigned type_quals); + + ClangASTType + CreateFunctionType (const ClangASTType &result_type, + const ClangASTType *args, + unsigned num_args, + bool is_variadic, + unsigned type_quals) + { + return ClangASTContext::CreateFunctionType(getASTContext(), + result_type, + args, + num_args, + is_variadic, + type_quals); + } + + clang::ParmVarDecl * + CreateParameterDeclaration (const char *name, + const ClangASTType ¶m_type, + int storage); + + void + SetFunctionParameters (clang::FunctionDecl *function_decl, + clang::ParmVarDecl **params, + unsigned num_params); + + //------------------------------------------------------------------ + // Array Types + //------------------------------------------------------------------ + + ClangASTType + CreateArrayType (const ClangASTType &element_type, + size_t element_count, + bool is_vector); + + //------------------------------------------------------------------ + // Enumeration Types + //------------------------------------------------------------------ + ClangASTType + CreateEnumerationType (const char *name, + clang::DeclContext *decl_ctx, + const Declaration &decl, + const ClangASTType &integer_qual_type); + + //------------------------------------------------------------------ + // Floating point functions + //------------------------------------------------------------------ + + ClangASTType + GetFloatTypeFromBitSize (size_t bit_size) + { + return GetFloatTypeFromBitSize (getASTContext(), bit_size); + } + + static ClangASTType + GetFloatTypeFromBitSize (clang::ASTContext *ast, + size_t bit_size); +protected: + //------------------------------------------------------------------ + // Classes that inherit from ClangASTContext can see and modify these + //------------------------------------------------------------------ + std::string m_target_triple; + std::unique_ptr m_ast_ap; + std::unique_ptr m_language_options_ap; + std::unique_ptr m_file_manager_ap; + std::unique_ptr m_file_system_options_ap; + std::unique_ptr m_source_manager_ap; + std::unique_ptr m_diagnostics_engine_ap; + std::unique_ptr m_diagnostic_consumer_ap; + llvm::IntrusiveRefCntPtr m_target_options_rp; + std::unique_ptr m_target_info_ap; + std::unique_ptr m_identifier_table_ap; + std::unique_ptr m_selector_table_ap; + std::unique_ptr m_builtins_ap; + CompleteTagDeclCallback m_callback_tag_decl; + CompleteObjCInterfaceDeclCallback m_callback_objc_decl; + void * m_callback_baton; + uint32_t m_pointer_byte_size; +private: + //------------------------------------------------------------------ + // For ClangASTContext only + //------------------------------------------------------------------ + ClangASTContext(const ClangASTContext&); + const ClangASTContext& operator=(const ClangASTContext&); +}; + +} // namespace lldb_private + +#endif // liblldb_ClangASTContext_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Symbol/ClangASTImporter.h b/contrib/llvm/tools/lldb/include/lldb/Symbol/ClangASTImporter.h new file mode 100644 index 00000000000..10df7da893a --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Symbol/ClangASTImporter.h @@ -0,0 +1,371 @@ +//===-- ClangASTImporter.h --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ClangASTImporter_h_ +#define liblldb_ClangASTImporter_h_ + +#include +#include + +#include "lldb/lldb-types.h" +#include "clang/AST/ASTImporter.h" +#include "clang/Basic/FileManager.h" +#include "clang/Basic/FileSystemOptions.h" +#include "lldb/Symbol/ClangNamespaceDecl.h" + +namespace lldb_private { + +class ClangASTMetrics +{ +public: + static void DumpCounters (Log *log); + static void ClearLocalCounters () + { + local_counters = { 0, 0, 0, 0, 0, 0 }; + } + + static void RegisterVisibleQuery () + { + ++global_counters.m_visible_query_count; + ++local_counters.m_visible_query_count; + } + + static void RegisterLexicalQuery () + { + ++global_counters.m_lexical_query_count; + ++local_counters.m_lexical_query_count; + } + + static void RegisterLLDBImport () + { + ++global_counters.m_lldb_import_count; + ++local_counters.m_lldb_import_count; + } + + static void RegisterClangImport () + { + ++global_counters.m_clang_import_count; + ++local_counters.m_clang_import_count; + } + + static void RegisterDeclCompletion () + { + ++global_counters.m_decls_completed_count; + ++local_counters.m_decls_completed_count; + } + + static void RegisterRecordLayout () + { + ++global_counters.m_record_layout_count; + ++local_counters.m_record_layout_count; + } + +private: + struct Counters + { + uint64_t m_visible_query_count; + uint64_t m_lexical_query_count; + uint64_t m_lldb_import_count; + uint64_t m_clang_import_count; + uint64_t m_decls_completed_count; + uint64_t m_record_layout_count; + }; + + static Counters global_counters; + static Counters local_counters; + + static void DumpCounters (Log *log, Counters &counters); +}; + +class ClangASTImporter +{ +public: + ClangASTImporter () : + m_file_manager(clang::FileSystemOptions()) + { + } + + clang::QualType + CopyType (clang::ASTContext *dst_ctx, + clang::ASTContext *src_ctx, + clang::QualType type); + + lldb::clang_type_t + CopyType (clang::ASTContext *dst_ctx, + clang::ASTContext *src_ctx, + lldb::clang_type_t type); + + clang::Decl * + CopyDecl (clang::ASTContext *dst_ctx, + clang::ASTContext *src_ctx, + clang::Decl *decl); + + lldb::clang_type_t + DeportType (clang::ASTContext *dst_ctx, + clang::ASTContext *src_ctx, + lldb::clang_type_t type); + + clang::Decl * + DeportDecl (clang::ASTContext *dst_ctx, + clang::ASTContext *src_ctx, + clang::Decl *decl); + + void + CompleteDecl (clang::Decl *decl); + + bool + CompleteTagDecl (clang::TagDecl *decl); + + bool + CompleteTagDeclWithOrigin (clang::TagDecl *decl, clang::TagDecl *origin); + + bool + CompleteObjCInterfaceDecl (clang::ObjCInterfaceDecl *interface_decl); + + bool + RequireCompleteType (clang::QualType type); + + bool + ResolveDeclOrigin (const clang::Decl *decl, clang::Decl **original_decl, clang::ASTContext **original_ctx) + { + DeclOrigin origin = GetDeclOrigin(decl); + + if (original_decl) + *original_decl = origin.decl; + + if (original_ctx) + *original_ctx = origin.ctx; + + return origin.Valid(); + } + + void + SetDeclOrigin (const clang::Decl *decl, clang::Decl *original_decl); + + ClangASTMetadata * + GetDeclMetadata (const clang::Decl *decl); + + // + // Namespace maps + // + + typedef std::vector < std::pair > NamespaceMap; + typedef std::shared_ptr NamespaceMapSP; + + void RegisterNamespaceMap (const clang::NamespaceDecl *decl, + NamespaceMapSP &namespace_map); + + NamespaceMapSP GetNamespaceMap (const clang::NamespaceDecl *decl); + + void BuildNamespaceMap (const clang::NamespaceDecl *decl); + + // + // Comleters for maps + // + + class MapCompleter + { + public: + virtual ~MapCompleter (); + + virtual void CompleteNamespaceMap (NamespaceMapSP &namespace_map, + const ConstString &name, + NamespaceMapSP &parent_map) const = 0; + }; + + void InstallMapCompleter (clang::ASTContext *dst_ctx, MapCompleter &completer) + { + ASTContextMetadataSP context_md; + ContextMetadataMap::iterator context_md_iter = m_metadata_map.find(dst_ctx); + + if (context_md_iter == m_metadata_map.end()) + { + context_md = ASTContextMetadataSP(new ASTContextMetadata(dst_ctx)); + m_metadata_map[dst_ctx] = context_md; + } + else + { + context_md = context_md_iter->second; + } + + context_md->m_map_completer = &completer; + } + + void ForgetDestination (clang::ASTContext *dst_ctx); + void ForgetSource (clang::ASTContext *dst_ctx, clang::ASTContext *src_ctx); +private: + struct DeclOrigin + { + DeclOrigin () : + ctx(NULL), + decl(NULL) + { + } + + DeclOrigin (clang::ASTContext *_ctx, + clang::Decl *_decl) : + ctx(_ctx), + decl(_decl) + { + } + + DeclOrigin (const DeclOrigin &rhs) + { + ctx = rhs.ctx; + decl = rhs.decl; + } + + void operator= (const DeclOrigin &rhs) + { + ctx = rhs.ctx; + decl = rhs.decl; + } + + bool + Valid () + { + return (ctx != NULL || decl != NULL); + } + + clang::ASTContext *ctx; + clang::Decl *decl; + }; + + typedef std::map OriginMap; + + class Minion : public clang::ASTImporter + { + public: + Minion (ClangASTImporter &master, + clang::ASTContext *target_ctx, + clang::ASTContext *source_ctx) : + clang::ASTImporter(*target_ctx, + master.m_file_manager, + *source_ctx, + master.m_file_manager, + true /*minimal*/), + m_decls_to_deport(NULL), + m_decls_already_deported(NULL), + m_master(master), + m_source_ctx(source_ctx) + { + } + + // A call to "InitDeportWorkQueues" puts the minion into deport mode. + // In deport mode, every copied Decl that could require completion is + // recorded and placed into the decls_to_deport set. + // + // A call to "ExecuteDeportWorkQueues" completes all the Decls that + // are in decls_to_deport, adding any Decls it sees along the way that + // it hasn't already deported. It proceeds until decls_to_deport is + // empty. + // + // These calls must be paired. Leaving a minion in deport mode or + // trying to start deport minion with a new pair of queues will result + // in an assertion failure. + + void InitDeportWorkQueues (std::set *decls_to_deport, + std::set *decls_already_deported); + void ExecuteDeportWorkQueues (); + + void ImportDefinitionTo (clang::Decl *to, clang::Decl *from); + + clang::Decl *Imported (clang::Decl *from, clang::Decl *to); + + std::set *m_decls_to_deport; + std::set *m_decls_already_deported; + ClangASTImporter &m_master; + clang::ASTContext *m_source_ctx; + }; + + typedef std::shared_ptr MinionSP; + typedef std::map MinionMap; + typedef std::map NamespaceMetaMap; + + struct ASTContextMetadata + { + ASTContextMetadata(clang::ASTContext *dst_ctx) : + m_dst_ctx (dst_ctx), + m_minions (), + m_origins (), + m_namespace_maps (), + m_map_completer (NULL) + { + } + + clang::ASTContext *m_dst_ctx; + MinionMap m_minions; + OriginMap m_origins; + + NamespaceMetaMap m_namespace_maps; + MapCompleter *m_map_completer; + }; + + typedef std::shared_ptr ASTContextMetadataSP; + typedef std::map ContextMetadataMap; + + ContextMetadataMap m_metadata_map; + + ASTContextMetadataSP + GetContextMetadata (clang::ASTContext *dst_ctx) + { + ContextMetadataMap::iterator context_md_iter = m_metadata_map.find(dst_ctx); + + if (context_md_iter == m_metadata_map.end()) + { + ASTContextMetadataSP context_md = ASTContextMetadataSP(new ASTContextMetadata(dst_ctx)); + m_metadata_map[dst_ctx] = context_md; + return context_md; + } + else + { + return context_md_iter->second; + } + } + + ASTContextMetadataSP + MaybeGetContextMetadata (clang::ASTContext *dst_ctx) + { + ContextMetadataMap::iterator context_md_iter = m_metadata_map.find(dst_ctx); + + if (context_md_iter != m_metadata_map.end()) + return context_md_iter->second; + else + return ASTContextMetadataSP(); + } + + MinionSP + GetMinion (clang::ASTContext *dst_ctx, clang::ASTContext *src_ctx) + { + ASTContextMetadataSP context_md = GetContextMetadata(dst_ctx); + + MinionMap &minions = context_md->m_minions; + MinionMap::iterator minion_iter = minions.find(src_ctx); + + if (minion_iter == minions.end()) + { + MinionSP minion = MinionSP(new Minion(*this, dst_ctx, src_ctx)); + minions[src_ctx] = minion; + return minion; + } + else + { + return minion_iter->second; + } + } + + DeclOrigin + GetDeclOrigin (const clang::Decl *decl); + + clang::FileManager m_file_manager; +}; + +} + +#endif diff --git a/contrib/llvm/tools/lldb/include/lldb/Symbol/ClangASTType.h b/contrib/llvm/tools/lldb/include/lldb/Symbol/ClangASTType.h new file mode 100644 index 00000000000..d9e754e8ceb --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Symbol/ClangASTType.h @@ -0,0 +1,679 @@ +//===-- ClangASTType.h ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ClangASTType_h_ +#define liblldb_ClangASTType_h_ + +#include +#include "lldb/lldb-private.h" +#include "lldb/Core/ClangForward.h" +#include "clang/AST/Type.h" + +namespace lldb_private { + +//---------------------------------------------------------------------- +// A class that can carry around a clang ASTContext and a opaque clang +// QualType. A clang::QualType can be easily reconstructed from an +// opaque clang type and often the ASTContext is needed when doing +// various type related tasks, so this class allows both items to travel +// in a single very lightweight class that can be used. There are many +// static equivalents of the member functions that allow the ASTContext +// and the opaque clang QualType to be specified for ease of use and +// to avoid code duplication. +//---------------------------------------------------------------------- +class ClangASTType +{ +public: + enum { + eTypeHasChildren = (1u << 0), + eTypeHasValue = (1u << 1), + eTypeIsArray = (1u << 2), + eTypeIsBlock = (1u << 3), + eTypeIsBuiltIn = (1u << 4), + eTypeIsClass = (1u << 5), + eTypeIsCPlusPlus = (1u << 6), + eTypeIsEnumeration = (1u << 7), + eTypeIsFuncPrototype = (1u << 8), + eTypeIsMember = (1u << 9), + eTypeIsObjC = (1u << 10), + eTypeIsPointer = (1u << 11), + eTypeIsReference = (1u << 12), + eTypeIsStructUnion = (1u << 13), + eTypeIsTemplate = (1u << 14), + eTypeIsTypedef = (1u << 15), + eTypeIsVector = (1u << 16), + eTypeIsScalar = (1u << 17), + eTypeIsInteger = (1u << 18), + eTypeIsFloat = (1u << 19), + eTypeIsComplex = (1u << 20), + eTypeIsSigned = (1u << 21) + }; + + + //---------------------------------------------------------------------- + // Constructors and Destructors + //---------------------------------------------------------------------- + ClangASTType (clang::ASTContext *ast_context, lldb::clang_type_t type) : + m_type (type), + m_ast (ast_context) + { + } + + ClangASTType (clang::ASTContext *ast_context, clang::QualType qual_type); + + ClangASTType (const ClangASTType &rhs) : + m_type (rhs.m_type), + m_ast (rhs.m_ast) + { + } + + ClangASTType () : + m_type (0), + m_ast (0) + { + } + + ~ClangASTType(); + + //---------------------------------------------------------------------- + // Operators + //---------------------------------------------------------------------- + + const ClangASTType & + operator= (const ClangASTType &rhs) + { + m_type = rhs.m_type; + m_ast = rhs.m_ast; + return *this; + } + + + //---------------------------------------------------------------------- + // Tests + //---------------------------------------------------------------------- + + operator bool () const + { + return m_type != NULL && m_ast != NULL; + } + + bool + operator < (const ClangASTType &rhs) const + { + if (m_ast == rhs.m_ast) + return m_type < rhs.m_type; + return m_ast < rhs.m_ast; + } + + bool + IsValid () const + { + return m_type != NULL && m_ast != NULL; + } + + bool + IsArrayType (ClangASTType *element_type, + uint64_t *size, + bool *is_incomplete) const; + + bool + IsArrayOfScalarType () const; + + bool + IsAggregateType () const; + + bool + IsBeingDefined () const; + + bool + IsCharType () const; + + bool + IsCompleteType () const; + + bool + IsConst() const; + + bool + IsCStringType (uint32_t &length) const; + + bool + IsCXXClassType () const; + + bool + IsDefined() const; + + bool + IsFloatingPointType (uint32_t &count, bool &is_complex) const; + + bool + IsFunctionType (bool *is_variadic_ptr = NULL) const; + + bool + IsVariadicFunctionType () const; + + bool + IsFunctionPointerType () const; + + bool + IsIntegerType (bool &is_signed) const; + + bool + IsObjCClassType () const; + + bool + IsObjCClassTypeAndHasIVars (bool check_superclass) const; + + bool + IsObjCObjectOrInterfaceType () const; + + bool + IsObjCObjectPointerType (ClangASTType *target_type = NULL); + + bool + IsPolymorphicClass () const; + + bool + IsPossibleCPlusPlusDynamicType (ClangASTType *target_type = NULL) const + { + return IsPossibleDynamicType (target_type, true, false); + } + + bool + IsPossibleDynamicType (ClangASTType *target_type, // Can pass NULL + bool check_cplusplus, + bool check_objc) const; + + + bool + IsPointerToScalarType () const; + + bool + IsPointerType (ClangASTType *pointee_type = NULL) const; + + bool + IsPointerOrReferenceType (ClangASTType *pointee_type = NULL) const; + + bool + IsReferenceType (ClangASTType *pointee_type = NULL) const; + + bool + IsScalarType () const; + + bool + IsTypedefType () const; + + bool + IsVoidType () const; + + bool + GetCXXClassName (std::string &class_name) const; + + bool + GetObjCClassName (std::string &class_name); + + + //---------------------------------------------------------------------- + // Type Completion + //---------------------------------------------------------------------- + + bool + GetCompleteType () const; + + //---------------------------------------------------------------------- + // AST related queries + //---------------------------------------------------------------------- + + size_t + GetPointerByteSize () const; + + //---------------------------------------------------------------------- + // Accessors + //---------------------------------------------------------------------- + + clang::ASTContext * + GetASTContext() const + { + return m_ast; + } + + ConstString + GetConstQualifiedTypeName () const; + + ConstString + GetConstTypeName () const; + + std::string + GetTypeName () const; + + uint32_t + GetTypeInfo (ClangASTType *pointee_or_element_clang_type = NULL) const; + + lldb::LanguageType + GetMinimumLanguage (); + + lldb::clang_type_t + GetOpaqueQualType() const + { + return m_type; + } + + lldb::TypeClass + GetTypeClass () const; + + void + SetClangType (clang::ASTContext *ast, lldb::clang_type_t type) + { + m_ast = ast; + m_type = type; + } + + void + SetClangType (clang::ASTContext *ast, clang::QualType qual_type); + + unsigned + GetTypeQualifiers() const; + + //---------------------------------------------------------------------- + // Creating related types + //---------------------------------------------------------------------- + + ClangASTType + AddConstModifier () const; + + ClangASTType + AddRestrictModifier () const; + + ClangASTType + AddVolatileModifier () const; + + // Using the current type, create a new typedef to that type using "typedef_name" + // as the name and "decl_ctx" as the decl context. + ClangASTType + CreateTypedefType (const char *typedef_name, + clang::DeclContext *decl_ctx) const; + + ClangASTType + GetArrayElementType (uint64_t& stride) const; + + ClangASTType + GetCanonicalType () const; + + ClangASTType + GetFullyUnqualifiedType () const; + + // Returns -1 if this isn't a function of if the fucntion doesn't have a prototype + // Returns a value >= 0 if there is a prototype. + int + GetFunctionArgumentCount () const; + + ClangASTType + GetFunctionArgumentTypeAtIndex (size_t idx); + + ClangASTType + GetFunctionReturnType () const; + + ClangASTType + GetLValueReferenceType () const; + + ClangASTType + GetNonReferenceType () const; + + ClangASTType + GetPointeeType () const; + + ClangASTType + GetPointerType () const; + + ClangASTType + GetRValueReferenceType () const; + + // If the current object represents a typedef type, get the underlying type + ClangASTType + GetTypedefedType () const; + + ClangASTType + RemoveFastQualifiers () const; + + //---------------------------------------------------------------------- + // Create related types using the current type's AST + //---------------------------------------------------------------------- + ClangASTType + GetBasicTypeFromAST (lldb::BasicType basic_type) const; + + //---------------------------------------------------------------------- + // Exploring the type + //---------------------------------------------------------------------- + + uint64_t + GetByteSize () const; + + uint64_t + GetBitSize () const; + + lldb::Encoding + GetEncoding (uint64_t &count) const; + + lldb::Format + GetFormat () const; + + size_t + GetTypeBitAlign () const; + + uint32_t + GetNumChildren (bool omit_empty_base_classes) const; + + lldb::BasicType + GetBasicTypeEnumeration () const; + + static lldb::BasicType + GetBasicTypeEnumeration (const ConstString &name); + + uint32_t + GetNumDirectBaseClasses () const; + + uint32_t + GetNumVirtualBaseClasses () const; + + uint32_t + GetNumFields () const; + + ClangASTType + GetDirectBaseClassAtIndex (size_t idx, + uint32_t *bit_offset_ptr) const; + + ClangASTType + GetVirtualBaseClassAtIndex (size_t idx, + uint32_t *bit_offset_ptr) const; + + ClangASTType + GetFieldAtIndex (size_t idx, + std::string& name, + uint64_t *bit_offset_ptr, + uint32_t *bitfield_bit_size_ptr, + bool *is_bitfield_ptr) const; + + uint32_t + GetIndexOfFieldWithName (const char* name, + ClangASTType* field_clang_type = NULL, + uint64_t *bit_offset_ptr = NULL, + uint32_t *bitfield_bit_size_ptr = NULL, + bool *is_bitfield_ptr = NULL) const; + + uint32_t + GetNumPointeeChildren () const; + + ClangASTType + GetChildClangTypeAtIndex (ExecutionContext *exe_ctx, + const char *parent_name, + size_t idx, + bool transparent_pointers, + bool omit_empty_base_classes, + bool ignore_array_bounds, + std::string& child_name, + uint32_t &child_byte_size, + int32_t &child_byte_offset, + uint32_t &child_bitfield_bit_size, + uint32_t &child_bitfield_bit_offset, + bool &child_is_base_class, + bool &child_is_deref_of_parent) const; + + // Lookup a child given a name. This function will match base class names + // and member member names in "clang_type" only, not descendants. + uint32_t + GetIndexOfChildWithName (const char *name, + bool omit_empty_base_classes) const; + + // Lookup a child member given a name. This function will match member names + // only and will descend into "clang_type" children in search for the first + // member in this class, or any base class that matches "name". + // TODO: Return all matches for a given name by returning a vector> + // so we catch all names that match a given child name, not just the first. + size_t + GetIndexOfChildMemberWithName (const char *name, + bool omit_empty_base_classes, + std::vector& child_indexes) const; + + size_t + GetNumTemplateArguments () const; + + ClangASTType + GetTemplateArgument (size_t idx, + lldb::TemplateArgumentKind &kind) const; + + + //---------------------------------------------------------------------- + // Modifying RecordType + //---------------------------------------------------------------------- + clang::FieldDecl * + AddFieldToRecordType (const char *name, + const ClangASTType &field_type, + lldb::AccessType access, + uint32_t bitfield_bit_size); + + void + BuildIndirectFields (); + + clang::VarDecl * + AddVariableToRecordType (const char *name, + const ClangASTType &var_type, + lldb::AccessType access); + + clang::CXXMethodDecl * + AddMethodToCXXRecordType (const char *name, + const ClangASTType &method_type, + lldb::AccessType access, + bool is_virtual, + bool is_static, + bool is_inline, + bool is_explicit, + bool is_attr_used, + bool is_artificial); + + // C++ Base Classes + clang::CXXBaseSpecifier * + CreateBaseClassSpecifier (lldb::AccessType access, + bool is_virtual, + bool base_of_class); + + static void + DeleteBaseClassSpecifiers (clang::CXXBaseSpecifier **base_classes, + unsigned num_base_classes); + + bool + SetBaseClassesForClassType (clang::CXXBaseSpecifier const * const *base_classes, + unsigned num_base_classes); + + + bool + SetObjCSuperClass (const ClangASTType &superclass_clang_type); + + bool + AddObjCClassProperty (const char *property_name, + const ClangASTType &property_clang_type, + clang::ObjCIvarDecl *ivar_decl, + const char *property_setter_name, + const char *property_getter_name, + uint32_t property_attributes, + ClangASTMetadata *metadata); + + clang::ObjCMethodDecl * + AddMethodToObjCObjectType (const char *name, // the full symbol name as seen in the symbol table ("-[NString stringWithCString:]") + const ClangASTType &method_clang_type, + lldb::AccessType access, + bool is_artificial); + + clang::DeclContext * + GetDeclContextForType () const; + + + bool + SetDefaultAccessForRecordFields (int default_accessibility, + int *assigned_accessibilities, + size_t num_assigned_accessibilities); + + bool + SetHasExternalStorage (bool has_extern); + + + //------------------------------------------------------------------ + // clang::TagType + //------------------------------------------------------------------ + + bool + SetTagTypeKind (int kind) const; + + //------------------------------------------------------------------ + // Tag Declarations + //------------------------------------------------------------------ + bool + StartTagDeclarationDefinition (); + + bool + CompleteTagDeclarationDefinition (); + + //---------------------------------------------------------------------- + // Modifying Enumeration types + //---------------------------------------------------------------------- + bool + AddEnumerationValueToEnumerationType (const ClangASTType &enumerator_qual_type, + const Declaration &decl, + const char *name, + int64_t enum_value, + uint32_t enum_value_bit_size); + + + + ClangASTType + GetEnumerationIntegerType () const; + + + //------------------------------------------------------------------ + // Pointers & References + //------------------------------------------------------------------ + + // Call this function using the class type when you want to make a + // member pointer type to pointee_type. + ClangASTType + CreateMemberPointerType (const ClangASTType &pointee_type) const; + + + // Converts "s" to a floating point value and place resulting floating + // point bytes in the "dst" buffer. + size_t + ConvertStringToFloatValue (const char *s, + uint8_t *dst, + size_t dst_size) const; + //---------------------------------------------------------------------- + // Dumping types + //---------------------------------------------------------------------- + void + DumpValue (ExecutionContext *exe_ctx, + Stream *s, + lldb::Format format, + const DataExtractor &data, + lldb::offset_t data_offset, + size_t data_byte_size, + uint32_t bitfield_bit_size, + uint32_t bitfield_bit_offset, + bool show_types, + bool show_summary, + bool verbose, + uint32_t depth); + + bool + DumpTypeValue (Stream *s, + lldb::Format format, + const DataExtractor &data, + lldb::offset_t data_offset, + size_t data_byte_size, + uint32_t bitfield_bit_size, + uint32_t bitfield_bit_offset, + ExecutionContextScope *exe_scope); + + void + DumpSummary (ExecutionContext *exe_ctx, + Stream *s, + const DataExtractor &data, + lldb::offset_t data_offset, + size_t data_byte_size); + + void + DumpTypeDescription () const; // Dump to stdout + + void + DumpTypeDescription (Stream *s) const; + + bool + GetValueAsScalar (const DataExtractor &data, + lldb::offset_t data_offset, + size_t data_byte_size, + Scalar &value) const; + + bool + SetValueFromScalar (const Scalar &value, + Stream &strm); + + bool + ReadFromMemory (ExecutionContext *exe_ctx, + lldb::addr_t addr, + AddressType address_type, + DataExtractor &data); + + bool + WriteToMemory (ExecutionContext *exe_ctx, + lldb::addr_t addr, + AddressType address_type, + StreamString &new_value); + + + clang::RecordDecl * + GetAsRecordDecl () const; + + clang::CXXRecordDecl * + GetAsCXXRecordDecl () const; + + clang::ObjCInterfaceDecl * + GetAsObjCInterfaceDecl () const; + + void + Clear() + { + m_type = NULL; + m_ast = NULL; + } + + clang::QualType + GetQualType () const + { + if (m_type) + return clang::QualType::getFromOpaquePtr(m_type); + return clang::QualType(); + } + clang::QualType + GetCanonicalQualType () const + { + if (m_type) + return clang::QualType::getFromOpaquePtr(m_type).getCanonicalType(); + return clang::QualType(); + } + +private: + lldb::clang_type_t m_type; + clang::ASTContext *m_ast; + +}; + +bool operator == (const ClangASTType &lhs, const ClangASTType &rhs); +bool operator != (const ClangASTType &lhs, const ClangASTType &rhs); + + +} // namespace lldb_private + +#endif // #ifndef liblldb_ClangASTType_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Symbol/ClangExternalASTSourceCallbacks.h b/contrib/llvm/tools/lldb/include/lldb/Symbol/ClangExternalASTSourceCallbacks.h new file mode 100644 index 00000000000..0c8121135ef --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Symbol/ClangExternalASTSourceCallbacks.h @@ -0,0 +1,171 @@ +//===-- ClangExternalASTSourceCallbacks.h -----------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ClangExternalASTSourceCallbacks_h_ +#define liblldb_ClangExternalASTSourceCallbacks_h_ + +// C Includes +// C++ Includes +#include +#include +#include + +// Other libraries and framework includes +#include "clang/AST/CharUnits.h" + +// Project includes +#include "lldb/lldb-enumerations.h" +#include "lldb/Core/ClangForward.h" +#include "lldb/Symbol/ClangASTType.h" +#include "lldb/Symbol/ClangExternalASTSourceCommon.h" + +namespace lldb_private { + +class ClangExternalASTSourceCallbacks : public ClangExternalASTSourceCommon +{ +public: + + typedef void (*CompleteTagDeclCallback)(void *baton, clang::TagDecl *); + typedef void (*CompleteObjCInterfaceDeclCallback)(void *baton, clang::ObjCInterfaceDecl *); + typedef void (*FindExternalVisibleDeclsByNameCallback)(void *baton, const clang::DeclContext *DC, clang::DeclarationName Name, llvm::SmallVectorImpl *results); + typedef bool (*LayoutRecordTypeCallback)(void *baton, + const clang::RecordDecl *Record, + uint64_t &Size, + uint64_t &Alignment, + llvm::DenseMap &FieldOffsets, + llvm::DenseMap &BaseOffsets, + llvm::DenseMap &VirtualBaseOffsets); + + ClangExternalASTSourceCallbacks (CompleteTagDeclCallback tag_decl_callback, + CompleteObjCInterfaceDeclCallback objc_decl_callback, + FindExternalVisibleDeclsByNameCallback find_by_name_callback, + LayoutRecordTypeCallback layout_record_type_callback, + void *callback_baton) : + m_callback_tag_decl (tag_decl_callback), + m_callback_objc_decl (objc_decl_callback), + m_callback_find_by_name (find_by_name_callback), + m_callback_layout_record_type (layout_record_type_callback), + m_callback_baton (callback_baton) + { + } + + //------------------------------------------------------------------ + // clang::ExternalASTSource + //------------------------------------------------------------------ + + virtual clang::Decl * + GetExternalDecl (uint32_t ID) + { + // This method only needs to be implemented if the AST source ever + // passes back decl sets as VisibleDeclaration objects. + return 0; + } + + virtual clang::Stmt * + GetExternalDeclStmt (uint64_t Offset) + { + // This operation is meant to be used via a LazyOffsetPtr. It only + // needs to be implemented if the AST source uses methods like + // FunctionDecl::setLazyBody when building decls. + return 0; + } + + virtual clang::Selector + GetExternalSelector (uint32_t ID) + { + // This operation only needs to be implemented if the AST source + // returns non-zero for GetNumKnownSelectors(). + return clang::Selector(); + } + + virtual uint32_t + GetNumExternalSelectors() + { + return 0; + } + + virtual clang::CXXBaseSpecifier * + GetExternalCXXBaseSpecifiers(uint64_t Offset) + { + return NULL; + } + + virtual void + MaterializeVisibleDecls (const clang::DeclContext *decl_ctx) + { + return; + } + + virtual clang::ExternalLoadResult + FindExternalLexicalDecls (const clang::DeclContext *decl_ctx, + bool (*isKindWeWant)(clang::Decl::Kind), + llvm::SmallVectorImpl &decls) + { + // This is used to support iterating through an entire lexical context, + // which isn't something the debugger should ever need to do. + return clang::ELR_Failure; + } + + virtual bool + FindExternalVisibleDeclsByName (const clang::DeclContext *decl_ctx, + clang::DeclarationName decl_name); + + virtual void + CompleteType (clang::TagDecl *tag_decl); + + virtual void + CompleteType (clang::ObjCInterfaceDecl *objc_decl); + + bool + layoutRecordType(const clang::RecordDecl *Record, + uint64_t &Size, + uint64_t &Alignment, + llvm::DenseMap &FieldOffsets, + llvm::DenseMap &BaseOffsets, + llvm::DenseMap &VirtualBaseOffsets); + void + SetExternalSourceCallbacks (CompleteTagDeclCallback tag_decl_callback, + CompleteObjCInterfaceDeclCallback objc_decl_callback, + FindExternalVisibleDeclsByNameCallback find_by_name_callback, + LayoutRecordTypeCallback layout_record_type_callback, + void *callback_baton) + { + m_callback_tag_decl = tag_decl_callback; + m_callback_objc_decl = objc_decl_callback; + m_callback_find_by_name = find_by_name_callback; + m_callback_layout_record_type = layout_record_type_callback; + m_callback_baton = callback_baton; + } + + void + RemoveExternalSourceCallbacks (void *callback_baton) + { + if (callback_baton == m_callback_baton) + { + m_callback_tag_decl = NULL; + m_callback_objc_decl = NULL; + m_callback_find_by_name = NULL; + m_callback_layout_record_type = NULL; + } + } + +protected: + //------------------------------------------------------------------ + // Classes that inherit from ClangExternalASTSourceCallbacks can see and modify these + //------------------------------------------------------------------ + CompleteTagDeclCallback m_callback_tag_decl; + CompleteObjCInterfaceDeclCallback m_callback_objc_decl; + FindExternalVisibleDeclsByNameCallback m_callback_find_by_name; + LayoutRecordTypeCallback m_callback_layout_record_type; + void * m_callback_baton; +}; + +} // namespace lldb_private + +#endif // liblldb_ClangExternalASTSourceCallbacks_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Symbol/ClangExternalASTSourceCommon.h b/contrib/llvm/tools/lldb/include/lldb/Symbol/ClangExternalASTSourceCommon.h new file mode 100644 index 00000000000..72d77e74ca9 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Symbol/ClangExternalASTSourceCommon.h @@ -0,0 +1,188 @@ +//===-- ClangExternalASTSourceCommon.h --------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ClangExternalASTSourceCommon_h +#define liblldb_ClangExternalASTSourceCommon_h + +// Clang headers like to use NDEBUG inside of them to enable/disable debug +// releated features using "#ifndef NDEBUG" preprocessor blocks to do one thing +// or another. This is bad because it means that if clang was built in release +// mode, it assumes that you are building in release mode which is not always +// the case. You can end up with functions that are defined as empty in header +// files when NDEBUG is not defined, and this can cause link errors with the +// clang .a files that you have since you might be missing functions in the .a +// file. So we have to define NDEBUG when including clang headers to avoid any +// mismatches. This is covered by rdar://problem/8691220 + +#if !defined(NDEBUG) && !defined(LLVM_NDEBUG_OFF) +#define LLDB_DEFINED_NDEBUG_FOR_CLANG +#define NDEBUG +// Need to include assert.h so it is as clang would expect it to be (disabled) +#include +#endif + +#include "clang/AST/ExternalASTSource.h" + +#ifdef LLDB_DEFINED_NDEBUG_FOR_CLANG +#undef NDEBUG +#undef LLDB_DEFINED_NDEBUG_FOR_CLANG +// Need to re-include assert.h so it is as _we_ would expect it to be (enabled) +#include +#endif + +#include "lldb/lldb-defines.h" +#include "lldb/lldb-enumerations.h" +#include "lldb/Core/dwarf.h" + +namespace lldb_private { + +class ClangASTMetadata +{ +public: + ClangASTMetadata () : + m_user_id(0), + m_union_is_user_id(false), + m_union_is_isa_ptr(false), + m_has_object_ptr(false), + m_is_self (false), + m_is_dynamic_cxx (true) + { + } + + bool + GetIsDynamicCXXType () const + { + return m_is_dynamic_cxx; + } + + void + SetIsDynamicCXXType (bool b) + { + m_is_dynamic_cxx = b; + } + + void + SetUserID (lldb::user_id_t user_id) + { + m_user_id = user_id; + m_union_is_user_id = true; + m_union_is_isa_ptr = false; + } + + lldb::user_id_t + GetUserID () const + { + if (m_union_is_user_id) + return m_user_id; + else + return LLDB_INVALID_UID; + } + + void + SetISAPtr (uint64_t isa_ptr) + { + m_isa_ptr = isa_ptr; + m_union_is_user_id = false; + m_union_is_isa_ptr = true; + } + + uint64_t + GetISAPtr () const + { + if (m_union_is_isa_ptr) + return m_isa_ptr; + else + return 0; + } + + void + SetObjectPtrName(const char *name) + { + m_has_object_ptr = true; + if (strcmp (name, "self") == 0) + m_is_self = true; + else if (strcmp (name, "this") == 0) + m_is_self = false; + else + m_has_object_ptr = false; + } + + lldb::LanguageType + GetObjectPtrLanguage () const + { + if (m_has_object_ptr) + { + if (m_is_self) + return lldb::eLanguageTypeObjC; + else + return lldb::eLanguageTypeC_plus_plus; + } + return lldb::eLanguageTypeUnknown; + + } + const char * + GetObjectPtrName() const + { + if (m_has_object_ptr) + { + if (m_is_self) + return "self"; + else + return "this"; + } + else + return NULL; + } + + bool + HasObjectPtr() const + { + return m_has_object_ptr; + } + + void + Dump (Stream *s); + +private: + union + { + lldb::user_id_t m_user_id; + uint64_t m_isa_ptr; + }; + bool m_union_is_user_id : 1, + m_union_is_isa_ptr : 1, + m_has_object_ptr : 1, + m_is_self : 1, + m_is_dynamic_cxx : 1; + +}; + +class ClangExternalASTSourceCommon : public clang::ExternalASTSource +{ +public: + ClangExternalASTSourceCommon(); + ~ClangExternalASTSourceCommon(); + + virtual ClangASTMetadata *GetMetadata(const void *object); + virtual void SetMetadata(const void *object, ClangASTMetadata &metadata); + virtual bool HasMetadata(const void *object); +private: + typedef llvm::DenseMap MetadataMap; + + MetadataMap m_metadata; + uint64_t m_magic; ///< Because we don't have RTTI, we must take it + ///< on faith that any valid ExternalASTSource that + ///< we try to use the *Metadata APIs on inherits + ///< from ClangExternalASTSourceCommon. This magic + ///< number exists to enforce that. +}; + +} + +#endif diff --git a/contrib/llvm/tools/lldb/include/lldb/Symbol/ClangNamespaceDecl.h b/contrib/llvm/tools/lldb/include/lldb/Symbol/ClangNamespaceDecl.h new file mode 100644 index 00000000000..d10ab2a2966 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Symbol/ClangNamespaceDecl.h @@ -0,0 +1,103 @@ +//===-- ClangNamespaceDecl.h ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ClangNamespaceDecl_h_ +#define liblldb_ClangNamespaceDecl_h_ + +#include "lldb/lldb-public.h" +#include "lldb/Core/ClangForward.h" + +namespace lldb_private { + +class ClangNamespaceDecl +{ +public: + ClangNamespaceDecl () : + m_ast (NULL), + m_namespace_decl (NULL) + { + } + + ClangNamespaceDecl (clang::ASTContext *ast, clang::NamespaceDecl *namespace_decl) : + m_ast (ast), + m_namespace_decl (namespace_decl) + { + } + + ClangNamespaceDecl (const ClangNamespaceDecl &rhs) : + m_ast (rhs.m_ast), + m_namespace_decl (rhs.m_namespace_decl) + { + } + + const ClangNamespaceDecl & + operator = (const ClangNamespaceDecl &rhs) + { + m_ast = rhs.m_ast; + m_namespace_decl = rhs.m_namespace_decl; + return *this; + } + + //------------------------------------------------------------------ + /// Convert to bool operator. + /// + /// This allows code to check a ClangNamespaceDecl object to see if + /// it contains a valid namespace decl using code such as: + /// + /// @code + /// ClangNamespaceDecl ns_decl(...); + /// if (ns_decl) + /// { ... + /// @endcode + /// + /// @return + /// /b True this object contains a valid namespace decl, \b + /// false otherwise. + //------------------------------------------------------------------ + operator bool() const + { + return m_ast != NULL && m_namespace_decl != NULL; + } + + clang::ASTContext * + GetASTContext() const + { + return m_ast; + } + + void + SetASTContext (clang::ASTContext *ast) + { + m_ast = ast; + } + + clang::NamespaceDecl * + GetNamespaceDecl () const + { + return m_namespace_decl; + } + + void + SetNamespaceDecl (clang::NamespaceDecl *namespace_decl) + { + m_namespace_decl = namespace_decl; + } + + std::string + GetQualifiedName () const; + +protected: + clang::ASTContext *m_ast; + clang::NamespaceDecl *m_namespace_decl; +}; + + +} // namespace lldb_private + +#endif // #ifndef liblldb_ClangNamespaceDecl_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Symbol/CompileUnit.h b/contrib/llvm/tools/lldb/include/lldb/Symbol/CompileUnit.h new file mode 100644 index 00000000000..5de93670c5a --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Symbol/CompileUnit.h @@ -0,0 +1,422 @@ +//===-- CompileUnit.h -------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_CompUnit_h_ +#define liblldb_CompUnit_h_ + +#include "lldb/lldb-enumerations.h" +#include "lldb/Symbol/Function.h" +#include "lldb/Core/FileSpecList.h" +#include "lldb/Core/ModuleChild.h" +#include "lldb/Core/Stream.h" +#include "lldb/Core/UserID.h" + +namespace lldb_private { +//---------------------------------------------------------------------- +/// @class CompileUnit CompileUnit.h "lldb/Symbol/CompileUnit.h" +/// @brief A class that describes a compilation unit. +/// +/// A representation of a compilation unit, or compiled source file. +/// The UserID of the compile unit is specified by the SymbolFile +/// plug-in and can have any value as long as the value is unique +/// within the Module that owns this compile units. +/// +/// Each compile unit has a list of functions, global and static +/// variables, support file list (include files and inlined source +/// files), and a line table. +//---------------------------------------------------------------------- +class CompileUnit : + public std::enable_shared_from_this, + public ModuleChild, + public FileSpec, + public UserID, + public SymbolContextScope +{ +public: + //------------------------------------------------------------------ + /// Construct with a module, path, UID and language. + /// + /// Initialize the compile unit given the owning \a module, a path + /// to convert into a FileSpec, the SymbolFile plug-in supplied + /// \a uid, and the source language type. + /// + /// @param[in] module + /// The parent module that owns this compile unit. This value + /// must be a valid pointer value. + /// + /// @param[in] user_data + /// User data where the SymbolFile parser can store data. + /// + /// @param[in] pathname + /// The path to the source file for this compile unit. + /// + /// @param[in] uid + /// The user ID of the compile unit. This value is supplied by + /// the SymbolFile plug-in and should be a value that allows + /// the SymbolFile plug-in to easily locate and parse additional + /// information for the compile unit. + /// + /// @param[in] language + /// A language enumeration type that describes the main language + /// of this compile unit. + /// + /// @see lldb::LanguageType + //------------------------------------------------------------------ + CompileUnit(const lldb::ModuleSP &module_sp, void *user_data, const char *pathname, lldb::user_id_t uid, lldb::LanguageType language); + + //------------------------------------------------------------------ + /// Construct with a module, file spec, UID and language. + /// + /// Initialize the compile unit given the owning \a module, a path + /// to convert into a FileSpec, the SymbolFile plug-in supplied + /// \a uid, and the source language type. + /// + /// @param[in] module + /// The parent module that owns this compile unit. This value + /// must be a valid pointer value. + /// + /// @param[in] user_data + /// User data where the SymbolFile parser can store data. + /// + /// @param[in] file_spec + /// The file specification for the source file of this compile + /// unit. + /// + /// @param[in] uid + /// The user ID of the compile unit. This value is supplied by + /// the SymbolFile plug-in and should be a value that allows + /// the plug-in to easily locate and parse + /// additional information for the compile unit. + /// + /// @param[in] language + /// A language enumeration type that describes the main language + /// of this compile unit. + /// + /// @see lldb::LanguageType + //------------------------------------------------------------------ + CompileUnit(const lldb::ModuleSP &module_sp, void *user_data, const FileSpec &file_spec, lldb::user_id_t uid, lldb::LanguageType language); + + //------------------------------------------------------------------ + /// Destructor + //------------------------------------------------------------------ + virtual + ~CompileUnit(); + + //------------------------------------------------------------------ + /// Add a function to this compile unit. + /// + /// Typically called by the SymbolFile plug-ins as they partially + /// parse the debug information. + /// + /// @param[in] function_sp + /// A shared pointer to the a Function object. + //------------------------------------------------------------------ + void + AddFunction(lldb::FunctionSP& function_sp); + + //------------------------------------------------------------------ + /// @copydoc SymbolContextScope::CalculateSymbolContext(SymbolContext*) + /// + /// @see SymbolContextScope + //------------------------------------------------------------------ + virtual void + CalculateSymbolContext(SymbolContext* sc); + + virtual lldb::ModuleSP + CalculateSymbolContextModule (); + + virtual CompileUnit * + CalculateSymbolContextCompileUnit (); + + //------------------------------------------------------------------ + /// @copydoc SymbolContextScope::DumpSymbolContext(Stream*) + /// + /// @see SymbolContextScope + //------------------------------------------------------------------ + virtual void + DumpSymbolContext(Stream *s); + + lldb::LanguageType + GetLanguage(); + + void + SetLanguage(lldb::LanguageType language) + { + m_flags.Set(flagsParsedLanguage); + m_language = language; + } + + void + GetDescription(Stream *s, lldb::DescriptionLevel level) const; + + //------------------------------------------------------------------ + /// Get a shared pointer to a function in this compile unit by + /// index. + /// + /// Typically called when iterating though all functions in a + /// compile unit after all functions have been parsed. This provides + /// raw access to the function shared pointer list and will not + /// cause the SymbolFile plug-in to parse any unparsed functions. + /// + /// @param[in] idx + /// An index into the function list. + /// + /// @return + /// A shared pointer to a function that might contain a NULL + /// Function class pointer. + //------------------------------------------------------------------ + lldb::FunctionSP + GetFunctionAtIndex (size_t idx); + + //------------------------------------------------------------------ + /// Dump the compile unit contents to the stream \a s. + /// + /// @param[in] s + /// The stream to which to dump the object descripton. + /// + /// @param[in] show_context + /// If \b true, variables will dump their symbol context + /// information. + //------------------------------------------------------------------ + void + Dump (Stream *s, bool show_context) const; + + //------------------------------------------------------------------ + /// Find the line entry by line and optional inlined file spec. + /// + /// Finds the first line entry that has an index greater than + /// \a start_idx that matches \a line. If \a file_spec_ptr + /// is NULL, then the search matches line entries whose file matches + /// the file for the compile unit. If \a file_spec_ptr is + /// not NULL, line entries must match the specified file spec (for + /// inlined line table entries). + /// + /// Multiple calls to this function can find all entries that match + /// a given file and line by starting with \a start_idx equal to zero, + /// and calling this function back with the return valeu + 1. + /// + /// @param[in] start_idx + /// The zero based index at which to start looking for matches. + /// + /// @param[in] line + /// The line number to search for. + /// + /// @param[in] file_spec_ptr + /// If non-NULL search for entries that match this file spec, + /// else if NULL, search for line entries that match the compile + /// unit file. + /// + /// @param[in] exact + /// If \btrue match only if there is a line table entry for this line number. + /// If \bfalse, find the line table entry equal to or after this line number. + /// + /// @param[out] line_entry + /// If non-NULL, a copy of the line entry that was found. + /// + /// @return + /// The zero based index of a matching line entry, or UINT32_MAX + /// if no matching line entry is found. + //------------------------------------------------------------------ + uint32_t + FindLineEntry (uint32_t start_idx, + uint32_t line, + const FileSpec* file_spec_ptr, + bool exact, + LineEntry *line_entry); + + //------------------------------------------------------------------ + /// Get the line table for the compile unit. + /// + /// Called by clients and the SymbolFile plug-in. The SymbolFile + /// plug-ins use this function to determine if the line table has + /// be parsed yet. Clients use this function to get the line table + /// from a compile unit. + /// + /// @return + /// The line table object pointer, or NULL if this line table + /// hasn't been parsed yet. + //------------------------------------------------------------------ + LineTable* + GetLineTable (); + + //------------------------------------------------------------------ + /// Get the compile unit's support file list. + /// + /// The support file list is used by the line table, and any objects + /// that have valid Declaration objects. + /// + /// @return + /// A support file list object. + //------------------------------------------------------------------ + FileSpecList& + GetSupportFiles (); + + //------------------------------------------------------------------ + /// Get the SymbolFile plug-in user data. + /// + /// SymbolFile plug-ins can store user data to internal state or + /// objects to quickly allow them to parse more information for a + /// given object. + /// + /// @return + /// The user data stored with the CompileUnit when it was + /// constructed. + //------------------------------------------------------------------ + void * + GetUserData () const; + + //------------------------------------------------------------------ + /// Get the variable list for a compile unit. + /// + /// Called by clients to get the variable list for a compile unit. + /// The variable list will contain all global and static variables + /// that were defined at the compile unit level. + /// + /// @param[in] can_create + /// If \b true, the variable list will be parsed on demand. If + /// \b false, the current variable list will be returned even + /// if it contains a NULL VariableList object (typically + /// called by dumping routines that want to display only what + /// has currently been parsed). + /// + /// @return + /// A shared pointer to a variable list, that can contain NULL + /// VariableList pointer if there are no global or static + /// variables. + //------------------------------------------------------------------ + lldb::VariableListSP + GetVariableList (bool can_create); + + //------------------------------------------------------------------ + /// Finds a function by user ID. + /// + /// Typically used by SymbolFile plug-ins when partially parsing + /// the debug information to see if the function has been parsed + /// yet. + /// + /// @param[in] uid + /// The user ID of the function to find. This value is supplied + /// by the SymbolFile plug-in and should be a value that + /// allows the plug-in to easily locate and parse additional + /// information in the function. + /// + /// @return + /// A shared pointer to the function object that might contain + /// a NULL Function pointer. + //------------------------------------------------------------------ + lldb::FunctionSP + FindFunctionByUID (lldb::user_id_t uid); + + //------------------------------------------------------------------ + /// Set the line table for the compile unit. + /// + /// Called by the SymbolFile plug-in when if first parses the line + /// table and hands ownership of the line table to this object. The + /// compile unit owns the line table object and will delete the + /// object when it is deleted. + /// + /// @param[in] line_table + /// A line table object pointer that this object now owns. + //------------------------------------------------------------------ + void + SetLineTable(LineTable* line_table); + + //------------------------------------------------------------------ + /// Set accessor for the variable list. + /// + /// Called by the SymbolFile plug-ins after they have parsed the + /// variable lists and are ready to hand ownership of the list over + /// to this object. + /// + /// @param[in] variable_list_sp + /// A shared pointer to a VariableList. + //------------------------------------------------------------------ + void + SetVariableList (lldb::VariableListSP& variable_list_sp); + + //------------------------------------------------------------------ + /// Resolve symbol contexts by file and line. + /// + /// Given a file in \a file_spec, and a line number, find all + /// instances and append them to the supplied symbol context list + /// \a sc_list. + /// + /// @param[in] file_spec + /// A file specification. If \a file_spec contains no directory + /// information, only the basename will be used when matching + /// contexts. If the directory in \a file_spec is valid, a + /// complete file specification match will be performed. + /// + /// @param[in] line + /// The line number to match against the compile unit's line + /// tables. + /// + /// @param[in] check_inlines + /// If \b true this function will also match any inline + /// file and line matches. If \b false, the compile unit's + /// file specification must match \a file_spec for any matches + /// to be returned. + /// + /// @param[in] exact + /// If true, only resolve the context if \a line exists in the line table. + /// If false, resolve the context to the closest line greater than \a line + /// in the line table. + /// + /// @param[in] resolve_scope + /// For each matching line entry, this bitfield indicates what + /// values within each SymbolContext that gets added to \a + /// sc_list will be resolved. See the SymbolContext::Scope + /// enumeration for a list of all available bits that can be + /// resolved. Only SymbolContext entries that can be resolved + /// using a LineEntry base address will be able to be resolved. + /// + /// @param[out] sc_list + /// A SymbolContext list class that willl get any matching + /// entries appended to. + /// + /// @return + /// The number of new matches that were added to \a sc_list. + /// + /// @see enum SymbolContext::Scope + //------------------------------------------------------------------ + uint32_t + ResolveSymbolContext (const FileSpec& file_spec, + uint32_t line, + bool check_inlines, + bool exact, + uint32_t resolve_scope, + SymbolContextList &sc_list); + + +protected: + void *m_user_data; ///< User data for the SymbolFile parser to store information into. + lldb::LanguageType m_language; ///< The programming language enumeration value. + Flags m_flags; ///< Compile unit flags that help with partial parsing. + std::vector m_functions; ///< The sparsely populated list of shared pointers to functions + ///< that gets populated as functions get partially parsed. + FileSpecList m_support_files; ///< Files associated with this compile unit's line table and declarations. + std::unique_ptr m_line_table_ap; ///< Line table that will get parsed on demand. + lldb::VariableListSP m_variables; ///< Global and static variable list that will get parsed on demand. + +private: + enum + { + flagsParsedAllFunctions = (1u << 0), ///< Have we already parsed all our functions + flagsParsedVariables = (1u << 1), ///< Have we already parsed globals and statics? + flagsParsedSupportFiles = (1u << 2), ///< Have we already parsed the support files for this compile unit? + flagsParsedLineTable = (1u << 3), ///< Have we parsed the line table already? + flagsParsedLanguage = (1u << 4) ///< Have we parsed the line table already? + }; + + DISALLOW_COPY_AND_ASSIGN (CompileUnit); +}; + +} // namespace lldb_private + +#endif // liblldb_CompUnit_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Symbol/DWARFCallFrameInfo.h b/contrib/llvm/tools/lldb/include/lldb/Symbol/DWARFCallFrameInfo.h new file mode 100644 index 00000000000..13a14f8c404 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Symbol/DWARFCallFrameInfo.h @@ -0,0 +1,150 @@ +//===-- DWARFCallFrameInfo.h ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_DWARFCallFrameInfo_h_ +#define liblldb_DWARFCallFrameInfo_h_ + +#include + +#include "lldb/Core/AddressRange.h" +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/Flags.h" +#include "lldb/Core/RangeMap.h" +#include "lldb/Core/VMRange.h" +#include "lldb/Core/dwarf.h" +#include "lldb/Host/Mutex.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Symbol/UnwindPlan.h" +#include "lldb/lldb-private.h" + +namespace lldb_private { + +// DWARFCallFrameInfo is a class which can read eh_frame and DWARF +// Call Frame Information FDEs. It stores little information internally. +// Only two APIs are exported - one to find the high/low pc values +// of a function given a text address via the information in the +// eh_frame / debug_frame, and one to generate an UnwindPlan based +// on the FDE in the eh_frame / debug_frame section. + +class DWARFCallFrameInfo +{ +public: + + DWARFCallFrameInfo (ObjectFile& objfile, + lldb::SectionSP& section, + lldb::RegisterKind reg_kind, + bool is_eh_frame); + + ~DWARFCallFrameInfo(); + + // Locate an AddressRange that includes the provided Address in this + // object's eh_frame/debug_info + // Returns true if a range is found to cover that address. + bool + GetAddressRange (Address addr, AddressRange &range); + + // Return an UnwindPlan based on the call frame information encoded + // in the FDE of this DWARFCallFrameInfo section. + bool + GetUnwindPlan (Address addr, UnwindPlan& unwind_plan); + + typedef RangeVector FunctionAddressAndSizeVector; + + //------------------------------------------------------------------ + // Build a vector of file address and size for all functions in this Module + // based on the eh_frame FDE entries. + // + // The eh_frame information can be a useful source of file address and size of + // the functions in a Module. Often a binary's non-exported symbols are stripped + // before shipping so lldb won't know the start addr / size of many functions + // in the Module. But the eh_frame can help to give the addresses of these + // stripped symbols, at least. + // + // @param[out] function_info + // A vector provided by the caller is filled out. May be empty if no FDEs/no eh_frame + // is present in this Module. + + void + GetFunctionAddressAndSizeVector (FunctionAddressAndSizeVector &function_info); + +private: + enum + { + CFI_AUG_MAX_SIZE = 8, + CFI_HEADER_SIZE = 8 + }; + + struct CIE + { + dw_offset_t cie_offset; + uint8_t version; + char augmentation[CFI_AUG_MAX_SIZE]; // This is typically empty or very short. + uint32_t code_align; + int32_t data_align; + uint32_t return_addr_reg_num; + dw_offset_t inst_offset; // offset of CIE instructions in mCFIData + uint32_t inst_length; // length of CIE instructions in mCFIData + uint8_t ptr_encoding; + lldb_private::UnwindPlan::Row initial_row; + + CIE(dw_offset_t offset) : cie_offset(offset), version (-1), code_align (0), + data_align (0), return_addr_reg_num (LLDB_INVALID_REGNUM), inst_offset (0), + inst_length (0), ptr_encoding (0), initial_row() {} + }; + + typedef std::shared_ptr CIESP; + + typedef std::map cie_map_t; + + // Start address (file address), size, offset of FDE location + // used for finding an FDE for a given File address; the start address field is + // an offset into an individual Module. + typedef RangeDataVector FDEEntryMap; + + bool + IsEHFrame() const; + + bool + GetFDEEntryByFileAddress (lldb::addr_t file_offset, FDEEntryMap::Entry& fde_entry); + + void + GetFDEIndex (); + + bool + FDEToUnwindPlan (uint32_t offset, Address startaddr, UnwindPlan& unwind_plan); + + const CIE* + GetCIE(dw_offset_t cie_offset); + + void + GetCFIData(); + + ObjectFile& m_objfile; + lldb::SectionSP m_section_sp; + lldb::RegisterKind m_reg_kind; + Flags m_flags; + cie_map_t m_cie_map; + + DataExtractor m_cfi_data; + bool m_cfi_data_initialized; // only copy the section into the DE once + + FDEEntryMap m_fde_index; + bool m_fde_index_initialized; // only scan the section for FDEs once + Mutex m_fde_index_mutex; // and isolate the thread that does it + + bool m_is_eh_frame; + + CIESP + ParseCIE (const uint32_t cie_offset); + +}; + +} // namespace lldb_private + +#endif // liblldb_DWARFCallFrameInfo_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Symbol/Declaration.h b/contrib/llvm/tools/lldb/include/lldb/Symbol/Declaration.h new file mode 100644 index 00000000000..f014571595f --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Symbol/Declaration.h @@ -0,0 +1,278 @@ +//===-- Declaration.h -------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_Declaration_h_ +#define liblldb_Declaration_h_ + +#include "lldb/lldb-private.h" +#include "lldb/Host/FileSpec.h" + +namespace lldb_private { + +//---------------------------------------------------------------------- +/// @class Declaration Declaration.h "lldb/Symbol/Declaration.h" +/// @brief A class that describes the declaration location of a +/// lldb object. +/// +/// The declarations include the file specification, line number, and +/// the column info and can help track where functions, blocks, inlined +/// functions, types, variables, any many other debug core objects were +/// declared. +//---------------------------------------------------------------------- +class Declaration +{ +public: + //------------------------------------------------------------------ + /// Default constructor. + //------------------------------------------------------------------ + Declaration () : + m_file (), + m_line (0) +#ifdef LLDB_ENABLE_DECLARATION_COLUMNS + ,m_column (0) +#endif + { + } + + + //------------------------------------------------------------------ + /// Construct with file specification, and optional line and column. + /// + /// @param[in] file_spec + /// The file specification that describes where this was + /// declared. + /// + /// @param[in] line + /// The line number that describes where this was declared. Set + /// to zero if there is no line number information. + /// + /// @param[in] column + /// The column number that describes where this was declared. + /// Set to zero if there is no column number information. + //------------------------------------------------------------------ + Declaration (const FileSpec& file_spec, uint32_t line = 0, uint32_t column = 0) : + m_file (file_spec), + m_line (line) +#ifdef LLDB_ENABLE_DECLARATION_COLUMNS + ,m_column (column) +#endif + { + } + + //------------------------------------------------------------------ + /// Construct with a reference to another Declaration object. + //------------------------------------------------------------------ + Declaration (const Declaration& rhs) : + m_file (rhs.m_file), + m_line (rhs.m_line) +#ifdef LLDB_ENABLE_DECLARATION_COLUMNS + ,m_column (rhs.m_column) +#endif + { + + } + + //------------------------------------------------------------------ + /// Construct with a pointer to another Declaration object. + //------------------------------------------------------------------ + Declaration(const Declaration* decl_ptr) : + m_file(), + m_line(0) +#ifdef LLDB_ENABLE_DECLARATION_COLUMNS + ,m_column(0) +#endif + { + if (decl_ptr) + *this = *decl_ptr; + } + + //------------------------------------------------------------------ + /// Clear the object's state. + /// + /// Sets the file specification to be empty, and the line and column + /// to zero. + //------------------------------------------------------------------ + void + Clear () + { + m_file.Clear(); + m_line= 0; +#ifdef LLDB_ENABLE_DECLARATION_COLUMNS + m_column = 0; +#endif + } + + //------------------------------------------------------------------ + /// Compare two declaration objects. + /// + /// Compares the two file specifications from \a lhs and \a rhs. If + /// the file specifications are equal, then continue to compare the + /// line number and column numbers respectively. + /// + /// @param[in] lhs + /// The Left Hand Side const Declaration object reference. + /// + /// @param[in] rhs + /// The Right Hand Side const Declaration object reference. + /// + /// @return + /// @li -1 if lhs < rhs + /// @li 0 if lhs == rhs + /// @li 1 if lhs > rhs + //------------------------------------------------------------------ + static int + Compare (const Declaration& lhs, const Declaration& rhs); + + //------------------------------------------------------------------ + /// Dump a description of this object to a Stream. + /// + /// Dump a description of the contents of this object to the + /// supplied stream \a s. + /// + /// @param[in] s + /// The stream to which to dump the object descripton. + //------------------------------------------------------------------ + void + Dump (Stream *s, bool show_fullpaths) const; + + bool + DumpStopContext (Stream *s, bool show_fullpaths) const; + //------------------------------------------------------------------ + /// Get accessor for the declaration column number. + /// + /// @return + /// Non-zero indicates a valid column number, zero indicates no + /// column information is available. + //------------------------------------------------------------------ + uint32_t + GetColumn () const + { +#ifdef LLDB_ENABLE_DECLARATION_COLUMNS + return m_column; +#else + return 0; +#endif + } + + //------------------------------------------------------------------ + /// Get accessor for file specification. + /// + /// @return + /// A reference to the file specification object. + //------------------------------------------------------------------ + FileSpec& + GetFile () + { + return m_file; + } + + //------------------------------------------------------------------ + /// Get const accessor for file specification. + /// + /// @return + /// A const reference to the file specification object. + //------------------------------------------------------------------ + const FileSpec& + GetFile () const + { + return m_file; + } + + //------------------------------------------------------------------ + /// Get accessor for the declaration line number. + /// + /// @return + /// Non-zero indicates a valid line number, zero indicates no + /// line information is available. + //------------------------------------------------------------------ + uint32_t + GetLine () const + { + return m_line; + } + + + bool + IsValid() const + { + return m_file && m_line != 0; + } + + //------------------------------------------------------------------ + /// Get the memory cost of this object. + /// + /// @return + /// The number of bytes that this object occupies in memory. + /// The returned value does not include the bytes for any + /// shared string values. + /// + /// @see ConstString::StaticMemorySize () + //------------------------------------------------------------------ + size_t + MemorySize () const; + + //------------------------------------------------------------------ + /// Set accessor for the declaration column number. + /// + /// @param[in] column + /// Non-zero indicates a valid column number, zero indicates no + /// column information is available. + //------------------------------------------------------------------ + void + SetColumn (uint32_t column) + { +#ifdef LLDB_ENABLE_DECLARATION_COLUMNS + m_column = col; +#endif + } + + //------------------------------------------------------------------ + /// Set accessor for the declaration file specification. + /// + /// @param[in] file_spec + /// The new declaration file specifciation. + //------------------------------------------------------------------ + void + SetFile (const FileSpec& file_spec) + { + m_file = file_spec; + } + + //------------------------------------------------------------------ + /// Set accessor for the declaration line number. + /// + /// @param[in] line + /// Non-zero indicates a valid line number, zero indicates no + /// line information is available. + //------------------------------------------------------------------ + void + SetLine (uint32_t line) + { + m_line = line; + } +protected: + //------------------------------------------------------------------ + /// Member variables. + //------------------------------------------------------------------ + FileSpec m_file; ///< The file specification that points to the + ///< source file where the declaration occurred. + uint32_t m_line; ///< Non-zero values indicates a valid line number, + ///< zero indicates no line number information is available. +#ifdef LLDB_ENABLE_DECLARATION_COLUMNS + uint32_t m_column; ///< Non-zero values indicates a valid column number, + ///< zero indicates no column information is available. +#endif +}; + +bool +operator == (const Declaration &lhs, const Declaration &rhs); + +} // namespace lldb_private + +#endif // liblldb_Declaration_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Symbol/FuncUnwinders.h b/contrib/llvm/tools/lldb/include/lldb/Symbol/FuncUnwinders.h new file mode 100644 index 00000000000..fa48dc27e12 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Symbol/FuncUnwinders.h @@ -0,0 +1,106 @@ +#ifndef liblldb_FuncUnwinders_h +#define liblldb_FuncUnwinders_h + +#include "lldb/Core/AddressRange.h" +#include "lldb/Core/ArchSpec.h" +#include "lldb/Core/AddressRange.h" +#include "lldb/Host/Mutex.h" + +namespace lldb_private { + +class UnwindTable; + +class FuncUnwinders +{ +public: + // FuncUnwinders objects are used to track UnwindPlans for a function + // (named or not - really just an address range) + + // We'll record three different UnwindPlans for each address range: + // 1. Unwinding from a call site (a valid exception throw location) + // This is often sourced from the eh_frame exception handling info + // 2. Unwinding from a non-call site (any location in the function) + // This is often done by analyzing the function prologue assembly + // langauge instructions + // 3. A fast unwind method for this function which only retrieves a + // limited set of registers necessary to walk the stack + // 4. An architectural default unwind plan when none of the above are + // available for some reason. + + // Additionally, FuncUnwinds object can be asked where the prologue + // instructions are finished for migrating breakpoints past the + // stack frame setup instructions when we don't have line table information. + + FuncUnwinders (lldb_private::UnwindTable& unwind_table, lldb_private::UnwindAssembly *assembly_profiler, AddressRange range); + + ~FuncUnwinders (); + + // current_offset is the byte offset into the function. + // 0 means no instructions have executed yet. -1 means the offset is unknown. + // On architectures where the pc points to the next instruction that will execute, this + // offset value will have already been decremented by 1 to stay within the bounds of the + // correct function body. + lldb::UnwindPlanSP + GetUnwindPlanAtCallSite (int current_offset); + + lldb::UnwindPlanSP + GetUnwindPlanAtNonCallSite (lldb_private::Thread& thread); + + lldb::UnwindPlanSP + GetUnwindPlanFastUnwind (lldb_private::Thread& Thread); + + lldb::UnwindPlanSP + GetUnwindPlanArchitectureDefault (lldb_private::Thread& thread); + + lldb::UnwindPlanSP + GetUnwindPlanArchitectureDefaultAtFunctionEntry (lldb_private::Thread& thread); + + Address& + GetFirstNonPrologueInsn (Target& target); + + const Address& + GetFunctionStartAddress () const; + + bool + ContainsAddress (const Address& addr) const + { + return m_range.ContainsFileAddress (addr); + } + + // When we're doing an unwind using the UnwindPlanAtNonCallSite and we find an + // impossible unwind condition, we know that the UnwindPlan is invalid. Calling + // this method on the FuncUnwinder will tell it to replace that UnwindPlan with + // the architectural default UnwindPlan so hopefully our stack walk will get past + // this frame. + void + InvalidateNonCallSiteUnwindPlan (lldb_private::Thread& Thread); + +private: + UnwindTable& m_unwind_table; + UnwindAssembly *m_assembly_profiler; + AddressRange m_range; + + Mutex m_mutex; + lldb::UnwindPlanSP m_unwind_plan_call_site_sp; + lldb::UnwindPlanSP m_unwind_plan_non_call_site_sp; + lldb::UnwindPlanSP m_unwind_plan_fast_sp; + lldb::UnwindPlanSP m_unwind_plan_arch_default_sp; + lldb::UnwindPlanSP m_unwind_plan_arch_default_at_func_entry_sp; + + bool m_tried_unwind_at_call_site:1, + m_tried_unwind_at_non_call_site:1, + m_tried_unwind_fast:1, + m_tried_unwind_arch_default:1, + m_tried_unwind_arch_default_at_func_entry:1; + + + Address m_first_non_prologue_insn; + + DISALLOW_COPY_AND_ASSIGN (FuncUnwinders); + +}; // class FuncUnwinders + +} // namespace lldb_private + + +#endif //liblldb_FuncUnwinders_h diff --git a/contrib/llvm/tools/lldb/include/lldb/Symbol/Function.h b/contrib/llvm/tools/lldb/include/lldb/Symbol/Function.h new file mode 100644 index 00000000000..787f81c5ad2 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Symbol/Function.h @@ -0,0 +1,638 @@ +//===-- Function.h ----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_Function_h_ +#define liblldb_Function_h_ + +#include "lldb/Core/ClangForward.h" +#include "lldb/Core/AddressRange.h" +#include "lldb/Symbol/Block.h" +#include "lldb/Symbol/Declaration.h" +#include "lldb/Expression/DWARFExpression.h" +#include "lldb/Core/Mangled.h" +#include "lldb/Core/UserID.h" + +namespace lldb_private { + +//---------------------------------------------------------------------- +/// @class FunctionInfo Function.h "lldb/Symbol/Function.h" +/// @brief A class that contains generic function information. +/// +/// This provides generic function information that gets resused between +/// inline functions and function types. +//---------------------------------------------------------------------- +class FunctionInfo +{ +public: + //------------------------------------------------------------------ + /// Construct with the function method name and optional declaration + /// information. + /// + /// @param[in] name + /// A C string name for the method name for this function. This + /// value should not be the mangled named, but the simple method + /// name. + /// + /// @param[in] decl_ptr + /// Optional declaration information that describes where the + /// function was declared. This can be NULL. + //------------------------------------------------------------------ + FunctionInfo (const char *name, const Declaration *decl_ptr); + + //------------------------------------------------------------------ + /// Construct with the function method name and optional declaration + /// information. + /// + /// @param[in] name + /// A name for the method name for this function. This value + /// should not be the mangled named, but the simple method name. + /// + /// @param[in] decl_ptr + /// Optional declaration information that describes where the + /// function was declared. This can be NULL. + //------------------------------------------------------------------ + FunctionInfo (const ConstString& name, const Declaration *decl_ptr); + + //------------------------------------------------------------------ + /// Destructor. + /// + /// The destructor is virtual since classes inherit from this class. + //------------------------------------------------------------------ + virtual + ~FunctionInfo (); + + //------------------------------------------------------------------ + /// Compare two function information objects. + /// + /// First compares the method names, and if equal, then compares + /// the declaration information. + /// + /// @param[in] lhs + /// The Left Hand Side const FunctionInfo object reference. + /// + /// @param[in] rhs + /// The Right Hand Side const FunctionInfo object reference. + /// + /// @return + /// @li -1 if lhs < rhs + /// @li 0 if lhs == rhs + /// @li 1 if lhs > rhs + //------------------------------------------------------------------ + static int + Compare (const FunctionInfo& lhs, const FunctionInfo& rhs); + + //------------------------------------------------------------------ + /// Dump a description of this object to a Stream. + /// + /// Dump a description of the contents of this object to the + /// supplied stream \a s. + /// + /// @param[in] s + /// The stream to which to dump the object descripton. + //------------------------------------------------------------------ + void + Dump (Stream *s, bool show_fullpaths) const; + + //------------------------------------------------------------------ + /// Get accessor for the declaration information. + /// + /// @return + /// A reference to the declaration object. + //------------------------------------------------------------------ + Declaration& + GetDeclaration (); + + //------------------------------------------------------------------ + /// Get const accessor for the declaration information. + /// + /// @return + /// A const reference to the declaration object. + //------------------------------------------------------------------ + const Declaration& + GetDeclaration () const; + + //------------------------------------------------------------------ + /// Get accessor for the method name. + /// + /// @return + /// A const reference to the method name object. + //------------------------------------------------------------------ + const ConstString& + GetName () const; + + //------------------------------------------------------------------ + /// Get the memory cost of this object. + /// + /// @return + /// The number of bytes that this object occupies in memory. + /// The returned value does not include the bytes for any + /// shared string values. + /// + /// @see ConstString::StaticMemorySize () + //------------------------------------------------------------------ + virtual size_t + MemorySize () const; + +protected: + //------------------------------------------------------------------ + // Member variables. + //------------------------------------------------------------------ + ConstString m_name; ///< Function method name (not a mangled name). + Declaration m_declaration; ///< Information describing where this function information was defined. +}; + + +//---------------------------------------------------------------------- +/// @class InlineFunctionInfo Function.h "lldb/Symbol/Function.h" +/// @brief A class that describes information for an inlined function. +//---------------------------------------------------------------------- +class InlineFunctionInfo : public FunctionInfo +{ +public: + //------------------------------------------------------------------ + /// Construct with the function method name, mangled name, and + /// optional declaration information. + /// + /// @param[in] name + /// A C string name for the method name for this function. This + /// value should not be the mangled named, but the simple method + /// name. + /// + /// @param[in] mangled + /// A C string name for the mangled name for this function. This + /// value can be NULL if there is no mangled information. + /// + /// @param[in] decl_ptr + /// Optional declaration information that describes where the + /// function was declared. This can be NULL. + /// + /// @param[in] call_decl_ptr + /// Optional calling location declaration information that + /// describes from where this inlined function was called. + //------------------------------------------------------------------ + InlineFunctionInfo(const char *name, const char *mangled, const Declaration *decl_ptr, const Declaration *call_decl_ptr); + + //------------------------------------------------------------------ + /// Construct with the function method name, mangled name, and + /// optional declaration information. + /// + /// @param[in] name + /// A name for the method name for this function. This value + /// should not be the mangled named, but the simple method name. + /// + /// @param[in] mangled + /// A name for the mangled name for this function. This value + /// can be empty if there is no mangled information. + /// + /// @param[in] decl_ptr + /// Optional declaration information that describes where the + /// function was declared. This can be NULL. + /// + /// @param[in] call_decl_ptr + /// Optional calling location declaration information that + /// describes from where this inlined function was called. + //------------------------------------------------------------------ + InlineFunctionInfo(const ConstString& name, const Mangled &mangled, const Declaration *decl_ptr, const Declaration *call_decl_ptr); + + //------------------------------------------------------------------ + /// Destructor. + //------------------------------------------------------------------ + ~InlineFunctionInfo(); + + //------------------------------------------------------------------ + /// Compare two inlined function information objects. + /// + /// First compares the FunctionInfo objects, and if equal, + /// compares the mangled names. + /// + /// @param[in] lhs + /// The Left Hand Side const InlineFunctionInfo object + /// reference. + /// + /// @param[in] rhs + /// The Right Hand Side const InlineFunctionInfo object + /// reference. + /// + /// @return + /// @li -1 if lhs < rhs + /// @li 0 if lhs == rhs + /// @li 1 if lhs > rhs + //------------------------------------------------------------------ + int + Compare(const InlineFunctionInfo& lhs, const InlineFunctionInfo& rhs); + + //------------------------------------------------------------------ + /// Dump a description of this object to a Stream. + /// + /// Dump a description of the contents of this object to the + /// supplied stream \a s. + /// + /// @param[in] s + /// The stream to which to dump the object descripton. + //------------------------------------------------------------------ + void + Dump(Stream *s, bool show_fullpaths) const; + + void + DumpStopContext (Stream *s) const; + + const ConstString & + GetName () const; + + //------------------------------------------------------------------ + /// Get accessor for the call site declaration information. + /// + /// @return + /// A reference to the declaration object. + //------------------------------------------------------------------ + Declaration& + GetCallSite (); + + //------------------------------------------------------------------ + /// Get const accessor for the call site declaration information. + /// + /// @return + /// A const reference to the declaration object. + //------------------------------------------------------------------ + const Declaration& + GetCallSite () const; + + //------------------------------------------------------------------ + /// Get accessor for the mangled name object. + /// + /// @return + /// A reference to the mangled name object. + //------------------------------------------------------------------ + Mangled& + GetMangled(); + + //------------------------------------------------------------------ + /// Get const accessor for the mangled name object. + /// + /// @return + /// A const reference to the mangled name object. + //------------------------------------------------------------------ + const Mangled& + GetMangled() const; + + //------------------------------------------------------------------ + /// Get the memory cost of this object. + /// + /// @return + /// The number of bytes that this object occupies in memory. + /// The returned value does not include the bytes for any + /// shared string values. + /// + /// @see ConstString::StaticMemorySize () + //------------------------------------------------------------------ + virtual size_t + MemorySize() const; + +private: + //------------------------------------------------------------------ + // Member variables. + //------------------------------------------------------------------ + Mangled m_mangled; ///< Mangled inlined function name (can be empty if there is no mangled information). + Declaration m_call_decl; +}; + +//---------------------------------------------------------------------- +/// @class Function Function.h "lldb/Symbol/Function.h" +/// @brief A class that describes a function. +/// +/// Functions belong to CompileUnit objects (Function::m_comp_unit), +/// have unique user IDs (Function::UserID), know how to reconstruct +/// their symbol context (Function::SymbolContextScope), have a +/// specific function type (Function::m_type_uid), have a simple +/// method name (FunctionInfo::m_name), be declared at a specific +/// location (FunctionInfo::m_declaration), possibly have mangled +/// names (Function::m_mangled), an optional return type +/// (Function::m_type), and contains lexical blocks +/// (Function::m_blocks). +/// +/// The function inforation is split into a few pieces: +/// @li The concrete instance information +/// @li The abstract information +/// +/// The abstract information is found in the function type (Type) that +/// describes a function information, return type and parameter types. +/// +/// The concreate information is the address range information and +/// specific locations for an instance of this function. +//---------------------------------------------------------------------- +class Function : + public UserID, + public SymbolContextScope +{ +public: + //------------------------------------------------------------------ + /// Construct with a compile unit, function UID, function type UID, + /// optional mangled name, function type, and a section offset + /// based address range. + /// + /// @param[in] comp_unit + /// The compile unit to which this function belongs. + /// + /// @param[in] func_uid + /// The UID for this function. This value is provided by the + /// SymbolFile plug-in and can be any value that allows + /// the plug-in to quickly find and parse more detailed + /// information when and if more information is needed. + /// + /// @param[in] func_type_uid + /// The type UID for the function Type to allow for lazy type + /// parsing from the debug information. + /// + /// @param[in] mangled + /// The optional mangled name for this function. If empty, there + /// is no mangled information. + /// + /// @param[in] func_type + /// The optional function type. If NULL, the function type will + /// be parsed on demand when accessed using the + /// Function::GetType() function by asking the SymbolFile + /// plug-in to get the type for \a func_type_uid. + /// + /// @param[in] range + /// The section offset based address for this function. + //------------------------------------------------------------------ + Function ( + CompileUnit *comp_unit, + lldb::user_id_t func_uid, + lldb::user_id_t func_type_uid, + const Mangled &mangled, + Type * func_type, + const AddressRange& range); + + //------------------------------------------------------------------ + /// Construct with a compile unit, function UID, function type UID, + /// optional mangled name, function type, and a section offset + /// based address range. + /// + /// @param[in] comp_unit + /// The compile unit to which this function belongs. + /// + /// @param[in] func_uid + /// The UID for this function. This value is provided by the + /// SymbolFile plug-in and can be any value that allows + /// the plug-in to quickly find and parse more detailed + /// information when and if more information is needed. + /// + /// @param[in] func_type_uid + /// The type UID for the function Type to allow for lazy type + /// parsing from the debug information. + /// + /// @param[in] mangled + /// The optional mangled name for this function. If empty, there + /// is no mangled information. + /// + /// @param[in] func_type + /// The optional function type. If NULL, the function type will + /// be parsed on demand when accessed using the + /// Function::GetType() function by asking the SymbolFile + /// plug-in to get the type for \a func_type_uid. + /// + /// @param[in] range + /// The section offset based address for this function. + //------------------------------------------------------------------ + Function ( + CompileUnit *comp_unit, + lldb::user_id_t func_uid, + lldb::user_id_t func_type_uid, + const char *mangled, + Type * func_type, + const AddressRange& range); + + //------------------------------------------------------------------ + /// Destructor. + //------------------------------------------------------------------ + ~Function (); + + //------------------------------------------------------------------ + /// @copydoc SymbolContextScope::CalculateSymbolContext(SymbolContext*) + /// + /// @see SymbolContextScope + //------------------------------------------------------------------ + virtual void + CalculateSymbolContext(SymbolContext* sc); + + virtual lldb::ModuleSP + CalculateSymbolContextModule (); + + virtual CompileUnit * + CalculateSymbolContextCompileUnit (); + + virtual Function * + CalculateSymbolContextFunction (); + + const AddressRange & + GetAddressRange() + { + return m_range; + } + + //------------------------------------------------------------------ + /// Find the file and line number of the source location of the start + /// of the function. This will use the declaration if present and fall + /// back on the line table if that fails. So there may NOT be a line + /// table entry for this source file/line combo. + /// + /// @param[out] source_file + /// The source file. + /// + /// @param[out] line_no + /// The line number. + //------------------------------------------------------------------ + void + GetStartLineSourceInfo (FileSpec &source_file, uint32_t &line_no); + + //------------------------------------------------------------------ + /// Find the file and line number of the source location of the end + /// of the function. + /// + /// + /// @param[out] source_file + /// The source file. + /// + /// @param[out] line_no + /// The line number. + //------------------------------------------------------------------ + void + GetEndLineSourceInfo (FileSpec &source_file, uint32_t &line_no); + + //------------------------------------------------------------------ + /// Get accessor for the block list. + /// + /// @return + /// The block list object that describes all lexical blocks + /// in the function. + /// + /// @see BlockList + //------------------------------------------------------------------ + Block& + GetBlock (bool can_create); + + //------------------------------------------------------------------ + /// Get accessor for the compile unit that owns this function. + /// + /// @return + /// A compile unit object pointer. + //------------------------------------------------------------------ + CompileUnit* + GetCompileUnit(); + + //------------------------------------------------------------------ + /// Get const accessor for the compile unit that owns this function. + /// + /// @return + /// A const compile unit object pointer. + //------------------------------------------------------------------ + const CompileUnit* + GetCompileUnit() const; + + void + GetDescription(Stream *s, lldb::DescriptionLevel level, Target *target); + + //------------------------------------------------------------------ + /// Get accessor for the frame base location. + /// + /// @return + /// A location expression that describes the function frame + /// base. + //------------------------------------------------------------------ + DWARFExpression & + GetFrameBaseExpression() + { + return m_frame_base; + } + + //------------------------------------------------------------------ + /// Get const accessor for the frame base location. + /// + /// @return + /// A const compile unit object pointer. + //------------------------------------------------------------------ + const DWARFExpression & + GetFrameBaseExpression() const + { + return m_frame_base; + } + + const ConstString & + GetName() const + { + return m_mangled.GetName(); + } + + const Mangled & + GetMangled() const + { + return m_mangled; + } + + //------------------------------------------------------------------ + /// Get the DeclContext for this function, if available. + /// + /// @return + /// The DeclContext, or NULL if none exists. + //------------------------------------------------------------------ + clang::DeclContext * + GetClangDeclContext(); + + //------------------------------------------------------------------ + /// Get accessor for the type that describes the function + /// return value type, and paramter types. + /// + /// @return + /// A type object pointer. + //------------------------------------------------------------------ + Type* + GetType(); + + //------------------------------------------------------------------ + /// Get const accessor for the type that describes the function + /// return value type, and paramter types. + /// + /// @return + /// A const type object pointer. + //------------------------------------------------------------------ + const Type* + GetType() const; + + ClangASTType + GetClangType (); + + uint32_t + GetPrologueByteSize (); + + //------------------------------------------------------------------ + /// Dump a description of this object to a Stream. + /// + /// Dump a description of the contents of this object to the + /// supplied stream \a s. + /// + /// @param[in] s + /// The stream to which to dump the object descripton. + /// + /// @param[in] show_context + /// If \b true, variables will dump their symbol context + /// information. + //------------------------------------------------------------------ + void + Dump(Stream *s, bool show_context) const; + + //------------------------------------------------------------------ + /// @copydoc SymbolContextScope::DumpSymbolContext(Stream*) + /// + /// @see SymbolContextScope + //------------------------------------------------------------------ + virtual void + DumpSymbolContext(Stream *s); + + //------------------------------------------------------------------ + /// Get the memory cost of this object. + /// + /// @return + /// The number of bytes that this object occupies in memory. + /// The returned value does not include the bytes for any + /// shared string values. + /// + /// @see ConstString::StaticMemorySize () + //------------------------------------------------------------------ + size_t + MemorySize () const; + +protected: + + enum + { + flagsCalculatedPrologueSize = (1 << 0) ///< Have we already tried to calculate the prologue size? + }; + + + + //------------------------------------------------------------------ + // Member variables. + //------------------------------------------------------------------ + CompileUnit *m_comp_unit; ///< The compile unit that owns this function. + lldb::user_id_t m_type_uid; ///< The user ID of for the prototype Type for this function. + Type * m_type; ///< The function prototype type for this function that include the function info (FunctionInfo), return type and parameters. + Mangled m_mangled; ///< The mangled function name if any, if empty, there is no mangled information. + Block m_block; ///< All lexical blocks contained in this function. + AddressRange m_range; ///< The function address range that covers the widest range needed to contain all blocks + DWARFExpression m_frame_base; ///< The frame base expression for variables that are relative to the frame pointer. + Flags m_flags; + uint32_t m_prologue_byte_size; ///< Compute the prologue size once and cache it +private: + DISALLOW_COPY_AND_ASSIGN(Function); +}; + +} // namespace lldb_private + +#endif // liblldb_Function_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Symbol/LineEntry.h b/contrib/llvm/tools/lldb/include/lldb/Symbol/LineEntry.h new file mode 100644 index 00000000000..d7750cd3491 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Symbol/LineEntry.h @@ -0,0 +1,174 @@ +//===-- LineEntry.h ---------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_LineEntry_h_ +#define liblldb_LineEntry_h_ + +#include "lldb/lldb-private.h" +#include "lldb/Core/AddressRange.h" +#include "lldb/Host/FileSpec.h" + +namespace lldb_private { + +//---------------------------------------------------------------------- +/// @class LineEntry LineEntry.h "lldb/Symbol/LineEntry.h" +/// @brief A line table entry class. +//---------------------------------------------------------------------- +struct LineEntry +{ + //------------------------------------------------------------------ + /// Default constructor. + /// + /// Initialize all member variables to invalid values. + //------------------------------------------------------------------ + LineEntry (); + + LineEntry + ( + const lldb::SectionSP §ion_sp, + lldb::addr_t section_offset, + lldb::addr_t byte_size, + const FileSpec &file, + uint32_t _line, + uint16_t _column, + bool _is_start_of_statement, + bool _is_start_of_basic_block, + bool _is_prologue_end, + bool _is_epilogue_begin, + bool _is_terminal_entry + ); + + //------------------------------------------------------------------ + /// Clear the object's state. + /// + /// Clears all member variables to invalid values. + //------------------------------------------------------------------ + void + Clear (); + + //------------------------------------------------------------------ + /// Dump a description of this object to a Stream. + /// + /// Dump a description of the contents of this object to the + /// supplied stream \a s. + /// + /// @param[in] s + /// The stream to which to dump the object descripton. + /// + /// @param[in] comp_unit + /// The compile unit object that contains the support file + /// list so the line entry can dump the file name (since this + /// object contains a file index into the support file list). + /// + /// @param[in] show_file + /// If \b true, display the filename with the line entry which + /// requires that the compile unit object \a comp_unit be a + /// valid pointer. + /// + /// @param[in] style + /// The display style for the section offset address. + /// + /// @return + /// Returns \b true if the address was able to be displayed + /// using \a style. File and load addresses may be unresolved + /// and it may not be possible to display a valid address value. + /// Returns \b false if the address was not able to be properly + /// dumped. + /// + /// @see Address::DumpStyle + //------------------------------------------------------------------ + bool + Dump (Stream *s, Target *target, bool show_file, Address::DumpStyle style, Address::DumpStyle fallback_style, bool show_range) const; + + bool + GetDescription (Stream *s, + lldb::DescriptionLevel level, + CompileUnit* cu, + Target *target, + bool show_address_only) const; + + //------------------------------------------------------------------ + /// Dumps information specific to a process that stops at this + /// line entry to the supplied stream \a s. + /// + /// @param[in] s + /// The stream to which to dump the object descripton. + /// + /// @param[in] comp_unit + /// The compile unit object that contains the support file + /// list so the line entry can dump the file name (since this + /// object contains a file index into the support file list). + /// + /// @return + /// Returns \b true if the file and line were properly dumped, + /// \b false otherwise. + //------------------------------------------------------------------ + bool + DumpStopContext (Stream *s, bool show_fullpaths) const; + + //------------------------------------------------------------------ + /// Check if a line entry object is valid. + /// + /// @return + /// Returns \b true if the line entry contains a valid section + /// offset address, file index, and line number, \b false + /// otherwise. + //------------------------------------------------------------------ + bool + IsValid () const; + + //------------------------------------------------------------------ + /// Compare two LineEntry objects. + /// + /// @param[in] lhs + /// The Left Hand Side const LineEntry object reference. + /// + /// @param[in] rhs + /// The Right Hand Side const LineEntry object reference. + /// + /// @return + /// @li -1 if lhs < rhs + /// @li 0 if lhs == rhs + /// @li 1 if lhs > rhs + //------------------------------------------------------------------ + static int + Compare (const LineEntry& lhs, const LineEntry& rhs); + + + //------------------------------------------------------------------ + // Member variables. + //------------------------------------------------------------------ + AddressRange range; ///< The section offset address range for this line entry. + FileSpec file; + uint32_t line; ///< The source line number, or zero if there is no line number information. + uint16_t column; ///< The column number of the source line, or zero if there is no column information. + uint16_t is_start_of_statement:1, ///< Indicates this entry is the beginning of a statement. + is_start_of_basic_block:1, ///< Indicates this entry is the beginning of a basic block. + is_prologue_end:1, ///< Indicates this entry is one (of possibly many) where execution should be suspended for an entry breakpoint of a function. + is_epilogue_begin:1, ///< Indicates this entry is one (of possibly many) where execution should be suspended for an exit breakpoint of a function. + is_terminal_entry:1; ///< Indicates this entry is that of the first byte after the end of a sequence of target machine instructions. +}; + +//------------------------------------------------------------------ +/// Less than operator. +/// +/// @param[in] lhs +/// The Left Hand Side const LineEntry object reference. +/// +/// @param[in] rhs +/// The Right Hand Side const LineEntry object reference. +/// +/// @return +/// Returns \b true if lhs < rhs, false otherwise. +//------------------------------------------------------------------ +bool operator<(const LineEntry& lhs, const LineEntry& rhs); + +} // namespace lldb_private + +#endif // liblldb_LineEntry_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Symbol/LineTable.h b/contrib/llvm/tools/lldb/include/lldb/Symbol/LineTable.h new file mode 100644 index 00000000000..477c8455ded --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Symbol/LineTable.h @@ -0,0 +1,422 @@ +//===-- LineTable.h ---------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_LineTable_h_ +#define liblldb_LineTable_h_ + +#include + +#include "lldb/lldb-private.h" +#include "lldb/Symbol/LineEntry.h" +#include "lldb/Core/ModuleChild.h" +#include "lldb/Core/Section.h" +#include "lldb/Core/RangeMap.h" + +namespace lldb_private { + +//---------------------------------------------------------------------- +/// @class LineSequence LineTable.h "lldb/Symbol/LineTable.h" +/// @brief An abstract base class used during symbol table creation. +//---------------------------------------------------------------------- +class LineSequence +{ +public: + LineSequence (); + + virtual + ~LineSequence() {} + + virtual void + Clear() = 0; + +private: + DISALLOW_COPY_AND_ASSIGN (LineSequence); +}; + +//---------------------------------------------------------------------- +/// @class LineTable LineTable.h "lldb/Symbol/LineTable.h" +/// @brief A line table class. +//---------------------------------------------------------------------- +class LineTable +{ +public: + //------------------------------------------------------------------ + /// Construct with compile unit. + /// + /// @param[in] comp_unit + /// The compile unit to which this line table belongs. + //------------------------------------------------------------------ + LineTable (CompileUnit* comp_unit); + + //------------------------------------------------------------------ + /// Destructor. + //------------------------------------------------------------------ + ~LineTable (); + + //------------------------------------------------------------------ + /// Adds a new line entry to this line table. + /// + /// All line entries are maintained in file address order. + /// + /// @param[in] line_entry + /// A const reference to a new line_entry to add to this line + /// table. + /// + /// @see Address::DumpStyle + //------------------------------------------------------------------ +// void +// AddLineEntry (const LineEntry& line_entry); + + // Called when you can't guarantee the addresses are in increasing order + void + InsertLineEntry (lldb::addr_t file_addr, + uint32_t line, + uint16_t column, + uint16_t file_idx, + bool is_start_of_statement, + bool is_start_of_basic_block, + bool is_prologue_end, + bool is_epilogue_begin, + bool is_terminal_entry); + + // Used to instantiate the LineSequence helper classw + LineSequence* + CreateLineSequenceContainer (); + + // Append an entry to a caller-provided collection that will later be + // inserted in this line table. + void + AppendLineEntryToSequence (LineSequence* sequence, + lldb::addr_t file_addr, + uint32_t line, + uint16_t column, + uint16_t file_idx, + bool is_start_of_statement, + bool is_start_of_basic_block, + bool is_prologue_end, + bool is_epilogue_begin, + bool is_terminal_entry); + + // Insert a sequence of entries into this line table. + void + InsertSequence (LineSequence* sequence); + + //------------------------------------------------------------------ + /// Dump all line entries in this line table to the stream \a s. + /// + /// @param[in] s + /// The stream to which to dump the object descripton. + /// + /// @param[in] style + /// The display style for the address. + /// + /// @see Address::DumpStyle + //------------------------------------------------------------------ + void + Dump (Stream *s, Target *target, + Address::DumpStyle style, + Address::DumpStyle fallback_style, + bool show_line_ranges); + + void + GetDescription (Stream *s, + Target *target, + lldb::DescriptionLevel level); + + //------------------------------------------------------------------ + /// Find a line entry that contains the section offset address \a + /// so_addr. + /// + /// @param[in] so_addr + /// A section offset address object containing the address we + /// are searching for. + /// + /// @param[out] line_entry + /// A copy of the line entry that was found if \b true is + /// returned, otherwise \a entry is left unmodified. + /// + /// @param[out] index_ptr + /// A pointer to a 32 bit integer that will get the actual line + /// entry index if it is not NULL. + /// + /// @return + /// Returns \b true if \a so_addr is contained in a line entry + /// in this line table, \b false otherwise. + //------------------------------------------------------------------ + bool + FindLineEntryByAddress (const Address &so_addr, LineEntry& line_entry, uint32_t *index_ptr = NULL); + + //------------------------------------------------------------------ + /// Find a line entry index that has a matching file index and + /// source line number. + /// + /// Finds the next line entry that has a matching \a file_idx and + /// source line number \a line starting at the \a start_idx entries + /// into the line entry collection. + /// + /// @param[in] start_idx + /// The number of entries to skip when starting the search. + /// + /// @param[out] file_idx + /// The file index to search for that should be found prior + /// to calling this function using the following functions: + /// CompileUnit::GetSupportFiles() + /// FileSpecList::FindFileIndex (uint32_t, const FileSpec &) const + /// + /// @param[in] line + /// The source line to match. + /// + /// @param[in] exact + /// If true, match only if you find a line entry exactly matching \a line. + /// If false, return the closest line entry greater than \a line. + /// + /// @param[out] line_entry + /// A reference to a line entry object that will get a copy of + /// the line entry if \b true is returned, otherwise \a + /// line_entry is left untouched. + /// + /// @return + /// Returns \b true if a matching line entry is found in this + /// line table, \b false otherwise. + /// + /// @see CompileUnit::GetSupportFiles() + /// @see FileSpecList::FindFileIndex (uint32_t, const FileSpec &) const + //------------------------------------------------------------------ + uint32_t + FindLineEntryIndexByFileIndex (uint32_t start_idx, + uint32_t file_idx, + uint32_t line, + bool exact, + LineEntry* line_entry_ptr); + + uint32_t + FindLineEntryIndexByFileIndex (uint32_t start_idx, + const std::vector &file_indexes, + uint32_t line, + bool exact, + LineEntry* line_entry_ptr); + + size_t + FineLineEntriesForFileIndex (uint32_t file_idx, + bool append, + SymbolContextList &sc_list); + + //------------------------------------------------------------------ + /// Get the line entry from the line table at index \a idx. + /// + /// @param[in] idx + /// An index into the line table entry collection. + /// + /// @return + /// A valid line entry if \a idx is a valid index, or an invalid + /// line entry if \a idx is not valid. + /// + /// @see LineTable::GetSize() + /// @see LineEntry::IsValid() const + //------------------------------------------------------------------ + bool + GetLineEntryAtIndex(uint32_t idx, LineEntry& line_entry); + + //------------------------------------------------------------------ + /// Gets the size of the line table in number of line table entries. + /// + /// @return + /// The number of line table entries in this line table. + //------------------------------------------------------------------ + uint32_t + GetSize () const; + + typedef lldb_private::RangeArray FileAddressRanges; + + //------------------------------------------------------------------ + /// Gets all contiguous file address ranges for the entire line table. + /// + /// @param[out] file_ranges + /// A collection of file address ranges that will be filled in + /// by this function. + /// + /// @param[out] append + /// If \b true, then append to \a file_ranges, otherwise clear + /// \a file_ranges prior to adding any ranges. + /// + /// @return + /// The number of address ranges added to \a file_ranges + //------------------------------------------------------------------ + size_t + GetContiguousFileAddressRanges (FileAddressRanges &file_ranges, bool append); + + //------------------------------------------------------------------ + /// Given a file range link map, relink the current line table + /// and return a fixed up line table. + /// + /// @param[out] file_range_map + /// A collection of file ranges that maps to new file ranges + /// that will be used when linking the line table. + /// + /// @return + /// A new line table if at least one line table entry was able + /// to be mapped. + //------------------------------------------------------------------ + typedef RangeDataVector FileRangeMap; + + LineTable * + LinkLineTable (const FileRangeMap &file_range_map); + +protected: + + struct Entry + { + Entry () : + file_addr (LLDB_INVALID_ADDRESS), + line (0), + column (0), + file_idx (0), + is_start_of_statement (false), + is_start_of_basic_block (false), + is_prologue_end (false), + is_epilogue_begin (false), + is_terminal_entry (false) + { + } + + Entry ( lldb::addr_t _file_addr, + uint32_t _line, + uint16_t _column, + uint16_t _file_idx, + bool _is_start_of_statement, + bool _is_start_of_basic_block, + bool _is_prologue_end, + bool _is_epilogue_begin, + bool _is_terminal_entry) : + file_addr (_file_addr), + line (_line), + column (_column), + file_idx (_file_idx), + is_start_of_statement (_is_start_of_statement), + is_start_of_basic_block (_is_start_of_basic_block), + is_prologue_end (_is_prologue_end), + is_epilogue_begin (_is_epilogue_begin), + is_terminal_entry (_is_terminal_entry) + { + } + + int + bsearch_compare (const void *key, const void *arrmem); + + void + Clear () + { + file_addr = LLDB_INVALID_ADDRESS; + line = 0; + column = 0; + file_idx = 0; + is_start_of_statement = false; + is_start_of_basic_block = false; + is_prologue_end = false; + is_epilogue_begin = false; + is_terminal_entry = false; + } + + static int + Compare (const Entry& lhs, const Entry& rhs) + { + // Compare the sections before calling + #define SCALAR_COMPARE(a,b) if (a < b) return -1; if (a > b) return +1 + SCALAR_COMPARE (lhs.file_addr, rhs.file_addr); + SCALAR_COMPARE (lhs.line, rhs.line); + SCALAR_COMPARE (lhs.column, rhs.column); + SCALAR_COMPARE (lhs.is_start_of_statement, rhs.is_start_of_statement); + SCALAR_COMPARE (lhs.is_start_of_basic_block, rhs.is_start_of_basic_block); + // rhs and lhs reversed on purpose below. + SCALAR_COMPARE (rhs.is_prologue_end, lhs.is_prologue_end); + SCALAR_COMPARE (lhs.is_epilogue_begin, rhs.is_epilogue_begin); + // rhs and lhs reversed on purpose below. + SCALAR_COMPARE (rhs.is_terminal_entry, lhs.is_terminal_entry); + SCALAR_COMPARE (lhs.file_idx, rhs.file_idx); + #undef SCALAR_COMPARE + return 0; + } + + + class LessThanBinaryPredicate + { + public: + LessThanBinaryPredicate(LineTable *line_table); + bool operator() (const LineTable::Entry&, const LineTable::Entry&) const; + protected: + LineTable *m_line_table; + }; + + static bool EntryAddressLessThan (const Entry& lhs, const Entry& rhs) + { + return lhs.file_addr < rhs.file_addr; + } + + //------------------------------------------------------------------ + // Member variables. + //------------------------------------------------------------------ + lldb::addr_t file_addr; ///< The file address for this line entry + uint32_t line; ///< The source line number, or zero if there is no line number information. + uint16_t column; ///< The column number of the source line, or zero if there is no column information. + uint16_t file_idx:11, ///< The file index into CompileUnit's file table, or zero if there is no file information. + is_start_of_statement:1, ///< Indicates this entry is the beginning of a statement. + is_start_of_basic_block:1, ///< Indicates this entry is the beginning of a basic block. + is_prologue_end:1, ///< Indicates this entry is one (of possibly many) where execution should be suspended for an entry breakpoint of a function. + is_epilogue_begin:1, ///< Indicates this entry is one (of possibly many) where execution should be suspended for an exit breakpoint of a function. + is_terminal_entry:1; ///< Indicates this entry is that of the first byte after the end of a sequence of target machine instructions. + }; + + struct EntrySearchInfo + { + LineTable* line_table; + lldb_private::Section *a_section; + Entry *a_entry; + }; + + //------------------------------------------------------------------ + // Types + //------------------------------------------------------------------ + typedef std::vector section_collection; ///< The collection type for the sections. + typedef std::vector entry_collection; ///< The collection type for the line entries. + //------------------------------------------------------------------ + // Member variables. + //------------------------------------------------------------------ + CompileUnit* m_comp_unit; ///< The compile unit that this line table belongs to. + entry_collection m_entries; ///< The collection of line entries in this line table. + + //------------------------------------------------------------------ + // Helper class + //------------------------------------------------------------------ + class LineSequenceImpl : public LineSequence + { + public: + LineSequenceImpl() : + LineSequence() + {} + + virtual + ~LineSequenceImpl() + {} + + virtual void + Clear(); + + entry_collection m_entries; ///< The collection of line entries in this sequence. + }; + + bool + ConvertEntryAtIndexToLineEntry (uint32_t idx, LineEntry &line_entry); + +private: + DISALLOW_COPY_AND_ASSIGN (LineTable); +}; + +} // namespace lldb_private + +#endif // liblldb_LineTable_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Symbol/ObjectContainer.h b/contrib/llvm/tools/lldb/include/lldb/Symbol/ObjectContainer.h new file mode 100644 index 00000000000..7fb68624505 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Symbol/ObjectContainer.h @@ -0,0 +1,236 @@ +//===-- ObjectContainer.h ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ObjectContainer_h_ +#define liblldb_ObjectContainer_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes + +#include "lldb/lldb-private.h" +#include "lldb/Core/DataExtractor.h" +#include "lldb/Host/FileSpec.h" +#include "lldb/Core/ModuleChild.h" +#include "lldb/Core/PluginInterface.h" +#include "lldb/Host/Endian.h" + +namespace lldb_private { + +//---------------------------------------------------------------------- +/// @class ObjectContainer ObjectContainer.h "lldb/Symbol/ObjectContainer.h" +/// @brief A plug-in interface definition class for object containers. +/// +/// Object containers contain object files from one or more +/// architectures, and also can contain one or more named objects. +/// +/// Typical object containers are static libraries (.a files) that +/// contain multiple named object files, and universal files that contain +/// multiple architectures. +//---------------------------------------------------------------------- +class ObjectContainer : + public PluginInterface, + public ModuleChild +{ +public: + //------------------------------------------------------------------ + /// Construct with a parent module, offset, and header data. + /// + /// Object files belong to modules and a valid module must be + /// supplied upon construction. The at an offset within a file for + /// objects that contain more than one architecture or object. + //------------------------------------------------------------------ + ObjectContainer (const lldb::ModuleSP &module_sp, + const FileSpec *file, + lldb::offset_t file_offset, + lldb::offset_t length, + lldb::DataBufferSP& data_sp, + lldb::offset_t data_offset) : + ModuleChild (module_sp), + m_file (), // This file can be different than the module's file spec + m_offset (file_offset), + m_length (length), + m_data () + { + if (file) + m_file = *file; + if (data_sp) + m_data.SetData (data_sp, data_offset, length); + } + + //------------------------------------------------------------------ + /// Destructor. + /// + /// The destructor is virtual since this class is designed to be + /// inherited from by the plug-in instance. + //------------------------------------------------------------------ + virtual + ~ObjectContainer() + { + } + + //------------------------------------------------------------------ + /// Dump a description of this object to a Stream. + /// + /// Dump a description of the current contents of this object + /// to the supplied stream \a s. The dumping should include the + /// section list if it has been parsed, and the symbol table + /// if it has been parsed. + /// + /// @param[in] s + /// The stream to which to dump the object descripton. + //------------------------------------------------------------------ + virtual void + Dump (Stream *s) const = 0; + + //------------------------------------------------------------------ + /// Gets the architecture given an index. + /// + /// Copies the architecture specification for index \a idx. + /// + /// @param[in] idx + /// The architecture index to extract. + /// + /// @param[out] arch + /// A architecture object that will be filled in if \a idx is a + /// architecture valid index. + /// + /// @return + /// Returns \b true if \a idx is valid and \a arch has been + /// filled in, \b false otherwise. + /// + /// @see ObjectContainer::GetNumArchitectures() const + //------------------------------------------------------------------ + virtual bool + GetArchitectureAtIndex (uint32_t idx, ArchSpec& arch) const + { + return false; + } + + //------------------------------------------------------------------ + /// Returns the offset into a file at which this object resides. + /// + /// Some files contain many object files, and this function allows + /// access to an object's offset within the file. + /// + /// @return + /// The offset in bytes into the file. Defaults to zero for + /// simple object files that a represented by an entire file. + //------------------------------------------------------------------ + virtual lldb::addr_t + GetOffset () const + { return m_offset; } + + virtual lldb::addr_t + GetByteSize () const + { return m_length; } + + //------------------------------------------------------------------ + /// Get the number of objects within this object file (archives). + /// + /// @return + /// Zero for object files that are not archives, or the number + /// of objects contained in the archive. + //------------------------------------------------------------------ + virtual size_t + GetNumObjects () const + { return 0; } + + //------------------------------------------------------------------ + /// Get the number of architectures in this object file. + /// + /// The default implementation returns 1 as for object files that + /// contain a single architecture. ObjectContainer instances that + /// contain more than one architecture should override this function + /// and return an appropriate value. + /// + /// @return + /// The number of architectures contained in this object file. + //------------------------------------------------------------------ + virtual size_t + GetNumArchitectures () const + { return 0; } + + //------------------------------------------------------------------ + /// Attempts to parse the object header. + /// + /// This function is used as a test to see if a given plug-in + /// instance can parse the header data already contained in + /// ObjectContainer::m_data. If an object file parser does not + /// recognize that magic bytes in a header, false should be returned + /// and the next plug-in can attempt to parse an object file. + /// + /// @return + /// Returns \b true if the header was parsed succesfully, \b + /// false otherwise. + //------------------------------------------------------------------ + virtual bool + ParseHeader () = 0; + + //------------------------------------------------------------------ + /// Selects an architecture in an object file. + /// + /// Object files that contain a single architecture should verify + /// that the specified \a arch matches the architecture in in + /// object file and return \b true or \b false accordingly. + /// + /// Object files that contain more than one architecture should + /// attempt to select that architecture, and if successful, clear + /// out any previous state from any previously selected architecture + /// and prepare to return information for the new architecture. + /// + /// @return + /// Returns a pointer to the object file of the requested \a + /// arch and optional \a name. Returns NULL of no such object + /// file exists in the container. + //------------------------------------------------------------------ + virtual lldb::ObjectFileSP + GetObjectFile (const FileSpec *file) = 0; + + virtual bool + ObjectAtIndexIsContainer (uint32_t object_idx) + { + return false; + } + + virtual ObjectFile * + GetObjectFileAtIndex (uint32_t object_idx) + { + return NULL; + } + + virtual ObjectContainer * + GetObjectContainerAtIndex (uint32_t object_idx) + { + return NULL; + } + + virtual const char * + GetObjectNameAtIndex (uint32_t object_idx) const + { + return NULL; + } + +protected: + //------------------------------------------------------------------ + // Member variables. + //------------------------------------------------------------------ + FileSpec m_file; ///< The file that represents this container objects (which can be different from the module's file). + lldb::addr_t m_offset; ///< The offset in bytes into the file, or the address in memory + lldb::addr_t m_length; ///< The size in bytes if known (can be zero). + DataExtractor m_data; ///< The data for this object file so things can be parsed lazily. + +private: + DISALLOW_COPY_AND_ASSIGN (ObjectContainer); +}; + +} // namespace lldb_private + +#endif // liblldb_ObjectContainer_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Symbol/ObjectFile.h b/contrib/llvm/tools/lldb/include/lldb/Symbol/ObjectFile.h new file mode 100644 index 00000000000..8934c31bb98 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Symbol/ObjectFile.h @@ -0,0 +1,706 @@ +//===-- ObjectFile.h --------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ObjectFile_h_ +#define liblldb_ObjectFile_h_ + +#include "lldb/lldb-private.h" +#include "lldb/Core/DataExtractor.h" +#include "lldb/Host/FileSpec.h" +#include "lldb/Core/FileSpecList.h" +#include "lldb/Core/ModuleChild.h" +#include "lldb/Core/PluginInterface.h" +#include "lldb/Host/Endian.h" +#include "lldb/Symbol/Symtab.h" +#include "lldb/Symbol/UnwindTable.h" + +namespace lldb_private { + +//---------------------------------------------------------------------- +/// @class ObjectFile ObjectFile.h "lldb/Symbol/ObjectFile.h" +/// @brief A plug-in interface definition class for object file parsers. +/// +/// Object files belong to Module objects and know how to extract +/// information from executable, shared library, and object (.o) files +/// used by operating system runtime. The symbol table and section list +/// for an object file. +/// +/// Object files can be represented by the entire file, or by part of a +/// file. Examples of object files that are part of a file include +/// object files that contain information for multiple architectures in +/// the same file, or archive files that contain multiple objects +/// (ranlib archives) (possibly for multiple architectures as well). +/// +/// Object archive files (e.g. ranlib archives) can contain +/// multiple .o (object) files that must be selected by index or by name. +/// The number of objects that an ObjectFile contains can be determined +/// using the ObjectFile::GetNumObjects() const +/// function, and followed by a call to +/// ObjectFile::SelectObjectAtIndex (uint32_t) to change the currently +/// selected object. Objects can also be selected by name using the +/// ObjectFile::SelectObject(const char *) function. +/// +/// Once an architecture is selected (and an object is selected for +/// for archives), the object file information can be extracted from +/// this abstract class. +//---------------------------------------------------------------------- +class ObjectFile: + public std::enable_shared_from_this, + public PluginInterface, + public ModuleChild +{ +friend class lldb_private::Module; + +public: + typedef enum + { + eTypeInvalid = 0, + eTypeCoreFile, /// A core file that has a checkpoint of a program's execution state + eTypeExecutable, /// A normal executable + eTypeDebugInfo, /// An object file that contains only debug information + eTypeDynamicLinker, /// The platform's dynamic linker executable + eTypeObjectFile, /// An intermediate object file + eTypeSharedLibrary, /// A shared library that can be used during execution + eTypeStubLibrary, /// A library that can be linked against but not used for execution + eTypeUnknown + } Type; + + typedef enum + { + eStrataInvalid = 0, + eStrataUnknown, + eStrataUser, + eStrataKernel, + eStrataRawImage + } Strata; + + //------------------------------------------------------------------ + /// Construct with a parent module, offset, and header data. + /// + /// Object files belong to modules and a valid module must be + /// supplied upon construction. The at an offset within a file for + /// objects that contain more than one architecture or object. + //------------------------------------------------------------------ + ObjectFile (const lldb::ModuleSP &module_sp, + const FileSpec *file_spec_ptr, + lldb::offset_t file_offset, + lldb::offset_t length, + lldb::DataBufferSP& data_sp, + lldb::offset_t data_offset); + + ObjectFile (const lldb::ModuleSP &module_sp, + const lldb::ProcessSP &process_sp, + lldb::addr_t header_addr, + lldb::DataBufferSP& data_sp); + + //------------------------------------------------------------------ + /// Destructor. + /// + /// The destructor is virtual since this class is designed to be + /// inherited from by the plug-in instance. + //------------------------------------------------------------------ + virtual + ~ObjectFile(); + + //------------------------------------------------------------------ + /// Dump a description of this object to a Stream. + /// + /// Dump a description of the current contents of this object + /// to the supplied stream \a s. The dumping should include the + /// section list if it has been parsed, and the symbol table + /// if it has been parsed. + /// + /// @param[in] s + /// The stream to which to dump the object descripton. + //------------------------------------------------------------------ + virtual void + Dump (Stream *s) = 0; + + //------------------------------------------------------------------ + /// Find a ObjectFile plug-in that can parse \a file_spec. + /// + /// Scans all loaded plug-in interfaces that implement versions of + /// the ObjectFile plug-in interface and returns the first + /// instance that can parse the file. + /// + /// @param[in] module + /// The parent module that owns this object file. + /// + /// @param[in] file_spec + /// A file specification that indicates which file to use as the + /// object file. + /// + /// @param[in] file_offset + /// The offset into the file at which to start parsing the + /// object. This is for files that contain multiple + /// architectures or objects. + /// + /// @param[in] file_size + /// The size of the current object file if it can be determined + /// or if it is known. This can be zero. + /// + /// @see ObjectFile::ParseHeader() + //------------------------------------------------------------------ + static lldb::ObjectFileSP + FindPlugin (const lldb::ModuleSP &module_sp, + const FileSpec* file_spec, + lldb::offset_t file_offset, + lldb::offset_t file_size, + lldb::DataBufferSP &data_sp, + lldb::offset_t &data_offset); + + //------------------------------------------------------------------ + /// Find a ObjectFile plug-in that can parse a file in memory. + /// + /// Scans all loaded plug-in interfaces that implement versions of + /// the ObjectFile plug-in interface and returns the first + /// instance that can parse the file. + /// + /// @param[in] module + /// The parent module that owns this object file. + /// + /// @param[in] process_sp + /// A shared pointer to the process whose memory space contains + /// an object file. This will be stored as a std::weak_ptr. + /// + /// @param[in] header_addr + /// The address of the header for the object file in memory. + //------------------------------------------------------------------ + static lldb::ObjectFileSP + FindPlugin (const lldb::ModuleSP &module_sp, + const lldb::ProcessSP &process_sp, + lldb::addr_t header_addr, + lldb::DataBufferSP &file_data_sp); + + + static size_t + GetModuleSpecifications (const FileSpec &file, + lldb::offset_t file_offset, + lldb::offset_t file_size, + ModuleSpecList &specs); + + static size_t + GetModuleSpecifications (const lldb_private::FileSpec& file, + lldb::DataBufferSP& data_sp, + lldb::offset_t data_offset, + lldb::offset_t file_offset, + lldb::offset_t file_size, + lldb_private::ModuleSpecList &specs); + //------------------------------------------------------------------ + /// Split a path into a file path with object name. + /// + /// For paths like "/tmp/foo.a(bar.o)" we often need to split a path + /// up into the actual path name and into the object name so we can + /// make a valid object file from it. + /// + /// @param[in] path_with_object + /// A path that might contain an archive path with a .o file + /// specified in parens in the basename of the path. + /// + /// @param[out] archive_file + /// If \b true is returned, \a file_spec will be filled in with + /// the path to the archive. + /// + /// @param[out] archive_object + /// If \b true is returned, \a object will be filled in with + /// the name of the object inside the archive. + /// + /// @return + /// \b true if the path matches the pattern of archive + object + /// and \a archive_file and \a archive_object are modified, + /// \b false otherwise and \a archive_file and \a archive_object + /// are guaranteed to be remain unchanged. + //------------------------------------------------------------------ + static bool + SplitArchivePathWithObject (const char *path_with_object, + lldb_private::FileSpec &archive_file, + lldb_private::ConstString &archive_object, + bool must_exist); + + //------------------------------------------------------------------ + /// Gets the address size in bytes for the current object file. + /// + /// @return + /// The size of an address in bytes for the currently selected + /// architecture (and object for archives). Returns zero if no + /// architecture or object has been selected. + //------------------------------------------------------------------ + virtual uint32_t + GetAddressByteSize () const = 0; + + //------------------------------------------------------------------ + /// Get the address type given a file address in an object file. + /// + /// Many binary file formats know what kinds + /// This is primarily for ARM binaries, though it can be applied to + /// any executable file format that supports different opcode types + /// within the same binary. ARM binaries support having both ARM and + /// Thumb within the same executable container. We need to be able + /// to get + /// @return + /// The size of an address in bytes for the currently selected + /// architecture (and object for archives). Returns zero if no + /// architecture or object has been selected. + //------------------------------------------------------------------ + virtual lldb::AddressClass + GetAddressClass (lldb::addr_t file_addr); + + //------------------------------------------------------------------ + /// Extract the dependent modules from an object file. + /// + /// If an object file has information about which other images it + /// depends on (such as shared libraries), this function will + /// provide the list. Since many executables or shared libraries + /// may depend on the same files, + /// FileSpecList::AppendIfUnique(const FileSpec &) should be + /// used to make sure any files that are added are not already in + /// the list. + /// + /// @param[out] file_list + /// A list of file specification objects that gets dependent + /// files appended to. + /// + /// @return + /// The number of new files that were appended to \a file_list. + /// + /// @see FileSpecList::AppendIfUnique(const FileSpec &) + //------------------------------------------------------------------ + virtual uint32_t + GetDependentModules (FileSpecList& file_list) = 0; + + //------------------------------------------------------------------ + /// Tells whether this object file is capable of being the main executable + /// for a process. + /// + /// @return + /// \b true if it is, \b false otherwise. + //------------------------------------------------------------------ + virtual bool + IsExecutable () const = 0; + + //------------------------------------------------------------------ + /// Returns the offset into a file at which this object resides. + /// + /// Some files contain many object files, and this function allows + /// access to an object's offset within the file. + /// + /// @return + /// The offset in bytes into the file. Defaults to zero for + /// simple object files that a represented by an entire file. + //------------------------------------------------------------------ + virtual lldb::addr_t + GetFileOffset () const + { return m_file_offset; } + + virtual lldb::addr_t + GetByteSize () const + { return m_length; } + + //------------------------------------------------------------------ + /// Get accessor to the object file specification. + /// + /// @return + /// The file specification object pointer if there is one, or + /// NULL if this object is only from memory. + //------------------------------------------------------------------ + virtual FileSpec& + GetFileSpec() { return m_file; } + + //------------------------------------------------------------------ + /// Get const accessor to the object file specification. + /// + /// @return + /// The const file specification object pointer if there is one, + /// or NULL if this object is only from memory. + //------------------------------------------------------------------ + virtual const FileSpec& + GetFileSpec() const { return m_file; } + + //------------------------------------------------------------------ + /// Get the name of the cpu, vendor and OS for this object file. + /// + /// This value is a string that represents the target triple where + /// the cpu type, the vendor and the OS are encoded into a string. + /// + /// @param[out] target_triple + /// The string value of the target triple. + /// + /// @return + /// \b True if the target triple was able to be computed, \b + /// false otherwise. + //------------------------------------------------------------------ + virtual bool + GetArchitecture (ArchSpec &arch) = 0; + + //------------------------------------------------------------------ + /// Gets the section list for the currently selected architecture + /// (and object for archives). + /// + /// Section list parsing can be deferred by ObjectFile instances + /// until this accessor is called the first time. + /// + /// @return + /// The list of sections contained in this object file. + //------------------------------------------------------------------ + virtual SectionList * + GetSectionList (); + + virtual void + CreateSections (SectionList &unified_section_list) = 0; + + //------------------------------------------------------------------ + /// Gets the symbol table for the currently selected architecture + /// (and object for archives). + /// + /// Symbol table parsing can be deferred by ObjectFile instances + /// until this accessor is called the first time. + /// + /// @return + /// The symbol table for this object file. + //------------------------------------------------------------------ + virtual Symtab * + GetSymtab () = 0; + + //------------------------------------------------------------------ + /// Detect if this object file has been stripped of local symbols. + /// + /// @return + /// Return \b true if the object file has been stripped of local + /// symbols. + //------------------------------------------------------------------ + virtual bool + IsStripped () = 0; + + //------------------------------------------------------------------ + /// Frees the symbol table. + /// + /// This function should only be used when an object file is + /// + /// @param[in] flags + /// eSymtabFromUnifiedSectionList: Whether to clear symbol table + /// for unified module section list, or object file. + /// + /// @return + /// The symbol table for this object file. + //------------------------------------------------------------------ + virtual void + ClearSymtab (); + + //------------------------------------------------------------------ + /// Gets the UUID for this object file. + /// + /// If the object file format contains a UUID, the value should be + /// returned. Else ObjectFile instances should return the MD5 + /// checksum of all of the bytes for the object file (or memory for + /// memory based object files). + /// + /// @return + /// Returns \b true if a UUID was successfully extracted into + /// \a uuid, \b false otherwise. + //------------------------------------------------------------------ + virtual bool + GetUUID (lldb_private::UUID* uuid) = 0; + + //------------------------------------------------------------------ + /// Gets the symbol file spec list for this object file. + /// + /// If the object file format contains a debug symbol file link, + /// the values will be return in the FileSpecList. + /// + /// @return + /// Returns filespeclist. + //------------------------------------------------------------------ + virtual lldb_private::FileSpecList + GetDebugSymbolFilePaths() + { + return FileSpecList(); + } + + //------------------------------------------------------------------ + /// Gets whether endian swapping should occur when extracting data + /// from this object file. + /// + /// @return + /// Returns \b true if endian swapping is needed, \b false + /// otherwise. + //------------------------------------------------------------------ + virtual lldb::ByteOrder + GetByteOrder () const = 0; + + //------------------------------------------------------------------ + /// Attempts to parse the object header. + /// + /// This function is used as a test to see if a given plug-in + /// instance can parse the header data already contained in + /// ObjectFile::m_data. If an object file parser does not + /// recognize that magic bytes in a header, false should be returned + /// and the next plug-in can attempt to parse an object file. + /// + /// @return + /// Returns \b true if the header was parsed succesfully, \b + /// false otherwise. + //------------------------------------------------------------------ + virtual bool + ParseHeader () = 0; + + //------------------------------------------------------------------ + /// Returns a reference to the UnwindTable for this ObjectFile + /// + /// The UnwindTable contains FuncUnwinders objects for any function in + /// this ObjectFile. If a FuncUnwinders object hasn't been created yet + /// (i.e. the function has yet to be unwound in a stack walk), it + /// will be created when requested. Specifically, we do not create + /// FuncUnwinders objects for functions until they are needed. + /// + /// @return + /// Returns the unwind table for this object file. + //------------------------------------------------------------------ + virtual lldb_private::UnwindTable& + GetUnwindTable () { return m_unwind_table; } + + //------------------------------------------------------------------ + /// Similar to Process::GetImageInfoAddress(). + /// + /// Some platforms embed auxiliary structures useful to debuggers in the + /// address space of the inferior process. This method returns the address + /// of such a structure if the information can be resolved via entries in + /// the object file. ELF, for example, provides a means to hook into the + /// runtime linker so that a debugger may monitor the loading and unloading + /// of shared libraries. + /// + /// @return + /// The address of any auxiliary tables, or an invalid address if this + /// object file format does not support or contain such information. + virtual lldb_private::Address + GetImageInfoAddress () { return Address(); } + + //------------------------------------------------------------------ + /// Returns the address of the Entry Point in this object file - if + /// the object file doesn't have an entry point (because it is not an + /// executable file) then an invalid address is returned. + /// + /// @return + /// Returns the entry address for this module. + //------------------------------------------------------------------ + virtual lldb_private::Address + GetEntryPointAddress () { return Address();} + + //------------------------------------------------------------------ + /// Returns the address that represents the header of this object + /// file. + /// + /// The header address is defined as where the header for the object + /// file is that describes the content of the file. If the header + /// doesn't appear in a section that is defined in the object file, + /// an address with no section is returned that has the file offset + /// set in the m_file_offset member of the lldb_private::Address object. + /// + /// @return + /// Returns the entry address for this module. + //------------------------------------------------------------------ + virtual lldb_private::Address + GetHeaderAddress () { return Address(m_memory_addr);} + + + virtual uint32_t + GetNumThreadContexts () + { + return 0; + } + + virtual lldb::RegisterContextSP + GetThreadContextAtIndex (uint32_t idx, lldb_private::Thread &thread) + { + return lldb::RegisterContextSP(); + } + + //------------------------------------------------------------------ + /// The object file should be able to calculate its type by looking + /// at its file header and possibly the sections or other data in + /// the object file. The file type is used in the debugger to help + /// select the correct plug-ins for the job at hand, so this is + /// important to get right. If any eTypeXXX definitions do not match + /// up with the type of file you are loading, please feel free to + /// add a new enumeration value. + /// + /// @return + /// The calculated file type for the current object file. + //------------------------------------------------------------------ + virtual Type + CalculateType() = 0; + + //------------------------------------------------------------------ + /// In cases where the type can't be calculated (elf files), this + /// routine allows someone to explicitly set it. As an example, + /// SymbolVendorELF uses this routine to set eTypeDebugInfo when + /// loading debug link files. + virtual void + SetType (Type type) + { + m_type = type; + } + + //------------------------------------------------------------------ + /// The object file should be able to calculate the strata of the + /// object file. + /// + /// Many object files for platforms might be for either user space + /// debugging or for kernel debugging. If your object file subclass + /// can figure this out, it will help with debugger plug-in selection + /// when it comes time to debug. + /// + /// @return + /// The calculated object file strata for the current object + /// file. + //------------------------------------------------------------------ + virtual Strata + CalculateStrata() = 0; + + //------------------------------------------------------------------ + /// Get the object file version numbers. + /// + /// Many object files have a set of version numbers that describe + /// the version of the executable or shared library. Typically there + /// are major, minor and build, but there may be more. This function + /// will extract the versions from object files if they are available. + /// + /// If \a versions is NULL, or if \a num_versions is 0, the return + /// value will indicate how many version numbers are available in + /// this object file. Then a subsequent call can be made to this + /// function with a value of \a versions and \a num_versions that + /// has enough storage to store some or all version numbers. + /// + /// @param[out] versions + /// A pointer to an array of uint32_t types that is \a num_versions + /// long. If this value is NULL, the return value will indicate + /// how many version numbers are required for a subsequent call + /// to this function so that all versions can be retrieved. If + /// the value is non-NULL, then at most \a num_versions of the + /// existing versions numbers will be filled into \a versions. + /// If there is no version information available, \a versions + /// will be filled with \a num_versions UINT32_MAX values + /// and zero will be returned. + /// + /// @param[in] num_versions + /// The maximum number of entries to fill into \a versions. If + /// this value is zero, then the return value will indicate + /// how many version numbers there are in total so another call + /// to this function can be make with adequate storage in + /// \a versions to get all of the version numbers. If \a + /// num_versions is less than the actual number of version + /// numbers in this object file, only \a num_versions will be + /// filled into \a versions (if \a versions is non-NULL). + /// + /// @return + /// This function always returns the number of version numbers + /// that this object file has regardless of the number of + /// version numbers that were copied into \a versions. + //------------------------------------------------------------------ + virtual uint32_t + GetVersion (uint32_t *versions, uint32_t num_versions) + { + if (versions && num_versions) + { + for (uint32_t i=0; i m_sections_ap; + std::unique_ptr m_symtab_ap; + + //------------------------------------------------------------------ + /// Sets the architecture for a module. At present the architecture + /// can only be set if it is invalid. It is not allowed to switch from + /// one concrete architecture to another. + /// + /// @param[in] new_arch + /// The architecture this module will be set to. + /// + /// @return + /// Returns \b true if the architecture was changed, \b + /// false otherwise. + //------------------------------------------------------------------ + bool SetModulesArchitecture (const ArchSpec &new_arch); + +private: + DISALLOW_COPY_AND_ASSIGN (ObjectFile); +}; + +} // namespace lldb_private + +#endif // liblldb_ObjectFile_h_ + diff --git a/contrib/llvm/tools/lldb/include/lldb/Symbol/Symbol.h b/contrib/llvm/tools/lldb/include/lldb/Symbol/Symbol.h new file mode 100644 index 00000000000..11c1cc7af9a --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Symbol/Symbol.h @@ -0,0 +1,317 @@ +//===-- Symbol.h ------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_Symbol_h_ +#define liblldb_Symbol_h_ + +#include "lldb/lldb-private.h" +#include "lldb/Core/AddressRange.h" +#include "lldb/Core/Mangled.h" +#include "lldb/Core/UserID.h" +#include "lldb/Symbol/SymbolContextScope.h" + +namespace lldb_private { + +class Symbol : + public SymbolContextScope +{ +public: + // ObjectFile readers can classify their symbol table entries and searches can be made + // on specific types where the symbol values will have drastically different meanings + // and sorting requirements. + Symbol(); + + Symbol (uint32_t symID, + const char *name, + bool name_is_mangled, + lldb::SymbolType type, + bool external, + bool is_debug, + bool is_trampoline, + bool is_artificial, + const lldb::SectionSP §ion_sp, + lldb::addr_t value, + lldb::addr_t size, + bool size_is_valid, + uint32_t flags); + + Symbol (uint32_t symID, + const char *name, + bool name_is_mangled, + lldb::SymbolType type, + bool external, + bool is_debug, + bool is_trampoline, + bool is_artificial, + const AddressRange &range, + bool size_is_valid, + uint32_t flags); + + Symbol (const Symbol& rhs); + + const Symbol& + operator= (const Symbol& rhs); + + void + Clear(); + + bool + Compare (const ConstString& name, lldb::SymbolType type) const; + + void + Dump (Stream *s, Target *target, uint32_t index) const; + + bool + ValueIsAddress() const; + + //------------------------------------------------------------------ + // Access the address value. Do NOT hand out the AddressRange as an + // object as the byte size of the address range may not be filled in + // and it should be accessed via GetByteSize(). + //------------------------------------------------------------------ + Address & + GetAddress() + { + return m_addr_range.GetBaseAddress(); + } + + //------------------------------------------------------------------ + // Access the address value. Do NOT hand out the AddressRange as an + // object as the byte size of the address range may not be filled in + // and it should be accessed via GetByteSize(). + //------------------------------------------------------------------ + const Address & + GetAddress() const + { + return m_addr_range.GetBaseAddress(); + } + + const ConstString & + GetName () const + { + return m_mangled.GetName(); + } + + uint32_t + GetID() const + { + return m_uid; + } + + void + SetID(uint32_t uid) + { + m_uid = uid; + } + + Mangled& + GetMangled () + { + return m_mangled; + } + + const Mangled& + GetMangled () const + { + return m_mangled; + } + + uint32_t + GetSiblingIndex () const; + + lldb::SymbolType + GetType () const + { + return (lldb::SymbolType)m_type; + } + + void + SetType (lldb::SymbolType type) + { + m_type = (lldb::SymbolType)type; + } + + const char * + GetTypeAsString () const; + + uint32_t + GetFlags () const + { + return m_flags; + } + + void + SetFlags (uint32_t flags) + { + m_flags = flags; + } + + void + GetDescription (Stream *s, lldb::DescriptionLevel level, Target *target) const; + + bool + IsSynthetic () const + { + return m_is_synthetic; + } + + void + SetIsSynthetic (bool b) + { + m_is_synthetic = b; + } + + + bool + GetSizeIsSynthesized() const + { + return m_size_is_synthesized; + } + + void + SetSizeIsSynthesized(bool b) + { + m_size_is_synthesized = b; + } + + bool + IsDebug () const + { + return m_is_debug; + } + + void + SetDebug (bool b) + { + m_is_debug = b; + } + + bool + IsExternal () const + { + return m_is_external; + } + + void + SetExternal (bool b) + { + m_is_external = b; + } + + bool + IsTrampoline () const; + + bool + IsIndirect () const; + + bool + GetByteSizeIsValid () const + { + return m_size_is_valid; + } + + lldb::addr_t + GetByteSize () const; + + void + SetByteSize (lldb::addr_t size) + { + m_size_is_valid = size > 0; + m_addr_range.SetByteSize(size); + } + + bool + GetSizeIsSibling () const + { + return m_size_is_sibling; + } + + void + SetSizeIsSibling (bool b) + { + m_size_is_sibling = b; + } + +// void +// SetValue (Address &value) +// { +// m_addr_range.GetBaseAddress() = value; +// } +// +// void +// SetValue (const AddressRange &range) +// { +// m_addr_range = range; +// } +// +// void +// SetValue (lldb::addr_t value); +// { +// m_addr_range.GetBaseAddress().SetRawAddress(value); +// } + + // If m_type is "Code" or "Function" then this will return the prologue size + // in bytes, else it will return zero. + uint32_t + GetPrologueByteSize (); + + bool + GetDemangledNameIsSynthesized() const + { + return m_demangled_is_synthesized; + } + void + SetDemangledNameIsSynthesized(bool b) + { + m_demangled_is_synthesized = b; + } + + //------------------------------------------------------------------ + /// @copydoc SymbolContextScope::CalculateSymbolContext(SymbolContext*) + /// + /// @see SymbolContextScope + //------------------------------------------------------------------ + virtual void + CalculateSymbolContext (SymbolContext *sc); + + virtual lldb::ModuleSP + CalculateSymbolContextModule (); + + virtual Symbol * + CalculateSymbolContextSymbol (); + + //------------------------------------------------------------------ + /// @copydoc SymbolContextScope::DumpSymbolContext(Stream*) + /// + /// @see SymbolContextScope + //------------------------------------------------------------------ + virtual void + DumpSymbolContext (Stream *s); + +protected: + + uint32_t m_uid; // User ID (usually the original symbol table index) + uint16_t m_type_data; // data specific to m_type + uint16_t m_type_data_resolved:1, // True if the data in m_type_data has already been calculated + m_is_synthetic:1, // non-zero if this symbol is not actually in the symbol table, but synthesized from other info in the object file. + m_is_debug:1, // non-zero if this symbol is debug information in a symbol + m_is_external:1, // non-zero if this symbol is globally visible + m_size_is_sibling:1, // m_size contains the index of this symbol's sibling + m_size_is_synthesized:1,// non-zero if this symbol's size was calculated using a delta between this symbol and the next + m_size_is_valid:1, + m_demangled_is_synthesized:1, // The demangled name was created should not be used for expressions or other lookups + m_type:8; + Mangled m_mangled; // uniqued symbol name/mangled name pair + AddressRange m_addr_range; // Contains the value, or the section offset address when the value is an address in a section, and the size (if any) + uint32_t m_flags; // A copy of the flags from the original symbol table, the ObjectFile plug-in can interpret these +}; + +} // namespace lldb_private + +#endif // liblldb_Symbol_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Symbol/SymbolContext.h b/contrib/llvm/tools/lldb/include/lldb/Symbol/SymbolContext.h new file mode 100644 index 00000000000..5b12adebf5f --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Symbol/SymbolContext.h @@ -0,0 +1,566 @@ +//===-- SymbolContext.h -----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + + +#ifndef liblldb_SymbolContext_h_ +#define liblldb_SymbolContext_h_ + +#include + +#include "lldb/lldb-private.h" +#include "lldb/Core/Address.h" +#include "lldb/Core/Mangled.h" +#include "lldb/Symbol/ClangASTType.h" +#include "lldb/Symbol/LineEntry.h" + +namespace lldb_private { + +class SymbolContextScope; +//---------------------------------------------------------------------- +/// @class SymbolContext SymbolContext.h "lldb/Symbol/SymbolContext.h" +/// @brief Defines a symbol context baton that can be handed other debug +/// core functions. +/// +/// Many debugger functions require a context when doing lookups. This +/// class provides a common structure that can be used as the result +/// of a query that can contain a single result. Examples of such +/// queries include +/// @li Looking up a load address. +//---------------------------------------------------------------------- +class SymbolContext +{ +public: + + //------------------------------------------------------------------ + /// Default constructor. + /// + /// Initialize all pointer members to NULL and all struct members + /// to their default state. + //------------------------------------------------------------------ + SymbolContext (); + + //------------------------------------------------------------------ + /// Construct with an object that knows how to reconstruct its + /// symbol context. + /// + /// @param[in] sc_scope + /// A symbol context scope object that knows how to reconstruct + /// it's context. + //------------------------------------------------------------------ + explicit + SymbolContext (SymbolContextScope *sc_scope); + + //------------------------------------------------------------------ + /// Construct with module, and optional compile unit, function, + /// block, line table, line entry and symbol. + /// + /// Initialize all pointer to the specified values. + /// + /// @param[in] module + /// A Module pointer to the module for this context. + /// + /// @param[in] comp_unit + /// A CompileUnit pointer to the compile unit for this context. + /// + /// @param[in] function + /// A Function pointer to the function for this context. + /// + /// @param[in] block + /// A Block pointer to the deepest block for this context. + /// + /// @param[in] line_entry + /// A LineEntry pointer to the line entry for this context. + /// + /// @param[in] symbol + /// A Symbol pointer to the symbol for this context. + //------------------------------------------------------------------ + explicit + SymbolContext (const lldb::TargetSP &target_sp, + const lldb::ModuleSP &module_sp, + CompileUnit *comp_unit = NULL, + Function *function = NULL, + Block *block = NULL, + LineEntry *line_entry = NULL, + Symbol *symbol = NULL); + + // This version sets the target to a NULL TargetSP if you don't know it. + explicit + SymbolContext (const lldb::ModuleSP &module_sp, + CompileUnit *comp_unit = NULL, + Function *function = NULL, + Block *block = NULL, + LineEntry *line_entry = NULL, + Symbol *symbol = NULL); + + ~SymbolContext (); + //------------------------------------------------------------------ + /// Copy constructor + /// + /// Makes a copy of the another SymbolContext object \a rhs. + /// + /// @param[in] rhs + /// A const SymbolContext object reference to copy. + //------------------------------------------------------------------ + SymbolContext (const SymbolContext& rhs); + + //------------------------------------------------------------------ + /// Assignment operator. + /// + /// Copies the address value from another SymbolContext object \a + /// rhs into \a this object. + /// + /// @param[in] rhs + /// A const SymbolContext object reference to copy. + /// + /// @return + /// A const SymbolContext object reference to \a this. + //------------------------------------------------------------------ + const SymbolContext& + operator= (const SymbolContext& rhs); + + //------------------------------------------------------------------ + /// Clear the object's state. + /// + /// Resets all pointer members to NULL, and clears any class objects + /// to their default state. + //------------------------------------------------------------------ + void + Clear (bool clear_target); + + //------------------------------------------------------------------ + /// Dump a description of this object to a Stream. + /// + /// Dump a description of the contents of this object to the + /// supplied stream \a s. + /// + /// @param[in] s + /// The stream to which to dump the object descripton. + //------------------------------------------------------------------ + void + Dump (Stream *s, Target *target) const; + + //------------------------------------------------------------------ + /// Dump the stop context in this object to a Stream. + /// + /// Dump the best description of this object to the stream. The + /// information displayed depends on the amount and quality of the + /// information in this context. If a module, function, file and + /// line number are available, they will be dumped. If only a + /// module and function or symbol name with offset is available, + /// that will be output. Else just the address at which the target + /// was stopped will be displayed. + /// + /// @param[in] s + /// The stream to which to dump the object descripton. + /// + /// @param[in] so_addr + /// The resolved section offset address. + //------------------------------------------------------------------ + bool + DumpStopContext (Stream *s, + ExecutionContextScope *exe_scope, + const Address &so_addr, + bool show_fullpaths, + bool show_module, + bool show_inlined_frames) const; + + //------------------------------------------------------------------ + /// Get the address range contained within a symbol context. + /// + /// Address range priority is as follows: + /// - line_entry address range if line_entry is valid and eSymbolContextLineEntry is set in \a scope + /// - block address range if block is not NULL and eSymbolContextBlock is set in \a scope + /// - function address range if function is not NULL and eSymbolContextFunction is set in \a scope + /// - symbol address range if symbol is not NULL and eSymbolContextSymbol is set in \a scope + /// + /// @param[in] scope + /// A mask of symbol context bits telling this function which + /// address ranges it can use when trying to extract one from + /// the valid (non-NULL) symbol context classes. + /// + /// @param[in] range_idx + /// The address range index to grab. Since many functions and + /// blocks are not always contiguous, they may have more than + /// one address range. + /// + /// @param[in] use_inline_block_range + /// If \a scope has the eSymbolContextBlock bit set, and there + /// is a valid block in the symbol context, return the block + /// address range for the containing inline function block, not + /// the deepest most block. This allows us to extract information + /// for the address range of the inlined function block, not + /// the deepest lexical block. + /// + /// @param[out] range + /// An address range object that will be filled in if \b true + /// is returned. + /// + /// @return + /// \b True if this symbol context contains items that describe + /// an address range, \b false otherwise. + //------------------------------------------------------------------ + bool + GetAddressRange (uint32_t scope, + uint32_t range_idx, + bool use_inline_block_range, + AddressRange &range) const; + + + void + GetDescription(Stream *s, + lldb::DescriptionLevel level, + Target *target) const; + + uint32_t + GetResolvedMask () const; + + + //------------------------------------------------------------------ + /// Find a block that defines the function represented by this + /// symbol context. + /// + /// If this symbol context points to a block that is an inlined + /// function, or is contained within an inlined function, the block + /// that defines the inlined function is returned. + /// + /// If this symbol context has no block in it, or the block is not + /// itself an inlined function block or contained within one, we + /// return the top level function block. + /// + /// This is a handy function to call when you want to get the block + /// whose variable list will include the arguments for the function + /// that is represented by this symbol context (whether the function + /// is an inline function or not). + /// + /// @return + /// The block object pointer that defines the function that is + /// represented by this symbol context object, NULL otherwise. + //------------------------------------------------------------------ + Block * + GetFunctionBlock (); + + + //------------------------------------------------------------------ + /// If this symbol context represents a function that is a method, + /// return true and provide information about the method. + /// + /// @param[out] language + /// If \b true is returned, the language for the method. + /// + /// @param[out] is_instance_method + /// If \b true is returned, \b true if this is a instance method, + /// \b false if this is a static/class function. + /// + /// @param[out] language_object_name + /// If \b true is returned, the name of the artificial variable + /// for the language ("this" for C++, "self" for ObjC). + /// + /// @return + /// \b True if this symbol context represents a function that + /// is a method of a class, \b false otherwise. + //------------------------------------------------------------------ + bool + GetFunctionMethodInfo (lldb::LanguageType &language, + bool &is_instance_method, + ConstString &language_object_name); + + //------------------------------------------------------------------ + /// Find a name of the innermost function for the symbol context. + /// + /// For instance, if the symbol context contains an inlined block, + /// it will return the inlined function name. + /// + /// @param[in] prefer_mangled + /// if \btrue, then the mangled name will be returned if there + /// is one. Otherwise the unmangled name will be returned if it + /// is available. + /// + /// @return + /// The name of the function represented by this symbol context. + //------------------------------------------------------------------ + ConstString + GetFunctionName (Mangled::NamePreference preference = Mangled::ePreferDemangled) const; + + + //------------------------------------------------------------------ + /// Get the line entry that corresponds to the function. + /// + /// If the symbol context contains an inlined block, the line entry + /// for the start address of the inlined function will be returned, + /// otherwise the line entry for the start address of the function + /// will be returned. This can be used after doing a + /// Module::FindFunctions(...) or ModuleList::FindFunctions(...) + /// call in order to get the correct line table information for + /// the symbol context. + /// it will return the inlined function name. + /// + /// @param[in] prefer_mangled + /// if \btrue, then the mangled name will be returned if there + /// is one. Otherwise the unmangled name will be returned if it + /// is available. + /// + /// @return + /// The name of the function represented by this symbol context. + //------------------------------------------------------------------ + LineEntry + GetFunctionStartLineEntry () const; + + //------------------------------------------------------------------ + /// Find the block containing the inlined block that contains this block. + /// + /// For instance, if the symbol context contains an inlined block, + /// it will return the inlined function name. + /// + /// @param[in] curr_frame_pc + /// The address within the block of this object. + /// + /// @param[out] next_frame_sc + /// A new symbol context that does what the title says it does. + /// + /// @param[out] next_frame_addr + /// This is what you should report as the PC in \a next_frame_sc. + /// + /// @return + /// \b true if this SymbolContext specifies a block contained in an + /// inlined block. If this returns \b true, \a next_frame_sc and + /// \a next_frame_addr will be filled in correctly. + //------------------------------------------------------------------ + bool + GetParentOfInlinedScope (const Address &curr_frame_pc, + SymbolContext &next_frame_sc, + Address &inlined_frame_addr) const; + + //------------------------------------------------------------------ + // Member variables + //------------------------------------------------------------------ + lldb::TargetSP target_sp; ///< The Target for a given query + lldb::ModuleSP module_sp; ///< The Module for a given query + CompileUnit * comp_unit; ///< The CompileUnit for a given query + Function * function; ///< The Function for a given query + Block * block; ///< The Block for a given query + LineEntry line_entry; ///< The LineEntry for a given query + Symbol * symbol; ///< The Symbol for a given query +}; + + +class SymbolContextSpecifier +{ +public: + typedef enum SpecificationType + { + eNothingSpecified = 0, + eModuleSpecified = 1 << 0, + eFileSpecified = 1 << 1, + eLineStartSpecified = 1 << 2, + eLineEndSpecified = 1 << 3, + eFunctionSpecified = 1 << 4, + eClassOrNamespaceSpecified = 1 << 5, + eAddressRangeSpecified = 1 << 6 + } SpecificationType; + + // This one produces a specifier that matches everything... + SymbolContextSpecifier (const lldb::TargetSP& target_sp); + + ~SymbolContextSpecifier(); + + bool + AddSpecification (const char *spec_string, SpecificationType type); + + bool + AddLineSpecification (uint32_t line_no, SpecificationType type); + + void + Clear(); + + bool + SymbolContextMatches(SymbolContext &sc); + + bool + AddressMatches(lldb::addr_t addr); + + void + GetDescription (Stream *s, lldb::DescriptionLevel level) const; + +private: + lldb::TargetSP m_target_sp; + std::string m_module_spec; + lldb::ModuleSP m_module_sp; + std::unique_ptr m_file_spec_ap; + size_t m_start_line; + size_t m_end_line; + std::string m_function_spec; + std::string m_class_name; + std::unique_ptr m_address_range_ap; + uint32_t m_type; // Or'ed bits from SpecificationType + +}; + +//---------------------------------------------------------------------- +/// @class SymbolContextList SymbolContext.h "lldb/Symbol/SymbolContext.h" +/// @brief Defines a list of symbol context objects. +/// +/// This class provides a common structure that can be used to contain +/// the result of a query that can contain a multiple results. Examples +/// of such queries include: +/// @li Looking up a function by name. +/// @li Finding all addressses for a specified file and line number. +//---------------------------------------------------------------------- +class SymbolContextList +{ +public: + //------------------------------------------------------------------ + /// Default constructor. + /// + /// Initialize with an empty list. + //------------------------------------------------------------------ + SymbolContextList (); + + //------------------------------------------------------------------ + /// Destructor. + //------------------------------------------------------------------ + ~SymbolContextList (); + + //------------------------------------------------------------------ + /// Append a new symbol context to the list. + /// + /// @param[in] sc + /// A symbol context to append to the list. + //------------------------------------------------------------------ + void + Append (const SymbolContext& sc); + + void + Append (const SymbolContextList& sc_list); + + bool + AppendIfUnique (const SymbolContext& sc, + bool merge_symbol_into_function); + + bool + MergeSymbolContextIntoFunctionContext (const SymbolContext& symbol_sc, + uint32_t start_idx = 0, + uint32_t stop_idx = UINT32_MAX); + + uint32_t + AppendIfUnique (const SymbolContextList& sc_list, + bool merge_symbol_into_function); + //------------------------------------------------------------------ + /// Clear the object's state. + /// + /// Clears the symbol context list. + //------------------------------------------------------------------ + void + Clear(); + + //------------------------------------------------------------------ + /// Dump a description of this object to a Stream. + /// + /// Dump a description of the contents of each symbol context in + /// the list to the supplied stream \a s. + /// + /// @param[in] s + /// The stream to which to dump the object descripton. + //------------------------------------------------------------------ + void + Dump(Stream *s, Target *target) const; + + //------------------------------------------------------------------ + /// Get accessor for a symbol context at index \a idx. + /// + /// Dump a description of the contents of each symbol context in + /// the list to the supplied stream \a s. + /// + /// @param[in] idx + /// The zero based index into the symbol context list. + /// + /// @param[out] sc + /// A reference to the symbol context to fill in. + /// + /// @return + /// Returns \b true if \a idx was a valid index into this + /// symbol context list and \a sc was filled in, \b false + /// otherwise. + //------------------------------------------------------------------ + bool + GetContextAtIndex(size_t idx, SymbolContext& sc) const; + + //------------------------------------------------------------------ + /// Direct reference accessor for a symbol context at index \a idx. + /// + /// The index \a idx must be a valid index, no error checking will + /// be done to ensure that it is valid. + /// + /// @param[in] idx + /// The zero based index into the symbol context list. + /// + /// @return + /// A const reference to the symbol context to fill in. + //------------------------------------------------------------------ + SymbolContext& + operator [] (size_t idx) + { + return m_symbol_contexts[idx]; + } + + const SymbolContext& + operator [] (size_t idx) const + { + return m_symbol_contexts[idx]; + } + + //------------------------------------------------------------------ + /// Get accessor for the last symbol context in the list. + /// + /// @param[out] sc + /// A reference to the symbol context to fill in. + /// + /// @return + /// Returns \b true if \a sc was filled in, \b false if the + /// list is empty. + //------------------------------------------------------------------ + bool + GetLastContext(SymbolContext& sc) const; + + bool + RemoveContextAtIndex (size_t idx); + //------------------------------------------------------------------ + /// Get accessor for a symbol context list size. + /// + /// @return + /// Returns the number of symbol context objects in the list. + //------------------------------------------------------------------ + uint32_t + GetSize() const; + + uint32_t + NumLineEntriesWithLine (uint32_t line) const; + + void + GetDescription(Stream *s, + lldb::DescriptionLevel level, + Target *target) const; + +protected: + typedef std::vector collection; ///< The collection type for the list. + + //------------------------------------------------------------------ + // Member variables. + //------------------------------------------------------------------ + collection m_symbol_contexts; ///< The list of symbol contexts. +}; + +bool operator== (const SymbolContext& lhs, const SymbolContext& rhs); +bool operator!= (const SymbolContext& lhs, const SymbolContext& rhs); + +bool operator== (const SymbolContextList& lhs, const SymbolContextList& rhs); +bool operator!= (const SymbolContextList& lhs, const SymbolContextList& rhs); + +} // namespace lldb_private + +#endif // liblldb_SymbolContext_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Symbol/SymbolContextScope.h b/contrib/llvm/tools/lldb/include/lldb/Symbol/SymbolContextScope.h new file mode 100644 index 00000000000..693cc0131e2 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Symbol/SymbolContextScope.h @@ -0,0 +1,137 @@ +//===-- SymbolContextScope.h ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_SymbolContextScope_h_ +#define liblldb_SymbolContextScope_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private.h" + +namespace lldb_private { + +//---------------------------------------------------------------------- +/// @class SymbolContextScope SymbolContextScope.h "lldb/Symbol/SymbolContextScope.h" +/// @brief Inherit from this if your object is part of a symbol context +/// and can reconstruct its symbol context. +/// +/// Many objects that are part of a symbol context that have pointers +/// back to parent objects that own them. Any members of a symbol +/// context that, once they are built, will not go away, can inherit +/// from this pure virtual class and can then reconstruct their symbol +/// context without having to keep a complete SymbolContext object in +/// the object. +/// +/// Examples of these objects include: +/// @li Module +/// @li CompileUnit +/// @li Function +/// @li Block +/// @li Symbol +/// +/// Other objects can store a "SymbolContextScope *" using any pointers +/// to one of the above objects. This allows clients to hold onto a +/// pointer that uniquely will identify a symbol context. Those clients +/// can then always reconstruct the symbol context using the pointer, or +/// use it to uniquely identify a symbol context for an object. +/// +/// Example objects include that currently use "SymbolContextScope *" +/// objects include: +/// @li Variable objects that can reconstruct where they are scoped +/// by making sure the SymbolContextScope * comes from the scope +/// in which the variable was declared. If a variable is a global, +/// the appropriate CompileUnit * will be used when creating the +/// variable. A static function variables, can the Block scope +/// in which the variable is defined. Function arguments can use +/// the Function object as their scope. The SymbolFile parsers +/// will set these correctly as the variables are parsed. +/// @li Type objects that know exactly in which scope they +/// originated much like the variables above. +/// @li StackID objects that are able to know that if the CFA +/// (stack pointer at the beginning of a function) and the +/// start PC for the function/symbol and the SymbolContextScope +/// pointer (a unique pointer that identifies a symbol context +/// location) match within the same thread, that the stack +/// frame is the same as the previous stack frame. +/// +/// Objects that adhere to this protocol can reconstruct enough of a +/// symbol context to allow functions that take a symbol context to be +/// called. Lists can also be created using a SymbolContextScope* and +/// and object pairs that allow large collections of objects to be +/// passed around with minimal overhead. +//---------------------------------------------------------------------- +class SymbolContextScope +{ +public: + virtual + ~SymbolContextScope () {} + + //------------------------------------------------------------------ + /// Reconstruct the object's symbolc context into \a sc. + /// + /// The object should fill in as much of the SymbolContext as it + /// can so function calls that require a symbol context can be made + /// for the given object. + /// + /// @param[out] sc + /// A symbol context object pointer that gets filled in. + //------------------------------------------------------------------ + virtual void + CalculateSymbolContext (SymbolContext *sc) = 0; + + + virtual lldb::ModuleSP + CalculateSymbolContextModule () + { + return lldb::ModuleSP(); + } + + virtual CompileUnit * + CalculateSymbolContextCompileUnit () + { + return NULL; + } + + virtual Function * + CalculateSymbolContextFunction () + { + return NULL; + } + + virtual Block * + CalculateSymbolContextBlock () + { + return NULL; + } + + virtual Symbol * + CalculateSymbolContextSymbol () + { + return NULL; + } + + //------------------------------------------------------------------ + /// Dump the object's symbolc context to the stream \a s. + /// + /// The object should dump its symbol context to the stream \a s. + /// This function is widely used in the DumpDebug and verbose output + /// for lldb objets. + /// + /// @param[in] s + /// The stream to which to dump the object's symbol context. + //------------------------------------------------------------------ + virtual void + DumpSymbolContext (Stream *s) = 0; +}; + +} // namespace lldb_private + +#endif // liblldb_SymbolContextScope_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Symbol/SymbolFile.h b/contrib/llvm/tools/lldb/include/lldb/Symbol/SymbolFile.h new file mode 100644 index 00000000000..5b774e3a7d1 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Symbol/SymbolFile.h @@ -0,0 +1,167 @@ +//===-- SymbolFile.h --------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_SymbolFile_h_ +#define liblldb_SymbolFile_h_ + +#include "lldb/lldb-private.h" +#include "lldb/Core/PluginInterface.h" +#include "lldb/Symbol/ClangASTType.h" +#include "lldb/Symbol/ClangNamespaceDecl.h" +#include "lldb/Symbol/Type.h" + +namespace lldb_private { + +class SymbolFile : + public PluginInterface +{ +public: + //------------------------------------------------------------------ + // Symbol file ability bits. + // + // Each symbol file can claim to support one or more symbol file + // abilities. These get returned from SymbolFile::GetAbilities(). + // These help us to determine which plug-in will be best to load + // the debug information found in files. + //------------------------------------------------------------------ + enum Abilities + { + CompileUnits = (1u << 0), + LineTables = (1u << 1), + Functions = (1u << 2), + Blocks = (1u << 3), + GlobalVariables = (1u << 4), + LocalVariables = (1u << 5), + VariableTypes = (1u << 6), + kAllAbilities =((1u << 7) - 1u) + }; + + static SymbolFile * + FindPlugin (ObjectFile* obj_file); + + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + SymbolFile(ObjectFile* obj_file) : + m_obj_file(obj_file), + m_abilities(0), + m_calculated_abilities(false) + { + } + + virtual + ~SymbolFile() + { + } + + //------------------------------------------------------------------ + /// Get a mask of what this symbol file supports for the object file + /// that it was constructed with. + /// + /// Each symbol file gets to respond with a mask of abilities that + /// it supports for each object file. This happens when we are + /// trying to figure out which symbol file plug-in will get used + /// for a given object file. The plug-in that resoonds with the + /// best mix of "SymbolFile::Abilities" bits set, will get chosen to + /// be the symbol file parser. This allows each plug-in to check for + /// sections that contain data a symbol file plug-in would need. For + /// example the DWARF plug-in requires DWARF sections in a file that + /// contain debug information. If the DWARF plug-in doesn't find + /// these sections, it won't respond with many ability bits set, and + /// we will probably fall back to the symbol table SymbolFile plug-in + /// which uses any information in the symbol table. Also, plug-ins + /// might check for some specific symbols in a symbol table in the + /// case where the symbol table contains debug information (STABS + /// and COFF). Not a lot of work should happen in these functions + /// as the plug-in might not get selected due to another plug-in + /// having more abilities. Any initialization work should be saved + /// for "void SymbolFile::InitializeObject()" which will get called + /// on the SymbolFile object with the best set of abilities. + /// + /// @return + /// A uint32_t mask containing bits from the SymbolFile::Abilities + /// enumeration. Any bits that are set represent an ability that + /// this symbol plug-in can parse from the object file. + ///------------------------------------------------------------------ + uint32_t GetAbilities () + { + if (!m_calculated_abilities) + { + m_abilities = CalculateAbilities(); + m_calculated_abilities = true; + } + + return m_abilities; + } + + virtual uint32_t CalculateAbilities() = 0; + + //------------------------------------------------------------------ + /// Initialize the SymbolFile object. + /// + /// The SymbolFile object with the best set of abilities (detected + /// in "uint32_t SymbolFile::GetAbilities()) will have this function + /// called if it is chosen to parse an object file. More complete + /// initialization can happen in this function which will get called + /// prior to any other functions in the SymbolFile protocol. + //------------------------------------------------------------------ + virtual void InitializeObject() {} + + //------------------------------------------------------------------ + // Compile Unit function calls + //------------------------------------------------------------------ + // Approach 1 - iterator + virtual uint32_t GetNumCompileUnits() = 0; + virtual lldb::CompUnitSP ParseCompileUnitAtIndex(uint32_t index) = 0; + + virtual lldb::LanguageType ParseCompileUnitLanguage (const SymbolContext& sc) = 0; + virtual size_t ParseCompileUnitFunctions (const SymbolContext& sc) = 0; + virtual bool ParseCompileUnitLineTable (const SymbolContext& sc) = 0; + virtual bool ParseCompileUnitSupportFiles (const SymbolContext& sc, FileSpecList& support_files) = 0; + virtual size_t ParseFunctionBlocks (const SymbolContext& sc) = 0; + virtual size_t ParseTypes (const SymbolContext& sc) = 0; + virtual size_t ParseVariablesForContext (const SymbolContext& sc) = 0; + virtual Type* ResolveTypeUID (lldb::user_id_t type_uid) = 0; + virtual bool ResolveClangOpaqueTypeDefinition (ClangASTType &clang_type) = 0; + virtual clang::DeclContext* GetClangDeclContextForTypeUID (const lldb_private::SymbolContext &sc, lldb::user_id_t type_uid) { return NULL; } + virtual clang::DeclContext* GetClangDeclContextContainingTypeUID (lldb::user_id_t type_uid) { return NULL; } + virtual uint32_t ResolveSymbolContext (const Address& so_addr, uint32_t resolve_scope, SymbolContext& sc) = 0; + virtual uint32_t ResolveSymbolContext (const FileSpec& file_spec, uint32_t line, bool check_inlines, uint32_t resolve_scope, SymbolContextList& sc_list) = 0; + virtual uint32_t FindGlobalVariables (const ConstString &name, const ClangNamespaceDecl *namespace_decl, bool append, uint32_t max_matches, VariableList& variables) = 0; + virtual uint32_t FindGlobalVariables (const RegularExpression& regex, bool append, uint32_t max_matches, VariableList& variables) = 0; + virtual uint32_t FindFunctions (const ConstString &name, const ClangNamespaceDecl *namespace_decl, uint32_t name_type_mask, bool include_inlines, bool append, SymbolContextList& sc_list) = 0; + virtual uint32_t FindFunctions (const RegularExpression& regex, bool include_inlines, bool append, SymbolContextList& sc_list) = 0; + virtual uint32_t FindTypes (const SymbolContext& sc, const ConstString &name, const ClangNamespaceDecl *namespace_decl, bool append, uint32_t max_matches, TypeList& types) = 0; +// virtual uint32_t FindTypes (const SymbolContext& sc, const RegularExpression& regex, bool append, uint32_t max_matches, TypeList& types) = 0; + virtual TypeList * GetTypeList (); + virtual size_t GetTypes (lldb_private::SymbolContextScope *sc_scope, + uint32_t type_mask, + lldb_private::TypeList &type_list) = 0; + virtual ClangASTContext & + GetClangASTContext (); + virtual ClangNamespaceDecl + FindNamespace (const SymbolContext& sc, + const ConstString &name, + const ClangNamespaceDecl *parent_namespace_decl) = 0; + + ObjectFile* GetObjectFile() { return m_obj_file; } + const ObjectFile* GetObjectFile() const { return m_obj_file; } + +protected: + ObjectFile* m_obj_file; // The object file that symbols can be extracted from. + uint32_t m_abilities; + bool m_calculated_abilities; +private: + DISALLOW_COPY_AND_ASSIGN (SymbolFile); +}; + + +} // namespace lldb_private + +#endif // liblldb_SymbolFile_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Symbol/SymbolVendor.h b/contrib/llvm/tools/lldb/include/lldb/Symbol/SymbolVendor.h new file mode 100644 index 00000000000..0eeea4eb466 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Symbol/SymbolVendor.h @@ -0,0 +1,207 @@ +//===-- SymbolVendor.h ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_SymbolVendor_h_ +#define liblldb_SymbolVendor_h_ + +#include + +#include "lldb/lldb-private.h" +#include "lldb/Core/ModuleChild.h" +#include "lldb/Core/PluginInterface.h" +#include "lldb/Symbol/ClangNamespaceDecl.h" +#include "lldb/Symbol/TypeList.h" + + +namespace lldb_private { + +//---------------------------------------------------------------------- +// The symbol vendor class is designed to abstract the process of +// searching for debug information for a given module. Platforms can +// subclass this class and provide extra ways to find debug information. +// Examples would be a subclass that would allow for locating a stand +// alone debug file, parsing debug maps, or runtime data in the object +// files. A symbol vendor can use multiple sources (SymbolFile +// objects) to provide the information and only parse as deep as needed +// in order to provide the information that is requested. +//---------------------------------------------------------------------- +class SymbolVendor : + public ModuleChild, + public PluginInterface +{ +public: + static SymbolVendor* + FindPlugin (const lldb::ModuleSP &module_sp, + Stream *feedback_strm); + + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + SymbolVendor(const lldb::ModuleSP &module_sp); + + virtual + ~SymbolVendor(); + + void + AddSymbolFileRepresentation(const lldb::ObjectFileSP &objfile_sp); + + virtual void + Dump(Stream *s); + + virtual lldb::LanguageType + ParseCompileUnitLanguage (const SymbolContext& sc); + + virtual size_t + ParseCompileUnitFunctions (const SymbolContext& sc); + + virtual bool + ParseCompileUnitLineTable (const SymbolContext& sc); + + virtual bool + ParseCompileUnitSupportFiles (const SymbolContext& sc, + FileSpecList& support_files); + + virtual size_t + ParseFunctionBlocks (const SymbolContext& sc); + + virtual size_t + ParseTypes (const SymbolContext& sc); + + virtual size_t + ParseVariablesForContext (const SymbolContext& sc); + + virtual Type* + ResolveTypeUID(lldb::user_id_t type_uid); + + virtual uint32_t + ResolveSymbolContext (const Address& so_addr, + uint32_t resolve_scope, + SymbolContext& sc); + + virtual uint32_t + ResolveSymbolContext (const FileSpec& file_spec, + uint32_t line, + bool check_inlines, + uint32_t resolve_scope, + SymbolContextList& sc_list); + + virtual size_t + FindGlobalVariables (const ConstString &name, + const ClangNamespaceDecl *namespace_decl, + bool append, + size_t max_matches, + VariableList& variables); + + virtual size_t + FindGlobalVariables (const RegularExpression& regex, + bool append, + size_t max_matches, + VariableList& variables); + + virtual size_t + FindFunctions (const ConstString &name, + const ClangNamespaceDecl *namespace_decl, + uint32_t name_type_mask, + bool include_inlines, + bool append, + SymbolContextList& sc_list); + + virtual size_t + FindFunctions (const RegularExpression& regex, + bool include_inlines, + bool append, + SymbolContextList& sc_list); + + virtual size_t + FindTypes (const SymbolContext& sc, + const ConstString &name, + const ClangNamespaceDecl *namespace_decl, + bool append, + size_t max_matches, + TypeList& types); + + virtual ClangNamespaceDecl + FindNamespace (const SymbolContext& sc, + const ConstString &name, + const ClangNamespaceDecl *parent_namespace_decl); + + virtual size_t + GetNumCompileUnits(); + + virtual bool + SetCompileUnitAtIndex (size_t cu_idx, + const lldb::CompUnitSP &cu_sp); + + virtual lldb::CompUnitSP + GetCompileUnitAtIndex(size_t idx); + + TypeList& + GetTypeList() + { + return m_type_list; + } + + const TypeList& + GetTypeList() const + { + return m_type_list; + } + + virtual size_t + GetTypes (SymbolContextScope *sc_scope, + uint32_t type_mask, + TypeList &type_list); + + SymbolFile * + GetSymbolFile() + { + return m_sym_file_ap.get(); + } + + // Get module unified section list symbol table. + virtual Symtab * + GetSymtab (); + + // Clear module unified section list symbol table. + virtual void + ClearSymtab (); + + //------------------------------------------------------------------ + // PluginInterface protocol + //------------------------------------------------------------------ + virtual ConstString + GetPluginName(); + + virtual uint32_t + GetPluginVersion(); + +protected: + //------------------------------------------------------------------ + // Classes that inherit from SymbolVendor can see and modify these + //------------------------------------------------------------------ + typedef std::vector CompileUnits; + typedef CompileUnits::iterator CompileUnitIter; + typedef CompileUnits::const_iterator CompileUnitConstIter; + + TypeList m_type_list; // Uniqued types for all parsers owned by this module + CompileUnits m_compile_units; // The current compile units + lldb::ObjectFileSP m_objfile_sp; // Keep a reference to the object file in case it isn't the same as the module object file (debug symbols in a separate file) + std::unique_ptr m_sym_file_ap; // A single symbol file. Subclasses can add more of these if needed. + +private: + //------------------------------------------------------------------ + // For SymbolVendor only + //------------------------------------------------------------------ + DISALLOW_COPY_AND_ASSIGN (SymbolVendor); +}; + + +} // namespace lldb_private + +#endif // liblldb_SymbolVendor_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Symbol/Symtab.h b/contrib/llvm/tools/lldb/include/lldb/Symbol/Symtab.h new file mode 100644 index 00000000000..666c3b5686b --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Symbol/Symtab.h @@ -0,0 +1,160 @@ +//===-- Symtab.h ------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + + +#ifndef liblldb_Symtab_h_ +#define liblldb_Symtab_h_ + +#include + +#include "lldb/lldb-private.h" +#include "lldb/Core/RangeMap.h" +#include "lldb/Core/UniqueCStringMap.h" +#include "lldb/Host/Mutex.h" +#include "lldb/Symbol/Symbol.h" + +namespace lldb_private { + +class Symtab +{ +public: + typedef std::vector IndexCollection; + typedef UniqueCStringMap NameToIndexMap; + + typedef enum Debug { + eDebugNo, // Not a debug symbol + eDebugYes, // A debug symbol + eDebugAny + } Debug; + + typedef enum Visibility { + eVisibilityAny, + eVisibilityExtern, + eVisibilityPrivate + } Visibility; + + Symtab(ObjectFile *objfile); + ~Symtab(); + + void Reserve (size_t count); + Symbol * Resize (size_t count); + uint32_t AddSymbol(const Symbol& symbol); + size_t GetNumSymbols() const; + void Dump(Stream *s, Target *target, SortOrder sort_type); + void Dump(Stream *s, Target *target, std::vector& indexes) const; + uint32_t GetIndexForSymbol (const Symbol *symbol) const; + Mutex & GetMutex () + { + return m_mutex; + } + Symbol * FindSymbolByID (lldb::user_id_t uid) const; + Symbol * SymbolAtIndex (size_t idx); + const Symbol * SymbolAtIndex (size_t idx) const; + Symbol * FindSymbolWithType (lldb::SymbolType symbol_type, Debug symbol_debug_type, Visibility symbol_visibility, uint32_t &start_idx); + uint32_t AppendSymbolIndexesWithType (lldb::SymbolType symbol_type, std::vector& indexes, uint32_t start_idx = 0, uint32_t end_index = UINT32_MAX) const; + uint32_t AppendSymbolIndexesWithTypeAndFlagsValue (lldb::SymbolType symbol_type, uint32_t flags_value, std::vector& indexes, uint32_t start_idx = 0, uint32_t end_index = UINT32_MAX) const; + uint32_t AppendSymbolIndexesWithType (lldb::SymbolType symbol_type, Debug symbol_debug_type, Visibility symbol_visibility, std::vector& matches, uint32_t start_idx = 0, uint32_t end_index = UINT32_MAX) const; + uint32_t AppendSymbolIndexesWithName (const ConstString& symbol_name, std::vector& matches); + uint32_t AppendSymbolIndexesWithName (const ConstString& symbol_name, Debug symbol_debug_type, Visibility symbol_visibility, std::vector& matches); + uint32_t AppendSymbolIndexesWithNameAndType (const ConstString& symbol_name, lldb::SymbolType symbol_type, std::vector& matches); + uint32_t AppendSymbolIndexesWithNameAndType (const ConstString& symbol_name, lldb::SymbolType symbol_type, Debug symbol_debug_type, Visibility symbol_visibility, std::vector& matches); + uint32_t AppendSymbolIndexesMatchingRegExAndType (const RegularExpression ®ex, lldb::SymbolType symbol_type, std::vector& indexes); + uint32_t AppendSymbolIndexesMatchingRegExAndType (const RegularExpression ®ex, lldb::SymbolType symbol_type, Debug symbol_debug_type, Visibility symbol_visibility, std::vector& indexes); + size_t FindAllSymbolsWithNameAndType (const ConstString &name, lldb::SymbolType symbol_type, std::vector& symbol_indexes); + size_t FindAllSymbolsWithNameAndType (const ConstString &name, lldb::SymbolType symbol_type, Debug symbol_debug_type, Visibility symbol_visibility, std::vector& symbol_indexes); + size_t FindAllSymbolsMatchingRexExAndType (const RegularExpression ®ex, lldb::SymbolType symbol_type, Debug symbol_debug_type, Visibility symbol_visibility, std::vector& symbol_indexes); + Symbol * FindFirstSymbolWithNameAndType (const ConstString &name, lldb::SymbolType symbol_type, Debug symbol_debug_type, Visibility symbol_visibility); + Symbol * FindSymbolContainingFileAddress (lldb::addr_t file_addr, const uint32_t* indexes, uint32_t num_indexes); + Symbol * FindSymbolContainingFileAddress (lldb::addr_t file_addr); + size_t FindFunctionSymbols (const ConstString &name, uint32_t name_type_mask, SymbolContextList& sc_list); + void CalculateSymbolSizes (); + + void SortSymbolIndexesByValue (std::vector& indexes, bool remove_duplicates) const; + + static void DumpSymbolHeader (Stream *s); + + + void Finalize () + { + // Shrink to fit the symbols so we don't waste memory + if (m_symbols.capacity() > m_symbols.size()) + { + collection new_symbols (m_symbols.begin(), m_symbols.end()); + m_symbols.swap (new_symbols); + } + } + + void AppendSymbolNamesToMap (const IndexCollection &indexes, + bool add_demangled, + bool add_mangled, + NameToIndexMap &name_to_index_map) const; + +protected: + typedef std::vector collection; + typedef collection::iterator iterator; + typedef collection::const_iterator const_iterator; + typedef RangeDataVector FileRangeToIndexMap; + void InitNameIndexes (); + void InitAddressIndexes (); + + ObjectFile * m_objfile; + collection m_symbols; + FileRangeToIndexMap m_file_addr_to_index; + UniqueCStringMap m_name_to_index; + UniqueCStringMap m_basename_to_index; + UniqueCStringMap m_method_to_index; + UniqueCStringMap m_selector_to_index; + mutable Mutex m_mutex; // Provide thread safety for this symbol table + bool m_file_addr_to_index_computed:1, + m_name_indexes_computed:1; +private: + + bool + CheckSymbolAtIndex (size_t idx, Debug symbol_debug_type, Visibility symbol_visibility) const + { + switch (symbol_debug_type) + { + case eDebugNo: + if (m_symbols[idx].IsDebug() == true) + return false; + break; + + case eDebugYes: + if (m_symbols[idx].IsDebug() == false) + return false; + break; + + case eDebugAny: + break; + } + + switch (symbol_visibility) + { + case eVisibilityAny: + return true; + + case eVisibilityExtern: + return m_symbols[idx].IsExternal(); + + case eVisibilityPrivate: + return !m_symbols[idx].IsExternal(); + } + return false; + } + + void + SymbolIndicesToSymbolContextList (std::vector &symbol_indexes, + SymbolContextList &sc_list); + + DISALLOW_COPY_AND_ASSIGN (Symtab); +}; + +} // namespace lldb_private + +#endif // liblldb_Symtab_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Symbol/TaggedASTType.h b/contrib/llvm/tools/lldb/include/lldb/Symbol/TaggedASTType.h new file mode 100644 index 00000000000..c44a5356f86 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Symbol/TaggedASTType.h @@ -0,0 +1,61 @@ +//===-- TaggedASTType.h -----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_TaggedASTType_h_ +#define liblldb_TaggedASTType_h_ + +#include "lldb/Symbol/ClangASTType.h" + +namespace lldb_private +{ + +// For cases in which there are multiple classes of types that are not +// interchangeable, to allow static type checking. +template class TaggedASTType : public ClangASTType +{ +public: + TaggedASTType (const ClangASTType &clang_type) : + ClangASTType(clang_type) + { + } + + TaggedASTType (lldb::clang_type_t type, clang::ASTContext *ast_context) : + ClangASTType(ast_context, type) + { + } + + TaggedASTType (const TaggedASTType &tw) : + ClangASTType(tw) + { + } + + TaggedASTType () : + ClangASTType() + { + } + + virtual + ~TaggedASTType() + { + } + + TaggedASTType &operator= (const TaggedASTType &tw) + { + ClangASTType::operator= (tw); + return *this; + } +}; + +// Commonly-used tagged types, so code using them is interoperable +typedef TaggedASTType<0> TypeFromParser; +typedef TaggedASTType<1> TypeFromUser; + +} + +#endif diff --git a/contrib/llvm/tools/lldb/include/lldb/Symbol/Type.h b/contrib/llvm/tools/lldb/include/lldb/Symbol/Type.h new file mode 100644 index 00000000000..50b22fe96b9 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Symbol/Type.h @@ -0,0 +1,596 @@ +//===-- Type.h --------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_Type_h_ +#define liblldb_Type_h_ + +#include "lldb/lldb-private.h" +#include "lldb/Core/ClangForward.h" +#include "lldb/Core/ConstString.h" +#include "lldb/Core/UserID.h" +#include "lldb/Symbol/ClangASTType.h" +#include "lldb/Symbol/Declaration.h" + +#include + +namespace lldb_private { + +class SymbolFileType : + public std::enable_shared_from_this, + public UserID + { + public: + SymbolFileType (SymbolFile &symbol_file, lldb::user_id_t uid) : + UserID (uid), + m_symbol_file (symbol_file) + { + } + + ~SymbolFileType () + { + } + + Type * + operator->() + { + return GetType (); + } + + Type * + GetType (); + + protected: + SymbolFile &m_symbol_file; + lldb::TypeSP m_type_sp; + }; + +class Type : + public std::enable_shared_from_this, + public UserID +{ +public: + typedef enum EncodingDataTypeTag + { + eEncodingInvalid, + eEncodingIsUID, ///< This type is the type whose UID is m_encoding_uid + eEncodingIsConstUID, ///< This type is the type whose UID is m_encoding_uid with the const qualifier added + eEncodingIsRestrictUID, ///< This type is the type whose UID is m_encoding_uid with the restrict qualifier added + eEncodingIsVolatileUID, ///< This type is the type whose UID is m_encoding_uid with the volatile qualifier added + eEncodingIsTypedefUID, ///< This type is pointer to a type whose UID is m_encoding_uid + eEncodingIsPointerUID, ///< This type is pointer to a type whose UID is m_encoding_uid + eEncodingIsLValueReferenceUID, ///< This type is L value reference to a type whose UID is m_encoding_uid + eEncodingIsRValueReferenceUID, ///< This type is R value reference to a type whose UID is m_encoding_uid + eEncodingIsSyntheticUID + } EncodingDataType; + + typedef enum ResolveStateTag + { + eResolveStateUnresolved = 0, + eResolveStateForward = 1, + eResolveStateLayout = 2, + eResolveStateFull = 3 + } ResolveState; + + Type (lldb::user_id_t uid, + SymbolFile* symbol_file, + const ConstString &name, + uint64_t byte_size, + SymbolContextScope *context, + lldb::user_id_t encoding_uid, + EncodingDataType encoding_uid_type, + const Declaration& decl, + const ClangASTType &clang_qual_type, + ResolveState clang_type_resolve_state); + + // This makes an invalid type. Used for functions that return a Type when they + // get an error. + Type(); + + Type (const Type &rhs); + + const Type& + operator= (const Type& rhs); + + void + Dump(Stream *s, bool show_context); + + void + DumpTypeName(Stream *s); + + + void + GetDescription (Stream *s, lldb::DescriptionLevel level, bool show_name); + + SymbolFile * + GetSymbolFile() + { + return m_symbol_file; + } + const SymbolFile * + GetSymbolFile() const + { + return m_symbol_file; + } + + TypeList* + GetTypeList(); + + const ConstString& + GetName(); + + uint64_t + GetByteSize(); + + uint32_t + GetNumChildren (bool omit_empty_base_classes); + + bool + IsAggregateType (); + + bool + IsValidType () + { + return m_encoding_uid_type != eEncodingInvalid; + } + + bool + IsTypedef () + { + return m_encoding_uid_type == eEncodingIsTypedefUID; + } + + lldb::TypeSP + GetTypedefType(); + + const ConstString & + GetName () const + { + return m_name; + } + + ConstString + GetQualifiedName (); + + void + DumpValue(ExecutionContext *exe_ctx, + Stream *s, + const DataExtractor &data, + uint32_t data_offset, + bool show_type, + bool show_summary, + bool verbose, + lldb::Format format = lldb::eFormatDefault); + + bool + DumpValueInMemory(ExecutionContext *exe_ctx, + Stream *s, + lldb::addr_t address, + AddressType address_type, + bool show_types, + bool show_summary, + bool verbose); + + bool + ReadFromMemory (ExecutionContext *exe_ctx, + lldb::addr_t address, + AddressType address_type, + DataExtractor &data); + + bool + WriteToMemory (ExecutionContext *exe_ctx, + lldb::addr_t address, + AddressType address_type, + DataExtractor &data); + + bool + GetIsDeclaration() const; + + void + SetIsDeclaration(bool b); + + bool + GetIsExternal() const; + + void + SetIsExternal(bool b); + + lldb::Format + GetFormat (); + + lldb::Encoding + GetEncoding (uint64_t &count); + + SymbolContextScope * + GetSymbolContextScope() + { + return m_context; + } + const SymbolContextScope * + GetSymbolContextScope() const + { + return m_context; + } + void + SetSymbolContextScope(SymbolContextScope *context) + { + m_context = context; + } + + const lldb_private::Declaration & + GetDeclaration () const; + + // Get the clang type, and resolve definitions for any + // class/struct/union/enum types completely. + ClangASTType + GetClangFullType (); + + // Get the clang type, and resolve definitions enough so that the type could + // have layout performed. This allows ptrs and refs to class/struct/union/enum + // types remain forward declarations. + ClangASTType + GetClangLayoutType (); + + // Get the clang type and leave class/struct/union/enum types as forward + // declarations if they haven't already been fully defined. + ClangASTType + GetClangForwardType (); + + ClangASTContext & + GetClangASTContext (); + + static int + Compare(const Type &a, const Type &b); + + // From a fully qualified typename, split the type into the type basename + // and the remaining type scope (namespaces/classes). + static bool + GetTypeScopeAndBasename (const char* &name_cstr, + std::string &scope, + std::string &basename, + lldb::TypeClass &type_class); + void + SetEncodingType (Type *encoding_type) + { + m_encoding_type = encoding_type; + } + + uint32_t + GetEncodingMask (); + + ClangASTType + CreateClangTypedefType (Type *typedef_type, Type *base_type); + + bool + IsRealObjCClass(); + + bool + IsCompleteObjCClass() + { + return m_flags.is_complete_objc_class; + } + + void + SetIsCompleteObjCClass(bool is_complete_objc_class) + { + m_flags.is_complete_objc_class = is_complete_objc_class; + } + +protected: + ConstString m_name; + SymbolFile *m_symbol_file; + SymbolContextScope *m_context; // The symbol context in which this type is defined + Type *m_encoding_type; + lldb::user_id_t m_encoding_uid; + EncodingDataType m_encoding_uid_type; + uint64_t m_byte_size; + Declaration m_decl; + ClangASTType m_clang_type; + + struct Flags { + ResolveState clang_type_resolve_state : 2; + bool is_complete_objc_class : 1; + } m_flags; + + Type * + GetEncodingType (); + + bool + ResolveClangType (ResolveState clang_type_resolve_state); +}; + + +/// +/// Sometimes you can find the name of the type corresponding to an object, but we don't have debug +/// information for it. If that is the case, you can return one of these objects, and then if it +/// has a full type, you can use that, but if not at least you can print the name for informational +/// purposes. +/// + +class TypeAndOrName +{ +public: + TypeAndOrName (); + TypeAndOrName (lldb::TypeSP &type_sp); + TypeAndOrName (const char *type_str); + TypeAndOrName (const TypeAndOrName &rhs); + TypeAndOrName (ConstString &type_const_string); + + TypeAndOrName & + operator= (const TypeAndOrName &rhs); + + bool + operator==(const TypeAndOrName &other) const; + + bool + operator!=(const TypeAndOrName &other) const; + + ConstString GetName () const; + + lldb::TypeSP + GetTypeSP () const + { + return m_type_sp; + } + + void + SetName (const ConstString &type_name); + + void + SetName (const char *type_name_cstr); + + void + SetTypeSP (lldb::TypeSP type_sp); + + bool + IsEmpty (); + + bool + HasName (); + + bool + HasTypeSP (); + + void + Clear (); + + operator + bool () + { + return !IsEmpty(); + } + +private: + lldb::TypeSP m_type_sp; + ConstString m_type_name; +}; + +// the two classes here are used by the public API as a backend to +// the SBType and SBTypeList classes + +class TypeImpl +{ +public: + + TypeImpl() : + m_clang_ast_type(), + m_type_sp() + { + } + + TypeImpl(const TypeImpl& rhs) : + m_clang_ast_type(rhs.m_clang_ast_type), + m_type_sp(rhs.m_type_sp) + { + } + + TypeImpl(const lldb_private::ClangASTType& type); + + TypeImpl(const lldb::TypeSP& type); + + TypeImpl& + operator = (const TypeImpl& rhs); + + bool + operator == (const TypeImpl& rhs) + { + return m_clang_ast_type == rhs.m_clang_ast_type && m_type_sp.get() == rhs.m_type_sp.get(); + } + + bool + operator != (const TypeImpl& rhs) + { + return m_clang_ast_type != rhs.m_clang_ast_type || m_type_sp.get() != rhs.m_type_sp.get(); + } + + bool + IsValid() + { + return m_type_sp.get() != NULL || m_clang_ast_type.IsValid(); + } + + const lldb_private::ClangASTType & + GetClangASTType() const + { + return m_clang_ast_type; + } + + clang::ASTContext* + GetASTContext(); + + lldb::clang_type_t + GetOpaqueQualType(); + + lldb::TypeSP + GetTypeSP () + { + return m_type_sp; + } + + ConstString + GetName (); + + bool + GetDescription (lldb_private::Stream &strm, + lldb::DescriptionLevel description_level); + + void + SetType (const lldb::TypeSP &type_sp); + +private: + ClangASTType m_clang_ast_type; + lldb::TypeSP m_type_sp; +}; + +class TypeListImpl +{ +public: + TypeListImpl() : + m_content() + { + } + + void + Append (const lldb::TypeImplSP& type) + { + m_content.push_back(type); + } + + class AppendVisitor + { + public: + AppendVisitor(TypeListImpl &type_list) : + m_type_list(type_list) + { + } + + void + operator() (const lldb::TypeImplSP& type) + { + m_type_list.Append(type); + } + + private: + TypeListImpl &m_type_list; + }; + + void + Append (const lldb_private::TypeList &type_list); + + lldb::TypeImplSP + GetTypeAtIndex(size_t idx) + { + lldb::TypeImplSP type_sp; + if (idx < GetSize()) + type_sp = m_content[idx]; + return type_sp; + } + + size_t + GetSize() + { + return m_content.size(); + } + +private: + std::vector m_content; +}; + +class TypeMemberImpl +{ +public: + TypeMemberImpl () : + m_type_impl_sp (), + m_bit_offset (0), + m_name (), + m_bitfield_bit_size (0), + m_is_bitfield (false) + + { + } + + TypeMemberImpl (const lldb::TypeImplSP &type_impl_sp, + uint64_t bit_offset, + const ConstString &name, + uint32_t bitfield_bit_size = 0, + bool is_bitfield = false) : + m_type_impl_sp (type_impl_sp), + m_bit_offset (bit_offset), + m_name (name), + m_bitfield_bit_size (bitfield_bit_size), + m_is_bitfield (is_bitfield) + { + } + + TypeMemberImpl (const lldb::TypeImplSP &type_impl_sp, + uint64_t bit_offset): + m_type_impl_sp (type_impl_sp), + m_bit_offset (bit_offset), + m_name (), + m_bitfield_bit_size (0), + m_is_bitfield (false) + { + if (m_type_impl_sp) + m_name = m_type_impl_sp->GetName(); + } + + const lldb::TypeImplSP & + GetTypeImpl () + { + return m_type_impl_sp; + } + + const ConstString & + GetName () const + { + return m_name; + } + + uint64_t + GetBitOffset () const + { + return m_bit_offset; + } + + uint32_t + GetBitfieldBitSize () const + { + return m_bitfield_bit_size; + } + + void + SetBitfieldBitSize (uint32_t bitfield_bit_size) + { + m_bitfield_bit_size = bitfield_bit_size; + } + + bool + GetIsBitfield () const + { + return m_is_bitfield; + } + + void + SetIsBitfield (bool is_bitfield) + { + m_is_bitfield = is_bitfield; + } + +protected: + lldb::TypeImplSP m_type_impl_sp; + uint64_t m_bit_offset; + ConstString m_name; + uint32_t m_bitfield_bit_size; // Bit size for bitfield members only + bool m_is_bitfield; +}; + + +} // namespace lldb_private + +#endif // liblldb_Type_h_ + diff --git a/contrib/llvm/tools/lldb/include/lldb/Symbol/TypeList.h b/contrib/llvm/tools/lldb/include/lldb/Symbol/TypeList.h new file mode 100644 index 00000000000..9c74db6bf1f --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Symbol/TypeList.h @@ -0,0 +1,88 @@ +//===-- TypeList.h ----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_TypeList_h_ +#define liblldb_TypeList_h_ + +#include "lldb/lldb-private.h" +#include "lldb/Symbol/Type.h" +#include + +namespace lldb_private { + +class TypeList +{ +public: + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + TypeList(); + + virtual + ~TypeList(); + + void + Clear(); + + void + Dump(Stream *s, bool show_context); + +// lldb::TypeSP +// FindType(lldb::user_id_t uid); + + TypeList + FindTypes(const ConstString &name); + + void + Insert (const lldb::TypeSP& type); + + bool + InsertUnique (const lldb::TypeSP& type); + + uint32_t + GetSize() const; + + lldb::TypeSP + GetTypeAtIndex(uint32_t idx); + + void + ForEach (std::function const &callback) const; + + void + ForEach (std::function const &callback); + + bool + RemoveTypeWithUID (lldb::user_id_t uid); + + void + RemoveMismatchedTypes (const char *qualified_typename, + bool exact_match); + + void + RemoveMismatchedTypes (const std::string &type_scope, + const std::string &type_basename, + lldb::TypeClass type_class, + bool exact_match); + + void + RemoveMismatchedTypes (lldb::TypeClass type_class); + +private: + typedef std::multimap collection; + typedef collection::iterator iterator; + typedef collection::const_iterator const_iterator; + + collection m_types; + + DISALLOW_COPY_AND_ASSIGN (TypeList); +}; + +} // namespace lldb_private + +#endif // liblldb_TypeList_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Symbol/TypeVendor.h b/contrib/llvm/tools/lldb/include/lldb/Symbol/TypeVendor.h new file mode 100644 index 00000000000..559b21eeb95 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Symbol/TypeVendor.h @@ -0,0 +1,61 @@ +//===-- TypeVendor.h --------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_TypeVendor_h_ +#define liblldb_TypeVendor_h_ + +#include "lldb/Core/ClangForward.h" + +namespace lldb_private { + +//---------------------------------------------------------------------- +// The type vendor class is intended as a generic interface to search +// for Clang types that are not necessarily backed by a specific symbol +// file. +//---------------------------------------------------------------------- +class TypeVendor +{ +public: + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + TypeVendor() + { + } + + virtual + ~TypeVendor() + { + } + + virtual uint32_t + FindTypes (const ConstString &name, + bool append, + uint32_t max_matches, + std::vector &types) = 0; + + virtual clang::ASTContext * + GetClangASTContext () = 0; + +protected: + //------------------------------------------------------------------ + // Classes that inherit from TypeVendor can see and modify these + //------------------------------------------------------------------ + +private: + //------------------------------------------------------------------ + // For TypeVendor only + //------------------------------------------------------------------ + DISALLOW_COPY_AND_ASSIGN (TypeVendor); +}; + + +} // namespace lldb_private + +#endif diff --git a/contrib/llvm/tools/lldb/include/lldb/Symbol/UnwindPlan.h b/contrib/llvm/tools/lldb/include/lldb/Symbol/UnwindPlan.h new file mode 100644 index 00000000000..6fc5ce04235 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Symbol/UnwindPlan.h @@ -0,0 +1,499 @@ +#ifndef liblldb_UnwindPlan_h +#define liblldb_UnwindPlan_h + +#include "lldb/lldb-private.h" +#include "lldb/Core/AddressRange.h" +#include "lldb/Core/Stream.h" +#include "lldb/Core/ConstString.h" + +#include +#include + +namespace lldb_private { + +// The UnwindPlan object specifies how to unwind out of a function - where +// this function saves the caller's register values before modifying them +// (for non-volatile aka saved registers) and how to find this frame's +// Canonical Frame Address (CFA). + +// Most commonly, registers are saved on the stack, offset some bytes from +// the Canonical Frame Address, or CFA, which is the starting address of +// this function's stack frame (the CFA is same as the eh_frame's CFA, +// whatever that may be on a given architecture). +// The CFA address for the stack frame does not change during +// the lifetime of the function. + +// Internally, the UnwindPlan is structured as a vector of register locations +// organized by code address in the function, showing which registers have been +// saved at that point and where they are saved. +// It can be thought of as the expanded table form of the DWARF CFI +// encoded information. + +// Other unwind information sources will be converted into UnwindPlans before +// being added to a FuncUnwinders object. The unwind source may be +// an eh_frame FDE, a DWARF debug_frame FDE, or assembly language based +// prologue analysis. +// The UnwindPlan is the canonical form of this information that the unwinder +// code will use when walking the stack. + +class UnwindPlan { +public: + + class Row { + public: + class RegisterLocation + { + public: + + enum RestoreType + { + unspecified, // not specified, we may be able to assume this + // is the same register. gcc doesn't specify all + // initial values so we really don't know... + undefined, // reg is not available, e.g. volatile reg + same, // reg is unchanged + atCFAPlusOffset, // reg = deref(CFA + offset) + isCFAPlusOffset, // reg = CFA + offset + inOtherRegister, // reg = other reg + atDWARFExpression, // reg = deref(eval(dwarf_expr)) + isDWARFExpression // reg = eval(dwarf_expr) + }; + + RegisterLocation() : + m_type(unspecified), + m_location() + { + } + + bool + operator == (const RegisterLocation& rhs) const; + + bool + operator != (const RegisterLocation &rhs) const + { + return !(*this == rhs); + } + + void + SetUnspecified() + { + m_type = unspecified; + } + + void + SetUndefined() + { + m_type = undefined; + } + + void + SetSame() + { + m_type = same; + } + + bool + IsSame () const + { + return m_type == same; + } + + bool + IsUnspecified () const + { + return m_type == unspecified; + } + + bool + IsCFAPlusOffset () const + { + return m_type == isCFAPlusOffset; + } + + bool + IsAtCFAPlusOffset () const + { + return m_type == atCFAPlusOffset; + } + + bool + IsInOtherRegister () const + { + return m_type == inOtherRegister; + } + + bool + IsAtDWARFExpression () const + { + return m_type == atDWARFExpression; + } + + bool + IsDWARFExpression () const + { + return m_type == isDWARFExpression; + } + + void + SetAtCFAPlusOffset (int32_t offset) + { + m_type = atCFAPlusOffset; + m_location.offset = offset; + } + + void + SetIsCFAPlusOffset (int32_t offset) + { + m_type = isCFAPlusOffset; + m_location.offset = offset; + } + + void + SetInRegister (uint32_t reg_num) + { + m_type = inOtherRegister; + m_location.reg_num = reg_num; + } + + uint32_t + GetRegisterNumber () const + { + if (m_type == inOtherRegister) + return m_location.reg_num; + return LLDB_INVALID_REGNUM; + } + + RestoreType + GetLocationType () const + { + return m_type; + } + + int32_t + GetOffset () const + { + if (m_type == atCFAPlusOffset || m_type == isCFAPlusOffset) + return m_location.offset; + return 0; + } + + void + GetDWARFExpr (const uint8_t **opcodes, uint16_t& len) const + { + if (m_type == atDWARFExpression || m_type == isDWARFExpression) + { + *opcodes = m_location.expr.opcodes; + len = m_location.expr.length; + } + else + { + *opcodes = NULL; + len = 0; + } + } + + void + SetAtDWARFExpression (const uint8_t *opcodes, uint32_t len); + + void + SetIsDWARFExpression (const uint8_t *opcodes, uint32_t len); + + const uint8_t * + GetDWARFExpressionBytes () + { + if (m_type == atDWARFExpression || m_type == isDWARFExpression) + return m_location.expr.opcodes; + return NULL; + } + + int + GetDWARFExpressionLength () + { + if (m_type == atDWARFExpression || m_type == isDWARFExpression) + return m_location.expr.length; + return 0; + } + + void + Dump (Stream &s, + const UnwindPlan* unwind_plan, + const UnwindPlan::Row* row, + Thread* thread, + bool verbose) const; + + private: + RestoreType m_type; // How do we locate this register? + union + { + // For m_type == atCFAPlusOffset or m_type == isCFAPlusOffset + int32_t offset; + // For m_type == inOtherRegister + uint32_t reg_num; // The register number + // For m_type == atDWARFExpression or m_type == isDWARFExpression + struct { + const uint8_t *opcodes; + uint16_t length; + } expr; + } m_location; + }; + + public: + Row (); + + Row (const UnwindPlan::Row& rhs) : + m_offset (rhs.m_offset), + m_cfa_reg_num (rhs.m_cfa_reg_num), + m_cfa_offset (rhs.m_cfa_offset), + m_register_locations (rhs.m_register_locations) + { + } + + bool + operator == (const Row &rhs) const; + + bool + GetRegisterInfo (uint32_t reg_num, RegisterLocation& register_location) const; + + void + SetRegisterInfo (uint32_t reg_num, const RegisterLocation register_location); + + lldb::addr_t + GetOffset() const + { + return m_offset; + } + + void + SetOffset(lldb::addr_t offset) + { + m_offset = offset; + } + + void + SlideOffset(lldb::addr_t offset) + { + m_offset += offset; + } + + uint32_t + GetCFARegister () const + { + return m_cfa_reg_num; + } + + bool + SetRegisterLocationToAtCFAPlusOffset (uint32_t reg_num, + int32_t offset, + bool can_replace); + + bool + SetRegisterLocationToIsCFAPlusOffset (uint32_t reg_num, + int32_t offset, + bool can_replace); + + bool + SetRegisterLocationToUndefined (uint32_t reg_num, + bool can_replace, + bool can_replace_only_if_unspecified); + + bool + SetRegisterLocationToUnspecified (uint32_t reg_num, + bool can_replace); + + bool + SetRegisterLocationToRegister (uint32_t reg_num, + uint32_t other_reg_num, + bool can_replace); + + bool + SetRegisterLocationToSame (uint32_t reg_num, + bool must_replace); + + + + void + SetCFARegister (uint32_t reg_num); + + int32_t + GetCFAOffset () const + { + return m_cfa_offset; + } + + void + SetCFAOffset (int32_t offset) + { + m_cfa_offset = offset; + } + + void + Clear (); + + void + Dump (Stream& s, const UnwindPlan* unwind_plan, Thread* thread, lldb::addr_t base_addr) const; + + protected: + typedef std::map collection; + lldb::addr_t m_offset; // Offset into the function for this row + uint32_t m_cfa_reg_num; // The Call Frame Address register number + int32_t m_cfa_offset; // The offset from the CFA for this row + collection m_register_locations; + }; // class Row + +public: + + typedef std::shared_ptr RowSP; + + UnwindPlan (lldb::RegisterKind reg_kind) : + m_row_list (), + m_plan_valid_address_range (), + m_register_kind (reg_kind), + m_return_addr_register (LLDB_INVALID_REGNUM), + m_source_name (), + m_plan_is_sourced_from_compiler (eLazyBoolCalculate), + m_plan_is_valid_at_all_instruction_locations (eLazyBoolCalculate) + { + } + + ~UnwindPlan () + { + } + + void + Dump (Stream& s, Thread* thread, lldb::addr_t base_addr) const; + + void + AppendRow (const RowSP& row_sp); + + // Returns a pointer to the best row for the given offset into the function's instructions. + // If offset is -1 it indicates that the function start is unknown - the final row in the UnwindPlan is returned. + // In practice, the UnwindPlan for a function with no known start address will be the architectural default + // UnwindPlan which will only have one row. + UnwindPlan::RowSP + GetRowForFunctionOffset (int offset) const; + + lldb::RegisterKind + GetRegisterKind () const + { + return m_register_kind; + } + + void + SetRegisterKind (lldb::RegisterKind kind) + { + m_register_kind = kind; + } + + void + SetReturnAddressRegister (uint32_t regnum) + { + m_return_addr_register = regnum; + } + + uint32_t + GetReturnAddressRegister (void) + { + return m_return_addr_register; + } + + uint32_t + GetInitialCFARegister () const + { + if (m_row_list.empty()) + return LLDB_INVALID_REGNUM; + return m_row_list.front()->GetCFARegister(); + } + + // This UnwindPlan may not be valid at every address of the function span. + // For instance, a FastUnwindPlan will not be valid at the prologue setup + // instructions - only in the body of the function. + void + SetPlanValidAddressRange (const AddressRange& range); + + const AddressRange & + GetAddressRange () const + { + return m_plan_valid_address_range; + } + + bool + PlanValidAtAddress (Address addr); + + bool + IsValidRowIndex (uint32_t idx) const; + + const UnwindPlan::RowSP + GetRowAtIndex (uint32_t idx) const; + + const UnwindPlan::RowSP + GetLastRow () const; + + lldb_private::ConstString + GetSourceName () const; + + void + SetSourceName (const char *); + + // Was this UnwindPlan emitted by a compiler? + lldb_private::LazyBool + GetSourcedFromCompiler () const + { + return m_plan_is_sourced_from_compiler; + } + + // Was this UnwindPlan emitted by a compiler? + void + SetSourcedFromCompiler (lldb_private::LazyBool from_compiler) + { + m_plan_is_sourced_from_compiler = from_compiler; + } + + // Is this UnwindPlan valid at all instructions? If not, then it is assumed valid at call sites, + // e.g. for exception handling. + lldb_private::LazyBool + GetUnwindPlanValidAtAllInstructions () const + { + return m_plan_is_valid_at_all_instruction_locations; + } + + // Is this UnwindPlan valid at all instructions? If not, then it is assumed valid at call sites, + // e.g. for exception handling. + void + SetUnwindPlanValidAtAllInstructions (lldb_private::LazyBool valid_at_all_insn) + { + m_plan_is_valid_at_all_instruction_locations = valid_at_all_insn; + } + + int + GetRowCount () const; + + void + Clear() + { + m_row_list.clear(); + m_plan_valid_address_range.Clear(); + m_register_kind = lldb::eRegisterKindDWARF; + m_source_name.Clear(); + } + + const RegisterInfo * + GetRegisterInfo (Thread* thread, uint32_t reg_num) const; + +private: + + + typedef std::vector collection; + collection m_row_list; + AddressRange m_plan_valid_address_range; + lldb::RegisterKind m_register_kind; // The RegisterKind these register numbers are in terms of - will need to be + // translated to lldb native reg nums at unwind time + uint32_t m_return_addr_register; // The register that has the return address for the caller frame + // e.g. the lr on arm + lldb_private::ConstString m_source_name; // for logging, where this UnwindPlan originated from + lldb_private::LazyBool m_plan_is_sourced_from_compiler; + lldb_private::LazyBool m_plan_is_valid_at_all_instruction_locations; +}; // class UnwindPlan + +} // namespace lldb_private + +#endif //liblldb_UnwindPlan_h diff --git a/contrib/llvm/tools/lldb/include/lldb/Symbol/UnwindTable.h b/contrib/llvm/tools/lldb/include/lldb/Symbol/UnwindTable.h new file mode 100644 index 00000000000..cefb91eb371 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Symbol/UnwindTable.h @@ -0,0 +1,69 @@ +//===-- Symtab.h ------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + + +#ifndef liblldb_UnwindTable_h +#define liblldb_UnwindTable_h + +#include + +#include "lldb/lldb-private.h" + +namespace lldb_private { + +// A class which holds all the FuncUnwinders objects for a given ObjectFile. +// The UnwindTable is populated with FuncUnwinders objects lazily during +// the debug session. + +class UnwindTable +{ +public: + UnwindTable(ObjectFile& objfile); + ~UnwindTable(); + + lldb_private::DWARFCallFrameInfo * + GetEHFrameInfo (); + + lldb::FuncUnwindersSP + GetFuncUnwindersContainingAddress (const Address& addr, SymbolContext &sc); + +// Normally when we create a new FuncUnwinders object we track it in this UnwindTable so it can +// be reused later. But for the target modules show-unwind we want to create brand new +// UnwindPlans for the function of interest - so ignore any existing FuncUnwinders for that +// function and don't add this new one to our UnwindTable. +// This FuncUnwinders object does have a reference to the UnwindTable but the lifetime of this +// uncached FuncUnwinders is expected to be short so in practice this will not be a problem. + lldb::FuncUnwindersSP + GetUncachedFuncUnwindersContainingAddress (const Address& addr, SymbolContext &sc); + +private: + void + Dump (Stream &s); + + void Initialize (); + + typedef std::map collection; + typedef collection::iterator iterator; + typedef collection::const_iterator const_iterator; + + ObjectFile& m_object_file; + collection m_unwinds; + + bool m_initialized; // delay some initialization until ObjectFile is set up + + UnwindAssembly* m_assembly_profiler; + + DWARFCallFrameInfo* m_eh_frame; + + DISALLOW_COPY_AND_ASSIGN (UnwindTable); +}; + +} // namespace lldb_private + +#endif // liblldb_UnwindTable_h diff --git a/contrib/llvm/tools/lldb/include/lldb/Symbol/Variable.h b/contrib/llvm/tools/lldb/include/lldb/Symbol/Variable.h new file mode 100644 index 00000000000..07295d090ee --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Symbol/Variable.h @@ -0,0 +1,184 @@ +//===-- Variable.h ----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_Variable_h_ +#define liblldb_Variable_h_ + +#include + +#include "lldb/lldb-private.h" +#include "lldb/lldb-enumerations.h" +#include "lldb/Core/Mangled.h" +#include "lldb/Core/UserID.h" +#include "lldb/Expression/DWARFExpression.h" +#include "lldb/Symbol/Declaration.h" + +namespace lldb_private { + +class Variable : public UserID +{ +public: + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + Variable (lldb::user_id_t uid, + const char *name, + const char *mangled, // The mangled variable name for variables in namespaces + const lldb::SymbolFileTypeSP &symfile_type_sp, + lldb::ValueType scope, + SymbolContextScope *owner_scope, + Declaration* decl, + const DWARFExpression& location, + bool external, + bool artificial); + + virtual + ~Variable(); + + void + Dump(Stream *s, bool show_context) const; + + bool + DumpDeclaration (Stream *s, + bool show_fullpaths, + bool show_module); + + const Declaration& + GetDeclaration() const + { + return m_declaration; + } + + const ConstString& + GetName() const; + + SymbolContextScope * + GetSymbolContextScope() const + { + return m_owner_scope; + } + + // Since a variable can have a basename "i" and also a mangled + // named "_ZN12_GLOBAL__N_11iE" and a demangled mangled name + // "(anonymous namespace)::i", this function will allow a generic match + // function that can be called by commands and expression parsers to make + // sure we match anything we come across. + bool + NameMatches (const ConstString &name) const + { + if (m_name == name) + return true; + return m_mangled.NameMatches (name); + } + + bool + NameMatches (const RegularExpression& regex) const; + + Type * + GetType(); + + lldb::ValueType + GetScope() const + { + return m_scope; + } + + bool + IsExternal() const + { + return m_external; + } + + bool + IsArtificial() const + { + return m_artificial; + } + + DWARFExpression & + LocationExpression() + { + return m_location; + } + + const DWARFExpression & + LocationExpression() const + { + return m_location; + } + + bool + DumpLocationForAddress (Stream *s, + const Address &address); + + size_t + MemorySize() const; + + void + CalculateSymbolContext (SymbolContext *sc); + + bool + IsInScope (StackFrame *frame); + + bool + LocationIsValidForFrame (StackFrame *frame); + + bool + LocationIsValidForAddress (const Address &address); + + bool + GetLocationIsConstantValueData () const + { + return m_loc_is_const_data; + } + + void + SetLocationIsConstantValueData (bool b) + { + m_loc_is_const_data = b; + } + + typedef size_t (*GetVariableCallback) (void *baton, + const char *name, + VariableList &var_list); + + + static Error + GetValuesForVariableExpressionPath (const char *variable_expr_path, + ExecutionContextScope *scope, + GetVariableCallback callback, + void *baton, + VariableList &variable_list, + ValueObjectList &valobj_list); + + static size_t + AutoComplete (const ExecutionContext &exe_ctx, + const char *name, + StringList &matches, + bool &word_complete); + +protected: + ConstString m_name; // The basename of the variable (no namespaces) + Mangled m_mangled; // The mangled name of the variable + lldb::SymbolFileTypeSP m_symfile_type_sp; // The type pointer of the variable (int, struct, class, etc) + lldb::ValueType m_scope; // global, parameter, local + SymbolContextScope *m_owner_scope; // The symbol file scope that this variable was defined in + Declaration m_declaration; // Declaration location for this item. + DWARFExpression m_location; // The location of this variable that can be fed to DWARFExpression::Evaluate() + uint8_t m_external:1, // Visible outside the containing compile unit? + m_artificial:1, // Non-zero if the variable is not explicitly declared in source + m_loc_is_const_data:1; // The m_location expression contains the constant variable value data, not a DWARF location +private: + Variable(const Variable& rhs); + Variable& operator=(const Variable& rhs); +}; + +} // namespace lldb_private + +#endif // liblldb_Variable_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Symbol/VariableList.h b/contrib/llvm/tools/lldb/include/lldb/Symbol/VariableList.h new file mode 100644 index 00000000000..2ce6146f462 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Symbol/VariableList.h @@ -0,0 +1,95 @@ +//===-- VariableList.h ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_VariableList_h_ +#define liblldb_VariableList_h_ + +#include "lldb/lldb-private.h" +#include "lldb/Symbol/SymbolContext.h" +#include "lldb/Symbol/Variable.h" + +namespace lldb_private { + +class VariableList +{ +public: + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ +// VariableList(const SymbolContext &symbol_context); + VariableList(); + virtual ~VariableList(); + + void + AddVariable (const lldb::VariableSP &var_sp); + + bool + AddVariableIfUnique (const lldb::VariableSP &var_sp); + + void + AddVariables (VariableList *variable_list); + + void + Clear(); + + void + Dump(Stream *s, bool show_context) const; + + lldb::VariableSP + GetVariableAtIndex(size_t idx) const; + + lldb::VariableSP + RemoveVariableAtIndex (size_t idx); + + lldb::VariableSP + FindVariable (const ConstString& name); + + uint32_t + FindVariableIndex (const lldb::VariableSP &var_sp); + + // Returns the actual number of unique variables that were added to the + // list. "total_matches" will get updated with the actualy number of + // matches that were found regardless of whether they were unique or not + // to allow for error conditions when nothing is found, versus conditions + // where any varaibles that match "regex" were already in "var_list". + size_t + AppendVariablesIfUnique (const RegularExpression& regex, + VariableList &var_list, + size_t& total_matches); + + size_t + AppendVariablesWithScope (lldb::ValueType type, + VariableList &var_list, + bool if_unique = true); + + uint32_t + FindIndexForVariable (Variable* variable); + + size_t + MemorySize() const; + + size_t + GetSize() const; + +protected: + typedef std::vector collection; + typedef collection::iterator iterator; + typedef collection::const_iterator const_iterator; + + collection m_variables; +private: + //------------------------------------------------------------------ + // For VariableList only + //------------------------------------------------------------------ + DISALLOW_COPY_AND_ASSIGN (VariableList); +}; + +} // namespace lldb_private + +#endif // liblldb_VariableList_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Symbol/VerifyDecl.h b/contrib/llvm/tools/lldb/include/lldb/Symbol/VerifyDecl.h new file mode 100644 index 00000000000..228e635652e --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Symbol/VerifyDecl.h @@ -0,0 +1,20 @@ +//===-- VerifyDecl.h --------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef lldb_VariableList_h_ +#define lldb_VariableList_h_ + +#include "lldb/Core/ClangForward.h" + +namespace lldb_private +{ + void VerifyDecl (clang::Decl *decl); +} + +#endif diff --git a/contrib/llvm/tools/lldb/include/lldb/Target/ABI.h b/contrib/llvm/tools/lldb/include/lldb/Target/ABI.h new file mode 100644 index 00000000000..16f8ee7fc7d --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Target/ABI.h @@ -0,0 +1,137 @@ +//===-- ABI.h ---------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ABI_h_ +#define liblldb_ABI_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/Error.h" +#include "lldb/Core/PluginInterface.h" +#include "lldb/lldb-private.h" + +namespace lldb_private { + +class ABI : + public PluginInterface +{ +public: + virtual + ~ABI(); + + virtual size_t + GetRedZoneSize () const = 0; + + virtual bool + PrepareTrivialCall (Thread &thread, + lldb::addr_t sp, + lldb::addr_t functionAddress, + lldb::addr_t returnAddress, + lldb::addr_t *arg1_ptr = NULL, + lldb::addr_t *arg2_ptr = NULL, + lldb::addr_t *arg3_ptr = NULL, + lldb::addr_t *arg4_ptr = NULL, + lldb::addr_t *arg5_ptr = NULL, + lldb::addr_t *arg6_ptr = NULL) const = 0; + + virtual bool + GetArgumentValues (Thread &thread, + ValueList &values) const = 0; + + lldb::ValueObjectSP + GetReturnValueObject (Thread &thread, + ClangASTType &type, + bool persistent = true) const; + + // Set the Return value object in the current frame as though a function with + virtual Error + SetReturnValueObject(lldb::StackFrameSP &frame_sp, lldb::ValueObjectSP &new_value) = 0; + +protected: + // This is the method the ABI will call to actually calculate the return value. + // Don't put it in a persistant value object, that will be done by the ABI::GetReturnValueObject. + virtual lldb::ValueObjectSP + GetReturnValueObjectImpl (Thread &thread, + ClangASTType &type) const = 0; +public: + virtual bool + CreateFunctionEntryUnwindPlan (UnwindPlan &unwind_plan) = 0; + + virtual bool + CreateDefaultUnwindPlan (UnwindPlan &unwind_plan) = 0; + + virtual bool + RegisterIsVolatile (const RegisterInfo *reg_info) = 0; + + // Should return true if your ABI uses frames when doing stack backtraces. This + // means a frame pointer is used that points to the previous stack frame in some + // way or another. + virtual bool + StackUsesFrames () = 0; + + // Should take a look at a call frame address (CFA) which is just the stack + // pointer value upon entry to a function. ABIs usually impose alignment + // restrictions (4, 8 or 16 byte aligned), and zero is usually not allowed. + // This function should return true if "cfa" is valid call frame address for + // the ABI, and false otherwise. This is used by the generic stack frame unwinding + // code to help determine when a stack ends. + virtual bool + CallFrameAddressIsValid (lldb::addr_t cfa) = 0; + + // Validates a possible PC value and returns true if an opcode can be at "pc". + virtual bool + CodeAddressIsValid (lldb::addr_t pc) = 0; + + virtual lldb::addr_t + FixCodeAddress (lldb::addr_t pc) + { + // Some targets might use bits in a code address to indicate + // a mode switch. ARM uses bit zero to signify a code address is + // thumb, so any ARM ABI plug-ins would strip those bits. + return pc; + } + + virtual const RegisterInfo * + GetRegisterInfoArray (uint32_t &count) = 0; + + // Some architectures (e.g. x86) will push the return address on the stack and decrement + // the stack pointer when making a function call. This means that every stack frame will + // have a unique CFA. + // Other architectures (e.g. arm) pass the return address in a register so it is possible + // to have a frame on a backtrace that does not push anything on the stack or change the + // CFA. + virtual bool + FunctionCallsChangeCFA () = 0; + + + bool + GetRegisterInfoByName (const ConstString &name, RegisterInfo &info); + + bool + GetRegisterInfoByKind (lldb::RegisterKind reg_kind, + uint32_t reg_num, + RegisterInfo &info); + + static lldb::ABISP + FindPlugin (const ArchSpec &arch); + +protected: + //------------------------------------------------------------------ + // Classes that inherit from ABI can see and modify these + //------------------------------------------------------------------ + ABI(); +private: + DISALLOW_COPY_AND_ASSIGN (ABI); +}; + +} // namespace lldb_private + +#endif // liblldb_ABI_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Target/CPPLanguageRuntime.h b/contrib/llvm/tools/lldb/include/lldb/Target/CPPLanguageRuntime.h new file mode 100644 index 00000000000..98a4ab88cb2 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Target/CPPLanguageRuntime.h @@ -0,0 +1,158 @@ +//===-- CPPLanguageRuntime.h ---------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_CPPLanguageRuntime_h_ +#define liblldb_CPPLanguageRuntime_h_ + +// C Includes +// C++ Includes +#include +// Other libraries and framework includes +// Project includes +#include "lldb/Core/PluginInterface.h" +#include "lldb/lldb-private.h" +#include "lldb/Target/LanguageRuntime.h" + +namespace lldb_private { + +class CPPLanguageRuntime : + public LanguageRuntime +{ +public: + + class MethodName + { + public: + enum Type + { + eTypeInvalid, + eTypeUnknownMethod, + eTypeClassMethod, + eTypeInstanceMethod + }; + + MethodName () : + m_full(), + m_basename(), + m_context(), + m_arguments(), + m_qualifiers(), + m_type (eTypeInvalid), + m_parsed (false), + m_parse_error (false) + { + } + + MethodName (const ConstString &s) : + m_full(s), + m_basename(), + m_context(), + m_arguments(), + m_qualifiers(), + m_type (eTypeInvalid), + m_parsed (false), + m_parse_error (false) + { + } + + void + Clear(); + + bool + IsValid () const + { + if (m_parse_error) + return false; + if (m_type == eTypeInvalid) + return false; + return (bool)m_full; + } + + Type + GetType () const + { + return m_type; + } + + const ConstString & + GetFullName () const + { + return m_full; + } + + llvm::StringRef + GetBasename (); + + llvm::StringRef + GetContext (); + + llvm::StringRef + GetArguments (); + + llvm::StringRef + GetQualifiers (); + + protected: + void + Parse(); + + ConstString m_full; // Full name: "lldb::SBTarget::GetBreakpointAtIndex(unsigned int) const" + llvm::StringRef m_basename; // Basename: "GetBreakpointAtIndex" + llvm::StringRef m_context; // Decl context: "lldb::SBTarget" + llvm::StringRef m_arguments; // Arguments: "(unsigned int)" + llvm::StringRef m_qualifiers; // Qualifiers: "const" + Type m_type; + bool m_parsed; + bool m_parse_error; + }; + + virtual + ~CPPLanguageRuntime(); + + virtual lldb::LanguageType + GetLanguageType () const + { + return lldb::eLanguageTypeC_plus_plus; + } + + virtual bool + IsVTableName (const char *name) = 0; + + virtual bool + GetObjectDescription (Stream &str, ValueObject &object); + + virtual bool + GetObjectDescription (Stream &str, Value &value, ExecutionContextScope *exe_scope); + + static bool + IsCPPMangledName(const char *name); + + static bool + StripNamespacesFromVariableName (const char *name, const char *&base_name_start, const char *&base_name_end); + + // in some cases, compilers will output different names for one same type. when tht happens, it might be impossible + // to construct SBType objects for a valid type, because the name that is available is not the same as the name that + // can be used as a search key in FindTypes(). the equivalents map here is meant to return possible alternative names + // for a type through which a search can be conducted. Currently, this is only enabled for C++ but can be extended + // to ObjC or other languages if necessary + static uint32_t + FindEquivalentNames(ConstString type_name, std::vector& equivalents); + +protected: + //------------------------------------------------------------------ + // Classes that inherit from CPPLanguageRuntime can see and modify these + //------------------------------------------------------------------ + CPPLanguageRuntime(Process *process); +private: + DISALLOW_COPY_AND_ASSIGN (CPPLanguageRuntime); +}; + +} // namespace lldb_private + +#endif // liblldb_CPPLanguageRuntime_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Target/DynamicLoader.h b/contrib/llvm/tools/lldb/include/lldb/Target/DynamicLoader.h new file mode 100644 index 00000000000..6b76e5891ae --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Target/DynamicLoader.h @@ -0,0 +1,239 @@ +//===-- DynamicLoader.h -----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_DynamicLoader_h_ +#define liblldb_DynamicLoader_h_ + +// Project includes +#include "lldb/lldb-private.h" +#include "lldb/Core/Error.h" +#include "lldb/Core/PluginInterface.h" + +namespace lldb_private { + +//---------------------------------------------------------------------- +/// @class DynamicLoader DynamicLoader.h "lldb/Target/DynamicLoader.h" +/// @brief A plug-in interface definition class for dynamic loaders. +/// +/// Dynamic loader plug-ins track image (shared library) loading and +/// unloading. The class is initialized given a live process that is +/// halted at its entry point or just after attaching. +/// +/// Dynamic loader plug-ins can track the process by registering +/// callbacks using the: +/// Process::RegisterNotificationCallbacks (const Notifications&) +/// function. +/// +/// Breakpoints can also be set in the process which can register +/// functions that get called using: +/// Process::BreakpointSetCallback (lldb::user_id_t, BreakpointHitCallback, void *). +/// These breakpoint callbacks return a boolean value that indicates if +/// the process should continue or halt and should return the global +/// setting for this using: +/// DynamicLoader::StopWhenImagesChange() const. +//---------------------------------------------------------------------- +class DynamicLoader : + public PluginInterface +{ +public: + //------------------------------------------------------------------ + /// Find a dynamic loader plugin for a given process. + /// + /// Scans the installed DynamicLoader plug-ins and tries to find + /// an instance that can be used to track image changes in \a + /// process. + /// + /// @param[in] process + /// The process for which to try and locate a dynamic loader + /// plug-in instance. + /// + /// @param[in] plugin_name + /// An optional name of a specific dynamic loader plug-in that + /// should be used. If NULL, pick the best plug-in. + //------------------------------------------------------------------ + static DynamicLoader* + FindPlugin (Process *process, const char *plugin_name); + + //------------------------------------------------------------------ + /// Construct with a process. + //------------------------------------------------------------------ + DynamicLoader (Process *process); + + //------------------------------------------------------------------ + /// Destructor. + /// + /// The destructor is virtual since this class is designed to be + /// inherited from by the plug-in instance. + //------------------------------------------------------------------ + virtual + ~DynamicLoader (); + + //------------------------------------------------------------------ + /// Called after attaching a process. + /// + /// Allow DynamicLoader plug-ins to execute some code after + /// attaching to a process. + //------------------------------------------------------------------ + virtual void + DidAttach () = 0; + + //------------------------------------------------------------------ + /// Called after launching a process. + /// + /// Allow DynamicLoader plug-ins to execute some code after + /// the process has stopped for the first time on launch. + //------------------------------------------------------------------ + virtual void + DidLaunch () = 0; + + + //------------------------------------------------------------------ + /// Helper function that can be used to detect when a process has + /// called exec and is now a new and different process. This can + /// be called when necessary to try and detect the exec. The process + /// might be able to answer this question, but sometimes it might + /// not be able and the dynamic loader often knows what the program + /// entry point is. So the process and the dynamic loader can work + /// together to detect this. + //------------------------------------------------------------------ + virtual bool + ProcessDidExec () + { + return false; + } + //------------------------------------------------------------------ + /// Get whether the process should stop when images change. + /// + /// When images (executables and shared libraries) get loaded or + /// unloaded, often debug sessions will want to try and resolve or + /// unresolve breakpoints that are set in these images. Any + /// breakpoints set by DynamicLoader plug-in instances should + /// return this value to ensure consistent debug session behaviour. + /// + /// @return + /// Returns \b true if the process should stop when images + /// change, \b false if the process should resume. + //------------------------------------------------------------------ + bool + GetStopWhenImagesChange () const; + + //------------------------------------------------------------------ + /// Set whether the process should stop when images change. + /// + /// When images (executables and shared libraries) get loaded or + /// unloaded, often debug sessions will want to try and resolve or + /// unresolve breakpoints that are set in these images. The default + /// is set so that the process stops when images change, but this + /// can be overridden using this function callback. + /// + /// @param[in] stop + /// Boolean value that indicates whether the process should stop + /// when images change. + //------------------------------------------------------------------ + void + SetStopWhenImagesChange (bool stop); + + //------------------------------------------------------------------ + /// Provides a plan to step through the dynamic loader trampoline + /// for the current state of \a thread. + /// + /// + /// @param[in] stop_others + /// Whether the plan should be set to stop other threads. + /// + /// @return + /// A pointer to the plan (caller owned) or NULL if we are not at such + /// a trampoline. + //------------------------------------------------------------------ + virtual lldb::ThreadPlanSP + GetStepThroughTrampolinePlan (Thread &thread, bool stop_others) = 0; + + + //------------------------------------------------------------------ + /// Some dynamic loaders provide features where there are a group of symbols "equivalent to" + /// a given symbol one of which will be chosen when the symbol is bound. If you want to + /// set a breakpoint on one of these symbols, you really need to set it on all the + /// equivalent symbols. + /// + /// + /// @param[in] original_symbol + /// The symbol for which we are finding equivalences. + /// + /// @param[in] module_list + /// The set of modules in which to search. + /// + /// @param[out] equivalent_symbols + /// The equivalent symbol list - any equivalent symbols found are appended to this list. + /// + /// @return + /// Number of equivalent symbols found. + //------------------------------------------------------------------ + virtual size_t + FindEquivalentSymbols (Symbol *original_symbol, ModuleList &module_list, SymbolContextList &equivalent_symbols) + { + return 0; + } + + //------------------------------------------------------------------ + /// Ask if it is ok to try and load or unload an shared library + /// (image). + /// + /// The dynamic loader often knows when it would be ok to try and + /// load or unload a shared library. This function call allows the + /// dynamic loader plug-ins to check any current dyld state to make + /// sure it is an ok time to load a shared library. + /// + /// @return + /// \b true if it is currently ok to try and load a shared + /// library into the process, \b false otherwise. + //------------------------------------------------------------------ + virtual Error + CanLoadImage () = 0; + + //------------------------------------------------------------------ + /// Ask if the eh_frame information for the given SymbolContext should + /// be relied on even when it's the first frame in a stack unwind. + /// + /// The CFI instructions from the eh_frame section are normally only + /// valid at call sites -- places where a program could throw an + /// exception and need to unwind out. But some Modules may be known + /// to the system as having reliable eh_frame information at all call + /// sites. This would be the case if the Module's contents are largely + /// hand-written assembly with hand-written eh_frame information. + /// Normally when unwinding from a function at the beginning of a stack + /// unwind lldb will examine the assembly instructions to understand + /// how the stack frame is set up and where saved registers are stored. + /// But with hand-written assembly this is not reliable enough -- we need + /// to consult those function's hand-written eh_frame information. + /// + /// @return + /// \b True if the symbol context should use eh_frame instructions + /// unconditionally when unwinding from this frame. Else \b false, + /// the normal lldb unwind behavior of only using eh_frame when the + /// function appears in the middle of the stack. + //------------------------------------------------------------------ + virtual bool + AlwaysRelyOnEHUnwindInfo (SymbolContext &sym_ctx) + { + return false; + } + +protected: + //------------------------------------------------------------------ + // Member variables. + //------------------------------------------------------------------ + Process* m_process; ///< The process that this dynamic loader plug-in is tracking. +private: + DISALLOW_COPY_AND_ASSIGN (DynamicLoader); + +}; + +} // namespace lldb_private + +#endif // liblldb_DynamicLoader_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Target/ExecutionContext.h b/contrib/llvm/tools/lldb/include/lldb/Target/ExecutionContext.h new file mode 100644 index 00000000000..de5fe14934a --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Target/ExecutionContext.h @@ -0,0 +1,778 @@ +//===-- ExecutionContext.h --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// Execution context objects refer to objects in the execution of the +/// program that is being debugged. The consist of one or more of the +/// following objects: target, process, thread, and frame. Many objects +/// in the debugger need to track different executions contexts. For +/// example, a local function variable might have an execution context +/// that refers to a stack frame. A global or static variable might +/// refer to a target since a stack frame isn't required in order to +/// evaluate a global or static variable (a process isn't necessarily +/// needed for a global variable since we might be able to read the +/// variable value from a data section in one of the object files in +/// a target). There are two types of objects that hold onto execution +/// contexts: ExecutionContextRef and ExecutionContext. Both of these +/// objects are deascribed below. +/// +/// Not all objects in an ExectionContext objects will be valid. If you want +/// to refer stronly (ExectionContext) or weakly (ExectionContextRef) to +/// a process, then only the process and target references will be valid. +/// For threads, only the thread, process and target references will be +/// filled in. For frames, all of the objects will be filled in. +/// +/// These classes are designed to be used as baton objects that get passed +/// to a wide variety of functions that require execution contexts. +//===----------------------------------------------------------------------===// + + + +#ifndef liblldb_ExecutionContext_h_ +#define liblldb_ExecutionContext_h_ + +#include "lldb/lldb-private.h" +#include "lldb/Target/StackID.h" +#include "lldb/Host/Mutex.h" + +namespace lldb_private { + +//---------------------------------------------------------------------- +/// @class ExecutionContextRef ExecutionContext.h "lldb/Target/ExecutionContext.h" +/// @brief A class that holds a weak reference to an execution context. +/// +/// ExecutionContextRef objects are designed to hold onto an execution +/// context that might change over time. For example, if an object wants +/// to refer to a stack frame, it should hold onto an ExecutionContextRef +/// to a frame object. The backing object that represents the stack frame +/// might change over time and instaces of this object can track the logical +/// object that refers to a frame even if it does change. +/// +/// These objects also don't keep execution objects around longer than they +/// should since they use weak pointers. For example if an object refers +/// to a stack frame and a stack frame is no longer in a thread, then a +/// ExecutionContextRef object that refers to that frame will not be able +/// to get a shared pointer to those objects since they are no longer around. +/// +/// ExecutionContextRef objects can also be used as objects in classes +/// that want to track a "previous execution context". Since the weak +/// references to the execution objects (target, process, thread and frame) +/// don't keep these objects around, they are safe to keep around. +/// +/// The general rule of thumb is all long lived objects that want to +/// refer to execution contexts should use ExecutionContextRef objcts. +/// The ExecutionContext class is used to temporarily get shared +/// pointers to any execution context objects that are still around +/// so they are guaranteed to exist during a function that requires the +/// objects. ExecutionContext objects should NOT be used for long term +/// storage since they will keep objects alive with extra shared pointer +/// references to these objects. +//---------------------------------------------------------------------- +class ExecutionContextRef +{ +public: + //------------------------------------------------------------------ + /// Default Constructor. + //------------------------------------------------------------------ + ExecutionContextRef(); + + //------------------------------------------------------------------ + /// Copy Constructor. + //------------------------------------------------------------------ + ExecutionContextRef (const ExecutionContextRef &rhs); + + //------------------------------------------------------------------ + /// Construct using an ExecutionContext object that might be NULL. + /// + /// If \a exe_ctx_ptr is valid, then make weak references to any + /// valid objects in the ExecutionContext, othewise no weak + /// references to any execution context objects will be made. + //------------------------------------------------------------------ + ExecutionContextRef (const ExecutionContext *exe_ctx_ptr); + + //------------------------------------------------------------------ + /// Construct using an ExecutionContext object. + /// + /// Make weak references to any valid objects in the ExecutionContext. + //------------------------------------------------------------------ + ExecutionContextRef (const ExecutionContext &exe_ctx); + + //------------------------------------------------------------------ + /// Assignment operator + /// + /// Copy all weak refernces in \a rhs. + //------------------------------------------------------------------ + ExecutionContextRef & + operator =(const ExecutionContextRef &rhs); + + //------------------------------------------------------------------ + /// Assignment operator from a ExecutionContext + /// + /// Make weak refernces to any stringly referenced objects in \a exe_ctx. + //------------------------------------------------------------------ + ExecutionContextRef & + operator =(const ExecutionContext &exe_ctx); + + //------------------------------------------------------------------ + /// Construct using the target and all the selected items inside of it + /// (the process and its selected thread, and the thread's selected + /// frame). If there is no selected thread, default to the first thread + /// If there is no selected frame, default to the first frame. + //------------------------------------------------------------------ + ExecutionContextRef (Target *target, bool adopt_selected); + + //------------------------------------------------------------------ + /// Construct using an execution context scope. + /// + /// If the ExecutionContextScope object is valid and refers to a frame, + /// make weak refernces too the frame, thread, process and target. + /// If the ExecutionContextScope object is valid and refers to a thread, + /// make weak refernces too the thread, process and target. + /// If the ExecutionContextScope object is valid and refers to a process, + /// make weak refernces too the process and target. + /// If the ExecutionContextScope object is valid and refers to a target, + /// make weak refernces too the target. + //------------------------------------------------------------------ + ExecutionContextRef (ExecutionContextScope *exe_scope); + + //------------------------------------------------------------------ + /// Construct using an execution context scope. + /// + /// If the ExecutionContextScope object refers to a frame, + /// make weak refernces too the frame, thread, process and target. + /// If the ExecutionContextScope object refers to a thread, + /// make weak refernces too the thread, process and target. + /// If the ExecutionContextScope object refers to a process, + /// make weak refernces too the process and target. + /// If the ExecutionContextScope object refers to a target, + /// make weak refernces too the target. + //------------------------------------------------------------------ + ExecutionContextRef (ExecutionContextScope &exe_scope); + + ~ExecutionContextRef(); + //------------------------------------------------------------------ + /// Clear the object's state. + /// + /// Sets the process and thread to NULL, and the frame index to an + /// invalid value. + //------------------------------------------------------------------ + void + Clear (); + + //------------------------------------------------------------------ + /// Set accessor that creates a weak reference to the target + /// referenced in \a target_sp. + /// + /// If \a target_sp is valid this object will create a weak + /// reference to that object, otherwise any previous target weak + /// reference contained in this object will be reset. + /// + /// Only the weak reference to the target will be updated, no other + /// weak references will be modified. If you want this execution + /// context to make a weak reference to the target's process, use + /// the ExecutionContextRef::SetContext() functions. + /// + /// @see ExecutionContextRef::SetContext(const lldb::TargetSP &, bool) + //------------------------------------------------------------------ + void + SetTargetSP (const lldb::TargetSP &target_sp); + + //------------------------------------------------------------------ + /// Set accessor that creates a weak reference to the process + /// referenced in \a process_sp. + /// + /// If \a process_sp is valid this object will create a weak + /// reference to that object, otherwise any previous process weak + /// reference contained in this object will be reset. + /// + /// Only the weak reference to the process will be updated, no other + /// weak references will be modified. If you want this execution + /// context to make a weak reference to the target, use the + /// ExecutionContextRef::SetContext() functions. + /// + /// @see ExecutionContextRef::SetContext(const lldb::ProcessSP &) + //------------------------------------------------------------------ + void + SetProcessSP (const lldb::ProcessSP &process_sp); + + //------------------------------------------------------------------ + /// Set accessor that creates a weak reference to the thread + /// referenced in \a thread_sp. + /// + /// If \a thread_sp is valid this object will create a weak + /// reference to that object, otherwise any previous thread weak + /// reference contained in this object will be reset. + /// + /// Only the weak reference to the thread will be updated, no other + /// weak references will be modified. If you want this execution + /// context to make a weak reference to the thread's process and + /// target, use the ExecutionContextRef::SetContext() functions. + /// + /// @see ExecutionContextRef::SetContext(const lldb::ThreadSP &) + //------------------------------------------------------------------ + void + SetThreadSP (const lldb::ThreadSP &thread_sp); + + //------------------------------------------------------------------ + /// Set accessor that creates a weak reference to the frame + /// referenced in \a frame_sp. + /// + /// If \a frame_sp is valid this object will create a weak + /// reference to that object, otherwise any previous frame weak + /// reference contained in this object will be reset. + /// + /// Only the weak reference to the frame will be updated, no other + /// weak references will be modified. If you want this execution + /// context to make a weak reference to the frame's thread, process + /// and target, use the ExecutionContextRef::SetContext() functions. + /// + /// @see ExecutionContextRef::SetContext(const lldb::StackFrameSP &) + //------------------------------------------------------------------ + void + SetFrameSP (const lldb::StackFrameSP &frame_sp); + + void + SetTargetPtr (Target* target, bool adopt_selected); + + void + SetProcessPtr (Process *process); + + void + SetThreadPtr (Thread *thread); + + void + SetFramePtr (StackFrame *frame); + + //------------------------------------------------------------------ + /// Get accessor that creates a strong reference from the weak target + /// reference contained in this object. + /// + /// @returns + /// A shared pointer to a target that is not guaranteed to be valid. + //------------------------------------------------------------------ + lldb::TargetSP + GetTargetSP () const; + + //------------------------------------------------------------------ + /// Get accessor that creates a strong reference from the weak process + /// reference contained in this object. + /// + /// @returns + /// A shared pointer to a process that is not guaranteed to be valid. + //------------------------------------------------------------------ + lldb::ProcessSP + GetProcessSP () const; + + //------------------------------------------------------------------ + /// Get accessor that creates a strong reference from the weak thread + /// reference contained in this object. + /// + /// @returns + /// A shared pointer to a thread that is not guaranteed to be valid. + //------------------------------------------------------------------ + lldb::ThreadSP + GetThreadSP () const; + + //------------------------------------------------------------------ + /// Get accessor that creates a strong reference from the weak frame + /// reference contained in this object. + /// + /// @returns + /// A shared pointer to a frame that is not guaranteed to be valid. + //------------------------------------------------------------------ + lldb::StackFrameSP + GetFrameSP () const; + + //------------------------------------------------------------------ + /// Create an ExecutionContext object from this object. + /// + /// Create strong references to any execution context objects that + /// are still valid. Any of the returned shared pointers in the + /// ExecutionContext objects is not guaranteed to be valid. + /// @returns + /// An execution context object that has strong references to + /// any valid weak references in this object. + //------------------------------------------------------------------ + ExecutionContext + Lock () const; + + //------------------------------------------------------------------ + /// Returns true if this object has a weak reference to a thread. + /// The return value is only an indication of wether this object has + /// a weak reference and does not indicate wether the weak rerference + /// is valid or not. + //------------------------------------------------------------------ + bool + HasThreadRef () const + { + return m_tid != LLDB_INVALID_THREAD_ID; + } + + //------------------------------------------------------------------ + /// Returns true if this object has a weak reference to a frame. + /// The return value is only an indication of wether this object has + /// a weak reference and does not indicate wether the weak rerference + /// is valid or not. + //------------------------------------------------------------------ + bool + HasFrameRef () const + { + return m_stack_id.IsValid(); + } + + void + ClearThread () + { + m_thread_wp.reset(); + m_tid = LLDB_INVALID_THREAD_ID; + } + + void + ClearFrame () + { + m_stack_id.Clear(); + } + +protected: + //------------------------------------------------------------------ + // Member variables + //------------------------------------------------------------------ + lldb::TargetWP m_target_wp; ///< A weak reference to a target + lldb::ProcessWP m_process_wp; ///< A weak reference to a process + mutable lldb::ThreadWP m_thread_wp; ///< A weak reference to a thread + lldb::tid_t m_tid; ///< The thread ID that this object refers to in case the backing object changes + StackID m_stack_id; ///< The stack ID that this object refers to in case the backing object changes +}; + +//---------------------------------------------------------------------- +/// @class ExecutionContext ExecutionContext.h "lldb/Target/ExecutionContext.h" +/// @brief A class that contains an execution context. +/// +/// This baton object can be passed into any function that requires +/// a context that specifies a target, process, thread and frame. +/// These objects are designed to be used for short term execution +/// context object storage while a function might be trying to evaluate +/// something that requires a thread or frame. ExecutionContextRef +/// objects can be used to initialize one of these objects to turn +/// the weak execution context object references to the target, process, +/// thread and frame into strong references (shared pointers) so that +/// functions can guarantee that these objects won't go away in the +/// middle of a function. +/// +/// ExecutionContext objects should be used as short lived objects +/// (typically on the stack) in order to lock down an execution context +/// for local use and for passing down to other functions that also +/// require specific contexts. They should NOT be used for long term +/// storage, for long term storage use ExecutionContextRef objects. +//---------------------------------------------------------------------- +class ExecutionContext +{ +public: + //------------------------------------------------------------------ + /// Default Constructor. + //------------------------------------------------------------------ + ExecutionContext(); + + //------------------------------------------------------------------ + // Copy constructor + //------------------------------------------------------------------ + ExecutionContext (const ExecutionContext &rhs); + + //------------------------------------------------------------------ + // Adopt the target and optionally its current context. + //------------------------------------------------------------------ + ExecutionContext (Target* t, bool fill_current_process_thread_frame = true); + + //------------------------------------------------------------------ + // Create execution contexts from shared pointers + //------------------------------------------------------------------ + ExecutionContext (const lldb::TargetSP &target_sp, bool get_process); + ExecutionContext (const lldb::ProcessSP &process_sp); + ExecutionContext (const lldb::ThreadSP &thread_sp); + ExecutionContext (const lldb::StackFrameSP &frame_sp); + //------------------------------------------------------------------ + // Create execution contexts from weak pointers + //------------------------------------------------------------------ + ExecutionContext (const lldb::TargetWP &target_wp, bool get_process); + ExecutionContext (const lldb::ProcessWP &process_wp); + ExecutionContext (const lldb::ThreadWP &thread_wp); + ExecutionContext (const lldb::StackFrameWP &frame_wp); + ExecutionContext (const ExecutionContextRef &exe_ctx_ref); + ExecutionContext (const ExecutionContextRef *exe_ctx_ref); + + // These two variants take in a locker, and grab the target, lock the API mutex into locker, then + // fill in the rest of the shared pointers. + ExecutionContext (const ExecutionContextRef &exe_ctx_ref, Mutex::Locker &locker); + ExecutionContext (const ExecutionContextRef *exe_ctx_ref, Mutex::Locker &locker); + //------------------------------------------------------------------ + // Create execution contexts from execution context scopes + //------------------------------------------------------------------ + ExecutionContext (ExecutionContextScope *exe_scope); + ExecutionContext (ExecutionContextScope &exe_scope); + + + ExecutionContext & + operator =(const ExecutionContext &rhs); + + bool + operator ==(const ExecutionContext &rhs) const; + + bool + operator !=(const ExecutionContext &rhs) const; + + //------------------------------------------------------------------ + /// Construct with process, thread, and frame index. + /// + /// Initialize with process \a p, thread \a t, and frame index \a f. + /// + /// @param[in] process + /// The process for this execution context. + /// + /// @param[in] thread + /// The thread for this execution context. + /// + /// @param[in] frame + /// The frame index for this execution context. + //------------------------------------------------------------------ + ExecutionContext (Process* process, + Thread *thread = NULL, + StackFrame * frame = NULL); + + + ~ExecutionContext(); + //------------------------------------------------------------------ + /// Clear the object's state. + /// + /// Sets the process and thread to NULL, and the frame index to an + /// invalid value. + //------------------------------------------------------------------ + void + Clear (); + + RegisterContext * + GetRegisterContext () const; + + ExecutionContextScope * + GetBestExecutionContextScope () const; + + uint32_t + GetAddressByteSize() const; + + //------------------------------------------------------------------ + /// Returns a pointer to the target object. + /// + /// The returned pointer might be NULL. Calling HasTargetScope(), + /// HasProcessScope(), HasThreadScope(), or HasFrameScope() + /// can help to pre-validate this pointer so that this accessor can + /// freely be used without having to check for NULL each time. + /// + /// @see ExecutionContext::HasTargetScope() const + /// @see ExecutionContext::HasProcessScope() const + /// @see ExecutionContext::HasThreadScope() const + /// @see ExecutionContext::HasFrameScope() const + //------------------------------------------------------------------ + Target * + GetTargetPtr () const; + + //------------------------------------------------------------------ + /// Returns a pointer to the process object. + /// + /// The returned pointer might be NULL. Calling HasProcessScope(), + /// HasThreadScope(), or HasFrameScope() can help to pre-validate + /// this pointer so that this accessor can freely be used without + /// having to check for NULL each time. + /// + /// @see ExecutionContext::HasProcessScope() const + /// @see ExecutionContext::HasThreadScope() const + /// @see ExecutionContext::HasFrameScope() const + //------------------------------------------------------------------ + Process * + GetProcessPtr () const; + + //------------------------------------------------------------------ + /// Returns a pointer to the thread object. + /// + /// The returned pointer might be NULL. Calling HasThreadScope() or + /// HasFrameScope() can help to pre-validate this pointer so that + /// this accessor can freely be used without having to check for + /// NULL each time. + /// + /// @see ExecutionContext::HasThreadScope() const + /// @see ExecutionContext::HasFrameScope() const + //------------------------------------------------------------------ + Thread * + GetThreadPtr () const + { + return m_thread_sp.get(); + } + + //------------------------------------------------------------------ + /// Returns a pointer to the frame object. + /// + /// The returned pointer might be NULL. Calling HasFrameScope(), + /// can help to pre-validate this pointer so that this accessor can + /// freely be used without having to check for NULL each time. + /// + /// @see ExecutionContext::HasFrameScope() const + //------------------------------------------------------------------ + StackFrame * + GetFramePtr () const + { + return m_frame_sp.get(); + } + + //------------------------------------------------------------------ + /// Returns a reference to the target object. + /// + /// Clients should call HasTargetScope(), HasProcessScope(), + /// HasThreadScope(), or HasFrameScope() prior to calling this + /// function to ensure that this ExecutionContext object contains + /// a valid target. + /// + /// @see ExecutionContext::HasTargetScope() const + /// @see ExecutionContext::HasProcessScope() const + /// @see ExecutionContext::HasThreadScope() const + /// @see ExecutionContext::HasFrameScope() const + //------------------------------------------------------------------ + Target & + GetTargetRef () const; + + //------------------------------------------------------------------ + /// Returns a reference to the process object. + /// + /// Clients should call HasProcessScope(), HasThreadScope(), or + /// HasFrameScope() prior to calling this function to ensure that + /// this ExecutionContext object contains a valid target. + /// + /// @see ExecutionContext::HasProcessScope() const + /// @see ExecutionContext::HasThreadScope() const + /// @see ExecutionContext::HasFrameScope() const + //------------------------------------------------------------------ + Process & + GetProcessRef () const; + + //------------------------------------------------------------------ + /// Returns a reference to the thread object. + /// + /// Clients should call HasThreadScope(), or HasFrameScope() prior + /// to calling this function to ensure that this ExecutionContext + /// object contains a valid target. + /// + /// @see ExecutionContext::HasThreadScope() const + /// @see ExecutionContext::HasFrameScope() const + //------------------------------------------------------------------ + Thread & + GetThreadRef () const; + + //------------------------------------------------------------------ + /// Returns a reference to the thread object. + /// + /// Clients should call HasFrameScope() prior to calling this + /// function to ensure that this ExecutionContext object contains + /// a valid target. + /// + /// @see ExecutionContext::HasFrameScope() const + //------------------------------------------------------------------ + StackFrame & + GetFrameRef () const; + + //------------------------------------------------------------------ + /// Get accessor to get the target shared pointer. + /// + /// The returned shared pointer is not guaranteed to be valid. + //------------------------------------------------------------------ + const lldb::TargetSP & + GetTargetSP () const + { + return m_target_sp; + } + + //------------------------------------------------------------------ + /// Get accessor to get the process shared pointer. + /// + /// The returned shared pointer is not guaranteed to be valid. + //------------------------------------------------------------------ + const lldb::ProcessSP & + GetProcessSP () const + { + return m_process_sp; + } + + //------------------------------------------------------------------ + /// Get accessor to get the thread shared pointer. + /// + /// The returned shared pointer is not guaranteed to be valid. + //------------------------------------------------------------------ + const lldb::ThreadSP & + GetThreadSP () const + { + return m_thread_sp; + } + + //------------------------------------------------------------------ + /// Get accessor to get the frame shared pointer. + /// + /// The returned shared pointer is not guaranteed to be valid. + //------------------------------------------------------------------ + const lldb::StackFrameSP & + GetFrameSP () const + { + return m_frame_sp; + } + + //------------------------------------------------------------------ + /// Set accessor to set only the target shared pointer. + //------------------------------------------------------------------ + void + SetTargetSP (const lldb::TargetSP &target_sp); + + //------------------------------------------------------------------ + /// Set accessor to set only the process shared pointer. + //------------------------------------------------------------------ + void + SetProcessSP (const lldb::ProcessSP &process_sp); + + //------------------------------------------------------------------ + /// Set accessor to set only the thread shared pointer. + //------------------------------------------------------------------ + void + SetThreadSP (const lldb::ThreadSP &thread_sp); + + //------------------------------------------------------------------ + /// Set accessor to set only the frame shared pointer. + //------------------------------------------------------------------ + void + SetFrameSP (const lldb::StackFrameSP &frame_sp); + + //------------------------------------------------------------------ + /// Set accessor to set only the target shared pointer from a target + /// pointer. + //------------------------------------------------------------------ + void + SetTargetPtr (Target* target); + + //------------------------------------------------------------------ + /// Set accessor to set only the process shared pointer from a + /// process pointer. + //------------------------------------------------------------------ + void + SetProcessPtr (Process *process); + + //------------------------------------------------------------------ + /// Set accessor to set only the thread shared pointer from a thread + /// pointer. + //------------------------------------------------------------------ + void + SetThreadPtr (Thread *thread); + + //------------------------------------------------------------------ + /// Set accessor to set only the frame shared pointer from a frame + /// pointer. + //------------------------------------------------------------------ + void + SetFramePtr (StackFrame *frame); + + //------------------------------------------------------------------ + // Set the execution context using a target shared pointer. + // + // If "target_sp" is valid, sets the target context to match and + // if "get_process" is true, sets the process shared pointer if + // the target currently has a process. + //------------------------------------------------------------------ + void + SetContext (const lldb::TargetSP &target_sp, bool get_process); + + //------------------------------------------------------------------ + // Set the execution context using a process shared pointer. + // + // If "process_sp" is valid, then set the process and target in this + // context. Thread and frame contexts will be cleared. + // If "process_sp" is not valid, all shared pointers are reset. + //------------------------------------------------------------------ + void + SetContext (const lldb::ProcessSP &process_sp); + + //------------------------------------------------------------------ + // Set the execution context using a thread shared pointer. + // + // If "thread_sp" is valid, then set the thread, process and target + // in this context. The frame context will be cleared. + // If "thread_sp" is not valid, all shared pointers are reset. + //------------------------------------------------------------------ + void + SetContext (const lldb::ThreadSP &thread_sp); + + //------------------------------------------------------------------ + // Set the execution context using a frame shared pointer. + // + // If "frame_sp" is valid, then set the frame, thread, process and + // target in this context + // If "frame_sp" is not valid, all shared pointers are reset. + //------------------------------------------------------------------ + void + SetContext (const lldb::StackFrameSP &frame_sp); + + //------------------------------------------------------------------ + /// Returns true the ExecutionContext object contains a valid + /// target. + /// + /// This function can be called after initializing an ExecutionContext + /// object, and if it returns true, calls to GetTargetPtr() and + /// GetTargetRef() do not need to be checked for validity. + //------------------------------------------------------------------ + bool + HasTargetScope () const; + + //------------------------------------------------------------------ + /// Returns true the ExecutionContext object contains a valid + /// target and process. + /// + /// This function can be called after initializing an ExecutionContext + /// object, and if it returns true, calls to GetTargetPtr() and + /// GetTargetRef(), GetProcessPtr(), and GetProcessRef(), do not + /// need to be checked for validity. + //------------------------------------------------------------------ + bool + HasProcessScope () const; + + //------------------------------------------------------------------ + /// Returns true the ExecutionContext object contains a valid + /// target, process, and thread. + /// + /// This function can be called after initializing an ExecutionContext + /// object, and if it returns true, calls to GetTargetPtr(), + /// GetTargetRef(), GetProcessPtr(), GetProcessRef(), GetThreadPtr(), + /// and GetThreadRef() do not need to be checked for validity. + //------------------------------------------------------------------ + bool + HasThreadScope () const; + + //------------------------------------------------------------------ + /// Returns true the ExecutionContext object contains a valid + /// target, process, thread and frame. + /// + /// This function can be called after initializing an ExecutionContext + /// object, and if it returns true, calls to GetTargetPtr(), + /// GetTargetRef(), GetProcessPtr(), GetProcessRef(), GetThreadPtr(), + /// GetThreadRef(), GetFramePtr(), and GetFrameRef() do not need + /// to be checked for validity. + //------------------------------------------------------------------ + bool + HasFrameScope () const; + +protected: + //------------------------------------------------------------------ + // Member variables + //------------------------------------------------------------------ + lldb::TargetSP m_target_sp; ///< The target that owns the process/thread/frame + lldb::ProcessSP m_process_sp; ///< The process that owns the thread/frame + lldb::ThreadSP m_thread_sp; ///< The thread that owns the frame + lldb::StackFrameSP m_frame_sp; ///< The stack frame in thread. +}; +} // namespace lldb_private + +#endif // liblldb_ExecutionContext_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Target/ExecutionContextScope.h b/contrib/llvm/tools/lldb/include/lldb/Target/ExecutionContextScope.h new file mode 100644 index 00000000000..7ba40971af2 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Target/ExecutionContextScope.h @@ -0,0 +1,74 @@ +//===-- ExecutionContextScope.h ---------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ExecutionContextScope_h_ +#define liblldb_ExecutionContextScope_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private.h" + +namespace lldb_private { + +//---------------------------------------------------------------------- +/// @class ExecutionContextScope ExecutionContextScope.h "lldb/Symbol/ExecutionContextScope.h" +/// @brief Inherit from this if your object can reconstruct its +/// execution context. +/// +/// Many objects that have pointers back to parent execution context +/// objects can inherit from this pure virtual class can reconstruct +/// their execution context without having to keep a complete +/// ExecutionContext object in the object state. Examples of these +/// objects include: Process, Thread, RegisterContext and StackFrame. +/// +/// Bbjects can contain a valid pointer to an instance of this so they +/// can reconstruct the execution context. +/// +/// Objects that adhere to this protocol can reconstruct enough of a +/// execution context to allow functions that take a execution contexts +/// to be called. +//---------------------------------------------------------------------- +class ExecutionContextScope +{ +public: + virtual + ~ExecutionContextScope () {} + + virtual lldb::TargetSP + CalculateTarget () = 0; + + virtual lldb::ProcessSP + CalculateProcess () = 0; + + virtual lldb::ThreadSP + CalculateThread () = 0; + + virtual lldb::StackFrameSP + CalculateStackFrame () = 0; + + //------------------------------------------------------------------ + /// Reconstruct the object's execution context into \a sc. + /// + /// The object should fill in as much of the ExecutionContextScope as it + /// can so function calls that require a execution context can be made + /// for the given object. + /// + /// @param[out] exe_ctx + /// A reference to an execution context object that gets filled + /// in. + //------------------------------------------------------------------ + virtual void + CalculateExecutionContext (ExecutionContext &exe_ctx) = 0; +}; + +} // namespace lldb_private + +#endif // liblldb_ExecutionContextScope_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Target/LanguageRuntime.h b/contrib/llvm/tools/lldb/include/lldb/Target/LanguageRuntime.h new file mode 100644 index 00000000000..93c0437da75 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Target/LanguageRuntime.h @@ -0,0 +1,113 @@ +//===-- LanguageRuntime.h ---------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_LanguageRuntime_h_ +#define liblldb_LanguageRuntime_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-public.h" +#include "lldb/Breakpoint/BreakpointResolver.h" +#include "lldb/Breakpoint/BreakpointResolverName.h" +#include "lldb/Core/PluginInterface.h" +#include "lldb/lldb-private.h" +#include "lldb/Core/ValueObject.h" +#include "lldb/Core/Value.h" +#include "lldb/Target/ExecutionContextScope.h" + +namespace lldb_private { + +class LanguageRuntime : + public PluginInterface +{ +public: + virtual + ~LanguageRuntime(); + + static LanguageRuntime* + FindPlugin (Process *process, lldb::LanguageType language); + + virtual lldb::LanguageType + GetLanguageType () const = 0; + + virtual bool + GetObjectDescription (Stream &str, ValueObject &object) = 0; + + virtual bool + GetObjectDescription (Stream &str, Value &value, ExecutionContextScope *exe_scope) = 0; + + // this call should return true if it could set the name and/or the type + virtual bool + GetDynamicTypeAndAddress (ValueObject &in_value, + lldb::DynamicValueType use_dynamic, + TypeAndOrName &class_type_or_name, + Address &address) = 0; + + // This should be a fast test to determine whether it is likely that this value would + // have a dynamic type. + virtual bool + CouldHaveDynamicValue (ValueObject &in_value) = 0; + + virtual void + SetExceptionBreakpoints () + { + } + + virtual void + ClearExceptionBreakpoints () + { + } + + virtual bool + ExceptionBreakpointsExplainStop (lldb::StopInfoSP stop_reason) + { + return false; + } + + static lldb::BreakpointSP + CreateExceptionBreakpoint (Target &target, + lldb::LanguageType language, + bool catch_bp, + bool throw_bp, + bool is_internal = false); + + static lldb::LanguageType + GetLanguageTypeFromString (const char *string); + + static const char * + GetNameForLanguageType (lldb::LanguageType language); + + Process * + GetProcess() + { + return m_process; + } + + virtual lldb::BreakpointResolverSP + CreateExceptionResolver (Breakpoint *bkpt, bool catch_bp, bool throw_bp) = 0; + + virtual lldb::SearchFilterSP + CreateExceptionSearchFilter (); + +protected: + //------------------------------------------------------------------ + // Classes that inherit from LanguageRuntime can see and modify these + //------------------------------------------------------------------ + + LanguageRuntime(Process *process); + Process *m_process; +private: + DISALLOW_COPY_AND_ASSIGN (LanguageRuntime); +}; + +} // namespace lldb_private + +#endif // liblldb_LanguageRuntime_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Target/Memory.h b/contrib/llvm/tools/lldb/include/lldb/Target/Memory.h new file mode 100644 index 00000000000..568bbcdf2f7 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Target/Memory.h @@ -0,0 +1,196 @@ +//===-- Memory.h ------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_Memory_h_ +#define liblldb_Memory_h_ + +// C Includes +// C++ Includes +#include +#include + +// Other libraries and framework includes + +// Project includes +#include "lldb/lldb-private.h" +#include "lldb/Core/RangeMap.h" +#include "lldb/Host/Mutex.h" + +namespace lldb_private { + //---------------------------------------------------------------------- + // A class to track memory that was read from a live process between + // runs. + //---------------------------------------------------------------------- + class MemoryCache + { + public: + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + MemoryCache (Process &process); + + ~MemoryCache (); + + void + Clear(bool clear_invalid_ranges = false); + + void + Flush (lldb::addr_t addr, size_t size); + + size_t + Read (lldb::addr_t addr, + void *dst, + size_t dst_len, + Error &error); + + uint32_t + GetMemoryCacheLineSize() const + { + return m_cache_line_byte_size ; + } + + void + AddInvalidRange (lldb::addr_t base_addr, lldb::addr_t byte_size); + + bool + RemoveInvalidRange (lldb::addr_t base_addr, lldb::addr_t byte_size); + + protected: + typedef std::map BlockMap; + typedef RangeArray InvalidRanges; + //------------------------------------------------------------------ + // Classes that inherit from MemoryCache can see and modify these + //------------------------------------------------------------------ + Process &m_process; + uint32_t m_cache_line_byte_size; + Mutex m_mutex; + BlockMap m_cache; + InvalidRanges m_invalid_ranges; + private: + DISALLOW_COPY_AND_ASSIGN (MemoryCache); + }; + + + class AllocatedBlock + { + public: + AllocatedBlock (lldb::addr_t addr, + uint32_t byte_size, + uint32_t permissions, + uint32_t chunk_size); + + ~AllocatedBlock (); + + lldb::addr_t + ReserveBlock (uint32_t size); + + bool + FreeBlock (lldb::addr_t addr); + + lldb::addr_t + GetBaseAddress () const + { + return m_addr; + } + + uint32_t + GetByteSize () const + { + return m_byte_size; + } + + uint32_t + GetPermissions () const + { + return m_permissions; + } + + uint32_t + GetChunkSize () const + { + return m_chunk_size; + } + + bool + Contains (lldb::addr_t addr) const + { + return ((addr >= m_addr) && addr < (m_addr + m_byte_size)); + } + protected: + uint32_t + TotalChunks () const + { + return m_byte_size / m_chunk_size; + } + + uint32_t + CalculateChunksNeededForSize (uint32_t size) const + { + return (size + m_chunk_size - 1) / m_chunk_size; + } + const lldb::addr_t m_addr; // Base address of this block of memory + const uint32_t m_byte_size; // 4GB of chunk should be enough... + const uint32_t m_permissions; // Permissions for this memory (logical OR of lldb::Permissions bits) + const uint32_t m_chunk_size; // The size of chunks that the memory at m_addr is divied up into + typedef std::map OffsetToChunkSize; + OffsetToChunkSize m_offset_to_chunk_size; + }; + + + //---------------------------------------------------------------------- + // A class that can track allocated memory and give out allocated memory + // without us having to make an allocate/deallocate call every time we + // need some memory in a process that is being debugged. + //---------------------------------------------------------------------- + class AllocatedMemoryCache + { + public: + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + AllocatedMemoryCache (Process &process); + + ~AllocatedMemoryCache (); + + void + Clear(); + + lldb::addr_t + AllocateMemory (size_t byte_size, + uint32_t permissions, + Error &error); + + bool + DeallocateMemory (lldb::addr_t ptr); + + protected: + typedef std::shared_ptr AllocatedBlockSP; + + AllocatedBlockSP + AllocatePage (uint32_t byte_size, + uint32_t permissions, + uint32_t chunk_size, + Error &error); + + + //------------------------------------------------------------------ + // Classes that inherit from MemoryCache can see and modify these + //------------------------------------------------------------------ + Process &m_process; + Mutex m_mutex; + typedef std::multimap PermissionsToBlockMap; + PermissionsToBlockMap m_memory_map; + + private: + DISALLOW_COPY_AND_ASSIGN (AllocatedMemoryCache); + }; + +} // namespace lldb_private + +#endif // liblldb_Memory_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Target/ObjCLanguageRuntime.h b/contrib/llvm/tools/lldb/include/lldb/Target/ObjCLanguageRuntime.h new file mode 100644 index 00000000000..7bac5725644 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Target/ObjCLanguageRuntime.h @@ -0,0 +1,604 @@ +//===-- ObjCLanguageRuntime.h ---------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ObjCLanguageRuntime_h_ +#define liblldb_ObjCLanguageRuntime_h_ + +// C Includes +// C++ Includes +#include +#include +#include + +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private.h" +#include "lldb/Core/PluginInterface.h" +#include "lldb/Symbol/Type.h" +#include "lldb/Symbol/TypeVendor.h" +#include "lldb/Target/LanguageRuntime.h" + +namespace lldb_private { + +class ClangUtilityFunction; + +class ObjCLanguageRuntime : + public LanguageRuntime +{ +public: + class MethodName + { + public: + enum Type + { + eTypeUnspecified, + eTypeClassMethod, + eTypeInstanceMethod + }; + + MethodName () : + m_full(), + m_class(), + m_category(), + m_selector(), + m_type (eTypeUnspecified), + m_category_is_valid (false) + { + } + + MethodName (const char *name, bool strict) : + m_full(), + m_class(), + m_category(), + m_selector(), + m_type (eTypeUnspecified), + m_category_is_valid (false) + { + SetName (name, strict); + } + + void + Clear(); + + bool + IsValid (bool strict) const + { + // If "strict" is true, the name must have everything specified including + // the leading "+" or "-" on the method name + if (strict && m_type == eTypeUnspecified) + return false; + // Other than that, m_full will only be filled in if the objective C + // name is valid. + return (bool)m_full; + } + + bool + HasCategory() + { + return (bool)GetCategory(); + } + + Type + GetType () const + { + return m_type; + } + + const ConstString & + GetFullName () const + { + return m_full; + } + + ConstString + GetFullNameWithoutCategory (bool empty_if_no_category); + + bool + SetName (const char *name, bool strict); + + const ConstString & + GetClassName (); + + const ConstString & + GetClassNameWithCategory (); + + const ConstString & + GetCategory (); + + const ConstString & + GetSelector (); + + // Get all possible names for a method. Examples: + // If name is "+[NSString(my_additions) myStringWithCString:]" + // names[0] => "+[NSString(my_additions) myStringWithCString:]" + // names[1] => "+[NSString myStringWithCString:]" + // If name is specified without the leading '+' or '-' like "[NSString(my_additions) myStringWithCString:]" + // names[0] => "+[NSString(my_additions) myStringWithCString:]" + // names[1] => "-[NSString(my_additions) myStringWithCString:]" + // names[2] => "+[NSString myStringWithCString:]" + // names[3] => "-[NSString myStringWithCString:]" + size_t + GetFullNames (std::vector &names, bool append); + protected: + ConstString m_full; // Full name: "+[NSString(my_additions) myStringWithCString:]" + ConstString m_class; // Class name: "NSString" + ConstString m_class_category; // Class with category: "NSString(my_additions)" + ConstString m_category; // Category: "my_additions" + ConstString m_selector; // Selector: "myStringWithCString:" + Type m_type; + bool m_category_is_valid; + + }; + typedef lldb::addr_t ObjCISA; + + class ClassDescriptor; + typedef std::shared_ptr ClassDescriptorSP; + + // the information that we want to support retrieving from an ObjC class + // this needs to be pure virtual since there are at least 2 different implementations + // of the runtime, and more might come + class ClassDescriptor + { + public: + + ClassDescriptor() : + m_is_kvo (eLazyBoolCalculate), + m_is_cf (eLazyBoolCalculate), + m_type_wp () + { + } + + virtual + ~ClassDescriptor () + { + } + + virtual ConstString + GetClassName () = 0; + + virtual ClassDescriptorSP + GetSuperclass () = 0; + + // virtual if any implementation has some other version-specific rules + // but for the known v1/v2 this is all that needs to be done + virtual bool + IsKVO () + { + if (m_is_kvo == eLazyBoolCalculate) + { + const char* class_name = GetClassName().AsCString(); + if (class_name && *class_name) + m_is_kvo = (LazyBool)(strstr(class_name,"NSKVONotifying_") == class_name); + } + return (m_is_kvo == eLazyBoolYes); + } + + // virtual if any implementation has some other version-specific rules + // but for the known v1/v2 this is all that needs to be done + virtual bool + IsCFType () + { + if (m_is_cf == eLazyBoolCalculate) + { + const char* class_name = GetClassName().AsCString(); + if (class_name && *class_name) + m_is_cf = (LazyBool)(strcmp(class_name,"__NSCFType") == 0 || + strcmp(class_name,"NSCFType") == 0); + } + return (m_is_cf == eLazyBoolYes); + } + + virtual bool + IsValid () = 0; + + virtual bool + GetTaggedPointerInfo (uint64_t* info_bits = NULL, + uint64_t* value_bits = NULL, + uint64_t* payload = NULL) = 0; + + virtual uint64_t + GetInstanceSize () = 0; + + // use to implement version-specific additional constraints on pointers + virtual bool + CheckPointer (lldb::addr_t value, + uint32_t ptr_size) const + { + return true; + } + + virtual ObjCISA + GetISA () = 0; + + // This should return true iff the interface could be completed + virtual bool + Describe (std::function const &superclass_func, + std::function const &instance_method_func, + std::function const &class_method_func, + std::function const &ivar_func) + { + return false; + } + + lldb::TypeSP + GetType () + { + return m_type_wp.lock(); + } + + void + SetType (const lldb::TypeSP &type_sp) + { + m_type_wp = type_sp; + } + + protected: + bool + IsPointerValid (lldb::addr_t value, + uint32_t ptr_size, + bool allow_NULLs = false, + bool allow_tagged = false, + bool check_version_specific = false) const; + + private: + LazyBool m_is_kvo; + LazyBool m_is_cf; + lldb::TypeWP m_type_wp; + }; + + virtual ClassDescriptorSP + GetClassDescriptor (ValueObject& in_value); + + ClassDescriptorSP + GetNonKVOClassDescriptor (ValueObject& in_value); + + virtual ClassDescriptorSP + GetClassDescriptorFromClassName (const ConstString &class_name); + + virtual ClassDescriptorSP + GetClassDescriptorFromISA (ObjCISA isa); + + ClassDescriptorSP + GetNonKVOClassDescriptor (ObjCISA isa); + + virtual + ~ObjCLanguageRuntime(); + + virtual lldb::LanguageType + GetLanguageType () const + { + return lldb::eLanguageTypeObjC; + } + + virtual bool + IsModuleObjCLibrary (const lldb::ModuleSP &module_sp) = 0; + + virtual bool + ReadObjCLibrary (const lldb::ModuleSP &module_sp) = 0; + + virtual bool + HasReadObjCLibrary () = 0; + + virtual lldb::ThreadPlanSP + GetStepThroughTrampolinePlan (Thread &thread, bool stop_others) = 0; + + lldb::addr_t + LookupInMethodCache (lldb::addr_t class_addr, lldb::addr_t sel); + + void + AddToMethodCache (lldb::addr_t class_addr, lldb::addr_t sel, lldb::addr_t impl_addr); + + TypeAndOrName + LookupInClassNameCache (lldb::addr_t class_addr); + + void + AddToClassNameCache (lldb::addr_t class_addr, const char *name, lldb::TypeSP type_sp); + + void + AddToClassNameCache (lldb::addr_t class_addr, const TypeAndOrName &class_or_type_name); + + lldb::TypeSP + LookupInCompleteClassCache (ConstString &name); + + virtual ClangUtilityFunction * + CreateObjectChecker (const char *) = 0; + + virtual ObjCRuntimeVersions + GetRuntimeVersion () + { + return eObjC_VersionUnknown; + } + + bool + IsValidISA(ObjCISA isa) + { + UpdateISAToDescriptorMap(); + return m_isa_to_descriptor.count(isa) > 0; + } + + virtual void + UpdateISAToDescriptorMapIfNeeded() = 0; + + void + UpdateISAToDescriptorMap() + { + if (m_process && m_process->GetStopID() != m_isa_to_descriptor_stop_id) + { + UpdateISAToDescriptorMapIfNeeded (); + } + } + + virtual ObjCISA + GetISA(const ConstString &name); + + virtual ConstString + GetActualTypeName(ObjCISA isa); + + virtual ObjCISA + GetParentClass(ObjCISA isa); + + virtual TypeVendor * + GetTypeVendor() + { + return NULL; + } + + // Finds the byte offset of the child_type ivar in parent_type. If it can't find the + // offset, returns LLDB_INVALID_IVAR_OFFSET. + + virtual size_t + GetByteOffsetForIvar (ClangASTType &parent_qual_type, const char *ivar_name); + + // Given the name of an Objective-C runtime symbol (e.g., ivar offset symbol), + // try to determine from the runtime what the value of that symbol would be. + // Useful when the underlying binary is stripped. + virtual lldb::addr_t + LookupRuntimeSymbol (const ConstString &name) + { + return LLDB_INVALID_ADDRESS; + } + + //------------------------------------------------------------------ + /// Chop up an objective C function prototype. + /// + /// Chop up an objective C function fullname and optionally fill in + /// any non-NULL ConstString objects. If a ConstString * is NULL, + /// then this name doesn't get filled in + /// + /// @param[in] name + /// A fully specified objective C function name. The string might + /// contain a category and it includes the leading "+" or "-" and + /// the square brackets, no types for the arguments, just the plain + /// selector. A few examples: + /// "-[NSStringDrawingContext init]" + /// "-[NSStringDrawingContext addString:inRect:]" + /// "-[NSString(NSStringDrawing) sizeWithAttributes:]" + /// "+[NSString(NSStringDrawing) usesFontLeading]" + /// + /// @param[out] class_name + /// If non-NULL, this string will be filled in with the class + /// name including the category. The examples above would return: + /// "NSStringDrawingContext" + /// "NSStringDrawingContext" + /// "NSString(NSStringDrawing)" + /// "NSString(NSStringDrawing)" + /// + /// @param[out] selector_name + /// If non-NULL, this string will be filled in with the selector + /// name. The examples above would return: + /// "init" + /// "addString:inRect:" + /// "sizeWithAttributes:" + /// "usesFontLeading" + /// + /// @param[out] name_sans_category + /// If non-NULL, this string will be filled in with the class + /// name _without_ the category. If there is no category, and empty + /// string will be returned (as the result would be normally returned + /// in the "class_name" argument). The examples above would return: + /// + /// + /// "-[NSString sizeWithAttributes:]" + /// "+[NSString usesFontLeading]" + /// + /// @param[out] class_name_sans_category + /// If non-NULL, this string will be filled in with the prototype + /// name _without_ the category. If there is no category, and empty + /// string will be returned (as this is already the value that was + /// passed in). The examples above would return: + /// + /// + /// "NSString" + /// "NSString" + /// + /// @return + /// Returns the number of strings that were successfully filled + /// in. + //------------------------------------------------------------------ +// static uint32_t +// ParseMethodName (const char *name, +// ConstString *class_name, // Class name (with category if there is one) +// ConstString *selector_name, // selector only +// ConstString *name_sans_category, // full function name with no category (empty if no category) +// ConstString *class_name_sans_category);// Class name without category (empty if no category) + + static bool + IsPossibleObjCMethodName (const char *name) + { + if (!name) + return false; + bool starts_right = (name[0] == '+' || name[0] == '-') && name[1] == '['; + bool ends_right = (name[strlen(name) - 1] == ']'); + return (starts_right && ends_right); + } + + static bool + IsPossibleObjCSelector (const char *name) + { + if (!name) + return false; + + if (strchr(name, ':') == NULL) + return true; + else if (name[strlen(name) - 1] == ':') + return true; + else + return false; + } + + bool + HasNewLiteralsAndIndexing () + { + if (m_has_new_literals_and_indexing == eLazyBoolCalculate) + { + if (CalculateHasNewLiteralsAndIndexing()) + m_has_new_literals_and_indexing = eLazyBoolYes; + else + m_has_new_literals_and_indexing = eLazyBoolNo; + } + + return (m_has_new_literals_and_indexing == eLazyBoolYes); + } + + virtual void + SymbolsDidLoad (const ModuleList& module_list) + { + m_negative_complete_class_cache.clear(); + } + +protected: + //------------------------------------------------------------------ + // Classes that inherit from ObjCLanguageRuntime can see and modify these + //------------------------------------------------------------------ + ObjCLanguageRuntime(Process *process); + + virtual bool CalculateHasNewLiteralsAndIndexing() + { + return false; + } + + + bool + ISAIsCached (ObjCISA isa) const + { + return m_isa_to_descriptor.find(isa) != m_isa_to_descriptor.end(); + } + + bool + AddClass (ObjCISA isa, const ClassDescriptorSP &descriptor_sp) + { + if (isa != 0) + { + m_isa_to_descriptor[isa] = descriptor_sp; + return true; + } + return false; + } + + bool + AddClass (ObjCISA isa, const ClassDescriptorSP &descriptor_sp, const char *class_name); + + bool + AddClass (ObjCISA isa, const ClassDescriptorSP &descriptor_sp, uint32_t class_name_hash) + { + if (isa != 0) + { + m_isa_to_descriptor[isa] = descriptor_sp; + m_hash_to_isa_map.insert(std::make_pair(class_name_hash, isa)); + return true; + } + return false; + } + +private: + // We keep a map of ->Implementation so we don't have to call the resolver + // function over and over. + + // FIXME: We need to watch for the loading of Protocols, and flush the cache for any + // class that we see so changed. + + struct ClassAndSel + { + ClassAndSel() + { + sel_addr = LLDB_INVALID_ADDRESS; + class_addr = LLDB_INVALID_ADDRESS; + } + ClassAndSel (lldb::addr_t in_sel_addr, lldb::addr_t in_class_addr) : + class_addr (in_class_addr), + sel_addr(in_sel_addr) + { + } + bool operator== (const ClassAndSel &rhs) + { + if (class_addr == rhs.class_addr + && sel_addr == rhs.sel_addr) + return true; + else + return false; + } + + bool operator< (const ClassAndSel &rhs) const + { + if (class_addr < rhs.class_addr) + return true; + else if (class_addr > rhs.class_addr) + return false; + else + { + if (sel_addr < rhs.sel_addr) + return true; + else + return false; + } + } + + lldb::addr_t class_addr; + lldb::addr_t sel_addr; + }; + + typedef std::map MsgImplMap; + typedef std::map ISAToDescriptorMap; + typedef std::multimap HashToISAMap; + typedef ISAToDescriptorMap::iterator ISAToDescriptorIterator; + typedef HashToISAMap::iterator HashToISAIterator; + + MsgImplMap m_impl_cache; + LazyBool m_has_new_literals_and_indexing; + ISAToDescriptorMap m_isa_to_descriptor; + HashToISAMap m_hash_to_isa_map; + +protected: + uint32_t m_isa_to_descriptor_stop_id; + + typedef std::map CompleteClassMap; + CompleteClassMap m_complete_class_cache; + + struct ConstStringSetHelpers { + size_t operator () (const ConstString& arg) const // for hashing + { + return (size_t)arg.GetCString(); + } + bool operator () (const ConstString& arg1, const ConstString& arg2) const // for equality + { + return arg1.operator==(arg2); + } + }; + typedef std::unordered_set CompleteClassSet; + CompleteClassSet m_negative_complete_class_cache; + + ISAToDescriptorIterator + GetDescriptorIterator (const ConstString &name); + + DISALLOW_COPY_AND_ASSIGN (ObjCLanguageRuntime); +}; + +} // namespace lldb_private + +#endif // liblldb_ObjCLanguageRuntime_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Target/OperatingSystem.h b/contrib/llvm/tools/lldb/include/lldb/Target/OperatingSystem.h new file mode 100644 index 00000000000..f1c0eb06026 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Target/OperatingSystem.h @@ -0,0 +1,101 @@ +//===-- OperatingSystem.h ----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_OperatingSystem_h_ +#define liblldb_OperatingSystem_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes + +#include "lldb/lldb-private.h" +#include "lldb/Core/PluginInterface.h" + +namespace lldb_private { + +//---------------------------------------------------------------------- +/// @class OperatingSystem OperatingSystem.h "lldb/Target/OperatingSystem.h" +/// @brief A plug-in interface definition class for halted OS helpers. +/// +/// Halted OS plug-ins can be used by any process to locate and create +/// OS objects, like threads, during the lifetime of a debug session. +/// This is commonly used when attaching to an operating system that is +/// halted, such as when debugging over JTAG or connecting to low level +/// kernel debug services. +//---------------------------------------------------------------------- + +class OperatingSystem : + public PluginInterface + +{ +public: + //------------------------------------------------------------------ + /// Find a halted OS plugin for a given process. + /// + /// Scans the installed OperatingSystem plug-ins and tries to find + /// an instance that matches the current target triple and + /// executable. + /// + /// @param[in] process + /// The process for which to try and locate a halted OS + /// plug-in instance. + /// + /// @param[in] plugin_name + /// An optional name of a specific halted OS plug-in that + /// should be used. If NULL, pick the best plug-in. + //------------------------------------------------------------------ + static OperatingSystem* + FindPlugin (Process *process, const char *plugin_name); + + //------------------------------------------------------------------ + // Class Methods + //------------------------------------------------------------------ + OperatingSystem (Process *process); + + virtual + ~OperatingSystem(); + + //------------------------------------------------------------------ + // Plug-in Methods + //------------------------------------------------------------------ + virtual bool + UpdateThreadList (ThreadList &old_thread_list, + ThreadList &real_thread_list, + ThreadList &new_thread_list) = 0; + + virtual void + ThreadWasSelected (Thread *thread) = 0; + + virtual lldb::RegisterContextSP + CreateRegisterContextForThread (Thread *thread, lldb::addr_t reg_data_addr) = 0; + + virtual lldb::StopInfoSP + CreateThreadStopReason (Thread *thread) = 0; + + virtual lldb::ThreadSP + CreateThread (lldb::tid_t tid, lldb::addr_t context) + { + return lldb::ThreadSP(); + } + + virtual bool + IsOperatingSystemPluginThread (const lldb::ThreadSP &thread_sp); + +protected: + //------------------------------------------------------------------ + // Member variables. + //------------------------------------------------------------------ + Process* m_process; ///< The process that this dynamic loader plug-in is tracking. +private: + DISALLOW_COPY_AND_ASSIGN (OperatingSystem); +}; + +} // namespace lldb_private + +#endif // #ifndef liblldb_OperatingSystem_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Target/PathMappingList.h b/contrib/llvm/tools/lldb/include/lldb/Target/PathMappingList.h new file mode 100644 index 00000000000..b5bcbbfd768 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Target/PathMappingList.h @@ -0,0 +1,171 @@ +//===-- PathMappingList.h ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_PathMappingList_h_ +#define liblldb_PathMappingList_h_ + +// C Includes +// C++ Includes +#include +#include +// Other libraries and framework includes +#include "lldb/Core/ConstString.h" +#include "lldb/Core/Error.h" +// Project includes + +namespace lldb_private { + +class PathMappingList +{ +public: + + typedef void (*ChangedCallback) (const PathMappingList &path_list, + void *baton); + + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + PathMappingList (); + + PathMappingList (ChangedCallback callback, + void *callback_baton); + + PathMappingList (const PathMappingList &rhs); + + ~PathMappingList (); + + const PathMappingList & + operator =(const PathMappingList &rhs); + + void + Append (const ConstString &path, const ConstString &replacement, bool notify); + + void + Append (const PathMappingList &rhs, bool notify); + + void + Clear (bool notify); + + // By default, dump all pairs. + void + Dump (Stream *s, int pair_index=-1); + + bool + IsEmpty() const + { + return m_pairs.empty(); + } + + size_t + GetSize () const + { + return m_pairs.size(); + } + + bool + GetPathsAtIndex (uint32_t idx, ConstString &path, ConstString &new_path) const; + + void + Insert (const ConstString &path, + const ConstString &replacement, + uint32_t insert_idx, + bool notify); + + bool + Remove (off_t index, bool notify); + + bool + Remove (const ConstString &path, bool notify); + + bool + Replace (const ConstString &path, + const ConstString &replacement, + bool notify); + + bool + Replace (const ConstString &path, + const ConstString &replacement, + uint32_t index, + bool notify); + bool + RemapPath (const ConstString &path, ConstString &new_path) const; + + //------------------------------------------------------------------ + /// Remaps a source file given \a path into \a new_path. + /// + /// Remaps \a path if any source remappings match. This function + /// does NOT stat the file system so it can be used in tight loops + /// where debug info is being parsed. + /// + /// @param[in] path + /// The original source file path to try and remap. + /// + /// @param[out] new_path + /// The newly remapped filespec that is may or may not exist. + /// + /// @return + /// /b true if \a path was successfully located and \a new_path + /// is filled in with a new source path, \b false otherwise. + //------------------------------------------------------------------ + bool + RemapPath (const char *path, std::string &new_path) const; + + + //------------------------------------------------------------------ + /// Finds a source file given a file spec using the path remappings. + /// + /// Tries to resolve \a orig_spec by checking the path remappings. + /// It makes sure the file exists by checking with the file system, + /// so this call can be expensive if the remappings are on a network + /// or are even on the local file system, so use this function + /// sparingly (not in a tight debug info parsing loop). + /// + /// @param[in] orig_spec + /// The original source file path to try and remap. + /// + /// @param[out] new_spec + /// The newly remapped filespec that is guaranteed to exist. + /// + /// @return + /// /b true if \a orig_spec was successfully located and + /// \a new_spec is filled in with an existing file spec, + /// \b false otherwise. + //------------------------------------------------------------------ + bool + FindFile (const FileSpec &orig_spec, FileSpec &new_spec) const; + + uint32_t + FindIndexForPath (const ConstString &path) const; + + uint32_t + GetModificationID() const + { + return m_mod_id; + } +protected: + typedef std::pair pair; + typedef std::vector collection; + typedef collection::iterator iterator; + typedef collection::const_iterator const_iterator; + + iterator + FindIteratorForPath (const ConstString &path); + + const_iterator + FindIteratorForPath (const ConstString &path) const; + + collection m_pairs; + ChangedCallback m_callback; + void * m_callback_baton; + uint32_t m_mod_id; // Incremented anytime anything is added or removed. +}; + +} // namespace lldb_private + +#endif // liblldb_PathMappingList_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Target/Platform.h b/contrib/llvm/tools/lldb/include/lldb/Target/Platform.h new file mode 100644 index 00000000000..b0a07946e74 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Target/Platform.h @@ -0,0 +1,755 @@ +//===-- Platform.h ----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_Platform_h_ +#define liblldb_Platform_h_ + +// C Includes +// C++ Includes +#include +#include +#include + +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-public.h" +#include "lldb/Core/ArchSpec.h" +#include "lldb/Core/ConstString.h" +#include "lldb/Core/PluginInterface.h" +#include "lldb/Host/Mutex.h" + +namespace lldb_private { + + //---------------------------------------------------------------------- + /// @class Platform Platform.h "lldb/Target/Platform.h" + /// @brief A plug-in interface definition class for debug platform that + /// includes many platform abilities such as: + /// @li getting platform information such as supported architectures, + /// supported binary file formats and more + /// @li launching new processes + /// @li attaching to existing processes + /// @li download/upload files + /// @li execute shell commands + /// @li listing and getting info for existing processes + /// @li attaching and possibly debugging the platform's kernel + //---------------------------------------------------------------------- + class Platform : public PluginInterface + { + public: + + //------------------------------------------------------------------ + /// Get the native host platform plug-in. + /// + /// There should only be one of these for each host that LLDB runs + /// upon that should be statically compiled in and registered using + /// preprocessor macros or other similar build mechanisms in a + /// PlatformSubclass::Initialize() function. + /// + /// This platform will be used as the default platform when launching + /// or attaching to processes unless another platform is specified. + //------------------------------------------------------------------ + static lldb::PlatformSP + GetDefaultPlatform (); + + static lldb::PlatformSP + GetPlatformForArchitecture (const ArchSpec &arch, + ArchSpec *platform_arch_ptr); + + static const char * + GetHostPlatformName (); + + static void + SetDefaultPlatform (const lldb::PlatformSP &platform_sp); + + static lldb::PlatformSP + Create (const char *platform_name, Error &error); + + static lldb::PlatformSP + Create (const ArchSpec &arch, ArchSpec *platform_arch_ptr, Error &error); + + static uint32_t + GetNumConnectedRemotePlatforms (); + + static lldb::PlatformSP + GetConnectedRemotePlatformAtIndex (uint32_t idx); + + //------------------------------------------------------------------ + /// Default Constructor + //------------------------------------------------------------------ + Platform (bool is_host_platform); + + //------------------------------------------------------------------ + /// Destructor. + /// + /// The destructor is virtual since this class is designed to be + /// inherited from by the plug-in instance. + //------------------------------------------------------------------ + virtual + ~Platform(); + + //------------------------------------------------------------------ + /// Find a platform plugin for a given process. + /// + /// Scans the installed Platform plug-ins and tries to find + /// an instance that can be used for \a process + /// + /// @param[in] process + /// The process for which to try and locate a platform + /// plug-in instance. + /// + /// @param[in] plugin_name + /// An optional name of a specific platform plug-in that + /// should be used. If NULL, pick the best plug-in. + //------------------------------------------------------------------ + static Platform* + FindPlugin (Process *process, const ConstString &plugin_name); + + //------------------------------------------------------------------ + /// Set the target's executable based off of the existing + /// architecture information in \a target given a path to an + /// executable \a exe_file. + /// + /// Each platform knows the architectures that it supports and can + /// select the correct architecture slice within \a exe_file by + /// inspecting the architecture in \a target. If the target had an + /// architecture specified, then in can try and obey that request + /// and optionally fail if the architecture doesn't match up. + /// If no architecture is specified, the platform should select the + /// default architecture from \a exe_file. Any application bundles + /// or executable wrappers can also be inspected for the actual + /// application binary within the bundle that should be used. + /// + /// @return + /// Returns \b true if this Platform plug-in was able to find + /// a suitable executable, \b false otherwise. + //------------------------------------------------------------------ + virtual Error + ResolveExecutable (const FileSpec &exe_file, + const ArchSpec &arch, + lldb::ModuleSP &module_sp, + const FileSpecList *module_search_paths_ptr); + + + //------------------------------------------------------------------ + /// Find a symbol file given a symbol file module specification. + /// + /// Each platform might have tricks to find symbol files for an + /// executable given information in a symbol file ModuleSpec. Some + /// platforms might also support symbol files that are bundles and + /// know how to extract the right symbol file given a bundle. + /// + /// @param[in] target + /// The target in which we are trying to resolve the symbol file. + /// The target has a list of modules that we might be able to + /// use in order to help find the right symbol file. If the + /// "m_file" or "m_platform_file" entries in the \a sym_spec + /// are filled in, then we might be able to locate a module in + /// the target, extract its UUID and locate a symbol file. + /// If just the "m_uuid" is specified, then we might be able + /// to find the module in the target that matches that UUID + /// and pair the symbol file along with it. If just "m_symbol_file" + /// is specified, we can use a variety of tricks to locate the + /// symbols in an SDK, PDK, or other development kit location. + /// + /// @param[in] sym_spec + /// A module spec that describes some information about the + /// symbol file we are trying to resolve. The ModuleSpec might + /// contain the following: + /// m_file - A full or partial path to an executable from the + /// target (might be empty). + /// m_platform_file - Another executable hint that contains + /// the path to the file as known on the + /// local/remote platform. + /// m_symbol_file - A full or partial path to a symbol file + /// or symbol bundle that should be used when + /// trying to resolve the symbol file. + /// m_arch - The architecture we are looking for when resolving + /// the symbol file. + /// m_uuid - The UUID of the executable and symbol file. This + /// can often be used to match up an exectuable with + /// a symbol file, or resolve an symbol file in a + /// symbol file bundle. + /// + /// @param[out] sym_file + /// The resolved symbol file spec if the returned error + /// indicates succes. + /// + /// @return + /// Returns an error that describes success or failure. + //------------------------------------------------------------------ + virtual Error + ResolveSymbolFile (Target &target, + const ModuleSpec &sym_spec, + FileSpec &sym_file); + + //------------------------------------------------------------------ + /// Resolves the FileSpec to a (possibly) remote path. Remote + /// platforms must override this to resolve to a path on the remote + /// side. + //------------------------------------------------------------------ + virtual bool + ResolveRemotePath (const FileSpec &platform_path, + FileSpec &resolved_platform_path); + + bool + GetOSVersion (uint32_t &major, + uint32_t &minor, + uint32_t &update); + + bool + SetOSVersion (uint32_t major, + uint32_t minor, + uint32_t update); + + bool + GetOSBuildString (std::string &s); + + bool + GetOSKernelDescription (std::string &s); + + // Returns the the hostname if we are connected, else the short plugin + // name. + ConstString + GetName (); + + virtual const char * + GetHostname (); + + virtual const char * + GetDescription () = 0; + + //------------------------------------------------------------------ + /// Report the current status for this platform. + /// + /// The returned string usually involves returning the OS version + /// (if available), and any SDK directory that might be being used + /// for local file caching, and if connected a quick blurb about + /// what this platform is connected to. + //------------------------------------------------------------------ + virtual void + GetStatus (Stream &strm); + + //------------------------------------------------------------------ + // Subclasses must be able to fetch the current OS version + // + // Remote classes must be connected for this to succeed. Local + // subclasses don't need to override this function as it will just + // call the Host::GetOSVersion(). + //------------------------------------------------------------------ + virtual bool + GetRemoteOSVersion () + { + return false; + } + + virtual bool + GetRemoteOSBuildString (std::string &s) + { + s.clear(); + return false; + } + + virtual bool + GetRemoteOSKernelDescription (std::string &s) + { + s.clear(); + return false; + } + + // Remote Platform subclasses need to override this function + virtual ArchSpec + GetRemoteSystemArchitecture () + { + return ArchSpec(); // Return an invalid architecture + } + + virtual const char * + GetUserName (uint32_t uid); + + virtual const char * + GetGroupName (uint32_t gid); + + //------------------------------------------------------------------ + /// Locate a file for a platform. + /// + /// The default implementation of this function will return the same + /// file patch in \a local_file as was in \a platform_file. + /// + /// @param[in] platform_file + /// The platform file path to locate and cache locally. + /// + /// @param[in] uuid_ptr + /// If we know the exact UUID of the file we are looking for, it + /// can be specified. If it is not specified, we might now know + /// the exact file. The UUID is usually some sort of MD5 checksum + /// for the file and is sometimes known by dynamic linkers/loaders. + /// If the UUID is known, it is best to supply it to platform + /// file queries to ensure we are finding the correct file, not + /// just a file at the correct path. + /// + /// @param[out] local_file + /// A locally cached version of the platform file. For platforms + /// that describe the current host computer, this will just be + /// the same file. For remote platforms, this file might come from + /// and SDK directory, or might need to be sync'ed over to the + /// current machine for efficient debugging access. + /// + /// @return + /// An error object. + //------------------------------------------------------------------ + virtual Error + GetFile (const FileSpec &platform_file, + const UUID *uuid_ptr, + FileSpec &local_file); + + //---------------------------------------------------------------------- + // Locate the scripting resource given a module specification. + // + // Locating the file should happen only on the local computer or using + // the current computers global settings. + //---------------------------------------------------------------------- + virtual FileSpecList + LocateExecutableScriptingResources (Target *target, + Module &module); + + virtual Error + GetSharedModule (const ModuleSpec &module_spec, + lldb::ModuleSP &module_sp, + const FileSpecList *module_search_paths_ptr, + lldb::ModuleSP *old_module_sp_ptr, + bool *did_create_ptr); + + virtual Error + ConnectRemote (Args& args); + + virtual Error + DisconnectRemote (); + + //------------------------------------------------------------------ + /// Get the platform's supported architectures in the order in which + /// they should be searched. + /// + /// @param[in] idx + /// A zero based architecture index + /// + /// @param[out] arch + /// A copy of the archgitecture at index if the return value is + /// \b true. + /// + /// @return + /// \b true if \a arch was filled in and is valid, \b false + /// otherwise. + //------------------------------------------------------------------ + virtual bool + GetSupportedArchitectureAtIndex (uint32_t idx, ArchSpec &arch) = 0; + + virtual size_t + GetSoftwareBreakpointTrapOpcode (Target &target, + BreakpointSite *bp_site) = 0; + + //------------------------------------------------------------------ + /// Launch a new process on a platform, not necessarily for + /// debugging, it could be just for running the process. + //------------------------------------------------------------------ + virtual Error + LaunchProcess (ProcessLaunchInfo &launch_info); + + //------------------------------------------------------------------ + /// Lets a platform answer if it is compatible with a given + /// architecture and the target triple contained within. + //------------------------------------------------------------------ + virtual bool + IsCompatibleArchitecture (const ArchSpec &arch, + bool exact_arch_match, + ArchSpec *compatible_arch_ptr); + + //------------------------------------------------------------------ + /// Not all platforms will support debugging a process by spawning + /// somehow halted for a debugger (specified using the + /// "eLaunchFlagDebug" launch flag) and then attaching. If your + /// platform doesn't support this, override this function and return + /// false. + //------------------------------------------------------------------ + virtual bool + CanDebugProcess () + { + return true; + } + + //------------------------------------------------------------------ + /// Subclasses should NOT need to implement this function as it uses + /// the Platform::LaunchProcess() followed by Platform::Attach () + //------------------------------------------------------------------ + lldb::ProcessSP + DebugProcess (ProcessLaunchInfo &launch_info, + Debugger &debugger, + Target *target, // Can be NULL, if NULL create a new target, else use existing one + Listener &listener, + Error &error); + + //------------------------------------------------------------------ + /// Attach to an existing process using a process ID. + /// + /// Each platform subclass needs to implement this function and + /// attempt to attach to the process with the process ID of \a pid. + /// The platform subclass should return an appropriate ProcessSP + /// subclass that is attached to the process, or an empty shared + /// pointer with an appriopriate error. + /// + /// @param[in] pid + /// The process ID that we should attempt to attach to. + /// + /// @return + /// An appropriate ProcessSP containing a valid shared pointer + /// to the default Process subclass for the platform that is + /// attached to the process, or an empty shared pointer with an + /// appriopriate error fill into the \a error object. + //------------------------------------------------------------------ + virtual lldb::ProcessSP + Attach (ProcessAttachInfo &attach_info, + Debugger &debugger, + Target *target, // Can be NULL, if NULL create a new target, else use existing one + Listener &listener, + Error &error) = 0; + + //------------------------------------------------------------------ + /// Attach to an existing process by process name. + /// + /// This function is not meant to be overridden by Process + /// subclasses. It will first call + /// Process::WillAttach (const char *) and if that returns \b + /// true, Process::DoAttach (const char *) will be called to + /// actually do the attach. If DoAttach returns \b true, then + /// Process::DidAttach() will be called. + /// + /// @param[in] process_name + /// A process name to match against the current process list. + /// + /// @return + /// Returns \a pid if attaching was successful, or + /// LLDB_INVALID_PROCESS_ID if attaching fails. + //------------------------------------------------------------------ +// virtual lldb::ProcessSP +// Attach (const char *process_name, +// bool wait_for_launch, +// Error &error) = 0; + + //------------------------------------------------------------------ + // The base class Platform will take care of the host platform. + // Subclasses will need to fill in the remote case. + //------------------------------------------------------------------ + virtual uint32_t + FindProcesses (const ProcessInstanceInfoMatch &match_info, + ProcessInstanceInfoList &proc_infos); + + virtual bool + GetProcessInfo (lldb::pid_t pid, ProcessInstanceInfo &proc_info); + + //------------------------------------------------------------------ + // Set a breakpoint on all functions that can end up creating a thread + // for this platform. This is needed when running expressions and + // also for process control. + //------------------------------------------------------------------ + virtual lldb::BreakpointSP + SetThreadCreationBreakpoint (Target &target); + + + const std::string & + GetRemoteURL () const + { + return m_remote_url; + } + + bool + IsHost () const + { + return m_is_host; // Is this the default host platform? + } + + bool + IsRemote () const + { + return !m_is_host; + } + + virtual bool + IsConnected () const + { + // Remote subclasses should override this function + return IsHost(); + } + + const ArchSpec & + GetSystemArchitecture(); + + void + SetSystemArchitecture (const ArchSpec &arch) + { + m_system_arch = arch; + if (IsHost()) + m_os_version_set_while_connected = m_system_arch.IsValid(); + } + + // Used for column widths + size_t + GetMaxUserIDNameLength() const + { + return m_max_uid_name_len; + } + // Used for column widths + size_t + GetMaxGroupIDNameLength() const + { + return m_max_gid_name_len; + } + + const ConstString & + GetSDKRootDirectory () const + { + return m_sdk_sysroot; + } + + void + SetSDKRootDirectory (const ConstString &dir) + { + m_sdk_sysroot = dir; + } + + const ConstString & + GetSDKBuild () const + { + return m_sdk_build; + } + + void + SetSDKBuild (const ConstString &sdk_build) + { + m_sdk_build = sdk_build; + } + + // There may be modules that we don't want to find by default for operations like "setting breakpoint by name". + // The platform will return "true" from this call if the passed in module happens to be one of these. + + virtual bool + ModuleIsExcludedForNonModuleSpecificSearches (Target &target, const lldb::ModuleSP &module_sp) + { + return false; + } + + virtual size_t + GetEnvironment (StringList &environment); + + protected: + bool m_is_host; + // Set to true when we are able to actually set the OS version while + // being connected. For remote platforms, we might set the version ahead + // of time before we actually connect and this version might change when + // we actually connect to a remote platform. For the host platform this + // will be set to the once we call Host::GetOSVersion(). + bool m_os_version_set_while_connected; + bool m_system_arch_set_while_connected; + ConstString m_sdk_sysroot; // the root location of where the SDK files are all located + ConstString m_sdk_build; + std::string m_remote_url; + std::string m_name; + uint32_t m_major_os_version; + uint32_t m_minor_os_version; + uint32_t m_update_os_version; + ArchSpec m_system_arch; // The architecture of the kernel or the remote platform + typedef std::map IDToNameMap; + Mutex m_uid_map_mutex; + Mutex m_gid_map_mutex; + IDToNameMap m_uid_map; + IDToNameMap m_gid_map; + size_t m_max_uid_name_len; + size_t m_max_gid_name_len; + + const char * + GetCachedUserName (uint32_t uid) + { + Mutex::Locker locker (m_uid_map_mutex); + IDToNameMap::iterator pos = m_uid_map.find (uid); + if (pos != m_uid_map.end()) + { + // return the empty string if our string is NULL + // so we can tell when things were in the negative + // cached (didn't find a valid user name, don't keep + // trying) + return pos->second.AsCString(""); + } + return NULL; + } + + const char * + SetCachedUserName (uint32_t uid, const char *name, size_t name_len) + { + Mutex::Locker locker (m_uid_map_mutex); + ConstString const_name (name); + m_uid_map[uid] = const_name; + if (m_max_uid_name_len < name_len) + m_max_uid_name_len = name_len; + // Const strings lives forever in our const string pool, so we can return the const char * + return const_name.GetCString(); + } + + void + SetUserNameNotFound (uint32_t uid) + { + Mutex::Locker locker (m_uid_map_mutex); + m_uid_map[uid] = ConstString(); + } + + + void + ClearCachedUserNames () + { + Mutex::Locker locker (m_uid_map_mutex); + m_uid_map.clear(); + } + + const char * + GetCachedGroupName (uint32_t gid) + { + Mutex::Locker locker (m_gid_map_mutex); + IDToNameMap::iterator pos = m_gid_map.find (gid); + if (pos != m_gid_map.end()) + { + // return the empty string if our string is NULL + // so we can tell when things were in the negative + // cached (didn't find a valid group name, don't keep + // trying) + return pos->second.AsCString(""); + } + return NULL; + } + + const char * + SetCachedGroupName (uint32_t gid, const char *name, size_t name_len) + { + Mutex::Locker locker (m_gid_map_mutex); + ConstString const_name (name); + m_gid_map[gid] = const_name; + if (m_max_gid_name_len < name_len) + m_max_gid_name_len = name_len; + // Const strings lives forever in our const string pool, so we can return the const char * + return const_name.GetCString(); + } + + void + SetGroupNameNotFound (uint32_t gid) + { + Mutex::Locker locker (m_gid_map_mutex); + m_gid_map[gid] = ConstString(); + } + + void + ClearCachedGroupNames () + { + Mutex::Locker locker (m_gid_map_mutex); + m_gid_map.clear(); + } + + private: + DISALLOW_COPY_AND_ASSIGN (Platform); + }; + + + class PlatformList + { + public: + PlatformList() : + m_mutex (Mutex::eMutexTypeRecursive), + m_platforms (), + m_selected_platform_sp() + { + } + + ~PlatformList() + { + } + + void + Append (const lldb::PlatformSP &platform_sp, bool set_selected) + { + Mutex::Locker locker (m_mutex); + m_platforms.push_back (platform_sp); + if (set_selected) + m_selected_platform_sp = m_platforms.back(); + } + + size_t + GetSize() + { + Mutex::Locker locker (m_mutex); + return m_platforms.size(); + } + + lldb::PlatformSP + GetAtIndex (uint32_t idx) + { + lldb::PlatformSP platform_sp; + { + Mutex::Locker locker (m_mutex); + if (idx < m_platforms.size()) + platform_sp = m_platforms[idx]; + } + return platform_sp; + } + + //------------------------------------------------------------------ + /// Select the active platform. + /// + /// In order to debug remotely, other platform's can be remotely + /// connected to and set as the selected platform for any subsequent + /// debugging. This allows connection to remote targets and allows + /// the ability to discover process info, launch and attach to remote + /// processes. + //------------------------------------------------------------------ + lldb::PlatformSP + GetSelectedPlatform () + { + Mutex::Locker locker (m_mutex); + if (!m_selected_platform_sp && !m_platforms.empty()) + m_selected_platform_sp = m_platforms.front(); + + return m_selected_platform_sp; + } + + void + SetSelectedPlatform (const lldb::PlatformSP &platform_sp) + { + if (platform_sp) + { + Mutex::Locker locker (m_mutex); + const size_t num_platforms = m_platforms.size(); + for (size_t idx=0; idx collection; + mutable Mutex m_mutex; + collection m_platforms; + lldb::PlatformSP m_selected_platform_sp; + + private: + DISALLOW_COPY_AND_ASSIGN (PlatformList); + }; +} // namespace lldb_private + +#endif // liblldb_Platform_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Target/Process.h b/contrib/llvm/tools/lldb/include/lldb/Target/Process.h new file mode 100644 index 00000000000..ef89a1eb141 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Target/Process.h @@ -0,0 +1,3786 @@ +//===-- Process.h -----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_Process_h_ +#define liblldb_Process_h_ + +// C Includes +#include +#include + +// C++ Includes +#include +#include +#include + +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private.h" +#include "lldb/Core/ArchSpec.h" +#include "lldb/Core/Broadcaster.h" +#include "lldb/Core/Communication.h" +#include "lldb/Core/Error.h" +#include "lldb/Core/Event.h" +#include "lldb/Core/RangeMap.h" +#include "lldb/Core/StringList.h" +#include "lldb/Core/ThreadSafeValue.h" +#include "lldb/Core/PluginInterface.h" +#include "lldb/Core/UserSettingsController.h" +#include "lldb/Breakpoint/BreakpointSiteList.h" +#include "lldb/Expression/ClangPersistentVariables.h" +#include "lldb/Expression/IRDynamicChecks.h" +#include "lldb/Host/FileSpec.h" +#include "lldb/Host/Host.h" +#include "lldb/Host/ProcessRunLock.h" +#include "lldb/Interpreter/Args.h" +#include "lldb/Interpreter/Options.h" +#include "lldb/Target/ExecutionContextScope.h" +#include "lldb/Target/Memory.h" +#include "lldb/Target/ThreadList.h" +#include "lldb/Target/UnixSignals.h" +#include "lldb/Utility/PseudoTerminal.h" + +namespace lldb_private { + +//---------------------------------------------------------------------- +// ProcessProperties +//---------------------------------------------------------------------- +class ProcessProperties : public Properties +{ +public: + ProcessProperties(bool is_global); + + virtual + ~ProcessProperties(); + + bool + GetDisableMemoryCache() const; + + Args + GetExtraStartupCommands () const; + + void + SetExtraStartupCommands (const Args &args); + + FileSpec + GetPythonOSPluginPath () const; + + void + SetPythonOSPluginPath (const FileSpec &file); + + bool + GetIgnoreBreakpointsInExpressions () const; + + void + SetIgnoreBreakpointsInExpressions (bool ignore); + + bool + GetUnwindOnErrorInExpressions () const; + + void + SetUnwindOnErrorInExpressions (bool ignore); + + bool + GetStopOnSharedLibraryEvents () const; + + void + SetStopOnSharedLibraryEvents (bool stop); + + bool + GetDetachKeepsStopped () const; + + void + SetDetachKeepsStopped (bool keep_stopped); +}; + +typedef std::shared_ptr ProcessPropertiesSP; + +//---------------------------------------------------------------------- +// ProcessInfo +// +// A base class for information for a process. This can be used to fill +// out information for a process prior to launching it, or it can be +// used for an instance of a process and can be filled in with the +// existing values for that process. +//---------------------------------------------------------------------- +class ProcessInfo +{ +public: + ProcessInfo () : + m_executable (), + m_arguments (), + m_environment (), + m_uid (UINT32_MAX), + m_gid (UINT32_MAX), + m_arch(), + m_pid (LLDB_INVALID_PROCESS_ID) + { + } + + ProcessInfo (const char *name, + const ArchSpec &arch, + lldb::pid_t pid) : + m_executable (name, false), + m_arguments (), + m_environment(), + m_uid (UINT32_MAX), + m_gid (UINT32_MAX), + m_arch (arch), + m_pid (pid) + { + } + + void + Clear () + { + m_executable.Clear(); + m_arguments.Clear(); + m_environment.Clear(); + m_uid = UINT32_MAX; + m_gid = UINT32_MAX; + m_arch.Clear(); + m_pid = LLDB_INVALID_PROCESS_ID; + } + + const char * + GetName() const + { + return m_executable.GetFilename().GetCString(); + } + + size_t + GetNameLength() const + { + return m_executable.GetFilename().GetLength(); + } + + FileSpec & + GetExecutableFile () + { + return m_executable; + } + + void + SetExecutableFile (const FileSpec &exe_file, bool add_exe_file_as_first_arg) + { + if (exe_file) + { + m_executable = exe_file; + if (add_exe_file_as_first_arg) + { + char filename[PATH_MAX]; + if (exe_file.GetPath(filename, sizeof(filename))) + m_arguments.InsertArgumentAtIndex (0, filename); + } + } + else + { + m_executable.Clear(); + } + } + + const FileSpec & + GetExecutableFile () const + { + return m_executable; + } + + uint32_t + GetUserID() const + { + return m_uid; + } + + uint32_t + GetGroupID() const + { + return m_gid; + } + + bool + UserIDIsValid () const + { + return m_uid != UINT32_MAX; + } + + bool + GroupIDIsValid () const + { + return m_gid != UINT32_MAX; + } + + void + SetUserID (uint32_t uid) + { + m_uid = uid; + } + + void + SetGroupID (uint32_t gid) + { + m_gid = gid; + } + + ArchSpec & + GetArchitecture () + { + return m_arch; + } + + const ArchSpec & + GetArchitecture () const + { + return m_arch; + } + + lldb::pid_t + GetProcessID () const + { + return m_pid; + } + + void + SetProcessID (lldb::pid_t pid) + { + m_pid = pid; + } + + bool + ProcessIDIsValid() const + { + return m_pid != LLDB_INVALID_PROCESS_ID; + } + + void + Dump (Stream &s, Platform *platform) const; + + Args & + GetArguments () + { + return m_arguments; + } + + const Args & + GetArguments () const + { + return m_arguments; + } + + const char * + GetArg0 () const + { + if (m_arg0.empty()) + return NULL; + return m_arg0.c_str(); + } + + void + SetArg0 (const char *arg) + { + if (arg && arg[0]) + m_arg0 = arg; + else + m_arg0.clear(); + } + + void + SetArguments (const Args& args, bool first_arg_is_executable); + + void + SetArguments (char const **argv, bool first_arg_is_executable); + + Args & + GetEnvironmentEntries () + { + return m_environment; + } + + const Args & + GetEnvironmentEntries () const + { + return m_environment; + } + +protected: + FileSpec m_executable; + std::string m_arg0; // argv[0] if supported. If empty, then use m_executable. + // Not all process plug-ins support specifying an argv[0] + // that differs from the resolved platform executable + // (which is in m_executable) + Args m_arguments; // All program arguments except argv[0] + Args m_environment; + uint32_t m_uid; + uint32_t m_gid; + ArchSpec m_arch; + lldb::pid_t m_pid; +}; + +//---------------------------------------------------------------------- +// ProcessInstanceInfo +// +// Describes an existing process and any discoverable information that +// pertains to that process. +//---------------------------------------------------------------------- +class ProcessInstanceInfo : public ProcessInfo +{ +public: + ProcessInstanceInfo () : + ProcessInfo (), + m_euid (UINT32_MAX), + m_egid (UINT32_MAX), + m_parent_pid (LLDB_INVALID_PROCESS_ID) + { + } + + ProcessInstanceInfo (const char *name, + const ArchSpec &arch, + lldb::pid_t pid) : + ProcessInfo (name, arch, pid), + m_euid (UINT32_MAX), + m_egid (UINT32_MAX), + m_parent_pid (LLDB_INVALID_PROCESS_ID) + { + } + + void + Clear () + { + ProcessInfo::Clear(); + m_euid = UINT32_MAX; + m_egid = UINT32_MAX; + m_parent_pid = LLDB_INVALID_PROCESS_ID; + } + + uint32_t + GetEffectiveUserID() const + { + return m_euid; + } + + uint32_t + GetEffectiveGroupID() const + { + return m_egid; + } + + bool + EffectiveUserIDIsValid () const + { + return m_euid != UINT32_MAX; + } + + bool + EffectiveGroupIDIsValid () const + { + return m_egid != UINT32_MAX; + } + + void + SetEffectiveUserID (uint32_t uid) + { + m_euid = uid; + } + + void + SetEffectiveGroupID (uint32_t gid) + { + m_egid = gid; + } + + lldb::pid_t + GetParentProcessID () const + { + return m_parent_pid; + } + + void + SetParentProcessID (lldb::pid_t pid) + { + m_parent_pid = pid; + } + + bool + ParentProcessIDIsValid() const + { + return m_parent_pid != LLDB_INVALID_PROCESS_ID; + } + + void + Dump (Stream &s, Platform *platform) const; + + static void + DumpTableHeader (Stream &s, Platform *platform, bool show_args, bool verbose); + + void + DumpAsTableRow (Stream &s, Platform *platform, bool show_args, bool verbose) const; + +protected: + uint32_t m_euid; + uint32_t m_egid; + lldb::pid_t m_parent_pid; +}; + + +//---------------------------------------------------------------------- +// ProcessLaunchInfo +// +// Describes any information that is required to launch a process. +//---------------------------------------------------------------------- + +class ProcessLaunchInfo : public ProcessInfo +{ +public: + + class FileAction + { + public: + enum Action + { + eFileActionNone, + eFileActionClose, + eFileActionDuplicate, + eFileActionOpen + }; + + + FileAction () : + m_action (eFileActionNone), + m_fd (-1), + m_arg (-1), + m_path () + { + } + + void + Clear() + { + m_action = eFileActionNone; + m_fd = -1; + m_arg = -1; + m_path.clear(); + } + + bool + Close (int fd); + + bool + Duplicate (int fd, int dup_fd); + + bool + Open (int fd, const char *path, bool read, bool write); + + static bool + AddPosixSpawnFileAction (posix_spawn_file_actions_t *file_actions, + const FileAction *info, + Log *log, + Error& error); + + int + GetFD () const + { + return m_fd; + } + + Action + GetAction () const + { + return m_action; + } + + int + GetActionArgument () const + { + return m_arg; + } + + const char * + GetPath () const + { + if (m_path.empty()) + return NULL; + return m_path.c_str(); + } + + protected: + Action m_action; // The action for this file + int m_fd; // An existing file descriptor + int m_arg; // oflag for eFileActionOpen*, dup_fd for eFileActionDuplicate + std::string m_path; // A file path to use for opening after fork or posix_spawn + }; + + ProcessLaunchInfo () : + ProcessInfo(), + m_working_dir (), + m_plugin_name (), + m_shell (), + m_flags (0), + m_file_actions (), + m_pty (), + m_resume_count (0), + m_monitor_callback (NULL), + m_monitor_callback_baton (NULL), + m_monitor_signals (false) + { + } + + ProcessLaunchInfo (const char *stdin_path, + const char *stdout_path, + const char *stderr_path, + const char *working_directory, + uint32_t launch_flags) : + ProcessInfo(), + m_working_dir (), + m_plugin_name (), + m_shell (), + m_flags (launch_flags), + m_file_actions (), + m_pty (), + m_resume_count (0), + m_monitor_callback (NULL), + m_monitor_callback_baton (NULL), + m_monitor_signals (false) + { + if (stdin_path) + { + ProcessLaunchInfo::FileAction file_action; + const bool read = true; + const bool write = false; + if (file_action.Open(STDIN_FILENO, stdin_path, read, write)) + AppendFileAction (file_action); + } + if (stdout_path) + { + ProcessLaunchInfo::FileAction file_action; + const bool read = false; + const bool write = true; + if (file_action.Open(STDOUT_FILENO, stdout_path, read, write)) + AppendFileAction (file_action); + } + if (stderr_path) + { + ProcessLaunchInfo::FileAction file_action; + const bool read = false; + const bool write = true; + if (file_action.Open(STDERR_FILENO, stderr_path, read, write)) + AppendFileAction (file_action); + } + if (working_directory) + SetWorkingDirectory(working_directory); + } + + void + AppendFileAction (const FileAction &info) + { + m_file_actions.push_back(info); + } + + bool + AppendCloseFileAction (int fd) + { + FileAction file_action; + if (file_action.Close (fd)) + { + AppendFileAction (file_action); + return true; + } + return false; + } + + bool + AppendDuplicateFileAction (int fd, int dup_fd) + { + FileAction file_action; + if (file_action.Duplicate (fd, dup_fd)) + { + AppendFileAction (file_action); + return true; + } + return false; + } + + bool + AppendOpenFileAction (int fd, const char *path, bool read, bool write) + { + FileAction file_action; + if (file_action.Open (fd, path, read, write)) + { + AppendFileAction (file_action); + return true; + } + return false; + } + + bool + AppendSuppressFileAction (int fd, bool read, bool write) + { + FileAction file_action; + if (file_action.Open (fd, "/dev/null", read, write)) + { + AppendFileAction (file_action); + return true; + } + return false; + } + + void + FinalizeFileActions (Target *target, + bool default_to_use_pty); + + size_t + GetNumFileActions () const + { + return m_file_actions.size(); + } + + const FileAction * + GetFileActionAtIndex (size_t idx) const + { + if (idx < m_file_actions.size()) + return &m_file_actions[idx]; + return NULL; + } + + const FileAction * + GetFileActionForFD (int fd) const + { + for (size_t idx=0, count=m_file_actions.size(); idx < count; ++idx) + { + if (m_file_actions[idx].GetFD () == fd) + return &m_file_actions[idx]; + } + return NULL; + } + + Flags & + GetFlags () + { + return m_flags; + } + + const Flags & + GetFlags () const + { + return m_flags; + } + + const char * + GetWorkingDirectory () const + { + if (m_working_dir.empty()) + return NULL; + return m_working_dir.c_str(); + } + + void + SetWorkingDirectory (const char *working_dir) + { + if (working_dir && working_dir[0]) + m_working_dir.assign (working_dir); + else + m_working_dir.clear(); + } + + void + SwapWorkingDirectory (std::string &working_dir) + { + m_working_dir.swap (working_dir); + } + + + const char * + GetProcessPluginName () const + { + if (m_plugin_name.empty()) + return NULL; + return m_plugin_name.c_str(); + } + + void + SetProcessPluginName (const char *plugin) + { + if (plugin && plugin[0]) + m_plugin_name.assign (plugin); + else + m_plugin_name.clear(); + } + + const char * + GetShell () const + { + if (m_shell.empty()) + return NULL; + return m_shell.c_str(); + } + + void + SetShell (const char * path) + { + if (path && path[0]) + { + m_shell.assign (path); + m_flags.Set (lldb::eLaunchFlagLaunchInShell); + } + else + { + m_shell.clear(); + m_flags.Clear (lldb::eLaunchFlagLaunchInShell); + } + } + + uint32_t + GetResumeCount () const + { + return m_resume_count; + } + + void + SetResumeCount (uint32_t c) + { + m_resume_count = c; + } + + bool + GetLaunchInSeparateProcessGroup () + { + return m_flags.Test(lldb::eLaunchFlagLaunchInSeparateProcessGroup); + } + + void + SetLaunchInSeparateProcessGroup (bool separate) + { + if (separate) + m_flags.Set(lldb::eLaunchFlagLaunchInSeparateProcessGroup); + else + m_flags.Clear (lldb::eLaunchFlagLaunchInSeparateProcessGroup); + + } + + void + Clear () + { + ProcessInfo::Clear(); + m_working_dir.clear(); + m_plugin_name.clear(); + m_shell.clear(); + m_flags.Clear(); + m_file_actions.clear(); + m_resume_count = 0; + } + + bool + ConvertArgumentsForLaunchingInShell (Error &error, + bool localhost, + bool will_debug, + bool first_arg_is_full_shell_command); + + void + SetMonitorProcessCallback (Host::MonitorChildProcessCallback callback, + void *baton, + bool monitor_signals) + { + m_monitor_callback = callback; + m_monitor_callback_baton = baton; + m_monitor_signals = monitor_signals; + } + + bool + MonitorProcess () const + { + if (m_monitor_callback && ProcessIDIsValid()) + { + Host::StartMonitoringChildProcess (m_monitor_callback, + m_monitor_callback_baton, + GetProcessID(), + m_monitor_signals); + return true; + } + return false; + } + + lldb_utility::PseudoTerminal & + GetPTY () + { + return m_pty; + } + +protected: + std::string m_working_dir; + std::string m_plugin_name; + std::string m_shell; + Flags m_flags; // Bitwise OR of bits from lldb::LaunchFlags + std::vector m_file_actions; // File actions for any other files + lldb_utility::PseudoTerminal m_pty; + uint32_t m_resume_count; // How many times do we resume after launching + Host::MonitorChildProcessCallback m_monitor_callback; + void *m_monitor_callback_baton; + bool m_monitor_signals; + +}; + +//---------------------------------------------------------------------- +// ProcessLaunchInfo +// +// Describes any information that is required to launch a process. +//---------------------------------------------------------------------- + +class ProcessAttachInfo : public ProcessInstanceInfo +{ +public: + ProcessAttachInfo() : + ProcessInstanceInfo(), + m_plugin_name (), + m_resume_count (0), + m_wait_for_launch (false), + m_ignore_existing (true), + m_continue_once_attached (false) + { + } + + ProcessAttachInfo (const ProcessLaunchInfo &launch_info) : + ProcessInstanceInfo(), + m_plugin_name (), + m_resume_count (0), + m_wait_for_launch (false), + m_ignore_existing (true), + m_continue_once_attached (false) + { + ProcessInfo::operator= (launch_info); + SetProcessPluginName (launch_info.GetProcessPluginName()); + SetResumeCount (launch_info.GetResumeCount()); + } + + bool + GetWaitForLaunch () const + { + return m_wait_for_launch; + } + + void + SetWaitForLaunch (bool b) + { + m_wait_for_launch = b; + } + + bool + GetIgnoreExisting () const + { + return m_ignore_existing; + } + + void + SetIgnoreExisting (bool b) + { + m_ignore_existing = b; + } + + bool + GetContinueOnceAttached () const + { + return m_continue_once_attached; + } + + void + SetContinueOnceAttached (bool b) + { + m_continue_once_attached = b; + } + + uint32_t + GetResumeCount () const + { + return m_resume_count; + } + + void + SetResumeCount (uint32_t c) + { + m_resume_count = c; + } + + const char * + GetProcessPluginName () const + { + if (m_plugin_name.empty()) + return NULL; + return m_plugin_name.c_str(); + } + + void + SetProcessPluginName (const char *plugin) + { + if (plugin && plugin[0]) + m_plugin_name.assign (plugin); + else + m_plugin_name.clear(); + } + + void + Clear () + { + ProcessInstanceInfo::Clear(); + m_plugin_name.clear(); + m_resume_count = 0; + m_wait_for_launch = false; + m_ignore_existing = true; + m_continue_once_attached = false; + } + + bool + ProcessInfoSpecified () const + { + if (GetExecutableFile()) + return true; + if (GetProcessID() != LLDB_INVALID_PROCESS_ID) + return true; + if (GetParentProcessID() != LLDB_INVALID_PROCESS_ID) + return true; + return false; + } +protected: + std::string m_plugin_name; + uint32_t m_resume_count; // How many times do we resume after launching + bool m_wait_for_launch; + bool m_ignore_existing; + bool m_continue_once_attached; // Supports the use-case scenario of immediately continuing the process once attached. +}; + +class ProcessLaunchCommandOptions : public Options +{ +public: + + ProcessLaunchCommandOptions (CommandInterpreter &interpreter) : + Options(interpreter) + { + // Keep default values of all options in one place: OptionParsingStarting () + OptionParsingStarting (); + } + + ~ProcessLaunchCommandOptions () + { + } + + Error + SetOptionValue (uint32_t option_idx, const char *option_arg); + + void + OptionParsingStarting () + { + launch_info.Clear(); + } + + const OptionDefinition* + GetDefinitions () + { + return g_option_table; + } + + // Options table: Required for subclasses of Options. + + static OptionDefinition g_option_table[]; + + // Instance variables to hold the values for command options. + + ProcessLaunchInfo launch_info; +}; + +//---------------------------------------------------------------------- +// ProcessInstanceInfoMatch +// +// A class to help matching one ProcessInstanceInfo to another. +//---------------------------------------------------------------------- + +class ProcessInstanceInfoMatch +{ +public: + ProcessInstanceInfoMatch () : + m_match_info (), + m_name_match_type (eNameMatchIgnore), + m_match_all_users (false) + { + } + + ProcessInstanceInfoMatch (const char *process_name, + NameMatchType process_name_match_type) : + m_match_info (), + m_name_match_type (process_name_match_type), + m_match_all_users (false) + { + m_match_info.GetExecutableFile().SetFile(process_name, false); + } + + ProcessInstanceInfo & + GetProcessInfo () + { + return m_match_info; + } + + const ProcessInstanceInfo & + GetProcessInfo () const + { + return m_match_info; + } + + bool + GetMatchAllUsers () const + { + return m_match_all_users; + } + + void + SetMatchAllUsers (bool b) + { + m_match_all_users = b; + } + + NameMatchType + GetNameMatchType () const + { + return m_name_match_type; + } + + void + SetNameMatchType (NameMatchType name_match_type) + { + m_name_match_type = name_match_type; + } + + bool + NameMatches (const char *process_name) const; + + bool + Matches (const ProcessInstanceInfo &proc_info) const; + + bool + MatchAllProcesses () const; + void + Clear (); + +protected: + ProcessInstanceInfo m_match_info; + NameMatchType m_name_match_type; + bool m_match_all_users; +}; + +class ProcessInstanceInfoList +{ +public: + ProcessInstanceInfoList () : + m_infos() + { + } + + void + Clear() + { + m_infos.clear(); + } + + size_t + GetSize() + { + return m_infos.size(); + } + + void + Append (const ProcessInstanceInfo &info) + { + m_infos.push_back (info); + } + + const char * + GetProcessNameAtIndex (size_t idx) + { + if (idx < m_infos.size()) + return m_infos[idx].GetName(); + return NULL; + } + + size_t + GetProcessNameLengthAtIndex (size_t idx) + { + if (idx < m_infos.size()) + return m_infos[idx].GetNameLength(); + return 0; + } + + lldb::pid_t + GetProcessIDAtIndex (size_t idx) + { + if (idx < m_infos.size()) + return m_infos[idx].GetProcessID(); + return 0; + } + + bool + GetInfoAtIndex (size_t idx, ProcessInstanceInfo &info) + { + if (idx < m_infos.size()) + { + info = m_infos[idx]; + return true; + } + return false; + } + + // You must ensure "idx" is valid before calling this function + const ProcessInstanceInfo & + GetProcessInfoAtIndex (size_t idx) const + { + assert (idx < m_infos.size()); + return m_infos[idx]; + } + +protected: + typedef std::vector collection; + collection m_infos; +}; + + +// This class tracks the Modification state of the process. Things that can currently modify +// the program are running the program (which will up the StopID) and writing memory (which +// will up the MemoryID.) +// FIXME: Should we also include modification of register states? + +class ProcessModID +{ +friend bool operator== (const ProcessModID &lhs, const ProcessModID &rhs); +public: + ProcessModID () : + m_stop_id (0), + m_last_natural_stop_id(0), + m_resume_id (0), + m_memory_id (0), + m_last_user_expression_resume (0), + m_running_user_expression (false) + {} + + ProcessModID (const ProcessModID &rhs) : + m_stop_id (rhs.m_stop_id), + m_memory_id (rhs.m_memory_id) + {} + + const ProcessModID & operator= (const ProcessModID &rhs) + { + if (this != &rhs) + { + m_stop_id = rhs.m_stop_id; + m_memory_id = rhs.m_memory_id; + } + return *this; + } + + ~ProcessModID () {} + + void BumpStopID () { + m_stop_id++; + if (!IsLastResumeForUserExpression()) + m_last_natural_stop_id++; + } + + void BumpMemoryID () { m_memory_id++; } + + void BumpResumeID () { + m_resume_id++; + if (m_running_user_expression > 0) + m_last_user_expression_resume = m_resume_id; + } + + uint32_t GetStopID() const { return m_stop_id; } + uint32_t GetLastNaturalStopID() const { return m_last_natural_stop_id; } + uint32_t GetMemoryID () const { return m_memory_id; } + uint32_t GetResumeID () const { return m_resume_id; } + uint32_t GetLastUserExpressionResumeID () const { return m_last_user_expression_resume; } + + bool MemoryIDEqual (const ProcessModID &compare) const + { + return m_memory_id == compare.m_memory_id; + } + + bool StopIDEqual (const ProcessModID &compare) const + { + return m_stop_id == compare.m_stop_id; + } + + void SetInvalid () + { + m_stop_id = UINT32_MAX; + } + + bool IsValid () const + { + return m_stop_id != UINT32_MAX; + } + + bool + IsLastResumeForUserExpression () const + { + return m_resume_id == m_last_user_expression_resume; + } + + void + SetRunningUserExpression (bool on) + { + // REMOVEME printf ("Setting running user expression %s at resume id %d - value: %d.\n", on ? "on" : "off", m_resume_id, m_running_user_expression); + if (on) + m_running_user_expression++; + else + m_running_user_expression--; + } + +private: + uint32_t m_stop_id; + uint32_t m_last_natural_stop_id; + uint32_t m_resume_id; + uint32_t m_memory_id; + uint32_t m_last_user_expression_resume; + uint32_t m_running_user_expression; +}; +inline bool operator== (const ProcessModID &lhs, const ProcessModID &rhs) +{ + if (lhs.StopIDEqual (rhs) + && lhs.MemoryIDEqual (rhs)) + return true; + else + return false; +} + +inline bool operator!= (const ProcessModID &lhs, const ProcessModID &rhs) +{ + if (!lhs.StopIDEqual (rhs) + || !lhs.MemoryIDEqual (rhs)) + return true; + else + return false; +} + +class MemoryRegionInfo +{ +public: + typedef Range RangeType; + + enum OptionalBool { + eDontKnow = -1, + eNo = 0, + eYes = 1 + }; + + MemoryRegionInfo () : + m_range (), + m_read (eDontKnow), + m_write (eDontKnow), + m_execute (eDontKnow) + { + } + + ~MemoryRegionInfo () + { + } + + RangeType & + GetRange() + { + return m_range; + } + + void + Clear() + { + m_range.Clear(); + m_read = m_write = m_execute = eDontKnow; + } + + const RangeType & + GetRange() const + { + return m_range; + } + + OptionalBool + GetReadable () const + { + return m_read; + } + + OptionalBool + GetWritable () const + { + return m_write; + } + + OptionalBool + GetExecutable () const + { + return m_execute; + } + + void + SetReadable (OptionalBool val) + { + m_read = val; + } + + void + SetWritable (OptionalBool val) + { + m_write = val; + } + + void + SetExecutable (OptionalBool val) + { + m_execute = val; + } + +protected: + RangeType m_range; + OptionalBool m_read; + OptionalBool m_write; + OptionalBool m_execute; +}; + +//---------------------------------------------------------------------- +/// @class Process Process.h "lldb/Target/Process.h" +/// @brief A plug-in interface definition class for debugging a process. +//---------------------------------------------------------------------- +class Process : + public std::enable_shared_from_this, + public ProcessProperties, + public UserID, + public Broadcaster, + public ExecutionContextScope, + public PluginInterface +{ +friend class ThreadList; +friend class ClangFunction; // For WaitForStateChangeEventsPrivate +friend class CommandObjectProcessLaunch; +friend class ProcessEventData; +friend class CommandObjectBreakpointCommand; +friend class StopInfo; + +public: + + //------------------------------------------------------------------ + /// Broadcaster event bits definitions. + //------------------------------------------------------------------ + enum + { + eBroadcastBitStateChanged = (1 << 0), + eBroadcastBitInterrupt = (1 << 1), + eBroadcastBitSTDOUT = (1 << 2), + eBroadcastBitSTDERR = (1 << 3), + eBroadcastBitProfileData = (1 << 4) + }; + + enum + { + eBroadcastInternalStateControlStop = (1<<0), + eBroadcastInternalStateControlPause = (1<<1), + eBroadcastInternalStateControlResume = (1<<2) + }; + + typedef Range LoadRange; + // We use a read/write lock to allow on or more clients to + // access the process state while the process is stopped (reader). + // We lock the write lock to control access to the process + // while it is running (readers, or clients that want the process + // stopped can block waiting for the process to stop, or just + // try to lock it to see if they can immediately access the stopped + // process. If the try read lock fails, then the process is running. + typedef ProcessRunLock::ProcessRunLocker StopLocker; + + // These two functions fill out the Broadcaster interface: + + static ConstString &GetStaticBroadcasterClass (); + + virtual ConstString &GetBroadcasterClass() const + { + return GetStaticBroadcasterClass(); + } + + + //------------------------------------------------------------------ + /// A notification structure that can be used by clients to listen + /// for changes in a process's lifetime. + /// + /// @see RegisterNotificationCallbacks (const Notifications&) + /// @see UnregisterNotificationCallbacks (const Notifications&) + //------------------------------------------------------------------ +#ifndef SWIG + typedef struct + { + void *baton; + void (*initialize)(void *baton, Process *process); + void (*process_state_changed) (void *baton, Process *process, lldb::StateType state); + } Notifications; + + class ProcessEventData : + public EventData + { + friend class Process; + + public: + ProcessEventData (); + ProcessEventData (const lldb::ProcessSP &process, lldb::StateType state); + + virtual ~ProcessEventData(); + + static const ConstString & + GetFlavorString (); + + virtual const ConstString & + GetFlavor () const; + + const lldb::ProcessSP & + GetProcessSP() const + { + return m_process_sp; + } + lldb::StateType + GetState() const + { + return m_state; + } + bool + GetRestarted () const + { + return m_restarted; + } + + size_t + GetNumRestartedReasons () + { + return m_restarted_reasons.size(); + } + + const char * + GetRestartedReasonAtIndex(size_t idx) + { + if (idx > m_restarted_reasons.size()) + return NULL; + else + return m_restarted_reasons[idx].c_str(); + } + + bool + GetInterrupted () const + { + return m_interrupted; + } + + virtual void + Dump (Stream *s) const; + + virtual void + DoOnRemoval (Event *event_ptr); + + static const Process::ProcessEventData * + GetEventDataFromEvent (const Event *event_ptr); + + static lldb::ProcessSP + GetProcessFromEvent (const Event *event_ptr); + + static lldb::StateType + GetStateFromEvent (const Event *event_ptr); + + static bool + GetRestartedFromEvent (const Event *event_ptr); + + static size_t + GetNumRestartedReasons(const Event *event_ptr); + + static const char * + GetRestartedReasonAtIndex(const Event *event_ptr, size_t idx); + + static void + AddRestartedReason (Event *event_ptr, const char *reason); + + static void + SetRestartedInEvent (Event *event_ptr, bool new_value); + + static bool + GetInterruptedFromEvent (const Event *event_ptr); + + static void + SetInterruptedInEvent (Event *event_ptr, bool new_value); + + static bool + SetUpdateStateOnRemoval (Event *event_ptr); + + private: + + void + SetUpdateStateOnRemoval() + { + m_update_state++; + } + void + SetRestarted (bool new_value) + { + m_restarted = new_value; + } + void + SetInterrupted (bool new_value) + { + m_interrupted = new_value; + } + void + AddRestartedReason (const char *reason) + { + m_restarted_reasons.push_back(reason); + } + + lldb::ProcessSP m_process_sp; + lldb::StateType m_state; + std::vector m_restarted_reasons; + bool m_restarted; // For "eStateStopped" events, this is true if the target was automatically restarted. + int m_update_state; + bool m_interrupted; + DISALLOW_COPY_AND_ASSIGN (ProcessEventData); + + }; + +#endif + + static void + SettingsInitialize (); + + static void + SettingsTerminate (); + + static const ProcessPropertiesSP & + GetGlobalProperties(); + + //------------------------------------------------------------------ + /// Construct with a shared pointer to a target, and the Process listener. + //------------------------------------------------------------------ + Process(Target &target, Listener &listener); + + //------------------------------------------------------------------ + /// Destructor. + /// + /// The destructor is virtual since this class is designed to be + /// inherited from by the plug-in instance. + //------------------------------------------------------------------ + virtual + ~Process(); + + //------------------------------------------------------------------ + /// Find a Process plug-in that can debug \a module using the + /// currently selected architecture. + /// + /// Scans all loaded plug-in interfaces that implement versions of + /// the Process plug-in interface and returns the first instance + /// that can debug the file. + /// + /// @param[in] module_sp + /// The module shared pointer that this process will debug. + /// + /// @param[in] plugin_name + /// If NULL, select the best plug-in for the binary. If non-NULL + /// then look for a plugin whose PluginInfo's name matches + /// this string. + /// + /// @see Process::CanDebug () + //------------------------------------------------------------------ + static lldb::ProcessSP + FindPlugin (Target &target, + const char *plugin_name, + Listener &listener, + const FileSpec *crash_file_path); + + + + //------------------------------------------------------------------ + /// Static function that can be used with the \b host function + /// Host::StartMonitoringChildProcess (). + /// + /// This function can be used by lldb_private::Process subclasses + /// when they want to watch for a local process and have its exit + /// status automatically set when the host child process exits. + /// Subclasses should call Host::StartMonitoringChildProcess () + /// with: + /// callback = Process::SetHostProcessExitStatus + /// callback_baton = NULL + /// pid = Process::GetID() + /// monitor_signals = false + //------------------------------------------------------------------ + static bool + SetProcessExitStatus (void *callback_baton, // The callback baton which should be set to NULL + lldb::pid_t pid, // The process ID we want to monitor + bool exited, + int signo, // Zero for no signal + int status); // Exit value of process if signal is zero + + lldb::ByteOrder + GetByteOrder () const; + + uint32_t + GetAddressByteSize () const; + + uint32_t + GetUniqueID() const + { + return m_process_unique_id; + } + //------------------------------------------------------------------ + /// Check if a plug-in instance can debug the file in \a module. + /// + /// Each plug-in is given a chance to say whether it can debug + /// the file in \a module. If the Process plug-in instance can + /// debug a file on the current system, it should return \b true. + /// + /// @return + /// Returns \b true if this Process plug-in instance can + /// debug the executable, \b false otherwise. + //------------------------------------------------------------------ + virtual bool + CanDebug (Target &target, + bool plugin_specified_by_name) = 0; + + + //------------------------------------------------------------------ + /// This object is about to be destroyed, do any necessary cleanup. + /// + /// Subclasses that override this method should always call this + /// superclass method. + //------------------------------------------------------------------ + virtual void + Finalize(); + + + //------------------------------------------------------------------ + /// Return whether this object is valid (i.e. has not been finalized.) + /// + /// @return + /// Returns \b true if this Process has not been finalized + /// and \b false otherwise. + //------------------------------------------------------------------ + bool + IsValid() const + { + return !m_finalize_called; + } + + //------------------------------------------------------------------ + /// Return a multi-word command object that can be used to expose + /// plug-in specific commands. + /// + /// This object will be used to resolve plug-in commands and can be + /// triggered by a call to: + /// + /// (lldb) process commmand + /// + /// @return + /// A CommandObject which can be one of the concrete subclasses + /// of CommandObject like CommandObjectRaw, CommandObjectParsed, + /// or CommandObjectMultiword. + //------------------------------------------------------------------ + virtual CommandObject * + GetPluginCommandObject() + { + return NULL; + } + + //------------------------------------------------------------------ + /// Launch a new process. + /// + /// Launch a new process by spawning a new process using the + /// target object's executable module's file as the file to launch. + /// Arguments are given in \a argv, and the environment variables + /// are in \a envp. Standard input and output files can be + /// optionally re-directed to \a stdin_path, \a stdout_path, and + /// \a stderr_path. + /// + /// This function is not meant to be overridden by Process + /// subclasses. It will first call Process::WillLaunch (Module *) + /// and if that returns \b true, Process::DoLaunch (Module*, + /// char const *[],char const *[],const char *,const char *, + /// const char *) will be called to actually do the launching. If + /// DoLaunch returns \b true, then Process::DidLaunch() will be + /// called. + /// + /// @param[in] argv + /// The argument array. + /// + /// @param[in] envp + /// The environment array. + /// + /// @param[in] launch_flags + /// Flags to modify the launch (@see lldb::LaunchFlags) + /// + /// @param[in] stdin_path + /// The path to use when re-directing the STDIN of the new + /// process. If all stdXX_path arguments are NULL, a pseudo + /// terminal will be used. + /// + /// @param[in] stdout_path + /// The path to use when re-directing the STDOUT of the new + /// process. If all stdXX_path arguments are NULL, a pseudo + /// terminal will be used. + /// + /// @param[in] stderr_path + /// The path to use when re-directing the STDERR of the new + /// process. If all stdXX_path arguments are NULL, a pseudo + /// terminal will be used. + /// + /// @param[in] working_directory + /// The working directory to have the child process run in + /// + /// @return + /// An error object. Call GetID() to get the process ID if + /// the error object is success. + //------------------------------------------------------------------ + virtual Error + Launch (const ProcessLaunchInfo &launch_info); + + virtual Error + LoadCore (); + + virtual Error + DoLoadCore () + { + Error error; + error.SetErrorStringWithFormat("error: %s does not support loading core files.", GetPluginName().GetCString()); + return error; + } + + //------------------------------------------------------------------ + /// Get the dynamic loader plug-in for this process. + /// + /// The default action is to let the DynamicLoader plug-ins check + /// the main executable and the DynamicLoader will select itself + /// automatically. Subclasses can override this if inspecting the + /// executable is not desired, or if Process subclasses can only + /// use a specific DynamicLoader plug-in. + //------------------------------------------------------------------ + virtual DynamicLoader * + GetDynamicLoader (); + + //------------------------------------------------------------------ + /// Attach to an existing process using the process attach info. + /// + /// This function is not meant to be overridden by Process + /// subclasses. It will first call WillAttach (lldb::pid_t) + /// or WillAttach (const char *), and if that returns \b + /// true, DoAttach (lldb::pid_t) or DoAttach (const char *) will + /// be called to actually do the attach. If DoAttach returns \b + /// true, then Process::DidAttach() will be called. + /// + /// @param[in] pid + /// The process ID that we should attempt to attach to. + /// + /// @return + /// Returns \a pid if attaching was successful, or + /// LLDB_INVALID_PROCESS_ID if attaching fails. + //------------------------------------------------------------------ + virtual Error + Attach (ProcessAttachInfo &attach_info); + + //------------------------------------------------------------------ + /// Attach to a remote system via a URL + /// + /// @param[in] strm + /// A stream where output intended for the user + /// (if the driver has a way to display that) generated during + /// the connection. This may be NULL if no output is needed.A + /// + /// @param[in] remote_url + /// The URL format that we are connecting to. + /// + /// @return + /// Returns an error object. + //------------------------------------------------------------------ + virtual Error + ConnectRemote (Stream *strm, const char *remote_url); + + bool + GetShouldDetach () const + { + return m_should_detach; + } + + void + SetShouldDetach (bool b) + { + m_should_detach = b; + } + + //------------------------------------------------------------------ + /// Get the image information address for the current process. + /// + /// Some runtimes have system functions that can help dynamic + /// loaders locate the dynamic loader information needed to observe + /// shared libraries being loaded or unloaded. This function is + /// in the Process interface (as opposed to the DynamicLoader + /// interface) to ensure that remote debugging can take advantage of + /// this functionality. + /// + /// @return + /// The address of the dynamic loader information, or + /// LLDB_INVALID_ADDRESS if this is not supported by this + /// interface. + //------------------------------------------------------------------ + virtual lldb::addr_t + GetImageInfoAddress (); + + //------------------------------------------------------------------ + /// Load a shared library into this process. + /// + /// Try and load a shared library into the current process. This + /// call might fail in the dynamic loader plug-in says it isn't safe + /// to try and load shared libraries at the moment. + /// + /// @param[in] image_spec + /// The image file spec that points to the shared library that + /// you want to load. + /// + /// @param[out] error + /// An error object that gets filled in with any errors that + /// might occur when trying to load the shared library. + /// + /// @return + /// A token that represents the shared library that can be + /// later used to unload the shared library. A value of + /// LLDB_INVALID_IMAGE_TOKEN will be returned if the shared + /// library can't be opened. + //------------------------------------------------------------------ + virtual uint32_t + LoadImage (const FileSpec &image_spec, Error &error); + + virtual Error + UnloadImage (uint32_t image_token); + + //------------------------------------------------------------------ + /// Register for process and thread notifications. + /// + /// Clients can register nofication callbacks by filling out a + /// Process::Notifications structure and calling this function. + /// + /// @param[in] callbacks + /// A structure that contains the notification baton and + /// callback functions. + /// + /// @see Process::Notifications + //------------------------------------------------------------------ +#ifndef SWIG + void + RegisterNotificationCallbacks (const Process::Notifications& callbacks); +#endif + //------------------------------------------------------------------ + /// Unregister for process and thread notifications. + /// + /// Clients can unregister nofication callbacks by passing a copy of + /// the original baton and callbacks in \a callbacks. + /// + /// @param[in] callbacks + /// A structure that contains the notification baton and + /// callback functions. + /// + /// @return + /// Returns \b true if the notification callbacks were + /// successfully removed from the process, \b false otherwise. + /// + /// @see Process::Notifications + //------------------------------------------------------------------ +#ifndef SWIG + bool + UnregisterNotificationCallbacks (const Process::Notifications& callbacks); +#endif + //================================================================== + // Built in Process Control functions + //================================================================== + //------------------------------------------------------------------ + /// Resumes all of a process's threads as configured using the + /// Thread run control functions. + /// + /// Threads for a process should be updated with one of the run + /// control actions (resume, step, or suspend) that they should take + /// when the process is resumed. If no run control action is given + /// to a thread it will be resumed by default. + /// + /// This function is not meant to be overridden by Process + /// subclasses. This function will take care of disabling any + /// breakpoints that threads may be stopped at, single stepping, and + /// re-enabling breakpoints, and enabling the basic flow control + /// that the plug-in instances need not worry about. + /// + /// N.B. This function also sets the Write side of the Run Lock, + /// which is unset when the corresponding stop event is pulled off + /// the Public Event Queue. If you need to resume the process without + /// setting the Run Lock, use PrivateResume (though you should only do + /// that from inside the Process class. + /// + /// @return + /// Returns an error object. + /// + /// @see Thread:Resume() + /// @see Thread:Step() + /// @see Thread:Suspend() + //------------------------------------------------------------------ + Error + Resume(); + + //------------------------------------------------------------------ + /// Halts a running process. + /// + /// This function is not meant to be overridden by Process + /// subclasses. + /// If the process is successfully halted, a eStateStopped + /// process event with GetInterrupted will be broadcast. If false, we will + /// halt the process with no events generated by the halt. + /// + /// @param[in] clear_thread_plans + /// If true, when the process stops, clear all thread plans. + /// + /// @return + /// Returns an error object. If the error is empty, the process is halted. + /// otherwise the halt has failed. + //------------------------------------------------------------------ + Error + Halt (bool clear_thread_plans = false); + + //------------------------------------------------------------------ + /// Detaches from a running or stopped process. + /// + /// This function is not meant to be overridden by Process + /// subclasses. + /// + /// @param[in] keep_stopped + /// If true, don't resume the process on detach. + /// + /// @return + /// Returns an error object. + //------------------------------------------------------------------ + Error + Detach (bool keep_stopped); + + //------------------------------------------------------------------ + /// Kills the process and shuts down all threads that were spawned + /// to track and monitor the process. + /// + /// This function is not meant to be overridden by Process + /// subclasses. + /// + /// @return + /// Returns an error object. + //------------------------------------------------------------------ + Error + Destroy(); + + //------------------------------------------------------------------ + /// Sends a process a UNIX signal \a signal. + /// + /// This function is not meant to be overridden by Process + /// subclasses. + /// + /// @return + /// Returns an error object. + //------------------------------------------------------------------ + Error + Signal (int signal); + + virtual UnixSignals & + GetUnixSignals () + { + return m_unix_signals; + } + + //================================================================== + // Plug-in Process Control Overrides + //================================================================== + + //------------------------------------------------------------------ + /// Called before attaching to a process. + /// + /// Allow Process plug-ins to execute some code before attaching a + /// process. + /// + /// @return + /// Returns an error object. + //------------------------------------------------------------------ + virtual Error + WillAttachToProcessWithID (lldb::pid_t pid) + { + return Error(); + } + + //------------------------------------------------------------------ + /// Called before attaching to a process. + /// + /// Allow Process plug-ins to execute some code before attaching a + /// process. + /// + /// @return + /// Returns an error object. + //------------------------------------------------------------------ + virtual Error + WillAttachToProcessWithName (const char *process_name, bool wait_for_launch) + { + return Error(); + } + + //------------------------------------------------------------------ + /// Attach to a remote system via a URL + /// + /// @param[in] strm + /// A stream where output intended for the user + /// (if the driver has a way to display that) generated during + /// the connection. This may be NULL if no output is needed.A + /// + /// @param[in] remote_url + /// The URL format that we are connecting to. + /// + /// @return + /// Returns an error object. + //------------------------------------------------------------------ + virtual Error + DoConnectRemote (Stream *strm, const char *remote_url) + { + Error error; + error.SetErrorString ("remote connections are not supported"); + return error; + } + + //------------------------------------------------------------------ + /// Attach to an existing process using a process ID. + /// + /// @param[in] pid + /// The process ID that we should attempt to attach to. + /// + /// @return + /// Returns \a pid if attaching was successful, or + /// LLDB_INVALID_PROCESS_ID if attaching fails. + //------------------------------------------------------------------ + virtual Error + DoAttachToProcessWithID (lldb::pid_t pid) + { + Error error; + error.SetErrorStringWithFormat("error: %s does not support attaching to a process by pid", GetPluginName().GetCString()); + return error; + } + + //------------------------------------------------------------------ + /// Attach to an existing process using a process ID. + /// + /// @param[in] pid + /// The process ID that we should attempt to attach to. + /// + /// @param[in] attach_info + /// Information on how to do the attach. For example, GetUserID() + /// will return the uid to attach as. + /// + /// @return + /// Returns \a pid if attaching was successful, or + /// LLDB_INVALID_PROCESS_ID if attaching fails. + /// hanming : need flag + //------------------------------------------------------------------ + virtual Error + DoAttachToProcessWithID (lldb::pid_t pid, const ProcessAttachInfo &attach_info) + { + Error error; + error.SetErrorStringWithFormat("error: %s does not support attaching to a process by pid", GetPluginName().GetCString()); + return error; + } + + //------------------------------------------------------------------ + /// Attach to an existing process using a partial process name. + /// + /// @param[in] process_name + /// The name of the process to attach to. + /// + /// @param[in] wait_for_launch + /// If \b true, wait for the process to be launched and attach + /// as soon as possible after it does launch. If \b false, then + /// search for a matching process the currently exists. + /// + /// @param[in] attach_info + /// Information on how to do the attach. For example, GetUserID() + /// will return the uid to attach as. + /// + /// @return + /// Returns \a pid if attaching was successful, or + /// LLDB_INVALID_PROCESS_ID if attaching fails. + //------------------------------------------------------------------ + virtual Error + DoAttachToProcessWithName (const char *process_name, bool wait_for_launch, const ProcessAttachInfo &attach_info) + { + Error error; + error.SetErrorString("attach by name is not supported"); + return error; + } + + //------------------------------------------------------------------ + /// Called after attaching a process. + /// + /// Allow Process plug-ins to execute some code after attaching to + /// a process. + //------------------------------------------------------------------ + virtual void + DidAttach () {} + + + //------------------------------------------------------------------ + /// Called after a process re-execs itself. + /// + /// Allow Process plug-ins to execute some code after a process has + /// exec'ed itself. Subclasses typically should override DoDidExec() + /// as the lldb_private::Process class needs to remove its dynamic + /// loader, runtime, ABI and other plug-ins, as well as unload all + /// shared libraries. + //------------------------------------------------------------------ + virtual void + DidExec (); + + //------------------------------------------------------------------ + /// Subclasses of Process should implement this function if they + /// need to do anything after a process exec's itself. + //------------------------------------------------------------------ + virtual void + DoDidExec () + { + } + + //------------------------------------------------------------------ + /// Called before launching to a process. + /// + /// Allow Process plug-ins to execute some code before launching a + /// process. + /// + /// @return + /// Returns an error object. + //------------------------------------------------------------------ + virtual Error + WillLaunch (Module* module) + { + return Error(); + } + + //------------------------------------------------------------------ + /// Launch a new process. + /// + /// Launch a new process by spawning a new process using \a module's + /// file as the file to launch. Arguments are given in \a argv, + /// and the environment variables are in \a envp. Standard input + /// and output files can be optionally re-directed to \a stdin_path, + /// \a stdout_path, and \a stderr_path. + /// + /// @param[in] module + /// The module from which to extract the file specification and + /// launch. + /// + /// @param[in] argv + /// The argument array. + /// + /// @param[in] envp + /// The environment array. + /// + /// @param[in] launch_flags + /// Flags to modify the launch (@see lldb::LaunchFlags) + /// + /// @param[in] stdin_path + /// The path to use when re-directing the STDIN of the new + /// process. If all stdXX_path arguments are NULL, a pseudo + /// terminal will be used. + /// + /// @param[in] stdout_path + /// The path to use when re-directing the STDOUT of the new + /// process. If all stdXX_path arguments are NULL, a pseudo + /// terminal will be used. + /// + /// @param[in] stderr_path + /// The path to use when re-directing the STDERR of the new + /// process. If all stdXX_path arguments are NULL, a pseudo + /// terminal will be used. + /// + /// @param[in] working_directory + /// The working directory to have the child process run in + /// + /// @return + /// A new valid process ID, or LLDB_INVALID_PROCESS_ID if + /// launching fails. + //------------------------------------------------------------------ + virtual Error + DoLaunch (Module *exe_module, + const ProcessLaunchInfo &launch_info) + { + Error error; + error.SetErrorStringWithFormat("error: %s does not support launching processes", GetPluginName().GetCString()); + return error; + } + + + //------------------------------------------------------------------ + /// Called after launching a process. + /// + /// Allow Process plug-ins to execute some code after launching + /// a process. + //------------------------------------------------------------------ + virtual void + DidLaunch () {} + + + + //------------------------------------------------------------------ + /// Called before resuming to a process. + /// + /// Allow Process plug-ins to execute some code before resuming a + /// process. + /// + /// @return + /// Returns an error object. + //------------------------------------------------------------------ + virtual Error + WillResume () { return Error(); } + + //------------------------------------------------------------------ + /// Resumes all of a process's threads as configured using the + /// Thread run control functions. + /// + /// Threads for a process should be updated with one of the run + /// control actions (resume, step, or suspend) that they should take + /// when the process is resumed. If no run control action is given + /// to a thread it will be resumed by default. + /// + /// @return + /// Returns \b true if the process successfully resumes using + /// the thread run control actions, \b false otherwise. + /// + /// @see Thread:Resume() + /// @see Thread:Step() + /// @see Thread:Suspend() + //------------------------------------------------------------------ + virtual Error + DoResume () + { + Error error; + error.SetErrorStringWithFormat("error: %s does not support resuming processes", GetPluginName().GetCString()); + return error; + } + + + //------------------------------------------------------------------ + /// Called after resuming a process. + /// + /// Allow Process plug-ins to execute some code after resuming + /// a process. + //------------------------------------------------------------------ + virtual void + DidResume () {} + + + //------------------------------------------------------------------ + /// Called before halting to a process. + /// + /// Allow Process plug-ins to execute some code before halting a + /// process. + /// + /// @return + /// Returns an error object. + //------------------------------------------------------------------ + virtual Error + WillHalt () { return Error(); } + + //------------------------------------------------------------------ + /// Halts a running process. + /// + /// DoHalt must produce one and only one stop StateChanged event if it actually + /// stops the process. If the stop happens through some natural event (for + /// instance a SIGSTOP), then forwarding that event will do. Otherwise, you must + /// generate the event manually. Note also, the private event thread is stopped when + /// DoHalt is run to prevent the events generated while halting to trigger + /// other state changes before the halt is complete. + /// + /// @param[out] caused_stop + /// If true, then this Halt caused the stop, otherwise, the + /// process was already stopped. + /// + /// @return + /// Returns \b true if the process successfully halts, \b false + /// otherwise. + //------------------------------------------------------------------ + virtual Error + DoHalt (bool &caused_stop) + { + Error error; + error.SetErrorStringWithFormat("error: %s does not support halting processes", GetPluginName().GetCString()); + return error; + } + + + //------------------------------------------------------------------ + /// Called after halting a process. + /// + /// Allow Process plug-ins to execute some code after halting + /// a process. + //------------------------------------------------------------------ + virtual void + DidHalt () {} + + //------------------------------------------------------------------ + /// Called before detaching from a process. + /// + /// Allow Process plug-ins to execute some code before detaching + /// from a process. + /// + /// @return + /// Returns an error object. + //------------------------------------------------------------------ + virtual Error + WillDetach () + { + return Error(); + } + + //------------------------------------------------------------------ + /// Detaches from a running or stopped process. + /// + /// @return + /// Returns \b true if the process successfully detaches, \b + /// false otherwise. + //------------------------------------------------------------------ + virtual Error + DoDetach (bool keep_stopped) + { + Error error; + error.SetErrorStringWithFormat("error: %s does not support detaching from processes", GetPluginName().GetCString()); + return error; + } + + + //------------------------------------------------------------------ + /// Called after detaching from a process. + /// + /// Allow Process plug-ins to execute some code after detaching + /// from a process. + //------------------------------------------------------------------ + virtual void + DidDetach () {} + + virtual bool + DetachRequiresHalt() { return false; } + + //------------------------------------------------------------------ + /// Called before sending a signal to a process. + /// + /// Allow Process plug-ins to execute some code before sending a + /// signal to a process. + /// + /// @return + /// Returns no error if it is safe to proceed with a call to + /// Process::DoSignal(int), otherwise an error describing what + /// prevents the signal from being sent. + //------------------------------------------------------------------ + virtual Error + WillSignal () { return Error(); } + + //------------------------------------------------------------------ + /// Sends a process a UNIX signal \a signal. + /// + /// @return + /// Returns an error object. + //------------------------------------------------------------------ + virtual Error + DoSignal (int signal) + { + Error error; + error.SetErrorStringWithFormat("error: %s does not support senging signals to processes", GetPluginName().GetCString()); + return error; + } + + virtual Error + WillDestroy () { return Error(); } + + virtual Error + DoDestroy () = 0; + + virtual void + DidDestroy () { } + + virtual bool + DestroyRequiresHalt() { return true; } + + + //------------------------------------------------------------------ + /// Called after sending a signal to a process. + /// + /// Allow Process plug-ins to execute some code after sending a + /// signal to a process. + //------------------------------------------------------------------ + virtual void + DidSignal () {} + + //------------------------------------------------------------------ + /// Currently called as part of ShouldStop. + /// FIXME: Should really happen when the target stops before the + /// event is taken from the queue... + /// + /// This callback is called as the event + /// is about to be queued up to allow Process plug-ins to execute + /// some code prior to clients being notified that a process was + /// stopped. Common operations include updating the thread list, + /// invalidating any thread state (registers, stack, etc) prior to + /// letting the notification go out. + /// + //------------------------------------------------------------------ + virtual void + RefreshStateAfterStop () = 0; + + //------------------------------------------------------------------ + /// Get the target object pointer for this module. + /// + /// @return + /// A Target object pointer to the target that owns this + /// module. + //------------------------------------------------------------------ + Target & + GetTarget () + { + return m_target; + } + + //------------------------------------------------------------------ + /// Get the const target object pointer for this module. + /// + /// @return + /// A const Target object pointer to the target that owns this + /// module. + //------------------------------------------------------------------ + const Target & + GetTarget () const + { + return m_target; + } + + //------------------------------------------------------------------ + /// Flush all data in the process. + /// + /// Flush the memory caches, all threads, and any other cached data + /// in the process. + /// + /// This function can be called after a world changing event like + /// adding a new symbol file, or after the process makes a large + /// context switch (from boot ROM to booted into an OS). + //------------------------------------------------------------------ + void + Flush (); + + //------------------------------------------------------------------ + /// Get accessor for the current process state. + /// + /// @return + /// The current state of the process. + /// + /// @see lldb::StateType + //------------------------------------------------------------------ + lldb::StateType + GetState (); + + ExecutionResults + RunThreadPlan (ExecutionContext &exe_ctx, + lldb::ThreadPlanSP &thread_plan_sp, + bool stop_others, + bool run_others, + bool unwind_on_error, + bool ignore_breakpoints, + uint32_t timeout_usec, + Stream &errors); + + static const char * + ExecutionResultAsCString (ExecutionResults result); + + void + GetStatus (Stream &ostrm); + + size_t + GetThreadStatus (Stream &ostrm, + bool only_threads_with_stop_reason, + uint32_t start_frame, + uint32_t num_frames, + uint32_t num_frames_with_source); + + void + SendAsyncInterrupt (); + +protected: + + void + SetState (lldb::EventSP &event_sp); + + lldb::StateType + GetPrivateState (); + + //------------------------------------------------------------------ + /// The "private" side of resuming a process. This doesn't alter the + /// state of m_run_lock, but just causes the process to resume. + /// + /// @return + /// An Error object describing the success or failure of the resume. + //------------------------------------------------------------------ + Error + PrivateResume (); + + //------------------------------------------------------------------ + // Called internally + //------------------------------------------------------------------ + void + CompleteAttach (); + +public: + //------------------------------------------------------------------ + /// Get the exit status for a process. + /// + /// @return + /// The process's return code, or -1 if the current process + /// state is not eStateExited. + //------------------------------------------------------------------ + int + GetExitStatus (); + + //------------------------------------------------------------------ + /// Get a textual description of what the process exited. + /// + /// @return + /// The textual description of why the process exited, or NULL + /// if there is no description available. + //------------------------------------------------------------------ + const char * + GetExitDescription (); + + + virtual void + DidExit () + { + } + + //------------------------------------------------------------------ + /// Get the Modification ID of the process. + /// + /// @return + /// The modification ID of the process. + //------------------------------------------------------------------ + ProcessModID + GetModID () const + { + return m_mod_id; + } + + const ProcessModID & + GetModIDRef () const + { + return m_mod_id; + } + + uint32_t + GetStopID () const + { + return m_mod_id.GetStopID(); + } + + uint32_t + GetResumeID () const + { + return m_mod_id.GetResumeID(); + } + + uint32_t + GetLastUserExpressionResumeID () const + { + return m_mod_id.GetLastUserExpressionResumeID(); + } + + uint32_t + GetLastNaturalStopID() + { + return m_mod_id.GetLastNaturalStopID(); + } + + //------------------------------------------------------------------ + /// Set accessor for the process exit status (return code). + /// + /// Sometimes a child exits and the exit can be detected by global + /// functions (signal handler for SIGCHLD for example). This + /// accessor allows the exit status to be set from an external + /// source. + /// + /// Setting this will cause a eStateExited event to be posted to + /// the process event queue. + /// + /// @param[in] exit_status + /// The value for the process's return code. + /// + /// @see lldb::StateType + //------------------------------------------------------------------ + virtual bool + SetExitStatus (int exit_status, const char *cstr); + + //------------------------------------------------------------------ + /// Check if a process is still alive. + /// + /// @return + /// Returns \b true if the process is still valid, \b false + /// otherwise. + //------------------------------------------------------------------ + virtual bool + IsAlive () = 0; + + //------------------------------------------------------------------ + /// Before lldb detaches from a process, it warns the user that they are about to lose their debug session. + /// In some cases, this warning doesn't need to be emitted -- for instance, with core file debugging where + /// the user can reconstruct the "state" by simply re-running the debugger on the core file. + /// + /// @return + // true if the user should be warned about detaching from this process. + //------------------------------------------------------------------ + virtual bool + WarnBeforeDetach () const + { + return true; + } + + //------------------------------------------------------------------ + /// Actually do the reading of memory from a process. + /// + /// Subclasses must override this function and can return fewer + /// bytes than requested when memory requests are too large. This + /// class will break up the memory requests and keep advancing the + /// arguments along as needed. + /// + /// @param[in] vm_addr + /// A virtual load address that indicates where to start reading + /// memory from. + /// + /// @param[in] size + /// The number of bytes to read. + /// + /// @param[out] buf + /// A byte buffer that is at least \a size bytes long that + /// will receive the memory bytes. + /// + /// @return + /// The number of bytes that were actually read into \a buf. + //------------------------------------------------------------------ + virtual size_t + DoReadMemory (lldb::addr_t vm_addr, + void *buf, + size_t size, + Error &error) = 0; + + //------------------------------------------------------------------ + /// Read of memory from a process. + /// + /// This function will read memory from the current process's + /// address space and remove any traps that may have been inserted + /// into the memory. + /// + /// This function is not meant to be overridden by Process + /// subclasses, the subclasses should implement + /// Process::DoReadMemory (lldb::addr_t, size_t, void *). + /// + /// @param[in] vm_addr + /// A virtual load address that indicates where to start reading + /// memory from. + /// + /// @param[out] buf + /// A byte buffer that is at least \a size bytes long that + /// will receive the memory bytes. + /// + /// @param[in] size + /// The number of bytes to read. + /// + /// @return + /// The number of bytes that were actually read into \a buf. If + /// the returned number is greater than zero, yet less than \a + /// size, then this function will get called again with \a + /// vm_addr, \a buf, and \a size updated appropriately. Zero is + /// returned to indicate an error. + //------------------------------------------------------------------ + virtual size_t + ReadMemory (lldb::addr_t vm_addr, + void *buf, + size_t size, + Error &error); + + //------------------------------------------------------------------ + /// Read a NULL terminated string from memory + /// + /// This function will read a cache page at a time until a NULL + /// string terminator is found. It will stop reading if an aligned + /// sequence of NULL termination \a type_width bytes is not found + /// before reading \a cstr_max_len bytes. The results are always + /// guaranteed to be NULL terminated, and that no more than + /// (max_bytes - type_width) bytes will be read. + /// + /// @param[in] vm_addr + /// The virtual load address to start the memory read. + /// + /// @param[in] str + /// A character buffer containing at least max_bytes. + /// + /// @param[in] max_bytes + /// The maximum number of bytes to read. + /// + /// @param[in] error + /// The error status of the read operation. + /// + /// @param[in] type_width + /// The size of the null terminator (1 to 4 bytes per + /// character). Defaults to 1. + /// + /// @return + /// The error status or the number of bytes prior to the null terminator. + //------------------------------------------------------------------ + size_t + ReadStringFromMemory (lldb::addr_t vm_addr, + char *str, + size_t max_bytes, + Error &error, + size_t type_width = 1); + + //------------------------------------------------------------------ + /// Read a NULL terminated C string from memory + /// + /// This function will read a cache page at a time until the NULL + /// C string terminator is found. It will stop reading if the NULL + /// termination byte isn't found before reading \a cstr_max_len + /// bytes, and the results are always guaranteed to be NULL + /// terminated (at most cstr_max_len - 1 bytes will be read). + //------------------------------------------------------------------ + size_t + ReadCStringFromMemory (lldb::addr_t vm_addr, + char *cstr, + size_t cstr_max_len, + Error &error); + + size_t + ReadCStringFromMemory (lldb::addr_t vm_addr, + std::string &out_str, + Error &error); + + size_t + ReadMemoryFromInferior (lldb::addr_t vm_addr, + void *buf, + size_t size, + Error &error); + + //------------------------------------------------------------------ + /// Reads an unsigned integer of the specified byte size from + /// process memory. + /// + /// @param[in] load_addr + /// A load address of the integer to read. + /// + /// @param[in] byte_size + /// The size in byte of the integer to read. + /// + /// @param[in] fail_value + /// The value to return if we fail to read an integer. + /// + /// @param[out] error + /// An error that indicates the success or failure of this + /// operation. If error indicates success (error.Success()), + /// then the value returned can be trusted, otherwise zero + /// will be returned. + /// + /// @return + /// The unsigned integer that was read from the process memory + /// space. If the integer was smaller than a uint64_t, any + /// unused upper bytes will be zero filled. If the process + /// byte order differs from the host byte order, the integer + /// value will be appropriately byte swapped into host byte + /// order. + //------------------------------------------------------------------ + uint64_t + ReadUnsignedIntegerFromMemory (lldb::addr_t load_addr, + size_t byte_size, + uint64_t fail_value, + Error &error); + + lldb::addr_t + ReadPointerFromMemory (lldb::addr_t vm_addr, + Error &error); + + bool + WritePointerToMemory (lldb::addr_t vm_addr, + lldb::addr_t ptr_value, + Error &error); + + //------------------------------------------------------------------ + /// Actually do the writing of memory to a process. + /// + /// @param[in] vm_addr + /// A virtual load address that indicates where to start writing + /// memory to. + /// + /// @param[in] buf + /// A byte buffer that is at least \a size bytes long that + /// contains the data to write. + /// + /// @param[in] size + /// The number of bytes to write. + /// + /// @param[out] error + /// An error value in case the memory write fails. + /// + /// @return + /// The number of bytes that were actually written. + //------------------------------------------------------------------ + virtual size_t + DoWriteMemory (lldb::addr_t vm_addr, const void *buf, size_t size, Error &error) + { + error.SetErrorStringWithFormat("error: %s does not support writing to processes", GetPluginName().GetCString()); + return 0; + } + + + //------------------------------------------------------------------ + /// Write all or part of a scalar value to memory. + /// + /// The value contained in \a scalar will be swapped to match the + /// byte order of the process that is being debugged. If \a size is + /// less than the size of scalar, the least significate \a size bytes + /// from scalar will be written. If \a size is larger than the byte + /// size of scalar, then the extra space will be padded with zeros + /// and the scalar value will be placed in the least significant + /// bytes in memory. + /// + /// @param[in] vm_addr + /// A virtual load address that indicates where to start writing + /// memory to. + /// + /// @param[in] scalar + /// The scalar to write to the debugged process. + /// + /// @param[in] size + /// This value can be smaller or larger than the scalar value + /// itself. If \a size is smaller than the size of \a scalar, + /// the least significant bytes in \a scalar will be used. If + /// \a size is larger than the byte size of \a scalar, then + /// the extra space will be padded with zeros. If \a size is + /// set to UINT32_MAX, then the size of \a scalar will be used. + /// + /// @param[out] error + /// An error value in case the memory write fails. + /// + /// @return + /// The number of bytes that were actually written. + //------------------------------------------------------------------ + size_t + WriteScalarToMemory (lldb::addr_t vm_addr, + const Scalar &scalar, + size_t size, + Error &error); + + size_t + ReadScalarIntegerFromMemory (lldb::addr_t addr, + uint32_t byte_size, + bool is_signed, + Scalar &scalar, + Error &error); + + //------------------------------------------------------------------ + /// Write memory to a process. + /// + /// This function will write memory to the current process's + /// address space and maintain any traps that might be present due + /// to software breakpoints. + /// + /// This function is not meant to be overridden by Process + /// subclasses, the subclasses should implement + /// Process::DoWriteMemory (lldb::addr_t, size_t, void *). + /// + /// @param[in] vm_addr + /// A virtual load address that indicates where to start writing + /// memory to. + /// + /// @param[in] buf + /// A byte buffer that is at least \a size bytes long that + /// contains the data to write. + /// + /// @param[in] size + /// The number of bytes to write. + /// + /// @return + /// The number of bytes that were actually written. + //------------------------------------------------------------------ + size_t + WriteMemory (lldb::addr_t vm_addr, const void *buf, size_t size, Error &error); + + + //------------------------------------------------------------------ + /// Actually allocate memory in the process. + /// + /// This function will allocate memory in the process's address + /// space. This can't rely on the generic function calling mechanism, + /// since that requires this function. + /// + /// @param[in] size + /// The size of the allocation requested. + /// + /// @return + /// The address of the allocated buffer in the process, or + /// LLDB_INVALID_ADDRESS if the allocation failed. + //------------------------------------------------------------------ + + virtual lldb::addr_t + DoAllocateMemory (size_t size, uint32_t permissions, Error &error) + { + error.SetErrorStringWithFormat("error: %s does not support allocating in the debug process", GetPluginName().GetCString()); + return LLDB_INVALID_ADDRESS; + } + + + //------------------------------------------------------------------ + /// The public interface to allocating memory in the process. + /// + /// This function will allocate memory in the process's address + /// space. This can't rely on the generic function calling mechanism, + /// since that requires this function. + /// + /// @param[in] size + /// The size of the allocation requested. + /// + /// @param[in] permissions + /// Or together any of the lldb::Permissions bits. The permissions on + /// a given memory allocation can't be changed after allocation. Note + /// that a block that isn't set writable can still be written on from lldb, + /// just not by the process itself. + /// + /// @param[in/out] error + /// An error object to fill in if things go wrong. + /// @return + /// The address of the allocated buffer in the process, or + /// LLDB_INVALID_ADDRESS if the allocation failed. + //------------------------------------------------------------------ + + lldb::addr_t + AllocateMemory (size_t size, uint32_t permissions, Error &error); + + + //------------------------------------------------------------------ + /// Resolve dynamically loaded indirect functions. + /// + /// @param[in] address + /// The load address of the indirect function to resolve. + /// + /// @param[out] error + /// An error value in case the resolve fails. + /// + /// @return + /// The address of the resolved function. + /// LLDB_INVALID_ADDRESS if the resolution failed. + //------------------------------------------------------------------ + + virtual lldb::addr_t + ResolveIndirectFunction(const Address *address, Error &error) + { + error.SetErrorStringWithFormat("error: %s does not support indirect functions in the debug process", GetPluginName().GetCString()); + return LLDB_INVALID_ADDRESS; + } + + virtual Error + GetMemoryRegionInfo (lldb::addr_t load_addr, + MemoryRegionInfo &range_info) + { + Error error; + error.SetErrorString ("Process::GetMemoryRegionInfo() not supported"); + return error; + } + + virtual Error + GetWatchpointSupportInfo (uint32_t &num) + { + Error error; + num = 0; + error.SetErrorString ("Process::GetWatchpointSupportInfo() not supported"); + return error; + } + + virtual Error + GetWatchpointSupportInfo (uint32_t &num, bool& after) + { + Error error; + num = 0; + after = true; + error.SetErrorString ("Process::GetWatchpointSupportInfo() not supported"); + return error; + } + + lldb::ModuleSP + ReadModuleFromMemory (const FileSpec& file_spec, + lldb::addr_t header_addr); + + //------------------------------------------------------------------ + /// Attempt to get the attributes for a region of memory in the process. + /// + /// It may be possible for the remote debug server to inspect attributes + /// for a region of memory in the process, such as whether there is a + /// valid page of memory at a given address or whether that page is + /// readable/writable/executable by the process. + /// + /// @param[in] load_addr + /// The address of interest in the process. + /// + /// @param[out] permissions + /// If this call returns successfully, this bitmask will have + /// its Permissions bits set to indicate whether the region is + /// readable/writable/executable. If this call fails, the + /// bitmask values are undefined. + /// + /// @return + /// Returns true if it was able to determine the attributes of the + /// memory region. False if not. + //------------------------------------------------------------------ + + virtual bool + GetLoadAddressPermissions (lldb::addr_t load_addr, uint32_t &permissions) + { + MemoryRegionInfo range_info; + permissions = 0; + Error error (GetMemoryRegionInfo (load_addr, range_info)); + if (!error.Success()) + return false; + if (range_info.GetReadable() == MemoryRegionInfo::eDontKnow + || range_info.GetWritable() == MemoryRegionInfo::eDontKnow + || range_info.GetExecutable() == MemoryRegionInfo::eDontKnow) + { + return false; + } + + if (range_info.GetReadable() == MemoryRegionInfo::eYes) + permissions |= lldb::ePermissionsReadable; + + if (range_info.GetWritable() == MemoryRegionInfo::eYes) + permissions |= lldb::ePermissionsWritable; + + if (range_info.GetExecutable() == MemoryRegionInfo::eYes) + permissions |= lldb::ePermissionsExecutable; + + return true; + } + + //------------------------------------------------------------------ + /// Determines whether executing JIT-compiled code in this process + /// is possible. + /// + /// @return + /// True if execution of JIT code is possible; false otherwise. + //------------------------------------------------------------------ + bool CanJIT (); + + //------------------------------------------------------------------ + /// Sets whether executing JIT-compiled code in this process + /// is possible. + /// + /// @param[in] can_jit + /// True if execution of JIT code is possible; false otherwise. + //------------------------------------------------------------------ + void SetCanJIT (bool can_jit); + + //------------------------------------------------------------------ + /// Actually deallocate memory in the process. + /// + /// This function will deallocate memory in the process's address + /// space that was allocated with AllocateMemory. + /// + /// @param[in] ptr + /// A return value from AllocateMemory, pointing to the memory you + /// want to deallocate. + /// + /// @return + /// \btrue if the memory was deallocated, \bfalse otherwise. + //------------------------------------------------------------------ + + virtual Error + DoDeallocateMemory (lldb::addr_t ptr) + { + Error error; + error.SetErrorStringWithFormat("error: %s does not support deallocating in the debug process", GetPluginName().GetCString()); + return error; + } + + + //------------------------------------------------------------------ + /// The public interface to deallocating memory in the process. + /// + /// This function will deallocate memory in the process's address + /// space that was allocated with AllocateMemory. + /// + /// @param[in] ptr + /// A return value from AllocateMemory, pointing to the memory you + /// want to deallocate. + /// + /// @return + /// \btrue if the memory was deallocated, \bfalse otherwise. + //------------------------------------------------------------------ + + Error + DeallocateMemory (lldb::addr_t ptr); + + //------------------------------------------------------------------ + /// Get any available STDOUT. + /// + /// If the process was launched without supplying valid file paths + /// for stdin, stdout, and stderr, then the Process class might + /// try to cache the STDOUT for the process if it is able. Events + /// will be queued indicating that there is STDOUT available that + /// can be retrieved using this function. + /// + /// @param[out] buf + /// A buffer that will receive any STDOUT bytes that are + /// currently available. + /// + /// @param[out] buf_size + /// The size in bytes for the buffer \a buf. + /// + /// @return + /// The number of bytes written into \a buf. If this value is + /// equal to \a buf_size, another call to this function should + /// be made to retrieve more STDOUT data. + //------------------------------------------------------------------ + virtual size_t + GetSTDOUT (char *buf, size_t buf_size, Error &error); + + //------------------------------------------------------------------ + /// Get any available STDERR. + /// + /// If the process was launched without supplying valid file paths + /// for stdin, stdout, and stderr, then the Process class might + /// try to cache the STDERR for the process if it is able. Events + /// will be queued indicating that there is STDERR available that + /// can be retrieved using this function. + /// + /// @param[out] buf + /// A buffer that will receive any STDERR bytes that are + /// currently available. + /// + /// @param[out] buf_size + /// The size in bytes for the buffer \a buf. + /// + /// @return + /// The number of bytes written into \a buf. If this value is + /// equal to \a buf_size, another call to this function should + /// be made to retrieve more STDERR data. + //------------------------------------------------------------------ + virtual size_t + GetSTDERR (char *buf, size_t buf_size, Error &error); + + virtual size_t + PutSTDIN (const char *buf, size_t buf_size, Error &error) + { + error.SetErrorString("stdin unsupported"); + return 0; + } + + //------------------------------------------------------------------ + /// Get any available profile data. + /// + /// @param[out] buf + /// A buffer that will receive any profile data bytes that are + /// currently available. + /// + /// @param[out] buf_size + /// The size in bytes for the buffer \a buf. + /// + /// @return + /// The number of bytes written into \a buf. If this value is + /// equal to \a buf_size, another call to this function should + /// be made to retrieve more profile data. + //------------------------------------------------------------------ + virtual size_t + GetAsyncProfileData (char *buf, size_t buf_size, Error &error); + + //---------------------------------------------------------------------- + // Process Breakpoints + //---------------------------------------------------------------------- + size_t + GetSoftwareBreakpointTrapOpcode (BreakpointSite* bp_site); + + virtual Error + EnableBreakpointSite (BreakpointSite *bp_site) + { + Error error; + error.SetErrorStringWithFormat("error: %s does not support enabling breakpoints", GetPluginName().GetCString()); + return error; + } + + + virtual Error + DisableBreakpointSite (BreakpointSite *bp_site) + { + Error error; + error.SetErrorStringWithFormat("error: %s does not support disabling breakpoints", GetPluginName().GetCString()); + return error; + } + + + // This is implemented completely using the lldb::Process API. Subclasses + // don't need to implement this function unless the standard flow of + // read existing opcode, write breakpoint opcode, verify breakpoint opcode + // doesn't work for a specific process plug-in. + virtual Error + EnableSoftwareBreakpoint (BreakpointSite *bp_site); + + // This is implemented completely using the lldb::Process API. Subclasses + // don't need to implement this function unless the standard flow of + // restoring original opcode in memory and verifying the restored opcode + // doesn't work for a specific process plug-in. + virtual Error + DisableSoftwareBreakpoint (BreakpointSite *bp_site); + + BreakpointSiteList & + GetBreakpointSiteList(); + + const BreakpointSiteList & + GetBreakpointSiteList() const; + + void + DisableAllBreakpointSites (); + + Error + ClearBreakpointSiteByID (lldb::user_id_t break_id); + + lldb::break_id_t + CreateBreakpointSite (const lldb::BreakpointLocationSP &owner, + bool use_hardware); + + Error + DisableBreakpointSiteByID (lldb::user_id_t break_id); + + Error + EnableBreakpointSiteByID (lldb::user_id_t break_id); + + + // BreakpointLocations use RemoveOwnerFromBreakpointSite to remove + // themselves from the owner's list of this breakpoint sites. + void + RemoveOwnerFromBreakpointSite (lldb::user_id_t owner_id, + lldb::user_id_t owner_loc_id, + lldb::BreakpointSiteSP &bp_site_sp); + + //---------------------------------------------------------------------- + // Process Watchpoints (optional) + //---------------------------------------------------------------------- + virtual Error + EnableWatchpoint (Watchpoint *wp, bool notify = true); + + virtual Error + DisableWatchpoint (Watchpoint *wp, bool notify = true); + + //------------------------------------------------------------------ + // Thread Queries + //------------------------------------------------------------------ + virtual bool + UpdateThreadList (ThreadList &old_thread_list, ThreadList &new_thread_list) = 0; + + void + UpdateThreadListIfNeeded (); + + ThreadList & + GetThreadList () + { + return m_thread_list; + } + + uint32_t + GetNextThreadIndexID (uint64_t thread_id); + + lldb::ThreadSP + CreateOSPluginThread (lldb::tid_t tid, lldb::addr_t context); + + // Returns true if an index id has been assigned to a thread. + bool + HasAssignedIndexIDToThread(uint64_t sb_thread_id); + + // Given a thread_id, it will assign a more reasonable index id for display to the user. + // If the thread_id has previously been assigned, the same index id will be used. + uint32_t + AssignIndexIDToThread(uint64_t thread_id); + + //------------------------------------------------------------------ + // Event Handling + //------------------------------------------------------------------ + lldb::StateType + GetNextEvent (lldb::EventSP &event_sp); + + lldb::StateType + WaitForProcessToStop (const TimeValue *timeout, lldb::EventSP *event_sp_ptr = NULL); + + lldb::StateType + WaitForStateChangedEvents (const TimeValue *timeout, lldb::EventSP &event_sp); + + Event * + PeekAtStateChangedEvents (); + + + class + ProcessEventHijacker + { + public: + ProcessEventHijacker (Process &process, Listener *listener) : + m_process (process) + { + m_process.HijackProcessEvents (listener); + } + ~ProcessEventHijacker () + { + m_process.RestoreProcessEvents(); + } + + private: + Process &m_process; + }; + friend class ProcessEventHijacker; + //------------------------------------------------------------------ + /// If you need to ensure that you and only you will hear about some public + /// event, then make a new listener, set to listen to process events, and + /// then call this with that listener. Then you will have to wait on that + /// listener explicitly for events (rather than using the GetNextEvent & WaitFor* + /// calls above. Be sure to call RestoreProcessEvents when you are done. + /// + /// @param[in] listener + /// This is the new listener to whom all process events will be delivered. + /// + /// @return + /// Returns \b true if the new listener could be installed, + /// \b false otherwise. + //------------------------------------------------------------------ + bool + HijackProcessEvents (Listener *listener); + + //------------------------------------------------------------------ + /// Restores the process event broadcasting to its normal state. + /// + //------------------------------------------------------------------ + void + RestoreProcessEvents (); + +private: + //------------------------------------------------------------------ + /// This is the part of the event handling that for a process event. + /// It decides what to do with the event and returns true if the + /// event needs to be propagated to the user, and false otherwise. + /// If the event is not propagated, this call will most likely set + /// the target to executing again. + /// There is only one place where this call should be called, HandlePrivateEvent. + /// Don't call it from anywhere else... + /// + /// @param[in] event_ptr + /// This is the event we are handling. + /// + /// @return + /// Returns \b true if the event should be reported to the + /// user, \b false otherwise. + //------------------------------------------------------------------ + bool + ShouldBroadcastEvent (Event *event_ptr); + +public: + const lldb::ABISP & + GetABI (); + + OperatingSystem * + GetOperatingSystem () + { + return m_os_ap.get(); + } + + + virtual LanguageRuntime * + GetLanguageRuntime (lldb::LanguageType language, bool retry_if_null = true); + + virtual CPPLanguageRuntime * + GetCPPLanguageRuntime (bool retry_if_null = true); + + virtual ObjCLanguageRuntime * + GetObjCLanguageRuntime (bool retry_if_null = true); + + bool + IsPossibleDynamicValue (ValueObject& in_value); + + bool + IsRunning () const; + + DynamicCheckerFunctions *GetDynamicCheckers() + { + return m_dynamic_checkers_ap.get(); + } + + void SetDynamicCheckers(DynamicCheckerFunctions *dynamic_checkers) + { + m_dynamic_checkers_ap.reset(dynamic_checkers); + } + + //------------------------------------------------------------------ + /// Call this to set the lldb in the mode where it breaks on new thread + /// creations, and then auto-restarts. This is useful when you are trying + /// to run only one thread, but either that thread or the kernel is creating + /// new threads in the process. If you stop when the thread is created, you + /// can immediately suspend it, and keep executing only the one thread you intend. + /// + /// @return + /// Returns \b true if we were able to start up the notification + /// \b false otherwise. + //------------------------------------------------------------------ + virtual bool + StartNoticingNewThreads() + { + return true; + } + + //------------------------------------------------------------------ + /// Call this to turn off the stop & notice new threads mode. + /// + /// @return + /// Returns \b true if we were able to start up the notification + /// \b false otherwise. + //------------------------------------------------------------------ + virtual bool + StopNoticingNewThreads() + { + return true; + } + + void + SetRunningUserExpression (bool on); + + //------------------------------------------------------------------ + // lldb::ExecutionContextScope pure virtual functions + //------------------------------------------------------------------ + virtual lldb::TargetSP + CalculateTarget (); + + virtual lldb::ProcessSP + CalculateProcess () + { + return shared_from_this(); + } + + virtual lldb::ThreadSP + CalculateThread () + { + return lldb::ThreadSP(); + } + + virtual lldb::StackFrameSP + CalculateStackFrame () + { + return lldb::StackFrameSP(); + } + + virtual void + CalculateExecutionContext (ExecutionContext &exe_ctx); + + void + SetSTDIOFileDescriptor (int file_descriptor); + + //------------------------------------------------------------------ + // Add a permanent region of memory that should never be read or + // written to. This can be used to ensure that memory reads or writes + // to certain areas of memory never end up being sent to the + // DoReadMemory or DoWriteMemory functions which can improve + // performance. + //------------------------------------------------------------------ + void + AddInvalidMemoryRegion (const LoadRange ®ion); + + //------------------------------------------------------------------ + // Remove a permanent region of memory that should never be read or + // written to that was previously added with AddInvalidMemoryRegion. + //------------------------------------------------------------------ + bool + RemoveInvalidMemoryRange (const LoadRange ®ion); + + //------------------------------------------------------------------ + // If the setup code of a thread plan needs to do work that might involve + // calling a function in the target, it should not do that work directly + // in one of the thread plan functions (DidPush/WillResume) because + // such work needs to be handled carefully. Instead, put that work in + // a PreResumeAction callback, and register it with the process. It will + // get done before the actual "DoResume" gets called. + //------------------------------------------------------------------ + + typedef bool (PreResumeActionCallback)(void *); + + void + AddPreResumeAction (PreResumeActionCallback callback, void *baton); + + bool + RunPreResumeActions (); + + void + ClearPreResumeActions (); + + ProcessRunLock & + GetRunLock () + { + if (Host::GetCurrentThread() == m_private_state_thread) + return m_private_run_lock; + else + return m_public_run_lock; + } + +protected: + //------------------------------------------------------------------ + // NextEventAction provides a way to register an action on the next + // event that is delivered to this process. There is currently only + // one next event action allowed in the process at one time. If a + // new "NextEventAction" is added while one is already present, the + // old action will be discarded (with HandleBeingUnshipped called + // after it is discarded.) + // + // If you want to resume the process as a result of a resume action, + // call RequestResume, don't call Resume directly. + //------------------------------------------------------------------ + class NextEventAction + { + public: + typedef enum EventActionResult + { + eEventActionSuccess, + eEventActionRetry, + eEventActionExit + } EventActionResult; + + NextEventAction (Process *process) : + m_process(process) + { + } + + virtual + ~NextEventAction() + { + } + + virtual EventActionResult PerformAction (lldb::EventSP &event_sp) = 0; + virtual void HandleBeingUnshipped () {} + virtual EventActionResult HandleBeingInterrupted () = 0; + virtual const char *GetExitString() = 0; + void RequestResume() + { + m_process->m_resume_requested = true; + } + protected: + Process *m_process; + }; + + void SetNextEventAction (Process::NextEventAction *next_event_action) + { + if (m_next_event_action_ap.get()) + m_next_event_action_ap->HandleBeingUnshipped(); + + m_next_event_action_ap.reset(next_event_action); + } + + // This is the completer for Attaching: + class AttachCompletionHandler : public NextEventAction + { + public: + AttachCompletionHandler (Process *process, uint32_t exec_count) : + NextEventAction (process), + m_exec_count (exec_count) + { + } + + virtual + ~AttachCompletionHandler() + { + } + + virtual EventActionResult PerformAction (lldb::EventSP &event_sp); + virtual EventActionResult HandleBeingInterrupted (); + virtual const char *GetExitString(); + private: + uint32_t m_exec_count; + std::string m_exit_string; + }; + + bool + HijackPrivateProcessEvents (Listener *listener); + + void + RestorePrivateProcessEvents (); + + bool + PrivateStateThreadIsValid () const + { + return IS_VALID_LLDB_HOST_THREAD(m_private_state_thread); + } + + //------------------------------------------------------------------ + // Type definitions + //------------------------------------------------------------------ + typedef std::map LanguageRuntimeCollection; + + struct PreResumeCallbackAndBaton + { + bool (*callback) (void *); + void *baton; + PreResumeCallbackAndBaton (PreResumeActionCallback in_callback, void *in_baton) : + callback (in_callback), + baton (in_baton) + { + } + }; + + //------------------------------------------------------------------ + // Member variables + //------------------------------------------------------------------ + Target & m_target; ///< The target that owns this process. + ThreadSafeValue m_public_state; + ThreadSafeValue m_private_state; // The actual state of our process + Broadcaster m_private_state_broadcaster; // This broadcaster feeds state changed events into the private state thread's listener. + Broadcaster m_private_state_control_broadcaster; // This is the control broadcaster, used to pause, resume & stop the private state thread. + Listener m_private_state_listener; // This is the listener for the private state thread. + Predicate m_private_state_control_wait; /// This Predicate is used to signal that a control operation is complete. + lldb::thread_t m_private_state_thread; // Thread ID for the thread that watches interal state events + ProcessModID m_mod_id; ///< Tracks the state of the process over stops and other alterations. + uint32_t m_process_unique_id; ///< Each lldb_private::Process class that is created gets a unique integer ID that increments with each new instance + uint32_t m_thread_index_id; ///< Each thread is created with a 1 based index that won't get re-used. + std::map m_thread_id_to_index_id_map; + int m_exit_status; ///< The exit status of the process, or -1 if not set. + std::string m_exit_string; ///< A textual description of why a process exited. + Mutex m_thread_mutex; + ThreadList m_thread_list_real; ///< The threads for this process as are known to the protocol we are debugging with + ThreadList m_thread_list; ///< The threads for this process as the user will see them. This is usually the same as + ///< m_thread_list_real, but might be different if there is an OS plug-in creating memory threads + std::vector m_notifications; ///< The list of notifications that this process can deliver. + std::vector m_image_tokens; + Listener &m_listener; + BreakpointSiteList m_breakpoint_site_list; ///< This is the list of breakpoint locations we intend to insert in the target. + std::unique_ptr m_dyld_ap; + std::unique_ptr m_dynamic_checkers_ap; ///< The functions used by the expression parser to validate data that expressions use. + std::unique_ptr m_os_ap; + UnixSignals m_unix_signals; /// This is the current signal set for this process. + lldb::ABISP m_abi_sp; + lldb::InputReaderSP m_process_input_reader; + Communication m_stdio_communication; + Mutex m_stdio_communication_mutex; + std::string m_stdout_data; + std::string m_stderr_data; + Mutex m_profile_data_comm_mutex; + std::vector m_profile_data; + MemoryCache m_memory_cache; + AllocatedMemoryCache m_allocated_memory_cache; + bool m_should_detach; /// Should we detach if the process object goes away with an explicit call to Kill or Detach? + LanguageRuntimeCollection m_language_runtimes; + std::unique_ptr m_next_event_action_ap; + std::vector m_pre_resume_actions; + ProcessRunLock m_public_run_lock; + ProcessRunLock m_private_run_lock; + Predicate m_currently_handling_event; // This predicate is set in HandlePrivateEvent while all its business is being done. + bool m_currently_handling_do_on_removals; + bool m_resume_requested; // If m_currently_handling_event or m_currently_handling_do_on_removals are true, Resume will only request a resume, using this flag to check. + bool m_finalize_called; + bool m_clear_thread_plans_on_stop; + lldb::StateType m_last_broadcast_state; /// This helps with the Public event coalescing in ShouldBroadcastEvent. + bool m_destroy_in_process; + + enum { + eCanJITDontKnow= 0, + eCanJITYes, + eCanJITNo + } m_can_jit; + + size_t + RemoveBreakpointOpcodesFromBuffer (lldb::addr_t addr, size_t size, uint8_t *buf) const; + + void + SynchronouslyNotifyStateChanged (lldb::StateType state); + + void + SetPublicState (lldb::StateType new_state, bool restarted); + + void + SetPrivateState (lldb::StateType state); + + bool + StartPrivateStateThread (bool force = false); + + void + StopPrivateStateThread (); + + void + PausePrivateStateThread (); + + void + ResumePrivateStateThread (); + + static void * + PrivateStateThread (void *arg); + + void * + RunPrivateStateThread (); + + void + HandlePrivateEvent (lldb::EventSP &event_sp); + + lldb::StateType + WaitForProcessStopPrivate (const TimeValue *timeout, lldb::EventSP &event_sp); + + // This waits for both the state change broadcaster, and the control broadcaster. + // If control_only, it only waits for the control broadcaster. + + bool + WaitForEventsPrivate (const TimeValue *timeout, lldb::EventSP &event_sp, bool control_only); + + lldb::StateType + WaitForStateChangedEventsPrivate (const TimeValue *timeout, lldb::EventSP &event_sp); + + lldb::StateType + WaitForState (const TimeValue *timeout, + const lldb::StateType *match_states, + const uint32_t num_match_states); + + size_t + WriteMemoryPrivate (lldb::addr_t addr, const void *buf, size_t size, Error &error); + + void + AppendSTDOUT (const char *s, size_t len); + + void + AppendSTDERR (const char *s, size_t len); + + void + BroadcastAsyncProfileData(const std::string &one_profile_data); + + static void + STDIOReadThreadBytesReceived (void *baton, const void *src, size_t src_len); + + void + PushProcessInputReader (); + + void + PopProcessInputReader (); + + void + ResetProcessInputReader (); + + static size_t + ProcessInputReaderCallback (void *baton, + InputReader &reader, + lldb::InputReaderAction notification, + const char *bytes, + size_t bytes_len); + + Error + HaltForDestroyOrDetach(lldb::EventSP &exit_event_sp); + +private: + //------------------------------------------------------------------ + // For Process only + //------------------------------------------------------------------ + void ControlPrivateStateThread (uint32_t signal); + + DISALLOW_COPY_AND_ASSIGN (Process); + +}; + +} // namespace lldb_private + +#endif // liblldb_Process_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Target/RegisterContext.h b/contrib/llvm/tools/lldb/include/lldb/Target/RegisterContext.h new file mode 100644 index 00000000000..dd0e73fc7eb --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Target/RegisterContext.h @@ -0,0 +1,213 @@ +//===-- RegisterContext.h ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_RegisterContext_h_ +#define liblldb_RegisterContext_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private.h" +#include "lldb/Target/ExecutionContextScope.h" + +namespace lldb_private { + +class RegisterContext : + public std::enable_shared_from_this, + public ExecutionContextScope +{ +public: + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + RegisterContext (Thread &thread, uint32_t concrete_frame_idx); + + virtual + ~RegisterContext (); + + void + InvalidateIfNeeded (bool force); + + //------------------------------------------------------------------ + // Subclasses must override these functions + //------------------------------------------------------------------ + virtual void + InvalidateAllRegisters () = 0; + + virtual size_t + GetRegisterCount () = 0; + + virtual const RegisterInfo * + GetRegisterInfoAtIndex (size_t reg) = 0; + + virtual size_t + GetRegisterSetCount () = 0; + + virtual const RegisterSet * + GetRegisterSet (size_t reg_set) = 0; + + virtual bool + ReadRegister (const RegisterInfo *reg_info, RegisterValue ®_value) = 0; + + virtual bool + WriteRegister (const RegisterInfo *reg_info, const RegisterValue ®_value) = 0; + + // These two functions are used to implement "push" and "pop" of register states. They are used primarily + // for expression evaluation, where we need to push a new state (storing the old one in data_sp) and then + // restoring the original state by passing the data_sp we got from ReadAllRegisters to WriteAllRegisterValues. + // ReadAllRegisters will do what is necessary to return a coherent set of register values for this thread, which + // may mean e.g. interrupting a thread that is sitting in a kernel trap. That is a somewhat disruptive operation, + // so these API's should only be used when this behavior is needed. + + virtual bool + ReadAllRegisterValues (lldb::DataBufferSP &data_sp) = 0; + + virtual bool + WriteAllRegisterValues (const lldb::DataBufferSP &data_sp) = 0; + + bool + CopyFromRegisterContext (lldb::RegisterContextSP context); + + virtual uint32_t + ConvertRegisterKindToRegisterNumber (uint32_t kind, uint32_t num) = 0; + + //------------------------------------------------------------------ + // Subclasses can override these functions if desired + //------------------------------------------------------------------ + virtual uint32_t + NumSupportedHardwareBreakpoints (); + + virtual uint32_t + SetHardwareBreakpoint (lldb::addr_t addr, size_t size); + + virtual bool + ClearHardwareBreakpoint (uint32_t hw_idx); + + virtual uint32_t + NumSupportedHardwareWatchpoints (); + + virtual uint32_t + SetHardwareWatchpoint (lldb::addr_t addr, size_t size, bool read, bool write); + + virtual bool + ClearHardwareWatchpoint (uint32_t hw_index); + + virtual bool + HardwareSingleStep (bool enable); + + virtual Error + ReadRegisterValueFromMemory (const lldb_private::RegisterInfo *reg_info, lldb::addr_t src_addr, uint32_t src_len, RegisterValue ®_value); + + virtual Error + WriteRegisterValueToMemory (const lldb_private::RegisterInfo *reg_info, lldb::addr_t dst_addr, uint32_t dst_len, const RegisterValue ®_value); + + //------------------------------------------------------------------ + // Subclasses should not override these + //------------------------------------------------------------------ + virtual lldb::tid_t + GetThreadID() const; + + virtual Thread & + GetThread () + { + return m_thread; + } + + const RegisterInfo * + GetRegisterInfoByName (const char *reg_name, uint32_t start_idx = 0); + + uint64_t + GetPC (uint64_t fail_value = LLDB_INVALID_ADDRESS); + + bool + SetPC (uint64_t pc); + + uint64_t + GetSP (uint64_t fail_value = LLDB_INVALID_ADDRESS); + + bool + SetSP (uint64_t sp); + + uint64_t + GetFP (uint64_t fail_value = LLDB_INVALID_ADDRESS); + + bool + SetFP (uint64_t fp); + + const char * + GetRegisterName (uint32_t reg); + + uint64_t + GetReturnAddress (uint64_t fail_value = LLDB_INVALID_ADDRESS); + + uint64_t + GetFlags (uint64_t fail_value = 0); + + uint64_t + ReadRegisterAsUnsigned (uint32_t reg, uint64_t fail_value); + + uint64_t + ReadRegisterAsUnsigned (const RegisterInfo *reg_info, uint64_t fail_value); + + bool + WriteRegisterFromUnsigned (uint32_t reg, uint64_t uval); + + bool + WriteRegisterFromUnsigned (const RegisterInfo *reg_info, uint64_t uval); + bool + ConvertBetweenRegisterKinds (int source_rk, uint32_t source_regnum, int target_rk, uint32_t& target_regnum); + + //------------------------------------------------------------------ + // lldb::ExecutionContextScope pure virtual functions + //------------------------------------------------------------------ + virtual lldb::TargetSP + CalculateTarget (); + + virtual lldb::ProcessSP + CalculateProcess (); + + virtual lldb::ThreadSP + CalculateThread (); + + virtual lldb::StackFrameSP + CalculateStackFrame (); + + virtual void + CalculateExecutionContext (ExecutionContext &exe_ctx); + + uint32_t + GetStopID () const + { + return m_stop_id; + } + + void + SetStopID (uint32_t stop_id) + { + m_stop_id = stop_id; + } + +protected: + //------------------------------------------------------------------ + // Classes that inherit from RegisterContext can see and modify these + //------------------------------------------------------------------ + Thread &m_thread; // The thread that this register context belongs to. + uint32_t m_concrete_frame_idx; // The concrete frame index for this register context + uint32_t m_stop_id; // The stop ID that any data in this context is valid for +private: + //------------------------------------------------------------------ + // For RegisterContext only + //------------------------------------------------------------------ + DISALLOW_COPY_AND_ASSIGN (RegisterContext); +}; + +} // namespace lldb_private + +#endif // liblldb_RegisterContext_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Target/SectionLoadList.h b/contrib/llvm/tools/lldb/include/lldb/Target/SectionLoadList.h new file mode 100644 index 00000000000..ac05bf7a9cb --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Target/SectionLoadList.h @@ -0,0 +1,89 @@ +//===-- SectionLoadList.h -----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_SectionLoadList_h_ +#define liblldb_SectionLoadList_h_ + +// C Includes +// C++ Includes +#include + +// Other libraries and framework includes +#include "llvm/ADT/DenseMap.h" +// Project includes +#include "lldb/lldb-public.h" +#include "lldb/Host/Mutex.h" + +namespace lldb_private { + +class SectionLoadList +{ +public: + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + SectionLoadList () : + m_addr_to_sect (), + m_sect_to_addr (), + m_mutex (Mutex::eMutexTypeRecursive) + + { + } + + ~SectionLoadList() + { + // Call clear since this takes a lock and clears the section load list + // in case another thread is currently using this section load list + Clear(); + } + + bool + IsEmpty() const; + + void + Clear (); + + lldb::addr_t + GetSectionLoadAddress (const lldb::SectionSP §ion_sp) const; + + bool + ResolveLoadAddress (lldb::addr_t load_addr, Address &so_addr) const; + + bool + SetSectionLoadAddress (const lldb::SectionSP §ion_sp, lldb::addr_t load_addr, bool warn_multiple = false); + + // The old load address should be specified when unloading to ensure we get + // the correct instance of the section as a shared library could be loaded + // at more than one location. + bool + SetSectionUnloaded (const lldb::SectionSP §ion_sp, lldb::addr_t load_addr); + + // Unload all instances of a section. This function can be used on systems + // that don't support multiple copies of the same shared library to be + // loaded at the same time. + size_t + SetSectionUnloaded (const lldb::SectionSP §ion_sp); + + void + Dump (Stream &s, Target *target); + +protected: + typedef std::map addr_to_sect_collection; + typedef llvm::DenseMap sect_to_addr_collection; + addr_to_sect_collection m_addr_to_sect; + sect_to_addr_collection m_sect_to_addr; + mutable Mutex m_mutex; + +private: + DISALLOW_COPY_AND_ASSIGN (SectionLoadList); +}; + +} // namespace lldb_private + +#endif // liblldb_SectionLoadList_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Target/StackFrame.h b/contrib/llvm/tools/lldb/include/lldb/Target/StackFrame.h new file mode 100644 index 00000000000..877bd8ce661 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Target/StackFrame.h @@ -0,0 +1,207 @@ +//===-- StackFrame.h --------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_StackFrame_h_ +#define liblldb_StackFrame_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/Error.h" +#include "lldb/Core/Flags.h" +#include "lldb/Core/Scalar.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Core/UserID.h" +#include "lldb/Core/ValueObjectList.h" +#include "lldb/Symbol/SymbolContext.h" +#include "lldb/Target/ExecutionContextScope.h" +#include "lldb/Target/StackID.h" + +namespace lldb_private { + +class StackFrame : + public std::enable_shared_from_this, + public ExecutionContextScope +{ +public: + enum ExpressionPathOption + { + eExpressionPathOptionCheckPtrVsMember = (1u << 0), + eExpressionPathOptionsNoFragileObjcIvar = (1u << 1), + eExpressionPathOptionsNoSyntheticChildren = (1u << 2), + eExpressionPathOptionsNoSyntheticArrayRange = (1u << 3), + eExpressionPathOptionsAllowDirectIVarAccess = (1u << 4) + }; + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + StackFrame (const lldb::ThreadSP &thread_sp, + lldb::user_id_t frame_idx, + lldb::user_id_t concrete_frame_idx, + lldb::addr_t cfa, + lldb::addr_t pc, + const SymbolContext *sc_ptr); + + StackFrame (const lldb::ThreadSP &thread_sp, + lldb::user_id_t frame_idx, + lldb::user_id_t concrete_frame_idx, + const lldb::RegisterContextSP ®_context_sp, + lldb::addr_t cfa, + lldb::addr_t pc, + const SymbolContext *sc_ptr); + + StackFrame (const lldb::ThreadSP &thread_sp, + lldb::user_id_t frame_idx, + lldb::user_id_t concrete_frame_idx, + const lldb::RegisterContextSP ®_context_sp, + lldb::addr_t cfa, + const Address& pc, + const SymbolContext *sc_ptr); + + virtual ~StackFrame (); + + lldb::ThreadSP + GetThread () const + { + return m_thread_wp.lock(); + } + + StackID& + GetStackID(); + + const Address& + GetFrameCodeAddress(); + + void + ChangePC (lldb::addr_t pc); + + const SymbolContext& + GetSymbolContext (uint32_t resolve_scope); + + bool + GetFrameBaseValue(Scalar &value, Error *error_ptr); + + Block * + GetFrameBlock (); + + lldb::RegisterContextSP + GetRegisterContext (); + + const lldb::RegisterContextSP & + GetRegisterContextSP () const + { + return m_reg_context_sp; + } + + VariableList * + GetVariableList (bool get_file_globals); + + lldb::VariableListSP + GetInScopeVariableList (bool get_file_globals); + + // See ExpressionPathOption enumeration for "options" values + lldb::ValueObjectSP + GetValueForVariableExpressionPath (const char *var_expr, + lldb::DynamicValueType use_dynamic, + uint32_t options, + lldb::VariableSP &var_sp, + Error &error); + + bool + HasDebugInformation (); + + const char * + Disassemble (); + + void + DumpUsingSettingsFormat (Stream *strm); + + void + Dump (Stream *strm, bool show_frame_index, bool show_fullpaths); + + bool + IsInlined (); + + uint32_t + GetFrameIndex () const; + + uint32_t + GetConcreteFrameIndex () const + { + return m_concrete_frame_index; + } + + lldb::ValueObjectSP + GetValueObjectForFrameVariable (const lldb::VariableSP &variable_sp, lldb::DynamicValueType use_dynamic); + + lldb::ValueObjectSP + TrackGlobalVariable (const lldb::VariableSP &variable_sp, lldb::DynamicValueType use_dynamic); + + //------------------------------------------------------------------ + // lldb::ExecutionContextScope pure virtual functions + //------------------------------------------------------------------ + virtual lldb::TargetSP + CalculateTarget (); + + virtual lldb::ProcessSP + CalculateProcess (); + + virtual lldb::ThreadSP + CalculateThread (); + + virtual lldb::StackFrameSP + CalculateStackFrame (); + + virtual void + CalculateExecutionContext (ExecutionContext &exe_ctx); + + bool + GetStatus (Stream &strm, + bool show_frame_info, + bool show_source); + +protected: + friend class StackFrameList; + + void + SetSymbolContextScope (SymbolContextScope *symbol_scope); + + void + UpdateCurrentFrameFromPreviousFrame (StackFrame &prev_frame); + + void + UpdatePreviousFrameFromCurrentFrame (StackFrame &curr_frame); + + bool + HasCachedData () const; + +private: + //------------------------------------------------------------------ + // For StackFrame only + //------------------------------------------------------------------ + lldb::ThreadWP m_thread_wp; + uint32_t m_frame_index; + uint32_t m_concrete_frame_index; + lldb::RegisterContextSP m_reg_context_sp; + StackID m_id; + Address m_frame_code_addr; // The frame code address (might not be the same as the actual PC for inlined frames) as a section/offset address + SymbolContext m_sc; + Flags m_flags; + Scalar m_frame_base; + Error m_frame_base_error; + lldb::VariableListSP m_variable_list_sp; + ValueObjectList m_variable_list_value_objects; // Value objects for each variable in m_variable_list_sp + StreamString m_disassembly; + DISALLOW_COPY_AND_ASSIGN (StackFrame); +}; + +} // namespace lldb_private + +#endif // liblldb_StackFrame_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Target/StackFrameList.h b/contrib/llvm/tools/lldb/include/lldb/Target/StackFrameList.h new file mode 100644 index 00000000000..b2689d0391e --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Target/StackFrameList.h @@ -0,0 +1,157 @@ +//===-- StackFrameList.h ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_StackFrameList_h_ +#define liblldb_StackFrameList_h_ + +// C Includes +// C++ Includes +#include + +// Other libraries and framework includes +// Project includes +#include "lldb/Host/Mutex.h" +#include "lldb/Target/StackFrame.h" + +namespace lldb_private { + +class StackFrameList +{ +public: + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + StackFrameList (Thread &thread, + const lldb::StackFrameListSP &prev_frames_sp, + bool show_inline_frames); + + ~StackFrameList(); + + uint32_t + GetNumFrames (bool can_create = true); + + lldb::StackFrameSP + GetFrameAtIndex (uint32_t idx); + + lldb::StackFrameSP + GetFrameWithConcreteFrameIndex (uint32_t unwind_idx); + + lldb::StackFrameSP + GetFrameWithStackID (const StackID &stack_id); + + // Mark a stack frame as the current frame + uint32_t + SetSelectedFrame (lldb_private::StackFrame *frame); + + uint32_t + GetSelectedFrameIndex () const; + + // Mark a stack frame as the current frame using the frame index + bool + SetSelectedFrameByIndex (uint32_t idx); + + uint32_t + GetVisibleStackFrameIndex(uint32_t idx) + { + if (m_current_inlined_depth < UINT32_MAX) + return idx - m_current_inlined_depth; + else + return idx; + } + + void + CalculateCurrentInlinedDepth (); + + void + SetDefaultFileAndLineToSelectedFrame(); + + void + Clear (); + + void + InvalidateFrames (uint32_t start_idx); + + void + Dump (Stream *s); + + lldb::StackFrameSP + GetStackFrameSPForStackFramePtr (StackFrame *stack_frame_ptr); + + size_t + GetStatus (Stream &strm, + uint32_t first_frame, + uint32_t num_frames, + bool show_frame_info, + uint32_t num_frames_with_source); + +protected: + + friend class Thread; + + bool + SetFrameAtIndex (uint32_t idx, lldb::StackFrameSP &frame_sp); + + static void + Merge (std::unique_ptr& curr_ap, + lldb::StackFrameListSP& prev_sp); + + void + GetFramesUpTo (uint32_t end_idx); + + bool + GetAllFramesFetched() + { + return m_concrete_frames_fetched == UINT32_MAX; + } + + void + SetAllFramesFetched () + { + m_concrete_frames_fetched = UINT32_MAX; + } + + bool + DecrementCurrentInlinedDepth (); + + void + ResetCurrentInlinedDepth(); + + uint32_t + GetCurrentInlinedDepth (); + + void + SetCurrentInlinedDepth (uint32_t new_depth); + + //------------------------------------------------------------------ + // Classes that inherit from StackFrameList can see and modify these + //------------------------------------------------------------------ + typedef std::vector collection; + typedef collection::iterator iterator; + typedef collection::const_iterator const_iterator; + + Thread &m_thread; + lldb::StackFrameListSP m_prev_frames_sp; + mutable Mutex m_mutex; + collection m_frames; + uint32_t m_selected_frame_idx; + uint32_t m_concrete_frames_fetched; + uint32_t m_current_inlined_depth; + lldb::addr_t m_current_inlined_pc; + bool m_show_inlined_frames; + +private: + //------------------------------------------------------------------ + // For StackFrameList only + //------------------------------------------------------------------ + DISALLOW_COPY_AND_ASSIGN (StackFrameList); +}; + +} // namespace lldb_private + +#endif // liblldb_StackFrameList_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Target/StackID.h b/contrib/llvm/tools/lldb/include/lldb/Target/StackID.h new file mode 100644 index 00000000000..7e713c73d97 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Target/StackID.h @@ -0,0 +1,149 @@ +//===-- StackID.h -----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_StackID_h_ +#define liblldb_StackID_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private.h" +#include "lldb/Core/AddressRange.h" + +namespace lldb_private { + +class StackID +{ +public: + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + StackID () : + m_pc (LLDB_INVALID_ADDRESS), + m_cfa (LLDB_INVALID_ADDRESS), + m_symbol_scope (NULL) + { + } + + explicit + StackID (lldb::addr_t pc, lldb::addr_t cfa, SymbolContextScope *symbol_scope) : + m_pc (pc), + m_cfa (cfa), + m_symbol_scope (symbol_scope) + { + } + + StackID (const StackID& rhs) : + m_pc (rhs.m_pc), + m_cfa (rhs.m_cfa), + m_symbol_scope (rhs.m_symbol_scope) + { + } + + ~StackID() + { + } + + lldb::addr_t + GetPC() const + { + return m_pc; + } + + lldb::addr_t + GetCallFrameAddress() const + { + return m_cfa; + } + + SymbolContextScope * + GetSymbolContextScope () const + { + return m_symbol_scope; + } + + void + SetSymbolContextScope (SymbolContextScope *symbol_scope) + { + m_symbol_scope = symbol_scope; + } + + void + Clear () + { + m_pc = LLDB_INVALID_ADDRESS; + m_cfa = LLDB_INVALID_ADDRESS; + m_symbol_scope = NULL; + } + + bool + IsValid () const + { + return m_pc != LLDB_INVALID_ADDRESS || m_cfa != LLDB_INVALID_ADDRESS; + } + + void + Dump (Stream *s); + + //------------------------------------------------------------------ + // Operators + //------------------------------------------------------------------ + const StackID& + operator=(const StackID& rhs) + { + if (this != &rhs) + { + m_pc = rhs.m_pc; + m_cfa = rhs.m_cfa; + m_symbol_scope = rhs.m_symbol_scope; + } + return *this; + } + +protected: + + friend class StackFrame; + + void + SetPC (lldb::addr_t pc) + { + m_pc = pc; + } + + + //------------------------------------------------------------------ + // Classes that inherit from StackID can see and modify these + //------------------------------------------------------------------ + lldb::addr_t m_pc; // The pc value for the function/symbol for this frame. This will + // only get used if the symbol scope is NULL (the code where we are + // stopped is not represented by any function or symbol in any + // shared library). + lldb::addr_t m_cfa; // The call frame address (stack pointer) value + // at the beginning of the function that uniquely + // identifies this frame (along with m_symbol_scope below) + SymbolContextScope *m_symbol_scope; // If NULL, there is no block or symbol for this frame. + // If not NULL, this will either be the scope for the + // lexical block for the frame, or the scope + // for the symbol. Symbol context scopes are + // always be unique pointers since the are part + // of the Block and Symbol objects and can easily + // be used to tell if a stack ID is the same as + // another. +}; + +bool operator== (const StackID& lhs, const StackID& rhs); +bool operator!= (const StackID& lhs, const StackID& rhs); + +// frame_id_1 < frame_id_2 means "frame_id_1 is YOUNGER than frame_id_2" +bool operator< (const StackID& lhs, const StackID& rhs); + +} // namespace lldb_private + +#endif // liblldb_StackID_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Target/StopInfo.h b/contrib/llvm/tools/lldb/include/lldb/Target/StopInfo.h new file mode 100644 index 00000000000..3435d392e2b --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Target/StopInfo.h @@ -0,0 +1,227 @@ +//===-- StopInfo.h ----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_StopInfo_h_ +#define liblldb_StopInfo_h_ + +// C Includes +// C++ Includes +#include + +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-public.h" +#include "lldb/Target/Process.h" + +namespace lldb_private { + +class StopInfo +{ + friend class Process::ProcessEventData; + friend class ThreadPlanBase; + +public: + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + StopInfo (Thread &thread, uint64_t value); + + virtual ~StopInfo() + { + } + + + bool + IsValid () const; + + void + SetThread (const lldb::ThreadSP &thread_sp) + { + m_thread_wp = thread_sp; + } + + lldb::ThreadSP + GetThread() const + { + return m_thread_wp.lock(); + } + + // The value of the StopInfo depends on the StopReason. + // StopReason Meaning + // ---------------------------------------------- + // eStopReasonBreakpoint BreakpointSiteID + // eStopReasonSignal Signal number + // eStopReasonWatchpoint WatchpointLocationID + // eStopReasonPlanComplete No significance + + uint64_t + GetValue() const + { + return m_value; + } + + virtual lldb::StopReason + GetStopReason () const = 0; + + // ShouldStopSynchronous will get called before any thread plans are consulted, and if it says we should + // resume the target, then we will just immediately resume. This should not run any code in or resume the + // target. + + virtual bool + ShouldStopSynchronous (Event *event_ptr) + { + return true; + } + + void + OverrideShouldNotify (bool override_value) + { + m_override_should_notify = override_value ? eLazyBoolYes : eLazyBoolNo; + } + + // If should stop returns false, check if we should notify of this event + virtual bool + ShouldNotify (Event *event_ptr) + { + if (m_override_should_notify == eLazyBoolCalculate) + return DoShouldNotify (event_ptr); + else + return m_override_should_notify == eLazyBoolYes; + } + + virtual void + WillResume (lldb::StateType resume_state) + { + // By default, don't do anything + } + + virtual const char * + GetDescription () + { + return m_description.c_str(); + } + + virtual void + SetDescription (const char *desc_cstr) + { + if (desc_cstr && desc_cstr[0]) + m_description.assign (desc_cstr); + else + m_description.clear(); + } + + // Sometimes the thread plan logic will know that it wants a given stop to stop or not, + // regardless of what the ordinary logic for that StopInfo would dictate. The main example + // of this is the ThreadPlanCallFunction, which for instance knows - based on how that particular + // expression was executed - whether it wants all breakpoints to auto-continue or not. + // Use OverrideShouldStop on the StopInfo to implement this. + + void + OverrideShouldStop (bool override_value) + { + m_override_should_stop = override_value ? eLazyBoolYes : eLazyBoolNo; + } + + bool + GetOverrideShouldStop() + { + return m_override_should_stop != eLazyBoolCalculate; + } + + bool + GetOverriddenShouldStopValue () + { + return m_override_should_stop == eLazyBoolYes; + } + + static lldb::StopInfoSP + CreateStopReasonWithBreakpointSiteID (Thread &thread, lldb::break_id_t break_id); + + // This creates a StopInfo for the thread where the should_stop is already set, and won't be recalculated. + static lldb::StopInfoSP + CreateStopReasonWithBreakpointSiteID (Thread &thread, lldb::break_id_t break_id, bool should_stop); + + static lldb::StopInfoSP + CreateStopReasonWithWatchpointID (Thread &thread, lldb::break_id_t watch_id); + + static lldb::StopInfoSP + CreateStopReasonWithSignal (Thread &thread, int signo); + + static lldb::StopInfoSP + CreateStopReasonToTrace (Thread &thread); + + static lldb::StopInfoSP + CreateStopReasonWithPlan (lldb::ThreadPlanSP &plan, lldb::ValueObjectSP return_valobj_sp); + + static lldb::StopInfoSP + CreateStopReasonWithException (Thread &thread, const char *description); + + static lldb::StopInfoSP + CreateStopReasonWithExec (Thread &thread); + + static lldb::ValueObjectSP + GetReturnValueObject (lldb::StopInfoSP &stop_info_sp); + +protected: + // Perform any action that is associated with this stop. This is done as the + // Event is removed from the event queue. ProcessEventData::DoOnRemoval does the job. + + virtual void + PerformAction (Event *event_ptr) + { + } + + virtual bool + DoShouldNotify (Event *event_ptr) + { + return false; + } + + // Stop the thread by default. Subclasses can override this to allow + // the thread to continue if desired. The ShouldStop method should not do anything + // that might run code. If you need to run code when deciding whether to stop + // at this StopInfo, that must be done in the PerformAction. + // The PerformAction will always get called before the ShouldStop. This is done by the + // ProcessEventData::DoOnRemoval, though the ThreadPlanBase needs to consult this later on. + virtual bool + ShouldStop (Event *event_ptr) + { + return true; + } + + //------------------------------------------------------------------ + // Classes that inherit from StackID can see and modify these + //------------------------------------------------------------------ + lldb::ThreadWP m_thread_wp; // The thread corresponding to the stop reason. + uint32_t m_stop_id; // The process stop ID for which this stop info is valid + uint32_t m_resume_id; // This is the resume ID when we made this stop ID. + uint64_t m_value; // A generic value that can be used for things pertaining to this stop info + std::string m_description; // A textual description describing this stop. + LazyBool m_override_should_notify; + LazyBool m_override_should_stop; + + // This determines whether the target has run since this stop info. + // N.B. running to evaluate a user expression does not count. + bool HasTargetRunSinceMe (); + + // MakeStopInfoValid is necessary to allow saved stop infos to resurrect themselves as valid. + // It should only be used by Thread::RestoreThreadStateFromCheckpoint and to make sure the one-step + // needed for before-the-fact watchpoints does not prevent us from stopping + void + MakeStopInfoValid (); + +private: + friend class Thread; + + DISALLOW_COPY_AND_ASSIGN (StopInfo); +}; + +} // namespace lldb_private + +#endif // liblldb_StopInfo_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Target/Target.h b/contrib/llvm/tools/lldb/include/lldb/Target/Target.h new file mode 100644 index 00000000000..87fa57b3a29 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Target/Target.h @@ -0,0 +1,1223 @@ +//===-- Target.h ------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_Target_h_ +#define liblldb_Target_h_ + +// C Includes +// C++ Includes +#include + +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-public.h" +#include "lldb/Breakpoint/BreakpointList.h" +#include "lldb/Breakpoint/BreakpointLocationCollection.h" +#include "lldb/Breakpoint/WatchpointList.h" +#include "lldb/Core/ArchSpec.h" +#include "lldb/Core/Broadcaster.h" +#include "lldb/Core/Disassembler.h" +#include "lldb/Core/Event.h" +#include "lldb/Core/ModuleList.h" +#include "lldb/Core/UserSettingsController.h" +#include "lldb/Expression/ClangPersistentVariables.h" +#include "lldb/Interpreter/Args.h" +#include "lldb/Interpreter/OptionValueBoolean.h" +#include "lldb/Interpreter/OptionValueEnumeration.h" +#include "lldb/Interpreter/OptionValueFileSpec.h" +#include "lldb/Symbol/SymbolContext.h" +#include "lldb/Target/ABI.h" +#include "lldb/Target/ExecutionContextScope.h" +#include "lldb/Target/PathMappingList.h" +#include "lldb/Target/SectionLoadList.h" + +namespace lldb_private { + +extern OptionEnumValueElement g_dynamic_value_types[]; + +typedef enum InlineStrategy +{ + eInlineBreakpointsNever = 0, + eInlineBreakpointsHeaders, + eInlineBreakpointsAlways +} InlineStrategy; + +typedef enum LoadScriptFromSymFile +{ + eLoadScriptFromSymFileTrue, + eLoadScriptFromSymFileFalse, + eLoadScriptFromSymFileWarn +} LoadScriptFromSymFile; + +//---------------------------------------------------------------------- +// TargetProperties +//---------------------------------------------------------------------- +class TargetProperties : public Properties +{ +public: + TargetProperties(Target *target); + + virtual + ~TargetProperties(); + + ArchSpec + GetDefaultArchitecture () const; + + void + SetDefaultArchitecture (const ArchSpec& arch); + + lldb::DynamicValueType + GetPreferDynamicValue() const; + + bool + GetDisableASLR () const; + + void + SetDisableASLR (bool b); + + bool + GetDisableSTDIO () const; + + void + SetDisableSTDIO (bool b); + + const char * + GetDisassemblyFlavor() const; + +// void +// SetDisassemblyFlavor(const char *flavor); + + InlineStrategy + GetInlineStrategy () const; + + const char * + GetArg0 () const; + + void + SetArg0 (const char *arg); + + bool + GetRunArguments (Args &args) const; + + void + SetRunArguments (const Args &args); + + size_t + GetEnvironmentAsArgs (Args &env) const; + + bool + GetSkipPrologue() const; + + PathMappingList & + GetSourcePathMap () const; + + FileSpecList & + GetExecutableSearchPaths (); + + FileSpecList & + GetDebugFileSearchPaths (); + + bool + GetEnableSyntheticValue () const; + + uint32_t + GetMaximumNumberOfChildrenToDisplay() const; + + uint32_t + GetMaximumSizeOfStringSummary() const; + + uint32_t + GetMaximumMemReadSize () const; + + FileSpec + GetStandardInputPath () const; + + void + SetStandardInputPath (const char *path); + + FileSpec + GetStandardOutputPath () const; + + void + SetStandardOutputPath (const char *path); + + FileSpec + GetStandardErrorPath () const; + + void + SetStandardErrorPath (const char *path); + + bool + GetBreakpointsConsultPlatformAvoidList (); + + const char * + GetExpressionPrefixContentsAsCString (); + + bool + GetUseHexImmediates() const; + + bool + GetUseFastStepping() const; + + LoadScriptFromSymFile + GetLoadScriptFromSymbolFile() const; + + Disassembler::HexImmediateStyle + GetHexImmediateStyle() const; + + MemoryModuleLoadLevel + GetMemoryModuleLoadLevel() const; + +}; + +typedef std::shared_ptr TargetPropertiesSP; + +class EvaluateExpressionOptions +{ +public: + static const uint32_t default_timeout = 500000; + EvaluateExpressionOptions() : + m_execution_policy(eExecutionPolicyOnlyWhenNeeded), + m_coerce_to_id(false), + m_unwind_on_error(true), + m_ignore_breakpoints (false), + m_keep_in_memory(false), + m_run_others(true), + m_use_dynamic(lldb::eNoDynamicValues), + m_timeout_usec(default_timeout) + {} + + ExecutionPolicy + GetExecutionPolicy () const + { + return m_execution_policy; + } + + EvaluateExpressionOptions& + SetExecutionPolicy (ExecutionPolicy policy = eExecutionPolicyAlways) + { + m_execution_policy = policy; + return *this; + } + + bool + DoesCoerceToId () const + { + return m_coerce_to_id; + } + + EvaluateExpressionOptions& + SetCoerceToId (bool coerce = true) + { + m_coerce_to_id = coerce; + return *this; + } + + bool + DoesUnwindOnError () const + { + return m_unwind_on_error; + } + + EvaluateExpressionOptions& + SetUnwindOnError (bool unwind = false) + { + m_unwind_on_error = unwind; + return *this; + } + + bool + DoesIgnoreBreakpoints () const + { + return m_ignore_breakpoints; + } + + EvaluateExpressionOptions& + SetIgnoreBreakpoints (bool ignore = false) + { + m_ignore_breakpoints = ignore; + return *this; + } + + bool + DoesKeepInMemory () const + { + return m_keep_in_memory; + } + + EvaluateExpressionOptions& + SetKeepInMemory (bool keep = true) + { + m_keep_in_memory = keep; + return *this; + } + + lldb::DynamicValueType + GetUseDynamic () const + { + return m_use_dynamic; + } + + EvaluateExpressionOptions& + SetUseDynamic (lldb::DynamicValueType dynamic = lldb::eDynamicCanRunTarget) + { + m_use_dynamic = dynamic; + return *this; + } + + uint32_t + GetTimeoutUsec () const + { + return m_timeout_usec; + } + + EvaluateExpressionOptions& + SetTimeoutUsec (uint32_t timeout = 0) + { + m_timeout_usec = timeout; + return *this; + } + + bool + GetRunOthers () const + { + return m_run_others; + } + + EvaluateExpressionOptions& + SetRunOthers (bool run_others = true) + { + m_run_others = run_others; + return *this; + } + +private: + ExecutionPolicy m_execution_policy; + bool m_coerce_to_id; + bool m_unwind_on_error; + bool m_ignore_breakpoints; + bool m_keep_in_memory; + bool m_run_others; + lldb::DynamicValueType m_use_dynamic; + uint32_t m_timeout_usec; +}; + +//---------------------------------------------------------------------- +// Target +//---------------------------------------------------------------------- +class Target : + public std::enable_shared_from_this, + public TargetProperties, + public Broadcaster, + public ExecutionContextScope, + public ModuleList::Notifier +{ +public: + friend class TargetList; + + //------------------------------------------------------------------ + /// Broadcaster event bits definitions. + //------------------------------------------------------------------ + enum + { + eBroadcastBitBreakpointChanged = (1 << 0), + eBroadcastBitModulesLoaded = (1 << 1), + eBroadcastBitModulesUnloaded = (1 << 2), + eBroadcastBitWatchpointChanged = (1 << 3), + eBroadcastBitSymbolsLoaded = (1 << 4) + }; + + // These two functions fill out the Broadcaster interface: + + static ConstString &GetStaticBroadcasterClass (); + + virtual ConstString &GetBroadcasterClass() const + { + return GetStaticBroadcasterClass(); + } + + // This event data class is for use by the TargetList to broadcast new target notifications. + class TargetEventData : public EventData + { + public: + + static const ConstString & + GetFlavorString (); + + virtual const ConstString & + GetFlavor () const; + + TargetEventData (const lldb::TargetSP &new_target_sp); + + lldb::TargetSP & + GetTarget() + { + return m_target_sp; + } + + virtual + ~TargetEventData(); + + virtual void + Dump (Stream *s) const; + + static const lldb::TargetSP + GetTargetFromEvent (const lldb::EventSP &event_sp); + + static const TargetEventData * + GetEventDataFromEvent (const Event *event_sp); + + private: + lldb::TargetSP m_target_sp; + + DISALLOW_COPY_AND_ASSIGN (TargetEventData); + }; + + static void + SettingsInitialize (); + + static void + SettingsTerminate (); + +// static lldb::UserSettingsControllerSP & +// GetSettingsController (); + + static FileSpecList + GetDefaultExecutableSearchPaths (); + + static FileSpecList + GetDefaultDebugFileSearchPaths (); + + static ArchSpec + GetDefaultArchitecture (); + + static void + SetDefaultArchitecture (const ArchSpec &arch); + +// void +// UpdateInstanceName (); + + lldb::ModuleSP + GetSharedModule (const ModuleSpec &module_spec, + Error *error_ptr = NULL); + + //---------------------------------------------------------------------- + // Settings accessors + //---------------------------------------------------------------------- + + static const TargetPropertiesSP & + GetGlobalProperties(); + + +private: + //------------------------------------------------------------------ + /// Construct with optional file and arch. + /// + /// This member is private. Clients must use + /// TargetList::CreateTarget(const FileSpec*, const ArchSpec*) + /// so all targets can be tracked from the central target list. + /// + /// @see TargetList::CreateTarget(const FileSpec*, const ArchSpec*) + //------------------------------------------------------------------ + Target (Debugger &debugger, + const ArchSpec &target_arch, + const lldb::PlatformSP &platform_sp); + + // Helper function. + bool + ProcessIsValid (); + +public: + ~Target(); + + Mutex & + GetAPIMutex () + { + return m_mutex; + } + + void + DeleteCurrentProcess (); + + void + CleanupProcess (); + //------------------------------------------------------------------ + /// Dump a description of this object to a Stream. + /// + /// Dump a description of the contents of this object to the + /// supplied stream \a s. The dumped content will be only what has + /// been loaded or parsed up to this point at which this function + /// is called, so this is a good way to see what has been parsed + /// in a target. + /// + /// @param[in] s + /// The stream to which to dump the object descripton. + //------------------------------------------------------------------ + void + Dump (Stream *s, lldb::DescriptionLevel description_level); + + const lldb::ProcessSP & + CreateProcess (Listener &listener, + const char *plugin_name, + const FileSpec *crash_file); + + const lldb::ProcessSP & + GetProcessSP () const; + + bool + IsValid() + { + return m_valid; + } + + void + Destroy(); + + //------------------------------------------------------------------ + // This part handles the breakpoints. + //------------------------------------------------------------------ + + BreakpointList & + GetBreakpointList(bool internal = false); + + const BreakpointList & + GetBreakpointList(bool internal = false) const; + + lldb::BreakpointSP + GetLastCreatedBreakpoint () + { + return m_last_created_breakpoint; + } + + lldb::BreakpointSP + GetBreakpointByID (lldb::break_id_t break_id); + + // Use this to create a file and line breakpoint to a given module or all module it is NULL + lldb::BreakpointSP + CreateBreakpoint (const FileSpecList *containingModules, + const FileSpec &file, + uint32_t line_no, + LazyBool check_inlines = eLazyBoolCalculate, + LazyBool skip_prologue = eLazyBoolCalculate, + bool internal = false); + + // Use this to create breakpoint that matches regex against the source lines in files given in source_file_list: + lldb::BreakpointSP + CreateSourceRegexBreakpoint (const FileSpecList *containingModules, + const FileSpecList *source_file_list, + RegularExpression &source_regex, + bool internal = false); + + // Use this to create a breakpoint from a load address + lldb::BreakpointSP + CreateBreakpoint (lldb::addr_t load_addr, + bool internal = false); + + // Use this to create Address breakpoints: + lldb::BreakpointSP + CreateBreakpoint (Address &addr, + bool internal = false); + + // Use this to create a function breakpoint by regexp in containingModule/containingSourceFiles, or all modules if it is NULL + // When "skip_prologue is set to eLazyBoolCalculate, we use the current target + // setting, else we use the values passed in + lldb::BreakpointSP + CreateFuncRegexBreakpoint (const FileSpecList *containingModules, + const FileSpecList *containingSourceFiles, + RegularExpression &func_regexp, + LazyBool skip_prologue = eLazyBoolCalculate, + bool internal = false); + + // Use this to create a function breakpoint by name in containingModule, or all modules if it is NULL + // When "skip_prologue is set to eLazyBoolCalculate, we use the current target + // setting, else we use the values passed in + lldb::BreakpointSP + CreateBreakpoint (const FileSpecList *containingModules, + const FileSpecList *containingSourceFiles, + const char *func_name, + uint32_t func_name_type_mask, + LazyBool skip_prologue = eLazyBoolCalculate, + bool internal = false); + + lldb::BreakpointSP + CreateExceptionBreakpoint (enum lldb::LanguageType language, bool catch_bp, bool throw_bp, bool internal = false); + + // This is the same as the func_name breakpoint except that you can specify a vector of names. This is cheaper + // than a regular expression breakpoint in the case where you just want to set a breakpoint on a set of names + // you already know. + lldb::BreakpointSP + CreateBreakpoint (const FileSpecList *containingModules, + const FileSpecList *containingSourceFiles, + const char *func_names[], + size_t num_names, + uint32_t func_name_type_mask, + LazyBool skip_prologue = eLazyBoolCalculate, + bool internal = false); + + lldb::BreakpointSP + CreateBreakpoint (const FileSpecList *containingModules, + const FileSpecList *containingSourceFiles, + const std::vector &func_names, + uint32_t func_name_type_mask, + LazyBool skip_prologue = eLazyBoolCalculate, + bool internal = false); + + + // Use this to create a general breakpoint: + lldb::BreakpointSP + CreateBreakpoint (lldb::SearchFilterSP &filter_sp, + lldb::BreakpointResolverSP &resolver_sp, + bool internal = false); + + // Use this to create a watchpoint: + lldb::WatchpointSP + CreateWatchpoint (lldb::addr_t addr, + size_t size, + const ClangASTType *type, + uint32_t kind, + Error &error); + + lldb::WatchpointSP + GetLastCreatedWatchpoint () + { + return m_last_created_watchpoint; + } + + WatchpointList & + GetWatchpointList() + { + return m_watchpoint_list; + } + + void + RemoveAllBreakpoints (bool internal_also = false); + + void + DisableAllBreakpoints (bool internal_also = false); + + void + EnableAllBreakpoints (bool internal_also = false); + + bool + DisableBreakpointByID (lldb::break_id_t break_id); + + bool + EnableBreakpointByID (lldb::break_id_t break_id); + + bool + RemoveBreakpointByID (lldb::break_id_t break_id); + + // The flag 'end_to_end', default to true, signifies that the operation is + // performed end to end, for both the debugger and the debuggee. + + bool + RemoveAllWatchpoints (bool end_to_end = true); + + bool + DisableAllWatchpoints (bool end_to_end = true); + + bool + EnableAllWatchpoints (bool end_to_end = true); + + bool + ClearAllWatchpointHitCounts (); + + bool + IgnoreAllWatchpoints (uint32_t ignore_count); + + bool + DisableWatchpointByID (lldb::watch_id_t watch_id); + + bool + EnableWatchpointByID (lldb::watch_id_t watch_id); + + bool + RemoveWatchpointByID (lldb::watch_id_t watch_id); + + bool + IgnoreWatchpointByID (lldb::watch_id_t watch_id, uint32_t ignore_count); + + //------------------------------------------------------------------ + /// Get \a load_addr as a callable code load address for this target + /// + /// Take \a load_addr and potentially add any address bits that are + /// needed to make the address callable. For ARM this can set bit + /// zero (if it already isn't) if \a load_addr is a thumb function. + /// If \a addr_class is set to eAddressClassInvalid, then the address + /// adjustment will always happen. If it is set to an address class + /// that doesn't have code in it, LLDB_INVALID_ADDRESS will be + /// returned. + //------------------------------------------------------------------ + lldb::addr_t + GetCallableLoadAddress (lldb::addr_t load_addr, lldb::AddressClass addr_class = lldb::eAddressClassInvalid) const; + + //------------------------------------------------------------------ + /// Get \a load_addr as an opcode for this target. + /// + /// Take \a load_addr and potentially strip any address bits that are + /// needed to make the address point to an opcode. For ARM this can + /// clear bit zero (if it already isn't) if \a load_addr is a + /// thumb function and load_addr is in code. + /// If \a addr_class is set to eAddressClassInvalid, then the address + /// adjustment will always happen. If it is set to an address class + /// that doesn't have code in it, LLDB_INVALID_ADDRESS will be + /// returned. + //------------------------------------------------------------------ + lldb::addr_t + GetOpcodeLoadAddress (lldb::addr_t load_addr, lldb::AddressClass addr_class = lldb::eAddressClassInvalid) const; + +protected: + //------------------------------------------------------------------ + /// Implementing of ModuleList::Notifier. + //------------------------------------------------------------------ + + virtual void + ModuleAdded (const ModuleList& module_list, const lldb::ModuleSP& module_sp); + + virtual void + ModuleRemoved (const ModuleList& module_list, const lldb::ModuleSP& module_sp); + + virtual void + ModuleUpdated (const ModuleList& module_list, + const lldb::ModuleSP& old_module_sp, + const lldb::ModuleSP& new_module_sp); + virtual void + WillClearList (const ModuleList& module_list); + +public: + + void + ModulesDidLoad (ModuleList &module_list); + + void + ModulesDidUnload (ModuleList &module_list); + + void + SymbolsDidLoad (ModuleList &module_list); + + //------------------------------------------------------------------ + /// Gets the module for the main executable. + /// + /// Each process has a notion of a main executable that is the file + /// that will be executed or attached to. Executable files can have + /// dependent modules that are discovered from the object files, or + /// discovered at runtime as things are dynamically loaded. + /// + /// @return + /// The shared pointer to the executable module which can + /// contains a NULL Module object if no executable has been + /// set. + /// + /// @see DynamicLoader + /// @see ObjectFile::GetDependentModules (FileSpecList&) + /// @see Process::SetExecutableModule(lldb::ModuleSP&) + //------------------------------------------------------------------ + lldb::ModuleSP + GetExecutableModule (); + + Module* + GetExecutableModulePointer (); + + //------------------------------------------------------------------ + /// Set the main executable module. + /// + /// Each process has a notion of a main executable that is the file + /// that will be executed or attached to. Executable files can have + /// dependent modules that are discovered from the object files, or + /// discovered at runtime as things are dynamically loaded. + /// + /// Setting the executable causes any of the current dependant + /// image information to be cleared and replaced with the static + /// dependent image information found by calling + /// ObjectFile::GetDependentModules (FileSpecList&) on the main + /// executable and any modules on which it depends. Calling + /// Process::GetImages() will return the newly found images that + /// were obtained from all of the object files. + /// + /// @param[in] module_sp + /// A shared pointer reference to the module that will become + /// the main executable for this process. + /// + /// @param[in] get_dependent_files + /// If \b true then ask the object files to track down any + /// known dependent files. + /// + /// @see ObjectFile::GetDependentModules (FileSpecList&) + /// @see Process::GetImages() + //------------------------------------------------------------------ + void + SetExecutableModule (lldb::ModuleSP& module_sp, bool get_dependent_files); + + bool + LoadScriptingResources (std::list& errors, + Stream* feedback_stream = NULL, + bool continue_on_error = true) + { + return m_images.LoadScriptingResourcesInTarget(this,errors,feedback_stream,continue_on_error); + } + + //------------------------------------------------------------------ + /// Get accessor for the images for this process. + /// + /// Each process has a notion of a main executable that is the file + /// that will be executed or attached to. Executable files can have + /// dependent modules that are discovered from the object files, or + /// discovered at runtime as things are dynamically loaded. After + /// a main executable has been set, the images will contain a list + /// of all the files that the executable depends upon as far as the + /// object files know. These images will usually contain valid file + /// virtual addresses only. When the process is launched or attached + /// to, the DynamicLoader plug-in will discover where these images + /// were loaded in memory and will resolve the load virtual + /// addresses is each image, and also in images that are loaded by + /// code. + /// + /// @return + /// A list of Module objects in a module list. + //------------------------------------------------------------------ + const ModuleList& + GetImages () const + { + return m_images; + } + + ModuleList& + GetImages () + { + return m_images; + } + + //------------------------------------------------------------------ + /// Return whether this FileSpec corresponds to a module that should be considered for general searches. + /// + /// This API will be consulted by the SearchFilterForNonModuleSpecificSearches + /// and any module that returns \b true will not be searched. Note the + /// SearchFilterForNonModuleSpecificSearches is the search filter that + /// gets used in the CreateBreakpoint calls when no modules is provided. + /// + /// The target call at present just consults the Platform's call of the + /// same name. + /// + /// @param[in] module_sp + /// A shared pointer reference to the module that checked. + /// + /// @return \b true if the module should be excluded, \b false otherwise. + //------------------------------------------------------------------ + bool + ModuleIsExcludedForNonModuleSpecificSearches (const FileSpec &module_spec); + + //------------------------------------------------------------------ + /// Return whether this module should be considered for general searches. + /// + /// This API will be consulted by the SearchFilterForNonModuleSpecificSearches + /// and any module that returns \b true will not be searched. Note the + /// SearchFilterForNonModuleSpecificSearches is the search filter that + /// gets used in the CreateBreakpoint calls when no modules is provided. + /// + /// The target call at present just consults the Platform's call of the + /// same name. + /// + /// FIXME: When we get time we should add a way for the user to set modules that they + /// don't want searched, in addition to or instead of the platform ones. + /// + /// @param[in] module_sp + /// A shared pointer reference to the module that checked. + /// + /// @return \b true if the module should be excluded, \b false otherwise. + //------------------------------------------------------------------ + bool + ModuleIsExcludedForNonModuleSpecificSearches (const lldb::ModuleSP &module_sp); + + ArchSpec & + GetArchitecture () + { + return m_arch; + } + + const ArchSpec & + GetArchitecture () const + { + return m_arch; + } + + //------------------------------------------------------------------ + /// Set the architecture for this target. + /// + /// If the current target has no Images read in, then this just sets the architecture, which will + /// be used to select the architecture of the ExecutableModule when that is set. + /// If the current target has an ExecutableModule, then calling SetArchitecture with a different + /// architecture from the currently selected one will reset the ExecutableModule to that slice + /// of the file backing the ExecutableModule. If the file backing the ExecutableModule does not + /// contain a fork of this architecture, then this code will return false, and the architecture + /// won't be changed. + /// If the input arch_spec is the same as the already set architecture, this is a no-op. + /// + /// @param[in] arch_spec + /// The new architecture. + /// + /// @return + /// \b true if the architecture was successfully set, \bfalse otherwise. + //------------------------------------------------------------------ + bool + SetArchitecture (const ArchSpec &arch_spec); + + Debugger & + GetDebugger () + { + return m_debugger; + } + + size_t + ReadMemoryFromFileCache (const Address& addr, + void *dst, + size_t dst_len, + Error &error); + + // Reading memory through the target allows us to skip going to the process + // for reading memory if possible and it allows us to try and read from + // any constant sections in our object files on disk. If you always want + // live program memory, read straight from the process. If you possibly + // want to read from const sections in object files, read from the target. + // This version of ReadMemory will try and read memory from the process + // if the process is alive. The order is: + // 1 - if (prefer_file_cache == true) then read from object file cache + // 2 - if there is a valid process, try and read from its memory + // 3 - if (prefer_file_cache == false) then read from object file cache + size_t + ReadMemory (const Address& addr, + bool prefer_file_cache, + void *dst, + size_t dst_len, + Error &error, + lldb::addr_t *load_addr_ptr = NULL); + + size_t + ReadCStringFromMemory (const Address& addr, std::string &out_str, Error &error); + + size_t + ReadCStringFromMemory (const Address& addr, char *dst, size_t dst_max_len, Error &result_error); + + size_t + ReadScalarIntegerFromMemory (const Address& addr, + bool prefer_file_cache, + uint32_t byte_size, + bool is_signed, + Scalar &scalar, + Error &error); + + uint64_t + ReadUnsignedIntegerFromMemory (const Address& addr, + bool prefer_file_cache, + size_t integer_byte_size, + uint64_t fail_value, + Error &error); + + bool + ReadPointerFromMemory (const Address& addr, + bool prefer_file_cache, + Error &error, + Address &pointer_addr); + + SectionLoadList& + GetSectionLoadList() + { + return m_section_load_list; + } + + const SectionLoadList& + GetSectionLoadList() const + { + return m_section_load_list; + } + + static Target * + GetTargetFromContexts (const ExecutionContext *exe_ctx_ptr, + const SymbolContext *sc_ptr); + + //------------------------------------------------------------------ + // lldb::ExecutionContextScope pure virtual functions + //------------------------------------------------------------------ + virtual lldb::TargetSP + CalculateTarget (); + + virtual lldb::ProcessSP + CalculateProcess (); + + virtual lldb::ThreadSP + CalculateThread (); + + virtual lldb::StackFrameSP + CalculateStackFrame (); + + virtual void + CalculateExecutionContext (ExecutionContext &exe_ctx); + + PathMappingList & + GetImageSearchPathList (); + + ClangASTContext * + GetScratchClangASTContext(bool create_on_demand=true); + + ClangASTImporter * + GetClangASTImporter(); + + + // Since expressions results can persist beyond the lifetime of a process, + // and the const expression results are available after a process is gone, + // we provide a way for expressions to be evaluated from the Target itself. + // If an expression is going to be run, then it should have a frame filled + // in in th execution context. + ExecutionResults + EvaluateExpression (const char *expression, + StackFrame *frame, + lldb::ValueObjectSP &result_valobj_sp, + const EvaluateExpressionOptions& options = EvaluateExpressionOptions()); + + ClangPersistentVariables & + GetPersistentVariables() + { + return m_persistent_variables; + } + + //------------------------------------------------------------------ + // Target Stop Hooks + //------------------------------------------------------------------ + class StopHook : public UserID + { + public: + ~StopHook (); + + StopHook (const StopHook &rhs); + + StringList * + GetCommandPointer () + { + return &m_commands; + } + + const StringList & + GetCommands() + { + return m_commands; + } + + lldb::TargetSP & + GetTarget() + { + return m_target_sp; + } + + void + SetCommands (StringList &in_commands) + { + m_commands = in_commands; + } + + // Set the specifier. The stop hook will own the specifier, and is responsible for deleting it when we're done. + void + SetSpecifier (SymbolContextSpecifier *specifier) + { + m_specifier_sp.reset (specifier); + } + + SymbolContextSpecifier * + GetSpecifier () + { + return m_specifier_sp.get(); + } + + // Set the Thread Specifier. The stop hook will own the thread specifier, and is responsible for deleting it when we're done. + void + SetThreadSpecifier (ThreadSpec *specifier); + + ThreadSpec * + GetThreadSpecifier() + { + return m_thread_spec_ap.get(); + } + + bool + IsActive() + { + return m_active; + } + + void + SetIsActive (bool is_active) + { + m_active = is_active; + } + + void + GetDescription (Stream *s, lldb::DescriptionLevel level) const; + + private: + lldb::TargetSP m_target_sp; + StringList m_commands; + lldb::SymbolContextSpecifierSP m_specifier_sp; + std::unique_ptr m_thread_spec_ap; + bool m_active; + + // Use AddStopHook to make a new empty stop hook. The GetCommandPointer and fill it with commands, + // and SetSpecifier to set the specifier shared pointer (can be null, that will match anything.) + StopHook (lldb::TargetSP target_sp, lldb::user_id_t uid); + friend class Target; + }; + typedef std::shared_ptr StopHookSP; + + // Add an empty stop hook to the Target's stop hook list, and returns a shared pointer to it in new_hook. + // Returns the id of the new hook. + lldb::user_id_t + AddStopHook (StopHookSP &new_hook); + + void + RunStopHooks (); + + size_t + GetStopHookSize(); + + bool + SetSuppresStopHooks (bool suppress) + { + bool old_value = m_suppress_stop_hooks; + m_suppress_stop_hooks = suppress; + return old_value; + } + + bool + GetSuppressStopHooks () + { + return m_suppress_stop_hooks; + } + + bool + SetSuppressSyntheticValue (bool suppress) + { + bool old_value = m_suppress_synthetic_value; + m_suppress_synthetic_value = suppress; + return old_value; + } + + bool + GetSuppressSyntheticValue () + { + return m_suppress_synthetic_value; + } + +// StopHookSP & +// GetStopHookByIndex (size_t index); +// + bool + RemoveStopHookByID (lldb::user_id_t uid); + + void + RemoveAllStopHooks (); + + StopHookSP + GetStopHookByID (lldb::user_id_t uid); + + bool + SetStopHookActiveStateByID (lldb::user_id_t uid, bool active_state); + + void + SetAllStopHooksActiveState (bool active_state); + + size_t GetNumStopHooks () const + { + return m_stop_hooks.size(); + } + + StopHookSP + GetStopHookAtIndex (size_t index) + { + if (index >= GetNumStopHooks()) + return StopHookSP(); + StopHookCollection::iterator pos = m_stop_hooks.begin(); + + while (index > 0) + { + pos++; + index--; + } + return (*pos).second; + } + + lldb::PlatformSP + GetPlatform () + { + return m_platform_sp; + } + + void + SetPlatform (const lldb::PlatformSP &platform_sp) + { + m_platform_sp = platform_sp; + } + + SourceManager & + GetSourceManager (); + + //------------------------------------------------------------------ + // Methods. + //------------------------------------------------------------------ + lldb::SearchFilterSP + GetSearchFilterForModule (const FileSpec *containingModule); + + lldb::SearchFilterSP + GetSearchFilterForModuleList (const FileSpecList *containingModuleList); + + lldb::SearchFilterSP + GetSearchFilterForModuleAndCUList (const FileSpecList *containingModules, const FileSpecList *containingSourceFiles); + +protected: + //------------------------------------------------------------------ + // Member variables. + //------------------------------------------------------------------ + Debugger & m_debugger; + lldb::PlatformSP m_platform_sp; ///< The platform for this target. + Mutex m_mutex; ///< An API mutex that is used by the lldb::SB* classes make the SB interface thread safe + ArchSpec m_arch; + ModuleList m_images; ///< The list of images for this process (shared libraries and anything dynamically loaded). + SectionLoadList m_section_load_list; + BreakpointList m_breakpoint_list; + BreakpointList m_internal_breakpoint_list; + lldb::BreakpointSP m_last_created_breakpoint; + WatchpointList m_watchpoint_list; + lldb::WatchpointSP m_last_created_watchpoint; + // We want to tightly control the process destruction process so + // we can correctly tear down everything that we need to, so the only + // class that knows about the process lifespan is this target class. + lldb::ProcessSP m_process_sp; + bool m_valid; + lldb::SearchFilterSP m_search_filter_sp; + PathMappingList m_image_search_paths; + std::unique_ptr m_scratch_ast_context_ap; + std::unique_ptr m_scratch_ast_source_ap; + std::unique_ptr m_ast_importer_ap; + ClangPersistentVariables m_persistent_variables; ///< These are the persistent variables associated with this process for the expression parser. + + std::unique_ptr m_source_manager_ap; + + typedef std::map StopHookCollection; + StopHookCollection m_stop_hooks; + lldb::user_id_t m_stop_hook_next_id; + bool m_suppress_stop_hooks; + bool m_suppress_synthetic_value; + + static void + ImageSearchPathsChanged (const PathMappingList &path_list, + void *baton); + +private: + DISALLOW_COPY_AND_ASSIGN (Target); +}; + +} // namespace lldb_private + +#endif // liblldb_Target_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Target/TargetList.h b/contrib/llvm/tools/lldb/include/lldb/Target/TargetList.h new file mode 100644 index 00000000000..41404e11c7f --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Target/TargetList.h @@ -0,0 +1,239 @@ +//===-- TargetList.h --------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_TargetList_h_ +#define liblldb_TargetList_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/Broadcaster.h" +#include "lldb/Host/Mutex.h" +#include "lldb/Target/Target.h" + +namespace lldb_private { + +class TargetList : public Broadcaster +{ +private: + friend class Debugger; + + //------------------------------------------------------------------ + /// Constructor + /// + /// The constructor for the target list is private. Clients can + /// get ahold of of the one and only target list through the + /// lldb_private::Debugger::GetSharedInstance().GetTargetList(). + /// + /// @see static TargetList& lldb_private::Debugger::GetTargetList(). + //------------------------------------------------------------------ + TargetList(Debugger &debugger); + +public: + + //------------------------------------------------------------------ + /// Broadcaster event bits definitions. + //------------------------------------------------------------------ + enum + { + eBroadcastBitInterrupt = (1 << 0) + }; + + + // These two functions fill out the Broadcaster interface: + + static ConstString &GetStaticBroadcasterClass (); + + virtual ConstString &GetBroadcasterClass() const + { + return GetStaticBroadcasterClass(); + } + + virtual ~TargetList(); + + //------------------------------------------------------------------ + /// Create a new Target. + /// + /// Clients must use this function to create a Target. This allows + /// a global list of targets to be maintained in a central location + /// so signal handlers and other global functions can use it to + /// locate an appropriate target to deliver asynchronous information + /// to. + /// + /// @param[in] debugger + /// The debugger to associate this target with + /// + /// @param[in] file_spec + /// The main executable file for a debug target. This value + /// can be NULL and the file can be set later using: + /// Target::SetExecutableModule (ModuleSP&) + /// + /// @param[in] triple_cstr + /// A target triple string to be used for the target. This can + /// be NULL if the triple is not known or when attaching to a + /// process. + /// + /// @param[in] get_dependent_modules + /// Track down the dependent modules for an executable and + /// load those into the module list. + /// + /// @param[in] platform_options + /// A pointer to the platform options to use when creating this + /// target. If this value is NULL, then the currently selected + /// platform will be used. + /// + /// @param[out] target_sp + /// A shared pointer to a target that will be filled in if + /// this call is successful. + /// + /// @return + /// An error object that indicates success or failure + //------------------------------------------------------------------ + Error + CreateTarget (Debugger &debugger, + const char *user_exe_path, + const char *triple_cstr, + bool get_dependent_modules, + const OptionGroupPlatform *platform_options, + lldb::TargetSP &target_sp); + + //------------------------------------------------------------------ + /// Create a new Target. + /// + /// Same as the function above, but used when you already know the + /// platform you will be using + //------------------------------------------------------------------ + Error + CreateTarget (Debugger &debugger, + const char *user_exe_path, + const ArchSpec& arch, + bool get_dependent_modules, + lldb::PlatformSP &platform_sp, + lldb::TargetSP &target_sp); + + //------------------------------------------------------------------ + /// Delete a Target object from the list. + /// + /// When clients are done with the Target objets, this function + /// should be called to release the memory associated with a target + /// object. + /// + /// @param[in] target_sp + /// The shared pointer to a target. + /// + /// @return + /// Returns \b true if the target was successfully removed from + /// from this target list, \b false otherwise. The client will + /// be left with the last remaining shared pointer to the target + /// in \a target_sp which can then be properly released. + //------------------------------------------------------------------ + bool + DeleteTarget (lldb::TargetSP &target_sp); + + int + GetNumTargets () const; + + lldb::TargetSP + GetTargetAtIndex (uint32_t index) const; + + uint32_t + GetIndexOfTarget (lldb::TargetSP target_sp) const; + + //------------------------------------------------------------------ + /// Find the target that contains has an executable whose path + /// matches \a exe_file_spec, and whose architecture matches + /// \a arch_ptr if arch_ptr is not NULL. + /// + /// @param[in] exe_file_spec + /// A file spec containing a basename, or a full path (directory + /// and basename). If \a exe_file_spec contains only a filename + /// (empty GetDirectory() value) then matching will be done + /// solely based on the filenames and directories won't be + /// compared. If \a exe_file_spec contains a filename and a + /// directory, then both must match. + /// + /// @param[in] exe_arch_ptr + /// If not NULL then the architecture also needs to match, else + /// the architectures will be compared. + /// + /// @return + /// A shared pointer to a target object. The returned shared + /// pointer will contain NULL if no target objects have a + /// executable whose full or partial path matches + /// with a matching process ID. + //------------------------------------------------------------------ + lldb::TargetSP + FindTargetWithExecutableAndArchitecture (const FileSpec &exe_file_spec, + const ArchSpec *exe_arch_ptr = NULL) const; + + //------------------------------------------------------------------ + /// Find the target that contains a process with process ID \a + /// pid. + /// + /// @param[in] pid + /// The process ID to search our target list for. + /// + /// @return + /// A shared pointer to a target object. The returned shared + /// pointer will contain NULL if no target objects own a process + /// with a matching process ID. + //------------------------------------------------------------------ + lldb::TargetSP + FindTargetWithProcessID (lldb::pid_t pid) const; + + lldb::TargetSP + FindTargetWithProcess (lldb_private::Process *process) const; + + lldb::TargetSP + GetTargetSP (Target *target) const; + + //------------------------------------------------------------------ + /// Send an async interrupt to one or all processes. + /// + /// Find the target that contains the process with process ID \a + /// pid and send a LLDB_EVENT_ASYNC_INTERRUPT event to the process's + /// event queue. + /// + /// @param[in] pid + /// The process ID to search our target list for, if \a pid is + /// LLDB_INVALID_PROCESS_ID, then the interrupt will be sent to + /// all processes. + /// + /// @return + /// The number of async interrupts sent. + //------------------------------------------------------------------ + uint32_t + SendAsyncInterrupt (lldb::pid_t pid = LLDB_INVALID_PROCESS_ID); + + uint32_t + SignalIfRunning (lldb::pid_t pid, int signo); + + uint32_t + SetSelectedTarget (Target *target); + + lldb::TargetSP + GetSelectedTarget (); + + +protected: + typedef std::vector collection; + //------------------------------------------------------------------ + // Member variables. + //------------------------------------------------------------------ + collection m_target_list; + mutable Mutex m_target_list_mutex; + uint32_t m_selected_target_idx; +private: + DISALLOW_COPY_AND_ASSIGN (TargetList); +}; + +} // namespace lldb_private + +#endif // liblldb_TargetList_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Target/Thread.h b/contrib/llvm/tools/lldb/include/lldb/Target/Thread.h new file mode 100644 index 00000000000..e4e532e4b33 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Target/Thread.h @@ -0,0 +1,1067 @@ +//===-- Thread.h ------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_Thread_h_ +#define liblldb_Thread_h_ + +#include "lldb/lldb-private.h" +#include "lldb/Host/Mutex.h" +#include "lldb/Core/Broadcaster.h" +#include "lldb/Core/Event.h" +#include "lldb/Core/UserID.h" +#include "lldb/Core/UserSettingsController.h" +#include "lldb/Target/ExecutionContextScope.h" +#include "lldb/Target/StackFrameList.h" + +#define LLDB_THREAD_MAX_STOP_EXC_DATA 8 + +namespace lldb_private { + +class ThreadProperties : public Properties +{ +public: + ThreadProperties(bool is_global); + + virtual + ~ThreadProperties(); + + //------------------------------------------------------------------ + /// The regular expression returned determines symbols that this + /// thread won't stop in during "step-in" operations. + /// + /// @return + /// A pointer to a regular expression to compare against symbols, + /// or NULL if all symbols are allowed. + /// + //------------------------------------------------------------------ + const RegularExpression * + GetSymbolsToAvoidRegexp(); + + bool + GetTraceEnabledState() const; +}; + +typedef std::shared_ptr ThreadPropertiesSP; + +class Thread : + public std::enable_shared_from_this, + public ThreadProperties, + public UserID, + public ExecutionContextScope, + public Broadcaster +{ +public: + //------------------------------------------------------------------ + /// Broadcaster event bits definitions. + //------------------------------------------------------------------ + enum + { + eBroadcastBitStackChanged = (1 << 0), + eBroadcastBitThreadSuspended = (1 << 1), + eBroadcastBitThreadResumed = (1 << 2), + eBroadcastBitSelectedFrameChanged = (1 << 3), + eBroadcastBitThreadSelected = (1 << 4) + }; + + static ConstString &GetStaticBroadcasterClass (); + + virtual ConstString &GetBroadcasterClass() const + { + return GetStaticBroadcasterClass(); + } + + class ThreadEventData : + public EventData + { + public: + ThreadEventData (const lldb::ThreadSP thread_sp); + + ThreadEventData (const lldb::ThreadSP thread_sp, const StackID &stack_id); + + ThreadEventData(); + + virtual ~ThreadEventData(); + + static const ConstString & + GetFlavorString (); + + virtual const ConstString & + GetFlavor () const + { + return ThreadEventData::GetFlavorString (); + } + + virtual void + Dump (Stream *s) const; + + static const ThreadEventData * + GetEventDataFromEvent (const Event *event_ptr); + + static lldb::ThreadSP + GetThreadFromEvent (const Event *event_ptr); + + static StackID + GetStackIDFromEvent (const Event *event_ptr); + + static lldb::StackFrameSP + GetStackFrameFromEvent (const Event *event_ptr); + + lldb::ThreadSP + GetThread () const + { + return m_thread_sp; + } + + StackID + GetStackID () const + { + return m_stack_id; + } + + private: + lldb::ThreadSP m_thread_sp; + StackID m_stack_id; + DISALLOW_COPY_AND_ASSIGN (ThreadEventData); + }; + + // TODO: You shouldn't just checkpoint the register state alone, so this should get + // moved to protected. To do that ThreadStateCheckpoint needs to be returned as a token... + class RegisterCheckpoint + { + public: + + RegisterCheckpoint() : + m_stack_id (), + m_data_sp () + { + } + + RegisterCheckpoint (const StackID &stack_id) : + m_stack_id (stack_id), + m_data_sp () + { + } + + ~RegisterCheckpoint() + { + } + + const RegisterCheckpoint& + operator= (const RegisterCheckpoint &rhs) + { + if (this != &rhs) + { + this->m_stack_id = rhs.m_stack_id; + this->m_data_sp = rhs.m_data_sp; + } + return *this; + } + + RegisterCheckpoint (const RegisterCheckpoint &rhs) : + m_stack_id (rhs.m_stack_id), + m_data_sp (rhs.m_data_sp) + { + } + + const StackID & + GetStackID() + { + return m_stack_id; + } + + void + SetStackID (const StackID &stack_id) + { + m_stack_id = stack_id; + } + + lldb::DataBufferSP & + GetData() + { + return m_data_sp; + } + + const lldb::DataBufferSP & + GetData() const + { + return m_data_sp; + } + + protected: + StackID m_stack_id; + lldb::DataBufferSP m_data_sp; + }; + + struct ThreadStateCheckpoint + { + uint32_t orig_stop_id; // Dunno if I need this yet but it is an interesting bit of data. + lldb::StopInfoSP stop_info_sp; // You have to restore the stop info or you might continue with the wrong signals. + RegisterCheckpoint register_backup; // You need to restore the registers, of course... + uint32_t current_inlined_depth; + lldb::addr_t current_inlined_pc; + }; + + static void + SettingsInitialize (); + + static void + SettingsTerminate (); + + static const ThreadPropertiesSP & + GetGlobalProperties(); + + Thread (Process &process, lldb::tid_t tid); + virtual ~Thread(); + + lldb::ProcessSP + GetProcess() const + { + return m_process_wp.lock(); + } + + int + GetResumeSignal () const + { + return m_resume_signal; + } + + void + SetResumeSignal (int signal) + { + m_resume_signal = signal; + } + + lldb::StateType + GetState() const; + + void + SetState (lldb::StateType state); + + lldb::StateType + GetResumeState () const + { + return m_resume_state; + } + + void + SetResumeState (lldb::StateType state) + { + m_resume_state = state; + } + + // This function is called on all the threads before "ShouldResume" and + // "WillResume" in case a thread needs to change its state before the + // ThreadList polls all the threads to figure out which ones actually + // will get to run and how. + void + SetupForResume (); + + // Do not override this function, it is for thread plan logic only + bool + ShouldResume (lldb::StateType resume_state); + + // Override this to do platform specific tasks before resume. + virtual void + WillResume (lldb::StateType resume_state) + { + } + + // This clears generic thread state after a resume. If you subclass this, + // be sure to call it. + virtual void + DidResume (); + + // This notifies the thread when a private stop occurs. + virtual void + DidStop (); + + virtual void + RefreshStateAfterStop() = 0; + + void + WillStop (); + + bool + ShouldStop (Event *event_ptr); + + Vote + ShouldReportStop (Event *event_ptr); + + Vote + ShouldReportRun (Event *event_ptr); + + void + Flush (); + + // Return whether this thread matches the specification in ThreadSpec. This is a virtual + // method because at some point we may extend the thread spec with a platform specific + // dictionary of attributes, which then only the platform specific Thread implementation + // would know how to match. For now, this just calls through to the ThreadSpec's + // ThreadPassesBasicTests method. + virtual bool + MatchesSpec (const ThreadSpec *spec); + + lldb::StopInfoSP + GetStopInfo (); + + lldb::StopReason + GetStopReason(); + + // This sets the stop reason to a "blank" stop reason, so you can call functions on the thread + // without having the called function run with whatever stop reason you stopped with. + void + SetStopInfoToNothing(); + + bool + ThreadStoppedForAReason (); + + static const char * + RunModeAsCString (lldb::RunMode mode); + + static const char * + StopReasonAsCString (lldb::StopReason reason); + + virtual const char * + GetInfo () + { + return NULL; + } + + virtual const char * + GetName () + { + return NULL; + } + + virtual const char * + GetQueueName () + { + return NULL; + } + + virtual uint32_t + GetStackFrameCount() + { + return GetStackFrameList()->GetNumFrames(); + } + + virtual lldb::StackFrameSP + GetStackFrameAtIndex (uint32_t idx) + { + return GetStackFrameList()->GetFrameAtIndex(idx); + } + + virtual lldb::StackFrameSP + GetFrameWithConcreteFrameIndex (uint32_t unwind_idx); + + bool + DecrementCurrentInlinedDepth() + { + return GetStackFrameList()->DecrementCurrentInlinedDepth(); + } + + uint32_t + GetCurrentInlinedDepth() + { + return GetStackFrameList()->GetCurrentInlinedDepth(); + } + + Error + ReturnFromFrameWithIndex (uint32_t frame_idx, lldb::ValueObjectSP return_value_sp, bool broadcast = false); + + Error + ReturnFromFrame (lldb::StackFrameSP frame_sp, lldb::ValueObjectSP return_value_sp, bool broadcast = false); + + virtual lldb::StackFrameSP + GetFrameWithStackID (const StackID &stack_id) + { + if (stack_id.IsValid()) + return GetStackFrameList()->GetFrameWithStackID (stack_id); + return lldb::StackFrameSP(); + } + + uint32_t + GetSelectedFrameIndex () + { + return GetStackFrameList()->GetSelectedFrameIndex(); + } + + lldb::StackFrameSP + GetSelectedFrame () + { + lldb::StackFrameListSP stack_frame_list_sp(GetStackFrameList()); + return stack_frame_list_sp->GetFrameAtIndex (stack_frame_list_sp->GetSelectedFrameIndex()); + } + + uint32_t + SetSelectedFrame (lldb_private::StackFrame *frame, bool broadcast = false); + + + bool + SetSelectedFrameByIndex (uint32_t frame_idx, bool broadcast = false); + + bool + SetSelectedFrameByIndexNoisily (uint32_t frame_idx, Stream &output_stream); + + void + SetDefaultFileAndLineToSelectedFrame() + { + GetStackFrameList()->SetDefaultFileAndLineToSelectedFrame(); + } + + virtual lldb::RegisterContextSP + GetRegisterContext () = 0; + + virtual lldb::RegisterContextSP + CreateRegisterContextForFrame (StackFrame *frame) = 0; + + virtual void + ClearStackFrames (); + + virtual bool + SetBackingThread (const lldb::ThreadSP &thread_sp) + { + return false; + } + + virtual lldb::ThreadSP + GetBackingThread () const + { + return lldb::ThreadSP(); + } + + virtual void + ClearBackingThread () + { + // Subclasses can use this function if a thread is actually backed by + // another thread. This is currently used for the OperatingSystem plug-ins + // where they might have a thread that is in memory, yet its registers + // are available through the lldb_private::Thread subclass for the current + // lldb_private::Process class. Since each time the process stops the backing + // threads for memory threads can change, we need a way to clear the backing + // thread for all memory threads each time we stop. + } + + void + DumpUsingSettingsFormat (Stream &strm, uint32_t frame_idx); + + //------------------------------------------------------------------ + // Thread Plan Providers: + // This section provides the basic thread plans that the Process control + // machinery uses to run the target. ThreadPlan.h provides more details on + // how this mechanism works. + // The thread provides accessors to a set of plans that perform basic operations. + // The idea is that particular Platform plugins can override these methods to + // provide the implementation of these basic operations appropriate to their + // environment. + // + // NB: All the QueueThreadPlanXXX providers return Shared Pointers to + // Thread plans. This is useful so that you can modify the plans after + // creation in ways specific to that plan type. Also, it is often necessary for + // ThreadPlans that utilize other ThreadPlans to implement their task to keep a shared + // pointer to the sub-plan. + // But besides that, the shared pointers should only be held onto by entities who live no longer + // than the thread containing the ThreadPlan. + // FIXME: If this becomes a problem, we can make a version that just returns a pointer, + // which it is clearly unsafe to hold onto, and a shared pointer version, and only allow + // ThreadPlan and Co. to use the latter. That is made more annoying to do because there's + // no elegant way to friend a method to all sub-classes of a given class. + // + //------------------------------------------------------------------ + + //------------------------------------------------------------------ + /// Queues the base plan for a thread. + /// The version returned by Process does some things that are useful, + /// like handle breakpoints and signals, so if you return a plugin specific + /// one you probably want to call through to the Process one for anything + /// your plugin doesn't explicitly handle. + /// + /// @param[in] abort_other_plans + /// \b true if we discard the currently queued plans and replace them with this one. + /// Otherwise this plan will go on the end of the plan stack. + /// + /// @return + /// A shared pointer to the newly queued thread plan, or NULL if the plan could not be queued. + //------------------------------------------------------------------ + virtual lldb::ThreadPlanSP + QueueFundamentalPlan (bool abort_other_plans); + + //------------------------------------------------------------------ + /// Queues the plan used to step over a breakpoint at the current PC of \a thread. + /// The default version returned by Process handles trap based breakpoints, and + /// will disable the breakpoint, single step over it, then re-enable it. + /// + /// @param[in] abort_other_plans + /// \b true if we discard the currently queued plans and replace them with this one. + /// Otherwise this plan will go on the end of the plan stack. + /// + /// @return + /// A shared pointer to the newly queued thread plan, or NULL if the plan could not be queued. + //------------------------------------------------------------------ + virtual lldb::ThreadPlanSP + QueueThreadPlanForStepOverBreakpointPlan (bool abort_other_plans); + + //------------------------------------------------------------------ + /// Queues the plan used to step one instruction from the current PC of \a thread. + /// + /// @param[in] step_over + /// \b true if we step over calls to functions, false if we step in. + /// + /// @param[in] abort_other_plans + /// \b true if we discard the currently queued plans and replace them with this one. + /// Otherwise this plan will go on the end of the plan stack. + /// + /// @param[in] stop_other_threads + /// \b true if we will stop other threads while we single step this one. + /// + /// @return + /// A shared pointer to the newly queued thread plan, or NULL if the plan could not be queued. + //------------------------------------------------------------------ + virtual lldb::ThreadPlanSP + QueueThreadPlanForStepSingleInstruction (bool step_over, + bool abort_other_plans, + bool stop_other_threads); + + //------------------------------------------------------------------ + /// Queues the plan used to step through an address range, stepping over + /// function calls. + /// + /// @param[in] abort_other_plans + /// \b true if we discard the currently queued plans and replace them with this one. + /// Otherwise this plan will go on the end of the plan stack. + /// + /// @param[in] type + /// Type of step to do, only eStepTypeInto and eStepTypeOver are supported by this plan. + /// + /// @param[in] range + /// The address range to step through. + /// + /// @param[in] addr_context + /// When dealing with stepping through inlined functions the current PC is not enough information to know + /// what "step" means. For instance a series of nested inline functions might start at the same address. + // The \a addr_context provides the current symbol context the step + /// is supposed to be out of. + // FIXME: Currently unused. + /// + /// @param[in] stop_other_threads + /// \b true if we will stop other threads while we single step this one. + /// + /// @return + /// A shared pointer to the newly queued thread plan, or NULL if the plan could not be queued. + //------------------------------------------------------------------ + virtual lldb::ThreadPlanSP + QueueThreadPlanForStepOverRange (bool abort_other_plans, + const AddressRange &range, + const SymbolContext &addr_context, + lldb::RunMode stop_other_threads); + + //------------------------------------------------------------------ + /// Queues the plan used to step through an address range, stepping into functions. + /// + /// @param[in] abort_other_plans + /// \b true if we discard the currently queued plans and replace them with this one. + /// Otherwise this plan will go on the end of the plan stack. + /// + /// @param[in] type + /// Type of step to do, only eStepTypeInto and eStepTypeOver are supported by this plan. + /// + /// @param[in] range + /// The address range to step through. + /// + /// @param[in] addr_context + /// When dealing with stepping through inlined functions the current PC is not enough information to know + /// what "step" means. For instance a series of nested inline functions might start at the same address. + // The \a addr_context provides the current symbol context the step + /// is supposed to be out of. + // FIXME: Currently unused. + /// + /// @param[in] step_in_target + /// Name if function we are trying to step into. We will step out if we don't land in that function. + /// + /// @param[in] stop_other_threads + /// \b true if we will stop other threads while we single step this one. + /// + /// @param[in] avoid_code_without_debug_info + /// If \b true we will step out if we step into code with no debug info. + /// + /// @return + /// A shared pointer to the newly queued thread plan, or NULL if the plan could not be queued. + //------------------------------------------------------------------ + virtual lldb::ThreadPlanSP + QueueThreadPlanForStepInRange (bool abort_other_plans, + const AddressRange &range, + const SymbolContext &addr_context, + const char *step_in_target, + lldb::RunMode stop_other_threads, + bool avoid_code_without_debug_info); + + //------------------------------------------------------------------ + /// Queue the plan used to step out of the function at the current PC of + /// \a thread. + /// + /// @param[in] abort_other_plans + /// \b true if we discard the currently queued plans and replace them with this one. + /// Otherwise this plan will go on the end of the plan stack. + /// + /// @param[in] addr_context + /// When dealing with stepping through inlined functions the current PC is not enough information to know + /// what "step" means. For instance a series of nested inline functions might start at the same address. + // The \a addr_context provides the current symbol context the step + /// is supposed to be out of. + // FIXME: Currently unused. + /// + /// @param[in] first_insn + /// \b true if this is the first instruction of a function. + /// + /// @param[in] stop_other_threads + /// \b true if we will stop other threads while we single step this one. + /// + /// @param[in] stop_vote + /// @param[in] run_vote + /// See standard meanings for the stop & run votes in ThreadPlan.h. + /// + /// @return + /// A shared pointer to the newly queued thread plan, or NULL if the plan could not be queued. + //------------------------------------------------------------------ + virtual lldb::ThreadPlanSP + QueueThreadPlanForStepOut (bool abort_other_plans, + SymbolContext *addr_context, + bool first_insn, + bool stop_other_threads, + Vote stop_vote, // = eVoteYes, + Vote run_vote, // = eVoteNoOpinion); + uint32_t frame_idx); + + //------------------------------------------------------------------ + /// Gets the plan used to step through the code that steps from a function + /// call site at the current PC into the actual function call. + /// + /// + /// @param[in] return_stack_id + /// The stack id that we will return to (by setting backstop breakpoints on the return + /// address to that frame) if we fail to step through. + /// + /// @param[in] abort_other_plans + /// \b true if we discard the currently queued plans and replace them with this one. + /// Otherwise this plan will go on the end of the plan stack. + /// + /// @param[in] stop_other_threads + /// \b true if we will stop other threads while we single step this one. + /// + /// @return + /// A shared pointer to the newly queued thread plan, or NULL if the plan could not be queued. + //------------------------------------------------------------------ + virtual lldb::ThreadPlanSP + QueueThreadPlanForStepThrough (StackID &return_stack_id, + bool abort_other_plans, + bool stop_other_threads); + + //------------------------------------------------------------------ + /// Gets the plan used to continue from the current PC. + /// This is a simple plan, mostly useful as a backstop when you are continuing + /// for some particular purpose. + /// + /// @param[in] abort_other_plans + /// \b true if we discard the currently queued plans and replace them with this one. + /// Otherwise this plan will go on the end of the plan stack. + /// + /// @param[in] target_addr + /// The address to which we're running. + /// + /// @param[in] stop_other_threads + /// \b true if we will stop other threads while we single step this one. + /// + /// @return + /// A shared pointer to the newly queued thread plan, or NULL if the plan could not be queued. + //------------------------------------------------------------------ + virtual lldb::ThreadPlanSP + QueueThreadPlanForRunToAddress (bool abort_other_plans, + Address &target_addr, + bool stop_other_threads); + + virtual lldb::ThreadPlanSP + QueueThreadPlanForStepUntil (bool abort_other_plans, + lldb::addr_t *address_list, + size_t num_addresses, + bool stop_others, + uint32_t frame_idx); + + virtual lldb::ThreadPlanSP + QueueThreadPlanForCallFunction (bool abort_other_plans, + Address& function, + lldb::addr_t arg, + bool stop_other_threads, + bool unwind_on_error = false, + bool ignore_breakpoints = true); + + //------------------------------------------------------------------ + // Thread Plan accessors: + //------------------------------------------------------------------ + + //------------------------------------------------------------------ + /// Gets the plan which will execute next on the plan stack. + /// + /// @return + /// A pointer to the next executed plan. + //------------------------------------------------------------------ + ThreadPlan * + GetCurrentPlan (); + + //------------------------------------------------------------------ + /// Unwinds the thread stack for the innermost expression plan currently + /// on the thread plan stack. + /// + /// @return + /// An error if the thread plan could not be unwound. + //------------------------------------------------------------------ + + Error + UnwindInnermostExpression(); + +private: + bool + PlanIsBasePlan (ThreadPlan *plan_ptr); + + void + BroadcastSelectedFrameChange(StackID &new_frame_id); + +public: + + //------------------------------------------------------------------ + /// Gets the outer-most plan that was popped off the plan stack in the + /// most recent stop. Useful for printing the stop reason accurately. + /// + /// @return + /// A pointer to the last completed plan. + //------------------------------------------------------------------ + lldb::ThreadPlanSP + GetCompletedPlan (); + + //------------------------------------------------------------------ + /// Gets the outer-most return value from the completed plans + /// + /// @return + /// A ValueObjectSP, either empty if there is no return value, + /// or containing the return value. + //------------------------------------------------------------------ + lldb::ValueObjectSP + GetReturnValueObject (); + + //------------------------------------------------------------------ + /// Checks whether the given plan is in the completed plans for this + /// stop. + /// + /// @param[in] plan + /// Pointer to the plan you're checking. + /// + /// @return + /// Returns true if the input plan is in the completed plan stack, + /// false otherwise. + //------------------------------------------------------------------ + bool + IsThreadPlanDone (ThreadPlan *plan); + + //------------------------------------------------------------------ + /// Checks whether the given plan is in the discarded plans for this + /// stop. + /// + /// @param[in] plan + /// Pointer to the plan you're checking. + /// + /// @return + /// Returns true if the input plan is in the discarded plan stack, + /// false otherwise. + //------------------------------------------------------------------ + bool + WasThreadPlanDiscarded (ThreadPlan *plan); + + //------------------------------------------------------------------ + /// Queues a generic thread plan. + /// + /// @param[in] plan_sp + /// The plan to queue. + /// + /// @param[in] abort_other_plans + /// \b true if we discard the currently queued plans and replace them with this one. + /// Otherwise this plan will go on the end of the plan stack. + /// + /// @return + /// A pointer to the last completed plan. + //------------------------------------------------------------------ + void + QueueThreadPlan (lldb::ThreadPlanSP &plan_sp, bool abort_other_plans); + + + //------------------------------------------------------------------ + /// Discards the plans queued on the plan stack of the current thread. This is + /// arbitrated by the "Master" ThreadPlans, using the "OkayToDiscard" call. + // But if \a force is true, all thread plans are discarded. + //------------------------------------------------------------------ + void + DiscardThreadPlans (bool force); + + //------------------------------------------------------------------ + /// Discards the plans queued on the plan stack of the current thread up to and + /// including up_to_plan_sp. + // + // @param[in] up_to_plan_sp + // Discard all plans up to and including this one. + //------------------------------------------------------------------ + void + DiscardThreadPlansUpToPlan (lldb::ThreadPlanSP &up_to_plan_sp); + + void + DiscardThreadPlansUpToPlan (ThreadPlan *up_to_plan_ptr); + + //------------------------------------------------------------------ + /// Prints the current plan stack. + /// + /// @param[in] s + /// The stream to which to dump the plan stack info. + /// + //------------------------------------------------------------------ + void + DumpThreadPlans (Stream *s) const; + + virtual bool + CheckpointThreadState (ThreadStateCheckpoint &saved_state); + + virtual bool + RestoreRegisterStateFromCheckpoint (ThreadStateCheckpoint &saved_state); + + virtual bool + RestoreThreadStateFromCheckpoint (ThreadStateCheckpoint &saved_state); + + void + EnableTracer (bool value, bool single_step); + + void + SetTracer (lldb::ThreadPlanTracerSP &tracer_sp); + + //------------------------------------------------------------------ + // Get the thread index ID. The index ID that is guaranteed to not + // be re-used by a process. They start at 1 and increase with each + // new thread. This allows easy command line access by a unique ID + // that is easier to type than the actual system thread ID. + //------------------------------------------------------------------ + uint32_t + GetIndexID () const; + + + //------------------------------------------------------------------ + // The API ID is often the same as the Thread::GetID(), but not in + // all cases. Thread::GetID() is the user visible thread ID that + // clients would want to see. The API thread ID is the thread ID + // that is used when sending data to/from the debugging protocol. + //------------------------------------------------------------------ + virtual lldb::user_id_t + GetProtocolID () const + { + return GetID(); + } + + //------------------------------------------------------------------ + // lldb::ExecutionContextScope pure virtual functions + //------------------------------------------------------------------ + virtual lldb::TargetSP + CalculateTarget (); + + virtual lldb::ProcessSP + CalculateProcess (); + + virtual lldb::ThreadSP + CalculateThread (); + + virtual lldb::StackFrameSP + CalculateStackFrame (); + + virtual void + CalculateExecutionContext (ExecutionContext &exe_ctx); + + lldb::StackFrameSP + GetStackFrameSPForStackFramePtr (StackFrame *stack_frame_ptr); + + size_t + GetStatus (Stream &strm, + uint32_t start_frame, + uint32_t num_frames, + uint32_t num_frames_with_source); + + size_t + GetStackFrameStatus (Stream& strm, + uint32_t first_frame, + uint32_t num_frames, + bool show_frame_info, + uint32_t num_frames_with_source); + + // We need a way to verify that even though we have a thread in a shared + // pointer that the object itself is still valid. Currently this won't be + // the case if DestroyThread() was called. DestroyThread is called when + // a thread has been removed from the Process' thread list. + bool + IsValid () const + { + return !m_destroy_called; + } + + // Sets and returns a valid stop info based on the process stop ID and the + // current thread plan. If the thread stop ID does not match the process' + // stop ID, the private stop reason is not set and an invalid StopInfoSP may + // be returned. + // + // NOTE: This function must be called before the current thread plan is + // moved to the completed plan stack (in Thread::ShouldStop()). + // + // NOTE: If subclasses override this function, ensure they do not overwrite + // the m_actual_stop_info if it is valid. The stop info may be a + // "checkpointed and restored" stop info, so if it is still around it is + // right even if you have not calculated this yourself, or if it disagrees + // with what you might have calculated. + virtual lldb::StopInfoSP + GetPrivateStopInfo (); + + //---------------------------------------------------------------------- + // Ask the thread subclass to set its stop info. + // + // Thread subclasses should call Thread::SetStopInfo(...) with the + // reason the thread stopped. + // + // @return + // True if Thread::SetStopInfo(...) was called, false otherwise. + //---------------------------------------------------------------------- + virtual bool + CalculateStopInfo () = 0; + + //---------------------------------------------------------------------- + // Gets the temporary resume state for a thread. + // + // This value gets set in each thread by complex debugger logic in + // Thread::ShouldResume() and an appropriate thread resume state will get + // set in each thread every time the process is resumed prior to calling + // Process::DoResume(). The lldb_private::Process subclass should adhere + // to the thread resume state request which will be one of: + // + // eStateRunning - thread will resume when process is resumed + // eStateStepping - thread should step 1 instruction and stop when process + // is resumed + // eStateSuspended - thread should not execute any instructions when + // process is resumed + //---------------------------------------------------------------------- + lldb::StateType + GetTemporaryResumeState() const + { + return m_temporary_resume_state; + } + + void + SetStopInfo (const lldb::StopInfoSP &stop_info_sp); + + void + SetShouldReportStop (Vote vote); + +protected: + + friend class ThreadPlan; + friend class ThreadList; + friend class ThreadEventData; + friend class StackFrameList; + friend class StackFrame; + friend class OperatingSystem; + + // This is necessary to make sure thread assets get destroyed while the thread is still in good shape + // to call virtual thread methods. This must be called by classes that derive from Thread in their destructor. + virtual void DestroyThread (); + + void + PushPlan (lldb::ThreadPlanSP &plan_sp); + + void + PopPlan (); + + void + DiscardPlan (); + + ThreadPlan *GetPreviousPlan (ThreadPlan *plan); + + typedef std::vector plan_stack; + + virtual bool + SaveFrameZeroState (RegisterCheckpoint &checkpoint); + + virtual bool + RestoreSaveFrameZero (const RegisterCheckpoint &checkpoint); + + // register_data_sp must be a DataSP passed to ReadAllRegisterValues. + bool + ResetFrameZeroRegisters (lldb::DataBufferSP register_data_sp); + + virtual lldb_private::Unwind * + GetUnwinder (); + + // Check to see whether the thread is still at the last breakpoint hit that stopped it. + virtual bool + IsStillAtLastBreakpointHit(); + + // Some threads are threads that are made up by OperatingSystem plugins that + // are threads that exist and are context switched out into memory. The + // OperatingSystem plug-in need a ways to know if a thread is "real" or made + // up. + virtual bool + IsOperatingSystemPluginThread () const + { + return false; + } + + + lldb::StackFrameListSP + GetStackFrameList (); + + struct ThreadState + { + uint32_t orig_stop_id; + lldb::StopInfoSP stop_info_sp; + RegisterCheckpoint register_backup; + }; + + //------------------------------------------------------------------ + // Classes that inherit from Process can see and modify these + //------------------------------------------------------------------ + lldb::ProcessWP m_process_wp; ///< The process that owns this thread. + lldb::StopInfoSP m_stop_info_sp; ///< The private stop reason for this thread + uint32_t m_stop_info_stop_id; // This is the stop id for which the StopInfo is valid. Can use this so you know that + // the thread's m_stop_info_sp is current and you don't have to fetch it again + const uint32_t m_index_id; ///< A unique 1 based index assigned to each thread for easy UI/command line access. + lldb::RegisterContextSP m_reg_context_sp; ///< The register context for this thread's current register state. + lldb::StateType m_state; ///< The state of our process. + mutable Mutex m_state_mutex; ///< Multithreaded protection for m_state. + plan_stack m_plan_stack; ///< The stack of plans this thread is executing. + plan_stack m_completed_plan_stack; ///< Plans that have been completed by this stop. They get deleted when the thread resumes. + plan_stack m_discarded_plan_stack; ///< Plans that have been discarded by this stop. They get deleted when the thread resumes. + mutable Mutex m_frame_mutex; ///< Multithreaded protection for m_state. + lldb::StackFrameListSP m_curr_frames_sp; ///< The stack frames that get lazily populated after a thread stops. + lldb::StackFrameListSP m_prev_frames_sp; ///< The previous stack frames from the last time this thread stopped. + int m_resume_signal; ///< The signal that should be used when continuing this thread. + lldb::StateType m_resume_state; ///< This state is used to force a thread to be suspended from outside the ThreadPlan logic. + lldb::StateType m_temporary_resume_state; ///< This state records what the thread was told to do by the thread plan logic for the current resume. + /// It gets set in Thread::ShoudResume. + std::unique_ptr m_unwinder_ap; + bool m_destroy_called; // This is used internally to make sure derived Thread classes call DestroyThread. + LazyBool m_override_should_notify; +private: + //------------------------------------------------------------------ + // For Thread only + //------------------------------------------------------------------ + + DISALLOW_COPY_AND_ASSIGN (Thread); + +}; + +} // namespace lldb_private + +#endif // liblldb_Thread_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Target/ThreadList.h b/contrib/llvm/tools/lldb/include/lldb/Target/ThreadList.h new file mode 100644 index 00000000000..ddf49b002ec --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Target/ThreadList.h @@ -0,0 +1,163 @@ +//===-- ThreadList.h --------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ThreadList_h_ +#define liblldb_ThreadList_h_ + +#include + +#include "lldb/lldb-private.h" +#include "lldb/Core/UserID.h" + + +// FIXME: Currently this is a thread list with lots of functionality for use only by +// the process for which this is the thread list. If we ever want a container class +// to hand out that is just a random subset of threads, with iterator functionality, +// then we should make that part a base class, and make a ProcessThreadList for the +// process. +namespace lldb_private { + +class ThreadList +{ +friend class Process; + +public: + + ThreadList (Process *process); + + ThreadList (const ThreadList &rhs); + + ~ThreadList (); + + const ThreadList& + operator = (const ThreadList& rhs); + + uint32_t + GetSize(bool can_update = true); + + void + AddThread (const lldb::ThreadSP &thread_sp); + + // Return the selected thread if there is one. Otherwise, return the thread + // selected at index 0. + lldb::ThreadSP + GetSelectedThread (); + + bool + SetSelectedThreadByID (lldb::tid_t tid, bool notify = false); + + bool + SetSelectedThreadByIndexID (uint32_t index_id, bool notify = false); + + void + Clear(); + + void + Flush(); + + void + Destroy(); + + // Note that "idx" is not the same as the "thread_index". It is a zero + // based index to accessing the current threads, whereas "thread_index" + // is a unique index assigned + lldb::ThreadSP + GetThreadAtIndex (uint32_t idx, bool can_update = true); + + lldb::ThreadSP + FindThreadByID (lldb::tid_t tid, bool can_update = true); + + lldb::ThreadSP + FindThreadByProtocolID (lldb::tid_t tid, bool can_update = true); + + lldb::ThreadSP + RemoveThreadByID (lldb::tid_t tid, bool can_update = true); + + lldb::ThreadSP + RemoveThreadByProtocolID (lldb::tid_t tid, bool can_update = true); + + lldb::ThreadSP + FindThreadByIndexID (uint32_t index_id, bool can_update = true); + + lldb::ThreadSP + GetThreadSPForThreadPtr (Thread *thread_ptr); + + bool + ShouldStop (Event *event_ptr); + + Vote + ShouldReportStop (Event *event_ptr); + + Vote + ShouldReportRun (Event *event_ptr); + + void + RefreshStateAfterStop (); + + //------------------------------------------------------------------ + /// The thread list asks tells all the threads it is about to resume. + /// If a thread can "resume" without having to resume the target, it + /// will return false for WillResume, and then the process will not be + /// restarted. + /// + /// @return + /// \b true instructs the process to resume normally, + /// \b false means start & stopped events will be generated, but + /// the process will not actually run. The thread must then return + /// the correct StopInfo when asked. + /// + //------------------------------------------------------------------ + bool + WillResume (); + + void + DidResume (); + + void + DidStop (); + + void + DiscardThreadPlans(); + + uint32_t + GetStopID () const; + + void + SetStopID (uint32_t stop_id); + + Mutex & + GetMutex (); + + void + Update (ThreadList &rhs); + +protected: + + void + SetShouldReportStop (Vote vote); + + void + NotifySelectedThreadChanged (lldb::tid_t tid); + + typedef std::vector collection; + //------------------------------------------------------------------ + // Classes that inherit from Process can see and modify these + //------------------------------------------------------------------ + Process *m_process; ///< The process that manages this thread list. + uint32_t m_stop_id; ///< The process stop ID that this thread list is valid for. + collection m_threads; ///< The threads for this process. + lldb::tid_t m_selected_tid; ///< For targets that need the notion of a current thread. + +private: + ThreadList (); +}; + +} // namespace lldb_private + +#endif // liblldb_ThreadList_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Target/ThreadPlan.h b/contrib/llvm/tools/lldb/include/lldb/Target/ThreadPlan.h new file mode 100644 index 00000000000..3c83fd1b963 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Target/ThreadPlan.h @@ -0,0 +1,658 @@ +//===-- ThreadPlan.h --------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ThreadPlan_h_ +#define liblldb_ThreadPlan_h_ + +// C Includes +// C++ Includes +#include +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private.h" +#include "lldb/Core/UserID.h" +#include "lldb/Host/Mutex.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" +#include "lldb/Target/ThreadPlanTracer.h" +#include "lldb/Target/StopInfo.h" + +namespace lldb_private { + +//------------------------------------------------------------------ +// ThreadPlan: +// This is the pure virtual base class for thread plans. +// +// The thread plans provide the "atoms" of behavior that +// all the logical process control, either directly from commands or through +// more complex composite plans will rely on. +// +// Plan Stack: +// +// The thread maintaining a thread plan stack, and you program the actions of a particular thread +// by pushing plans onto the plan stack. +// There is always a "Current" plan, which is the head of the plan stack, though in some cases +// a plan may defer to plans higher in the stack for some piece of information. +// +// The plan stack is never empty, there is always a Base Plan which persists through the life +// of the running process. +// +// +// Creating Plans: +// +// The thread plan is generally created and added to the plan stack through the QueueThreadPlanFor... API +// in lldb::Thread. Those API's will return the plan that performs the named operation in a manner +// appropriate for the current process. The plans in lldb/source/Target are generic +// implementations, but a Process plugin can override them. +// +// ValidatePlan is then called. If it returns false, the plan is unshipped. This is a little +// convenience which keeps us from having to error out of the constructor. +// +// Then the plan is added to the plan stack. When the plan is added to the plan stack its DidPush +// will get called. This is useful if a plan wants to push any additional plans as it is constructed, +// since you need to make sure you're already on the stack before you push additional plans. +// +// Completed Plans: +// +// When the target process stops the plans are queried, among other things, for whether their job is done. +// If it is they are moved from the plan stack to the Completed Plan stack in reverse order from their position +// on the plan stack (since multiple plans may be done at a given stop.) This is used primarily so that +// the lldb::Thread::StopInfo for the thread can be set properly. If one plan pushes another to achieve part of +// its job, but it doesn't want that sub-plan to be the one that sets the StopInfo, then call SetPrivate on the +// sub-plan when you create it, and the Thread will pass over that plan in reporting the reason for the stop. +// +// Discarded plans: +// +// Your plan may also get discarded, i.e. moved from the plan stack to the "discarded plan stack". This can +// happen, for instance, if the plan is calling a function and the function call crashes and you want +// to unwind the attempt to call. So don't assume that your plan will always successfully stop. Which leads to: +// +// Cleaning up after your plans: +// +// When the plan is moved from the plan stack its WillPop method is always called, no matter why. Once it is +// moved off the plan stack it is done, and won't get a chance to run again. So you should +// undo anything that affects target state in this method. But be sure to leave the plan able to correctly +// fill the StopInfo, however. +// N.B. Don't wait to do clean up target state till the destructor, since that will usually get called when +// the target resumes, and you want to leave the target state correct for new plans in the time between when +// your plan gets unshipped and the next resume. +// +// Over the lifetime of the plan, various methods of the ThreadPlan are then called in response to changes of state in +// the process we are debugging as follows: +// +// Resuming: +// +// When the target process is about to be restarted, the plan's WillResume method is called, +// giving the plan a chance to prepare for the run. If WillResume returns false, then the +// process is not restarted. Be sure to set an appropriate error value in the Process if +// you have to do this. Note, ThreadPlans actually implement DoWillResume, WillResume wraps that call. +// +// Next the "StopOthers" method of all the threads are polled, and if one thread's Current plan +// returns "true" then only that thread gets to run. If more than one returns "true" the threads that want to run solo +// get run one by one round robin fashion. Otherwise all are let to run. +// +// Note, the way StopOthers is implemented, the base class implementation just asks the previous plan. So if your plan +// has no opinion about whether it should run stopping others or not, just don't implement StopOthers, and the parent +// will be asked. +// +// Finally, for each thread that is running, it run state is set to the return of RunState from the +// thread's Current plan. +// +// Responding to a stop: +// +// When the target process stops, the plan is called in the following stages: +// +// First the thread asks the Current Plan if it can handle this stop by calling PlanExplainsStop. +// If the Current plan answers "true" then it is asked if the stop should percolate all the way to the +// user by calling the ShouldStop method. If the current plan doesn't explain the stop, then we query down +// the plan stack for a plan that does explain the stop. The plan that does explain the stop then needs to +// figure out what to do about the plans below it in the stack. If the stop is recoverable, then the plan that +// understands it can just do what it needs to set up to restart, and then continue. +// Otherwise, the plan that understood the stop should call DiscardPlanStack to clean up the stack below it. +// Note, plans actually implement DoPlanExplainsStop, the result is cached in PlanExplainsStop so the DoPlanExplainsStop +// itself will only get called once per stop. +// +// Master plans: +// +// In the normal case, when we decide to stop, we will collapse the plan stack up to the point of the plan that understood +// the stop reason. However, if a plan wishes to stay on the stack after an event it didn't directly handle +// it can designate itself a "Master" plan by responding true to IsMasterPlan, and then if it wants not to be +// discarded, it can return true to OkayToDiscard, and it and all its dependent plans will be preserved when +// we resume execution. +// +// The other effect of being a master plan is that when the Master plan is done , if it has set "OkayToDiscard" to false, +// then it will be popped & execution will stop and return to the user. Remember that if OkayToDiscard is false, the +// plan will be popped and control will be given to the next plan above it on the stack So setting OkayToDiscard to +// false means the user will regain control when the MasterPlan is completed. +// +// Between these two controls this allows things like: a MasterPlan/DontDiscard Step Over to hit a breakpoint, stop and +// return control to the user, but then when the user continues, the step out succeeds. +// Even more tricky, when the breakpoint is hit, the user can continue to step in/step over/etc, and finally when they +// continue, they will finish up the Step Over. +// +// FIXME: MasterPlan & OkayToDiscard aren't really orthogonal. MasterPlan designation means that this plan controls +// it's fate and the fate of plans below it. OkayToDiscard tells whether the MasterPlan wants to stay on the stack. I +// originally thought "MasterPlan-ness" would need to be a fixed characteristic of a ThreadPlan, in which case you needed +// the extra control. But that doesn't seem to be true. So we should be able to convert to only MasterPlan status to mean +// the current "MasterPlan/DontDiscard". Then no plans would be MasterPlans by default, and you would set the ones you +// wanted to be "user level" in this way. +// +// +// Actually Stopping: +// +// If a plan says responds "true" to ShouldStop, then it is asked if it's job is complete by calling +// MischiefManaged. If that returns true, the thread is popped from the plan stack and added to the +// Completed Plan Stack. Then the next plan in the stack is asked if it ShouldStop, and it returns "true", +// it is asked if it is done, and if yes popped, and so on till we reach a plan that is not done. +// +// Since you often know in the ShouldStop method whether your plan is complete, as a convenience you can call +// SetPlanComplete and the ThreadPlan implementation of MischiefManaged will return "true", without your having +// to redo the calculation when your sub-classes MischiefManaged is called. If you call SetPlanComplete, you can +// later use IsPlanComplete to determine whether the plan is complete. This is only a convenience for sub-classes, +// the logic in lldb::Thread will only call MischiefManaged. +// +// One slightly tricky point is you have to be careful using SetPlanComplete in PlanExplainsStop because you +// are not guaranteed that PlanExplainsStop for a plan will get called before ShouldStop gets called. If your sub-plan +// explained the stop and then popped itself, only your ShouldStop will get called. +// +// If ShouldStop for any thread returns "true", then the WillStop method of the Current plan of +// all threads will be called, the stop event is placed on the Process's public broadcaster, and +// control returns to the upper layers of the debugger. +// +// Reporting the stop: +// +// When the process stops, the thread is given a StopReason, in the form of a StopInfo object. If there is a completed +// plan corresponding to the stop, then the "actual" stop reason will be suppressed, and instead a StopInfoThreadPlan +// object will be cons'ed up from the highest completed plan in the stack. However, if the plan doesn't want to be +// the stop reason, then it can call SetPlanComplete and pass in "false" for the "success" parameter. In that case, +// the real stop reason will be used instead. One exapmle of this is the "StepRangeStepIn" thread plan. If it stops +// because of a crash or breakpoint hit, it wants to unship itself, because it isn't so useful to have step in keep going +// after a breakpoint hit. But it can't be the reason for the stop or no-one would see that they had hit a breakpoint. +// +// Cleaning up the plan stack: +// +// One of the complications of MasterPlans is that you may get past the limits of a plan without triggering it to clean +// itself up. For instance, if you are doing a MasterPlan StepOver, and hit a breakpoint in a called function, then +// step over enough times to step out of the initial StepOver range, each of the step overs will explain the stop & +// take themselves off the stack, but control would never be returned to the original StepOver. Eventually, the user +// will continue, and when that continue stops, the old stale StepOver plan that was left on the stack will get woken +// up and notice it is done. But that can leave junk on the stack for a while. To avoid that, the plans implement a +// "IsPlanStale" method, that can check whether it is relevant anymore. On stop, after the regular plan negotiation, +// the remaining plan stack is consulted and if any plan says it is stale, it and the plans below it are discarded from +// the stack. +// +// Automatically Resuming: +// +// If ShouldStop for all threads returns "false", then the target process will resume. This then cycles back to +// Resuming above. +// +// Reporting eStateStopped events when the target is restarted: +// +// If a plan decides to auto-continue the target by returning "false" from ShouldStop, then it will be asked +// whether the Stopped event should still be reported. For instance, if you hit a breakpoint that is a User set +// breakpoint, but the breakpoint callback said to continue the target process, you might still want to inform +// the upper layers of lldb that the stop had happened. +// The way this works is every thread gets to vote on whether to report the stop. If all votes are eVoteNoOpinion, +// then the thread list will decide what to do (at present it will pretty much always suppress these stopped events.) +// If there is an eVoteYes, then the event will be reported regardless of the other votes. If there is an eVoteNo +// and no eVoteYes's, then the event won't be reported. +// +// One other little detail here, sometimes a plan will push another plan onto the plan stack to do some part of +// the first plan's job, and it would be convenient to tell that plan how it should respond to ShouldReportStop. +// You can do that by setting the stop_vote in the child plan when you create it. +// +// Suppressing the initial eStateRunning event: +// +// The private process running thread will take care of ensuring that only one "eStateRunning" event will be +// delivered to the public Process broadcaster per public eStateStopped event. However there are some cases +// where the public state of this process is eStateStopped, but a thread plan needs to restart the target, but +// doesn't want the running event to be publically broadcast. The obvious example of this is running functions +// by hand as part of expression evaluation. To suppress the running event return eVoteNo from ShouldReportStop, +// to force a running event to be reported return eVoteYes, in general though you should return eVoteNoOpinion +// which will allow the ThreadList to figure out the right thing to do. +// The run_vote argument to the constructor works like stop_vote, and is a way for a plan to instruct a sub-plan +// on how to respond to ShouldReportStop. +// +//------------------------------------------------------------------ + +class ThreadPlan : + public UserID +{ +public: + typedef enum + { + eAllThreads, + eSomeThreads, + eThisThread + } ThreadScope; + + // We use these enums so that we can cast a base thread plan to it's real type without having to resort + // to dynamic casting. + typedef enum + { + eKindGeneric, + eKindNull, + eKindBase, + eKindCallFunction, + eKindStepInstruction, + eKindStepOut, + eKindStepOverBreakpoint, + eKindStepOverRange, + eKindStepInRange, + eKindRunToAddress, + eKindStepThrough, + eKindStepUntil, + eKindTestCondition + + } ThreadPlanKind; + + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + ThreadPlan (ThreadPlanKind kind, + const char *name, + Thread &thread, + Vote stop_vote, + Vote run_vote); + + virtual + ~ThreadPlan(); + + //------------------------------------------------------------------ + /// Returns the name of this thread plan. + /// + /// @return + /// A const char * pointer to the thread plan's name. + //------------------------------------------------------------------ + const char * + GetName () const + { + return m_name.c_str(); + } + + //------------------------------------------------------------------ + /// Returns the Thread that is using this thread plan. + /// + /// @return + /// A pointer to the thread plan's owning thread. + //------------------------------------------------------------------ + Thread & + GetThread() + { + return m_thread; + } + + const Thread & + GetThread() const + { + return m_thread; + } + + Target & + GetTarget() + { + return m_thread.GetProcess()->GetTarget(); + } + + const Target & + GetTarget() const + { + return m_thread.GetProcess()->GetTarget(); + } + + //------------------------------------------------------------------ + /// Print a description of this thread to the stream \a s. + /// \a thread. + /// + /// @param[in] s + /// The stream to which to print the description. + /// + /// @param[in] level + /// The level of description desired. Note that eDescriptionLevelBrief + /// will be used in the stop message printed when the plan is complete. + //------------------------------------------------------------------ + virtual void + GetDescription (Stream *s, + lldb::DescriptionLevel level) = 0; + + //------------------------------------------------------------------ + /// Returns whether this plan could be successfully created. + /// + /// @param[in] error + /// A stream to which to print some reason why the plan could not be created. + /// Can be NULL. + /// + /// @return + /// \b true if the plan should be queued, \b false otherwise. + //------------------------------------------------------------------ + virtual bool + ValidatePlan (Stream *error) = 0; + + bool + TracerExplainsStop () + { + if (!m_tracer_sp) + return false; + else + return m_tracer_sp->TracerExplainsStop(); + } + + + lldb::StateType + RunState (); + + bool + PlanExplainsStop (Event *event_ptr); + + virtual bool + ShouldStop (Event *event_ptr) = 0; + + virtual bool + ShouldAutoContinue (Event *event_ptr) + { + return false; + } + + // Whether a "stop class" event should be reported to the "outside world". In general + // if a thread plan is active, events should not be reported. + + virtual Vote + ShouldReportStop (Event *event_ptr); + + virtual Vote + ShouldReportRun (Event *event_ptr); + + virtual void + SetStopOthers (bool new_value); + + virtual bool + StopOthers (); + + // This is the wrapper for DoWillResume that does generic ThreadPlan logic, then + // calls DoWillResume. + bool + WillResume (lldb::StateType resume_state, bool current_plan); + + virtual bool + WillStop () = 0; + + bool + IsMasterPlan() + { + return m_is_master_plan; + } + + bool + SetIsMasterPlan (bool value) + { + bool old_value = m_is_master_plan; + m_is_master_plan = value; + return old_value; + } + + virtual bool + OkayToDiscard(); + + void + SetOkayToDiscard (bool value) + { + m_okay_to_discard = value; + } + + // The base class MischiefManaged does some cleanup - so you have to call it + // in your MischiefManaged derived class. + virtual bool + MischiefManaged (); + + virtual void + ThreadDestroyed () + { + // Any cleanup that a plan might want to do in case the thread goes away + // in the middle of the plan being queued on a thread can be done here. + } + + bool + GetPrivate () + { + return m_plan_private; + } + + void + SetPrivate (bool input) + { + m_plan_private = input; + } + + virtual void + DidPush(); + + virtual void + WillPop(); + + // This pushes a plan onto the plan stack of the current plan's thread. + void + PushPlan (lldb::ThreadPlanSP &thread_plan_sp) + { + m_thread.PushPlan (thread_plan_sp); + } + + ThreadPlanKind GetKind() const + { + return m_kind; + } + + bool + IsPlanComplete(); + + void + SetPlanComplete (bool success = true); + + virtual bool + IsPlanStale () + { + return false; + } + + bool + PlanSucceeded () + { + return m_plan_succeeded; + } + + virtual bool + IsBasePlan() + { + return false; + } + + lldb::ThreadPlanTracerSP & + GetThreadPlanTracer() + { + return m_tracer_sp; + } + + void + SetThreadPlanTracer (lldb::ThreadPlanTracerSP new_tracer_sp) + { + m_tracer_sp = new_tracer_sp; + } + + void + DoTraceLog () + { + if (m_tracer_sp && m_tracer_sp->TracingEnabled()) + m_tracer_sp->Log(); + } + + // Some thread plans hide away the actual stop info which caused any particular stop. For + // instance the ThreadPlanCallFunction restores the original stop reason so that stopping and + // calling a few functions won't lose the history of the run. + // This call can be implemented to get you back to the real stop info. + virtual lldb::StopInfoSP + GetRealStopInfo () + { + return m_thread.GetStopInfo (); + } + + virtual lldb::ValueObjectSP + GetReturnValueObject () + { + return lldb::ValueObjectSP(); + } + + // If a thread plan stores the state before it was run, then you might + // want to restore the state when it is done. This will do that job. + // This is mostly useful for artificial plans like CallFunction plans. + + virtual bool + RestoreThreadState() + { + // Nothing to do in general. + return true; + } + + virtual bool + IsVirtualStep() + { + return false; + } + +protected: + //------------------------------------------------------------------ + // Classes that inherit from ThreadPlan can see and modify these + //------------------------------------------------------------------ + + virtual bool + DoWillResume (lldb::StateType resume_state, bool current_plan) { return true; }; + + virtual bool + DoPlanExplainsStop (Event *event_ptr) = 0; + + // This gets the previous plan to the current plan (for forwarding requests). + // This is mostly a formal requirement, it allows us to make the Thread's + // GetPreviousPlan protected, but only friend ThreadPlan to thread. + + ThreadPlan * + GetPreviousPlan () + { + return m_thread.GetPreviousPlan (this); + } + + // This forwards the private Thread::GetPrivateStopInfo which is generally what + // ThreadPlan's need to know. + + lldb::StopInfoSP + GetPrivateStopInfo() + { + return m_thread.GetPrivateStopInfo (); + } + + void + SetStopInfo (lldb::StopInfoSP stop_reason_sp) + { + m_thread.SetStopInfo (stop_reason_sp); + } + + void + CachePlanExplainsStop (bool does_explain) + { + m_cached_plan_explains_stop = does_explain ? eLazyBoolYes : eLazyBoolNo; + } + + LazyBool + GetCachedPlanExplainsStop () const + { + return m_cached_plan_explains_stop; + } + + virtual lldb::StateType + GetPlanRunState () = 0; + + Thread &m_thread; + Vote m_stop_vote; + Vote m_run_vote; + +private: + //------------------------------------------------------------------ + // For ThreadPlan only + //------------------------------------------------------------------ + static lldb::user_id_t GetNextID (); + + ThreadPlanKind m_kind; + std::string m_name; + Mutex m_plan_complete_mutex; + LazyBool m_cached_plan_explains_stop; + bool m_plan_complete; + bool m_plan_private; + bool m_okay_to_discard; + bool m_is_master_plan; + bool m_plan_succeeded; + + lldb::ThreadPlanTracerSP m_tracer_sp; + +private: + DISALLOW_COPY_AND_ASSIGN(ThreadPlan); +}; + +//---------------------------------------------------------------------- +// ThreadPlanNull: +// Threads are assumed to always have at least one plan on the plan stack. +// This is put on the plan stack when a thread is destroyed so that if you +// accidentally access a thread after it is destroyed you won't crash. +// But asking questions of the ThreadPlanNull is definitely an error. +//---------------------------------------------------------------------- + +class ThreadPlanNull : public ThreadPlan +{ +public: + ThreadPlanNull (Thread &thread); + virtual ~ThreadPlanNull (); + + virtual void + GetDescription (Stream *s, + lldb::DescriptionLevel level); + + virtual bool + ValidatePlan (Stream *error); + + virtual bool + ShouldStop (Event *event_ptr); + + virtual bool + MischiefManaged (); + + virtual bool + WillStop (); + + virtual bool + IsBasePlan() + { + return true; + } + + virtual bool + OkayToDiscard () + { + return false; + } + +protected: + virtual bool + DoPlanExplainsStop (Event *event_ptr); + + virtual lldb::StateType + GetPlanRunState (); + +}; + + +} // namespace lldb_private + +#endif // liblldb_ThreadPlan_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Target/ThreadPlanBase.h b/contrib/llvm/tools/lldb/include/lldb/Target/ThreadPlanBase.h new file mode 100644 index 00000000000..69959e12f84 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Target/ThreadPlanBase.h @@ -0,0 +1,71 @@ +//===-- ThreadPlanBase.h ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ThreadPlanFundamental_h_ +#define liblldb_ThreadPlanFundamental_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Target/Process.h" +#include "lldb/Target/Thread.h" +#include "lldb/Target/ThreadPlan.h" + +namespace lldb_private { + + +//------------------------------------------------------------------ +// Base thread plans: +// This is the generic version of the bottom most plan on the plan stack. It should +// be able to handle generic breakpoint hitting, and signals and exceptions. +//------------------------------------------------------------------ + +class ThreadPlanBase : public ThreadPlan +{ +friend class Process; // RunThreadPlan manages "stopper" base plans. +public: + virtual ~ThreadPlanBase (); + + virtual void GetDescription (Stream *s, lldb::DescriptionLevel level); + virtual bool ValidatePlan (Stream *error); + virtual bool ShouldStop (Event *event_ptr); + virtual Vote ShouldReportStop (Event *event_ptr); + virtual bool StopOthers (); + virtual lldb::StateType GetPlanRunState (); + virtual bool WillStop (); + virtual bool MischiefManaged (); + + virtual bool OkayToDiscard() + { + return false; + } + + virtual bool + IsBasePlan() + { + return true; + } + +protected: + virtual bool DoWillResume (lldb::StateType resume_state, bool current_plan); + virtual bool DoPlanExplainsStop (Event *event_ptr); + ThreadPlanBase (Thread &thread); + +private: + friend lldb::ThreadPlanSP + Thread::QueueFundamentalPlan(bool abort_other_plans); + + DISALLOW_COPY_AND_ASSIGN (ThreadPlanBase); +}; + + +} // namespace lldb_private + +#endif // liblldb_ThreadPlanFundamental_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Target/ThreadPlanCallFunction.h b/contrib/llvm/tools/lldb/include/lldb/Target/ThreadPlanCallFunction.h new file mode 100644 index 00000000000..d747706c639 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Target/ThreadPlanCallFunction.h @@ -0,0 +1,193 @@ +//===-- ThreadPlanCallFunction.h --------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ThreadPlanCallFunction_h_ +#define liblldb_ThreadPlanCallFunction_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private.h" +#include "lldb/Target/Thread.h" +#include "lldb/Target/ThreadPlan.h" + +namespace lldb_private { + +class ThreadPlanCallFunction : public ThreadPlan +{ + // Create a thread plan to call a function at the address passed in the "function" + // argument. If you plan to call GetReturnValueObject, then pass in the + // return type, otherwise just pass in an invalid ClangASTType. +public: + ThreadPlanCallFunction (Thread &thread, + const Address &function, + const ClangASTType &return_type, + lldb::addr_t arg, + bool stop_other_threads, + bool unwind_on_error = true, + bool ignore_breakpoints = false, + lldb::addr_t *this_arg = 0, + lldb::addr_t *cmd_arg = 0); + + ThreadPlanCallFunction (Thread &thread, + const Address &function, + const ClangASTType &return_type, + bool stop_other_threads, + bool unwind_on_error, + bool ignore_breakpoints, + lldb::addr_t *arg1_ptr = NULL, + lldb::addr_t *arg2_ptr = NULL, + lldb::addr_t *arg3_ptr = NULL, + lldb::addr_t *arg4_ptr = NULL, + lldb::addr_t *arg5_ptr = NULL, + lldb::addr_t *arg6_ptr = NULL); + + virtual + ~ThreadPlanCallFunction (); + + virtual void + GetDescription (Stream *s, lldb::DescriptionLevel level); + + virtual bool + ValidatePlan (Stream *error); + + virtual bool + ShouldStop (Event *event_ptr); + + virtual Vote + ShouldReportStop(Event *event_ptr); + + virtual bool + StopOthers (); + + virtual void + SetStopOthers (bool new_value); + + virtual lldb::StateType + GetPlanRunState (); + + virtual void + DidPush (); + + virtual bool + WillStop (); + + virtual bool + MischiefManaged (); + + // To get the return value from a function call you must create a + // lldb::ValueSP that contains a valid clang type in its context and call + // RequestReturnValue. The ValueSP will be stored and when the function is + // done executing, the object will check if there is a requested return + // value. If there is, the return value will be retrieved using the + // ABI::GetReturnValue() for the ABI in the process. Then after the thread + // plan is complete, you can call "GetReturnValue()" to retrieve the value + // that was extracted. + + virtual lldb::ValueObjectSP + GetReturnValueObject () + { + return m_return_valobj_sp; + } + + // Return the stack pointer that the function received + // on entry. Any stack address below this should be + // considered invalid after the function has been + // cleaned up. + lldb::addr_t + GetFunctionStackPointer() + { + return m_function_sp; + } + + // Classes that derive from ClangFunction, and implement + // their own WillPop methods should call this so that the + // thread state gets restored if the plan gets discarded. + virtual void + WillPop (); + + // If the thread plan stops mid-course, this will be the stop reason that interrupted us. + // Once DoTakedown is called, this will be the real stop reason at the end of the function call. + // If it hasn't been set for one or the other of these reasons, we'll return the PrivateStopReason. + // This is needed because we want the CallFunction thread plans not to show up as the stop reason. + // But if something bad goes wrong, it is nice to be able to tell the user what really happened. + + virtual lldb::StopInfoSP + GetRealStopInfo() + { + if (m_real_stop_info_sp) + return m_real_stop_info_sp; + else + return GetPrivateStopInfo (); + } + + lldb::addr_t + GetStopAddress () + { + return m_stop_address; + } + + virtual bool + RestoreThreadState(); + +protected: + void ReportRegisterState (const char *message); + + virtual bool + DoPlanExplainsStop (Event *event_ptr); + +private: + + bool + ConstructorSetup (Thread &thread, + ABI *& abi, + lldb::addr_t &start_load_addr, + lldb::addr_t &function_load_addr); + + void + DoTakedown (bool success); + + void + SetBreakpoints (); + + void + ClearBreakpoints (); + + bool + BreakpointsExplainStop (); + + bool m_valid; + bool m_stop_other_threads; + Address m_function_addr; + Address m_start_addr; + lldb::addr_t m_function_sp; + Thread::RegisterCheckpoint m_register_backup; + lldb::ThreadPlanSP m_subplan_sp; + LanguageRuntime *m_cxx_language_runtime; + LanguageRuntime *m_objc_language_runtime; + Thread::ThreadStateCheckpoint m_stored_thread_state; + lldb::StopInfoSP m_real_stop_info_sp; // In general we want to hide call function + // thread plans, but for reporting purposes, + // it's nice to know the real stop reason. + // This gets set in DoTakedown. + StreamString m_constructor_errors; + ClangASTType m_return_type; + lldb::ValueObjectSP m_return_valobj_sp; // If this contains a valid pointer, use the ABI to extract values when complete + bool m_takedown_done; // We want to ensure we only do the takedown once. This ensures that. + lldb::addr_t m_stop_address; // This is the address we stopped at. Also set in DoTakedown; + bool m_unwind_on_error; + bool m_ignore_breakpoints; + + DISALLOW_COPY_AND_ASSIGN (ThreadPlanCallFunction); +}; + +} // namespace lldb_private + +#endif // liblldb_ThreadPlanCallFunction_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Target/ThreadPlanCallUserExpression.h b/contrib/llvm/tools/lldb/include/lldb/Target/ThreadPlanCallUserExpression.h new file mode 100644 index 00000000000..7a7ec33049e --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Target/ThreadPlanCallUserExpression.h @@ -0,0 +1,65 @@ +//===-- ThreadPlanCallUserExpression.h --------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ThreadPlanCallUserExpression_h_ +#define liblldb_ThreadPlanCallUserExpression_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private.h" +#include "lldb/Expression/ClangUserExpression.h" +#include "lldb/Target/Thread.h" +#include "lldb/Target/ThreadPlan.h" +#include "lldb/Target/ThreadPlanCallFunction.h" + +namespace lldb_private { + +class ThreadPlanCallUserExpression : public ThreadPlanCallFunction +{ +public: + ThreadPlanCallUserExpression (Thread &thread, + Address &function, + lldb::addr_t arg, + bool stop_other_threads, + bool unwind_on_error, + bool ignore_breakpoints, + lldb::addr_t *this_arg, + lldb::addr_t *cmd_arg, + ClangUserExpression::ClangUserExpressionSP &user_expression_sp); + + virtual + ~ThreadPlanCallUserExpression (); + + virtual void + GetDescription (Stream *s, lldb::DescriptionLevel level); + + virtual void + WillPop () + { + ThreadPlanCallFunction::WillPop(); + if (m_user_expression_sp) + m_user_expression_sp.reset(); + } + + virtual lldb::StopInfoSP + GetRealStopInfo(); + +protected: +private: + ClangUserExpression::ClangUserExpressionSP m_user_expression_sp; // This is currently just used to ensure the + // User expression the initiated this ThreadPlan + // lives as long as the thread plan does. + DISALLOW_COPY_AND_ASSIGN (ThreadPlanCallUserExpression); +}; + +} // namespace lldb_private + +#endif // liblldb_ThreadPlanCallUserExpression_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Target/ThreadPlanRunToAddress.h b/contrib/llvm/tools/lldb/include/lldb/Target/ThreadPlanRunToAddress.h new file mode 100644 index 00000000000..d9482066801 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Target/ThreadPlanRunToAddress.h @@ -0,0 +1,85 @@ +//===-- ThreadPlanRunToAddress.h --------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ThreadPlanRunToAddress_h_ +#define liblldb_ThreadPlanRunToAddress_h_ + +// C Includes +// C++ Includes +#include + +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private.h" +#include "lldb/Target/ThreadPlan.h" + +namespace lldb_private { + +class ThreadPlanRunToAddress : public ThreadPlan +{ +public: + ThreadPlanRunToAddress (Thread &thread, + Address &address, + bool stop_others); + + ThreadPlanRunToAddress (Thread &thread, + lldb::addr_t address, + bool stop_others); + + ThreadPlanRunToAddress (Thread &thread, + const std::vector &addresses, + bool stop_others); + + + virtual + ~ThreadPlanRunToAddress (); + + virtual void + GetDescription (Stream *s, lldb::DescriptionLevel level); + + virtual bool + ValidatePlan (Stream *error); + + virtual bool + ShouldStop (Event *event_ptr); + + virtual bool + StopOthers (); + + virtual void + SetStopOthers (bool new_value); + + virtual lldb::StateType + GetPlanRunState (); + + virtual bool + WillStop (); + + virtual bool + MischiefManaged (); + +protected: + virtual bool + DoPlanExplainsStop (Event *event_ptr); + + void SetInitialBreakpoints(); + bool AtOurAddress(); +private: + bool m_stop_others; + std::vector m_addresses; // This is the address we are going to run to. + // TODO: Would it be useful to have multiple addresses? + std::vector m_break_ids; // This is the breakpoint we are using to stop us at m_address. + + DISALLOW_COPY_AND_ASSIGN (ThreadPlanRunToAddress); + +}; + +} // namespace lldb_private + +#endif // liblldb_ThreadPlanRunToAddress_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Target/ThreadPlanShouldStopHere.h b/contrib/llvm/tools/lldb/include/lldb/Target/ThreadPlanShouldStopHere.h new file mode 100644 index 00000000000..62068b78ae4 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Target/ThreadPlanShouldStopHere.h @@ -0,0 +1,94 @@ +//===-- ThreadPlanShouldStopHere.h ------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ThreadPlanShouldStopHere_h_ +#define liblldb_ThreadPlanShouldStopHere_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Target/ThreadPlan.h" + +namespace lldb_private { + +// This is an interface that ThreadPlans can adopt to allow flexible modifications of the behavior +// when a thread plan comes to a place where it would ordinarily stop. If such modification makes +// sense for your plan, inherit from this class, and when you would be about to stop (in your ShouldStop +// method), call InvokeShouldStopHereCallback, and if that returns a non-NULL plan, execute that +// plan instead of stopping. +// +// The classic example of the use of this is ThreadPlanStepInRange not stopping in frames that have +// no debug information. +// +// This class also defines a set of flags to control general aspects of this "ShouldStop" behavior. +// A class implementing this protocol needs to define a default set of flags, and can provide access to +// changing that default flag set if it wishes. + +class ThreadPlanShouldStopHere +{ +public: + enum + { + eNone = 0, + eAvoidInlines = (1 << 0), + eAvoidNoDebug = (1 << 1) + }; + + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + ThreadPlanShouldStopHere (ThreadPlan *owner, + ThreadPlanShouldStopHereCallback callback = NULL, + void *baton = NULL); + virtual + ~ThreadPlanShouldStopHere(); + + void + SetShouldStopHereCallback (ThreadPlanShouldStopHereCallback callback, void *baton); + + lldb::ThreadPlanSP + InvokeShouldStopHereCallback (); + + lldb_private::Flags & + GetFlags () + { + return m_flags; + } + + const lldb_private::Flags & + GetFlags () const + { + return m_flags; + } + +protected: + // Implement this, and call it in the plan's constructor to set the default flags. + virtual void SetFlagsToDefault () = 0; + + //------------------------------------------------------------------ + // Classes that inherit from ThreadPlanShouldStopHere can see and modify these + //------------------------------------------------------------------ + ThreadPlanShouldStopHereCallback m_callback; + void * m_baton; + ThreadPlan *m_owner; + lldb_private::Flags m_flags; + +private: + //------------------------------------------------------------------ + // For ThreadPlanShouldStopHere only + //------------------------------------------------------------------ + + DISALLOW_COPY_AND_ASSIGN (ThreadPlanShouldStopHere); + +}; + +} // namespace lldb_private + +#endif // liblldb_ThreadPlanShouldStopHere_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Target/ThreadPlanStepInRange.h b/contrib/llvm/tools/lldb/include/lldb/Target/ThreadPlanStepInRange.h new file mode 100644 index 00000000000..dbc8446b2e1 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Target/ThreadPlanStepInRange.h @@ -0,0 +1,110 @@ +//===-- ThreadPlanStepInRange.h ---------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ThreadPlanStepInRange_h_ +#define liblldb_ThreadPlanStepInRange_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/AddressRange.h" +#include "lldb/Target/StackID.h" +#include "lldb/Target/Thread.h" +#include "lldb/Target/ThreadPlanStepRange.h" +#include "lldb/Target/ThreadPlanShouldStopHere.h" + +namespace lldb_private { + +class ThreadPlanStepInRange : + public ThreadPlanStepRange, + public ThreadPlanShouldStopHere +{ +public: + ThreadPlanStepInRange (Thread &thread, + const AddressRange &range, + const SymbolContext &addr_context, + lldb::RunMode stop_others); + + ThreadPlanStepInRange (Thread &thread, + const AddressRange &range, + const SymbolContext &addr_context, + const char *step_into_function_name, + lldb::RunMode stop_others); + + virtual + ~ThreadPlanStepInRange (); + + virtual void + GetDescription (Stream *s, lldb::DescriptionLevel level); + + virtual bool + ShouldStop (Event *event_ptr); + + void SetAvoidRegexp(const char *name); + + void SetStepInTarget (const char *target) + { + m_step_into_target.SetCString(target); + } + + static lldb::ThreadPlanSP + DefaultShouldStopHereCallback (ThreadPlan *current_plan, Flags &flags, void *baton); + + static void + SetDefaultFlagValue (uint32_t new_value); + + bool + IsVirtualStep(); + +protected: + virtual bool DoWillResume (lldb::StateType resume_state, bool current_plan); + + virtual bool + DoPlanExplainsStop (Event *event_ptr); + + virtual void + SetFlagsToDefault (); + + bool + FrameMatchesAvoidRegexp (); + +private: + + friend lldb::ThreadPlanSP + Thread::QueueThreadPlanForStepOverRange (bool abort_other_plans, + const AddressRange &range, + const SymbolContext &addr_context, + lldb::RunMode stop_others); + friend lldb::ThreadPlanSP + Thread::QueueThreadPlanForStepInRange (bool abort_other_plans, + const AddressRange &range, + const SymbolContext &addr_context, + const char *step_in_target, + lldb::RunMode stop_others, + bool avoid_code_without_debug_info); + + + // Need an appropriate marker for the current stack so we can tell step out + // from step in. + + static uint32_t s_default_flag_values; + lldb::ThreadPlanSP m_sub_plan_sp; // Keep track of the last plan we were running. If it fails, we should stop. + std::unique_ptr m_avoid_regexp_ap; + bool m_step_past_prologue; // FIXME: For now hard-coded to true, we could put a switch in for this if there's + // demand for that. + bool m_virtual_step; // true if we've just done a "virtual step", i.e. just moved the inline stack depth. + ConstString m_step_into_target; + DISALLOW_COPY_AND_ASSIGN (ThreadPlanStepInRange); + +}; + +} // namespace lldb_private + +#endif // liblldb_ThreadPlanStepInRange_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Target/ThreadPlanStepInstruction.h b/contrib/llvm/tools/lldb/include/lldb/Target/ThreadPlanStepInstruction.h new file mode 100644 index 00000000000..eb4a64bcbc8 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Target/ThreadPlanStepInstruction.h @@ -0,0 +1,64 @@ +//===-- ThreadPlanStepInstruction.h -----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ThreadPlanStepInstruction_h_ +#define liblldb_ThreadPlanStepInstruction_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private.h" +#include "lldb/Target/Thread.h" +#include "lldb/Target/ThreadPlan.h" + +namespace lldb_private { + +class ThreadPlanStepInstruction : public ThreadPlan +{ +public: + virtual ~ThreadPlanStepInstruction (); + + virtual void GetDescription (Stream *s, lldb::DescriptionLevel level); + virtual bool ValidatePlan (Stream *error); + virtual bool ShouldStop (Event *event_ptr); + virtual bool StopOthers (); + virtual lldb::StateType GetPlanRunState (); + virtual bool WillStop (); + virtual bool MischiefManaged (); + +protected: + virtual bool DoPlanExplainsStop (Event *event_ptr); + + ThreadPlanStepInstruction (Thread &thread, + bool step_over, + bool stop_others, + Vote stop_vote, + Vote run_vote); + +private: + friend lldb::ThreadPlanSP + Thread::QueueThreadPlanForStepSingleInstruction (bool step_over, bool abort_other_plans, bool stop_other_threads); + + lldb::addr_t m_instruction_addr; + bool m_stop_other_threads; + bool m_step_over; + // These two are used only for the step over case. + bool m_start_has_symbol; + StackID m_stack_id; + StackID m_parent_frame_id; + + DISALLOW_COPY_AND_ASSIGN (ThreadPlanStepInstruction); + +}; + + +} // namespace lldb_private + +#endif // liblldb_ThreadPlanStepInstruction_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Target/ThreadPlanStepOut.h b/contrib/llvm/tools/lldb/include/lldb/Target/ThreadPlanStepOut.h new file mode 100644 index 00000000000..2737978a4ed --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Target/ThreadPlanStepOut.h @@ -0,0 +1,90 @@ +//===-- ThreadPlanStepOut.h -------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ThreadPlanStepOut_h_ +#define liblldb_ThreadPlanStepOut_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Target/Thread.h" +#include "lldb/Target/ThreadPlan.h" + +namespace lldb_private { + +class ThreadPlanStepOut : public ThreadPlan +{ +public: + ThreadPlanStepOut (Thread &thread, + SymbolContext *addr_context, + bool first_insn, + bool stop_others, + Vote stop_vote, + Vote run_vote, + uint32_t frame_idx); + + virtual ~ThreadPlanStepOut (); + + virtual void GetDescription (Stream *s, lldb::DescriptionLevel level); + virtual bool ValidatePlan (Stream *error); + virtual bool ShouldStop (Event *event_ptr); + virtual bool StopOthers (); + virtual lldb::StateType GetPlanRunState (); + virtual bool WillStop (); + virtual bool MischiefManaged (); + virtual void DidPush(); + virtual bool IsPlanStale(); + + virtual lldb::ValueObjectSP GetReturnValueObject() + { + return m_return_valobj_sp; + } + +protected: + virtual bool DoPlanExplainsStop (Event *event_ptr); + virtual bool DoWillResume (lldb::StateType resume_state, bool current_plan); + bool QueueInlinedStepPlan (bool queue_now); + +private: + SymbolContext *m_step_from_context; + lldb::addr_t m_step_from_insn; + StackID m_step_out_to_id; + StackID m_immediate_step_from_id; + lldb::break_id_t m_return_bp_id; + lldb::addr_t m_return_addr; + bool m_first_insn; + bool m_stop_others; + lldb::ThreadPlanSP m_step_through_inline_plan_sp; + lldb::ThreadPlanSP m_step_out_plan_sp; + Function *m_immediate_step_from_function; + lldb::ValueObjectSP m_return_valobj_sp; + + friend lldb::ThreadPlanSP + Thread::QueueThreadPlanForStepOut (bool abort_other_plans, + SymbolContext *addr_context, + bool first_insn, + bool stop_others, + Vote stop_vote, + Vote run_vote, + uint32_t frame_idx); + + // Need an appropriate marker for the current stack so we can tell step out + // from step in. + + void + CalculateReturnValue(); + + DISALLOW_COPY_AND_ASSIGN (ThreadPlanStepOut); + +}; + +} // namespace lldb_private + +#endif // liblldb_ThreadPlanStepOut_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Target/ThreadPlanStepOverBreakpoint.h b/contrib/llvm/tools/lldb/include/lldb/Target/ThreadPlanStepOverBreakpoint.h new file mode 100644 index 00000000000..41cac5c9b0b --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Target/ThreadPlanStepOverBreakpoint.h @@ -0,0 +1,57 @@ +//===-- ThreadPlanStepOverBreakpoint.h --------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ThreadPlanStepOverBreakpoint_h_ +#define liblldb_ThreadPlanStepOverBreakpoint_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Target/Thread.h" +#include "lldb/Target/ThreadPlan.h" + +namespace lldb_private { + +class ThreadPlanStepOverBreakpoint : public ThreadPlan +{ +public: + virtual ~ThreadPlanStepOverBreakpoint (); + + ThreadPlanStepOverBreakpoint (Thread &thread); + virtual void GetDescription (Stream *s, lldb::DescriptionLevel level); + virtual bool ValidatePlan (Stream *error); + virtual bool ShouldStop (Event *event_ptr); + virtual bool StopOthers (); + virtual lldb::StateType GetPlanRunState (); + virtual bool WillStop (); + virtual bool MischiefManaged (); + virtual void ThreadDestroyed (); + void SetAutoContinue (bool do_it); + virtual bool ShouldAutoContinue(Event *event_ptr); + +protected: + virtual bool DoPlanExplainsStop (Event *event_ptr); + virtual bool DoWillResume (lldb::StateType resume_state, bool current_plan); + + void ReenableBreakpointSite (); +private: + + lldb::addr_t m_breakpoint_addr; + lldb::user_id_t m_breakpoint_site_id; + bool m_auto_continue; + bool m_reenabled_breakpoint_site; + + DISALLOW_COPY_AND_ASSIGN (ThreadPlanStepOverBreakpoint); + +}; + +} // namespace lldb_private + +#endif // liblldb_ThreadPlanStepOverBreakpoint_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Target/ThreadPlanStepOverRange.h b/contrib/llvm/tools/lldb/include/lldb/Target/ThreadPlanStepOverRange.h new file mode 100644 index 00000000000..de9e66829dc --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Target/ThreadPlanStepOverRange.h @@ -0,0 +1,52 @@ +//===-- ThreadPlanStepOverRange.h -------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ThreadPlanStepOverRange_h_ +#define liblldb_ThreadPlanStepOverRange_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/AddressRange.h" +#include "lldb/Target/StackID.h" +#include "lldb/Target/Thread.h" +#include "lldb/Target/ThreadPlanStepRange.h" + +namespace lldb_private { + +class ThreadPlanStepOverRange : public ThreadPlanStepRange +{ +public: + + ThreadPlanStepOverRange (Thread &thread, + const AddressRange &range, + const SymbolContext &addr_context, + lldb::RunMode stop_others); + + virtual ~ThreadPlanStepOverRange (); + + virtual void GetDescription (Stream *s, lldb::DescriptionLevel level); + virtual bool ShouldStop (Event *event_ptr); + +protected: + virtual bool DoPlanExplainsStop (Event *event_ptr); + virtual bool DoWillResume (lldb::StateType resume_state, bool current_plan); + +private: + + bool m_first_resume; + + DISALLOW_COPY_AND_ASSIGN (ThreadPlanStepOverRange); + +}; + +} // namespace lldb_private + +#endif // liblldb_ThreadPlanStepOverRange_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Target/ThreadPlanStepRange.h b/contrib/llvm/tools/lldb/include/lldb/Target/ThreadPlanStepRange.h new file mode 100644 index 00000000000..486fd652839 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Target/ThreadPlanStepRange.h @@ -0,0 +1,94 @@ +//===-- ThreadPlanStepRange.h -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ThreadPlanStepRange_h_ +#define liblldb_ThreadPlanStepRange_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/AddressRange.h" +#include "lldb/Target/StackID.h" +#include "lldb/Target/Thread.h" +#include "lldb/Target/ThreadPlan.h" +#include "lldb/Target/ThreadPlanShouldStopHere.h" + +namespace lldb_private { + +class ThreadPlanStepRange : public ThreadPlan +{ +public: + ThreadPlanStepRange (ThreadPlanKind kind, + const char *name, + Thread &thread, + const AddressRange &range, + const SymbolContext &addr_context, + lldb::RunMode stop_others); + + virtual ~ThreadPlanStepRange (); + + virtual void GetDescription (Stream *s, lldb::DescriptionLevel level) = 0; + virtual bool ValidatePlan (Stream *error); + virtual bool ShouldStop (Event *event_ptr) = 0; + virtual Vote ShouldReportStop (Event *event_ptr); + virtual bool StopOthers (); + virtual lldb::StateType GetPlanRunState (); + virtual bool WillStop (); + virtual bool MischiefManaged (); + virtual void DidPush (); + virtual bool IsPlanStale (); + + + void AddRange(const AddressRange &new_range); + +protected: + + bool InRange(); + lldb::FrameComparison CompareCurrentFrameToStartFrame(); + bool InSymbol(); + void DumpRanges (Stream *s); + + Disassembler * + GetDisassembler (); + + InstructionList * + GetInstructionsForAddress(lldb::addr_t addr, size_t &range_index, size_t &insn_offset); + + // Pushes a plan to proceed through the next section of instructions in the range - usually just a RunToAddress + // plan to run to the next branch. Returns true if it pushed such a plan. If there was no available 'quick run' + // plan, then just single step. + bool + SetNextBranchBreakpoint (); + + void + ClearNextBranchBreakpoint(); + + bool + NextRangeBreakpointExplainsStop (lldb::StopInfoSP stop_info_sp); + + SymbolContext m_addr_context; + std::vector m_address_ranges; + lldb::RunMode m_stop_others; + StackID m_stack_id; // Use the stack ID so we can tell step out from step in. + bool m_no_more_plans; // Need this one so we can tell if we stepped into a call, + // but can't continue, in which case we are done. + bool m_first_run_event; // We want to broadcast only one running event, our first. + lldb::BreakpointSP m_next_branch_bp_sp; + bool m_use_fast_step; + +private: + std::vector m_instruction_ranges; + DISALLOW_COPY_AND_ASSIGN (ThreadPlanStepRange); + +}; + +} // namespace lldb_private + +#endif // liblldb_ThreadPlanStepRange_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Target/ThreadPlanStepThrough.h b/contrib/llvm/tools/lldb/include/lldb/Target/ThreadPlanStepThrough.h new file mode 100644 index 00000000000..16979663eb1 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Target/ThreadPlanStepThrough.h @@ -0,0 +1,71 @@ +//===-- ThreadPlanStepThrough.h ---------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ThreadPlanStepThrough_h_ +#define liblldb_ThreadPlanStepThrough_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Target/Thread.h" +#include "lldb/Target/ThreadPlan.h" + +namespace lldb_private { + +class ThreadPlanStepThrough : public ThreadPlan +{ +public: + virtual ~ThreadPlanStepThrough (); + + virtual void GetDescription (Stream *s, lldb::DescriptionLevel level); + virtual bool ValidatePlan (Stream *error); + virtual bool ShouldStop (Event *event_ptr); + virtual bool StopOthers (); + virtual lldb::StateType GetPlanRunState (); + virtual bool WillStop (); + virtual bool MischiefManaged (); + virtual void DidPush(); + +protected: + virtual bool DoPlanExplainsStop (Event *event_ptr); + virtual bool DoWillResume (lldb::StateType resume_state, bool current_plan); + + ThreadPlanStepThrough (Thread &thread, + StackID &return_stack_id, + bool stop_others); + + void + LookForPlanToStepThroughFromCurrentPC (); + + bool + HitOurBackstopBreakpoint(); + +private: + friend lldb::ThreadPlanSP + Thread::QueueThreadPlanForStepThrough (StackID &return_stack_id, + bool abort_other_plans, + bool stop_others); + + void ClearBackstopBreakpoint(); + + lldb::ThreadPlanSP m_sub_plan_sp; + lldb::addr_t m_start_address; + lldb::break_id_t m_backstop_bkpt_id; + lldb::addr_t m_backstop_addr; + StackID m_return_stack_id; + bool m_stop_others; + + DISALLOW_COPY_AND_ASSIGN (ThreadPlanStepThrough); + +}; + +} // namespace lldb_private + +#endif // liblldb_ThreadPlanStepThrough_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Target/ThreadPlanStepUntil.h b/contrib/llvm/tools/lldb/include/lldb/Target/ThreadPlanStepUntil.h new file mode 100644 index 00000000000..5aa3876df53 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Target/ThreadPlanStepUntil.h @@ -0,0 +1,80 @@ +//===-- ThreadPlanStepUntil.h -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ThreadPlanStepUntil_h_ +#define liblldb_ThreadPlanStepUntil_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Target/Thread.h" +#include "lldb/Target/ThreadPlan.h" + +namespace lldb_private { + + +class ThreadPlanStepUntil : public ThreadPlan +{ +public: + virtual ~ThreadPlanStepUntil (); + + virtual void GetDescription (Stream *s, lldb::DescriptionLevel level); + virtual bool ValidatePlan (Stream *error); + virtual bool ShouldStop (Event *event_ptr); + virtual bool StopOthers (); + virtual lldb::StateType GetPlanRunState (); + virtual bool WillStop (); + virtual bool MischiefManaged (); + +protected: + virtual bool DoWillResume (lldb::StateType resume_state, bool current_plan); + virtual bool DoPlanExplainsStop (Event *event_ptr); + + ThreadPlanStepUntil (Thread &thread, + lldb::addr_t *address_list, + size_t num_addresses, + bool stop_others, + uint32_t frame_idx = 0); + void AnalyzeStop(void); + +private: + + StackID m_stack_id; + lldb::addr_t m_step_from_insn; + lldb::break_id_t m_return_bp_id; + lldb::addr_t m_return_addr; + bool m_stepped_out; + bool m_should_stop; + bool m_ran_analyze; + bool m_explains_stop; + + typedef std::map until_collection; + until_collection m_until_points; + bool m_stop_others; + + void Clear(); + + friend lldb::ThreadPlanSP + Thread::QueueThreadPlanForStepUntil (bool abort_other_plans, + lldb::addr_t *address_list, + size_t num_addresses, + bool stop_others, + uint32_t frame_idx); + + // Need an appropriate marker for the current stack so we can tell step out + // from step in. + + DISALLOW_COPY_AND_ASSIGN (ThreadPlanStepUntil); + +}; + +} // namespace lldb_private + +#endif // liblldb_ThreadPlanStepUntil_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Target/ThreadPlanTracer.h b/contrib/llvm/tools/lldb/include/lldb/Target/ThreadPlanTracer.h new file mode 100644 index 00000000000..4eb0c783e57 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Target/ThreadPlanTracer.h @@ -0,0 +1,131 @@ +//===-- ThreadPlanTracer.h --------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ThreadPlanTracer_h_ +#define liblldb_ThreadPlanTracer_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private.h" +#include "lldb/Core/RegisterValue.h" +#include "lldb/Symbol/TaggedASTType.h" +#include "lldb/Target/Thread.h" + +namespace lldb_private { + +class ThreadPlanTracer +{ +friend class ThreadPlan; + +public: + + typedef enum ThreadPlanTracerStyle + { + eLocation = 0, + eStateChange, + eCheckFrames, + ePython + } ThreadPlanTracerStyle; + ThreadPlanTracer (Thread &thread, lldb::StreamSP &stream_sp); + ThreadPlanTracer (Thread &thread); + + virtual ~ThreadPlanTracer() + { + } + + virtual void TracingStarted () + { + + } + + virtual void TracingEnded () + { + + } + + bool + EnableTracing(bool value) + { + bool old_value = m_enabled; + m_enabled = value; + if (old_value == false && value == true) + TracingStarted(); + else if (old_value == true && value == false) + TracingEnded(); + + return old_value; + } + + bool + TracingEnabled() + { + return m_enabled; + } + + bool + EnableSingleStep (bool value) + { + bool old_value = m_single_step; + m_single_step = value; + return old_value; + } + + bool + SingleStepEnabled () + { + return m_single_step; + } + +protected: + Thread &m_thread; + + Stream * + GetLogStream (); + + + + virtual void Log(); + +private: + bool + TracerExplainsStop (); + + bool m_single_step; + bool m_enabled; + lldb::StreamSP m_stream_sp; +}; + +class ThreadPlanAssemblyTracer : public ThreadPlanTracer +{ +public: + ThreadPlanAssemblyTracer (Thread &thread, lldb::StreamSP &stream_sp); + ThreadPlanAssemblyTracer (Thread &thread); + virtual ~ThreadPlanAssemblyTracer (); + virtual void TracingStarted (); + virtual void TracingEnded (); + virtual void Log(); +private: + + Disassembler * + GetDisassembler (); + + TypeFromUser + GetIntPointerType(); + + lldb::DisassemblerSP m_disassembler_sp; + TypeFromUser m_intptr_type; + std::vector m_register_values; + lldb::DataBufferSP m_buffer_sp; +}; + +} // namespace lldb_private + +#endif // liblldb_ThreadPlanTracer_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Target/ThreadSpec.h b/contrib/llvm/tools/lldb/include/lldb/Target/ThreadSpec.h new file mode 100644 index 00000000000..e0d30934f37 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Target/ThreadSpec.h @@ -0,0 +1,155 @@ +//===-- ThreadSpec.h ------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ThreadSpec_h_ +#define liblldb_ThreadSpec_h_ + +#include +#include + +#include "lldb/lldb-private.h" + +namespace lldb_private { + +// Note: For now the thread spec has only fixed elements - +// Thread ID +// Thread Index +// Thread Name +// Thread Queue Name +// +// But if we need more generality, we can hang a key/value map off of this structure. +// That's why the thread matches spec test is done as a virtual method in Thread::MatchesSpec, +// since it is the native thread that would know how to interpret the keys. +// I was going to do the Queue Name this way out of sheer orneriness, but that seems a +// sufficiently general concept, so I put it in here on its own. + +class ThreadSpec +{ +public: + ThreadSpec (); + + ThreadSpec (const ThreadSpec &rhs); + + const ThreadSpec & + operator=(const ThreadSpec &rhs); + + void + SetIndex (uint32_t index) + { + m_index = index; + } + + void + SetTID (lldb::tid_t tid) + { + m_tid = tid; + } + + void + SetName (const char *name) + { + m_name = name; + } + + void + SetQueueName (const char *queue_name) + { + m_queue_name = queue_name; + } + + uint32_t + GetIndex () const + { + return m_index; + } + + lldb::tid_t + GetTID () const + { + return m_tid; + } + + const char * + GetName () const; + + const char * + GetQueueName () const; + + bool + TIDMatches (lldb::tid_t thread_id) const + { + if (m_tid == LLDB_INVALID_THREAD_ID || thread_id == LLDB_INVALID_THREAD_ID) + return true; + else + return thread_id == m_tid; + } + + bool + TIDMatches (Thread &thread) const; + + bool + IndexMatches (uint32_t index) const + { + if (m_index == UINT32_MAX || index == UINT32_MAX) + return true; + else + return index == m_index; + } + + bool + IndexMatches (Thread &thread) const; + + bool + NameMatches (const char *name) const + { + if (m_name.empty()) + return true; + else if (name == NULL) + return false; + else + return m_name == name; + } + + bool + NameMatches (Thread &thread) const; + + bool + QueueNameMatches (const char *queue_name) const + { + if (m_queue_name.empty()) + return true; + else if (queue_name == NULL) + return false; + else + return m_queue_name == queue_name; + } + + bool + QueueNameMatches (Thread &thread) const; + + bool + ThreadPassesBasicTests (Thread &thread) const; + + bool + HasSpecification () const; + + void + GetDescription (Stream *s, lldb::DescriptionLevel level) const; + +protected: +private: + uint32_t m_index; + lldb::tid_t m_tid; + std::string m_name; + std::string m_queue_name; +}; + +} // namespace lldb_private + +#endif // liblldb_ThreadSpec_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Target/UnixSignals.h b/contrib/llvm/tools/lldb/include/lldb/Target/UnixSignals.h new file mode 100644 index 00000000000..f47a90bbf54 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Target/UnixSignals.h @@ -0,0 +1,144 @@ +//===-- UnixSignals.h -------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef lldb_UnixSignals_h_ +#define lldb_UnixSignals_h_ + +// C Includes +// C++ Includes +#include +#include + +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private.h" +#include "lldb/Core/ConstString.h" + +namespace lldb_private +{ + +class UnixSignals +{ +public: + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + UnixSignals(); + + virtual + ~UnixSignals(); + + const char * + GetSignalAsCString (int32_t signo) const; + + bool + SignalIsValid (int32_t signo) const; + + int32_t + GetSignalNumberFromName (const char *name) const; + + const char * + GetSignalInfo (int32_t signo, + bool &should_suppress, + bool &should_stop, + bool &should_notify) const; + + bool + GetShouldSuppress (int32_t signo) const; + + bool + SetShouldSuppress (int32_t signo, + bool value); + + bool + SetShouldSuppress (const char *signal_name, + bool value); + + bool + GetShouldStop (int32_t signo) const; + + bool + SetShouldStop (int32_t signo, + bool value); + bool + SetShouldStop (const char *signal_name, + bool value); + + bool + GetShouldNotify (int32_t signo) const; + + bool + SetShouldNotify (int32_t signo, bool value); + + bool + SetShouldNotify (const char *signal_name, + bool value); + + // These provide an iterator through the signals available on this system. + // Call GetFirstSignalNumber to get the first entry, then iterate on GetNextSignalNumber + // till you get back LLDB_INVALID_SIGNAL_NUMBER. + int32_t + GetFirstSignalNumber () const; + + int32_t + GetNextSignalNumber (int32_t current_signal) const; + + // We assume that the elements of this object are constant once it is constructed, + // since a process should never need to add or remove symbols as it runs. So don't + // call these functions anywhere but the constructor of your subclass of UnixSignals or in + // your Process Plugin's GetUnixSignals method before you return the UnixSignal object. + + void + AddSignal (int signo, + const char *name, + const char *short_name, + bool default_suppress, + bool default_stop, + bool default_notify, + const char *description); + + void + RemoveSignal (int signo); + +protected: + //------------------------------------------------------------------ + // Classes that inherit from UnixSignals can see and modify these + //------------------------------------------------------------------ + + struct Signal + { + ConstString m_name; + ConstString m_short_name; + std::string m_description; + bool m_suppress:1, + m_stop:1, + m_notify:1; + + Signal (const char *name, + const char *short_name, + bool default_suppress, + bool default_stop, + bool default_notify, + const char *description); + + ~Signal () {} + }; + + void + Reset (); + + typedef std::map collection; + + collection m_signals; + + DISALLOW_COPY_AND_ASSIGN (UnixSignals); +}; + +} // Namespace lldb +#endif // lldb_UnixSignals_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Target/Unwind.h b/contrib/llvm/tools/lldb/include/lldb/Target/Unwind.h new file mode 100644 index 00000000000..7cda4aeb2e1 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Target/Unwind.h @@ -0,0 +1,120 @@ +//===-- Unwind.h ------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_Unwind_h_ +#define liblldb_Unwind_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private.h" +#include "lldb/Host/Mutex.h" + +namespace lldb_private { + +class Unwind +{ +protected: + //------------------------------------------------------------------ + // Classes that inherit from Unwind can see and modify these + //------------------------------------------------------------------ + Unwind(Thread &thread) : + m_thread (thread), + m_unwind_mutex() + { + } + +public: + virtual + ~Unwind() + { + } + + void + Clear() + { + Mutex::Locker locker(m_unwind_mutex); + DoClear(); + + } + + uint32_t + GetFrameCount() + { + Mutex::Locker locker(m_unwind_mutex); + return DoGetFrameCount(); + } + + uint32_t + GetFramesUpTo (uint32_t end_idx) + { + lldb::addr_t cfa; + lldb::addr_t pc; + uint32_t idx; + + for (idx = 0; idx < end_idx; idx++) + { + if (!DoGetFrameInfoAtIndex (idx, cfa, pc)) + { + break; + } + } + return idx; + } + + bool + GetFrameInfoAtIndex (uint32_t frame_idx, + lldb::addr_t& cfa, + lldb::addr_t& pc) + { + Mutex::Locker locker(m_unwind_mutex); + return DoGetFrameInfoAtIndex (frame_idx, cfa, pc); + } + + lldb::RegisterContextSP + CreateRegisterContextForFrame (StackFrame *frame) + { + Mutex::Locker locker(m_unwind_mutex); + return DoCreateRegisterContextForFrame (frame); + } + + Thread & + GetThread() + { + return m_thread; + } + +protected: + //------------------------------------------------------------------ + // Classes that inherit from Unwind can see and modify these + //------------------------------------------------------------------ + virtual void + DoClear() = 0; + + virtual uint32_t + DoGetFrameCount() = 0; + + virtual bool + DoGetFrameInfoAtIndex (uint32_t frame_idx, + lldb::addr_t& cfa, + lldb::addr_t& pc) = 0; + + virtual lldb::RegisterContextSP + DoCreateRegisterContextForFrame (StackFrame *frame) = 0; + + Thread &m_thread; + Mutex m_unwind_mutex; +private: + DISALLOW_COPY_AND_ASSIGN (Unwind); +}; + +} // namespace lldb_private + +#endif // liblldb_Unwind_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Target/UnwindAssembly.h b/contrib/llvm/tools/lldb/include/lldb/Target/UnwindAssembly.h new file mode 100644 index 00000000000..6a4ae0c30f2 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Target/UnwindAssembly.h @@ -0,0 +1,58 @@ +//===-- UnwindAssembly.h --------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef utility_UnwindAssembly_h_ +#define utility_UnwindAssembly_h_ + +#include "lldb/lldb-private.h" +#include "lldb/Core/ArchSpec.h" +#include "lldb/Core/PluginInterface.h" + +namespace lldb_private { + +class UnwindAssembly : + public PluginInterface +{ +public: + static UnwindAssembly* + FindPlugin (const ArchSpec &arch); + + virtual + ~UnwindAssembly(); + + virtual bool + GetNonCallSiteUnwindPlanFromAssembly (AddressRange& func, + Thread& thread, + UnwindPlan& unwind_plan) = 0; + + virtual bool + GetFastUnwindPlan (AddressRange& func, + Thread& thread, + UnwindPlan &unwind_plan) = 0; + + // thread may be NULL in which case we only use the Target (e.g. if this is called pre-process-launch). + virtual bool + FirstNonPrologueInsn (AddressRange& func, + const lldb_private::ExecutionContext &exe_ctx, + Address& first_non_prologue_insn) = 0; + +protected: + UnwindAssembly (const ArchSpec &arch); + ArchSpec m_arch; + +private: + UnwindAssembly(); // Outlaw default constructor + DISALLOW_COPY_AND_ASSIGN (UnwindAssembly); +}; + +} // namespace lldb_private + +#endif //utility_UnwindAssembly_h_ + + diff --git a/contrib/llvm/tools/lldb/include/lldb/Utility/AnsiTerminal.h b/contrib/llvm/tools/lldb/include/lldb/Utility/AnsiTerminal.h new file mode 100644 index 00000000000..036950c1bd4 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Utility/AnsiTerminal.h @@ -0,0 +1,156 @@ +//===---------------------AnsiTerminal.h ------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + + + +#define ANSI_FG_COLOR_BLACK 30 +#define ANSI_FG_COLOR_RED 31 +#define ANSI_FG_COLOR_GREEN 32 +#define ANSI_FG_COLOR_YELLOW 33 +#define ANSI_FG_COLOR_BLUE 34 +#define ANSI_FG_COLOR_PURPLE 35 +#define ANSI_FG_COLOR_CYAN 36 +#define ANSI_FG_COLOR_WHITE 37 + +#define ANSI_BG_COLOR_BLACK 40 +#define ANSI_BG_COLOR_RED 41 +#define ANSI_BG_COLOR_GREEN 42 +#define ANSI_BG_COLOR_YELLOW 43 +#define ANSI_BG_COLOR_BLUE 44 +#define ANSI_BG_COLOR_PURPLE 45 +#define ANSI_BG_COLOR_CYAN 46 +#define ANSI_BG_COLOR_WHITE 47 + +#define ANSI_SPECIAL_FRAMED 51 +#define ANSI_SPECIAL_ENCIRCLED 52 + +#define ANSI_CTRL_NORMAL 0 +#define ANSI_CTRL_BOLD 1 +#define ANSI_CTRL_FAINT 2 +#define ANSI_CTRL_ITALIC 3 +#define ANSI_CTRL_UNDERLINE 4 +#define ANSI_CTRL_SLOW_BLINK 5 +#define ANSI_CTRL_FAST_BLINK 6 +#define ANSI_CTRL_IMAGE_NEGATIVE 7 +#define ANSI_CTRL_CONCEAL 8 +#define ANSI_CTRL_CROSSED_OUT 9 + +#define ANSI_ESC_START "\033[" +#define ANSI_ESC_END "m" + +#define ANSI_1_CTRL(ctrl1) "\033["##ctrl1 ANSI_ESC_END +#define ANSI_2_CTRL(ctrl1,ctrl2) "\033["##ctrl1";"##ctrl2 ANSI_ESC_END + +namespace lldb_utility { + + namespace ansi { + const char *k_escape_start = "\033["; + const char *k_escape_end = "m"; + + const char *k_fg_black = "30"; + const char *k_fg_red = "31"; + const char *k_fg_green = "32"; + const char *k_fg_yellow = "33"; + const char *k_fg_blue = "34"; + const char *k_fg_purple = "35"; + const char *k_fg_cyan = "36"; + const char *k_fg_white = "37"; + + const char *k_bg_black = "40"; + const char *k_bg_red = "41"; + const char *k_bg_green = "42"; + const char *k_bg_yellow = "43"; + const char *k_bg_blue = "44"; + const char *k_bg_purple = "45"; + const char *k_bg_cyan = "46"; + const char *k_bg_white = "47"; + + const char *k_ctrl_normal = "0"; + const char *k_ctrl_bold = "1"; + const char *k_ctrl_faint = "2"; + const char *k_ctrl_italic = "3"; + const char *k_ctrl_underline = "4"; + const char *k_ctrl_slow_blink = "5"; + const char *k_ctrl_fast_blink = "6"; + const char *k_ctrl_negative = "7"; + const char *k_ctrl_conceal = "8"; + const char *k_ctrl_crossed_out = "9"; + + inline std::string + FormatAnsiTerminalCodes(const char *format, bool do_color = true) + { + // Convert "${ansi.XXX}" tokens to ansi values or clear them if do_color is false. + static const struct + { + const char *name; + const char *value; + } g_color_tokens[] = + { + #define _TO_STR2(_val) #_val + #define _TO_STR(_val) _TO_STR2(_val) + { "fg.black}", ANSI_ESC_START _TO_STR(ANSI_FG_COLOR_BLACK) ANSI_ESC_END }, + { "fg.red}", ANSI_ESC_START _TO_STR(ANSI_FG_COLOR_RED) ANSI_ESC_END }, + { "fg.green}", ANSI_ESC_START _TO_STR(ANSI_FG_COLOR_GREEN) ANSI_ESC_END }, + { "fg.yellow}", ANSI_ESC_START _TO_STR(ANSI_FG_COLOR_YELLOW) ANSI_ESC_END }, + { "fg.blue}", ANSI_ESC_START _TO_STR(ANSI_FG_COLOR_BLUE) ANSI_ESC_END }, + { "fg.purple}", ANSI_ESC_START _TO_STR(ANSI_FG_COLOR_PURPLE) ANSI_ESC_END }, + { "fg.cyan}", ANSI_ESC_START _TO_STR(ANSI_FG_COLOR_CYAN) ANSI_ESC_END }, + { "fg.white}", ANSI_ESC_START _TO_STR(ANSI_FG_COLOR_WHITE) ANSI_ESC_END }, + { "bg.black}", ANSI_ESC_START _TO_STR(ANSI_BG_COLOR_BLACK) ANSI_ESC_END }, + { "bg.red}", ANSI_ESC_START _TO_STR(ANSI_BG_COLOR_RED) ANSI_ESC_END }, + { "bg.green}", ANSI_ESC_START _TO_STR(ANSI_BG_COLOR_GREEN) ANSI_ESC_END }, + { "bg.yellow}", ANSI_ESC_START _TO_STR(ANSI_BG_COLOR_YELLOW) ANSI_ESC_END }, + { "bg.blue}", ANSI_ESC_START _TO_STR(ANSI_BG_COLOR_BLUE) ANSI_ESC_END }, + { "bg.purple}", ANSI_ESC_START _TO_STR(ANSI_BG_COLOR_PURPLE) ANSI_ESC_END }, + { "bg.cyan}", ANSI_ESC_START _TO_STR(ANSI_BG_COLOR_CYAN) ANSI_ESC_END }, + { "bg.white}", ANSI_ESC_START _TO_STR(ANSI_BG_COLOR_WHITE) ANSI_ESC_END }, + { "normal}", ANSI_ESC_START _TO_STR(ANSI_CTRL_NORMAL) ANSI_ESC_END }, + { "bold}", ANSI_ESC_START _TO_STR(ANSI_CTRL_BOLD) ANSI_ESC_END }, + { "faint}", ANSI_ESC_START _TO_STR(ANSI_CTRL_FAINT) ANSI_ESC_END }, + { "italic}", ANSI_ESC_START _TO_STR(ANSI_CTRL_ITALIC) ANSI_ESC_END }, + { "underline}", ANSI_ESC_START _TO_STR(ANSI_CTRL_UNDERLINE) ANSI_ESC_END }, + { "slow-blink}", ANSI_ESC_START _TO_STR(ANSI_CTRL_SLOW_BLINK) ANSI_ESC_END }, + { "fast-blink}", ANSI_ESC_START _TO_STR(ANSI_CTRL_FAST_BLINK) ANSI_ESC_END }, + { "negative}", ANSI_ESC_START _TO_STR(ANSI_CTRL_IMAGE_NEGATIVE) ANSI_ESC_END }, + { "conceal}", ANSI_ESC_START _TO_STR(ANSI_CTRL_CONCEAL) ANSI_ESC_END }, + { "crossed-out}", ANSI_ESC_START _TO_STR(ANSI_CTRL_CROSSED_OUT) ANSI_ESC_END }, + #undef _TO_STR + #undef _TO_STR2 + }; + static const char tok_hdr[] = "${ansi."; + + std::string fmt; + for (const char *p = format; *p; ++p) + { + const char *tok_start = strstr (p, tok_hdr); + if (!tok_start) + { + fmt.append (p, strlen(p)); + break; + } + + fmt.append (p, tok_start - p); + p = tok_start; + + const char *tok_str = tok_start + sizeof(tok_hdr) - 1; + for (size_t i = 0; i < sizeof(g_color_tokens) / sizeof(g_color_tokens[0]); ++i) + { + if (!strncmp (tok_str, g_color_tokens[i].name, strlen(g_color_tokens[i].name))) + { + if (do_color) + fmt.append (g_color_tokens[i].value); + p = tok_str + strlen (g_color_tokens[i].name) - 1; + break; + } + } + } + return fmt; + } + } +} diff --git a/contrib/llvm/tools/lldb/include/lldb/Utility/CleanUp.h b/contrib/llvm/tools/lldb/include/lldb/Utility/CleanUp.h new file mode 100644 index 00000000000..ab15d1999b7 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Utility/CleanUp.h @@ -0,0 +1,322 @@ +//===-- CleanUp.h -----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_CleanUp_h_ +#define liblldb_CleanUp_h_ + +#include "lldb/lldb-public.h" + +namespace lldb_utility { + +//---------------------------------------------------------------------- +// Templated class that guarantees that a cleanup callback function will +// be called. The cleanup function will be called once under the +// following conditions: +// - when the object goes out of scope +// - when the user explicitly calls clean. +// - the current value will be cleaned up when a new value is set using +// set(T value) as long as the current value hasn't already been cleaned. +// +// This class is designed to be used with simple types for type T (like +// file descriptors, opaque handles, pointers, etc). If more complex +// type T objects are desired, we need to probably specialize this class +// to take "const T&" for all input T parameters. Yet if a type T is +// complex already it might be better to build the cleanup funcionality +// into T. +// +// The cleanup function must take one argument that is of type T. +// The calback function return type is R. The return value is currently +// needed for "CallbackType". If there is an easy way to get around the +// need for the return value we can change this class. +// +// The two template parameters are: +// T - The variable type of value that will be stored and used as the +// sole argument for the cleanup callback. +// R - The return type for the cleanup function. +// +// EXAMPLES +// // Use with file handles that get opened where you want to close +// // them. Below we use "int open(const char *path, int oflag, ...)" +// // which returns an integer file descriptor. -1 is the invalid file +// // descriptor so to make an object that will call "int close(int fd)" +// // automatically we can use: +// +// CleanUp fd(open("/tmp/a.txt", O_RDONLY, 0), -1, close); +// +// // malloc/free example +// CleanUp malloced_bytes(malloc(32), NULL, free); +//---------------------------------------------------------------------- +template +class CleanUp +{ +public: + typedef T value_type; + typedef R (*CallbackType)(value_type); + + //---------------------------------------------------------------------- + // Constructor that sets the current value only. No values are + // considered to be invalid and the cleanup function will be called + // regardless of the value of m_current_value. + //---------------------------------------------------------------------- + CleanUp (value_type value, CallbackType callback) : + m_current_value (value), + m_invalid_value (), + m_callback (callback), + m_callback_called (false), + m_invalid_value_is_valid (false) + { + } + + //---------------------------------------------------------------------- + // Constructor that sets the current value and also the invalid value. + // The cleanup function will be called on "m_value" as long as it isn't + // equal to "m_invalid_value". + //---------------------------------------------------------------------- + CleanUp (value_type value, value_type invalid, CallbackType callback) : + m_current_value (value), + m_invalid_value (invalid), + m_callback (callback), + m_callback_called (false), + m_invalid_value_is_valid (true) + { + } + + //---------------------------------------------------------------------- + // Automatically cleanup when this object goes out of scope. + //---------------------------------------------------------------------- + ~CleanUp () + { + clean(); + } + + //---------------------------------------------------------------------- + // Access the value stored in this class + //---------------------------------------------------------------------- + value_type get() + { + return m_current_value; + } + + //---------------------------------------------------------------------- + // Access the value stored in this class + //---------------------------------------------------------------------- + const value_type + get() const + { + return m_current_value; + } + + //---------------------------------------------------------------------- + // Reset the owned value to "value". If a current value is valid and + // the cleanup callback hasn't been called, the previous value will + // be cleaned up (see void CleanUp::clean()). + //---------------------------------------------------------------------- + void + set (const value_type value) + { + // Cleanup the current value if needed + clean (); + // Now set the new value and mark our callback as not called + m_callback_called = false; + m_current_value = value; + } + + //---------------------------------------------------------------------- + // Checks is "m_current_value" is valid. The value is considered valid + // no invalid value was supplied during construction of this object or + // if an invalid value was supplied and "m_current_value" is not equal + // to "m_invalid_value". + // + // Returns true if "m_current_value" is valid, false otherwise. + //---------------------------------------------------------------------- + bool + is_valid() const + { + if (m_invalid_value_is_valid) + return m_current_value != m_invalid_value; + return true; + } + + //---------------------------------------------------------------------- + // This function will call the cleanup callback provided in the + // constructor one time if the value is considered valid (See is_valid()). + // This function sets m_callback_called to true so we don't call the + // cleanup callback multiple times on the same value. + //---------------------------------------------------------------------- + void + clean() + { + if (m_callback && !m_callback_called) + { + m_callback_called = true; + if (is_valid()) + m_callback(m_current_value); + } + } + + //---------------------------------------------------------------------- + // Cancels the cleanup that would have been called on "m_current_value" + // if it was valid. This function can be used to release the value + // contained in this object so ownership can be transfered to the caller. + //---------------------------------------------------------------------- + value_type + release () + { + m_callback_called = true; + return m_current_value; + } + +private: + value_type m_current_value; + const value_type m_invalid_value; + CallbackType m_callback; + bool m_callback_called; + bool m_invalid_value_is_valid; + + // Outlaw default constructor, copy constructor and the assignment operator + DISALLOW_COPY_AND_ASSIGN (CleanUp); +}; + +template +class CleanUp2 +{ +public: + typedef T value_type; + typedef R (*CallbackType)(value_type, A0); + + //---------------------------------------------------------------------- + // Constructor that sets the current value only. No values are + // considered to be invalid and the cleanup function will be called + // regardless of the value of m_current_value. + //---------------------------------------------------------------------- + CleanUp2 (value_type value, CallbackType callback, A0 arg) : + m_current_value (value), + m_invalid_value (), + m_callback (callback), + m_callback_called (false), + m_invalid_value_is_valid (false), + m_argument(arg) + { + } + + //---------------------------------------------------------------------- + // Constructor that sets the current value and also the invalid value. + // The cleanup function will be called on "m_value" as long as it isn't + // equal to "m_invalid_value". + //---------------------------------------------------------------------- + CleanUp2 (value_type value, value_type invalid, CallbackType callback, A0 arg) : + m_current_value (value), + m_invalid_value (invalid), + m_callback (callback), + m_callback_called (false), + m_invalid_value_is_valid (true), + m_argument(arg) + { + } + + //---------------------------------------------------------------------- + // Automatically cleanup when this object goes out of scope. + //---------------------------------------------------------------------- + ~CleanUp2 () + { + clean(); + } + + //---------------------------------------------------------------------- + // Access the value stored in this class + //---------------------------------------------------------------------- + value_type get() + { + return m_current_value; + } + + //---------------------------------------------------------------------- + // Access the value stored in this class + //---------------------------------------------------------------------- + const value_type + get() const + { + return m_current_value; + } + + //---------------------------------------------------------------------- + // Reset the owned value to "value". If a current value is valid and + // the cleanup callback hasn't been called, the previous value will + // be cleaned up (see void CleanUp::clean()). + //---------------------------------------------------------------------- + void + set (const value_type value) + { + // Cleanup the current value if needed + clean (); + // Now set the new value and mark our callback as not called + m_callback_called = false; + m_current_value = value; + } + + //---------------------------------------------------------------------- + // Checks is "m_current_value" is valid. The value is considered valid + // no invalid value was supplied during construction of this object or + // if an invalid value was supplied and "m_current_value" is not equal + // to "m_invalid_value". + // + // Returns true if "m_current_value" is valid, false otherwise. + //---------------------------------------------------------------------- + bool + is_valid() const + { + if (m_invalid_value_is_valid) + return m_current_value != m_invalid_value; + return true; + } + + //---------------------------------------------------------------------- + // This function will call the cleanup callback provided in the + // constructor one time if the value is considered valid (See is_valid()). + // This function sets m_callback_called to true so we don't call the + // cleanup callback multiple times on the same value. + //---------------------------------------------------------------------- + void + clean() + { + if (m_callback && !m_callback_called) + { + m_callback_called = true; + if (is_valid()) + m_callback(m_current_value, m_argument); + } + } + + //---------------------------------------------------------------------- + // Cancels the cleanup that would have been called on "m_current_value" + // if it was valid. This function can be used to release the value + // contained in this object so ownership can be transfered to the caller. + //---------------------------------------------------------------------- + value_type + release () + { + m_callback_called = true; + return m_current_value; + } + +private: + value_type m_current_value; + const value_type m_invalid_value; + CallbackType m_callback; + bool m_callback_called; + bool m_invalid_value_is_valid; + A0 m_argument; + + // Outlaw default constructor, copy constructor and the assignment operator + DISALLOW_COPY_AND_ASSIGN (CleanUp2); +}; + +} // namespace lldb_utility + +#endif // #ifndef liblldb_CleanUp_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Utility/PriorityPointerPair.h b/contrib/llvm/tools/lldb/include/lldb/Utility/PriorityPointerPair.h new file mode 100644 index 00000000000..49f0765d0f9 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Utility/PriorityPointerPair.h @@ -0,0 +1,150 @@ +//===-- PriorityPointerPair.h ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_PriorityPointerPair_h_ +#define liblldb_PriorityPointerPair_h_ + +#include "lldb/lldb-public.h" +#include "lldb/Utility/SharingPtr.h" + +namespace lldb_utility { + +//---------------------------------------------------------------------- +// A prioritized pair of SharedPtr. One of the two pointers is high +// priority, the other is low priority. +// The Get() method always returns high, if *high != NULL, +// otherwise, low is returned (even if *low == NULL) +//---------------------------------------------------------------------- + +template +class PriorityPointerPair +{ +public: + + typedef T& reference_type; + typedef T* pointer_type; + + typedef typename std::shared_ptr T_SP; + + PriorityPointerPair() : + m_high(), + m_low() + {} + + PriorityPointerPair(pointer_type high, + pointer_type low) : + m_high(high), + m_low(low) + {} + + PriorityPointerPair(pointer_type low) : + m_high(), + m_low(low) + {} + + PriorityPointerPair(T_SP& high, + T_SP& low) : + m_high(high), + m_low(low) + {} + + PriorityPointerPair(T_SP& low) : + m_high(), + m_low(low) + {} + + void + SwapLow(pointer_type l) + { + m_low.swap(l); + } + + void + SwapHigh(pointer_type h) + { + m_high.swap(h); + } + + void + SwapLow(T_SP l) + { + m_low.swap(l); + } + + void + SwapHigh(T_SP h) + { + m_high.swap(h); + } + + T_SP + GetLow() + { + return m_low; + } + + T_SP + GetHigh() + { + return m_high; + } + + T_SP + Get() + { + if (m_high.get()) + return m_high; + return m_low; + } + + void + ResetHigh() + { + m_high.reset(); + } + + void + ResetLow() + { + m_low.reset(); + } + + void + Reset() + { + ResetLow(); + ResetHigh(); + } + + reference_type + operator*() const + { + return Get().operator*(); + } + + pointer_type + operator->() const + { + return Get().operator->(); + } + + ~PriorityPointerPair(); + +private: + + T_SP m_high; + T_SP m_low; + + DISALLOW_COPY_AND_ASSIGN (PriorityPointerPair); + +}; + +} // namespace lldb_utility + +#endif // #ifndef liblldb_PriorityPointerPair_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Utility/PseudoTerminal.h b/contrib/llvm/tools/lldb/include/lldb/Utility/PseudoTerminal.h new file mode 100644 index 00000000000..c79800fab75 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Utility/PseudoTerminal.h @@ -0,0 +1,266 @@ +//===-- PseudoTerminal.h ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_PseudoTerminal_h_ +#define liblldb_PseudoTerminal_h_ +#if defined(__cplusplus) + + +#include +#include + +#include "lldb/lldb-defines.h" + +namespace lldb_utility { + +//---------------------------------------------------------------------- +/// @class PseudoTerminal PseudoTerminal.h "lldb/Core/PseudoTerminal.h" +/// @brief A pseudo terminal helper class. +/// +/// The pseudo terminal class abtracts the use of pseudo terminals on +/// the host system. +//---------------------------------------------------------------------- +class PseudoTerminal +{ +public: + enum + { + invalid_fd = -1 ///< Invalid file descriptor value + }; + + //------------------------------------------------------------------ + /// Default constructor + /// + /// Constructs this object with invalid master and slave file + /// descriptors. + //------------------------------------------------------------------ + PseudoTerminal (); + + //------------------------------------------------------------------ + /// Destructor + /// + /// The destructor will close the master and slave file descriptors + /// if they are valid and ownwership has not been released using + /// one of: + /// @li PseudoTerminal::ReleaseMasterFileDescriptor() + /// @li PseudoTerminal::ReleaseSaveFileDescriptor() + //------------------------------------------------------------------ + ~PseudoTerminal (); + + //------------------------------------------------------------------ + /// Close the master file descriptor if it is valid. + //------------------------------------------------------------------ + void + CloseMasterFileDescriptor (); + + //------------------------------------------------------------------ + /// Close the slave file descriptor if it is valid. + //------------------------------------------------------------------ + void + CloseSlaveFileDescriptor (); + + //------------------------------------------------------------------ + /// Fork a child process that uses pseudo terminals for its stdio. + /// + /// In the parent process, a call to this function results in a pid + /// being returned. If the pid is valid, the master file descriptor + /// can be used for read/write access to stdio of the child process. + /// + /// In the child process the stdin/stdout/stderr will already be + /// routed to the slave pseudo terminal and the master file + /// descriptor will be closed as it is no longer needed by the child + /// process. + /// + /// This class will close the file descriptors for the master/slave + /// when the destructor is called. The file handles can be released + /// using either: + /// @li PseudoTerminal::ReleaseMasterFileDescriptor() + /// @li PseudoTerminal::ReleaseSaveFileDescriptor() + /// + /// @param[out] error + /// An pointer to an error that can describe any errors that + /// occur. This can be NULL if no error status is desired. + /// + /// @return + /// @li \b Parent process: a child process ID that is greater + /// than zero, or -1 if the fork fails. + /// @li \b Child process: zero. + //------------------------------------------------------------------ + lldb::pid_t + Fork (char *error_str, size_t error_len); + + //------------------------------------------------------------------ + /// The master file descriptor accessor. + /// + /// This object retains ownership of the master file descriptor when + /// this accessor is used. Users can call the member function + /// PseudoTerminal::ReleaseMasterFileDescriptor() if this + /// object should release ownership of the slave file descriptor. + /// + /// @return + /// The master file descriptor, or PseudoTerminal::invalid_fd + /// if the master file descriptor is not currently valid. + /// + /// @see PseudoTerminal::ReleaseMasterFileDescriptor() + //------------------------------------------------------------------ + int + GetMasterFileDescriptor () const; + + //------------------------------------------------------------------ + /// The slave file descriptor accessor. + /// + /// This object retains ownership of the slave file descriptor when + /// this accessor is used. Users can call the member function + /// PseudoTerminal::ReleaseSlaveFileDescriptor() if this + /// object should release ownership of the slave file descriptor. + /// + /// @return + /// The slave file descriptor, or PseudoTerminal::invalid_fd + /// if the slave file descriptor is not currently valid. + /// + /// @see PseudoTerminal::ReleaseSlaveFileDescriptor() + //------------------------------------------------------------------ + int + GetSlaveFileDescriptor () const; + + //------------------------------------------------------------------ + /// Get the name of the slave pseudo terminal. + /// + /// A master pseudo terminal should already be valid prior to + /// calling this function. + /// + /// @param[out] error + /// An pointer to an error that can describe any errors that + /// occur. This can be NULL if no error status is desired. + /// + /// @return + /// The name of the slave pseudo terminal as a NULL terminated + /// C. This string that comes from static memory, so a copy of + /// the string should be made as subsequent calls can change + /// this value. NULL is returned if this object doesn't have + /// a valid master pseudo terminal opened or if the call to + /// \c ptsname() fails. + /// + /// @see PseudoTerminal::OpenFirstAvailableMaster() + //------------------------------------------------------------------ + const char* + GetSlaveName (char *error_str, size_t error_len) const; + + //------------------------------------------------------------------ + /// Open the first available pseudo terminal. + /// + /// Opens the first available pseudo terminal with \a oflag as the + /// permissions. The opened master file descriptor is stored in this + /// object and can be accessed by calling the + /// PseudoTerminal::GetMasterFileDescriptor() accessor. Clients + /// can call the PseudoTerminal::ReleaseMasterFileDescriptor() + /// accessor function if they wish to use the master file descriptor + /// beyond the lifespan of this object. + /// + /// If this object still has a valid master file descriptor when its + /// destructor is called, it will close it. + /// + /// @param[in] oflag + /// Flags to use when calling \c posix_openpt(\a oflag). + /// A value of "O_RDWR|O_NOCTTY" is suggested. + /// + /// @param[out] error + /// An pointer to an error that can describe any errors that + /// occur. This can be NULL if no error status is desired. + /// + /// @return + /// @li \b true when the a master files descriptor is + /// successfully opened. + /// @li \b false if anything goes wrong. + /// + /// @see PseudoTerminal::GetMasterFileDescriptor() + /// @see PseudoTerminal::ReleaseMasterFileDescriptor() + //------------------------------------------------------------------ + bool + OpenFirstAvailableMaster (int oflag, char *error_str, size_t error_len); + + //------------------------------------------------------------------ + /// Open the slave for the current master pseudo terminal. + /// + /// A master pseudo terminal should already be valid prior to + /// calling this function. The opened slave file descriptor is + /// stored in this object and can be accessed by calling the + /// PseudoTerminal::GetSlaveFileDescriptor() accessor. Clients + /// can call the PseudoTerminal::ReleaseSlaveFileDescriptor() + /// accessor function if they wish to use the slave file descriptor + /// beyond the lifespan of this object. + /// + /// If this object still has a valid slave file descriptor when its + /// destructor is called, it will close it. + /// + /// @param[in] oflag + /// Flags to use when calling \c open(\a oflag). + /// + /// @param[out] error + /// An pointer to an error that can describe any errors that + /// occur. This can be NULL if no error status is desired. + /// + /// @return + /// @li \b true when the a master files descriptor is + /// successfully opened. + /// @li \b false if anything goes wrong. + /// + /// @see PseudoTerminal::OpenFirstAvailableMaster() + /// @see PseudoTerminal::GetSlaveFileDescriptor() + /// @see PseudoTerminal::ReleaseSlaveFileDescriptor() + //------------------------------------------------------------------ + bool + OpenSlave (int oflag, char *error_str, size_t error_len); + + //------------------------------------------------------------------ + /// Release the master file descriptor. + /// + /// Releases ownership of the master pseudo terminal file descriptor + /// without closing it. The destructor for this class will close the + /// master file descriptor if the ownership isn't released using this + /// call and the master file descriptor has been opened. + /// + /// @return + /// The master file descriptor, or PseudoTerminal::invalid_fd + /// if the mast file descriptor is not currently valid. + //------------------------------------------------------------------ + int + ReleaseMasterFileDescriptor (); + + //------------------------------------------------------------------ + /// Release the slave file descriptor. + /// + /// Release ownership of the slave pseudo terminal file descriptor + /// without closing it. The destructor for this class will close the + /// slave file descriptor if the ownership isn't released using this + /// call and the slave file descriptor has been opened. + /// + /// @return + /// The slave file descriptor, or PseudoTerminal::invalid_fd + /// if the slave file descriptor is not currently valid. + //------------------------------------------------------------------ + int + ReleaseSlaveFileDescriptor (); + +protected: + //------------------------------------------------------------------ + // Member variables + //------------------------------------------------------------------ + int m_master_fd; ///< The file descriptor for the master. + int m_slave_fd; ///< The file descriptor for the slave. + +private: + DISALLOW_COPY_AND_ASSIGN (PseudoTerminal); + +}; + +} // namespace lldb + +#endif // #if defined(__cplusplus) +#endif // #ifndef liblldb_PseudoTerminal_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Utility/PythonPointer.h b/contrib/llvm/tools/lldb/include/lldb/Utility/PythonPointer.h new file mode 100644 index 00000000000..f782f7f1313 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Utility/PythonPointer.h @@ -0,0 +1,77 @@ +//===---------------------PythonPointer.h ------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef utility_PythonPointer_h_ +#define utility_PythonPointer_h_ + +#include + +#if defined (__APPLE__) +#include +#else +#include +#endif + +namespace lldb_private { + +template +class PythonPointer +{ +public: + typedef PyObject* element_type; +private: + element_type* ptr_; + bool my_ref; +public: + + PythonPointer(element_type p, bool steal_ref = false) : + ptr_(p), + my_ref(!steal_ref) + { + if (my_ref) + Py_INCREF(ptr_); + } + + PythonPointer(const PythonPointer& r, bool steal_ref = false) : + ptr_(r.ptr_), + my_ref(!steal_ref) + { + if (my_ref) + Py_INCREF(ptr_); + } + + ~PythonPointer() + { + if (my_ref) + Py_XDECREF(ptr_); + } + + PythonPointer + StealReference() + { + return PythonPointer(ptr_,true); + } + + PythonPointer + DuplicateReference() + { + return PythonPointer(ptr_, false); + } + + element_type get() const {return ptr_;} + + bool IsNull() { return ptr_ == NULL; } + bool IsNone() { return ptr_ == Py_None; } + + operator PyObject* () { return ptr_; } +}; + +} // namespace lldb + +#endif // utility_PythonPointer_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Utility/Range.h b/contrib/llvm/tools/lldb/include/lldb/Utility/Range.h new file mode 100644 index 00000000000..1257adb719a --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Utility/Range.h @@ -0,0 +1,89 @@ +//===--------------------- Range.h ------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef utility_Range_h_ +#define utility_Range_h_ + +#include +#include + +namespace lldb_utility { + +class Range +{ +public: + + typedef uint64_t ValueType; + + static const ValueType OPEN_END = UINT64_MAX; + + Range (const Range& rng); + + Range (ValueType low = 0, + ValueType high = OPEN_END); + + Range& + operator = (const Range& rhs); + + ValueType + GetLow () + { + return m_low; + } + + ValueType + GetHigh () + { + return m_high; + } + + void + SetLow (ValueType low) + { + m_low = low; + } + + void + SetHigh (ValueType high) + { + m_high = high; + } + + void + Flip (); + + void + Intersection (const Range& other); + + void + Union (const Range& other); + + typedef bool (*RangeCallback)(ValueType index); + + void + Iterate (RangeCallback callback); + + ValueType + GetSize (); + + bool + IsEmpty (); + +private: + + void + InitRange (); + + ValueType m_low; + ValueType m_high; +}; + +} // namespace lldb_private + +#endif // #ifndef utility_Range_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Utility/RefCounter.h b/contrib/llvm/tools/lldb/include/lldb/Utility/RefCounter.h new file mode 100644 index 00000000000..6daed5498eb --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Utility/RefCounter.h @@ -0,0 +1,56 @@ +//===-- RefCounter.h --------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_RefCounter_h_ +#define liblldb_RefCounter_h_ + +#include "lldb/lldb-public.h" + +namespace lldb_utility { + +//---------------------------------------------------------------------- +// A simple reference counter object. You need an uint32_t* to use it +// Once that is in place, everyone who needs to ref-count, can say +// RefCounter ref(ptr); +// (of course, the pointer is a shared resource, and must be accessible to +// everyone who needs it). Synchronization is handled by RefCounter itself +// The counter is decreased each time a RefCounter to it goes out of scope +//---------------------------------------------------------------------- +class RefCounter +{ +public: + typedef uint32_t value_type; + + RefCounter(value_type* ctr); + + ~RefCounter(); + +private: + value_type* m_counter; + DISALLOW_COPY_AND_ASSIGN (RefCounter); + + template + inline T + increment(T* t) + { + return __sync_fetch_and_add(t, 1); + } + + template + inline T + decrement(T* t) + { + return __sync_fetch_and_add(t, -1); + } + +}; + +} // namespace lldb_utility + +#endif // #ifndef liblldb_RefCounter_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Utility/SharedCluster.h b/contrib/llvm/tools/lldb/include/lldb/Utility/SharedCluster.h new file mode 100644 index 00000000000..991af4b4fa4 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Utility/SharedCluster.h @@ -0,0 +1,108 @@ +//===------------------SharedCluster.h --------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef utility_SharedCluster_h_ +#define utility_SharedCluster_h_ + +#include "lldb/Utility/SharingPtr.h" +#include "lldb/Host/Mutex.h" + +namespace lldb_private { + +namespace imp +{ + template + class shared_ptr_refcount : public lldb_private::imp::shared_count + { + public: + template shared_ptr_refcount (Y *in) : shared_count (0), manager(in) {} + + shared_ptr_refcount() : shared_count (0) {} + + virtual ~shared_ptr_refcount () + { + } + + virtual void on_zero_shared () + { + manager->DecrementRefCount(); + } + private: + T *manager; + }; + +} // namespace imp + +template +class ClusterManager +{ +public: + ClusterManager () : + m_objects(), + m_external_ref(0), + m_mutex(Mutex::eMutexTypeNormal) {} + + ~ClusterManager () + { + size_t n_items = m_objects.size(); + for (size_t i = 0; i < n_items; i++) + { + delete m_objects[i]; + } + // Decrement refcount should have been called on this ClusterManager, + // and it should have locked the mutex, now we will unlock it before + // we destroy it... + m_mutex.Unlock(); + } + + void ManageObject (T *new_object) + { + Mutex::Locker locker (m_mutex); + if (!ContainsObject(new_object)) + m_objects.push_back (new_object); + } + + typename lldb_private::SharingPtr GetSharedPointer(T *desired_object) + { + { + Mutex::Locker locker (m_mutex); + m_external_ref++; + assert (ContainsObject(desired_object)); + } + return typename lldb_private::SharingPtr (desired_object, new imp::shared_ptr_refcount (this)); + } + +private: + + bool ContainsObject (const T *desired_object) + { + typename std::vector::iterator pos, end = m_objects.end(); + pos = std::find(m_objects.begin(), end, desired_object); + return pos != end; + } + + void DecrementRefCount () + { + m_mutex.Lock(); + m_external_ref--; + if (m_external_ref == 0) + delete this; + else + m_mutex.Unlock(); + } + + friend class imp::shared_ptr_refcount; + + std::vector m_objects; + int m_external_ref; + Mutex m_mutex; +}; + +} // namespace lldb_private +#endif // utility_SharedCluster_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Utility/SharingPtr.h b/contrib/llvm/tools/lldb/include/lldb/Utility/SharingPtr.h new file mode 100644 index 00000000000..814b54b5cfe --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Utility/SharingPtr.h @@ -0,0 +1,816 @@ +//===---------------------SharingPtr.h --------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef utility_SharingPtr_h_ +#define utility_SharingPtr_h_ + +#include +#include + +//#define ENABLE_SP_LOGGING 1 // DON'T CHECK THIS LINE IN UNLESS COMMENTED OUT +#if defined (ENABLE_SP_LOGGING) + +extern "C" void track_sp (void *sp_this, void *ptr, long count); + +#endif + +namespace lldb_private { + +namespace imp { + +template +inline T +increment(T& t) +{ + return __sync_add_and_fetch(&t, 1); +} + +template +inline T +decrement(T& t) +{ + return __sync_add_and_fetch(&t, -1); +} + +class shared_count +{ + shared_count(const shared_count&); + shared_count& operator=(const shared_count&); + +protected: + long shared_owners_; + virtual ~shared_count(); +private: + virtual void on_zero_shared() = 0; + +public: + explicit shared_count(long refs = 0) + : shared_owners_(refs) {} + + void add_shared(); + void release_shared(); + long use_count() const {return shared_owners_ + 1;} +}; + +template +class shared_ptr_pointer + : public shared_count +{ + T data_; +public: + shared_ptr_pointer(T p) + : data_(p) {} + +private: + virtual void on_zero_shared(); + + // Outlaw copy constructor and assignment operator to keep effictive C++ + // warnings down to a minumum + shared_ptr_pointer (const shared_ptr_pointer &); + shared_ptr_pointer & operator=(const shared_ptr_pointer &); +}; + +template +void +shared_ptr_pointer::on_zero_shared() +{ + delete data_; +} + +template +class shared_ptr_emplace + : public shared_count +{ + T data_; +public: + + shared_ptr_emplace() + : data_() {} + + template + shared_ptr_emplace(A0& a0) + : data_(a0) {} + + template + shared_ptr_emplace(A0& a0, A1& a1) + : data_(a0, a1) {} + + template + shared_ptr_emplace(A0& a0, A1& a1, A2& a2) + : data_(a0, a1, a2) {} + + template + shared_ptr_emplace(A0& a0, A1& a1, A2& a2, A3& a3) + : data_(a0, a1, a2, a3) {} + + template + shared_ptr_emplace(A0& a0, A1& a1, A2& a2, A3& a3, A4& a4) + : data_(a0, a1, a2, a3, a4) {} + +private: + virtual void on_zero_shared(); +public: + T* get() {return &data_;} +}; + +template +void +shared_ptr_emplace::on_zero_shared() +{ +} + +} // namespace + +template +class SharingPtr +{ +public: + typedef T element_type; +private: + element_type* ptr_; + imp::shared_count* cntrl_; + + struct nat {int for_bool_;}; +public: + SharingPtr(); + template explicit SharingPtr(Y* p); + template explicit SharingPtr(Y* p, imp::shared_count *ctrl_block); + template SharingPtr(const SharingPtr& r, element_type *p); + SharingPtr(const SharingPtr& r); + template + SharingPtr(const SharingPtr& r); + + ~SharingPtr(); + + SharingPtr& operator=(const SharingPtr& r); + template SharingPtr& operator=(const SharingPtr& r); + + void swap(SharingPtr& r); + void reset(); + template void reset(Y* p); + + element_type* get() const {return ptr_;} + element_type& operator*() const {return *ptr_;} + element_type* operator->() const {return ptr_;} + long use_count() const {return cntrl_ ? cntrl_->use_count() : 0;} + bool unique() const {return use_count() == 1;} + bool empty() const {return cntrl_ == 0;} + operator nat*() const {return (nat*)get();} + + static SharingPtr make_shared(); + + template + static SharingPtr make_shared(A0&); + + template + static SharingPtr make_shared(A0&, A1&); + + template + static SharingPtr make_shared(A0&, A1&, A2&); + + template + static SharingPtr make_shared(A0&, A1&, A2&, A3&); + + template + static SharingPtr make_shared(A0&, A1&, A2&, A3&, A4&); + +private: + + template friend class SharingPtr; +}; + +template +inline +SharingPtr::SharingPtr() + : ptr_(0), + cntrl_(0) +{ +} + +template +template +SharingPtr::SharingPtr(Y* p) + : ptr_(p), cntrl_(0) +{ + std::unique_ptr hold(p); + typedef imp::shared_ptr_pointer _CntrlBlk; + cntrl_ = new _CntrlBlk(p); + hold.release(); +} + +template +template +SharingPtr::SharingPtr(Y* p, imp::shared_count *cntrl_block) + : ptr_(p), cntrl_(cntrl_block) +{ +} + +template +template +inline +SharingPtr::SharingPtr(const SharingPtr& r, element_type *p) + : ptr_(p), + cntrl_(r.cntrl_) +{ + if (cntrl_) + cntrl_->add_shared(); +} + +template +inline +SharingPtr::SharingPtr(const SharingPtr& r) + : ptr_(r.ptr_), + cntrl_(r.cntrl_) +{ + if (cntrl_) + cntrl_->add_shared(); +} + +template +template +inline +SharingPtr::SharingPtr(const SharingPtr& r) + : ptr_(r.ptr_), + cntrl_(r.cntrl_) +{ + if (cntrl_) + cntrl_->add_shared(); +} + +template +SharingPtr::~SharingPtr() +{ + if (cntrl_) + cntrl_->release_shared(); +} + +template +inline +SharingPtr& +SharingPtr::operator=(const SharingPtr& r) +{ + SharingPtr(r).swap(*this); + return *this; +} + +template +template +inline +SharingPtr& +SharingPtr::operator=(const SharingPtr& r) +{ + SharingPtr(r).swap(*this); + return *this; +} + +template +inline +void +SharingPtr::swap(SharingPtr& r) +{ + std::swap(ptr_, r.ptr_); + std::swap(cntrl_, r.cntrl_); +} + +template +inline +void +SharingPtr::reset() +{ + SharingPtr().swap(*this); +} + +template +template +inline +void +SharingPtr::reset(Y* p) +{ + SharingPtr(p).swap(*this); +} + +template +SharingPtr +SharingPtr::make_shared() +{ + typedef imp::shared_ptr_emplace CntrlBlk; + SharingPtr r; + r.cntrl_ = new CntrlBlk(); + r.ptr_ = static_cast(r.cntrl_)->get(); + return r; +} + +template +template +SharingPtr +SharingPtr::make_shared(A0& a0) +{ + typedef imp::shared_ptr_emplace CntrlBlk; + SharingPtr r; + r.cntrl_ = new CntrlBlk(a0); + r.ptr_ = static_cast(r.cntrl_)->get(); + return r; +} + +template +template +SharingPtr +SharingPtr::make_shared(A0& a0, A1& a1) +{ + typedef imp::shared_ptr_emplace CntrlBlk; + SharingPtr r; + r.cntrl_ = new CntrlBlk(a0, a1); + r.ptr_ = static_cast(r.cntrl_)->get(); + return r; +} + +template +template +SharingPtr +SharingPtr::make_shared(A0& a0, A1& a1, A2& a2) +{ + typedef imp::shared_ptr_emplace CntrlBlk; + SharingPtr r; + r.cntrl_ = new CntrlBlk(a0, a1, a2); + r.ptr_ = static_cast(r.cntrl_)->get(); + return r; +} + +template +template +SharingPtr +SharingPtr::make_shared(A0& a0, A1& a1, A2& a2, A3& a3) +{ + typedef imp::shared_ptr_emplace CntrlBlk; + SharingPtr r; + r.cntrl_ = new CntrlBlk(a0, a1, a2, a3); + r.ptr_ = static_cast(r.cntrl_)->get(); + return r; +} + +template +template +SharingPtr +SharingPtr::make_shared(A0& a0, A1& a1, A2& a2, A3& a3, A4& a4) +{ + typedef imp::shared_ptr_emplace CntrlBlk; + SharingPtr r; + r.cntrl_ = new CntrlBlk(a0, a1, a2, a3, a4); + r.ptr_ = static_cast(r.cntrl_)->get(); + return r; +} + +template +inline +SharingPtr +make_shared() +{ + return SharingPtr::make_shared(); +} + +template +inline +SharingPtr +make_shared(A0& a0) +{ + return SharingPtr::make_shared(a0); +} + +template +inline +SharingPtr +make_shared(A0& a0, A1& a1) +{ + return SharingPtr::make_shared(a0, a1); +} + +template +inline +SharingPtr +make_shared(A0& a0, A1& a1, A2& a2) +{ + return SharingPtr::make_shared(a0, a1, a2); +} + +template +inline +SharingPtr +make_shared(A0& a0, A1& a1, A2& a2, A3& a3) +{ + return SharingPtr::make_shared(a0, a1, a2, a3); +} + +template +inline +SharingPtr +make_shared(A0& a0, A1& a1, A2& a2, A3& a3, A4& a4) +{ + return SharingPtr::make_shared(a0, a1, a2, a3, a4); +} + + +template +inline +bool +operator==(const SharingPtr& __x, const SharingPtr& __y) +{ + return __x.get() == __y.get(); +} + +template +inline +bool +operator!=(const SharingPtr& __x, const SharingPtr& __y) +{ + return !(__x == __y); +} + +template +inline +bool +operator<(const SharingPtr& __x, const SharingPtr& __y) +{ + return __x.get() < __y.get(); +} + +template +inline +void +swap(SharingPtr& __x, SharingPtr& __y) +{ + __x.swap(__y); +} + +template +inline +SharingPtr +static_pointer_cast(const SharingPtr& r) +{ + return SharingPtr(r, static_cast(r.get())); +} + +template +SharingPtr +const_pointer_cast(const SharingPtr& r) +{ + return SharingPtr(r, const_cast(r.get())); +} + +template +class LoggingSharingPtr + : public SharingPtr +{ + typedef SharingPtr base; + +public: + typedef void (*Callback)(void*, const LoggingSharingPtr&, bool action); + // action: false means increment just happened + // true means decrement is about to happen + +private: + Callback cb_; + void* baton_; + +public: + LoggingSharingPtr() : cb_(0), baton_(0) {} + LoggingSharingPtr(Callback cb, void* baton) + : cb_(cb), baton_(baton) + { + if (cb_) + cb_(baton_, *this, false); + } + + template + LoggingSharingPtr(Y* p) + : base(p), cb_(0), baton_(0) {} + + template + LoggingSharingPtr(Y* p, Callback cb, void* baton) + : base(p), cb_(cb), baton_(baton) + { + if (cb_) + cb_(baton_, *this, false); + } + + ~LoggingSharingPtr() + { + if (cb_) + cb_(baton_, *this, true); + } + + LoggingSharingPtr(const LoggingSharingPtr& p) + : base(p), cb_(p.cb_), baton_(p.baton_) + { + if (cb_) + cb_(baton_, *this, false); + } + + LoggingSharingPtr& operator=(const LoggingSharingPtr& p) + { + if (cb_) + cb_(baton_, *this, true); + base::operator=(p); + cb_ = p.cb_; + baton_ = p.baton_; + if (cb_) + cb_(baton_, *this, false); + return *this; + } + + void reset() + { + if (cb_) + cb_(baton_, *this, true); + base::reset(); + } + + template + void reset(Y* p) + { + if (cb_) + cb_(baton_, *this, true); + base::reset(p); + if (cb_) + cb_(baton_, *this, false); + } + + void SetCallback(Callback cb, void* baton) + { + cb_ = cb; + baton_ = baton; + } + + void ClearCallback() + { + cb_ = 0; + baton_ = 0; + } +}; + + +template +class IntrusiveSharingPtr; + +template +class ReferenceCountedBase +{ +public: + explicit ReferenceCountedBase() + : shared_owners_(-1) + { + } + + void + add_shared(); + + void + release_shared(); + + long + use_count() const + { + return shared_owners_ + 1; + } + +protected: + long shared_owners_; + + friend class IntrusiveSharingPtr; + +private: + ReferenceCountedBase(const ReferenceCountedBase&); + ReferenceCountedBase& operator=(const ReferenceCountedBase&); +}; + + template + void + lldb_private::ReferenceCountedBase::add_shared() + { + imp::increment(shared_owners_); + } + + template + void + lldb_private::ReferenceCountedBase::release_shared() + { + if (imp::decrement(shared_owners_) == -1) + delete static_cast(this); + } + + +template +class ReferenceCountedBaseVirtual : public imp::shared_count +{ +public: + explicit ReferenceCountedBaseVirtual () : + imp::shared_count(-1) + { + } + + virtual + ~ReferenceCountedBaseVirtual () + { + } + + virtual void on_zero_shared (); + +}; + +template +void +ReferenceCountedBaseVirtual::on_zero_shared() +{ +} + +template +class IntrusiveSharingPtr +{ +public: + typedef T element_type; + + explicit + IntrusiveSharingPtr () : + ptr_(0) + { + } + + explicit + IntrusiveSharingPtr (T* ptr) : + ptr_(ptr) + { + add_shared(); + } + + IntrusiveSharingPtr (const IntrusiveSharingPtr& rhs) : + ptr_(rhs.ptr_) + { + add_shared(); + } + + template + IntrusiveSharingPtr (const IntrusiveSharingPtr& rhs) + : ptr_(rhs.get()) + { + add_shared(); + } + + IntrusiveSharingPtr& + operator= (const IntrusiveSharingPtr& rhs) + { + reset(rhs.get()); + return *this; + } + + template IntrusiveSharingPtr& + operator= (const IntrusiveSharingPtr& rhs) + { + reset(rhs.get()); + return *this; + } + + IntrusiveSharingPtr& + operator= (T *ptr) + { + reset(ptr); + return *this; + } + + ~IntrusiveSharingPtr() + { + release_shared(); +#if defined (LLDB_CONFIGURATION_DEBUG) || defined (LLDB_CONFIGURATION_RELEASE) + // NULL out the pointer in objects which can help with leaks detection. + // We don't enable this for LLDB_CONFIGURATION_BUILD_AND_INTEGRATION or + // when none of the LLDB_CONFIGURATION_XXX macros are defined since + // those would be builds for release. But for debug and release builds + // that are for development, we NULL out the pointers to catch potential + // issues. + ptr_ = NULL; +#endif // #if defined (LLDB_CONFIGURATION_DEBUG) || defined (LLDB_CONFIGURATION_RELEASE) + } + + T& + operator*() const + { + return *ptr_; + } + + T* + operator->() const + { + return ptr_; + } + + T* + get() const + { + return ptr_; + } + + operator bool() const + { + return ptr_ != 0; + } + + void + swap (IntrusiveSharingPtr& rhs) + { + std::swap(ptr_, rhs.ptr_); +#if defined (ENABLE_SP_LOGGING) + track_sp (this, ptr_, use_count()); + track_sp (&rhs, rhs.ptr_, rhs.use_count()); +#endif + } + + void + reset(T* ptr = NULL) + { + IntrusiveSharingPtr(ptr).swap(*this); + } + + long + use_count () const + { + if (ptr_) + return ptr_->use_count(); + return 0; + } + + bool + unique () const + { + return use_count () == 1; + } + +private: + element_type *ptr_; + + void + add_shared() + { + if (ptr_) + { + ptr_->add_shared(); +#if defined (ENABLE_SP_LOGGING) + track_sp (this, ptr_, ptr_->use_count()); +#endif + } + } + void + release_shared() + { + if (ptr_) + { +#if defined (ENABLE_SP_LOGGING) + track_sp (this, NULL, ptr_->use_count() - 1); +#endif + ptr_->release_shared(); + } + } +}; + +template +inline bool operator== (const IntrusiveSharingPtr& lhs, const IntrusiveSharingPtr& rhs) +{ + return lhs.get() == rhs.get(); +} + +template +inline bool operator!= (const IntrusiveSharingPtr& lhs, const IntrusiveSharingPtr& rhs) +{ + return lhs.get() != rhs.get(); +} + +template +inline bool operator== (const IntrusiveSharingPtr& lhs, U* rhs) +{ + return lhs.get() == rhs; +} + +template +inline bool operator!= (const IntrusiveSharingPtr& lhs, U* rhs) +{ + return lhs.get() != rhs; +} + +template +inline bool operator== (T* lhs, const IntrusiveSharingPtr& rhs) +{ + return lhs == rhs.get(); +} + +template +inline bool operator!= (T* lhs, const IntrusiveSharingPtr& rhs) +{ + return lhs != rhs.get(); +} + +} // namespace lldb_private + +#endif // utility_SharingPtr_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/Utility/Utils.h b/contrib/llvm/tools/lldb/include/lldb/Utility/Utils.h new file mode 100644 index 00000000000..46bc1847c0b --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/Utility/Utils.h @@ -0,0 +1,22 @@ +//===-- Utils.h -------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef utility_Utils_h_ +#define utility_Utils_h_ + +// These utilities have llvm namespace. +#include "llvm/ADT/STLExtras.h" + +namespace lldb_private { + +// Add lldb utilities here. + +} // namespace lldb_private + +#endif // utility_Utils diff --git a/contrib/llvm/tools/lldb/include/lldb/lldb-defines.h b/contrib/llvm/tools/lldb/include/lldb/lldb-defines.h new file mode 100644 index 00000000000..3318aa15f5a --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/lldb-defines.h @@ -0,0 +1,125 @@ +//===-- lldb-defines.h ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_lldb_defines_h_ +#define LLDB_lldb_defines_h_ + +#include "lldb/lldb-types.h" + +#if !defined(UINT32_MAX) + #define UINT32_MAX 4294967295U +#endif + +#if !defined(UINT64_MAX) + #define UINT64_MAX 18446744073709551615ULL +#endif + +//---------------------------------------------------------------------- +// LLDB version +// +// A build script phase can modify this version number if needed. +//---------------------------------------------------------------------- +//#define LLDB_VERSION +//#define LLDB_REVISION +//#define LLDB_VERSION_STRING + +//---------------------------------------------------------------------- +// LLDB defines +//---------------------------------------------------------------------- +#define LLDB_GENERIC_ERROR UINT32_MAX + +//---------------------------------------------------------------------- +// Breakpoints +//---------------------------------------------------------------------- +#define LLDB_INVALID_BREAK_ID 0 +#define LLDB_DEFAULT_BREAK_SIZE 0 +#define LLDB_BREAK_ID_IS_VALID(bid) ((bid) != (LLDB_INVALID_BREAK_ID)) +#define LLDB_BREAK_ID_IS_INTERNAL(bid) ((bid) < 0) + +//---------------------------------------------------------------------- +// Watchpoints +//---------------------------------------------------------------------- +#define LLDB_INVALID_WATCH_ID 0 +#define LLDB_WATCH_ID_IS_VALID(uid) ((uid) != (LLDB_INVALID_WATCH_ID)) +#define LLDB_WATCH_TYPE_READ (1u << 0) +#define LLDB_WATCH_TYPE_WRITE (1u << 1) +#define LLDB_WATCH_TYPE_IS_VALID(type) ((type | LLDB_WATCH_TYPE_READ) || (type | LLDB_WATCH_TYPE_WRITE)) + +//---------------------------------------------------------------------- +// Generic Register Numbers +//---------------------------------------------------------------------- +#define LLDB_REGNUM_GENERIC_PC 0 // Program Counter +#define LLDB_REGNUM_GENERIC_SP 1 // Stack Pointer +#define LLDB_REGNUM_GENERIC_FP 2 // Frame Pointer +#define LLDB_REGNUM_GENERIC_RA 3 // Return Address +#define LLDB_REGNUM_GENERIC_FLAGS 4 // Processor flags register +#define LLDB_REGNUM_GENERIC_ARG1 5 // The register that would contain pointer size or less argument 1 (if any) +#define LLDB_REGNUM_GENERIC_ARG2 6 // The register that would contain pointer size or less argument 2 (if any) +#define LLDB_REGNUM_GENERIC_ARG3 7 // The register that would contain pointer size or less argument 3 (if any) +#define LLDB_REGNUM_GENERIC_ARG4 8 // The register that would contain pointer size or less argument 4 (if any) +#define LLDB_REGNUM_GENERIC_ARG5 9 // The register that would contain pointer size or less argument 5 (if any) +#define LLDB_REGNUM_GENERIC_ARG6 10 // The register that would contain pointer size or less argument 6 (if any) +#define LLDB_REGNUM_GENERIC_ARG7 11 // The register that would contain pointer size or less argument 7 (if any) +#define LLDB_REGNUM_GENERIC_ARG8 12 // The register that would contain pointer size or less argument 8 (if any) +//--------------------------------------------------------------------- +/// Invalid value definitions +//---------------------------------------------------------------------- +#define LLDB_INVALID_ADDRESS UINT64_MAX +#define LLDB_INVALID_INDEX32 UINT32_MAX +#define LLDB_INVALID_IVAR_OFFSET UINT32_MAX +#define LLDB_INVALID_IMAGE_TOKEN UINT32_MAX +#define LLDB_INVALID_REGNUM UINT32_MAX +#define LLDB_INVALID_UID UINT64_MAX +#define LLDB_INVALID_PROCESS_ID 0 +#define LLDB_INVALID_THREAD_ID 0 +#define LLDB_INVALID_FRAME_ID UINT32_MAX +#define LLDB_INVALID_SIGNAL_NUMBER INT32_MAX +#define LLDB_INVALID_OFFSET UINT64_MAX // Must match max of lldb::offset_t + +//---------------------------------------------------------------------- +/// CPU Type defintions +//---------------------------------------------------------------------- +#define LLDB_ARCH_DEFAULT "systemArch" +#define LLDB_ARCH_DEFAULT_32BIT "systemArch32" +#define LLDB_ARCH_DEFAULT_64BIT "systemArch64" +#define LLDB_INVALID_CPUTYPE (0xFFFFFFFEu) + +//---------------------------------------------------------------------- +/// Option Set defintions +//---------------------------------------------------------------------- +// FIXME: I'm sure there's some #define magic that can create all 32 sets on the +// fly. That would have the added benefit of making this unreadable. +#define LLDB_MAX_NUM_OPTION_SETS 32 +#define LLDB_OPT_SET_ALL 0xFFFFFFFFU +#define LLDB_OPT_SET_1 (1U << 0) +#define LLDB_OPT_SET_2 (1U << 1) +#define LLDB_OPT_SET_3 (1U << 2) +#define LLDB_OPT_SET_4 (1U << 3) +#define LLDB_OPT_SET_5 (1U << 4) +#define LLDB_OPT_SET_6 (1U << 5) +#define LLDB_OPT_SET_7 (1U << 6) +#define LLDB_OPT_SET_8 (1U << 7) +#define LLDB_OPT_SET_9 (1U << 8) +#define LLDB_OPT_SET_10 (1U << 9) +#define LLDB_OPT_SET_FROM_TO(A, B) (((1U << (B)) - 1) ^ (((1U << (A))-1) >> 1)) + +#if defined(__cplusplus) + +//---------------------------------------------------------------------- +/// @def DISALLOW_COPY_AND_ASSIGN(TypeName) +/// Macro definition for easily disallowing copy constructor and +/// assignment operators in C++ classes. +//---------------------------------------------------------------------- +#define DISALLOW_COPY_AND_ASSIGN(TypeName) \ + TypeName(const TypeName&); \ + const TypeName& operator=(const TypeName&) + +#endif // #if defined(__cplusplus) + +#endif // LLDB_lldb_defines_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/lldb-enumerations.h b/contrib/llvm/tools/lldb/include/lldb/lldb-enumerations.h new file mode 100644 index 00000000000..6ec5ed6a35e --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/lldb-enumerations.h @@ -0,0 +1,685 @@ +//===-- lldb-enumerations.h -------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_lldb_enumerations_h_ +#define LLDB_lldb_enumerations_h_ + +namespace lldb { + + //---------------------------------------------------------------------- + // Process and Thread States + //---------------------------------------------------------------------- + typedef enum StateType + { + eStateInvalid = 0, + eStateUnloaded, ///< Process is object is valid, but not currently loaded + eStateConnected, ///< Process is connected to remote debug services, but not launched or attached to anything yet + eStateAttaching, ///< Process is currently trying to attach + eStateLaunching, ///< Process is in the process of launching + eStateStopped, ///< Process or thread is stopped and can be examined. + eStateRunning, ///< Process or thread is running and can't be examined. + eStateStepping, ///< Process or thread is in the process of stepping and can not be examined. + eStateCrashed, ///< Process or thread has crashed and can be examined. + eStateDetached, ///< Process has been detached and can't be examined. + eStateExited, ///< Process has exited and can't be examined. + eStateSuspended ///< Process or thread is in a suspended state as far + ///< as the debugger is concerned while other processes + ///< or threads get the chance to run. + } StateType; + + //---------------------------------------------------------------------- + // Launch Flags + //---------------------------------------------------------------------- + typedef enum LaunchFlags + { + eLaunchFlagNone = 0u, + eLaunchFlagExec = (1u << 0), ///< Exec when launching and turn the calling process into a new process + eLaunchFlagDebug = (1u << 1), ///< Stop as soon as the process launches to allow the process to be debugged + eLaunchFlagStopAtEntry = (1u << 2), ///< Stop at the program entry point instead of auto-continuing when launching or attaching at entry point + eLaunchFlagDisableASLR = (1u << 3), ///< Disable Address Space Layout Randomization + eLaunchFlagDisableSTDIO = (1u << 4), ///< Disable stdio for inferior process (e.g. for a GUI app) + eLaunchFlagLaunchInTTY = (1u << 5), ///< Launch the process in a new TTY if supported by the host + eLaunchFlagLaunchInShell= (1u << 6), ///< Launch the process inside a shell to get shell expansion + eLaunchFlagLaunchInSeparateProcessGroup = (1u << 7) ///< Launch the process in a separate process group + } LaunchFlags; + + //---------------------------------------------------------------------- + // Thread Run Modes + //---------------------------------------------------------------------- + typedef enum RunMode { + eOnlyThisThread, + eAllThreads, + eOnlyDuringStepping + } RunMode; + + //---------------------------------------------------------------------- + // Byte ordering definitions + //---------------------------------------------------------------------- + typedef enum ByteOrder + { + eByteOrderInvalid = 0, + eByteOrderBig = 1, + eByteOrderPDP = 2, + eByteOrderLittle = 4 + } ByteOrder; + + //---------------------------------------------------------------------- + // Register encoding definitions + //---------------------------------------------------------------------- + typedef enum Encoding + { + eEncodingInvalid = 0, + eEncodingUint, // unsigned integer + eEncodingSint, // signed integer + eEncodingIEEE754, // float + eEncodingVector // vector registers + } Encoding; + + //---------------------------------------------------------------------- + // Display format definitions + //---------------------------------------------------------------------- + typedef enum Format + { + eFormatDefault = 0, + eFormatInvalid = 0, + eFormatBoolean, + eFormatBinary, + eFormatBytes, + eFormatBytesWithASCII, + eFormatChar, + eFormatCharPrintable, // Only printable characters, space if not printable + eFormatComplex, // Floating point complex type + eFormatComplexFloat = eFormatComplex, + eFormatCString, // NULL terminated C strings + eFormatDecimal, + eFormatEnum, + eFormatHex, + eFormatHexUppercase, + eFormatFloat, + eFormatOctal, + eFormatOSType, // OS character codes encoded into an integer 'PICT' 'text' etc... + eFormatUnicode16, + eFormatUnicode32, + eFormatUnsigned, + eFormatPointer, + eFormatVectorOfChar, + eFormatVectorOfSInt8, + eFormatVectorOfUInt8, + eFormatVectorOfSInt16, + eFormatVectorOfUInt16, + eFormatVectorOfSInt32, + eFormatVectorOfUInt32, + eFormatVectorOfSInt64, + eFormatVectorOfUInt64, + eFormatVectorOfFloat32, + eFormatVectorOfFloat64, + eFormatVectorOfUInt128, + eFormatComplexInteger, // Integer complex type + eFormatCharArray, // Print characters with no single quotes, used for character arrays that can contain non printable characters + eFormatAddressInfo, // Describe what an address points to (func + offset with file/line, symbol + offset, data, etc) + eFormatHexFloat, // ISO C99 hex float string + eFormatInstruction, // Disassemble an opcode + eFormatVoid, // Do not print this + kNumFormats + } Format; + + //---------------------------------------------------------------------- + // Description levels for "void GetDescription(Stream *, DescriptionLevel)" calls + //---------------------------------------------------------------------- + typedef enum DescriptionLevel + { + eDescriptionLevelBrief = 0, + eDescriptionLevelFull, + eDescriptionLevelVerbose, + eDescriptionLevelInitial, + kNumDescriptionLevels + } DescriptionLevel; + + //---------------------------------------------------------------------- + // Script interpreter types + //---------------------------------------------------------------------- + typedef enum ScriptLanguage + { + eScriptLanguageNone, + eScriptLanguagePython, + eScriptLanguageDefault = eScriptLanguagePython + } ScriptLanguage; + + //---------------------------------------------------------------------- + // Register numbering types + //---------------------------------------------------------------------- + typedef enum RegisterKind + { + eRegisterKindGCC = 0, // the register numbers seen in eh_frame + eRegisterKindDWARF, // the register numbers seen DWARF + eRegisterKindGeneric, // insn ptr reg, stack ptr reg, etc not specific to any particular target + eRegisterKindGDB, // the register numbers gdb uses (matches stabs numbers?) + eRegisterKindLLDB, // lldb's internal register numbers + kNumRegisterKinds + } RegisterKind; + + //---------------------------------------------------------------------- + // Thread stop reasons + //---------------------------------------------------------------------- + typedef enum StopReason + { + eStopReasonInvalid = 0, + eStopReasonNone, + eStopReasonTrace, + eStopReasonBreakpoint, + eStopReasonWatchpoint, + eStopReasonSignal, + eStopReasonException, + eStopReasonExec, // Program was re-exec'ed + eStopReasonPlanComplete, + eStopReasonThreadExiting + } StopReason; + + //---------------------------------------------------------------------- + // Command Return Status Types + //---------------------------------------------------------------------- + typedef enum ReturnStatus + { + eReturnStatusInvalid, + eReturnStatusSuccessFinishNoResult, + eReturnStatusSuccessFinishResult, + eReturnStatusSuccessContinuingNoResult, + eReturnStatusSuccessContinuingResult, + eReturnStatusStarted, + eReturnStatusFailed, + eReturnStatusQuit + } ReturnStatus; + + + //---------------------------------------------------------------------- + // Connection Status Types + //---------------------------------------------------------------------- + typedef enum ConnectionStatus + { + eConnectionStatusSuccess, // Success + eConnectionStatusEndOfFile, // End-of-file encountered + eConnectionStatusError, // Check GetError() for details + eConnectionStatusTimedOut, // Request timed out + eConnectionStatusNoConnection, // No connection + eConnectionStatusLostConnection // Lost connection while connected to a valid connection + } ConnectionStatus; + + typedef enum ErrorType + { + eErrorTypeInvalid, + eErrorTypeGeneric, ///< Generic errors that can be any value. + eErrorTypeMachKernel, ///< Mach kernel error codes. + eErrorTypePOSIX ///< POSIX error codes. + } ErrorType; + + + typedef enum ValueType + { + eValueTypeInvalid = 0, + eValueTypeVariableGlobal = 1, // globals variable + eValueTypeVariableStatic = 2, // static variable + eValueTypeVariableArgument = 3, // function argument variables + eValueTypeVariableLocal = 4, // function local variables + eValueTypeRegister = 5, // stack frame register value + eValueTypeRegisterSet = 6, // A collection of stack frame register values + eValueTypeConstResult = 7 // constant result variables + } ValueType; + + //---------------------------------------------------------------------- + // Token size/granularities for Input Readers + //---------------------------------------------------------------------- + + typedef enum InputReaderGranularity + { + eInputReaderGranularityInvalid = 0, + eInputReaderGranularityByte, + eInputReaderGranularityWord, + eInputReaderGranularityLine, + eInputReaderGranularityAll + } InputReaderGranularity; + + //------------------------------------------------------------------ + /// These mask bits allow a common interface for queries that can + /// limit the amount of information that gets parsed to only the + /// information that is requested. These bits also can indicate what + /// actually did get resolved during query function calls. + /// + /// Each definition corresponds to a one of the member variables + /// in this class, and requests that that item be resolved, or + /// indicates that the member did get resolved. + //------------------------------------------------------------------ + typedef enum SymbolContextItem + { + eSymbolContextTarget = (1u << 0), ///< Set when \a target is requested from a query, or was located in query results + eSymbolContextModule = (1u << 1), ///< Set when \a module is requested from a query, or was located in query results + eSymbolContextCompUnit = (1u << 2), ///< Set when \a comp_unit is requested from a query, or was located in query results + eSymbolContextFunction = (1u << 3), ///< Set when \a function is requested from a query, or was located in query results + eSymbolContextBlock = (1u << 4), ///< Set when the deepest \a block is requested from a query, or was located in query results + eSymbolContextLineEntry = (1u << 5), ///< Set when \a line_entry is requested from a query, or was located in query results + eSymbolContextSymbol = (1u << 6), ///< Set when \a symbol is requested from a query, or was located in query results + eSymbolContextEverything = ((eSymbolContextSymbol << 1) - 1u) ///< Indicates to try and lookup everything up during a query. + } SymbolContextItem; + + typedef enum Permissions + { + ePermissionsWritable = (1u << 0), + ePermissionsReadable = (1u << 1), + ePermissionsExecutable = (1u << 2) + } Permissions; + + typedef enum InputReaderAction + { + eInputReaderActivate, // reader is newly pushed onto the reader stack + eInputReaderAsynchronousOutputWritten, // an async output event occurred; the reader may want to do something + eInputReaderReactivate, // reader is on top of the stack again after another reader was popped off + eInputReaderDeactivate, // another reader was pushed on the stack + eInputReaderGotToken, // reader got one of its tokens (granularity) + eInputReaderInterrupt, // reader received an interrupt signal (probably from a control-c) + eInputReaderEndOfFile, // reader received an EOF char (probably from a control-d) + eInputReaderDone // reader was just popped off the stack and is done + } InputReaderAction; + + typedef enum BreakpointEventType + { + eBreakpointEventTypeInvalidType = (1u << 0), + eBreakpointEventTypeAdded = (1u << 1), + eBreakpointEventTypeRemoved = (1u << 2), + eBreakpointEventTypeLocationsAdded = (1u << 3), // Locations added doesn't get sent when the breakpoint is created + eBreakpointEventTypeLocationsRemoved = (1u << 4), + eBreakpointEventTypeLocationsResolved = (1u << 5), + eBreakpointEventTypeEnabled = (1u << 6), + eBreakpointEventTypeDisabled = (1u << 7), + eBreakpointEventTypeCommandChanged = (1u << 8), + eBreakpointEventTypeConditionChanged = (1u << 9), + eBreakpointEventTypeIgnoreChanged = (1u << 10), + eBreakpointEventTypeThreadChanged = (1u << 11) + } BreakpointEventType; + + typedef enum WatchpointEventType + { + eWatchpointEventTypeInvalidType = (1u << 0), + eWatchpointEventTypeAdded = (1u << 1), + eWatchpointEventTypeRemoved = (1u << 2), + eWatchpointEventTypeEnabled = (1u << 6), + eWatchpointEventTypeDisabled = (1u << 7), + eWatchpointEventTypeCommandChanged = (1u << 8), + eWatchpointEventTypeConditionChanged = (1u << 9), + eWatchpointEventTypeIgnoreChanged = (1u << 10), + eWatchpointEventTypeThreadChanged = (1u << 11), + eWatchpointEventTypeTypeChanged = (1u << 12) + } WatchpointEventType; + + + //---------------------------------------------------------------------- + /// Programming language type. + /// + /// These enumerations use the same language enumerations as the DWARF + /// specification for ease of use and consistency. + /// The enum -> string code is in LanguageRuntime.cpp, don't change this + /// table without updating that code as well. + //---------------------------------------------------------------------- + typedef enum LanguageType + { + eLanguageTypeUnknown = 0x0000, ///< Unknown or invalid language value. + eLanguageTypeC89 = 0x0001, ///< ISO C:1989. + eLanguageTypeC = 0x0002, ///< Non-standardized C, such as K&R. + eLanguageTypeAda83 = 0x0003, ///< ISO Ada:1983. + eLanguageTypeC_plus_plus = 0x0004, ///< ISO C++:1998. + eLanguageTypeCobol74 = 0x0005, ///< ISO Cobol:1974. + eLanguageTypeCobol85 = 0x0006, ///< ISO Cobol:1985. + eLanguageTypeFortran77 = 0x0007, ///< ISO Fortran 77. + eLanguageTypeFortran90 = 0x0008, ///< ISO Fortran 90. + eLanguageTypePascal83 = 0x0009, ///< ISO Pascal:1983. + eLanguageTypeModula2 = 0x000a, ///< ISO Modula-2:1996. + eLanguageTypeJava = 0x000b, ///< Java. + eLanguageTypeC99 = 0x000c, ///< ISO C:1999. + eLanguageTypeAda95 = 0x000d, ///< ISO Ada:1995. + eLanguageTypeFortran95 = 0x000e, ///< ISO Fortran 95. + eLanguageTypePLI = 0x000f, ///< ANSI PL/I:1976. + eLanguageTypeObjC = 0x0010, ///< Objective-C. + eLanguageTypeObjC_plus_plus = 0x0011, ///< Objective-C++. + eLanguageTypeUPC = 0x0012, ///< Unified Parallel C. + eLanguageTypeD = 0x0013, ///< D. + eLanguageTypePython = 0x0014, ///< Python. + eNumLanguageTypes + } LanguageType; + + typedef enum DynamicValueType + { + eNoDynamicValues = 0, + eDynamicCanRunTarget = 1, + eDynamicDontRunTarget = 2 + } DynamicValueType; + + typedef enum AccessType + { + eAccessNone, + eAccessPublic, + eAccessPrivate, + eAccessProtected, + eAccessPackage + } AccessType; + + typedef enum CommandArgumentType + { + eArgTypeAddress = 0, + eArgTypeAddressOrExpression, + eArgTypeAliasName, + eArgTypeAliasOptions, + eArgTypeArchitecture, + eArgTypeBoolean, + eArgTypeBreakpointID, + eArgTypeBreakpointIDRange, + eArgTypeByteSize, + eArgTypeClassName, + eArgTypeCommandName, + eArgTypeCount, + eArgTypeDirectoryName, + eArgTypeDisassemblyFlavor, + eArgTypeEndAddress, + eArgTypeExpression, + eArgTypeExpressionPath, + eArgTypeExprFormat, + eArgTypeFilename, + eArgTypeFormat, + eArgTypeFrameIndex, + eArgTypeFullName, + eArgTypeFunctionName, + eArgTypeFunctionOrSymbol, + eArgTypeGDBFormat, + eArgTypeIndex, + eArgTypeLanguage, + eArgTypeLineNum, + eArgTypeLogCategory, + eArgTypeLogChannel, + eArgTypeMethod, + eArgTypeName, + eArgTypeNewPathPrefix, + eArgTypeNumLines, + eArgTypeNumberPerLine, + eArgTypeOffset, + eArgTypeOldPathPrefix, + eArgTypeOneLiner, + eArgTypePid, + eArgTypePlugin, + eArgTypeProcessName, + eArgTypePythonClass, + eArgTypePythonFunction, + eArgTypePythonScript, + eArgTypeQueueName, + eArgTypeRegisterName, + eArgTypeRegularExpression, + eArgTypeRunArgs, + eArgTypeRunMode, + eArgTypeScriptedCommandSynchronicity, + eArgTypeScriptLang, + eArgTypeSearchWord, + eArgTypeSelector, + eArgTypeSettingIndex, + eArgTypeSettingKey, + eArgTypeSettingPrefix, + eArgTypeSettingVariableName, + eArgTypeShlibName, + eArgTypeSourceFile, + eArgTypeSortOrder, + eArgTypeStartAddress, + eArgTypeSummaryString, + eArgTypeSymbol, + eArgTypeThreadID, + eArgTypeThreadIndex, + eArgTypeThreadName, + eArgTypeUnsignedInteger, + eArgTypeUnixSignal, + eArgTypeVarName, + eArgTypeValue, + eArgTypeWidth, + eArgTypeNone, + eArgTypePlatform, + eArgTypeWatchpointID, + eArgTypeWatchpointIDRange, + eArgTypeWatchType, + eArgTypeLastArg // Always keep this entry as the last entry in this enumeration!! + } CommandArgumentType; + + //---------------------------------------------------------------------- + // Symbol types + //---------------------------------------------------------------------- + typedef enum SymbolType + { + eSymbolTypeAny = 0, + eSymbolTypeInvalid = 0, + eSymbolTypeAbsolute, + eSymbolTypeCode, + eSymbolTypeResolver, + eSymbolTypeData, + eSymbolTypeTrampoline, + eSymbolTypeRuntime, + eSymbolTypeException, + eSymbolTypeSourceFile, + eSymbolTypeHeaderFile, + eSymbolTypeObjectFile, + eSymbolTypeCommonBlock, + eSymbolTypeBlock, + eSymbolTypeLocal, + eSymbolTypeParam, + eSymbolTypeVariable, + eSymbolTypeVariableType, + eSymbolTypeLineEntry, + eSymbolTypeLineHeader, + eSymbolTypeScopeBegin, + eSymbolTypeScopeEnd, + eSymbolTypeAdditional, // When symbols take more than one entry, the extra entries get this type + eSymbolTypeCompiler, + eSymbolTypeInstrumentation, + eSymbolTypeUndefined, + eSymbolTypeObjCClass, + eSymbolTypeObjCMetaClass, + eSymbolTypeObjCIVar + } SymbolType; + + typedef enum SectionType + { + eSectionTypeInvalid, + eSectionTypeCode, + eSectionTypeContainer, // The section contains child sections + eSectionTypeData, + eSectionTypeDataCString, // Inlined C string data + eSectionTypeDataCStringPointers, // Pointers to C string data + eSectionTypeDataSymbolAddress, // Address of a symbol in the symbol table + eSectionTypeData4, + eSectionTypeData8, + eSectionTypeData16, + eSectionTypeDataPointers, + eSectionTypeDebug, + eSectionTypeZeroFill, + eSectionTypeDataObjCMessageRefs, // Pointer to function pointer + selector + eSectionTypeDataObjCCFStrings, // Objective C const CFString/NSString objects + eSectionTypeDWARFDebugAbbrev, + eSectionTypeDWARFDebugAranges, + eSectionTypeDWARFDebugFrame, + eSectionTypeDWARFDebugInfo, + eSectionTypeDWARFDebugLine, + eSectionTypeDWARFDebugLoc, + eSectionTypeDWARFDebugMacInfo, + eSectionTypeDWARFDebugPubNames, + eSectionTypeDWARFDebugPubTypes, + eSectionTypeDWARFDebugRanges, + eSectionTypeDWARFDebugStr, + eSectionTypeDWARFAppleNames, + eSectionTypeDWARFAppleTypes, + eSectionTypeDWARFAppleNamespaces, + eSectionTypeDWARFAppleObjC, + eSectionTypeELFSymbolTable, // Elf SHT_SYMTAB section + eSectionTypeELFDynamicSymbols, // Elf SHT_DYNSYM section + eSectionTypeELFRelocationEntries, // Elf SHT_REL or SHT_REL section + eSectionTypeELFDynamicLinkInfo, // Elf SHT_DYNAMIC section + eSectionTypeEHFrame, + eSectionTypeOther + + } SectionType; + + typedef enum EmulateInstructionOptions + { + eEmulateInstructionOptionNone = (0u), + eEmulateInstructionOptionAutoAdvancePC = (1u << 0), + eEmulateInstructionOptionIgnoreConditions = (1u << 1) + } EmulateInstructionOptions; + + typedef enum FunctionNameType + { + eFunctionNameTypeNone = 0u, + eFunctionNameTypeAuto = (1u << 1), // Automatically figure out which FunctionNameType + // bits to set based on the function name. + eFunctionNameTypeFull = (1u << 2), // The function name. + // For C this is the same as just the name of the function + // For C++ this is the mangled or demangled version of the mangled name. + // For ObjC this is the full function signature with the + or + // - and the square brackets and the class and selector + eFunctionNameTypeBase = (1u << 3), // The function name only, no namespaces or arguments and no class + // methods or selectors will be searched. + eFunctionNameTypeMethod = (1u << 4), // Find function by method name (C++) with no namespace or arguments + eFunctionNameTypeSelector = (1u << 5), // Find function by selector name (ObjC) names + eFunctionNameTypeAny = eFunctionNameTypeAuto // DEPRECATED: use eFunctionNameTypeAuto + } FunctionNameType; + + + //---------------------------------------------------------------------- + // Basic types enumeration for the public API SBType::GetBasicType() + //---------------------------------------------------------------------- + typedef enum BasicType + { + eBasicTypeInvalid = 0, + eBasicTypeVoid = 1, + eBasicTypeChar, + eBasicTypeSignedChar, + eBasicTypeUnsignedChar, + eBasicTypeWChar, + eBasicTypeSignedWChar, + eBasicTypeUnsignedWChar, + eBasicTypeChar16, + eBasicTypeChar32, + eBasicTypeShort, + eBasicTypeUnsignedShort, + eBasicTypeInt, + eBasicTypeUnsignedInt, + eBasicTypeLong, + eBasicTypeUnsignedLong, + eBasicTypeLongLong, + eBasicTypeUnsignedLongLong, + eBasicTypeInt128, + eBasicTypeUnsignedInt128, + eBasicTypeBool, + eBasicTypeHalf, + eBasicTypeFloat, + eBasicTypeDouble, + eBasicTypeLongDouble, + eBasicTypeFloatComplex, + eBasicTypeDoubleComplex, + eBasicTypeLongDoubleComplex, + eBasicTypeObjCID, + eBasicTypeObjCClass, + eBasicTypeObjCSel, + eBasicTypeNullPtr, + eBasicTypeOther + } BasicType; + + typedef enum TypeClass + { + eTypeClassInvalid = (0u), + eTypeClassArray = (1u << 0), + eTypeClassBlockPointer = (1u << 1), + eTypeClassBuiltin = (1u << 2), + eTypeClassClass = (1u << 3), + eTypeClassComplexFloat = (1u << 4), + eTypeClassComplexInteger = (1u << 5), + eTypeClassEnumeration = (1u << 6), + eTypeClassFunction = (1u << 7), + eTypeClassMemberPointer = (1u << 8), + eTypeClassObjCObject = (1u << 9), + eTypeClassObjCInterface = (1u << 10), + eTypeClassObjCObjectPointer = (1u << 11), + eTypeClassPointer = (1u << 12), + eTypeClassReference = (1u << 13), + eTypeClassStruct = (1u << 14), + eTypeClassTypedef = (1u << 15), + eTypeClassUnion = (1u << 16), + eTypeClassVector = (1u << 17), + // Define the last type class as the MSBit of a 32 bit value + eTypeClassOther = (1u << 31), + // Define a mask that can be used for any type when finding types + eTypeClassAny = (0xffffffffu) + } TypeClass; + + typedef enum TemplateArgumentKind + { + eTemplateArgumentKindNull = 0, + eTemplateArgumentKindType, + eTemplateArgumentKindDeclaration, + eTemplateArgumentKindIntegral, + eTemplateArgumentKindTemplate, + eTemplateArgumentKindTemplateExpansion, + eTemplateArgumentKindExpression, + eTemplateArgumentKindPack + + } TemplateArgumentKind; + + //---------------------------------------------------------------------- + // Options that can be set for a formatter to alter its behavior + // Not all of these are applicable to all formatter types + //---------------------------------------------------------------------- + typedef enum TypeOptions + { + eTypeOptionNone = (0u), + eTypeOptionCascade = (1u << 0), + eTypeOptionSkipPointers = (1u << 1), + eTypeOptionSkipReferences = (1u << 2), + eTypeOptionHideChildren = (1u << 3), + eTypeOptionHideValue = (1u << 4), + eTypeOptionShowOneLiner = (1u << 5), + eTypeOptionHideNames = (1u << 6) + } TypeOptions; + + //---------------------------------------------------------------------- + // This is the return value for frame comparisons. When frame A pushes + // frame B onto the stack, frame A is OLDER than frame B. + //---------------------------------------------------------------------- + typedef enum FrameComparison + { + eFrameCompareInvalid, + eFrameCompareUnknown, + eFrameCompareEqual, + eFrameCompareYounger, + eFrameCompareOlder + } FrameComparison; + + //---------------------------------------------------------------------- + // Address Class + // + // A way of classifying an address used for disassembling and setting + // breakpoints. Many object files can track exactly what parts of their + // object files are code, data and other information. This is of course + // above and beyond just looking at the section types. For example, code + // might contain PC relative data and the object file might be able to + // tell us that an address in code is data. + //---------------------------------------------------------------------- + typedef enum AddressClass + { + eAddressClassInvalid, + eAddressClassUnknown, + eAddressClassCode, + eAddressClassCodeAlternateISA, + eAddressClassData, + eAddressClassDebug, + eAddressClassRuntime + } AddressClass; + +} // namespace lldb + + +#endif // LLDB_lldb_enumerations_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/lldb-forward.h b/contrib/llvm/tools/lldb/include/lldb/lldb-forward.h new file mode 100644 index 00000000000..84af8b64690 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/lldb-forward.h @@ -0,0 +1,378 @@ +//===-- lldb-forward.h ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_lldb_forward_h_ +#define LLDB_lldb_forward_h_ + +#if defined(__cplusplus) + +#include "lldb/Utility/SharingPtr.h" + +//---------------------------------------------------------------------- +// lldb forward declarations +//---------------------------------------------------------------------- +namespace lldb_private { + +class ABI; +class Address; +class AddressImpl; +class AddressRange; +class AddressResolver; +class ArchSpec; +class Args; +class ASTResultSynthesizer; +class Baton; +class Block; +class Breakpoint; +class BreakpointID; +class BreakpointIDList; +class BreakpointList; +class BreakpointLocation; +class BreakpointLocationCollection; +class BreakpointLocationList; +class BreakpointOptions; +class BreakpointResolver; +class BreakpointSite; +class BreakpointSiteList; +class BroadcastEventSpec; +class Broadcaster; +class BroadcasterManager; +class CPPLanguageRuntime; +class ClangASTContext; +class ClangASTImporter; +class ClangASTMetadata; +class ClangASTSource; +class ClangASTType; +class ClangNamespaceDecl; +class ClangExpression; +class ClangExpressionDeclMap; +class ClangExpressionParser; +class ClangExpressionVariable; +class ClangExpressionVariableList; +class ClangExpressionVariableList; +class ClangExpressionVariables; +class ClangFunction; +class ClangPersistentVariables; +class ClangUserExpression; +class ClangUtilityFunction; +class CommandInterpreter; +class CommandObject; +class CommandReturnObject; +class Communication; +class CompileUnit; +class Condition; +class Connection; +class ConnectionFileDescriptor; +class ConstString; +class CXXSyntheticChildren; +class DWARFCallFrameInfo; +class DWARFExpression; +class DataBuffer; +class DataEncoder; +class DataExtractor; +class Debugger; +class Declaration; +class Disassembler; +class DynamicLibrary; +class DynamicLoader; +class EmulateInstruction; +class Error; +class EvaluateExpressionOptions; +class Event; +class EventData; +class ExecutionContext; +class ExecutionContextRef; +class ExecutionContextRefLocker; +class ExecutionContextScope; +class FileSpec; +class FileSpecList; +class Flags; +class TypeCategoryImpl; +class FormatManager; +class FuncUnwinders; +class Function; +class FunctionInfo; +class InlineFunctionInfo; +class InputReader; +class Instruction; +class InstructionList; +class IRExecutionUnit; +class LanguageRuntime; +class LineTable; +class Listener; +class Log; +class LogChannel; +class Mangled; +class Materializer; +class Module; +class ModuleList; +class ModuleSpec; +class ModuleSpecList; +class Mutex; +struct NameSearchContext; +class ObjCLanguageRuntime; +class ObjectContainer; +class OptionGroup; +class OptionGroupPlatform; +class ObjectFile; +class OperatingSystem; +class Options; +class OptionValue; +class OptionValueArch; +class OptionValueArgs; +class OptionValueArray; +class OptionValueBoolean; +class OptionValueDictionary; +class OptionValueEnumeration; +class OptionValueFileSpec; +class OptionValueFileSpecList; +class OptionValueFormat; +class OptionValuePathMappings; +class OptionValueProperties; +class OptionValueRegex; +class OptionValueSInt64; +class OptionValueString; +class OptionValueUInt64; +class OptionValueUUID; +class NamedOption; +class PathMappingList; +class Platform; +class Process; +class ProcessAttachInfo; +class ProcessModID; +class ProcessInfo; +class ProcessInstanceInfo; +class ProcessInstanceInfoList; +class ProcessInstanceInfoMatch; +class ProcessLaunchInfo; +class Property; +struct PropertyDefinition; +class PythonArray; +class PythonDictionary; +class PythonInteger; +class PythonObject; +class PythonString; +class RegisterContext; +class RegisterLocation; +class RegisterLocationList; +class RegisterValue; +class RegularExpression; +class Scalar; +class ScriptInterpreter; +class ScriptInterpreterLocker; +class ScriptInterpreterObject; +#ifndef LLDB_DISABLE_PYTHON +class ScriptInterpreterPython; +struct ScriptSummaryFormat; +#endif +class SearchFilter; +class Section; +class SectionImpl; +class SectionList; +class Settings; +class SourceManager; +class SourceManagerImpl; +class StackFrame; +class StackFrameImpl; +class StackFrameList; +class StackID; +class StopInfo; +class Stoppoint; +class StoppointCallbackContext; +class StoppointLocation; +class Stream; +template class StreamBuffer; +class StreamFile; +class StreamString; +class StringList; +struct StringSummaryFormat; +class TypeSummaryImpl; +class Symbol; +class SymbolContext; +class SymbolContextList; +class SymbolContextScope; +class SymbolContextSpecifier; +class SymbolFile; +class SymbolFileType; +class SymbolVendor; +class Symtab; +class SyntheticChildren; +class SyntheticChildrenFrontEnd; +class TypeFilterImpl; +#ifndef LLDB_DISABLE_PYTHON +class ScriptedSyntheticChildren; +#endif +class Target; +class TargetList; +class Thread; +class ThreadList; +class ThreadPlan; +class ThreadPlanBase; +class ThreadPlanRunToAddress; +class ThreadPlanStepInstruction; +class ThreadPlanStepOut; +class ThreadPlanStepOverBreakpoint; +class ThreadPlanStepRange; +class ThreadPlanStepThrough; +class ThreadPlanTracer; +class ThreadSpec; +class TimeValue; +class Type; +class TypeCategoryMap; +class TypeImpl; +class TypeAndOrName; +class TypeList; +class TypeListImpl; +class TypeMemberImpl; +class TypeNameSpecifierImpl; +class UUID; +class Unwind; +class UnwindAssembly; +class UnwindPlan; +class UnwindTable; +class VMRange; +class Value; +class TypeFormatImpl; +class ValueList; +class ValueObject; +class ValueObjectChild; +class ValueObjectConstResult; +class ValueObjectConstResultChild; +class ValueObjectConstResultImpl; +class ValueObjectList; +class Variable; +class VariableList; +class Watchpoint; +class WatchpointList; +class WatchpointOptions; +struct LineEntry; + +} // namespace lldb_private + +//---------------------------------------------------------------------- +// lldb forward declarations +//---------------------------------------------------------------------- +namespace lldb { + + typedef std::shared_ptr ABISP; + typedef std::shared_ptr BatonSP; + typedef std::shared_ptr BlockSP; + typedef std::shared_ptr BreakpointSP; + typedef std::weak_ptr BreakpointWP; + typedef std::shared_ptr BreakpointSiteSP; + typedef std::weak_ptr BreakpointSiteWP; + typedef std::shared_ptr BreakpointLocationSP; + typedef std::weak_ptr BreakpointLocationWP; + typedef std::shared_ptr BreakpointResolverSP; + typedef std::shared_ptr BroadcasterSP; + typedef std::shared_ptr ClangExpressionVariableSP; + typedef std::shared_ptr CommandObjectSP; + typedef std::shared_ptr CommunicationSP; + typedef std::shared_ptr ConnectionSP; + typedef std::shared_ptr CompUnitSP; + typedef std::shared_ptr DataBufferSP; + typedef std::shared_ptr DataExtractorSP; + typedef std::shared_ptr DebuggerSP; + typedef std::weak_ptr DebuggerWP; + typedef std::shared_ptr DisassemblerSP; + typedef std::shared_ptr DynamicLibrarySP; + typedef std::shared_ptr DynamicLoaderSP; + typedef std::shared_ptr EventSP; + typedef std::shared_ptr ExecutionContextRefSP; + typedef std::shared_ptr FunctionSP; + typedef std::shared_ptr FuncUnwindersSP; + typedef std::shared_ptr InlineFunctionInfoSP; + typedef std::shared_ptr InputReaderSP; + typedef std::shared_ptr InstructionSP; + typedef std::shared_ptr LanguageRuntimeSP; + typedef std::shared_ptr LineTableSP; + typedef std::shared_ptr ListenerSP; + typedef std::shared_ptr LogChannelSP; + typedef std::shared_ptr ModuleSP; + typedef std::weak_ptr ModuleWP; + typedef std::shared_ptr ObjectFileSP; + typedef std::weak_ptr ObjectFileWP; + typedef std::shared_ptr OptionValueSP; + typedef std::weak_ptr OptionValueWP; + typedef std::shared_ptr OptionValueArchSP; + typedef std::shared_ptr OptionValueArgsSP; + typedef std::shared_ptr OptionValueArraySP; + typedef std::shared_ptr OptionValueBooleanSP; + typedef std::shared_ptr OptionValueDictionarySP; + typedef std::shared_ptr OptionValueFileSpecSP; + typedef std::shared_ptr OptionValueFileSpecListSP; + typedef std::shared_ptr OptionValueFormatSP; + typedef std::shared_ptr OptionValuePathMappingsSP; + typedef std::shared_ptr OptionValuePropertiesSP; + typedef std::shared_ptr OptionValueRegexSP; + typedef std::shared_ptr OptionValueSInt64SP; + typedef std::shared_ptr OptionValueStringSP; + typedef std::shared_ptr OptionValueUInt64SP; + typedef std::shared_ptr OptionValueUUIDSP; + typedef std::shared_ptr PlatformSP; + typedef std::shared_ptr ProcessSP; + typedef std::shared_ptr ProcessAttachInfoSP; + typedef std::shared_ptr ProcessLaunchInfoSP; + typedef std::weak_ptr ProcessWP; + typedef std::shared_ptr PropertySP; + typedef std::shared_ptr RegisterContextSP; + typedef std::shared_ptr RegularExpressionSP; + typedef std::shared_ptr ScriptInterpreterObjectSP; +#ifndef LLDB_DISABLE_PYTHON + typedef std::shared_ptr ScriptSummaryFormatSP; +#endif // #ifndef LLDB_DISABLE_PYTHON + typedef std::shared_ptr SectionSP; + typedef std::weak_ptr SectionWP; + typedef std::shared_ptr SearchFilterSP; + typedef std::shared_ptr SettingsSP; + typedef std::shared_ptr StackFrameSP; + typedef std::weak_ptr StackFrameWP; + typedef std::shared_ptr StackFrameListSP; + typedef std::shared_ptr StopInfoSP; + typedef std::shared_ptr StoppointLocationSP; + typedef std::shared_ptr StreamSP; + typedef std::weak_ptr StreamWP; + typedef std::shared_ptr StringTypeSummaryImplSP; + typedef std::shared_ptr SymbolFileSP; + typedef std::shared_ptr SymbolFileTypeSP; + typedef std::weak_ptr SymbolFileTypeWP; + typedef std::shared_ptr SymbolContextSpecifierSP; + typedef std::shared_ptr SyntheticChildrenSP; + typedef std::shared_ptr SyntheticChildrenFrontEndSP; + typedef std::shared_ptr TargetSP; + typedef std::weak_ptr TargetWP; + typedef std::shared_ptr ThreadSP; + typedef std::weak_ptr ThreadWP; + typedef std::shared_ptr ThreadPlanSP; + typedef std::shared_ptr ThreadPlanTracerSP; + typedef std::shared_ptr TypeSP; + typedef std::weak_ptr TypeWP; + typedef std::shared_ptr TypeCategoryImplSP; + typedef std::shared_ptr TypeImplSP; + typedef std::shared_ptr TypeFilterImplSP; + typedef std::shared_ptr TypeFormatImplSP; + typedef std::shared_ptr TypeNameSpecifierImplSP; + typedef std::shared_ptr TypeSummaryImplSP; +#ifndef LLDB_DISABLE_PYTHON + typedef std::shared_ptr ScriptedSyntheticChildrenSP; +#endif + typedef std::shared_ptr UnwindPlanSP; + typedef lldb_private::SharingPtr ValueObjectSP; + typedef std::shared_ptr ValueSP; + typedef std::shared_ptr ValueListSP; + typedef std::shared_ptr VariableSP; + typedef std::shared_ptr VariableListSP; + typedef std::shared_ptr ValueObjectListSP; + typedef std::shared_ptr WatchpointSP; + +} // namespace lldb + + +#endif // #if defined(__cplusplus) +#endif // LLDB_lldb_forward_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/lldb-private-enumerations.h b/contrib/llvm/tools/lldb/include/lldb/lldb-private-enumerations.h new file mode 100644 index 00000000000..60c90907145 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/lldb-private-enumerations.h @@ -0,0 +1,245 @@ +//===-- lldb-private-enumerations.h -----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_lldb_private_enumerations_h_ +#define LLDB_lldb_private_enumerations_h_ + +namespace lldb_private { + +//---------------------------------------------------------------------- +// Thread Step Types +//---------------------------------------------------------------------- +typedef enum StepType +{ + eStepTypeNone, + eStepTypeTrace, ///< Single step one instruction. + eStepTypeTraceOver, ///< Single step one instruction, stepping over. + eStepTypeInto, ///< Single step into a specified context. + eStepTypeOver, ///< Single step over a specified context. + eStepTypeOut ///< Single step out a specified context. +} StepType; + +//---------------------------------------------------------------------- +// Address Types +//---------------------------------------------------------------------- +typedef enum AddressType +{ + eAddressTypeInvalid = 0, + eAddressTypeFile, ///< Address is an address as found in an object or symbol file + eAddressTypeLoad, ///< Address is an address as in the current target inferior process + eAddressTypeHost ///< Address is an address in the process that is running this code +} AddressType; + +//---------------------------------------------------------------------- +// Votes - Need a tri-state, yes, no, no opinion... +//---------------------------------------------------------------------- +typedef enum Vote +{ + eVoteNo = -1, + eVoteNoOpinion = 0, + eVoteYes = 1 +} Vote; + +typedef enum ArchitectureType +{ + eArchTypeInvalid, + eArchTypeMachO, + eArchTypeELF, + kNumArchTypes +} ArchitectureType; + +//---------------------------------------------------------------------- +/// Settable state variable types. +/// +//---------------------------------------------------------------------- + +//typedef enum SettableVariableType +//{ +// eSetVarTypeInt, +// eSetVarTypeBoolean, +// eSetVarTypeString, +// eSetVarTypeArray, +// eSetVarTypeDictionary, +// eSetVarTypeEnum, +// eSetVarTypeNone +//} SettableVariableType; + +typedef enum VarSetOperationType +{ + eVarSetOperationReplace, + eVarSetOperationInsertBefore, + eVarSetOperationInsertAfter, + eVarSetOperationRemove, + eVarSetOperationAppend, + eVarSetOperationClear, + eVarSetOperationAssign, + eVarSetOperationInvalid +} VarSetOperationType; + +typedef enum ArgumentRepetitionType +{ + eArgRepeatPlain, // Exactly one occurrence + eArgRepeatOptional, // At most one occurrence, but it's optional + eArgRepeatPlus, // One or more occurrences + eArgRepeatStar, // Zero or more occurrences + eArgRepeatRange, // Repetition of same argument, from 1 to n + eArgRepeatPairPlain, // A pair of arguments that must always go together ([arg-type arg-value]), occurs exactly once + eArgRepeatPairOptional, // A pair that occurs at most once (optional) + eArgRepeatPairPlus, // One or more occurrences of a pair + eArgRepeatPairStar, // Zero or more occurrences of a pair + eArgRepeatPairRange, // A pair that repeats from 1 to n + eArgRepeatPairRangeOptional // A pair that repeats from 1 to n, but is optional +} ArgumentRepetitionType; + +typedef enum SortOrder +{ + eSortOrderNone, + eSortOrderByAddress, + eSortOrderByName +} SortOrder; + + +//---------------------------------------------------------------------- +// Used in conjunction with Host::GetLLDBPath () to find files that +// are related to +//---------------------------------------------------------------------- +typedef enum PathType +{ + ePathTypeLLDBShlibDir, // The directory where the lldb.so (unix) or LLDB mach-o file in LLDB.framework (MacOSX) exists + ePathTypeSupportExecutableDir, // Find LLDB support executable directory (debugserver, etc) + ePathTypeHeaderDir, // Find LLDB header file directory + ePathTypePythonDir, // Find Python modules (PYTHONPATH) directory + ePathTypeLLDBSystemPlugins, // System plug-ins directory + ePathTypeLLDBUserPlugins // User plug-ins directory +} PathType; + + +//---------------------------------------------------------------------- +// We can execute ThreadPlans on one thread with various fall-back modes +// (try other threads after timeout, etc.) This enum gives the result of +// thread plan executions. +//---------------------------------------------------------------------- +typedef enum ExecutionResults +{ + eExecutionSetupError, + eExecutionCompleted, + eExecutionDiscarded, + eExecutionInterrupted, + eExecutionHitBreakpoint, + eExecutionTimedOut +} ExecutionResults; + +typedef enum ObjCRuntimeVersions { + eObjC_VersionUnknown = 0, + eAppleObjC_V1 = 1, + eAppleObjC_V2 = 2 +} ObjCRuntimeVersions; + + +//---------------------------------------------------------------------- +// LazyBool is for boolean values that need to be calculated lazily. +// Values start off set to eLazyBoolCalculate, and then they can be +// calculated once and set to eLazyBoolNo or eLazyBoolYes. +//---------------------------------------------------------------------- +typedef enum LazyBool { + eLazyBoolCalculate = -1, + eLazyBoolNo = 0, + eLazyBoolYes = 1 +} LazyBool; + +//------------------------------------------------------------------ +/// Name matching +//------------------------------------------------------------------ +typedef enum NameMatchType +{ + eNameMatchIgnore, + eNameMatchEquals, + eNameMatchContains, + eNameMatchStartsWith, + eNameMatchEndsWith, + eNameMatchRegularExpression + +} NameMatchType; + + +//------------------------------------------------------------------ +/// Instruction types +//------------------------------------------------------------------ +typedef enum InstructionType +{ + eInstructionTypeAny, // Support for any instructions at all (at least one) + eInstructionTypePrologueEpilogue, // All prologue and epilogue instructons that push and pop register values and modify sp/fp + eInstructionTypePCModifying, // Any instruction that modifies the program counter/instruction pointer + eInstructionTypeAll // All instructions of any kind + +} InstructionType; + + +//------------------------------------------------------------------ +/// Format category entry types +//------------------------------------------------------------------ +typedef enum FormatCategoryItem +{ + eFormatCategoryItemSummary = 0x0001, + eFormatCategoryItemRegexSummary = 0x0002, + eFormatCategoryItemFilter = 0x0004, + eFormatCategoryItemRegexFilter = 0x0008, + eFormatCategoryItemSynth = 0x0010, + eFormatCategoryItemRegexSynth = 0x0020 +} FormatCategoryItem; + +//------------------------------------------------------------------ +/// Expression execution policies +//------------------------------------------------------------------ +typedef enum { + eExecutionPolicyOnlyWhenNeeded, + eExecutionPolicyNever, + eExecutionPolicyAlways +} ExecutionPolicy; + +//---------------------------------------------------------------------- +// Ways that the FormatManager picks a particular format for a type +//---------------------------------------------------------------------- +typedef enum FormatterChoiceCriterion +{ + eFormatterChoiceCriterionDirectChoice = 0x00000000, + eFormatterChoiceCriterionStrippedPointerReference = 0x00000001, + eFormatterChoiceCriterionNavigatedTypedefs = 0x00000002, + eFormatterChoiceCriterionRegularExpressionSummary = 0x00000004, + eFormatterChoiceCriterionRegularExpressionFilter = 0x00000004, + eFormatterChoiceCriterionDynamicObjCDiscovery = 0x00000008, + eFormatterChoiceCriterionStrippedBitField = 0x00000010, + eFormatterChoiceCriterionWentToStaticValue = 0x00000020 +} FormatterChoiceCriterion; + +//---------------------------------------------------------------------- +// Synchronicity behavior of scripted commands +//---------------------------------------------------------------------- +typedef enum ScriptedCommandSynchronicity +{ + eScriptedCommandSynchronicitySynchronous, + eScriptedCommandSynchronicityAsynchronous, + eScriptedCommandSynchronicityCurrentValue // use whatever the current synchronicity is +} ScriptedCommandSynchronicity; + + +//---------------------------------------------------------------------- +// Loading modules from memory +//---------------------------------------------------------------------- +typedef enum MemoryModuleLoadLevel { + eMemoryModuleLoadLevelMinimal, // Load sections only + eMemoryModuleLoadLevelPartial, // Load function bounds but no symbols + eMemoryModuleLoadLevelComplete, // Load sections and all symbols +} MemoryModuleLoadLevel; + + +} // namespace lldb_private + + +#endif // LLDB_lldb_private_enumerations_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/lldb-private-interfaces.h b/contrib/llvm/tools/lldb/include/lldb/lldb-private-interfaces.h new file mode 100644 index 00000000000..949cafed98b --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/lldb-private-interfaces.h @@ -0,0 +1,46 @@ +//===-- lldb-private-interfaces.h -------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_lldb_private_interfaces_h_ +#define liblldb_lldb_private_interfaces_h_ + +#if defined(__cplusplus) + +#include "lldb/lldb-private.h" + +namespace lldb_private +{ + typedef lldb::ABISP (*ABICreateInstance) (const ArchSpec &arch); + typedef Disassembler* (*DisassemblerCreateInstance) (const ArchSpec &arch, const char *flavor); + typedef DynamicLoader* (*DynamicLoaderCreateInstance) (Process* process, bool force); + typedef ObjectContainer* (*ObjectContainerCreateInstance) (const lldb::ModuleSP &module_sp, lldb::DataBufferSP& data_sp, lldb::offset_t data_offset, const FileSpec *file, lldb::offset_t offset, lldb::offset_t length); + typedef size_t (*ObjectFileGetModuleSpecifications) (const FileSpec &file, lldb::DataBufferSP& data_sp, lldb::offset_t data_offset, lldb::offset_t file_offset, lldb::offset_t length, ModuleSpecList &module_specs); + typedef ObjectFile* (*ObjectFileCreateInstance) (const lldb::ModuleSP &module_sp, lldb::DataBufferSP& data_sp, lldb::offset_t data_offset, const FileSpec* file, lldb::offset_t file_offset, lldb::offset_t length); + typedef ObjectFile* (*ObjectFileCreateMemoryInstance) (const lldb::ModuleSP &module_sp, lldb::DataBufferSP& data_sp, const lldb::ProcessSP &process_sp, lldb::addr_t offset); + typedef LogChannel* (*LogChannelCreateInstance) (); + typedef EmulateInstruction * (*EmulateInstructionCreateInstance) (const ArchSpec &arch, InstructionType inst_type); + typedef OperatingSystem* (*OperatingSystemCreateInstance) (Process *process, bool force); + typedef LanguageRuntime *(*LanguageRuntimeCreateInstance) (Process *process, lldb::LanguageType language); + typedef Platform* (*PlatformCreateInstance) (bool force, const ArchSpec *arch); + typedef lldb::ProcessSP (*ProcessCreateInstance) (Target &target, Listener &listener, const FileSpec *crash_file_path); + typedef SymbolFile* (*SymbolFileCreateInstance) (ObjectFile* obj_file); + typedef SymbolVendor* (*SymbolVendorCreateInstance) (const lldb::ModuleSP &module_sp, lldb_private::Stream *feedback_strm); // Module can be NULL for default system symbol vendor + typedef bool (*BreakpointHitCallback) (void *baton, StoppointCallbackContext *context, lldb::user_id_t break_id, lldb::user_id_t break_loc_id); + typedef bool (*WatchpointHitCallback) (void *baton, StoppointCallbackContext *context, lldb::user_id_t watch_id); + typedef lldb::ThreadPlanSP (*ThreadPlanShouldStopHereCallback) (ThreadPlan *current_plan, Flags &flags, void *baton); + typedef UnwindAssembly* (*UnwindAssemblyCreateInstance) (const ArchSpec &arch); + typedef int (*ComparisonFunction)(const void *, const void *); + typedef bool (*CommandOverrideCallback)(void *baton, const char **argv); + typedef void (*DebuggerInitializeCallback)(Debugger &debugger); + +} // namespace lldb_private + +#endif // #if defined(__cplusplus) + +#endif // liblldb_lldb_private_interfaces_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/lldb-private-log.h b/contrib/llvm/tools/lldb/include/lldb/lldb-private-log.h new file mode 100644 index 00000000000..31a1c23c5e6 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/lldb-private-log.h @@ -0,0 +1,90 @@ +//===-- lldb-private-log.h --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_lldb_private_log_h_ +#define liblldb_lldb_private_log_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private.h" + +//---------------------------------------------------------------------- +// Log Bits specific to logging in lldb +//---------------------------------------------------------------------- +#define LIBLLDB_LOG_VERBOSE (1u << 0) +#define LIBLLDB_LOG_PROCESS (1u << 1) +#define LIBLLDB_LOG_THREAD (1u << 2) +#define LIBLLDB_LOG_DYNAMIC_LOADER (1u << 3) +#define LIBLLDB_LOG_EVENTS (1u << 4) +#define LIBLLDB_LOG_BREAKPOINTS (1u << 5) +#define LIBLLDB_LOG_WATCHPOINTS (1u << 6) +#define LIBLLDB_LOG_STEP (1u << 7) +#define LIBLLDB_LOG_EXPRESSIONS (1u << 8) +#define LIBLLDB_LOG_TEMPORARY (1u << 9) +#define LIBLLDB_LOG_STATE (1u << 10) +#define LIBLLDB_LOG_OBJECT (1u << 11) +#define LIBLLDB_LOG_COMMUNICATION (1u << 12) +#define LIBLLDB_LOG_CONNECTION (1u << 13) +#define LIBLLDB_LOG_HOST (1u << 14) +#define LIBLLDB_LOG_UNWIND (1u << 15) +#define LIBLLDB_LOG_API (1u << 16) +#define LIBLLDB_LOG_SCRIPT (1u << 17) +#define LIBLLDB_LOG_COMMANDS (1U << 18) +#define LIBLLDB_LOG_TYPES (1u << 19) +#define LIBLLDB_LOG_SYMBOLS (1u << 20) +#define LIBLLDB_LOG_MODULES (1u << 21) +#define LIBLLDB_LOG_TARGET (1u << 22) +#define LIBLLDB_LOG_MMAP (1u << 23) +#define LIBLLDB_LOG_OS (1u << 24) +#define LIBLLDB_LOG_ALL (UINT32_MAX) +#define LIBLLDB_LOG_DEFAULT (LIBLLDB_LOG_PROCESS |\ + LIBLLDB_LOG_THREAD |\ + LIBLLDB_LOG_DYNAMIC_LOADER |\ + LIBLLDB_LOG_BREAKPOINTS |\ + LIBLLDB_LOG_WATCHPOINTS |\ + LIBLLDB_LOG_STEP |\ + LIBLLDB_LOG_STATE |\ + LIBLLDB_LOG_SYMBOLS |\ + LIBLLDB_LOG_TARGET |\ + LIBLLDB_LOG_COMMANDS) + +namespace lldb_private { + +void +LogIfAllCategoriesSet (uint32_t mask, const char *format, ...); + +void +LogIfAnyCategoriesSet (uint32_t mask, const char *format, ...); + +Log * +GetLogIfAllCategoriesSet (uint32_t mask); + +Log * +GetLogIfAnyCategoriesSet (uint32_t mask); + +uint32_t +GetLogMask (); + +bool +IsLogVerbose (); + +void +DisableLog (const char **categories, Stream *feedback_strm); + +Log * +EnableLog (lldb::StreamSP &log_stream_sp, uint32_t log_options, const char **categories, Stream *feedback_strm); + +void +ListLogCategories (Stream *strm); + +} // namespace lldb_private + +#endif // liblldb_lldb_private_log_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/lldb-private-types.h b/contrib/llvm/tools/lldb/include/lldb/lldb-private-types.h new file mode 100644 index 00000000000..4340af114be --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/lldb-private-types.h @@ -0,0 +1,74 @@ +//===-- lldb-private-types.h ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_lldb_private_types_h_ +#define liblldb_lldb_private_types_h_ + +#if defined(__cplusplus) + +#include "lldb/lldb-private.h" + +namespace lldb_private +{ + //---------------------------------------------------------------------- + // Every register is described in detail including its name, alternate + // name (optional), encoding, size in bytes and the default display + // format. + //---------------------------------------------------------------------- + typedef struct + { + const char *name; // Name of this register, can't be NULL + const char *alt_name; // Alternate name of this register, can be NULL + uint32_t byte_size; // Size in bytes of the register + uint32_t byte_offset; // The byte offset in the register context data where this register's value is found + lldb::Encoding encoding; // Encoding of the register bits + lldb::Format format; // Default display format + uint32_t kinds[lldb::kNumRegisterKinds]; // Holds all of the various register numbers for all register kinds + uint32_t *value_regs; // List of registers that must be terminated with LLDB_INVALID_REGNUM + uint32_t *invalidate_regs; // List of registers that must be invalidated when this register is modified, list must be terminated with LLDB_INVALID_REGNUM + } RegisterInfo; + + //---------------------------------------------------------------------- + // Registers are grouped into register sets + //---------------------------------------------------------------------- + typedef struct + { + const char *name; // Name of this register set + const char *short_name; // A short name for this register set + size_t num_registers; // The number of registers in REGISTERS array below + const uint32_t *registers; // An array of register numbers in this set + } RegisterSet; + + typedef struct + { + int64_t value; + const char *string_value; + const char *usage; + } OptionEnumValueElement; + + typedef struct + { + uint32_t usage_mask; // Used to mark options that can be used together. If (1 << n & usage_mask) != 0 + // then this option belongs to option set n. + bool required; // This option is required (in the current usage level) + const char *long_option; // Full name for this option. + int short_option; // Single character for this option. + int option_has_arg; // no_argument, required_argument or optional_argument + OptionEnumValueElement *enum_values; // If non-NULL an array of enum values. + uint32_t completion_type; // Cookie the option class can use to do define the argument completion. + lldb::CommandArgumentType argument_type; // Type of argument this option takes + const char *usage_text; // Full text explaining what this options does and what (if any) argument to + // pass it. + } OptionDefinition; + +} // namespace lldb_private + +#endif // #if defined(__cplusplus) + +#endif // liblldb_lldb_private_types_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/lldb-private.h b/contrib/llvm/tools/lldb/include/lldb/lldb-private.h new file mode 100644 index 00000000000..90590601d94 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/lldb-private.h @@ -0,0 +1,84 @@ +//===-- lldb-private.h ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef lldb_lldb_private_h_ +#define lldb_lldb_private_h_ + +#if defined(__cplusplus) + +#include "lldb/lldb-public.h" +#include "lldb/lldb-private-enumerations.h" +#include "lldb/lldb-private-interfaces.h" +#include "lldb/lldb-private-log.h" +#include "lldb/lldb-private-types.h" + +namespace lldb_private { + +//------------------------------------------------------------------ +/// Initializes lldb. +/// +/// This function should be called prior to using any lldb +/// classes to ensure they have a chance to do any static +/// initialization that they need to do. +//------------------------------------------------------------------ +void +Initialize(); + + +//------------------------------------------------------------------ +/// Notifies any classes that lldb will be terminating soon. +/// +/// This function will be called when the Debugger shared instance +/// is being destructed and will give classes the ability to clean +/// up any threads or other resources they have that they might not +/// be able to clean up in their own destructors. +/// +/// Internal classes that need this ability will need to add their +/// void T::WillTerminate() method in the body of this function in +/// lldb.cpp to ensure it will get called. +/// +/// TODO: when we start having external plug-ins, we will need a way +/// for plug-ins to register a WillTerminate callback. +//------------------------------------------------------------------ +void +WillTerminate(); + +//------------------------------------------------------------------ +/// Terminates lldb +/// +/// This function optionally can be called when clients are done +/// using lldb functionality to free up any static resources +/// that have been allocated during initialization or during +/// function calls. No lldb functions should be called after +/// calling this function without again calling DCInitialize() +/// again. +//------------------------------------------------------------------ +void +Terminate(); + + +const char * +GetVersion (); + +const char * +GetVoteAsCString (Vote vote); + +const char * +GetSectionTypeAsCString (lldb::SectionType sect_type); + +bool +NameMatches (const char *name, NameMatchType match_type, const char *match); + +} // namespace lldb_private + + +#endif // defined(__cplusplus) + + +#endif // lldb_lldb_private_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/lldb-public.h b/contrib/llvm/tools/lldb/include/lldb/lldb-public.h new file mode 100644 index 00000000000..b010edf5859 --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/lldb-public.h @@ -0,0 +1,18 @@ +//===-- lldb-include.h ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_lldb_h_ +#define LLDB_lldb_h_ + +#include "lldb/lldb-defines.h" +#include "lldb/lldb-enumerations.h" +#include "lldb/lldb-forward.h" +#include "lldb/lldb-types.h" + +#endif // LLDB_lldb_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/lldb-python.h b/contrib/llvm/tools/lldb/include/lldb/lldb-python.h new file mode 100644 index 00000000000..229e3967e4a --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/lldb-python.h @@ -0,0 +1,29 @@ +//===-- lldb-python.h --------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_lldb_python_h_ +#define LLDB_lldb_python_h_ + +// Python.h needs to be included before any system headers in order to avoid redefinition of macros + +#ifdef LLDB_DISABLE_PYTHON + +// Python is disabled in this build + +#else + +#if defined (__APPLE__) +#include +#else +#include +#endif + +#endif // LLDB_DISABLE_PYTHON + +#endif // LLDB_lldb_python_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/lldb-types.h b/contrib/llvm/tools/lldb/include/lldb/lldb-types.h new file mode 100644 index 00000000000..2693c0c822b --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/lldb-types.h @@ -0,0 +1,83 @@ +//===-- lldb-types.h --------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_lldb_types_h_ +#define LLDB_lldb_types_h_ + +#include "lldb/lldb-enumerations.h" +#include "lldb/lldb-forward.h" + +#include +#include +#include +#include +#include +#include + +//---------------------------------------------------------------------- +// All host systems must define: +// lldb::condition_t The native condition type (or a substitute class) for conditions on the host system. +// lldb::mutex_t The native mutex type for mutex objects on the host system. +// lldb::thread_t The native thread type for spawned threads on the system +// lldb::thread_arg_t The type of the one any only thread creation argument for the host system +// lldb::thread_result_t The return type that gets returned when a thread finishes. +// lldb::thread_func_t The function prototype used to spawn a thread on the host system. +// #define LLDB_INVALID_PROCESS_ID ... +// #define LLDB_INVALID_THREAD_ID ... +// #define LLDB_INVALID_HOST_THREAD ... +// #define IS_VALID_LLDB_HOST_THREAD ... +//---------------------------------------------------------------------- + +// TODO: Add a bunch of ifdefs to determine the host system and what +// things should be defined. Currently MacOSX is being assumed by default +// since that is what lldb was first developed for. + +namespace lldb { + //---------------------------------------------------------------------- + // MacOSX Types + //---------------------------------------------------------------------- + typedef ::pthread_mutex_t mutex_t; + typedef pthread_cond_t condition_t; + typedef pthread_t thread_t; // Host thread type + typedef void * thread_arg_t; // Host thread argument type + typedef void * thread_result_t; // Host thread result type + typedef void * (*thread_func_t)(void *); // Host thread function type + typedef void (*LogOutputCallback) (const char *, void *baton); + typedef bool (*CommandOverrideCallback)(void *baton, const char **argv); +} // namespace lldb + +#if defined(__MINGW32__) + +const lldb::thread_t lldb_invalid_host_thread_const = { NULL, 0 } ; +#define LLDB_INVALID_HOST_THREAD (lldb_invalid_host_thread_const) +#define IS_VALID_LLDB_HOST_THREAD(t) (!(NULL == (t).p && 0 == (t).x)) + +#else + +#define LLDB_INVALID_HOST_THREAD ((lldb::thread_t)NULL) +#define IS_VALID_LLDB_HOST_THREAD(t) ((t) != LLDB_INVALID_HOST_THREAD) + +#endif + +#define LLDB_INVALID_HOST_TIME { 0, 0 } + +namespace lldb +{ + typedef uint64_t addr_t; + typedef uint64_t user_id_t; + typedef uint64_t pid_t; + typedef uint64_t tid_t; + typedef uint64_t offset_t; + typedef int32_t break_id_t; + typedef int32_t watch_id_t; + typedef void * clang_type_t; +} + + +#endif // LLDB_lldb_types_h_ diff --git a/contrib/llvm/tools/lldb/include/lldb/lldb-versioning.h b/contrib/llvm/tools/lldb/include/lldb/lldb-versioning.h new file mode 100644 index 00000000000..8ccc67d8e9c --- /dev/null +++ b/contrib/llvm/tools/lldb/include/lldb/lldb-versioning.h @@ -0,0 +1,1607 @@ +//===-- lldb-versioning.h ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_lldb_versioning_h_ +#define LLDB_lldb_versioning_h_ + +//---------------------------------------------------------------------- +// LLDB API version +//---------------------------------------------------------------------- +#define LLDB_API_MAJOR_VERSION 1 +#define LLDB_API_MINOR_VERSION 0 + +/* + API versioning + --------------------------------- + + The LLDB API is versioned independently of the LLDB source base + Our API version numbers are composed of a major and a minor number + + The major number means a complete and stable revision of the API. Major numbers are compatibility breakers + (i.e. when we change the API major number, there is no promise of compatibility with the previous major version + and we are free to remove and/or change any APIs) + Minor numbers are a work-in-progress evolution of the API. APIs will not be removed or changed across minor versions + (minors do not break compatibility). However, we can deprecate APIs in minor versions or add new APIs in minor versions + A deprecated API is supposedly going to be removed in the next major version and will generate a warning if used + APIs we add in minor versions will not be removed (at least until the following major) but they might theoretically be deprecated + in a following minor version + Users are discouraged from using the LLDB version number to test for API features and should instead use the API version checking + as discussed below + + API version checking + --------------------------------- + + You can (optionally) sign into an API version checking feature + To do so you need to define three macros: + LLDB_API_CHECK_VERSIONING - define to any value (or no value) + LLDB_API_MAJOR_VERSION_WANTED - which major version of the LLDB API you are targeting + LLDB_API_MINOR_VERSION_WANTED - which minor version of the LLDB API you are targeting + + If these macros exist - LLDB will enable version checking of the public API + + If LLDB_API_MAJOR_VERSION is not equal to LLDB_API_MAJOR_VERSION_WANTED we will immediately halt your compilation with an error + This is by design, since we do not make any promise of compatibility across major versions - if you really want to test your luck, disable the versioning altogether + + If the major version test passes, you have signed up for a specific minor version of the API + Whenever we add or deprecate an API in a minor version, we will mark it with either + LLDB_API_NEW_IN_DOT_x - this API is new in LLDB .x + LLDB_API_DEPRECATED_IN_DOT_x - this API is deprecated as of .x + + If you are using an API new in DOT_x + if LLDB_API_MINOR_VERSION_WANTED >= x then all is well, else you will get a compilation error + This is meant to prevent you from using APIs that are newer than whatever LLDB you want to target + + If you are using an API deprecated in DOT_x + if LLDB_API_MINOR_VERSION_WANTED >= x then you will get a compilation warning, else all is well + This is meant to let you know that you are using an API that is deprecated and might go away + + Caveats + --------------------------------- + + Version checking only works on clang on OSX - you will get an error if you try to enable it on any other OS/compiler + If you want to enable version checking on other platforms, you will need to define appropriate implementations for + LLDB_API_IMPL_DEPRECATED and LLDB_API_IMPL_TOONEW and any other infrastructure your compiler needs for this purpose + + We have no deprecation-as-error mode + + There is no support for API versioning in Python + + We reserve to use macros whose names begin with LLDB_API_ and you should not use them in your source code as they might conflict + with present or future macro names we are using to implement versioning +*/ + +// if you want the version checking to work on other OS/compiler, define appropriate IMPL_DEPRECATED/IMPL_TOONEW +// and define LLDB_API_CHECK_VERSIONING_WORKS when you are ready to go live +#if defined(__APPLE__) && defined(__clang__) +#define LLDB_API_IMPL_DEPRECATED __attribute__((deprecated)) +#define LLDB_API_IMPL_TOONEW __attribute__((unavailable)) +#define LLDB_API_CHECK_VERSIONING_WORKS +#endif + +#if defined(LLDB_API_CHECK_VERSIONING) && !defined(LLDB_API_CHECK_VERSIONING_WORKS) +#error "API version checking will not work here - please disable or create and submit patches to lldb-versioning.h" +#endif + +#if defined(LLDB_API_CHECK_VERSIONING_WORKS) && (!defined(LLDB_API_IMPL_DEPRECATED) || !defined(LLDB_API_IMPL_TOONEW)) +#error "LLDB_API_CHECK_VERSIONING_WORKS needs LLDB_API_IMPL_DEPRECATED and LLDB_API_IMPL_TOONEW to be defined" +#endif + +#if defined(LLDB_API_CHECK_VERSIONING) && defined(LLDB_API_MAJOR_VERSION_WANTED) && defined(LLDB_API_MINOR_VERSION_WANTED) + +#if defined (LLDB_API_MAJOR_VERSION) && (LLDB_API_MAJOR_VERSION != LLDB_API_MAJOR_VERSION_WANTED) +#error "Cannot link using this LLDB version - public API versions are incompatible" +#endif + +#define LLDB_API_MINOR_VERSION_DOT_0 0 +#define LLDB_API_MINOR_VERSION_DOT_1 1 +#define LLDB_API_MINOR_VERSION_DOT_2 2 +#define LLDB_API_MINOR_VERSION_DOT_3 3 +#define LLDB_API_MINOR_VERSION_DOT_4 4 +#define LLDB_API_MINOR_VERSION_DOT_5 5 +#define LLDB_API_MINOR_VERSION_DOT_6 6 +#define LLDB_API_MINOR_VERSION_DOT_7 7 +#define LLDB_API_MINOR_VERSION_DOT_8 8 +#define LLDB_API_MINOR_VERSION_DOT_9 9 +#define LLDB_API_MINOR_VERSION_DOT_10 10 +#define LLDB_API_MINOR_VERSION_DOT_11 11 +#define LLDB_API_MINOR_VERSION_DOT_12 12 +#define LLDB_API_MINOR_VERSION_DOT_13 13 +#define LLDB_API_MINOR_VERSION_DOT_14 14 +#define LLDB_API_MINOR_VERSION_DOT_15 15 +#define LLDB_API_MINOR_VERSION_DOT_16 16 +#define LLDB_API_MINOR_VERSION_DOT_17 17 +#define LLDB_API_MINOR_VERSION_DOT_18 18 +#define LLDB_API_MINOR_VERSION_DOT_19 19 +#define LLDB_API_MINOR_VERSION_DOT_20 20 +#define LLDB_API_MINOR_VERSION_DOT_21 21 +#define LLDB_API_MINOR_VERSION_DOT_22 22 +#define LLDB_API_MINOR_VERSION_DOT_23 23 +#define LLDB_API_MINOR_VERSION_DOT_24 24 +#define LLDB_API_MINOR_VERSION_DOT_25 25 +#define LLDB_API_MINOR_VERSION_DOT_26 26 +#define LLDB_API_MINOR_VERSION_DOT_27 27 +#define LLDB_API_MINOR_VERSION_DOT_28 28 +#define LLDB_API_MINOR_VERSION_DOT_29 29 +#define LLDB_API_MINOR_VERSION_DOT_30 30 +#define LLDB_API_MINOR_VERSION_DOT_31 31 +#define LLDB_API_MINOR_VERSION_DOT_32 32 +#define LLDB_API_MINOR_VERSION_DOT_33 33 +#define LLDB_API_MINOR_VERSION_DOT_34 34 +#define LLDB_API_MINOR_VERSION_DOT_35 35 +#define LLDB_API_MINOR_VERSION_DOT_36 36 +#define LLDB_API_MINOR_VERSION_DOT_37 37 +#define LLDB_API_MINOR_VERSION_DOT_38 38 +#define LLDB_API_MINOR_VERSION_DOT_39 39 +#define LLDB_API_MINOR_VERSION_DOT_40 40 +#define LLDB_API_MINOR_VERSION_DOT_41 41 +#define LLDB_API_MINOR_VERSION_DOT_42 42 +#define LLDB_API_MINOR_VERSION_DOT_43 43 +#define LLDB_API_MINOR_VERSION_DOT_44 44 +#define LLDB_API_MINOR_VERSION_DOT_45 45 +#define LLDB_API_MINOR_VERSION_DOT_46 46 +#define LLDB_API_MINOR_VERSION_DOT_47 47 +#define LLDB_API_MINOR_VERSION_DOT_48 48 +#define LLDB_API_MINOR_VERSION_DOT_49 49 +#define LLDB_API_MINOR_VERSION_DOT_50 50 +#define LLDB_API_MINOR_VERSION_DOT_51 51 +#define LLDB_API_MINOR_VERSION_DOT_52 52 +#define LLDB_API_MINOR_VERSION_DOT_53 53 +#define LLDB_API_MINOR_VERSION_DOT_54 54 +#define LLDB_API_MINOR_VERSION_DOT_55 55 +#define LLDB_API_MINOR_VERSION_DOT_56 56 +#define LLDB_API_MINOR_VERSION_DOT_57 57 +#define LLDB_API_MINOR_VERSION_DOT_58 58 +#define LLDB_API_MINOR_VERSION_DOT_59 59 +#define LLDB_API_MINOR_VERSION_DOT_60 60 +#define LLDB_API_MINOR_VERSION_DOT_61 61 +#define LLDB_API_MINOR_VERSION_DOT_62 62 +#define LLDB_API_MINOR_VERSION_DOT_63 63 +#define LLDB_API_MINOR_VERSION_DOT_64 64 +#define LLDB_API_MINOR_VERSION_DOT_65 65 +#define LLDB_API_MINOR_VERSION_DOT_66 66 +#define LLDB_API_MINOR_VERSION_DOT_67 67 +#define LLDB_API_MINOR_VERSION_DOT_68 68 +#define LLDB_API_MINOR_VERSION_DOT_69 69 +#define LLDB_API_MINOR_VERSION_DOT_70 70 +#define LLDB_API_MINOR_VERSION_DOT_71 71 +#define LLDB_API_MINOR_VERSION_DOT_72 72 +#define LLDB_API_MINOR_VERSION_DOT_73 73 +#define LLDB_API_MINOR_VERSION_DOT_74 74 +#define LLDB_API_MINOR_VERSION_DOT_75 75 +#define LLDB_API_MINOR_VERSION_DOT_76 76 +#define LLDB_API_MINOR_VERSION_DOT_77 77 +#define LLDB_API_MINOR_VERSION_DOT_78 78 +#define LLDB_API_MINOR_VERSION_DOT_79 79 +#define LLDB_API_MINOR_VERSION_DOT_80 80 +#define LLDB_API_MINOR_VERSION_DOT_81 81 +#define LLDB_API_MINOR_VERSION_DOT_82 82 +#define LLDB_API_MINOR_VERSION_DOT_83 83 +#define LLDB_API_MINOR_VERSION_DOT_84 84 +#define LLDB_API_MINOR_VERSION_DOT_85 85 +#define LLDB_API_MINOR_VERSION_DOT_86 86 +#define LLDB_API_MINOR_VERSION_DOT_87 87 +#define LLDB_API_MINOR_VERSION_DOT_88 88 +#define LLDB_API_MINOR_VERSION_DOT_89 89 +#define LLDB_API_MINOR_VERSION_DOT_90 90 +#define LLDB_API_MINOR_VERSION_DOT_91 91 +#define LLDB_API_MINOR_VERSION_DOT_92 92 +#define LLDB_API_MINOR_VERSION_DOT_93 93 +#define LLDB_API_MINOR_VERSION_DOT_94 94 +#define LLDB_API_MINOR_VERSION_DOT_95 95 +#define LLDB_API_MINOR_VERSION_DOT_96 96 +#define LLDB_API_MINOR_VERSION_DOT_97 97 +#define LLDB_API_MINOR_VERSION_DOT_98 98 +#define LLDB_API_MINOR_VERSION_DOT_99 99 + +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_0 +#define LLDB_API_NEW_IN_DOT_0 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_0 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_0 +#define LLDB_API_DEPRECATED_IN_DOT_0 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_0 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_1 +#define LLDB_API_NEW_IN_DOT_1 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_1 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_1 +#define LLDB_API_DEPRECATED_IN_DOT_1 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_1 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_2 +#define LLDB_API_NEW_IN_DOT_2 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_2 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_2 +#define LLDB_API_DEPRECATED_IN_DOT_2 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_2 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_3 +#define LLDB_API_NEW_IN_DOT_3 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_3 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_3 +#define LLDB_API_DEPRECATED_IN_DOT_3 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_3 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_4 +#define LLDB_API_NEW_IN_DOT_4 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_4 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_4 +#define LLDB_API_DEPRECATED_IN_DOT_4 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_4 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_5 +#define LLDB_API_NEW_IN_DOT_5 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_5 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_5 +#define LLDB_API_DEPRECATED_IN_DOT_5 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_5 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_6 +#define LLDB_API_NEW_IN_DOT_6 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_6 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_6 +#define LLDB_API_DEPRECATED_IN_DOT_6 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_6 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_7 +#define LLDB_API_NEW_IN_DOT_7 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_7 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_7 +#define LLDB_API_DEPRECATED_IN_DOT_7 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_7 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_8 +#define LLDB_API_NEW_IN_DOT_8 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_8 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_8 +#define LLDB_API_DEPRECATED_IN_DOT_8 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_8 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_9 +#define LLDB_API_NEW_IN_DOT_9 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_9 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_9 +#define LLDB_API_DEPRECATED_IN_DOT_9 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_9 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_10 +#define LLDB_API_NEW_IN_DOT_10 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_10 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_10 +#define LLDB_API_DEPRECATED_IN_DOT_10 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_10 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_11 +#define LLDB_API_NEW_IN_DOT_11 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_11 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_11 +#define LLDB_API_DEPRECATED_IN_DOT_11 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_11 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_12 +#define LLDB_API_NEW_IN_DOT_12 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_12 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_12 +#define LLDB_API_DEPRECATED_IN_DOT_12 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_12 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_13 +#define LLDB_API_NEW_IN_DOT_13 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_13 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_13 +#define LLDB_API_DEPRECATED_IN_DOT_13 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_13 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_14 +#define LLDB_API_NEW_IN_DOT_14 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_14 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_14 +#define LLDB_API_DEPRECATED_IN_DOT_14 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_14 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_15 +#define LLDB_API_NEW_IN_DOT_15 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_15 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_15 +#define LLDB_API_DEPRECATED_IN_DOT_15 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_15 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_16 +#define LLDB_API_NEW_IN_DOT_16 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_16 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_16 +#define LLDB_API_DEPRECATED_IN_DOT_16 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_16 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_17 +#define LLDB_API_NEW_IN_DOT_17 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_17 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_17 +#define LLDB_API_DEPRECATED_IN_DOT_17 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_17 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_18 +#define LLDB_API_NEW_IN_DOT_18 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_18 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_18 +#define LLDB_API_DEPRECATED_IN_DOT_18 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_18 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_19 +#define LLDB_API_NEW_IN_DOT_19 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_19 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_19 +#define LLDB_API_DEPRECATED_IN_DOT_19 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_19 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_20 +#define LLDB_API_NEW_IN_DOT_20 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_20 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_20 +#define LLDB_API_DEPRECATED_IN_DOT_20 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_20 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_21 +#define LLDB_API_NEW_IN_DOT_21 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_21 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_21 +#define LLDB_API_DEPRECATED_IN_DOT_21 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_21 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_22 +#define LLDB_API_NEW_IN_DOT_22 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_22 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_22 +#define LLDB_API_DEPRECATED_IN_DOT_22 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_22 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_23 +#define LLDB_API_NEW_IN_DOT_23 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_23 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_23 +#define LLDB_API_DEPRECATED_IN_DOT_23 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_23 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_24 +#define LLDB_API_NEW_IN_DOT_24 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_24 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_24 +#define LLDB_API_DEPRECATED_IN_DOT_24 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_24 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_25 +#define LLDB_API_NEW_IN_DOT_25 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_25 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_25 +#define LLDB_API_DEPRECATED_IN_DOT_25 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_25 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_26 +#define LLDB_API_NEW_IN_DOT_26 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_26 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_26 +#define LLDB_API_DEPRECATED_IN_DOT_26 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_26 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_27 +#define LLDB_API_NEW_IN_DOT_27 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_27 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_27 +#define LLDB_API_DEPRECATED_IN_DOT_27 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_27 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_28 +#define LLDB_API_NEW_IN_DOT_28 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_28 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_28 +#define LLDB_API_DEPRECATED_IN_DOT_28 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_28 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_29 +#define LLDB_API_NEW_IN_DOT_29 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_29 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_29 +#define LLDB_API_DEPRECATED_IN_DOT_29 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_29 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_30 +#define LLDB_API_NEW_IN_DOT_30 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_30 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_30 +#define LLDB_API_DEPRECATED_IN_DOT_30 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_30 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_31 +#define LLDB_API_NEW_IN_DOT_31 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_31 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_31 +#define LLDB_API_DEPRECATED_IN_DOT_31 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_31 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_32 +#define LLDB_API_NEW_IN_DOT_32 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_32 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_32 +#define LLDB_API_DEPRECATED_IN_DOT_32 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_32 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_33 +#define LLDB_API_NEW_IN_DOT_33 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_33 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_33 +#define LLDB_API_DEPRECATED_IN_DOT_33 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_33 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_34 +#define LLDB_API_NEW_IN_DOT_34 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_34 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_34 +#define LLDB_API_DEPRECATED_IN_DOT_34 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_34 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_35 +#define LLDB_API_NEW_IN_DOT_35 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_35 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_35 +#define LLDB_API_DEPRECATED_IN_DOT_35 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_35 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_36 +#define LLDB_API_NEW_IN_DOT_36 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_36 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_36 +#define LLDB_API_DEPRECATED_IN_DOT_36 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_36 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_37 +#define LLDB_API_NEW_IN_DOT_37 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_37 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_37 +#define LLDB_API_DEPRECATED_IN_DOT_37 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_37 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_38 +#define LLDB_API_NEW_IN_DOT_38 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_38 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_38 +#define LLDB_API_DEPRECATED_IN_DOT_38 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_38 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_39 +#define LLDB_API_NEW_IN_DOT_39 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_39 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_39 +#define LLDB_API_DEPRECATED_IN_DOT_39 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_39 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_40 +#define LLDB_API_NEW_IN_DOT_40 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_40 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_40 +#define LLDB_API_DEPRECATED_IN_DOT_40 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_40 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_41 +#define LLDB_API_NEW_IN_DOT_41 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_41 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_41 +#define LLDB_API_DEPRECATED_IN_DOT_41 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_41 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_42 +#define LLDB_API_NEW_IN_DOT_42 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_42 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_42 +#define LLDB_API_DEPRECATED_IN_DOT_42 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_42 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_43 +#define LLDB_API_NEW_IN_DOT_43 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_43 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_43 +#define LLDB_API_DEPRECATED_IN_DOT_43 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_43 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_44 +#define LLDB_API_NEW_IN_DOT_44 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_44 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_44 +#define LLDB_API_DEPRECATED_IN_DOT_44 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_44 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_45 +#define LLDB_API_NEW_IN_DOT_45 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_45 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_45 +#define LLDB_API_DEPRECATED_IN_DOT_45 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_45 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_46 +#define LLDB_API_NEW_IN_DOT_46 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_46 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_46 +#define LLDB_API_DEPRECATED_IN_DOT_46 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_46 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_47 +#define LLDB_API_NEW_IN_DOT_47 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_47 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_47 +#define LLDB_API_DEPRECATED_IN_DOT_47 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_47 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_48 +#define LLDB_API_NEW_IN_DOT_48 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_48 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_48 +#define LLDB_API_DEPRECATED_IN_DOT_48 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_48 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_49 +#define LLDB_API_NEW_IN_DOT_49 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_49 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_49 +#define LLDB_API_DEPRECATED_IN_DOT_49 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_49 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_50 +#define LLDB_API_NEW_IN_DOT_50 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_50 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_50 +#define LLDB_API_DEPRECATED_IN_DOT_50 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_50 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_51 +#define LLDB_API_NEW_IN_DOT_51 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_51 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_51 +#define LLDB_API_DEPRECATED_IN_DOT_51 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_51 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_52 +#define LLDB_API_NEW_IN_DOT_52 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_52 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_52 +#define LLDB_API_DEPRECATED_IN_DOT_52 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_52 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_53 +#define LLDB_API_NEW_IN_DOT_53 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_53 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_53 +#define LLDB_API_DEPRECATED_IN_DOT_53 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_53 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_54 +#define LLDB_API_NEW_IN_DOT_54 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_54 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_54 +#define LLDB_API_DEPRECATED_IN_DOT_54 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_54 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_55 +#define LLDB_API_NEW_IN_DOT_55 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_55 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_55 +#define LLDB_API_DEPRECATED_IN_DOT_55 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_55 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_56 +#define LLDB_API_NEW_IN_DOT_56 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_56 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_56 +#define LLDB_API_DEPRECATED_IN_DOT_56 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_56 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_57 +#define LLDB_API_NEW_IN_DOT_57 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_57 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_57 +#define LLDB_API_DEPRECATED_IN_DOT_57 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_57 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_58 +#define LLDB_API_NEW_IN_DOT_58 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_58 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_58 +#define LLDB_API_DEPRECATED_IN_DOT_58 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_58 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_59 +#define LLDB_API_NEW_IN_DOT_59 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_59 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_59 +#define LLDB_API_DEPRECATED_IN_DOT_59 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_59 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_60 +#define LLDB_API_NEW_IN_DOT_60 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_60 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_60 +#define LLDB_API_DEPRECATED_IN_DOT_60 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_60 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_61 +#define LLDB_API_NEW_IN_DOT_61 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_61 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_61 +#define LLDB_API_DEPRECATED_IN_DOT_61 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_61 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_62 +#define LLDB_API_NEW_IN_DOT_62 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_62 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_62 +#define LLDB_API_DEPRECATED_IN_DOT_62 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_62 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_63 +#define LLDB_API_NEW_IN_DOT_63 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_63 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_63 +#define LLDB_API_DEPRECATED_IN_DOT_63 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_63 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_64 +#define LLDB_API_NEW_IN_DOT_64 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_64 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_64 +#define LLDB_API_DEPRECATED_IN_DOT_64 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_64 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_65 +#define LLDB_API_NEW_IN_DOT_65 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_65 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_65 +#define LLDB_API_DEPRECATED_IN_DOT_65 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_65 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_66 +#define LLDB_API_NEW_IN_DOT_66 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_66 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_66 +#define LLDB_API_DEPRECATED_IN_DOT_66 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_66 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_67 +#define LLDB_API_NEW_IN_DOT_67 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_67 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_67 +#define LLDB_API_DEPRECATED_IN_DOT_67 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_67 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_68 +#define LLDB_API_NEW_IN_DOT_68 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_68 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_68 +#define LLDB_API_DEPRECATED_IN_DOT_68 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_68 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_69 +#define LLDB_API_NEW_IN_DOT_69 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_69 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_69 +#define LLDB_API_DEPRECATED_IN_DOT_69 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_69 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_70 +#define LLDB_API_NEW_IN_DOT_70 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_70 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_70 +#define LLDB_API_DEPRECATED_IN_DOT_70 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_70 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_71 +#define LLDB_API_NEW_IN_DOT_71 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_71 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_71 +#define LLDB_API_DEPRECATED_IN_DOT_71 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_71 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_72 +#define LLDB_API_NEW_IN_DOT_72 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_72 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_72 +#define LLDB_API_DEPRECATED_IN_DOT_72 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_72 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_73 +#define LLDB_API_NEW_IN_DOT_73 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_73 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_73 +#define LLDB_API_DEPRECATED_IN_DOT_73 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_73 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_74 +#define LLDB_API_NEW_IN_DOT_74 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_74 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_74 +#define LLDB_API_DEPRECATED_IN_DOT_74 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_74 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_75 +#define LLDB_API_NEW_IN_DOT_75 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_75 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_75 +#define LLDB_API_DEPRECATED_IN_DOT_75 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_75 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_76 +#define LLDB_API_NEW_IN_DOT_76 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_76 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_76 +#define LLDB_API_DEPRECATED_IN_DOT_76 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_76 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_77 +#define LLDB_API_NEW_IN_DOT_77 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_77 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_77 +#define LLDB_API_DEPRECATED_IN_DOT_77 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_77 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_78 +#define LLDB_API_NEW_IN_DOT_78 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_78 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_78 +#define LLDB_API_DEPRECATED_IN_DOT_78 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_78 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_79 +#define LLDB_API_NEW_IN_DOT_79 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_79 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_79 +#define LLDB_API_DEPRECATED_IN_DOT_79 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_79 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_80 +#define LLDB_API_NEW_IN_DOT_80 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_80 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_80 +#define LLDB_API_DEPRECATED_IN_DOT_80 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_80 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_81 +#define LLDB_API_NEW_IN_DOT_81 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_81 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_81 +#define LLDB_API_DEPRECATED_IN_DOT_81 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_81 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_82 +#define LLDB_API_NEW_IN_DOT_82 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_82 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_82 +#define LLDB_API_DEPRECATED_IN_DOT_82 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_82 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_83 +#define LLDB_API_NEW_IN_DOT_83 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_83 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_83 +#define LLDB_API_DEPRECATED_IN_DOT_83 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_83 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_84 +#define LLDB_API_NEW_IN_DOT_84 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_84 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_84 +#define LLDB_API_DEPRECATED_IN_DOT_84 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_84 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_85 +#define LLDB_API_NEW_IN_DOT_85 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_85 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_85 +#define LLDB_API_DEPRECATED_IN_DOT_85 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_85 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_86 +#define LLDB_API_NEW_IN_DOT_86 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_86 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_86 +#define LLDB_API_DEPRECATED_IN_DOT_86 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_86 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_87 +#define LLDB_API_NEW_IN_DOT_87 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_87 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_87 +#define LLDB_API_DEPRECATED_IN_DOT_87 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_87 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_88 +#define LLDB_API_NEW_IN_DOT_88 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_88 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_88 +#define LLDB_API_DEPRECATED_IN_DOT_88 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_88 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_89 +#define LLDB_API_NEW_IN_DOT_89 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_89 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_89 +#define LLDB_API_DEPRECATED_IN_DOT_89 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_89 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_90 +#define LLDB_API_NEW_IN_DOT_90 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_90 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_90 +#define LLDB_API_DEPRECATED_IN_DOT_90 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_90 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_91 +#define LLDB_API_NEW_IN_DOT_91 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_91 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_91 +#define LLDB_API_DEPRECATED_IN_DOT_91 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_91 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_92 +#define LLDB_API_NEW_IN_DOT_92 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_92 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_92 +#define LLDB_API_DEPRECATED_IN_DOT_92 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_92 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_93 +#define LLDB_API_NEW_IN_DOT_93 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_93 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_93 +#define LLDB_API_DEPRECATED_IN_DOT_93 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_93 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_94 +#define LLDB_API_NEW_IN_DOT_94 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_94 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_94 +#define LLDB_API_DEPRECATED_IN_DOT_94 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_94 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_95 +#define LLDB_API_NEW_IN_DOT_95 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_95 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_95 +#define LLDB_API_DEPRECATED_IN_DOT_95 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_95 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_96 +#define LLDB_API_NEW_IN_DOT_96 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_96 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_96 +#define LLDB_API_DEPRECATED_IN_DOT_96 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_96 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_97 +#define LLDB_API_NEW_IN_DOT_97 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_97 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_97 +#define LLDB_API_DEPRECATED_IN_DOT_97 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_97 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_98 +#define LLDB_API_NEW_IN_DOT_98 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_98 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_98 +#define LLDB_API_DEPRECATED_IN_DOT_98 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_98 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_99 +#define LLDB_API_NEW_IN_DOT_99 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_99 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_99 +#define LLDB_API_DEPRECATED_IN_DOT_99 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_99 +#endif + +#else // defined(LLDB_CHECK_API_VERSIONING) && defined(LLDB_API_MAJOR_VERSION_WANTED) && defined(LLDB_API_MINOR_VERSION_WANTED) && defined (LLDB_API_MAJOR_VERSION) + +#define LLDB_API_NEW_IN_DOT_0 +#define LLDB_API_DEPRECATED_IN_DOT_0 +#define LLDB_API_NEW_IN_DOT_1 +#define LLDB_API_DEPRECATED_IN_DOT_1 +#define LLDB_API_NEW_IN_DOT_2 +#define LLDB_API_DEPRECATED_IN_DOT_2 +#define LLDB_API_NEW_IN_DOT_3 +#define LLDB_API_DEPRECATED_IN_DOT_3 +#define LLDB_API_NEW_IN_DOT_4 +#define LLDB_API_DEPRECATED_IN_DOT_4 +#define LLDB_API_NEW_IN_DOT_5 +#define LLDB_API_DEPRECATED_IN_DOT_5 +#define LLDB_API_NEW_IN_DOT_6 +#define LLDB_API_DEPRECATED_IN_DOT_6 +#define LLDB_API_NEW_IN_DOT_7 +#define LLDB_API_DEPRECATED_IN_DOT_7 +#define LLDB_API_NEW_IN_DOT_8 +#define LLDB_API_DEPRECATED_IN_DOT_8 +#define LLDB_API_NEW_IN_DOT_9 +#define LLDB_API_DEPRECATED_IN_DOT_9 +#define LLDB_API_NEW_IN_DOT_10 +#define LLDB_API_DEPRECATED_IN_DOT_10 +#define LLDB_API_NEW_IN_DOT_11 +#define LLDB_API_DEPRECATED_IN_DOT_11 +#define LLDB_API_NEW_IN_DOT_12 +#define LLDB_API_DEPRECATED_IN_DOT_12 +#define LLDB_API_NEW_IN_DOT_13 +#define LLDB_API_DEPRECATED_IN_DOT_13 +#define LLDB_API_NEW_IN_DOT_14 +#define LLDB_API_DEPRECATED_IN_DOT_14 +#define LLDB_API_NEW_IN_DOT_15 +#define LLDB_API_DEPRECATED_IN_DOT_15 +#define LLDB_API_NEW_IN_DOT_16 +#define LLDB_API_DEPRECATED_IN_DOT_16 +#define LLDB_API_NEW_IN_DOT_17 +#define LLDB_API_DEPRECATED_IN_DOT_17 +#define LLDB_API_NEW_IN_DOT_18 +#define LLDB_API_DEPRECATED_IN_DOT_18 +#define LLDB_API_NEW_IN_DOT_19 +#define LLDB_API_DEPRECATED_IN_DOT_19 +#define LLDB_API_NEW_IN_DOT_20 +#define LLDB_API_DEPRECATED_IN_DOT_20 +#define LLDB_API_NEW_IN_DOT_21 +#define LLDB_API_DEPRECATED_IN_DOT_21 +#define LLDB_API_NEW_IN_DOT_22 +#define LLDB_API_DEPRECATED_IN_DOT_22 +#define LLDB_API_NEW_IN_DOT_23 +#define LLDB_API_DEPRECATED_IN_DOT_23 +#define LLDB_API_NEW_IN_DOT_24 +#define LLDB_API_DEPRECATED_IN_DOT_24 +#define LLDB_API_NEW_IN_DOT_25 +#define LLDB_API_DEPRECATED_IN_DOT_25 +#define LLDB_API_NEW_IN_DOT_26 +#define LLDB_API_DEPRECATED_IN_DOT_26 +#define LLDB_API_NEW_IN_DOT_27 +#define LLDB_API_DEPRECATED_IN_DOT_27 +#define LLDB_API_NEW_IN_DOT_28 +#define LLDB_API_DEPRECATED_IN_DOT_28 +#define LLDB_API_NEW_IN_DOT_29 +#define LLDB_API_DEPRECATED_IN_DOT_29 +#define LLDB_API_NEW_IN_DOT_30 +#define LLDB_API_DEPRECATED_IN_DOT_30 +#define LLDB_API_NEW_IN_DOT_31 +#define LLDB_API_DEPRECATED_IN_DOT_31 +#define LLDB_API_NEW_IN_DOT_32 +#define LLDB_API_DEPRECATED_IN_DOT_32 +#define LLDB_API_NEW_IN_DOT_33 +#define LLDB_API_DEPRECATED_IN_DOT_33 +#define LLDB_API_NEW_IN_DOT_34 +#define LLDB_API_DEPRECATED_IN_DOT_34 +#define LLDB_API_NEW_IN_DOT_35 +#define LLDB_API_DEPRECATED_IN_DOT_35 +#define LLDB_API_NEW_IN_DOT_36 +#define LLDB_API_DEPRECATED_IN_DOT_36 +#define LLDB_API_NEW_IN_DOT_37 +#define LLDB_API_DEPRECATED_IN_DOT_37 +#define LLDB_API_NEW_IN_DOT_38 +#define LLDB_API_DEPRECATED_IN_DOT_38 +#define LLDB_API_NEW_IN_DOT_39 +#define LLDB_API_DEPRECATED_IN_DOT_39 +#define LLDB_API_NEW_IN_DOT_40 +#define LLDB_API_DEPRECATED_IN_DOT_40 +#define LLDB_API_NEW_IN_DOT_41 +#define LLDB_API_DEPRECATED_IN_DOT_41 +#define LLDB_API_NEW_IN_DOT_42 +#define LLDB_API_DEPRECATED_IN_DOT_42 +#define LLDB_API_NEW_IN_DOT_43 +#define LLDB_API_DEPRECATED_IN_DOT_43 +#define LLDB_API_NEW_IN_DOT_44 +#define LLDB_API_DEPRECATED_IN_DOT_44 +#define LLDB_API_NEW_IN_DOT_45 +#define LLDB_API_DEPRECATED_IN_DOT_45 +#define LLDB_API_NEW_IN_DOT_46 +#define LLDB_API_DEPRECATED_IN_DOT_46 +#define LLDB_API_NEW_IN_DOT_47 +#define LLDB_API_DEPRECATED_IN_DOT_47 +#define LLDB_API_NEW_IN_DOT_48 +#define LLDB_API_DEPRECATED_IN_DOT_48 +#define LLDB_API_NEW_IN_DOT_49 +#define LLDB_API_DEPRECATED_IN_DOT_49 +#define LLDB_API_NEW_IN_DOT_50 +#define LLDB_API_DEPRECATED_IN_DOT_50 +#define LLDB_API_NEW_IN_DOT_51 +#define LLDB_API_DEPRECATED_IN_DOT_51 +#define LLDB_API_NEW_IN_DOT_52 +#define LLDB_API_DEPRECATED_IN_DOT_52 +#define LLDB_API_NEW_IN_DOT_53 +#define LLDB_API_DEPRECATED_IN_DOT_53 +#define LLDB_API_NEW_IN_DOT_54 +#define LLDB_API_DEPRECATED_IN_DOT_54 +#define LLDB_API_NEW_IN_DOT_55 +#define LLDB_API_DEPRECATED_IN_DOT_55 +#define LLDB_API_NEW_IN_DOT_56 +#define LLDB_API_DEPRECATED_IN_DOT_56 +#define LLDB_API_NEW_IN_DOT_57 +#define LLDB_API_DEPRECATED_IN_DOT_57 +#define LLDB_API_NEW_IN_DOT_58 +#define LLDB_API_DEPRECATED_IN_DOT_58 +#define LLDB_API_NEW_IN_DOT_59 +#define LLDB_API_DEPRECATED_IN_DOT_59 +#define LLDB_API_NEW_IN_DOT_60 +#define LLDB_API_DEPRECATED_IN_DOT_60 +#define LLDB_API_NEW_IN_DOT_61 +#define LLDB_API_DEPRECATED_IN_DOT_61 +#define LLDB_API_NEW_IN_DOT_62 +#define LLDB_API_DEPRECATED_IN_DOT_62 +#define LLDB_API_NEW_IN_DOT_63 +#define LLDB_API_DEPRECATED_IN_DOT_63 +#define LLDB_API_NEW_IN_DOT_64 +#define LLDB_API_DEPRECATED_IN_DOT_64 +#define LLDB_API_NEW_IN_DOT_65 +#define LLDB_API_DEPRECATED_IN_DOT_65 +#define LLDB_API_NEW_IN_DOT_66 +#define LLDB_API_DEPRECATED_IN_DOT_66 +#define LLDB_API_NEW_IN_DOT_67 +#define LLDB_API_DEPRECATED_IN_DOT_67 +#define LLDB_API_NEW_IN_DOT_68 +#define LLDB_API_DEPRECATED_IN_DOT_68 +#define LLDB_API_NEW_IN_DOT_69 +#define LLDB_API_DEPRECATED_IN_DOT_69 +#define LLDB_API_NEW_IN_DOT_70 +#define LLDB_API_DEPRECATED_IN_DOT_70 +#define LLDB_API_NEW_IN_DOT_71 +#define LLDB_API_DEPRECATED_IN_DOT_71 +#define LLDB_API_NEW_IN_DOT_72 +#define LLDB_API_DEPRECATED_IN_DOT_72 +#define LLDB_API_NEW_IN_DOT_73 +#define LLDB_API_DEPRECATED_IN_DOT_73 +#define LLDB_API_NEW_IN_DOT_74 +#define LLDB_API_DEPRECATED_IN_DOT_74 +#define LLDB_API_NEW_IN_DOT_75 +#define LLDB_API_DEPRECATED_IN_DOT_75 +#define LLDB_API_NEW_IN_DOT_76 +#define LLDB_API_DEPRECATED_IN_DOT_76 +#define LLDB_API_NEW_IN_DOT_77 +#define LLDB_API_DEPRECATED_IN_DOT_77 +#define LLDB_API_NEW_IN_DOT_78 +#define LLDB_API_DEPRECATED_IN_DOT_78 +#define LLDB_API_NEW_IN_DOT_79 +#define LLDB_API_DEPRECATED_IN_DOT_79 +#define LLDB_API_NEW_IN_DOT_80 +#define LLDB_API_DEPRECATED_IN_DOT_80 +#define LLDB_API_NEW_IN_DOT_81 +#define LLDB_API_DEPRECATED_IN_DOT_81 +#define LLDB_API_NEW_IN_DOT_82 +#define LLDB_API_DEPRECATED_IN_DOT_82 +#define LLDB_API_NEW_IN_DOT_83 +#define LLDB_API_DEPRECATED_IN_DOT_83 +#define LLDB_API_NEW_IN_DOT_84 +#define LLDB_API_DEPRECATED_IN_DOT_84 +#define LLDB_API_NEW_IN_DOT_85 +#define LLDB_API_DEPRECATED_IN_DOT_85 +#define LLDB_API_NEW_IN_DOT_86 +#define LLDB_API_DEPRECATED_IN_DOT_86 +#define LLDB_API_NEW_IN_DOT_87 +#define LLDB_API_DEPRECATED_IN_DOT_87 +#define LLDB_API_NEW_IN_DOT_88 +#define LLDB_API_DEPRECATED_IN_DOT_88 +#define LLDB_API_NEW_IN_DOT_89 +#define LLDB_API_DEPRECATED_IN_DOT_89 +#define LLDB_API_NEW_IN_DOT_90 +#define LLDB_API_DEPRECATED_IN_DOT_90 +#define LLDB_API_NEW_IN_DOT_91 +#define LLDB_API_DEPRECATED_IN_DOT_91 +#define LLDB_API_NEW_IN_DOT_92 +#define LLDB_API_DEPRECATED_IN_DOT_92 +#define LLDB_API_NEW_IN_DOT_93 +#define LLDB_API_DEPRECATED_IN_DOT_93 +#define LLDB_API_NEW_IN_DOT_94 +#define LLDB_API_DEPRECATED_IN_DOT_94 +#define LLDB_API_NEW_IN_DOT_95 +#define LLDB_API_DEPRECATED_IN_DOT_95 +#define LLDB_API_NEW_IN_DOT_96 +#define LLDB_API_DEPRECATED_IN_DOT_96 +#define LLDB_API_NEW_IN_DOT_97 +#define LLDB_API_DEPRECATED_IN_DOT_97 +#define LLDB_API_NEW_IN_DOT_98 +#define LLDB_API_DEPRECATED_IN_DOT_98 +#define LLDB_API_NEW_IN_DOT_99 +#define LLDB_API_DEPRECATED_IN_DOT_99 +#endif // defined(LLDB_CHECK_API_VERSIONING) && defined(LLDB_API_MAJOR_VERSION_WANTED) && defined(LLDB_API_MINOR_VERSION_WANTED) && defined (LLDB_API_MAJOR_VERSION) + +#endif // LLDB_lldb_versioning_h_ \ No newline at end of file diff --git a/contrib/llvm/tools/lldb/source/API/SBAddress.cpp b/contrib/llvm/tools/lldb/source/API/SBAddress.cpp new file mode 100644 index 00000000000..799c9090763 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/API/SBAddress.cpp @@ -0,0 +1,326 @@ +//===-- SBAddress.cpp -------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/API/SBAddress.h" +#include "lldb/API/SBProcess.h" +#include "lldb/API/SBSection.h" +#include "lldb/API/SBStream.h" +#include "lldb/Core/Address.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Module.h" +#include "lldb/Host/Mutex.h" +#include "lldb/Target/Target.h" + + +using namespace lldb; +using namespace lldb_private; + + +SBAddress::SBAddress () : + m_opaque_ap () +{ +} + +SBAddress::SBAddress (const Address *lldb_object_ptr) : + m_opaque_ap () +{ + if (lldb_object_ptr) + ref() = *lldb_object_ptr; +} + +SBAddress::SBAddress (const SBAddress &rhs) : + m_opaque_ap () +{ + if (rhs.IsValid()) + ref() = rhs.ref(); +} + + +SBAddress::SBAddress (lldb::SBSection section, lldb::addr_t offset) : + m_opaque_ap(new Address (section.GetSP(), offset)) +{ +} + +// Create an address by resolving a load address using the supplied target +SBAddress::SBAddress (lldb::addr_t load_addr, lldb::SBTarget &target) : + m_opaque_ap() +{ + SetLoadAddress (load_addr, target); +} + + + +SBAddress::~SBAddress () +{ +} + +const SBAddress & +SBAddress::operator = (const SBAddress &rhs) +{ + if (this != &rhs) + { + if (rhs.IsValid()) + ref() = rhs.ref(); + else + m_opaque_ap.reset(); + } + return *this; +} + +bool +SBAddress::IsValid () const +{ + return m_opaque_ap.get() != NULL && m_opaque_ap->IsValid(); +} + +void +SBAddress::Clear () +{ + m_opaque_ap.reset(); +} + +void +SBAddress::SetAddress (lldb::SBSection section, lldb::addr_t offset) +{ + Address &addr = ref(); + addr.SetSection (section.GetSP()); + addr.SetOffset (offset); +} + + +void +SBAddress::SetAddress (const Address *lldb_object_ptr) +{ + if (lldb_object_ptr) + ref() = *lldb_object_ptr; + else + m_opaque_ap.reset(); +} + +lldb::addr_t +SBAddress::GetFileAddress () const +{ + if (m_opaque_ap.get()) + return m_opaque_ap->GetFileAddress(); + else + return LLDB_INVALID_ADDRESS; +} + +lldb::addr_t +SBAddress::GetLoadAddress (const SBTarget &target) const +{ + Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + lldb::addr_t addr = LLDB_INVALID_ADDRESS; + TargetSP target_sp (target.GetSP()); + if (target_sp) + { + if (m_opaque_ap.get()) + { + Mutex::Locker api_locker (target_sp->GetAPIMutex()); + addr = m_opaque_ap->GetLoadAddress (target_sp.get()); + } + } + + if (log) + { + if (addr == LLDB_INVALID_ADDRESS) + log->Printf ("SBAddress::GetLoadAddress (SBTarget(%p)) => LLDB_INVALID_ADDRESS", target_sp.get()); + else + log->Printf ("SBAddress::GetLoadAddress (SBTarget(%p)) => 0x%" PRIx64, target_sp.get(), addr); + } + + return addr; +} + +void +SBAddress::SetLoadAddress (lldb::addr_t load_addr, lldb::SBTarget &target) +{ + // Create the address object if we don't already have one + ref(); + if (target.IsValid()) + *this = target.ResolveLoadAddress(load_addr); + else + m_opaque_ap->Clear(); + + // Check if we weren't were able to resolve a section offset address. + // If we weren't it is ok, the load address might be a location on the + // stack or heap, so we should just have an address with no section and + // a valid offset + if (!m_opaque_ap->IsValid()) + m_opaque_ap->SetOffset(load_addr); +} + +bool +SBAddress::OffsetAddress (addr_t offset) +{ + if (m_opaque_ap.get()) + { + addr_t addr_offset = m_opaque_ap->GetOffset(); + if (addr_offset != LLDB_INVALID_ADDRESS) + { + m_opaque_ap->SetOffset(addr_offset + offset); + return true; + } + } + return false; +} + +lldb::SBSection +SBAddress::GetSection () +{ + lldb::SBSection sb_section; + if (m_opaque_ap.get()) + sb_section.SetSP (m_opaque_ap->GetSection()); + return sb_section; +} + +lldb::addr_t +SBAddress::GetOffset () +{ + if (m_opaque_ap.get()) + return m_opaque_ap->GetOffset(); + return 0; +} + +Address * +SBAddress::operator->() +{ + return m_opaque_ap.get(); +} + +const Address * +SBAddress::operator->() const +{ + return m_opaque_ap.get(); +} + +Address & +SBAddress::ref () +{ + if (m_opaque_ap.get() == NULL) + m_opaque_ap.reset (new Address()); + return *m_opaque_ap; +} + +const Address & +SBAddress::ref () const +{ + // This object should already have checked with "IsValid()" + // prior to calling this function. In case you didn't we will assert + // and die to let you know. + assert (m_opaque_ap.get()); + return *m_opaque_ap; +} + +Address * +SBAddress::get () +{ + return m_opaque_ap.get(); +} + +bool +SBAddress::GetDescription (SBStream &description) +{ + // Call "ref()" on the stream to make sure it creates a backing stream in + // case there isn't one already... + Stream &strm = description.ref(); + if (m_opaque_ap.get()) + { + m_opaque_ap->Dump (&strm, + NULL, + Address::DumpStyleResolvedDescription, + Address::DumpStyleModuleWithFileAddress, + 4); + StreamString sstrm; +// m_opaque_ap->Dump (&sstrm, NULL, Address::DumpStyleResolvedDescription, Address::DumpStyleInvalid, 4); +// if (sstrm.GetData()) +// strm.Printf (" (%s)", sstrm.GetData()); + } + else + strm.PutCString ("No value"); + + return true; +} + +SBModule +SBAddress::GetModule () +{ + SBModule sb_module; + if (m_opaque_ap.get()) + sb_module.SetSP (m_opaque_ap->GetModule()); + return sb_module; +} + +SBSymbolContext +SBAddress::GetSymbolContext (uint32_t resolve_scope) +{ + SBSymbolContext sb_sc; + if (m_opaque_ap.get()) + m_opaque_ap->CalculateSymbolContext (&sb_sc.ref(), resolve_scope); + return sb_sc; +} + +SBCompileUnit +SBAddress::GetCompileUnit () +{ + SBCompileUnit sb_comp_unit; + if (m_opaque_ap.get()) + sb_comp_unit.reset(m_opaque_ap->CalculateSymbolContextCompileUnit()); + return sb_comp_unit; +} + +SBFunction +SBAddress::GetFunction () +{ + SBFunction sb_function; + if (m_opaque_ap.get()) + sb_function.reset(m_opaque_ap->CalculateSymbolContextFunction()); + return sb_function; +} + +SBBlock +SBAddress::GetBlock () +{ + SBBlock sb_block; + if (m_opaque_ap.get()) + sb_block.SetPtr(m_opaque_ap->CalculateSymbolContextBlock()); + return sb_block; +} + +SBSymbol +SBAddress::GetSymbol () +{ + SBSymbol sb_symbol; + if (m_opaque_ap.get()) + sb_symbol.reset(m_opaque_ap->CalculateSymbolContextSymbol()); + return sb_symbol; +} + +SBLineEntry +SBAddress::GetLineEntry () +{ + SBLineEntry sb_line_entry; + if (m_opaque_ap.get()) + { + LineEntry line_entry; + if (m_opaque_ap->CalculateSymbolContextLineEntry (line_entry)) + sb_line_entry.SetLineEntry (line_entry); + } + return sb_line_entry; +} + +AddressClass +SBAddress::GetAddressClass () +{ + if (m_opaque_ap.get()) + return m_opaque_ap->GetAddressClass(); + return eAddressClassInvalid; +} + diff --git a/contrib/llvm/tools/lldb/source/API/SBBlock.cpp b/contrib/llvm/tools/lldb/source/API/SBBlock.cpp new file mode 100644 index 00000000000..c8a665f7d6f --- /dev/null +++ b/contrib/llvm/tools/lldb/source/API/SBBlock.cpp @@ -0,0 +1,373 @@ +//===-- SBBlock.cpp ---------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/API/SBBlock.h" +#include "lldb/API/SBAddress.h" +#include "lldb/API/SBFileSpec.h" +#include "lldb/API/SBFrame.h" +#include "lldb/API/SBStream.h" +#include "lldb/API/SBValue.h" +#include "lldb/Core/AddressRange.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/ValueObjectVariable.h" +#include "lldb/Symbol/Block.h" +#include "lldb/Symbol/Function.h" +#include "lldb/Symbol/SymbolContext.h" +#include "lldb/Symbol/VariableList.h" +#include "lldb/Target/StackFrame.h" +#include "lldb/Target/Target.h" + +using namespace lldb; +using namespace lldb_private; + + +SBBlock::SBBlock () : + m_opaque_ptr (NULL) +{ +} + +SBBlock::SBBlock (lldb_private::Block *lldb_object_ptr) : + m_opaque_ptr (lldb_object_ptr) +{ +} + +SBBlock::SBBlock(const SBBlock &rhs) : + m_opaque_ptr (rhs.m_opaque_ptr) +{ +} + +const SBBlock & +SBBlock::operator = (const SBBlock &rhs) +{ + m_opaque_ptr = rhs.m_opaque_ptr; + return *this; +} + +SBBlock::~SBBlock () +{ + m_opaque_ptr = NULL; +} + +bool +SBBlock::IsValid () const +{ + return m_opaque_ptr != NULL; +} + +bool +SBBlock::IsInlined () const +{ + if (m_opaque_ptr) + return m_opaque_ptr->GetInlinedFunctionInfo () != NULL; + return false; +} + +const char * +SBBlock::GetInlinedName () const +{ + if (m_opaque_ptr) + { + const InlineFunctionInfo* inlined_info = m_opaque_ptr->GetInlinedFunctionInfo (); + if (inlined_info) + return inlined_info->GetName().AsCString (NULL); + } + return NULL; +} + +SBFileSpec +SBBlock::GetInlinedCallSiteFile () const +{ + SBFileSpec sb_file; + if (m_opaque_ptr) + { + const InlineFunctionInfo* inlined_info = m_opaque_ptr->GetInlinedFunctionInfo (); + if (inlined_info) + sb_file.SetFileSpec (inlined_info->GetCallSite().GetFile()); + } + return sb_file; +} + +uint32_t +SBBlock::GetInlinedCallSiteLine () const +{ + if (m_opaque_ptr) + { + const InlineFunctionInfo* inlined_info = m_opaque_ptr->GetInlinedFunctionInfo (); + if (inlined_info) + return inlined_info->GetCallSite().GetLine(); + } + return 0; +} + +uint32_t +SBBlock::GetInlinedCallSiteColumn () const +{ + if (m_opaque_ptr) + { + const InlineFunctionInfo* inlined_info = m_opaque_ptr->GetInlinedFunctionInfo (); + if (inlined_info) + return inlined_info->GetCallSite().GetColumn(); + } + return 0; +} + +void +SBBlock::AppendVariables (bool can_create, bool get_parent_variables, lldb_private::VariableList *var_list) +{ + if (IsValid()) + { + bool show_inline = true; + m_opaque_ptr->AppendVariables (can_create, get_parent_variables, show_inline, var_list); + } +} + +SBBlock +SBBlock::GetParent () +{ + SBBlock sb_block; + if (m_opaque_ptr) + sb_block.m_opaque_ptr = m_opaque_ptr->GetParent(); + return sb_block; +} + +lldb::SBBlock +SBBlock::GetContainingInlinedBlock () +{ + SBBlock sb_block; + if (m_opaque_ptr) + sb_block.m_opaque_ptr = m_opaque_ptr->GetContainingInlinedBlock (); + return sb_block; +} + +SBBlock +SBBlock::GetSibling () +{ + SBBlock sb_block; + if (m_opaque_ptr) + sb_block.m_opaque_ptr = m_opaque_ptr->GetSibling(); + return sb_block; +} + +SBBlock +SBBlock::GetFirstChild () +{ + SBBlock sb_block; + if (m_opaque_ptr) + sb_block.m_opaque_ptr = m_opaque_ptr->GetFirstChild(); + return sb_block; +} + +lldb_private::Block * +SBBlock::GetPtr () +{ + return m_opaque_ptr; +} + +void +SBBlock::SetPtr (lldb_private::Block *block) +{ + m_opaque_ptr = block; +} + +bool +SBBlock::GetDescription (SBStream &description) +{ + Stream &strm = description.ref(); + + if (m_opaque_ptr) + { + lldb::user_id_t id = m_opaque_ptr->GetID(); + strm.Printf ("Block: {id: %" PRIu64 "} ", id); + if (IsInlined()) + { + strm.Printf (" (inlined, '%s') ", GetInlinedName()); + } + lldb_private::SymbolContext sc; + m_opaque_ptr->CalculateSymbolContext (&sc); + if (sc.function) + { + m_opaque_ptr->DumpAddressRanges (&strm, + sc.function->GetAddressRange().GetBaseAddress().GetFileAddress()); + } + } + else + strm.PutCString ("No value"); + + return true; +} + +uint32_t +SBBlock::GetNumRanges () +{ + if (m_opaque_ptr) + return m_opaque_ptr->GetNumRanges(); + return 0; +} + +lldb::SBAddress +SBBlock::GetRangeStartAddress (uint32_t idx) +{ + lldb::SBAddress sb_addr; + if (m_opaque_ptr) + { + AddressRange range; + if (m_opaque_ptr->GetRangeAtIndex(idx, range)) + { + sb_addr.ref() = range.GetBaseAddress(); + } + } + return sb_addr; +} + +lldb::SBAddress +SBBlock::GetRangeEndAddress (uint32_t idx) +{ + lldb::SBAddress sb_addr; + if (m_opaque_ptr) + { + AddressRange range; + if (m_opaque_ptr->GetRangeAtIndex(idx, range)) + { + sb_addr.ref() = range.GetBaseAddress(); + sb_addr.ref().Slide(range.GetByteSize()); + } + } + return sb_addr; +} + +uint32_t +SBBlock::GetRangeIndexForBlockAddress (lldb::SBAddress block_addr) +{ + if (m_opaque_ptr && block_addr.IsValid()) + { + return m_opaque_ptr->GetRangeIndexContainingAddress (block_addr.ref()); + } + + return UINT32_MAX; +} + + +lldb::SBValueList +SBBlock::GetVariables (lldb::SBFrame& frame, + bool arguments, + bool locals, + bool statics, + lldb::DynamicValueType use_dynamic) +{ + Block *block = GetPtr(); + SBValueList value_list; + if (block) + { + StackFrameSP frame_sp(frame.GetFrameSP()); + VariableListSP variable_list_sp (block->GetBlockVariableList (true)); + + if (variable_list_sp) + { + const size_t num_variables = variable_list_sp->GetSize(); + if (num_variables) + { + for (size_t i = 0; i < num_variables; ++i) + { + VariableSP variable_sp (variable_list_sp->GetVariableAtIndex(i)); + if (variable_sp) + { + bool add_variable = false; + switch (variable_sp->GetScope()) + { + case eValueTypeVariableGlobal: + case eValueTypeVariableStatic: + add_variable = statics; + break; + + case eValueTypeVariableArgument: + add_variable = arguments; + break; + + case eValueTypeVariableLocal: + add_variable = locals; + break; + + default: + break; + } + if (add_variable) + { + if (frame_sp) + { + lldb::ValueObjectSP valobj_sp(frame_sp->GetValueObjectForFrameVariable (variable_sp,eNoDynamicValues)); + SBValue value_sb; + value_sb.SetSP(valobj_sp, use_dynamic); + value_list.Append (value_sb); + } + } + } + } + } + } + } + return value_list; +} + +lldb::SBValueList +SBBlock::GetVariables (lldb::SBTarget& target, + bool arguments, + bool locals, + bool statics) +{ + Block *block = GetPtr(); + + SBValueList value_list; + if (block) + { + TargetSP target_sp(target.GetSP()); + + VariableListSP variable_list_sp (block->GetBlockVariableList (true)); + + if (variable_list_sp) + { + const size_t num_variables = variable_list_sp->GetSize(); + if (num_variables) + { + for (size_t i = 0; i < num_variables; ++i) + { + VariableSP variable_sp (variable_list_sp->GetVariableAtIndex(i)); + if (variable_sp) + { + bool add_variable = false; + switch (variable_sp->GetScope()) + { + case eValueTypeVariableGlobal: + case eValueTypeVariableStatic: + add_variable = statics; + break; + + case eValueTypeVariableArgument: + add_variable = arguments; + break; + + case eValueTypeVariableLocal: + add_variable = locals; + break; + + default: + break; + } + if (add_variable) + { + if (target_sp) + value_list.Append (ValueObjectVariable::Create (target_sp.get(), variable_sp)); + } + } + } + } + } + } + return value_list; +} + diff --git a/contrib/llvm/tools/lldb/source/API/SBBreakpoint.cpp b/contrib/llvm/tools/lldb/source/API/SBBreakpoint.cpp new file mode 100644 index 00000000000..11ad149fddd --- /dev/null +++ b/contrib/llvm/tools/lldb/source/API/SBBreakpoint.cpp @@ -0,0 +1,648 @@ +//===-- SBBreakpoint.cpp ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/API/SBBreakpoint.h" +#include "lldb/API/SBBreakpointLocation.h" +#include "lldb/API/SBDebugger.h" +#include "lldb/API/SBEvent.h" +#include "lldb/API/SBProcess.h" +#include "lldb/API/SBStream.h" +#include "lldb/API/SBThread.h" + +#include "lldb/Breakpoint/Breakpoint.h" +#include "lldb/Breakpoint/BreakpointLocation.h" +#include "lldb/Breakpoint/StoppointCallbackContext.h" +#include "lldb/Core/Address.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Stream.h" +#include "lldb/Core/StreamFile.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" +#include "lldb/Target/ThreadSpec.h" + + +#include "lldb/lldb-enumerations.h" + +using namespace lldb; +using namespace lldb_private; + +struct CallbackData +{ + SBBreakpoint::BreakpointHitCallback callback; + void *callback_baton; +}; + +class SBBreakpointCallbackBaton : public Baton +{ +public: + + SBBreakpointCallbackBaton (SBBreakpoint::BreakpointHitCallback callback, void *baton) : + Baton (new CallbackData) + { + CallbackData *data = (CallbackData *)m_data; + data->callback = callback; + data->callback_baton = baton; + } + + virtual ~SBBreakpointCallbackBaton() + { + CallbackData *data = (CallbackData *)m_data; + + if (data) + { + delete data; + m_data = NULL; + } + } +}; + + +SBBreakpoint::SBBreakpoint () : + m_opaque_sp () +{ +} + +SBBreakpoint::SBBreakpoint (const SBBreakpoint& rhs) : + m_opaque_sp (rhs.m_opaque_sp) +{ +} + + +SBBreakpoint::SBBreakpoint (const lldb::BreakpointSP &bp_sp) : + m_opaque_sp (bp_sp) +{ +} + +SBBreakpoint::~SBBreakpoint() +{ +} + +const SBBreakpoint & +SBBreakpoint::operator = (const SBBreakpoint& rhs) +{ + if (this != &rhs) + m_opaque_sp = rhs.m_opaque_sp; + return *this; +} + +bool +SBBreakpoint::operator == (const lldb::SBBreakpoint& rhs) +{ + if (m_opaque_sp && rhs.m_opaque_sp) + return m_opaque_sp.get() == rhs.m_opaque_sp.get(); + return false; +} + +bool +SBBreakpoint::operator != (const lldb::SBBreakpoint& rhs) +{ + if (m_opaque_sp && rhs.m_opaque_sp) + return m_opaque_sp.get() != rhs.m_opaque_sp.get(); + return (m_opaque_sp && !rhs.m_opaque_sp) || (rhs.m_opaque_sp && !m_opaque_sp); +} + +break_id_t +SBBreakpoint::GetID () const +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + break_id_t break_id = LLDB_INVALID_BREAK_ID; + if (m_opaque_sp) + break_id = m_opaque_sp->GetID(); + + if (log) + { + if (break_id == LLDB_INVALID_BREAK_ID) + log->Printf ("SBBreakpoint(%p)::GetID () => LLDB_INVALID_BREAK_ID", m_opaque_sp.get()); + else + log->Printf ("SBBreakpoint(%p)::GetID () => %u", m_opaque_sp.get(), break_id); + } + + return break_id; +} + + +bool +SBBreakpoint::IsValid() const +{ + return (bool) m_opaque_sp; +} + +void +SBBreakpoint::ClearAllBreakpointSites () +{ + if (m_opaque_sp) + { + Mutex::Locker api_locker (m_opaque_sp->GetTarget().GetAPIMutex()); + m_opaque_sp->ClearAllBreakpointSites (); + } +} + +SBBreakpointLocation +SBBreakpoint::FindLocationByAddress (addr_t vm_addr) +{ + SBBreakpointLocation sb_bp_location; + + if (m_opaque_sp) + { + if (vm_addr != LLDB_INVALID_ADDRESS) + { + Mutex::Locker api_locker (m_opaque_sp->GetTarget().GetAPIMutex()); + Address address; + Target &target = m_opaque_sp->GetTarget(); + if (target.GetSectionLoadList().ResolveLoadAddress (vm_addr, address) == false) + { + address.SetRawAddress (vm_addr); + } + sb_bp_location.SetLocation (m_opaque_sp->FindLocationByAddress (address)); + } + } + return sb_bp_location; +} + +break_id_t +SBBreakpoint::FindLocationIDByAddress (addr_t vm_addr) +{ + break_id_t break_id = LLDB_INVALID_BREAK_ID; + + if (m_opaque_sp && vm_addr != LLDB_INVALID_ADDRESS) + { + Mutex::Locker api_locker (m_opaque_sp->GetTarget().GetAPIMutex()); + Address address; + Target &target = m_opaque_sp->GetTarget(); + if (target.GetSectionLoadList().ResolveLoadAddress (vm_addr, address) == false) + { + address.SetRawAddress (vm_addr); + } + break_id = m_opaque_sp->FindLocationIDByAddress (address); + } + + return break_id; +} + +SBBreakpointLocation +SBBreakpoint::FindLocationByID (break_id_t bp_loc_id) +{ + SBBreakpointLocation sb_bp_location; + + if (m_opaque_sp) + { + Mutex::Locker api_locker (m_opaque_sp->GetTarget().GetAPIMutex()); + sb_bp_location.SetLocation (m_opaque_sp->FindLocationByID (bp_loc_id)); + } + + return sb_bp_location; +} + +SBBreakpointLocation +SBBreakpoint::GetLocationAtIndex (uint32_t index) +{ + SBBreakpointLocation sb_bp_location; + + if (m_opaque_sp) + { + Mutex::Locker api_locker (m_opaque_sp->GetTarget().GetAPIMutex()); + sb_bp_location.SetLocation (m_opaque_sp->GetLocationAtIndex (index)); + } + + return sb_bp_location; +} + +void +SBBreakpoint::SetEnabled (bool enable) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + if (log) + log->Printf ("SBBreakpoint(%p)::SetEnabled (enabled=%i)", m_opaque_sp.get(), enable); + + if (m_opaque_sp) + { + Mutex::Locker api_locker (m_opaque_sp->GetTarget().GetAPIMutex()); + m_opaque_sp->SetEnabled (enable); + } +} + +bool +SBBreakpoint::IsEnabled () +{ + if (m_opaque_sp) + { + Mutex::Locker api_locker (m_opaque_sp->GetTarget().GetAPIMutex()); + return m_opaque_sp->IsEnabled(); + } + else + return false; +} + +void +SBBreakpoint::SetOneShot (bool one_shot) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + if (log) + log->Printf ("SBBreakpoint(%p)::SetOneShot (one_shot=%i)", m_opaque_sp.get(), one_shot); + + if (m_opaque_sp) + { + Mutex::Locker api_locker (m_opaque_sp->GetTarget().GetAPIMutex()); + m_opaque_sp->SetOneShot (one_shot); + } +} + +bool +SBBreakpoint::IsOneShot () const +{ + if (m_opaque_sp) + { + Mutex::Locker api_locker (m_opaque_sp->GetTarget().GetAPIMutex()); + return m_opaque_sp->IsOneShot(); + } + else + return false; +} + +bool +SBBreakpoint::IsInternal () +{ + if (m_opaque_sp) + { + Mutex::Locker api_locker (m_opaque_sp->GetTarget().GetAPIMutex()); + return m_opaque_sp->IsInternal(); + } + else + return false; +} + +void +SBBreakpoint::SetIgnoreCount (uint32_t count) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + if (log) + log->Printf ("SBBreakpoint(%p)::SetIgnoreCount (count=%u)", m_opaque_sp.get(), count); + + if (m_opaque_sp) + { + Mutex::Locker api_locker (m_opaque_sp->GetTarget().GetAPIMutex()); + m_opaque_sp->SetIgnoreCount (count); + } +} + +void +SBBreakpoint::SetCondition (const char *condition) +{ + if (m_opaque_sp) + { + Mutex::Locker api_locker (m_opaque_sp->GetTarget().GetAPIMutex()); + m_opaque_sp->SetCondition (condition); + } +} + +const char * +SBBreakpoint::GetCondition () +{ + if (m_opaque_sp) + { + Mutex::Locker api_locker (m_opaque_sp->GetTarget().GetAPIMutex()); + return m_opaque_sp->GetConditionText (); + } + return NULL; +} + +uint32_t +SBBreakpoint::GetHitCount () const +{ + uint32_t count = 0; + if (m_opaque_sp) + { + Mutex::Locker api_locker (m_opaque_sp->GetTarget().GetAPIMutex()); + count = m_opaque_sp->GetHitCount(); + } + + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (log) + log->Printf ("SBBreakpoint(%p)::GetHitCount () => %u", m_opaque_sp.get(), count); + + return count; +} + +uint32_t +SBBreakpoint::GetIgnoreCount () const +{ + uint32_t count = 0; + if (m_opaque_sp) + { + Mutex::Locker api_locker (m_opaque_sp->GetTarget().GetAPIMutex()); + count = m_opaque_sp->GetIgnoreCount(); + } + + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (log) + log->Printf ("SBBreakpoint(%p)::GetIgnoreCount () => %u", m_opaque_sp.get(), count); + + return count; +} + +void +SBBreakpoint::SetThreadID (tid_t tid) +{ + if (m_opaque_sp) + { + Mutex::Locker api_locker (m_opaque_sp->GetTarget().GetAPIMutex()); + m_opaque_sp->SetThreadID (tid); + } + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (log) + log->Printf ("SBBreakpoint(%p)::SetThreadID (tid=0x%4.4" PRIx64 ")", m_opaque_sp.get(), tid); + +} + +tid_t +SBBreakpoint::GetThreadID () +{ + tid_t tid = LLDB_INVALID_THREAD_ID; + if (m_opaque_sp) + { + Mutex::Locker api_locker (m_opaque_sp->GetTarget().GetAPIMutex()); + tid = m_opaque_sp->GetThreadID(); + } + + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (log) + log->Printf ("SBBreakpoint(%p)::GetThreadID () => 0x%4.4" PRIx64, m_opaque_sp.get(), tid); + return tid; +} + +void +SBBreakpoint::SetThreadIndex (uint32_t index) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (log) + log->Printf ("SBBreakpoint(%p)::SetThreadIndex (%u)", m_opaque_sp.get(), index); + if (m_opaque_sp) + { + Mutex::Locker api_locker (m_opaque_sp->GetTarget().GetAPIMutex()); + m_opaque_sp->GetOptions()->GetThreadSpec()->SetIndex (index); + } +} + +uint32_t +SBBreakpoint::GetThreadIndex() const +{ + uint32_t thread_idx = UINT32_MAX; + if (m_opaque_sp) + { + Mutex::Locker api_locker (m_opaque_sp->GetTarget().GetAPIMutex()); + const ThreadSpec *thread_spec = m_opaque_sp->GetOptions()->GetThreadSpecNoCreate(); + if (thread_spec != NULL) + thread_idx = thread_spec->GetIndex(); + } + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (log) + log->Printf ("SBBreakpoint(%p)::GetThreadIndex () => %u", m_opaque_sp.get(), thread_idx); + + return thread_idx; +} + + +void +SBBreakpoint::SetThreadName (const char *thread_name) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (log) + log->Printf ("SBBreakpoint(%p)::SetThreadName (%s)", m_opaque_sp.get(), thread_name); + + if (m_opaque_sp) + { + Mutex::Locker api_locker (m_opaque_sp->GetTarget().GetAPIMutex()); + m_opaque_sp->GetOptions()->GetThreadSpec()->SetName (thread_name); + } +} + +const char * +SBBreakpoint::GetThreadName () const +{ + const char *name = NULL; + if (m_opaque_sp) + { + Mutex::Locker api_locker (m_opaque_sp->GetTarget().GetAPIMutex()); + const ThreadSpec *thread_spec = m_opaque_sp->GetOptions()->GetThreadSpecNoCreate(); + if (thread_spec != NULL) + name = thread_spec->GetName(); + } + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (log) + log->Printf ("SBBreakpoint(%p)::GetThreadName () => %s", m_opaque_sp.get(), name); + + return name; +} + +void +SBBreakpoint::SetQueueName (const char *queue_name) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (log) + log->Printf ("SBBreakpoint(%p)::SetQueueName (%s)", m_opaque_sp.get(), queue_name); + if (m_opaque_sp) + { + Mutex::Locker api_locker (m_opaque_sp->GetTarget().GetAPIMutex()); + m_opaque_sp->GetOptions()->GetThreadSpec()->SetQueueName (queue_name); + } +} + +const char * +SBBreakpoint::GetQueueName () const +{ + const char *name = NULL; + if (m_opaque_sp) + { + Mutex::Locker api_locker (m_opaque_sp->GetTarget().GetAPIMutex()); + const ThreadSpec *thread_spec = m_opaque_sp->GetOptions()->GetThreadSpecNoCreate(); + if (thread_spec) + name = thread_spec->GetQueueName(); + } + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (log) + log->Printf ("SBBreakpoint(%p)::GetQueueName () => %s", m_opaque_sp.get(), name); + + return name; +} + +size_t +SBBreakpoint::GetNumResolvedLocations() const +{ + size_t num_resolved = 0; + if (m_opaque_sp) + { + Mutex::Locker api_locker (m_opaque_sp->GetTarget().GetAPIMutex()); + num_resolved = m_opaque_sp->GetNumResolvedLocations(); + } + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (log) + log->Printf ("SBBreakpoint(%p)::GetNumResolvedLocations () => %" PRIu64, m_opaque_sp.get(), (uint64_t)num_resolved); + return num_resolved; +} + +size_t +SBBreakpoint::GetNumLocations() const +{ + size_t num_locs = 0; + if (m_opaque_sp) + { + Mutex::Locker api_locker (m_opaque_sp->GetTarget().GetAPIMutex()); + num_locs = m_opaque_sp->GetNumLocations(); + } + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (log) + log->Printf ("SBBreakpoint(%p)::GetNumLocations () => %" PRIu64, m_opaque_sp.get(), (uint64_t)num_locs); + return num_locs; +} + +bool +SBBreakpoint::GetDescription (SBStream &s) +{ + if (m_opaque_sp) + { + Mutex::Locker api_locker (m_opaque_sp->GetTarget().GetAPIMutex()); + s.Printf("SBBreakpoint: id = %i, ", m_opaque_sp->GetID()); + m_opaque_sp->GetResolverDescription (s.get()); + m_opaque_sp->GetFilterDescription (s.get()); + const size_t num_locations = m_opaque_sp->GetNumLocations (); + s.Printf(", locations = %" PRIu64, (uint64_t)num_locations); + return true; + } + s.Printf ("No value"); + return false; +} + +bool +SBBreakpoint::PrivateBreakpointHitCallback +( + void *baton, + StoppointCallbackContext *ctx, + lldb::user_id_t break_id, + lldb::user_id_t break_loc_id +) +{ + ExecutionContext exe_ctx (ctx->exe_ctx_ref); + BreakpointSP bp_sp(exe_ctx.GetTargetRef().GetBreakpointList().FindBreakpointByID(break_id)); + if (baton && bp_sp) + { + CallbackData *data = (CallbackData *)baton; + lldb_private::Breakpoint *bp = bp_sp.get(); + if (bp && data->callback) + { + Process *process = exe_ctx.GetProcessPtr(); + if (process) + { + SBProcess sb_process (process->shared_from_this()); + SBThread sb_thread; + SBBreakpointLocation sb_location; + assert (bp_sp); + sb_location.SetLocation (bp_sp->FindLocationByID (break_loc_id)); + Thread *thread = exe_ctx.GetThreadPtr(); + if (thread) + sb_thread.SetThread(thread->shared_from_this()); + + return data->callback (data->callback_baton, + sb_process, + sb_thread, + sb_location); + } + } + } + return true; // Return true if we should stop at this breakpoint +} + +void +SBBreakpoint::SetCallback (BreakpointHitCallback callback, void *baton) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + if (log) + log->Printf ("SBBreakpoint(%p)::SetCallback (callback=%p, baton=%p)", m_opaque_sp.get(), callback, baton); + + if (m_opaque_sp) + { + Mutex::Locker api_locker (m_opaque_sp->GetTarget().GetAPIMutex()); + BatonSP baton_sp(new SBBreakpointCallbackBaton (callback, baton)); + m_opaque_sp->SetCallback (SBBreakpoint::PrivateBreakpointHitCallback, baton_sp, false); + } +} + + +lldb_private::Breakpoint * +SBBreakpoint::operator->() const +{ + return m_opaque_sp.get(); +} + +lldb_private::Breakpoint * +SBBreakpoint::get() const +{ + return m_opaque_sp.get(); +} + +lldb::BreakpointSP & +SBBreakpoint::operator *() +{ + return m_opaque_sp; +} + +const lldb::BreakpointSP & +SBBreakpoint::operator *() const +{ + return m_opaque_sp; +} + +bool +SBBreakpoint::EventIsBreakpointEvent (const lldb::SBEvent &event) +{ + return Breakpoint::BreakpointEventData::GetEventDataFromEvent(event.get()) != NULL; + +} + +BreakpointEventType +SBBreakpoint::GetBreakpointEventTypeFromEvent (const SBEvent& event) +{ + if (event.IsValid()) + return Breakpoint::BreakpointEventData::GetBreakpointEventTypeFromEvent (event.GetSP()); + return eBreakpointEventTypeInvalidType; +} + +SBBreakpoint +SBBreakpoint::GetBreakpointFromEvent (const lldb::SBEvent& event) +{ + SBBreakpoint sb_breakpoint; + if (event.IsValid()) + sb_breakpoint.m_opaque_sp = Breakpoint::BreakpointEventData::GetBreakpointFromEvent (event.GetSP()); + return sb_breakpoint; +} + +SBBreakpointLocation +SBBreakpoint::GetBreakpointLocationAtIndexFromEvent (const lldb::SBEvent& event, uint32_t loc_idx) +{ + SBBreakpointLocation sb_breakpoint_loc; + if (event.IsValid()) + sb_breakpoint_loc.SetLocation (Breakpoint::BreakpointEventData::GetBreakpointLocationAtIndexFromEvent (event.GetSP(), loc_idx)); + return sb_breakpoint_loc; +} + +uint32_t +SBBreakpoint::GetNumBreakpointLocationsFromEvent (const lldb::SBEvent &event) +{ + uint32_t num_locations = 0; + if (event.IsValid()) + num_locations = (Breakpoint::BreakpointEventData::GetNumBreakpointLocationsFromEvent (event.GetSP())); + return num_locations; +} + + diff --git a/contrib/llvm/tools/lldb/source/API/SBBreakpointLocation.cpp b/contrib/llvm/tools/lldb/source/API/SBBreakpointLocation.cpp new file mode 100644 index 00000000000..6fdf59f38b4 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/API/SBBreakpointLocation.cpp @@ -0,0 +1,320 @@ +//===-- SBBreakpointLocation.cpp --------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/API/SBBreakpointLocation.h" +#include "lldb/API/SBDefines.h" +#include "lldb/API/SBAddress.h" +#include "lldb/API/SBDebugger.h" +#include "lldb/API/SBStream.h" + +#include "lldb/lldb-types.h" +#include "lldb/lldb-defines.h" +#include "lldb/Breakpoint/Breakpoint.h" +#include "lldb/Breakpoint/BreakpointLocation.h" +#include "lldb/Target/ThreadSpec.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Stream.h" +#include "lldb/Core/StreamFile.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/ThreadSpec.h" + +using namespace lldb; +using namespace lldb_private; + + +SBBreakpointLocation::SBBreakpointLocation () : + m_opaque_sp () +{ +} + +SBBreakpointLocation::SBBreakpointLocation (const lldb::BreakpointLocationSP &break_loc_sp) : + m_opaque_sp (break_loc_sp) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + if (log) + { + SBStream sstr; + GetDescription (sstr, lldb::eDescriptionLevelBrief); + log->Printf ("SBBreakpointLocation::SBBreakpointLocaiton (const lldb::BreakpointLocationsSP &break_loc_sp" + "=%p) => this.sp = %p (%s)", break_loc_sp.get(), m_opaque_sp.get(), sstr.GetData()); + } +} + +SBBreakpointLocation::SBBreakpointLocation(const SBBreakpointLocation &rhs) : + m_opaque_sp (rhs.m_opaque_sp) +{ +} + +const SBBreakpointLocation & +SBBreakpointLocation::operator = (const SBBreakpointLocation &rhs) +{ + if (this != &rhs) + m_opaque_sp = rhs.m_opaque_sp; + return *this; +} + + +SBBreakpointLocation::~SBBreakpointLocation () +{ +} + +bool +SBBreakpointLocation::IsValid() const +{ + return m_opaque_sp.get() != NULL; +} + +SBAddress +SBBreakpointLocation::GetAddress () +{ + if (m_opaque_sp) + return SBAddress(&m_opaque_sp->GetAddress()); + else + return SBAddress(); +} + +addr_t +SBBreakpointLocation::GetLoadAddress () +{ + addr_t ret_addr = LLDB_INVALID_ADDRESS; + + if (m_opaque_sp) + { + Mutex::Locker api_locker (m_opaque_sp->GetBreakpoint().GetTarget().GetAPIMutex()); + ret_addr = m_opaque_sp->GetLoadAddress(); + } + + return ret_addr; +} + +void +SBBreakpointLocation::SetEnabled (bool enabled) +{ + if (m_opaque_sp) + { + Mutex::Locker api_locker (m_opaque_sp->GetBreakpoint().GetTarget().GetAPIMutex()); + m_opaque_sp->SetEnabled (enabled); + } +} + +bool +SBBreakpointLocation::IsEnabled () +{ + if (m_opaque_sp) + { + Mutex::Locker api_locker (m_opaque_sp->GetBreakpoint().GetTarget().GetAPIMutex()); + return m_opaque_sp->IsEnabled(); + } + else + return false; +} + +uint32_t +SBBreakpointLocation::GetIgnoreCount () +{ + if (m_opaque_sp) + { + Mutex::Locker api_locker (m_opaque_sp->GetBreakpoint().GetTarget().GetAPIMutex()); + return m_opaque_sp->GetIgnoreCount(); + } + else + return 0; +} + +void +SBBreakpointLocation::SetIgnoreCount (uint32_t n) +{ + if (m_opaque_sp) + { + Mutex::Locker api_locker (m_opaque_sp->GetBreakpoint().GetTarget().GetAPIMutex()); + m_opaque_sp->SetIgnoreCount (n); + } +} + +void +SBBreakpointLocation::SetCondition (const char *condition) +{ + if (m_opaque_sp) + { + Mutex::Locker api_locker (m_opaque_sp->GetBreakpoint().GetTarget().GetAPIMutex()); + m_opaque_sp->SetCondition (condition); + } +} + +const char * +SBBreakpointLocation::GetCondition () +{ + if (m_opaque_sp) + { + Mutex::Locker api_locker (m_opaque_sp->GetBreakpoint().GetTarget().GetAPIMutex()); + return m_opaque_sp->GetConditionText (); + } + return NULL; +} + +void +SBBreakpointLocation::SetThreadID (tid_t thread_id) +{ + if (m_opaque_sp) + { + Mutex::Locker api_locker (m_opaque_sp->GetBreakpoint().GetTarget().GetAPIMutex()); + m_opaque_sp->SetThreadID (thread_id); + } +} + +tid_t +SBBreakpointLocation::GetThreadID () +{ + tid_t tid = LLDB_INVALID_THREAD_ID; + if (m_opaque_sp) + { + Mutex::Locker api_locker (m_opaque_sp->GetBreakpoint().GetTarget().GetAPIMutex()); + return m_opaque_sp->GetThreadID(); + } + return tid; +} + +void +SBBreakpointLocation::SetThreadIndex (uint32_t index) +{ + if (m_opaque_sp) + { + Mutex::Locker api_locker (m_opaque_sp->GetBreakpoint().GetTarget().GetAPIMutex()); + m_opaque_sp->SetThreadIndex (index); + } +} + +uint32_t +SBBreakpointLocation::GetThreadIndex() const +{ + uint32_t thread_idx = UINT32_MAX; + if (m_opaque_sp) + { + Mutex::Locker api_locker (m_opaque_sp->GetBreakpoint().GetTarget().GetAPIMutex()); + return m_opaque_sp->GetThreadIndex(); + } + return thread_idx; +} + + +void +SBBreakpointLocation::SetThreadName (const char *thread_name) +{ + if (m_opaque_sp) + { + Mutex::Locker api_locker (m_opaque_sp->GetBreakpoint().GetTarget().GetAPIMutex()); + m_opaque_sp->SetThreadName (thread_name); + } +} + +const char * +SBBreakpointLocation::GetThreadName () const +{ + if (m_opaque_sp) + { + Mutex::Locker api_locker (m_opaque_sp->GetBreakpoint().GetTarget().GetAPIMutex()); + return m_opaque_sp->GetThreadName(); + } + return NULL; +} + +void +SBBreakpointLocation::SetQueueName (const char *queue_name) +{ + if (m_opaque_sp) + { + Mutex::Locker api_locker (m_opaque_sp->GetBreakpoint().GetTarget().GetAPIMutex()); + m_opaque_sp->SetQueueName (queue_name); + } +} + +const char * +SBBreakpointLocation::GetQueueName () const +{ + if (m_opaque_sp) + { + Mutex::Locker api_locker (m_opaque_sp->GetBreakpoint().GetTarget().GetAPIMutex()); + m_opaque_sp->GetQueueName (); + } + return NULL; +} + +bool +SBBreakpointLocation::IsResolved () +{ + if (m_opaque_sp) + { + Mutex::Locker api_locker (m_opaque_sp->GetBreakpoint().GetTarget().GetAPIMutex()); + return m_opaque_sp->IsResolved(); + } + return false; +} + +void +SBBreakpointLocation::SetLocation (const lldb::BreakpointLocationSP &break_loc_sp) +{ + // Uninstall the callbacks? + m_opaque_sp = break_loc_sp; +} + +bool +SBBreakpointLocation::GetDescription (SBStream &description, DescriptionLevel level) +{ + Stream &strm = description.ref(); + + if (m_opaque_sp) + { + Mutex::Locker api_locker (m_opaque_sp->GetBreakpoint().GetTarget().GetAPIMutex()); + m_opaque_sp->GetDescription (&strm, level); + strm.EOL(); + } + else + strm.PutCString ("No value"); + + return true; +} + +break_id_t +SBBreakpointLocation::GetID () +{ + if (m_opaque_sp) + { + Mutex::Locker api_locker (m_opaque_sp->GetBreakpoint().GetTarget().GetAPIMutex()); + return m_opaque_sp->GetID (); + } + else + return LLDB_INVALID_BREAK_ID; +} + +SBBreakpoint +SBBreakpointLocation::GetBreakpoint () +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + //if (log) + // log->Printf ("SBBreakpointLocation::GetBreakpoint ()"); + + SBBreakpoint sb_bp; + if (m_opaque_sp) + { + Mutex::Locker api_locker (m_opaque_sp->GetBreakpoint().GetTarget().GetAPIMutex()); + *sb_bp = m_opaque_sp->GetBreakpoint ().shared_from_this(); + } + + if (log) + { + SBStream sstr; + sb_bp.GetDescription (sstr); + log->Printf ("SBBreakpointLocation(%p)::GetBreakpoint () => SBBreakpoint(%p) %s", + m_opaque_sp.get(), sb_bp.get(), sstr.GetData()); + } + return sb_bp; +} + diff --git a/contrib/llvm/tools/lldb/source/API/SBBroadcaster.cpp b/contrib/llvm/tools/lldb/source/API/SBBroadcaster.cpp new file mode 100644 index 00000000000..7168305ac80 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/API/SBBroadcaster.cpp @@ -0,0 +1,196 @@ +//===-- SBBroadcaster.cpp ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Core/Broadcaster.h" +#include "lldb/Core/Log.h" + +#include "lldb/API/SBBroadcaster.h" +#include "lldb/API/SBListener.h" +#include "lldb/API/SBEvent.h" + +using namespace lldb; +using namespace lldb_private; + + +SBBroadcaster::SBBroadcaster () : + m_opaque_sp (), + m_opaque_ptr (NULL) +{ +} + +SBBroadcaster::SBBroadcaster (const char *name) : + m_opaque_sp (new Broadcaster (NULL, name)), + m_opaque_ptr (NULL) +{ + m_opaque_ptr = m_opaque_sp.get(); + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API | LIBLLDB_LOG_VERBOSE)); + + if (log) + log->Printf ("SBBroadcaster::SBBroadcaster (name=\"%s\") => SBBroadcaster(%p)", + name, m_opaque_ptr); +} + +SBBroadcaster::SBBroadcaster (lldb_private::Broadcaster *broadcaster, bool owns) : + m_opaque_sp (owns ? broadcaster : NULL), + m_opaque_ptr (broadcaster) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API | LIBLLDB_LOG_VERBOSE)); + + if (log) + log->Printf ("SBBroadcaster::SBBroadcaster (broadcaster=%p, bool owns=%i) => SBBroadcaster(%p)", + broadcaster, owns, m_opaque_ptr); +} + +SBBroadcaster::SBBroadcaster (const SBBroadcaster &rhs) : + m_opaque_sp (rhs.m_opaque_sp), + m_opaque_ptr (rhs.m_opaque_ptr) +{ +} + +const SBBroadcaster & +SBBroadcaster::operator = (const SBBroadcaster &rhs) +{ + if (this != &rhs) + { + m_opaque_sp = rhs.m_opaque_sp; + m_opaque_ptr = rhs.m_opaque_ptr; + } + return *this; +} + +SBBroadcaster::~SBBroadcaster() +{ + reset (NULL, false); +} + +void +SBBroadcaster::BroadcastEventByType (uint32_t event_type, bool unique) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + if (log) + log->Printf ("SBBroadcaster(%p)::BroadcastEventByType (event_type=0x%8.8x, unique=%i)", m_opaque_ptr, event_type, unique); + + if (m_opaque_ptr == NULL) + return; + + if (unique) + m_opaque_ptr->BroadcastEventIfUnique (event_type); + else + m_opaque_ptr->BroadcastEvent (event_type); +} + +void +SBBroadcaster::BroadcastEvent (const SBEvent &event, bool unique) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + if (log) + log->Printf ("SBBroadcaster(%p)::BroadcastEventByType (SBEvent(%p), unique=%i)", m_opaque_ptr, event.get(), unique); + + if (m_opaque_ptr == NULL) + return; + + EventSP event_sp = event.GetSP (); + if (unique) + m_opaque_ptr->BroadcastEventIfUnique (event_sp); + else + m_opaque_ptr->BroadcastEvent (event_sp); +} + +void +SBBroadcaster::AddInitialEventsToListener (const SBListener &listener, uint32_t requested_events) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (log) + log->Printf ("SBBroadcaster(%p)::AddInitialEventsToListener (SBListener(%p), event_mask=0x%8.8x)", m_opaque_ptr, listener.get(), requested_events); + if (m_opaque_ptr) + m_opaque_ptr->AddInitialEventsToListener (listener.get(), requested_events); +} + +uint32_t +SBBroadcaster::AddListener (const SBListener &listener, uint32_t event_mask) +{ + if (m_opaque_ptr) + return m_opaque_ptr->AddListener (listener.get(), event_mask); + return 0; +} + +const char * +SBBroadcaster::GetName () const +{ + if (m_opaque_ptr) + return m_opaque_ptr->GetBroadcasterName().GetCString(); + return NULL; +} + +bool +SBBroadcaster::EventTypeHasListeners (uint32_t event_type) +{ + if (m_opaque_ptr) + return m_opaque_ptr->EventTypeHasListeners (event_type); + return false; +} + +bool +SBBroadcaster::RemoveListener (const SBListener &listener, uint32_t event_mask) +{ + if (m_opaque_ptr) + return m_opaque_ptr->RemoveListener (listener.get(), event_mask); + return false; +} + +Broadcaster * +SBBroadcaster::get () const +{ + return m_opaque_ptr; +} + +void +SBBroadcaster::reset (Broadcaster *broadcaster, bool owns) +{ + if (owns) + m_opaque_sp.reset (broadcaster); + else + m_opaque_sp.reset (); + m_opaque_ptr = broadcaster; +} + + +bool +SBBroadcaster::IsValid () const +{ + return m_opaque_ptr != NULL; +} + +void +SBBroadcaster::Clear () +{ + m_opaque_sp.reset(); + m_opaque_ptr = NULL; +} + +bool +SBBroadcaster::operator == (const SBBroadcaster &rhs) const +{ + return m_opaque_ptr == rhs.m_opaque_ptr; + +} + +bool +SBBroadcaster::operator != (const SBBroadcaster &rhs) const +{ + return m_opaque_ptr != rhs.m_opaque_ptr; +} + +bool +SBBroadcaster::operator < (const SBBroadcaster &rhs) const +{ + return m_opaque_ptr < rhs.m_opaque_ptr; +} diff --git a/contrib/llvm/tools/lldb/source/API/SBCommandInterpreter.cpp b/contrib/llvm/tools/lldb/source/API/SBCommandInterpreter.cpp new file mode 100644 index 00000000000..0c839004601 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/API/SBCommandInterpreter.cpp @@ -0,0 +1,490 @@ +//===-- SBCommandInterpreter.cpp --------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "lldb/lldb-types.h" +#include "lldb/Core/SourceManager.h" +#include "lldb/Core/Listener.h" +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Interpreter/CommandObjectMultiword.h" +#include "lldb/Interpreter/CommandReturnObject.h" +#include "lldb/Target/Target.h" + +#include "lldb/API/SBBroadcaster.h" +#include "lldb/API/SBCommandReturnObject.h" +#include "lldb/API/SBCommandInterpreter.h" +#include "lldb/API/SBProcess.h" +#include "lldb/API/SBTarget.h" +#include "lldb/API/SBListener.h" +#include "lldb/API/SBStream.h" +#include "lldb/API/SBStringList.h" + +using namespace lldb; +using namespace lldb_private; + +class CommandPluginInterfaceImplementation : public CommandObjectParsed +{ +public: + CommandPluginInterfaceImplementation (CommandInterpreter &interpreter, + const char *name, + lldb::SBCommandPluginInterface* backend, + const char *help = NULL, + const char *syntax = NULL, + uint32_t flags = 0) : + CommandObjectParsed (interpreter, name, help, syntax, flags), + m_backend(backend) {} + + virtual bool + IsRemovable() const { return true; } + +protected: + virtual bool + DoExecute (Args& command, CommandReturnObject &result) + { + SBCommandReturnObject sb_return(&result); + SBCommandInterpreter sb_interpreter(&m_interpreter); + SBDebugger debugger_sb(m_interpreter.GetDebugger().shared_from_this()); + bool ret = m_backend->DoExecute (debugger_sb,(char**)command.GetArgumentVector(), sb_return); + sb_return.Release(); + return ret; + } + lldb::SBCommandPluginInterface* m_backend; +}; + +SBCommandInterpreter::SBCommandInterpreter (CommandInterpreter *interpreter) : + m_opaque_ptr (interpreter) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + if (log) + log->Printf ("SBCommandInterpreter::SBCommandInterpreter (interpreter=%p)" + " => SBCommandInterpreter(%p)", interpreter, m_opaque_ptr); +} + +SBCommandInterpreter::SBCommandInterpreter(const SBCommandInterpreter &rhs) : + m_opaque_ptr (rhs.m_opaque_ptr) +{ +} + +const SBCommandInterpreter & +SBCommandInterpreter::operator = (const SBCommandInterpreter &rhs) +{ + m_opaque_ptr = rhs.m_opaque_ptr; + return *this; +} + +SBCommandInterpreter::~SBCommandInterpreter () +{ +} + +bool +SBCommandInterpreter::IsValid() const +{ + return m_opaque_ptr != NULL; +} + + +bool +SBCommandInterpreter::CommandExists (const char *cmd) +{ + if (cmd && m_opaque_ptr) + return m_opaque_ptr->CommandExists (cmd); + return false; +} + +bool +SBCommandInterpreter::AliasExists (const char *cmd) +{ + if (cmd && m_opaque_ptr) + return m_opaque_ptr->AliasExists (cmd); + return false; +} + +lldb::ReturnStatus +SBCommandInterpreter::HandleCommand (const char *command_line, SBCommandReturnObject &result, bool add_to_history) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + if (log) + log->Printf ("SBCommandInterpreter(%p)::HandleCommand (command=\"%s\", SBCommandReturnObject(%p), add_to_history=%i)", + m_opaque_ptr, command_line, result.get(), add_to_history); + + result.Clear(); + if (command_line && m_opaque_ptr) + { + m_opaque_ptr->HandleCommand (command_line, add_to_history ? eLazyBoolYes : eLazyBoolNo, result.ref()); + } + else + { + result->AppendError ("SBCommandInterpreter or the command line is not valid"); + result->SetStatus (eReturnStatusFailed); + } + + // We need to get the value again, in case the command disabled the log! + log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API); + if (log) + { + SBStream sstr; + result.GetDescription (sstr); + log->Printf ("SBCommandInterpreter(%p)::HandleCommand (command=\"%s\", SBCommandReturnObject(%p): %s, add_to_history=%i) => %i", + m_opaque_ptr, command_line, result.get(), sstr.GetData(), add_to_history, result.GetStatus()); + } + + return result.GetStatus(); +} + +int +SBCommandInterpreter::HandleCompletion (const char *current_line, + const char *cursor, + const char *last_char, + int match_start_point, + int max_return_elements, + SBStringList &matches) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + int num_completions = 0; + + // Sanity check the arguments that are passed in: + // cursor & last_char have to be within the current_line. + if (current_line == NULL || cursor == NULL || last_char == NULL) + return 0; + + if (cursor < current_line || last_char < current_line) + return 0; + + size_t current_line_size = strlen (current_line); + if (cursor - current_line > current_line_size || last_char - current_line > current_line_size) + return 0; + + if (log) + log->Printf ("SBCommandInterpreter(%p)::HandleCompletion (current_line=\"%s\", cursor at: %" PRId64 ", last char at: %" PRId64 ", match_start_point: %d, max_return_elements: %d)", + m_opaque_ptr, current_line, (uint64_t) (cursor - current_line), (uint64_t) (last_char - current_line), match_start_point, max_return_elements); + + if (m_opaque_ptr) + { + lldb_private::StringList lldb_matches; + num_completions = m_opaque_ptr->HandleCompletion (current_line, cursor, last_char, match_start_point, + max_return_elements, lldb_matches); + + SBStringList temp_list (&lldb_matches); + matches.AppendList (temp_list); + } + if (log) + log->Printf ("SBCommandInterpreter(%p)::HandleCompletion - Found %d completions.", m_opaque_ptr, num_completions); + + return num_completions; +} + +int +SBCommandInterpreter::HandleCompletion (const char *current_line, + uint32_t cursor_pos, + int match_start_point, + int max_return_elements, + lldb::SBStringList &matches) +{ + const char *cursor = current_line + cursor_pos; + const char *last_char = current_line + strlen (current_line); + return HandleCompletion (current_line, cursor, last_char, match_start_point, max_return_elements, matches); +} + +bool +SBCommandInterpreter::HasCommands () +{ + if (m_opaque_ptr) + return m_opaque_ptr->HasCommands(); + return false; +} + +bool +SBCommandInterpreter::HasAliases () +{ + if (m_opaque_ptr) + return m_opaque_ptr->HasAliases(); + return false; +} + +bool +SBCommandInterpreter::HasAliasOptions () +{ + if (m_opaque_ptr) + return m_opaque_ptr->HasAliasOptions (); + return false; +} + +SBProcess +SBCommandInterpreter::GetProcess () +{ + SBProcess sb_process; + ProcessSP process_sp; + if (m_opaque_ptr) + { + TargetSP target_sp(m_opaque_ptr->GetDebugger().GetSelectedTarget()); + if (target_sp) + { + Mutex::Locker api_locker(target_sp->GetAPIMutex()); + process_sp = target_sp->GetProcessSP(); + sb_process.SetSP(process_sp); + } + } + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + if (log) + log->Printf ("SBCommandInterpreter(%p)::GetProcess () => SBProcess(%p)", + m_opaque_ptr, process_sp.get()); + + + return sb_process; +} + +SBDebugger +SBCommandInterpreter::GetDebugger () +{ + SBDebugger sb_debugger; + if (m_opaque_ptr) + sb_debugger.reset(m_opaque_ptr->GetDebugger().shared_from_this()); + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + if (log) + log->Printf ("SBCommandInterpreter(%p)::GetDebugger () => SBDebugger(%p)", + m_opaque_ptr, sb_debugger.get()); + + + return sb_debugger; +} + +CommandInterpreter * +SBCommandInterpreter::get () +{ + return m_opaque_ptr; +} + +CommandInterpreter & +SBCommandInterpreter::ref () +{ + assert (m_opaque_ptr); + return *m_opaque_ptr; +} + +void +SBCommandInterpreter::reset (lldb_private::CommandInterpreter *interpreter) +{ + m_opaque_ptr = interpreter; +} + +void +SBCommandInterpreter::SourceInitFileInHomeDirectory (SBCommandReturnObject &result) +{ + result.Clear(); + if (m_opaque_ptr) + { + TargetSP target_sp(m_opaque_ptr->GetDebugger().GetSelectedTarget()); + Mutex::Locker api_locker; + if (target_sp) + api_locker.Lock(target_sp->GetAPIMutex()); + m_opaque_ptr->SourceInitFile (false, result.ref()); + } + else + { + result->AppendError ("SBCommandInterpreter is not valid"); + result->SetStatus (eReturnStatusFailed); + } + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + if (log) + log->Printf ("SBCommandInterpreter(%p)::SourceInitFileInHomeDirectory (&SBCommandReturnObject(%p))", + m_opaque_ptr, result.get()); + +} + +void +SBCommandInterpreter::SourceInitFileInCurrentWorkingDirectory (SBCommandReturnObject &result) +{ + result.Clear(); + if (m_opaque_ptr) + { + TargetSP target_sp(m_opaque_ptr->GetDebugger().GetSelectedTarget()); + Mutex::Locker api_locker; + if (target_sp) + api_locker.Lock(target_sp->GetAPIMutex()); + m_opaque_ptr->SourceInitFile (true, result.ref()); + } + else + { + result->AppendError ("SBCommandInterpreter is not valid"); + result->SetStatus (eReturnStatusFailed); + } + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + if (log) + log->Printf ("SBCommandInterpreter(%p)::SourceInitFileInCurrentWorkingDirectory (&SBCommandReturnObject(%p))", + m_opaque_ptr, result.get()); +} + +SBBroadcaster +SBCommandInterpreter::GetBroadcaster () +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + SBBroadcaster broadcaster (m_opaque_ptr, false); + + if (log) + log->Printf ("SBCommandInterpreter(%p)::GetBroadcaster() => SBBroadcaster(%p)", + m_opaque_ptr, broadcaster.get()); + + return broadcaster; +} + +const char * +SBCommandInterpreter::GetBroadcasterClass () +{ + return Communication::GetStaticBroadcasterClass().AsCString(); +} + +const char * +SBCommandInterpreter::GetArgumentTypeAsCString (const lldb::CommandArgumentType arg_type) +{ + return CommandObject::GetArgumentTypeAsCString (arg_type); +} + +const char * +SBCommandInterpreter::GetArgumentDescriptionAsCString (const lldb::CommandArgumentType arg_type) +{ + return CommandObject::GetArgumentDescriptionAsCString (arg_type); +} + +bool +SBCommandInterpreter::SetCommandOverrideCallback (const char *command_name, + lldb::CommandOverrideCallback callback, + void *baton) +{ + if (command_name && command_name[0] && m_opaque_ptr) + { + std::string command_name_str (command_name); + CommandObject *cmd_obj = m_opaque_ptr->GetCommandObjectForCommand(command_name_str); + if (cmd_obj) + { + assert(command_name_str.empty()); + cmd_obj->SetOverrideCallback (callback, baton); + return true; + } + } + return false; +} + +#ifndef LLDB_DISABLE_PYTHON + +// Defined in the SWIG source file +extern "C" void +init_lldb(void); + +#else + +extern "C" void init_lldb(void); + +// Usually defined in the SWIG source file, but we have sripting disabled +extern "C" void +init_lldb(void) +{ +} + +#endif + +void +SBCommandInterpreter::InitializeSWIG () +{ + static bool g_initialized = false; + if (!g_initialized) + { + g_initialized = true; +#ifndef LLDB_DISABLE_PYTHON + ScriptInterpreter::InitializeInterpreter (init_lldb); +#endif + } +} + +lldb::SBCommand +SBCommandInterpreter::AddMultiwordCommand (const char* name, const char* help) +{ + CommandObjectMultiword *new_command = new CommandObjectMultiword(*m_opaque_ptr,name,help); + new_command->SetRemovable (true); + lldb::CommandObjectSP new_command_sp(new_command); + if (new_command_sp && m_opaque_ptr->AddUserCommand(name, new_command_sp, true)) + return lldb::SBCommand(new_command_sp); + return lldb::SBCommand(); +} + +lldb::SBCommand +SBCommandInterpreter::AddCommand (const char* name, lldb::SBCommandPluginInterface* impl, const char* help) +{ + lldb::CommandObjectSP new_command_sp; + new_command_sp.reset(new CommandPluginInterfaceImplementation(*m_opaque_ptr,name,impl,help)); + + if (new_command_sp && m_opaque_ptr->AddUserCommand(name, new_command_sp, true)) + return lldb::SBCommand(new_command_sp); + return lldb::SBCommand(); +} + +SBCommand::SBCommand () +{} + +SBCommand::SBCommand (lldb::CommandObjectSP cmd_sp) : m_opaque_sp (cmd_sp) +{} + +bool +SBCommand::IsValid () +{ + return (bool)m_opaque_sp; +} + +const char* +SBCommand::GetName () +{ + if (IsValid ()) + return m_opaque_sp->GetCommandName (); + return NULL; +} + +const char* +SBCommand::GetHelp () +{ + if (IsValid ()) + return m_opaque_sp->GetHelp (); + return NULL; +} + +lldb::SBCommand +SBCommand::AddMultiwordCommand (const char* name, const char* help) +{ + if (!IsValid ()) + return lldb::SBCommand(); + if (m_opaque_sp->IsMultiwordObject() == false) + return lldb::SBCommand(); + CommandObjectMultiword *new_command = new CommandObjectMultiword(m_opaque_sp->GetCommandInterpreter(),name,help); + new_command->SetRemovable (true); + lldb::CommandObjectSP new_command_sp(new_command); + if (new_command_sp && m_opaque_sp->LoadSubCommand(name,new_command_sp)) + return lldb::SBCommand(new_command_sp); + return lldb::SBCommand(); +} + +lldb::SBCommand +SBCommand::AddCommand (const char* name, lldb::SBCommandPluginInterface *impl, const char* help) +{ + if (!IsValid ()) + return lldb::SBCommand(); + if (m_opaque_sp->IsMultiwordObject() == false) + return lldb::SBCommand(); + lldb::CommandObjectSP new_command_sp; + new_command_sp.reset(new CommandPluginInterfaceImplementation(m_opaque_sp->GetCommandInterpreter(),name,impl,help)); + if (new_command_sp && m_opaque_sp->LoadSubCommand(name,new_command_sp)) + return lldb::SBCommand(new_command_sp); + return lldb::SBCommand(); +} + diff --git a/contrib/llvm/tools/lldb/source/API/SBCommandReturnObject.cpp b/contrib/llvm/tools/lldb/source/API/SBCommandReturnObject.cpp new file mode 100644 index 00000000000..83d65637d3f --- /dev/null +++ b/contrib/llvm/tools/lldb/source/API/SBCommandReturnObject.cpp @@ -0,0 +1,352 @@ +//===-- SBCommandReturnObject.cpp -------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/API/SBCommandReturnObject.h" +#include "lldb/API/SBError.h" +#include "lldb/API/SBStream.h" + +#include "lldb/Core/Error.h" +#include "lldb/Core/Log.h" +#include "lldb/Interpreter/CommandReturnObject.h" + +using namespace lldb; +using namespace lldb_private; + +SBCommandReturnObject::SBCommandReturnObject () : + m_opaque_ap (new CommandReturnObject ()) +{ +} + +SBCommandReturnObject::SBCommandReturnObject (const SBCommandReturnObject &rhs): + m_opaque_ap () +{ + if (rhs.m_opaque_ap.get()) + m_opaque_ap.reset (new CommandReturnObject (*rhs.m_opaque_ap)); +} + +SBCommandReturnObject::SBCommandReturnObject (CommandReturnObject *ptr) : + m_opaque_ap (ptr) +{ +} + +CommandReturnObject * +SBCommandReturnObject::Release () +{ + return m_opaque_ap.release(); +} + +const SBCommandReturnObject & +SBCommandReturnObject::operator = (const SBCommandReturnObject &rhs) +{ + if (this != &rhs) + { + if (rhs.m_opaque_ap.get()) + m_opaque_ap.reset (new CommandReturnObject (*rhs.m_opaque_ap)); + else + m_opaque_ap.reset(); + } + return *this; +} + + +SBCommandReturnObject::~SBCommandReturnObject () +{ + // m_opaque_ap will automatically delete any pointer it owns +} + +bool +SBCommandReturnObject::IsValid() const +{ + return m_opaque_ap.get() != NULL; +} + + +const char * +SBCommandReturnObject::GetOutput () +{ + Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + if (m_opaque_ap.get()) + { + if (log) + log->Printf ("SBCommandReturnObject(%p)::GetOutput () => \"%s\"", m_opaque_ap.get(), + m_opaque_ap->GetOutputData()); + + return m_opaque_ap->GetOutputData(); + } + + if (log) + log->Printf ("SBCommandReturnObject(%p)::GetOutput () => NULL", m_opaque_ap.get()); + + return NULL; +} + +const char * +SBCommandReturnObject::GetError () +{ + Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + if (m_opaque_ap.get()) + { + if (log) + log->Printf ("SBCommandReturnObject(%p)::GetError () => \"%s\"", m_opaque_ap.get(), + m_opaque_ap->GetErrorData()); + + return m_opaque_ap->GetErrorData(); + } + + if (log) + log->Printf ("SBCommandReturnObject(%p)::GetError () => NULL", m_opaque_ap.get()); + + return NULL; +} + +size_t +SBCommandReturnObject::GetOutputSize () +{ + if (m_opaque_ap.get()) + return strlen (m_opaque_ap->GetOutputData()); + return 0; +} + +size_t +SBCommandReturnObject::GetErrorSize () +{ + if (m_opaque_ap.get()) + return strlen(m_opaque_ap->GetErrorData()); + return 0; +} + +size_t +SBCommandReturnObject::PutOutput (FILE *fh) +{ + if (fh) + { + size_t num_bytes = GetOutputSize (); + if (num_bytes) + return ::fprintf (fh, "%s", GetOutput()); + } + return 0; +} + +size_t +SBCommandReturnObject::PutError (FILE *fh) +{ + if (fh) + { + size_t num_bytes = GetErrorSize (); + if (num_bytes) + return ::fprintf (fh, "%s", GetError()); + } + return 0; +} + +void +SBCommandReturnObject::Clear() +{ + if (m_opaque_ap.get()) + m_opaque_ap->Clear(); +} + +lldb::ReturnStatus +SBCommandReturnObject::GetStatus() +{ + if (m_opaque_ap.get()) + return m_opaque_ap->GetStatus(); + return lldb::eReturnStatusInvalid; +} + +void +SBCommandReturnObject::SetStatus(lldb::ReturnStatus status) +{ + if (m_opaque_ap.get()) + m_opaque_ap->SetStatus(status); +} + +bool +SBCommandReturnObject::Succeeded () +{ + if (m_opaque_ap.get()) + return m_opaque_ap->Succeeded(); + return false; +} + +bool +SBCommandReturnObject::HasResult () +{ + if (m_opaque_ap.get()) + return m_opaque_ap->HasResult(); + return false; +} + +void +SBCommandReturnObject::AppendMessage (const char *message) +{ + if (m_opaque_ap.get()) + m_opaque_ap->AppendMessage (message); +} + +void +SBCommandReturnObject::AppendWarning (const char *message) +{ + if (m_opaque_ap.get()) + m_opaque_ap->AppendWarning (message); +} + +CommandReturnObject * +SBCommandReturnObject::operator ->() const +{ + return m_opaque_ap.get(); +} + +CommandReturnObject * +SBCommandReturnObject::get() const +{ + return m_opaque_ap.get(); +} + +CommandReturnObject & +SBCommandReturnObject::operator *() const +{ + assert(m_opaque_ap.get()); + return *(m_opaque_ap.get()); +} + + +CommandReturnObject & +SBCommandReturnObject::ref() const +{ + assert(m_opaque_ap.get()); + return *(m_opaque_ap.get()); +} + + +void +SBCommandReturnObject::SetLLDBObjectPtr (CommandReturnObject *ptr) +{ + if (m_opaque_ap.get()) + m_opaque_ap.reset (ptr); +} + +bool +SBCommandReturnObject::GetDescription (SBStream &description) +{ + Stream &strm = description.ref(); + + if (m_opaque_ap.get()) + { + description.Printf ("Status: "); + lldb::ReturnStatus status = m_opaque_ap->GetStatus(); + if (status == lldb::eReturnStatusStarted) + strm.PutCString ("Started"); + else if (status == lldb::eReturnStatusInvalid) + strm.PutCString ("Invalid"); + else if (m_opaque_ap->Succeeded()) + strm.PutCString ("Success"); + else + strm.PutCString ("Fail"); + + if (GetOutputSize() > 0) + strm.Printf ("\nOutput Message:\n%s", GetOutput()); + + if (GetErrorSize() > 0) + strm.Printf ("\nError Message:\n%s", GetError()); + } + else + strm.PutCString ("No value"); + + return true; +} + +void +SBCommandReturnObject::SetImmediateOutputFile (FILE *fh) +{ + if (m_opaque_ap.get()) + m_opaque_ap->SetImmediateOutputFile (fh); +} + +void +SBCommandReturnObject::SetImmediateErrorFile (FILE *fh) +{ + if (m_opaque_ap.get()) + m_opaque_ap->SetImmediateErrorFile (fh); +} + +void +SBCommandReturnObject::PutCString(const char* string, int len) +{ + if (m_opaque_ap.get()) + { + if (len == 0 || string == NULL || *string == 0) + { + return; + } + else if (len > 0) + { + std::string buffer(string, len); + m_opaque_ap->AppendMessage(buffer.c_str()); + } + else + m_opaque_ap->AppendMessage(string); + } +} + +const char * +SBCommandReturnObject::GetOutput (bool only_if_no_immediate) +{ + if (!m_opaque_ap.get()) + return NULL; + if (only_if_no_immediate == false || m_opaque_ap->GetImmediateOutputStream().get() == NULL) + return GetOutput(); + return NULL; +} + +const char * +SBCommandReturnObject::GetError (bool only_if_no_immediate) +{ + if (!m_opaque_ap.get()) + return NULL; + if (only_if_no_immediate == false || m_opaque_ap->GetImmediateErrorStream().get() == NULL) + return GetError(); + return NULL; +} + +size_t +SBCommandReturnObject::Printf(const char* format, ...) +{ + if (m_opaque_ap.get()) + { + va_list args; + va_start (args, format); + size_t result = m_opaque_ap->GetOutputStream().PrintfVarArg(format, args); + va_end (args); + return result; + } + return 0; +} + +void +SBCommandReturnObject::SetError (lldb::SBError &error, const char *fallback_error_cstr) +{ + if (m_opaque_ap.get()) + { + if (error.IsValid()) + m_opaque_ap->SetError(error.ref(), fallback_error_cstr); + else if (fallback_error_cstr) + m_opaque_ap->SetError(Error(), fallback_error_cstr); + } +} + +void +SBCommandReturnObject::SetError (const char *error_cstr) +{ + if (m_opaque_ap.get() && error_cstr) + m_opaque_ap->SetError(error_cstr); +} + diff --git a/contrib/llvm/tools/lldb/source/API/SBCommunication.cpp b/contrib/llvm/tools/lldb/source/API/SBCommunication.cpp new file mode 100644 index 00000000000..10feae5d4eb --- /dev/null +++ b/contrib/llvm/tools/lldb/source/API/SBCommunication.cpp @@ -0,0 +1,285 @@ +//===-- SBCommunication.cpp -------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/API/SBCommunication.h" +#include "lldb/API/SBBroadcaster.h" +#include "lldb/Core/Communication.h" +#include "lldb/Core/ConnectionFileDescriptor.h" +#include "lldb/Core/Log.h" + +using namespace lldb; +using namespace lldb_private; + + + +SBCommunication::SBCommunication() : + m_opaque (NULL), + m_opaque_owned (false) +{ +} + +SBCommunication::SBCommunication(const char * broadcaster_name) : + m_opaque (new Communication (broadcaster_name)), + m_opaque_owned (true) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + if (log) + log->Printf ("SBCommunication::SBCommunication (broadcaster_name=\"%s\") => " + "SBCommunication(%p)", broadcaster_name, m_opaque); +} + +SBCommunication::~SBCommunication() +{ + if (m_opaque && m_opaque_owned) + delete m_opaque; + m_opaque = NULL; + m_opaque_owned = false; +} + +bool +SBCommunication::IsValid () const +{ + return m_opaque != NULL; +} + +bool +SBCommunication::GetCloseOnEOF () +{ + if (m_opaque) + return m_opaque->GetCloseOnEOF (); + return false; +} + +void +SBCommunication::SetCloseOnEOF (bool b) +{ + if (m_opaque) + m_opaque->SetCloseOnEOF (b); +} + +ConnectionStatus +SBCommunication::Connect (const char *url) +{ + if (m_opaque) + { + if (!m_opaque->HasConnection ()) + m_opaque->SetConnection (new ConnectionFileDescriptor()); + return m_opaque->Connect (url, NULL); + } + return eConnectionStatusNoConnection; +} + +ConnectionStatus +SBCommunication::AdoptFileDesriptor (int fd, bool owns_fd) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + ConnectionStatus status = eConnectionStatusNoConnection; + if (m_opaque) + { + if (m_opaque->HasConnection ()) + { + if (m_opaque->IsConnected()) + m_opaque->Disconnect(); + } + m_opaque->SetConnection (new ConnectionFileDescriptor (fd, owns_fd)); + if (m_opaque->IsConnected()) + status = eConnectionStatusSuccess; + else + status = eConnectionStatusLostConnection; + } + + if (log) + log->Printf ("SBCommunication(%p)::AdoptFileDescriptor (fd=%d, ownd_fd=%i) => %s", + m_opaque, fd, owns_fd, Communication::ConnectionStatusAsCString (status)); + + return status; +} + + +ConnectionStatus +SBCommunication::Disconnect () +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + ConnectionStatus status= eConnectionStatusNoConnection; + if (m_opaque) + status = m_opaque->Disconnect (); + + if (log) + log->Printf ("SBCommunication(%p)::Disconnect () => %s", m_opaque, + Communication::ConnectionStatusAsCString (status)); + + return status; +} + +bool +SBCommunication::IsConnected () const +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + bool result = false; + if (m_opaque) + result = m_opaque->IsConnected (); + + if (log) + log->Printf ("SBCommunication(%p)::IsConnected () => %i", m_opaque, result); + + return false; +} + +size_t +SBCommunication::Read (void *dst, size_t dst_len, uint32_t timeout_usec, ConnectionStatus &status) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (log) + log->Printf ("SBCommunication(%p)::Read (dst=%p, dst_len=%" PRIu64 ", timeout_usec=%u, &status)...", + m_opaque, + dst, + (uint64_t)dst_len, + timeout_usec); + size_t bytes_read = 0; + if (m_opaque) + bytes_read = m_opaque->Read (dst, dst_len, timeout_usec, status, NULL); + else + status = eConnectionStatusNoConnection; + + if (log) + log->Printf ("SBCommunication(%p)::Read (dst=%p, dst_len=%" PRIu64 ", timeout_usec=%u, &status=%s) => %" PRIu64, + m_opaque, + dst, + (uint64_t)dst_len, + timeout_usec, + Communication::ConnectionStatusAsCString (status), + (uint64_t)bytes_read); + return bytes_read; +} + + +size_t +SBCommunication::Write (const void *src, size_t src_len, ConnectionStatus &status) +{ + size_t bytes_written = 0; + if (m_opaque) + bytes_written = m_opaque->Write (src, src_len, status, NULL); + else + status = eConnectionStatusNoConnection; + + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (log) + log->Printf ("SBCommunication(%p)::Write (src=%p, src_len=%" PRIu64 ", &status=%s) => %" PRIu64, + m_opaque, src, (uint64_t)src_len, Communication::ConnectionStatusAsCString (status), (uint64_t)bytes_written); + + return 0; +} + +bool +SBCommunication::ReadThreadStart () +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + bool success = false; + if (m_opaque) + success = m_opaque->StartReadThread (); + + if (log) + log->Printf ("SBCommunication(%p)::ReadThreadStart () => %i", m_opaque, success); + + return success; +} + + +bool +SBCommunication::ReadThreadStop () +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (log) + log->Printf ("SBCommunication(%p)::ReadThreadStop ()...", m_opaque); + + bool success = false; + if (m_opaque) + success = m_opaque->StopReadThread (); + + if (log) + log->Printf ("SBCommunication(%p)::ReadThreadStop () => %i", m_opaque, success); + + return success; +} + +bool +SBCommunication::ReadThreadIsRunning () +{ + bool result = false; + if (m_opaque) + result = m_opaque->ReadThreadIsRunning (); + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (log) + log->Printf ("SBCommunication(%p)::ReadThreadIsRunning () => %i", m_opaque, result); + return result; +} + +bool +SBCommunication::SetReadThreadBytesReceivedCallback +( + ReadThreadBytesReceived callback, + void *callback_baton +) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + bool result = false; + if (m_opaque) + { + m_opaque->SetReadThreadBytesReceivedCallback (callback, callback_baton); + result = true; + } + + if (log) + log->Printf ("SBCommunication(%p)::SetReadThreadBytesReceivedCallback (callback=%p, baton=%p) => %i", + m_opaque, callback, callback_baton, result); + + return result; +} + +SBBroadcaster +SBCommunication::GetBroadcaster () +{ + SBBroadcaster broadcaster (m_opaque, false); + + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + if (log) + log->Printf ("SBCommunication(%p)::GetBroadcaster () => SBBroadcaster (%p)", + m_opaque, broadcaster.get()); + + return broadcaster; +} + +const char * +SBCommunication::GetBroadcasterClass () +{ + return Communication::GetStaticBroadcasterClass().AsCString(); +} + +// +//void +//SBCommunication::CreateIfNeeded () +//{ +// if (m_opaque == NULL) +// { +// static uint32_t g_broadcaster_num; +// char broadcaster_name[256]; +// ::snprintf (name, broadcaster_name, "%p SBCommunication", this); +// m_opaque = new Communication (broadcaster_name); +// m_opaque_owned = true; +// } +// assert (m_opaque); +//} +// +// diff --git a/contrib/llvm/tools/lldb/source/API/SBCompileUnit.cpp b/contrib/llvm/tools/lldb/source/API/SBCompileUnit.cpp new file mode 100644 index 00000000000..9f7487746a8 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/API/SBCompileUnit.cpp @@ -0,0 +1,278 @@ +//===-- SBCompileUnit.cpp ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/API/SBCompileUnit.h" +#include "lldb/API/SBLineEntry.h" +#include "lldb/API/SBStream.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Module.h" +#include "lldb/Symbol/CompileUnit.h" +#include "lldb/Symbol/LineEntry.h" +#include "lldb/Symbol/LineTable.h" +#include "lldb/Symbol/SymbolVendor.h" +#include "lldb/Symbol/Type.h" + +using namespace lldb; +using namespace lldb_private; + + +SBCompileUnit::SBCompileUnit () : + m_opaque_ptr (NULL) +{ +} + +SBCompileUnit::SBCompileUnit (lldb_private::CompileUnit *lldb_object_ptr) : + m_opaque_ptr (lldb_object_ptr) +{ +} + +SBCompileUnit::SBCompileUnit(const SBCompileUnit &rhs) : + m_opaque_ptr (rhs.m_opaque_ptr) +{ +} + +const SBCompileUnit & +SBCompileUnit::operator = (const SBCompileUnit &rhs) +{ + m_opaque_ptr = rhs.m_opaque_ptr; + return *this; +} + + +SBCompileUnit::~SBCompileUnit () +{ + m_opaque_ptr = NULL; +} + +SBFileSpec +SBCompileUnit::GetFileSpec () const +{ + SBFileSpec file_spec; + if (m_opaque_ptr) + file_spec.SetFileSpec(*m_opaque_ptr); + return file_spec; +} + +uint32_t +SBCompileUnit::GetNumLineEntries () const +{ + if (m_opaque_ptr) + { + LineTable *line_table = m_opaque_ptr->GetLineTable (); + if (line_table) + return line_table->GetSize(); + } + return 0; +} + +SBLineEntry +SBCompileUnit::GetLineEntryAtIndex (uint32_t idx) const +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + SBLineEntry sb_line_entry; + if (m_opaque_ptr) + { + LineTable *line_table = m_opaque_ptr->GetLineTable (); + if (line_table) + { + LineEntry line_entry; + if (line_table->GetLineEntryAtIndex(idx, line_entry)) + sb_line_entry.SetLineEntry(line_entry); + } + } + + if (log) + { + SBStream sstr; + sb_line_entry.GetDescription (sstr); + log->Printf ("SBCompileUnit(%p)::GetLineEntryAtIndex (idx=%u) => SBLineEntry(%p): '%s'", + m_opaque_ptr, idx, sb_line_entry.get(), sstr.GetData()); + } + + return sb_line_entry; +} + +uint32_t +SBCompileUnit::FindLineEntryIndex (uint32_t start_idx, uint32_t line, SBFileSpec *inline_file_spec) const +{ + const bool exact = true; + return FindLineEntryIndex (start_idx, line, inline_file_spec, exact); +} + +uint32_t +SBCompileUnit::FindLineEntryIndex (uint32_t start_idx, uint32_t line, SBFileSpec *inline_file_spec, bool exact) const +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + uint32_t index = UINT32_MAX; + if (m_opaque_ptr) + { + FileSpec file_spec; + if (inline_file_spec && inline_file_spec->IsValid()) + file_spec = inline_file_spec->ref(); + else + file_spec = *m_opaque_ptr; + + + index = m_opaque_ptr->FindLineEntry (start_idx, + line, + inline_file_spec ? inline_file_spec->get() : NULL, + exact, + NULL); + } + + if (log) + { + SBStream sstr; + if (index == UINT32_MAX) + { + log->Printf ("SBCompileUnit(%p)::FindLineEntryIndex (start_idx=%u, line=%u, SBFileSpec(%p)) => NOT FOUND", + m_opaque_ptr, start_idx, line, inline_file_spec ? inline_file_spec->get() : NULL); + } + else + { + log->Printf ("SBCompileUnit(%p)::FindLineEntryIndex (start_idx=%u, line=%u, SBFileSpec(%p)) => %u", + m_opaque_ptr, start_idx, line, inline_file_spec ? inline_file_spec->get() : NULL, index); + } + } + + return index; +} + +uint32_t +SBCompileUnit::GetNumSupportFiles () const +{ + if (m_opaque_ptr) + { + FileSpecList& support_files = m_opaque_ptr->GetSupportFiles (); + return support_files.GetSize(); + } + return 0; +} + + + +lldb::SBTypeList +SBCompileUnit::GetTypes (uint32_t type_mask) +{ + SBTypeList sb_type_list; + + if (m_opaque_ptr) + { + ModuleSP module_sp (m_opaque_ptr->GetModule()); + if (module_sp) + { + SymbolVendor* vendor = module_sp->GetSymbolVendor(); + if (vendor) + { + TypeList type_list; + vendor->GetTypes (m_opaque_ptr, type_mask, type_list); + sb_type_list.m_opaque_ap->Append(type_list); + } + } + } + return sb_type_list; +} + + + + +SBFileSpec +SBCompileUnit::GetSupportFileAtIndex (uint32_t idx) const +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + SBFileSpec sb_file_spec; + if (m_opaque_ptr) + { + FileSpecList &support_files = m_opaque_ptr->GetSupportFiles (); + FileSpec file_spec = support_files.GetFileSpecAtIndex(idx); + sb_file_spec.SetFileSpec(file_spec); + } + + if (log) + { + SBStream sstr; + sb_file_spec.GetDescription (sstr); + log->Printf ("SBCompileUnit(%p)::GetGetFileSpecAtIndex (idx=%u) => SBFileSpec(%p): '%s'", + m_opaque_ptr, idx, sb_file_spec.get(), sstr.GetData()); + } + + return sb_file_spec; +} + +uint32_t +SBCompileUnit::FindSupportFileIndex (uint32_t start_idx, const SBFileSpec &sb_file, bool full) +{ + if (m_opaque_ptr) + { + FileSpecList &support_files = m_opaque_ptr->GetSupportFiles (); + return support_files.FindFileIndex(start_idx, sb_file.ref(), full); + } + return 0; +} + +bool +SBCompileUnit::IsValid () const +{ + return m_opaque_ptr != NULL; +} + +bool +SBCompileUnit::operator == (const SBCompileUnit &rhs) const +{ + return m_opaque_ptr == rhs.m_opaque_ptr; +} + +bool +SBCompileUnit::operator != (const SBCompileUnit &rhs) const +{ + return m_opaque_ptr != rhs.m_opaque_ptr; +} + +const lldb_private::CompileUnit * +SBCompileUnit::operator->() const +{ + return m_opaque_ptr; +} + +const lldb_private::CompileUnit & +SBCompileUnit::operator*() const +{ + return *m_opaque_ptr; +} + +lldb_private::CompileUnit * +SBCompileUnit::get () +{ + return m_opaque_ptr; +} + +void +SBCompileUnit::reset (lldb_private::CompileUnit *lldb_object_ptr) +{ + m_opaque_ptr = lldb_object_ptr; +} + + +bool +SBCompileUnit::GetDescription (SBStream &description) +{ + Stream &strm = description.ref(); + + if (m_opaque_ptr) + { + m_opaque_ptr->Dump (&strm, false); + } + else + strm.PutCString ("No value"); + + return true; +} diff --git a/contrib/llvm/tools/lldb/source/API/SBData.cpp b/contrib/llvm/tools/lldb/source/API/SBData.cpp new file mode 100644 index 00000000000..5b2f075158b --- /dev/null +++ b/contrib/llvm/tools/lldb/source/API/SBData.cpp @@ -0,0 +1,785 @@ +//===-- SBData.cpp ----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/API/SBData.h" +#include "lldb/API/SBError.h" +#include "lldb/API/SBStream.h" + +#include "lldb/Core/DataBufferHeap.h" +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Stream.h" + + +using namespace lldb; +using namespace lldb_private; + +SBData::SBData () : + m_opaque_sp(new DataExtractor()) +{ +} + +SBData::SBData (const lldb::DataExtractorSP& data_sp) : + m_opaque_sp (data_sp) +{ +} + +SBData::SBData(const SBData &rhs) : + m_opaque_sp (rhs.m_opaque_sp) +{ +} + +const SBData & +SBData::operator = (const SBData &rhs) +{ + if (this != &rhs) + m_opaque_sp = rhs.m_opaque_sp; + return *this; +} + +SBData::~SBData () +{ +} + +void +SBData::SetOpaque (const lldb::DataExtractorSP &data_sp) +{ + m_opaque_sp = data_sp; +} + +lldb_private::DataExtractor * +SBData::get() const +{ + return m_opaque_sp.get(); +} + +lldb_private::DataExtractor * +SBData::operator->() const +{ + return m_opaque_sp.operator->(); +} + +lldb::DataExtractorSP & +SBData::operator*() +{ + return m_opaque_sp; +} + +const lldb::DataExtractorSP & +SBData::operator*() const +{ + return m_opaque_sp; +} + +bool +SBData::IsValid() +{ + return m_opaque_sp.get() != NULL; +} + +uint8_t +SBData::GetAddressByteSize () +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + uint8_t value = 0; + if (m_opaque_sp.get()) + value = m_opaque_sp->GetAddressByteSize(); + if (log) + log->Printf ("SBData::GetAddressByteSize () => " + "(%i)", value); + return value; +} + +void +SBData::SetAddressByteSize (uint8_t addr_byte_size) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (m_opaque_sp.get()) + m_opaque_sp->SetAddressByteSize(addr_byte_size); + if (log) + log->Printf ("SBData::SetAddressByteSize (%i)", addr_byte_size); +} + +void +SBData::Clear () +{ + if (m_opaque_sp.get()) + m_opaque_sp->Clear(); +} + +size_t +SBData::GetByteSize () +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + size_t value = 0; + if (m_opaque_sp.get()) + value = m_opaque_sp->GetByteSize(); + if (log) + log->Printf ("SBData::GetByteSize () => " + "(%lu)", value); + return value; +} + +lldb::ByteOrder +SBData::GetByteOrder () +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + lldb::ByteOrder value = eByteOrderInvalid; + if (m_opaque_sp.get()) + value = m_opaque_sp->GetByteOrder(); + if (log) + log->Printf ("SBData::GetByteOrder () => " + "(%i)", value); + return value; +} + +void +SBData::SetByteOrder (lldb::ByteOrder endian) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (m_opaque_sp.get()) + m_opaque_sp->SetByteOrder(endian); + if (log) + log->Printf ("SBData::GetByteOrder (%i)", endian); +} + + +float +SBData::GetFloat (lldb::SBError& error, lldb::offset_t offset) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + float value = 0; + if (!m_opaque_sp.get()) + { + error.SetErrorString("no value to read from"); + } + else + { + uint32_t old_offset = offset; + value = m_opaque_sp->GetFloat(&offset); + if (offset == old_offset) + error.SetErrorString("unable to read data"); + } + if (log) + log->Printf ("SBData::GetFloat (error=%p,offset=%" PRIu64 ") => " + "(%f)", error.get(), offset, value); + return value; +} + +double +SBData::GetDouble (lldb::SBError& error, lldb::offset_t offset) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + double value = 0; + if (!m_opaque_sp.get()) + { + error.SetErrorString("no value to read from"); + } + else + { + uint32_t old_offset = offset; + value = m_opaque_sp->GetDouble(&offset); + if (offset == old_offset) + error.SetErrorString("unable to read data"); + } + if (log) + log->Printf ("SBData::GetDouble (error=%p,offset=%" PRIu64 ") => " + "(%f)", error.get(), offset, value); + return value; +} + +long double +SBData::GetLongDouble (lldb::SBError& error, lldb::offset_t offset) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + long double value = 0; + if (!m_opaque_sp.get()) + { + error.SetErrorString("no value to read from"); + } + else + { + uint32_t old_offset = offset; + value = m_opaque_sp->GetLongDouble(&offset); + if (offset == old_offset) + error.SetErrorString("unable to read data"); + } + if (log) + log->Printf ("SBData::GetLongDouble (error=%p,offset=%" PRIu64 ") => " + "(%Lf)", error.get(), offset, value); + return value; +} + +lldb::addr_t +SBData::GetAddress (lldb::SBError& error, lldb::offset_t offset) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + lldb::addr_t value = 0; + if (!m_opaque_sp.get()) + { + error.SetErrorString("no value to read from"); + } + else + { + uint32_t old_offset = offset; + value = m_opaque_sp->GetAddress(&offset); + if (offset == old_offset) + error.SetErrorString("unable to read data"); + } + if (log) + log->Printf ("SBData::GetAddress (error=%p,offset=%" PRIu64 ") => " + "(%p)", error.get(), offset, (void*)value); + return value; +} + +uint8_t +SBData::GetUnsignedInt8 (lldb::SBError& error, lldb::offset_t offset) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + uint8_t value = 0; + if (!m_opaque_sp.get()) + { + error.SetErrorString("no value to read from"); + } + else + { + uint32_t old_offset = offset; + value = m_opaque_sp->GetU8(&offset); + if (offset == old_offset) + error.SetErrorString("unable to read data"); + } + if (log) + log->Printf ("SBData::GetUnsignedInt8 (error=%p,offset=%" PRIu64 ") => " + "(%c)", error.get(), offset, value); + return value; +} + +uint16_t +SBData::GetUnsignedInt16 (lldb::SBError& error, lldb::offset_t offset) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + uint16_t value = 0; + if (!m_opaque_sp.get()) + { + error.SetErrorString("no value to read from"); + } + else + { + uint32_t old_offset = offset; + value = m_opaque_sp->GetU16(&offset); + if (offset == old_offset) + error.SetErrorString("unable to read data"); + } + if (log) + log->Printf ("SBData::GetUnsignedInt16 (error=%p,offset=%" PRIu64 ") => " + "(%hd)", error.get(), offset, value); + return value; +} + +uint32_t +SBData::GetUnsignedInt32 (lldb::SBError& error, lldb::offset_t offset) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + uint32_t value = 0; + if (!m_opaque_sp.get()) + { + error.SetErrorString("no value to read from"); + } + else + { + uint32_t old_offset = offset; + value = m_opaque_sp->GetU32(&offset); + if (offset == old_offset) + error.SetErrorString("unable to read data"); + } + if (log) + log->Printf ("SBData::GetUnsignedInt32 (error=%p,offset=%" PRIu64 ") => " + "(%d)", error.get(), offset, value); + return value; +} + +uint64_t +SBData::GetUnsignedInt64 (lldb::SBError& error, lldb::offset_t offset) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + uint64_t value = 0; + if (!m_opaque_sp.get()) + { + error.SetErrorString("no value to read from"); + } + else + { + uint32_t old_offset = offset; + value = m_opaque_sp->GetU64(&offset); + if (offset == old_offset) + error.SetErrorString("unable to read data"); + } + if (log) + log->Printf ("SBData::GetUnsignedInt64 (error=%p,offset=%" PRIu64 ") => " + "(%" PRId64 ")", error.get(), offset, value); + return value; +} + +int8_t +SBData::GetSignedInt8 (lldb::SBError& error, lldb::offset_t offset) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + int8_t value = 0; + if (!m_opaque_sp.get()) + { + error.SetErrorString("no value to read from"); + } + else + { + uint32_t old_offset = offset; + value = (int8_t)m_opaque_sp->GetMaxS64(&offset, 1); + if (offset == old_offset) + error.SetErrorString("unable to read data"); + } + if (log) + log->Printf ("SBData::GetSignedInt8 (error=%p,offset=%" PRIu64 ") => " + "(%c)", error.get(), offset, value); + return value; +} + +int16_t +SBData::GetSignedInt16 (lldb::SBError& error, lldb::offset_t offset) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + int16_t value = 0; + if (!m_opaque_sp.get()) + { + error.SetErrorString("no value to read from"); + } + else + { + uint32_t old_offset = offset; + value = (int16_t)m_opaque_sp->GetMaxS64(&offset, 2); + if (offset == old_offset) + error.SetErrorString("unable to read data"); + } + if (log) + log->Printf ("SBData::GetSignedInt16 (error=%p,offset=%" PRIu64 ") => " + "(%hd)", error.get(), offset, value); + return value; +} + +int32_t +SBData::GetSignedInt32 (lldb::SBError& error, lldb::offset_t offset) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + int32_t value = 0; + if (!m_opaque_sp.get()) + { + error.SetErrorString("no value to read from"); + } + else + { + uint32_t old_offset = offset; + value = (int32_t)m_opaque_sp->GetMaxS64(&offset, 4); + if (offset == old_offset) + error.SetErrorString("unable to read data"); + } + if (log) + log->Printf ("SBData::GetSignedInt32 (error=%p,offset=%" PRIu64 ") => " + "(%d)", error.get(), offset, value); + return value; +} + +int64_t +SBData::GetSignedInt64 (lldb::SBError& error, lldb::offset_t offset) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + int64_t value = 0; + if (!m_opaque_sp.get()) + { + error.SetErrorString("no value to read from"); + } + else + { + uint32_t old_offset = offset; + value = (int64_t)m_opaque_sp->GetMaxS64(&offset, 8); + if (offset == old_offset) + error.SetErrorString("unable to read data"); + } + if (log) + log->Printf ("SBData::GetSignedInt64 (error=%p,offset=%" PRIu64 ") => " + "(%" PRId64 ")", error.get(), offset, value); + return value; +} + +const char* +SBData::GetString (lldb::SBError& error, lldb::offset_t offset) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + const char* value = 0; + if (!m_opaque_sp.get()) + { + error.SetErrorString("no value to read from"); + } + else + { + uint32_t old_offset = offset; + value = m_opaque_sp->GetCStr(&offset); + if (offset == old_offset || (value == NULL)) + error.SetErrorString("unable to read data"); + } + if (log) + log->Printf ("SBData::GetString (error=%p,offset=%" PRIu64 ") => " + "(%p)", error.get(), offset, value); + return value; +} + +bool +SBData::GetDescription (lldb::SBStream &description, lldb::addr_t base_addr) +{ + Stream &strm = description.ref(); + + if (m_opaque_sp) + { + m_opaque_sp->Dump (&strm, + 0, + lldb::eFormatBytesWithASCII, + 1, + m_opaque_sp->GetByteSize(), + 16, + base_addr, + 0, + 0); + } + else + strm.PutCString ("No value"); + + return true; +} + +size_t +SBData::ReadRawData (lldb::SBError& error, + lldb::offset_t offset, + void *buf, + size_t size) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + void* ok = NULL; + if (!m_opaque_sp.get()) + { + error.SetErrorString("no value to read from"); + } + else + { + uint32_t old_offset = offset; + ok = m_opaque_sp->GetU8(&offset, buf, size); + if ((offset == old_offset) || (ok == NULL)) + error.SetErrorString("unable to read data"); + } + if (log) + log->Printf ("SBData::ReadRawData (error=%p,offset=%" PRIu64 ",buf=%p,size=%lu) => " + "(%p)", error.get(), offset, buf, size, ok); + return ok ? size : 0; +} + +void +SBData::SetData (lldb::SBError& error, + const void *buf, + size_t size, + lldb::ByteOrder endian, + uint8_t addr_size) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (!m_opaque_sp.get()) + m_opaque_sp.reset(new DataExtractor(buf, size, endian, addr_size)); + else + m_opaque_sp->SetData(buf, size, endian); + if (log) + log->Printf ("SBData::SetData (error=%p,buf=%p,size=%lu,endian=%d,addr_size=%c) => " + "(%p)", error.get(), buf, size, endian, addr_size, m_opaque_sp.get()); +} + +bool +SBData::Append (const SBData& rhs) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + bool value = false; + if (m_opaque_sp.get() && rhs.m_opaque_sp.get()) + value = m_opaque_sp.get()->Append(*rhs.m_opaque_sp); + if (log) + log->Printf ("SBData::Append (rhs=%p) => " + "(%s)", rhs.get(), value ? "true" : "false"); + return value; +} + +lldb::SBData +SBData::CreateDataFromCString (lldb::ByteOrder endian, uint32_t addr_byte_size, const char* data) +{ + if (!data || !data[0]) + return SBData(); + + uint32_t data_len = strlen(data); + + lldb::DataBufferSP buffer_sp(new DataBufferHeap(data, data_len)); + lldb::DataExtractorSP data_sp(new DataExtractor(buffer_sp, endian, addr_byte_size)); + + SBData ret(data_sp); + + return ret; +} + +lldb::SBData +SBData::CreateDataFromUInt64Array (lldb::ByteOrder endian, uint32_t addr_byte_size, uint64_t* array, size_t array_len) +{ + if (!array || array_len == 0) + return SBData(); + + size_t data_len = array_len * sizeof(uint64_t); + + lldb::DataBufferSP buffer_sp(new DataBufferHeap(array, data_len)); + lldb::DataExtractorSP data_sp(new DataExtractor(buffer_sp, endian, addr_byte_size)); + + SBData ret(data_sp); + + return ret; +} + +lldb::SBData +SBData::CreateDataFromUInt32Array (lldb::ByteOrder endian, uint32_t addr_byte_size, uint32_t* array, size_t array_len) +{ + if (!array || array_len == 0) + return SBData(); + + size_t data_len = array_len * sizeof(uint32_t); + + lldb::DataBufferSP buffer_sp(new DataBufferHeap(array, data_len)); + lldb::DataExtractorSP data_sp(new DataExtractor(buffer_sp, endian, addr_byte_size)); + + SBData ret(data_sp); + + return ret; +} + +lldb::SBData +SBData::CreateDataFromSInt64Array (lldb::ByteOrder endian, uint32_t addr_byte_size, int64_t* array, size_t array_len) +{ + if (!array || array_len == 0) + return SBData(); + + size_t data_len = array_len * sizeof(int64_t); + + lldb::DataBufferSP buffer_sp(new DataBufferHeap(array, data_len)); + lldb::DataExtractorSP data_sp(new DataExtractor(buffer_sp, endian, addr_byte_size)); + + SBData ret(data_sp); + + return ret; +} + +lldb::SBData +SBData::CreateDataFromSInt32Array (lldb::ByteOrder endian, uint32_t addr_byte_size, int32_t* array, size_t array_len) +{ + if (!array || array_len == 0) + return SBData(); + + size_t data_len = array_len * sizeof(int32_t); + + lldb::DataBufferSP buffer_sp(new DataBufferHeap(array, data_len)); + lldb::DataExtractorSP data_sp(new DataExtractor(buffer_sp, endian, addr_byte_size)); + + SBData ret(data_sp); + + return ret; +} + +lldb::SBData +SBData::CreateDataFromDoubleArray (lldb::ByteOrder endian, uint32_t addr_byte_size, double* array, size_t array_len) +{ + if (!array || array_len == 0) + return SBData(); + + size_t data_len = array_len * sizeof(double); + + lldb::DataBufferSP buffer_sp(new DataBufferHeap(array, data_len)); + lldb::DataExtractorSP data_sp(new DataExtractor(buffer_sp, endian, addr_byte_size)); + + SBData ret(data_sp); + + return ret; +} + +bool +SBData::SetDataFromCString (const char* data) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + if (!data) + { + if (log) + log->Printf ("SBData::SetDataFromCString (data=%p) => " + "false", data); + return false; + } + + size_t data_len = strlen(data); + + lldb::DataBufferSP buffer_sp(new DataBufferHeap(data, data_len)); + + if (!m_opaque_sp.get()) + m_opaque_sp.reset(new DataExtractor(buffer_sp, GetByteOrder(), GetAddressByteSize())); + else + m_opaque_sp->SetData(buffer_sp); + + if (log) + log->Printf ("SBData::SetDataFromCString (data=%p) => " + "true", data); + + return true; +} + +bool +SBData::SetDataFromUInt64Array (uint64_t* array, size_t array_len) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + if (!array || array_len == 0) + { + if (log) + log->Printf ("SBData::SetDataFromUInt64Array (array=%p, array_len = %lu) => " + "false", array, array_len); + return false; + } + + size_t data_len = array_len * sizeof(uint64_t); + + lldb::DataBufferSP buffer_sp(new DataBufferHeap(array, data_len)); + + if (!m_opaque_sp.get()) + m_opaque_sp.reset(new DataExtractor(buffer_sp, GetByteOrder(), GetAddressByteSize())); + else + m_opaque_sp->SetData(buffer_sp); + + if (log) + log->Printf ("SBData::SetDataFromUInt64Array (array=%p, array_len = %lu) => " + "true", array, array_len); + + return true; +} + +bool +SBData::SetDataFromUInt32Array (uint32_t* array, size_t array_len) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + if (!array || array_len == 0) + { + if (log) + log->Printf ("SBData::SetDataFromUInt32Array (array=%p, array_len = %lu) => " + "false", array, array_len); + return false; + } + + size_t data_len = array_len * sizeof(uint32_t); + + lldb::DataBufferSP buffer_sp(new DataBufferHeap(array, data_len)); + + if (!m_opaque_sp.get()) + m_opaque_sp.reset(new DataExtractor(buffer_sp, GetByteOrder(), GetAddressByteSize())); + else + m_opaque_sp->SetData(buffer_sp); + + if (log) + log->Printf ("SBData::SetDataFromUInt32Array (array=%p, array_len = %lu) => " + "true", array, array_len); + + return true; +} + +bool +SBData::SetDataFromSInt64Array (int64_t* array, size_t array_len) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + if (!array || array_len == 0) + { + if (log) + log->Printf ("SBData::SetDataFromSInt64Array (array=%p, array_len = %lu) => " + "false", array, array_len); + return false; + } + + size_t data_len = array_len * sizeof(int64_t); + + lldb::DataBufferSP buffer_sp(new DataBufferHeap(array, data_len)); + + if (!m_opaque_sp.get()) + m_opaque_sp.reset(new DataExtractor(buffer_sp, GetByteOrder(), GetAddressByteSize())); + else + m_opaque_sp->SetData(buffer_sp); + + if (log) + log->Printf ("SBData::SetDataFromSInt64Array (array=%p, array_len = %lu) => " + "true", array, array_len); + + return true; +} + +bool +SBData::SetDataFromSInt32Array (int32_t* array, size_t array_len) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + if (!array || array_len == 0) + { + if (log) + log->Printf ("SBData::SetDataFromSInt32Array (array=%p, array_len = %lu) => " + "false", array, array_len); + return false; + } + + size_t data_len = array_len * sizeof(int32_t); + + lldb::DataBufferSP buffer_sp(new DataBufferHeap(array, data_len)); + + if (!m_opaque_sp.get()) + m_opaque_sp.reset(new DataExtractor(buffer_sp, GetByteOrder(), GetAddressByteSize())); + else + m_opaque_sp->SetData(buffer_sp); + + if (log) + log->Printf ("SBData::SetDataFromSInt32Array (array=%p, array_len = %lu) => " + "true", array, array_len); + + return true; +} + +bool +SBData::SetDataFromDoubleArray (double* array, size_t array_len) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + if (!array || array_len == 0) + { + if (log) + log->Printf ("SBData::SetDataFromDoubleArray (array=%p, array_len = %lu) => " + "false", array, array_len); + return false; + } + + size_t data_len = array_len * sizeof(double); + + lldb::DataBufferSP buffer_sp(new DataBufferHeap(array, data_len)); + + if (!m_opaque_sp.get()) + m_opaque_sp.reset(new DataExtractor(buffer_sp, GetByteOrder(), GetAddressByteSize())); + else + m_opaque_sp->SetData(buffer_sp); + + if (log) + log->Printf ("SBData::SetDataFromDoubleArray (array=%p, array_len = %lu) => " + "true", array, array_len); + + return true; +} diff --git a/contrib/llvm/tools/lldb/source/API/SBDebugger.cpp b/contrib/llvm/tools/lldb/source/API/SBDebugger.cpp new file mode 100644 index 00000000000..f5e71d5f1a0 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/API/SBDebugger.cpp @@ -0,0 +1,1264 @@ +//===-- SBDebugger.cpp ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "lldb/API/SBDebugger.h" + +#include "lldb/lldb-private.h" + +#include "lldb/API/SBListener.h" +#include "lldb/API/SBBroadcaster.h" +#include "lldb/API/SBCommandInterpreter.h" +#include "lldb/API/SBCommandReturnObject.h" +#include "lldb/API/SBError.h" +#include "lldb/API/SBEvent.h" +#include "lldb/API/SBFrame.h" +#include "lldb/API/SBInputReader.h" +#include "lldb/API/SBProcess.h" +#include "lldb/API/SBSourceManager.h" +#include "lldb/API/SBStream.h" +#include "lldb/API/SBStringList.h" +#include "lldb/API/SBTarget.h" +#include "lldb/API/SBThread.h" +#include "lldb/API/SBTypeCategory.h" +#include "lldb/API/SBTypeFormat.h" +#include "lldb/API/SBTypeFilter.h" +#include "lldb/API/SBTypeNameSpecifier.h" +#include "lldb/API/SBTypeSummary.h" +#include "lldb/API/SBTypeSynthetic.h" + + +#include "lldb/Core/Debugger.h" +#include "lldb/Core/State.h" +#include "lldb/DataFormatters/DataVisualization.h" +#include "lldb/Interpreter/Args.h" +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Interpreter/OptionGroupPlatform.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/TargetList.h" + +using namespace lldb; +using namespace lldb_private; + +void +SBDebugger::Initialize () +{ + Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + if (log) + log->Printf ("SBDebugger::Initialize ()"); + + SBCommandInterpreter::InitializeSWIG (); + + Debugger::Initialize(); +} + +void +SBDebugger::Terminate () +{ + Debugger::Terminate(); +} + +void +SBDebugger::Clear () +{ + Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + if (log) + log->Printf ("SBDebugger(%p)::Clear ()", m_opaque_sp.get()); + + if (m_opaque_sp) + m_opaque_sp->CleanUpInputReaders (); + + m_opaque_sp.reset(); +} + +SBDebugger +SBDebugger::Create() +{ + return SBDebugger::Create(false, NULL, NULL); +} + +SBDebugger +SBDebugger::Create(bool source_init_files) +{ + return SBDebugger::Create (source_init_files, NULL, NULL); +} + +SBDebugger +SBDebugger::Create(bool source_init_files, lldb::LogOutputCallback callback, void *baton) + +{ + Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + SBDebugger debugger; + debugger.reset(Debugger::CreateInstance(callback, baton)); + + if (log) + { + SBStream sstr; + debugger.GetDescription (sstr); + log->Printf ("SBDebugger::Create () => SBDebugger(%p): %s", debugger.m_opaque_sp.get(), sstr.GetData()); + } + + SBCommandInterpreter interp = debugger.GetCommandInterpreter(); + if (source_init_files) + { + interp.get()->SkipLLDBInitFiles(false); + interp.get()->SkipAppInitFiles (false); + SBCommandReturnObject result; + interp.SourceInitFileInHomeDirectory(result); + } + else + { + interp.get()->SkipLLDBInitFiles(true); + interp.get()->SkipAppInitFiles (true); + } + return debugger; +} + +void +SBDebugger::Destroy (SBDebugger &debugger) +{ + Log *log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + if (log) + { + SBStream sstr; + debugger.GetDescription (sstr); + log->Printf ("SBDebugger::Destroy () => SBDebugger(%p): %s", debugger.m_opaque_sp.get(), sstr.GetData()); + } + + Debugger::Destroy (debugger.m_opaque_sp); + + if (debugger.m_opaque_sp.get() != NULL) + debugger.m_opaque_sp.reset(); +} + +void +SBDebugger::MemoryPressureDetected () +{ + // Since this function can be call asynchronously, we allow it to be + // non-mandatory. We have seen deadlocks with this function when called + // so we need to safeguard against this until we can determine what is + // causing the deadlocks. + Log *log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + const bool mandatory = false; + if (log) + { + log->Printf ("SBDebugger::MemoryPressureDetected (), mandatory = %d", mandatory); + } + + ModuleList::RemoveOrphanSharedModules(mandatory); +} + +SBDebugger::SBDebugger () : + m_opaque_sp () +{ +} + +SBDebugger::SBDebugger(const lldb::DebuggerSP &debugger_sp) : + m_opaque_sp(debugger_sp) +{ +} + +SBDebugger::SBDebugger(const SBDebugger &rhs) : + m_opaque_sp (rhs.m_opaque_sp) +{ +} + +SBDebugger & +SBDebugger::operator = (const SBDebugger &rhs) +{ + if (this != &rhs) + { + m_opaque_sp = rhs.m_opaque_sp; + } + return *this; +} + +SBDebugger::~SBDebugger () +{ +} + +bool +SBDebugger::IsValid() const +{ + return m_opaque_sp.get() != NULL; +} + + +void +SBDebugger::SetAsync (bool b) +{ + if (m_opaque_sp) + m_opaque_sp->SetAsyncExecution(b); +} + +bool +SBDebugger::GetAsync () +{ + if (m_opaque_sp) + return m_opaque_sp->GetAsyncExecution(); + else + return false; +} + +void +SBDebugger::SkipLLDBInitFiles (bool b) +{ + if (m_opaque_sp) + m_opaque_sp->GetCommandInterpreter().SkipLLDBInitFiles (b); +} + +void +SBDebugger::SkipAppInitFiles (bool b) +{ + if (m_opaque_sp) + m_opaque_sp->GetCommandInterpreter().SkipAppInitFiles (b); +} + +// Shouldn't really be settable after initialization as this could cause lots of problems; don't want users +// trying to switch modes in the middle of a debugging session. +void +SBDebugger::SetInputFileHandle (FILE *fh, bool transfer_ownership) +{ + Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + if (log) + log->Printf ("SBDebugger(%p)::SetInputFileHandle (fh=%p, transfer_ownership=%i)", m_opaque_sp.get(), + fh, transfer_ownership); + + if (m_opaque_sp) + m_opaque_sp->SetInputFileHandle (fh, transfer_ownership); +} + +void +SBDebugger::SetOutputFileHandle (FILE *fh, bool transfer_ownership) +{ + Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + + if (log) + log->Printf ("SBDebugger(%p)::SetOutputFileHandle (fh=%p, transfer_ownership=%i)", m_opaque_sp.get(), + fh, transfer_ownership); + + if (m_opaque_sp) + m_opaque_sp->SetOutputFileHandle (fh, transfer_ownership); +} + +void +SBDebugger::SetErrorFileHandle (FILE *fh, bool transfer_ownership) +{ + Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + + if (log) + log->Printf ("SBDebugger(%p)::SetErrorFileHandle (fh=%p, transfer_ownership=%i)", m_opaque_sp.get(), + fh, transfer_ownership); + + if (m_opaque_sp) + m_opaque_sp->SetErrorFileHandle (fh, transfer_ownership); +} + +FILE * +SBDebugger::GetInputFileHandle () +{ + if (m_opaque_sp) + return m_opaque_sp->GetInputFile().GetStream(); + return NULL; +} + +FILE * +SBDebugger::GetOutputFileHandle () +{ + if (m_opaque_sp) + return m_opaque_sp->GetOutputFile().GetStream(); + return NULL; +} + +FILE * +SBDebugger::GetErrorFileHandle () +{ + if (m_opaque_sp) + return m_opaque_sp->GetErrorFile().GetStream(); + return NULL; +} + +void +SBDebugger::SaveInputTerminalState() +{ + if (m_opaque_sp) + m_opaque_sp->SaveInputTerminalState(); +} + +void +SBDebugger::RestoreInputTerminalState() +{ + if (m_opaque_sp) + m_opaque_sp->RestoreInputTerminalState(); + +} +SBCommandInterpreter +SBDebugger::GetCommandInterpreter () +{ + Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + SBCommandInterpreter sb_interpreter; + if (m_opaque_sp) + sb_interpreter.reset (&m_opaque_sp->GetCommandInterpreter()); + + if (log) + log->Printf ("SBDebugger(%p)::GetCommandInterpreter () => SBCommandInterpreter(%p)", + m_opaque_sp.get(), sb_interpreter.get()); + + return sb_interpreter; +} + +void +SBDebugger::HandleCommand (const char *command) +{ + if (m_opaque_sp) + { + TargetSP target_sp (m_opaque_sp->GetSelectedTarget()); + Mutex::Locker api_locker; + if (target_sp) + api_locker.Lock(target_sp->GetAPIMutex()); + + SBCommandInterpreter sb_interpreter(GetCommandInterpreter ()); + SBCommandReturnObject result; + + sb_interpreter.HandleCommand (command, result, false); + + if (GetErrorFileHandle() != NULL) + result.PutError (GetErrorFileHandle()); + if (GetOutputFileHandle() != NULL) + result.PutOutput (GetOutputFileHandle()); + + if (m_opaque_sp->GetAsyncExecution() == false) + { + SBProcess process(GetCommandInterpreter().GetProcess ()); + ProcessSP process_sp (process.GetSP()); + if (process_sp) + { + EventSP event_sp; + Listener &lldb_listener = m_opaque_sp->GetListener(); + while (lldb_listener.GetNextEventForBroadcaster (process_sp.get(), event_sp)) + { + SBEvent event(event_sp); + HandleProcessEvent (process, event, GetOutputFileHandle(), GetErrorFileHandle()); + } + } + } + } +} + +SBListener +SBDebugger::GetListener () +{ + Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + SBListener sb_listener; + if (m_opaque_sp) + sb_listener.reset(&m_opaque_sp->GetListener(), false); + + if (log) + log->Printf ("SBDebugger(%p)::GetListener () => SBListener(%p)", m_opaque_sp.get(), + sb_listener.get()); + + return sb_listener; +} + +void +SBDebugger::HandleProcessEvent (const SBProcess &process, const SBEvent &event, FILE *out, FILE *err) +{ + if (!process.IsValid()) + return; + + TargetSP target_sp (process.GetTarget().GetSP()); + if (!target_sp) + return; + + const uint32_t event_type = event.GetType(); + char stdio_buffer[1024]; + size_t len; + + Mutex::Locker api_locker (target_sp->GetAPIMutex()); + + if (event_type & (Process::eBroadcastBitSTDOUT | Process::eBroadcastBitStateChanged)) + { + // Drain stdout when we stop just in case we have any bytes + while ((len = process.GetSTDOUT (stdio_buffer, sizeof (stdio_buffer))) > 0) + if (out != NULL) + ::fwrite (stdio_buffer, 1, len, out); + } + + if (event_type & (Process::eBroadcastBitSTDERR | Process::eBroadcastBitStateChanged)) + { + // Drain stderr when we stop just in case we have any bytes + while ((len = process.GetSTDERR (stdio_buffer, sizeof (stdio_buffer))) > 0) + if (err != NULL) + ::fwrite (stdio_buffer, 1, len, err); + } + + if (event_type & Process::eBroadcastBitStateChanged) + { + StateType event_state = SBProcess::GetStateFromEvent (event); + + if (event_state == eStateInvalid) + return; + + bool is_stopped = StateIsStoppedState (event_state); + if (!is_stopped) + process.ReportEventState (event, out); + } +} + +SBSourceManager +SBDebugger::GetSourceManager () +{ + SBSourceManager sb_source_manager (*this); + return sb_source_manager; +} + + +bool +SBDebugger::GetDefaultArchitecture (char *arch_name, size_t arch_name_len) +{ + if (arch_name && arch_name_len) + { + ArchSpec default_arch = Target::GetDefaultArchitecture (); + + if (default_arch.IsValid()) + { + const std::string &triple_str = default_arch.GetTriple().str(); + if (!triple_str.empty()) + ::snprintf (arch_name, arch_name_len, "%s", triple_str.c_str()); + else + ::snprintf (arch_name, arch_name_len, "%s", default_arch.GetArchitectureName()); + return true; + } + } + if (arch_name && arch_name_len) + arch_name[0] = '\0'; + return false; +} + + +bool +SBDebugger::SetDefaultArchitecture (const char *arch_name) +{ + if (arch_name) + { + ArchSpec arch (arch_name); + if (arch.IsValid()) + { + Target::SetDefaultArchitecture (arch); + return true; + } + } + return false; +} + +ScriptLanguage +SBDebugger::GetScriptingLanguage (const char *script_language_name) +{ + + return Args::StringToScriptLanguage (script_language_name, + eScriptLanguageDefault, + NULL); +} + +const char * +SBDebugger::GetVersionString () +{ + return GetVersion(); +} + +const char * +SBDebugger::StateAsCString (StateType state) +{ + return lldb_private::StateAsCString (state); +} + +bool +SBDebugger::StateIsRunningState (StateType state) +{ + Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + const bool result = lldb_private::StateIsRunningState (state); + if (log) + log->Printf ("SBDebugger::StateIsRunningState (state=%s) => %i", + StateAsCString (state), result); + + return result; +} + +bool +SBDebugger::StateIsStoppedState (StateType state) +{ + Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + const bool result = lldb_private::StateIsStoppedState (state, false); + if (log) + log->Printf ("SBDebugger::StateIsStoppedState (state=%s) => %i", + StateAsCString (state), result); + + return result; +} + +lldb::SBTarget +SBDebugger::CreateTarget (const char *filename, + const char *target_triple, + const char *platform_name, + bool add_dependent_modules, + lldb::SBError& sb_error) +{ + SBTarget sb_target; + TargetSP target_sp; + if (m_opaque_sp) + { + sb_error.Clear(); + OptionGroupPlatform platform_options (false); + platform_options.SetPlatformName (platform_name); + + sb_error.ref() = m_opaque_sp->GetTargetList().CreateTarget (*m_opaque_sp, + filename, + target_triple, + add_dependent_modules, + &platform_options, + target_sp); + + if (sb_error.Success()) + sb_target.SetSP (target_sp); + } + else + { + sb_error.SetErrorString("invalid target"); + } + + Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (log) + { + log->Printf ("SBDebugger(%p)::CreateTarget (filename=\"%s\", triple=%s, platform_name=%s, add_dependent_modules=%u, error=%s) => SBTarget(%p)", + m_opaque_sp.get(), + filename, + target_triple, + platform_name, + add_dependent_modules, + sb_error.GetCString(), + target_sp.get()); + } + + return sb_target; +} + +SBTarget +SBDebugger::CreateTargetWithFileAndTargetTriple (const char *filename, + const char *target_triple) +{ + SBTarget sb_target; + TargetSP target_sp; + if (m_opaque_sp) + { + const bool add_dependent_modules = true; + Error error (m_opaque_sp->GetTargetList().CreateTarget (*m_opaque_sp, + filename, + target_triple, + add_dependent_modules, + NULL, + target_sp)); + sb_target.SetSP (target_sp); + } + + Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (log) + { + log->Printf ("SBDebugger(%p)::CreateTargetWithFileAndTargetTriple (filename=\"%s\", triple=%s) => SBTarget(%p)", + m_opaque_sp.get(), filename, target_triple, target_sp.get()); + } + + return sb_target; +} + +SBTarget +SBDebugger::CreateTargetWithFileAndArch (const char *filename, const char *arch_cstr) +{ + Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + SBTarget sb_target; + TargetSP target_sp; + if (m_opaque_sp) + { + Error error; + const bool add_dependent_modules = true; + + error = m_opaque_sp->GetTargetList().CreateTarget (*m_opaque_sp, + filename, + arch_cstr, + add_dependent_modules, + NULL, + target_sp); + + if (error.Success()) + { + m_opaque_sp->GetTargetList().SetSelectedTarget (target_sp.get()); + sb_target.SetSP (target_sp); + } + } + + if (log) + { + log->Printf ("SBDebugger(%p)::CreateTargetWithFileAndArch (filename=\"%s\", arch=%s) => SBTarget(%p)", + m_opaque_sp.get(), filename, arch_cstr, target_sp.get()); + } + + return sb_target; +} + +SBTarget +SBDebugger::CreateTarget (const char *filename) +{ + SBTarget sb_target; + TargetSP target_sp; + if (m_opaque_sp) + { + ArchSpec arch = Target::GetDefaultArchitecture (); + Error error; + const bool add_dependent_modules = true; + + PlatformSP platform_sp(m_opaque_sp->GetPlatformList().GetSelectedPlatform()); + error = m_opaque_sp->GetTargetList().CreateTarget (*m_opaque_sp, + filename, + arch, + add_dependent_modules, + platform_sp, + target_sp); + + if (error.Success()) + { + m_opaque_sp->GetTargetList().SetSelectedTarget (target_sp.get()); + sb_target.SetSP (target_sp); + } + } + Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (log) + { + log->Printf ("SBDebugger(%p)::CreateTarget (filename=\"%s\") => SBTarget(%p)", + m_opaque_sp.get(), filename, target_sp.get()); + } + return sb_target; +} + +bool +SBDebugger::DeleteTarget (lldb::SBTarget &target) +{ + bool result = false; + if (m_opaque_sp) + { + TargetSP target_sp(target.GetSP()); + if (target_sp) + { + // No need to lock, the target list is thread safe + result = m_opaque_sp->GetTargetList().DeleteTarget (target_sp); + target_sp->Destroy(); + target.Clear(); + const bool mandatory = true; + ModuleList::RemoveOrphanSharedModules(mandatory); + } + } + + Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (log) + { + log->Printf ("SBDebugger(%p)::DeleteTarget (SBTarget(%p)) => %i", m_opaque_sp.get(), target.m_opaque_sp.get(), result); + } + + return result; +} +SBTarget +SBDebugger::GetTargetAtIndex (uint32_t idx) +{ + SBTarget sb_target; + if (m_opaque_sp) + { + // No need to lock, the target list is thread safe + sb_target.SetSP (m_opaque_sp->GetTargetList().GetTargetAtIndex (idx)); + } + return sb_target; +} + +uint32_t +SBDebugger::GetIndexOfTarget (lldb::SBTarget target) +{ + + lldb::TargetSP target_sp = target.GetSP(); + if (!target_sp) + return UINT32_MAX; + + if (!m_opaque_sp) + return UINT32_MAX; + + return m_opaque_sp->GetTargetList().GetIndexOfTarget (target.GetSP()); +} + +SBTarget +SBDebugger::FindTargetWithProcessID (pid_t pid) +{ + SBTarget sb_target; + if (m_opaque_sp) + { + // No need to lock, the target list is thread safe + sb_target.SetSP (m_opaque_sp->GetTargetList().FindTargetWithProcessID (pid)); + } + return sb_target; +} + +SBTarget +SBDebugger::FindTargetWithFileAndArch (const char *filename, const char *arch_name) +{ + SBTarget sb_target; + if (m_opaque_sp && filename && filename[0]) + { + // No need to lock, the target list is thread safe + ArchSpec arch (arch_name, m_opaque_sp->GetPlatformList().GetSelectedPlatform().get()); + TargetSP target_sp (m_opaque_sp->GetTargetList().FindTargetWithExecutableAndArchitecture (FileSpec(filename, false), arch_name ? &arch : NULL)); + sb_target.SetSP (target_sp); + } + return sb_target; +} + +SBTarget +SBDebugger::FindTargetWithLLDBProcess (const ProcessSP &process_sp) +{ + SBTarget sb_target; + if (m_opaque_sp) + { + // No need to lock, the target list is thread safe + sb_target.SetSP (m_opaque_sp->GetTargetList().FindTargetWithProcess (process_sp.get())); + } + return sb_target; +} + + +uint32_t +SBDebugger::GetNumTargets () +{ + if (m_opaque_sp) + { + // No need to lock, the target list is thread safe + return m_opaque_sp->GetTargetList().GetNumTargets (); + } + return 0; +} + +SBTarget +SBDebugger::GetSelectedTarget () +{ + Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + SBTarget sb_target; + TargetSP target_sp; + if (m_opaque_sp) + { + // No need to lock, the target list is thread safe + target_sp = m_opaque_sp->GetTargetList().GetSelectedTarget (); + sb_target.SetSP (target_sp); + } + + if (log) + { + SBStream sstr; + sb_target.GetDescription (sstr, eDescriptionLevelBrief); + log->Printf ("SBDebugger(%p)::GetSelectedTarget () => SBTarget(%p): %s", m_opaque_sp.get(), + target_sp.get(), sstr.GetData()); + } + + return sb_target; +} + +void +SBDebugger::SetSelectedTarget (SBTarget &sb_target) +{ + Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + TargetSP target_sp (sb_target.GetSP()); + if (m_opaque_sp) + { + m_opaque_sp->GetTargetList().SetSelectedTarget (target_sp.get()); + } + if (log) + { + SBStream sstr; + sb_target.GetDescription (sstr, eDescriptionLevelBrief); + log->Printf ("SBDebugger(%p)::SetSelectedTarget () => SBTarget(%p): %s", m_opaque_sp.get(), + target_sp.get(), sstr.GetData()); + } +} + +void +SBDebugger::DispatchInput (void* baton, const void *data, size_t data_len) +{ + DispatchInput (data,data_len); +} + +void +SBDebugger::DispatchInput (const void *data, size_t data_len) +{ + Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + if (log) + log->Printf ("SBDebugger(%p)::DispatchInput (data=\"%.*s\", size_t=%" PRIu64 ")", + m_opaque_sp.get(), + (int) data_len, + (const char *) data, + (uint64_t)data_len); + + if (m_opaque_sp) + m_opaque_sp->DispatchInput ((const char *) data, data_len); +} + +void +SBDebugger::DispatchInputInterrupt () +{ + if (m_opaque_sp) + m_opaque_sp->DispatchInputInterrupt (); +} + +void +SBDebugger::DispatchInputEndOfFile () +{ + if (m_opaque_sp) + m_opaque_sp->DispatchInputEndOfFile (); +} + +bool +SBDebugger::InputReaderIsTopReader (const lldb::SBInputReader &reader) +{ + Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + if (log) + log->Printf ("SBDebugger(%p)::InputReaderIsTopReader (SBInputReader(%p))", m_opaque_sp.get(), &reader); + + if (m_opaque_sp && reader.IsValid()) + { + InputReaderSP reader_sp (*reader); + return m_opaque_sp->InputReaderIsTopReader (reader_sp); + } + + return false; +} + + +void +SBDebugger::PushInputReader (SBInputReader &reader) +{ + Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + if (log) + log->Printf ("SBDebugger(%p)::PushInputReader (SBInputReader(%p))", m_opaque_sp.get(), &reader); + + if (m_opaque_sp && reader.IsValid()) + { + TargetSP target_sp (m_opaque_sp->GetSelectedTarget()); + Mutex::Locker api_locker; + if (target_sp) + api_locker.Lock(target_sp->GetAPIMutex()); + InputReaderSP reader_sp(*reader); + m_opaque_sp->PushInputReader (reader_sp); + } +} + +void +SBDebugger::NotifyTopInputReader (InputReaderAction notification) +{ + Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + if (log) + log->Printf ("SBDebugger(%p)::NotifyTopInputReader (%d)", m_opaque_sp.get(), notification); + + if (m_opaque_sp) + m_opaque_sp->NotifyTopInputReader (notification); +} + +void +SBDebugger::reset (const DebuggerSP &debugger_sp) +{ + m_opaque_sp = debugger_sp; +} + +Debugger * +SBDebugger::get () const +{ + return m_opaque_sp.get(); +} + +Debugger & +SBDebugger::ref () const +{ + assert (m_opaque_sp.get()); + return *m_opaque_sp; +} + +const lldb::DebuggerSP & +SBDebugger::get_sp () const +{ + return m_opaque_sp; +} + +SBDebugger +SBDebugger::FindDebuggerWithID (int id) +{ + // No need to lock, the debugger list is thread safe + SBDebugger sb_debugger; + DebuggerSP debugger_sp = Debugger::FindDebuggerWithID (id); + if (debugger_sp) + sb_debugger.reset (debugger_sp); + return sb_debugger; +} + +const char * +SBDebugger::GetInstanceName() +{ + if (m_opaque_sp) + return m_opaque_sp->GetInstanceName().AsCString(); + else + return NULL; +} + +SBError +SBDebugger::SetInternalVariable (const char *var_name, const char *value, const char *debugger_instance_name) +{ + SBError sb_error; + DebuggerSP debugger_sp(Debugger::FindDebuggerWithInstanceName (ConstString(debugger_instance_name))); + Error error; + if (debugger_sp) + { + ExecutionContext exe_ctx (debugger_sp->GetCommandInterpreter().GetExecutionContext()); + error = debugger_sp->SetPropertyValue (&exe_ctx, + eVarSetOperationAssign, + var_name, + value); + } + else + { + error.SetErrorStringWithFormat ("invalid debugger instance name '%s'", debugger_instance_name); + } + if (error.Fail()) + sb_error.SetError(error); + return sb_error; +} + +SBStringList +SBDebugger::GetInternalVariableValue (const char *var_name, const char *debugger_instance_name) +{ + SBStringList ret_value; + DebuggerSP debugger_sp(Debugger::FindDebuggerWithInstanceName (ConstString(debugger_instance_name))); + Error error; + if (debugger_sp) + { + ExecutionContext exe_ctx (debugger_sp->GetCommandInterpreter().GetExecutionContext()); + lldb::OptionValueSP value_sp (debugger_sp->GetPropertyValue (&exe_ctx, + var_name, + false, + error)); + if (value_sp) + { + StreamString value_strm; + value_sp->DumpValue (&exe_ctx, value_strm, OptionValue::eDumpOptionValue); + const std::string &value_str = value_strm.GetString(); + if (!value_str.empty()) + { + StringList string_list; + string_list.SplitIntoLines(value_str.c_str(), value_str.size()); + return SBStringList(&string_list); + } + } + } + return SBStringList(); +} + +uint32_t +SBDebugger::GetTerminalWidth () const +{ + if (m_opaque_sp) + return m_opaque_sp->GetTerminalWidth (); + return 0; +} + +void +SBDebugger::SetTerminalWidth (uint32_t term_width) +{ + if (m_opaque_sp) + m_opaque_sp->SetTerminalWidth (term_width); +} + +const char * +SBDebugger::GetPrompt() const +{ + Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + if (log) + log->Printf ("SBDebugger(%p)::GetPrompt () => \"%s\"", m_opaque_sp.get(), + (m_opaque_sp ? m_opaque_sp->GetPrompt() : "")); + + if (m_opaque_sp) + return m_opaque_sp->GetPrompt (); + return 0; +} + +void +SBDebugger::SetPrompt (const char *prompt) +{ + if (m_opaque_sp) + m_opaque_sp->SetPrompt (prompt); +} + + +ScriptLanguage +SBDebugger::GetScriptLanguage() const +{ + if (m_opaque_sp) + return m_opaque_sp->GetScriptLanguage (); + return eScriptLanguageNone; +} + +void +SBDebugger::SetScriptLanguage (ScriptLanguage script_lang) +{ + if (m_opaque_sp) + { + m_opaque_sp->SetScriptLanguage (script_lang); + } +} + +bool +SBDebugger::SetUseExternalEditor (bool value) +{ + if (m_opaque_sp) + return m_opaque_sp->SetUseExternalEditor (value); + return false; +} + +bool +SBDebugger::GetUseExternalEditor () +{ + if (m_opaque_sp) + return m_opaque_sp->GetUseExternalEditor (); + return false; +} + +bool +SBDebugger::SetUseColor (bool value) +{ + if (m_opaque_sp) + return m_opaque_sp->SetUseColor (value); + return false; +} + +bool +SBDebugger::GetUseColor () const +{ + if (m_opaque_sp) + return m_opaque_sp->GetUseColor (); + return false; +} + +bool +SBDebugger::GetDescription (SBStream &description) +{ + Stream &strm = description.ref(); + + if (m_opaque_sp) + { + const char *name = m_opaque_sp->GetInstanceName().AsCString(); + user_id_t id = m_opaque_sp->GetID(); + strm.Printf ("Debugger (instance: \"%s\", id: %" PRIu64 ")", name, id); + } + else + strm.PutCString ("No value"); + + return true; +} + +user_id_t +SBDebugger::GetID() +{ + if (m_opaque_sp) + return m_opaque_sp->GetID(); + return LLDB_INVALID_UID; +} + + +SBError +SBDebugger::SetCurrentPlatform (const char *platform_name) +{ + SBError sb_error; + if (m_opaque_sp) + { + PlatformSP platform_sp (Platform::Create (platform_name, sb_error.ref())); + + if (platform_sp) + { + bool make_selected = true; + m_opaque_sp->GetPlatformList().Append (platform_sp, make_selected); + } + } + return sb_error; +} + +bool +SBDebugger::SetCurrentPlatformSDKRoot (const char *sysroot) +{ + if (m_opaque_sp) + { + PlatformSP platform_sp (m_opaque_sp->GetPlatformList().GetSelectedPlatform()); + + if (platform_sp) + { + platform_sp->SetSDKRootDirectory (ConstString (sysroot)); + return true; + } + } + return false; +} + +bool +SBDebugger::GetCloseInputOnEOF () const +{ + if (m_opaque_sp) + return m_opaque_sp->GetCloseInputOnEOF (); + return false; +} + +void +SBDebugger::SetCloseInputOnEOF (bool b) +{ + if (m_opaque_sp) + m_opaque_sp->SetCloseInputOnEOF (b); +} + +SBTypeCategory +SBDebugger::GetCategory (const char* category_name) +{ + if (!category_name || *category_name == 0) + return SBTypeCategory(); + + TypeCategoryImplSP category_sp; + + if (DataVisualization::Categories::GetCategory(ConstString(category_name), category_sp, false)) + return SBTypeCategory(category_sp); + else + return SBTypeCategory(); +} + +SBTypeCategory +SBDebugger::CreateCategory (const char* category_name) +{ + if (!category_name || *category_name == 0) + return SBTypeCategory(); + + TypeCategoryImplSP category_sp; + + if (DataVisualization::Categories::GetCategory(ConstString(category_name), category_sp, true)) + return SBTypeCategory(category_sp); + else + return SBTypeCategory(); +} + +bool +SBDebugger::DeleteCategory (const char* category_name) +{ + if (!category_name || *category_name == 0) + return false; + + return DataVisualization::Categories::Delete(ConstString(category_name)); +} + +uint32_t +SBDebugger::GetNumCategories() +{ + return DataVisualization::Categories::GetCount(); +} + +SBTypeCategory +SBDebugger::GetCategoryAtIndex (uint32_t index) +{ + return SBTypeCategory(DataVisualization::Categories::GetCategoryAtIndex(index)); +} + +SBTypeCategory +SBDebugger::GetDefaultCategory() +{ + return GetCategory("default"); +} + +SBTypeFormat +SBDebugger::GetFormatForType (SBTypeNameSpecifier type_name) +{ + SBTypeCategory default_category_sb = GetDefaultCategory(); + if (default_category_sb.GetEnabled()) + return default_category_sb.GetFormatForType(type_name); + return SBTypeFormat(); +} + +#ifndef LLDB_DISABLE_PYTHON +SBTypeSummary +SBDebugger::GetSummaryForType (SBTypeNameSpecifier type_name) +{ + if (type_name.IsValid() == false) + return SBTypeSummary(); + return SBTypeSummary(DataVisualization::GetSummaryForType(type_name.GetSP())); +} +#endif // LLDB_DISABLE_PYTHON + +SBTypeFilter +SBDebugger::GetFilterForType (SBTypeNameSpecifier type_name) +{ + if (type_name.IsValid() == false) + return SBTypeFilter(); + return SBTypeFilter(DataVisualization::GetFilterForType(type_name.GetSP())); +} + +#ifndef LLDB_DISABLE_PYTHON +SBTypeSynthetic +SBDebugger::GetSyntheticForType (SBTypeNameSpecifier type_name) +{ + if (type_name.IsValid() == false) + return SBTypeSynthetic(); + return SBTypeSynthetic(DataVisualization::GetSyntheticForType(type_name.GetSP())); +} +#endif // LLDB_DISABLE_PYTHON + +bool +SBDebugger::EnableLog (const char *channel, const char **categories) +{ + if (m_opaque_sp) + { + uint32_t log_options = LLDB_LOG_OPTION_PREPEND_TIMESTAMP | LLDB_LOG_OPTION_PREPEND_THREAD_NAME; + StreamString errors; + return m_opaque_sp->EnableLog (channel, categories, NULL, log_options, errors); + + } + else + return false; +} + +void +SBDebugger::SetLoggingCallback (lldb::LogOutputCallback log_callback, void *baton) +{ + if (m_opaque_sp) + { + return m_opaque_sp->SetLoggingCallback (log_callback, baton); + } +} + + diff --git a/contrib/llvm/tools/lldb/source/API/SBDeclaration.cpp b/contrib/llvm/tools/lldb/source/API/SBDeclaration.cpp new file mode 100644 index 00000000000..fc90156e75a --- /dev/null +++ b/contrib/llvm/tools/lldb/source/API/SBDeclaration.cpp @@ -0,0 +1,206 @@ +//===-- SBDeclaration.cpp -----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/API/SBDeclaration.h" +#include "lldb/API/SBStream.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Stream.h" +#include "lldb/Symbol/Declaration.h" + +#include + +using namespace lldb; +using namespace lldb_private; + + +SBDeclaration::SBDeclaration () : + m_opaque_ap () +{ +} + +SBDeclaration::SBDeclaration (const SBDeclaration &rhs) : + m_opaque_ap () +{ + if (rhs.IsValid()) + ref() = rhs.ref(); +} + +SBDeclaration::SBDeclaration (const lldb_private::Declaration *lldb_object_ptr) : + m_opaque_ap () +{ + if (lldb_object_ptr) + ref() = *lldb_object_ptr; +} + +const SBDeclaration & +SBDeclaration::operator = (const SBDeclaration &rhs) +{ + if (this != &rhs) + { + if (rhs.IsValid()) + ref() = rhs.ref(); + else + m_opaque_ap.reset(); + } + return *this; +} + +void +SBDeclaration::SetDeclaration (const lldb_private::Declaration &lldb_object_ref) +{ + ref() = lldb_object_ref; +} + + +SBDeclaration::~SBDeclaration () +{ +} + + +bool +SBDeclaration::IsValid () const +{ + return m_opaque_ap.get() && m_opaque_ap->IsValid(); +} + + +SBFileSpec +SBDeclaration::GetFileSpec () const +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + SBFileSpec sb_file_spec; + if (m_opaque_ap.get() && m_opaque_ap->GetFile()) + sb_file_spec.SetFileSpec(m_opaque_ap->GetFile()); + + if (log) + { + SBStream sstr; + sb_file_spec.GetDescription (sstr); + log->Printf ("SBLineEntry(%p)::GetFileSpec () => SBFileSpec(%p): %s", m_opaque_ap.get(), + sb_file_spec.get(), sstr.GetData()); + } + + return sb_file_spec; +} + +uint32_t +SBDeclaration::GetLine () const +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + uint32_t line = 0; + if (m_opaque_ap.get()) + line = m_opaque_ap->GetLine(); + + if (log) + log->Printf ("SBLineEntry(%p)::GetLine () => %u", m_opaque_ap.get(), line); + + return line; +} + + +uint32_t +SBDeclaration::GetColumn () const +{ + if (m_opaque_ap.get()) + return m_opaque_ap->GetColumn(); + return 0; +} + +void +SBDeclaration::SetFileSpec (lldb::SBFileSpec filespec) +{ + if (filespec.IsValid()) + ref().SetFile(filespec.ref()); + else + ref().SetFile(FileSpec()); +} +void +SBDeclaration::SetLine (uint32_t line) +{ + ref().SetLine(line); +} + +void +SBDeclaration::SetColumn (uint32_t column) +{ + ref().SetColumn(column); +} + + + +bool +SBDeclaration::operator == (const SBDeclaration &rhs) const +{ + lldb_private::Declaration *lhs_ptr = m_opaque_ap.get(); + lldb_private::Declaration *rhs_ptr = rhs.m_opaque_ap.get(); + + if (lhs_ptr && rhs_ptr) + return lldb_private::Declaration::Compare (*lhs_ptr, *rhs_ptr) == 0; + + return lhs_ptr == rhs_ptr; +} + +bool +SBDeclaration::operator != (const SBDeclaration &rhs) const +{ + lldb_private::Declaration *lhs_ptr = m_opaque_ap.get(); + lldb_private::Declaration *rhs_ptr = rhs.m_opaque_ap.get(); + + if (lhs_ptr && rhs_ptr) + return lldb_private::Declaration::Compare (*lhs_ptr, *rhs_ptr) != 0; + + return lhs_ptr != rhs_ptr; +} + +const lldb_private::Declaration * +SBDeclaration::operator->() const +{ + return m_opaque_ap.get(); +} + +lldb_private::Declaration & +SBDeclaration::ref() +{ + if (m_opaque_ap.get() == NULL) + m_opaque_ap.reset (new lldb_private::Declaration ()); + return *m_opaque_ap; +} + +const lldb_private::Declaration & +SBDeclaration::ref() const +{ + return *m_opaque_ap; +} + +bool +SBDeclaration::GetDescription (SBStream &description) +{ + Stream &strm = description.ref(); + + if (m_opaque_ap.get()) + { + char file_path[PATH_MAX*2]; + m_opaque_ap->GetFile().GetPath (file_path, sizeof (file_path)); + strm.Printf ("%s:%u", file_path, GetLine()); + if (GetColumn() > 0) + strm.Printf (":%u", GetColumn()); + } + else + strm.PutCString ("No value"); + + return true; +} + +lldb_private::Declaration * +SBDeclaration::get () +{ + return m_opaque_ap.get(); +} diff --git a/contrib/llvm/tools/lldb/source/API/SBError.cpp b/contrib/llvm/tools/lldb/source/API/SBError.cpp new file mode 100644 index 00000000000..bd6b54300f6 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/API/SBError.cpp @@ -0,0 +1,233 @@ +//===-- SBError.cpp ---------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/API/SBError.h" +#include "lldb/API/SBStream.h" +#include "lldb/Core/Error.h" +#include "lldb/Core/Log.h" + +#include + +using namespace lldb; +using namespace lldb_private; + + +SBError::SBError () : + m_opaque_ap () +{ +} + +SBError::SBError (const SBError &rhs) : + m_opaque_ap () +{ + if (rhs.IsValid()) + m_opaque_ap.reset (new Error(*rhs)); +} + + +SBError::~SBError() +{ +} + +const SBError & +SBError::operator = (const SBError &rhs) +{ + if (rhs.IsValid()) + { + if (m_opaque_ap.get()) + *m_opaque_ap = *rhs; + else + m_opaque_ap.reset (new Error(*rhs)); + } + else + m_opaque_ap.reset(); + + return *this; +} + + +const char * +SBError::GetCString () const +{ + if (m_opaque_ap.get()) + return m_opaque_ap->AsCString(); + return NULL; +} + +void +SBError::Clear () +{ + if (m_opaque_ap.get()) + m_opaque_ap->Clear(); +} + +bool +SBError::Fail () const +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + bool ret_value = false; + if (m_opaque_ap.get()) + ret_value = m_opaque_ap->Fail(); + + if (log) + log->Printf ("SBError(%p)::Fail () => %i", m_opaque_ap.get(), ret_value); + + return ret_value; +} + +bool +SBError::Success () const +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + bool ret_value = true; + if (m_opaque_ap.get()) + ret_value = m_opaque_ap->Success(); + + if (log) + log->Printf ("SBError(%p)::Success () => %i", m_opaque_ap.get(), ret_value); + + return ret_value; +} + +uint32_t +SBError::GetError () const +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + uint32_t err = 0; + if (m_opaque_ap.get()) + err = m_opaque_ap->GetError(); + + if (log) + log->Printf ("SBError(%p)::GetError () => 0x%8.8x", m_opaque_ap.get(), err); + + + return err; +} + +ErrorType +SBError::GetType () const +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + ErrorType err_type = eErrorTypeInvalid; + if (m_opaque_ap.get()) + err_type = m_opaque_ap->GetType(); + + if (log) + log->Printf ("SBError(%p)::GetType () => %i", m_opaque_ap.get(), err_type); + + return err_type; +} + +void +SBError::SetError (uint32_t err, ErrorType type) +{ + CreateIfNeeded (); + m_opaque_ap->SetError (err, type); +} + +void +SBError::SetError (const Error &lldb_error) +{ + CreateIfNeeded (); + *m_opaque_ap = lldb_error; +} + + +void +SBError::SetErrorToErrno () +{ + CreateIfNeeded (); + m_opaque_ap->SetErrorToErrno (); +} + +void +SBError::SetErrorToGenericError () +{ + CreateIfNeeded (); + m_opaque_ap->SetErrorToErrno (); +} + +void +SBError::SetErrorString (const char *err_str) +{ + CreateIfNeeded (); + m_opaque_ap->SetErrorString (err_str); +} + +int +SBError::SetErrorStringWithFormat (const char *format, ...) +{ + CreateIfNeeded (); + va_list args; + va_start (args, format); + int num_chars = m_opaque_ap->SetErrorStringWithVarArg (format, args); + va_end (args); + return num_chars; +} + +bool +SBError::IsValid () const +{ + return m_opaque_ap.get() != NULL; +} + +void +SBError::CreateIfNeeded () +{ + if (m_opaque_ap.get() == NULL) + m_opaque_ap.reset(new Error ()); +} + + +lldb_private::Error * +SBError::operator->() +{ + return m_opaque_ap.get(); +} + +lldb_private::Error * +SBError::get() +{ + return m_opaque_ap.get(); +} + +lldb_private::Error & +SBError::ref() +{ + CreateIfNeeded(); + return *m_opaque_ap; +} + +const lldb_private::Error & +SBError::operator*() const +{ + // Be sure to call "IsValid()" before calling this function or it will crash + return *m_opaque_ap; +} + +bool +SBError::GetDescription (SBStream &description) +{ + if (m_opaque_ap.get()) + { + if (m_opaque_ap->Success()) + description.Printf ("success"); + else + { + const char * err_string = GetCString(); + description.Printf ("error: %s", (err_string != NULL ? err_string : "")); + } + } + else + description.Printf ("error: "); + + return true; +} diff --git a/contrib/llvm/tools/lldb/source/API/SBEvent.cpp b/contrib/llvm/tools/lldb/source/API/SBEvent.cpp new file mode 100644 index 00000000000..d5d4a84bc1f --- /dev/null +++ b/contrib/llvm/tools/lldb/source/API/SBEvent.cpp @@ -0,0 +1,245 @@ +//===-- SBEvent.cpp ---------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "lldb/API/SBEvent.h" +#include "lldb/API/SBBroadcaster.h" +#include "lldb/API/SBStream.h" + +#include "lldb/Core/Event.h" +#include "lldb/Core/Stream.h" +#include "lldb/Core/StreamFile.h" +#include "lldb/Core/ConstString.h" +#include "lldb/Target/Process.h" +#include "lldb/Breakpoint/Breakpoint.h" +#include "lldb/Interpreter/CommandInterpreter.h" + +using namespace lldb; +using namespace lldb_private; + + +SBEvent::SBEvent () : + m_event_sp (), + m_opaque_ptr (NULL) +{ +} + +SBEvent::SBEvent (uint32_t event_type, const char *cstr, uint32_t cstr_len) : + m_event_sp (new Event (event_type, new EventDataBytes (cstr, cstr_len))), + m_opaque_ptr (m_event_sp.get()) +{ +} + +SBEvent::SBEvent (EventSP &event_sp) : + m_event_sp (event_sp), + m_opaque_ptr (event_sp.get()) +{ +} + +SBEvent::SBEvent (const SBEvent &rhs) : + m_event_sp (rhs.m_event_sp), + m_opaque_ptr (rhs.m_opaque_ptr) +{ + +} + +const SBEvent & +SBEvent::operator = (const SBEvent &rhs) +{ + if (this != &rhs) + { + m_event_sp = rhs.m_event_sp; + m_opaque_ptr = rhs.m_opaque_ptr; + } + return *this; +} + +SBEvent::~SBEvent() +{ +} + +const char * +SBEvent::GetDataFlavor () +{ + Event *lldb_event = get(); + if (lldb_event) + { + EventData *event_data = lldb_event->GetData(); + if (event_data) + return lldb_event->GetData()->GetFlavor().AsCString(); + } + return NULL; +} + +uint32_t +SBEvent::GetType () const +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + const Event *lldb_event = get(); + uint32_t event_type = 0; + if (lldb_event) + event_type = lldb_event->GetType(); + + if (log) + { + StreamString sstr; + if (lldb_event && lldb_event->GetBroadcaster() && lldb_event->GetBroadcaster()->GetEventNames(sstr, event_type, true)) + log->Printf ("SBEvent(%p)::GetType () => 0x%8.8x (%s)", get(), event_type, sstr.GetData()); + else + log->Printf ("SBEvent(%p)::GetType () => 0x%8.8x", get(), event_type); + + } + + return event_type; +} + +SBBroadcaster +SBEvent::GetBroadcaster () const +{ + SBBroadcaster broadcaster; + const Event *lldb_event = get(); + if (lldb_event) + broadcaster.reset (lldb_event->GetBroadcaster(), false); + return broadcaster; +} + +const char * +SBEvent::GetBroadcasterClass () const +{ + const Event *lldb_event = get(); + if (lldb_event) + return lldb_event->GetBroadcaster()->GetBroadcasterClass().AsCString(); + else + return "unknown class"; +} + +bool +SBEvent::BroadcasterMatchesPtr (const SBBroadcaster *broadcaster) +{ + if (broadcaster) + return BroadcasterMatchesRef (*broadcaster); + return false; +} + +bool +SBEvent::BroadcasterMatchesRef (const SBBroadcaster &broadcaster) +{ + + Event *lldb_event = get(); + bool success = false; + if (lldb_event) + success = lldb_event->BroadcasterIs (broadcaster.get()); + + // For logging, this gets a little chatty so only enable this when verbose logging is on + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API | LIBLLDB_LOG_VERBOSE)); + if (log) + log->Printf ("SBEvent(%p)::BroadcasterMatchesRef (SBBroadcaster(%p): %s) => %i", + get(), + broadcaster.get(), + broadcaster.GetName(), + success); + + return success; +} + +void +SBEvent::Clear() +{ + Event *lldb_event = get(); + if (lldb_event) + lldb_event->Clear(); +} + +EventSP & +SBEvent::GetSP () const +{ + return m_event_sp; +} + +Event * +SBEvent::get() const +{ + // There is a dangerous accessor call GetSharedPtr which can be used, so if + // we have anything valid in m_event_sp, we must use that since if it gets + // used by a function that puts something in there, then it won't update + // m_opaque_ptr... + if (m_event_sp) + m_opaque_ptr = m_event_sp.get(); + + return m_opaque_ptr; +} + +void +SBEvent::reset (EventSP &event_sp) +{ + m_event_sp = event_sp; + m_opaque_ptr = m_event_sp.get(); +} + +void +SBEvent::reset (Event* event_ptr) +{ + m_opaque_ptr = event_ptr; + m_event_sp.reset(); +} + +bool +SBEvent::IsValid() const +{ + // Do NOT use m_opaque_ptr directly!!! Must use the SBEvent::get() + // accessor. See comments in SBEvent::get().... + return SBEvent::get() != NULL; + +} + +const char * +SBEvent::GetCStringFromEvent (const SBEvent &event) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + if (log) + log->Printf ("SBEvent(%p)::GetCStringFromEvent () => \"%s\"", + event.get(), + reinterpret_cast(EventDataBytes::GetBytesFromEvent (event.get()))); + + return reinterpret_cast(EventDataBytes::GetBytesFromEvent (event.get())); +} + + +bool +SBEvent::GetDescription (SBStream &description) +{ + Stream &strm = description.ref(); + + if (get()) + { + m_opaque_ptr->Dump (&strm); + } + else + strm.PutCString ("No value"); + + return true; +} + +bool +SBEvent::GetDescription (SBStream &description) const +{ + Stream &strm = description.ref(); + + if (get()) + { + m_opaque_ptr->Dump (&strm); + } + else + strm.PutCString ("No value"); + + return true; +} diff --git a/contrib/llvm/tools/lldb/source/API/SBExpressionOptions.cpp b/contrib/llvm/tools/lldb/source/API/SBExpressionOptions.cpp new file mode 100644 index 00000000000..127b0cf13cd --- /dev/null +++ b/contrib/llvm/tools/lldb/source/API/SBExpressionOptions.cpp @@ -0,0 +1,126 @@ +//===-- SBExpressionOptions.cpp ---------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/API/SBExpressionOptions.h" +#include "lldb/API/SBStream.h" + +#include "lldb/Target/Target.h" + +using namespace lldb; +using namespace lldb_private; + + +SBExpressionOptions::SBExpressionOptions () : + m_opaque_ap(new EvaluateExpressionOptions()) +{ +} + +SBExpressionOptions::SBExpressionOptions (const SBExpressionOptions &rhs) +{ + m_opaque_ap.reset(new EvaluateExpressionOptions()); + *(m_opaque_ap.get()) = rhs.ref(); +} + +const SBExpressionOptions & +SBExpressionOptions::operator = (const SBExpressionOptions &rhs) +{ + if (this != &rhs) + { + this->ref() = rhs.ref(); + } + return *this; +} + +SBExpressionOptions::~SBExpressionOptions() +{ +} + +bool +SBExpressionOptions::GetCoerceResultToId () const +{ + return m_opaque_ap->DoesCoerceToId (); +} + +void +SBExpressionOptions::SetCoerceResultToId (bool coerce) +{ + m_opaque_ap->SetCoerceToId (coerce); +} + +bool +SBExpressionOptions::GetUnwindOnError () const +{ + return m_opaque_ap->DoesUnwindOnError (); +} + +void +SBExpressionOptions::SetUnwindOnError (bool unwind) +{ + m_opaque_ap->SetUnwindOnError (unwind); +} + +bool +SBExpressionOptions::GetIgnoreBreakpoints () const +{ + return m_opaque_ap->DoesIgnoreBreakpoints (); +} + +void +SBExpressionOptions::SetIgnoreBreakpoints (bool ignore) +{ + m_opaque_ap->SetIgnoreBreakpoints (ignore); +} + +lldb::DynamicValueType +SBExpressionOptions::GetFetchDynamicValue () const +{ + return m_opaque_ap->GetUseDynamic (); +} + +void +SBExpressionOptions::SetFetchDynamicValue (lldb::DynamicValueType dynamic) +{ + m_opaque_ap->SetUseDynamic (dynamic); +} + +uint32_t +SBExpressionOptions::GetTimeoutInMicroSeconds () const +{ + return m_opaque_ap->GetTimeoutUsec (); +} + +void +SBExpressionOptions::SetTimeoutInMicroSeconds (uint32_t timeout) +{ + m_opaque_ap->SetTimeoutUsec (timeout); +} + +bool +SBExpressionOptions::GetTryAllThreads () const +{ + return m_opaque_ap->GetRunOthers (); +} + +void +SBExpressionOptions::SetTryAllThreads (bool run_others) +{ + m_opaque_ap->SetRunOthers (run_others); +} + +EvaluateExpressionOptions * +SBExpressionOptions::get() const +{ + return m_opaque_ap.get(); +} + +EvaluateExpressionOptions & +SBExpressionOptions::ref () const +{ + return *(m_opaque_ap.get()); +} diff --git a/contrib/llvm/tools/lldb/source/API/SBFileSpec.cpp b/contrib/llvm/tools/lldb/source/API/SBFileSpec.cpp new file mode 100644 index 00000000000..4413689501a --- /dev/null +++ b/contrib/llvm/tools/lldb/source/API/SBFileSpec.cpp @@ -0,0 +1,181 @@ +//===-- SBFileSpec.cpp ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include + +#include "lldb/API/SBFileSpec.h" +#include "lldb/API/SBStream.h" +#include "lldb/Host/FileSpec.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Stream.h" + +using namespace lldb; +using namespace lldb_private; + + + +SBFileSpec::SBFileSpec () : + m_opaque_ap(new lldb_private::FileSpec()) +{ +} + +SBFileSpec::SBFileSpec (const SBFileSpec &rhs) : + m_opaque_ap(new lldb_private::FileSpec(*rhs.m_opaque_ap)) +{ +} + +SBFileSpec::SBFileSpec (const lldb_private::FileSpec& fspec) : + m_opaque_ap(new lldb_private::FileSpec(fspec)) +{ +} + +// Deprected!!! +SBFileSpec::SBFileSpec (const char *path) : + m_opaque_ap(new FileSpec (path, true)) +{ +} + +SBFileSpec::SBFileSpec (const char *path, bool resolve) : + m_opaque_ap(new FileSpec (path, resolve)) +{ +} + +SBFileSpec::~SBFileSpec () +{ +} + +const SBFileSpec & +SBFileSpec::operator = (const SBFileSpec &rhs) +{ + if (this != &rhs) + *m_opaque_ap = *rhs.m_opaque_ap; + return *this; +} + +bool +SBFileSpec::IsValid() const +{ + return *m_opaque_ap; +} + +bool +SBFileSpec::Exists () const +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + bool result = m_opaque_ap->Exists(); + + if (log) + log->Printf ("SBFileSpec(%p)::Exists () => %s", m_opaque_ap.get(), (result ? "true" : "false")); + + return result; +} + +bool +SBFileSpec::ResolveExecutableLocation () +{ + return m_opaque_ap->ResolveExecutableLocation (); +} + +int +SBFileSpec::ResolvePath (const char *src_path, char *dst_path, size_t dst_len) +{ + return lldb_private::FileSpec::Resolve (src_path, dst_path, dst_len); +} + +const char * +SBFileSpec::GetFilename() const +{ + const char *s = m_opaque_ap->GetFilename().AsCString(); + + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (log) + { + if (s) + log->Printf ("SBFileSpec(%p)::GetFilename () => \"%s\"", m_opaque_ap.get(), s); + else + log->Printf ("SBFileSpec(%p)::GetFilename () => NULL", m_opaque_ap.get()); + } + + return s; +} + +const char * +SBFileSpec::GetDirectory() const +{ + const char *s = m_opaque_ap->GetDirectory().AsCString(); + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (log) + { + if (s) + log->Printf ("SBFileSpec(%p)::GetDirectory () => \"%s\"", m_opaque_ap.get(), s); + else + log->Printf ("SBFileSpec(%p)::GetDirectory () => NULL", m_opaque_ap.get()); + } + return s; +} + +uint32_t +SBFileSpec::GetPath (char *dst_path, size_t dst_len) const +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + uint32_t result = m_opaque_ap->GetPath (dst_path, dst_len); + + if (log) + log->Printf ("SBFileSpec(%p)::GetPath (dst_path=\"%.*s\", dst_len=%" PRIu64 ") => %u", + m_opaque_ap.get(), result, dst_path, (uint64_t)dst_len, result); + + if (result == 0 && dst_path && dst_len > 0) + *dst_path = '\0'; + return result; +} + + +const lldb_private::FileSpec * +SBFileSpec::operator->() const +{ + return m_opaque_ap.get(); +} + +const lldb_private::FileSpec * +SBFileSpec::get() const +{ + return m_opaque_ap.get(); +} + + +const lldb_private::FileSpec & +SBFileSpec::operator*() const +{ + return *m_opaque_ap.get(); +} + +const lldb_private::FileSpec & +SBFileSpec::ref() const +{ + return *m_opaque_ap.get(); +} + + +void +SBFileSpec::SetFileSpec (const lldb_private::FileSpec& fs) +{ + *m_opaque_ap = fs; +} + +bool +SBFileSpec::GetDescription (SBStream &description) const +{ + Stream &strm = description.ref(); + char path[PATH_MAX]; + if (m_opaque_ap->GetPath(path, sizeof(path))) + strm.PutCString (path); + return true; +} diff --git a/contrib/llvm/tools/lldb/source/API/SBFileSpecList.cpp b/contrib/llvm/tools/lldb/source/API/SBFileSpecList.cpp new file mode 100644 index 00000000000..3ebf3cc80a2 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/API/SBFileSpecList.cpp @@ -0,0 +1,142 @@ +//===-- SBFileSpecListList.cpp ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include + +#include "lldb/API/SBFileSpec.h" +#include "lldb/API/SBFileSpecList.h" +#include "lldb/API/SBStream.h" +#include "lldb/Core/FileSpecList.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Stream.h" +#include "lldb/Host/FileSpec.h" + +using namespace lldb; +using namespace lldb_private; + + + +SBFileSpecList::SBFileSpecList () : + m_opaque_ap(new FileSpecList()) +{ +} + +SBFileSpecList::SBFileSpecList (const SBFileSpecList &rhs) : + m_opaque_ap() +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + if (rhs.m_opaque_ap.get()) + m_opaque_ap.reset (new FileSpecList (*(rhs.get()))); + + if (log) + { + log->Printf ("SBFileSpecList::SBFileSpecList (const SBFileSpecList rhs.ap=%p) => SBFileSpecList(%p)", + rhs.m_opaque_ap.get(), m_opaque_ap.get()); + } +} + +SBFileSpecList::~SBFileSpecList () +{ +} + +const SBFileSpecList & +SBFileSpecList::operator = (const SBFileSpecList &rhs) +{ + if (this != &rhs) + { + m_opaque_ap.reset (new lldb_private::FileSpecList(*(rhs.get()))); + } + return *this; +} + +uint32_t +SBFileSpecList::GetSize () const +{ + return m_opaque_ap->GetSize(); +} + +void +SBFileSpecList::Append (const SBFileSpec &sb_file) +{ + m_opaque_ap->Append (sb_file.ref()); +} + +bool +SBFileSpecList::AppendIfUnique (const SBFileSpec &sb_file) +{ + return m_opaque_ap->AppendIfUnique (sb_file.ref()); +} + +void +SBFileSpecList::Clear() +{ + m_opaque_ap->Clear(); +} + +uint32_t +SBFileSpecList::FindFileIndex (uint32_t idx, const SBFileSpec &sb_file, bool full) +{ + return m_opaque_ap->FindFileIndex (idx, sb_file.ref(), full); +} + +const SBFileSpec +SBFileSpecList::GetFileSpecAtIndex (uint32_t idx) const +{ + SBFileSpec new_spec; + new_spec.SetFileSpec(m_opaque_ap->GetFileSpecAtIndex(idx)); + return new_spec; +} + +const lldb_private::FileSpecList * +SBFileSpecList::operator->() const +{ + return m_opaque_ap.get(); +} + +const lldb_private::FileSpecList * +SBFileSpecList::get() const +{ + return m_opaque_ap.get(); +} + + +const lldb_private::FileSpecList & +SBFileSpecList::operator*() const +{ + return *m_opaque_ap.get(); +} + +const lldb_private::FileSpecList & +SBFileSpecList::ref() const +{ + return *m_opaque_ap.get(); +} + +bool +SBFileSpecList::GetDescription (SBStream &description) const +{ + Stream &strm = description.ref(); + + if (m_opaque_ap.get()) + { + uint32_t num_files = m_opaque_ap->GetSize(); + strm.Printf ("%d files: ", num_files); + for (uint32_t i = 0; i < num_files; i++) + { + char path[PATH_MAX]; + if (m_opaque_ap->GetFileSpecAtIndex(i).GetPath(path, sizeof(path))) + strm.Printf ("\n %s", path); + } + } + else + strm.PutCString ("No value"); + + return true; +} diff --git a/contrib/llvm/tools/lldb/source/API/SBFrame.cpp b/contrib/llvm/tools/lldb/source/API/SBFrame.cpp new file mode 100644 index 00000000000..1a1a63bd067 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/API/SBFrame.cpp @@ -0,0 +1,1526 @@ +//===-- SBFrame.cpp ---------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/API/SBFrame.h" + +#include +#include + +#include "lldb/lldb-types.h" + +#include "lldb/Core/Address.h" +#include "lldb/Core/ConstString.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Stream.h" +#include "lldb/Core/StreamFile.h" +#include "lldb/Core/ValueObjectRegister.h" +#include "lldb/Core/ValueObjectVariable.h" +#include "lldb/Expression/ClangUserExpression.h" +#include "lldb/Host/Host.h" +#include "lldb/Symbol/Block.h" +#include "lldb/Symbol/Function.h" +#include "lldb/Symbol/Symbol.h" +#include "lldb/Symbol/SymbolContext.h" +#include "lldb/Symbol/VariableList.h" +#include "lldb/Symbol/Variable.h" +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/StackFrame.h" +#include "lldb/Target/StackID.h" +#include "lldb/Target/Thread.h" + +#include "lldb/API/SBDebugger.h" +#include "lldb/API/SBValue.h" +#include "lldb/API/SBAddress.h" +#include "lldb/API/SBExpressionOptions.h" +#include "lldb/API/SBStream.h" +#include "lldb/API/SBSymbolContext.h" +#include "lldb/API/SBThread.h" + +using namespace lldb; +using namespace lldb_private; + + +SBFrame::SBFrame () : + m_opaque_sp (new ExecutionContextRef()) +{ +} + +SBFrame::SBFrame (const StackFrameSP &lldb_object_sp) : + m_opaque_sp (new ExecutionContextRef (lldb_object_sp)) +{ + Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + if (log) + { + SBStream sstr; + GetDescription (sstr); + log->Printf ("SBFrame::SBFrame (sp=%p) => SBFrame(%p): %s", + lldb_object_sp.get(), lldb_object_sp.get(), sstr.GetData()); + + } +} + +SBFrame::SBFrame(const SBFrame &rhs) : + m_opaque_sp (new ExecutionContextRef (*rhs.m_opaque_sp)) +{ +} + +const SBFrame & +SBFrame::operator = (const SBFrame &rhs) +{ + if (this != &rhs) + *m_opaque_sp = *rhs.m_opaque_sp; + return *this; +} + +SBFrame::~SBFrame() +{ +} + +StackFrameSP +SBFrame::GetFrameSP() const +{ + if (m_opaque_sp) + return m_opaque_sp->GetFrameSP(); + return StackFrameSP(); +} + +void +SBFrame::SetFrameSP (const StackFrameSP &lldb_object_sp) +{ + return m_opaque_sp->SetFrameSP(lldb_object_sp); +} + +bool +SBFrame::IsValid() const +{ + return GetFrameSP().get() != NULL; +} + +SBSymbolContext +SBFrame::GetSymbolContext (uint32_t resolve_scope) const +{ + Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + SBSymbolContext sb_sym_ctx; + Mutex::Locker api_locker; + ExecutionContext exe_ctx (m_opaque_sp.get(), api_locker); + + StackFrame *frame = NULL; + Target *target = exe_ctx.GetTargetPtr(); + Process *process = exe_ctx.GetProcessPtr(); + if (target && process) + { + Process::StopLocker stop_locker; + if (stop_locker.TryLock(&process->GetRunLock())) + { + frame = exe_ctx.GetFramePtr(); + if (frame) + { + sb_sym_ctx.SetSymbolContext(&frame->GetSymbolContext (resolve_scope)); + } + else + { + if (log) + log->Printf ("SBFrame::GetVariables () => error: could not reconstruct frame object for this SBFrame."); + } + } + else + { + if (log) + log->Printf ("SBFrame::GetSymbolContext () => error: process is running"); + } + } + + if (log) + log->Printf ("SBFrame(%p)::GetSymbolContext (resolve_scope=0x%8.8x) => SBSymbolContext(%p)", + frame, resolve_scope, sb_sym_ctx.get()); + + return sb_sym_ctx; +} + +SBModule +SBFrame::GetModule () const +{ + Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + SBModule sb_module; + ModuleSP module_sp; + Mutex::Locker api_locker; + ExecutionContext exe_ctx (m_opaque_sp.get(), api_locker); + + StackFrame *frame = NULL; + Target *target = exe_ctx.GetTargetPtr(); + Process *process = exe_ctx.GetProcessPtr(); + if (target && process) + { + Process::StopLocker stop_locker; + if (stop_locker.TryLock(&process->GetRunLock())) + { + frame = exe_ctx.GetFramePtr(); + if (frame) + { + module_sp = frame->GetSymbolContext (eSymbolContextModule).module_sp; + sb_module.SetSP (module_sp); + } + else + { + if (log) + log->Printf ("SBFrame::GetModule () => error: could not reconstruct frame object for this SBFrame."); + } + } + else + { + if (log) + log->Printf ("SBFrame::GetModule () => error: process is running"); + } + } + + if (log) + log->Printf ("SBFrame(%p)::GetModule () => SBModule(%p)", + frame, module_sp.get()); + + return sb_module; +} + +SBCompileUnit +SBFrame::GetCompileUnit () const +{ + Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + SBCompileUnit sb_comp_unit; + Mutex::Locker api_locker; + ExecutionContext exe_ctx (m_opaque_sp.get(), api_locker); + + StackFrame *frame = NULL; + Target *target = exe_ctx.GetTargetPtr(); + Process *process = exe_ctx.GetProcessPtr(); + if (target && process) + { + Process::StopLocker stop_locker; + if (stop_locker.TryLock(&process->GetRunLock())) + { + frame = exe_ctx.GetFramePtr(); + if (frame) + { + sb_comp_unit.reset (frame->GetSymbolContext (eSymbolContextCompUnit).comp_unit); + } + else + { + if (log) + log->Printf ("SBFrame::GetCompileUnit () => error: could not reconstruct frame object for this SBFrame."); + } + } + else + { + if (log) + log->Printf ("SBFrame::GetCompileUnit () => error: process is running"); + } + } + if (log) + log->Printf ("SBFrame(%p)::GetCompileUnit () => SBCompileUnit(%p)", + frame, sb_comp_unit.get()); + + return sb_comp_unit; +} + +SBFunction +SBFrame::GetFunction () const +{ + Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + SBFunction sb_function; + Mutex::Locker api_locker; + ExecutionContext exe_ctx (m_opaque_sp.get(), api_locker); + + StackFrame *frame = NULL; + Target *target = exe_ctx.GetTargetPtr(); + Process *process = exe_ctx.GetProcessPtr(); + if (target && process) + { + Process::StopLocker stop_locker; + if (stop_locker.TryLock(&process->GetRunLock())) + { + frame = exe_ctx.GetFramePtr(); + if (frame) + { + sb_function.reset(frame->GetSymbolContext (eSymbolContextFunction).function); + } + else + { + if (log) + log->Printf ("SBFrame::GetFunction () => error: could not reconstruct frame object for this SBFrame."); + } + } + else + { + if (log) + log->Printf ("SBFrame::GetFunction () => error: process is running"); + } + } + if (log) + log->Printf ("SBFrame(%p)::GetFunction () => SBFunction(%p)", + frame, sb_function.get()); + + return sb_function; +} + +SBSymbol +SBFrame::GetSymbol () const +{ + Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + SBSymbol sb_symbol; + Mutex::Locker api_locker; + ExecutionContext exe_ctx (m_opaque_sp.get(), api_locker); + + StackFrame *frame = NULL; + Target *target = exe_ctx.GetTargetPtr(); + Process *process = exe_ctx.GetProcessPtr(); + if (target && process) + { + Process::StopLocker stop_locker; + if (stop_locker.TryLock(&process->GetRunLock())) + { + frame = exe_ctx.GetFramePtr(); + if (frame) + { + sb_symbol.reset(frame->GetSymbolContext (eSymbolContextSymbol).symbol); + } + else + { + if (log) + log->Printf ("SBFrame::GetSymbol () => error: could not reconstruct frame object for this SBFrame."); + } + } + else + { + if (log) + log->Printf ("SBFrame::GetSymbol () => error: process is running"); + } + } + if (log) + log->Printf ("SBFrame(%p)::GetSymbol () => SBSymbol(%p)", + frame, sb_symbol.get()); + return sb_symbol; +} + +SBBlock +SBFrame::GetBlock () const +{ + Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + SBBlock sb_block; + Mutex::Locker api_locker; + ExecutionContext exe_ctx (m_opaque_sp.get(), api_locker); + + StackFrame *frame = NULL; + Target *target = exe_ctx.GetTargetPtr(); + Process *process = exe_ctx.GetProcessPtr(); + if (target && process) + { + Process::StopLocker stop_locker; + if (stop_locker.TryLock(&process->GetRunLock())) + { + frame = exe_ctx.GetFramePtr(); + if (frame) + { + sb_block.SetPtr (frame->GetSymbolContext (eSymbolContextBlock).block); + } + else + { + if (log) + log->Printf ("SBFrame::GetBlock () => error: could not reconstruct frame object for this SBFrame."); + } + } + else + { + if (log) + log->Printf ("SBFrame(%p)::GetBlock () => error: process is running", frame); + } + } + if (log) + log->Printf ("SBFrame(%p)::GetBlock () => SBBlock(%p)", + frame, sb_block.GetPtr()); + return sb_block; +} + +SBBlock +SBFrame::GetFrameBlock () const +{ + SBBlock sb_block; + Mutex::Locker api_locker; + ExecutionContext exe_ctx (m_opaque_sp.get(), api_locker); + + StackFrame *frame = NULL; + Target *target = exe_ctx.GetTargetPtr(); + Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + Process *process = exe_ctx.GetProcessPtr(); + if (target && process) + { + Process::StopLocker stop_locker; + if (stop_locker.TryLock(&process->GetRunLock())) + { + frame = exe_ctx.GetFramePtr(); + if (frame) + { + sb_block.SetPtr(frame->GetFrameBlock ()); + } + else + { + if (log) + log->Printf ("SBFrame::GetFrameBlock () => error: could not reconstruct frame object for this SBFrame."); + } + } + else + { + if (log) + log->Printf ("SBFrame::GetFrameBlock () => error: process is running"); + } + } + if (log) + log->Printf ("SBFrame(%p)::GetFrameBlock () => SBBlock(%p)", + frame, sb_block.GetPtr()); + return sb_block; +} + +SBLineEntry +SBFrame::GetLineEntry () const +{ + Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + SBLineEntry sb_line_entry; + Mutex::Locker api_locker; + ExecutionContext exe_ctx (m_opaque_sp.get(), api_locker); + + StackFrame *frame = NULL; + Target *target = exe_ctx.GetTargetPtr(); + Process *process = exe_ctx.GetProcessPtr(); + if (target && process) + { + Process::StopLocker stop_locker; + if (stop_locker.TryLock(&process->GetRunLock())) + { + frame = exe_ctx.GetFramePtr(); + if (frame) + { + sb_line_entry.SetLineEntry (frame->GetSymbolContext (eSymbolContextLineEntry).line_entry); + } + else + { + if (log) + log->Printf ("SBFrame::GetLineEntry () => error: could not reconstruct frame object for this SBFrame."); + } + } + else + { + if (log) + log->Printf ("SBFrame::GetLineEntry () => error: process is running"); + } + } + if (log) + log->Printf ("SBFrame(%p)::GetLineEntry () => SBLineEntry(%p)", + frame, sb_line_entry.get()); + return sb_line_entry; +} + +uint32_t +SBFrame::GetFrameID () const +{ + uint32_t frame_idx = UINT32_MAX; + + ExecutionContext exe_ctx(m_opaque_sp.get()); + StackFrame *frame = exe_ctx.GetFramePtr(); + if (frame) + frame_idx = frame->GetFrameIndex (); + + Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (log) + log->Printf ("SBFrame(%p)::GetFrameID () => %u", + frame, frame_idx); + return frame_idx; +} + +addr_t +SBFrame::GetPC () const +{ + Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + addr_t addr = LLDB_INVALID_ADDRESS; + Mutex::Locker api_locker; + ExecutionContext exe_ctx (m_opaque_sp.get(), api_locker); + + StackFrame *frame = NULL; + Target *target = exe_ctx.GetTargetPtr(); + Process *process = exe_ctx.GetProcessPtr(); + if (target && process) + { + Process::StopLocker stop_locker; + if (stop_locker.TryLock(&process->GetRunLock())) + { + frame = exe_ctx.GetFramePtr(); + if (frame) + { + addr = frame->GetFrameCodeAddress().GetOpcodeLoadAddress (target); + } + else + { + if (log) + log->Printf ("SBFrame::GetPC () => error: could not reconstruct frame object for this SBFrame."); + } + } + else + { + if (log) + log->Printf ("SBFrame::GetPC () => error: process is running"); + } + } + + if (log) + log->Printf ("SBFrame(%p)::GetPC () => 0x%" PRIx64, frame, addr); + + return addr; +} + +bool +SBFrame::SetPC (addr_t new_pc) +{ + Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + bool ret_val = false; + Mutex::Locker api_locker; + ExecutionContext exe_ctx (m_opaque_sp.get(), api_locker); + + StackFrame *frame = NULL; + Target *target = exe_ctx.GetTargetPtr(); + Process *process = exe_ctx.GetProcessPtr(); + if (target && process) + { + Process::StopLocker stop_locker; + if (stop_locker.TryLock(&process->GetRunLock())) + { + frame = exe_ctx.GetFramePtr(); + if (frame) + { + ret_val = frame->GetRegisterContext()->SetPC (new_pc); + } + else + { + if (log) + log->Printf ("SBFrame::SetPC () => error: could not reconstruct frame object for this SBFrame."); + } + } + else + { + if (log) + log->Printf ("SBFrame::SetPC () => error: process is running"); + } + } + + if (log) + log->Printf ("SBFrame(%p)::SetPC (new_pc=0x%" PRIx64 ") => %i", + frame, new_pc, ret_val); + + return ret_val; +} + +addr_t +SBFrame::GetSP () const +{ + Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + addr_t addr = LLDB_INVALID_ADDRESS; + Mutex::Locker api_locker; + ExecutionContext exe_ctx (m_opaque_sp.get(), api_locker); + + StackFrame *frame = NULL; + Target *target = exe_ctx.GetTargetPtr(); + Process *process = exe_ctx.GetProcessPtr(); + if (target && process) + { + Process::StopLocker stop_locker; + if (stop_locker.TryLock(&process->GetRunLock())) + { + frame = exe_ctx.GetFramePtr(); + if (frame) + { + addr = frame->GetRegisterContext()->GetSP(); + } + else + { + if (log) + log->Printf ("SBFrame::GetSP () => error: could not reconstruct frame object for this SBFrame."); + } + } + else + { + if (log) + log->Printf ("SBFrame::GetSP () => error: process is running"); + } + } + if (log) + log->Printf ("SBFrame(%p)::GetSP () => 0x%" PRIx64, frame, addr); + + return addr; +} + + +addr_t +SBFrame::GetFP () const +{ + Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + addr_t addr = LLDB_INVALID_ADDRESS; + Mutex::Locker api_locker; + ExecutionContext exe_ctx (m_opaque_sp.get(), api_locker); + + StackFrame *frame = NULL; + Target *target = exe_ctx.GetTargetPtr(); + Process *process = exe_ctx.GetProcessPtr(); + if (target && process) + { + Process::StopLocker stop_locker; + if (stop_locker.TryLock(&process->GetRunLock())) + { + frame = exe_ctx.GetFramePtr(); + if (frame) + { + addr = frame->GetRegisterContext()->GetFP(); + } + else + { + if (log) + log->Printf ("SBFrame::GetFP () => error: could not reconstruct frame object for this SBFrame."); + } + } + else + { + if (log) + log->Printf ("SBFrame::GetFP () => error: process is running"); + } + } + + if (log) + log->Printf ("SBFrame(%p)::GetFP () => 0x%" PRIx64, frame, addr); + return addr; +} + + +SBAddress +SBFrame::GetPCAddress () const +{ + Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + SBAddress sb_addr; + Mutex::Locker api_locker; + ExecutionContext exe_ctx (m_opaque_sp.get(), api_locker); + + StackFrame *frame = exe_ctx.GetFramePtr(); + Target *target = exe_ctx.GetTargetPtr(); + Process *process = exe_ctx.GetProcessPtr(); + if (target && process) + { + Process::StopLocker stop_locker; + if (stop_locker.TryLock(&process->GetRunLock())) + { + frame = exe_ctx.GetFramePtr(); + if (frame) + { + sb_addr.SetAddress (&frame->GetFrameCodeAddress()); + } + else + { + if (log) + log->Printf ("SBFrame::GetPCAddress () => error: could not reconstruct frame object for this SBFrame."); + } + } + else + { + if (log) + log->Printf ("SBFrame::GetPCAddress () => error: process is running"); + } + } + if (log) + log->Printf ("SBFrame(%p)::GetPCAddress () => SBAddress(%p)", frame, sb_addr.get()); + return sb_addr; +} + +void +SBFrame::Clear() +{ + m_opaque_sp->Clear(); +} + +lldb::SBValue +SBFrame::GetValueForVariablePath (const char *var_path) +{ + SBValue sb_value; + ExecutionContext exe_ctx(m_opaque_sp.get()); + StackFrame *frame = exe_ctx.GetFramePtr(); + Target *target = exe_ctx.GetTargetPtr(); + if (frame && target) + { + lldb::DynamicValueType use_dynamic = frame->CalculateTarget()->GetPreferDynamicValue(); + sb_value = GetValueForVariablePath (var_path, use_dynamic); + } + return sb_value; +} + +lldb::SBValue +SBFrame::GetValueForVariablePath (const char *var_path, DynamicValueType use_dynamic) +{ + SBValue sb_value; + Mutex::Locker api_locker; + Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (var_path == NULL || var_path[0] == '\0') + { + if (log) + log->Printf ("SBFrame::GetValueForVariablePath called with empty variable path."); + return sb_value; + } + + ExecutionContext exe_ctx (m_opaque_sp.get(), api_locker); + + StackFrame *frame = NULL; + Target *target = exe_ctx.GetTargetPtr(); + Process *process = exe_ctx.GetProcessPtr(); + if (target && process) + { + Process::StopLocker stop_locker; + if (stop_locker.TryLock(&process->GetRunLock())) + { + frame = exe_ctx.GetFramePtr(); + if (frame) + { + VariableSP var_sp; + Error error; + ValueObjectSP value_sp (frame->GetValueForVariableExpressionPath (var_path, + eNoDynamicValues, + StackFrame::eExpressionPathOptionCheckPtrVsMember | StackFrame::eExpressionPathOptionsAllowDirectIVarAccess, + var_sp, + error)); + sb_value.SetSP(value_sp, use_dynamic); + } + else + { + if (log) + log->Printf ("SBFrame::GetValueForVariablePath () => error: could not reconstruct frame object for this SBFrame."); + } + } + else + { + if (log) + log->Printf ("SBFrame::GetValueForVariablePath () => error: process is running"); + } + } + return sb_value; +} + +SBValue +SBFrame::FindVariable (const char *name) +{ + SBValue value; + ExecutionContext exe_ctx(m_opaque_sp.get()); + StackFrame *frame = exe_ctx.GetFramePtr(); + Target *target = exe_ctx.GetTargetPtr(); + if (frame && target) + { + lldb::DynamicValueType use_dynamic = frame->CalculateTarget()->GetPreferDynamicValue(); + value = FindVariable (name, use_dynamic); + } + return value; +} + + +SBValue +SBFrame::FindVariable (const char *name, lldb::DynamicValueType use_dynamic) +{ + Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + VariableSP var_sp; + SBValue sb_value; + + if (name == NULL || name[0] == '\0') + { + if (log) + log->Printf ("SBFrame::FindVariable called with empty name"); + return sb_value; + } + + ValueObjectSP value_sp; + Mutex::Locker api_locker; + ExecutionContext exe_ctx (m_opaque_sp.get(), api_locker); + + StackFrame *frame = NULL; + Target *target = exe_ctx.GetTargetPtr(); + Process *process = exe_ctx.GetProcessPtr(); + if (target && process) + { + Process::StopLocker stop_locker; + if (stop_locker.TryLock(&process->GetRunLock())) + { + frame = exe_ctx.GetFramePtr(); + if (frame) + { + VariableList variable_list; + SymbolContext sc (frame->GetSymbolContext (eSymbolContextBlock)); + + if (sc.block) + { + const bool can_create = true; + const bool get_parent_variables = true; + const bool stop_if_block_is_inlined_function = true; + + if (sc.block->AppendVariables (can_create, + get_parent_variables, + stop_if_block_is_inlined_function, + &variable_list)) + { + var_sp = variable_list.FindVariable (ConstString(name)); + } + } + + if (var_sp) + { + value_sp = frame->GetValueObjectForFrameVariable(var_sp, eNoDynamicValues); + sb_value.SetSP(value_sp, use_dynamic); + } + } + else + { + if (log) + log->Printf ("SBFrame::FindVariable () => error: could not reconstruct frame object for this SBFrame."); + } + } + else + { + if (log) + log->Printf ("SBFrame::FindVariable () => error: process is running"); + } + } + + if (log) + log->Printf ("SBFrame(%p)::FindVariable (name=\"%s\") => SBValue(%p)", + frame, name, value_sp.get()); + + return sb_value; +} + +SBValue +SBFrame::FindValue (const char *name, ValueType value_type) +{ + SBValue value; + ExecutionContext exe_ctx(m_opaque_sp.get()); + StackFrame *frame = exe_ctx.GetFramePtr(); + Target *target = exe_ctx.GetTargetPtr(); + if (frame && target) + { + lldb::DynamicValueType use_dynamic = frame->CalculateTarget()->GetPreferDynamicValue(); + value = FindValue (name, value_type, use_dynamic); + } + return value; +} + +SBValue +SBFrame::FindValue (const char *name, ValueType value_type, lldb::DynamicValueType use_dynamic) +{ + Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + SBValue sb_value; + + if (name == NULL || name[0] == '\0') + { + if (log) + log->Printf ("SBFrame::FindValue called with empty name."); + return sb_value; + } + + ValueObjectSP value_sp; + Mutex::Locker api_locker; + ExecutionContext exe_ctx (m_opaque_sp.get(), api_locker); + + StackFrame *frame = NULL; + Target *target = exe_ctx.GetTargetPtr(); + Process *process = exe_ctx.GetProcessPtr(); + if (target && process) + { + Process::StopLocker stop_locker; + if (stop_locker.TryLock(&process->GetRunLock())) + { + frame = exe_ctx.GetFramePtr(); + if (frame) + { + switch (value_type) + { + case eValueTypeVariableGlobal: // global variable + case eValueTypeVariableStatic: // static variable + case eValueTypeVariableArgument: // function argument variables + case eValueTypeVariableLocal: // function local variables + { + VariableList *variable_list = frame->GetVariableList(true); + + SymbolContext sc (frame->GetSymbolContext (eSymbolContextBlock)); + + const bool can_create = true; + const bool get_parent_variables = true; + const bool stop_if_block_is_inlined_function = true; + + if (sc.block && sc.block->AppendVariables (can_create, + get_parent_variables, + stop_if_block_is_inlined_function, + variable_list)) + { + ConstString const_name(name); + const uint32_t num_variables = variable_list->GetSize(); + for (uint32_t i = 0; i < num_variables; ++i) + { + VariableSP variable_sp (variable_list->GetVariableAtIndex(i)); + if (variable_sp && + variable_sp->GetScope() == value_type && + variable_sp->GetName() == const_name) + { + value_sp = frame->GetValueObjectForFrameVariable (variable_sp, eNoDynamicValues); + sb_value.SetSP (value_sp, use_dynamic); + break; + } + } + } + } + break; + + case eValueTypeRegister: // stack frame register value + { + RegisterContextSP reg_ctx (frame->GetRegisterContext()); + if (reg_ctx) + { + const uint32_t num_regs = reg_ctx->GetRegisterCount(); + for (uint32_t reg_idx = 0; reg_idx < num_regs; ++reg_idx) + { + const RegisterInfo *reg_info = reg_ctx->GetRegisterInfoAtIndex (reg_idx); + if (reg_info && + ((reg_info->name && strcasecmp (reg_info->name, name) == 0) || + (reg_info->alt_name && strcasecmp (reg_info->alt_name, name) == 0))) + { + value_sp = ValueObjectRegister::Create (frame, reg_ctx, reg_idx); + sb_value.SetSP (value_sp); + break; + } + } + } + } + break; + + case eValueTypeRegisterSet: // A collection of stack frame register values + { + RegisterContextSP reg_ctx (frame->GetRegisterContext()); + if (reg_ctx) + { + const uint32_t num_sets = reg_ctx->GetRegisterSetCount(); + for (uint32_t set_idx = 0; set_idx < num_sets; ++set_idx) + { + const RegisterSet *reg_set = reg_ctx->GetRegisterSet (set_idx); + if (reg_set && + ((reg_set->name && strcasecmp (reg_set->name, name) == 0) || + (reg_set->short_name && strcasecmp (reg_set->short_name, name) == 0))) + { + value_sp = ValueObjectRegisterSet::Create (frame, reg_ctx, set_idx); + sb_value.SetSP (value_sp); + break; + } + } + } + } + break; + + case eValueTypeConstResult: // constant result variables + { + ConstString const_name(name); + ClangExpressionVariableSP expr_var_sp (target->GetPersistentVariables().GetVariable (const_name)); + if (expr_var_sp) + { + value_sp = expr_var_sp->GetValueObject(); + sb_value.SetSP (value_sp, use_dynamic); + } + } + break; + + default: + break; + } + } + else + { + if (log) + log->Printf ("SBFrame::FindValue () => error: could not reconstruct frame object for this SBFrame."); + } + } + else + { + if (log) + log->Printf ("SBFrame::FindValue () => error: process is running"); + } + } + + if (log) + log->Printf ("SBFrame(%p)::FindVariableInScope (name=\"%s\", value_type=%i) => SBValue(%p)", + frame, name, value_type, value_sp.get()); + + + return sb_value; +} + +bool +SBFrame::IsEqual (const SBFrame &that) const +{ + lldb::StackFrameSP this_sp = GetFrameSP(); + lldb::StackFrameSP that_sp = that.GetFrameSP(); + return (this_sp && that_sp && this_sp->GetStackID() == that_sp->GetStackID()); +} + +bool +SBFrame::operator == (const SBFrame &rhs) const +{ + return IsEqual(rhs); +} + +bool +SBFrame::operator != (const SBFrame &rhs) const +{ + return !IsEqual(rhs); +} + +SBThread +SBFrame::GetThread () const +{ + Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + ExecutionContext exe_ctx(m_opaque_sp.get()); + ThreadSP thread_sp (exe_ctx.GetThreadSP()); + SBThread sb_thread (thread_sp); + + if (log) + { + SBStream sstr; + sb_thread.GetDescription (sstr); + log->Printf ("SBFrame(%p)::GetThread () => SBThread(%p): %s", + exe_ctx.GetFramePtr(), + thread_sp.get(), + sstr.GetData()); + } + + return sb_thread; +} + +const char * +SBFrame::Disassemble () const +{ + Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + const char *disassembly = NULL; + Mutex::Locker api_locker; + ExecutionContext exe_ctx (m_opaque_sp.get(), api_locker); + + StackFrame *frame = NULL; + Target *target = exe_ctx.GetTargetPtr(); + Process *process = exe_ctx.GetProcessPtr(); + if (target && process) + { + Process::StopLocker stop_locker; + if (stop_locker.TryLock(&process->GetRunLock())) + { + frame = exe_ctx.GetFramePtr(); + if (frame) + { + disassembly = frame->Disassemble(); + } + else + { + if (log) + log->Printf ("SBFrame::Disassemble () => error: could not reconstruct frame object for this SBFrame."); + } + } + else + { + if (log) + log->Printf ("SBFrame::Disassemble () => error: process is running"); + } + } + + if (log) + log->Printf ("SBFrame(%p)::Disassemble () => %s", frame, disassembly); + + return disassembly; +} + + +SBValueList +SBFrame::GetVariables (bool arguments, + bool locals, + bool statics, + bool in_scope_only) +{ + SBValueList value_list; + ExecutionContext exe_ctx(m_opaque_sp.get()); + StackFrame *frame = exe_ctx.GetFramePtr(); + Target *target = exe_ctx.GetTargetPtr(); + if (frame && target) + { + lldb::DynamicValueType use_dynamic = frame->CalculateTarget()->GetPreferDynamicValue(); + value_list = GetVariables (arguments, locals, statics, in_scope_only, use_dynamic); + } + return value_list; +} + +SBValueList +SBFrame::GetVariables (bool arguments, + bool locals, + bool statics, + bool in_scope_only, + lldb::DynamicValueType use_dynamic) +{ + Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + SBValueList value_list; + Mutex::Locker api_locker; + ExecutionContext exe_ctx (m_opaque_sp.get(), api_locker); + + StackFrame *frame = NULL; + Target *target = exe_ctx.GetTargetPtr(); + + if (log) + log->Printf ("SBFrame::GetVariables (arguments=%i, locals=%i, statics=%i, in_scope_only=%i)", + arguments, + locals, + statics, + in_scope_only); + + Process *process = exe_ctx.GetProcessPtr(); + if (target && process) + { + Process::StopLocker stop_locker; + if (stop_locker.TryLock(&process->GetRunLock())) + { + frame = exe_ctx.GetFramePtr(); + if (frame) + { + size_t i; + VariableList *variable_list = NULL; + variable_list = frame->GetVariableList(true); + if (variable_list) + { + const size_t num_variables = variable_list->GetSize(); + if (num_variables) + { + for (i = 0; i < num_variables; ++i) + { + VariableSP variable_sp (variable_list->GetVariableAtIndex(i)); + if (variable_sp) + { + bool add_variable = false; + switch (variable_sp->GetScope()) + { + case eValueTypeVariableGlobal: + case eValueTypeVariableStatic: + add_variable = statics; + break; + + case eValueTypeVariableArgument: + add_variable = arguments; + break; + + case eValueTypeVariableLocal: + add_variable = locals; + break; + + default: + break; + } + if (add_variable) + { + if (in_scope_only && !variable_sp->IsInScope(frame)) + continue; + + ValueObjectSP valobj_sp(frame->GetValueObjectForFrameVariable (variable_sp, eNoDynamicValues)); + SBValue value_sb; + value_sb.SetSP(valobj_sp,use_dynamic); + value_list.Append(value_sb); + } + } + } + } + } + } + else + { + if (log) + log->Printf ("SBFrame::GetVariables () => error: could not reconstruct frame object for this SBFrame."); + } + } + else + { + if (log) + log->Printf ("SBFrame::GetVariables () => error: process is running"); + } + } + + if (log) + { + log->Printf ("SBFrame(%p)::GetVariables (...) => SBValueList(%p)", frame, value_list.opaque_ptr()); + } + + return value_list; +} + +SBValueList +SBFrame::GetRegisters () +{ + Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + SBValueList value_list; + Mutex::Locker api_locker; + ExecutionContext exe_ctx (m_opaque_sp.get(), api_locker); + + StackFrame *frame = NULL; + Target *target = exe_ctx.GetTargetPtr(); + Process *process = exe_ctx.GetProcessPtr(); + if (target && process) + { + Process::StopLocker stop_locker; + if (stop_locker.TryLock(&process->GetRunLock())) + { + frame = exe_ctx.GetFramePtr(); + if (frame) + { + RegisterContextSP reg_ctx (frame->GetRegisterContext()); + if (reg_ctx) + { + const uint32_t num_sets = reg_ctx->GetRegisterSetCount(); + for (uint32_t set_idx = 0; set_idx < num_sets; ++set_idx) + { + value_list.Append(ValueObjectRegisterSet::Create (frame, reg_ctx, set_idx)); + } + } + } + else + { + if (log) + log->Printf ("SBFrame::GetRegisters () => error: could not reconstruct frame object for this SBFrame."); + } + } + else + { + if (log) + log->Printf ("SBFrame::GetRegisters () => error: process is running"); + } + } + + if (log) + log->Printf ("SBFrame(%p)::GetRegisters () => SBValueList(%p)", frame, value_list.opaque_ptr()); + + return value_list; +} + +SBValue +SBFrame::FindRegister (const char *name) +{ + Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + SBValue result; + ValueObjectSP value_sp; + Mutex::Locker api_locker; + ExecutionContext exe_ctx (m_opaque_sp.get(), api_locker); + + StackFrame *frame = NULL; + Target *target = exe_ctx.GetTargetPtr(); + Process *process = exe_ctx.GetProcessPtr(); + if (target && process) + { + Process::StopLocker stop_locker; + if (stop_locker.TryLock(&process->GetRunLock())) + { + frame = exe_ctx.GetFramePtr(); + if (frame) + { + RegisterContextSP reg_ctx (frame->GetRegisterContext()); + if (reg_ctx) + { + const uint32_t num_regs = reg_ctx->GetRegisterCount(); + for (uint32_t reg_idx = 0; reg_idx < num_regs; ++reg_idx) + { + const RegisterInfo *reg_info = reg_ctx->GetRegisterInfoAtIndex (reg_idx); + if (reg_info && + ((reg_info->name && strcasecmp (reg_info->name, name) == 0) || + (reg_info->alt_name && strcasecmp (reg_info->alt_name, name) == 0))) + { + value_sp = ValueObjectRegister::Create (frame, reg_ctx, reg_idx); + result.SetSP (value_sp); + break; + } + } + } + } + else + { + if (log) + log->Printf ("SBFrame::FindRegister () => error: could not reconstruct frame object for this SBFrame."); + } + } + else + { + if (log) + log->Printf ("SBFrame::FindRegister () => error: process is running"); + } + } + + if (log) + log->Printf ("SBFrame(%p)::FindRegister () => SBValue(%p)", frame, value_sp.get()); + + return result; +} + +bool +SBFrame::GetDescription (SBStream &description) +{ + Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + Stream &strm = description.ref(); + + Mutex::Locker api_locker; + ExecutionContext exe_ctx (m_opaque_sp.get(), api_locker); + + StackFrame *frame; + Target *target = exe_ctx.GetTargetPtr(); + Process *process = exe_ctx.GetProcessPtr(); + if (target && process) + { + Process::StopLocker stop_locker; + if (stop_locker.TryLock(&process->GetRunLock())) + { + frame = exe_ctx.GetFramePtr(); + if (frame) + { + frame->DumpUsingSettingsFormat (&strm); + } + else + { + if (log) + log->Printf ("SBFrame::GetDescription () => error: could not reconstruct frame object for this SBFrame."); + } + } + else + { + if (log) + log->Printf ("SBFrame::GetDescription () => error: process is running"); + } + + } + else + strm.PutCString ("No value"); + + return true; +} + +SBValue +SBFrame::EvaluateExpression (const char *expr) +{ + SBValue result; + ExecutionContext exe_ctx(m_opaque_sp.get()); + StackFrame *frame = exe_ctx.GetFramePtr(); + Target *target = exe_ctx.GetTargetPtr(); + if (frame && target) + { + SBExpressionOptions options; + lldb::DynamicValueType fetch_dynamic_value = frame->CalculateTarget()->GetPreferDynamicValue(); + options.SetFetchDynamicValue (fetch_dynamic_value); + options.SetUnwindOnError (true); + return EvaluateExpression (expr, options); + } + return result; +} + +SBValue +SBFrame::EvaluateExpression (const char *expr, lldb::DynamicValueType fetch_dynamic_value) +{ + SBExpressionOptions options; + options.SetFetchDynamicValue (fetch_dynamic_value); + options.SetUnwindOnError (true); + return EvaluateExpression (expr, options); +} + +SBValue +SBFrame::EvaluateExpression (const char *expr, lldb::DynamicValueType fetch_dynamic_value, bool unwind_on_error) +{ + SBExpressionOptions options; + options.SetFetchDynamicValue (fetch_dynamic_value); + options.SetUnwindOnError (unwind_on_error); + return EvaluateExpression (expr, options); +} + +lldb::SBValue +SBFrame::EvaluateExpression (const char *expr, const SBExpressionOptions &options) +{ + Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + Log *expr_log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + ExecutionResults exe_results = eExecutionSetupError; + SBValue expr_result; + + if (expr == NULL || expr[0] == '\0') + { + if (log) + log->Printf ("SBFrame::EvaluateExpression called with an empty expression"); + return expr_result; + } + + ValueObjectSP expr_value_sp; + + Mutex::Locker api_locker; + ExecutionContext exe_ctx (m_opaque_sp.get(), api_locker); + + if (log) + log->Printf ("SBFrame()::EvaluateExpression (expr=\"%s\")...", expr); + + StackFrame *frame = NULL; + Target *target = exe_ctx.GetTargetPtr(); + Process *process = exe_ctx.GetProcessPtr(); + + if (target && process) + { + Process::StopLocker stop_locker; + if (stop_locker.TryLock(&process->GetRunLock())) + { + frame = exe_ctx.GetFramePtr(); + if (frame) + { +#ifdef LLDB_CONFIGURATION_DEBUG + StreamString frame_description; + frame->DumpUsingSettingsFormat (&frame_description); + Host::SetCrashDescriptionWithFormat ("SBFrame::EvaluateExpression (expr = \"%s\", fetch_dynamic_value = %u) %s", + expr, options.GetFetchDynamicValue(), frame_description.GetString().c_str()); +#endif + exe_results = target->EvaluateExpression (expr, + frame, + expr_value_sp, + options.ref()); + expr_result.SetSP(expr_value_sp, options.GetFetchDynamicValue()); +#ifdef LLDB_CONFIGURATION_DEBUG + Host::SetCrashDescription (NULL); +#endif + } + else + { + if (log) + log->Printf ("SBFrame::EvaluateExpression () => error: could not reconstruct frame object for this SBFrame."); + } + } + else + { + if (log) + log->Printf ("SBFrame::EvaluateExpression () => error: process is running"); + } + } + +#ifndef LLDB_DISABLE_PYTHON + if (expr_log) + expr_log->Printf("** [SBFrame::EvaluateExpression] Expression result is %s, summary %s **", + expr_result.GetValue(), + expr_result.GetSummary()); + + if (log) + log->Printf ("SBFrame(%p)::EvaluateExpression (expr=\"%s\") => SBValue(%p) (execution result=%d)", + frame, + expr, + expr_value_sp.get(), + exe_results); +#endif + + return expr_result; +} + +bool +SBFrame::IsInlined() +{ + Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + ExecutionContext exe_ctx(m_opaque_sp.get()); + StackFrame *frame = NULL; + Target *target = exe_ctx.GetTargetPtr(); + Process *process = exe_ctx.GetProcessPtr(); + if (target && process) + { + Process::StopLocker stop_locker; + if (stop_locker.TryLock(&process->GetRunLock())) + { + frame = exe_ctx.GetFramePtr(); + if (frame) + { + + Block *block = frame->GetSymbolContext(eSymbolContextBlock).block; + if (block) + return block->GetContainingInlinedBlock () != NULL; + } + else + { + if (log) + log->Printf ("SBFrame::IsInlined () => error: could not reconstruct frame object for this SBFrame."); + } + } + else + { + if (log) + log->Printf ("SBFrame::IsInlined () => error: process is running"); + } + + } + return false; +} + +const char * +SBFrame::GetFunctionName() +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + const char *name = NULL; + ExecutionContext exe_ctx(m_opaque_sp.get()); + StackFrame *frame = NULL; + Target *target = exe_ctx.GetTargetPtr(); + Process *process = exe_ctx.GetProcessPtr(); + if (target && process) + { + Process::StopLocker stop_locker; + if (stop_locker.TryLock(&process->GetRunLock())) + { + frame = exe_ctx.GetFramePtr(); + if (frame) + { + SymbolContext sc (frame->GetSymbolContext(eSymbolContextFunction | eSymbolContextBlock | eSymbolContextSymbol)); + if (sc.block) + { + Block *inlined_block = sc.block->GetContainingInlinedBlock (); + if (inlined_block) + { + const InlineFunctionInfo* inlined_info = inlined_block->GetInlinedFunctionInfo(); + name = inlined_info->GetName().AsCString(); + } + } + + if (name == NULL) + { + if (sc.function) + name = sc.function->GetName().GetCString(); + } + + if (name == NULL) + { + if (sc.symbol) + name = sc.symbol->GetName().GetCString(); + } + } + else + { + if (log) + log->Printf ("SBFrame::GetFunctionName () => error: could not reconstruct frame object for this SBFrame."); + } + } + else + { + if (log) + log->Printf ("SBFrame::GetFunctionName() => error: process is running"); + + } + } + return name; +} + diff --git a/contrib/llvm/tools/lldb/source/API/SBFunction.cpp b/contrib/llvm/tools/lldb/source/API/SBFunction.cpp new file mode 100644 index 00000000000..914d2d77f3e --- /dev/null +++ b/contrib/llvm/tools/lldb/source/API/SBFunction.cpp @@ -0,0 +1,225 @@ +//===-- SBFunction.cpp ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/API/SBFunction.h" +#include "lldb/API/SBProcess.h" +#include "lldb/API/SBStream.h" +#include "lldb/Core/Disassembler.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Module.h" +#include "lldb/Symbol/CompileUnit.h" +#include "lldb/Symbol/Function.h" +#include "lldb/Symbol/Type.h" +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/Target.h" + +using namespace lldb; +using namespace lldb_private; + +SBFunction::SBFunction () : + m_opaque_ptr (NULL) +{ +} + +SBFunction::SBFunction (lldb_private::Function *lldb_object_ptr) : + m_opaque_ptr (lldb_object_ptr) +{ +} + +SBFunction::SBFunction (const lldb::SBFunction &rhs) : + m_opaque_ptr (rhs.m_opaque_ptr) +{ +} + +const SBFunction & +SBFunction::operator = (const SBFunction &rhs) +{ + m_opaque_ptr = rhs.m_opaque_ptr; + return *this; +} + +SBFunction::~SBFunction () +{ + m_opaque_ptr = NULL; +} + +bool +SBFunction::IsValid () const +{ + return m_opaque_ptr != NULL; +} + +const char * +SBFunction::GetName() const +{ + const char *cstr = NULL; + if (m_opaque_ptr) + cstr = m_opaque_ptr->GetMangled().GetName().AsCString(); + + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (log) + { + if (cstr) + log->Printf ("SBFunction(%p)::GetName () => \"%s\"", m_opaque_ptr, cstr); + else + log->Printf ("SBFunction(%p)::GetName () => NULL", m_opaque_ptr); + } + return cstr; +} + +const char * +SBFunction::GetMangledName () const +{ + const char *cstr = NULL; + if (m_opaque_ptr) + cstr = m_opaque_ptr->GetMangled().GetMangledName().AsCString(); + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (log) + { + if (cstr) + log->Printf ("SBFunction(%p)::GetMangledName () => \"%s\"", m_opaque_ptr, cstr); + else + log->Printf ("SBFunction(%p)::GetMangledName () => NULL", m_opaque_ptr); + } + return cstr; +} + +bool +SBFunction::operator == (const SBFunction &rhs) const +{ + return m_opaque_ptr == rhs.m_opaque_ptr; +} + +bool +SBFunction::operator != (const SBFunction &rhs) const +{ + return m_opaque_ptr != rhs.m_opaque_ptr; +} + +bool +SBFunction::GetDescription (SBStream &s) +{ + if (m_opaque_ptr) + { + s.Printf ("SBFunction: id = 0x%8.8" PRIx64 ", name = %s", + m_opaque_ptr->GetID(), + m_opaque_ptr->GetName().AsCString()); + Type *func_type = m_opaque_ptr->GetType(); + if (func_type) + s.Printf(", type = %s", func_type->GetName().AsCString()); + return true; + } + s.Printf ("No value"); + return false; +} + +SBInstructionList +SBFunction::GetInstructions (SBTarget target) +{ + return GetInstructions (target, NULL); +} + +SBInstructionList +SBFunction::GetInstructions (SBTarget target, const char *flavor) +{ + SBInstructionList sb_instructions; + if (m_opaque_ptr) + { + Mutex::Locker api_locker; + ExecutionContext exe_ctx; + TargetSP target_sp (target.GetSP()); + if (target_sp) + { + api_locker.Lock (target_sp->GetAPIMutex()); + target_sp->CalculateExecutionContext (exe_ctx); + exe_ctx.SetProcessSP(target_sp->GetProcessSP()); + } + ModuleSP module_sp (m_opaque_ptr->GetAddressRange().GetBaseAddress().GetModule()); + if (module_sp) + { + sb_instructions.SetDisassembler (Disassembler::DisassembleRange (module_sp->GetArchitecture(), + NULL, + flavor, + exe_ctx, + m_opaque_ptr->GetAddressRange())); + } + } + return sb_instructions; +} + +lldb_private::Function * +SBFunction::get () +{ + return m_opaque_ptr; +} + +void +SBFunction::reset (lldb_private::Function *lldb_object_ptr) +{ + m_opaque_ptr = lldb_object_ptr; +} + +SBAddress +SBFunction::GetStartAddress () +{ + SBAddress addr; + if (m_opaque_ptr) + addr.SetAddress (&m_opaque_ptr->GetAddressRange().GetBaseAddress()); + return addr; +} + +SBAddress +SBFunction::GetEndAddress () +{ + SBAddress addr; + if (m_opaque_ptr) + { + addr_t byte_size = m_opaque_ptr->GetAddressRange().GetByteSize(); + if (byte_size > 0) + { + addr.SetAddress (&m_opaque_ptr->GetAddressRange().GetBaseAddress()); + addr->Slide (byte_size); + } + } + return addr; +} + + +uint32_t +SBFunction::GetPrologueByteSize () +{ + if (m_opaque_ptr) + return m_opaque_ptr->GetPrologueByteSize(); + return 0; +} + +SBType +SBFunction::GetType () +{ + SBType sb_type; + if (m_opaque_ptr) + { + Type *function_type = m_opaque_ptr->GetType(); + if (function_type) + sb_type.ref().SetType (function_type->shared_from_this()); + } + return sb_type; +} + +SBBlock +SBFunction::GetBlock () +{ + SBBlock sb_block; + if (m_opaque_ptr) + sb_block.SetPtr (&m_opaque_ptr->GetBlock (true)); + return sb_block; +} + + + diff --git a/contrib/llvm/tools/lldb/source/API/SBHostOS.cpp b/contrib/llvm/tools/lldb/source/API/SBHostOS.cpp new file mode 100644 index 00000000000..a8f7db90a15 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/API/SBHostOS.cpp @@ -0,0 +1,85 @@ +//===-- SBHostOS.cpp --------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/API/SBHostOS.h" +#include "lldb/API/SBError.h" +#include "lldb/Host/FileSpec.h" +#include "lldb/Core/Log.h" +#include "lldb/Host/Host.h" + +using namespace lldb; +using namespace lldb_private; + + + +SBFileSpec +SBHostOS::GetProgramFileSpec () +{ + SBFileSpec sb_filespec; + sb_filespec.SetFileSpec (Host::GetProgramFileSpec ()); + return sb_filespec; +} + +SBFileSpec +SBHostOS::GetLLDBPythonPath () +{ + SBFileSpec sb_lldb_python_filespec; + FileSpec lldb_python_spec; + if (Host::GetLLDBPath (ePathTypePythonDir, lldb_python_spec)) + { + sb_lldb_python_filespec.SetFileSpec (lldb_python_spec); + } + return sb_lldb_python_filespec; +} + +lldb::thread_t +SBHostOS::ThreadCreate +( + const char *name, + void *(*thread_function)(void *), + void *thread_arg, + SBError *error_ptr +) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + if (log) + log->Printf ("SBHostOS::ThreadCreate (name=\"%s\", thread_function=%p, thread_arg=%p, error_ptr=%p)", name, + thread_function, thread_arg, error_ptr); + + // FIXME: You should log the return value? + + return Host::ThreadCreate (name, thread_function, thread_arg, error_ptr ? error_ptr->get() : NULL); +} + +void +SBHostOS::ThreadCreated (const char *name) +{ + Host::ThreadCreated (name); +} + +bool +SBHostOS::ThreadCancel (lldb::thread_t thread, SBError *error_ptr) +{ + return Host::ThreadCancel (thread, error_ptr ? error_ptr->get() : NULL); +} + +bool +SBHostOS::ThreadDetach (lldb::thread_t thread, SBError *error_ptr) +{ + return Host::ThreadDetach (thread, error_ptr ? error_ptr->get() : NULL); +} + +bool +SBHostOS::ThreadJoin (lldb::thread_t thread, void **result, SBError *error_ptr) +{ + return Host::ThreadJoin (thread, result, error_ptr ? error_ptr->get() : NULL); +} + + diff --git a/contrib/llvm/tools/lldb/source/API/SBInputReader.cpp b/contrib/llvm/tools/lldb/source/API/SBInputReader.cpp new file mode 100644 index 00000000000..82b75c869f0 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/API/SBInputReader.cpp @@ -0,0 +1,216 @@ +//===-- SBInputReader.cpp ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + + +#include "lldb/lldb-enumerations.h" + +#include "lldb/API/SBDebugger.h" +#include "lldb/API/SBError.h" +#include "lldb/API/SBInputReader.h" +#include "lldb/API/SBStream.h" +#include "lldb/API/SBStringList.h" +#include "lldb/Core/InputReader.h" +#include "lldb/Core/Log.h" + + +using namespace lldb; +using namespace lldb_private; + +SBInputReader::SBInputReader () : + m_opaque_sp (), + m_callback_function (NULL), + m_callback_baton (NULL) + +{ +} + +SBInputReader::SBInputReader (const lldb::InputReaderSP &reader_sp) : + m_opaque_sp (reader_sp) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + if (log) + log->Printf ("SBInputReader::SBInputReader (reader_sp=%p) => SBInputReader(%p)", reader_sp.get(), + m_opaque_sp.get()); +} + +SBInputReader::SBInputReader (const SBInputReader &rhs) : + m_opaque_sp (rhs.m_opaque_sp) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + if (log) + log->Printf("SBInputReader::SBInputReader (rhs.sp=%p) => SBInputReader(%p)", + rhs.m_opaque_sp.get(), m_opaque_sp.get()); +} + +SBInputReader::~SBInputReader () +{ +} + +size_t +SBInputReader::PrivateCallback +( + void *baton, + InputReader &reader, + lldb::InputReaderAction notification, + const char *bytes, + size_t bytes_len +) +{ + SBInputReader *sb_reader = (SBInputReader *)baton; + return sb_reader->m_callback_function (sb_reader->m_callback_baton, + sb_reader, + notification, + bytes, + bytes_len); +} + +SBError +SBInputReader::Initialize +( + SBDebugger &debugger, + Callback callback_function, + void *callback_baton, + lldb::InputReaderGranularity granularity, + const char *end_token, + const char *prompt, + bool echo +) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + if (log) + log->Printf("SBInputReader(%p)::Initialize (SBDebugger(%p), callback_function=%p, callback_baton=%p, " + "granularity=%s, end_token=\"%s\", prompt=\"%s\", echo=%i)", + m_opaque_sp.get(), + debugger.get(), + callback_function, + callback_baton, + InputReader::GranularityAsCString (granularity), end_token, prompt, + echo); + + SBError sb_error; + m_opaque_sp.reset (new InputReader (debugger.ref())); + + m_callback_function = callback_function; + m_callback_baton = callback_baton; + + if (m_opaque_sp) + { + sb_error.SetError (m_opaque_sp->Initialize (SBInputReader::PrivateCallback, + this, + granularity, + end_token, + prompt, + echo)); + } + + if (sb_error.Fail()) + { + m_opaque_sp.reset (); + m_callback_function = NULL; + m_callback_baton = NULL; + } + + if (log) + { + SBStream sstr; + sb_error.GetDescription (sstr); + log->Printf ("SBInputReader(%p)::Initialize (...) => SBError(%p): %s", m_opaque_sp.get(), + sb_error.get(), sstr.GetData()); + } + + return sb_error; +} + +bool +SBInputReader::IsValid () const +{ + return (m_opaque_sp.get() != NULL); +} + +const SBInputReader & +SBInputReader::operator = (const SBInputReader &rhs) +{ + if (this != &rhs) + m_opaque_sp = rhs.m_opaque_sp; + return *this; +} + +InputReader * +SBInputReader::operator->() const +{ + return m_opaque_sp.get(); +} + +lldb::InputReaderSP & +SBInputReader::operator *() +{ + return m_opaque_sp; +} + +const lldb::InputReaderSP & +SBInputReader::operator *() const +{ + return m_opaque_sp; +} + +InputReader * +SBInputReader::get() const +{ + return m_opaque_sp.get(); +} + +InputReader & +SBInputReader::ref() const +{ + assert (m_opaque_sp.get()); + return *m_opaque_sp; +} + +bool +SBInputReader::IsDone () const +{ + if (m_opaque_sp) + return m_opaque_sp->IsDone(); + else + return true; +} + +void +SBInputReader::SetIsDone (bool value) +{ + if (m_opaque_sp) + m_opaque_sp->SetIsDone (value); +} + +bool +SBInputReader::IsActive () const +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + bool ret_value = false; + if (m_opaque_sp) + ret_value = m_opaque_sp->IsActive(); + + if (log) + log->Printf ("SBInputReader(%p)::IsActive () => %i", m_opaque_sp.get(), ret_value); + + return ret_value; +} + +InputReaderGranularity +SBInputReader::GetGranularity () +{ + if (m_opaque_sp) + return m_opaque_sp->GetGranularity(); + else + return eInputReaderGranularityInvalid; +} diff --git a/contrib/llvm/tools/lldb/source/API/SBInstruction.cpp b/contrib/llvm/tools/lldb/source/API/SBInstruction.cpp new file mode 100644 index 00000000000..2334cc0d124 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/API/SBInstruction.cpp @@ -0,0 +1,248 @@ +//===-- SBInstruction.cpp ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/API/SBInstruction.h" + +#include "lldb/API/SBAddress.h" +#include "lldb/API/SBFrame.h" +#include "lldb/API/SBInstruction.h" +#include "lldb/API/SBStream.h" +#include "lldb/API/SBTarget.h" + +#include "lldb/Core/ArchSpec.h" +#include "lldb/Core/DataBufferHeap.h" +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/Disassembler.h" +#include "lldb/Core/EmulateInstruction.h" +#include "lldb/Core/StreamFile.h" +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/StackFrame.h" +#include "lldb/Target/Target.h" + +using namespace lldb; +using namespace lldb_private; + +SBInstruction::SBInstruction () +{ +} + +SBInstruction::SBInstruction (const lldb::InstructionSP& inst_sp) : + m_opaque_sp (inst_sp) +{ +} + +SBInstruction::SBInstruction(const SBInstruction &rhs) : + m_opaque_sp (rhs.m_opaque_sp) +{ +} + +const SBInstruction & +SBInstruction::operator = (const SBInstruction &rhs) +{ + if (this != &rhs) + m_opaque_sp = rhs.m_opaque_sp; + return *this; +} + +SBInstruction::~SBInstruction () +{ +} + +bool +SBInstruction::IsValid() +{ + return (m_opaque_sp.get() != NULL); +} + +SBAddress +SBInstruction::GetAddress() +{ + SBAddress sb_addr; + if (m_opaque_sp && m_opaque_sp->GetAddress().IsValid()) + sb_addr.SetAddress(&m_opaque_sp->GetAddress()); + return sb_addr; +} + +const char * +SBInstruction::GetMnemonic(SBTarget target) +{ + if (m_opaque_sp) + { + Mutex::Locker api_locker; + ExecutionContext exe_ctx; + TargetSP target_sp (target.GetSP()); + if (target_sp) + { + api_locker.Lock (target_sp->GetAPIMutex()); + target_sp->CalculateExecutionContext (exe_ctx); + exe_ctx.SetProcessSP(target_sp->GetProcessSP()); + } + return m_opaque_sp->GetMnemonic(&exe_ctx); + } + return NULL; +} + +const char * +SBInstruction::GetOperands(SBTarget target) +{ + if (m_opaque_sp) + { + Mutex::Locker api_locker; + ExecutionContext exe_ctx; + TargetSP target_sp (target.GetSP()); + if (target_sp) + { + api_locker.Lock (target_sp->GetAPIMutex()); + target_sp->CalculateExecutionContext (exe_ctx); + exe_ctx.SetProcessSP(target_sp->GetProcessSP()); + } + return m_opaque_sp->GetOperands(&exe_ctx); + } + return NULL; +} + +const char * +SBInstruction::GetComment(SBTarget target) +{ + if (m_opaque_sp) + { + Mutex::Locker api_locker; + ExecutionContext exe_ctx; + TargetSP target_sp (target.GetSP()); + if (target_sp) + { + api_locker.Lock (target_sp->GetAPIMutex()); + target_sp->CalculateExecutionContext (exe_ctx); + exe_ctx.SetProcessSP(target_sp->GetProcessSP()); + } + return m_opaque_sp->GetComment(&exe_ctx); + } + return NULL; +} + +size_t +SBInstruction::GetByteSize () +{ + if (m_opaque_sp) + return m_opaque_sp->GetOpcode().GetByteSize(); + return 0; +} + +SBData +SBInstruction::GetData (SBTarget target) +{ + lldb::SBData sb_data; + if (m_opaque_sp) + { + DataExtractorSP data_extractor_sp (new DataExtractor()); + if (m_opaque_sp->GetData (*data_extractor_sp)) + { + sb_data.SetOpaque (data_extractor_sp); + } + } + return sb_data; +} + + + +bool +SBInstruction::DoesBranch () +{ + if (m_opaque_sp) + return m_opaque_sp->DoesBranch (); + return false; +} + +void +SBInstruction::SetOpaque (const lldb::InstructionSP &inst_sp) +{ + m_opaque_sp = inst_sp; +} + +bool +SBInstruction::GetDescription (lldb::SBStream &s) +{ + if (m_opaque_sp) + { + // Use the "ref()" instead of the "get()" accessor in case the SBStream + // didn't have a stream already created, one will get created... + m_opaque_sp->Dump (&s.ref(), 0, true, false, NULL); + return true; + } + return false; +} + +void +SBInstruction::Print (FILE *out) +{ + if (out == NULL) + return; + + if (m_opaque_sp) + { + StreamFile out_stream (out, false); + m_opaque_sp->Dump (&out_stream, 0, true, false, NULL); + } +} + +bool +SBInstruction::EmulateWithFrame (lldb::SBFrame &frame, uint32_t evaluate_options) +{ + if (m_opaque_sp) + { + lldb::StackFrameSP frame_sp (frame.GetFrameSP()); + + if (frame_sp) + { + lldb_private::ExecutionContext exe_ctx; + frame_sp->CalculateExecutionContext (exe_ctx); + lldb_private::Target *target = exe_ctx.GetTargetPtr(); + lldb_private::ArchSpec arch = target->GetArchitecture(); + + return m_opaque_sp->Emulate (arch, + evaluate_options, + (void *) frame_sp.get(), + &lldb_private::EmulateInstruction::ReadMemoryFrame, + &lldb_private::EmulateInstruction::WriteMemoryFrame, + &lldb_private::EmulateInstruction::ReadRegisterFrame, + &lldb_private::EmulateInstruction::WriteRegisterFrame); + } + } + return false; +} + +bool +SBInstruction::DumpEmulation (const char *triple) +{ + if (m_opaque_sp && triple) + { + lldb_private::ArchSpec arch (triple, NULL); + + return m_opaque_sp->DumpEmulation (arch); + + } + return false; +} + +bool +SBInstruction::TestEmulation (lldb::SBStream &output_stream, const char *test_file) +{ + if (!m_opaque_sp.get()) + m_opaque_sp.reset (new PseudoInstruction()); + + return m_opaque_sp->TestEmulation (output_stream.get(), test_file); +} + +lldb::AddressClass +SBInstruction::GetAddressClass () +{ + if (m_opaque_sp.get()) + return m_opaque_sp->GetAddressClass(); + return eAddressClassInvalid; +} diff --git a/contrib/llvm/tools/lldb/source/API/SBInstructionList.cpp b/contrib/llvm/tools/lldb/source/API/SBInstructionList.cpp new file mode 100644 index 00000000000..fe22d9c29e4 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/API/SBInstructionList.cpp @@ -0,0 +1,132 @@ +//===-- SBInstructionList.cpp -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/API/SBInstructionList.h" +#include "lldb/API/SBInstruction.h" +#include "lldb/API/SBStream.h" +#include "lldb/Core/Disassembler.h" +#include "lldb/Core/Stream.h" + +using namespace lldb; +using namespace lldb_private; + + +SBInstructionList::SBInstructionList () : + m_opaque_sp() +{ +} + +SBInstructionList::SBInstructionList(const SBInstructionList &rhs) : + m_opaque_sp (rhs.m_opaque_sp) +{ +} + +const SBInstructionList & +SBInstructionList::operator = (const SBInstructionList &rhs) +{ + if (this != &rhs) + m_opaque_sp = rhs.m_opaque_sp; + return *this; +} + + +SBInstructionList::~SBInstructionList () +{ +} + +bool +SBInstructionList::IsValid () const +{ + return m_opaque_sp.get() != NULL; +} + +size_t +SBInstructionList::GetSize () +{ + if (m_opaque_sp) + return m_opaque_sp->GetInstructionList().GetSize(); + return 0; +} + +SBInstruction +SBInstructionList::GetInstructionAtIndex (uint32_t idx) +{ + SBInstruction inst; + if (m_opaque_sp && idx < m_opaque_sp->GetInstructionList().GetSize()) + inst.SetOpaque (m_opaque_sp->GetInstructionList().GetInstructionAtIndex (idx)); + return inst; +} + +void +SBInstructionList::Clear () +{ + m_opaque_sp.reset(); +} + +void +SBInstructionList::AppendInstruction (SBInstruction insn) +{ +} + +void +SBInstructionList::SetDisassembler (const lldb::DisassemblerSP &opaque_sp) +{ + m_opaque_sp = opaque_sp; +} + +void +SBInstructionList::Print (FILE *out) +{ + if (out == NULL) + return; +} + + +bool +SBInstructionList::GetDescription (lldb::SBStream &description) +{ + if (m_opaque_sp) + { + size_t num_instructions = GetSize (); + if (num_instructions) + { + // Call the ref() to make sure a stream is created if one deesn't + // exist already inside description... + Stream &sref = description.ref(); + const uint32_t max_opcode_byte_size = m_opaque_sp->GetInstructionList().GetMaxOpcocdeByteSize(); + for (size_t i=0; iGetInstructionList().GetInstructionAtIndex (i).get(); + if (inst == NULL) + break; + inst->Dump (&sref, max_opcode_byte_size, true, false, NULL); + sref.EOL(); + } + return true; + } + } + return false; +} + + +bool +SBInstructionList::DumpEmulationForAllInstructions (const char *triple) +{ + if (m_opaque_sp) + { + size_t len = GetSize(); + for (size_t i = 0; i < len; ++i) + { + if (!GetInstructionAtIndex((uint32_t) i).DumpEmulation (triple)) + return false; + } + } + return true; +} + diff --git a/contrib/llvm/tools/lldb/source/API/SBLineEntry.cpp b/contrib/llvm/tools/lldb/source/API/SBLineEntry.cpp new file mode 100644 index 00000000000..0864a2e006c --- /dev/null +++ b/contrib/llvm/tools/lldb/source/API/SBLineEntry.cpp @@ -0,0 +1,250 @@ +//===-- SBLineEntry.cpp -----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include + +#include "lldb/API/SBLineEntry.h" +#include "lldb/API/SBStream.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Core/Log.h" +#include "lldb/Symbol/LineEntry.h" + +using namespace lldb; +using namespace lldb_private; + + +SBLineEntry::SBLineEntry () : + m_opaque_ap () +{ +} + +SBLineEntry::SBLineEntry (const SBLineEntry &rhs) : + m_opaque_ap () +{ + if (rhs.IsValid()) + ref() = rhs.ref(); +} + +SBLineEntry::SBLineEntry (const lldb_private::LineEntry *lldb_object_ptr) : + m_opaque_ap () +{ + if (lldb_object_ptr) + ref() = *lldb_object_ptr; +} + +const SBLineEntry & +SBLineEntry::operator = (const SBLineEntry &rhs) +{ + if (this != &rhs) + { + if (rhs.IsValid()) + ref() = rhs.ref(); + else + m_opaque_ap.reset(); + } + return *this; +} + +void +SBLineEntry::SetLineEntry (const lldb_private::LineEntry &lldb_object_ref) +{ + ref() = lldb_object_ref; +} + + +SBLineEntry::~SBLineEntry () +{ +} + + +SBAddress +SBLineEntry::GetStartAddress () const +{ + + SBAddress sb_address; + if (m_opaque_ap.get()) + sb_address.SetAddress(&m_opaque_ap->range.GetBaseAddress()); + + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (log) + { + StreamString sstr; + const Address *addr = sb_address.get(); + if (addr) + addr->Dump (&sstr, NULL, Address::DumpStyleModuleWithFileAddress, Address::DumpStyleInvalid, 4); + log->Printf ("SBLineEntry(%p)::GetStartAddress () => SBAddress (%p): %s", + m_opaque_ap.get(), sb_address.get(), sstr.GetData()); + } + + return sb_address; +} + +SBAddress +SBLineEntry::GetEndAddress () const +{ + SBAddress sb_address; + if (m_opaque_ap.get()) + { + sb_address.SetAddress(&m_opaque_ap->range.GetBaseAddress()); + sb_address.OffsetAddress(m_opaque_ap->range.GetByteSize()); + } + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (log) + { + StreamString sstr; + const Address *addr = sb_address.get(); + if (addr) + addr->Dump (&sstr, NULL, Address::DumpStyleModuleWithFileAddress, Address::DumpStyleInvalid, 4); + log->Printf ("SBLineEntry(%p)::GetEndAddress () => SBAddress (%p): %s", + m_opaque_ap.get(), sb_address.get(), sstr.GetData()); + } + return sb_address; +} + +bool +SBLineEntry::IsValid () const +{ + return m_opaque_ap.get() && m_opaque_ap->IsValid(); +} + + +SBFileSpec +SBLineEntry::GetFileSpec () const +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + SBFileSpec sb_file_spec; + if (m_opaque_ap.get() && m_opaque_ap->file) + sb_file_spec.SetFileSpec(m_opaque_ap->file); + + if (log) + { + SBStream sstr; + sb_file_spec.GetDescription (sstr); + log->Printf ("SBLineEntry(%p)::GetFileSpec () => SBFileSpec(%p): %s", m_opaque_ap.get(), + sb_file_spec.get(), sstr.GetData()); + } + + return sb_file_spec; +} + +uint32_t +SBLineEntry::GetLine () const +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + uint32_t line = 0; + if (m_opaque_ap.get()) + line = m_opaque_ap->line; + + if (log) + log->Printf ("SBLineEntry(%p)::GetLine () => %u", m_opaque_ap.get(), line); + + return line; +} + + +uint32_t +SBLineEntry::GetColumn () const +{ + if (m_opaque_ap.get()) + return m_opaque_ap->column; + return 0; +} + +void +SBLineEntry::SetFileSpec (lldb::SBFileSpec filespec) +{ + if (filespec.IsValid()) + ref().file = filespec.ref(); + else + ref().file.Clear(); +} +void +SBLineEntry::SetLine (uint32_t line) +{ + ref().line = line; +} + +void +SBLineEntry::SetColumn (uint32_t column) +{ + ref().line = column; +} + + + +bool +SBLineEntry::operator == (const SBLineEntry &rhs) const +{ + lldb_private::LineEntry *lhs_ptr = m_opaque_ap.get(); + lldb_private::LineEntry *rhs_ptr = rhs.m_opaque_ap.get(); + + if (lhs_ptr && rhs_ptr) + return lldb_private::LineEntry::Compare (*lhs_ptr, *rhs_ptr) == 0; + + return lhs_ptr == rhs_ptr; +} + +bool +SBLineEntry::operator != (const SBLineEntry &rhs) const +{ + lldb_private::LineEntry *lhs_ptr = m_opaque_ap.get(); + lldb_private::LineEntry *rhs_ptr = rhs.m_opaque_ap.get(); + + if (lhs_ptr && rhs_ptr) + return lldb_private::LineEntry::Compare (*lhs_ptr, *rhs_ptr) != 0; + + return lhs_ptr != rhs_ptr; +} + +const lldb_private::LineEntry * +SBLineEntry::operator->() const +{ + return m_opaque_ap.get(); +} + +lldb_private::LineEntry & +SBLineEntry::ref() +{ + if (m_opaque_ap.get() == NULL) + m_opaque_ap.reset (new lldb_private::LineEntry ()); + return *m_opaque_ap; +} + +const lldb_private::LineEntry & +SBLineEntry::ref() const +{ + return *m_opaque_ap; +} + +bool +SBLineEntry::GetDescription (SBStream &description) +{ + Stream &strm = description.ref(); + + if (m_opaque_ap.get()) + { + char file_path[PATH_MAX*2]; + m_opaque_ap->file.GetPath (file_path, sizeof (file_path)); + strm.Printf ("%s:%u", file_path, GetLine()); + if (GetColumn() > 0) + strm.Printf (":%u", GetColumn()); + } + else + strm.PutCString ("No value"); + + return true; +} + +lldb_private::LineEntry * +SBLineEntry::get () +{ + return m_opaque_ap.get(); +} diff --git a/contrib/llvm/tools/lldb/source/API/SBListener.cpp b/contrib/llvm/tools/lldb/source/API/SBListener.cpp new file mode 100644 index 00000000000..2e67b4c24e8 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/API/SBListener.cpp @@ -0,0 +1,443 @@ +//===-- SBListener.cpp ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "lldb/API/SBListener.h" +#include "lldb/API/SBBroadcaster.h" +#include "lldb/API/SBDebugger.h" +#include "lldb/API/SBEvent.h" +#include "lldb/API/SBStream.h" +#include "lldb/Core/Broadcaster.h" +#include "lldb/Core/Debugger.h" +#include "lldb/Core/Listener.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Host/TimeValue.h" + + +using namespace lldb; +using namespace lldb_private; + + +SBListener::SBListener () : + m_opaque_sp (), + m_opaque_ptr (NULL) +{ +} + +SBListener::SBListener (const char *name) : + m_opaque_sp (new Listener (name)), + m_opaque_ptr (NULL) +{ + m_opaque_ptr = m_opaque_sp.get(); + + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + if (log) + log->Printf ("SBListener::SBListener (name=\"%s\") => SBListener(%p)", + name, m_opaque_ptr); +} + + +SBListener::SBListener (const SBListener &rhs) : + m_opaque_sp (rhs.m_opaque_sp), + m_opaque_ptr (rhs.m_opaque_ptr) +{ +} + +const lldb::SBListener & +SBListener::operator = (const lldb::SBListener &rhs) +{ + if (this != &rhs) + { + m_opaque_sp = rhs.m_opaque_sp; + m_opaque_ptr = rhs.m_opaque_ptr; + } + return *this; +} + +SBListener::SBListener (Listener &listener) : + m_opaque_sp (), + m_opaque_ptr (&listener) +{ +} + +SBListener::~SBListener () +{ +} + +bool +SBListener::IsValid() const +{ + return m_opaque_ptr != NULL; +} + +void +SBListener::AddEvent (const SBEvent &event) +{ + EventSP &event_sp = event.GetSP (); + if (event_sp) + m_opaque_ptr->AddEvent (event_sp); +} + +void +SBListener::Clear () +{ + if (m_opaque_ptr) + m_opaque_ptr->Clear (); +} + +uint32_t +SBListener::StartListeningForEventClass (SBDebugger &debugger, + const char *broadcaster_class, + uint32_t event_mask) +{ + if (m_opaque_ptr) + { + Debugger *lldb_debugger = debugger.get(); + if (!lldb_debugger) + return 0; + BroadcastEventSpec event_spec (ConstString (broadcaster_class), event_mask); + return m_opaque_ptr->StartListeningForEventSpec (*lldb_debugger, event_spec); + } + else + return 0; +} + +bool +SBListener::StopListeningForEventClass (SBDebugger &debugger, + const char *broadcaster_class, + uint32_t event_mask) +{ + if (m_opaque_ptr) + { + Debugger *lldb_debugger = debugger.get(); + if (!lldb_debugger) + return false; + BroadcastEventSpec event_spec (ConstString (broadcaster_class), event_mask); + return m_opaque_ptr->StopListeningForEventSpec (*lldb_debugger, event_spec); + } + else + return false; +} + +uint32_t +SBListener::StartListeningForEvents (const SBBroadcaster& broadcaster, uint32_t event_mask) +{ + uint32_t acquired_event_mask = 0; + if (m_opaque_ptr && broadcaster.IsValid()) + { + acquired_event_mask = m_opaque_ptr->StartListeningForEvents (broadcaster.get(), event_mask); + } + + Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API); + if (log) + { + StreamString sstr_requested; + StreamString sstr_acquired; + + Broadcaster *lldb_broadcaster = broadcaster.get(); + if (lldb_broadcaster) + { + const bool got_requested_names = lldb_broadcaster->GetEventNames (sstr_requested, event_mask, false); + const bool got_acquired_names = lldb_broadcaster->GetEventNames (sstr_acquired, acquired_event_mask, false); + log->Printf ("SBListener(%p)::StartListeneingForEvents (SBBroadcaster(%p): %s, event_mask=0x%8.8x%s%s%s) => 0x%8.8x%s%s%s", + m_opaque_ptr, + lldb_broadcaster, + lldb_broadcaster->GetBroadcasterName().GetCString(), + event_mask, + got_requested_names ? " (" : "", + sstr_requested.GetData(), + got_requested_names ? ")" : "", + acquired_event_mask, + got_acquired_names ? " (" : "", + sstr_acquired.GetData(), + got_acquired_names ? ")" : ""); + } + else + { + log->Printf ("SBListener(%p)::StartListeneingForEvents (SBBroadcaster(%p), event_mask=0x%8.8x) => 0x%8.8x", + m_opaque_ptr, + lldb_broadcaster, + event_mask, + acquired_event_mask); + + } + } + + return acquired_event_mask; +} + +bool +SBListener::StopListeningForEvents (const SBBroadcaster& broadcaster, uint32_t event_mask) +{ + if (m_opaque_ptr && broadcaster.IsValid()) + { + return m_opaque_ptr->StopListeningForEvents (broadcaster.get(), event_mask); + } + return false; +} + +bool +SBListener::WaitForEvent (uint32_t timeout_secs, SBEvent &event) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (log) + { + if (timeout_secs == UINT32_MAX) + { + log->Printf ("SBListener(%p)::WaitForEvent (timeout_secs=INFINITE, SBEvent(%p))...", + m_opaque_ptr, event.get()); + } + else + { + log->Printf ("SBListener(%p)::WaitForEvent (timeout_secs=%d, SBEvent(%p))...", + m_opaque_ptr, timeout_secs, event.get()); + } + } + bool success = false; + + if (m_opaque_ptr) + { + TimeValue time_value; + if (timeout_secs != UINT32_MAX) + { + assert (timeout_secs != 0); // Take this out after all calls with timeout set to zero have been removed.... + time_value = TimeValue::Now(); + time_value.OffsetWithSeconds (timeout_secs); + } + EventSP event_sp; + if (m_opaque_ptr->WaitForEvent (time_value.IsValid() ? &time_value : NULL, event_sp)) + { + event.reset (event_sp); + success = true; + } + } + + if (log) + { + if (timeout_secs == UINT32_MAX) + { + log->Printf ("SBListener(%p)::WaitForEvent (timeout_secs=INFINITE, SBEvent(%p)) => %i", + m_opaque_ptr, event.get(), success); + } + else + { + log->Printf ("SBListener(%p)::WaitForEvent (timeout_secs=%d, SBEvent(%p)) => %i", + m_opaque_ptr, timeout_secs, event.get(), success); + } + } + if (!success) + event.reset (NULL); + return success; +} + +bool +SBListener::WaitForEventForBroadcaster +( + uint32_t num_seconds, + const SBBroadcaster &broadcaster, + SBEvent &event +) +{ + if (m_opaque_ptr && broadcaster.IsValid()) + { + TimeValue time_value; + if (num_seconds != UINT32_MAX) + { + time_value = TimeValue::Now(); + time_value.OffsetWithSeconds (num_seconds); + } + EventSP event_sp; + if (m_opaque_ptr->WaitForEventForBroadcaster (time_value.IsValid() ? &time_value : NULL, + broadcaster.get(), + event_sp)) + { + event.reset (event_sp); + return true; + } + + } + event.reset (NULL); + return false; +} + +bool +SBListener::WaitForEventForBroadcasterWithType +( + uint32_t num_seconds, + const SBBroadcaster &broadcaster, + uint32_t event_type_mask, + SBEvent &event +) +{ + if (m_opaque_ptr && broadcaster.IsValid()) + { + TimeValue time_value; + if (num_seconds != UINT32_MAX) + { + time_value = TimeValue::Now(); + time_value.OffsetWithSeconds (num_seconds); + } + EventSP event_sp; + if (m_opaque_ptr->WaitForEventForBroadcasterWithType (time_value.IsValid() ? &time_value : NULL, + broadcaster.get(), + event_type_mask, + event_sp)) + { + event.reset (event_sp); + return true; + } + } + event.reset (NULL); + return false; +} + +bool +SBListener::PeekAtNextEvent (SBEvent &event) +{ + if (m_opaque_ptr) + { + event.reset (m_opaque_ptr->PeekAtNextEvent ()); + return event.IsValid(); + } + event.reset (NULL); + return false; +} + +bool +SBListener::PeekAtNextEventForBroadcaster (const SBBroadcaster &broadcaster, SBEvent &event) +{ + if (m_opaque_ptr && broadcaster.IsValid()) + { + event.reset (m_opaque_ptr->PeekAtNextEventForBroadcaster (broadcaster.get())); + return event.IsValid(); + } + event.reset (NULL); + return false; +} + +bool +SBListener::PeekAtNextEventForBroadcasterWithType (const SBBroadcaster &broadcaster, uint32_t event_type_mask, + SBEvent &event) +{ + if (m_opaque_ptr && broadcaster.IsValid()) + { + event.reset(m_opaque_ptr->PeekAtNextEventForBroadcasterWithType (broadcaster.get(), event_type_mask)); + return event.IsValid(); + } + event.reset (NULL); + return false; +} + +bool +SBListener::GetNextEvent (SBEvent &event) +{ + if (m_opaque_ptr) + { + EventSP event_sp; + if (m_opaque_ptr->GetNextEvent (event_sp)) + { + event.reset (event_sp); + return true; + } + } + event.reset (NULL); + return false; +} + +bool +SBListener::GetNextEventForBroadcaster (const SBBroadcaster &broadcaster, SBEvent &event) +{ + if (m_opaque_ptr && broadcaster.IsValid()) + { + EventSP event_sp; + if (m_opaque_ptr->GetNextEventForBroadcaster (broadcaster.get(), event_sp)) + { + event.reset (event_sp); + return true; + } + } + event.reset (NULL); + return false; +} + +bool +SBListener::GetNextEventForBroadcasterWithType +( + const SBBroadcaster &broadcaster, + uint32_t event_type_mask, + SBEvent &event +) +{ + if (m_opaque_ptr && broadcaster.IsValid()) + { + EventSP event_sp; + if (m_opaque_ptr->GetNextEventForBroadcasterWithType (broadcaster.get(), + event_type_mask, + event_sp)) + { + event.reset (event_sp); + return true; + } + } + event.reset (NULL); + return false; +} + +bool +SBListener::HandleBroadcastEvent (const SBEvent &event) +{ + if (m_opaque_ptr) + return m_opaque_ptr->HandleBroadcastEvent (event.GetSP()); + return false; +} + +Listener * +SBListener::operator->() const +{ + return m_opaque_ptr; +} + +Listener * +SBListener::get() const +{ + return m_opaque_ptr; +} + +void +SBListener::reset(Listener *listener, bool owns) +{ + if (owns) + m_opaque_sp.reset (listener); + else + m_opaque_sp.reset (); + m_opaque_ptr = listener; +} + +Listener & +SBListener::ref() const +{ + return *m_opaque_ptr; +} + +Listener & +SBListener::operator *() +{ + return *m_opaque_ptr; +} + +const Listener & +SBListener::operator *() const +{ + return *m_opaque_ptr; +} + + diff --git a/contrib/llvm/tools/lldb/source/API/SBModule.cpp b/contrib/llvm/tools/lldb/source/API/SBModule.cpp new file mode 100644 index 00000000000..5f5fc9292cd --- /dev/null +++ b/contrib/llvm/tools/lldb/source/API/SBModule.cpp @@ -0,0 +1,655 @@ +//===-- SBModule.cpp --------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/API/SBModule.h" +#include "lldb/API/SBAddress.h" +#include "lldb/API/SBFileSpec.h" +#include "lldb/API/SBModuleSpec.h" +#include "lldb/API/SBProcess.h" +#include "lldb/API/SBStream.h" +#include "lldb/API/SBSymbolContextList.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Section.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Core/ValueObjectList.h" +#include "lldb/Core/ValueObjectVariable.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Symbol/SymbolVendor.h" +#include "lldb/Symbol/Symtab.h" +#include "lldb/Symbol/VariableList.h" +#include "lldb/Target/Target.h" + +using namespace lldb; +using namespace lldb_private; + + +SBModule::SBModule () : + m_opaque_sp () +{ +} + +SBModule::SBModule (const lldb::ModuleSP& module_sp) : + m_opaque_sp (module_sp) +{ +} + +SBModule::SBModule(const SBModuleSpec &module_spec) : + m_opaque_sp () +{ + ModuleSP module_sp; + Error error = ModuleList::GetSharedModule (*module_spec.m_opaque_ap, + module_sp, + NULL, + NULL, + NULL); + if (module_sp) + SetSP(module_sp); +} + +SBModule::SBModule(const SBModule &rhs) : + m_opaque_sp (rhs.m_opaque_sp) +{ +} + +SBModule::SBModule (lldb::SBProcess &process, lldb::addr_t header_addr) : + m_opaque_sp () +{ + ProcessSP process_sp (process.GetSP()); + if (process_sp) + { + m_opaque_sp = process_sp->ReadModuleFromMemory (FileSpec(), header_addr); + if (m_opaque_sp) + { + Target &target = process_sp->GetTarget(); + bool changed = false; + m_opaque_sp->SetLoadAddress(target, 0, changed); + target.GetImages().Append(m_opaque_sp); + } + } +} + +const SBModule & +SBModule::operator = (const SBModule &rhs) +{ + if (this != &rhs) + m_opaque_sp = rhs.m_opaque_sp; + return *this; +} + +SBModule::~SBModule () +{ +} + +bool +SBModule::IsValid () const +{ + return m_opaque_sp.get() != NULL; +} + +void +SBModule::Clear() +{ + m_opaque_sp.reset(); +} + +SBFileSpec +SBModule::GetFileSpec () const +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + SBFileSpec file_spec; + ModuleSP module_sp (GetSP ()); + if (module_sp) + file_spec.SetFileSpec(module_sp->GetFileSpec()); + + if (log) + { + log->Printf ("SBModule(%p)::GetFileSpec () => SBFileSpec(%p)", + module_sp.get(), file_spec.get()); + } + + return file_spec; +} + +lldb::SBFileSpec +SBModule::GetPlatformFileSpec () const +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + SBFileSpec file_spec; + ModuleSP module_sp (GetSP ()); + if (module_sp) + file_spec.SetFileSpec(module_sp->GetPlatformFileSpec()); + + if (log) + { + log->Printf ("SBModule(%p)::GetPlatformFileSpec () => SBFileSpec(%p)", + module_sp.get(), file_spec.get()); + } + + return file_spec; + +} + +bool +SBModule::SetPlatformFileSpec (const lldb::SBFileSpec &platform_file) +{ + bool result = false; + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + ModuleSP module_sp (GetSP ()); + if (module_sp) + { + module_sp->SetPlatformFileSpec(*platform_file); + result = true; + } + + if (log) + { + log->Printf ("SBModule(%p)::SetPlatformFileSpec (SBFileSpec(%p (%s)) => %i", + module_sp.get(), + platform_file.get(), + platform_file->GetPath().c_str(), + result); + } + return result; +} + + + +const uint8_t * +SBModule::GetUUIDBytes () const +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + const uint8_t *uuid_bytes = NULL; + ModuleSP module_sp (GetSP ()); + if (module_sp) + uuid_bytes = (const uint8_t *)module_sp->GetUUID().GetBytes(); + + if (log) + { + if (uuid_bytes) + { + StreamString s; + module_sp->GetUUID().Dump (&s); + log->Printf ("SBModule(%p)::GetUUIDBytes () => %s", module_sp.get(), s.GetData()); + } + else + log->Printf ("SBModule(%p)::GetUUIDBytes () => NULL", module_sp.get()); + } + return uuid_bytes; +} + + +const char * +SBModule::GetUUIDString () const +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + static char uuid_string_buffer[80]; + const char *uuid_c_string = NULL; + std::string uuid_string; + ModuleSP module_sp (GetSP ()); + if (module_sp) + uuid_string = module_sp->GetUUID().GetAsString(); + + if (!uuid_string.empty()) + { + strncpy (uuid_string_buffer, uuid_string.c_str(), sizeof (uuid_string_buffer)); + uuid_c_string = uuid_string_buffer; + } + + if (log) + { + if (!uuid_string.empty()) + { + StreamString s; + module_sp->GetUUID().Dump (&s); + log->Printf ("SBModule(%p)::GetUUIDString () => %s", module_sp.get(), s.GetData()); + } + else + log->Printf ("SBModule(%p)::GetUUIDString () => NULL", module_sp.get()); + } + return uuid_c_string; +} + + +bool +SBModule::operator == (const SBModule &rhs) const +{ + if (m_opaque_sp) + return m_opaque_sp.get() == rhs.m_opaque_sp.get(); + return false; +} + +bool +SBModule::operator != (const SBModule &rhs) const +{ + if (m_opaque_sp) + return m_opaque_sp.get() != rhs.m_opaque_sp.get(); + return false; +} + +ModuleSP +SBModule::GetSP () const +{ + return m_opaque_sp; +} + +void +SBModule::SetSP (const ModuleSP &module_sp) +{ + m_opaque_sp = module_sp; +} + +SBAddress +SBModule::ResolveFileAddress (lldb::addr_t vm_addr) +{ + lldb::SBAddress sb_addr; + ModuleSP module_sp (GetSP ()); + if (module_sp) + { + Address addr; + if (module_sp->ResolveFileAddress (vm_addr, addr)) + sb_addr.ref() = addr; + } + return sb_addr; +} + +SBSymbolContext +SBModule::ResolveSymbolContextForAddress (const SBAddress& addr, uint32_t resolve_scope) +{ + SBSymbolContext sb_sc; + ModuleSP module_sp (GetSP ()); + if (module_sp && addr.IsValid()) + module_sp->ResolveSymbolContextForAddress (addr.ref(), resolve_scope, *sb_sc); + return sb_sc; +} + +bool +SBModule::GetDescription (SBStream &description) +{ + Stream &strm = description.ref(); + + ModuleSP module_sp (GetSP ()); + if (module_sp) + { + module_sp->GetDescription (&strm); + } + else + strm.PutCString ("No value"); + + return true; +} + +uint32_t +SBModule::GetNumCompileUnits() +{ + ModuleSP module_sp (GetSP ()); + if (module_sp) + { + return module_sp->GetNumCompileUnits (); + } + return 0; +} + +SBCompileUnit +SBModule::GetCompileUnitAtIndex (uint32_t index) +{ + SBCompileUnit sb_cu; + ModuleSP module_sp (GetSP ()); + if (module_sp) + { + CompUnitSP cu_sp = module_sp->GetCompileUnitAtIndex (index); + sb_cu.reset(cu_sp.get()); + } + return sb_cu; +} + +static Symtab * +GetUnifiedSymbolTable (const lldb::ModuleSP& module_sp) +{ + if (module_sp) + { + SymbolVendor *symbols = module_sp->GetSymbolVendor(); + if (symbols) + return symbols->GetSymtab(); + } + return NULL; +} + +size_t +SBModule::GetNumSymbols () +{ + ModuleSP module_sp (GetSP ()); + if (module_sp) + { + Symtab *symtab = GetUnifiedSymbolTable (module_sp); + if (symtab) + return symtab->GetNumSymbols(); + } + return 0; +} + +SBSymbol +SBModule::GetSymbolAtIndex (size_t idx) +{ + SBSymbol sb_symbol; + ModuleSP module_sp (GetSP ()); + Symtab *symtab = GetUnifiedSymbolTable (module_sp); + if (symtab) + sb_symbol.SetSymbol(symtab->SymbolAtIndex (idx)); + return sb_symbol; +} + +lldb::SBSymbol +SBModule::FindSymbol (const char *name, + lldb::SymbolType symbol_type) +{ + SBSymbol sb_symbol; + if (name && name[0]) + { + ModuleSP module_sp (GetSP ()); + Symtab *symtab = GetUnifiedSymbolTable (module_sp); + if (symtab) + sb_symbol.SetSymbol(symtab->FindFirstSymbolWithNameAndType(ConstString(name), symbol_type, Symtab::eDebugAny, Symtab::eVisibilityAny)); + } + return sb_symbol; +} + + +lldb::SBSymbolContextList +SBModule::FindSymbols (const char *name, lldb::SymbolType symbol_type) +{ + SBSymbolContextList sb_sc_list; + if (name && name[0]) + { + ModuleSP module_sp (GetSP ()); + Symtab *symtab = GetUnifiedSymbolTable (module_sp); + if (symtab) + { + std::vector matching_symbol_indexes; + const size_t num_matches = symtab->FindAllSymbolsWithNameAndType(ConstString(name), symbol_type, matching_symbol_indexes); + if (num_matches) + { + SymbolContext sc; + sc.module_sp = module_sp; + SymbolContextList &sc_list = *sb_sc_list; + for (size_t i=0; iSymbolAtIndex (matching_symbol_indexes[i]); + if (sc.symbol) + sc_list.Append(sc); + } + } + } + } + return sb_sc_list; + +} + + + +size_t +SBModule::GetNumSections () +{ + ModuleSP module_sp (GetSP ()); + if (module_sp) + { + // Give the symbol vendor a chance to add to the unified section list. + module_sp->GetSymbolVendor(); + SectionList *section_list = module_sp->GetSectionList(); + if (section_list) + return section_list->GetSize(); + } + return 0; +} + +SBSection +SBModule::GetSectionAtIndex (size_t idx) +{ + SBSection sb_section; + ModuleSP module_sp (GetSP ()); + if (module_sp) + { + // Give the symbol vendor a chance to add to the unified section list. + module_sp->GetSymbolVendor(); + SectionList *section_list = module_sp->GetSectionList (); + + if (section_list) + sb_section.SetSP(section_list->GetSectionAtIndex (idx)); + } + return sb_section; +} + +lldb::SBSymbolContextList +SBModule::FindFunctions (const char *name, + uint32_t name_type_mask) +{ + lldb::SBSymbolContextList sb_sc_list; + ModuleSP module_sp (GetSP ()); + if (name && module_sp) + { + const bool append = true; + const bool symbols_ok = true; + const bool inlines_ok = true; + module_sp->FindFunctions (ConstString(name), + NULL, + name_type_mask, + symbols_ok, + inlines_ok, + append, + *sb_sc_list); + } + return sb_sc_list; +} + + +SBValueList +SBModule::FindGlobalVariables (SBTarget &target, const char *name, uint32_t max_matches) +{ + SBValueList sb_value_list; + ModuleSP module_sp (GetSP ()); + if (name && module_sp) + { + VariableList variable_list; + const uint32_t match_count = module_sp->FindGlobalVariables (ConstString (name), + NULL, + false, + max_matches, + variable_list); + + if (match_count > 0) + { + for (uint32_t i=0; i 0) + return sb_value_list.GetValueAtIndex(0); + return SBValue(); +} + +lldb::SBType +SBModule::FindFirstType (const char *name_cstr) +{ + SBType sb_type; + ModuleSP module_sp (GetSP ()); + if (name_cstr && module_sp) + { + SymbolContext sc; + const bool exact_match = false; + ConstString name(name_cstr); + + sb_type = SBType (module_sp->FindFirstType(sc, name, exact_match)); + + if (!sb_type.IsValid()) + sb_type = SBType (ClangASTContext::GetBasicType (module_sp->GetClangASTContext().getASTContext(), name)); + } + return sb_type; +} + +lldb::SBType +SBModule::GetBasicType(lldb::BasicType type) +{ + ModuleSP module_sp (GetSP ()); + if (module_sp) + return SBType (ClangASTContext::GetBasicType (module_sp->GetClangASTContext().getASTContext(), type)); + return SBType(); +} + +lldb::SBTypeList +SBModule::FindTypes (const char *type) +{ + SBTypeList retval; + + ModuleSP module_sp (GetSP ()); + if (type && module_sp) + { + SymbolContext sc; + TypeList type_list; + const bool exact_match = false; + ConstString name(type); + const uint32_t num_matches = module_sp->FindTypes (sc, + name, + exact_match, + UINT32_MAX, + type_list); + + if (num_matches > 0) + { + for (size_t idx = 0; idx < num_matches; idx++) + { + TypeSP type_sp (type_list.GetTypeAtIndex(idx)); + if (type_sp) + retval.Append(SBType(type_sp)); + } + } + else + { + SBType sb_type(ClangASTContext::GetBasicType (module_sp->GetClangASTContext().getASTContext(), name)); + if (sb_type.IsValid()) + retval.Append(sb_type); + } + } + + return retval; +} + +lldb::SBTypeList +SBModule::GetTypes (uint32_t type_mask) +{ + SBTypeList sb_type_list; + + ModuleSP module_sp (GetSP ()); + if (module_sp) + { + SymbolVendor* vendor = module_sp->GetSymbolVendor(); + if (vendor) + { + TypeList type_list; + vendor->GetTypes (NULL, type_mask, type_list); + sb_type_list.m_opaque_ap->Append(type_list); + } + } + return sb_type_list; +} + +SBSection +SBModule::FindSection (const char *sect_name) +{ + SBSection sb_section; + + ModuleSP module_sp (GetSP ()); + if (sect_name && module_sp) + { + // Give the symbol vendor a chance to add to the unified section list. + module_sp->GetSymbolVendor(); + SectionList *section_list = module_sp->GetSectionList(); + if (section_list) + { + ConstString const_sect_name(sect_name); + SectionSP section_sp (section_list->FindSectionByName(const_sect_name)); + if (section_sp) + { + sb_section.SetSP (section_sp); + } + } + } + return sb_section; +} + +lldb::ByteOrder +SBModule::GetByteOrder () +{ + ModuleSP module_sp (GetSP ()); + if (module_sp) + return module_sp->GetArchitecture().GetByteOrder(); + return eByteOrderInvalid; +} + +const char * +SBModule::GetTriple () +{ + ModuleSP module_sp (GetSP ()); + if (module_sp) + { + std::string triple (module_sp->GetArchitecture().GetTriple().str()); + // Unique the string so we don't run into ownership issues since + // the const strings put the string into the string pool once and + // the strings never comes out + ConstString const_triple (triple.c_str()); + return const_triple.GetCString(); + } + return NULL; +} + +uint32_t +SBModule::GetAddressByteSize() +{ + ModuleSP module_sp (GetSP ()); + if (module_sp) + return module_sp->GetArchitecture().GetAddressByteSize(); + return sizeof(void*); +} + + +uint32_t +SBModule::GetVersion (uint32_t *versions, uint32_t num_versions) +{ + ModuleSP module_sp (GetSP ()); + if (module_sp) + return module_sp->GetVersion(versions, num_versions); + else + { + if (versions && num_versions) + { + for (uint32_t i=0; iClear(); +} + +SBFileSpec +SBModuleSpec::GetFileSpec () +{ + SBFileSpec sb_spec(m_opaque_ap->GetFileSpec()); + return sb_spec; +} + +void +SBModuleSpec::SetFileSpec (const lldb::SBFileSpec &sb_spec) +{ + m_opaque_ap->GetFileSpec() = *sb_spec; +} + +lldb::SBFileSpec +SBModuleSpec::GetPlatformFileSpec () +{ + return SBFileSpec(m_opaque_ap->GetPlatformFileSpec()); +} + +void +SBModuleSpec::SetPlatformFileSpec (const lldb::SBFileSpec &sb_spec) +{ + m_opaque_ap->GetPlatformFileSpec() = *sb_spec; +} + +lldb::SBFileSpec +SBModuleSpec::GetSymbolFileSpec () +{ + return SBFileSpec(m_opaque_ap->GetSymbolFileSpec()); +} + +void +SBModuleSpec::SetSymbolFileSpec (const lldb::SBFileSpec &sb_spec) +{ + m_opaque_ap->GetSymbolFileSpec() = *sb_spec; +} + +const char * +SBModuleSpec::GetObjectName () +{ + return m_opaque_ap->GetObjectName().GetCString(); +} + +void +SBModuleSpec::SetObjectName (const char *name) +{ + m_opaque_ap->GetObjectName().SetCString(name); +} + +const char * +SBModuleSpec::GetTriple () +{ + std::string triple (m_opaque_ap->GetArchitecture().GetTriple().str()); + // Unique the string so we don't run into ownership issues since + // the const strings put the string into the string pool once and + // the strings never comes out + ConstString const_triple (triple.c_str()); + return const_triple.GetCString(); +} + +void +SBModuleSpec::SetTriple (const char *triple) +{ + m_opaque_ap->GetArchitecture().SetTriple(triple); +} + +const uint8_t * +SBModuleSpec::GetUUIDBytes () +{ + return (const uint8_t *)m_opaque_ap->GetUUID().GetBytes(); +} + +size_t +SBModuleSpec::GetUUIDLength () +{ + return m_opaque_ap->GetUUID().GetByteSize(); +} + +bool +SBModuleSpec::SetUUIDBytes (const uint8_t *uuid, size_t uuid_len) +{ + return m_opaque_ap->GetUUID().SetBytes(uuid, uuid_len); +} + +bool +SBModuleSpec::GetDescription (lldb::SBStream &description) +{ + m_opaque_ap->Dump (description.ref()); + return true; +} + +SBModuleSpecList::SBModuleSpecList() : + m_opaque_ap(new ModuleSpecList()) +{ + +} + +SBModuleSpecList::SBModuleSpecList (const SBModuleSpecList &rhs) : + m_opaque_ap(new ModuleSpecList(*rhs.m_opaque_ap)) +{ + +} + +SBModuleSpecList & +SBModuleSpecList::operator = (const SBModuleSpecList &rhs) +{ + if (this != &rhs) + *m_opaque_ap = *rhs.m_opaque_ap; + return *this; +} + +SBModuleSpecList::~SBModuleSpecList() +{ + +} + +SBModuleSpecList +SBModuleSpecList::GetModuleSpecifications (const char *path) +{ + SBModuleSpecList specs; + FileSpec file_spec(path, true); + Host::ResolveExecutableInBundle(file_spec); + ObjectFile::GetModuleSpecifications(file_spec, 0, 0, *specs.m_opaque_ap); + return specs; +} + +void +SBModuleSpecList::Append (const SBModuleSpec &spec) +{ + m_opaque_ap->Append (*spec.m_opaque_ap); +} + +void +SBModuleSpecList::Append (const SBModuleSpecList &spec_list) +{ + m_opaque_ap->Append (*spec_list.m_opaque_ap); +} + +size_t +SBModuleSpecList::GetSize() +{ + return m_opaque_ap->GetSize(); +} + +SBModuleSpec +SBModuleSpecList::GetSpecAtIndex (size_t i) +{ + SBModuleSpec sb_module_spec; + m_opaque_ap->GetModuleSpecAtIndex(i, *sb_module_spec.m_opaque_ap); + return sb_module_spec; +} + +SBModuleSpec +SBModuleSpecList::FindFirstMatchingSpec (const SBModuleSpec &match_spec) +{ + SBModuleSpec sb_module_spec; + m_opaque_ap->FindMatchingModuleSpec(*match_spec.m_opaque_ap, *sb_module_spec.m_opaque_ap); + return sb_module_spec; +} + +SBModuleSpecList +SBModuleSpecList::FindMatchingSpecs (const SBModuleSpec &match_spec) +{ + SBModuleSpecList specs; + m_opaque_ap->FindMatchingModuleSpecs(*match_spec.m_opaque_ap, *specs.m_opaque_ap); + return specs; + +} + +bool +SBModuleSpecList::GetDescription (lldb::SBStream &description) +{ + m_opaque_ap->Dump (description.ref()); + return true; +} diff --git a/contrib/llvm/tools/lldb/source/API/SBProcess.cpp b/contrib/llvm/tools/lldb/source/API/SBProcess.cpp new file mode 100644 index 00000000000..259eb5e9703 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/API/SBProcess.cpp @@ -0,0 +1,1256 @@ +//===-- SBProcess.cpp -------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "lldb/API/SBProcess.h" + +#include "lldb/lldb-defines.h" +#include "lldb/lldb-types.h" + +#include "lldb/Interpreter/Args.h" +#include "lldb/Core/Debugger.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/State.h" +#include "lldb/Core/Stream.h" +#include "lldb/Core/StreamFile.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" + +// Project includes + +#include "lldb/API/SBBroadcaster.h" +#include "lldb/API/SBCommandReturnObject.h" +#include "lldb/API/SBDebugger.h" +#include "lldb/API/SBEvent.h" +#include "lldb/API/SBFileSpec.h" +#include "lldb/API/SBThread.h" +#include "lldb/API/SBStream.h" +#include "lldb/API/SBStringList.h" + +using namespace lldb; +using namespace lldb_private; + + +SBProcess::SBProcess () : + m_opaque_wp() +{ +} + + +//---------------------------------------------------------------------- +// SBProcess constructor +//---------------------------------------------------------------------- + +SBProcess::SBProcess (const SBProcess& rhs) : + m_opaque_wp (rhs.m_opaque_wp) +{ +} + + +SBProcess::SBProcess (const lldb::ProcessSP &process_sp) : + m_opaque_wp (process_sp) +{ +} + +const SBProcess& +SBProcess::operator = (const SBProcess& rhs) +{ + if (this != &rhs) + m_opaque_wp = rhs.m_opaque_wp; + return *this; +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +SBProcess::~SBProcess() +{ +} + +const char * +SBProcess::GetBroadcasterClassName () +{ + return Process::GetStaticBroadcasterClass().AsCString(); +} + +const char * +SBProcess::GetPluginName () +{ + ProcessSP process_sp(GetSP()); + if (process_sp) + { + return process_sp->GetPluginName().GetCString(); + } + return ""; +} + +const char * +SBProcess::GetShortPluginName () +{ + ProcessSP process_sp(GetSP()); + if (process_sp) + { + return process_sp->GetPluginName().GetCString(); + } + return ""; +} + + +lldb::ProcessSP +SBProcess::GetSP() const +{ + return m_opaque_wp.lock(); +} + +void +SBProcess::SetSP (const ProcessSP &process_sp) +{ + m_opaque_wp = process_sp; +} + +void +SBProcess::Clear () +{ + m_opaque_wp.reset(); +} + + +bool +SBProcess::IsValid() const +{ + ProcessSP process_sp(m_opaque_wp.lock()); + return ((bool) process_sp && process_sp->IsValid()); +} + +bool +SBProcess::RemoteLaunch (char const **argv, + char const **envp, + const char *stdin_path, + const char *stdout_path, + const char *stderr_path, + const char *working_directory, + uint32_t launch_flags, + bool stop_at_entry, + lldb::SBError& error) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (log) { + log->Printf ("SBProcess(%p)::RemoteLaunch (argv=%p, envp=%p, stdin=%s, stdout=%s, stderr=%s, working-dir=%s, launch_flags=0x%x, stop_at_entry=%i, &error (%p))...", + m_opaque_wp.lock().get(), + argv, + envp, + stdin_path ? stdin_path : "NULL", + stdout_path ? stdout_path : "NULL", + stderr_path ? stderr_path : "NULL", + working_directory ? working_directory : "NULL", + launch_flags, + stop_at_entry, + error.get()); + } + + ProcessSP process_sp(GetSP()); + if (process_sp) + { + Mutex::Locker api_locker (process_sp->GetTarget().GetAPIMutex()); + if (process_sp->GetState() == eStateConnected) + { + if (stop_at_entry) + launch_flags |= eLaunchFlagStopAtEntry; + ProcessLaunchInfo launch_info (stdin_path, + stdout_path, + stderr_path, + working_directory, + launch_flags); + Module *exe_module = process_sp->GetTarget().GetExecutableModulePointer(); + if (exe_module) + launch_info.SetExecutableFile(exe_module->GetPlatformFileSpec(), true); + if (argv) + launch_info.GetArguments().AppendArguments (argv); + if (envp) + launch_info.GetEnvironmentEntries ().SetArguments (envp); + error.SetError (process_sp->Launch (launch_info)); + } + else + { + error.SetErrorString ("must be in eStateConnected to call RemoteLaunch"); + } + } + else + { + error.SetErrorString ("unable to attach pid"); + } + + if (log) { + SBStream sstr; + error.GetDescription (sstr); + log->Printf ("SBProcess(%p)::RemoteLaunch (...) => SBError (%p): %s", process_sp.get(), error.get(), sstr.GetData()); + } + + return error.Success(); +} + +bool +SBProcess::RemoteAttachToProcessWithID (lldb::pid_t pid, lldb::SBError& error) +{ + ProcessSP process_sp(GetSP()); + if (process_sp) + { + Mutex::Locker api_locker (process_sp->GetTarget().GetAPIMutex()); + if (process_sp->GetState() == eStateConnected) + { + ProcessAttachInfo attach_info; + attach_info.SetProcessID (pid); + error.SetError (process_sp->Attach (attach_info)); + } + else + { + error.SetErrorString ("must be in eStateConnected to call RemoteAttachToProcessWithID"); + } + } + else + { + error.SetErrorString ("unable to attach pid"); + } + + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (log) { + SBStream sstr; + error.GetDescription (sstr); + log->Printf ("SBProcess(%p)::RemoteAttachToProcessWithID (%" PRIu64 ") => SBError (%p): %s", process_sp.get(), pid, error.get(), sstr.GetData()); + } + + return error.Success(); +} + + +uint32_t +SBProcess::GetNumThreads () +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + uint32_t num_threads = 0; + ProcessSP process_sp(GetSP()); + if (process_sp) + { + Process::StopLocker stop_locker; + + const bool can_update = stop_locker.TryLock(&process_sp->GetRunLock()); + Mutex::Locker api_locker (process_sp->GetTarget().GetAPIMutex()); + num_threads = process_sp->GetThreadList().GetSize(can_update); + } + + if (log) + log->Printf ("SBProcess(%p)::GetNumThreads () => %d", process_sp.get(), num_threads); + + return num_threads; +} + +SBThread +SBProcess::GetSelectedThread () const +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + SBThread sb_thread; + ThreadSP thread_sp; + ProcessSP process_sp(GetSP()); + if (process_sp) + { + Mutex::Locker api_locker (process_sp->GetTarget().GetAPIMutex()); + thread_sp = process_sp->GetThreadList().GetSelectedThread(); + sb_thread.SetThread (thread_sp); + } + + if (log) + { + log->Printf ("SBProcess(%p)::GetSelectedThread () => SBThread(%p)", process_sp.get(), thread_sp.get()); + } + + return sb_thread; +} + +SBThread +SBProcess::CreateOSPluginThread (lldb::tid_t tid, lldb::addr_t context) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + SBThread sb_thread; + ThreadSP thread_sp; + ProcessSP process_sp(GetSP()); + if (process_sp) + { + Mutex::Locker api_locker (process_sp->GetTarget().GetAPIMutex()); + thread_sp = process_sp->CreateOSPluginThread(tid, context); + sb_thread.SetThread (thread_sp); + } + + if (log) + log->Printf ("SBProcess(%p)::CreateOSPluginThread (tid=0x%" PRIx64 ", context=0x%" PRIx64 ") => SBThread(%p)", process_sp.get(), tid, context, thread_sp.get()); + + return sb_thread; +} + +SBTarget +SBProcess::GetTarget() const +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + SBTarget sb_target; + TargetSP target_sp; + ProcessSP process_sp(GetSP()); + if (process_sp) + { + target_sp = process_sp->GetTarget().shared_from_this(); + sb_target.SetSP (target_sp); + } + + if (log) + log->Printf ("SBProcess(%p)::GetTarget () => SBTarget(%p)", process_sp.get(), target_sp.get()); + + return sb_target; +} + + +size_t +SBProcess::PutSTDIN (const char *src, size_t src_len) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + size_t ret_val = 0; + ProcessSP process_sp(GetSP()); + if (process_sp) + { + Error error; + ret_val = process_sp->PutSTDIN (src, src_len, error); + } + + if (log) + log->Printf ("SBProcess(%p)::PutSTDIN (src=\"%s\", src_len=%d) => %lu", + process_sp.get(), + src, + (uint32_t) src_len, + ret_val); + + return ret_val; +} + +size_t +SBProcess::GetSTDOUT (char *dst, size_t dst_len) const +{ + size_t bytes_read = 0; + ProcessSP process_sp(GetSP()); + if (process_sp) + { + Error error; + bytes_read = process_sp->GetSTDOUT (dst, dst_len, error); + } + + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (log) + log->Printf ("SBProcess(%p)::GetSTDOUT (dst=\"%.*s\", dst_len=%" PRIu64 ") => %" PRIu64, + process_sp.get(), + (int) bytes_read, + dst, + (uint64_t)dst_len, + (uint64_t)bytes_read); + + return bytes_read; +} + +size_t +SBProcess::GetSTDERR (char *dst, size_t dst_len) const +{ + size_t bytes_read = 0; + ProcessSP process_sp(GetSP()); + if (process_sp) + { + Error error; + bytes_read = process_sp->GetSTDERR (dst, dst_len, error); + } + + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (log) + log->Printf ("SBProcess(%p)::GetSTDERR (dst=\"%.*s\", dst_len=%" PRIu64 ") => %" PRIu64, + process_sp.get(), + (int) bytes_read, + dst, + (uint64_t)dst_len, + (uint64_t)bytes_read); + + return bytes_read; +} + +size_t +SBProcess::GetAsyncProfileData(char *dst, size_t dst_len) const +{ + size_t bytes_read = 0; + ProcessSP process_sp(GetSP()); + if (process_sp) + { + Error error; + bytes_read = process_sp->GetAsyncProfileData (dst, dst_len, error); + } + + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (log) + log->Printf ("SBProcess(%p)::GetProfileData (dst=\"%.*s\", dst_len=%" PRIu64 ") => %" PRIu64, + process_sp.get(), + (int) bytes_read, + dst, + (uint64_t)dst_len, + (uint64_t)bytes_read); + + return bytes_read; +} + +void +SBProcess::ReportEventState (const SBEvent &event, FILE *out) const +{ + if (out == NULL) + return; + + ProcessSP process_sp(GetSP()); + if (process_sp) + { + const StateType event_state = SBProcess::GetStateFromEvent (event); + char message[1024]; + int message_len = ::snprintf (message, + sizeof (message), + "Process %" PRIu64 " %s\n", + process_sp->GetID(), + SBDebugger::StateAsCString (event_state)); + + if (message_len > 0) + ::fwrite (message, 1, message_len, out); + } +} + +void +SBProcess::AppendEventStateReport (const SBEvent &event, SBCommandReturnObject &result) +{ + ProcessSP process_sp(GetSP()); + if (process_sp) + { + const StateType event_state = SBProcess::GetStateFromEvent (event); + char message[1024]; + ::snprintf (message, + sizeof (message), + "Process %" PRIu64 " %s\n", + process_sp->GetID(), + SBDebugger::StateAsCString (event_state)); + + result.AppendMessage (message); + } +} + +bool +SBProcess::SetSelectedThread (const SBThread &thread) +{ + ProcessSP process_sp(GetSP()); + if (process_sp) + { + Mutex::Locker api_locker (process_sp->GetTarget().GetAPIMutex()); + return process_sp->GetThreadList().SetSelectedThreadByID (thread.GetThreadID()); + } + return false; +} + +bool +SBProcess::SetSelectedThreadByID (lldb::tid_t tid) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + bool ret_val = false; + ProcessSP process_sp(GetSP()); + if (process_sp) + { + Mutex::Locker api_locker (process_sp->GetTarget().GetAPIMutex()); + ret_val = process_sp->GetThreadList().SetSelectedThreadByID (tid); + } + + if (log) + log->Printf ("SBProcess(%p)::SetSelectedThreadByID (tid=0x%4.4" PRIx64 ") => %s", + process_sp.get(), tid, (ret_val ? "true" : "false")); + + return ret_val; +} + +bool +SBProcess::SetSelectedThreadByIndexID (uint32_t index_id) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + bool ret_val = false; + ProcessSP process_sp(GetSP()); + if (process_sp) + { + Mutex::Locker api_locker (process_sp->GetTarget().GetAPIMutex()); + ret_val = process_sp->GetThreadList().SetSelectedThreadByIndexID (index_id); + } + + if (log) + log->Printf ("SBProcess(%p)::SetSelectedThreadByID (tid=0x%x) => %s", + process_sp.get(), index_id, (ret_val ? "true" : "false")); + + return ret_val; +} + +SBThread +SBProcess::GetThreadAtIndex (size_t index) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + SBThread sb_thread; + ThreadSP thread_sp; + ProcessSP process_sp(GetSP()); + if (process_sp) + { + Process::StopLocker stop_locker; + const bool can_update = stop_locker.TryLock(&process_sp->GetRunLock()); + Mutex::Locker api_locker (process_sp->GetTarget().GetAPIMutex()); + thread_sp = process_sp->GetThreadList().GetThreadAtIndex(index, can_update); + sb_thread.SetThread (thread_sp); + } + + if (log) + { + log->Printf ("SBProcess(%p)::GetThreadAtIndex (index=%d) => SBThread(%p)", + process_sp.get(), (uint32_t) index, thread_sp.get()); + } + + return sb_thread; +} + +uint32_t +SBProcess::GetStopID(bool include_expression_stops) +{ + ProcessSP process_sp(GetSP()); + if (process_sp) + { + Mutex::Locker api_locker (process_sp->GetTarget().GetAPIMutex()); + if (include_expression_stops) + return process_sp->GetStopID(); + else + return process_sp->GetLastNaturalStopID(); + } + return 0; +} + +StateType +SBProcess::GetState () +{ + + StateType ret_val = eStateInvalid; + ProcessSP process_sp(GetSP()); + if (process_sp) + { + Mutex::Locker api_locker (process_sp->GetTarget().GetAPIMutex()); + ret_val = process_sp->GetState(); + } + + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (log) + log->Printf ("SBProcess(%p)::GetState () => %s", + process_sp.get(), + lldb_private::StateAsCString (ret_val)); + + return ret_val; +} + + +int +SBProcess::GetExitStatus () +{ + int exit_status = 0; + ProcessSP process_sp(GetSP()); + if (process_sp) + { + Mutex::Locker api_locker (process_sp->GetTarget().GetAPIMutex()); + exit_status = process_sp->GetExitStatus (); + } + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (log) + log->Printf ("SBProcess(%p)::GetExitStatus () => %i (0x%8.8x)", + process_sp.get(), exit_status, exit_status); + + return exit_status; +} + +const char * +SBProcess::GetExitDescription () +{ + const char *exit_desc = NULL; + ProcessSP process_sp(GetSP()); + if (process_sp) + { + Mutex::Locker api_locker (process_sp->GetTarget().GetAPIMutex()); + exit_desc = process_sp->GetExitDescription (); + } + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (log) + log->Printf ("SBProcess(%p)::GetExitDescription () => %s", + process_sp.get(), exit_desc); + return exit_desc; +} + +lldb::pid_t +SBProcess::GetProcessID () +{ + lldb::pid_t ret_val = LLDB_INVALID_PROCESS_ID; + ProcessSP process_sp(GetSP()); + if (process_sp) + ret_val = process_sp->GetID(); + + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (log) + log->Printf ("SBProcess(%p)::GetProcessID () => %" PRIu64, process_sp.get(), ret_val); + + return ret_val; +} + +uint32_t +SBProcess::GetUniqueID() +{ + uint32_t ret_val = 0; + ProcessSP process_sp(GetSP()); + if (process_sp) + ret_val = process_sp->GetUniqueID(); + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (log) + log->Printf ("SBProcess(%p)::GetUniqueID () => %" PRIu32, process_sp.get(), ret_val); + return ret_val; +} + +ByteOrder +SBProcess::GetByteOrder () const +{ + ByteOrder byteOrder = eByteOrderInvalid; + ProcessSP process_sp(GetSP()); + if (process_sp) + byteOrder = process_sp->GetTarget().GetArchitecture().GetByteOrder(); + + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (log) + log->Printf ("SBProcess(%p)::GetByteOrder () => %d", process_sp.get(), byteOrder); + + return byteOrder; +} + +uint32_t +SBProcess::GetAddressByteSize () const +{ + uint32_t size = 0; + ProcessSP process_sp(GetSP()); + if (process_sp) + size = process_sp->GetTarget().GetArchitecture().GetAddressByteSize(); + + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (log) + log->Printf ("SBProcess(%p)::GetAddressByteSize () => %d", process_sp.get(), size); + + return size; +} + +SBError +SBProcess::Continue () +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + SBError sb_error; + ProcessSP process_sp(GetSP()); + + if (log) + log->Printf ("SBProcess(%p)::Continue ()...", process_sp.get()); + + if (process_sp) + { + Mutex::Locker api_locker (process_sp->GetTarget().GetAPIMutex()); + + Error error (process_sp->Resume()); + if (error.Success()) + { + if (process_sp->GetTarget().GetDebugger().GetAsyncExecution () == false) + { + if (log) + log->Printf ("SBProcess(%p)::Continue () waiting for process to stop...", process_sp.get()); + process_sp->WaitForProcessToStop (NULL); + } + } + sb_error.SetError(error); + } + else + sb_error.SetErrorString ("SBProcess is invalid"); + + if (log) + { + SBStream sstr; + sb_error.GetDescription (sstr); + log->Printf ("SBProcess(%p)::Continue () => SBError (%p): %s", process_sp.get(), sb_error.get(), sstr.GetData()); + } + + return sb_error; +} + + +SBError +SBProcess::Destroy () +{ + SBError sb_error; + ProcessSP process_sp(GetSP()); + if (process_sp) + { + Mutex::Locker api_locker (process_sp->GetTarget().GetAPIMutex()); + sb_error.SetError(process_sp->Destroy()); + } + else + sb_error.SetErrorString ("SBProcess is invalid"); + + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (log) + { + SBStream sstr; + sb_error.GetDescription (sstr); + log->Printf ("SBProcess(%p)::Destroy () => SBError (%p): %s", + process_sp.get(), + sb_error.get(), + sstr.GetData()); + } + + return sb_error; +} + + +SBError +SBProcess::Stop () +{ + SBError sb_error; + ProcessSP process_sp(GetSP()); + if (process_sp) + { + Mutex::Locker api_locker (process_sp->GetTarget().GetAPIMutex()); + sb_error.SetError (process_sp->Halt()); + } + else + sb_error.SetErrorString ("SBProcess is invalid"); + + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (log) + { + SBStream sstr; + sb_error.GetDescription (sstr); + log->Printf ("SBProcess(%p)::Stop () => SBError (%p): %s", + process_sp.get(), + sb_error.get(), + sstr.GetData()); + } + + return sb_error; +} + +SBError +SBProcess::Kill () +{ + SBError sb_error; + ProcessSP process_sp(GetSP()); + if (process_sp) + { + Mutex::Locker api_locker (process_sp->GetTarget().GetAPIMutex()); + sb_error.SetError (process_sp->Destroy()); + } + else + sb_error.SetErrorString ("SBProcess is invalid"); + + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (log) + { + SBStream sstr; + sb_error.GetDescription (sstr); + log->Printf ("SBProcess(%p)::Kill () => SBError (%p): %s", + process_sp.get(), + sb_error.get(), + sstr.GetData()); + } + + return sb_error; +} + +SBError +SBProcess::Detach () +{ + // FIXME: This should come from a process default. + bool keep_stopped = false; + return Detach (keep_stopped); +} + +SBError +SBProcess::Detach (bool keep_stopped) +{ + SBError sb_error; + ProcessSP process_sp(GetSP()); + if (process_sp) + { + Mutex::Locker api_locker (process_sp->GetTarget().GetAPIMutex()); + sb_error.SetError (process_sp->Detach(keep_stopped)); + } + else + sb_error.SetErrorString ("SBProcess is invalid"); + + return sb_error; +} + +SBError +SBProcess::Signal (int signo) +{ + SBError sb_error; + ProcessSP process_sp(GetSP()); + if (process_sp) + { + Mutex::Locker api_locker (process_sp->GetTarget().GetAPIMutex()); + sb_error.SetError (process_sp->Signal (signo)); + } + else + sb_error.SetErrorString ("SBProcess is invalid"); + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (log) + { + SBStream sstr; + sb_error.GetDescription (sstr); + log->Printf ("SBProcess(%p)::Signal (signo=%i) => SBError (%p): %s", + process_sp.get(), + signo, + sb_error.get(), + sstr.GetData()); + } + return sb_error; +} + +void +SBProcess::SendAsyncInterrupt () +{ + ProcessSP process_sp(GetSP()); + if (process_sp) + { + process_sp->SendAsyncInterrupt (); + } +} + +SBThread +SBProcess::GetThreadByID (tid_t tid) +{ + SBThread sb_thread; + ThreadSP thread_sp; + ProcessSP process_sp(GetSP()); + if (process_sp) + { + Mutex::Locker api_locker (process_sp->GetTarget().GetAPIMutex()); + Process::StopLocker stop_locker; + const bool can_update = stop_locker.TryLock(&process_sp->GetRunLock()); + thread_sp = process_sp->GetThreadList().FindThreadByID (tid, can_update); + sb_thread.SetThread (thread_sp); + } + + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (log) + { + log->Printf ("SBProcess(%p)::GetThreadByID (tid=0x%4.4" PRIx64 ") => SBThread (%p)", + process_sp.get(), + tid, + thread_sp.get()); + } + + return sb_thread; +} + +SBThread +SBProcess::GetThreadByIndexID (uint32_t index_id) +{ + SBThread sb_thread; + ThreadSP thread_sp; + ProcessSP process_sp(GetSP()); + if (process_sp) + { + Mutex::Locker api_locker (process_sp->GetTarget().GetAPIMutex()); + Process::StopLocker stop_locker; + const bool can_update = stop_locker.TryLock(&process_sp->GetRunLock()); + thread_sp = process_sp->GetThreadList().FindThreadByIndexID (index_id, can_update); + sb_thread.SetThread (thread_sp); + } + + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (log) + { + log->Printf ("SBProcess(%p)::GetThreadByID (tid=0x%x) => SBThread (%p)", + process_sp.get(), + index_id, + thread_sp.get()); + } + + return sb_thread; +} + +StateType +SBProcess::GetStateFromEvent (const SBEvent &event) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + StateType ret_val = Process::ProcessEventData::GetStateFromEvent (event.get()); + + if (log) + log->Printf ("SBProcess::GetStateFromEvent (event.sp=%p) => %s", event.get(), + lldb_private::StateAsCString (ret_val)); + + return ret_val; +} + +bool +SBProcess::GetRestartedFromEvent (const SBEvent &event) +{ + return Process::ProcessEventData::GetRestartedFromEvent (event.get()); +} + +size_t +SBProcess::GetNumRestartedReasonsFromEvent (const lldb::SBEvent &event) +{ + return Process::ProcessEventData::GetNumRestartedReasons(event.get()); +} + +const char * +SBProcess::GetRestartedReasonAtIndexFromEvent (const lldb::SBEvent &event, size_t idx) +{ + return Process::ProcessEventData::GetRestartedReasonAtIndex(event.get(), idx); +} + +SBProcess +SBProcess::GetProcessFromEvent (const SBEvent &event) +{ + SBProcess process(Process::ProcessEventData::GetProcessFromEvent (event.get())); + return process; +} + +bool +SBProcess::EventIsProcessEvent (const SBEvent &event) +{ + return strcmp (event.GetBroadcasterClass(), SBProcess::GetBroadcasterClass()) == 0; +} + +SBBroadcaster +SBProcess::GetBroadcaster () const +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + ProcessSP process_sp(GetSP()); + + SBBroadcaster broadcaster(process_sp.get(), false); + + if (log) + log->Printf ("SBProcess(%p)::GetBroadcaster () => SBBroadcaster (%p)", process_sp.get(), + broadcaster.get()); + + return broadcaster; +} + +const char * +SBProcess::GetBroadcasterClass () +{ + return Process::GetStaticBroadcasterClass().AsCString(); +} + +size_t +SBProcess::ReadMemory (addr_t addr, void *dst, size_t dst_len, SBError &sb_error) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + size_t bytes_read = 0; + + ProcessSP process_sp(GetSP()); + + if (log) + { + log->Printf ("SBProcess(%p)::ReadMemory (addr=0x%" PRIx64 ", dst=%p, dst_len=%" PRIu64 ", SBError (%p))...", + process_sp.get(), + addr, + dst, + (uint64_t)dst_len, + sb_error.get()); + } + + if (process_sp) + { + Process::StopLocker stop_locker; + if (stop_locker.TryLock(&process_sp->GetRunLock())) + { + Mutex::Locker api_locker (process_sp->GetTarget().GetAPIMutex()); + bytes_read = process_sp->ReadMemory (addr, dst, dst_len, sb_error.ref()); + } + else + { + if (log) + log->Printf ("SBProcess(%p)::ReadMemory() => error: process is running", process_sp.get()); + sb_error.SetErrorString("process is running"); + } + } + else + { + sb_error.SetErrorString ("SBProcess is invalid"); + } + + if (log) + { + SBStream sstr; + sb_error.GetDescription (sstr); + log->Printf ("SBProcess(%p)::ReadMemory (addr=0x%" PRIx64 ", dst=%p, dst_len=%" PRIu64 ", SBError (%p): %s) => %" PRIu64, + process_sp.get(), + addr, + dst, + (uint64_t)dst_len, + sb_error.get(), + sstr.GetData(), + (uint64_t)bytes_read); + } + + return bytes_read; +} + +size_t +SBProcess::ReadCStringFromMemory (addr_t addr, void *buf, size_t size, lldb::SBError &sb_error) +{ + size_t bytes_read = 0; + ProcessSP process_sp(GetSP()); + if (process_sp) + { + Process::StopLocker stop_locker; + if (stop_locker.TryLock(&process_sp->GetRunLock())) + { + Mutex::Locker api_locker (process_sp->GetTarget().GetAPIMutex()); + bytes_read = process_sp->ReadCStringFromMemory (addr, (char *)buf, size, sb_error.ref()); + } + else + { + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (log) + log->Printf ("SBProcess(%p)::ReadCStringFromMemory() => error: process is running", process_sp.get()); + sb_error.SetErrorString("process is running"); + } + } + else + { + sb_error.SetErrorString ("SBProcess is invalid"); + } + return bytes_read; +} + +uint64_t +SBProcess::ReadUnsignedFromMemory (addr_t addr, uint32_t byte_size, lldb::SBError &sb_error) +{ + uint64_t value = 0; + ProcessSP process_sp(GetSP()); + if (process_sp) + { + Process::StopLocker stop_locker; + if (stop_locker.TryLock(&process_sp->GetRunLock())) + { + Mutex::Locker api_locker (process_sp->GetTarget().GetAPIMutex()); + value = process_sp->ReadUnsignedIntegerFromMemory (addr, byte_size, 0, sb_error.ref()); + } + else + { + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (log) + log->Printf ("SBProcess(%p)::ReadUnsignedFromMemory() => error: process is running", process_sp.get()); + sb_error.SetErrorString("process is running"); + } + } + else + { + sb_error.SetErrorString ("SBProcess is invalid"); + } + return value; +} + +lldb::addr_t +SBProcess::ReadPointerFromMemory (addr_t addr, lldb::SBError &sb_error) +{ + lldb::addr_t ptr = LLDB_INVALID_ADDRESS; + ProcessSP process_sp(GetSP()); + if (process_sp) + { + Process::StopLocker stop_locker; + if (stop_locker.TryLock(&process_sp->GetRunLock())) + { + Mutex::Locker api_locker (process_sp->GetTarget().GetAPIMutex()); + ptr = process_sp->ReadPointerFromMemory (addr, sb_error.ref()); + } + else + { + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (log) + log->Printf ("SBProcess(%p)::ReadPointerFromMemory() => error: process is running", process_sp.get()); + sb_error.SetErrorString("process is running"); + } + } + else + { + sb_error.SetErrorString ("SBProcess is invalid"); + } + return ptr; +} + +size_t +SBProcess::WriteMemory (addr_t addr, const void *src, size_t src_len, SBError &sb_error) +{ + size_t bytes_written = 0; + + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + ProcessSP process_sp(GetSP()); + + if (log) + { + log->Printf ("SBProcess(%p)::WriteMemory (addr=0x%" PRIx64 ", src=%p, src_len=%" PRIu64 ", SBError (%p))...", + process_sp.get(), + addr, + src, + (uint64_t)src_len, + sb_error.get()); + } + + if (process_sp) + { + Process::StopLocker stop_locker; + if (stop_locker.TryLock(&process_sp->GetRunLock())) + { + Mutex::Locker api_locker (process_sp->GetTarget().GetAPIMutex()); + bytes_written = process_sp->WriteMemory (addr, src, src_len, sb_error.ref()); + } + else + { + if (log) + log->Printf ("SBProcess(%p)::WriteMemory() => error: process is running", process_sp.get()); + sb_error.SetErrorString("process is running"); + } + } + + if (log) + { + SBStream sstr; + sb_error.GetDescription (sstr); + log->Printf ("SBProcess(%p)::WriteMemory (addr=0x%" PRIx64 ", src=%p, src_len=%" PRIu64 ", SBError (%p): %s) => %" PRIu64, + process_sp.get(), + addr, + src, + (uint64_t)src_len, + sb_error.get(), + sstr.GetData(), + (uint64_t)bytes_written); + } + + return bytes_written; +} + +bool +SBProcess::GetDescription (SBStream &description) +{ + Stream &strm = description.ref(); + + ProcessSP process_sp(GetSP()); + if (process_sp) + { + char path[PATH_MAX]; + GetTarget().GetExecutable().GetPath (path, sizeof(path)); + Module *exe_module = process_sp->GetTarget().GetExecutableModulePointer(); + const char *exe_name = NULL; + if (exe_module) + exe_name = exe_module->GetFileSpec().GetFilename().AsCString(); + + strm.Printf ("SBProcess: pid = %" PRIu64 ", state = %s, threads = %d%s%s", + process_sp->GetID(), + lldb_private::StateAsCString (GetState()), + GetNumThreads(), + exe_name ? ", executable = " : "", + exe_name ? exe_name : ""); + } + else + strm.PutCString ("No value"); + + return true; +} + +uint32_t +SBProcess::GetNumSupportedHardwareWatchpoints (lldb::SBError &sb_error) const +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + uint32_t num = 0; + ProcessSP process_sp(GetSP()); + if (process_sp) + { + Mutex::Locker api_locker (process_sp->GetTarget().GetAPIMutex()); + sb_error.SetError(process_sp->GetWatchpointSupportInfo (num)); + if (log) + log->Printf ("SBProcess(%p)::GetNumSupportedHardwareWatchpoints () => %u", + process_sp.get(), num); + } + else + { + sb_error.SetErrorString ("SBProcess is invalid"); + } + return num; +} + +uint32_t +SBProcess::LoadImage (lldb::SBFileSpec &sb_image_spec, lldb::SBError &sb_error) +{ + ProcessSP process_sp(GetSP()); + if (process_sp) + { + Process::StopLocker stop_locker; + if (stop_locker.TryLock(&process_sp->GetRunLock())) + { + Mutex::Locker api_locker (process_sp->GetTarget().GetAPIMutex()); + return process_sp->LoadImage (*sb_image_spec, sb_error.ref()); + } + else + { + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (log) + log->Printf ("SBProcess(%p)::LoadImage() => error: process is running", process_sp.get()); + sb_error.SetErrorString("process is running"); + } + } + return LLDB_INVALID_IMAGE_TOKEN; +} + +lldb::SBError +SBProcess::UnloadImage (uint32_t image_token) +{ + lldb::SBError sb_error; + ProcessSP process_sp(GetSP()); + if (process_sp) + { + Process::StopLocker stop_locker; + if (stop_locker.TryLock(&process_sp->GetRunLock())) + { + Mutex::Locker api_locker (process_sp->GetTarget().GetAPIMutex()); + sb_error.SetError (process_sp->UnloadImage (image_token)); + } + else + { + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (log) + log->Printf ("SBProcess(%p)::UnloadImage() => error: process is running", process_sp.get()); + sb_error.SetErrorString("process is running"); + } + } + else + sb_error.SetErrorString("invalid process"); + return sb_error; +} diff --git a/contrib/llvm/tools/lldb/source/API/SBSection.cpp b/contrib/llvm/tools/lldb/source/API/SBSection.cpp new file mode 100644 index 00000000000..3fb84e81465 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/API/SBSection.cpp @@ -0,0 +1,291 @@ +//===-- SBSection.cpp -------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/API/SBSection.h" +#include "lldb/API/SBStream.h" +#include "lldb/API/SBTarget.h" +#include "lldb/Core/DataBuffer.h" +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/Section.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Symbol/ObjectFile.h" + + +using namespace lldb; +using namespace lldb_private; + + +SBSection::SBSection () : + m_opaque_wp () +{ +} + +SBSection::SBSection (const SBSection &rhs) : + m_opaque_wp (rhs.m_opaque_wp) +{ +} + + + +SBSection::SBSection (const lldb::SectionSP §ion_sp) : + m_opaque_wp () // Don't init with section_sp otherwise this will throw if section_sp doesn't contain a valid Section * +{ + if (section_sp) + m_opaque_wp = section_sp; +} + +const SBSection & +SBSection::operator = (const SBSection &rhs) +{ + m_opaque_wp = rhs.m_opaque_wp; + return *this; +} + +SBSection::~SBSection () +{ +} + +bool +SBSection::IsValid () const +{ + SectionSP section_sp (GetSP()); + return section_sp && section_sp->GetModule().get() != NULL; +} + +const char * +SBSection::GetName () +{ + SectionSP section_sp (GetSP()); + if (section_sp) + return section_sp->GetName().GetCString(); + return NULL; +} + +lldb::SBSection +SBSection::GetParent() +{ + lldb::SBSection sb_section; + SectionSP section_sp (GetSP()); + if (section_sp) + { + SectionSP parent_section_sp (section_sp->GetParent()); + if (parent_section_sp) + sb_section.SetSP(parent_section_sp); + } + return sb_section; +} + + +lldb::SBSection +SBSection::FindSubSection (const char *sect_name) +{ + lldb::SBSection sb_section; + if (sect_name) + { + SectionSP section_sp (GetSP()); + if (section_sp) + { + ConstString const_sect_name(sect_name); + sb_section.SetSP(section_sp->GetChildren ().FindSectionByName(const_sect_name)); + } + } + return sb_section; +} + +size_t +SBSection::GetNumSubSections () +{ + SectionSP section_sp (GetSP()); + if (section_sp) + return section_sp->GetChildren ().GetSize(); + return 0; +} + +lldb::SBSection +SBSection::GetSubSectionAtIndex (size_t idx) +{ + lldb::SBSection sb_section; + SectionSP section_sp (GetSP()); + if (section_sp) + sb_section.SetSP (section_sp->GetChildren ().GetSectionAtIndex(idx)); + return sb_section; +} + +lldb::SectionSP +SBSection::GetSP() const +{ + return m_opaque_wp.lock(); +} + +void +SBSection::SetSP(const lldb::SectionSP §ion_sp) +{ + m_opaque_wp = section_sp; +} + +lldb::addr_t +SBSection::GetFileAddress () +{ + lldb::addr_t file_addr = LLDB_INVALID_ADDRESS; + SectionSP section_sp (GetSP()); + if (section_sp) + return section_sp->GetFileAddress(); + return file_addr; +} + +lldb::addr_t +SBSection::GetLoadAddress (lldb::SBTarget &sb_target) +{ + TargetSP target_sp(sb_target.GetSP()); + if (target_sp) + { + SectionSP section_sp (GetSP()); + if (section_sp) + return section_sp->GetLoadBaseAddress(target_sp.get()); + } + return LLDB_INVALID_ADDRESS; + +} + + + +lldb::addr_t +SBSection::GetByteSize () +{ + SectionSP section_sp (GetSP()); + if (section_sp) + return section_sp->GetByteSize(); + return 0; +} + +uint64_t +SBSection::GetFileOffset () +{ + SectionSP section_sp (GetSP()); + if (section_sp) + { + ModuleSP module_sp (section_sp->GetModule()); + if (module_sp) + { + ObjectFile *objfile = module_sp->GetObjectFile(); + if (objfile) + return objfile->GetFileOffset() + section_sp->GetFileOffset(); + } + } + return UINT64_MAX; +} + +uint64_t +SBSection::GetFileByteSize () +{ + SectionSP section_sp (GetSP()); + if (section_sp) + return section_sp->GetFileSize(); + return 0; +} + +SBData +SBSection::GetSectionData () +{ + return GetSectionData (0, UINT64_MAX); +} + +SBData +SBSection::GetSectionData (uint64_t offset, uint64_t size) +{ + SBData sb_data; + SectionSP section_sp (GetSP()); + if (section_sp) + { + const uint64_t sect_file_size = section_sp->GetFileSize(); + if (sect_file_size > 0) + { + ModuleSP module_sp (section_sp->GetModule()); + if (module_sp) + { + ObjectFile *objfile = module_sp->GetObjectFile(); + if (objfile) + { + const uint64_t sect_file_offset = objfile->GetFileOffset() + section_sp->GetFileOffset(); + const uint64_t file_offset = sect_file_offset + offset; + uint64_t file_size = size; + if (file_size == UINT64_MAX) + { + file_size = section_sp->GetByteSize(); + if (file_size > offset) + file_size -= offset; + else + file_size = 0; + } + DataBufferSP data_buffer_sp (objfile->GetFileSpec().ReadFileContents (file_offset, file_size)); + if (data_buffer_sp && data_buffer_sp->GetByteSize() > 0) + { + DataExtractorSP data_extractor_sp (new DataExtractor (data_buffer_sp, + objfile->GetByteOrder(), + objfile->GetAddressByteSize())); + + sb_data.SetOpaque (data_extractor_sp); + } + } + } + } + } + return sb_data; +} + +SectionType +SBSection::GetSectionType () +{ + SectionSP section_sp (GetSP()); + if (section_sp.get()) + return section_sp->GetType(); + return eSectionTypeInvalid; +} + + +bool +SBSection::operator == (const SBSection &rhs) +{ + SectionSP lhs_section_sp (GetSP()); + SectionSP rhs_section_sp (rhs.GetSP()); + if (lhs_section_sp && rhs_section_sp) + return lhs_section_sp == rhs_section_sp; + return false; +} + +bool +SBSection::operator != (const SBSection &rhs) +{ + SectionSP lhs_section_sp (GetSP()); + SectionSP rhs_section_sp (rhs.GetSP()); + return lhs_section_sp != rhs_section_sp; +} + +bool +SBSection::GetDescription (SBStream &description) +{ + Stream &strm = description.ref(); + + SectionSP section_sp (GetSP()); + if (section_sp) + { + const addr_t file_addr = section_sp->GetFileAddress(); + strm.Printf ("[0x%16.16" PRIx64 "-0x%16.16" PRIx64 ") ", file_addr, file_addr + section_sp->GetByteSize()); + section_sp->DumpName(&strm); + } + else + { + strm.PutCString ("No value"); + } + + return true; +} + diff --git a/contrib/llvm/tools/lldb/source/API/SBSourceManager.cpp b/contrib/llvm/tools/lldb/source/API/SBSourceManager.cpp new file mode 100644 index 00000000000..0b8cbfceda0 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/API/SBSourceManager.cpp @@ -0,0 +1,146 @@ +//===-- SBSourceManager.cpp -------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "lldb/API/SBDebugger.h" +#include "lldb/API/SBSourceManager.h" +#include "lldb/API/SBTarget.h" +#include "lldb/API/SBStream.h" + +#include "lldb/API/SBFileSpec.h" +#include "lldb/Core/Debugger.h" +#include "lldb/Core/Stream.h" +#include "lldb/Core/StreamFile.h" +#include "lldb/Core/SourceManager.h" + +#include "lldb/Target/Target.h" + +namespace lldb_private +{ + class SourceManagerImpl + { + public: + SourceManagerImpl (const lldb::DebuggerSP &debugger_sp) : + m_debugger_wp (debugger_sp), + m_target_wp () + { + } + + SourceManagerImpl (const lldb::TargetSP &target_sp) : + m_debugger_wp (), + m_target_wp (target_sp) + { + } + + SourceManagerImpl (const SourceManagerImpl &rhs) + { + if (&rhs == this) + return; + m_debugger_wp = rhs.m_debugger_wp; + m_target_wp = rhs.m_target_wp; + } + + size_t + DisplaySourceLinesWithLineNumbers (const lldb_private::FileSpec &file, + uint32_t line, + uint32_t context_before, + uint32_t context_after, + const char *current_line_cstr, + lldb_private::Stream *s) + { + if (!file) + return 0; + + lldb::TargetSP target_sp (m_target_wp.lock()); + if (target_sp) + { + return target_sp->GetSourceManager().DisplaySourceLinesWithLineNumbers (file, + line, + context_before, + context_after, + current_line_cstr, + s); + } + else + { + lldb::DebuggerSP debugger_sp (m_debugger_wp.lock()); + if (debugger_sp) + { + return debugger_sp->GetSourceManager().DisplaySourceLinesWithLineNumbers (file, + line, + context_before, + context_after, + current_line_cstr, + s); + } + } + return 0; + } + + private: + lldb::DebuggerWP m_debugger_wp; + lldb::TargetWP m_target_wp; + + }; +} + +using namespace lldb; +using namespace lldb_private; + +SBSourceManager::SBSourceManager (const SBDebugger &debugger) +{ + m_opaque_ap.reset(new SourceManagerImpl (debugger.get_sp())); +} + +SBSourceManager::SBSourceManager (const SBTarget &target) +{ + m_opaque_ap.reset(new SourceManagerImpl (target.GetSP())); +} + +SBSourceManager::SBSourceManager (const SBSourceManager &rhs) +{ + if (&rhs == this) + return; + + m_opaque_ap.reset(new SourceManagerImpl (*(rhs.m_opaque_ap.get()))); +} + +const lldb::SBSourceManager & +SBSourceManager::operator = (const lldb::SBSourceManager &rhs) +{ + m_opaque_ap.reset (new SourceManagerImpl (*(rhs.m_opaque_ap.get()))); + return *this; +} + +SBSourceManager::~SBSourceManager() +{ +} + +size_t +SBSourceManager::DisplaySourceLinesWithLineNumbers +( + const SBFileSpec &file, + uint32_t line, + uint32_t context_before, + uint32_t context_after, + const char *current_line_cstr, + SBStream &s +) +{ + if (m_opaque_ap.get() == NULL) + return 0; + + return m_opaque_ap->DisplaySourceLinesWithLineNumbers (file.ref(), + line, + context_before, + context_after, + current_line_cstr, + s.get()); +} diff --git a/contrib/llvm/tools/lldb/source/API/SBStream.cpp b/contrib/llvm/tools/lldb/source/API/SBStream.cpp new file mode 100644 index 00000000000..dc8eb05ab0b --- /dev/null +++ b/contrib/llvm/tools/lldb/source/API/SBStream.cpp @@ -0,0 +1,187 @@ +//===-- SBStream.cpp ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/API/SBStream.h" + +#include "lldb/Core/Error.h" +#include "lldb/Core/Stream.h" +#include "lldb/Core/StreamFile.h" +#include "lldb/Core/StreamString.h" + +using namespace lldb; +using namespace lldb_private; + +SBStream::SBStream () : + m_opaque_ap (new StreamString()), + m_is_file (false) +{ +} + +SBStream::~SBStream () +{ +} + +bool +SBStream::IsValid() const +{ + return (m_opaque_ap.get() != NULL); +} + +// If this stream is not redirected to a file, it will maintain a local +// cache for the stream data which can be accessed using this accessor. +const char * +SBStream::GetData () +{ + if (m_is_file || m_opaque_ap.get() == NULL) + return NULL; + + return static_cast(m_opaque_ap.get())->GetData(); +} + +// If this stream is not redirected to a file, it will maintain a local +// cache for the stream output whose length can be accessed using this +// accessor. +size_t +SBStream::GetSize() +{ + if (m_is_file || m_opaque_ap.get() == NULL) + return 0; + + return static_cast(m_opaque_ap.get())->GetSize(); +} + +void +SBStream::Printf (const char *format, ...) +{ + if (!format) + return; + va_list args; + va_start (args, format); + ref().PrintfVarArg (format, args); + va_end (args); +} + +void +SBStream::RedirectToFile (const char *path, bool append) +{ + std::string local_data; + if (m_opaque_ap.get()) + { + // See if we have any locally backed data. If so, copy it so we can then + // redirect it to the file so we don't lose the data + if (!m_is_file) + local_data.swap(static_cast(m_opaque_ap.get())->GetString()); + } + StreamFile *stream_file = new StreamFile; + uint32_t open_options = File::eOpenOptionWrite | File::eOpenOptionCanCreate; + if (append) + open_options |= File::eOpenOptionAppend; + stream_file->GetFile().Open (path, open_options, File::ePermissionsDefault); + + m_opaque_ap.reset (stream_file); + + if (m_opaque_ap.get()) + { + m_is_file = true; + + // If we had any data locally in our StreamString, then pass that along to + // the to new file we are redirecting to. + if (!local_data.empty()) + m_opaque_ap->Write (&local_data[0], local_data.size()); + } + else + m_is_file = false; +} + +void +SBStream::RedirectToFileHandle (FILE *fh, bool transfer_fh_ownership) +{ + std::string local_data; + if (m_opaque_ap.get()) + { + // See if we have any locally backed data. If so, copy it so we can then + // redirect it to the file so we don't lose the data + if (!m_is_file) + local_data.swap(static_cast(m_opaque_ap.get())->GetString()); + } + m_opaque_ap.reset (new StreamFile (fh, transfer_fh_ownership)); + + if (m_opaque_ap.get()) + { + m_is_file = true; + + // If we had any data locally in our StreamString, then pass that along to + // the to new file we are redirecting to. + if (!local_data.empty()) + m_opaque_ap->Write (&local_data[0], local_data.size()); + } + else + m_is_file = false; +} + +void +SBStream::RedirectToFileDescriptor (int fd, bool transfer_fh_ownership) +{ + std::string local_data; + if (m_opaque_ap.get()) + { + // See if we have any locally backed data. If so, copy it so we can then + // redirect it to the file so we don't lose the data + if (!m_is_file) + local_data.swap(static_cast(m_opaque_ap.get())->GetString()); + } + + m_opaque_ap.reset (new StreamFile (::fdopen (fd, "w"), transfer_fh_ownership)); + if (m_opaque_ap.get()) + { + m_is_file = true; + + // If we had any data locally in our StreamString, then pass that along to + // the to new file we are redirecting to. + if (!local_data.empty()) + m_opaque_ap->Write (&local_data[0], local_data.size()); + } + else + m_is_file = false; + +} + +lldb_private::Stream * +SBStream::operator->() +{ + return m_opaque_ap.get(); +} + +lldb_private::Stream * +SBStream::get() +{ + return m_opaque_ap.get(); +} + +lldb_private::Stream & +SBStream::ref() +{ + if (m_opaque_ap.get() == NULL) + m_opaque_ap.reset (new StreamString()); + return *m_opaque_ap.get(); +} + +void +SBStream::Clear () +{ + if (m_opaque_ap.get()) + { + // See if we have any locally backed data. If so, copy it so we can then + // redirect it to the file so we don't lose the data + if (m_is_file) + m_opaque_ap.reset(); + else + static_cast(m_opaque_ap.get())->GetString().clear(); + } +} diff --git a/contrib/llvm/tools/lldb/source/API/SBStringList.cpp b/contrib/llvm/tools/lldb/source/API/SBStringList.cpp new file mode 100644 index 00000000000..129d2f4c11f --- /dev/null +++ b/contrib/llvm/tools/lldb/source/API/SBStringList.cpp @@ -0,0 +1,136 @@ +//===-- SBStringList.cpp ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/API/SBStringList.h" + +#include "lldb/Core/StringList.h" + +using namespace lldb; +using namespace lldb_private; + +SBStringList::SBStringList () : + m_opaque_ap () +{ +} + +SBStringList::SBStringList (const lldb_private::StringList *lldb_strings_ptr) : + m_opaque_ap () +{ + if (lldb_strings_ptr) + m_opaque_ap.reset (new lldb_private::StringList (*lldb_strings_ptr)); +} + +SBStringList::SBStringList (const SBStringList &rhs) : + m_opaque_ap () +{ + if (rhs.IsValid()) + m_opaque_ap.reset (new lldb_private::StringList(*rhs)); +} + + +const SBStringList & +SBStringList::operator = (const SBStringList &rhs) +{ + if (this != &rhs) + { + if (rhs.IsValid()) + m_opaque_ap.reset(new lldb_private::StringList(*rhs)); + else + m_opaque_ap.reset(); + } + return *this; +} + +SBStringList::~SBStringList () +{ +} + +const lldb_private::StringList * +SBStringList::operator->() const +{ + return m_opaque_ap.get(); +} + +const lldb_private::StringList & +SBStringList::operator*() const +{ + return *m_opaque_ap; +} + +bool +SBStringList::IsValid() const +{ + return (m_opaque_ap.get() != NULL); +} + +void +SBStringList::AppendString (const char *str) +{ + if (str != NULL) + { + if (IsValid()) + m_opaque_ap->AppendString (str); + else + m_opaque_ap.reset (new lldb_private::StringList (str)); + } + +} + +void +SBStringList::AppendList (const char **strv, int strc) +{ + if ((strv != NULL) + && (strc > 0)) + { + if (IsValid()) + m_opaque_ap->AppendList (strv, strc); + else + m_opaque_ap.reset (new lldb_private::StringList (strv, strc)); + } +} + +void +SBStringList::AppendList (const SBStringList &strings) +{ + if (strings.IsValid()) + { + if (! IsValid()) + m_opaque_ap.reset (new lldb_private::StringList()); + m_opaque_ap->AppendList (*(strings.m_opaque_ap)); + } +} + +uint32_t +SBStringList::GetSize () const +{ + if (IsValid()) + { + return m_opaque_ap->GetSize(); + } + return 0; +} + +const char * +SBStringList::GetStringAtIndex (size_t idx) +{ + if (IsValid()) + { + return m_opaque_ap->GetStringAtIndex (idx); + } + return NULL; +} + +void +SBStringList::Clear () +{ + if (IsValid()) + { + m_opaque_ap->Clear(); + } +} diff --git a/contrib/llvm/tools/lldb/source/API/SBSymbol.cpp b/contrib/llvm/tools/lldb/source/API/SBSymbol.cpp new file mode 100644 index 00000000000..dd057e81a55 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/API/SBSymbol.cpp @@ -0,0 +1,223 @@ +//===-- SBSymbol.cpp --------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/API/SBSymbol.h" +#include "lldb/API/SBStream.h" +#include "lldb/Core/Disassembler.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Module.h" +#include "lldb/Symbol/Symbol.h" +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/Target.h" + +using namespace lldb; +using namespace lldb_private; + +SBSymbol::SBSymbol () : + m_opaque_ptr (NULL) +{ +} + +SBSymbol::SBSymbol (lldb_private::Symbol *lldb_object_ptr) : + m_opaque_ptr (lldb_object_ptr) +{ +} + +SBSymbol::SBSymbol (const lldb::SBSymbol &rhs) : + m_opaque_ptr (rhs.m_opaque_ptr) +{ +} + +const SBSymbol & +SBSymbol::operator = (const SBSymbol &rhs) +{ + m_opaque_ptr = rhs.m_opaque_ptr; + return *this; +} + +SBSymbol::~SBSymbol () +{ + m_opaque_ptr = NULL; +} + +void +SBSymbol::SetSymbol (lldb_private::Symbol *lldb_object_ptr) +{ + m_opaque_ptr = lldb_object_ptr; +} + +bool +SBSymbol::IsValid () const +{ + return m_opaque_ptr != NULL; +} + +const char * +SBSymbol::GetName() const +{ + const char *name = NULL; + if (m_opaque_ptr) + name = m_opaque_ptr->GetMangled().GetName().AsCString(); + + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (log) + log->Printf ("SBSymbol(%p)::GetName () => \"%s\"", m_opaque_ptr, name ? name : ""); + return name; +} + +const char * +SBSymbol::GetMangledName () const +{ + const char *name = NULL; + if (m_opaque_ptr) + name = m_opaque_ptr->GetMangled().GetMangledName().AsCString(); + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (log) + log->Printf ("SBSymbol(%p)::GetMangledName () => \"%s\"", m_opaque_ptr, name ? name : ""); + + return name; +} + + +bool +SBSymbol::operator == (const SBSymbol &rhs) const +{ + return m_opaque_ptr == rhs.m_opaque_ptr; +} + +bool +SBSymbol::operator != (const SBSymbol &rhs) const +{ + return m_opaque_ptr != rhs.m_opaque_ptr; +} + +bool +SBSymbol::GetDescription (SBStream &description) +{ + Stream &strm = description.ref(); + + if (m_opaque_ptr) + { + m_opaque_ptr->GetDescription (&strm, + lldb::eDescriptionLevelFull, NULL); + } + else + strm.PutCString ("No value"); + + return true; +} + +SBInstructionList +SBSymbol::GetInstructions (SBTarget target) +{ + return GetInstructions (target, NULL); +} + +SBInstructionList +SBSymbol::GetInstructions (SBTarget target, const char *flavor_string) +{ + SBInstructionList sb_instructions; + if (m_opaque_ptr) + { + Mutex::Locker api_locker; + ExecutionContext exe_ctx; + TargetSP target_sp (target.GetSP()); + if (target_sp) + { + api_locker.Lock (target_sp->GetAPIMutex()); + target_sp->CalculateExecutionContext (exe_ctx); + } + if (m_opaque_ptr->ValueIsAddress()) + { + ModuleSP module_sp (m_opaque_ptr->GetAddress().GetModule()); + if (module_sp) + { + AddressRange symbol_range (m_opaque_ptr->GetAddress(), m_opaque_ptr->GetByteSize()); + sb_instructions.SetDisassembler (Disassembler::DisassembleRange (module_sp->GetArchitecture (), + NULL, + flavor_string, + exe_ctx, + symbol_range)); + } + } + } + return sb_instructions; +} + +lldb_private::Symbol * +SBSymbol::get () +{ + return m_opaque_ptr; +} + +void +SBSymbol::reset (lldb_private::Symbol *symbol) +{ + m_opaque_ptr = symbol; +} + +SBAddress +SBSymbol::GetStartAddress () +{ + SBAddress addr; + if (m_opaque_ptr && m_opaque_ptr->ValueIsAddress()) + { + addr.SetAddress (&m_opaque_ptr->GetAddress()); + } + return addr; +} + +SBAddress +SBSymbol::GetEndAddress () +{ + SBAddress addr; + if (m_opaque_ptr && m_opaque_ptr->ValueIsAddress()) + { + lldb::addr_t range_size = m_opaque_ptr->GetByteSize(); + if (range_size > 0) + { + addr.SetAddress (&m_opaque_ptr->GetAddress()); + addr->Slide (m_opaque_ptr->GetByteSize()); + } + } + return addr; +} + +uint32_t +SBSymbol::GetPrologueByteSize () +{ + if (m_opaque_ptr) + return m_opaque_ptr->GetPrologueByteSize(); + return 0; +} + +SymbolType +SBSymbol::GetType () +{ + if (m_opaque_ptr) + return m_opaque_ptr->GetType(); + return eSymbolTypeInvalid; +} + +bool +SBSymbol::IsExternal() +{ + if (m_opaque_ptr) + return m_opaque_ptr->IsExternal(); + return false; +} + +bool +SBSymbol::IsSynthetic() +{ + if (m_opaque_ptr) + return m_opaque_ptr->IsSynthetic(); + return false; +} + diff --git a/contrib/llvm/tools/lldb/source/API/SBSymbolContext.cpp b/contrib/llvm/tools/lldb/source/API/SBSymbolContext.cpp new file mode 100644 index 00000000000..479b0f75bfe --- /dev/null +++ b/contrib/llvm/tools/lldb/source/API/SBSymbolContext.cpp @@ -0,0 +1,285 @@ +//===-- SBSymbolContext.cpp -------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/API/SBSymbolContext.h" +#include "lldb/API/SBStream.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Module.h" +#include "lldb/Symbol/Function.h" +#include "lldb/Symbol/Symbol.h" +#include "lldb/Symbol/SymbolContext.h" + +using namespace lldb; +using namespace lldb_private; + + + +SBSymbolContext::SBSymbolContext () : + m_opaque_ap () +{ +} + +SBSymbolContext::SBSymbolContext (const SymbolContext *sc_ptr) : + m_opaque_ap () +{ + if (sc_ptr) + m_opaque_ap.reset (new SymbolContext (*sc_ptr)); +} + +SBSymbolContext::SBSymbolContext (const SBSymbolContext& rhs) : + m_opaque_ap () +{ + if (rhs.IsValid()) + { + if (m_opaque_ap.get()) + *m_opaque_ap = *rhs.m_opaque_ap; + else + ref() = *rhs.m_opaque_ap; + } +} + +SBSymbolContext::~SBSymbolContext () +{ +} + +const SBSymbolContext & +SBSymbolContext::operator = (const SBSymbolContext &rhs) +{ + if (this != &rhs) + { + if (rhs.IsValid()) + m_opaque_ap.reset (new lldb_private::SymbolContext(*rhs.m_opaque_ap.get())); + } + return *this; +} + +void +SBSymbolContext::SetSymbolContext (const SymbolContext *sc_ptr) +{ + if (sc_ptr) + { + if (m_opaque_ap.get()) + *m_opaque_ap = *sc_ptr; + else + m_opaque_ap.reset (new SymbolContext (*sc_ptr)); + } + else + { + if (m_opaque_ap.get()) + m_opaque_ap->Clear(true); + } +} + +bool +SBSymbolContext::IsValid () const +{ + return m_opaque_ap.get() != NULL; +} + + + +SBModule +SBSymbolContext::GetModule () +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + SBModule sb_module; + ModuleSP module_sp; + if (m_opaque_ap.get()) + { + module_sp = m_opaque_ap->module_sp; + sb_module.SetSP (module_sp); + } + + if (log) + { + SBStream sstr; + sb_module.GetDescription (sstr); + log->Printf ("SBSymbolContext(%p)::GetModule () => SBModule(%p): %s", + m_opaque_ap.get(), module_sp.get(), sstr.GetData()); + } + + return sb_module; +} + +SBCompileUnit +SBSymbolContext::GetCompileUnit () +{ + return SBCompileUnit (m_opaque_ap.get() ? m_opaque_ap->comp_unit : NULL); +} + +SBFunction +SBSymbolContext::GetFunction () +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + Function *function = NULL; + + if (m_opaque_ap.get()) + function = m_opaque_ap->function; + + SBFunction sb_function (function); + + if (log) + log->Printf ("SBSymbolContext(%p)::GetFunction () => SBFunction(%p)", + m_opaque_ap.get(), function); + + return sb_function; +} + +SBBlock +SBSymbolContext::GetBlock () +{ + return SBBlock (m_opaque_ap.get() ? m_opaque_ap->block : NULL); +} + +SBLineEntry +SBSymbolContext::GetLineEntry () +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + SBLineEntry sb_line_entry; + if (m_opaque_ap.get()) + sb_line_entry.SetLineEntry (m_opaque_ap->line_entry); + + if (log) + { + log->Printf ("SBSymbolContext(%p)::GetLineEntry () => SBLineEntry(%p)", + m_opaque_ap.get(), sb_line_entry.get()); + } + + return sb_line_entry; +} + +SBSymbol +SBSymbolContext::GetSymbol () +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + Symbol *symbol = NULL; + + if (m_opaque_ap.get()) + symbol = m_opaque_ap->symbol; + + SBSymbol sb_symbol (symbol); + + if (log) + { + log->Printf ("SBSymbolContext(%p)::GetSymbol () => SBSymbol(%p)", + m_opaque_ap.get(), symbol); + } + + return sb_symbol; +} + +void +SBSymbolContext::SetModule (lldb::SBModule module) +{ + ref().module_sp = module.GetSP(); +} + +void +SBSymbolContext::SetCompileUnit (lldb::SBCompileUnit compile_unit) +{ + ref().comp_unit = compile_unit.get(); +} + +void +SBSymbolContext::SetFunction (lldb::SBFunction function) +{ + ref().function = function.get(); +} + +void +SBSymbolContext::SetBlock (lldb::SBBlock block) +{ + ref().block = block.GetPtr(); +} + +void +SBSymbolContext::SetLineEntry (lldb::SBLineEntry line_entry) +{ + if (line_entry.IsValid()) + ref().line_entry = line_entry.ref(); + else + ref().line_entry.Clear(); +} + +void +SBSymbolContext::SetSymbol (lldb::SBSymbol symbol) +{ + ref().symbol = symbol.get(); +} + + +lldb_private::SymbolContext* +SBSymbolContext::operator->() const +{ + return m_opaque_ap.get(); +} + + +const lldb_private::SymbolContext& +SBSymbolContext::operator*() const +{ + assert (m_opaque_ap.get()); + return *m_opaque_ap.get(); +} + + +lldb_private::SymbolContext& +SBSymbolContext::operator*() +{ + if (m_opaque_ap.get() == NULL) + m_opaque_ap.reset (new SymbolContext); + return *m_opaque_ap.get(); +} + +lldb_private::SymbolContext& +SBSymbolContext::ref() +{ + if (m_opaque_ap.get() == NULL) + m_opaque_ap.reset (new SymbolContext); + return *m_opaque_ap.get(); +} + +lldb_private::SymbolContext * +SBSymbolContext::get() const +{ + return m_opaque_ap.get(); +} + +bool +SBSymbolContext::GetDescription (SBStream &description) +{ + Stream &strm = description.ref(); + + if (m_opaque_ap.get()) + { + m_opaque_ap->GetDescription (&strm, lldb::eDescriptionLevelFull, NULL); + } + else + strm.PutCString ("No value"); + + return true; +} + +SBSymbolContext +SBSymbolContext::GetParentOfInlinedScope (const SBAddress &curr_frame_pc, + SBAddress &parent_frame_addr) const +{ + SBSymbolContext sb_sc; + if (m_opaque_ap.get() && curr_frame_pc.IsValid()) + { + if (m_opaque_ap->GetParentOfInlinedScope (curr_frame_pc.ref(), sb_sc.ref(), parent_frame_addr.ref())) + return sb_sc; + } + return SBSymbolContext(); +} + diff --git a/contrib/llvm/tools/lldb/source/API/SBSymbolContextList.cpp b/contrib/llvm/tools/lldb/source/API/SBSymbolContextList.cpp new file mode 100644 index 00000000000..0730096c5f3 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/API/SBSymbolContextList.cpp @@ -0,0 +1,117 @@ +//===-- SBSymbolContextList.cpp ---------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/API/SBSymbolContextList.h" +#include "lldb/API/SBStream.h" +#include "lldb/Symbol/SymbolContext.h" + +using namespace lldb; +using namespace lldb_private; + +SBSymbolContextList::SBSymbolContextList () : + m_opaque_ap (new SymbolContextList()) +{ +} + +SBSymbolContextList::SBSymbolContextList (const SBSymbolContextList& rhs) : + m_opaque_ap (new SymbolContextList(*rhs.m_opaque_ap)) +{ +} + +SBSymbolContextList::~SBSymbolContextList () +{ +} + +const SBSymbolContextList & +SBSymbolContextList::operator = (const SBSymbolContextList &rhs) +{ + if (this != &rhs) + { + *m_opaque_ap = *rhs.m_opaque_ap; + } + return *this; +} + +uint32_t +SBSymbolContextList::GetSize() const +{ + if (m_opaque_ap.get()) + return m_opaque_ap->GetSize(); + return 0; +} + +SBSymbolContext +SBSymbolContextList::GetContextAtIndex (uint32_t idx) +{ + SBSymbolContext sb_sc; + if (m_opaque_ap.get()) + { + SymbolContext sc; + if (m_opaque_ap->GetContextAtIndex (idx, sc)) + { + sb_sc.SetSymbolContext(&sc); + } + } + return sb_sc; +} + +void +SBSymbolContextList::Clear() +{ + if (m_opaque_ap.get()) + m_opaque_ap->Clear(); +} + +void +SBSymbolContextList::Append(SBSymbolContext &sc) +{ + if (sc.IsValid() && m_opaque_ap.get()) + m_opaque_ap->Append(*sc); +} + +void +SBSymbolContextList::Append(SBSymbolContextList &sc_list) +{ + if (sc_list.IsValid() && m_opaque_ap.get()) + m_opaque_ap->Append(*sc_list); +} + + +bool +SBSymbolContextList::IsValid () const +{ + return m_opaque_ap.get() != NULL; +} + + + +lldb_private::SymbolContextList* +SBSymbolContextList::operator->() const +{ + return m_opaque_ap.get(); +} + + +lldb_private::SymbolContextList& +SBSymbolContextList::operator*() const +{ + assert (m_opaque_ap.get()); + return *m_opaque_ap.get(); +} + +bool +SBSymbolContextList::GetDescription (lldb::SBStream &description) +{ + Stream &strm = description.ref(); + if (m_opaque_ap.get()) + m_opaque_ap->GetDescription (&strm, lldb::eDescriptionLevelFull, NULL); + return true; +} + + diff --git a/contrib/llvm/tools/lldb/source/API/SBTarget.cpp b/contrib/llvm/tools/lldb/source/API/SBTarget.cpp new file mode 100644 index 00000000000..f37c8f8a614 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/API/SBTarget.cpp @@ -0,0 +1,2660 @@ +//===-- SBTarget.cpp --------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "lldb/API/SBTarget.h" + +#include "lldb/lldb-public.h" + +#include "lldb/API/SBDebugger.h" +#include "lldb/API/SBBreakpoint.h" +#include "lldb/API/SBExpressionOptions.h" +#include "lldb/API/SBFileSpec.h" +#include "lldb/API/SBListener.h" +#include "lldb/API/SBModule.h" +#include "lldb/API/SBModuleSpec.h" +#include "lldb/API/SBSourceManager.h" +#include "lldb/API/SBProcess.h" +#include "lldb/API/SBStream.h" +#include "lldb/API/SBSymbolContextList.h" +#include "lldb/Breakpoint/BreakpointID.h" +#include "lldb/Breakpoint/BreakpointIDList.h" +#include "lldb/Breakpoint/BreakpointList.h" +#include "lldb/Breakpoint/BreakpointLocation.h" +#include "lldb/Core/Address.h" +#include "lldb/Core/AddressResolver.h" +#include "lldb/Core/AddressResolverName.h" +#include "lldb/Core/ArchSpec.h" +#include "lldb/Core/Debugger.h" +#include "lldb/Core/Disassembler.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/ModuleSpec.h" +#include "lldb/Core/RegularExpression.h" +#include "lldb/Core/SearchFilter.h" +#include "lldb/Core/Section.h" +#include "lldb/Core/STLUtils.h" +#include "lldb/Core/ValueObjectList.h" +#include "lldb/Core/ValueObjectVariable.h" +#include "lldb/Host/FileSpec.h" +#include "lldb/Host/Host.h" +#include "lldb/Interpreter/Args.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Symbol/SymbolVendor.h" +#include "lldb/Symbol/VariableList.h" +#include "lldb/Target/LanguageRuntime.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/TargetList.h" + +#include "lldb/Interpreter/CommandReturnObject.h" +#include "../source/Commands/CommandObjectBreakpoint.h" + + +using namespace lldb; +using namespace lldb_private; + +#define DEFAULT_DISASM_BYTE_SIZE 32 + +SBLaunchInfo::SBLaunchInfo (const char **argv) : + m_opaque_sp(new ProcessLaunchInfo()) +{ + m_opaque_sp->GetFlags().Reset (eLaunchFlagDebug | eLaunchFlagDisableASLR); + if (argv && argv[0]) + m_opaque_sp->GetArguments().SetArguments(argv); +} + +SBLaunchInfo::~SBLaunchInfo() +{ +} + +lldb_private::ProcessLaunchInfo & +SBLaunchInfo::ref () +{ + return *m_opaque_sp; +} + + +uint32_t +SBLaunchInfo::GetUserID() +{ + return m_opaque_sp->GetUserID(); +} + +uint32_t +SBLaunchInfo::GetGroupID() +{ + return m_opaque_sp->GetGroupID(); +} + +bool +SBLaunchInfo::UserIDIsValid () +{ + return m_opaque_sp->UserIDIsValid(); +} + +bool +SBLaunchInfo::GroupIDIsValid () +{ + return m_opaque_sp->GroupIDIsValid(); +} + +void +SBLaunchInfo::SetUserID (uint32_t uid) +{ + m_opaque_sp->SetUserID (uid); +} + +void +SBLaunchInfo::SetGroupID (uint32_t gid) +{ + m_opaque_sp->SetGroupID (gid); +} + +uint32_t +SBLaunchInfo::GetNumArguments () +{ + return m_opaque_sp->GetArguments().GetArgumentCount(); +} + +const char * +SBLaunchInfo::GetArgumentAtIndex (uint32_t idx) +{ + return m_opaque_sp->GetArguments().GetArgumentAtIndex(idx); +} + +void +SBLaunchInfo::SetArguments (const char **argv, bool append) +{ + if (append) + { + if (argv) + m_opaque_sp->GetArguments().AppendArguments(argv); + } + else + { + if (argv) + m_opaque_sp->GetArguments().SetArguments(argv); + else + m_opaque_sp->GetArguments().Clear(); + } +} + +uint32_t +SBLaunchInfo::GetNumEnvironmentEntries () +{ + return m_opaque_sp->GetEnvironmentEntries().GetArgumentCount(); +} + +const char * +SBLaunchInfo::GetEnvironmentEntryAtIndex (uint32_t idx) +{ + return m_opaque_sp->GetEnvironmentEntries().GetArgumentAtIndex(idx); +} + +void +SBLaunchInfo::SetEnvironmentEntries (const char **envp, bool append) +{ + if (append) + { + if (envp) + m_opaque_sp->GetEnvironmentEntries().AppendArguments(envp); + } + else + { + if (envp) + m_opaque_sp->GetEnvironmentEntries().SetArguments(envp); + else + m_opaque_sp->GetEnvironmentEntries().Clear(); + } +} + +void +SBLaunchInfo::Clear () +{ + m_opaque_sp->Clear(); +} + +const char * +SBLaunchInfo::GetWorkingDirectory () const +{ + return m_opaque_sp->GetWorkingDirectory(); +} + +void +SBLaunchInfo::SetWorkingDirectory (const char *working_dir) +{ + m_opaque_sp->SetWorkingDirectory(working_dir); +} + +uint32_t +SBLaunchInfo::GetLaunchFlags () +{ + return m_opaque_sp->GetFlags().Get(); +} + +void +SBLaunchInfo::SetLaunchFlags (uint32_t flags) +{ + m_opaque_sp->GetFlags().Reset(flags); +} + +const char * +SBLaunchInfo::GetProcessPluginName () +{ + return m_opaque_sp->GetProcessPluginName(); +} + +void +SBLaunchInfo::SetProcessPluginName (const char *plugin_name) +{ + return m_opaque_sp->SetProcessPluginName (plugin_name); +} + +const char * +SBLaunchInfo::GetShell () +{ + return m_opaque_sp->GetShell(); +} + +void +SBLaunchInfo::SetShell (const char * path) +{ + m_opaque_sp->SetShell (path); +} + +uint32_t +SBLaunchInfo::GetResumeCount () +{ + return m_opaque_sp->GetResumeCount(); +} + +void +SBLaunchInfo::SetResumeCount (uint32_t c) +{ + m_opaque_sp->SetResumeCount (c); +} + +bool +SBLaunchInfo::AddCloseFileAction (int fd) +{ + return m_opaque_sp->AppendCloseFileAction(fd); +} + +bool +SBLaunchInfo::AddDuplicateFileAction (int fd, int dup_fd) +{ + return m_opaque_sp->AppendDuplicateFileAction(fd, dup_fd); +} + +bool +SBLaunchInfo::AddOpenFileAction (int fd, const char *path, bool read, bool write) +{ + return m_opaque_sp->AppendOpenFileAction(fd, path, read, write); +} + +bool +SBLaunchInfo::AddSuppressFileAction (int fd, bool read, bool write) +{ + return m_opaque_sp->AppendSuppressFileAction(fd, read, write); +} + + +SBAttachInfo::SBAttachInfo () : + m_opaque_sp (new ProcessAttachInfo()) +{ +} + +SBAttachInfo::SBAttachInfo (lldb::pid_t pid) : + m_opaque_sp (new ProcessAttachInfo()) +{ + m_opaque_sp->SetProcessID (pid); +} + +SBAttachInfo::SBAttachInfo (const char *path, bool wait_for) : + m_opaque_sp (new ProcessAttachInfo()) +{ + if (path && path[0]) + m_opaque_sp->GetExecutableFile().SetFile(path, false); + m_opaque_sp->SetWaitForLaunch (wait_for); +} + +SBAttachInfo::SBAttachInfo (const SBAttachInfo &rhs) : + m_opaque_sp (new ProcessAttachInfo()) +{ + *m_opaque_sp = *rhs.m_opaque_sp; +} + +SBAttachInfo::~SBAttachInfo() +{ +} + +lldb_private::ProcessAttachInfo & +SBAttachInfo::ref () +{ + return *m_opaque_sp; +} + +SBAttachInfo & +SBAttachInfo::operator = (const SBAttachInfo &rhs) +{ + if (this != &rhs) + *m_opaque_sp = *rhs.m_opaque_sp; + return *this; +} + +lldb::pid_t +SBAttachInfo::GetProcessID () +{ + return m_opaque_sp->GetProcessID(); +} + +void +SBAttachInfo::SetProcessID (lldb::pid_t pid) +{ + m_opaque_sp->SetProcessID (pid); +} + + +uint32_t +SBAttachInfo::GetResumeCount () +{ + return m_opaque_sp->GetResumeCount(); +} + +void +SBAttachInfo::SetResumeCount (uint32_t c) +{ + m_opaque_sp->SetResumeCount (c); +} + +const char * +SBAttachInfo::GetProcessPluginName () +{ + return m_opaque_sp->GetProcessPluginName(); +} + +void +SBAttachInfo::SetProcessPluginName (const char *plugin_name) +{ + return m_opaque_sp->SetProcessPluginName (plugin_name); +} + +void +SBAttachInfo::SetExecutable (const char *path) +{ + if (path && path[0]) + m_opaque_sp->GetExecutableFile().SetFile(path, false); + else + m_opaque_sp->GetExecutableFile().Clear(); +} + +void +SBAttachInfo::SetExecutable (SBFileSpec exe_file) +{ + if (exe_file.IsValid()) + m_opaque_sp->GetExecutableFile() = exe_file.ref(); + else + m_opaque_sp->GetExecutableFile().Clear(); +} + +bool +SBAttachInfo::GetWaitForLaunch () +{ + return m_opaque_sp->GetWaitForLaunch(); +} + +void +SBAttachInfo::SetWaitForLaunch (bool b) +{ + m_opaque_sp->SetWaitForLaunch (b); +} + +bool +SBAttachInfo::GetIgnoreExisting () +{ + return m_opaque_sp->GetIgnoreExisting(); +} + +void +SBAttachInfo::SetIgnoreExisting (bool b) +{ + m_opaque_sp->SetIgnoreExisting (b); +} + +uint32_t +SBAttachInfo::GetUserID() +{ + return m_opaque_sp->GetUserID(); +} + +uint32_t +SBAttachInfo::GetGroupID() +{ + return m_opaque_sp->GetGroupID(); +} + +bool +SBAttachInfo::UserIDIsValid () +{ + return m_opaque_sp->UserIDIsValid(); +} + +bool +SBAttachInfo::GroupIDIsValid () +{ + return m_opaque_sp->GroupIDIsValid(); +} + +void +SBAttachInfo::SetUserID (uint32_t uid) +{ + m_opaque_sp->SetUserID (uid); +} + +void +SBAttachInfo::SetGroupID (uint32_t gid) +{ + m_opaque_sp->SetGroupID (gid); +} + +uint32_t +SBAttachInfo::GetEffectiveUserID() +{ + return m_opaque_sp->GetEffectiveUserID(); +} + +uint32_t +SBAttachInfo::GetEffectiveGroupID() +{ + return m_opaque_sp->GetEffectiveGroupID(); +} + +bool +SBAttachInfo::EffectiveUserIDIsValid () +{ + return m_opaque_sp->EffectiveUserIDIsValid(); +} + +bool +SBAttachInfo::EffectiveGroupIDIsValid () +{ + return m_opaque_sp->EffectiveGroupIDIsValid (); +} + +void +SBAttachInfo::SetEffectiveUserID (uint32_t uid) +{ + m_opaque_sp->SetEffectiveUserID(uid); +} + +void +SBAttachInfo::SetEffectiveGroupID (uint32_t gid) +{ + m_opaque_sp->SetEffectiveGroupID(gid); +} + +lldb::pid_t +SBAttachInfo::GetParentProcessID () +{ + return m_opaque_sp->GetParentProcessID(); +} + +void +SBAttachInfo::SetParentProcessID (lldb::pid_t pid) +{ + m_opaque_sp->SetParentProcessID (pid); +} + +bool +SBAttachInfo::ParentProcessIDIsValid() +{ + return m_opaque_sp->ParentProcessIDIsValid(); +} + + +//---------------------------------------------------------------------- +// SBTarget constructor +//---------------------------------------------------------------------- +SBTarget::SBTarget () : + m_opaque_sp () +{ +} + +SBTarget::SBTarget (const SBTarget& rhs) : + m_opaque_sp (rhs.m_opaque_sp) +{ +} + +SBTarget::SBTarget(const TargetSP& target_sp) : + m_opaque_sp (target_sp) +{ +} + +const SBTarget& +SBTarget::operator = (const SBTarget& rhs) +{ + if (this != &rhs) + m_opaque_sp = rhs.m_opaque_sp; + return *this; +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +SBTarget::~SBTarget() +{ +} + +const char * +SBTarget::GetBroadcasterClassName () +{ + return Target::GetStaticBroadcasterClass().AsCString(); +} + +bool +SBTarget::IsValid () const +{ + return m_opaque_sp.get() != NULL && m_opaque_sp->IsValid(); +} + +SBProcess +SBTarget::GetProcess () +{ + SBProcess sb_process; + ProcessSP process_sp; + TargetSP target_sp(GetSP()); + if (target_sp) + { + process_sp = target_sp->GetProcessSP(); + sb_process.SetSP (process_sp); + } + + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (log) + { + log->Printf ("SBTarget(%p)::GetProcess () => SBProcess(%p)", + target_sp.get(), process_sp.get()); + } + + return sb_process; +} + +SBDebugger +SBTarget::GetDebugger () const +{ + SBDebugger debugger; + TargetSP target_sp(GetSP()); + if (target_sp) + debugger.reset (target_sp->GetDebugger().shared_from_this()); + return debugger; +} + +SBProcess +SBTarget::LoadCore (const char *core_file) +{ + SBProcess sb_process; + TargetSP target_sp(GetSP()); + if (target_sp) + { + FileSpec filespec(core_file, true); + ProcessSP process_sp (target_sp->CreateProcess(target_sp->GetDebugger().GetListener(), + NULL, + &filespec)); + if (process_sp) + { + process_sp->LoadCore(); + sb_process.SetSP (process_sp); + } + } + return sb_process; +} + +SBProcess +SBTarget::LaunchSimple +( + char const **argv, + char const **envp, + const char *working_directory +) +{ + char *stdin_path = NULL; + char *stdout_path = NULL; + char *stderr_path = NULL; + uint32_t launch_flags = 0; + bool stop_at_entry = false; + SBError error; + SBListener listener = GetDebugger().GetListener(); + return Launch (listener, + argv, + envp, + stdin_path, + stdout_path, + stderr_path, + working_directory, + launch_flags, + stop_at_entry, + error); +} + +SBProcess +SBTarget::Launch +( + SBListener &listener, + char const **argv, + char const **envp, + const char *stdin_path, + const char *stdout_path, + const char *stderr_path, + const char *working_directory, + uint32_t launch_flags, // See LaunchFlags + bool stop_at_entry, + lldb::SBError& error +) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + SBProcess sb_process; + ProcessSP process_sp; + TargetSP target_sp(GetSP()); + + if (log) + { + log->Printf ("SBTarget(%p)::Launch (argv=%p, envp=%p, stdin=%s, stdout=%s, stderr=%s, working-dir=%s, launch_flags=0x%x, stop_at_entry=%i, &error (%p))...", + target_sp.get(), + argv, + envp, + stdin_path ? stdin_path : "NULL", + stdout_path ? stdout_path : "NULL", + stderr_path ? stderr_path : "NULL", + working_directory ? working_directory : "NULL", + launch_flags, + stop_at_entry, + error.get()); + } + + if (target_sp) + { + Mutex::Locker api_locker (target_sp->GetAPIMutex()); + + if (getenv("LLDB_LAUNCH_FLAG_DISABLE_ASLR")) + launch_flags |= eLaunchFlagDisableASLR; + + StateType state = eStateInvalid; + process_sp = target_sp->GetProcessSP(); + if (process_sp) + { + state = process_sp->GetState(); + + if (process_sp->IsAlive() && state != eStateConnected) + { + if (state == eStateAttaching) + error.SetErrorString ("process attach is in progress"); + else + error.SetErrorString ("a process is already being debugged"); + return sb_process; + } + } + + if (state == eStateConnected) + { + // If we are already connected, then we have already specified the + // listener, so if a valid listener is supplied, we need to error out + // to let the client know. + if (listener.IsValid()) + { + error.SetErrorString ("process is connected and already has a listener, pass empty listener"); + return sb_process; + } + } + else + { + if (listener.IsValid()) + process_sp = target_sp->CreateProcess (listener.ref(), NULL, NULL); + else + process_sp = target_sp->CreateProcess (target_sp->GetDebugger().GetListener(), NULL, NULL); + } + + if (process_sp) + { + sb_process.SetSP (process_sp); + if (getenv("LLDB_LAUNCH_FLAG_DISABLE_STDIO")) + launch_flags |= eLaunchFlagDisableSTDIO; + + ProcessLaunchInfo launch_info (stdin_path, stdout_path, stderr_path, working_directory, launch_flags); + + Module *exe_module = target_sp->GetExecutableModulePointer(); + if (exe_module) + launch_info.SetExecutableFile(exe_module->GetPlatformFileSpec(), true); + if (argv) + launch_info.GetArguments().AppendArguments (argv); + if (envp) + launch_info.GetEnvironmentEntries ().SetArguments (envp); + + error.SetError (process_sp->Launch (launch_info)); + if (error.Success()) + { + // We we are stopping at the entry point, we can return now! + if (stop_at_entry) + return sb_process; + + // Make sure we are stopped at the entry + StateType state = process_sp->WaitForProcessToStop (NULL); + if (state == eStateStopped) + { + // resume the process to skip the entry point + error.SetError (process_sp->Resume()); + if (error.Success()) + { + // If we are doing synchronous mode, then wait for the + // process to stop yet again! + if (target_sp->GetDebugger().GetAsyncExecution () == false) + process_sp->WaitForProcessToStop (NULL); + } + } + } + } + else + { + error.SetErrorString ("unable to create lldb_private::Process"); + } + } + else + { + error.SetErrorString ("SBTarget is invalid"); + } + + log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API); + if (log) + { + log->Printf ("SBTarget(%p)::Launch (...) => SBProcess(%p)", + target_sp.get(), process_sp.get()); + } + + return sb_process; +} + +SBProcess +SBTarget::Launch (SBLaunchInfo &sb_launch_info, SBError& error) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + SBProcess sb_process; + ProcessSP process_sp; + TargetSP target_sp(GetSP()); + + if (log) + { + log->Printf ("SBTarget(%p)::Launch (launch_info, error)...", target_sp.get()); + } + + if (target_sp) + { + Mutex::Locker api_locker (target_sp->GetAPIMutex()); + StateType state = eStateInvalid; + process_sp = target_sp->GetProcessSP(); + if (process_sp) + { + state = process_sp->GetState(); + + if (process_sp->IsAlive() && state != eStateConnected) + { + if (state == eStateAttaching) + error.SetErrorString ("process attach is in progress"); + else + error.SetErrorString ("a process is already being debugged"); + return sb_process; + } + } + + if (state != eStateConnected) + process_sp = target_sp->CreateProcess (target_sp->GetDebugger().GetListener(), NULL, NULL); + + if (process_sp) + { + sb_process.SetSP (process_sp); + lldb_private::ProcessLaunchInfo &launch_info = sb_launch_info.ref(); + + Module *exe_module = target_sp->GetExecutableModulePointer(); + if (exe_module) + launch_info.SetExecutableFile(exe_module->GetPlatformFileSpec(), true); + + const ArchSpec &arch_spec = target_sp->GetArchitecture(); + if (arch_spec.IsValid()) + launch_info.GetArchitecture () = arch_spec; + + error.SetError (process_sp->Launch (launch_info)); + const bool synchronous_execution = target_sp->GetDebugger().GetAsyncExecution () == false; + if (error.Success()) + { + if (launch_info.GetFlags().Test(eLaunchFlagStopAtEntry)) + { + // If we are doing synchronous mode, then wait for the initial + // stop to happen, else, return and let the caller watch for + // the stop + if (synchronous_execution) + process_sp->WaitForProcessToStop (NULL); + // We we are stopping at the entry point, we can return now! + return sb_process; + } + + // Make sure we are stopped at the entry + StateType state = process_sp->WaitForProcessToStop (NULL); + if (state == eStateStopped) + { + // resume the process to skip the entry point + error.SetError (process_sp->Resume()); + if (error.Success()) + { + // If we are doing synchronous mode, then wait for the + // process to stop yet again! + if (synchronous_execution) + process_sp->WaitForProcessToStop (NULL); + } + } + } + } + else + { + error.SetErrorString ("unable to create lldb_private::Process"); + } + } + else + { + error.SetErrorString ("SBTarget is invalid"); + } + + log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API); + if (log) + { + log->Printf ("SBTarget(%p)::Launch (...) => SBProcess(%p)", + target_sp.get(), process_sp.get()); + } + + return sb_process; +} + +lldb::SBProcess +SBTarget::Attach (SBAttachInfo &sb_attach_info, SBError& error) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + SBProcess sb_process; + ProcessSP process_sp; + TargetSP target_sp(GetSP()); + + if (log) + { + log->Printf ("SBTarget(%p)::Attach (sb_attach_info, error)...", target_sp.get()); + } + + if (target_sp) + { + Mutex::Locker api_locker (target_sp->GetAPIMutex()); + + StateType state = eStateInvalid; + process_sp = target_sp->GetProcessSP(); + if (process_sp) + { + state = process_sp->GetState(); + + if (process_sp->IsAlive() && state != eStateConnected) + { + if (state == eStateAttaching) + error.SetErrorString ("process attach is in progress"); + else + error.SetErrorString ("a process is already being debugged"); + if (log) + { + log->Printf ("SBTarget(%p)::Attach (...) => error %s", + target_sp.get(), error.GetCString()); + } + return sb_process; + } + } + + if (state != eStateConnected) + process_sp = target_sp->CreateProcess (target_sp->GetDebugger().GetListener(), NULL, NULL); + + if (process_sp) + { + ProcessAttachInfo &attach_info = sb_attach_info.ref(); + if (attach_info.ProcessIDIsValid() && !attach_info.UserIDIsValid()) + { + PlatformSP platform_sp = target_sp->GetPlatform(); + // See if we can pre-verify if a process exists or not + if (platform_sp && platform_sp->IsConnected()) + { + lldb::pid_t attach_pid = attach_info.GetProcessID(); + ProcessInstanceInfo instance_info; + if (platform_sp->GetProcessInfo(attach_pid, instance_info)) + { + attach_info.SetUserID(instance_info.GetEffectiveUserID()); + } + else + { + error.ref().SetErrorStringWithFormat("no process found with process ID %" PRIu64, attach_pid); + if (log) + { + log->Printf ("SBTarget(%p)::Attach (...) => error %s", + target_sp.get(), error.GetCString()); + } + return sb_process; + } + } + } + error.SetError (process_sp->Attach (attach_info)); + if (error.Success()) + { + sb_process.SetSP (process_sp); + // If we are doing synchronous mode, then wait for the + // process to stop! + if (target_sp->GetDebugger().GetAsyncExecution () == false) + process_sp->WaitForProcessToStop (NULL); + } + } + else + { + error.SetErrorString ("unable to create lldb_private::Process"); + } + } + else + { + error.SetErrorString ("SBTarget is invalid"); + } + + if (log) + { + log->Printf ("SBTarget(%p)::Attach (...) => SBProcess(%p)", + target_sp.get(), process_sp.get()); + } + + return sb_process; +} + + +#if defined(__APPLE__) + +lldb::SBProcess +SBTarget::AttachToProcessWithID (SBListener &listener, + ::pid_t pid, + lldb::SBError& error) +{ + return AttachToProcessWithID (listener, (lldb::pid_t)pid, error); +} + +#endif // #if defined(__APPLE__) + +lldb::SBProcess +SBTarget::AttachToProcessWithID +( + SBListener &listener, + lldb::pid_t pid,// The process ID to attach to + SBError& error // An error explaining what went wrong if attach fails +) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + SBProcess sb_process; + ProcessSP process_sp; + TargetSP target_sp(GetSP()); + + if (log) + { + log->Printf ("SBTarget(%p)::AttachToProcessWithID (listener, pid=%" PRId64 ", error)...", target_sp.get(), pid); + } + + if (target_sp) + { + Mutex::Locker api_locker (target_sp->GetAPIMutex()); + + StateType state = eStateInvalid; + process_sp = target_sp->GetProcessSP(); + if (process_sp) + { + state = process_sp->GetState(); + + if (process_sp->IsAlive() && state != eStateConnected) + { + if (state == eStateAttaching) + error.SetErrorString ("process attach is in progress"); + else + error.SetErrorString ("a process is already being debugged"); + return sb_process; + } + } + + if (state == eStateConnected) + { + // If we are already connected, then we have already specified the + // listener, so if a valid listener is supplied, we need to error out + // to let the client know. + if (listener.IsValid()) + { + error.SetErrorString ("process is connected and already has a listener, pass empty listener"); + return sb_process; + } + } + else + { + if (listener.IsValid()) + process_sp = target_sp->CreateProcess (listener.ref(), NULL, NULL); + else + process_sp = target_sp->CreateProcess (target_sp->GetDebugger().GetListener(), NULL, NULL); + } + if (process_sp) + { + sb_process.SetSP (process_sp); + + ProcessAttachInfo attach_info; + attach_info.SetProcessID (pid); + + PlatformSP platform_sp = target_sp->GetPlatform(); + ProcessInstanceInfo instance_info; + if (platform_sp->GetProcessInfo(pid, instance_info)) + { + attach_info.SetUserID(instance_info.GetEffectiveUserID()); + } + error.SetError (process_sp->Attach (attach_info)); + if (error.Success()) + { + // If we are doing synchronous mode, then wait for the + // process to stop! + if (target_sp->GetDebugger().GetAsyncExecution () == false) + process_sp->WaitForProcessToStop (NULL); + } + } + else + { + error.SetErrorString ("unable to create lldb_private::Process"); + } + } + else + { + error.SetErrorString ("SBTarget is invalid"); + } + + if (log) + { + log->Printf ("SBTarget(%p)::AttachToProcessWithID (...) => SBProcess(%p)", + target_sp.get(), process_sp.get()); + } + return sb_process; +} + +lldb::SBProcess +SBTarget::AttachToProcessWithName +( + SBListener &listener, + const char *name, // basename of process to attach to + bool wait_for, // if true wait for a new instance of "name" to be launched + SBError& error // An error explaining what went wrong if attach fails +) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + SBProcess sb_process; + ProcessSP process_sp; + TargetSP target_sp(GetSP()); + + if (log) + { + log->Printf ("SBTarget(%p)::AttachToProcessWithName (listener, name=%s, wait_for=%s, error)...", target_sp.get(), name, wait_for ? "true" : "false"); + } + + if (name && target_sp) + { + Mutex::Locker api_locker (target_sp->GetAPIMutex()); + + StateType state = eStateInvalid; + process_sp = target_sp->GetProcessSP(); + if (process_sp) + { + state = process_sp->GetState(); + + if (process_sp->IsAlive() && state != eStateConnected) + { + if (state == eStateAttaching) + error.SetErrorString ("process attach is in progress"); + else + error.SetErrorString ("a process is already being debugged"); + return sb_process; + } + } + + if (state == eStateConnected) + { + // If we are already connected, then we have already specified the + // listener, so if a valid listener is supplied, we need to error out + // to let the client know. + if (listener.IsValid()) + { + error.SetErrorString ("process is connected and already has a listener, pass empty listener"); + return sb_process; + } + } + else + { + if (listener.IsValid()) + process_sp = target_sp->CreateProcess (listener.ref(), NULL, NULL); + else + process_sp = target_sp->CreateProcess (target_sp->GetDebugger().GetListener(), NULL, NULL); + } + + if (process_sp) + { + sb_process.SetSP (process_sp); + ProcessAttachInfo attach_info; + attach_info.GetExecutableFile().SetFile(name, false); + attach_info.SetWaitForLaunch(wait_for); + error.SetError (process_sp->Attach (attach_info)); + if (error.Success()) + { + // If we are doing synchronous mode, then wait for the + // process to stop! + if (target_sp->GetDebugger().GetAsyncExecution () == false) + process_sp->WaitForProcessToStop (NULL); + } + } + else + { + error.SetErrorString ("unable to create lldb_private::Process"); + } + } + else + { + error.SetErrorString ("SBTarget is invalid"); + } + + if (log) + { + log->Printf ("SBTarget(%p)::AttachToPorcessWithName (...) => SBProcess(%p)", + target_sp.get(), process_sp.get()); + } + return sb_process; +} + +lldb::SBProcess +SBTarget::ConnectRemote +( + SBListener &listener, + const char *url, + const char *plugin_name, + SBError& error +) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + SBProcess sb_process; + ProcessSP process_sp; + TargetSP target_sp(GetSP()); + + if (log) + { + log->Printf ("SBTarget(%p)::ConnectRemote (listener, url=%s, plugin_name=%s, error)...", target_sp.get(), url, plugin_name); + } + + if (target_sp) + { + Mutex::Locker api_locker (target_sp->GetAPIMutex()); + if (listener.IsValid()) + process_sp = target_sp->CreateProcess (listener.ref(), plugin_name, NULL); + else + process_sp = target_sp->CreateProcess (target_sp->GetDebugger().GetListener(), plugin_name, NULL); + + + if (process_sp) + { + sb_process.SetSP (process_sp); + error.SetError (process_sp->ConnectRemote (NULL, url)); + } + else + { + error.SetErrorString ("unable to create lldb_private::Process"); + } + } + else + { + error.SetErrorString ("SBTarget is invalid"); + } + + if (log) + { + log->Printf ("SBTarget(%p)::ConnectRemote (...) => SBProcess(%p)", + target_sp.get(), process_sp.get()); + } + return sb_process; +} + +SBFileSpec +SBTarget::GetExecutable () +{ + + SBFileSpec exe_file_spec; + TargetSP target_sp(GetSP()); + if (target_sp) + { + Module *exe_module = target_sp->GetExecutableModulePointer(); + if (exe_module) + exe_file_spec.SetFileSpec (exe_module->GetFileSpec()); + } + + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (log) + { + log->Printf ("SBTarget(%p)::GetExecutable () => SBFileSpec(%p)", + target_sp.get(), exe_file_spec.get()); + } + + return exe_file_spec; +} + +bool +SBTarget::operator == (const SBTarget &rhs) const +{ + return m_opaque_sp.get() == rhs.m_opaque_sp.get(); +} + +bool +SBTarget::operator != (const SBTarget &rhs) const +{ + return m_opaque_sp.get() != rhs.m_opaque_sp.get(); +} + +lldb::TargetSP +SBTarget::GetSP () const +{ + return m_opaque_sp; +} + +void +SBTarget::SetSP (const lldb::TargetSP& target_sp) +{ + m_opaque_sp = target_sp; +} + +lldb::SBAddress +SBTarget::ResolveLoadAddress (lldb::addr_t vm_addr) +{ + lldb::SBAddress sb_addr; + Address &addr = sb_addr.ref(); + TargetSP target_sp(GetSP()); + if (target_sp) + { + Mutex::Locker api_locker (target_sp->GetAPIMutex()); + if (target_sp->GetSectionLoadList().ResolveLoadAddress (vm_addr, addr)) + return sb_addr; + } + + // We have a load address that isn't in a section, just return an address + // with the offset filled in (the address) and the section set to NULL + addr.SetRawAddress(vm_addr); + return sb_addr; +} + +SBSymbolContext +SBTarget::ResolveSymbolContextForAddress (const SBAddress& addr, uint32_t resolve_scope) +{ + SBSymbolContext sc; + if (addr.IsValid()) + { + TargetSP target_sp(GetSP()); + if (target_sp) + target_sp->GetImages().ResolveSymbolContextForAddress (addr.ref(), resolve_scope, sc.ref()); + } + return sc; +} + + +SBBreakpoint +SBTarget::BreakpointCreateByLocation (const char *file, uint32_t line) +{ + return SBBreakpoint(BreakpointCreateByLocation (SBFileSpec (file, false), line)); +} + +SBBreakpoint +SBTarget::BreakpointCreateByLocation (const SBFileSpec &sb_file_spec, uint32_t line) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + SBBreakpoint sb_bp; + TargetSP target_sp(GetSP()); + if (target_sp && line != 0) + { + Mutex::Locker api_locker (target_sp->GetAPIMutex()); + + const LazyBool check_inlines = eLazyBoolCalculate; + const LazyBool skip_prologue = eLazyBoolCalculate; + const bool internal = false; + *sb_bp = target_sp->CreateBreakpoint (NULL, *sb_file_spec, line, check_inlines, skip_prologue, internal); + } + + if (log) + { + SBStream sstr; + sb_bp.GetDescription (sstr); + char path[PATH_MAX]; + sb_file_spec->GetPath (path, sizeof(path)); + log->Printf ("SBTarget(%p)::BreakpointCreateByLocation ( %s:%u ) => SBBreakpoint(%p): %s", + target_sp.get(), + path, + line, + sb_bp.get(), + sstr.GetData()); + } + + return sb_bp; +} + +SBBreakpoint +SBTarget::BreakpointCreateByName (const char *symbol_name, const char *module_name) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + SBBreakpoint sb_bp; + TargetSP target_sp(GetSP()); + if (target_sp.get()) + { + Mutex::Locker api_locker (target_sp->GetAPIMutex()); + + const bool internal = false; + const LazyBool skip_prologue = eLazyBoolCalculate; + if (module_name && module_name[0]) + { + FileSpecList module_spec_list; + module_spec_list.Append (FileSpec (module_name, false)); + *sb_bp = target_sp->CreateBreakpoint (&module_spec_list, NULL, symbol_name, eFunctionNameTypeAuto, skip_prologue, internal); + } + else + { + *sb_bp = target_sp->CreateBreakpoint (NULL, NULL, symbol_name, eFunctionNameTypeAuto, skip_prologue, internal); + } + } + + if (log) + { + log->Printf ("SBTarget(%p)::BreakpointCreateByName (symbol=\"%s\", module=\"%s\") => SBBreakpoint(%p)", + target_sp.get(), symbol_name, module_name, sb_bp.get()); + } + + return sb_bp; +} + +lldb::SBBreakpoint +SBTarget::BreakpointCreateByName (const char *symbol_name, + const SBFileSpecList &module_list, + const SBFileSpecList &comp_unit_list) +{ + uint32_t name_type_mask = eFunctionNameTypeAuto; + return BreakpointCreateByName (symbol_name, name_type_mask, module_list, comp_unit_list); +} + +lldb::SBBreakpoint +SBTarget::BreakpointCreateByName (const char *symbol_name, + uint32_t name_type_mask, + const SBFileSpecList &module_list, + const SBFileSpecList &comp_unit_list) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + SBBreakpoint sb_bp; + TargetSP target_sp(GetSP()); + if (target_sp && symbol_name && symbol_name[0]) + { + const bool internal = false; + const LazyBool skip_prologue = eLazyBoolCalculate; + Mutex::Locker api_locker (target_sp->GetAPIMutex()); + *sb_bp = target_sp->CreateBreakpoint (module_list.get(), + comp_unit_list.get(), + symbol_name, + name_type_mask, + skip_prologue, + internal); + } + + if (log) + { + log->Printf ("SBTarget(%p)::BreakpointCreateByName (symbol=\"%s\", name_type: %d) => SBBreakpoint(%p)", + target_sp.get(), symbol_name, name_type_mask, sb_bp.get()); + } + + return sb_bp; +} + +lldb::SBBreakpoint +SBTarget::BreakpointCreateByNames (const char *symbol_names[], + uint32_t num_names, + uint32_t name_type_mask, + const SBFileSpecList &module_list, + const SBFileSpecList &comp_unit_list) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + SBBreakpoint sb_bp; + TargetSP target_sp(GetSP()); + if (target_sp && num_names > 0) + { + Mutex::Locker api_locker (target_sp->GetAPIMutex()); + const bool internal = false; + const LazyBool skip_prologue = eLazyBoolCalculate; + *sb_bp = target_sp->CreateBreakpoint (module_list.get(), + comp_unit_list.get(), + symbol_names, + num_names, + name_type_mask, + skip_prologue, + internal); + } + + if (log) + { + log->Printf ("SBTarget(%p)::BreakpointCreateByName (symbols={", target_sp.get()); + for (uint32_t i = 0 ; i < num_names; i++) + { + char sep; + if (i < num_names - 1) + sep = ','; + else + sep = '}'; + if (symbol_names[i] != NULL) + log->Printf ("\"%s\"%c ", symbol_names[i], sep); + else + log->Printf ("\"\"%c ", sep); + + } + log->Printf ("name_type: %d) => SBBreakpoint(%p)", name_type_mask, sb_bp.get()); + } + + return sb_bp; +} + +SBBreakpoint +SBTarget::BreakpointCreateByRegex (const char *symbol_name_regex, const char *module_name) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + SBBreakpoint sb_bp; + TargetSP target_sp(GetSP()); + if (target_sp && symbol_name_regex && symbol_name_regex[0]) + { + Mutex::Locker api_locker (target_sp->GetAPIMutex()); + RegularExpression regexp(symbol_name_regex); + const bool internal = false; + const LazyBool skip_prologue = eLazyBoolCalculate; + + if (module_name && module_name[0]) + { + FileSpecList module_spec_list; + module_spec_list.Append (FileSpec (module_name, false)); + + *sb_bp = target_sp->CreateFuncRegexBreakpoint (&module_spec_list, NULL, regexp, skip_prologue, internal); + } + else + { + *sb_bp = target_sp->CreateFuncRegexBreakpoint (NULL, NULL, regexp, skip_prologue, internal); + } + } + + if (log) + { + log->Printf ("SBTarget(%p)::BreakpointCreateByRegex (symbol_regex=\"%s\", module_name=\"%s\") => SBBreakpoint(%p)", + target_sp.get(), symbol_name_regex, module_name, sb_bp.get()); + } + + return sb_bp; +} + +lldb::SBBreakpoint +SBTarget::BreakpointCreateByRegex (const char *symbol_name_regex, + const SBFileSpecList &module_list, + const SBFileSpecList &comp_unit_list) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + SBBreakpoint sb_bp; + TargetSP target_sp(GetSP()); + if (target_sp && symbol_name_regex && symbol_name_regex[0]) + { + Mutex::Locker api_locker (target_sp->GetAPIMutex()); + RegularExpression regexp(symbol_name_regex); + const bool internal = false; + const LazyBool skip_prologue = eLazyBoolCalculate; + + *sb_bp = target_sp->CreateFuncRegexBreakpoint (module_list.get(), comp_unit_list.get(), regexp, skip_prologue, internal); + } + + if (log) + { + log->Printf ("SBTarget(%p)::BreakpointCreateByRegex (symbol_regex=\"%s\") => SBBreakpoint(%p)", + target_sp.get(), symbol_name_regex, sb_bp.get()); + } + + return sb_bp; +} + +SBBreakpoint +SBTarget::BreakpointCreateByAddress (addr_t address) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + SBBreakpoint sb_bp; + TargetSP target_sp(GetSP()); + if (target_sp) + { + Mutex::Locker api_locker (target_sp->GetAPIMutex()); + *sb_bp = target_sp->CreateBreakpoint (address, false); + } + + if (log) + { + log->Printf ("SBTarget(%p)::BreakpointCreateByAddress (address=%" PRIu64 ") => SBBreakpoint(%p)", target_sp.get(), (uint64_t) address, sb_bp.get()); + } + + return sb_bp; +} + +lldb::SBBreakpoint +SBTarget::BreakpointCreateBySourceRegex (const char *source_regex, const lldb::SBFileSpec &source_file, const char *module_name) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + SBBreakpoint sb_bp; + TargetSP target_sp(GetSP()); + if (target_sp && source_regex && source_regex[0]) + { + Mutex::Locker api_locker (target_sp->GetAPIMutex()); + RegularExpression regexp(source_regex); + FileSpecList source_file_spec_list; + source_file_spec_list.Append (source_file.ref()); + + if (module_name && module_name[0]) + { + FileSpecList module_spec_list; + module_spec_list.Append (FileSpec (module_name, false)); + + *sb_bp = target_sp->CreateSourceRegexBreakpoint (&module_spec_list, &source_file_spec_list, regexp, false); + } + else + { + *sb_bp = target_sp->CreateSourceRegexBreakpoint (NULL, &source_file_spec_list, regexp, false); + } + } + + if (log) + { + char path[PATH_MAX]; + source_file->GetPath (path, sizeof(path)); + log->Printf ("SBTarget(%p)::BreakpointCreateByRegex (source_regex=\"%s\", file=\"%s\", module_name=\"%s\") => SBBreakpoint(%p)", + target_sp.get(), source_regex, path, module_name, sb_bp.get()); + } + + return sb_bp; +} + +lldb::SBBreakpoint +SBTarget::BreakpointCreateBySourceRegex (const char *source_regex, + const SBFileSpecList &module_list, + const lldb::SBFileSpecList &source_file_list) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + SBBreakpoint sb_bp; + TargetSP target_sp(GetSP()); + if (target_sp && source_regex && source_regex[0]) + { + Mutex::Locker api_locker (target_sp->GetAPIMutex()); + RegularExpression regexp(source_regex); + *sb_bp = target_sp->CreateSourceRegexBreakpoint (module_list.get(), source_file_list.get(), regexp, false); + } + + if (log) + { + log->Printf ("SBTarget(%p)::BreakpointCreateByRegex (source_regex=\"%s\") => SBBreakpoint(%p)", + target_sp.get(), source_regex, sb_bp.get()); + } + + return sb_bp; +} + +lldb::SBBreakpoint +SBTarget::BreakpointCreateForException (lldb::LanguageType language, + bool catch_bp, + bool throw_bp) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + SBBreakpoint sb_bp; + TargetSP target_sp(GetSP()); + if (target_sp) + { + Mutex::Locker api_locker (target_sp->GetAPIMutex()); + *sb_bp = target_sp->CreateExceptionBreakpoint (language, catch_bp, throw_bp); + } + + if (log) + { + log->Printf ("SBTarget(%p)::BreakpointCreateByRegex (Language: %s, catch: %s throw: %s) => SBBreakpoint(%p)", + target_sp.get(), + LanguageRuntime::GetNameForLanguageType(language), + catch_bp ? "on" : "off", + throw_bp ? "on" : "off", + sb_bp.get()); + } + + return sb_bp; +} + +uint32_t +SBTarget::GetNumBreakpoints () const +{ + TargetSP target_sp(GetSP()); + if (target_sp) + { + // The breakpoint list is thread safe, no need to lock + return target_sp->GetBreakpointList().GetSize(); + } + return 0; +} + +SBBreakpoint +SBTarget::GetBreakpointAtIndex (uint32_t idx) const +{ + SBBreakpoint sb_breakpoint; + TargetSP target_sp(GetSP()); + if (target_sp) + { + // The breakpoint list is thread safe, no need to lock + *sb_breakpoint = target_sp->GetBreakpointList().GetBreakpointAtIndex(idx); + } + return sb_breakpoint; +} + +bool +SBTarget::BreakpointDelete (break_id_t bp_id) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + bool result = false; + TargetSP target_sp(GetSP()); + if (target_sp) + { + Mutex::Locker api_locker (target_sp->GetAPIMutex()); + result = target_sp->RemoveBreakpointByID (bp_id); + } + + if (log) + { + log->Printf ("SBTarget(%p)::BreakpointDelete (bp_id=%d) => %i", target_sp.get(), (uint32_t) bp_id, result); + } + + return result; +} + +SBBreakpoint +SBTarget::FindBreakpointByID (break_id_t bp_id) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + SBBreakpoint sb_breakpoint; + TargetSP target_sp(GetSP()); + if (target_sp && bp_id != LLDB_INVALID_BREAK_ID) + { + Mutex::Locker api_locker (target_sp->GetAPIMutex()); + *sb_breakpoint = target_sp->GetBreakpointByID (bp_id); + } + + if (log) + { + log->Printf ("SBTarget(%p)::FindBreakpointByID (bp_id=%d) => SBBreakpoint(%p)", + target_sp.get(), (uint32_t) bp_id, sb_breakpoint.get()); + } + + return sb_breakpoint; +} + +bool +SBTarget::EnableAllBreakpoints () +{ + TargetSP target_sp(GetSP()); + if (target_sp) + { + Mutex::Locker api_locker (target_sp->GetAPIMutex()); + target_sp->EnableAllBreakpoints (); + return true; + } + return false; +} + +bool +SBTarget::DisableAllBreakpoints () +{ + TargetSP target_sp(GetSP()); + if (target_sp) + { + Mutex::Locker api_locker (target_sp->GetAPIMutex()); + target_sp->DisableAllBreakpoints (); + return true; + } + return false; +} + +bool +SBTarget::DeleteAllBreakpoints () +{ + TargetSP target_sp(GetSP()); + if (target_sp) + { + Mutex::Locker api_locker (target_sp->GetAPIMutex()); + target_sp->RemoveAllBreakpoints (); + return true; + } + return false; +} + +uint32_t +SBTarget::GetNumWatchpoints () const +{ + TargetSP target_sp(GetSP()); + if (target_sp) + { + // The watchpoint list is thread safe, no need to lock + return target_sp->GetWatchpointList().GetSize(); + } + return 0; +} + +SBWatchpoint +SBTarget::GetWatchpointAtIndex (uint32_t idx) const +{ + SBWatchpoint sb_watchpoint; + TargetSP target_sp(GetSP()); + if (target_sp) + { + // The watchpoint list is thread safe, no need to lock + sb_watchpoint.SetSP (target_sp->GetWatchpointList().GetByIndex(idx)); + } + return sb_watchpoint; +} + +bool +SBTarget::DeleteWatchpoint (watch_id_t wp_id) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + bool result = false; + TargetSP target_sp(GetSP()); + if (target_sp) + { + Mutex::Locker api_locker (target_sp->GetAPIMutex()); + Mutex::Locker locker; + target_sp->GetWatchpointList().GetListMutex(locker); + result = target_sp->RemoveWatchpointByID (wp_id); + } + + if (log) + { + log->Printf ("SBTarget(%p)::WatchpointDelete (wp_id=%d) => %i", target_sp.get(), (uint32_t) wp_id, result); + } + + return result; +} + +SBWatchpoint +SBTarget::FindWatchpointByID (lldb::watch_id_t wp_id) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + SBWatchpoint sb_watchpoint; + lldb::WatchpointSP watchpoint_sp; + TargetSP target_sp(GetSP()); + if (target_sp && wp_id != LLDB_INVALID_WATCH_ID) + { + Mutex::Locker api_locker (target_sp->GetAPIMutex()); + Mutex::Locker locker; + target_sp->GetWatchpointList().GetListMutex(locker); + watchpoint_sp = target_sp->GetWatchpointList().FindByID(wp_id); + sb_watchpoint.SetSP (watchpoint_sp); + } + + if (log) + { + log->Printf ("SBTarget(%p)::FindWatchpointByID (bp_id=%d) => SBWatchpoint(%p)", + target_sp.get(), (uint32_t) wp_id, watchpoint_sp.get()); + } + + return sb_watchpoint; +} + +lldb::SBWatchpoint +SBTarget::WatchAddress (lldb::addr_t addr, size_t size, bool read, bool write, SBError &error) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + SBWatchpoint sb_watchpoint; + lldb::WatchpointSP watchpoint_sp; + TargetSP target_sp(GetSP()); + if (target_sp && (read || write) && addr != LLDB_INVALID_ADDRESS && size > 0) + { + Mutex::Locker api_locker (target_sp->GetAPIMutex()); + uint32_t watch_type = 0; + if (read) + watch_type |= LLDB_WATCH_TYPE_READ; + if (write) + watch_type |= LLDB_WATCH_TYPE_WRITE; + if (watch_type == 0) + { + error.SetErrorString("Can't create a watchpoint that is neither read nor write."); + return sb_watchpoint; + } + + // Target::CreateWatchpoint() is thread safe. + Error cw_error; + // This API doesn't take in a type, so we can't figure out what it is. + ClangASTType *type = NULL; + watchpoint_sp = target_sp->CreateWatchpoint(addr, size, type, watch_type, cw_error); + error.SetError(cw_error); + sb_watchpoint.SetSP (watchpoint_sp); + } + + if (log) + { + log->Printf ("SBTarget(%p)::WatchAddress (addr=0x%" PRIx64 ", 0x%u) => SBWatchpoint(%p)", + target_sp.get(), addr, (uint32_t) size, watchpoint_sp.get()); + } + + return sb_watchpoint; +} + +bool +SBTarget::EnableAllWatchpoints () +{ + TargetSP target_sp(GetSP()); + if (target_sp) + { + Mutex::Locker api_locker (target_sp->GetAPIMutex()); + Mutex::Locker locker; + target_sp->GetWatchpointList().GetListMutex(locker); + target_sp->EnableAllWatchpoints (); + return true; + } + return false; +} + +bool +SBTarget::DisableAllWatchpoints () +{ + TargetSP target_sp(GetSP()); + if (target_sp) + { + Mutex::Locker api_locker (target_sp->GetAPIMutex()); + Mutex::Locker locker; + target_sp->GetWatchpointList().GetListMutex(locker); + target_sp->DisableAllWatchpoints (); + return true; + } + return false; +} + +bool +SBTarget::DeleteAllWatchpoints () +{ + TargetSP target_sp(GetSP()); + if (target_sp) + { + Mutex::Locker api_locker (target_sp->GetAPIMutex()); + Mutex::Locker locker; + target_sp->GetWatchpointList().GetListMutex(locker); + target_sp->RemoveAllWatchpoints (); + return true; + } + return false; +} + + +lldb::SBModule +SBTarget::AddModule (const char *path, + const char *triple, + const char *uuid_cstr) +{ + return AddModule (path, triple, uuid_cstr, NULL); +} + +lldb::SBModule +SBTarget::AddModule (const char *path, + const char *triple, + const char *uuid_cstr, + const char *symfile) +{ + lldb::SBModule sb_module; + TargetSP target_sp(GetSP()); + if (target_sp) + { + ModuleSpec module_spec; + if (path) + module_spec.GetFileSpec().SetFile(path, false); + + if (uuid_cstr) + module_spec.GetUUID().SetFromCString(uuid_cstr); + + if (triple) + module_spec.GetArchitecture().SetTriple (triple, target_sp->GetPlatform ().get()); + + if (symfile) + module_spec.GetSymbolFileSpec ().SetFile(symfile, false); + + sb_module.SetSP(target_sp->GetSharedModule (module_spec)); + } + return sb_module; +} + +lldb::SBModule +SBTarget::AddModule (const SBModuleSpec &module_spec) +{ + lldb::SBModule sb_module; + TargetSP target_sp(GetSP()); + if (target_sp) + sb_module.SetSP(target_sp->GetSharedModule (*module_spec.m_opaque_ap)); + return sb_module; +} + +bool +SBTarget::AddModule (lldb::SBModule &module) +{ + TargetSP target_sp(GetSP()); + if (target_sp) + { + target_sp->GetImages().AppendIfNeeded (module.GetSP()); + return true; + } + return false; +} + +uint32_t +SBTarget::GetNumModules () const +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + uint32_t num = 0; + TargetSP target_sp(GetSP()); + if (target_sp) + { + // The module list is thread safe, no need to lock + num = target_sp->GetImages().GetSize(); + } + + if (log) + log->Printf ("SBTarget(%p)::GetNumModules () => %d", target_sp.get(), num); + + return num; +} + +void +SBTarget::Clear () +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + if (log) + log->Printf ("SBTarget(%p)::Clear ()", m_opaque_sp.get()); + + m_opaque_sp.reset(); +} + + +SBModule +SBTarget::FindModule (const SBFileSpec &sb_file_spec) +{ + SBModule sb_module; + TargetSP target_sp(GetSP()); + if (target_sp && sb_file_spec.IsValid()) + { + ModuleSpec module_spec(*sb_file_spec); + // The module list is thread safe, no need to lock + sb_module.SetSP (target_sp->GetImages().FindFirstModule (module_spec)); + } + return sb_module; +} + +lldb::ByteOrder +SBTarget::GetByteOrder () +{ + TargetSP target_sp(GetSP()); + if (target_sp) + return target_sp->GetArchitecture().GetByteOrder(); + return eByteOrderInvalid; +} + +const char * +SBTarget::GetTriple () +{ + TargetSP target_sp(GetSP()); + if (target_sp) + { + std::string triple (target_sp->GetArchitecture().GetTriple().str()); + // Unique the string so we don't run into ownership issues since + // the const strings put the string into the string pool once and + // the strings never comes out + ConstString const_triple (triple.c_str()); + return const_triple.GetCString(); + } + return NULL; +} + +uint32_t +SBTarget::GetAddressByteSize() +{ + TargetSP target_sp(GetSP()); + if (target_sp) + return target_sp->GetArchitecture().GetAddressByteSize(); + return sizeof(void*); +} + + +SBModule +SBTarget::GetModuleAtIndex (uint32_t idx) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + SBModule sb_module; + ModuleSP module_sp; + TargetSP target_sp(GetSP()); + if (target_sp) + { + // The module list is thread safe, no need to lock + module_sp = target_sp->GetImages().GetModuleAtIndex(idx); + sb_module.SetSP (module_sp); + } + + if (log) + { + log->Printf ("SBTarget(%p)::GetModuleAtIndex (idx=%d) => SBModule(%p)", + target_sp.get(), idx, module_sp.get()); + } + + return sb_module; +} + +bool +SBTarget::RemoveModule (lldb::SBModule module) +{ + TargetSP target_sp(GetSP()); + if (target_sp) + return target_sp->GetImages().Remove(module.GetSP()); + return false; +} + + +SBBroadcaster +SBTarget::GetBroadcaster () const +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + TargetSP target_sp(GetSP()); + SBBroadcaster broadcaster(target_sp.get(), false); + + if (log) + log->Printf ("SBTarget(%p)::GetBroadcaster () => SBBroadcaster(%p)", + target_sp.get(), broadcaster.get()); + + return broadcaster; +} + +bool +SBTarget::GetDescription (SBStream &description, lldb::DescriptionLevel description_level) +{ + Stream &strm = description.ref(); + + TargetSP target_sp(GetSP()); + if (target_sp) + { + target_sp->Dump (&strm, description_level); + } + else + strm.PutCString ("No value"); + + return true; +} + +lldb::SBSymbolContextList +SBTarget::FindFunctions (const char *name, uint32_t name_type_mask) +{ + lldb::SBSymbolContextList sb_sc_list; + if (name && name[0]) + { + TargetSP target_sp(GetSP()); + if (target_sp) + { + const bool symbols_ok = true; + const bool inlines_ok = true; + const bool append = true; + target_sp->GetImages().FindFunctions (ConstString(name), + name_type_mask, + symbols_ok, + inlines_ok, + append, + *sb_sc_list); + } + } + return sb_sc_list; +} + +lldb::SBType +SBTarget::FindFirstType (const char* typename_cstr) +{ + TargetSP target_sp(GetSP()); + if (typename_cstr && typename_cstr[0] && target_sp) + { + ConstString const_typename(typename_cstr); + SymbolContext sc; + const bool exact_match = false; + + const ModuleList &module_list = target_sp->GetImages(); + size_t count = module_list.GetSize(); + for (size_t idx = 0; idx < count; idx++) + { + ModuleSP module_sp (module_list.GetModuleAtIndex(idx)); + if (module_sp) + { + TypeSP type_sp (module_sp->FindFirstType(sc, const_typename, exact_match)); + if (type_sp) + return SBType(type_sp); + } + } + + // Didn't find the type in the symbols; try the Objective-C runtime + // if one is installed + + ProcessSP process_sp(target_sp->GetProcessSP()); + + if (process_sp) + { + ObjCLanguageRuntime *objc_language_runtime = process_sp->GetObjCLanguageRuntime(); + + if (objc_language_runtime) + { + TypeVendor *objc_type_vendor = objc_language_runtime->GetTypeVendor(); + + if (objc_type_vendor) + { + std::vector types; + + if (objc_type_vendor->FindTypes(const_typename, true, 1, types) > 0) + return SBType(types[0]); + } + } + } + + // No matches, search for basic typename matches + ClangASTContext *clang_ast = target_sp->GetScratchClangASTContext(); + if (clang_ast) + return SBType (ClangASTContext::GetBasicType (clang_ast->getASTContext(), const_typename)); + } + return SBType(); +} + +SBType +SBTarget::GetBasicType(lldb::BasicType type) +{ + TargetSP target_sp(GetSP()); + if (target_sp) + { + ClangASTContext *clang_ast = target_sp->GetScratchClangASTContext(); + if (clang_ast) + return SBType (ClangASTContext::GetBasicType (clang_ast->getASTContext(), type)); + } + return SBType(); +} + + +lldb::SBTypeList +SBTarget::FindTypes (const char* typename_cstr) +{ + SBTypeList sb_type_list; + TargetSP target_sp(GetSP()); + if (typename_cstr && typename_cstr[0] && target_sp) + { + ModuleList& images = target_sp->GetImages(); + ConstString const_typename(typename_cstr); + bool exact_match = false; + SymbolContext sc; + TypeList type_list; + + uint32_t num_matches = images.FindTypes (sc, + const_typename, + exact_match, + UINT32_MAX, + type_list); + + if (num_matches > 0) + { + for (size_t idx = 0; idx < num_matches; idx++) + { + TypeSP type_sp (type_list.GetTypeAtIndex(idx)); + if (type_sp) + sb_type_list.Append(SBType(type_sp)); + } + } + + // Try the Objective-C runtime if one is installed + + ProcessSP process_sp(target_sp->GetProcessSP()); + + if (process_sp) + { + ObjCLanguageRuntime *objc_language_runtime = process_sp->GetObjCLanguageRuntime(); + + if (objc_language_runtime) + { + TypeVendor *objc_type_vendor = objc_language_runtime->GetTypeVendor(); + + if (objc_type_vendor) + { + std::vector types; + + if (objc_type_vendor->FindTypes(const_typename, true, UINT32_MAX, types)) + { + for (ClangASTType &type : types) + { + sb_type_list.Append(SBType(type)); + } + } + } + } + } + + if (sb_type_list.GetSize() == 0) + { + // No matches, search for basic typename matches + ClangASTContext *clang_ast = target_sp->GetScratchClangASTContext(); + if (clang_ast) + sb_type_list.Append (SBType (ClangASTContext::GetBasicType (clang_ast->getASTContext(), const_typename))); + } + } + return sb_type_list; +} + +SBValueList +SBTarget::FindGlobalVariables (const char *name, uint32_t max_matches) +{ + SBValueList sb_value_list; + + TargetSP target_sp(GetSP()); + if (name && target_sp) + { + VariableList variable_list; + const bool append = true; + const uint32_t match_count = target_sp->GetImages().FindGlobalVariables (ConstString (name), + append, + max_matches, + variable_list); + + if (match_count > 0) + { + ExecutionContextScope *exe_scope = target_sp->GetProcessSP().get(); + if (exe_scope == NULL) + exe_scope = target_sp.get(); + for (uint32_t i=0; i 0) + return sb_value_list.GetValueAtIndex(0); + return SBValue(); +} + +SBSourceManager +SBTarget::GetSourceManager() +{ + SBSourceManager source_manager (*this); + return source_manager; +} + +lldb::SBInstructionList +SBTarget::ReadInstructions (lldb::SBAddress base_addr, uint32_t count) +{ + return ReadInstructions (base_addr, count, NULL); +} + +lldb::SBInstructionList +SBTarget::ReadInstructions (lldb::SBAddress base_addr, uint32_t count, const char *flavor_string) +{ + SBInstructionList sb_instructions; + + TargetSP target_sp(GetSP()); + if (target_sp) + { + Address *addr_ptr = base_addr.get(); + + if (addr_ptr) + { + DataBufferHeap data (target_sp->GetArchitecture().GetMaximumOpcodeByteSize() * count, 0); + bool prefer_file_cache = false; + lldb_private::Error error; + lldb::addr_t load_addr = LLDB_INVALID_ADDRESS; + const size_t bytes_read = target_sp->ReadMemory(*addr_ptr, + prefer_file_cache, + data.GetBytes(), + data.GetByteSize(), + error, + &load_addr); + const bool data_from_file = load_addr == LLDB_INVALID_ADDRESS; + sb_instructions.SetDisassembler (Disassembler::DisassembleBytes (target_sp->GetArchitecture(), + NULL, + flavor_string, + *addr_ptr, + data.GetBytes(), + bytes_read, + count, + data_from_file)); + } + } + + return sb_instructions; + +} + +lldb::SBInstructionList +SBTarget::GetInstructions (lldb::SBAddress base_addr, const void *buf, size_t size) +{ + return GetInstructionsWithFlavor (base_addr, NULL, buf, size); +} + +lldb::SBInstructionList +SBTarget::GetInstructionsWithFlavor (lldb::SBAddress base_addr, const char *flavor_string, const void *buf, size_t size) +{ + SBInstructionList sb_instructions; + + TargetSP target_sp(GetSP()); + if (target_sp) + { + Address addr; + + if (base_addr.get()) + addr = *base_addr.get(); + + const bool data_from_file = true; + + sb_instructions.SetDisassembler (Disassembler::DisassembleBytes (target_sp->GetArchitecture(), + NULL, + flavor_string, + addr, + buf, + size, + UINT32_MAX, + data_from_file)); + } + + return sb_instructions; +} + +lldb::SBInstructionList +SBTarget::GetInstructions (lldb::addr_t base_addr, const void *buf, size_t size) +{ + return GetInstructionsWithFlavor (ResolveLoadAddress(base_addr), NULL, buf, size); +} + +lldb::SBInstructionList +SBTarget::GetInstructionsWithFlavor (lldb::addr_t base_addr, const char *flavor_string, const void *buf, size_t size) +{ + return GetInstructionsWithFlavor (ResolveLoadAddress(base_addr), flavor_string, buf, size); +} + +SBError +SBTarget::SetSectionLoadAddress (lldb::SBSection section, + lldb::addr_t section_base_addr) +{ + SBError sb_error; + TargetSP target_sp(GetSP()); + if (target_sp) + { + if (!section.IsValid()) + { + sb_error.SetErrorStringWithFormat ("invalid section"); + } + else + { + SectionSP section_sp (section.GetSP()); + if (section_sp) + { + if (section_sp->IsThreadSpecific()) + { + sb_error.SetErrorString ("thread specific sections are not yet supported"); + } + else + { + if (target_sp->GetSectionLoadList().SetSectionLoadAddress (section_sp, section_base_addr)) + { + // Flush info in the process (stack frames, etc) + ProcessSP process_sp (target_sp->GetProcessSP()); + if (process_sp) + process_sp->Flush(); + } + } + } + } + } + else + { + sb_error.SetErrorString ("invalid target"); + } + return sb_error; +} + +SBError +SBTarget::ClearSectionLoadAddress (lldb::SBSection section) +{ + SBError sb_error; + + TargetSP target_sp(GetSP()); + if (target_sp) + { + if (!section.IsValid()) + { + sb_error.SetErrorStringWithFormat ("invalid section"); + } + else + { + if (target_sp->GetSectionLoadList().SetSectionUnloaded (section.GetSP())) + { + // Flush info in the process (stack frames, etc) + ProcessSP process_sp (target_sp->GetProcessSP()); + if (process_sp) + process_sp->Flush(); + } + } + } + else + { + sb_error.SetErrorStringWithFormat ("invalid target"); + } + return sb_error; +} + +SBError +SBTarget::SetModuleLoadAddress (lldb::SBModule module, int64_t slide_offset) +{ + SBError sb_error; + + TargetSP target_sp(GetSP()); + if (target_sp) + { + ModuleSP module_sp (module.GetSP()); + if (module_sp) + { + bool changed = false; + if (module_sp->SetLoadAddress (*target_sp, slide_offset, changed)) + { + // The load was successful, make sure that at least some sections + // changed before we notify that our module was loaded. + if (changed) + { + ModuleList module_list; + module_list.Append(module_sp); + target_sp->ModulesDidLoad (module_list); + // Flush info in the process (stack frames, etc) + ProcessSP process_sp (target_sp->GetProcessSP()); + if (process_sp) + process_sp->Flush(); + } + } + } + else + { + sb_error.SetErrorStringWithFormat ("invalid module"); + } + + } + else + { + sb_error.SetErrorStringWithFormat ("invalid target"); + } + return sb_error; +} + +SBError +SBTarget::ClearModuleLoadAddress (lldb::SBModule module) +{ + SBError sb_error; + + char path[PATH_MAX]; + TargetSP target_sp(GetSP()); + if (target_sp) + { + ModuleSP module_sp (module.GetSP()); + if (module_sp) + { + ObjectFile *objfile = module_sp->GetObjectFile(); + if (objfile) + { + SectionList *section_list = objfile->GetSectionList(); + if (section_list) + { + bool changed = false; + const size_t num_sections = section_list->GetSize(); + for (size_t sect_idx = 0; sect_idx < num_sections; ++sect_idx) + { + SectionSP section_sp (section_list->GetSectionAtIndex(sect_idx)); + if (section_sp) + changed |= target_sp->GetSectionLoadList().SetSectionUnloaded (section_sp) > 0; + } + if (changed) + { + // Flush info in the process (stack frames, etc) + ProcessSP process_sp (target_sp->GetProcessSP()); + if (process_sp) + process_sp->Flush(); + } + } + else + { + module_sp->GetFileSpec().GetPath (path, sizeof(path)); + sb_error.SetErrorStringWithFormat ("no sections in object file '%s'", path); + } + } + else + { + module_sp->GetFileSpec().GetPath (path, sizeof(path)); + sb_error.SetErrorStringWithFormat ("no object file for module '%s'", path); + } + } + else + { + sb_error.SetErrorStringWithFormat ("invalid module"); + } + } + else + { + sb_error.SetErrorStringWithFormat ("invalid target"); + } + return sb_error; +} + + +lldb::SBSymbolContextList +SBTarget::FindSymbols (const char *name, lldb::SymbolType symbol_type) +{ + SBSymbolContextList sb_sc_list; + if (name && name[0]) + { + TargetSP target_sp(GetSP()); + if (target_sp) + { + bool append = true; + target_sp->GetImages().FindSymbolsWithNameAndType (ConstString(name), + symbol_type, + *sb_sc_list, + append); + } + } + return sb_sc_list; + +} + + +lldb::SBValue +SBTarget::EvaluateExpression (const char *expr, const SBExpressionOptions &options) +{ + Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + Log * expr_log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + SBValue expr_result; + ExecutionResults exe_results = eExecutionSetupError; + ValueObjectSP expr_value_sp; + TargetSP target_sp(GetSP()); + StackFrame *frame = NULL; + if (target_sp) + { + if (expr == NULL || expr[0] == '\0') + { + if (log) + log->Printf ("SBTarget::EvaluateExpression called with an empty expression"); + return expr_result; + } + + Mutex::Locker api_locker (target_sp->GetAPIMutex()); + ExecutionContext exe_ctx (m_opaque_sp.get()); + + if (log) + log->Printf ("SBTarget()::EvaluateExpression (expr=\"%s\")...", expr); + + frame = exe_ctx.GetFramePtr(); + Target *target = exe_ctx.GetTargetPtr(); + + if (target) + { +#ifdef LLDB_CONFIGURATION_DEBUG + StreamString frame_description; + if (frame) + frame->DumpUsingSettingsFormat (&frame_description); + Host::SetCrashDescriptionWithFormat ("SBTarget::EvaluateExpression (expr = \"%s\", fetch_dynamic_value = %u) %s", + expr, options.GetFetchDynamicValue(), frame_description.GetString().c_str()); +#endif + exe_results = target->EvaluateExpression (expr, + frame, + expr_value_sp, + options.ref()); + + expr_result.SetSP(expr_value_sp, options.GetFetchDynamicValue()); +#ifdef LLDB_CONFIGURATION_DEBUG + Host::SetCrashDescription (NULL); +#endif + } + else + { + if (log) + log->Printf ("SBTarget::EvaluateExpression () => error: could not reconstruct frame object for this SBTarget."); + } + } +#ifndef LLDB_DISABLE_PYTHON + if (expr_log) + expr_log->Printf("** [SBTarget::EvaluateExpression] Expression result is %s, summary %s **", + expr_result.GetValue(), + expr_result.GetSummary()); + + if (log) + log->Printf ("SBTarget(%p)::EvaluateExpression (expr=\"%s\") => SBValue(%p) (execution result=%d)", + frame, + expr, + expr_value_sp.get(), + exe_results); +#endif + + return expr_result; +} + + +lldb::addr_t +SBTarget::GetStackRedZoneSize() +{ + TargetSP target_sp(GetSP()); + if (target_sp) + { + ABISP abi_sp; + ProcessSP process_sp (target_sp->GetProcessSP()); + if (process_sp) + abi_sp = process_sp->GetABI(); + else + abi_sp = ABI::FindPlugin(target_sp->GetArchitecture()); + if (abi_sp) + return abi_sp->GetRedZoneSize(); + } + return 0; +} + diff --git a/contrib/llvm/tools/lldb/source/API/SBThread.cpp b/contrib/llvm/tools/lldb/source/API/SBThread.cpp new file mode 100644 index 00000000000..2752620c9ba --- /dev/null +++ b/contrib/llvm/tools/lldb/source/API/SBThread.cpp @@ -0,0 +1,1229 @@ +//===-- SBThread.cpp --------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "lldb/API/SBThread.h" + +#include "lldb/API/SBSymbolContext.h" +#include "lldb/API/SBFileSpec.h" +#include "lldb/API/SBStream.h" +#include "lldb/Breakpoint/BreakpointLocation.h" +#include "lldb/Core/Debugger.h" +#include "lldb/Core/State.h" +#include "lldb/Core/Stream.h" +#include "lldb/Core/StreamFile.h" +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Target/Thread.h" +#include "lldb/Target/Process.h" +#include "lldb/Symbol/SymbolContext.h" +#include "lldb/Symbol/CompileUnit.h" +#include "lldb/Target/StopInfo.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/ThreadPlan.h" +#include "lldb/Target/ThreadPlanStepInstruction.h" +#include "lldb/Target/ThreadPlanStepOut.h" +#include "lldb/Target/ThreadPlanStepRange.h" +#include "lldb/Target/ThreadPlanStepInRange.h" + + +#include "lldb/API/SBAddress.h" +#include "lldb/API/SBDebugger.h" +#include "lldb/API/SBEvent.h" +#include "lldb/API/SBFrame.h" +#include "lldb/API/SBProcess.h" +#include "lldb/API/SBValue.h" + +using namespace lldb; +using namespace lldb_private; + +const char * +SBThread::GetBroadcasterClassName () +{ + return Thread::GetStaticBroadcasterClass().AsCString(); +} + +//---------------------------------------------------------------------- +// Constructors +//---------------------------------------------------------------------- +SBThread::SBThread () : + m_opaque_sp (new ExecutionContextRef()) +{ +} + +SBThread::SBThread (const ThreadSP& lldb_object_sp) : + m_opaque_sp (new ExecutionContextRef(lldb_object_sp)) +{ +} + +SBThread::SBThread (const SBThread &rhs) : + m_opaque_sp (new ExecutionContextRef(*rhs.m_opaque_sp)) +{ + +} + +//---------------------------------------------------------------------- +// Assignment operator +//---------------------------------------------------------------------- + +const lldb::SBThread & +SBThread::operator = (const SBThread &rhs) +{ + if (this != &rhs) + *m_opaque_sp = *rhs.m_opaque_sp; + return *this; +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +SBThread::~SBThread() +{ +} + +bool +SBThread::IsValid() const +{ + return m_opaque_sp->GetThreadSP().get() != NULL; +} + +void +SBThread::Clear () +{ + m_opaque_sp->Clear(); +} + + +StopReason +SBThread::GetStopReason() +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + StopReason reason = eStopReasonInvalid; + Mutex::Locker api_locker; + ExecutionContext exe_ctx (m_opaque_sp.get(), api_locker); + + if (exe_ctx.HasThreadScope()) + { + Process::StopLocker stop_locker; + if (stop_locker.TryLock(&exe_ctx.GetProcessPtr()->GetRunLock())) + { + return exe_ctx.GetThreadPtr()->GetStopReason(); + } + else + { + if (log) + log->Printf ("SBThread(%p)::GetStopReason() => error: process is running", exe_ctx.GetThreadPtr()); + } + } + + if (log) + log->Printf ("SBThread(%p)::GetStopReason () => %s", exe_ctx.GetThreadPtr(), + Thread::StopReasonAsCString (reason)); + + return reason; +} + +size_t +SBThread::GetStopReasonDataCount () +{ + Mutex::Locker api_locker; + ExecutionContext exe_ctx (m_opaque_sp.get(), api_locker); + + if (exe_ctx.HasThreadScope()) + { + Process::StopLocker stop_locker; + if (stop_locker.TryLock(&exe_ctx.GetProcessPtr()->GetRunLock())) + { + StopInfoSP stop_info_sp = exe_ctx.GetThreadPtr()->GetStopInfo (); + if (stop_info_sp) + { + StopReason reason = stop_info_sp->GetStopReason(); + switch (reason) + { + case eStopReasonInvalid: + case eStopReasonNone: + case eStopReasonTrace: + case eStopReasonExec: + case eStopReasonPlanComplete: + case eStopReasonThreadExiting: + // There is no data for these stop reasons. + return 0; + + case eStopReasonBreakpoint: + { + break_id_t site_id = stop_info_sp->GetValue(); + lldb::BreakpointSiteSP bp_site_sp (exe_ctx.GetProcessPtr()->GetBreakpointSiteList().FindByID (site_id)); + if (bp_site_sp) + return bp_site_sp->GetNumberOfOwners () * 2; + else + return 0; // Breakpoint must have cleared itself... + } + break; + + case eStopReasonWatchpoint: + return 1; + + case eStopReasonSignal: + return 1; + + case eStopReasonException: + return 1; + } + } + } + else + { + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (log) + log->Printf ("SBThread(%p)::GetStopReasonDataCount() => error: process is running", exe_ctx.GetThreadPtr()); + } + } + return 0; +} + +uint64_t +SBThread::GetStopReasonDataAtIndex (uint32_t idx) +{ + Mutex::Locker api_locker; + ExecutionContext exe_ctx (m_opaque_sp.get(), api_locker); + + if (exe_ctx.HasThreadScope()) + { + Process::StopLocker stop_locker; + if (stop_locker.TryLock(&exe_ctx.GetProcessPtr()->GetRunLock())) + { + Thread *thread = exe_ctx.GetThreadPtr(); + StopInfoSP stop_info_sp = thread->GetStopInfo (); + if (stop_info_sp) + { + StopReason reason = stop_info_sp->GetStopReason(); + switch (reason) + { + case eStopReasonInvalid: + case eStopReasonNone: + case eStopReasonTrace: + case eStopReasonExec: + case eStopReasonPlanComplete: + case eStopReasonThreadExiting: + // There is no data for these stop reasons. + return 0; + + case eStopReasonBreakpoint: + { + break_id_t site_id = stop_info_sp->GetValue(); + lldb::BreakpointSiteSP bp_site_sp (exe_ctx.GetProcessPtr()->GetBreakpointSiteList().FindByID (site_id)); + if (bp_site_sp) + { + uint32_t bp_index = idx / 2; + BreakpointLocationSP bp_loc_sp (bp_site_sp->GetOwnerAtIndex (bp_index)); + if (bp_loc_sp) + { + if (bp_index & 1) + { + // Odd idx, return the breakpoint location ID + return bp_loc_sp->GetID(); + } + else + { + // Even idx, return the breakpoint ID + return bp_loc_sp->GetBreakpoint().GetID(); + } + } + } + return LLDB_INVALID_BREAK_ID; + } + break; + + case eStopReasonWatchpoint: + return stop_info_sp->GetValue(); + + case eStopReasonSignal: + return stop_info_sp->GetValue(); + + case eStopReasonException: + return stop_info_sp->GetValue(); + } + } + } + else + { + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (log) + log->Printf ("SBThread(%p)::GetStopReasonDataAtIndex() => error: process is running", exe_ctx.GetThreadPtr()); + } + } + return 0; +} + +size_t +SBThread::GetStopDescription (char *dst, size_t dst_len) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + Mutex::Locker api_locker; + ExecutionContext exe_ctx (m_opaque_sp.get(), api_locker); + + if (exe_ctx.HasThreadScope()) + { + Process::StopLocker stop_locker; + if (stop_locker.TryLock(&exe_ctx.GetProcessPtr()->GetRunLock())) + { + + StopInfoSP stop_info_sp = exe_ctx.GetThreadPtr()->GetStopInfo (); + if (stop_info_sp) + { + const char *stop_desc = stop_info_sp->GetDescription(); + if (stop_desc) + { + if (log) + log->Printf ("SBThread(%p)::GetStopDescription (dst, dst_len) => \"%s\"", + exe_ctx.GetThreadPtr(), stop_desc); + if (dst) + return ::snprintf (dst, dst_len, "%s", stop_desc); + else + { + // NULL dst passed in, return the length needed to contain the description + return ::strlen (stop_desc) + 1; // Include the NULL byte for size + } + } + else + { + size_t stop_desc_len = 0; + switch (stop_info_sp->GetStopReason()) + { + case eStopReasonTrace: + case eStopReasonPlanComplete: + { + static char trace_desc[] = "step"; + stop_desc = trace_desc; + stop_desc_len = sizeof(trace_desc); // Include the NULL byte for size + } + break; + + case eStopReasonBreakpoint: + { + static char bp_desc[] = "breakpoint hit"; + stop_desc = bp_desc; + stop_desc_len = sizeof(bp_desc); // Include the NULL byte for size + } + break; + + case eStopReasonWatchpoint: + { + static char wp_desc[] = "watchpoint hit"; + stop_desc = wp_desc; + stop_desc_len = sizeof(wp_desc); // Include the NULL byte for size + } + break; + + case eStopReasonSignal: + { + stop_desc = exe_ctx.GetProcessPtr()->GetUnixSignals ().GetSignalAsCString (stop_info_sp->GetValue()); + if (stop_desc == NULL || stop_desc[0] == '\0') + { + static char signal_desc[] = "signal"; + stop_desc = signal_desc; + stop_desc_len = sizeof(signal_desc); // Include the NULL byte for size + } + } + break; + + case eStopReasonException: + { + char exc_desc[] = "exception"; + stop_desc = exc_desc; + stop_desc_len = sizeof(exc_desc); // Include the NULL byte for size + } + break; + + case eStopReasonExec: + { + char exc_desc[] = "exec"; + stop_desc = exc_desc; + stop_desc_len = sizeof(exc_desc); // Include the NULL byte for size + } + break; + + case eStopReasonThreadExiting: + { + char limbo_desc[] = "thread exiting"; + stop_desc = limbo_desc; + stop_desc_len = sizeof(limbo_desc); + } + break; + default: + break; + } + + if (stop_desc && stop_desc[0]) + { + if (log) + log->Printf ("SBThread(%p)::GetStopDescription (dst, dst_len) => '%s'", + exe_ctx.GetThreadPtr(), stop_desc); + + if (dst) + return ::snprintf (dst, dst_len, "%s", stop_desc) + 1; // Include the NULL byte + + if (stop_desc_len == 0) + stop_desc_len = ::strlen (stop_desc) + 1; // Include the NULL byte + + return stop_desc_len; + } + } + } + } + else + { + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (log) + log->Printf ("SBThread(%p)::GetStopDescription() => error: process is running", exe_ctx.GetThreadPtr()); + } + } + if (dst) + *dst = 0; + return 0; +} + +SBValue +SBThread::GetStopReturnValue () +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + ValueObjectSP return_valobj_sp; + Mutex::Locker api_locker; + ExecutionContext exe_ctx (m_opaque_sp.get(), api_locker); + + if (exe_ctx.HasThreadScope()) + { + Process::StopLocker stop_locker; + if (stop_locker.TryLock(&exe_ctx.GetProcessPtr()->GetRunLock())) + { + StopInfoSP stop_info_sp = exe_ctx.GetThreadPtr()->GetStopInfo (); + if (stop_info_sp) + { + return_valobj_sp = StopInfo::GetReturnValueObject (stop_info_sp); + } + } + else + { + if (log) + log->Printf ("SBThread(%p)::GetStopReturnValue() => error: process is running", exe_ctx.GetThreadPtr()); + } + } + + if (log) + log->Printf ("SBThread(%p)::GetStopReturnValue () => %s", exe_ctx.GetThreadPtr(), + return_valobj_sp.get() + ? return_valobj_sp->GetValueAsCString() + : ""); + + return SBValue (return_valobj_sp); +} + +void +SBThread::SetThread (const ThreadSP& lldb_object_sp) +{ + m_opaque_sp->SetThreadSP (lldb_object_sp); +} + + +lldb::tid_t +SBThread::GetThreadID () const +{ + ThreadSP thread_sp(m_opaque_sp->GetThreadSP()); + if (thread_sp) + return thread_sp->GetID(); + return LLDB_INVALID_THREAD_ID; +} + +uint32_t +SBThread::GetIndexID () const +{ + ThreadSP thread_sp(m_opaque_sp->GetThreadSP()); + if (thread_sp) + return thread_sp->GetIndexID(); + return LLDB_INVALID_INDEX32; +} + +const char * +SBThread::GetName () const +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + const char *name = NULL; + Mutex::Locker api_locker; + ExecutionContext exe_ctx (m_opaque_sp.get(), api_locker); + + if (exe_ctx.HasThreadScope()) + { + Process::StopLocker stop_locker; + if (stop_locker.TryLock(&exe_ctx.GetProcessPtr()->GetRunLock())) + { + name = exe_ctx.GetThreadPtr()->GetName(); + } + else + { + if (log) + log->Printf ("SBThread(%p)::GetName() => error: process is running", exe_ctx.GetThreadPtr()); + } + } + + if (log) + log->Printf ("SBThread(%p)::GetName () => %s", exe_ctx.GetThreadPtr(), name ? name : "NULL"); + + return name; +} + +const char * +SBThread::GetQueueName () const +{ + const char *name = NULL; + Mutex::Locker api_locker; + ExecutionContext exe_ctx (m_opaque_sp.get(), api_locker); + + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (exe_ctx.HasThreadScope()) + { + Process::StopLocker stop_locker; + if (stop_locker.TryLock(&exe_ctx.GetProcessPtr()->GetRunLock())) + { + name = exe_ctx.GetThreadPtr()->GetQueueName(); + } + else + { + if (log) + log->Printf ("SBThread(%p)::GetQueueName() => error: process is running", exe_ctx.GetThreadPtr()); + } + } + + if (log) + log->Printf ("SBThread(%p)::GetQueueName () => %s", exe_ctx.GetThreadPtr(), name ? name : "NULL"); + + return name; +} + +SBError +SBThread::ResumeNewPlan (ExecutionContext &exe_ctx, ThreadPlan *new_plan) +{ + SBError sb_error; + + Process *process = exe_ctx.GetProcessPtr(); + if (!process) + { + sb_error.SetErrorString("No process in SBThread::ResumeNewPlan"); + return sb_error; + } + + Thread *thread = exe_ctx.GetThreadPtr(); + if (!thread) + { + sb_error.SetErrorString("No thread in SBThread::ResumeNewPlan"); + return sb_error; + } + + // User level plans should be Master Plans so they can be interrupted, other plans executed, and + // then a "continue" will resume the plan. + if (new_plan != NULL) + { + new_plan->SetIsMasterPlan(true); + new_plan->SetOkayToDiscard(false); + } + + // Why do we need to set the current thread by ID here??? + process->GetThreadList().SetSelectedThreadByID (thread->GetID()); + sb_error.ref() = process->Resume(); + + if (sb_error.Success()) + { + // If we are doing synchronous mode, then wait for the + // process to stop yet again! + if (process->GetTarget().GetDebugger().GetAsyncExecution () == false) + process->WaitForProcessToStop (NULL); + } + + return sb_error; +} + +void +SBThread::StepOver (lldb::RunMode stop_other_threads) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + Mutex::Locker api_locker; + ExecutionContext exe_ctx (m_opaque_sp.get(), api_locker); + + + if (log) + log->Printf ("SBThread(%p)::StepOver (stop_other_threads='%s')", exe_ctx.GetThreadPtr(), + Thread::RunModeAsCString (stop_other_threads)); + + if (exe_ctx.HasThreadScope()) + { + Thread *thread = exe_ctx.GetThreadPtr(); + bool abort_other_plans = false; + StackFrameSP frame_sp(thread->GetStackFrameAtIndex (0)); + + ThreadPlanSP new_plan_sp; + if (frame_sp) + { + if (frame_sp->HasDebugInformation ()) + { + SymbolContext sc(frame_sp->GetSymbolContext(eSymbolContextEverything)); + new_plan_sp = thread->QueueThreadPlanForStepOverRange (abort_other_plans, + sc.line_entry.range, + sc, + stop_other_threads); + } + else + { + new_plan_sp = thread->QueueThreadPlanForStepSingleInstruction (true, + abort_other_plans, + stop_other_threads); + } + } + + // This returns an error, we should use it! + ResumeNewPlan (exe_ctx, new_plan_sp.get()); + } +} + +void +SBThread::StepInto (lldb::RunMode stop_other_threads) +{ + StepInto (NULL, stop_other_threads); +} + +void +SBThread::StepInto (const char *target_name, lldb::RunMode stop_other_threads) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + Mutex::Locker api_locker; + ExecutionContext exe_ctx (m_opaque_sp.get(), api_locker); + + if (log) + log->Printf ("SBThread(%p)::StepInto (target_name='%s', stop_other_threads='%s')", + exe_ctx.GetThreadPtr(), + target_name? target_name: "", + Thread::RunModeAsCString (stop_other_threads)); + + if (exe_ctx.HasThreadScope()) + { + bool abort_other_plans = false; + + Thread *thread = exe_ctx.GetThreadPtr(); + StackFrameSP frame_sp(thread->GetStackFrameAtIndex (0)); + ThreadPlanSP new_plan_sp; + + if (frame_sp && frame_sp->HasDebugInformation ()) + { + bool avoid_code_without_debug_info = true; + SymbolContext sc(frame_sp->GetSymbolContext(eSymbolContextEverything)); + new_plan_sp = thread->QueueThreadPlanForStepInRange (abort_other_plans, + sc.line_entry.range, + sc, + target_name, + stop_other_threads, + avoid_code_without_debug_info); + } + else + { + new_plan_sp = thread->QueueThreadPlanForStepSingleInstruction (false, + abort_other_plans, + stop_other_threads); + } + + // This returns an error, we should use it! + ResumeNewPlan (exe_ctx, new_plan_sp.get()); + } +} + +void +SBThread::StepOut () +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + Mutex::Locker api_locker; + ExecutionContext exe_ctx (m_opaque_sp.get(), api_locker); + + + if (log) + log->Printf ("SBThread(%p)::StepOut ()", exe_ctx.GetThreadPtr()); + + if (exe_ctx.HasThreadScope()) + { + bool abort_other_plans = false; + bool stop_other_threads = false; + + Thread *thread = exe_ctx.GetThreadPtr(); + + ThreadPlanSP new_plan_sp(thread->QueueThreadPlanForStepOut (abort_other_plans, + NULL, + false, + stop_other_threads, + eVoteYes, + eVoteNoOpinion, + 0)); + + // This returns an error, we should use it! + ResumeNewPlan (exe_ctx, new_plan_sp.get()); + } +} + +void +SBThread::StepOutOfFrame (lldb::SBFrame &sb_frame) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + Mutex::Locker api_locker; + ExecutionContext exe_ctx (m_opaque_sp.get(), api_locker); + + StackFrameSP frame_sp (sb_frame.GetFrameSP()); + if (log) + { + SBStream frame_desc_strm; + sb_frame.GetDescription (frame_desc_strm); + log->Printf ("SBThread(%p)::StepOutOfFrame (frame = SBFrame(%p): %s)", exe_ctx.GetThreadPtr(), frame_sp.get(), frame_desc_strm.GetData()); + } + + if (exe_ctx.HasThreadScope()) + { + bool abort_other_plans = false; + bool stop_other_threads = false; + Thread *thread = exe_ctx.GetThreadPtr(); + + ThreadPlanSP new_plan_sp(thread->QueueThreadPlanForStepOut (abort_other_plans, + NULL, + false, + stop_other_threads, + eVoteYes, + eVoteNoOpinion, + frame_sp->GetFrameIndex())); + + // This returns an error, we should use it! + ResumeNewPlan (exe_ctx, new_plan_sp.get()); + } +} + +void +SBThread::StepInstruction (bool step_over) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + Mutex::Locker api_locker; + ExecutionContext exe_ctx (m_opaque_sp.get(), api_locker); + + + + if (log) + log->Printf ("SBThread(%p)::StepInstruction (step_over=%i)", exe_ctx.GetThreadPtr(), step_over); + + if (exe_ctx.HasThreadScope()) + { + Thread *thread = exe_ctx.GetThreadPtr(); + ThreadPlanSP new_plan_sp(thread->QueueThreadPlanForStepSingleInstruction (step_over, true, true)); + + // This returns an error, we should use it! + ResumeNewPlan (exe_ctx, new_plan_sp.get()); + } +} + +void +SBThread::RunToAddress (lldb::addr_t addr) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + Mutex::Locker api_locker; + ExecutionContext exe_ctx (m_opaque_sp.get(), api_locker); + + + if (log) + log->Printf ("SBThread(%p)::RunToAddress (addr=0x%" PRIx64 ")", exe_ctx.GetThreadPtr(), addr); + + if (exe_ctx.HasThreadScope()) + { + bool abort_other_plans = false; + bool stop_other_threads = true; + + Address target_addr (addr); + + Thread *thread = exe_ctx.GetThreadPtr(); + + ThreadPlanSP new_plan_sp(thread->QueueThreadPlanForRunToAddress (abort_other_plans, target_addr, stop_other_threads)); + + // This returns an error, we should use it! + ResumeNewPlan (exe_ctx, new_plan_sp.get()); + } +} + +SBError +SBThread::StepOverUntil (lldb::SBFrame &sb_frame, + lldb::SBFileSpec &sb_file_spec, + uint32_t line) +{ + SBError sb_error; + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + char path[PATH_MAX]; + + Mutex::Locker api_locker; + ExecutionContext exe_ctx (m_opaque_sp.get(), api_locker); + + StackFrameSP frame_sp (sb_frame.GetFrameSP()); + + if (log) + { + SBStream frame_desc_strm; + sb_frame.GetDescription (frame_desc_strm); + sb_file_spec->GetPath (path, sizeof(path)); + log->Printf ("SBThread(%p)::StepOverUntil (frame = SBFrame(%p): %s, file+line = %s:%u)", + exe_ctx.GetThreadPtr(), + frame_sp.get(), + frame_desc_strm.GetData(), + path, line); + } + + if (exe_ctx.HasThreadScope()) + { + Target *target = exe_ctx.GetTargetPtr(); + Thread *thread = exe_ctx.GetThreadPtr(); + + if (line == 0) + { + sb_error.SetErrorString("invalid line argument"); + return sb_error; + } + + if (!frame_sp) + { + frame_sp = thread->GetSelectedFrame (); + if (!frame_sp) + frame_sp = thread->GetStackFrameAtIndex (0); + } + + SymbolContext frame_sc; + if (!frame_sp) + { + sb_error.SetErrorString("no valid frames in thread to step"); + return sb_error; + } + + // If we have a frame, get its line + frame_sc = frame_sp->GetSymbolContext (eSymbolContextCompUnit | + eSymbolContextFunction | + eSymbolContextLineEntry | + eSymbolContextSymbol ); + + if (frame_sc.comp_unit == NULL) + { + sb_error.SetErrorStringWithFormat("frame %u doesn't have debug information", frame_sp->GetFrameIndex()); + return sb_error; + } + + FileSpec step_file_spec; + if (sb_file_spec.IsValid()) + { + // The file spec passed in was valid, so use it + step_file_spec = sb_file_spec.ref(); + } + else + { + if (frame_sc.line_entry.IsValid()) + step_file_spec = frame_sc.line_entry.file; + else + { + sb_error.SetErrorString("invalid file argument or no file for frame"); + return sb_error; + } + } + + // Grab the current function, then we will make sure the "until" address is + // within the function. We discard addresses that are out of the current + // function, and then if there are no addresses remaining, give an appropriate + // error message. + + bool all_in_function = true; + AddressRange fun_range = frame_sc.function->GetAddressRange(); + + std::vector step_over_until_addrs; + const bool abort_other_plans = false; + const bool stop_other_threads = false; + const bool check_inlines = true; + const bool exact = false; + + SymbolContextList sc_list; + const uint32_t num_matches = frame_sc.comp_unit->ResolveSymbolContext (step_file_spec, + line, + check_inlines, + exact, + eSymbolContextLineEntry, + sc_list); + if (num_matches > 0) + { + SymbolContext sc; + for (uint32_t i=0; iQueueThreadPlanForStepUntil (abort_other_plans, + &step_over_until_addrs[0], + step_over_until_addrs.size(), + stop_other_threads, + frame_sp->GetFrameIndex())); + + sb_error = ResumeNewPlan (exe_ctx, new_plan_sp.get()); + } + } + else + { + sb_error.SetErrorString("this SBThread object is invalid"); + } + return sb_error; +} + +SBError +SBThread::ReturnFromFrame (SBFrame &frame, SBValue &return_value) +{ + SBError sb_error; + + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + Mutex::Locker api_locker; + ExecutionContext exe_ctx (m_opaque_sp.get(), api_locker); + + + if (log) + log->Printf ("SBThread(%p)::ReturnFromFrame (frame=%d)", exe_ctx.GetThreadPtr(), frame.GetFrameID()); + + if (exe_ctx.HasThreadScope()) + { + Thread *thread = exe_ctx.GetThreadPtr(); + sb_error.SetError (thread->ReturnFromFrame(frame.GetFrameSP(), return_value.GetSP())); + } + + return sb_error; +} + + +bool +SBThread::Suspend() +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + ExecutionContext exe_ctx (m_opaque_sp.get()); + bool result = false; + if (exe_ctx.HasThreadScope()) + { + Process::StopLocker stop_locker; + if (stop_locker.TryLock(&exe_ctx.GetProcessPtr()->GetRunLock())) + { + exe_ctx.GetThreadPtr()->SetResumeState (eStateSuspended); + result = true; + } + else + { + if (log) + log->Printf ("SBThread(%p)::Suspend() => error: process is running", exe_ctx.GetThreadPtr()); + } + } + if (log) + log->Printf ("SBThread(%p)::Suspend() => %i", exe_ctx.GetThreadPtr(), result); + return result; +} + +bool +SBThread::Resume () +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + ExecutionContext exe_ctx (m_opaque_sp.get()); + bool result = false; + if (exe_ctx.HasThreadScope()) + { + Process::StopLocker stop_locker; + if (stop_locker.TryLock(&exe_ctx.GetProcessPtr()->GetRunLock())) + { + exe_ctx.GetThreadPtr()->SetResumeState (eStateRunning); + result = true; + } + else + { + if (log) + log->Printf ("SBThread(%p)::Resume() => error: process is running", exe_ctx.GetThreadPtr()); + } + } + if (log) + log->Printf ("SBThread(%p)::Resume() => %i", exe_ctx.GetThreadPtr(), result); + return result; +} + +bool +SBThread::IsSuspended() +{ + ExecutionContext exe_ctx (m_opaque_sp.get()); + if (exe_ctx.HasThreadScope()) + return exe_ctx.GetThreadPtr()->GetResumeState () == eStateSuspended; + return false; +} + +bool +SBThread::IsStopped() +{ + ExecutionContext exe_ctx (m_opaque_sp.get()); + if (exe_ctx.HasThreadScope()) + return StateIsStoppedState(exe_ctx.GetThreadPtr()->GetState(), true); + return false; +} + +SBProcess +SBThread::GetProcess () +{ + SBProcess sb_process; + ExecutionContext exe_ctx (m_opaque_sp.get()); + if (exe_ctx.HasThreadScope()) + { + // Have to go up to the target so we can get a shared pointer to our process... + sb_process.SetSP (exe_ctx.GetProcessSP()); + } + + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (log) + { + SBStream frame_desc_strm; + sb_process.GetDescription (frame_desc_strm); + log->Printf ("SBThread(%p)::GetProcess () => SBProcess(%p): %s", exe_ctx.GetThreadPtr(), + sb_process.GetSP().get(), frame_desc_strm.GetData()); + } + + return sb_process; +} + +uint32_t +SBThread::GetNumFrames () +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + uint32_t num_frames = 0; + Mutex::Locker api_locker; + ExecutionContext exe_ctx (m_opaque_sp.get(), api_locker); + + if (exe_ctx.HasThreadScope()) + { + Process::StopLocker stop_locker; + if (stop_locker.TryLock(&exe_ctx.GetProcessPtr()->GetRunLock())) + { + num_frames = exe_ctx.GetThreadPtr()->GetStackFrameCount(); + } + else + { + if (log) + log->Printf ("SBThread(%p)::GetNumFrames() => error: process is running", exe_ctx.GetThreadPtr()); + } + } + + if (log) + log->Printf ("SBThread(%p)::GetNumFrames () => %u", exe_ctx.GetThreadPtr(), num_frames); + + return num_frames; +} + +SBFrame +SBThread::GetFrameAtIndex (uint32_t idx) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + SBFrame sb_frame; + StackFrameSP frame_sp; + Mutex::Locker api_locker; + ExecutionContext exe_ctx (m_opaque_sp.get(), api_locker); + + if (exe_ctx.HasThreadScope()) + { + Process::StopLocker stop_locker; + if (stop_locker.TryLock(&exe_ctx.GetProcessPtr()->GetRunLock())) + { + frame_sp = exe_ctx.GetThreadPtr()->GetStackFrameAtIndex (idx); + sb_frame.SetFrameSP (frame_sp); + } + else + { + if (log) + log->Printf ("SBThread(%p)::GetFrameAtIndex() => error: process is running", exe_ctx.GetThreadPtr()); + } + } + + if (log) + { + SBStream frame_desc_strm; + sb_frame.GetDescription (frame_desc_strm); + log->Printf ("SBThread(%p)::GetFrameAtIndex (idx=%d) => SBFrame(%p): %s", + exe_ctx.GetThreadPtr(), idx, frame_sp.get(), frame_desc_strm.GetData()); + } + + return sb_frame; +} + +lldb::SBFrame +SBThread::GetSelectedFrame () +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + SBFrame sb_frame; + StackFrameSP frame_sp; + Mutex::Locker api_locker; + ExecutionContext exe_ctx (m_opaque_sp.get(), api_locker); + + if (exe_ctx.HasThreadScope()) + { + Process::StopLocker stop_locker; + if (stop_locker.TryLock(&exe_ctx.GetProcessPtr()->GetRunLock())) + { + frame_sp = exe_ctx.GetThreadPtr()->GetSelectedFrame (); + sb_frame.SetFrameSP (frame_sp); + } + else + { + if (log) + log->Printf ("SBThread(%p)::GetSelectedFrame() => error: process is running", exe_ctx.GetThreadPtr()); + } + } + + if (log) + { + SBStream frame_desc_strm; + sb_frame.GetDescription (frame_desc_strm); + log->Printf ("SBThread(%p)::GetSelectedFrame () => SBFrame(%p): %s", + exe_ctx.GetThreadPtr(), frame_sp.get(), frame_desc_strm.GetData()); + } + + return sb_frame; +} + +lldb::SBFrame +SBThread::SetSelectedFrame (uint32_t idx) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + SBFrame sb_frame; + StackFrameSP frame_sp; + Mutex::Locker api_locker; + ExecutionContext exe_ctx (m_opaque_sp.get(), api_locker); + + if (exe_ctx.HasThreadScope()) + { + Process::StopLocker stop_locker; + if (stop_locker.TryLock(&exe_ctx.GetProcessPtr()->GetRunLock())) + { + Thread *thread = exe_ctx.GetThreadPtr(); + frame_sp = thread->GetStackFrameAtIndex (idx); + if (frame_sp) + { + thread->SetSelectedFrame (frame_sp.get()); + sb_frame.SetFrameSP (frame_sp); + } + } + else + { + if (log) + log->Printf ("SBThread(%p)::SetSelectedFrame() => error: process is running", exe_ctx.GetThreadPtr()); + } + } + + if (log) + { + SBStream frame_desc_strm; + sb_frame.GetDescription (frame_desc_strm); + log->Printf ("SBThread(%p)::SetSelectedFrame (idx=%u) => SBFrame(%p): %s", + exe_ctx.GetThreadPtr(), idx, frame_sp.get(), frame_desc_strm.GetData()); + } + return sb_frame; +} + +bool +SBThread::EventIsThreadEvent (const SBEvent &event) +{ + return Thread::ThreadEventData::GetEventDataFromEvent(event.get()) != NULL; +} + +SBFrame +SBThread::GetStackFrameFromEvent (const SBEvent &event) +{ + return Thread::ThreadEventData::GetStackFrameFromEvent (event.get()); + +} + +SBThread +SBThread::GetThreadFromEvent (const SBEvent &event) +{ + return Thread::ThreadEventData::GetThreadFromEvent (event.get()); +} + +bool +SBThread::operator == (const SBThread &rhs) const +{ + return m_opaque_sp->GetThreadSP().get() == rhs.m_opaque_sp->GetThreadSP().get(); +} + +bool +SBThread::operator != (const SBThread &rhs) const +{ + return m_opaque_sp->GetThreadSP().get() != rhs.m_opaque_sp->GetThreadSP().get(); +} + +bool +SBThread::GetStatus (SBStream &status) const +{ + Stream &strm = status.ref(); + + ExecutionContext exe_ctx (m_opaque_sp.get()); + if (exe_ctx.HasThreadScope()) + { + exe_ctx.GetThreadPtr()->GetStatus(strm, 0, 1, 1); + } + else + strm.PutCString ("No status"); + + return true; +} + +bool +SBThread::GetDescription (SBStream &description) const +{ + Stream &strm = description.ref(); + + ExecutionContext exe_ctx (m_opaque_sp.get()); + if (exe_ctx.HasThreadScope()) + { + strm.Printf("SBThread: tid = 0x%4.4" PRIx64, exe_ctx.GetThreadPtr()->GetID()); + } + else + strm.PutCString ("No value"); + + return true; +} diff --git a/contrib/llvm/tools/lldb/source/API/SBType.cpp b/contrib/llvm/tools/lldb/source/API/SBType.cpp new file mode 100644 index 00000000000..372d073acf1 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/API/SBType.cpp @@ -0,0 +1,648 @@ +//===-- SBType.cpp ----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include + +#include "clang/AST/ASTContext.h" +#include "clang/AST/TemplateBase.h" +#include "clang/AST/Type.h" + +#include "lldb/API/SBDefines.h" +#include "lldb/API/SBType.h" +#include "lldb/API/SBStream.h" +#include "lldb/Core/ConstString.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Stream.h" +#include "lldb/Symbol/ClangASTContext.h" +#include "lldb/Symbol/ClangASTType.h" +#include "lldb/Symbol/Type.h" + +using namespace lldb; +using namespace lldb_private; +using namespace clang; + +SBType::SBType() : + m_opaque_sp() +{ +} + +SBType::SBType (const ClangASTType &type) : + m_opaque_sp(new TypeImpl(ClangASTType(type.GetASTContext(), + type.GetOpaqueQualType()))) +{ +} + +SBType::SBType (const lldb::TypeSP &type_sp) : + m_opaque_sp(new TypeImpl(type_sp)) +{ +} + +SBType::SBType (const lldb::TypeImplSP &type_impl_sp) : + m_opaque_sp(type_impl_sp) +{ +} + + +SBType::SBType (const SBType &rhs) : + m_opaque_sp() +{ + if (this != &rhs) + { + m_opaque_sp = rhs.m_opaque_sp; + } +} + + +//SBType::SBType (TypeImpl* impl) : +// m_opaque_ap(impl) +//{} +// +bool +SBType::operator == (SBType &rhs) +{ + if (IsValid() == false) + return !rhs.IsValid(); + + return (rhs.m_opaque_sp->GetASTContext() == m_opaque_sp->GetASTContext()) && + (rhs.m_opaque_sp->GetOpaqueQualType() == m_opaque_sp->GetOpaqueQualType()); +} + +bool +SBType::operator != (SBType &rhs) +{ + if (IsValid() == false) + return rhs.IsValid(); + + return (rhs.m_opaque_sp->GetASTContext() != m_opaque_sp->GetASTContext()) || + (rhs.m_opaque_sp->GetOpaqueQualType() != m_opaque_sp->GetOpaqueQualType()); +} + +lldb::TypeImplSP +SBType::GetSP () +{ + return m_opaque_sp; +} + + +void +SBType::SetSP (const lldb::TypeImplSP &type_impl_sp) +{ + m_opaque_sp = type_impl_sp; +} + +SBType & +SBType::operator = (const SBType &rhs) +{ + if (this != &rhs) + { + m_opaque_sp = rhs.m_opaque_sp; + } + return *this; +} + +SBType::~SBType () +{} + +TypeImpl & +SBType::ref () +{ + if (m_opaque_sp.get() == NULL) + m_opaque_sp.reset (new TypeImpl()); + return *m_opaque_sp; +} + +const TypeImpl & +SBType::ref () const +{ + // "const SBAddress &addr" should already have checked "addr.IsValid()" + // prior to calling this function. In case you didn't we will assert + // and die to let you know. + assert (m_opaque_sp.get()); + return *m_opaque_sp; +} + +bool +SBType::IsValid() const +{ + if (m_opaque_sp.get() == NULL) + return false; + + return m_opaque_sp->IsValid(); +} + +uint64_t +SBType::GetByteSize() +{ + if (!IsValid()) + return 0; + + return m_opaque_sp->GetClangASTType().GetByteSize(); + +} + +bool +SBType::IsPointerType() +{ + if (!IsValid()) + return false; + return m_opaque_sp->GetClangASTType().IsPointerType(); +} + +bool +SBType::IsReferenceType() +{ + if (!IsValid()) + return false; + return m_opaque_sp->GetClangASTType().IsReferenceType(); +} + +SBType +SBType::GetPointerType() +{ + if (!IsValid()) + return SBType(); + + return SBType(ClangASTType(m_opaque_sp->GetClangASTType().GetPointerType())); +} + +SBType +SBType::GetPointeeType() +{ + if (!IsValid()) + return SBType(); + return SBType(ClangASTType(m_opaque_sp->GetClangASTType().GetPointeeType())); +} + +SBType +SBType::GetReferenceType() +{ + if (!IsValid()) + return SBType(); + return SBType(ClangASTType(m_opaque_sp->GetClangASTType().GetLValueReferenceType())); +} + +SBType +SBType::GetDereferencedType() +{ + if (!IsValid()) + return SBType(); + return SBType(ClangASTType(m_opaque_sp->GetClangASTType().GetNonReferenceType())); +} + +bool +SBType::IsFunctionType () +{ + if (!IsValid()) + return false; + return m_opaque_sp->GetClangASTType().IsFunctionType(); +} + +bool +SBType::IsPolymorphicClass () +{ + if (!IsValid()) + return false; + return m_opaque_sp->GetClangASTType().IsPolymorphicClass(); +} + + + +lldb::SBType +SBType::GetFunctionReturnType () +{ + if (IsValid()) + { + ClangASTType return_clang_type (m_opaque_sp->GetClangASTType().GetFunctionReturnType()); + if (return_clang_type.IsValid()) + return SBType(return_clang_type); + } + return lldb::SBType(); +} + +lldb::SBTypeList +SBType::GetFunctionArgumentTypes () +{ + SBTypeList sb_type_list; + if (IsValid()) + { + QualType qual_type(QualType::getFromOpaquePtr(m_opaque_sp->GetOpaqueQualType())); + const FunctionProtoType* func = dyn_cast(qual_type.getTypePtr()); + if (func) + { + const uint32_t num_args = func->getNumArgs(); + for (uint32_t i=0; iGetASTContext(), func->getArgType(i).getAsOpaquePtr()))); + } + } + return sb_type_list; +} + +lldb::SBType +SBType::GetUnqualifiedType() +{ + if (!IsValid()) + return SBType(); + return SBType(m_opaque_sp->GetClangASTType().GetFullyUnqualifiedType()); +} + +lldb::SBType +SBType::GetCanonicalType() +{ + if (IsValid()) + return SBType(m_opaque_sp->GetClangASTType().GetCanonicalType()); + return SBType(); +} + + +lldb::BasicType +SBType::GetBasicType() +{ + if (IsValid()) + return m_opaque_sp->GetClangASTType().GetBasicTypeEnumeration (); + return eBasicTypeInvalid; +} + +SBType +SBType::GetBasicType(lldb::BasicType basic_type) +{ + if (IsValid()) + return SBType (ClangASTContext::GetBasicType (m_opaque_sp->GetASTContext(), basic_type)); + return SBType(); +} + +uint32_t +SBType::GetNumberOfDirectBaseClasses () +{ + if (IsValid()) + return m_opaque_sp->GetClangASTType().GetNumDirectBaseClasses(); + return 0; +} + +uint32_t +SBType::GetNumberOfVirtualBaseClasses () +{ + if (IsValid()) + return m_opaque_sp->GetClangASTType().GetNumVirtualBaseClasses(); + return 0; +} + +uint32_t +SBType::GetNumberOfFields () +{ + if (IsValid()) + return m_opaque_sp->GetClangASTType().GetNumFields(); + return 0; +} + +bool +SBType::GetDescription (SBStream &description, lldb::DescriptionLevel description_level) +{ + Stream &strm = description.ref(); + + if (m_opaque_sp) + { + m_opaque_sp->GetDescription (strm, description_level); + } + else + strm.PutCString ("No value"); + + return true; +} + + + +SBTypeMember +SBType::GetDirectBaseClassAtIndex (uint32_t idx) +{ + SBTypeMember sb_type_member; + if (IsValid()) + { + ClangASTType this_type (m_opaque_sp->GetClangASTType ()); + if (this_type.IsValid()) + { + uint32_t bit_offset = 0; + ClangASTType base_class_type (this_type.GetDirectBaseClassAtIndex(idx, &bit_offset)); + if (base_class_type.IsValid()) + { + sb_type_member.reset (new TypeMemberImpl (TypeImplSP(new TypeImpl(base_class_type)), bit_offset)); + } + } + } + return sb_type_member; + +} + +SBTypeMember +SBType::GetVirtualBaseClassAtIndex (uint32_t idx) +{ + SBTypeMember sb_type_member; + if (IsValid()) + { + ClangASTType this_type (m_opaque_sp->GetClangASTType ()); + if (this_type.IsValid()) + { + uint32_t bit_offset = 0; + ClangASTType base_class_type (this_type.GetVirtualBaseClassAtIndex(idx, &bit_offset)); + if (base_class_type.IsValid()) + { + sb_type_member.reset (new TypeMemberImpl (TypeImplSP(new TypeImpl(base_class_type)), bit_offset)); + } + } + } + return sb_type_member; +} + +SBTypeMember +SBType::GetFieldAtIndex (uint32_t idx) +{ + SBTypeMember sb_type_member; + if (IsValid()) + { + ClangASTType this_type (m_opaque_sp->GetClangASTType ()); + if (this_type.IsValid()) + { + uint64_t bit_offset = 0; + uint32_t bitfield_bit_size = 0; + bool is_bitfield = false; + std::string name_sstr; + ClangASTType field_type (this_type.GetFieldAtIndex (idx, + name_sstr, + &bit_offset, + &bitfield_bit_size, + &is_bitfield)); + if (field_type.IsValid()) + { + ConstString name; + if (!name_sstr.empty()) + name.SetCString(name_sstr.c_str()); + sb_type_member.reset (new TypeMemberImpl (TypeImplSP (new TypeImpl(field_type)), + bit_offset, + name, + bitfield_bit_size, + is_bitfield)); + } + } + } + return sb_type_member; +} + +bool +SBType::IsTypeComplete() +{ + if (!IsValid()) + return false; + return m_opaque_sp->GetClangASTType().IsCompleteType(); +} + +const char* +SBType::GetName() +{ + if (!IsValid()) + return ""; + return m_opaque_sp->GetClangASTType().GetConstTypeName().GetCString(); +} + +lldb::TypeClass +SBType::GetTypeClass () +{ + if (IsValid()) + return m_opaque_sp->GetClangASTType().GetTypeClass(); + return lldb::eTypeClassInvalid; +} + +uint32_t +SBType::GetNumberOfTemplateArguments () +{ + if (IsValid()) + return m_opaque_sp->GetClangASTType().GetNumTemplateArguments(); + return 0; +} + +lldb::SBType +SBType::GetTemplateArgumentType (uint32_t idx) +{ + if (IsValid()) + { + TemplateArgumentKind kind = eTemplateArgumentKindNull; + ClangASTType template_arg_type = m_opaque_sp->GetClangASTType().GetTemplateArgument (idx, kind); + if (template_arg_type.IsValid()) + return SBType(template_arg_type); + } + return SBType(); +} + + +lldb::TemplateArgumentKind +SBType::GetTemplateArgumentKind (uint32_t idx) +{ + TemplateArgumentKind kind = eTemplateArgumentKindNull; + if (IsValid()) + m_opaque_sp->GetClangASTType().GetTemplateArgument (idx, kind); + return kind; +} + + +SBTypeList::SBTypeList() : + m_opaque_ap(new TypeListImpl()) +{ +} + +SBTypeList::SBTypeList(const SBTypeList& rhs) : + m_opaque_ap(new TypeListImpl()) +{ + for (uint32_t i = 0, rhs_size = const_cast(rhs).GetSize(); i < rhs_size; i++) + Append(const_cast(rhs).GetTypeAtIndex(i)); +} + +bool +SBTypeList::IsValid () +{ + return (m_opaque_ap.get() != NULL); +} + +SBTypeList& +SBTypeList::operator = (const SBTypeList& rhs) +{ + if (this != &rhs) + { + m_opaque_ap.reset (new TypeListImpl()); + for (uint32_t i = 0, rhs_size = const_cast(rhs).GetSize(); i < rhs_size; i++) + Append(const_cast(rhs).GetTypeAtIndex(i)); + } + return *this; +} + +void +SBTypeList::Append (SBType type) +{ + if (type.IsValid()) + m_opaque_ap->Append (type.m_opaque_sp); +} + +SBType +SBTypeList::GetTypeAtIndex(uint32_t index) +{ + if (m_opaque_ap.get()) + return SBType(m_opaque_ap->GetTypeAtIndex(index)); + return SBType(); +} + +uint32_t +SBTypeList::GetSize() +{ + return m_opaque_ap->GetSize(); +} + +SBTypeList::~SBTypeList() +{ +} + +SBTypeMember::SBTypeMember() : + m_opaque_ap() +{ +} + +SBTypeMember::~SBTypeMember() +{ +} + +SBTypeMember::SBTypeMember (const SBTypeMember& rhs) : + m_opaque_ap() +{ + if (this != &rhs) + { + if (rhs.IsValid()) + m_opaque_ap.reset(new TypeMemberImpl(rhs.ref())); + } +} + +lldb::SBTypeMember& +SBTypeMember::operator = (const lldb::SBTypeMember& rhs) +{ + if (this != &rhs) + { + if (rhs.IsValid()) + m_opaque_ap.reset(new TypeMemberImpl(rhs.ref())); + } + return *this; +} + +bool +SBTypeMember::IsValid() const +{ + return m_opaque_ap.get(); +} + +const char * +SBTypeMember::GetName () +{ + if (m_opaque_ap.get()) + return m_opaque_ap->GetName().GetCString(); + return NULL; +} + +SBType +SBTypeMember::GetType () +{ + SBType sb_type; + if (m_opaque_ap.get()) + { + sb_type.SetSP (m_opaque_ap->GetTypeImpl()); + } + return sb_type; + +} + +uint64_t +SBTypeMember::GetOffsetInBytes() +{ + if (m_opaque_ap.get()) + return m_opaque_ap->GetBitOffset() / 8u; + return 0; +} + +uint64_t +SBTypeMember::GetOffsetInBits() +{ + if (m_opaque_ap.get()) + return m_opaque_ap->GetBitOffset(); + return 0; +} + +bool +SBTypeMember::IsBitfield() +{ + if (m_opaque_ap.get()) + return m_opaque_ap->GetIsBitfield(); + return false; +} + +uint32_t +SBTypeMember::GetBitfieldSizeInBits() +{ + if (m_opaque_ap.get()) + return m_opaque_ap->GetBitfieldBitSize(); + return 0; +} + + +bool +SBTypeMember::GetDescription (lldb::SBStream &description, lldb::DescriptionLevel description_level) +{ + Stream &strm = description.ref(); + + if (m_opaque_ap.get()) + { + const uint32_t bit_offset = m_opaque_ap->GetBitOffset(); + const uint32_t byte_offset = bit_offset / 8u; + const uint32_t byte_bit_offset = bit_offset % 8u; + const char *name = m_opaque_ap->GetName().GetCString(); + if (byte_bit_offset) + strm.Printf ("+%u + %u bits: (", byte_offset, byte_bit_offset); + else + strm.Printf ("+%u: (", byte_offset); + + TypeImplSP type_impl_sp (m_opaque_ap->GetTypeImpl()); + if (type_impl_sp) + type_impl_sp->GetDescription(strm, description_level); + + strm.Printf (") %s", name); + if (m_opaque_ap->GetIsBitfield()) + { + const uint32_t bitfield_bit_size = m_opaque_ap->GetBitfieldBitSize(); + strm.Printf (" : %u", bitfield_bit_size); + } + } + else + { + strm.PutCString ("No value"); + } + return true; +} + + +void +SBTypeMember::reset(TypeMemberImpl *type_member_impl) +{ + m_opaque_ap.reset(type_member_impl); +} + +TypeMemberImpl & +SBTypeMember::ref () +{ + if (m_opaque_ap.get() == NULL) + m_opaque_ap.reset (new TypeMemberImpl()); + return *m_opaque_ap.get(); +} + +const TypeMemberImpl & +SBTypeMember::ref () const +{ + return *m_opaque_ap.get(); +} diff --git a/contrib/llvm/tools/lldb/source/API/SBTypeCategory.cpp b/contrib/llvm/tools/lldb/source/API/SBTypeCategory.cpp new file mode 100644 index 00000000000..e3978693c81 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/API/SBTypeCategory.cpp @@ -0,0 +1,576 @@ +//===-- SBTypeCategory.cpp ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "lldb/API/SBTypeCategory.h" + +#include "lldb/API/SBTypeFilter.h" +#include "lldb/API/SBTypeFormat.h" +#include "lldb/API/SBTypeSummary.h" +#include "lldb/API/SBTypeSynthetic.h" +#include "lldb/API/SBTypeNameSpecifier.h" +#include "lldb/API/SBStream.h" + +#include "lldb/Core/Debugger.h" +#include "lldb/DataFormatters/DataVisualization.h" +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Interpreter/ScriptInterpreter.h" + +using namespace lldb; +using namespace lldb_private; + +typedef std::pair ImplType; + +SBTypeCategory::SBTypeCategory() : +m_opaque_sp() +{ +} + +SBTypeCategory::SBTypeCategory (const char* name) : +m_opaque_sp() +{ + DataVisualization::Categories::GetCategory(ConstString(name), m_opaque_sp); +} + +SBTypeCategory::SBTypeCategory (const lldb::SBTypeCategory &rhs) : +m_opaque_sp(rhs.m_opaque_sp) +{ +} + +SBTypeCategory::~SBTypeCategory () +{ +} + +bool +SBTypeCategory::IsValid() const +{ + return (m_opaque_sp.get() != NULL); +} + +bool +SBTypeCategory::GetEnabled () +{ + if (!IsValid()) + return false; + return m_opaque_sp->IsEnabled(); +} + +void +SBTypeCategory::SetEnabled (bool enabled) +{ + if (!IsValid()) + return; + if (enabled) + DataVisualization::Categories::Enable(m_opaque_sp); + else + DataVisualization::Categories::Disable(m_opaque_sp); +} + +const char* +SBTypeCategory::GetName() +{ + if (!IsValid()) + return NULL; + return m_opaque_sp->GetName(); +} + +uint32_t +SBTypeCategory::GetNumFormats () +{ + if (!IsDefaultCategory()) + return 0; + + return DataVisualization::ValueFormats::GetCount(); +} + +uint32_t +SBTypeCategory::GetNumSummaries () +{ + if (!IsValid()) + return 0; + return m_opaque_sp->GetSummaryNavigator()->GetCount() + m_opaque_sp->GetRegexSummaryNavigator()->GetCount(); +} + +uint32_t +SBTypeCategory::GetNumFilters () +{ + if (!IsValid()) + return 0; + return m_opaque_sp->GetFilterNavigator()->GetCount() + m_opaque_sp->GetRegexFilterNavigator()->GetCount(); +} + +#ifndef LLDB_DISABLE_PYTHON +uint32_t +SBTypeCategory::GetNumSynthetics () +{ + if (!IsValid()) + return 0; + return m_opaque_sp->GetSyntheticNavigator()->GetCount() + m_opaque_sp->GetRegexSyntheticNavigator()->GetCount(); +} +#endif + +lldb::SBTypeNameSpecifier +SBTypeCategory::GetTypeNameSpecifierForFilterAtIndex (uint32_t index) +{ + if (!IsValid()) + return SBTypeNameSpecifier(); + return SBTypeNameSpecifier(m_opaque_sp->GetTypeNameSpecifierForFilterAtIndex(index)); +} + +lldb::SBTypeNameSpecifier +SBTypeCategory::GetTypeNameSpecifierForFormatAtIndex (uint32_t index) +{ + if (!IsDefaultCategory()) + return SBTypeNameSpecifier(); + return SBTypeNameSpecifier(DataVisualization::ValueFormats::GetTypeNameSpecifierForFormatAtIndex(index)); +} + +lldb::SBTypeNameSpecifier +SBTypeCategory::GetTypeNameSpecifierForSummaryAtIndex (uint32_t index) +{ + if (!IsValid()) + return SBTypeNameSpecifier(); + return SBTypeNameSpecifier(m_opaque_sp->GetTypeNameSpecifierForSummaryAtIndex(index)); +} + +#ifndef LLDB_DISABLE_PYTHON +lldb::SBTypeNameSpecifier +SBTypeCategory::GetTypeNameSpecifierForSyntheticAtIndex (uint32_t index) +{ + if (!IsValid()) + return SBTypeNameSpecifier(); + return SBTypeNameSpecifier(m_opaque_sp->GetTypeNameSpecifierForSyntheticAtIndex(index)); +} +#endif + +SBTypeFilter +SBTypeCategory::GetFilterForType (SBTypeNameSpecifier spec) +{ + if (!IsValid()) + return SBTypeFilter(); + + if (!spec.IsValid()) + return SBTypeFilter(); + + lldb::SyntheticChildrenSP children_sp; + + if (spec.IsRegex()) + m_opaque_sp->GetRegexFilterNavigator()->GetExact(ConstString(spec.GetName()), children_sp); + else + m_opaque_sp->GetFilterNavigator()->GetExact(ConstString(spec.GetName()), children_sp); + + if (!children_sp) + return lldb::SBTypeFilter(); + + TypeFilterImplSP filter_sp = std::static_pointer_cast(children_sp); + + return lldb::SBTypeFilter(filter_sp); + +} +SBTypeFormat +SBTypeCategory::GetFormatForType (SBTypeNameSpecifier spec) +{ + if (!IsDefaultCategory()) + return SBTypeFormat(); + + if (!spec.IsValid()) + return SBTypeFormat(); + + if (spec.IsRegex()) + return SBTypeFormat(); + + return SBTypeFormat(DataVisualization::ValueFormats::GetFormat(ConstString(spec.GetName()))); +} + +#ifndef LLDB_DISABLE_PYTHON +SBTypeSummary +SBTypeCategory::GetSummaryForType (SBTypeNameSpecifier spec) +{ + if (!IsValid()) + return SBTypeSummary(); + + if (!spec.IsValid()) + return SBTypeSummary(); + + lldb::TypeSummaryImplSP summary_sp; + + if (spec.IsRegex()) + m_opaque_sp->GetRegexSummaryNavigator()->GetExact(ConstString(spec.GetName()), summary_sp); + else + m_opaque_sp->GetSummaryNavigator()->GetExact(ConstString(spec.GetName()), summary_sp); + + if (!summary_sp) + return lldb::SBTypeSummary(); + + return lldb::SBTypeSummary(summary_sp); +} +#endif // LLDB_DISABLE_PYTHON + +#ifndef LLDB_DISABLE_PYTHON +SBTypeSynthetic +SBTypeCategory::GetSyntheticForType (SBTypeNameSpecifier spec) +{ + if (!IsValid()) + return SBTypeSynthetic(); + + if (!spec.IsValid()) + return SBTypeSynthetic(); + + lldb::SyntheticChildrenSP children_sp; + + if (spec.IsRegex()) + m_opaque_sp->GetRegexSyntheticNavigator()->GetExact(ConstString(spec.GetName()), children_sp); + else + m_opaque_sp->GetSyntheticNavigator()->GetExact(ConstString(spec.GetName()), children_sp); + + if (!children_sp) + return lldb::SBTypeSynthetic(); + + ScriptedSyntheticChildrenSP synth_sp = std::static_pointer_cast(children_sp); + + return lldb::SBTypeSynthetic(synth_sp); +} +#endif + +#ifndef LLDB_DISABLE_PYTHON +SBTypeFilter +SBTypeCategory::GetFilterAtIndex (uint32_t index) +{ + if (!IsValid()) + return SBTypeFilter(); + lldb::SyntheticChildrenSP children_sp = m_opaque_sp->GetSyntheticAtIndex((index)); + + if (!children_sp.get()) + return lldb::SBTypeFilter(); + + TypeFilterImplSP filter_sp = std::static_pointer_cast(children_sp); + + return lldb::SBTypeFilter(filter_sp); +} +#endif + +SBTypeFormat +SBTypeCategory::GetFormatAtIndex (uint32_t index) +{ + if (!IsDefaultCategory()) + return SBTypeFormat(); + return SBTypeFormat(DataVisualization::ValueFormats::GetFormatAtIndex((index))); +} + +#ifndef LLDB_DISABLE_PYTHON +SBTypeSummary +SBTypeCategory::GetSummaryAtIndex (uint32_t index) +{ + if (!IsValid()) + return SBTypeSummary(); + return SBTypeSummary(m_opaque_sp->GetSummaryAtIndex((index))); +} +#endif + +#ifndef LLDB_DISABLE_PYTHON +SBTypeSynthetic +SBTypeCategory::GetSyntheticAtIndex (uint32_t index) +{ + if (!IsValid()) + return SBTypeSynthetic(); + lldb::SyntheticChildrenSP children_sp = m_opaque_sp->GetSyntheticAtIndex((index)); + + if (!children_sp.get()) + return lldb::SBTypeSynthetic(); + + ScriptedSyntheticChildrenSP synth_sp = std::static_pointer_cast(children_sp); + + return lldb::SBTypeSynthetic(synth_sp); +} +#endif + +bool +SBTypeCategory::AddTypeFormat (SBTypeNameSpecifier type_name, + SBTypeFormat format) +{ + if (!IsDefaultCategory()) + return false; + + if (!type_name.IsValid()) + return false; + + if (!format.IsValid()) + return false; + + if (type_name.IsRegex()) + return false; + + DataVisualization::ValueFormats::Add(ConstString(type_name.GetName()), format.GetSP()); + + return true; +} + +bool +SBTypeCategory::DeleteTypeFormat (SBTypeNameSpecifier type_name) +{ + if (!IsDefaultCategory()) + return false; + + if (!type_name.IsValid()) + return false; + + if (type_name.IsRegex()) + return false; + + return DataVisualization::ValueFormats::Delete(ConstString(type_name.GetName())); +} + +#ifndef LLDB_DISABLE_PYTHON +bool +SBTypeCategory::AddTypeSummary (SBTypeNameSpecifier type_name, + SBTypeSummary summary) +{ + if (!IsValid()) + return false; + + if (!type_name.IsValid()) + return false; + + if (!summary.IsValid()) + return false; + + // FIXME: we need to iterate over all the Debugger objects and have each of them contain a copy of the function + // since we currently have formatters live in a global space, while Python code lives in a specific Debugger-related environment + // this should eventually be fixed by deciding a final location in the LLDB object space for formatters + if (summary.IsFunctionCode()) + { + void *name_token = (void*)ConstString(type_name.GetName()).GetCString(); + const char* script = summary.GetData(); + StringList input; input.SplitIntoLines(script, strlen(script)); + uint32_t num_debuggers = lldb_private::Debugger::GetNumDebuggers(); + bool need_set = true; + for (uint32_t j = 0; + j < num_debuggers; + j++) + { + DebuggerSP debugger_sp = lldb_private::Debugger::GetDebuggerAtIndex(j); + if (debugger_sp) + { + ScriptInterpreter* interpreter_ptr = debugger_sp->GetCommandInterpreter().GetScriptInterpreter(); + if (interpreter_ptr) + { + std::string output; + if (interpreter_ptr->GenerateTypeScriptFunction(input, output, name_token) && !output.empty()) + { + if (need_set) + { + need_set = false; + summary.SetFunctionName(output.c_str()); + } + } + } + } + } + } + + if (type_name.IsRegex()) + m_opaque_sp->GetRegexSummaryNavigator()->Add(lldb::RegularExpressionSP(new RegularExpression(type_name.GetName())), summary.GetSP()); + else + m_opaque_sp->GetSummaryNavigator()->Add(ConstString(type_name.GetName()), summary.GetSP()); + + return true; +} +#endif + +bool +SBTypeCategory::DeleteTypeSummary (SBTypeNameSpecifier type_name) +{ + if (!IsValid()) + return false; + + if (!type_name.IsValid()) + return false; + + if (type_name.IsRegex()) + return m_opaque_sp->GetRegexSummaryNavigator()->Delete(ConstString(type_name.GetName())); + else + return m_opaque_sp->GetSummaryNavigator()->Delete(ConstString(type_name.GetName())); +} + +bool +SBTypeCategory::AddTypeFilter (SBTypeNameSpecifier type_name, + SBTypeFilter filter) +{ + if (!IsValid()) + return false; + + if (!type_name.IsValid()) + return false; + + if (!filter.IsValid()) + return false; + + if (type_name.IsRegex()) + m_opaque_sp->GetRegexFilterNavigator()->Add(lldb::RegularExpressionSP(new RegularExpression(type_name.GetName())), filter.GetSP()); + else + m_opaque_sp->GetFilterNavigator()->Add(ConstString(type_name.GetName()), filter.GetSP()); + + return true; +} + +bool +SBTypeCategory::DeleteTypeFilter (SBTypeNameSpecifier type_name) +{ + if (!IsValid()) + return false; + + if (!type_name.IsValid()) + return false; + + if (type_name.IsRegex()) + return m_opaque_sp->GetRegexFilterNavigator()->Delete(ConstString(type_name.GetName())); + else + return m_opaque_sp->GetFilterNavigator()->Delete(ConstString(type_name.GetName())); +} + +#ifndef LLDB_DISABLE_PYTHON +bool +SBTypeCategory::AddTypeSynthetic (SBTypeNameSpecifier type_name, + SBTypeSynthetic synth) +{ + if (!IsValid()) + return false; + + if (!type_name.IsValid()) + return false; + + if (!synth.IsValid()) + return false; + + // FIXME: we need to iterate over all the Debugger objects and have each of them contain a copy of the function + // since we currently have formatters live in a global space, while Python code lives in a specific Debugger-related environment + // this should eventually be fixed by deciding a final location in the LLDB object space for formatters + if (synth.IsClassCode()) + { + void *name_token = (void*)ConstString(type_name.GetName()).GetCString(); + const char* script = synth.GetData(); + StringList input; input.SplitIntoLines(script, strlen(script)); + uint32_t num_debuggers = lldb_private::Debugger::GetNumDebuggers(); + bool need_set = true; + for (uint32_t j = 0; + j < num_debuggers; + j++) + { + DebuggerSP debugger_sp = lldb_private::Debugger::GetDebuggerAtIndex(j); + if (debugger_sp) + { + ScriptInterpreter* interpreter_ptr = debugger_sp->GetCommandInterpreter().GetScriptInterpreter(); + if (interpreter_ptr) + { + std::string output; + if (interpreter_ptr->GenerateTypeSynthClass(input, output, name_token) && !output.empty()) + { + if (need_set) + { + need_set = false; + synth.SetClassName(output.c_str()); + } + } + } + } + } + } + + if (type_name.IsRegex()) + m_opaque_sp->GetRegexSyntheticNavigator()->Add(lldb::RegularExpressionSP(new RegularExpression(type_name.GetName())), synth.GetSP()); + else + m_opaque_sp->GetSyntheticNavigator()->Add(ConstString(type_name.GetName()), synth.GetSP()); + + return true; +} + +bool +SBTypeCategory::DeleteTypeSynthetic (SBTypeNameSpecifier type_name) +{ + if (!IsValid()) + return false; + + if (!type_name.IsValid()) + return false; + + if (type_name.IsRegex()) + return m_opaque_sp->GetRegexSyntheticNavigator()->Delete(ConstString(type_name.GetName())); + else + return m_opaque_sp->GetSyntheticNavigator()->Delete(ConstString(type_name.GetName())); +} +#endif // LLDB_DISABLE_PYTHON + +bool +SBTypeCategory::GetDescription (lldb::SBStream &description, + lldb::DescriptionLevel description_level) +{ + if (!IsValid()) + return false; + description.Printf("Category name: %s\n",GetName()); + return true; +} + +lldb::SBTypeCategory & +SBTypeCategory::operator = (const lldb::SBTypeCategory &rhs) +{ + if (this != &rhs) + { + m_opaque_sp = rhs.m_opaque_sp; + } + return *this; +} + +bool +SBTypeCategory::operator == (lldb::SBTypeCategory &rhs) +{ + if (IsValid() == false) + return !rhs.IsValid(); + + return m_opaque_sp.get() == rhs.m_opaque_sp.get(); + +} + +bool +SBTypeCategory::operator != (lldb::SBTypeCategory &rhs) +{ + if (IsValid() == false) + return rhs.IsValid(); + + return m_opaque_sp.get() != rhs.m_opaque_sp.get(); +} + +lldb::TypeCategoryImplSP +SBTypeCategory::GetSP () +{ + if (!IsValid()) + return lldb::TypeCategoryImplSP(); + return m_opaque_sp; +} + +void +SBTypeCategory::SetSP (const lldb::TypeCategoryImplSP &typecategory_impl_sp) +{ + m_opaque_sp = typecategory_impl_sp; +} + +SBTypeCategory::SBTypeCategory (const lldb::TypeCategoryImplSP &typecategory_impl_sp) : +m_opaque_sp(typecategory_impl_sp) +{ +} + +bool +SBTypeCategory::IsDefaultCategory() +{ + if (!IsValid()) + return false; + + return (strcmp(m_opaque_sp->GetName(),"default") == 0); +} + diff --git a/contrib/llvm/tools/lldb/source/API/SBTypeFilter.cpp b/contrib/llvm/tools/lldb/source/API/SBTypeFilter.cpp new file mode 100644 index 00000000000..605e92de699 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/API/SBTypeFilter.cpp @@ -0,0 +1,199 @@ +//===-- SBTypeFilter.cpp ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "lldb/API/SBTypeFilter.h" + +#include "lldb/API/SBStream.h" + +#include "lldb/DataFormatters/DataVisualization.h" + +using namespace lldb; +using namespace lldb_private; + +SBTypeFilter::SBTypeFilter() : +m_opaque_sp() +{ +} + +SBTypeFilter::SBTypeFilter (uint32_t options) +: m_opaque_sp(TypeFilterImplSP(new TypeFilterImpl(options))) +{ +} + +SBTypeFilter::SBTypeFilter (const lldb::SBTypeFilter &rhs) : +m_opaque_sp(rhs.m_opaque_sp) +{ +} + +SBTypeFilter::~SBTypeFilter () +{ +} + +bool +SBTypeFilter::IsValid() const +{ + return m_opaque_sp.get() != NULL; +} + +uint32_t +SBTypeFilter::GetOptions() +{ + if (IsValid()) + return m_opaque_sp->GetOptions(); + return 0; +} + +void +SBTypeFilter::SetOptions (uint32_t value) +{ + if (CopyOnWrite_Impl()) + m_opaque_sp->SetOptions(value); +} + +bool +SBTypeFilter::GetDescription (lldb::SBStream &description, + lldb::DescriptionLevel description_level) +{ + if (!IsValid()) + return false; + else { + description.Printf("%s\n", + m_opaque_sp->GetDescription().c_str()); + return true; + } +} + +void +SBTypeFilter::Clear() +{ + if (CopyOnWrite_Impl()) + m_opaque_sp->Clear(); +} + +uint32_t +SBTypeFilter::GetNumberOfExpressionPaths() +{ + if (IsValid()) + return m_opaque_sp->GetCount(); + return 0; +} + +const char* +SBTypeFilter::GetExpressionPathAtIndex (uint32_t i) +{ + if (IsValid()) + { + const char* item = m_opaque_sp->GetExpressionPathAtIndex(i); + if (item && *item == '.') + item++; + return item; + } + return NULL; +} + +bool +SBTypeFilter::ReplaceExpressionPathAtIndex (uint32_t i, const char* item) +{ + if (CopyOnWrite_Impl()) + return m_opaque_sp->SetExpressionPathAtIndex(i, item); + else + return false; +} + +void +SBTypeFilter::AppendExpressionPath (const char* item) +{ + if (CopyOnWrite_Impl()) + m_opaque_sp->AddExpressionPath(item); +} + +lldb::SBTypeFilter & +SBTypeFilter::operator = (const lldb::SBTypeFilter &rhs) +{ + if (this != &rhs) + { + m_opaque_sp = rhs.m_opaque_sp; + } + return *this; +} + +bool +SBTypeFilter::operator == (lldb::SBTypeFilter &rhs) +{ + if (IsValid() == false) + return !rhs.IsValid(); + + return m_opaque_sp == rhs.m_opaque_sp; +} + +bool +SBTypeFilter::IsEqualTo (lldb::SBTypeFilter &rhs) +{ + if (IsValid() == false) + return !rhs.IsValid(); + + if (GetNumberOfExpressionPaths() != rhs.GetNumberOfExpressionPaths()) + return false; + + for (uint32_t j = 0; + j < GetNumberOfExpressionPaths(); + j++) + if ( strcmp(GetExpressionPathAtIndex(j),rhs.GetExpressionPathAtIndex(j)) != 0) + return false; + + return GetOptions() == rhs.GetOptions(); +} + +bool +SBTypeFilter::operator != (lldb::SBTypeFilter &rhs) +{ + if (IsValid() == false) + return !rhs.IsValid(); + + return m_opaque_sp != rhs.m_opaque_sp; +} + +lldb::TypeFilterImplSP +SBTypeFilter::GetSP () +{ + return m_opaque_sp; +} + +void +SBTypeFilter::SetSP (const lldb::TypeFilterImplSP &typefilter_impl_sp) +{ + m_opaque_sp = typefilter_impl_sp; +} + +SBTypeFilter::SBTypeFilter (const lldb::TypeFilterImplSP &typefilter_impl_sp) : +m_opaque_sp(typefilter_impl_sp) +{ +} + +bool +SBTypeFilter::CopyOnWrite_Impl() +{ + if (!IsValid()) + return false; + if (m_opaque_sp.unique()) + return true; + + TypeFilterImplSP new_sp(new TypeFilterImpl(GetOptions())); + + for (uint32_t j = 0; + j < GetNumberOfExpressionPaths(); + j++) + new_sp->AddExpressionPath(GetExpressionPathAtIndex(j)); + + SetSP(new_sp); + + return true; +} diff --git a/contrib/llvm/tools/lldb/source/API/SBTypeFormat.cpp b/contrib/llvm/tools/lldb/source/API/SBTypeFormat.cpp new file mode 100644 index 00000000000..34ab404a206 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/API/SBTypeFormat.cpp @@ -0,0 +1,155 @@ +//===-- SBTypeFormat.cpp ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "lldb/API/SBTypeFormat.h" + +#include "lldb/API/SBStream.h" + +#include "lldb/DataFormatters/DataVisualization.h" + +using namespace lldb; +using namespace lldb_private; + +SBTypeFormat::SBTypeFormat() : +m_opaque_sp() +{ +} + +SBTypeFormat::SBTypeFormat (lldb::Format format, + uint32_t options) +: m_opaque_sp(TypeFormatImplSP(new TypeFormatImpl(format,options))) +{ +} + +SBTypeFormat::SBTypeFormat (const lldb::SBTypeFormat &rhs) : +m_opaque_sp(rhs.m_opaque_sp) +{ +} + +SBTypeFormat::~SBTypeFormat () +{ +} + +bool +SBTypeFormat::IsValid() const +{ + return m_opaque_sp.get() != NULL; +} + +lldb::Format +SBTypeFormat::GetFormat () +{ + if (IsValid()) + return m_opaque_sp->GetFormat(); + return lldb::eFormatInvalid; +} + +uint32_t +SBTypeFormat::GetOptions() +{ + if (IsValid()) + return m_opaque_sp->GetOptions(); + return 0; +} + +void +SBTypeFormat::SetFormat (lldb::Format fmt) +{ + if (CopyOnWrite_Impl()) + m_opaque_sp->SetFormat(fmt); +} + +void +SBTypeFormat::SetOptions (uint32_t value) +{ + if (CopyOnWrite_Impl()) + m_opaque_sp->SetOptions(value); +} + +bool +SBTypeFormat::GetDescription (lldb::SBStream &description, + lldb::DescriptionLevel description_level) +{ + if (!IsValid()) + return false; + else { + description.Printf("%s\n", + m_opaque_sp->GetDescription().c_str()); + return true; + } +} + +lldb::SBTypeFormat & +SBTypeFormat::operator = (const lldb::SBTypeFormat &rhs) +{ + if (this != &rhs) + { + m_opaque_sp = rhs.m_opaque_sp; + } + return *this; +} + +bool +SBTypeFormat::operator == (lldb::SBTypeFormat &rhs) +{ + if (IsValid() == false) + return !rhs.IsValid(); + return m_opaque_sp == rhs.m_opaque_sp; +} + +bool +SBTypeFormat::IsEqualTo (lldb::SBTypeFormat &rhs) +{ + if (IsValid() == false) + return !rhs.IsValid(); + + if (GetFormat() == rhs.GetFormat()) + return GetOptions() == rhs.GetOptions(); + else + return false; +} + +bool +SBTypeFormat::operator != (lldb::SBTypeFormat &rhs) +{ + if (IsValid() == false) + return !rhs.IsValid(); + return m_opaque_sp != rhs.m_opaque_sp; +} + +lldb::TypeFormatImplSP +SBTypeFormat::GetSP () +{ + return m_opaque_sp; +} + +void +SBTypeFormat::SetSP (const lldb::TypeFormatImplSP &typeformat_impl_sp) +{ + m_opaque_sp = typeformat_impl_sp; +} + +SBTypeFormat::SBTypeFormat (const lldb::TypeFormatImplSP &typeformat_impl_sp) : + m_opaque_sp(typeformat_impl_sp) +{ +} + +bool +SBTypeFormat::CopyOnWrite_Impl() +{ + if (!IsValid()) + return false; + if (m_opaque_sp.unique()) + return true; + + SetSP(TypeFormatImplSP(new TypeFormatImpl(GetFormat(),GetOptions()))); + return true; +} diff --git a/contrib/llvm/tools/lldb/source/API/SBTypeNameSpecifier.cpp b/contrib/llvm/tools/lldb/source/API/SBTypeNameSpecifier.cpp new file mode 100644 index 00000000000..d417499ecbd --- /dev/null +++ b/contrib/llvm/tools/lldb/source/API/SBTypeNameSpecifier.cpp @@ -0,0 +1,150 @@ +//===-- SBTypeNameSpecifier.cpp ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "lldb/API/SBTypeNameSpecifier.h" + +#include "lldb/API/SBStream.h" +#include "lldb/API/SBType.h" + +#include "lldb/DataFormatters/DataVisualization.h" + +using namespace lldb; +using namespace lldb_private; + +SBTypeNameSpecifier::SBTypeNameSpecifier() : +m_opaque_sp() +{ +} + +SBTypeNameSpecifier::SBTypeNameSpecifier (const char* name, + bool is_regex) : +m_opaque_sp(new TypeNameSpecifierImpl(name, is_regex)) +{ + if (name == NULL || (*name) == 0) + m_opaque_sp.reset(); +} + +SBTypeNameSpecifier::SBTypeNameSpecifier (SBType type) : +m_opaque_sp() +{ + if (type.IsValid()) + m_opaque_sp = TypeNameSpecifierImplSP(new TypeNameSpecifierImpl(type.m_opaque_sp->GetClangASTType())); +} + +SBTypeNameSpecifier::SBTypeNameSpecifier (const lldb::SBTypeNameSpecifier &rhs) : +m_opaque_sp(rhs.m_opaque_sp) +{} + +SBTypeNameSpecifier::~SBTypeNameSpecifier () +{ +} + +bool +SBTypeNameSpecifier::IsValid() const +{ + return m_opaque_sp.get() != NULL; +} + +const char* +SBTypeNameSpecifier::GetName () +{ + if (!IsValid()) + return NULL; + + return m_opaque_sp->GetName(); +} + +SBType +SBTypeNameSpecifier::GetType () +{ + if (!IsValid()) + return SBType(); + lldb_private::ClangASTType c_type = m_opaque_sp->GetClangASTType(); + if (c_type.IsValid()) + return SBType(c_type); + return SBType(); +} + +bool +SBTypeNameSpecifier::IsRegex () +{ + if (!IsValid()) + return false; + + return m_opaque_sp->IsRegex(); +} + +bool +SBTypeNameSpecifier::GetDescription (lldb::SBStream &description, + lldb::DescriptionLevel description_level) +{ + if (!IsValid()) + return false; + description.Printf("SBTypeNameSpecifier(%s,%s)", GetName(), IsRegex() ? "regex" : "plain"); + return true; +} + +lldb::SBTypeNameSpecifier & +SBTypeNameSpecifier::operator = (const lldb::SBTypeNameSpecifier &rhs) +{ + if (this != &rhs) + { + m_opaque_sp = rhs.m_opaque_sp; + } + return *this; +} + +bool +SBTypeNameSpecifier::operator == (lldb::SBTypeNameSpecifier &rhs) +{ + if (IsValid() == false) + return !rhs.IsValid(); + return m_opaque_sp == rhs.m_opaque_sp; +} + +bool +SBTypeNameSpecifier::IsEqualTo (lldb::SBTypeNameSpecifier &rhs) +{ + if (IsValid() == false) + return !rhs.IsValid(); + + if (IsRegex() != rhs.IsRegex()) + return false; + if (GetName() == NULL || rhs.GetName() == NULL) + return false; + + return (strcmp(GetName(), rhs.GetName()) == 0); +} + +bool +SBTypeNameSpecifier::operator != (lldb::SBTypeNameSpecifier &rhs) +{ + if (IsValid() == false) + return !rhs.IsValid(); + return m_opaque_sp != rhs.m_opaque_sp; +} + +lldb::TypeNameSpecifierImplSP +SBTypeNameSpecifier::GetSP () +{ + return m_opaque_sp; +} + +void +SBTypeNameSpecifier::SetSP (const lldb::TypeNameSpecifierImplSP &type_namespec_sp) +{ + m_opaque_sp = type_namespec_sp; +} + +SBTypeNameSpecifier::SBTypeNameSpecifier (const lldb::TypeNameSpecifierImplSP &type_namespec_sp) : +m_opaque_sp(type_namespec_sp) +{ +} diff --git a/contrib/llvm/tools/lldb/source/API/SBTypeSummary.cpp b/contrib/llvm/tools/lldb/source/API/SBTypeSummary.cpp new file mode 100644 index 00000000000..aaa09c289cb --- /dev/null +++ b/contrib/llvm/tools/lldb/source/API/SBTypeSummary.cpp @@ -0,0 +1,335 @@ +//===-- SBTypeSummary.cpp -----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "lldb/API/SBTypeSummary.h" + +#include "lldb/API/SBStream.h" + +#include "lldb/DataFormatters/DataVisualization.h" + +using namespace lldb; +using namespace lldb_private; + +#ifndef LLDB_DISABLE_PYTHON + +SBTypeSummary::SBTypeSummary() : +m_opaque_sp() +{ +} + +SBTypeSummary +SBTypeSummary::CreateWithSummaryString (const char* data, uint32_t options) +{ + if (!data || data[0] == 0) + return SBTypeSummary(); + + return SBTypeSummary(TypeSummaryImplSP(new StringSummaryFormat(options, data))); +} + +SBTypeSummary +SBTypeSummary::CreateWithFunctionName (const char* data, uint32_t options) +{ + if (!data || data[0] == 0) + return SBTypeSummary(); + + return SBTypeSummary(TypeSummaryImplSP(new ScriptSummaryFormat(options, data))); +} + +SBTypeSummary +SBTypeSummary::CreateWithScriptCode (const char* data, uint32_t options) +{ + if (!data || data[0] == 0) + return SBTypeSummary(); + + return SBTypeSummary(TypeSummaryImplSP(new ScriptSummaryFormat(options, "", data))); +} + +SBTypeSummary::SBTypeSummary (const lldb::SBTypeSummary &rhs) : +m_opaque_sp(rhs.m_opaque_sp) +{ +} + +SBTypeSummary::~SBTypeSummary () +{ +} + +bool +SBTypeSummary::IsValid() const +{ + return m_opaque_sp.get() != NULL; +} + +bool +SBTypeSummary::IsFunctionCode() +{ + if (!IsValid()) + return false; + if (m_opaque_sp->IsScripted()) + { + ScriptSummaryFormat* script_summary_ptr = (ScriptSummaryFormat*)m_opaque_sp.get(); + const char* ftext = script_summary_ptr->GetPythonScript(); + return (ftext && *ftext != 0); + } + return false; +} + +bool +SBTypeSummary::IsFunctionName() +{ + if (!IsValid()) + return false; + if (m_opaque_sp->IsScripted()) + { + ScriptSummaryFormat* script_summary_ptr = (ScriptSummaryFormat*)m_opaque_sp.get(); + const char* ftext = script_summary_ptr->GetPythonScript(); + return (!ftext || *ftext == 0); + } + return false; +} + +bool +SBTypeSummary::IsSummaryString() +{ + if (!IsValid()) + return false; + + if (m_opaque_sp->GetType() == lldb_private::TypeSummaryImpl::eTypeCallback) + return false; + + return !m_opaque_sp->IsScripted(); +} + +const char* +SBTypeSummary::GetData () +{ + if (!IsValid()) + return NULL; + if (m_opaque_sp->GetType() == lldb_private::TypeSummaryImpl::eTypeCallback) + return NULL; + if (m_opaque_sp->IsScripted()) + { + ScriptSummaryFormat* script_summary_ptr = (ScriptSummaryFormat*)m_opaque_sp.get(); + const char* fname = script_summary_ptr->GetFunctionName(); + const char* ftext = script_summary_ptr->GetPythonScript(); + if (ftext && *ftext) + return ftext; + return fname; + } + else + { + StringSummaryFormat* string_summary_ptr = (StringSummaryFormat*)m_opaque_sp.get(); + return string_summary_ptr->GetSummaryString(); + } +} + +uint32_t +SBTypeSummary::GetOptions () +{ + if (!IsValid()) + return lldb::eTypeOptionNone; + return m_opaque_sp->GetOptions(); +} + +void +SBTypeSummary::SetOptions (uint32_t value) +{ + if (!CopyOnWrite_Impl()) + return; + m_opaque_sp->SetOptions(value); +} + +void +SBTypeSummary::SetSummaryString (const char* data) +{ + if (!IsValid()) + return; + if (m_opaque_sp->IsScripted() || (m_opaque_sp->GetType() == lldb_private::TypeSummaryImpl::eTypeCallback)) + ChangeSummaryType(false); + ((StringSummaryFormat*)m_opaque_sp.get())->SetSummaryString(data); +} + +void +SBTypeSummary::SetFunctionName (const char* data) +{ + if (!IsValid()) + return; + if (!m_opaque_sp->IsScripted()) + ChangeSummaryType(true); + ((ScriptSummaryFormat*)m_opaque_sp.get())->SetFunctionName(data); +} + +void +SBTypeSummary::SetFunctionCode (const char* data) +{ + if (!IsValid()) + return; + if (!m_opaque_sp->IsScripted()) + ChangeSummaryType(true); + ((ScriptSummaryFormat*)m_opaque_sp.get())->SetPythonScript(data); +} + +bool +SBTypeSummary::GetDescription (lldb::SBStream &description, + lldb::DescriptionLevel description_level) +{ + if (!CopyOnWrite_Impl()) + return false; + else { + description.Printf("%s\n", + m_opaque_sp->GetDescription().c_str()); + return true; + } +} + +lldb::SBTypeSummary & +SBTypeSummary::operator = (const lldb::SBTypeSummary &rhs) +{ + if (this != &rhs) + { + m_opaque_sp = rhs.m_opaque_sp; + } + return *this; +} + +bool +SBTypeSummary::operator == (lldb::SBTypeSummary &rhs) +{ + if (IsValid() == false) + return !rhs.IsValid(); + return m_opaque_sp == rhs.m_opaque_sp; +} + +bool +SBTypeSummary::IsEqualTo (lldb::SBTypeSummary &rhs) +{ + if (IsValid() == false) + return !rhs.IsValid(); + + if (m_opaque_sp->GetType() != rhs.m_opaque_sp->GetType()) + return false; + + if (m_opaque_sp->GetType() == lldb_private::TypeSummaryImpl::eTypeCallback) + { + lldb_private::CXXFunctionSummaryFormat *self_cxx = (lldb_private::CXXFunctionSummaryFormat*)m_opaque_sp.get(); + lldb_private::CXXFunctionSummaryFormat *other_cxx = (lldb_private::CXXFunctionSummaryFormat*)rhs.m_opaque_sp.get(); + return (self_cxx->m_impl == other_cxx->m_impl); + } + + if (m_opaque_sp->IsScripted() != rhs.m_opaque_sp->IsScripted()) + return false; + + if (IsFunctionCode() != rhs.IsFunctionCode()) + return false; + + if (IsSummaryString() != rhs.IsSummaryString()) + return false; + + if (IsFunctionName() != rhs.IsFunctionName()) + return false; + + if ( GetData() == NULL || rhs.GetData() == NULL || strcmp(GetData(), rhs.GetData()) ) + return false; + + return GetOptions() == rhs.GetOptions(); + +} + +bool +SBTypeSummary::operator != (lldb::SBTypeSummary &rhs) +{ + if (IsValid() == false) + return !rhs.IsValid(); + return m_opaque_sp != rhs.m_opaque_sp; +} + +lldb::TypeSummaryImplSP +SBTypeSummary::GetSP () +{ + return m_opaque_sp; +} + +void +SBTypeSummary::SetSP (const lldb::TypeSummaryImplSP &typesummary_impl_sp) +{ + m_opaque_sp = typesummary_impl_sp; +} + +SBTypeSummary::SBTypeSummary (const lldb::TypeSummaryImplSP &typesummary_impl_sp) : +m_opaque_sp(typesummary_impl_sp) +{ +} + +bool +SBTypeSummary::CopyOnWrite_Impl() +{ + if (!IsValid()) + return false; + + if (m_opaque_sp.unique()) + return true; + + TypeSummaryImplSP new_sp; + + if (m_opaque_sp->GetType() == lldb_private::TypeSummaryImpl::eTypeCallback) + { + CXXFunctionSummaryFormat* current_summary_ptr = (CXXFunctionSummaryFormat*)m_opaque_sp.get(); + new_sp = TypeSummaryImplSP(new CXXFunctionSummaryFormat(GetOptions(), + current_summary_ptr->m_impl, + current_summary_ptr->m_description.c_str())); + } + else if (m_opaque_sp->IsScripted()) + { + ScriptSummaryFormat* current_summary_ptr = (ScriptSummaryFormat*)m_opaque_sp.get(); + new_sp = TypeSummaryImplSP(new ScriptSummaryFormat(GetOptions(), + current_summary_ptr->GetFunctionName(), + current_summary_ptr->GetPythonScript())); + } + else { + StringSummaryFormat* current_summary_ptr = (StringSummaryFormat*)m_opaque_sp.get(); + new_sp = TypeSummaryImplSP(new StringSummaryFormat(GetOptions(), + current_summary_ptr->GetSummaryString())); + } + + SetSP(new_sp); + + return true; +} + +bool +SBTypeSummary::ChangeSummaryType (bool want_script) +{ + if (!IsValid()) + return false; + + TypeSummaryImplSP new_sp; + + if (want_script == m_opaque_sp->IsScripted()) + { + if (m_opaque_sp->GetType() == lldb_private::TypeSummaryImpl::eTypeCallback && !want_script) + new_sp = TypeSummaryImplSP(new StringSummaryFormat(GetOptions(), "")); + else + return CopyOnWrite_Impl(); + } + + if (!new_sp) + { + if (want_script) + new_sp = TypeSummaryImplSP(new ScriptSummaryFormat(GetOptions(), "", "")); + else + new_sp = TypeSummaryImplSP(new StringSummaryFormat(GetOptions(), "")); + } + + SetSP(new_sp); + + return true; +} + +#endif // LLDB_DISABLE_PYTHON diff --git a/contrib/llvm/tools/lldb/source/API/SBTypeSynthetic.cpp b/contrib/llvm/tools/lldb/source/API/SBTypeSynthetic.cpp new file mode 100644 index 00000000000..681ed6c032a --- /dev/null +++ b/contrib/llvm/tools/lldb/source/API/SBTypeSynthetic.cpp @@ -0,0 +1,209 @@ +//===-- SBTypeSynthetic.cpp -----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "lldb/API/SBTypeSynthetic.h" + +#include "lldb/API/SBStream.h" + +#include "lldb/DataFormatters/DataVisualization.h" + +using namespace lldb; +using namespace lldb_private; + +#ifndef LLDB_DISABLE_PYTHON + +SBTypeSynthetic::SBTypeSynthetic() : +m_opaque_sp() +{ +} + +SBTypeSynthetic +SBTypeSynthetic::CreateWithClassName (const char* data, uint32_t options) +{ + if (!data || data[0] == 0) + return SBTypeSynthetic(); + return SBTypeSynthetic(ScriptedSyntheticChildrenSP(new ScriptedSyntheticChildren(options, data, ""))); +} + +SBTypeSynthetic +SBTypeSynthetic::CreateWithScriptCode (const char* data, uint32_t options) +{ + if (!data || data[0] == 0) + return SBTypeSynthetic(); + return SBTypeSynthetic(ScriptedSyntheticChildrenSP(new ScriptedSyntheticChildren(options, "", data))); +} + +SBTypeSynthetic::SBTypeSynthetic (const lldb::SBTypeSynthetic &rhs) : +m_opaque_sp(rhs.m_opaque_sp) +{ +} + +SBTypeSynthetic::~SBTypeSynthetic () +{ +} + +bool +SBTypeSynthetic::IsValid() const +{ + return m_opaque_sp.get() != NULL; +} + +bool +SBTypeSynthetic::IsClassCode() +{ + if (!IsValid()) + return false; + const char* code = m_opaque_sp->GetPythonCode(); + return (code && *code); +} + +bool +SBTypeSynthetic::IsClassName() +{ + if (!IsValid()) + return false; + return !IsClassCode(); +} + +const char* +SBTypeSynthetic::GetData () +{ + if (!IsValid()) + return NULL; + if (IsClassCode()) + return m_opaque_sp->GetPythonCode(); + else + return m_opaque_sp->GetPythonClassName(); +} + +void +SBTypeSynthetic::SetClassName (const char* data) +{ + if (IsValid() && data && *data) + m_opaque_sp->SetPythonClassName(data); +} + +void +SBTypeSynthetic::SetClassCode (const char* data) +{ + if (IsValid() && data && *data) + m_opaque_sp->SetPythonCode(data); +} + +uint32_t +SBTypeSynthetic::GetOptions () +{ + if (!IsValid()) + return lldb::eTypeOptionNone; + return m_opaque_sp->GetOptions(); +} + +void +SBTypeSynthetic::SetOptions (uint32_t value) +{ + if (!CopyOnWrite_Impl()) + return; + m_opaque_sp->SetOptions(value); +} + +bool +SBTypeSynthetic::GetDescription (lldb::SBStream &description, + lldb::DescriptionLevel description_level) +{ + if (m_opaque_sp) + { + description.Printf("%s\n", + m_opaque_sp->GetDescription().c_str()); + return true; + } + return false; +} + +lldb::SBTypeSynthetic & +SBTypeSynthetic::operator = (const lldb::SBTypeSynthetic &rhs) +{ + if (this != &rhs) + { + m_opaque_sp = rhs.m_opaque_sp; + } + return *this; +} + +bool +SBTypeSynthetic::operator == (lldb::SBTypeSynthetic &rhs) +{ + if (IsValid() == false) + return !rhs.IsValid(); + return m_opaque_sp == rhs.m_opaque_sp; +} + +bool +SBTypeSynthetic::IsEqualTo (lldb::SBTypeSynthetic &rhs) +{ + if (IsValid() == false) + return !rhs.IsValid(); + + if (m_opaque_sp->IsScripted() != rhs.m_opaque_sp->IsScripted()) + return false; + + if (IsClassCode() != rhs.IsClassCode()) + return false; + + if ( strcmp(GetData(), rhs.GetData()) ) + return false; + + return GetOptions() == rhs.GetOptions(); + +} + +bool +SBTypeSynthetic::operator != (lldb::SBTypeSynthetic &rhs) +{ + if (IsValid() == false) + return !rhs.IsValid(); + return m_opaque_sp != rhs.m_opaque_sp; +} + +lldb::ScriptedSyntheticChildrenSP +SBTypeSynthetic::GetSP () +{ + return m_opaque_sp; +} + +void +SBTypeSynthetic::SetSP (const lldb::ScriptedSyntheticChildrenSP &TypeSynthetic_impl_sp) +{ + m_opaque_sp = TypeSynthetic_impl_sp; +} + +SBTypeSynthetic::SBTypeSynthetic (const lldb::ScriptedSyntheticChildrenSP &TypeSynthetic_impl_sp) : +m_opaque_sp(TypeSynthetic_impl_sp) +{ +} + +bool +SBTypeSynthetic::CopyOnWrite_Impl() +{ + if (!IsValid()) + return false; + if (m_opaque_sp.unique()) + return true; + + ScriptedSyntheticChildrenSP new_sp(new ScriptedSyntheticChildren(m_opaque_sp->GetOptions(), + m_opaque_sp->GetPythonClassName(), + m_opaque_sp->GetPythonCode())); + + SetSP(new_sp); + + return true; +} + +#endif // LLDB_DISABLE_PYTHON diff --git a/contrib/llvm/tools/lldb/source/API/SBValue.cpp b/contrib/llvm/tools/lldb/source/API/SBValue.cpp new file mode 100644 index 00000000000..aa9b23ac7c6 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/API/SBValue.cpp @@ -0,0 +1,1719 @@ +//===-- SBValue.cpp ---------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "lldb/API/SBValue.h" + +#include "lldb/API/SBDeclaration.h" +#include "lldb/API/SBStream.h" +#include "lldb/API/SBTypeFilter.h" +#include "lldb/API/SBTypeFormat.h" +#include "lldb/API/SBTypeSummary.h" +#include "lldb/API/SBTypeSynthetic.h" + +#include "lldb/Breakpoint/Watchpoint.h" +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/Scalar.h" +#include "lldb/Core/Section.h" +#include "lldb/Core/Stream.h" +#include "lldb/Core/StreamFile.h" +#include "lldb/Core/Value.h" +#include "lldb/Core/ValueObject.h" +#include "lldb/Core/ValueObjectConstResult.h" +#include "lldb/DataFormatters/DataVisualization.h" +#include "lldb/Symbol/Block.h" +#include "lldb/Symbol/Declaration.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Symbol/Type.h" +#include "lldb/Symbol/Variable.h" +#include "lldb/Symbol/VariableList.h" +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/StackFrame.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" + +#include "lldb/API/SBDebugger.h" +#include "lldb/API/SBExpressionOptions.h" +#include "lldb/API/SBFrame.h" +#include "lldb/API/SBProcess.h" +#include "lldb/API/SBTarget.h" +#include "lldb/API/SBThread.h" + +using namespace lldb; +using namespace lldb_private; + +class ValueImpl +{ +public: + ValueImpl () + { + } + + ValueImpl (lldb::ValueObjectSP in_valobj_sp, + lldb::DynamicValueType use_dynamic, + bool use_synthetic, + const char *name = NULL) : + m_valobj_sp(in_valobj_sp), + m_use_dynamic(use_dynamic), + m_use_synthetic(use_synthetic), + m_name (name) + { + if (!m_name.IsEmpty() && m_valobj_sp) + m_valobj_sp->SetName(m_name); + } + + ValueImpl (const ValueImpl& rhs) : + m_valobj_sp(rhs.m_valobj_sp), + m_use_dynamic(rhs.m_use_dynamic), + m_use_synthetic(rhs.m_use_synthetic), + m_name (rhs.m_name) + { + } + + ValueImpl & + operator = (const ValueImpl &rhs) + { + if (this != &rhs) + { + m_valobj_sp = rhs.m_valobj_sp; + m_use_dynamic = rhs.m_use_dynamic; + m_use_synthetic = rhs.m_use_synthetic; + m_name = rhs.m_name; + } + return *this; + } + + bool + IsValid () + { + return m_valobj_sp.get() != NULL; + } + + lldb::ValueObjectSP + GetRootSP () + { + return m_valobj_sp; + } + + lldb::ValueObjectSP + GetSP (Process::StopLocker &stop_locker, Mutex::Locker &api_locker, Error &error) + { + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (!m_valobj_sp) + { + error.SetErrorString("invalid value object"); + return m_valobj_sp; + } + + lldb::ValueObjectSP value_sp = m_valobj_sp; + + Target *target = value_sp->GetTargetSP().get(); + if (target) + api_locker.Lock(target->GetAPIMutex()); + + ProcessSP process_sp(value_sp->GetProcessSP()); + if (process_sp && !stop_locker.TryLock (&process_sp->GetRunLock())) + { + // We don't allow people to play around with ValueObject if the process is running. + // If you want to look at values, pause the process, then look. + if (log) + log->Printf ("SBValue(%p)::GetSP() => error: process is running", value_sp.get()); + error.SetErrorString ("process must be stopped."); + return ValueObjectSP(); + } + + if (value_sp->GetDynamicValue(m_use_dynamic)) + value_sp = value_sp->GetDynamicValue(m_use_dynamic); + if (value_sp->GetSyntheticValue(m_use_synthetic)) + value_sp = value_sp->GetSyntheticValue(m_use_synthetic); + if (!value_sp) + error.SetErrorString("invalid value object"); + if (!m_name.IsEmpty()) + value_sp->SetName(m_name); + + return value_sp; + } + + void + SetUseDynamic (lldb::DynamicValueType use_dynamic) + { + m_use_dynamic = use_dynamic; + } + + void + SetUseSynthetic (bool use_synthetic) + { + m_use_synthetic = use_synthetic; + } + + lldb::DynamicValueType + GetUseDynamic () + { + return m_use_dynamic; + } + + bool + GetUseSynthetic () + { + return m_use_synthetic; + } + + // All the derived values that we would make from the m_valobj_sp will share + // the ExecutionContext with m_valobj_sp, so we don't need to do the calculations + // in GetSP to return the Target, Process, Thread or Frame. It is convenient to + // provide simple accessors for these, which I do here. + TargetSP + GetTargetSP () + { + if (m_valobj_sp) + return m_valobj_sp->GetTargetSP(); + else + return TargetSP(); + } + + ProcessSP + GetProcessSP () + { + if (m_valobj_sp) + return m_valobj_sp->GetProcessSP(); + else + return ProcessSP(); + } + + ThreadSP + GetThreadSP () + { + if (m_valobj_sp) + return m_valobj_sp->GetThreadSP(); + else + return ThreadSP(); + } + + StackFrameSP + GetFrameSP () + { + if (m_valobj_sp) + return m_valobj_sp->GetFrameSP(); + else + return StackFrameSP(); + } + +private: + lldb::ValueObjectSP m_valobj_sp; + lldb::DynamicValueType m_use_dynamic; + bool m_use_synthetic; + ConstString m_name; +}; + +class ValueLocker +{ +public: + ValueLocker () + { + } + + ValueObjectSP + GetLockedSP(ValueImpl &in_value) + { + return in_value.GetSP(m_stop_locker, m_api_locker, m_lock_error); + } + + Error & + GetError() + { + return m_lock_error; + } + +private: + Process::StopLocker m_stop_locker; + Mutex::Locker m_api_locker; + Error m_lock_error; + +}; + +SBValue::SBValue () : + m_opaque_sp () +{ +} + +SBValue::SBValue (const lldb::ValueObjectSP &value_sp) +{ + SetSP(value_sp); +} + +SBValue::SBValue(const SBValue &rhs) +{ + SetSP(rhs.m_opaque_sp); +} + +SBValue & +SBValue::operator = (const SBValue &rhs) +{ + if (this != &rhs) + { + SetSP(rhs.m_opaque_sp); + } + return *this; +} + +SBValue::~SBValue() +{ +} + +bool +SBValue::IsValid () +{ + // If this function ever changes to anything that does more than just + // check if the opaque shared pointer is non NULL, then we need to update + // all "if (m_opaque_sp)" code in this file. + return m_opaque_sp.get() != NULL && m_opaque_sp->GetRootSP().get() != NULL; +} + +void +SBValue::Clear() +{ + m_opaque_sp.reset(); +} + +SBError +SBValue::GetError() +{ + SBError sb_error; + + ValueLocker locker; + lldb::ValueObjectSP value_sp(GetSP(locker)); + if (value_sp) + sb_error.SetError(value_sp->GetError()); + else + sb_error.SetErrorStringWithFormat ("error: %s", locker.GetError().AsCString()); + + return sb_error; +} + +user_id_t +SBValue::GetID() +{ + ValueLocker locker; + lldb::ValueObjectSP value_sp(GetSP(locker)); + if (value_sp) + return value_sp->GetID(); + return LLDB_INVALID_UID; +} + +const char * +SBValue::GetName() +{ + const char *name = NULL; + ValueLocker locker; + lldb::ValueObjectSP value_sp(GetSP(locker)); + if (value_sp) + name = value_sp->GetName().GetCString(); + + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (log) + { + if (name) + log->Printf ("SBValue(%p)::GetName () => \"%s\"", value_sp.get(), name); + else + log->Printf ("SBValue(%p)::GetName () => NULL", value_sp.get()); + } + + return name; +} + +const char * +SBValue::GetTypeName () +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + const char *name = NULL; + ValueLocker locker; + lldb::ValueObjectSP value_sp(GetSP(locker)); + if (value_sp) + { + name = value_sp->GetQualifiedTypeName().GetCString(); + } + + if (log) + { + if (name) + log->Printf ("SBValue(%p)::GetTypeName () => \"%s\"", value_sp.get(), name); + else + log->Printf ("SBValue(%p)::GetTypeName () => NULL", value_sp.get()); + } + + return name; +} + +size_t +SBValue::GetByteSize () +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + size_t result = 0; + + ValueLocker locker; + lldb::ValueObjectSP value_sp(GetSP(locker)); + if (value_sp) + { + result = value_sp->GetByteSize(); + } + + if (log) + log->Printf ("SBValue(%p)::GetByteSize () => %" PRIu64, value_sp.get(), (uint64_t)result); + + return result; +} + +bool +SBValue::IsInScope () +{ + bool result = false; + + ValueLocker locker; + lldb::ValueObjectSP value_sp(GetSP(locker)); + if (value_sp) + { + result = value_sp->IsInScope (); + } + + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (log) + log->Printf ("SBValue(%p)::IsInScope () => %i", value_sp.get(), result); + + return result; +} + +const char * +SBValue::GetValue () +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + const char *cstr = NULL; + ValueLocker locker; + lldb::ValueObjectSP value_sp(GetSP(locker)); + if (value_sp) + { + cstr = value_sp->GetValueAsCString (); + } + if (log) + { + if (cstr) + log->Printf ("SBValue(%p)::GetValue() => \"%s\"", value_sp.get(), cstr); + else + log->Printf ("SBValue(%p)::GetValue() => NULL", value_sp.get()); + } + + return cstr; +} + +ValueType +SBValue::GetValueType () +{ + ValueType result = eValueTypeInvalid; + ValueLocker locker; + lldb::ValueObjectSP value_sp(GetSP(locker)); + if (value_sp) + result = value_sp->GetValueType(); + + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (log) + { + switch (result) + { + case eValueTypeInvalid: log->Printf ("SBValue(%p)::GetValueType () => eValueTypeInvalid", value_sp.get()); break; + case eValueTypeVariableGlobal: log->Printf ("SBValue(%p)::GetValueType () => eValueTypeVariableGlobal", value_sp.get()); break; + case eValueTypeVariableStatic: log->Printf ("SBValue(%p)::GetValueType () => eValueTypeVariableStatic", value_sp.get()); break; + case eValueTypeVariableArgument:log->Printf ("SBValue(%p)::GetValueType () => eValueTypeVariableArgument", value_sp.get()); break; + case eValueTypeVariableLocal: log->Printf ("SBValue(%p)::GetValueType () => eValueTypeVariableLocal", value_sp.get()); break; + case eValueTypeRegister: log->Printf ("SBValue(%p)::GetValueType () => eValueTypeRegister", value_sp.get()); break; + case eValueTypeRegisterSet: log->Printf ("SBValue(%p)::GetValueType () => eValueTypeRegisterSet", value_sp.get()); break; + case eValueTypeConstResult: log->Printf ("SBValue(%p)::GetValueType () => eValueTypeConstResult", value_sp.get()); break; + } + } + return result; +} + +const char * +SBValue::GetObjectDescription () +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + const char *cstr = NULL; + ValueLocker locker; + lldb::ValueObjectSP value_sp(GetSP(locker)); + if (value_sp) + { + cstr = value_sp->GetObjectDescription (); + } + if (log) + { + if (cstr) + log->Printf ("SBValue(%p)::GetObjectDescription() => \"%s\"", value_sp.get(), cstr); + else + log->Printf ("SBValue(%p)::GetObjectDescription() => NULL", value_sp.get()); + } + return cstr; +} + +SBType +SBValue::GetType() +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + SBType sb_type; + ValueLocker locker; + lldb::ValueObjectSP value_sp(GetSP(locker)); + TypeImplSP type_sp; + if (value_sp) + { + type_sp.reset (new TypeImpl(value_sp->GetClangType())); + sb_type.SetSP(type_sp); + } + if (log) + { + if (type_sp) + log->Printf ("SBValue(%p)::GetType => SBType(%p)", value_sp.get(), type_sp.get()); + else + log->Printf ("SBValue(%p)::GetType => NULL", value_sp.get()); + } + return sb_type; +} + +bool +SBValue::GetValueDidChange () +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + bool result = false; + ValueLocker locker; + lldb::ValueObjectSP value_sp(GetSP(locker)); + if (value_sp) + { + result = value_sp->GetValueDidChange (); + } + if (log) + log->Printf ("SBValue(%p)::GetValueDidChange() => %i", value_sp.get(), result); + + return result; +} + +#ifndef LLDB_DISABLE_PYTHON +const char * +SBValue::GetSummary () +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + const char *cstr = NULL; + ValueLocker locker; + lldb::ValueObjectSP value_sp(GetSP(locker)); + if (value_sp) + { + cstr = value_sp->GetSummaryAsCString(); + } + if (log) + { + if (cstr) + log->Printf ("SBValue(%p)::GetSummary() => \"%s\"", value_sp.get(), cstr); + else + log->Printf ("SBValue(%p)::GetSummary() => NULL", value_sp.get()); + } + return cstr; +} +#endif // LLDB_DISABLE_PYTHON + +const char * +SBValue::GetLocation () +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + const char *cstr = NULL; + ValueLocker locker; + lldb::ValueObjectSP value_sp(GetSP(locker)); + if (value_sp) + { + cstr = value_sp->GetLocationAsCString(); + } + if (log) + { + if (cstr) + log->Printf ("SBValue(%p)::GetLocation() => \"%s\"", value_sp.get(), cstr); + else + log->Printf ("SBValue(%p)::GetLocation() => NULL", value_sp.get()); + } + return cstr; +} + +// Deprecated - use the one that takes an lldb::SBError +bool +SBValue::SetValueFromCString (const char *value_str) +{ + lldb::SBError dummy; + return SetValueFromCString(value_str,dummy); +} + +bool +SBValue::SetValueFromCString (const char *value_str, lldb::SBError& error) +{ + bool success = false; + ValueLocker locker; + lldb::ValueObjectSP value_sp(GetSP(locker)); + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (value_sp) + { + success = value_sp->SetValueFromCString (value_str,error.ref()); + } + else + error.SetErrorStringWithFormat ("Could not get value: %s", locker.GetError().AsCString()); + + if (log) + log->Printf ("SBValue(%p)::SetValueFromCString(\"%s\") => %i", value_sp.get(), value_str, success); + + return success; +} + +lldb::SBTypeFormat +SBValue::GetTypeFormat () +{ + lldb::SBTypeFormat format; + ValueLocker locker; + lldb::ValueObjectSP value_sp(GetSP(locker)); + if (value_sp) + { + if (value_sp->UpdateValueIfNeeded(true)) + { + lldb::TypeFormatImplSP format_sp = value_sp->GetValueFormat(); + if (format_sp) + format.SetSP(format_sp); + } + } + return format; +} + +#ifndef LLDB_DISABLE_PYTHON +lldb::SBTypeSummary +SBValue::GetTypeSummary () +{ + lldb::SBTypeSummary summary; + ValueLocker locker; + lldb::ValueObjectSP value_sp(GetSP(locker)); + if (value_sp) + { + if (value_sp->UpdateValueIfNeeded(true)) + { + lldb::TypeSummaryImplSP summary_sp = value_sp->GetSummaryFormat(); + if (summary_sp) + summary.SetSP(summary_sp); + } + } + return summary; +} +#endif // LLDB_DISABLE_PYTHON + +lldb::SBTypeFilter +SBValue::GetTypeFilter () +{ + lldb::SBTypeFilter filter; + ValueLocker locker; + lldb::ValueObjectSP value_sp(GetSP(locker)); + if (value_sp) + { + if (value_sp->UpdateValueIfNeeded(true)) + { + lldb::SyntheticChildrenSP synthetic_sp = value_sp->GetSyntheticChildren(); + + if (synthetic_sp && !synthetic_sp->IsScripted()) + { + TypeFilterImplSP filter_sp = std::static_pointer_cast(synthetic_sp); + filter.SetSP(filter_sp); + } + } + } + return filter; +} + +#ifndef LLDB_DISABLE_PYTHON +lldb::SBTypeSynthetic +SBValue::GetTypeSynthetic () +{ + lldb::SBTypeSynthetic synthetic; + ValueLocker locker; + lldb::ValueObjectSP value_sp(GetSP(locker)); + if (value_sp) + { + if (value_sp->UpdateValueIfNeeded(true)) + { + lldb::SyntheticChildrenSP children_sp = value_sp->GetSyntheticChildren(); + + if (children_sp && children_sp->IsScripted()) + { + ScriptedSyntheticChildrenSP synth_sp = std::static_pointer_cast(children_sp); + synthetic.SetSP(synth_sp); + } + } + } + return synthetic; +} +#endif + +lldb::SBValue +SBValue::CreateChildAtOffset (const char *name, uint32_t offset, SBType type) +{ + lldb::SBValue sb_value; + ValueLocker locker; + lldb::ValueObjectSP value_sp(GetSP(locker)); + lldb::ValueObjectSP new_value_sp; + if (value_sp) + { + TypeImplSP type_sp (type.GetSP()); + if (type.IsValid()) + { + sb_value.SetSP(value_sp->GetSyntheticChildAtOffset(offset, type_sp->GetClangASTType(), true),GetPreferDynamicValue(),GetPreferSyntheticValue(), name); + } + } + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (log) + { + if (new_value_sp) + log->Printf ("SBValue(%p)::CreateChildAtOffset => \"%s\"", + value_sp.get(), + new_value_sp->GetName().AsCString()); + else + log->Printf ("SBValue(%p)::CreateChildAtOffset => NULL", + value_sp.get()); + } + return sb_value; +} + +lldb::SBValue +SBValue::Cast (SBType type) +{ + lldb::SBValue sb_value; + ValueLocker locker; + lldb::ValueObjectSP value_sp(GetSP(locker)); + TypeImplSP type_sp (type.GetSP()); + if (value_sp && type_sp) + sb_value.SetSP(value_sp->Cast(type_sp->GetClangASTType()),GetPreferDynamicValue(),GetPreferSyntheticValue()); + return sb_value; +} + +lldb::SBValue +SBValue::CreateValueFromExpression (const char *name, const char* expression) +{ + SBExpressionOptions options; + options.ref().SetKeepInMemory(true); + return CreateValueFromExpression (name, expression, options); +} + +lldb::SBValue +SBValue::CreateValueFromExpression (const char *name, const char *expression, SBExpressionOptions &options) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + lldb::SBValue sb_value; + ValueLocker locker; + lldb::ValueObjectSP value_sp(GetSP(locker)); + lldb::ValueObjectSP new_value_sp; + if (value_sp) + { + ExecutionContext exe_ctx (value_sp->GetExecutionContextRef()); + Target* target = exe_ctx.GetTargetPtr(); + if (target) + { + options.ref().SetKeepInMemory(true); + target->EvaluateExpression (expression, + exe_ctx.GetFramePtr(), + new_value_sp, + options.ref()); + if (new_value_sp) + { + new_value_sp->SetName(ConstString(name)); + sb_value.SetSP(new_value_sp); + } + } + } + if (log) + { + if (new_value_sp) + log->Printf ("SBValue(%p)::CreateValueFromExpression(name=\"%s\", expression=\"%s\") => SBValue (%p)", + value_sp.get(), + name, + expression, + new_value_sp.get()); + else + log->Printf ("SBValue(%p)::CreateValueFromExpression(name=\"%s\", expression=\"%s\") => NULL", + value_sp.get(), + name, + expression); + } + return sb_value; +} + +lldb::SBValue +SBValue::CreateValueFromAddress(const char* name, lldb::addr_t address, SBType sb_type) +{ + lldb::SBValue sb_value; + ValueLocker locker; + lldb::ValueObjectSP value_sp(GetSP(locker)); + lldb::ValueObjectSP new_value_sp; + lldb::TypeImplSP type_impl_sp (sb_type.GetSP()); + if (value_sp && type_impl_sp) + { + ClangASTType pointee_ast_type(type_impl_sp->GetClangASTType().GetPointerType ()); + if (pointee_ast_type) + { + lldb::DataBufferSP buffer(new lldb_private::DataBufferHeap(&address,sizeof(lldb::addr_t))); + + ExecutionContext exe_ctx (value_sp->GetExecutionContextRef()); + ValueObjectSP ptr_result_valobj_sp(ValueObjectConstResult::Create (exe_ctx.GetBestExecutionContextScope(), + pointee_ast_type, + ConstString(name), + buffer, + lldb::endian::InlHostByteOrder(), + exe_ctx.GetAddressByteSize())); + + if (ptr_result_valobj_sp) + { + ptr_result_valobj_sp->GetValue().SetValueType(Value::eValueTypeLoadAddress); + Error err; + new_value_sp = ptr_result_valobj_sp->Dereference(err); + if (new_value_sp) + new_value_sp->SetName(ConstString(name)); + } + sb_value.SetSP(new_value_sp); + } + } + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (log) + { + if (new_value_sp) + log->Printf ("SBValue(%p)::CreateValueFromAddress => \"%s\"", value_sp.get(), new_value_sp->GetName().AsCString()); + else + log->Printf ("SBValue(%p)::CreateValueFromAddress => NULL", value_sp.get()); + } + return sb_value; +} + +lldb::SBValue +SBValue::CreateValueFromData (const char* name, SBData data, SBType type) +{ + lldb::SBValue sb_value; + lldb::ValueObjectSP new_value_sp; + ValueLocker locker; + lldb::ValueObjectSP value_sp(GetSP(locker)); + if (value_sp) + { + ExecutionContext exe_ctx (value_sp->GetExecutionContextRef()); + + new_value_sp = ValueObjectConstResult::Create (exe_ctx.GetBestExecutionContextScope(), + type.m_opaque_sp->GetClangASTType(), + ConstString(name), + *data.m_opaque_sp, + LLDB_INVALID_ADDRESS); + new_value_sp->SetAddressTypeOfChildren(eAddressTypeLoad); + sb_value.SetSP(new_value_sp); + } + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (log) + { + if (new_value_sp) + log->Printf ("SBValue(%p)::CreateValueFromData => \"%s\"", value_sp.get(), new_value_sp->GetName().AsCString()); + else + log->Printf ("SBValue(%p)::CreateValueFromData => NULL", value_sp.get()); + } + return sb_value; +} + +SBValue +SBValue::GetChildAtIndex (uint32_t idx) +{ + const bool can_create_synthetic = false; + lldb::DynamicValueType use_dynamic = eNoDynamicValues; + TargetSP target_sp; + if (m_opaque_sp) + target_sp = m_opaque_sp->GetTargetSP(); + + if (target_sp) + use_dynamic = target_sp->GetPreferDynamicValue(); + + return GetChildAtIndex (idx, use_dynamic, can_create_synthetic); +} + +SBValue +SBValue::GetChildAtIndex (uint32_t idx, lldb::DynamicValueType use_dynamic, bool can_create_synthetic) +{ + lldb::ValueObjectSP child_sp; + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + ValueLocker locker; + lldb::ValueObjectSP value_sp(GetSP(locker)); + if (value_sp) + { + const bool can_create = true; + child_sp = value_sp->GetChildAtIndex (idx, can_create); + if (can_create_synthetic && !child_sp) + { + if (value_sp->IsPointerType()) + { + child_sp = value_sp->GetSyntheticArrayMemberFromPointer(idx, can_create); + } + else if (value_sp->IsArrayType()) + { + child_sp = value_sp->GetSyntheticArrayMemberFromArray(idx, can_create); + } + } + } + + SBValue sb_value; + sb_value.SetSP (child_sp, use_dynamic, GetPreferSyntheticValue()); + if (log) + log->Printf ("SBValue(%p)::GetChildAtIndex (%u) => SBValue(%p)", value_sp.get(), idx, value_sp.get()); + + return sb_value; +} + +uint32_t +SBValue::GetIndexOfChildWithName (const char *name) +{ + uint32_t idx = UINT32_MAX; + ValueLocker locker; + lldb::ValueObjectSP value_sp(GetSP(locker)); + if (value_sp) + { + idx = value_sp->GetIndexOfChildWithName (ConstString(name)); + } + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (log) + { + if (idx == UINT32_MAX) + log->Printf ("SBValue(%p)::GetIndexOfChildWithName (name=\"%s\") => NOT FOUND", value_sp.get(), name); + else + log->Printf ("SBValue(%p)::GetIndexOfChildWithName (name=\"%s\") => %u", value_sp.get(), name, idx); + } + return idx; +} + +SBValue +SBValue::GetChildMemberWithName (const char *name) +{ + lldb::DynamicValueType use_dynamic_value = eNoDynamicValues; + TargetSP target_sp; + if (m_opaque_sp) + target_sp = m_opaque_sp->GetTargetSP(); + + if (target_sp) + use_dynamic_value = target_sp->GetPreferDynamicValue(); + return GetChildMemberWithName (name, use_dynamic_value); +} + +SBValue +SBValue::GetChildMemberWithName (const char *name, lldb::DynamicValueType use_dynamic_value) +{ + lldb::ValueObjectSP child_sp; + const ConstString str_name (name); + + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + ValueLocker locker; + lldb::ValueObjectSP value_sp(GetSP(locker)); + if (value_sp) + { + child_sp = value_sp->GetChildMemberWithName (str_name, true); + } + + SBValue sb_value; + sb_value.SetSP(child_sp, use_dynamic_value, GetPreferSyntheticValue()); + + if (log) + log->Printf ("SBValue(%p)::GetChildMemberWithName (name=\"%s\") => SBValue(%p)", value_sp.get(), name, value_sp.get()); + + return sb_value; +} + +lldb::SBValue +SBValue::GetDynamicValue (lldb::DynamicValueType use_dynamic) +{ + SBValue value_sb; + if (IsValid()) + { + ValueImplSP proxy_sp(new ValueImpl(m_opaque_sp->GetRootSP(),use_dynamic,m_opaque_sp->GetUseSynthetic())); + value_sb.SetSP(proxy_sp); + } + return value_sb; +} + +lldb::SBValue +SBValue::GetStaticValue () +{ + SBValue value_sb; + if (IsValid()) + { + ValueImplSP proxy_sp(new ValueImpl(m_opaque_sp->GetRootSP(),eNoDynamicValues,m_opaque_sp->GetUseSynthetic())); + value_sb.SetSP(proxy_sp); + } + return value_sb; +} + +lldb::SBValue +SBValue::GetNonSyntheticValue () +{ + SBValue value_sb; + if (IsValid()) + { + ValueImplSP proxy_sp(new ValueImpl(m_opaque_sp->GetRootSP(),m_opaque_sp->GetUseDynamic(),false)); + value_sb.SetSP(proxy_sp); + } + return value_sb; +} + +lldb::DynamicValueType +SBValue::GetPreferDynamicValue () +{ + if (!IsValid()) + return eNoDynamicValues; + return m_opaque_sp->GetUseDynamic(); +} + +void +SBValue::SetPreferDynamicValue (lldb::DynamicValueType use_dynamic) +{ + if (IsValid()) + return m_opaque_sp->SetUseDynamic (use_dynamic); +} + +bool +SBValue::GetPreferSyntheticValue () +{ + if (!IsValid()) + return false; + return m_opaque_sp->GetUseSynthetic(); +} + +void +SBValue::SetPreferSyntheticValue (bool use_synthetic) +{ + if (IsValid()) + return m_opaque_sp->SetUseSynthetic (use_synthetic); +} + +bool +SBValue::IsDynamic() +{ + ValueLocker locker; + lldb::ValueObjectSP value_sp(GetSP(locker)); + if (value_sp) + return value_sp->IsDynamic(); + return false; +} + +bool +SBValue::IsSynthetic () +{ + ValueLocker locker; + lldb::ValueObjectSP value_sp(GetSP(locker)); + if (value_sp) + return value_sp->IsSynthetic(); + return false; +} + +lldb::SBValue +SBValue::GetValueForExpressionPath(const char* expr_path) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + lldb::ValueObjectSP child_sp; + ValueLocker locker; + lldb::ValueObjectSP value_sp(GetSP(locker)); + if (value_sp) + { + // using default values for all the fancy options, just do it if you can + child_sp = value_sp->GetValueForExpressionPath(expr_path); + } + + SBValue sb_value; + sb_value.SetSP(child_sp,GetPreferDynamicValue(),GetPreferSyntheticValue()); + + if (log) + log->Printf ("SBValue(%p)::GetValueForExpressionPath (expr_path=\"%s\") => SBValue(%p)", value_sp.get(), expr_path, value_sp.get()); + + return sb_value; +} + +int64_t +SBValue::GetValueAsSigned(SBError& error, int64_t fail_value) +{ + error.Clear(); + ValueLocker locker; + lldb::ValueObjectSP value_sp(GetSP(locker)); + if (value_sp) + { + Scalar scalar; + if (value_sp->ResolveValue (scalar)) + return scalar.SLongLong (fail_value); + else + error.SetErrorString ("could not resolve value"); + } + else + error.SetErrorStringWithFormat ("could not get SBValue: %s", locker.GetError().AsCString()); + + return fail_value; +} + +uint64_t +SBValue::GetValueAsUnsigned(SBError& error, uint64_t fail_value) +{ + error.Clear(); + ValueLocker locker; + lldb::ValueObjectSP value_sp(GetSP(locker)); + if (value_sp) + { + Scalar scalar; + if (value_sp->ResolveValue (scalar)) + return scalar.ULongLong(fail_value); + else + error.SetErrorString("could not resolve value"); + } + else + error.SetErrorStringWithFormat ("could not get SBValue: %s", locker.GetError().AsCString()); + + return fail_value; +} + +int64_t +SBValue::GetValueAsSigned(int64_t fail_value) +{ + ValueLocker locker; + lldb::ValueObjectSP value_sp(GetSP(locker)); + if (value_sp) + { + Scalar scalar; + if (value_sp->ResolveValue (scalar)) + return scalar.SLongLong(fail_value); + } + return fail_value; +} + +uint64_t +SBValue::GetValueAsUnsigned(uint64_t fail_value) +{ + ValueLocker locker; + lldb::ValueObjectSP value_sp(GetSP(locker)); + if (value_sp) + { + Scalar scalar; + if (value_sp->ResolveValue (scalar)) + return scalar.ULongLong(fail_value); + } + return fail_value; +} + +bool +SBValue::MightHaveChildren () +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + bool has_children = false; + ValueLocker locker; + lldb::ValueObjectSP value_sp(GetSP(locker)); + if (value_sp) + has_children = value_sp->MightHaveChildren(); + + if (log) + log->Printf ("SBValue(%p)::MightHaveChildren() => %i", value_sp.get(), has_children); + return has_children; +} + +uint32_t +SBValue::GetNumChildren () +{ + uint32_t num_children = 0; + + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + ValueLocker locker; + lldb::ValueObjectSP value_sp(GetSP(locker)); + if (value_sp) + num_children = value_sp->GetNumChildren(); + + if (log) + log->Printf ("SBValue(%p)::GetNumChildren () => %u", value_sp.get(), num_children); + + return num_children; +} + + +SBValue +SBValue::Dereference () +{ + SBValue sb_value; + ValueLocker locker; + lldb::ValueObjectSP value_sp(GetSP(locker)); + if (value_sp) + { + Error error; + sb_value = value_sp->Dereference (error); + } + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (log) + log->Printf ("SBValue(%p)::Dereference () => SBValue(%p)", value_sp.get(), value_sp.get()); + + return sb_value; +} + +bool +SBValue::TypeIsPointerType () +{ + bool is_ptr_type = false; + + ValueLocker locker; + lldb::ValueObjectSP value_sp(GetSP(locker)); + if (value_sp) + is_ptr_type = value_sp->IsPointerType(); + + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (log) + log->Printf ("SBValue(%p)::TypeIsPointerType () => %i", value_sp.get(), is_ptr_type); + + + return is_ptr_type; +} + +void * +SBValue::GetOpaqueType() +{ + ValueLocker locker; + lldb::ValueObjectSP value_sp(GetSP(locker)); + if (value_sp) + return value_sp->GetClangType().GetOpaqueQualType(); + return NULL; +} + +lldb::SBTarget +SBValue::GetTarget() +{ + SBTarget sb_target; + TargetSP target_sp; + if (m_opaque_sp) + { + target_sp = m_opaque_sp->GetTargetSP(); + sb_target.SetSP (target_sp); + } + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (log) + { + if (target_sp.get() == NULL) + log->Printf ("SBValue(%p)::GetTarget () => NULL", m_opaque_sp.get()); + else + log->Printf ("SBValue(%p)::GetTarget () => %p", m_opaque_sp.get(), target_sp.get()); + } + return sb_target; +} + +lldb::SBProcess +SBValue::GetProcess() +{ + SBProcess sb_process; + ProcessSP process_sp; + if (m_opaque_sp) + { + process_sp = m_opaque_sp->GetProcessSP(); + sb_process.SetSP (process_sp); + } + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (log) + { + if (process_sp.get() == NULL) + log->Printf ("SBValue(%p)::GetProcess () => NULL", m_opaque_sp.get()); + else + log->Printf ("SBValue(%p)::GetProcess () => %p", m_opaque_sp.get(), process_sp.get()); + } + return sb_process; +} + +lldb::SBThread +SBValue::GetThread() +{ + SBThread sb_thread; + ThreadSP thread_sp; + if (m_opaque_sp) + { + thread_sp = m_opaque_sp->GetThreadSP(); + sb_thread.SetThread(thread_sp); + } + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (log) + { + if (thread_sp.get() == NULL) + log->Printf ("SBValue(%p)::GetThread () => NULL", m_opaque_sp.get()); + else + log->Printf ("SBValue(%p)::GetThread () => %p", m_opaque_sp.get(), thread_sp.get()); + } + return sb_thread; +} + +lldb::SBFrame +SBValue::GetFrame() +{ + SBFrame sb_frame; + StackFrameSP frame_sp; + if (m_opaque_sp) + { + frame_sp = m_opaque_sp->GetFrameSP(); + sb_frame.SetFrameSP (frame_sp); + } + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (log) + { + if (frame_sp.get() == NULL) + log->Printf ("SBValue(%p)::GetFrame () => NULL", m_opaque_sp.get()); + else + log->Printf ("SBValue(%p)::GetFrame () => %p", m_opaque_sp.get(), frame_sp.get()); + } + return sb_frame; +} + + +lldb::ValueObjectSP +SBValue::GetSP (ValueLocker &locker) const +{ + if (!m_opaque_sp || !m_opaque_sp->IsValid()) + return ValueObjectSP(); + return locker.GetLockedSP(*m_opaque_sp.get()); +} + +lldb::ValueObjectSP +SBValue::GetSP () const +{ + ValueLocker locker; + return GetSP(locker); +} + +void +SBValue::SetSP (ValueImplSP impl_sp) +{ + m_opaque_sp = impl_sp; +} + +void +SBValue::SetSP (const lldb::ValueObjectSP &sp) +{ + if (sp) + { + lldb::TargetSP target_sp(sp->GetTargetSP()); + if (target_sp) + { + lldb::DynamicValueType use_dynamic = target_sp->GetPreferDynamicValue(); + bool use_synthetic = target_sp->TargetProperties::GetEnableSyntheticValue(); + m_opaque_sp = ValueImplSP(new ValueImpl(sp, use_dynamic, use_synthetic)); + } + else + m_opaque_sp = ValueImplSP(new ValueImpl(sp,eNoDynamicValues,true)); + } + else + m_opaque_sp = ValueImplSP(new ValueImpl(sp,eNoDynamicValues,false)); +} + +void +SBValue::SetSP (const lldb::ValueObjectSP &sp, lldb::DynamicValueType use_dynamic) +{ + if (sp) + { + lldb::TargetSP target_sp(sp->GetTargetSP()); + if (target_sp) + { + bool use_synthetic = target_sp->TargetProperties::GetEnableSyntheticValue(); + SetSP (sp, use_dynamic, use_synthetic); + } + else + SetSP (sp, use_dynamic, true); + } + else + SetSP (sp, use_dynamic, false); +} + +void +SBValue::SetSP (const lldb::ValueObjectSP &sp, bool use_synthetic) +{ + if (sp) + { + lldb::TargetSP target_sp(sp->GetTargetSP()); + if (target_sp) + { + lldb::DynamicValueType use_dynamic = target_sp->GetPreferDynamicValue(); + SetSP (sp, use_dynamic, use_synthetic); + } + else + SetSP (sp, eNoDynamicValues, use_synthetic); + } + else + SetSP (sp, eNoDynamicValues, use_synthetic); +} + +void +SBValue::SetSP (const lldb::ValueObjectSP &sp, lldb::DynamicValueType use_dynamic, bool use_synthetic) +{ + m_opaque_sp = ValueImplSP(new ValueImpl(sp,use_dynamic,use_synthetic)); +} + +void +SBValue::SetSP (const lldb::ValueObjectSP &sp, lldb::DynamicValueType use_dynamic, bool use_synthetic, const char *name) +{ + m_opaque_sp = ValueImplSP(new ValueImpl(sp,use_dynamic,use_synthetic, name)); +} + +bool +SBValue::GetExpressionPath (SBStream &description) +{ + ValueLocker locker; + lldb::ValueObjectSP value_sp(GetSP(locker)); + if (value_sp) + { + value_sp->GetExpressionPath (description.ref(), false); + return true; + } + return false; +} + +bool +SBValue::GetExpressionPath (SBStream &description, bool qualify_cxx_base_classes) +{ + ValueLocker locker; + lldb::ValueObjectSP value_sp(GetSP(locker)); + if (value_sp) + { + value_sp->GetExpressionPath (description.ref(), qualify_cxx_base_classes); + return true; + } + return false; +} + +bool +SBValue::GetDescription (SBStream &description) +{ + Stream &strm = description.ref(); + + ValueLocker locker; + lldb::ValueObjectSP value_sp(GetSP(locker)); + if (value_sp) + { + ValueObject::DumpValueObject (strm, value_sp.get()); + } + else + strm.PutCString ("No value"); + + return true; +} + +lldb::Format +SBValue::GetFormat () +{ + ValueLocker locker; + lldb::ValueObjectSP value_sp(GetSP(locker)); + if (value_sp) + return value_sp->GetFormat(); + return eFormatDefault; +} + +void +SBValue::SetFormat (lldb::Format format) +{ + ValueLocker locker; + lldb::ValueObjectSP value_sp(GetSP(locker)); + if (value_sp) + value_sp->SetFormat(format); +} + +lldb::SBValue +SBValue::AddressOf() +{ + SBValue sb_value; + ValueLocker locker; + lldb::ValueObjectSP value_sp(GetSP(locker)); + if (value_sp) + { + Error error; + sb_value.SetSP(value_sp->AddressOf (error),GetPreferDynamicValue(), GetPreferSyntheticValue()); + } + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (log) + log->Printf ("SBValue(%p)::AddressOf () => SBValue(%p)", value_sp.get(), value_sp.get()); + + return sb_value; +} + +lldb::addr_t +SBValue::GetLoadAddress() +{ + lldb::addr_t value = LLDB_INVALID_ADDRESS; + ValueLocker locker; + lldb::ValueObjectSP value_sp(GetSP(locker)); + if (value_sp) + { + TargetSP target_sp (value_sp->GetTargetSP()); + if (target_sp) + { + const bool scalar_is_load_address = true; + AddressType addr_type; + value = value_sp->GetAddressOf(scalar_is_load_address, &addr_type); + if (addr_type == eAddressTypeFile) + { + ModuleSP module_sp (value_sp->GetModule()); + if (!module_sp) + value = LLDB_INVALID_ADDRESS; + else + { + Address addr; + module_sp->ResolveFileAddress(value, addr); + value = addr.GetLoadAddress(target_sp.get()); + } + } + else if (addr_type == eAddressTypeHost || addr_type == eAddressTypeInvalid) + value = LLDB_INVALID_ADDRESS; + } + } + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (log) + log->Printf ("SBValue(%p)::GetLoadAddress () => (%" PRIu64 ")", value_sp.get(), value); + + return value; +} + +lldb::SBAddress +SBValue::GetAddress() +{ + Address addr; + ValueLocker locker; + lldb::ValueObjectSP value_sp(GetSP(locker)); + if (value_sp) + { + TargetSP target_sp (value_sp->GetTargetSP()); + if (target_sp) + { + lldb::addr_t value = LLDB_INVALID_ADDRESS; + const bool scalar_is_load_address = true; + AddressType addr_type; + value = value_sp->GetAddressOf(scalar_is_load_address, &addr_type); + if (addr_type == eAddressTypeFile) + { + ModuleSP module_sp (value_sp->GetModule()); + if (module_sp) + module_sp->ResolveFileAddress(value, addr); + } + else if (addr_type == eAddressTypeLoad) + { + // no need to check the return value on this.. if it can actually do the resolve + // addr will be in the form (section,offset), otherwise it will simply be returned + // as (NULL, value) + addr.SetLoadAddress(value, target_sp.get()); + } + } + } + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (log) + log->Printf ("SBValue(%p)::GetAddress () => (%s,%" PRIu64 ")", value_sp.get(), + (addr.GetSection() ? addr.GetSection()->GetName().GetCString() : "NULL"), + addr.GetOffset()); + return SBAddress(new Address(addr)); +} + +lldb::SBData +SBValue::GetPointeeData (uint32_t item_idx, + uint32_t item_count) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + lldb::SBData sb_data; + ValueLocker locker; + lldb::ValueObjectSP value_sp(GetSP(locker)); + if (value_sp) + { + TargetSP target_sp (value_sp->GetTargetSP()); + if (target_sp) + { + DataExtractorSP data_sp(new DataExtractor()); + value_sp->GetPointeeData(*data_sp, item_idx, item_count); + if (data_sp->GetByteSize() > 0) + *sb_data = data_sp; + } + } + if (log) + log->Printf ("SBValue(%p)::GetPointeeData (%d, %d) => SBData(%p)", + value_sp.get(), + item_idx, + item_count, + sb_data.get()); + + return sb_data; +} + +lldb::SBData +SBValue::GetData () +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + lldb::SBData sb_data; + ValueLocker locker; + lldb::ValueObjectSP value_sp(GetSP(locker)); + if (value_sp) + { + DataExtractorSP data_sp(new DataExtractor()); + value_sp->GetData(*data_sp); + if (data_sp->GetByteSize() > 0) + *sb_data = data_sp; + } + if (log) + log->Printf ("SBValue(%p)::GetData () => SBData(%p)", + value_sp.get(), + sb_data.get()); + + return sb_data; +} + +bool +SBValue::SetData (lldb::SBData &data, SBError &error) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + ValueLocker locker; + lldb::ValueObjectSP value_sp(GetSP(locker)); + bool ret = true; + + if (value_sp) + { + DataExtractor *data_extractor = data.get(); + + if (!data_extractor) + { + if (log) + log->Printf ("SBValue(%p)::SetData() => error: no data to set", value_sp.get()); + + error.SetErrorString("No data to set"); + ret = false; + } + else + { + Error set_error; + + value_sp->SetData(*data_extractor, set_error); + + if (!set_error.Success()) + { + error.SetErrorStringWithFormat("Couldn't set data: %s", set_error.AsCString()); + ret = false; + } + } + } + else + { + error.SetErrorStringWithFormat ("Couldn't set data: could not get SBValue: %s", locker.GetError().AsCString()); + ret = false; + } + + if (log) + log->Printf ("SBValue(%p)::SetData (%p) => %s", + value_sp.get(), + data.get(), + ret ? "true" : "false"); + return ret; +} + +lldb::SBDeclaration +SBValue::GetDeclaration () +{ + ValueLocker locker; + lldb::ValueObjectSP value_sp(GetSP(locker)); + SBDeclaration decl_sb; + if (value_sp) + { + Declaration decl; + if (value_sp->GetDeclaration(decl)) + decl_sb.SetDeclaration(decl); + } + return decl_sb; +} + +lldb::SBWatchpoint +SBValue::Watch (bool resolve_location, bool read, bool write, SBError &error) +{ + SBWatchpoint sb_watchpoint; + + // If the SBValue is not valid, there's no point in even trying to watch it. + ValueLocker locker; + lldb::ValueObjectSP value_sp(GetSP(locker)); + TargetSP target_sp (GetTarget().GetSP()); + if (value_sp && target_sp) + { + // Read and Write cannot both be false. + if (!read && !write) + return sb_watchpoint; + + // If the value is not in scope, don't try and watch and invalid value + if (!IsInScope()) + return sb_watchpoint; + + addr_t addr = GetLoadAddress(); + if (addr == LLDB_INVALID_ADDRESS) + return sb_watchpoint; + size_t byte_size = GetByteSize(); + if (byte_size == 0) + return sb_watchpoint; + + uint32_t watch_type = 0; + if (read) + watch_type |= LLDB_WATCH_TYPE_READ; + if (write) + watch_type |= LLDB_WATCH_TYPE_WRITE; + + Error rc; + ClangASTType type (value_sp->GetClangType()); + WatchpointSP watchpoint_sp = target_sp->CreateWatchpoint(addr, byte_size, &type, watch_type, rc); + error.SetError(rc); + + if (watchpoint_sp) + { + sb_watchpoint.SetSP (watchpoint_sp); + Declaration decl; + if (value_sp->GetDeclaration (decl)) + { + if (decl.GetFile()) + { + StreamString ss; + // True to show fullpath for declaration file. + decl.DumpStopContext(&ss, true); + watchpoint_sp->SetDeclInfo(ss.GetString()); + } + } + } + } + else if (target_sp) + { + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (log) + log->Printf ("SBValue(%p)::Watch() => error getting SBValue: %s", value_sp.get(), locker.GetError().AsCString()); + + error.SetErrorStringWithFormat("could not get SBValue: %s", locker.GetError().AsCString()); + } + else + { + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (log) + log->Printf ("SBValue(%p)::Watch() => error getting SBValue: no target", value_sp.get()); + error.SetErrorString("could not set watchpoint, a target is required"); + } + + return sb_watchpoint; +} + +// FIXME: Remove this method impl (as well as the decl in .h) once it is no longer needed. +// Backward compatibility fix in the interim. +lldb::SBWatchpoint +SBValue::Watch (bool resolve_location, bool read, bool write) +{ + SBError error; + return Watch(resolve_location, read, write, error); +} + +lldb::SBWatchpoint +SBValue::WatchPointee (bool resolve_location, bool read, bool write, SBError &error) +{ + SBWatchpoint sb_watchpoint; + if (IsInScope() && GetType().IsPointerType()) + sb_watchpoint = Dereference().Watch (resolve_location, read, write, error); + return sb_watchpoint; +} diff --git a/contrib/llvm/tools/lldb/source/API/SBValueList.cpp b/contrib/llvm/tools/lldb/source/API/SBValueList.cpp new file mode 100644 index 00000000000..46866eb3742 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/API/SBValueList.cpp @@ -0,0 +1,275 @@ +//===-- SBValueList.cpp -----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + + +#include "lldb/API/SBValueList.h" +#include "lldb/API/SBValue.h" +#include "lldb/API/SBStream.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/ValueObjectList.h" + +#include + +using namespace lldb; +using namespace lldb_private; + +class ValueListImpl +{ +public: + ValueListImpl () : + m_values() + { + } + + ValueListImpl (const ValueListImpl& rhs) : + m_values(rhs.m_values) + { + } + + ValueListImpl& + operator = (const ValueListImpl& rhs) + { + if (this == &rhs) + return *this; + m_values = rhs.m_values; + return *this; + }; + + uint32_t + GetSize () + { + return m_values.size(); + } + + void + Append (const lldb::SBValue& sb_value) + { + m_values.push_back(sb_value); + } + + void + Append (const ValueListImpl& list) + { + for (auto val : list.m_values) + Append (val); + } + + lldb::SBValue + GetValueAtIndex (uint32_t index) + { + if (index >= GetSize()) + return lldb::SBValue(); + return m_values[index]; + } + + lldb::SBValue + FindValueByUID (lldb::user_id_t uid) + { + for (auto val : m_values) + { + if (val.IsValid() && val.GetID() == uid) + return val; + } + return lldb::SBValue(); + } + +private: + std::vector m_values; +}; + +SBValueList::SBValueList () : + m_opaque_ap () +{ +} + +SBValueList::SBValueList (const SBValueList &rhs) : + m_opaque_ap () +{ + Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + if (rhs.IsValid()) + m_opaque_ap.reset (new ValueListImpl (*rhs)); + + if (log) + { + log->Printf ("SBValueList::SBValueList (rhs.ap=%p) => this.ap = %p", + (rhs.IsValid() ? rhs.m_opaque_ap.get() : NULL), + m_opaque_ap.get()); + } +} + +SBValueList::SBValueList (const ValueListImpl *lldb_object_ptr) : + m_opaque_ap () +{ + Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + if (lldb_object_ptr) + m_opaque_ap.reset (new ValueListImpl (*lldb_object_ptr)); + + if (log) + { + log->Printf ("SBValueList::SBValueList (lldb_object_ptr=%p) => this.ap = %p", + lldb_object_ptr, + m_opaque_ap.get()); + } +} + +SBValueList::~SBValueList () +{ +} + +bool +SBValueList::IsValid () const +{ + return (m_opaque_ap.get() != NULL); +} + +void +SBValueList::Clear() +{ + m_opaque_ap.reset(); +} + +const SBValueList & +SBValueList::operator = (const SBValueList &rhs) +{ + if (this != &rhs) + { + if (rhs.IsValid()) + m_opaque_ap.reset (new ValueListImpl (*rhs)); + else + m_opaque_ap.reset (); + } + return *this; +} + +ValueListImpl * +SBValueList::operator->() +{ + return m_opaque_ap.get(); +} + +ValueListImpl & +SBValueList::operator*() +{ + return *m_opaque_ap; +} + +const ValueListImpl * +SBValueList::operator->() const +{ + return m_opaque_ap.get(); +} + +const ValueListImpl & +SBValueList::operator*() const +{ + return *m_opaque_ap; +} + +void +SBValueList::Append (const SBValue &val_obj) +{ + CreateIfNeeded (); + m_opaque_ap->Append (val_obj); +} + +void +SBValueList::Append (lldb::ValueObjectSP& val_obj_sp) +{ + if (val_obj_sp) + { + CreateIfNeeded (); + m_opaque_ap->Append (SBValue(val_obj_sp)); + } +} + +void +SBValueList::Append (const lldb::SBValueList& value_list) +{ + if (value_list.IsValid()) + { + CreateIfNeeded (); + m_opaque_ap->Append (*value_list); + } +} + + +SBValue +SBValueList::GetValueAtIndex (uint32_t idx) const +{ + Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + //if (log) + // log->Printf ("SBValueList::GetValueAtIndex (uint32_t idx) idx = %d", idx); + + SBValue sb_value; + if (m_opaque_ap.get()) + sb_value = m_opaque_ap->GetValueAtIndex (idx); + + if (log) + { + SBStream sstr; + sb_value.GetDescription (sstr); + log->Printf ("SBValueList::GetValueAtIndex (this.ap=%p, idx=%d) => SBValue (this.sp = %p, '%s')", + m_opaque_ap.get(), idx, sb_value.GetSP().get(), sstr.GetData()); + } + + return sb_value; +} + +uint32_t +SBValueList::GetSize () const +{ + Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + //if (log) + // log->Printf ("SBValueList::GetSize ()"); + + uint32_t size = 0; + if (m_opaque_ap.get()) + size = m_opaque_ap->GetSize(); + + if (log) + log->Printf ("SBValueList::GetSize (this.ap=%p) => %d", m_opaque_ap.get(), size); + + return size; +} + +void +SBValueList::CreateIfNeeded () +{ + if (m_opaque_ap.get() == NULL) + m_opaque_ap.reset (new ValueListImpl()); +} + + +SBValue +SBValueList::FindValueObjectByUID (lldb::user_id_t uid) +{ + SBValue sb_value; + if (m_opaque_ap.get()) + sb_value = m_opaque_ap->FindValueByUID(uid); + return sb_value; +} + +void * +SBValueList::opaque_ptr () +{ + return m_opaque_ap.get(); +} + +ValueListImpl & +SBValueList::ref () +{ + CreateIfNeeded(); + return *m_opaque_ap.get(); +} + + diff --git a/contrib/llvm/tools/lldb/source/API/SBWatchpoint.cpp b/contrib/llvm/tools/lldb/source/API/SBWatchpoint.cpp new file mode 100644 index 00000000000..194695c31d5 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/API/SBWatchpoint.cpp @@ -0,0 +1,298 @@ +//===-- SBWatchpoint.cpp --------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/API/SBWatchpoint.h" +#include "lldb/API/SBDefines.h" +#include "lldb/API/SBAddress.h" +#include "lldb/API/SBDebugger.h" +#include "lldb/API/SBEvent.h" +#include "lldb/API/SBStream.h" + +#include "lldb/lldb-types.h" +#include "lldb/lldb-defines.h" +#include "lldb/Breakpoint/Watchpoint.h" +#include "lldb/Breakpoint/WatchpointList.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Stream.h" +#include "lldb/Core/StreamFile.h" +#include "lldb/Target/Target.h" + +using namespace lldb; +using namespace lldb_private; + + +SBWatchpoint::SBWatchpoint () : + m_opaque_sp () +{ +} + +SBWatchpoint::SBWatchpoint (const lldb::WatchpointSP &wp_sp) : + m_opaque_sp (wp_sp) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + if (log) + { + SBStream sstr; + GetDescription (sstr, lldb::eDescriptionLevelBrief); + log->Printf ("SBWatchpoint::SBWatchpoint (const lldb::WatchpointSP &wp_sp" + "=%p) => this.sp = %p (%s)", wp_sp.get(), m_opaque_sp.get(), sstr.GetData()); + } +} + +SBWatchpoint::SBWatchpoint(const SBWatchpoint &rhs) : + m_opaque_sp (rhs.m_opaque_sp) +{ +} + +const SBWatchpoint & +SBWatchpoint::operator = (const SBWatchpoint &rhs) +{ + if (this != &rhs) + m_opaque_sp = rhs.m_opaque_sp; + return *this; +} + + +SBWatchpoint::~SBWatchpoint () +{ +} + +watch_id_t +SBWatchpoint::GetID () +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + watch_id_t watch_id = LLDB_INVALID_WATCH_ID; + lldb::WatchpointSP watchpoint_sp(GetSP()); + if (watchpoint_sp) + watch_id = watchpoint_sp->GetID(); + + if (log) + { + if (watch_id == LLDB_INVALID_WATCH_ID) + log->Printf ("SBWatchpoint(%p)::GetID () => LLDB_INVALID_WATCH_ID", watchpoint_sp.get()); + else + log->Printf ("SBWatchpoint(%p)::GetID () => %u", watchpoint_sp.get(), watch_id); + } + + return watch_id; +} + +bool +SBWatchpoint::IsValid() const +{ + return (bool) m_opaque_sp; +} + +SBError +SBWatchpoint::GetError () +{ + SBError sb_error; + lldb::WatchpointSP watchpoint_sp(GetSP()); + if (watchpoint_sp) + { + sb_error.SetError(watchpoint_sp->GetError()); + } + return sb_error; +} + +int32_t +SBWatchpoint::GetHardwareIndex () +{ + int32_t hw_index = -1; + + lldb::WatchpointSP watchpoint_sp(GetSP()); + if (watchpoint_sp) + { + Mutex::Locker api_locker (watchpoint_sp->GetTarget().GetAPIMutex()); + hw_index = watchpoint_sp->GetHardwareIndex(); + } + + return hw_index; +} + +addr_t +SBWatchpoint::GetWatchAddress () +{ + addr_t ret_addr = LLDB_INVALID_ADDRESS; + + lldb::WatchpointSP watchpoint_sp(GetSP()); + if (watchpoint_sp) + { + Mutex::Locker api_locker (watchpoint_sp->GetTarget().GetAPIMutex()); + ret_addr = watchpoint_sp->GetLoadAddress(); + } + + return ret_addr; +} + +size_t +SBWatchpoint::GetWatchSize () +{ + size_t watch_size = 0; + + lldb::WatchpointSP watchpoint_sp(GetSP()); + if (watchpoint_sp) + { + Mutex::Locker api_locker (watchpoint_sp->GetTarget().GetAPIMutex()); + watch_size = watchpoint_sp->GetByteSize(); + } + + return watch_size; +} + +void +SBWatchpoint::SetEnabled (bool enabled) +{ + lldb::WatchpointSP watchpoint_sp(GetSP()); + if (watchpoint_sp) + { + Mutex::Locker api_locker (watchpoint_sp->GetTarget().GetAPIMutex()); + watchpoint_sp->GetTarget().DisableWatchpointByID(watchpoint_sp->GetID()); + } +} + +bool +SBWatchpoint::IsEnabled () +{ + lldb::WatchpointSP watchpoint_sp(GetSP()); + if (watchpoint_sp) + { + Mutex::Locker api_locker (watchpoint_sp->GetTarget().GetAPIMutex()); + return watchpoint_sp->IsEnabled(); + } + else + return false; +} + +uint32_t +SBWatchpoint::GetHitCount () +{ + uint32_t count = 0; + lldb::WatchpointSP watchpoint_sp(GetSP()); + if (watchpoint_sp) + { + Mutex::Locker api_locker (watchpoint_sp->GetTarget().GetAPIMutex()); + count = watchpoint_sp->GetHitCount(); + } + + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (log) + log->Printf ("SBWatchpoint(%p)::GetHitCount () => %u", watchpoint_sp.get(), count); + + return count; +} + +uint32_t +SBWatchpoint::GetIgnoreCount () +{ + lldb::WatchpointSP watchpoint_sp(GetSP()); + if (watchpoint_sp) + { + Mutex::Locker api_locker (watchpoint_sp->GetTarget().GetAPIMutex()); + return watchpoint_sp->GetIgnoreCount(); + } + else + return 0; +} + +void +SBWatchpoint::SetIgnoreCount (uint32_t n) +{ + lldb::WatchpointSP watchpoint_sp(GetSP()); + if (watchpoint_sp) + { + Mutex::Locker api_locker (watchpoint_sp->GetTarget().GetAPIMutex()); + watchpoint_sp->SetIgnoreCount (n); + } +} + +const char * +SBWatchpoint::GetCondition () +{ + lldb::WatchpointSP watchpoint_sp(GetSP()); + if (watchpoint_sp) + { + Mutex::Locker api_locker (watchpoint_sp->GetTarget().GetAPIMutex()); + return watchpoint_sp->GetConditionText (); + } + return NULL; +} + +void +SBWatchpoint::SetCondition (const char *condition) +{ + lldb::WatchpointSP watchpoint_sp(GetSP()); + if (watchpoint_sp) + { + Mutex::Locker api_locker (watchpoint_sp->GetTarget().GetAPIMutex()); + watchpoint_sp->SetCondition (condition); + } +} + +bool +SBWatchpoint::GetDescription (SBStream &description, DescriptionLevel level) +{ + Stream &strm = description.ref(); + + lldb::WatchpointSP watchpoint_sp(GetSP()); + if (watchpoint_sp) + { + Mutex::Locker api_locker (watchpoint_sp->GetTarget().GetAPIMutex()); + watchpoint_sp->GetDescription (&strm, level); + strm.EOL(); + } + else + strm.PutCString ("No value"); + + return true; +} + +void +SBWatchpoint::Clear () +{ + m_opaque_sp.reset(); +} + +lldb::WatchpointSP +SBWatchpoint::GetSP () const +{ + return m_opaque_sp; +} + +void +SBWatchpoint::SetSP (const lldb::WatchpointSP &sp) +{ + m_opaque_sp = sp; +} + +bool +SBWatchpoint::EventIsWatchpointEvent (const lldb::SBEvent &event) +{ + return Watchpoint::WatchpointEventData::GetEventDataFromEvent(event.get()) != NULL; + +} + +WatchpointEventType +SBWatchpoint::GetWatchpointEventTypeFromEvent (const SBEvent& event) +{ + if (event.IsValid()) + return Watchpoint::WatchpointEventData::GetWatchpointEventTypeFromEvent (event.GetSP()); + return eWatchpointEventTypeInvalidType; +} + +SBWatchpoint +SBWatchpoint::GetWatchpointFromEvent (const lldb::SBEvent& event) +{ + SBWatchpoint sb_watchpoint; + if (event.IsValid()) + sb_watchpoint.m_opaque_sp = Watchpoint::WatchpointEventData::GetWatchpointFromEvent (event.GetSP()); + return sb_watchpoint; +} diff --git a/contrib/llvm/tools/lldb/source/Breakpoint/Breakpoint.cpp b/contrib/llvm/tools/lldb/source/Breakpoint/Breakpoint.cpp new file mode 100644 index 00000000000..9bc43814b48 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Breakpoint/Breakpoint.cpp @@ -0,0 +1,794 @@ +//===-- Breakpoint.cpp ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes + +#include "lldb/Core/Address.h" +#include "lldb/Breakpoint/Breakpoint.h" +#include "lldb/Breakpoint/BreakpointLocation.h" +#include "lldb/Breakpoint/BreakpointLocationCollection.h" +#include "lldb/Breakpoint/BreakpointResolver.h" +#include "lldb/Breakpoint/BreakpointResolverFileLine.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/ModuleList.h" +#include "lldb/Core/SearchFilter.h" +#include "lldb/Core/Section.h" +#include "lldb/Core/Stream.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Symbol/SymbolContext.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/ThreadSpec.h" +#include "lldb/lldb-private-log.h" +#include "llvm/Support/Casting.h" + +using namespace lldb; +using namespace lldb_private; +using namespace llvm; + +const ConstString & +Breakpoint::GetEventIdentifier () +{ + static ConstString g_identifier("event-identifier.breakpoint.changed"); + return g_identifier; +} + +//---------------------------------------------------------------------- +// Breakpoint constructor +//---------------------------------------------------------------------- +Breakpoint::Breakpoint(Target &target, SearchFilterSP &filter_sp, BreakpointResolverSP &resolver_sp) : + m_being_created(true), + m_target (target), + m_filter_sp (filter_sp), + m_resolver_sp (resolver_sp), + m_options (), + m_locations (*this) +{ + m_being_created = false; +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +Breakpoint::~Breakpoint() +{ +} + +bool +Breakpoint::IsInternal () const +{ + return LLDB_BREAK_ID_IS_INTERNAL(m_bid); +} + + + +Target& +Breakpoint::GetTarget () +{ + return m_target; +} + +const Target& +Breakpoint::GetTarget () const +{ + return m_target; +} + +BreakpointLocationSP +Breakpoint::AddLocation (const Address &addr, bool *new_location) +{ + return m_locations.AddLocation (addr, new_location); +} + +BreakpointLocationSP +Breakpoint::FindLocationByAddress (const Address &addr) +{ + return m_locations.FindByAddress(addr); +} + +break_id_t +Breakpoint::FindLocationIDByAddress (const Address &addr) +{ + return m_locations.FindIDByAddress(addr); +} + +BreakpointLocationSP +Breakpoint::FindLocationByID (break_id_t bp_loc_id) +{ + return m_locations.FindByID(bp_loc_id); +} + +BreakpointLocationSP +Breakpoint::GetLocationAtIndex (size_t index) +{ + return m_locations.GetByIndex(index); +} + +// For each of the overall options we need to decide how they propagate to +// the location options. This will determine the precedence of options on +// the breakpoint vs. its locations. + +// Disable at the breakpoint level should override the location settings. +// That way you can conveniently turn off a whole breakpoint without messing +// up the individual settings. + +void +Breakpoint::SetEnabled (bool enable) +{ + if (enable == m_options.IsEnabled()) + return; + + m_options.SetEnabled(enable); + if (enable) + m_locations.ResolveAllBreakpointSites(); + else + m_locations.ClearAllBreakpointSites(); + + SendBreakpointChangedEvent (enable ? eBreakpointEventTypeEnabled : eBreakpointEventTypeDisabled); + +} + +bool +Breakpoint::IsEnabled () +{ + return m_options.IsEnabled(); +} + +void +Breakpoint::SetIgnoreCount (uint32_t n) +{ + if (m_options.GetIgnoreCount() == n) + return; + + m_options.SetIgnoreCount(n); + SendBreakpointChangedEvent (eBreakpointEventTypeIgnoreChanged); +} + +void +Breakpoint::DecrementIgnoreCount () +{ + uint32_t ignore = m_options.GetIgnoreCount(); + if (ignore != 0) + m_options.SetIgnoreCount(ignore - 1); +} + +uint32_t +Breakpoint::GetIgnoreCount () const +{ + return m_options.GetIgnoreCount(); +} + +bool +Breakpoint::IgnoreCountShouldStop () +{ + uint32_t ignore = GetIgnoreCount(); + if (ignore != 0) + { + // When we get here we know the location that caused the stop doesn't have an ignore count, + // since by contract we call it first... So we don't have to find & decrement it, we only have + // to decrement our own ignore count. + DecrementIgnoreCount(); + return false; + } + else + return true; +} + +uint32_t +Breakpoint::GetHitCount () const +{ + return m_locations.GetHitCount(); +} + +bool +Breakpoint::IsOneShot () const +{ + return m_options.IsOneShot(); +} + +void +Breakpoint::SetOneShot (bool one_shot) +{ + m_options.SetOneShot (one_shot); +} + +void +Breakpoint::SetThreadID (lldb::tid_t thread_id) +{ + if (m_options.GetThreadSpec()->GetTID() == thread_id) + return; + + m_options.GetThreadSpec()->SetTID(thread_id); + SendBreakpointChangedEvent (eBreakpointEventTypeThreadChanged); +} + +lldb::tid_t +Breakpoint::GetThreadID () const +{ + if (m_options.GetThreadSpecNoCreate() == NULL) + return LLDB_INVALID_THREAD_ID; + else + return m_options.GetThreadSpecNoCreate()->GetTID(); +} + +void +Breakpoint::SetThreadIndex (uint32_t index) +{ + if (m_options.GetThreadSpec()->GetIndex() == index) + return; + + m_options.GetThreadSpec()->SetIndex(index); + SendBreakpointChangedEvent (eBreakpointEventTypeThreadChanged); +} + +uint32_t +Breakpoint::GetThreadIndex() const +{ + if (m_options.GetThreadSpecNoCreate() == NULL) + return 0; + else + return m_options.GetThreadSpecNoCreate()->GetIndex(); +} + +void +Breakpoint::SetThreadName (const char *thread_name) +{ + if (m_options.GetThreadSpec()->GetName() != NULL + && ::strcmp (m_options.GetThreadSpec()->GetName(), thread_name) == 0) + return; + + m_options.GetThreadSpec()->SetName (thread_name); + SendBreakpointChangedEvent (eBreakpointEventTypeThreadChanged); +} + +const char * +Breakpoint::GetThreadName () const +{ + if (m_options.GetThreadSpecNoCreate() == NULL) + return NULL; + else + return m_options.GetThreadSpecNoCreate()->GetName(); +} + +void +Breakpoint::SetQueueName (const char *queue_name) +{ + if (m_options.GetThreadSpec()->GetQueueName() != NULL + && ::strcmp (m_options.GetThreadSpec()->GetQueueName(), queue_name) == 0) + return; + + m_options.GetThreadSpec()->SetQueueName (queue_name); + SendBreakpointChangedEvent (eBreakpointEventTypeThreadChanged); +} + +const char * +Breakpoint::GetQueueName () const +{ + if (m_options.GetThreadSpecNoCreate() == NULL) + return NULL; + else + return m_options.GetThreadSpecNoCreate()->GetQueueName(); +} + +void +Breakpoint::SetCondition (const char *condition) +{ + m_options.SetCondition (condition); + SendBreakpointChangedEvent (eBreakpointEventTypeConditionChanged); +} + +const char * +Breakpoint::GetConditionText () const +{ + return m_options.GetConditionText(); +} + +// This function is used when "baton" doesn't need to be freed +void +Breakpoint::SetCallback (BreakpointHitCallback callback, void *baton, bool is_synchronous) +{ + // The default "Baton" class will keep a copy of "baton" and won't free + // or delete it when it goes goes out of scope. + m_options.SetCallback(callback, BatonSP (new Baton(baton)), is_synchronous); + + SendBreakpointChangedEvent (eBreakpointEventTypeCommandChanged); +} + +// This function is used when a baton needs to be freed and therefore is +// contained in a "Baton" subclass. +void +Breakpoint::SetCallback (BreakpointHitCallback callback, const BatonSP &callback_baton_sp, bool is_synchronous) +{ + m_options.SetCallback(callback, callback_baton_sp, is_synchronous); +} + +void +Breakpoint::ClearCallback () +{ + m_options.ClearCallback (); +} + +bool +Breakpoint::InvokeCallback (StoppointCallbackContext *context, break_id_t bp_loc_id) +{ + return m_options.InvokeCallback (context, GetID(), bp_loc_id); +} + +BreakpointOptions * +Breakpoint::GetOptions () +{ + return &m_options; +} + +void +Breakpoint::ResolveBreakpoint () +{ + if (m_resolver_sp) + m_resolver_sp->ResolveBreakpoint(*m_filter_sp); +} + +void +Breakpoint::ResolveBreakpointInModules (ModuleList &module_list) +{ + if (m_resolver_sp) + m_resolver_sp->ResolveBreakpointInModules(*m_filter_sp, module_list); +} + +void +Breakpoint::ClearAllBreakpointSites () +{ + m_locations.ClearAllBreakpointSites(); +} + +//---------------------------------------------------------------------- +// ModulesChanged: Pass in a list of new modules, and +//---------------------------------------------------------------------- + +void +Breakpoint::ModulesChanged (ModuleList &module_list, bool load, bool delete_locations) +{ + Mutex::Locker modules_mutex(module_list.GetMutex()); + if (load) + { + // The logic for handling new modules is: + // 1) If the filter rejects this module, then skip it. + // 2) Run through the current location list and if there are any locations + // for that module, we mark the module as "seen" and we don't try to re-resolve + // breakpoint locations for that module. + // However, we do add breakpoint sites to these locations if needed. + // 3) If we don't see this module in our breakpoint location list, call ResolveInModules. + + ModuleList new_modules; // We'll stuff the "unseen" modules in this list, and then resolve + // them after the locations pass. Have to do it this way because + // resolving breakpoints will add new locations potentially. + + const size_t num_locs = m_locations.GetSize(); + size_t num_modules = module_list.GetSize(); + for (size_t i = 0; i < num_modules; i++) + { + bool seen = false; + ModuleSP module_sp (module_list.GetModuleAtIndexUnlocked (i)); + if (!m_filter_sp->ModulePasses (module_sp)) + continue; + + for (size_t loc_idx = 0; loc_idx < num_locs; loc_idx++) + { + BreakpointLocationSP break_loc = m_locations.GetByIndex(loc_idx); + if (!break_loc->IsEnabled()) + continue; + SectionSP section_sp (break_loc->GetAddress().GetSection()); + if (!section_sp || section_sp->GetModule() == module_sp) + { + if (!seen) + seen = true; + + if (!break_loc->ResolveBreakpointSite()) + { + Log *log (lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_BREAKPOINTS)); + if (log) + log->Printf ("Warning: could not set breakpoint site for breakpoint location %d of breakpoint %d.\n", + break_loc->GetID(), GetID()); + } + } + } + + if (!seen) + new_modules.AppendIfNeeded (module_sp); + + } + + if (new_modules.GetSize() > 0) + { + // If this is not an internal breakpoint, set up to record the new locations, then dispatch + // an event with the new locations. + if (!IsInternal()) + { + BreakpointEventData *new_locations_event = new BreakpointEventData (eBreakpointEventTypeLocationsAdded, + shared_from_this()); + + m_locations.StartRecordingNewLocations(new_locations_event->GetBreakpointLocationCollection()); + + ResolveBreakpointInModules(new_modules); + + m_locations.StopRecordingNewLocations(); + if (new_locations_event->GetBreakpointLocationCollection().GetSize() != 0) + { + SendBreakpointChangedEvent (new_locations_event); + } + else + delete new_locations_event; + } + else + ResolveBreakpointInModules(new_modules); + + } + } + else + { + // Go through the currently set locations and if any have breakpoints in + // the module list, then remove their breakpoint sites, and their locations if asked to. + + BreakpointEventData *removed_locations_event; + if (!IsInternal()) + removed_locations_event = new BreakpointEventData (eBreakpointEventTypeLocationsRemoved, + shared_from_this()); + else + removed_locations_event = NULL; + + size_t num_modules = module_list.GetSize(); + for (size_t i = 0; i < num_modules; i++) + { + ModuleSP module_sp (module_list.GetModuleAtIndexUnlocked (i)); + if (m_filter_sp->ModulePasses (module_sp)) + { + size_t loc_idx = 0; + size_t num_locations = m_locations.GetSize(); + BreakpointLocationCollection locations_to_remove; + for (loc_idx = 0; loc_idx < num_locations; loc_idx++) + { + BreakpointLocationSP break_loc_sp (m_locations.GetByIndex(loc_idx)); + SectionSP section_sp (break_loc_sp->GetAddress().GetSection()); + if (section_sp && section_sp->GetModule() == module_sp) + { + // Remove this breakpoint since the shared library is + // unloaded, but keep the breakpoint location around + // so we always get complete hit count and breakpoint + // lifetime info + break_loc_sp->ClearBreakpointSite(); + if (removed_locations_event) + { + removed_locations_event->GetBreakpointLocationCollection().Add(break_loc_sp); + } + if (delete_locations) + locations_to_remove.Add (break_loc_sp); + + } + } + + if (delete_locations) + { + size_t num_locations_to_remove = locations_to_remove.GetSize(); + for (loc_idx = 0; loc_idx < num_locations_to_remove; loc_idx++) + m_locations.RemoveLocation (locations_to_remove.GetByIndex(loc_idx)); + } + } + } + SendBreakpointChangedEvent (removed_locations_event); + } +} + +void +Breakpoint::ModuleReplaced (ModuleSP old_module_sp, ModuleSP new_module_sp) +{ + ModuleList temp_list; + temp_list.Append (new_module_sp); + ModulesChanged (temp_list, true); + + // TO DO: For now I'm just adding locations for the new module and removing the + // breakpoint locations that were in the old module. + // We should really go find the ones that are in the new module & if we can determine that they are "equivalent" + // carry over the options from the old location to the new. + + temp_list.Clear(); + temp_list.Append (old_module_sp); + ModulesChanged (temp_list, false, true); +} + +void +Breakpoint::Dump (Stream *) +{ +} + +size_t +Breakpoint::GetNumResolvedLocations() const +{ + // Return the number of breakpoints that are actually resolved and set + // down in the inferior process. + return m_locations.GetNumResolvedLocations(); +} + +size_t +Breakpoint::GetNumLocations() const +{ + return m_locations.GetSize(); +} + +void +Breakpoint::GetDescription (Stream *s, lldb::DescriptionLevel level, bool show_locations) +{ + assert (s != NULL); + + if (!m_kind_description.empty()) + { + if (eDescriptionLevelBrief) + { + s->PutCString (GetBreakpointKind()); + return; + } + else + s->Printf("Kind: %s\n", GetBreakpointKind ()); + } + + const size_t num_locations = GetNumLocations (); + const size_t num_resolved_locations = GetNumResolvedLocations (); + + // They just made the breakpoint, they don't need to be told HOW they made it... + // Also, we'll print the breakpoint number differently depending on whether there is 1 or more locations. + if (level != eDescriptionLevelInitial) + { + s->Printf("%i: ", GetID()); + GetResolverDescription (s); + GetFilterDescription (s); + } + + switch (level) + { + case lldb::eDescriptionLevelBrief: + case lldb::eDescriptionLevelFull: + if (num_locations > 0) + { + s->Printf(", locations = %" PRIu64, (uint64_t)num_locations); + if (num_resolved_locations > 0) + s->Printf(", resolved = %" PRIu64, (uint64_t)num_resolved_locations); + } + else + { + // Don't print the pending notification for exception resolvers since we don't generally + // know how to set them until the target is run. + if (m_resolver_sp->getResolverID() != BreakpointResolver::ExceptionResolver) + s->Printf(", locations = 0 (pending)"); + } + + GetOptions()->GetDescription(s, level); + + if (level == lldb::eDescriptionLevelFull) + { + s->IndentLess(); + s->EOL(); + } + break; + + case lldb::eDescriptionLevelInitial: + s->Printf ("Breakpoint %i: ", GetID()); + if (num_locations == 0) + { + s->Printf ("no locations (pending)."); + } + else if (num_locations == 1) + { + // If there is one location only, we'll just print that location information. But don't do this if + // show locations is true, then that will be handled below. + if (show_locations == false) + { + GetLocationAtIndex(0)->GetDescription(s, level); + } + else + { + s->Printf ("%zd locations.", num_locations); + } + } + else + { + s->Printf ("%zd locations.", num_locations); + } + s->EOL(); + break; + case lldb::eDescriptionLevelVerbose: + // Verbose mode does a debug dump of the breakpoint + Dump (s); + s->EOL (); + //s->Indent(); + GetOptions()->GetDescription(s, level); + break; + + default: + break; + } + + // The brief description is just the location name (1.2 or whatever). That's pointless to + // show in the breakpoint's description, so suppress it. + if (show_locations && level != lldb::eDescriptionLevelBrief) + { + s->IndentMore(); + for (size_t i = 0; i < num_locations; ++i) + { + BreakpointLocation *loc = GetLocationAtIndex(i).get(); + loc->GetDescription(s, level); + s->EOL(); + } + s->IndentLess(); + } +} + +void +Breakpoint::GetResolverDescription (Stream *s) +{ + if (m_resolver_sp) + m_resolver_sp->GetDescription (s); +} + + +bool +Breakpoint::GetMatchingFileLine (const ConstString &filename, uint32_t line_number, BreakpointLocationCollection &loc_coll) +{ + // TODO: To be correct, this method needs to fill the breakpoint location collection + // with the location IDs which match the filename and line_number. + // + + if (m_resolver_sp) + { + BreakpointResolverFileLine *resolverFileLine = dyn_cast(m_resolver_sp.get()); + if (resolverFileLine && + resolverFileLine->m_file_spec.GetFilename() == filename && + resolverFileLine->m_line_number == line_number) + { + return true; + } + } + return false; +} + +void +Breakpoint::GetFilterDescription (Stream *s) +{ + m_filter_sp->GetDescription (s); +} + +void +Breakpoint::SendBreakpointChangedEvent (lldb::BreakpointEventType eventKind) +{ + if (!m_being_created + && !IsInternal() + && GetTarget().EventTypeHasListeners(Target::eBroadcastBitBreakpointChanged)) + { + BreakpointEventData *data = new Breakpoint::BreakpointEventData (eventKind, shared_from_this()); + + GetTarget().BroadcastEvent (Target::eBroadcastBitBreakpointChanged, data); + } +} + +void +Breakpoint::SendBreakpointChangedEvent (BreakpointEventData *data) +{ + + if (data == NULL) + return; + + if (!m_being_created + && !IsInternal() + && GetTarget().EventTypeHasListeners(Target::eBroadcastBitBreakpointChanged)) + GetTarget().BroadcastEvent (Target::eBroadcastBitBreakpointChanged, data); + else + delete data; +} + +Breakpoint::BreakpointEventData::BreakpointEventData (BreakpointEventType sub_type, + const BreakpointSP &new_breakpoint_sp) : + EventData (), + m_breakpoint_event (sub_type), + m_new_breakpoint_sp (new_breakpoint_sp) +{ +} + +Breakpoint::BreakpointEventData::~BreakpointEventData () +{ +} + +const ConstString & +Breakpoint::BreakpointEventData::GetFlavorString () +{ + static ConstString g_flavor ("Breakpoint::BreakpointEventData"); + return g_flavor; +} + +const ConstString & +Breakpoint::BreakpointEventData::GetFlavor () const +{ + return BreakpointEventData::GetFlavorString (); +} + + +BreakpointSP & +Breakpoint::BreakpointEventData::GetBreakpoint () +{ + return m_new_breakpoint_sp; +} + +BreakpointEventType +Breakpoint::BreakpointEventData::GetBreakpointEventType () const +{ + return m_breakpoint_event; +} + +void +Breakpoint::BreakpointEventData::Dump (Stream *s) const +{ +} + +const Breakpoint::BreakpointEventData * +Breakpoint::BreakpointEventData::GetEventDataFromEvent (const Event *event) +{ + if (event) + { + const EventData *event_data = event->GetData(); + if (event_data && event_data->GetFlavor() == BreakpointEventData::GetFlavorString()) + return static_cast (event->GetData()); + } + return NULL; +} + +BreakpointEventType +Breakpoint::BreakpointEventData::GetBreakpointEventTypeFromEvent (const EventSP &event_sp) +{ + const BreakpointEventData *data = GetEventDataFromEvent (event_sp.get()); + + if (data == NULL) + return eBreakpointEventTypeInvalidType; + else + return data->GetBreakpointEventType(); +} + +BreakpointSP +Breakpoint::BreakpointEventData::GetBreakpointFromEvent (const EventSP &event_sp) +{ + BreakpointSP bp_sp; + + const BreakpointEventData *data = GetEventDataFromEvent (event_sp.get()); + if (data) + bp_sp = data->m_new_breakpoint_sp; + + return bp_sp; +} + +size_t +Breakpoint::BreakpointEventData::GetNumBreakpointLocationsFromEvent (const EventSP &event_sp) +{ + const BreakpointEventData *data = GetEventDataFromEvent (event_sp.get()); + if (data) + return data->m_locations.GetSize(); + + return 0; +} + +lldb::BreakpointLocationSP +Breakpoint::BreakpointEventData::GetBreakpointLocationAtIndexFromEvent (const lldb::EventSP &event_sp, uint32_t bp_loc_idx) +{ + lldb::BreakpointLocationSP bp_loc_sp; + + const BreakpointEventData *data = GetEventDataFromEvent (event_sp.get()); + if (data) + { + bp_loc_sp = data->m_locations.GetByIndex(bp_loc_idx); + } + + return bp_loc_sp; +} diff --git a/contrib/llvm/tools/lldb/source/Breakpoint/BreakpointID.cpp b/contrib/llvm/tools/lldb/source/Breakpoint/BreakpointID.cpp new file mode 100644 index 00000000000..9a59e29d007 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Breakpoint/BreakpointID.cpp @@ -0,0 +1,123 @@ +//===-- BreakpointID.cpp ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + + +// C Includes +#include + +// C++ Includes +// Other libraries and framework includes +// Project includes + +#include "lldb/Breakpoint/BreakpointID.h" +#include "lldb/Breakpoint/Breakpoint.h" +#include "lldb/Core/Stream.h" + +using namespace lldb; +using namespace lldb_private; + +BreakpointID::BreakpointID (break_id_t bp_id, break_id_t loc_id) : + m_break_id (bp_id), + m_location_id (loc_id) +{ +} + +BreakpointID::~BreakpointID () +{ +} + +const char *BreakpointID::g_range_specifiers[] = { "-", "to", "To", "TO", NULL }; + +// Tells whether or not STR is valid to use between two strings representing breakpoint IDs, to +// indicate a range of breakpoint IDs. This is broken out into a separate function so that we can +// easily change or add to the format for specifying ID ranges at a later date. + +bool +BreakpointID::IsRangeIdentifier (const char *str) +{ + int specifier_count = 0; + for (int i = 0; g_range_specifiers[i] != NULL; ++i) + ++specifier_count; + + for (int i = 0; i < specifier_count; ++i) + { + if (strcmp (g_range_specifiers[i], str) == 0) + return true; + } + + return false; +} + +bool +BreakpointID::IsValidIDExpression (const char *str) +{ + break_id_t bp_id; + break_id_t loc_id; + BreakpointID::ParseCanonicalReference (str, &bp_id, &loc_id); + + if (bp_id == LLDB_INVALID_BREAK_ID) + return false; + else + return true; +} + +void +BreakpointID::GetDescription (Stream *s, lldb::DescriptionLevel level) +{ + if (level == eDescriptionLevelVerbose) + s->Printf("%p BreakpointID:", this); + + if (m_break_id == LLDB_INVALID_BREAK_ID) + s->PutCString (""); + else if (m_location_id == LLDB_INVALID_BREAK_ID) + s->Printf("%i", m_break_id); + else + s->Printf("%i.%i", m_break_id, m_location_id); +} + +void +BreakpointID::GetCanonicalReference (Stream *s, break_id_t bp_id, break_id_t loc_id) +{ + if (bp_id == LLDB_INVALID_BREAK_ID) + s->PutCString (""); + else if (loc_id == LLDB_INVALID_BREAK_ID) + s->Printf("%i", bp_id); + else + s->Printf("%i.%i", bp_id, loc_id); +} + +bool +BreakpointID::ParseCanonicalReference (const char *input, break_id_t *break_id_ptr, break_id_t *break_loc_id_ptr) +{ + *break_id_ptr = LLDB_INVALID_BREAK_ID; + *break_loc_id_ptr = LLDB_INVALID_BREAK_ID; + + if (input == NULL || *input == '\0') + return false; + + const char *format = "%i%n.%i%n"; + int chars_consumed_1 = 0; + int chars_consumed_2 = 0; + int n_items_parsed = ::sscanf (input, + format, + break_id_ptr, // %i parse the breakpoint ID + &chars_consumed_1, // %n gets the number of characters parsed so far + break_loc_id_ptr, // %i parse the breakpoint location ID + &chars_consumed_2); // %n gets the number of characters parsed so far + + if ((n_items_parsed == 1 && input[chars_consumed_1] == '\0') || + (n_items_parsed == 2 && input[chars_consumed_2] == '\0')) + return true; + + // Badly formatted canonical reference. + *break_id_ptr = LLDB_INVALID_BREAK_ID; + *break_loc_id_ptr = LLDB_INVALID_BREAK_ID; + return false; +} + diff --git a/contrib/llvm/tools/lldb/source/Breakpoint/BreakpointIDList.cpp b/contrib/llvm/tools/lldb/source/Breakpoint/BreakpointIDList.cpp new file mode 100644 index 00000000000..24101b1442f --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Breakpoint/BreakpointIDList.cpp @@ -0,0 +1,397 @@ +//===-- BreakpointIDList.cpp ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Breakpoint/BreakpointIDList.h" + +#include "lldb/Breakpoint/Breakpoint.h" +#include "lldb/Breakpoint/BreakpointLocation.h" +#include "lldb/Interpreter/CommandReturnObject.h" +#include "lldb/Interpreter/Args.h" +#include "lldb/Target/Target.h" + +using namespace lldb; +using namespace lldb_private; + +//---------------------------------------------------------------------- +// class BreakpointIDList +//---------------------------------------------------------------------- + +BreakpointIDList::BreakpointIDList () : +m_invalid_id (LLDB_INVALID_BREAK_ID, LLDB_INVALID_BREAK_ID) +{ +} + +BreakpointIDList::~BreakpointIDList () +{ +} + +size_t +BreakpointIDList::GetSize() +{ + return m_breakpoint_ids.size(); +} + +BreakpointID & +BreakpointIDList::GetBreakpointIDAtIndex (size_t index) +{ + if (index < m_breakpoint_ids.size()) + return m_breakpoint_ids[index]; + else + return m_invalid_id; +} + +bool +BreakpointIDList::RemoveBreakpointIDAtIndex (size_t index) +{ + if (index >= m_breakpoint_ids.size()) + return false; + + m_breakpoint_ids.erase (m_breakpoint_ids.begin() + index); + return true; +} + +void +BreakpointIDList::Clear() +{ + m_breakpoint_ids.clear (); +} + +bool +BreakpointIDList::AddBreakpointID (BreakpointID bp_id) +{ + m_breakpoint_ids.push_back (bp_id); + + return true; // We don't do any verification in this function, so always return true. +} + +bool +BreakpointIDList::AddBreakpointID (const char *bp_id_str) +{ + BreakpointID temp_bp_id; + break_id_t bp_id; + break_id_t loc_id; + + bool success = BreakpointID::ParseCanonicalReference (bp_id_str, &bp_id, &loc_id); + + if (success) + { + temp_bp_id.SetID (bp_id, loc_id); + m_breakpoint_ids.push_back (temp_bp_id); + } + + return success; +} + +bool +BreakpointIDList::FindBreakpointID (BreakpointID &bp_id, size_t *position) +{ + for (size_t i = 0; i < m_breakpoint_ids.size(); ++i) + { + BreakpointID tmp_id = m_breakpoint_ids[i]; + if (tmp_id.GetBreakpointID() == bp_id.GetBreakpointID() + && tmp_id.GetLocationID() == bp_id.GetLocationID()) + { + *position = i; + return true; + } + } + + return false; +} + +bool +BreakpointIDList::FindBreakpointID (const char *bp_id_str, size_t *position) +{ + BreakpointID temp_bp_id; + break_id_t bp_id; + break_id_t loc_id; + + if (BreakpointID::ParseCanonicalReference (bp_id_str, &bp_id, &loc_id)) + { + temp_bp_id.SetID (bp_id, loc_id); + return FindBreakpointID (temp_bp_id, position); + } + else + return false; +} + +void +BreakpointIDList::InsertStringArray (const char **string_array, size_t array_size, CommandReturnObject &result) +{ + if (string_array == NULL) + return; + + for (uint32_t i = 0; i < array_size; ++i) + { + break_id_t bp_id; + break_id_t loc_id; + + if (BreakpointID::ParseCanonicalReference (string_array[i], &bp_id, &loc_id)) + { + if (bp_id != LLDB_INVALID_BREAK_ID) + { + BreakpointID temp_bp_id(bp_id, loc_id); + m_breakpoint_ids.push_back (temp_bp_id); + } + else + { + result.AppendErrorWithFormat ("'%s' is not a valid breakpoint ID.\n", string_array[i]); + result.SetStatus (eReturnStatusFailed); + return; + } + } + } + result.SetStatus (eReturnStatusSuccessFinishNoResult); +} + + +// This function takes OLD_ARGS, which is usually the result of breaking the command string arguments into +// an array of space-separated strings, and searches through the arguments for any breakpoint ID range specifiers. +// Any string in the array that is not part of an ID range specifier is copied directly into NEW_ARGS. If any +// ID range specifiers are found, the range is interpreted and a list of canonical breakpoint IDs corresponding to +// all the current breakpoints and locations in the range are added to NEW_ARGS. When this function is done, +// NEW_ARGS should be a copy of OLD_ARGS, with and ID range specifiers replaced by the members of the range. + +void +BreakpointIDList::FindAndReplaceIDRanges (Args &old_args, Target *target, CommandReturnObject &result, + Args &new_args) +{ + std::string range_start; + const char *range_end; + const char *current_arg; + const size_t num_old_args = old_args.GetArgumentCount(); + + for (size_t i = 0; i < num_old_args; ++i) + { + bool is_range = false; + current_arg = old_args.GetArgumentAtIndex (i); + + size_t range_start_len = 0; + size_t range_end_pos = 0; + if (BreakpointIDList::StringContainsIDRangeExpression (current_arg, &range_start_len, &range_end_pos)) + { + is_range = true; + range_start.assign (current_arg, range_start_len); + range_end = current_arg + range_end_pos; + } + else if ((i + 2 < num_old_args) + && BreakpointID::IsRangeIdentifier (old_args.GetArgumentAtIndex (i+1)) + && BreakpointID::IsValidIDExpression (current_arg) + && BreakpointID::IsValidIDExpression (old_args.GetArgumentAtIndex (i+2))) + { + range_start.assign (current_arg); + range_end = old_args.GetArgumentAtIndex (i+2); + is_range = true; + i = i+2; + } + else + { + // See if user has specified id.* + std::string tmp_str = old_args.GetArgumentAtIndex (i); + size_t pos = tmp_str.find ('.'); + if (pos != std::string::npos) + { + std::string bp_id_str = tmp_str.substr (0, pos); + if (BreakpointID::IsValidIDExpression (bp_id_str.c_str()) + && tmp_str[pos+1] == '*' + && tmp_str.length() == (pos + 2)) + { + break_id_t bp_id; + break_id_t bp_loc_id; + + BreakpointID::ParseCanonicalReference (bp_id_str.c_str(), &bp_id, &bp_loc_id); + BreakpointSP breakpoint_sp = target->GetBreakpointByID (bp_id); + if (! breakpoint_sp) + { + new_args.Clear(); + result.AppendErrorWithFormat ("'%d' is not a valid breakpoint ID.\n", bp_id); + result.SetStatus (eReturnStatusFailed); + return; + } + const size_t num_locations = breakpoint_sp->GetNumLocations(); + for (size_t j = 0; j < num_locations; ++j) + { + BreakpointLocation *bp_loc = breakpoint_sp->GetLocationAtIndex(j).get(); + StreamString canonical_id_str; + BreakpointID::GetCanonicalReference (&canonical_id_str, bp_id, bp_loc->GetID()); + new_args.AppendArgument (canonical_id_str.GetData()); + } + } + + } + } + + if (is_range) + { + break_id_t start_bp_id; + break_id_t end_bp_id; + break_id_t start_loc_id; + break_id_t end_loc_id; + + BreakpointID::ParseCanonicalReference (range_start.c_str(), &start_bp_id, &start_loc_id); + BreakpointID::ParseCanonicalReference (range_end, &end_bp_id, &end_loc_id); + + if ((start_bp_id == LLDB_INVALID_BREAK_ID) + || (! target->GetBreakpointByID (start_bp_id))) + { + new_args.Clear(); + result.AppendErrorWithFormat ("'%s' is not a valid breakpoint ID.\n", range_start.c_str()); + result.SetStatus (eReturnStatusFailed); + return; + } + + if ((end_bp_id == LLDB_INVALID_BREAK_ID) + || (! target->GetBreakpointByID (end_bp_id))) + { + new_args.Clear(); + result.AppendErrorWithFormat ("'%s' is not a valid breakpoint ID.\n", range_end); + result.SetStatus (eReturnStatusFailed); + return; + } + + + if (((start_loc_id == LLDB_INVALID_BREAK_ID) + && (end_loc_id != LLDB_INVALID_BREAK_ID)) + || ((start_loc_id != LLDB_INVALID_BREAK_ID) + && (end_loc_id == LLDB_INVALID_BREAK_ID))) + { + new_args.Clear (); + result.AppendErrorWithFormat ("Invalid breakpoint id range: Either both ends of range must specify" + " a breakpoint location, or neither can specify a breakpoint location.\n"); + result.SetStatus (eReturnStatusFailed); + return; + } + + // We have valid range starting & ending breakpoint IDs. Go through all the breakpoints in the + // target and find all the breakpoints that fit into this range, and add them to new_args. + + // Next check to see if we have location id's. If so, make sure the start_bp_id and end_bp_id are + // for the same breakpoint; otherwise we have an illegal range: breakpoint id ranges that specify + // bp locations are NOT allowed to cross major bp id numbers. + + if ((start_loc_id != LLDB_INVALID_BREAK_ID) + || (end_loc_id != LLDB_INVALID_BREAK_ID)) + { + if (start_bp_id != end_bp_id) + { + new_args.Clear(); + result.AppendErrorWithFormat ("Invalid range: Ranges that specify particular breakpoint locations" + " must be within the same major breakpoint; you specified two" + " different major breakpoints, %d and %d.\n", + start_bp_id, end_bp_id); + result.SetStatus (eReturnStatusFailed); + return; + } + } + + const BreakpointList& breakpoints = target->GetBreakpointList(); + const size_t num_breakpoints = breakpoints.GetSize(); + for (size_t j = 0; j < num_breakpoints; ++j) + { + Breakpoint *breakpoint = breakpoints.GetBreakpointAtIndex (j).get(); + break_id_t cur_bp_id = breakpoint->GetID(); + + if ((cur_bp_id < start_bp_id) || (cur_bp_id > end_bp_id)) + continue; + + const size_t num_locations = breakpoint->GetNumLocations(); + + if ((cur_bp_id == start_bp_id) && (start_loc_id != LLDB_INVALID_BREAK_ID)) + { + for (size_t k = 0; k < num_locations; ++k) + { + BreakpointLocation * bp_loc = breakpoint->GetLocationAtIndex(k).get(); + if ((bp_loc->GetID() >= start_loc_id) && (bp_loc->GetID() <= end_loc_id)) + { + StreamString canonical_id_str; + BreakpointID::GetCanonicalReference (&canonical_id_str, cur_bp_id, bp_loc->GetID()); + new_args.AppendArgument (canonical_id_str.GetData()); + } + } + } + else if ((cur_bp_id == end_bp_id) && (end_loc_id != LLDB_INVALID_BREAK_ID)) + { + for (size_t k = 0; k < num_locations; ++k) + { + BreakpointLocation * bp_loc = breakpoint->GetLocationAtIndex(k).get(); + if (bp_loc->GetID() <= end_loc_id) + { + StreamString canonical_id_str; + BreakpointID::GetCanonicalReference (&canonical_id_str, cur_bp_id, bp_loc->GetID()); + new_args.AppendArgument (canonical_id_str.GetData()); + } + } + } + else + { + StreamString canonical_id_str; + BreakpointID::GetCanonicalReference (&canonical_id_str, cur_bp_id, LLDB_INVALID_BREAK_ID); + new_args.AppendArgument (canonical_id_str.GetData()); + } + } + } + else // else is_range was false + { + new_args.AppendArgument (current_arg); + } + } + + result.SetStatus (eReturnStatusSuccessFinishNoResult); + return; +} + +bool +BreakpointIDList::StringContainsIDRangeExpression (const char *in_string, + size_t *range_start_len, + size_t *range_end_pos) +{ + bool is_range_expression = false; + std::string arg_str = in_string; + std::string::size_type idx; + std::string::size_type start_pos = 0; + + *range_start_len = 0; + *range_end_pos = 0; + + int specifiers_size = 0; + for (int i = 0; BreakpointID::g_range_specifiers[i] != NULL; ++i) + ++specifiers_size; + + for (int i = 0; i < specifiers_size && !is_range_expression; ++i) + { + const char *specifier_str = BreakpointID::g_range_specifiers[i]; + size_t len = strlen (specifier_str); + idx = arg_str.find (BreakpointID::g_range_specifiers[i]); + if (idx != std::string::npos) + { + *range_start_len = idx - start_pos; + std::string start_str = arg_str.substr (start_pos, *range_start_len); + if (idx + len < arg_str.length()) + { + *range_end_pos = idx + len; + std::string end_str = arg_str.substr (*range_end_pos); + if (BreakpointID::IsValidIDExpression (start_str.c_str()) + && BreakpointID::IsValidIDExpression (end_str.c_str())) + { + is_range_expression = true; + //*range_start = start_str; + //*range_end = end_str; + } + } + } + } + + if (!is_range_expression) + { + *range_start_len = 0; + *range_end_pos = 0; + } + + return is_range_expression; +} diff --git a/contrib/llvm/tools/lldb/source/Breakpoint/BreakpointList.cpp b/contrib/llvm/tools/lldb/source/Breakpoint/BreakpointList.cpp new file mode 100644 index 00000000000..5926663af7b --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Breakpoint/BreakpointList.cpp @@ -0,0 +1,243 @@ +//===-- BreakpointList.cpp --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Breakpoint/BreakpointList.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Target/Target.h" + +using namespace lldb; +using namespace lldb_private; + +BreakpointList::BreakpointList (bool is_internal) : + m_mutex (Mutex::eMutexTypeRecursive), + m_breakpoints(), + m_next_break_id (0), + m_is_internal (is_internal) +{ +} + +BreakpointList::~BreakpointList() +{ +} + + +break_id_t +BreakpointList::Add (BreakpointSP &bp_sp, bool notify) +{ + Mutex::Locker locker(m_mutex); + // Internal breakpoint IDs are negative, normal ones are positive + bp_sp->SetID (m_is_internal ? --m_next_break_id : ++m_next_break_id); + + m_breakpoints.push_back(bp_sp); + if (notify) + { + if (bp_sp->GetTarget().EventTypeHasListeners(Target::eBroadcastBitBreakpointChanged)) + bp_sp->GetTarget().BroadcastEvent (Target::eBroadcastBitBreakpointChanged, + new Breakpoint::BreakpointEventData (eBreakpointEventTypeAdded, bp_sp)); + } + return bp_sp->GetID(); +} + +bool +BreakpointList::Remove (break_id_t break_id, bool notify) +{ + Mutex::Locker locker(m_mutex); + bp_collection::iterator pos = GetBreakpointIDIterator(break_id); // Predicate + if (pos != m_breakpoints.end()) + { + BreakpointSP bp_sp (*pos); + m_breakpoints.erase(pos); + if (notify) + { + if (bp_sp->GetTarget().EventTypeHasListeners(Target::eBroadcastBitBreakpointChanged)) + bp_sp->GetTarget().BroadcastEvent (Target::eBroadcastBitBreakpointChanged, + new Breakpoint::BreakpointEventData (eBreakpointEventTypeRemoved, bp_sp)); + } + return true; + } + return false; +} + +void +BreakpointList::SetEnabledAll (bool enabled) +{ + Mutex::Locker locker(m_mutex); + bp_collection::iterator pos, end = m_breakpoints.end(); + for (pos = m_breakpoints.begin(); pos != end; ++pos) + (*pos)->SetEnabled (enabled); +} + + +void +BreakpointList::RemoveAll (bool notify) +{ + Mutex::Locker locker(m_mutex); + ClearAllBreakpointSites (); + + if (notify) + { + bp_collection::iterator pos, end = m_breakpoints.end(); + for (pos = m_breakpoints.begin(); pos != end; ++pos) + { + if ((*pos)->GetTarget().EventTypeHasListeners(Target::eBroadcastBitBreakpointChanged)) + { + (*pos)->GetTarget().BroadcastEvent (Target::eBroadcastBitBreakpointChanged, + new Breakpoint::BreakpointEventData (eBreakpointEventTypeRemoved, + *pos)); + } + } + } + m_breakpoints.erase (m_breakpoints.begin(), m_breakpoints.end()); +} + +class BreakpointIDMatches +{ +public: + BreakpointIDMatches (break_id_t break_id) : + m_break_id(break_id) + { + } + + bool operator() (const BreakpointSP &bp) const + { + return m_break_id == bp->GetID(); + } + +private: + const break_id_t m_break_id; +}; + +BreakpointList::bp_collection::iterator +BreakpointList::GetBreakpointIDIterator (break_id_t break_id) +{ + return std::find_if(m_breakpoints.begin(), m_breakpoints.end(), // Search full range + BreakpointIDMatches(break_id)); // Predicate +} + +BreakpointList::bp_collection::const_iterator +BreakpointList::GetBreakpointIDConstIterator (break_id_t break_id) const +{ + return std::find_if(m_breakpoints.begin(), m_breakpoints.end(), // Search full range + BreakpointIDMatches(break_id)); // Predicate +} + +BreakpointSP +BreakpointList::FindBreakpointByID (break_id_t break_id) +{ + Mutex::Locker locker(m_mutex); + BreakpointSP stop_sp; + bp_collection::iterator pos = GetBreakpointIDIterator(break_id); + if (pos != m_breakpoints.end()) + stop_sp = *pos; + + return stop_sp; +} + +const BreakpointSP +BreakpointList::FindBreakpointByID (break_id_t break_id) const +{ + Mutex::Locker locker(m_mutex); + BreakpointSP stop_sp; + bp_collection::const_iterator pos = GetBreakpointIDConstIterator(break_id); + if (pos != m_breakpoints.end()) + stop_sp = *pos; + + return stop_sp; +} + +void +BreakpointList::Dump (Stream *s) const +{ + Mutex::Locker locker(m_mutex); + s->Printf("%p: ", this); + s->Indent(); + s->Printf("BreakpointList with %u Breakpoints:\n", (uint32_t)m_breakpoints.size()); + s->IndentMore(); + bp_collection::const_iterator pos; + bp_collection::const_iterator end = m_breakpoints.end(); + for (pos = m_breakpoints.begin(); pos != end; ++pos) + (*pos)->Dump(s); + s->IndentLess(); +} + + +BreakpointSP +BreakpointList::GetBreakpointAtIndex (size_t i) +{ + Mutex::Locker locker(m_mutex); + BreakpointSP stop_sp; + bp_collection::iterator end = m_breakpoints.end(); + bp_collection::iterator pos; + size_t curr_i = 0; + for (pos = m_breakpoints.begin(), curr_i = 0; pos != end; ++pos, ++curr_i) + { + if (curr_i == i) + stop_sp = *pos; + } + return stop_sp; +} + +const BreakpointSP +BreakpointList::GetBreakpointAtIndex (size_t i) const +{ + Mutex::Locker locker(m_mutex); + BreakpointSP stop_sp; + bp_collection::const_iterator end = m_breakpoints.end(); + bp_collection::const_iterator pos; + size_t curr_i = 0; + for (pos = m_breakpoints.begin(), curr_i = 0; pos != end; ++pos, ++curr_i) + { + if (curr_i == i) + stop_sp = *pos; + } + return stop_sp; +} + +void +BreakpointList::UpdateBreakpoints (ModuleList& module_list, bool added) +{ + Mutex::Locker locker(m_mutex); + bp_collection::iterator end = m_breakpoints.end(); + bp_collection::iterator pos; + for (pos = m_breakpoints.begin(); pos != end; ++pos) + (*pos)->ModulesChanged (module_list, added); + +} + +void +BreakpointList::UpdateBreakpointsWhenModuleIsReplaced (ModuleSP old_module_sp, ModuleSP new_module_sp) +{ + Mutex::Locker locker(m_mutex); + bp_collection::iterator end = m_breakpoints.end(); + bp_collection::iterator pos; + for (pos = m_breakpoints.begin(); pos != end; ++pos) + (*pos)->ModuleReplaced (old_module_sp, new_module_sp); + +} + +void +BreakpointList::ClearAllBreakpointSites () +{ + Mutex::Locker locker(m_mutex); + bp_collection::iterator end = m_breakpoints.end(); + bp_collection::iterator pos; + for (pos = m_breakpoints.begin(); pos != end; ++pos) + (*pos)->ClearAllBreakpointSites (); + +} + +void +BreakpointList::GetListMutex (Mutex::Locker &locker) +{ + return locker.Lock (m_mutex); +} diff --git a/contrib/llvm/tools/lldb/source/Breakpoint/BreakpointLocation.cpp b/contrib/llvm/tools/lldb/source/Breakpoint/BreakpointLocation.cpp new file mode 100644 index 00000000000..1ec726dd52b --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Breakpoint/BreakpointLocation.cpp @@ -0,0 +1,677 @@ +//===-- BreakpointLocation.cpp ----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +// C Includes +// C++ Includes +#include + +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private-log.h" +#include "lldb/Breakpoint/BreakpointLocation.h" +#include "lldb/Breakpoint/BreakpointID.h" +#include "lldb/Breakpoint/StoppointCallbackContext.h" +#include "lldb/Core/Debugger.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Symbol/CompileUnit.h" +#include "lldb/Symbol/Symbol.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Thread.h" +#include "lldb/Target/ThreadSpec.h" + +using namespace lldb; +using namespace lldb_private; + +BreakpointLocation::BreakpointLocation +( + break_id_t loc_id, + Breakpoint &owner, + const Address &addr, + lldb::tid_t tid, + bool hardware +) : + StoppointLocation (loc_id, addr.GetOpcodeLoadAddress(&owner.GetTarget()), hardware), + m_being_created(true), + m_address (addr), + m_owner (owner), + m_options_ap (), + m_bp_site_sp (), + m_condition_mutex () +{ + SetThreadID (tid); + m_being_created = false; +} + +BreakpointLocation::~BreakpointLocation() +{ + ClearBreakpointSite(); +} + +lldb::addr_t +BreakpointLocation::GetLoadAddress () const +{ + return m_address.GetOpcodeLoadAddress (&m_owner.GetTarget()); +} + +Address & +BreakpointLocation::GetAddress () +{ + return m_address; +} + +Breakpoint & +BreakpointLocation::GetBreakpoint () +{ + return m_owner; +} + +bool +BreakpointLocation::IsEnabled () const +{ + if (!m_owner.IsEnabled()) + return false; + else if (m_options_ap.get() != NULL) + return m_options_ap->IsEnabled(); + else + return true; +} + +void +BreakpointLocation::SetEnabled (bool enabled) +{ + GetLocationOptions()->SetEnabled(enabled); + if (enabled) + { + ResolveBreakpointSite(); + } + else + { + ClearBreakpointSite(); + } + SendBreakpointLocationChangedEvent (enabled ? eBreakpointEventTypeEnabled : eBreakpointEventTypeDisabled); +} + +void +BreakpointLocation::SetThreadID (lldb::tid_t thread_id) +{ + if (thread_id != LLDB_INVALID_THREAD_ID) + GetLocationOptions()->SetThreadID(thread_id); + else + { + // If we're resetting this to an invalid thread id, then + // don't make an options pointer just to do that. + if (m_options_ap.get() != NULL) + m_options_ap->SetThreadID (thread_id); + } + SendBreakpointLocationChangedEvent (eBreakpointEventTypeThreadChanged); +} + +lldb::tid_t +BreakpointLocation::GetThreadID () +{ + if (GetOptionsNoCreate()->GetThreadSpecNoCreate()) + return GetOptionsNoCreate()->GetThreadSpecNoCreate()->GetTID(); + else + return LLDB_INVALID_THREAD_ID; +} + +void +BreakpointLocation::SetThreadIndex (uint32_t index) +{ + if (index != 0) + GetLocationOptions()->GetThreadSpec()->SetIndex(index); + else + { + // If we're resetting this to an invalid thread id, then + // don't make an options pointer just to do that. + if (m_options_ap.get() != NULL) + m_options_ap->GetThreadSpec()->SetIndex(index); + } + SendBreakpointLocationChangedEvent (eBreakpointEventTypeThreadChanged); + +} + +uint32_t +BreakpointLocation::GetThreadIndex() const +{ + if (GetOptionsNoCreate()->GetThreadSpecNoCreate()) + return GetOptionsNoCreate()->GetThreadSpecNoCreate()->GetIndex(); + else + return 0; +} + +void +BreakpointLocation::SetThreadName (const char *thread_name) +{ + if (thread_name != NULL) + GetLocationOptions()->GetThreadSpec()->SetName(thread_name); + else + { + // If we're resetting this to an invalid thread id, then + // don't make an options pointer just to do that. + if (m_options_ap.get() != NULL) + m_options_ap->GetThreadSpec()->SetName(thread_name); + } + SendBreakpointLocationChangedEvent (eBreakpointEventTypeThreadChanged); +} + +const char * +BreakpointLocation::GetThreadName () const +{ + if (GetOptionsNoCreate()->GetThreadSpecNoCreate()) + return GetOptionsNoCreate()->GetThreadSpecNoCreate()->GetName(); + else + return NULL; +} + +void +BreakpointLocation::SetQueueName (const char *queue_name) +{ + if (queue_name != NULL) + GetLocationOptions()->GetThreadSpec()->SetQueueName(queue_name); + else + { + // If we're resetting this to an invalid thread id, then + // don't make an options pointer just to do that. + if (m_options_ap.get() != NULL) + m_options_ap->GetThreadSpec()->SetQueueName(queue_name); + } + SendBreakpointLocationChangedEvent (eBreakpointEventTypeThreadChanged); +} + +const char * +BreakpointLocation::GetQueueName () const +{ + if (GetOptionsNoCreate()->GetThreadSpecNoCreate()) + return GetOptionsNoCreate()->GetThreadSpecNoCreate()->GetQueueName(); + else + return NULL; +} + +bool +BreakpointLocation::InvokeCallback (StoppointCallbackContext *context) +{ + if (m_options_ap.get() != NULL && m_options_ap->HasCallback()) + return m_options_ap->InvokeCallback (context, m_owner.GetID(), GetID()); + else + return m_owner.InvokeCallback (context, GetID()); +} + +void +BreakpointLocation::SetCallback (BreakpointHitCallback callback, void *baton, + bool is_synchronous) +{ + // The default "Baton" class will keep a copy of "baton" and won't free + // or delete it when it goes goes out of scope. + GetLocationOptions()->SetCallback(callback, BatonSP (new Baton(baton)), is_synchronous); + SendBreakpointLocationChangedEvent (eBreakpointEventTypeCommandChanged); +} + +void +BreakpointLocation::SetCallback (BreakpointHitCallback callback, const BatonSP &baton_sp, + bool is_synchronous) +{ + GetLocationOptions()->SetCallback (callback, baton_sp, is_synchronous); + SendBreakpointLocationChangedEvent (eBreakpointEventTypeCommandChanged); +} + + +void +BreakpointLocation::ClearCallback () +{ + GetLocationOptions()->ClearCallback(); +} + +void +BreakpointLocation::SetCondition (const char *condition) +{ + GetLocationOptions()->SetCondition (condition); + SendBreakpointLocationChangedEvent (eBreakpointEventTypeConditionChanged); +} + +const char * +BreakpointLocation::GetConditionText (size_t *hash) const +{ + return GetOptionsNoCreate()->GetConditionText(hash); +} + +bool +BreakpointLocation::ConditionSaysStop (ExecutionContext &exe_ctx, Error &error) +{ + Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_BREAKPOINTS); + + Mutex::Locker evaluation_locker(m_condition_mutex); + + size_t condition_hash; + const char *condition_text = GetConditionText(&condition_hash); + + if (!condition_text) + { + m_user_expression_sp.reset(); + return false; + } + + if (condition_hash != m_condition_hash || + !m_user_expression_sp || + !m_user_expression_sp->MatchesContext(exe_ctx)) + { + m_user_expression_sp.reset(new ClangUserExpression(condition_text, + NULL, + lldb::eLanguageTypeUnknown, + ClangUserExpression::eResultTypeAny)); + + StreamString errors; + + if (!m_user_expression_sp->Parse(errors, + exe_ctx, + eExecutionPolicyOnlyWhenNeeded, + true)) + { + error.SetErrorStringWithFormat("Couldn't parse conditional expression:\n%s", + errors.GetData()); + m_user_expression_sp.reset(); + return false; + } + + m_condition_hash = condition_hash; + } + + // We need to make sure the user sees any parse errors in their condition, so we'll hook the + // constructor errors up to the debugger's Async I/O. + + ValueObjectSP result_value_sp; + const bool unwind_on_error = true; + const bool ignore_breakpoints = true; + const bool try_all_threads = true; + + Error expr_error; + + StreamString execution_errors; + + ClangExpressionVariableSP result_variable_sp; + + ExecutionResults result_code = + m_user_expression_sp->Execute(execution_errors, + exe_ctx, + unwind_on_error, + ignore_breakpoints, + m_user_expression_sp, + result_variable_sp, + try_all_threads, + ClangUserExpression::kDefaultTimeout); + + bool ret; + + if (result_code == eExecutionCompleted) + { + if (!result_variable_sp) + { + ret = false; + error.SetErrorString("Expression did not return a result"); + return false; + } + + result_value_sp = result_variable_sp->GetValueObject(); + + if (result_value_sp) + { + Scalar scalar_value; + if (result_value_sp->ResolveValue (scalar_value)) + { + if (scalar_value.ULongLong(1) == 0) + ret = false; + else + ret = true; + if (log) + log->Printf("Condition successfully evaluated, result is %s.\n", + ret ? "true" : "false"); + } + else + { + ret = false; + error.SetErrorString("Failed to get an integer result from the expression"); + } + } + else + { + ret = false; + error.SetErrorString("Failed to get any result from the expression"); + } + } + else + { + ret = false; + error.SetErrorStringWithFormat("Couldn't execute expression:\n%s", execution_errors.GetData()); + } + + return ret; +} + +uint32_t +BreakpointLocation::GetIgnoreCount () +{ + return GetOptionsNoCreate()->GetIgnoreCount(); +} + +void +BreakpointLocation::SetIgnoreCount (uint32_t n) +{ + GetLocationOptions()->SetIgnoreCount(n); + SendBreakpointLocationChangedEvent (eBreakpointEventTypeIgnoreChanged); +} + +void +BreakpointLocation::DecrementIgnoreCount() +{ + if (m_options_ap.get() != NULL) + { + uint32_t loc_ignore = m_options_ap->GetIgnoreCount(); + if (loc_ignore != 0) + m_options_ap->SetIgnoreCount(loc_ignore - 1); + } +} + +bool +BreakpointLocation::IgnoreCountShouldStop() +{ + if (m_options_ap.get() != NULL) + { + uint32_t loc_ignore = m_options_ap->GetIgnoreCount(); + if (loc_ignore != 0) + { + m_owner.DecrementIgnoreCount(); + DecrementIgnoreCount(); // Have to decrement our owners' ignore count, since it won't get a + // chance to. + return false; + } + } + return true; +} + +const BreakpointOptions * +BreakpointLocation::GetOptionsNoCreate () const +{ + if (m_options_ap.get() != NULL) + return m_options_ap.get(); + else + return m_owner.GetOptions (); +} + +BreakpointOptions * +BreakpointLocation::GetLocationOptions () +{ + // If we make the copy we don't copy the callbacks because that is potentially + // expensive and we don't want to do that for the simple case where someone is + // just disabling the location. + if (m_options_ap.get() == NULL) + m_options_ap.reset(BreakpointOptions::CopyOptionsNoCallback(*m_owner.GetOptions ())); + + return m_options_ap.get(); +} + +bool +BreakpointLocation::ValidForThisThread (Thread *thread) +{ + return thread->MatchesSpec(GetOptionsNoCreate()->GetThreadSpecNoCreate()); +} + +// RETURNS - true if we should stop at this breakpoint, false if we +// should continue. Note, we don't check the thread spec for the breakpoint +// here, since if the breakpoint is not for this thread, then the event won't +// even get reported, so the check is redundant. + +bool +BreakpointLocation::ShouldStop (StoppointCallbackContext *context) +{ + bool should_stop = true; + Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_BREAKPOINTS); + + IncrementHitCount(); + + if (!IsEnabled()) + return false; + + if (!IgnoreCountShouldStop()) + return false; + + if (!m_owner.IgnoreCountShouldStop()) + return false; + + // We only run synchronous callbacks in ShouldStop: + context->is_synchronous = true; + should_stop = InvokeCallback (context); + + if (log) + { + StreamString s; + GetDescription (&s, lldb::eDescriptionLevelVerbose); + log->Printf ("Hit breakpoint location: %s, %s.\n", s.GetData(), should_stop ? "stopping" : "continuing"); + } + + return should_stop; +} + +bool +BreakpointLocation::IsResolved () const +{ + return m_bp_site_sp.get() != NULL; +} + +lldb::BreakpointSiteSP +BreakpointLocation::GetBreakpointSite() const +{ + return m_bp_site_sp; +} + +bool +BreakpointLocation::ResolveBreakpointSite () +{ + if (m_bp_site_sp) + return true; + + Process *process = m_owner.GetTarget().GetProcessSP().get(); + if (process == NULL) + return false; + + lldb::break_id_t new_id = process->CreateBreakpointSite (shared_from_this(), false); + + if (new_id == LLDB_INVALID_BREAK_ID) + { + Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_BREAKPOINTS); + if (log) + log->Warning ("Tried to add breakpoint site at 0x%" PRIx64 " but it was already present.\n", + m_address.GetOpcodeLoadAddress (&m_owner.GetTarget())); + return false; + } + + return true; +} + +bool +BreakpointLocation::SetBreakpointSite (BreakpointSiteSP& bp_site_sp) +{ + m_bp_site_sp = bp_site_sp; + return true; +} + +bool +BreakpointLocation::ClearBreakpointSite () +{ + if (m_bp_site_sp.get()) + { + m_owner.GetTarget().GetProcessSP()->RemoveOwnerFromBreakpointSite (GetBreakpoint().GetID(), + GetID(), m_bp_site_sp); + m_bp_site_sp.reset(); + return true; + } + return false; +} + +void +BreakpointLocation::GetDescription (Stream *s, lldb::DescriptionLevel level) +{ + SymbolContext sc; + + // If the description level is "initial" then the breakpoint is printing out our initial state, + // and we should let it decide how it wants to print our label. + if (level != eDescriptionLevelInitial) + { + s->Indent(); + BreakpointID::GetCanonicalReference(s, m_owner.GetID(), GetID()); + } + + if (level == lldb::eDescriptionLevelBrief) + return; + + if (level != eDescriptionLevelInitial) + s->PutCString(": "); + + if (level == lldb::eDescriptionLevelVerbose) + s->IndentMore(); + + if (m_address.IsSectionOffset()) + { + m_address.CalculateSymbolContext(&sc); + + if (level == lldb::eDescriptionLevelFull || level == eDescriptionLevelInitial) + { + s->PutCString("where = "); + sc.DumpStopContext (s, m_owner.GetTarget().GetProcessSP().get(), m_address, false, true, false); + } + else + { + if (sc.module_sp) + { + s->EOL(); + s->Indent("module = "); + sc.module_sp->GetFileSpec().Dump (s); + } + + if (sc.comp_unit != NULL) + { + s->EOL(); + s->Indent("compile unit = "); + static_cast(sc.comp_unit)->GetFilename().Dump (s); + + if (sc.function != NULL) + { + s->EOL(); + s->Indent("function = "); + s->PutCString (sc.function->GetMangled().GetName().AsCString("")); + } + + if (sc.line_entry.line > 0) + { + s->EOL(); + s->Indent("location = "); + sc.line_entry.DumpStopContext (s, true); + } + + } + else + { + // If we don't have a comp unit, see if we have a symbol we can print. + if (sc.symbol) + { + s->EOL(); + s->Indent("symbol = "); + s->PutCString(sc.symbol->GetMangled().GetName().AsCString("")); + } + } + } + } + + if (level == lldb::eDescriptionLevelVerbose) + { + s->EOL(); + s->Indent(); + } + + if (m_address.IsSectionOffset() && (level == eDescriptionLevelFull || level == eDescriptionLevelInitial)) + s->Printf (", "); + s->Printf ("address = "); + + ExecutionContextScope *exe_scope = NULL; + Target *target = &m_owner.GetTarget(); + if (target) + exe_scope = target->GetProcessSP().get(); + if (exe_scope == NULL) + exe_scope = target; + + if (eDescriptionLevelInitial) + m_address.Dump(s, exe_scope, Address::DumpStyleLoadAddress, Address::DumpStyleFileAddress); + else + m_address.Dump(s, exe_scope, Address::DumpStyleLoadAddress, Address::DumpStyleModuleWithFileAddress); + + if (level == lldb::eDescriptionLevelVerbose) + { + s->EOL(); + s->Indent(); + s->Printf("resolved = %s\n", IsResolved() ? "true" : "false"); + + s->Indent(); + s->Printf ("hit count = %-4u\n", GetHitCount()); + + if (m_options_ap.get()) + { + s->Indent(); + m_options_ap->GetDescription (s, level); + s->EOL(); + } + s->IndentLess(); + } + else if (level != eDescriptionLevelInitial) + { + s->Printf(", %sresolved, hit count = %u ", + (IsResolved() ? "" : "un"), + GetHitCount()); + if (m_options_ap.get()) + { + m_options_ap->GetDescription (s, level); + } + } +} + +void +BreakpointLocation::Dump(Stream *s) const +{ + if (s == NULL) + return; + + s->Printf("BreakpointLocation %u: tid = %4.4" PRIx64 " load addr = 0x%8.8" PRIx64 " state = %s type = %s breakpoint " + "hw_index = %i hit_count = %-4u ignore_count = %-4u", + GetID(), + GetOptionsNoCreate()->GetThreadSpecNoCreate()->GetTID(), + (uint64_t) m_address.GetOpcodeLoadAddress (&m_owner.GetTarget()), + (m_options_ap.get() ? m_options_ap->IsEnabled() : m_owner.IsEnabled()) ? "enabled " : "disabled", + IsHardware() ? "hardware" : "software", + GetHardwareIndex(), + GetHitCount(), + GetOptionsNoCreate()->GetIgnoreCount()); +} + +void +BreakpointLocation::SendBreakpointLocationChangedEvent (lldb::BreakpointEventType eventKind) +{ + if (!m_being_created + && !m_owner.IsInternal() + && m_owner.GetTarget().EventTypeHasListeners(Target::eBroadcastBitBreakpointChanged)) + { + Breakpoint::BreakpointEventData *data = new Breakpoint::BreakpointEventData (eventKind, + m_owner.shared_from_this()); + data->GetBreakpointLocationCollection().Add (shared_from_this()); + m_owner.GetTarget().BroadcastEvent (Target::eBroadcastBitBreakpointChanged, data); + } +} + diff --git a/contrib/llvm/tools/lldb/source/Breakpoint/BreakpointLocationCollection.cpp b/contrib/llvm/tools/lldb/source/Breakpoint/BreakpointLocationCollection.cpp new file mode 100644 index 00000000000..ee3f56f928d --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Breakpoint/BreakpointLocationCollection.cpp @@ -0,0 +1,198 @@ +//===-- BreakpointLocationCollection.cpp ------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Breakpoint/BreakpointLocationCollection.h" +#include "lldb/Core/ModuleList.h" +#include "lldb/Breakpoint/Breakpoint.h" +#include "lldb/Breakpoint/BreakpointLocation.h" +#include "lldb/Target/Thread.h" +#include "lldb/Target/ThreadSpec.h" + +using namespace lldb; +using namespace lldb_private; + +//---------------------------------------------------------------------- +// BreakpointLocationCollection constructor +//---------------------------------------------------------------------- +BreakpointLocationCollection::BreakpointLocationCollection() : + m_break_loc_collection() +{ +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +BreakpointLocationCollection::~BreakpointLocationCollection() +{ +} + +void +BreakpointLocationCollection::Add(const BreakpointLocationSP &bp_loc) +{ + BreakpointLocationSP old_bp_loc = FindByIDPair (bp_loc->GetBreakpoint().GetID(), bp_loc->GetID()); + if (!old_bp_loc.get()) + m_break_loc_collection.push_back(bp_loc); +} + +bool +BreakpointLocationCollection::Remove (lldb::break_id_t bp_id, lldb::break_id_t bp_loc_id) +{ + collection::iterator pos = GetIDPairIterator(bp_id, bp_loc_id); // Predicate + if (pos != m_break_loc_collection.end()) + { + m_break_loc_collection.erase(pos); + return true; + } + return false; + +} + +class BreakpointIDPairMatches +{ +public: + BreakpointIDPairMatches (lldb::break_id_t break_id, lldb::break_id_t break_loc_id) : + m_break_id(break_id), + m_break_loc_id (break_loc_id) + { + } + + bool operator() (const BreakpointLocationSP &bp_loc) const + { + return m_break_id == bp_loc->GetBreakpoint().GetID() + && m_break_loc_id == bp_loc->GetID(); + } + +private: + const lldb::break_id_t m_break_id; + const lldb::break_id_t m_break_loc_id; +}; + +BreakpointLocationCollection::collection::iterator +BreakpointLocationCollection::GetIDPairIterator (lldb::break_id_t break_id, lldb::break_id_t break_loc_id) +{ + return std::find_if(m_break_loc_collection.begin(), m_break_loc_collection.end(), // Search full range + BreakpointIDPairMatches(break_id, break_loc_id)); // Predicate +} + +BreakpointLocationCollection::collection::const_iterator +BreakpointLocationCollection::GetIDPairConstIterator (lldb::break_id_t break_id, lldb::break_id_t break_loc_id) const +{ + return std::find_if(m_break_loc_collection.begin(), m_break_loc_collection.end(), // Search full range + BreakpointIDPairMatches(break_id, break_loc_id)); // Predicate +} + +BreakpointLocationSP +BreakpointLocationCollection::FindByIDPair (lldb::break_id_t break_id, lldb::break_id_t break_loc_id) +{ + BreakpointLocationSP stop_sp; + collection::iterator pos = GetIDPairIterator(break_id, break_loc_id); + if (pos != m_break_loc_collection.end()) + stop_sp = *pos; + + return stop_sp; +} + +const BreakpointLocationSP +BreakpointLocationCollection::FindByIDPair (lldb::break_id_t break_id, lldb::break_id_t break_loc_id) const +{ + BreakpointLocationSP stop_sp; + collection::const_iterator pos = GetIDPairConstIterator(break_id, break_loc_id); + if (pos != m_break_loc_collection.end()) + stop_sp = *pos; + + return stop_sp; +} + +BreakpointLocationSP +BreakpointLocationCollection::GetByIndex (size_t i) +{ + BreakpointLocationSP stop_sp; + if (i < m_break_loc_collection.size()) + stop_sp = m_break_loc_collection[i]; + + return stop_sp; +} + +const BreakpointLocationSP +BreakpointLocationCollection::GetByIndex (size_t i) const +{ + BreakpointLocationSP stop_sp; + if (i < m_break_loc_collection.size()) + stop_sp = m_break_loc_collection[i]; + + return stop_sp; +} + +bool +BreakpointLocationCollection::ShouldStop (StoppointCallbackContext *context) +{ + bool shouldStop = false; + const size_t count = GetSize(); + for (size_t i = 0; i < count; i++) + { + if (GetByIndex(i)->ShouldStop(context)) + shouldStop = true; + } + return shouldStop; +} + +bool +BreakpointLocationCollection::ValidForThisThread (Thread *thread) +{ + collection::iterator pos, + begin = m_break_loc_collection.begin(), + end = m_break_loc_collection.end(); + + for (pos = begin; pos != end; ++pos) + { + if ((*pos)->ValidForThisThread (thread)) + return true; + } + return false; +} + +bool +BreakpointLocationCollection::IsInternal () const +{ + collection::const_iterator pos, + begin = m_break_loc_collection.begin(), + end = m_break_loc_collection.end(); + + bool is_internal = true; + + for (pos = begin; pos != end; ++pos) + { + if (!(*pos)->GetBreakpoint().IsInternal ()) + { + is_internal = false; + break; + } + } + return is_internal; +} + +void +BreakpointLocationCollection::GetDescription (Stream *s, lldb::DescriptionLevel level) +{ + collection::iterator pos, + begin = m_break_loc_collection.begin(), + end = m_break_loc_collection.end(); + + for (pos = begin; pos != end; ++pos) + { + if (pos != begin) + s->PutChar(' '); + (*pos)->GetDescription(s, level); + } +} diff --git a/contrib/llvm/tools/lldb/source/Breakpoint/BreakpointLocationList.cpp b/contrib/llvm/tools/lldb/source/Breakpoint/BreakpointLocationList.cpp new file mode 100644 index 00000000000..22a4ff0c68e --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Breakpoint/BreakpointLocationList.cpp @@ -0,0 +1,305 @@ +//===-- BreakpointLocationList.cpp ------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Breakpoint/BreakpointLocationList.h" +#include "lldb/Breakpoint/BreakpointLocation.h" +#include "lldb/Breakpoint/Breakpoint.h" +#include "lldb/Core/Section.h" +#include "lldb/Target/Target.h" + +using namespace lldb; +using namespace lldb_private; + +BreakpointLocationList::BreakpointLocationList(Breakpoint &owner) : + m_owner (owner), + m_locations(), + m_address_to_location (), + m_mutex (Mutex::eMutexTypeRecursive), + m_next_id (0), + m_new_location_recorder (NULL) +{ +} + +BreakpointLocationList::~BreakpointLocationList() +{ +} + +BreakpointLocationSP +BreakpointLocationList::Create (const Address &addr) +{ + Mutex::Locker locker (m_mutex); + // The location ID is just the size of the location list + 1 + lldb::break_id_t bp_loc_id = ++m_next_id; + BreakpointLocationSP bp_loc_sp (new BreakpointLocation (bp_loc_id, m_owner, addr)); + m_locations.push_back (bp_loc_sp); + m_address_to_location[addr] = bp_loc_sp; + return bp_loc_sp; +} + +bool +BreakpointLocationList::ShouldStop (StoppointCallbackContext *context, lldb::break_id_t break_id) +{ + BreakpointLocationSP bp = FindByID (break_id); + if (bp) + { + // Let the BreakpointLocation decide if it should stop here (could not have + // reached it's target hit count yet, or it could have a callback + // that decided it shouldn't stop (shared library loads/unloads). + return bp->ShouldStop (context); + } + // We should stop here since this BreakpointLocation isn't valid anymore or it + // doesn't exist. + return true; +} + +lldb::break_id_t +BreakpointLocationList::FindIDByAddress (const Address &addr) +{ + BreakpointLocationSP bp_loc_sp = FindByAddress (addr); + if (bp_loc_sp) + { + return bp_loc_sp->GetID(); + } + return LLDB_INVALID_BREAK_ID; +} + +BreakpointLocationSP +BreakpointLocationList::FindByID (lldb::break_id_t break_id) const +{ + BreakpointLocationSP bp_loc_sp; + Mutex::Locker locker (m_mutex); + // We never remove a breakpoint locations, so the ID can be translated into + // the location index by subtracting 1 + uint32_t idx = break_id - 1; + if (idx <= m_locations.size()) + { + bp_loc_sp = m_locations[idx]; + } + return bp_loc_sp; +} + +size_t +BreakpointLocationList::FindInModule (Module *module, + BreakpointLocationCollection& bp_loc_list) +{ + Mutex::Locker locker (m_mutex); + const size_t orig_size = bp_loc_list.GetSize(); + collection::iterator pos, end = m_locations.end(); + + for (pos = m_locations.begin(); pos != end; ++pos) + { + BreakpointLocationSP break_loc = (*pos); + SectionSP section_sp (break_loc->GetAddress().GetSection()); + if (section_sp && section_sp->GetModule().get() == module) + { + bp_loc_list.Add (break_loc); + } + } + return bp_loc_list.GetSize() - orig_size; +} + +const BreakpointLocationSP +BreakpointLocationList::FindByAddress (const Address &addr) const +{ + Mutex::Locker locker (m_mutex); + BreakpointLocationSP bp_loc_sp; + if (!m_locations.empty()) + { + Address so_addr; + + if (addr.IsSectionOffset()) + { + so_addr = addr; + } + else + { + // Try and resolve as a load address if possible. + m_owner.GetTarget().GetSectionLoadList().ResolveLoadAddress (addr.GetOffset(), so_addr); + if (!so_addr.IsValid()) + { + // The address didn't resolve, so just set to passed in addr. + so_addr = addr; + } + } + + addr_map::const_iterator pos = m_address_to_location.find (so_addr); + if (pos != m_address_to_location.end()) + bp_loc_sp = pos->second; + } + + return bp_loc_sp; +} + +void +BreakpointLocationList::Dump (Stream *s) const +{ + s->Printf("%p: ", this); + //s->Indent(); + Mutex::Locker locker (m_mutex); + s->Printf("BreakpointLocationList with %" PRIu64 " BreakpointLocations:\n", (uint64_t)m_locations.size()); + s->IndentMore(); + collection::const_iterator pos, end = m_locations.end(); + for (pos = m_locations.begin(); pos != end; ++pos) + (*pos).get()->Dump(s); + s->IndentLess(); +} + + +BreakpointLocationSP +BreakpointLocationList::GetByIndex (size_t i) +{ + Mutex::Locker locker (m_mutex); + BreakpointLocationSP bp_loc_sp; + if (i < m_locations.size()) + bp_loc_sp = m_locations[i]; + + return bp_loc_sp; +} + +const BreakpointLocationSP +BreakpointLocationList::GetByIndex (size_t i) const +{ + Mutex::Locker locker (m_mutex); + BreakpointLocationSP bp_loc_sp; + if (i < m_locations.size()) + bp_loc_sp = m_locations[i]; + + return bp_loc_sp; +} + +void +BreakpointLocationList::ClearAllBreakpointSites () +{ + Mutex::Locker locker (m_mutex); + collection::iterator pos, end = m_locations.end(); + for (pos = m_locations.begin(); pos != end; ++pos) + (*pos)->ClearBreakpointSite(); +} + +void +BreakpointLocationList::ResolveAllBreakpointSites () +{ + Mutex::Locker locker (m_mutex); + collection::iterator pos, end = m_locations.end(); + + for (pos = m_locations.begin(); pos != end; ++pos) + { + if ((*pos)->IsEnabled()) + (*pos)->ResolveBreakpointSite(); + } +} + +uint32_t +BreakpointLocationList::GetHitCount () const +{ + uint32_t hit_count = 0; + Mutex::Locker locker (m_mutex); + collection::const_iterator pos, end = m_locations.end(); + for (pos = m_locations.begin(); pos != end; ++pos) + hit_count += (*pos)->GetHitCount(); + return hit_count; +} + +size_t +BreakpointLocationList::GetNumResolvedLocations() const +{ + Mutex::Locker locker (m_mutex); + size_t resolve_count = 0; + collection::const_iterator pos, end = m_locations.end(); + for (pos = m_locations.begin(); pos != end; ++pos) + { + if ((*pos)->IsResolved()) + ++resolve_count; + } + return resolve_count; +} + +void +BreakpointLocationList::GetDescription (Stream *s, lldb::DescriptionLevel level) +{ + Mutex::Locker locker (m_mutex); + collection::iterator pos, end = m_locations.end(); + + for (pos = m_locations.begin(); pos != end; ++pos) + { + s->Printf(" "); + (*pos)->GetDescription(s, level); + } +} + +BreakpointLocationSP +BreakpointLocationList::AddLocation (const Address &addr, bool *new_location) +{ + Mutex::Locker locker (m_mutex); + + if (new_location) + *new_location = false; + BreakpointLocationSP bp_loc_sp (FindByAddress(addr)); + if (!bp_loc_sp) + { + bp_loc_sp = Create (addr); + if (bp_loc_sp) + { + bp_loc_sp->ResolveBreakpointSite(); + + if (new_location) + *new_location = true; + if(m_new_location_recorder) + { + m_new_location_recorder->Add(bp_loc_sp); + } + } + } + return bp_loc_sp; +} + +bool +BreakpointLocationList::RemoveLocation (const lldb::BreakpointLocationSP &bp_loc_sp) +{ + if (bp_loc_sp) + { + Mutex::Locker locker (m_mutex); + + m_address_to_location.erase (bp_loc_sp->GetAddress()); + + collection::iterator pos, end = m_locations.end(); + for (pos = m_locations.begin(); pos != end; ++pos) + { + if ((*pos).get() == bp_loc_sp.get()) + { + m_locations.erase (pos); + return true; + } + } + } + return false; +} + + + +void +BreakpointLocationList::StartRecordingNewLocations (BreakpointLocationCollection &new_locations) +{ + Mutex::Locker locker (m_mutex); + assert (m_new_location_recorder == NULL); + m_new_location_recorder = &new_locations; +} + +void +BreakpointLocationList::StopRecordingNewLocations () +{ + Mutex::Locker locker (m_mutex); + m_new_location_recorder = NULL; +} + diff --git a/contrib/llvm/tools/lldb/source/Breakpoint/BreakpointOptions.cpp b/contrib/llvm/tools/lldb/source/Breakpoint/BreakpointOptions.cpp new file mode 100644 index 00000000000..3a4a117695f --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Breakpoint/BreakpointOptions.cpp @@ -0,0 +1,298 @@ +//===-- BreakpointOptions.cpp -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Breakpoint/BreakpointOptions.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/Stream.h" +#include "lldb/Core/StringList.h" +#include "lldb/Core/Value.h" +#include "lldb/Breakpoint/StoppointCallbackContext.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/ThreadSpec.h" +#include "lldb/Expression/ClangUserExpression.h" + +using namespace lldb; +using namespace lldb_private; + +bool +BreakpointOptions::NullCallback (void *baton, StoppointCallbackContext *context, lldb::user_id_t break_id, lldb::user_id_t break_loc_id) +{ + return true; +} + +//---------------------------------------------------------------------- +// BreakpointOptions constructor +//---------------------------------------------------------------------- +BreakpointOptions::BreakpointOptions() : + m_callback (BreakpointOptions::NullCallback), + m_callback_baton_sp (), + m_callback_is_synchronous (false), + m_enabled (true), + m_one_shot (false), + m_ignore_count (0), + m_thread_spec_ap (), + m_condition_text (), + m_condition_text_hash (0) +{ +} + +//---------------------------------------------------------------------- +// BreakpointOptions copy constructor +//---------------------------------------------------------------------- +BreakpointOptions::BreakpointOptions(const BreakpointOptions& rhs) : + m_callback (rhs.m_callback), + m_callback_baton_sp (rhs.m_callback_baton_sp), + m_callback_is_synchronous (rhs.m_callback_is_synchronous), + m_enabled (rhs.m_enabled), + m_one_shot (rhs.m_one_shot), + m_ignore_count (rhs.m_ignore_count), + m_thread_spec_ap () +{ + if (rhs.m_thread_spec_ap.get() != NULL) + m_thread_spec_ap.reset (new ThreadSpec(*rhs.m_thread_spec_ap.get())); + m_condition_text = rhs.m_condition_text; + m_condition_text_hash = rhs.m_condition_text_hash; +} + +//---------------------------------------------------------------------- +// BreakpointOptions assignment operator +//---------------------------------------------------------------------- +const BreakpointOptions& +BreakpointOptions::operator=(const BreakpointOptions& rhs) +{ + m_callback = rhs.m_callback; + m_callback_baton_sp = rhs.m_callback_baton_sp; + m_callback_is_synchronous = rhs.m_callback_is_synchronous; + m_enabled = rhs.m_enabled; + m_one_shot = rhs.m_one_shot; + m_ignore_count = rhs.m_ignore_count; + if (rhs.m_thread_spec_ap.get() != NULL) + m_thread_spec_ap.reset(new ThreadSpec(*rhs.m_thread_spec_ap.get())); + m_condition_text = rhs.m_condition_text; + m_condition_text_hash = rhs.m_condition_text_hash; + return *this; +} + +BreakpointOptions * +BreakpointOptions::CopyOptionsNoCallback (BreakpointOptions &orig) +{ + BreakpointHitCallback orig_callback = orig.m_callback; + lldb::BatonSP orig_callback_baton_sp = orig.m_callback_baton_sp; + bool orig_is_sync = orig.m_callback_is_synchronous; + + orig.ClearCallback(); + BreakpointOptions *ret_val = new BreakpointOptions(orig); + + orig.SetCallback (orig_callback, orig_callback_baton_sp, orig_is_sync); + + return ret_val; +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +BreakpointOptions::~BreakpointOptions() +{ +} + +//------------------------------------------------------------------ +// Callbacks +//------------------------------------------------------------------ +void +BreakpointOptions::SetCallback (BreakpointHitCallback callback, const BatonSP &callback_baton_sp, bool callback_is_synchronous) +{ + m_callback_is_synchronous = callback_is_synchronous; + m_callback = callback; + m_callback_baton_sp = callback_baton_sp; +} + +void +BreakpointOptions::ClearCallback () +{ + m_callback = BreakpointOptions::NullCallback; + m_callback_is_synchronous = false; + m_callback_baton_sp.reset(); +} + +Baton * +BreakpointOptions::GetBaton () +{ + return m_callback_baton_sp.get(); +} + +const Baton * +BreakpointOptions::GetBaton () const +{ + return m_callback_baton_sp.get(); +} + +bool +BreakpointOptions::InvokeCallback (StoppointCallbackContext *context, + lldb::user_id_t break_id, + lldb::user_id_t break_loc_id) +{ + if (m_callback && context->is_synchronous == IsCallbackSynchronous()) + { + return m_callback (m_callback_baton_sp ? m_callback_baton_sp->m_data : NULL, + context, + break_id, + break_loc_id); + } + else + return true; +} + +bool +BreakpointOptions::HasCallback () +{ + return m_callback != BreakpointOptions::NullCallback; +} + +void +BreakpointOptions::SetCondition (const char *condition) +{ + if (!condition) + condition = ""; + + m_condition_text.assign(condition); + std::hash hasher; + m_condition_text_hash = hasher(m_condition_text); +} + +const char * +BreakpointOptions::GetConditionText (size_t *hash) const +{ + if (!m_condition_text.empty()) + { + if (hash) + *hash = m_condition_text_hash; + + return m_condition_text.c_str(); + } + else + { + return NULL; + } +} + +const ThreadSpec * +BreakpointOptions::GetThreadSpecNoCreate () const +{ + return m_thread_spec_ap.get(); +} + +ThreadSpec * +BreakpointOptions::GetThreadSpec () +{ + if (m_thread_spec_ap.get() == NULL) + m_thread_spec_ap.reset (new ThreadSpec()); + + return m_thread_spec_ap.get(); +} + +void +BreakpointOptions::SetThreadID (lldb::tid_t thread_id) +{ + GetThreadSpec()->SetTID(thread_id); +} + +void +BreakpointOptions::GetDescription (Stream *s, lldb::DescriptionLevel level) const +{ + + // Figure out if there are any options not at their default value, and only print + // anything if there are: + + if (m_ignore_count != 0 || !m_enabled || m_one_shot || (GetThreadSpecNoCreate() != NULL && GetThreadSpecNoCreate()->HasSpecification ())) + { + if (level == lldb::eDescriptionLevelVerbose) + { + s->EOL (); + s->IndentMore(); + s->Indent(); + s->PutCString("Breakpoint Options:\n"); + s->IndentMore(); + s->Indent(); + } + else + s->PutCString(" Options: "); + + if (m_ignore_count > 0) + s->Printf("ignore: %d ", m_ignore_count); + s->Printf("%sabled ", m_enabled ? "en" : "dis"); + + if (m_one_shot) + s->Printf ("one-shot "); + + if (m_thread_spec_ap.get()) + m_thread_spec_ap->GetDescription (s, level); + else if (level == eDescriptionLevelBrief) + s->PutCString ("thread spec: no "); + if (level == lldb::eDescriptionLevelFull) + { + s->IndentLess(); + s->IndentMore(); + } + } + + if (m_callback_baton_sp.get()) + { + if (level != eDescriptionLevelBrief) + { + s->EOL(); + m_callback_baton_sp->GetDescription (s, level); + } + } + if (!m_condition_text.empty()) + { + if (level != eDescriptionLevelBrief) + { + s->EOL(); + s->Printf("Condition: %s\n", m_condition_text.c_str()); + } + } +} + +void +BreakpointOptions::CommandBaton::GetDescription (Stream *s, lldb::DescriptionLevel level) const +{ + CommandData *data = (CommandData *)m_data; + + if (level == eDescriptionLevelBrief) + { + s->Printf (", commands = %s", (data && data->user_source.GetSize() > 0) ? "yes" : "no"); + return; + } + + s->IndentMore (); + s->Indent("Breakpoint commands:\n"); + + s->IndentMore (); + if (data && data->user_source.GetSize() > 0) + { + const size_t num_strings = data->user_source.GetSize(); + for (size_t i = 0; i < num_strings; ++i) + { + s->Indent(data->user_source.GetStringAtIndex(i)); + s->EOL(); + } + } + else + { + s->PutCString ("No commands.\n"); + } + s->IndentLess (); + s->IndentLess (); +} + diff --git a/contrib/llvm/tools/lldb/source/Breakpoint/BreakpointResolver.cpp b/contrib/llvm/tools/lldb/source/Breakpoint/BreakpointResolver.cpp new file mode 100644 index 00000000000..b22fa1e6dbc --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Breakpoint/BreakpointResolver.cpp @@ -0,0 +1,61 @@ +//===-- BreakpointResolver.cpp ----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Breakpoint/BreakpointResolver.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/Address.h" +#include "lldb/Breakpoint/Breakpoint.h" +#include "lldb/Breakpoint/BreakpointLocation.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/ModuleList.h" +#include "lldb/Core/SearchFilter.h" +#include "lldb/Core/Stream.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Symbol/SymbolContext.h" +#include "lldb/Target/Target.h" +#include "lldb/lldb-private-log.h" + +using namespace lldb_private; + +//---------------------------------------------------------------------- +// BreakpointResolver: +//---------------------------------------------------------------------- +BreakpointResolver::BreakpointResolver (Breakpoint *bkpt, const unsigned char resolverTy) : + m_breakpoint (bkpt), + SubclassID (resolverTy) +{ +} + +BreakpointResolver::~BreakpointResolver () +{ + +} + +void +BreakpointResolver::SetBreakpoint (Breakpoint *bkpt) +{ + m_breakpoint = bkpt; +} + +void +BreakpointResolver::ResolveBreakpointInModules (SearchFilter &filter, ModuleList &modules) +{ + filter.SearchInModuleList(*this, modules); +} + +void +BreakpointResolver::ResolveBreakpoint (SearchFilter &filter) +{ + filter.Search (*this); +} + diff --git a/contrib/llvm/tools/lldb/source/Breakpoint/BreakpointResolverAddress.cpp b/contrib/llvm/tools/lldb/source/Breakpoint/BreakpointResolverAddress.cpp new file mode 100644 index 00000000000..1bcef93aeda --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Breakpoint/BreakpointResolverAddress.cpp @@ -0,0 +1,111 @@ +//===-- BreakpointResolverAddress.cpp ---------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Breakpoint/BreakpointResolverAddress.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private-log.h" + +#include "lldb/Breakpoint/BreakpointLocation.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Target/Target.h" + +using namespace lldb; +using namespace lldb_private; + +//---------------------------------------------------------------------- +// BreakpointResolverAddress: +//---------------------------------------------------------------------- +BreakpointResolverAddress::BreakpointResolverAddress +( + Breakpoint *bkpt, + const Address &addr +) : + BreakpointResolver (bkpt, BreakpointResolver::AddressResolver), + m_addr (addr) +{ +} + +BreakpointResolverAddress::~BreakpointResolverAddress () +{ + +} + +void +BreakpointResolverAddress::ResolveBreakpoint (SearchFilter &filter) +{ + // The address breakpoint only takes once, so if we've already set it we're done. + if (m_breakpoint->GetNumLocations() > 0) + return; + else + BreakpointResolver::ResolveBreakpoint(filter); +} + +void +BreakpointResolverAddress::ResolveBreakpointInModules +( + SearchFilter &filter, + ModuleList &modules +) +{ + // The address breakpoint only takes once, so if we've already set it we're done. + if (m_breakpoint->GetNumLocations() > 0) + return; + else + BreakpointResolver::ResolveBreakpointInModules (filter, modules); +} + +Searcher::CallbackReturn +BreakpointResolverAddress::SearchCallback +( + SearchFilter &filter, + SymbolContext &context, + Address *addr, + bool containing +) +{ + assert (m_breakpoint != NULL); + + if (filter.AddressPasses (m_addr)) + { + BreakpointLocationSP bp_loc_sp(m_breakpoint->AddLocation(m_addr)); + if (bp_loc_sp && !m_breakpoint->IsInternal()) + { + StreamString s; + bp_loc_sp->GetDescription(&s, lldb::eDescriptionLevelVerbose); + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_BREAKPOINTS)); + if (log) + log->Printf ("Added location: %s\n", s.GetData()); + } + } + return Searcher::eCallbackReturnStop; +} + +Searcher::Depth +BreakpointResolverAddress::GetDepth() +{ + return Searcher::eDepthTarget; +} + +void +BreakpointResolverAddress::GetDescription (Stream *s) +{ + s->PutCString ("address = "); + m_addr.Dump(s, m_breakpoint->GetTarget().GetProcessSP().get(), Address::DumpStyleLoadAddress, Address::DumpStyleModuleWithFileAddress); +} + +void +BreakpointResolverAddress::Dump (Stream *s) const +{ + +} diff --git a/contrib/llvm/tools/lldb/source/Breakpoint/BreakpointResolverFileLine.cpp b/contrib/llvm/tools/lldb/source/Breakpoint/BreakpointResolverFileLine.cpp new file mode 100644 index 00000000000..91a218fdb80 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Breakpoint/BreakpointResolverFileLine.cpp @@ -0,0 +1,246 @@ +//===-- BreakpointResolverFileLine.cpp --------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Breakpoint/BreakpointResolverFileLine.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Breakpoint/BreakpointLocation.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Symbol/CompileUnit.h" +#include "lldb/Symbol/Function.h" +#include "lldb/lldb-private-log.h" + +using namespace lldb; +using namespace lldb_private; + +//---------------------------------------------------------------------- +// BreakpointResolverFileLine: +//---------------------------------------------------------------------- +BreakpointResolverFileLine::BreakpointResolverFileLine +( + Breakpoint *bkpt, + const FileSpec &file_spec, + uint32_t line_no, + bool check_inlines, + bool skip_prologue +) : + BreakpointResolver (bkpt, BreakpointResolver::FileLineResolver), + m_file_spec (file_spec), + m_line_number (line_no), + m_inlines (check_inlines), + m_skip_prologue(skip_prologue) +{ +} + +BreakpointResolverFileLine::~BreakpointResolverFileLine () +{ +} + +Searcher::CallbackReturn +BreakpointResolverFileLine::SearchCallback +( + SearchFilter &filter, + SymbolContext &context, + Address *addr, + bool containing +) +{ + SymbolContextList sc_list; + + assert (m_breakpoint != NULL); + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_BREAKPOINTS)); + + // There is a tricky bit here. You can have two compilation units that #include the same file, and + // in one of them the function at m_line_number is used (and so code and a line entry for it is generated) but in the + // other it isn't. If we considered the CU's independently, then in the second inclusion, we'd move the breakpoint + // to the next function that actually generated code in the header file. That would end up being confusing. + // So instead, we do the CU iterations by hand here, then scan through the complete list of matches, and figure out + // the closest line number match, and only set breakpoints on that match. + + // Note also that if file_spec only had a file name and not a directory, there may be many different file spec's in + // the resultant list. The closest line match for one will not be right for some totally different file. + // So we go through the match list and pull out the sets that have the same file spec in their line_entry + // and treat each set separately. + + const size_t num_comp_units = context.module_sp->GetNumCompileUnits(); + for (size_t i = 0; i < num_comp_units; i++) + { + CompUnitSP cu_sp (context.module_sp->GetCompileUnitAtIndex (i)); + if (cu_sp) + { + if (filter.CompUnitPasses(*cu_sp)) + cu_sp->ResolveSymbolContext (m_file_spec, m_line_number, m_inlines, false, eSymbolContextEverything, sc_list); + } + } + + while (sc_list.GetSize() > 0) + { + SymbolContextList tmp_sc_list; + unsigned current_idx = 0; + SymbolContext sc; + bool first_entry = true; + + FileSpec match_file_spec; + uint32_t closest_line_number = UINT32_MAX; + + // Pull out the first entry, and all the others that match its file spec, and stuff them in the tmp list. + while (current_idx < sc_list.GetSize()) + { + bool matches; + + sc_list.GetContextAtIndex (current_idx, sc); + if (first_entry) + { + match_file_spec = sc.line_entry.file; + matches = true; + first_entry = false; + } + else + matches = (sc.line_entry.file == match_file_spec); + + if (matches) + { + tmp_sc_list.Append (sc); + sc_list.RemoveContextAtIndex(current_idx); + + // ResolveSymbolContext will always return a number that is >= the line number you pass in. + // So the smaller line number is always better. + if (sc.line_entry.line < closest_line_number) + closest_line_number = sc.line_entry.line; + } + else + current_idx++; + } + + // Okay, we've found the closest line number match, now throw away all the others: + + current_idx = 0; + while (current_idx < tmp_sc_list.GetSize()) + { + if (tmp_sc_list.GetContextAtIndex(current_idx, sc)) + { + if (sc.line_entry.line != closest_line_number) + tmp_sc_list.RemoveContextAtIndex(current_idx); + else + current_idx++; + } + } + + // Next go through and see if there are line table entries that are contiguous, and if so keep only the + // first of the contiguous range: + + lldb::addr_t last_end_addr = LLDB_INVALID_ADDRESS; + current_idx = 0; + while (current_idx < tmp_sc_list.GetSize()) + { + if (tmp_sc_list.GetContextAtIndex(current_idx, sc)) + { + lldb::addr_t start_file_addr = sc.line_entry.range.GetBaseAddress().GetFileAddress(); + lldb::addr_t end_file_addr = start_file_addr + sc.line_entry.range.GetByteSize(); + + if (start_file_addr == last_end_addr) + tmp_sc_list.RemoveContextAtIndex(current_idx); + else + current_idx++; + + last_end_addr = end_file_addr; + } + } + + // and make breakpoints out of the closest line number match. + + uint32_t tmp_sc_list_size = tmp_sc_list.GetSize(); + + for (uint32_t i = 0; i < tmp_sc_list_size; i++) + { + if (tmp_sc_list.GetContextAtIndex(i, sc)) + { + Address line_start = sc.line_entry.range.GetBaseAddress(); + if (line_start.IsValid()) + { + if (filter.AddressPasses(line_start)) + { + // If the line number is before the prologue end, move it there... + bool skipped_prologue = false; + if (m_skip_prologue) + { + if (sc.function) + { + Address prologue_addr(sc.function->GetAddressRange().GetBaseAddress()); + if (prologue_addr.IsValid() && (line_start == prologue_addr)) + { + const uint32_t prologue_byte_size = sc.function->GetPrologueByteSize(); + if (prologue_byte_size) + { + prologue_addr.Slide(prologue_byte_size); + + if (filter.AddressPasses(prologue_addr)) + { + skipped_prologue = true; + line_start = prologue_addr; + } + } + } + } + } + + BreakpointLocationSP bp_loc_sp (m_breakpoint->AddLocation(line_start)); + if (log && bp_loc_sp && !m_breakpoint->IsInternal()) + { + StreamString s; + bp_loc_sp->GetDescription (&s, lldb::eDescriptionLevelVerbose); + log->Printf ("Added location (skipped prologue: %s): %s \n", skipped_prologue ? "yes" : "no", s.GetData()); + } + } + else if (log) + { + log->Printf ("Breakpoint at file address 0x%" PRIx64 " for %s:%d didn't pass the filter.\n", + line_start.GetFileAddress(), + m_file_spec.GetFilename().AsCString(""), + m_line_number); + } + } + else + { + if (log) + log->Printf ("error: Unable to set breakpoint at file address 0x%" PRIx64 " for %s:%d\n", + line_start.GetFileAddress(), + m_file_spec.GetFilename().AsCString(""), + m_line_number); + } + } + } + } + + return Searcher::eCallbackReturnContinue; +} + +Searcher::Depth +BreakpointResolverFileLine::GetDepth() +{ + return Searcher::eDepthModule; +} + +void +BreakpointResolverFileLine::GetDescription (Stream *s) +{ + s->Printf ("file = '%s', line = %u", m_file_spec.GetPath().c_str(), m_line_number); +} + +void +BreakpointResolverFileLine::Dump (Stream *s) const +{ + +} + diff --git a/contrib/llvm/tools/lldb/source/Breakpoint/BreakpointResolverFileRegex.cpp b/contrib/llvm/tools/lldb/source/Breakpoint/BreakpointResolverFileRegex.cpp new file mode 100644 index 00000000000..de974d04894 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Breakpoint/BreakpointResolverFileRegex.cpp @@ -0,0 +1,134 @@ +//===-- BreakpointResolverFileRegex.cpp --------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Breakpoint/BreakpointResolverFileRegex.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Breakpoint/BreakpointLocation.h" +#include "lldb/Core/SourceManager.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Symbol/CompileUnit.h" +#include "lldb/Target/Target.h" +#include "lldb/lldb-private-log.h" + +using namespace lldb; +using namespace lldb_private; + +//---------------------------------------------------------------------- +// BreakpointResolverFileRegex: +//---------------------------------------------------------------------- +BreakpointResolverFileRegex::BreakpointResolverFileRegex +( + Breakpoint *bkpt, + RegularExpression ®ex +) : + BreakpointResolver (bkpt, BreakpointResolver::FileLineResolver), + m_regex (regex) +{ +} + +BreakpointResolverFileRegex::~BreakpointResolverFileRegex () +{ +} + +Searcher::CallbackReturn +BreakpointResolverFileRegex::SearchCallback +( + SearchFilter &filter, + SymbolContext &context, + Address *addr, + bool containing +) +{ + + assert (m_breakpoint != NULL); + if (!context.target_sp) + return eCallbackReturnContinue; + + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_BREAKPOINTS)); + + CompileUnit *cu = context.comp_unit; + FileSpec cu_file_spec = *(static_cast(cu)); + std::vector line_matches; + context.target_sp->GetSourceManager().FindLinesMatchingRegex(cu_file_spec, m_regex, 1, UINT32_MAX, line_matches); + uint32_t num_matches = line_matches.size(); + for (uint32_t i = 0; i < num_matches; i++) + { + uint32_t start_idx = 0; + bool exact = false; + while (1) + { + LineEntry line_entry; + + // Cycle through all the line entries that might match this one: + start_idx = cu->FindLineEntry (start_idx, line_matches[i], NULL, exact, &line_entry); + if (start_idx == UINT32_MAX) + break; + exact = true; + start_idx++; + + Address line_start = line_entry.range.GetBaseAddress(); + if (line_start.IsValid()) + { + if (filter.AddressPasses(line_start)) + { + BreakpointLocationSP bp_loc_sp (m_breakpoint->AddLocation(line_start)); + if (log && bp_loc_sp && !m_breakpoint->IsInternal()) + { + StreamString s; + bp_loc_sp->GetDescription (&s, lldb::eDescriptionLevelVerbose); + log->Printf ("Added location: %s\n", s.GetData()); + } + } + else if (log) + { + log->Printf ("Breakpoint at file address 0x%" PRIx64 " for %s:%d didn't pass filter.\n", + line_start.GetFileAddress(), + cu_file_spec.GetFilename().AsCString(""), + line_matches[i]); + } + } + else + { + if (log) + log->Printf ("error: Unable to set breakpoint at file address 0x%" PRIx64 " for %s:%d\n", + line_start.GetFileAddress(), + cu_file_spec.GetFilename().AsCString(""), + line_matches[i]); + } + + } + } + assert (m_breakpoint != NULL); + + return Searcher::eCallbackReturnContinue; +} + +Searcher::Depth +BreakpointResolverFileRegex::GetDepth() +{ + return Searcher::eDepthCompUnit; +} + +void +BreakpointResolverFileRegex::GetDescription (Stream *s) +{ + s->Printf ("source regex = \"%s\"", m_regex.GetText()); +} + +void +BreakpointResolverFileRegex::Dump (Stream *s) const +{ + +} + diff --git a/contrib/llvm/tools/lldb/source/Breakpoint/BreakpointResolverName.cpp b/contrib/llvm/tools/lldb/source/Breakpoint/BreakpointResolverName.cpp new file mode 100644 index 00000000000..27f85653d64 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Breakpoint/BreakpointResolverName.cpp @@ -0,0 +1,357 @@ +//===-- BreakpointResolverName.cpp ------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Breakpoint/BreakpointResolverName.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Breakpoint/BreakpointLocation.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Symbol/ClangNamespaceDecl.h" +#include "lldb/Symbol/Block.h" +#include "lldb/Symbol/Function.h" +#include "lldb/Symbol/Symbol.h" +#include "lldb/Symbol/SymbolContext.h" +#include "lldb/Target/ObjCLanguageRuntime.h" + +using namespace lldb; +using namespace lldb_private; + +BreakpointResolverName::BreakpointResolverName (Breakpoint *bkpt, + const char *name_cstr, + uint32_t name_type_mask, + Breakpoint::MatchType type, + bool skip_prologue) : + BreakpointResolver (bkpt, BreakpointResolver::NameResolver), + m_class_name (), + m_regex (), + m_match_type (type), + m_skip_prologue (skip_prologue) +{ + + if (m_match_type == Breakpoint::Regexp) + { + if (!m_regex.Compile (name_cstr)) + { + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_BREAKPOINTS)); + + if (log) + log->Warning ("function name regexp: \"%s\" did not compile.", name_cstr); + } + } + else + { + AddNameLookup (ConstString(name_cstr), name_type_mask); + } +} + +BreakpointResolverName::BreakpointResolverName (Breakpoint *bkpt, + const char *names[], + size_t num_names, + uint32_t name_type_mask, + bool skip_prologue) : + BreakpointResolver (bkpt, BreakpointResolver::NameResolver), + m_match_type (Breakpoint::Exact), + m_skip_prologue (skip_prologue) +{ + for (size_t i = 0; i < num_names; i++) + { + AddNameLookup (ConstString (names[i]), name_type_mask); + } +} + +BreakpointResolverName::BreakpointResolverName (Breakpoint *bkpt, + std::vector names, + uint32_t name_type_mask, + bool skip_prologue) : + BreakpointResolver (bkpt, BreakpointResolver::NameResolver), + m_match_type (Breakpoint::Exact), + m_skip_prologue (skip_prologue) +{ + for (const std::string& name : names) + { + AddNameLookup (ConstString (name.c_str(), name.size()), name_type_mask); + } +} + +BreakpointResolverName::BreakpointResolverName (Breakpoint *bkpt, + RegularExpression &func_regex, + bool skip_prologue) : + BreakpointResolver (bkpt, BreakpointResolver::NameResolver), + m_class_name (NULL), + m_regex (func_regex), + m_match_type (Breakpoint::Regexp), + m_skip_prologue (skip_prologue) +{ +} + +BreakpointResolverName::BreakpointResolverName +( + Breakpoint *bkpt, + const char *class_name, + const char *method, + Breakpoint::MatchType type, + bool skip_prologue +) : + BreakpointResolver (bkpt, BreakpointResolver::NameResolver), + m_class_name (class_name), + m_regex (), + m_match_type (type), + m_skip_prologue (skip_prologue) +{ + LookupInfo lookup; + lookup.name.SetCString(method); + lookup.lookup_name = lookup.name; + lookup.name_type_mask = eFunctionNameTypeMethod; + lookup.match_name_after_lookup = false; + m_lookups.push_back (lookup); +} + +BreakpointResolverName::~BreakpointResolverName () +{ +} + +void +BreakpointResolverName::AddNameLookup (const ConstString &name, uint32_t name_type_mask) +{ + ObjCLanguageRuntime::MethodName objc_method(name.GetCString(), false); + if (objc_method.IsValid(false)) + { + std::vector objc_names; + objc_method.GetFullNames(objc_names, true); + for (ConstString objc_name : objc_names) + { + LookupInfo lookup; + lookup.name = name; + lookup.lookup_name = objc_name; + lookup.name_type_mask = eFunctionNameTypeFull; + lookup.match_name_after_lookup = false; + m_lookups.push_back (lookup); + } + } + else + { + LookupInfo lookup; + lookup.name = name; + Module::PrepareForFunctionNameLookup(lookup.name, name_type_mask, lookup.lookup_name, lookup.name_type_mask, lookup.match_name_after_lookup); + m_lookups.push_back (lookup); + } +} + + +void +BreakpointResolverName::LookupInfo::Prune (SymbolContextList &sc_list, size_t start_idx) const +{ + if (match_name_after_lookup && name) + { + SymbolContext sc; + size_t i = start_idx; + while (i < sc_list.GetSize()) + { + if (!sc_list.GetContextAtIndex(i, sc)) + break; + ConstString full_name (sc.GetFunctionName()); + if (full_name && ::strstr(full_name.GetCString(), name.GetCString()) == NULL) + { + sc_list.RemoveContextAtIndex(i); + } + else + { + ++i; + } + } + } +} + + +// FIXME: Right now we look at the module level, and call the module's "FindFunctions". +// Greg says he will add function tables, maybe at the CompileUnit level to accelerate function +// lookup. At that point, we should switch the depth to CompileUnit, and look in these tables. + +Searcher::CallbackReturn +BreakpointResolverName::SearchCallback +( + SearchFilter &filter, + SymbolContext &context, + Address *addr, + bool containing +) +{ + SymbolContextList func_list; + //SymbolContextList sym_list; + + uint32_t i; + bool new_location; + Address break_addr; + assert (m_breakpoint != NULL); + + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_BREAKPOINTS)); + + if (m_class_name) + { + if (log) + log->Warning ("Class/method function specification not supported yet.\n"); + return Searcher::eCallbackReturnStop; + } + bool filter_by_cu = (filter.GetFilterRequiredItems() & eSymbolContextCompUnit) != 0; + const bool include_symbols = filter_by_cu == false; + const bool include_inlines = true; + const bool append = true; + + switch (m_match_type) + { + case Breakpoint::Exact: + if (context.module_sp) + { + for (const LookupInfo &lookup : m_lookups) + { + const size_t start_func_idx = func_list.GetSize(); + context.module_sp->FindFunctions (lookup.lookup_name, + NULL, + lookup.name_type_mask, + include_symbols, + include_inlines, + append, + func_list); + const size_t end_func_idx = func_list.GetSize(); + + if (start_func_idx < end_func_idx) + lookup.Prune (func_list, start_func_idx); + } + } + break; + case Breakpoint::Regexp: + if (context.module_sp) + { + context.module_sp->FindFunctions (m_regex, + !filter_by_cu, // include symbols only if we aren't filterning by CU + include_inlines, + append, + func_list); + } + break; + case Breakpoint::Glob: + if (log) + log->Warning ("glob is not supported yet."); + break; + } + + // If the filter specifies a Compilation Unit, remove the ones that don't pass at this point. + if (filter_by_cu) + { + uint32_t num_functions = func_list.GetSize(); + + for (size_t idx = 0; idx < num_functions; idx++) + { + SymbolContext sc; + func_list.GetContextAtIndex(idx, sc); + if (!sc.comp_unit || !filter.CompUnitPasses(*sc.comp_unit)) + { + func_list.RemoveContextAtIndex(idx); + num_functions--; + idx--; + } + } + } + + // Remove any duplicates between the funcion list and the symbol list + SymbolContext sc; + if (func_list.GetSize()) + { + for (i = 0; i < func_list.GetSize(); i++) + { + if (func_list.GetContextAtIndex(i, sc)) + { + if (sc.block && sc.block->GetInlinedFunctionInfo()) + { + if (!sc.block->GetStartAddress(break_addr)) + break_addr.Clear(); + } + else if (sc.function) + { + break_addr = sc.function->GetAddressRange().GetBaseAddress(); + if (m_skip_prologue && break_addr.IsValid()) + { + const uint32_t prologue_byte_size = sc.function->GetPrologueByteSize(); + if (prologue_byte_size) + break_addr.SetOffset(break_addr.GetOffset() + prologue_byte_size); + } + } + else if (sc.symbol) + { + break_addr = sc.symbol->GetAddress(); + if (m_skip_prologue && break_addr.IsValid()) + { + const uint32_t prologue_byte_size = sc.symbol->GetPrologueByteSize(); + if (prologue_byte_size) + break_addr.SetOffset(break_addr.GetOffset() + prologue_byte_size); + } + } + + if (break_addr.IsValid()) + { + if (filter.AddressPasses(break_addr)) + { + BreakpointLocationSP bp_loc_sp (m_breakpoint->AddLocation(break_addr, &new_location)); + if (bp_loc_sp && new_location && !m_breakpoint->IsInternal()) + { + if (log) + { + StreamString s; + bp_loc_sp->GetDescription(&s, lldb::eDescriptionLevelVerbose); + log->Printf ("Added location: %s\n", s.GetData()); + } + } + } + } + } + } + } + + return Searcher::eCallbackReturnContinue; +} + +Searcher::Depth +BreakpointResolverName::GetDepth() +{ + return Searcher::eDepthModule; +} + +void +BreakpointResolverName::GetDescription (Stream *s) +{ + if (m_match_type == Breakpoint::Regexp) + s->Printf("regex = '%s'", m_regex.GetText()); + else + { + size_t num_names = m_lookups.size(); + if (num_names == 1) + s->Printf("name = '%s'", m_lookups[0].name.GetCString()); + else + { + s->Printf("names = {"); + for (size_t i = 0; i < num_names - 1; i++) + { + s->Printf ("'%s', ", m_lookups[i].name.GetCString()); + } + s->Printf ("'%s'}", m_lookups[num_names - 1].name.GetCString()); + } + } +} + +void +BreakpointResolverName::Dump (Stream *s) const +{ + +} + diff --git a/contrib/llvm/tools/lldb/source/Breakpoint/BreakpointSite.cpp b/contrib/llvm/tools/lldb/source/Breakpoint/BreakpointSite.cpp new file mode 100644 index 00000000000..fa5d8c1f9f8 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Breakpoint/BreakpointSite.cpp @@ -0,0 +1,234 @@ +//===-- BreakpointSite.cpp --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Breakpoint/BreakpointSite.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Breakpoint/Breakpoint.h" +#include "lldb/Breakpoint/BreakpointLocation.h" +#include "lldb/Breakpoint/BreakpointSiteList.h" + +using namespace lldb; +using namespace lldb_private; + +BreakpointSite::BreakpointSite +( + BreakpointSiteList *list, + const BreakpointLocationSP& owner, + lldb::addr_t addr, + bool use_hardware +) : + StoppointLocation(GetNextID(), addr, 0, use_hardware), + m_type (eSoftware), // Process subclasses need to set this correctly using SetType() + m_saved_opcode(), + m_trap_opcode(), + m_enabled(false), // Need to create it disabled, so the first enable turns it on. + m_owners() +{ + m_owners.Add(owner); +} + +BreakpointSite::~BreakpointSite() +{ + BreakpointLocationSP bp_loc_sp; + const size_t owner_count = m_owners.GetSize(); + for (size_t i = 0; i < owner_count; i++) + { + m_owners.GetByIndex(i)->ClearBreakpointSite(); + } +} + +break_id_t +BreakpointSite::GetNextID() +{ + static break_id_t g_next_id = 0; + return ++g_next_id; +} + +// RETURNS - true if we should stop at this breakpoint, false if we +// should continue. + +bool +BreakpointSite::ShouldStop (StoppointCallbackContext *context) +{ + IncrementHitCount(); + return m_owners.ShouldStop (context); +} + +bool +BreakpointSite::IsBreakpointAtThisSite (lldb::break_id_t bp_id) +{ + const size_t owner_count = m_owners.GetSize(); + for (size_t i = 0; i < owner_count; i++) + { + if (m_owners.GetByIndex(i)->GetBreakpoint().GetID() == bp_id) + return true; + } + return false; +} + +void +BreakpointSite::Dump(Stream *s) const +{ + if (s == NULL) + return; + + s->Printf("BreakpointSite %u: addr = 0x%8.8" PRIx64 " type = %s breakpoint hw_index = %i hit_count = %-4u", + GetID(), + (uint64_t)m_addr, + IsHardware() ? "hardware" : "software", + GetHardwareIndex(), + GetHitCount()); +} + +void +BreakpointSite::GetDescription (Stream *s, lldb::DescriptionLevel level) +{ + if (level != lldb::eDescriptionLevelBrief) + s->Printf ("breakpoint site: %d at 0x%8.8" PRIx64, GetID(), GetLoadAddress()); + m_owners.GetDescription (s, level); +} + +bool +BreakpointSite::IsInternal() const +{ + return m_owners.IsInternal(); +} + +uint8_t * +BreakpointSite::GetTrapOpcodeBytes() +{ + return &m_trap_opcode[0]; +} + +const uint8_t * +BreakpointSite::GetTrapOpcodeBytes() const +{ + return &m_trap_opcode[0]; +} + +size_t +BreakpointSite::GetTrapOpcodeMaxByteSize() const +{ + return sizeof(m_trap_opcode); +} + +bool +BreakpointSite::SetTrapOpcode (const uint8_t *trap_opcode, uint32_t trap_opcode_size) +{ + if (trap_opcode_size > 0 && trap_opcode_size <= sizeof(m_trap_opcode)) + { + m_byte_size = trap_opcode_size; + ::memcpy (m_trap_opcode, trap_opcode, trap_opcode_size); + return true; + } + m_byte_size = 0; + return false; +} + +uint8_t * +BreakpointSite::GetSavedOpcodeBytes() +{ + return &m_saved_opcode[0]; +} + +const uint8_t * +BreakpointSite::GetSavedOpcodeBytes() const +{ + return &m_saved_opcode[0]; +} + +bool +BreakpointSite::IsEnabled () const +{ + return m_enabled; +} + +void +BreakpointSite::SetEnabled (bool enabled) +{ + m_enabled = enabled; +} + +void +BreakpointSite::AddOwner (const BreakpointLocationSP &owner) +{ + m_owners.Add(owner); +} + +size_t +BreakpointSite::RemoveOwner (lldb::break_id_t break_id, lldb::break_id_t break_loc_id) +{ + m_owners.Remove(break_id, break_loc_id); + return m_owners.GetSize(); +} + +size_t +BreakpointSite::GetNumberOfOwners () +{ + return m_owners.GetSize(); +} + +BreakpointLocationSP +BreakpointSite::GetOwnerAtIndex (size_t index) +{ + return m_owners.GetByIndex (index); +} + +bool +BreakpointSite::ValidForThisThread (Thread *thread) +{ + return m_owners.ValidForThisThread(thread); +} + +bool +BreakpointSite::IntersectsRange(lldb::addr_t addr, size_t size, lldb::addr_t *intersect_addr, size_t *intersect_size, size_t *opcode_offset) const +{ + // We only use software traps for software breakpoints + if (!IsHardware()) + { + if (m_byte_size > 0) + { + const lldb::addr_t bp_end_addr = m_addr + m_byte_size; + const lldb::addr_t end_addr = addr + size; + // Is the breakpoint end address before the passed in start address? + if (bp_end_addr <= addr) + return false; + // Is the breakpoint start address after passed in end address? + if (end_addr <= m_addr) + return false; + if (intersect_addr || intersect_size || opcode_offset) + { + if (m_addr < addr) + { + if (intersect_addr) + *intersect_addr = addr; + if (intersect_size) + *intersect_size = std::min(bp_end_addr, end_addr) - addr; + if (opcode_offset) + *opcode_offset = addr - m_addr; + } + else + { + if (intersect_addr) + *intersect_addr = m_addr; + if (intersect_size) + *intersect_size = std::min(bp_end_addr, end_addr) - m_addr; + if (opcode_offset) + *opcode_offset = 0; + } + } + return true; + } + } + return false; +} diff --git a/contrib/llvm/tools/lldb/source/Breakpoint/BreakpointSiteList.cpp b/contrib/llvm/tools/lldb/source/Breakpoint/BreakpointSiteList.cpp new file mode 100644 index 00000000000..68c4af18ec5 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Breakpoint/BreakpointSiteList.cpp @@ -0,0 +1,240 @@ +//===-- BreakpointSiteList.cpp ----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Breakpoint/BreakpointSiteList.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/Stream.h" +#include + +using namespace lldb; +using namespace lldb_private; + +BreakpointSiteList::BreakpointSiteList() : + m_mutex (Mutex::eMutexTypeRecursive), + m_bp_site_list() +{ +} + +BreakpointSiteList::~BreakpointSiteList() +{ +} + +// Add breakpoint site to the list. However, if the element already exists in the +// list, then we don't add it, and return LLDB_INVALID_BREAK_ID. + +lldb::break_id_t +BreakpointSiteList::Add(const BreakpointSiteSP &bp) +{ + lldb::addr_t bp_site_load_addr = bp->GetLoadAddress(); + Mutex::Locker locker(m_mutex); + collection::iterator iter = m_bp_site_list.find (bp_site_load_addr); + + if (iter == m_bp_site_list.end()) + { + m_bp_site_list.insert (iter, collection::value_type (bp_site_load_addr, bp)); + return bp->GetID(); + } + else + { + return LLDB_INVALID_BREAK_ID; + } +} + +bool +BreakpointSiteList::ShouldStop (StoppointCallbackContext *context, lldb::break_id_t site_id) +{ + BreakpointSiteSP site_sp (FindByID (site_id)); + if (site_sp) + { + // Let the BreakpointSite decide if it should stop here (could not have + // reached it's target hit count yet, or it could have a callback + // that decided it shouldn't stop (shared library loads/unloads). + return site_sp->ShouldStop (context); + } + // We should stop here since this BreakpointSite isn't valid anymore or it + // doesn't exist. + return true; +} +lldb::break_id_t +BreakpointSiteList::FindIDByAddress (lldb::addr_t addr) +{ + BreakpointSiteSP bp = FindByAddress (addr); + if (bp) + { + //DBLogIf(PD_LOG_BREAKPOINTS, "BreakpointSiteList::%s ( addr = 0x%8.8" PRIx64 " ) => %u", __FUNCTION__, (uint64_t)addr, bp->GetID()); + return bp.get()->GetID(); + } + //DBLogIf(PD_LOG_BREAKPOINTS, "BreakpointSiteList::%s ( addr = 0x%8.8" PRIx64 " ) => NONE", __FUNCTION__, (uint64_t)addr); + return LLDB_INVALID_BREAK_ID; +} + +bool +BreakpointSiteList::Remove (lldb::break_id_t break_id) +{ + Mutex::Locker locker(m_mutex); + collection::iterator pos = GetIDIterator(break_id); // Predicate + if (pos != m_bp_site_list.end()) + { + m_bp_site_list.erase(pos); + return true; + } + return false; +} + +bool +BreakpointSiteList::RemoveByAddress (lldb::addr_t address) +{ + Mutex::Locker locker(m_mutex); + collection::iterator pos = m_bp_site_list.find(address); + if (pos != m_bp_site_list.end()) + { + m_bp_site_list.erase(pos); + return true; + } + return false; +} + +class BreakpointSiteIDMatches +{ +public: + BreakpointSiteIDMatches (lldb::break_id_t break_id) : + m_break_id(break_id) + { + } + + bool operator() (std::pair val_pair) const + { + return m_break_id == val_pair.second.get()->GetID(); + } + +private: + const lldb::break_id_t m_break_id; +}; + +BreakpointSiteList::collection::iterator +BreakpointSiteList::GetIDIterator (lldb::break_id_t break_id) +{ + Mutex::Locker locker(m_mutex); + return std::find_if(m_bp_site_list.begin(), m_bp_site_list.end(), // Search full range + BreakpointSiteIDMatches(break_id)); // Predicate +} + +BreakpointSiteList::collection::const_iterator +BreakpointSiteList::GetIDConstIterator (lldb::break_id_t break_id) const +{ + Mutex::Locker locker(m_mutex); + return std::find_if(m_bp_site_list.begin(), m_bp_site_list.end(), // Search full range + BreakpointSiteIDMatches(break_id)); // Predicate +} + +BreakpointSiteSP +BreakpointSiteList::FindByID (lldb::break_id_t break_id) +{ + Mutex::Locker locker(m_mutex); + BreakpointSiteSP stop_sp; + collection::iterator pos = GetIDIterator(break_id); + if (pos != m_bp_site_list.end()) + stop_sp = pos->second; + + return stop_sp; +} + +const BreakpointSiteSP +BreakpointSiteList::FindByID (lldb::break_id_t break_id) const +{ + Mutex::Locker locker(m_mutex); + BreakpointSiteSP stop_sp; + collection::const_iterator pos = GetIDConstIterator(break_id); + if (pos != m_bp_site_list.end()) + stop_sp = pos->second; + + return stop_sp; +} + +BreakpointSiteSP +BreakpointSiteList::FindByAddress (lldb::addr_t addr) +{ + BreakpointSiteSP found_sp; + Mutex::Locker locker(m_mutex); + collection::iterator iter = m_bp_site_list.find(addr); + if (iter != m_bp_site_list.end()) + found_sp = iter->second; + return found_sp; +} + +bool +BreakpointSiteList::BreakpointSiteContainsBreakpoint (lldb::break_id_t bp_site_id, lldb::break_id_t bp_id) +{ + Mutex::Locker locker(m_mutex); + collection::const_iterator pos = GetIDConstIterator(bp_site_id); + if (pos != m_bp_site_list.end()) + return pos->second->IsBreakpointAtThisSite (bp_id); + + return false; +} + +void +BreakpointSiteList::Dump (Stream *s) const +{ + s->Printf("%p: ", this); + //s->Indent(); + s->Printf("BreakpointSiteList with %u BreakpointSites:\n", (uint32_t)m_bp_site_list.size()); + s->IndentMore(); + collection::const_iterator pos; + collection::const_iterator end = m_bp_site_list.end(); + for (pos = m_bp_site_list.begin(); pos != end; ++pos) + pos->second.get()->Dump(s); + s->IndentLess(); +} + +void +BreakpointSiteList::ForEach (std::function const &callback) +{ + Mutex::Locker locker(m_mutex); + for (auto pair : m_bp_site_list) + callback (pair.second.get()); +} + +bool +BreakpointSiteList::FindInRange (lldb::addr_t lower_bound, lldb::addr_t upper_bound, BreakpointSiteList &bp_site_list) const +{ + if (lower_bound > upper_bound) + return false; + + Mutex::Locker locker(m_mutex); + collection::const_iterator lower, upper, pos; + lower = m_bp_site_list.lower_bound(lower_bound); + if (lower == m_bp_site_list.end() + || (*lower).first >= upper_bound) + return false; + + // This is one tricky bit. The breakpoint might overlap the bottom end of the range. So we grab the + // breakpoint prior to the lower bound, and check that that + its byte size isn't in our range. + if (lower != m_bp_site_list.begin()) + { + collection::const_iterator prev_pos = lower; + prev_pos--; + const BreakpointSiteSP &prev_bp = (*prev_pos).second; + if (prev_bp->GetLoadAddress() + prev_bp->GetByteSize() > lower_bound) + bp_site_list.Add (prev_bp); + + } + + upper = m_bp_site_list.upper_bound(upper_bound); + + for (pos = lower; pos != upper; pos++) + { + bp_site_list.Add ((*pos).second); + } + return true; +} diff --git a/contrib/llvm/tools/lldb/source/Breakpoint/Stoppoint.cpp b/contrib/llvm/tools/lldb/source/Breakpoint/Stoppoint.cpp new file mode 100644 index 00000000000..583ab47005f --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Breakpoint/Stoppoint.cpp @@ -0,0 +1,46 @@ +//===-- Stoppoint.cpp -------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-private.h" +#include "lldb/Breakpoint/Stoppoint.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes + +using namespace lldb; +using namespace lldb_private; + +//---------------------------------------------------------------------- +// Stoppoint constructor +//---------------------------------------------------------------------- +Stoppoint::Stoppoint() : + m_bid (LLDB_INVALID_BREAK_ID) +{ +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +Stoppoint::~Stoppoint() +{ +} + +break_id_t +Stoppoint::GetID () const +{ + return m_bid; +} + +void +Stoppoint::SetID (break_id_t bid) +{ + m_bid = bid; +} diff --git a/contrib/llvm/tools/lldb/source/Breakpoint/StoppointCallbackContext.cpp b/contrib/llvm/tools/lldb/source/Breakpoint/StoppointCallbackContext.cpp new file mode 100644 index 00000000000..2266c3e429c --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Breakpoint/StoppointCallbackContext.cpp @@ -0,0 +1,39 @@ +//===-- StoppointCallbackContext.cpp ----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Breakpoint/StoppointCallbackContext.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes + +using namespace lldb_private; + +StoppointCallbackContext::StoppointCallbackContext() : + event (NULL), + exe_ctx_ref (), + is_synchronous (false) +{ +} + +StoppointCallbackContext::StoppointCallbackContext(Event *e, const ExecutionContext &exe_ctx, bool synchronously) : + event (e), + exe_ctx_ref (exe_ctx), + is_synchronous(synchronously) +{ +} + +void +StoppointCallbackContext::Clear() +{ + event = NULL; + exe_ctx_ref.Clear(); + is_synchronous = false; +} diff --git a/contrib/llvm/tools/lldb/source/Breakpoint/StoppointLocation.cpp b/contrib/llvm/tools/lldb/source/Breakpoint/StoppointLocation.cpp new file mode 100644 index 00000000000..092caa5a932 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Breakpoint/StoppointLocation.cpp @@ -0,0 +1,48 @@ +//===-- StoppointLocation.cpp -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Breakpoint/StoppointLocation.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes + +using namespace lldb; +using namespace lldb_private; + +//---------------------------------------------------------------------- +// StoppointLocation constructor +//---------------------------------------------------------------------- +StoppointLocation::StoppointLocation (break_id_t bid, addr_t addr, bool hardware) : + m_loc_id(bid), + m_addr(addr), + m_hw_preferred(hardware), + m_hw_index(LLDB_INVALID_INDEX32), + m_byte_size(0), + m_hit_count(0) +{ +} + +StoppointLocation::StoppointLocation (break_id_t bid, addr_t addr, uint32_t byte_size, bool hardware) : + m_loc_id(bid), + m_addr(addr), + m_hw_preferred(hardware), + m_hw_index(LLDB_INVALID_INDEX32), + m_byte_size(byte_size), + m_hit_count(0) +{ +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +StoppointLocation::~StoppointLocation() +{ +} diff --git a/contrib/llvm/tools/lldb/source/Breakpoint/Watchpoint.cpp b/contrib/llvm/tools/lldb/source/Breakpoint/Watchpoint.cpp new file mode 100644 index 00000000000..45559b1901a --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Breakpoint/Watchpoint.cpp @@ -0,0 +1,489 @@ +//===-- Watchpoint.cpp ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Breakpoint/Watchpoint.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Breakpoint/StoppointCallbackContext.h" +#include "lldb/Core/Stream.h" +#include "lldb/Core/Value.h" +#include "lldb/Core/ValueObject.h" +#include "lldb/Core/ValueObjectMemory.h" +#include "lldb/Symbol/ClangASTContext.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/ThreadSpec.h" +#include "lldb/Expression/ClangUserExpression.h" + +using namespace lldb; +using namespace lldb_private; + +Watchpoint::Watchpoint (Target& target, lldb::addr_t addr, uint32_t size, const ClangASTType *type, bool hardware) : + StoppointLocation (0, addr, size, hardware), + m_target(target), + m_enabled(false), + m_is_hardware(hardware), + m_is_watch_variable(false), + m_is_ephemeral(false), + m_disabled_count(0), + m_watch_read(0), + m_watch_write(0), + m_watch_was_read(0), + m_watch_was_written(0), + m_ignore_count(0), + m_false_alarms(0), + m_decl_str(), + m_watch_spec_str(), + m_type(), + m_error(), + m_options (), + m_being_created(true) +{ + if (type && type->IsValid()) + m_type = *type; + else + { + // If we don't have a known type, then we force it to unsigned int of the right size. + ClangASTContext *ast_context = target.GetScratchClangASTContext(); + m_type = ast_context->GetBuiltinTypeForEncodingAndBitSize(eEncodingUint, 8 * size); + } + + // Set the initial value of the watched variable: + if (m_target.GetProcessSP()) + { + ExecutionContext exe_ctx; + m_target.GetProcessSP()->CalculateExecutionContext(exe_ctx); + CaptureWatchedValue (exe_ctx); + } + m_being_created = false; +} + +Watchpoint::~Watchpoint() +{ +} + +// This function is used when "baton" doesn't need to be freed +void +Watchpoint::SetCallback (WatchpointHitCallback callback, void *baton, bool is_synchronous) +{ + // The default "Baton" class will keep a copy of "baton" and won't free + // or delete it when it goes goes out of scope. + m_options.SetCallback(callback, BatonSP (new Baton(baton)), is_synchronous); + + SendWatchpointChangedEvent (eWatchpointEventTypeCommandChanged); +} + +// This function is used when a baton needs to be freed and therefore is +// contained in a "Baton" subclass. +void +Watchpoint::SetCallback (WatchpointHitCallback callback, const BatonSP &callback_baton_sp, bool is_synchronous) +{ + m_options.SetCallback(callback, callback_baton_sp, is_synchronous); + SendWatchpointChangedEvent (eWatchpointEventTypeCommandChanged); +} + +void +Watchpoint::ClearCallback () +{ + m_options.ClearCallback (); + SendWatchpointChangedEvent (eWatchpointEventTypeCommandChanged); +} + +void +Watchpoint::SetDeclInfo (const std::string &str) +{ + m_decl_str = str; + return; +} + +std::string +Watchpoint::GetWatchSpec() +{ + return m_watch_spec_str; +} + +void +Watchpoint::SetWatchSpec (const std::string &str) +{ + m_watch_spec_str = str; + return; +} + +// Override default impl of StoppointLocation::IsHardware() since m_is_hardware +// member field is more accurate. +bool +Watchpoint::IsHardware () const +{ + return m_is_hardware; +} + +bool +Watchpoint::IsWatchVariable() const +{ + return m_is_watch_variable; +} + +void +Watchpoint::SetWatchVariable(bool val) +{ + m_is_watch_variable = val; +} + +bool +Watchpoint::CaptureWatchedValue (const ExecutionContext &exe_ctx) +{ + ConstString watch_name("$__lldb__watch_value"); + m_old_value_sp = m_new_value_sp; + Address watch_address(GetLoadAddress()); + if (!m_type.IsValid()) + { + // Don't know how to report new & old values, since we couldn't make a scalar type for this watchpoint. + // This works around an assert in ValueObjectMemory::Create. + // FIXME: This should not happen, but if it does in some case we care about, + // we can go grab the value raw and print it as unsigned. + return false; + } + m_new_value_sp = ValueObjectMemory::Create (exe_ctx.GetBestExecutionContextScope(), watch_name.AsCString(), watch_address, m_type); + m_new_value_sp = m_new_value_sp->CreateConstantValue(watch_name); + if (m_new_value_sp && m_new_value_sp->GetError().Success()) + return true; + else + return false; +} + +void +Watchpoint::IncrementFalseAlarmsAndReviseHitCount() +{ + ++m_false_alarms; + if (m_false_alarms) + { + if (m_hit_count >= m_false_alarms) + { + m_hit_count -= m_false_alarms; + m_false_alarms = 0; + } + else + { + m_false_alarms -= m_hit_count; + m_hit_count = 0; + } + } +} + +// RETURNS - true if we should stop at this breakpoint, false if we +// should continue. + +bool +Watchpoint::ShouldStop (StoppointCallbackContext *context) +{ + IncrementHitCount(); + + if (!IsEnabled()) + return false; + + if (GetHitCount() <= GetIgnoreCount()) + return false; + + return true; +} + +void +Watchpoint::GetDescription (Stream *s, lldb::DescriptionLevel level) +{ + DumpWithLevel(s, level); + return; +} + +void +Watchpoint::Dump(Stream *s) const +{ + DumpWithLevel(s, lldb::eDescriptionLevelBrief); +} + +// If prefix is NULL, we display the watch id and ignore the prefix altogether. +void +Watchpoint::DumpSnapshots(Stream *s, const char *prefix) const +{ + if (!prefix) + { + s->Printf("\nWatchpoint %u hit:", GetID()); + prefix = ""; + } + + if (m_old_value_sp) + { + s->Printf("\n%sold value: %s", prefix, m_old_value_sp->GetValueAsCString()); + } + if (m_new_value_sp) + { + s->Printf("\n%snew value: %s", prefix, m_new_value_sp->GetValueAsCString()); + } +} + +void +Watchpoint::DumpWithLevel(Stream *s, lldb::DescriptionLevel description_level) const +{ + if (s == NULL) + return; + + assert(description_level >= lldb::eDescriptionLevelBrief && + description_level <= lldb::eDescriptionLevelVerbose); + + s->Printf("Watchpoint %u: addr = 0x%8.8" PRIx64 " size = %u state = %s type = %s%s", + GetID(), + GetLoadAddress(), + m_byte_size, + IsEnabled() ? "enabled" : "disabled", + m_watch_read ? "r" : "", + m_watch_write ? "w" : ""); + + if (description_level >= lldb::eDescriptionLevelFull) { + if (!m_decl_str.empty()) + s->Printf("\n declare @ '%s'", m_decl_str.c_str()); + if (!m_watch_spec_str.empty()) + s->Printf("\n watchpoint spec = '%s'", m_watch_spec_str.c_str()); + + // Dump the snapshots we have taken. + DumpSnapshots(s, " "); + + if (GetConditionText()) + s->Printf("\n condition = '%s'", GetConditionText()); + m_options.GetCallbackDescription(s, description_level); + } + + if (description_level >= lldb::eDescriptionLevelVerbose) + { + s->Printf("\n hw_index = %i hit_count = %-4u ignore_count = %-4u", + GetHardwareIndex(), + GetHitCount(), + GetIgnoreCount()); + } +} + +bool +Watchpoint::IsEnabled() const +{ + return m_enabled; +} + +// Within StopInfo.cpp, we purposely turn on the ephemeral mode right before temporarily disable the watchpoint +// in order to perform possible watchpoint actions without triggering further watchpoint events. +// After the temporary disabled watchpoint is enabled, we then turn off the ephemeral mode. + +void +Watchpoint::TurnOnEphemeralMode() +{ + m_is_ephemeral = true; +} + +void +Watchpoint::TurnOffEphemeralMode() +{ + m_is_ephemeral = false; + // Leaving ephemeral mode, reset the m_disabled_count! + m_disabled_count = 0; +} + +bool +Watchpoint::IsDisabledDuringEphemeralMode() +{ + return m_disabled_count > 1; +} + +void +Watchpoint::SetEnabled(bool enabled, bool notify) +{ + if (!enabled) + { + if (!m_is_ephemeral) + SetHardwareIndex(LLDB_INVALID_INDEX32); + else + ++m_disabled_count; + + // Don't clear the snapshots for now. + // Within StopInfo.cpp, we purposely do disable/enable watchpoint while performing watchpoint actions. + } + bool changed = enabled != m_enabled; + m_enabled = enabled; + if (notify && !m_is_ephemeral && changed) + SendWatchpointChangedEvent (enabled ? eWatchpointEventTypeEnabled : eWatchpointEventTypeDisabled); +} + +void +Watchpoint::SetWatchpointType (uint32_t type, bool notify) +{ + int old_watch_read = m_watch_read; + int old_watch_write = m_watch_write; + m_watch_read = (type & LLDB_WATCH_TYPE_READ) != 0; + m_watch_write = (type & LLDB_WATCH_TYPE_WRITE) != 0; + if (notify && (old_watch_read != m_watch_read || old_watch_write != m_watch_write)) + SendWatchpointChangedEvent (eWatchpointEventTypeTypeChanged); +} + +bool +Watchpoint::WatchpointRead () const +{ + return m_watch_read != 0; +} +bool +Watchpoint::WatchpointWrite () const +{ + return m_watch_write != 0; +} +uint32_t +Watchpoint::GetIgnoreCount () const +{ + return m_ignore_count; +} + +void +Watchpoint::SetIgnoreCount (uint32_t n) +{ + bool changed = m_ignore_count != n; + m_ignore_count = n; + if (changed) + SendWatchpointChangedEvent (eWatchpointEventTypeIgnoreChanged); +} + +bool +Watchpoint::InvokeCallback (StoppointCallbackContext *context) +{ + return m_options.InvokeCallback (context, GetID()); +} + +void +Watchpoint::SetCondition (const char *condition) +{ + if (condition == NULL || condition[0] == '\0') + { + if (m_condition_ap.get()) + m_condition_ap.reset(); + } + else + { + // Pass NULL for expr_prefix (no translation-unit level definitions). + m_condition_ap.reset(new ClangUserExpression (condition, NULL, lldb::eLanguageTypeUnknown, ClangUserExpression::eResultTypeAny)); + } + SendWatchpointChangedEvent (eWatchpointEventTypeConditionChanged); +} + +const char * +Watchpoint::GetConditionText () const +{ + if (m_condition_ap.get()) + return m_condition_ap->GetUserText(); + else + return NULL; +} + +void +Watchpoint::SendWatchpointChangedEvent (lldb::WatchpointEventType eventKind) +{ + if (!m_being_created + && GetTarget().EventTypeHasListeners(Target::eBroadcastBitWatchpointChanged)) + { + WatchpointEventData *data = new Watchpoint::WatchpointEventData (eventKind, shared_from_this()); + GetTarget().BroadcastEvent (Target::eBroadcastBitWatchpointChanged, data); + } +} + +void +Watchpoint::SendWatchpointChangedEvent (WatchpointEventData *data) +{ + + if (data == NULL) + return; + + if (!m_being_created + && GetTarget().EventTypeHasListeners(Target::eBroadcastBitWatchpointChanged)) + GetTarget().BroadcastEvent (Target::eBroadcastBitWatchpointChanged, data); + else + delete data; +} + +Watchpoint::WatchpointEventData::WatchpointEventData (WatchpointEventType sub_type, + const WatchpointSP &new_watchpoint_sp) : + EventData (), + m_watchpoint_event (sub_type), + m_new_watchpoint_sp (new_watchpoint_sp) +{ +} + +Watchpoint::WatchpointEventData::~WatchpointEventData () +{ +} + +const ConstString & +Watchpoint::WatchpointEventData::GetFlavorString () +{ + static ConstString g_flavor ("Watchpoint::WatchpointEventData"); + return g_flavor; +} + +const ConstString & +Watchpoint::WatchpointEventData::GetFlavor () const +{ + return WatchpointEventData::GetFlavorString (); +} + + +WatchpointSP & +Watchpoint::WatchpointEventData::GetWatchpoint () +{ + return m_new_watchpoint_sp; +} + +WatchpointEventType +Watchpoint::WatchpointEventData::GetWatchpointEventType () const +{ + return m_watchpoint_event; +} + +void +Watchpoint::WatchpointEventData::Dump (Stream *s) const +{ +} + +const Watchpoint::WatchpointEventData * +Watchpoint::WatchpointEventData::GetEventDataFromEvent (const Event *event) +{ + if (event) + { + const EventData *event_data = event->GetData(); + if (event_data && event_data->GetFlavor() == WatchpointEventData::GetFlavorString()) + return static_cast (event->GetData()); + } + return NULL; +} + +WatchpointEventType +Watchpoint::WatchpointEventData::GetWatchpointEventTypeFromEvent (const EventSP &event_sp) +{ + const WatchpointEventData *data = GetEventDataFromEvent (event_sp.get()); + + if (data == NULL) + return eWatchpointEventTypeInvalidType; + else + return data->GetWatchpointEventType(); +} + +WatchpointSP +Watchpoint::WatchpointEventData::GetWatchpointFromEvent (const EventSP &event_sp) +{ + WatchpointSP wp_sp; + + const WatchpointEventData *data = GetEventDataFromEvent (event_sp.get()); + if (data) + wp_sp = data->m_new_watchpoint_sp; + + return wp_sp; +} diff --git a/contrib/llvm/tools/lldb/source/Breakpoint/WatchpointList.cpp b/contrib/llvm/tools/lldb/source/Breakpoint/WatchpointList.cpp new file mode 100644 index 00000000000..6d62dffd22c --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Breakpoint/WatchpointList.cpp @@ -0,0 +1,306 @@ +//===-- WatchpointList.cpp --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Breakpoint/WatchpointList.h" +#include "lldb/Breakpoint/Watchpoint.h" + +using namespace lldb; +using namespace lldb_private; + +WatchpointList::WatchpointList() : + m_watchpoints (), + m_mutex (Mutex::eMutexTypeRecursive), + m_next_wp_id (0) +{ +} + +WatchpointList::~WatchpointList() +{ +} + +// Add a watchpoint to the list. +lldb::watch_id_t +WatchpointList::Add (const WatchpointSP &wp_sp, bool notify) +{ + Mutex::Locker locker (m_mutex); + wp_sp->SetID(++m_next_wp_id); + m_watchpoints.push_back(wp_sp); + if (notify) + { + if (wp_sp->GetTarget().EventTypeHasListeners(Target::eBroadcastBitWatchpointChanged)) + wp_sp->GetTarget().BroadcastEvent (Target::eBroadcastBitWatchpointChanged, + new Watchpoint::WatchpointEventData (eWatchpointEventTypeAdded, wp_sp)); + } + return wp_sp->GetID(); +} + +void +WatchpointList::Dump (Stream *s) const +{ + DumpWithLevel(s, lldb::eDescriptionLevelBrief); +} + +void +WatchpointList::DumpWithLevel (Stream *s, lldb::DescriptionLevel description_level) const +{ + Mutex::Locker locker (m_mutex); + s->Printf("%p: ", this); + //s->Indent(); + s->Printf("WatchpointList with %" PRIu64 " Watchpoints:\n", + (uint64_t)m_watchpoints.size()); + s->IndentMore(); + wp_collection::const_iterator pos, end = m_watchpoints.end(); + for (pos = m_watchpoints.begin(); pos != end; ++pos) + (*pos)->DumpWithLevel(s, description_level); + s->IndentLess(); +} + +const WatchpointSP +WatchpointList::FindByAddress (lldb::addr_t addr) const +{ + WatchpointSP wp_sp; + Mutex::Locker locker (m_mutex); + if (!m_watchpoints.empty()) + { + wp_collection::const_iterator pos, end = m_watchpoints.end(); + for (pos = m_watchpoints.begin(); pos != end; ++pos) + if ((*pos)->GetLoadAddress() == addr) { + wp_sp = *pos; + break; + } + } + + return wp_sp; +} + +const WatchpointSP +WatchpointList::FindBySpec (std::string spec) const +{ + WatchpointSP wp_sp; + Mutex::Locker locker (m_mutex); + if (!m_watchpoints.empty()) + { + wp_collection::const_iterator pos, end = m_watchpoints.end(); + for (pos = m_watchpoints.begin(); pos != end; ++pos) + if ((*pos)->GetWatchSpec() == spec) { + wp_sp = *pos; + break; + } + } + + return wp_sp; +} + +class WatchpointIDMatches +{ +public: + WatchpointIDMatches (lldb::watch_id_t watch_id) : + m_watch_id(watch_id) + { + } + + bool operator() (const WatchpointSP &wp) const + { + return m_watch_id == wp->GetID(); + } + +private: + const lldb::watch_id_t m_watch_id; +}; + +WatchpointList::wp_collection::iterator +WatchpointList::GetIDIterator (lldb::watch_id_t watch_id) +{ + return std::find_if(m_watchpoints.begin(), m_watchpoints.end(), // Search full range + WatchpointIDMatches(watch_id)); // Predicate +} + +WatchpointList::wp_collection::const_iterator +WatchpointList::GetIDConstIterator (lldb::watch_id_t watch_id) const +{ + return std::find_if(m_watchpoints.begin(), m_watchpoints.end(), // Search full range + WatchpointIDMatches(watch_id)); // Predicate +} + +WatchpointSP +WatchpointList::FindByID (lldb::watch_id_t watch_id) const +{ + WatchpointSP wp_sp; + Mutex::Locker locker (m_mutex); + wp_collection::const_iterator pos = GetIDConstIterator(watch_id); + if (pos != m_watchpoints.end()) + wp_sp = *pos; + + return wp_sp; +} + +lldb::watch_id_t +WatchpointList::FindIDByAddress (lldb::addr_t addr) +{ + WatchpointSP wp_sp = FindByAddress (addr); + if (wp_sp) + { + return wp_sp->GetID(); + } + return LLDB_INVALID_WATCH_ID; +} + +lldb::watch_id_t +WatchpointList::FindIDBySpec (std::string spec) +{ + WatchpointSP wp_sp = FindBySpec (spec); + if (wp_sp) + { + return wp_sp->GetID(); + } + return LLDB_INVALID_WATCH_ID; +} + +WatchpointSP +WatchpointList::GetByIndex (uint32_t i) +{ + Mutex::Locker locker (m_mutex); + WatchpointSP wp_sp; + if (i < m_watchpoints.size()) + { + wp_collection::const_iterator pos = m_watchpoints.begin(); + std::advance(pos, i); + wp_sp = *pos; + } + return wp_sp; +} + +const WatchpointSP +WatchpointList::GetByIndex (uint32_t i) const +{ + Mutex::Locker locker (m_mutex); + WatchpointSP wp_sp; + if (i < m_watchpoints.size()) + { + wp_collection::const_iterator pos = m_watchpoints.begin(); + std::advance(pos, i); + wp_sp = *pos; + } + return wp_sp; +} + +std::vector +WatchpointList::GetWatchpointIDs() const +{ + std::vector IDs; + wp_collection::const_iterator pos, end = m_watchpoints.end(); + for (pos = m_watchpoints.begin(); pos != end; ++pos) + IDs.push_back((*pos)->GetID()); + return IDs; +} + +bool +WatchpointList::Remove (lldb::watch_id_t watch_id, bool notify) +{ + Mutex::Locker locker (m_mutex); + wp_collection::iterator pos = GetIDIterator(watch_id); + if (pos != m_watchpoints.end()) + { + WatchpointSP wp_sp = *pos; + if (notify) + { + if (wp_sp->GetTarget().EventTypeHasListeners(Target::eBroadcastBitWatchpointChanged)) + wp_sp->GetTarget().BroadcastEvent (Target::eBroadcastBitWatchpointChanged, + new Watchpoint::WatchpointEventData (eWatchpointEventTypeRemoved, wp_sp)); + } + m_watchpoints.erase(pos); + return true; + } + return false; +} + +uint32_t +WatchpointList::GetHitCount () const +{ + uint32_t hit_count = 0; + Mutex::Locker locker (m_mutex); + wp_collection::const_iterator pos, end = m_watchpoints.end(); + for (pos = m_watchpoints.begin(); pos != end; ++pos) + hit_count += (*pos)->GetHitCount(); + return hit_count; +} + +bool +WatchpointList::ShouldStop (StoppointCallbackContext *context, lldb::watch_id_t watch_id) +{ + + WatchpointSP wp_sp = FindByID (watch_id); + if (wp_sp) + { + // Let the Watchpoint decide if it should stop here (could not have + // reached it's target hit count yet, or it could have a callback + // that decided it shouldn't stop. + return wp_sp->ShouldStop (context); + } + // We should stop here since this Watchpoint isn't valid anymore or it + // doesn't exist. + return true; +} + +void +WatchpointList::GetDescription (Stream *s, lldb::DescriptionLevel level) +{ + Mutex::Locker locker (m_mutex); + wp_collection::iterator pos, end = m_watchpoints.end(); + + for (pos = m_watchpoints.begin(); pos != end; ++pos) + { + s->Printf(" "); + (*pos)->Dump(s); + } +} + +void +WatchpointList::SetEnabledAll (bool enabled) +{ + Mutex::Locker locker(m_mutex); + + wp_collection::iterator pos, end = m_watchpoints.end(); + for (pos = m_watchpoints.begin(); pos != end; ++pos) + (*pos)->SetEnabled (enabled); +} + +void +WatchpointList::RemoveAll (bool notify) +{ + Mutex::Locker locker(m_mutex); + if (notify) + { + + { + wp_collection::iterator pos, end = m_watchpoints.end(); + for (pos = m_watchpoints.begin(); pos != end; ++pos) + { + if ((*pos)->GetTarget().EventTypeHasListeners(Target::eBroadcastBitBreakpointChanged)) + { + (*pos)->GetTarget().BroadcastEvent (Target::eBroadcastBitWatchpointChanged, + new Watchpoint::WatchpointEventData (eWatchpointEventTypeRemoved, + *pos)); + } + } + } + } + m_watchpoints.clear(); +} + +void +WatchpointList::GetListMutex (Mutex::Locker &locker) +{ + return locker.Lock (m_mutex); +} diff --git a/contrib/llvm/tools/lldb/source/Breakpoint/WatchpointOptions.cpp b/contrib/llvm/tools/lldb/source/Breakpoint/WatchpointOptions.cpp new file mode 100644 index 00000000000..c2c9696c4ce --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Breakpoint/WatchpointOptions.cpp @@ -0,0 +1,241 @@ +//===-- WatchpointOptions.cpp -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Breakpoint/WatchpointOptions.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/Stream.h" +#include "lldb/Core/StringList.h" +#include "lldb/Core/Value.h" +#include "lldb/Breakpoint/StoppointCallbackContext.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/ThreadSpec.h" +#include "lldb/Expression/ClangUserExpression.h" + +using namespace lldb; +using namespace lldb_private; + +bool +WatchpointOptions::NullCallback (void *baton, StoppointCallbackContext *context, lldb::user_id_t watch_id) +{ + return true; +} + +//---------------------------------------------------------------------- +// WatchpointOptions constructor +//---------------------------------------------------------------------- +WatchpointOptions::WatchpointOptions() : + m_callback (WatchpointOptions::NullCallback), + m_callback_baton_sp (), + m_callback_is_synchronous (false), + m_thread_spec_ap () +{ +} + +//---------------------------------------------------------------------- +// WatchpointOptions copy constructor +//---------------------------------------------------------------------- +WatchpointOptions::WatchpointOptions(const WatchpointOptions& rhs) : + m_callback (rhs.m_callback), + m_callback_baton_sp (rhs.m_callback_baton_sp), + m_callback_is_synchronous (rhs.m_callback_is_synchronous), + m_thread_spec_ap () +{ + if (rhs.m_thread_spec_ap.get() != NULL) + m_thread_spec_ap.reset (new ThreadSpec(*rhs.m_thread_spec_ap.get())); +} + +//---------------------------------------------------------------------- +// WatchpointOptions assignment operator +//---------------------------------------------------------------------- +const WatchpointOptions& +WatchpointOptions::operator=(const WatchpointOptions& rhs) +{ + m_callback = rhs.m_callback; + m_callback_baton_sp = rhs.m_callback_baton_sp; + m_callback_is_synchronous = rhs.m_callback_is_synchronous; + if (rhs.m_thread_spec_ap.get() != NULL) + m_thread_spec_ap.reset(new ThreadSpec(*rhs.m_thread_spec_ap.get())); + return *this; +} + +WatchpointOptions * +WatchpointOptions::CopyOptionsNoCallback (WatchpointOptions &orig) +{ + WatchpointHitCallback orig_callback = orig.m_callback; + lldb::BatonSP orig_callback_baton_sp = orig.m_callback_baton_sp; + bool orig_is_sync = orig.m_callback_is_synchronous; + + orig.ClearCallback(); + WatchpointOptions *ret_val = new WatchpointOptions(orig); + + orig.SetCallback (orig_callback, orig_callback_baton_sp, orig_is_sync); + + return ret_val; +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +WatchpointOptions::~WatchpointOptions() +{ +} + +//------------------------------------------------------------------ +// Callbacks +//------------------------------------------------------------------ +void +WatchpointOptions::SetCallback (WatchpointHitCallback callback, const BatonSP &callback_baton_sp, bool callback_is_synchronous) +{ + m_callback_is_synchronous = callback_is_synchronous; + m_callback = callback; + m_callback_baton_sp = callback_baton_sp; +} + +void +WatchpointOptions::ClearCallback () +{ + m_callback = WatchpointOptions::NullCallback; + m_callback_is_synchronous = false; + m_callback_baton_sp.reset(); +} + +Baton * +WatchpointOptions::GetBaton () +{ + return m_callback_baton_sp.get(); +} + +const Baton * +WatchpointOptions::GetBaton () const +{ + return m_callback_baton_sp.get(); +} + +bool +WatchpointOptions::InvokeCallback (StoppointCallbackContext *context, + lldb::user_id_t watch_id) +{ + if (m_callback && context->is_synchronous == IsCallbackSynchronous()) + { + return m_callback (m_callback_baton_sp ? m_callback_baton_sp->m_data : NULL, + context, + watch_id); + } + else + return true; +} + +bool +WatchpointOptions::HasCallback () +{ + return m_callback != WatchpointOptions::NullCallback; +} + +const ThreadSpec * +WatchpointOptions::GetThreadSpecNoCreate () const +{ + return m_thread_spec_ap.get(); +} + +ThreadSpec * +WatchpointOptions::GetThreadSpec () +{ + if (m_thread_spec_ap.get() == NULL) + m_thread_spec_ap.reset (new ThreadSpec()); + + return m_thread_spec_ap.get(); +} + +void +WatchpointOptions::SetThreadID (lldb::tid_t thread_id) +{ + GetThreadSpec()->SetTID(thread_id); +} + +void +WatchpointOptions::GetCallbackDescription (Stream *s, lldb::DescriptionLevel level) const +{ + if (m_callback_baton_sp.get()) + { + s->EOL(); + m_callback_baton_sp->GetDescription (s, level); + } +} +void +WatchpointOptions::GetDescription (Stream *s, lldb::DescriptionLevel level) const +{ + + // Figure out if there are any options not at their default value, and only print + // anything if there are: + + if ((GetThreadSpecNoCreate() != NULL && GetThreadSpecNoCreate()->HasSpecification ())) + { + if (level == lldb::eDescriptionLevelVerbose) + { + s->EOL (); + s->IndentMore(); + s->Indent(); + s->PutCString("Watchpoint Options:\n"); + s->IndentMore(); + s->Indent(); + } + else + s->PutCString(" Options: "); + + if (m_thread_spec_ap.get()) + m_thread_spec_ap->GetDescription (s, level); + else if (level == eDescriptionLevelBrief) + s->PutCString ("thread spec: no "); + if (level == lldb::eDescriptionLevelFull) + { + s->IndentLess(); + s->IndentMore(); + } + } + + GetCallbackDescription(s, level); +} + +void +WatchpointOptions::CommandBaton::GetDescription (Stream *s, lldb::DescriptionLevel level) const +{ + CommandData *data = (CommandData *)m_data; + + if (level == eDescriptionLevelBrief) + { + s->Printf (", commands = %s", (data && data->user_source.GetSize() > 0) ? "yes" : "no"); + return; + } + + s->IndentMore (); + s->Indent("watchpoint commands:\n"); + + s->IndentMore (); + if (data && data->user_source.GetSize() > 0) + { + const size_t num_strings = data->user_source.GetSize(); + for (size_t i = 0; i < num_strings; ++i) + { + s->Indent(data->user_source.GetStringAtIndex(i)); + s->EOL(); + } + } + else + { + s->PutCString ("No commands.\n"); + } + s->IndentLess (); + s->IndentLess (); +} + diff --git a/contrib/llvm/tools/lldb/source/Commands/CommandCompletions.cpp b/contrib/llvm/tools/lldb/source/Commands/CommandCompletions.cpp new file mode 100644 index 00000000000..a9d2f21b9ba --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Commands/CommandCompletions.cpp @@ -0,0 +1,754 @@ +//===-- CommandCompletions.cpp ----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +// C Includes +#include +#include +#if defined(__APPLE__) || defined(__linux__) +#include +#endif + +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Host/FileSpec.h" +#include "lldb/Core/FileSpecList.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/Module.h" +#include "lldb/Interpreter/Args.h" +#include "lldb/Interpreter/CommandCompletions.h" +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Symbol/CompileUnit.h" +#include "lldb/Symbol/Variable.h" +#include "lldb/Target/Target.h" +#include "lldb/Utility/CleanUp.h" + +using namespace lldb_private; + +CommandCompletions::CommonCompletionElement +CommandCompletions::g_common_completions[] = +{ + {eCustomCompletion, NULL}, + {eSourceFileCompletion, CommandCompletions::SourceFiles}, + {eDiskFileCompletion, CommandCompletions::DiskFiles}, + {eDiskDirectoryCompletion, CommandCompletions::DiskDirectories}, + {eSymbolCompletion, CommandCompletions::Symbols}, + {eModuleCompletion, CommandCompletions::Modules}, + {eSettingsNameCompletion, CommandCompletions::SettingsNames}, + {ePlatformPluginCompletion, CommandCompletions::PlatformPluginNames}, + {eArchitectureCompletion, CommandCompletions::ArchitectureNames}, + {eVariablePathCompletion, CommandCompletions::VariablePath}, + {eNoCompletion, NULL} // This one has to be last in the list. +}; + +bool +CommandCompletions::InvokeCommonCompletionCallbacks +( + CommandInterpreter &interpreter, + uint32_t completion_mask, + const char *completion_str, + int match_start_point, + int max_return_elements, + SearchFilter *searcher, + bool &word_complete, + StringList &matches +) +{ + bool handled = false; + + if (completion_mask & eCustomCompletion) + return false; + + for (int i = 0; ; i++) + { + if (g_common_completions[i].type == eNoCompletion) + break; + else if ((g_common_completions[i].type & completion_mask) == g_common_completions[i].type + && g_common_completions[i].callback != NULL) + { + handled = true; + g_common_completions[i].callback (interpreter, + completion_str, + match_start_point, + max_return_elements, + searcher, + word_complete, + matches); + } + } + return handled; +} + +int +CommandCompletions::SourceFiles +( + CommandInterpreter &interpreter, + const char *partial_file_name, + int match_start_point, + int max_return_elements, + SearchFilter *searcher, + bool &word_complete, + StringList &matches +) +{ + word_complete = true; + // Find some way to switch "include support files..." + SourceFileCompleter completer (interpreter, + false, + partial_file_name, + match_start_point, + max_return_elements, + matches); + + if (searcher == NULL) + { + lldb::TargetSP target_sp = interpreter.GetDebugger().GetSelectedTarget(); + SearchFilter null_searcher (target_sp); + completer.DoCompletion (&null_searcher); + } + else + { + completer.DoCompletion (searcher); + } + return matches.GetSize(); +} + +static int +DiskFilesOrDirectories +( + const char *partial_file_name, + bool only_directories, + bool &saw_directory, + StringList &matches +) +{ + // I'm going to use the "glob" function with GLOB_TILDE for user directory expansion. + // If it is not defined on your host system, you'll need to implement it yourself... + + size_t partial_name_len = strlen(partial_file_name); + + if (partial_name_len >= PATH_MAX) + return matches.GetSize(); + + // This copy of the string will be cut up into the directory part, and the remainder. end_ptr + // below will point to the place of the remainder in this string. Then when we've resolved the + // containing directory, and opened it, we'll read the directory contents and overwrite the + // partial_name_copy starting from end_ptr with each of the matches. Thus we will preserve + // the form the user originally typed. + + char partial_name_copy[PATH_MAX]; + memcpy(partial_name_copy, partial_file_name, partial_name_len); + partial_name_copy[partial_name_len] = '\0'; + + // We'll need to save a copy of the remainder for comparison, which we do here. + char remainder[PATH_MAX]; + + // end_ptr will point past the last / in partial_name_copy, or if there is no slash to the beginning of the string. + char *end_ptr; + + end_ptr = strrchr(partial_name_copy, '/'); + + // This will store the resolved form of the containing directory + char containing_part[PATH_MAX]; + + if (end_ptr == NULL) + { + // There's no directory. If the thing begins with a "~" then this is a bare + // user name. + if (*partial_name_copy == '~') + { + // Nothing here but the user name. We could just put a slash on the end, + // but for completeness sake we'll resolve the user name and only put a slash + // on the end if it exists. + char resolved_username[PATH_MAX]; + size_t resolved_username_len = FileSpec::ResolveUsername (partial_name_copy, resolved_username, + sizeof (resolved_username)); + + // Not sure how this would happen, a username longer than PATH_MAX? Still... + if (resolved_username_len >= sizeof (resolved_username)) + return matches.GetSize(); + else if (resolved_username_len == 0) + { + // The user name didn't resolve, let's look in the password database for matches. + // The user name database contains duplicates, and is not in alphabetical order, so + // we'll use a set to manage that for us. + FileSpec::ResolvePartialUsername (partial_name_copy, matches); + if (matches.GetSize() > 0) + saw_directory = true; + return matches.GetSize(); + } + else + { + //The thing exists, put a '/' on the end, and return it... + // FIXME: complete user names here: + partial_name_copy[partial_name_len] = '/'; + partial_name_copy[partial_name_len+1] = '\0'; + matches.AppendString(partial_name_copy); + saw_directory = true; + return matches.GetSize(); + } + } + else + { + // The containing part is the CWD, and the whole string is the remainder. + containing_part[0] = '.'; + containing_part[1] = '\0'; + strcpy(remainder, partial_name_copy); + end_ptr = partial_name_copy; + } + } + else + { + if (end_ptr == partial_name_copy) + { + // We're completing a file or directory in the root volume. + containing_part[0] = '/'; + containing_part[1] = '\0'; + } + else + { + size_t len = end_ptr - partial_name_copy; + memcpy(containing_part, partial_name_copy, len); + containing_part[len] = '\0'; + } + // Push end_ptr past the final "/" and set remainder. + end_ptr++; + strcpy(remainder, end_ptr); + } + + // Look for a user name in the containing part, and if it's there, resolve it and stick the + // result back into the containing_part: + + if (*partial_name_copy == '~') + { + size_t resolved_username_len = FileSpec::ResolveUsername(containing_part, + containing_part, + sizeof (containing_part)); + // User name doesn't exist, we're not getting any further... + if (resolved_username_len == 0 || resolved_username_len >= sizeof (containing_part)) + return matches.GetSize(); + } + + // Okay, containing_part is now the directory we want to open and look for files: + + lldb_utility::CleanUp dir_stream (opendir(containing_part), NULL, closedir); + if (!dir_stream.is_valid()) + return matches.GetSize(); + + struct dirent *dirent_buf; + + size_t baselen = end_ptr - partial_name_copy; + + while ((dirent_buf = readdir(dir_stream.get())) != NULL) + { + char *name = dirent_buf->d_name; + + // Omit ".", ".." and any . files if the match string doesn't start with . + if (name[0] == '.') + { + if (name[1] == '\0') + continue; + else if (name[1] == '.' && name[2] == '\0') + continue; + else if (remainder[0] != '.') + continue; + } + + // If we found a directory, we put a "/" at the end of the name. + + if (remainder[0] == '\0' || strstr(dirent_buf->d_name, remainder) == name) + { + if (strlen(name) + baselen >= PATH_MAX) + continue; + + strcpy(end_ptr, name); + + bool isa_directory = false; + if (dirent_buf->d_type & DT_DIR) + isa_directory = true; + else if (dirent_buf->d_type & DT_LNK) + { + struct stat stat_buf; + if ((stat(partial_name_copy, &stat_buf) == 0) && S_ISDIR(stat_buf.st_mode)) + isa_directory = true; + } + + if (isa_directory) + { + saw_directory = true; + size_t len = strlen(partial_name_copy); + partial_name_copy[len] = '/'; + partial_name_copy[len + 1] = '\0'; + } + if (only_directories && !isa_directory) + continue; + matches.AppendString(partial_name_copy); + } + } + + return matches.GetSize(); +} + +int +CommandCompletions::DiskFiles +( + CommandInterpreter &interpreter, + const char *partial_file_name, + int match_start_point, + int max_return_elements, + SearchFilter *searcher, + bool &word_complete, + StringList &matches +) +{ + + int ret_val = DiskFilesOrDirectories (partial_file_name, + false, + word_complete, + matches); + word_complete = !word_complete; + return ret_val; +} + +int +CommandCompletions::DiskDirectories +( + CommandInterpreter &interpreter, + const char *partial_file_name, + int match_start_point, + int max_return_elements, + SearchFilter *searcher, + bool &word_complete, + StringList &matches +) +{ + int ret_val = DiskFilesOrDirectories (partial_file_name, + true, + word_complete, + matches); + word_complete = false; + return ret_val; +} + +int +CommandCompletions::Modules +( + CommandInterpreter &interpreter, + const char *partial_file_name, + int match_start_point, + int max_return_elements, + SearchFilter *searcher, + bool &word_complete, + StringList &matches +) +{ + word_complete = true; + ModuleCompleter completer (interpreter, + partial_file_name, + match_start_point, + max_return_elements, + matches); + + if (searcher == NULL) + { + lldb::TargetSP target_sp = interpreter.GetDebugger().GetSelectedTarget(); + SearchFilter null_searcher (target_sp); + completer.DoCompletion (&null_searcher); + } + else + { + completer.DoCompletion (searcher); + } + return matches.GetSize(); +} + +int +CommandCompletions::Symbols +( + CommandInterpreter &interpreter, + const char *partial_file_name, + int match_start_point, + int max_return_elements, + SearchFilter *searcher, + bool &word_complete, + StringList &matches) +{ + word_complete = true; + SymbolCompleter completer (interpreter, + partial_file_name, + match_start_point, + max_return_elements, + matches); + + if (searcher == NULL) + { + lldb::TargetSP target_sp = interpreter.GetDebugger().GetSelectedTarget(); + SearchFilter null_searcher (target_sp); + completer.DoCompletion (&null_searcher); + } + else + { + completer.DoCompletion (searcher); + } + return matches.GetSize(); +} + +int +CommandCompletions::SettingsNames (CommandInterpreter &interpreter, + const char *partial_setting_name, + int match_start_point, + int max_return_elements, + SearchFilter *searcher, + bool &word_complete, + StringList &matches) +{ + // Cache the full setting name list + static StringList g_property_names; + if (g_property_names.GetSize() == 0) + { + // Generate the full setting name list on demand + lldb::OptionValuePropertiesSP properties_sp (interpreter.GetDebugger().GetValueProperties()); + if (properties_sp) + { + StreamString strm; + properties_sp->DumpValue(NULL, strm, OptionValue::eDumpOptionName); + const std::string &str = strm.GetString(); + g_property_names.SplitIntoLines(str.c_str(), str.size()); + } + } + + size_t exact_matches_idx = SIZE_MAX; + const size_t num_matches = g_property_names.AutoComplete (partial_setting_name, matches, exact_matches_idx); + word_complete = exact_matches_idx != SIZE_MAX; + return num_matches; +} + + +int +CommandCompletions::PlatformPluginNames (CommandInterpreter &interpreter, + const char *partial_name, + int match_start_point, + int max_return_elements, + SearchFilter *searcher, + bool &word_complete, + lldb_private::StringList &matches) +{ + const uint32_t num_matches = PluginManager::AutoCompletePlatformName(partial_name, matches); + word_complete = num_matches == 1; + return num_matches; +} + +int +CommandCompletions::ArchitectureNames (CommandInterpreter &interpreter, + const char *partial_name, + int match_start_point, + int max_return_elements, + SearchFilter *searcher, + bool &word_complete, + lldb_private::StringList &matches) +{ + const uint32_t num_matches = ArchSpec::AutoComplete (partial_name, matches); + word_complete = num_matches == 1; + return num_matches; +} + + +int +CommandCompletions::VariablePath (CommandInterpreter &interpreter, + const char *partial_name, + int match_start_point, + int max_return_elements, + SearchFilter *searcher, + bool &word_complete, + lldb_private::StringList &matches) +{ + return Variable::AutoComplete (interpreter.GetExecutionContext(), partial_name, matches, word_complete); +} + + +CommandCompletions::Completer::Completer +( + CommandInterpreter &interpreter, + const char *completion_str, + int match_start_point, + int max_return_elements, + StringList &matches +) : + m_interpreter (interpreter), + m_completion_str (completion_str), + m_match_start_point (match_start_point), + m_max_return_elements (max_return_elements), + m_matches (matches) +{ +} + +CommandCompletions::Completer::~Completer () +{ + +} + +//---------------------------------------------------------------------- +// SourceFileCompleter +//---------------------------------------------------------------------- + +CommandCompletions::SourceFileCompleter::SourceFileCompleter +( + CommandInterpreter &interpreter, + bool include_support_files, + const char *completion_str, + int match_start_point, + int max_return_elements, + StringList &matches +) : + CommandCompletions::Completer (interpreter, completion_str, match_start_point, max_return_elements, matches), + m_include_support_files (include_support_files), + m_matching_files() +{ + FileSpec partial_spec (m_completion_str.c_str(), false); + m_file_name = partial_spec.GetFilename().GetCString(); + m_dir_name = partial_spec.GetDirectory().GetCString(); +} + +Searcher::Depth +CommandCompletions::SourceFileCompleter::GetDepth() +{ + return eDepthCompUnit; +} + +Searcher::CallbackReturn +CommandCompletions::SourceFileCompleter::SearchCallback ( + SearchFilter &filter, + SymbolContext &context, + Address *addr, + bool complete +) +{ + if (context.comp_unit != NULL) + { + if (m_include_support_files) + { + FileSpecList supporting_files = context.comp_unit->GetSupportFiles(); + for (size_t sfiles = 0; sfiles < supporting_files.GetSize(); sfiles++) + { + const FileSpec &sfile_spec = supporting_files.GetFileSpecAtIndex(sfiles); + const char *sfile_file_name = sfile_spec.GetFilename().GetCString(); + const char *sfile_dir_name = sfile_spec.GetFilename().GetCString(); + bool match = false; + if (m_file_name && sfile_file_name + && strstr (sfile_file_name, m_file_name) == sfile_file_name) + match = true; + if (match && m_dir_name && sfile_dir_name + && strstr (sfile_dir_name, m_dir_name) != sfile_dir_name) + match = false; + + if (match) + { + m_matching_files.AppendIfUnique(sfile_spec); + } + } + + } + else + { + const char *cur_file_name = context.comp_unit->GetFilename().GetCString(); + const char *cur_dir_name = context.comp_unit->GetDirectory().GetCString(); + + bool match = false; + if (m_file_name && cur_file_name + && strstr (cur_file_name, m_file_name) == cur_file_name) + match = true; + + if (match && m_dir_name && cur_dir_name + && strstr (cur_dir_name, m_dir_name) != cur_dir_name) + match = false; + + if (match) + { + m_matching_files.AppendIfUnique(context.comp_unit); + } + } + } + return Searcher::eCallbackReturnContinue; +} + +size_t +CommandCompletions::SourceFileCompleter::DoCompletion (SearchFilter *filter) +{ + filter->Search (*this); + // Now convert the filelist to completions: + for (size_t i = 0; i < m_matching_files.GetSize(); i++) + { + m_matches.AppendString (m_matching_files.GetFileSpecAtIndex(i).GetFilename().GetCString()); + } + return m_matches.GetSize(); + +} + +//---------------------------------------------------------------------- +// SymbolCompleter +//---------------------------------------------------------------------- + +static bool +regex_chars (const char comp) +{ + if (comp == '[' || comp == ']' || + comp == '(' || comp == ')' || + comp == '{' || comp == '}' || + comp == '+' || + comp == '.' || + comp == '*' || + comp == '|' || + comp == '^' || + comp == '$' || + comp == '\\' || + comp == '?') + return true; + else + return false; +} +CommandCompletions::SymbolCompleter::SymbolCompleter +( + CommandInterpreter &interpreter, + const char *completion_str, + int match_start_point, + int max_return_elements, + StringList &matches +) : + CommandCompletions::Completer (interpreter, completion_str, match_start_point, max_return_elements, matches) +{ + std::string regex_str; + if (completion_str && completion_str[0]) + { + regex_str.append("^"); + regex_str.append(completion_str); + } + else + { + // Match anything since the completion string is empty + regex_str.append("."); + } + std::string::iterator pos = find_if(regex_str.begin() + 1, regex_str.end(), regex_chars); + while (pos < regex_str.end()) + { + pos = regex_str.insert(pos, '\\'); + pos = find_if(pos + 2, regex_str.end(), regex_chars); + } + m_regex.Compile(regex_str.c_str()); +} + +Searcher::Depth +CommandCompletions::SymbolCompleter::GetDepth() +{ + return eDepthModule; +} + +Searcher::CallbackReturn +CommandCompletions::SymbolCompleter::SearchCallback ( + SearchFilter &filter, + SymbolContext &context, + Address *addr, + bool complete +) +{ + if (context.module_sp) + { + SymbolContextList sc_list; + const bool include_symbols = true; + const bool include_inlines = true; + const bool append = true; + context.module_sp->FindFunctions (m_regex, include_symbols, include_inlines, append, sc_list); + + SymbolContext sc; + // Now add the functions & symbols to the list - only add if unique: + for (uint32_t i = 0; i < sc_list.GetSize(); i++) + { + if (sc_list.GetContextAtIndex(i, sc)) + { + ConstString func_name = sc.GetFunctionName(Mangled::ePreferDemangled); + if (!func_name.IsEmpty()) + m_match_set.insert (func_name); + } + } + } + return Searcher::eCallbackReturnContinue; +} + +size_t +CommandCompletions::SymbolCompleter::DoCompletion (SearchFilter *filter) +{ + filter->Search (*this); + collection::iterator pos = m_match_set.begin(), end = m_match_set.end(); + for (pos = m_match_set.begin(); pos != end; pos++) + m_matches.AppendString((*pos).GetCString()); + + return m_matches.GetSize(); +} + +//---------------------------------------------------------------------- +// ModuleCompleter +//---------------------------------------------------------------------- +CommandCompletions::ModuleCompleter::ModuleCompleter +( + CommandInterpreter &interpreter, + const char *completion_str, + int match_start_point, + int max_return_elements, + StringList &matches +) : + CommandCompletions::Completer (interpreter, completion_str, match_start_point, max_return_elements, matches) +{ + FileSpec partial_spec (m_completion_str.c_str(), false); + m_file_name = partial_spec.GetFilename().GetCString(); + m_dir_name = partial_spec.GetDirectory().GetCString(); +} + +Searcher::Depth +CommandCompletions::ModuleCompleter::GetDepth() +{ + return eDepthModule; +} + +Searcher::CallbackReturn +CommandCompletions::ModuleCompleter::SearchCallback ( + SearchFilter &filter, + SymbolContext &context, + Address *addr, + bool complete +) +{ + if (context.module_sp) + { + const char *cur_file_name = context.module_sp->GetFileSpec().GetFilename().GetCString(); + const char *cur_dir_name = context.module_sp->GetFileSpec().GetDirectory().GetCString(); + + bool match = false; + if (m_file_name && cur_file_name + && strstr (cur_file_name, m_file_name) == cur_file_name) + match = true; + + if (match && m_dir_name && cur_dir_name + && strstr (cur_dir_name, m_dir_name) != cur_dir_name) + match = false; + + if (match) + { + m_matches.AppendString (cur_file_name); + } + } + return Searcher::eCallbackReturnContinue; +} + +size_t +CommandCompletions::ModuleCompleter::DoCompletion (SearchFilter *filter) +{ + filter->Search (*this); + return m_matches.GetSize(); +} diff --git a/contrib/llvm/tools/lldb/source/Commands/CommandObjectApropos.cpp b/contrib/llvm/tools/lldb/source/Commands/CommandObjectApropos.cpp new file mode 100644 index 00000000000..02dc7269775 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Commands/CommandObjectApropos.cpp @@ -0,0 +1,154 @@ +//===-- CommandObjectApropos.cpp ---------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "CommandObjectApropos.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Interpreter/Args.h" +#include "lldb/Interpreter/Options.h" + +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Interpreter/CommandReturnObject.h" + +using namespace lldb; +using namespace lldb_private; + +//------------------------------------------------------------------------- +// CommandObjectApropos +//------------------------------------------------------------------------- + +CommandObjectApropos::CommandObjectApropos (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "apropos", + "Find a list of debugger commands related to a particular word/subject.", + NULL) +{ + CommandArgumentEntry arg; + CommandArgumentData search_word_arg; + + // Define the first (and only) variant of this arg. + search_word_arg.arg_type = eArgTypeSearchWord; + search_word_arg.arg_repetition = eArgRepeatPlain; + + // There is only one variant this argument could be; put it into the argument entry. + arg.push_back (search_word_arg); + + // Push the data for the first argument into the m_arguments vector. + m_arguments.push_back (arg); +} + +CommandObjectApropos::~CommandObjectApropos() +{ +} + + +bool +CommandObjectApropos::DoExecute (Args& args, CommandReturnObject &result) +{ + const size_t argc = args.GetArgumentCount (); + + if (argc == 1) + { + const char *search_word = args.GetArgumentAtIndex(0); + if ((search_word != NULL) + && (strlen (search_word) > 0)) + { + // The bulk of the work must be done inside the Command Interpreter, since the command dictionary + // is private. + StringList commands_found; + StringList commands_help; + StringList user_commands_found; + StringList user_commands_help; + + m_interpreter.FindCommandsForApropos (search_word, commands_found, commands_help, true, false); + m_interpreter.FindCommandsForApropos (search_word, user_commands_found, user_commands_help, false, true); + + if (commands_found.GetSize() == 0 && user_commands_found.GetSize() == 0) + { + result.AppendMessageWithFormat ("No commands found pertaining to '%s'. Try 'help' to see a complete list of debugger commands.\n", search_word); + } + else + { + if (commands_found.GetSize() > 0) + { + result.AppendMessageWithFormat ("The following built-in commands may relate to '%s':\n", search_word); + size_t max_len = 0; + + for (size_t i = 0; i < commands_found.GetSize(); ++i) + { + size_t len = strlen (commands_found.GetStringAtIndex (i)); + if (len > max_len) + max_len = len; + } + + for (size_t i = 0; i < commands_found.GetSize(); ++i) + m_interpreter.OutputFormattedHelpText (result.GetOutputStream(), + commands_found.GetStringAtIndex(i), + "--", + commands_help.GetStringAtIndex(i), + max_len); + if (user_commands_found.GetSize() > 0) + result.AppendMessage(""); + } + + if (user_commands_found.GetSize() > 0) + { + result.AppendMessageWithFormat ("The following user commands may relate to '%s':\n", search_word); + size_t max_len = 0; + + for (size_t i = 0; i < user_commands_found.GetSize(); ++i) + { + size_t len = strlen (user_commands_found.GetStringAtIndex (i)); + if (len > max_len) + max_len = len; + } + + for (size_t i = 0; i < user_commands_found.GetSize(); ++i) + m_interpreter.OutputFormattedHelpText (result.GetOutputStream(), + user_commands_found.GetStringAtIndex(i), + "--", + user_commands_help.GetStringAtIndex(i), + max_len); + } + + } + + + std::vector properties; + const size_t num_properties = m_interpreter.GetDebugger().Apropos(search_word, properties); + if (num_properties) + { + const bool dump_qualified_name = true; + result.AppendMessageWithFormat ("\nThe following settings variables may relate to '%s': \n\n", search_word); + for (size_t i=0; iDumpDescription (m_interpreter, result.GetOutputStream(), 0, dump_qualified_name); + + } + + result.SetStatus (eReturnStatusSuccessFinishNoResult); + } + else + { + result.AppendError ("'' is not a valid search word.\n"); + result.SetStatus (eReturnStatusFailed); + } + } + else + { + result.AppendError ("'apropos' must be called with exactly one argument.\n"); + result.SetStatus (eReturnStatusFailed); + } + + return result.Succeeded(); +} diff --git a/contrib/llvm/tools/lldb/source/Commands/CommandObjectApropos.h b/contrib/llvm/tools/lldb/source/Commands/CommandObjectApropos.h new file mode 100644 index 00000000000..f5154177bb2 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Commands/CommandObjectApropos.h @@ -0,0 +1,44 @@ +//===-- CommandObjectApropos.h -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_CommandObjectApropos_h_ +#define liblldb_CommandObjectApropos_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Interpreter/CommandObject.h" + +namespace lldb_private { + +//------------------------------------------------------------------------- +// CommandObjectApropos +//------------------------------------------------------------------------- + +class CommandObjectApropos : public CommandObjectParsed +{ +public: + + CommandObjectApropos (CommandInterpreter &interpreter); + + virtual + ~CommandObjectApropos (); + +protected: + virtual bool + DoExecute (Args& command, + CommandReturnObject &result); + + +}; + +} // namespace lldb_private + +#endif // liblldb_CommandObjectApropos_h_ diff --git a/contrib/llvm/tools/lldb/source/Commands/CommandObjectArgs.cpp b/contrib/llvm/tools/lldb/source/Commands/CommandObjectArgs.cpp new file mode 100644 index 00000000000..05fd53bbe89 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Commands/CommandObjectArgs.cpp @@ -0,0 +1,272 @@ +//===-- CommandObjectArgs.cpp -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "CommandObjectArgs.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Interpreter/Args.h" +#include "lldb/Core/Debugger.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/Value.h" +#include "lldb/Expression/ClangExpression.h" +#include "lldb/Expression/ClangExpressionVariable.h" +#include "lldb/Expression/ClangFunction.h" +#include "lldb/Host/Host.h" +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Interpreter/CommandReturnObject.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Symbol/Variable.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" +#include "lldb/Target/StackFrame.h" + +using namespace lldb; +using namespace lldb_private; + +// This command is a toy. I'm just using it to have a way to construct the arguments to +// calling functions. +// + +CommandObjectArgs::CommandOptions::CommandOptions (CommandInterpreter &interpreter) : + Options(interpreter) +{ + // Keep only one place to reset the values to their defaults + OptionParsingStarting(); +} + + +CommandObjectArgs::CommandOptions::~CommandOptions () +{ +} + +Error +CommandObjectArgs::CommandOptions::SetOptionValue (uint32_t option_idx, const char *option_arg) +{ + Error error; + + const int short_option = m_getopt_table[option_idx].val; + + switch (short_option) + { + default: + error.SetErrorStringWithFormat("invalid short option character '%c'", short_option); + break; + } + + return error; +} + +void +CommandObjectArgs::CommandOptions::OptionParsingStarting () +{ +} + +const OptionDefinition* +CommandObjectArgs::CommandOptions::GetDefinitions () +{ + return g_option_table; +} + +CommandObjectArgs::CommandObjectArgs (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "args", + "When stopped at the start of a function, reads function arguments of type (u?)int(8|16|32|64)_t, (void|char)*", + "args"), + m_options (interpreter) +{ +} + +CommandObjectArgs::~CommandObjectArgs () +{ +} + +Options * +CommandObjectArgs::GetOptions () +{ + return &m_options; +} + +bool +CommandObjectArgs::DoExecute (Args& args, CommandReturnObject &result) +{ + ConstString target_triple; + + + Process *process = m_exe_ctx.GetProcessPtr(); + if (!process) + { + result.AppendError ("Args found no process."); + result.SetStatus (eReturnStatusFailed); + return false; + } + + const ABI *abi = process->GetABI().get(); + if (!abi) + { + result.AppendError ("The current process has no ABI."); + result.SetStatus (eReturnStatusFailed); + return false; + } + + const size_t num_args = args.GetArgumentCount (); + size_t arg_index; + + if (!num_args) + { + result.AppendError ("args requires at least one argument"); + result.SetStatus (eReturnStatusFailed); + return false; + } + + Thread *thread = m_exe_ctx.GetThreadPtr(); + + if (!thread) + { + result.AppendError ("args found no thread."); + result.SetStatus (eReturnStatusFailed); + return false; + } + + lldb::StackFrameSP thread_cur_frame = thread->GetSelectedFrame (); + if (!thread_cur_frame) + { + result.AppendError ("The current thread has no current frame."); + result.SetStatus (eReturnStatusFailed); + return false; + } + + ModuleSP thread_module_sp (thread_cur_frame->GetFrameCodeAddress ().GetModule()); + if (!thread_module_sp) + { + result.AppendError ("The PC has no associated module."); + result.SetStatus (eReturnStatusFailed); + return false; + } + + ClangASTContext &ast_context = thread_module_sp->GetClangASTContext(); + + ValueList value_list; + + for (arg_index = 0; arg_index < num_args; ++arg_index) + { + const char *arg_type_cstr = args.GetArgumentAtIndex(arg_index); + Value value; + value.SetValueType(Value::eValueTypeScalar); + ClangASTType clang_type; + + char *int_pos; + if ((int_pos = strstr (const_cast(arg_type_cstr), "int"))) + { + Encoding encoding = eEncodingSint; + + int width = 0; + + if (int_pos > arg_type_cstr + 1) + { + result.AppendErrorWithFormat ("Invalid format: %s.\n", arg_type_cstr); + result.SetStatus (eReturnStatusFailed); + return false; + } + if (int_pos == arg_type_cstr + 1 && arg_type_cstr[0] != 'u') + { + result.AppendErrorWithFormat ("Invalid format: %s.\n", arg_type_cstr); + result.SetStatus (eReturnStatusFailed); + return false; + } + if (arg_type_cstr[0] == 'u') + { + encoding = eEncodingUint; + } + + char *width_pos = int_pos + 3; + + if (!strcmp (width_pos, "8_t")) + width = 8; + else if (!strcmp (width_pos, "16_t")) + width = 16; + else if (!strcmp (width_pos, "32_t")) + width = 32; + else if (!strcmp (width_pos, "64_t")) + width = 64; + else + { + result.AppendErrorWithFormat ("Invalid format: %s.\n", arg_type_cstr); + result.SetStatus (eReturnStatusFailed); + return false; + } + + clang_type = ast_context.GetBuiltinTypeForEncodingAndBitSize(encoding, width); + + if (!clang_type.IsValid()) + { + result.AppendErrorWithFormat ("Couldn't get Clang type for format %s (%s integer, width %d).\n", + arg_type_cstr, + (encoding == eEncodingSint ? "signed" : "unsigned"), + width); + + result.SetStatus (eReturnStatusFailed); + return false; + } + } + else if (strchr (arg_type_cstr, '*')) + { + if (!strcmp (arg_type_cstr, "void*")) + clang_type = ast_context.GetBasicType(eBasicTypeVoid).GetPointerType(); + else if (!strcmp (arg_type_cstr, "char*")) + clang_type = ast_context.GetCStringType (false); + else + { + result.AppendErrorWithFormat ("Invalid format: %s.\n", arg_type_cstr); + result.SetStatus (eReturnStatusFailed); + return false; + } + } + else + { + result.AppendErrorWithFormat ("Invalid format: %s.\n", arg_type_cstr); + result.SetStatus (eReturnStatusFailed); + return false; + } + + value.SetClangType (clang_type); + value_list.PushValue(value); + } + + if (!abi->GetArgumentValues (*thread, value_list)) + { + result.AppendError ("Couldn't get argument values"); + result.SetStatus (eReturnStatusFailed); + return false; + } + + result.GetOutputStream ().Printf("Arguments : \n"); + + for (arg_index = 0; arg_index < num_args; ++arg_index) + { + result.GetOutputStream ().Printf ("%zu (%s): ", arg_index, args.GetArgumentAtIndex (arg_index)); + value_list.GetValueAtIndex (arg_index)->Dump (&result.GetOutputStream ()); + result.GetOutputStream ().Printf("\n"); + } + + return result.Succeeded(); +} + +OptionDefinition +CommandObjectArgs::CommandOptions::g_option_table[] = +{ + { LLDB_OPT_SET_1, false, "debug", 'g', no_argument, NULL, 0, eArgTypeNone, "Enable verbose debug logging of the expression parsing and evaluation."}, + { 0, false, NULL, 0, 0, NULL, 0, eArgTypeNone, NULL } +}; + diff --git a/contrib/llvm/tools/lldb/source/Commands/CommandObjectArgs.h b/contrib/llvm/tools/lldb/source/Commands/CommandObjectArgs.h new file mode 100644 index 00000000000..6691283ce09 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Commands/CommandObjectArgs.h @@ -0,0 +1,72 @@ +//===-- CommandObjectArgs.h -------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_CommandObjectArgs_h_ +#define liblldb_CommandObjectArgs_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Interpreter/CommandObject.h" +#include "lldb/Interpreter/Options.h" +#include "lldb/Core/Language.h" + +namespace lldb_private { + + class CommandObjectArgs : public CommandObjectParsed + { + public: + + class CommandOptions : public Options + { + public: + + CommandOptions (CommandInterpreter &interpreter); + + virtual + ~CommandOptions (); + + virtual Error + SetOptionValue (uint32_t option_idx, const char *option_arg); + + void + OptionParsingStarting (); + + const OptionDefinition* + GetDefinitions (); + + // Options table: Required for subclasses of Options. + + static OptionDefinition g_option_table[]; + }; + + CommandObjectArgs (CommandInterpreter &interpreter); + + virtual + ~CommandObjectArgs (); + + virtual + Options * + GetOptions (); + + + protected: + + CommandOptions m_options; + + virtual bool + DoExecute ( Args& command, + CommandReturnObject &result); + + }; + +} // namespace lldb_private + +#endif // liblldb_CommandObjectArgs_h_ diff --git a/contrib/llvm/tools/lldb/source/Commands/CommandObjectBreakpoint.cpp b/contrib/llvm/tools/lldb/source/Commands/CommandObjectBreakpoint.cpp new file mode 100644 index 00000000000..cb70c99a195 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Commands/CommandObjectBreakpoint.cpp @@ -0,0 +1,1843 @@ +//===-- CommandObjectBreakpoint.cpp -----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "CommandObjectBreakpoint.h" +#include "CommandObjectBreakpointCommand.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Breakpoint/Breakpoint.h" +#include "lldb/Breakpoint/BreakpointIDList.h" +#include "lldb/Breakpoint/BreakpointLocation.h" +#include "lldb/Interpreter/Options.h" +#include "lldb/Core/RegularExpression.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Interpreter/CommandReturnObject.h" +#include "lldb/Target/Target.h" +#include "lldb/Interpreter/CommandCompletions.h" +#include "lldb/Target/StackFrame.h" +#include "lldb/Target/Thread.h" +#include "lldb/Target/ThreadSpec.h" + +#include + +using namespace lldb; +using namespace lldb_private; + +static void +AddBreakpointDescription (Stream *s, Breakpoint *bp, lldb::DescriptionLevel level) +{ + s->IndentMore(); + bp->GetDescription (s, level, true); + s->IndentLess(); + s->EOL(); +} + +//------------------------------------------------------------------------- +// CommandObjectBreakpointSet +//------------------------------------------------------------------------- + + +class CommandObjectBreakpointSet : public CommandObjectParsed +{ +public: + + typedef enum BreakpointSetType + { + eSetTypeInvalid, + eSetTypeFileAndLine, + eSetTypeAddress, + eSetTypeFunctionName, + eSetTypeFunctionRegexp, + eSetTypeSourceRegexp, + eSetTypeException + } BreakpointSetType; + + CommandObjectBreakpointSet (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "breakpoint set", + "Sets a breakpoint or set of breakpoints in the executable.", + "breakpoint set "), + m_options (interpreter) + { + } + + + virtual + ~CommandObjectBreakpointSet () {} + + virtual Options * + GetOptions () + { + return &m_options; + } + + class CommandOptions : public Options + { + public: + + CommandOptions (CommandInterpreter &interpreter) : + Options (interpreter), + m_condition (), + m_filenames (), + m_line_num (0), + m_column (0), + m_func_names (), + m_func_name_type_mask (eFunctionNameTypeNone), + m_func_regexp (), + m_source_text_regexp(), + m_modules (), + m_load_addr(), + m_ignore_count (0), + m_thread_id(LLDB_INVALID_THREAD_ID), + m_thread_index (UINT32_MAX), + m_thread_name(), + m_queue_name(), + m_catch_bp (false), + m_throw_bp (true), + m_language (eLanguageTypeUnknown), + m_skip_prologue (eLazyBoolCalculate), + m_one_shot (false) + { + } + + + virtual + ~CommandOptions () {} + + virtual Error + SetOptionValue (uint32_t option_idx, const char *option_arg) + { + Error error; + const int short_option = m_getopt_table[option_idx].val; + + switch (short_option) + { + case 'a': + { + ExecutionContext exe_ctx (m_interpreter.GetExecutionContext()); + m_load_addr = Args::StringToAddress(&exe_ctx, option_arg, LLDB_INVALID_ADDRESS, &error); + } + break; + + case 'b': + m_func_names.push_back (option_arg); + m_func_name_type_mask |= eFunctionNameTypeBase; + break; + + case 'C': + m_column = Args::StringToUInt32 (option_arg, 0); + break; + + case 'c': + m_condition.assign(option_arg); + break; + + case 'E': + { + LanguageType language = LanguageRuntime::GetLanguageTypeFromString (option_arg); + + switch (language) + { + case eLanguageTypeC89: + case eLanguageTypeC: + case eLanguageTypeC99: + m_language = eLanguageTypeC; + break; + case eLanguageTypeC_plus_plus: + m_language = eLanguageTypeC_plus_plus; + break; + case eLanguageTypeObjC: + m_language = eLanguageTypeObjC; + break; + case eLanguageTypeObjC_plus_plus: + error.SetErrorStringWithFormat ("Set exception breakpoints separately for c++ and objective-c"); + break; + case eLanguageTypeUnknown: + error.SetErrorStringWithFormat ("Unknown language type: '%s' for exception breakpoint", option_arg); + break; + default: + error.SetErrorStringWithFormat ("Unsupported language type: '%s' for exception breakpoint", option_arg); + } + } + break; + + case 'f': + m_filenames.AppendIfUnique (FileSpec(option_arg, false)); + break; + + case 'F': + m_func_names.push_back (option_arg); + m_func_name_type_mask |= eFunctionNameTypeFull; + break; + + case 'h': + { + bool success; + m_catch_bp = Args::StringToBoolean (option_arg, true, &success); + if (!success) + error.SetErrorStringWithFormat ("Invalid boolean value for on-catch option: '%s'", option_arg); + } + break; + case 'i': + { + m_ignore_count = Args::StringToUInt32(option_arg, UINT32_MAX, 0); + if (m_ignore_count == UINT32_MAX) + error.SetErrorStringWithFormat ("invalid ignore count '%s'", option_arg); + break; + } + + case 'K': + { + bool success; + bool value; + value = Args::StringToBoolean (option_arg, true, &success); + if (value) + m_skip_prologue = eLazyBoolYes; + else + m_skip_prologue = eLazyBoolNo; + + if (!success) + error.SetErrorStringWithFormat ("Invalid boolean value for skip prologue option: '%s'", option_arg); + } + break; + + case 'l': + m_line_num = Args::StringToUInt32 (option_arg, 0); + break; + + case 'M': + m_func_names.push_back (option_arg); + m_func_name_type_mask |= eFunctionNameTypeMethod; + break; + + case 'n': + m_func_names.push_back (option_arg); + m_func_name_type_mask |= eFunctionNameTypeAuto; + break; + + case 'o': + m_one_shot = true; + break; + + case 'p': + m_source_text_regexp.assign (option_arg); + break; + + case 'q': + m_queue_name.assign (option_arg); + break; + + case 'r': + m_func_regexp.assign (option_arg); + break; + + case 's': + { + m_modules.AppendIfUnique (FileSpec (option_arg, false)); + break; + } + + case 'S': + m_func_names.push_back (option_arg); + m_func_name_type_mask |= eFunctionNameTypeSelector; + break; + + case 't' : + { + m_thread_id = Args::StringToUInt64(option_arg, LLDB_INVALID_THREAD_ID, 0); + if (m_thread_id == LLDB_INVALID_THREAD_ID) + error.SetErrorStringWithFormat ("invalid thread id string '%s'", option_arg); + } + break; + + case 'T': + m_thread_name.assign (option_arg); + break; + + case 'w': + { + bool success; + m_throw_bp = Args::StringToBoolean (option_arg, true, &success); + if (!success) + error.SetErrorStringWithFormat ("Invalid boolean value for on-throw option: '%s'", option_arg); + } + break; + + case 'x': + { + m_thread_index = Args::StringToUInt32(option_arg, UINT32_MAX, 0); + if (m_thread_id == UINT32_MAX) + error.SetErrorStringWithFormat ("invalid thread index string '%s'", option_arg); + + } + break; + + default: + error.SetErrorStringWithFormat ("unrecognized option '%c'", short_option); + break; + } + + return error; + } + void + OptionParsingStarting () + { + m_condition.clear(); + m_filenames.Clear(); + m_line_num = 0; + m_column = 0; + m_func_names.clear(); + m_func_name_type_mask = eFunctionNameTypeNone; + m_func_regexp.clear(); + m_source_text_regexp.clear(); + m_modules.Clear(); + m_load_addr = LLDB_INVALID_ADDRESS; + m_ignore_count = 0; + m_thread_id = LLDB_INVALID_THREAD_ID; + m_thread_index = UINT32_MAX; + m_thread_name.clear(); + m_queue_name.clear(); + m_catch_bp = false; + m_throw_bp = true; + m_language = eLanguageTypeUnknown; + m_skip_prologue = eLazyBoolCalculate; + m_one_shot = false; + } + + const OptionDefinition* + GetDefinitions () + { + return g_option_table; + } + + // Options table: Required for subclasses of Options. + + static OptionDefinition g_option_table[]; + + // Instance variables to hold the values for command options. + + std::string m_condition; + FileSpecList m_filenames; + uint32_t m_line_num; + uint32_t m_column; + std::vector m_func_names; + uint32_t m_func_name_type_mask; + std::string m_func_regexp; + std::string m_source_text_regexp; + FileSpecList m_modules; + lldb::addr_t m_load_addr; + uint32_t m_ignore_count; + lldb::tid_t m_thread_id; + uint32_t m_thread_index; + std::string m_thread_name; + std::string m_queue_name; + bool m_catch_bp; + bool m_throw_bp; + lldb::LanguageType m_language; + LazyBool m_skip_prologue; + bool m_one_shot; + + }; + +protected: + virtual bool + DoExecute (Args& command, + CommandReturnObject &result) + { + Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get(); + if (target == NULL) + { + result.AppendError ("Invalid target. Must set target before setting breakpoints (see 'target create' command)."); + result.SetStatus (eReturnStatusFailed); + return false; + } + + // The following are the various types of breakpoints that could be set: + // 1). -f -l -p [-s -g] (setting breakpoint by source location) + // 2). -a [-s -g] (setting breakpoint by address) + // 3). -n [-s -g] (setting breakpoint by function name) + // 4). -r [-s -g] (setting breakpoint by function name regular expression) + // 5). -p -f (setting a breakpoint by comparing a reg-exp to source text) + // 6). -E [-w -h] (setting a breakpoint for exceptions for a given language.) + + BreakpointSetType break_type = eSetTypeInvalid; + + if (m_options.m_line_num != 0) + break_type = eSetTypeFileAndLine; + else if (m_options.m_load_addr != LLDB_INVALID_ADDRESS) + break_type = eSetTypeAddress; + else if (!m_options.m_func_names.empty()) + break_type = eSetTypeFunctionName; + else if (!m_options.m_func_regexp.empty()) + break_type = eSetTypeFunctionRegexp; + else if (!m_options.m_source_text_regexp.empty()) + break_type = eSetTypeSourceRegexp; + else if (m_options.m_language != eLanguageTypeUnknown) + break_type = eSetTypeException; + + Breakpoint *bp = NULL; + FileSpec module_spec; + const bool internal = false; + + switch (break_type) + { + case eSetTypeFileAndLine: // Breakpoint by source position + { + FileSpec file; + const size_t num_files = m_options.m_filenames.GetSize(); + if (num_files == 0) + { + if (!GetDefaultFile (target, file, result)) + { + result.AppendError("No file supplied and no default file available."); + result.SetStatus (eReturnStatusFailed); + return false; + } + } + else if (num_files > 1) + { + result.AppendError("Only one file at a time is allowed for file and line breakpoints."); + result.SetStatus (eReturnStatusFailed); + return false; + } + else + file = m_options.m_filenames.GetFileSpecAtIndex(0); + + // Only check for inline functions if + LazyBool check_inlines = eLazyBoolCalculate; + + bp = target->CreateBreakpoint (&(m_options.m_modules), + file, + m_options.m_line_num, + check_inlines, + m_options.m_skip_prologue, + internal).get(); + } + break; + + case eSetTypeAddress: // Breakpoint by address + bp = target->CreateBreakpoint (m_options.m_load_addr, false).get(); + break; + + case eSetTypeFunctionName: // Breakpoint by function name + { + uint32_t name_type_mask = m_options.m_func_name_type_mask; + + if (name_type_mask == 0) + name_type_mask = eFunctionNameTypeAuto; + + bp = target->CreateBreakpoint (&(m_options.m_modules), + &(m_options.m_filenames), + m_options.m_func_names, + name_type_mask, + m_options.m_skip_prologue, + internal).get(); + } + break; + + case eSetTypeFunctionRegexp: // Breakpoint by regular expression function name + { + RegularExpression regexp(m_options.m_func_regexp.c_str()); + if (!regexp.IsValid()) + { + char err_str[1024]; + regexp.GetErrorAsCString(err_str, sizeof(err_str)); + result.AppendErrorWithFormat("Function name regular expression could not be compiled: \"%s\"", + err_str); + result.SetStatus (eReturnStatusFailed); + return false; + } + + bp = target->CreateFuncRegexBreakpoint (&(m_options.m_modules), + &(m_options.m_filenames), + regexp, + m_options.m_skip_prologue, + internal).get(); + } + break; + case eSetTypeSourceRegexp: // Breakpoint by regexp on source text. + { + const size_t num_files = m_options.m_filenames.GetSize(); + + if (num_files == 0) + { + FileSpec file; + if (!GetDefaultFile (target, file, result)) + { + result.AppendError ("No files provided and could not find default file."); + result.SetStatus (eReturnStatusFailed); + return false; + } + else + { + m_options.m_filenames.Append (file); + } + } + + RegularExpression regexp(m_options.m_source_text_regexp.c_str()); + if (!regexp.IsValid()) + { + char err_str[1024]; + regexp.GetErrorAsCString(err_str, sizeof(err_str)); + result.AppendErrorWithFormat("Source text regular expression could not be compiled: \"%s\"", + err_str); + result.SetStatus (eReturnStatusFailed); + return false; + } + bp = target->CreateSourceRegexBreakpoint (&(m_options.m_modules), &(m_options.m_filenames), regexp).get(); + } + break; + case eSetTypeException: + { + bp = target->CreateExceptionBreakpoint (m_options.m_language, m_options.m_catch_bp, m_options.m_throw_bp).get(); + } + break; + default: + break; + } + + // Now set the various options that were passed in: + if (bp) + { + if (m_options.m_thread_id != LLDB_INVALID_THREAD_ID) + bp->SetThreadID (m_options.m_thread_id); + + if (m_options.m_thread_index != UINT32_MAX) + bp->GetOptions()->GetThreadSpec()->SetIndex(m_options.m_thread_index); + + if (!m_options.m_thread_name.empty()) + bp->GetOptions()->GetThreadSpec()->SetName(m_options.m_thread_name.c_str()); + + if (!m_options.m_queue_name.empty()) + bp->GetOptions()->GetThreadSpec()->SetQueueName(m_options.m_queue_name.c_str()); + + if (m_options.m_ignore_count != 0) + bp->GetOptions()->SetIgnoreCount(m_options.m_ignore_count); + + if (!m_options.m_condition.empty()) + bp->GetOptions()->SetCondition(m_options.m_condition.c_str()); + + bp->SetOneShot (m_options.m_one_shot); + } + + if (bp) + { + Stream &output_stream = result.GetOutputStream(); + const bool show_locations = false; + bp->GetDescription(&output_stream, lldb::eDescriptionLevelInitial, show_locations); + // Don't print out this warning for exception breakpoints. They can get set before the target + // is set, but we won't know how to actually set the breakpoint till we run. + if (bp->GetNumLocations() == 0 && break_type != eSetTypeException) + output_stream.Printf ("WARNING: Unable to resolve breakpoint to any actual locations.\n"); + result.SetStatus (eReturnStatusSuccessFinishResult); + } + else if (!bp) + { + result.AppendError ("Breakpoint creation failed: No breakpoint created."); + result.SetStatus (eReturnStatusFailed); + } + + return result.Succeeded(); + } + +private: + bool + GetDefaultFile (Target *target, FileSpec &file, CommandReturnObject &result) + { + uint32_t default_line; + // First use the Source Manager's default file. + // Then use the current stack frame's file. + if (!target->GetSourceManager().GetDefaultFileAndLine(file, default_line)) + { + StackFrame *cur_frame = m_exe_ctx.GetFramePtr(); + if (cur_frame == NULL) + { + result.AppendError ("No selected frame to use to find the default file."); + result.SetStatus (eReturnStatusFailed); + return false; + } + else if (!cur_frame->HasDebugInformation()) + { + result.AppendError ("Cannot use the selected frame to find the default file, it has no debug info."); + result.SetStatus (eReturnStatusFailed); + return false; + } + else + { + const SymbolContext &sc = cur_frame->GetSymbolContext (eSymbolContextLineEntry); + if (sc.line_entry.file) + { + file = sc.line_entry.file; + } + else + { + result.AppendError ("Can't find the file for the selected frame to use as the default file."); + result.SetStatus (eReturnStatusFailed); + return false; + } + } + } + return true; + } + + CommandOptions m_options; +}; +// If an additional option set beyond LLDB_OPTION_SET_10 is added, make sure to +// update the numbers passed to LLDB_OPT_SET_FROM_TO(...) appropriately. +#define LLDB_OPT_FILE ( LLDB_OPT_SET_FROM_TO(1, 9) & ~LLDB_OPT_SET_2 ) +#define LLDB_OPT_NOT_10 ( LLDB_OPT_SET_FROM_TO(1, 10) & ~LLDB_OPT_SET_10 ) +#define LLDB_OPT_SKIP_PROLOGUE ( LLDB_OPT_SET_1 | LLDB_OPT_SET_FROM_TO(3,8) ) + +OptionDefinition +CommandObjectBreakpointSet::CommandOptions::g_option_table[] = +{ + { LLDB_OPT_NOT_10, false, "shlib", 's', required_argument, NULL, CommandCompletions::eModuleCompletion, eArgTypeShlibName, + "Set the breakpoint only in this shared library. " + "Can repeat this option multiple times to specify multiple shared libraries."}, + + { LLDB_OPT_SET_ALL, false, "ignore-count", 'i', required_argument, NULL, 0, eArgTypeCount, + "Set the number of times this breakpoint is skipped before stopping." }, + + { LLDB_OPT_SET_ALL, false, "one-shot", 'o', no_argument, NULL, 0, eArgTypeNone, + "The breakpoint is deleted the first time it causes a stop." }, + + { LLDB_OPT_SET_ALL, false, "condition", 'c', required_argument, NULL, 0, eArgTypeExpression, + "The breakpoint stops only if this condition expression evaluates to true."}, + + { LLDB_OPT_SET_ALL, false, "thread-index", 'x', required_argument, NULL, 0, eArgTypeThreadIndex, + "The breakpoint stops only for the thread whose indeX matches this argument."}, + + { LLDB_OPT_SET_ALL, false, "thread-id", 't', required_argument, NULL, 0, eArgTypeThreadID, + "The breakpoint stops only for the thread whose TID matches this argument."}, + + { LLDB_OPT_SET_ALL, false, "thread-name", 'T', required_argument, NULL, 0, eArgTypeThreadName, + "The breakpoint stops only for the thread whose thread name matches this argument."}, + + { LLDB_OPT_SET_ALL, false, "queue-name", 'q', required_argument, NULL, 0, eArgTypeQueueName, + "The breakpoint stops only for threads in the queue whose name is given by this argument."}, + + { LLDB_OPT_FILE, false, "file", 'f', required_argument, NULL, CommandCompletions::eSourceFileCompletion, eArgTypeFilename, + "Specifies the source file in which to set this breakpoint. " + "Note, by default lldb only looks for files that are #included if they use the standard include file extensions. " + "To set breakpoints on .c/.cpp/.m/.mm files that are #included, set target.inline-breakpoint-strategy" + " to \"always\"."}, + + { LLDB_OPT_SET_1, true, "line", 'l', required_argument, NULL, 0, eArgTypeLineNum, + "Specifies the line number on which to set this breakpoint."}, + + // Comment out this option for the moment, as we don't actually use it, but will in the future. + // This way users won't see it, but the infrastructure is left in place. + // { 0, false, "column", 'C', required_argument, NULL, "", + // "Set the breakpoint by source location at this particular column."}, + + { LLDB_OPT_SET_2, true, "address", 'a', required_argument, NULL, 0, eArgTypeAddressOrExpression, + "Set the breakpoint by address, at the specified address."}, + + { LLDB_OPT_SET_3, true, "name", 'n', required_argument, NULL, CommandCompletions::eSymbolCompletion, eArgTypeFunctionName, + "Set the breakpoint by function name. Can be repeated multiple times to make one breakpoint for multiple names" }, + + { LLDB_OPT_SET_4, true, "fullname", 'F', required_argument, NULL, CommandCompletions::eSymbolCompletion, eArgTypeFullName, + "Set the breakpoint by fully qualified function names. For C++ this means namespaces and all arguments, and " + "for Objective C this means a full function prototype with class and selector. " + "Can be repeated multiple times to make one breakpoint for multiple names." }, + + { LLDB_OPT_SET_5, true, "selector", 'S', required_argument, NULL, 0, eArgTypeSelector, + "Set the breakpoint by ObjC selector name. Can be repeated multiple times to make one breakpoint for multiple Selectors." }, + + { LLDB_OPT_SET_6, true, "method", 'M', required_argument, NULL, 0, eArgTypeMethod, + "Set the breakpoint by C++ method names. Can be repeated multiple times to make one breakpoint for multiple methods." }, + + { LLDB_OPT_SET_7, true, "func-regex", 'r', required_argument, NULL, 0, eArgTypeRegularExpression, + "Set the breakpoint by function name, evaluating a regular-expression to find the function name(s)." }, + + { LLDB_OPT_SET_8, true, "basename", 'b', required_argument, NULL, CommandCompletions::eSymbolCompletion, eArgTypeFunctionName, + "Set the breakpoint by function basename (C++ namespaces and arguments will be ignored). " + "Can be repeated multiple times to make one breakpoint for multiple symbols." }, + + { LLDB_OPT_SET_9, true, "source-pattern-regexp", 'p', required_argument, NULL, 0, eArgTypeRegularExpression, + "Set the breakpoint by specifying a regular expression which is matched against the source text in a source file or files " + "specified with the -f option. The -f option can be specified more than once. " + "If no source files are specified, uses the current \"default source file\"" }, + + { LLDB_OPT_SET_10, true, "language-exception", 'E', required_argument, NULL, 0, eArgTypeLanguage, + "Set the breakpoint on exceptions thrown by the specified language (without options, on throw but not catch.)" }, + + { LLDB_OPT_SET_10, false, "on-throw", 'w', required_argument, NULL, 0, eArgTypeBoolean, + "Set the breakpoint on exception throW." }, + + { LLDB_OPT_SET_10, false, "on-catch", 'h', required_argument, NULL, 0, eArgTypeBoolean, + "Set the breakpoint on exception catcH." }, + + { LLDB_OPT_SKIP_PROLOGUE, false, "skip-prologue", 'K', required_argument, NULL, 0, eArgTypeBoolean, + "sKip the prologue if the breakpoint is at the beginning of a function. If not set the target.skip-prologue setting is used." }, + + { 0, false, NULL, 0, 0, NULL, 0, eArgTypeNone, NULL } +}; + +//------------------------------------------------------------------------- +// CommandObjectBreakpointModify +//------------------------------------------------------------------------- +#pragma mark Modify + +class CommandObjectBreakpointModify : public CommandObjectParsed +{ +public: + + CommandObjectBreakpointModify (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "breakpoint modify", + "Modify the options on a breakpoint or set of breakpoints in the executable. " + "If no breakpoint is specified, acts on the last created breakpoint. " + "With the exception of -e, -d and -i, passing an empty argument clears the modification.", + NULL), + m_options (interpreter) + { + CommandArgumentEntry arg; + CommandObject::AddIDsArgumentData(arg, eArgTypeBreakpointID, eArgTypeBreakpointIDRange); + // Add the entry for the first argument for this command to the object's arguments vector. + m_arguments.push_back (arg); + } + + + virtual + ~CommandObjectBreakpointModify () {} + + virtual Options * + GetOptions () + { + return &m_options; + } + + class CommandOptions : public Options + { + public: + + CommandOptions (CommandInterpreter &interpreter) : + Options (interpreter), + m_ignore_count (0), + m_thread_id(LLDB_INVALID_THREAD_ID), + m_thread_id_passed(false), + m_thread_index (UINT32_MAX), + m_thread_index_passed(false), + m_thread_name(), + m_queue_name(), + m_condition (), + m_one_shot (false), + m_enable_passed (false), + m_enable_value (false), + m_name_passed (false), + m_queue_passed (false), + m_condition_passed (false), + m_one_shot_passed (false) + { + } + + virtual + ~CommandOptions () {} + + virtual Error + SetOptionValue (uint32_t option_idx, const char *option_arg) + { + Error error; + const int short_option = m_getopt_table[option_idx].val; + + switch (short_option) + { + case 'c': + if (option_arg != NULL) + m_condition.assign (option_arg); + else + m_condition.clear(); + m_condition_passed = true; + break; + case 'd': + m_enable_passed = true; + m_enable_value = false; + break; + case 'e': + m_enable_passed = true; + m_enable_value = true; + break; + case 'i': + { + m_ignore_count = Args::StringToUInt32(option_arg, UINT32_MAX, 0); + if (m_ignore_count == UINT32_MAX) + error.SetErrorStringWithFormat ("invalid ignore count '%s'", option_arg); + } + break; + case 'o': + { + bool value, success; + value = Args::StringToBoolean(option_arg, false, &success); + if (success) + { + m_one_shot_passed = true; + m_one_shot = value; + } + else + error.SetErrorStringWithFormat("invalid boolean value '%s' passed for -o option", option_arg); + } + break; + case 't' : + { + if (option_arg[0] == '\0') + { + m_thread_id = LLDB_INVALID_THREAD_ID; + m_thread_id_passed = true; + } + else + { + m_thread_id = Args::StringToUInt64(option_arg, LLDB_INVALID_THREAD_ID, 0); + if (m_thread_id == LLDB_INVALID_THREAD_ID) + error.SetErrorStringWithFormat ("invalid thread id string '%s'", option_arg); + else + m_thread_id_passed = true; + } + } + break; + case 'T': + if (option_arg != NULL) + m_thread_name.assign (option_arg); + else + m_thread_name.clear(); + m_name_passed = true; + break; + case 'q': + if (option_arg != NULL) + m_queue_name.assign (option_arg); + else + m_queue_name.clear(); + m_queue_passed = true; + break; + case 'x': + { + if (option_arg[0] == '\n') + { + m_thread_index = UINT32_MAX; + m_thread_index_passed = true; + } + else + { + m_thread_index = Args::StringToUInt32 (option_arg, UINT32_MAX, 0); + if (m_thread_id == UINT32_MAX) + error.SetErrorStringWithFormat ("invalid thread index string '%s'", option_arg); + else + m_thread_index_passed = true; + } + } + break; + default: + error.SetErrorStringWithFormat ("unrecognized option '%c'", short_option); + break; + } + + return error; + } + void + OptionParsingStarting () + { + m_ignore_count = 0; + m_thread_id = LLDB_INVALID_THREAD_ID; + m_thread_id_passed = false; + m_thread_index = UINT32_MAX; + m_thread_index_passed = false; + m_thread_name.clear(); + m_queue_name.clear(); + m_condition.clear(); + m_one_shot = false; + m_enable_passed = false; + m_queue_passed = false; + m_name_passed = false; + m_condition_passed = false; + m_one_shot_passed = false; + } + + const OptionDefinition* + GetDefinitions () + { + return g_option_table; + } + + + // Options table: Required for subclasses of Options. + + static OptionDefinition g_option_table[]; + + // Instance variables to hold the values for command options. + + uint32_t m_ignore_count; + lldb::tid_t m_thread_id; + bool m_thread_id_passed; + uint32_t m_thread_index; + bool m_thread_index_passed; + std::string m_thread_name; + std::string m_queue_name; + std::string m_condition; + bool m_one_shot; + bool m_enable_passed; + bool m_enable_value; + bool m_name_passed; + bool m_queue_passed; + bool m_condition_passed; + bool m_one_shot_passed; + + }; + +protected: + virtual bool + DoExecute (Args& command, CommandReturnObject &result) + { + Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get(); + if (target == NULL) + { + result.AppendError ("Invalid target. No existing target or breakpoints."); + result.SetStatus (eReturnStatusFailed); + return false; + } + + Mutex::Locker locker; + target->GetBreakpointList().GetListMutex(locker); + + BreakpointIDList valid_bp_ids; + + CommandObjectMultiwordBreakpoint::VerifyBreakpointIDs (command, target, result, &valid_bp_ids); + + if (result.Succeeded()) + { + const size_t count = valid_bp_ids.GetSize(); + for (size_t i = 0; i < count; ++i) + { + BreakpointID cur_bp_id = valid_bp_ids.GetBreakpointIDAtIndex (i); + + if (cur_bp_id.GetBreakpointID() != LLDB_INVALID_BREAK_ID) + { + Breakpoint *bp = target->GetBreakpointByID (cur_bp_id.GetBreakpointID()).get(); + if (cur_bp_id.GetLocationID() != LLDB_INVALID_BREAK_ID) + { + BreakpointLocation *location = bp->FindLocationByID (cur_bp_id.GetLocationID()).get(); + if (location) + { + if (m_options.m_thread_id_passed) + location->SetThreadID (m_options.m_thread_id); + + if (m_options.m_thread_index_passed) + location->SetThreadIndex(m_options.m_thread_index); + + if (m_options.m_name_passed) + location->SetThreadName(m_options.m_thread_name.c_str()); + + if (m_options.m_queue_passed) + location->SetQueueName(m_options.m_queue_name.c_str()); + + if (m_options.m_ignore_count != 0) + location->SetIgnoreCount(m_options.m_ignore_count); + + if (m_options.m_enable_passed) + location->SetEnabled (m_options.m_enable_value); + + if (m_options.m_condition_passed) + location->SetCondition (m_options.m_condition.c_str()); + } + } + else + { + if (m_options.m_thread_id_passed) + bp->SetThreadID (m_options.m_thread_id); + + if (m_options.m_thread_index_passed) + bp->SetThreadIndex(m_options.m_thread_index); + + if (m_options.m_name_passed) + bp->SetThreadName(m_options.m_thread_name.c_str()); + + if (m_options.m_queue_passed) + bp->SetQueueName(m_options.m_queue_name.c_str()); + + if (m_options.m_ignore_count != 0) + bp->SetIgnoreCount(m_options.m_ignore_count); + + if (m_options.m_enable_passed) + bp->SetEnabled (m_options.m_enable_value); + + if (m_options.m_condition_passed) + bp->SetCondition (m_options.m_condition.c_str()); + } + } + } + } + + return result.Succeeded(); + } + +private: + CommandOptions m_options; +}; + +#pragma mark Modify::CommandOptions +OptionDefinition +CommandObjectBreakpointModify::CommandOptions::g_option_table[] = +{ +{ LLDB_OPT_SET_ALL, false, "ignore-count", 'i', required_argument, NULL, 0, eArgTypeCount, "Set the number of times this breakpoint is skipped before stopping." }, +{ LLDB_OPT_SET_ALL, false, "one-shot", 'o', required_argument, NULL, 0, eArgTypeBoolean, "The breakpoint is deleted the first time it stop causes a stop." }, +{ LLDB_OPT_SET_ALL, false, "thread-index", 'x', required_argument, NULL, 0, eArgTypeThreadIndex, "The breakpoint stops only for the thread whose index matches this argument."}, +{ LLDB_OPT_SET_ALL, false, "thread-id", 't', required_argument, NULL, 0, eArgTypeThreadID, "The breakpoint stops only for the thread whose TID matches this argument."}, +{ LLDB_OPT_SET_ALL, false, "thread-name", 'T', required_argument, NULL, 0, eArgTypeThreadName, "The breakpoint stops only for the thread whose thread name matches this argument."}, +{ LLDB_OPT_SET_ALL, false, "queue-name", 'q', required_argument, NULL, 0, eArgTypeQueueName, "The breakpoint stops only for threads in the queue whose name is given by this argument."}, +{ LLDB_OPT_SET_ALL, false, "condition", 'c', required_argument, NULL, 0, eArgTypeExpression, "The breakpoint stops only if this condition expression evaluates to true."}, +{ LLDB_OPT_SET_1, false, "enable", 'e', no_argument, NULL, 0, eArgTypeNone, "Enable the breakpoint."}, +{ LLDB_OPT_SET_2, false, "disable", 'd', no_argument, NULL, 0, eArgTypeNone, "Disable the breakpoint."}, +{ 0, false, NULL, 0 , 0, NULL, 0, eArgTypeNone, NULL } +}; + +//------------------------------------------------------------------------- +// CommandObjectBreakpointEnable +//------------------------------------------------------------------------- +#pragma mark Enable + +class CommandObjectBreakpointEnable : public CommandObjectParsed +{ +public: + CommandObjectBreakpointEnable (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "enable", + "Enable the specified disabled breakpoint(s). If no breakpoints are specified, enable all of them.", + NULL) + { + CommandArgumentEntry arg; + CommandObject::AddIDsArgumentData(arg, eArgTypeBreakpointID, eArgTypeBreakpointIDRange); + // Add the entry for the first argument for this command to the object's arguments vector. + m_arguments.push_back (arg); + } + + + virtual + ~CommandObjectBreakpointEnable () {} + +protected: + virtual bool + DoExecute (Args& command, CommandReturnObject &result) + { + Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get(); + if (target == NULL) + { + result.AppendError ("Invalid target. No existing target or breakpoints."); + result.SetStatus (eReturnStatusFailed); + return false; + } + + Mutex::Locker locker; + target->GetBreakpointList().GetListMutex(locker); + + const BreakpointList &breakpoints = target->GetBreakpointList(); + + size_t num_breakpoints = breakpoints.GetSize(); + + if (num_breakpoints == 0) + { + result.AppendError ("No breakpoints exist to be enabled."); + result.SetStatus (eReturnStatusFailed); + return false; + } + + if (command.GetArgumentCount() == 0) + { + // No breakpoint selected; enable all currently set breakpoints. + target->EnableAllBreakpoints (); + result.AppendMessageWithFormat ("All breakpoints enabled. (%lu breakpoints)\n", num_breakpoints); + result.SetStatus (eReturnStatusSuccessFinishNoResult); + } + else + { + // Particular breakpoint selected; enable that breakpoint. + BreakpointIDList valid_bp_ids; + CommandObjectMultiwordBreakpoint::VerifyBreakpointIDs (command, target, result, &valid_bp_ids); + + if (result.Succeeded()) + { + int enable_count = 0; + int loc_count = 0; + const size_t count = valid_bp_ids.GetSize(); + for (size_t i = 0; i < count; ++i) + { + BreakpointID cur_bp_id = valid_bp_ids.GetBreakpointIDAtIndex (i); + + if (cur_bp_id.GetBreakpointID() != LLDB_INVALID_BREAK_ID) + { + Breakpoint *breakpoint = target->GetBreakpointByID (cur_bp_id.GetBreakpointID()).get(); + if (cur_bp_id.GetLocationID() != LLDB_INVALID_BREAK_ID) + { + BreakpointLocation *location = breakpoint->FindLocationByID (cur_bp_id.GetLocationID()).get(); + if (location) + { + location->SetEnabled (true); + ++loc_count; + } + } + else + { + breakpoint->SetEnabled (true); + ++enable_count; + } + } + } + result.AppendMessageWithFormat ("%d breakpoints enabled.\n", enable_count + loc_count); + result.SetStatus (eReturnStatusSuccessFinishNoResult); + } + } + + return result.Succeeded(); + } +}; + +//------------------------------------------------------------------------- +// CommandObjectBreakpointDisable +//------------------------------------------------------------------------- +#pragma mark Disable + +class CommandObjectBreakpointDisable : public CommandObjectParsed +{ +public: + CommandObjectBreakpointDisable (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "breakpoint disable", + "Disable the specified breakpoint(s) without removing it/them. If no breakpoints are specified, disable them all.", + NULL) + { + SetHelpLong( +"Disable the specified breakpoint(s) without removing it/them. \n\ +If no breakpoints are specified, disable them all.\n\ +\n\ +Note: disabling a breakpoint will cause none of its locations to be hit\n\ +regardless of whether they are enabled or disabled. So the sequence: \n\ +\n\ + (lldb) break disable 1\n\ + (lldb) break enable 1.1\n\ +\n\ +will NOT cause location 1.1 to get hit. To achieve that, do:\n\ +\n\ + (lldb) break disable 1.*\n\ + (lldb) break enable 1.1\n\ +\n\ +The first command disables all the locations of breakpoint 1, \n\ +the second re-enables the first location." + ); + + CommandArgumentEntry arg; + CommandObject::AddIDsArgumentData(arg, eArgTypeBreakpointID, eArgTypeBreakpointIDRange); + // Add the entry for the first argument for this command to the object's arguments vector. + m_arguments.push_back (arg); + + } + + + virtual + ~CommandObjectBreakpointDisable () {} + +protected: + virtual bool + DoExecute (Args& command, CommandReturnObject &result) + { + Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get(); + if (target == NULL) + { + result.AppendError ("Invalid target. No existing target or breakpoints."); + result.SetStatus (eReturnStatusFailed); + return false; + } + + Mutex::Locker locker; + target->GetBreakpointList().GetListMutex(locker); + + const BreakpointList &breakpoints = target->GetBreakpointList(); + size_t num_breakpoints = breakpoints.GetSize(); + + if (num_breakpoints == 0) + { + result.AppendError ("No breakpoints exist to be disabled."); + result.SetStatus (eReturnStatusFailed); + return false; + } + + if (command.GetArgumentCount() == 0) + { + // No breakpoint selected; disable all currently set breakpoints. + target->DisableAllBreakpoints (); + result.AppendMessageWithFormat ("All breakpoints disabled. (%lu breakpoints)\n", num_breakpoints); + result.SetStatus (eReturnStatusSuccessFinishNoResult); + } + else + { + // Particular breakpoint selected; disable that breakpoint. + BreakpointIDList valid_bp_ids; + + CommandObjectMultiwordBreakpoint::VerifyBreakpointIDs (command, target, result, &valid_bp_ids); + + if (result.Succeeded()) + { + int disable_count = 0; + int loc_count = 0; + const size_t count = valid_bp_ids.GetSize(); + for (size_t i = 0; i < count; ++i) + { + BreakpointID cur_bp_id = valid_bp_ids.GetBreakpointIDAtIndex (i); + + if (cur_bp_id.GetBreakpointID() != LLDB_INVALID_BREAK_ID) + { + Breakpoint *breakpoint = target->GetBreakpointByID (cur_bp_id.GetBreakpointID()).get(); + if (cur_bp_id.GetLocationID() != LLDB_INVALID_BREAK_ID) + { + BreakpointLocation *location = breakpoint->FindLocationByID (cur_bp_id.GetLocationID()).get(); + if (location) + { + location->SetEnabled (false); + ++loc_count; + } + } + else + { + breakpoint->SetEnabled (false); + ++disable_count; + } + } + } + result.AppendMessageWithFormat ("%d breakpoints disabled.\n", disable_count + loc_count); + result.SetStatus (eReturnStatusSuccessFinishNoResult); + } + } + + return result.Succeeded(); + } + +}; + +//------------------------------------------------------------------------- +// CommandObjectBreakpointList +//------------------------------------------------------------------------- +#pragma mark List + +class CommandObjectBreakpointList : public CommandObjectParsed +{ +public: + CommandObjectBreakpointList (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "breakpoint list", + "List some or all breakpoints at configurable levels of detail.", + NULL), + m_options (interpreter) + { + CommandArgumentEntry arg; + CommandArgumentData bp_id_arg; + + // Define the first (and only) variant of this arg. + bp_id_arg.arg_type = eArgTypeBreakpointID; + bp_id_arg.arg_repetition = eArgRepeatOptional; + + // There is only one variant this argument could be; put it into the argument entry. + arg.push_back (bp_id_arg); + + // Push the data for the first argument into the m_arguments vector. + m_arguments.push_back (arg); + } + + + virtual + ~CommandObjectBreakpointList () {} + + virtual Options * + GetOptions () + { + return &m_options; + } + + class CommandOptions : public Options + { + public: + + CommandOptions (CommandInterpreter &interpreter) : + Options (interpreter), + m_level (lldb::eDescriptionLevelBrief) // Breakpoint List defaults to brief descriptions + { + } + + virtual + ~CommandOptions () {} + + virtual Error + SetOptionValue (uint32_t option_idx, const char *option_arg) + { + Error error; + const int short_option = m_getopt_table[option_idx].val; + + switch (short_option) + { + case 'b': + m_level = lldb::eDescriptionLevelBrief; + break; + case 'f': + m_level = lldb::eDescriptionLevelFull; + break; + case 'v': + m_level = lldb::eDescriptionLevelVerbose; + break; + case 'i': + m_internal = true; + break; + default: + error.SetErrorStringWithFormat ("unrecognized option '%c'", short_option); + break; + } + + return error; + } + + void + OptionParsingStarting () + { + m_level = lldb::eDescriptionLevelFull; + m_internal = false; + } + + const OptionDefinition * + GetDefinitions () + { + return g_option_table; + } + + // Options table: Required for subclasses of Options. + + static OptionDefinition g_option_table[]; + + // Instance variables to hold the values for command options. + + lldb::DescriptionLevel m_level; + + bool m_internal; + }; + +protected: + virtual bool + DoExecute (Args& command, CommandReturnObject &result) + { + Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get(); + if (target == NULL) + { + result.AppendError ("Invalid target. No current target or breakpoints."); + result.SetStatus (eReturnStatusSuccessFinishNoResult); + return true; + } + + const BreakpointList &breakpoints = target->GetBreakpointList(m_options.m_internal); + Mutex::Locker locker; + target->GetBreakpointList(m_options.m_internal).GetListMutex(locker); + + size_t num_breakpoints = breakpoints.GetSize(); + + if (num_breakpoints == 0) + { + result.AppendMessage ("No breakpoints currently set."); + result.SetStatus (eReturnStatusSuccessFinishNoResult); + return true; + } + + Stream &output_stream = result.GetOutputStream(); + + if (command.GetArgumentCount() == 0) + { + // No breakpoint selected; show info about all currently set breakpoints. + result.AppendMessage ("Current breakpoints:"); + for (size_t i = 0; i < num_breakpoints; ++i) + { + Breakpoint *breakpoint = breakpoints.GetBreakpointAtIndex (i).get(); + AddBreakpointDescription (&output_stream, breakpoint, m_options.m_level); + } + result.SetStatus (eReturnStatusSuccessFinishNoResult); + } + else + { + // Particular breakpoints selected; show info about that breakpoint. + BreakpointIDList valid_bp_ids; + CommandObjectMultiwordBreakpoint::VerifyBreakpointIDs (command, target, result, &valid_bp_ids); + + if (result.Succeeded()) + { + for (size_t i = 0; i < valid_bp_ids.GetSize(); ++i) + { + BreakpointID cur_bp_id = valid_bp_ids.GetBreakpointIDAtIndex (i); + Breakpoint *breakpoint = target->GetBreakpointByID (cur_bp_id.GetBreakpointID()).get(); + AddBreakpointDescription (&output_stream, breakpoint, m_options.m_level); + } + result.SetStatus (eReturnStatusSuccessFinishNoResult); + } + else + { + result.AppendError ("Invalid breakpoint id."); + result.SetStatus (eReturnStatusFailed); + } + } + + return result.Succeeded(); + } + +private: + CommandOptions m_options; +}; + +#pragma mark List::CommandOptions +OptionDefinition +CommandObjectBreakpointList::CommandOptions::g_option_table[] = +{ + { LLDB_OPT_SET_ALL, false, "internal", 'i', no_argument, NULL, 0, eArgTypeNone, + "Show debugger internal breakpoints" }, + + { LLDB_OPT_SET_1, false, "brief", 'b', no_argument, NULL, 0, eArgTypeNone, + "Give a brief description of the breakpoint (no location info)."}, + + // FIXME: We need to add an "internal" command, and then add this sort of thing to it. + // But I need to see it for now, and don't want to wait. + { LLDB_OPT_SET_2, false, "full", 'f', no_argument, NULL, 0, eArgTypeNone, + "Give a full description of the breakpoint and its locations."}, + + { LLDB_OPT_SET_3, false, "verbose", 'v', no_argument, NULL, 0, eArgTypeNone, + "Explain everything we know about the breakpoint (for debugging debugger bugs)." }, + + { 0, false, NULL, 0, 0, NULL, 0, eArgTypeNone, NULL } +}; + +//------------------------------------------------------------------------- +// CommandObjectBreakpointClear +//------------------------------------------------------------------------- +#pragma mark Clear + +class CommandObjectBreakpointClear : public CommandObjectParsed +{ +public: + + typedef enum BreakpointClearType + { + eClearTypeInvalid, + eClearTypeFileAndLine + } BreakpointClearType; + + CommandObjectBreakpointClear (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "breakpoint clear", + "Clears a breakpoint or set of breakpoints in the executable.", + "breakpoint clear "), + m_options (interpreter) + { + } + + virtual + ~CommandObjectBreakpointClear () {} + + virtual Options * + GetOptions () + { + return &m_options; + } + + class CommandOptions : public Options + { + public: + + CommandOptions (CommandInterpreter &interpreter) : + Options (interpreter), + m_filename (), + m_line_num (0) + { + } + + virtual + ~CommandOptions () {} + + virtual Error + SetOptionValue (uint32_t option_idx, const char *option_arg) + { + Error error; + const int short_option = m_getopt_table[option_idx].val; + + switch (short_option) + { + case 'f': + m_filename.assign (option_arg); + break; + + case 'l': + m_line_num = Args::StringToUInt32 (option_arg, 0); + break; + + default: + error.SetErrorStringWithFormat ("unrecognized option '%c'", short_option); + break; + } + + return error; + } + + void + OptionParsingStarting () + { + m_filename.clear(); + m_line_num = 0; + } + + const OptionDefinition* + GetDefinitions () + { + return g_option_table; + } + + // Options table: Required for subclasses of Options. + + static OptionDefinition g_option_table[]; + + // Instance variables to hold the values for command options. + + std::string m_filename; + uint32_t m_line_num; + + }; + +protected: + virtual bool + DoExecute (Args& command, CommandReturnObject &result) + { + Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get(); + if (target == NULL) + { + result.AppendError ("Invalid target. No existing target or breakpoints."); + result.SetStatus (eReturnStatusFailed); + return false; + } + + // The following are the various types of breakpoints that could be cleared: + // 1). -f -l (clearing breakpoint by source location) + + BreakpointClearType break_type = eClearTypeInvalid; + + if (m_options.m_line_num != 0) + break_type = eClearTypeFileAndLine; + + Mutex::Locker locker; + target->GetBreakpointList().GetListMutex(locker); + + BreakpointList &breakpoints = target->GetBreakpointList(); + size_t num_breakpoints = breakpoints.GetSize(); + + // Early return if there's no breakpoint at all. + if (num_breakpoints == 0) + { + result.AppendError ("Breakpoint clear: No breakpoint cleared."); + result.SetStatus (eReturnStatusFailed); + return result.Succeeded(); + } + + // Find matching breakpoints and delete them. + + // First create a copy of all the IDs. + std::vector BreakIDs; + for (size_t i = 0; i < num_breakpoints; ++i) + BreakIDs.push_back(breakpoints.GetBreakpointAtIndex(i).get()->GetID()); + + int num_cleared = 0; + StreamString ss; + switch (break_type) + { + case eClearTypeFileAndLine: // Breakpoint by source position + { + const ConstString filename(m_options.m_filename.c_str()); + BreakpointLocationCollection loc_coll; + + for (size_t i = 0; i < num_breakpoints; ++i) + { + Breakpoint *bp = breakpoints.FindBreakpointByID(BreakIDs[i]).get(); + + if (bp->GetMatchingFileLine(filename, m_options.m_line_num, loc_coll)) + { + // If the collection size is 0, it's a full match and we can just remove the breakpoint. + if (loc_coll.GetSize() == 0) + { + bp->GetDescription(&ss, lldb::eDescriptionLevelBrief); + ss.EOL(); + target->RemoveBreakpointByID (bp->GetID()); + ++num_cleared; + } + } + } + } + break; + + default: + break; + } + + if (num_cleared > 0) + { + Stream &output_stream = result.GetOutputStream(); + output_stream.Printf ("%d breakpoints cleared:\n", num_cleared); + output_stream << ss.GetData(); + output_stream.EOL(); + result.SetStatus (eReturnStatusSuccessFinishNoResult); + } + else + { + result.AppendError ("Breakpoint clear: No breakpoint cleared."); + result.SetStatus (eReturnStatusFailed); + } + + return result.Succeeded(); + } + +private: + CommandOptions m_options; +}; + +#pragma mark Clear::CommandOptions + +OptionDefinition +CommandObjectBreakpointClear::CommandOptions::g_option_table[] = +{ + { LLDB_OPT_SET_1, false, "file", 'f', required_argument, NULL, CommandCompletions::eSourceFileCompletion, eArgTypeFilename, + "Specify the breakpoint by source location in this particular file."}, + + { LLDB_OPT_SET_1, true, "line", 'l', required_argument, NULL, 0, eArgTypeLineNum, + "Specify the breakpoint by source location at this particular line."}, + + { 0, false, NULL, 0, 0, NULL, 0, eArgTypeNone, NULL } +}; + +//------------------------------------------------------------------------- +// CommandObjectBreakpointDelete +//------------------------------------------------------------------------- +#pragma mark Delete + +class CommandObjectBreakpointDelete : public CommandObjectParsed +{ +public: + CommandObjectBreakpointDelete (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "breakpoint delete", + "Delete the specified breakpoint(s). If no breakpoints are specified, delete them all.", + NULL) + { + CommandArgumentEntry arg; + CommandObject::AddIDsArgumentData(arg, eArgTypeBreakpointID, eArgTypeBreakpointIDRange); + // Add the entry for the first argument for this command to the object's arguments vector. + m_arguments.push_back (arg); + } + + virtual + ~CommandObjectBreakpointDelete () {} + +protected: + virtual bool + DoExecute (Args& command, CommandReturnObject &result) + { + Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get(); + if (target == NULL) + { + result.AppendError ("Invalid target. No existing target or breakpoints."); + result.SetStatus (eReturnStatusFailed); + return false; + } + + Mutex::Locker locker; + target->GetBreakpointList().GetListMutex(locker); + + const BreakpointList &breakpoints = target->GetBreakpointList(); + + size_t num_breakpoints = breakpoints.GetSize(); + + if (num_breakpoints == 0) + { + result.AppendError ("No breakpoints exist to be deleted."); + result.SetStatus (eReturnStatusFailed); + return false; + } + + if (command.GetArgumentCount() == 0) + { + if (!m_interpreter.Confirm ("About to delete all breakpoints, do you want to do that?", true)) + { + result.AppendMessage("Operation cancelled..."); + } + else + { + target->RemoveAllBreakpoints (); + result.AppendMessageWithFormat ("All breakpoints removed. (%lu %s)\n", num_breakpoints, num_breakpoints > 1 ? "breakpoints" : "breakpoint"); + } + result.SetStatus (eReturnStatusSuccessFinishNoResult); + } + else + { + // Particular breakpoint selected; disable that breakpoint. + BreakpointIDList valid_bp_ids; + CommandObjectMultiwordBreakpoint::VerifyBreakpointIDs (command, target, result, &valid_bp_ids); + + if (result.Succeeded()) + { + int delete_count = 0; + int disable_count = 0; + const size_t count = valid_bp_ids.GetSize(); + for (size_t i = 0; i < count; ++i) + { + BreakpointID cur_bp_id = valid_bp_ids.GetBreakpointIDAtIndex (i); + + if (cur_bp_id.GetBreakpointID() != LLDB_INVALID_BREAK_ID) + { + if (cur_bp_id.GetLocationID() != LLDB_INVALID_BREAK_ID) + { + Breakpoint *breakpoint = target->GetBreakpointByID (cur_bp_id.GetBreakpointID()).get(); + BreakpointLocation *location = breakpoint->FindLocationByID (cur_bp_id.GetLocationID()).get(); + // It makes no sense to try to delete individual locations, so we disable them instead. + if (location) + { + location->SetEnabled (false); + ++disable_count; + } + } + else + { + target->RemoveBreakpointByID (cur_bp_id.GetBreakpointID()); + ++delete_count; + } + } + } + result.AppendMessageWithFormat ("%d breakpoints deleted; %d breakpoint locations disabled.\n", + delete_count, disable_count); + result.SetStatus (eReturnStatusSuccessFinishNoResult); + } + } + return result.Succeeded(); + } +}; + +//------------------------------------------------------------------------- +// CommandObjectMultiwordBreakpoint +//------------------------------------------------------------------------- +#pragma mark MultiwordBreakpoint + +CommandObjectMultiwordBreakpoint::CommandObjectMultiwordBreakpoint (CommandInterpreter &interpreter) : + CommandObjectMultiword (interpreter, + "breakpoint", + "A set of commands for operating on breakpoints. Also see _regexp-break.", + "breakpoint []") +{ + CommandObjectSP list_command_object (new CommandObjectBreakpointList (interpreter)); + CommandObjectSP enable_command_object (new CommandObjectBreakpointEnable (interpreter)); + CommandObjectSP disable_command_object (new CommandObjectBreakpointDisable (interpreter)); + CommandObjectSP clear_command_object (new CommandObjectBreakpointClear (interpreter)); + CommandObjectSP delete_command_object (new CommandObjectBreakpointDelete (interpreter)); + CommandObjectSP set_command_object (new CommandObjectBreakpointSet (interpreter)); + CommandObjectSP command_command_object (new CommandObjectBreakpointCommand (interpreter)); + CommandObjectSP modify_command_object (new CommandObjectBreakpointModify(interpreter)); + + list_command_object->SetCommandName ("breakpoint list"); + enable_command_object->SetCommandName("breakpoint enable"); + disable_command_object->SetCommandName("breakpoint disable"); + clear_command_object->SetCommandName("breakpoint clear"); + delete_command_object->SetCommandName("breakpoint delete"); + set_command_object->SetCommandName("breakpoint set"); + command_command_object->SetCommandName ("breakpoint command"); + modify_command_object->SetCommandName ("breakpoint modify"); + + LoadSubCommand ("list", list_command_object); + LoadSubCommand ("enable", enable_command_object); + LoadSubCommand ("disable", disable_command_object); + LoadSubCommand ("clear", clear_command_object); + LoadSubCommand ("delete", delete_command_object); + LoadSubCommand ("set", set_command_object); + LoadSubCommand ("command", command_command_object); + LoadSubCommand ("modify", modify_command_object); +} + +CommandObjectMultiwordBreakpoint::~CommandObjectMultiwordBreakpoint () +{ +} + +void +CommandObjectMultiwordBreakpoint::VerifyBreakpointIDs (Args &args, Target *target, CommandReturnObject &result, + BreakpointIDList *valid_ids) +{ + // args can be strings representing 1). integers (for breakpoint ids) + // 2). the full breakpoint & location canonical representation + // 3). the word "to" or a hyphen, representing a range (in which case there + // had *better* be an entry both before & after of one of the first two types. + // If args is empty, we will use the last created breakpoint (if there is one.) + + Args temp_args; + + if (args.GetArgumentCount() == 0) + { + if (target->GetLastCreatedBreakpoint()) + { + valid_ids->AddBreakpointID (BreakpointID(target->GetLastCreatedBreakpoint()->GetID(), LLDB_INVALID_BREAK_ID)); + result.SetStatus (eReturnStatusSuccessFinishNoResult); + } + else + { + result.AppendError("No breakpoint specified and no last created breakpoint."); + result.SetStatus (eReturnStatusFailed); + } + return; + } + + // Create a new Args variable to use; copy any non-breakpoint-id-ranges stuff directly from the old ARGS to + // the new TEMP_ARGS. Do not copy breakpoint id range strings over; instead generate a list of strings for + // all the breakpoint ids in the range, and shove all of those breakpoint id strings into TEMP_ARGS. + + BreakpointIDList::FindAndReplaceIDRanges (args, target, result, temp_args); + + // NOW, convert the list of breakpoint id strings in TEMP_ARGS into an actual BreakpointIDList: + + valid_ids->InsertStringArray (temp_args.GetConstArgumentVector(), temp_args.GetArgumentCount(), result); + + // At this point, all of the breakpoint ids that the user passed in have been converted to breakpoint IDs + // and put into valid_ids. + + if (result.Succeeded()) + { + // Now that we've converted everything from args into a list of breakpoint ids, go through our tentative list + // of breakpoint id's and verify that they correspond to valid/currently set breakpoints. + + const size_t count = valid_ids->GetSize(); + for (size_t i = 0; i < count; ++i) + { + BreakpointID cur_bp_id = valid_ids->GetBreakpointIDAtIndex (i); + Breakpoint *breakpoint = target->GetBreakpointByID (cur_bp_id.GetBreakpointID()).get(); + if (breakpoint != NULL) + { + const size_t num_locations = breakpoint->GetNumLocations(); + if (cur_bp_id.GetLocationID() > num_locations) + { + StreamString id_str; + BreakpointID::GetCanonicalReference (&id_str, + cur_bp_id.GetBreakpointID(), + cur_bp_id.GetLocationID()); + i = valid_ids->GetSize() + 1; + result.AppendErrorWithFormat ("'%s' is not a currently valid breakpoint/location id.\n", + id_str.GetData()); + result.SetStatus (eReturnStatusFailed); + } + } + else + { + i = valid_ids->GetSize() + 1; + result.AppendErrorWithFormat ("'%d' is not a currently valid breakpoint id.\n", cur_bp_id.GetBreakpointID()); + result.SetStatus (eReturnStatusFailed); + } + } + } +} diff --git a/contrib/llvm/tools/lldb/source/Commands/CommandObjectBreakpoint.h b/contrib/llvm/tools/lldb/source/Commands/CommandObjectBreakpoint.h new file mode 100644 index 00000000000..2d674b22d70 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Commands/CommandObjectBreakpoint.h @@ -0,0 +1,47 @@ +//===-- CommandObjectBreakpoint.h -------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_CommandObjectBreakpoint_h_ +#define liblldb_CommandObjectBreakpoint_h_ + +// C Includes +// C++ Includes + +#include +#include + +// Other libraries and framework includes +// Project includes +#include "lldb/Core/Address.h" +#include "lldb/Interpreter/CommandObjectMultiword.h" +#include "lldb/Interpreter/Options.h" +#include "lldb/Core/STLUtils.h" + +namespace lldb_private { + +//------------------------------------------------------------------------- +// CommandObjectMultiwordBreakpoint +//------------------------------------------------------------------------- + +class CommandObjectMultiwordBreakpoint : public CommandObjectMultiword +{ +public: + CommandObjectMultiwordBreakpoint (CommandInterpreter &interpreter); + + virtual + ~CommandObjectMultiwordBreakpoint (); + + static void + VerifyBreakpointIDs (Args &args, Target *target, CommandReturnObject &result, BreakpointIDList *valid_ids); + +}; + +} // namespace lldb_private + +#endif // liblldb_CommandObjectBreakpoint_h_ diff --git a/contrib/llvm/tools/lldb/source/Commands/CommandObjectBreakpointCommand.cpp b/contrib/llvm/tools/lldb/source/Commands/CommandObjectBreakpointCommand.cpp new file mode 100644 index 00000000000..c4504a4c651 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Commands/CommandObjectBreakpointCommand.cpp @@ -0,0 +1,915 @@ +//===-- CommandObjectBreakpointCommand.cpp ----------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +// C Includes +// C++ Includes + + +#include "CommandObjectBreakpointCommand.h" +#include "CommandObjectBreakpoint.h" + +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Interpreter/CommandReturnObject.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" +#include "lldb/Breakpoint/BreakpointIDList.h" +#include "lldb/Breakpoint/Breakpoint.h" +#include "lldb/Breakpoint/BreakpointLocation.h" +#include "lldb/Breakpoint/StoppointCallbackContext.h" +#include "lldb/Core/State.h" + +using namespace lldb; +using namespace lldb_private; + +//------------------------------------------------------------------------- +// CommandObjectBreakpointCommandAdd +//------------------------------------------------------------------------- + + +class CommandObjectBreakpointCommandAdd : public CommandObjectParsed +{ +public: + + CommandObjectBreakpointCommandAdd (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "add", + "Add a set of commands to a breakpoint, to be executed whenever the breakpoint is hit.", + NULL), + m_options (interpreter) + { + SetHelpLong ( +"\nGeneral information about entering breakpoint commands\n\ +------------------------------------------------------\n\ +\n\ +This command will cause you to be prompted to enter the command or set of\n\ +commands you wish to be executed when the specified breakpoint is hit. You\n\ +will be told to enter your command(s), and will see a '> 'prompt. Because\n\ +you can enter one or many commands to be executed when a breakpoint is hit,\n\ +you will continue to be prompted after each new-line that you enter, until you\n\ +enter the word 'DONE', which will cause the commands you have entered to be\n\ +stored with the breakpoint and executed when the breakpoint is hit.\n\ +\n\ +Syntax checking is not necessarily done when breakpoint commands are entered.\n\ +An improperly written breakpoint command will attempt to get executed when the\n\ +breakpoint gets hit, and usually silently fail. If your breakpoint command does\n\ +not appear to be getting executed, go back and check your syntax.\n\ +\n\ +Special information about PYTHON breakpoint commands\n\ +----------------------------------------------------\n\ +\n\ +You may enter either one line of Python, multiple lines of Python (including\n\ +function definitions), or specify a Python function in a module that has already,\n\ +or will be imported. If you enter a single line of Python, that will be passed\n\ +to the Python interpreter 'as is' when the breakpoint gets hit. If you enter\n\ +function definitions, they will be passed to the Python interpreter as soon as\n\ +you finish entering the breakpoint command, and they can be called later (don't\n\ +forget to add calls to them, if you want them called when the breakpoint is\n\ +hit). If you enter multiple lines of Python that are not function definitions,\n\ +they will be collected into a new, automatically generated Python function, and\n\ +a call to the newly generated function will be attached to the breakpoint.\n\ +\n\ +\n\ +This auto-generated function is passed in three arguments:\n\ +\n\ + frame: a lldb.SBFrame object for the frame which hit breakpoint.\n\ + bp_loc: a lldb.SBBreakpointLocation object that represents the breakpoint\n\ + location that was hit.\n\ + dict: the python session dictionary hit.\n\ +\n\ +When specifying a python function with the --python-function option, you need\n\ +to supply the function name prepended by the module name. So if you import a\n\ +module named 'myutils' that contains a 'breakpoint_callback' function, you would\n\ +specify the option as:\n\ +\n\ + --python-function myutils.breakpoint_callback\n\ +\n\ +The function itself must have the following prototype:\n\ +\n\ +def breakpoint_callback(frame, bp_loc, dict):\n\ + # Your code goes here\n\ +\n\ +The arguments are the same as the 3 auto generation function arguments listed\n\ +above. Note that the global variable 'lldb.frame' will NOT be setup when this\n\ +function is called, so be sure to use the 'frame' argument. The 'frame' argument\n\ +can get you to the thread (frame.GetThread()), the thread can get you to the\n\ +process (thread.GetProcess()), and the process can get you back to the target\n\ +(process.GetTarget()).\n\ +\n\ +Important Note: Because loose Python code gets collected into functions, if you\n\ +want to access global variables in the 'loose' code, you need to specify that\n\ +they are global, using the 'global' keyword. Be sure to use correct Python\n\ +syntax, including indentation, when entering Python breakpoint commands.\n\ +\n\ +As a third option, you can pass the name of an already existing Python function\n\ +and that function will be attached to the breakpoint. It will get passed the\n\ +frame and bp_loc arguments mentioned above.\n\ +\n\ +Example Python one-line breakpoint command:\n\ +\n\ +(lldb) breakpoint command add -s python 1\n\ +Enter your Python command(s). Type 'DONE' to end.\n\ +> print \"Hit this breakpoint!\"\n\ +> DONE\n\ +\n\ +As a convenience, this also works for a short Python one-liner:\n\ +(lldb) breakpoint command add -s python 1 -o \"import time; print time.asctime()\"\n\ +(lldb) run\n\ +Launching '.../a.out' (x86_64)\n\ +(lldb) Fri Sep 10 12:17:45 2010\n\ +Process 21778 Stopped\n\ +* thread #1: tid = 0x2e03, 0x0000000100000de8 a.out`c + 7 at main.c:39, stop reason = breakpoint 1.1, queue = com.apple.main-thread\n\ + 36 \n\ + 37 int c(int val)\n\ + 38 {\n\ + 39 -> return val + 3;\n\ + 40 }\n\ + 41 \n\ + 42 int main (int argc, char const *argv[])\n\ +(lldb)\n\ +\n\ +Example multiple line Python breakpoint command, using function definition:\n\ +\n\ +(lldb) breakpoint command add -s python 1\n\ +Enter your Python command(s). Type 'DONE' to end.\n\ +> def breakpoint_output (bp_no):\n\ +> out_string = \"Hit breakpoint number \" + repr (bp_no)\n\ +> print out_string\n\ +> return True\n\ +> breakpoint_output (1)\n\ +> DONE\n\ +\n\ +\n\ +Example multiple line Python breakpoint command, using 'loose' Python:\n\ +\n\ +(lldb) breakpoint command add -s p 1\n\ +Enter your Python command(s). Type 'DONE' to end.\n\ +> global bp_count\n\ +> bp_count = bp_count + 1\n\ +> print \"Hit this breakpoint \" + repr(bp_count) + \" times!\"\n\ +> DONE\n\ +\n\ +In this case, since there is a reference to a global variable,\n\ +'bp_count', you will also need to make sure 'bp_count' exists and is\n\ +initialized:\n\ +\n\ +(lldb) script\n\ +>>> bp_count = 0\n\ +>>> quit()\n\ +\n\ +(lldb)\n\ +\n\ +\n\ +Your Python code, however organized, can optionally return a value.\n\ +If the returned value is False, that tells LLDB not to stop at the breakpoint\n\ +to which the code is associated. Returning anything other than False, or even\n\ +returning None, or even omitting a return statement entirely, will cause\n\ +LLDB to stop.\n\ +\n\ +Final Note: If you get a warning that no breakpoint command was generated, but\n\ +you did not get any syntax errors, you probably forgot to add a call to your\n\ +functions.\n\ +\n\ +Special information about debugger command breakpoint commands\n\ +--------------------------------------------------------------\n\ +\n\ +You may enter any debugger command, exactly as you would at the debugger prompt.\n\ +You may enter as many debugger commands as you like, but do NOT enter more than\n\ +one command per line.\n" ); + + CommandArgumentEntry arg; + CommandArgumentData bp_id_arg; + + // Define the first (and only) variant of this arg. + bp_id_arg.arg_type = eArgTypeBreakpointID; + bp_id_arg.arg_repetition = eArgRepeatPlain; + + // There is only one variant this argument could be; put it into the argument entry. + arg.push_back (bp_id_arg); + + // Push the data for the first argument into the m_arguments vector. + m_arguments.push_back (arg); + } + + virtual + ~CommandObjectBreakpointCommandAdd () {} + + virtual Options * + GetOptions () + { + return &m_options; + } + + void + CollectDataForBreakpointCommandCallback (BreakpointOptions *bp_options, + CommandReturnObject &result) + { + InputReaderSP reader_sp (new InputReader(m_interpreter.GetDebugger())); + std::unique_ptr data_ap(new BreakpointOptions::CommandData()); + if (reader_sp && data_ap.get()) + { + BatonSP baton_sp (new BreakpointOptions::CommandBaton (data_ap.release())); + bp_options->SetCallback (BreakpointOptionsCallbackFunction, baton_sp); + + Error err (reader_sp->Initialize (CommandObjectBreakpointCommandAdd::GenerateBreakpointCommandCallback, + bp_options, // baton + eInputReaderGranularityLine, // token size, to pass to callback function + "DONE", // end token + "> ", // prompt + true)); // echo input + if (err.Success()) + { + m_interpreter.GetDebugger().PushInputReader (reader_sp); + result.SetStatus (eReturnStatusSuccessFinishNoResult); + } + else + { + result.AppendError (err.AsCString()); + result.SetStatus (eReturnStatusFailed); + } + } + else + { + result.AppendError("out of memory"); + result.SetStatus (eReturnStatusFailed); + } + + } + + /// Set a one-liner as the callback for the breakpoint. + void + SetBreakpointCommandCallback (BreakpointOptions *bp_options, + const char *oneliner) + { + std::unique_ptr data_ap(new BreakpointOptions::CommandData()); + + // It's necessary to set both user_source and script_source to the oneliner. + // The former is used to generate callback description (as in breakpoint command list) + // while the latter is used for Python to interpret during the actual callback. + data_ap->user_source.AppendString (oneliner); + data_ap->script_source.assign (oneliner); + data_ap->stop_on_error = m_options.m_stop_on_error; + + BatonSP baton_sp (new BreakpointOptions::CommandBaton (data_ap.release())); + bp_options->SetCallback (BreakpointOptionsCallbackFunction, baton_sp); + + return; + } + + static size_t + GenerateBreakpointCommandCallback (void *baton, + InputReader &reader, + lldb::InputReaderAction notification, + const char *bytes, + size_t bytes_len) + { + StreamSP out_stream = reader.GetDebugger().GetAsyncOutputStream(); + bool batch_mode = reader.GetDebugger().GetCommandInterpreter().GetBatchCommandMode(); + + switch (notification) + { + case eInputReaderActivate: + if (!batch_mode) + { + out_stream->Printf ("%s\n", g_reader_instructions); + if (reader.GetPrompt()) + out_stream->Printf ("%s", reader.GetPrompt()); + out_stream->Flush(); + } + break; + + case eInputReaderDeactivate: + break; + + case eInputReaderReactivate: + if (reader.GetPrompt() && !batch_mode) + { + out_stream->Printf ("%s", reader.GetPrompt()); + out_stream->Flush(); + } + break; + + case eInputReaderAsynchronousOutputWritten: + break; + + case eInputReaderGotToken: + if (bytes && bytes_len && baton) + { + BreakpointOptions *bp_options = (BreakpointOptions *) baton; + if (bp_options) + { + Baton *bp_options_baton = bp_options->GetBaton(); + if (bp_options_baton) + ((BreakpointOptions::CommandData *)bp_options_baton->m_data)->user_source.AppendString (bytes, bytes_len); + } + } + if (!reader.IsDone() && reader.GetPrompt() && !batch_mode) + { + out_stream->Printf ("%s", reader.GetPrompt()); + out_stream->Flush(); + } + break; + + case eInputReaderInterrupt: + { + // Finish, and cancel the breakpoint command. + reader.SetIsDone (true); + BreakpointOptions *bp_options = (BreakpointOptions *) baton; + if (bp_options) + { + Baton *bp_options_baton = bp_options->GetBaton (); + if (bp_options_baton) + { + ((BreakpointOptions::CommandData *) bp_options_baton->m_data)->user_source.Clear(); + ((BreakpointOptions::CommandData *) bp_options_baton->m_data)->script_source.clear(); + } + } + if (!batch_mode) + { + out_stream->Printf ("Warning: No command attached to breakpoint.\n"); + out_stream->Flush(); + } + } + break; + + case eInputReaderEndOfFile: + reader.SetIsDone (true); + break; + + case eInputReaderDone: + break; + } + + return bytes_len; + } + + static bool + BreakpointOptionsCallbackFunction (void *baton, + StoppointCallbackContext *context, + lldb::user_id_t break_id, + lldb::user_id_t break_loc_id) + { + bool ret_value = true; + if (baton == NULL) + return true; + + + BreakpointOptions::CommandData *data = (BreakpointOptions::CommandData *) baton; + StringList &commands = data->user_source; + + if (commands.GetSize() > 0) + { + ExecutionContext exe_ctx (context->exe_ctx_ref); + Target *target = exe_ctx.GetTargetPtr(); + if (target) + { + CommandReturnObject result; + Debugger &debugger = target->GetDebugger(); + // Rig up the results secondary output stream to the debugger's, so the output will come out synchronously + // if the debugger is set up that way. + + StreamSP output_stream (debugger.GetAsyncOutputStream()); + StreamSP error_stream (debugger.GetAsyncErrorStream()); + result.SetImmediateOutputStream (output_stream); + result.SetImmediateErrorStream (error_stream); + + bool stop_on_continue = true; + bool echo_commands = false; + bool print_results = true; + + debugger.GetCommandInterpreter().HandleCommands (commands, + &exe_ctx, + stop_on_continue, + data->stop_on_error, + echo_commands, + print_results, + eLazyBoolNo, + result); + result.GetImmediateOutputStream()->Flush(); + result.GetImmediateErrorStream()->Flush(); + } + } + return ret_value; + } + + class CommandOptions : public Options + { + public: + + CommandOptions (CommandInterpreter &interpreter) : + Options (interpreter), + m_use_commands (false), + m_use_script_language (false), + m_script_language (eScriptLanguageNone), + m_use_one_liner (false), + m_one_liner(), + m_function_name() + { + } + + virtual + ~CommandOptions () {} + + virtual Error + SetOptionValue (uint32_t option_idx, const char *option_arg) + { + Error error; + const int short_option = m_getopt_table[option_idx].val; + + switch (short_option) + { + case 'o': + m_use_one_liner = true; + m_one_liner = option_arg; + break; + + case 's': + m_script_language = (lldb::ScriptLanguage) Args::StringToOptionEnum (option_arg, + g_option_table[option_idx].enum_values, + eScriptLanguageNone, + error); + + if (m_script_language == eScriptLanguagePython || m_script_language == eScriptLanguageDefault) + { + m_use_script_language = true; + } + else + { + m_use_script_language = false; + } + break; + + case 'e': + { + bool success = false; + m_stop_on_error = Args::StringToBoolean(option_arg, false, &success); + if (!success) + error.SetErrorStringWithFormat("invalid value for stop-on-error: \"%s\"", option_arg); + } + break; + + case 'F': + { + m_use_one_liner = false; + m_use_script_language = true; + m_function_name.assign(option_arg); + } + break; + + default: + break; + } + return error; + } + void + OptionParsingStarting () + { + m_use_commands = true; + m_use_script_language = false; + m_script_language = eScriptLanguageNone; + + m_use_one_liner = false; + m_stop_on_error = true; + m_one_liner.clear(); + m_function_name.clear(); + } + + const OptionDefinition* + GetDefinitions () + { + return g_option_table; + } + + // Options table: Required for subclasses of Options. + + static OptionDefinition g_option_table[]; + + // Instance variables to hold the values for command options. + + bool m_use_commands; + bool m_use_script_language; + lldb::ScriptLanguage m_script_language; + + // Instance variables to hold the values for one_liner options. + bool m_use_one_liner; + std::string m_one_liner; + bool m_stop_on_error; + std::string m_function_name; + }; + +protected: + virtual bool + DoExecute (Args& command, CommandReturnObject &result) + { + Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get(); + + if (target == NULL) + { + result.AppendError ("There is not a current executable; there are no breakpoints to which to add commands"); + result.SetStatus (eReturnStatusFailed); + return false; + } + + const BreakpointList &breakpoints = target->GetBreakpointList(); + size_t num_breakpoints = breakpoints.GetSize(); + + if (num_breakpoints == 0) + { + result.AppendError ("No breakpoints exist to have commands added"); + result.SetStatus (eReturnStatusFailed); + return false; + } + + if (m_options.m_use_script_language == false && m_options.m_function_name.size()) + { + result.AppendError ("need to enable scripting to have a function run as a breakpoint command"); + result.SetStatus (eReturnStatusFailed); + return false; + } + + BreakpointIDList valid_bp_ids; + CommandObjectMultiwordBreakpoint::VerifyBreakpointIDs (command, target, result, &valid_bp_ids); + + if (result.Succeeded()) + { + const size_t count = valid_bp_ids.GetSize(); + if (count > 1) + { + result.AppendError ("can only add commands to one breakpoint at a time."); + result.SetStatus (eReturnStatusFailed); + return false; + } + + for (size_t i = 0; i < count; ++i) + { + BreakpointID cur_bp_id = valid_bp_ids.GetBreakpointIDAtIndex (i); + if (cur_bp_id.GetBreakpointID() != LLDB_INVALID_BREAK_ID) + { + Breakpoint *bp = target->GetBreakpointByID (cur_bp_id.GetBreakpointID()).get(); + BreakpointOptions *bp_options = NULL; + if (cur_bp_id.GetLocationID() == LLDB_INVALID_BREAK_ID) + { + // This breakpoint does not have an associated location. + bp_options = bp->GetOptions(); + } + else + { + BreakpointLocationSP bp_loc_sp(bp->FindLocationByID (cur_bp_id.GetLocationID())); + // This breakpoint does have an associated location. + // Get its breakpoint options. + if (bp_loc_sp) + bp_options = bp_loc_sp->GetLocationOptions(); + } + + // Skip this breakpoint if bp_options is not good. + if (bp_options == NULL) continue; + + // If we are using script language, get the script interpreter + // in order to set or collect command callback. Otherwise, call + // the methods associated with this object. + if (m_options.m_use_script_language) + { + // Special handling for one-liner specified inline. + if (m_options.m_use_one_liner) + { + m_interpreter.GetScriptInterpreter()->SetBreakpointCommandCallback (bp_options, + m_options.m_one_liner.c_str()); + } + // Special handling for using a Python function by name + // instead of extending the breakpoint callback data structures, we just automatize + // what the user would do manually: make their breakpoint command be a function call + else if (m_options.m_function_name.size()) + { + std::string oneliner("return "); + oneliner += m_options.m_function_name; + oneliner += "(frame, bp_loc, internal_dict)"; + m_interpreter.GetScriptInterpreter()->SetBreakpointCommandCallback (bp_options, + oneliner.c_str()); + } + else + { + m_interpreter.GetScriptInterpreter()->CollectDataForBreakpointCommandCallback (bp_options, + result); + } + } + else + { + // Special handling for one-liner specified inline. + if (m_options.m_use_one_liner) + SetBreakpointCommandCallback (bp_options, + m_options.m_one_liner.c_str()); + else + CollectDataForBreakpointCommandCallback (bp_options, + result); + } + } + } + } + + return result.Succeeded(); + } + +private: + CommandOptions m_options; + static const char *g_reader_instructions; + +}; + +const char * +CommandObjectBreakpointCommandAdd::g_reader_instructions = "Enter your debugger command(s). Type 'DONE' to end."; + +// FIXME: "script-type" needs to have its contents determined dynamically, so somebody can add a new scripting +// language to lldb and have it pickable here without having to change this enumeration by hand and rebuild lldb proper. + +static OptionEnumValueElement +g_script_option_enumeration[4] = +{ + { eScriptLanguageNone, "command", "Commands are in the lldb command interpreter language"}, + { eScriptLanguagePython, "python", "Commands are in the Python language."}, + { eSortOrderByName, "default-script", "Commands are in the default scripting language."}, + { 0, NULL, NULL } +}; + +OptionDefinition +CommandObjectBreakpointCommandAdd::CommandOptions::g_option_table[] = +{ + { LLDB_OPT_SET_1, false, "one-liner", 'o', required_argument, NULL, 0, eArgTypeOneLiner, + "Specify a one-line breakpoint command inline. Be sure to surround it with quotes." }, + + { LLDB_OPT_SET_ALL, false, "stop-on-error", 'e', required_argument, NULL, 0, eArgTypeBoolean, + "Specify whether breakpoint command execution should terminate on error." }, + + { LLDB_OPT_SET_ALL, false, "script-type", 's', required_argument, g_script_option_enumeration, 0, eArgTypeNone, + "Specify the language for the commands - if none is specified, the lldb command interpreter will be used."}, + + { LLDB_OPT_SET_2, false, "python-function", 'F', required_argument, NULL, 0, eArgTypePythonFunction, + "Give the name of a Python function to run as command for this breakpoint. Be sure to give a module name if appropriate."}, + + { 0, false, NULL, 0, 0, NULL, 0, eArgTypeNone, NULL } +}; + +//------------------------------------------------------------------------- +// CommandObjectBreakpointCommandDelete +//------------------------------------------------------------------------- + +class CommandObjectBreakpointCommandDelete : public CommandObjectParsed +{ +public: + CommandObjectBreakpointCommandDelete (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "delete", + "Delete the set of commands from a breakpoint.", + NULL) + { + CommandArgumentEntry arg; + CommandArgumentData bp_id_arg; + + // Define the first (and only) variant of this arg. + bp_id_arg.arg_type = eArgTypeBreakpointID; + bp_id_arg.arg_repetition = eArgRepeatPlain; + + // There is only one variant this argument could be; put it into the argument entry. + arg.push_back (bp_id_arg); + + // Push the data for the first argument into the m_arguments vector. + m_arguments.push_back (arg); + } + + + virtual + ~CommandObjectBreakpointCommandDelete () {} + +protected: + virtual bool + DoExecute (Args& command, CommandReturnObject &result) + { + Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get(); + + if (target == NULL) + { + result.AppendError ("There is not a current executable; there are no breakpoints from which to delete commands"); + result.SetStatus (eReturnStatusFailed); + return false; + } + + const BreakpointList &breakpoints = target->GetBreakpointList(); + size_t num_breakpoints = breakpoints.GetSize(); + + if (num_breakpoints == 0) + { + result.AppendError ("No breakpoints exist to have commands deleted"); + result.SetStatus (eReturnStatusFailed); + return false; + } + + if (command.GetArgumentCount() == 0) + { + result.AppendError ("No breakpoint specified from which to delete the commands"); + result.SetStatus (eReturnStatusFailed); + return false; + } + + BreakpointIDList valid_bp_ids; + CommandObjectMultiwordBreakpoint::VerifyBreakpointIDs (command, target, result, &valid_bp_ids); + + if (result.Succeeded()) + { + const size_t count = valid_bp_ids.GetSize(); + for (size_t i = 0; i < count; ++i) + { + BreakpointID cur_bp_id = valid_bp_ids.GetBreakpointIDAtIndex (i); + if (cur_bp_id.GetBreakpointID() != LLDB_INVALID_BREAK_ID) + { + Breakpoint *bp = target->GetBreakpointByID (cur_bp_id.GetBreakpointID()).get(); + if (cur_bp_id.GetLocationID() != LLDB_INVALID_BREAK_ID) + { + BreakpointLocationSP bp_loc_sp (bp->FindLocationByID (cur_bp_id.GetLocationID())); + if (bp_loc_sp) + bp_loc_sp->ClearCallback(); + else + { + result.AppendErrorWithFormat("Invalid breakpoint ID: %u.%u.\n", + cur_bp_id.GetBreakpointID(), + cur_bp_id.GetLocationID()); + result.SetStatus (eReturnStatusFailed); + return false; + } + } + else + { + bp->ClearCallback(); + } + } + } + } + return result.Succeeded(); + } +}; + +//------------------------------------------------------------------------- +// CommandObjectBreakpointCommandList +//------------------------------------------------------------------------- + +class CommandObjectBreakpointCommandList : public CommandObjectParsed +{ +public: + CommandObjectBreakpointCommandList (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "list", + "List the script or set of commands to be executed when the breakpoint is hit.", + NULL) + { + CommandArgumentEntry arg; + CommandArgumentData bp_id_arg; + + // Define the first (and only) variant of this arg. + bp_id_arg.arg_type = eArgTypeBreakpointID; + bp_id_arg.arg_repetition = eArgRepeatPlain; + + // There is only one variant this argument could be; put it into the argument entry. + arg.push_back (bp_id_arg); + + // Push the data for the first argument into the m_arguments vector. + m_arguments.push_back (arg); + } + + virtual + ~CommandObjectBreakpointCommandList () {} + +protected: + virtual bool + DoExecute (Args& command, + CommandReturnObject &result) + { + Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get(); + + if (target == NULL) + { + result.AppendError ("There is not a current executable; there are no breakpoints for which to list commands"); + result.SetStatus (eReturnStatusFailed); + return false; + } + + const BreakpointList &breakpoints = target->GetBreakpointList(); + size_t num_breakpoints = breakpoints.GetSize(); + + if (num_breakpoints == 0) + { + result.AppendError ("No breakpoints exist for which to list commands"); + result.SetStatus (eReturnStatusFailed); + return false; + } + + if (command.GetArgumentCount() == 0) + { + result.AppendError ("No breakpoint specified for which to list the commands"); + result.SetStatus (eReturnStatusFailed); + return false; + } + + BreakpointIDList valid_bp_ids; + CommandObjectMultiwordBreakpoint::VerifyBreakpointIDs (command, target, result, &valid_bp_ids); + + if (result.Succeeded()) + { + const size_t count = valid_bp_ids.GetSize(); + for (size_t i = 0; i < count; ++i) + { + BreakpointID cur_bp_id = valid_bp_ids.GetBreakpointIDAtIndex (i); + if (cur_bp_id.GetBreakpointID() != LLDB_INVALID_BREAK_ID) + { + Breakpoint *bp = target->GetBreakpointByID (cur_bp_id.GetBreakpointID()).get(); + + if (bp) + { + const BreakpointOptions *bp_options = NULL; + if (cur_bp_id.GetLocationID() != LLDB_INVALID_BREAK_ID) + { + BreakpointLocationSP bp_loc_sp(bp->FindLocationByID (cur_bp_id.GetLocationID())); + if (bp_loc_sp) + bp_options = bp_loc_sp->GetOptionsNoCreate(); + else + { + result.AppendErrorWithFormat("Invalid breakpoint ID: %u.%u.\n", + cur_bp_id.GetBreakpointID(), + cur_bp_id.GetLocationID()); + result.SetStatus (eReturnStatusFailed); + return false; + } + } + else + { + bp_options = bp->GetOptions(); + } + + if (bp_options) + { + StreamString id_str; + BreakpointID::GetCanonicalReference (&id_str, + cur_bp_id.GetBreakpointID(), + cur_bp_id.GetLocationID()); + const Baton *baton = bp_options->GetBaton(); + if (baton) + { + result.GetOutputStream().Printf ("Breakpoint %s:\n", id_str.GetData()); + result.GetOutputStream().IndentMore (); + baton->GetDescription(&result.GetOutputStream(), eDescriptionLevelFull); + result.GetOutputStream().IndentLess (); + } + else + { + result.AppendMessageWithFormat ("Breakpoint %s does not have an associated command.\n", + id_str.GetData()); + } + } + result.SetStatus (eReturnStatusSuccessFinishResult); + } + else + { + result.AppendErrorWithFormat("Invalid breakpoint ID: %u.\n", cur_bp_id.GetBreakpointID()); + result.SetStatus (eReturnStatusFailed); + } + + } + } + } + + return result.Succeeded(); + } +}; + +//------------------------------------------------------------------------- +// CommandObjectBreakpointCommand +//------------------------------------------------------------------------- + +CommandObjectBreakpointCommand::CommandObjectBreakpointCommand (CommandInterpreter &interpreter) : + CommandObjectMultiword (interpreter, + "command", + "A set of commands for adding, removing and examining bits of code to be executed when the breakpoint is hit (breakpoint 'commmands').", + "command [] ") +{ + CommandObjectSP add_command_object (new CommandObjectBreakpointCommandAdd (interpreter)); + CommandObjectSP delete_command_object (new CommandObjectBreakpointCommandDelete (interpreter)); + CommandObjectSP list_command_object (new CommandObjectBreakpointCommandList (interpreter)); + + add_command_object->SetCommandName ("breakpoint command add"); + delete_command_object->SetCommandName ("breakpoint command delete"); + list_command_object->SetCommandName ("breakpoint command list"); + + LoadSubCommand ("add", add_command_object); + LoadSubCommand ("delete", delete_command_object); + LoadSubCommand ("list", list_command_object); +} + +CommandObjectBreakpointCommand::~CommandObjectBreakpointCommand () +{ +} + + diff --git a/contrib/llvm/tools/lldb/source/Commands/CommandObjectBreakpointCommand.h b/contrib/llvm/tools/lldb/source/Commands/CommandObjectBreakpointCommand.h new file mode 100644 index 00000000000..afedb7602cd --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Commands/CommandObjectBreakpointCommand.h @@ -0,0 +1,46 @@ +//===-- CommandObjectBreakpointCommand.h ------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_CommandObjectBreakpointCommand_h_ +#define liblldb_CommandObjectBreakpointCommand_h_ + +// C Includes +// C++ Includes + + +// Other libraries and framework includes +// Project includes + +#include "lldb/lldb-types.h" +#include "lldb/Interpreter/Options.h" +#include "lldb/Core/InputReader.h" +#include "lldb/Interpreter/CommandObject.h" +#include "lldb/Interpreter/CommandReturnObject.h" +#include "lldb/Interpreter/CommandObjectMultiword.h" + + +namespace lldb_private { + +//------------------------------------------------------------------------- +// CommandObjectMultiwordBreakpoint +//------------------------------------------------------------------------- + +class CommandObjectBreakpointCommand : public CommandObjectMultiword +{ +public: + CommandObjectBreakpointCommand (CommandInterpreter &interpreter); + + virtual + ~CommandObjectBreakpointCommand (); + +}; + +} // namespace lldb_private + +#endif // liblldb_CommandObjectBreakpointCommand_h_ diff --git a/contrib/llvm/tools/lldb/source/Commands/CommandObjectCommands.cpp b/contrib/llvm/tools/lldb/source/Commands/CommandObjectCommands.cpp new file mode 100644 index 00000000000..4699aa6d9c4 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Commands/CommandObjectCommands.cpp @@ -0,0 +1,2021 @@ +//===-- CommandObjectSource.cpp ---------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "CommandObjectCommands.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +#include "llvm/ADT/StringRef.h" + +// Project includes +#include "lldb/Core/Debugger.h" +#include "lldb/Core/InputReader.h" +#include "lldb/Core/InputReaderEZ.h" +#include "lldb/Core/StringList.h" +#include "lldb/Interpreter/Args.h" +#include "lldb/Interpreter/CommandHistory.h" +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Interpreter/CommandObjectRegexCommand.h" +#include "lldb/Interpreter/CommandReturnObject.h" +#include "lldb/Interpreter/OptionValueBoolean.h" +#include "lldb/Interpreter/OptionValueUInt64.h" +#include "lldb/Interpreter/Options.h" +#include "lldb/Interpreter/ScriptInterpreter.h" +#include "lldb/Interpreter/ScriptInterpreterPython.h" + +using namespace lldb; +using namespace lldb_private; + +//------------------------------------------------------------------------- +// CommandObjectCommandsSource +//------------------------------------------------------------------------- + +class CommandObjectCommandsHistory : public CommandObjectParsed +{ +public: + CommandObjectCommandsHistory(CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "command history", + "Dump the history of commands in this session.", + NULL), + m_options (interpreter) + { + } + + ~CommandObjectCommandsHistory () {} + + virtual Options * + GetOptions () + { + return &m_options; + } + +protected: + + class CommandOptions : public Options + { + public: + + CommandOptions (CommandInterpreter &interpreter) : + Options (interpreter), + m_start_idx(0), + m_stop_idx(0), + m_count(0), + m_clear(false) + { + } + + virtual + ~CommandOptions (){} + + virtual Error + SetOptionValue (uint32_t option_idx, const char *option_arg) + { + Error error; + const int short_option = m_getopt_table[option_idx].val; + + switch (short_option) + { + case 'c': + error = m_count.SetValueFromCString(option_arg,eVarSetOperationAssign); + break; + case 's': + if (option_arg && strcmp("end", option_arg) == 0) + { + m_start_idx.SetCurrentValue(UINT64_MAX); + m_start_idx.SetOptionWasSet(); + } + else + error = m_start_idx.SetValueFromCString(option_arg,eVarSetOperationAssign); + break; + case 'e': + error = m_stop_idx.SetValueFromCString(option_arg,eVarSetOperationAssign); + break; + case 'C': + m_clear.SetCurrentValue(true); + m_clear.SetOptionWasSet(); + break; + default: + error.SetErrorStringWithFormat ("unrecognized option '%c'", short_option); + break; + } + + return error; + } + + void + OptionParsingStarting () + { + m_start_idx.Clear(); + m_stop_idx.Clear(); + m_count.Clear(); + m_clear.Clear(); + } + + const OptionDefinition* + GetDefinitions () + { + return g_option_table; + } + + // Options table: Required for subclasses of Options. + + static OptionDefinition g_option_table[]; + + // Instance variables to hold the values for command options. + + OptionValueUInt64 m_start_idx; + OptionValueUInt64 m_stop_idx; + OptionValueUInt64 m_count; + OptionValueBoolean m_clear; + }; + + bool + DoExecute (Args& command, CommandReturnObject &result) + { + if (m_options.m_clear.GetCurrentValue() && m_options.m_clear.OptionWasSet()) + { + m_interpreter.GetCommandHistory().Clear(); + result.SetStatus(lldb::eReturnStatusSuccessFinishNoResult); + } + else + { + if (m_options.m_start_idx.OptionWasSet() && m_options.m_stop_idx.OptionWasSet() && m_options.m_count.OptionWasSet()) + { + result.AppendError("--count, --start-index and --end-index cannot be all specified in the same invocation"); + result.SetStatus(lldb::eReturnStatusFailed); + } + else + { + std::pair start_idx = {m_options.m_start_idx.OptionWasSet(),m_options.m_start_idx.GetCurrentValue()}; + std::pair stop_idx = {m_options.m_stop_idx.OptionWasSet(),m_options.m_stop_idx.GetCurrentValue()}; + std::pair count = {m_options.m_count.OptionWasSet(),m_options.m_count.GetCurrentValue()}; + + const CommandHistory& history(m_interpreter.GetCommandHistory()); + + if (start_idx.first && start_idx.second == UINT64_MAX) + { + if (count.first) + { + start_idx.second = history.GetSize() - count.second; + stop_idx.second = history.GetSize() - 1; + } + else if (stop_idx.first) + { + start_idx.second = stop_idx.second; + stop_idx.second = history.GetSize() - 1; + } + else + { + start_idx.second = 0; + stop_idx.second = history.GetSize() - 1; + } + } + else + { + if (!start_idx.first && !stop_idx.first && !count.first) + { + start_idx.second = 0; + stop_idx.second = history.GetSize() - 1; + } + else if (start_idx.first) + { + if (count.first) + { + stop_idx.second = start_idx.second + count.second - 1; + } + else if (!stop_idx.first) + { + stop_idx.second = history.GetSize() - 1; + } + } + else if (stop_idx.first) + { + if (count.first) + { + if (stop_idx.second >= count.second) + start_idx.second = stop_idx.second - count.second + 1; + else + start_idx.second = 0; + } + } + else /* if (count.first) */ + { + start_idx.second = 0; + stop_idx.second = count.second - 1; + } + } + history.Dump(result.GetOutputStream(), start_idx.second, stop_idx.second); + } + } + return result.Succeeded(); + + } + + CommandOptions m_options; +}; + +OptionDefinition +CommandObjectCommandsHistory::CommandOptions::g_option_table[] = +{ +{ LLDB_OPT_SET_1, false, "count", 'c', required_argument, NULL, 0, eArgTypeUnsignedInteger, "How many history commands to print."}, +{ LLDB_OPT_SET_1, false, "start-index", 's', required_argument, NULL, 0, eArgTypeUnsignedInteger, "Index at which to start printing history commands (or end to mean tail mode)."}, +{ LLDB_OPT_SET_1, false, "end-index", 'e', required_argument, NULL, 0, eArgTypeUnsignedInteger, "Index at which to stop printing history commands."}, +{ LLDB_OPT_SET_2, false, "clear", 'C', no_argument, NULL, 0, eArgTypeBoolean, "Clears the current command history."}, +{ 0, false, NULL, 0, 0, NULL, 0, eArgTypeNone, NULL } +}; + + +//------------------------------------------------------------------------- +// CommandObjectCommandsSource +//------------------------------------------------------------------------- + +class CommandObjectCommandsSource : public CommandObjectParsed +{ +public: + CommandObjectCommandsSource(CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "command source", + "Read in debugger commands from the file and execute them.", + NULL), + m_options (interpreter) + { + CommandArgumentEntry arg; + CommandArgumentData file_arg; + + // Define the first (and only) variant of this arg. + file_arg.arg_type = eArgTypeFilename; + file_arg.arg_repetition = eArgRepeatPlain; + + // There is only one variant this argument could be; put it into the argument entry. + arg.push_back (file_arg); + + // Push the data for the first argument into the m_arguments vector. + m_arguments.push_back (arg); + } + + ~CommandObjectCommandsSource () {} + + virtual const char* + GetRepeatCommand (Args ¤t_command_args, uint32_t index) + { + return ""; + } + + virtual int + HandleArgumentCompletion (Args &input, + int &cursor_index, + int &cursor_char_position, + OptionElementVector &opt_element_vector, + int match_start_point, + int max_return_elements, + bool &word_complete, + StringList &matches) + { + std::string completion_str (input.GetArgumentAtIndex(cursor_index)); + completion_str.erase (cursor_char_position); + + CommandCompletions::InvokeCommonCompletionCallbacks (m_interpreter, + CommandCompletions::eDiskFileCompletion, + completion_str.c_str(), + match_start_point, + max_return_elements, + NULL, + word_complete, + matches); + return matches.GetSize(); + } + + virtual Options * + GetOptions () + { + return &m_options; + } + +protected: + + class CommandOptions : public Options + { + public: + + CommandOptions (CommandInterpreter &interpreter) : + Options (interpreter), + m_stop_on_error (true) + { + } + + virtual + ~CommandOptions (){} + + virtual Error + SetOptionValue (uint32_t option_idx, const char *option_arg) + { + Error error; + const int short_option = m_getopt_table[option_idx].val; + bool success; + + switch (short_option) + { + case 'e': + error = m_stop_on_error.SetValueFromCString(option_arg); + break; + case 'c': + m_stop_on_continue = Args::StringToBoolean(option_arg, true, &success); + if (!success) + error.SetErrorStringWithFormat("invalid value for stop-on-continue: %s", option_arg); + break; + case 's': + m_silent_run = Args::StringToBoolean(option_arg, true, &success); + if (!success) + error.SetErrorStringWithFormat("invalid value for silent-run: %s", option_arg); + break; + default: + error.SetErrorStringWithFormat ("unrecognized option '%c'", short_option); + break; + } + + return error; + } + + void + OptionParsingStarting () + { + m_stop_on_error.Clear(); + m_silent_run = false; + m_stop_on_continue = true; + } + + const OptionDefinition* + GetDefinitions () + { + return g_option_table; + } + + // Options table: Required for subclasses of Options. + + static OptionDefinition g_option_table[]; + + // Instance variables to hold the values for command options. + + OptionValueBoolean m_stop_on_error; + bool m_silent_run; + bool m_stop_on_continue; + }; + + bool + DoExecute(Args& command, CommandReturnObject &result) + { + const size_t argc = command.GetArgumentCount(); + if (argc == 1) + { + const char *filename = command.GetArgumentAtIndex(0); + + result.AppendMessageWithFormat ("Executing commands in '%s'.\n", filename); + + FileSpec cmd_file (filename, true); + ExecutionContext *exe_ctx = NULL; // Just use the default context. + bool echo_commands = !m_options.m_silent_run; + bool print_results = true; + bool stop_on_error = m_options.m_stop_on_error.OptionWasSet() ? (bool)m_options.m_stop_on_error : m_interpreter.GetStopCmdSourceOnError(); + + m_interpreter.HandleCommandsFromFile (cmd_file, + exe_ctx, + m_options.m_stop_on_continue, + stop_on_error, + echo_commands, + print_results, + eLazyBoolCalculate, + result); + } + else + { + result.AppendErrorWithFormat("'%s' takes exactly one executable filename argument.\n", GetCommandName()); + result.SetStatus (eReturnStatusFailed); + } + return result.Succeeded(); + + } + CommandOptions m_options; +}; + +OptionDefinition +CommandObjectCommandsSource::CommandOptions::g_option_table[] = +{ +{ LLDB_OPT_SET_ALL, false, "stop-on-error", 'e', required_argument, NULL, 0, eArgTypeBoolean, "If true, stop executing commands on error."}, +{ LLDB_OPT_SET_ALL, false, "stop-on-continue", 'c', required_argument, NULL, 0, eArgTypeBoolean, "If true, stop executing commands on continue."}, +{ LLDB_OPT_SET_ALL, false, "silent-run", 's', required_argument, NULL, 0, eArgTypeBoolean, "If true don't echo commands while executing."}, +{ 0, false, NULL, 0, 0, NULL, 0, eArgTypeNone, NULL } +}; + +#pragma mark CommandObjectCommandsAlias +//------------------------------------------------------------------------- +// CommandObjectCommandsAlias +//------------------------------------------------------------------------- + +static const char *g_python_command_instructions = "Enter your Python command(s). Type 'DONE' to end.\n" + "You must define a Python function with this signature:\n" + "def my_command_impl(debugger, args, result, internal_dict):"; + + +class CommandObjectCommandsAlias : public CommandObjectRaw +{ + + +public: + CommandObjectCommandsAlias (CommandInterpreter &interpreter) : + CommandObjectRaw (interpreter, + "command alias", + "Allow users to define their own debugger command abbreviations.", + NULL) + { + SetHelpLong( + "'alias' allows the user to create a short-cut or abbreviation for long \n\ + commands, multi-word commands, and commands that take particular options. \n\ + Below are some simple examples of how one might use the 'alias' command: \n\ + \n 'command alias sc script' // Creates the abbreviation 'sc' for the 'script' \n\ + // command. \n\ + 'command alias bp breakpoint' // Creates the abbreviation 'bp' for the 'breakpoint' \n\ + // command. Since breakpoint commands are two-word \n\ + // commands, the user will still need to enter the \n\ + // second word after 'bp', e.g. 'bp enable' or \n\ + // 'bp delete'. \n\ + 'command alias bpl breakpoint list' // Creates the abbreviation 'bpl' for the \n\ + // two-word command 'breakpoint list'. \n\ + \nAn alias can include some options for the command, with the values either \n\ + filled in at the time the alias is created, or specified as positional \n\ + arguments, to be filled in when the alias is invoked. The following example \n\ + shows how to create aliases with options: \n\ + \n\ + 'command alias bfl breakpoint set -f %1 -l %2' \n\ + \nThis creates the abbreviation 'bfl' (for break-file-line), with the -f and -l \n\ + options already part of the alias. So if the user wants to set a breakpoint \n\ + by file and line without explicitly having to use the -f and -l options, the \n\ + user can now use 'bfl' instead. The '%1' and '%2' are positional placeholders \n\ + for the actual arguments that will be passed when the alias command is used. \n\ + The number in the placeholder refers to the position/order the actual value \n\ + occupies when the alias is used. All the occurrences of '%1' in the alias \n\ + will be replaced with the first argument, all the occurrences of '%2' in the \n\ + alias will be replaced with the second argument, and so on. This also allows \n\ + actual arguments to be used multiple times within an alias (see 'process \n\ + launch' example below). \n\ + Note: the positional arguments must substitute as whole words in the resultant\n\ + command, so you can't at present do something like:\n\ + \n\ + command alias bcppfl breakpoint set -f %1.cpp -l %2\n\ + \n\ + to get the file extension \".cpp\" automatically appended. For more complex\n\ + aliasing, use the \"command regex\" command instead.\n\ + \nSo in the 'bfl' case, the actual file value will be \n\ + filled in with the first argument following 'bfl' and the actual line number \n\ + value will be filled in with the second argument. The user would use this \n\ + alias as follows: \n\ + \n (lldb) command alias bfl breakpoint set -f %1 -l %2 \n\ + <... some time later ...> \n\ + (lldb) bfl my-file.c 137 \n\ + \nThis would be the same as if the user had entered \n\ + 'breakpoint set -f my-file.c -l 137'. \n\ + \nAnother example: \n\ + \n (lldb) command alias pltty process launch -s -o %1 -e %1 \n\ + (lldb) pltty /dev/tty0 \n\ + // becomes 'process launch -s -o /dev/tty0 -e /dev/tty0' \n\ + \nIf the user always wanted to pass the same value to a particular option, the \n\ + alias could be defined with that value directly in the alias as a constant, \n\ + rather than using a positional placeholder: \n\ + \n command alias bl3 breakpoint set -f %1 -l 3 // Always sets a breakpoint on line \n\ + // 3 of whatever file is indicated. \n"); + + CommandArgumentEntry arg1; + CommandArgumentEntry arg2; + CommandArgumentEntry arg3; + CommandArgumentData alias_arg; + CommandArgumentData cmd_arg; + CommandArgumentData options_arg; + + // Define the first (and only) variant of this arg. + alias_arg.arg_type = eArgTypeAliasName; + alias_arg.arg_repetition = eArgRepeatPlain; + + // There is only one variant this argument could be; put it into the argument entry. + arg1.push_back (alias_arg); + + // Define the first (and only) variant of this arg. + cmd_arg.arg_type = eArgTypeCommandName; + cmd_arg.arg_repetition = eArgRepeatPlain; + + // There is only one variant this argument could be; put it into the argument entry. + arg2.push_back (cmd_arg); + + // Define the first (and only) variant of this arg. + options_arg.arg_type = eArgTypeAliasOptions; + options_arg.arg_repetition = eArgRepeatOptional; + + // There is only one variant this argument could be; put it into the argument entry. + arg3.push_back (options_arg); + + // Push the data for the first argument into the m_arguments vector. + m_arguments.push_back (arg1); + m_arguments.push_back (arg2); + m_arguments.push_back (arg3); + } + + ~CommandObjectCommandsAlias () + { + } + +protected: + virtual bool + DoExecute (const char *raw_command_line, CommandReturnObject &result) + { + Args args (raw_command_line); + std::string raw_command_string (raw_command_line); + + size_t argc = args.GetArgumentCount(); + + if (argc < 2) + { + result.AppendError ("'alias' requires at least two arguments"); + result.SetStatus (eReturnStatusFailed); + return false; + } + + // Get the alias command. + + const std::string alias_command = args.GetArgumentAtIndex (0); + + // Strip the new alias name off 'raw_command_string' (leave it on args, which gets passed to 'Execute', which + // does the stripping itself. + size_t pos = raw_command_string.find (alias_command); + if (pos == 0) + { + raw_command_string = raw_command_string.substr (alias_command.size()); + pos = raw_command_string.find_first_not_of (' '); + if ((pos != std::string::npos) && (pos > 0)) + raw_command_string = raw_command_string.substr (pos); + } + else + { + result.AppendError ("Error parsing command string. No alias created."); + result.SetStatus (eReturnStatusFailed); + return false; + } + + + // Verify that the command is alias-able. + if (m_interpreter.CommandExists (alias_command.c_str())) + { + result.AppendErrorWithFormat ("'%s' is a permanent debugger command and cannot be redefined.\n", + alias_command.c_str()); + result.SetStatus (eReturnStatusFailed); + return false; + } + + // Get CommandObject that is being aliased. The command name is read from the front of raw_command_string. + // raw_command_string is returned with the name of the command object stripped off the front. + CommandObject *cmd_obj = m_interpreter.GetCommandObjectForCommand (raw_command_string); + + if (!cmd_obj) + { + result.AppendErrorWithFormat ("invalid command given to 'alias'. '%s' does not begin with a valid command." + " No alias created.", raw_command_string.c_str()); + result.SetStatus (eReturnStatusFailed); + return false; + } + else if (!cmd_obj->WantsRawCommandString ()) + { + // Note that args was initialized with the original command, and has not been updated to this point. + // Therefore can we pass it to the version of Execute that does not need/expect raw input in the alias. + return HandleAliasingNormalCommand (args, result); + } + else + { + return HandleAliasingRawCommand (alias_command, raw_command_string, *cmd_obj, result); + } + return result.Succeeded(); + } + + bool + HandleAliasingRawCommand (const std::string &alias_command, std::string &raw_command_string, CommandObject &cmd_obj, CommandReturnObject &result) + { + // Verify & handle any options/arguments passed to the alias command + + OptionArgVectorSP option_arg_vector_sp = OptionArgVectorSP (new OptionArgVector); + OptionArgVector *option_arg_vector = option_arg_vector_sp.get(); + + CommandObjectSP cmd_obj_sp = m_interpreter.GetCommandSPExact (cmd_obj.GetCommandName(), false); + + if (!m_interpreter.ProcessAliasOptionsArgs (cmd_obj_sp, raw_command_string.c_str(), option_arg_vector_sp)) + { + result.AppendError ("Unable to create requested alias.\n"); + result.SetStatus (eReturnStatusFailed); + return false; + } + + // Create the alias + if (m_interpreter.AliasExists (alias_command.c_str()) + || m_interpreter.UserCommandExists (alias_command.c_str())) + { + OptionArgVectorSP temp_option_arg_sp (m_interpreter.GetAliasOptions (alias_command.c_str())); + if (temp_option_arg_sp.get()) + { + if (option_arg_vector->size() == 0) + m_interpreter.RemoveAliasOptions (alias_command.c_str()); + } + result.AppendWarningWithFormat ("Overwriting existing definition for '%s'.\n", + alias_command.c_str()); + } + + if (cmd_obj_sp) + { + m_interpreter.AddAlias (alias_command.c_str(), cmd_obj_sp); + if (option_arg_vector->size() > 0) + m_interpreter.AddOrReplaceAliasOptions (alias_command.c_str(), option_arg_vector_sp); + result.SetStatus (eReturnStatusSuccessFinishNoResult); + } + else + { + result.AppendError ("Unable to create requested alias.\n"); + result.SetStatus (eReturnStatusFailed); + } + return result.Succeeded (); + } + + bool + HandleAliasingNormalCommand (Args& args, CommandReturnObject &result) + { + size_t argc = args.GetArgumentCount(); + + if (argc < 2) + { + result.AppendError ("'alias' requires at least two arguments"); + result.SetStatus (eReturnStatusFailed); + return false; + } + + const std::string alias_command = args.GetArgumentAtIndex(0); + const std::string actual_command = args.GetArgumentAtIndex(1); + + args.Shift(); // Shift the alias command word off the argument vector. + args.Shift(); // Shift the old command word off the argument vector. + + // Verify that the command is alias'able, and get the appropriate command object. + + if (m_interpreter.CommandExists (alias_command.c_str())) + { + result.AppendErrorWithFormat ("'%s' is a permanent debugger command and cannot be redefined.\n", + alias_command.c_str()); + result.SetStatus (eReturnStatusFailed); + } + else + { + CommandObjectSP command_obj_sp(m_interpreter.GetCommandSPExact (actual_command.c_str(), true)); + CommandObjectSP subcommand_obj_sp; + bool use_subcommand = false; + if (command_obj_sp.get()) + { + CommandObject *cmd_obj = command_obj_sp.get(); + CommandObject *sub_cmd_obj = NULL; + OptionArgVectorSP option_arg_vector_sp = OptionArgVectorSP (new OptionArgVector); + OptionArgVector *option_arg_vector = option_arg_vector_sp.get(); + + while (cmd_obj->IsMultiwordObject() && args.GetArgumentCount() > 0) + { + if (argc >= 3) + { + const std::string sub_command = args.GetArgumentAtIndex(0); + assert (sub_command.length() != 0); + subcommand_obj_sp = cmd_obj->GetSubcommandSP (sub_command.c_str()); + if (subcommand_obj_sp.get()) + { + sub_cmd_obj = subcommand_obj_sp.get(); + use_subcommand = true; + args.Shift(); // Shift the sub_command word off the argument vector. + cmd_obj = sub_cmd_obj; + } + else + { + result.AppendErrorWithFormat("'%s' is not a valid sub-command of '%s'. " + "Unable to create alias.\n", + sub_command.c_str(), actual_command.c_str()); + result.SetStatus (eReturnStatusFailed); + return false; + } + } + } + + // Verify & handle any options/arguments passed to the alias command + + if (args.GetArgumentCount () > 0) + { + CommandObjectSP tmp_sp = m_interpreter.GetCommandSPExact (cmd_obj->GetCommandName(), false); + if (use_subcommand) + tmp_sp = m_interpreter.GetCommandSPExact (sub_cmd_obj->GetCommandName(), false); + + std::string args_string; + args.GetCommandString (args_string); + + if (!m_interpreter.ProcessAliasOptionsArgs (tmp_sp, args_string.c_str(), option_arg_vector_sp)) + { + result.AppendError ("Unable to create requested alias.\n"); + result.SetStatus (eReturnStatusFailed); + return false; + } + } + + // Create the alias. + + if (m_interpreter.AliasExists (alias_command.c_str()) + || m_interpreter.UserCommandExists (alias_command.c_str())) + { + OptionArgVectorSP tmp_option_arg_sp (m_interpreter.GetAliasOptions (alias_command.c_str())); + if (tmp_option_arg_sp.get()) + { + if (option_arg_vector->size() == 0) + m_interpreter.RemoveAliasOptions (alias_command.c_str()); + } + result.AppendWarningWithFormat ("Overwriting existing definition for '%s'.\n", + alias_command.c_str()); + } + + if (use_subcommand) + m_interpreter.AddAlias (alias_command.c_str(), subcommand_obj_sp); + else + m_interpreter.AddAlias (alias_command.c_str(), command_obj_sp); + if (option_arg_vector->size() > 0) + m_interpreter.AddOrReplaceAliasOptions (alias_command.c_str(), option_arg_vector_sp); + result.SetStatus (eReturnStatusSuccessFinishNoResult); + } + else + { + result.AppendErrorWithFormat ("'%s' is not an existing command.\n", actual_command.c_str()); + result.SetStatus (eReturnStatusFailed); + return false; + } + } + + return result.Succeeded(); + } + +}; + +#pragma mark CommandObjectCommandsUnalias +//------------------------------------------------------------------------- +// CommandObjectCommandsUnalias +//------------------------------------------------------------------------- + +class CommandObjectCommandsUnalias : public CommandObjectParsed +{ +public: + CommandObjectCommandsUnalias (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "command unalias", + "Allow the user to remove/delete a user-defined command abbreviation.", + NULL) + { + CommandArgumentEntry arg; + CommandArgumentData alias_arg; + + // Define the first (and only) variant of this arg. + alias_arg.arg_type = eArgTypeAliasName; + alias_arg.arg_repetition = eArgRepeatPlain; + + // There is only one variant this argument could be; put it into the argument entry. + arg.push_back (alias_arg); + + // Push the data for the first argument into the m_arguments vector. + m_arguments.push_back (arg); + } + + ~CommandObjectCommandsUnalias() + { + } + +protected: + bool + DoExecute (Args& args, CommandReturnObject &result) + { + CommandObject::CommandMap::iterator pos; + CommandObject *cmd_obj; + + if (args.GetArgumentCount() != 0) + { + const char *command_name = args.GetArgumentAtIndex(0); + cmd_obj = m_interpreter.GetCommandObject(command_name); + if (cmd_obj) + { + if (m_interpreter.CommandExists (command_name)) + { + result.AppendErrorWithFormat ("'%s' is a permanent debugger command and cannot be removed.\n", + command_name); + result.SetStatus (eReturnStatusFailed); + } + else + { + + if (m_interpreter.RemoveAlias (command_name) == false) + { + if (m_interpreter.AliasExists (command_name)) + result.AppendErrorWithFormat ("Error occurred while attempting to unalias '%s'.\n", + command_name); + else + result.AppendErrorWithFormat ("'%s' is not an existing alias.\n", command_name); + result.SetStatus (eReturnStatusFailed); + } + else + result.SetStatus (eReturnStatusSuccessFinishNoResult); + } + } + else + { + result.AppendErrorWithFormat ("'%s' is not a known command.\nTry 'help' to see a " + "current list of commands.\n", + command_name); + result.SetStatus (eReturnStatusFailed); + } + } + else + { + result.AppendError ("must call 'unalias' with a valid alias"); + result.SetStatus (eReturnStatusFailed); + } + + return result.Succeeded(); + } +}; + +//------------------------------------------------------------------------- +// CommandObjectCommandsAddRegex +//------------------------------------------------------------------------- +#pragma mark CommandObjectCommandsAddRegex + +class CommandObjectCommandsAddRegex : public CommandObjectParsed +{ +public: + CommandObjectCommandsAddRegex (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "command regex", + "Allow the user to create a regular expression command.", + "command regex [s/// ...]"), + m_options (interpreter) + { + SetHelpLong( +"This command allows the user to create powerful regular expression commands\n" +"with substitutions. The regular expressions and substitutions are specified\n" +"using the regular exression substitution format of:\n" +"\n" +" s///\n" +"\n" +" is a regular expression that can use parenthesis to capture regular\n" +"expression input and substitute the captured matches in the output using %1\n" +"for the first match, %2 for the second, and so on.\n" +"\n" +"The regular expressions can all be specified on the command line if more than\n" +"one argument is provided. If just the command name is provided on the command\n" +"line, then the regular expressions and substitutions can be entered on separate\n" +" lines, followed by an empty line to terminate the command definition.\n" +"\n" +"EXAMPLES\n" +"\n" +"The following example will define a regular expression command named 'f' that\n" +"will call 'finish' if there are no arguments, or 'frame select ' if\n" +"a number follows 'f':\n" +"\n" +" (lldb) command regex f s/^$/finish/ 's/([0-9]+)/frame select %1/'\n" +"\n" + ); + } + + ~CommandObjectCommandsAddRegex() + { + } + + +protected: + bool + DoExecute (Args& command, CommandReturnObject &result) + { + const size_t argc = command.GetArgumentCount(); + if (argc == 0) + { + result.AppendError ("usage: 'command regex [s/// s/// ...]'\n"); + result.SetStatus (eReturnStatusFailed); + } + else + { + Error error; + const char *name = command.GetArgumentAtIndex(0); + m_regex_cmd_ap.reset (new CommandObjectRegexCommand (m_interpreter, + name, + m_options.GetHelp (), + m_options.GetSyntax (), + 10)); + + if (argc == 1) + { + InputReaderSP reader_sp (new InputReader(m_interpreter.GetDebugger())); + if (reader_sp) + { + error =reader_sp->Initialize (CommandObjectCommandsAddRegex::InputReaderCallback, + this, // baton + eInputReaderGranularityLine, // token size, to pass to callback function + NULL, // end token + "> ", // prompt + true); // echo input + if (error.Success()) + { + m_interpreter.GetDebugger().PushInputReader (reader_sp); + result.SetStatus (eReturnStatusSuccessFinishNoResult); + return true; + } + } + } + else + { + for (size_t arg_idx = 1; arg_idx < argc; ++arg_idx) + { + llvm::StringRef arg_strref (command.GetArgumentAtIndex(arg_idx)); + error = AppendRegexSubstitution (arg_strref); + if (error.Fail()) + break; + } + + if (error.Success()) + { + AddRegexCommandToInterpreter(); + } + } + if (error.Fail()) + { + result.AppendError (error.AsCString()); + result.SetStatus (eReturnStatusFailed); + } + } + + return result.Succeeded(); + } + + Error + AppendRegexSubstitution (const llvm::StringRef ®ex_sed) + { + Error error; + + if (m_regex_cmd_ap.get() == NULL) + { + error.SetErrorStringWithFormat("invalid regular expression command object for: '%.*s'", + (int)regex_sed.size(), + regex_sed.data()); + return error; + } + + size_t regex_sed_size = regex_sed.size(); + + if (regex_sed_size <= 1) + { + error.SetErrorStringWithFormat("regular expression substitution string is too short: '%.*s'", + (int)regex_sed.size(), + regex_sed.data()); + return error; + } + + if (regex_sed[0] != 's') + { + error.SetErrorStringWithFormat("regular expression substitution string doesn't start with 's': '%.*s'", + (int)regex_sed.size(), + regex_sed.data()); + return error; + } + const size_t first_separator_char_pos = 1; + // use the char that follows 's' as the regex separator character + // so we can have "s///" or "s|||" + const char separator_char = regex_sed[first_separator_char_pos]; + const size_t second_separator_char_pos = regex_sed.find (separator_char, first_separator_char_pos + 1); + + if (second_separator_char_pos == std::string::npos) + { + error.SetErrorStringWithFormat("missing second '%c' separator char after '%.*s'", + separator_char, + (int)(regex_sed.size() - first_separator_char_pos - 1), + regex_sed.data() + (first_separator_char_pos + 1)); + return error; + } + + const size_t third_separator_char_pos = regex_sed.find (separator_char, second_separator_char_pos + 1); + + if (third_separator_char_pos == std::string::npos) + { + error.SetErrorStringWithFormat("missing third '%c' separator char after '%.*s'", + separator_char, + (int)(regex_sed.size() - second_separator_char_pos - 1), + regex_sed.data() + (second_separator_char_pos + 1)); + return error; + } + + if (third_separator_char_pos != regex_sed_size - 1) + { + // Make sure that everything that follows the last regex + // separator char + if (regex_sed.find_first_not_of("\t\n\v\f\r ", third_separator_char_pos + 1) != std::string::npos) + { + error.SetErrorStringWithFormat("extra data found after the '%.*s' regular expression substitution string: '%.*s'", + (int)third_separator_char_pos + 1, + regex_sed.data(), + (int)(regex_sed.size() - third_separator_char_pos - 1), + regex_sed.data() + (third_separator_char_pos + 1)); + return error; + } + + } + else if (first_separator_char_pos + 1 == second_separator_char_pos) + { + error.SetErrorStringWithFormat(" can't be empty in 's%c%c%c' string: '%.*s'", + separator_char, + separator_char, + separator_char, + (int)regex_sed.size(), + regex_sed.data()); + return error; + } + else if (second_separator_char_pos + 1 == third_separator_char_pos) + { + error.SetErrorStringWithFormat(" can't be empty in 's%c%c%c' string: '%.*s'", + separator_char, + separator_char, + separator_char, + (int)regex_sed.size(), + regex_sed.data()); + return error; + } + std::string regex(regex_sed.substr(first_separator_char_pos + 1, second_separator_char_pos - first_separator_char_pos - 1)); + std::string subst(regex_sed.substr(second_separator_char_pos + 1, third_separator_char_pos - second_separator_char_pos - 1)); + m_regex_cmd_ap->AddRegexCommand (regex.c_str(), + subst.c_str()); + return error; + } + + void + AddRegexCommandToInterpreter() + { + if (m_regex_cmd_ap.get()) + { + if (m_regex_cmd_ap->HasRegexEntries()) + { + CommandObjectSP cmd_sp (m_regex_cmd_ap.release()); + m_interpreter.AddCommand(cmd_sp->GetCommandName(), cmd_sp, true); + } + } + } + + void + InputReaderDidCancel() + { + m_regex_cmd_ap.reset(); + } + + static size_t + InputReaderCallback (void *baton, + InputReader &reader, + lldb::InputReaderAction notification, + const char *bytes, + size_t bytes_len) + { + CommandObjectCommandsAddRegex *add_regex_cmd = (CommandObjectCommandsAddRegex *) baton; + bool batch_mode = reader.GetDebugger().GetCommandInterpreter().GetBatchCommandMode(); + + switch (notification) + { + case eInputReaderActivate: + if (!batch_mode) + { + StreamSP out_stream = reader.GetDebugger().GetAsyncOutputStream (); + out_stream->Printf("%s\n", "Enter regular expressions in the form 's///' and terminate with an empty line:"); + out_stream->Flush(); + } + break; + case eInputReaderReactivate: + break; + + case eInputReaderDeactivate: + break; + + case eInputReaderAsynchronousOutputWritten: + break; + + case eInputReaderGotToken: + while (bytes_len > 0 && (bytes[bytes_len-1] == '\r' || bytes[bytes_len-1] == '\n')) + --bytes_len; + if (bytes_len == 0) + reader.SetIsDone(true); + else if (bytes) + { + llvm::StringRef bytes_strref (bytes, bytes_len); + Error error (add_regex_cmd->AppendRegexSubstitution (bytes_strref)); + if (error.Fail()) + { + if (!batch_mode) + { + StreamSP out_stream = reader.GetDebugger().GetAsyncOutputStream(); + out_stream->Printf("error: %s\n", error.AsCString()); + out_stream->Flush(); + } + add_regex_cmd->InputReaderDidCancel (); + reader.SetIsDone (true); + } + } + break; + + case eInputReaderInterrupt: + { + reader.SetIsDone (true); + if (!batch_mode) + { + StreamSP out_stream = reader.GetDebugger().GetAsyncOutputStream(); + out_stream->PutCString("Regular expression command creations was cancelled.\n"); + out_stream->Flush(); + } + add_regex_cmd->InputReaderDidCancel (); + } + break; + + case eInputReaderEndOfFile: + reader.SetIsDone (true); + break; + + case eInputReaderDone: + add_regex_cmd->AddRegexCommandToInterpreter(); + break; + } + + return bytes_len; + } + +private: + std::unique_ptr m_regex_cmd_ap; + + class CommandOptions : public Options + { + public: + + CommandOptions (CommandInterpreter &interpreter) : + Options (interpreter) + { + } + + virtual + ~CommandOptions (){} + + virtual Error + SetOptionValue (uint32_t option_idx, const char *option_arg) + { + Error error; + const int short_option = m_getopt_table[option_idx].val; + + switch (short_option) + { + case 'h': + m_help.assign (option_arg); + break; + case 's': + m_syntax.assign (option_arg); + break; + + default: + error.SetErrorStringWithFormat ("unrecognized option '%c'", short_option); + break; + } + + return error; + } + + void + OptionParsingStarting () + { + m_help.clear(); + m_syntax.clear(); + } + + const OptionDefinition* + GetDefinitions () + { + return g_option_table; + } + + // Options table: Required for subclasses of Options. + + static OptionDefinition g_option_table[]; + + const char * + GetHelp () + { + if (m_help.empty()) + return NULL; + return m_help.c_str(); + } + const char * + GetSyntax () + { + if (m_syntax.empty()) + return NULL; + return m_syntax.c_str(); + } + // Instance variables to hold the values for command options. + protected: + std::string m_help; + std::string m_syntax; + }; + + virtual Options * + GetOptions () + { + return &m_options; + } + + CommandOptions m_options; +}; + +OptionDefinition +CommandObjectCommandsAddRegex::CommandOptions::g_option_table[] = +{ +{ LLDB_OPT_SET_1, false, "help" , 'h', required_argument, NULL, 0, eArgTypeNone, "The help text to display for this command."}, +{ LLDB_OPT_SET_1, false, "syntax", 's', required_argument, NULL, 0, eArgTypeNone, "A syntax string showing the typical usage syntax."}, +{ 0 , false, NULL , 0 , 0 , NULL, 0, eArgTypeNone, NULL } +}; + + +class CommandObjectPythonFunction : public CommandObjectRaw +{ +private: + std::string m_function_name; + ScriptedCommandSynchronicity m_synchro; + bool m_fetched_help_long; + +public: + + CommandObjectPythonFunction (CommandInterpreter &interpreter, + std::string name, + std::string funct, + ScriptedCommandSynchronicity synch) : + CommandObjectRaw (interpreter, + name.c_str(), + (std::string("Run Python function ") + funct).c_str(), + NULL), + m_function_name(funct), + m_synchro(synch), + m_fetched_help_long(false) + { + } + + virtual + ~CommandObjectPythonFunction () + { + } + + virtual bool + IsRemovable () const + { + return true; + } + + const std::string& + GetFunctionName () + { + return m_function_name; + } + + ScriptedCommandSynchronicity + GetSynchronicity () + { + return m_synchro; + } + + virtual const char * + GetHelpLong () + { + if (!m_fetched_help_long) + { + ScriptInterpreter* scripter = m_interpreter.GetScriptInterpreter(); + if (scripter) + { + std::string docstring; + m_fetched_help_long = scripter->GetDocumentationForItem(m_function_name.c_str(),docstring); + if (!docstring.empty()) + SetHelpLong(docstring); + } + } + return CommandObjectRaw::GetHelpLong(); + } + +protected: + virtual bool + DoExecute (const char *raw_command_line, CommandReturnObject &result) + { + ScriptInterpreter* scripter = m_interpreter.GetScriptInterpreter(); + + Error error; + + result.SetStatus(eReturnStatusInvalid); + + if (!scripter || scripter->RunScriptBasedCommand(m_function_name.c_str(), + raw_command_line, + m_synchro, + result, + error) == false) + { + result.AppendError(error.AsCString()); + result.SetStatus(eReturnStatusFailed); + } + else + { + // Don't change the status if the command already set it... + if (result.GetStatus() == eReturnStatusInvalid) + { + if (result.GetOutputData() == NULL || result.GetOutputData()[0] == '\0') + result.SetStatus(eReturnStatusSuccessFinishNoResult); + else + result.SetStatus(eReturnStatusSuccessFinishResult); + } + } + + return result.Succeeded(); + } + +}; + +//------------------------------------------------------------------------- +// CommandObjectCommandsScriptImport +//------------------------------------------------------------------------- + +class CommandObjectCommandsScriptImport : public CommandObjectParsed +{ +public: + CommandObjectCommandsScriptImport (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "command script import", + "Import a scripting module in LLDB.", + NULL), + m_options(interpreter) + { + CommandArgumentEntry arg1; + CommandArgumentData cmd_arg; + + // Define the first (and only) variant of this arg. + cmd_arg.arg_type = eArgTypeFilename; + cmd_arg.arg_repetition = eArgRepeatPlain; + + // There is only one variant this argument could be; put it into the argument entry. + arg1.push_back (cmd_arg); + + // Push the data for the first argument into the m_arguments vector. + m_arguments.push_back (arg1); + } + + ~CommandObjectCommandsScriptImport () + { + } + + virtual int + HandleArgumentCompletion (Args &input, + int &cursor_index, + int &cursor_char_position, + OptionElementVector &opt_element_vector, + int match_start_point, + int max_return_elements, + bool &word_complete, + StringList &matches) + { + std::string completion_str (input.GetArgumentAtIndex(cursor_index)); + completion_str.erase (cursor_char_position); + + CommandCompletions::InvokeCommonCompletionCallbacks (m_interpreter, + CommandCompletions::eDiskFileCompletion, + completion_str.c_str(), + match_start_point, + max_return_elements, + NULL, + word_complete, + matches); + return matches.GetSize(); + } + + virtual Options * + GetOptions () + { + return &m_options; + } + +protected: + + class CommandOptions : public Options + { + public: + + CommandOptions (CommandInterpreter &interpreter) : + Options (interpreter) + { + } + + virtual + ~CommandOptions (){} + + virtual Error + SetOptionValue (uint32_t option_idx, const char *option_arg) + { + Error error; + const int short_option = m_getopt_table[option_idx].val; + + switch (short_option) + { + case 'r': + m_allow_reload = true; + break; + default: + error.SetErrorStringWithFormat ("unrecognized option '%c'", short_option); + break; + } + + return error; + } + + void + OptionParsingStarting () + { + m_allow_reload = true; + } + + const OptionDefinition* + GetDefinitions () + { + return g_option_table; + } + + // Options table: Required for subclasses of Options. + + static OptionDefinition g_option_table[]; + + // Instance variables to hold the values for command options. + + bool m_allow_reload; + }; + + bool + DoExecute (Args& command, CommandReturnObject &result) + { + + if (m_interpreter.GetDebugger().GetScriptLanguage() != lldb::eScriptLanguagePython) + { + result.AppendError ("only scripting language supported for module importing is currently Python"); + result.SetStatus (eReturnStatusFailed); + return false; + } + + size_t argc = command.GetArgumentCount(); + + if (argc != 1) + { + result.AppendError ("'command script import' requires one argument"); + result.SetStatus (eReturnStatusFailed); + return false; + } + + std::string path = command.GetArgumentAtIndex(0); + Error error; + + const bool init_session = true; + // FIXME: this is necessary because CommandObject::CheckRequirements() assumes that + // commands won't ever be recursively invoked, but it's actually possible to craft + // a Python script that does other "command script imports" in __lldb_init_module + // the real fix is to have recursive commands possible with a CommandInvocation object + // separate from the CommandObject itself, so that recursive command invocations + // won't stomp on each other (wrt to execution contents, options, and more) + m_exe_ctx.Clear(); + if (m_interpreter.GetScriptInterpreter()->LoadScriptingModule(path.c_str(), + m_options.m_allow_reload, + init_session, + error)) + { + result.SetStatus (eReturnStatusSuccessFinishNoResult); + } + else + { + result.AppendErrorWithFormat("module importing failed: %s", error.AsCString()); + result.SetStatus (eReturnStatusFailed); + } + + return result.Succeeded(); + } + + CommandOptions m_options; +}; + +OptionDefinition +CommandObjectCommandsScriptImport::CommandOptions::g_option_table[] = +{ + { LLDB_OPT_SET_1, false, "allow-reload", 'r', no_argument, NULL, 0, eArgTypeNone, "Allow the script to be loaded even if it was already loaded before. This argument exists for backwards compatibility, but reloading is always allowed, whether you specify it or not."}, + { 0, false, NULL, 0, 0, NULL, 0, eArgTypeNone, NULL } +}; + + +//------------------------------------------------------------------------- +// CommandObjectCommandsScriptAdd +//------------------------------------------------------------------------- + +class CommandObjectCommandsScriptAdd : public CommandObjectParsed +{ +public: + CommandObjectCommandsScriptAdd(CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "command script add", + "Add a scripted function as an LLDB command.", + NULL), + m_options (interpreter) + { + CommandArgumentEntry arg1; + CommandArgumentData cmd_arg; + + // Define the first (and only) variant of this arg. + cmd_arg.arg_type = eArgTypeCommandName; + cmd_arg.arg_repetition = eArgRepeatPlain; + + // There is only one variant this argument could be; put it into the argument entry. + arg1.push_back (cmd_arg); + + // Push the data for the first argument into the m_arguments vector. + m_arguments.push_back (arg1); + } + + ~CommandObjectCommandsScriptAdd () + { + } + + virtual Options * + GetOptions () + { + return &m_options; + } + +protected: + + class CommandOptions : public Options + { + public: + + CommandOptions (CommandInterpreter &interpreter) : + Options (interpreter) + { + } + + virtual + ~CommandOptions (){} + + virtual Error + SetOptionValue (uint32_t option_idx, const char *option_arg) + { + Error error; + const int short_option = m_getopt_table[option_idx].val; + + switch (short_option) + { + case 'f': + m_funct_name = std::string(option_arg); + break; + case 's': + m_synchronous = (ScriptedCommandSynchronicity) Args::StringToOptionEnum(option_arg, g_option_table[option_idx].enum_values, 0, error); + if (!error.Success()) + error.SetErrorStringWithFormat ("unrecognized value for synchronicity '%s'", option_arg); + break; + default: + error.SetErrorStringWithFormat ("unrecognized option '%c'", short_option); + break; + } + + return error; + } + + void + OptionParsingStarting () + { + m_funct_name = ""; + m_synchronous = eScriptedCommandSynchronicitySynchronous; + } + + const OptionDefinition* + GetDefinitions () + { + return g_option_table; + } + + // Options table: Required for subclasses of Options. + + static OptionDefinition g_option_table[]; + + // Instance variables to hold the values for command options. + + std::string m_funct_name; + ScriptedCommandSynchronicity m_synchronous; + }; + +private: + class PythonAliasReader : public InputReaderEZ + { + private: + CommandInterpreter& m_interpreter; + std::string m_cmd_name; + ScriptedCommandSynchronicity m_synchronous; + StringList m_user_input; + DISALLOW_COPY_AND_ASSIGN (PythonAliasReader); + public: + PythonAliasReader(Debugger& debugger, + CommandInterpreter& interpreter, + std::string cmd_name, + ScriptedCommandSynchronicity synch) : + InputReaderEZ(debugger), + m_interpreter(interpreter), + m_cmd_name(cmd_name), + m_synchronous(synch), + m_user_input() + {} + + virtual + ~PythonAliasReader() + { + } + + virtual void ActivateHandler(HandlerData& data) + { + StreamSP out_stream = data.GetOutStream(); + bool batch_mode = data.GetBatchMode(); + if (!batch_mode) + { + out_stream->Printf ("%s\n", g_python_command_instructions); + if (data.reader.GetPrompt()) + out_stream->Printf ("%s", data.reader.GetPrompt()); + out_stream->Flush(); + } + } + + virtual void ReactivateHandler(HandlerData& data) + { + StreamSP out_stream = data.GetOutStream(); + bool batch_mode = data.GetBatchMode(); + if (data.reader.GetPrompt() && !batch_mode) + { + out_stream->Printf ("%s", data.reader.GetPrompt()); + out_stream->Flush(); + } + } + virtual void GotTokenHandler(HandlerData& data) + { + StreamSP out_stream = data.GetOutStream(); + bool batch_mode = data.GetBatchMode(); + if (data.bytes && data.bytes_len) + { + m_user_input.AppendString(data.bytes, data.bytes_len); + } + if (!data.reader.IsDone() && data.reader.GetPrompt() && !batch_mode) + { + out_stream->Printf ("%s", data.reader.GetPrompt()); + out_stream->Flush(); + } + } + virtual void InterruptHandler(HandlerData& data) + { + StreamSP out_stream = data.GetOutStream(); + bool batch_mode = data.GetBatchMode(); + data.reader.SetIsDone (true); + if (!batch_mode) + { + out_stream->Printf ("Warning: No script attached.\n"); + out_stream->Flush(); + } + } + virtual void EOFHandler(HandlerData& data) + { + data.reader.SetIsDone (true); + } + virtual void DoneHandler(HandlerData& data) + { + StreamSP out_stream = data.GetOutStream(); + + ScriptInterpreter *interpreter = data.reader.GetDebugger().GetCommandInterpreter().GetScriptInterpreter(); + if (!interpreter) + { + out_stream->Printf ("Script interpreter missing: no script attached.\n"); + out_stream->Flush(); + return; + } + std::string funct_name_str; + if (!interpreter->GenerateScriptAliasFunction (m_user_input, + funct_name_str)) + { + out_stream->Printf ("Unable to create function: no script attached.\n"); + out_stream->Flush(); + return; + } + if (funct_name_str.empty()) + { + out_stream->Printf ("Unable to obtain a function name: no script attached.\n"); + out_stream->Flush(); + return; + } + // everything should be fine now, let's add this alias + + CommandObjectSP command_obj_sp(new CommandObjectPythonFunction(m_interpreter, + m_cmd_name, + funct_name_str.c_str(), + m_synchronous)); + + if (!m_interpreter.AddUserCommand(m_cmd_name, command_obj_sp, true)) + { + out_stream->Printf ("Unable to add selected command: no script attached.\n"); + out_stream->Flush(); + return; + } + } + }; + +protected: + bool + DoExecute (Args& command, CommandReturnObject &result) + { + + if (m_interpreter.GetDebugger().GetScriptLanguage() != lldb::eScriptLanguagePython) + { + result.AppendError ("only scripting language supported for scripted commands is currently Python"); + result.SetStatus (eReturnStatusFailed); + return false; + } + + size_t argc = command.GetArgumentCount(); + + if (argc != 1) + { + result.AppendError ("'command script add' requires one argument"); + result.SetStatus (eReturnStatusFailed); + return false; + } + + std::string cmd_name = command.GetArgumentAtIndex(0); + + if (m_options.m_funct_name.empty()) + { + InputReaderSP reader_sp (new PythonAliasReader (m_interpreter.GetDebugger(), + m_interpreter, + cmd_name, + m_options.m_synchronous)); + + if (reader_sp) + { + + InputReaderEZ::InitializationParameters ipr; + + Error err (reader_sp->Initialize (ipr.SetBaton(NULL).SetPrompt(" "))); + if (err.Success()) + { + m_interpreter.GetDebugger().PushInputReader (reader_sp); + result.SetStatus (eReturnStatusSuccessFinishNoResult); + } + else + { + result.AppendError (err.AsCString()); + result.SetStatus (eReturnStatusFailed); + } + } + else + { + result.AppendError("out of memory"); + result.SetStatus (eReturnStatusFailed); + } + } + else + { + CommandObjectSP new_cmd(new CommandObjectPythonFunction(m_interpreter, + cmd_name, + m_options.m_funct_name, + m_options.m_synchronous)); + if (m_interpreter.AddUserCommand(cmd_name, new_cmd, true)) + { + result.SetStatus (eReturnStatusSuccessFinishNoResult); + } + else + { + result.AppendError("cannot add command"); + result.SetStatus (eReturnStatusFailed); + } + } + + return result.Succeeded(); + + } + + CommandOptions m_options; +}; + +static OptionEnumValueElement g_script_synchro_type[] = +{ + { eScriptedCommandSynchronicitySynchronous, "synchronous", "Run synchronous"}, + { eScriptedCommandSynchronicityAsynchronous, "asynchronous", "Run asynchronous"}, + { eScriptedCommandSynchronicityCurrentValue, "current", "Do not alter current setting"}, + { 0, NULL, NULL } +}; + +OptionDefinition +CommandObjectCommandsScriptAdd::CommandOptions::g_option_table[] = +{ + { LLDB_OPT_SET_1, false, "function", 'f', required_argument, NULL, 0, eArgTypePythonFunction, "Name of the Python function to bind to this command name."}, + { LLDB_OPT_SET_1, false, "synchronicity", 's', required_argument, g_script_synchro_type, 0, eArgTypeScriptedCommandSynchronicity, "Set the synchronicity of this command's executions with regard to LLDB event system."}, + { 0, false, NULL, 0, 0, NULL, 0, eArgTypeNone, NULL } +}; + +//------------------------------------------------------------------------- +// CommandObjectCommandsScriptList +//------------------------------------------------------------------------- + +class CommandObjectCommandsScriptList : public CommandObjectParsed +{ +private: + +public: + CommandObjectCommandsScriptList(CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "command script list", + "List defined scripted commands.", + NULL) + { + } + + ~CommandObjectCommandsScriptList () + { + } + + bool + DoExecute (Args& command, CommandReturnObject &result) + { + + m_interpreter.GetHelp(result, + CommandInterpreter::eCommandTypesUserDef); + + result.SetStatus (eReturnStatusSuccessFinishResult); + + return true; + + + } +}; + +//------------------------------------------------------------------------- +// CommandObjectCommandsScriptClear +//------------------------------------------------------------------------- + +class CommandObjectCommandsScriptClear : public CommandObjectParsed +{ +private: + +public: + CommandObjectCommandsScriptClear(CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "command script clear", + "Delete all scripted commands.", + NULL) + { + } + + ~CommandObjectCommandsScriptClear () + { + } + +protected: + bool + DoExecute (Args& command, CommandReturnObject &result) + { + + m_interpreter.RemoveAllUser(); + + result.SetStatus (eReturnStatusSuccessFinishResult); + + return true; + } +}; + +//------------------------------------------------------------------------- +// CommandObjectCommandsScriptDelete +//------------------------------------------------------------------------- + +class CommandObjectCommandsScriptDelete : public CommandObjectParsed +{ +public: + CommandObjectCommandsScriptDelete(CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "command script delete", + "Delete a scripted command.", + NULL) + { + CommandArgumentEntry arg1; + CommandArgumentData cmd_arg; + + // Define the first (and only) variant of this arg. + cmd_arg.arg_type = eArgTypeCommandName; + cmd_arg.arg_repetition = eArgRepeatPlain; + + // There is only one variant this argument could be; put it into the argument entry. + arg1.push_back (cmd_arg); + + // Push the data for the first argument into the m_arguments vector. + m_arguments.push_back (arg1); + } + + ~CommandObjectCommandsScriptDelete () + { + } + +protected: + bool + DoExecute (Args& command, CommandReturnObject &result) + { + + size_t argc = command.GetArgumentCount(); + + if (argc != 1) + { + result.AppendError ("'command script delete' requires one argument"); + result.SetStatus (eReturnStatusFailed); + return false; + } + + const char* cmd_name = command.GetArgumentAtIndex(0); + + if (cmd_name && *cmd_name && m_interpreter.HasUserCommands() && m_interpreter.UserCommandExists(cmd_name)) + { + m_interpreter.RemoveUser(cmd_name); + result.SetStatus (eReturnStatusSuccessFinishResult); + } + else + { + result.AppendErrorWithFormat ("command %s not found", cmd_name); + result.SetStatus (eReturnStatusFailed); + } + + return result.Succeeded(); + + } +}; + +#pragma mark CommandObjectMultiwordCommandsScript + +//------------------------------------------------------------------------- +// CommandObjectMultiwordCommandsScript +//------------------------------------------------------------------------- + +class CommandObjectMultiwordCommandsScript : public CommandObjectMultiword +{ +public: + CommandObjectMultiwordCommandsScript (CommandInterpreter &interpreter) : + CommandObjectMultiword (interpreter, + "command script", + "A set of commands for managing or customizing script commands.", + "command script []") + { + LoadSubCommand ("add", CommandObjectSP (new CommandObjectCommandsScriptAdd (interpreter))); + LoadSubCommand ("delete", CommandObjectSP (new CommandObjectCommandsScriptDelete (interpreter))); + LoadSubCommand ("clear", CommandObjectSP (new CommandObjectCommandsScriptClear (interpreter))); + LoadSubCommand ("list", CommandObjectSP (new CommandObjectCommandsScriptList (interpreter))); + LoadSubCommand ("import", CommandObjectSP (new CommandObjectCommandsScriptImport (interpreter))); + } + + ~CommandObjectMultiwordCommandsScript () + { + } + +}; + + +#pragma mark CommandObjectMultiwordCommands + +//------------------------------------------------------------------------- +// CommandObjectMultiwordCommands +//------------------------------------------------------------------------- + +CommandObjectMultiwordCommands::CommandObjectMultiwordCommands (CommandInterpreter &interpreter) : + CommandObjectMultiword (interpreter, + "command", + "A set of commands for managing or customizing the debugger commands.", + "command []") +{ + LoadSubCommand ("source", CommandObjectSP (new CommandObjectCommandsSource (interpreter))); + LoadSubCommand ("alias", CommandObjectSP (new CommandObjectCommandsAlias (interpreter))); + LoadSubCommand ("unalias", CommandObjectSP (new CommandObjectCommandsUnalias (interpreter))); + LoadSubCommand ("regex", CommandObjectSP (new CommandObjectCommandsAddRegex (interpreter))); + LoadSubCommand ("history", CommandObjectSP (new CommandObjectCommandsHistory (interpreter))); + LoadSubCommand ("script", CommandObjectSP (new CommandObjectMultiwordCommandsScript (interpreter))); +} + +CommandObjectMultiwordCommands::~CommandObjectMultiwordCommands () +{ +} + diff --git a/contrib/llvm/tools/lldb/source/Commands/CommandObjectCommands.h b/contrib/llvm/tools/lldb/source/Commands/CommandObjectCommands.h new file mode 100644 index 00000000000..8a56e8dae6f --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Commands/CommandObjectCommands.h @@ -0,0 +1,40 @@ +//===-- CommandObjectCommands.h -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_CommandObjectCommands_h_ +#define liblldb_CommandObjectCommands_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Interpreter/CommandObject.h" +#include "lldb/Interpreter/CommandObjectMultiword.h" +#include "lldb/Core/STLUtils.h" + +namespace lldb_private { + +//------------------------------------------------------------------------- +// CommandObjectMultiwordCommands +//------------------------------------------------------------------------- + +class CommandObjectMultiwordCommands : public CommandObjectMultiword +{ +public: + + CommandObjectMultiwordCommands (CommandInterpreter &interpreter); + + virtual + ~CommandObjectMultiwordCommands (); + +}; + +} // namespace lldb_private + +#endif // liblldb_CommandObjectCommands_h_ diff --git a/contrib/llvm/tools/lldb/source/Commands/CommandObjectDisassemble.cpp b/contrib/llvm/tools/lldb/source/Commands/CommandObjectDisassemble.cpp new file mode 100644 index 00000000000..0d40fcd7c0b --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Commands/CommandObjectDisassemble.cpp @@ -0,0 +1,574 @@ +//===-- CommandObjectDisassemble.cpp ----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "CommandObjectDisassemble.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/AddressRange.h" +#include "lldb/Core/Disassembler.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/SourceManager.h" +#include "lldb/Interpreter/Args.h" +#include "lldb/Interpreter/CommandCompletions.h" +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Interpreter/CommandReturnObject.h" +#include "lldb/Interpreter/Options.h" +#include "lldb/Symbol/Function.h" +#include "lldb/Symbol/Symbol.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/StackFrame.h" +#include "lldb/Target/Target.h" + +#define DEFAULT_DISASM_BYTE_SIZE 32 +#define DEFAULT_DISASM_NUM_INS 4 + +using namespace lldb; +using namespace lldb_private; + +CommandObjectDisassemble::CommandOptions::CommandOptions (CommandInterpreter &interpreter) : + Options(interpreter), + num_lines_context(0), + num_instructions (0), + func_name(), + current_function (false), + start_addr(), + end_addr (), + at_pc (false), + frame_line (false), + plugin_name (), + flavor_string(), + arch(), + some_location_specified (false), + symbol_containing_addr () +{ + OptionParsingStarting(); +} + +CommandObjectDisassemble::CommandOptions::~CommandOptions () +{ +} + +Error +CommandObjectDisassemble::CommandOptions::SetOptionValue (uint32_t option_idx, const char *option_arg) +{ + Error error; + + const int short_option = m_getopt_table[option_idx].val; + + bool success; + + switch (short_option) + { + case 'm': + show_mixed = true; + break; + + case 'C': + num_lines_context = Args::StringToUInt32(option_arg, 0, 0, &success); + if (!success) + error.SetErrorStringWithFormat ("invalid num context lines string: \"%s\"", option_arg); + break; + + case 'c': + num_instructions = Args::StringToUInt32(option_arg, 0, 0, &success); + if (!success) + error.SetErrorStringWithFormat ("invalid num of instructions string: \"%s\"", option_arg); + break; + + case 'b': + show_bytes = true; + break; + + case 's': + { + ExecutionContext exe_ctx (m_interpreter.GetExecutionContext()); + start_addr = Args::StringToAddress(&exe_ctx, option_arg, LLDB_INVALID_ADDRESS, &error); + if (start_addr != LLDB_INVALID_ADDRESS) + some_location_specified = true; + } + break; + case 'e': + { + ExecutionContext exe_ctx (m_interpreter.GetExecutionContext()); + end_addr = Args::StringToAddress(&exe_ctx, option_arg, LLDB_INVALID_ADDRESS, &error); + if (end_addr != LLDB_INVALID_ADDRESS) + some_location_specified = true; + } + break; + case 'n': + func_name.assign (option_arg); + some_location_specified = true; + break; + + case 'p': + at_pc = true; + some_location_specified = true; + break; + + case 'l': + frame_line = true; + // Disassemble the current source line kind of implies showing mixed + // source code context. + show_mixed = true; + some_location_specified = true; + break; + + case 'P': + plugin_name.assign (option_arg); + break; + + case 'F': + { + Target *target = m_interpreter.GetExecutionContext().GetTargetPtr(); + if (target->GetArchitecture().GetTriple().getArch() == llvm::Triple::x86 + || target->GetArchitecture().GetTriple().getArch() == llvm::Triple::x86_64) + { + flavor_string.assign (option_arg); + } + else + error.SetErrorStringWithFormat("Disassembler flavors are currently only supported for x86 and x86_64 targets."); + break; + } + case 'r': + raw = true; + break; + + case 'f': + current_function = true; + some_location_specified = true; + break; + + case 'A': + if (!arch.SetTriple (option_arg, m_interpreter.GetPlatform (true).get())) + arch.SetTriple (option_arg); + break; + + case 'a': + { + ExecutionContext exe_ctx (m_interpreter.GetExecutionContext()); + symbol_containing_addr = Args::StringToAddress(&exe_ctx, option_arg, LLDB_INVALID_ADDRESS, &error); + if (symbol_containing_addr != LLDB_INVALID_ADDRESS) + { + some_location_specified = true; + } + } + break; + + default: + error.SetErrorStringWithFormat("unrecognized short option '%c'", short_option); + break; + } + + return error; +} + +void +CommandObjectDisassemble::CommandOptions::OptionParsingStarting () +{ + show_mixed = false; + show_bytes = false; + num_lines_context = 0; + num_instructions = 0; + func_name.clear(); + current_function = false; + at_pc = false; + frame_line = false; + start_addr = LLDB_INVALID_ADDRESS; + end_addr = LLDB_INVALID_ADDRESS; + symbol_containing_addr = LLDB_INVALID_ADDRESS; + raw = false; + plugin_name.clear(); + + Target *target = m_interpreter.GetExecutionContext().GetTargetPtr(); + + // This is a hack till we get the ability to specify features based on architecture. For now GetDisassemblyFlavor + // is really only valid for x86 (and for the llvm assembler plugin, but I'm papering over that since that is the + // only disassembler plugin we have... + if (target) + { + if (target->GetArchitecture().GetTriple().getArch() == llvm::Triple::x86 + || target->GetArchitecture().GetTriple().getArch() == llvm::Triple::x86_64) + { + flavor_string.assign(target->GetDisassemblyFlavor()); + } + else + flavor_string.assign ("default"); + + } + else + flavor_string.assign("default"); + + arch.Clear(); + some_location_specified = false; +} + +Error +CommandObjectDisassemble::CommandOptions::OptionParsingFinished () +{ + if (!some_location_specified) + current_function = true; + return Error(); + +} + +const OptionDefinition* +CommandObjectDisassemble::CommandOptions::GetDefinitions () +{ + return g_option_table; +} + +OptionDefinition +CommandObjectDisassemble::CommandOptions::g_option_table[] = +{ +{ LLDB_OPT_SET_ALL, false, "bytes" , 'b', no_argument , NULL, 0, eArgTypeNone, "Show opcode bytes when disassembling."}, +{ LLDB_OPT_SET_ALL, false, "context" , 'C', required_argument , NULL, 0, eArgTypeNumLines, "Number of context lines of source to show."}, +{ LLDB_OPT_SET_ALL, false, "mixed" , 'm', no_argument , NULL, 0, eArgTypeNone, "Enable mixed source and assembly display."}, +{ LLDB_OPT_SET_ALL, false, "raw" , 'r', no_argument , NULL, 0, eArgTypeNone, "Print raw disassembly with no symbol information."}, +{ LLDB_OPT_SET_ALL, false, "plugin" , 'P', required_argument , NULL, 0, eArgTypePlugin, "Name of the disassembler plugin you want to use."}, +{ LLDB_OPT_SET_ALL, false, "flavor" , 'F', required_argument , NULL, 0, eArgTypeDisassemblyFlavor, "Name of the disassembly flavor you want to use. " + "Currently the only valid options are default, and for Intel" + " architectures, att and intel."}, +{ LLDB_OPT_SET_ALL, false, "arch" , 'A', required_argument , NULL, 0, eArgTypeArchitecture,"Specify the architecture to use from cross disassembly."}, +{ LLDB_OPT_SET_1 | + LLDB_OPT_SET_2 , true , "start-address", 's', required_argument , NULL, 0, eArgTypeAddressOrExpression,"Address at which to start disassembling."}, +{ LLDB_OPT_SET_1 , false, "end-address" , 'e', required_argument , NULL, 0, eArgTypeAddressOrExpression, "Address at which to end disassembling."}, +{ LLDB_OPT_SET_2 | + LLDB_OPT_SET_3 | + LLDB_OPT_SET_4 | + LLDB_OPT_SET_5 , false, "count" , 'c', required_argument , NULL, 0, eArgTypeNumLines, "Number of instructions to display."}, +{ LLDB_OPT_SET_3 , false, "name" , 'n', required_argument , NULL, CommandCompletions::eSymbolCompletion, eArgTypeFunctionName, + "Disassemble entire contents of the given function name."}, +{ LLDB_OPT_SET_4 , false, "frame" , 'f', no_argument , NULL, 0, eArgTypeNone, "Disassemble from the start of the current frame's function."}, +{ LLDB_OPT_SET_5 , false, "pc" , 'p', no_argument , NULL, 0, eArgTypeNone, "Disassemble around the current pc."}, +{ LLDB_OPT_SET_6 , false, "line" , 'l', no_argument , NULL, 0, eArgTypeNone, "Disassemble the current frame's current source line instructions if there debug line table information, else disasemble around the pc."}, +{ LLDB_OPT_SET_7 , false, "address" , 'a', required_argument , NULL, 0, eArgTypeAddressOrExpression, "Disassemble function containing this address."}, +{ 0 , false, NULL , 0, 0 , NULL, 0, eArgTypeNone, NULL } +}; + + + +//------------------------------------------------------------------------- +// CommandObjectDisassemble +//------------------------------------------------------------------------- + +CommandObjectDisassemble::CommandObjectDisassemble (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "disassemble", + "Disassemble bytes in the current function, or elsewhere in the executable program as specified by the user.", + "disassemble []"), + m_options (interpreter) +{ +} + +CommandObjectDisassemble::~CommandObjectDisassemble() +{ +} + +bool +CommandObjectDisassemble::DoExecute (Args& command, CommandReturnObject &result) +{ + Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get(); + if (target == NULL) + { + result.AppendError ("invalid target, create a debug target using the 'target create' command"); + result.SetStatus (eReturnStatusFailed); + return false; + } + if (!m_options.arch.IsValid()) + m_options.arch = target->GetArchitecture(); + + if (!m_options.arch.IsValid()) + { + result.AppendError ("use the --arch option or set the target architecure to disassemble"); + result.SetStatus (eReturnStatusFailed); + return false; + } + + const char *plugin_name = m_options.GetPluginName (); + const char *flavor_string = m_options.GetFlavorString(); + + DisassemblerSP disassembler = Disassembler::FindPlugin(m_options.arch, flavor_string, plugin_name); + + if (!disassembler) + { + if (plugin_name) + { + result.AppendErrorWithFormat ("Unable to find Disassembler plug-in named '%s' that supports the '%s' architecture.\n", + plugin_name, + m_options.arch.GetArchitectureName()); + } + else + result.AppendErrorWithFormat ("Unable to find Disassembler plug-in for the '%s' architecture.\n", + m_options.arch.GetArchitectureName()); + result.SetStatus (eReturnStatusFailed); + return false; + } + else if (flavor_string != NULL && !disassembler->FlavorValidForArchSpec(m_options.arch, flavor_string)) + result.AppendWarningWithFormat("invalid disassembler flavor \"%s\", using default.\n", flavor_string); + + result.SetStatus (eReturnStatusSuccessFinishResult); + + if (command.GetArgumentCount() != 0) + { + result.AppendErrorWithFormat ("\"disassemble\" arguments are specified as options.\n"); + GetOptions()->GenerateOptionUsage (result.GetErrorStream(), this); + result.SetStatus (eReturnStatusFailed); + return false; + } + + if (m_options.show_mixed && m_options.num_lines_context == 0) + m_options.num_lines_context = 1; + + // Always show the PC in the disassembly + uint32_t options = Disassembler::eOptionMarkPCAddress; + + // Mark the source line for the current PC only if we are doing mixed source and assembly + if (m_options.show_mixed) + options |= Disassembler::eOptionMarkPCSourceLine; + + if (m_options.show_bytes) + options |= Disassembler::eOptionShowBytes; + + if (m_options.raw) + options |= Disassembler::eOptionRawOuput; + + if (!m_options.func_name.empty()) + { + ConstString name(m_options.func_name.c_str()); + + if (Disassembler::Disassemble (m_interpreter.GetDebugger(), + m_options.arch, + plugin_name, + flavor_string, + m_exe_ctx, + name, + NULL, // Module * + m_options.num_instructions, + m_options.show_mixed ? m_options.num_lines_context : 0, + options, + result.GetOutputStream())) + { + result.SetStatus (eReturnStatusSuccessFinishResult); + } + else + { + result.AppendErrorWithFormat ("Unable to find symbol with name '%s'.\n", name.GetCString()); + result.SetStatus (eReturnStatusFailed); + } + } + else + { + AddressRange range; + StackFrame *frame = m_exe_ctx.GetFramePtr(); + if (m_options.frame_line) + { + if (frame == NULL) + { + result.AppendError ("Cannot disassemble around the current line without a selected frame.\n"); + result.SetStatus (eReturnStatusFailed); + return false; + } + LineEntry pc_line_entry (frame->GetSymbolContext(eSymbolContextLineEntry).line_entry); + if (pc_line_entry.IsValid()) + { + range = pc_line_entry.range; + } + else + { + m_options.at_pc = true; // No line entry, so just disassemble around the current pc + m_options.show_mixed = false; + } + } + else if (m_options.current_function) + { + if (frame == NULL) + { + result.AppendError ("Cannot disassemble around the current function without a selected frame.\n"); + result.SetStatus (eReturnStatusFailed); + return false; + } + Symbol *symbol = frame->GetSymbolContext(eSymbolContextSymbol).symbol; + if (symbol) + { + range.GetBaseAddress() = symbol->GetAddress(); + range.SetByteSize(symbol->GetByteSize()); + } + } + + // Did the "m_options.frame_line" find a valid range already? If so + // skip the rest... + if (range.GetByteSize() == 0) + { + if (m_options.at_pc) + { + if (frame == NULL) + { + result.AppendError ("Cannot disassemble around the current PC without a selected frame.\n"); + result.SetStatus (eReturnStatusFailed); + return false; + } + range.GetBaseAddress() = frame->GetFrameCodeAddress(); + if (m_options.num_instructions == 0) + { + // Disassembling at the PC always disassembles some number of instructions (not the whole function). + m_options.num_instructions = DEFAULT_DISASM_NUM_INS; + } + } + else + { + range.GetBaseAddress().SetOffset (m_options.start_addr); + if (range.GetBaseAddress().IsValid()) + { + if (m_options.end_addr != LLDB_INVALID_ADDRESS) + { + if (m_options.end_addr <= m_options.start_addr) + { + result.AppendErrorWithFormat ("End address before start address.\n"); + result.SetStatus (eReturnStatusFailed); + return false; + } + range.SetByteSize (m_options.end_addr - m_options.start_addr); + } + } + else + { + if (m_options.symbol_containing_addr != LLDB_INVALID_ADDRESS + && target + && !target->GetSectionLoadList().IsEmpty()) + { + bool failed = false; + Address symbol_containing_address; + if (target->GetSectionLoadList().ResolveLoadAddress (m_options.symbol_containing_addr, symbol_containing_address)) + { + ModuleSP module_sp (symbol_containing_address.GetModule()); + SymbolContext sc; + module_sp->ResolveSymbolContextForAddress (symbol_containing_address, eSymbolContextEverything, sc); + if (sc.function || sc.symbol) + { + sc.GetAddressRange (eSymbolContextFunction | eSymbolContextSymbol, 0, false, range); + } + else + { + failed = true; + } + } + else + { + failed = true; + } + if (failed) + { + result.AppendErrorWithFormat ("Could not find function bounds for address 0x%" PRIx64 "\n", m_options.symbol_containing_addr); + result.SetStatus (eReturnStatusFailed); + return false; + } + } + } + } + } + + if (m_options.num_instructions != 0) + { + if (!range.GetBaseAddress().IsValid()) + { + // The default action is to disassemble the current frame function. + if (frame) + { + SymbolContext sc(frame->GetSymbolContext(eSymbolContextFunction | eSymbolContextSymbol)); + if (sc.function) + range.GetBaseAddress() = sc.function->GetAddressRange().GetBaseAddress(); + else if (sc.symbol && sc.symbol->ValueIsAddress()) + range.GetBaseAddress() = sc.symbol->GetAddress(); + else + range.GetBaseAddress() = frame->GetFrameCodeAddress(); + } + + if (!range.GetBaseAddress().IsValid()) + { + result.AppendError ("invalid frame"); + result.SetStatus (eReturnStatusFailed); + return false; + } + } + + if (Disassembler::Disassemble (m_interpreter.GetDebugger(), + m_options.arch, + plugin_name, + flavor_string, + m_exe_ctx, + range.GetBaseAddress(), + m_options.num_instructions, + m_options.show_mixed ? m_options.num_lines_context : 0, + options, + result.GetOutputStream())) + { + result.SetStatus (eReturnStatusSuccessFinishResult); + } + else + { + result.AppendErrorWithFormat ("Failed to disassemble memory at 0x%8.8" PRIx64 ".\n", m_options.start_addr); + result.SetStatus (eReturnStatusFailed); + } + } + else + { + if (!range.GetBaseAddress().IsValid()) + { + // The default action is to disassemble the current frame function. + if (frame) + { + SymbolContext sc(frame->GetSymbolContext(eSymbolContextFunction | eSymbolContextSymbol)); + if (sc.function) + range = sc.function->GetAddressRange(); + else if (sc.symbol && sc.symbol->ValueIsAddress()) + { + range.GetBaseAddress() = sc.symbol->GetAddress(); + range.SetByteSize (sc.symbol->GetByteSize()); + } + else + range.GetBaseAddress() = frame->GetFrameCodeAddress(); + } + else + { + result.AppendError ("invalid frame"); + result.SetStatus (eReturnStatusFailed); + return false; + } + } + if (range.GetByteSize() == 0) + range.SetByteSize(DEFAULT_DISASM_BYTE_SIZE); + + if (Disassembler::Disassemble (m_interpreter.GetDebugger(), + m_options.arch, + plugin_name, + flavor_string, + m_exe_ctx, + range, + m_options.num_instructions, + m_options.show_mixed ? m_options.num_lines_context : 0, + options, + result.GetOutputStream())) + { + result.SetStatus (eReturnStatusSuccessFinishResult); + } + else + { + result.AppendErrorWithFormat ("Failed to disassemble memory at 0x%8.8" PRIx64 ".\n", m_options.start_addr); + result.SetStatus (eReturnStatusFailed); + } + } + } + + return result.Succeeded(); +} diff --git a/contrib/llvm/tools/lldb/source/Commands/CommandObjectDisassemble.h b/contrib/llvm/tools/lldb/source/Commands/CommandObjectDisassemble.h new file mode 100644 index 00000000000..7a7509858b9 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Commands/CommandObjectDisassemble.h @@ -0,0 +1,110 @@ +//===-- CommandObjectDisassemble.h ------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_CommandObjectDisassemble_h_ +#define liblldb_CommandObjectDisassemble_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/ArchSpec.h" +#include "lldb/Interpreter/CommandObject.h" +#include "lldb/Interpreter/Options.h" + +namespace lldb_private { + +//------------------------------------------------------------------------- +// CommandObjectDisassemble +//------------------------------------------------------------------------- + +class CommandObjectDisassemble : public CommandObjectParsed +{ +public: + class CommandOptions : public Options + { + public: + + CommandOptions (CommandInterpreter &interpreter); + + virtual + ~CommandOptions (); + + virtual Error + SetOptionValue (uint32_t option_idx, const char *option_arg); + + void + OptionParsingStarting (); + + const OptionDefinition* + GetDefinitions (); + + const char * + GetPluginName () + { + if (plugin_name.empty()) + return NULL; + return plugin_name.c_str(); + } + + const char * + GetFlavorString () + { + if (flavor_string.empty() || flavor_string == "default") + return NULL; + return flavor_string.c_str(); + } + + virtual Error + OptionParsingFinished (); + + bool show_mixed; // Show mixed source/assembly + bool show_bytes; + uint32_t num_lines_context; + uint32_t num_instructions; + bool raw; + std::string func_name; + bool current_function; + lldb::addr_t start_addr; + lldb::addr_t end_addr; + bool at_pc; + bool frame_line; + std::string plugin_name; + std::string flavor_string; + ArchSpec arch; + bool some_location_specified; // If no location was specified, we'll select "at_pc". This should be set + // in SetOptionValue if anything the selects a location is set. + lldb::addr_t symbol_containing_addr; + static OptionDefinition g_option_table[]; + }; + + CommandObjectDisassemble (CommandInterpreter &interpreter); + + virtual + ~CommandObjectDisassemble (); + + virtual + Options * + GetOptions () + { + return &m_options; + } + +protected: + virtual bool + DoExecute (Args& command, + CommandReturnObject &result); + + CommandOptions m_options; + +}; + +} // namespace lldb_private + +#endif // liblldb_CommandObjectDisassemble_h_ diff --git a/contrib/llvm/tools/lldb/source/Commands/CommandObjectExpression.cpp b/contrib/llvm/tools/lldb/source/Commands/CommandObjectExpression.cpp new file mode 100644 index 00000000000..da472d17331 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Commands/CommandObjectExpression.cpp @@ -0,0 +1,505 @@ +//===-- CommandObjectExpression.cpp -----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "CommandObjectExpression.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Interpreter/Args.h" +#include "lldb/Core/Value.h" +#include "lldb/Core/InputReader.h" +#include "lldb/Core/ValueObjectVariable.h" +#include "lldb/Expression/ClangExpressionVariable.h" +#include "lldb/Expression/ClangUserExpression.h" +#include "lldb/Expression/ClangFunction.h" +#include "lldb/Expression/DWARFExpression.h" +#include "lldb/Host/Host.h" +#include "lldb/Core/Debugger.h" +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Interpreter/CommandReturnObject.h" +#include "lldb/Target/ObjCLanguageRuntime.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Symbol/Variable.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/StackFrame.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" +#include "llvm/ADT/StringRef.h" + +using namespace lldb; +using namespace lldb_private; + +CommandObjectExpression::CommandOptions::CommandOptions () : + OptionGroup() +{ +} + + +CommandObjectExpression::CommandOptions::~CommandOptions () +{ +} + +OptionDefinition +CommandObjectExpression::CommandOptions::g_option_table[] = +{ + { LLDB_OPT_SET_1 | LLDB_OPT_SET_2, false, "all-threads", 'a', required_argument, NULL, 0, eArgTypeBoolean, "Should we run all threads if the execution doesn't complete on one thread."}, + { LLDB_OPT_SET_1 | LLDB_OPT_SET_2, false, "ignore-breakpoints", 'i', required_argument, NULL, 0, eArgTypeBoolean, "Ignore breakpoint hits while running expressions"}, + { LLDB_OPT_SET_1 | LLDB_OPT_SET_2, false, "timeout", 't', required_argument, NULL, 0, eArgTypeUnsignedInteger, "Timeout value (in microseconds) for running the expression."}, + { LLDB_OPT_SET_1 | LLDB_OPT_SET_2, false, "unwind-on-error", 'u', required_argument, NULL, 0, eArgTypeBoolean, "Clean up program state if the expression causes a crash, or raises a signal. Note, unlike gdb hitting a breakpoint is controlled by another option (-i)."}, +}; + + +uint32_t +CommandObjectExpression::CommandOptions::GetNumDefinitions () +{ + return sizeof(g_option_table)/sizeof(OptionDefinition); +} + +Error +CommandObjectExpression::CommandOptions::SetOptionValue (CommandInterpreter &interpreter, + uint32_t option_idx, + const char *option_arg) +{ + Error error; + + const int short_option = g_option_table[option_idx].short_option; + + switch (short_option) + { + //case 'l': + //if (language.SetLanguageFromCString (option_arg) == false) + //{ + // error.SetErrorStringWithFormat("invalid language option argument '%s'", option_arg); + //} + //break; + + case 'a': + { + bool success; + bool result; + result = Args::StringToBoolean(option_arg, true, &success); + if (!success) + error.SetErrorStringWithFormat("invalid all-threads value setting: \"%s\"", option_arg); + else + try_all_threads = result; + } + break; + + case 'i': + { + bool success; + bool tmp_value = Args::StringToBoolean(option_arg, true, &success); + if (success) + ignore_breakpoints = tmp_value; + else + error.SetErrorStringWithFormat("could not convert \"%s\" to a boolean value.", option_arg); + break; + } + case 't': + { + bool success; + uint32_t result; + result = Args::StringToUInt32(option_arg, 0, 0, &success); + if (success) + timeout = result; + else + error.SetErrorStringWithFormat ("invalid timeout setting \"%s\"", option_arg); + } + break; + + case 'u': + { + bool success; + bool tmp_value = Args::StringToBoolean(option_arg, true, &success); + if (success) + unwind_on_error = tmp_value; + else + error.SetErrorStringWithFormat("could not convert \"%s\" to a boolean value.", option_arg); + break; + } + default: + error.SetErrorStringWithFormat("invalid short option character '%c'", short_option); + break; + } + + return error; +} + +void +CommandObjectExpression::CommandOptions::OptionParsingStarting (CommandInterpreter &interpreter) +{ + Process *process = interpreter.GetExecutionContext().GetProcessPtr(); + if (process != NULL) + { + ignore_breakpoints = process->GetIgnoreBreakpointsInExpressions(); + unwind_on_error = process->GetUnwindOnErrorInExpressions(); + } + else + { + ignore_breakpoints = false; + unwind_on_error = true; + } + + show_summary = true; + try_all_threads = true; + timeout = 0; +} + +const OptionDefinition* +CommandObjectExpression::CommandOptions::GetDefinitions () +{ + return g_option_table; +} + +CommandObjectExpression::CommandObjectExpression (CommandInterpreter &interpreter) : + CommandObjectRaw (interpreter, + "expression", + "Evaluate a C/ObjC/C++ expression in the current program context, using user defined variables and variables currently in scope.", + NULL, + eFlagProcessMustBePaused | eFlagTryTargetAPILock), + m_option_group (interpreter), + m_format_options (eFormatDefault), + m_command_options (), + m_expr_line_count (0), + m_expr_lines () +{ + SetHelpLong( +"Timeouts:\n\ + If the expression can be evaluated statically (without runnning code) then it will be.\n\ + Otherwise, by default the expression will run on the current thread with a short timeout:\n\ + currently .25 seconds. If it doesn't return in that time, the evaluation will be interrupted\n\ + and resumed with all threads running. You can use the -a option to disable retrying on all\n\ + threads. You can use the -t option to set a shorter timeout.\n\ +\n\ +User defined variables:\n\ + You can define your own variables for convenience or to be used in subsequent expressions.\n\ + You define them the same way you would define variables in C. If the first character of \n\ + your user defined variable is a $, then the variable's value will be available in future\n\ + expressions, otherwise it will just be available in the current expression.\n\ +\n\ +Examples: \n\ +\n\ + expr my_struct->a = my_array[3] \n\ + expr -f bin -- (index * 8) + 5 \n\ + expr unsigned int $foo = 5\n\ + expr char c[] = \"foo\"; c[0]\n"); + + CommandArgumentEntry arg; + CommandArgumentData expression_arg; + + // Define the first (and only) variant of this arg. + expression_arg.arg_type = eArgTypeExpression; + expression_arg.arg_repetition = eArgRepeatPlain; + + // There is only one variant this argument could be; put it into the argument entry. + arg.push_back (expression_arg); + + // Push the data for the first argument into the m_arguments vector. + m_arguments.push_back (arg); + + // Add the "--format" and "--gdb-format" + m_option_group.Append (&m_format_options, OptionGroupFormat::OPTION_GROUP_FORMAT | OptionGroupFormat::OPTION_GROUP_GDB_FMT, LLDB_OPT_SET_1); + m_option_group.Append (&m_command_options); + m_option_group.Append (&m_varobj_options, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1 | LLDB_OPT_SET_2); + m_option_group.Finalize(); +} + +CommandObjectExpression::~CommandObjectExpression () +{ +} + +Options * +CommandObjectExpression::GetOptions () +{ + return &m_option_group; +} + +size_t +CommandObjectExpression::MultiLineExpressionCallback +( + void *baton, + InputReader &reader, + lldb::InputReaderAction notification, + const char *bytes, + size_t bytes_len +) +{ + CommandObjectExpression *cmd_object_expr = (CommandObjectExpression *) baton; + bool batch_mode = reader.GetDebugger().GetCommandInterpreter().GetBatchCommandMode(); + + switch (notification) + { + case eInputReaderActivate: + if (!batch_mode) + { + StreamSP async_strm_sp(reader.GetDebugger().GetAsyncOutputStream()); + if (async_strm_sp) + { + async_strm_sp->PutCString("Enter expressions, then terminate with an empty line to evaluate:\n"); + async_strm_sp->Flush(); + } + } + // Fall through + case eInputReaderReactivate: + break; + + case eInputReaderDeactivate: + break; + + case eInputReaderAsynchronousOutputWritten: + break; + + case eInputReaderGotToken: + ++cmd_object_expr->m_expr_line_count; + if (bytes && bytes_len) + { + cmd_object_expr->m_expr_lines.append (bytes, bytes_len + 1); + } + + if (bytes_len == 0) + reader.SetIsDone(true); + break; + + case eInputReaderInterrupt: + cmd_object_expr->m_expr_lines.clear(); + reader.SetIsDone (true); + if (!batch_mode) + { + StreamSP async_strm_sp (reader.GetDebugger().GetAsyncOutputStream()); + if (async_strm_sp) + { + async_strm_sp->PutCString("Expression evaluation cancelled.\n"); + async_strm_sp->Flush(); + } + } + break; + + case eInputReaderEndOfFile: + reader.SetIsDone (true); + break; + + case eInputReaderDone: + if (cmd_object_expr->m_expr_lines.size() > 0) + { + StreamSP output_stream = reader.GetDebugger().GetAsyncOutputStream(); + StreamSP error_stream = reader.GetDebugger().GetAsyncErrorStream(); + cmd_object_expr->EvaluateExpression (cmd_object_expr->m_expr_lines.c_str(), + output_stream.get(), + error_stream.get()); + output_stream->Flush(); + error_stream->Flush(); + } + break; + } + + return bytes_len; +} + +bool +CommandObjectExpression::EvaluateExpression +( + const char *expr, + Stream *output_stream, + Stream *error_stream, + CommandReturnObject *result +) +{ + // Don't use m_exe_ctx as this might be called asynchronously + // after the command object DoExecute has finished when doing + // multi-line expression that use an input reader... + ExecutionContext exe_ctx (m_interpreter.GetExecutionContext()); + + Target *target = exe_ctx.GetTargetPtr(); + + if (!target) + target = Host::GetDummyTarget(m_interpreter.GetDebugger()).get(); + + if (target) + { + lldb::ValueObjectSP result_valobj_sp; + + ExecutionResults exe_results; + + bool keep_in_memory = true; + + EvaluateExpressionOptions options; + options.SetCoerceToId(m_varobj_options.use_objc) + .SetUnwindOnError(m_command_options.unwind_on_error) + .SetIgnoreBreakpoints (m_command_options.ignore_breakpoints) + .SetKeepInMemory(keep_in_memory) + .SetUseDynamic(m_varobj_options.use_dynamic) + .SetRunOthers(m_command_options.try_all_threads) + .SetTimeoutUsec(m_command_options.timeout); + + exe_results = target->EvaluateExpression (expr, + exe_ctx.GetFramePtr(), + result_valobj_sp, + options); + + if (result_valobj_sp) + { + Format format = m_format_options.GetFormat(); + + if (result_valobj_sp->GetError().Success()) + { + if (format != eFormatVoid) + { + if (format != eFormatDefault) + result_valobj_sp->SetFormat (format); + + ValueObject::DumpValueObjectOptions options(m_varobj_options.GetAsDumpOptions(true,format)); + + ValueObject::DumpValueObject (*(output_stream), + result_valobj_sp.get(), // Variable object to dump + options); + if (result) + result->SetStatus (eReturnStatusSuccessFinishResult); + } + } + else + { + if (result_valobj_sp->GetError().GetError() == ClangUserExpression::kNoResult) + { + if (format != eFormatVoid && m_interpreter.GetDebugger().GetNotifyVoid()) + { + error_stream->PutCString("(void)\n"); + } + + if (result) + result->SetStatus (eReturnStatusSuccessFinishResult); + } + else + { + const char *error_cstr = result_valobj_sp->GetError().AsCString(); + if (error_cstr && error_cstr[0]) + { + const size_t error_cstr_len = strlen (error_cstr); + const bool ends_with_newline = error_cstr[error_cstr_len - 1] == '\n'; + if (strstr(error_cstr, "error:") != error_cstr) + error_stream->PutCString ("error: "); + error_stream->Write(error_cstr, error_cstr_len); + if (!ends_with_newline) + error_stream->EOL(); + } + else + { + error_stream->PutCString ("error: unknown error\n"); + } + + if (result) + result->SetStatus (eReturnStatusFailed); + } + } + } + } + else + { + error_stream->Printf ("error: invalid execution context for expression\n"); + return false; + } + + return true; +} + +bool +CommandObjectExpression::DoExecute +( + const char *command, + CommandReturnObject &result +) +{ + m_option_group.NotifyOptionParsingStarting(); + + const char * expr = NULL; + + if (command[0] == '\0') + { + m_expr_lines.clear(); + m_expr_line_count = 0; + + InputReaderSP reader_sp (new InputReader(m_interpreter.GetDebugger())); + if (reader_sp) + { + Error err (reader_sp->Initialize (CommandObjectExpression::MultiLineExpressionCallback, + this, // baton + eInputReaderGranularityLine, // token size, to pass to callback function + NULL, // end token + NULL, // prompt + true)); // echo input + if (err.Success()) + { + m_interpreter.GetDebugger().PushInputReader (reader_sp); + result.SetStatus (eReturnStatusSuccessFinishNoResult); + } + else + { + result.AppendError (err.AsCString()); + result.SetStatus (eReturnStatusFailed); + } + } + else + { + result.AppendError("out of memory"); + result.SetStatus (eReturnStatusFailed); + } + return result.Succeeded(); + } + + if (command[0] == '-') + { + // We have some options and these options MUST end with --. + const char *end_options = NULL; + const char *s = command; + while (s && s[0]) + { + end_options = ::strstr (s, "--"); + if (end_options) + { + end_options += 2; // Get past the "--" + if (::isspace (end_options[0])) + { + expr = end_options; + while (::isspace (*expr)) + ++expr; + break; + } + } + s = end_options; + } + + if (end_options) + { + Args args (command, end_options - command); + if (!ParseOptions (args, result)) + return false; + + Error error (m_option_group.NotifyOptionParsingFinished()); + if (error.Fail()) + { + result.AppendError (error.AsCString()); + result.SetStatus (eReturnStatusFailed); + return false; + } + } + } + + if (expr == NULL) + expr = command; + + if (EvaluateExpression (expr, &(result.GetOutputStream()), &(result.GetErrorStream()), &result)) + return true; + + result.SetStatus (eReturnStatusFailed); + return false; +} + diff --git a/contrib/llvm/tools/lldb/source/Commands/CommandObjectExpression.h b/contrib/llvm/tools/lldb/source/Commands/CommandObjectExpression.h new file mode 100644 index 00000000000..3964f2d423e --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Commands/CommandObjectExpression.h @@ -0,0 +1,99 @@ +//===-- CommandObjectExpression.h -------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_CommandObjectExpression_h_ +#define liblldb_CommandObjectExpression_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Interpreter/CommandObject.h" +#include "lldb/Interpreter/OptionGroupFormat.h" +#include "lldb/Interpreter/OptionGroupValueObjectDisplay.h" +#include "lldb/Target/ExecutionContext.h" + +namespace lldb_private { + +class CommandObjectExpression : public CommandObjectRaw +{ +public: + + class CommandOptions : public OptionGroup + { + public: + + CommandOptions (); + + virtual + ~CommandOptions (); + + virtual uint32_t + GetNumDefinitions (); + + virtual const OptionDefinition* + GetDefinitions (); + + virtual Error + SetOptionValue (CommandInterpreter &interpreter, + uint32_t option_idx, + const char *option_value); + + virtual void + OptionParsingStarting (CommandInterpreter &interpreter); + + // Options table: Required for subclasses of Options. + + static OptionDefinition g_option_table[]; + bool unwind_on_error; + bool ignore_breakpoints; + bool show_types; + bool show_summary; + uint32_t timeout; + bool try_all_threads; + }; + + CommandObjectExpression (CommandInterpreter &interpreter); + + virtual + ~CommandObjectExpression (); + + virtual + Options * + GetOptions (); + +protected: + virtual bool + DoExecute (const char *command, + CommandReturnObject &result); + + static size_t + MultiLineExpressionCallback (void *baton, + InputReader &reader, + lldb::InputReaderAction notification, + const char *bytes, + size_t bytes_len); + + bool + EvaluateExpression (const char *expr, + Stream *output_stream, + Stream *error_stream, + CommandReturnObject *result = NULL); + + OptionGroupOptions m_option_group; + OptionGroupFormat m_format_options; + OptionGroupValueObjectDisplay m_varobj_options; + CommandOptions m_command_options; + uint32_t m_expr_line_count; + std::string m_expr_lines; // Multi-line expression support +}; + +} // namespace lldb_private + +#endif // liblldb_CommandObjectExpression_h_ diff --git a/contrib/llvm/tools/lldb/source/Commands/CommandObjectFrame.cpp b/contrib/llvm/tools/lldb/source/Commands/CommandObjectFrame.cpp new file mode 100644 index 00000000000..aace3a64960 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Commands/CommandObjectFrame.cpp @@ -0,0 +1,624 @@ +//===-- CommandObjectFrame.cpp ----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "CommandObjectFrame.h" + +// C Includes +// C++ Includes +#include +// Other libraries and framework includes +// Project includes +#include "lldb/Core/Debugger.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/StreamFile.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Core/Timer.h" +#include "lldb/Core/Value.h" +#include "lldb/Core/ValueObject.h" +#include "lldb/Core/ValueObjectVariable.h" +#include "lldb/DataFormatters/DataVisualization.h" +#include "lldb/Host/Host.h" +#include "lldb/Interpreter/Args.h" +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Interpreter/CommandReturnObject.h" +#include "lldb/Interpreter/Options.h" +#include "lldb/Interpreter/OptionGroupFormat.h" +#include "lldb/Interpreter/OptionGroupValueObjectDisplay.h" +#include "lldb/Interpreter/OptionGroupVariable.h" +#include "lldb/Symbol/ClangASTType.h" +#include "lldb/Symbol/ClangASTContext.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Symbol/SymbolContext.h" +#include "lldb/Symbol/Type.h" +#include "lldb/Symbol/Variable.h" +#include "lldb/Symbol/VariableList.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/StackFrame.h" +#include "lldb/Target/Thread.h" +#include "lldb/Target/Target.h" + +using namespace lldb; +using namespace lldb_private; + +#pragma mark CommandObjectFrameInfo + +//------------------------------------------------------------------------- +// CommandObjectFrameInfo +//------------------------------------------------------------------------- + +class CommandObjectFrameInfo : public CommandObjectParsed +{ +public: + + CommandObjectFrameInfo (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "frame info", + "List information about the currently selected frame in the current thread.", + "frame info", + eFlagRequiresFrame | + eFlagTryTargetAPILock | + eFlagProcessMustBeLaunched | + eFlagProcessMustBePaused ) + { + } + + ~CommandObjectFrameInfo () + { + } + +protected: + bool + DoExecute (Args& command, CommandReturnObject &result) + { + m_exe_ctx.GetFrameRef().DumpUsingSettingsFormat (&result.GetOutputStream()); + result.SetStatus (eReturnStatusSuccessFinishResult); + return result.Succeeded(); + } +}; + +#pragma mark CommandObjectFrameSelect + +//------------------------------------------------------------------------- +// CommandObjectFrameSelect +//------------------------------------------------------------------------- + +class CommandObjectFrameSelect : public CommandObjectParsed +{ +public: + + class CommandOptions : public Options + { + public: + + CommandOptions (CommandInterpreter &interpreter) : + Options(interpreter) + { + OptionParsingStarting (); + } + + virtual + ~CommandOptions () + { + } + + virtual Error + SetOptionValue (uint32_t option_idx, const char *option_arg) + { + Error error; + bool success = false; + const int short_option = m_getopt_table[option_idx].val; + switch (short_option) + { + case 'r': + relative_frame_offset = Args::StringToSInt32 (option_arg, INT32_MIN, 0, &success); + if (!success) + error.SetErrorStringWithFormat ("invalid frame offset argument '%s'", option_arg); + break; + + default: + error.SetErrorStringWithFormat ("invalid short option character '%c'", short_option); + break; + } + + return error; + } + + void + OptionParsingStarting () + { + relative_frame_offset = INT32_MIN; + } + + const OptionDefinition* + GetDefinitions () + { + return g_option_table; + } + + // Options table: Required for subclasses of Options. + + static OptionDefinition g_option_table[]; + int32_t relative_frame_offset; + }; + + CommandObjectFrameSelect (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "frame select", + "Select a frame by index from within the current thread and make it the current frame.", + NULL, + eFlagRequiresThread | + eFlagTryTargetAPILock | + eFlagProcessMustBeLaunched | + eFlagProcessMustBePaused ), + m_options (interpreter) + { + CommandArgumentEntry arg; + CommandArgumentData index_arg; + + // Define the first (and only) variant of this arg. + index_arg.arg_type = eArgTypeFrameIndex; + index_arg.arg_repetition = eArgRepeatOptional; + + // There is only one variant this argument could be; put it into the argument entry. + arg.push_back (index_arg); + + // Push the data for the first argument into the m_arguments vector. + m_arguments.push_back (arg); + } + + ~CommandObjectFrameSelect () + { + } + + virtual + Options * + GetOptions () + { + return &m_options; + } + + +protected: + bool + DoExecute (Args& command, CommandReturnObject &result) + { + // No need to check "thread" for validity as eFlagRequiresThread ensures it is valid + Thread *thread = m_exe_ctx.GetThreadPtr(); + + uint32_t frame_idx = UINT32_MAX; + if (m_options.relative_frame_offset != INT32_MIN) + { + // The one and only argument is a signed relative frame index + frame_idx = thread->GetSelectedFrameIndex (); + if (frame_idx == UINT32_MAX) + frame_idx = 0; + + if (m_options.relative_frame_offset < 0) + { + if (frame_idx >= -m_options.relative_frame_offset) + frame_idx += m_options.relative_frame_offset; + else + { + if (frame_idx == 0) + { + //If you are already at the bottom of the stack, then just warn and don't reset the frame. + result.AppendError("Already at the bottom of the stack"); + result.SetStatus(eReturnStatusFailed); + return false; + } + else + frame_idx = 0; + } + } + else if (m_options.relative_frame_offset > 0) + { + // I don't want "up 20" where "20" takes you past the top of the stack to produce + // an error, but rather to just go to the top. So I have to count the stack here... + const uint32_t num_frames = thread->GetStackFrameCount(); + if (num_frames - frame_idx > m_options.relative_frame_offset) + frame_idx += m_options.relative_frame_offset; + else + { + if (frame_idx == num_frames - 1) + { + //If we are already at the top of the stack, just warn and don't reset the frame. + result.AppendError("Already at the top of the stack"); + result.SetStatus(eReturnStatusFailed); + return false; + } + else + frame_idx = num_frames - 1; + } + } + } + else + { + if (command.GetArgumentCount() == 1) + { + const char *frame_idx_cstr = command.GetArgumentAtIndex(0); + frame_idx = Args::StringToUInt32 (frame_idx_cstr, UINT32_MAX, 0); + } + else if (command.GetArgumentCount() == 0) + { + frame_idx = thread->GetSelectedFrameIndex (); + if (frame_idx == UINT32_MAX) + { + frame_idx = 0; + } + } + else + { + result.AppendError ("invalid arguments.\n"); + m_options.GenerateOptionUsage (result.GetErrorStream(), this); + } + } + + bool success = thread->SetSelectedFrameByIndexNoisily (frame_idx, result.GetOutputStream()); + if (success) + { + m_exe_ctx.SetFrameSP(thread->GetSelectedFrame ()); + result.SetStatus (eReturnStatusSuccessFinishResult); + } + else + { + result.AppendErrorWithFormat ("Frame index (%u) out of range.\n", frame_idx); + result.SetStatus (eReturnStatusFailed); + } + + return result.Succeeded(); + } +protected: + + CommandOptions m_options; +}; + +OptionDefinition +CommandObjectFrameSelect::CommandOptions::g_option_table[] = +{ +{ LLDB_OPT_SET_1, false, "relative", 'r', required_argument, NULL, 0, eArgTypeOffset, "A relative frame index offset from the current frame index."}, +{ 0, false, NULL, 0, 0, NULL, 0, eArgTypeNone, NULL } +}; + +#pragma mark CommandObjectFrameVariable +//---------------------------------------------------------------------- +// List images with associated information +//---------------------------------------------------------------------- +class CommandObjectFrameVariable : public CommandObjectParsed +{ +public: + + CommandObjectFrameVariable (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "frame variable", + "Show frame variables. All argument and local variables " + "that are in scope will be shown when no arguments are given. " + "If any arguments are specified, they can be names of " + "argument, local, file static and file global variables. " + "Children of aggregate variables can be specified such as " + "'var->child.x'.", + NULL, + eFlagRequiresFrame | + eFlagTryTargetAPILock | + eFlagProcessMustBeLaunched | + eFlagProcessMustBePaused | + eFlagRequiresProcess), + m_option_group (interpreter), + m_option_variable(true), // Include the frame specific options by passing "true" + m_option_format (eFormatDefault), + m_varobj_options() + { + CommandArgumentEntry arg; + CommandArgumentData var_name_arg; + + // Define the first (and only) variant of this arg. + var_name_arg.arg_type = eArgTypeVarName; + var_name_arg.arg_repetition = eArgRepeatStar; + + // There is only one variant this argument could be; put it into the argument entry. + arg.push_back (var_name_arg); + + // Push the data for the first argument into the m_arguments vector. + m_arguments.push_back (arg); + + m_option_group.Append (&m_option_variable, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1); + m_option_group.Append (&m_option_format, OptionGroupFormat::OPTION_GROUP_FORMAT | OptionGroupFormat::OPTION_GROUP_GDB_FMT, LLDB_OPT_SET_1); + m_option_group.Append (&m_varobj_options, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1); + m_option_group.Finalize(); + } + + virtual + ~CommandObjectFrameVariable () + { + } + + virtual + Options * + GetOptions () + { + return &m_option_group; + } + + + virtual int + HandleArgumentCompletion (Args &input, + int &cursor_index, + int &cursor_char_position, + OptionElementVector &opt_element_vector, + int match_start_point, + int max_return_elements, + bool &word_complete, + StringList &matches) + { + // Arguments are the standard source file completer. + std::string completion_str (input.GetArgumentAtIndex(cursor_index)); + completion_str.erase (cursor_char_position); + + CommandCompletions::InvokeCommonCompletionCallbacks (m_interpreter, + CommandCompletions::eVariablePathCompletion, + completion_str.c_str(), + match_start_point, + max_return_elements, + NULL, + word_complete, + matches); + return matches.GetSize(); + } + +protected: + virtual bool + DoExecute (Args& command, CommandReturnObject &result) + { + // No need to check "frame" for validity as eFlagRequiresFrame ensures it is valid + StackFrame *frame = m_exe_ctx.GetFramePtr(); + + Stream &s = result.GetOutputStream(); + + bool get_file_globals = true; + + // Be careful about the stack frame, if any summary formatter runs code, it might clear the StackFrameList + // for the thread. So hold onto a shared pointer to the frame so it stays alive. + + VariableList *variable_list = frame->GetVariableList (get_file_globals); + + VariableSP var_sp; + ValueObjectSP valobj_sp; + + const char *name_cstr = NULL; + size_t idx; + + TypeSummaryImplSP summary_format_sp; + if (!m_option_variable.summary.IsCurrentValueEmpty()) + DataVisualization::NamedSummaryFormats::GetSummaryFormat(ConstString(m_option_variable.summary.GetCurrentValue()), summary_format_sp); + else if (!m_option_variable.summary_string.IsCurrentValueEmpty()) + summary_format_sp.reset(new StringSummaryFormat(TypeSummaryImpl::Flags(),m_option_variable.summary_string.GetCurrentValue())); + + ValueObject::DumpValueObjectOptions options(m_varobj_options.GetAsDumpOptions(false,eFormatDefault,summary_format_sp)); + + if (variable_list) + { + const Format format = m_option_format.GetFormat(); + options.SetFormat(format); + + if (command.GetArgumentCount() > 0) + { + VariableList regex_var_list; + + // If we have any args to the variable command, we will make + // variable objects from them... + for (idx = 0; (name_cstr = command.GetArgumentAtIndex(idx)) != NULL; ++idx) + { + if (m_option_variable.use_regex) + { + const size_t regex_start_index = regex_var_list.GetSize(); + RegularExpression regex (name_cstr); + if (regex.Compile(name_cstr)) + { + size_t num_matches = 0; + const size_t num_new_regex_vars = variable_list->AppendVariablesIfUnique(regex, + regex_var_list, + num_matches); + if (num_new_regex_vars > 0) + { + for (size_t regex_idx = regex_start_index, end_index = regex_var_list.GetSize(); + regex_idx < end_index; + ++regex_idx) + { + var_sp = regex_var_list.GetVariableAtIndex (regex_idx); + if (var_sp) + { + valobj_sp = frame->GetValueObjectForFrameVariable (var_sp, m_varobj_options.use_dynamic); + if (valobj_sp) + { +// if (format != eFormatDefault) +// valobj_sp->SetFormat (format); + + if (m_option_variable.show_decl && var_sp->GetDeclaration ().GetFile()) + { + bool show_fullpaths = false; + bool show_module = true; + if (var_sp->DumpDeclaration(&s, show_fullpaths, show_module)) + s.PutCString (": "); + } + ValueObject::DumpValueObject (result.GetOutputStream(), + valobj_sp.get(), + options); + } + } + } + } + else if (num_matches == 0) + { + result.GetErrorStream().Printf ("error: no variables matched the regular expression '%s'.\n", name_cstr); + } + } + else + { + char regex_error[1024]; + if (regex.GetErrorAsCString(regex_error, sizeof(regex_error))) + result.GetErrorStream().Printf ("error: %s\n", regex_error); + else + result.GetErrorStream().Printf ("error: unkown regex error when compiling '%s'\n", name_cstr); + } + } + else // No regex, either exact variable names or variable expressions. + { + Error error; + uint32_t expr_path_options = StackFrame::eExpressionPathOptionCheckPtrVsMember | + StackFrame::eExpressionPathOptionsAllowDirectIVarAccess; + lldb::VariableSP var_sp; + valobj_sp = frame->GetValueForVariableExpressionPath (name_cstr, + m_varobj_options.use_dynamic, + expr_path_options, + var_sp, + error); + if (valobj_sp) + { +// if (format != eFormatDefault) +// valobj_sp->SetFormat (format); + if (m_option_variable.show_decl && var_sp && var_sp->GetDeclaration ().GetFile()) + { + var_sp->GetDeclaration ().DumpStopContext (&s, false); + s.PutCString (": "); + } + + options.SetFormat(format); + + Stream &output_stream = result.GetOutputStream(); + options.SetRootValueObjectName(valobj_sp->GetParent() ? name_cstr : NULL); + ValueObject::DumpValueObject (output_stream, + valobj_sp.get(), + options); + } + else + { + const char *error_cstr = error.AsCString(NULL); + if (error_cstr) + result.GetErrorStream().Printf("error: %s\n", error_cstr); + else + result.GetErrorStream().Printf ("error: unable to find any variable expression path that matches '%s'\n", name_cstr); + } + } + } + } + else // No command arg specified. Use variable_list, instead. + { + const size_t num_variables = variable_list->GetSize(); + if (num_variables > 0) + { + for (size_t i=0; iGetVariableAtIndex(i); + bool dump_variable = true; + switch (var_sp->GetScope()) + { + case eValueTypeVariableGlobal: + dump_variable = m_option_variable.show_globals; + if (dump_variable && m_option_variable.show_scope) + s.PutCString("GLOBAL: "); + break; + + case eValueTypeVariableStatic: + dump_variable = m_option_variable.show_globals; + if (dump_variable && m_option_variable.show_scope) + s.PutCString("STATIC: "); + break; + + case eValueTypeVariableArgument: + dump_variable = m_option_variable.show_args; + if (dump_variable && m_option_variable.show_scope) + s.PutCString(" ARG: "); + break; + + case eValueTypeVariableLocal: + dump_variable = m_option_variable.show_locals; + if (dump_variable && m_option_variable.show_scope) + s.PutCString(" LOCAL: "); + break; + + default: + break; + } + + if (dump_variable) + { + // Use the variable object code to make sure we are + // using the same APIs as the the public API will be + // using... + valobj_sp = frame->GetValueObjectForFrameVariable (var_sp, + m_varobj_options.use_dynamic); + if (valobj_sp) + { +// if (format != eFormatDefault) +// valobj_sp->SetFormat (format); + + // When dumping all variables, don't print any variables + // that are not in scope to avoid extra unneeded output + if (valobj_sp->IsInScope ()) + { + if (m_option_variable.show_decl && var_sp->GetDeclaration ().GetFile()) + { + var_sp->GetDeclaration ().DumpStopContext (&s, false); + s.PutCString (": "); + } + + options.SetFormat(format); + options.SetRootValueObjectName(name_cstr); + ValueObject::DumpValueObject (result.GetOutputStream(), + valobj_sp.get(), + options); + } + } + } + } + } + } + result.SetStatus (eReturnStatusSuccessFinishResult); + } + + if (m_interpreter.TruncationWarningNecessary()) + { + result.GetOutputStream().Printf(m_interpreter.TruncationWarningText(), + m_cmd_name.c_str()); + m_interpreter.TruncationWarningGiven(); + } + + return result.Succeeded(); + } +protected: + + OptionGroupOptions m_option_group; + OptionGroupVariable m_option_variable; + OptionGroupFormat m_option_format; + OptionGroupValueObjectDisplay m_varobj_options; +}; + + +#pragma mark CommandObjectMultiwordFrame + +//------------------------------------------------------------------------- +// CommandObjectMultiwordFrame +//------------------------------------------------------------------------- + +CommandObjectMultiwordFrame::CommandObjectMultiwordFrame (CommandInterpreter &interpreter) : + CommandObjectMultiword (interpreter, + "frame", + "A set of commands for operating on the current thread's frames.", + "frame []") +{ + LoadSubCommand ("info", CommandObjectSP (new CommandObjectFrameInfo (interpreter))); + LoadSubCommand ("select", CommandObjectSP (new CommandObjectFrameSelect (interpreter))); + LoadSubCommand ("variable", CommandObjectSP (new CommandObjectFrameVariable (interpreter))); +} + +CommandObjectMultiwordFrame::~CommandObjectMultiwordFrame () +{ +} + diff --git a/contrib/llvm/tools/lldb/source/Commands/CommandObjectFrame.h b/contrib/llvm/tools/lldb/source/Commands/CommandObjectFrame.h new file mode 100644 index 00000000000..ea7c808e84b --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Commands/CommandObjectFrame.h @@ -0,0 +1,40 @@ +//===-- CommandObjectFrame.h ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_CommandObjectFrame_h_ +#define liblldb_CommandObjectFrame_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Interpreter/Options.h" +#include "lldb/Core/ArchSpec.h" +#include "lldb/Interpreter/CommandObjectMultiword.h" + +namespace lldb_private { + +//------------------------------------------------------------------------- +// CommandObjectMultiwordFrame +//------------------------------------------------------------------------- + +class CommandObjectMultiwordFrame : public CommandObjectMultiword +{ +public: + + CommandObjectMultiwordFrame (CommandInterpreter &interpreter); + + virtual + ~CommandObjectMultiwordFrame (); + +}; + +} // namespace lldb_private + +#endif // liblldb_CommandObjectFrame_h_ diff --git a/contrib/llvm/tools/lldb/source/Commands/CommandObjectHelp.cpp b/contrib/llvm/tools/lldb/source/Commands/CommandObjectHelp.cpp new file mode 100644 index 00000000000..d2c97f91260 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Commands/CommandObjectHelp.cpp @@ -0,0 +1,249 @@ +//===-- CommandObjectHelp.cpp -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "CommandObjectHelp.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Interpreter/CommandObjectMultiword.h" +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Interpreter/Options.h" +#include "lldb/Interpreter/CommandReturnObject.h" + +using namespace lldb; +using namespace lldb_private; + +//------------------------------------------------------------------------- +// CommandObjectHelp +//------------------------------------------------------------------------- + +CommandObjectHelp::CommandObjectHelp (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "help", + "Show a list of all debugger commands, or give details about specific commands.", + "help []"), m_options (interpreter) +{ + CommandArgumentEntry arg; + CommandArgumentData command_arg; + + // Define the first (and only) variant of this arg. + command_arg.arg_type = eArgTypeCommandName; + command_arg.arg_repetition = eArgRepeatStar; + + // There is only one variant this argument could be; put it into the argument entry. + arg.push_back (command_arg); + + // Push the data for the first argument into the m_arguments vector. + m_arguments.push_back (arg); +} + +CommandObjectHelp::~CommandObjectHelp() +{ +} + +OptionDefinition +CommandObjectHelp::CommandOptions::g_option_table[] = +{ + { LLDB_OPT_SET_ALL, false, "show-aliases", 'a', no_argument, NULL, 0, eArgTypeNone, "Show aliases in the command list."}, + { LLDB_OPT_SET_ALL, false, "hide-user-commands", 'u', no_argument, NULL, 0, eArgTypeNone, "Hide user-defined commands from the list."}, + { 0, false, NULL, 0, 0, NULL, 0, eArgTypeNone, NULL } +}; + +bool +CommandObjectHelp::DoExecute (Args& command, CommandReturnObject &result) +{ + CommandObject::CommandMap::iterator pos; + CommandObject *cmd_obj; + const size_t argc = command.GetArgumentCount (); + + // 'help' doesn't take any arguments, other than command names. If argc is 0, we show the user + // all commands (aliases and user commands if asked for). Otherwise every argument must be the name of a command or a sub-command. + if (argc == 0) + { + uint32_t cmd_types = CommandInterpreter::eCommandTypesBuiltin; + if (m_options.m_show_aliases) + cmd_types |= CommandInterpreter::eCommandTypesAliases; + if (m_options.m_show_user_defined) + cmd_types |= CommandInterpreter::eCommandTypesUserDef; + + result.SetStatus (eReturnStatusSuccessFinishNoResult); + m_interpreter.GetHelp (result, cmd_types); // General help + } + else + { + // Get command object for the first command argument. Only search built-in command dictionary. + StringList matches; + cmd_obj = m_interpreter.GetCommandObject (command.GetArgumentAtIndex (0), &matches); + bool is_alias_command = m_interpreter.AliasExists (command.GetArgumentAtIndex (0)); + std::string alias_name = command.GetArgumentAtIndex(0); + + if (cmd_obj != NULL) + { + StringList matches; + bool all_okay = true; + CommandObject *sub_cmd_obj = cmd_obj; + // Loop down through sub_command dictionaries until we find the command object that corresponds + // to the help command entered. + for (size_t i = 1; i < argc && all_okay; ++i) + { + std::string sub_command = command.GetArgumentAtIndex(i); + matches.Clear(); + if (! sub_cmd_obj->IsMultiwordObject ()) + { + all_okay = false; + } + else + { + CommandObject *found_cmd; + found_cmd = sub_cmd_obj->GetSubcommandObject(sub_command.c_str(), &matches); + if (found_cmd == NULL) + all_okay = false; + else if (matches.GetSize() > 1) + all_okay = false; + else + sub_cmd_obj = found_cmd; + } + } + + if (!all_okay || (sub_cmd_obj == NULL)) + { + std::string cmd_string; + command.GetCommandString (cmd_string); + if (matches.GetSize() >= 2) + { + StreamString s; + s.Printf ("ambiguous command %s", cmd_string.c_str()); + size_t num_matches = matches.GetSize(); + for (size_t match_idx = 0; match_idx < num_matches; match_idx++) + { + s.Printf ("\n\t%s", matches.GetStringAtIndex(match_idx)); + } + s.Printf ("\n"); + result.AppendError(s.GetData()); + result.SetStatus (eReturnStatusFailed); + return false; + } + else if (!sub_cmd_obj) + { + result.AppendErrorWithFormat("'%s' is not a known command.\n" + "Try 'help' to see a current list of commands.\n", + cmd_string.c_str()); + result.SetStatus (eReturnStatusFailed); + return false; + } + else + { + result.GetOutputStream().Printf("'%s' is not a known command.\n" + "Try 'help' to see a current list of commands.\n" + "The closest match is '%s'. Help on it follows.\n\n", + cmd_string.c_str(), + sub_cmd_obj->GetCommandName()); + } + } + + sub_cmd_obj->GenerateHelpText(result); + + if (is_alias_command) + { + StreamString sstr; + m_interpreter.GetAliasHelp (alias_name.c_str(), cmd_obj->GetCommandName(), sstr); + result.GetOutputStream().Printf ("\n'%s' is an abbreviation for %s\n", alias_name.c_str(), sstr.GetData()); + } + } + else if (matches.GetSize() > 0) + { + Stream &output_strm = result.GetOutputStream(); + output_strm.Printf("Help requested with ambiguous command name, possible completions:\n"); + const size_t match_count = matches.GetSize(); + for (size_t i = 0; i < match_count; i++) + { + output_strm.Printf("\t%s\n", matches.GetStringAtIndex(i)); + } + } + else + { + // Maybe the user is asking for help about a command argument rather than a command. + const CommandArgumentType arg_type = CommandObject::LookupArgumentName (command.GetArgumentAtIndex (0)); + if (arg_type != eArgTypeLastArg) + { + Stream &output_strm = result.GetOutputStream (); + CommandObject::GetArgumentHelp (output_strm, arg_type, m_interpreter); + result.SetStatus (eReturnStatusSuccessFinishNoResult); + } + else + { + result.AppendErrorWithFormat + ("'%s' is not a known command.\nTry 'help' to see a current list of commands.\n", + command.GetArgumentAtIndex(0)); + result.SetStatus (eReturnStatusFailed); + } + } + } + + return result.Succeeded(); +} + +int +CommandObjectHelp::HandleCompletion +( + Args &input, + int &cursor_index, + int &cursor_char_position, + int match_start_point, + int max_return_elements, + bool &word_complete, + StringList &matches +) +{ + // Return the completions of the commands in the help system: + if (cursor_index == 0) + { + return m_interpreter.HandleCompletionMatches (input, + cursor_index, + cursor_char_position, + match_start_point, + max_return_elements, + word_complete, + matches); + } + else + { + CommandObject *cmd_obj = m_interpreter.GetCommandObject (input.GetArgumentAtIndex(0)); + + // The command that they are getting help on might be ambiguous, in which case we should complete that, + // otherwise complete with the command the user is getting help on... + + if (cmd_obj) + { + input.Shift(); + cursor_index--; + return cmd_obj->HandleCompletion (input, + cursor_index, + cursor_char_position, + match_start_point, + max_return_elements, + word_complete, + matches); + } + else + { + return m_interpreter.HandleCompletionMatches (input, + cursor_index, + cursor_char_position, + match_start_point, + max_return_elements, + word_complete, + matches); + } + } +} diff --git a/contrib/llvm/tools/lldb/source/Commands/CommandObjectHelp.h b/contrib/llvm/tools/lldb/source/Commands/CommandObjectHelp.h new file mode 100644 index 00000000000..6e8f9d4cbc7 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Commands/CommandObjectHelp.h @@ -0,0 +1,119 @@ +//===-- CommandObjectHelp.h -------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_CommandObjectHelp_h_ +#define liblldb_CommandObjectHelp_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Interpreter/CommandObject.h" +#include "lldb/Interpreter/Options.h" + +namespace lldb_private { + +//------------------------------------------------------------------------- +// CommandObjectHelp +//------------------------------------------------------------------------- + +class CommandObjectHelp : public CommandObjectParsed +{ +public: + + CommandObjectHelp (CommandInterpreter &interpreter); + + virtual + ~CommandObjectHelp (); + + virtual int + HandleCompletion (Args &input, + int &cursor_index, + int &cursor_char_position, + int match_start_point, + int max_return_elements, + bool &word_complete, + StringList &matches); + + class CommandOptions : public Options + { + public: + + CommandOptions (CommandInterpreter &interpreter) : + Options (interpreter) + { + } + + virtual + ~CommandOptions (){} + + virtual Error + SetOptionValue (uint32_t option_idx, const char *option_arg) + { + Error error; + const int short_option = m_getopt_table[option_idx].val; + + switch (short_option) + { + case 'a': + m_show_aliases = true; + break; + case 'u': + m_show_user_defined = false; + break; + default: + error.SetErrorStringWithFormat ("unrecognized option '%c'", short_option); + break; + } + + return error; + } + + void + OptionParsingStarting () + { + m_show_aliases = false; + m_show_user_defined = true; + } + + const OptionDefinition* + GetDefinitions () + { + return g_option_table; + } + + // Options table: Required for subclasses of Options. + + static OptionDefinition g_option_table[]; + + // Instance variables to hold the values for command options. + + bool m_show_aliases; + bool m_show_user_defined; + }; + + virtual Options * + GetOptions () + { + return &m_options; + } + +protected: + virtual bool + DoExecute (Args& command, + CommandReturnObject &result); + +private: + CommandOptions m_options; + +}; + +} // namespace lldb_private + +#endif // liblldb_CommandObjectHelp_h_ diff --git a/contrib/llvm/tools/lldb/source/Commands/CommandObjectLog.cpp b/contrib/llvm/tools/lldb/source/Commands/CommandObjectLog.cpp new file mode 100644 index 00000000000..5fb79154c4e --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Commands/CommandObjectLog.cpp @@ -0,0 +1,503 @@ +//===-- CommandObjectLog.cpp ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "CommandObjectLog.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private-log.h" + +#include "lldb/Interpreter/Args.h" +#include "lldb/Core/Debugger.h" +#include "lldb/Host/FileSpec.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Module.h" +#include "lldb/Interpreter/Options.h" +#include "lldb/Core/RegularExpression.h" +#include "lldb/Core/Stream.h" +#include "lldb/Core/StreamFile.h" +#include "lldb/Core/Timer.h" + +#include "lldb/Core/Debugger.h" +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Interpreter/CommandReturnObject.h" + +#include "lldb/Symbol/LineTable.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Symbol/SymbolFile.h" +#include "lldb/Symbol/SymbolVendor.h" + +#include "lldb/Target/Process.h" +#include "lldb/Target/Target.h" + +using namespace lldb; +using namespace lldb_private; + + +class CommandObjectLogEnable : public CommandObjectParsed +{ +public: + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + CommandObjectLogEnable(CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "log enable", + "Enable logging for a single log channel.", + NULL), + m_options (interpreter) + { + + CommandArgumentEntry arg1; + CommandArgumentEntry arg2; + CommandArgumentData channel_arg; + CommandArgumentData category_arg; + + // Define the first (and only) variant of this arg. + channel_arg.arg_type = eArgTypeLogChannel; + channel_arg.arg_repetition = eArgRepeatPlain; + + // There is only one variant this argument could be; put it into the argument entry. + arg1.push_back (channel_arg); + + category_arg.arg_type = eArgTypeLogCategory; + category_arg.arg_repetition = eArgRepeatPlus; + + arg2.push_back (category_arg); + + // Push the data for the first argument into the m_arguments vector. + m_arguments.push_back (arg1); + m_arguments.push_back (arg2); + } + + virtual + ~CommandObjectLogEnable() + { + } + + Options * + GetOptions () + { + return &m_options; + } + +// int +// HandleArgumentCompletion (Args &input, +// int &cursor_index, +// int &cursor_char_position, +// OptionElementVector &opt_element_vector, +// int match_start_point, +// int max_return_elements, +// bool &word_complete, +// StringList &matches) +// { +// std::string completion_str (input.GetArgumentAtIndex(cursor_index)); +// completion_str.erase (cursor_char_position); +// +// if (cursor_index == 1) +// { +// // +// Log::AutoCompleteChannelName (completion_str.c_str(), matches); +// } +// return matches.GetSize(); +// } +// + + class CommandOptions : public Options + { + public: + + CommandOptions (CommandInterpreter &interpreter) : + Options (interpreter), + log_file (), + log_options (0) + { + } + + + virtual + ~CommandOptions () + { + } + + virtual Error + SetOptionValue (uint32_t option_idx, const char *option_arg) + { + Error error; + const int short_option = m_getopt_table[option_idx].val; + + switch (short_option) + { + case 'f': log_file.SetFile(option_arg, true); break; + case 't': log_options |= LLDB_LOG_OPTION_THREADSAFE; break; + case 'v': log_options |= LLDB_LOG_OPTION_VERBOSE; break; + case 'g': log_options |= LLDB_LOG_OPTION_DEBUG; break; + case 's': log_options |= LLDB_LOG_OPTION_PREPEND_SEQUENCE; break; + case 'T': log_options |= LLDB_LOG_OPTION_PREPEND_TIMESTAMP; break; + case 'p': log_options |= LLDB_LOG_OPTION_PREPEND_PROC_AND_THREAD;break; + case 'n': log_options |= LLDB_LOG_OPTION_PREPEND_THREAD_NAME; break; + case 'S': log_options |= LLDB_LOG_OPTION_BACKTRACE; break; + default: + error.SetErrorStringWithFormat ("unrecognized option '%c'", short_option); + break; + } + + return error; + } + + void + OptionParsingStarting () + { + log_file.Clear(); + log_options = 0; + } + + const OptionDefinition* + GetDefinitions () + { + return g_option_table; + } + + // Options table: Required for subclasses of Options. + + static OptionDefinition g_option_table[]; + + // Instance variables to hold the values for command options. + + FileSpec log_file; + uint32_t log_options; + }; + +protected: + virtual bool + DoExecute (Args& args, + CommandReturnObject &result) + { + if (args.GetArgumentCount() < 2) + { + result.AppendErrorWithFormat("%s takes a log channel and one or more log types.\n", m_cmd_name.c_str()); + } + else + { + std::string channel(args.GetArgumentAtIndex(0)); + args.Shift (); // Shift off the channel + char log_file[PATH_MAX]; + if (m_options.log_file) + m_options.log_file.GetPath(log_file, sizeof(log_file)); + else + log_file[0] = '\0'; + bool success = m_interpreter.GetDebugger().EnableLog (channel.c_str(), + args.GetConstArgumentVector(), + log_file, + m_options.log_options, + result.GetErrorStream()); + if (success) + result.SetStatus (eReturnStatusSuccessFinishNoResult); + else + result.SetStatus (eReturnStatusFailed); + } + return result.Succeeded(); + } + + CommandOptions m_options; +}; + +OptionDefinition +CommandObjectLogEnable::CommandOptions::g_option_table[] = +{ +{ LLDB_OPT_SET_1, false, "file", 'f', required_argument, NULL, 0, eArgTypeFilename, "Set the destination file to log to."}, +{ LLDB_OPT_SET_1, false, "threadsafe", 't', no_argument, NULL, 0, eArgTypeNone, "Enable thread safe logging to avoid interweaved log lines." }, +{ LLDB_OPT_SET_1, false, "verbose", 'v', no_argument, NULL, 0, eArgTypeNone, "Enable verbose logging." }, +{ LLDB_OPT_SET_1, false, "debug", 'g', no_argument, NULL, 0, eArgTypeNone, "Enable debug logging." }, +{ LLDB_OPT_SET_1, false, "sequence", 's', no_argument, NULL, 0, eArgTypeNone, "Prepend all log lines with an increasing integer sequence id." }, +{ LLDB_OPT_SET_1, false, "timestamp", 'T', no_argument, NULL, 0, eArgTypeNone, "Prepend all log lines with a timestamp." }, +{ LLDB_OPT_SET_1, false, "pid-tid", 'p', no_argument, NULL, 0, eArgTypeNone, "Prepend all log lines with the process and thread ID that generates the log line." }, +{ LLDB_OPT_SET_1, false, "thread-name",'n', no_argument, NULL, 0, eArgTypeNone, "Prepend all log lines with the thread name for the thread that generates the log line." }, +{ LLDB_OPT_SET_1, false, "stack", 'S', no_argument, NULL, 0, eArgTypeNone, "Append a stack backtrace to each log line." }, +{ 0, false, NULL, 0, 0, NULL, 0, eArgTypeNone, NULL } +}; + +class CommandObjectLogDisable : public CommandObjectParsed +{ +public: + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + CommandObjectLogDisable(CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "log disable", + "Disable one or more log channel categories.", + NULL) + { + CommandArgumentEntry arg1; + CommandArgumentEntry arg2; + CommandArgumentData channel_arg; + CommandArgumentData category_arg; + + // Define the first (and only) variant of this arg. + channel_arg.arg_type = eArgTypeLogChannel; + channel_arg.arg_repetition = eArgRepeatPlain; + + // There is only one variant this argument could be; put it into the argument entry. + arg1.push_back (channel_arg); + + category_arg.arg_type = eArgTypeLogCategory; + category_arg.arg_repetition = eArgRepeatPlus; + + arg2.push_back (category_arg); + + // Push the data for the first argument into the m_arguments vector. + m_arguments.push_back (arg1); + m_arguments.push_back (arg2); + } + + virtual + ~CommandObjectLogDisable() + { + } + +protected: + virtual bool + DoExecute (Args& args, + CommandReturnObject &result) + { + const size_t argc = args.GetArgumentCount(); + if (argc == 0) + { + result.AppendErrorWithFormat("%s takes a log channel and one or more log types.\n", m_cmd_name.c_str()); + } + else + { + Log::Callbacks log_callbacks; + + std::string channel(args.GetArgumentAtIndex(0)); + args.Shift (); // Shift off the channel + if (Log::GetLogChannelCallbacks (ConstString(channel.c_str()), log_callbacks)) + { + log_callbacks.disable (args.GetConstArgumentVector(), &result.GetErrorStream()); + result.SetStatus(eReturnStatusSuccessFinishNoResult); + } + else if (channel == "all") + { + Log::DisableAllLogChannels(&result.GetErrorStream()); + } + else + { + LogChannelSP log_channel_sp (LogChannel::FindPlugin(channel.c_str())); + if (log_channel_sp) + { + log_channel_sp->Disable(args.GetConstArgumentVector(), &result.GetErrorStream()); + result.SetStatus(eReturnStatusSuccessFinishNoResult); + } + else + result.AppendErrorWithFormat("Invalid log channel '%s'.\n", args.GetArgumentAtIndex(0)); + } + } + return result.Succeeded(); + } +}; + +class CommandObjectLogList : public CommandObjectParsed +{ +public: + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + CommandObjectLogList(CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "log list", + "List the log categories for one or more log channels. If none specified, lists them all.", + NULL) + { + CommandArgumentEntry arg; + CommandArgumentData channel_arg; + + // Define the first (and only) variant of this arg. + channel_arg.arg_type = eArgTypeLogChannel; + channel_arg.arg_repetition = eArgRepeatStar; + + // There is only one variant this argument could be; put it into the argument entry. + arg.push_back (channel_arg); + + // Push the data for the first argument into the m_arguments vector. + m_arguments.push_back (arg); + } + + virtual + ~CommandObjectLogList() + { + } + +protected: + virtual bool + DoExecute (Args& args, + CommandReturnObject &result) + { + const size_t argc = args.GetArgumentCount(); + if (argc == 0) + { + Log::ListAllLogChannels (&result.GetOutputStream()); + result.SetStatus(eReturnStatusSuccessFinishResult); + } + else + { + for (size_t i=0; iListCategories(&result.GetOutputStream()); + result.SetStatus(eReturnStatusSuccessFinishNoResult); + } + else + result.AppendErrorWithFormat("Invalid log channel '%s'.\n", args.GetArgumentAtIndex(0)); + } + } + } + return result.Succeeded(); + } +}; + +class CommandObjectLogTimer : public CommandObjectParsed +{ +public: + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + CommandObjectLogTimer(CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "log timers", + "Enable, disable, dump, and reset LLDB internal performance timers.", + "log timers < enable | disable | dump | increment | reset >") + { + } + + virtual + ~CommandObjectLogTimer() + { + } + +protected: + virtual bool + DoExecute (Args& args, + CommandReturnObject &result) + { + const size_t argc = args.GetArgumentCount(); + result.SetStatus(eReturnStatusFailed); + + if (argc == 1) + { + const char *sub_command = args.GetArgumentAtIndex(0); + + if (strcasecmp(sub_command, "enable") == 0) + { + Timer::SetDisplayDepth (UINT32_MAX); + result.SetStatus(eReturnStatusSuccessFinishNoResult); + } + else if (strcasecmp(sub_command, "disable") == 0) + { + Timer::DumpCategoryTimes (&result.GetOutputStream()); + Timer::SetDisplayDepth (0); + result.SetStatus(eReturnStatusSuccessFinishResult); + } + else if (strcasecmp(sub_command, "dump") == 0) + { + Timer::DumpCategoryTimes (&result.GetOutputStream()); + result.SetStatus(eReturnStatusSuccessFinishResult); + } + else if (strcasecmp(sub_command, "reset") == 0) + { + Timer::ResetCategoryTimes (); + result.SetStatus(eReturnStatusSuccessFinishResult); + } + + } + else if (argc == 2) + { + const char *sub_command = args.GetArgumentAtIndex(0); + + if (strcasecmp(sub_command, "enable") == 0) + { + bool success; + uint32_t depth = Args::StringToUInt32(args.GetArgumentAtIndex(1), 0, 0, &success); + if (success) + { + Timer::SetDisplayDepth (depth); + result.SetStatus(eReturnStatusSuccessFinishNoResult); + } + else + result.AppendError("Could not convert enable depth to an unsigned integer."); + } + if (strcasecmp(sub_command, "increment") == 0) + { + bool success; + bool increment = Args::StringToBoolean(args.GetArgumentAtIndex(1), false, &success); + if (success) + { + Timer::SetQuiet (!increment); + result.SetStatus(eReturnStatusSuccessFinishNoResult); + } + else + result.AppendError("Could not convert increment value to boolean."); + } + } + + if (!result.Succeeded()) + { + result.AppendError("Missing subcommand"); + result.AppendErrorWithFormat("Usage: %s\n", m_cmd_syntax.c_str()); + } + return result.Succeeded(); + } +}; + +//---------------------------------------------------------------------- +// CommandObjectLog constructor +//---------------------------------------------------------------------- +CommandObjectLog::CommandObjectLog(CommandInterpreter &interpreter) : + CommandObjectMultiword (interpreter, + "log", + "A set of commands for operating on logs.", + "log []") +{ + LoadSubCommand ("enable", CommandObjectSP (new CommandObjectLogEnable (interpreter))); + LoadSubCommand ("disable", CommandObjectSP (new CommandObjectLogDisable (interpreter))); + LoadSubCommand ("list", CommandObjectSP (new CommandObjectLogList (interpreter))); + LoadSubCommand ("timers", CommandObjectSP (new CommandObjectLogTimer (interpreter))); +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +CommandObjectLog::~CommandObjectLog() +{ +} + + + + diff --git a/contrib/llvm/tools/lldb/source/Commands/CommandObjectLog.h b/contrib/llvm/tools/lldb/source/Commands/CommandObjectLog.h new file mode 100644 index 00000000000..3e731fa1d18 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Commands/CommandObjectLog.h @@ -0,0 +1,48 @@ +//===-- CommandObjectLog.h --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_CommandObjectLog_h_ +#define liblldb_CommandObjectLog_h_ + +// C Includes +// C++ Includes +#include +#include + +// Other libraries and framework includes +// Project includes +#include "lldb/Interpreter/CommandObjectMultiword.h" + +namespace lldb_private { + +//------------------------------------------------------------------------- +// CommandObjectLog +//------------------------------------------------------------------------- + +class CommandObjectLog : public CommandObjectMultiword +{ +public: + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + CommandObjectLog(CommandInterpreter &interpreter); + + virtual + ~CommandObjectLog(); + +private: + //------------------------------------------------------------------ + // For CommandObjectLog only + //------------------------------------------------------------------ + DISALLOW_COPY_AND_ASSIGN (CommandObjectLog); +}; + +} // namespace lldb_private + +#endif // liblldb_CommandObjectLog_h_ diff --git a/contrib/llvm/tools/lldb/source/Commands/CommandObjectMemory.cpp b/contrib/llvm/tools/lldb/source/Commands/CommandObjectMemory.cpp new file mode 100644 index 00000000000..4725a4da657 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Commands/CommandObjectMemory.cpp @@ -0,0 +1,1370 @@ +//===-- CommandObjectMemory.cpp ---------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "CommandObjectMemory.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/DataBufferHeap.h" +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/Debugger.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Core/ValueObjectMemory.h" +#include "lldb/Interpreter/Args.h" +#include "lldb/Interpreter/CommandReturnObject.h" +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Interpreter/Options.h" +#include "lldb/Interpreter/OptionGroupFormat.h" +#include "lldb/Interpreter/OptionGroupOutputFile.h" +#include "lldb/Interpreter/OptionGroupValueObjectDisplay.h" +#include "lldb/Interpreter/OptionValueString.h" +#include "lldb/Symbol/TypeList.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/StackFrame.h" + +using namespace lldb; +using namespace lldb_private; + +static OptionDefinition +g_option_table[] = +{ + { LLDB_OPT_SET_1, false, "num-per-line" ,'l', required_argument, NULL, 0, eArgTypeNumberPerLine ,"The number of items per line to display."}, + { LLDB_OPT_SET_2, false, "binary" ,'b', no_argument , NULL, 0, eArgTypeNone ,"If true, memory will be saved as binary. If false, the memory is saved save as an ASCII dump that uses the format, size, count and number per line settings."}, + { LLDB_OPT_SET_3, true , "type" ,'t', required_argument, NULL, 0, eArgTypeNone ,"The name of a type to view memory as."}, + { LLDB_OPT_SET_1| + LLDB_OPT_SET_2| + LLDB_OPT_SET_3, false, "force" ,'r', no_argument, NULL, 0, eArgTypeNone ,"Necessary if reading over target.max-memory-read-size bytes."}, +}; + + + +class OptionGroupReadMemory : public OptionGroup +{ +public: + + OptionGroupReadMemory () : + m_num_per_line (1,1), + m_output_as_binary (false), + m_view_as_type() + { + } + + virtual + ~OptionGroupReadMemory () + { + } + + + virtual uint32_t + GetNumDefinitions () + { + return sizeof (g_option_table) / sizeof (OptionDefinition); + } + + virtual const OptionDefinition* + GetDefinitions () + { + return g_option_table; + } + + virtual Error + SetOptionValue (CommandInterpreter &interpreter, + uint32_t option_idx, + const char *option_arg) + { + Error error; + const int short_option = g_option_table[option_idx].short_option; + + switch (short_option) + { + case 'l': + error = m_num_per_line.SetValueFromCString (option_arg); + if (m_num_per_line.GetCurrentValue() == 0) + error.SetErrorStringWithFormat("invalid value for --num-per-line option '%s'", option_arg); + break; + + case 'b': + m_output_as_binary = true; + break; + + case 't': + error = m_view_as_type.SetValueFromCString (option_arg); + break; + + case 'r': + m_force = true; + break; + + default: + error.SetErrorStringWithFormat("unrecognized short option '%c'", short_option); + break; + } + return error; + } + + virtual void + OptionParsingStarting (CommandInterpreter &interpreter) + { + m_num_per_line.Clear(); + m_output_as_binary = false; + m_view_as_type.Clear(); + m_force = false; + } + + Error + FinalizeSettings (Target *target, OptionGroupFormat& format_options) + { + Error error; + OptionValueUInt64 &byte_size_value = format_options.GetByteSizeValue(); + OptionValueUInt64 &count_value = format_options.GetCountValue(); + const bool byte_size_option_set = byte_size_value.OptionWasSet(); + const bool num_per_line_option_set = m_num_per_line.OptionWasSet(); + const bool count_option_set = format_options.GetCountValue().OptionWasSet(); + + switch (format_options.GetFormat()) + { + default: + break; + + case eFormatBoolean: + if (!byte_size_option_set) + byte_size_value = 1; + if (!num_per_line_option_set) + m_num_per_line = 1; + if (!count_option_set) + format_options.GetCountValue() = 8; + break; + + case eFormatCString: + break; + + case eFormatInstruction: + if (count_option_set) + byte_size_value = target->GetArchitecture().GetMaximumOpcodeByteSize(); + m_num_per_line = 1; + break; + + case eFormatAddressInfo: + if (!byte_size_option_set) + byte_size_value = target->GetArchitecture().GetAddressByteSize(); + m_num_per_line = 1; + if (!count_option_set) + format_options.GetCountValue() = 8; + break; + + case eFormatPointer: + byte_size_value = target->GetArchitecture().GetAddressByteSize(); + if (!num_per_line_option_set) + m_num_per_line = 4; + if (!count_option_set) + format_options.GetCountValue() = 8; + break; + + case eFormatBinary: + case eFormatFloat: + case eFormatOctal: + case eFormatDecimal: + case eFormatEnum: + case eFormatUnicode16: + case eFormatUnicode32: + case eFormatUnsigned: + case eFormatHexFloat: + if (!byte_size_option_set) + byte_size_value = 4; + if (!num_per_line_option_set) + m_num_per_line = 1; + if (!count_option_set) + format_options.GetCountValue() = 8; + break; + + case eFormatBytes: + case eFormatBytesWithASCII: + if (byte_size_option_set) + { + if (byte_size_value > 1) + error.SetErrorStringWithFormat ("display format (bytes/bytes with ascii) conflicts with the specified byte size %" PRIu64 "\n" + "\tconsider using a different display format or don't specify the byte size", + byte_size_value.GetCurrentValue()); + } + else + byte_size_value = 1; + if (!num_per_line_option_set) + m_num_per_line = 16; + if (!count_option_set) + format_options.GetCountValue() = 32; + break; + case eFormatCharArray: + case eFormatChar: + case eFormatCharPrintable: + if (!byte_size_option_set) + byte_size_value = 1; + if (!num_per_line_option_set) + m_num_per_line = 32; + if (!count_option_set) + format_options.GetCountValue() = 64; + break; + case eFormatComplex: + if (!byte_size_option_set) + byte_size_value = 8; + if (!num_per_line_option_set) + m_num_per_line = 1; + if (!count_option_set) + format_options.GetCountValue() = 8; + break; + case eFormatComplexInteger: + if (!byte_size_option_set) + byte_size_value = 8; + if (!num_per_line_option_set) + m_num_per_line = 1; + if (!count_option_set) + format_options.GetCountValue() = 8; + break; + case eFormatHex: + if (!byte_size_option_set) + byte_size_value = 4; + if (!num_per_line_option_set) + { + switch (byte_size_value) + { + case 1: + case 2: + m_num_per_line = 8; + break; + case 4: + m_num_per_line = 4; + break; + case 8: + m_num_per_line = 2; + break; + default: + m_num_per_line = 1; + break; + } + } + if (!count_option_set) + count_value = 8; + break; + + case eFormatVectorOfChar: + case eFormatVectorOfSInt8: + case eFormatVectorOfUInt8: + case eFormatVectorOfSInt16: + case eFormatVectorOfUInt16: + case eFormatVectorOfSInt32: + case eFormatVectorOfUInt32: + case eFormatVectorOfSInt64: + case eFormatVectorOfUInt64: + case eFormatVectorOfFloat32: + case eFormatVectorOfFloat64: + case eFormatVectorOfUInt128: + if (!byte_size_option_set) + byte_size_value = 128; + if (!num_per_line_option_set) + m_num_per_line = 1; + if (!count_option_set) + count_value = 4; + break; + } + return error; + } + + bool + AnyOptionWasSet () const + { + return m_num_per_line.OptionWasSet() || + m_output_as_binary || + m_view_as_type.OptionWasSet(); + } + + OptionValueUInt64 m_num_per_line; + bool m_output_as_binary; + OptionValueString m_view_as_type; + bool m_force; +}; + + + +//---------------------------------------------------------------------- +// Read memory from the inferior process +//---------------------------------------------------------------------- +class CommandObjectMemoryRead : public CommandObjectParsed +{ +public: + + CommandObjectMemoryRead (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "memory read", + "Read from the memory of the process being debugged.", + NULL, + eFlagRequiresTarget | eFlagProcessMustBePaused), + m_option_group (interpreter), + m_format_options (eFormatBytesWithASCII, 1, 8), + m_memory_options (), + m_outfile_options (), + m_varobj_options(), + m_next_addr(LLDB_INVALID_ADDRESS), + m_prev_byte_size(0), + m_prev_format_options (eFormatBytesWithASCII, 1, 8), + m_prev_memory_options (), + m_prev_outfile_options (), + m_prev_varobj_options() + { + CommandArgumentEntry arg1; + CommandArgumentEntry arg2; + CommandArgumentData start_addr_arg; + CommandArgumentData end_addr_arg; + + // Define the first (and only) variant of this arg. + start_addr_arg.arg_type = eArgTypeAddressOrExpression; + start_addr_arg.arg_repetition = eArgRepeatPlain; + + // There is only one variant this argument could be; put it into the argument entry. + arg1.push_back (start_addr_arg); + + // Define the first (and only) variant of this arg. + end_addr_arg.arg_type = eArgTypeAddressOrExpression; + end_addr_arg.arg_repetition = eArgRepeatOptional; + + // There is only one variant this argument could be; put it into the argument entry. + arg2.push_back (end_addr_arg); + + // Push the data for the first argument into the m_arguments vector. + m_arguments.push_back (arg1); + m_arguments.push_back (arg2); + + // Add the "--format" and "--count" options to group 1 and 3 + m_option_group.Append (&m_format_options, + OptionGroupFormat::OPTION_GROUP_FORMAT | OptionGroupFormat::OPTION_GROUP_COUNT, + LLDB_OPT_SET_1 | LLDB_OPT_SET_2 | LLDB_OPT_SET_3); + m_option_group.Append (&m_format_options, + OptionGroupFormat::OPTION_GROUP_GDB_FMT, + LLDB_OPT_SET_1 | LLDB_OPT_SET_3); + // Add the "--size" option to group 1 and 2 + m_option_group.Append (&m_format_options, + OptionGroupFormat::OPTION_GROUP_SIZE, + LLDB_OPT_SET_1 | LLDB_OPT_SET_2); + m_option_group.Append (&m_memory_options); + m_option_group.Append (&m_outfile_options, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1 | LLDB_OPT_SET_2 | LLDB_OPT_SET_3); + m_option_group.Append (&m_varobj_options, LLDB_OPT_SET_ALL, LLDB_OPT_SET_3); + m_option_group.Finalize(); + } + + virtual + ~CommandObjectMemoryRead () + { + } + + Options * + GetOptions () + { + return &m_option_group; + } + + virtual const char *GetRepeatCommand (Args ¤t_command_args, uint32_t index) + { + return m_cmd_name.c_str(); + } + +protected: + virtual bool + DoExecute (Args& command, CommandReturnObject &result) + { + // No need to check "target" for validity as eFlagRequiresTarget ensures it is valid + Target *target = m_exe_ctx.GetTargetPtr(); + + const size_t argc = command.GetArgumentCount(); + + if ((argc == 0 && m_next_addr == LLDB_INVALID_ADDRESS) || argc > 2) + { + result.AppendErrorWithFormat ("%s takes a start address expression with an optional end address expression.\n", m_cmd_name.c_str()); + result.AppendRawWarning("Expressions should be quoted if they contain spaces or other special characters.\n"); + result.SetStatus(eReturnStatusFailed); + return false; + } + + ClangASTType clang_ast_type; + Error error; + + const char *view_as_type_cstr = m_memory_options.m_view_as_type.GetCurrentValue(); + if (view_as_type_cstr && view_as_type_cstr[0]) + { + // We are viewing memory as a type + + SymbolContext sc; + const bool exact_match = false; + TypeList type_list; + uint32_t reference_count = 0; + uint32_t pointer_count = 0; + size_t idx; + +#define ALL_KEYWORDS \ + KEYWORD("const") \ + KEYWORD("volatile") \ + KEYWORD("restrict") \ + KEYWORD("struct") \ + KEYWORD("class") \ + KEYWORD("union") + +#define KEYWORD(s) s, + static const char *g_keywords[] = + { + ALL_KEYWORDS + }; +#undef KEYWORD + +#define KEYWORD(s) (sizeof(s) - 1), + static const int g_keyword_lengths[] = + { + ALL_KEYWORDS + }; +#undef KEYWORD + +#undef ALL_KEYWORDS + + static size_t g_num_keywords = sizeof(g_keywords) / sizeof(const char *); + std::string type_str(view_as_type_cstr); + + // Remove all instances of g_keywords that are followed by spaces + for (size_t i = 0; i < g_num_keywords; ++i) + { + const char *keyword = g_keywords[i]; + int keyword_len = g_keyword_lengths[i]; + + idx = 0; + while ((idx = type_str.find (keyword, idx)) != std::string::npos) + { + if (type_str[idx + keyword_len] == ' ' || type_str[idx + keyword_len] == '\t') + { + type_str.erase(idx, keyword_len+1); + idx = 0; + } + else + { + idx += keyword_len; + } + } + } + bool done = type_str.empty(); + // + idx = type_str.find_first_not_of (" \t"); + if (idx > 0 && idx != std::string::npos) + type_str.erase (0, idx); + while (!done) + { + // Strip trailing spaces + if (type_str.empty()) + done = true; + else + { + switch (type_str[type_str.size()-1]) + { + case '*': + ++pointer_count; + // fall through... + case ' ': + case '\t': + type_str.erase(type_str.size()-1); + break; + + case '&': + if (reference_count == 0) + { + reference_count = 1; + type_str.erase(type_str.size()-1); + } + else + { + result.AppendErrorWithFormat ("invalid type string: '%s'\n", view_as_type_cstr); + result.SetStatus(eReturnStatusFailed); + return false; + } + break; + + default: + done = true; + break; + } + } + } + + ConstString lookup_type_name(type_str.c_str()); + StackFrame *frame = m_exe_ctx.GetFramePtr(); + if (frame) + { + sc = frame->GetSymbolContext (eSymbolContextModule); + if (sc.module_sp) + { + sc.module_sp->FindTypes (sc, + lookup_type_name, + exact_match, + 1, + type_list); + } + } + if (type_list.GetSize() == 0) + { + target->GetImages().FindTypes (sc, + lookup_type_name, + exact_match, + 1, + type_list); + } + + if (type_list.GetSize() == 0 && lookup_type_name.GetCString() && *lookup_type_name.GetCString() == '$') + { + clang::TypeDecl *tdecl = target->GetPersistentVariables().GetPersistentType(ConstString(lookup_type_name)); + if (tdecl) + { + clang_ast_type.SetClangType(&tdecl->getASTContext(),(lldb::clang_type_t)tdecl->getTypeForDecl()); + } + } + + if (clang_ast_type.IsValid() == false) + { + if (type_list.GetSize() == 0) + { + result.AppendErrorWithFormat ("unable to find any types that match the raw type '%s' for full type '%s'\n", + lookup_type_name.GetCString(), + view_as_type_cstr); + result.SetStatus(eReturnStatusFailed); + return false; + } + else + { + TypeSP type_sp (type_list.GetTypeAtIndex(0)); + clang_ast_type = type_sp->GetClangFullType(); + } + } + + while (pointer_count > 0) + { + ClangASTType pointer_type = clang_ast_type.GetPointerType(); + if (pointer_type.IsValid()) + clang_ast_type = pointer_type; + else + { + result.AppendError ("unable make a pointer type\n"); + result.SetStatus(eReturnStatusFailed); + return false; + } + --pointer_count; + } + + m_format_options.GetByteSizeValue() = clang_ast_type.GetByteSize(); + + if (m_format_options.GetByteSizeValue() == 0) + { + result.AppendErrorWithFormat ("unable to get the byte size of the type '%s'\n", + view_as_type_cstr); + result.SetStatus(eReturnStatusFailed); + return false; + } + + if (!m_format_options.GetCountValue().OptionWasSet()) + m_format_options.GetCountValue() = 1; + } + else + { + error = m_memory_options.FinalizeSettings (target, m_format_options); + } + + // Look for invalid combinations of settings + if (error.Fail()) + { + result.AppendError(error.AsCString()); + result.SetStatus(eReturnStatusFailed); + return false; + } + + lldb::addr_t addr; + size_t total_byte_size = 0; + if (argc == 0) + { + // Use the last address and byte size and all options as they were + // if no options have been set + addr = m_next_addr; + total_byte_size = m_prev_byte_size; + clang_ast_type = m_prev_clang_ast_type; + if (!m_format_options.AnyOptionWasSet() && + !m_memory_options.AnyOptionWasSet() && + !m_outfile_options.AnyOptionWasSet() && + !m_varobj_options.AnyOptionWasSet()) + { + m_format_options = m_prev_format_options; + m_memory_options = m_prev_memory_options; + m_outfile_options = m_prev_outfile_options; + m_varobj_options = m_prev_varobj_options; + } + } + + size_t item_count = m_format_options.GetCountValue().GetCurrentValue(); + size_t item_byte_size = m_format_options.GetByteSizeValue().GetCurrentValue(); + const size_t num_per_line = m_memory_options.m_num_per_line.GetCurrentValue(); + + if (total_byte_size == 0) + { + total_byte_size = item_count * item_byte_size; + if (total_byte_size == 0) + total_byte_size = 32; + } + + if (argc > 0) + addr = Args::StringToAddress(&m_exe_ctx, command.GetArgumentAtIndex(0), LLDB_INVALID_ADDRESS, &error); + + if (addr == LLDB_INVALID_ADDRESS) + { + result.AppendError("invalid start address expression."); + result.AppendError(error.AsCString()); + result.SetStatus(eReturnStatusFailed); + return false; + } + + if (argc == 2) + { + lldb::addr_t end_addr = Args::StringToAddress(&m_exe_ctx, command.GetArgumentAtIndex(1), LLDB_INVALID_ADDRESS, 0); + if (end_addr == LLDB_INVALID_ADDRESS) + { + result.AppendError("invalid end address expression."); + result.AppendError(error.AsCString()); + result.SetStatus(eReturnStatusFailed); + return false; + } + else if (end_addr <= addr) + { + result.AppendErrorWithFormat("end address (0x%" PRIx64 ") must be greater that the start address (0x%" PRIx64 ").\n", end_addr, addr); + result.SetStatus(eReturnStatusFailed); + return false; + } + else if (m_format_options.GetCountValue().OptionWasSet()) + { + result.AppendErrorWithFormat("specify either the end address (0x%" PRIx64 ") or the count (--count %lu), not both.\n", end_addr, item_count); + result.SetStatus(eReturnStatusFailed); + return false; + } + + total_byte_size = end_addr - addr; + item_count = total_byte_size / item_byte_size; + } + + uint32_t max_unforced_size = target->GetMaximumMemReadSize(); + + if (total_byte_size > max_unforced_size && !m_memory_options.m_force) + { + result.AppendErrorWithFormat("Normally, \'memory read\' will not read over %" PRIu32 " bytes of data.\n",max_unforced_size); + result.AppendErrorWithFormat("Please use --force to override this restriction just once.\n"); + result.AppendErrorWithFormat("or set target.max-memory-read-size if you will often need a larger limit.\n"); + return false; + } + + DataBufferSP data_sp; + size_t bytes_read = 0; + if (clang_ast_type.GetOpaqueQualType()) + { + // Make sure we don't display our type as ASCII bytes like the default memory read + if (m_format_options.GetFormatValue().OptionWasSet() == false) + m_format_options.GetFormatValue().SetCurrentValue(eFormatDefault); + + bytes_read = clang_ast_type.GetByteSize() * m_format_options.GetCountValue().GetCurrentValue(); + } + else if (m_format_options.GetFormatValue().GetCurrentValue() != eFormatCString) + { + data_sp.reset (new DataBufferHeap (total_byte_size, '\0')); + if (data_sp->GetBytes() == NULL) + { + result.AppendErrorWithFormat ("can't allocate 0x%zx bytes for the memory read buffer, specify a smaller size to read", total_byte_size); + result.SetStatus(eReturnStatusFailed); + return false; + } + + Address address(addr, NULL); + bytes_read = target->ReadMemory(address, false, data_sp->GetBytes (), data_sp->GetByteSize(), error); + if (bytes_read == 0) + { + const char *error_cstr = error.AsCString(); + if (error_cstr && error_cstr[0]) + { + result.AppendError(error_cstr); + } + else + { + result.AppendErrorWithFormat("failed to read memory from 0x%" PRIx64 ".\n", addr); + } + result.SetStatus(eReturnStatusFailed); + return false; + } + + if (bytes_read < total_byte_size) + result.AppendWarningWithFormat("Not all bytes (%lu/%lu) were able to be read from 0x%" PRIx64 ".\n", bytes_read, total_byte_size, addr); + } + else + { + // we treat c-strings as a special case because they do not have a fixed size + if (m_format_options.GetByteSizeValue().OptionWasSet() && !m_format_options.HasGDBFormat()) + item_byte_size = m_format_options.GetByteSizeValue().GetCurrentValue(); + else + item_byte_size = target->GetMaximumSizeOfStringSummary(); + if (!m_format_options.GetCountValue().OptionWasSet()) + item_count = 1; + data_sp.reset (new DataBufferHeap ((item_byte_size+1) * item_count, '\0')); // account for NULLs as necessary + if (data_sp->GetBytes() == NULL) + { + result.AppendErrorWithFormat ("can't allocate 0x%" PRIx64 " bytes for the memory read buffer, specify a smaller size to read", (uint64_t)((item_byte_size+1) * item_count)); + result.SetStatus(eReturnStatusFailed); + return false; + } + uint8_t *data_ptr = data_sp->GetBytes(); + auto data_addr = addr; + auto count = item_count; + item_count = 0; + while (item_count < count) + { + std::string buffer; + buffer.resize(item_byte_size+1,0); + Error error; + size_t read = target->ReadCStringFromMemory(data_addr, &buffer[0], item_byte_size+1, error); + if (error.Fail()) + { + result.AppendErrorWithFormat("failed to read memory from 0x%" PRIx64 ".\n", addr); + result.SetStatus(eReturnStatusFailed); + return false; + } + if (item_byte_size == read) + { + result.AppendWarningWithFormat("unable to find a NULL terminated string at 0x%" PRIx64 ".Consider increasing the maximum read length.\n", data_addr); + break; + } + read+=1; // account for final NULL byte + memcpy(data_ptr, &buffer[0], read); + data_ptr += read; + data_addr += read; + bytes_read += read; + item_count++; // if we break early we know we only read item_count strings + } + data_sp.reset(new DataBufferHeap(data_sp->GetBytes(),bytes_read+1)); + } + + m_next_addr = addr + bytes_read; + m_prev_byte_size = bytes_read; + m_prev_format_options = m_format_options; + m_prev_memory_options = m_memory_options; + m_prev_outfile_options = m_outfile_options; + m_prev_varobj_options = m_varobj_options; + m_prev_clang_ast_type = clang_ast_type; + + StreamFile outfile_stream; + Stream *output_stream = NULL; + const FileSpec &outfile_spec = m_outfile_options.GetFile().GetCurrentValue(); + if (outfile_spec) + { + char path[PATH_MAX]; + outfile_spec.GetPath (path, sizeof(path)); + + uint32_t open_options = File::eOpenOptionWrite | File::eOpenOptionCanCreate; + const bool append = m_outfile_options.GetAppend().GetCurrentValue(); + if (append) + open_options |= File::eOpenOptionAppend; + + if (outfile_stream.GetFile ().Open (path, open_options).Success()) + { + if (m_memory_options.m_output_as_binary) + { + const size_t bytes_written = outfile_stream.Write (data_sp->GetBytes(), bytes_read); + if (bytes_written > 0) + { + result.GetOutputStream().Printf ("%zi bytes %s to '%s'\n", + bytes_written, + append ? "appended" : "written", + path); + return true; + } + else + { + result.AppendErrorWithFormat("Failed to write %" PRIu64 " bytes to '%s'.\n", (uint64_t)bytes_read, path); + result.SetStatus(eReturnStatusFailed); + return false; + } + } + else + { + // We are going to write ASCII to the file just point the + // output_stream to our outfile_stream... + output_stream = &outfile_stream; + } + } + else + { + result.AppendErrorWithFormat("Failed to open file '%s' for %s.\n", path, append ? "append" : "write"); + result.SetStatus(eReturnStatusFailed); + return false; + } + } + else + { + output_stream = &result.GetOutputStream(); + } + + + ExecutionContextScope *exe_scope = m_exe_ctx.GetBestExecutionContextScope(); + if (clang_ast_type.GetOpaqueQualType()) + { + for (uint32_t i = 0; iSetFormat (format); + + ValueObject::DumpValueObjectOptions options(m_varobj_options.GetAsDumpOptions(false,format)); + + ValueObject::DumpValueObject (*output_stream, + valobj_sp.get(), + options); + } + else + { + result.AppendErrorWithFormat ("failed to create a value object for: (%s) %s\n", + view_as_type_cstr, + name_strm.GetString().c_str()); + result.SetStatus(eReturnStatusFailed); + return false; + } + } + return true; + } + + result.SetStatus(eReturnStatusSuccessFinishResult); + DataExtractor data (data_sp, + target->GetArchitecture().GetByteOrder(), + target->GetArchitecture().GetAddressByteSize()); + + Format format = m_format_options.GetFormat(); + if ( ( (format == eFormatChar) || (format == eFormatCharPrintable) ) + && (item_byte_size != 1) + && (item_count == 1)) + { + // this turns requests such as + // memory read -fc -s10 -c1 *charPtrPtr + // which make no sense (what is a char of size 10?) + // into a request for fetching 10 chars of size 1 from the same memory location + format = eFormatCharArray; + item_count = item_byte_size; + item_byte_size = 1; + } + + assert (output_stream); + size_t bytes_dumped = data.Dump (output_stream, + 0, + format, + item_byte_size, + item_count, + num_per_line, + addr, + 0, + 0, + exe_scope); + m_next_addr = addr + bytes_dumped; + output_stream->EOL(); + return true; + } + + OptionGroupOptions m_option_group; + OptionGroupFormat m_format_options; + OptionGroupReadMemory m_memory_options; + OptionGroupOutputFile m_outfile_options; + OptionGroupValueObjectDisplay m_varobj_options; + lldb::addr_t m_next_addr; + lldb::addr_t m_prev_byte_size; + OptionGroupFormat m_prev_format_options; + OptionGroupReadMemory m_prev_memory_options; + OptionGroupOutputFile m_prev_outfile_options; + OptionGroupValueObjectDisplay m_prev_varobj_options; + ClangASTType m_prev_clang_ast_type; +}; + + +OptionDefinition +g_memory_write_option_table[] = +{ +{ LLDB_OPT_SET_1, true, "infile", 'i', required_argument, NULL, 0, eArgTypeFilename, "Write memory using the contents of a file."}, +{ LLDB_OPT_SET_1, false, "offset", 'o', required_argument, NULL, 0, eArgTypeOffset, "Start writng bytes from an offset within the input file."}, +}; + + +//---------------------------------------------------------------------- +// Write memory to the inferior process +//---------------------------------------------------------------------- +class CommandObjectMemoryWrite : public CommandObjectParsed +{ +public: + + class OptionGroupWriteMemory : public OptionGroup + { + public: + OptionGroupWriteMemory () : + OptionGroup() + { + } + + virtual + ~OptionGroupWriteMemory () + { + } + + virtual uint32_t + GetNumDefinitions () + { + return sizeof (g_memory_write_option_table) / sizeof (OptionDefinition); + } + + virtual const OptionDefinition* + GetDefinitions () + { + return g_memory_write_option_table; + } + + virtual Error + SetOptionValue (CommandInterpreter &interpreter, + uint32_t option_idx, + const char *option_arg) + { + Error error; + const int short_option = g_memory_write_option_table[option_idx].short_option; + + switch (short_option) + { + case 'i': + m_infile.SetFile (option_arg, true); + if (!m_infile.Exists()) + { + m_infile.Clear(); + error.SetErrorStringWithFormat("input file does not exist: '%s'", option_arg); + } + break; + + case 'o': + { + bool success; + m_infile_offset = Args::StringToUInt64(option_arg, 0, 0, &success); + if (!success) + { + error.SetErrorStringWithFormat("invalid offset string '%s'", option_arg); + } + } + break; + + default: + error.SetErrorStringWithFormat("unrecognized short option '%c'", short_option); + break; + } + return error; + } + + virtual void + OptionParsingStarting (CommandInterpreter &interpreter) + { + m_infile.Clear(); + m_infile_offset = 0; + } + + FileSpec m_infile; + off_t m_infile_offset; + }; + + CommandObjectMemoryWrite (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "memory write", + "Write to the memory of the process being debugged.", + NULL, + eFlagRequiresProcess | eFlagProcessMustBeLaunched), + m_option_group (interpreter), + m_format_options (eFormatBytes, 1, UINT64_MAX), + m_memory_options () + { + CommandArgumentEntry arg1; + CommandArgumentEntry arg2; + CommandArgumentData addr_arg; + CommandArgumentData value_arg; + + // Define the first (and only) variant of this arg. + addr_arg.arg_type = eArgTypeAddress; + addr_arg.arg_repetition = eArgRepeatPlain; + + // There is only one variant this argument could be; put it into the argument entry. + arg1.push_back (addr_arg); + + // Define the first (and only) variant of this arg. + value_arg.arg_type = eArgTypeValue; + value_arg.arg_repetition = eArgRepeatPlus; + + // There is only one variant this argument could be; put it into the argument entry. + arg2.push_back (value_arg); + + // Push the data for the first argument into the m_arguments vector. + m_arguments.push_back (arg1); + m_arguments.push_back (arg2); + + m_option_group.Append (&m_format_options, OptionGroupFormat::OPTION_GROUP_FORMAT, LLDB_OPT_SET_1); + m_option_group.Append (&m_format_options, OptionGroupFormat::OPTION_GROUP_SIZE , LLDB_OPT_SET_1|LLDB_OPT_SET_2); + m_option_group.Append (&m_memory_options, LLDB_OPT_SET_ALL, LLDB_OPT_SET_2); + m_option_group.Finalize(); + + } + + virtual + ~CommandObjectMemoryWrite () + { + } + + Options * + GetOptions () + { + return &m_option_group; + } + + bool + UIntValueIsValidForSize (uint64_t uval64, size_t total_byte_size) + { + if (total_byte_size > 8) + return false; + + if (total_byte_size == 8) + return true; + + const uint64_t max = ((uint64_t)1 << (uint64_t)(total_byte_size * 8)) - 1; + return uval64 <= max; + } + + bool + SIntValueIsValidForSize (int64_t sval64, size_t total_byte_size) + { + if (total_byte_size > 8) + return false; + + if (total_byte_size == 8) + return true; + + const int64_t max = ((int64_t)1 << (uint64_t)(total_byte_size * 8 - 1)) - 1; + const int64_t min = ~(max); + return min <= sval64 && sval64 <= max; + } + +protected: + virtual bool + DoExecute (Args& command, CommandReturnObject &result) + { + // No need to check "process" for validity as eFlagRequiresProcess ensures it is valid + Process *process = m_exe_ctx.GetProcessPtr(); + + const size_t argc = command.GetArgumentCount(); + + if (m_memory_options.m_infile) + { + if (argc < 1) + { + result.AppendErrorWithFormat ("%s takes a destination address when writing file contents.\n", m_cmd_name.c_str()); + result.SetStatus(eReturnStatusFailed); + return false; + } + } + else if (argc < 2) + { + result.AppendErrorWithFormat ("%s takes a destination address and at least one value.\n", m_cmd_name.c_str()); + result.SetStatus(eReturnStatusFailed); + return false; + } + + StreamString buffer (Stream::eBinary, + process->GetTarget().GetArchitecture().GetAddressByteSize(), + process->GetTarget().GetArchitecture().GetByteOrder()); + + OptionValueUInt64 &byte_size_value = m_format_options.GetByteSizeValue(); + size_t item_byte_size = byte_size_value.GetCurrentValue(); + + Error error; + lldb::addr_t addr = Args::StringToAddress (&m_exe_ctx, + command.GetArgumentAtIndex(0), + LLDB_INVALID_ADDRESS, + &error); + + if (addr == LLDB_INVALID_ADDRESS) + { + result.AppendError("invalid address expression\n"); + result.AppendError(error.AsCString()); + result.SetStatus(eReturnStatusFailed); + return false; + } + + if (m_memory_options.m_infile) + { + size_t length = SIZE_MAX; + if (item_byte_size > 0) + length = item_byte_size; + lldb::DataBufferSP data_sp (m_memory_options.m_infile.ReadFileContents (m_memory_options.m_infile_offset, length)); + if (data_sp) + { + length = data_sp->GetByteSize(); + if (length > 0) + { + Error error; + size_t bytes_written = process->WriteMemory (addr, data_sp->GetBytes(), length, error); + + if (bytes_written == length) + { + // All bytes written + result.GetOutputStream().Printf("%" PRIu64 " bytes were written to 0x%" PRIx64 "\n", (uint64_t)bytes_written, addr); + result.SetStatus(eReturnStatusSuccessFinishResult); + } + else if (bytes_written > 0) + { + // Some byte written + result.GetOutputStream().Printf("%" PRIu64 " bytes of %" PRIu64 " requested were written to 0x%" PRIx64 "\n", (uint64_t)bytes_written, (uint64_t)length, addr); + result.SetStatus(eReturnStatusSuccessFinishResult); + } + else + { + result.AppendErrorWithFormat ("Memory write to 0x%" PRIx64 " failed: %s.\n", addr, error.AsCString()); + result.SetStatus(eReturnStatusFailed); + } + } + } + else + { + result.AppendErrorWithFormat ("Unable to read contents of file.\n"); + result.SetStatus(eReturnStatusFailed); + } + return result.Succeeded(); + } + else if (item_byte_size == 0) + { + if (m_format_options.GetFormat() == eFormatPointer) + item_byte_size = buffer.GetAddressByteSize(); + else + item_byte_size = 1; + } + + command.Shift(); // shift off the address argument + uint64_t uval64; + int64_t sval64; + bool success = false; + const size_t num_value_args = command.GetArgumentCount(); + for (size_t i=0; iWriteMemory (addr, value_str, len, error) == len) + { + addr += len; + } + else + { + result.AppendErrorWithFormat ("Memory write to 0x%" PRIx64 " failed: %s.\n", addr, error.AsCString()); + result.SetStatus(eReturnStatusFailed); + return false; + } + } + break; + + case eFormatDecimal: + sval64 = Args::StringToSInt64(value_str, INT64_MAX, 0, &success); + if (!success) + { + result.AppendErrorWithFormat ("'%s' is not a valid signed decimal value.\n", value_str); + result.SetStatus(eReturnStatusFailed); + return false; + } + else if (!SIntValueIsValidForSize (sval64, item_byte_size)) + { + result.AppendErrorWithFormat ("Value %" PRIi64 " is too large or small to fit in a %lu byte signed integer value.\n", sval64, item_byte_size); + result.SetStatus(eReturnStatusFailed); + return false; + } + buffer.PutMaxHex64 (sval64, item_byte_size); + break; + + case eFormatUnsigned: + uval64 = Args::StringToUInt64(value_str, UINT64_MAX, 0, &success); + if (!success) + { + result.AppendErrorWithFormat ("'%s' is not a valid unsigned decimal string value.\n", value_str); + result.SetStatus(eReturnStatusFailed); + return false; + } + else if (!UIntValueIsValidForSize (uval64, item_byte_size)) + { + result.AppendErrorWithFormat ("Value %" PRIu64 " is too large to fit in a %lu byte unsigned integer value.\n", uval64, item_byte_size); + result.SetStatus(eReturnStatusFailed); + return false; + } + buffer.PutMaxHex64 (uval64, item_byte_size); + break; + + case eFormatOctal: + uval64 = Args::StringToUInt64(value_str, UINT64_MAX, 8, &success); + if (!success) + { + result.AppendErrorWithFormat ("'%s' is not a valid octal string value.\n", value_str); + result.SetStatus(eReturnStatusFailed); + return false; + } + else if (!UIntValueIsValidForSize (uval64, item_byte_size)) + { + result.AppendErrorWithFormat ("Value %" PRIo64 " is too large to fit in a %lu byte unsigned integer value.\n", uval64, item_byte_size); + result.SetStatus(eReturnStatusFailed); + return false; + } + buffer.PutMaxHex64 (uval64, item_byte_size); + break; + } + } + + if (!buffer.GetString().empty()) + { + Error error; + if (process->WriteMemory (addr, buffer.GetString().c_str(), buffer.GetString().size(), error) == buffer.GetString().size()) + return true; + else + { + result.AppendErrorWithFormat ("Memory write to 0x%" PRIx64 " failed: %s.\n", addr, error.AsCString()); + result.SetStatus(eReturnStatusFailed); + return false; + } + } + return true; + } + + OptionGroupOptions m_option_group; + OptionGroupFormat m_format_options; + OptionGroupWriteMemory m_memory_options; +}; + + +//------------------------------------------------------------------------- +// CommandObjectMemory +//------------------------------------------------------------------------- + +CommandObjectMemory::CommandObjectMemory (CommandInterpreter &interpreter) : + CommandObjectMultiword (interpreter, + "memory", + "A set of commands for operating on memory.", + "memory []") +{ + LoadSubCommand ("read", CommandObjectSP (new CommandObjectMemoryRead (interpreter))); + LoadSubCommand ("write", CommandObjectSP (new CommandObjectMemoryWrite (interpreter))); +} + +CommandObjectMemory::~CommandObjectMemory () +{ +} diff --git a/contrib/llvm/tools/lldb/source/Commands/CommandObjectMemory.h b/contrib/llvm/tools/lldb/source/Commands/CommandObjectMemory.h new file mode 100644 index 00000000000..b044921ae07 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Commands/CommandObjectMemory.h @@ -0,0 +1,33 @@ +//===-- CommandObjectMemory.h -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_CommandObjectMemory_h_ +#define liblldb_CommandObjectMemory_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Interpreter/CommandObjectMultiword.h" + +namespace lldb_private { + +class CommandObjectMemory : public CommandObjectMultiword +{ +public: + CommandObjectMemory (CommandInterpreter &interpreter); + + virtual + ~CommandObjectMemory (); +}; + + +} // namespace lldb_private + +#endif // liblldb_CommandObjectMemory_h_ diff --git a/contrib/llvm/tools/lldb/source/Commands/CommandObjectMultiword.cpp b/contrib/llvm/tools/lldb/source/Commands/CommandObjectMultiword.cpp new file mode 100644 index 00000000000..f84b401f3aa --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Commands/CommandObjectMultiword.cpp @@ -0,0 +1,520 @@ +//===-- CommandObjectMultiword.cpp ------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "lldb/Interpreter/CommandObjectMultiword.h" +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/Debugger.h" +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Interpreter/Options.h" +#include "lldb/Interpreter/CommandReturnObject.h" + +using namespace lldb; +using namespace lldb_private; + +//------------------------------------------------------------------------- +// CommandObjectMultiword +//------------------------------------------------------------------------- + +CommandObjectMultiword::CommandObjectMultiword +( + CommandInterpreter &interpreter, + const char *name, + const char *help, + const char *syntax, + uint32_t flags +) : + CommandObject (interpreter, name, help, syntax, flags), + m_can_be_removed(false) +{ +} + +CommandObjectMultiword::~CommandObjectMultiword () +{ +} + +CommandObjectSP +CommandObjectMultiword::GetSubcommandSP (const char *sub_cmd, StringList *matches) +{ + CommandObjectSP return_cmd_sp; + CommandObject::CommandMap::iterator pos; + + if (!m_subcommand_dict.empty()) + { + pos = m_subcommand_dict.find (sub_cmd); + if (pos != m_subcommand_dict.end()) { + // An exact match; append the sub_cmd to the 'matches' string list. + if (matches) + matches->AppendString(sub_cmd); + return_cmd_sp = pos->second; + } + else + { + + StringList local_matches; + if (matches == NULL) + matches = &local_matches; + int num_matches = CommandObject::AddNamesMatchingPartialString (m_subcommand_dict, sub_cmd, *matches); + + if (num_matches == 1) + { + // Cleaner, but slightly less efficient would be to call back into this function, since I now + // know I have an exact match... + + sub_cmd = matches->GetStringAtIndex(0); + pos = m_subcommand_dict.find(sub_cmd); + if (pos != m_subcommand_dict.end()) + return_cmd_sp = pos->second; + } + } + } + return return_cmd_sp; +} + +CommandObject * +CommandObjectMultiword::GetSubcommandObject (const char *sub_cmd, StringList *matches) +{ + return GetSubcommandSP(sub_cmd, matches).get(); +} + +bool +CommandObjectMultiword::LoadSubCommand +( + const char *name, + const CommandObjectSP& cmd_obj +) +{ + CommandMap::iterator pos; + bool success = true; + + pos = m_subcommand_dict.find(name); + if (pos == m_subcommand_dict.end()) + { + m_subcommand_dict[name] = cmd_obj; + } + else + success = false; + + return success; +} + +bool +CommandObjectMultiword::Execute(const char *args_string, CommandReturnObject &result) +{ + Args args (args_string); + const size_t argc = args.GetArgumentCount(); + if (argc == 0) + { + this->CommandObject::GenerateHelpText (result); + } + else + { + const char *sub_command = args.GetArgumentAtIndex (0); + + if (sub_command) + { + if (::strcasecmp (sub_command, "help") == 0) + { + this->CommandObject::GenerateHelpText (result); + } + else if (!m_subcommand_dict.empty()) + { + StringList matches; + CommandObject *sub_cmd_obj = GetSubcommandObject(sub_command, &matches); + if (sub_cmd_obj != NULL) + { + // Now call CommandObject::Execute to process and options in 'rest_of_line'. From there + // the command-specific version of Execute will be called, with the processed arguments. + + args.Shift(); + + sub_cmd_obj->Execute (args_string, result); + } + else + { + std::string error_msg; + const size_t num_subcmd_matches = matches.GetSize(); + if (num_subcmd_matches > 0) + error_msg.assign ("ambiguous command "); + else + error_msg.assign ("invalid command "); + + error_msg.append ("'"); + error_msg.append (GetCommandName()); + error_msg.append (" "); + error_msg.append (sub_command); + error_msg.append ("'"); + + if (num_subcmd_matches > 0) + { + error_msg.append (" Possible completions:"); + for (size_t i = 0; i < num_subcmd_matches; i++) + { + error_msg.append ("\n\t"); + error_msg.append (matches.GetStringAtIndex (i)); + } + } + error_msg.append ("\n"); + result.AppendRawError (error_msg.c_str()); + result.SetStatus (eReturnStatusFailed); + } + } + else + { + result.AppendErrorWithFormat ("'%s' does not have any subcommands.\n", GetCommandName()); + result.SetStatus (eReturnStatusFailed); + } + } + } + + return result.Succeeded(); +} + +void +CommandObjectMultiword::GenerateHelpText (Stream &output_stream) +{ + // First time through here, generate the help text for the object and + // push it to the return result object as well + + output_stream.PutCString ("The following subcommands are supported:\n\n"); + + CommandMap::iterator pos; + uint32_t max_len = m_interpreter.FindLongestCommandWord (m_subcommand_dict); + + if (max_len) + max_len += 4; // Indent the output by 4 spaces. + + for (pos = m_subcommand_dict.begin(); pos != m_subcommand_dict.end(); ++pos) + { + std::string indented_command (" "); + indented_command.append (pos->first); + if (pos->second->WantsRawCommandString ()) + { + std::string help_text (pos->second->GetHelp()); + help_text.append (" This command takes 'raw' input (no need to quote stuff)."); + m_interpreter.OutputFormattedHelpText (output_stream, + indented_command.c_str(), + "--", + help_text.c_str(), + max_len); + } + else + m_interpreter.OutputFormattedHelpText (output_stream, + indented_command.c_str(), + "--", + pos->second->GetHelp(), + max_len); + } + + output_stream.PutCString ("\nFor more help on any particular subcommand, type 'help '.\n"); +} + +int +CommandObjectMultiword::HandleCompletion +( + Args &input, + int &cursor_index, + int &cursor_char_position, + int match_start_point, + int max_return_elements, + bool &word_complete, + StringList &matches +) +{ + // Any of the command matches will provide a complete word, otherwise the individual + // completers will override this. + word_complete = true; + + if (cursor_index == 0) + { + CommandObject::AddNamesMatchingPartialString (m_subcommand_dict, + input.GetArgumentAtIndex(0), + matches); + + if (matches.GetSize() == 1 + && matches.GetStringAtIndex(0) != NULL + && strcmp (input.GetArgumentAtIndex(0), matches.GetStringAtIndex(0)) == 0) + { + StringList temp_matches; + CommandObject *cmd_obj = GetSubcommandObject (input.GetArgumentAtIndex(0), + &temp_matches); + if (cmd_obj != NULL) + { + matches.DeleteStringAtIndex (0); + input.Shift(); + cursor_char_position = 0; + input.AppendArgument (""); + return cmd_obj->HandleCompletion (input, + cursor_index, + cursor_char_position, + match_start_point, + max_return_elements, + word_complete, + matches); + } + else + return matches.GetSize(); + } + else + return matches.GetSize(); + } + else + { + CommandObject *sub_command_object = GetSubcommandObject (input.GetArgumentAtIndex(0), + &matches); + if (sub_command_object == NULL) + { + return matches.GetSize(); + } + else + { + // Remove the one match that we got from calling GetSubcommandObject. + matches.DeleteStringAtIndex(0); + input.Shift(); + cursor_index--; + return sub_command_object->HandleCompletion (input, + cursor_index, + cursor_char_position, + match_start_point, + max_return_elements, + word_complete, + matches); + } + + } +} + +const char * +CommandObjectMultiword::GetRepeatCommand (Args ¤t_command_args, uint32_t index) +{ + index++; + if (current_command_args.GetArgumentCount() <= index) + return NULL; + CommandObject *sub_command_object = GetSubcommandObject (current_command_args.GetArgumentAtIndex(index)); + if (sub_command_object == NULL) + return NULL; + return sub_command_object->GetRepeatCommand(current_command_args, index); +} + + +void +CommandObjectMultiword::AproposAllSubCommands (const char *prefix, + const char *search_word, + StringList &commands_found, + StringList &commands_help) +{ + CommandObject::CommandMap::const_iterator pos; + + for (pos = m_subcommand_dict.begin(); pos != m_subcommand_dict.end(); ++pos) + { + const char * command_name = pos->first.c_str(); + CommandObject *sub_cmd_obj = pos->second.get(); + StreamString complete_command_name; + + complete_command_name.Printf ("%s %s", prefix, command_name); + + if (sub_cmd_obj->HelpTextContainsWord (search_word)) + { + commands_found.AppendString (complete_command_name.GetData()); + commands_help.AppendString (sub_cmd_obj->GetHelp()); + } + + if (sub_cmd_obj->IsMultiwordObject()) + sub_cmd_obj->AproposAllSubCommands (complete_command_name.GetData(), + search_word, + commands_found, + commands_help); + } +} + + + +CommandObjectProxy::CommandObjectProxy (CommandInterpreter &interpreter, + const char *name, + const char *help, + const char *syntax, + uint32_t flags) : + CommandObject (interpreter, name, help, syntax, flags) +{ +} + +CommandObjectProxy::~CommandObjectProxy () +{ +} + +const char * +CommandObjectProxy::GetHelpLong () +{ + CommandObject *proxy_command = GetProxyCommandObject(); + if (proxy_command) + return proxy_command->GetHelpLong(); + return NULL; +} + +bool +CommandObjectProxy::IsRemovable() const +{ + const CommandObject *proxy_command = const_cast(this)->GetProxyCommandObject(); + if (proxy_command) + return proxy_command->IsRemovable(); + return false; +} + +bool +CommandObjectProxy::IsMultiwordObject () +{ + CommandObject *proxy_command = GetProxyCommandObject(); + if (proxy_command) + return proxy_command->IsMultiwordObject(); + return false; +} + +lldb::CommandObjectSP +CommandObjectProxy::GetSubcommandSP (const char *sub_cmd, StringList *matches) +{ + CommandObject *proxy_command = GetProxyCommandObject(); + if (proxy_command) + return proxy_command->GetSubcommandSP(sub_cmd, matches); + return lldb::CommandObjectSP(); +} + +CommandObject * +CommandObjectProxy::GetSubcommandObject (const char *sub_cmd, StringList *matches) +{ + CommandObject *proxy_command = GetProxyCommandObject(); + if (proxy_command) + return proxy_command->GetSubcommandObject(sub_cmd, matches); + return NULL; +} + +void +CommandObjectProxy::AproposAllSubCommands (const char *prefix, + const char *search_word, + StringList &commands_found, + StringList &commands_help) +{ + CommandObject *proxy_command = GetProxyCommandObject(); + if (proxy_command) + return proxy_command->AproposAllSubCommands (prefix, + search_word, + commands_found, + commands_help); +} + +bool +CommandObjectProxy::LoadSubCommand (const char *cmd_name, + const lldb::CommandObjectSP& command_sp) +{ + CommandObject *proxy_command = GetProxyCommandObject(); + if (proxy_command) + return proxy_command->LoadSubCommand (cmd_name, command_sp); + return false; +} + +bool +CommandObjectProxy::WantsRawCommandString() +{ + CommandObject *proxy_command = GetProxyCommandObject(); + if (proxy_command) + return proxy_command->WantsRawCommandString(); + return false; +} + +bool +CommandObjectProxy::WantsCompletion() +{ + CommandObject *proxy_command = GetProxyCommandObject(); + if (proxy_command) + return proxy_command->WantsCompletion(); + return false; +} + + +Options * +CommandObjectProxy::GetOptions () +{ + CommandObject *proxy_command = GetProxyCommandObject(); + if (proxy_command) + return proxy_command->GetOptions (); + return NULL; +} + + +int +CommandObjectProxy::HandleCompletion (Args &input, + int &cursor_index, + int &cursor_char_position, + int match_start_point, + int max_return_elements, + bool &word_complete, + StringList &matches) +{ + CommandObject *proxy_command = GetProxyCommandObject(); + if (proxy_command) + return proxy_command->HandleCompletion (input, + cursor_index, + cursor_char_position, + match_start_point, + max_return_elements, + word_complete, + matches); + matches.Clear(); + return 0; +} +int +CommandObjectProxy::HandleArgumentCompletion (Args &input, + int &cursor_index, + int &cursor_char_position, + OptionElementVector &opt_element_vector, + int match_start_point, + int max_return_elements, + bool &word_complete, + StringList &matches) +{ + CommandObject *proxy_command = GetProxyCommandObject(); + if (proxy_command) + return proxy_command->HandleArgumentCompletion (input, + cursor_index, + cursor_char_position, + opt_element_vector, + match_start_point, + max_return_elements, + word_complete, + matches); + matches.Clear(); + return 0; +} + +const char * +CommandObjectProxy::GetRepeatCommand (Args ¤t_command_args, + uint32_t index) +{ + CommandObject *proxy_command = GetProxyCommandObject(); + if (proxy_command) + return proxy_command->GetRepeatCommand (current_command_args, index); + return NULL; +} + +bool +CommandObjectProxy::Execute (const char *args_string, + CommandReturnObject &result) +{ + CommandObject *proxy_command = GetProxyCommandObject(); + if (proxy_command) + return proxy_command->Execute (args_string, result); + result.AppendError ("command is not implemented"); + result.SetStatus (eReturnStatusFailed); + return false; +} + + diff --git a/contrib/llvm/tools/lldb/source/Commands/CommandObjectPlatform.cpp b/contrib/llvm/tools/lldb/source/Commands/CommandObjectPlatform.cpp new file mode 100644 index 00000000000..c2185e598ad --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Commands/CommandObjectPlatform.cpp @@ -0,0 +1,987 @@ +//===-- CommandObjectPlatform.cpp -------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "CommandObjectPlatform.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/Debugger.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Interpreter/Args.h" +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Interpreter/CommandReturnObject.h" +#include "lldb/Interpreter/OptionGroupPlatform.h" +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/Platform.h" +#include "lldb/Target/Process.h" + +using namespace lldb; +using namespace lldb_private; + + +//---------------------------------------------------------------------- +// "platform select " +//---------------------------------------------------------------------- +class CommandObjectPlatformSelect : public CommandObjectParsed +{ +public: + CommandObjectPlatformSelect (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "platform select", + "Create a platform if needed and select it as the current platform.", + "platform select ", + 0), + m_option_group (interpreter), + m_platform_options (false) // Don't include the "--platform" option by passing false + { + m_option_group.Append (&m_platform_options, LLDB_OPT_SET_ALL, 1); + m_option_group.Finalize(); + } + + virtual + ~CommandObjectPlatformSelect () + { + } + + virtual int + HandleCompletion (Args &input, + int &cursor_index, + int &cursor_char_position, + int match_start_point, + int max_return_elements, + bool &word_complete, + StringList &matches) + { + std::string completion_str (input.GetArgumentAtIndex(cursor_index)); + completion_str.erase (cursor_char_position); + + CommandCompletions::PlatformPluginNames (m_interpreter, + completion_str.c_str(), + match_start_point, + max_return_elements, + NULL, + word_complete, + matches); + return matches.GetSize(); + } + + virtual Options * + GetOptions () + { + return &m_option_group; + } + +protected: + virtual bool + DoExecute (Args& args, CommandReturnObject &result) + { + if (args.GetArgumentCount() == 1) + { + const char *platform_name = args.GetArgumentAtIndex (0); + if (platform_name && platform_name[0]) + { + const bool select = true; + m_platform_options.SetPlatformName (platform_name); + Error error; + ArchSpec platform_arch; + PlatformSP platform_sp (m_platform_options.CreatePlatformWithOptions (m_interpreter, ArchSpec(), select, error, platform_arch)); + if (platform_sp) + { + platform_sp->GetStatus (result.GetOutputStream()); + result.SetStatus (eReturnStatusSuccessFinishResult); + } + else + { + result.AppendError(error.AsCString()); + result.SetStatus (eReturnStatusFailed); + } + } + else + { + result.AppendError ("invalid platform name"); + result.SetStatus (eReturnStatusFailed); + } + } + else + { + result.AppendError ("platform create takes a platform name as an argument\n"); + result.SetStatus (eReturnStatusFailed); + } + return result.Succeeded(); + } + + OptionGroupOptions m_option_group; + OptionGroupPlatform m_platform_options; +}; + +//---------------------------------------------------------------------- +// "platform list" +//---------------------------------------------------------------------- +class CommandObjectPlatformList : public CommandObjectParsed +{ +public: + CommandObjectPlatformList (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "platform list", + "List all platforms that are available.", + NULL, + 0) + { + } + + virtual + ~CommandObjectPlatformList () + { + } + +protected: + virtual bool + DoExecute (Args& args, CommandReturnObject &result) + { + Stream &ostrm = result.GetOutputStream(); + ostrm.Printf("Available platforms:\n"); + + PlatformSP host_platform_sp (Platform::GetDefaultPlatform()); + ostrm.Printf ("%s: %s\n", + host_platform_sp->GetPluginName().GetCString(), + host_platform_sp->GetDescription()); + + uint32_t idx; + for (idx = 0; 1; ++idx) + { + const char *plugin_name = PluginManager::GetPlatformPluginNameAtIndex (idx); + if (plugin_name == NULL) + break; + const char *plugin_desc = PluginManager::GetPlatformPluginDescriptionAtIndex (idx); + if (plugin_desc == NULL) + break; + ostrm.Printf("%s: %s\n", plugin_name, plugin_desc); + } + + if (idx == 0) + { + result.AppendError ("no platforms are available\n"); + result.SetStatus (eReturnStatusFailed); + } + else + result.SetStatus (eReturnStatusSuccessFinishResult); + return result.Succeeded(); + } +}; + +//---------------------------------------------------------------------- +// "platform status" +//---------------------------------------------------------------------- +class CommandObjectPlatformStatus : public CommandObjectParsed +{ +public: + CommandObjectPlatformStatus (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "platform status", + "Display status for the currently selected platform.", + NULL, + 0) + { + } + + virtual + ~CommandObjectPlatformStatus () + { + } + +protected: + virtual bool + DoExecute (Args& args, CommandReturnObject &result) + { + Stream &ostrm = result.GetOutputStream(); + + Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get(); + PlatformSP platform_sp; + if (target) + { + platform_sp = target->GetPlatform(); + } + if (!platform_sp) + { + platform_sp = m_interpreter.GetDebugger().GetPlatformList().GetSelectedPlatform(); + } + if (platform_sp) + { + platform_sp->GetStatus (ostrm); + result.SetStatus (eReturnStatusSuccessFinishResult); + } + else + { + result.AppendError ("no platform us currently selected\n"); + result.SetStatus (eReturnStatusFailed); + } + return result.Succeeded(); + } +}; + +//---------------------------------------------------------------------- +// "platform connect " +//---------------------------------------------------------------------- +class CommandObjectPlatformConnect : public CommandObjectParsed +{ +public: + CommandObjectPlatformConnect (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "platform connect", + "Connect a platform by name to be the currently selected platform.", + "platform connect ", + 0) + { + } + + virtual + ~CommandObjectPlatformConnect () + { + } + +protected: + virtual bool + DoExecute (Args& args, CommandReturnObject &result) + { + Stream &ostrm = result.GetOutputStream(); + + PlatformSP platform_sp (m_interpreter.GetDebugger().GetPlatformList().GetSelectedPlatform()); + if (platform_sp) + { + Error error (platform_sp->ConnectRemote (args)); + if (error.Success()) + { + platform_sp->GetStatus (ostrm); + result.SetStatus (eReturnStatusSuccessFinishResult); + } + else + { + result.AppendErrorWithFormat ("%s\n", error.AsCString()); + result.SetStatus (eReturnStatusFailed); + } + } + else + { + result.AppendError ("no platform us currently selected\n"); + result.SetStatus (eReturnStatusFailed); + } + return result.Succeeded(); + } +}; + +//---------------------------------------------------------------------- +// "platform disconnect" +//---------------------------------------------------------------------- +class CommandObjectPlatformDisconnect : public CommandObjectParsed +{ +public: + CommandObjectPlatformDisconnect (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "platform disconnect", + "Disconnect a platform by name to be the currently selected platform.", + "platform disconnect", + 0) + { + } + + virtual + ~CommandObjectPlatformDisconnect () + { + } + +protected: + virtual bool + DoExecute (Args& args, CommandReturnObject &result) + { + PlatformSP platform_sp (m_interpreter.GetDebugger().GetPlatformList().GetSelectedPlatform()); + if (platform_sp) + { + if (args.GetArgumentCount() == 0) + { + Error error; + + if (platform_sp->IsConnected()) + { + // Cache the instance name if there is one since we are + // about to disconnect and the name might go with it. + const char *hostname_cstr = platform_sp->GetHostname(); + std::string hostname; + if (hostname_cstr) + hostname.assign (hostname_cstr); + + error = platform_sp->DisconnectRemote (); + if (error.Success()) + { + Stream &ostrm = result.GetOutputStream(); + if (hostname.empty()) + ostrm.Printf ("Disconnected from \"%s\"\n", platform_sp->GetPluginName().GetCString()); + else + ostrm.Printf ("Disconnected from \"%s\"\n", hostname.c_str()); + result.SetStatus (eReturnStatusSuccessFinishResult); + } + else + { + result.AppendErrorWithFormat ("%s", error.AsCString()); + result.SetStatus (eReturnStatusFailed); + } + } + else + { + // Not connected... + result.AppendErrorWithFormat ("not connected to '%s'", platform_sp->GetPluginName().GetCString()); + result.SetStatus (eReturnStatusFailed); + } + } + else + { + // Bad args + result.AppendError ("\"platform disconnect\" doesn't take any arguments"); + result.SetStatus (eReturnStatusFailed); + } + } + else + { + result.AppendError ("no platform is currently selected"); + result.SetStatus (eReturnStatusFailed); + } + return result.Succeeded(); + } +}; +//---------------------------------------------------------------------- +// "platform process launch" +//---------------------------------------------------------------------- +class CommandObjectPlatformProcessLaunch : public CommandObjectParsed +{ +public: + CommandObjectPlatformProcessLaunch (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "platform process launch", + "Launch a new process on a remote platform.", + "platform process launch program", + eFlagRequiresTarget | eFlagTryTargetAPILock), + m_options (interpreter) + { + } + + virtual + ~CommandObjectPlatformProcessLaunch () + { + } + + virtual Options * + GetOptions () + { + return &m_options; + } + +protected: + virtual bool + DoExecute (Args& args, CommandReturnObject &result) + { + Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get(); + PlatformSP platform_sp; + if (target) + { + platform_sp = target->GetPlatform(); + } + if (!platform_sp) + { + platform_sp = m_interpreter.GetDebugger().GetPlatformList().GetSelectedPlatform(); + } + + if (platform_sp) + { + Error error; + const size_t argc = args.GetArgumentCount(); + Target *target = m_exe_ctx.GetTargetPtr(); + Module *exe_module = target->GetExecutableModulePointer(); + if (exe_module) + { + m_options.launch_info.GetExecutableFile () = exe_module->GetFileSpec(); + char exe_path[PATH_MAX]; + if (m_options.launch_info.GetExecutableFile ().GetPath (exe_path, sizeof(exe_path))) + m_options.launch_info.GetArguments().AppendArgument (exe_path); + m_options.launch_info.GetArchitecture() = exe_module->GetArchitecture(); + } + + if (argc > 0) + { + if (m_options.launch_info.GetExecutableFile ()) + { + // We already have an executable file, so we will use this + // and all arguments to this function are extra arguments + m_options.launch_info.GetArguments().AppendArguments (args); + } + else + { + // We don't have any file yet, so the first argument is our + // executable, and the rest are program arguments + const bool first_arg_is_executable = true; + m_options.launch_info.SetArguments (args, first_arg_is_executable); + } + } + + if (m_options.launch_info.GetExecutableFile ()) + { + Debugger &debugger = m_interpreter.GetDebugger(); + + if (argc == 0) + target->GetRunArguments(m_options.launch_info.GetArguments()); + + ProcessSP process_sp (platform_sp->DebugProcess (m_options.launch_info, + debugger, + target, + debugger.GetListener(), + error)); + if (process_sp && process_sp->IsAlive()) + { + result.SetStatus (eReturnStatusSuccessFinishNoResult); + return true; + } + + if (error.Success()) + result.AppendError ("process launch failed"); + else + result.AppendError (error.AsCString()); + result.SetStatus (eReturnStatusFailed); + } + else + { + result.AppendError ("'platform process launch' uses the current target file and arguments, or the executable and its arguments can be specified in this command"); + result.SetStatus (eReturnStatusFailed); + return false; + } + } + else + { + result.AppendError ("no platform is selected\n"); + } + return result.Succeeded(); + } + +protected: + ProcessLaunchCommandOptions m_options; +}; + + + +//---------------------------------------------------------------------- +// "platform process list" +//---------------------------------------------------------------------- +class CommandObjectPlatformProcessList : public CommandObjectParsed +{ +public: + CommandObjectPlatformProcessList (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "platform process list", + "List processes on a remote platform by name, pid, or many other matching attributes.", + "platform process list", + 0), + m_options (interpreter) + { + } + + virtual + ~CommandObjectPlatformProcessList () + { + } + + virtual Options * + GetOptions () + { + return &m_options; + } + +protected: + virtual bool + DoExecute (Args& args, CommandReturnObject &result) + { + Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get(); + PlatformSP platform_sp; + if (target) + { + platform_sp = target->GetPlatform(); + } + if (!platform_sp) + { + platform_sp = m_interpreter.GetDebugger().GetPlatformList().GetSelectedPlatform(); + } + + if (platform_sp) + { + Error error; + if (args.GetArgumentCount() == 0) + { + + if (platform_sp) + { + Stream &ostrm = result.GetOutputStream(); + + lldb::pid_t pid = m_options.match_info.GetProcessInfo().GetProcessID(); + if (pid != LLDB_INVALID_PROCESS_ID) + { + ProcessInstanceInfo proc_info; + if (platform_sp->GetProcessInfo (pid, proc_info)) + { + ProcessInstanceInfo::DumpTableHeader (ostrm, platform_sp.get(), m_options.show_args, m_options.verbose); + proc_info.DumpAsTableRow(ostrm, platform_sp.get(), m_options.show_args, m_options.verbose); + result.SetStatus (eReturnStatusSuccessFinishResult); + } + else + { + result.AppendErrorWithFormat ("no process found with pid = %" PRIu64 "\n", pid); + result.SetStatus (eReturnStatusFailed); + } + } + else + { + ProcessInstanceInfoList proc_infos; + const uint32_t matches = platform_sp->FindProcesses (m_options.match_info, proc_infos); + const char *match_desc = NULL; + const char *match_name = m_options.match_info.GetProcessInfo().GetName(); + if (match_name && match_name[0]) + { + switch (m_options.match_info.GetNameMatchType()) + { + case eNameMatchIgnore: break; + case eNameMatchEquals: match_desc = "matched"; break; + case eNameMatchContains: match_desc = "contained"; break; + case eNameMatchStartsWith: match_desc = "started with"; break; + case eNameMatchEndsWith: match_desc = "ended with"; break; + case eNameMatchRegularExpression: match_desc = "matched the regular expression"; break; + } + } + + if (matches == 0) + { + if (match_desc) + result.AppendErrorWithFormat ("no processes were found that %s \"%s\" on the \"%s\" platform\n", + match_desc, + match_name, + platform_sp->GetPluginName().GetCString()); + else + result.AppendErrorWithFormat ("no processes were found on the \"%s\" platform\n", platform_sp->GetPluginName().GetCString()); + result.SetStatus (eReturnStatusFailed); + } + else + { + result.AppendMessageWithFormat ("%u matching process%s found on \"%s\"", + matches, + matches > 1 ? "es were" : " was", + platform_sp->GetName().GetCString()); + if (match_desc) + result.AppendMessageWithFormat (" whose name %s \"%s\"", + match_desc, + match_name); + result.AppendMessageWithFormat ("\n"); + ProcessInstanceInfo::DumpTableHeader (ostrm, platform_sp.get(), m_options.show_args, m_options.verbose); + for (uint32_t i=0; i [ ...]", + 0) + { + CommandArgumentEntry arg; + CommandArgumentData pid_args; + + // Define the first (and only) variant of this arg. + pid_args.arg_type = eArgTypePid; + pid_args.arg_repetition = eArgRepeatStar; + + // There is only one variant this argument could be; put it into the argument entry. + arg.push_back (pid_args); + + // Push the data for the first argument into the m_arguments vector. + m_arguments.push_back (arg); + } + + virtual + ~CommandObjectPlatformProcessInfo () + { + } + +protected: + virtual bool + DoExecute (Args& args, CommandReturnObject &result) + { + Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get(); + PlatformSP platform_sp; + if (target) + { + platform_sp = target->GetPlatform(); + } + if (!platform_sp) + { + platform_sp = m_interpreter.GetDebugger().GetPlatformList().GetSelectedPlatform(); + } + + if (platform_sp) + { + const size_t argc = args.GetArgumentCount(); + if (argc > 0) + { + Error error; + + if (platform_sp->IsConnected()) + { + Stream &ostrm = result.GetOutputStream(); + bool success; + for (size_t i=0; iGetProcessInfo (pid, proc_info)) + { + ostrm.Printf ("Process information for process %" PRIu64 ":\n", pid); + proc_info.Dump (ostrm, platform_sp.get()); + } + else + { + ostrm.Printf ("error: no process information is available for process %" PRIu64 "\n", pid); + } + ostrm.EOL(); + } + else + { + result.AppendErrorWithFormat ("invalid process ID argument '%s'", arg); + result.SetStatus (eReturnStatusFailed); + break; + } + } + } + else + { + // Not connected... + result.AppendErrorWithFormat ("not connected to '%s'", platform_sp->GetPluginName().GetCString()); + result.SetStatus (eReturnStatusFailed); + } + } + else + { + // No args + result.AppendError ("one or more process id(s) must be specified"); + result.SetStatus (eReturnStatusFailed); + } + } + else + { + result.AppendError ("no platform is currently selected"); + result.SetStatus (eReturnStatusFailed); + } + return result.Succeeded(); + } +}; + + + + +class CommandObjectPlatformProcess : public CommandObjectMultiword +{ +public: + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + CommandObjectPlatformProcess (CommandInterpreter &interpreter) : + CommandObjectMultiword (interpreter, + "platform process", + "A set of commands to query, launch and attach to platform processes", + "platform process [attach|launch|list] ...") + { +// LoadSubCommand ("attach", CommandObjectSP (new CommandObjectPlatformProcessAttach (interpreter))); + LoadSubCommand ("launch", CommandObjectSP (new CommandObjectPlatformProcessLaunch (interpreter))); + LoadSubCommand ("info" , CommandObjectSP (new CommandObjectPlatformProcessInfo (interpreter))); + LoadSubCommand ("list" , CommandObjectSP (new CommandObjectPlatformProcessList (interpreter))); + + } + + virtual + ~CommandObjectPlatformProcess () + { + } + +private: + //------------------------------------------------------------------ + // For CommandObjectPlatform only + //------------------------------------------------------------------ + DISALLOW_COPY_AND_ASSIGN (CommandObjectPlatformProcess); +}; + + +class CommandObjectPlatformShell : public CommandObjectRaw +{ +public: + CommandObjectPlatformShell (CommandInterpreter &interpreter) : + CommandObjectRaw (interpreter, + "platform shell", + "Run a shell command on a the selected platform.", + "platform shell ", + 0) + { + } + + virtual + ~CommandObjectPlatformShell () + { + } + +protected: + virtual bool + DoExecute (const char *raw_command_line, CommandReturnObject &result) + { + // TODO: Implement "Platform::RunShellCommand()" and switch over to using + // the current platform when it is in the interface. + const char *working_dir = NULL; + std::string output; + int status = -1; + int signo = -1; + Error error (Host::RunShellCommand (raw_command_line, working_dir, &status, &signo, &output, 10)); + if (!output.empty()) + result.GetOutputStream().PutCString(output.c_str()); + if (status > 0) + { + if (signo > 0) + { + const char *signo_cstr = Host::GetSignalAsCString(signo); + if (signo_cstr) + result.GetOutputStream().Printf("error: command returned with status %i and signal %s\n", status, signo_cstr); + else + result.GetOutputStream().Printf("error: command returned with status %i and signal %i\n", status, signo); + } + else + result.GetOutputStream().Printf("error: command returned with status %i\n", status); + } + + if (error.Fail()) + { + result.AppendError(error.AsCString()); + result.SetStatus (eReturnStatusFailed); + } + else + { + result.SetStatus (eReturnStatusSuccessFinishResult); + } + return true; + } +}; + +//---------------------------------------------------------------------- +// CommandObjectPlatform constructor +//---------------------------------------------------------------------- +CommandObjectPlatform::CommandObjectPlatform(CommandInterpreter &interpreter) : + CommandObjectMultiword (interpreter, + "platform", + "A set of commands to manage and create platforms.", + "platform [connect|disconnect|info|list|status|select] ...") +{ + LoadSubCommand ("select", CommandObjectSP (new CommandObjectPlatformSelect (interpreter))); + LoadSubCommand ("list" , CommandObjectSP (new CommandObjectPlatformList (interpreter))); + LoadSubCommand ("status", CommandObjectSP (new CommandObjectPlatformStatus (interpreter))); + LoadSubCommand ("connect", CommandObjectSP (new CommandObjectPlatformConnect (interpreter))); + LoadSubCommand ("disconnect", CommandObjectSP (new CommandObjectPlatformDisconnect (interpreter))); + LoadSubCommand ("process", CommandObjectSP (new CommandObjectPlatformProcess (interpreter))); + LoadSubCommand ("shell", CommandObjectSP (new CommandObjectPlatformShell (interpreter))); +} + + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +CommandObjectPlatform::~CommandObjectPlatform() +{ +} diff --git a/contrib/llvm/tools/lldb/source/Commands/CommandObjectPlatform.h b/contrib/llvm/tools/lldb/source/Commands/CommandObjectPlatform.h new file mode 100644 index 00000000000..f3bd7584864 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Commands/CommandObjectPlatform.h @@ -0,0 +1,40 @@ +//===-- CommandObjectPlatform.h ---------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_CommandObjectPlatform_h_ +#define liblldb_CommandObjectPlatform_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Interpreter/CommandObjectMultiword.h" +#include "lldb/Interpreter/Options.h" + +namespace lldb_private { + +//------------------------------------------------------------------------- +// CommandObjectPlatform +//------------------------------------------------------------------------- + +class CommandObjectPlatform : public CommandObjectMultiword +{ +public: + CommandObjectPlatform(CommandInterpreter &interpreter); + + virtual + ~CommandObjectPlatform(); + + private: + DISALLOW_COPY_AND_ASSIGN (CommandObjectPlatform); +}; + +} // namespace lldb_private + +#endif // liblldb_CommandObjectPlatform_h_ diff --git a/contrib/llvm/tools/lldb/source/Commands/CommandObjectPlugin.cpp b/contrib/llvm/tools/lldb/source/Commands/CommandObjectPlugin.cpp new file mode 100644 index 00000000000..1bc7632e298 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Commands/CommandObjectPlugin.cpp @@ -0,0 +1,122 @@ +//===-- CommandObjectPlugin.cpp ----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "CommandObjectPlugin.h" + +#include "lldb/API/SBDebugger.h" +#include "lldb/API/SBCommandInterpreter.h" +#include "lldb/API/SBCommandReturnObject.h" + +#include "lldb/Host/Host.h" + +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Interpreter/CommandReturnObject.h" + +using namespace lldb; +using namespace lldb_private; + +class CommandObjectPluginLoad : public CommandObjectParsed +{ +private: +public: + CommandObjectPluginLoad (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "plugin load", + "Import a dylib that implements an LLDB plugin.", + NULL) + { + CommandArgumentEntry arg1; + CommandArgumentData cmd_arg; + + // Define the first (and only) variant of this arg. + cmd_arg.arg_type = eArgTypeFilename; + cmd_arg.arg_repetition = eArgRepeatPlain; + + // There is only one variant this argument could be; put it into the argument entry. + arg1.push_back (cmd_arg); + + // Push the data for the first argument into the m_arguments vector. + m_arguments.push_back (arg1); + } + + ~CommandObjectPluginLoad () + { + } + + int + HandleArgumentCompletion (Args &input, + int &cursor_index, + int &cursor_char_position, + OptionElementVector &opt_element_vector, + int match_start_point, + int max_return_elements, + bool &word_complete, + StringList &matches) + { + std::string completion_str (input.GetArgumentAtIndex(cursor_index)); + completion_str.erase (cursor_char_position); + + CommandCompletions::InvokeCommonCompletionCallbacks (m_interpreter, + CommandCompletions::eDiskFileCompletion, + completion_str.c_str(), + match_start_point, + max_return_elements, + NULL, + word_complete, + matches); + return matches.GetSize(); + } + +protected: + bool + DoExecute (Args& command, CommandReturnObject &result) + { + typedef void (*LLDBCommandPluginInit) (lldb::SBDebugger debugger); + + size_t argc = command.GetArgumentCount(); + + if (argc != 1) + { + result.AppendError ("'plugin load' requires one argument"); + result.SetStatus (eReturnStatusFailed); + return false; + } + + const char* path = command.GetArgumentAtIndex(0); + + Error error; + + FileSpec dylib_fspec(path,true); + + if (m_interpreter.GetDebugger().LoadPlugin(dylib_fspec, error)) + result.SetStatus(eReturnStatusSuccessFinishResult); + else + { + result.AppendError(error.AsCString()); + result.SetStatus(eReturnStatusFailed); + } + + return result.Succeeded(); + } +}; + +CommandObjectPlugin::CommandObjectPlugin (CommandInterpreter &interpreter) : +CommandObjectMultiword (interpreter, + "plugin", + "A set of commands for managing or customizing plugin commands.", + "plugin []") +{ + LoadSubCommand ("load", CommandObjectSP (new CommandObjectPluginLoad (interpreter))); +} + +CommandObjectPlugin::~CommandObjectPlugin () +{ +} diff --git a/contrib/llvm/tools/lldb/source/Commands/CommandObjectPlugin.h b/contrib/llvm/tools/lldb/source/Commands/CommandObjectPlugin.h new file mode 100644 index 00000000000..9d0f0fcc1ed --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Commands/CommandObjectPlugin.h @@ -0,0 +1,36 @@ +//===-- CommandObjectPlugin.h -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_CommandObjectPlugin_h_ +#define liblldb_CommandObjectPlugin_h_ + +// C Includes +// C++ Includes + + +// Other libraries and framework includes +// Project includes + +#include "lldb/lldb-types.h" +#include "lldb/Interpreter/CommandObjectMultiword.h" + +namespace lldb_private { + + class CommandObjectPlugin : public CommandObjectMultiword + { + public: + CommandObjectPlugin (CommandInterpreter &interpreter); + + virtual + ~CommandObjectPlugin (); + }; + +} // namespace lldb_private + +#endif // liblldb_CommandObjectPlugin_h_ diff --git a/contrib/llvm/tools/lldb/source/Commands/CommandObjectProcess.cpp b/contrib/llvm/tools/lldb/source/Commands/CommandObjectProcess.cpp new file mode 100644 index 00000000000..4c406a4f2aa --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Commands/CommandObjectProcess.cpp @@ -0,0 +1,1945 @@ +//===-- CommandObjectProcess.cpp --------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "CommandObjectProcess.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Breakpoint/Breakpoint.h" +#include "lldb/Breakpoint/BreakpointLocation.h" +#include "lldb/Breakpoint/BreakpointSite.h" +#include "lldb/Core/State.h" +#include "lldb/Core/Module.h" +#include "lldb/Host/Host.h" +#include "lldb/Interpreter/Args.h" +#include "lldb/Interpreter/Options.h" +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Interpreter/CommandReturnObject.h" +#include "lldb/Target/Platform.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/StopInfo.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" + +using namespace lldb; +using namespace lldb_private; + +class CommandObjectProcessLaunchOrAttach : public CommandObjectParsed +{ +public: + CommandObjectProcessLaunchOrAttach (CommandInterpreter &interpreter, + const char *name, + const char *help, + const char *syntax, + uint32_t flags, + const char *new_process_action) : + CommandObjectParsed (interpreter, name, help, syntax, flags), + m_new_process_action (new_process_action) {} + + virtual ~CommandObjectProcessLaunchOrAttach () {} +protected: + bool + StopProcessIfNecessary (Process *&process, StateType &state, CommandReturnObject &result) + { + state = eStateInvalid; + if (process) + { + state = process->GetState(); + + if (process->IsAlive() && state != eStateConnected) + { + char message[1024]; + if (process->GetState() == eStateAttaching) + ::snprintf (message, sizeof(message), "There is a pending attach, abort it and %s?", m_new_process_action.c_str()); + else if (process->GetShouldDetach()) + ::snprintf (message, sizeof(message), "There is a running process, detach from it and %s?", m_new_process_action.c_str()); + else + ::snprintf (message, sizeof(message), "There is a running process, kill it and %s?", m_new_process_action.c_str()); + + if (!m_interpreter.Confirm (message, true)) + { + result.SetStatus (eReturnStatusFailed); + return false; + } + else + { + if (process->GetShouldDetach()) + { + bool keep_stopped = false; + Error detach_error (process->Detach(keep_stopped)); + if (detach_error.Success()) + { + result.SetStatus (eReturnStatusSuccessFinishResult); + process = NULL; + } + else + { + result.AppendErrorWithFormat ("Failed to detach from process: %s\n", detach_error.AsCString()); + result.SetStatus (eReturnStatusFailed); + } + } + else + { + Error destroy_error (process->Destroy()); + if (destroy_error.Success()) + { + result.SetStatus (eReturnStatusSuccessFinishResult); + process = NULL; + } + else + { + result.AppendErrorWithFormat ("Failed to kill process: %s\n", destroy_error.AsCString()); + result.SetStatus (eReturnStatusFailed); + } + } + } + } + } + return result.Succeeded(); + } + std::string m_new_process_action; +}; +//------------------------------------------------------------------------- +// CommandObjectProcessLaunch +//------------------------------------------------------------------------- +#pragma mark CommandObjectProcessLaunch +class CommandObjectProcessLaunch : public CommandObjectProcessLaunchOrAttach +{ +public: + + CommandObjectProcessLaunch (CommandInterpreter &interpreter) : + CommandObjectProcessLaunchOrAttach (interpreter, + "process launch", + "Launch the executable in the debugger.", + NULL, + eFlagRequiresTarget, + "restart"), + m_options (interpreter) + { + CommandArgumentEntry arg; + CommandArgumentData run_args_arg; + + // Define the first (and only) variant of this arg. + run_args_arg.arg_type = eArgTypeRunArgs; + run_args_arg.arg_repetition = eArgRepeatOptional; + + // There is only one variant this argument could be; put it into the argument entry. + arg.push_back (run_args_arg); + + // Push the data for the first argument into the m_arguments vector. + m_arguments.push_back (arg); + } + + + ~CommandObjectProcessLaunch () + { + } + + virtual int + HandleArgumentCompletion (Args &input, + int &cursor_index, + int &cursor_char_position, + OptionElementVector &opt_element_vector, + int match_start_point, + int max_return_elements, + bool &word_complete, + StringList &matches) + { + std::string completion_str (input.GetArgumentAtIndex(cursor_index)); + completion_str.erase (cursor_char_position); + + CommandCompletions::InvokeCommonCompletionCallbacks (m_interpreter, + CommandCompletions::eDiskFileCompletion, + completion_str.c_str(), + match_start_point, + max_return_elements, + NULL, + word_complete, + matches); + return matches.GetSize(); + } + + Options * + GetOptions () + { + return &m_options; + } + + virtual const char *GetRepeatCommand (Args ¤t_command_args, uint32_t index) + { + // No repeat for "process launch"... + return ""; + } + +protected: + bool + DoExecute (Args& launch_args, CommandReturnObject &result) + { + Debugger &debugger = m_interpreter.GetDebugger(); + Target *target = debugger.GetSelectedTarget().get(); + Error error; + // If our listener is NULL, users aren't allows to launch + char filename[PATH_MAX]; + const Module *exe_module = target->GetExecutableModulePointer(); + + if (exe_module == NULL) + { + result.AppendError ("no file in target, create a debug target using the 'target create' command"); + result.SetStatus (eReturnStatusFailed); + return false; + } + + StateType state = eStateInvalid; + Process *process = m_exe_ctx.GetProcessPtr(); + + if (!StopProcessIfNecessary(process, state, result)) + return false; + + const char *target_settings_argv0 = target->GetArg0(); + + exe_module->GetFileSpec().GetPath (filename, sizeof(filename)); + + if (target_settings_argv0) + { + m_options.launch_info.GetArguments().AppendArgument (target_settings_argv0); + m_options.launch_info.SetExecutableFile(exe_module->GetPlatformFileSpec(), false); + } + else + { + m_options.launch_info.SetExecutableFile(exe_module->GetPlatformFileSpec(), true); + } + + if (launch_args.GetArgumentCount() == 0) + { + Args target_setting_args; + if (target->GetRunArguments(target_setting_args)) + m_options.launch_info.GetArguments().AppendArguments (target_setting_args); + } + else + { + m_options.launch_info.GetArguments().AppendArguments (launch_args); + + // Save the arguments for subsequent runs in the current target. + target->SetRunArguments (launch_args); + } + + if (target->GetDisableASLR()) + m_options.launch_info.GetFlags().Set (eLaunchFlagDisableASLR); + + if (target->GetDisableSTDIO()) + m_options.launch_info.GetFlags().Set (eLaunchFlagDisableSTDIO); + + m_options.launch_info.GetFlags().Set (eLaunchFlagDebug); + + Args environment; + target->GetEnvironmentAsArgs (environment); + if (environment.GetArgumentCount() > 0) + m_options.launch_info.GetEnvironmentEntries ().AppendArguments (environment); + + // Get the value of synchronous execution here. If you wait till after you have started to + // run, then you could have hit a breakpoint, whose command might switch the value, and + // then you'll pick up that incorrect value. + bool synchronous_execution = m_interpreter.GetSynchronous (); + + // Finalize the file actions, and if none were given, default to opening + // up a pseudo terminal + const bool default_to_use_pty = true; + m_options.launch_info.FinalizeFileActions (target, default_to_use_pty); + + if (state == eStateConnected) + { + if (m_options.launch_info.GetFlags().Test (eLaunchFlagLaunchInTTY)) + { + result.AppendWarning("can't launch in tty when launching through a remote connection"); + m_options.launch_info.GetFlags().Clear (eLaunchFlagLaunchInTTY); + } + } + + if (!m_options.launch_info.GetArchitecture().IsValid()) + m_options.launch_info.GetArchitecture() = target->GetArchitecture(); + + PlatformSP platform_sp (target->GetPlatform()); + + if (platform_sp && platform_sp->CanDebugProcess ()) + { + process = target->GetPlatform()->DebugProcess (m_options.launch_info, + debugger, + target, + debugger.GetListener(), + error).get(); + } + else + { + const char *plugin_name = m_options.launch_info.GetProcessPluginName(); + process = target->CreateProcess (debugger.GetListener(), plugin_name, NULL).get(); + if (process) + error = process->Launch (m_options.launch_info); + } + + if (process == NULL) + { + result.SetError (error, "failed to launch or debug process"); + return false; + } + + + if (error.Success()) + { + const char *archname = exe_module->GetArchitecture().GetArchitectureName(); + + result.AppendMessageWithFormat ("Process %" PRIu64 " launched: '%s' (%s)\n", process->GetID(), filename, archname); + result.SetDidChangeProcessState (true); + if (m_options.launch_info.GetFlags().Test(eLaunchFlagStopAtEntry) == false) + { + result.SetStatus (eReturnStatusSuccessContinuingNoResult); + StateType state = process->WaitForProcessToStop (NULL); + + if (state == eStateStopped) + { + error = process->Resume(); + if (error.Success()) + { + if (synchronous_execution) + { + state = process->WaitForProcessToStop (NULL); + const bool must_be_alive = true; + if (!StateIsStoppedState(state, must_be_alive)) + { + result.AppendErrorWithFormat ("process isn't stopped: %s", StateAsCString(state)); + } + result.SetDidChangeProcessState (true); + result.SetStatus (eReturnStatusSuccessFinishResult); + } + else + { + result.SetStatus (eReturnStatusSuccessContinuingNoResult); + } + } + else + { + result.AppendErrorWithFormat ("process resume at entry point failed: %s", error.AsCString()); + result.SetStatus (eReturnStatusFailed); + } + } + else + { + result.AppendErrorWithFormat ("initial process state wasn't stopped: %s", StateAsCString(state)); + result.SetStatus (eReturnStatusFailed); + } + } + } + else + { + result.AppendErrorWithFormat ("process launch failed: %s", error.AsCString()); + result.SetStatus (eReturnStatusFailed); + } + + return result.Succeeded(); + } + +protected: + ProcessLaunchCommandOptions m_options; +}; + + +//#define SET1 LLDB_OPT_SET_1 +//#define SET2 LLDB_OPT_SET_2 +//#define SET3 LLDB_OPT_SET_3 +// +//OptionDefinition +//CommandObjectProcessLaunch::CommandOptions::g_option_table[] = +//{ +//{ SET1 | SET2 | SET3, false, "stop-at-entry", 's', no_argument, NULL, 0, eArgTypeNone, "Stop at the entry point of the program when launching a process."}, +//{ SET1 , false, "stdin", 'i', required_argument, NULL, 0, eArgTypeDirectoryName, "Redirect stdin for the process to ."}, +//{ SET1 , false, "stdout", 'o', required_argument, NULL, 0, eArgTypeDirectoryName, "Redirect stdout for the process to ."}, +//{ SET1 , false, "stderr", 'e', required_argument, NULL, 0, eArgTypeDirectoryName, "Redirect stderr for the process to ."}, +//{ SET1 | SET2 | SET3, false, "plugin", 'p', required_argument, NULL, 0, eArgTypePlugin, "Name of the process plugin you want to use."}, +//{ SET2 , false, "tty", 't', optional_argument, NULL, 0, eArgTypeDirectoryName, "Start the process in a terminal. If is specified, look for a terminal whose name contains , else start the process in a new terminal."}, +//{ SET3, false, "no-stdio", 'n', no_argument, NULL, 0, eArgTypeNone, "Do not set up for terminal I/O to go to running process."}, +//{ SET1 | SET2 | SET3, false, "working-dir", 'w', required_argument, NULL, 0, eArgTypeDirectoryName, "Set the current working directory to when running the inferior."}, +//{ 0, false, NULL, 0, 0, NULL, 0, eArgTypeNone, NULL } +//}; +// +//#undef SET1 +//#undef SET2 +//#undef SET3 + +//------------------------------------------------------------------------- +// CommandObjectProcessAttach +//------------------------------------------------------------------------- +#pragma mark CommandObjectProcessAttach +class CommandObjectProcessAttach : public CommandObjectProcessLaunchOrAttach +{ +public: + + class CommandOptions : public Options + { + public: + + CommandOptions (CommandInterpreter &interpreter) : + Options(interpreter) + { + // Keep default values of all options in one place: OptionParsingStarting () + OptionParsingStarting (); + } + + ~CommandOptions () + { + } + + Error + SetOptionValue (uint32_t option_idx, const char *option_arg) + { + Error error; + const int short_option = m_getopt_table[option_idx].val; + bool success = false; + switch (short_option) + { + case 'c': + attach_info.SetContinueOnceAttached(true); + break; + + case 'p': + { + lldb::pid_t pid = Args::StringToUInt32 (option_arg, LLDB_INVALID_PROCESS_ID, 0, &success); + if (!success || pid == LLDB_INVALID_PROCESS_ID) + { + error.SetErrorStringWithFormat("invalid process ID '%s'", option_arg); + } + else + { + attach_info.SetProcessID (pid); + } + } + break; + + case 'P': + attach_info.SetProcessPluginName (option_arg); + break; + + case 'n': + attach_info.GetExecutableFile().SetFile(option_arg, false); + break; + + case 'w': + attach_info.SetWaitForLaunch(true); + break; + + case 'i': + attach_info.SetIgnoreExisting(false); + break; + + default: + error.SetErrorStringWithFormat("invalid short option character '%c'", short_option); + break; + } + return error; + } + + void + OptionParsingStarting () + { + attach_info.Clear(); + } + + const OptionDefinition* + GetDefinitions () + { + return g_option_table; + } + + virtual bool + HandleOptionArgumentCompletion (Args &input, + int cursor_index, + int char_pos, + OptionElementVector &opt_element_vector, + int opt_element_index, + int match_start_point, + int max_return_elements, + bool &word_complete, + StringList &matches) + { + int opt_arg_pos = opt_element_vector[opt_element_index].opt_arg_pos; + int opt_defs_index = opt_element_vector[opt_element_index].opt_defs_index; + + // We are only completing the name option for now... + + const OptionDefinition *opt_defs = GetDefinitions(); + if (opt_defs[opt_defs_index].short_option == 'n') + { + // Are we in the name? + + // Look to see if there is a -P argument provided, and if so use that plugin, otherwise + // use the default plugin. + + const char *partial_name = NULL; + partial_name = input.GetArgumentAtIndex(opt_arg_pos); + + PlatformSP platform_sp (m_interpreter.GetPlatform (true)); + if (platform_sp) + { + ProcessInstanceInfoList process_infos; + ProcessInstanceInfoMatch match_info; + if (partial_name) + { + match_info.GetProcessInfo().GetExecutableFile().SetFile(partial_name, false); + match_info.SetNameMatchType(eNameMatchStartsWith); + } + platform_sp->FindProcesses (match_info, process_infos); + const size_t num_matches = process_infos.GetSize(); + if (num_matches > 0) + { + for (size_t i=0; i", + 0, + "attach"), + m_options (interpreter) + { + } + + ~CommandObjectProcessAttach () + { + } + + Options * + GetOptions () + { + return &m_options; + } + +protected: + bool + DoExecute (Args& command, + CommandReturnObject &result) + { + Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get(); + // N.B. The attach should be synchronous. It doesn't help much to get the prompt back between initiating the attach + // and the target actually stopping. So even if the interpreter is set to be asynchronous, we wait for the stop + // ourselves here. + + StateType state = eStateInvalid; + Process *process = m_exe_ctx.GetProcessPtr(); + + if (!StopProcessIfNecessary (process, state, result)) + return false; + + if (target == NULL) + { + // If there isn't a current target create one. + TargetSP new_target_sp; + Error error; + + error = m_interpreter.GetDebugger().GetTargetList().CreateTarget (m_interpreter.GetDebugger(), + NULL, + NULL, + false, + NULL, // No platform options + new_target_sp); + target = new_target_sp.get(); + if (target == NULL || error.Fail()) + { + result.AppendError(error.AsCString("Error creating target")); + return false; + } + m_interpreter.GetDebugger().GetTargetList().SetSelectedTarget(target); + } + + // Record the old executable module, we want to issue a warning if the process of attaching changed the + // current executable (like somebody said "file foo" then attached to a PID whose executable was bar.) + + ModuleSP old_exec_module_sp = target->GetExecutableModule(); + ArchSpec old_arch_spec = target->GetArchitecture(); + + if (command.GetArgumentCount()) + { + result.AppendErrorWithFormat("Invalid arguments for '%s'.\nUsage: %s\n", m_cmd_name.c_str(), m_cmd_syntax.c_str()); + result.SetStatus (eReturnStatusFailed); + } + else + { + if (state != eStateConnected) + { + const char *plugin_name = m_options.attach_info.GetProcessPluginName(); + process = target->CreateProcess (m_interpreter.GetDebugger().GetListener(), plugin_name, NULL).get(); + } + + if (process) + { + Error error; + // If no process info was specified, then use the target executable + // name as the process to attach to by default + if (!m_options.attach_info.ProcessInfoSpecified ()) + { + if (old_exec_module_sp) + m_options.attach_info.GetExecutableFile().GetFilename() = old_exec_module_sp->GetPlatformFileSpec().GetFilename(); + + if (!m_options.attach_info.ProcessInfoSpecified ()) + { + error.SetErrorString ("no process specified, create a target with a file, or specify the --pid or --name command option"); + } + } + + if (error.Success()) + { + error = process->Attach (m_options.attach_info); + + if (error.Success()) + { + result.SetStatus (eReturnStatusSuccessContinuingNoResult); + } + else + { + result.AppendErrorWithFormat ("attach failed: %s\n", error.AsCString()); + result.SetStatus (eReturnStatusFailed); + return false; + } + // If we're synchronous, wait for the stopped event and report that. + // Otherwise just return. + // FIXME: in the async case it will now be possible to get to the command + // interpreter with a state eStateAttaching. Make sure we handle that correctly. + StateType state = process->WaitForProcessToStop (NULL); + + result.SetDidChangeProcessState (true); + + if (state == eStateStopped) + { + result.AppendMessageWithFormat ("Process %" PRIu64 " %s\n", process->GetID(), StateAsCString (state)); + result.SetStatus (eReturnStatusSuccessFinishNoResult); + } + else + { + result.AppendError ("attach failed: process did not stop (no such process or permission problem?)"); + process->Destroy(); + result.SetStatus (eReturnStatusFailed); + return false; + } + } + } + } + + if (result.Succeeded()) + { + // Okay, we're done. Last step is to warn if the executable module has changed: + char new_path[PATH_MAX]; + ModuleSP new_exec_module_sp (target->GetExecutableModule()); + if (!old_exec_module_sp) + { + // We might not have a module if we attached to a raw pid... + if (new_exec_module_sp) + { + new_exec_module_sp->GetFileSpec().GetPath(new_path, PATH_MAX); + result.AppendMessageWithFormat("Executable module set to \"%s\".\n", new_path); + } + } + else if (old_exec_module_sp->GetFileSpec() != new_exec_module_sp->GetFileSpec()) + { + char old_path[PATH_MAX]; + + old_exec_module_sp->GetFileSpec().GetPath (old_path, PATH_MAX); + new_exec_module_sp->GetFileSpec().GetPath (new_path, PATH_MAX); + + result.AppendWarningWithFormat("Executable module changed from \"%s\" to \"%s\".\n", + old_path, new_path); + } + + if (!old_arch_spec.IsValid()) + { + result.AppendMessageWithFormat ("Architecture set to: %s.\n", target->GetArchitecture().GetTriple().getTriple().c_str()); + } + else if (!old_arch_spec.IsExactMatch(target->GetArchitecture())) + { + result.AppendWarningWithFormat("Architecture changed from %s to %s.\n", + old_arch_spec.GetTriple().getTriple().c_str(), + target->GetArchitecture().GetTriple().getTriple().c_str()); + } + + // This supports the use-case scenario of immediately continuing the process once attached. + if (m_options.attach_info.GetContinueOnceAttached()) + m_interpreter.HandleCommand("process continue", eLazyBoolNo, result); + } + return result.Succeeded(); + } + + CommandOptions m_options; +}; + + +OptionDefinition +CommandObjectProcessAttach::CommandOptions::g_option_table[] = +{ +{ LLDB_OPT_SET_ALL, false, "continue",'c', no_argument, NULL, 0, eArgTypeNone, "Immediately continue the process once attached."}, +{ LLDB_OPT_SET_ALL, false, "plugin", 'P', required_argument, NULL, 0, eArgTypePlugin, "Name of the process plugin you want to use."}, +{ LLDB_OPT_SET_1, false, "pid", 'p', required_argument, NULL, 0, eArgTypePid, "The process ID of an existing process to attach to."}, +{ LLDB_OPT_SET_2, false, "name", 'n', required_argument, NULL, 0, eArgTypeProcessName, "The name of the process to attach to."}, +{ LLDB_OPT_SET_2, false, "include-existing", 'i', no_argument, NULL, 0, eArgTypeNone, "Include existing processes when doing attach -w."}, +{ LLDB_OPT_SET_2, false, "waitfor", 'w', no_argument, NULL, 0, eArgTypeNone, "Wait for the process with to launch."}, +{ 0, false, NULL, 0, 0, NULL, 0, eArgTypeNone, NULL } +}; + +//------------------------------------------------------------------------- +// CommandObjectProcessContinue +//------------------------------------------------------------------------- +#pragma mark CommandObjectProcessContinue + +class CommandObjectProcessContinue : public CommandObjectParsed +{ +public: + + CommandObjectProcessContinue (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "process continue", + "Continue execution of all threads in the current process.", + "process continue", + eFlagRequiresProcess | + eFlagTryTargetAPILock | + eFlagProcessMustBeLaunched | + eFlagProcessMustBePaused ), + m_options(interpreter) + { + } + + + ~CommandObjectProcessContinue () + { + } + +protected: + + class CommandOptions : public Options + { + public: + + CommandOptions (CommandInterpreter &interpreter) : + Options(interpreter) + { + // Keep default values of all options in one place: OptionParsingStarting () + OptionParsingStarting (); + } + + ~CommandOptions () + { + } + + Error + SetOptionValue (uint32_t option_idx, const char *option_arg) + { + Error error; + const int short_option = m_getopt_table[option_idx].val; + bool success = false; + switch (short_option) + { + case 'i': + m_ignore = Args::StringToUInt32 (option_arg, 0, 0, &success); + if (!success) + error.SetErrorStringWithFormat ("invalid value for ignore option: \"%s\", should be a number.", option_arg); + break; + + default: + error.SetErrorStringWithFormat("invalid short option character '%c'", short_option); + break; + } + return error; + } + + void + OptionParsingStarting () + { + m_ignore = 0; + } + + const OptionDefinition* + GetDefinitions () + { + return g_option_table; + } + + // Options table: Required for subclasses of Options. + + static OptionDefinition g_option_table[]; + + uint32_t m_ignore; + }; + + bool + DoExecute (Args& command, CommandReturnObject &result) + { + Process *process = m_exe_ctx.GetProcessPtr(); + bool synchronous_execution = m_interpreter.GetSynchronous (); + StateType state = process->GetState(); + if (state == eStateStopped) + { + if (command.GetArgumentCount() != 0) + { + result.AppendErrorWithFormat ("The '%s' command does not take any arguments.\n", m_cmd_name.c_str()); + result.SetStatus (eReturnStatusFailed); + return false; + } + + if (m_options.m_ignore > 0) + { + ThreadSP sel_thread_sp(process->GetThreadList().GetSelectedThread()); + if (sel_thread_sp) + { + StopInfoSP stop_info_sp = sel_thread_sp->GetStopInfo(); + if (stop_info_sp && stop_info_sp->GetStopReason() == eStopReasonBreakpoint) + { + lldb::break_id_t bp_site_id = (lldb::break_id_t)stop_info_sp->GetValue(); + BreakpointSiteSP bp_site_sp(process->GetBreakpointSiteList().FindByID(bp_site_id)); + if (bp_site_sp) + { + const size_t num_owners = bp_site_sp->GetNumberOfOwners(); + for (size_t i = 0; i < num_owners; i++) + { + Breakpoint &bp_ref = bp_site_sp->GetOwnerAtIndex(i)->GetBreakpoint(); + if (!bp_ref.IsInternal()) + { + bp_ref.SetIgnoreCount(m_options.m_ignore); + } + } + } + } + } + } + + { // Scope for thread list mutex: + Mutex::Locker locker (process->GetThreadList().GetMutex()); + const uint32_t num_threads = process->GetThreadList().GetSize(); + + // Set the actions that the threads should each take when resuming + for (uint32_t idx=0; idxGetThreadList().GetThreadAtIndex(idx)->SetResumeState (eStateRunning); + } + } + + Error error(process->Resume()); + if (error.Success()) + { + result.AppendMessageWithFormat ("Process %" PRIu64 " resuming\n", process->GetID()); + if (synchronous_execution) + { + state = process->WaitForProcessToStop (NULL); + + result.SetDidChangeProcessState (true); + result.AppendMessageWithFormat ("Process %" PRIu64 " %s\n", process->GetID(), StateAsCString (state)); + result.SetStatus (eReturnStatusSuccessFinishNoResult); + } + else + { + result.SetStatus (eReturnStatusSuccessContinuingNoResult); + } + } + else + { + result.AppendErrorWithFormat("Failed to resume process: %s.\n", error.AsCString()); + result.SetStatus (eReturnStatusFailed); + } + } + else + { + result.AppendErrorWithFormat ("Process cannot be continued from its current state (%s).\n", + StateAsCString(state)); + result.SetStatus (eReturnStatusFailed); + } + return result.Succeeded(); + } + + Options * + GetOptions () + { + return &m_options; + } + + CommandOptions m_options; + +}; + +OptionDefinition +CommandObjectProcessContinue::CommandOptions::g_option_table[] = +{ +{ LLDB_OPT_SET_ALL, false, "ignore-count",'i', required_argument, NULL, 0, eArgTypeUnsignedInteger, + "Ignore crossings of the breakpoint (if it exists) for the currently selected thread."}, +{ 0, false, NULL, 0, 0, NULL, 0, eArgTypeNone, NULL } +}; + +//------------------------------------------------------------------------- +// CommandObjectProcessDetach +//------------------------------------------------------------------------- +#pragma mark CommandObjectProcessDetach + +class CommandObjectProcessDetach : public CommandObjectParsed +{ +public: + class CommandOptions : public Options + { + public: + + CommandOptions (CommandInterpreter &interpreter) : + Options (interpreter) + { + OptionParsingStarting (); + } + + ~CommandOptions () + { + } + + Error + SetOptionValue (uint32_t option_idx, const char *option_arg) + { + Error error; + const int short_option = m_getopt_table[option_idx].val; + + switch (short_option) + { + case 's': + bool tmp_result; + bool success; + tmp_result = Args::StringToBoolean(option_arg, false, &success); + if (!success) + error.SetErrorStringWithFormat("invalid boolean option: \"%s\"", option_arg); + else + { + if (tmp_result) + m_keep_stopped = eLazyBoolYes; + else + m_keep_stopped = eLazyBoolNo; + } + break; + default: + error.SetErrorStringWithFormat("invalid short option character '%c'", short_option); + break; + } + return error; + } + + void + OptionParsingStarting () + { + m_keep_stopped = eLazyBoolCalculate; + } + + const OptionDefinition* + GetDefinitions () + { + return g_option_table; + } + + // Options table: Required for subclasses of Options. + + static OptionDefinition g_option_table[]; + + // Instance variables to hold the values for command options. + LazyBool m_keep_stopped; + }; + + CommandObjectProcessDetach (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "process detach", + "Detach from the current process being debugged.", + "process detach", + eFlagRequiresProcess | + eFlagTryTargetAPILock | + eFlagProcessMustBeLaunched), + m_options(interpreter) + { + } + + ~CommandObjectProcessDetach () + { + } + + Options * + GetOptions () + { + return &m_options; + } + + +protected: + bool + DoExecute (Args& command, CommandReturnObject &result) + { + Process *process = m_exe_ctx.GetProcessPtr(); + result.AppendMessageWithFormat ("Detaching from process %" PRIu64 "\n", process->GetID()); + // FIXME: This will be a Command Option: + bool keep_stopped; + if (m_options.m_keep_stopped == eLazyBoolCalculate) + { + // Check the process default: + if (process->GetDetachKeepsStopped()) + keep_stopped = true; + else + keep_stopped = false; + } + else if (m_options.m_keep_stopped == eLazyBoolYes) + keep_stopped = true; + else + keep_stopped = false; + + Error error (process->Detach(keep_stopped)); + if (error.Success()) + { + result.SetStatus (eReturnStatusSuccessFinishResult); + } + else + { + result.AppendErrorWithFormat ("Detach failed: %s\n", error.AsCString()); + result.SetStatus (eReturnStatusFailed); + return false; + } + return result.Succeeded(); + } + + CommandOptions m_options; +}; + +OptionDefinition +CommandObjectProcessDetach::CommandOptions::g_option_table[] = +{ +{ LLDB_OPT_SET_1, false, "keep-stopped", 's', required_argument, NULL, 0, eArgTypeBoolean, "Whether or not the process should be kept stopped on detach (if possible)." }, +{ 0, false, NULL, 0, 0, NULL, 0, eArgTypeNone, NULL } +}; + +//------------------------------------------------------------------------- +// CommandObjectProcessConnect +//------------------------------------------------------------------------- +#pragma mark CommandObjectProcessConnect + +class CommandObjectProcessConnect : public CommandObjectParsed +{ +public: + + class CommandOptions : public Options + { + public: + + CommandOptions (CommandInterpreter &interpreter) : + Options(interpreter) + { + // Keep default values of all options in one place: OptionParsingStarting () + OptionParsingStarting (); + } + + ~CommandOptions () + { + } + + Error + SetOptionValue (uint32_t option_idx, const char *option_arg) + { + Error error; + const int short_option = m_getopt_table[option_idx].val; + + switch (short_option) + { + case 'p': + plugin_name.assign (option_arg); + break; + + default: + error.SetErrorStringWithFormat("invalid short option character '%c'", short_option); + break; + } + return error; + } + + void + OptionParsingStarting () + { + plugin_name.clear(); + } + + const OptionDefinition* + GetDefinitions () + { + return g_option_table; + } + + // Options table: Required for subclasses of Options. + + static OptionDefinition g_option_table[]; + + // Instance variables to hold the values for command options. + + std::string plugin_name; + }; + + CommandObjectProcessConnect (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "process connect", + "Connect to a remote debug service.", + "process connect ", + 0), + m_options (interpreter) + { + } + + ~CommandObjectProcessConnect () + { + } + + + Options * + GetOptions () + { + return &m_options; + } + +protected: + bool + DoExecute (Args& command, + CommandReturnObject &result) + { + + TargetSP target_sp (m_interpreter.GetDebugger().GetSelectedTarget()); + Error error; + Process *process = m_exe_ctx.GetProcessPtr(); + if (process) + { + if (process->IsAlive()) + { + result.AppendErrorWithFormat ("Process %" PRIu64 " is currently being debugged, kill the process before connecting.\n", + process->GetID()); + result.SetStatus (eReturnStatusFailed); + return false; + } + } + + if (!target_sp) + { + // If there isn't a current target create one. + + error = m_interpreter.GetDebugger().GetTargetList().CreateTarget (m_interpreter.GetDebugger(), + NULL, + NULL, + false, + NULL, // No platform options + target_sp); + if (!target_sp || error.Fail()) + { + result.AppendError(error.AsCString("Error creating target")); + result.SetStatus (eReturnStatusFailed); + return false; + } + m_interpreter.GetDebugger().GetTargetList().SetSelectedTarget(target_sp.get()); + } + + if (command.GetArgumentCount() == 1) + { + const char *plugin_name = NULL; + if (!m_options.plugin_name.empty()) + plugin_name = m_options.plugin_name.c_str(); + + const char *remote_url = command.GetArgumentAtIndex(0); + process = target_sp->CreateProcess (m_interpreter.GetDebugger().GetListener(), plugin_name, NULL).get(); + + if (process) + { + error = process->ConnectRemote (&process->GetTarget().GetDebugger().GetOutputStream(), remote_url); + + if (error.Fail()) + { + result.AppendError(error.AsCString("Remote connect failed")); + result.SetStatus (eReturnStatusFailed); + target_sp->DeleteCurrentProcess(); + return false; + } + } + else + { + result.AppendErrorWithFormat ("Unable to find process plug-in for remote URL '%s'.\nPlease specify a process plug-in name with the --plugin option, or specify an object file using the \"file\" command.\n", + remote_url); + result.SetStatus (eReturnStatusFailed); + } + } + else + { + result.AppendErrorWithFormat ("'%s' takes exactly one argument:\nUsage: %s\n", + m_cmd_name.c_str(), + m_cmd_syntax.c_str()); + result.SetStatus (eReturnStatusFailed); + } + return result.Succeeded(); + } + + CommandOptions m_options; +}; + +OptionDefinition +CommandObjectProcessConnect::CommandOptions::g_option_table[] = +{ + { LLDB_OPT_SET_ALL, false, "plugin", 'p', required_argument, NULL, 0, eArgTypePlugin, "Name of the process plugin you want to use."}, + { 0, false, NULL, 0 , 0, NULL, 0, eArgTypeNone, NULL } +}; + +//------------------------------------------------------------------------- +// CommandObjectProcessPlugin +//------------------------------------------------------------------------- +#pragma mark CommandObjectProcessPlugin + +class CommandObjectProcessPlugin : public CommandObjectProxy +{ +public: + + CommandObjectProcessPlugin (CommandInterpreter &interpreter) : + CommandObjectProxy (interpreter, + "process plugin", + "Send a custom command to the current process plug-in.", + "process plugin ", + 0) + { + } + + ~CommandObjectProcessPlugin () + { + } + + virtual CommandObject * + GetProxyCommandObject() + { + Process *process = m_interpreter.GetExecutionContext().GetProcessPtr(); + if (process) + return process->GetPluginCommandObject(); + return NULL; + } +}; + + +//------------------------------------------------------------------------- +// CommandObjectProcessLoad +//------------------------------------------------------------------------- +#pragma mark CommandObjectProcessLoad + +class CommandObjectProcessLoad : public CommandObjectParsed +{ +public: + + CommandObjectProcessLoad (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "process load", + "Load a shared library into the current process.", + "process load [ ...]", + eFlagRequiresProcess | + eFlagTryTargetAPILock | + eFlagProcessMustBeLaunched | + eFlagProcessMustBePaused ) + { + } + + ~CommandObjectProcessLoad () + { + } + +protected: + bool + DoExecute (Args& command, + CommandReturnObject &result) + { + Process *process = m_exe_ctx.GetProcessPtr(); + + const size_t argc = command.GetArgumentCount(); + + for (uint32_t i=0; iGetTarget().GetPlatform()->ResolveRemotePath(image_spec, image_spec); + uint32_t image_token = process->LoadImage(image_spec, error); + if (image_token != LLDB_INVALID_IMAGE_TOKEN) + { + result.AppendMessageWithFormat ("Loading \"%s\"...ok\nImage %u loaded.\n", image_path, image_token); + result.SetStatus (eReturnStatusSuccessFinishResult); + } + else + { + result.AppendErrorWithFormat ("failed to load '%s': %s", image_path, error.AsCString()); + result.SetStatus (eReturnStatusFailed); + } + } + return result.Succeeded(); + } +}; + + +//------------------------------------------------------------------------- +// CommandObjectProcessUnload +//------------------------------------------------------------------------- +#pragma mark CommandObjectProcessUnload + +class CommandObjectProcessUnload : public CommandObjectParsed +{ +public: + + CommandObjectProcessUnload (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "process unload", + "Unload a shared library from the current process using the index returned by a previous call to \"process load\".", + "process unload ", + eFlagRequiresProcess | + eFlagTryTargetAPILock | + eFlagProcessMustBeLaunched | + eFlagProcessMustBePaused ) + { + } + + ~CommandObjectProcessUnload () + { + } + +protected: + bool + DoExecute (Args& command, + CommandReturnObject &result) + { + Process *process = m_exe_ctx.GetProcessPtr(); + + const size_t argc = command.GetArgumentCount(); + + for (uint32_t i=0; iUnloadImage(image_token)); + if (error.Success()) + { + result.AppendMessageWithFormat ("Unloading shared library with index %u...ok\n", image_token); + result.SetStatus (eReturnStatusSuccessFinishResult); + } + else + { + result.AppendErrorWithFormat ("failed to unload image: %s", error.AsCString()); + result.SetStatus (eReturnStatusFailed); + break; + } + } + } + return result.Succeeded(); + } +}; + +//------------------------------------------------------------------------- +// CommandObjectProcessSignal +//------------------------------------------------------------------------- +#pragma mark CommandObjectProcessSignal + +class CommandObjectProcessSignal : public CommandObjectParsed +{ +public: + + CommandObjectProcessSignal (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "process signal", + "Send a UNIX signal to the current process being debugged.", + NULL, + eFlagRequiresProcess | eFlagTryTargetAPILock) + { + CommandArgumentEntry arg; + CommandArgumentData signal_arg; + + // Define the first (and only) variant of this arg. + signal_arg.arg_type = eArgTypeUnixSignal; + signal_arg.arg_repetition = eArgRepeatPlain; + + // There is only one variant this argument could be; put it into the argument entry. + arg.push_back (signal_arg); + + // Push the data for the first argument into the m_arguments vector. + m_arguments.push_back (arg); + } + + ~CommandObjectProcessSignal () + { + } + +protected: + bool + DoExecute (Args& command, + CommandReturnObject &result) + { + Process *process = m_exe_ctx.GetProcessPtr(); + + if (command.GetArgumentCount() == 1) + { + int signo = LLDB_INVALID_SIGNAL_NUMBER; + + const char *signal_name = command.GetArgumentAtIndex(0); + if (::isxdigit (signal_name[0])) + signo = Args::StringToSInt32(signal_name, LLDB_INVALID_SIGNAL_NUMBER, 0); + else + signo = process->GetUnixSignals().GetSignalNumberFromName (signal_name); + + if (signo == LLDB_INVALID_SIGNAL_NUMBER) + { + result.AppendErrorWithFormat ("Invalid signal argument '%s'.\n", command.GetArgumentAtIndex(0)); + result.SetStatus (eReturnStatusFailed); + } + else + { + Error error (process->Signal (signo)); + if (error.Success()) + { + result.SetStatus (eReturnStatusSuccessFinishResult); + } + else + { + result.AppendErrorWithFormat ("Failed to send signal %i: %s\n", signo, error.AsCString()); + result.SetStatus (eReturnStatusFailed); + } + } + } + else + { + result.AppendErrorWithFormat("'%s' takes exactly one signal number argument:\nUsage: %s\n", m_cmd_name.c_str(), + m_cmd_syntax.c_str()); + result.SetStatus (eReturnStatusFailed); + } + return result.Succeeded(); + } +}; + + +//------------------------------------------------------------------------- +// CommandObjectProcessInterrupt +//------------------------------------------------------------------------- +#pragma mark CommandObjectProcessInterrupt + +class CommandObjectProcessInterrupt : public CommandObjectParsed +{ +public: + + + CommandObjectProcessInterrupt (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "process interrupt", + "Interrupt the current process being debugged.", + "process interrupt", + eFlagRequiresProcess | + eFlagTryTargetAPILock | + eFlagProcessMustBeLaunched) + { + } + + ~CommandObjectProcessInterrupt () + { + } + +protected: + bool + DoExecute (Args& command, + CommandReturnObject &result) + { + Process *process = m_exe_ctx.GetProcessPtr(); + if (process == NULL) + { + result.AppendError ("no process to halt"); + result.SetStatus (eReturnStatusFailed); + return false; + } + + if (command.GetArgumentCount() == 0) + { + bool clear_thread_plans = true; + Error error(process->Halt (clear_thread_plans)); + if (error.Success()) + { + result.SetStatus (eReturnStatusSuccessFinishResult); + } + else + { + result.AppendErrorWithFormat ("Failed to halt process: %s\n", error.AsCString()); + result.SetStatus (eReturnStatusFailed); + } + } + else + { + result.AppendErrorWithFormat("'%s' takes no arguments:\nUsage: %s\n", + m_cmd_name.c_str(), + m_cmd_syntax.c_str()); + result.SetStatus (eReturnStatusFailed); + } + return result.Succeeded(); + } +}; + +//------------------------------------------------------------------------- +// CommandObjectProcessKill +//------------------------------------------------------------------------- +#pragma mark CommandObjectProcessKill + +class CommandObjectProcessKill : public CommandObjectParsed +{ +public: + + CommandObjectProcessKill (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "process kill", + "Terminate the current process being debugged.", + "process kill", + eFlagRequiresProcess | + eFlagTryTargetAPILock | + eFlagProcessMustBeLaunched) + { + } + + ~CommandObjectProcessKill () + { + } + +protected: + bool + DoExecute (Args& command, + CommandReturnObject &result) + { + Process *process = m_exe_ctx.GetProcessPtr(); + if (process == NULL) + { + result.AppendError ("no process to kill"); + result.SetStatus (eReturnStatusFailed); + return false; + } + + if (command.GetArgumentCount() == 0) + { + Error error (process->Destroy()); + if (error.Success()) + { + result.SetStatus (eReturnStatusSuccessFinishResult); + } + else + { + result.AppendErrorWithFormat ("Failed to kill process: %s\n", error.AsCString()); + result.SetStatus (eReturnStatusFailed); + } + } + else + { + result.AppendErrorWithFormat("'%s' takes no arguments:\nUsage: %s\n", + m_cmd_name.c_str(), + m_cmd_syntax.c_str()); + result.SetStatus (eReturnStatusFailed); + } + return result.Succeeded(); + } +}; + +//------------------------------------------------------------------------- +// CommandObjectProcessStatus +//------------------------------------------------------------------------- +#pragma mark CommandObjectProcessStatus + +class CommandObjectProcessStatus : public CommandObjectParsed +{ +public: + CommandObjectProcessStatus (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "process status", + "Show the current status and location of executing process.", + "process status", + eFlagRequiresProcess | eFlagTryTargetAPILock) + { + } + + ~CommandObjectProcessStatus() + { + } + + + bool + DoExecute (Args& command, CommandReturnObject &result) + { + Stream &strm = result.GetOutputStream(); + result.SetStatus (eReturnStatusSuccessFinishNoResult); + // No need to check "process" for validity as eFlagRequiresProcess ensures it is valid + Process *process = m_exe_ctx.GetProcessPtr(); + const bool only_threads_with_stop_reason = true; + const uint32_t start_frame = 0; + const uint32_t num_frames = 1; + const uint32_t num_frames_with_source = 1; + process->GetStatus(strm); + process->GetThreadStatus (strm, + only_threads_with_stop_reason, + start_frame, + num_frames, + num_frames_with_source); + return result.Succeeded(); + } +}; + +//------------------------------------------------------------------------- +// CommandObjectProcessHandle +//------------------------------------------------------------------------- +#pragma mark CommandObjectProcessHandle + +class CommandObjectProcessHandle : public CommandObjectParsed +{ +public: + + class CommandOptions : public Options + { + public: + + CommandOptions (CommandInterpreter &interpreter) : + Options (interpreter) + { + OptionParsingStarting (); + } + + ~CommandOptions () + { + } + + Error + SetOptionValue (uint32_t option_idx, const char *option_arg) + { + Error error; + const int short_option = m_getopt_table[option_idx].val; + + switch (short_option) + { + case 's': + stop = option_arg; + break; + case 'n': + notify = option_arg; + break; + case 'p': + pass = option_arg; + break; + default: + error.SetErrorStringWithFormat("invalid short option character '%c'", short_option); + break; + } + return error; + } + + void + OptionParsingStarting () + { + stop.clear(); + notify.clear(); + pass.clear(); + } + + const OptionDefinition* + GetDefinitions () + { + return g_option_table; + } + + // Options table: Required for subclasses of Options. + + static OptionDefinition g_option_table[]; + + // Instance variables to hold the values for command options. + + std::string stop; + std::string notify; + std::string pass; + }; + + + CommandObjectProcessHandle (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "process handle", + "Show or update what the process and debugger should do with various signals received from the OS.", + NULL), + m_options (interpreter) + { + SetHelpLong ("If no signals are specified, update them all. If no update option is specified, list the current values.\n"); + CommandArgumentEntry arg; + CommandArgumentData signal_arg; + + signal_arg.arg_type = eArgTypeUnixSignal; + signal_arg.arg_repetition = eArgRepeatStar; + + arg.push_back (signal_arg); + + m_arguments.push_back (arg); + } + + ~CommandObjectProcessHandle () + { + } + + Options * + GetOptions () + { + return &m_options; + } + + bool + VerifyCommandOptionValue (const std::string &option, int &real_value) + { + bool okay = true; + + bool success = false; + bool tmp_value = Args::StringToBoolean (option.c_str(), false, &success); + + if (success && tmp_value) + real_value = 1; + else if (success && !tmp_value) + real_value = 0; + else + { + // If the value isn't 'true' or 'false', it had better be 0 or 1. + real_value = Args::StringToUInt32 (option.c_str(), 3); + if (real_value != 0 && real_value != 1) + okay = false; + } + + return okay; + } + + void + PrintSignalHeader (Stream &str) + { + str.Printf ("NAME PASS STOP NOTIFY\n"); + str.Printf ("========== ===== ===== ======\n"); + } + + void + PrintSignal (Stream &str, int32_t signo, const char *sig_name, UnixSignals &signals) + { + bool stop; + bool suppress; + bool notify; + + str.Printf ("%-10s ", sig_name); + if (signals.GetSignalInfo (signo, suppress, stop, notify)) + { + bool pass = !suppress; + str.Printf ("%s %s %s", + (pass ? "true " : "false"), + (stop ? "true " : "false"), + (notify ? "true " : "false")); + } + str.Printf ("\n"); + } + + void + PrintSignalInformation (Stream &str, Args &signal_args, int num_valid_signals, UnixSignals &signals) + { + PrintSignalHeader (str); + + if (num_valid_signals > 0) + { + size_t num_args = signal_args.GetArgumentCount(); + for (size_t i = 0; i < num_args; ++i) + { + int32_t signo = signals.GetSignalNumberFromName (signal_args.GetArgumentAtIndex (i)); + if (signo != LLDB_INVALID_SIGNAL_NUMBER) + PrintSignal (str, signo, signal_args.GetArgumentAtIndex (i), signals); + } + } + else // Print info for ALL signals + { + int32_t signo = signals.GetFirstSignalNumber(); + while (signo != LLDB_INVALID_SIGNAL_NUMBER) + { + PrintSignal (str, signo, signals.GetSignalAsCString (signo), signals); + signo = signals.GetNextSignalNumber (signo); + } + } + } + +protected: + bool + DoExecute (Args &signal_args, CommandReturnObject &result) + { + TargetSP target_sp = m_interpreter.GetDebugger().GetSelectedTarget(); + + if (!target_sp) + { + result.AppendError ("No current target;" + " cannot handle signals until you have a valid target and process.\n"); + result.SetStatus (eReturnStatusFailed); + return false; + } + + ProcessSP process_sp = target_sp->GetProcessSP(); + + if (!process_sp) + { + result.AppendError ("No current process; cannot handle signals until you have a valid process.\n"); + result.SetStatus (eReturnStatusFailed); + return false; + } + + int stop_action = -1; // -1 means leave the current setting alone + int pass_action = -1; // -1 means leave the current setting alone + int notify_action = -1; // -1 means leave the current setting alone + + if (! m_options.stop.empty() + && ! VerifyCommandOptionValue (m_options.stop, stop_action)) + { + result.AppendError ("Invalid argument for command option --stop; must be true or false.\n"); + result.SetStatus (eReturnStatusFailed); + return false; + } + + if (! m_options.notify.empty() + && ! VerifyCommandOptionValue (m_options.notify, notify_action)) + { + result.AppendError ("Invalid argument for command option --notify; must be true or false.\n"); + result.SetStatus (eReturnStatusFailed); + return false; + } + + if (! m_options.pass.empty() + && ! VerifyCommandOptionValue (m_options.pass, pass_action)) + { + result.AppendError ("Invalid argument for command option --pass; must be true or false.\n"); + result.SetStatus (eReturnStatusFailed); + return false; + } + + size_t num_args = signal_args.GetArgumentCount(); + UnixSignals &signals = process_sp->GetUnixSignals(); + int num_signals_set = 0; + + if (num_args > 0) + { + for (size_t i = 0; i < num_args; ++i) + { + int32_t signo = signals.GetSignalNumberFromName (signal_args.GetArgumentAtIndex (i)); + if (signo != LLDB_INVALID_SIGNAL_NUMBER) + { + // Casting the actions as bools here should be okay, because VerifyCommandOptionValue guarantees + // the value is either 0 or 1. + if (stop_action != -1) + signals.SetShouldStop (signo, (bool) stop_action); + if (pass_action != -1) + { + bool suppress = ! ((bool) pass_action); + signals.SetShouldSuppress (signo, suppress); + } + if (notify_action != -1) + signals.SetShouldNotify (signo, (bool) notify_action); + ++num_signals_set; + } + else + { + result.AppendErrorWithFormat ("Invalid signal name '%s'\n", signal_args.GetArgumentAtIndex (i)); + } + } + } + else + { + // No signal specified, if any command options were specified, update ALL signals. + if ((notify_action != -1) || (stop_action != -1) || (pass_action != -1)) + { + if (m_interpreter.Confirm ("Do you really want to update all the signals?", false)) + { + int32_t signo = signals.GetFirstSignalNumber(); + while (signo != LLDB_INVALID_SIGNAL_NUMBER) + { + if (notify_action != -1) + signals.SetShouldNotify (signo, (bool) notify_action); + if (stop_action != -1) + signals.SetShouldStop (signo, (bool) stop_action); + if (pass_action != -1) + { + bool suppress = ! ((bool) pass_action); + signals.SetShouldSuppress (signo, suppress); + } + signo = signals.GetNextSignalNumber (signo); + } + } + } + } + + PrintSignalInformation (result.GetOutputStream(), signal_args, num_signals_set, signals); + + if (num_signals_set > 0) + result.SetStatus (eReturnStatusSuccessFinishNoResult); + else + result.SetStatus (eReturnStatusFailed); + + return result.Succeeded(); + } + + CommandOptions m_options; +}; + +OptionDefinition +CommandObjectProcessHandle::CommandOptions::g_option_table[] = +{ +{ LLDB_OPT_SET_1, false, "stop", 's', required_argument, NULL, 0, eArgTypeBoolean, "Whether or not the process should be stopped if the signal is received." }, +{ LLDB_OPT_SET_1, false, "notify", 'n', required_argument, NULL, 0, eArgTypeBoolean, "Whether or not the debugger should notify the user if the signal is received." }, +{ LLDB_OPT_SET_1, false, "pass", 'p', required_argument, NULL, 0, eArgTypeBoolean, "Whether or not the signal should be passed to the process." }, +{ 0, false, NULL, 0, 0, NULL, 0, eArgTypeNone, NULL } +}; + +//------------------------------------------------------------------------- +// CommandObjectMultiwordProcess +//------------------------------------------------------------------------- + +CommandObjectMultiwordProcess::CommandObjectMultiwordProcess (CommandInterpreter &interpreter) : + CommandObjectMultiword (interpreter, + "process", + "A set of commands for operating on a process.", + "process []") +{ + LoadSubCommand ("attach", CommandObjectSP (new CommandObjectProcessAttach (interpreter))); + LoadSubCommand ("launch", CommandObjectSP (new CommandObjectProcessLaunch (interpreter))); + LoadSubCommand ("continue", CommandObjectSP (new CommandObjectProcessContinue (interpreter))); + LoadSubCommand ("connect", CommandObjectSP (new CommandObjectProcessConnect (interpreter))); + LoadSubCommand ("detach", CommandObjectSP (new CommandObjectProcessDetach (interpreter))); + LoadSubCommand ("load", CommandObjectSP (new CommandObjectProcessLoad (interpreter))); + LoadSubCommand ("unload", CommandObjectSP (new CommandObjectProcessUnload (interpreter))); + LoadSubCommand ("signal", CommandObjectSP (new CommandObjectProcessSignal (interpreter))); + LoadSubCommand ("handle", CommandObjectSP (new CommandObjectProcessHandle (interpreter))); + LoadSubCommand ("status", CommandObjectSP (new CommandObjectProcessStatus (interpreter))); + LoadSubCommand ("interrupt", CommandObjectSP (new CommandObjectProcessInterrupt (interpreter))); + LoadSubCommand ("kill", CommandObjectSP (new CommandObjectProcessKill (interpreter))); + LoadSubCommand ("plugin", CommandObjectSP (new CommandObjectProcessPlugin (interpreter))); +} + +CommandObjectMultiwordProcess::~CommandObjectMultiwordProcess () +{ +} + diff --git a/contrib/llvm/tools/lldb/source/Commands/CommandObjectProcess.h b/contrib/llvm/tools/lldb/source/Commands/CommandObjectProcess.h new file mode 100644 index 00000000000..0aaa74d28a0 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Commands/CommandObjectProcess.h @@ -0,0 +1,37 @@ +//===-- CommandObjectProcess.h ----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_CommandObjectProcess_h_ +#define liblldb_CommandObjectProcess_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Interpreter/CommandObjectMultiword.h" + +namespace lldb_private { + +//------------------------------------------------------------------------- +// CommandObjectMultiwordProcess +//------------------------------------------------------------------------- + +class CommandObjectMultiwordProcess : public CommandObjectMultiword +{ +public: + CommandObjectMultiwordProcess (CommandInterpreter &interpreter); + + virtual + ~CommandObjectMultiwordProcess (); + +}; + +} // namespace lldb_private + +#endif // liblldb_CommandObjectProcess_h_ diff --git a/contrib/llvm/tools/lldb/source/Commands/CommandObjectQuit.cpp b/contrib/llvm/tools/lldb/source/Commands/CommandObjectQuit.cpp new file mode 100644 index 00000000000..d04ecdd9885 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Commands/CommandObjectQuit.cpp @@ -0,0 +1,99 @@ +//===-- CommandObjectQuit.cpp -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "CommandObjectQuit.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Interpreter/CommandReturnObject.h" + +using namespace lldb; +using namespace lldb_private; + +//------------------------------------------------------------------------- +// CommandObjectQuit +//------------------------------------------------------------------------- + +CommandObjectQuit::CommandObjectQuit (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, "quit", "Quit out of the LLDB debugger.", "quit") +{ +} + +CommandObjectQuit::~CommandObjectQuit () +{ +} + +// returns true if there is at least one alive process +// is_a_detach will be true if all alive processes will be detached when you quit +// and false if at least one process will be killed instead +bool +CommandObjectQuit::ShouldAskForConfirmation (bool& is_a_detach) +{ + if (m_interpreter.GetPromptOnQuit() == false) + return false; + bool should_prompt = false; + is_a_detach = true; + for (uint32_t debugger_idx = 0; + debugger_idx < Debugger::GetNumDebuggers(); + debugger_idx++) + { + DebuggerSP debugger_sp(Debugger::GetDebuggerAtIndex(debugger_idx)); + if (!debugger_sp) + continue; + const TargetList& target_list(debugger_sp->GetTargetList()); + for (uint32_t target_idx = 0; + target_idx < target_list.GetNumTargets(); + target_idx++) + { + TargetSP target_sp(target_list.GetTargetAtIndex(target_idx)); + if (!target_sp) + continue; + ProcessSP process_sp(target_sp->GetProcessSP()); + if (process_sp + && process_sp->IsValid() + && process_sp->IsAlive() + && process_sp->WarnBeforeDetach()) + { + should_prompt = true; + if (process_sp->GetShouldDetach() == false) + { + // if we need to kill at least one process, just say so and return + is_a_detach = false; + return should_prompt; + } + } + } + } + return should_prompt; +} + +bool +CommandObjectQuit::DoExecute (Args& command, CommandReturnObject &result) +{ + bool is_a_detach = true; + if (ShouldAskForConfirmation (is_a_detach)) + { + StreamString message; + message.Printf("Quitting LLDB will %s one or more processes. Do you really want to proceed", (is_a_detach ? "detach from" : "kill")); + if (!m_interpreter.Confirm(message.GetData(), true)) + { + result.SetStatus(eReturnStatusFailed); + return false; + } + } + m_interpreter.BroadcastEvent (CommandInterpreter::eBroadcastBitQuitCommandReceived); + result.SetStatus (eReturnStatusQuit); + return true; +} + diff --git a/contrib/llvm/tools/lldb/source/Commands/CommandObjectQuit.h b/contrib/llvm/tools/lldb/source/Commands/CommandObjectQuit.h new file mode 100644 index 00000000000..aab0e26cce5 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Commands/CommandObjectQuit.h @@ -0,0 +1,46 @@ +//===-- CommandObjectQuit.h -------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_CommandObjectQuit_h_ +#define liblldb_CommandObjectQuit_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Interpreter/CommandObject.h" + +namespace lldb_private { + +//------------------------------------------------------------------------- +// CommandObjectQuit +//------------------------------------------------------------------------- + +class CommandObjectQuit : public CommandObjectParsed +{ +public: + + CommandObjectQuit (CommandInterpreter &interpreter); + + virtual + ~CommandObjectQuit (); + +protected: + virtual bool + DoExecute (Args& args, + CommandReturnObject &result); + + bool + ShouldAskForConfirmation (bool& is_a_detach); + +}; + +} // namespace lldb_private + +#endif // liblldb_CommandObjectQuit_h_ diff --git a/contrib/llvm/tools/lldb/source/Commands/CommandObjectRegister.cpp b/contrib/llvm/tools/lldb/source/Commands/CommandObjectRegister.cpp new file mode 100644 index 00000000000..ba43f23f34a --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Commands/CommandObjectRegister.cpp @@ -0,0 +1,499 @@ +//===-- CommandObjectRegister.cpp -------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "CommandObjectRegister.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/RegisterValue.h" +#include "lldb/Core/Scalar.h" +#include "lldb/Core/Debugger.h" +#include "lldb/Interpreter/Args.h" +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Interpreter/CommandReturnObject.h" +#include "lldb/Interpreter/Options.h" +#include "lldb/Interpreter/OptionGroupFormat.h" +#include "lldb/Interpreter/OptionValueArray.h" +#include "lldb/Interpreter/OptionValueUInt64.h" +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/Thread.h" + +using namespace lldb; +using namespace lldb_private; + +//---------------------------------------------------------------------- +// "register read" +//---------------------------------------------------------------------- +class CommandObjectRegisterRead : public CommandObjectParsed +{ +public: + CommandObjectRegisterRead (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "register read", + "Dump the contents of one or more register values from the current frame. If no register is specified, dumps them all.", + NULL, + eFlagRequiresFrame | + eFlagRequiresRegContext | + eFlagProcessMustBeLaunched | + eFlagProcessMustBePaused ), + m_option_group (interpreter), + m_format_options (eFormatDefault), + m_command_options () + { + CommandArgumentEntry arg; + CommandArgumentData register_arg; + + // Define the first (and only) variant of this arg. + register_arg.arg_type = eArgTypeRegisterName; + register_arg.arg_repetition = eArgRepeatStar; + + // There is only one variant this argument could be; put it into the argument entry. + arg.push_back (register_arg); + + // Push the data for the first argument into the m_arguments vector. + m_arguments.push_back (arg); + + // Add the "--format" + m_option_group.Append (&m_format_options, OptionGroupFormat::OPTION_GROUP_FORMAT | OptionGroupFormat::OPTION_GROUP_GDB_FMT, LLDB_OPT_SET_ALL); + m_option_group.Append (&m_command_options); + m_option_group.Finalize(); + + } + + virtual + ~CommandObjectRegisterRead () + { + } + + Options * + GetOptions () + { + return &m_option_group; + } + + bool + DumpRegister (const ExecutionContext &exe_ctx, + Stream &strm, + RegisterContext *reg_ctx, + const RegisterInfo *reg_info) + { + if (reg_info) + { + RegisterValue reg_value; + + if (reg_ctx->ReadRegister (reg_info, reg_value)) + { + strm.Indent (); + + bool prefix_with_altname = m_command_options.alternate_name; + bool prefix_with_name = !prefix_with_altname; + reg_value.Dump(&strm, reg_info, prefix_with_name, prefix_with_altname, m_format_options.GetFormat(), 8); + if ((reg_info->encoding == eEncodingUint) || (reg_info->encoding == eEncodingSint)) + { + Process *process = exe_ctx.GetProcessPtr(); + if (process && reg_info->byte_size == process->GetAddressByteSize()) + { + addr_t reg_addr = reg_value.GetAsUInt64(LLDB_INVALID_ADDRESS); + if (reg_addr != LLDB_INVALID_ADDRESS) + { + Address so_reg_addr; + if (exe_ctx.GetTargetRef().GetSectionLoadList().ResolveLoadAddress(reg_addr, so_reg_addr)) + { + strm.PutCString (" "); + so_reg_addr.Dump(&strm, exe_ctx.GetBestExecutionContextScope(), Address::DumpStyleResolvedDescription); + } + } + } + } + strm.EOL(); + return true; + } + } + return false; + } + + bool + DumpRegisterSet (const ExecutionContext &exe_ctx, + Stream &strm, + RegisterContext *reg_ctx, + size_t set_idx, + bool primitive_only=false) + { + uint32_t unavailable_count = 0; + uint32_t available_count = 0; + + if (!reg_ctx) + return false; // thread has no registers (i.e. core files are corrupt, incomplete crash logs...) + + const RegisterSet * const reg_set = reg_ctx->GetRegisterSet(set_idx); + if (reg_set) + { + strm.Printf ("%s:\n", reg_set->name); + strm.IndentMore (); + const size_t num_registers = reg_set->num_registers; + for (size_t reg_idx = 0; reg_idx < num_registers; ++reg_idx) + { + const uint32_t reg = reg_set->registers[reg_idx]; + const RegisterInfo *reg_info = reg_ctx->GetRegisterInfoAtIndex(reg); + // Skip the dumping of derived register if primitive_only is true. + if (primitive_only && reg_info && reg_info->value_regs) + continue; + + if (DumpRegister (exe_ctx, strm, reg_ctx, reg_info)) + ++available_count; + else + ++unavailable_count; + } + strm.IndentLess (); + if (unavailable_count) + { + strm.Indent (); + strm.Printf("%u registers were unavailable.\n", unavailable_count); + } + strm.EOL(); + } + return available_count > 0; + } + +protected: + virtual bool + DoExecute (Args& command, CommandReturnObject &result) + { + Stream &strm = result.GetOutputStream(); + RegisterContext *reg_ctx = m_exe_ctx.GetRegisterContext (); + + const RegisterInfo *reg_info = NULL; + if (command.GetArgumentCount() == 0) + { + size_t set_idx; + + size_t num_register_sets = 1; + const size_t set_array_size = m_command_options.set_indexes.GetSize(); + if (set_array_size > 0) + { + for (size_t i=0; iGetUInt64Value (UINT32_MAX, NULL); + if (set_idx < reg_ctx->GetRegisterSetCount()) + { + if (!DumpRegisterSet (m_exe_ctx, strm, reg_ctx, set_idx)) + { + if (errno) + result.AppendErrorWithFormat ("register read failed with errno: %d\n", errno); + else + result.AppendError ("unknown error while reading registers.\n"); + result.SetStatus (eReturnStatusFailed); + break; + } + } + else + { + result.AppendErrorWithFormat ("invalid register set index: %zu\n", set_idx); + result.SetStatus (eReturnStatusFailed); + break; + } + } + } + else + { + if (m_command_options.dump_all_sets) + num_register_sets = reg_ctx->GetRegisterSetCount(); + + for (set_idx = 0; set_idx < num_register_sets; ++set_idx) + { + // When dump_all_sets option is set, dump primitive as well as derived registers. + DumpRegisterSet (m_exe_ctx, strm, reg_ctx, set_idx, !m_command_options.dump_all_sets.GetCurrentValue()); + } + } + } + else + { + if (m_command_options.dump_all_sets) + { + result.AppendError ("the --all option can't be used when registers names are supplied as arguments\n"); + result.SetStatus (eReturnStatusFailed); + } + else if (m_command_options.set_indexes.GetSize() > 0) + { + result.AppendError ("the --set option can't be used when registers names are supplied as arguments\n"); + result.SetStatus (eReturnStatusFailed); + } + else + { + const char *arg_cstr; + for (int arg_idx = 0; (arg_cstr = command.GetArgumentAtIndex(arg_idx)) != NULL; ++arg_idx) + { + // in most LLDB commands we accept $rbx as the name for register RBX - and here we would + // reject it and non-existant. we should be more consistent towards the user and allow them + // to say reg read $rbx - internally, however, we should be strict and not allow ourselves + // to call our registers $rbx in our own API + if (*arg_cstr == '$') + arg_cstr = arg_cstr+1; + reg_info = reg_ctx->GetRegisterInfoByName(arg_cstr); + + if (reg_info) + { + if (!DumpRegister (m_exe_ctx, strm, reg_ctx, reg_info)) + strm.Printf("%-12s = error: unavailable\n", reg_info->name); + } + else + { + result.AppendErrorWithFormat ("Invalid register name '%s'.\n", arg_cstr); + } + } + } + } + return result.Succeeded(); + } + + class CommandOptions : public OptionGroup + { + public: + CommandOptions () : + OptionGroup(), + set_indexes (OptionValue::ConvertTypeToMask (OptionValue::eTypeUInt64)), + dump_all_sets (false, false), // Initial and default values are false + alternate_name (false, false) + { + } + + virtual + ~CommandOptions () + { + } + + + virtual uint32_t + GetNumDefinitions (); + + virtual const OptionDefinition* + GetDefinitions () + { + return g_option_table; + } + + virtual void + OptionParsingStarting (CommandInterpreter &interpreter) + { + set_indexes.Clear(); + dump_all_sets.Clear(); + alternate_name.Clear(); + } + + virtual Error + SetOptionValue (CommandInterpreter &interpreter, + uint32_t option_idx, + const char *option_value) + { + Error error; + const int short_option = g_option_table[option_idx].short_option; + switch (short_option) + { + case 's': + { + OptionValueSP value_sp (OptionValueUInt64::Create (option_value, error)); + if (value_sp) + set_indexes.AppendValue (value_sp); + } + break; + + case 'a': + // When we don't use OptionValue::SetValueFromCString(const char *) to + // set an option value, it won't be marked as being set in the options + // so we make a call to let users know the value was set via option + dump_all_sets.SetCurrentValue (true); + dump_all_sets.SetOptionWasSet (); + break; + + case 'A': + // When we don't use OptionValue::SetValueFromCString(const char *) to + // set an option value, it won't be marked as being set in the options + // so we make a call to let users know the value was set via option + alternate_name.SetCurrentValue (true); + dump_all_sets.SetOptionWasSet (); + break; + + default: + error.SetErrorStringWithFormat("unrecognized short option '%c'", short_option); + break; + } + return error; + } + + // Options table: Required for subclasses of Options. + + static const OptionDefinition g_option_table[]; + + // Instance variables to hold the values for command options. + OptionValueArray set_indexes; + OptionValueBoolean dump_all_sets; + OptionValueBoolean alternate_name; + }; + + OptionGroupOptions m_option_group; + OptionGroupFormat m_format_options; + CommandOptions m_command_options; +}; + +const OptionDefinition +CommandObjectRegisterRead::CommandOptions::g_option_table[] = +{ + { LLDB_OPT_SET_ALL, false, "alternate", 'A', no_argument , NULL, 0, eArgTypeNone , "Display register names using the alternate register name if there is one."}, + { LLDB_OPT_SET_1 , false, "set" , 's', required_argument, NULL, 0, eArgTypeIndex , "Specify which register sets to dump by index."}, + { LLDB_OPT_SET_2 , false, "all" , 'a', no_argument , NULL, 0, eArgTypeNone , "Show all register sets."}, +}; + +uint32_t +CommandObjectRegisterRead::CommandOptions::GetNumDefinitions () +{ + return sizeof(g_option_table)/sizeof(OptionDefinition); +} + + +//---------------------------------------------------------------------- +// "register write" +//---------------------------------------------------------------------- +class CommandObjectRegisterWrite : public CommandObjectParsed +{ +public: + CommandObjectRegisterWrite (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "register write", + "Modify a single register value.", + NULL, + eFlagRequiresFrame | + eFlagRequiresRegContext | + eFlagProcessMustBeLaunched | + eFlagProcessMustBePaused) + { + CommandArgumentEntry arg1; + CommandArgumentEntry arg2; + CommandArgumentData register_arg; + CommandArgumentData value_arg; + + // Define the first (and only) variant of this arg. + register_arg.arg_type = eArgTypeRegisterName; + register_arg.arg_repetition = eArgRepeatPlain; + + // There is only one variant this argument could be; put it into the argument entry. + arg1.push_back (register_arg); + + // Define the first (and only) variant of this arg. + value_arg.arg_type = eArgTypeValue; + value_arg.arg_repetition = eArgRepeatPlain; + + // There is only one variant this argument could be; put it into the argument entry. + arg2.push_back (value_arg); + + // Push the data for the first argument into the m_arguments vector. + m_arguments.push_back (arg1); + m_arguments.push_back (arg2); + } + + virtual + ~CommandObjectRegisterWrite () + { + } + +protected: + virtual bool + DoExecute(Args& command, CommandReturnObject &result) + { + DataExtractor reg_data; + RegisterContext *reg_ctx = m_exe_ctx.GetRegisterContext (); + + if (command.GetArgumentCount() != 2) + { + result.AppendError ("register write takes exactly 2 arguments: "); + result.SetStatus (eReturnStatusFailed); + } + else + { + const char *reg_name = command.GetArgumentAtIndex(0); + const char *value_str = command.GetArgumentAtIndex(1); + + + // in most LLDB commands we accept $rbx as the name for register RBX - and here we would + // reject it and non-existant. we should be more consistent towards the user and allow them + // to say reg write $rbx - internally, however, we should be strict and not allow ourselves + // to call our registers $rbx in our own API + if (reg_name && *reg_name == '$') + reg_name = reg_name+1; + + const RegisterInfo *reg_info = reg_ctx->GetRegisterInfoByName(reg_name); + + if (reg_info) + { + RegisterValue reg_value; + + Error error (reg_value.SetValueFromCString (reg_info, value_str)); + if (error.Success()) + { + if (reg_ctx->WriteRegister (reg_info, reg_value)) + { + // Toss all frames and anything else in the thread + // after a register has been written. + m_exe_ctx.GetThreadRef().Flush(); + result.SetStatus (eReturnStatusSuccessFinishNoResult); + return true; + } + } + if (error.AsCString()) + { + result.AppendErrorWithFormat ("Failed to write register '%s' with value '%s': %s\n", + reg_name, + value_str, + error.AsCString()); + } + else + { + result.AppendErrorWithFormat ("Failed to write register '%s' with value '%s'", + reg_name, + value_str); + } + result.SetStatus (eReturnStatusFailed); + } + else + { + result.AppendErrorWithFormat ("Register not found for '%s'.\n", reg_name); + result.SetStatus (eReturnStatusFailed); + } + } + return result.Succeeded(); + } +}; + + +//---------------------------------------------------------------------- +// CommandObjectRegister constructor +//---------------------------------------------------------------------- +CommandObjectRegister::CommandObjectRegister(CommandInterpreter &interpreter) : + CommandObjectMultiword (interpreter, + "register", + "A set of commands to access thread registers.", + "register [read|write] ...") +{ + LoadSubCommand ("read", CommandObjectSP (new CommandObjectRegisterRead (interpreter))); + LoadSubCommand ("write", CommandObjectSP (new CommandObjectRegisterWrite (interpreter))); +} + + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +CommandObjectRegister::~CommandObjectRegister() +{ +} diff --git a/contrib/llvm/tools/lldb/source/Commands/CommandObjectRegister.h b/contrib/llvm/tools/lldb/source/Commands/CommandObjectRegister.h new file mode 100644 index 00000000000..7f856c2de52 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Commands/CommandObjectRegister.h @@ -0,0 +1,45 @@ +//===-- CommandObjectRegister.h ---------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_CommandObjectRegister_h_ +#define liblldb_CommandObjectRegister_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Interpreter/CommandObjectMultiword.h" + +namespace lldb_private { + +//------------------------------------------------------------------------- +// CommandObjectRegister +//------------------------------------------------------------------------- + +class CommandObjectRegister : public CommandObjectMultiword +{ +public: + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + CommandObjectRegister(CommandInterpreter &interpreter); + + virtual + ~CommandObjectRegister(); + +private: + //------------------------------------------------------------------ + // For CommandObjectRegister only + //------------------------------------------------------------------ + DISALLOW_COPY_AND_ASSIGN (CommandObjectRegister); +}; + +} // namespace lldb_private + +#endif // liblldb_CommandObjectRegister_h_ diff --git a/contrib/llvm/tools/lldb/source/Commands/CommandObjectSettings.cpp b/contrib/llvm/tools/lldb/source/Commands/CommandObjectSettings.cpp new file mode 100644 index 00000000000..95cc9b68a8f --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Commands/CommandObjectSettings.cpp @@ -0,0 +1,1208 @@ +//===-- CommandObjectSettings.cpp -------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "CommandObjectSettings.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Interpreter/CommandReturnObject.h" +#include "lldb/Interpreter/CommandCompletions.h" + +using namespace lldb; +using namespace lldb_private; +#include "llvm/ADT/StringRef.h" + +//------------------------------------------------------------------------- +// CommandObjectSettingsSet +//------------------------------------------------------------------------- + +class CommandObjectSettingsSet : public CommandObjectRaw +{ +public: + CommandObjectSettingsSet (CommandInterpreter &interpreter) : + CommandObjectRaw (interpreter, + "settings set", + "Set or change the value of a single debugger setting variable.", + NULL), + m_options (interpreter) + { + CommandArgumentEntry arg1; + CommandArgumentEntry arg2; + CommandArgumentData var_name_arg; + CommandArgumentData value_arg; + + // Define the first (and only) variant of this arg. + var_name_arg.arg_type = eArgTypeSettingVariableName; + var_name_arg.arg_repetition = eArgRepeatPlain; + + // There is only one variant this argument could be; put it into the argument entry. + arg1.push_back (var_name_arg); + + // Define the first (and only) variant of this arg. + value_arg.arg_type = eArgTypeValue; + value_arg.arg_repetition = eArgRepeatPlain; + + // There is only one variant this argument could be; put it into the argument entry. + arg2.push_back (value_arg); + + // Push the data for the first argument into the m_arguments vector. + m_arguments.push_back (arg1); + m_arguments.push_back (arg2); + + SetHelpLong ( +"When setting a dictionary or array variable, you can set multiple entries \n\ +at once by giving the values to the set command. For example: \n\ +\n\ +(lldb) settings set target.run-args value1 value2 value3 \n\ +(lldb) settings set target.env-vars MYPATH=~/.:/usr/bin SOME_ENV_VAR=12345 \n\ +\n\ +(lldb) settings show target.run-args \n\ + [0]: 'value1' \n\ + [1]: 'value2' \n\ + [3]: 'value3' \n\ +(lldb) settings show target.env-vars \n\ + 'MYPATH=~/.:/usr/bin'\n\ + 'SOME_ENV_VAR=12345' \n\ +\n\ +Warning: The 'set' command re-sets the entire array or dictionary. If you \n\ +just want to add, remove or update individual values (or add something to \n\ +the end), use one of the other settings sub-commands: append, replace, \n\ +insert-before or insert-after.\n"); + + } + + + virtual + ~CommandObjectSettingsSet () {} + + // Overrides base class's behavior where WantsCompletion = !WantsRawCommandString. + virtual bool + WantsCompletion() { return true; } + + virtual Options * + GetOptions () + { + return &m_options; + } + + class CommandOptions : public Options + { + public: + + CommandOptions (CommandInterpreter &interpreter) : + Options (interpreter), + m_global (false) + { + } + + virtual + ~CommandOptions () {} + + virtual Error + SetOptionValue (uint32_t option_idx, const char *option_arg) + { + Error error; + const int short_option = m_getopt_table[option_idx].val; + + switch (short_option) + { + case 'g': + m_global = true; + break; + default: + error.SetErrorStringWithFormat ("unrecognized options '%c'", short_option); + break; + } + + return error; + } + + void + OptionParsingStarting () + { + m_global = false; + } + + const OptionDefinition* + GetDefinitions () + { + return g_option_table; + } + + // Options table: Required for subclasses of Options. + + static OptionDefinition g_option_table[]; + + // Instance variables to hold the values for command options. + + bool m_global; + }; + + virtual int + HandleArgumentCompletion (Args &input, + int &cursor_index, + int &cursor_char_position, + OptionElementVector &opt_element_vector, + int match_start_point, + int max_return_elements, + bool &word_complete, + StringList &matches) + { + std::string completion_str (input.GetArgumentAtIndex (cursor_index), cursor_char_position); + + const size_t argc = input.GetArgumentCount(); + const char *arg = NULL; + int setting_var_idx; + for (setting_var_idx = 1; setting_var_idx < argc; ++setting_var_idx) + { + arg = input.GetArgumentAtIndex(setting_var_idx); + if (arg && arg[0] != '-') + break; // We found our setting variable name index + } + if (cursor_index == setting_var_idx) + { + // Attempting to complete setting variable name + CommandCompletions::InvokeCommonCompletionCallbacks (m_interpreter, + CommandCompletions::eSettingsNameCompletion, + completion_str.c_str(), + match_start_point, + max_return_elements, + NULL, + word_complete, + matches); + } + else + { + arg = input.GetArgumentAtIndex(cursor_index); + + if (arg) + { + if (arg[0] == '-') + { + // Complete option name + } + else + { + // Complete setting value + const char *setting_var_name = input.GetArgumentAtIndex(setting_var_idx); + Error error; + lldb::OptionValueSP value_sp (m_interpreter.GetDebugger().GetPropertyValue(&m_exe_ctx, setting_var_name, false, error)); + if (value_sp) + { + value_sp->AutoComplete (m_interpreter, + completion_str.c_str(), + match_start_point, + max_return_elements, + word_complete, + matches); + } + } + } + } + return matches.GetSize(); + } + +protected: + virtual bool + DoExecute (const char *command, CommandReturnObject &result) + { + Args cmd_args(command); + + // Process possible options. + if (!ParseOptions (cmd_args, result)) + return false; + + const size_t argc = cmd_args.GetArgumentCount (); + if ((argc < 2) && (!m_options.m_global)) + { + result.AppendError ("'settings set' takes more arguments"); + result.SetStatus (eReturnStatusFailed); + return false; + } + + const char *var_name = cmd_args.GetArgumentAtIndex (0); + if ((var_name == NULL) || (var_name[0] == '\0')) + { + result.AppendError ("'settings set' command requires a valid variable name"); + result.SetStatus (eReturnStatusFailed); + return false; + } + + // Split the raw command into var_name and value pair. + llvm::StringRef raw_str(command); + std::string var_value_string = raw_str.split(var_name).second.str(); + const char *var_value_cstr = Args::StripSpaces(var_value_string, true, true, false); + + Error error; + if (m_options.m_global) + { + error = m_interpreter.GetDebugger().SetPropertyValue (NULL, + eVarSetOperationAssign, + var_name, + var_value_cstr); + } + + if (error.Success()) + { + // FIXME this is the same issue as the one in commands script import + // we could be setting target.load-script-from-symbol-file which would cause + // Python scripts to be loaded, which could run LLDB commands + // (e.g. settings set target.process.python-os-plugin-path) and cause a crash + // if we did not clear the command's exe_ctx first + ExecutionContext exe_ctx(m_exe_ctx); + m_exe_ctx.Clear(); + error = m_interpreter.GetDebugger().SetPropertyValue (&exe_ctx, + eVarSetOperationAssign, + var_name, + var_value_cstr); + } + + if (error.Fail()) + { + result.AppendError (error.AsCString()); + result.SetStatus (eReturnStatusFailed); + return false; + } + else + { + result.SetStatus (eReturnStatusSuccessFinishResult); + } + + return result.Succeeded(); + } +private: + CommandOptions m_options; +}; + +OptionDefinition +CommandObjectSettingsSet::CommandOptions::g_option_table[] = +{ + { LLDB_OPT_SET_2, false, "global", 'g', no_argument, NULL, 0, eArgTypeNone, "Apply the new value to the global default value." }, + { 0, false, NULL, 0, 0, NULL, 0, eArgTypeNone, NULL } +}; + + +//------------------------------------------------------------------------- +// CommandObjectSettingsShow -- Show current values +//------------------------------------------------------------------------- + +class CommandObjectSettingsShow : public CommandObjectParsed +{ +public: + CommandObjectSettingsShow (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "settings show", + "Show the specified internal debugger setting variable and its value, or show all the currently set variables and their values, if nothing is specified.", + NULL) + { + CommandArgumentEntry arg1; + CommandArgumentData var_name_arg; + + // Define the first (and only) variant of this arg. + var_name_arg.arg_type = eArgTypeSettingVariableName; + var_name_arg.arg_repetition = eArgRepeatOptional; + + // There is only one variant this argument could be; put it into the argument entry. + arg1.push_back (var_name_arg); + + // Push the data for the first argument into the m_arguments vector. + m_arguments.push_back (arg1); + } + + virtual + ~CommandObjectSettingsShow () {} + + + virtual int + HandleArgumentCompletion (Args &input, + int &cursor_index, + int &cursor_char_position, + OptionElementVector &opt_element_vector, + int match_start_point, + int max_return_elements, + bool &word_complete, + StringList &matches) + { + std::string completion_str (input.GetArgumentAtIndex (cursor_index), cursor_char_position); + + CommandCompletions::InvokeCommonCompletionCallbacks (m_interpreter, + CommandCompletions::eSettingsNameCompletion, + completion_str.c_str(), + match_start_point, + max_return_elements, + NULL, + word_complete, + matches); + return matches.GetSize(); + } + +protected: + virtual bool + DoExecute (Args& args, CommandReturnObject &result) + { + result.SetStatus (eReturnStatusSuccessFinishResult); + + const size_t argc = args.GetArgumentCount (); + if (argc > 0) + { + for (size_t i=0; i 0) + { + const bool dump_qualified_name = true; + + for (size_t i=0; iGetPropertyAtPath (&m_exe_ctx, will_modify, property_path); + + if (property) + { + property->DumpDescription (m_interpreter, result.GetOutputStream(), 0, dump_qualified_name); + } + else + { + result.AppendErrorWithFormat ("invalid property path '%s'", property_path); + result.SetStatus (eReturnStatusFailed); + } + } + } + else + { + m_interpreter.GetDebugger().DumpAllDescriptions (m_interpreter, result.GetOutputStream()); + } + + return result.Succeeded(); + } +}; + +//------------------------------------------------------------------------- +// CommandObjectSettingsRemove +//------------------------------------------------------------------------- + +class CommandObjectSettingsRemove : public CommandObjectRaw +{ +public: + CommandObjectSettingsRemove (CommandInterpreter &interpreter) : + CommandObjectRaw (interpreter, + "settings remove", + "Remove the specified element from an array or dictionary settings variable.", + NULL) + { + CommandArgumentEntry arg1; + CommandArgumentEntry arg2; + CommandArgumentData var_name_arg; + CommandArgumentData index_arg; + CommandArgumentData key_arg; + + // Define the first (and only) variant of this arg. + var_name_arg.arg_type = eArgTypeSettingVariableName; + var_name_arg.arg_repetition = eArgRepeatPlain; + + // There is only one variant this argument could be; put it into the argument entry. + arg1.push_back (var_name_arg); + + // Define the first variant of this arg. + index_arg.arg_type = eArgTypeSettingIndex; + index_arg.arg_repetition = eArgRepeatPlain; + + // Define the second variant of this arg. + key_arg.arg_type = eArgTypeSettingKey; + key_arg.arg_repetition = eArgRepeatPlain; + + // Push both variants into this arg + arg2.push_back (index_arg); + arg2.push_back (key_arg); + + // Push the data for the first argument into the m_arguments vector. + m_arguments.push_back (arg1); + m_arguments.push_back (arg2); + } + + virtual + ~CommandObjectSettingsRemove () {} + + virtual int + HandleArgumentCompletion (Args &input, + int &cursor_index, + int &cursor_char_position, + OptionElementVector &opt_element_vector, + int match_start_point, + int max_return_elements, + bool &word_complete, + StringList &matches) + { + std::string completion_str (input.GetArgumentAtIndex (cursor_index), cursor_char_position); + + // Attempting to complete variable name + if (cursor_index < 2) + CommandCompletions::InvokeCommonCompletionCallbacks (m_interpreter, + CommandCompletions::eSettingsNameCompletion, + completion_str.c_str(), + match_start_point, + max_return_elements, + NULL, + word_complete, + matches); + + return matches.GetSize(); + } + +protected: + virtual bool + DoExecute (const char *command, CommandReturnObject &result) + { + result.SetStatus (eReturnStatusSuccessFinishNoResult); + + Args cmd_args(command); + + // Process possible options. + if (!ParseOptions (cmd_args, result)) + return false; + + const size_t argc = cmd_args.GetArgumentCount (); + if (argc == 0) + { + result.AppendError ("'settings set' takes an array or dictionary item, or an array followed by one or more indexes, or a dictionary followed by one or more key names to remove"); + result.SetStatus (eReturnStatusFailed); + return false; + } + + const char *var_name = cmd_args.GetArgumentAtIndex (0); + if ((var_name == NULL) || (var_name[0] == '\0')) + { + result.AppendError ("'settings set' command requires a valid variable name"); + result.SetStatus (eReturnStatusFailed); + return false; + } + + // Split the raw command into var_name and value pair. + llvm::StringRef raw_str(command); + std::string var_value_string = raw_str.split(var_name).second.str(); + const char *var_value_cstr = Args::StripSpaces(var_value_string, true, true, false); + + Error error (m_interpreter.GetDebugger().SetPropertyValue (&m_exe_ctx, + eVarSetOperationRemove, + var_name, + var_value_cstr)); + if (error.Fail()) + { + result.AppendError (error.AsCString()); + result.SetStatus (eReturnStatusFailed); + return false; + } + + return result.Succeeded(); + } +}; + +//------------------------------------------------------------------------- +// CommandObjectSettingsReplace +//------------------------------------------------------------------------- + +class CommandObjectSettingsReplace : public CommandObjectRaw +{ +public: + CommandObjectSettingsReplace (CommandInterpreter &interpreter) : + CommandObjectRaw (interpreter, + "settings replace", + "Replace the specified element from an internal debugger settings array or dictionary variable with the specified new value.", + NULL) + { + CommandArgumentEntry arg1; + CommandArgumentEntry arg2; + CommandArgumentEntry arg3; + CommandArgumentData var_name_arg; + CommandArgumentData index_arg; + CommandArgumentData key_arg; + CommandArgumentData value_arg; + + // Define the first (and only) variant of this arg. + var_name_arg.arg_type = eArgTypeSettingVariableName; + var_name_arg.arg_repetition = eArgRepeatPlain; + + // There is only one variant this argument could be; put it into the argument entry. + arg1.push_back (var_name_arg); + + // Define the first (variant of this arg. + index_arg.arg_type = eArgTypeSettingIndex; + index_arg.arg_repetition = eArgRepeatPlain; + + // Define the second (variant of this arg. + key_arg.arg_type = eArgTypeSettingKey; + key_arg.arg_repetition = eArgRepeatPlain; + + // Put both variants into this arg + arg2.push_back (index_arg); + arg2.push_back (key_arg); + + // Define the first (and only) variant of this arg. + value_arg.arg_type = eArgTypeValue; + value_arg.arg_repetition = eArgRepeatPlain; + + // There is only one variant this argument could be; put it into the argument entry. + arg3.push_back (value_arg); + + // Push the data for the first argument into the m_arguments vector. + m_arguments.push_back (arg1); + m_arguments.push_back (arg2); + m_arguments.push_back (arg3); + } + + + virtual + ~CommandObjectSettingsReplace () {} + + // Overrides base class's behavior where WantsCompletion = !WantsRawCommandString. + virtual bool + WantsCompletion() { return true; } + + virtual int + HandleArgumentCompletion (Args &input, + int &cursor_index, + int &cursor_char_position, + OptionElementVector &opt_element_vector, + int match_start_point, + int max_return_elements, + bool &word_complete, + StringList &matches) + { + std::string completion_str (input.GetArgumentAtIndex (cursor_index), cursor_char_position); + + // Attempting to complete variable name + if (cursor_index < 2) + CommandCompletions::InvokeCommonCompletionCallbacks (m_interpreter, + CommandCompletions::eSettingsNameCompletion, + completion_str.c_str(), + match_start_point, + max_return_elements, + NULL, + word_complete, + matches); + + return matches.GetSize(); + } + +protected: + virtual bool + DoExecute (const char *command, CommandReturnObject &result) + { + result.SetStatus (eReturnStatusSuccessFinishNoResult); + + Args cmd_args(command); + const char *var_name = cmd_args.GetArgumentAtIndex (0); + if ((var_name == NULL) || (var_name[0] == '\0')) + { + result.AppendError ("'settings replace' command requires a valid variable name; No value supplied"); + result.SetStatus (eReturnStatusFailed); + return false; + } + + + // Split the raw command into var_name, index_value, and value triple. + llvm::StringRef raw_str(command); + std::string var_value_string = raw_str.split(var_name).second.str(); + const char *var_value_cstr = Args::StripSpaces(var_value_string, true, true, false); + + Error error(m_interpreter.GetDebugger().SetPropertyValue (&m_exe_ctx, + eVarSetOperationReplace, + var_name, + var_value_cstr)); + if (error.Fail()) + { + result.AppendError (error.AsCString()); + result.SetStatus (eReturnStatusFailed); + return false; + } + else + { + result.SetStatus (eReturnStatusSuccessFinishNoResult); + + } + + return result.Succeeded(); + } +}; + +//------------------------------------------------------------------------- +// CommandObjectSettingsInsertBefore +//------------------------------------------------------------------------- + +class CommandObjectSettingsInsertBefore : public CommandObjectRaw +{ +public: + CommandObjectSettingsInsertBefore (CommandInterpreter &interpreter) : + CommandObjectRaw (interpreter, + "settings insert-before", + "Insert value(s) into an internal debugger settings array variable, immediately before the specified element.", + NULL) + { + CommandArgumentEntry arg1; + CommandArgumentEntry arg2; + CommandArgumentEntry arg3; + CommandArgumentData var_name_arg; + CommandArgumentData index_arg; + CommandArgumentData value_arg; + + // Define the first (and only) variant of this arg. + var_name_arg.arg_type = eArgTypeSettingVariableName; + var_name_arg.arg_repetition = eArgRepeatPlain; + + // There is only one variant this argument could be; put it into the argument entry. + arg1.push_back (var_name_arg); + + // Define the first (variant of this arg. + index_arg.arg_type = eArgTypeSettingIndex; + index_arg.arg_repetition = eArgRepeatPlain; + + // There is only one variant this argument could be; put it into the argument entry. + arg2.push_back (index_arg); + + // Define the first (and only) variant of this arg. + value_arg.arg_type = eArgTypeValue; + value_arg.arg_repetition = eArgRepeatPlain; + + // There is only one variant this argument could be; put it into the argument entry. + arg3.push_back (value_arg); + + // Push the data for the first argument into the m_arguments vector. + m_arguments.push_back (arg1); + m_arguments.push_back (arg2); + m_arguments.push_back (arg3); + } + + virtual + ~CommandObjectSettingsInsertBefore () {} + + // Overrides base class's behavior where WantsCompletion = !WantsRawCommandString. + virtual bool + WantsCompletion() { return true; } + + virtual int + HandleArgumentCompletion (Args &input, + int &cursor_index, + int &cursor_char_position, + OptionElementVector &opt_element_vector, + int match_start_point, + int max_return_elements, + bool &word_complete, + StringList &matches) + { + std::string completion_str (input.GetArgumentAtIndex (cursor_index), cursor_char_position); + + // Attempting to complete variable name + if (cursor_index < 2) + CommandCompletions::InvokeCommonCompletionCallbacks (m_interpreter, + CommandCompletions::eSettingsNameCompletion, + completion_str.c_str(), + match_start_point, + max_return_elements, + NULL, + word_complete, + matches); + + return matches.GetSize(); + } + +protected: + virtual bool + DoExecute (const char *command, CommandReturnObject &result) + { + result.SetStatus (eReturnStatusSuccessFinishNoResult); + + Args cmd_args(command); + const size_t argc = cmd_args.GetArgumentCount (); + + if (argc < 3) + { + result.AppendError ("'settings insert-before' takes more arguments"); + result.SetStatus (eReturnStatusFailed); + return false; + } + + const char *var_name = cmd_args.GetArgumentAtIndex (0); + if ((var_name == NULL) || (var_name[0] == '\0')) + { + result.AppendError ("'settings insert-before' command requires a valid variable name; No value supplied"); + result.SetStatus (eReturnStatusFailed); + return false; + } + + // Split the raw command into var_name, index_value, and value triple. + llvm::StringRef raw_str(command); + std::string var_value_string = raw_str.split(var_name).second.str(); + const char *var_value_cstr = Args::StripSpaces(var_value_string, true, true, false); + + Error error(m_interpreter.GetDebugger().SetPropertyValue (&m_exe_ctx, + eVarSetOperationInsertBefore, + var_name, + var_value_cstr)); + if (error.Fail()) + { + result.AppendError (error.AsCString()); + result.SetStatus (eReturnStatusFailed); + return false; + } + + return result.Succeeded(); + } +}; + +//------------------------------------------------------------------------- +// CommandObjectSettingInsertAfter +//------------------------------------------------------------------------- + +class CommandObjectSettingsInsertAfter : public CommandObjectRaw +{ +public: + CommandObjectSettingsInsertAfter (CommandInterpreter &interpreter) : + CommandObjectRaw (interpreter, + "settings insert-after", + "Insert value(s) into an internal debugger settings array variable, immediately after the specified element.", + NULL) + { + CommandArgumentEntry arg1; + CommandArgumentEntry arg2; + CommandArgumentEntry arg3; + CommandArgumentData var_name_arg; + CommandArgumentData index_arg; + CommandArgumentData value_arg; + + // Define the first (and only) variant of this arg. + var_name_arg.arg_type = eArgTypeSettingVariableName; + var_name_arg.arg_repetition = eArgRepeatPlain; + + // There is only one variant this argument could be; put it into the argument entry. + arg1.push_back (var_name_arg); + + // Define the first (variant of this arg. + index_arg.arg_type = eArgTypeSettingIndex; + index_arg.arg_repetition = eArgRepeatPlain; + + // There is only one variant this argument could be; put it into the argument entry. + arg2.push_back (index_arg); + + // Define the first (and only) variant of this arg. + value_arg.arg_type = eArgTypeValue; + value_arg.arg_repetition = eArgRepeatPlain; + + // There is only one variant this argument could be; put it into the argument entry. + arg3.push_back (value_arg); + + // Push the data for the first argument into the m_arguments vector. + m_arguments.push_back (arg1); + m_arguments.push_back (arg2); + m_arguments.push_back (arg3); + } + + virtual + ~CommandObjectSettingsInsertAfter () {} + + // Overrides base class's behavior where WantsCompletion = !WantsRawCommandString. + virtual bool + WantsCompletion() { return true; } + + virtual int + HandleArgumentCompletion (Args &input, + int &cursor_index, + int &cursor_char_position, + OptionElementVector &opt_element_vector, + int match_start_point, + int max_return_elements, + bool &word_complete, + StringList &matches) + { + std::string completion_str (input.GetArgumentAtIndex (cursor_index), cursor_char_position); + + // Attempting to complete variable name + if (cursor_index < 2) + CommandCompletions::InvokeCommonCompletionCallbacks (m_interpreter, + CommandCompletions::eSettingsNameCompletion, + completion_str.c_str(), + match_start_point, + max_return_elements, + NULL, + word_complete, + matches); + + return matches.GetSize(); + } + +protected: + virtual bool + DoExecute (const char *command, CommandReturnObject &result) + { + result.SetStatus (eReturnStatusSuccessFinishNoResult); + + Args cmd_args(command); + const size_t argc = cmd_args.GetArgumentCount (); + + if (argc < 3) + { + result.AppendError ("'settings insert-after' takes more arguments"); + result.SetStatus (eReturnStatusFailed); + return false; + } + + const char *var_name = cmd_args.GetArgumentAtIndex (0); + if ((var_name == NULL) || (var_name[0] == '\0')) + { + result.AppendError ("'settings insert-after' command requires a valid variable name; No value supplied"); + result.SetStatus (eReturnStatusFailed); + return false; + } + + // Split the raw command into var_name, index_value, and value triple. + llvm::StringRef raw_str(command); + std::string var_value_string = raw_str.split(var_name).second.str(); + const char *var_value_cstr = Args::StripSpaces(var_value_string, true, true, false); + + Error error(m_interpreter.GetDebugger().SetPropertyValue (&m_exe_ctx, + eVarSetOperationInsertAfter, + var_name, + var_value_cstr)); + if (error.Fail()) + { + result.AppendError (error.AsCString()); + result.SetStatus (eReturnStatusFailed); + return false; + } + + return result.Succeeded(); + } +}; + +//------------------------------------------------------------------------- +// CommandObjectSettingsAppend +//------------------------------------------------------------------------- + +class CommandObjectSettingsAppend : public CommandObjectRaw +{ +public: + CommandObjectSettingsAppend (CommandInterpreter &interpreter) : + CommandObjectRaw (interpreter, + "settings append", + "Append a new value to the end of an internal debugger settings array, dictionary or string variable.", + NULL) + { + CommandArgumentEntry arg1; + CommandArgumentEntry arg2; + CommandArgumentData var_name_arg; + CommandArgumentData value_arg; + + // Define the first (and only) variant of this arg. + var_name_arg.arg_type = eArgTypeSettingVariableName; + var_name_arg.arg_repetition = eArgRepeatPlain; + + // There is only one variant this argument could be; put it into the argument entry. + arg1.push_back (var_name_arg); + + // Define the first (and only) variant of this arg. + value_arg.arg_type = eArgTypeValue; + value_arg.arg_repetition = eArgRepeatPlain; + + // There is only one variant this argument could be; put it into the argument entry. + arg2.push_back (value_arg); + + // Push the data for the first argument into the m_arguments vector. + m_arguments.push_back (arg1); + m_arguments.push_back (arg2); + } + + virtual + ~CommandObjectSettingsAppend () {} + + // Overrides base class's behavior where WantsCompletion = !WantsRawCommandString. + virtual bool + WantsCompletion() { return true; } + + virtual int + HandleArgumentCompletion (Args &input, + int &cursor_index, + int &cursor_char_position, + OptionElementVector &opt_element_vector, + int match_start_point, + int max_return_elements, + bool &word_complete, + StringList &matches) + { + std::string completion_str (input.GetArgumentAtIndex (cursor_index), cursor_char_position); + + // Attempting to complete variable name + if (cursor_index < 2) + CommandCompletions::InvokeCommonCompletionCallbacks (m_interpreter, + CommandCompletions::eSettingsNameCompletion, + completion_str.c_str(), + match_start_point, + max_return_elements, + NULL, + word_complete, + matches); + + return matches.GetSize(); + } + +protected: + virtual bool + DoExecute (const char *command, CommandReturnObject &result) + { + result.SetStatus (eReturnStatusSuccessFinishNoResult); + Args cmd_args(command); + const size_t argc = cmd_args.GetArgumentCount (); + + if (argc < 2) + { + result.AppendError ("'settings append' takes more arguments"); + result.SetStatus (eReturnStatusFailed); + return false; + } + + const char *var_name = cmd_args.GetArgumentAtIndex (0); + if ((var_name == NULL) || (var_name[0] == '\0')) + { + result.AppendError ("'settings append' command requires a valid variable name; No value supplied"); + result.SetStatus (eReturnStatusFailed); + return false; + } + + // Do not perform cmd_args.Shift() since StringRef is manipulating the + // raw character string later on. + + // Split the raw command into var_name and value pair. + llvm::StringRef raw_str(command); + std::string var_value_string = raw_str.split(var_name).second.str(); + const char *var_value_cstr = Args::StripSpaces(var_value_string, true, true, false); + + Error error(m_interpreter.GetDebugger().SetPropertyValue (&m_exe_ctx, + eVarSetOperationAppend, + var_name, + var_value_cstr)); + if (error.Fail()) + { + result.AppendError (error.AsCString()); + result.SetStatus (eReturnStatusFailed); + return false; + } + + return result.Succeeded(); + } +}; + +//------------------------------------------------------------------------- +// CommandObjectSettingsClear +//------------------------------------------------------------------------- + +class CommandObjectSettingsClear : public CommandObjectParsed +{ +public: + CommandObjectSettingsClear (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "settings clear", + "Erase all the contents of an internal debugger settings variables; this is only valid for variables with clearable types, i.e. strings, arrays or dictionaries.", + NULL) + { + CommandArgumentEntry arg; + CommandArgumentData var_name_arg; + + // Define the first (and only) variant of this arg. + var_name_arg.arg_type = eArgTypeSettingVariableName; + var_name_arg.arg_repetition = eArgRepeatPlain; + + // There is only one variant this argument could be; put it into the argument entry. + arg.push_back (var_name_arg); + + // Push the data for the first argument into the m_arguments vector. + m_arguments.push_back (arg); + } + + virtual + ~CommandObjectSettingsClear () {} + + virtual int + HandleArgumentCompletion (Args &input, + int &cursor_index, + int &cursor_char_position, + OptionElementVector &opt_element_vector, + int match_start_point, + int max_return_elements, + bool &word_complete, + StringList &matches) + { + std::string completion_str (input.GetArgumentAtIndex (cursor_index), cursor_char_position); + + // Attempting to complete variable name + if (cursor_index < 2) + CommandCompletions::InvokeCommonCompletionCallbacks (m_interpreter, + CommandCompletions::eSettingsNameCompletion, + completion_str.c_str(), + match_start_point, + max_return_elements, + NULL, + word_complete, + matches); + + return matches.GetSize(); + } + +protected: + virtual bool + DoExecute (Args& command, CommandReturnObject &result) + { + result.SetStatus (eReturnStatusSuccessFinishNoResult); + const size_t argc = command.GetArgumentCount (); + + if (argc != 1) + { + result.AppendError ("'setttings clear' takes exactly one argument"); + result.SetStatus (eReturnStatusFailed); + return false; + } + + const char *var_name = command.GetArgumentAtIndex (0); + if ((var_name == NULL) || (var_name[0] == '\0')) + { + result.AppendError ("'settings clear' command requires a valid variable name; No value supplied"); + result.SetStatus (eReturnStatusFailed); + return false; + } + + Error error (m_interpreter.GetDebugger().SetPropertyValue (&m_exe_ctx, + eVarSetOperationClear, + var_name, + NULL)); + if (error.Fail()) + { + result.AppendError (error.AsCString()); + result.SetStatus (eReturnStatusFailed); + return false; + } + + return result.Succeeded(); + } +}; + +//------------------------------------------------------------------------- +// CommandObjectMultiwordSettings +//------------------------------------------------------------------------- + +CommandObjectMultiwordSettings::CommandObjectMultiwordSettings (CommandInterpreter &interpreter) : + CommandObjectMultiword (interpreter, + "settings", + "A set of commands for manipulating internal settable debugger variables.", + "settings []") +{ + LoadSubCommand ("set", CommandObjectSP (new CommandObjectSettingsSet (interpreter))); + LoadSubCommand ("show", CommandObjectSP (new CommandObjectSettingsShow (interpreter))); + LoadSubCommand ("list", CommandObjectSP (new CommandObjectSettingsList (interpreter))); + LoadSubCommand ("remove", CommandObjectSP (new CommandObjectSettingsRemove (interpreter))); + LoadSubCommand ("replace", CommandObjectSP (new CommandObjectSettingsReplace (interpreter))); + LoadSubCommand ("insert-before", CommandObjectSP (new CommandObjectSettingsInsertBefore (interpreter))); + LoadSubCommand ("insert-after", CommandObjectSP (new CommandObjectSettingsInsertAfter (interpreter))); + LoadSubCommand ("append", CommandObjectSP (new CommandObjectSettingsAppend (interpreter))); + LoadSubCommand ("clear", CommandObjectSP (new CommandObjectSettingsClear (interpreter))); +} + +CommandObjectMultiwordSettings::~CommandObjectMultiwordSettings () +{ +} diff --git a/contrib/llvm/tools/lldb/source/Commands/CommandObjectSettings.h b/contrib/llvm/tools/lldb/source/Commands/CommandObjectSettings.h new file mode 100644 index 00000000000..eca7adeea76 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Commands/CommandObjectSettings.h @@ -0,0 +1,41 @@ +//===-- CommandObjectSettings.h ---------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_CommandObjectSettings_h_ +#define liblldb_CommandObjectSettings_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Interpreter/CommandObject.h" +#include "lldb/Interpreter/CommandObjectMultiword.h" +#include "lldb/Interpreter/Options.h" + + +namespace lldb_private { + +//------------------------------------------------------------------------- +// CommandObjectMultiwordSettings +//------------------------------------------------------------------------- + +class CommandObjectMultiwordSettings : public CommandObjectMultiword +{ +public: + + CommandObjectMultiwordSettings (CommandInterpreter &interpreter); + + virtual + ~CommandObjectMultiwordSettings (); + +}; + +} // namespace lldb_private + +#endif // liblldb_CommandObjectSettings_h_ diff --git a/contrib/llvm/tools/lldb/source/Commands/CommandObjectSource.cpp b/contrib/llvm/tools/lldb/source/Commands/CommandObjectSource.cpp new file mode 100644 index 00000000000..a08e39352b3 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Commands/CommandObjectSource.cpp @@ -0,0 +1,925 @@ +//===-- CommandObjectSource.cpp ---------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "CommandObjectSource.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Interpreter/Args.h" +#include "lldb/Core/Debugger.h" +#include "lldb/Core/FileLineResolver.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/ModuleSpec.h" +#include "lldb/Core/SourceManager.h" +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Interpreter/CommandReturnObject.h" +#include "lldb/Host/FileSpec.h" +#include "lldb/Symbol/CompileUnit.h" +#include "lldb/Symbol/Function.h" +#include "lldb/Symbol/Symbol.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/TargetList.h" +#include "lldb/Interpreter/CommandCompletions.h" +#include "lldb/Interpreter/Options.h" + +using namespace lldb; +using namespace lldb_private; + +//------------------------------------------------------------------------- +// CommandObjectSourceInfo +//------------------------------------------------------------------------- + +class CommandObjectSourceInfo : public CommandObjectParsed +{ + + class CommandOptions : public Options + { + public: + CommandOptions (CommandInterpreter &interpreter) : + Options(interpreter) + { + } + + ~CommandOptions () + { + } + + Error + SetOptionValue (uint32_t option_idx, const char *option_arg) + { + Error error; + const int short_option = g_option_table[option_idx].short_option; + switch (short_option) + { + case 'l': + start_line = Args::StringToUInt32 (option_arg, 0); + if (start_line == 0) + error.SetErrorStringWithFormat("invalid line number: '%s'", option_arg); + break; + + case 'f': + file_name = option_arg; + break; + + default: + error.SetErrorStringWithFormat("unrecognized short option '%c'", short_option); + break; + } + + return error; + } + + void + OptionParsingStarting () + { + file_spec.Clear(); + file_name.clear(); + start_line = 0; + } + + const OptionDefinition* + GetDefinitions () + { + return g_option_table; + } + static OptionDefinition g_option_table[]; + + // Instance variables to hold the values for command options. + FileSpec file_spec; + std::string file_name; + uint32_t start_line; + + }; + +public: + CommandObjectSourceInfo(CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "source info", + "Display information about the source lines from the current executable's debug info.", + "source info []"), + m_options (interpreter) + { + } + + ~CommandObjectSourceInfo () + { + } + + + Options * + GetOptions () + { + return &m_options; + } + +protected: + bool + DoExecute (Args& command, CommandReturnObject &result) + { + result.AppendError ("Not yet implemented"); + result.SetStatus (eReturnStatusFailed); + return false; + } + + CommandOptions m_options; +}; + +OptionDefinition +CommandObjectSourceInfo::CommandOptions::g_option_table[] = +{ +{ LLDB_OPT_SET_1, false, "line", 'l', required_argument, NULL, 0, eArgTypeLineNum, "The line number at which to start the display source."}, +{ LLDB_OPT_SET_1, false, "file", 'f', required_argument, NULL, CommandCompletions::eSourceFileCompletion, eArgTypeFilename, "The file from which to display source."}, +{ 0, false, NULL, 0, 0, NULL, 0, eArgTypeNone, NULL } +}; + +#pragma mark CommandObjectSourceList +//------------------------------------------------------------------------- +// CommandObjectSourceList +//------------------------------------------------------------------------- + +class CommandObjectSourceList : public CommandObjectParsed +{ + + class CommandOptions : public Options + { + public: + CommandOptions (CommandInterpreter &interpreter) : + Options(interpreter) + { + } + + ~CommandOptions () + { + } + + Error + SetOptionValue (uint32_t option_idx, const char *option_arg) + { + Error error; + const int short_option = g_option_table[option_idx].short_option; + switch (short_option) + { + case 'l': + start_line = Args::StringToUInt32 (option_arg, 0); + if (start_line == 0) + error.SetErrorStringWithFormat("invalid line number: '%s'", option_arg); + break; + + case 'c': + num_lines = Args::StringToUInt32 (option_arg, 0); + if (num_lines == 0) + error.SetErrorStringWithFormat("invalid line count: '%s'", option_arg); + break; + + case 'f': + file_name = option_arg; + break; + + case 'n': + symbol_name = option_arg; + break; + + case 'a': + { + ExecutionContext exe_ctx (m_interpreter.GetExecutionContext()); + address = Args::StringToAddress(&exe_ctx, option_arg, LLDB_INVALID_ADDRESS, &error); + } + break; + case 's': + modules.push_back (std::string (option_arg)); + break; + + case 'b': + show_bp_locs = true; + break; + case 'r': + reverse = true; + break; + default: + error.SetErrorStringWithFormat("unrecognized short option '%c'", short_option); + break; + } + + return error; + } + + void + OptionParsingStarting () + { + file_spec.Clear(); + file_name.clear(); + symbol_name.clear(); + address = LLDB_INVALID_ADDRESS; + start_line = 0; + num_lines = 0; + show_bp_locs = false; + reverse = false; + modules.clear(); + } + + const OptionDefinition* + GetDefinitions () + { + return g_option_table; + } + static OptionDefinition g_option_table[]; + + // Instance variables to hold the values for command options. + FileSpec file_spec; + std::string file_name; + std::string symbol_name; + lldb::addr_t address; + uint32_t start_line; + uint32_t num_lines; + STLStringArray modules; + bool show_bp_locs; + bool reverse; + }; + +public: + CommandObjectSourceList(CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "source list", + "Display source code (as specified) based on the current executable's debug info.", + NULL, + eFlagRequiresTarget), + m_options (interpreter) + { + } + + ~CommandObjectSourceList () + { + } + + + Options * + GetOptions () + { + return &m_options; + } + + virtual const char * + GetRepeatCommand (Args ¤t_command_args, uint32_t index) + { + // This is kind of gross, but the command hasn't been parsed yet so we can't look at the option + // values for this invocation... I have to scan the arguments directly. + size_t num_args = current_command_args.GetArgumentCount(); + bool is_reverse = false; + for (size_t i = 0 ; i < num_args; i++) + { + const char *arg = current_command_args.GetArgumentAtIndex(i); + if (arg && (strcmp(arg, "-r") == 0 || strcmp(arg, "--reverse") == 0)) + { + is_reverse = true; + } + } + if (is_reverse) + { + if (m_reverse_name.empty()) + { + m_reverse_name = m_cmd_name; + m_reverse_name.append (" -r"); + } + return m_reverse_name.c_str(); + } + else + return m_cmd_name.c_str(); + } + +protected: + + struct SourceInfo + { + ConstString function; + LineEntry line_entry; + + SourceInfo (const ConstString &name, const LineEntry &line_entry) : + function(name), + line_entry(line_entry) + { + } + + SourceInfo () : + function(), + line_entry() + { + } + + bool + IsValid () const + { + return (bool)function && line_entry.IsValid(); + } + + bool + operator == (const SourceInfo &rhs) const + { + return function == rhs.function && + line_entry.file == rhs.line_entry.file && + line_entry.line == rhs.line_entry.line; + } + + bool + operator != (const SourceInfo &rhs) const + { + return function != rhs.function || + line_entry.file != rhs.line_entry.file || + line_entry.line != rhs.line_entry.line; + } + + bool + operator < (const SourceInfo &rhs) const + { + if (function.GetCString() < rhs.function.GetCString()) + return true; + if (line_entry.file.GetDirectory().GetCString() < rhs.line_entry.file.GetDirectory().GetCString()) + return true; + if (line_entry.file.GetFilename().GetCString() < rhs.line_entry.file.GetFilename().GetCString()) + return true; + if (line_entry.line < rhs.line_entry.line) + return true; + return false; + } + }; + + size_t + DisplayFunctionSource (const SymbolContext &sc, + SourceInfo &source_info, + CommandReturnObject &result) + { + if (!source_info.IsValid()) + { + source_info.function = sc.GetFunctionName(); + source_info.line_entry = sc.GetFunctionStartLineEntry(); + } + + if (sc.function) + { + Target *target = m_exe_ctx.GetTargetPtr(); + + FileSpec start_file; + uint32_t start_line; + uint32_t end_line; + FileSpec end_file; + + if (sc.block == NULL) + { + // Not an inlined function + sc.function->GetStartLineSourceInfo (start_file, start_line); + if (start_line == 0) + { + result.AppendErrorWithFormat("Could not find line information for start of function: \"%s\".\n", source_info.function.GetCString()); + result.SetStatus (eReturnStatusFailed); + return 0; + } + sc.function->GetEndLineSourceInfo (end_file, end_line); + } + else + { + // We have an inlined function + start_file = source_info.line_entry.file; + start_line = source_info.line_entry.line; + end_line = start_line + m_options.num_lines; + } + + // This is a little hacky, but the first line table entry for a function points to the "{" that + // starts the function block. It would be nice to actually get the function + // declaration in there too. So back up a bit, but not further than what you're going to display. + uint32_t extra_lines; + if (m_options.num_lines >= 10) + extra_lines = 5; + else + extra_lines = m_options.num_lines/2; + uint32_t line_no; + if (start_line <= extra_lines) + line_no = 1; + else + line_no = start_line - extra_lines; + + // For fun, if the function is shorter than the number of lines we're supposed to display, + // only display the function... + if (end_line != 0) + { + if (m_options.num_lines > end_line - line_no) + m_options.num_lines = end_line - line_no + extra_lines; + } + + m_breakpoint_locations.Clear(); + + if (m_options.show_bp_locs) + { + const bool show_inlines = true; + m_breakpoint_locations.Reset (start_file, 0, show_inlines); + SearchFilter target_search_filter (m_exe_ctx.GetTargetSP()); + target_search_filter.Search (m_breakpoint_locations); + } + + result.AppendMessageWithFormat("File: %s\n", start_file.GetPath().c_str()); + return target->GetSourceManager().DisplaySourceLinesWithLineNumbers (start_file, + line_no, + 0, + m_options.num_lines, + "", + &result.GetOutputStream(), + GetBreakpointLocations ()); + } + else + { + result.AppendErrorWithFormat("Could not find function info for: \"%s\".\n", m_options.symbol_name.c_str()); + } + return 0; + } + + // From Jim: The FindMatchingFunctions / FindMatchingFunctionSymbols functions + // "take a possibly empty vector of strings which are names of modules, and + // run the two search functions on the subset of the full module list that + // matches the strings in the input vector". If we wanted to put these somewhere, + // there should probably be a module-filter-list that can be passed to the + // various ModuleList::Find* calls, which would either be a vector of string + // names or a ModuleSpecList. + size_t FindMatchingFunctions (Target *target, const ConstString &name, SymbolContextList& sc_list) + { + // Displaying the source for a symbol: + bool include_inlines = true; + bool append = true; + bool include_symbols = false; + size_t num_matches = 0; + + if (m_options.num_lines == 0) + m_options.num_lines = 10; + + const size_t num_modules = m_options.modules.size(); + if (num_modules > 0) + { + ModuleList matching_modules; + for (size_t i = 0; i < num_modules; ++i) + { + FileSpec module_file_spec(m_options.modules[i].c_str(), false); + if (module_file_spec) + { + ModuleSpec module_spec (module_file_spec); + matching_modules.Clear(); + target->GetImages().FindModules (module_spec, matching_modules); + num_matches += matching_modules.FindFunctions (name, eFunctionNameTypeAuto, include_symbols, include_inlines, append, sc_list); + } + } + } + else + { + num_matches = target->GetImages().FindFunctions (name, eFunctionNameTypeAuto, include_symbols, include_inlines, append, sc_list); + } + return num_matches; + } + + size_t FindMatchingFunctionSymbols (Target *target, const ConstString &name, SymbolContextList& sc_list) + { + size_t num_matches = 0; + const size_t num_modules = m_options.modules.size(); + if (num_modules > 0) + { + ModuleList matching_modules; + for (size_t i = 0; i < num_modules; ++i) + { + FileSpec module_file_spec(m_options.modules[i].c_str(), false); + if (module_file_spec) + { + ModuleSpec module_spec (module_file_spec); + matching_modules.Clear(); + target->GetImages().FindModules (module_spec, matching_modules); + num_matches += matching_modules.FindFunctionSymbols (name, eFunctionNameTypeAuto, sc_list); + } + } + } + else + { + num_matches = target->GetImages().FindFunctionSymbols (name, eFunctionNameTypeAuto, sc_list); + } + return num_matches; + } + + bool + DoExecute (Args& command, CommandReturnObject &result) + { + const size_t argc = command.GetArgumentCount(); + + if (argc != 0) + { + result.AppendErrorWithFormat("'%s' takes no arguments, only flags.\n", GetCommandName()); + result.SetStatus (eReturnStatusFailed); + return false; + } + + Target *target = m_exe_ctx.GetTargetPtr(); + + if (!m_options.symbol_name.empty()) + { + SymbolContextList sc_list; + ConstString name(m_options.symbol_name.c_str()); + + // Displaying the source for a symbol. Search for function named name. + size_t num_matches = FindMatchingFunctions (target, name, sc_list); + if (!num_matches) + { + // If we didn't find any functions with that name, try searching for symbols + // that line up exactly with function addresses. + SymbolContextList sc_list_symbols; + size_t num_symbol_matches = FindMatchingFunctionSymbols (target, name, sc_list_symbols); + for (size_t i = 0; i < num_symbol_matches; i++) + { + SymbolContext sc; + sc_list_symbols.GetContextAtIndex (i, sc); + if (sc.symbol) + { + const Address &base_address = sc.symbol->GetAddress(); + Function *function = base_address.CalculateSymbolContextFunction(); + if (function) + { + sc_list.Append (SymbolContext(function)); + num_matches++; + break; + } + } + } + } + + if (num_matches == 0) + { + result.AppendErrorWithFormat("Could not find function named: \"%s\".\n", m_options.symbol_name.c_str()); + result.SetStatus (eReturnStatusFailed); + return false; + } + + if (num_matches > 1) + { + std::set source_match_set; + + bool displayed_something = false; + for (size_t i = 0; i < num_matches; i++) + { + SymbolContext sc; + sc_list.GetContextAtIndex (i, sc); + SourceInfo source_info (sc.GetFunctionName(), + sc.GetFunctionStartLineEntry()); + + if (source_info.IsValid()) + { + if (source_match_set.find(source_info) == source_match_set.end()) + { + source_match_set.insert(source_info); + if (DisplayFunctionSource (sc, source_info, result)) + displayed_something = true; + } + } + } + + if (displayed_something) + result.SetStatus (eReturnStatusSuccessFinishResult); + else + result.SetStatus (eReturnStatusFailed); + } + else + { + SymbolContext sc; + sc_list.GetContextAtIndex (0, sc); + SourceInfo source_info; + + if (DisplayFunctionSource (sc, source_info, result)) + { + result.SetStatus (eReturnStatusSuccessFinishResult); + } + else + { + result.SetStatus (eReturnStatusFailed); + } + } + return result.Succeeded(); + } + else if (m_options.address != LLDB_INVALID_ADDRESS) + { + Address so_addr; + StreamString error_strm; + SymbolContextList sc_list; + + if (target->GetSectionLoadList().IsEmpty()) + { + // The target isn't loaded yet, we need to lookup the file address + // in all modules + const ModuleList &module_list = target->GetImages(); + const size_t num_modules = module_list.GetSize(); + for (size_t i=0; iResolveFileAddress(m_options.address, so_addr)) + { + SymbolContext sc; + sc.Clear(true); + if (module_sp->ResolveSymbolContextForAddress (so_addr, eSymbolContextEverything, sc) & eSymbolContextLineEntry) + sc_list.Append(sc); + } + } + + if (sc_list.GetSize() == 0) + { + result.AppendErrorWithFormat("no modules have source information for file address 0x%" PRIx64 ".\n", + m_options.address); + result.SetStatus (eReturnStatusFailed); + return false; + } + } + else + { + // The target has some things loaded, resolve this address to a + // compile unit + file + line and display + if (target->GetSectionLoadList().ResolveLoadAddress (m_options.address, so_addr)) + { + ModuleSP module_sp (so_addr.GetModule()); + if (module_sp) + { + SymbolContext sc; + sc.Clear(true); + if (module_sp->ResolveSymbolContextForAddress (so_addr, eSymbolContextEverything, sc) & eSymbolContextLineEntry) + { + sc_list.Append(sc); + } + else + { + so_addr.Dump(&error_strm, NULL, Address::DumpStyleModuleWithFileAddress); + result.AppendErrorWithFormat("address resolves to %s, but there is no line table information available for this address.\n", + error_strm.GetData()); + result.SetStatus (eReturnStatusFailed); + return false; + } + } + } + + if (sc_list.GetSize() == 0) + { + result.AppendErrorWithFormat("no modules contain load address 0x%" PRIx64 ".\n", m_options.address); + result.SetStatus (eReturnStatusFailed); + return false; + } + } + uint32_t num_matches = sc_list.GetSize(); + for (uint32_t i=0; ishared_from_this()); + target_search_filter.Search (m_breakpoint_locations); + } + + bool show_fullpaths = true; + bool show_module = true; + bool show_inlined_frames = true; + sc.DumpStopContext(&result.GetOutputStream(), + m_exe_ctx.GetBestExecutionContextScope(), + sc.line_entry.range.GetBaseAddress(), + show_fullpaths, + show_module, + show_inlined_frames); + result.GetOutputStream().EOL(); + + if (m_options.num_lines == 0) + m_options.num_lines = 10; + + size_t lines_to_back_up = m_options.num_lines >= 10 ? 5 : m_options.num_lines/2; + + target->GetSourceManager().DisplaySourceLinesWithLineNumbers (sc.comp_unit, + sc.line_entry.line, + lines_to_back_up, + m_options.num_lines - lines_to_back_up, + "->", + &result.GetOutputStream(), + GetBreakpointLocations ()); + result.SetStatus (eReturnStatusSuccessFinishResult); + } + } + } + else if (m_options.file_name.empty()) + { + // Last valid source manager context, or the current frame if no + // valid last context in source manager. + // One little trick here, if you type the exact same list command twice in a row, it is + // more likely because you typed it once, then typed it again + if (m_options.start_line == 0) + { + if (target->GetSourceManager().DisplayMoreWithLineNumbers (&result.GetOutputStream(), + m_options.num_lines, + m_options.reverse, + GetBreakpointLocations ())) + { + result.SetStatus (eReturnStatusSuccessFinishResult); + } + } + else + { + if (m_options.num_lines == 0) + m_options.num_lines = 10; + + if (m_options.show_bp_locs) + { + SourceManager::FileSP last_file_sp (target->GetSourceManager().GetLastFile ()); + if (last_file_sp) + { + const bool show_inlines = true; + m_breakpoint_locations.Reset (last_file_sp->GetFileSpec(), 0, show_inlines); + SearchFilter target_search_filter (target->shared_from_this()); + target_search_filter.Search (m_breakpoint_locations); + } + } + else + m_breakpoint_locations.Clear(); + + if (target->GetSourceManager().DisplaySourceLinesWithLineNumbersUsingLastFile( + m_options.start_line, // Line to display + m_options.num_lines, // Lines after line to + UINT32_MAX, // Don't mark "line" + "", // Don't mark "line" + &result.GetOutputStream(), + GetBreakpointLocations ())) + { + result.SetStatus (eReturnStatusSuccessFinishResult); + } + + } + } + else + { + const char *filename = m_options.file_name.c_str(); + + bool check_inlines = false; + SymbolContextList sc_list; + size_t num_matches = 0; + + if (m_options.modules.size() > 0) + { + ModuleList matching_modules; + for (size_t i = 0, e = m_options.modules.size(); i < e; ++i) + { + FileSpec module_file_spec(m_options.modules[i].c_str(), false); + if (module_file_spec) + { + ModuleSpec module_spec (module_file_spec); + matching_modules.Clear(); + target->GetImages().FindModules (module_spec, matching_modules); + num_matches += matching_modules.ResolveSymbolContextForFilePath (filename, + 0, + check_inlines, + eSymbolContextModule | eSymbolContextCompUnit, + sc_list); + } + } + } + else + { + num_matches = target->GetImages().ResolveSymbolContextForFilePath (filename, + 0, + check_inlines, + eSymbolContextModule | eSymbolContextCompUnit, + sc_list); + } + + if (num_matches == 0) + { + result.AppendErrorWithFormat("Could not find source file \"%s\".\n", + m_options.file_name.c_str()); + result.SetStatus (eReturnStatusFailed); + return false; + } + + if (num_matches > 1) + { + bool got_multiple = false; + FileSpec *test_cu_spec = NULL; + + for (unsigned i = 0; i < num_matches; i++) + { + SymbolContext sc; + sc_list.GetContextAtIndex(i, sc); + if (sc.comp_unit) + { + if (test_cu_spec) + { + if (test_cu_spec != static_cast (sc.comp_unit)) + got_multiple = true; + break; + } + else + test_cu_spec = sc.comp_unit; + } + } + if (got_multiple) + { + result.AppendErrorWithFormat("Multiple source files found matching: \"%s.\"\n", + m_options.file_name.c_str()); + result.SetStatus (eReturnStatusFailed); + return false; + } + } + + SymbolContext sc; + if (sc_list.GetContextAtIndex(0, sc)) + { + if (sc.comp_unit) + { + if (m_options.show_bp_locs) + { + const bool show_inlines = true; + m_breakpoint_locations.Reset (*sc.comp_unit, 0, show_inlines); + SearchFilter target_search_filter (target->shared_from_this()); + target_search_filter.Search (m_breakpoint_locations); + } + else + m_breakpoint_locations.Clear(); + + if (m_options.num_lines == 0) + m_options.num_lines = 10; + + target->GetSourceManager().DisplaySourceLinesWithLineNumbers (sc.comp_unit, + m_options.start_line, + 0, + m_options.num_lines, + "", + &result.GetOutputStream(), + GetBreakpointLocations ()); + + result.SetStatus (eReturnStatusSuccessFinishResult); + } + else + { + result.AppendErrorWithFormat("No comp unit found for: \"%s.\"\n", + m_options.file_name.c_str()); + result.SetStatus (eReturnStatusFailed); + return false; + } + } + } + return result.Succeeded(); + } + + const SymbolContextList * + GetBreakpointLocations () + { + if (m_breakpoint_locations.GetFileLineMatches().GetSize() > 0) + return &m_breakpoint_locations.GetFileLineMatches(); + return NULL; + } + CommandOptions m_options; + FileLineResolver m_breakpoint_locations; + std::string m_reverse_name; + +}; + +OptionDefinition +CommandObjectSourceList::CommandOptions::g_option_table[] = +{ +{ LLDB_OPT_SET_ALL, false, "count", 'c', required_argument, NULL, 0, eArgTypeCount, "The number of source lines to display."}, +{ LLDB_OPT_SET_1 | + LLDB_OPT_SET_2 , false, "shlib", 's', required_argument, NULL, CommandCompletions::eModuleCompletion, eArgTypeShlibName, "Look up the source file in the given shared library."}, +{ LLDB_OPT_SET_ALL, false, "show-breakpoints", 'b', no_argument, NULL, 0, eArgTypeNone, "Show the line table locations from the debug information that indicate valid places to set source level breakpoints."}, +{ LLDB_OPT_SET_1 , false, "file", 'f', required_argument, NULL, CommandCompletions::eSourceFileCompletion, eArgTypeFilename, "The file from which to display source."}, +{ LLDB_OPT_SET_1 , false, "line", 'l', required_argument, NULL, 0, eArgTypeLineNum, "The line number at which to start the display source."}, +{ LLDB_OPT_SET_2 , false, "name", 'n', required_argument, NULL, CommandCompletions::eSymbolCompletion, eArgTypeSymbol, "The name of a function whose source to display."}, +{ LLDB_OPT_SET_3 , false, "address",'a', required_argument, NULL, 0, eArgTypeAddressOrExpression, "Lookup the address and display the source information for the corresponding file and line."}, +{ LLDB_OPT_SET_4, false, "reverse", 'r', no_argument, NULL, 0, eArgTypeNone, "Reverse the listing to look backwards from the last displayed block of source."}, +{ 0, false, NULL, 0, 0, NULL, 0, eArgTypeNone, NULL } +}; + +#pragma mark CommandObjectMultiwordSource + +//------------------------------------------------------------------------- +// CommandObjectMultiwordSource +//------------------------------------------------------------------------- + +CommandObjectMultiwordSource::CommandObjectMultiwordSource (CommandInterpreter &interpreter) : + CommandObjectMultiword (interpreter, + "source", + "A set of commands for accessing source file information", + "source []") +{ + // "source info" isn't implemented yet... + //LoadSubCommand ("info", CommandObjectSP (new CommandObjectSourceInfo (interpreter))); + LoadSubCommand ("list", CommandObjectSP (new CommandObjectSourceList (interpreter))); +} + +CommandObjectMultiwordSource::~CommandObjectMultiwordSource () +{ +} + diff --git a/contrib/llvm/tools/lldb/source/Commands/CommandObjectSource.h b/contrib/llvm/tools/lldb/source/Commands/CommandObjectSource.h new file mode 100644 index 00000000000..0daef138586 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Commands/CommandObjectSource.h @@ -0,0 +1,40 @@ +//===-- CommandObjectSource.h.h -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_CommandObjectSource_h_ +#define liblldb_CommandObjectSource_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Interpreter/CommandObject.h" +#include "lldb/Interpreter/CommandObjectMultiword.h" +#include "lldb/Core/STLUtils.h" + +namespace lldb_private { + +//------------------------------------------------------------------------- +// CommandObjectMultiwordSource +//------------------------------------------------------------------------- + +class CommandObjectMultiwordSource : public CommandObjectMultiword +{ +public: + + CommandObjectMultiwordSource (CommandInterpreter &interpreter); + + virtual + ~CommandObjectMultiwordSource (); + +}; + +} // namespace lldb_private + +#endif // liblldb_CommandObjectSource.h_h_ diff --git a/contrib/llvm/tools/lldb/source/Commands/CommandObjectSyntax.cpp b/contrib/llvm/tools/lldb/source/Commands/CommandObjectSyntax.cpp new file mode 100644 index 00000000000..d2021ea3eb1 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Commands/CommandObjectSyntax.cpp @@ -0,0 +1,113 @@ +//===-- CommandObjectSyntax.cpp ---------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "CommandObjectSyntax.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Interpreter/Args.h" +#include "lldb/Interpreter/Options.h" + +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Interpreter/CommandReturnObject.h" +#include "lldb/Interpreter/CommandObjectMultiword.h" + +using namespace lldb; +using namespace lldb_private; + +//------------------------------------------------------------------------- +// CommandObjectSyntax +//------------------------------------------------------------------------- + +CommandObjectSyntax::CommandObjectSyntax (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "syntax", + "Shows the correct syntax for a given debugger command.", + "syntax ") +{ + CommandArgumentEntry arg; + CommandArgumentData command_arg; + + // Define the first (and only) variant of this arg. + command_arg.arg_type = eArgTypeCommandName; + command_arg.arg_repetition = eArgRepeatPlain; + + // There is only one variant this argument could be; put it into the argument entry. + arg.push_back (command_arg); + + // Push the data for the first argument into the m_arguments vector. + m_arguments.push_back (arg); +} + +CommandObjectSyntax::~CommandObjectSyntax() +{ +} + + +bool +CommandObjectSyntax::DoExecute (Args& command, CommandReturnObject &result) +{ + CommandObject::CommandMap::iterator pos; + CommandObject *cmd_obj; + const size_t argc = command.GetArgumentCount(); + + if (argc > 0) + { + cmd_obj = m_interpreter.GetCommandObject (command.GetArgumentAtIndex(0)); + bool all_okay = true; + for (size_t i = 1; i < argc; ++i) + { + std::string sub_command = command.GetArgumentAtIndex (i); + if (!cmd_obj->IsMultiwordObject()) + all_okay = false; + else + { + cmd_obj = cmd_obj->GetSubcommandObject(sub_command.c_str()); + if (!cmd_obj) + all_okay = false; + } + } + + if (all_okay && (cmd_obj != NULL)) + { + Stream &output_strm = result.GetOutputStream(); + if (cmd_obj->GetOptions() != NULL) + { + output_strm.Printf ("\nSyntax: %s\n", cmd_obj->GetSyntax()); + output_strm.Printf ("(Try 'help %s' for more information on command options syntax.)\n", + cmd_obj->GetCommandName()); + result.SetStatus (eReturnStatusSuccessFinishNoResult); + } + else + { + output_strm.Printf ("\nSyntax: %s\n", cmd_obj->GetSyntax()); + result.SetStatus (eReturnStatusSuccessFinishNoResult); + } + } + else + { + std::string cmd_string; + command.GetCommandString (cmd_string); + result.AppendErrorWithFormat ("'%s' is not a known command.\n", cmd_string.c_str()); + result.AppendError ("Try 'help' to see a current list of commands."); + result.SetStatus (eReturnStatusFailed); + } + } + else + { + result.AppendError ("Must call 'syntax' with a valid command."); + result.SetStatus (eReturnStatusFailed); + } + + return result.Succeeded(); +} diff --git a/contrib/llvm/tools/lldb/source/Commands/CommandObjectSyntax.h b/contrib/llvm/tools/lldb/source/Commands/CommandObjectSyntax.h new file mode 100644 index 00000000000..47bf85f8549 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Commands/CommandObjectSyntax.h @@ -0,0 +1,44 @@ +//===-- CommandObjectSyntax.h -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_CommandObjectSyntax_h_ +#define liblldb_CommandObjectSyntax_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Interpreter/CommandObject.h" + +namespace lldb_private { + +//------------------------------------------------------------------------- +// CommandObjectSyntax +//------------------------------------------------------------------------- + +class CommandObjectSyntax : public CommandObjectParsed +{ +public: + + CommandObjectSyntax (CommandInterpreter &interpreter); + + virtual + ~CommandObjectSyntax (); + +protected: + virtual bool + DoExecute (Args& command, + CommandReturnObject &result); + + +}; + +} // namespace lldb_private + +#endif // liblldb_CommandObjectSyntax_h_ diff --git a/contrib/llvm/tools/lldb/source/Commands/CommandObjectTarget.cpp b/contrib/llvm/tools/lldb/source/Commands/CommandObjectTarget.cpp new file mode 100644 index 00000000000..dd0e2a0011b --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Commands/CommandObjectTarget.cpp @@ -0,0 +1,5354 @@ +//===-- CommandObjectTarget.cpp ---------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "CommandObjectTarget.h" + +// C Includes +#include + +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Interpreter/Args.h" +#include "lldb/Core/Debugger.h" +#include "lldb/Core/InputReader.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/ModuleSpec.h" +#include "lldb/Core/Section.h" +#include "lldb/Core/State.h" +#include "lldb/Core/Timer.h" +#include "lldb/Core/ValueObjectVariable.h" +#include "lldb/Host/Symbols.h" +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Interpreter/CommandReturnObject.h" +#include "lldb/Interpreter/Options.h" +#include "lldb/Interpreter/OptionGroupArchitecture.h" +#include "lldb/Interpreter/OptionGroupBoolean.h" +#include "lldb/Interpreter/OptionGroupFile.h" +#include "lldb/Interpreter/OptionGroupFormat.h" +#include "lldb/Interpreter/OptionGroupVariable.h" +#include "lldb/Interpreter/OptionGroupPlatform.h" +#include "lldb/Interpreter/OptionGroupUInt64.h" +#include "lldb/Interpreter/OptionGroupUUID.h" +#include "lldb/Interpreter/OptionGroupValueObjectDisplay.h" +#include "lldb/Symbol/CompileUnit.h" +#include "lldb/Symbol/FuncUnwinders.h" +#include "lldb/Symbol/LineTable.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Symbol/SymbolFile.h" +#include "lldb/Symbol/SymbolVendor.h" +#include "lldb/Symbol/UnwindPlan.h" +#include "lldb/Symbol/VariableList.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/StackFrame.h" +#include "lldb/Target/Thread.h" +#include "lldb/Target/ThreadSpec.h" + +using namespace lldb; +using namespace lldb_private; + + + +static void +DumpTargetInfo (uint32_t target_idx, Target *target, const char *prefix_cstr, bool show_stopped_process_status, Stream &strm) +{ + const ArchSpec &target_arch = target->GetArchitecture(); + + Module *exe_module = target->GetExecutableModulePointer(); + char exe_path[PATH_MAX]; + bool exe_valid = false; + if (exe_module) + exe_valid = exe_module->GetFileSpec().GetPath (exe_path, sizeof(exe_path)); + + if (!exe_valid) + ::strcpy (exe_path, ""); + + strm.Printf ("%starget #%u: %s", prefix_cstr ? prefix_cstr : "", target_idx, exe_path); + + uint32_t properties = 0; + if (target_arch.IsValid()) + { + strm.Printf ("%sarch=%s", properties++ > 0 ? ", " : " ( ", target_arch.GetTriple().str().c_str()); + properties++; + } + PlatformSP platform_sp (target->GetPlatform()); + if (platform_sp) + strm.Printf ("%splatform=%s", properties++ > 0 ? ", " : " ( ", platform_sp->GetName().GetCString()); + + ProcessSP process_sp (target->GetProcessSP()); + bool show_process_status = false; + if (process_sp) + { + lldb::pid_t pid = process_sp->GetID(); + StateType state = process_sp->GetState(); + if (show_stopped_process_status) + show_process_status = StateIsStoppedState(state, true); + const char *state_cstr = StateAsCString (state); + if (pid != LLDB_INVALID_PROCESS_ID) + strm.Printf ("%spid=%" PRIu64, properties++ > 0 ? ", " : " ( ", pid); + strm.Printf ("%sstate=%s", properties++ > 0 ? ", " : " ( ", state_cstr); + } + if (properties > 0) + strm.PutCString (" )\n"); + else + strm.EOL(); + if (show_process_status) + { + const bool only_threads_with_stop_reason = true; + const uint32_t start_frame = 0; + const uint32_t num_frames = 1; + const uint32_t num_frames_with_source = 1; + process_sp->GetStatus (strm); + process_sp->GetThreadStatus (strm, + only_threads_with_stop_reason, + start_frame, + num_frames, + num_frames_with_source); + + } +} + +static uint32_t +DumpTargetList (TargetList &target_list, bool show_stopped_process_status, Stream &strm) +{ + const uint32_t num_targets = target_list.GetNumTargets(); + if (num_targets) + { + TargetSP selected_target_sp (target_list.GetSelectedTarget()); + strm.PutCString ("Current targets:\n"); + for (uint32_t i=0; iGetExecutableModule()); + if (module_sp) + { + if (symfile) + module_sp->SetSymbolFileFileSpec(symfile); + if (remote_file) + { + std::string remote_path = remote_file.GetPath(); + target_sp->SetArg0(remote_path.c_str()); + module_sp->SetPlatformFileSpec(remote_file); + } + } + } + + debugger.GetTargetList().SetSelectedTarget(target_sp.get()); + if (core_file) + { + char core_path[PATH_MAX]; + core_file.GetPath(core_path, sizeof(core_path)); + if (core_file.Exists()) + { + FileSpec core_file_dir; + core_file_dir.GetDirectory() = core_file.GetDirectory(); + target_sp->GetExecutableSearchPaths ().Append (core_file_dir); + + ProcessSP process_sp (target_sp->CreateProcess (m_interpreter.GetDebugger().GetListener(), NULL, &core_file)); + + if (process_sp) + { + // Seems wierd that we Launch a core file, but that is + // what we do! + error = process_sp->LoadCore(); + + if (error.Fail()) + { + result.AppendError(error.AsCString("can't find plug-in for core file")); + result.SetStatus (eReturnStatusFailed); + return false; + } + else + { + result.AppendMessageWithFormat ("Core file '%s' (%s) was loaded.\n", core_path, target_sp->GetArchitecture().GetArchitectureName()); + result.SetStatus (eReturnStatusSuccessFinishNoResult); + } + } + else + { + result.AppendErrorWithFormat ("Unable to find process plug-in for core file '%s'\n", core_path); + result.SetStatus (eReturnStatusFailed); + } + } + else + { + result.AppendErrorWithFormat ("Core file '%s' does not exist\n", core_path); + result.SetStatus (eReturnStatusFailed); + } + } + else + { + result.AppendMessageWithFormat ("Current executable set to '%s' (%s).\n", file_path, target_sp->GetArchitecture().GetArchitectureName()); + result.SetStatus (eReturnStatusSuccessFinishNoResult); + } + } + else + { + result.AppendError(error.AsCString()); + result.SetStatus (eReturnStatusFailed); + } + } + else + { + result.AppendErrorWithFormat("'%s' takes exactly one executable path argument, or use the --core-file option.\n", m_cmd_name.c_str()); + result.SetStatus (eReturnStatusFailed); + } + return result.Succeeded(); + + } + +private: + OptionGroupOptions m_option_group; + OptionGroupArchitecture m_arch_option; + OptionGroupPlatform m_platform_options; + OptionGroupFile m_core_file; + OptionGroupFile m_symbol_file; + OptionGroupFile m_remote_file; + OptionGroupBoolean m_add_dependents; +}; + +#pragma mark CommandObjectTargetList + +//---------------------------------------------------------------------- +// "target list" +//---------------------------------------------------------------------- + +class CommandObjectTargetList : public CommandObjectParsed +{ +public: + CommandObjectTargetList (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "target list", + "List all current targets in the current debug session.", + NULL, + 0) + { + } + + virtual + ~CommandObjectTargetList () + { + } + +protected: + virtual bool + DoExecute (Args& args, CommandReturnObject &result) + { + if (args.GetArgumentCount() == 0) + { + Stream &strm = result.GetOutputStream(); + + bool show_stopped_process_status = false; + if (DumpTargetList (m_interpreter.GetDebugger().GetTargetList(), show_stopped_process_status, strm) == 0) + { + strm.PutCString ("No targets.\n"); + } + result.SetStatus (eReturnStatusSuccessFinishResult); + } + else + { + result.AppendError ("the 'target list' command takes no arguments\n"); + result.SetStatus (eReturnStatusFailed); + } + return result.Succeeded(); + } +}; + + +#pragma mark CommandObjectTargetSelect + +//---------------------------------------------------------------------- +// "target select" +//---------------------------------------------------------------------- + +class CommandObjectTargetSelect : public CommandObjectParsed +{ +public: + CommandObjectTargetSelect (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "target select", + "Select a target as the current target by target index.", + NULL, + 0) + { + } + + virtual + ~CommandObjectTargetSelect () + { + } + +protected: + virtual bool + DoExecute (Args& args, CommandReturnObject &result) + { + if (args.GetArgumentCount() == 1) + { + bool success = false; + const char *target_idx_arg = args.GetArgumentAtIndex(0); + uint32_t target_idx = Args::StringToUInt32 (target_idx_arg, UINT32_MAX, 0, &success); + if (success) + { + TargetList &target_list = m_interpreter.GetDebugger().GetTargetList(); + const uint32_t num_targets = target_list.GetNumTargets(); + if (target_idx < num_targets) + { + TargetSP target_sp (target_list.GetTargetAtIndex (target_idx)); + if (target_sp) + { + Stream &strm = result.GetOutputStream(); + target_list.SetSelectedTarget (target_sp.get()); + bool show_stopped_process_status = false; + DumpTargetList (target_list, show_stopped_process_status, strm); + result.SetStatus (eReturnStatusSuccessFinishResult); + } + else + { + result.AppendErrorWithFormat ("target #%u is NULL in target list\n", target_idx); + result.SetStatus (eReturnStatusFailed); + } + } + else + { + result.AppendErrorWithFormat ("index %u is out of range, valid target indexes are 0 - %u\n", + target_idx, + num_targets - 1); + result.SetStatus (eReturnStatusFailed); + } + } + else + { + result.AppendErrorWithFormat("invalid index string value '%s'\n", target_idx_arg); + result.SetStatus (eReturnStatusFailed); + } + } + else + { + result.AppendError ("'target select' takes a single argument: a target index\n"); + result.SetStatus (eReturnStatusFailed); + } + return result.Succeeded(); + } +}; + +#pragma mark CommandObjectTargetSelect + +//---------------------------------------------------------------------- +// "target delete" +//---------------------------------------------------------------------- + +class CommandObjectTargetDelete : public CommandObjectParsed +{ +public: + CommandObjectTargetDelete (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "target delete", + "Delete one or more targets by target index.", + NULL, + 0), + m_option_group (interpreter), + m_cleanup_option (LLDB_OPT_SET_1, false, "clean", 'c', "Perform extra cleanup to minimize memory consumption after deleting the target.", false, false) + { + m_option_group.Append (&m_cleanup_option, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1); + m_option_group.Finalize(); + } + + virtual + ~CommandObjectTargetDelete () + { + } + + Options * + GetOptions () + { + return &m_option_group; + } + +protected: + virtual bool + DoExecute (Args& args, CommandReturnObject &result) + { + const size_t argc = args.GetArgumentCount(); + std::vector delete_target_list; + TargetList &target_list = m_interpreter.GetDebugger().GetTargetList(); + bool success = true; + TargetSP target_sp; + if (argc > 0) + { + const uint32_t num_targets = target_list.GetNumTargets(); + // Bail out if don't have any targets. + if (num_targets == 0) { + result.AppendError("no targets to delete"); + result.SetStatus(eReturnStatusFailed); + success = false; + } + + for (uint32_t arg_idx = 0; success && arg_idx < argc; ++arg_idx) + { + const char *target_idx_arg = args.GetArgumentAtIndex(arg_idx); + uint32_t target_idx = Args::StringToUInt32 (target_idx_arg, UINT32_MAX, 0, &success); + if (success) + { + if (target_idx < num_targets) + { + target_sp = target_list.GetTargetAtIndex (target_idx); + if (target_sp) + { + delete_target_list.push_back (target_sp); + continue; + } + } + if (num_targets > 1) + result.AppendErrorWithFormat ("target index %u is out of range, valid target indexes are 0 - %u\n", + target_idx, + num_targets - 1); + else + result.AppendErrorWithFormat("target index %u is out of range, the only valid index is 0\n", + target_idx); + + result.SetStatus (eReturnStatusFailed); + success = false; + } + else + { + result.AppendErrorWithFormat("invalid target index '%s'\n", target_idx_arg); + result.SetStatus (eReturnStatusFailed); + success = false; + } + } + + } + else + { + target_sp = target_list.GetSelectedTarget(); + if (target_sp) + { + delete_target_list.push_back (target_sp); + } + else + { + result.AppendErrorWithFormat("no target is currently selected\n"); + result.SetStatus (eReturnStatusFailed); + success = false; + } + } + if (success) + { + const size_t num_targets_to_delete = delete_target_list.size(); + for (size_t idx = 0; idx < num_targets_to_delete; ++idx) + { + target_sp = delete_target_list[idx]; + target_list.DeleteTarget(target_sp); + target_sp->Destroy(); + } + // If "--clean" was specified, prune any orphaned shared modules from + // the global shared module list + if (m_cleanup_option.GetOptionValue ()) + { + const bool mandatory = true; + ModuleList::RemoveOrphanSharedModules(mandatory); + } + result.GetOutputStream().Printf("%u targets deleted.\n", (uint32_t)num_targets_to_delete); + result.SetStatus(eReturnStatusSuccessFinishResult); + } + + return result.Succeeded(); + } + + OptionGroupOptions m_option_group; + OptionGroupBoolean m_cleanup_option; +}; + + +#pragma mark CommandObjectTargetVariable + +//---------------------------------------------------------------------- +// "target variable" +//---------------------------------------------------------------------- + +class CommandObjectTargetVariable : public CommandObjectParsed +{ +public: + CommandObjectTargetVariable (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "target variable", + "Read global variable(s) prior to, or while running your binary.", + NULL, + eFlagRequiresTarget), + m_option_group (interpreter), + m_option_variable (false), // Don't include frame options + m_option_format (eFormatDefault), + m_option_compile_units (LLDB_OPT_SET_1, false, "file", 'file', 0, eArgTypeFilename, "A basename or fullpath to a file that contains global variables. This option can be specified multiple times."), + m_option_shared_libraries (LLDB_OPT_SET_1, false, "shlib",'shlb', 0, eArgTypeFilename, "A basename or fullpath to a shared library to use in the search for global variables. This option can be specified multiple times."), + m_varobj_options() + { + CommandArgumentEntry arg; + CommandArgumentData var_name_arg; + + // Define the first (and only) variant of this arg. + var_name_arg.arg_type = eArgTypeVarName; + var_name_arg.arg_repetition = eArgRepeatPlus; + + // There is only one variant this argument could be; put it into the argument entry. + arg.push_back (var_name_arg); + + // Push the data for the first argument into the m_arguments vector. + m_arguments.push_back (arg); + + m_option_group.Append (&m_varobj_options, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1); + m_option_group.Append (&m_option_variable, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1); + m_option_group.Append (&m_option_format, OptionGroupFormat::OPTION_GROUP_FORMAT | OptionGroupFormat::OPTION_GROUP_GDB_FMT, LLDB_OPT_SET_1); + m_option_group.Append (&m_option_compile_units, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1); + m_option_group.Append (&m_option_shared_libraries, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1); + m_option_group.Finalize(); + } + + virtual + ~CommandObjectTargetVariable () + { + } + + void + DumpValueObject (Stream &s, VariableSP &var_sp, ValueObjectSP &valobj_sp, const char *root_name) + { + ValueObject::DumpValueObjectOptions options(m_varobj_options.GetAsDumpOptions()); + + switch (var_sp->GetScope()) + { + case eValueTypeVariableGlobal: + if (m_option_variable.show_scope) + s.PutCString("GLOBAL: "); + break; + + case eValueTypeVariableStatic: + if (m_option_variable.show_scope) + s.PutCString("STATIC: "); + break; + + case eValueTypeVariableArgument: + if (m_option_variable.show_scope) + s.PutCString(" ARG: "); + break; + + case eValueTypeVariableLocal: + if (m_option_variable.show_scope) + s.PutCString(" LOCAL: "); + break; + + default: + break; + } + + if (m_option_variable.show_decl) + { + bool show_fullpaths = false; + bool show_module = true; + if (var_sp->DumpDeclaration(&s, show_fullpaths, show_module)) + s.PutCString (": "); + } + + const Format format = m_option_format.GetFormat(); + if (format != eFormatDefault) + options.SetFormat(format); + + options.SetRootValueObjectName(root_name); + + ValueObject::DumpValueObject (s, + valobj_sp.get(), + options); + + } + + + static size_t GetVariableCallback (void *baton, + const char *name, + VariableList &variable_list) + { + Target *target = static_cast(baton); + if (target) + { + return target->GetImages().FindGlobalVariables (ConstString(name), + true, + UINT32_MAX, + variable_list); + } + return 0; + } + + + + Options * + GetOptions () + { + return &m_option_group; + } + +protected: + + void + DumpGlobalVariableList(const ExecutionContext &exe_ctx, const SymbolContext &sc, const VariableList &variable_list, Stream &s) + { + size_t count = variable_list.GetSize(); + if (count > 0) + { + if (sc.module_sp) + { + if (sc.comp_unit) + { + s.Printf ("Global variables for %s in %s:\n", + sc.comp_unit->GetPath().c_str(), + sc.module_sp->GetFileSpec().GetPath().c_str()); + } + else + { + s.Printf ("Global variables for %s\n", + sc.module_sp->GetFileSpec().GetPath().c_str()); + } + } + else if (sc.comp_unit) + { + s.Printf ("Global variables for %s\n", + sc.comp_unit->GetPath().c_str()); + } + + for (uint32_t i=0; iGetName().GetCString()); + } + } + } + + } + virtual bool + DoExecute (Args& args, CommandReturnObject &result) + { + Target *target = m_exe_ctx.GetTargetPtr(); + const size_t argc = args.GetArgumentCount(); + Stream &s = result.GetOutputStream(); + + if (argc > 0) + { + + for (size_t idx = 0; idx < argc; ++idx) + { + VariableList variable_list; + ValueObjectList valobj_list; + + const char *arg = args.GetArgumentAtIndex(idx); + size_t matches = 0; + bool use_var_name = false; + if (m_option_variable.use_regex) + { + RegularExpression regex(arg); + if (!regex.IsValid ()) + { + result.GetErrorStream().Printf ("error: invalid regular expression: '%s'\n", arg); + result.SetStatus (eReturnStatusFailed); + return false; + } + use_var_name = true; + matches = target->GetImages().FindGlobalVariables (regex, + true, + UINT32_MAX, + variable_list); + } + else + { + Error error (Variable::GetValuesForVariableExpressionPath (arg, + m_exe_ctx.GetBestExecutionContextScope(), + GetVariableCallback, + target, + variable_list, + valobj_list)); + matches = variable_list.GetSize(); + } + + if (matches == 0) + { + result.GetErrorStream().Printf ("error: can't find global variable '%s'\n", arg); + result.SetStatus (eReturnStatusFailed); + return false; + } + else + { + for (uint32_t global_idx=0; global_idxGetName().GetCString() : arg); + } + } + } + } + } + else + { + const FileSpecList &compile_units = m_option_compile_units.GetOptionValue().GetCurrentValue(); + const FileSpecList &shlibs = m_option_shared_libraries.GetOptionValue().GetCurrentValue(); + SymbolContextList sc_list; + const size_t num_compile_units = compile_units.GetSize(); + const size_t num_shlibs = shlibs.GetSize(); + if (num_compile_units == 0 && num_shlibs == 0) + { + bool success = false; + StackFrame *frame = m_exe_ctx.GetFramePtr(); + CompileUnit *comp_unit = NULL; + if (frame) + { + SymbolContext sc = frame->GetSymbolContext (eSymbolContextCompUnit); + if (sc.comp_unit) + { + const bool can_create = true; + VariableListSP comp_unit_varlist_sp (sc.comp_unit->GetVariableList(can_create)); + if (comp_unit_varlist_sp) + { + size_t count = comp_unit_varlist_sp->GetSize(); + if (count > 0) + { + DumpGlobalVariableList(m_exe_ctx, sc, *comp_unit_varlist_sp, s); + success = true; + } + } + } + } + if (!success) + { + if (frame) + { + if (comp_unit) + result.AppendErrorWithFormat ("no global variables in current compile unit: %s\n", + comp_unit->GetPath().c_str()); + else + result.AppendErrorWithFormat ("no debug information for frame %u\n", frame->GetFrameIndex()); + } + else + result.AppendError ("'target variable' takes one or more global variable names as arguments\n"); + result.SetStatus (eReturnStatusFailed); + } + } + else + { + SymbolContextList sc_list; + const bool append = true; + // We have one or more compile unit or shlib + if (num_shlibs > 0) + { + for (size_t shlib_idx=0; shlib_idxGetImages().FindFirstModule(module_spec)); + if (module_sp) + { + if (num_compile_units > 0) + { + for (size_t cu_idx=0; cu_idxFindCompileUnits(compile_units.GetFileSpecAtIndex(cu_idx), append, sc_list); + } + else + { + SymbolContext sc; + sc.module_sp = module_sp; + sc_list.Append(sc); + } + } + else + { + // Didn't find matching shlib/module in target... + result.AppendErrorWithFormat ("target doesn't contain the specified shared library: %s\n", + module_file.GetPath().c_str()); + } + } + } + else + { + // No shared libraries, we just want to find globals for the compile units files that were specified + for (size_t cu_idx=0; cu_idxGetImages().FindCompileUnits(compile_units.GetFileSpecAtIndex(cu_idx), append, sc_list); + } + + const uint32_t num_scs = sc_list.GetSize(); + if (num_scs > 0) + { + SymbolContext sc; + for (uint32_t sc_idx=0; sc_idxGetVariableList(can_create)); + if (comp_unit_varlist_sp) + DumpGlobalVariableList(m_exe_ctx, sc, *comp_unit_varlist_sp, s); + } + else if (sc.module_sp) + { + // Get all global variables for this module + lldb_private::RegularExpression all_globals_regex("."); // Any global with at least one character + VariableList variable_list; + sc.module_sp->FindGlobalVariables(all_globals_regex, append, UINT32_MAX, variable_list); + DumpGlobalVariableList(m_exe_ctx, sc, variable_list, s); + } + } + } + } + } + } + + if (m_interpreter.TruncationWarningNecessary()) + { + result.GetOutputStream().Printf(m_interpreter.TruncationWarningText(), + m_cmd_name.c_str()); + m_interpreter.TruncationWarningGiven(); + } + + return result.Succeeded(); + } + + OptionGroupOptions m_option_group; + OptionGroupVariable m_option_variable; + OptionGroupFormat m_option_format; + OptionGroupFileList m_option_compile_units; + OptionGroupFileList m_option_shared_libraries; + OptionGroupValueObjectDisplay m_varobj_options; + +}; + + +#pragma mark CommandObjectTargetModulesSearchPathsAdd + +class CommandObjectTargetModulesSearchPathsAdd : public CommandObjectParsed +{ +public: + + CommandObjectTargetModulesSearchPathsAdd (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "target modules search-paths add", + "Add new image search paths substitution pairs to the current target.", + NULL) + { + CommandArgumentEntry arg; + CommandArgumentData old_prefix_arg; + CommandArgumentData new_prefix_arg; + + // Define the first variant of this arg pair. + old_prefix_arg.arg_type = eArgTypeOldPathPrefix; + old_prefix_arg.arg_repetition = eArgRepeatPairPlus; + + // Define the first variant of this arg pair. + new_prefix_arg.arg_type = eArgTypeNewPathPrefix; + new_prefix_arg.arg_repetition = eArgRepeatPairPlus; + + // There are two required arguments that must always occur together, i.e. an argument "pair". Because they + // must always occur together, they are treated as two variants of one argument rather than two independent + // arguments. Push them both into the first argument position for m_arguments... + + arg.push_back (old_prefix_arg); + arg.push_back (new_prefix_arg); + + m_arguments.push_back (arg); + } + + ~CommandObjectTargetModulesSearchPathsAdd () + { + } + +protected: + bool + DoExecute (Args& command, + CommandReturnObject &result) + { + Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get(); + if (target) + { + const size_t argc = command.GetArgumentCount(); + if (argc & 1) + { + result.AppendError ("add requires an even number of arguments\n"); + result.SetStatus (eReturnStatusFailed); + } + else + { + for (size_t i=0; iGetImageSearchPathList().Append (ConstString(from), + ConstString(to), + last_pair); // Notify if this is the last pair + result.SetStatus (eReturnStatusSuccessFinishNoResult); + } + else + { + if (from[0]) + result.AppendError (" can't be empty\n"); + else + result.AppendError (" can't be empty\n"); + result.SetStatus (eReturnStatusFailed); + } + } + } + } + else + { + result.AppendError ("invalid target\n"); + result.SetStatus (eReturnStatusFailed); + } + return result.Succeeded(); + } +}; + +#pragma mark CommandObjectTargetModulesSearchPathsClear + +class CommandObjectTargetModulesSearchPathsClear : public CommandObjectParsed +{ +public: + + CommandObjectTargetModulesSearchPathsClear (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "target modules search-paths clear", + "Clear all current image search path substitution pairs from the current target.", + "target modules search-paths clear") + { + } + + ~CommandObjectTargetModulesSearchPathsClear () + { + } + +protected: + bool + DoExecute (Args& command, + CommandReturnObject &result) + { + Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get(); + if (target) + { + bool notify = true; + target->GetImageSearchPathList().Clear(notify); + result.SetStatus (eReturnStatusSuccessFinishNoResult); + } + else + { + result.AppendError ("invalid target\n"); + result.SetStatus (eReturnStatusFailed); + } + return result.Succeeded(); + } +}; + +#pragma mark CommandObjectTargetModulesSearchPathsInsert + +class CommandObjectTargetModulesSearchPathsInsert : public CommandObjectParsed +{ +public: + + CommandObjectTargetModulesSearchPathsInsert (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "target modules search-paths insert", + "Insert a new image search path substitution pair into the current target at the specified index.", + NULL) + { + CommandArgumentEntry arg1; + CommandArgumentEntry arg2; + CommandArgumentData index_arg; + CommandArgumentData old_prefix_arg; + CommandArgumentData new_prefix_arg; + + // Define the first and only variant of this arg. + index_arg.arg_type = eArgTypeIndex; + index_arg.arg_repetition = eArgRepeatPlain; + + // Put the one and only variant into the first arg for m_arguments: + arg1.push_back (index_arg); + + // Define the first variant of this arg pair. + old_prefix_arg.arg_type = eArgTypeOldPathPrefix; + old_prefix_arg.arg_repetition = eArgRepeatPairPlus; + + // Define the first variant of this arg pair. + new_prefix_arg.arg_type = eArgTypeNewPathPrefix; + new_prefix_arg.arg_repetition = eArgRepeatPairPlus; + + // There are two required arguments that must always occur together, i.e. an argument "pair". Because they + // must always occur together, they are treated as two variants of one argument rather than two independent + // arguments. Push them both into the same argument position for m_arguments... + + arg2.push_back (old_prefix_arg); + arg2.push_back (new_prefix_arg); + + // Add arguments to m_arguments. + m_arguments.push_back (arg1); + m_arguments.push_back (arg2); + } + + ~CommandObjectTargetModulesSearchPathsInsert () + { + } + +protected: + bool + DoExecute (Args& command, + CommandReturnObject &result) + { + Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get(); + if (target) + { + size_t argc = command.GetArgumentCount(); + // check for at least 3 arguments and an odd nubmer of parameters + if (argc >= 3 && argc & 1) + { + bool success = false; + + uint32_t insert_idx = Args::StringToUInt32(command.GetArgumentAtIndex(0), UINT32_MAX, 0, &success); + + if (!success) + { + result.AppendErrorWithFormat(" parameter is not an integer: '%s'.\n", command.GetArgumentAtIndex(0)); + result.SetStatus (eReturnStatusFailed); + return result.Succeeded(); + } + + // shift off the index + command.Shift(); + argc = command.GetArgumentCount(); + + for (uint32_t i=0; iGetImageSearchPathList().Insert (ConstString(from), + ConstString(to), + insert_idx, + last_pair); + result.SetStatus (eReturnStatusSuccessFinishNoResult); + } + else + { + if (from[0]) + result.AppendError (" can't be empty\n"); + else + result.AppendError (" can't be empty\n"); + result.SetStatus (eReturnStatusFailed); + return false; + } + } + } + else + { + result.AppendError ("insert requires at least three arguments\n"); + result.SetStatus (eReturnStatusFailed); + return result.Succeeded(); + } + + } + else + { + result.AppendError ("invalid target\n"); + result.SetStatus (eReturnStatusFailed); + } + return result.Succeeded(); + } +}; + + +#pragma mark CommandObjectTargetModulesSearchPathsList + + +class CommandObjectTargetModulesSearchPathsList : public CommandObjectParsed +{ +public: + + CommandObjectTargetModulesSearchPathsList (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "target modules search-paths list", + "List all current image search path substitution pairs in the current target.", + "target modules search-paths list") + { + } + + ~CommandObjectTargetModulesSearchPathsList () + { + } + +protected: + bool + DoExecute (Args& command, + CommandReturnObject &result) + { + Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get(); + if (target) + { + if (command.GetArgumentCount() != 0) + { + result.AppendError ("list takes no arguments\n"); + result.SetStatus (eReturnStatusFailed); + return result.Succeeded(); + } + + target->GetImageSearchPathList().Dump(&result.GetOutputStream()); + result.SetStatus (eReturnStatusSuccessFinishResult); + } + else + { + result.AppendError ("invalid target\n"); + result.SetStatus (eReturnStatusFailed); + } + return result.Succeeded(); + } +}; + +#pragma mark CommandObjectTargetModulesSearchPathsQuery + +class CommandObjectTargetModulesSearchPathsQuery : public CommandObjectParsed +{ +public: + + CommandObjectTargetModulesSearchPathsQuery (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "target modules search-paths query", + "Transform a path using the first applicable image search path.", + NULL) + { + CommandArgumentEntry arg; + CommandArgumentData path_arg; + + // Define the first (and only) variant of this arg. + path_arg.arg_type = eArgTypeDirectoryName; + path_arg.arg_repetition = eArgRepeatPlain; + + // There is only one variant this argument could be; put it into the argument entry. + arg.push_back (path_arg); + + // Push the data for the first argument into the m_arguments vector. + m_arguments.push_back (arg); + } + + ~CommandObjectTargetModulesSearchPathsQuery () + { + } + +protected: + bool + DoExecute (Args& command, + CommandReturnObject &result) + { + Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get(); + if (target) + { + if (command.GetArgumentCount() != 1) + { + result.AppendError ("query requires one argument\n"); + result.SetStatus (eReturnStatusFailed); + return result.Succeeded(); + } + + ConstString orig(command.GetArgumentAtIndex(0)); + ConstString transformed; + if (target->GetImageSearchPathList().RemapPath(orig, transformed)) + result.GetOutputStream().Printf("%s\n", transformed.GetCString()); + else + result.GetOutputStream().Printf("%s\n", orig.GetCString()); + + result.SetStatus (eReturnStatusSuccessFinishResult); + } + else + { + result.AppendError ("invalid target\n"); + result.SetStatus (eReturnStatusFailed); + } + return result.Succeeded(); + } +}; + +//---------------------------------------------------------------------- +// Static Helper functions +//---------------------------------------------------------------------- +static void +DumpModuleArchitecture (Stream &strm, Module *module, bool full_triple, uint32_t width) +{ + if (module) + { + const char *arch_cstr; + if (full_triple) + arch_cstr = module->GetArchitecture().GetTriple().str().c_str(); + else + arch_cstr = module->GetArchitecture().GetArchitectureName(); + if (width) + strm.Printf("%-*s", width, arch_cstr); + else + strm.PutCString(arch_cstr); + } +} + +static void +DumpModuleUUID (Stream &strm, Module *module) +{ + if (module && module->GetUUID().IsValid()) + module->GetUUID().Dump (&strm); + else + strm.PutCString(" "); +} + +static uint32_t +DumpCompileUnitLineTable (CommandInterpreter &interpreter, + Stream &strm, + Module *module, + const FileSpec &file_spec, + bool load_addresses) +{ + uint32_t num_matches = 0; + if (module) + { + SymbolContextList sc_list; + num_matches = module->ResolveSymbolContextsForFileSpec (file_spec, + 0, + false, + eSymbolContextCompUnit, + sc_list); + + for (uint32_t i=0; i 0) + strm << "\n\n"; + + strm << "Line table for " << *static_cast (sc.comp_unit) << " in `" + << module->GetFileSpec().GetFilename() << "\n"; + LineTable *line_table = sc.comp_unit->GetLineTable(); + if (line_table) + line_table->GetDescription (&strm, + interpreter.GetExecutionContext().GetTargetPtr(), + lldb::eDescriptionLevelBrief); + else + strm << "No line table"; + } + } + } + return num_matches; +} + +static void +DumpFullpath (Stream &strm, const FileSpec *file_spec_ptr, uint32_t width) +{ + if (file_spec_ptr) + { + if (width > 0) + { + std::string fullpath = file_spec_ptr->GetPath(); + strm.Printf("%-*s", width, fullpath.c_str()); + return; + } + else + { + file_spec_ptr->Dump(&strm); + return; + } + } + // Keep the width spacing correct if things go wrong... + if (width > 0) + strm.Printf("%-*s", width, ""); +} + +static void +DumpDirectory (Stream &strm, const FileSpec *file_spec_ptr, uint32_t width) +{ + if (file_spec_ptr) + { + if (width > 0) + strm.Printf("%-*s", width, file_spec_ptr->GetDirectory().AsCString("")); + else + file_spec_ptr->GetDirectory().Dump(&strm); + return; + } + // Keep the width spacing correct if things go wrong... + if (width > 0) + strm.Printf("%-*s", width, ""); +} + +static void +DumpBasename (Stream &strm, const FileSpec *file_spec_ptr, uint32_t width) +{ + if (file_spec_ptr) + { + if (width > 0) + strm.Printf("%-*s", width, file_spec_ptr->GetFilename().AsCString("")); + else + file_spec_ptr->GetFilename().Dump(&strm); + return; + } + // Keep the width spacing correct if things go wrong... + if (width > 0) + strm.Printf("%-*s", width, ""); +} + + +static void +DumpModuleSymtab (CommandInterpreter &interpreter, Stream &strm, Module *module, SortOrder sort_order) +{ + if (module) + { + SymbolVendor *sym_vendor = module->GetSymbolVendor (); + if (sym_vendor) + { + Symtab *symtab = sym_vendor->GetSymtab(); + if (symtab) + symtab->Dump(&strm, interpreter.GetExecutionContext().GetTargetPtr(), sort_order); + } + } +} + +static void +DumpModuleSections (CommandInterpreter &interpreter, Stream &strm, Module *module) +{ + if (module) + { + SectionList *section_list = module->GetSectionList(); + if (section_list) + { + strm.Printf ("Sections for '%s' (%s):\n", + module->GetSpecificationDescription().c_str(), + module->GetArchitecture().GetArchitectureName()); + strm.IndentMore(); + section_list->Dump(&strm, interpreter.GetExecutionContext().GetTargetPtr(), true, UINT32_MAX); + strm.IndentLess(); + } + } +} + +static bool +DumpModuleSymbolVendor (Stream &strm, Module *module) +{ + if (module) + { + SymbolVendor *symbol_vendor = module->GetSymbolVendor(true); + if (symbol_vendor) + { + symbol_vendor->Dump(&strm); + return true; + } + } + return false; +} + +static void +DumpAddress (ExecutionContextScope *exe_scope, const Address &so_addr, bool verbose, Stream &strm) +{ + strm.IndentMore(); + strm.Indent (" Address: "); + so_addr.Dump (&strm, exe_scope, Address::DumpStyleModuleWithFileAddress); + strm.PutCString (" ("); + so_addr.Dump (&strm, exe_scope, Address::DumpStyleSectionNameOffset); + strm.PutCString (")\n"); + strm.Indent (" Summary: "); + const uint32_t save_indent = strm.GetIndentLevel (); + strm.SetIndentLevel (save_indent + 13); + so_addr.Dump (&strm, exe_scope, Address::DumpStyleResolvedDescription); + strm.SetIndentLevel (save_indent); + // Print out detailed address information when verbose is enabled + if (verbose) + { + strm.EOL(); + so_addr.Dump (&strm, exe_scope, Address::DumpStyleDetailedSymbolContext); + } + strm.IndentLess(); +} + +static bool +LookupAddressInModule (CommandInterpreter &interpreter, + Stream &strm, + Module *module, + uint32_t resolve_mask, + lldb::addr_t raw_addr, + lldb::addr_t offset, + bool verbose) +{ + if (module) + { + lldb::addr_t addr = raw_addr - offset; + Address so_addr; + SymbolContext sc; + Target *target = interpreter.GetExecutionContext().GetTargetPtr(); + if (target && !target->GetSectionLoadList().IsEmpty()) + { + if (!target->GetSectionLoadList().ResolveLoadAddress (addr, so_addr)) + return false; + else if (so_addr.GetModule().get() != module) + return false; + } + else + { + if (!module->ResolveFileAddress (addr, so_addr)) + return false; + } + + ExecutionContextScope *exe_scope = interpreter.GetExecutionContext().GetBestExecutionContextScope(); + DumpAddress (exe_scope, so_addr, verbose, strm); +// strm.IndentMore(); +// strm.Indent (" Address: "); +// so_addr.Dump (&strm, exe_scope, Address::DumpStyleModuleWithFileAddress); +// strm.PutCString (" ("); +// so_addr.Dump (&strm, exe_scope, Address::DumpStyleSectionNameOffset); +// strm.PutCString (")\n"); +// strm.Indent (" Summary: "); +// const uint32_t save_indent = strm.GetIndentLevel (); +// strm.SetIndentLevel (save_indent + 13); +// so_addr.Dump (&strm, exe_scope, Address::DumpStyleResolvedDescription); +// strm.SetIndentLevel (save_indent); +// // Print out detailed address information when verbose is enabled +// if (verbose) +// { +// strm.EOL(); +// so_addr.Dump (&strm, exe_scope, Address::DumpStyleDetailedSymbolContext); +// } +// strm.IndentLess(); + return true; + } + + return false; +} + +static uint32_t +LookupSymbolInModule (CommandInterpreter &interpreter, Stream &strm, Module *module, const char *name, bool name_is_regex, bool verbose) +{ + if (module) + { + SymbolContext sc; + + SymbolVendor *sym_vendor = module->GetSymbolVendor (); + if (sym_vendor) + { + Symtab *symtab = sym_vendor->GetSymtab(); + if (symtab) + { + uint32_t i; + std::vector match_indexes; + ConstString symbol_name (name); + uint32_t num_matches = 0; + if (name_is_regex) + { + RegularExpression name_regexp(name); + num_matches = symtab->AppendSymbolIndexesMatchingRegExAndType (name_regexp, + eSymbolTypeAny, + match_indexes); + } + else + { + num_matches = symtab->AppendSymbolIndexesWithName (symbol_name, match_indexes); + } + + + if (num_matches > 0) + { + strm.Indent (); + strm.Printf("%u symbols match %s'%s' in ", num_matches, + name_is_regex ? "the regular expression " : "", name); + DumpFullpath (strm, &module->GetFileSpec(), 0); + strm.PutCString(":\n"); + strm.IndentMore (); + //Symtab::DumpSymbolHeader (&strm); + for (i=0; i < num_matches; ++i) + { + Symbol *symbol = symtab->SymbolAtIndex(match_indexes[i]); + DumpAddress (interpreter.GetExecutionContext().GetBestExecutionContextScope(), + symbol->GetAddress(), + verbose, + strm); + +// strm.Indent (); +// symbol->Dump (&strm, interpreter.GetExecutionContext().GetTargetPtr(), i); + } + strm.IndentLess (); + return num_matches; + } + } + } + } + return 0; +} + + +static void +DumpSymbolContextList (ExecutionContextScope *exe_scope, Stream &strm, SymbolContextList &sc_list, bool verbose) +{ + strm.IndentMore (); + uint32_t i; + const uint32_t num_matches = sc_list.GetSize(); + + for (i=0; iFindFunctions (function_name_regex, + include_symbols, + include_inlines, + append, + sc_list); + } + else + { + ConstString function_name (name); + num_matches = module->FindFunctions (function_name, + NULL, + eFunctionNameTypeAuto, + include_symbols, + include_inlines, + append, + sc_list); + } + + if (num_matches) + { + strm.Indent (); + strm.Printf("%zu match%s found in ", num_matches, num_matches > 1 ? "es" : ""); + DumpFullpath (strm, &module->GetFileSpec(), 0); + strm.PutCString(":\n"); + DumpSymbolContextList (interpreter.GetExecutionContext().GetBestExecutionContextScope(), strm, sc_list, verbose); + } + return num_matches; + } + return 0; +} + +static size_t +LookupTypeInModule (CommandInterpreter &interpreter, + Stream &strm, + Module *module, + const char *name_cstr, + bool name_is_regex) +{ + if (module && name_cstr && name_cstr[0]) + { + TypeList type_list; + const uint32_t max_num_matches = UINT32_MAX; + size_t num_matches = 0; + bool name_is_fully_qualified = false; + SymbolContext sc; + + ConstString name(name_cstr); + num_matches = module->FindTypes(sc, name, name_is_fully_qualified, max_num_matches, type_list); + + if (num_matches) + { + strm.Indent (); + strm.Printf("%zu match%s found in ", num_matches, num_matches > 1 ? "es" : ""); + DumpFullpath (strm, &module->GetFileSpec(), 0); + strm.PutCString(":\n"); + const uint32_t num_types = type_list.GetSize(); + for (uint32_t i=0; iGetClangFullType (); + type_sp->GetDescription (&strm, eDescriptionLevelFull, true); + // Print all typedef chains + TypeSP typedef_type_sp (type_sp); + TypeSP typedefed_type_sp (typedef_type_sp->GetTypedefType()); + while (typedefed_type_sp) + { + strm.EOL(); + strm.Printf(" typedef '%s': ", typedef_type_sp->GetName().GetCString()); + typedefed_type_sp->GetClangFullType (); + typedefed_type_sp->GetDescription (&strm, eDescriptionLevelFull, true); + typedef_type_sp = typedefed_type_sp; + typedefed_type_sp = typedef_type_sp->GetTypedefType(); + } + } + strm.EOL(); + } + } + return num_matches; + } + return 0; +} + +static size_t +LookupTypeHere (CommandInterpreter &interpreter, + Stream &strm, + const SymbolContext &sym_ctx, + const char *name_cstr, + bool name_is_regex) +{ + if (!sym_ctx.module_sp) + return 0; + + TypeList type_list; + const uint32_t max_num_matches = UINT32_MAX; + size_t num_matches = 1; + bool name_is_fully_qualified = false; + + ConstString name(name_cstr); + num_matches = sym_ctx.module_sp->FindTypes(sym_ctx, name, name_is_fully_qualified, max_num_matches, type_list); + + if (num_matches) + { + strm.Indent (); + strm.PutCString("Best match found in "); + DumpFullpath (strm, &sym_ctx.module_sp->GetFileSpec(), 0); + strm.PutCString(":\n"); + + TypeSP type_sp (type_list.GetTypeAtIndex(0)); + if (type_sp) + { + // Resolve the clang type so that any forward references + // to types that haven't yet been parsed will get parsed. + type_sp->GetClangFullType (); + type_sp->GetDescription (&strm, eDescriptionLevelFull, true); + // Print all typedef chains + TypeSP typedef_type_sp (type_sp); + TypeSP typedefed_type_sp (typedef_type_sp->GetTypedefType()); + while (typedefed_type_sp) + { + strm.EOL(); + strm.Printf(" typedef '%s': ", typedef_type_sp->GetName().GetCString()); + typedefed_type_sp->GetClangFullType (); + typedefed_type_sp->GetDescription (&strm, eDescriptionLevelFull, true); + typedef_type_sp = typedefed_type_sp; + typedefed_type_sp = typedef_type_sp->GetTypedefType(); + } + } + strm.EOL(); + } + return num_matches; +} + +static uint32_t +LookupFileAndLineInModule (CommandInterpreter &interpreter, + Stream &strm, + Module *module, + const FileSpec &file_spec, + uint32_t line, + bool check_inlines, + bool verbose) +{ + if (module && file_spec) + { + SymbolContextList sc_list; + const uint32_t num_matches = module->ResolveSymbolContextsForFileSpec(file_spec, line, check_inlines, + eSymbolContextEverything, sc_list); + if (num_matches > 0) + { + strm.Indent (); + strm.Printf("%u match%s found in ", num_matches, num_matches > 1 ? "es" : ""); + strm << file_spec; + if (line > 0) + strm.Printf (":%u", line); + strm << " in "; + DumpFullpath (strm, &module->GetFileSpec(), 0); + strm.PutCString(":\n"); + DumpSymbolContextList (interpreter.GetExecutionContext().GetBestExecutionContextScope(), strm, sc_list, verbose); + return num_matches; + } + } + return 0; + +} + + +static size_t +FindModulesByName (Target *target, + const char *module_name, + ModuleList &module_list, + bool check_global_list) +{ +// Dump specified images (by basename or fullpath) + FileSpec module_file_spec(module_name, false); + ModuleSpec module_spec (module_file_spec); + + const size_t initial_size = module_list.GetSize (); + + if (check_global_list) + { + // Check the global list + Mutex::Locker locker(Module::GetAllocationModuleCollectionMutex()); + const size_t num_modules = Module::GetNumberAllocatedModules(); + ModuleSP module_sp; + for (size_t image_idx = 0; image_idxMatchesModuleSpec (module_spec)) + { + module_sp = module->shared_from_this(); + module_list.AppendIfNeeded(module_sp); + } + } + } + } + else + { + if (target) + { + const size_t num_matches = target->GetImages().FindModules (module_spec, module_list); + + // Not found in our module list for our target, check the main + // shared module list in case it is a extra file used somewhere + // else + if (num_matches == 0) + { + module_spec.GetArchitecture() = target->GetArchitecture(); + ModuleList::FindSharedModules (module_spec, module_list); + } + } + else + { + ModuleList::FindSharedModules (module_spec,module_list); + } + } + + return module_list.GetSize () - initial_size; +} + +#pragma mark CommandObjectTargetModulesModuleAutoComplete + +//---------------------------------------------------------------------- +// A base command object class that can auto complete with module file +// paths +//---------------------------------------------------------------------- + +class CommandObjectTargetModulesModuleAutoComplete : public CommandObjectParsed +{ +public: + + CommandObjectTargetModulesModuleAutoComplete (CommandInterpreter &interpreter, + const char *name, + const char *help, + const char *syntax) : + CommandObjectParsed (interpreter, name, help, syntax) + { + CommandArgumentEntry arg; + CommandArgumentData file_arg; + + // Define the first (and only) variant of this arg. + file_arg.arg_type = eArgTypeFilename; + file_arg.arg_repetition = eArgRepeatStar; + + // There is only one variant this argument could be; put it into the argument entry. + arg.push_back (file_arg); + + // Push the data for the first argument into the m_arguments vector. + m_arguments.push_back (arg); + } + + virtual + ~CommandObjectTargetModulesModuleAutoComplete () + { + } + + virtual int + HandleArgumentCompletion (Args &input, + int &cursor_index, + int &cursor_char_position, + OptionElementVector &opt_element_vector, + int match_start_point, + int max_return_elements, + bool &word_complete, + StringList &matches) + { + // Arguments are the standard module completer. + std::string completion_str (input.GetArgumentAtIndex(cursor_index)); + completion_str.erase (cursor_char_position); + + CommandCompletions::InvokeCommonCompletionCallbacks (m_interpreter, + CommandCompletions::eModuleCompletion, + completion_str.c_str(), + match_start_point, + max_return_elements, + NULL, + word_complete, + matches); + return matches.GetSize(); + } +}; + +#pragma mark CommandObjectTargetModulesSourceFileAutoComplete + +//---------------------------------------------------------------------- +// A base command object class that can auto complete with module source +// file paths +//---------------------------------------------------------------------- + +class CommandObjectTargetModulesSourceFileAutoComplete : public CommandObjectParsed +{ +public: + + CommandObjectTargetModulesSourceFileAutoComplete (CommandInterpreter &interpreter, + const char *name, + const char *help, + const char *syntax, + uint32_t flags) : + CommandObjectParsed (interpreter, name, help, syntax, flags) + { + CommandArgumentEntry arg; + CommandArgumentData source_file_arg; + + // Define the first (and only) variant of this arg. + source_file_arg.arg_type = eArgTypeSourceFile; + source_file_arg.arg_repetition = eArgRepeatPlus; + + // There is only one variant this argument could be; put it into the argument entry. + arg.push_back (source_file_arg); + + // Push the data for the first argument into the m_arguments vector. + m_arguments.push_back (arg); + } + + virtual + ~CommandObjectTargetModulesSourceFileAutoComplete () + { + } + + virtual int + HandleArgumentCompletion (Args &input, + int &cursor_index, + int &cursor_char_position, + OptionElementVector &opt_element_vector, + int match_start_point, + int max_return_elements, + bool &word_complete, + StringList &matches) + { + // Arguments are the standard source file completer. + std::string completion_str (input.GetArgumentAtIndex(cursor_index)); + completion_str.erase (cursor_char_position); + + CommandCompletions::InvokeCommonCompletionCallbacks (m_interpreter, + CommandCompletions::eSourceFileCompletion, + completion_str.c_str(), + match_start_point, + max_return_elements, + NULL, + word_complete, + matches); + return matches.GetSize(); + } +}; + + +#pragma mark CommandObjectTargetModulesDumpSymtab + + +class CommandObjectTargetModulesDumpSymtab : public CommandObjectTargetModulesModuleAutoComplete +{ +public: + CommandObjectTargetModulesDumpSymtab (CommandInterpreter &interpreter) : + CommandObjectTargetModulesModuleAutoComplete (interpreter, + "target modules dump symtab", + "Dump the symbol table from one or more target modules.", + NULL), + m_options (interpreter) + { + } + + virtual + ~CommandObjectTargetModulesDumpSymtab () + { + } + + virtual Options * + GetOptions () + { + return &m_options; + } + + class CommandOptions : public Options + { + public: + + CommandOptions (CommandInterpreter &interpreter) : + Options(interpreter), + m_sort_order (eSortOrderNone) + { + } + + virtual + ~CommandOptions () + { + } + + virtual Error + SetOptionValue (uint32_t option_idx, const char *option_arg) + { + Error error; + const int short_option = m_getopt_table[option_idx].val; + + switch (short_option) + { + case 's': + m_sort_order = (SortOrder) Args::StringToOptionEnum (option_arg, + g_option_table[option_idx].enum_values, + eSortOrderNone, + error); + break; + + default: + error.SetErrorStringWithFormat("invalid short option character '%c'", short_option); + break; + + } + return error; + } + + void + OptionParsingStarting () + { + m_sort_order = eSortOrderNone; + } + + const OptionDefinition* + GetDefinitions () + { + return g_option_table; + } + + // Options table: Required for subclasses of Options. + static OptionDefinition g_option_table[]; + + SortOrder m_sort_order; + }; + +protected: + virtual bool + DoExecute (Args& command, + CommandReturnObject &result) + { + Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get(); + if (target == NULL) + { + result.AppendError ("invalid target, create a debug target using the 'target create' command"); + result.SetStatus (eReturnStatusFailed); + return false; + } + else + { + uint32_t num_dumped = 0; + + uint32_t addr_byte_size = target->GetArchitecture().GetAddressByteSize(); + result.GetOutputStream().SetAddressByteSize(addr_byte_size); + result.GetErrorStream().SetAddressByteSize(addr_byte_size); + + if (command.GetArgumentCount() == 0) + { + // Dump all sections for all modules images + Mutex::Locker modules_locker(target->GetImages().GetMutex()); + const size_t num_modules = target->GetImages().GetSize(); + if (num_modules > 0) + { + result.GetOutputStream().Printf("Dumping symbol table for %zu modules.\n", num_modules); + for (size_t image_idx = 0; image_idx 0) + { + result.GetOutputStream().EOL(); + result.GetOutputStream().EOL(); + } + num_dumped++; + DumpModuleSymtab (m_interpreter, + result.GetOutputStream(), + target->GetImages().GetModulePointerAtIndexUnlocked(image_idx), + m_options.m_sort_order); + } + } + else + { + result.AppendError ("the target has no associated executable images"); + result.SetStatus (eReturnStatusFailed); + return false; + } + } + else + { + // Dump specified images (by basename or fullpath) + const char *arg_cstr; + for (int arg_idx = 0; (arg_cstr = command.GetArgumentAtIndex(arg_idx)) != NULL; ++arg_idx) + { + ModuleList module_list; + const size_t num_matches = FindModulesByName (target, arg_cstr, module_list, true); + if (num_matches > 0) + { + for (size_t i=0; i 0) + { + result.GetOutputStream().EOL(); + result.GetOutputStream().EOL(); + } + num_dumped++; + DumpModuleSymtab (m_interpreter, result.GetOutputStream(), module, m_options.m_sort_order); + } + } + } + else + result.AppendWarningWithFormat("Unable to find an image that matches '%s'.\n", arg_cstr); + } + } + + if (num_dumped > 0) + result.SetStatus (eReturnStatusSuccessFinishResult); + else + { + result.AppendError ("no matching executable images found"); + result.SetStatus (eReturnStatusFailed); + } + } + return result.Succeeded(); + } + + + CommandOptions m_options; +}; + +static OptionEnumValueElement +g_sort_option_enumeration[4] = +{ + { eSortOrderNone, "none", "No sorting, use the original symbol table order."}, + { eSortOrderByAddress, "address", "Sort output by symbol address."}, + { eSortOrderByName, "name", "Sort output by symbol name."}, + { 0, NULL, NULL } +}; + + +OptionDefinition +CommandObjectTargetModulesDumpSymtab::CommandOptions::g_option_table[] = +{ + { LLDB_OPT_SET_1, false, "sort", 's', required_argument, g_sort_option_enumeration, 0, eArgTypeSortOrder, "Supply a sort order when dumping the symbol table."}, + { 0, false, NULL, 0, 0, NULL, 0, eArgTypeNone, NULL } +}; + +#pragma mark CommandObjectTargetModulesDumpSections + +//---------------------------------------------------------------------- +// Image section dumping command +//---------------------------------------------------------------------- + +class CommandObjectTargetModulesDumpSections : public CommandObjectTargetModulesModuleAutoComplete +{ +public: + CommandObjectTargetModulesDumpSections (CommandInterpreter &interpreter) : + CommandObjectTargetModulesModuleAutoComplete (interpreter, + "target modules dump sections", + "Dump the sections from one or more target modules.", + //"target modules dump sections [ ...]") + NULL) + { + } + + virtual + ~CommandObjectTargetModulesDumpSections () + { + } + +protected: + virtual bool + DoExecute (Args& command, + CommandReturnObject &result) + { + Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get(); + if (target == NULL) + { + result.AppendError ("invalid target, create a debug target using the 'target create' command"); + result.SetStatus (eReturnStatusFailed); + return false; + } + else + { + uint32_t num_dumped = 0; + + uint32_t addr_byte_size = target->GetArchitecture().GetAddressByteSize(); + result.GetOutputStream().SetAddressByteSize(addr_byte_size); + result.GetErrorStream().SetAddressByteSize(addr_byte_size); + + if (command.GetArgumentCount() == 0) + { + // Dump all sections for all modules images + const size_t num_modules = target->GetImages().GetSize(); + if (num_modules > 0) + { + result.GetOutputStream().Printf("Dumping sections for %zu modules.\n", num_modules); + for (size_t image_idx = 0; image_idxGetImages().GetModulePointerAtIndex(image_idx)); + } + } + else + { + result.AppendError ("the target has no associated executable images"); + result.SetStatus (eReturnStatusFailed); + return false; + } + } + else + { + // Dump specified images (by basename or fullpath) + const char *arg_cstr; + for (int arg_idx = 0; (arg_cstr = command.GetArgumentAtIndex(arg_idx)) != NULL; ++arg_idx) + { + ModuleList module_list; + const size_t num_matches = FindModulesByName (target, arg_cstr, module_list, true); + if (num_matches > 0) + { + for (size_t i=0; i 0) + result.SetStatus (eReturnStatusSuccessFinishResult); + else + { + result.AppendError ("no matching executable images found"); + result.SetStatus (eReturnStatusFailed); + } + } + return result.Succeeded(); + } +}; + + +#pragma mark CommandObjectTargetModulesDumpSymfile + +//---------------------------------------------------------------------- +// Image debug symbol dumping command +//---------------------------------------------------------------------- + +class CommandObjectTargetModulesDumpSymfile : public CommandObjectTargetModulesModuleAutoComplete +{ +public: + CommandObjectTargetModulesDumpSymfile (CommandInterpreter &interpreter) : + CommandObjectTargetModulesModuleAutoComplete (interpreter, + "target modules dump symfile", + "Dump the debug symbol file for one or more target modules.", + //"target modules dump symfile [ ...]") + NULL) + { + } + + virtual + ~CommandObjectTargetModulesDumpSymfile () + { + } + +protected: + virtual bool + DoExecute (Args& command, + CommandReturnObject &result) + { + Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get(); + if (target == NULL) + { + result.AppendError ("invalid target, create a debug target using the 'target create' command"); + result.SetStatus (eReturnStatusFailed); + return false; + } + else + { + uint32_t num_dumped = 0; + + uint32_t addr_byte_size = target->GetArchitecture().GetAddressByteSize(); + result.GetOutputStream().SetAddressByteSize(addr_byte_size); + result.GetErrorStream().SetAddressByteSize(addr_byte_size); + + if (command.GetArgumentCount() == 0) + { + // Dump all sections for all modules images + const ModuleList &target_modules = target->GetImages(); + Mutex::Locker modules_locker (target_modules.GetMutex()); + const size_t num_modules = target_modules.GetSize(); + if (num_modules > 0) + { + result.GetOutputStream().Printf("Dumping debug symbols for %zu modules.\n", num_modules); + for (uint32_t image_idx = 0; image_idx 0) + { + for (size_t i=0; i 0) + result.SetStatus (eReturnStatusSuccessFinishResult); + else + { + result.AppendError ("no matching executable images found"); + result.SetStatus (eReturnStatusFailed); + } + } + return result.Succeeded(); + } +}; + + +#pragma mark CommandObjectTargetModulesDumpLineTable + +//---------------------------------------------------------------------- +// Image debug line table dumping command +//---------------------------------------------------------------------- + +class CommandObjectTargetModulesDumpLineTable : public CommandObjectTargetModulesSourceFileAutoComplete +{ +public: + CommandObjectTargetModulesDumpLineTable (CommandInterpreter &interpreter) : + CommandObjectTargetModulesSourceFileAutoComplete (interpreter, + "target modules dump line-table", + "Dump the line table for one or more compilation units.", + NULL, + eFlagRequiresTarget) + { + } + + virtual + ~CommandObjectTargetModulesDumpLineTable () + { + } + +protected: + virtual bool + DoExecute (Args& command, + CommandReturnObject &result) + { + Target *target = m_exe_ctx.GetTargetPtr(); + uint32_t total_num_dumped = 0; + + uint32_t addr_byte_size = target->GetArchitecture().GetAddressByteSize(); + result.GetOutputStream().SetAddressByteSize(addr_byte_size); + result.GetErrorStream().SetAddressByteSize(addr_byte_size); + + if (command.GetArgumentCount() == 0) + { + result.AppendErrorWithFormat ("\nSyntax: %s\n", m_cmd_syntax.c_str()); + result.SetStatus (eReturnStatusFailed); + } + else + { + // Dump specified images (by basename or fullpath) + const char *arg_cstr; + for (int arg_idx = 0; (arg_cstr = command.GetArgumentAtIndex(arg_idx)) != NULL; ++arg_idx) + { + FileSpec file_spec(arg_cstr, false); + + const ModuleList &target_modules = target->GetImages(); + Mutex::Locker modules_locker(target_modules.GetMutex()); + const size_t num_modules = target_modules.GetSize(); + if (num_modules > 0) + { + uint32_t num_dumped = 0; + for (uint32_t i = 0; i 0) + result.SetStatus (eReturnStatusSuccessFinishResult); + else + { + result.AppendError ("no source filenames matched any command arguments"); + result.SetStatus (eReturnStatusFailed); + } + return result.Succeeded(); + } +}; + + +#pragma mark CommandObjectTargetModulesDump + +//---------------------------------------------------------------------- +// Dump multi-word command for target modules +//---------------------------------------------------------------------- + +class CommandObjectTargetModulesDump : public CommandObjectMultiword +{ +public: + + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + CommandObjectTargetModulesDump(CommandInterpreter &interpreter) : + CommandObjectMultiword (interpreter, + "target modules dump", + "A set of commands for dumping information about one or more target modules.", + "target modules dump [symtab|sections|symfile|line-table] [ ...]") + { + LoadSubCommand ("symtab", CommandObjectSP (new CommandObjectTargetModulesDumpSymtab (interpreter))); + LoadSubCommand ("sections", CommandObjectSP (new CommandObjectTargetModulesDumpSections (interpreter))); + LoadSubCommand ("symfile", CommandObjectSP (new CommandObjectTargetModulesDumpSymfile (interpreter))); + LoadSubCommand ("line-table", CommandObjectSP (new CommandObjectTargetModulesDumpLineTable (interpreter))); + } + + virtual + ~CommandObjectTargetModulesDump() + { + } +}; + +class CommandObjectTargetModulesAdd : public CommandObjectParsed +{ +public: + CommandObjectTargetModulesAdd (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "target modules add", + "Add a new module to the current target's modules.", + "target modules add []"), + m_option_group (interpreter), + m_symbol_file (LLDB_OPT_SET_1, false, "symfile", 's', 0, eArgTypeFilename, "Fullpath to a stand alone debug symbols file for when debug symbols are not in the executable.") + { + m_option_group.Append (&m_uuid_option_group, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1); + m_option_group.Append (&m_symbol_file, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1); + m_option_group.Finalize(); + } + + virtual + ~CommandObjectTargetModulesAdd () + { + } + + virtual Options * + GetOptions () + { + return &m_option_group; + } + + virtual int + HandleArgumentCompletion (Args &input, + int &cursor_index, + int &cursor_char_position, + OptionElementVector &opt_element_vector, + int match_start_point, + int max_return_elements, + bool &word_complete, + StringList &matches) + { + std::string completion_str (input.GetArgumentAtIndex(cursor_index)); + completion_str.erase (cursor_char_position); + + CommandCompletions::InvokeCommonCompletionCallbacks (m_interpreter, + CommandCompletions::eDiskFileCompletion, + completion_str.c_str(), + match_start_point, + max_return_elements, + NULL, + word_complete, + matches); + return matches.GetSize(); + } + +protected: + + OptionGroupOptions m_option_group; + OptionGroupUUID m_uuid_option_group; + OptionGroupFile m_symbol_file; + + + virtual bool + DoExecute (Args& args, + CommandReturnObject &result) + { + Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get(); + if (target == NULL) + { + result.AppendError ("invalid target, create a debug target using the 'target create' command"); + result.SetStatus (eReturnStatusFailed); + return false; + } + else + { + bool flush = false; + + const size_t argc = args.GetArgumentCount(); + if (argc == 0) + { + if (m_uuid_option_group.GetOptionValue ().OptionWasSet()) + { + // We are given a UUID only, go locate the file + ModuleSpec module_spec; + module_spec.GetUUID() = m_uuid_option_group.GetOptionValue ().GetCurrentValue(); + if (m_symbol_file.GetOptionValue().OptionWasSet()) + module_spec.GetSymbolFileSpec() = m_symbol_file.GetOptionValue().GetCurrentValue(); + if (Symbols::DownloadObjectAndSymbolFile (module_spec)) + { + ModuleSP module_sp (target->GetSharedModule (module_spec)); + if (module_sp) + { + result.SetStatus (eReturnStatusSuccessFinishResult); + return true; + } + else + { + flush = true; + + StreamString strm; + module_spec.GetUUID().Dump (&strm); + if (module_spec.GetFileSpec()) + { + if (module_spec.GetSymbolFileSpec()) + { + result.AppendErrorWithFormat ("Unable to create the executable or symbol file with UUID %s with path %s and symbol file %s", + strm.GetString().c_str(), + module_spec.GetFileSpec().GetPath().c_str(), + module_spec.GetSymbolFileSpec().GetPath().c_str()); + } + else + { + result.AppendErrorWithFormat ("Unable to create the executable or symbol file with UUID %s with path %s", + strm.GetString().c_str(), + module_spec.GetFileSpec().GetPath().c_str()); + } + } + else + { + result.AppendErrorWithFormat ("Unable to create the executable or symbol file with UUID %s", + strm.GetString().c_str()); + } + result.SetStatus (eReturnStatusFailed); + return false; + } + } + else + { + StreamString strm; + module_spec.GetUUID().Dump (&strm); + result.AppendErrorWithFormat ("Unable to locate the executable or symbol file with UUID %s", strm.GetString().c_str()); + result.SetStatus (eReturnStatusFailed); + return false; + } + } + else + { + result.AppendError ("one or more executable image paths must be specified"); + result.SetStatus (eReturnStatusFailed); + return false; + } + } + else + { + for (size_t i=0; iGetSharedModule (module_spec, &error)); + if (!module_sp) + { + const char *error_cstr = error.AsCString(); + if (error_cstr) + result.AppendError (error_cstr); + else + result.AppendErrorWithFormat ("unsupported module: %s", path); + result.SetStatus (eReturnStatusFailed); + return false; + } + else + { + flush = true; + } + result.SetStatus (eReturnStatusSuccessFinishResult); + } + else + { + char resolved_path[PATH_MAX]; + result.SetStatus (eReturnStatusFailed); + if (file_spec.GetPath (resolved_path, sizeof(resolved_path))) + { + if (strcmp (resolved_path, path) != 0) + { + result.AppendErrorWithFormat ("invalid module path '%s' with resolved path '%s'\n", path, resolved_path); + break; + } + } + result.AppendErrorWithFormat ("invalid module path '%s'\n", path); + break; + } + } + } + } + + if (flush) + { + ProcessSP process = target->GetProcessSP(); + if (process) + process->Flush(); + } + } + + return result.Succeeded(); + } + +}; + +class CommandObjectTargetModulesLoad : public CommandObjectTargetModulesModuleAutoComplete +{ +public: + CommandObjectTargetModulesLoad (CommandInterpreter &interpreter) : + CommandObjectTargetModulesModuleAutoComplete (interpreter, + "target modules load", + "Set the load addresses for one or more sections in a target module.", + "target modules load [--file --uuid ]
[
....]"), + m_option_group (interpreter), + m_file_option (LLDB_OPT_SET_1, false, "file", 'f', 0, eArgTypeFilename, "Fullpath or basename for module to load."), + m_slide_option(LLDB_OPT_SET_1, false, "slide", 's', 0, eArgTypeOffset, "Set the load address for all sections to be the virtual address in the file plus the offset.", 0) + { + m_option_group.Append (&m_uuid_option_group, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1); + m_option_group.Append (&m_file_option, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1); + m_option_group.Append (&m_slide_option, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1); + m_option_group.Finalize(); + } + + virtual + ~CommandObjectTargetModulesLoad () + { + } + + virtual Options * + GetOptions () + { + return &m_option_group; + } + +protected: + virtual bool + DoExecute (Args& args, + CommandReturnObject &result) + { + Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get(); + if (target == NULL) + { + result.AppendError ("invalid target, create a debug target using the 'target create' command"); + result.SetStatus (eReturnStatusFailed); + return false; + } + else + { + const size_t argc = args.GetArgumentCount(); + ModuleSpec module_spec; + bool search_using_module_spec = false; + if (m_file_option.GetOptionValue().OptionWasSet()) + { + search_using_module_spec = true; + module_spec.GetFileSpec() = m_file_option.GetOptionValue().GetCurrentValue(); + } + + if (m_uuid_option_group.GetOptionValue().OptionWasSet()) + { + search_using_module_spec = true; + module_spec.GetUUID() = m_uuid_option_group.GetOptionValue().GetCurrentValue(); + } + + if (search_using_module_spec) + { + + ModuleList matching_modules; + const size_t num_matches = target->GetImages().FindModules (module_spec, matching_modules); + + char path[PATH_MAX]; + if (num_matches == 1) + { + Module *module = matching_modules.GetModulePointerAtIndex(0); + if (module) + { + ObjectFile *objfile = module->GetObjectFile(); + if (objfile) + { + SectionList *section_list = module->GetSectionList(); + if (section_list) + { + bool changed = false; + if (argc == 0) + { + if (m_slide_option.GetOptionValue().OptionWasSet()) + { + const addr_t slide = m_slide_option.GetOptionValue().GetCurrentValue(); + module->SetLoadAddress (*target, slide, changed); + } + else + { + result.AppendError ("one or more section name + load address pair must be specified"); + result.SetStatus (eReturnStatusFailed); + return false; + } + } + else + { + if (m_slide_option.GetOptionValue().OptionWasSet()) + { + result.AppendError ("The \"--slide \" option can't be used in conjunction with setting section load addresses.\n"); + result.SetStatus (eReturnStatusFailed); + return false; + } + + for (size_t i=0; iFindSectionByName(const_sect_name)); + if (section_sp) + { + if (section_sp->IsThreadSpecific()) + { + result.AppendErrorWithFormat ("thread specific sections are not yet supported (section '%s')\n", sect_name); + result.SetStatus (eReturnStatusFailed); + break; + } + else + { + if (target->GetSectionLoadList().SetSectionLoadAddress (section_sp, load_addr)) + changed = true; + result.AppendMessageWithFormat("section '%s' loaded at 0x%" PRIx64 "\n", sect_name, load_addr); + } + } + else + { + result.AppendErrorWithFormat ("no section found that matches the section name '%s'\n", sect_name); + result.SetStatus (eReturnStatusFailed); + break; + } + } + else + { + result.AppendErrorWithFormat ("invalid load address string '%s'\n", load_addr_cstr); + result.SetStatus (eReturnStatusFailed); + break; + } + } + else + { + if (sect_name) + result.AppendError ("section names must be followed by a load address.\n"); + else + result.AppendError ("one or more section name + load address pair must be specified.\n"); + result.SetStatus (eReturnStatusFailed); + break; + } + } + } + + if (changed) + { + target->ModulesDidLoad (matching_modules); + Process *process = m_exe_ctx.GetProcessPtr(); + if (process) + process->Flush(); + } + } + else + { + module->GetFileSpec().GetPath (path, sizeof(path)); + result.AppendErrorWithFormat ("no sections in object file '%s'\n", path); + result.SetStatus (eReturnStatusFailed); + } + } + else + { + module->GetFileSpec().GetPath (path, sizeof(path)); + result.AppendErrorWithFormat ("no object file for module '%s'\n", path); + result.SetStatus (eReturnStatusFailed); + } + } + else + { + FileSpec *module_spec_file = module_spec.GetFileSpecPtr(); + if (module_spec_file) + { + module_spec_file->GetPath (path, sizeof(path)); + result.AppendErrorWithFormat ("invalid module '%s'.\n", path); + } + else + result.AppendError ("no module spec"); + result.SetStatus (eReturnStatusFailed); + } + } + else + { + std::string uuid_str; + + if (module_spec.GetFileSpec()) + module_spec.GetFileSpec().GetPath (path, sizeof(path)); + else + path[0] = '\0'; + + if (module_spec.GetUUIDPtr()) + uuid_str = module_spec.GetUUID().GetAsString(); + if (num_matches > 1) + { + result.AppendErrorWithFormat ("multiple modules match%s%s%s%s:\n", + path[0] ? " file=" : "", + path, + !uuid_str.empty() ? " uuid=" : "", + uuid_str.c_str()); + for (size_t i=0; iGetFileSpec().GetPath (path, sizeof(path))) + result.AppendMessageWithFormat("%s\n", path); + } + } + else + { + result.AppendErrorWithFormat ("no modules were found that match%s%s%s%s.\n", + path[0] ? " file=" : "", + path, + !uuid_str.empty() ? " uuid=" : "", + uuid_str.c_str()); + } + result.SetStatus (eReturnStatusFailed); + } + } + else + { + result.AppendError ("either the \"--file \" or the \"--uuid \" option must be specified.\n"); + result.SetStatus (eReturnStatusFailed); + return false; + } + } + return result.Succeeded(); + } + + OptionGroupOptions m_option_group; + OptionGroupUUID m_uuid_option_group; + OptionGroupFile m_file_option; + OptionGroupUInt64 m_slide_option; +}; + +//---------------------------------------------------------------------- +// List images with associated information +//---------------------------------------------------------------------- +class CommandObjectTargetModulesList : public CommandObjectParsed +{ +public: + + class CommandOptions : public Options + { + public: + + CommandOptions (CommandInterpreter &interpreter) : + Options(interpreter), + m_format_array(), + m_use_global_module_list (false), + m_module_addr (LLDB_INVALID_ADDRESS) + { + } + + virtual + ~CommandOptions () + { + } + + virtual Error + SetOptionValue (uint32_t option_idx, const char *option_arg) + { + Error error; + + const int short_option = m_getopt_table[option_idx].val; + if (short_option == 'g') + { + m_use_global_module_list = true; + } + else if (short_option == 'a') + { + ExecutionContext exe_ctx (m_interpreter.GetExecutionContext()); + m_module_addr = Args::StringToAddress(&exe_ctx, option_arg, LLDB_INVALID_ADDRESS, &error); + } + else + { + unsigned long width = 0; + if (option_arg) + width = strtoul (option_arg, NULL, 0); + m_format_array.push_back(std::make_pair(short_option, width)); + } + return error; + } + + void + OptionParsingStarting () + { + m_format_array.clear(); + m_use_global_module_list = false; + m_module_addr = LLDB_INVALID_ADDRESS; + } + + const OptionDefinition* + GetDefinitions () + { + return g_option_table; + } + + // Options table: Required for subclasses of Options. + + static OptionDefinition g_option_table[]; + + // Instance variables to hold the values for command options. + typedef std::vector< std::pair > FormatWidthCollection; + FormatWidthCollection m_format_array; + bool m_use_global_module_list; + lldb::addr_t m_module_addr; + }; + + CommandObjectTargetModulesList (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "target modules list", + "List current executable and dependent shared library images.", + "target modules list []"), + m_options (interpreter) + { + } + + virtual + ~CommandObjectTargetModulesList () + { + } + + virtual + Options * + GetOptions () + { + return &m_options; + } + +protected: + virtual bool + DoExecute (Args& command, + CommandReturnObject &result) + { + Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get(); + const bool use_global_module_list = m_options.m_use_global_module_list; + // Define a local module list here to ensure it lives longer than any "locker" + // object which might lock its contents below (through the "module_list_ptr" + // variable). + ModuleList module_list; + if (target == NULL && use_global_module_list == false) + { + result.AppendError ("invalid target, create a debug target using the 'target create' command"); + result.SetStatus (eReturnStatusFailed); + return false; + } + else + { + if (target) + { + uint32_t addr_byte_size = target->GetArchitecture().GetAddressByteSize(); + result.GetOutputStream().SetAddressByteSize(addr_byte_size); + result.GetErrorStream().SetAddressByteSize(addr_byte_size); + } + // Dump all sections for all modules images + Stream &strm = result.GetOutputStream(); + + if (m_options.m_module_addr != LLDB_INVALID_ADDRESS) + { + if (target) + { + Address module_address; + if (module_address.SetLoadAddress(m_options.m_module_addr, target)) + { + ModuleSP module_sp (module_address.GetModule()); + if (module_sp) + { + PrintModule (target, module_sp.get(), 0, strm); + result.SetStatus (eReturnStatusSuccessFinishResult); + } + else + { + result.AppendErrorWithFormat ("Couldn't find module matching address: 0x%" PRIx64 ".", m_options.m_module_addr); + result.SetStatus (eReturnStatusFailed); + } + } + else + { + result.AppendErrorWithFormat ("Couldn't find module containing address: 0x%" PRIx64 ".", m_options.m_module_addr); + result.SetStatus (eReturnStatusFailed); + } + } + else + { + result.AppendError ("Can only look up modules by address with a valid target."); + result.SetStatus (eReturnStatusFailed); + } + return result.Succeeded(); + } + + size_t num_modules = 0; + Mutex::Locker locker; // This locker will be locked on the mutex in module_list_ptr if it is non-NULL. + // Otherwise it will lock the AllocationModuleCollectionMutex when accessing + // the global module list directly. + const ModuleList *module_list_ptr = NULL; + const size_t argc = command.GetArgumentCount(); + if (argc == 0) + { + if (use_global_module_list) + { + locker.Lock (Module::GetAllocationModuleCollectionMutex()); + num_modules = Module::GetNumberAllocatedModules(); + } + else + { + module_list_ptr = &target->GetImages(); + } + } + else + { + for (size_t i=0; iGetMutex()); + num_modules = module_list_ptr->GetSize(); + } + + if (num_modules > 0) + { + for (uint32_t image_idx = 0; image_idxGetModuleAtIndexUnlocked(image_idx); + module = module_sp.get(); + } + else + { + module = Module::GetAllocatedModuleAtIndex(image_idx); + module_sp = module->shared_from_this(); + } + + const size_t indent = strm.Printf("[%3u] ", image_idx); + PrintModule (target, module, indent, strm); + + } + result.SetStatus (eReturnStatusSuccessFinishResult); + } + else + { + if (argc) + { + if (use_global_module_list) + result.AppendError ("the global module list has no matching modules"); + else + result.AppendError ("the target has no matching modules"); + } + else + { + if (use_global_module_list) + result.AppendError ("the global module list is empty"); + else + result.AppendError ("the target has no associated executable images"); + } + result.SetStatus (eReturnStatusFailed); + return false; + } + } + return result.Succeeded(); + } + + void + PrintModule (Target *target, Module *module, int indent, Stream &strm) + { + + if (module == NULL) + { + strm.PutCString("Null module"); + return; + } + + bool dump_object_name = false; + if (m_options.m_format_array.empty()) + { + m_options.m_format_array.push_back(std::make_pair('u', 0)); + m_options.m_format_array.push_back(std::make_pair('h', 0)); + m_options.m_format_array.push_back(std::make_pair('f', 0)); + m_options.m_format_array.push_back(std::make_pair('S', 0)); + } + const size_t num_entries = m_options.m_format_array.size(); + bool print_space = false; + for (size_t i=0; iGetFileSpec(), width); + dump_object_name = true; + break; + + case 'd': + DumpDirectory (strm, &module->GetFileSpec(), width); + break; + + case 'b': + DumpBasename (strm, &module->GetFileSpec(), width); + dump_object_name = true; + break; + + case 'h': + case 'o': + // Image header address + { + uint32_t addr_nibble_width = target ? (target->GetArchitecture().GetAddressByteSize() * 2) : 16; + + ObjectFile *objfile = module->GetObjectFile (); + if (objfile) + { + Address header_addr(objfile->GetHeaderAddress()); + if (header_addr.IsValid()) + { + if (target && !target->GetSectionLoadList().IsEmpty()) + { + lldb::addr_t header_load_addr = header_addr.GetLoadAddress (target); + if (header_load_addr == LLDB_INVALID_ADDRESS) + { + header_addr.Dump (&strm, target, Address::DumpStyleModuleWithFileAddress, Address::DumpStyleFileAddress); + } + else + { + if (format_char == 'o') + { + // Show the offset of slide for the image + strm.Printf ("0x%*.*" PRIx64, addr_nibble_width, addr_nibble_width, header_load_addr - header_addr.GetFileAddress()); + } + else + { + // Show the load address of the image + strm.Printf ("0x%*.*" PRIx64, addr_nibble_width, addr_nibble_width, header_load_addr); + } + } + break; + } + // The address was valid, but the image isn't loaded, output the address in an appropriate format + header_addr.Dump (&strm, target, Address::DumpStyleFileAddress); + break; + } + } + strm.Printf ("%*s", addr_nibble_width + 2, ""); + } + break; + case 'r': + { + size_t ref_count = 0; + ModuleSP module_sp (module->shared_from_this()); + if (module_sp) + { + // Take one away to make sure we don't count our local "module_sp" + ref_count = module_sp.use_count() - 1; + } + if (width) + strm.Printf("{%*zu}", width, ref_count); + else + strm.Printf("{%zu}", ref_count); + } + break; + + case 's': + case 'S': + { + SymbolVendor *symbol_vendor = module->GetSymbolVendor(); + if (symbol_vendor) + { + SymbolFile *symbol_file = symbol_vendor->GetSymbolFile(); + if (symbol_file) + { + if (format_char == 'S') + { + FileSpec &symfile_spec = symbol_file->GetObjectFile()->GetFileSpec(); + // Dump symbol file only if different from module file + if (!symfile_spec || symfile_spec == module->GetFileSpec()) + { + print_space = false; + break; + } + // Add a newline and indent past the index + strm.Printf ("\n%*s", indent, ""); + } + DumpFullpath (strm, &symbol_file->GetObjectFile()->GetFileSpec(), width); + dump_object_name = true; + break; + } + } + strm.Printf("%.*s", width, ""); + } + break; + + case 'm': + module->GetModificationTime().Dump(&strm, width); + break; + + case 'p': + strm.Printf("%p", module); + break; + + case 'u': + DumpModuleUUID(strm, module); + break; + + default: + break; + } + + } + if (dump_object_name) + { + const char *object_name = module->GetObjectName().GetCString(); + if (object_name) + strm.Printf ("(%s)", object_name); + } + strm.EOL(); + } + + CommandOptions m_options; +}; + +OptionDefinition +CommandObjectTargetModulesList::CommandOptions::g_option_table[] = +{ + { LLDB_OPT_SET_1, false, "address", 'a', required_argument, NULL, 0, eArgTypeAddressOrExpression, "Display the image at this address."}, + { LLDB_OPT_SET_1, false, "arch", 'A', optional_argument, NULL, 0, eArgTypeWidth, "Display the architecture when listing images."}, + { LLDB_OPT_SET_1, false, "triple", 't', optional_argument, NULL, 0, eArgTypeWidth, "Display the triple when listing images."}, + { LLDB_OPT_SET_1, false, "header", 'h', no_argument, NULL, 0, eArgTypeNone, "Display the image header address as a load address if debugging, a file address otherwise."}, + { LLDB_OPT_SET_1, false, "offset", 'o', no_argument, NULL, 0, eArgTypeNone, "Display the image header address offset from the header file address (the slide amount)."}, + { LLDB_OPT_SET_1, false, "uuid", 'u', no_argument, NULL, 0, eArgTypeNone, "Display the UUID when listing images."}, + { LLDB_OPT_SET_1, false, "fullpath", 'f', optional_argument, NULL, 0, eArgTypeWidth, "Display the fullpath to the image object file."}, + { LLDB_OPT_SET_1, false, "directory", 'd', optional_argument, NULL, 0, eArgTypeWidth, "Display the directory with optional width for the image object file."}, + { LLDB_OPT_SET_1, false, "basename", 'b', optional_argument, NULL, 0, eArgTypeWidth, "Display the basename with optional width for the image object file."}, + { LLDB_OPT_SET_1, false, "symfile", 's', optional_argument, NULL, 0, eArgTypeWidth, "Display the fullpath to the image symbol file with optional width."}, + { LLDB_OPT_SET_1, false, "symfile-unique", 'S', optional_argument, NULL, 0, eArgTypeWidth, "Display the symbol file with optional width only if it is different from the executable object file."}, + { LLDB_OPT_SET_1, false, "mod-time", 'm', optional_argument, NULL, 0, eArgTypeWidth, "Display the modification time with optional width of the module."}, + { LLDB_OPT_SET_1, false, "ref-count", 'r', optional_argument, NULL, 0, eArgTypeWidth, "Display the reference count if the module is still in the shared module cache."}, + { LLDB_OPT_SET_1, false, "pointer", 'p', optional_argument, NULL, 0, eArgTypeNone, "Display the module pointer."}, + { LLDB_OPT_SET_1, false, "global", 'g', no_argument, NULL, 0, eArgTypeNone, "Display the modules from the global module list, not just the current target."}, + { 0, false, NULL, 0, 0, NULL, 0, eArgTypeNone, NULL } +}; + +#pragma mark CommandObjectTargetModulesShowUnwind + +//---------------------------------------------------------------------- +// Lookup unwind information in images +//---------------------------------------------------------------------- + +class CommandObjectTargetModulesShowUnwind : public CommandObjectParsed +{ +public: + + enum + { + eLookupTypeInvalid = -1, + eLookupTypeAddress = 0, + eLookupTypeSymbol, + eLookupTypeFunction, + eLookupTypeFunctionOrSymbol, + kNumLookupTypes + }; + + class CommandOptions : public Options + { + public: + + CommandOptions (CommandInterpreter &interpreter) : + Options(interpreter), + m_type(eLookupTypeInvalid), + m_str(), + m_addr(LLDB_INVALID_ADDRESS) + { + } + + virtual + ~CommandOptions () + { + } + + virtual Error + SetOptionValue (uint32_t option_idx, const char *option_arg) + { + Error error; + + const int short_option = m_getopt_table[option_idx].val; + + switch (short_option) + { + case 'a': + { + ExecutionContext exe_ctx (m_interpreter.GetExecutionContext()); + m_type = eLookupTypeAddress; + m_addr = Args::StringToAddress(&exe_ctx, option_arg, LLDB_INVALID_ADDRESS, &error); + if (m_addr == LLDB_INVALID_ADDRESS) + error.SetErrorStringWithFormat ("invalid address string '%s'", option_arg); + break; + } + + case 'n': + { + m_str = option_arg; + m_type = eLookupTypeFunctionOrSymbol; + break; + } + } + + return error; + } + + void + OptionParsingStarting () + { + m_type = eLookupTypeInvalid; + m_str.clear(); + m_addr = LLDB_INVALID_ADDRESS; + } + + const OptionDefinition* + GetDefinitions () + { + return g_option_table; + } + + // Options table: Required for subclasses of Options. + + static OptionDefinition g_option_table[]; + + // Instance variables to hold the values for command options. + + int m_type; // Should be a eLookupTypeXXX enum after parsing options + std::string m_str; // Holds name lookup + lldb::addr_t m_addr; // Holds the address to lookup + }; + + CommandObjectTargetModulesShowUnwind (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "target modules show-unwind", + "Show synthesized unwind instructions for a function.", + NULL, + eFlagRequiresTarget | + eFlagRequiresProcess | + eFlagProcessMustBeLaunched | + eFlagProcessMustBePaused ), + m_options (interpreter) + { + } + + virtual + ~CommandObjectTargetModulesShowUnwind () + { + } + + virtual + Options * + GetOptions () + { + return &m_options; + } + +protected: + bool + DoExecute (Args& command, + CommandReturnObject &result) + { + Target *target = m_exe_ctx.GetTargetPtr(); + Process *process = m_exe_ctx.GetProcessPtr(); + ABI *abi = NULL; + if (process) + abi = process->GetABI().get(); + + if (process == NULL) + { + result.AppendError ("You must have a process running to use this command."); + result.SetStatus (eReturnStatusFailed); + return false; + } + + ThreadList threads(process->GetThreadList()); + if (threads.GetSize() == 0) + { + result.AppendError ("The process must be paused to use this command."); + result.SetStatus (eReturnStatusFailed); + return false; + } + + ThreadSP thread(threads.GetThreadAtIndex(0)); + if (thread.get() == NULL) + { + result.AppendError ("The process must be paused to use this command."); + result.SetStatus (eReturnStatusFailed); + return false; + } + + SymbolContextList sc_list; + + if (m_options.m_type == eLookupTypeFunctionOrSymbol) + { + ConstString function_name (m_options.m_str.c_str()); + target->GetImages().FindFunctions (function_name, eFunctionNameTypeAuto, true, false, true, sc_list); + } + else if (m_options.m_type == eLookupTypeAddress && target) + { + Address addr; + if (target->GetSectionLoadList().ResolveLoadAddress (m_options.m_addr, addr)) + { + SymbolContext sc; + ModuleSP module_sp (addr.GetModule()); + module_sp->ResolveSymbolContextForAddress (addr, eSymbolContextEverything, sc); + if (sc.function || sc.symbol) + { + sc_list.Append(sc); + } + } + } + + size_t num_matches = sc_list.GetSize(); + for (uint32_t idx = 0; idx < num_matches; idx++) + { + SymbolContext sc; + sc_list.GetContextAtIndex(idx, sc); + if (sc.symbol == NULL && sc.function == NULL) + continue; + if (sc.module_sp.get() == NULL || sc.module_sp->GetObjectFile() == NULL) + continue; + AddressRange range; + if (!sc.GetAddressRange (eSymbolContextFunction | eSymbolContextSymbol, 0, false, range)) + continue; + if (!range.GetBaseAddress().IsValid()) + continue; + ConstString funcname(sc.GetFunctionName()); + if (funcname.IsEmpty()) + continue; + addr_t start_addr = range.GetBaseAddress().GetLoadAddress(target); + if (abi) + start_addr = abi->FixCodeAddress(start_addr); + + FuncUnwindersSP func_unwinders_sp (sc.module_sp->GetObjectFile()->GetUnwindTable().GetUncachedFuncUnwindersContainingAddress(start_addr, sc)); + if (func_unwinders_sp.get() == NULL) + continue; + + Address first_non_prologue_insn (func_unwinders_sp->GetFirstNonPrologueInsn(*target)); + if (first_non_prologue_insn.IsValid()) + { + result.GetOutputStream().Printf("First non-prologue instruction is at address 0x%" PRIx64 " or offset %" PRId64 " into the function.\n", first_non_prologue_insn.GetLoadAddress(target), first_non_prologue_insn.GetLoadAddress(target) - start_addr); + result.GetOutputStream().Printf ("\n"); + } + + UnwindPlanSP non_callsite_unwind_plan = func_unwinders_sp->GetUnwindPlanAtNonCallSite(*thread.get()); + if (non_callsite_unwind_plan.get()) + { + result.GetOutputStream().Printf("Asynchronous (not restricted to call-sites) UnwindPlan for %s`%s (start addr 0x%" PRIx64 "):\n", sc.module_sp->GetPlatformFileSpec().GetFilename().AsCString(), funcname.AsCString(), start_addr); + non_callsite_unwind_plan->Dump(result.GetOutputStream(), thread.get(), LLDB_INVALID_ADDRESS); + result.GetOutputStream().Printf ("\n"); + } + + UnwindPlanSP callsite_unwind_plan = func_unwinders_sp->GetUnwindPlanAtCallSite(-1); + if (callsite_unwind_plan.get()) + { + result.GetOutputStream().Printf("Synchronous (restricted to call-sites) UnwindPlan for %s`%s (start addr 0x%" PRIx64 "):\n", sc.module_sp->GetPlatformFileSpec().GetFilename().AsCString(), funcname.AsCString(), start_addr); + callsite_unwind_plan->Dump(result.GetOutputStream(), thread.get(), LLDB_INVALID_ADDRESS); + result.GetOutputStream().Printf ("\n"); + } + + UnwindPlanSP arch_default_unwind_plan = func_unwinders_sp->GetUnwindPlanArchitectureDefault(*thread.get()); + if (arch_default_unwind_plan.get()) + { + result.GetOutputStream().Printf("Architecture default UnwindPlan for %s`%s (start addr 0x%" PRIx64 "):\n", sc.module_sp->GetPlatformFileSpec().GetFilename().AsCString(), funcname.AsCString(), start_addr); + arch_default_unwind_plan->Dump(result.GetOutputStream(), thread.get(), LLDB_INVALID_ADDRESS); + result.GetOutputStream().Printf ("\n"); + } + + UnwindPlanSP fast_unwind_plan = func_unwinders_sp->GetUnwindPlanFastUnwind(*thread.get()); + if (fast_unwind_plan.get()) + { + result.GetOutputStream().Printf("Fast UnwindPlan for %s`%s (start addr 0x%" PRIx64 "):\n", sc.module_sp->GetPlatformFileSpec().GetFilename().AsCString(), funcname.AsCString(), start_addr); + fast_unwind_plan->Dump(result.GetOutputStream(), thread.get(), LLDB_INVALID_ADDRESS); + result.GetOutputStream().Printf ("\n"); + } + + + result.GetOutputStream().Printf ("\n"); + } + return result.Succeeded(); + } + + CommandOptions m_options; +}; + +OptionDefinition +CommandObjectTargetModulesShowUnwind::CommandOptions::g_option_table[] = +{ + { LLDB_OPT_SET_1, false, "name", 'n', required_argument, NULL, 0, eArgTypeFunctionName, "Show unwind instructions for a function or symbol name."}, + { LLDB_OPT_SET_2, false, "address", 'a', required_argument, NULL, 0, eArgTypeAddressOrExpression, "Show unwind instructions for a function or symbol containing an address"}, + { 0, false, NULL, 0, 0, NULL, 0, eArgTypeNone, NULL } +}; + +//---------------------------------------------------------------------- +// Lookup information in images +//---------------------------------------------------------------------- +class CommandObjectTargetModulesLookup : public CommandObjectParsed +{ +public: + + enum + { + eLookupTypeInvalid = -1, + eLookupTypeAddress = 0, + eLookupTypeSymbol, + eLookupTypeFileLine, // Line is optional + eLookupTypeFunction, + eLookupTypeFunctionOrSymbol, + eLookupTypeType, + kNumLookupTypes + }; + + class CommandOptions : public Options + { + public: + + CommandOptions (CommandInterpreter &interpreter) : + Options(interpreter) + { + OptionParsingStarting(); + } + + virtual + ~CommandOptions () + { + } + + virtual Error + SetOptionValue (uint32_t option_idx, const char *option_arg) + { + Error error; + + const int short_option = m_getopt_table[option_idx].val; + + switch (short_option) + { + case 'a': + { + m_type = eLookupTypeAddress; + ExecutionContext exe_ctx (m_interpreter.GetExecutionContext()); + m_addr = Args::StringToAddress(&exe_ctx, option_arg, LLDB_INVALID_ADDRESS, &error); + } + break; + + case 'o': + m_offset = Args::StringToUInt64(option_arg, LLDB_INVALID_ADDRESS); + if (m_offset == LLDB_INVALID_ADDRESS) + error.SetErrorStringWithFormat ("invalid offset string '%s'", option_arg); + break; + + case 's': + m_str = option_arg; + m_type = eLookupTypeSymbol; + break; + + case 'f': + m_file.SetFile (option_arg, false); + m_type = eLookupTypeFileLine; + break; + + case 'i': + m_include_inlines = false; + break; + + case 'l': + m_line_number = Args::StringToUInt32(option_arg, UINT32_MAX); + if (m_line_number == UINT32_MAX) + error.SetErrorStringWithFormat ("invalid line number string '%s'", option_arg); + else if (m_line_number == 0) + error.SetErrorString ("zero is an invalid line number"); + m_type = eLookupTypeFileLine; + break; + + case 'F': + m_str = option_arg; + m_type = eLookupTypeFunction; + break; + + case 'n': + m_str = option_arg; + m_type = eLookupTypeFunctionOrSymbol; + break; + + case 't': + m_str = option_arg; + m_type = eLookupTypeType; + break; + + case 'v': + m_verbose = 1; + break; + + case 'A': + m_print_all = true; + break; + + case 'r': + m_use_regex = true; + break; + } + + return error; + } + + void + OptionParsingStarting () + { + m_type = eLookupTypeInvalid; + m_str.clear(); + m_file.Clear(); + m_addr = LLDB_INVALID_ADDRESS; + m_offset = 0; + m_line_number = 0; + m_use_regex = false; + m_include_inlines = true; + m_verbose = false; + m_print_all = false; + } + + const OptionDefinition* + GetDefinitions () + { + return g_option_table; + } + + // Options table: Required for subclasses of Options. + + static OptionDefinition g_option_table[]; + int m_type; // Should be a eLookupTypeXXX enum after parsing options + std::string m_str; // Holds name lookup + FileSpec m_file; // Files for file lookups + lldb::addr_t m_addr; // Holds the address to lookup + lldb::addr_t m_offset; // Subtract this offset from m_addr before doing lookups. + uint32_t m_line_number; // Line number for file+line lookups + bool m_use_regex; // Name lookups in m_str are regular expressions. + bool m_include_inlines;// Check for inline entries when looking up by file/line. + bool m_verbose; // Enable verbose lookup info + bool m_print_all; // Print all matches, even in cases where there's a best match. + + }; + + CommandObjectTargetModulesLookup (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "target modules lookup", + "Look up information within executable and dependent shared library images.", + NULL, + eFlagRequiresTarget), + m_options (interpreter) + { + CommandArgumentEntry arg; + CommandArgumentData file_arg; + + // Define the first (and only) variant of this arg. + file_arg.arg_type = eArgTypeFilename; + file_arg.arg_repetition = eArgRepeatStar; + + // There is only one variant this argument could be; put it into the argument entry. + arg.push_back (file_arg); + + // Push the data for the first argument into the m_arguments vector. + m_arguments.push_back (arg); + } + + virtual + ~CommandObjectTargetModulesLookup () + { + } + + virtual Options * + GetOptions () + { + return &m_options; + } + + bool + LookupHere (CommandInterpreter &interpreter, CommandReturnObject &result, bool &syntax_error) + { + switch (m_options.m_type) + { + case eLookupTypeAddress: + case eLookupTypeFileLine: + case eLookupTypeFunction: + case eLookupTypeFunctionOrSymbol: + case eLookupTypeSymbol: + default: + return false; + case eLookupTypeType: + break; + } + + StackFrameSP frame = m_exe_ctx.GetFrameSP(); + + if (!frame) + return false; + + const SymbolContext &sym_ctx(frame->GetSymbolContext(eSymbolContextModule)); + + if (!sym_ctx.module_sp) + return false; + + switch (m_options.m_type) + { + default: + return false; + case eLookupTypeType: + if (!m_options.m_str.empty()) + { + if (LookupTypeHere (m_interpreter, + result.GetOutputStream(), + sym_ctx, + m_options.m_str.c_str(), + m_options.m_use_regex)) + { + result.SetStatus(eReturnStatusSuccessFinishResult); + return true; + } + } + break; + } + + return true; + } + + bool + LookupInModule (CommandInterpreter &interpreter, Module *module, CommandReturnObject &result, bool &syntax_error) + { + switch (m_options.m_type) + { + case eLookupTypeAddress: + if (m_options.m_addr != LLDB_INVALID_ADDRESS) + { + if (LookupAddressInModule (m_interpreter, + result.GetOutputStream(), + module, + eSymbolContextEverything, + m_options.m_addr, + m_options.m_offset, + m_options.m_verbose)) + { + result.SetStatus(eReturnStatusSuccessFinishResult); + return true; + } + } + break; + + case eLookupTypeSymbol: + if (!m_options.m_str.empty()) + { + if (LookupSymbolInModule (m_interpreter, + result.GetOutputStream(), + module, + m_options.m_str.c_str(), + m_options.m_use_regex, + m_options.m_verbose)) + { + result.SetStatus(eReturnStatusSuccessFinishResult); + return true; + } + } + break; + + case eLookupTypeFileLine: + if (m_options.m_file) + { + + if (LookupFileAndLineInModule (m_interpreter, + result.GetOutputStream(), + module, + m_options.m_file, + m_options.m_line_number, + m_options.m_include_inlines, + m_options.m_verbose)) + { + result.SetStatus(eReturnStatusSuccessFinishResult); + return true; + } + } + break; + + case eLookupTypeFunctionOrSymbol: + case eLookupTypeFunction: + if (!m_options.m_str.empty()) + { + if (LookupFunctionInModule (m_interpreter, + result.GetOutputStream(), + module, + m_options.m_str.c_str(), + m_options.m_use_regex, + m_options.m_include_inlines, + m_options.m_type == eLookupTypeFunctionOrSymbol, // include symbols + m_options.m_verbose)) + { + result.SetStatus(eReturnStatusSuccessFinishResult); + return true; + } + } + break; + + + case eLookupTypeType: + if (!m_options.m_str.empty()) + { + if (LookupTypeInModule (m_interpreter, + result.GetOutputStream(), + module, + m_options.m_str.c_str(), + m_options.m_use_regex)) + { + result.SetStatus(eReturnStatusSuccessFinishResult); + return true; + } + } + break; + + default: + m_options.GenerateOptionUsage (result.GetErrorStream(), this); + syntax_error = true; + break; + } + + result.SetStatus (eReturnStatusFailed); + return false; + } + +protected: + virtual bool + DoExecute (Args& command, + CommandReturnObject &result) + { + Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get(); + if (target == NULL) + { + result.AppendError ("invalid target, create a debug target using the 'target create' command"); + result.SetStatus (eReturnStatusFailed); + return false; + } + else + { + bool syntax_error = false; + uint32_t i; + uint32_t num_successful_lookups = 0; + uint32_t addr_byte_size = target->GetArchitecture().GetAddressByteSize(); + result.GetOutputStream().SetAddressByteSize(addr_byte_size); + result.GetErrorStream().SetAddressByteSize(addr_byte_size); + // Dump all sections for all modules images + + if (command.GetArgumentCount() == 0) + { + ModuleSP current_module; + + // Where it is possible to look in the current symbol context + // first, try that. If this search was successful and --all + // was not passed, don't print anything else. + if (LookupHere (m_interpreter, result, syntax_error)) + { + result.GetOutputStream().EOL(); + num_successful_lookups++; + if (!m_options.m_print_all) + { + result.SetStatus (eReturnStatusSuccessFinishResult); + return result.Succeeded(); + } + } + + // Dump all sections for all other modules + + const ModuleList &target_modules = target->GetImages(); + Mutex::Locker modules_locker(target_modules.GetMutex()); + const size_t num_modules = target_modules.GetSize(); + if (num_modules > 0) + { + for (i = 0; i 0) + { + for (size_t j=0; j 0) + result.SetStatus (eReturnStatusSuccessFinishResult); + else + result.SetStatus (eReturnStatusFailed); + } + return result.Succeeded(); + } + + CommandOptions m_options; +}; + +OptionDefinition +CommandObjectTargetModulesLookup::CommandOptions::g_option_table[] = +{ + { LLDB_OPT_SET_1, true, "address", 'a', required_argument, NULL, 0, eArgTypeAddressOrExpression, "Lookup an address in one or more target modules."}, + { LLDB_OPT_SET_1, false, "offset", 'o', required_argument, NULL, 0, eArgTypeOffset, "When looking up an address subtract from any addresses before doing the lookup."}, + { LLDB_OPT_SET_2| LLDB_OPT_SET_4 | LLDB_OPT_SET_5 + /* FIXME: re-enable this for types when the LookupTypeInModule actually uses the regex option: | LLDB_OPT_SET_6 */ , + false, "regex", 'r', no_argument, NULL, 0, eArgTypeNone, "The argument for name lookups are regular expressions."}, + { LLDB_OPT_SET_2, true, "symbol", 's', required_argument, NULL, 0, eArgTypeSymbol, "Lookup a symbol by name in the symbol tables in one or more target modules."}, + { LLDB_OPT_SET_3, true, "file", 'f', required_argument, NULL, 0, eArgTypeFilename, "Lookup a file by fullpath or basename in one or more target modules."}, + { LLDB_OPT_SET_3, false, "line", 'l', required_argument, NULL, 0, eArgTypeLineNum, "Lookup a line number in a file (must be used in conjunction with --file)."}, + { LLDB_OPT_SET_FROM_TO(3,5), + false, "no-inlines", 'i', no_argument, NULL, 0, eArgTypeNone, "Ignore inline entries (must be used in conjunction with --file or --function)."}, + { LLDB_OPT_SET_4, true, "function", 'F', required_argument, NULL, 0, eArgTypeFunctionName, "Lookup a function by name in the debug symbols in one or more target modules."}, + { LLDB_OPT_SET_5, true, "name", 'n', required_argument, NULL, 0, eArgTypeFunctionOrSymbol, "Lookup a function or symbol by name in one or more target modules."}, + { LLDB_OPT_SET_6, true, "type", 't', required_argument, NULL, 0, eArgTypeName, "Lookup a type by name in the debug symbols in one or more target modules."}, + { LLDB_OPT_SET_ALL, false, "verbose", 'v', no_argument, NULL, 0, eArgTypeNone, "Enable verbose lookup information."}, + { LLDB_OPT_SET_ALL, false, "all", 'A', no_argument, NULL, 0, eArgTypeNone, "Print all matches, not just the best match, if a best match is available."}, + { 0, false, NULL, 0, 0, NULL, 0, eArgTypeNone, NULL } +}; + + +#pragma mark CommandObjectMultiwordImageSearchPaths + +//------------------------------------------------------------------------- +// CommandObjectMultiwordImageSearchPaths +//------------------------------------------------------------------------- + +class CommandObjectTargetModulesImageSearchPaths : public CommandObjectMultiword +{ +public: + + CommandObjectTargetModulesImageSearchPaths (CommandInterpreter &interpreter) : + CommandObjectMultiword (interpreter, + "target modules search-paths", + "A set of commands for operating on debugger target image search paths.", + "target modules search-paths []") + { + LoadSubCommand ("add", CommandObjectSP (new CommandObjectTargetModulesSearchPathsAdd (interpreter))); + LoadSubCommand ("clear", CommandObjectSP (new CommandObjectTargetModulesSearchPathsClear (interpreter))); + LoadSubCommand ("insert", CommandObjectSP (new CommandObjectTargetModulesSearchPathsInsert (interpreter))); + LoadSubCommand ("list", CommandObjectSP (new CommandObjectTargetModulesSearchPathsList (interpreter))); + LoadSubCommand ("query", CommandObjectSP (new CommandObjectTargetModulesSearchPathsQuery (interpreter))); + } + + ~CommandObjectTargetModulesImageSearchPaths() + { + } +}; + + + +#pragma mark CommandObjectTargetModules + +//------------------------------------------------------------------------- +// CommandObjectTargetModules +//------------------------------------------------------------------------- + +class CommandObjectTargetModules : public CommandObjectMultiword +{ +public: + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + CommandObjectTargetModules(CommandInterpreter &interpreter) : + CommandObjectMultiword (interpreter, + "target modules", + "A set of commands for accessing information for one or more target modules.", + "target modules ...") + { + LoadSubCommand ("add", CommandObjectSP (new CommandObjectTargetModulesAdd (interpreter))); + LoadSubCommand ("load", CommandObjectSP (new CommandObjectTargetModulesLoad (interpreter))); + LoadSubCommand ("dump", CommandObjectSP (new CommandObjectTargetModulesDump (interpreter))); + LoadSubCommand ("list", CommandObjectSP (new CommandObjectTargetModulesList (interpreter))); + LoadSubCommand ("lookup", CommandObjectSP (new CommandObjectTargetModulesLookup (interpreter))); + LoadSubCommand ("search-paths", CommandObjectSP (new CommandObjectTargetModulesImageSearchPaths (interpreter))); + LoadSubCommand ("show-unwind", CommandObjectSP (new CommandObjectTargetModulesShowUnwind (interpreter))); + + } + virtual + ~CommandObjectTargetModules() + { + } + +private: + //------------------------------------------------------------------ + // For CommandObjectTargetModules only + //------------------------------------------------------------------ + DISALLOW_COPY_AND_ASSIGN (CommandObjectTargetModules); +}; + + + +class CommandObjectTargetSymbolsAdd : public CommandObjectParsed +{ +public: + CommandObjectTargetSymbolsAdd (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "target symbols add", + "Add a debug symbol file to one of the target's current modules by specifying a path to a debug symbols file, or using the options to specify a module to download symbols for.", + "target symbols add []", eFlagRequiresTarget), + m_option_group (interpreter), + m_file_option (LLDB_OPT_SET_1, false, "shlib", 's', CommandCompletions::eModuleCompletion, eArgTypeShlibName, "Fullpath or basename for module to find debug symbols for."), + m_current_frame_option (LLDB_OPT_SET_2, false, "frame", 'F', "Locate the debug symbols the currently selected frame.", false, true) + + { + m_option_group.Append (&m_uuid_option_group, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1); + m_option_group.Append (&m_file_option, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1); + m_option_group.Append (&m_current_frame_option, LLDB_OPT_SET_2, LLDB_OPT_SET_2); + m_option_group.Finalize(); + } + + virtual + ~CommandObjectTargetSymbolsAdd () + { + } + + virtual int + HandleArgumentCompletion (Args &input, + int &cursor_index, + int &cursor_char_position, + OptionElementVector &opt_element_vector, + int match_start_point, + int max_return_elements, + bool &word_complete, + StringList &matches) + { + std::string completion_str (input.GetArgumentAtIndex(cursor_index)); + completion_str.erase (cursor_char_position); + + CommandCompletions::InvokeCommonCompletionCallbacks (m_interpreter, + CommandCompletions::eDiskFileCompletion, + completion_str.c_str(), + match_start_point, + max_return_elements, + NULL, + word_complete, + matches); + return matches.GetSize(); + } + + virtual Options * + GetOptions () + { + return &m_option_group; + } + + +protected: + + bool + AddModuleSymbols (Target *target, + ModuleSpec &module_spec, + bool &flush, + CommandReturnObject &result) + { + const FileSpec &symbol_fspec = module_spec.GetSymbolFileSpec(); + if (symbol_fspec) + { + char symfile_path[PATH_MAX]; + symbol_fspec.GetPath (symfile_path, sizeof(symfile_path)); + + if (!module_spec.GetUUID().IsValid()) + { + if (!module_spec.GetFileSpec() && !module_spec.GetPlatformFileSpec()) + module_spec.GetFileSpec().GetFilename() = symbol_fspec.GetFilename(); + } + // We now have a module that represents a symbol file + // that can be used for a module that might exist in the + // current target, so we need to find that module in the + // target + ModuleList matching_module_list; + + size_t num_matches = 0; + // First extract all module specs from the symbol file + lldb_private::ModuleSpecList symfile_module_specs; + if (ObjectFile::GetModuleSpecifications(module_spec.GetSymbolFileSpec(), 0, 0, symfile_module_specs)) + { + // Now extract the module spec that matches the target architecture + ModuleSpec target_arch_module_spec; + ModuleSpec symfile_module_spec; + target_arch_module_spec.GetArchitecture() = target->GetArchitecture(); + if (symfile_module_specs.FindMatchingModuleSpec(target_arch_module_spec, symfile_module_spec)) + { + // See if it has a UUID? + if (symfile_module_spec.GetUUID().IsValid()) + { + // It has a UUID, look for this UUID in the target modules + ModuleSpec symfile_uuid_module_spec; + symfile_uuid_module_spec.GetUUID() = symfile_module_spec.GetUUID(); + num_matches = target->GetImages().FindModules (symfile_uuid_module_spec, matching_module_list); + } + } + + if (num_matches == 0) + { + // No matches yet, iterate through the module specs to find a UUID value that + // we can match up to an image in our target + const size_t num_symfile_module_specs = symfile_module_specs.GetSize(); + for (size_t i=0; iGetImages().FindModules (symfile_uuid_module_spec, matching_module_list); + } + } + } + } + } + + // Just try to match up the file by basename if we have no matches at this point + if (num_matches == 0) + num_matches = target->GetImages().FindModules (module_spec, matching_module_list); + + while (num_matches == 0) + { + ConstString filename_no_extension(module_spec.GetFileSpec().GetFileNameStrippingExtension()); + // Empty string returned, lets bail + if (!filename_no_extension) + break; + + // Check if there was no extension to strip and the basename is the same + if (filename_no_extension == module_spec.GetFileSpec().GetFilename()) + break; + + // Replace basename with one less extension + module_spec.GetFileSpec().GetFilename() = filename_no_extension; + + num_matches = target->GetImages().FindModules (module_spec, matching_module_list); + + } + + if (num_matches > 1) + { + result.AppendErrorWithFormat ("multiple modules match symbol file '%s', use the --uuid option to resolve the ambiguity.\n", symfile_path); + } + else if (num_matches == 1) + { + ModuleSP module_sp (matching_module_list.GetModuleAtIndex(0)); + + // The module has not yet created its symbol vendor, we can just + // give the existing target module the symfile path to use for + // when it decides to create it! + module_sp->SetSymbolFileFileSpec (symbol_fspec); + + SymbolVendor *symbol_vendor = module_sp->GetSymbolVendor(true, &result.GetErrorStream()); + if (symbol_vendor) + { + SymbolFile *symbol_file = symbol_vendor->GetSymbolFile(); + + if (symbol_file) + { + ObjectFile *object_file = symbol_file->GetObjectFile(); + + if (object_file && object_file->GetFileSpec() == symbol_fspec) + { + // Provide feedback that the symfile has been successfully added. + const FileSpec &module_fs = module_sp->GetFileSpec(); + result.AppendMessageWithFormat("symbol file '%s' has been added to '%s'\n", + symfile_path, + module_fs.GetPath().c_str()); + + // Let clients know something changed in the module + // if it is currently loaded + ModuleList module_list; + module_list.Append (module_sp); + target->SymbolsDidLoad (module_list); + + // Make sure we load any scripting resources that may be embedded + // in the debug info files in case the platform supports that. + Error error; + StreamString feedback_stream; + module_sp->LoadScriptingResourceInTarget (target, error,&feedback_stream); + if (error.Fail() && error.AsCString()) + result.AppendWarningWithFormat("unable to load scripting data for module %s - error reported was %s", + module_sp->GetFileSpec().GetFileNameStrippingExtension().GetCString(), + error.AsCString()); + else if (feedback_stream.GetSize()) + result.AppendWarningWithFormat("%s",feedback_stream.GetData()); + + flush = true; + result.SetStatus (eReturnStatusSuccessFinishResult); + return true; + } + } + } + // Clear the symbol file spec if anything went wrong + module_sp->SetSymbolFileFileSpec (FileSpec()); + } + + if (module_spec.GetUUID().IsValid()) + { + StreamString ss_symfile_uuid; + module_spec.GetUUID().Dump(&ss_symfile_uuid); + result.AppendErrorWithFormat ("symbol file '%s' (%s) does not match any existing module%s\n", + symfile_path, + ss_symfile_uuid.GetData(), + (symbol_fspec.GetFileType() != FileSpec::eFileTypeRegular) + ? "\n please specify the full path to the symbol file" + : ""); + } + else + { + result.AppendErrorWithFormat ("symbol file '%s' does not match any existing module%s\n", + symfile_path, + (symbol_fspec.GetFileType() != FileSpec::eFileTypeRegular) + ? "\n please specify the full path to the symbol file" + : ""); + } + } + else + { + result.AppendError ("one or more executable image paths must be specified"); + } + result.SetStatus (eReturnStatusFailed); + return false; + } + + virtual bool + DoExecute (Args& args, + CommandReturnObject &result) + { + Target *target = m_exe_ctx.GetTargetPtr(); + result.SetStatus (eReturnStatusFailed); + bool flush = false; + ModuleSpec module_spec; + const bool uuid_option_set = m_uuid_option_group.GetOptionValue().OptionWasSet(); + const bool file_option_set = m_file_option.GetOptionValue().OptionWasSet(); + const bool frame_option_set = m_current_frame_option.GetOptionValue().OptionWasSet(); + + const size_t argc = args.GetArgumentCount(); + if (argc == 0) + { + if (uuid_option_set || file_option_set || frame_option_set) + { + bool success = false; + bool error_set = false; + if (frame_option_set) + { + Process *process = m_exe_ctx.GetProcessPtr(); + if (process) + { + const StateType process_state = process->GetState(); + if (StateIsStoppedState (process_state, true)) + { + StackFrame *frame = m_exe_ctx.GetFramePtr(); + if (frame) + { + ModuleSP frame_module_sp (frame->GetSymbolContext(eSymbolContextModule).module_sp); + if (frame_module_sp) + { + if (frame_module_sp->GetPlatformFileSpec().Exists()) + { + module_spec.GetArchitecture() = frame_module_sp->GetArchitecture(); + module_spec.GetFileSpec() = frame_module_sp->GetPlatformFileSpec(); + } + module_spec.GetUUID() = frame_module_sp->GetUUID(); + success = module_spec.GetUUID().IsValid() || module_spec.GetFileSpec(); + } + else + { + result.AppendError ("frame has no module"); + error_set = true; + } + } + else + { + result.AppendError ("invalid current frame"); + error_set = true; + } + } + else + { + result.AppendErrorWithFormat ("process is not stopped: %s", StateAsCString(process_state)); + error_set = true; + } + } + else + { + result.AppendError ("a process must exist in order to use the --frame option"); + error_set = true; + } + } + else + { + if (uuid_option_set) + { + module_spec.GetUUID() = m_uuid_option_group.GetOptionValue().GetCurrentValue(); + success |= module_spec.GetUUID().IsValid(); + } + else if (file_option_set) + { + module_spec.GetFileSpec() = m_file_option.GetOptionValue().GetCurrentValue(); + ModuleSP module_sp (target->GetImages().FindFirstModule(module_spec)); + if (module_sp) + { + module_spec.GetFileSpec() = module_sp->GetFileSpec(); + module_spec.GetPlatformFileSpec() = module_sp->GetPlatformFileSpec(); + module_spec.GetUUID() = module_sp->GetUUID(); + module_spec.GetArchitecture() = module_sp->GetArchitecture(); + } + else + { + module_spec.GetArchitecture() = target->GetArchitecture(); + } + success |= module_spec.GetFileSpec().Exists(); + } + } + + if (success) + { + if (Symbols::DownloadObjectAndSymbolFile (module_spec)) + { + if (module_spec.GetSymbolFileSpec()) + success = AddModuleSymbols (target, module_spec, flush, result); + } + } + + if (!success && !error_set) + { + StreamString error_strm; + if (uuid_option_set) + { + error_strm.PutCString("unable to find debug symbols for UUID "); + module_spec.GetUUID().Dump (&error_strm); + } + else if (file_option_set) + { + error_strm.PutCString("unable to find debug symbols for the executable file "); + error_strm << module_spec.GetFileSpec(); + } + else if (frame_option_set) + { + error_strm.PutCString("unable to find debug symbols for the current frame"); + } + result.AppendError (error_strm.GetData()); + } + } + else + { + result.AppendError ("one or more symbol file paths must be specified, or options must be specified"); + } + } + else + { + if (uuid_option_set) + { + result.AppendError ("specify either one or more paths to symbol files or use the --uuid option without arguments"); + } + else if (file_option_set) + { + result.AppendError ("specify either one or more paths to symbol files or use the --file option without arguments"); + } + else if (frame_option_set) + { + result.AppendError ("specify either one or more paths to symbol files or use the --frame option without arguments"); + } + else + { + PlatformSP platform_sp (target->GetPlatform()); + + for (size_t i=0; iResolveSymbolFile(*target, module_spec, symfile_spec).Success()) + module_spec.GetSymbolFileSpec() = symfile_spec; + } + + ArchSpec arch; + bool symfile_exists = module_spec.GetSymbolFileSpec().Exists(); + + if (symfile_exists) + { + if (!AddModuleSymbols (target, module_spec, flush, result)) + break; + } + else + { + char resolved_symfile_path[PATH_MAX]; + if (module_spec.GetSymbolFileSpec().GetPath (resolved_symfile_path, sizeof(resolved_symfile_path))) + { + if (strcmp (resolved_symfile_path, symfile_path) != 0) + { + result.AppendErrorWithFormat ("invalid module path '%s' with resolved path '%s'\n", symfile_path, resolved_symfile_path); + break; + } + } + result.AppendErrorWithFormat ("invalid module path '%s'\n", symfile_path); + break; + } + } + } + } + } + + if (flush) + { + Process *process = m_exe_ctx.GetProcessPtr(); + if (process) + process->Flush(); + } + return result.Succeeded(); + } + + OptionGroupOptions m_option_group; + OptionGroupUUID m_uuid_option_group; + OptionGroupFile m_file_option; + OptionGroupBoolean m_current_frame_option; + + +}; + + +#pragma mark CommandObjectTargetSymbols + +//------------------------------------------------------------------------- +// CommandObjectTargetSymbols +//------------------------------------------------------------------------- + +class CommandObjectTargetSymbols : public CommandObjectMultiword +{ +public: + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + CommandObjectTargetSymbols(CommandInterpreter &interpreter) : + CommandObjectMultiword (interpreter, + "target symbols", + "A set of commands for adding and managing debug symbol files.", + "target symbols ...") + { + LoadSubCommand ("add", CommandObjectSP (new CommandObjectTargetSymbolsAdd (interpreter))); + + } + virtual + ~CommandObjectTargetSymbols() + { + } + +private: + //------------------------------------------------------------------ + // For CommandObjectTargetModules only + //------------------------------------------------------------------ + DISALLOW_COPY_AND_ASSIGN (CommandObjectTargetSymbols); +}; + + +#pragma mark CommandObjectTargetStopHookAdd + +//------------------------------------------------------------------------- +// CommandObjectTargetStopHookAdd +//------------------------------------------------------------------------- + +class CommandObjectTargetStopHookAdd : public CommandObjectParsed +{ +public: + + class CommandOptions : public Options + { + public: + CommandOptions (CommandInterpreter &interpreter) : + Options(interpreter), + m_line_start(0), + m_line_end (UINT_MAX), + m_func_name_type_mask (eFunctionNameTypeAuto), + m_sym_ctx_specified (false), + m_thread_specified (false), + m_use_one_liner (false), + m_one_liner() + { + } + + ~CommandOptions () {} + + const OptionDefinition* + GetDefinitions () + { + return g_option_table; + } + + virtual Error + SetOptionValue (uint32_t option_idx, const char *option_arg) + { + Error error; + const int short_option = m_getopt_table[option_idx].val; + bool success; + + switch (short_option) + { + case 'c': + m_class_name = option_arg; + m_sym_ctx_specified = true; + break; + + case 'e': + m_line_end = Args::StringToUInt32 (option_arg, UINT_MAX, 0, &success); + if (!success) + { + error.SetErrorStringWithFormat ("invalid end line number: \"%s\"", option_arg); + break; + } + m_sym_ctx_specified = true; + break; + + case 'l': + m_line_start = Args::StringToUInt32 (option_arg, 0, 0, &success); + if (!success) + { + error.SetErrorStringWithFormat ("invalid start line number: \"%s\"", option_arg); + break; + } + m_sym_ctx_specified = true; + break; + + case 'i': + m_no_inlines = true; + break; + + case 'n': + m_function_name = option_arg; + m_func_name_type_mask |= eFunctionNameTypeAuto; + m_sym_ctx_specified = true; + break; + + case 'f': + m_file_name = option_arg; + m_sym_ctx_specified = true; + break; + case 's': + m_module_name = option_arg; + m_sym_ctx_specified = true; + break; + case 't' : + { + m_thread_id = Args::StringToUInt64(option_arg, LLDB_INVALID_THREAD_ID, 0); + if (m_thread_id == LLDB_INVALID_THREAD_ID) + error.SetErrorStringWithFormat ("invalid thread id string '%s'", option_arg); + m_thread_specified = true; + } + break; + case 'T': + m_thread_name = option_arg; + m_thread_specified = true; + break; + case 'q': + m_queue_name = option_arg; + m_thread_specified = true; + break; + case 'x': + { + m_thread_index = Args::StringToUInt32(option_arg, UINT32_MAX, 0); + if (m_thread_id == UINT32_MAX) + error.SetErrorStringWithFormat ("invalid thread index string '%s'", option_arg); + m_thread_specified = true; + } + break; + case 'o': + m_use_one_liner = true; + m_one_liner = option_arg; + break; + default: + error.SetErrorStringWithFormat ("unrecognized option %c.", short_option); + break; + } + return error; + } + + void + OptionParsingStarting () + { + m_class_name.clear(); + m_function_name.clear(); + m_line_start = 0; + m_line_end = UINT_MAX; + m_file_name.clear(); + m_module_name.clear(); + m_func_name_type_mask = eFunctionNameTypeAuto; + m_thread_id = LLDB_INVALID_THREAD_ID; + m_thread_index = UINT32_MAX; + m_thread_name.clear(); + m_queue_name.clear(); + + m_no_inlines = false; + m_sym_ctx_specified = false; + m_thread_specified = false; + + m_use_one_liner = false; + m_one_liner.clear(); + } + + + static OptionDefinition g_option_table[]; + + std::string m_class_name; + std::string m_function_name; + uint32_t m_line_start; + uint32_t m_line_end; + std::string m_file_name; + std::string m_module_name; + uint32_t m_func_name_type_mask; // A pick from lldb::FunctionNameType. + lldb::tid_t m_thread_id; + uint32_t m_thread_index; + std::string m_thread_name; + std::string m_queue_name; + bool m_sym_ctx_specified; + bool m_no_inlines; + bool m_thread_specified; + // Instance variables to hold the values for one_liner options. + bool m_use_one_liner; + std::string m_one_liner; + }; + + Options * + GetOptions () + { + return &m_options; + } + + CommandObjectTargetStopHookAdd (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "target stop-hook add ", + "Add a hook to be executed when the target stops.", + "target stop-hook add"), + m_options (interpreter) + { + } + + ~CommandObjectTargetStopHookAdd () + { + } + + static size_t + ReadCommandsCallbackFunction (void *baton, + InputReader &reader, + lldb::InputReaderAction notification, + const char *bytes, + size_t bytes_len) + { + StreamSP out_stream = reader.GetDebugger().GetAsyncOutputStream(); + Target::StopHook *new_stop_hook = ((Target::StopHook *) baton); + static bool got_interrupted; + bool batch_mode = reader.GetDebugger().GetCommandInterpreter().GetBatchCommandMode(); + + switch (notification) + { + case eInputReaderActivate: + if (!batch_mode) + { + out_stream->Printf ("%s\n", "Enter your stop hook command(s). Type 'DONE' to end."); + if (reader.GetPrompt()) + out_stream->Printf ("%s", reader.GetPrompt()); + out_stream->Flush(); + } + got_interrupted = false; + break; + + case eInputReaderDeactivate: + break; + + case eInputReaderReactivate: + if (reader.GetPrompt() && !batch_mode) + { + out_stream->Printf ("%s", reader.GetPrompt()); + out_stream->Flush(); + } + got_interrupted = false; + break; + + case eInputReaderAsynchronousOutputWritten: + break; + + case eInputReaderGotToken: + if (bytes && bytes_len && baton) + { + StringList *commands = new_stop_hook->GetCommandPointer(); + if (commands) + { + commands->AppendString (bytes, bytes_len); + } + } + if (!reader.IsDone() && reader.GetPrompt() && !batch_mode) + { + out_stream->Printf ("%s", reader.GetPrompt()); + out_stream->Flush(); + } + break; + + case eInputReaderInterrupt: + { + // Finish, and cancel the stop hook. + new_stop_hook->GetTarget()->RemoveStopHookByID(new_stop_hook->GetID()); + if (!batch_mode) + { + out_stream->Printf ("Stop hook cancelled.\n"); + out_stream->Flush(); + } + + reader.SetIsDone (true); + } + got_interrupted = true; + break; + + case eInputReaderEndOfFile: + reader.SetIsDone (true); + break; + + case eInputReaderDone: + if (!got_interrupted && !batch_mode) + { + out_stream->Printf ("Stop hook #%" PRIu64 " added.\n", new_stop_hook->GetID()); + out_stream->Flush(); + } + break; + } + + return bytes_len; + } + +protected: + bool + DoExecute (Args& command, CommandReturnObject &result) + { + Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get(); + if (target) + { + Target::StopHookSP new_hook_sp; + target->AddStopHook (new_hook_sp); + + // First step, make the specifier. + std::unique_ptr specifier_ap; + if (m_options.m_sym_ctx_specified) + { + specifier_ap.reset(new SymbolContextSpecifier(m_interpreter.GetDebugger().GetSelectedTarget())); + + if (!m_options.m_module_name.empty()) + { + specifier_ap->AddSpecification (m_options.m_module_name.c_str(), SymbolContextSpecifier::eModuleSpecified); + } + + if (!m_options.m_class_name.empty()) + { + specifier_ap->AddSpecification (m_options.m_class_name.c_str(), SymbolContextSpecifier::eClassOrNamespaceSpecified); + } + + if (!m_options.m_file_name.empty()) + { + specifier_ap->AddSpecification (m_options.m_file_name.c_str(), SymbolContextSpecifier::eFileSpecified); + } + + if (m_options.m_line_start != 0) + { + specifier_ap->AddLineSpecification (m_options.m_line_start, SymbolContextSpecifier::eLineStartSpecified); + } + + if (m_options.m_line_end != UINT_MAX) + { + specifier_ap->AddLineSpecification (m_options.m_line_end, SymbolContextSpecifier::eLineEndSpecified); + } + + if (!m_options.m_function_name.empty()) + { + specifier_ap->AddSpecification (m_options.m_function_name.c_str(), SymbolContextSpecifier::eFunctionSpecified); + } + } + + if (specifier_ap.get()) + new_hook_sp->SetSpecifier (specifier_ap.release()); + + // Next see if any of the thread options have been entered: + + if (m_options.m_thread_specified) + { + ThreadSpec *thread_spec = new ThreadSpec(); + + if (m_options.m_thread_id != LLDB_INVALID_THREAD_ID) + { + thread_spec->SetTID (m_options.m_thread_id); + } + + if (m_options.m_thread_index != UINT32_MAX) + thread_spec->SetIndex (m_options.m_thread_index); + + if (!m_options.m_thread_name.empty()) + thread_spec->SetName (m_options.m_thread_name.c_str()); + + if (!m_options.m_queue_name.empty()) + thread_spec->SetQueueName (m_options.m_queue_name.c_str()); + + new_hook_sp->SetThreadSpecifier (thread_spec); + + } + if (m_options.m_use_one_liner) + { + // Use one-liner. + new_hook_sp->GetCommandPointer()->AppendString (m_options.m_one_liner.c_str()); + result.AppendMessageWithFormat("Stop hook #%" PRIu64 " added.\n", new_hook_sp->GetID()); + } + else + { + // Otherwise gather up the command list, we'll push an input reader and suck the data from that directly into + // the new stop hook's command string. + InputReaderSP reader_sp (new InputReader(m_interpreter.GetDebugger())); + if (!reader_sp) + { + result.AppendError("out of memory\n"); + result.SetStatus (eReturnStatusFailed); + target->RemoveStopHookByID (new_hook_sp->GetID()); + return false; + } + + Error err (reader_sp->Initialize (CommandObjectTargetStopHookAdd::ReadCommandsCallbackFunction, + new_hook_sp.get(), // baton + eInputReaderGranularityLine, // token size, to pass to callback function + "DONE", // end token + "> ", // prompt + true)); // echo input + if (!err.Success()) + { + result.AppendError (err.AsCString()); + result.SetStatus (eReturnStatusFailed); + target->RemoveStopHookByID (new_hook_sp->GetID()); + return false; + } + m_interpreter.GetDebugger().PushInputReader (reader_sp); + } + result.SetStatus (eReturnStatusSuccessFinishNoResult); + } + else + { + result.AppendError ("invalid target\n"); + result.SetStatus (eReturnStatusFailed); + } + + return result.Succeeded(); + } +private: + CommandOptions m_options; +}; + +OptionDefinition +CommandObjectTargetStopHookAdd::CommandOptions::g_option_table[] = +{ + { LLDB_OPT_SET_ALL, false, "one-liner", 'o', required_argument, NULL, 0, eArgTypeOneLiner, + "Specify a one-line breakpoint command inline. Be sure to surround it with quotes." }, + { LLDB_OPT_SET_ALL, false, "shlib", 's', required_argument, NULL, CommandCompletions::eModuleCompletion, eArgTypeShlibName, + "Set the module within which the stop-hook is to be run."}, + { LLDB_OPT_SET_ALL, false, "thread-index", 'x', required_argument, NULL, 0, eArgTypeThreadIndex, + "The stop hook is run only for the thread whose index matches this argument."}, + { LLDB_OPT_SET_ALL, false, "thread-id", 't', required_argument, NULL, 0, eArgTypeThreadID, + "The stop hook is run only for the thread whose TID matches this argument."}, + { LLDB_OPT_SET_ALL, false, "thread-name", 'T', required_argument, NULL, 0, eArgTypeThreadName, + "The stop hook is run only for the thread whose thread name matches this argument."}, + { LLDB_OPT_SET_ALL, false, "queue-name", 'q', required_argument, NULL, 0, eArgTypeQueueName, + "The stop hook is run only for threads in the queue whose name is given by this argument."}, + { LLDB_OPT_SET_1, false, "file", 'f', required_argument, NULL, CommandCompletions::eSourceFileCompletion, eArgTypeFilename, + "Specify the source file within which the stop-hook is to be run." }, + { LLDB_OPT_SET_1, false, "start-line", 'l', required_argument, NULL, 0, eArgTypeLineNum, + "Set the start of the line range for which the stop-hook is to be run."}, + { LLDB_OPT_SET_1, false, "end-line", 'e', required_argument, NULL, 0, eArgTypeLineNum, + "Set the end of the line range for which the stop-hook is to be run."}, + { LLDB_OPT_SET_2, false, "classname", 'c', required_argument, NULL, 0, eArgTypeClassName, + "Specify the class within which the stop-hook is to be run." }, + { LLDB_OPT_SET_3, false, "name", 'n', required_argument, NULL, CommandCompletions::eSymbolCompletion, eArgTypeFunctionName, + "Set the function name within which the stop hook will be run." }, + { 0, false, NULL, 0, 0, NULL, 0, eArgTypeNone, NULL } +}; + +#pragma mark CommandObjectTargetStopHookDelete + +//------------------------------------------------------------------------- +// CommandObjectTargetStopHookDelete +//------------------------------------------------------------------------- + +class CommandObjectTargetStopHookDelete : public CommandObjectParsed +{ +public: + + CommandObjectTargetStopHookDelete (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "target stop-hook delete", + "Delete a stop-hook.", + "target stop-hook delete []") + { + } + + ~CommandObjectTargetStopHookDelete () + { + } + +protected: + bool + DoExecute (Args& command, CommandReturnObject &result) + { + Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get(); + if (target) + { + // FIXME: see if we can use the breakpoint id style parser? + size_t num_args = command.GetArgumentCount(); + if (num_args == 0) + { + if (!m_interpreter.Confirm ("Delete all stop hooks?", true)) + { + result.SetStatus (eReturnStatusFailed); + return false; + } + else + { + target->RemoveAllStopHooks(); + } + } + else + { + bool success; + for (size_t i = 0; i < num_args; i++) + { + lldb::user_id_t user_id = Args::StringToUInt32 (command.GetArgumentAtIndex(i), 0, 0, &success); + if (!success) + { + result.AppendErrorWithFormat ("invalid stop hook id: \"%s\".\n", command.GetArgumentAtIndex(i)); + result.SetStatus(eReturnStatusFailed); + return false; + } + success = target->RemoveStopHookByID (user_id); + if (!success) + { + result.AppendErrorWithFormat ("unknown stop hook id: \"%s\".\n", command.GetArgumentAtIndex(i)); + result.SetStatus(eReturnStatusFailed); + return false; + } + } + } + result.SetStatus (eReturnStatusSuccessFinishNoResult); + } + else + { + result.AppendError ("invalid target\n"); + result.SetStatus (eReturnStatusFailed); + } + + return result.Succeeded(); + } +}; +#pragma mark CommandObjectTargetStopHookEnableDisable + +//------------------------------------------------------------------------- +// CommandObjectTargetStopHookEnableDisable +//------------------------------------------------------------------------- + +class CommandObjectTargetStopHookEnableDisable : public CommandObjectParsed +{ +public: + + CommandObjectTargetStopHookEnableDisable (CommandInterpreter &interpreter, bool enable, const char *name, const char *help, const char *syntax) : + CommandObjectParsed (interpreter, + name, + help, + syntax), + m_enable (enable) + { + } + + ~CommandObjectTargetStopHookEnableDisable () + { + } + +protected: + bool + DoExecute (Args& command, CommandReturnObject &result) + { + Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get(); + if (target) + { + // FIXME: see if we can use the breakpoint id style parser? + size_t num_args = command.GetArgumentCount(); + bool success; + + if (num_args == 0) + { + target->SetAllStopHooksActiveState (m_enable); + } + else + { + for (size_t i = 0; i < num_args; i++) + { + lldb::user_id_t user_id = Args::StringToUInt32 (command.GetArgumentAtIndex(i), 0, 0, &success); + if (!success) + { + result.AppendErrorWithFormat ("invalid stop hook id: \"%s\".\n", command.GetArgumentAtIndex(i)); + result.SetStatus(eReturnStatusFailed); + return false; + } + success = target->SetStopHookActiveStateByID (user_id, m_enable); + if (!success) + { + result.AppendErrorWithFormat ("unknown stop hook id: \"%s\".\n", command.GetArgumentAtIndex(i)); + result.SetStatus(eReturnStatusFailed); + return false; + } + } + } + result.SetStatus (eReturnStatusSuccessFinishNoResult); + } + else + { + result.AppendError ("invalid target\n"); + result.SetStatus (eReturnStatusFailed); + } + return result.Succeeded(); + } +private: + bool m_enable; +}; + +#pragma mark CommandObjectTargetStopHookList + +//------------------------------------------------------------------------- +// CommandObjectTargetStopHookList +//------------------------------------------------------------------------- + +class CommandObjectTargetStopHookList : public CommandObjectParsed +{ +public: + + CommandObjectTargetStopHookList (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "target stop-hook list", + "List all stop-hooks.", + "target stop-hook list []") + { + } + + ~CommandObjectTargetStopHookList () + { + } + +protected: + bool + DoExecute (Args& command, CommandReturnObject &result) + { + Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get(); + if (!target) + { + result.AppendError ("invalid target\n"); + result.SetStatus (eReturnStatusFailed); + return result.Succeeded(); + } + + size_t num_hooks = target->GetNumStopHooks (); + if (num_hooks == 0) + { + result.GetOutputStream().PutCString ("No stop hooks.\n"); + } + else + { + for (size_t i = 0; i < num_hooks; i++) + { + Target::StopHookSP this_hook = target->GetStopHookAtIndex (i); + if (i > 0) + result.GetOutputStream().PutCString ("\n"); + this_hook->GetDescription (&(result.GetOutputStream()), eDescriptionLevelFull); + } + } + result.SetStatus (eReturnStatusSuccessFinishResult); + return result.Succeeded(); + } +}; + +#pragma mark CommandObjectMultiwordTargetStopHooks +//------------------------------------------------------------------------- +// CommandObjectMultiwordTargetStopHooks +//------------------------------------------------------------------------- + +class CommandObjectMultiwordTargetStopHooks : public CommandObjectMultiword +{ +public: + + CommandObjectMultiwordTargetStopHooks (CommandInterpreter &interpreter) : + CommandObjectMultiword (interpreter, + "target stop-hook", + "A set of commands for operating on debugger target stop-hooks.", + "target stop-hook []") + { + LoadSubCommand ("add", CommandObjectSP (new CommandObjectTargetStopHookAdd (interpreter))); + LoadSubCommand ("delete", CommandObjectSP (new CommandObjectTargetStopHookDelete (interpreter))); + LoadSubCommand ("disable", CommandObjectSP (new CommandObjectTargetStopHookEnableDisable (interpreter, + false, + "target stop-hook disable []", + "Disable a stop-hook.", + "target stop-hook disable"))); + LoadSubCommand ("enable", CommandObjectSP (new CommandObjectTargetStopHookEnableDisable (interpreter, + true, + "target stop-hook enable []", + "Enable a stop-hook.", + "target stop-hook enable"))); + LoadSubCommand ("list", CommandObjectSP (new CommandObjectTargetStopHookList (interpreter))); + } + + ~CommandObjectMultiwordTargetStopHooks() + { + } +}; + + + +#pragma mark CommandObjectMultiwordTarget + +//------------------------------------------------------------------------- +// CommandObjectMultiwordTarget +//------------------------------------------------------------------------- + +CommandObjectMultiwordTarget::CommandObjectMultiwordTarget (CommandInterpreter &interpreter) : + CommandObjectMultiword (interpreter, + "target", + "A set of commands for operating on debugger targets.", + "target []") +{ + + LoadSubCommand ("create", CommandObjectSP (new CommandObjectTargetCreate (interpreter))); + LoadSubCommand ("delete", CommandObjectSP (new CommandObjectTargetDelete (interpreter))); + LoadSubCommand ("list", CommandObjectSP (new CommandObjectTargetList (interpreter))); + LoadSubCommand ("select", CommandObjectSP (new CommandObjectTargetSelect (interpreter))); + LoadSubCommand ("stop-hook", CommandObjectSP (new CommandObjectMultiwordTargetStopHooks (interpreter))); + LoadSubCommand ("modules", CommandObjectSP (new CommandObjectTargetModules (interpreter))); + LoadSubCommand ("symbols", CommandObjectSP (new CommandObjectTargetSymbols (interpreter))); + LoadSubCommand ("variable", CommandObjectSP (new CommandObjectTargetVariable (interpreter))); +} + +CommandObjectMultiwordTarget::~CommandObjectMultiwordTarget () +{ +} + + diff --git a/contrib/llvm/tools/lldb/source/Commands/CommandObjectTarget.h b/contrib/llvm/tools/lldb/source/Commands/CommandObjectTarget.h new file mode 100644 index 00000000000..7b6637812c4 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Commands/CommandObjectTarget.h @@ -0,0 +1,41 @@ +//===-- CommandObjectTarget.h -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_CommandObjectTarget_h_ +#define liblldb_CommandObjectTarget_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Interpreter/Options.h" +#include "lldb/Core/ArchSpec.h" +#include "lldb/Interpreter/CommandObjectMultiword.h" + +namespace lldb_private { + +//------------------------------------------------------------------------- +// CommandObjectMultiwordTarget +//------------------------------------------------------------------------- + +class CommandObjectMultiwordTarget : public CommandObjectMultiword +{ +public: + + CommandObjectMultiwordTarget (CommandInterpreter &interpreter); + + virtual + ~CommandObjectMultiwordTarget (); + + +}; + +} // namespace lldb_private + +#endif // liblldb_CommandObjectTarget_h_ diff --git a/contrib/llvm/tools/lldb/source/Commands/CommandObjectThread.cpp b/contrib/llvm/tools/lldb/source/Commands/CommandObjectThread.cpp new file mode 100644 index 00000000000..b8657b4361d --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Commands/CommandObjectThread.cpp @@ -0,0 +1,1526 @@ +//===-- CommandObjectThread.cpp ---------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "CommandObjectThread.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private.h" +#include "lldb/Core/State.h" +#include "lldb/Core/SourceManager.h" +#include "lldb/Host/Host.h" +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Interpreter/CommandReturnObject.h" +#include "lldb/Interpreter/Options.h" +#include "lldb/Symbol/CompileUnit.h" +#include "lldb/Symbol/Function.h" +#include "lldb/Symbol/LineTable.h" +#include "lldb/Symbol/LineEntry.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" +#include "lldb/Target/ThreadPlan.h" +#include "lldb/Target/ThreadPlanStepInstruction.h" +#include "lldb/Target/ThreadPlanStepOut.h" +#include "lldb/Target/ThreadPlanStepRange.h" +#include "lldb/Target/ThreadPlanStepInRange.h" + + +using namespace lldb; +using namespace lldb_private; + + +//------------------------------------------------------------------------- +// CommandObjectThreadBacktrace +//------------------------------------------------------------------------- + +class CommandObjectThreadBacktrace : public CommandObjectParsed +{ +public: + + class CommandOptions : public Options + { + public: + + CommandOptions (CommandInterpreter &interpreter) : + Options(interpreter) + { + // Keep default values of all options in one place: OptionParsingStarting () + OptionParsingStarting (); + } + + virtual + ~CommandOptions () + { + } + + virtual Error + SetOptionValue (uint32_t option_idx, const char *option_arg) + { + Error error; + const int short_option = m_getopt_table[option_idx].val; + + switch (short_option) + { + case 'c': + { + bool success; + int32_t input_count = Args::StringToSInt32 (option_arg, -1, 0, &success); + if (!success) + error.SetErrorStringWithFormat("invalid integer value for option '%c'", short_option); + if (input_count < -1) + m_count = UINT32_MAX; + else + m_count = input_count; + } + break; + case 's': + { + bool success; + m_start = Args::StringToUInt32 (option_arg, 0, 0, &success); + if (!success) + error.SetErrorStringWithFormat("invalid integer value for option '%c'", short_option); + } + break; + default: + error.SetErrorStringWithFormat("invalid short option character '%c'", short_option); + break; + + } + return error; + } + + void + OptionParsingStarting () + { + m_count = UINT32_MAX; + m_start = 0; + } + + const OptionDefinition* + GetDefinitions () + { + return g_option_table; + } + + // Options table: Required for subclasses of Options. + + static OptionDefinition g_option_table[]; + + // Instance variables to hold the values for command options. + uint32_t m_count; + uint32_t m_start; + }; + + CommandObjectThreadBacktrace (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "thread backtrace", + "Show the stack for one or more threads. If no threads are specified, show the currently selected thread. Use the thread-index \"all\" to see all threads.", + NULL, + eFlagRequiresProcess | + eFlagRequiresThread | + eFlagTryTargetAPILock | + eFlagProcessMustBeLaunched | + eFlagProcessMustBePaused ), + m_options(interpreter) + { + CommandArgumentEntry arg; + CommandArgumentData thread_idx_arg; + + // Define the first (and only) variant of this arg. + thread_idx_arg.arg_type = eArgTypeThreadIndex; + thread_idx_arg.arg_repetition = eArgRepeatStar; + + // There is only one variant this argument could be; put it into the argument entry. + arg.push_back (thread_idx_arg); + + // Push the data for the first argument into the m_arguments vector. + m_arguments.push_back (arg); + } + + ~CommandObjectThreadBacktrace() + { + } + + virtual Options * + GetOptions () + { + return &m_options; + } + +protected: + virtual bool + DoExecute (Args& command, CommandReturnObject &result) + { + result.SetStatus (eReturnStatusSuccessFinishResult); + Stream &strm = result.GetOutputStream(); + + // Don't show source context when doing backtraces. + const uint32_t num_frames_with_source = 0; + if (command.GetArgumentCount() == 0) + { + Thread *thread = m_exe_ctx.GetThreadPtr(); + // Thread::GetStatus() returns the number of frames shown. + if (thread->GetStatus (strm, + m_options.m_start, + m_options.m_count, + num_frames_with_source)) + { + result.SetStatus (eReturnStatusSuccessFinishResult); + } + } + else if (command.GetArgumentCount() == 1 && ::strcmp (command.GetArgumentAtIndex(0), "all") == 0) + { + Process *process = m_exe_ctx.GetProcessPtr(); + Mutex::Locker locker (process->GetThreadList().GetMutex()); + uint32_t num_threads = process->GetThreadList().GetSize(); + for (uint32_t i = 0; i < num_threads; i++) + { + ThreadSP thread_sp = process->GetThreadList().GetThreadAtIndex(i); + if (!thread_sp->GetStatus (strm, + m_options.m_start, + m_options.m_count, + num_frames_with_source)) + { + result.AppendErrorWithFormat ("error displaying backtrace for thread: \"0x%4.4x\"\n", i); + result.SetStatus (eReturnStatusFailed); + return false; + } + + if (i < num_threads - 1) + result.AppendMessage(""); + + } + } + else + { + const size_t num_args = command.GetArgumentCount(); + Process *process = m_exe_ctx.GetProcessPtr(); + Mutex::Locker locker (process->GetThreadList().GetMutex()); + std::vector thread_sps; + + for (size_t i = 0; i < num_args; i++) + { + bool success; + + uint32_t thread_idx = Args::StringToUInt32(command.GetArgumentAtIndex(i), 0, 0, &success); + if (!success) + { + result.AppendErrorWithFormat ("invalid thread specification: \"%s\"\n", command.GetArgumentAtIndex(i)); + result.SetStatus (eReturnStatusFailed); + return false; + } + + thread_sps.push_back(process->GetThreadList().FindThreadByIndexID(thread_idx)); + + if (!thread_sps[i]) + { + result.AppendErrorWithFormat ("no thread with index: \"%s\"\n", command.GetArgumentAtIndex(i)); + result.SetStatus (eReturnStatusFailed); + return false; + } + + } + + for (uint32_t i = 0; i < num_args; i++) + { + if (!thread_sps[i]->GetStatus (strm, + m_options.m_start, + m_options.m_count, + num_frames_with_source)) + { + result.AppendErrorWithFormat ("error displaying backtrace for thread: \"%s\"\n", command.GetArgumentAtIndex(i)); + result.SetStatus (eReturnStatusFailed); + return false; + } + + if (i < num_args - 1) + result.AppendMessage(""); + } + } + return result.Succeeded(); + } + + CommandOptions m_options; +}; + +OptionDefinition +CommandObjectThreadBacktrace::CommandOptions::g_option_table[] = +{ +{ LLDB_OPT_SET_1, false, "count", 'c', required_argument, NULL, 0, eArgTypeCount, "How many frames to display (-1 for all)"}, +{ LLDB_OPT_SET_1, false, "start", 's', required_argument, NULL, 0, eArgTypeFrameIndex, "Frame in which to start the backtrace"}, +{ 0, false, NULL, 0, 0, NULL, 0, eArgTypeNone, NULL } +}; + +enum StepScope +{ + eStepScopeSource, + eStepScopeInstruction +}; + +class CommandObjectThreadStepWithTypeAndScope : public CommandObjectParsed +{ +public: + + class CommandOptions : public Options + { + public: + + CommandOptions (CommandInterpreter &interpreter) : + Options (interpreter) + { + // Keep default values of all options in one place: OptionParsingStarting () + OptionParsingStarting (); + } + + virtual + ~CommandOptions () + { + } + + virtual Error + SetOptionValue (uint32_t option_idx, const char *option_arg) + { + Error error; + const int short_option = m_getopt_table[option_idx].val; + + switch (short_option) + { + case 'a': + { + bool success; + m_avoid_no_debug = Args::StringToBoolean (option_arg, true, &success); + if (!success) + error.SetErrorStringWithFormat("invalid boolean value for option '%c'", short_option); + } + break; + + case 'm': + { + OptionEnumValueElement *enum_values = g_option_table[option_idx].enum_values; + m_run_mode = (lldb::RunMode) Args::StringToOptionEnum(option_arg, enum_values, eOnlyDuringStepping, error); + } + break; + + case 'r': + { + m_avoid_regexp.clear(); + m_avoid_regexp.assign(option_arg); + } + break; + + case 't': + { + m_step_in_target.clear(); + m_step_in_target.assign(option_arg); + + } + break; + default: + error.SetErrorStringWithFormat("invalid short option character '%c'", short_option); + break; + + } + return error; + } + + void + OptionParsingStarting () + { + m_avoid_no_debug = true; + m_run_mode = eOnlyDuringStepping; + m_avoid_regexp.clear(); + m_step_in_target.clear(); + } + + const OptionDefinition* + GetDefinitions () + { + return g_option_table; + } + + // Options table: Required for subclasses of Options. + + static OptionDefinition g_option_table[]; + + // Instance variables to hold the values for command options. + bool m_avoid_no_debug; + RunMode m_run_mode; + std::string m_avoid_regexp; + std::string m_step_in_target; + }; + + CommandObjectThreadStepWithTypeAndScope (CommandInterpreter &interpreter, + const char *name, + const char *help, + const char *syntax, + StepType step_type, + StepScope step_scope) : + CommandObjectParsed (interpreter, name, help, syntax, + eFlagRequiresProcess | + eFlagRequiresThread | + eFlagTryTargetAPILock | + eFlagProcessMustBeLaunched | + eFlagProcessMustBePaused ), + m_step_type (step_type), + m_step_scope (step_scope), + m_options (interpreter) + { + CommandArgumentEntry arg; + CommandArgumentData thread_id_arg; + + // Define the first (and only) variant of this arg. + thread_id_arg.arg_type = eArgTypeThreadID; + thread_id_arg.arg_repetition = eArgRepeatOptional; + + // There is only one variant this argument could be; put it into the argument entry. + arg.push_back (thread_id_arg); + + // Push the data for the first argument into the m_arguments vector. + m_arguments.push_back (arg); + } + + virtual + ~CommandObjectThreadStepWithTypeAndScope () + { + } + + virtual + Options * + GetOptions () + { + return &m_options; + } + +protected: + virtual bool + DoExecute (Args& command, CommandReturnObject &result) + { + Process *process = m_exe_ctx.GetProcessPtr(); + bool synchronous_execution = m_interpreter.GetSynchronous(); + + const uint32_t num_threads = process->GetThreadList().GetSize(); + Thread *thread = NULL; + + if (command.GetArgumentCount() == 0) + { + thread = process->GetThreadList().GetSelectedThread().get(); + if (thread == NULL) + { + result.AppendError ("no selected thread in process"); + result.SetStatus (eReturnStatusFailed); + return false; + } + } + else + { + const char *thread_idx_cstr = command.GetArgumentAtIndex(0); + uint32_t step_thread_idx = Args::StringToUInt32 (thread_idx_cstr, LLDB_INVALID_INDEX32); + if (step_thread_idx == LLDB_INVALID_INDEX32) + { + result.AppendErrorWithFormat ("invalid thread index '%s'.\n", thread_idx_cstr); + result.SetStatus (eReturnStatusFailed); + return false; + } + thread = process->GetThreadList().FindThreadByIndexID(step_thread_idx).get(); + if (thread == NULL) + { + result.AppendErrorWithFormat ("Thread index %u is out of range (valid values are 0 - %u).\n", + step_thread_idx, num_threads); + result.SetStatus (eReturnStatusFailed); + return false; + } + } + + const bool abort_other_plans = false; + const lldb::RunMode stop_other_threads = m_options.m_run_mode; + + // This is a bit unfortunate, but not all the commands in this command object support + // only while stepping, so I use the bool for them. + bool bool_stop_other_threads; + if (m_options.m_run_mode == eAllThreads) + bool_stop_other_threads = false; + else if (m_options.m_run_mode == eOnlyDuringStepping) + { + if (m_step_type == eStepTypeOut) + bool_stop_other_threads = false; + else + bool_stop_other_threads = true; + } + else + bool_stop_other_threads = true; + + ThreadPlanSP new_plan_sp; + + if (m_step_type == eStepTypeInto) + { + StackFrame *frame = thread->GetStackFrameAtIndex(0).get(); + + if (frame->HasDebugInformation ()) + { + new_plan_sp = thread->QueueThreadPlanForStepInRange (abort_other_plans, + frame->GetSymbolContext(eSymbolContextEverything).line_entry.range, + frame->GetSymbolContext(eSymbolContextEverything), + m_options.m_step_in_target.c_str(), + stop_other_threads, + m_options.m_avoid_no_debug); + if (new_plan_sp && !m_options.m_avoid_regexp.empty()) + { + ThreadPlanStepInRange *step_in_range_plan = static_cast (new_plan_sp.get()); + step_in_range_plan->SetAvoidRegexp(m_options.m_avoid_regexp.c_str()); + } + } + else + new_plan_sp = thread->QueueThreadPlanForStepSingleInstruction (false, abort_other_plans, bool_stop_other_threads); + + } + else if (m_step_type == eStepTypeOver) + { + StackFrame *frame = thread->GetStackFrameAtIndex(0).get(); + + if (frame->HasDebugInformation()) + new_plan_sp = thread->QueueThreadPlanForStepOverRange (abort_other_plans, + frame->GetSymbolContext(eSymbolContextEverything).line_entry.range, + frame->GetSymbolContext(eSymbolContextEverything), + stop_other_threads); + else + new_plan_sp = thread->QueueThreadPlanForStepSingleInstruction (true, + abort_other_plans, + bool_stop_other_threads); + + } + else if (m_step_type == eStepTypeTrace) + { + new_plan_sp = thread->QueueThreadPlanForStepSingleInstruction (false, abort_other_plans, bool_stop_other_threads); + } + else if (m_step_type == eStepTypeTraceOver) + { + new_plan_sp = thread->QueueThreadPlanForStepSingleInstruction (true, abort_other_plans, bool_stop_other_threads); + } + else if (m_step_type == eStepTypeOut) + { + new_plan_sp = thread->QueueThreadPlanForStepOut (abort_other_plans, + NULL, + false, + bool_stop_other_threads, + eVoteYes, + eVoteNoOpinion, + thread->GetSelectedFrameIndex()); + } + else + { + result.AppendError ("step type is not supported"); + result.SetStatus (eReturnStatusFailed); + return false; + } + + // If we got a new plan, then set it to be a master plan (User level Plans should be master plans + // so that they can be interruptible). Then resume the process. + + if (new_plan_sp) + { + new_plan_sp->SetIsMasterPlan (true); + new_plan_sp->SetOkayToDiscard (false); + + process->GetThreadList().SetSelectedThreadByID (thread->GetID()); + process->Resume (); + + + if (synchronous_execution) + { + StateType state = process->WaitForProcessToStop (NULL); + + //EventSP event_sp; + //StateType state = process->WaitForStateChangedEvents (NULL, event_sp); + //while (! StateIsStoppedState (state)) + // { + // state = process->WaitForStateChangedEvents (NULL, event_sp); + // } + process->GetThreadList().SetSelectedThreadByID (thread->GetID()); + result.SetDidChangeProcessState (true); + result.AppendMessageWithFormat ("Process %" PRIu64 " %s\n", process->GetID(), StateAsCString (state)); + result.SetStatus (eReturnStatusSuccessFinishNoResult); + } + else + { + result.SetStatus (eReturnStatusSuccessContinuingNoResult); + } + } + else + { + result.AppendError ("Couldn't find thread plan to implement step type."); + result.SetStatus (eReturnStatusFailed); + } + return result.Succeeded(); + } + +protected: + StepType m_step_type; + StepScope m_step_scope; + CommandOptions m_options; +}; + +static OptionEnumValueElement +g_tri_running_mode[] = +{ +{ eOnlyThisThread, "this-thread", "Run only this thread"}, +{ eAllThreads, "all-threads", "Run all threads"}, +{ eOnlyDuringStepping, "while-stepping", "Run only this thread while stepping"}, +{ 0, NULL, NULL } +}; + +static OptionEnumValueElement +g_duo_running_mode[] = +{ +{ eOnlyThisThread, "this-thread", "Run only this thread"}, +{ eAllThreads, "all-threads", "Run all threads"}, +{ 0, NULL, NULL } +}; + +OptionDefinition +CommandObjectThreadStepWithTypeAndScope::CommandOptions::g_option_table[] = +{ +{ LLDB_OPT_SET_1, false, "avoid-no-debug", 'a', required_argument, NULL, 0, eArgTypeBoolean, "A boolean value that sets whether step-in will step over functions with no debug information."}, +{ LLDB_OPT_SET_1, false, "run-mode", 'm', required_argument, g_tri_running_mode, 0, eArgTypeRunMode, "Determine how to run other threads while stepping the current thread."}, +{ LLDB_OPT_SET_1, false, "step-over-regexp",'r', required_argument, NULL, 0, eArgTypeRegularExpression, "A regular expression that defines function names to not to stop at when stepping in."}, +{ LLDB_OPT_SET_1, false, "step-in-target", 't', required_argument, NULL, 0, eArgTypeFunctionName, "The name of the directly called function step in should stop at when stepping into."}, +{ 0, false, NULL, 0, 0, NULL, 0, eArgTypeNone, NULL } +}; + + +//------------------------------------------------------------------------- +// CommandObjectThreadContinue +//------------------------------------------------------------------------- + +class CommandObjectThreadContinue : public CommandObjectParsed +{ +public: + + CommandObjectThreadContinue (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "thread continue", + "Continue execution of one or more threads in an active process.", + NULL, + eFlagRequiresThread | + eFlagTryTargetAPILock | + eFlagProcessMustBeLaunched | + eFlagProcessMustBePaused) + { + CommandArgumentEntry arg; + CommandArgumentData thread_idx_arg; + + // Define the first (and only) variant of this arg. + thread_idx_arg.arg_type = eArgTypeThreadIndex; + thread_idx_arg.arg_repetition = eArgRepeatPlus; + + // There is only one variant this argument could be; put it into the argument entry. + arg.push_back (thread_idx_arg); + + // Push the data for the first argument into the m_arguments vector. + m_arguments.push_back (arg); + } + + + virtual + ~CommandObjectThreadContinue () + { + } + + virtual bool + DoExecute (Args& command, CommandReturnObject &result) + { + bool synchronous_execution = m_interpreter.GetSynchronous (); + + if (!m_interpreter.GetDebugger().GetSelectedTarget().get()) + { + result.AppendError ("invalid target, create a debug target using the 'target create' command"); + result.SetStatus (eReturnStatusFailed); + return false; + } + + Process *process = m_exe_ctx.GetProcessPtr(); + if (process == NULL) + { + result.AppendError ("no process exists. Cannot continue"); + result.SetStatus (eReturnStatusFailed); + return false; + } + + StateType state = process->GetState(); + if ((state == eStateCrashed) || (state == eStateStopped) || (state == eStateSuspended)) + { + Mutex::Locker locker (process->GetThreadList().GetMutex()); + const uint32_t num_threads = process->GetThreadList().GetSize(); + const size_t argc = command.GetArgumentCount(); + if (argc > 0) + { + std::vector resume_threads; + for (uint32_t i=0; iGetThreadList().FindThreadByIndexID(thread_idx).get(); + + if (thread) + { + resume_threads.push_back(thread); + } + else + { + result.AppendErrorWithFormat("invalid thread index %u.\n", thread_idx); + result.SetStatus (eReturnStatusFailed); + return false; + } + } + else + { + result.AppendErrorWithFormat ("invalid thread index argument: \"%s\".\n", command.GetArgumentAtIndex(i)); + result.SetStatus (eReturnStatusFailed); + return false; + } + } + + if (resume_threads.empty()) + { + result.AppendError ("no valid thread indexes were specified"); + result.SetStatus (eReturnStatusFailed); + return false; + } + else + { + if (resume_threads.size() == 1) + result.AppendMessageWithFormat ("Resuming thread: "); + else + result.AppendMessageWithFormat ("Resuming threads: "); + + for (uint32_t idx=0; idxGetThreadList().GetThreadAtIndex(idx).get(); + std::vector::iterator this_thread_pos = find(resume_threads.begin(), resume_threads.end(), thread); + + if (this_thread_pos != resume_threads.end()) + { + resume_threads.erase(this_thread_pos); + if (resume_threads.size() > 0) + result.AppendMessageWithFormat ("%u, ", thread->GetIndexID()); + else + result.AppendMessageWithFormat ("%u ", thread->GetIndexID()); + + thread->SetResumeState (eStateRunning); + } + else + { + thread->SetResumeState (eStateSuspended); + } + } + result.AppendMessageWithFormat ("in process %" PRIu64 "\n", process->GetID()); + } + } + else + { + Thread *current_thread = process->GetThreadList().GetSelectedThread().get(); + if (current_thread == NULL) + { + result.AppendError ("the process doesn't have a current thread"); + result.SetStatus (eReturnStatusFailed); + return false; + } + // Set the actions that the threads should each take when resuming + for (uint32_t idx=0; idxGetThreadList().GetThreadAtIndex(idx).get(); + if (thread == current_thread) + { + result.AppendMessageWithFormat ("Resuming thread 0x%4.4" PRIx64 " in process %" PRIu64 "\n", thread->GetID(), process->GetID()); + thread->SetResumeState (eStateRunning); + } + else + { + thread->SetResumeState (eStateSuspended); + } + } + } + + Error error (process->Resume()); + if (error.Success()) + { + result.AppendMessageWithFormat ("Process %" PRIu64 " resuming\n", process->GetID()); + if (synchronous_execution) + { + state = process->WaitForProcessToStop (NULL); + + result.SetDidChangeProcessState (true); + result.AppendMessageWithFormat ("Process %" PRIu64 " %s\n", process->GetID(), StateAsCString (state)); + result.SetStatus (eReturnStatusSuccessFinishNoResult); + } + else + { + result.SetStatus (eReturnStatusSuccessContinuingNoResult); + } + } + else + { + result.AppendErrorWithFormat("Failed to resume process: %s\n", error.AsCString()); + result.SetStatus (eReturnStatusFailed); + } + } + else + { + result.AppendErrorWithFormat ("Process cannot be continued from its current state (%s).\n", + StateAsCString(state)); + result.SetStatus (eReturnStatusFailed); + } + + return result.Succeeded(); + } + +}; + +//------------------------------------------------------------------------- +// CommandObjectThreadUntil +//------------------------------------------------------------------------- + +class CommandObjectThreadUntil : public CommandObjectParsed +{ +public: + + class CommandOptions : public Options + { + public: + uint32_t m_thread_idx; + uint32_t m_frame_idx; + + CommandOptions (CommandInterpreter &interpreter) : + Options (interpreter), + m_thread_idx(LLDB_INVALID_THREAD_ID), + m_frame_idx(LLDB_INVALID_FRAME_ID) + { + // Keep default values of all options in one place: OptionParsingStarting () + OptionParsingStarting (); + } + + virtual + ~CommandOptions () + { + } + + virtual Error + SetOptionValue (uint32_t option_idx, const char *option_arg) + { + Error error; + const int short_option = m_getopt_table[option_idx].val; + + switch (short_option) + { + case 't': + { + m_thread_idx = Args::StringToUInt32 (option_arg, LLDB_INVALID_INDEX32); + if (m_thread_idx == LLDB_INVALID_INDEX32) + { + error.SetErrorStringWithFormat ("invalid thread index '%s'", option_arg); + } + } + break; + case 'f': + { + m_frame_idx = Args::StringToUInt32 (option_arg, LLDB_INVALID_FRAME_ID); + if (m_frame_idx == LLDB_INVALID_FRAME_ID) + { + error.SetErrorStringWithFormat ("invalid frame index '%s'", option_arg); + } + } + break; + case 'm': + { + OptionEnumValueElement *enum_values = g_option_table[option_idx].enum_values; + lldb::RunMode run_mode = (lldb::RunMode) Args::StringToOptionEnum(option_arg, enum_values, eOnlyDuringStepping, error); + + if (error.Success()) + { + if (run_mode == eAllThreads) + m_stop_others = false; + else + m_stop_others = true; + } + } + break; + default: + error.SetErrorStringWithFormat("invalid short option character '%c'", short_option); + break; + + } + return error; + } + + void + OptionParsingStarting () + { + m_thread_idx = LLDB_INVALID_THREAD_ID; + m_frame_idx = 0; + m_stop_others = false; + } + + const OptionDefinition* + GetDefinitions () + { + return g_option_table; + } + + uint32_t m_step_thread_idx; + bool m_stop_others; + + // Options table: Required for subclasses of Options. + + static OptionDefinition g_option_table[]; + + // Instance variables to hold the values for command options. + }; + + CommandObjectThreadUntil (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "thread until", + "Run the current or specified thread until it reaches a given line number or leaves the current function.", + NULL, + eFlagRequiresThread | + eFlagTryTargetAPILock | + eFlagProcessMustBeLaunched | + eFlagProcessMustBePaused ), + m_options (interpreter) + { + CommandArgumentEntry arg; + CommandArgumentData line_num_arg; + + // Define the first (and only) variant of this arg. + line_num_arg.arg_type = eArgTypeLineNum; + line_num_arg.arg_repetition = eArgRepeatPlain; + + // There is only one variant this argument could be; put it into the argument entry. + arg.push_back (line_num_arg); + + // Push the data for the first argument into the m_arguments vector. + m_arguments.push_back (arg); + } + + + virtual + ~CommandObjectThreadUntil () + { + } + + virtual + Options * + GetOptions () + { + return &m_options; + } + +protected: + virtual bool + DoExecute (Args& command, CommandReturnObject &result) + { + bool synchronous_execution = m_interpreter.GetSynchronous (); + + Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get(); + if (target == NULL) + { + result.AppendError ("invalid target, create a debug target using the 'target create' command"); + result.SetStatus (eReturnStatusFailed); + return false; + } + + Process *process = m_exe_ctx.GetProcessPtr(); + if (process == NULL) + { + result.AppendError ("need a valid process to step"); + result.SetStatus (eReturnStatusFailed); + + } + else + { + Thread *thread = NULL; + uint32_t line_number; + + if (command.GetArgumentCount() != 1) + { + result.AppendErrorWithFormat ("No line number provided:\n%s", GetSyntax()); + result.SetStatus (eReturnStatusFailed); + return false; + } + + line_number = Args::StringToUInt32 (command.GetArgumentAtIndex(0), UINT32_MAX); + if (line_number == UINT32_MAX) + { + result.AppendErrorWithFormat ("invalid line number: '%s'.\n", command.GetArgumentAtIndex(0)); + result.SetStatus (eReturnStatusFailed); + return false; + } + + if (m_options.m_thread_idx == LLDB_INVALID_THREAD_ID) + { + thread = process->GetThreadList().GetSelectedThread().get(); + } + else + { + thread = process->GetThreadList().FindThreadByIndexID(m_options.m_thread_idx).get(); + } + + if (thread == NULL) + { + const uint32_t num_threads = process->GetThreadList().GetSize(); + result.AppendErrorWithFormat ("Thread index %u is out of range (valid values are 0 - %u).\n", + m_options.m_thread_idx, + num_threads); + result.SetStatus (eReturnStatusFailed); + return false; + } + + const bool abort_other_plans = false; + + StackFrame *frame = thread->GetStackFrameAtIndex(m_options.m_frame_idx).get(); + if (frame == NULL) + { + + result.AppendErrorWithFormat ("Frame index %u is out of range for thread %u.\n", + m_options.m_frame_idx, + m_options.m_thread_idx); + result.SetStatus (eReturnStatusFailed); + return false; + } + + ThreadPlanSP new_plan_sp; + + if (frame->HasDebugInformation ()) + { + // Finally we got here... Translate the given line number to a bunch of addresses: + SymbolContext sc(frame->GetSymbolContext (eSymbolContextCompUnit)); + LineTable *line_table = NULL; + if (sc.comp_unit) + line_table = sc.comp_unit->GetLineTable(); + + if (line_table == NULL) + { + result.AppendErrorWithFormat ("Failed to resolve the line table for frame %u of thread index %u.\n", + m_options.m_frame_idx, m_options.m_thread_idx); + result.SetStatus (eReturnStatusFailed); + return false; + } + + LineEntry function_start; + uint32_t index_ptr = 0, end_ptr; + std::vector address_list; + + // Find the beginning & end index of the + AddressRange fun_addr_range = sc.function->GetAddressRange(); + Address fun_start_addr = fun_addr_range.GetBaseAddress(); + line_table->FindLineEntryByAddress (fun_start_addr, function_start, &index_ptr); + + Address fun_end_addr(fun_start_addr.GetSection(), + fun_start_addr.GetOffset() + fun_addr_range.GetByteSize()); + line_table->FindLineEntryByAddress (fun_end_addr, function_start, &end_ptr); + + bool all_in_function = true; + + while (index_ptr <= end_ptr) + { + LineEntry line_entry; + const bool exact = false; + index_ptr = sc.comp_unit->FindLineEntry(index_ptr, line_number, sc.comp_unit, exact, &line_entry); + if (index_ptr == UINT32_MAX) + break; + + addr_t address = line_entry.range.GetBaseAddress().GetLoadAddress(target); + if (address != LLDB_INVALID_ADDRESS) + { + if (fun_addr_range.ContainsLoadAddress (address, target)) + address_list.push_back (address); + else + all_in_function = false; + } + index_ptr++; + } + + if (address_list.size() == 0) + { + if (all_in_function) + result.AppendErrorWithFormat ("No line entries matching until target.\n"); + else + result.AppendErrorWithFormat ("Until target outside of the current function.\n"); + + result.SetStatus (eReturnStatusFailed); + return false; + } + + new_plan_sp = thread->QueueThreadPlanForStepUntil (abort_other_plans, + &address_list.front(), + address_list.size(), + m_options.m_stop_others, + m_options.m_frame_idx); + // User level plans should be master plans so they can be interrupted (e.g. by hitting a breakpoint) + // and other plans executed by the user (stepping around the breakpoint) and then a "continue" + // will resume the original plan. + new_plan_sp->SetIsMasterPlan (true); + new_plan_sp->SetOkayToDiscard(false); + } + else + { + result.AppendErrorWithFormat ("Frame index %u of thread %u has no debug information.\n", + m_options.m_frame_idx, + m_options.m_thread_idx); + result.SetStatus (eReturnStatusFailed); + return false; + + } + + process->GetThreadList().SetSelectedThreadByID (m_options.m_thread_idx); + Error error (process->Resume ()); + if (error.Success()) + { + result.AppendMessageWithFormat ("Process %" PRIu64 " resuming\n", process->GetID()); + if (synchronous_execution) + { + StateType state = process->WaitForProcessToStop (NULL); + + result.SetDidChangeProcessState (true); + result.AppendMessageWithFormat ("Process %" PRIu64 " %s\n", process->GetID(), StateAsCString (state)); + result.SetStatus (eReturnStatusSuccessFinishNoResult); + } + else + { + result.SetStatus (eReturnStatusSuccessContinuingNoResult); + } + } + else + { + result.AppendErrorWithFormat("Failed to resume process: %s.\n", error.AsCString()); + result.SetStatus (eReturnStatusFailed); + } + + } + return result.Succeeded(); + } + + CommandOptions m_options; + +}; + +OptionDefinition +CommandObjectThreadUntil::CommandOptions::g_option_table[] = +{ +{ LLDB_OPT_SET_1, false, "frame", 'f', required_argument, NULL, 0, eArgTypeFrameIndex, "Frame index for until operation - defaults to 0"}, +{ LLDB_OPT_SET_1, false, "thread", 't', required_argument, NULL, 0, eArgTypeThreadIndex, "Thread index for the thread for until operation"}, +{ LLDB_OPT_SET_1, false, "run-mode",'m', required_argument, g_duo_running_mode, 0, eArgTypeRunMode,"Determine how to run other threads while stepping this one"}, +{ 0, false, NULL, 0, 0, NULL, 0, eArgTypeNone, NULL } +}; + + +//------------------------------------------------------------------------- +// CommandObjectThreadSelect +//------------------------------------------------------------------------- + +class CommandObjectThreadSelect : public CommandObjectParsed +{ +public: + + CommandObjectThreadSelect (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "thread select", + "Select a thread as the currently active thread.", + NULL, + eFlagRequiresProcess | + eFlagTryTargetAPILock | + eFlagProcessMustBeLaunched | + eFlagProcessMustBePaused ) + { + CommandArgumentEntry arg; + CommandArgumentData thread_idx_arg; + + // Define the first (and only) variant of this arg. + thread_idx_arg.arg_type = eArgTypeThreadIndex; + thread_idx_arg.arg_repetition = eArgRepeatPlain; + + // There is only one variant this argument could be; put it into the argument entry. + arg.push_back (thread_idx_arg); + + // Push the data for the first argument into the m_arguments vector. + m_arguments.push_back (arg); + } + + + virtual + ~CommandObjectThreadSelect () + { + } + +protected: + virtual bool + DoExecute (Args& command, CommandReturnObject &result) + { + Process *process = m_exe_ctx.GetProcessPtr(); + if (process == NULL) + { + result.AppendError ("no process"); + result.SetStatus (eReturnStatusFailed); + return false; + } + else if (command.GetArgumentCount() != 1) + { + result.AppendErrorWithFormat("'%s' takes exactly one thread index argument:\nUsage: %s\n", m_cmd_name.c_str(), m_cmd_syntax.c_str()); + result.SetStatus (eReturnStatusFailed); + return false; + } + + uint32_t index_id = Args::StringToUInt32(command.GetArgumentAtIndex(0), 0, 0); + + Thread *new_thread = process->GetThreadList().FindThreadByIndexID(index_id).get(); + if (new_thread == NULL) + { + result.AppendErrorWithFormat ("invalid thread #%s.\n", command.GetArgumentAtIndex(0)); + result.SetStatus (eReturnStatusFailed); + return false; + } + + process->GetThreadList().SetSelectedThreadByID(new_thread->GetID(), true); + result.SetStatus (eReturnStatusSuccessFinishNoResult); + + return result.Succeeded(); + } + +}; + + +//------------------------------------------------------------------------- +// CommandObjectThreadList +//------------------------------------------------------------------------- + +class CommandObjectThreadList : public CommandObjectParsed +{ +public: + + + CommandObjectThreadList (CommandInterpreter &interpreter): + CommandObjectParsed (interpreter, + "thread list", + "Show a summary of all current threads in a process.", + "thread list", + eFlagRequiresProcess | + eFlagTryTargetAPILock | + eFlagProcessMustBeLaunched | + eFlagProcessMustBePaused ) + { + } + + ~CommandObjectThreadList() + { + } + +protected: + bool + DoExecute (Args& command, CommandReturnObject &result) + { + Stream &strm = result.GetOutputStream(); + result.SetStatus (eReturnStatusSuccessFinishNoResult); + Process *process = m_exe_ctx.GetProcessPtr(); + const bool only_threads_with_stop_reason = false; + const uint32_t start_frame = 0; + const uint32_t num_frames = 0; + const uint32_t num_frames_with_source = 0; + process->GetStatus(strm); + process->GetThreadStatus (strm, + only_threads_with_stop_reason, + start_frame, + num_frames, + num_frames_with_source); + return result.Succeeded(); + } +}; + +//------------------------------------------------------------------------- +// CommandObjectThreadReturn +//------------------------------------------------------------------------- + +class CommandObjectThreadReturn : public CommandObjectRaw +{ +public: + class CommandOptions : public Options + { + public: + + CommandOptions (CommandInterpreter &interpreter) : + Options (interpreter), + m_from_expression (false) + { + // Keep default values of all options in one place: OptionParsingStarting () + OptionParsingStarting (); + } + + virtual + ~CommandOptions () + { + } + + virtual Error + SetOptionValue (uint32_t option_idx, const char *option_arg) + { + Error error; + const int short_option = m_getopt_table[option_idx].val; + + switch (short_option) + { + case 'x': + { + bool success; + bool tmp_value = Args::StringToBoolean (option_arg, false, &success); + if (success) + m_from_expression = tmp_value; + else + { + error.SetErrorStringWithFormat ("invalid boolean value '%s' for 'x' option", option_arg); + } + } + break; + default: + error.SetErrorStringWithFormat("invalid short option character '%c'", short_option); + break; + + } + return error; + } + + void + OptionParsingStarting () + { + m_from_expression = false; + } + + const OptionDefinition* + GetDefinitions () + { + return g_option_table; + } + + bool m_from_expression; + + // Options table: Required for subclasses of Options. + + static OptionDefinition g_option_table[]; + + // Instance variables to hold the values for command options. + }; + + virtual + Options * + GetOptions () + { + return &m_options; + } + + CommandObjectThreadReturn (CommandInterpreter &interpreter) : + CommandObjectRaw (interpreter, + "thread return", + "Return from the currently selected frame, short-circuiting execution of the frames below it, with an optional return value," + " or with the -x option from the innermost function evaluation.", + "thread return", + eFlagRequiresFrame | + eFlagTryTargetAPILock | + eFlagProcessMustBeLaunched | + eFlagProcessMustBePaused ), + m_options (interpreter) + { + CommandArgumentEntry arg; + CommandArgumentData expression_arg; + + // Define the first (and only) variant of this arg. + expression_arg.arg_type = eArgTypeExpression; + expression_arg.arg_repetition = eArgRepeatOptional; + + // There is only one variant this argument could be; put it into the argument entry. + arg.push_back (expression_arg); + + // Push the data for the first argument into the m_arguments vector. + m_arguments.push_back (arg); + + + } + + ~CommandObjectThreadReturn() + { + } + +protected: + + bool DoExecute + ( + const char *command, + CommandReturnObject &result + ) + { + // I am going to handle this by hand, because I don't want you to have to say: + // "thread return -- -5". + if (command[0] == '-' && command[1] == 'x') + { + if (command && command[2] != '\0') + result.AppendWarning("Return values ignored when returning from user called expressions"); + + Thread *thread = m_exe_ctx.GetThreadPtr(); + Error error; + error = thread->UnwindInnermostExpression(); + if (!error.Success()) + { + result.AppendErrorWithFormat ("Unwinding expression failed - %s.", error.AsCString()); + result.SetStatus (eReturnStatusFailed); + } + else + { + bool success = thread->SetSelectedFrameByIndexNoisily (0, result.GetOutputStream()); + if (success) + { + m_exe_ctx.SetFrameSP(thread->GetSelectedFrame ()); + result.SetStatus (eReturnStatusSuccessFinishResult); + } + else + { + result.AppendErrorWithFormat ("Could not select 0th frame after unwinding expression."); + result.SetStatus (eReturnStatusFailed); + } + } + return result.Succeeded(); + } + + ValueObjectSP return_valobj_sp; + + StackFrameSP frame_sp = m_exe_ctx.GetFrameSP(); + uint32_t frame_idx = frame_sp->GetFrameIndex(); + + if (frame_sp->IsInlined()) + { + result.AppendError("Don't know how to return from inlined frames."); + result.SetStatus (eReturnStatusFailed); + return false; + } + + if (command && command[0] != '\0') + { + Target *target = m_exe_ctx.GetTargetPtr(); + EvaluateExpressionOptions options; + + options.SetUnwindOnError(true); + options.SetUseDynamic(eNoDynamicValues); + + ExecutionResults exe_results = eExecutionSetupError; + exe_results = target->EvaluateExpression (command, + frame_sp.get(), + return_valobj_sp, + options); + if (exe_results != eExecutionCompleted) + { + if (return_valobj_sp) + result.AppendErrorWithFormat("Error evaluating result expression: %s", return_valobj_sp->GetError().AsCString()); + else + result.AppendErrorWithFormat("Unknown error evaluating result expression."); + result.SetStatus (eReturnStatusFailed); + return false; + + } + } + + Error error; + ThreadSP thread_sp = m_exe_ctx.GetThreadSP(); + const bool broadcast = true; + error = thread_sp->ReturnFromFrame (frame_sp, return_valobj_sp, broadcast); + if (!error.Success()) + { + result.AppendErrorWithFormat("Error returning from frame %d of thread %d: %s.", frame_idx, thread_sp->GetIndexID(), error.AsCString()); + result.SetStatus (eReturnStatusFailed); + return false; + } + + result.SetStatus (eReturnStatusSuccessFinishResult); + return true; + } + + CommandOptions m_options; + +}; +OptionDefinition +CommandObjectThreadReturn::CommandOptions::g_option_table[] = +{ +{ LLDB_OPT_SET_ALL, false, "from-expression", 'x', no_argument, NULL, 0, eArgTypeNone, "Return from the innermost expression evaluation."}, +{ 0, false, NULL, 0, 0, NULL, 0, eArgTypeNone, NULL } +}; + +//------------------------------------------------------------------------- +// CommandObjectMultiwordThread +//------------------------------------------------------------------------- + +CommandObjectMultiwordThread::CommandObjectMultiwordThread (CommandInterpreter &interpreter) : + CommandObjectMultiword (interpreter, + "thread", + "A set of commands for operating on one or more threads within a running process.", + "thread []") +{ + LoadSubCommand ("backtrace", CommandObjectSP (new CommandObjectThreadBacktrace (interpreter))); + LoadSubCommand ("continue", CommandObjectSP (new CommandObjectThreadContinue (interpreter))); + LoadSubCommand ("list", CommandObjectSP (new CommandObjectThreadList (interpreter))); + LoadSubCommand ("return", CommandObjectSP (new CommandObjectThreadReturn (interpreter))); + LoadSubCommand ("select", CommandObjectSP (new CommandObjectThreadSelect (interpreter))); + LoadSubCommand ("until", CommandObjectSP (new CommandObjectThreadUntil (interpreter))); + LoadSubCommand ("step-in", CommandObjectSP (new CommandObjectThreadStepWithTypeAndScope ( + interpreter, + "thread step-in", + "Source level single step in specified thread (current thread, if none specified).", + NULL, + eStepTypeInto, + eStepScopeSource))); + + LoadSubCommand ("step-out", CommandObjectSP (new CommandObjectThreadStepWithTypeAndScope ( + interpreter, + "thread step-out", + "Finish executing the function of the currently selected frame and return to its call site in specified thread (current thread, if none specified).", + NULL, + eStepTypeOut, + eStepScopeSource))); + + LoadSubCommand ("step-over", CommandObjectSP (new CommandObjectThreadStepWithTypeAndScope ( + interpreter, + "thread step-over", + "Source level single step in specified thread (current thread, if none specified), stepping over calls.", + NULL, + eStepTypeOver, + eStepScopeSource))); + + LoadSubCommand ("step-inst", CommandObjectSP (new CommandObjectThreadStepWithTypeAndScope ( + interpreter, + "thread step-inst", + "Single step one instruction in specified thread (current thread, if none specified).", + NULL, + eStepTypeTrace, + eStepScopeInstruction))); + + LoadSubCommand ("step-inst-over", CommandObjectSP (new CommandObjectThreadStepWithTypeAndScope ( + interpreter, + "thread step-inst-over", + "Single step one instruction in specified thread (current thread, if none specified), stepping over calls.", + NULL, + eStepTypeTraceOver, + eStepScopeInstruction))); +} + +CommandObjectMultiwordThread::~CommandObjectMultiwordThread () +{ +} + + diff --git a/contrib/llvm/tools/lldb/source/Commands/CommandObjectThread.h b/contrib/llvm/tools/lldb/source/Commands/CommandObjectThread.h new file mode 100644 index 00000000000..52902ee36c7 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Commands/CommandObjectThread.h @@ -0,0 +1,34 @@ +//===-- CommandObjectThread.h -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_CommandObjectThread_h_ +#define liblldb_CommandObjectThread_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Interpreter/CommandObjectMultiword.h" + +namespace lldb_private { + +class CommandObjectMultiwordThread : public CommandObjectMultiword +{ +public: + + CommandObjectMultiwordThread (CommandInterpreter &interpreter); + + virtual + ~CommandObjectMultiwordThread (); + +}; + +} // namespace lldb_private + +#endif // liblldb_CommandObjectThread_h_ diff --git a/contrib/llvm/tools/lldb/source/Commands/CommandObjectType.cpp b/contrib/llvm/tools/lldb/source/Commands/CommandObjectType.cpp new file mode 100644 index 00000000000..b300f213db0 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Commands/CommandObjectType.cpp @@ -0,0 +1,4112 @@ +//===-- CommandObjectType.cpp ----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "CommandObjectType.h" + +// C Includes + +#include + +// C++ Includes + +#include "lldb/Core/ConstString.h" +#include "lldb/Core/Debugger.h" +#include "lldb/Core/InputReaderEZ.h" +#include "lldb/Core/RegularExpression.h" +#include "lldb/Core/State.h" +#include "lldb/Core/StringList.h" +#include "lldb/DataFormatters/DataVisualization.h" +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Interpreter/CommandObject.h" +#include "lldb/Interpreter/CommandReturnObject.h" +#include "lldb/Interpreter/Options.h" +#include "lldb/Interpreter/OptionGroupFormat.h" + +using namespace lldb; +using namespace lldb_private; + + +class ScriptAddOptions +{ + +public: + + TypeSummaryImpl::Flags m_flags; + + StringList m_target_types; + StringList m_user_source; + + bool m_regex; + + ConstString m_name; + + std::string m_category; + + ScriptAddOptions(const TypeSummaryImpl::Flags& flags, + bool regx, + const ConstString& name, + std::string catg) : + m_flags(flags), + m_regex(regx), + m_name(name), + m_category(catg) + { + } + + typedef std::shared_ptr SharedPointer; + +}; + +class SynthAddOptions +{ + +public: + + bool m_skip_pointers; + bool m_skip_references; + bool m_cascade; + bool m_regex; + StringList m_user_source; + StringList m_target_types; + + std::string m_category; + + SynthAddOptions(bool sptr, + bool sref, + bool casc, + bool regx, + std::string catg) : + m_skip_pointers(sptr), + m_skip_references(sref), + m_cascade(casc), + m_regex(regx), + m_user_source(), + m_target_types(), + m_category(catg) + { + } + + typedef std::shared_ptr SharedPointer; + +}; + + + +class CommandObjectTypeSummaryAdd : public CommandObjectParsed +{ + +private: + + class CommandOptions : public Options + { + public: + + CommandOptions (CommandInterpreter &interpreter) : + Options (interpreter) + { + } + + virtual + ~CommandOptions (){} + + virtual Error + SetOptionValue (uint32_t option_idx, const char *option_arg); + + void + OptionParsingStarting (); + + const OptionDefinition* + GetDefinitions () + { + return g_option_table; + } + + // Options table: Required for subclasses of Options. + + static OptionDefinition g_option_table[]; + + // Instance variables to hold the values for command options. + + TypeSummaryImpl::Flags m_flags; + bool m_regex; + std::string m_format_string; + ConstString m_name; + std::string m_python_script; + std::string m_python_function; + bool m_is_add_script; + std::string m_category; + }; + + CommandOptions m_options; + + virtual Options * + GetOptions () + { + return &m_options; + } + + void + CollectPythonScript(ScriptAddOptions *options, + CommandReturnObject &result); + + bool + Execute_ScriptSummary (Args& command, CommandReturnObject &result); + + bool + Execute_StringSummary (Args& command, CommandReturnObject &result); + +public: + + enum SummaryFormatType + { + eRegularSummary, + eRegexSummary, + eNamedSummary + }; + + CommandObjectTypeSummaryAdd (CommandInterpreter &interpreter); + + ~CommandObjectTypeSummaryAdd () + { + } + + static bool + AddSummary(ConstString type_name, + lldb::TypeSummaryImplSP entry, + SummaryFormatType type, + std::string category, + Error* error = NULL); +protected: + bool + DoExecute (Args& command, CommandReturnObject &result); + +}; + +class CommandObjectTypeSynthAdd : public CommandObjectParsed +{ + +private: + + class CommandOptions : public Options + { + public: + + CommandOptions (CommandInterpreter &interpreter) : + Options (interpreter) + { + } + + virtual + ~CommandOptions (){} + + virtual Error + SetOptionValue (uint32_t option_idx, const char *option_arg) + { + Error error; + const int short_option = m_getopt_table[option_idx].val; + bool success; + + switch (short_option) + { + case 'C': + m_cascade = Args::StringToBoolean(option_arg, true, &success); + if (!success) + error.SetErrorStringWithFormat("invalid value for cascade: %s", option_arg); + break; + case 'P': + handwrite_python = true; + break; + case 'l': + m_class_name = std::string(option_arg); + is_class_based = true; + break; + case 'p': + m_skip_pointers = true; + break; + case 'r': + m_skip_references = true; + break; + case 'w': + m_category = std::string(option_arg); + break; + case 'x': + m_regex = true; + break; + default: + error.SetErrorStringWithFormat ("unrecognized option '%c'", short_option); + break; + } + + return error; + } + + void + OptionParsingStarting () + { + m_cascade = true; + m_class_name = ""; + m_skip_pointers = false; + m_skip_references = false; + m_category = "default"; + is_class_based = false; + handwrite_python = false; + m_regex = false; + } + + const OptionDefinition* + GetDefinitions () + { + return g_option_table; + } + + // Options table: Required for subclasses of Options. + + static OptionDefinition g_option_table[]; + + // Instance variables to hold the values for command options. + + bool m_cascade; + bool m_skip_references; + bool m_skip_pointers; + std::string m_class_name; + bool m_input_python; + std::string m_category; + + bool is_class_based; + + bool handwrite_python; + + bool m_regex; + + }; + + CommandOptions m_options; + + virtual Options * + GetOptions () + { + return &m_options; + } + + void + CollectPythonScript (SynthAddOptions *options, + CommandReturnObject &result); + bool + Execute_HandwritePython (Args& command, CommandReturnObject &result); + + bool + Execute_PythonClass (Args& command, CommandReturnObject &result); + +protected: + bool + DoExecute (Args& command, CommandReturnObject &result); + +public: + + enum SynthFormatType + { + eRegularSynth, + eRegexSynth + }; + + CommandObjectTypeSynthAdd (CommandInterpreter &interpreter); + + ~CommandObjectTypeSynthAdd () + { + } + + static bool + AddSynth(ConstString type_name, + lldb::SyntheticChildrenSP entry, + SynthFormatType type, + std::string category_name, + Error* error); +}; + +//------------------------------------------------------------------------- +// CommandObjectTypeFormatAdd +//------------------------------------------------------------------------- + +class CommandObjectTypeFormatAdd : public CommandObjectParsed +{ + +private: + + class CommandOptions : public OptionGroup + { + public: + + CommandOptions () : + OptionGroup() + { + } + + virtual + ~CommandOptions () + { + } + + virtual uint32_t + GetNumDefinitions (); + + virtual const OptionDefinition* + GetDefinitions () + { + return g_option_table; + } + + virtual void + OptionParsingStarting (CommandInterpreter &interpreter) + { + m_cascade = true; + m_skip_pointers = false; + m_skip_references = false; + } + virtual Error + SetOptionValue (CommandInterpreter &interpreter, + uint32_t option_idx, + const char *option_value) + { + Error error; + const int short_option = g_option_table[option_idx].short_option; + bool success; + + switch (short_option) + { + case 'C': + m_cascade = Args::StringToBoolean(option_value, true, &success); + if (!success) + error.SetErrorStringWithFormat("invalid value for cascade: %s", option_value); + break; + case 'p': + m_skip_pointers = true; + break; + case 'r': + m_skip_references = true; + break; + default: + error.SetErrorStringWithFormat ("unrecognized option '%c'", short_option); + break; + } + + return error; + } + + // Options table: Required for subclasses of Options. + + static OptionDefinition g_option_table[]; + + // Instance variables to hold the values for command options. + + bool m_cascade; + bool m_skip_references; + bool m_skip_pointers; + }; + + OptionGroupOptions m_option_group; + OptionGroupFormat m_format_options; + CommandOptions m_command_options; + + virtual Options * + GetOptions () + { + return &m_option_group; + } + +public: + CommandObjectTypeFormatAdd (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "type format add", + "Add a new formatting style for a type.", + NULL), + m_option_group (interpreter), + m_format_options (eFormatInvalid), + m_command_options () + { + CommandArgumentEntry type_arg; + CommandArgumentData type_style_arg; + + type_style_arg.arg_type = eArgTypeName; + type_style_arg.arg_repetition = eArgRepeatPlus; + + type_arg.push_back (type_style_arg); + + m_arguments.push_back (type_arg); + + SetHelpLong( + "Some examples of using this command.\n" + "We use as reference the following snippet of code:\n" + "\n" + "typedef int Aint;\n" + "typedef float Afloat;\n" + "typedef Aint Bint;\n" + "typedef Afloat Bfloat;\n" + "\n" + "Aint ix = 5;\n" + "Bint iy = 5;\n" + "\n" + "Afloat fx = 3.14;\n" + "BFloat fy = 3.14;\n" + "\n" + "Typing:\n" + "type format add -f hex AInt\n" + "frame variable iy\n" + "will produce an hex display of iy, because no formatter is available for Bint and the one for Aint is used instead\n" + "To prevent this type\n" + "type format add -f hex -C no AInt\n" + "\n" + "A similar reasoning applies to\n" + "type format add -f hex -C no float -p\n" + "which now prints all floats and float&s as hexadecimal, but does not format float*s\n" + "and does not change the default display for Afloat and Bfloat objects.\n" + ); + + // Add the "--format" to all options groups + m_option_group.Append (&m_format_options, OptionGroupFormat::OPTION_GROUP_FORMAT, LLDB_OPT_SET_ALL); + m_option_group.Append (&m_command_options); + m_option_group.Finalize(); + + } + + ~CommandObjectTypeFormatAdd () + { + } + +protected: + bool + DoExecute (Args& command, CommandReturnObject &result) + { + const size_t argc = command.GetArgumentCount(); + + if (argc < 1) + { + result.AppendErrorWithFormat ("%s takes one or more args.\n", m_cmd_name.c_str()); + result.SetStatus(eReturnStatusFailed); + return false; + } + + const Format format = m_format_options.GetFormat(); + if (format == eFormatInvalid) + { + result.AppendErrorWithFormat ("%s needs a valid format.\n", m_cmd_name.c_str()); + result.SetStatus(eReturnStatusFailed); + return false; + } + + TypeFormatImplSP entry; + + entry.reset(new TypeFormatImpl(format, + TypeFormatImpl::Flags().SetCascades(m_command_options.m_cascade). + SetSkipPointers(m_command_options.m_skip_pointers). + SetSkipReferences(m_command_options.m_skip_references))); + + // now I have a valid format, let's add it to every type + + for (size_t i = 0; i < argc; i++) + { + const char* typeA = command.GetArgumentAtIndex(i); + ConstString typeCS(typeA); + if (typeCS) + DataVisualization::ValueFormats::Add(typeCS, entry); + else + { + result.AppendError("empty typenames not allowed"); + result.SetStatus(eReturnStatusFailed); + return false; + } + } + + result.SetStatus(eReturnStatusSuccessFinishNoResult); + return result.Succeeded(); + } +}; + +OptionDefinition +CommandObjectTypeFormatAdd::CommandOptions::g_option_table[] = +{ + { LLDB_OPT_SET_ALL, false, "cascade", 'C', required_argument, NULL, 0, eArgTypeBoolean, "If true, cascade through typedef chains."}, + { LLDB_OPT_SET_ALL, false, "skip-pointers", 'p', no_argument, NULL, 0, eArgTypeNone, "Don't use this format for pointers-to-type objects."}, + { LLDB_OPT_SET_ALL, false, "skip-references", 'r', no_argument, NULL, 0, eArgTypeNone, "Don't use this format for references-to-type objects."}, +}; + + +uint32_t +CommandObjectTypeFormatAdd::CommandOptions::GetNumDefinitions () +{ + return sizeof(g_option_table) / sizeof (OptionDefinition); +} + + +//------------------------------------------------------------------------- +// CommandObjectTypeFormatDelete +//------------------------------------------------------------------------- + +class CommandObjectTypeFormatDelete : public CommandObjectParsed +{ +public: + CommandObjectTypeFormatDelete (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "type format delete", + "Delete an existing formatting style for a type.", + NULL) + { + CommandArgumentEntry type_arg; + CommandArgumentData type_style_arg; + + type_style_arg.arg_type = eArgTypeName; + type_style_arg.arg_repetition = eArgRepeatPlain; + + type_arg.push_back (type_style_arg); + + m_arguments.push_back (type_arg); + + } + + ~CommandObjectTypeFormatDelete () + { + } + +protected: + bool + DoExecute (Args& command, CommandReturnObject &result) + { + const size_t argc = command.GetArgumentCount(); + + if (argc != 1) + { + result.AppendErrorWithFormat ("%s takes 1 arg.\n", m_cmd_name.c_str()); + result.SetStatus(eReturnStatusFailed); + return false; + } + + const char* typeA = command.GetArgumentAtIndex(0); + ConstString typeCS(typeA); + + if (!typeCS) + { + result.AppendError("empty typenames not allowed"); + result.SetStatus(eReturnStatusFailed); + return false; + } + + + if (DataVisualization::ValueFormats::Delete(typeCS)) + { + result.SetStatus(eReturnStatusSuccessFinishNoResult); + return result.Succeeded(); + } + else + { + result.AppendErrorWithFormat ("no custom format for %s.\n", typeA); + result.SetStatus(eReturnStatusFailed); + return false; + } + + } + +}; + +//------------------------------------------------------------------------- +// CommandObjectTypeFormatClear +//------------------------------------------------------------------------- + +class CommandObjectTypeFormatClear : public CommandObjectParsed +{ +public: + CommandObjectTypeFormatClear (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "type format clear", + "Delete all existing format styles.", + NULL) + { + } + + ~CommandObjectTypeFormatClear () + { + } + +protected: + bool + DoExecute (Args& command, CommandReturnObject &result) + { + DataVisualization::ValueFormats::Clear(); + result.SetStatus(eReturnStatusSuccessFinishResult); + return result.Succeeded(); + } + +}; + +//------------------------------------------------------------------------- +// CommandObjectTypeFormatList +//------------------------------------------------------------------------- + +bool CommandObjectTypeFormatList_LoopCallback(void* pt2self, ConstString type, const lldb::TypeFormatImplSP& entry); + +class CommandObjectTypeFormatList; + +struct CommandObjectTypeFormatList_LoopCallbackParam { + CommandObjectTypeFormatList* self; + CommandReturnObject* result; + RegularExpression* regex; + CommandObjectTypeFormatList_LoopCallbackParam(CommandObjectTypeFormatList* S, CommandReturnObject* R, + RegularExpression* X = NULL) : self(S), result(R), regex(X) {} +}; + +class CommandObjectTypeFormatList : public CommandObjectParsed +{ +public: + CommandObjectTypeFormatList (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "type format list", + "Show a list of current formatting styles.", + NULL) + { + CommandArgumentEntry type_arg; + CommandArgumentData type_style_arg; + + type_style_arg.arg_type = eArgTypeName; + type_style_arg.arg_repetition = eArgRepeatOptional; + + type_arg.push_back (type_style_arg); + + m_arguments.push_back (type_arg); + } + + ~CommandObjectTypeFormatList () + { + } + +protected: + bool + DoExecute (Args& command, CommandReturnObject &result) + { + const size_t argc = command.GetArgumentCount(); + + CommandObjectTypeFormatList_LoopCallbackParam *param; + + if (argc == 1) + { + RegularExpression* regex = new RegularExpression(command.GetArgumentAtIndex(0)); + regex->Compile(command.GetArgumentAtIndex(0)); + param = new CommandObjectTypeFormatList_LoopCallbackParam(this,&result,regex); + } + else + param = new CommandObjectTypeFormatList_LoopCallbackParam(this,&result); + DataVisualization::ValueFormats::LoopThrough(CommandObjectTypeFormatList_LoopCallback, param); + delete param; + result.SetStatus(eReturnStatusSuccessFinishResult); + return result.Succeeded(); + } + +private: + + bool + LoopCallback (ConstString type, + const lldb::TypeFormatImplSP& entry, + RegularExpression* regex, + CommandReturnObject *result) + { + if (regex == NULL || regex->Execute(type.AsCString())) + { + result->GetOutputStream().Printf ("%s: %s\n", type.AsCString(), + entry->GetDescription().c_str()); + } + return true; + } + + friend bool CommandObjectTypeFormatList_LoopCallback(void* pt2self, ConstString type, const lldb::TypeFormatImplSP& entry); + +}; + +bool +CommandObjectTypeFormatList_LoopCallback ( + void* pt2self, + ConstString type, + const lldb::TypeFormatImplSP& entry) +{ + CommandObjectTypeFormatList_LoopCallbackParam* param = (CommandObjectTypeFormatList_LoopCallbackParam*)pt2self; + return param->self->LoopCallback(type, entry, param->regex, param->result); +} + + +#ifndef LLDB_DISABLE_PYTHON + +//------------------------------------------------------------------------- +// CommandObjectTypeSummaryAdd +//------------------------------------------------------------------------- + +static const char *g_summary_addreader_instructions = "Enter your Python command(s). Type 'DONE' to end.\n" + "def function (valobj,internal_dict):\n" + " \"\"\"valobj: an SBValue which you want to provide a summary for\n" + " internal_dict: an LLDB support object not to be used\"\"\""; + +class TypeScriptAddInputReader : public InputReaderEZ +{ +private: + DISALLOW_COPY_AND_ASSIGN (TypeScriptAddInputReader); +public: + TypeScriptAddInputReader(Debugger& debugger) : + InputReaderEZ(debugger) + {} + + virtual + ~TypeScriptAddInputReader() + { + } + + virtual void ActivateHandler(HandlerData& data) + { + StreamSP out_stream = data.reader.GetDebugger().GetAsyncOutputStream(); + bool batch_mode = data.reader.GetDebugger().GetCommandInterpreter().GetBatchCommandMode(); + if (!batch_mode) + { + out_stream->Printf ("%s\n", g_summary_addreader_instructions); + if (data.reader.GetPrompt()) + out_stream->Printf ("%s", data.reader.GetPrompt()); + out_stream->Flush(); + } + } + + virtual void ReactivateHandler(HandlerData& data) + { + StreamSP out_stream = data.reader.GetDebugger().GetAsyncOutputStream(); + bool batch_mode = data.reader.GetDebugger().GetCommandInterpreter().GetBatchCommandMode(); + if (data.reader.GetPrompt() && !batch_mode) + { + out_stream->Printf ("%s", data.reader.GetPrompt()); + out_stream->Flush(); + } + } + virtual void GotTokenHandler(HandlerData& data) + { + StreamSP out_stream = data.reader.GetDebugger().GetAsyncOutputStream(); + bool batch_mode = data.reader.GetDebugger().GetCommandInterpreter().GetBatchCommandMode(); + if (data.bytes && data.bytes_len && data.baton) + { + ((ScriptAddOptions*)data.baton)->m_user_source.AppendString(data.bytes, data.bytes_len); + } + if (!data.reader.IsDone() && data.reader.GetPrompt() && !batch_mode) + { + out_stream->Printf ("%s", data.reader.GetPrompt()); + out_stream->Flush(); + } + } + virtual void InterruptHandler(HandlerData& data) + { + StreamSP out_stream = data.reader.GetDebugger().GetAsyncOutputStream(); + bool batch_mode = data.reader.GetDebugger().GetCommandInterpreter().GetBatchCommandMode(); + data.reader.SetIsDone (true); + if (!batch_mode) + { + out_stream->Printf ("Warning: No command attached to breakpoint.\n"); + out_stream->Flush(); + } + } + virtual void EOFHandler(HandlerData& data) + { + data.reader.SetIsDone (true); + } + virtual void DoneHandler(HandlerData& data) + { + StreamSP out_stream = data.reader.GetDebugger().GetAsyncOutputStream(); + ScriptAddOptions *options_ptr = ((ScriptAddOptions*)data.baton); + if (!options_ptr) + { + out_stream->Printf ("internal synchronization information missing or invalid.\n"); + out_stream->Flush(); + return; + } + + ScriptAddOptions::SharedPointer options(options_ptr); // this will ensure that we get rid of the pointer when going out of scope + + ScriptInterpreter *interpreter = data.reader.GetDebugger().GetCommandInterpreter().GetScriptInterpreter(); + if (!interpreter) + { + out_stream->Printf ("no script interpreter.\n"); + out_stream->Flush(); + return; + } + std::string funct_name_str; + if (!interpreter->GenerateTypeScriptFunction (options->m_user_source, + funct_name_str)) + { + out_stream->Printf ("unable to generate a function.\n"); + out_stream->Flush(); + return; + } + if (funct_name_str.empty()) + { + out_stream->Printf ("unable to obtain a valid function name from the script interpreter.\n"); + out_stream->Flush(); + return; + } + // now I have a valid function name, let's add this as script for every type in the list + + TypeSummaryImplSP script_format; + script_format.reset(new ScriptSummaryFormat(options->m_flags, + funct_name_str.c_str(), + options->m_user_source.CopyList(" ").c_str())); + + Error error; + + for (size_t i = 0; i < options->m_target_types.GetSize(); i++) + { + const char *type_name = options->m_target_types.GetStringAtIndex(i); + CommandObjectTypeSummaryAdd::AddSummary(ConstString(type_name), + script_format, + (options->m_regex ? CommandObjectTypeSummaryAdd::eRegexSummary : CommandObjectTypeSummaryAdd::eRegularSummary), + options->m_category, + &error); + if (error.Fail()) + { + out_stream->Printf ("%s", error.AsCString()); + out_stream->Flush(); + return; + } + } + + if (options->m_name) + { + CommandObjectTypeSummaryAdd::AddSummary (options->m_name, + script_format, + CommandObjectTypeSummaryAdd::eNamedSummary, + options->m_category, + &error); + if (error.Fail()) + { + CommandObjectTypeSummaryAdd::AddSummary (options->m_name, + script_format, + CommandObjectTypeSummaryAdd::eNamedSummary, + options->m_category, + &error); + if (error.Fail()) + { + out_stream->Printf ("%s", error.AsCString()); + out_stream->Flush(); + return; + } + } + else + { + out_stream->Printf ("%s", error.AsCString()); + out_stream->Flush(); + return; + } + } + else + { + if (error.AsCString()) + { + out_stream->PutCString (error.AsCString()); + out_stream->Flush(); + } + return; + } + } +}; + +#endif // #ifndef LLDB_DISABLE_PYTHON + +Error +CommandObjectTypeSummaryAdd::CommandOptions::SetOptionValue (uint32_t option_idx, const char *option_arg) +{ + Error error; + const int short_option = m_getopt_table[option_idx].val; + bool success; + + switch (short_option) + { + case 'C': + m_flags.SetCascades(Args::StringToBoolean(option_arg, true, &success)); + if (!success) + error.SetErrorStringWithFormat("invalid value for cascade: %s", option_arg); + break; + case 'e': + m_flags.SetDontShowChildren(false); + break; + case 'v': + m_flags.SetDontShowValue(true); + break; + case 'c': + m_flags.SetShowMembersOneLiner(true); + break; + case 's': + m_format_string = std::string(option_arg); + break; + case 'p': + m_flags.SetSkipPointers(true); + break; + case 'r': + m_flags.SetSkipReferences(true); + break; + case 'x': + m_regex = true; + break; + case 'n': + m_name.SetCString(option_arg); + break; + case 'o': + m_python_script = std::string(option_arg); + m_is_add_script = true; + break; + case 'F': + m_python_function = std::string(option_arg); + m_is_add_script = true; + break; + case 'P': + m_is_add_script = true; + break; + case 'w': + m_category = std::string(option_arg); + break; + case 'O': + m_flags.SetHideItemNames(true); + break; + default: + error.SetErrorStringWithFormat ("unrecognized option '%c'", short_option); + break; + } + + return error; +} + +void +CommandObjectTypeSummaryAdd::CommandOptions::OptionParsingStarting () +{ + m_flags.Clear().SetCascades().SetDontShowChildren().SetDontShowValue(false); + m_flags.SetShowMembersOneLiner(false).SetSkipPointers(false).SetSkipReferences(false).SetHideItemNames(false); + + m_regex = false; + m_name.Clear(); + m_python_script = ""; + m_python_function = ""; + m_format_string = ""; + m_is_add_script = false; + m_category = "default"; +} + +#ifndef LLDB_DISABLE_PYTHON +void +CommandObjectTypeSummaryAdd::CollectPythonScript (ScriptAddOptions *options, + CommandReturnObject &result) +{ + InputReaderSP reader_sp (new TypeScriptAddInputReader(m_interpreter.GetDebugger())); + if (reader_sp && options) + { + + InputReaderEZ::InitializationParameters ipr; + + Error err (reader_sp->Initialize (ipr.SetBaton(options).SetPrompt(" "))); + if (err.Success()) + { + m_interpreter.GetDebugger().PushInputReader (reader_sp); + result.SetStatus (eReturnStatusSuccessFinishNoResult); + } + else + { + result.AppendError (err.AsCString()); + result.SetStatus (eReturnStatusFailed); + } + } + else + { + result.AppendError("out of memory"); + result.SetStatus (eReturnStatusFailed); + } +} + +bool +CommandObjectTypeSummaryAdd::Execute_ScriptSummary (Args& command, CommandReturnObject &result) +{ + const size_t argc = command.GetArgumentCount(); + + if (argc < 1 && !m_options.m_name) + { + result.AppendErrorWithFormat ("%s takes one or more args.\n", m_cmd_name.c_str()); + result.SetStatus(eReturnStatusFailed); + return false; + } + + TypeSummaryImplSP script_format; + + if (!m_options.m_python_function.empty()) // we have a Python function ready to use + { + const char *funct_name = m_options.m_python_function.c_str(); + if (!funct_name || !funct_name[0]) + { + result.AppendError ("function name empty.\n"); + result.SetStatus (eReturnStatusFailed); + return false; + } + + std::string code = (" " + m_options.m_python_function + "(valobj,internal_dict)"); + + script_format.reset(new ScriptSummaryFormat(m_options.m_flags, + funct_name, + code.c_str())); + + ScriptInterpreter *interpreter = m_interpreter.GetScriptInterpreter(); + + if (interpreter && interpreter->CheckObjectExists(funct_name) == false) + result.AppendWarningWithFormat("The provided function \"%s\" does not exist - " + "please define it before attempting to use this summary.\n", + funct_name); + } + else if (!m_options.m_python_script.empty()) // we have a quick 1-line script, just use it + { + ScriptInterpreter *interpreter = m_interpreter.GetScriptInterpreter(); + if (!interpreter) + { + result.AppendError ("script interpreter missing - unable to generate function wrapper.\n"); + result.SetStatus (eReturnStatusFailed); + return false; + } + StringList funct_sl; + funct_sl << m_options.m_python_script.c_str(); + std::string funct_name_str; + if (!interpreter->GenerateTypeScriptFunction (funct_sl, + funct_name_str)) + { + result.AppendError ("unable to generate function wrapper.\n"); + result.SetStatus (eReturnStatusFailed); + return false; + } + if (funct_name_str.empty()) + { + result.AppendError ("script interpreter failed to generate a valid function name.\n"); + result.SetStatus (eReturnStatusFailed); + return false; + } + + std::string code = " " + m_options.m_python_script; + + script_format.reset(new ScriptSummaryFormat(m_options.m_flags, + funct_name_str.c_str(), + code.c_str())); + } + else // use an InputReader to grab Python code from the user + { + ScriptAddOptions *options = new ScriptAddOptions(m_options.m_flags, + m_options.m_regex, + m_options.m_name, + m_options.m_category); + + for (size_t i = 0; i < argc; i++) + { + const char* typeA = command.GetArgumentAtIndex(i); + if (typeA && *typeA) + options->m_target_types << typeA; + else + { + result.AppendError("empty typenames not allowed"); + result.SetStatus(eReturnStatusFailed); + return false; + } + } + + CollectPythonScript(options,result); + return result.Succeeded(); + } + + // if I am here, script_format must point to something good, so I can add that + // as a script summary to all interested parties + + Error error; + + for (size_t i = 0; i < command.GetArgumentCount(); i++) + { + const char *type_name = command.GetArgumentAtIndex(i); + CommandObjectTypeSummaryAdd::AddSummary(ConstString(type_name), + script_format, + (m_options.m_regex ? eRegexSummary : eRegularSummary), + m_options.m_category, + &error); + if (error.Fail()) + { + result.AppendError(error.AsCString()); + result.SetStatus(eReturnStatusFailed); + return false; + } + } + + if (m_options.m_name) + { + AddSummary(m_options.m_name, script_format, eNamedSummary, m_options.m_category, &error); + if (error.Fail()) + { + result.AppendError(error.AsCString()); + result.AppendError("added to types, but not given a name"); + result.SetStatus(eReturnStatusFailed); + return false; + } + } + + return result.Succeeded(); +} + +#endif + + +bool +CommandObjectTypeSummaryAdd::Execute_StringSummary (Args& command, CommandReturnObject &result) +{ + const size_t argc = command.GetArgumentCount(); + + if (argc < 1 && !m_options.m_name) + { + result.AppendErrorWithFormat ("%s takes one or more args.\n", m_cmd_name.c_str()); + result.SetStatus(eReturnStatusFailed); + return false; + } + + if (!m_options.m_flags.GetShowMembersOneLiner() && m_options.m_format_string.empty()) + { + result.AppendError("empty summary strings not allowed"); + result.SetStatus(eReturnStatusFailed); + return false; + } + + const char* format_cstr = (m_options.m_flags.GetShowMembersOneLiner() ? "" : m_options.m_format_string.c_str()); + + // ${var%S} is an endless recursion, prevent it + if (strcmp(format_cstr, "${var%S}") == 0) + { + result.AppendError("recursive summary not allowed"); + result.SetStatus(eReturnStatusFailed); + return false; + } + + Error error; + + lldb::TypeSummaryImplSP entry(new StringSummaryFormat(m_options.m_flags, + format_cstr)); + + if (error.Fail()) + { + result.AppendError(error.AsCString()); + result.SetStatus(eReturnStatusFailed); + return false; + } + + // now I have a valid format, let's add it to every type + + for (size_t i = 0; i < argc; i++) + { + const char* typeA = command.GetArgumentAtIndex(i); + if (!typeA || typeA[0] == '\0') + { + result.AppendError("empty typenames not allowed"); + result.SetStatus(eReturnStatusFailed); + return false; + } + ConstString typeCS(typeA); + + AddSummary(typeCS, + entry, + (m_options.m_regex ? eRegexSummary : eRegularSummary), + m_options.m_category, + &error); + + if (error.Fail()) + { + result.AppendError(error.AsCString()); + result.SetStatus(eReturnStatusFailed); + return false; + } + } + + if (m_options.m_name) + { + AddSummary(m_options.m_name, entry, eNamedSummary, m_options.m_category, &error); + if (error.Fail()) + { + result.AppendError(error.AsCString()); + result.AppendError("added to types, but not given a name"); + result.SetStatus(eReturnStatusFailed); + return false; + } + } + + result.SetStatus(eReturnStatusSuccessFinishNoResult); + return result.Succeeded(); +} + +CommandObjectTypeSummaryAdd::CommandObjectTypeSummaryAdd (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "type summary add", + "Add a new summary style for a type.", + NULL), + m_options (interpreter) +{ + CommandArgumentEntry type_arg; + CommandArgumentData type_style_arg; + + type_style_arg.arg_type = eArgTypeName; + type_style_arg.arg_repetition = eArgRepeatPlus; + + type_arg.push_back (type_style_arg); + + m_arguments.push_back (type_arg); + + SetHelpLong( + "Some examples of using this command.\n" + "We use as reference the following snippet of code:\n" + "struct JustADemo\n" + "{\n" + "int* ptr;\n" + "float value;\n" + "JustADemo(int p = 1, float v = 0.1) : ptr(new int(p)), value(v) {}\n" + "};\n" + "JustADemo object(42,3.14);\n" + "struct AnotherDemo : public JustADemo\n" + "{\n" + "uint8_t byte;\n" + "AnotherDemo(uint8_t b = 'E', int p = 1, float v = 0.1) : JustADemo(p,v), byte(b) {}\n" + "};\n" + "AnotherDemo *another_object = new AnotherDemo('E',42,3.14);\n" + "\n" + "type summary add --summary-string \"the answer is ${*var.ptr}\" JustADemo\n" + "when typing frame variable object you will get \"the answer is 42\"\n" + "type summary add --summary-string \"the answer is ${*var.ptr}, and the question is ${var.value}\" JustADemo\n" + "when typing frame variable object you will get \"the answer is 42 and the question is 3.14\"\n" + "\n" + "Alternatively, you could also say\n" + "type summary add --summary-string \"${var%V} -> ${*var}\" \"int *\"\n" + "and replace the above summary string with\n" + "type summary add --summary-string \"the answer is ${var.ptr}, and the question is ${var.value}\" JustADemo\n" + "to obtain a similar result\n" + "\n" + "To add a summary valid for both JustADemo and AnotherDemo you can use the scoping operator, as in:\n" + "type summary add --summary-string \"${var.ptr}, ${var.value},{${var.byte}}\" JustADemo -C yes\n" + "\n" + "This will be used for both variables of type JustADemo and AnotherDemo. To prevent this, change the -C to read -C no\n" + "If you do not want pointers to be shown using that summary, you can use the -p option, as in:\n" + "type summary add --summary-string \"${var.ptr}, ${var.value},{${var.byte}}\" JustADemo -C yes -p\n" + "A similar option -r exists for references.\n" + "\n" + "If you simply want a one-line summary of the content of your variable, without typing an explicit string to that effect\n" + "you can use the -c option, without giving any summary string:\n" + "type summary add -c JustADemo\n" + "frame variable object\n" + "the output being similar to (ptr=0xsomeaddress, value=3.14)\n" + "\n" + "If you want to display some summary text, but also expand the structure of your object, you can add the -e option, as in:\n" + "type summary add -e --summary-string \"*ptr = ${*var.ptr}\" JustADemo\n" + "Here the value of the int* is displayed, followed by the standard LLDB sequence of children objects, one per line.\n" + "to get an output like:\n" + "\n" + "*ptr = 42 {\n" + " ptr = 0xsomeaddress\n" + " value = 3.14\n" + "}\n" + "\n" + "You can also add Python summaries, in which case you will use lldb public API to gather information from your variables" + "and elaborate them to a meaningful summary inside a script written in Python. The variable object will be passed to your" + "script as an SBValue object. The following example might help you when starting to use the Python summaries feature:\n" + "type summary add JustADemo -o \"value = valobj.GetChildMemberWithName('value'); return 'My value is ' + value.GetValue();\"\n" + "If you prefer to type your scripts on multiple lines, you will use the -P option and then type your script, ending it with " + "the word DONE on a line by itself to mark you're finished editing your code:\n" + "(lldb)type summary add JustADemo -P\n" + " value = valobj.GetChildMemberWithName('value');\n" + " return 'My value is ' + value.GetValue();\n" + "DONE\n" + "(lldb) <-- type further LLDB commands here\n" + ); +} + +bool +CommandObjectTypeSummaryAdd::DoExecute (Args& command, CommandReturnObject &result) +{ + if (m_options.m_is_add_script) + { +#ifndef LLDB_DISABLE_PYTHON + return Execute_ScriptSummary(command, result); +#else + result.AppendError ("python is disabled"); + result.SetStatus(eReturnStatusFailed); + return false; +#endif + } + + return Execute_StringSummary(command, result); +} + +bool +CommandObjectTypeSummaryAdd::AddSummary(ConstString type_name, + TypeSummaryImplSP entry, + SummaryFormatType type, + std::string category_name, + Error* error) +{ + lldb::TypeCategoryImplSP category; + DataVisualization::Categories::GetCategory(ConstString(category_name.c_str()), category); + + if (type == eRegularSummary) + { + std::string type_name_str(type_name.GetCString()); + if (type_name_str.compare(type_name_str.length() - 2, 2, "[]") == 0) + { + type_name_str.resize(type_name_str.length()-2); + if (type_name_str.back() != ' ') + type_name_str.append(" \\[[0-9]+\\]"); + else + type_name_str.append("\\[[0-9]+\\]"); + type_name.SetCString(type_name_str.c_str()); + type = eRegexSummary; + } + } + + if (type == eRegexSummary) + { + RegularExpressionSP typeRX(new RegularExpression()); + if (!typeRX->Compile(type_name.GetCString())) + { + if (error) + error->SetErrorString("regex format error (maybe this is not really a regex?)"); + return false; + } + + category->GetRegexSummaryNavigator()->Delete(type_name); + category->GetRegexSummaryNavigator()->Add(typeRX, entry); + + return true; + } + else if (type == eNamedSummary) + { + // system named summaries do not exist (yet?) + DataVisualization::NamedSummaryFormats::Add(type_name,entry); + return true; + } + else + { + category->GetSummaryNavigator()->Add(type_name, entry); + return true; + } +} + +OptionDefinition +CommandObjectTypeSummaryAdd::CommandOptions::g_option_table[] = +{ + { LLDB_OPT_SET_ALL, false, "category", 'w', required_argument, NULL, 0, eArgTypeName, "Add this to the given category instead of the default one."}, + { LLDB_OPT_SET_ALL, false, "cascade", 'C', required_argument, NULL, 0, eArgTypeBoolean, "If true, cascade through typedef chains."}, + { LLDB_OPT_SET_ALL, false, "no-value", 'v', no_argument, NULL, 0, eArgTypeNone, "Don't show the value, just show the summary, for this type."}, + { LLDB_OPT_SET_ALL, false, "skip-pointers", 'p', no_argument, NULL, 0, eArgTypeNone, "Don't use this format for pointers-to-type objects."}, + { LLDB_OPT_SET_ALL, false, "skip-references", 'r', no_argument, NULL, 0, eArgTypeNone, "Don't use this format for references-to-type objects."}, + { LLDB_OPT_SET_ALL, false, "regex", 'x', no_argument, NULL, 0, eArgTypeNone, "Type names are actually regular expressions."}, + { LLDB_OPT_SET_1 , true, "inline-children", 'c', no_argument, NULL, 0, eArgTypeNone, "If true, inline all child values into summary string."}, + { LLDB_OPT_SET_1 , false, "omit-names", 'O', no_argument, NULL, 0, eArgTypeNone, "If true, omit value names in the summary display."}, + { LLDB_OPT_SET_2 , true, "summary-string", 's', required_argument, NULL, 0, eArgTypeSummaryString, "Summary string used to display text and object contents."}, + { LLDB_OPT_SET_3, false, "python-script", 'o', required_argument, NULL, 0, eArgTypePythonScript, "Give a one-liner Python script as part of the command."}, + { LLDB_OPT_SET_3, false, "python-function", 'F', required_argument, NULL, 0, eArgTypePythonFunction, "Give the name of a Python function to use for this type."}, + { LLDB_OPT_SET_3, false, "input-python", 'P', no_argument, NULL, 0, eArgTypeNone, "Input Python code to use for this type manually."}, + { LLDB_OPT_SET_2 | LLDB_OPT_SET_3, false, "expand", 'e', no_argument, NULL, 0, eArgTypeNone, "Expand aggregate data types to show children on separate lines."}, + { LLDB_OPT_SET_2 | LLDB_OPT_SET_3, false, "name", 'n', required_argument, NULL, 0, eArgTypeName, "A name for this summary string."}, + { 0, false, NULL, 0, 0, NULL, 0, eArgTypeNone, NULL } +}; + + +//------------------------------------------------------------------------- +// CommandObjectTypeSummaryDelete +//------------------------------------------------------------------------- + +class CommandObjectTypeSummaryDelete : public CommandObjectParsed +{ +private: + class CommandOptions : public Options + { + public: + + CommandOptions (CommandInterpreter &interpreter) : + Options (interpreter) + { + } + + virtual + ~CommandOptions (){} + + virtual Error + SetOptionValue (uint32_t option_idx, const char *option_arg) + { + Error error; + const int short_option = m_getopt_table[option_idx].val; + + switch (short_option) + { + case 'a': + m_delete_all = true; + break; + case 'w': + m_category = std::string(option_arg); + break; + default: + error.SetErrorStringWithFormat ("unrecognized option '%c'", short_option); + break; + } + + return error; + } + + void + OptionParsingStarting () + { + m_delete_all = false; + m_category = "default"; + } + + const OptionDefinition* + GetDefinitions () + { + return g_option_table; + } + + // Options table: Required for subclasses of Options. + + static OptionDefinition g_option_table[]; + + // Instance variables to hold the values for command options. + + bool m_delete_all; + std::string m_category; + + }; + + CommandOptions m_options; + + virtual Options * + GetOptions () + { + return &m_options; + } + + static bool + PerCategoryCallback(void* param, + const lldb::TypeCategoryImplSP& category_sp) + { + ConstString *name = (ConstString*)param; + category_sp->Delete(*name, eFormatCategoryItemSummary | eFormatCategoryItemRegexSummary); + return true; + } + +public: + CommandObjectTypeSummaryDelete (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "type summary delete", + "Delete an existing summary style for a type.", + NULL), + m_options(interpreter) + { + CommandArgumentEntry type_arg; + CommandArgumentData type_style_arg; + + type_style_arg.arg_type = eArgTypeName; + type_style_arg.arg_repetition = eArgRepeatPlain; + + type_arg.push_back (type_style_arg); + + m_arguments.push_back (type_arg); + + } + + ~CommandObjectTypeSummaryDelete () + { + } + +protected: + bool + DoExecute (Args& command, CommandReturnObject &result) + { + const size_t argc = command.GetArgumentCount(); + + if (argc != 1) + { + result.AppendErrorWithFormat ("%s takes 1 arg.\n", m_cmd_name.c_str()); + result.SetStatus(eReturnStatusFailed); + return false; + } + + const char* typeA = command.GetArgumentAtIndex(0); + ConstString typeCS(typeA); + + if (!typeCS) + { + result.AppendError("empty typenames not allowed"); + result.SetStatus(eReturnStatusFailed); + return false; + } + + if (m_options.m_delete_all) + { + DataVisualization::Categories::LoopThrough(PerCategoryCallback, &typeCS); + result.SetStatus(eReturnStatusSuccessFinishNoResult); + return result.Succeeded(); + } + + lldb::TypeCategoryImplSP category; + DataVisualization::Categories::GetCategory(ConstString(m_options.m_category.c_str()), category); + + bool delete_category = category->Delete(typeCS, + eFormatCategoryItemSummary | eFormatCategoryItemRegexSummary); + bool delete_named = DataVisualization::NamedSummaryFormats::Delete(typeCS); + + if (delete_category || delete_named) + { + result.SetStatus(eReturnStatusSuccessFinishNoResult); + return result.Succeeded(); + } + else + { + result.AppendErrorWithFormat ("no custom summary for %s.\n", typeA); + result.SetStatus(eReturnStatusFailed); + return false; + } + + } +}; + +OptionDefinition +CommandObjectTypeSummaryDelete::CommandOptions::g_option_table[] = +{ + { LLDB_OPT_SET_1, false, "all", 'a', no_argument, NULL, 0, eArgTypeNone, "Delete from every category."}, + { LLDB_OPT_SET_2, false, "category", 'w', required_argument, NULL, 0, eArgTypeName, "Delete from given category."}, + { 0, false, NULL, 0, 0, NULL, 0, eArgTypeNone, NULL } +}; + +class CommandObjectTypeSummaryClear : public CommandObjectParsed +{ +private: + + class CommandOptions : public Options + { + public: + + CommandOptions (CommandInterpreter &interpreter) : + Options (interpreter) + { + } + + virtual + ~CommandOptions (){} + + virtual Error + SetOptionValue (uint32_t option_idx, const char *option_arg) + { + Error error; + const int short_option = m_getopt_table[option_idx].val; + + switch (short_option) + { + case 'a': + m_delete_all = true; + break; + default: + error.SetErrorStringWithFormat ("unrecognized option '%c'", short_option); + break; + } + + return error; + } + + void + OptionParsingStarting () + { + m_delete_all = false; + } + + const OptionDefinition* + GetDefinitions () + { + return g_option_table; + } + + // Options table: Required for subclasses of Options. + + static OptionDefinition g_option_table[]; + + // Instance variables to hold the values for command options. + + bool m_delete_all; + bool m_delete_named; + }; + + CommandOptions m_options; + + virtual Options * + GetOptions () + { + return &m_options; + } + + static bool + PerCategoryCallback(void* param, + const lldb::TypeCategoryImplSP& cate) + { + cate->GetSummaryNavigator()->Clear(); + cate->GetRegexSummaryNavigator()->Clear(); + return true; + + } + +public: + CommandObjectTypeSummaryClear (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "type summary clear", + "Delete all existing summary styles.", + NULL), + m_options(interpreter) + { + } + + ~CommandObjectTypeSummaryClear () + { + } + +protected: + bool + DoExecute (Args& command, CommandReturnObject &result) + { + + if (m_options.m_delete_all) + DataVisualization::Categories::LoopThrough(PerCategoryCallback, NULL); + + else + { + lldb::TypeCategoryImplSP category; + if (command.GetArgumentCount() > 0) + { + const char* cat_name = command.GetArgumentAtIndex(0); + ConstString cat_nameCS(cat_name); + DataVisualization::Categories::GetCategory(cat_nameCS, category); + } + else + DataVisualization::Categories::GetCategory(ConstString(NULL), category); + category->Clear(eFormatCategoryItemSummary | eFormatCategoryItemRegexSummary); + } + + DataVisualization::NamedSummaryFormats::Clear(); + + result.SetStatus(eReturnStatusSuccessFinishResult); + return result.Succeeded(); + } + +}; + +OptionDefinition +CommandObjectTypeSummaryClear::CommandOptions::g_option_table[] = +{ + { LLDB_OPT_SET_ALL, false, "all", 'a', no_argument, NULL, 0, eArgTypeNone, "Clear every category."}, + { 0, false, NULL, 0, 0, NULL, 0, eArgTypeNone, NULL } +}; + +//------------------------------------------------------------------------- +// CommandObjectTypeSummaryList +//------------------------------------------------------------------------- + +bool CommandObjectTypeSummaryList_LoopCallback(void* pt2self, ConstString type, const StringSummaryFormat::SharedPointer& entry); +bool CommandObjectTypeRXSummaryList_LoopCallback(void* pt2self, lldb::RegularExpressionSP regex, const StringSummaryFormat::SharedPointer& entry); + +class CommandObjectTypeSummaryList; + +struct CommandObjectTypeSummaryList_LoopCallbackParam { + CommandObjectTypeSummaryList* self; + CommandReturnObject* result; + RegularExpression* regex; + RegularExpression* cate_regex; + CommandObjectTypeSummaryList_LoopCallbackParam(CommandObjectTypeSummaryList* S, CommandReturnObject* R, + RegularExpression* X = NULL, + RegularExpression* CX = NULL) : self(S), result(R), regex(X), cate_regex(CX) {} +}; + +class CommandObjectTypeSummaryList : public CommandObjectParsed +{ + + class CommandOptions : public Options + { + public: + + CommandOptions (CommandInterpreter &interpreter) : + Options (interpreter) + { + } + + virtual + ~CommandOptions (){} + + virtual Error + SetOptionValue (uint32_t option_idx, const char *option_arg) + { + Error error; + const int short_option = m_getopt_table[option_idx].val; + + switch (short_option) + { + case 'w': + m_category_regex = std::string(option_arg); + break; + default: + error.SetErrorStringWithFormat ("unrecognized option '%c'", short_option); + break; + } + + return error; + } + + void + OptionParsingStarting () + { + m_category_regex = ""; + } + + const OptionDefinition* + GetDefinitions () + { + return g_option_table; + } + + // Options table: Required for subclasses of Options. + + static OptionDefinition g_option_table[]; + + // Instance variables to hold the values for command options. + + std::string m_category_regex; + + }; + + CommandOptions m_options; + + virtual Options * + GetOptions () + { + return &m_options; + } + +public: + CommandObjectTypeSummaryList (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "type summary list", + "Show a list of current summary styles.", + NULL), + m_options(interpreter) + { + CommandArgumentEntry type_arg; + CommandArgumentData type_style_arg; + + type_style_arg.arg_type = eArgTypeName; + type_style_arg.arg_repetition = eArgRepeatOptional; + + type_arg.push_back (type_style_arg); + + m_arguments.push_back (type_arg); + } + + ~CommandObjectTypeSummaryList () + { + } + +protected: + bool + DoExecute (Args& command, CommandReturnObject &result) + { + const size_t argc = command.GetArgumentCount(); + + CommandObjectTypeSummaryList_LoopCallbackParam *param; + RegularExpression* cate_regex = + m_options.m_category_regex.empty() ? NULL : + new RegularExpression(m_options.m_category_regex.c_str()); + + if (argc == 1) + { + RegularExpression* regex = new RegularExpression(command.GetArgumentAtIndex(0)); + regex->Compile(command.GetArgumentAtIndex(0)); + param = new CommandObjectTypeSummaryList_LoopCallbackParam(this,&result,regex,cate_regex); + } + else + param = new CommandObjectTypeSummaryList_LoopCallbackParam(this,&result,NULL,cate_regex); + + DataVisualization::Categories::LoopThrough(PerCategoryCallback,param); + + if (DataVisualization::NamedSummaryFormats::GetCount() > 0) + { + result.GetOutputStream().Printf("Named summaries:\n"); + if (argc == 1) + { + RegularExpression* regex = new RegularExpression(command.GetArgumentAtIndex(0)); + regex->Compile(command.GetArgumentAtIndex(0)); + param = new CommandObjectTypeSummaryList_LoopCallbackParam(this,&result,regex); + } + else + param = new CommandObjectTypeSummaryList_LoopCallbackParam(this,&result); + DataVisualization::NamedSummaryFormats::LoopThrough(CommandObjectTypeSummaryList_LoopCallback, param); + delete param; + } + + if (cate_regex) + delete cate_regex; + + result.SetStatus(eReturnStatusSuccessFinishResult); + return result.Succeeded(); + } + +private: + + static bool + PerCategoryCallback(void* param_vp, + const lldb::TypeCategoryImplSP& cate) + { + + CommandObjectTypeSummaryList_LoopCallbackParam* param = + (CommandObjectTypeSummaryList_LoopCallbackParam*)param_vp; + CommandReturnObject* result = param->result; + + const char* cate_name = cate->GetName(); + + // if the category is disabled or empty and there is no regex, just skip it + if ((cate->IsEnabled() == false || cate->GetCount(eFormatCategoryItemSummary | eFormatCategoryItemRegexSummary) == 0) && param->cate_regex == NULL) + return true; + + // if we have a regex and this category does not match it, just skip it + if(param->cate_regex != NULL && strcmp(cate_name,param->cate_regex->GetText()) != 0 && param->cate_regex->Execute(cate_name) == false) + return true; + + result->GetOutputStream().Printf("-----------------------\nCategory: %s (%s)\n-----------------------\n", + cate_name, + (cate->IsEnabled() ? "enabled" : "disabled")); + + cate->GetSummaryNavigator()->LoopThrough(CommandObjectTypeSummaryList_LoopCallback, param_vp); + + if (cate->GetRegexSummaryNavigator()->GetCount() > 0) + { + result->GetOutputStream().Printf("Regex-based summaries (slower):\n"); + cate->GetRegexSummaryNavigator()->LoopThrough(CommandObjectTypeRXSummaryList_LoopCallback, param_vp); + } + return true; + } + + + bool + LoopCallback (const char* type, + const lldb::TypeSummaryImplSP& entry, + RegularExpression* regex, + CommandReturnObject *result) + { + if (regex == NULL || strcmp(type,regex->GetText()) == 0 || regex->Execute(type)) + result->GetOutputStream().Printf ("%s: %s\n", type, entry->GetDescription().c_str()); + return true; + } + + friend bool CommandObjectTypeSummaryList_LoopCallback(void* pt2self, ConstString type, const lldb::TypeSummaryImplSP& entry); + friend bool CommandObjectTypeRXSummaryList_LoopCallback(void* pt2self, lldb::RegularExpressionSP regex, const lldb::TypeSummaryImplSP& entry); +}; + +bool +CommandObjectTypeSummaryList_LoopCallback ( + void* pt2self, + ConstString type, + const lldb::TypeSummaryImplSP& entry) +{ + CommandObjectTypeSummaryList_LoopCallbackParam* param = (CommandObjectTypeSummaryList_LoopCallbackParam*)pt2self; + return param->self->LoopCallback(type.AsCString(), entry, param->regex, param->result); +} + +bool +CommandObjectTypeRXSummaryList_LoopCallback ( + void* pt2self, + lldb::RegularExpressionSP regex, + const lldb::TypeSummaryImplSP& entry) +{ + CommandObjectTypeSummaryList_LoopCallbackParam* param = (CommandObjectTypeSummaryList_LoopCallbackParam*)pt2self; + return param->self->LoopCallback(regex->GetText(), entry, param->regex, param->result); +} + +OptionDefinition +CommandObjectTypeSummaryList::CommandOptions::g_option_table[] = +{ + { LLDB_OPT_SET_ALL, false, "category-regex", 'w', required_argument, NULL, 0, eArgTypeName, "Only show categories matching this filter."}, + { 0, false, NULL, 0, 0, NULL, 0, eArgTypeNone, NULL } +}; + +//------------------------------------------------------------------------- +// CommandObjectTypeCategoryEnable +//------------------------------------------------------------------------- + +class CommandObjectTypeCategoryEnable : public CommandObjectParsed +{ +public: + CommandObjectTypeCategoryEnable (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "type category enable", + "Enable a category as a source of formatters.", + NULL) + { + CommandArgumentEntry type_arg; + CommandArgumentData type_style_arg; + + type_style_arg.arg_type = eArgTypeName; + type_style_arg.arg_repetition = eArgRepeatPlus; + + type_arg.push_back (type_style_arg); + + m_arguments.push_back (type_arg); + + } + + ~CommandObjectTypeCategoryEnable () + { + } + +protected: + bool + DoExecute (Args& command, CommandReturnObject &result) + { + const size_t argc = command.GetArgumentCount(); + + if (argc < 1) + { + result.AppendErrorWithFormat ("%s takes 1 or more args.\n", m_cmd_name.c_str()); + result.SetStatus(eReturnStatusFailed); + return false; + } + + if (argc == 1 && strcmp(command.GetArgumentAtIndex(0),"*") == 0) + { + // we want to make sure to enable "system" last and "default" first + DataVisualization::Categories::Enable(ConstString("default"), TypeCategoryMap::First); + uint32_t num_categories = DataVisualization::Categories::GetCount(); + for (uint32_t i = 0; i < num_categories; i++) + { + lldb::TypeCategoryImplSP category_sp = DataVisualization::Categories::GetCategoryAtIndex(i); + if (category_sp) + { + if ( ::strcmp(category_sp->GetName(), "system") == 0 || + ::strcmp(category_sp->GetName(), "default") == 0 ) + continue; + else + DataVisualization::Categories::Enable(category_sp, TypeCategoryMap::Default); + } + } + DataVisualization::Categories::Enable(ConstString("system"), TypeCategoryMap::Last); + } + else + { + for (int i = argc - 1; i >= 0; i--) + { + const char* typeA = command.GetArgumentAtIndex(i); + ConstString typeCS(typeA); + + if (!typeCS) + { + result.AppendError("empty category name not allowed"); + result.SetStatus(eReturnStatusFailed); + return false; + } + DataVisualization::Categories::Enable(typeCS); + lldb::TypeCategoryImplSP cate; + if (DataVisualization::Categories::GetCategory(typeCS, cate) && cate.get()) + { + if (cate->GetCount() == 0) + { + result.AppendWarning("empty category enabled (typo?)"); + } + } + } + } + + result.SetStatus(eReturnStatusSuccessFinishResult); + return result.Succeeded(); + } + +}; + +//------------------------------------------------------------------------- +// CommandObjectTypeCategoryDelete +//------------------------------------------------------------------------- + +class CommandObjectTypeCategoryDelete : public CommandObjectParsed +{ +public: + CommandObjectTypeCategoryDelete (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "type category delete", + "Delete a category and all associated formatters.", + NULL) + { + CommandArgumentEntry type_arg; + CommandArgumentData type_style_arg; + + type_style_arg.arg_type = eArgTypeName; + type_style_arg.arg_repetition = eArgRepeatPlus; + + type_arg.push_back (type_style_arg); + + m_arguments.push_back (type_arg); + + } + + ~CommandObjectTypeCategoryDelete () + { + } + +protected: + bool + DoExecute (Args& command, CommandReturnObject &result) + { + const size_t argc = command.GetArgumentCount(); + + if (argc < 1) + { + result.AppendErrorWithFormat ("%s takes 1 or more arg.\n", m_cmd_name.c_str()); + result.SetStatus(eReturnStatusFailed); + return false; + } + + bool success = true; + + // the order is not relevant here + for (int i = argc - 1; i >= 0; i--) + { + const char* typeA = command.GetArgumentAtIndex(i); + ConstString typeCS(typeA); + + if (!typeCS) + { + result.AppendError("empty category name not allowed"); + result.SetStatus(eReturnStatusFailed); + return false; + } + if (!DataVisualization::Categories::Delete(typeCS)) + success = false; // keep deleting even if we hit an error + } + if (success) + { + result.SetStatus(eReturnStatusSuccessFinishResult); + return result.Succeeded(); + } + else + { + result.AppendError("cannot delete one or more categories\n"); + result.SetStatus(eReturnStatusFailed); + return false; + } + } +}; + +//------------------------------------------------------------------------- +// CommandObjectTypeCategoryDisable +//------------------------------------------------------------------------- + +class CommandObjectTypeCategoryDisable : public CommandObjectParsed +{ +public: + CommandObjectTypeCategoryDisable (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "type category disable", + "Disable a category as a source of formatters.", + NULL) + { + CommandArgumentEntry type_arg; + CommandArgumentData type_style_arg; + + type_style_arg.arg_type = eArgTypeName; + type_style_arg.arg_repetition = eArgRepeatPlus; + + type_arg.push_back (type_style_arg); + + m_arguments.push_back (type_arg); + + } + + ~CommandObjectTypeCategoryDisable () + { + } + +protected: + bool + DoExecute (Args& command, CommandReturnObject &result) + { + const size_t argc = command.GetArgumentCount(); + + if (argc < 1) + { + result.AppendErrorWithFormat ("%s takes 1 or more args.\n", m_cmd_name.c_str()); + result.SetStatus(eReturnStatusFailed); + return false; + } + + if (argc == 1 && strcmp(command.GetArgumentAtIndex(0),"*") == 0) + { + uint32_t num_categories = DataVisualization::Categories::GetCount(); + for (uint32_t i = 0; i < num_categories; i++) + { + lldb::TypeCategoryImplSP category_sp = DataVisualization::Categories::GetCategoryAtIndex(i); + // no need to check if the category is enabled - disabling a disabled category has no effect + if (category_sp) + DataVisualization::Categories::Disable(category_sp); + } + } + else + { + // the order is not relevant here + for (int i = argc - 1; i >= 0; i--) + { + const char* typeA = command.GetArgumentAtIndex(i); + ConstString typeCS(typeA); + + if (!typeCS) + { + result.AppendError("empty category name not allowed"); + result.SetStatus(eReturnStatusFailed); + return false; + } + DataVisualization::Categories::Disable(typeCS); + } + } + + result.SetStatus(eReturnStatusSuccessFinishResult); + return result.Succeeded(); + } + +}; + +//------------------------------------------------------------------------- +// CommandObjectTypeCategoryList +//------------------------------------------------------------------------- + +class CommandObjectTypeCategoryList : public CommandObjectParsed +{ +private: + + struct CommandObjectTypeCategoryList_CallbackParam + { + CommandReturnObject* result; + RegularExpression* regex; + + CommandObjectTypeCategoryList_CallbackParam(CommandReturnObject* res, + RegularExpression* rex = NULL) : + result(res), + regex(rex) + { + } + + }; + + static bool + PerCategoryCallback(void* param_vp, + const lldb::TypeCategoryImplSP& cate) + { + CommandObjectTypeCategoryList_CallbackParam* param = + (CommandObjectTypeCategoryList_CallbackParam*)param_vp; + CommandReturnObject* result = param->result; + RegularExpression* regex = param->regex; + + const char* cate_name = cate->GetName(); + + if (regex == NULL || strcmp(cate_name, regex->GetText()) == 0 || regex->Execute(cate_name)) + result->GetOutputStream().Printf("Category %s is%s enabled\n", + cate_name, + (cate->IsEnabled() ? "" : " not")); + return true; + } +public: + CommandObjectTypeCategoryList (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "type category list", + "Provide a list of all existing categories.", + NULL) + { + CommandArgumentEntry type_arg; + CommandArgumentData type_style_arg; + + type_style_arg.arg_type = eArgTypeName; + type_style_arg.arg_repetition = eArgRepeatOptional; + + type_arg.push_back (type_style_arg); + + m_arguments.push_back (type_arg); + } + + ~CommandObjectTypeCategoryList () + { + } + +protected: + bool + DoExecute (Args& command, CommandReturnObject &result) + { + const size_t argc = command.GetArgumentCount(); + RegularExpression* regex = NULL; + + if (argc == 0) + ; + else if (argc == 1) + regex = new RegularExpression(command.GetArgumentAtIndex(0)); + else + { + result.AppendErrorWithFormat ("%s takes 0 or one arg.\n", m_cmd_name.c_str()); + result.SetStatus(eReturnStatusFailed); + return false; + } + + CommandObjectTypeCategoryList_CallbackParam param(&result, + regex); + + DataVisualization::Categories::LoopThrough(PerCategoryCallback, ¶m); + + if (regex) + delete regex; + + result.SetStatus(eReturnStatusSuccessFinishResult); + return result.Succeeded(); + } + +}; + +//------------------------------------------------------------------------- +// CommandObjectTypeFilterList +//------------------------------------------------------------------------- + +bool CommandObjectTypeFilterList_LoopCallback(void* pt2self, ConstString type, const SyntheticChildren::SharedPointer& entry); +bool CommandObjectTypeFilterRXList_LoopCallback(void* pt2self, lldb::RegularExpressionSP regex, const SyntheticChildren::SharedPointer& entry); + +class CommandObjectTypeFilterList; + +struct CommandObjectTypeFilterList_LoopCallbackParam { + CommandObjectTypeFilterList* self; + CommandReturnObject* result; + RegularExpression* regex; + RegularExpression* cate_regex; + CommandObjectTypeFilterList_LoopCallbackParam(CommandObjectTypeFilterList* S, CommandReturnObject* R, + RegularExpression* X = NULL, + RegularExpression* CX = NULL) : self(S), result(R), regex(X), cate_regex(CX) {} +}; + +class CommandObjectTypeFilterList : public CommandObjectParsed +{ + + class CommandOptions : public Options + { + public: + + CommandOptions (CommandInterpreter &interpreter) : + Options (interpreter) + { + } + + virtual + ~CommandOptions (){} + + virtual Error + SetOptionValue (uint32_t option_idx, const char *option_arg) + { + Error error; + const int short_option = m_getopt_table[option_idx].val; + + switch (short_option) + { + case 'w': + m_category_regex = std::string(option_arg); + break; + default: + error.SetErrorStringWithFormat ("unrecognized option '%c'", short_option); + break; + } + + return error; + } + + void + OptionParsingStarting () + { + m_category_regex = ""; + } + + const OptionDefinition* + GetDefinitions () + { + return g_option_table; + } + + // Options table: Required for subclasses of Options. + + static OptionDefinition g_option_table[]; + + // Instance variables to hold the values for command options. + + std::string m_category_regex; + + }; + + CommandOptions m_options; + + virtual Options * + GetOptions () + { + return &m_options; + } + +public: + CommandObjectTypeFilterList (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "type filter list", + "Show a list of current filters.", + NULL), + m_options(interpreter) + { + CommandArgumentEntry type_arg; + CommandArgumentData type_style_arg; + + type_style_arg.arg_type = eArgTypeName; + type_style_arg.arg_repetition = eArgRepeatOptional; + + type_arg.push_back (type_style_arg); + + m_arguments.push_back (type_arg); + } + + ~CommandObjectTypeFilterList () + { + } + +protected: + bool + DoExecute (Args& command, CommandReturnObject &result) + { + const size_t argc = command.GetArgumentCount(); + + CommandObjectTypeFilterList_LoopCallbackParam *param; + RegularExpression* cate_regex = + m_options.m_category_regex.empty() ? NULL : + new RegularExpression(m_options.m_category_regex.c_str()); + + if (argc == 1) + { + RegularExpression* regex = new RegularExpression(command.GetArgumentAtIndex(0)); + regex->Compile(command.GetArgumentAtIndex(0)); + param = new CommandObjectTypeFilterList_LoopCallbackParam(this,&result,regex,cate_regex); + } + else + param = new CommandObjectTypeFilterList_LoopCallbackParam(this,&result,NULL,cate_regex); + + DataVisualization::Categories::LoopThrough(PerCategoryCallback,param); + + if (cate_regex) + delete cate_regex; + + result.SetStatus(eReturnStatusSuccessFinishResult); + return result.Succeeded(); + } + +private: + + static bool + PerCategoryCallback(void* param_vp, + const lldb::TypeCategoryImplSP& cate) + { + + const char* cate_name = cate->GetName(); + + CommandObjectTypeFilterList_LoopCallbackParam* param = + (CommandObjectTypeFilterList_LoopCallbackParam*)param_vp; + CommandReturnObject* result = param->result; + + // if the category is disabled or empty and there is no regex, just skip it + if ((cate->IsEnabled() == false || cate->GetCount(eFormatCategoryItemFilter | eFormatCategoryItemRegexFilter) == 0) && param->cate_regex == NULL) + return true; + + // if we have a regex and this category does not match it, just skip it + if(param->cate_regex != NULL && strcmp(cate_name,param->cate_regex->GetText()) != 0 && param->cate_regex->Execute(cate_name) == false) + return true; + + result->GetOutputStream().Printf("-----------------------\nCategory: %s (%s)\n-----------------------\n", + cate_name, + (cate->IsEnabled() ? "enabled" : "disabled")); + + cate->GetFilterNavigator()->LoopThrough(CommandObjectTypeFilterList_LoopCallback, param_vp); + + if (cate->GetRegexFilterNavigator()->GetCount() > 0) + { + result->GetOutputStream().Printf("Regex-based filters (slower):\n"); + cate->GetRegexFilterNavigator()->LoopThrough(CommandObjectTypeFilterRXList_LoopCallback, param_vp); + } + + return true; + } + + bool + LoopCallback (const char* type, + const SyntheticChildren::SharedPointer& entry, + RegularExpression* regex, + CommandReturnObject *result) + { + if (regex == NULL || regex->Execute(type)) + result->GetOutputStream().Printf ("%s: %s\n", type, entry->GetDescription().c_str()); + return true; + } + + friend bool CommandObjectTypeFilterList_LoopCallback(void* pt2self, ConstString type, const SyntheticChildren::SharedPointer& entry); + friend bool CommandObjectTypeFilterRXList_LoopCallback(void* pt2self, lldb::RegularExpressionSP regex, const SyntheticChildren::SharedPointer& entry); +}; + +bool +CommandObjectTypeFilterList_LoopCallback (void* pt2self, + ConstString type, + const SyntheticChildren::SharedPointer& entry) +{ + CommandObjectTypeFilterList_LoopCallbackParam* param = (CommandObjectTypeFilterList_LoopCallbackParam*)pt2self; + return param->self->LoopCallback(type.AsCString(), entry, param->regex, param->result); +} + +bool +CommandObjectTypeFilterRXList_LoopCallback (void* pt2self, + lldb::RegularExpressionSP regex, + const SyntheticChildren::SharedPointer& entry) +{ + CommandObjectTypeFilterList_LoopCallbackParam* param = (CommandObjectTypeFilterList_LoopCallbackParam*)pt2self; + return param->self->LoopCallback(regex->GetText(), entry, param->regex, param->result); +} + + +OptionDefinition +CommandObjectTypeFilterList::CommandOptions::g_option_table[] = +{ + { LLDB_OPT_SET_ALL, false, "category-regex", 'w', required_argument, NULL, 0, eArgTypeName, "Only show categories matching this filter."}, + { 0, false, NULL, 0, 0, NULL, 0, eArgTypeNone, NULL } +}; + +#ifndef LLDB_DISABLE_PYTHON + +//------------------------------------------------------------------------- +// CommandObjectTypeSynthList +//------------------------------------------------------------------------- + +bool CommandObjectTypeSynthList_LoopCallback(void* pt2self, ConstString type, const SyntheticChildren::SharedPointer& entry); +bool CommandObjectTypeSynthRXList_LoopCallback(void* pt2self, lldb::RegularExpressionSP regex, const SyntheticChildren::SharedPointer& entry); + +class CommandObjectTypeSynthList; + +struct CommandObjectTypeSynthList_LoopCallbackParam { + CommandObjectTypeSynthList* self; + CommandReturnObject* result; + RegularExpression* regex; + RegularExpression* cate_regex; + CommandObjectTypeSynthList_LoopCallbackParam(CommandObjectTypeSynthList* S, CommandReturnObject* R, + RegularExpression* X = NULL, + RegularExpression* CX = NULL) : self(S), result(R), regex(X), cate_regex(CX) {} +}; + +class CommandObjectTypeSynthList : public CommandObjectParsed +{ + + class CommandOptions : public Options + { + public: + + CommandOptions (CommandInterpreter &interpreter) : + Options (interpreter) + { + } + + virtual + ~CommandOptions (){} + + virtual Error + SetOptionValue (uint32_t option_idx, const char *option_arg) + { + Error error; + const int short_option = m_getopt_table[option_idx].val; + + switch (short_option) + { + case 'w': + m_category_regex = std::string(option_arg); + break; + default: + error.SetErrorStringWithFormat ("unrecognized option '%c'", short_option); + break; + } + + return error; + } + + void + OptionParsingStarting () + { + m_category_regex = ""; + } + + const OptionDefinition* + GetDefinitions () + { + return g_option_table; + } + + // Options table: Required for subclasses of Options. + + static OptionDefinition g_option_table[]; + + // Instance variables to hold the values for command options. + + std::string m_category_regex; + + }; + + CommandOptions m_options; + + virtual Options * + GetOptions () + { + return &m_options; + } + +public: + CommandObjectTypeSynthList (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "type synthetic list", + "Show a list of current synthetic providers.", + NULL), + m_options(interpreter) + { + CommandArgumentEntry type_arg; + CommandArgumentData type_style_arg; + + type_style_arg.arg_type = eArgTypeName; + type_style_arg.arg_repetition = eArgRepeatOptional; + + type_arg.push_back (type_style_arg); + + m_arguments.push_back (type_arg); + } + + ~CommandObjectTypeSynthList () + { + } + +protected: + bool + DoExecute (Args& command, CommandReturnObject &result) + { + const size_t argc = command.GetArgumentCount(); + + CommandObjectTypeSynthList_LoopCallbackParam *param; + RegularExpression* cate_regex = + m_options.m_category_regex.empty() ? NULL : + new RegularExpression(m_options.m_category_regex.c_str()); + + if (argc == 1) + { + RegularExpression* regex = new RegularExpression(command.GetArgumentAtIndex(0)); + regex->Compile(command.GetArgumentAtIndex(0)); + param = new CommandObjectTypeSynthList_LoopCallbackParam(this,&result,regex,cate_regex); + } + else + param = new CommandObjectTypeSynthList_LoopCallbackParam(this,&result,NULL,cate_regex); + + DataVisualization::Categories::LoopThrough(PerCategoryCallback,param); + + if (cate_regex) + delete cate_regex; + + result.SetStatus(eReturnStatusSuccessFinishResult); + return result.Succeeded(); + } + +private: + + static bool + PerCategoryCallback(void* param_vp, + const lldb::TypeCategoryImplSP& cate) + { + + CommandObjectTypeSynthList_LoopCallbackParam* param = + (CommandObjectTypeSynthList_LoopCallbackParam*)param_vp; + CommandReturnObject* result = param->result; + + const char* cate_name = cate->GetName(); + + // if the category is disabled or empty and there is no regex, just skip it + if ((cate->IsEnabled() == false || cate->GetCount(eFormatCategoryItemSynth | eFormatCategoryItemRegexSynth) == 0) && param->cate_regex == NULL) + return true; + + // if we have a regex and this category does not match it, just skip it + if(param->cate_regex != NULL && strcmp(cate_name,param->cate_regex->GetText()) != 0 && param->cate_regex->Execute(cate_name) == false) + return true; + + result->GetOutputStream().Printf("-----------------------\nCategory: %s (%s)\n-----------------------\n", + cate_name, + (cate->IsEnabled() ? "enabled" : "disabled")); + + cate->GetSyntheticNavigator()->LoopThrough(CommandObjectTypeSynthList_LoopCallback, param_vp); + + if (cate->GetRegexSyntheticNavigator()->GetCount() > 0) + { + result->GetOutputStream().Printf("Regex-based synthetic providers (slower):\n"); + cate->GetRegexSyntheticNavigator()->LoopThrough(CommandObjectTypeSynthRXList_LoopCallback, param_vp); + } + + return true; + } + + bool + LoopCallback (const char* type, + const SyntheticChildren::SharedPointer& entry, + RegularExpression* regex, + CommandReturnObject *result) + { + if (regex == NULL || regex->Execute(type)) + result->GetOutputStream().Printf ("%s: %s\n", type, entry->GetDescription().c_str()); + return true; + } + + friend bool CommandObjectTypeSynthList_LoopCallback(void* pt2self, ConstString type, const SyntheticChildren::SharedPointer& entry); + friend bool CommandObjectTypeSynthRXList_LoopCallback(void* pt2self, lldb::RegularExpressionSP regex, const SyntheticChildren::SharedPointer& entry); +}; + +bool +CommandObjectTypeSynthList_LoopCallback (void* pt2self, + ConstString type, + const SyntheticChildren::SharedPointer& entry) +{ + CommandObjectTypeSynthList_LoopCallbackParam* param = (CommandObjectTypeSynthList_LoopCallbackParam*)pt2self; + return param->self->LoopCallback(type.AsCString(), entry, param->regex, param->result); +} + +bool +CommandObjectTypeSynthRXList_LoopCallback (void* pt2self, + lldb::RegularExpressionSP regex, + const SyntheticChildren::SharedPointer& entry) +{ + CommandObjectTypeSynthList_LoopCallbackParam* param = (CommandObjectTypeSynthList_LoopCallbackParam*)pt2self; + return param->self->LoopCallback(regex->GetText(), entry, param->regex, param->result); +} + + +OptionDefinition +CommandObjectTypeSynthList::CommandOptions::g_option_table[] = +{ + { LLDB_OPT_SET_ALL, false, "category-regex", 'w', required_argument, NULL, 0, eArgTypeName, "Only show categories matching this filter."}, + { 0, false, NULL, 0, 0, NULL, 0, eArgTypeNone, NULL } +}; + +#endif // #ifndef LLDB_DISABLE_PYTHON +//------------------------------------------------------------------------- +// CommandObjectTypeFilterDelete +//------------------------------------------------------------------------- + +class CommandObjectTypeFilterDelete : public CommandObjectParsed +{ +private: + class CommandOptions : public Options + { + public: + + CommandOptions (CommandInterpreter &interpreter) : + Options (interpreter) + { + } + + virtual + ~CommandOptions (){} + + virtual Error + SetOptionValue (uint32_t option_idx, const char *option_arg) + { + Error error; + const int short_option = m_getopt_table[option_idx].val; + + switch (short_option) + { + case 'a': + m_delete_all = true; + break; + case 'w': + m_category = std::string(option_arg); + break; + default: + error.SetErrorStringWithFormat ("unrecognized option '%c'", short_option); + break; + } + + return error; + } + + void + OptionParsingStarting () + { + m_delete_all = false; + m_category = "default"; + } + + const OptionDefinition* + GetDefinitions () + { + return g_option_table; + } + + // Options table: Required for subclasses of Options. + + static OptionDefinition g_option_table[]; + + // Instance variables to hold the values for command options. + + bool m_delete_all; + std::string m_category; + + }; + + CommandOptions m_options; + + virtual Options * + GetOptions () + { + return &m_options; + } + + static bool + PerCategoryCallback(void* param, + const lldb::TypeCategoryImplSP& cate) + { + ConstString *name = (ConstString*)param; + return cate->Delete(*name, eFormatCategoryItemFilter | eFormatCategoryItemRegexFilter); + } + +public: + CommandObjectTypeFilterDelete (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "type filter delete", + "Delete an existing filter for a type.", + NULL), + m_options(interpreter) + { + CommandArgumentEntry type_arg; + CommandArgumentData type_style_arg; + + type_style_arg.arg_type = eArgTypeName; + type_style_arg.arg_repetition = eArgRepeatPlain; + + type_arg.push_back (type_style_arg); + + m_arguments.push_back (type_arg); + + } + + ~CommandObjectTypeFilterDelete () + { + } + +protected: + bool + DoExecute (Args& command, CommandReturnObject &result) + { + const size_t argc = command.GetArgumentCount(); + + if (argc != 1) + { + result.AppendErrorWithFormat ("%s takes 1 arg.\n", m_cmd_name.c_str()); + result.SetStatus(eReturnStatusFailed); + return false; + } + + const char* typeA = command.GetArgumentAtIndex(0); + ConstString typeCS(typeA); + + if (!typeCS) + { + result.AppendError("empty typenames not allowed"); + result.SetStatus(eReturnStatusFailed); + return false; + } + + if (m_options.m_delete_all) + { + DataVisualization::Categories::LoopThrough(PerCategoryCallback, (void*)&typeCS); + result.SetStatus(eReturnStatusSuccessFinishNoResult); + return result.Succeeded(); + } + + lldb::TypeCategoryImplSP category; + DataVisualization::Categories::GetCategory(ConstString(m_options.m_category.c_str()), category); + + bool delete_category = category->GetFilterNavigator()->Delete(typeCS); + delete_category = category->GetRegexFilterNavigator()->Delete(typeCS) || delete_category; + + if (delete_category) + { + result.SetStatus(eReturnStatusSuccessFinishNoResult); + return result.Succeeded(); + } + else + { + result.AppendErrorWithFormat ("no custom synthetic provider for %s.\n", typeA); + result.SetStatus(eReturnStatusFailed); + return false; + } + + } +}; + +OptionDefinition +CommandObjectTypeFilterDelete::CommandOptions::g_option_table[] = +{ + { LLDB_OPT_SET_1, false, "all", 'a', no_argument, NULL, 0, eArgTypeNone, "Delete from every category."}, + { LLDB_OPT_SET_2, false, "category", 'w', required_argument, NULL, 0, eArgTypeName, "Delete from given category."}, + { 0, false, NULL, 0, 0, NULL, 0, eArgTypeNone, NULL } +}; + +#ifndef LLDB_DISABLE_PYTHON + +//------------------------------------------------------------------------- +// CommandObjectTypeSynthDelete +//------------------------------------------------------------------------- + +class CommandObjectTypeSynthDelete : public CommandObjectParsed +{ +private: + class CommandOptions : public Options + { + public: + + CommandOptions (CommandInterpreter &interpreter) : + Options (interpreter) + { + } + + virtual + ~CommandOptions (){} + + virtual Error + SetOptionValue (uint32_t option_idx, const char *option_arg) + { + Error error; + const int short_option = m_getopt_table[option_idx].val; + + switch (short_option) + { + case 'a': + m_delete_all = true; + break; + case 'w': + m_category = std::string(option_arg); + break; + default: + error.SetErrorStringWithFormat ("unrecognized option '%c'", short_option); + break; + } + + return error; + } + + void + OptionParsingStarting () + { + m_delete_all = false; + m_category = "default"; + } + + const OptionDefinition* + GetDefinitions () + { + return g_option_table; + } + + // Options table: Required for subclasses of Options. + + static OptionDefinition g_option_table[]; + + // Instance variables to hold the values for command options. + + bool m_delete_all; + std::string m_category; + + }; + + CommandOptions m_options; + + virtual Options * + GetOptions () + { + return &m_options; + } + + static bool + PerCategoryCallback(void* param, + const lldb::TypeCategoryImplSP& cate) + { + ConstString* name = (ConstString*)param; + return cate->Delete(*name, eFormatCategoryItemSynth | eFormatCategoryItemRegexSynth); + } + +public: + CommandObjectTypeSynthDelete (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "type synthetic delete", + "Delete an existing synthetic provider for a type.", + NULL), + m_options(interpreter) + { + CommandArgumentEntry type_arg; + CommandArgumentData type_style_arg; + + type_style_arg.arg_type = eArgTypeName; + type_style_arg.arg_repetition = eArgRepeatPlain; + + type_arg.push_back (type_style_arg); + + m_arguments.push_back (type_arg); + + } + + ~CommandObjectTypeSynthDelete () + { + } + +protected: + bool + DoExecute (Args& command, CommandReturnObject &result) + { + const size_t argc = command.GetArgumentCount(); + + if (argc != 1) + { + result.AppendErrorWithFormat ("%s takes 1 arg.\n", m_cmd_name.c_str()); + result.SetStatus(eReturnStatusFailed); + return false; + } + + const char* typeA = command.GetArgumentAtIndex(0); + ConstString typeCS(typeA); + + if (!typeCS) + { + result.AppendError("empty typenames not allowed"); + result.SetStatus(eReturnStatusFailed); + return false; + } + + if (m_options.m_delete_all) + { + DataVisualization::Categories::LoopThrough(PerCategoryCallback, (void*)&typeCS); + result.SetStatus(eReturnStatusSuccessFinishNoResult); + return result.Succeeded(); + } + + lldb::TypeCategoryImplSP category; + DataVisualization::Categories::GetCategory(ConstString(m_options.m_category.c_str()), category); + + bool delete_category = category->GetSyntheticNavigator()->Delete(typeCS); + delete_category = category->GetRegexSyntheticNavigator()->Delete(typeCS) || delete_category; + + if (delete_category) + { + result.SetStatus(eReturnStatusSuccessFinishNoResult); + return result.Succeeded(); + } + else + { + result.AppendErrorWithFormat ("no custom synthetic provider for %s.\n", typeA); + result.SetStatus(eReturnStatusFailed); + return false; + } + + } +}; + +OptionDefinition +CommandObjectTypeSynthDelete::CommandOptions::g_option_table[] = +{ + { LLDB_OPT_SET_1, false, "all", 'a', no_argument, NULL, 0, eArgTypeNone, "Delete from every category."}, + { LLDB_OPT_SET_2, false, "category", 'w', required_argument, NULL, 0, eArgTypeName, "Delete from given category."}, + { 0, false, NULL, 0, 0, NULL, 0, eArgTypeNone, NULL } +}; + +#endif // #ifndef LLDB_DISABLE_PYTHON + +//------------------------------------------------------------------------- +// CommandObjectTypeFilterClear +//------------------------------------------------------------------------- + +class CommandObjectTypeFilterClear : public CommandObjectParsed +{ +private: + + class CommandOptions : public Options + { + public: + + CommandOptions (CommandInterpreter &interpreter) : + Options (interpreter) + { + } + + virtual + ~CommandOptions (){} + + virtual Error + SetOptionValue (uint32_t option_idx, const char *option_arg) + { + Error error; + const int short_option = m_getopt_table[option_idx].val; + + switch (short_option) + { + case 'a': + m_delete_all = true; + break; + default: + error.SetErrorStringWithFormat ("unrecognized option '%c'", short_option); + break; + } + + return error; + } + + void + OptionParsingStarting () + { + m_delete_all = false; + } + + const OptionDefinition* + GetDefinitions () + { + return g_option_table; + } + + // Options table: Required for subclasses of Options. + + static OptionDefinition g_option_table[]; + + // Instance variables to hold the values for command options. + + bool m_delete_all; + bool m_delete_named; + }; + + CommandOptions m_options; + + virtual Options * + GetOptions () + { + return &m_options; + } + + static bool + PerCategoryCallback(void* param, + const lldb::TypeCategoryImplSP& cate) + { + cate->Clear(eFormatCategoryItemFilter | eFormatCategoryItemRegexFilter); + return true; + + } + +public: + CommandObjectTypeFilterClear (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "type filter clear", + "Delete all existing filters.", + NULL), + m_options(interpreter) + { + } + + ~CommandObjectTypeFilterClear () + { + } + +protected: + bool + DoExecute (Args& command, CommandReturnObject &result) + { + + if (m_options.m_delete_all) + DataVisualization::Categories::LoopThrough(PerCategoryCallback, NULL); + + else + { + lldb::TypeCategoryImplSP category; + if (command.GetArgumentCount() > 0) + { + const char* cat_name = command.GetArgumentAtIndex(0); + ConstString cat_nameCS(cat_name); + DataVisualization::Categories::GetCategory(cat_nameCS, category); + } + else + DataVisualization::Categories::GetCategory(ConstString(NULL), category); + category->GetFilterNavigator()->Clear(); + category->GetRegexFilterNavigator()->Clear(); + } + + result.SetStatus(eReturnStatusSuccessFinishResult); + return result.Succeeded(); + } + +}; + +OptionDefinition +CommandObjectTypeFilterClear::CommandOptions::g_option_table[] = +{ + { LLDB_OPT_SET_ALL, false, "all", 'a', no_argument, NULL, 0, eArgTypeNone, "Clear every category."}, + { 0, false, NULL, 0, 0, NULL, 0, eArgTypeNone, NULL } +}; + +#ifndef LLDB_DISABLE_PYTHON +//------------------------------------------------------------------------- +// CommandObjectTypeSynthClear +//------------------------------------------------------------------------- + +class CommandObjectTypeSynthClear : public CommandObjectParsed +{ +private: + + class CommandOptions : public Options + { + public: + + CommandOptions (CommandInterpreter &interpreter) : + Options (interpreter) + { + } + + virtual + ~CommandOptions (){} + + virtual Error + SetOptionValue (uint32_t option_idx, const char *option_arg) + { + Error error; + const int short_option = m_getopt_table[option_idx].val; + + switch (short_option) + { + case 'a': + m_delete_all = true; + break; + default: + error.SetErrorStringWithFormat ("unrecognized option '%c'", short_option); + break; + } + + return error; + } + + void + OptionParsingStarting () + { + m_delete_all = false; + } + + const OptionDefinition* + GetDefinitions () + { + return g_option_table; + } + + // Options table: Required for subclasses of Options. + + static OptionDefinition g_option_table[]; + + // Instance variables to hold the values for command options. + + bool m_delete_all; + bool m_delete_named; + }; + + CommandOptions m_options; + + virtual Options * + GetOptions () + { + return &m_options; + } + + static bool + PerCategoryCallback(void* param, + const lldb::TypeCategoryImplSP& cate) + { + cate->Clear(eFormatCategoryItemSynth | eFormatCategoryItemRegexSynth); + return true; + + } + +public: + CommandObjectTypeSynthClear (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "type synthetic clear", + "Delete all existing synthetic providers.", + NULL), + m_options(interpreter) + { + } + + ~CommandObjectTypeSynthClear () + { + } + +protected: + bool + DoExecute (Args& command, CommandReturnObject &result) + { + + if (m_options.m_delete_all) + DataVisualization::Categories::LoopThrough(PerCategoryCallback, NULL); + + else + { + lldb::TypeCategoryImplSP category; + if (command.GetArgumentCount() > 0) + { + const char* cat_name = command.GetArgumentAtIndex(0); + ConstString cat_nameCS(cat_name); + DataVisualization::Categories::GetCategory(cat_nameCS, category); + } + else + DataVisualization::Categories::GetCategory(ConstString(NULL), category); + category->GetSyntheticNavigator()->Clear(); + category->GetRegexSyntheticNavigator()->Clear(); + } + + result.SetStatus(eReturnStatusSuccessFinishResult); + return result.Succeeded(); + } + +}; + +OptionDefinition +CommandObjectTypeSynthClear::CommandOptions::g_option_table[] = +{ + { LLDB_OPT_SET_ALL, false, "all", 'a', no_argument, NULL, 0, eArgTypeNone, "Clear every category."}, + { 0, false, NULL, 0, 0, NULL, 0, eArgTypeNone, NULL } +}; + + +//------------------------------------------------------------------------- +// TypeSynthAddInputReader +//------------------------------------------------------------------------- + +static const char *g_synth_addreader_instructions = "Enter your Python command(s). Type 'DONE' to end.\n" + "You must define a Python class with these methods:\n" + " def __init__(self, valobj, dict):\n" + " def num_children(self):\n" + " def get_child_at_index(self, index):\n" + " def get_child_index(self, name):\n" + "Optionally, you can also define a method:\n" + " def update(self):\n" + "if your synthetic provider is holding on to any per-object state variables (currently, this is not implemented because of the way LLDB handles instances of SBValue and you should not rely on object persistence and per-object state)\n" + "class synthProvider:"; + +class TypeSynthAddInputReader : public InputReaderEZ +{ +public: + TypeSynthAddInputReader(Debugger& debugger) : + InputReaderEZ(debugger) + {} + + virtual + ~TypeSynthAddInputReader() + { + } + + virtual void ActivateHandler(HandlerData& data) + { + StreamSP out_stream = data.GetOutStream(); + bool batch_mode = data.GetBatchMode(); + if (!batch_mode) + { + out_stream->Printf ("%s\n", g_synth_addreader_instructions); + if (data.reader.GetPrompt()) + out_stream->Printf ("%s", data.reader.GetPrompt()); + out_stream->Flush(); + } + } + + virtual void ReactivateHandler(HandlerData& data) + { + StreamSP out_stream = data.GetOutStream(); + bool batch_mode = data.GetBatchMode(); + if (data.reader.GetPrompt() && !batch_mode) + { + out_stream->Printf ("%s", data.reader.GetPrompt()); + out_stream->Flush(); + } + } + virtual void GotTokenHandler(HandlerData& data) + { + StreamSP out_stream = data.GetOutStream(); + bool batch_mode = data.GetBatchMode(); + if (data.bytes && data.bytes_len && data.baton) + { + ((SynthAddOptions*)data.baton)->m_user_source.AppendString(data.bytes, data.bytes_len); + } + if (!data.reader.IsDone() && data.reader.GetPrompt() && !batch_mode) + { + out_stream->Printf ("%s", data.reader.GetPrompt()); + out_stream->Flush(); + } + } + virtual void InterruptHandler(HandlerData& data) + { + StreamSP out_stream = data.GetOutStream(); + bool batch_mode = data.GetBatchMode(); + data.reader.SetIsDone (true); + if (!batch_mode) + { + out_stream->Printf ("Warning: No command attached to breakpoint.\n"); + out_stream->Flush(); + } + } + virtual void EOFHandler(HandlerData& data) + { + data.reader.SetIsDone (true); + } + virtual void DoneHandler(HandlerData& data) + { + StreamSP out_stream = data.GetOutStream(); + SynthAddOptions *options_ptr = ((SynthAddOptions*)data.baton); + if (!options_ptr) + { + out_stream->Printf ("internal synchronization data missing.\n"); + out_stream->Flush(); + return; + } + + SynthAddOptions::SharedPointer options(options_ptr); // this will ensure that we get rid of the pointer when going out of scope + + ScriptInterpreter *interpreter = data.reader.GetDebugger().GetCommandInterpreter().GetScriptInterpreter(); + if (!interpreter) + { + out_stream->Printf ("no script interpreter.\n"); + out_stream->Flush(); + return; + } + std::string class_name_str; + if (!interpreter->GenerateTypeSynthClass (options->m_user_source, + class_name_str)) + { + out_stream->Printf ("unable to generate a class.\n"); + out_stream->Flush(); + return; + } + if (class_name_str.empty()) + { + out_stream->Printf ("unable to obtain a proper name for the class.\n"); + out_stream->Flush(); + return; + } + + // everything should be fine now, let's add the synth provider class + + SyntheticChildrenSP synth_provider; + synth_provider.reset(new ScriptedSyntheticChildren(SyntheticChildren::Flags().SetCascades(options->m_cascade). + SetSkipPointers(options->m_skip_pointers). + SetSkipReferences(options->m_skip_references), + class_name_str.c_str())); + + + lldb::TypeCategoryImplSP category; + DataVisualization::Categories::GetCategory(ConstString(options->m_category.c_str()), category); + + Error error; + + for (size_t i = 0; i < options->m_target_types.GetSize(); i++) + { + const char *type_name = options->m_target_types.GetStringAtIndex(i); + ConstString typeCS(type_name); + if (typeCS) + { + if (!CommandObjectTypeSynthAdd::AddSynth(typeCS, + synth_provider, + options->m_regex ? CommandObjectTypeSynthAdd::eRegexSynth : CommandObjectTypeSynthAdd::eRegularSynth, + options->m_category, + &error)) + { + out_stream->Printf("%s\n", error.AsCString()); + out_stream->Flush(); + return; + } + } + else + { + out_stream->Printf ("invalid type name.\n"); + out_stream->Flush(); + return; + } + } + } + +private: + DISALLOW_COPY_AND_ASSIGN (TypeSynthAddInputReader); +}; + +void +CommandObjectTypeSynthAdd::CollectPythonScript (SynthAddOptions *options, + CommandReturnObject &result) +{ + InputReaderSP reader_sp (new TypeSynthAddInputReader(m_interpreter.GetDebugger())); + if (reader_sp && options) + { + + InputReaderEZ::InitializationParameters ipr; + + Error err (reader_sp->Initialize (ipr.SetBaton(options).SetPrompt(" "))); + if (err.Success()) + { + m_interpreter.GetDebugger().PushInputReader (reader_sp); + result.SetStatus (eReturnStatusSuccessFinishNoResult); + } + else + { + result.AppendError (err.AsCString()); + result.SetStatus (eReturnStatusFailed); + } + } + else + { + result.AppendError("out of memory"); + result.SetStatus (eReturnStatusFailed); + } +} + +bool +CommandObjectTypeSynthAdd::Execute_HandwritePython (Args& command, CommandReturnObject &result) +{ + SynthAddOptions *options = new SynthAddOptions ( m_options.m_skip_pointers, + m_options.m_skip_references, + m_options.m_cascade, + m_options.m_regex, + m_options.m_category); + + const size_t argc = command.GetArgumentCount(); + + for (size_t i = 0; i < argc; i++) + { + const char* typeA = command.GetArgumentAtIndex(i); + if (typeA && *typeA) + options->m_target_types << typeA; + else + { + result.AppendError("empty typenames not allowed"); + result.SetStatus(eReturnStatusFailed); + return false; + } + } + + CollectPythonScript(options,result); + return result.Succeeded(); +} + +bool +CommandObjectTypeSynthAdd::Execute_PythonClass (Args& command, CommandReturnObject &result) +{ + const size_t argc = command.GetArgumentCount(); + + if (argc < 1) + { + result.AppendErrorWithFormat ("%s takes one or more args.\n", m_cmd_name.c_str()); + result.SetStatus(eReturnStatusFailed); + return false; + } + + if (m_options.m_class_name.empty() && !m_options.m_input_python) + { + result.AppendErrorWithFormat ("%s needs either a Python class name or -P to directly input Python code.\n", m_cmd_name.c_str()); + result.SetStatus(eReturnStatusFailed); + return false; + } + + SyntheticChildrenSP entry; + + ScriptedSyntheticChildren* impl = new ScriptedSyntheticChildren(SyntheticChildren::Flags(). + SetCascades(m_options.m_cascade). + SetSkipPointers(m_options.m_skip_pointers). + SetSkipReferences(m_options.m_skip_references), + m_options.m_class_name.c_str()); + + entry.reset(impl); + + ScriptInterpreter *interpreter = m_interpreter.GetScriptInterpreter(); + + if (interpreter && interpreter->CheckObjectExists(impl->GetPythonClassName()) == false) + result.AppendWarning("The provided class does not exist - please define it before attempting to use this synthetic provider"); + + // now I have a valid provider, let's add it to every type + + lldb::TypeCategoryImplSP category; + DataVisualization::Categories::GetCategory(ConstString(m_options.m_category.c_str()), category); + + Error error; + + for (size_t i = 0; i < argc; i++) + { + const char* typeA = command.GetArgumentAtIndex(i); + ConstString typeCS(typeA); + if (typeCS) + { + if (!AddSynth(typeCS, + entry, + m_options.m_regex ? eRegexSynth : eRegularSynth, + m_options.m_category, + &error)) + { + result.AppendError(error.AsCString()); + result.SetStatus(eReturnStatusFailed); + return false; + } + } + else + { + result.AppendError("empty typenames not allowed"); + result.SetStatus(eReturnStatusFailed); + return false; + } + } + + result.SetStatus(eReturnStatusSuccessFinishNoResult); + return result.Succeeded(); +} + +CommandObjectTypeSynthAdd::CommandObjectTypeSynthAdd (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "type synthetic add", + "Add a new synthetic provider for a type.", + NULL), + m_options (interpreter) +{ + CommandArgumentEntry type_arg; + CommandArgumentData type_style_arg; + + type_style_arg.arg_type = eArgTypeName; + type_style_arg.arg_repetition = eArgRepeatPlus; + + type_arg.push_back (type_style_arg); + + m_arguments.push_back (type_arg); + +} + +bool +CommandObjectTypeSynthAdd::AddSynth(ConstString type_name, + SyntheticChildrenSP entry, + SynthFormatType type, + std::string category_name, + Error* error) +{ + lldb::TypeCategoryImplSP category; + DataVisualization::Categories::GetCategory(ConstString(category_name.c_str()), category); + + if (type == eRegularSynth) + { + std::string type_name_str(type_name.GetCString()); + if (type_name_str.compare(type_name_str.length() - 2, 2, "[]") == 0) + { + type_name_str.resize(type_name_str.length()-2); + if (type_name_str.back() != ' ') + type_name_str.append(" \\[[0-9]+\\]"); + else + type_name_str.append("\\[[0-9]+\\]"); + type_name.SetCString(type_name_str.c_str()); + type = eRegularSynth; + } + } + + if (category->AnyMatches(type_name, + eFormatCategoryItemFilter | eFormatCategoryItemRegexFilter, + false)) + { + if (error) + error->SetErrorStringWithFormat("cannot add synthetic for type %s when filter is defined in same category!", type_name.AsCString()); + return false; + } + + if (type == eRegexSynth) + { + RegularExpressionSP typeRX(new RegularExpression()); + if (!typeRX->Compile(type_name.GetCString())) + { + if (error) + error->SetErrorString("regex format error (maybe this is not really a regex?)"); + return false; + } + + category->GetRegexSyntheticNavigator()->Delete(type_name); + category->GetRegexSyntheticNavigator()->Add(typeRX, entry); + + return true; + } + else + { + category->GetSyntheticNavigator()->Add(type_name, entry); + return true; + } +} + +bool +CommandObjectTypeSynthAdd::DoExecute (Args& command, CommandReturnObject &result) +{ + if (m_options.handwrite_python) + return Execute_HandwritePython(command, result); + else if (m_options.is_class_based) + return Execute_PythonClass(command, result); + else + { + result.AppendError("must either provide a children list, a Python class name, or use -P and type a Python class line-by-line"); + result.SetStatus(eReturnStatusFailed); + return false; + } +} + +OptionDefinition +CommandObjectTypeSynthAdd::CommandOptions::g_option_table[] = +{ + { LLDB_OPT_SET_ALL, false, "cascade", 'C', required_argument, NULL, 0, eArgTypeBoolean, "If true, cascade through typedef chains."}, + { LLDB_OPT_SET_ALL, false, "skip-pointers", 'p', no_argument, NULL, 0, eArgTypeNone, "Don't use this format for pointers-to-type objects."}, + { LLDB_OPT_SET_ALL, false, "skip-references", 'r', no_argument, NULL, 0, eArgTypeNone, "Don't use this format for references-to-type objects."}, + { LLDB_OPT_SET_ALL, false, "category", 'w', required_argument, NULL, 0, eArgTypeName, "Add this to the given category instead of the default one."}, + { LLDB_OPT_SET_2, false, "python-class", 'l', required_argument, NULL, 0, eArgTypePythonClass, "Use this Python class to produce synthetic children."}, + { LLDB_OPT_SET_3, false, "input-python", 'P', no_argument, NULL, 0, eArgTypeNone, "Type Python code to generate a class that provides synthetic children."}, + { LLDB_OPT_SET_ALL, false, "regex", 'x', no_argument, NULL, 0, eArgTypeNone, "Type names are actually regular expressions."}, + { 0, false, NULL, 0, 0, NULL, 0, eArgTypeNone, NULL } +}; + +#endif // #ifndef LLDB_DISABLE_PYTHON + +class CommandObjectTypeFilterAdd : public CommandObjectParsed +{ + +private: + + class CommandOptions : public Options + { + typedef std::vector option_vector; + public: + + CommandOptions (CommandInterpreter &interpreter) : + Options (interpreter) + { + } + + virtual + ~CommandOptions (){} + + virtual Error + SetOptionValue (uint32_t option_idx, const char *option_arg) + { + Error error; + const int short_option = m_getopt_table[option_idx].val; + bool success; + + switch (short_option) + { + case 'C': + m_cascade = Args::StringToBoolean(option_arg, true, &success); + if (!success) + error.SetErrorStringWithFormat("invalid value for cascade: %s", option_arg); + break; + case 'c': + m_expr_paths.push_back(option_arg); + has_child_list = true; + break; + case 'p': + m_skip_pointers = true; + break; + case 'r': + m_skip_references = true; + break; + case 'w': + m_category = std::string(option_arg); + break; + case 'x': + m_regex = true; + break; + default: + error.SetErrorStringWithFormat ("unrecognized option '%c'", short_option); + break; + } + + return error; + } + + void + OptionParsingStarting () + { + m_cascade = true; + m_skip_pointers = false; + m_skip_references = false; + m_category = "default"; + m_expr_paths.clear(); + has_child_list = false; + m_regex = false; + } + + const OptionDefinition* + GetDefinitions () + { + return g_option_table; + } + + // Options table: Required for subclasses of Options. + + static OptionDefinition g_option_table[]; + + // Instance variables to hold the values for command options. + + bool m_cascade; + bool m_skip_references; + bool m_skip_pointers; + bool m_input_python; + option_vector m_expr_paths; + std::string m_category; + + bool has_child_list; + + bool m_regex; + + typedef option_vector::iterator ExpressionPathsIterator; + }; + + CommandOptions m_options; + + virtual Options * + GetOptions () + { + return &m_options; + } + + enum FilterFormatType + { + eRegularFilter, + eRegexFilter + }; + + bool + AddFilter(ConstString type_name, + SyntheticChildrenSP entry, + FilterFormatType type, + std::string category_name, + Error* error) + { + lldb::TypeCategoryImplSP category; + DataVisualization::Categories::GetCategory(ConstString(category_name.c_str()), category); + + if (type == eRegularFilter) + { + std::string type_name_str(type_name.GetCString()); + if (type_name_str.compare(type_name_str.length() - 2, 2, "[]") == 0) + { + type_name_str.resize(type_name_str.length()-2); + if (type_name_str.back() != ' ') + type_name_str.append(" \\[[0-9]+\\]"); + else + type_name_str.append("\\[[0-9]+\\]"); + type_name.SetCString(type_name_str.c_str()); + type = eRegexFilter; + } + } + + if (category->AnyMatches(type_name, + eFormatCategoryItemSynth | eFormatCategoryItemRegexSynth, + false)) + { + if (error) + error->SetErrorStringWithFormat("cannot add filter for type %s when synthetic is defined in same category!", type_name.AsCString()); + return false; + } + + if (type == eRegexFilter) + { + RegularExpressionSP typeRX(new RegularExpression()); + if (!typeRX->Compile(type_name.GetCString())) + { + if (error) + error->SetErrorString("regex format error (maybe this is not really a regex?)"); + return false; + } + + category->GetRegexFilterNavigator()->Delete(type_name); + category->GetRegexFilterNavigator()->Add(typeRX, entry); + + return true; + } + else + { + category->GetFilterNavigator()->Add(type_name, entry); + return true; + } + } + + +public: + + CommandObjectTypeFilterAdd (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "type filter add", + "Add a new filter for a type.", + NULL), + m_options (interpreter) + { + CommandArgumentEntry type_arg; + CommandArgumentData type_style_arg; + + type_style_arg.arg_type = eArgTypeName; + type_style_arg.arg_repetition = eArgRepeatPlus; + + type_arg.push_back (type_style_arg); + + m_arguments.push_back (type_arg); + + SetHelpLong( + "Some examples of using this command.\n" + "We use as reference the following snippet of code:\n" + "\n" + "class Foo {;\n" + " int a;\n" + " int b;\n" + " int c;\n" + " int d;\n" + " int e;\n" + " int f;\n" + " int g;\n" + " int h;\n" + " int i;\n" + "} \n" + "Typing:\n" + "type filter add --child a --child g Foo\n" + "frame variable a_foo\n" + "will produce an output where only a and g are displayed\n" + "Other children of a_foo (b,c,d,e,f,h and i) are available by asking for them, as in:\n" + "frame variable a_foo.b a_foo.c ... a_foo.i\n" + "\n" + "Use option --raw to frame variable prevails on the filter\n" + "frame variable a_foo --raw\n" + "shows all the children of a_foo (a thru i) as if no filter was defined\n" + ); + } + + ~CommandObjectTypeFilterAdd () + { + } + +protected: + bool + DoExecute (Args& command, CommandReturnObject &result) + { + const size_t argc = command.GetArgumentCount(); + + if (argc < 1) + { + result.AppendErrorWithFormat ("%s takes one or more args.\n", m_cmd_name.c_str()); + result.SetStatus(eReturnStatusFailed); + return false; + } + + if (m_options.m_expr_paths.size() == 0) + { + result.AppendErrorWithFormat ("%s needs one or more children.\n", m_cmd_name.c_str()); + result.SetStatus(eReturnStatusFailed); + return false; + } + + SyntheticChildrenSP entry; + + TypeFilterImpl* impl = new TypeFilterImpl(SyntheticChildren::Flags().SetCascades(m_options.m_cascade). + SetSkipPointers(m_options.m_skip_pointers). + SetSkipReferences(m_options.m_skip_references)); + + entry.reset(impl); + + // go through the expression paths + CommandOptions::ExpressionPathsIterator begin, end = m_options.m_expr_paths.end(); + + for (begin = m_options.m_expr_paths.begin(); begin != end; begin++) + impl->AddExpressionPath(*begin); + + + // now I have a valid provider, let's add it to every type + + lldb::TypeCategoryImplSP category; + DataVisualization::Categories::GetCategory(ConstString(m_options.m_category.c_str()), category); + + Error error; + + for (size_t i = 0; i < argc; i++) + { + const char* typeA = command.GetArgumentAtIndex(i); + ConstString typeCS(typeA); + if (typeCS) + { + if (!AddFilter(typeCS, + entry, + m_options.m_regex ? eRegexFilter : eRegularFilter, + m_options.m_category, + &error)) + { + result.AppendError(error.AsCString()); + result.SetStatus(eReturnStatusFailed); + return false; + } + } + else + { + result.AppendError("empty typenames not allowed"); + result.SetStatus(eReturnStatusFailed); + return false; + } + } + + result.SetStatus(eReturnStatusSuccessFinishNoResult); + return result.Succeeded(); + } + +}; + +OptionDefinition +CommandObjectTypeFilterAdd::CommandOptions::g_option_table[] = +{ + { LLDB_OPT_SET_ALL, false, "cascade", 'C', required_argument, NULL, 0, eArgTypeBoolean, "If true, cascade through typedef chains."}, + { LLDB_OPT_SET_ALL, false, "skip-pointers", 'p', no_argument, NULL, 0, eArgTypeNone, "Don't use this format for pointers-to-type objects."}, + { LLDB_OPT_SET_ALL, false, "skip-references", 'r', no_argument, NULL, 0, eArgTypeNone, "Don't use this format for references-to-type objects."}, + { LLDB_OPT_SET_ALL, false, "category", 'w', required_argument, NULL, 0, eArgTypeName, "Add this to the given category instead of the default one."}, + { LLDB_OPT_SET_ALL, false, "child", 'c', required_argument, NULL, 0, eArgTypeExpressionPath, "Include this expression path in the synthetic view."}, + { LLDB_OPT_SET_ALL, false, "regex", 'x', no_argument, NULL, 0, eArgTypeNone, "Type names are actually regular expressions."}, + { 0, false, NULL, 0, 0, NULL, 0, eArgTypeNone, NULL } +}; + +class CommandObjectTypeFormat : public CommandObjectMultiword +{ +public: + CommandObjectTypeFormat (CommandInterpreter &interpreter) : + CommandObjectMultiword (interpreter, + "type format", + "A set of commands for editing variable value display options", + "type format [] ") + { + LoadSubCommand ("add", CommandObjectSP (new CommandObjectTypeFormatAdd (interpreter))); + LoadSubCommand ("clear", CommandObjectSP (new CommandObjectTypeFormatClear (interpreter))); + LoadSubCommand ("delete", CommandObjectSP (new CommandObjectTypeFormatDelete (interpreter))); + LoadSubCommand ("list", CommandObjectSP (new CommandObjectTypeFormatList (interpreter))); + } + + + ~CommandObjectTypeFormat () + { + } +}; + +#ifndef LLDB_DISABLE_PYTHON + +class CommandObjectTypeSynth : public CommandObjectMultiword +{ +public: + CommandObjectTypeSynth (CommandInterpreter &interpreter) : + CommandObjectMultiword (interpreter, + "type synthetic", + "A set of commands for operating on synthetic type representations", + "type synthetic [] ") + { + LoadSubCommand ("add", CommandObjectSP (new CommandObjectTypeSynthAdd (interpreter))); + LoadSubCommand ("clear", CommandObjectSP (new CommandObjectTypeSynthClear (interpreter))); + LoadSubCommand ("delete", CommandObjectSP (new CommandObjectTypeSynthDelete (interpreter))); + LoadSubCommand ("list", CommandObjectSP (new CommandObjectTypeSynthList (interpreter))); + } + + + ~CommandObjectTypeSynth () + { + } +}; + +#endif // #ifndef LLDB_DISABLE_PYTHON + +class CommandObjectTypeFilter : public CommandObjectMultiword +{ +public: + CommandObjectTypeFilter (CommandInterpreter &interpreter) : + CommandObjectMultiword (interpreter, + "type filter", + "A set of commands for operating on type filters", + "type synthetic [] ") + { + LoadSubCommand ("add", CommandObjectSP (new CommandObjectTypeFilterAdd (interpreter))); + LoadSubCommand ("clear", CommandObjectSP (new CommandObjectTypeFilterClear (interpreter))); + LoadSubCommand ("delete", CommandObjectSP (new CommandObjectTypeFilterDelete (interpreter))); + LoadSubCommand ("list", CommandObjectSP (new CommandObjectTypeFilterList (interpreter))); + } + + + ~CommandObjectTypeFilter () + { + } +}; + +class CommandObjectTypeCategory : public CommandObjectMultiword +{ +public: + CommandObjectTypeCategory (CommandInterpreter &interpreter) : + CommandObjectMultiword (interpreter, + "type category", + "A set of commands for operating on categories", + "type category [] ") + { + LoadSubCommand ("enable", CommandObjectSP (new CommandObjectTypeCategoryEnable (interpreter))); + LoadSubCommand ("disable", CommandObjectSP (new CommandObjectTypeCategoryDisable (interpreter))); + LoadSubCommand ("delete", CommandObjectSP (new CommandObjectTypeCategoryDelete (interpreter))); + LoadSubCommand ("list", CommandObjectSP (new CommandObjectTypeCategoryList (interpreter))); + } + + + ~CommandObjectTypeCategory () + { + } +}; + +class CommandObjectTypeSummary : public CommandObjectMultiword +{ +public: + CommandObjectTypeSummary (CommandInterpreter &interpreter) : + CommandObjectMultiword (interpreter, + "type summary", + "A set of commands for editing variable summary display options", + "type summary [] ") + { + LoadSubCommand ("add", CommandObjectSP (new CommandObjectTypeSummaryAdd (interpreter))); + LoadSubCommand ("clear", CommandObjectSP (new CommandObjectTypeSummaryClear (interpreter))); + LoadSubCommand ("delete", CommandObjectSP (new CommandObjectTypeSummaryDelete (interpreter))); + LoadSubCommand ("list", CommandObjectSP (new CommandObjectTypeSummaryList (interpreter))); + } + + + ~CommandObjectTypeSummary () + { + } +}; + +//------------------------------------------------------------------------- +// CommandObjectType +//------------------------------------------------------------------------- + +CommandObjectType::CommandObjectType (CommandInterpreter &interpreter) : + CommandObjectMultiword (interpreter, + "type", + "A set of commands for operating on the type system", + "type []") +{ + LoadSubCommand ("category", CommandObjectSP (new CommandObjectTypeCategory (interpreter))); + LoadSubCommand ("filter", CommandObjectSP (new CommandObjectTypeFilter (interpreter))); + LoadSubCommand ("format", CommandObjectSP (new CommandObjectTypeFormat (interpreter))); + LoadSubCommand ("summary", CommandObjectSP (new CommandObjectTypeSummary (interpreter))); +#ifndef LLDB_DISABLE_PYTHON + LoadSubCommand ("synthetic", CommandObjectSP (new CommandObjectTypeSynth (interpreter))); +#endif +} + + +CommandObjectType::~CommandObjectType () +{ +} + + diff --git a/contrib/llvm/tools/lldb/source/Commands/CommandObjectType.h b/contrib/llvm/tools/lldb/source/Commands/CommandObjectType.h new file mode 100644 index 00000000000..c796902cf3b --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Commands/CommandObjectType.h @@ -0,0 +1,37 @@ +//===-- CommandObjectType.h ------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_CommandObjectType_h_ +#define liblldb_CommandObjectType_h_ + +// C Includes +// C++ Includes + + +// Other libraries and framework includes +// Project includes + +#include "lldb/lldb-types.h" +#include "lldb/Interpreter/CommandObjectMultiword.h" +#include "lldb/Interpreter/Options.h" + +namespace lldb_private { + +class CommandObjectType : public CommandObjectMultiword +{ +public: + CommandObjectType (CommandInterpreter &interpreter); + + virtual + ~CommandObjectType (); +}; + +} // namespace lldb_private + +#endif // liblldb_CommandObjectType_h_ diff --git a/contrib/llvm/tools/lldb/source/Commands/CommandObjectVersion.cpp b/contrib/llvm/tools/lldb/source/Commands/CommandObjectVersion.cpp new file mode 100644 index 00000000000..2d950a89c9b --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Commands/CommandObjectVersion.cpp @@ -0,0 +1,53 @@ +//===-- CommandObjectVersion.cpp --------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "CommandObjectVersion.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private.h" +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Interpreter/CommandReturnObject.h" + +using namespace lldb; +using namespace lldb_private; + +//------------------------------------------------------------------------- +// CommandObjectVersion +//------------------------------------------------------------------------- + +CommandObjectVersion::CommandObjectVersion (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, "version", "Show version of LLDB debugger.", "version") +{ +} + +CommandObjectVersion::~CommandObjectVersion () +{ +} + +bool +CommandObjectVersion::DoExecute (Args& args, CommandReturnObject &result) +{ + if (args.GetArgumentCount() == 0) + { + result.AppendMessageWithFormat ("%s\n", lldb_private::GetVersion()); + result.SetStatus (eReturnStatusSuccessFinishResult); + } + else + { + result.AppendError("the version command takes no arguments."); + result.SetStatus (eReturnStatusFailed); + } + return true; +} + diff --git a/contrib/llvm/tools/lldb/source/Commands/CommandObjectVersion.h b/contrib/llvm/tools/lldb/source/Commands/CommandObjectVersion.h new file mode 100644 index 00000000000..1fdbed60c65 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Commands/CommandObjectVersion.h @@ -0,0 +1,43 @@ +//===-- CommandObjectVersion.h ----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_CommandObjectVersion_h_ +#define liblldb_CommandObjectVersion_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Interpreter/CommandObject.h" + +namespace lldb_private { + +//------------------------------------------------------------------------- +// CommandObjectVersion +//------------------------------------------------------------------------- + +class CommandObjectVersion : public CommandObjectParsed +{ +public: + + CommandObjectVersion (CommandInterpreter &interpreter); + + virtual + ~CommandObjectVersion (); + +protected: + virtual bool + DoExecute (Args& args, + CommandReturnObject &result); + +}; + +} // namespace lldb_private + +#endif // liblldb_CommandObjectVersion_h_ diff --git a/contrib/llvm/tools/lldb/source/Commands/CommandObjectWatchpoint.cpp b/contrib/llvm/tools/lldb/source/Commands/CommandObjectWatchpoint.cpp new file mode 100644 index 00000000000..6f70f39c1c7 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Commands/CommandObjectWatchpoint.cpp @@ -0,0 +1,1394 @@ +//===-- CommandObjectWatchpoint.cpp -----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "CommandObjectWatchpoint.h" +#include "CommandObjectWatchpointCommand.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Breakpoint/Watchpoint.h" +#include "lldb/Breakpoint/WatchpointList.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Core/ValueObject.h" +#include "lldb/Core/ValueObjectVariable.h" +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Interpreter/CommandReturnObject.h" +#include "lldb/Interpreter/CommandCompletions.h" +#include "lldb/Symbol/Variable.h" +#include "lldb/Symbol/VariableList.h" +#include "lldb/Target/Target.h" + +#include + +using namespace lldb; +using namespace lldb_private; + +static void +AddWatchpointDescription(Stream *s, Watchpoint *wp, lldb::DescriptionLevel level) +{ + s->IndentMore(); + wp->GetDescription(s, level); + s->IndentLess(); + s->EOL(); +} + +static bool +CheckTargetForWatchpointOperations(Target *target, CommandReturnObject &result) +{ + if (target == NULL) + { + result.AppendError ("Invalid target. No existing target or watchpoints."); + result.SetStatus (eReturnStatusFailed); + return false; + } + bool process_is_valid = target->GetProcessSP() && target->GetProcessSP()->IsAlive(); + if (!process_is_valid) + { + result.AppendError ("Thre's no process or it is not alive."); + result.SetStatus (eReturnStatusFailed); + return false; + } + // Target passes our checks, return true. + return true; +} + +// FIXME: This doesn't seem to be the right place for this functionality. +#include "llvm/ADT/StringRef.h" +static inline void StripLeadingSpaces(llvm::StringRef &Str) +{ + while (!Str.empty() && isspace(Str[0])) + Str = Str.substr(1); +} + +// Equivalent class: {"-", "to", "To", "TO"} of range specifier array. +static const char* RSA[4] = { "-", "to", "To", "TO" }; + +// Return the index to RSA if found; otherwise -1 is returned. +static int32_t +WithRSAIndex(llvm::StringRef &Arg) +{ + + uint32_t i; + for (i = 0; i < 4; ++i) + if (Arg.find(RSA[i]) != llvm::StringRef::npos) + return i; + return -1; +} + +// Return true if wp_ids is successfully populated with the watch ids. +// False otherwise. +bool +CommandObjectMultiwordWatchpoint::VerifyWatchpointIDs(Target *target, Args &args, std::vector &wp_ids) +{ + // Pre-condition: args.GetArgumentCount() > 0. + if (args.GetArgumentCount() == 0) + { + if (target == NULL) + return false; + WatchpointSP watch_sp = target->GetLastCreatedWatchpoint(); + if (watch_sp) + { + wp_ids.push_back(watch_sp->GetID()); + return true; + } + else + return false; + } + + llvm::StringRef Minus("-"); + std::vector StrRefArgs; + std::pair Pair; + size_t i; + int32_t idx; + // Go through the argments and make a canonical form of arg list containing + // only numbers with possible "-" in between. + for (i = 0; i < args.GetArgumentCount(); ++i) { + llvm::StringRef Arg(args.GetArgumentAtIndex(i)); + if ((idx = WithRSAIndex(Arg)) == -1) { + StrRefArgs.push_back(Arg); + continue; + } + // The Arg contains the range specifier, split it, then. + Pair = Arg.split(RSA[idx]); + if (!Pair.first.empty()) + StrRefArgs.push_back(Pair.first); + StrRefArgs.push_back(Minus); + if (!Pair.second.empty()) + StrRefArgs.push_back(Pair.second); + } + // Now process the canonical list and fill in the vector of uint32_t's. + // If there is any error, return false and the client should ignore wp_ids. + uint32_t beg, end, id; + size_t size = StrRefArgs.size(); + bool in_range = false; + for (i = 0; i < size; ++i) { + llvm::StringRef Arg = StrRefArgs[i]; + if (in_range) { + // Look for the 'end' of the range. Note StringRef::getAsInteger() + // returns true to signify error while parsing. + if (Arg.getAsInteger(0, end)) + return false; + // Found a range! Now append the elements. + for (id = beg; id <= end; ++id) + wp_ids.push_back(id); + in_range = false; + continue; + } + if (i < (size - 1) && StrRefArgs[i+1] == Minus) { + if (Arg.getAsInteger(0, beg)) + return false; + // Turn on the in_range flag, we are looking for end of range next. + ++i; in_range = true; + continue; + } + // Otherwise, we have a simple ID. Just append it. + if (Arg.getAsInteger(0, beg)) + return false; + wp_ids.push_back(beg); + } + // It is an error if after the loop, we're still in_range. + if (in_range) + return false; + + return true; // Success! +} + +//------------------------------------------------------------------------- +// CommandObjectWatchpointList +//------------------------------------------------------------------------- +#pragma mark List + +class CommandObjectWatchpointList : public CommandObjectParsed +{ +public: + CommandObjectWatchpointList (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "watchpoint list", + "List all watchpoints at configurable levels of detail.", + NULL), + m_options(interpreter) + { + CommandArgumentEntry arg; + CommandObject::AddIDsArgumentData(arg, eArgTypeWatchpointID, eArgTypeWatchpointIDRange); + // Add the entry for the first argument for this command to the object's arguments vector. + m_arguments.push_back(arg); + } + + virtual + ~CommandObjectWatchpointList () {} + + virtual Options * + GetOptions () + { + return &m_options; + } + + class CommandOptions : public Options + { + public: + + CommandOptions (CommandInterpreter &interpreter) : + Options(interpreter), + m_level(lldb::eDescriptionLevelBrief) // Watchpoint List defaults to brief descriptions + { + } + + virtual + ~CommandOptions () {} + + virtual Error + SetOptionValue (uint32_t option_idx, const char *option_arg) + { + Error error; + const int short_option = m_getopt_table[option_idx].val; + + switch (short_option) + { + case 'b': + m_level = lldb::eDescriptionLevelBrief; + break; + case 'f': + m_level = lldb::eDescriptionLevelFull; + break; + case 'v': + m_level = lldb::eDescriptionLevelVerbose; + break; + default: + error.SetErrorStringWithFormat("unrecognized option '%c'", short_option); + break; + } + + return error; + } + + void + OptionParsingStarting () + { + m_level = lldb::eDescriptionLevelFull; + } + + const OptionDefinition * + GetDefinitions () + { + return g_option_table; + } + + + // Options table: Required for subclasses of Options. + + static OptionDefinition g_option_table[]; + + // Instance variables to hold the values for command options. + + lldb::DescriptionLevel m_level; + }; + +protected: + virtual bool + DoExecute (Args& command, CommandReturnObject &result) + { + Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get(); + if (target == NULL) + { + result.AppendError ("Invalid target. No current target or watchpoints."); + result.SetStatus (eReturnStatusSuccessFinishNoResult); + return true; + } + + if (target->GetProcessSP() && target->GetProcessSP()->IsAlive()) + { + uint32_t num_supported_hardware_watchpoints; + Error error = target->GetProcessSP()->GetWatchpointSupportInfo(num_supported_hardware_watchpoints); + if (error.Success()) + result.AppendMessageWithFormat("Number of supported hardware watchpoints: %u\n", + num_supported_hardware_watchpoints); + } + + const WatchpointList &watchpoints = target->GetWatchpointList(); + Mutex::Locker locker; + target->GetWatchpointList().GetListMutex(locker); + + size_t num_watchpoints = watchpoints.GetSize(); + + if (num_watchpoints == 0) + { + result.AppendMessage("No watchpoints currently set."); + result.SetStatus(eReturnStatusSuccessFinishNoResult); + return true; + } + + Stream &output_stream = result.GetOutputStream(); + + if (command.GetArgumentCount() == 0) + { + // No watchpoint selected; show info about all currently set watchpoints. + result.AppendMessage ("Current watchpoints:"); + for (size_t i = 0; i < num_watchpoints; ++i) + { + Watchpoint *wp = watchpoints.GetByIndex(i).get(); + AddWatchpointDescription(&output_stream, wp, m_options.m_level); + } + result.SetStatus(eReturnStatusSuccessFinishNoResult); + } + else + { + // Particular watchpoints selected; enable them. + std::vector wp_ids; + if (!CommandObjectMultiwordWatchpoint::VerifyWatchpointIDs(target, command, wp_ids)) + { + result.AppendError("Invalid watchpoints specification."); + result.SetStatus(eReturnStatusFailed); + return false; + } + + const size_t size = wp_ids.size(); + for (size_t i = 0; i < size; ++i) + { + Watchpoint *wp = watchpoints.FindByID(wp_ids[i]).get(); + if (wp) + AddWatchpointDescription(&output_stream, wp, m_options.m_level); + result.SetStatus(eReturnStatusSuccessFinishNoResult); + } + } + + return result.Succeeded(); + } + +private: + CommandOptions m_options; +}; + +//------------------------------------------------------------------------- +// CommandObjectWatchpointList::Options +//------------------------------------------------------------------------- +#pragma mark List::CommandOptions +OptionDefinition +CommandObjectWatchpointList::CommandOptions::g_option_table[] = +{ + { LLDB_OPT_SET_1, false, "brief", 'b', no_argument, NULL, 0, eArgTypeNone, + "Give a brief description of the watchpoint (no location info)."}, + + { LLDB_OPT_SET_2, false, "full", 'f', no_argument, NULL, 0, eArgTypeNone, + "Give a full description of the watchpoint and its locations."}, + + { LLDB_OPT_SET_3, false, "verbose", 'v', no_argument, NULL, 0, eArgTypeNone, + "Explain everything we know about the watchpoint (for debugging debugger bugs)." }, + + { 0, false, NULL, 0, 0, NULL, 0, eArgTypeNone, NULL } +}; + +//------------------------------------------------------------------------- +// CommandObjectWatchpointEnable +//------------------------------------------------------------------------- +#pragma mark Enable + +class CommandObjectWatchpointEnable : public CommandObjectParsed +{ +public: + CommandObjectWatchpointEnable (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "enable", + "Enable the specified disabled watchpoint(s). If no watchpoints are specified, enable all of them.", + NULL) + { + CommandArgumentEntry arg; + CommandObject::AddIDsArgumentData(arg, eArgTypeWatchpointID, eArgTypeWatchpointIDRange); + // Add the entry for the first argument for this command to the object's arguments vector. + m_arguments.push_back(arg); + } + + virtual + ~CommandObjectWatchpointEnable () {} + +protected: + virtual bool + DoExecute (Args& command, + CommandReturnObject &result) + { + Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get(); + if (!CheckTargetForWatchpointOperations(target, result)) + return false; + + Mutex::Locker locker; + target->GetWatchpointList().GetListMutex(locker); + + const WatchpointList &watchpoints = target->GetWatchpointList(); + + size_t num_watchpoints = watchpoints.GetSize(); + + if (num_watchpoints == 0) + { + result.AppendError("No watchpoints exist to be enabled."); + result.SetStatus(eReturnStatusFailed); + return false; + } + + if (command.GetArgumentCount() == 0) + { + // No watchpoint selected; enable all currently set watchpoints. + target->EnableAllWatchpoints(); + result.AppendMessageWithFormat("All watchpoints enabled. (%lu watchpoints)\n", num_watchpoints); + result.SetStatus(eReturnStatusSuccessFinishNoResult); + } + else + { + // Particular watchpoints selected; enable them. + std::vector wp_ids; + if (!CommandObjectMultiwordWatchpoint::VerifyWatchpointIDs(target, command, wp_ids)) + { + result.AppendError("Invalid watchpoints specification."); + result.SetStatus(eReturnStatusFailed); + return false; + } + + int count = 0; + const size_t size = wp_ids.size(); + for (size_t i = 0; i < size; ++i) + if (target->EnableWatchpointByID(wp_ids[i])) + ++count; + result.AppendMessageWithFormat("%d watchpoints enabled.\n", count); + result.SetStatus(eReturnStatusSuccessFinishNoResult); + } + + return result.Succeeded(); + } + +private: +}; + +//------------------------------------------------------------------------- +// CommandObjectWatchpointDisable +//------------------------------------------------------------------------- +#pragma mark Disable + +class CommandObjectWatchpointDisable : public CommandObjectParsed +{ +public: + CommandObjectWatchpointDisable (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "watchpoint disable", + "Disable the specified watchpoint(s) without removing it/them. If no watchpoints are specified, disable them all.", + NULL) + { + CommandArgumentEntry arg; + CommandObject::AddIDsArgumentData(arg, eArgTypeWatchpointID, eArgTypeWatchpointIDRange); + // Add the entry for the first argument for this command to the object's arguments vector. + m_arguments.push_back(arg); + } + + + virtual + ~CommandObjectWatchpointDisable () {} + +protected: + virtual bool + DoExecute (Args& command, CommandReturnObject &result) + { + Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get(); + if (!CheckTargetForWatchpointOperations(target, result)) + return false; + + Mutex::Locker locker; + target->GetWatchpointList().GetListMutex(locker); + + const WatchpointList &watchpoints = target->GetWatchpointList(); + size_t num_watchpoints = watchpoints.GetSize(); + + if (num_watchpoints == 0) + { + result.AppendError("No watchpoints exist to be disabled."); + result.SetStatus(eReturnStatusFailed); + return false; + } + + if (command.GetArgumentCount() == 0) + { + // No watchpoint selected; disable all currently set watchpoints. + if (target->DisableAllWatchpoints()) + { + result.AppendMessageWithFormat("All watchpoints disabled. (%lu watchpoints)\n", num_watchpoints); + result.SetStatus(eReturnStatusSuccessFinishNoResult); + } + else + { + result.AppendError("Disable all watchpoints failed\n"); + result.SetStatus(eReturnStatusFailed); + } + } + else + { + // Particular watchpoints selected; disable them. + std::vector wp_ids; + if (!CommandObjectMultiwordWatchpoint::VerifyWatchpointIDs(target, command, wp_ids)) + { + result.AppendError("Invalid watchpoints specification."); + result.SetStatus(eReturnStatusFailed); + return false; + } + + int count = 0; + const size_t size = wp_ids.size(); + for (size_t i = 0; i < size; ++i) + if (target->DisableWatchpointByID(wp_ids[i])) + ++count; + result.AppendMessageWithFormat("%d watchpoints disabled.\n", count); + result.SetStatus(eReturnStatusSuccessFinishNoResult); + } + + return result.Succeeded(); + } + +}; + +//------------------------------------------------------------------------- +// CommandObjectWatchpointDelete +//------------------------------------------------------------------------- +#pragma mark Delete + +class CommandObjectWatchpointDelete : public CommandObjectParsed +{ +public: + CommandObjectWatchpointDelete (CommandInterpreter &interpreter) : + CommandObjectParsed(interpreter, + "watchpoint delete", + "Delete the specified watchpoint(s). If no watchpoints are specified, delete them all.", + NULL) + { + CommandArgumentEntry arg; + CommandObject::AddIDsArgumentData(arg, eArgTypeWatchpointID, eArgTypeWatchpointIDRange); + // Add the entry for the first argument for this command to the object's arguments vector. + m_arguments.push_back(arg); + } + + virtual + ~CommandObjectWatchpointDelete () {} + +protected: + virtual bool + DoExecute (Args& command, CommandReturnObject &result) + { + Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get(); + if (!CheckTargetForWatchpointOperations(target, result)) + return false; + + Mutex::Locker locker; + target->GetWatchpointList().GetListMutex(locker); + + const WatchpointList &watchpoints = target->GetWatchpointList(); + + size_t num_watchpoints = watchpoints.GetSize(); + + if (num_watchpoints == 0) + { + result.AppendError("No watchpoints exist to be deleted."); + result.SetStatus(eReturnStatusFailed); + return false; + } + + if (command.GetArgumentCount() == 0) + { + if (!m_interpreter.Confirm("About to delete all watchpoints, do you want to do that?", true)) + { + result.AppendMessage("Operation cancelled..."); + } + else + { + target->RemoveAllWatchpoints(); + result.AppendMessageWithFormat("All watchpoints removed. (%lu watchpoints)\n", num_watchpoints); + } + result.SetStatus (eReturnStatusSuccessFinishNoResult); + } + else + { + // Particular watchpoints selected; delete them. + std::vector wp_ids; + if (!CommandObjectMultiwordWatchpoint::VerifyWatchpointIDs(target, command, wp_ids)) + { + result.AppendError("Invalid watchpoints specification."); + result.SetStatus(eReturnStatusFailed); + return false; + } + + int count = 0; + const size_t size = wp_ids.size(); + for (size_t i = 0; i < size; ++i) + if (target->RemoveWatchpointByID(wp_ids[i])) + ++count; + result.AppendMessageWithFormat("%d watchpoints deleted.\n",count); + result.SetStatus (eReturnStatusSuccessFinishNoResult); + } + + return result.Succeeded(); + } + +}; + +//------------------------------------------------------------------------- +// CommandObjectWatchpointIgnore +//------------------------------------------------------------------------- + +class CommandObjectWatchpointIgnore : public CommandObjectParsed +{ +public: + CommandObjectWatchpointIgnore (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "watchpoint ignore", + "Set ignore count on the specified watchpoint(s). If no watchpoints are specified, set them all.", + NULL), + m_options (interpreter) + { + CommandArgumentEntry arg; + CommandObject::AddIDsArgumentData(arg, eArgTypeWatchpointID, eArgTypeWatchpointIDRange); + // Add the entry for the first argument for this command to the object's arguments vector. + m_arguments.push_back(arg); + } + + virtual + ~CommandObjectWatchpointIgnore () {} + + virtual Options * + GetOptions () + { + return &m_options; + } + + class CommandOptions : public Options + { + public: + + CommandOptions (CommandInterpreter &interpreter) : + Options (interpreter), + m_ignore_count (0) + { + } + + virtual + ~CommandOptions () {} + + virtual Error + SetOptionValue (uint32_t option_idx, const char *option_arg) + { + Error error; + const int short_option = m_getopt_table[option_idx].val; + + switch (short_option) + { + case 'i': + { + m_ignore_count = Args::StringToUInt32(option_arg, UINT32_MAX, 0); + if (m_ignore_count == UINT32_MAX) + error.SetErrorStringWithFormat ("invalid ignore count '%s'", option_arg); + } + break; + default: + error.SetErrorStringWithFormat ("unrecognized option '%c'", short_option); + break; + } + + return error; + } + + void + OptionParsingStarting () + { + m_ignore_count = 0; + } + + const OptionDefinition * + GetDefinitions () + { + return g_option_table; + } + + + // Options table: Required for subclasses of Options. + + static OptionDefinition g_option_table[]; + + // Instance variables to hold the values for command options. + + uint32_t m_ignore_count; + }; + +protected: + virtual bool + DoExecute (Args& command, + CommandReturnObject &result) + { + Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get(); + if (!CheckTargetForWatchpointOperations(target, result)) + return false; + + Mutex::Locker locker; + target->GetWatchpointList().GetListMutex(locker); + + const WatchpointList &watchpoints = target->GetWatchpointList(); + + size_t num_watchpoints = watchpoints.GetSize(); + + if (num_watchpoints == 0) + { + result.AppendError("No watchpoints exist to be ignored."); + result.SetStatus(eReturnStatusFailed); + return false; + } + + if (command.GetArgumentCount() == 0) + { + target->IgnoreAllWatchpoints(m_options.m_ignore_count); + result.AppendMessageWithFormat("All watchpoints ignored. (%lu watchpoints)\n", num_watchpoints); + result.SetStatus (eReturnStatusSuccessFinishNoResult); + } + else + { + // Particular watchpoints selected; ignore them. + std::vector wp_ids; + if (!CommandObjectMultiwordWatchpoint::VerifyWatchpointIDs(target, command, wp_ids)) + { + result.AppendError("Invalid watchpoints specification."); + result.SetStatus(eReturnStatusFailed); + return false; + } + + int count = 0; + const size_t size = wp_ids.size(); + for (size_t i = 0; i < size; ++i) + if (target->IgnoreWatchpointByID(wp_ids[i], m_options.m_ignore_count)) + ++count; + result.AppendMessageWithFormat("%d watchpoints ignored.\n",count); + result.SetStatus (eReturnStatusSuccessFinishNoResult); + } + + return result.Succeeded(); + } + +private: + CommandOptions m_options; +}; + +#pragma mark Ignore::CommandOptions +OptionDefinition +CommandObjectWatchpointIgnore::CommandOptions::g_option_table[] = +{ + { LLDB_OPT_SET_ALL, true, "ignore-count", 'i', required_argument, NULL, 0, eArgTypeCount, "Set the number of times this watchpoint is skipped before stopping." }, + { 0, false, NULL, 0 , 0, NULL, 0, eArgTypeNone, NULL } +}; + + +//------------------------------------------------------------------------- +// CommandObjectWatchpointModify +//------------------------------------------------------------------------- +#pragma mark Modify + +class CommandObjectWatchpointModify : public CommandObjectParsed +{ +public: + + CommandObjectWatchpointModify (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "watchpoint modify", + "Modify the options on a watchpoint or set of watchpoints in the executable. " + "If no watchpoint is specified, act on the last created watchpoint. " + "Passing an empty argument clears the modification.", + NULL), + m_options (interpreter) + { + CommandArgumentEntry arg; + CommandObject::AddIDsArgumentData(arg, eArgTypeWatchpointID, eArgTypeWatchpointIDRange); + // Add the entry for the first argument for this command to the object's arguments vector. + m_arguments.push_back (arg); + } + + virtual + ~CommandObjectWatchpointModify () {} + + virtual Options * + GetOptions () + { + return &m_options; + } + + class CommandOptions : public Options + { + public: + + CommandOptions (CommandInterpreter &interpreter) : + Options (interpreter), + m_condition (), + m_condition_passed (false) + { + } + + virtual + ~CommandOptions () {} + + virtual Error + SetOptionValue (uint32_t option_idx, const char *option_arg) + { + Error error; + const int short_option = m_getopt_table[option_idx].val; + + switch (short_option) + { + case 'c': + if (option_arg != NULL) + m_condition.assign (option_arg); + else + m_condition.clear(); + m_condition_passed = true; + break; + default: + error.SetErrorStringWithFormat ("unrecognized option '%c'", short_option); + break; + } + + return error; + } + + void + OptionParsingStarting () + { + m_condition.clear(); + m_condition_passed = false; + } + + const OptionDefinition* + GetDefinitions () + { + return g_option_table; + } + + // Options table: Required for subclasses of Options. + + static OptionDefinition g_option_table[]; + + // Instance variables to hold the values for command options. + + std::string m_condition; + bool m_condition_passed; + }; + +protected: + virtual bool + DoExecute (Args& command, CommandReturnObject &result) + { + Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get(); + if (!CheckTargetForWatchpointOperations(target, result)) + return false; + + Mutex::Locker locker; + target->GetWatchpointList().GetListMutex(locker); + + const WatchpointList &watchpoints = target->GetWatchpointList(); + + size_t num_watchpoints = watchpoints.GetSize(); + + if (num_watchpoints == 0) + { + result.AppendError("No watchpoints exist to be modified."); + result.SetStatus(eReturnStatusFailed); + return false; + } + + if (command.GetArgumentCount() == 0) + { + WatchpointSP wp_sp = target->GetLastCreatedWatchpoint(); + wp_sp->SetCondition(m_options.m_condition.c_str()); + result.SetStatus (eReturnStatusSuccessFinishNoResult); + } + else + { + // Particular watchpoints selected; set condition on them. + std::vector wp_ids; + if (!CommandObjectMultiwordWatchpoint::VerifyWatchpointIDs(target, command, wp_ids)) + { + result.AppendError("Invalid watchpoints specification."); + result.SetStatus(eReturnStatusFailed); + return false; + } + + int count = 0; + const size_t size = wp_ids.size(); + for (size_t i = 0; i < size; ++i) + { + WatchpointSP wp_sp = watchpoints.FindByID(wp_ids[i]); + if (wp_sp) + { + wp_sp->SetCondition(m_options.m_condition.c_str()); + ++count; + } + } + result.AppendMessageWithFormat("%d watchpoints modified.\n",count); + result.SetStatus (eReturnStatusSuccessFinishNoResult); + } + + return result.Succeeded(); + } + +private: + CommandOptions m_options; +}; + +#pragma mark Modify::CommandOptions +OptionDefinition +CommandObjectWatchpointModify::CommandOptions::g_option_table[] = +{ +{ LLDB_OPT_SET_ALL, false, "condition", 'c', required_argument, NULL, 0, eArgTypeExpression, "The watchpoint stops only if this condition expression evaluates to true."}, +{ 0, false, NULL, 0 , 0, NULL, 0, eArgTypeNone, NULL } +}; + +//------------------------------------------------------------------------- +// CommandObjectWatchpointSetVariable +//------------------------------------------------------------------------- +#pragma mark SetVariable + +class CommandObjectWatchpointSetVariable : public CommandObjectParsed +{ +public: + + CommandObjectWatchpointSetVariable (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "watchpoint set variable", + "Set a watchpoint on a variable. " + "Use the '-w' option to specify the type of watchpoint and " + "the '-x' option to specify the byte size to watch for. " + "If no '-w' option is specified, it defaults to write. " + "If no '-x' option is specified, it defaults to the variable's " + "byte size. " + "Note that there are limited hardware resources for watchpoints. " + "If watchpoint setting fails, consider disable/delete existing ones " + "to free up resources.", + NULL, + eFlagRequiresFrame | + eFlagTryTargetAPILock | + eFlagProcessMustBeLaunched | + eFlagProcessMustBePaused ), + m_option_group (interpreter), + m_option_watchpoint () + { + SetHelpLong( + "Examples: \n\ + \n\ + watchpoint set variable -w read_wriate my_global_var \n\ + # Watch my_global_var for read/write access, with the region to watch corresponding to the byte size of the data type.\n"); + + CommandArgumentEntry arg; + CommandArgumentData var_name_arg; + + // Define the only variant of this arg. + var_name_arg.arg_type = eArgTypeVarName; + var_name_arg.arg_repetition = eArgRepeatPlain; + + // Push the variant into the argument entry. + arg.push_back (var_name_arg); + + // Push the data for the only argument into the m_arguments vector. + m_arguments.push_back (arg); + + // Absorb the '-w' and '-x' options into our option group. + m_option_group.Append (&m_option_watchpoint, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1); + m_option_group.Finalize(); + } + + virtual + ~CommandObjectWatchpointSetVariable () {} + + virtual Options * + GetOptions () + { + return &m_option_group; + } + +protected: + static size_t GetVariableCallback (void *baton, + const char *name, + VariableList &variable_list) + { + Target *target = static_cast(baton); + if (target) + { + return target->GetImages().FindGlobalVariables (ConstString(name), + true, + UINT32_MAX, + variable_list); + } + return 0; + } + + virtual bool + DoExecute (Args& command, CommandReturnObject &result) + { + Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get(); + StackFrame *frame = m_exe_ctx.GetFramePtr(); + + // If no argument is present, issue an error message. There's no way to set a watchpoint. + if (command.GetArgumentCount() <= 0) + { + result.GetErrorStream().Printf("error: required argument missing; specify your program variable to watch for\n"); + result.SetStatus(eReturnStatusFailed); + return false; + } + + // If no '-w' is specified, default to '-w write'. + if (!m_option_watchpoint.watch_type_specified) + { + m_option_watchpoint.watch_type = OptionGroupWatchpoint::eWatchWrite; + } + + // We passed the sanity check for the command. + // Proceed to set the watchpoint now. + lldb::addr_t addr = 0; + size_t size = 0; + + VariableSP var_sp; + ValueObjectSP valobj_sp; + Stream &output_stream = result.GetOutputStream(); + + // A simple watch variable gesture allows only one argument. + if (command.GetArgumentCount() != 1) + { + result.GetErrorStream().Printf("error: specify exactly one variable to watch for\n"); + result.SetStatus(eReturnStatusFailed); + return false; + } + + // Things have checked out ok... + Error error; + uint32_t expr_path_options = StackFrame::eExpressionPathOptionCheckPtrVsMember | + StackFrame::eExpressionPathOptionsAllowDirectIVarAccess; + valobj_sp = frame->GetValueForVariableExpressionPath (command.GetArgumentAtIndex(0), + eNoDynamicValues, + expr_path_options, + var_sp, + error); + + if (!valobj_sp) + { + // Not in the frame; let's check the globals. + + VariableList variable_list; + ValueObjectList valobj_list; + + Error error (Variable::GetValuesForVariableExpressionPath (command.GetArgumentAtIndex(0), + m_exe_ctx.GetBestExecutionContextScope(), + GetVariableCallback, + target, + variable_list, + valobj_list)); + + if (valobj_list.GetSize()) + valobj_sp = valobj_list.GetValueObjectAtIndex(0); + } + + ClangASTType clang_type; + + if (valobj_sp) + { + AddressType addr_type; + addr = valobj_sp->GetAddressOf(false, &addr_type); + if (addr_type == eAddressTypeLoad) + { + // We're in business. + // Find out the size of this variable. + size = m_option_watchpoint.watch_size == 0 ? valobj_sp->GetByteSize() + : m_option_watchpoint.watch_size; + } + clang_type = valobj_sp->GetClangType(); + } + else + { + const char *error_cstr = error.AsCString(NULL); + if (error_cstr) + result.GetErrorStream().Printf("error: %s\n", error_cstr); + else + result.GetErrorStream().Printf ("error: unable to find any variable expression path that matches '%s'\n", + command.GetArgumentAtIndex(0)); + return false; + } + + // Now it's time to create the watchpoint. + uint32_t watch_type = m_option_watchpoint.watch_type; + + error.Clear(); + Watchpoint *wp = target->CreateWatchpoint(addr, size, &clang_type, watch_type, error).get(); + if (wp) + { + wp->SetWatchSpec(command.GetArgumentAtIndex(0)); + wp->SetWatchVariable(true); + if (var_sp && var_sp->GetDeclaration().GetFile()) + { + StreamString ss; + // True to show fullpath for declaration file. + var_sp->GetDeclaration().DumpStopContext(&ss, true); + wp->SetDeclInfo(ss.GetString()); + } + output_stream.Printf("Watchpoint created: "); + wp->GetDescription(&output_stream, lldb::eDescriptionLevelFull); + output_stream.EOL(); + result.SetStatus(eReturnStatusSuccessFinishResult); + } + else + { + result.AppendErrorWithFormat("Watchpoint creation failed (addr=0x%" PRIx64 ", size=%lu, variable expression='%s').\n", + addr, size, command.GetArgumentAtIndex(0)); + if (error.AsCString(NULL)) + result.AppendError(error.AsCString()); + result.SetStatus(eReturnStatusFailed); + } + + return result.Succeeded(); + } + +private: + OptionGroupOptions m_option_group; + OptionGroupWatchpoint m_option_watchpoint; +}; + +//------------------------------------------------------------------------- +// CommandObjectWatchpointSetExpression +//------------------------------------------------------------------------- +#pragma mark Set + +class CommandObjectWatchpointSetExpression : public CommandObjectRaw +{ +public: + + CommandObjectWatchpointSetExpression (CommandInterpreter &interpreter) : + CommandObjectRaw (interpreter, + "watchpoint set expression", + "Set a watchpoint on an address by supplying an expression. " + "Use the '-w' option to specify the type of watchpoint and " + "the '-x' option to specify the byte size to watch for. " + "If no '-w' option is specified, it defaults to write. " + "If no '-x' option is specified, it defaults to the target's " + "pointer byte size. " + "Note that there are limited hardware resources for watchpoints. " + "If watchpoint setting fails, consider disable/delete existing ones " + "to free up resources.", + NULL, + eFlagRequiresFrame | + eFlagTryTargetAPILock | + eFlagProcessMustBeLaunched | + eFlagProcessMustBePaused ), + m_option_group (interpreter), + m_option_watchpoint () + { + SetHelpLong( + "Examples: \n\ + \n\ + watchpoint set expression -w write -x 1 -- foo + 32\n\ + # Watch write access for the 1-byte region pointed to by the address 'foo + 32'.\n"); + + CommandArgumentEntry arg; + CommandArgumentData expression_arg; + + // Define the only variant of this arg. + expression_arg.arg_type = eArgTypeExpression; + expression_arg.arg_repetition = eArgRepeatPlain; + + // Push the only variant into the argument entry. + arg.push_back (expression_arg); + + // Push the data for the only argument into the m_arguments vector. + m_arguments.push_back (arg); + + // Absorb the '-w' and '-x' options into our option group. + m_option_group.Append (&m_option_watchpoint, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1); + m_option_group.Finalize(); + } + + + virtual + ~CommandObjectWatchpointSetExpression () {} + + // Overrides base class's behavior where WantsCompletion = !WantsRawCommandString. + virtual bool + WantsCompletion() { return true; } + + virtual Options * + GetOptions () + { + return &m_option_group; + } + +protected: + virtual bool + DoExecute (const char *raw_command, CommandReturnObject &result) + { + m_option_group.NotifyOptionParsingStarting(); // This is a raw command, so notify the option group + + Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get(); + StackFrame *frame = m_exe_ctx.GetFramePtr(); + + Args command(raw_command); + const char *expr = NULL; + if (raw_command[0] == '-') + { + // We have some options and these options MUST end with --. + const char *end_options = NULL; + const char *s = raw_command; + while (s && s[0]) + { + end_options = ::strstr (s, "--"); + if (end_options) + { + end_options += 2; // Get past the "--" + if (::isspace (end_options[0])) + { + expr = end_options; + while (::isspace (*expr)) + ++expr; + break; + } + } + s = end_options; + } + + if (end_options) + { + Args args (raw_command, end_options - raw_command); + if (!ParseOptions (args, result)) + return false; + + Error error (m_option_group.NotifyOptionParsingFinished()); + if (error.Fail()) + { + result.AppendError (error.AsCString()); + result.SetStatus (eReturnStatusFailed); + return false; + } + } + } + + if (expr == NULL) + expr = raw_command; + + // If no argument is present, issue an error message. There's no way to set a watchpoint. + if (command.GetArgumentCount() == 0) + { + result.GetErrorStream().Printf("error: required argument missing; specify an expression to evaulate into the address to watch for\n"); + result.SetStatus(eReturnStatusFailed); + return false; + } + + // If no '-w' is specified, default to '-w write'. + if (!m_option_watchpoint.watch_type_specified) + { + m_option_watchpoint.watch_type = OptionGroupWatchpoint::eWatchWrite; + } + + // We passed the sanity check for the command. + // Proceed to set the watchpoint now. + lldb::addr_t addr = 0; + size_t size = 0; + + ValueObjectSP valobj_sp; + + // Use expression evaluation to arrive at the address to watch. + EvaluateExpressionOptions options; + options.SetCoerceToId(false) + .SetUnwindOnError(true) + .SetKeepInMemory(false) + .SetRunOthers(true) + .SetTimeoutUsec(0); + + ExecutionResults expr_result = target->EvaluateExpression (expr, + frame, + valobj_sp, + options); + if (expr_result != eExecutionCompleted) + { + result.GetErrorStream().Printf("error: expression evaluation of address to watch failed\n"); + result.GetErrorStream().Printf("expression evaluated: %s\n", expr); + result.SetStatus(eReturnStatusFailed); + return false; + } + + // Get the address to watch. + bool success = false; + addr = valobj_sp->GetValueAsUnsigned(0, &success); + if (!success) + { + result.GetErrorStream().Printf("error: expression did not evaluate to an address\n"); + result.SetStatus(eReturnStatusFailed); + return false; + } + + if (m_option_watchpoint.watch_size != 0) + size = m_option_watchpoint.watch_size; + else + size = target->GetArchitecture().GetAddressByteSize(); + + // Now it's time to create the watchpoint. + uint32_t watch_type = m_option_watchpoint.watch_type; + + // Fetch the type from the value object, the type of the watched object is the pointee type + /// of the expression, so convert to that if we found a valid type. + ClangASTType clang_type(valobj_sp->GetClangType()); + + Error error; + Watchpoint *wp = target->CreateWatchpoint(addr, size, &clang_type, watch_type, error).get(); + if (wp) + { + Stream &output_stream = result.GetOutputStream(); + output_stream.Printf("Watchpoint created: "); + wp->GetDescription(&output_stream, lldb::eDescriptionLevelFull); + output_stream.EOL(); + result.SetStatus(eReturnStatusSuccessFinishResult); + } + else + { + result.AppendErrorWithFormat("Watchpoint creation failed (addr=0x%" PRIx64 ", size=%lu).\n", + addr, size); + if (error.AsCString(NULL)) + result.AppendError(error.AsCString()); + result.SetStatus(eReturnStatusFailed); + } + + return result.Succeeded(); + } + +private: + OptionGroupOptions m_option_group; + OptionGroupWatchpoint m_option_watchpoint; +}; + +//------------------------------------------------------------------------- +// CommandObjectWatchpointSet +//------------------------------------------------------------------------- +#pragma mark Set + +class CommandObjectWatchpointSet : public CommandObjectMultiword +{ +public: + + CommandObjectWatchpointSet (CommandInterpreter &interpreter) : + CommandObjectMultiword (interpreter, + "watchpoint set", + "A set of commands for setting a watchpoint.", + "watchpoint set []") + { + + LoadSubCommand ("variable", CommandObjectSP (new CommandObjectWatchpointSetVariable (interpreter))); + LoadSubCommand ("expression", CommandObjectSP (new CommandObjectWatchpointSetExpression (interpreter))); + } + + + virtual + ~CommandObjectWatchpointSet () {} + +}; + +//------------------------------------------------------------------------- +// CommandObjectMultiwordWatchpoint +//------------------------------------------------------------------------- +#pragma mark MultiwordWatchpoint + +CommandObjectMultiwordWatchpoint::CommandObjectMultiwordWatchpoint(CommandInterpreter &interpreter) : + CommandObjectMultiword (interpreter, + "watchpoint", + "A set of commands for operating on watchpoints.", + "watchpoint []") +{ + CommandObjectSP list_command_object (new CommandObjectWatchpointList (interpreter)); + CommandObjectSP enable_command_object (new CommandObjectWatchpointEnable (interpreter)); + CommandObjectSP disable_command_object (new CommandObjectWatchpointDisable (interpreter)); + CommandObjectSP delete_command_object (new CommandObjectWatchpointDelete (interpreter)); + CommandObjectSP ignore_command_object (new CommandObjectWatchpointIgnore (interpreter)); + CommandObjectSP command_command_object (new CommandObjectWatchpointCommand (interpreter)); + CommandObjectSP modify_command_object (new CommandObjectWatchpointModify (interpreter)); + CommandObjectSP set_command_object (new CommandObjectWatchpointSet (interpreter)); + + list_command_object->SetCommandName ("watchpoint list"); + enable_command_object->SetCommandName("watchpoint enable"); + disable_command_object->SetCommandName("watchpoint disable"); + delete_command_object->SetCommandName("watchpoint delete"); + ignore_command_object->SetCommandName("watchpoint ignore"); + command_command_object->SetCommandName ("watchpoint command"); + modify_command_object->SetCommandName("watchpoint modify"); + set_command_object->SetCommandName("watchpoint set"); + + LoadSubCommand ("list", list_command_object); + LoadSubCommand ("enable", enable_command_object); + LoadSubCommand ("disable", disable_command_object); + LoadSubCommand ("delete", delete_command_object); + LoadSubCommand ("ignore", ignore_command_object); + LoadSubCommand ("command", command_command_object); + LoadSubCommand ("modify", modify_command_object); + LoadSubCommand ("set", set_command_object); +} + +CommandObjectMultiwordWatchpoint::~CommandObjectMultiwordWatchpoint() +{ +} + diff --git a/contrib/llvm/tools/lldb/source/Commands/CommandObjectWatchpoint.h b/contrib/llvm/tools/lldb/source/Commands/CommandObjectWatchpoint.h new file mode 100644 index 00000000000..1b1ebd7764a --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Commands/CommandObjectWatchpoint.h @@ -0,0 +1,43 @@ +//===-- CommandObjectWatchpoint.h -------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_CommandObjectWatchpoint_h_ +#define liblldb_CommandObjectWatchpoint_h_ + +// C Includes +// C++ Includes + +// Other libraries and framework includes +// Project includes +#include "lldb/Interpreter/CommandObjectMultiword.h" +#include "lldb/Interpreter/Options.h" +#include "lldb/Interpreter/OptionGroupWatchpoint.h" + +namespace lldb_private { + +//------------------------------------------------------------------------- +// CommandObjectMultiwordWatchpoint +//------------------------------------------------------------------------- + +class CommandObjectMultiwordWatchpoint : public CommandObjectMultiword +{ +public: + CommandObjectMultiwordWatchpoint (CommandInterpreter &interpreter); + + virtual + ~CommandObjectMultiwordWatchpoint (); + + static bool + VerifyWatchpointIDs(Target *target, Args &args, std::vector &wp_ids); + +}; + +} // namespace lldb_private + +#endif // liblldb_CommandObjectWatchpoint_h_ diff --git a/contrib/llvm/tools/lldb/source/Commands/CommandObjectWatchpointCommand.cpp b/contrib/llvm/tools/lldb/source/Commands/CommandObjectWatchpointCommand.cpp new file mode 100644 index 00000000000..4e200465031 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Commands/CommandObjectWatchpointCommand.cpp @@ -0,0 +1,850 @@ +//===-- CommandObjectWatchpointCommand.cpp ----------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +// C Includes +// C++ Includes + + +#include "CommandObjectWatchpointCommand.h" +#include "CommandObjectWatchpoint.h" + +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Interpreter/CommandReturnObject.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" +#include "lldb/Breakpoint/Watchpoint.h" +#include "lldb/Breakpoint/StoppointCallbackContext.h" +#include "lldb/Core/State.h" + +#include + +using namespace lldb; +using namespace lldb_private; + +//------------------------------------------------------------------------- +// CommandObjectWatchpointCommandAdd +//------------------------------------------------------------------------- + + +class CommandObjectWatchpointCommandAdd : public CommandObjectParsed +{ +public: + + CommandObjectWatchpointCommandAdd (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "add", + "Add a set of commands to a watchpoint, to be executed whenever the watchpoint is hit.", + NULL), + m_options (interpreter) + { + SetHelpLong ( +"\nGeneral information about entering watchpoint commands \n\ +------------------------------------------------------ \n\ + \n\ +This command will cause you to be prompted to enter the command or set \n\ +of commands you wish to be executed when the specified watchpoint is \n\ +hit. You will be told to enter your command(s), and will see a '> ' \n\ +prompt. Because you can enter one or many commands to be executed when \n\ +a watchpoint is hit, you will continue to be prompted after each \n\ +new-line that you enter, until you enter the word 'DONE', which will \n\ +cause the commands you have entered to be stored with the watchpoint \n\ +and executed when the watchpoint is hit. \n\ + \n\ +Syntax checking is not necessarily done when watchpoint commands are \n\ +entered. An improperly written watchpoint command will attempt to get \n\ +executed when the watchpoint gets hit, and usually silently fail. If \n\ +your watchpoint command does not appear to be getting executed, go \n\ +back and check your syntax. \n\ + \n\ + \n\ +Special information about PYTHON watchpoint commands \n\ +---------------------------------------------------- \n\ + \n\ +You may enter either one line of Python or multiple lines of Python \n\ +(including defining whole functions, if desired). If you enter a \n\ +single line of Python, that will be passed to the Python interpreter \n\ +'as is' when the watchpoint gets hit. If you enter function \n\ +definitions, they will be passed to the Python interpreter as soon as \n\ +you finish entering the watchpoint command, and they can be called \n\ +later (don't forget to add calls to them, if you want them called when \n\ +the watchpoint is hit). If you enter multiple lines of Python that \n\ +are not function definitions, they will be collected into a new, \n\ +automatically generated Python function, and a call to the newly \n\ +generated function will be attached to the watchpoint. \n\ + \n\ +This auto-generated function is passed in two arguments: \n\ + \n\ + frame: an SBFrame object representing the frame which hit the watchpoint. \n\ + From the frame you can get back to the thread and process. \n\ + wp: the watchpoint that was hit. \n\ + \n\ +Important Note: Because loose Python code gets collected into functions, \n\ +if you want to access global variables in the 'loose' code, you need to \n\ +specify that they are global, using the 'global' keyword. Be sure to \n\ +use correct Python syntax, including indentation, when entering Python \n\ +watchpoint commands. \n\ + \n\ +As a third option, you can pass the name of an already existing Python function \n\ +and that function will be attached to the watchpoint. It will get passed the \n\ +frame and wp_loc arguments mentioned above. \n\ + \n\ +Example Python one-line watchpoint command: \n\ + \n\ +(lldb) watchpoint command add -s python 1 \n\ +Enter your Python command(s). Type 'DONE' to end. \n\ +> print \"Hit this watchpoint!\" \n\ +> DONE \n\ + \n\ +As a convenience, this also works for a short Python one-liner: \n\ +(lldb) watchpoint command add -s python 1 -o \"import time; print time.asctime()\" \n\ +(lldb) run \n\ +Launching '.../a.out' (x86_64) \n\ +(lldb) Fri Sep 10 12:17:45 2010 \n\ +Process 21778 Stopped \n\ +* thread #1: tid = 0x2e03, 0x0000000100000de8 a.out`c + 7 at main.c:39, stop reason = watchpoint 1.1, queue = com.apple.main-thread \n\ + 36 \n\ + 37 int c(int val)\n\ + 38 {\n\ + 39 -> return val + 3;\n\ + 40 }\n\ + 41 \n\ + 42 int main (int argc, char const *argv[])\n\ +(lldb) \n\ + \n\ +Example multiple line Python watchpoint command, using function definition: \n\ + \n\ +(lldb) watchpoint command add -s python 1 \n\ +Enter your Python command(s). Type 'DONE' to end. \n\ +> def watchpoint_output (wp_no): \n\ +> out_string = \"Hit watchpoint number \" + repr (wp_no) \n\ +> print out_string \n\ +> return True \n\ +> watchpoint_output (1) \n\ +> DONE \n\ + \n\ + \n\ +Example multiple line Python watchpoint command, using 'loose' Python: \n\ + \n\ +(lldb) watchpoint command add -s p 1 \n\ +Enter your Python command(s). Type 'DONE' to end. \n\ +> global wp_count \n\ +> wp_count = wp_count + 1 \n\ +> print \"Hit this watchpoint \" + repr(wp_count) + \" times!\" \n\ +> DONE \n\ + \n\ +In this case, since there is a reference to a global variable, \n\ +'wp_count', you will also need to make sure 'wp_count' exists and is \n\ +initialized: \n\ + \n\ +(lldb) script \n\ +>>> wp_count = 0 \n\ +>>> quit() \n\ + \n\ +(lldb) \n\ + \n\ + \n\ +Final Note: If you get a warning that no watchpoint command was generated, \n\ +but you did not get any syntax errors, you probably forgot to add a call \n\ +to your functions. \n\ + \n\ +Special information about debugger command watchpoint commands \n\ +-------------------------------------------------------------- \n\ + \n\ +You may enter any debugger command, exactly as you would at the \n\ +debugger prompt. You may enter as many debugger commands as you like, \n\ +but do NOT enter more than one command per line. \n" ); + + CommandArgumentEntry arg; + CommandArgumentData wp_id_arg; + + // Define the first (and only) variant of this arg. + wp_id_arg.arg_type = eArgTypeWatchpointID; + wp_id_arg.arg_repetition = eArgRepeatPlain; + + // There is only one variant this argument could be; put it into the argument entry. + arg.push_back (wp_id_arg); + + // Push the data for the first argument into the m_arguments vector. + m_arguments.push_back (arg); + } + + virtual + ~CommandObjectWatchpointCommandAdd () {} + + virtual Options * + GetOptions () + { + return &m_options; + } + + void + CollectDataForWatchpointCommandCallback (WatchpointOptions *wp_options, + CommandReturnObject &result) + { + InputReaderSP reader_sp (new InputReader(m_interpreter.GetDebugger())); + std::unique_ptr data_ap(new WatchpointOptions::CommandData()); + if (reader_sp && data_ap.get()) + { + BatonSP baton_sp (new WatchpointOptions::CommandBaton (data_ap.release())); + wp_options->SetCallback (WatchpointOptionsCallbackFunction, baton_sp); + + Error err (reader_sp->Initialize (CommandObjectWatchpointCommandAdd::GenerateWatchpointCommandCallback, + wp_options, // callback_data + eInputReaderGranularityLine, // token size, to pass to callback function + "DONE", // end token + "> ", // prompt + true)); // echo input + if (err.Success()) + { + m_interpreter.GetDebugger().PushInputReader (reader_sp); + result.SetStatus (eReturnStatusSuccessFinishNoResult); + } + else + { + result.AppendError (err.AsCString()); + result.SetStatus (eReturnStatusFailed); + } + } + else + { + result.AppendError("out of memory"); + result.SetStatus (eReturnStatusFailed); + } + + } + + /// Set a one-liner as the callback for the watchpoint. + void + SetWatchpointCommandCallback (WatchpointOptions *wp_options, + const char *oneliner) + { + std::unique_ptr data_ap(new WatchpointOptions::CommandData()); + + // It's necessary to set both user_source and script_source to the oneliner. + // The former is used to generate callback description (as in watchpoint command list) + // while the latter is used for Python to interpret during the actual callback. + data_ap->user_source.AppendString (oneliner); + data_ap->script_source.assign (oneliner); + data_ap->stop_on_error = m_options.m_stop_on_error; + + BatonSP baton_sp (new WatchpointOptions::CommandBaton (data_ap.release())); + wp_options->SetCallback (WatchpointOptionsCallbackFunction, baton_sp); + + return; + } + + static size_t + GenerateWatchpointCommandCallback (void *callback_data, + InputReader &reader, + lldb::InputReaderAction notification, + const char *bytes, + size_t bytes_len) + { + StreamSP out_stream = reader.GetDebugger().GetAsyncOutputStream(); + bool batch_mode = reader.GetDebugger().GetCommandInterpreter().GetBatchCommandMode(); + + switch (notification) + { + case eInputReaderActivate: + if (!batch_mode) + { + out_stream->Printf ("%s\n", g_reader_instructions); + if (reader.GetPrompt()) + out_stream->Printf ("%s", reader.GetPrompt()); + out_stream->Flush(); + } + break; + + case eInputReaderDeactivate: + break; + + case eInputReaderReactivate: + if (reader.GetPrompt() && !batch_mode) + { + out_stream->Printf ("%s", reader.GetPrompt()); + out_stream->Flush(); + } + break; + + case eInputReaderAsynchronousOutputWritten: + break; + + case eInputReaderGotToken: + if (bytes && bytes_len && callback_data) + { + WatchpointOptions *wp_options = (WatchpointOptions *) callback_data; + if (wp_options) + { + Baton *wp_options_baton = wp_options->GetBaton(); + if (wp_options_baton) + ((WatchpointOptions::CommandData *)wp_options_baton->m_data)->user_source.AppendString (bytes, bytes_len); + } + } + if (!reader.IsDone() && reader.GetPrompt() && !batch_mode) + { + out_stream->Printf ("%s", reader.GetPrompt()); + out_stream->Flush(); + } + break; + + case eInputReaderInterrupt: + { + // Finish, and cancel the watchpoint command. + reader.SetIsDone (true); + WatchpointOptions *wp_options = (WatchpointOptions *) callback_data; + if (wp_options) + { + Baton *wp_options_baton = wp_options->GetBaton (); + if (wp_options_baton) + { + ((WatchpointOptions::CommandData *) wp_options_baton->m_data)->user_source.Clear(); + ((WatchpointOptions::CommandData *) wp_options_baton->m_data)->script_source.clear(); + } + } + if (!batch_mode) + { + out_stream->Printf ("Warning: No command attached to watchpoint.\n"); + out_stream->Flush(); + } + } + break; + + case eInputReaderEndOfFile: + reader.SetIsDone (true); + break; + + case eInputReaderDone: + break; + } + + return bytes_len; + } + + static bool + WatchpointOptionsCallbackFunction (void *baton, + StoppointCallbackContext *context, + lldb::user_id_t watch_id) + { + bool ret_value = true; + if (baton == NULL) + return true; + + + WatchpointOptions::CommandData *data = (WatchpointOptions::CommandData *) baton; + StringList &commands = data->user_source; + + if (commands.GetSize() > 0) + { + ExecutionContext exe_ctx (context->exe_ctx_ref); + Target *target = exe_ctx.GetTargetPtr(); + if (target) + { + CommandReturnObject result; + Debugger &debugger = target->GetDebugger(); + // Rig up the results secondary output stream to the debugger's, so the output will come out synchronously + // if the debugger is set up that way. + + StreamSP output_stream (debugger.GetAsyncOutputStream()); + StreamSP error_stream (debugger.GetAsyncErrorStream()); + result.SetImmediateOutputStream (output_stream); + result.SetImmediateErrorStream (error_stream); + + bool stop_on_continue = true; + bool echo_commands = false; + bool print_results = true; + + debugger.GetCommandInterpreter().HandleCommands (commands, + &exe_ctx, + stop_on_continue, + data->stop_on_error, + echo_commands, + print_results, + eLazyBoolNo, + result); + result.GetImmediateOutputStream()->Flush(); + result.GetImmediateErrorStream()->Flush(); + } + } + return ret_value; + } + + class CommandOptions : public Options + { + public: + + CommandOptions (CommandInterpreter &interpreter) : + Options (interpreter), + m_use_commands (false), + m_use_script_language (false), + m_script_language (eScriptLanguageNone), + m_use_one_liner (false), + m_one_liner(), + m_function_name() + { + } + + virtual + ~CommandOptions () {} + + virtual Error + SetOptionValue (uint32_t option_idx, const char *option_arg) + { + Error error; + const int short_option = m_getopt_table[option_idx].val; + + switch (short_option) + { + case 'o': + m_use_one_liner = true; + m_one_liner = option_arg; + break; + + case 's': + m_script_language = (lldb::ScriptLanguage) Args::StringToOptionEnum (option_arg, + g_option_table[option_idx].enum_values, + eScriptLanguageNone, + error); + + if (m_script_language == eScriptLanguagePython || m_script_language == eScriptLanguageDefault) + { + m_use_script_language = true; + } + else + { + m_use_script_language = false; + } + break; + + case 'e': + { + bool success = false; + m_stop_on_error = Args::StringToBoolean(option_arg, false, &success); + if (!success) + error.SetErrorStringWithFormat("invalid value for stop-on-error: \"%s\"", option_arg); + } + break; + + case 'F': + { + m_use_one_liner = false; + m_use_script_language = true; + m_function_name.assign(option_arg); + } + break; + + default: + break; + } + return error; + } + void + OptionParsingStarting () + { + m_use_commands = true; + m_use_script_language = false; + m_script_language = eScriptLanguageNone; + + m_use_one_liner = false; + m_stop_on_error = true; + m_one_liner.clear(); + m_function_name.clear(); + } + + const OptionDefinition* + GetDefinitions () + { + return g_option_table; + } + + // Options table: Required for subclasses of Options. + + static OptionDefinition g_option_table[]; + + // Instance variables to hold the values for command options. + + bool m_use_commands; + bool m_use_script_language; + lldb::ScriptLanguage m_script_language; + + // Instance variables to hold the values for one_liner options. + bool m_use_one_liner; + std::string m_one_liner; + bool m_stop_on_error; + std::string m_function_name; + }; + +protected: + virtual bool + DoExecute (Args& command, CommandReturnObject &result) + { + Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get(); + + if (target == NULL) + { + result.AppendError ("There is not a current executable; there are no watchpoints to which to add commands"); + result.SetStatus (eReturnStatusFailed); + return false; + } + + const WatchpointList &watchpoints = target->GetWatchpointList(); + size_t num_watchpoints = watchpoints.GetSize(); + + if (num_watchpoints == 0) + { + result.AppendError ("No watchpoints exist to have commands added"); + result.SetStatus (eReturnStatusFailed); + return false; + } + + if (m_options.m_use_script_language == false && m_options.m_function_name.size()) + { + result.AppendError ("need to enable scripting to have a function run as a watchpoint command"); + result.SetStatus (eReturnStatusFailed); + return false; + } + + std::vector valid_wp_ids; + if (!CommandObjectMultiwordWatchpoint::VerifyWatchpointIDs(target, command, valid_wp_ids)) + { + result.AppendError("Invalid watchpoints specification."); + result.SetStatus(eReturnStatusFailed); + return false; + } + + result.SetStatus(eReturnStatusSuccessFinishNoResult); + const size_t count = valid_wp_ids.size(); + for (size_t i = 0; i < count; ++i) + { + uint32_t cur_wp_id = valid_wp_ids.at (i); + if (cur_wp_id != LLDB_INVALID_WATCH_ID) + { + Watchpoint *wp = target->GetWatchpointList().FindByID (cur_wp_id).get(); + // Sanity check wp first. + if (wp == NULL) continue; + + WatchpointOptions *wp_options = wp->GetOptions(); + // Skip this watchpoint if wp_options is not good. + if (wp_options == NULL) continue; + + // If we are using script language, get the script interpreter + // in order to set or collect command callback. Otherwise, call + // the methods associated with this object. + if (m_options.m_use_script_language) + { + // Special handling for one-liner specified inline. + if (m_options.m_use_one_liner) + { + m_interpreter.GetScriptInterpreter()->SetWatchpointCommandCallback (wp_options, + m_options.m_one_liner.c_str()); + } + // Special handling for using a Python function by name + // instead of extending the watchpoint callback data structures, we just automatize + // what the user would do manually: make their watchpoint command be a function call + else if (m_options.m_function_name.size()) + { + std::string oneliner(m_options.m_function_name); + oneliner += "(frame, wp, internal_dict)"; + m_interpreter.GetScriptInterpreter()->SetWatchpointCommandCallback (wp_options, + oneliner.c_str()); + } + else + { + m_interpreter.GetScriptInterpreter()->CollectDataForWatchpointCommandCallback (wp_options, + result); + } + } + else + { + // Special handling for one-liner specified inline. + if (m_options.m_use_one_liner) + SetWatchpointCommandCallback (wp_options, + m_options.m_one_liner.c_str()); + else + CollectDataForWatchpointCommandCallback (wp_options, + result); + } + } + } + + return result.Succeeded(); + } + +private: + CommandOptions m_options; + static const char *g_reader_instructions; + +}; + +const char * +CommandObjectWatchpointCommandAdd::g_reader_instructions = "Enter your debugger command(s). Type 'DONE' to end."; + +// FIXME: "script-type" needs to have its contents determined dynamically, so somebody can add a new scripting +// language to lldb and have it pickable here without having to change this enumeration by hand and rebuild lldb proper. + +static OptionEnumValueElement +g_script_option_enumeration[4] = +{ + { eScriptLanguageNone, "command", "Commands are in the lldb command interpreter language"}, + { eScriptLanguagePython, "python", "Commands are in the Python language."}, + { eSortOrderByName, "default-script", "Commands are in the default scripting language."}, + { 0, NULL, NULL } +}; + +OptionDefinition +CommandObjectWatchpointCommandAdd::CommandOptions::g_option_table[] = +{ + { LLDB_OPT_SET_1, false, "one-liner", 'o', required_argument, NULL, 0, eArgTypeOneLiner, + "Specify a one-line watchpoint command inline. Be sure to surround it with quotes." }, + + { LLDB_OPT_SET_ALL, false, "stop-on-error", 'e', required_argument, NULL, 0, eArgTypeBoolean, + "Specify whether watchpoint command execution should terminate on error." }, + + { LLDB_OPT_SET_ALL, false, "script-type", 's', required_argument, g_script_option_enumeration, 0, eArgTypeNone, + "Specify the language for the commands - if none is specified, the lldb command interpreter will be used."}, + + { LLDB_OPT_SET_2, false, "python-function", 'F', required_argument, NULL, 0, eArgTypePythonFunction, + "Give the name of a Python function to run as command for this watchpoint. Be sure to give a module name if appropriate."}, + + { 0, false, NULL, 0, 0, NULL, 0, eArgTypeNone, NULL } +}; + +//------------------------------------------------------------------------- +// CommandObjectWatchpointCommandDelete +//------------------------------------------------------------------------- + +class CommandObjectWatchpointCommandDelete : public CommandObjectParsed +{ +public: + CommandObjectWatchpointCommandDelete (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "delete", + "Delete the set of commands from a watchpoint.", + NULL) + { + CommandArgumentEntry arg; + CommandArgumentData wp_id_arg; + + // Define the first (and only) variant of this arg. + wp_id_arg.arg_type = eArgTypeWatchpointID; + wp_id_arg.arg_repetition = eArgRepeatPlain; + + // There is only one variant this argument could be; put it into the argument entry. + arg.push_back (wp_id_arg); + + // Push the data for the first argument into the m_arguments vector. + m_arguments.push_back (arg); + } + + + virtual + ~CommandObjectWatchpointCommandDelete () {} + +protected: + virtual bool + DoExecute (Args& command, CommandReturnObject &result) + { + Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get(); + + if (target == NULL) + { + result.AppendError ("There is not a current executable; there are no watchpoints from which to delete commands"); + result.SetStatus (eReturnStatusFailed); + return false; + } + + const WatchpointList &watchpoints = target->GetWatchpointList(); + size_t num_watchpoints = watchpoints.GetSize(); + + if (num_watchpoints == 0) + { + result.AppendError ("No watchpoints exist to have commands deleted"); + result.SetStatus (eReturnStatusFailed); + return false; + } + + if (command.GetArgumentCount() == 0) + { + result.AppendError ("No watchpoint specified from which to delete the commands"); + result.SetStatus (eReturnStatusFailed); + return false; + } + + std::vector valid_wp_ids; + if (!CommandObjectMultiwordWatchpoint::VerifyWatchpointIDs(target, command, valid_wp_ids)) + { + result.AppendError("Invalid watchpoints specification."); + result.SetStatus(eReturnStatusFailed); + return false; + } + + result.SetStatus(eReturnStatusSuccessFinishNoResult); + const size_t count = valid_wp_ids.size(); + for (size_t i = 0; i < count; ++i) + { + uint32_t cur_wp_id = valid_wp_ids.at (i); + if (cur_wp_id != LLDB_INVALID_WATCH_ID) + { + Watchpoint *wp = target->GetWatchpointList().FindByID (cur_wp_id).get(); + if (wp) + wp->ClearCallback(); + } + else + { + result.AppendErrorWithFormat("Invalid watchpoint ID: %u.\n", + cur_wp_id); + result.SetStatus (eReturnStatusFailed); + return false; + } + } + return result.Succeeded(); + } +}; + +//------------------------------------------------------------------------- +// CommandObjectWatchpointCommandList +//------------------------------------------------------------------------- + +class CommandObjectWatchpointCommandList : public CommandObjectParsed +{ +public: + CommandObjectWatchpointCommandList (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "list", + "List the script or set of commands to be executed when the watchpoint is hit.", + NULL) + { + CommandArgumentEntry arg; + CommandArgumentData wp_id_arg; + + // Define the first (and only) variant of this arg. + wp_id_arg.arg_type = eArgTypeWatchpointID; + wp_id_arg.arg_repetition = eArgRepeatPlain; + + // There is only one variant this argument could be; put it into the argument entry. + arg.push_back (wp_id_arg); + + // Push the data for the first argument into the m_arguments vector. + m_arguments.push_back (arg); + } + + virtual + ~CommandObjectWatchpointCommandList () {} + +protected: + virtual bool + DoExecute (Args& command, + CommandReturnObject &result) + { + Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get(); + + if (target == NULL) + { + result.AppendError ("There is not a current executable; there are no watchpoints for which to list commands"); + result.SetStatus (eReturnStatusFailed); + return false; + } + + const WatchpointList &watchpoints = target->GetWatchpointList(); + size_t num_watchpoints = watchpoints.GetSize(); + + if (num_watchpoints == 0) + { + result.AppendError ("No watchpoints exist for which to list commands"); + result.SetStatus (eReturnStatusFailed); + return false; + } + + if (command.GetArgumentCount() == 0) + { + result.AppendError ("No watchpoint specified for which to list the commands"); + result.SetStatus (eReturnStatusFailed); + return false; + } + + std::vector valid_wp_ids; + if (!CommandObjectMultiwordWatchpoint::VerifyWatchpointIDs(target, command, valid_wp_ids)) + { + result.AppendError("Invalid watchpoints specification."); + result.SetStatus(eReturnStatusFailed); + return false; + } + + result.SetStatus(eReturnStatusSuccessFinishNoResult); + const size_t count = valid_wp_ids.size(); + for (size_t i = 0; i < count; ++i) + { + uint32_t cur_wp_id = valid_wp_ids.at (i); + if (cur_wp_id != LLDB_INVALID_WATCH_ID) + { + Watchpoint *wp = target->GetWatchpointList().FindByID (cur_wp_id).get(); + + if (wp) + { + const WatchpointOptions *wp_options = wp->GetOptions(); + if (wp_options) + { + // Get the callback baton associated with the current watchpoint. + const Baton *baton = wp_options->GetBaton(); + if (baton) + { + result.GetOutputStream().Printf ("Watchpoint %u:\n", cur_wp_id); + result.GetOutputStream().IndentMore (); + baton->GetDescription(&result.GetOutputStream(), eDescriptionLevelFull); + result.GetOutputStream().IndentLess (); + } + else + { + result.AppendMessageWithFormat ("Watchpoint %u does not have an associated command.\n", + cur_wp_id); + } + } + result.SetStatus (eReturnStatusSuccessFinishResult); + } + else + { + result.AppendErrorWithFormat("Invalid watchpoint ID: %u.\n", cur_wp_id); + result.SetStatus (eReturnStatusFailed); + } + } + } + + return result.Succeeded(); + } +}; + +//------------------------------------------------------------------------- +// CommandObjectWatchpointCommand +//------------------------------------------------------------------------- + +CommandObjectWatchpointCommand::CommandObjectWatchpointCommand (CommandInterpreter &interpreter) : + CommandObjectMultiword (interpreter, + "command", + "A set of commands for adding, removing and examining bits of code to be executed when the watchpoint is hit (watchpoint 'commmands').", + "command [] ") +{ + CommandObjectSP add_command_object (new CommandObjectWatchpointCommandAdd (interpreter)); + CommandObjectSP delete_command_object (new CommandObjectWatchpointCommandDelete (interpreter)); + CommandObjectSP list_command_object (new CommandObjectWatchpointCommandList (interpreter)); + + add_command_object->SetCommandName ("watchpoint command add"); + delete_command_object->SetCommandName ("watchpoint command delete"); + list_command_object->SetCommandName ("watchpoint command list"); + + LoadSubCommand ("add", add_command_object); + LoadSubCommand ("delete", delete_command_object); + LoadSubCommand ("list", list_command_object); +} + +CommandObjectWatchpointCommand::~CommandObjectWatchpointCommand () +{ +} + + diff --git a/contrib/llvm/tools/lldb/source/Commands/CommandObjectWatchpointCommand.h b/contrib/llvm/tools/lldb/source/Commands/CommandObjectWatchpointCommand.h new file mode 100644 index 00000000000..c2faf7187db --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Commands/CommandObjectWatchpointCommand.h @@ -0,0 +1,46 @@ +//===-- CommandObjectWatchpointCommand.h ------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_CommandObjectWatchpointCommand_h_ +#define liblldb_CommandObjectWatchpointCommand_h_ + +// C Includes +// C++ Includes + + +// Other libraries and framework includes +// Project includes + +#include "lldb/lldb-types.h" +#include "lldb/Interpreter/Options.h" +#include "lldb/Core/InputReader.h" +#include "lldb/Interpreter/CommandObject.h" +#include "lldb/Interpreter/CommandReturnObject.h" +#include "lldb/Interpreter/CommandObjectMultiword.h" + + +namespace lldb_private { + +//------------------------------------------------------------------------- +// CommandObjectMultiwordWatchpoint +//------------------------------------------------------------------------- + +class CommandObjectWatchpointCommand : public CommandObjectMultiword +{ +public: + CommandObjectWatchpointCommand (CommandInterpreter &interpreter); + + virtual + ~CommandObjectWatchpointCommand (); + +}; + +} // namespace lldb_private + +#endif // liblldb_CommandObjectWatchpointCommand_h_ diff --git a/contrib/llvm/tools/lldb/source/Core/Address.cpp b/contrib/llvm/tools/lldb/source/Core/Address.cpp new file mode 100644 index 00000000000..8d599d80ad4 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Core/Address.cpp @@ -0,0 +1,1045 @@ +//===-- Address.cpp ---------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Core/Address.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/Section.h" +#include "lldb/Symbol/Block.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Symbol/Type.h" +#include "lldb/Symbol/Variable.h" +#include "lldb/Symbol/VariableList.h" +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Target.h" +#include "lldb/Symbol/SymbolVendor.h" + +#include "llvm/ADT/Triple.h" + +using namespace lldb; +using namespace lldb_private; + +static size_t +ReadBytes (ExecutionContextScope *exe_scope, const Address &address, void *dst, size_t dst_len) +{ + if (exe_scope == NULL) + return 0; + + TargetSP target_sp (exe_scope->CalculateTarget()); + if (target_sp) + { + Error error; + bool prefer_file_cache = false; + return target_sp->ReadMemory (address, prefer_file_cache, dst, dst_len, error); + } + return 0; +} + +static bool +GetByteOrderAndAddressSize (ExecutionContextScope *exe_scope, const Address &address, ByteOrder& byte_order, uint32_t& addr_size) +{ + byte_order = eByteOrderInvalid; + addr_size = 0; + if (exe_scope == NULL) + return false; + + TargetSP target_sp (exe_scope->CalculateTarget()); + if (target_sp) + { + byte_order = target_sp->GetArchitecture().GetByteOrder(); + addr_size = target_sp->GetArchitecture().GetAddressByteSize(); + } + + if (byte_order == eByteOrderInvalid || addr_size == 0) + { + ModuleSP module_sp (address.GetModule()); + if (module_sp) + { + byte_order = module_sp->GetArchitecture().GetByteOrder(); + addr_size = module_sp->GetArchitecture().GetAddressByteSize(); + } + } + return byte_order != eByteOrderInvalid && addr_size != 0; +} + +static uint64_t +ReadUIntMax64 (ExecutionContextScope *exe_scope, const Address &address, uint32_t byte_size, bool &success) +{ + uint64_t uval64 = 0; + if (exe_scope == NULL || byte_size > sizeof(uint64_t)) + { + success = false; + return 0; + } + uint64_t buf = 0; + + success = ReadBytes (exe_scope, address, &buf, byte_size) == byte_size; + if (success) + { + ByteOrder byte_order = eByteOrderInvalid; + uint32_t addr_size = 0; + if (GetByteOrderAndAddressSize (exe_scope, address, byte_order, addr_size)) + { + DataExtractor data (&buf, sizeof(buf), byte_order, addr_size); + lldb::offset_t offset = 0; + uval64 = data.GetU64(&offset); + } + else + success = false; + } + return uval64; +} + +static bool +ReadAddress (ExecutionContextScope *exe_scope, const Address &address, uint32_t pointer_size, Address &deref_so_addr) +{ + if (exe_scope == NULL) + return false; + + + bool success = false; + addr_t deref_addr = ReadUIntMax64 (exe_scope, address, pointer_size, success); + if (success) + { + ExecutionContext exe_ctx; + exe_scope->CalculateExecutionContext(exe_ctx); + // If we have any sections that are loaded, try and resolve using the + // section load list + Target *target = exe_ctx.GetTargetPtr(); + if (target && !target->GetSectionLoadList().IsEmpty()) + { + if (target->GetSectionLoadList().ResolveLoadAddress (deref_addr, deref_so_addr)) + return true; + } + else + { + // If we were not running, yet able to read an integer, we must + // have a module + ModuleSP module_sp (address.GetModule()); + + assert (module_sp); + if (module_sp->ResolveFileAddress(deref_addr, deref_so_addr)) + return true; + } + + // We couldn't make "deref_addr" into a section offset value, but we were + // able to read the address, so we return a section offset address with + // no section and "deref_addr" as the offset (address). + deref_so_addr.SetRawAddress(deref_addr); + return true; + } + return false; +} + +static bool +DumpUInt (ExecutionContextScope *exe_scope, const Address &address, uint32_t byte_size, Stream* strm) +{ + if (exe_scope == NULL || byte_size == 0) + return 0; + std::vector buf(byte_size, 0); + + if (ReadBytes (exe_scope, address, &buf[0], buf.size()) == buf.size()) + { + ByteOrder byte_order = eByteOrderInvalid; + uint32_t addr_size = 0; + if (GetByteOrderAndAddressSize (exe_scope, address, byte_order, addr_size)) + { + DataExtractor data (&buf.front(), buf.size(), byte_order, addr_size); + + data.Dump (strm, + 0, // Start offset in "data" + eFormatHex, // Print as characters + buf.size(), // Size of item + 1, // Items count + UINT32_MAX, // num per line + LLDB_INVALID_ADDRESS,// base address + 0, // bitfield bit size + 0); // bitfield bit offset + + return true; + } + } + return false; +} + + +static size_t +ReadCStringFromMemory (ExecutionContextScope *exe_scope, const Address &address, Stream *strm) +{ + if (exe_scope == NULL) + return 0; + const size_t k_buf_len = 256; + char buf[k_buf_len+1]; + buf[k_buf_len] = '\0'; // NULL terminate + + // Byte order and address size don't matter for C string dumping.. + DataExtractor data (buf, sizeof(buf), lldb::endian::InlHostByteOrder(), 4); + size_t total_len = 0; + size_t bytes_read; + Address curr_address(address); + strm->PutChar ('"'); + while ((bytes_read = ReadBytes (exe_scope, curr_address, buf, k_buf_len)) > 0) + { + size_t len = strlen(buf); + if (len == 0) + break; + if (len > bytes_read) + len = bytes_read; + + data.Dump (strm, + 0, // Start offset in "data" + eFormatChar, // Print as characters + 1, // Size of item (1 byte for a char!) + len, // How many bytes to print? + UINT32_MAX, // num per line + LLDB_INVALID_ADDRESS,// base address + 0, // bitfield bit size + + 0); // bitfield bit offset + + total_len += bytes_read; + + if (len < k_buf_len) + break; + curr_address.SetOffset (curr_address.GetOffset() + bytes_read); + } + strm->PutChar ('"'); + return total_len; +} + +Address::Address (lldb::addr_t abs_addr) : + m_section_wp (), + m_offset (abs_addr) +{ +} + +Address::Address (addr_t address, const SectionList *section_list) : + m_section_wp (), + m_offset (LLDB_INVALID_ADDRESS) +{ + ResolveAddressUsingFileSections(address, section_list); +} + +const Address& +Address::operator= (const Address& rhs) +{ + if (this != &rhs) + { + m_section_wp = rhs.m_section_wp; + m_offset = rhs.m_offset.load(); + } + return *this; +} + +bool +Address::ResolveAddressUsingFileSections (addr_t file_addr, const SectionList *section_list) +{ + if (section_list) + { + SectionSP section_sp (section_list->FindSectionContainingFileAddress(file_addr)); + m_section_wp = section_sp; + if (section_sp) + { + assert( section_sp->ContainsFileAddress(file_addr) ); + m_offset = file_addr - section_sp->GetFileAddress(); + return true; // Successfully transformed addr into a section offset address + } + } + m_offset = file_addr; + return false; // Failed to resolve this address to a section offset value +} + +ModuleSP +Address::GetModule () const +{ + lldb::ModuleSP module_sp; + SectionSP section_sp (GetSection()); + if (section_sp) + module_sp = section_sp->GetModule(); + return module_sp; +} + +addr_t +Address::GetFileAddress () const +{ + SectionSP section_sp (GetSection()); + if (section_sp) + { + addr_t sect_file_addr = section_sp->GetFileAddress(); + if (sect_file_addr == LLDB_INVALID_ADDRESS) + { + // Section isn't resolved, we can't return a valid file address + return LLDB_INVALID_ADDRESS; + } + // We have a valid file range, so we can return the file based + // address by adding the file base address to our offset + return sect_file_addr + m_offset; + } + // No section, we just return the offset since it is the value in this case + return m_offset; +} + +addr_t +Address::GetLoadAddress (Target *target) const +{ + SectionSP section_sp (GetSection()); + if (!section_sp) + { + // No section, we just return the offset since it is the value in this case + return m_offset; + } + + if (target) + { + addr_t sect_load_addr = section_sp->GetLoadBaseAddress (target); + + if (sect_load_addr != LLDB_INVALID_ADDRESS) + { + // We have a valid file range, so we can return the file based + // address by adding the file base address to our offset + return sect_load_addr + m_offset; + } + } + // The section isn't resolved or no process was supplied so we can't + // return a valid file address. + return LLDB_INVALID_ADDRESS; +} + +addr_t +Address::GetCallableLoadAddress (Target *target, bool is_indirect) const +{ + if (is_indirect && target) { + ProcessSP processSP = target->GetProcessSP(); + Error error; + if (processSP.get()) + return processSP->ResolveIndirectFunction(this, error); + } + + addr_t code_addr = GetLoadAddress (target); + + if (target) + return target->GetCallableLoadAddress (code_addr, GetAddressClass()); + return code_addr; +} + +bool +Address::SetCallableLoadAddress (lldb::addr_t load_addr, Target *target) +{ + if (SetLoadAddress (load_addr, target)) + { + if (target) + m_offset = target->GetCallableLoadAddress(m_offset, GetAddressClass()); + return true; + } + return false; +} + +addr_t +Address::GetOpcodeLoadAddress (Target *target) const +{ + addr_t code_addr = GetLoadAddress (target); + if (code_addr != LLDB_INVALID_ADDRESS) + code_addr = target->GetOpcodeLoadAddress (code_addr, GetAddressClass()); + return code_addr; +} + +bool +Address::SetOpcodeLoadAddress (lldb::addr_t load_addr, Target *target) +{ + if (SetLoadAddress (load_addr, target)) + { + if (target) + m_offset = target->GetOpcodeLoadAddress (m_offset, GetAddressClass()); + return true; + } + return false; +} + +bool +Address::Dump (Stream *s, ExecutionContextScope *exe_scope, DumpStyle style, DumpStyle fallback_style, uint32_t addr_size) const +{ + // If the section was NULL, only load address is going to work unless we are + // trying to deref a pointer + SectionSP section_sp (GetSection()); + if (!section_sp && style != DumpStyleResolvedPointerDescription) + style = DumpStyleLoadAddress; + + ExecutionContext exe_ctx (exe_scope); + Target *target = exe_ctx.GetTargetPtr(); + // If addr_byte_size is UINT32_MAX, then determine the correct address + // byte size for the process or default to the size of addr_t + if (addr_size == UINT32_MAX) + { + if (target) + addr_size = target->GetArchitecture().GetAddressByteSize (); + else + addr_size = sizeof(addr_t); + } + + Address so_addr; + switch (style) + { + case DumpStyleInvalid: + return false; + + case DumpStyleSectionNameOffset: + if (section_sp) + { + section_sp->DumpName(s); + s->Printf (" + %" PRIu64, m_offset.load()); + } + else + { + s->Address(m_offset, addr_size); + } + break; + + case DumpStyleSectionPointerOffset: + s->Printf("(Section *)%p + ", section_sp.get()); + s->Address(m_offset, addr_size); + break; + + case DumpStyleModuleWithFileAddress: + if (section_sp) + s->Printf("%s[", section_sp->GetModule()->GetFileSpec().GetFilename().AsCString()); + // Fall through + case DumpStyleFileAddress: + { + addr_t file_addr = GetFileAddress(); + if (file_addr == LLDB_INVALID_ADDRESS) + { + if (fallback_style != DumpStyleInvalid) + return Dump (s, exe_scope, fallback_style, DumpStyleInvalid, addr_size); + return false; + } + s->Address (file_addr, addr_size); + if (style == DumpStyleModuleWithFileAddress && section_sp) + s->PutChar(']'); + } + break; + + case DumpStyleLoadAddress: + { + addr_t load_addr = GetLoadAddress (target); + if (load_addr == LLDB_INVALID_ADDRESS) + { + if (fallback_style != DumpStyleInvalid) + return Dump (s, exe_scope, fallback_style, DumpStyleInvalid, addr_size); + return false; + } + s->Address (load_addr, addr_size); + } + break; + + case DumpStyleResolvedDescription: + case DumpStyleResolvedDescriptionNoModule: + if (IsSectionOffset()) + { + uint32_t pointer_size = 4; + ModuleSP module_sp (GetModule()); + if (target) + pointer_size = target->GetArchitecture().GetAddressByteSize(); + else if (module_sp) + pointer_size = module_sp->GetArchitecture().GetAddressByteSize(); + + bool showed_info = false; + if (section_sp) + { + SectionType sect_type = section_sp->GetType(); + switch (sect_type) + { + case eSectionTypeData: + if (module_sp) + { + SymbolVendor *sym_vendor = module_sp->GetSymbolVendor(); + if (sym_vendor) + { + Symtab *symtab = sym_vendor->GetSymtab(); + if (symtab) + { + const addr_t file_Addr = GetFileAddress(); + Symbol *symbol = symtab->FindSymbolContainingFileAddress (file_Addr); + if (symbol) + { + const char *symbol_name = symbol->GetName().AsCString(); + if (symbol_name) + { + s->PutCString(symbol_name); + addr_t delta = file_Addr - symbol->GetAddress().GetFileAddress(); + if (delta) + s->Printf(" + %" PRIu64, delta); + showed_info = true; + } + } + } + } + } + break; + + case eSectionTypeDataCString: + // Read the C string from memory and display it + showed_info = true; + ReadCStringFromMemory (exe_scope, *this, s); + break; + + case eSectionTypeDataCStringPointers: + { + if (ReadAddress (exe_scope, *this, pointer_size, so_addr)) + { +#if VERBOSE_OUTPUT + s->PutCString("(char *)"); + so_addr.Dump(s, exe_scope, DumpStyleLoadAddress, DumpStyleFileAddress); + s->PutCString(": "); +#endif + showed_info = true; + ReadCStringFromMemory (exe_scope, so_addr, s); + } + } + break; + + case eSectionTypeDataObjCMessageRefs: + { + if (ReadAddress (exe_scope, *this, pointer_size, so_addr)) + { + if (target && so_addr.IsSectionOffset()) + { + SymbolContext func_sc; + target->GetImages().ResolveSymbolContextForAddress (so_addr, + eSymbolContextEverything, + func_sc); + if (func_sc.function || func_sc.symbol) + { + showed_info = true; +#if VERBOSE_OUTPUT + s->PutCString ("(objc_msgref *) -> { (func*)"); + so_addr.Dump(s, exe_scope, DumpStyleLoadAddress, DumpStyleFileAddress); +#else + s->PutCString ("{ "); +#endif + Address cstr_addr(*this); + cstr_addr.SetOffset(cstr_addr.GetOffset() + pointer_size); + func_sc.DumpStopContext(s, exe_scope, so_addr, true, true, false); + if (ReadAddress (exe_scope, cstr_addr, pointer_size, so_addr)) + { +#if VERBOSE_OUTPUT + s->PutCString("), (char *)"); + so_addr.Dump(s, exe_scope, DumpStyleLoadAddress, DumpStyleFileAddress); + s->PutCString(" ("); +#else + s->PutCString(", "); +#endif + ReadCStringFromMemory (exe_scope, so_addr, s); + } +#if VERBOSE_OUTPUT + s->PutCString(") }"); +#else + s->PutCString(" }"); +#endif + } + } + } + } + break; + + case eSectionTypeDataObjCCFStrings: + { + Address cfstring_data_addr(*this); + cfstring_data_addr.SetOffset(cfstring_data_addr.GetOffset() + (2 * pointer_size)); + if (ReadAddress (exe_scope, cfstring_data_addr, pointer_size, so_addr)) + { +#if VERBOSE_OUTPUT + s->PutCString("(CFString *) "); + cfstring_data_addr.Dump(s, exe_scope, DumpStyleLoadAddress, DumpStyleFileAddress); + s->PutCString(" -> @"); +#else + s->PutChar('@'); +#endif + if (so_addr.Dump(s, exe_scope, DumpStyleResolvedDescription)) + showed_info = true; + } + } + break; + + case eSectionTypeData4: + // Read the 4 byte data and display it + showed_info = true; + s->PutCString("(uint32_t) "); + DumpUInt (exe_scope, *this, 4, s); + break; + + case eSectionTypeData8: + // Read the 8 byte data and display it + showed_info = true; + s->PutCString("(uint64_t) "); + DumpUInt (exe_scope, *this, 8, s); + break; + + case eSectionTypeData16: + // Read the 16 byte data and display it + showed_info = true; + s->PutCString("(uint128_t) "); + DumpUInt (exe_scope, *this, 16, s); + break; + + case eSectionTypeDataPointers: + // Read the pointer data and display it + { + if (ReadAddress (exe_scope, *this, pointer_size, so_addr)) + { + s->PutCString ("(void *)"); + so_addr.Dump(s, exe_scope, DumpStyleLoadAddress, DumpStyleFileAddress); + + showed_info = true; + if (so_addr.IsSectionOffset()) + { + SymbolContext pointer_sc; + if (target) + { + target->GetImages().ResolveSymbolContextForAddress (so_addr, + eSymbolContextEverything, + pointer_sc); + if (pointer_sc.function || pointer_sc.symbol) + { + s->PutCString(": "); + pointer_sc.DumpStopContext(s, exe_scope, so_addr, true, false, false); + } + } + } + } + } + break; + + default: + break; + } + } + + if (!showed_info) + { + if (module_sp) + { + SymbolContext sc; + module_sp->ResolveSymbolContextForAddress(*this, eSymbolContextEverything, sc); + if (sc.function || sc.symbol) + { + bool show_stop_context = true; + const bool show_module = (style == DumpStyleResolvedDescription); + const bool show_fullpaths = false; + const bool show_inlined_frames = true; + if (sc.function == NULL && sc.symbol != NULL) + { + // If we have just a symbol make sure it is in the right section + if (sc.symbol->ValueIsAddress()) + { + if (sc.symbol->GetAddress().GetSection() != GetSection()) + { + // don't show the module if the symbol is a trampoline symbol + show_stop_context = false; + } + } + } + if (show_stop_context) + { + // We have a function or a symbol from the same + // sections as this address. + sc.DumpStopContext (s, + exe_scope, + *this, + show_fullpaths, + show_module, + show_inlined_frames); + } + else + { + // We found a symbol but it was in a different + // section so it isn't the symbol we should be + // showing, just show the section name + offset + Dump (s, exe_scope, DumpStyleSectionNameOffset); + } + } + } + } + } + else + { + if (fallback_style != DumpStyleInvalid) + return Dump (s, exe_scope, fallback_style, DumpStyleInvalid, addr_size); + return false; + } + break; + + case DumpStyleDetailedSymbolContext: + if (IsSectionOffset()) + { + ModuleSP module_sp (GetModule()); + if (module_sp) + { + SymbolContext sc; + module_sp->ResolveSymbolContextForAddress(*this, eSymbolContextEverything, sc); + if (sc.symbol) + { + // If we have just a symbol make sure it is in the same section + // as our address. If it isn't, then we might have just found + // the last symbol that came before the address that we are + // looking up that has nothing to do with our address lookup. + if (sc.symbol->ValueIsAddress() && sc.symbol->GetAddress().GetSection() != GetSection()) + sc.symbol = NULL; + } + sc.GetDescription(s, eDescriptionLevelBrief, target); + + if (sc.block) + { + bool can_create = true; + bool get_parent_variables = true; + bool stop_if_block_is_inlined_function = false; + VariableList variable_list; + sc.block->AppendVariables (can_create, + get_parent_variables, + stop_if_block_is_inlined_function, + &variable_list); + + const size_t num_variables = variable_list.GetSize(); + for (size_t var_idx = 0; var_idx < num_variables; ++var_idx) + { + Variable *var = variable_list.GetVariableAtIndex (var_idx).get(); + if (var && var->LocationIsValidForAddress (*this)) + { + s->Indent(); + s->Printf (" Variable: id = {0x%8.8" PRIx64 "}, name = \"%s\", type= \"%s\", location =", + var->GetID(), + var->GetName().GetCString(), + var->GetType()->GetName().GetCString()); + var->DumpLocationForAddress(s, *this); + s->PutCString(", decl = "); + var->GetDeclaration().DumpStopContext(s, false); + s->EOL(); + } + } + } + } + } + else + { + if (fallback_style != DumpStyleInvalid) + return Dump (s, exe_scope, fallback_style, DumpStyleInvalid, addr_size); + return false; + } + break; + case DumpStyleResolvedPointerDescription: + { + Process *process = exe_ctx.GetProcessPtr(); + if (process) + { + addr_t load_addr = GetLoadAddress (target); + if (load_addr != LLDB_INVALID_ADDRESS) + { + Error memory_error; + addr_t dereferenced_load_addr = process->ReadPointerFromMemory(load_addr, memory_error); + if (dereferenced_load_addr != LLDB_INVALID_ADDRESS) + { + Address dereferenced_addr; + if (dereferenced_addr.SetLoadAddress(dereferenced_load_addr, target)) + { + StreamString strm; + if (dereferenced_addr.Dump (&strm, exe_scope, DumpStyleResolvedDescription, DumpStyleInvalid, addr_size)) + { + s->Address (dereferenced_load_addr, addr_size, " -> ", " "); + s->Write(strm.GetData(), strm.GetSize()); + return true; + } + } + } + } + } + if (fallback_style != DumpStyleInvalid) + return Dump (s, exe_scope, fallback_style, DumpStyleInvalid, addr_size); + return false; + } + break; + } + + return true; +} + +uint32_t +Address::CalculateSymbolContext (SymbolContext *sc, uint32_t resolve_scope) const +{ + sc->Clear(false); + // Absolute addresses don't have enough information to reconstruct even their target. + + SectionSP section_sp (GetSection()); + if (section_sp) + { + ModuleSP module_sp (section_sp->GetModule()); + if (module_sp) + { + sc->module_sp = module_sp; + if (sc->module_sp) + return sc->module_sp->ResolveSymbolContextForAddress (*this, resolve_scope, *sc); + } + } + return 0; +} + +ModuleSP +Address::CalculateSymbolContextModule () const +{ + SectionSP section_sp (GetSection()); + if (section_sp) + return section_sp->GetModule(); + return ModuleSP(); +} + +CompileUnit * +Address::CalculateSymbolContextCompileUnit () const +{ + SectionSP section_sp (GetSection()); + if (section_sp) + { + SymbolContext sc; + sc.module_sp = section_sp->GetModule(); + if (sc.module_sp) + { + sc.module_sp->ResolveSymbolContextForAddress (*this, eSymbolContextCompUnit, sc); + return sc.comp_unit; + } + } + return NULL; +} + +Function * +Address::CalculateSymbolContextFunction () const +{ + SectionSP section_sp (GetSection()); + if (section_sp) + { + SymbolContext sc; + sc.module_sp = section_sp->GetModule(); + if (sc.module_sp) + { + sc.module_sp->ResolveSymbolContextForAddress (*this, eSymbolContextFunction, sc); + return sc.function; + } + } + return NULL; +} + +Block * +Address::CalculateSymbolContextBlock () const +{ + SectionSP section_sp (GetSection()); + if (section_sp) + { + SymbolContext sc; + sc.module_sp = section_sp->GetModule(); + if (sc.module_sp) + { + sc.module_sp->ResolveSymbolContextForAddress (*this, eSymbolContextBlock, sc); + return sc.block; + } + } + return NULL; +} + +Symbol * +Address::CalculateSymbolContextSymbol () const +{ + SectionSP section_sp (GetSection()); + if (section_sp) + { + SymbolContext sc; + sc.module_sp = section_sp->GetModule(); + if (sc.module_sp) + { + sc.module_sp->ResolveSymbolContextForAddress (*this, eSymbolContextSymbol, sc); + return sc.symbol; + } + } + return NULL; +} + +bool +Address::CalculateSymbolContextLineEntry (LineEntry &line_entry) const +{ + SectionSP section_sp (GetSection()); + if (section_sp) + { + SymbolContext sc; + sc.module_sp = section_sp->GetModule(); + if (sc.module_sp) + { + sc.module_sp->ResolveSymbolContextForAddress (*this, eSymbolContextLineEntry, sc); + if (sc.line_entry.IsValid()) + { + line_entry = sc.line_entry; + return true; + } + } + } + line_entry.Clear(); + return false; +} + +int +Address::CompareFileAddress (const Address& a, const Address& b) +{ + addr_t a_file_addr = a.GetFileAddress(); + addr_t b_file_addr = b.GetFileAddress(); + if (a_file_addr < b_file_addr) + return -1; + if (a_file_addr > b_file_addr) + return +1; + return 0; +} + + +int +Address::CompareLoadAddress (const Address& a, const Address& b, Target *target) +{ + assert (target != NULL); + addr_t a_load_addr = a.GetLoadAddress (target); + addr_t b_load_addr = b.GetLoadAddress (target); + if (a_load_addr < b_load_addr) + return -1; + if (a_load_addr > b_load_addr) + return +1; + return 0; +} + +int +Address::CompareModulePointerAndOffset (const Address& a, const Address& b) +{ + ModuleSP a_module_sp (a.GetModule()); + ModuleSP b_module_sp (b.GetModule()); + Module *a_module = a_module_sp.get(); + Module *b_module = b_module_sp.get(); + if (a_module < b_module) + return -1; + if (a_module > b_module) + return +1; + // Modules are the same, just compare the file address since they should + // be unique + addr_t a_file_addr = a.GetFileAddress(); + addr_t b_file_addr = b.GetFileAddress(); + if (a_file_addr < b_file_addr) + return -1; + if (a_file_addr > b_file_addr) + return +1; + return 0; +} + + +size_t +Address::MemorySize () const +{ + // Noting special for the memory size of a single Address object, + // it is just the size of itself. + return sizeof(Address); +} + + +//---------------------------------------------------------------------- +// NOTE: Be careful using this operator. It can correctly compare two +// addresses from the same Module correctly. It can't compare two +// addresses from different modules in any meaningful way, but it will +// compare the module pointers. +// +// To sum things up: +// - works great for addresses within the same module +// - it works for addresses across multiple modules, but don't expect the +// address results to make much sense +// +// This basically lets Address objects be used in ordered collection +// classes. +//---------------------------------------------------------------------- + +bool +lldb_private::operator< (const Address& lhs, const Address& rhs) +{ + ModuleSP lhs_module_sp (lhs.GetModule()); + ModuleSP rhs_module_sp (rhs.GetModule()); + Module *lhs_module = lhs_module_sp.get(); + Module *rhs_module = rhs_module_sp.get(); + if (lhs_module == rhs_module) + { + // Addresses are in the same module, just compare the file addresses + return lhs.GetFileAddress() < rhs.GetFileAddress(); + } + else + { + // The addresses are from different modules, just use the module + // pointer value to get consistent ordering + return lhs_module < rhs_module; + } +} + +bool +lldb_private::operator> (const Address& lhs, const Address& rhs) +{ + ModuleSP lhs_module_sp (lhs.GetModule()); + ModuleSP rhs_module_sp (rhs.GetModule()); + Module *lhs_module = lhs_module_sp.get(); + Module *rhs_module = rhs_module_sp.get(); + if (lhs_module == rhs_module) + { + // Addresses are in the same module, just compare the file addresses + return lhs.GetFileAddress() > rhs.GetFileAddress(); + } + else + { + // The addresses are from different modules, just use the module + // pointer value to get consistent ordering + return lhs_module > rhs_module; + } +} + + +// The operator == checks for exact equality only (same section, same offset) +bool +lldb_private::operator== (const Address& a, const Address& rhs) +{ + return a.GetOffset() == rhs.GetOffset() && + a.GetSection() == rhs.GetSection(); +} +// The operator != checks for exact inequality only (differing section, or +// different offset) +bool +lldb_private::operator!= (const Address& a, const Address& rhs) +{ + return a.GetOffset() != rhs.GetOffset() || + a.GetSection() != rhs.GetSection(); +} + +AddressClass +Address::GetAddressClass () const +{ + ModuleSP module_sp (GetModule()); + if (module_sp) + { + ObjectFile *obj_file = module_sp->GetObjectFile(); + if (obj_file) + { + // Give the symbol vendor a chance to add to the unified section list. + module_sp->GetSymbolVendor(); + return obj_file->GetAddressClass (GetFileAddress()); + } + } + return eAddressClassUnknown; +} + +bool +Address::SetLoadAddress (lldb::addr_t load_addr, Target *target) +{ + if (target && target->GetSectionLoadList().ResolveLoadAddress(load_addr, *this)) + return true; + m_section_wp.reset(); + m_offset = load_addr; + return false; +} + diff --git a/contrib/llvm/tools/lldb/source/Core/AddressRange.cpp b/contrib/llvm/tools/lldb/source/Core/AddressRange.cpp new file mode 100644 index 00000000000..835a01d82aa --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Core/AddressRange.cpp @@ -0,0 +1,208 @@ +//===-- AddressRange.cpp ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Core/AddressRange.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/Stream.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Target.h" + +using namespace lldb; +using namespace lldb_private; + +AddressRange::AddressRange () : + m_base_addr(), + m_byte_size(0) +{ +} + +AddressRange::AddressRange (addr_t file_addr, addr_t byte_size, const SectionList *section_list) : + m_base_addr(file_addr, section_list), + m_byte_size(byte_size) +{ +} + +AddressRange::AddressRange (const lldb::SectionSP §ion, addr_t offset, addr_t byte_size) : + m_base_addr(section, offset), + m_byte_size(byte_size) +{ +} + +AddressRange::AddressRange (const Address& so_addr, addr_t byte_size) : + m_base_addr(so_addr), + m_byte_size(byte_size) +{ +} + +AddressRange::~AddressRange () +{ +} + +//bool +//AddressRange::Contains (const Address &addr) const +//{ +// const addr_t byte_size = GetByteSize(); +// if (byte_size) +// return addr.GetSection() == m_base_addr.GetSection() && (addr.GetOffset() - m_base_addr.GetOffset()) < byte_size; +//} +// +//bool +//AddressRange::Contains (const Address *addr) const +//{ +// if (addr) +// return Contains (*addr); +// return false; +//} + +bool +AddressRange::ContainsFileAddress (const Address &addr) const +{ + if (addr.GetSection() == m_base_addr.GetSection()) + return (addr.GetOffset() - m_base_addr.GetOffset()) < GetByteSize(); + addr_t file_base_addr = GetBaseAddress().GetFileAddress(); + if (file_base_addr == LLDB_INVALID_ADDRESS) + return false; + + addr_t file_addr = addr.GetFileAddress(); + if (file_addr == LLDB_INVALID_ADDRESS) + return false; + + if (file_base_addr <= file_addr) + return (file_addr - file_base_addr) < GetByteSize(); + + return false; +} + +bool +AddressRange::ContainsFileAddress (addr_t file_addr) const +{ + if (file_addr == LLDB_INVALID_ADDRESS) + return false; + + addr_t file_base_addr = GetBaseAddress().GetFileAddress(); + if (file_base_addr == LLDB_INVALID_ADDRESS) + return false; + + if (file_base_addr <= file_addr) + return (file_addr - file_base_addr) < GetByteSize(); + + return false; +} + + +bool +AddressRange::ContainsLoadAddress (const Address &addr, Target *target) const +{ + if (addr.GetSection() == m_base_addr.GetSection()) + return (addr.GetOffset() - m_base_addr.GetOffset()) < GetByteSize(); + addr_t load_base_addr = GetBaseAddress().GetLoadAddress(target); + if (load_base_addr == LLDB_INVALID_ADDRESS) + return false; + + addr_t load_addr = addr.GetLoadAddress(target); + if (load_addr == LLDB_INVALID_ADDRESS) + return false; + + if (load_base_addr <= load_addr) + return (load_addr - load_base_addr) < GetByteSize(); + + return false; +} + +bool +AddressRange::ContainsLoadAddress (addr_t load_addr, Target *target) const +{ + if (load_addr == LLDB_INVALID_ADDRESS) + return false; + + addr_t load_base_addr = GetBaseAddress().GetLoadAddress(target); + if (load_base_addr == LLDB_INVALID_ADDRESS) + return false; + + if (load_base_addr <= load_addr) + return (load_addr - load_base_addr) < GetByteSize(); + + return false; +} + +void +AddressRange::Clear() +{ + m_base_addr.Clear(); + m_byte_size = 0; +} + +bool +AddressRange::Dump(Stream *s, Target *target, Address::DumpStyle style, Address::DumpStyle fallback_style) const +{ + addr_t vmaddr = LLDB_INVALID_ADDRESS; + int addr_size = sizeof (addr_t); + if (target) + addr_size = target->GetArchitecture().GetAddressByteSize (); + + bool show_module = false; + switch (style) + { + default: + break; + case Address::DumpStyleSectionNameOffset: + case Address::DumpStyleSectionPointerOffset: + s->PutChar ('['); + m_base_addr.Dump(s, target, style, fallback_style); + s->PutChar ('-'); + s->Address (m_base_addr.GetOffset() + GetByteSize(), addr_size); + s->PutChar (')'); + return true; + break; + + case Address::DumpStyleModuleWithFileAddress: + show_module = true; + // fall through + case Address::DumpStyleFileAddress: + vmaddr = m_base_addr.GetFileAddress(); + break; + + case Address::DumpStyleLoadAddress: + vmaddr = m_base_addr.GetLoadAddress(target); + break; + } + + if (vmaddr != LLDB_INVALID_ADDRESS) + { + if (show_module) + { + ModuleSP module_sp (GetBaseAddress().GetModule()); + if (module_sp) + s->Printf("%s", module_sp->GetFileSpec().GetFilename().AsCString()); + } + s->AddressRange(vmaddr, vmaddr + GetByteSize(), addr_size); + return true; + } + else if (fallback_style != Address::DumpStyleInvalid) + { + return Dump(s, target, fallback_style, Address::DumpStyleInvalid); + } + + return false; +} + + +void +AddressRange::DumpDebug (Stream *s) const +{ + s->Printf("%p: AddressRange section = %p, offset = 0x%16.16" PRIx64 ", byte_size = 0x%16.16" PRIx64 "\n", this, m_base_addr.GetSection().get(), m_base_addr.GetOffset(), GetByteSize()); +} +// +//bool +//lldb::operator== (const AddressRange& lhs, const AddressRange& rhs) +//{ +// if (lhs.GetBaseAddress() == rhs.GetBaseAddress()) +// return lhs.GetByteSize() == rhs.GetByteSize(); +// return false; +//} diff --git a/contrib/llvm/tools/lldb/source/Core/AddressResolver.cpp b/contrib/llvm/tools/lldb/source/Core/AddressResolver.cpp new file mode 100644 index 00000000000..5369d960f25 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Core/AddressResolver.cpp @@ -0,0 +1,67 @@ +//===-- AddressResolver.cpp -------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Core/AddressResolver.h" + + +// Project includes + +#include "lldb/Core/Address.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/ModuleList.h" +#include "lldb/Core/SearchFilter.h" +#include "lldb/Core/Stream.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Symbol/SymbolContext.h" +#include "lldb/Target/Target.h" +#include "lldb/lldb-private-log.h" + +using namespace lldb_private; + +//---------------------------------------------------------------------- +// AddressResolver: +//---------------------------------------------------------------------- +AddressResolver::AddressResolver () +{ +} + +AddressResolver::~AddressResolver () +{ + +} + +void +AddressResolver::ResolveAddressInModules (SearchFilter &filter, ModuleList &modules) +{ + filter.SearchInModuleList(*this, modules); +} + +void +AddressResolver::ResolveAddress (SearchFilter &filter) +{ + filter.Search (*this); +} + +std::vector & +AddressResolver::GetAddressRanges () +{ + return m_address_ranges; +} + +size_t +AddressResolver::GetNumberOfAddresses () +{ + return m_address_ranges.size(); +} + +AddressRange & +AddressResolver::GetAddressRangeAtIndex (size_t idx) +{ + return m_address_ranges[idx]; +} diff --git a/contrib/llvm/tools/lldb/source/Core/AddressResolverFileLine.cpp b/contrib/llvm/tools/lldb/source/Core/AddressResolverFileLine.cpp new file mode 100644 index 00000000000..f7004c8bb08 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Core/AddressResolverFileLine.cpp @@ -0,0 +1,102 @@ +//===-- AddressResolverFileLine.cpp -----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Core/AddressResolverFileLine.h" + +// Project includes +#include "lldb/Core/Log.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Symbol/CompileUnit.h" +#include "lldb/Symbol/SymbolContext.h" +#include "lldb/lldb-private-log.h" + +using namespace lldb; +using namespace lldb_private; + +//---------------------------------------------------------------------- +// AddressResolverFileLine: +//---------------------------------------------------------------------- +AddressResolverFileLine::AddressResolverFileLine +( + const FileSpec &file_spec, + uint32_t line_no, + bool check_inlines +) : + AddressResolver (), + m_file_spec (file_spec), + m_line_number (line_no), + m_inlines (check_inlines) +{ +} + +AddressResolverFileLine::~AddressResolverFileLine () +{ +} + +Searcher::CallbackReturn +AddressResolverFileLine::SearchCallback +( + SearchFilter &filter, + SymbolContext &context, + Address *addr, + bool containing +) +{ + SymbolContextList sc_list; + uint32_t sc_list_size; + CompileUnit *cu = context.comp_unit; + + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_BREAKPOINTS)); + + sc_list_size = cu->ResolveSymbolContext (m_file_spec, m_line_number, m_inlines, false, eSymbolContextEverything, + sc_list); + for (uint32_t i = 0; i < sc_list_size; i++) + { + SymbolContext sc; + if (sc_list.GetContextAtIndex(i, sc)) + { + Address line_start = sc.line_entry.range.GetBaseAddress(); + addr_t byte_size = sc.line_entry.range.GetByteSize(); + if (line_start.IsValid()) + { + AddressRange new_range (line_start, byte_size); + m_address_ranges.push_back (new_range); + if (log) + { + StreamString s; + //new_bp_loc->GetDescription (&s, lldb::eDescriptionLevelVerbose); + //log->Printf ("Added address: %s\n", s.GetData()); + } + } + else + { + if (log) + log->Printf ("error: Unable to resolve address at file address 0x%" PRIx64 " for %s:%d\n", + line_start.GetFileAddress(), + m_file_spec.GetFilename().AsCString(""), + m_line_number); + } + } + } + return Searcher::eCallbackReturnContinue; +} + +Searcher::Depth +AddressResolverFileLine::GetDepth() +{ + return Searcher::eDepthCompUnit; +} + +void +AddressResolverFileLine::GetDescription (Stream *s) +{ + s->Printf ("File and line address - file: \"%s\" line: %u", m_file_spec.GetFilename().AsCString(), m_line_number); +} + + diff --git a/contrib/llvm/tools/lldb/source/Core/AddressResolverName.cpp b/contrib/llvm/tools/lldb/source/Core/AddressResolverName.cpp new file mode 100644 index 00000000000..dd22e17402b --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Core/AddressResolverName.cpp @@ -0,0 +1,255 @@ +//===-- AddressResolverName.cpp ---------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Core/AddressResolverName.h" + +// Project includes +#include "lldb/Core/Log.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Symbol/ClangNamespaceDecl.h" +#include "lldb/Symbol/Function.h" +#include "lldb/Symbol/SymbolContext.h" +#include "lldb/Symbol/Symbol.h" +#include "lldb/lldb-private-log.h" + +using namespace lldb; +using namespace lldb_private; + +AddressResolverName::AddressResolverName +( + const char *func_name, + AddressResolver::MatchType type +) : + AddressResolver (), + m_func_name (func_name), + m_class_name (NULL), + m_regex (), + m_match_type (type) +{ + if (m_match_type == AddressResolver::Regexp) + { + if (!m_regex.Compile (m_func_name.AsCString())) + { + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_BREAKPOINTS)); + + if (log) + log->Warning ("function name regexp: \"%s\" did not compile.", m_func_name.AsCString()); + } + } +} + +AddressResolverName::AddressResolverName +( + RegularExpression &func_regex +) : + AddressResolver (), + m_func_name (NULL), + m_class_name (NULL), + m_regex (func_regex), + m_match_type (AddressResolver::Regexp) +{ + +} + +AddressResolverName::AddressResolverName +( + const char *class_name, + const char *method, + AddressResolver::MatchType type +) : + AddressResolver (), + m_func_name (method), + m_class_name (class_name), + m_regex (), + m_match_type (type) +{ + +} + +AddressResolverName::~AddressResolverName () +{ +} + +// FIXME: Right now we look at the module level, and call the module's "FindFunctions". +// Greg says he will add function tables, maybe at the CompileUnit level to accelerate function +// lookup. At that point, we should switch the depth to CompileUnit, and look in these tables. + +Searcher::CallbackReturn +AddressResolverName::SearchCallback +( + SearchFilter &filter, + SymbolContext &context, + Address *addr, + bool containing +) +{ + SymbolContextList func_list; + SymbolContextList sym_list; + + bool skip_prologue = true; + uint32_t i; + SymbolContext sc; + Address func_addr; + + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_BREAKPOINTS)); + + if (m_class_name) + { + if (log) + log->Warning ("Class/method function specification not supported yet.\n"); + return Searcher::eCallbackReturnStop; + } + + const bool include_symbols = false; + const bool include_inlines = true; + const bool append = false; + switch (m_match_type) + { + case AddressResolver::Exact: + if (context.module_sp) + { + context.module_sp->FindSymbolsWithNameAndType (m_func_name, + eSymbolTypeCode, + sym_list); + context.module_sp->FindFunctions (m_func_name, + NULL, + eFunctionNameTypeAuto, + include_symbols, + include_inlines, + append, + func_list); + } + break; + + case AddressResolver::Regexp: + if (context.module_sp) + { + context.module_sp->FindSymbolsMatchingRegExAndType (m_regex, + eSymbolTypeCode, + sym_list); + context.module_sp->FindFunctions (m_regex, + include_symbols, + include_inlines, + append, + func_list); + } + break; + + case AddressResolver::Glob: + if (log) + log->Warning ("glob is not supported yet."); + break; + } + + // Remove any duplicates between the funcion list and the symbol list + if (func_list.GetSize()) + { + for (i = 0; i < func_list.GetSize(); i++) + { + if (func_list.GetContextAtIndex(i, sc) == false) + continue; + + if (sc.function == NULL) + continue; + uint32_t j = 0; + while (j < sym_list.GetSize()) + { + SymbolContext symbol_sc; + if (sym_list.GetContextAtIndex(j, symbol_sc)) + { + if (symbol_sc.symbol && symbol_sc.symbol->ValueIsAddress()) + { + if (sc.function->GetAddressRange().GetBaseAddress() == symbol_sc.symbol->GetAddress()) + { + sym_list.RemoveContextAtIndex(j); + continue; // Don't increment j + } + } + } + + j++; + } + } + + for (i = 0; i < func_list.GetSize(); i++) + { + if (func_list.GetContextAtIndex(i, sc)) + { + if (sc.function) + { + func_addr = sc.function->GetAddressRange().GetBaseAddress(); + addr_t byte_size = sc.function->GetAddressRange().GetByteSize(); + if (skip_prologue) + { + const uint32_t prologue_byte_size = sc.function->GetPrologueByteSize(); + if (prologue_byte_size) + { + func_addr.SetOffset (func_addr.GetOffset() + prologue_byte_size); + byte_size -= prologue_byte_size; + } + } + + if (filter.AddressPasses (func_addr)) + { + AddressRange new_range (func_addr, byte_size); + m_address_ranges.push_back (new_range); + } + } + } + } + } + + for (i = 0; i < sym_list.GetSize(); i++) + { + if (sym_list.GetContextAtIndex(i, sc)) + { + if (sc.symbol && sc.symbol->ValueIsAddress()) + { + func_addr = sc.symbol->GetAddress(); + addr_t byte_size = sc.symbol->GetByteSize(); + + if (skip_prologue) + { + const uint32_t prologue_byte_size = sc.symbol->GetPrologueByteSize(); + if (prologue_byte_size) + { + func_addr.SetOffset (func_addr.GetOffset() + prologue_byte_size); + byte_size -= prologue_byte_size; + } + } + + if (filter.AddressPasses (func_addr)) + { + AddressRange new_range (func_addr, byte_size); + m_address_ranges.push_back (new_range); + } + } + } + } + return Searcher::eCallbackReturnContinue; +} + +Searcher::Depth +AddressResolverName::GetDepth() +{ + return Searcher::eDepthModule; +} + +void +AddressResolverName::GetDescription (Stream *s) +{ + s->PutCString("Address by function name: "); + + if (m_match_type == AddressResolver::Regexp) + s->Printf("'%s' (regular expression)", m_regex.GetText()); + else + s->Printf("'%s'", m_func_name.AsCString()); +} + diff --git a/contrib/llvm/tools/lldb/source/Core/ArchSpec.cpp b/contrib/llvm/tools/lldb/source/Core/ArchSpec.cpp new file mode 100644 index 00000000000..27d62c358bb --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Core/ArchSpec.cpp @@ -0,0 +1,893 @@ +//===-- ArchSpec.cpp --------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Core/ArchSpec.h" + +#include +#include + +#include + +#include "llvm/Support/ELF.h" +#include "llvm/Support/Host.h" +#include "llvm/Support/MachO.h" +#include "lldb/Core/RegularExpression.h" +#include "lldb/Host/Endian.h" +#include "lldb/Host/Host.h" +#include "lldb/Target/Platform.h" + +using namespace lldb; +using namespace lldb_private; + +#define ARCH_SPEC_SEPARATOR_CHAR '-' + + +static bool cores_match (const ArchSpec::Core core1, const ArchSpec::Core core2, bool try_inverse, bool enforce_exact_match); + +namespace lldb_private { + + struct CoreDefinition + { + ByteOrder default_byte_order; + uint32_t addr_byte_size; + uint32_t min_opcode_byte_size; + uint32_t max_opcode_byte_size; + llvm::Triple::ArchType machine; + ArchSpec::Core core; + const char *name; + }; + +} + +// This core information can be looked using the ArchSpec::Core as the index +static const CoreDefinition g_core_definitions[ArchSpec::kNumCores] = +{ + { eByteOrderLittle, 4, 2, 4, llvm::Triple::arm , ArchSpec::eCore_arm_generic , "arm" }, + { eByteOrderLittle, 4, 2, 4, llvm::Triple::arm , ArchSpec::eCore_arm_armv4 , "armv4" }, + { eByteOrderLittle, 4, 2, 4, llvm::Triple::arm , ArchSpec::eCore_arm_armv4t , "armv4t" }, + { eByteOrderLittle, 4, 2, 4, llvm::Triple::arm , ArchSpec::eCore_arm_armv5 , "armv5" }, + { eByteOrderLittle, 4, 2, 4, llvm::Triple::arm , ArchSpec::eCore_arm_armv5e , "armv5e" }, + { eByteOrderLittle, 4, 2, 4, llvm::Triple::arm , ArchSpec::eCore_arm_armv5t , "armv5t" }, + { eByteOrderLittle, 4, 2, 4, llvm::Triple::arm , ArchSpec::eCore_arm_armv6 , "armv6" }, + { eByteOrderLittle, 4, 2, 4, llvm::Triple::arm , ArchSpec::eCore_arm_armv7 , "armv7" }, + { eByteOrderLittle, 4, 2, 4, llvm::Triple::arm , ArchSpec::eCore_arm_armv7f , "armv7f" }, + { eByteOrderLittle, 4, 2, 4, llvm::Triple::arm , ArchSpec::eCore_arm_armv7s , "armv7s" }, + { eByteOrderLittle, 4, 2, 4, llvm::Triple::arm , ArchSpec::eCore_arm_armv7k , "armv7k" }, + { eByteOrderLittle, 4, 2, 4, llvm::Triple::arm , ArchSpec::eCore_arm_armv7m , "armv7m" }, + { eByteOrderLittle, 4, 2, 4, llvm::Triple::arm , ArchSpec::eCore_arm_armv7em , "armv7em" }, + { eByteOrderLittle, 4, 2, 4, llvm::Triple::arm , ArchSpec::eCore_arm_xscale , "xscale" }, + { eByteOrderLittle, 4, 2, 4, llvm::Triple::thumb , ArchSpec::eCore_thumb , "thumb" }, + { eByteOrderLittle, 4, 2, 4, llvm::Triple::thumb , ArchSpec::eCore_thumbv4t , "thumbv4t" }, + { eByteOrderLittle, 4, 2, 4, llvm::Triple::thumb , ArchSpec::eCore_thumbv5 , "thumbv5" }, + { eByteOrderLittle, 4, 2, 4, llvm::Triple::thumb , ArchSpec::eCore_thumbv5e , "thumbv5e" }, + { eByteOrderLittle, 4, 2, 4, llvm::Triple::thumb , ArchSpec::eCore_thumbv6 , "thumbv6" }, + { eByteOrderLittle, 4, 2, 4, llvm::Triple::thumb , ArchSpec::eCore_thumbv7 , "thumbv7" }, + { eByteOrderLittle, 4, 2, 4, llvm::Triple::thumb , ArchSpec::eCore_thumbv7f , "thumbv7f" }, + { eByteOrderLittle, 4, 2, 4, llvm::Triple::thumb , ArchSpec::eCore_thumbv7s , "thumbv7s" }, + { eByteOrderLittle, 4, 2, 4, llvm::Triple::thumb , ArchSpec::eCore_thumbv7k , "thumbv7k" }, + { eByteOrderLittle, 4, 2, 4, llvm::Triple::thumb , ArchSpec::eCore_thumbv7m , "thumbv7m" }, + { eByteOrderLittle, 4, 2, 4, llvm::Triple::thumb , ArchSpec::eCore_thumbv7em , "thumbv7em" }, + + + { eByteOrderBig , 4, 4, 4, llvm::Triple::ppc , ArchSpec::eCore_ppc_generic , "ppc" }, + { eByteOrderBig , 4, 4, 4, llvm::Triple::ppc , ArchSpec::eCore_ppc_ppc601 , "ppc601" }, + { eByteOrderBig , 4, 4, 4, llvm::Triple::ppc , ArchSpec::eCore_ppc_ppc602 , "ppc602" }, + { eByteOrderBig , 4, 4, 4, llvm::Triple::ppc , ArchSpec::eCore_ppc_ppc603 , "ppc603" }, + { eByteOrderBig , 4, 4, 4, llvm::Triple::ppc , ArchSpec::eCore_ppc_ppc603e , "ppc603e" }, + { eByteOrderBig , 4, 4, 4, llvm::Triple::ppc , ArchSpec::eCore_ppc_ppc603ev , "ppc603ev" }, + { eByteOrderBig , 4, 4, 4, llvm::Triple::ppc , ArchSpec::eCore_ppc_ppc604 , "ppc604" }, + { eByteOrderBig , 4, 4, 4, llvm::Triple::ppc , ArchSpec::eCore_ppc_ppc604e , "ppc604e" }, + { eByteOrderBig , 4, 4, 4, llvm::Triple::ppc , ArchSpec::eCore_ppc_ppc620 , "ppc620" }, + { eByteOrderBig , 4, 4, 4, llvm::Triple::ppc , ArchSpec::eCore_ppc_ppc750 , "ppc750" }, + { eByteOrderBig , 4, 4, 4, llvm::Triple::ppc , ArchSpec::eCore_ppc_ppc7400 , "ppc7400" }, + { eByteOrderBig , 4, 4, 4, llvm::Triple::ppc , ArchSpec::eCore_ppc_ppc7450 , "ppc7450" }, + { eByteOrderBig , 4, 4, 4, llvm::Triple::ppc , ArchSpec::eCore_ppc_ppc970 , "ppc970" }, + + { eByteOrderBig , 8, 4, 4, llvm::Triple::ppc64 , ArchSpec::eCore_ppc64_generic , "ppc64" }, + { eByteOrderBig , 8, 4, 4, llvm::Triple::ppc64 , ArchSpec::eCore_ppc64_ppc970_64 , "ppc970-64" }, + + { eByteOrderLittle, 4, 4, 4, llvm::Triple::sparc , ArchSpec::eCore_sparc_generic , "sparc" }, + { eByteOrderLittle, 8, 4, 4, llvm::Triple::sparcv9, ArchSpec::eCore_sparc9_generic , "sparcv9" }, + + { eByteOrderLittle, 4, 1, 15, llvm::Triple::x86 , ArchSpec::eCore_x86_32_i386 , "i386" }, + { eByteOrderLittle, 4, 1, 15, llvm::Triple::x86 , ArchSpec::eCore_x86_32_i486 , "i486" }, + { eByteOrderLittle, 4, 1, 15, llvm::Triple::x86 , ArchSpec::eCore_x86_32_i486sx , "i486sx" }, + + { eByteOrderLittle, 8, 1, 15, llvm::Triple::x86_64 , ArchSpec::eCore_x86_64_x86_64 , "x86_64" }, + { eByteOrderLittle, 4, 4, 4 , llvm::Triple::UnknownArch , ArchSpec::eCore_uknownMach32 , "unknown-mach-32" }, + { eByteOrderLittle, 8, 4, 4 , llvm::Triple::UnknownArch , ArchSpec::eCore_uknownMach64 , "unknown-mach-64" } +}; + +struct ArchDefinitionEntry +{ + ArchSpec::Core core; + uint32_t cpu; + uint32_t sub; + uint32_t cpu_mask; + uint32_t sub_mask; +}; + +struct ArchDefinition +{ + ArchitectureType type; + size_t num_entries; + const ArchDefinitionEntry *entries; + const char *name; +}; + + +size_t +ArchSpec::AutoComplete (const char *name, StringList &matches) +{ + uint32_t i; + if (name && name[0]) + { + for (i = 0; i < ArchSpec::kNumCores; ++i) + { + if (NameMatches(g_core_definitions[i].name, eNameMatchStartsWith, name)) + matches.AppendString (g_core_definitions[i].name); + } + } + else + { + for (i = 0; i < ArchSpec::kNumCores; ++i) + matches.AppendString (g_core_definitions[i].name); + } + return matches.GetSize(); +} + + + +#define CPU_ANY (UINT32_MAX) + +//===----------------------------------------------------------------------===// +// A table that gets searched linearly for matches. This table is used to +// convert cpu type and subtypes to architecture names, and to convert +// architecture names to cpu types and subtypes. The ordering is important and +// allows the precedence to be set when the table is built. +#define SUBTYPE_MASK 0x00FFFFFFu +static const ArchDefinitionEntry g_macho_arch_entries[] = +{ + { ArchSpec::eCore_arm_generic , llvm::MachO::CPUTypeARM , CPU_ANY, UINT32_MAX , UINT32_MAX }, + { ArchSpec::eCore_arm_generic , llvm::MachO::CPUTypeARM , 0 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_arm_armv4 , llvm::MachO::CPUTypeARM , 5 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_arm_armv4t , llvm::MachO::CPUTypeARM , 5 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_arm_armv6 , llvm::MachO::CPUTypeARM , 6 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_arm_armv5 , llvm::MachO::CPUTypeARM , 7 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_arm_armv5e , llvm::MachO::CPUTypeARM , 7 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_arm_armv5t , llvm::MachO::CPUTypeARM , 7 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_arm_xscale , llvm::MachO::CPUTypeARM , 8 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_arm_armv7 , llvm::MachO::CPUTypeARM , 9 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_arm_armv7f , llvm::MachO::CPUTypeARM , 10 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_arm_armv7s , llvm::MachO::CPUTypeARM , 11 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_arm_armv7k , llvm::MachO::CPUTypeARM , 12 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_arm_armv7m , llvm::MachO::CPUTypeARM , 15 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_arm_armv7em , llvm::MachO::CPUTypeARM , 16 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_thumb , llvm::MachO::CPUTypeARM , 0 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_thumbv4t , llvm::MachO::CPUTypeARM , 5 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_thumbv5 , llvm::MachO::CPUTypeARM , 7 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_thumbv5e , llvm::MachO::CPUTypeARM , 7 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_thumbv6 , llvm::MachO::CPUTypeARM , 6 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_thumbv7 , llvm::MachO::CPUTypeARM , 9 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_thumbv7f , llvm::MachO::CPUTypeARM , 10 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_thumbv7s , llvm::MachO::CPUTypeARM , 11 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_thumbv7k , llvm::MachO::CPUTypeARM , 12 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_thumbv7m , llvm::MachO::CPUTypeARM , 15 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_thumbv7em , llvm::MachO::CPUTypeARM , 16 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_ppc_generic , llvm::MachO::CPUTypePowerPC , CPU_ANY, UINT32_MAX , UINT32_MAX }, + { ArchSpec::eCore_ppc_generic , llvm::MachO::CPUTypePowerPC , 0 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_ppc_ppc601 , llvm::MachO::CPUTypePowerPC , 1 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_ppc_ppc602 , llvm::MachO::CPUTypePowerPC , 2 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_ppc_ppc603 , llvm::MachO::CPUTypePowerPC , 3 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_ppc_ppc603e , llvm::MachO::CPUTypePowerPC , 4 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_ppc_ppc603ev , llvm::MachO::CPUTypePowerPC , 5 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_ppc_ppc604 , llvm::MachO::CPUTypePowerPC , 6 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_ppc_ppc604e , llvm::MachO::CPUTypePowerPC , 7 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_ppc_ppc620 , llvm::MachO::CPUTypePowerPC , 8 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_ppc_ppc750 , llvm::MachO::CPUTypePowerPC , 9 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_ppc_ppc7400 , llvm::MachO::CPUTypePowerPC , 10 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_ppc_ppc7450 , llvm::MachO::CPUTypePowerPC , 11 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_ppc_ppc970 , llvm::MachO::CPUTypePowerPC , 100 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_ppc64_generic , llvm::MachO::CPUTypePowerPC64 , 0 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_ppc64_ppc970_64 , llvm::MachO::CPUTypePowerPC64 , 100 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_x86_32_i386 , llvm::MachO::CPUTypeI386 , 3 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_x86_32_i486 , llvm::MachO::CPUTypeI386 , 4 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_x86_32_i486sx , llvm::MachO::CPUTypeI386 , 0x84 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_x86_32_i386 , llvm::MachO::CPUTypeI386 , CPU_ANY, UINT32_MAX , UINT32_MAX }, + { ArchSpec::eCore_x86_64_x86_64 , llvm::MachO::CPUTypeX86_64 , 3 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_x86_64_x86_64 , llvm::MachO::CPUTypeX86_64 , 4 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_x86_64_x86_64 , llvm::MachO::CPUTypeX86_64 , CPU_ANY, UINT32_MAX , UINT32_MAX }, + // Catch any unknown mach architectures so we can always use the object and symbol mach-o files + { ArchSpec::eCore_uknownMach32 , 0 , 0 , 0xFF000000u, 0x00000000u }, + { ArchSpec::eCore_uknownMach64 , llvm::MachO::CPUArchABI64 , 0 , 0xFF000000u, 0x00000000u } +}; +static const ArchDefinition g_macho_arch_def = { + eArchTypeMachO, + sizeof(g_macho_arch_entries)/sizeof(g_macho_arch_entries[0]), + g_macho_arch_entries, + "mach-o" +}; + +//===----------------------------------------------------------------------===// +// A table that gets searched linearly for matches. This table is used to +// convert cpu type and subtypes to architecture names, and to convert +// architecture names to cpu types and subtypes. The ordering is important and +// allows the precedence to be set when the table is built. +static const ArchDefinitionEntry g_elf_arch_entries[] = +{ + { ArchSpec::eCore_sparc_generic , llvm::ELF::EM_SPARC , LLDB_INVALID_CPUTYPE, 0xFFFFFFFFu, 0xFFFFFFFFu }, // Sparc + { ArchSpec::eCore_x86_32_i386 , llvm::ELF::EM_386 , LLDB_INVALID_CPUTYPE, 0xFFFFFFFFu, 0xFFFFFFFFu }, // Intel 80386 + { ArchSpec::eCore_x86_32_i486 , llvm::ELF::EM_486 , LLDB_INVALID_CPUTYPE, 0xFFFFFFFFu, 0xFFFFFFFFu }, // Intel 486 (deprecated) + { ArchSpec::eCore_ppc_generic , llvm::ELF::EM_PPC , LLDB_INVALID_CPUTYPE, 0xFFFFFFFFu, 0xFFFFFFFFu }, // PowerPC + { ArchSpec::eCore_ppc64_generic , llvm::ELF::EM_PPC64 , LLDB_INVALID_CPUTYPE, 0xFFFFFFFFu, 0xFFFFFFFFu }, // PowerPC64 + { ArchSpec::eCore_arm_generic , llvm::ELF::EM_ARM , LLDB_INVALID_CPUTYPE, 0xFFFFFFFFu, 0xFFFFFFFFu }, // ARM + { ArchSpec::eCore_sparc9_generic , llvm::ELF::EM_SPARCV9, LLDB_INVALID_CPUTYPE, 0xFFFFFFFFu, 0xFFFFFFFFu }, // SPARC V9 + { ArchSpec::eCore_x86_64_x86_64 , llvm::ELF::EM_X86_64 , LLDB_INVALID_CPUTYPE, 0xFFFFFFFFu, 0xFFFFFFFFu } // AMD64 +}; + +static const ArchDefinition g_elf_arch_def = { + eArchTypeELF, + sizeof(g_elf_arch_entries)/sizeof(g_elf_arch_entries[0]), + g_elf_arch_entries, + "elf", +}; + +//===----------------------------------------------------------------------===// +// Table of all ArchDefinitions +static const ArchDefinition *g_arch_definitions[] = { + &g_macho_arch_def, + &g_elf_arch_def +}; + +static const size_t k_num_arch_definitions = + sizeof(g_arch_definitions) / sizeof(g_arch_definitions[0]); + +//===----------------------------------------------------------------------===// +// Static helper functions. + + +// Get the architecture definition for a given object type. +static const ArchDefinition * +FindArchDefinition (ArchitectureType arch_type) +{ + for (unsigned int i = 0; i < k_num_arch_definitions; ++i) + { + const ArchDefinition *def = g_arch_definitions[i]; + if (def->type == arch_type) + return def; + } + return NULL; +} + +// Get an architecture definition by name. +static const CoreDefinition * +FindCoreDefinition (llvm::StringRef name) +{ + for (unsigned int i = 0; i < ArchSpec::kNumCores; ++i) + { + if (name.equals_lower(g_core_definitions[i].name)) + return &g_core_definitions[i]; + } + return NULL; +} + +static inline const CoreDefinition * +FindCoreDefinition (ArchSpec::Core core) +{ + if (core >= 0 && core < ArchSpec::kNumCores) + return &g_core_definitions[core]; + return NULL; +} + +// Get a definition entry by cpu type and subtype. +static const ArchDefinitionEntry * +FindArchDefinitionEntry (const ArchDefinition *def, uint32_t cpu, uint32_t sub) +{ + if (def == NULL) + return NULL; + + const ArchDefinitionEntry *entries = def->entries; + for (size_t i = 0; i < def->num_entries; ++i) + { + if (entries[i].cpu == (cpu & entries[i].cpu_mask)) + if (entries[i].sub == (sub & entries[i].sub_mask)) + return &entries[i]; + } + return NULL; +} + +static const ArchDefinitionEntry * +FindArchDefinitionEntry (const ArchDefinition *def, ArchSpec::Core core) +{ + if (def == NULL) + return NULL; + + const ArchDefinitionEntry *entries = def->entries; + for (size_t i = 0; i < def->num_entries; ++i) + { + if (entries[i].core == core) + return &entries[i]; + } + return NULL; +} + +//===----------------------------------------------------------------------===// +// Constructors and destructors. + +ArchSpec::ArchSpec() : + m_triple (), + m_core (kCore_invalid), + m_byte_order (eByteOrderInvalid) +{ +} + +ArchSpec::ArchSpec (const char *triple_cstr, Platform *platform) : + m_triple (), + m_core (kCore_invalid), + m_byte_order (eByteOrderInvalid) +{ + if (triple_cstr) + SetTriple(triple_cstr, platform); +} + + +ArchSpec::ArchSpec (const char *triple_cstr) : + m_triple (), + m_core (kCore_invalid), + m_byte_order (eByteOrderInvalid) +{ + if (triple_cstr) + SetTriple(triple_cstr); +} + +ArchSpec::ArchSpec(const llvm::Triple &triple) : + m_triple (), + m_core (kCore_invalid), + m_byte_order (eByteOrderInvalid) +{ + SetTriple(triple); +} + +ArchSpec::ArchSpec (ArchitectureType arch_type, uint32_t cpu, uint32_t subtype) : + m_triple (), + m_core (kCore_invalid), + m_byte_order (eByteOrderInvalid) +{ + SetArchitecture (arch_type, cpu, subtype); +} + +ArchSpec::~ArchSpec() +{ +} + +//===----------------------------------------------------------------------===// +// Assignment and initialization. + +const ArchSpec& +ArchSpec::operator= (const ArchSpec& rhs) +{ + if (this != &rhs) + { + m_triple = rhs.m_triple; + m_core = rhs.m_core; + m_byte_order = rhs.m_byte_order; + } + return *this; +} + +void +ArchSpec::Clear() +{ + m_triple = llvm::Triple(); + m_core = kCore_invalid; + m_byte_order = eByteOrderInvalid; +} + +//===----------------------------------------------------------------------===// +// Predicates. + + +const char * +ArchSpec::GetArchitectureName () const +{ + const CoreDefinition *core_def = FindCoreDefinition (m_core); + if (core_def) + return core_def->name; + return "unknown"; +} + +uint32_t +ArchSpec::GetMachOCPUType () const +{ + const CoreDefinition *core_def = FindCoreDefinition (m_core); + if (core_def) + { + const ArchDefinitionEntry *arch_def = FindArchDefinitionEntry (&g_macho_arch_def, core_def->core); + if (arch_def) + { + return arch_def->cpu; + } + } + return LLDB_INVALID_CPUTYPE; +} + +uint32_t +ArchSpec::GetMachOCPUSubType () const +{ + const CoreDefinition *core_def = FindCoreDefinition (m_core); + if (core_def) + { + const ArchDefinitionEntry *arch_def = FindArchDefinitionEntry (&g_macho_arch_def, core_def->core); + if (arch_def) + { + return arch_def->sub; + } + } + return LLDB_INVALID_CPUTYPE; +} + +llvm::Triple::ArchType +ArchSpec::GetMachine () const +{ + const CoreDefinition *core_def = FindCoreDefinition (m_core); + if (core_def) + return core_def->machine; + + return llvm::Triple::UnknownArch; +} + +uint32_t +ArchSpec::GetAddressByteSize() const +{ + const CoreDefinition *core_def = FindCoreDefinition (m_core); + if (core_def) + return core_def->addr_byte_size; + return 0; +} + +ByteOrder +ArchSpec::GetDefaultEndian () const +{ + const CoreDefinition *core_def = FindCoreDefinition (m_core); + if (core_def) + return core_def->default_byte_order; + return eByteOrderInvalid; +} + +lldb::ByteOrder +ArchSpec::GetByteOrder () const +{ + if (m_byte_order == eByteOrderInvalid) + return GetDefaultEndian(); + return m_byte_order; +} + +//===----------------------------------------------------------------------===// +// Mutators. + +bool +ArchSpec::SetTriple (const llvm::Triple &triple) +{ + m_triple = triple; + + llvm::StringRef arch_name (m_triple.getArchName()); + const CoreDefinition *core_def = FindCoreDefinition (arch_name); + if (core_def) + { + m_core = core_def->core; + // Set the byte order to the default byte order for an architecture. + // This can be modified if needed for cases when cores handle both + // big and little endian + m_byte_order = core_def->default_byte_order; + } + else + { + Clear(); + } + + + return IsValid(); +} + +static bool +ParseMachCPUDashSubtypeTriple (const char *triple_cstr, ArchSpec &arch) +{ + // Accept "12-10" or "12.10" as cpu type/subtype + if (isdigit(triple_cstr[0])) + { + char *end = NULL; + errno = 0; + uint32_t cpu = (uint32_t)::strtoul (triple_cstr, &end, 0); + if (errno == 0 && cpu != 0 && end && ((*end == '-') || (*end == '.'))) + { + errno = 0; + uint32_t sub = (uint32_t)::strtoul (end + 1, &end, 0); + if (errno == 0 && end && ((*end == '-') || (*end == '.') || (*end == '\0'))) + { + if (arch.SetArchitecture (eArchTypeMachO, cpu, sub)) + { + if (*end == '-') + { + llvm::StringRef vendor_os (end + 1); + size_t dash_pos = vendor_os.find('-'); + if (dash_pos != llvm::StringRef::npos) + { + llvm::StringRef vendor_str(vendor_os.substr(0, dash_pos)); + arch.GetTriple().setVendorName(vendor_str); + const size_t vendor_start_pos = dash_pos+1; + dash_pos = vendor_os.find('-', vendor_start_pos); + if (dash_pos == llvm::StringRef::npos) + { + if (vendor_start_pos < vendor_os.size()) + arch.GetTriple().setOSName(vendor_os.substr(vendor_start_pos)); + } + else + { + arch.GetTriple().setOSName(vendor_os.substr(vendor_start_pos, dash_pos - vendor_start_pos)); + } + } + } + return true; + } + } + } + } + return false; +} +bool +ArchSpec::SetTriple (const char *triple_cstr) +{ + if (triple_cstr && triple_cstr[0]) + { + if (ParseMachCPUDashSubtypeTriple (triple_cstr, *this)) + return true; + + llvm::StringRef triple_stref (triple_cstr); + if (triple_stref.startswith (LLDB_ARCH_DEFAULT)) + { + // Special case for the current host default architectures... + if (triple_stref.equals (LLDB_ARCH_DEFAULT_32BIT)) + *this = Host::GetArchitecture (Host::eSystemDefaultArchitecture32); + else if (triple_stref.equals (LLDB_ARCH_DEFAULT_64BIT)) + *this = Host::GetArchitecture (Host::eSystemDefaultArchitecture64); + else if (triple_stref.equals (LLDB_ARCH_DEFAULT)) + *this = Host::GetArchitecture (Host::eSystemDefaultArchitecture); + } + else + { + std::string normalized_triple_sstr (llvm::Triple::normalize(triple_stref)); + triple_stref = normalized_triple_sstr; + SetTriple (llvm::Triple (triple_stref)); + } + } + else + Clear(); + return IsValid(); +} + +bool +ArchSpec::SetTriple (const char *triple_cstr, Platform *platform) +{ + if (triple_cstr && triple_cstr[0]) + { + if (ParseMachCPUDashSubtypeTriple (triple_cstr, *this)) + return true; + + llvm::StringRef triple_stref (triple_cstr); + if (triple_stref.startswith (LLDB_ARCH_DEFAULT)) + { + // Special case for the current host default architectures... + if (triple_stref.equals (LLDB_ARCH_DEFAULT_32BIT)) + *this = Host::GetArchitecture (Host::eSystemDefaultArchitecture32); + else if (triple_stref.equals (LLDB_ARCH_DEFAULT_64BIT)) + *this = Host::GetArchitecture (Host::eSystemDefaultArchitecture64); + else if (triple_stref.equals (LLDB_ARCH_DEFAULT)) + *this = Host::GetArchitecture (Host::eSystemDefaultArchitecture); + } + else + { + ArchSpec raw_arch (triple_cstr); + + std::string normalized_triple_sstr (llvm::Triple::normalize(triple_stref)); + triple_stref = normalized_triple_sstr; + llvm::Triple normalized_triple (triple_stref); + + const bool os_specified = normalized_triple.getOSName().size() > 0; + const bool vendor_specified = normalized_triple.getVendorName().size() > 0; + const bool env_specified = normalized_triple.getEnvironmentName().size() > 0; + + // If we got an arch only, then default the vendor, os, environment + // to match the platform if one is supplied + if (!(os_specified || vendor_specified || env_specified)) + { + if (platform) + { + // If we were given a platform, use the platform's system + // architecture. If this is not available (might not be + // connected) use the first supported architecture. + ArchSpec compatible_arch; + if (platform->IsCompatibleArchitecture (raw_arch, false, &compatible_arch)) + { + if (compatible_arch.IsValid()) + { + const llvm::Triple &compatible_triple = compatible_arch.GetTriple(); + if (!vendor_specified) + normalized_triple.setVendor(compatible_triple.getVendor()); + if (!os_specified) + normalized_triple.setOS(compatible_triple.getOS()); + if (!env_specified && compatible_triple.getEnvironmentName().size()) + normalized_triple.setEnvironment(compatible_triple.getEnvironment()); + } + } + else + { + *this = raw_arch; + return IsValid(); + } + } + else + { + // No platform specified, fall back to the host system for + // the default vendor, os, and environment. + llvm::Triple host_triple(llvm::sys::getDefaultTargetTriple()); + if (!vendor_specified) + normalized_triple.setVendor(host_triple.getVendor()); + if (!vendor_specified) + normalized_triple.setOS(host_triple.getOS()); + if (!env_specified && host_triple.getEnvironmentName().size()) + normalized_triple.setEnvironment(host_triple.getEnvironment()); + } + } + SetTriple (normalized_triple); + } + } + else + Clear(); + return IsValid(); +} + +bool +ArchSpec::SetArchitecture (ArchitectureType arch_type, uint32_t cpu, uint32_t sub) +{ + m_core = kCore_invalid; + bool update_triple = true; + const ArchDefinition *arch_def = FindArchDefinition(arch_type); + if (arch_def) + { + const ArchDefinitionEntry *arch_def_entry = FindArchDefinitionEntry (arch_def, cpu, sub); + if (arch_def_entry) + { + const CoreDefinition *core_def = FindCoreDefinition (arch_def_entry->core); + if (core_def) + { + m_core = core_def->core; + update_triple = false; + // Always use the architecture name because it might be more descriptive + // than the architecture enum ("armv7" -> llvm::Triple::arm). + m_triple.setArchName(llvm::StringRef(core_def->name)); + if (arch_type == eArchTypeMachO) + { + m_triple.setVendor (llvm::Triple::Apple); + + switch (core_def->machine) + { + case llvm::Triple::arm: + case llvm::Triple::thumb: + m_triple.setOS (llvm::Triple::IOS); + break; + + case llvm::Triple::x86: + case llvm::Triple::x86_64: + default: + m_triple.setOS (llvm::Triple::MacOSX); + break; + } + } + else + { + m_triple.setVendor (llvm::Triple::UnknownVendor); + m_triple.setOS (llvm::Triple::UnknownOS); + } + // Fall back onto setting the machine type if the arch by name failed... + if (m_triple.getArch () == llvm::Triple::UnknownArch) + m_triple.setArch (core_def->machine); + } + } + } + CoreUpdated(update_triple); + return IsValid(); +} + +uint32_t +ArchSpec::GetMinimumOpcodeByteSize() const +{ + const CoreDefinition *core_def = FindCoreDefinition (m_core); + if (core_def) + return core_def->min_opcode_byte_size; + return 0; +} + +uint32_t +ArchSpec::GetMaximumOpcodeByteSize() const +{ + const CoreDefinition *core_def = FindCoreDefinition (m_core); + if (core_def) + return core_def->max_opcode_byte_size; + return 0; +} + +bool +ArchSpec::IsExactMatch (const ArchSpec& rhs) const +{ + return IsEqualTo (rhs, true); +} + +bool +ArchSpec::IsCompatibleMatch (const ArchSpec& rhs) const +{ + return IsEqualTo (rhs, false); +} + +bool +ArchSpec::IsEqualTo (const ArchSpec& rhs, bool exact_match) const +{ + if (GetByteOrder() != rhs.GetByteOrder()) + return false; + + const ArchSpec::Core lhs_core = GetCore (); + const ArchSpec::Core rhs_core = rhs.GetCore (); + + const bool core_match = cores_match (lhs_core, rhs_core, true, exact_match); + + if (core_match) + { + const llvm::Triple &lhs_triple = GetTriple(); + const llvm::Triple &rhs_triple = rhs.GetTriple(); + + const llvm::Triple::VendorType lhs_triple_vendor = lhs_triple.getVendor(); + const llvm::Triple::VendorType rhs_triple_vendor = rhs_triple.getVendor(); + if (lhs_triple_vendor != rhs_triple_vendor) + { + if (exact_match) + { + const bool rhs_vendor_specified = rhs.TripleVendorWasSpecified(); + const bool lhs_vendor_specified = TripleVendorWasSpecified(); + // Both architectures had the vendor specified, so if they aren't + // equal then we return false + if (rhs_vendor_specified && lhs_vendor_specified) + return false; + } + + // Only fail if both vendor types are not unknown + if (lhs_triple_vendor != llvm::Triple::UnknownVendor && + rhs_triple_vendor != llvm::Triple::UnknownVendor) + return false; + } + + const llvm::Triple::OSType lhs_triple_os = lhs_triple.getOS(); + const llvm::Triple::OSType rhs_triple_os = rhs_triple.getOS(); + if (lhs_triple_os != rhs_triple_os) + { + if (exact_match) + { + const bool rhs_os_specified = rhs.TripleOSWasSpecified(); + const bool lhs_os_specified = TripleOSWasSpecified(); + // Both architectures had the OS specified, so if they aren't + // equal then we return false + if (rhs_os_specified && lhs_os_specified) + return false; + } + // Only fail if both os types are not unknown + if (lhs_triple_os != llvm::Triple::UnknownOS && + rhs_triple_os != llvm::Triple::UnknownOS) + return false; + } + + const llvm::Triple::EnvironmentType lhs_triple_env = lhs_triple.getEnvironment(); + const llvm::Triple::EnvironmentType rhs_triple_env = rhs_triple.getEnvironment(); + + if (lhs_triple_env != rhs_triple_env) + { + // Only fail if both environment types are not unknown + if (lhs_triple_env != llvm::Triple::UnknownEnvironment && + rhs_triple_env != llvm::Triple::UnknownEnvironment) + return false; + } + return true; + } + return false; +} + +//===----------------------------------------------------------------------===// +// Helper methods. + +void +ArchSpec::CoreUpdated (bool update_triple) +{ + const CoreDefinition *core_def = FindCoreDefinition (m_core); + if (core_def) + { + if (update_triple) + m_triple = llvm::Triple(core_def->name, "unknown", "unknown"); + m_byte_order = core_def->default_byte_order; + } + else + { + if (update_triple) + m_triple = llvm::Triple(); + m_byte_order = eByteOrderInvalid; + } +} + +//===----------------------------------------------------------------------===// +// Operators. + +static bool +cores_match (const ArchSpec::Core core1, const ArchSpec::Core core2, bool try_inverse, bool enforce_exact_match) +{ + if (core1 == core2) + return true; + + switch (core1) + { + case ArchSpec::kCore_any: + return true; + + case ArchSpec::kCore_arm_any: + if (core2 >= ArchSpec::kCore_arm_first && core2 <= ArchSpec::kCore_arm_last) + return true; + if (core2 >= ArchSpec::kCore_thumb_first && core2 <= ArchSpec::kCore_thumb_last) + return true; + if (core2 == ArchSpec::kCore_arm_any) + return true; + break; + + case ArchSpec::kCore_x86_32_any: + if ((core2 >= ArchSpec::kCore_x86_32_first && core2 <= ArchSpec::kCore_x86_32_last) || (core2 == ArchSpec::kCore_x86_32_any)) + return true; + break; + + case ArchSpec::kCore_ppc_any: + if ((core2 >= ArchSpec::kCore_ppc_first && core2 <= ArchSpec::kCore_ppc_last) || (core2 == ArchSpec::kCore_ppc_any)) + return true; + break; + + case ArchSpec::kCore_ppc64_any: + if ((core2 >= ArchSpec::kCore_ppc64_first && core2 <= ArchSpec::kCore_ppc64_last) || (core2 == ArchSpec::kCore_ppc64_any)) + return true; + break; + + case ArchSpec::eCore_arm_armv7m: + case ArchSpec::eCore_arm_armv7em: + case ArchSpec::eCore_arm_armv7f: + case ArchSpec::eCore_arm_armv7k: + case ArchSpec::eCore_arm_armv7s: + if (!enforce_exact_match) + { + try_inverse = false; + if (core2 == ArchSpec::eCore_arm_armv7) + return true; + } + break; + + default: + break; + } + if (try_inverse) + return cores_match (core2, core1, false, enforce_exact_match); + return false; +} + +bool +lldb_private::operator<(const ArchSpec& lhs, const ArchSpec& rhs) +{ + const ArchSpec::Core lhs_core = lhs.GetCore (); + const ArchSpec::Core rhs_core = rhs.GetCore (); + return lhs_core < rhs_core; +} diff --git a/contrib/llvm/tools/lldb/source/Core/Baton.cpp b/contrib/llvm/tools/lldb/source/Core/Baton.cpp new file mode 100644 index 00000000000..8bed01be883 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Core/Baton.cpp @@ -0,0 +1,24 @@ +//===-- Baton.cpp -----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Core/Baton.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/Stream.h" + +using namespace lldb; +using namespace lldb_private; + +void +Baton::GetDescription (Stream *s, lldb::DescriptionLevel level) const +{ +} diff --git a/contrib/llvm/tools/lldb/source/Core/Broadcaster.cpp b/contrib/llvm/tools/lldb/source/Core/Broadcaster.cpp new file mode 100644 index 00000000000..5af7497c8da --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Core/Broadcaster.cpp @@ -0,0 +1,499 @@ +//===-- Broadcaster.cpp -----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Core/Broadcaster.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/Log.h" +#include "lldb/Core/Event.h" +#include "lldb/Core/StreamString.h" +#include "lldb/lldb-private-log.h" + +using namespace lldb; +using namespace lldb_private; + +Broadcaster::Broadcaster (BroadcasterManager *manager, const char *name) : + m_broadcaster_name (name), + m_listeners (), + m_listeners_mutex (Mutex::eMutexTypeRecursive), + m_hijacking_listeners(), + m_hijacking_masks(), + m_manager (manager) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_OBJECT)); + if (log) + log->Printf ("%p Broadcaster::Broadcaster(\"%s\")", this, m_broadcaster_name.AsCString()); + +} + +Broadcaster::~Broadcaster() +{ + Log *log (lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_OBJECT)); + if (log) + log->Printf ("%p Broadcaster::~Broadcaster(\"%s\")", this, m_broadcaster_name.AsCString()); + + Clear(); +} + +void +Broadcaster::CheckInWithManager () +{ + if (m_manager != NULL) + { + m_manager->SignUpListenersForBroadcaster(*this); + } +} + +void +Broadcaster::Clear() +{ + Mutex::Locker listeners_locker(m_listeners_mutex); + + // Make sure the listener forgets about this broadcaster. We do + // this in the broadcaster in case the broadcaster object initiates + // the removal. + + collection::iterator pos, end = m_listeners.end(); + for (pos = m_listeners.begin(); pos != end; ++pos) + pos->first->BroadcasterWillDestruct (this); + + m_listeners.clear(); +} +const ConstString & +Broadcaster::GetBroadcasterName () +{ + return m_broadcaster_name; +} + +bool +Broadcaster::GetEventNames (Stream &s, uint32_t event_mask, bool prefix_with_broadcaster_name) const +{ + uint32_t num_names_added = 0; + if (event_mask && !m_event_names.empty()) + { + event_names_map::const_iterator end = m_event_names.end(); + for (uint32_t bit=1u, mask=event_mask; mask != 0 && bit != 0; bit <<= 1, mask >>= 1) + { + if (mask & 1) + { + event_names_map::const_iterator pos = m_event_names.find(bit); + if (pos != end) + { + if (num_names_added > 0) + s.PutCString(", "); + + if (prefix_with_broadcaster_name) + { + s.PutCString (m_broadcaster_name.GetCString()); + s.PutChar('.'); + } + s.PutCString(pos->second.c_str()); + ++num_names_added; + } + } + } + } + return num_names_added > 0; +} + +void +Broadcaster::AddInitialEventsToListener (Listener *listener, uint32_t requested_events) +{ + +} + +uint32_t +Broadcaster::AddListener (Listener* listener, uint32_t event_mask) +{ + if (listener == NULL) + return 0; + + Mutex::Locker locker(m_listeners_mutex); + collection::iterator pos, end = m_listeners.end(); + + collection::iterator existing_pos = end; + // See if we already have this listener, and if so, update its mask + uint32_t taken_event_types = 0; + for (pos = m_listeners.begin(); pos != end; ++pos) + { + if (pos->first == listener) + existing_pos = pos; + // For now don't descriminate on who gets what + // FIXME: Implement "unique listener for this bit" mask + // taken_event_types |= pos->second; + } + + // Each event bit in a Broadcaster object can only be used + // by one listener + uint32_t available_event_types = ~taken_event_types & event_mask; + + if (available_event_types) + { + // If we didn't find our listener, add it + if (existing_pos == end) + { + // Grant a new listener the available event bits + m_listeners.push_back(std::make_pair(listener, available_event_types)); + } + else + { + // Grant the existing listener the available event bits + existing_pos->second |= available_event_types; + } + + // Individual broadcasters decide whether they have outstanding data when a + // listener attaches, and insert it into the listener with this method. + + AddInitialEventsToListener (listener, available_event_types); + } + + // Return the event bits that were granted to the listener + return available_event_types; +} + +bool +Broadcaster::EventTypeHasListeners (uint32_t event_type) +{ + Mutex::Locker locker (m_listeners_mutex); + + if (m_hijacking_listeners.size() > 0 && event_type & m_hijacking_masks.back()) + return true; + + if (m_listeners.empty()) + return false; + + collection::iterator pos, end = m_listeners.end(); + for (pos = m_listeners.begin(); pos != end; ++pos) + { + if (pos->second & event_type) + return true; + } + return false; +} + +bool +Broadcaster::RemoveListener (Listener* listener, uint32_t event_mask) +{ + Mutex::Locker locker(m_listeners_mutex); + collection::iterator pos, end = m_listeners.end(); + // See if we already have this listener, and if so, update its mask + for (pos = m_listeners.begin(); pos != end; ++pos) + { + if (pos->first == listener) + { + // Relinquish all event bits in "event_mask" + pos->second &= ~event_mask; + // If all bits have been relinquished then remove this listener + if (pos->second == 0) + m_listeners.erase (pos); + return true; + } + } + return false; +} + +void +Broadcaster::BroadcastEvent (EventSP &event_sp) +{ + return PrivateBroadcastEvent (event_sp, false); +} + +void +Broadcaster::BroadcastEventIfUnique (EventSP &event_sp) +{ + return PrivateBroadcastEvent (event_sp, true); +} + +void +Broadcaster::PrivateBroadcastEvent (EventSP &event_sp, bool unique) +{ + // Can't add a NULL event... + if (event_sp.get() == NULL) + return; + + // Update the broadcaster on this event + event_sp->SetBroadcaster (this); + + const uint32_t event_type = event_sp->GetType(); + + Mutex::Locker event_types_locker(m_listeners_mutex); + + Listener *hijacking_listener = NULL; + if (!m_hijacking_listeners.empty()) + { + assert (!m_hijacking_masks.empty()); + hijacking_listener = m_hijacking_listeners.back(); + if ((event_type & m_hijacking_masks.back()) == 0) + hijacking_listener = NULL; + } + + Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_EVENTS)); + if (log) + { + StreamString event_description; + event_sp->Dump (&event_description); + log->Printf ("%p Broadcaster(\"%s\")::BroadcastEvent (event_sp = {%s}, unique =%i) hijack = %p", + this, + m_broadcaster_name.AsCString(""), + event_description.GetData(), + unique, + hijacking_listener); + } + + if (hijacking_listener) + { + if (unique && hijacking_listener->PeekAtNextEventForBroadcasterWithType (this, event_type)) + return; + hijacking_listener->AddEvent (event_sp); + } + else + { + collection::iterator pos, end = m_listeners.end(); + + + // Iterate through all listener/mask pairs + for (pos = m_listeners.begin(); pos != end; ++pos) + { + // If the listener's mask matches any bits that we just set, then + // put the new event on its event queue. + if (event_type & pos->second) + { + if (unique && pos->first->PeekAtNextEventForBroadcasterWithType (this, event_type)) + continue; + pos->first->AddEvent (event_sp); + } + } + } +} + +void +Broadcaster::BroadcastEvent (uint32_t event_type, EventData *event_data) +{ + EventSP event_sp (new Event (event_type, event_data)); + PrivateBroadcastEvent (event_sp, false); +} + +void +Broadcaster::BroadcastEventIfUnique (uint32_t event_type, EventData *event_data) +{ + EventSP event_sp (new Event (event_type, event_data)); + PrivateBroadcastEvent (event_sp, true); +} + +bool +Broadcaster::HijackBroadcaster (Listener *listener, uint32_t event_mask) +{ + Mutex::Locker event_types_locker(m_listeners_mutex); + + Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_EVENTS)); + if (log) + { + log->Printf ("%p Broadcaster(\"%s\")::HijackBroadcaster (listener(\"%s\")=%p)", + this, + m_broadcaster_name.AsCString(""), + listener->m_name.c_str(), + listener); + } + m_hijacking_listeners.push_back(listener); + m_hijacking_masks.push_back(event_mask); + return true; +} + +void +Broadcaster::RestoreBroadcaster () +{ + Mutex::Locker event_types_locker(m_listeners_mutex); + + Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_EVENTS)); + if (log) + { + Listener *listener = m_hijacking_listeners.back(); + log->Printf ("%p Broadcaster(\"%s\")::RestoreBroadcaster (about to pop listener(\"%s\")=%p)", + this, + m_broadcaster_name.AsCString(""), + listener->m_name.c_str(), + listener); + } + m_hijacking_listeners.pop_back(); + m_hijacking_masks.pop_back(); +} + +ConstString & +Broadcaster::GetBroadcasterClass() const +{ + static ConstString class_name ("lldb.anonymous"); + return class_name; +} + +BroadcastEventSpec::BroadcastEventSpec (const BroadcastEventSpec &rhs) : + m_broadcaster_class (rhs.m_broadcaster_class), + m_event_bits (rhs.m_event_bits) +{ +} + +bool +BroadcastEventSpec::operator< (const BroadcastEventSpec &rhs) const +{ + if (GetBroadcasterClass() == rhs.GetBroadcasterClass()) + { + return GetEventBits() < rhs.GetEventBits(); + } + else + { + return GetBroadcasterClass() < rhs.GetBroadcasterClass(); + } +} + +const BroadcastEventSpec & +BroadcastEventSpec::operator= (const BroadcastEventSpec &rhs) +{ + m_broadcaster_class = rhs.m_broadcaster_class; + m_event_bits = rhs.m_event_bits; + return *this; +} + +BroadcasterManager::BroadcasterManager() : + m_manager_mutex(Mutex::eMutexTypeRecursive) +{ + +} + +uint32_t +BroadcasterManager::RegisterListenerForEvents (Listener &listener, BroadcastEventSpec event_spec) +{ + Mutex::Locker locker(m_manager_mutex); + + collection::iterator iter = m_event_map.begin(), end_iter = m_event_map.end(); + uint32_t available_bits = event_spec.GetEventBits(); + + while (iter != end_iter + && (iter = find_if (iter, end_iter, BroadcasterClassMatches(event_spec.GetBroadcasterClass()))) != end_iter) + { + available_bits &= ~((*iter).first.GetEventBits()); + iter++; + } + + if (available_bits != 0) + { + m_event_map.insert (event_listener_key (BroadcastEventSpec (event_spec.GetBroadcasterClass(), available_bits), &listener)); + m_listeners.insert(&listener); + } + + return available_bits; +} + +bool +BroadcasterManager::UnregisterListenerForEvents (Listener &listener, BroadcastEventSpec event_spec) +{ + Mutex::Locker locker(m_manager_mutex); + bool removed_some = false; + + if (m_listeners.erase(&listener) == 0) + return false; + + ListenerMatchesAndSharedBits predicate (event_spec, listener); + std::vector to_be_readded; + uint32_t event_bits_to_remove = event_spec.GetEventBits(); + + // Go through the map and delete the exact matches, and build a list of matches that weren't exact to re-add: + while (1) + { + collection::iterator iter, end_iter = m_event_map.end(); + iter = find_if (m_event_map.begin(), end_iter, predicate); + if (iter == end_iter) + { + break; + } + else + { + uint32_t iter_event_bits = (*iter).first.GetEventBits(); + removed_some = true; + + if (event_bits_to_remove != iter_event_bits) + { + uint32_t new_event_bits = iter_event_bits & ~event_bits_to_remove; + to_be_readded.push_back(BroadcastEventSpec (event_spec.GetBroadcasterClass(), new_event_bits)); + } + m_event_map.erase (iter); + } + } + + // Okay now add back the bits that weren't completely removed: + for (size_t i = 0; i < to_be_readded.size(); i++) + { + m_event_map.insert (event_listener_key (to_be_readded[i], &listener)); + } + + return removed_some; +} + +Listener * +BroadcasterManager::GetListenerForEventSpec (BroadcastEventSpec event_spec) const +{ + Mutex::Locker locker(*(const_cast (&m_manager_mutex))); + + collection::const_iterator iter, end_iter = m_event_map.end(); + iter = find_if (m_event_map.begin(), end_iter, BroadcastEventSpecMatches (event_spec)); + if (iter != end_iter) + return (*iter).second; + else + return NULL; +} + +void +BroadcasterManager::RemoveListener (Listener &listener) +{ + Mutex::Locker locker(m_manager_mutex); + ListenerMatches predicate (listener); + + + if (m_listeners.erase (&listener) == 0) + return; + + while (1) + { + collection::iterator iter, end_iter = m_event_map.end(); + iter = find_if (m_event_map.begin(), end_iter, predicate); + if (iter == end_iter) + break; + else + m_event_map.erase(iter); + } +} + +void +BroadcasterManager::SignUpListenersForBroadcaster (Broadcaster &broadcaster) +{ + Mutex::Locker locker(m_manager_mutex); + + collection::iterator iter = m_event_map.begin(), end_iter = m_event_map.end(); + + while (iter != end_iter + && (iter = find_if (iter, end_iter, BroadcasterClassMatches(broadcaster.GetBroadcasterClass()))) != end_iter) + { + (*iter).second->StartListeningForEvents (&broadcaster, (*iter).first.GetEventBits()); + iter++; + } +} + +void +BroadcasterManager::Clear () +{ + Mutex::Locker locker(m_manager_mutex); + listener_collection::iterator end_iter = m_listeners.end(); + + for (listener_collection::iterator iter = m_listeners.begin(); iter != end_iter; iter++) + (*iter)->BroadcasterManagerWillDestruct(this); + m_listeners.clear(); + m_event_map.clear(); + +} diff --git a/contrib/llvm/tools/lldb/source/Core/Communication.cpp b/contrib/llvm/tools/lldb/source/Core/Communication.cpp new file mode 100644 index 00000000000..7f40e652020 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Core/Communication.cpp @@ -0,0 +1,431 @@ +//===-- Communication.cpp ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private-log.h" +#include "lldb/Core/Communication.h" +#include "lldb/Core/Connection.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Timer.h" +#include "lldb/Core/Event.h" +#include "lldb/Host/Host.h" +#include + +using namespace lldb; +using namespace lldb_private; + +ConstString & +Communication::GetStaticBroadcasterClass () +{ + static ConstString class_name ("lldb.communication"); + return class_name; +} + +//---------------------------------------------------------------------- +// Constructor +//---------------------------------------------------------------------- +Communication::Communication(const char *name) : + Broadcaster (NULL, name), + m_connection_sp (), + m_read_thread (LLDB_INVALID_HOST_THREAD), + m_read_thread_enabled (false), + m_bytes(), + m_bytes_mutex (Mutex::eMutexTypeRecursive), + m_write_mutex (Mutex::eMutexTypeNormal), + m_callback (NULL), + m_callback_baton (NULL), + m_close_on_eof (true) + +{ + lldb_private::LogIfAnyCategoriesSet (LIBLLDB_LOG_OBJECT | LIBLLDB_LOG_COMMUNICATION, + "%p Communication::Communication (name = %s)", + this, name); + + SetEventName (eBroadcastBitDisconnected, "disconnected"); + SetEventName (eBroadcastBitReadThreadGotBytes, "got bytes"); + SetEventName (eBroadcastBitReadThreadDidExit, "read thread did exit"); + SetEventName (eBroadcastBitReadThreadShouldExit, "read thread should exit"); + SetEventName (eBroadcastBitPacketAvailable, "packet available"); + + CheckInWithManager(); +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +Communication::~Communication() +{ + lldb_private::LogIfAnyCategoriesSet (LIBLLDB_LOG_OBJECT | LIBLLDB_LOG_COMMUNICATION, + "%p Communication::~Communication (name = %s)", + this, m_broadcaster_name.AsCString("")); + Clear(); +} + +void +Communication::Clear() +{ + SetReadThreadBytesReceivedCallback (NULL, NULL); + Disconnect (NULL); + StopReadThread (NULL); +} + +ConnectionStatus +Communication::Connect (const char *url, Error *error_ptr) +{ + Clear(); + + lldb_private::LogIfAnyCategoriesSet (LIBLLDB_LOG_COMMUNICATION, "%p Communication::Connect (url = %s)", this, url); + + lldb::ConnectionSP connection_sp (m_connection_sp); + if (connection_sp.get()) + return connection_sp->Connect (url, error_ptr); + if (error_ptr) + error_ptr->SetErrorString("Invalid connection."); + return eConnectionStatusNoConnection; +} + +ConnectionStatus +Communication::Disconnect (Error *error_ptr) +{ + lldb_private::LogIfAnyCategoriesSet (LIBLLDB_LOG_COMMUNICATION, "%p Communication::Disconnect ()", this); + + lldb::ConnectionSP connection_sp (m_connection_sp); + if (connection_sp.get()) + { + ConnectionStatus status = connection_sp->Disconnect (error_ptr); + // We currently don't protect connection_sp with any mutex for + // multi-threaded environments. So lets not nuke our connection class + // without putting some multi-threaded protections in. We also probably + // don't want to pay for the overhead it might cause if every time we + // access the connection we have to take a lock. + // + // This unique pointer will cleanup after itself when this object goes away, + // so there is no need to currently have it destroy itself immediately + // upon disconnnect. + //connection_sp.reset(); + return status; + } + return eConnectionStatusNoConnection; +} + +bool +Communication::IsConnected () const +{ + lldb::ConnectionSP connection_sp (m_connection_sp); + if (connection_sp.get()) + return connection_sp->IsConnected (); + return false; +} + +bool +Communication::HasConnection () const +{ + return m_connection_sp.get() != NULL; +} + +size_t +Communication::Read (void *dst, size_t dst_len, uint32_t timeout_usec, ConnectionStatus &status, Error *error_ptr) +{ + lldb_private::LogIfAnyCategoriesSet (LIBLLDB_LOG_COMMUNICATION, + "%p Communication::Read (dst = %p, dst_len = %" PRIu64 ", timeout = %u usec) connection = %p", + this, + dst, + (uint64_t)dst_len, + timeout_usec, + m_connection_sp.get()); + + if (m_read_thread_enabled) + { + // We have a dedicated read thread that is getting data for us + size_t cached_bytes = GetCachedBytes (dst, dst_len); + if (cached_bytes > 0 || timeout_usec == 0) + { + status = eConnectionStatusSuccess; + return cached_bytes; + } + + if (m_connection_sp.get() == NULL) + { + if (error_ptr) + error_ptr->SetErrorString("Invalid connection."); + status = eConnectionStatusNoConnection; + return 0; + } + // Set the timeout appropriately + TimeValue timeout_time; + if (timeout_usec != UINT32_MAX) + { + timeout_time = TimeValue::Now(); + timeout_time.OffsetWithMicroSeconds (timeout_usec); + } + + Listener listener ("Communication::Read"); + listener.StartListeningForEvents (this, eBroadcastBitReadThreadGotBytes | eBroadcastBitReadThreadDidExit); + EventSP event_sp; + while (listener.WaitForEvent (timeout_time.IsValid() ? &timeout_time : NULL, event_sp)) + { + const uint32_t event_type = event_sp->GetType(); + if (event_type & eBroadcastBitReadThreadGotBytes) + { + return GetCachedBytes (dst, dst_len); + } + + if (event_type & eBroadcastBitReadThreadDidExit) + { + Disconnect (NULL); + break; + } + } + return 0; + } + + // We aren't using a read thread, just read the data synchronously in this + // thread. + lldb::ConnectionSP connection_sp (m_connection_sp); + if (connection_sp.get()) + { + return connection_sp->Read (dst, dst_len, timeout_usec, status, error_ptr); + } + + if (error_ptr) + error_ptr->SetErrorString("Invalid connection."); + status = eConnectionStatusNoConnection; + return 0; +} + + +size_t +Communication::Write (const void *src, size_t src_len, ConnectionStatus &status, Error *error_ptr) +{ + lldb::ConnectionSP connection_sp (m_connection_sp); + + Mutex::Locker locker(m_write_mutex); + lldb_private::LogIfAnyCategoriesSet (LIBLLDB_LOG_COMMUNICATION, + "%p Communication::Write (src = %p, src_len = %" PRIu64 ") connection = %p", + this, + src, + (uint64_t)src_len, + connection_sp.get()); + + if (connection_sp.get()) + return connection_sp->Write (src, src_len, status, error_ptr); + + if (error_ptr) + error_ptr->SetErrorString("Invalid connection."); + status = eConnectionStatusNoConnection; + return 0; +} + + +bool +Communication::StartReadThread (Error *error_ptr) +{ + if (error_ptr) + error_ptr->Clear(); + + if (IS_VALID_LLDB_HOST_THREAD(m_read_thread)) + return true; + + lldb_private::LogIfAnyCategoriesSet (LIBLLDB_LOG_COMMUNICATION, + "%p Communication::StartReadThread ()", this); + + + char thread_name[1024]; + snprintf(thread_name, sizeof(thread_name), "", m_broadcaster_name.AsCString()); + + m_read_thread_enabled = true; + m_read_thread = Host::ThreadCreate (thread_name, Communication::ReadThread, this, error_ptr); + if (!IS_VALID_LLDB_HOST_THREAD(m_read_thread)) + m_read_thread_enabled = false; + return m_read_thread_enabled; +} + +bool +Communication::StopReadThread (Error *error_ptr) +{ + if (!IS_VALID_LLDB_HOST_THREAD(m_read_thread)) + return true; + + lldb_private::LogIfAnyCategoriesSet (LIBLLDB_LOG_COMMUNICATION, + "%p Communication::StopReadThread ()", this); + + m_read_thread_enabled = false; + + BroadcastEvent (eBroadcastBitReadThreadShouldExit, NULL); + + //Host::ThreadCancel (m_read_thread, error_ptr); + + bool status = Host::ThreadJoin (m_read_thread, NULL, error_ptr); + m_read_thread = LLDB_INVALID_HOST_THREAD; + return status; +} + + +size_t +Communication::GetCachedBytes (void *dst, size_t dst_len) +{ + Mutex::Locker locker(m_bytes_mutex); + if (m_bytes.size() > 0) + { + // If DST is NULL and we have a thread, then return the number + // of bytes that are available so the caller can call again + if (dst == NULL) + return m_bytes.size(); + + const size_t len = std::min(dst_len, m_bytes.size()); + + ::memcpy (dst, m_bytes.c_str(), len); + m_bytes.erase(m_bytes.begin(), m_bytes.begin() + len); + + return len; + } + return 0; +} + +void +Communication::AppendBytesToCache (const uint8_t * bytes, size_t len, bool broadcast, ConnectionStatus status) +{ + lldb_private::LogIfAnyCategoriesSet (LIBLLDB_LOG_COMMUNICATION, + "%p Communication::AppendBytesToCache (src = %p, src_len = %" PRIu64 ", broadcast = %i)", + this, bytes, (uint64_t)len, broadcast); + if ((bytes == NULL || len == 0) + && (status != lldb::eConnectionStatusEndOfFile)) + return; + if (m_callback) + { + // If the user registered a callback, then call it and do not broadcast + m_callback (m_callback_baton, bytes, len); + } + else if (bytes != NULL && len > 0) + { + Mutex::Locker locker(m_bytes_mutex); + m_bytes.append ((const char *)bytes, len); + if (broadcast) + BroadcastEventIfUnique (eBroadcastBitReadThreadGotBytes); + } +} + +size_t +Communication::ReadFromConnection (void *dst, + size_t dst_len, + uint32_t timeout_usec, + ConnectionStatus &status, + Error *error_ptr) +{ + lldb::ConnectionSP connection_sp (m_connection_sp); + if (connection_sp.get()) + return connection_sp->Read (dst, dst_len, timeout_usec, status, error_ptr); + return 0; +} + +bool +Communication::ReadThreadIsRunning () +{ + return m_read_thread_enabled; +} + +void * +Communication::ReadThread (void *p) +{ + Communication *comm = (Communication *)p; + + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_COMMUNICATION)); + + if (log) + log->Printf ("%p Communication::ReadThread () thread starting...", p); + + uint8_t buf[1024]; + + Error error; + ConnectionStatus status = eConnectionStatusSuccess; + bool done = false; + while (!done && comm->m_read_thread_enabled) + { + size_t bytes_read = comm->ReadFromConnection (buf, sizeof(buf), 5 * TimeValue::MicroSecPerSec, status, &error); + if (bytes_read > 0) + comm->AppendBytesToCache (buf, bytes_read, true, status); + else if ((bytes_read == 0) + && status == eConnectionStatusEndOfFile) + { + if (comm->GetCloseOnEOF ()) + comm->Disconnect (); + comm->AppendBytesToCache (buf, bytes_read, true, status); + } + + switch (status) + { + case eConnectionStatusSuccess: + break; + + case eConnectionStatusEndOfFile: + if (comm->GetCloseOnEOF()) + done = true; + break; + case eConnectionStatusNoConnection: // No connection + case eConnectionStatusLostConnection: // Lost connection while connected to a valid connection + done = true; + // Fall through... + case eConnectionStatusError: // Check GetError() for details + case eConnectionStatusTimedOut: // Request timed out + if (log) + error.LogIfError (log, + "%p Communication::ReadFromConnection () => status = %s", + p, + Communication::ConnectionStatusAsCString (status)); + break; + } + } + log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_COMMUNICATION); + if (log) + log->Printf ("%p Communication::ReadThread () thread exiting...", p); + + // Let clients know that this thread is exiting + comm->BroadcastEvent (eBroadcastBitReadThreadDidExit); + return NULL; +} + +void +Communication::SetReadThreadBytesReceivedCallback +( + ReadThreadBytesReceived callback, + void *callback_baton +) +{ + m_callback = callback; + m_callback_baton = callback_baton; +} + +void +Communication::SetConnection (Connection *connection) +{ + Disconnect (NULL); + StopReadThread(NULL); + m_connection_sp.reset(connection); +} + +const char * +Communication::ConnectionStatusAsCString (lldb::ConnectionStatus status) +{ + switch (status) + { + case eConnectionStatusSuccess: return "success"; + case eConnectionStatusError: return "error"; + case eConnectionStatusTimedOut: return "timed out"; + case eConnectionStatusNoConnection: return "no connection"; + case eConnectionStatusLostConnection: return "lost connection"; + case eConnectionStatusEndOfFile: return "end of file"; + } + + static char unknown_state_string[64]; + snprintf(unknown_state_string, sizeof (unknown_state_string), "ConnectionStatus = %i", status); + return unknown_state_string; +} diff --git a/contrib/llvm/tools/lldb/source/Core/Connection.cpp b/contrib/llvm/tools/lldb/source/Core/Connection.cpp new file mode 100644 index 00000000000..3c9bb8b1b7e --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Core/Connection.cpp @@ -0,0 +1,24 @@ +//===-- Connection.cpp ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/Connection.h" + +using namespace lldb_private; + +Connection::Connection () +{ +} + +Connection::~Connection () +{ +} diff --git a/contrib/llvm/tools/lldb/source/Core/ConnectionFileDescriptor.cpp b/contrib/llvm/tools/lldb/source/Core/ConnectionFileDescriptor.cpp new file mode 100644 index 00000000000..e320bda2fcd --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Core/ConnectionFileDescriptor.cpp @@ -0,0 +1,1528 @@ +//===-- ConnectionFileDescriptor.cpp ----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#if defined(__APPLE__) +// Enable this special support for Apple builds where we can have unlimited +// select bounds. We tried switching to poll() and kqueue and we were panicing +// the kernel, so we have to stick with select for now. +#define _DARWIN_UNLIMITED_SELECT +#endif + +#include "lldb/Core/ConnectionFileDescriptor.h" + +// C Includes +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// C++ Includes +// Other libraries and framework includes +#if defined(__APPLE__) +#include "llvm/ADT/SmallVector.h" +#endif +// Project includes +#include "lldb/lldb-private-log.h" +#include "lldb/Interpreter/Args.h" +#include "lldb/Core/Communication.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/RegularExpression.h" +#include "lldb/Core/Timer.h" + +using namespace lldb; +using namespace lldb_private; + +static bool +DecodeHostAndPort (const char *host_and_port, + std::string &host_str, + std::string &port_str, + int32_t& port, + Error *error_ptr) +{ + static RegularExpression g_regex ("([^:]+):([0-9]+)"); + RegularExpression::Match regex_match(2); + if (g_regex.Execute (host_and_port, ®ex_match)) + { + if (regex_match.GetMatchAtIndex (host_and_port, 1, host_str) && + regex_match.GetMatchAtIndex (host_and_port, 2, port_str)) + { + port = Args::StringToSInt32 (port_str.c_str(), INT32_MIN); + if (port != INT32_MIN) + { + if (error_ptr) + error_ptr->Clear(); + return true; + } + } + } + host_str.clear(); + port_str.clear(); + port = INT32_MIN; + if (error_ptr) + error_ptr->SetErrorStringWithFormat("invalid host:port specification: '%s'", host_and_port); + return false; +} + +ConnectionFileDescriptor::ConnectionFileDescriptor () : + Connection(), + m_fd_send (-1), + m_fd_recv (-1), + m_fd_send_type (eFDTypeFile), + m_fd_recv_type (eFDTypeFile), + m_udp_send_sockaddr (), + m_should_close_fd (false), + m_socket_timeout_usec(0), + m_pipe_read(-1), + m_pipe_write(-1), + m_mutex (Mutex::eMutexTypeRecursive), + m_shutting_down (false) +{ + Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_CONNECTION | LIBLLDB_LOG_OBJECT)); + if (log) + log->Printf ("%p ConnectionFileDescriptor::ConnectionFileDescriptor ()", this); +} + +ConnectionFileDescriptor::ConnectionFileDescriptor (int fd, bool owns_fd) : + Connection(), + m_fd_send (fd), + m_fd_recv (fd), + m_fd_send_type (eFDTypeFile), + m_fd_recv_type (eFDTypeFile), + m_udp_send_sockaddr (), + m_should_close_fd (owns_fd), + m_socket_timeout_usec(0), + m_pipe_read(-1), + m_pipe_write(-1), + m_mutex (Mutex::eMutexTypeRecursive), + m_shutting_down (false) +{ + Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_CONNECTION | LIBLLDB_LOG_OBJECT)); + if (log) + log->Printf ("%p ConnectionFileDescriptor::ConnectionFileDescriptor (fd = %i, owns_fd = %i)", this, fd, owns_fd); + OpenCommandPipe (); +} + + +ConnectionFileDescriptor::~ConnectionFileDescriptor () +{ + Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_CONNECTION | LIBLLDB_LOG_OBJECT)); + if (log) + log->Printf ("%p ConnectionFileDescriptor::~ConnectionFileDescriptor ()", this); + Disconnect (NULL); + CloseCommandPipe (); +} + +void +ConnectionFileDescriptor::OpenCommandPipe () +{ + CloseCommandPipe(); + + Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_CONNECTION)); + // Make the command file descriptor here: + int filedes[2]; + int result = pipe (filedes); + if (result != 0) + { + if (log) + log->Printf ("%p ConnectionFileDescriptor::ConnectionFileDescriptor () - could not make pipe: %s", + this, + strerror(errno)); + } + else + { + m_pipe_read = filedes[0]; + m_pipe_write = filedes[1]; + } +} + +void +ConnectionFileDescriptor::CloseCommandPipe () +{ + if (m_pipe_read != -1) + { + close (m_pipe_read); + m_pipe_read = -1; + } + + if (m_pipe_write != -1) + { + close (m_pipe_write); + m_pipe_write = -1; + } +} + +bool +ConnectionFileDescriptor::IsConnected () const +{ + return m_fd_send >= 0 || m_fd_recv >= 0; +} + +ConnectionStatus +ConnectionFileDescriptor::Connect (const char *s, Error *error_ptr) +{ + Mutex::Locker locker (m_mutex); + Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_CONNECTION)); + if (log) + log->Printf ("%p ConnectionFileDescriptor::Connect (url = '%s')", this, s); + + OpenCommandPipe(); + + if (s && s[0]) + { + char *end = NULL; + if (strstr(s, "listen://")) + { + // listen://HOST:PORT + unsigned long listen_port = ::strtoul(s + strlen("listen://"), &end, 0); + return SocketListen (listen_port, error_ptr); + } + else if (strstr(s, "unix-accept://")) + { + // unix://SOCKNAME + return NamedSocketAccept (s + strlen("unix-accept://"), error_ptr); + } + else if (strstr(s, "connect://")) + { + return ConnectTCP (s + strlen("connect://"), error_ptr); + } + else if (strstr(s, "tcp-connect://")) + { + return ConnectTCP (s + strlen("tcp-connect://"), error_ptr); + } + else if (strstr(s, "udp://")) + { + return ConnectUDP (s + strlen("udp://"), error_ptr); + } + else if (strstr(s, "fd://")) + { + // Just passing a native file descriptor within this current process + // that is already opened (possibly from a service or other source). + s += strlen ("fd://"); + bool success = false; + m_fd_send = m_fd_recv = Args::StringToSInt32 (s, -1, 0, &success); + + if (success) + { + // We have what looks to be a valid file descriptor, but we + // should make sure it is. We currently are doing this by trying to + // get the flags from the file descriptor and making sure it + // isn't a bad fd. + errno = 0; + int flags = ::fcntl (m_fd_send, F_GETFL, 0); + if (flags == -1 || errno == EBADF) + { + if (error_ptr) + error_ptr->SetErrorStringWithFormat ("stale file descriptor: %s", s); + m_fd_send = m_fd_recv = -1; + return eConnectionStatusError; + } + else + { + // Try and get a socket option from this file descriptor to + // see if this is a socket and set m_is_socket accordingly. + int resuse; + bool is_socket = GetSocketOption (m_fd_send, SOL_SOCKET, SO_REUSEADDR, resuse) == 0; + if (is_socket) + m_fd_send_type = m_fd_recv_type = eFDTypeSocket; + // Don't take ownership of a file descriptor that gets passed + // to us since someone else opened the file descriptor and + // handed it to us. + // TODO: Since are using a URL to open connection we should + // eventually parse options using the web standard where we + // have "fd://123?opt1=value;opt2=value" and we can have an + // option be "owns=1" or "owns=0" or something like this to + // allow us to specify this. For now, we assume we must + // assume we don't own it. + m_should_close_fd = false; + return eConnectionStatusSuccess; + } + } + + if (error_ptr) + error_ptr->SetErrorStringWithFormat ("invalid file descriptor: \"fd://%s\"", s); + m_fd_send = m_fd_recv = -1; + return eConnectionStatusError; + } + else if (strstr(s, "file://")) + { + // file:///PATH + const char *path = s + strlen("file://"); + do + { + m_fd_send = m_fd_recv = ::open (path, O_RDWR); + } while (m_fd_send == -1 && errno == EINTR); + if (m_fd_send == -1) + { + if (error_ptr) + error_ptr->SetErrorToErrno(); + return eConnectionStatusError; + } + + if (::isatty(m_fd_send)) + { + // Set up serial terminal emulation + struct termios options; + ::tcgetattr (m_fd_send, &options); + + // Set port speed to maximum + ::cfsetospeed (&options, B115200); + ::cfsetispeed (&options, B115200); + + // Raw input, disable echo and signals + options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); + + // Make sure only one character is needed to return from a read + options.c_cc[VMIN] = 1; + options.c_cc[VTIME] = 0; + + ::tcsetattr (m_fd_send, TCSANOW, &options); + } + + int flags = ::fcntl (m_fd_send, F_GETFL, 0); + if (flags >= 0) + { + if ((flags & O_NONBLOCK) == 0) + { + flags |= O_NONBLOCK; + ::fcntl (m_fd_send, F_SETFL, flags); + } + } + m_should_close_fd = true; + return eConnectionStatusSuccess; + } + if (error_ptr) + error_ptr->SetErrorStringWithFormat ("unsupported connection URL: '%s'", s); + return eConnectionStatusError; + } + if (error_ptr) + error_ptr->SetErrorString("invalid connect arguments"); + return eConnectionStatusError; +} + +ConnectionStatus +ConnectionFileDescriptor::Disconnect (Error *error_ptr) +{ + Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_CONNECTION)); + if (log) + log->Printf ("%p ConnectionFileDescriptor::Disconnect ()", this); + + ConnectionStatus status = eConnectionStatusSuccess; + + if (m_fd_send < 0 && m_fd_recv < 0) + { + if (log) + log->Printf ("%p ConnectionFileDescriptor::Disconnect(): Nothing to disconnect", this); + return eConnectionStatusSuccess; + } + + // Try to get the ConnectionFileDescriptor's mutex. If we fail, that is quite likely + // because somebody is doing a blocking read on our file descriptor. If that's the case, + // then send the "q" char to the command file channel so the read will wake up and the connection + // will then know to shut down. + + m_shutting_down = true; + + Mutex::Locker locker; + bool got_lock= locker.TryLock (m_mutex); + + if (!got_lock) + { + if (m_pipe_write != -1 ) + { + write (m_pipe_write, "q", 1); + close (m_pipe_write); + m_pipe_write = -1; + } + locker.Lock (m_mutex); + } + + if (m_should_close_fd == true) + { + if (m_fd_send == m_fd_recv) + { + status = Close (m_fd_send, error_ptr); + } + else + { + // File descriptors are the different, close both if needed + if (m_fd_send >= 0) + status = Close (m_fd_send, error_ptr); + if (m_fd_recv >= 0) + { + ConnectionStatus recv_status = Close (m_fd_recv, error_ptr); + if (status == eConnectionStatusSuccess) + status = recv_status; + } + } + } + + // Now set all our descriptors to invalid values. + + m_fd_send = m_fd_recv = -1; + + if (status != eConnectionStatusSuccess) + { + + return status; + } + + m_shutting_down = false; + return eConnectionStatusSuccess; +} + +size_t +ConnectionFileDescriptor::Read (void *dst, + size_t dst_len, + uint32_t timeout_usec, + ConnectionStatus &status, + Error *error_ptr) +{ + Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_CONNECTION)); + if (log) + log->Printf ("%p ConnectionFileDescriptor::Read () ::read (fd = %i, dst = %p, dst_len = %" PRIu64 ")...", + this, m_fd_recv, dst, (uint64_t)dst_len); + + Mutex::Locker locker; + bool got_lock = locker.TryLock (m_mutex); + if (!got_lock) + { + if (log) + log->Printf ("%p ConnectionFileDescriptor::Read () failed to get the connection lock.", + this); + if (error_ptr) + error_ptr->SetErrorString ("failed to get the connection lock for read."); + + status = eConnectionStatusTimedOut; + return 0; + } + else if (m_shutting_down) + return eConnectionStatusError; + + ssize_t bytes_read = 0; + + status = BytesAvailable (timeout_usec, error_ptr); + if (status == eConnectionStatusSuccess) + { + do + { + bytes_read = ::read (m_fd_recv, dst, dst_len); + } while (bytes_read < 0 && errno == EINTR); + } + + if (status != eConnectionStatusSuccess) + return 0; + + Error error; + if (bytes_read == 0) + { + error.Clear(); // End-of-file. Do not automatically close; pass along for the end-of-file handlers. + status = eConnectionStatusEndOfFile; + } + else if (bytes_read < 0) + { + error.SetErrorToErrno(); + } + else + { + error.Clear(); + } + + if (log) + log->Printf ("%p ConnectionFileDescriptor::Read () ::read (fd = %i, dst = %p, dst_len = %" PRIu64 ") => %" PRIi64 ", error = %s", + this, + m_fd_recv, + dst, + (uint64_t)dst_len, + (int64_t)bytes_read, + error.AsCString()); + + if (error_ptr) + *error_ptr = error; + + if (error.Fail()) + { + uint32_t error_value = error.GetError(); + switch (error_value) + { + case EAGAIN: // The file was marked for non-blocking I/O, and no data were ready to be read. + if (m_fd_recv_type == eFDTypeSocket || m_fd_recv_type == eFDTypeSocketUDP) + status = eConnectionStatusTimedOut; + else + status = eConnectionStatusSuccess; + return 0; + + case EFAULT: // Buf points outside the allocated address space. + case EINTR: // A read from a slow device was interrupted before any data arrived by the delivery of a signal. + case EINVAL: // The pointer associated with fildes was negative. + case EIO: // An I/O error occurred while reading from the file system. + // The process group is orphaned. + // The file is a regular file, nbyte is greater than 0, + // the starting position is before the end-of-file, and + // the starting position is greater than or equal to the + // offset maximum established for the open file + // descriptor associated with fildes. + case EISDIR: // An attempt is made to read a directory. + case ENOBUFS: // An attempt to allocate a memory buffer fails. + case ENOMEM: // Insufficient memory is available. + status = eConnectionStatusError; + break; // Break to close.... + + case ENOENT: // no such file or directory + case EBADF: // fildes is not a valid file or socket descriptor open for reading. + case ENXIO: // An action is requested of a device that does not exist.. + // A requested action cannot be performed by the device. + case ECONNRESET:// The connection is closed by the peer during a read attempt on a socket. + case ENOTCONN: // A read is attempted on an unconnected socket. + status = eConnectionStatusLostConnection; + break; // Break to close.... + + case ETIMEDOUT: // A transmission timeout occurs during a read attempt on a socket. + status = eConnectionStatusTimedOut; + return 0; + } + + return 0; + } + return bytes_read; +} + +size_t +ConnectionFileDescriptor::Write (const void *src, size_t src_len, ConnectionStatus &status, Error *error_ptr) +{ + Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_CONNECTION)); + if (log) + log->Printf ("%p ConnectionFileDescriptor::Write (src = %p, src_len = %" PRIu64 ")", this, src, (uint64_t)src_len); + + if (!IsConnected ()) + { + if (error_ptr) + error_ptr->SetErrorString("not connected"); + status = eConnectionStatusNoConnection; + return 0; + } + + + Error error; + + ssize_t bytes_sent = 0; + + switch (m_fd_send_type) + { + case eFDTypeFile: // Other FD requireing read/write + do + { + bytes_sent = ::write (m_fd_send, src, src_len); + } while (bytes_sent < 0 && errno == EINTR); + break; + + case eFDTypeSocket: // Socket requiring send/recv + do + { + bytes_sent = ::send (m_fd_send, src, src_len, 0); + } while (bytes_sent < 0 && errno == EINTR); + break; + + case eFDTypeSocketUDP: // Unconnected UDP socket requiring sendto/recvfrom + assert (m_udp_send_sockaddr.GetFamily() != 0); + do + { + bytes_sent = ::sendto (m_fd_send, + src, + src_len, + 0, + m_udp_send_sockaddr, + m_udp_send_sockaddr.GetLength()); + } while (bytes_sent < 0 && errno == EINTR); + break; + } + + if (bytes_sent < 0) + error.SetErrorToErrno (); + else + error.Clear (); + + if (log) + { + switch (m_fd_send_type) + { + case eFDTypeFile: // Other FD requireing read/write + log->Printf ("%p ConnectionFileDescriptor::Write() ::write (fd = %i, src = %p, src_len = %" PRIu64 ") => %" PRIi64 " (error = %s)", + this, + m_fd_send, + src, + (uint64_t)src_len, + (int64_t)bytes_sent, + error.AsCString()); + break; + + case eFDTypeSocket: // Socket requiring send/recv + log->Printf ("%p ConnectionFileDescriptor::Write() ::send (socket = %i, src = %p, src_len = %" PRIu64 ", flags = 0) => %" PRIi64 " (error = %s)", + this, + m_fd_send, + src, + (uint64_t)src_len, + (int64_t)bytes_sent, + error.AsCString()); + break; + + case eFDTypeSocketUDP: // Unconnected UDP socket requiring sendto/recvfrom + log->Printf ("%p ConnectionFileDescriptor::Write() ::sendto (socket = %i, src = %p, src_len = %" PRIu64 ", flags = 0) => %" PRIi64 " (error = %s)", + this, + m_fd_send, + src, + (uint64_t)src_len, + (int64_t)bytes_sent, + error.AsCString()); + break; + } + } + + if (error_ptr) + *error_ptr = error; + + if (error.Fail()) + { + switch (error.GetError()) + { + case EAGAIN: + case EINTR: + status = eConnectionStatusSuccess; + return 0; + + case ECONNRESET:// The connection is closed by the peer during a read attempt on a socket. + case ENOTCONN: // A read is attempted on an unconnected socket. + status = eConnectionStatusLostConnection; + break; // Break to close.... + + default: + status = eConnectionStatusError; + break; // Break to close.... + } + + return 0; + } + + status = eConnectionStatusSuccess; + return bytes_sent; +} + + + +#if defined(__APPLE__) + +// This ConnectionFileDescriptor::BytesAvailable() uses select(). +// +// PROS: +// - select is consistent across most unix platforms +// - this Apple specific version allows for unlimited fds in the fd_sets by +// setting the _DARWIN_UNLIMITED_SELECT define prior to including the +// required header files. + +// CONS: +// - Darwin only + +ConnectionStatus +ConnectionFileDescriptor::BytesAvailable (uint32_t timeout_usec, Error *error_ptr) +{ + // Don't need to take the mutex here separately since we are only called from Read. If we + // ever get used more generally we will need to lock here as well. + + Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_CONNECTION)); + if (log) + log->Printf("%p ConnectionFileDescriptor::BytesAvailable (timeout_usec = %u)", this, timeout_usec); + struct timeval *tv_ptr; + struct timeval tv; + if (timeout_usec == UINT32_MAX) + { + // Infinite wait... + tv_ptr = NULL; + } + else + { + TimeValue time_value; + time_value.OffsetWithMicroSeconds (timeout_usec); + tv = time_value.GetAsTimeVal(); + tv_ptr = &tv; + } + + // Make a copy of the file descriptors to make sure we don't + // have another thread change these values out from under us + // and cause problems in the loop below where like in FS_SET() + const int data_fd = m_fd_recv; + const int pipe_fd = m_pipe_read; + + if (data_fd >= 0) + { + const bool have_pipe_fd = pipe_fd >= 0; + + while (data_fd == m_fd_recv) + { + const int nfds = std::max(data_fd, pipe_fd) + 1; + llvm::SmallVector read_fds; + read_fds.resize((nfds/FD_SETSIZE) + 1); + for (size_t i=0; iPrintf("%p ConnectionFileDescriptor::BytesAvailable() ::select (nfds=%i, fds={%i, %i}, NULL, NULL, timeout=%p)...", + this, nfds, data_fd, pipe_fd, tv_ptr); + else + log->Printf("%p ConnectionFileDescriptor::BytesAvailable() ::select (nfds=%i, fds={%i}, NULL, NULL, timeout=%p)...", + this, nfds, data_fd, tv_ptr); + } + + const int num_set_fds = ::select (nfds, read_fds.data(), NULL, NULL, tv_ptr); + if (num_set_fds < 0) + error.SetErrorToErrno(); + else + error.Clear(); + + if (log) + { + if (have_pipe_fd) + log->Printf("%p ConnectionFileDescriptor::BytesAvailable() ::select (nfds=%i, fds={%i, %i}, NULL, NULL, timeout=%p) => %d, error = %s", + this, nfds, data_fd, pipe_fd, tv_ptr, num_set_fds, error.AsCString()); + else + log->Printf("%p ConnectionFileDescriptor::BytesAvailable() ::select (nfds=%i, fds={%i}, NULL, NULL, timeout=%p) => %d, error = %s", + this, nfds, data_fd, tv_ptr, num_set_fds, error.AsCString()); + } + + if (error_ptr) + *error_ptr = error; + + if (error.Fail()) + { + switch (error.GetError()) + { + case EBADF: // One of the descriptor sets specified an invalid descriptor. + return eConnectionStatusLostConnection; + + case EINVAL: // The specified time limit is invalid. One of its components is negative or too large. + default: // Other unknown error + return eConnectionStatusError; + + case EAGAIN: // The kernel was (perhaps temporarily) unable to + // allocate the requested number of file descriptors, + // or we have non-blocking IO + case EINTR: // A signal was delivered before the time limit + // expired and before any of the selected events + // occurred. + break; // Lets keep reading to until we timeout + } + } + else if (num_set_fds == 0) + { + return eConnectionStatusTimedOut; + } + else if (num_set_fds > 0) + { + // FD_ISSET is happy to deal with a something larger than + // a single fd_set. + if (FD_ISSET(data_fd, read_fds.data())) + return eConnectionStatusSuccess; + if (have_pipe_fd && FD_ISSET(pipe_fd, read_fds.data())) + { + // We got a command to exit. Read the data from that pipe: + char buffer[16]; + ssize_t bytes_read; + + do + { + bytes_read = ::read (pipe_fd, buffer, sizeof(buffer)); + } while (bytes_read < 0 && errno == EINTR); + assert (bytes_read == 1 && buffer[0] == 'q'); + + if (log) + log->Printf("%p ConnectionFileDescriptor::BytesAvailable() got data: %*s from the command channel.", + this, (int) bytes_read, buffer); + + return eConnectionStatusEndOfFile; + } + } + } + } + + if (error_ptr) + error_ptr->SetErrorString("not connected"); + return eConnectionStatusLostConnection; +} + +#else + +// This ConnectionFileDescriptor::BytesAvailable() uses select(). +// +// PROS: +// - select is consistent across most unix platforms +// CONS: +// - only supports file descriptors up to FD_SETSIZE. This implementation +// will assert if it runs into that hard limit to let users know that +// another ConnectionFileDescriptor::BytesAvailable() should be used +// or a new version of ConnectionFileDescriptor::BytesAvailable() should +// be written for the system that is running into the limitations. MacOSX +// uses kqueues, and there is a poll() based implementation below. + +ConnectionStatus +ConnectionFileDescriptor::BytesAvailable (uint32_t timeout_usec, Error *error_ptr) +{ + // Don't need to take the mutex here separately since we are only called from Read. If we + // ever get used more generally we will need to lock here as well. + + Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_CONNECTION)); + if (log) + log->Printf("%p ConnectionFileDescriptor::BytesAvailable (timeout_usec = %u)", this, timeout_usec); + struct timeval *tv_ptr; + struct timeval tv; + if (timeout_usec == UINT32_MAX) + { + // Infinite wait... + tv_ptr = NULL; + } + else + { + TimeValue time_value; + time_value.OffsetWithMicroSeconds (timeout_usec); + tv = time_value.GetAsTimeVal(); + tv_ptr = &tv; + } + + // Make a copy of the file descriptors to make sure we don't + // have another thread change these values out from under us + // and cause problems in the loop below where like in FS_SET() + const int data_fd = m_fd_recv; + const int pipe_fd = m_pipe_read; + + if (data_fd >= 0) + { + // If this assert fires off on MacOSX, we will need to switch to using + // libdispatch to read from file descriptors because poll() is causing + // kernel panics and if we exceed FD_SETSIZE we will have no choice... + assert (data_fd < FD_SETSIZE); + + const bool have_pipe_fd = pipe_fd >= 0; + + if (have_pipe_fd) + { + assert (pipe_fd < FD_SETSIZE); + } + + while (data_fd == m_fd_recv) + { + fd_set read_fds; + FD_ZERO (&read_fds); + FD_SET (data_fd, &read_fds); + if (have_pipe_fd) + FD_SET (pipe_fd, &read_fds); + + const int nfds = std::max(data_fd, pipe_fd) + 1; + + Error error; + + if (log) + { + if (have_pipe_fd) + log->Printf("%p ConnectionFileDescriptor::BytesAvailable() ::select (nfds=%i, fds={%i, %i}, NULL, NULL, timeout=%p)...", + this, nfds, data_fd, pipe_fd, tv_ptr); + else + log->Printf("%p ConnectionFileDescriptor::BytesAvailable() ::select (nfds=%i, fds={%i}, NULL, NULL, timeout=%p)...", + this, nfds, data_fd, tv_ptr); + } + + const int num_set_fds = ::select (nfds, &read_fds, NULL, NULL, tv_ptr); + if (num_set_fds < 0) + error.SetErrorToErrno(); + else + error.Clear(); + + if (log) + { + if (have_pipe_fd) + log->Printf("%p ConnectionFileDescriptor::BytesAvailable() ::select (nfds=%i, fds={%i, %i}, NULL, NULL, timeout=%p) => %d, error = %s", + this, nfds, data_fd, pipe_fd, tv_ptr, num_set_fds, error.AsCString()); + else + log->Printf("%p ConnectionFileDescriptor::BytesAvailable() ::select (nfds=%i, fds={%i}, NULL, NULL, timeout=%p) => %d, error = %s", + this, nfds, data_fd, tv_ptr, num_set_fds, error.AsCString()); + } + + if (error_ptr) + *error_ptr = error; + + if (error.Fail()) + { + switch (error.GetError()) + { + case EBADF: // One of the descriptor sets specified an invalid descriptor. + return eConnectionStatusLostConnection; + + case EINVAL: // The specified time limit is invalid. One of its components is negative or too large. + default: // Other unknown error + return eConnectionStatusError; + + case EAGAIN: // The kernel was (perhaps temporarily) unable to + // allocate the requested number of file descriptors, + // or we have non-blocking IO + case EINTR: // A signal was delivered before the time limit + // expired and before any of the selected events + // occurred. + break; // Lets keep reading to until we timeout + } + } + else if (num_set_fds == 0) + { + return eConnectionStatusTimedOut; + } + else if (num_set_fds > 0) + { + if (FD_ISSET(data_fd, &read_fds)) + return eConnectionStatusSuccess; + if (have_pipe_fd && FD_ISSET(pipe_fd, &read_fds)) + { + // We got a command to exit. Read the data from that pipe: + char buffer[16]; + ssize_t bytes_read; + + do + { + bytes_read = ::read (pipe_fd, buffer, sizeof(buffer)); + } while (bytes_read < 0 && errno == EINTR); + assert (bytes_read == 1 && buffer[0] == 'q'); + + if (log) + log->Printf("%p ConnectionFileDescriptor::BytesAvailable() got data: %*s from the command channel.", + this, (int) bytes_read, buffer); + + return eConnectionStatusEndOfFile; + } + } + } + } + + if (error_ptr) + error_ptr->SetErrorString("not connected"); + return eConnectionStatusLostConnection; +} + +#endif + +#if 0 +#include + +// This ConnectionFileDescriptor::BytesAvailable() uses poll(). poll() should NOT +// be used on MacOSX as it has all sorts of restrictions on the types of file descriptors +// that it doesn't support. +// +// There may be some systems that properly support poll() that could use this +// implementation. I will let each system opt into this on their own. +// +// PROS: +// - no restrictions on the fd value that is used +// CONS: +// - varies wildly from platform to platform in its implementation restrictions + +ConnectionStatus +ConnectionFileDescriptor::BytesAvailable (uint32_t timeout_usec, Error *error_ptr) +{ + // Don't need to take the mutex here separately since we are only called from Read. If we + // ever get used more generally we will need to lock here as well. + + Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_CONNECTION)); + if (log) + log->Printf("%p ConnectionFileDescriptor::BytesAvailable (timeout_usec = %u)", this, timeout_usec); + int timeout_msec = 0; + if (timeout_usec == UINT32_MAX) + { + // Infinite wait... + timeout_msec = -1; + } + else if (timeout_usec == 0) + { + // Return immediately, don't wait + timeout_msec = 0; + } + else + { + // Convert usec to msec + timeout_msec = (timeout_usec + 999) / 1000; + } + + // Make a copy of the file descriptors to make sure we don't + // have another thread change these values out from under us + // and cause problems in the loop below where like in FS_SET() + const int data_fd = m_fd_recv; + const int pipe_fd = m_pipe_read; + + // Make sure the file descriptor can be used with select as it + // must be in range + if (data_fd >= 0) + { + const bool have_pipe_fd = pipe_fd >= 0; + struct pollfd fds[2] = + { + { data_fd, POLLIN, 0 }, + { pipe_fd, POLLIN, 0 } + }; + const int nfds = have_pipe_fd ? 2 : 1; + Error error; + while (data_fd == m_fd_recv) + { + const int num_set_fds = ::poll (fds, nfds, timeout_msec); + + if (num_set_fds < 0) + error.SetErrorToErrno(); + else + error.Clear(); + + if (error_ptr) + *error_ptr = error; + + if (log) + { + if (have_pipe_fd) + log->Printf("%p ConnectionFileDescriptor::BytesAvailable() ::poll (fds={{%i,POLLIN},{%i,POLLIN}}, nfds=%i, timeout_ms=%i) => %d, error = %s\n", + this, + data_fd, + pipe_fd, + nfds, + timeout_msec, + num_set_fds, + error.AsCString()); + else + log->Printf("%p ConnectionFileDescriptor::BytesAvailable() ::poll (fds={{%i,POLLIN}}, nfds=%i, timeout_ms=%i) => %d, error = %s\n", + this, + data_fd, + nfds, + timeout_msec, + num_set_fds, + error.AsCString()); + } + + if (error.Fail()) + { + switch (error.GetError()) + { + case EBADF: // One of the descriptor sets specified an invalid descriptor. + return eConnectionStatusLostConnection; + + case EINVAL: // The specified time limit is invalid. One of its components is negative or too large. + default: // Other unknown error + return eConnectionStatusError; + + case EAGAIN: // The kernel was (perhaps temporarily) unable to + // allocate the requested number of file descriptors, + // or we have non-blocking IO + case EINTR: // A signal was delivered before the time limit + // expired and before any of the selected events + // occurred. + break; // Lets keep reading to until we timeout + } + } + else if (num_set_fds == 0) + { + return eConnectionStatusTimedOut; + } + else if (num_set_fds > 0) + { + if (fds[0].revents & POLLIN) + return eConnectionStatusSuccess; + if (fds[1].revents & POLLIN) + { + // We got a command to exit. Read the data from that pipe: + char buffer[16]; + ssize_t bytes_read; + + do + { + bytes_read = ::read (pipe_fd, buffer, sizeof(buffer)); + } while (bytes_read < 0 && errno == EINTR); + assert (bytes_read == 1 && buffer[0] == 'q'); + + if (log) + log->Printf("%p ConnectionFileDescriptor::BytesAvailable() got data: %*s from the command channel.", + this, (int) bytes_read, buffer); + + return eConnectionStatusEndOfFile; + } + } + } + } + if (error_ptr) + error_ptr->SetErrorString("not connected"); + return eConnectionStatusLostConnection; +} + +#endif + +ConnectionStatus +ConnectionFileDescriptor::Close (int& fd, Error *error_ptr) +{ + if (error_ptr) + error_ptr->Clear(); + bool success = true; + // Avoid taking a lock if we can + if (fd >= 0) + { + Mutex::Locker locker (m_mutex); + // Check the FD after the lock is taken to ensure only one thread + // can get into the close scope below + if (fd >= 0) + { + Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_CONNECTION)); + if (log) + log->Printf ("%p ConnectionFileDescriptor::Close (fd = %i)", this,fd); + + success = ::close (fd) == 0; + // A reference to a FD was passed in, set it to an invalid value + fd = -1; + if (!success && error_ptr) + { + // Only set the error if we have been asked to since something else + // might have caused us to try and shut down the connection and may + // have already set the error. + error_ptr->SetErrorToErrno(); + } + } + } + if (success) + return eConnectionStatusSuccess; + else + return eConnectionStatusError; +} + +ConnectionStatus +ConnectionFileDescriptor::NamedSocketAccept (const char *socket_name, Error *error_ptr) +{ + ConnectionStatus result = eConnectionStatusError; + struct sockaddr_un saddr_un; + + m_fd_send_type = m_fd_recv_type = eFDTypeSocket; + + int listen_socket = ::socket (AF_UNIX, SOCK_STREAM, 0); + if (listen_socket == -1) + { + if (error_ptr) + error_ptr->SetErrorToErrno(); + return eConnectionStatusError; + } + + saddr_un.sun_family = AF_UNIX; + ::strncpy(saddr_un.sun_path, socket_name, sizeof(saddr_un.sun_path) - 1); + saddr_un.sun_path[sizeof(saddr_un.sun_path) - 1] = '\0'; +#if defined(__APPLE__) || defined(__FreeBSD__) + saddr_un.sun_len = SUN_LEN (&saddr_un); +#endif + + if (::bind (listen_socket, (struct sockaddr *)&saddr_un, SUN_LEN (&saddr_un)) == 0) + { + if (::listen (listen_socket, 5) == 0) + { + m_fd_send = m_fd_recv = ::accept (listen_socket, NULL, 0); + if (m_fd_send > 0) + { + m_should_close_fd = true; + + if (error_ptr) + error_ptr->Clear(); + result = eConnectionStatusSuccess; + } + } + } + + if (result != eConnectionStatusSuccess) + { + if (error_ptr) + error_ptr->SetErrorToErrno(); + } + // We are done with the listen port + Close (listen_socket, NULL); + return result; +} + +ConnectionStatus +ConnectionFileDescriptor::NamedSocketConnect (const char *socket_name, Error *error_ptr) +{ + Disconnect (NULL); + m_fd_send_type = m_fd_recv_type = eFDTypeSocket; + + // Open the socket that was passed in as an option + struct sockaddr_un saddr_un; + m_fd_send = m_fd_recv = ::socket (AF_UNIX, SOCK_STREAM, 0); + if (m_fd_send == -1) + { + if (error_ptr) + error_ptr->SetErrorToErrno(); + return eConnectionStatusError; + } + + saddr_un.sun_family = AF_UNIX; + ::strncpy(saddr_un.sun_path, socket_name, sizeof(saddr_un.sun_path) - 1); + saddr_un.sun_path[sizeof(saddr_un.sun_path) - 1] = '\0'; +#if defined(__APPLE__) || defined(__FreeBSD__) + saddr_un.sun_len = SUN_LEN (&saddr_un); +#endif + + if (::connect (m_fd_send, (struct sockaddr *)&saddr_un, SUN_LEN (&saddr_un)) < 0) + { + if (error_ptr) + error_ptr->SetErrorToErrno(); + Disconnect (NULL); + return eConnectionStatusError; + } + if (error_ptr) + error_ptr->Clear(); + return eConnectionStatusSuccess; +} + +ConnectionStatus +ConnectionFileDescriptor::SocketListen (uint16_t listen_port_num, Error *error_ptr) +{ + Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_CONNECTION)); + if (log) + log->Printf ("%p ConnectionFileDescriptor::SocketListen (port = %i)", this, listen_port_num); + + Disconnect (NULL); + m_fd_send_type = m_fd_recv_type = eFDTypeSocket; + int listen_port = ::socket (AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (listen_port == -1) + { + if (error_ptr) + error_ptr->SetErrorToErrno(); + return eConnectionStatusError; + } + + // enable local address reuse + SetSocketOption (listen_port, SOL_SOCKET, SO_REUSEADDR, 1); + + SocketAddress localhost; + if (localhost.SetToLocalhost (AF_INET, listen_port_num)) + { + int err = ::bind (listen_port, localhost, localhost.GetLength()); + if (err == -1) + { + if (error_ptr) + error_ptr->SetErrorToErrno(); + Close (listen_port, NULL); + return eConnectionStatusError; + } + + err = ::listen (listen_port, 1); + if (err == -1) + { + if (error_ptr) + error_ptr->SetErrorToErrno(); + Close (listen_port, NULL); + return eConnectionStatusError; + } + + m_fd_send = m_fd_recv = ::accept (listen_port, NULL, 0); + if (m_fd_send == -1) + { + if (error_ptr) + error_ptr->SetErrorToErrno(); + Close (listen_port, NULL); + return eConnectionStatusError; + } + } + + // We are done with the listen port + Close (listen_port, NULL); + + m_should_close_fd = true; + + // Keep our TCP packets coming without any delays. + SetSocketOption (m_fd_send, IPPROTO_TCP, TCP_NODELAY, 1); + if (error_ptr) + error_ptr->Clear(); + return eConnectionStatusSuccess; +} + +ConnectionStatus +ConnectionFileDescriptor::ConnectTCP (const char *host_and_port, Error *error_ptr) +{ + Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_CONNECTION)); + if (log) + log->Printf ("%p ConnectionFileDescriptor::ConnectTCP (host/port = %s)", this, host_and_port); + Disconnect (NULL); + + m_fd_send_type = m_fd_recv_type = eFDTypeSocket; + std::string host_str; + std::string port_str; + int32_t port = INT32_MIN; + if (!DecodeHostAndPort (host_and_port, host_str, port_str, port, error_ptr)) + return eConnectionStatusError; + + // Create the socket + m_fd_send = m_fd_recv = ::socket (AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (m_fd_send == -1) + { + if (error_ptr) + error_ptr->SetErrorToErrno(); + return eConnectionStatusError; + } + + m_should_close_fd = true; + + // Enable local address reuse + SetSocketOption (m_fd_send, SOL_SOCKET, SO_REUSEADDR, 1); + + struct sockaddr_in sa; + ::memset (&sa, 0, sizeof (sa)); + sa.sin_family = AF_INET; + sa.sin_port = htons (port); + + int inet_pton_result = ::inet_pton (AF_INET, host_str.c_str(), &sa.sin_addr); + + if (inet_pton_result <= 0) + { + struct hostent *host_entry = gethostbyname (host_str.c_str()); + if (host_entry) + host_str = ::inet_ntoa (*(struct in_addr *)*host_entry->h_addr_list); + inet_pton_result = ::inet_pton (AF_INET, host_str.c_str(), &sa.sin_addr); + if (inet_pton_result <= 0) + { + + if (error_ptr) + { + if (inet_pton_result == -1) + error_ptr->SetErrorToErrno(); + else + error_ptr->SetErrorStringWithFormat("invalid host string: '%s'", host_str.c_str()); + } + Disconnect (NULL); + + return eConnectionStatusError; + } + } + + if (-1 == ::connect (m_fd_send, (const struct sockaddr *)&sa, sizeof(sa))) + { + if (error_ptr) + error_ptr->SetErrorToErrno(); + Disconnect (NULL); + + return eConnectionStatusError; + } + + // Keep our TCP packets coming without any delays. + SetSocketOption (m_fd_send, IPPROTO_TCP, TCP_NODELAY, 1); + if (error_ptr) + error_ptr->Clear(); + return eConnectionStatusSuccess; +} + +ConnectionStatus +ConnectionFileDescriptor::ConnectUDP (const char *host_and_port, Error *error_ptr) +{ + Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_CONNECTION)); + if (log) + log->Printf ("%p ConnectionFileDescriptor::ConnectUDP (host/port = %s)", this, host_and_port); + Disconnect (NULL); + + m_fd_send_type = m_fd_recv_type = eFDTypeSocketUDP; + + std::string host_str; + std::string port_str; + int32_t port = INT32_MIN; + if (!DecodeHostAndPort (host_and_port, host_str, port_str, port, error_ptr)) + return eConnectionStatusError; + + // Setup the receiving end of the UDP connection on this localhost + // on port zero. After we bind to port zero we can read the port. + m_fd_recv = ::socket (AF_INET, SOCK_DGRAM, 0); + if (m_fd_recv == -1) + { + // Socket creation failed... + if (error_ptr) + error_ptr->SetErrorToErrno(); + } + else + { + // Socket was created, now lets bind to the requested port + SocketAddress addr; + addr.SetToLocalhost (AF_INET, 0); + + if (::bind (m_fd_recv, addr, addr.GetLength()) == -1) + { + // Bind failed... + if (error_ptr) + error_ptr->SetErrorToErrno(); + Disconnect (NULL); + } + } + + if (m_fd_recv == -1) + return eConnectionStatusError; + + // At this point we have setup the recieve port, now we need to + // setup the UDP send socket + + struct addrinfo hints; + struct addrinfo *service_info_list = NULL; + + ::memset (&hints, 0, sizeof(hints)); + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_DGRAM; + int err = ::getaddrinfo (host_str.c_str(), port_str.c_str(), &hints, &service_info_list); + if (err != 0) + { + if (error_ptr) + error_ptr->SetErrorStringWithFormat("getaddrinfo(%s, %s, &hints, &info) returned error %i (%s)", + host_str.c_str(), + port_str.c_str(), + err, + gai_strerror(err)); + Disconnect (NULL); + return eConnectionStatusError; + } + + for (struct addrinfo *service_info_ptr = service_info_list; + service_info_ptr != NULL; + service_info_ptr = service_info_ptr->ai_next) + { + m_fd_send = ::socket (service_info_ptr->ai_family, + service_info_ptr->ai_socktype, + service_info_ptr->ai_protocol); + + if (m_fd_send != -1) + { + m_udp_send_sockaddr = service_info_ptr; + break; + } + else + continue; + } + + :: freeaddrinfo (service_info_list); + + if (m_fd_send == -1) + { + Disconnect (NULL); + return eConnectionStatusError; + } + + if (error_ptr) + error_ptr->Clear(); + + m_should_close_fd = true; + return eConnectionStatusSuccess; +} + +#if defined(__MINGW32__) || defined(__MINGW64__) +typedef const char * set_socket_option_arg_type; +typedef char * get_socket_option_arg_type; +#else // #if defined(__MINGW32__) || defined(__MINGW64__) +typedef const void * set_socket_option_arg_type; +typedef void * get_socket_option_arg_type; +#endif // #if defined(__MINGW32__) || defined(__MINGW64__) + +int +ConnectionFileDescriptor::GetSocketOption(int fd, int level, int option_name, int &option_value) +{ + get_socket_option_arg_type option_value_p = static_cast(&option_value); + socklen_t option_value_size = sizeof(int); + return ::getsockopt(fd, level, option_name, option_value_p, &option_value_size); +} + +int +ConnectionFileDescriptor::SetSocketOption(int fd, int level, int option_name, int option_value) +{ + set_socket_option_arg_type option_value_p = static_cast(&option_value); + return ::setsockopt(fd, level, option_name, option_value_p, sizeof(option_value)); +} + +bool +ConnectionFileDescriptor::SetSocketReceiveTimeout (uint32_t timeout_usec) +{ + switch (m_fd_recv_type) + { + case eFDTypeFile: // Other FD requireing read/write + break; + + case eFDTypeSocket: // Socket requiring send/recv + case eFDTypeSocketUDP: // Unconnected UDP socket requiring sendto/recvfrom + { + // Check in case timeout for m_fd has already been set to this value + if (timeout_usec == m_socket_timeout_usec) + return true; + //printf ("ConnectionFileDescriptor::SetSocketReceiveTimeout (timeout_usec = %u)\n", timeout_usec); + + struct timeval timeout; + if (timeout_usec == UINT32_MAX) + { + timeout.tv_sec = 0; + timeout.tv_usec = 0; + } + else if (timeout_usec == 0) + { + // Sending in zero does an infinite timeout, so set this as low + // as we can go to get an effective zero timeout... + timeout.tv_sec = 0; + timeout.tv_usec = 1; + } + else + { + timeout.tv_sec = timeout_usec / TimeValue::MicroSecPerSec; + timeout.tv_usec = timeout_usec % TimeValue::MicroSecPerSec; + } + if (::setsockopt (m_fd_recv, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)) == 0) + { + m_socket_timeout_usec = timeout_usec; + return true; + } + } + } + return false; +} + +in_port_t +ConnectionFileDescriptor::GetSocketPort (int fd) +{ + // We bound to port zero, so we need to figure out which port we actually bound to + SocketAddress sock_addr; + socklen_t sock_addr_len = sock_addr.GetMaxLength (); + if (::getsockname (fd, sock_addr, &sock_addr_len) == 0) + return sock_addr.GetPort (); + + return 0; +} + +// If the read file descriptor is a socket, then return +// the port number that is being used by the socket. +in_port_t +ConnectionFileDescriptor::GetReadPort () const +{ + return ConnectionFileDescriptor::GetSocketPort (m_fd_recv); +} + +// If the write file descriptor is a socket, then return +// the port number that is being used by the socket. +in_port_t +ConnectionFileDescriptor::GetWritePort () const +{ + return ConnectionFileDescriptor::GetSocketPort (m_fd_send); +} + + diff --git a/contrib/llvm/tools/lldb/source/Core/ConnectionMachPort.cpp b/contrib/llvm/tools/lldb/source/Core/ConnectionMachPort.cpp new file mode 100644 index 00000000000..ca818d405a2 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Core/ConnectionMachPort.cpp @@ -0,0 +1,323 @@ +//===-- ConnectionMachPort.cpp ----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +#if defined(__APPLE__) + +#include "lldb/Core/ConnectionMachPort.h" + +// C Includes +#include + +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private-log.h" +#include "lldb/Core/Communication.h" +#include "lldb/Core/Log.h" + +using namespace lldb; +using namespace lldb_private; + +struct MessageType +{ + mach_msg_header_t head; + ConnectionMachPort::PayloadType payload; +}; + + + +ConnectionMachPort::ConnectionMachPort () : + Connection(), + m_task(mach_task_self()), + m_port(MACH_PORT_TYPE_NONE) +{ +} + +ConnectionMachPort::~ConnectionMachPort () +{ + Disconnect (NULL); +} + +bool +ConnectionMachPort::IsConnected () const +{ + return m_port != MACH_PORT_TYPE_NONE; +} + +ConnectionStatus +ConnectionMachPort::Connect (const char *s, Error *error_ptr) +{ + if (IsConnected()) + { + if (error_ptr) + error_ptr->SetErrorString ("already connected"); + return eConnectionStatusError; + } + + if (s == NULL || s[0] == '\0') + { + if (error_ptr) + error_ptr->SetErrorString ("empty connect URL"); + return eConnectionStatusError; + } + + ConnectionStatus status = eConnectionStatusError; + + if (strncmp (s, "bootstrap-checkin://", strlen("bootstrap-checkin://"))) + { + s += strlen("bootstrap-checkin://"); + + if (*s) + { + status = BootstrapCheckIn (s, error_ptr); + } + else + { + if (error_ptr) + error_ptr->SetErrorString ("bootstrap port name is empty"); + } + } + else if (strncmp (s, "bootstrap-lookup://", strlen("bootstrap-lookup://"))) + { + s += strlen("bootstrap-lookup://"); + if (*s) + { + status = BootstrapLookup (s, error_ptr); + } + else + { + if (error_ptr) + error_ptr->SetErrorString ("bootstrap port name is empty"); + } + } + else + { + if (error_ptr) + error_ptr->SetErrorStringWithFormat ("unsupported connection URL: '%s'", s); + } + + + if (status == eConnectionStatusSuccess) + { + if (error_ptr) + error_ptr->Clear(); + } + else + { + Disconnect(NULL); + } + + return status; +} + +ConnectionStatus +ConnectionMachPort::BootstrapCheckIn (const char *port, Error *error_ptr) +{ + mach_port_t bootstrap_port = MACH_PORT_TYPE_NONE; + + /* Getting bootstrap server port */ + kern_return_t kret = task_get_bootstrap_port(mach_task_self(), &bootstrap_port); + if (kret == KERN_SUCCESS) + { + name_t port_name; + int len = snprintf(port_name, sizeof(port_name), "%s", port); + if (len < sizeof(port_name)) + { + kret = ::bootstrap_check_in (bootstrap_port, + port_name, + &m_port); + } + else + { + Disconnect(NULL); + if (error_ptr) + error_ptr->SetErrorString ("bootstrap is too long"); + return eConnectionStatusError; + } + } + + if (kret != KERN_SUCCESS) + { + Disconnect(NULL); + if (error_ptr) + error_ptr->SetError (kret, eErrorTypeMachKernel); + return eConnectionStatusError; + } + return eConnectionStatusSuccess; +} + +lldb::ConnectionStatus +ConnectionMachPort::BootstrapLookup (const char *port, + Error *error_ptr) +{ + name_t port_name; + + if (port && port[0]) + { + if (::snprintf (port_name, sizeof (port_name), "%s", port) >= sizeof (port_name)) + { + if (error_ptr) + error_ptr->SetErrorString ("port netname is too long"); + return eConnectionStatusError; + } + } + else + { + if (error_ptr) + error_ptr->SetErrorString ("empty port netname"); + return eConnectionStatusError; + } + + mach_port_t bootstrap_port = MACH_PORT_TYPE_NONE; + + /* Getting bootstrap server port */ + kern_return_t kret = task_get_bootstrap_port(mach_task_self(), &bootstrap_port); + if (kret == KERN_SUCCESS) + { + kret = ::bootstrap_look_up (bootstrap_port, + port_name, + &m_port); + } + + if (kret != KERN_SUCCESS) + { + if (error_ptr) + error_ptr->SetError (kret, eErrorTypeMachKernel); + return eConnectionStatusError; + } + + return eConnectionStatusSuccess; +} + +ConnectionStatus +ConnectionMachPort::Disconnect (Error *error_ptr) +{ + kern_return_t kret; + + // TODO: verify if we need to netname_check_out for + // either or both + if (m_port != MACH_PORT_TYPE_NONE) + { + kret = ::mach_port_deallocate (m_task, m_port); + if (error_ptr) + error_ptr->SetError (kret, eErrorTypeMachKernel); + m_port = MACH_PORT_TYPE_NONE; + } + + return eConnectionStatusSuccess; +} + +size_t +ConnectionMachPort::Read (void *dst, + size_t dst_len, + uint32_t timeout_usec, + ConnectionStatus &status, + Error *error_ptr) +{ + PayloadType payload; + + kern_return_t kret = Receive (payload); + if (kret == KERN_SUCCESS) + { + memcpy (dst, payload.data, payload.data_length); + status = eConnectionStatusSuccess; + return payload.data_length; + } + + if (error_ptr) + error_ptr->SetError (kret, eErrorTypeMachKernel); + status = eConnectionStatusError; + return 0; +} + +size_t +ConnectionMachPort::Write (const void *src, size_t src_len, ConnectionStatus &status, Error *error_ptr) +{ + PayloadType payload; + payload.command = 0; + payload.data_length = src_len; + const size_t max_payload_size = sizeof(payload.data); + if (src_len > max_payload_size) + payload.data_length = max_payload_size; + memcpy (payload.data, src, payload.data_length); + + if (Send (payload) == KERN_SUCCESS) + { + status = eConnectionStatusSuccess; + return payload.data_length; + } + status = eConnectionStatusError; + return 0; +} + +ConnectionStatus +ConnectionMachPort::BytesAvailable (uint32_t timeout_usec, Error *error_ptr) +{ + return eConnectionStatusLostConnection; +} + +kern_return_t +ConnectionMachPort::Send (const PayloadType &payload) +{ + struct MessageType message; + + /* (i) Form the message : */ + + /* (i.a) Fill the header fields : */ + message.head.msgh_bits = MACH_MSGH_BITS_REMOTE (MACH_MSG_TYPE_MAKE_SEND) | + MACH_MSGH_BITS_OTHER (MACH_MSGH_BITS_COMPLEX); + message.head.msgh_size = sizeof(MessageType); + message.head.msgh_local_port = MACH_PORT_NULL; + message.head.msgh_remote_port = m_port; + + /* (i.b) Explain the message type ( an integer ) */ + // message.type.msgt_name = MACH_MSG_TYPE_INTEGER_32; + // message.type.msgt_size = 32; + // message.type.msgt_number = 1; + // message.type.msgt_inline = TRUE; + // message.type.msgt_longform = FALSE; + // message.type.msgt_deallocate = FALSE; + /* message.type.msgt_unused = 0; */ /* not needed, I think */ + + /* (i.c) Fill the message with the given integer : */ + message.payload = payload; + + /* (ii) Send the message : */ + kern_return_t kret = ::mach_msg (&message.head, + MACH_SEND_MSG, + message.head.msgh_size, + 0, + MACH_PORT_NULL, + MACH_MSG_TIMEOUT_NONE, + MACH_PORT_NULL); + + return kret; +} + +kern_return_t +ConnectionMachPort::Receive (PayloadType &payload) +{ + MessageType message; + message.head.msgh_size = sizeof(MessageType); + + kern_return_t kret = ::mach_msg (&message.head, + MACH_RCV_MSG, + 0, + sizeof(MessageType), + m_port, + MACH_MSG_TIMEOUT_NONE, + MACH_PORT_NULL); + + if (kret == KERN_SUCCESS) + payload = message.payload; + + return kret; +} + + +#endif // #if defined(__APPLE__) diff --git a/contrib/llvm/tools/lldb/source/Core/ConnectionSharedMemory.cpp b/contrib/llvm/tools/lldb/source/Core/ConnectionSharedMemory.cpp new file mode 100644 index 00000000000..625f17a0985 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Core/ConnectionSharedMemory.cpp @@ -0,0 +1,131 @@ +//===-- ConnectionSharedMemory.cpp ----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Core/ConnectionSharedMemory.h" + +// C Includes +#include +#include +#include +#include +#include +#include +#include + +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private-log.h" +#include "lldb/Core/Communication.h" +#include "lldb/Core/Log.h" + +using namespace lldb; +using namespace lldb_private; + +ConnectionSharedMemory::ConnectionSharedMemory () : + Connection(), + m_name(), + m_fd (-1), + m_mmap() +{ +} + +ConnectionSharedMemory::~ConnectionSharedMemory () +{ + Disconnect (NULL); +} + +bool +ConnectionSharedMemory::IsConnected () const +{ + return m_fd >= 0; +} + +ConnectionStatus +ConnectionSharedMemory::Connect (const char *s, Error *error_ptr) +{ +// if (s && s[0]) +// { +// if (strstr(s, "shm-create://")) +// { +// } +// else if (strstr(s, "shm-connect://")) +// { +// } +// if (error_ptr) +// error_ptr->SetErrorStringWithFormat ("unsupported connection URL: '%s'", s); +// return eConnectionStatusError; +// } + if (error_ptr) + error_ptr->SetErrorString("invalid connect arguments"); + return eConnectionStatusError; +} + +ConnectionStatus +ConnectionSharedMemory::Disconnect (Error *error_ptr) +{ + m_mmap.Clear(); + if (!m_name.empty()) + { + shm_unlink (m_name.c_str()); + m_name.clear(); + } + return eConnectionStatusSuccess; +} + +size_t +ConnectionSharedMemory::Read (void *dst, + size_t dst_len, + uint32_t timeout_usec, + ConnectionStatus &status, + Error *error_ptr) +{ + status = eConnectionStatusSuccess; + return 0; +} + +size_t +ConnectionSharedMemory::Write (const void *src, size_t src_len, ConnectionStatus &status, Error *error_ptr) +{ + status = eConnectionStatusSuccess; + return 0; +} + +ConnectionStatus +ConnectionSharedMemory::BytesAvailable (uint32_t timeout_usec, Error *error_ptr) +{ + return eConnectionStatusLostConnection; +} + +ConnectionStatus +ConnectionSharedMemory::Open (bool create, const char *name, size_t size, Error *error_ptr) +{ + if (m_fd != -1) + { + if (error_ptr) + error_ptr->SetErrorString("already open"); + return eConnectionStatusError; + } + + m_name.assign (name); + int oflag = O_RDWR; + if (create) + oflag |= O_CREAT; + m_fd = ::shm_open (m_name.c_str(), oflag, S_IRUSR|S_IWUSR); + + if (create) + ::ftruncate (m_fd, size); + + if (m_mmap.MemoryMapFromFileDescriptor(m_fd, 0, size, true, false) == size) + return eConnectionStatusSuccess; + + Disconnect(NULL); + return eConnectionStatusError; +} + diff --git a/contrib/llvm/tools/lldb/source/Core/ConstString.cpp b/contrib/llvm/tools/lldb/source/Core/ConstString.cpp new file mode 100644 index 00000000000..875169428d2 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Core/ConstString.cpp @@ -0,0 +1,342 @@ +//===-- ConstString.cpp -----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +#include "lldb/Core/ConstString.h" +#include "lldb/Core/Stream.h" +#include "lldb/Host/Mutex.h" +#include "llvm/ADT/StringMap.h" + +using namespace lldb_private; + + +class Pool +{ +public: + typedef const char * StringPoolValueType; + typedef llvm::StringMap StringPool; + typedef llvm::StringMapEntry StringPoolEntryType; + + //------------------------------------------------------------------ + // Default constructor + // + // Initialize the member variables and create the empty string. + //------------------------------------------------------------------ + Pool () : + m_mutex (Mutex::eMutexTypeRecursive), + m_string_map () + { + } + + //------------------------------------------------------------------ + // Destructor + //------------------------------------------------------------------ + ~Pool () + { + } + + + static StringPoolEntryType & + GetStringMapEntryFromKeyData (const char *keyData) + { + char *ptr = const_cast(keyData) - sizeof (StringPoolEntryType); + return *reinterpret_cast(ptr); + } + + size_t + GetConstCStringLength (const char *ccstr) const + { + if (ccstr) + { + const StringPoolEntryType&entry = GetStringMapEntryFromKeyData (ccstr); + return entry.getKey().size(); + } + return 0; + } + + StringPoolValueType + GetMangledCounterpart (const char *ccstr) const + { + if (ccstr) + return GetStringMapEntryFromKeyData (ccstr).getValue(); + return 0; + } + + bool + SetMangledCounterparts (const char *key_ccstr, const char *value_ccstr) + { + if (key_ccstr && value_ccstr) + { + GetStringMapEntryFromKeyData (key_ccstr).setValue(value_ccstr); + GetStringMapEntryFromKeyData (value_ccstr).setValue(key_ccstr); + return true; + } + return false; + } + + const char * + GetConstCString (const char *cstr) + { + if (cstr) + return GetConstCStringWithLength (cstr, strlen (cstr)); + return NULL; + } + + const char * + GetConstCStringWithLength (const char *cstr, size_t cstr_len) + { + if (cstr) + { + Mutex::Locker locker (m_mutex); + llvm::StringRef string_ref (cstr, cstr_len); + StringPoolEntryType& entry = m_string_map.GetOrCreateValue (string_ref, (StringPoolValueType)NULL); + return entry.getKeyData(); + } + return NULL; + } + + const char * + GetConstCStringWithStringRef (const llvm::StringRef &string_ref) + { + if (string_ref.data()) + { + Mutex::Locker locker (m_mutex); + StringPoolEntryType& entry = m_string_map.GetOrCreateValue (string_ref, (StringPoolValueType)NULL); + return entry.getKeyData(); + } + return NULL; + } + + const char * + GetConstCStringAndSetMangledCounterPart (const char *demangled_cstr, const char *mangled_ccstr) + { + if (demangled_cstr) + { + Mutex::Locker locker (m_mutex); + // Make string pool entry with the mangled counterpart already set + StringPoolEntryType& entry = m_string_map.GetOrCreateValue (llvm::StringRef (demangled_cstr), mangled_ccstr); + + // Extract the const version of the demangled_cstr + const char *demangled_ccstr = entry.getKeyData(); + // Now assign the demangled const string as the counterpart of the + // mangled const string... + GetStringMapEntryFromKeyData (mangled_ccstr).setValue(demangled_ccstr); + // Return the constant demangled C string + return demangled_ccstr; + } + return NULL; + } + + const char * + GetConstTrimmedCStringWithLength (const char *cstr, size_t cstr_len) + { + if (cstr) + { + const size_t trimmed_len = std::min (strlen (cstr), cstr_len); + return GetConstCStringWithLength (cstr, trimmed_len); + } + return NULL; + } + + //------------------------------------------------------------------ + // Return the size in bytes that this object and any items in its + // collection of uniqued strings + data count values takes in + // memory. + //------------------------------------------------------------------ + size_t + MemorySize() const + { + Mutex::Locker locker (m_mutex); + size_t mem_size = sizeof(Pool); + const_iterator end = m_string_map.end(); + for (const_iterator pos = m_string_map.begin(); pos != end; ++pos) + { + mem_size += sizeof(StringPoolEntryType) + pos->getKey().size(); + } + return mem_size; + } + +protected: + //------------------------------------------------------------------ + // Typedefs + //------------------------------------------------------------------ + typedef StringPool::iterator iterator; + typedef StringPool::const_iterator const_iterator; + + //------------------------------------------------------------------ + // Member variables + //------------------------------------------------------------------ + mutable Mutex m_mutex; + StringPool m_string_map; +}; + +//---------------------------------------------------------------------- +// Frameworks and dylibs aren't supposed to have global C++ +// initializers so we hide the string pool in a static function so +// that it will get initialized on the first call to this static +// function. +// +// Note, for now we make the string pool a pointer to the pool, because +// we can't guarantee that some objects won't get destroyed after the +// global destructor chain is run, and trying to make sure no destructors +// touch ConstStrings is difficult. So we leak the pool instead. +// +// FIXME: If we are going to keep it this way we should come up with some +// abstraction to "pthread_once" so we don't have to check the pointer +// every time. +//---------------------------------------------------------------------- +static Pool & +StringPool() +{ + static Mutex g_pool_initialization_mutex; + static Pool *g_string_pool = NULL; + + if (g_string_pool == NULL) + { + Mutex::Locker initialization_locker(g_pool_initialization_mutex); + if (g_string_pool == NULL) + { + g_string_pool = new Pool(); + } + } + + return *g_string_pool; +} + +ConstString::ConstString (const char *cstr) : + m_string (StringPool().GetConstCString (cstr)) +{ +} + +ConstString::ConstString (const char *cstr, size_t cstr_len) : + m_string (StringPool().GetConstCStringWithLength (cstr, cstr_len)) +{ +} + +ConstString::ConstString (const llvm::StringRef &s) : + m_string (StringPool().GetConstCStringWithLength (s.data(), s.size())) +{ +} + +bool +ConstString::operator < (const ConstString& rhs) const +{ + if (m_string == rhs.m_string) + return false; + + llvm::StringRef lhs_string_ref (m_string, StringPool().GetConstCStringLength (m_string)); + llvm::StringRef rhs_string_ref (rhs.m_string, StringPool().GetConstCStringLength (rhs.m_string)); + + // If both have valid C strings, then return the comparison + if (lhs_string_ref.data() && rhs_string_ref.data()) + return lhs_string_ref < rhs_string_ref; + + // Else one of them was NULL, so if LHS is NULL then it is less than + return lhs_string_ref.data() == NULL; +} + +Stream& +lldb_private::operator << (Stream& s, const ConstString& str) +{ + const char *cstr = str.GetCString(); + if (cstr) + s << cstr; + + return s; +} + +size_t +ConstString::GetLength () const +{ + return StringPool().GetConstCStringLength (m_string); +} + +int +ConstString::Compare (const ConstString& lhs, const ConstString& rhs) +{ + // If the iterators are the same, this is the same string + register const char *lhs_cstr = lhs.m_string; + register const char *rhs_cstr = rhs.m_string; + if (lhs_cstr == rhs_cstr) + return 0; + if (lhs_cstr && rhs_cstr) + { + llvm::StringRef lhs_string_ref (lhs_cstr, StringPool().GetConstCStringLength (lhs_cstr)); + llvm::StringRef rhs_string_ref (rhs_cstr, StringPool().GetConstCStringLength (rhs_cstr)); + return lhs_string_ref.compare(rhs_string_ref); + } + + if (lhs_cstr) + return +1; // LHS isn't NULL but RHS is + else + return -1; // LHS is NULL but RHS isn't +} + +void +ConstString::Dump(Stream *s, const char *fail_value) const +{ + if (s) + { + const char *cstr = AsCString (fail_value); + if (cstr) + s->PutCString (cstr); + } +} + +void +ConstString::DumpDebug(Stream *s) const +{ + const char *cstr = GetCString (); + size_t cstr_len = GetLength(); + // Only print the parens if we have a non-NULL string + const char *parens = cstr ? "\"" : ""; + s->Printf("%*p: ConstString, string = %s%s%s, length = %" PRIu64, (int)sizeof(void*) * 2, this, parens, cstr, parens, (uint64_t)cstr_len); +} + +void +ConstString::SetCString (const char *cstr) +{ + m_string = StringPool().GetConstCString (cstr); +} + +void +ConstString::SetString (const llvm::StringRef &s) +{ + m_string = StringPool().GetConstCStringWithLength (s.data(), s.size()); +} + +void +ConstString::SetCStringWithMangledCounterpart (const char *demangled, const ConstString &mangled) +{ + m_string = StringPool().GetConstCStringAndSetMangledCounterPart (demangled, mangled.m_string); +} + +bool +ConstString::GetMangledCounterpart (ConstString &counterpart) const +{ + counterpart.m_string = StringPool().GetMangledCounterpart(m_string); + return counterpart; +} + +void +ConstString::SetCStringWithLength (const char *cstr, size_t cstr_len) +{ + m_string = StringPool().GetConstCStringWithLength(cstr, cstr_len); +} + +void +ConstString::SetTrimmedCStringWithLength (const char *cstr, size_t cstr_len) +{ + m_string = StringPool().GetConstTrimmedCStringWithLength (cstr, cstr_len); +} + +size_t +ConstString::StaticMemorySize() +{ + // Get the size of the static string pool + return StringPool().MemorySize(); +} diff --git a/contrib/llvm/tools/lldb/source/Core/DataBufferHeap.cpp b/contrib/llvm/tools/lldb/source/Core/DataBufferHeap.cpp new file mode 100644 index 00000000000..2c8a865b966 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Core/DataBufferHeap.cpp @@ -0,0 +1,111 @@ +//===-- DataBufferHeap.cpp --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Core/DataBufferHeap.h" + +using namespace lldb_private; + +//---------------------------------------------------------------------- +// Default constructor +//---------------------------------------------------------------------- +DataBufferHeap::DataBufferHeap () : + m_data() +{ +} + +//---------------------------------------------------------------------- +// Initialize this class with "n" characters and fill the buffer +// with "ch". +//---------------------------------------------------------------------- +DataBufferHeap::DataBufferHeap (lldb::offset_t n, uint8_t ch) : + m_data() +{ + if (n < m_data.max_size()) + m_data.assign (n, ch); +} + +//---------------------------------------------------------------------- +// Initialize this class with a copy of the "n" bytes from the "bytes" +// buffer. +//---------------------------------------------------------------------- +DataBufferHeap::DataBufferHeap (const void *src, lldb::offset_t src_len) : + m_data() +{ + CopyData (src, src_len); +} + +//---------------------------------------------------------------------- +// Virtual destructor since this class inherits from a pure virtual +// base class. +//---------------------------------------------------------------------- +DataBufferHeap::~DataBufferHeap () +{ +} + +//---------------------------------------------------------------------- +// Return a pointer to the bytes owned by this object, or NULL if +// the object contains no bytes. +//---------------------------------------------------------------------- +uint8_t * +DataBufferHeap::GetBytes () +{ + if (m_data.empty()) + return NULL; + return &m_data[0]; +} + +//---------------------------------------------------------------------- +// Return a const pointer to the bytes owned by this object, or NULL +// if the object contains no bytes. +//---------------------------------------------------------------------- +const uint8_t * +DataBufferHeap::GetBytes () const +{ + if (m_data.empty()) + return NULL; + return &m_data[0]; +} + +//---------------------------------------------------------------------- +// Return the number of bytes this object currently contains. +//---------------------------------------------------------------------- +uint64_t +DataBufferHeap::GetByteSize () const +{ + return m_data.size(); +} + + +//---------------------------------------------------------------------- +// Sets the number of bytes that this object should be able to +// contain. This can be used prior to copying data into the buffer. +//---------------------------------------------------------------------- +uint64_t +DataBufferHeap::SetByteSize (uint64_t new_size) +{ + m_data.resize(new_size); + return m_data.size(); +} + +void +DataBufferHeap::CopyData (const void *src, uint64_t src_len) +{ + const uint8_t *src_u8 = (const uint8_t *)src; + if (src && src_len > 0) + m_data.assign (src_u8, src_u8 + src_len); + else + m_data.clear(); +} + +void +DataBufferHeap::Clear() +{ + buffer_t empty; + m_data.swap(empty); +} diff --git a/contrib/llvm/tools/lldb/source/Core/DataBufferMemoryMap.cpp b/contrib/llvm/tools/lldb/source/Core/DataBufferMemoryMap.cpp new file mode 100644 index 00000000000..a4382a0c67e --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Core/DataBufferMemoryMap.cpp @@ -0,0 +1,258 @@ +//===-- DataBufferMemoryMap.cpp ---------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + + +#include +#include +#include +#include +#include + +#include "lldb/Core/DataBufferMemoryMap.h" +#include "lldb/Core/Error.h" +#include "lldb/Host/File.h" +#include "lldb/Host/FileSpec.h" +#include "lldb/Host/Host.h" +#include "lldb/Core/Log.h" +#include "lldb/lldb-private-log.h" + +using namespace lldb; +using namespace lldb_private; + +//---------------------------------------------------------------------- +// Default Constructor +//---------------------------------------------------------------------- +DataBufferMemoryMap::DataBufferMemoryMap() : + m_mmap_addr(NULL), + m_mmap_size(0), + m_data(NULL), + m_size(0) +{ +} + +//---------------------------------------------------------------------- +// Virtual destructor since this class inherits from a pure virtual +// base class. +//---------------------------------------------------------------------- +DataBufferMemoryMap::~DataBufferMemoryMap() +{ + Clear(); +} + +//---------------------------------------------------------------------- +// Return a pointer to the bytes owned by this object, or NULL if +// the object contains no bytes. +//---------------------------------------------------------------------- +uint8_t * +DataBufferMemoryMap::GetBytes() +{ + return m_data; +} + +//---------------------------------------------------------------------- +// Return a const pointer to the bytes owned by this object, or NULL +// if the object contains no bytes. +//---------------------------------------------------------------------- +const uint8_t * +DataBufferMemoryMap::GetBytes() const +{ + return m_data; +} + +//---------------------------------------------------------------------- +// Return the number of bytes this object currently contains. +//---------------------------------------------------------------------- +uint64_t +DataBufferMemoryMap::GetByteSize() const +{ + return m_size; +} + +//---------------------------------------------------------------------- +// Reverts this object to an empty state by unmapping any memory +// that is currently owned. +//---------------------------------------------------------------------- +void +DataBufferMemoryMap::Clear() +{ + if (m_mmap_addr != NULL) + { + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_MMAP)); + if (log) + log->Printf("DataBufferMemoryMap::Clear() m_mmap_addr = %p, m_mmap_size = %zu", m_mmap_addr, m_mmap_size); + ::munmap((void *)m_mmap_addr, m_mmap_size); + m_mmap_addr = NULL; + m_mmap_size = 0; + m_data = NULL; + m_size = 0; + } +} + +//---------------------------------------------------------------------- +// Memory map "length" bytes from "file" starting "offset" +// bytes into the file. If "length" is set to SIZE_MAX, then +// map as many bytes as possible. +// +// Returns the number of bytes mapped starting from the requested +// offset. +//---------------------------------------------------------------------- +size_t +DataBufferMemoryMap::MemoryMapFromFileSpec (const FileSpec* filespec, + lldb::offset_t offset, + lldb::offset_t length, + bool writeable) +{ + if (filespec != NULL) + { + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_MMAP)); + if (log) + { + log->Printf("DataBufferMemoryMap::MemoryMapFromFileSpec(file=\"%s\", offset=0x%" PRIx64 ", length=0x%" PRIx64 ", writeable=%i", + filespec->GetPath().c_str(), + offset, + length, + writeable); + } + char path[PATH_MAX]; + if (filespec->GetPath(path, sizeof(path))) + { + uint32_t options = File::eOpenOptionRead; + if (writeable) + options |= File::eOpenOptionWrite; + + File file; + Error error (file.Open(path, options)); + if (error.Success()) + { + const bool fd_is_file = true; + return MemoryMapFromFileDescriptor (file.GetDescriptor(), offset, length, writeable, fd_is_file); + } + } + } + // We should only get here if there was an error + Clear(); + return 0; +} + + +//---------------------------------------------------------------------- +// The file descriptor FD is assumed to already be opened as read only +// and the STAT structure is assumed to a valid pointer and already +// containing valid data from a call to stat(). +// +// Memory map FILE_LENGTH bytes in FILE starting FILE_OFFSET bytes into +// the file. If FILE_LENGTH is set to SIZE_MAX, then map as many bytes +// as possible. +// +// RETURNS +// Number of bytes mapped starting from the requested offset. +//---------------------------------------------------------------------- +size_t +DataBufferMemoryMap::MemoryMapFromFileDescriptor (int fd, + lldb::offset_t offset, + lldb::offset_t length, + bool writeable, + bool fd_is_file) +{ + Clear(); + if (fd >= 0) + { + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_MMAP|LIBLLDB_LOG_VERBOSE)); + if (log) + { + log->Printf("DataBufferMemoryMap::MemoryMapFromFileSpec(fd=%i, offset=0x%" PRIx64 ", length=0x%" PRIx64 ", writeable=%i, fd_is_file=%i)", + fd, + offset, + length, + writeable, + fd_is_file); + } + struct stat stat; + if (::fstat(fd, &stat) == 0) + { + if (S_ISREG(stat.st_mode) && (stat.st_size > offset)) + { + const size_t max_bytes_available = stat.st_size - offset; + if (length == SIZE_MAX) + { + length = max_bytes_available; + } + else if (length > max_bytes_available) + { + // Cap the length if too much data was requested + length = max_bytes_available; + } + + if (length > 0) + { + int prot = PROT_READ; + if (writeable) + prot |= PROT_WRITE; + + int flags = MAP_PRIVATE; + if (fd_is_file) + flags |= MAP_FILE; + + m_mmap_addr = (uint8_t *)::mmap(NULL, length, prot, flags, fd, offset); + Error error; + + if (m_mmap_addr == (void*)-1) + { + error.SetErrorToErrno (); + if (error.GetError() == EINVAL) + { + // We may still have a shot at memory mapping if we align things correctly + size_t page_offset = offset % Host::GetPageSize(); + if (page_offset != 0) + { + m_mmap_addr = (uint8_t *)::mmap(NULL, length + page_offset, prot, flags, fd, offset - page_offset); + if (m_mmap_addr == (void*)-1) + { + // Failed to map file + m_mmap_addr = NULL; + } + else if (m_mmap_addr != NULL) + { + // We recovered and were able to memory map + // after we aligned things to page boundaries + + // Save the actual mmap'ed size + m_mmap_size = length + page_offset; + // Our data is at an offset into the the mapped data + m_data = m_mmap_addr + page_offset; + // Our pretend size is the size that was requestd + m_size = length; + } + } + } + if (error.GetError() == ENOMEM) + { + error.SetErrorStringWithFormat("could not allocate %" PRId64 " bytes of memory to mmap in file", (uint64_t) length); + } + } + else + { + // We were able to map the requested data in one chunk + // where our mmap and actual data are the same. + m_mmap_size = length; + m_data = m_mmap_addr; + m_size = length; + } + + if (log) + { + log->Printf("DataBufferMemoryMap::MemoryMapFromFileSpec() m_mmap_addr = %p, m_mmap_size = %zu, error = %s", + m_mmap_addr, m_mmap_size, error.AsCString()); + } + } + } + } + } + return GetByteSize (); +} diff --git a/contrib/llvm/tools/lldb/source/Core/DataEncoder.cpp b/contrib/llvm/tools/lldb/source/Core/DataEncoder.cpp new file mode 100644 index 00000000000..92a9104acc3 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Core/DataEncoder.cpp @@ -0,0 +1,335 @@ +//===-- DataEncoder.cpp ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Core/DataEncoder.h" + +#include +#include + +#include "llvm/Support/MathExtras.h" + +#include "lldb/Core/DataBuffer.h" +#include "lldb/Host/Endian.h" + +using namespace lldb; +using namespace lldb_private; + +static inline void +WriteInt16(const unsigned char* ptr, unsigned offset, uint16_t value) +{ + *(uint16_t *)(ptr + offset) = value; +} +static inline void +WriteInt32 (const unsigned char* ptr, unsigned offset, uint32_t value) +{ + *(uint32_t *)(ptr + offset) = value; +} + +static inline void +WriteInt64(const unsigned char* ptr, unsigned offset, uint64_t value) +{ + *(uint64_t *)(ptr + offset) = value; +} + +static inline void +WriteSwappedInt16(const unsigned char* ptr, unsigned offset, uint16_t value) +{ + *(uint16_t *)(ptr + offset) = llvm::ByteSwap_16(value); +} + +static inline void +WriteSwappedInt32 (const unsigned char* ptr, unsigned offset, uint32_t value) +{ + *(uint32_t *)(ptr + offset) = llvm::ByteSwap_32(value); +} + +static inline void +WriteSwappedInt64(const unsigned char* ptr, unsigned offset, uint64_t value) +{ + *(uint64_t *)(ptr + offset) = llvm::ByteSwap_64(value); +} + +//---------------------------------------------------------------------- +// Default constructor. +//---------------------------------------------------------------------- +DataEncoder::DataEncoder () : + m_start (NULL), + m_end (NULL), + m_byte_order(lldb::endian::InlHostByteOrder()), + m_addr_size (sizeof(void*)), + m_data_sp () +{ +} + +//---------------------------------------------------------------------- +// This constructor allows us to use data that is owned by someone else. +// The data must stay around as long as this object is valid. +//---------------------------------------------------------------------- +DataEncoder::DataEncoder (void* data, uint32_t length, ByteOrder endian, uint8_t addr_size) : + m_start ((uint8_t*)data), + m_end ((uint8_t*)data + length), + m_byte_order(endian), + m_addr_size (addr_size), + m_data_sp () +{ +} + +//---------------------------------------------------------------------- +// Make a shared pointer reference to the shared data in "data_sp" and +// set the endian swapping setting to "swap", and the address size to +// "addr_size". The shared data reference will ensure the data lives +// as long as any DataEncoder objects exist that have a reference to +// this data. +//---------------------------------------------------------------------- +DataEncoder::DataEncoder (const DataBufferSP& data_sp, ByteOrder endian, uint8_t addr_size) : + m_start (NULL), + m_end (NULL), + m_byte_order(endian), + m_addr_size (addr_size), + m_data_sp () +{ + SetData (data_sp); +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +DataEncoder::~DataEncoder () +{ +} + +//------------------------------------------------------------------ +// Clears the object contents back to a default invalid state, and +// release any references to shared data that this object may +// contain. +//------------------------------------------------------------------ +void +DataEncoder::Clear () +{ + m_start = NULL; + m_end = NULL; + m_byte_order = lldb::endian::InlHostByteOrder(); + m_addr_size = sizeof(void*); + m_data_sp.reset(); +} + +//------------------------------------------------------------------ +// If this object contains shared data, this function returns the +// offset into that shared data. Else zero is returned. +//------------------------------------------------------------------ +size_t +DataEncoder::GetSharedDataOffset () const +{ + if (m_start != NULL) + { + const DataBuffer * data = m_data_sp.get(); + if (data != NULL) + { + const uint8_t * data_bytes = data->GetBytes(); + if (data_bytes != NULL) + { + assert(m_start >= data_bytes); + return m_start - data_bytes; + } + } + } + return 0; +} + +//---------------------------------------------------------------------- +// Set the data with which this object will extract from to data +// starting at BYTES and set the length of the data to LENGTH bytes +// long. The data is externally owned must be around at least as +// long as this object points to the data. No copy of the data is +// made, this object just refers to this data and can extract from +// it. If this object refers to any shared data upon entry, the +// reference to that data will be released. Is SWAP is set to true, +// any data extracted will be endian swapped. +//---------------------------------------------------------------------- +uint32_t +DataEncoder::SetData (const void *bytes, uint32_t length, ByteOrder endian) +{ + m_byte_order = endian; + m_data_sp.reset(); + if (bytes == NULL || length == 0) + { + m_start = NULL; + m_end = NULL; + } + else + { + m_start = (uint8_t *)bytes; + m_end = m_start + length; + } + return GetByteSize(); +} + +//---------------------------------------------------------------------- +// Assign the data for this object to be a subrange of the shared +// data in "data_sp" starting "data_offset" bytes into "data_sp" +// and ending "data_length" bytes later. If "data_offset" is not +// a valid offset into "data_sp", then this object will contain no +// bytes. If "data_offset" is within "data_sp" yet "data_length" is +// too large, the length will be capped at the number of bytes +// remaining in "data_sp". A ref counted pointer to the data in +// "data_sp" will be made in this object IF the number of bytes this +// object refers to in greater than zero (if at least one byte was +// available starting at "data_offset") to ensure the data stays +// around as long as it is needed. The address size and endian swap +// settings will remain unchanged from their current settings. +//---------------------------------------------------------------------- +uint32_t +DataEncoder::SetData (const DataBufferSP& data_sp, uint32_t data_offset, uint32_t data_length) +{ + m_start = m_end = NULL; + + if (data_length > 0) + { + m_data_sp = data_sp; + if (data_sp.get()) + { + const size_t data_size = data_sp->GetByteSize(); + if (data_offset < data_size) + { + m_start = data_sp->GetBytes() + data_offset; + const size_t bytes_left = data_size - data_offset; + // Cap the length of we asked for too many + if (data_length <= bytes_left) + m_end = m_start + data_length; // We got all the bytes we wanted + else + m_end = m_start + bytes_left; // Not all the bytes requested were available in the shared data + } + } + } + + uint32_t new_size = GetByteSize(); + + // Don't hold a shared pointer to the data buffer if we don't share + // any valid bytes in the shared buffer. + if (new_size == 0) + m_data_sp.reset(); + + return new_size; +} + +//---------------------------------------------------------------------- +// Extract a single unsigned char from the binary data and update +// the offset pointed to by "offset_ptr". +// +// RETURNS the byte that was extracted, or zero on failure. +//---------------------------------------------------------------------- +uint32_t +DataEncoder::PutU8 (uint32_t offset, uint8_t value) +{ + if (ValidOffset(offset)) + { + m_start[offset] = value; + return offset + 1; + } + return UINT32_MAX; +} + +uint32_t +DataEncoder::PutU16 (uint32_t offset, uint16_t value) +{ + if (ValidOffsetForDataOfSize(offset, sizeof(value))) + { + if (m_byte_order != lldb::endian::InlHostByteOrder()) + WriteSwappedInt16 (m_start, offset, value); + else + WriteInt16 (m_start, offset, value); + + return offset + sizeof (value); + } + return UINT32_MAX; +} + +uint32_t +DataEncoder::PutU32 (uint32_t offset, uint32_t value) +{ + if (ValidOffsetForDataOfSize(offset, sizeof(value))) + { + if (m_byte_order != lldb::endian::InlHostByteOrder()) + WriteSwappedInt32 (m_start, offset, value); + else + WriteInt32 (m_start, offset, value); + + return offset + sizeof (value); + } + return UINT32_MAX; +} + +uint32_t +DataEncoder::PutU64 (uint32_t offset, uint64_t value) +{ + if (ValidOffsetForDataOfSize(offset, sizeof(value))) + { + if (m_byte_order != lldb::endian::InlHostByteOrder()) + WriteSwappedInt64 (m_start, offset, value); + else + WriteInt64 (m_start, offset, value); + + return offset + sizeof (value); + } + return UINT32_MAX; +} + +//---------------------------------------------------------------------- +// Extract a single integer value from the data and update the offset +// pointed to by "offset_ptr". The size of the extracted integer +// is specified by the "byte_size" argument. "byte_size" should have +// a value >= 1 and <= 8 since the return value is only 64 bits +// wide. Any "byte_size" values less than 1 or greater than 8 will +// result in nothing being extracted, and zero being returned. +// +// RETURNS the integer value that was extracted, or zero on failure. +//---------------------------------------------------------------------- +uint32_t +DataEncoder::PutMaxU64 (uint32_t offset, uint32_t byte_size, uint64_t value) +{ + switch (byte_size) + { + case 1: return PutU8 (offset, value); + case 2: return PutU16(offset, value); + case 4: return PutU32(offset, value); + case 8: return PutU64(offset, value); + default: + assert(!"GetMax64 unhandled case!"); + break; + } + return UINT32_MAX; +} + +uint32_t +DataEncoder::PutData (uint32_t offset, const void *src, uint32_t src_len) +{ + if (src == NULL || src_len == 0) + return offset; + + if (ValidOffsetForDataOfSize(offset, src_len)) + { + memcpy (m_start + offset, src, src_len); + return offset + src_len; + } + return UINT32_MAX; +} + +uint32_t +DataEncoder::PutAddress (uint32_t offset, lldb::addr_t addr) +{ + return PutMaxU64 (offset, GetAddressByteSize(), addr); +} + +uint32_t +DataEncoder::PutCString (uint32_t offset, const char *cstr) +{ + if (cstr) + return PutData (offset, cstr, strlen(cstr) + 1); + return UINT32_MAX; +} diff --git a/contrib/llvm/tools/lldb/source/Core/DataExtractor.cpp b/contrib/llvm/tools/lldb/source/Core/DataExtractor.cpp new file mode 100644 index 00000000000..518faeb71ea --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Core/DataExtractor.cpp @@ -0,0 +1,2179 @@ +//===-- DataExtractor.cpp ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include +#include + +#include +#include +#include +#include + +#include "clang/AST/ASTContext.h" + +#include "llvm/ADT/APFloat.h" +#include "llvm/ADT/APInt.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/Support/MathExtras.h" + + +#include "lldb/Core/DataBufferHeap.h" +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/DataBuffer.h" +#include "lldb/Core/Disassembler.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Stream.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Core/UUID.h" +#include "lldb/Core/dwarf.h" +#include "lldb/Host/Endian.h" +#include "lldb/Symbol/ClangASTContext.h" +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/ExecutionContextScope.h" +#include "lldb/Target/Target.h" + +using namespace lldb; +using namespace lldb_private; + +static inline uint16_t +ReadInt16(const unsigned char* ptr, offset_t offset) +{ + return *(uint16_t *)(ptr + offset); +} +static inline uint32_t +ReadInt32 (const unsigned char* ptr, offset_t offset) +{ + return *(uint32_t *)(ptr + offset); +} + +static inline uint64_t +ReadInt64(const unsigned char* ptr, offset_t offset) +{ + return *(uint64_t *)(ptr + offset); +} + +static inline uint16_t +ReadInt16(const void* ptr) +{ + return *(uint16_t *)(ptr); +} +static inline uint32_t +ReadInt32 (const void* ptr) +{ + return *(uint32_t *)(ptr); +} + +static inline uint64_t +ReadInt64(const void* ptr) +{ + return *(uint64_t *)(ptr); +} + +static inline uint16_t +ReadSwapInt16(const unsigned char* ptr, offset_t offset) +{ + return llvm::ByteSwap_16(*(uint16_t *)(ptr + offset)); +} + +static inline uint32_t +ReadSwapInt32 (const unsigned char* ptr, offset_t offset) +{ + return llvm::ByteSwap_32(*(uint32_t *)(ptr + offset)); +} +static inline uint64_t +ReadSwapInt64(const unsigned char* ptr, offset_t offset) +{ + return llvm::ByteSwap_64(*(uint64_t *)(ptr + offset)); +} + +static inline uint16_t +ReadSwapInt16(const void* ptr) +{ + return llvm::ByteSwap_16(*(uint16_t *)(ptr)); +} + +static inline uint32_t +ReadSwapInt32 (const void* ptr) +{ + return llvm::ByteSwap_32(*(uint32_t *)(ptr)); +} +static inline uint64_t +ReadSwapInt64(const void* ptr) +{ + return llvm::ByteSwap_64(*(uint64_t *)(ptr)); +} + +#define NON_PRINTABLE_CHAR '.' +//---------------------------------------------------------------------- +// Default constructor. +//---------------------------------------------------------------------- +DataExtractor::DataExtractor () : + m_start (NULL), + m_end (NULL), + m_byte_order(lldb::endian::InlHostByteOrder()), + m_addr_size (4), + m_data_sp () +{ +} + +//---------------------------------------------------------------------- +// This constructor allows us to use data that is owned by someone else. +// The data must stay around as long as this object is valid. +//---------------------------------------------------------------------- +DataExtractor::DataExtractor (const void* data, offset_t length, ByteOrder endian, uint32_t addr_size) : + m_start ((uint8_t*)data), + m_end ((uint8_t*)data + length), + m_byte_order(endian), + m_addr_size (addr_size), + m_data_sp () +{ +} + +//---------------------------------------------------------------------- +// Make a shared pointer reference to the shared data in "data_sp" and +// set the endian swapping setting to "swap", and the address size to +// "addr_size". The shared data reference will ensure the data lives +// as long as any DataExtractor objects exist that have a reference to +// this data. +//---------------------------------------------------------------------- +DataExtractor::DataExtractor (const DataBufferSP& data_sp, ByteOrder endian, uint32_t addr_size) : + m_start (NULL), + m_end (NULL), + m_byte_order(endian), + m_addr_size (addr_size), + m_data_sp () +{ + SetData (data_sp); +} + +//---------------------------------------------------------------------- +// Initialize this object with a subset of the data bytes in "data". +// If "data" contains shared data, then a reference to this shared +// data will added and the shared data will stay around as long +// as any object contains a reference to that data. The endian +// swap and address size settings are copied from "data". +//---------------------------------------------------------------------- +DataExtractor::DataExtractor (const DataExtractor& data, offset_t offset, offset_t length) : + m_start(NULL), + m_end(NULL), + m_byte_order(data.m_byte_order), + m_addr_size(data.m_addr_size), + m_data_sp() +{ + if (data.ValidOffset(offset)) + { + offset_t bytes_available = data.GetByteSize() - offset; + if (length > bytes_available) + length = bytes_available; + SetData(data, offset, length); + } +} + +DataExtractor::DataExtractor (const DataExtractor& rhs) : + m_start (rhs.m_start), + m_end (rhs.m_end), + m_byte_order (rhs.m_byte_order), + m_addr_size (rhs.m_addr_size), + m_data_sp (rhs.m_data_sp) +{ +} + +//---------------------------------------------------------------------- +// Assignment operator +//---------------------------------------------------------------------- +const DataExtractor& +DataExtractor::operator= (const DataExtractor& rhs) +{ + if (this != &rhs) + { + m_start = rhs.m_start; + m_end = rhs.m_end; + m_byte_order = rhs.m_byte_order; + m_addr_size = rhs.m_addr_size; + m_data_sp = rhs.m_data_sp; + } + return *this; +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +DataExtractor::~DataExtractor () +{ +} + +//------------------------------------------------------------------ +// Clears the object contents back to a default invalid state, and +// release any references to shared data that this object may +// contain. +//------------------------------------------------------------------ +void +DataExtractor::Clear () +{ + m_start = NULL; + m_end = NULL; + m_byte_order = lldb::endian::InlHostByteOrder(); + m_addr_size = 4; + m_data_sp.reset(); +} + +//------------------------------------------------------------------ +// If this object contains shared data, this function returns the +// offset into that shared data. Else zero is returned. +//------------------------------------------------------------------ +size_t +DataExtractor::GetSharedDataOffset () const +{ + if (m_start != NULL) + { + const DataBuffer * data = m_data_sp.get(); + if (data != NULL) + { + const uint8_t * data_bytes = data->GetBytes(); + if (data_bytes != NULL) + { + assert(m_start >= data_bytes); + return m_start - data_bytes; + } + } + } + return 0; +} + +//---------------------------------------------------------------------- +// Set the data with which this object will extract from to data +// starting at BYTES and set the length of the data to LENGTH bytes +// long. The data is externally owned must be around at least as +// long as this object points to the data. No copy of the data is +// made, this object just refers to this data and can extract from +// it. If this object refers to any shared data upon entry, the +// reference to that data will be released. Is SWAP is set to true, +// any data extracted will be endian swapped. +//---------------------------------------------------------------------- +lldb::offset_t +DataExtractor::SetData (const void *bytes, offset_t length, ByteOrder endian) +{ + m_byte_order = endian; + m_data_sp.reset(); + if (bytes == NULL || length == 0) + { + m_start = NULL; + m_end = NULL; + } + else + { + m_start = (uint8_t *)bytes; + m_end = m_start + length; + } + return GetByteSize(); +} + +//---------------------------------------------------------------------- +// Assign the data for this object to be a subrange in "data" +// starting "data_offset" bytes into "data" and ending "data_length" +// bytes later. If "data_offset" is not a valid offset into "data", +// then this object will contain no bytes. If "data_offset" is +// within "data" yet "data_length" is too large, the length will be +// capped at the number of bytes remaining in "data". If "data" +// contains a shared pointer to other data, then a ref counted +// pointer to that data will be made in this object. If "data" +// doesn't contain a shared pointer to data, then the bytes referred +// to in "data" will need to exist at least as long as this object +// refers to those bytes. The address size and endian swap settings +// are copied from the current values in "data". +//---------------------------------------------------------------------- +lldb::offset_t +DataExtractor::SetData (const DataExtractor& data, offset_t data_offset, offset_t data_length) +{ + m_addr_size = data.m_addr_size; + // If "data" contains shared pointer to data, then we can use that + if (data.m_data_sp.get()) + { + m_byte_order = data.m_byte_order; + return SetData(data.m_data_sp, data.GetSharedDataOffset() + data_offset, data_length); + } + + // We have a DataExtractor object that just has a pointer to bytes + if (data.ValidOffset(data_offset)) + { + if (data_length > data.GetByteSize() - data_offset) + data_length = data.GetByteSize() - data_offset; + return SetData (data.GetDataStart() + data_offset, data_length, data.GetByteOrder()); + } + return 0; +} + +//---------------------------------------------------------------------- +// Assign the data for this object to be a subrange of the shared +// data in "data_sp" starting "data_offset" bytes into "data_sp" +// and ending "data_length" bytes later. If "data_offset" is not +// a valid offset into "data_sp", then this object will contain no +// bytes. If "data_offset" is within "data_sp" yet "data_length" is +// too large, the length will be capped at the number of bytes +// remaining in "data_sp". A ref counted pointer to the data in +// "data_sp" will be made in this object IF the number of bytes this +// object refers to in greater than zero (if at least one byte was +// available starting at "data_offset") to ensure the data stays +// around as long as it is needed. The address size and endian swap +// settings will remain unchanged from their current settings. +//---------------------------------------------------------------------- +lldb::offset_t +DataExtractor::SetData (const DataBufferSP& data_sp, offset_t data_offset, offset_t data_length) +{ + m_start = m_end = NULL; + + if (data_length > 0) + { + m_data_sp = data_sp; + if (data_sp.get()) + { + const size_t data_size = data_sp->GetByteSize(); + if (data_offset < data_size) + { + m_start = data_sp->GetBytes() + data_offset; + const size_t bytes_left = data_size - data_offset; + // Cap the length of we asked for too many + if (data_length <= bytes_left) + m_end = m_start + data_length; // We got all the bytes we wanted + else + m_end = m_start + bytes_left; // Not all the bytes requested were available in the shared data + } + } + } + + size_t new_size = GetByteSize(); + + // Don't hold a shared pointer to the data buffer if we don't share + // any valid bytes in the shared buffer. + if (new_size == 0) + m_data_sp.reset(); + + return new_size; +} + +//---------------------------------------------------------------------- +// Extract a single unsigned char from the binary data and update +// the offset pointed to by "offset_ptr". +// +// RETURNS the byte that was extracted, or zero on failure. +//---------------------------------------------------------------------- +uint8_t +DataExtractor::GetU8 (offset_t *offset_ptr) const +{ + const uint8_t *data = (const uint8_t *)GetData (offset_ptr, 1); + if (data) + return *data; + return 0; +} + +//---------------------------------------------------------------------- +// Extract "count" unsigned chars from the binary data and update the +// offset pointed to by "offset_ptr". The extracted data is copied into +// "dst". +// +// RETURNS the non-NULL buffer pointer upon successful extraction of +// all the requested bytes, or NULL when the data is not available in +// the buffer due to being out of bounds, or unsufficient data. +//---------------------------------------------------------------------- +void * +DataExtractor::GetU8 (offset_t *offset_ptr, void *dst, uint32_t count) const +{ + const uint8_t *data = (const uint8_t *)GetData (offset_ptr, count); + if (data) + { + // Copy the data into the buffer + memcpy (dst, data, count); + // Return a non-NULL pointer to the converted data as an indicator of success + return dst; + } + return NULL; +} + +//---------------------------------------------------------------------- +// Extract a single uint16_t from the data and update the offset +// pointed to by "offset_ptr". +// +// RETURNS the uint16_t that was extracted, or zero on failure. +//---------------------------------------------------------------------- +uint16_t +DataExtractor::GetU16 (offset_t *offset_ptr) const +{ + uint16_t val = 0; + const uint8_t *data = (const uint8_t *)GetData (offset_ptr, sizeof(val)); + if (data) + { + if (m_byte_order != lldb::endian::InlHostByteOrder()) + val = ReadSwapInt16(data); + else + val = ReadInt16 (data); + } + return val; +} + +uint16_t +DataExtractor::GetU16_unchecked (offset_t *offset_ptr) const +{ + uint16_t val; + if (m_byte_order == lldb::endian::InlHostByteOrder()) + val = ReadInt16 (m_start, *offset_ptr); + else + val = ReadSwapInt16(m_start, *offset_ptr); + *offset_ptr += sizeof(val); + return val; +} + +uint32_t +DataExtractor::GetU32_unchecked (offset_t *offset_ptr) const +{ + uint32_t val; + if (m_byte_order == lldb::endian::InlHostByteOrder()) + val = ReadInt32 (m_start, *offset_ptr); + else + val = ReadSwapInt32 (m_start, *offset_ptr); + *offset_ptr += sizeof(val); + return val; +} + +uint64_t +DataExtractor::GetU64_unchecked (offset_t *offset_ptr) const +{ + uint64_t val; + if (m_byte_order == lldb::endian::InlHostByteOrder()) + val = ReadInt64 (m_start, *offset_ptr); + else + val = ReadSwapInt64 (m_start, *offset_ptr); + *offset_ptr += sizeof(val); + return val; +} + + +//---------------------------------------------------------------------- +// Extract "count" uint16_t values from the binary data and update +// the offset pointed to by "offset_ptr". The extracted data is +// copied into "dst". +// +// RETURNS the non-NULL buffer pointer upon successful extraction of +// all the requested bytes, or NULL when the data is not available +// in the buffer due to being out of bounds, or unsufficient data. +//---------------------------------------------------------------------- +void * +DataExtractor::GetU16 (offset_t *offset_ptr, void *void_dst, uint32_t count) const +{ + const size_t src_size = sizeof(uint16_t) * count; + const uint16_t *src = (const uint16_t *)GetData (offset_ptr, src_size); + if (src) + { + if (m_byte_order != lldb::endian::InlHostByteOrder()) + { + uint16_t *dst_pos = (uint16_t *)void_dst; + uint16_t *dst_end = dst_pos + count; + const uint16_t *src_pos = src; + while (dst_pos < dst_end) + { + *dst_pos = ReadSwapInt16 (src_pos); + ++dst_pos; + ++src_pos; + } + } + else + { + memcpy (void_dst, src, src_size); + } + // Return a non-NULL pointer to the converted data as an indicator of success + return void_dst; + } + return NULL; +} + +//---------------------------------------------------------------------- +// Extract a single uint32_t from the data and update the offset +// pointed to by "offset_ptr". +// +// RETURNS the uint32_t that was extracted, or zero on failure. +//---------------------------------------------------------------------- +uint32_t +DataExtractor::GetU32 (offset_t *offset_ptr) const +{ + uint32_t val = 0; + const uint32_t *data = (const uint32_t *)GetData (offset_ptr, sizeof(val)); + if (data) + { + if (m_byte_order != lldb::endian::InlHostByteOrder()) + val = ReadSwapInt32 (data); + else + val = *data; + } + return val; +} + +//---------------------------------------------------------------------- +// Extract "count" uint32_t values from the binary data and update +// the offset pointed to by "offset_ptr". The extracted data is +// copied into "dst". +// +// RETURNS the non-NULL buffer pointer upon successful extraction of +// all the requested bytes, or NULL when the data is not available +// in the buffer due to being out of bounds, or unsufficient data. +//---------------------------------------------------------------------- +void * +DataExtractor::GetU32 (offset_t *offset_ptr, void *void_dst, uint32_t count) const +{ + const size_t src_size = sizeof(uint32_t) * count; + const uint32_t *src = (const uint32_t *)GetData (offset_ptr, src_size); + if (src) + { + if (m_byte_order != lldb::endian::InlHostByteOrder()) + { + uint32_t *dst_pos = (uint32_t *)void_dst; + uint32_t *dst_end = dst_pos + count; + const uint32_t *src_pos = src; + while (dst_pos < dst_end) + { + *dst_pos = ReadSwapInt32 (src_pos); + ++dst_pos; + ++src_pos; + } + } + else + { + memcpy (void_dst, src, src_size); + } + // Return a non-NULL pointer to the converted data as an indicator of success + return void_dst; + } + return NULL; +} + +//---------------------------------------------------------------------- +// Extract a single uint64_t from the data and update the offset +// pointed to by "offset_ptr". +// +// RETURNS the uint64_t that was extracted, or zero on failure. +//---------------------------------------------------------------------- +uint64_t +DataExtractor::GetU64 (offset_t *offset_ptr) const +{ + uint64_t val = 0; + const uint64_t *data = (const uint64_t *)GetData (offset_ptr, sizeof(val)); + if (data) + { + if (m_byte_order != lldb::endian::InlHostByteOrder()) + val = ReadSwapInt64 (data); + else + val = *data; + } + return val; +} + +//---------------------------------------------------------------------- +// GetU64 +// +// Get multiple consecutive 64 bit values. Return true if the entire +// read succeeds and increment the offset pointed to by offset_ptr, else +// return false and leave the offset pointed to by offset_ptr unchanged. +//---------------------------------------------------------------------- +void * +DataExtractor::GetU64 (offset_t *offset_ptr, void *void_dst, uint32_t count) const +{ + const size_t src_size = sizeof(uint64_t) * count; + const uint64_t *src = (const uint64_t *)GetData (offset_ptr, src_size); + if (src) + { + if (m_byte_order != lldb::endian::InlHostByteOrder()) + { + uint64_t *dst_pos = (uint64_t *)void_dst; + uint64_t *dst_end = dst_pos + count; + const uint64_t *src_pos = src; + while (dst_pos < dst_end) + { + *dst_pos = ReadSwapInt64 (src_pos); + ++dst_pos; + ++src_pos; + } + } + else + { + memcpy (void_dst, src, src_size); + } + // Return a non-NULL pointer to the converted data as an indicator of success + return void_dst; + } + return NULL; +} + +//---------------------------------------------------------------------- +// Extract a single integer value from the data and update the offset +// pointed to by "offset_ptr". The size of the extracted integer +// is specified by the "byte_size" argument. "byte_size" should have +// a value between 1 and 4 since the return value is only 32 bits +// wide. Any "byte_size" values less than 1 or greater than 4 will +// result in nothing being extracted, and zero being returned. +// +// RETURNS the integer value that was extracted, or zero on failure. +//---------------------------------------------------------------------- +uint32_t +DataExtractor::GetMaxU32 (offset_t *offset_ptr, size_t byte_size) const +{ + switch (byte_size) + { + case 1: return GetU8 (offset_ptr); break; + case 2: return GetU16(offset_ptr); break; + case 4: return GetU32(offset_ptr); break; + default: + assert("GetMaxU32 unhandled case!" == NULL); + break; + } + return 0; +} + +//---------------------------------------------------------------------- +// Extract a single integer value from the data and update the offset +// pointed to by "offset_ptr". The size of the extracted integer +// is specified by the "byte_size" argument. "byte_size" should have +// a value >= 1 and <= 8 since the return value is only 64 bits +// wide. Any "byte_size" values less than 1 or greater than 8 will +// result in nothing being extracted, and zero being returned. +// +// RETURNS the integer value that was extracted, or zero on failure. +//---------------------------------------------------------------------- +uint64_t +DataExtractor::GetMaxU64 (offset_t *offset_ptr, size_t size) const +{ + switch (size) + { + case 1: return GetU8 (offset_ptr); break; + case 2: return GetU16(offset_ptr); break; + case 4: return GetU32(offset_ptr); break; + case 8: return GetU64(offset_ptr); break; + default: + assert("GetMax64 unhandled case!" == NULL); + break; + } + return 0; +} + +uint64_t +DataExtractor::GetMaxU64_unchecked (offset_t *offset_ptr, size_t size) const +{ + switch (size) + { + case 1: return GetU8_unchecked (offset_ptr); break; + case 2: return GetU16_unchecked (offset_ptr); break; + case 4: return GetU32_unchecked (offset_ptr); break; + case 8: return GetU64_unchecked (offset_ptr); break; + default: + assert("GetMax64 unhandled case!" == NULL); + break; + } + return 0; +} + +int64_t +DataExtractor::GetMaxS64 (offset_t *offset_ptr, size_t size) const +{ + switch (size) + { + case 1: return (int8_t)GetU8 (offset_ptr); break; + case 2: return (int16_t)GetU16(offset_ptr); break; + case 4: return (int32_t)GetU32(offset_ptr); break; + case 8: return (int64_t)GetU64(offset_ptr); break; + default: + assert("GetMax64 unhandled case!" == NULL); + break; + } + return 0; +} + +uint64_t +DataExtractor::GetMaxU64Bitfield (offset_t *offset_ptr, size_t size, uint32_t bitfield_bit_size, uint32_t bitfield_bit_offset) const +{ + uint64_t uval64 = GetMaxU64 (offset_ptr, size); + if (bitfield_bit_size > 0) + { + if (bitfield_bit_offset > 0) + uval64 >>= bitfield_bit_offset; + uint64_t bitfield_mask = ((1ul << bitfield_bit_size) - 1); + if (!bitfield_mask && bitfield_bit_offset == 0 && bitfield_bit_size == 64) + return uval64; + uval64 &= bitfield_mask; + } + return uval64; +} + +int64_t +DataExtractor::GetMaxS64Bitfield (offset_t *offset_ptr, size_t size, uint32_t bitfield_bit_size, uint32_t bitfield_bit_offset) const +{ + int64_t sval64 = GetMaxS64 (offset_ptr, size); + if (bitfield_bit_size > 0) + { + if (bitfield_bit_offset > 0) + sval64 >>= bitfield_bit_offset; + uint64_t bitfield_mask = (((uint64_t)1) << bitfield_bit_size) - 1; + sval64 &= bitfield_mask; + // sign extend if needed + if (sval64 & (((uint64_t)1) << (bitfield_bit_size - 1))) + sval64 |= ~bitfield_mask; + } + return sval64; +} + + +float +DataExtractor::GetFloat (offset_t *offset_ptr) const +{ + typedef float float_type; + float_type val = 0.0; + const size_t src_size = sizeof(float_type); + const float_type *src = (const float_type *)GetData (offset_ptr, src_size); + if (src) + { + if (m_byte_order != lldb::endian::InlHostByteOrder()) + { + const uint8_t *src_data = (const uint8_t *)src; + uint8_t *dst_data = (uint8_t *)&val; + for (size_t i=0; im_addr_size" member variable and should be +// set correctly prior to extracting any address values. +// +// RETURNS the address that was extracted, or zero on failure. +//------------------------------------------------------------------ +uint64_t +DataExtractor::GetAddress (offset_t *offset_ptr) const +{ + return GetMaxU64 (offset_ptr, m_addr_size); +} + +uint64_t +DataExtractor::GetAddress_unchecked (offset_t *offset_ptr) const +{ + return GetMaxU64_unchecked (offset_ptr, m_addr_size); +} + +//------------------------------------------------------------------ +// Extract a single pointer from the data and update the offset +// pointed to by "offset_ptr". The size of the extracted pointer +// comes from the "this->m_addr_size" member variable and should be +// set correctly prior to extracting any pointer values. +// +// RETURNS the pointer that was extracted, or zero on failure. +//------------------------------------------------------------------ +uint64_t +DataExtractor::GetPointer (offset_t *offset_ptr) const +{ + return GetMaxU64 (offset_ptr, m_addr_size); +} + +//---------------------------------------------------------------------- +// GetDwarfEHPtr +// +// Used for calls when the value type is specified by a DWARF EH Frame +// pointer encoding. +//---------------------------------------------------------------------- + +uint64_t +DataExtractor::GetGNUEHPointer (offset_t *offset_ptr, uint32_t eh_ptr_enc, lldb::addr_t pc_rel_addr, lldb::addr_t text_addr, lldb::addr_t data_addr)//, BSDRelocs *data_relocs) const +{ + if (eh_ptr_enc == DW_EH_PE_omit) + return ULLONG_MAX; // Value isn't in the buffer... + + uint64_t baseAddress = 0; + uint64_t addressValue = 0; + const uint32_t addr_size = GetAddressByteSize(); + + bool signExtendValue = false; + // Decode the base part or adjust our offset + switch (eh_ptr_enc & 0x70) + { + case DW_EH_PE_pcrel: + signExtendValue = true; + baseAddress = *offset_ptr; + if (pc_rel_addr != LLDB_INVALID_ADDRESS) + baseAddress += pc_rel_addr; +// else +// Log::GlobalWarning ("PC relative pointer encoding found with invalid pc relative address."); + break; + + case DW_EH_PE_textrel: + signExtendValue = true; + if (text_addr != LLDB_INVALID_ADDRESS) + baseAddress = text_addr; +// else +// Log::GlobalWarning ("text relative pointer encoding being decoded with invalid text section address, setting base address to zero."); + break; + + case DW_EH_PE_datarel: + signExtendValue = true; + if (data_addr != LLDB_INVALID_ADDRESS) + baseAddress = data_addr; +// else +// Log::GlobalWarning ("data relative pointer encoding being decoded with invalid data section address, setting base address to zero."); + break; + + case DW_EH_PE_funcrel: + signExtendValue = true; + break; + + case DW_EH_PE_aligned: + { + // SetPointerSize should be called prior to extracting these so the + // pointer size is cached + assert(addr_size != 0); + if (addr_size) + { + // Align to a address size boundary first + uint32_t alignOffset = *offset_ptr % addr_size; + if (alignOffset) + offset_ptr += addr_size - alignOffset; + } + } + break; + + default: + break; + } + + // Decode the value part + switch (eh_ptr_enc & DW_EH_PE_MASK_ENCODING) + { + case DW_EH_PE_absptr : + { + addressValue = GetAddress (offset_ptr); +// if (data_relocs) +// addressValue = data_relocs->Relocate(*offset_ptr - addr_size, *this, addressValue); + } + break; + case DW_EH_PE_uleb128 : addressValue = GetULEB128(offset_ptr); break; + case DW_EH_PE_udata2 : addressValue = GetU16(offset_ptr); break; + case DW_EH_PE_udata4 : addressValue = GetU32(offset_ptr); break; + case DW_EH_PE_udata8 : addressValue = GetU64(offset_ptr); break; + case DW_EH_PE_sleb128 : addressValue = GetSLEB128(offset_ptr); break; + case DW_EH_PE_sdata2 : addressValue = (int16_t)GetU16(offset_ptr); break; + case DW_EH_PE_sdata4 : addressValue = (int32_t)GetU32(offset_ptr); break; + case DW_EH_PE_sdata8 : addressValue = (int64_t)GetU64(offset_ptr); break; + default: + // Unhandled encoding type + assert(eh_ptr_enc); + break; + } + + // Since we promote everything to 64 bit, we may need to sign extend + if (signExtendValue && addr_size < sizeof(baseAddress)) + { + uint64_t sign_bit = 1ull << ((addr_size * 8ull) - 1ull); + if (sign_bit & addressValue) + { + uint64_t mask = ~sign_bit + 1; + addressValue |= mask; + } + } + return baseAddress + addressValue; +} + +size_t +DataExtractor::ExtractBytes (offset_t offset, offset_t length, ByteOrder dst_byte_order, void *dst) const +{ + const uint8_t *src = PeekData (offset, length); + if (src) + { + if (dst_byte_order != GetByteOrder()) + { + for (uint32_t i=0; i 0); + assert (m_byte_order == eByteOrderBig || m_byte_order == eByteOrderLittle); + + // Validate the destination info + assert (dst_void_ptr != NULL); + assert (dst_len > 0); + assert (dst_byte_order == eByteOrderBig || dst_byte_order == eByteOrderLittle); + + // Must have valid byte orders set in this object and for destination + if (!(dst_byte_order == eByteOrderBig || dst_byte_order == eByteOrderLittle) || + !(m_byte_order == eByteOrderBig || m_byte_order == eByteOrderLittle)) + return 0; + + uint32_t i; + uint8_t* dst = (uint8_t*)dst_void_ptr; + const uint8_t* src = (const uint8_t *)PeekData (src_offset, src_len); + if (src) + { + if (dst_len >= src_len) + { + // We are copying the entire value from src into dst. + // Calculate how many, if any, zeroes we need for the most + // significant bytes if "dst_len" is greater than "src_len"... + const size_t num_zeroes = dst_len - src_len; + if (dst_byte_order == eByteOrderBig) + { + // Big endian, so we lead with zeroes... + if (num_zeroes > 0) + ::memset (dst, 0, num_zeroes); + // Then either copy or swap the rest + if (m_byte_order == eByteOrderBig) + { + ::memcpy (dst + num_zeroes, src, src_len); + } + else + { + for (i=0; i 0) + ::memset (dst + src_len, 0, num_zeroes); + } + return src_len; + } + else + { + // We are only copying some of the value from src into dst.. + + if (dst_byte_order == eByteOrderBig) + { + // Big endian dst + if (m_byte_order == eByteOrderBig) + { + // Big endian dst, with big endian src + ::memcpy (dst, src + (src_len - dst_len), dst_len); + } + else + { + // Big endian dst, with little endian src + for (i=0; i= 0x80) + { + result &= 0x7f; + int shift = 7; + while (src < end) + { + uint8_t byte = *src++; + result |= (byte & 0x7f) << shift; + if ((byte & 0x80) == 0) + break; + shift += 7; + } + } + *offset_ptr = src - m_start; + return result; + } + + return 0; +} + +//---------------------------------------------------------------------- +// Extracts an signed LEB128 number from this object's data +// starting at the offset pointed to by "offset_ptr". The offset +// pointed to by "offset_ptr" will be updated with the offset of the +// byte following the last extracted byte. +// +// Returned the extracted integer value. +//---------------------------------------------------------------------- +int64_t +DataExtractor::GetSLEB128 (offset_t *offset_ptr) const +{ + const uint8_t *src = (const uint8_t *)PeekData (*offset_ptr, 1); + if (src == NULL) + return 0; + + const uint8_t *end = m_end; + + if (src < end) + { + int64_t result = 0; + int shift = 0; + int size = sizeof (int64_t) * 8; + + uint8_t byte = 0; + int bytecount = 0; + + while (src < end) + { + bytecount++; + byte = *src++; + result |= (byte & 0x7f) << shift; + shift += 7; + if ((byte & 0x80) == 0) + break; + } + + // Sign bit of byte is 2nd high order bit (0x40) + if (shift < size && (byte & 0x40)) + result |= - (1 << shift); + + *offset_ptr += bytecount; + return result; + } + return 0; +} + +//---------------------------------------------------------------------- +// Skips a ULEB128 number (signed or unsigned) from this object's +// data starting at the offset pointed to by "offset_ptr". The +// offset pointed to by "offset_ptr" will be updated with the offset +// of the byte following the last extracted byte. +// +// Returns the number of bytes consumed during the extraction. +//---------------------------------------------------------------------- +uint32_t +DataExtractor::Skip_LEB128 (offset_t *offset_ptr) const +{ + uint32_t bytes_consumed = 0; + const uint8_t *src = (const uint8_t *)PeekData (*offset_ptr, 1); + if (src == NULL) + return 0; + + const uint8_t *end = m_end; + + if (src < end) + { + const uint8_t *src_pos = src; + while ((src_pos < end) && (*src_pos++ & 0x80)) + ++bytes_consumed; + *offset_ptr += src_pos - src; + } + return bytes_consumed; +} + +static bool +GetAPInt (const DataExtractor &data, lldb::offset_t *offset_ptr, lldb::offset_t byte_size, llvm::APInt &result) +{ + llvm::SmallVector uint64_array; + lldb::offset_t bytes_left = byte_size; + uint64_t u64; + const lldb::ByteOrder byte_order = data.GetByteOrder(); + if (byte_order == lldb::eByteOrderLittle) + { + while (bytes_left > 0) + { + if (bytes_left >= 8) + { + u64 = data.GetU64(offset_ptr); + bytes_left -= 8; + } + else + { + u64 = data.GetMaxU64(offset_ptr, (uint32_t)bytes_left); + bytes_left = 0; + } + uint64_array.push_back(u64); + } + result = llvm::APInt(byte_size * 8, llvm::ArrayRef(uint64_array)); + return true; + } + else if (byte_order == lldb::eByteOrderBig) + { + lldb::offset_t be_offset = *offset_ptr + byte_size; + lldb::offset_t temp_offset; + while (bytes_left > 0) + { + if (bytes_left >= 8) + { + be_offset -= 8; + temp_offset = be_offset; + u64 = data.GetU64(&temp_offset); + bytes_left -= 8; + } + else + { + be_offset -= bytes_left; + temp_offset = be_offset; + u64 = data.GetMaxU64(&temp_offset, (uint32_t)bytes_left); + bytes_left = 0; + } + uint64_array.push_back(u64); + } + *offset_ptr += byte_size; + result = llvm::APInt(byte_size * 8, llvm::ArrayRef(uint64_array)); + return true; + } + return false; +} + +static lldb::offset_t +DumpAPInt (Stream *s, const DataExtractor &data, lldb::offset_t offset, lldb::offset_t byte_size, bool is_signed, unsigned radix) +{ + llvm::APInt apint; + if (GetAPInt (data, &offset, byte_size, apint)) + { + std::string apint_str(apint.toString(radix, is_signed)); + switch (radix) + { + case 2: + s->Write ("0b", 2); + break; + case 8: + s->Write ("0", 1); + break; + case 10: + break; + } + s->Write(apint_str.c_str(), apint_str.size()); + } + return offset; +} + +static float half2float (uint16_t half) +{ + union{ float f; uint32_t u;}u; + int32_t v = (int16_t) half; + + if( 0 == (v & 0x7c00)) + { + u.u = v & 0x80007FFFU; + return u.f * 0x1.0p125f; + } + + v <<= 13; + u.u = v | 0x70000000U; + return u.f * 0x1.0p-112f; +} + +lldb::offset_t +DataExtractor::Dump (Stream *s, + offset_t start_offset, + lldb::Format item_format, + size_t item_byte_size, + size_t item_count, + size_t num_per_line, + uint64_t base_addr, + uint32_t item_bit_size, // If zero, this is not a bitfield value, if non-zero, the value is a bitfield + uint32_t item_bit_offset, // If "item_bit_size" is non-zero, this is the shift amount to apply to a bitfield + ExecutionContextScope *exe_scope) const +{ + if (s == NULL) + return start_offset; + + if (item_format == eFormatPointer) + { + if (item_byte_size != 4 && item_byte_size != 8) + item_byte_size = s->GetAddressByteSize(); + } + + offset_t offset = start_offset; + + if (item_format == eFormatInstruction) + { + TargetSP target_sp; + if (exe_scope) + target_sp = exe_scope->CalculateTarget(); + if (target_sp) + { + DisassemblerSP disassembler_sp (Disassembler::FindPlugin(target_sp->GetArchitecture(), NULL, NULL)); + if (disassembler_sp) + { + lldb::addr_t addr = base_addr + start_offset; + lldb_private::Address so_addr; + bool data_from_file = true; + if (target_sp->GetSectionLoadList().ResolveLoadAddress(addr, so_addr)) + { + data_from_file = false; + } + else + { + if (target_sp->GetSectionLoadList().IsEmpty() || !target_sp->GetImages().ResolveFileAddress(addr, so_addr)) + so_addr.SetRawAddress(addr); + } + + size_t bytes_consumed = disassembler_sp->DecodeInstructions (so_addr, *this, start_offset, item_count, false, data_from_file); + + if (bytes_consumed) + { + offset += bytes_consumed; + const bool show_address = base_addr != LLDB_INVALID_ADDRESS; + const bool show_bytes = true; + ExecutionContext exe_ctx; + exe_scope->CalculateExecutionContext(exe_ctx); + disassembler_sp->GetInstructionList().Dump (s, show_address, show_bytes, &exe_ctx); + + // FIXME: The DisassemblerLLVMC has a reference cycle and won't go away if it has any active instructions. + // I'll fix that but for now, just clear the list and it will go away nicely. + disassembler_sp->GetInstructionList().Clear(); + } + } + } + else + s->Printf ("invalid target"); + + return offset; + } + + if ((item_format == eFormatOSType || item_format == eFormatAddressInfo) && item_byte_size > 8) + item_format = eFormatHex; + + lldb::offset_t line_start_offset = start_offset; + for (uint32_t count = 0; ValidOffset(offset) && count < item_count; ++count) + { + if ((count % num_per_line) == 0) + { + if (count > 0) + { + if (item_format == eFormatBytesWithASCII && offset > line_start_offset) + { + s->Printf("%*s", static_cast((num_per_line - (offset - line_start_offset)) * 3 + 2), ""); + Dump(s, line_start_offset, eFormatCharPrintable, 1, offset - line_start_offset, LLDB_INVALID_OFFSET, LLDB_INVALID_ADDRESS, 0, 0); + } + s->EOL(); + } + if (base_addr != LLDB_INVALID_ADDRESS) + s->Printf ("0x%8.8" PRIx64 ": ", (uint64_t)(base_addr + (offset - start_offset))); + line_start_offset = offset; + } + else + if (item_format != eFormatChar && + item_format != eFormatCharPrintable && + item_format != eFormatCharArray && + count > 0) + { + s->PutChar(' '); + } + + uint32_t i; + switch (item_format) + { + case eFormatBoolean: + if (item_byte_size <= 8) + s->Printf ("%s", GetMaxU64Bitfield(&offset, item_byte_size, item_bit_size, item_bit_offset) ? "true" : "false"); + else + { + s->Printf("error: unsupported byte size (%zu) for boolean format", item_byte_size); + return offset; + } + break; + + case eFormatBinary: + if (item_byte_size <= 8) + { + uint64_t uval64 = GetMaxU64Bitfield(&offset, item_byte_size, item_bit_size, item_bit_offset); + // Avoid std::bitset<64>::to_string() since it is missing in + // earlier C++ libraries + std::string binary_value(64, '0'); + std::bitset<64> bits(uval64); + for (i = 0; i < 64; ++i) + if (bits[i]) + binary_value[64 - 1 - i] = '1'; + if (item_bit_size > 0) + s->Printf("0b%s", binary_value.c_str() + 64 - item_bit_size); + else if (item_byte_size > 0 && item_byte_size <= 8) + s->Printf("0b%s", binary_value.c_str() + 64 - item_byte_size * 8); + } + else + { + const bool is_signed = false; + const unsigned radix = 2; + offset = DumpAPInt (s, *this, offset, item_byte_size, is_signed, radix); + } + break; + + case eFormatBytes: + case eFormatBytesWithASCII: + for (i=0; iPrintf ("%2.2x", GetU8(&offset)); + } + // Put an extra space between the groups of bytes if more than one + // is being dumped in a group (item_byte_size is more than 1). + if (item_byte_size > 1) + s->PutChar(' '); + break; + + case eFormatChar: + case eFormatCharPrintable: + case eFormatCharArray: + { + // If we are only printing one character surround it with single + // quotes + if (item_count == 1 && item_format == eFormatChar) + s->PutChar('\''); + + const uint64_t ch = GetMaxU64Bitfield(&offset, item_byte_size, item_bit_size, item_bit_offset); + if (isprint(ch)) + s->Printf ("%c", (char)ch); + else if (item_format != eFormatCharPrintable) + { + switch (ch) + { + case '\033': s->Printf ("\\e"); break; + case '\a': s->Printf ("\\a"); break; + case '\b': s->Printf ("\\b"); break; + case '\f': s->Printf ("\\f"); break; + case '\n': s->Printf ("\\n"); break; + case '\r': s->Printf ("\\r"); break; + case '\t': s->Printf ("\\t"); break; + case '\v': s->Printf ("\\v"); break; + case '\0': s->Printf ("\\0"); break; + default: + if (item_byte_size == 1) + s->Printf ("\\x%2.2x", (uint8_t)ch); + else + s->Printf ("%" PRIu64, ch); + break; + } + } + else + { + s->PutChar(NON_PRINTABLE_CHAR); + } + + // If we are only printing one character surround it with single quotes + if (item_count == 1 && item_format == eFormatChar) + s->PutChar('\''); + } + break; + + case eFormatEnum: // Print enum value as a signed integer when we don't get the enum type + case eFormatDecimal: + if (item_byte_size <= 8) + s->Printf ("%" PRId64, GetMaxS64Bitfield(&offset, item_byte_size, item_bit_size, item_bit_offset)); + else + { + const bool is_signed = true; + const unsigned radix = 10; + offset = DumpAPInt (s, *this, offset, item_byte_size, is_signed, radix); + } + break; + + case eFormatUnsigned: + if (item_byte_size <= 8) + s->Printf ("%" PRIu64, GetMaxU64Bitfield(&offset, item_byte_size, item_bit_size, item_bit_offset)); + else + { + const bool is_signed = false; + const unsigned radix = 10; + offset = DumpAPInt (s, *this, offset, item_byte_size, is_signed, radix); + } + break; + + case eFormatOctal: + if (item_byte_size <= 8) + s->Printf ("0%" PRIo64, GetMaxS64Bitfield(&offset, item_byte_size, item_bit_size, item_bit_offset)); + else + { + const bool is_signed = false; + const unsigned radix = 8; + offset = DumpAPInt (s, *this, offset, item_byte_size, is_signed, radix); + } + break; + + case eFormatOSType: + { + uint64_t uval64 = GetMaxU64Bitfield(&offset, item_byte_size, item_bit_size, item_bit_offset); + s->PutChar('\''); + for (i=0; i> ((item_byte_size - i - 1) * 8)); + if (isprint(ch)) + s->Printf ("%c", ch); + else + { + switch (ch) + { + case '\033': s->Printf ("\\e"); break; + case '\a': s->Printf ("\\a"); break; + case '\b': s->Printf ("\\b"); break; + case '\f': s->Printf ("\\f"); break; + case '\n': s->Printf ("\\n"); break; + case '\r': s->Printf ("\\r"); break; + case '\t': s->Printf ("\\t"); break; + case '\v': s->Printf ("\\v"); break; + case '\0': s->Printf ("\\0"); break; + default: s->Printf ("\\x%2.2x", ch); break; + } + } + } + s->PutChar('\''); + } + break; + + case eFormatCString: + { + const char *cstr = GetCStr(&offset); + + if (!cstr) + { + s->Printf("NULL"); + offset = LLDB_INVALID_OFFSET; + } + else + { + s->PutChar('\"'); + + while (const char c = *cstr) + { + if (isprint(c)) + { + s->PutChar(c); + } + else + { + switch (c) + { + case '\033': s->Printf ("\\e"); break; + case '\a': s->Printf ("\\a"); break; + case '\b': s->Printf ("\\b"); break; + case '\f': s->Printf ("\\f"); break; + case '\n': s->Printf ("\\n"); break; + case '\r': s->Printf ("\\r"); break; + case '\t': s->Printf ("\\t"); break; + case '\v': s->Printf ("\\v"); break; + default: s->Printf ("\\x%2.2x", c); break; + } + } + + ++cstr; + } + + s->PutChar('\"'); + } + } + break; + + + case eFormatPointer: + s->Address(GetMaxU64Bitfield(&offset, item_byte_size, item_bit_size, item_bit_offset), sizeof (addr_t)); + break; + + + case eFormatComplexInteger: + { + size_t complex_int_byte_size = item_byte_size / 2; + + if (complex_int_byte_size <= 8) + { + s->Printf("%" PRIu64, GetMaxU64Bitfield(&offset, complex_int_byte_size, 0, 0)); + s->Printf(" + %" PRIu64 "i", GetMaxU64Bitfield(&offset, complex_int_byte_size, 0, 0)); + } + else + { + s->Printf("error: unsupported byte size (%zu) for complex integer format", item_byte_size); + return offset; + } + } + break; + + case eFormatComplex: + if (sizeof(float) * 2 == item_byte_size) + { + float f32_1 = GetFloat (&offset); + float f32_2 = GetFloat (&offset); + + s->Printf ("%g + %gi", f32_1, f32_2); + break; + } + else if (sizeof(double) * 2 == item_byte_size) + { + double d64_1 = GetDouble (&offset); + double d64_2 = GetDouble (&offset); + + s->Printf ("%lg + %lgi", d64_1, d64_2); + break; + } + else if (sizeof(long double) * 2 == item_byte_size) + { + long double ld64_1 = GetLongDouble (&offset); + long double ld64_2 = GetLongDouble (&offset); + s->Printf ("%Lg + %Lgi", ld64_1, ld64_2); + break; + } + else + { + s->Printf("error: unsupported byte size (%zu) for complex float format", item_byte_size); + return offset; + } + break; + + default: + case eFormatDefault: + case eFormatHex: + case eFormatHexUppercase: + { + bool wantsuppercase = (item_format == eFormatHexUppercase); + if (item_byte_size <= 8) + { + s->Printf(wantsuppercase ? "0x%*.*" PRIX64 : "0x%*.*" PRIx64, (int)(2 * item_byte_size), (int)(2 * item_byte_size), GetMaxU64Bitfield(&offset, item_byte_size, item_bit_size, item_bit_offset)); + } + else + { + assert (item_bit_size == 0 && item_bit_offset == 0); + s->PutCString("0x"); + const uint8_t *bytes = (const uint8_t* )GetData(&offset, item_byte_size); + if (bytes) + { + uint32_t idx; + if (m_byte_order == eByteOrderBig) + { + for (idx = 0; idx < item_byte_size; ++idx) + s->Printf(wantsuppercase ? "%2.2X" : "%2.2x", bytes[idx]); + } + else + { + for (idx = 0; idx < item_byte_size; ++idx) + s->Printf(wantsuppercase ? "%2.2X" : "%2.2x", bytes[item_byte_size - 1 - idx]); + } + } + } + } + break; + + case eFormatFloat: + { + TargetSP target_sp; + bool used_apfloat = false; + if (exe_scope) + target_sp = exe_scope->CalculateTarget(); + if (target_sp) + { + ClangASTContext *clang_ast = target_sp->GetScratchClangASTContext(); + if (clang_ast) + { + clang::ASTContext *ast = clang_ast->getASTContext(); + if (ast) + { + llvm::SmallVector sv; + // Show full precision when printing float values + const unsigned format_precision = 0; + const unsigned format_max_padding = 100; + size_t item_bit_size = item_byte_size * 8; + + if (item_bit_size == ast->getTypeSize(ast->FloatTy)) + { + llvm::APInt apint(item_bit_size, this->GetMaxU64(&offset, item_byte_size)); + llvm::APFloat apfloat (ast->getFloatTypeSemantics(ast->FloatTy), apint); + apfloat.toString(sv, format_precision, format_max_padding); + } + else if (item_bit_size == ast->getTypeSize(ast->DoubleTy)) + { + llvm::APInt apint; + if (GetAPInt (*this, &offset, item_byte_size, apint)) + { + llvm::APFloat apfloat (ast->getFloatTypeSemantics(ast->DoubleTy), apint); + apfloat.toString(sv, format_precision, format_max_padding); + } + } + else if (item_bit_size == ast->getTypeSize(ast->LongDoubleTy)) + { + llvm::APInt apint; + switch (target_sp->GetArchitecture().GetCore()) + { + case ArchSpec::eCore_x86_32_i386: + case ArchSpec::eCore_x86_32_i486: + case ArchSpec::eCore_x86_32_i486sx: + case ArchSpec::eCore_x86_64_x86_64: + // clang will assert when contructing the apfloat if we use a 16 byte integer value + if (GetAPInt (*this, &offset, 10, apint)) + { + llvm::APFloat apfloat (ast->getFloatTypeSemantics(ast->LongDoubleTy), apint); + apfloat.toString(sv, format_precision, format_max_padding); + } + break; + + default: + if (GetAPInt (*this, &offset, item_byte_size, apint)) + { + llvm::APFloat apfloat (ast->getFloatTypeSemantics(ast->LongDoubleTy), apint); + apfloat.toString(sv, format_precision, format_max_padding); + } + break; + } + } + else if (item_bit_size == ast->getTypeSize(ast->HalfTy)) + { + llvm::APInt apint(item_bit_size, this->GetU16(&offset)); + llvm::APFloat apfloat (ast->getFloatTypeSemantics(ast->HalfTy), apint); + apfloat.toString(sv, format_precision, format_max_padding); + } + + if (!sv.empty()) + { + s->Printf("%*.*s", (int)sv.size(), (int)sv.size(), sv.data()); + used_apfloat = true; + } + } + } + } + + if (!used_apfloat) + { + std::ostringstream ss; + if (item_byte_size == sizeof(float) || item_byte_size == 2) + { + float f; + if (item_byte_size == 2) + { + uint16_t half = this->GetU16(&offset); + f = half2float(half); + } + else + { + f = GetFloat (&offset); + } + ss.precision(std::numeric_limits::digits10); + ss << f; + } + else if (item_byte_size == sizeof(double)) + { + ss.precision(std::numeric_limits::digits10); + ss << GetDouble(&offset); + } + else if (item_byte_size == sizeof(long double) || item_byte_size == 10) + { + ss.precision(std::numeric_limits::digits10); + ss << GetLongDouble(&offset); + } + else + { + s->Printf("error: unsupported byte size (%zu) for float format", item_byte_size); + return offset; + } + ss.flush(); + s->Printf("%s", ss.str().c_str()); + } + } + break; + + case eFormatUnicode16: + s->Printf("U+%4.4x", GetU16 (&offset)); + break; + + case eFormatUnicode32: + s->Printf("U+0x%8.8x", GetU32 (&offset)); + break; + + case eFormatAddressInfo: + { + addr_t addr = GetMaxU64Bitfield(&offset, item_byte_size, item_bit_size, item_bit_offset); + s->Printf("0x%*.*" PRIx64, (int)(2 * item_byte_size), (int)(2 * item_byte_size), addr); + if (exe_scope) + { + TargetSP target_sp (exe_scope->CalculateTarget()); + lldb_private::Address so_addr; + if (target_sp) + { + if (target_sp->GetSectionLoadList().ResolveLoadAddress(addr, so_addr)) + { + s->PutChar(' '); + so_addr.Dump (s, + exe_scope, + Address::DumpStyleResolvedDescription, + Address::DumpStyleModuleWithFileAddress); + } + else + { + so_addr.SetOffset(addr); + so_addr.Dump (s, exe_scope, Address::DumpStyleResolvedPointerDescription); + } + } + } + } + break; + + case eFormatHexFloat: + if (sizeof(float) == item_byte_size) + { + char float_cstr[256]; + llvm::APFloat ap_float (GetFloat (&offset)); + ap_float.convertToHexString (float_cstr, 0, false, llvm::APFloat::rmNearestTiesToEven); + s->Printf ("%s", float_cstr); + break; + } + else if (sizeof(double) == item_byte_size) + { + char float_cstr[256]; + llvm::APFloat ap_float (GetDouble (&offset)); + ap_float.convertToHexString (float_cstr, 0, false, llvm::APFloat::rmNearestTiesToEven); + s->Printf ("%s", float_cstr); + break; + } + else + { + s->Printf("error: unsupported byte size (%zu) for hex float format", item_byte_size); + return offset; + } + break; + +// please keep the single-item formats below in sync with FormatManager::GetSingleItemFormat +// if you fail to do so, users will start getting different outputs depending on internal +// implementation details they should not care about || + case eFormatVectorOfChar: // || + s->PutChar('{'); // \/ + offset = Dump (s, offset, eFormatCharArray, 1, item_byte_size, item_byte_size, LLDB_INVALID_ADDRESS, 0, 0); + s->PutChar('}'); + break; + + case eFormatVectorOfSInt8: + s->PutChar('{'); + offset = Dump (s, offset, eFormatDecimal, 1, item_byte_size, item_byte_size, LLDB_INVALID_ADDRESS, 0, 0); + s->PutChar('}'); + break; + + case eFormatVectorOfUInt8: + s->PutChar('{'); + offset = Dump (s, offset, eFormatHex, 1, item_byte_size, item_byte_size, LLDB_INVALID_ADDRESS, 0, 0); + s->PutChar('}'); + break; + + case eFormatVectorOfSInt16: + s->PutChar('{'); + offset = Dump (s, offset, eFormatDecimal, sizeof(uint16_t), item_byte_size / sizeof(uint16_t), item_byte_size / sizeof(uint16_t), LLDB_INVALID_ADDRESS, 0, 0); + s->PutChar('}'); + break; + + case eFormatVectorOfUInt16: + s->PutChar('{'); + offset = Dump (s, offset, eFormatHex, sizeof(uint16_t), item_byte_size / sizeof(uint16_t), item_byte_size / sizeof(uint16_t), LLDB_INVALID_ADDRESS, 0, 0); + s->PutChar('}'); + break; + + case eFormatVectorOfSInt32: + s->PutChar('{'); + offset = Dump (s, offset, eFormatDecimal, sizeof(uint32_t), item_byte_size / sizeof(uint32_t), item_byte_size / sizeof(uint32_t), LLDB_INVALID_ADDRESS, 0, 0); + s->PutChar('}'); + break; + + case eFormatVectorOfUInt32: + s->PutChar('{'); + offset = Dump (s, offset, eFormatHex, sizeof(uint32_t), item_byte_size / sizeof(uint32_t), item_byte_size / sizeof(uint32_t), LLDB_INVALID_ADDRESS, 0, 0); + s->PutChar('}'); + break; + + case eFormatVectorOfSInt64: + s->PutChar('{'); + offset = Dump (s, offset, eFormatDecimal, sizeof(uint64_t), item_byte_size / sizeof(uint64_t), item_byte_size / sizeof(uint64_t), LLDB_INVALID_ADDRESS, 0, 0); + s->PutChar('}'); + break; + + case eFormatVectorOfUInt64: + s->PutChar('{'); + offset = Dump (s, offset, eFormatHex, sizeof(uint64_t), item_byte_size / sizeof(uint64_t), item_byte_size / sizeof(uint64_t), LLDB_INVALID_ADDRESS, 0, 0); + s->PutChar('}'); + break; + + case eFormatVectorOfFloat32: + s->PutChar('{'); + offset = Dump (s, offset, eFormatFloat, 4, item_byte_size / 4, item_byte_size / 4, LLDB_INVALID_ADDRESS, 0, 0); + s->PutChar('}'); + break; + + case eFormatVectorOfFloat64: + s->PutChar('{'); + offset = Dump (s, offset, eFormatFloat, 8, item_byte_size / 8, item_byte_size / 8, LLDB_INVALID_ADDRESS, 0, 0); + s->PutChar('}'); + break; + + case eFormatVectorOfUInt128: + s->PutChar('{'); + offset = Dump (s, offset, eFormatHex, 16, item_byte_size / 16, item_byte_size / 16, LLDB_INVALID_ADDRESS, 0, 0); + s->PutChar('}'); + break; + } + } + + if (item_format == eFormatBytesWithASCII && offset > line_start_offset) + { + s->Printf("%*s", static_cast((num_per_line - (offset - line_start_offset)) * 3 + 2), ""); + Dump(s, line_start_offset, eFormatCharPrintable, 1, offset - line_start_offset, LLDB_INVALID_OFFSET, LLDB_INVALID_ADDRESS, 0, 0); + } + return offset; // Return the offset at which we ended up +} + +//---------------------------------------------------------------------- +// Dumps bytes from this object's data to the stream "s" starting +// "start_offset" bytes into this data, and ending with the byte +// before "end_offset". "base_addr" will be added to the offset +// into the dumped data when showing the offset into the data in the +// output information. "num_per_line" objects of type "type" will +// be dumped with the option to override the format for each object +// with "type_format". "type_format" is a printf style formatting +// string. If "type_format" is NULL, then an appropriate format +// string will be used for the supplied "type". If the stream "s" +// is NULL, then the output will be send to Log(). +//---------------------------------------------------------------------- +lldb::offset_t +DataExtractor::PutToLog +( + Log *log, + offset_t start_offset, + offset_t length, + uint64_t base_addr, + uint32_t num_per_line, + DataExtractor::Type type, + const char *format +) const +{ + if (log == NULL) + return start_offset; + + offset_t offset; + offset_t end_offset; + uint32_t count; + StreamString sstr; + for (offset = start_offset, end_offset = offset + length, count = 0; ValidOffset(offset) && offset < end_offset; ++count) + { + if ((count % num_per_line) == 0) + { + // Print out any previous string + if (sstr.GetSize() > 0) + { + log->Printf("%s", sstr.GetData()); + sstr.Clear(); + } + // Reset string offset and fill the current line string with address: + if (base_addr != LLDB_INVALID_ADDRESS) + sstr.Printf("0x%8.8" PRIx64 ":", (uint64_t)(base_addr + (offset - start_offset))); + } + + switch (type) + { + case TypeUInt8: sstr.Printf (format ? format : " %2.2x", GetU8(&offset)); break; + case TypeChar: + { + char ch = GetU8(&offset); + sstr.Printf (format ? format : " %c", isprint(ch) ? ch : ' '); + } + break; + case TypeUInt16: sstr.Printf (format ? format : " %4.4x", GetU16(&offset)); break; + case TypeUInt32: sstr.Printf (format ? format : " %8.8x", GetU32(&offset)); break; + case TypeUInt64: sstr.Printf (format ? format : " %16.16" PRIx64, GetU64(&offset)); break; + case TypePointer: sstr.Printf (format ? format : " 0x%" PRIx64, GetAddress(&offset)); break; + case TypeULEB128: sstr.Printf (format ? format : " 0x%" PRIx64, GetULEB128(&offset)); break; + case TypeSLEB128: sstr.Printf (format ? format : " %" PRId64, GetSLEB128(&offset)); break; + } + } + + if (sstr.GetSize() > 0) + log->Printf("%s", sstr.GetData()); + + return offset; // Return the offset at which we ended up +} + +//---------------------------------------------------------------------- +// DumpUUID +// +// Dump out a UUID starting at 'offset' bytes into the buffer +//---------------------------------------------------------------------- +void +DataExtractor::DumpUUID (Stream *s, offset_t offset) const +{ + if (s) + { + const uint8_t *uuid_data = PeekData(offset, 16); + if ( uuid_data ) + { + lldb_private::UUID uuid(uuid_data, 16); + uuid.Dump(s); + } + else + { + s->Printf("", offset); + } + } +} + +void +DataExtractor::DumpHexBytes (Stream *s, + const void *src, + size_t src_len, + uint32_t bytes_per_line, + addr_t base_addr) +{ + DataExtractor data (src, src_len, eByteOrderLittle, 4); + data.Dump (s, + 0, // Offset into "src" + eFormatBytes, // Dump as hex bytes + 1, // Size of each item is 1 for single bytes + src_len, // Number of bytes + bytes_per_line, // Num bytes per line + base_addr, // Base address + 0, 0); // Bitfield info +} + +size_t +DataExtractor::Copy (DataExtractor &dest_data) const +{ + if (m_data_sp.get()) + { + // we can pass along the SP to the data + dest_data.SetData(m_data_sp); + } + else + { + const uint8_t *base_ptr = m_start; + size_t data_size = GetByteSize(); + dest_data.SetData(DataBufferSP(new DataBufferHeap(base_ptr, data_size))); + } + return GetByteSize(); +} + +bool +DataExtractor::Append(DataExtractor& rhs) +{ + if (rhs.GetByteOrder() != GetByteOrder()) + return false; + + if (rhs.GetByteSize() == 0) + return true; + + if (GetByteSize() == 0) + return (rhs.Copy(*this) > 0); + + size_t bytes = GetByteSize() + rhs.GetByteSize(); + + DataBufferHeap *buffer_heap_ptr = NULL; + DataBufferSP buffer_sp(buffer_heap_ptr = new DataBufferHeap(bytes, 0)); + + if (buffer_sp.get() == NULL || buffer_heap_ptr == NULL) + return false; + + uint8_t* bytes_ptr = buffer_heap_ptr->GetBytes(); + + memcpy(bytes_ptr, GetDataStart(), GetByteSize()); + memcpy(bytes_ptr + GetByteSize(), rhs.GetDataStart(), rhs.GetByteSize()); + + SetData(buffer_sp); + + return true; +} + +bool +DataExtractor::Append(void* buf, offset_t length) +{ + if (buf == NULL) + return false; + + if (length == 0) + return true; + + size_t bytes = GetByteSize() + length; + + DataBufferHeap *buffer_heap_ptr = NULL; + DataBufferSP buffer_sp(buffer_heap_ptr = new DataBufferHeap(bytes, 0)); + + if (buffer_sp.get() == NULL || buffer_heap_ptr == NULL) + return false; + + uint8_t* bytes_ptr = buffer_heap_ptr->GetBytes(); + + if (GetByteSize() > 0) + memcpy(bytes_ptr, GetDataStart(), GetByteSize()); + + memcpy(bytes_ptr + GetByteSize(), buf, length); + + SetData(buffer_sp); + + return true; +} diff --git a/contrib/llvm/tools/lldb/source/Core/Debugger.cpp b/contrib/llvm/tools/lldb/source/Core/Debugger.cpp new file mode 100644 index 00000000000..d1d2ebb0550 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Core/Debugger.cpp @@ -0,0 +1,2695 @@ +//===-- Debugger.cpp --------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "lldb/API/SBDebugger.h" + +#include "lldb/Core/Debugger.h" + +#include + +#include "clang/AST/DeclCXX.h" +#include "clang/AST/Type.h" + +#include "lldb/lldb-private.h" +#include "lldb/Core/ConnectionFileDescriptor.h" +#include "lldb/Core/InputReader.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/RegisterValue.h" +#include "lldb/Core/State.h" +#include "lldb/Core/StreamAsynchronousIO.h" +#include "lldb/Core/StreamCallback.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Core/Timer.h" +#include "lldb/Core/ValueObject.h" +#include "lldb/Core/ValueObjectVariable.h" +#include "lldb/DataFormatters/DataVisualization.h" +#include "lldb/DataFormatters/FormatManager.h" +#include "lldb/Host/DynamicLibrary.h" +#include "lldb/Host/Terminal.h" +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Interpreter/OptionValueSInt64.h" +#include "lldb/Interpreter/OptionValueString.h" +#include "lldb/Symbol/ClangASTContext.h" +#include "lldb/Symbol/CompileUnit.h" +#include "lldb/Symbol/Function.h" +#include "lldb/Symbol/Symbol.h" +#include "lldb/Symbol/VariableList.h" +#include "lldb/Target/TargetList.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/StopInfo.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" +#include "lldb/Utility/AnsiTerminal.h" + +using namespace lldb; +using namespace lldb_private; + + +static uint32_t g_shared_debugger_refcount = 0; +static lldb::user_id_t g_unique_id = 1; + +#pragma mark Static Functions + +static Mutex & +GetDebuggerListMutex () +{ + static Mutex g_mutex(Mutex::eMutexTypeRecursive); + return g_mutex; +} + +typedef std::vector DebuggerList; + +static DebuggerList & +GetDebuggerList() +{ + // hide the static debugger list inside a singleton accessor to avoid + // global init contructors + static DebuggerList g_list; + return g_list; +} + +OptionEnumValueElement +g_show_disassembly_enum_values[] = +{ + { Debugger::eStopDisassemblyTypeNever, "never", "Never show disassembly when displaying a stop context."}, + { Debugger::eStopDisassemblyTypeNoSource, "no-source", "Show disassembly when there is no source information, or the source file is missing when displaying a stop context."}, + { Debugger::eStopDisassemblyTypeAlways, "always", "Always show disassembly when displaying a stop context."}, + { 0, NULL, NULL } +}; + +OptionEnumValueElement +g_language_enumerators[] = +{ + { eScriptLanguageNone, "none", "Disable scripting languages."}, + { eScriptLanguagePython, "python", "Select python as the default scripting language."}, + { eScriptLanguageDefault, "default", "Select the lldb default as the default scripting language."}, + { 0, NULL, NULL } +}; + +#define MODULE_WITH_FUNC "{ ${module.file.basename}{`${function.name-with-args}${function.pc-offset}}}" +#define FILE_AND_LINE "{ at ${line.file.basename}:${line.number}}" + +#define DEFAULT_THREAD_FORMAT "thread #${thread.index}: tid = ${thread.id%tid}"\ + "{, ${frame.pc}}"\ + MODULE_WITH_FUNC\ + FILE_AND_LINE\ + "{, name = '${thread.name}'}"\ + "{, queue = '${thread.queue}'}"\ + "{, stop reason = ${thread.stop-reason}}"\ + "{\\nReturn value: ${thread.return-value}}"\ + "\\n" + +#define DEFAULT_FRAME_FORMAT "frame #${frame.index}: ${frame.pc}"\ + MODULE_WITH_FUNC\ + FILE_AND_LINE\ + "\\n" + + + +static PropertyDefinition +g_properties[] = +{ +{ "auto-confirm", OptionValue::eTypeBoolean, true, false, NULL, NULL, "If true all confirmation prompts will receive their default reply." }, +{ "frame-format", OptionValue::eTypeString , true, 0 , DEFAULT_FRAME_FORMAT, NULL, "The default frame format string to use when displaying stack frame information for threads." }, +{ "notify-void", OptionValue::eTypeBoolean, true, false, NULL, NULL, "Notify the user explicitly if an expression returns void (default: false)." }, +{ "prompt", OptionValue::eTypeString , true, OptionValueString::eOptionEncodeCharacterEscapeSequences, "(lldb) ", NULL, "The debugger command line prompt displayed for the user." }, +{ "script-lang", OptionValue::eTypeEnum , true, eScriptLanguagePython, NULL, g_language_enumerators, "The script language to be used for evaluating user-written scripts." }, +{ "stop-disassembly-count", OptionValue::eTypeSInt64 , true, 4 , NULL, NULL, "The number of disassembly lines to show when displaying a stopped context." }, +{ "stop-disassembly-display", OptionValue::eTypeEnum , true, Debugger::eStopDisassemblyTypeNoSource, NULL, g_show_disassembly_enum_values, "Control when to display disassembly when displaying a stopped context." }, +{ "stop-line-count-after", OptionValue::eTypeSInt64 , true, 3 , NULL, NULL, "The number of sources lines to display that come after the current source line when displaying a stopped context." }, +{ "stop-line-count-before", OptionValue::eTypeSInt64 , true, 3 , NULL, NULL, "The number of sources lines to display that come before the current source line when displaying a stopped context." }, +{ "term-width", OptionValue::eTypeSInt64 , true, 80 , NULL, NULL, "The maximum number of columns to use for displaying text." }, +{ "thread-format", OptionValue::eTypeString , true, 0 , DEFAULT_THREAD_FORMAT, NULL, "The default thread format string to use when displaying thread information." }, +{ "use-external-editor", OptionValue::eTypeBoolean, true, false, NULL, NULL, "Whether to use an external editor or not." }, +{ "use-color", OptionValue::eTypeBoolean, true, true , NULL, NULL, "Whether to use Ansi color codes or not." }, + + { NULL, OptionValue::eTypeInvalid, true, 0 , NULL, NULL, NULL } +}; + +enum +{ + ePropertyAutoConfirm = 0, + ePropertyFrameFormat, + ePropertyNotiftVoid, + ePropertyPrompt, + ePropertyScriptLanguage, + ePropertyStopDisassemblyCount, + ePropertyStopDisassemblyDisplay, + ePropertyStopLineCountAfter, + ePropertyStopLineCountBefore, + ePropertyTerminalWidth, + ePropertyThreadFormat, + ePropertyUseExternalEditor, + ePropertyUseColor, +}; + +// +//const char * +//Debugger::GetFrameFormat() const +//{ +// return m_properties_sp->GetFrameFormat(); +//} +//const char * +//Debugger::GetThreadFormat() const +//{ +// return m_properties_sp->GetThreadFormat(); +//} +// + + +Error +Debugger::SetPropertyValue (const ExecutionContext *exe_ctx, + VarSetOperationType op, + const char *property_path, + const char *value) +{ + bool is_load_script = strcmp(property_path,"target.load-script-from-symbol-file") == 0; + TargetSP target_sp; + LoadScriptFromSymFile load_script_old_value; + if (is_load_script && exe_ctx->GetTargetSP()) + { + target_sp = exe_ctx->GetTargetSP(); + load_script_old_value = target_sp->TargetProperties::GetLoadScriptFromSymbolFile(); + } + Error error (Properties::SetPropertyValue (exe_ctx, op, property_path, value)); + if (error.Success()) + { + // FIXME it would be nice to have "on-change" callbacks for properties + if (strcmp(property_path, g_properties[ePropertyPrompt].name) == 0) + { + const char *new_prompt = GetPrompt(); + std::string str = lldb_utility::ansi::FormatAnsiTerminalCodes (new_prompt, GetUseColor()); + if (str.length()) + new_prompt = str.c_str(); + EventSP prompt_change_event_sp (new Event(CommandInterpreter::eBroadcastBitResetPrompt, new EventDataBytes (new_prompt))); + GetCommandInterpreter().BroadcastEvent (prompt_change_event_sp); + } + else if (strcmp(property_path, g_properties[ePropertyUseColor].name) == 0) + { + // use-color changed. Ping the prompt so it can reset the ansi terminal codes. + SetPrompt (GetPrompt()); + } + else if (is_load_script && target_sp && load_script_old_value == eLoadScriptFromSymFileWarn) + { + if (target_sp->TargetProperties::GetLoadScriptFromSymbolFile() == eLoadScriptFromSymFileTrue) + { + std::list errors; + StreamString feedback_stream; + if (!target_sp->LoadScriptingResources(errors,&feedback_stream)) + { + for (auto error : errors) + { + GetErrorStream().Printf("%s\n",error.AsCString()); + } + if (feedback_stream.GetSize()) + GetErrorStream().Printf("%s",feedback_stream.GetData()); + } + } + } + } + return error; +} + +bool +Debugger::GetAutoConfirm () const +{ + const uint32_t idx = ePropertyAutoConfirm; + return m_collection_sp->GetPropertyAtIndexAsBoolean (NULL, idx, g_properties[idx].default_uint_value != 0); +} + +const char * +Debugger::GetFrameFormat() const +{ + const uint32_t idx = ePropertyFrameFormat; + return m_collection_sp->GetPropertyAtIndexAsString (NULL, idx, g_properties[idx].default_cstr_value); +} + +bool +Debugger::GetNotifyVoid () const +{ + const uint32_t idx = ePropertyNotiftVoid; + return m_collection_sp->GetPropertyAtIndexAsBoolean (NULL, idx, g_properties[idx].default_uint_value != 0); +} + +const char * +Debugger::GetPrompt() const +{ + const uint32_t idx = ePropertyPrompt; + return m_collection_sp->GetPropertyAtIndexAsString (NULL, idx, g_properties[idx].default_cstr_value); +} + +void +Debugger::SetPrompt(const char *p) +{ + const uint32_t idx = ePropertyPrompt; + m_collection_sp->SetPropertyAtIndexAsString (NULL, idx, p); + const char *new_prompt = GetPrompt(); + std::string str = lldb_utility::ansi::FormatAnsiTerminalCodes (new_prompt, GetUseColor()); + if (str.length()) + new_prompt = str.c_str(); + EventSP prompt_change_event_sp (new Event(CommandInterpreter::eBroadcastBitResetPrompt, new EventDataBytes (new_prompt)));; + GetCommandInterpreter().BroadcastEvent (prompt_change_event_sp); +} + +const char * +Debugger::GetThreadFormat() const +{ + const uint32_t idx = ePropertyThreadFormat; + return m_collection_sp->GetPropertyAtIndexAsString (NULL, idx, g_properties[idx].default_cstr_value); +} + +lldb::ScriptLanguage +Debugger::GetScriptLanguage() const +{ + const uint32_t idx = ePropertyScriptLanguage; + return (lldb::ScriptLanguage)m_collection_sp->GetPropertyAtIndexAsEnumeration (NULL, idx, g_properties[idx].default_uint_value); +} + +bool +Debugger::SetScriptLanguage (lldb::ScriptLanguage script_lang) +{ + const uint32_t idx = ePropertyScriptLanguage; + return m_collection_sp->SetPropertyAtIndexAsEnumeration (NULL, idx, script_lang); +} + +uint32_t +Debugger::GetTerminalWidth () const +{ + const uint32_t idx = ePropertyTerminalWidth; + return m_collection_sp->GetPropertyAtIndexAsSInt64 (NULL, idx, g_properties[idx].default_uint_value); +} + +bool +Debugger::SetTerminalWidth (uint32_t term_width) +{ + const uint32_t idx = ePropertyTerminalWidth; + return m_collection_sp->SetPropertyAtIndexAsSInt64 (NULL, idx, term_width); +} + +bool +Debugger::GetUseExternalEditor () const +{ + const uint32_t idx = ePropertyUseExternalEditor; + return m_collection_sp->GetPropertyAtIndexAsBoolean (NULL, idx, g_properties[idx].default_uint_value != 0); +} + +bool +Debugger::SetUseExternalEditor (bool b) +{ + const uint32_t idx = ePropertyUseExternalEditor; + return m_collection_sp->SetPropertyAtIndexAsBoolean (NULL, idx, b); +} + +bool +Debugger::GetUseColor () const +{ + const uint32_t idx = ePropertyUseColor; + return m_collection_sp->GetPropertyAtIndexAsBoolean (NULL, idx, g_properties[idx].default_uint_value != 0); +} + +bool +Debugger::SetUseColor (bool b) +{ + const uint32_t idx = ePropertyUseColor; + bool ret = m_collection_sp->SetPropertyAtIndexAsBoolean (NULL, idx, b); + SetPrompt (GetPrompt()); + return ret; +} + +uint32_t +Debugger::GetStopSourceLineCount (bool before) const +{ + const uint32_t idx = before ? ePropertyStopLineCountBefore : ePropertyStopLineCountAfter; + return m_collection_sp->GetPropertyAtIndexAsSInt64 (NULL, idx, g_properties[idx].default_uint_value); +} + +Debugger::StopDisassemblyType +Debugger::GetStopDisassemblyDisplay () const +{ + const uint32_t idx = ePropertyStopDisassemblyDisplay; + return (Debugger::StopDisassemblyType)m_collection_sp->GetPropertyAtIndexAsEnumeration (NULL, idx, g_properties[idx].default_uint_value); +} + +uint32_t +Debugger::GetDisassemblyLineCount () const +{ + const uint32_t idx = ePropertyStopDisassemblyCount; + return m_collection_sp->GetPropertyAtIndexAsSInt64 (NULL, idx, g_properties[idx].default_uint_value); +} + +#pragma mark Debugger + +//const DebuggerPropertiesSP & +//Debugger::GetSettings() const +//{ +// return m_properties_sp; +//} +// + +int +Debugger::TestDebuggerRefCount () +{ + return g_shared_debugger_refcount; +} + +void +Debugger::Initialize () +{ + if (g_shared_debugger_refcount++ == 0) + lldb_private::Initialize(); +} + +void +Debugger::Terminate () +{ + if (g_shared_debugger_refcount > 0) + { + g_shared_debugger_refcount--; + if (g_shared_debugger_refcount == 0) + { + lldb_private::WillTerminate(); + lldb_private::Terminate(); + + // Clear our master list of debugger objects + Mutex::Locker locker (GetDebuggerListMutex ()); + GetDebuggerList().clear(); + } + } +} + +void +Debugger::SettingsInitialize () +{ + Target::SettingsInitialize (); +} + +void +Debugger::SettingsTerminate () +{ + Target::SettingsTerminate (); +} + +bool +Debugger::LoadPlugin (const FileSpec& spec, Error& error) +{ + lldb::DynamicLibrarySP dynlib_sp(new lldb_private::DynamicLibrary(spec)); + if (!dynlib_sp || dynlib_sp->IsValid() == false) + { + if (spec.Exists()) + error.SetErrorString("this file does not represent a loadable dylib"); + else + error.SetErrorString("no such file"); + return false; + } + lldb::DebuggerSP debugger_sp(shared_from_this()); + lldb::SBDebugger debugger_sb(debugger_sp); + // This calls the bool lldb::PluginInitialize(lldb::SBDebugger debugger) function. + // TODO: mangle this differently for your system - on OSX, the first underscore needs to be removed and the second one stays + LLDBCommandPluginInit init_func = dynlib_sp->GetSymbol("_ZN4lldb16PluginInitializeENS_10SBDebuggerE"); + if (!init_func) + { + error.SetErrorString("cannot find the initialization function lldb::PluginInitialize(lldb::SBDebugger)"); + return false; + } + if (init_func(debugger_sb)) + { + m_loaded_plugins.push_back(dynlib_sp); + return true; + } + error.SetErrorString("dylib refused to be loaded"); + return false; +} + +static FileSpec::EnumerateDirectoryResult +LoadPluginCallback +( + void *baton, + FileSpec::FileType file_type, + const FileSpec &file_spec + ) +{ + Error error; + + static ConstString g_dylibext("dylib"); + static ConstString g_solibext("so"); + + if (!baton) + return FileSpec::eEnumerateDirectoryResultQuit; + + Debugger *debugger = (Debugger*)baton; + + // If we have a regular file, a symbolic link or unknown file type, try + // and process the file. We must handle unknown as sometimes the directory + // enumeration might be enumerating a file system that doesn't have correct + // file type information. + if (file_type == FileSpec::eFileTypeRegular || + file_type == FileSpec::eFileTypeSymbolicLink || + file_type == FileSpec::eFileTypeUnknown ) + { + FileSpec plugin_file_spec (file_spec); + plugin_file_spec.ResolvePath (); + + if (plugin_file_spec.GetFileNameExtension() != g_dylibext && + plugin_file_spec.GetFileNameExtension() != g_solibext) + { + return FileSpec::eEnumerateDirectoryResultNext; + } + + Error plugin_load_error; + debugger->LoadPlugin (plugin_file_spec, plugin_load_error); + + return FileSpec::eEnumerateDirectoryResultNext; + } + + else if (file_type == FileSpec::eFileTypeUnknown || + file_type == FileSpec::eFileTypeDirectory || + file_type == FileSpec::eFileTypeSymbolicLink ) + { + // Try and recurse into anything that a directory or symbolic link. + // We must also do this for unknown as sometimes the directory enumeration + // might be enurating a file system that doesn't have correct file type + // information. + return FileSpec::eEnumerateDirectoryResultEnter; + } + + return FileSpec::eEnumerateDirectoryResultNext; +} + +void +Debugger::InstanceInitialize () +{ + FileSpec dir_spec; + const bool find_directories = true; + const bool find_files = true; + const bool find_other = true; + char dir_path[PATH_MAX]; + if (Host::GetLLDBPath (ePathTypeLLDBSystemPlugins, dir_spec)) + { + if (dir_spec.Exists() && dir_spec.GetPath(dir_path, sizeof(dir_path))) + { + FileSpec::EnumerateDirectory (dir_path, + find_directories, + find_files, + find_other, + LoadPluginCallback, + this); + } + } + + if (Host::GetLLDBPath (ePathTypeLLDBUserPlugins, dir_spec)) + { + if (dir_spec.Exists() && dir_spec.GetPath(dir_path, sizeof(dir_path))) + { + FileSpec::EnumerateDirectory (dir_path, + find_directories, + find_files, + find_other, + LoadPluginCallback, + this); + } + } + + PluginManager::DebuggerInitialize (*this); +} + +DebuggerSP +Debugger::CreateInstance (lldb::LogOutputCallback log_callback, void *baton) +{ + DebuggerSP debugger_sp (new Debugger(log_callback, baton)); + if (g_shared_debugger_refcount > 0) + { + Mutex::Locker locker (GetDebuggerListMutex ()); + GetDebuggerList().push_back(debugger_sp); + } + debugger_sp->InstanceInitialize (); + return debugger_sp; +} + +void +Debugger::Destroy (DebuggerSP &debugger_sp) +{ + if (debugger_sp.get() == NULL) + return; + + debugger_sp->Clear(); + + if (g_shared_debugger_refcount > 0) + { + Mutex::Locker locker (GetDebuggerListMutex ()); + DebuggerList &debugger_list = GetDebuggerList (); + DebuggerList::iterator pos, end = debugger_list.end(); + for (pos = debugger_list.begin (); pos != end; ++pos) + { + if ((*pos).get() == debugger_sp.get()) + { + debugger_list.erase (pos); + return; + } + } + } +} + +DebuggerSP +Debugger::FindDebuggerWithInstanceName (const ConstString &instance_name) +{ + DebuggerSP debugger_sp; + if (g_shared_debugger_refcount > 0) + { + Mutex::Locker locker (GetDebuggerListMutex ()); + DebuggerList &debugger_list = GetDebuggerList(); + DebuggerList::iterator pos, end = debugger_list.end(); + + for (pos = debugger_list.begin(); pos != end; ++pos) + { + if ((*pos).get()->m_instance_name == instance_name) + { + debugger_sp = *pos; + break; + } + } + } + return debugger_sp; +} + +TargetSP +Debugger::FindTargetWithProcessID (lldb::pid_t pid) +{ + TargetSP target_sp; + if (g_shared_debugger_refcount > 0) + { + Mutex::Locker locker (GetDebuggerListMutex ()); + DebuggerList &debugger_list = GetDebuggerList(); + DebuggerList::iterator pos, end = debugger_list.end(); + for (pos = debugger_list.begin(); pos != end; ++pos) + { + target_sp = (*pos)->GetTargetList().FindTargetWithProcessID (pid); + if (target_sp) + break; + } + } + return target_sp; +} + +TargetSP +Debugger::FindTargetWithProcess (Process *process) +{ + TargetSP target_sp; + if (g_shared_debugger_refcount > 0) + { + Mutex::Locker locker (GetDebuggerListMutex ()); + DebuggerList &debugger_list = GetDebuggerList(); + DebuggerList::iterator pos, end = debugger_list.end(); + for (pos = debugger_list.begin(); pos != end; ++pos) + { + target_sp = (*pos)->GetTargetList().FindTargetWithProcess (process); + if (target_sp) + break; + } + } + return target_sp; +} + +Debugger::Debugger (lldb::LogOutputCallback log_callback, void *baton) : + UserID (g_unique_id++), + Properties(OptionValuePropertiesSP(new OptionValueProperties())), + m_input_comm("debugger.input"), + m_input_file (), + m_output_file (), + m_error_file (), + m_terminal_state (), + m_target_list (*this), + m_platform_list (), + m_listener ("lldb.Debugger"), + m_source_manager_ap(), + m_source_file_cache(), + m_command_interpreter_ap (new CommandInterpreter (*this, eScriptLanguageDefault, false)), + m_input_reader_stack (), + m_input_reader_data (), + m_instance_name() +{ + char instance_cstr[256]; + snprintf(instance_cstr, sizeof(instance_cstr), "debugger_%d", (int)GetID()); + m_instance_name.SetCString(instance_cstr); + if (log_callback) + m_log_callback_stream_sp.reset (new StreamCallback (log_callback, baton)); + m_command_interpreter_ap->Initialize (); + // Always add our default platform to the platform list + PlatformSP default_platform_sp (Platform::GetDefaultPlatform()); + assert (default_platform_sp.get()); + m_platform_list.Append (default_platform_sp, true); + + m_collection_sp->Initialize (g_properties); + m_collection_sp->AppendProperty (ConstString("target"), + ConstString("Settings specify to debugging targets."), + true, + Target::GetGlobalProperties()->GetValueProperties()); + if (m_command_interpreter_ap.get()) + { + m_collection_sp->AppendProperty (ConstString("interpreter"), + ConstString("Settings specify to the debugger's command interpreter."), + true, + m_command_interpreter_ap->GetValueProperties()); + } + OptionValueSInt64 *term_width = m_collection_sp->GetPropertyAtIndexAsOptionValueSInt64 (NULL, ePropertyTerminalWidth); + term_width->SetMinimumValue(10); + term_width->SetMaximumValue(1024); + + // Turn off use-color if this is a dumb terminal. + const char *term = getenv ("TERM"); + if (term && !strcmp (term, "dumb")) + SetUseColor (false); +} + +Debugger::~Debugger () +{ + Clear(); +} + +void +Debugger::Clear() +{ + CleanUpInputReaders(); + m_listener.Clear(); + int num_targets = m_target_list.GetNumTargets(); + for (int i = 0; i < num_targets; i++) + { + TargetSP target_sp (m_target_list.GetTargetAtIndex (i)); + if (target_sp) + { + ProcessSP process_sp (target_sp->GetProcessSP()); + if (process_sp) + process_sp->Finalize(); + target_sp->Destroy(); + } + } + BroadcasterManager::Clear (); + + // Close the input file _before_ we close the input read communications class + // as it does NOT own the input file, our m_input_file does. + m_terminal_state.Clear(); + GetInputFile().Close (); + // Now that we have closed m_input_file, we can now tell our input communication + // class to close down. Its read thread should quickly exit after we close + // the input file handle above. + m_input_comm.Clear (); +} + +bool +Debugger::GetCloseInputOnEOF () const +{ + return m_input_comm.GetCloseOnEOF(); +} + +void +Debugger::SetCloseInputOnEOF (bool b) +{ + m_input_comm.SetCloseOnEOF(b); +} + +bool +Debugger::GetAsyncExecution () +{ + return !m_command_interpreter_ap->GetSynchronous(); +} + +void +Debugger::SetAsyncExecution (bool async_execution) +{ + m_command_interpreter_ap->SetSynchronous (!async_execution); +} + + +void +Debugger::SetInputFileHandle (FILE *fh, bool tranfer_ownership) +{ + File &in_file = GetInputFile(); + in_file.SetStream (fh, tranfer_ownership); + if (in_file.IsValid() == false) + in_file.SetStream (stdin, true); + + // Disconnect from any old connection if we had one + m_input_comm.Disconnect (); + // Pass false as the second argument to ConnectionFileDescriptor below because + // our "in_file" above will already take ownership if requested and we don't + // want to objects trying to own and close a file descriptor. + m_input_comm.SetConnection (new ConnectionFileDescriptor (in_file.GetDescriptor(), false)); + m_input_comm.SetReadThreadBytesReceivedCallback (Debugger::DispatchInputCallback, this); + + // Save away the terminal state if that is relevant, so that we can restore it in RestoreInputState. + SaveInputTerminalState (); + + Error error; + if (m_input_comm.StartReadThread (&error) == false) + { + File &err_file = GetErrorFile(); + + err_file.Printf ("error: failed to main input read thread: %s", error.AsCString() ? error.AsCString() : "unkown error"); + exit(1); + } +} + +void +Debugger::SetOutputFileHandle (FILE *fh, bool tranfer_ownership) +{ + File &out_file = GetOutputFile(); + out_file.SetStream (fh, tranfer_ownership); + if (out_file.IsValid() == false) + out_file.SetStream (stdout, false); + + // do not create the ScriptInterpreter just for setting the output file handle + // as the constructor will know how to do the right thing on its own + const bool can_create = false; + ScriptInterpreter* script_interpreter = GetCommandInterpreter().GetScriptInterpreter(can_create); + if (script_interpreter) + script_interpreter->ResetOutputFileHandle (fh); +} + +void +Debugger::SetErrorFileHandle (FILE *fh, bool tranfer_ownership) +{ + File &err_file = GetErrorFile(); + err_file.SetStream (fh, tranfer_ownership); + if (err_file.IsValid() == false) + err_file.SetStream (stderr, false); +} + +void +Debugger::SaveInputTerminalState () +{ + File &in_file = GetInputFile(); + if (in_file.GetDescriptor() != File::kInvalidDescriptor) + m_terminal_state.Save(in_file.GetDescriptor(), true); +} + +void +Debugger::RestoreInputTerminalState () +{ + m_terminal_state.Restore(); +} + +ExecutionContext +Debugger::GetSelectedExecutionContext () +{ + ExecutionContext exe_ctx; + TargetSP target_sp(GetSelectedTarget()); + exe_ctx.SetTargetSP (target_sp); + + if (target_sp) + { + ProcessSP process_sp (target_sp->GetProcessSP()); + exe_ctx.SetProcessSP (process_sp); + if (process_sp && process_sp->IsRunning() == false) + { + ThreadSP thread_sp (process_sp->GetThreadList().GetSelectedThread()); + if (thread_sp) + { + exe_ctx.SetThreadSP (thread_sp); + exe_ctx.SetFrameSP (thread_sp->GetSelectedFrame()); + if (exe_ctx.GetFramePtr() == NULL) + exe_ctx.SetFrameSP (thread_sp->GetStackFrameAtIndex (0)); + } + } + } + return exe_ctx; + +} + +InputReaderSP +Debugger::GetCurrentInputReader () +{ + InputReaderSP reader_sp; + + if (!m_input_reader_stack.IsEmpty()) + { + // Clear any finished readers from the stack + while (CheckIfTopInputReaderIsDone()) ; + + if (!m_input_reader_stack.IsEmpty()) + reader_sp = m_input_reader_stack.Top(); + } + + return reader_sp; +} + +void +Debugger::DispatchInputCallback (void *baton, const void *bytes, size_t bytes_len) +{ + if (bytes_len > 0) + ((Debugger *)baton)->DispatchInput ((char *)bytes, bytes_len); + else + ((Debugger *)baton)->DispatchInputEndOfFile (); +} + + +void +Debugger::DispatchInput (const char *bytes, size_t bytes_len) +{ + if (bytes == NULL || bytes_len == 0) + return; + + WriteToDefaultReader (bytes, bytes_len); +} + +void +Debugger::DispatchInputInterrupt () +{ + m_input_reader_data.clear(); + + InputReaderSP reader_sp (GetCurrentInputReader ()); + if (reader_sp) + { + reader_sp->Notify (eInputReaderInterrupt); + + // If notifying the reader of the interrupt finished the reader, we should pop it off the stack. + while (CheckIfTopInputReaderIsDone ()) ; + } +} + +void +Debugger::DispatchInputEndOfFile () +{ + m_input_reader_data.clear(); + + InputReaderSP reader_sp (GetCurrentInputReader ()); + if (reader_sp) + { + reader_sp->Notify (eInputReaderEndOfFile); + + // If notifying the reader of the end-of-file finished the reader, we should pop it off the stack. + while (CheckIfTopInputReaderIsDone ()) ; + } +} + +void +Debugger::CleanUpInputReaders () +{ + m_input_reader_data.clear(); + + // The bottom input reader should be the main debugger input reader. We do not want to close that one here. + while (m_input_reader_stack.GetSize() > 1) + { + InputReaderSP reader_sp (GetCurrentInputReader ()); + if (reader_sp) + { + reader_sp->Notify (eInputReaderEndOfFile); + reader_sp->SetIsDone (true); + } + } +} + +void +Debugger::NotifyTopInputReader (InputReaderAction notification) +{ + InputReaderSP reader_sp (GetCurrentInputReader()); + if (reader_sp) + { + reader_sp->Notify (notification); + + // Flush out any input readers that are done. + while (CheckIfTopInputReaderIsDone ()) + /* Do nothing. */; + } +} + +bool +Debugger::InputReaderIsTopReader (const InputReaderSP& reader_sp) +{ + InputReaderSP top_reader_sp (GetCurrentInputReader()); + + return (reader_sp.get() == top_reader_sp.get()); +} + + +void +Debugger::WriteToDefaultReader (const char *bytes, size_t bytes_len) +{ + if (bytes && bytes_len) + m_input_reader_data.append (bytes, bytes_len); + + if (m_input_reader_data.empty()) + return; + + while (!m_input_reader_stack.IsEmpty() && !m_input_reader_data.empty()) + { + // Get the input reader from the top of the stack + InputReaderSP reader_sp (GetCurrentInputReader ()); + if (!reader_sp) + break; + + size_t bytes_handled = reader_sp->HandleRawBytes (m_input_reader_data.c_str(), + m_input_reader_data.size()); + if (bytes_handled) + { + m_input_reader_data.erase (0, bytes_handled); + } + else + { + // No bytes were handled, we might not have reached our + // granularity, just return and wait for more data + break; + } + } + + // Flush out any input readers that are done. + while (CheckIfTopInputReaderIsDone ()) + /* Do nothing. */; + +} + +void +Debugger::PushInputReader (const InputReaderSP& reader_sp) +{ + if (!reader_sp) + return; + + // Deactivate the old top reader + InputReaderSP top_reader_sp (GetCurrentInputReader ()); + + if (top_reader_sp) + top_reader_sp->Notify (eInputReaderDeactivate); + + m_input_reader_stack.Push (reader_sp); + reader_sp->Notify (eInputReaderActivate); + ActivateInputReader (reader_sp); +} + +bool +Debugger::PopInputReader (const InputReaderSP& pop_reader_sp) +{ + bool result = false; + + // The reader on the stop of the stack is done, so let the next + // read on the stack referesh its prompt and if there is one... + if (!m_input_reader_stack.IsEmpty()) + { + // Cannot call GetCurrentInputReader here, as that would cause an infinite loop. + InputReaderSP reader_sp(m_input_reader_stack.Top()); + + if (!pop_reader_sp || pop_reader_sp.get() == reader_sp.get()) + { + m_input_reader_stack.Pop (); + reader_sp->Notify (eInputReaderDeactivate); + reader_sp->Notify (eInputReaderDone); + result = true; + + if (!m_input_reader_stack.IsEmpty()) + { + reader_sp = m_input_reader_stack.Top(); + if (reader_sp) + { + ActivateInputReader (reader_sp); + reader_sp->Notify (eInputReaderReactivate); + } + } + } + } + return result; +} + +bool +Debugger::CheckIfTopInputReaderIsDone () +{ + bool result = false; + if (!m_input_reader_stack.IsEmpty()) + { + // Cannot call GetCurrentInputReader here, as that would cause an infinite loop. + InputReaderSP reader_sp(m_input_reader_stack.Top()); + + if (reader_sp && reader_sp->IsDone()) + { + result = true; + PopInputReader (reader_sp); + } + } + return result; +} + +void +Debugger::ActivateInputReader (const InputReaderSP &reader_sp) +{ + int input_fd = m_input_file.GetFile().GetDescriptor(); + + if (input_fd >= 0) + { + Terminal tty(input_fd); + + tty.SetEcho(reader_sp->GetEcho()); + + switch (reader_sp->GetGranularity()) + { + case eInputReaderGranularityByte: + case eInputReaderGranularityWord: + tty.SetCanonical (false); + break; + + case eInputReaderGranularityLine: + case eInputReaderGranularityAll: + tty.SetCanonical (true); + break; + + default: + break; + } + } +} + +StreamSP +Debugger::GetAsyncOutputStream () +{ + return StreamSP (new StreamAsynchronousIO (GetCommandInterpreter(), + CommandInterpreter::eBroadcastBitAsynchronousOutputData)); +} + +StreamSP +Debugger::GetAsyncErrorStream () +{ + return StreamSP (new StreamAsynchronousIO (GetCommandInterpreter(), + CommandInterpreter::eBroadcastBitAsynchronousErrorData)); +} + +size_t +Debugger::GetNumDebuggers() +{ + if (g_shared_debugger_refcount > 0) + { + Mutex::Locker locker (GetDebuggerListMutex ()); + return GetDebuggerList().size(); + } + return 0; +} + +lldb::DebuggerSP +Debugger::GetDebuggerAtIndex (size_t index) +{ + DebuggerSP debugger_sp; + + if (g_shared_debugger_refcount > 0) + { + Mutex::Locker locker (GetDebuggerListMutex ()); + DebuggerList &debugger_list = GetDebuggerList(); + + if (index < debugger_list.size()) + debugger_sp = debugger_list[index]; + } + + return debugger_sp; +} + +DebuggerSP +Debugger::FindDebuggerWithID (lldb::user_id_t id) +{ + DebuggerSP debugger_sp; + + if (g_shared_debugger_refcount > 0) + { + Mutex::Locker locker (GetDebuggerListMutex ()); + DebuggerList &debugger_list = GetDebuggerList(); + DebuggerList::iterator pos, end = debugger_list.end(); + for (pos = debugger_list.begin(); pos != end; ++pos) + { + if ((*pos).get()->GetID() == id) + { + debugger_sp = *pos; + break; + } + } + } + return debugger_sp; +} + +static void +TestPromptFormats (StackFrame *frame) +{ + if (frame == NULL) + return; + + StreamString s; + const char *prompt_format = + "{addr = '${addr}'\n}" + "{process.id = '${process.id}'\n}" + "{process.name = '${process.name}'\n}" + "{process.file.basename = '${process.file.basename}'\n}" + "{process.file.fullpath = '${process.file.fullpath}'\n}" + "{thread.id = '${thread.id}'\n}" + "{thread.index = '${thread.index}'\n}" + "{thread.name = '${thread.name}'\n}" + "{thread.queue = '${thread.queue}'\n}" + "{thread.stop-reason = '${thread.stop-reason}'\n}" + "{target.arch = '${target.arch}'\n}" + "{module.file.basename = '${module.file.basename}'\n}" + "{module.file.fullpath = '${module.file.fullpath}'\n}" + "{file.basename = '${file.basename}'\n}" + "{file.fullpath = '${file.fullpath}'\n}" + "{frame.index = '${frame.index}'\n}" + "{frame.pc = '${frame.pc}'\n}" + "{frame.sp = '${frame.sp}'\n}" + "{frame.fp = '${frame.fp}'\n}" + "{frame.flags = '${frame.flags}'\n}" + "{frame.reg.rdi = '${frame.reg.rdi}'\n}" + "{frame.reg.rip = '${frame.reg.rip}'\n}" + "{frame.reg.rsp = '${frame.reg.rsp}'\n}" + "{frame.reg.rbp = '${frame.reg.rbp}'\n}" + "{frame.reg.rflags = '${frame.reg.rflags}'\n}" + "{frame.reg.xmm0 = '${frame.reg.xmm0}'\n}" + "{frame.reg.carp = '${frame.reg.carp}'\n}" + "{function.id = '${function.id}'\n}" + "{function.name = '${function.name}'\n}" + "{function.name-with-args = '${function.name-with-args}'\n}" + "{function.addr-offset = '${function.addr-offset}'\n}" + "{function.line-offset = '${function.line-offset}'\n}" + "{function.pc-offset = '${function.pc-offset}'\n}" + "{line.file.basename = '${line.file.basename}'\n}" + "{line.file.fullpath = '${line.file.fullpath}'\n}" + "{line.number = '${line.number}'\n}" + "{line.start-addr = '${line.start-addr}'\n}" + "{line.end-addr = '${line.end-addr}'\n}" +; + + SymbolContext sc (frame->GetSymbolContext(eSymbolContextEverything)); + ExecutionContext exe_ctx; + frame->CalculateExecutionContext(exe_ctx); + if (Debugger::FormatPrompt (prompt_format, &sc, &exe_ctx, &sc.line_entry.range.GetBaseAddress(), s)) + { + printf("%s\n", s.GetData()); + } + else + { + printf ("what we got: %s\n", s.GetData()); + } +} + +static bool +ScanFormatDescriptor (const char* var_name_begin, + const char* var_name_end, + const char** var_name_final, + const char** percent_position, + Format* custom_format, + ValueObject::ValueObjectRepresentationStyle* val_obj_display) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_TYPES)); + *percent_position = ::strchr(var_name_begin,'%'); + if (!*percent_position || *percent_position > var_name_end) + { + if (log) + log->Printf("[ScanFormatDescriptor] no format descriptor in string, skipping"); + *var_name_final = var_name_end; + } + else + { + *var_name_final = *percent_position; + std::string format_name(*var_name_final+1, var_name_end-*var_name_final-1); + if (log) + log->Printf("[ScanFormatDescriptor] parsing %s as a format descriptor", format_name.c_str()); + if ( !FormatManager::GetFormatFromCString(format_name.c_str(), + true, + *custom_format) ) + { + if (log) + log->Printf("[ScanFormatDescriptor] %s is an unknown format", format_name.c_str()); + + switch (format_name.front()) + { + case '@': // if this is an @ sign, print ObjC description + *val_obj_display = ValueObject::eValueObjectRepresentationStyleLanguageSpecific; + break; + case 'V': // if this is a V, print the value using the default format + *val_obj_display = ValueObject::eValueObjectRepresentationStyleValue; + break; + case 'L': // if this is an L, print the location of the value + *val_obj_display = ValueObject::eValueObjectRepresentationStyleLocation; + break; + case 'S': // if this is an S, print the summary after all + *val_obj_display = ValueObject::eValueObjectRepresentationStyleSummary; + break; + case '#': // if this is a '#', print the number of children + *val_obj_display = ValueObject::eValueObjectRepresentationStyleChildrenCount; + break; + case 'T': // if this is a 'T', print the type + *val_obj_display = ValueObject::eValueObjectRepresentationStyleType; + break; + case 'N': // if this is a 'N', print the name + *val_obj_display = ValueObject::eValueObjectRepresentationStyleName; + break; + case '>': // if this is a '>', print the name + *val_obj_display = ValueObject::eValueObjectRepresentationStyleExpressionPath; + break; + default: + if (log) + log->Printf("ScanFormatDescriptor] %s is an error, leaving the previous value alone", format_name.c_str()); + break; + } + } + // a good custom format tells us to print the value using it + else + { + if (log) + log->Printf("[ScanFormatDescriptor] will display value for this VO"); + *val_obj_display = ValueObject::eValueObjectRepresentationStyleValue; + } + } + if (log) + log->Printf("[ScanFormatDescriptor] final format description outcome: custom_format = %d, val_obj_display = %d", + *custom_format, + *val_obj_display); + return true; +} + +static bool +ScanBracketedRange (const char* var_name_begin, + const char* var_name_end, + const char* var_name_final, + const char** open_bracket_position, + const char** separator_position, + const char** close_bracket_position, + const char** var_name_final_if_array_range, + int64_t* index_lower, + int64_t* index_higher) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_TYPES)); + *open_bracket_position = ::strchr(var_name_begin,'['); + if (*open_bracket_position && *open_bracket_position < var_name_final) + { + *separator_position = ::strchr(*open_bracket_position,'-'); // might be NULL if this is a simple var[N] bitfield + *close_bracket_position = ::strchr(*open_bracket_position,']'); + // as usual, we assume that [] will come before % + //printf("trying to expand a []\n"); + *var_name_final_if_array_range = *open_bracket_position; + if (*close_bracket_position - *open_bracket_position == 1) + { + if (log) + log->Printf("[ScanBracketedRange] '[]' detected.. going from 0 to end of data"); + *index_lower = 0; + } + else if (*separator_position == NULL || *separator_position > var_name_end) + { + char *end = NULL; + *index_lower = ::strtoul (*open_bracket_position+1, &end, 0); + *index_higher = *index_lower; + if (log) + log->Printf("[ScanBracketedRange] [%" PRId64 "] detected, high index is same", *index_lower); + } + else if (*close_bracket_position && *close_bracket_position < var_name_end) + { + char *end = NULL; + *index_lower = ::strtoul (*open_bracket_position+1, &end, 0); + *index_higher = ::strtoul (*separator_position+1, &end, 0); + if (log) + log->Printf("[ScanBracketedRange] [%" PRId64 "-%" PRId64 "] detected", *index_lower, *index_higher); + } + else + { + if (log) + log->Printf("[ScanBracketedRange] expression is erroneous, cannot extract indices out of it"); + return false; + } + if (*index_lower > *index_higher && *index_higher > 0) + { + if (log) + log->Printf("[ScanBracketedRange] swapping indices"); + int64_t temp = *index_lower; + *index_lower = *index_higher; + *index_higher = temp; + } + } + else if (log) + log->Printf("[ScanBracketedRange] no bracketed range, skipping entirely"); + return true; +} + +template +static bool RunScriptFormatKeyword(Stream &s, ScriptInterpreter *script_interpreter, T t, const std::string& script_name) +{ + if (script_interpreter) + { + Error script_error; + std::string script_output; + + if (script_interpreter->RunScriptFormatKeyword(script_name.c_str(), t, script_output, script_error) && script_error.Success()) + { + s.Printf("%s", script_output.c_str()); + return true; + } + else + { + s.Printf("",script_error.AsCString()); + } + } + return false; +} + +static ValueObjectSP +ExpandIndexedExpression (ValueObject* valobj, + size_t index, + StackFrame* frame, + bool deref_pointer) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_TYPES)); + const char* ptr_deref_format = "[%d]"; + std::string ptr_deref_buffer(10,0); + ::sprintf(&ptr_deref_buffer[0], ptr_deref_format, index); + if (log) + log->Printf("[ExpandIndexedExpression] name to deref: %s",ptr_deref_buffer.c_str()); + const char* first_unparsed; + ValueObject::GetValueForExpressionPathOptions options; + ValueObject::ExpressionPathEndResultType final_value_type; + ValueObject::ExpressionPathScanEndReason reason_to_stop; + ValueObject::ExpressionPathAftermath what_next = (deref_pointer ? ValueObject::eExpressionPathAftermathDereference : ValueObject::eExpressionPathAftermathNothing); + ValueObjectSP item = valobj->GetValueForExpressionPath (ptr_deref_buffer.c_str(), + &first_unparsed, + &reason_to_stop, + &final_value_type, + options, + &what_next); + if (!item) + { + if (log) + log->Printf("[ExpandIndexedExpression] ERROR: unparsed portion = %s, why stopping = %d," + " final_value_type %d", + first_unparsed, reason_to_stop, final_value_type); + } + else + { + if (log) + log->Printf("[ExpandIndexedExpression] ALL RIGHT: unparsed portion = %s, why stopping = %d," + " final_value_type %d", + first_unparsed, reason_to_stop, final_value_type); + } + return item; +} + +static inline bool +IsToken(const char *var_name_begin, const char *var) +{ + return (::strncmp (var_name_begin, var, strlen(var)) == 0); +} + +static bool +IsTokenWithFormat(const char *var_name_begin, const char *var, std::string &format, const char *default_format, + const ExecutionContext *exe_ctx_ptr, const SymbolContext *sc_ptr) +{ + int var_len = strlen(var); + if (::strncmp (var_name_begin, var, var_len) == 0) + { + var_name_begin += var_len; + if (*var_name_begin == '}') + { + format = default_format; + return true; + } + else if (*var_name_begin == '%') + { + // Allow format specifiers: x|X|u with optional width specifiers. + // ${thread.id%x} ; hex + // ${thread.id%X} ; uppercase hex + // ${thread.id%u} ; unsigned decimal + // ${thread.id%8.8X} ; width.precision + specifier + // ${thread.id%tid} ; unsigned on FreeBSD/Linux, otherwise default_format (0x%4.4x for thread.id) + int dot_count = 0; + const char *specifier = NULL; + int width_precision_length = 0; + const char *width_precision = ++var_name_begin; + while (isdigit(*var_name_begin) || *var_name_begin == '.') + { + dot_count += (*var_name_begin == '.'); + if (dot_count > 1) + break; + var_name_begin++; + width_precision_length++; + } + + if (IsToken (var_name_begin, "tid}")) + { + Target *target = Target::GetTargetFromContexts (exe_ctx_ptr, sc_ptr); + if (target) + { + ArchSpec arch (target->GetArchitecture ()); + llvm::Triple::OSType ostype = arch.IsValid() ? arch.GetTriple().getOS() : llvm::Triple::UnknownOS; + if ((ostype == llvm::Triple::FreeBSD) || (ostype == llvm::Triple::Linux)) + specifier = PRIu64; + } + if (!specifier) + { + format = default_format; + return true; + } + } + else if (IsToken (var_name_begin, "x}")) + specifier = PRIx64; + else if (IsToken (var_name_begin, "X}")) + specifier = PRIX64; + else if (IsToken (var_name_begin, "u}")) + specifier = PRIu64; + + if (specifier) + { + format = "%"; + if (width_precision_length) + format += std::string(width_precision, width_precision_length); + format += specifier; + return true; + } + } + } + return false; +} + +static bool +FormatPromptRecurse +( + const char *format, + const SymbolContext *sc, + const ExecutionContext *exe_ctx, + const Address *addr, + Stream &s, + const char **end, + ValueObject* valobj +) +{ + ValueObject* realvalobj = NULL; // makes it super-easy to parse pointers + bool success = true; + const char *p; + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_TYPES)); + + for (p = format; *p != '\0'; ++p) + { + if (realvalobj) + { + valobj = realvalobj; + realvalobj = NULL; + } + size_t non_special_chars = ::strcspn (p, "${}\\"); + if (non_special_chars > 0) + { + if (success) + s.Write (p, non_special_chars); + p += non_special_chars; + } + + if (*p == '\0') + { + break; + } + else if (*p == '{') + { + // Start a new scope that must have everything it needs if it is to + // to make it into the final output stream "s". If you want to make + // a format that only prints out the function or symbol name if there + // is one in the symbol context you can use: + // "{function =${function.name}}" + // The first '{' starts a new scope that end with the matching '}' at + // the end of the string. The contents "function =${function.name}" + // will then be evaluated and only be output if there is a function + // or symbol with a valid name. + StreamString sub_strm; + + ++p; // Skip the '{' + + if (FormatPromptRecurse (p, sc, exe_ctx, addr, sub_strm, &p, valobj)) + { + // The stream had all it needed + s.Write(sub_strm.GetData(), sub_strm.GetSize()); + } + if (*p != '}') + { + success = false; + break; + } + } + else if (*p == '}') + { + // End of a enclosing scope + break; + } + else if (*p == '$') + { + // We have a prompt variable to print + ++p; + if (*p == '{') + { + ++p; + const char *var_name_begin = p; + const char *var_name_end = ::strchr (p, '}'); + + if (var_name_end && var_name_begin < var_name_end) + { + // if we have already failed to parse, skip this variable + if (success) + { + const char *cstr = NULL; + std::string token_format; + Address format_addr; + bool calculate_format_addr_function_offset = false; + // Set reg_kind and reg_num to invalid values + RegisterKind reg_kind = kNumRegisterKinds; + uint32_t reg_num = LLDB_INVALID_REGNUM; + FileSpec format_file_spec; + const RegisterInfo *reg_info = NULL; + RegisterContext *reg_ctx = NULL; + bool do_deref_pointer = false; + ValueObject::ExpressionPathScanEndReason reason_to_stop = ValueObject::eExpressionPathScanEndReasonEndOfString; + ValueObject::ExpressionPathEndResultType final_value_type = ValueObject::eExpressionPathEndResultTypePlain; + + // Each variable must set success to true below... + bool var_success = false; + switch (var_name_begin[0]) + { + case '*': + case 'v': + case 's': + { + if (!valobj) + break; + + if (log) + log->Printf("[Debugger::FormatPrompt] initial string: %s",var_name_begin); + + // check for *var and *svar + if (*var_name_begin == '*') + { + do_deref_pointer = true; + var_name_begin++; + if (log) + log->Printf("[Debugger::FormatPrompt] found a deref, new string is: %s",var_name_begin); + } + + if (*var_name_begin == 's') + { + if (!valobj->IsSynthetic()) + valobj = valobj->GetSyntheticValue().get(); + if (!valobj) + break; + var_name_begin++; + if (log) + log->Printf("[Debugger::FormatPrompt] found a synthetic, new string is: %s",var_name_begin); + } + + // should be a 'v' by now + if (*var_name_begin != 'v') + break; + + if (log) + log->Printf("[Debugger::FormatPrompt] string I am working with: %s",var_name_begin); + + ValueObject::ExpressionPathAftermath what_next = (do_deref_pointer ? + ValueObject::eExpressionPathAftermathDereference : ValueObject::eExpressionPathAftermathNothing); + ValueObject::GetValueForExpressionPathOptions options; + options.DontCheckDotVsArrowSyntax().DoAllowBitfieldSyntax().DoAllowFragileIVar().DoAllowSyntheticChildren(); + ValueObject::ValueObjectRepresentationStyle val_obj_display = ValueObject::eValueObjectRepresentationStyleSummary; + ValueObject* target = NULL; + Format custom_format = eFormatInvalid; + const char* var_name_final = NULL; + const char* var_name_final_if_array_range = NULL; + const char* close_bracket_position = NULL; + int64_t index_lower = -1; + int64_t index_higher = -1; + bool is_array_range = false; + const char* first_unparsed; + bool was_plain_var = false; + bool was_var_format = false; + bool was_var_indexed = false; + + if (!valobj) break; + // simplest case ${var}, just print valobj's value + if (IsToken (var_name_begin, "var}")) + { + was_plain_var = true; + target = valobj; + val_obj_display = ValueObject::eValueObjectRepresentationStyleValue; + } + else if (IsToken (var_name_begin,"var%")) + { + was_var_format = true; + // this is a variable with some custom format applied to it + const char* percent_position; + target = valobj; + val_obj_display = ValueObject::eValueObjectRepresentationStyleValue; + ScanFormatDescriptor (var_name_begin, + var_name_end, + &var_name_final, + &percent_position, + &custom_format, + &val_obj_display); + } + // this is ${var.something} or multiple .something nested + else if (IsToken (var_name_begin, "var")) + { + if (IsToken (var_name_begin, "var[")) + was_var_indexed = true; + const char* percent_position; + ScanFormatDescriptor (var_name_begin, + var_name_end, + &var_name_final, + &percent_position, + &custom_format, + &val_obj_display); + + const char* open_bracket_position; + const char* separator_position; + ScanBracketedRange (var_name_begin, + var_name_end, + var_name_final, + &open_bracket_position, + &separator_position, + &close_bracket_position, + &var_name_final_if_array_range, + &index_lower, + &index_higher); + + Error error; + + std::string expr_path(var_name_final-var_name_begin-1,0); + memcpy(&expr_path[0], var_name_begin+3,var_name_final-var_name_begin-3); + + if (log) + log->Printf("[Debugger::FormatPrompt] symbol to expand: %s",expr_path.c_str()); + + target = valobj->GetValueForExpressionPath(expr_path.c_str(), + &first_unparsed, + &reason_to_stop, + &final_value_type, + options, + &what_next).get(); + + if (!target) + { + if (log) + log->Printf("[Debugger::FormatPrompt] ERROR: unparsed portion = %s, why stopping = %d," + " final_value_type %d", + first_unparsed, reason_to_stop, final_value_type); + break; + } + else + { + if (log) + log->Printf("[Debugger::FormatPrompt] ALL RIGHT: unparsed portion = %s, why stopping = %d," + " final_value_type %d", + first_unparsed, reason_to_stop, final_value_type); + } + } + else + break; + + is_array_range = (final_value_type == ValueObject::eExpressionPathEndResultTypeBoundedRange || + final_value_type == ValueObject::eExpressionPathEndResultTypeUnboundedRange); + + do_deref_pointer = (what_next == ValueObject::eExpressionPathAftermathDereference); + + if (do_deref_pointer && !is_array_range) + { + // I have not deref-ed yet, let's do it + // this happens when we are not going through GetValueForVariableExpressionPath + // to get to the target ValueObject + Error error; + target = target->Dereference(error).get(); + if (error.Fail()) + { + if (log) + log->Printf("[Debugger::FormatPrompt] ERROR: %s\n", error.AsCString("unknown")); \ + break; + } + do_deref_pointer = false; + } + + // + // we do not want to use the summary for a bitfield of type T:n + // if we were originally dealing with just a T - that would get + // us into an endless recursion + if (target->IsBitfield() && was_var_indexed) + { + // TODO: check for a (T:n)-specific summary - we should still obey that + StreamString bitfield_name; + bitfield_name.Printf("%s:%d", target->GetTypeName().AsCString(), target->GetBitfieldBitSize()); + lldb::TypeNameSpecifierImplSP type_sp(new TypeNameSpecifierImpl(bitfield_name.GetData(),false)); + if (!DataVisualization::GetSummaryForType(type_sp)) + val_obj_display = ValueObject::eValueObjectRepresentationStyleValue; + } + + // TODO use flags for these + const uint32_t type_info_flags = target->GetClangType().GetTypeInfo(NULL); + bool is_array = (type_info_flags & ClangASTType::eTypeIsArray) != 0; + bool is_pointer = (type_info_flags & ClangASTType::eTypeIsPointer) != 0; + bool is_aggregate = target->GetClangType().IsAggregateType(); + + if ((is_array || is_pointer) && (!is_array_range) && val_obj_display == ValueObject::eValueObjectRepresentationStyleValue) // this should be wrong, but there are some exceptions + { + StreamString str_temp; + if (log) + log->Printf("[Debugger::FormatPrompt] I am into array || pointer && !range"); + + if (target->HasSpecialPrintableRepresentation(val_obj_display, custom_format)) + { + // try to use the special cases + var_success = target->DumpPrintableRepresentation(str_temp, + val_obj_display, + custom_format); + if (log) + log->Printf("[Debugger::FormatPrompt] special cases did%s match", var_success ? "" : "n't"); + + // should not happen + if (var_success) + s << str_temp.GetData(); + var_success = true; + break; + } + else + { + if (was_plain_var) // if ${var} + { + s << target->GetTypeName() << " @ " << target->GetLocationAsCString(); + } + else if (is_pointer) // if pointer, value is the address stored + { + target->DumpPrintableRepresentation (s, + val_obj_display, + custom_format, + ValueObject::ePrintableRepresentationSpecialCasesDisable); + } + var_success = true; + break; + } + } + + // if directly trying to print ${var}, and this is an aggregate, display a nice + // type @ location message + if (is_aggregate && was_plain_var) + { + s << target->GetTypeName() << " @ " << target->GetLocationAsCString(); + var_success = true; + break; + } + + // if directly trying to print ${var%V}, and this is an aggregate, do not let the user do it + if (is_aggregate && ((was_var_format && val_obj_display == ValueObject::eValueObjectRepresentationStyleValue))) + { + s << ""; + var_success = true; + break; + } + + if (!is_array_range) + { + if (log) + log->Printf("[Debugger::FormatPrompt] dumping ordinary printable output"); + var_success = target->DumpPrintableRepresentation(s,val_obj_display, custom_format); + } + else + { + if (log) + log->Printf("[Debugger::FormatPrompt] checking if I can handle as array"); + if (!is_array && !is_pointer) + break; + if (log) + log->Printf("[Debugger::FormatPrompt] handle as array"); + const char* special_directions = NULL; + StreamString special_directions_writer; + if (close_bracket_position && (var_name_end-close_bracket_position > 1)) + { + ConstString additional_data; + additional_data.SetCStringWithLength(close_bracket_position+1, var_name_end-close_bracket_position-1); + special_directions_writer.Printf("${%svar%s}", + do_deref_pointer ? "*" : "", + additional_data.GetCString()); + special_directions = special_directions_writer.GetData(); + } + + // let us display items index_lower thru index_higher of this array + s.PutChar('['); + var_success = true; + + if (index_higher < 0) + index_higher = valobj->GetNumChildren() - 1; + + uint32_t max_num_children = target->GetTargetSP()->GetMaximumNumberOfChildrenToDisplay(); + + for (;index_lower<=index_higher;index_lower++) + { + ValueObject* item = ExpandIndexedExpression (target, + index_lower, + exe_ctx->GetFramePtr(), + false).get(); + + if (!item) + { + if (log) + log->Printf("[Debugger::FormatPrompt] ERROR in getting child item at index %" PRId64, index_lower); + } + else + { + if (log) + log->Printf("[Debugger::FormatPrompt] special_directions for child item: %s",special_directions); + } + + if (!special_directions) + var_success &= item->DumpPrintableRepresentation(s,val_obj_display, custom_format); + else + var_success &= FormatPromptRecurse(special_directions, sc, exe_ctx, addr, s, NULL, item); + + if (--max_num_children == 0) + { + s.PutCString(", ..."); + break; + } + + if (index_lower < index_higher) + s.PutChar(','); + } + s.PutChar(']'); + } + } + break; + case 'a': + if (IsToken (var_name_begin, "addr}")) + { + if (addr && addr->IsValid()) + { + var_success = true; + format_addr = *addr; + } + } + break; + + case 'p': + if (IsToken (var_name_begin, "process.")) + { + if (exe_ctx) + { + Process *process = exe_ctx->GetProcessPtr(); + if (process) + { + var_name_begin += ::strlen ("process."); + if (IsTokenWithFormat (var_name_begin, "id", token_format, "%" PRIu64, exe_ctx, sc)) + { + s.Printf(token_format.c_str(), process->GetID()); + var_success = true; + } + else if ((IsToken (var_name_begin, "name}")) || + (IsToken (var_name_begin, "file.basename}")) || + (IsToken (var_name_begin, "file.fullpath}"))) + { + Module *exe_module = process->GetTarget().GetExecutableModulePointer(); + if (exe_module) + { + if (var_name_begin[0] == 'n' || var_name_begin[5] == 'f') + { + format_file_spec.GetFilename() = exe_module->GetFileSpec().GetFilename(); + var_success = format_file_spec; + } + else + { + format_file_spec = exe_module->GetFileSpec(); + var_success = format_file_spec; + } + } + } + else if (IsToken (var_name_begin, "script:")) + { + var_name_begin += ::strlen("script:"); + std::string script_name(var_name_begin,var_name_end); + ScriptInterpreter* script_interpreter = process->GetTarget().GetDebugger().GetCommandInterpreter().GetScriptInterpreter(); + if (RunScriptFormatKeyword (s, script_interpreter, process, script_name)) + var_success = true; + } + } + } + } + break; + + case 't': + if (IsToken (var_name_begin, "thread.")) + { + if (exe_ctx) + { + Thread *thread = exe_ctx->GetThreadPtr(); + if (thread) + { + var_name_begin += ::strlen ("thread."); + if (IsTokenWithFormat (var_name_begin, "id", token_format, "0x%4.4" PRIx64, exe_ctx, sc)) + { + s.Printf(token_format.c_str(), thread->GetID()); + var_success = true; + } + else if (IsTokenWithFormat (var_name_begin, "protocol_id", token_format, "0x%4.4" PRIx64, exe_ctx, sc)) + { + s.Printf(token_format.c_str(), thread->GetProtocolID()); + var_success = true; + } + else if (IsTokenWithFormat (var_name_begin, "index", token_format, "%" PRIu64, exe_ctx, sc)) + { + s.Printf(token_format.c_str(), (uint64_t)thread->GetIndexID()); + var_success = true; + } + else if (IsToken (var_name_begin, "name}")) + { + cstr = thread->GetName(); + var_success = cstr && cstr[0]; + if (var_success) + s.PutCString(cstr); + } + else if (IsToken (var_name_begin, "queue}")) + { + cstr = thread->GetQueueName(); + var_success = cstr && cstr[0]; + if (var_success) + s.PutCString(cstr); + } + else if (IsToken (var_name_begin, "stop-reason}")) + { + StopInfoSP stop_info_sp = thread->GetStopInfo (); + if (stop_info_sp && stop_info_sp->IsValid()) + { + cstr = stop_info_sp->GetDescription(); + if (cstr && cstr[0]) + { + s.PutCString(cstr); + var_success = true; + } + } + } + else if (IsToken (var_name_begin, "return-value}")) + { + StopInfoSP stop_info_sp = thread->GetStopInfo (); + if (stop_info_sp && stop_info_sp->IsValid()) + { + ValueObjectSP return_valobj_sp = StopInfo::GetReturnValueObject (stop_info_sp); + if (return_valobj_sp) + { + ValueObject::DumpValueObject (s, return_valobj_sp.get()); + var_success = true; + } + } + } + else if (IsToken (var_name_begin, "script:")) + { + var_name_begin += ::strlen("script:"); + std::string script_name(var_name_begin,var_name_end); + ScriptInterpreter* script_interpreter = thread->GetProcess()->GetTarget().GetDebugger().GetCommandInterpreter().GetScriptInterpreter(); + if (RunScriptFormatKeyword (s, script_interpreter, thread, script_name)) + var_success = true; + } + } + } + } + else if (IsToken (var_name_begin, "target.")) + { + // TODO: hookup properties +// if (!target_properties_sp) +// { +// Target *target = Target::GetTargetFromContexts (exe_ctx, sc); +// if (target) +// target_properties_sp = target->GetProperties(); +// } +// +// if (target_properties_sp) +// { +// var_name_begin += ::strlen ("target."); +// const char *end_property = strchr(var_name_begin, '}'); +// if (end_property) +// { +// ConstString property_name(var_name_begin, end_property - var_name_begin); +// std::string property_value (target_properties_sp->GetPropertyValue(property_name)); +// if (!property_value.empty()) +// { +// s.PutCString (property_value.c_str()); +// var_success = true; +// } +// } +// } + Target *target = Target::GetTargetFromContexts (exe_ctx, sc); + if (target) + { + var_name_begin += ::strlen ("target."); + if (IsToken (var_name_begin, "arch}")) + { + ArchSpec arch (target->GetArchitecture ()); + if (arch.IsValid()) + { + s.PutCString (arch.GetArchitectureName()); + var_success = true; + } + } + else if (IsToken (var_name_begin, "script:")) + { + var_name_begin += ::strlen("script:"); + std::string script_name(var_name_begin,var_name_end); + ScriptInterpreter* script_interpreter = target->GetDebugger().GetCommandInterpreter().GetScriptInterpreter(); + if (RunScriptFormatKeyword (s, script_interpreter, target, script_name)) + var_success = true; + } + } + } + break; + + + case 'm': + if (IsToken (var_name_begin, "module.")) + { + if (sc && sc->module_sp.get()) + { + Module *module = sc->module_sp.get(); + var_name_begin += ::strlen ("module."); + + if (IsToken (var_name_begin, "file.")) + { + if (module->GetFileSpec()) + { + var_name_begin += ::strlen ("file."); + + if (IsToken (var_name_begin, "basename}")) + { + format_file_spec.GetFilename() = module->GetFileSpec().GetFilename(); + var_success = format_file_spec; + } + else if (IsToken (var_name_begin, "fullpath}")) + { + format_file_spec = module->GetFileSpec(); + var_success = format_file_spec; + } + } + } + } + } + break; + + + case 'f': + if (IsToken (var_name_begin, "file.")) + { + if (sc && sc->comp_unit != NULL) + { + var_name_begin += ::strlen ("file."); + + if (IsToken (var_name_begin, "basename}")) + { + format_file_spec.GetFilename() = sc->comp_unit->GetFilename(); + var_success = format_file_spec; + } + else if (IsToken (var_name_begin, "fullpath}")) + { + format_file_spec = *sc->comp_unit; + var_success = format_file_spec; + } + } + } + else if (IsToken (var_name_begin, "frame.")) + { + if (exe_ctx) + { + StackFrame *frame = exe_ctx->GetFramePtr(); + if (frame) + { + var_name_begin += ::strlen ("frame."); + if (IsToken (var_name_begin, "index}")) + { + s.Printf("%u", frame->GetFrameIndex()); + var_success = true; + } + else if (IsToken (var_name_begin, "pc}")) + { + reg_kind = eRegisterKindGeneric; + reg_num = LLDB_REGNUM_GENERIC_PC; + var_success = true; + } + else if (IsToken (var_name_begin, "sp}")) + { + reg_kind = eRegisterKindGeneric; + reg_num = LLDB_REGNUM_GENERIC_SP; + var_success = true; + } + else if (IsToken (var_name_begin, "fp}")) + { + reg_kind = eRegisterKindGeneric; + reg_num = LLDB_REGNUM_GENERIC_FP; + var_success = true; + } + else if (IsToken (var_name_begin, "flags}")) + { + reg_kind = eRegisterKindGeneric; + reg_num = LLDB_REGNUM_GENERIC_FLAGS; + var_success = true; + } + else if (IsToken (var_name_begin, "reg.")) + { + reg_ctx = frame->GetRegisterContext().get(); + if (reg_ctx) + { + var_name_begin += ::strlen ("reg."); + if (var_name_begin < var_name_end) + { + std::string reg_name (var_name_begin, var_name_end); + reg_info = reg_ctx->GetRegisterInfoByName (reg_name.c_str()); + if (reg_info) + var_success = true; + } + } + } + else if (IsToken (var_name_begin, "script:")) + { + var_name_begin += ::strlen("script:"); + std::string script_name(var_name_begin,var_name_end); + ScriptInterpreter* script_interpreter = frame->GetThread()->GetProcess()->GetTarget().GetDebugger().GetCommandInterpreter().GetScriptInterpreter(); + if (RunScriptFormatKeyword (s, script_interpreter, frame, script_name)) + var_success = true; + } + } + } + } + else if (IsToken (var_name_begin, "function.")) + { + if (sc && (sc->function != NULL || sc->symbol != NULL)) + { + var_name_begin += ::strlen ("function."); + if (IsToken (var_name_begin, "id}")) + { + if (sc->function) + s.Printf("function{0x%8.8" PRIx64 "}", sc->function->GetID()); + else + s.Printf("symbol[%u]", sc->symbol->GetID()); + + var_success = true; + } + else if (IsToken (var_name_begin, "name}")) + { + if (sc->function) + cstr = sc->function->GetName().AsCString (NULL); + else if (sc->symbol) + cstr = sc->symbol->GetName().AsCString (NULL); + if (cstr) + { + s.PutCString(cstr); + + if (sc->block) + { + Block *inline_block = sc->block->GetContainingInlinedBlock (); + if (inline_block) + { + const InlineFunctionInfo *inline_info = sc->block->GetInlinedFunctionInfo(); + if (inline_info) + { + s.PutCString(" [inlined] "); + inline_info->GetName().Dump(&s); + } + } + } + var_success = true; + } + } + else if (IsToken (var_name_begin, "name-with-args}")) + { + // Print the function name with arguments in it + + if (sc->function) + { + var_success = true; + ExecutionContextScope *exe_scope = exe_ctx ? exe_ctx->GetBestExecutionContextScope() : NULL; + cstr = sc->function->GetName().AsCString (NULL); + if (cstr) + { + const InlineFunctionInfo *inline_info = NULL; + VariableListSP variable_list_sp; + bool get_function_vars = true; + if (sc->block) + { + Block *inline_block = sc->block->GetContainingInlinedBlock (); + + if (inline_block) + { + get_function_vars = false; + inline_info = sc->block->GetInlinedFunctionInfo(); + if (inline_info) + variable_list_sp = inline_block->GetBlockVariableList (true); + } + } + + if (get_function_vars) + { + variable_list_sp = sc->function->GetBlock(true).GetBlockVariableList (true); + } + + if (inline_info) + { + s.PutCString (cstr); + s.PutCString (" [inlined] "); + cstr = inline_info->GetName().GetCString(); + } + + VariableList args; + if (variable_list_sp) + variable_list_sp->AppendVariablesWithScope(eValueTypeVariableArgument, args); + if (args.GetSize() > 0) + { + const char *open_paren = strchr (cstr, '('); + const char *close_paren = NULL; + if (open_paren) + { + if (IsToken (open_paren, "(anonymous namespace)")) + { + open_paren = strchr (open_paren + strlen("(anonymous namespace)"), '('); + if (open_paren) + close_paren = strchr (open_paren, ')'); + } + else + close_paren = strchr (open_paren, ')'); + } + + if (open_paren) + s.Write(cstr, open_paren - cstr + 1); + else + { + s.PutCString (cstr); + s.PutChar ('('); + } + const size_t num_args = args.GetSize(); + for (size_t arg_idx = 0; arg_idx < num_args; ++arg_idx) + { + VariableSP var_sp (args.GetVariableAtIndex (arg_idx)); + ValueObjectSP var_value_sp (ValueObjectVariable::Create (exe_scope, var_sp)); + const char *var_name = var_value_sp->GetName().GetCString(); + const char *var_value = var_value_sp->GetValueAsCString(); + if (arg_idx > 0) + s.PutCString (", "); + if (var_value_sp->GetError().Success()) + { + if (var_value) + s.Printf ("%s=%s", var_name, var_value); + else + s.Printf ("%s=%s at %s", var_name, var_value_sp->GetTypeName().GetCString(), var_value_sp->GetLocationAsCString()); + } + else + s.Printf ("%s=", var_name); + } + + if (close_paren) + s.PutCString (close_paren); + else + s.PutChar(')'); + + } + else + { + s.PutCString(cstr); + } + } + } + else if (sc->symbol) + { + cstr = sc->symbol->GetName().AsCString (NULL); + if (cstr) + { + s.PutCString(cstr); + var_success = true; + } + } + } + else if (IsToken (var_name_begin, "addr-offset}")) + { + var_success = addr != NULL; + if (var_success) + { + format_addr = *addr; + calculate_format_addr_function_offset = true; + } + } + else if (IsToken (var_name_begin, "line-offset}")) + { + var_success = sc->line_entry.range.GetBaseAddress().IsValid(); + if (var_success) + { + format_addr = sc->line_entry.range.GetBaseAddress(); + calculate_format_addr_function_offset = true; + } + } + else if (IsToken (var_name_begin, "pc-offset}")) + { + StackFrame *frame = exe_ctx->GetFramePtr(); + var_success = frame != NULL; + if (var_success) + { + format_addr = frame->GetFrameCodeAddress(); + calculate_format_addr_function_offset = true; + } + } + } + } + break; + + case 'l': + if (IsToken (var_name_begin, "line.")) + { + if (sc && sc->line_entry.IsValid()) + { + var_name_begin += ::strlen ("line."); + if (IsToken (var_name_begin, "file.")) + { + var_name_begin += ::strlen ("file."); + + if (IsToken (var_name_begin, "basename}")) + { + format_file_spec.GetFilename() = sc->line_entry.file.GetFilename(); + var_success = format_file_spec; + } + else if (IsToken (var_name_begin, "fullpath}")) + { + format_file_spec = sc->line_entry.file; + var_success = format_file_spec; + } + } + else if (IsTokenWithFormat (var_name_begin, "number", token_format, "%" PRIu64, exe_ctx, sc)) + { + var_success = true; + s.Printf(token_format.c_str(), (uint64_t)sc->line_entry.line); + } + else if ((IsToken (var_name_begin, "start-addr}")) || + (IsToken (var_name_begin, "end-addr}"))) + { + var_success = sc && sc->line_entry.range.GetBaseAddress().IsValid(); + if (var_success) + { + format_addr = sc->line_entry.range.GetBaseAddress(); + if (var_name_begin[0] == 'e') + format_addr.Slide (sc->line_entry.range.GetByteSize()); + } + } + } + } + break; + } + + if (var_success) + { + // If format addr is valid, then we need to print an address + if (reg_num != LLDB_INVALID_REGNUM) + { + StackFrame *frame = exe_ctx->GetFramePtr(); + // We have a register value to display... + if (reg_num == LLDB_REGNUM_GENERIC_PC && reg_kind == eRegisterKindGeneric) + { + format_addr = frame->GetFrameCodeAddress(); + } + else + { + if (reg_ctx == NULL) + reg_ctx = frame->GetRegisterContext().get(); + + if (reg_ctx) + { + if (reg_kind != kNumRegisterKinds) + reg_num = reg_ctx->ConvertRegisterKindToRegisterNumber(reg_kind, reg_num); + reg_info = reg_ctx->GetRegisterInfoAtIndex (reg_num); + var_success = reg_info != NULL; + } + } + } + + if (reg_info != NULL) + { + RegisterValue reg_value; + var_success = reg_ctx->ReadRegister (reg_info, reg_value); + if (var_success) + { + reg_value.Dump(&s, reg_info, false, false, eFormatDefault); + } + } + + if (format_file_spec) + { + s << format_file_spec; + } + + // If format addr is valid, then we need to print an address + if (format_addr.IsValid()) + { + var_success = false; + + if (calculate_format_addr_function_offset) + { + Address func_addr; + + if (sc) + { + if (sc->function) + { + func_addr = sc->function->GetAddressRange().GetBaseAddress(); + if (sc->block) + { + // Check to make sure we aren't in an inline + // function. If we are, use the inline block + // range that contains "format_addr" since + // blocks can be discontiguous. + Block *inline_block = sc->block->GetContainingInlinedBlock (); + AddressRange inline_range; + if (inline_block && inline_block->GetRangeContainingAddress (format_addr, inline_range)) + func_addr = inline_range.GetBaseAddress(); + } + } + else if (sc->symbol && sc->symbol->ValueIsAddress()) + func_addr = sc->symbol->GetAddress(); + } + + if (func_addr.IsValid()) + { + if (func_addr.GetSection() == format_addr.GetSection()) + { + addr_t func_file_addr = func_addr.GetFileAddress(); + addr_t addr_file_addr = format_addr.GetFileAddress(); + if (addr_file_addr > func_file_addr) + s.Printf(" + %" PRIu64, addr_file_addr - func_file_addr); + else if (addr_file_addr < func_file_addr) + s.Printf(" - %" PRIu64, func_file_addr - addr_file_addr); + var_success = true; + } + else + { + Target *target = Target::GetTargetFromContexts (exe_ctx, sc); + if (target) + { + addr_t func_load_addr = func_addr.GetLoadAddress (target); + addr_t addr_load_addr = format_addr.GetLoadAddress (target); + if (addr_load_addr > func_load_addr) + s.Printf(" + %" PRIu64, addr_load_addr - func_load_addr); + else if (addr_load_addr < func_load_addr) + s.Printf(" - %" PRIu64, func_load_addr - addr_load_addr); + var_success = true; + } + } + } + } + else + { + Target *target = Target::GetTargetFromContexts (exe_ctx, sc); + addr_t vaddr = LLDB_INVALID_ADDRESS; + if (exe_ctx && !target->GetSectionLoadList().IsEmpty()) + vaddr = format_addr.GetLoadAddress (target); + if (vaddr == LLDB_INVALID_ADDRESS) + vaddr = format_addr.GetFileAddress (); + + if (vaddr != LLDB_INVALID_ADDRESS) + { + int addr_width = target->GetArchitecture().GetAddressByteSize() * 2; + if (addr_width == 0) + addr_width = 16; + s.Printf("0x%*.*" PRIx64, addr_width, addr_width, vaddr); + var_success = true; + } + } + } + } + + if (var_success == false) + success = false; + } + p = var_name_end; + } + else + break; + } + else + { + // We got a dollar sign with no '{' after it, it must just be a dollar sign + s.PutChar(*p); + } + } + else if (*p == '\\') + { + ++p; // skip the slash + switch (*p) + { + case 'a': s.PutChar ('\a'); break; + case 'b': s.PutChar ('\b'); break; + case 'f': s.PutChar ('\f'); break; + case 'n': s.PutChar ('\n'); break; + case 'r': s.PutChar ('\r'); break; + case 't': s.PutChar ('\t'); break; + case 'v': s.PutChar ('\v'); break; + case '\'': s.PutChar ('\''); break; + case '\\': s.PutChar ('\\'); break; + case '0': + // 1 to 3 octal chars + { + // Make a string that can hold onto the initial zero char, + // up to 3 octal digits, and a terminating NULL. + char oct_str[5] = { 0, 0, 0, 0, 0 }; + + int i; + for (i=0; (p[i] >= '0' && p[i] <= '7') && i<4; ++i) + oct_str[i] = p[i]; + + // We don't want to consume the last octal character since + // the main for loop will do this for us, so we advance p by + // one less than i (even if i is zero) + p += i - 1; + unsigned long octal_value = ::strtoul (oct_str, NULL, 8); + if (octal_value <= UINT8_MAX) + { + s.PutChar((char)octal_value); + } + } + break; + + case 'x': + // hex number in the format + if (isxdigit(p[1])) + { + ++p; // Skip the 'x' + + // Make a string that can hold onto two hex chars plus a + // NULL terminator + char hex_str[3] = { 0,0,0 }; + hex_str[0] = *p; + if (isxdigit(p[1])) + { + ++p; // Skip the first of the two hex chars + hex_str[1] = *p; + } + + unsigned long hex_value = strtoul (hex_str, NULL, 16); + if (hex_value <= UINT8_MAX) + s.PutChar ((char)hex_value); + } + else + { + s.PutChar('x'); + } + break; + + default: + // Just desensitize any other character by just printing what + // came after the '\' + s << *p; + break; + + } + + } + } + if (end) + *end = p; + return success; +} + +bool +Debugger::FormatPrompt +( + const char *format, + const SymbolContext *sc, + const ExecutionContext *exe_ctx, + const Address *addr, + Stream &s, + ValueObject* valobj +) +{ + bool use_color = exe_ctx ? exe_ctx->GetTargetRef().GetDebugger().GetUseColor() : true; + std::string format_str = lldb_utility::ansi::FormatAnsiTerminalCodes (format, use_color); + if (format_str.length()) + format = format_str.c_str(); + return FormatPromptRecurse (format, sc, exe_ctx, addr, s, NULL, valobj); +} + +void +Debugger::SetLoggingCallback (lldb::LogOutputCallback log_callback, void *baton) +{ + // For simplicity's sake, I am not going to deal with how to close down any + // open logging streams, I just redirect everything from here on out to the + // callback. + m_log_callback_stream_sp.reset (new StreamCallback (log_callback, baton)); +} + +bool +Debugger::EnableLog (const char *channel, const char **categories, const char *log_file, uint32_t log_options, Stream &error_stream) +{ + Log::Callbacks log_callbacks; + + StreamSP log_stream_sp; + if (m_log_callback_stream_sp) + { + log_stream_sp = m_log_callback_stream_sp; + // For now when using the callback mode you always get thread & timestamp. + log_options |= LLDB_LOG_OPTION_PREPEND_TIMESTAMP | LLDB_LOG_OPTION_PREPEND_THREAD_NAME; + } + else if (log_file == NULL || *log_file == '\0') + { + log_stream_sp.reset(new StreamFile(GetOutputFile().GetDescriptor(), false)); + } + else + { + LogStreamMap::iterator pos = m_log_streams.find(log_file); + if (pos != m_log_streams.end()) + log_stream_sp = pos->second.lock(); + if (!log_stream_sp) + { + log_stream_sp.reset (new StreamFile (log_file)); + m_log_streams[log_file] = log_stream_sp; + } + } + assert (log_stream_sp.get()); + + if (log_options == 0) + log_options = LLDB_LOG_OPTION_PREPEND_THREAD_NAME | LLDB_LOG_OPTION_THREADSAFE; + + if (Log::GetLogChannelCallbacks (ConstString(channel), log_callbacks)) + { + log_callbacks.enable (log_stream_sp, log_options, categories, &error_stream); + return true; + } + else + { + LogChannelSP log_channel_sp (LogChannel::FindPlugin (channel)); + if (log_channel_sp) + { + if (log_channel_sp->Enable (log_stream_sp, log_options, &error_stream, categories)) + { + return true; + } + else + { + error_stream.Printf ("Invalid log channel '%s'.\n", channel); + return false; + } + } + else + { + error_stream.Printf ("Invalid log channel '%s'.\n", channel); + return false; + } + } + return false; +} + +SourceManager & +Debugger::GetSourceManager () +{ + if (m_source_manager_ap.get() == NULL) + m_source_manager_ap.reset (new SourceManager (shared_from_this())); + return *m_source_manager_ap; +} + + diff --git a/contrib/llvm/tools/lldb/source/Core/Disassembler.cpp b/contrib/llvm/tools/lldb/source/Core/Disassembler.cpp new file mode 100644 index 00000000000..e80e92c91b5 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Core/Disassembler.cpp @@ -0,0 +1,1269 @@ +//===-- Disassembler.cpp ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "lldb/Core/Disassembler.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private.h" +#include "lldb/Core/Error.h" +#include "lldb/Core/DataBufferHeap.h" +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/Debugger.h" +#include "lldb/Core/EmulateInstruction.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/RegularExpression.h" +#include "lldb/Core/Timer.h" +#include "lldb/Interpreter/OptionValue.h" +#include "lldb/Interpreter/OptionValueArray.h" +#include "lldb/Interpreter/OptionValueDictionary.h" +#include "lldb/Interpreter/OptionValueString.h" +#include "lldb/Interpreter/OptionValueUInt64.h" +#include "lldb/Symbol/ClangNamespaceDecl.h" +#include "lldb/Symbol/Function.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/StackFrame.h" +#include "lldb/Target/Target.h" + +#define DEFAULT_DISASM_BYTE_SIZE 32 + +using namespace lldb; +using namespace lldb_private; + + +DisassemblerSP +Disassembler::FindPlugin (const ArchSpec &arch, const char *flavor, const char *plugin_name) +{ + Timer scoped_timer (__PRETTY_FUNCTION__, + "Disassembler::FindPlugin (arch = %s, plugin_name = %s)", + arch.GetArchitectureName(), + plugin_name); + + DisassemblerCreateInstance create_callback = NULL; + + if (plugin_name) + { + ConstString const_plugin_name (plugin_name); + create_callback = PluginManager::GetDisassemblerCreateCallbackForPluginName (const_plugin_name); + if (create_callback) + { + DisassemblerSP disassembler_sp(create_callback(arch, flavor)); + + if (disassembler_sp.get()) + return disassembler_sp; + } + } + else + { + for (uint32_t idx = 0; (create_callback = PluginManager::GetDisassemblerCreateCallbackAtIndex(idx)) != NULL; ++idx) + { + DisassemblerSP disassembler_sp(create_callback(arch, flavor)); + + if (disassembler_sp.get()) + return disassembler_sp; + } + } + return DisassemblerSP(); +} + +DisassemblerSP +Disassembler::FindPluginForTarget(const TargetSP target_sp, const ArchSpec &arch, const char *flavor, const char *plugin_name) +{ + if (target_sp && flavor == NULL) + { + // FIXME - we don't have the mechanism in place to do per-architecture settings. But since we know that for now + // we only support flavors on x86 & x86_64, + if (arch.GetTriple().getArch() == llvm::Triple::x86 + || arch.GetTriple().getArch() == llvm::Triple::x86_64) + flavor = target_sp->GetDisassemblyFlavor(); + } + return FindPlugin(arch, flavor, plugin_name); +} + + +static void +ResolveAddress (const ExecutionContext &exe_ctx, + const Address &addr, + Address &resolved_addr) +{ + if (!addr.IsSectionOffset()) + { + // If we weren't passed in a section offset address range, + // try and resolve it to something + Target *target = exe_ctx.GetTargetPtr(); + if (target) + { + if (target->GetSectionLoadList().IsEmpty()) + { + target->GetImages().ResolveFileAddress (addr.GetOffset(), resolved_addr); + } + else + { + target->GetSectionLoadList().ResolveLoadAddress (addr.GetOffset(), resolved_addr); + } + // We weren't able to resolve the address, just treat it as a + // raw address + if (resolved_addr.IsValid()) + return; + } + } + resolved_addr = addr; +} + +size_t +Disassembler::Disassemble +( + Debugger &debugger, + const ArchSpec &arch, + const char *plugin_name, + const char *flavor, + const ExecutionContext &exe_ctx, + SymbolContextList &sc_list, + uint32_t num_instructions, + uint32_t num_mixed_context_lines, + uint32_t options, + Stream &strm +) +{ + size_t success_count = 0; + const size_t count = sc_list.GetSize(); + SymbolContext sc; + AddressRange range; + const uint32_t scope = eSymbolContextBlock | eSymbolContextFunction | eSymbolContextSymbol; + const bool use_inline_block_range = true; + for (size_t i=0; iFindFunctions (name, + NULL, + eFunctionNameTypeAuto, + include_symbols, + include_inlines, + true, + sc_list); + } + else if (exe_ctx.GetTargetPtr()) + { + exe_ctx.GetTargetPtr()->GetImages().FindFunctions (name, + eFunctionNameTypeAuto, + include_symbols, + include_inlines, + false, + sc_list); + } + } + + if (sc_list.GetSize ()) + { + return Disassemble (debugger, + arch, + plugin_name, + flavor, + exe_ctx, + sc_list, + num_instructions, + num_mixed_context_lines, + options, + strm); + } + return false; +} + + +lldb::DisassemblerSP +Disassembler::DisassembleRange +( + const ArchSpec &arch, + const char *plugin_name, + const char *flavor, + const ExecutionContext &exe_ctx, + const AddressRange &range +) +{ + lldb::DisassemblerSP disasm_sp; + if (range.GetByteSize() > 0 && range.GetBaseAddress().IsValid()) + { + disasm_sp = Disassembler::FindPluginForTarget(exe_ctx.GetTargetSP(), arch, flavor, plugin_name); + + if (disasm_sp) + { + const bool prefer_file_cache = false; + size_t bytes_disassembled = disasm_sp->ParseInstructions (&exe_ctx, range, NULL, prefer_file_cache); + if (bytes_disassembled == 0) + disasm_sp.reset(); + } + } + return disasm_sp; +} + +lldb::DisassemblerSP +Disassembler::DisassembleBytes (const ArchSpec &arch, + const char *plugin_name, + const char *flavor, + const Address &start, + const void *src, + size_t src_len, + uint32_t num_instructions, + bool data_from_file) +{ + lldb::DisassemblerSP disasm_sp; + + if (src) + { + disasm_sp = Disassembler::FindPlugin(arch, flavor, plugin_name); + + if (disasm_sp) + { + DataExtractor data(src, src_len, arch.GetByteOrder(), arch.GetAddressByteSize()); + + (void)disasm_sp->DecodeInstructions (start, + data, + 0, + num_instructions, + false, + data_from_file); + } + } + + return disasm_sp; +} + + +bool +Disassembler::Disassemble +( + Debugger &debugger, + const ArchSpec &arch, + const char *plugin_name, + const char *flavor, + const ExecutionContext &exe_ctx, + const AddressRange &disasm_range, + uint32_t num_instructions, + uint32_t num_mixed_context_lines, + uint32_t options, + Stream &strm +) +{ + if (disasm_range.GetByteSize()) + { + lldb::DisassemblerSP disasm_sp (Disassembler::FindPluginForTarget(exe_ctx.GetTargetSP(), arch, flavor, plugin_name)); + + if (disasm_sp.get()) + { + AddressRange range; + ResolveAddress (exe_ctx, disasm_range.GetBaseAddress(), range.GetBaseAddress()); + range.SetByteSize (disasm_range.GetByteSize()); + const bool prefer_file_cache = false; + size_t bytes_disassembled = disasm_sp->ParseInstructions (&exe_ctx, range, &strm, prefer_file_cache); + if (bytes_disassembled == 0) + return false; + + bool result = PrintInstructions (disasm_sp.get(), + debugger, + arch, + exe_ctx, + num_instructions, + num_mixed_context_lines, + options, + strm); + + // FIXME: The DisassemblerLLVMC has a reference cycle and won't go away if it has any active instructions. + // I'll fix that but for now, just clear the list and it will go away nicely. + disasm_sp->GetInstructionList().Clear(); + return result; + } + } + return false; +} + +bool +Disassembler::Disassemble +( + Debugger &debugger, + const ArchSpec &arch, + const char *plugin_name, + const char *flavor, + const ExecutionContext &exe_ctx, + const Address &start_address, + uint32_t num_instructions, + uint32_t num_mixed_context_lines, + uint32_t options, + Stream &strm +) +{ + if (num_instructions > 0) + { + lldb::DisassemblerSP disasm_sp (Disassembler::FindPluginForTarget(exe_ctx.GetTargetSP(), + arch, + flavor, + plugin_name)); + if (disasm_sp.get()) + { + Address addr; + ResolveAddress (exe_ctx, start_address, addr); + const bool prefer_file_cache = false; + size_t bytes_disassembled = disasm_sp->ParseInstructions (&exe_ctx, + addr, + num_instructions, + prefer_file_cache); + if (bytes_disassembled == 0) + return false; + bool result = PrintInstructions (disasm_sp.get(), + debugger, + arch, + exe_ctx, + num_instructions, + num_mixed_context_lines, + options, + strm); + + // FIXME: The DisassemblerLLVMC has a reference cycle and won't go away if it has any active instructions. + // I'll fix that but for now, just clear the list and it will go away nicely. + disasm_sp->GetInstructionList().Clear(); + return result; + } + } + return false; +} + +bool +Disassembler::PrintInstructions +( + Disassembler *disasm_ptr, + Debugger &debugger, + const ArchSpec &arch, + const ExecutionContext &exe_ctx, + uint32_t num_instructions, + uint32_t num_mixed_context_lines, + uint32_t options, + Stream &strm +) +{ + // We got some things disassembled... + size_t num_instructions_found = disasm_ptr->GetInstructionList().GetSize(); + + if (num_instructions > 0 && num_instructions < num_instructions_found) + num_instructions_found = num_instructions; + + const uint32_t max_opcode_byte_size = disasm_ptr->GetInstructionList().GetMaxOpcocdeByteSize (); + uint32_t offset = 0; + SymbolContext sc; + SymbolContext prev_sc; + AddressRange sc_range; + const Address *pc_addr_ptr = NULL; + ExecutionContextScope *exe_scope = exe_ctx.GetBestExecutionContextScope(); + StackFrame *frame = exe_ctx.GetFramePtr(); + + TargetSP target_sp (exe_ctx.GetTargetSP()); + SourceManager &source_manager = target_sp ? target_sp->GetSourceManager() : debugger.GetSourceManager(); + + if (frame) + pc_addr_ptr = &frame->GetFrameCodeAddress(); + const uint32_t scope = eSymbolContextLineEntry | eSymbolContextFunction | eSymbolContextSymbol; + const bool use_inline_block_range = false; + for (size_t i=0; iGetInstructionList().GetInstructionAtIndex (i).get(); + if (inst) + { + const Address &addr = inst->GetAddress(); + const bool inst_is_at_pc = pc_addr_ptr && addr == *pc_addr_ptr; + + prev_sc = sc; + + ModuleSP module_sp (addr.GetModule()); + if (module_sp) + { + uint32_t resolved_mask = module_sp->ResolveSymbolContextForAddress(addr, eSymbolContextEverything, sc); + if (resolved_mask) + { + if (num_mixed_context_lines) + { + if (!sc_range.ContainsFileAddress (addr)) + { + sc.GetAddressRange (scope, 0, use_inline_block_range, sc_range); + + if (sc != prev_sc) + { + if (offset != 0) + strm.EOL(); + + sc.DumpStopContext(&strm, exe_ctx.GetProcessPtr(), addr, false, true, false); + strm.EOL(); + + if (sc.comp_unit && sc.line_entry.IsValid()) + { + source_manager.DisplaySourceLinesWithLineNumbers (sc.line_entry.file, + sc.line_entry.line, + num_mixed_context_lines, + num_mixed_context_lines, + ((inst_is_at_pc && (options & eOptionMarkPCSourceLine)) ? "->" : ""), + &strm); + } + } + } + } + else if ((sc.function || sc.symbol) && (sc.function != prev_sc.function || sc.symbol != prev_sc.symbol)) + { + if (prev_sc.function || prev_sc.symbol) + strm.EOL(); + + bool show_fullpaths = false; + bool show_module = true; + bool show_inlined_frames = true; + sc.DumpStopContext (&strm, + exe_scope, + addr, + show_fullpaths, + show_module, + show_inlined_frames); + + strm << ":\n"; + } + } + else + { + sc.Clear(true); + } + } + + if ((options & eOptionMarkPCAddress) && pc_addr_ptr) + { + strm.PutCString(inst_is_at_pc ? "-> " : " "); + } + const bool show_bytes = (options & eOptionShowBytes) != 0; + inst->Dump(&strm, max_opcode_byte_size, true, show_bytes, &exe_ctx); + strm.EOL(); + } + else + { + break; + } + } + + return true; +} + + +bool +Disassembler::Disassemble +( + Debugger &debugger, + const ArchSpec &arch, + const char *plugin_name, + const char *flavor, + const ExecutionContext &exe_ctx, + uint32_t num_instructions, + uint32_t num_mixed_context_lines, + uint32_t options, + Stream &strm +) +{ + AddressRange range; + StackFrame *frame = exe_ctx.GetFramePtr(); + if (frame) + { + SymbolContext sc(frame->GetSymbolContext(eSymbolContextFunction | eSymbolContextSymbol)); + if (sc.function) + { + range = sc.function->GetAddressRange(); + } + else if (sc.symbol && sc.symbol->ValueIsAddress()) + { + range.GetBaseAddress() = sc.symbol->GetAddress(); + range.SetByteSize (sc.symbol->GetByteSize()); + } + else + { + range.GetBaseAddress() = frame->GetFrameCodeAddress(); + } + + if (range.GetBaseAddress().IsValid() && range.GetByteSize() == 0) + range.SetByteSize (DEFAULT_DISASM_BYTE_SIZE); + } + + return Disassemble (debugger, + arch, + plugin_name, + flavor, + exe_ctx, + range, + num_instructions, + num_mixed_context_lines, + options, + strm); +} + +Instruction::Instruction(const Address &address, AddressClass addr_class) : + m_address (address), + m_address_class (addr_class), + m_opcode(), + m_calculated_strings(false) +{ +} + +Instruction::~Instruction() +{ +} + +AddressClass +Instruction::GetAddressClass () +{ + if (m_address_class == eAddressClassInvalid) + m_address_class = m_address.GetAddressClass(); + return m_address_class; +} + +void +Instruction::Dump (lldb_private::Stream *s, + uint32_t max_opcode_byte_size, + bool show_address, + bool show_bytes, + const ExecutionContext* exe_ctx) +{ + size_t opcode_column_width = 7; + const size_t operand_column_width = 25; + + CalculateMnemonicOperandsAndCommentIfNeeded (exe_ctx); + + StreamString ss; + + if (show_address) + { + m_address.Dump(&ss, + exe_ctx ? exe_ctx->GetBestExecutionContextScope() : NULL, + Address::DumpStyleLoadAddress, + Address::DumpStyleModuleWithFileAddress, + 0); + + ss.PutCString(": "); + } + + if (show_bytes) + { + if (m_opcode.GetType() == Opcode::eTypeBytes) + { + // x86_64 and i386 are the only ones that use bytes right now so + // pad out the byte dump to be able to always show 15 bytes (3 chars each) + // plus a space + if (max_opcode_byte_size > 0) + m_opcode.Dump (&ss, max_opcode_byte_size * 3 + 1); + else + m_opcode.Dump (&ss, 15 * 3 + 1); + } + else + { + // Else, we have ARM which can show up to a uint32_t 0x00000000 (10 spaces) + // plus two for padding... + if (max_opcode_byte_size > 0) + m_opcode.Dump (&ss, max_opcode_byte_size * 3 + 1); + else + m_opcode.Dump (&ss, 12); + } + } + + const size_t opcode_pos = ss.GetSize(); + + // The default opcode size of 7 characters is plenty for most architectures + // but some like arm can pull out the occasional vqrshrun.s16. We won't get + // consistent column spacing in these cases, unfortunately. + if (m_opcode_name.length() >= opcode_column_width) + { + opcode_column_width = m_opcode_name.length() + 1; + } + + ss.PutCString (m_opcode_name.c_str()); + ss.FillLastLineToColumn (opcode_pos + opcode_column_width, ' '); + ss.PutCString (m_mnemonics.c_str()); + + if (!m_comment.empty()) + { + ss.FillLastLineToColumn (opcode_pos + opcode_column_width + operand_column_width, ' '); + ss.PutCString (" ; "); + ss.PutCString (m_comment.c_str()); + } + s->Write (ss.GetData(), ss.GetSize()); +} + +bool +Instruction::DumpEmulation (const ArchSpec &arch) +{ + std::unique_ptr insn_emulator_ap (EmulateInstruction::FindPlugin (arch, eInstructionTypeAny, NULL)); + if (insn_emulator_ap.get()) + { + insn_emulator_ap->SetInstruction (GetOpcode(), GetAddress(), NULL); + return insn_emulator_ap->EvaluateInstruction (0); + } + + return false; +} + +OptionValueSP +Instruction::ReadArray (FILE *in_file, Stream *out_stream, OptionValue::Type data_type) +{ + bool done = false; + char buffer[1024]; + + OptionValueSP option_value_sp (new OptionValueArray (1u << data_type)); + + int idx = 0; + while (!done) + { + if (!fgets (buffer, 1023, in_file)) + { + out_stream->Printf ("Instruction::ReadArray: Error reading file (fgets).\n"); + option_value_sp.reset (); + return option_value_sp; + } + + std::string line (buffer); + + size_t len = line.size(); + if (line[len-1] == '\n') + { + line[len-1] = '\0'; + line.resize (len-1); + } + + if ((line.size() == 1) && line[0] == ']') + { + done = true; + line.clear(); + } + + if (line.size() > 0) + { + std::string value; + static RegularExpression g_reg_exp ("^[ \t]*([^ \t]+)[ \t]*$"); + RegularExpression::Match regex_match(1); + bool reg_exp_success = g_reg_exp.Execute (line.c_str(), ®ex_match); + if (reg_exp_success) + regex_match.GetMatchAtIndex (line.c_str(), 1, value); + else + value = line; + + OptionValueSP data_value_sp; + switch (data_type) + { + case OptionValue::eTypeUInt64: + data_value_sp.reset (new OptionValueUInt64 (0, 0)); + data_value_sp->SetValueFromCString (value.c_str()); + break; + // Other types can be added later as needed. + default: + data_value_sp.reset (new OptionValueString (value.c_str(), "")); + break; + } + + option_value_sp->GetAsArray()->InsertValue (idx, data_value_sp); + ++idx; + } + } + + return option_value_sp; +} + +OptionValueSP +Instruction::ReadDictionary (FILE *in_file, Stream *out_stream) +{ + bool done = false; + char buffer[1024]; + + OptionValueSP option_value_sp (new OptionValueDictionary()); + static ConstString encoding_key ("data_encoding"); + OptionValue::Type data_type = OptionValue::eTypeInvalid; + + + while (!done) + { + // Read the next line in the file + if (!fgets (buffer, 1023, in_file)) + { + out_stream->Printf ("Instruction::ReadDictionary: Error reading file (fgets).\n"); + option_value_sp.reset (); + return option_value_sp; + } + + // Check to see if the line contains the end-of-dictionary marker ("}") + std::string line (buffer); + + size_t len = line.size(); + if (line[len-1] == '\n') + { + line[len-1] = '\0'; + line.resize (len-1); + } + + if ((line.size() == 1) && (line[0] == '}')) + { + done = true; + line.clear(); + } + + // Try to find a key-value pair in the current line and add it to the dictionary. + if (line.size() > 0) + { + static RegularExpression g_reg_exp ("^[ \t]*([a-zA-Z_][a-zA-Z0-9_]*)[ \t]*=[ \t]*(.*)[ \t]*$"); + RegularExpression::Match regex_match(2); + + bool reg_exp_success = g_reg_exp.Execute (line.c_str(), ®ex_match); + std::string key; + std::string value; + if (reg_exp_success) + { + regex_match.GetMatchAtIndex (line.c_str(), 1, key); + regex_match.GetMatchAtIndex (line.c_str(), 2, value); + } + else + { + out_stream->Printf ("Instruction::ReadDictionary: Failure executing regular expression.\n"); + option_value_sp.reset(); + return option_value_sp; + } + + ConstString const_key (key.c_str()); + // Check value to see if it's the start of an array or dictionary. + + lldb::OptionValueSP value_sp; + assert (value.empty() == false); + assert (key.empty() == false); + + if (value[0] == '{') + { + assert (value.size() == 1); + // value is a dictionary + value_sp = ReadDictionary (in_file, out_stream); + if (value_sp.get() == NULL) + { + option_value_sp.reset (); + return option_value_sp; + } + } + else if (value[0] == '[') + { + assert (value.size() == 1); + // value is an array + value_sp = ReadArray (in_file, out_stream, data_type); + if (value_sp.get() == NULL) + { + option_value_sp.reset (); + return option_value_sp; + } + // We've used the data_type to read an array; re-set the type to Invalid + data_type = OptionValue::eTypeInvalid; + } + else if ((value[0] == '0') && (value[1] == 'x')) + { + value_sp.reset (new OptionValueUInt64 (0, 0)); + value_sp->SetValueFromCString (value.c_str()); + } + else + { + size_t len = value.size(); + if ((value[0] == '"') && (value[len-1] == '"')) + value = value.substr (1, len-2); + value_sp.reset (new OptionValueString (value.c_str(), "")); + } + + + + if (const_key == encoding_key) + { + // A 'data_encoding=..." is NOT a normal key-value pair; it is meta-data indicating the + // data type of an upcoming array (usually the next bit of data to be read in). + if (strcmp (value.c_str(), "uint32_t") == 0) + data_type = OptionValue::eTypeUInt64; + } + else + option_value_sp->GetAsDictionary()->SetValueForKey (const_key, value_sp, false); + } + } + + return option_value_sp; +} + +bool +Instruction::TestEmulation (Stream *out_stream, const char *file_name) +{ + if (!out_stream) + return false; + + if (!file_name) + { + out_stream->Printf ("Instruction::TestEmulation: Missing file_name."); + return false; + } + + FILE *test_file = fopen (file_name, "r"); + if (!test_file) + { + out_stream->Printf ("Instruction::TestEmulation: Attempt to open test file failed."); + return false; + } + + char buffer[256]; + if (!fgets (buffer, 255, test_file)) + { + out_stream->Printf ("Instruction::TestEmulation: Error reading first line of test file.\n"); + fclose (test_file); + return false; + } + + if (strncmp (buffer, "InstructionEmulationState={", 27) != 0) + { + out_stream->Printf ("Instructin::TestEmulation: Test file does not contain emulation state dictionary\n"); + fclose (test_file); + return false; + } + + // Read all the test information from the test file into an OptionValueDictionary. + + OptionValueSP data_dictionary_sp (ReadDictionary (test_file, out_stream)); + if (data_dictionary_sp.get() == NULL) + { + out_stream->Printf ("Instruction::TestEmulation: Error reading Dictionary Object.\n"); + fclose (test_file); + return false; + } + + fclose (test_file); + + OptionValueDictionary *data_dictionary = data_dictionary_sp->GetAsDictionary(); + static ConstString description_key ("assembly_string"); + static ConstString triple_key ("triple"); + + OptionValueSP value_sp = data_dictionary->GetValueForKey (description_key); + + if (value_sp.get() == NULL) + { + out_stream->Printf ("Instruction::TestEmulation: Test file does not contain description string.\n"); + return false; + } + + SetDescription (value_sp->GetStringValue()); + + + value_sp = data_dictionary->GetValueForKey (triple_key); + if (value_sp.get() == NULL) + { + out_stream->Printf ("Instruction::TestEmulation: Test file does not contain triple.\n"); + return false; + } + + ArchSpec arch; + arch.SetTriple (llvm::Triple (value_sp->GetStringValue())); + + bool success = false; + std::unique_ptr insn_emulator_ap (EmulateInstruction::FindPlugin (arch, eInstructionTypeAny, NULL)); + if (insn_emulator_ap.get()) + success = insn_emulator_ap->TestEmulation (out_stream, arch, data_dictionary); + + if (success) + out_stream->Printf ("Emulation test succeeded."); + else + out_stream->Printf ("Emulation test failed."); + + return success; +} + +bool +Instruction::Emulate (const ArchSpec &arch, + uint32_t evaluate_options, + void *baton, + EmulateInstruction::ReadMemoryCallback read_mem_callback, + EmulateInstruction::WriteMemoryCallback write_mem_callback, + EmulateInstruction::ReadRegisterCallback read_reg_callback, + EmulateInstruction::WriteRegisterCallback write_reg_callback) +{ + std::unique_ptr insn_emulator_ap (EmulateInstruction::FindPlugin (arch, eInstructionTypeAny, NULL)); + if (insn_emulator_ap.get()) + { + insn_emulator_ap->SetBaton (baton); + insn_emulator_ap->SetCallbacks (read_mem_callback, write_mem_callback, read_reg_callback, write_reg_callback); + insn_emulator_ap->SetInstruction (GetOpcode(), GetAddress(), NULL); + return insn_emulator_ap->EvaluateInstruction (evaluate_options); + } + + return false; +} + + +uint32_t +Instruction::GetData (DataExtractor &data) +{ + return m_opcode.GetData(data); +} + +InstructionList::InstructionList() : + m_instructions() +{ +} + +InstructionList::~InstructionList() +{ +} + +size_t +InstructionList::GetSize() const +{ + return m_instructions.size(); +} + +uint32_t +InstructionList::GetMaxOpcocdeByteSize () const +{ + uint32_t max_inst_size = 0; + collection::const_iterator pos, end; + for (pos = m_instructions.begin(), end = m_instructions.end(); + pos != end; + ++pos) + { + uint32_t inst_size = (*pos)->GetOpcode().GetByteSize(); + if (max_inst_size < inst_size) + max_inst_size = inst_size; + } + return max_inst_size; +} + + + +InstructionSP +InstructionList::GetInstructionAtIndex (size_t idx) const +{ + InstructionSP inst_sp; + if (idx < m_instructions.size()) + inst_sp = m_instructions[idx]; + return inst_sp; +} + +void +InstructionList::Dump (Stream *s, + bool show_address, + bool show_bytes, + const ExecutionContext* exe_ctx) +{ + const uint32_t max_opcode_byte_size = GetMaxOpcocdeByteSize(); + collection::const_iterator pos, begin, end; + for (begin = m_instructions.begin(), end = m_instructions.end(), pos = begin; + pos != end; + ++pos) + { + if (pos != begin) + s->EOL(); + (*pos)->Dump(s, max_opcode_byte_size, show_address, show_bytes, exe_ctx); + } +} + + +void +InstructionList::Clear() +{ + m_instructions.clear(); +} + +void +InstructionList::Append (lldb::InstructionSP &inst_sp) +{ + if (inst_sp) + m_instructions.push_back(inst_sp); +} + +uint32_t +InstructionList::GetIndexOfNextBranchInstruction(uint32_t start) const +{ + size_t num_instructions = m_instructions.size(); + + uint32_t next_branch = UINT32_MAX; + for (size_t i = start; i < num_instructions; i++) + { + if (m_instructions[i]->DoesBranch()) + { + next_branch = i; + break; + } + } + return next_branch; +} + +uint32_t +InstructionList::GetIndexOfInstructionAtLoadAddress (lldb::addr_t load_addr, Target &target) +{ + Address address; + address.SetLoadAddress(load_addr, &target); + size_t num_instructions = m_instructions.size(); + uint32_t index = UINT32_MAX; + for (size_t i = 0; i < num_instructions; i++) + { + if (m_instructions[i]->GetAddress() == address) + { + index = i; + break; + } + } + return index; +} + +size_t +Disassembler::ParseInstructions (const ExecutionContext *exe_ctx, + const AddressRange &range, + Stream *error_strm_ptr, + bool prefer_file_cache) +{ + if (exe_ctx) + { + Target *target = exe_ctx->GetTargetPtr(); + const addr_t byte_size = range.GetByteSize(); + if (target == NULL || byte_size == 0 || !range.GetBaseAddress().IsValid()) + return 0; + + DataBufferHeap *heap_buffer = new DataBufferHeap (byte_size, '\0'); + DataBufferSP data_sp(heap_buffer); + + Error error; + lldb::addr_t load_addr = LLDB_INVALID_ADDRESS; + const size_t bytes_read = target->ReadMemory (range.GetBaseAddress(), + prefer_file_cache, + heap_buffer->GetBytes(), + heap_buffer->GetByteSize(), + error, + &load_addr); + + if (bytes_read > 0) + { + if (bytes_read != heap_buffer->GetByteSize()) + heap_buffer->SetByteSize (bytes_read); + DataExtractor data (data_sp, + m_arch.GetByteOrder(), + m_arch.GetAddressByteSize()); + const bool data_from_file = load_addr == LLDB_INVALID_ADDRESS; + return DecodeInstructions (range.GetBaseAddress(), data, 0, UINT32_MAX, false, data_from_file); + } + else if (error_strm_ptr) + { + const char *error_cstr = error.AsCString(); + if (error_cstr) + { + error_strm_ptr->Printf("error: %s\n", error_cstr); + } + } + } + else if (error_strm_ptr) + { + error_strm_ptr->PutCString("error: invalid execution context\n"); + } + return 0; +} + +size_t +Disassembler::ParseInstructions (const ExecutionContext *exe_ctx, + const Address &start, + uint32_t num_instructions, + bool prefer_file_cache) +{ + m_instruction_list.Clear(); + + if (exe_ctx == NULL || num_instructions == 0 || !start.IsValid()) + return 0; + + Target *target = exe_ctx->GetTargetPtr(); + // Calculate the max buffer size we will need in order to disassemble + const addr_t byte_size = num_instructions * m_arch.GetMaximumOpcodeByteSize(); + + if (target == NULL || byte_size == 0) + return 0; + + DataBufferHeap *heap_buffer = new DataBufferHeap (byte_size, '\0'); + DataBufferSP data_sp (heap_buffer); + + Error error; + lldb::addr_t load_addr = LLDB_INVALID_ADDRESS; + const size_t bytes_read = target->ReadMemory (start, + prefer_file_cache, + heap_buffer->GetBytes(), + byte_size, + error, + &load_addr); + + const bool data_from_file = load_addr == LLDB_INVALID_ADDRESS; + + if (bytes_read == 0) + return 0; + DataExtractor data (data_sp, + m_arch.GetByteOrder(), + m_arch.GetAddressByteSize()); + + const bool append_instructions = true; + DecodeInstructions (start, + data, + 0, + num_instructions, + append_instructions, + data_from_file); + + return m_instruction_list.GetSize(); +} + +//---------------------------------------------------------------------- +// Disassembler copy constructor +//---------------------------------------------------------------------- +Disassembler::Disassembler(const ArchSpec& arch, const char *flavor) : + m_arch (arch), + m_instruction_list(), + m_base_addr(LLDB_INVALID_ADDRESS), + m_flavor () +{ + if (flavor == NULL) + m_flavor.assign("default"); + else + m_flavor.assign(flavor); +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +Disassembler::~Disassembler() +{ +} + +InstructionList & +Disassembler::GetInstructionList () +{ + return m_instruction_list; +} + +const InstructionList & +Disassembler::GetInstructionList () const +{ + return m_instruction_list; +} + +//---------------------------------------------------------------------- +// Class PseudoInstruction +//---------------------------------------------------------------------- +PseudoInstruction::PseudoInstruction () : + Instruction (Address(), eAddressClassUnknown), + m_description () +{ +} + +PseudoInstruction::~PseudoInstruction () +{ +} + +bool +PseudoInstruction::DoesBranch () +{ + // This is NOT a valid question for a pseudo instruction. + return false; +} + +size_t +PseudoInstruction::Decode (const lldb_private::Disassembler &disassembler, + const lldb_private::DataExtractor &data, + lldb::offset_t data_offset) +{ + return m_opcode.GetByteSize(); +} + + +void +PseudoInstruction::SetOpcode (size_t opcode_size, void *opcode_data) +{ + if (!opcode_data) + return; + + switch (opcode_size) + { + case 8: + { + uint8_t value8 = *((uint8_t *) opcode_data); + m_opcode.SetOpcode8 (value8); + break; + } + case 16: + { + uint16_t value16 = *((uint16_t *) opcode_data); + m_opcode.SetOpcode16 (value16); + break; + } + case 32: + { + uint32_t value32 = *((uint32_t *) opcode_data); + m_opcode.SetOpcode32 (value32); + break; + } + case 64: + { + uint64_t value64 = *((uint64_t *) opcode_data); + m_opcode.SetOpcode64 (value64); + break; + } + default: + break; + } +} + +void +PseudoInstruction::SetDescription (const char *description) +{ + if (description && strlen (description) > 0) + m_description = description; +} diff --git a/contrib/llvm/tools/lldb/source/Core/DynamicLoader.cpp b/contrib/llvm/tools/lldb/source/Core/DynamicLoader.cpp new file mode 100644 index 00000000000..82f84048b32 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Core/DynamicLoader.cpp @@ -0,0 +1,76 @@ +//===-- DynamicLoader.cpp ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-private.h" +#include "lldb/Target/DynamicLoader.h" +#include "lldb/Target/Process.h" +#include "lldb/Core/PluginManager.h" + +using namespace lldb; +using namespace lldb_private; + +DynamicLoader* +DynamicLoader::FindPlugin (Process *process, const char *plugin_name) +{ + DynamicLoaderCreateInstance create_callback = NULL; + if (plugin_name) + { + ConstString const_plugin_name(plugin_name); + create_callback = PluginManager::GetDynamicLoaderCreateCallbackForPluginName (const_plugin_name); + if (create_callback) + { + std::unique_ptr instance_ap(create_callback(process, true)); + if (instance_ap.get()) + return instance_ap.release(); + } + } + else + { + for (uint32_t idx = 0; (create_callback = PluginManager::GetDynamicLoaderCreateCallbackAtIndex(idx)) != NULL; ++idx) + { + std::unique_ptr instance_ap(create_callback(process, false)); + if (instance_ap.get()) + return instance_ap.release(); + } + } + return NULL; +} + + +//---------------------------------------------------------------------- +// DynamicLoader constructor +//---------------------------------------------------------------------- +DynamicLoader::DynamicLoader(Process *process) : + m_process (process) +{ +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +DynamicLoader::~DynamicLoader() +{ +} + +//---------------------------------------------------------------------- +// Accessosors to the global setting as to whether to stop at image +// (shared library) loading/unloading. +//---------------------------------------------------------------------- +bool +DynamicLoader::GetStopWhenImagesChange () const +{ + return m_process->GetStopOnSharedLibraryEvents(); +} + +void +DynamicLoader::SetStopWhenImagesChange (bool stop) +{ + m_process->SetStopOnSharedLibraryEvents (stop); +} + diff --git a/contrib/llvm/tools/lldb/source/Core/EmulateInstruction.cpp b/contrib/llvm/tools/lldb/source/Core/EmulateInstruction.cpp new file mode 100644 index 00000000000..bf6c6d88b56 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Core/EmulateInstruction.cpp @@ -0,0 +1,670 @@ +//===-- EmulateInstruction.h ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Core/EmulateInstruction.h" + +#include "lldb/Core/Address.h" +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/Error.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/RegisterValue.h" +#include "lldb/Core/StreamFile.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Host/Endian.h" +#include "lldb/Symbol/UnwindPlan.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" + +using namespace lldb; +using namespace lldb_private; + +EmulateInstruction* +EmulateInstruction::FindPlugin (const ArchSpec &arch, InstructionType supported_inst_type, const char *plugin_name) +{ + EmulateInstructionCreateInstance create_callback = NULL; + if (plugin_name) + { + ConstString const_plugin_name (plugin_name); + create_callback = PluginManager::GetEmulateInstructionCreateCallbackForPluginName (const_plugin_name); + if (create_callback) + { + EmulateInstruction *emulate_insn_ptr = create_callback(arch, supported_inst_type); + if (emulate_insn_ptr) + return emulate_insn_ptr; + } + } + else + { + for (uint32_t idx = 0; (create_callback = PluginManager::GetEmulateInstructionCreateCallbackAtIndex(idx)) != NULL; ++idx) + { + EmulateInstruction *emulate_insn_ptr = create_callback(arch, supported_inst_type); + if (emulate_insn_ptr) + return emulate_insn_ptr; + } + } + return NULL; +} + +EmulateInstruction::EmulateInstruction (const ArchSpec &arch) : + m_arch (arch), + m_baton (NULL), + m_read_mem_callback (&ReadMemoryDefault), + m_write_mem_callback (&WriteMemoryDefault), + m_read_reg_callback (&ReadRegisterDefault), + m_write_reg_callback (&WriteRegisterDefault), + m_addr (LLDB_INVALID_ADDRESS) +{ + ::memset (&m_opcode, 0, sizeof (m_opcode)); +} + + +bool +EmulateInstruction::ReadRegister (const RegisterInfo *reg_info, RegisterValue& reg_value) +{ + if (m_read_reg_callback) + return m_read_reg_callback (this, m_baton, reg_info, reg_value); + return false; +} + +bool +EmulateInstruction::ReadRegister (uint32_t reg_kind, uint32_t reg_num, RegisterValue& reg_value) +{ + RegisterInfo reg_info; + if (GetRegisterInfo(reg_kind, reg_num, reg_info)) + return ReadRegister (®_info, reg_value); + return false; +} + +uint64_t +EmulateInstruction::ReadRegisterUnsigned (uint32_t reg_kind, + uint32_t reg_num, + uint64_t fail_value, + bool *success_ptr) +{ + RegisterValue reg_value; + if (ReadRegister (reg_kind, reg_num, reg_value)) + return reg_value.GetAsUInt64(fail_value, success_ptr); + if (success_ptr) + *success_ptr = false; + return fail_value; +} + +uint64_t +EmulateInstruction::ReadRegisterUnsigned (const RegisterInfo *reg_info, + uint64_t fail_value, + bool *success_ptr) +{ + RegisterValue reg_value; + if (ReadRegister (reg_info, reg_value)) + return reg_value.GetAsUInt64(fail_value, success_ptr); + if (success_ptr) + *success_ptr = false; + return fail_value; +} + +bool +EmulateInstruction::WriteRegister (const Context &context, + const RegisterInfo *reg_info, + const RegisterValue& reg_value) +{ + if (m_write_reg_callback) + return m_write_reg_callback (this, m_baton, context, reg_info, reg_value); + return false; +} + +bool +EmulateInstruction::WriteRegister (const Context &context, + uint32_t reg_kind, + uint32_t reg_num, + const RegisterValue& reg_value) +{ + RegisterInfo reg_info; + if (GetRegisterInfo(reg_kind, reg_num, reg_info)) + return WriteRegister (context, ®_info, reg_value); + return false; +} + + +bool +EmulateInstruction::WriteRegisterUnsigned (const Context &context, + uint32_t reg_kind, + uint32_t reg_num, + uint64_t uint_value) +{ + + RegisterInfo reg_info; + if (GetRegisterInfo(reg_kind, reg_num, reg_info)) + { + RegisterValue reg_value; + if (reg_value.SetUInt(uint_value, reg_info.byte_size)) + return WriteRegister (context, ®_info, reg_value); + } + return false; +} + +bool +EmulateInstruction::WriteRegisterUnsigned (const Context &context, + const RegisterInfo *reg_info, + uint64_t uint_value) +{ + + if (reg_info) + { + RegisterValue reg_value; + if (reg_value.SetUInt(uint_value, reg_info->byte_size)) + return WriteRegister (context, reg_info, reg_value); + } + return false; +} + +size_t +EmulateInstruction::ReadMemory (const Context &context, + lldb::addr_t addr, + void *dst, + size_t dst_len) +{ + if (m_read_mem_callback) + return m_read_mem_callback (this, m_baton, context, addr, dst, dst_len) == dst_len; + return false; +} + +uint64_t +EmulateInstruction::ReadMemoryUnsigned (const Context &context, lldb::addr_t addr, size_t byte_size, uint64_t fail_value, bool *success_ptr) +{ + uint64_t uval64 = 0; + bool success = false; + if (byte_size <= 8) + { + uint8_t buf[sizeof(uint64_t)]; + size_t bytes_read = m_read_mem_callback (this, m_baton, context, addr, buf, byte_size); + if (bytes_read == byte_size) + { + lldb::offset_t offset = 0; + DataExtractor data (buf, byte_size, GetByteOrder(), GetAddressByteSize()); + uval64 = data.GetMaxU64 (&offset, byte_size); + success = true; + } + } + + if (success_ptr) + *success_ptr = success; + + if (!success) + uval64 = fail_value; + return uval64; +} + + +bool +EmulateInstruction::WriteMemoryUnsigned (const Context &context, + lldb::addr_t addr, + uint64_t uval, + size_t uval_byte_size) +{ + StreamString strm(Stream::eBinary, GetAddressByteSize(), GetByteOrder()); + strm.PutMaxHex64 (uval, uval_byte_size); + + size_t bytes_written = m_write_mem_callback (this, m_baton, context, addr, strm.GetData(), uval_byte_size); + if (bytes_written == uval_byte_size) + return true; + return false; +} + +bool +EmulateInstruction::WriteMemory (const Context &context, + lldb::addr_t addr, + const void *src, + size_t src_len) +{ + if (m_write_mem_callback) + return m_write_mem_callback (this, m_baton, context, addr, src, src_len) == src_len; + return false; +} + + +void +EmulateInstruction::SetBaton (void *baton) +{ + m_baton = baton; +} + +void +EmulateInstruction::SetCallbacks (ReadMemoryCallback read_mem_callback, + WriteMemoryCallback write_mem_callback, + ReadRegisterCallback read_reg_callback, + WriteRegisterCallback write_reg_callback) +{ + m_read_mem_callback = read_mem_callback; + m_write_mem_callback = write_mem_callback; + m_read_reg_callback = read_reg_callback; + m_write_reg_callback = write_reg_callback; +} + +void +EmulateInstruction::SetReadMemCallback (ReadMemoryCallback read_mem_callback) +{ + m_read_mem_callback = read_mem_callback; +} + + +void +EmulateInstruction::SetWriteMemCallback (WriteMemoryCallback write_mem_callback) +{ + m_write_mem_callback = write_mem_callback; +} + + +void +EmulateInstruction::SetReadRegCallback (ReadRegisterCallback read_reg_callback) +{ + m_read_reg_callback = read_reg_callback; +} + + +void +EmulateInstruction::SetWriteRegCallback (WriteRegisterCallback write_reg_callback) +{ + m_write_reg_callback = write_reg_callback; +} + + + +// +// Read & Write Memory and Registers callback functions. +// + +size_t +EmulateInstruction::ReadMemoryFrame (EmulateInstruction *instruction, + void *baton, + const Context &context, + lldb::addr_t addr, + void *dst, + size_t dst_len) +{ + if (!baton || dst == NULL || dst_len == 0) + return 0; + + StackFrame *frame = (StackFrame *) baton; + + ProcessSP process_sp (frame->CalculateProcess()); + if (process_sp) + { + Error error; + return process_sp->ReadMemory (addr, dst, dst_len, error); + } + return 0; +} + +size_t +EmulateInstruction::WriteMemoryFrame (EmulateInstruction *instruction, + void *baton, + const Context &context, + lldb::addr_t addr, + const void *src, + size_t src_len) +{ + if (!baton || src == NULL || src_len == 0) + return 0; + + StackFrame *frame = (StackFrame *) baton; + + ProcessSP process_sp (frame->CalculateProcess()); + if (process_sp) + { + Error error; + return process_sp->WriteMemory (addr, src, src_len, error); + } + + return 0; +} + +bool +EmulateInstruction::ReadRegisterFrame (EmulateInstruction *instruction, + void *baton, + const RegisterInfo *reg_info, + RegisterValue ®_value) +{ + if (!baton) + return false; + + StackFrame *frame = (StackFrame *) baton; + return frame->GetRegisterContext()->ReadRegister (reg_info, reg_value); +} + +bool +EmulateInstruction::WriteRegisterFrame (EmulateInstruction *instruction, + void *baton, + const Context &context, + const RegisterInfo *reg_info, + const RegisterValue ®_value) +{ + if (!baton) + return false; + + StackFrame *frame = (StackFrame *) baton; + return frame->GetRegisterContext()->WriteRegister (reg_info, reg_value); +} + +size_t +EmulateInstruction::ReadMemoryDefault (EmulateInstruction *instruction, + void *baton, + const Context &context, + lldb::addr_t addr, + void *dst, + size_t length) +{ + StreamFile strm (stdout, false); + strm.Printf (" Read from Memory (address = 0x%" PRIx64 ", length = %" PRIu64 ", context = ", addr, (uint64_t)length); + context.Dump (strm, instruction); + strm.EOL(); + *((uint64_t *) dst) = 0xdeadbeef; + return length; +} + +size_t +EmulateInstruction::WriteMemoryDefault (EmulateInstruction *instruction, + void *baton, + const Context &context, + lldb::addr_t addr, + const void *dst, + size_t length) +{ + StreamFile strm (stdout, false); + strm.Printf (" Write to Memory (address = 0x%" PRIx64 ", length = %" PRIu64 ", context = ", addr, (uint64_t)length); + context.Dump (strm, instruction); + strm.EOL(); + return length; +} + +bool +EmulateInstruction::ReadRegisterDefault (EmulateInstruction *instruction, + void *baton, + const RegisterInfo *reg_info, + RegisterValue ®_value) +{ + StreamFile strm (stdout, false); + strm.Printf (" Read Register (%s)\n", reg_info->name); + uint32_t reg_kind, reg_num; + if (GetBestRegisterKindAndNumber (reg_info, reg_kind, reg_num)) + reg_value.SetUInt64((uint64_t)reg_kind << 24 | reg_num); + else + reg_value.SetUInt64(0); + + return true; +} + +bool +EmulateInstruction::WriteRegisterDefault (EmulateInstruction *instruction, + void *baton, + const Context &context, + const RegisterInfo *reg_info, + const RegisterValue ®_value) +{ + StreamFile strm (stdout, false); + strm.Printf (" Write to Register (name = %s, value = " , reg_info->name); + reg_value.Dump(&strm, reg_info, false, false, eFormatDefault); + strm.PutCString (", context = "); + context.Dump (strm, instruction); + strm.EOL(); + return true; +} + +void +EmulateInstruction::Context::Dump (Stream &strm, + EmulateInstruction *instruction) const +{ + switch (type) + { + case eContextReadOpcode: + strm.PutCString ("reading opcode"); + break; + + case eContextImmediate: + strm.PutCString ("immediate"); + break; + + case eContextPushRegisterOnStack: + strm.PutCString ("push register"); + break; + + case eContextPopRegisterOffStack: + strm.PutCString ("pop register"); + break; + + case eContextAdjustStackPointer: + strm.PutCString ("adjust sp"); + break; + + case eContextSetFramePointer: + strm.PutCString ("set frame pointer"); + break; + + case eContextAdjustBaseRegister: + strm.PutCString ("adjusting (writing value back to) a base register"); + break; + + case eContextRegisterPlusOffset: + strm.PutCString ("register + offset"); + break; + + case eContextRegisterStore: + strm.PutCString ("store register"); + break; + + case eContextRegisterLoad: + strm.PutCString ("load register"); + break; + + case eContextRelativeBranchImmediate: + strm.PutCString ("relative branch immediate"); + break; + + case eContextAbsoluteBranchRegister: + strm.PutCString ("absolute branch register"); + break; + + case eContextSupervisorCall: + strm.PutCString ("supervisor call"); + break; + + case eContextTableBranchReadMemory: + strm.PutCString ("table branch read memory"); + break; + + case eContextWriteRegisterRandomBits: + strm.PutCString ("write random bits to a register"); + break; + + case eContextWriteMemoryRandomBits: + strm.PutCString ("write random bits to a memory address"); + break; + + case eContextArithmetic: + strm.PutCString ("arithmetic"); + break; + + case eContextReturnFromException: + strm.PutCString ("return from exception"); + break; + + default: + strm.PutCString ("unrecognized context."); + break; + } + + switch (info_type) + { + case eInfoTypeRegisterPlusOffset: + { + strm.Printf (" (reg_plus_offset = %s%+" PRId64 ")", + info.RegisterPlusOffset.reg.name, + info.RegisterPlusOffset.signed_offset); + } + break; + + case eInfoTypeRegisterPlusIndirectOffset: + { + strm.Printf (" (reg_plus_reg = %s + %s)", + info.RegisterPlusIndirectOffset.base_reg.name, + info.RegisterPlusIndirectOffset.offset_reg.name); + } + break; + + case eInfoTypeRegisterToRegisterPlusOffset: + { + strm.Printf (" (base_and_imm_offset = %s%+" PRId64 ", data_reg = %s)", + info.RegisterToRegisterPlusOffset.base_reg.name, + info.RegisterToRegisterPlusOffset.offset, + info.RegisterToRegisterPlusOffset.data_reg.name); + } + break; + + case eInfoTypeRegisterToRegisterPlusIndirectOffset: + { + strm.Printf (" (base_and_reg_offset = %s + %s, data_reg = %s)", + info.RegisterToRegisterPlusIndirectOffset.base_reg.name, + info.RegisterToRegisterPlusIndirectOffset.offset_reg.name, + info.RegisterToRegisterPlusIndirectOffset.data_reg.name); + } + break; + + case eInfoTypeRegisterRegisterOperands: + { + strm.Printf (" (register to register binary op: %s and %s)", + info.RegisterRegisterOperands.operand1.name, + info.RegisterRegisterOperands.operand2.name); + } + break; + + case eInfoTypeOffset: + strm.Printf (" (signed_offset = %+" PRId64 ")", info.signed_offset); + break; + + case eInfoTypeRegister: + strm.Printf (" (reg = %s)", info.reg.name); + break; + + case eInfoTypeImmediate: + strm.Printf (" (unsigned_immediate = %" PRIu64 " (0x%16.16" PRIx64 "))", + info.unsigned_immediate, + info.unsigned_immediate); + break; + + case eInfoTypeImmediateSigned: + strm.Printf (" (signed_immediate = %+" PRId64 " (0x%16.16" PRIx64 "))", + info.signed_immediate, + info.signed_immediate); + break; + + case eInfoTypeAddress: + strm.Printf (" (address = 0x%" PRIx64 ")", info.address); + break; + + case eInfoTypeISAAndImmediate: + strm.Printf (" (isa = %u, unsigned_immediate = %u (0x%8.8x))", + info.ISAAndImmediate.isa, + info.ISAAndImmediate.unsigned_data32, + info.ISAAndImmediate.unsigned_data32); + break; + + case eInfoTypeISAAndImmediateSigned: + strm.Printf (" (isa = %u, signed_immediate = %i (0x%8.8x))", + info.ISAAndImmediateSigned.isa, + info.ISAAndImmediateSigned.signed_data32, + info.ISAAndImmediateSigned.signed_data32); + break; + + case eInfoTypeISA: + strm.Printf (" (isa = %u)", info.isa); + break; + + case eInfoTypeNoArgs: + break; + } +} + +bool +EmulateInstruction::SetInstruction (const Opcode &opcode, const Address &inst_addr, Target *target) +{ + m_opcode = opcode; + m_addr = LLDB_INVALID_ADDRESS; + if (inst_addr.IsValid()) + { + if (target) + m_addr = inst_addr.GetLoadAddress (target); + if (m_addr == LLDB_INVALID_ADDRESS) + m_addr = inst_addr.GetFileAddress (); + } + return true; +} + +bool +EmulateInstruction::GetBestRegisterKindAndNumber (const RegisterInfo *reg_info, + uint32_t ®_kind, + uint32_t ®_num) +{ + // Generic and DWARF should be the two most popular register kinds when + // emulating instructions since they are the most platform agnostic... + reg_num = reg_info->kinds[eRegisterKindGeneric]; + if (reg_num != LLDB_INVALID_REGNUM) + { + reg_kind = eRegisterKindGeneric; + return true; + } + + reg_num = reg_info->kinds[eRegisterKindDWARF]; + if (reg_num != LLDB_INVALID_REGNUM) + { + reg_kind = eRegisterKindDWARF; + return true; + } + + reg_num = reg_info->kinds[eRegisterKindLLDB]; + if (reg_num != LLDB_INVALID_REGNUM) + { + reg_kind = eRegisterKindLLDB; + return true; + } + + reg_num = reg_info->kinds[eRegisterKindGCC]; + if (reg_num != LLDB_INVALID_REGNUM) + { + reg_kind = eRegisterKindGCC; + return true; + } + + reg_num = reg_info->kinds[eRegisterKindGDB]; + if (reg_num != LLDB_INVALID_REGNUM) + { + reg_kind = eRegisterKindGDB; + return true; + } + return false; +} + +uint32_t +EmulateInstruction::GetInternalRegisterNumber (RegisterContext *reg_ctx, const RegisterInfo ®_info) +{ + uint32_t reg_kind, reg_num; + if (reg_ctx && GetBestRegisterKindAndNumber (®_info, reg_kind, reg_num)) + return reg_ctx->ConvertRegisterKindToRegisterNumber (reg_kind, reg_num); + return LLDB_INVALID_REGNUM; +} + + +bool +EmulateInstruction::CreateFunctionEntryUnwind (UnwindPlan &unwind_plan) +{ + unwind_plan.Clear(); + return false; +} + + diff --git a/contrib/llvm/tools/lldb/source/Core/Error.cpp b/contrib/llvm/tools/lldb/source/Core/Error.cpp new file mode 100644 index 00000000000..e29f12f0b2c --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Core/Error.cpp @@ -0,0 +1,399 @@ +//===-- Error.cpp -----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// C Includes +#include + +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/Error.h" +#include "lldb/Core/Log.h" +#include "llvm/ADT/SmallVector.h" +#include +#include +#include + +#if defined (__arm__) && defined (__APPLE__) +#include +#endif + +using namespace lldb; +using namespace lldb_private; + +Error::Error (): + m_code (0), + m_type (eErrorTypeInvalid), + m_string () +{ +} + +//---------------------------------------------------------------------- +// Default constructor +//---------------------------------------------------------------------- +Error::Error(ValueType err, ErrorType type) : + m_code (err), + m_type (type), + m_string () +{ +} + +Error::Error (const Error &rhs) : + m_code (rhs.m_code), + m_type (rhs.m_type), + m_string (rhs.m_string) +{ +} + +Error::Error (const char* err_str): + m_code (0), + m_type (eErrorTypeInvalid), + m_string () +{ + SetErrorString(err_str); +} + +//---------------------------------------------------------------------- +// Assignment operator +//---------------------------------------------------------------------- +const Error& +Error::operator = (const Error& rhs) +{ + if (this != &rhs) + { + m_code = rhs.m_code; + m_type = rhs.m_type; + m_string = rhs.m_string; + } + return *this; +} + + +//---------------------------------------------------------------------- +// Assignment operator +//---------------------------------------------------------------------- +const Error& +Error::operator = (uint32_t err) +{ + m_code = err; + m_type = eErrorTypeMachKernel; + m_string.clear(); + return *this; +} + +Error::~Error() +{ +} + +//---------------------------------------------------------------------- +// Get the error value as a NULL C string. The error string will be +// fetched and cached on demand. The cached error string value will +// remain until the error value is changed or cleared. +//---------------------------------------------------------------------- +const char * +Error::AsCString(const char *default_error_str) const +{ + if (Success()) + return NULL; + + if (m_string.empty()) + { + const char *s = NULL; + switch (m_type) + { + case eErrorTypeMachKernel: +#if defined (__APPLE__) + s = ::mach_error_string (m_code); +#endif + break; + + case eErrorTypePOSIX: + s = ::strerror (m_code); + break; + + default: + break; + } + if (s) + m_string.assign(s); + } + if (m_string.empty()) + { + if (default_error_str) + m_string.assign(default_error_str); + else + return NULL; // User wanted a NULL string back... + } + return m_string.c_str(); +} + + +//---------------------------------------------------------------------- +// Clear the error and any cached error string that it might contain. +//---------------------------------------------------------------------- +void +Error::Clear () +{ + m_code = 0; + m_type = eErrorTypeGeneric; + m_string.clear(); +} + +//---------------------------------------------------------------------- +// Access the error value. +//---------------------------------------------------------------------- +Error::ValueType +Error::GetError () const +{ + return m_code; +} + +//---------------------------------------------------------------------- +// Access the error type. +//---------------------------------------------------------------------- +ErrorType +Error::GetType () const +{ + return m_type; +} + +//---------------------------------------------------------------------- +// Retuns true if this object contains an value that describes an +// error or otherwise non-success result. +//---------------------------------------------------------------------- +bool +Error::Fail () const +{ + return m_code != 0; +} + +//---------------------------------------------------------------------- +// Log the error given a string with format. If the this object +// contains an error code, update the error string to contain the +// "error: " followed by the formatted string, followed by the error +// value and any string that describes the current error. This +// allows more context to be given to an error string that remains +// cached in this object. Logging always occurs even when the error +// code contains a non-error value. +//---------------------------------------------------------------------- +void +Error::PutToLog (Log *log, const char *format, ...) +{ + char *arg_msg = NULL; + va_list args; + va_start (args, format); + ::vasprintf (&arg_msg, format, args); + va_end (args); + + if (arg_msg != NULL) + { + if (Fail()) + { + const char *err_str = AsCString(); + if (err_str == NULL) + err_str = "???"; + + SetErrorStringWithFormat("error: %s err = %s (0x%8.8x)", arg_msg, err_str, m_code); + if (log) + log->Error("%s", m_string.c_str()); + } + else + { + if (log) + log->Printf("%s err = 0x%8.8x", arg_msg, m_code); + } + ::free (arg_msg); + } +} + +//---------------------------------------------------------------------- +// Log the error given a string with format. If the this object +// contains an error code, update the error string to contain the +// "error: " followed by the formatted string, followed by the error +// value and any string that describes the current error. This +// allows more context to be given to an error string that remains +// cached in this object. Logging only occurs even when the error +// code contains a error value. +//---------------------------------------------------------------------- +void +Error::LogIfError (Log *log, const char *format, ...) +{ + if (Fail()) + { + char *arg_msg = NULL; + va_list args; + va_start (args, format); + ::vasprintf (&arg_msg, format, args); + va_end (args); + + if (arg_msg != NULL) + { + const char *err_str = AsCString(); + if (err_str == NULL) + err_str = "???"; + + SetErrorStringWithFormat("error: %s err = %s (0x%8.8x)", arg_msg, err_str, m_code); + if (log) + log->Error("%s", m_string.c_str()); + + ::free (arg_msg); + } + } +} + +//---------------------------------------------------------------------- +// Set accesssor for the error value to "err" and the type to +// "eErrorTypeMachKernel" +//---------------------------------------------------------------------- +void +Error::SetMachError (uint32_t err) +{ + m_code = err; + m_type = eErrorTypeMachKernel; + m_string.clear(); +} + +//---------------------------------------------------------------------- +// Set accesssor for the error value and type. +//---------------------------------------------------------------------- +void +Error::SetError (ValueType err, ErrorType type) +{ + m_code = err; + m_type = type; + m_string.clear(); +} + +//---------------------------------------------------------------------- +// Update the error value to be "errno" and update the type to +// be "POSIX". +//---------------------------------------------------------------------- +void +Error::SetErrorToErrno() +{ + m_code = errno; + m_type = eErrorTypePOSIX; + m_string.clear(); +} + +//---------------------------------------------------------------------- +// Update the error value to be LLDB_GENERIC_ERROR and update the type +// to be "Generic". +//---------------------------------------------------------------------- +void +Error::SetErrorToGenericError () +{ + m_code = LLDB_GENERIC_ERROR; + m_type = eErrorTypeGeneric; + m_string.clear(); +} + +//---------------------------------------------------------------------- +// Set accessor for the error string value for a specific error. +// This allows any string to be supplied as an error explanation. +// The error string value will remain until the error value is +// cleared or a new error value/type is assigned. +//---------------------------------------------------------------------- +void +Error::SetErrorString (const char *err_str) +{ + if (err_str && err_str[0]) + { + // If we have an error string, we should always at least have + // an error set to a generic value. + if (Success()) + SetErrorToGenericError(); + m_string = err_str; + } + else + m_string.clear(); +} + +//------------------------------------------------------------------ +/// Set the current error string to a formatted error string. +/// +/// @param format +/// A printf style format string +//------------------------------------------------------------------ +int +Error::SetErrorStringWithFormat (const char *format, ...) +{ + if (format && format[0]) + { + va_list args; + va_start (args, format); + int length = SetErrorStringWithVarArg (format, args); + va_end (args); + return length; + } + else + { + m_string.clear(); + } + return 0; +} + +int +Error::SetErrorStringWithVarArg (const char *format, va_list args) +{ + if (format && format[0]) + { + // If we have an error string, we should always at least have + // an error set to a generic value. + if (Success()) + SetErrorToGenericError(); + + // Try and fit our error into a 1024 byte buffer first... + llvm::SmallVector buf; + buf.resize(1024); + // Copy in case our first call to vsnprintf doesn't fit into our + // allocated buffer above + va_list copy_args; + va_copy (copy_args, args); + unsigned length = ::vsnprintf (buf.data(), buf.size(), format, args); + if (length >= buf.size()) + { + // The error formatted string didn't fit into our buffer, resize it + // to the exact needed size, and retry + buf.resize(length + 1); + length = ::vsnprintf (buf.data(), buf.size(), format, copy_args); + va_end (copy_args); + assert (length < buf.size()); + } + m_string.assign(buf.data(), length); + va_end (args); + return length; + } + else + { + m_string.clear(); + } + return 0; +} + + +//---------------------------------------------------------------------- +// Returns true if the error code in this object is considered a +// successful return value. +//---------------------------------------------------------------------- +bool +Error::Success() const +{ + return m_code == 0; +} + +bool +Error::WasInterrupted() const +{ + if (m_type == eErrorTypePOSIX && m_code == EINTR) + return true; + else + return false; +} + diff --git a/contrib/llvm/tools/lldb/source/Core/Event.cpp b/contrib/llvm/tools/lldb/source/Core/Event.cpp new file mode 100644 index 00000000000..2d4899dd6dc --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Core/Event.cpp @@ -0,0 +1,225 @@ +//===-- Event.cpp -----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/Event.h" +#include "lldb/Core/Broadcaster.h" +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/State.h" +#include "lldb/Core/Stream.h" +#include "lldb/Host/Endian.h" +#include "lldb/Target/Process.h" +#include + +using namespace lldb; +using namespace lldb_private; + +//---------------------------------------------------------------------- +// Event constructor +//---------------------------------------------------------------------- +Event::Event (Broadcaster *broadcaster, uint32_t event_type, EventData *data) : + m_broadcaster (broadcaster), + m_type (event_type), + m_data_ap (data) +{ +} + +Event::Event(uint32_t event_type, EventData *data) : + m_broadcaster (NULL), // Set by the broadcaster when this event gets broadcast + m_type (event_type), + m_data_ap (data) +{ +} + + +//---------------------------------------------------------------------- +// Event destructor +//---------------------------------------------------------------------- +Event::~Event () +{ +} + +void +Event::Dump (Stream *s) const +{ + if (m_broadcaster) + { + StreamString event_name; + if (m_broadcaster->GetEventNames (event_name, m_type, false)) + s->Printf("%p Event: broadcaster = %p (%s), type = 0x%8.8x (%s), data = ", + this, + m_broadcaster, + m_broadcaster->GetBroadcasterName().GetCString(), + m_type, + event_name.GetString().c_str()); + else + s->Printf("%p Event: broadcaster = %p (%s), type = 0x%8.8x, data = ", + this, + m_broadcaster, + m_broadcaster->GetBroadcasterName().GetCString(), + m_type); + } + else + s->Printf("%p Event: broadcaster = NULL, type = 0x%8.8x, data = ", this, m_type); + + if (m_data_ap.get() == NULL) + s->Printf (""); + else + { + s->PutChar('{'); + m_data_ap->Dump (s); + s->PutChar('}'); + } +} + +void +Event::DoOnRemoval () +{ + if (m_data_ap.get()) + m_data_ap->DoOnRemoval (this); +} + +EventData::EventData() +{ +} + +EventData::~EventData() +{ +} + +void +EventData::Dump (Stream *s) const +{ + s->PutCString ("Generic Event Data"); +} + +EventDataBytes::EventDataBytes () : + m_bytes() +{ +} + +EventDataBytes::EventDataBytes (const char *cstr) : + m_bytes() +{ + SetBytesFromCString (cstr); +} + +EventDataBytes::EventDataBytes (const void *src, size_t src_len) : + m_bytes() +{ + SetBytes (src, src_len); +} + +EventDataBytes::~EventDataBytes() +{ +} + +const ConstString & +EventDataBytes::GetFlavorString () +{ + static ConstString g_flavor ("EventDataBytes"); + return g_flavor; +} + +const ConstString & +EventDataBytes::GetFlavor () const +{ + return EventDataBytes::GetFlavorString (); +} + +void +EventDataBytes::Dump (Stream *s) const +{ + size_t num_printable_chars = std::count_if (m_bytes.begin(), m_bytes.end(), isprint); + if (num_printable_chars == m_bytes.size()) + { + s->Printf("\"%s\"", m_bytes.c_str()); + } + else if (m_bytes.size() > 0) + { + DataExtractor data; + data.SetData(&m_bytes[0], m_bytes.size(), lldb::endian::InlHostByteOrder()); + data.Dump(s, 0, eFormatBytes, 1, m_bytes.size(), 32, LLDB_INVALID_ADDRESS, 0, 0); + } +} + +const void * +EventDataBytes::GetBytes() const +{ + if (m_bytes.empty()) + return NULL; + return &m_bytes[0]; +} + +size_t +EventDataBytes::GetByteSize() const +{ + return m_bytes.size (); +} + +void +EventDataBytes::SetBytes (const void *src, size_t src_len) +{ + if (src && src_len > 0) + m_bytes.assign ((const char *)src, src_len); + else + m_bytes.clear(); +} + +void +EventDataBytes::SetBytesFromCString (const char *cstr) +{ + if (cstr && cstr[0]) + m_bytes.assign (cstr); + else + m_bytes.clear(); +} + + +const void * +EventDataBytes::GetBytesFromEvent (const Event *event_ptr) +{ + const EventDataBytes *e = GetEventDataFromEvent (event_ptr); + if (e) + return e->GetBytes(); + return NULL; +} + +size_t +EventDataBytes::GetByteSizeFromEvent (const Event *event_ptr) +{ + const EventDataBytes *e = GetEventDataFromEvent (event_ptr); + if (e) + return e->GetByteSize(); + return 0; +} + +const EventDataBytes * +EventDataBytes::GetEventDataFromEvent (const Event *event_ptr) +{ + if (event_ptr) + { + const EventData *event_data = event_ptr->GetData(); + if (event_data && event_data->GetFlavor() == EventDataBytes::GetFlavorString()) + return static_cast (event_data); + } + return NULL; +} + +void +EventDataBytes::SwapBytes (std::string &new_bytes) +{ + m_bytes.swap (new_bytes); +} + + diff --git a/contrib/llvm/tools/lldb/source/Core/FileLineResolver.cpp b/contrib/llvm/tools/lldb/source/Core/FileLineResolver.cpp new file mode 100644 index 00000000000..15cbbe6ff9e --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Core/FileLineResolver.cpp @@ -0,0 +1,117 @@ +//===-- FileLineResolver.cpp ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Core/FileLineResolver.h" + +// Project includes +#include "lldb/lldb-private-log.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Symbol/CompileUnit.h" +#include "lldb/Symbol/LineTable.h" + +using namespace lldb; +using namespace lldb_private; + +//---------------------------------------------------------------------- +// FileLineResolver: +//---------------------------------------------------------------------- +FileLineResolver::FileLineResolver +( + const FileSpec &file_spec, + uint32_t line_no, + bool check_inlines +) : + Searcher (), + m_file_spec (file_spec), + m_line_number (line_no), + m_inlines (check_inlines) +{ +} + +FileLineResolver::~FileLineResolver () +{ +} + +Searcher::CallbackReturn +FileLineResolver::SearchCallback +( + SearchFilter &filter, + SymbolContext &context, + Address *addr, + bool containing +) +{ + CompileUnit *cu = context.comp_unit; + + if (m_inlines || m_file_spec.Compare(*cu, m_file_spec, m_file_spec.GetDirectory())) + { + uint32_t start_file_idx = 0; + uint32_t file_idx = cu->GetSupportFiles().FindFileIndex(start_file_idx, m_file_spec, false); + if (file_idx != UINT32_MAX) + { + LineTable *line_table = cu->GetLineTable(); + if (line_table) + { + if (m_line_number == 0) + { + // Match all lines in a file... + const bool append = true; + while (file_idx != UINT32_MAX) + { + line_table->FineLineEntriesForFileIndex (file_idx, append, m_sc_list); + // Get the next file index in case we have multiple file + // entries for the same file + file_idx = cu->GetSupportFiles().FindFileIndex(file_idx + 1, m_file_spec, false); + } + } + else + { + // Match a specific line in a file... + } + } + } + } + return Searcher::eCallbackReturnContinue; +} + +Searcher::Depth +FileLineResolver::GetDepth() +{ + return Searcher::eDepthCompUnit; +} + +void +FileLineResolver::GetDescription (Stream *s) +{ + s->Printf ("File and line resolver for file: \"%s\" line: %u", + m_file_spec.GetPath().c_str(), + m_line_number); +} + +void +FileLineResolver::Clear() +{ + m_file_spec.Clear(); + m_line_number = UINT32_MAX; + m_sc_list.Clear(); + m_inlines = true; +} + +void +FileLineResolver::Reset (const FileSpec &file_spec, + uint32_t line, + bool check_inlines) +{ + m_file_spec = file_spec; + m_line_number = line; + m_sc_list.Clear(); + m_inlines = check_inlines; +} + diff --git a/contrib/llvm/tools/lldb/source/Core/FileSpecList.cpp b/contrib/llvm/tools/lldb/source/Core/FileSpecList.cpp new file mode 100644 index 00000000000..0cec8faa6bc --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Core/FileSpecList.cpp @@ -0,0 +1,234 @@ +//===-- FileSpecList.cpp ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +#include "lldb/Core/FileSpecList.h" +#include "lldb/Core/Stream.h" +#include + +using namespace lldb_private; +using namespace std; + +//------------------------------------------------------------------ +// Default constructor +//------------------------------------------------------------------ +FileSpecList::FileSpecList() : + m_files() +{ +} + +//------------------------------------------------------------------ +// Copy constructor +//------------------------------------------------------------------ +FileSpecList::FileSpecList(const FileSpecList& rhs) : + m_files(rhs.m_files) +{ +} + +//------------------------------------------------------------------ +// Destructor +//------------------------------------------------------------------ +FileSpecList::~FileSpecList() +{ +} + +//------------------------------------------------------------------ +// Assignment operator +//------------------------------------------------------------------ +const FileSpecList& +FileSpecList::operator= (const FileSpecList& rhs) +{ + if (this != &rhs) + m_files = rhs.m_files; + return *this; +} + +//------------------------------------------------------------------ +// Append the "file_spec" to the end of the file spec list. +//------------------------------------------------------------------ +void +FileSpecList::Append(const FileSpec &file_spec) +{ + m_files.push_back(file_spec); +} + +//------------------------------------------------------------------ +// Only append the "file_spec" if this list doesn't already contain +// it. +// +// Returns true if "file_spec" was added, false if this list already +// contained a copy of "file_spec". +//------------------------------------------------------------------ +bool +FileSpecList::AppendIfUnique(const FileSpec &file_spec) +{ + collection::iterator pos, end = m_files.end(); + if (find(m_files.begin(), end, file_spec) == end) + { + m_files.push_back(file_spec); + return true; + } + return false; +} + +//------------------------------------------------------------------ +// Clears the file list. +//------------------------------------------------------------------ +void +FileSpecList::Clear() +{ + m_files.clear(); +} + +//------------------------------------------------------------------ +// Dumps the file list to the supplied stream pointer "s". +//------------------------------------------------------------------ +void +FileSpecList::Dump(Stream *s, const char *separator_cstr) const +{ + collection::const_iterator pos, end = m_files.end(); + for (pos = m_files.begin(); pos != end; ++pos) + { + pos->Dump(s); + if (separator_cstr && ((pos + 1) != end)) + s->PutCString(separator_cstr); + } +} + +//------------------------------------------------------------------ +// Find the index of the file in the file spec list that matches +// "file_spec" starting "start_idx" entries into the file spec list. +// +// Returns the valid index of the file that matches "file_spec" if +// it is found, else UINT32_MAX is returned. +//------------------------------------------------------------------ +size_t +FileSpecList::FindFileIndex (size_t start_idx, const FileSpec &file_spec, bool full) const +{ + const size_t num_files = m_files.size(); + + // When looking for files, we will compare only the filename if the + // FILE_SPEC argument is empty + bool compare_filename_only = file_spec.GetDirectory().IsEmpty(); + + for (size_t idx = start_idx; idx < num_files; ++idx) + { + if (compare_filename_only) + { + if (m_files[idx].GetFilename() == file_spec.GetFilename()) + return idx; + } + else + { + if (FileSpec::Equal (m_files[idx], file_spec, full)) + return idx; + } + } + + // We didn't find the file, return an invalid index + return UINT32_MAX; +} + +//------------------------------------------------------------------ +// Returns the FileSpec object at index "idx". If "idx" is out of +// range, then an empty FileSpec object will be returned. +//------------------------------------------------------------------ +const FileSpec & +FileSpecList::GetFileSpecAtIndex(size_t idx) const +{ + + if (idx < m_files.size()) + return m_files[idx]; + static FileSpec g_empty_file_spec; + return g_empty_file_spec; +} + +const FileSpec * +FileSpecList::GetFileSpecPointerAtIndex(size_t idx) const +{ + if (idx < m_files.size()) + return &m_files[idx]; + return NULL; +} + +//------------------------------------------------------------------ +// Return the size in bytes that this object takes in memory. This +// returns the size in bytes of this object's member variables and +// any FileSpec objects its member variables contain, the result +// doesn't not include the string values for the directories any +// filenames as those are in shared string pools. +//------------------------------------------------------------------ +size_t +FileSpecList::MemorySize () const +{ + size_t mem_size = sizeof(FileSpecList); + collection::const_iterator pos, end = m_files.end(); + for (pos = m_files.begin(); pos != end; ++pos) + { + mem_size += pos->MemorySize(); + } + + return mem_size; +} + +//------------------------------------------------------------------ +// Return the number of files in the file spec list. +//------------------------------------------------------------------ +size_t +FileSpecList::GetSize() const +{ + return m_files.size(); +} + +size_t +FileSpecList::GetFilesMatchingPartialPath (const char *path, bool dir_okay, FileSpecList &matches) +{ +#if 0 // FIXME: Just sketching... + matches.Clear(); + FileSpec path_spec = FileSpec (path); + if (path_spec.Exists ()) + { + FileSpec::FileType type = path_spec.GetFileType(); + if (type == FileSpec::eFileTypeSymbolicLink) + // Shouldn't there be a Resolve on a file spec that real-path's it? + { + } + + if (type == FileSpec::eFileTypeRegular + || (type == FileSpec::eFileTypeDirectory && dir_okay)) + { + matches.Append (path_spec); + return 1; + } + else if (type == FileSpec::eFileTypeDirectory) + { + // Fill the match list with all the files in the directory: + + } + else + { + return 0; + } + + } + else + { + ConstString dir_name = path_spec.GetDirectory(); + Constring file_name = GetFilename(); + if (dir_name == NULL) + { + // Match files in the CWD. + } + else + { + // Match files in the given directory: + + } + } +#endif + return 0; +} diff --git a/contrib/llvm/tools/lldb/source/Core/History.cpp b/contrib/llvm/tools/lldb/source/Core/History.cpp new file mode 100644 index 00000000000..0105dce730d --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Core/History.cpp @@ -0,0 +1,26 @@ +//===-- History.cpp ---------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Core/History.h" + +// C Includes +#include +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/Stream.h" + +using namespace lldb; +using namespace lldb_private; + +void +HistorySourceUInt::DumpHistoryEvent (Stream &strm, HistoryEvent event) +{ + strm.Printf ("%s %" PRIu64, m_name.c_str(), (uint64_t)((uintptr_t)event)); +} diff --git a/contrib/llvm/tools/lldb/source/Core/InputReader.cpp b/contrib/llvm/tools/lldb/source/Core/InputReader.cpp new file mode 100644 index 00000000000..cbaa671bcba --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Core/InputReader.cpp @@ -0,0 +1,387 @@ +//===-- InputReader.cpp -----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include + +#include "lldb/Core/InputReader.h" +#include "lldb/Core/Debugger.h" +#include "lldb/Interpreter/CommandInterpreter.h" + +using namespace lldb; +using namespace lldb_private; + +InputReader::InputReader (Debugger &debugger) : + m_debugger (debugger), + m_callback (NULL), + m_callback_baton (NULL), + m_end_token (), + m_granularity (eInputReaderGranularityInvalid), + m_done (true), + m_echo (true), + m_active (false), + m_reader_done (false), + m_user_input(), + m_save_user_input(false) +{ +} + +InputReader::~InputReader () +{ +} + +Error +InputReader::Initialize +( + Callback callback, + void *baton, + lldb::InputReaderGranularity granularity, + const char *end_token, + const char *prompt, + bool echo +) +{ + Error err; + m_callback = callback; + m_callback_baton = baton, + m_granularity = granularity; + if (end_token != NULL) + m_end_token = end_token; + if (prompt != NULL) + m_prompt = prompt; + m_done = true; + m_echo = echo; + + if (m_granularity == eInputReaderGranularityInvalid) + { + err.SetErrorString ("Invalid read token size: Reader must be initialized with a token size other than 'eInputReaderGranularityInvalid'."); + } + else + if (end_token != NULL && granularity != eInputReaderGranularityInvalid) + { + if (granularity == eInputReaderGranularityByte) + { + // Check to see if end_token is longer than one byte. + + if (strlen (end_token) > 1) + { + err.SetErrorString ("Invalid end token: End token cannot be larger than specified token size (byte)."); + } + } + else if (granularity == eInputReaderGranularityWord) + { + // Check to see if m_end_token contains any white space (i.e. is multiple words). + + const char *white_space = " \t\n"; + size_t pos = m_end_token.find_first_of (white_space); + if (pos != std::string::npos) + { + err.SetErrorString ("Invalid end token: End token cannot be larger than specified token size (word)."); + } + } + else + { + // Check to see if m_end_token contains any newlines; cannot handle multi-line end tokens. + + size_t pos = m_end_token.find_first_of ('\n'); + if (pos != std::string::npos) + { + err.SetErrorString ("Invalid end token: End token cannot contain a newline."); + } + } + } + + m_done = err.Fail(); + + return err; +} + +size_t +InputReader::HandleRawBytes (const char *bytes, size_t bytes_len) +{ + const char *end_token = NULL; + + if (m_end_token.empty() == false) + { + end_token = ::strstr (bytes, m_end_token.c_str()); + if (end_token >= bytes + bytes_len) + end_token = NULL; + } + + const char *p = bytes; + const char *end = bytes + bytes_len; + + switch (m_granularity) + { + case eInputReaderGranularityInvalid: + break; + + case eInputReaderGranularityByte: + while (p < end) + { + if (end_token == p) + { + p += m_end_token.size(); + SetIsDone(true); + break; + } + + if (m_callback (m_callback_baton, *this, eInputReaderGotToken, p, 1) == 0) + break; + ++p; + if (IsDone()) + break; + } + // Return how many bytes were handled. + return p - bytes; + break; + + + case eInputReaderGranularityWord: + { + char quote = '\0'; + const char *word_start = NULL; + bool send_word = false; + for (; p < end; ++p, send_word = false) + { + if (end_token && end_token == p) + { + m_end_token.size(); + SetIsDone(true); + break; + } + + const char ch = *p; + if (isspace(ch) && (!quote || (quote == ch && p[-1] != '\\'))) + { + // We have a space character or the terminating quote + send_word = word_start != NULL; + quote = '\0'; + } + else if (quote) + { + // We are in the middle of a quoted character + continue; + } + else if (ch == '"' || ch == '\'' || ch == '`') + quote = ch; + else if (word_start == NULL) + { + // We have the first character in a word + word_start = p; + } + + if (send_word) + { + const size_t word_len = p - word_start; + size_t bytes_handled = m_callback (m_callback_baton, + *this, + eInputReaderGotToken, + word_start, + word_len); + + if (bytes_handled != word_len) + return word_start - bytes + bytes_handled; + + if (IsDone()) + return p - bytes; + } + } + } + break; + + + case eInputReaderGranularityLine: + { + const char *line_start = bytes; + const char *end_line = NULL; + while (p < end) + { + const char ch = *p; + if (ch == '\n' || ch == '\r') + { + size_t line_length = p - line_start; + // Now skip the newline character + ++p; + // Skip a complete DOS newline if we run into one + if (ch == 0xd && p < end && *p == 0xa) + ++p; + + if (line_start <= end_token && end_token < line_start + line_length) + { + SetIsDone(true); + m_callback (m_callback_baton, + *this, + eInputReaderGotToken, + line_start, + end_token - line_start); + + return p - bytes; + } + + size_t bytes_handled = m_callback (m_callback_baton, + *this, + eInputReaderGotToken, + line_start, + line_length); + + end_line = p; + + if (bytes_handled != line_length) + { + // The input reader wasn't able to handle all the data + return line_start - bytes + bytes_handled; + } + + + if (IsDone()) + return p - bytes; + + line_start = p; + } + else + { + ++p; + } + } + + if (end_line) + return end_line - bytes; + } + break; + + + case eInputReaderGranularityAll: + { + // Nothing should be handle unless we see our end token + if (end_token) + { + size_t length = end_token - bytes; + size_t bytes_handled = m_callback (m_callback_baton, + *this, + eInputReaderGotToken, + bytes, + length); + m_done = true; + + p += bytes_handled + m_end_token.size(); + + // Consume any white space, such as newlines, beyond the end token + + while (p < end && isspace(*p)) + ++p; + + if (bytes_handled != length) + return bytes_handled; + else + { + return p - bytes; + //return bytes_handled + m_end_token.size(); + } + } + return 0; + } + break; + } + return 0; +} + +const char * +InputReader::GetPrompt () const +{ + if (!m_prompt.empty()) + return m_prompt.c_str(); + else + return NULL; +} + +void +InputReader::RefreshPrompt () +{ + if (m_debugger.GetCommandInterpreter().GetBatchCommandMode()) + return; + + if (!m_prompt.empty()) + { + File &out_file = m_debugger.GetOutputFile(); + if (out_file.IsValid()) + { + out_file.Printf ("%s", m_prompt.c_str()); + out_file.Flush(); + } + } +} + +void +InputReader::Notify (InputReaderAction notification) +{ + switch (notification) + { + case eInputReaderActivate: + case eInputReaderReactivate: + m_active = true; + m_reader_done.SetValue(false, eBroadcastAlways); + break; + + case eInputReaderDeactivate: + case eInputReaderDone: + m_active = false; + break; + + case eInputReaderAsynchronousOutputWritten: + break; + + case eInputReaderInterrupt: + case eInputReaderEndOfFile: + break; + + case eInputReaderGotToken: + return; // We don't notify the tokens here, it is done in HandleRawBytes + } + if (m_callback) + m_callback (m_callback_baton, *this, notification, NULL, 0); + if (notification == eInputReaderDone) + m_reader_done.SetValue(true, eBroadcastAlways); +} + +void +InputReader::WaitOnReaderIsDone () +{ + m_reader_done.WaitForValueEqualTo (true); +} + +const char * +InputReader::GranularityAsCString (lldb::InputReaderGranularity granularity) +{ + switch (granularity) + { + case eInputReaderGranularityInvalid: return "invalid"; + case eInputReaderGranularityByte: return "byte"; + case eInputReaderGranularityWord: return "word"; + case eInputReaderGranularityLine: return "line"; + case eInputReaderGranularityAll: return "all"; + } + + static char unknown_state_string[64]; + snprintf(unknown_state_string, sizeof (unknown_state_string), "InputReaderGranularity = %i", granularity); + return unknown_state_string; +} + +bool +InputReader::HandlerData::GetBatchMode() +{ + return reader.GetDebugger().GetCommandInterpreter().GetBatchCommandMode(); +} + +lldb::StreamSP +InputReader::HandlerData::GetOutStream() +{ + return reader.GetDebugger().GetAsyncOutputStream(); +} diff --git a/contrib/llvm/tools/lldb/source/Core/InputReaderEZ.cpp b/contrib/llvm/tools/lldb/source/Core/InputReaderEZ.cpp new file mode 100644 index 00000000000..7a865bdc509 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Core/InputReaderEZ.cpp @@ -0,0 +1,91 @@ +//===-- InputReaderEZ.cpp ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Core/InputReaderEZ.h" + +using namespace lldb; +using namespace lldb_private; + +size_t +InputReaderEZ::Callback_Impl(void *baton, + InputReader &reader, + lldb::InputReaderAction notification, + const char *bytes, + size_t bytes_len) + +{ + HandlerData hand_data(reader, + bytes, + bytes_len, + baton); + + switch (notification) + { + case eInputReaderActivate: + reader.ActivateHandler(hand_data); + break; + case eInputReaderDeactivate: + reader.DeactivateHandler(hand_data); + break; + case eInputReaderReactivate: + reader.ReactivateHandler(hand_data); + break; + case eInputReaderAsynchronousOutputWritten: + reader.AsynchronousOutputWrittenHandler(hand_data); + break; + case eInputReaderGotToken: + { + if (reader.GetSaveUserInput()) + reader.GetUserInput().AppendString(bytes, bytes_len); + reader.GotTokenHandler(hand_data); + } + break; + case eInputReaderInterrupt: + reader.InterruptHandler(hand_data); + break; + case eInputReaderEndOfFile: + reader.EOFHandler(hand_data); + break; + case eInputReaderDone: + reader.DoneHandler(hand_data); + break; + } + return bytes_len; +} + +Error +InputReaderEZ::Initialize(void* baton, + lldb::InputReaderGranularity token_size, + const char* end_token, + const char *prompt, + bool echo) +{ + return InputReader::Initialize(Callback_Impl, + baton, + token_size, + end_token, + prompt, + echo); +} + +Error +InputReaderEZ::Initialize(InitializationParameters& params) +{ + Error ret = Initialize(params.m_baton, + params.m_token_size, + params.m_end_token, + params.m_prompt, + params.m_echo); + m_save_user_input = params.m_save_user_input; + return ret; +} + +InputReaderEZ::~InputReaderEZ () +{ +} diff --git a/contrib/llvm/tools/lldb/source/Core/InputReaderStack.cpp b/contrib/llvm/tools/lldb/source/Core/InputReaderStack.cpp new file mode 100644 index 00000000000..764ea26550f --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Core/InputReaderStack.cpp @@ -0,0 +1,80 @@ +//===-- InputReaderStack.cpp ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Core/InputReaderStack.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes + + +using namespace lldb; +using namespace lldb_private; + +InputReaderStack::InputReaderStack () : + m_input_readers (), + m_input_readers_mutex (Mutex::eMutexTypeRecursive) +{ +} + +InputReaderStack::~InputReaderStack () +{ +} + +size_t +InputReaderStack::GetSize () const +{ + Mutex::Locker locker (m_input_readers_mutex); + return m_input_readers.size(); +} + +void +InputReaderStack::Push (const lldb::InputReaderSP& reader_sp) +{ + if (reader_sp) + { + Mutex::Locker locker (m_input_readers_mutex); + m_input_readers.push (reader_sp); + } +} + +bool +InputReaderStack::IsEmpty () const +{ + Mutex::Locker locker (m_input_readers_mutex); + return m_input_readers.empty(); +} + +InputReaderSP +InputReaderStack::Top () +{ + InputReaderSP input_reader_sp; + { + Mutex::Locker locker (m_input_readers_mutex); + if (!m_input_readers.empty()) + input_reader_sp = m_input_readers.top(); + } + + return input_reader_sp; +} + +void +InputReaderStack::Pop () +{ + Mutex::Locker locker (m_input_readers_mutex); + if (!m_input_readers.empty()) + m_input_readers.pop(); +} + +Mutex & +InputReaderStack::GetStackMutex () +{ + return m_input_readers_mutex; +} diff --git a/contrib/llvm/tools/lldb/source/Core/Language.cpp b/contrib/llvm/tools/lldb/source/Core/Language.cpp new file mode 100644 index 00000000000..af62af37da0 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Core/Language.cpp @@ -0,0 +1,151 @@ +//===-- Language.cpp --------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-private.h" +#include "lldb/Core/Language.h" +#include "lldb/Core/Stream.h" +#include + +using namespace lldb; +using namespace lldb_private; + +#define ENUM_TO_DCSTREAM(x) case x: s->PutCString(#x); return + +struct LanguageStrings +{ + const char * names[3]; +}; + +static LanguageStrings +g_languages[] = +{ + { { "unknown" , NULL , NULL } }, + { { "c89" , NULL , "ISO C:1989" } }, + { { NULL , NULL , "K&R C" } }, + { { "ada83" , "Ada83" , "ISO Ada:1983" } }, + { { "c++" , "cxx" , "ISO C++:1998" } }, + { { "cobol74" , "Cobol74" , "ISO Cobol:1974" } }, + { { "cobol" , "Cobol85" , "ISO Cobol:1985." } }, + { { "f77" , "Fortran77" , "ISO Fortran 77." } }, + { { "f90" , "Fortran90" , "ISO Fortran 90" } }, + { { "pascal" , "Pascal83" , "ISO Pascal:1983" } }, + { { "modula2" , "Modula2" , "ISO Modula-2:1996" } }, + { { "java" , NULL , "Java" } }, + { { "c" , "C99" , "ISO C:1999" } }, + { { "ada" , "Ada95" , "ISO Ada:1995" } }, + { { "f95" , "Fortran95" , "ISO Fortran 95" } }, + { { "PLI" , NULL , "ANSI PL/I:1976" } }, + { { "objc" , NULL , "Objective-C" } }, + { { "objc++" , NULL , "Objective-C++" } }, + { { "upc" , NULL , "Unified Parallel C" } }, + { { "d" , NULL , "D" } }, + { { "python" , NULL , "Python" } } +}; + +static const size_t +g_num_languages = sizeof(g_languages)/sizeof(LanguageStrings); + +Language::Language(LanguageType language) : + m_language (language) +{ +} + +Language::~Language() +{ +} + +LanguageType +Language::GetLanguage() const +{ + return m_language; +} + +void +Language::Clear () +{ + m_language = eLanguageTypeUnknown; +} + +void +Language::SetLanguage(LanguageType language) +{ + m_language = language; +} + +bool +Language::SetLanguageFromCString(const char *language_cstr) +{ + size_t i, desc_idx; + const char *name; + + // First check the most common name for the languages + for (desc_idx=lldb::eDescriptionLevelBrief; desc_idxPutCString(lang_cstr); + else + s->Printf("Language(language = 0x%4.4x)", m_language); +} + + + + +Stream& +lldb_private::operator << (Stream& s, const Language& language) +{ + language.Dump(&s); + return s; +} + diff --git a/contrib/llvm/tools/lldb/source/Core/Listener.cpp b/contrib/llvm/tools/lldb/source/Core/Listener.cpp new file mode 100644 index 00000000000..aca2b374092 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Core/Listener.cpp @@ -0,0 +1,557 @@ +//===-- Listener.cpp --------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Core/Listener.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/Broadcaster.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Core/Event.h" +#include "lldb/Host/TimeValue.h" +#include "lldb/lldb-private-log.h" +#include + +using namespace lldb; +using namespace lldb_private; + +Listener::Listener(const char *name) : + m_name (name), + m_broadcasters(), + m_broadcasters_mutex (Mutex::eMutexTypeRecursive), + m_events (), + m_events_mutex (Mutex::eMutexTypeRecursive), + m_cond_wait() +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_OBJECT)); + if (log) + log->Printf ("%p Listener::Listener('%s')", this, m_name.c_str()); +} + +Listener::~Listener() +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_OBJECT)); + Mutex::Locker locker (m_broadcasters_mutex); + + size_t num_managers = m_broadcaster_managers.size(); + + for (size_t i = 0; i < num_managers; i++) + m_broadcaster_managers[i]->RemoveListener(*this); + + if (log) + log->Printf ("%p Listener::~Listener('%s')", this, m_name.c_str()); + Clear(); +} + +void +Listener::Clear() +{ + Mutex::Locker locker(m_broadcasters_mutex); + broadcaster_collection::iterator pos, end = m_broadcasters.end(); + for (pos = m_broadcasters.begin(); pos != end; ++pos) + pos->first->RemoveListener (this, pos->second.event_mask); + m_broadcasters.clear(); + m_cond_wait.SetValue (false, eBroadcastNever); + m_broadcasters.clear(); + Mutex::Locker event_locker(m_events_mutex); + m_events.clear(); +} + +uint32_t +Listener::StartListeningForEvents (Broadcaster* broadcaster, uint32_t event_mask) +{ + if (broadcaster) + { + // Scope for "locker" + // Tell the broadcaster to add this object as a listener + { + Mutex::Locker locker(m_broadcasters_mutex); + m_broadcasters.insert(std::make_pair(broadcaster, BroadcasterInfo(event_mask))); + } + + uint32_t acquired_mask = broadcaster->AddListener (this, event_mask); + + if (event_mask != acquired_mask) + { + + } + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EVENTS)); + if (log) + log->Printf ("%p Listener::StartListeningForEvents (broadcaster = %p, mask = 0x%8.8x) acquired_mask = 0x%8.8x for %s", + this, + broadcaster, + event_mask, + acquired_mask, + m_name.c_str()); + + return acquired_mask; + + } + return 0; +} + +uint32_t +Listener::StartListeningForEvents (Broadcaster* broadcaster, uint32_t event_mask, HandleBroadcastCallback callback, void *callback_user_data) +{ + if (broadcaster) + { + // Scope for "locker" + // Tell the broadcaster to add this object as a listener + { + Mutex::Locker locker(m_broadcasters_mutex); + m_broadcasters.insert(std::make_pair(broadcaster, BroadcasterInfo(event_mask, callback, callback_user_data))); + } + + uint32_t acquired_mask = broadcaster->AddListener (this, event_mask); + + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EVENTS)); + if (log) + log->Printf ("%p Listener::StartListeningForEvents (broadcaster = %p, mask = 0x%8.8x, callback = %p, user_data = %p) acquired_mask = 0x%8.8x for %s", + this, broadcaster, event_mask, callback, callback_user_data, acquired_mask, m_name.c_str()); + + return acquired_mask; + } + return 0; +} + +bool +Listener::StopListeningForEvents (Broadcaster* broadcaster, uint32_t event_mask) +{ + if (broadcaster) + { + // Scope for "locker" + { + Mutex::Locker locker(m_broadcasters_mutex); + m_broadcasters.erase (broadcaster); + } + // Remove the broadcaster from our set of broadcasters + return broadcaster->RemoveListener (this, event_mask); + } + + return false; +} + +// Called when a Broadcaster is in its destuctor. We need to remove all +// knowledge of this broadcaster and any events that it may have queued up +void +Listener::BroadcasterWillDestruct (Broadcaster *broadcaster) +{ + // Scope for "broadcasters_locker" + { + Mutex::Locker broadcasters_locker(m_broadcasters_mutex); + m_broadcasters.erase (broadcaster); + } + + // Scope for "event_locker" + { + Mutex::Locker event_locker(m_events_mutex); + // Remove all events for this broadcaster object. + event_collection::iterator pos = m_events.begin(); + while (pos != m_events.end()) + { + if ((*pos)->GetBroadcaster() == broadcaster) + pos = m_events.erase(pos); + else + ++pos; + } + + if (m_events.empty()) + m_cond_wait.SetValue (false, eBroadcastNever); + + } +} + +void +Listener::BroadcasterManagerWillDestruct (BroadcasterManager *manager) +{ + // Just need to remove this broadcast manager from the list of managers: + broadcaster_manager_collection::iterator iter, end_iter = m_broadcaster_managers.end(); + iter = find(m_broadcaster_managers.begin(), end_iter, manager); + if (iter != end_iter) + m_broadcaster_managers.erase (iter); +} + +void +Listener::AddEvent (EventSP &event_sp) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EVENTS)); + if (log) + log->Printf ("%p Listener('%s')::AddEvent (event_sp = {%p})", this, m_name.c_str(), event_sp.get()); + + // Scope for "locker" + { + Mutex::Locker locker(m_events_mutex); + m_events.push_back (event_sp); + } + m_cond_wait.SetValue (true, eBroadcastAlways); +} + +class EventBroadcasterMatches +{ +public: + EventBroadcasterMatches (Broadcaster *broadcaster) : + m_broadcaster (broadcaster) { + } + + bool operator() (const EventSP &event_sp) const + { + if (event_sp->BroadcasterIs(m_broadcaster)) + return true; + else + return false; + } + +private: + Broadcaster *m_broadcaster; + +}; + +class EventMatcher +{ +public: + EventMatcher (Broadcaster *broadcaster, const ConstString *broadcaster_names, uint32_t num_broadcaster_names, uint32_t event_type_mask) : + m_broadcaster (broadcaster), + m_broadcaster_names (broadcaster_names), + m_num_broadcaster_names (num_broadcaster_names), + m_event_type_mask (event_type_mask) + { + } + + bool operator() (const EventSP &event_sp) const + { + if (m_broadcaster && !event_sp->BroadcasterIs(m_broadcaster)) + return false; + + if (m_broadcaster_names) + { + bool found_source = false; + const ConstString &event_broadcaster_name = event_sp->GetBroadcaster()->GetBroadcasterName(); + for (uint32_t i=0; iGetType()) + return true; + return false; + } + +private: + Broadcaster *m_broadcaster; + const ConstString *m_broadcaster_names; + const uint32_t m_num_broadcaster_names; + const uint32_t m_event_type_mask; +}; + + +bool +Listener::FindNextEventInternal +( + Broadcaster *broadcaster, // NULL for any broadcaster + const ConstString *broadcaster_names, // NULL for any event + uint32_t num_broadcaster_names, + uint32_t event_type_mask, + EventSP &event_sp, + bool remove) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EVENTS)); + + Mutex::Locker lock(m_events_mutex); + + if (m_events.empty()) + return false; + + + Listener::event_collection::iterator pos = m_events.end(); + + if (broadcaster == NULL && broadcaster_names == NULL && event_type_mask == 0) + { + pos = m_events.begin(); + } + else + { + pos = std::find_if (m_events.begin(), m_events.end(), EventMatcher (broadcaster, broadcaster_names, num_broadcaster_names, event_type_mask)); + } + + if (pos != m_events.end()) + { + event_sp = *pos; + + if (log) + log->Printf ("%p '%s' Listener::FindNextEventInternal(broadcaster=%p, broadcaster_names=%p[%u], event_type_mask=0x%8.8x, remove=%i) event %p", + this, + GetName(), + broadcaster, + broadcaster_names, + num_broadcaster_names, + event_type_mask, + remove, + event_sp.get()); + + if (remove) + { + m_events.erase(pos); + + if (m_events.empty()) + m_cond_wait.SetValue (false, eBroadcastNever); + } + + // Unlock the event queue here. We've removed this event and are about to return + // it so it should be okay to get the next event off the queue here - and it might + // be useful to do that in the "DoOnRemoval". + lock.Unlock(); + + // Don't call DoOnRemoval if you aren't removing the event... + if (remove) + event_sp->DoOnRemoval(); + + return true; + } + + event_sp.reset(); + return false; +} + +Event * +Listener::PeekAtNextEvent () +{ + EventSP event_sp; + if (FindNextEventInternal (NULL, NULL, 0, 0, event_sp, false)) + return event_sp.get(); + return NULL; +} + +Event * +Listener::PeekAtNextEventForBroadcaster (Broadcaster *broadcaster) +{ + EventSP event_sp; + if (FindNextEventInternal (broadcaster, NULL, 0, 0, event_sp, false)) + return event_sp.get(); + return NULL; +} + +Event * +Listener::PeekAtNextEventForBroadcasterWithType (Broadcaster *broadcaster, uint32_t event_type_mask) +{ + EventSP event_sp; + if (FindNextEventInternal (broadcaster, NULL, 0, event_type_mask, event_sp, false)) + return event_sp.get(); + return NULL; +} + + +bool +Listener::GetNextEventInternal +( + Broadcaster *broadcaster, // NULL for any broadcaster + const ConstString *broadcaster_names, // NULL for any event + uint32_t num_broadcaster_names, + uint32_t event_type_mask, + EventSP &event_sp +) +{ + return FindNextEventInternal (broadcaster, broadcaster_names, num_broadcaster_names, event_type_mask, event_sp, true); +} + +bool +Listener::GetNextEvent (EventSP &event_sp) +{ + return GetNextEventInternal (NULL, NULL, 0, 0, event_sp); +} + + +bool +Listener::GetNextEventForBroadcaster (Broadcaster *broadcaster, EventSP &event_sp) +{ + return GetNextEventInternal (broadcaster, NULL, 0, 0, event_sp); +} + +bool +Listener::GetNextEventForBroadcasterWithType (Broadcaster *broadcaster, uint32_t event_type_mask, EventSP &event_sp) +{ + return GetNextEventInternal (broadcaster, NULL, 0, event_type_mask, event_sp); +} + + +bool +Listener::WaitForEventsInternal +( + const TimeValue *timeout, + Broadcaster *broadcaster, // NULL for any broadcaster + const ConstString *broadcaster_names, // NULL for any event + uint32_t num_broadcaster_names, + uint32_t event_type_mask, + EventSP &event_sp +) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EVENTS)); + bool timed_out = false; + + if (log) + { + log->Printf ("%p Listener::WaitForEventsInternal (timeout = { %p }) for %s", + this, timeout, m_name.c_str()); + } + + while (1) + { + // Note, we don't want to lock the m_events_mutex in the call to GetNextEventInternal, since the DoOnRemoval + // code might require that new events be serviced. For instance, the Breakpoint Command's + if (GetNextEventInternal (broadcaster, broadcaster_names, num_broadcaster_names, event_type_mask, event_sp)) + return true; + + { + // Reset condition value to false, so we can wait for new events to be + // added that might meet our current filter + // But first poll for any new event that might satisfy our condition, and if so consume it, + // otherwise wait. + + Mutex::Locker event_locker(m_events_mutex); + const bool remove = false; + if (FindNextEventInternal (broadcaster, broadcaster_names, num_broadcaster_names, event_type_mask, event_sp, remove)) + continue; + else + m_cond_wait.SetValue (false, eBroadcastNever); + } + + if (m_cond_wait.WaitForValueEqualTo (true, timeout, &timed_out)) + continue; + + else if (timed_out) + { + log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EVENTS); + if (log) + log->Printf ("%p Listener::WaitForEventsInternal() timed out for %s", this, m_name.c_str()); + break; + } + else + { + log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EVENTS); + if (log) + log->Printf ("%p Listener::WaitForEventsInternal() unknown error for %s", this, m_name.c_str()); + break; + } + } + + return false; +} + +bool +Listener::WaitForEventForBroadcasterWithType +( + const TimeValue *timeout, + Broadcaster *broadcaster, + uint32_t event_type_mask, + EventSP &event_sp +) +{ + return WaitForEventsInternal (timeout, broadcaster, NULL, 0, event_type_mask, event_sp); +} + +bool +Listener::WaitForEventForBroadcaster +( + const TimeValue *timeout, + Broadcaster *broadcaster, + EventSP &event_sp +) +{ + return WaitForEventsInternal (timeout, broadcaster, NULL, 0, 0, event_sp); +} + +bool +Listener::WaitForEvent (const TimeValue *timeout, EventSP &event_sp) +{ + return WaitForEventsInternal (timeout, NULL, NULL, 0, 0, event_sp); +} + +//Listener::broadcaster_collection::iterator +//Listener::FindBroadcasterWithMask (Broadcaster *broadcaster, uint32_t event_mask, bool exact) +//{ +// broadcaster_collection::iterator pos; +// broadcaster_collection::iterator end = m_broadcasters.end(); +// for (pos = m_broadcasters.find (broadcaster); +// pos != end && pos->first == broadcaster; +// ++pos) +// { +// if (exact) +// { +// if ((event_mask & pos->second.event_mask) == event_mask) +// return pos; +// } +// else +// { +// if (event_mask & pos->second.event_mask) +// return pos; +// } +// } +// return end; +//} + +size_t +Listener::HandleBroadcastEvent (EventSP &event_sp) +{ + size_t num_handled = 0; + Mutex::Locker locker(m_broadcasters_mutex); + Broadcaster *broadcaster = event_sp->GetBroadcaster(); + broadcaster_collection::iterator pos; + broadcaster_collection::iterator end = m_broadcasters.end(); + for (pos = m_broadcasters.find (broadcaster); + pos != end && pos->first == broadcaster; + ++pos) + { + BroadcasterInfo info = pos->second; + if (event_sp->GetType () & info.event_mask) + { + if (info.callback != NULL) + { + info.callback (event_sp, info.callback_user_data); + ++num_handled; + } + } + } + return num_handled; +} + +uint32_t +Listener::StartListeningForEventSpec (BroadcasterManager &manager, + const BroadcastEventSpec &event_spec) +{ + // The BroadcasterManager mutex must be locked before m_broadcasters_mutex + // to avoid violating the lock hierarchy (manager before broadcasters). + Mutex::Locker manager_locker(manager.m_manager_mutex); + Mutex::Locker locker(m_broadcasters_mutex); + + uint32_t bits_acquired = manager.RegisterListenerForEvents(*this, event_spec); + if (bits_acquired) + m_broadcaster_managers.push_back(&manager); + + return bits_acquired; +} + +bool +Listener::StopListeningForEventSpec (BroadcasterManager &manager, + const BroadcastEventSpec &event_spec) +{ + Mutex::Locker locker(m_broadcasters_mutex); + return manager.UnregisterListenerForEvents (*this, event_spec); + +} + + diff --git a/contrib/llvm/tools/lldb/source/Core/Log.cpp b/contrib/llvm/tools/lldb/source/Core/Log.cpp new file mode 100644 index 00000000000..d73ab15f697 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Core/Log.cpp @@ -0,0 +1,529 @@ +//===-- Log.cpp -------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +// C Includes +#include +#include +#include +#include +#include + +// C++ Includes +#include +#include + +// Other libraries and framework includes +// Project includes +#include "lldb/Core/Debugger.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/StreamFile.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Host/Host.h" +#include "lldb/Host/TimeValue.h" +#include "lldb/Host/Mutex.h" +#include "lldb/Interpreter/Args.h" +using namespace lldb; +using namespace lldb_private; + +Log::Log () : + m_stream_sp(), + m_options(0), + m_mask_bits(0) +{ +} + +Log::Log (const StreamSP &stream_sp) : + m_stream_sp(stream_sp), + m_options(0), + m_mask_bits(0) +{ +} + +Log::~Log () +{ +} + +Flags & +Log::GetOptions() +{ + return m_options; +} + +const Flags & +Log::GetOptions() const +{ + return m_options; +} + +Flags & +Log::GetMask() +{ + return m_mask_bits; +} + +const Flags & +Log::GetMask() const +{ + return m_mask_bits; +} + + +//---------------------------------------------------------------------- +// All logging eventually boils down to this function call. If we have +// a callback registered, then we call the logging callback. If we have +// a valid file handle, we also log to the file. +//---------------------------------------------------------------------- +void +Log::PrintfWithFlagsVarArg (uint32_t flags, const char *format, va_list args) +{ + if (m_stream_sp) + { + static uint32_t g_sequence_id = 0; + StreamString header; + // Enabling the thread safe logging actually deadlocks right now. + // Need to fix this at some point. +// static Mutex g_LogThreadedMutex(Mutex::eMutexTypeRecursive); +// Mutex::Locker locker (g_LogThreadedMutex); + + // Add a sequence ID if requested + if (m_options.Test (LLDB_LOG_OPTION_PREPEND_SEQUENCE)) + header.Printf ("%u ", ++g_sequence_id); + + // Timestamp if requested + if (m_options.Test (LLDB_LOG_OPTION_PREPEND_TIMESTAMP)) + { + struct timeval tv = TimeValue::Now().GetAsTimeVal(); + header.Printf ("%9ld.%6.6d ", tv.tv_sec, (int32_t)tv.tv_usec); + } + + // Add the process and thread if requested + if (m_options.Test (LLDB_LOG_OPTION_PREPEND_PROC_AND_THREAD)) + header.Printf ("[%4.4x/%4.4" PRIx64 "]: ", getpid(), Host::GetCurrentThreadID()); + + // Add the process and thread if requested + if (m_options.Test (LLDB_LOG_OPTION_PREPEND_THREAD_NAME)) + { + std::string thread_name (Host::GetThreadName (getpid(), Host::GetCurrentThreadID())); + if (!thread_name.empty()) + header.Printf ("%s ", thread_name.c_str()); + } + + header.PrintfVarArg (format, args); + m_stream_sp->Printf("%s\n", header.GetData()); + + if (m_options.Test (LLDB_LOG_OPTION_BACKTRACE)) + Host::Backtrace (*m_stream_sp, 1024); + m_stream_sp->Flush(); + } +} + + +void +Log::PutCString (const char *cstr) +{ + Printf ("%s", cstr); +} + + +//---------------------------------------------------------------------- +// Simple variable argument logging with flags. +//---------------------------------------------------------------------- +void +Log::Printf(const char *format, ...) +{ + va_list args; + va_start (args, format); + PrintfWithFlagsVarArg (0, format, args); + va_end (args); +} + +void +Log::VAPrintf (const char *format, va_list args) +{ + PrintfWithFlagsVarArg (0, format, args); +} + + +//---------------------------------------------------------------------- +// Simple variable argument logging with flags. +//---------------------------------------------------------------------- +void +Log::PrintfWithFlags (uint32_t flags, const char *format, ...) +{ + va_list args; + va_start (args, format); + PrintfWithFlagsVarArg (flags, format, args); + va_end (args); +} + +//---------------------------------------------------------------------- +// Print debug strings if and only if the global debug option is set to +// a non-zero value. +//---------------------------------------------------------------------- +void +Log::Debug (const char *format, ...) +{ + if (GetOptions().Test(LLDB_LOG_OPTION_DEBUG)) + { + va_list args; + va_start (args, format); + PrintfWithFlagsVarArg (LLDB_LOG_FLAG_DEBUG, format, args); + va_end (args); + } +} + + +//---------------------------------------------------------------------- +// Print debug strings if and only if the global debug option is set to +// a non-zero value. +//---------------------------------------------------------------------- +void +Log::DebugVerbose (const char *format, ...) +{ + if (GetOptions().AllSet (LLDB_LOG_OPTION_DEBUG | LLDB_LOG_OPTION_VERBOSE)) + { + va_list args; + va_start (args, format); + PrintfWithFlagsVarArg (LLDB_LOG_FLAG_DEBUG | LLDB_LOG_FLAG_VERBOSE, format, args); + va_end (args); + } +} + + +//---------------------------------------------------------------------- +// Log only if all of the bits are set +//---------------------------------------------------------------------- +void +Log::LogIf (uint32_t bits, const char *format, ...) +{ + if (m_options.AllSet (bits)) + { + va_list args; + va_start (args, format); + PrintfWithFlagsVarArg (0, format, args); + va_end (args); + } +} + + +//---------------------------------------------------------------------- +// Printing of errors that are not fatal. +//---------------------------------------------------------------------- +void +Log::Error (const char *format, ...) +{ + char *arg_msg = NULL; + va_list args; + va_start (args, format); + ::vasprintf (&arg_msg, format, args); + va_end (args); + + if (arg_msg != NULL) + { + PrintfWithFlags (LLDB_LOG_FLAG_ERROR, "error: %s", arg_msg); + free (arg_msg); + } +} + +//---------------------------------------------------------------------- +// Printing of errors that ARE fatal. Exit with ERR exit code +// immediately. +//---------------------------------------------------------------------- +void +Log::FatalError (int err, const char *format, ...) +{ + char *arg_msg = NULL; + va_list args; + va_start (args, format); + ::vasprintf (&arg_msg, format, args); + va_end (args); + + if (arg_msg != NULL) + { + PrintfWithFlags (LLDB_LOG_FLAG_ERROR | LLDB_LOG_FLAG_FATAL, "error: %s", arg_msg); + ::free (arg_msg); + } + ::exit (err); +} + + +//---------------------------------------------------------------------- +// Printing of warnings that are not fatal only if verbose mode is +// enabled. +//---------------------------------------------------------------------- +void +Log::Verbose (const char *format, ...) +{ + if (m_options.Test(LLDB_LOG_OPTION_VERBOSE)) + { + va_list args; + va_start (args, format); + PrintfWithFlagsVarArg (LLDB_LOG_FLAG_VERBOSE, format, args); + va_end (args); + } +} + +//---------------------------------------------------------------------- +// Printing of warnings that are not fatal only if verbose mode is +// enabled. +//---------------------------------------------------------------------- +void +Log::WarningVerbose (const char *format, ...) +{ + if (m_options.Test(LLDB_LOG_OPTION_VERBOSE)) + { + char *arg_msg = NULL; + va_list args; + va_start (args, format); + ::vasprintf (&arg_msg, format, args); + va_end (args); + + if (arg_msg != NULL) + { + PrintfWithFlags (LLDB_LOG_FLAG_WARNING | LLDB_LOG_FLAG_VERBOSE, "warning: %s", arg_msg); + free (arg_msg); + } + } +} +//---------------------------------------------------------------------- +// Printing of warnings that are not fatal. +//---------------------------------------------------------------------- +void +Log::Warning (const char *format, ...) +{ + char *arg_msg = NULL; + va_list args; + va_start (args, format); + ::vasprintf (&arg_msg, format, args); + va_end (args); + + if (arg_msg != NULL) + { + PrintfWithFlags (LLDB_LOG_FLAG_WARNING, "warning: %s", arg_msg); + free (arg_msg); + } +} + +typedef std::map CallbackMap; +typedef CallbackMap::iterator CallbackMapIter; + +typedef std::map LogChannelMap; +typedef LogChannelMap::iterator LogChannelMapIter; + + +// Surround our callback map with a singleton function so we don't have any +// global initializers. +static CallbackMap & +GetCallbackMap () +{ + static CallbackMap g_callback_map; + return g_callback_map; +} + +static LogChannelMap & +GetChannelMap () +{ + static LogChannelMap g_channel_map; + return g_channel_map; +} + +void +Log::RegisterLogChannel (const ConstString &channel, const Log::Callbacks &log_callbacks) +{ + GetCallbackMap().insert(std::make_pair(channel, log_callbacks)); +} + +bool +Log::UnregisterLogChannel (const ConstString &channel) +{ + return GetCallbackMap().erase(channel) != 0; +} + +bool +Log::GetLogChannelCallbacks (const ConstString &channel, Log::Callbacks &log_callbacks) +{ + CallbackMap &callback_map = GetCallbackMap (); + CallbackMapIter pos = callback_map.find(channel); + if (pos != callback_map.end()) + { + log_callbacks = pos->second; + return true; + } + ::memset (&log_callbacks, 0, sizeof(log_callbacks)); + return false; +} + +void +Log::EnableAllLogChannels +( + StreamSP &log_stream_sp, + uint32_t log_options, + const char **categories, + Stream *feedback_strm +) +{ + CallbackMap &callback_map = GetCallbackMap (); + CallbackMapIter pos, end = callback_map.end(); + + for (pos = callback_map.begin(); pos != end; ++pos) + pos->second.enable (log_stream_sp, log_options, categories, feedback_strm); + + LogChannelMap &channel_map = GetChannelMap (); + LogChannelMapIter channel_pos, channel_end = channel_map.end(); + for (channel_pos = channel_map.begin(); channel_pos != channel_end; ++channel_pos) + { + channel_pos->second->Enable (log_stream_sp, log_options, feedback_strm, categories); + } + +} + +void +Log::AutoCompleteChannelName (const char *channel_name, StringList &matches) +{ + LogChannelMap &map = GetChannelMap (); + LogChannelMapIter pos, end = map.end(); + for (pos = map.begin(); pos != end; ++pos) + { + const char *pos_channel_name = pos->first.GetCString(); + if (channel_name && channel_name[0]) + { + if (NameMatches (channel_name, eNameMatchStartsWith, pos_channel_name)) + { + matches.AppendString(pos_channel_name); + } + } + else + matches.AppendString(pos_channel_name); + + } +} + +void +Log::DisableAllLogChannels (Stream *feedback_strm) +{ + CallbackMap &callback_map = GetCallbackMap (); + CallbackMapIter pos, end = callback_map.end(); + const char *categories[1] = {NULL}; + + for (pos = callback_map.begin(); pos != end; ++pos) + pos->second.disable (categories, feedback_strm); + + LogChannelMap &channel_map = GetChannelMap (); + LogChannelMapIter channel_pos, channel_end = channel_map.end(); + for (channel_pos = channel_map.begin(); channel_pos != channel_end; ++channel_pos) + channel_pos->second->Disable (categories, feedback_strm); +} + +void +Log::Initialize() +{ + Log::Callbacks log_callbacks = { DisableLog, EnableLog, ListLogCategories }; + Log::RegisterLogChannel (ConstString("lldb"), log_callbacks); +} + +void +Log::Terminate () +{ + DisableAllLogChannels (NULL); +} + +void +Log::ListAllLogChannels (Stream *strm) +{ + CallbackMap &callback_map = GetCallbackMap (); + LogChannelMap &channel_map = GetChannelMap (); + + if (callback_map.empty() && channel_map.empty()) + { + strm->PutCString ("No logging channels are currently registered.\n"); + return; + } + + CallbackMapIter pos, end = callback_map.end(); + for (pos = callback_map.begin(); pos != end; ++pos) + pos->second.list_categories (strm); + + uint32_t idx = 0; + const char *name; + for (idx = 0; (name = PluginManager::GetLogChannelCreateNameAtIndex (idx)) != NULL; ++idx) + { + LogChannelSP log_channel_sp(LogChannel::FindPlugin (name)); + if (log_channel_sp) + log_channel_sp->ListCategories (strm); + } +} + +bool +Log::GetVerbose() const +{ + // FIXME: This has to be centralized between the stream and the log... + if (m_options.Test(LLDB_LOG_OPTION_VERBOSE)) + return true; + + if (m_stream_sp) + return m_stream_sp->GetVerbose(); + return false; +} + +//------------------------------------------------------------------ +// Returns true if the debug flag bit is set in this stream. +//------------------------------------------------------------------ +bool +Log::GetDebug() const +{ + if (m_stream_sp) + return m_stream_sp->GetDebug(); + return false; +} + + +LogChannelSP +LogChannel::FindPlugin (const char *plugin_name) +{ + LogChannelSP log_channel_sp; + LogChannelMap &channel_map = GetChannelMap (); + ConstString log_channel_name (plugin_name); + LogChannelMapIter pos = channel_map.find (log_channel_name); + if (pos == channel_map.end()) + { + ConstString const_plugin_name (plugin_name); + LogChannelCreateInstance create_callback = PluginManager::GetLogChannelCreateCallbackForPluginName (const_plugin_name); + if (create_callback) + { + log_channel_sp.reset(create_callback()); + if (log_channel_sp) + { + // Cache the one and only loaded instance of each log channel + // plug-in after it has been loaded once. + channel_map[log_channel_name] = log_channel_sp; + } + } + } + else + { + // We have already loaded an instance of this log channel class, + // so just return the cached instance. + log_channel_sp = pos->second; + } + return log_channel_sp; +} + +LogChannel::LogChannel () : + m_log_ap () +{ +} + +LogChannel::~LogChannel () +{ +} + + diff --git a/contrib/llvm/tools/lldb/source/Core/Mangled.cpp b/contrib/llvm/tools/lldb/source/Core/Mangled.cpp new file mode 100644 index 00000000000..4655eb131a6 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Core/Mangled.cpp @@ -0,0 +1,313 @@ +//===-- Mangled.cpp ---------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + + +// FreeBSD9-STABLE requires this to know about size_t in cxxabi.h +#include +#include + + +#include "llvm/ADT/DenseMap.h" + +#include "lldb/Core/ConstString.h" +#include "lldb/Core/Mangled.h" +#include "lldb/Core/RegularExpression.h" +#include "lldb/Core/Stream.h" +#include "lldb/Core/Timer.h" +#include +#include +#include + +using namespace lldb_private; + +static inline bool +cstring_is_mangled (const char *s) +{ + if (s) + return s[0] == '_' && s[1] == 'Z'; + return false; +} + +#pragma mark Mangled +//---------------------------------------------------------------------- +// Default constructor +//---------------------------------------------------------------------- +Mangled::Mangled () : + m_mangled(), + m_demangled() +{ +} + +//---------------------------------------------------------------------- +// Constructor with an optional string and a boolean indicating if it is +// the mangled version. +//---------------------------------------------------------------------- +Mangled::Mangled (const ConstString &s, bool mangled) : + m_mangled(), + m_demangled() +{ + if (s) + SetValue(s, mangled); +} + +Mangled::Mangled (const ConstString &s) : + m_mangled(), + m_demangled() +{ + if (s) + SetValue(s); +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +Mangled::~Mangled () +{ +} + +//---------------------------------------------------------------------- +// Convert to pointer operator. This allows code to check any Mangled +// objects to see if they contain anything valid using code such as: +// +// Mangled mangled(...); +// if (mangled) +// { ... +//---------------------------------------------------------------------- +Mangled::operator void* () const +{ + return (m_mangled) ? const_cast(this) : NULL; +} + +//---------------------------------------------------------------------- +// Logical NOT operator. This allows code to check any Mangled +// objects to see if they are invalid using code such as: +// +// Mangled mangled(...); +// if (!file_spec) +// { ... +//---------------------------------------------------------------------- +bool +Mangled::operator! () const +{ + return !m_mangled; +} + +//---------------------------------------------------------------------- +// Clear the mangled and demangled values. +//---------------------------------------------------------------------- +void +Mangled::Clear () +{ + m_mangled.Clear(); + m_demangled.Clear(); +} + + +//---------------------------------------------------------------------- +// Compare the the string values. +//---------------------------------------------------------------------- +int +Mangled::Compare (const Mangled& a, const Mangled& b) +{ + return ConstString::Compare(a.GetName(ePreferMangled), a.GetName(ePreferMangled)); +} + + + +//---------------------------------------------------------------------- +// Set the string value in this objects. If "mangled" is true, then +// the mangled named is set with the new value in "s", else the +// demangled name is set. +//---------------------------------------------------------------------- +void +Mangled::SetValue (const ConstString &s, bool mangled) +{ + if (s) + { + if (mangled) + { + m_demangled.Clear(); + m_mangled = s; + } + else + { + m_demangled = s; + m_mangled.Clear(); + } + } + else + { + m_demangled.Clear(); + m_mangled.Clear(); + } +} + +void +Mangled::SetValue (const ConstString &name) +{ + if (name) + { + if (cstring_is_mangled(name.GetCString())) + { + m_demangled.Clear(); + m_mangled = name; + } + else + { + m_demangled = name; + m_mangled.Clear(); + } + } + else + { + m_demangled.Clear(); + m_mangled.Clear(); + } +} + + +//---------------------------------------------------------------------- +// Generate the demangled name on demand using this accessor. Code in +// this class will need to use this accessor if it wishes to decode +// the demangled name. The result is cached and will be kept until a +// new string value is supplied to this object, or until the end of the +// object's lifetime. +//---------------------------------------------------------------------- +const ConstString& +Mangled::GetDemangledName () const +{ + // Check to make sure we have a valid mangled name and that we + // haven't already decoded our mangled name. + if (m_mangled && !m_demangled) + { + // We need to generate and cache the demangled name. + Timer scoped_timer (__PRETTY_FUNCTION__, + "Mangled::GetDemangledName (m_mangled = %s)", + m_mangled.GetCString()); + + // Don't bother running anything that isn't mangled + const char *mangled_cstr = m_mangled.GetCString(); + if (cstring_is_mangled(mangled_cstr)) + { + if (!m_mangled.GetMangledCounterpart(m_demangled)) + { + // We didn't already mangle this name, demangle it and if all goes well + // add it to our map. + char *demangled_name = abi::__cxa_demangle (mangled_cstr, NULL, NULL, NULL); + + if (demangled_name) + { + m_demangled.SetCStringWithMangledCounterpart(demangled_name, m_mangled); + free (demangled_name); + } + } + } + if (!m_demangled) + { + // Set the demangled string to the empty string to indicate we + // tried to parse it once and failed. + m_demangled.SetCString(""); + } + } + + return m_demangled; +} + + +bool +Mangled::NameMatches (const RegularExpression& regex) const +{ + if (m_mangled && regex.Execute (m_mangled.AsCString())) + return true; + + if (GetDemangledName() && regex.Execute (m_demangled.AsCString())) + return true; + return false; +} + +//---------------------------------------------------------------------- +// Get the demangled name if there is one, else return the mangled name. +//---------------------------------------------------------------------- +const ConstString& +Mangled::GetName (Mangled::NamePreference preference) const +{ + if (preference == ePreferDemangled) + { + // Call the accessor to make sure we get a demangled name in case + // it hasn't been demangled yet... + if (GetDemangledName()) + return m_demangled; + return m_mangled; + } + else + { + if (m_mangled) + return m_mangled; + return GetDemangledName(); + } +} + +//---------------------------------------------------------------------- +// Dump a Mangled object to stream "s". We don't force our +// demangled name to be computed currently (we don't use the accessor). +//---------------------------------------------------------------------- +void +Mangled::Dump (Stream *s) const +{ + if (m_mangled) + { + *s << ", mangled = " << m_mangled; + } + if (m_demangled) + { + const char * demangled = m_demangled.AsCString(); + s->Printf(", demangled = %s", demangled[0] ? demangled : ""); + } +} + +//---------------------------------------------------------------------- +// Dumps a debug version of this string with extra object and state +// information to stream "s". +//---------------------------------------------------------------------- +void +Mangled::DumpDebug (Stream *s) const +{ + s->Printf("%*p: Mangled mangled = ", (int)sizeof(void*) * 2, this); + m_mangled.DumpDebug(s); + s->Printf(", demangled = "); + m_demangled.DumpDebug(s); +} + +//---------------------------------------------------------------------- +// Return the size in byte that this object takes in memory. The size +// includes the size of the objects it owns, and not the strings that +// it references because they are shared strings. +//---------------------------------------------------------------------- +size_t +Mangled::MemorySize () const +{ + return m_mangled.MemorySize() + m_demangled.MemorySize(); +} + +//---------------------------------------------------------------------- +// Dump OBJ to the supplied stream S. +//---------------------------------------------------------------------- +Stream& +operator << (Stream& s, const Mangled& obj) +{ + if (obj.GetMangledName()) + s << "mangled = '" << obj.GetMangledName() << "'"; + + const ConstString& demangled = obj.GetDemangledName(); + if (demangled) + s << ", demangled = '" << demangled << '\''; + else + s << ", demangled = "; + return s; +} diff --git a/contrib/llvm/tools/lldb/source/Core/Module.cpp b/contrib/llvm/tools/lldb/source/Core/Module.cpp new file mode 100644 index 00000000000..4252ed4cb6c --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Core/Module.cpp @@ -0,0 +1,1609 @@ +//===-- Module.cpp ----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "lldb/Core/Error.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/DataBuffer.h" +#include "lldb/Core/DataBufferHeap.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/ModuleList.h" +#include "lldb/Core/ModuleSpec.h" +#include "lldb/Core/RegularExpression.h" +#include "lldb/Core/Section.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Core/Timer.h" +#include "lldb/Host/Host.h" +#include "lldb/Host/Symbols.h" +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Interpreter/ScriptInterpreter.h" +#include "lldb/lldb-private-log.h" +#include "lldb/Symbol/CompileUnit.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Symbol/SymbolContext.h" +#include "lldb/Symbol/SymbolVendor.h" +#include "lldb/Target/CPPLanguageRuntime.h" +#include "lldb/Target/ObjCLanguageRuntime.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Target.h" +#include "lldb/Symbol/SymbolFile.h" + +using namespace lldb; +using namespace lldb_private; + +// Shared pointers to modules track module lifetimes in +// targets and in the global module, but this collection +// will track all module objects that are still alive +typedef std::vector ModuleCollection; + +static ModuleCollection & +GetModuleCollection() +{ + // This module collection needs to live past any module, so we could either make it a + // shared pointer in each module or just leak is. Since it is only an empty vector by + // the time all the modules have gone away, we just leak it for now. If we decide this + // is a big problem we can introduce a Finalize method that will tear everything down in + // a predictable order. + + static ModuleCollection *g_module_collection = NULL; + if (g_module_collection == NULL) + g_module_collection = new ModuleCollection(); + + return *g_module_collection; +} + +Mutex * +Module::GetAllocationModuleCollectionMutex() +{ + // NOTE: The mutex below must be leaked since the global module list in + // the ModuleList class will get torn at some point, and we can't know + // if it will tear itself down before the "g_module_collection_mutex" below + // will. So we leak a Mutex object below to safeguard against that + + static Mutex *g_module_collection_mutex = NULL; + if (g_module_collection_mutex == NULL) + g_module_collection_mutex = new Mutex (Mutex::eMutexTypeRecursive); // NOTE: known leak + return g_module_collection_mutex; +} + +size_t +Module::GetNumberAllocatedModules () +{ + Mutex::Locker locker (GetAllocationModuleCollectionMutex()); + return GetModuleCollection().size(); +} + +Module * +Module::GetAllocatedModuleAtIndex (size_t idx) +{ + Mutex::Locker locker (GetAllocationModuleCollectionMutex()); + ModuleCollection &modules = GetModuleCollection(); + if (idx < modules.size()) + return modules[idx]; + return NULL; +} +#if 0 + +// These functions help us to determine if modules are still loaded, yet don't require that +// you have a command interpreter and can easily be called from an external debugger. +namespace lldb { + + void + ClearModuleInfo (void) + { + const bool mandatory = true; + ModuleList::RemoveOrphanSharedModules(mandatory); + } + + void + DumpModuleInfo (void) + { + Mutex::Locker locker (Module::GetAllocationModuleCollectionMutex()); + ModuleCollection &modules = GetModuleCollection(); + const size_t count = modules.size(); + printf ("%s: %" PRIu64 " modules:\n", __PRETTY_FUNCTION__, (uint64_t)count); + for (size_t i=0; iGetDescription(&strm, eDescriptionLevelFull); + printf ("%p: shared = %i, ref_count = %3u, module = %s\n", + module, + in_shared_module_list, + (uint32_t)module->use_count(), + strm.GetString().c_str()); + } + } +} + +#endif + +Module::Module (const ModuleSpec &module_spec) : + m_mutex (Mutex::eMutexTypeRecursive), + m_mod_time (module_spec.GetFileSpec().GetModificationTime()), + m_arch (module_spec.GetArchitecture()), + m_uuid (), + m_file (module_spec.GetFileSpec()), + m_platform_file(module_spec.GetPlatformFileSpec()), + m_symfile_spec (module_spec.GetSymbolFileSpec()), + m_object_name (module_spec.GetObjectName()), + m_object_offset (module_spec.GetObjectOffset()), + m_object_mod_time (module_spec.GetObjectModificationTime()), + m_objfile_sp (), + m_symfile_ap (), + m_ast (), + m_source_mappings (), + m_did_load_objfile (false), + m_did_load_symbol_vendor (false), + m_did_parse_uuid (false), + m_did_init_ast (false), + m_is_dynamic_loader_module (false), + m_file_has_changed (false), + m_first_file_changed_log (false) +{ + // Scope for locker below... + { + Mutex::Locker locker (GetAllocationModuleCollectionMutex()); + GetModuleCollection().push_back(this); + } + + Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_OBJECT|LIBLLDB_LOG_MODULES)); + if (log) + log->Printf ("%p Module::Module((%s) '%s%s%s%s')", + this, + m_arch.GetArchitectureName(), + m_file.GetPath().c_str(), + m_object_name.IsEmpty() ? "" : "(", + m_object_name.IsEmpty() ? "" : m_object_name.AsCString(""), + m_object_name.IsEmpty() ? "" : ")"); +} + +Module::Module(const FileSpec& file_spec, + const ArchSpec& arch, + const ConstString *object_name, + off_t object_offset, + const TimeValue *object_mod_time_ptr) : + m_mutex (Mutex::eMutexTypeRecursive), + m_mod_time (file_spec.GetModificationTime()), + m_arch (arch), + m_uuid (), + m_file (file_spec), + m_platform_file(), + m_symfile_spec (), + m_object_name (), + m_object_offset (object_offset), + m_object_mod_time (), + m_objfile_sp (), + m_symfile_ap (), + m_ast (), + m_source_mappings (), + m_did_load_objfile (false), + m_did_load_symbol_vendor (false), + m_did_parse_uuid (false), + m_did_init_ast (false), + m_is_dynamic_loader_module (false), + m_file_has_changed (false), + m_first_file_changed_log (false) +{ + // Scope for locker below... + { + Mutex::Locker locker (GetAllocationModuleCollectionMutex()); + GetModuleCollection().push_back(this); + } + + if (object_name) + m_object_name = *object_name; + + if (object_mod_time_ptr) + m_object_mod_time = *object_mod_time_ptr; + + Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_OBJECT|LIBLLDB_LOG_MODULES)); + if (log) + log->Printf ("%p Module::Module((%s) '%s%s%s%s')", + this, + m_arch.GetArchitectureName(), + m_file.GetPath().c_str(), + m_object_name.IsEmpty() ? "" : "(", + m_object_name.IsEmpty() ? "" : m_object_name.AsCString(""), + m_object_name.IsEmpty() ? "" : ")"); +} + +Module::~Module() +{ + // Lock our module down while we tear everything down to make sure + // we don't get any access to the module while it is being destroyed + Mutex::Locker locker (m_mutex); + // Scope for locker below... + { + Mutex::Locker locker (GetAllocationModuleCollectionMutex()); + ModuleCollection &modules = GetModuleCollection(); + ModuleCollection::iterator end = modules.end(); + ModuleCollection::iterator pos = std::find(modules.begin(), end, this); + assert (pos != end); + modules.erase(pos); + } + Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_OBJECT|LIBLLDB_LOG_MODULES)); + if (log) + log->Printf ("%p Module::~Module((%s) '%s%s%s%s')", + this, + m_arch.GetArchitectureName(), + m_file.GetPath().c_str(), + m_object_name.IsEmpty() ? "" : "(", + m_object_name.IsEmpty() ? "" : m_object_name.AsCString(""), + m_object_name.IsEmpty() ? "" : ")"); + // Release any auto pointers before we start tearing down our member + // variables since the object file and symbol files might need to make + // function calls back into this module object. The ordering is important + // here because symbol files can require the module object file. So we tear + // down the symbol file first, then the object file. + m_sections_ap.reset(); + m_symfile_ap.reset(); + m_objfile_sp.reset(); +} + +ObjectFile * +Module::GetMemoryObjectFile (const lldb::ProcessSP &process_sp, lldb::addr_t header_addr, Error &error) +{ + if (m_objfile_sp) + { + error.SetErrorString ("object file already exists"); + } + else + { + Mutex::Locker locker (m_mutex); + if (process_sp) + { + m_did_load_objfile = true; + std::unique_ptr data_ap (new DataBufferHeap (512, 0)); + Error readmem_error; + const size_t bytes_read = process_sp->ReadMemory (header_addr, + data_ap->GetBytes(), + data_ap->GetByteSize(), + readmem_error); + if (bytes_read == 512) + { + DataBufferSP data_sp(data_ap.release()); + m_objfile_sp = ObjectFile::FindPlugin(shared_from_this(), process_sp, header_addr, data_sp); + if (m_objfile_sp) + { + StreamString s; + s.Printf("0x%16.16" PRIx64, header_addr); + m_object_name.SetCString (s.GetData()); + + // Once we get the object file, update our module with the object file's + // architecture since it might differ in vendor/os if some parts were + // unknown. + m_objfile_sp->GetArchitecture (m_arch); + } + else + { + error.SetErrorString ("unable to find suitable object file plug-in"); + } + } + else + { + error.SetErrorStringWithFormat ("unable to read header from memory: %s", readmem_error.AsCString()); + } + } + else + { + error.SetErrorString ("invalid process"); + } + } + return m_objfile_sp.get(); +} + + +const lldb_private::UUID& +Module::GetUUID() +{ + Mutex::Locker locker (m_mutex); + if (m_did_parse_uuid == false) + { + ObjectFile * obj_file = GetObjectFile (); + + if (obj_file != NULL) + { + obj_file->GetUUID(&m_uuid); + m_did_parse_uuid = true; + } + } + return m_uuid; +} + +ClangASTContext & +Module::GetClangASTContext () +{ + Mutex::Locker locker (m_mutex); + if (m_did_init_ast == false) + { + ObjectFile * objfile = GetObjectFile(); + ArchSpec object_arch; + if (objfile && objfile->GetArchitecture(object_arch)) + { + m_did_init_ast = true; + + // LLVM wants this to be set to iOS or MacOSX; if we're working on + // a bare-boards type image, change the triple for llvm's benefit. + if (object_arch.GetTriple().getVendor() == llvm::Triple::Apple + && object_arch.GetTriple().getOS() == llvm::Triple::UnknownOS) + { + if (object_arch.GetTriple().getArch() == llvm::Triple::arm || + object_arch.GetTriple().getArch() == llvm::Triple::thumb) + { + object_arch.GetTriple().setOS(llvm::Triple::IOS); + } + else + { + object_arch.GetTriple().setOS(llvm::Triple::MacOSX); + } + } + m_ast.SetArchitecture (object_arch); + } + } + return m_ast; +} + +void +Module::ParseAllDebugSymbols() +{ + Mutex::Locker locker (m_mutex); + size_t num_comp_units = GetNumCompileUnits(); + if (num_comp_units == 0) + return; + + SymbolContext sc; + sc.module_sp = shared_from_this(); + SymbolVendor *symbols = GetSymbolVendor (); + + for (size_t cu_idx = 0; cu_idx < num_comp_units; cu_idx++) + { + sc.comp_unit = symbols->GetCompileUnitAtIndex(cu_idx).get(); + if (sc.comp_unit) + { + sc.function = NULL; + symbols->ParseVariablesForContext(sc); + + symbols->ParseCompileUnitFunctions(sc); + + for (size_t func_idx = 0; (sc.function = sc.comp_unit->GetFunctionAtIndex(func_idx).get()) != NULL; ++func_idx) + { + symbols->ParseFunctionBlocks(sc); + + // Parse the variables for this function and all its blocks + symbols->ParseVariablesForContext(sc); + } + + + // Parse all types for this compile unit + sc.function = NULL; + symbols->ParseTypes(sc); + } + } +} + +void +Module::CalculateSymbolContext(SymbolContext* sc) +{ + sc->module_sp = shared_from_this(); +} + +ModuleSP +Module::CalculateSymbolContextModule () +{ + return shared_from_this(); +} + +void +Module::DumpSymbolContext(Stream *s) +{ + s->Printf(", Module{%p}", this); +} + +size_t +Module::GetNumCompileUnits() +{ + Mutex::Locker locker (m_mutex); + Timer scoped_timer(__PRETTY_FUNCTION__, "Module::GetNumCompileUnits (module = %p)", this); + SymbolVendor *symbols = GetSymbolVendor (); + if (symbols) + return symbols->GetNumCompileUnits(); + return 0; +} + +CompUnitSP +Module::GetCompileUnitAtIndex (size_t index) +{ + Mutex::Locker locker (m_mutex); + size_t num_comp_units = GetNumCompileUnits (); + CompUnitSP cu_sp; + + if (index < num_comp_units) + { + SymbolVendor *symbols = GetSymbolVendor (); + if (symbols) + cu_sp = symbols->GetCompileUnitAtIndex(index); + } + return cu_sp; +} + +bool +Module::ResolveFileAddress (lldb::addr_t vm_addr, Address& so_addr) +{ + Mutex::Locker locker (m_mutex); + Timer scoped_timer(__PRETTY_FUNCTION__, "Module::ResolveFileAddress (vm_addr = 0x%" PRIx64 ")", vm_addr); + SectionList *section_list = GetSectionList(); + if (section_list) + return so_addr.ResolveAddressUsingFileSections(vm_addr, section_list); + return false; +} + +uint32_t +Module::ResolveSymbolContextForAddress (const Address& so_addr, uint32_t resolve_scope, SymbolContext& sc) +{ + Mutex::Locker locker (m_mutex); + uint32_t resolved_flags = 0; + + // Clear the result symbol context in case we don't find anything, but don't clear the target + sc.Clear(false); + + // Get the section from the section/offset address. + SectionSP section_sp (so_addr.GetSection()); + + // Make sure the section matches this module before we try and match anything + if (section_sp && section_sp->GetModule().get() == this) + { + // If the section offset based address resolved itself, then this + // is the right module. + sc.module_sp = shared_from_this(); + resolved_flags |= eSymbolContextModule; + + // Resolve the compile unit, function, block, line table or line + // entry if requested. + if (resolve_scope & eSymbolContextCompUnit || + resolve_scope & eSymbolContextFunction || + resolve_scope & eSymbolContextBlock || + resolve_scope & eSymbolContextLineEntry ) + { + SymbolVendor *symbols = GetSymbolVendor (); + if (symbols) + resolved_flags |= symbols->ResolveSymbolContext (so_addr, resolve_scope, sc); + } + + // Resolve the symbol if requested, but don't re-look it up if we've already found it. + if (resolve_scope & eSymbolContextSymbol && !(resolved_flags & eSymbolContextSymbol)) + { + SymbolVendor* sym_vendor = GetSymbolVendor(); + if (sym_vendor) + { + Symtab *symtab = sym_vendor->GetSymtab(); + if (symtab) + { + if (so_addr.IsSectionOffset()) + { + sc.symbol = symtab->FindSymbolContainingFileAddress(so_addr.GetFileAddress()); + if (sc.symbol) + resolved_flags |= eSymbolContextSymbol; + } + } + } + } + } + return resolved_flags; +} + +uint32_t +Module::ResolveSymbolContextForFilePath +( + const char *file_path, + uint32_t line, + bool check_inlines, + uint32_t resolve_scope, + SymbolContextList& sc_list +) +{ + FileSpec file_spec(file_path, false); + return ResolveSymbolContextsForFileSpec (file_spec, line, check_inlines, resolve_scope, sc_list); +} + +uint32_t +Module::ResolveSymbolContextsForFileSpec (const FileSpec &file_spec, uint32_t line, bool check_inlines, uint32_t resolve_scope, SymbolContextList& sc_list) +{ + Mutex::Locker locker (m_mutex); + Timer scoped_timer(__PRETTY_FUNCTION__, + "Module::ResolveSymbolContextForFilePath (%s:%u, check_inlines = %s, resolve_scope = 0x%8.8x)", + file_spec.GetPath().c_str(), + line, + check_inlines ? "yes" : "no", + resolve_scope); + + const uint32_t initial_count = sc_list.GetSize(); + + SymbolVendor *symbols = GetSymbolVendor (); + if (symbols) + symbols->ResolveSymbolContext (file_spec, line, check_inlines, resolve_scope, sc_list); + + return sc_list.GetSize() - initial_count; +} + + +size_t +Module::FindGlobalVariables (const ConstString &name, + const ClangNamespaceDecl *namespace_decl, + bool append, + size_t max_matches, + VariableList& variables) +{ + SymbolVendor *symbols = GetSymbolVendor (); + if (symbols) + return symbols->FindGlobalVariables(name, namespace_decl, append, max_matches, variables); + return 0; +} + +size_t +Module::FindGlobalVariables (const RegularExpression& regex, + bool append, + size_t max_matches, + VariableList& variables) +{ + SymbolVendor *symbols = GetSymbolVendor (); + if (symbols) + return symbols->FindGlobalVariables(regex, append, max_matches, variables); + return 0; +} + +size_t +Module::FindCompileUnits (const FileSpec &path, + bool append, + SymbolContextList &sc_list) +{ + if (!append) + sc_list.Clear(); + + const size_t start_size = sc_list.GetSize(); + const size_t num_compile_units = GetNumCompileUnits(); + SymbolContext sc; + sc.module_sp = shared_from_this(); + const bool compare_directory = path.GetDirectory(); + for (size_t i=0; iFindFunctions(lookup_name, + namespace_decl, + lookup_name_type_mask, + include_inlines, + append, + sc_list); + + // Now check our symbol table for symbols that are code symbols if requested + if (include_symbols) + { + Symtab *symtab = symbols->GetSymtab(); + if (symtab) + symtab->FindFunctionSymbols(lookup_name, lookup_name_type_mask, sc_list); + } + } + + if (match_name_after_lookup) + { + SymbolContext sc; + size_t i = old_size; + while (iFindFunctions(name, namespace_decl, name_type_mask, include_inlines, append, sc_list); + + // Now check our symbol table for symbols that are code symbols if requested + if (include_symbols) + { + Symtab *symtab = symbols->GetSymtab(); + if (symtab) + symtab->FindFunctionSymbols(name, name_type_mask, sc_list); + } + } + } + + return sc_list.GetSize() - old_size; +} + +size_t +Module::FindFunctions (const RegularExpression& regex, + bool include_symbols, + bool include_inlines, + bool append, + SymbolContextList& sc_list) +{ + if (!append) + sc_list.Clear(); + + const size_t start_size = sc_list.GetSize(); + + SymbolVendor *symbols = GetSymbolVendor (); + if (symbols) + { + symbols->FindFunctions(regex, include_inlines, append, sc_list); + + // Now check our symbol table for symbols that are code symbols if requested + if (include_symbols) + { + Symtab *symtab = symbols->GetSymtab(); + if (symtab) + { + std::vector symbol_indexes; + symtab->AppendSymbolIndexesMatchingRegExAndType (regex, eSymbolTypeAny, Symtab::eDebugAny, Symtab::eVisibilityAny, symbol_indexes); + const size_t num_matches = symbol_indexes.size(); + if (num_matches) + { + SymbolContext sc(this); + const size_t end_functions_added_index = sc_list.GetSize(); + size_t num_functions_added_to_sc_list = end_functions_added_index - start_size; + if (num_functions_added_to_sc_list == 0) + { + // No functions were added, just symbols, so we can just append them + for (size_t i=0; iSymbolAtIndex(symbol_indexes[i]); + SymbolType sym_type = sc.symbol->GetType(); + if (sc.symbol && (sym_type == eSymbolTypeCode || + sym_type == eSymbolTypeResolver)) + sc_list.Append(sc); + } + } + else + { + typedef std::map FileAddrToIndexMap; + FileAddrToIndexMap file_addr_to_index; + for (size_t i=start_size; iGetAddressRange().GetBaseAddress().GetFileAddress()] = i; + } + + FileAddrToIndexMap::const_iterator end = file_addr_to_index.end(); + // Functions were added so we need to merge symbols into any + // existing function symbol contexts + for (size_t i=start_size; iSymbolAtIndex(symbol_indexes[i]); + SymbolType sym_type = sc.symbol->GetType(); + if (sc.symbol && (sym_type == eSymbolTypeCode || + sym_type == eSymbolTypeResolver)) + { + FileAddrToIndexMap::const_iterator pos = file_addr_to_index.find(sc.symbol->GetAddress().GetFileAddress()); + if (pos == end) + sc_list.Append(sc); + else + sc_list[pos->second].symbol = sc.symbol; + } + } + } + } + } + } + } + return sc_list.GetSize() - start_size; +} + +size_t +Module::FindTypes_Impl (const SymbolContext& sc, + const ConstString &name, + const ClangNamespaceDecl *namespace_decl, + bool append, + size_t max_matches, + TypeList& types) +{ + Timer scoped_timer(__PRETTY_FUNCTION__, __PRETTY_FUNCTION__); + if (sc.module_sp.get() == NULL || sc.module_sp.get() == this) + { + SymbolVendor *symbols = GetSymbolVendor (); + if (symbols) + return symbols->FindTypes(sc, name, namespace_decl, append, max_matches, types); + } + return 0; +} + +size_t +Module::FindTypesInNamespace (const SymbolContext& sc, + const ConstString &type_name, + const ClangNamespaceDecl *namespace_decl, + size_t max_matches, + TypeList& type_list) +{ + const bool append = true; + return FindTypes_Impl(sc, type_name, namespace_decl, append, max_matches, type_list); +} + +lldb::TypeSP +Module::FindFirstType (const SymbolContext& sc, + const ConstString &name, + bool exact_match) +{ + TypeList type_list; + const size_t num_matches = FindTypes (sc, name, exact_match, 1, type_list); + if (num_matches) + return type_list.GetTypeAtIndex(0); + return TypeSP(); +} + + +size_t +Module::FindTypes (const SymbolContext& sc, + const ConstString &name, + bool exact_match, + size_t max_matches, + TypeList& types) +{ + size_t num_matches = 0; + const char *type_name_cstr = name.GetCString(); + std::string type_scope; + std::string type_basename; + const bool append = true; + TypeClass type_class = eTypeClassAny; + if (Type::GetTypeScopeAndBasename (type_name_cstr, type_scope, type_basename, type_class)) + { + // Check if "name" starts with "::" which means the qualified type starts + // from the root namespace and implies and exact match. The typenames we + // get back from clang do not start with "::" so we need to strip this off + // in order to get the qualfied names to match + + if (type_scope.size() >= 2 && type_scope[0] == ':' && type_scope[1] == ':') + { + type_scope.erase(0,2); + exact_match = true; + } + ConstString type_basename_const_str (type_basename.c_str()); + if (FindTypes_Impl(sc, type_basename_const_str, NULL, append, max_matches, types)) + { + types.RemoveMismatchedTypes (type_scope, type_basename, type_class, exact_match); + num_matches = types.GetSize(); + } + } + else + { + // The type is not in a namespace/class scope, just search for it by basename + if (type_class != eTypeClassAny) + { + // The "type_name_cstr" will have been modified if we have a valid type class + // prefix (like "struct", "class", "union", "typedef" etc). + num_matches = FindTypes_Impl(sc, ConstString(type_name_cstr), NULL, append, max_matches, types); + types.RemoveMismatchedTypes (type_class); + num_matches = types.GetSize(); + } + else + { + num_matches = FindTypes_Impl(sc, name, NULL, append, max_matches, types); + } + } + + return num_matches; + +} + +SymbolVendor* +Module::GetSymbolVendor (bool can_create, lldb_private::Stream *feedback_strm) +{ + Mutex::Locker locker (m_mutex); + if (m_did_load_symbol_vendor == false && can_create) + { + ObjectFile *obj_file = GetObjectFile (); + if (obj_file != NULL) + { + Timer scoped_timer(__PRETTY_FUNCTION__, __PRETTY_FUNCTION__); + m_symfile_ap.reset(SymbolVendor::FindPlugin(shared_from_this(), feedback_strm)); + m_did_load_symbol_vendor = true; + } + } + return m_symfile_ap.get(); +} + +void +Module::SetFileSpecAndObjectName (const FileSpec &file, const ConstString &object_name) +{ + // Container objects whose paths do not specify a file directly can call + // this function to correct the file and object names. + m_file = file; + m_mod_time = file.GetModificationTime(); + m_object_name = object_name; +} + +const ArchSpec& +Module::GetArchitecture () const +{ + return m_arch; +} + +std::string +Module::GetSpecificationDescription () const +{ + std::string spec(GetFileSpec().GetPath()); + if (m_object_name) + { + spec += '('; + spec += m_object_name.GetCString(); + spec += ')'; + } + return spec; +} + +void +Module::GetDescription (Stream *s, lldb::DescriptionLevel level) +{ + Mutex::Locker locker (m_mutex); + + if (level >= eDescriptionLevelFull) + { + if (m_arch.IsValid()) + s->Printf("(%s) ", m_arch.GetArchitectureName()); + } + + if (level == eDescriptionLevelBrief) + { + const char *filename = m_file.GetFilename().GetCString(); + if (filename) + s->PutCString (filename); + } + else + { + char path[PATH_MAX]; + if (m_file.GetPath(path, sizeof(path))) + s->PutCString(path); + } + + const char *object_name = m_object_name.GetCString(); + if (object_name) + s->Printf("(%s)", object_name); +} + +void +Module::ReportError (const char *format, ...) +{ + if (format && format[0]) + { + StreamString strm; + strm.PutCString("error: "); + GetDescription(&strm, lldb::eDescriptionLevelBrief); + strm.PutChar (' '); + va_list args; + va_start (args, format); + strm.PrintfVarArg(format, args); + va_end (args); + + const int format_len = strlen(format); + if (format_len > 0) + { + const char last_char = format[format_len-1]; + if (last_char != '\n' || last_char != '\r') + strm.EOL(); + } + Host::SystemLog (Host::eSystemLogError, "%s", strm.GetString().c_str()); + + } +} + +bool +Module::FileHasChanged () const +{ + if (m_file_has_changed == false) + m_file_has_changed = (m_file.GetModificationTime() != m_mod_time); + return m_file_has_changed; +} + +void +Module::ReportErrorIfModifyDetected (const char *format, ...) +{ + if (m_first_file_changed_log == false) + { + if (FileHasChanged ()) + { + m_first_file_changed_log = true; + if (format) + { + StreamString strm; + strm.PutCString("error: the object file "); + GetDescription(&strm, lldb::eDescriptionLevelFull); + strm.PutCString (" has been modified\n"); + + va_list args; + va_start (args, format); + strm.PrintfVarArg(format, args); + va_end (args); + + const int format_len = strlen(format); + if (format_len > 0) + { + const char last_char = format[format_len-1]; + if (last_char != '\n' || last_char != '\r') + strm.EOL(); + } + strm.PutCString("The debug session should be aborted as the original debug information has been overwritten.\n"); + Host::SystemLog (Host::eSystemLogError, "%s", strm.GetString().c_str()); + } + } + } +} + +void +Module::ReportWarning (const char *format, ...) +{ + if (format && format[0]) + { + StreamString strm; + strm.PutCString("warning: "); + GetDescription(&strm, lldb::eDescriptionLevelFull); + strm.PutChar (' '); + + va_list args; + va_start (args, format); + strm.PrintfVarArg(format, args); + va_end (args); + + const int format_len = strlen(format); + if (format_len > 0) + { + const char last_char = format[format_len-1]; + if (last_char != '\n' || last_char != '\r') + strm.EOL(); + } + Host::SystemLog (Host::eSystemLogWarning, "%s", strm.GetString().c_str()); + } +} + +void +Module::LogMessage (Log *log, const char *format, ...) +{ + if (log) + { + StreamString log_message; + GetDescription(&log_message, lldb::eDescriptionLevelFull); + log_message.PutCString (": "); + va_list args; + va_start (args, format); + log_message.PrintfVarArg (format, args); + va_end (args); + log->PutCString(log_message.GetString().c_str()); + } +} + +void +Module::LogMessageVerboseBacktrace (Log *log, const char *format, ...) +{ + if (log) + { + StreamString log_message; + GetDescription(&log_message, lldb::eDescriptionLevelFull); + log_message.PutCString (": "); + va_list args; + va_start (args, format); + log_message.PrintfVarArg (format, args); + va_end (args); + if (log->GetVerbose()) + Host::Backtrace (log_message, 1024); + log->PutCString(log_message.GetString().c_str()); + } +} + +void +Module::Dump(Stream *s) +{ + Mutex::Locker locker (m_mutex); + //s->Printf("%.*p: ", (int)sizeof(void*) * 2, this); + s->Indent(); + s->Printf("Module %s%s%s%s\n", + m_file.GetPath().c_str(), + m_object_name ? "(" : "", + m_object_name ? m_object_name.GetCString() : "", + m_object_name ? ")" : ""); + + s->IndentMore(); + + ObjectFile *objfile = GetObjectFile (); + if (objfile) + objfile->Dump(s); + + SymbolVendor *symbols = GetSymbolVendor (); + if (symbols) + symbols->Dump(s); + + s->IndentLess(); +} + + +TypeList* +Module::GetTypeList () +{ + SymbolVendor *symbols = GetSymbolVendor (); + if (symbols) + return &symbols->GetTypeList(); + return NULL; +} + +const ConstString & +Module::GetObjectName() const +{ + return m_object_name; +} + +ObjectFile * +Module::GetObjectFile() +{ + Mutex::Locker locker (m_mutex); + if (m_did_load_objfile == false) + { + Timer scoped_timer(__PRETTY_FUNCTION__, + "Module::GetObjectFile () module = %s", GetFileSpec().GetFilename().AsCString("")); + DataBufferSP data_sp; + lldb::offset_t data_offset = 0; + const lldb::offset_t file_size = m_file.GetByteSize(); + if (file_size > m_object_offset) + { + m_did_load_objfile = true; + m_objfile_sp = ObjectFile::FindPlugin (shared_from_this(), + &m_file, + m_object_offset, + file_size - m_object_offset, + data_sp, + data_offset); + if (m_objfile_sp) + { + // Once we get the object file, update our module with the object file's + // architecture since it might differ in vendor/os if some parts were + // unknown. + m_objfile_sp->GetArchitecture (m_arch); + } + } + } + return m_objfile_sp.get(); +} + +SectionList * +Module::GetSectionList() +{ + // Populate m_unified_sections_ap with sections from objfile. + if (m_sections_ap.get() == NULL) + { + ObjectFile *obj_file = GetObjectFile(); + if (obj_file) + obj_file->CreateSections(*GetUnifiedSectionList()); + } + return m_sections_ap.get(); +} + +SectionList * +Module::GetUnifiedSectionList() +{ + // Populate m_unified_sections_ap with sections from objfile. + if (m_sections_ap.get() == NULL) + m_sections_ap.reset(new SectionList()); + return m_sections_ap.get(); +} + +const Symbol * +Module::FindFirstSymbolWithNameAndType (const ConstString &name, SymbolType symbol_type) +{ + Timer scoped_timer(__PRETTY_FUNCTION__, + "Module::FindFirstSymbolWithNameAndType (name = %s, type = %i)", + name.AsCString(), + symbol_type); + SymbolVendor* sym_vendor = GetSymbolVendor(); + if (sym_vendor) + { + Symtab *symtab = sym_vendor->GetSymtab(); + if (symtab) + return symtab->FindFirstSymbolWithNameAndType (name, symbol_type, Symtab::eDebugAny, Symtab::eVisibilityAny); + } + return NULL; +} +void +Module::SymbolIndicesToSymbolContextList (Symtab *symtab, std::vector &symbol_indexes, SymbolContextList &sc_list) +{ + // No need to protect this call using m_mutex all other method calls are + // already thread safe. + + size_t num_indices = symbol_indexes.size(); + if (num_indices > 0) + { + SymbolContext sc; + CalculateSymbolContext (&sc); + for (size_t i = 0; i < num_indices; i++) + { + sc.symbol = symtab->SymbolAtIndex (symbol_indexes[i]); + if (sc.symbol) + sc_list.Append (sc); + } + } +} + +size_t +Module::FindFunctionSymbols (const ConstString &name, + uint32_t name_type_mask, + SymbolContextList& sc_list) +{ + Timer scoped_timer(__PRETTY_FUNCTION__, + "Module::FindSymbolsFunctions (name = %s, mask = 0x%8.8x)", + name.AsCString(), + name_type_mask); + SymbolVendor* sym_vendor = GetSymbolVendor(); + if (sym_vendor) + { + Symtab *symtab = sym_vendor->GetSymtab(); + if (symtab) + return symtab->FindFunctionSymbols (name, name_type_mask, sc_list); + } + return 0; +} + +size_t +Module::FindSymbolsWithNameAndType (const ConstString &name, SymbolType symbol_type, SymbolContextList &sc_list) +{ + // No need to protect this call using m_mutex all other method calls are + // already thread safe. + + + Timer scoped_timer(__PRETTY_FUNCTION__, + "Module::FindSymbolsWithNameAndType (name = %s, type = %i)", + name.AsCString(), + symbol_type); + const size_t initial_size = sc_list.GetSize(); + SymbolVendor* sym_vendor = GetSymbolVendor(); + if (sym_vendor) + { + Symtab *symtab = sym_vendor->GetSymtab(); + if (symtab) + { + std::vector symbol_indexes; + symtab->FindAllSymbolsWithNameAndType (name, symbol_type, symbol_indexes); + SymbolIndicesToSymbolContextList (symtab, symbol_indexes, sc_list); + } + } + return sc_list.GetSize() - initial_size; +} + +size_t +Module::FindSymbolsMatchingRegExAndType (const RegularExpression ®ex, SymbolType symbol_type, SymbolContextList &sc_list) +{ + // No need to protect this call using m_mutex all other method calls are + // already thread safe. + + Timer scoped_timer(__PRETTY_FUNCTION__, + "Module::FindSymbolsMatchingRegExAndType (regex = %s, type = %i)", + regex.GetText(), + symbol_type); + const size_t initial_size = sc_list.GetSize(); + SymbolVendor* sym_vendor = GetSymbolVendor(); + if (sym_vendor) + { + Symtab *symtab = sym_vendor->GetSymtab(); + if (symtab) + { + std::vector symbol_indexes; + symtab->FindAllSymbolsMatchingRexExAndType (regex, symbol_type, Symtab::eDebugAny, Symtab::eVisibilityAny, symbol_indexes); + SymbolIndicesToSymbolContextList (symtab, symbol_indexes, sc_list); + } + } + return sc_list.GetSize() - initial_size; +} + +void +Module::SetSymbolFileFileSpec (const FileSpec &file) +{ + // Remove any sections in the unified section list that come from the current symbol vendor. + if (m_symfile_ap) + { + SectionList *section_list = GetSectionList(); + SymbolFile *symbol_file = m_symfile_ap->GetSymbolFile(); + if (section_list && symbol_file) + { + ObjectFile *obj_file = symbol_file->GetObjectFile(); + // Make sure we have an object file and that the symbol vendor's objfile isn't + // the same as the module's objfile before we remove any sections for it... + if (obj_file && obj_file != m_objfile_sp.get()) + { + size_t num_sections = section_list->GetNumSections (0); + for (size_t idx = num_sections; idx > 0; --idx) + { + lldb::SectionSP section_sp (section_list->GetSectionAtIndex (idx - 1)); + if (section_sp->GetObjectFile() == obj_file) + { + section_list->DeleteSection (idx - 1); + } + } + } + } + } + + m_symfile_spec = file; + m_symfile_ap.reset(); + m_did_load_symbol_vendor = false; +} + +bool +Module::IsExecutable () +{ + if (GetObjectFile() == NULL) + return false; + else + return GetObjectFile()->IsExecutable(); +} + +bool +Module::IsLoadedInTarget (Target *target) +{ + ObjectFile *obj_file = GetObjectFile(); + if (obj_file) + { + SectionList *sections = GetSectionList(); + if (sections != NULL) + { + size_t num_sections = sections->GetSize(); + for (size_t sect_idx = 0; sect_idx < num_sections; sect_idx++) + { + SectionSP section_sp = sections->GetSectionAtIndex(sect_idx); + if (section_sp->GetLoadBaseAddress(target) != LLDB_INVALID_ADDRESS) + { + return true; + } + } + } + } + return false; +} + +bool +Module::LoadScriptingResourceInTarget (Target *target, Error& error, Stream* feedback_stream) +{ + if (!target) + { + error.SetErrorString("invalid destination Target"); + return false; + } + + LoadScriptFromSymFile shoud_load = target->TargetProperties::GetLoadScriptFromSymbolFile(); + + Debugger &debugger = target->GetDebugger(); + const ScriptLanguage script_language = debugger.GetScriptLanguage(); + if (script_language != eScriptLanguageNone) + { + + PlatformSP platform_sp(target->GetPlatform()); + + if (!platform_sp) + { + error.SetErrorString("invalid Platform"); + return false; + } + + FileSpecList file_specs = platform_sp->LocateExecutableScriptingResources (target, + *this); + + + const uint32_t num_specs = file_specs.GetSize(); + if (num_specs) + { + ScriptInterpreter *script_interpreter = debugger.GetCommandInterpreter().GetScriptInterpreter(); + if (script_interpreter) + { + for (uint32_t i=0; iPrintf("warning: '%s' contains a debug script. To run this script in " + "this debug session:\n\n command script import \"%s\"\n\n" + "To run all discovered debug scripts in this session:\n\n" + " settings set target.load-script-from-symbol-file true\n", + GetFileSpec().GetFileNameStrippingExtension().GetCString(), + scripting_fspec.GetPath().c_str()); + return false; + } + StreamString scripting_stream; + scripting_fspec.Dump(&scripting_stream); + const bool can_reload = true; + const bool init_lldb_globals = false; + bool did_load = script_interpreter->LoadScriptingModule(scripting_stream.GetData(), + can_reload, + init_lldb_globals, + error); + if (!did_load) + return false; + } + } + } + else + { + error.SetErrorString("invalid ScriptInterpreter"); + return false; + } + } + } + return true; +} + +bool +Module::SetArchitecture (const ArchSpec &new_arch) +{ + if (!m_arch.IsValid()) + { + m_arch = new_arch; + return true; + } + return m_arch.IsExactMatch(new_arch); +} + +bool +Module::SetLoadAddress (Target &target, lldb::addr_t offset, bool &changed) +{ + size_t num_loaded_sections = 0; + SectionList *section_list = GetSectionList (); + if (section_list) + { + const size_t num_sections = section_list->GetSize(); + size_t sect_idx = 0; + for (sect_idx = 0; sect_idx < num_sections; ++sect_idx) + { + // Iterate through the object file sections to find the + // first section that starts of file offset zero and that + // has bytes in the file... + SectionSP section_sp (section_list->GetSectionAtIndex (sect_idx)); + // Only load non-thread specific sections when given a slide + if (section_sp && !section_sp->IsThreadSpecific()) + { + if (target.GetSectionLoadList().SetSectionLoadAddress (section_sp, section_sp->GetFileAddress() + offset)) + ++num_loaded_sections; + } + } + } + changed = num_loaded_sections > 0; + return num_loaded_sections > 0; +} + + +bool +Module::MatchesModuleSpec (const ModuleSpec &module_ref) +{ + const UUID &uuid = module_ref.GetUUID(); + + if (uuid.IsValid()) + { + // If the UUID matches, then nothing more needs to match... + if (uuid == GetUUID()) + return true; + else + return false; + } + + const FileSpec &file_spec = module_ref.GetFileSpec(); + if (file_spec) + { + if (!FileSpec::Equal (file_spec, m_file, file_spec.GetDirectory())) + return false; + } + + const FileSpec &platform_file_spec = module_ref.GetPlatformFileSpec(); + if (platform_file_spec) + { + if (!FileSpec::Equal (platform_file_spec, GetPlatformFileSpec (), platform_file_spec.GetDirectory())) + return false; + } + + const ArchSpec &arch = module_ref.GetArchitecture(); + if (arch.IsValid()) + { + if (!m_arch.IsCompatibleMatch(arch)) + return false; + } + + const ConstString &object_name = module_ref.GetObjectName(); + if (object_name) + { + if (object_name != GetObjectName()) + return false; + } + return true; +} + +bool +Module::FindSourceFile (const FileSpec &orig_spec, FileSpec &new_spec) const +{ + Mutex::Locker locker (m_mutex); + return m_source_mappings.FindFile (orig_spec, new_spec); +} + +bool +Module::RemapSourceFile (const char *path, std::string &new_path) const +{ + Mutex::Locker locker (m_mutex); + return m_source_mappings.RemapPath(path, new_path); +} + +uint32_t +Module::GetVersion (uint32_t *versions, uint32_t num_versions) +{ + ObjectFile *obj_file = GetObjectFile(); + if (obj_file) + return obj_file->GetVersion (versions, num_versions); + + if (versions && num_versions) + { + for (uint32_t i=0; iModuleAdded(*this, module_sp); + } +} + +void +ModuleList::Append (const ModuleSP &module_sp) +{ + AppendImpl (module_sp); +} + +void +ModuleList::ReplaceEquivalent (const ModuleSP &module_sp) +{ + if (module_sp) + { + Mutex::Locker locker(m_modules_mutex); + + // First remove any equivalent modules. Equivalent modules are modules + // whose path, platform path and architecture match. + ModuleSpec equivalent_module_spec (module_sp->GetFileSpec(), module_sp->GetArchitecture()); + equivalent_module_spec.GetPlatformFileSpec() = module_sp->GetPlatformFileSpec(); + + size_t idx = 0; + while (idx < m_modules.size()) + { + ModuleSP module_sp (m_modules[idx]); + if (module_sp->MatchesModuleSpec (equivalent_module_spec)) + RemoveImpl(m_modules.begin() + idx); + else + ++idx; + } + // Now add the new module to the list + Append(module_sp); + } +} + +bool +ModuleList::AppendIfNeeded (const ModuleSP &module_sp) +{ + if (module_sp) + { + Mutex::Locker locker(m_modules_mutex); + collection::iterator pos, end = m_modules.end(); + for (pos = m_modules.begin(); pos != end; ++pos) + { + if (pos->get() == module_sp.get()) + return false; // Already in the list + } + // Only push module_sp on the list if it wasn't already in there. + Append(module_sp); + return true; + } + return false; +} + +void +ModuleList::Append (const ModuleList& module_list) +{ + for (auto pos : module_list.m_modules) + Append(pos); +} + +bool +ModuleList::AppendIfNeeded (const ModuleList& module_list) +{ + bool any_in = false; + for (auto pos : module_list.m_modules) + { + if (AppendIfNeeded(pos)) + any_in = true; + } + return any_in; +} + +bool +ModuleList::RemoveImpl (const ModuleSP &module_sp, bool use_notifier) +{ + if (module_sp) + { + Mutex::Locker locker(m_modules_mutex); + collection::iterator pos, end = m_modules.end(); + for (pos = m_modules.begin(); pos != end; ++pos) + { + if (pos->get() == module_sp.get()) + { + m_modules.erase (pos); + if (use_notifier && m_notifier) + m_notifier->ModuleRemoved(*this, module_sp); + return true; + } + } + } + return false; +} + +ModuleList::collection::iterator +ModuleList::RemoveImpl (ModuleList::collection::iterator pos, bool use_notifier) +{ + ModuleSP module_sp(*pos); + collection::iterator retval = m_modules.erase(pos); + if (use_notifier && m_notifier) + m_notifier->ModuleRemoved(*this, module_sp); + return retval; +} + +bool +ModuleList::Remove (const ModuleSP &module_sp) +{ + return RemoveImpl (module_sp); +} + +bool +ModuleList::ReplaceModule (const lldb::ModuleSP &old_module_sp, const lldb::ModuleSP &new_module_sp) +{ + if (!RemoveImpl(old_module_sp, false)) + return false; + AppendImpl (new_module_sp, false); + if (m_notifier) + m_notifier->ModuleUpdated(*this, old_module_sp,new_module_sp); + return true; +} + +bool +ModuleList::RemoveIfOrphaned (const Module *module_ptr) +{ + if (module_ptr) + { + Mutex::Locker locker(m_modules_mutex); + collection::iterator pos, end = m_modules.end(); + for (pos = m_modules.begin(); pos != end; ++pos) + { + if (pos->get() == module_ptr) + { + if (pos->unique()) + { + pos = RemoveImpl(pos); + return true; + } + else + return false; + } + } + } + return false; +} + +size_t +ModuleList::RemoveOrphans (bool mandatory) +{ + Mutex::Locker locker; + + if (mandatory) + { + locker.Lock (m_modules_mutex); + } + else + { + // Not mandatory, remove orphans if we can get the mutex + if (!locker.TryLock(m_modules_mutex)) + return 0; + } + collection::iterator pos = m_modules.begin(); + size_t remove_count = 0; + while (pos != m_modules.end()) + { + if (pos->unique()) + { + pos = RemoveImpl(pos); + ++remove_count; + } + else + { + ++pos; + } + } + return remove_count; +} + +size_t +ModuleList::Remove (ModuleList &module_list) +{ + Mutex::Locker locker(m_modules_mutex); + size_t num_removed = 0; + collection::iterator pos, end = module_list.m_modules.end(); + for (pos = module_list.m_modules.begin(); pos != end; ++pos) + { + if (Remove (*pos)) + ++num_removed; + } + return num_removed; +} + + +void +ModuleList::Clear() +{ + ClearImpl(); +} + +void +ModuleList::Destroy() +{ + ClearImpl(); +} + +void +ModuleList::ClearImpl (bool use_notifier) +{ + Mutex::Locker locker(m_modules_mutex); + if (use_notifier && m_notifier) + m_notifier->WillClearList(*this); + m_modules.clear(); +} + +Module* +ModuleList::GetModulePointerAtIndex (size_t idx) const +{ + Mutex::Locker locker(m_modules_mutex); + return GetModulePointerAtIndexUnlocked(idx); +} + +Module* +ModuleList::GetModulePointerAtIndexUnlocked (size_t idx) const +{ + if (idx < m_modules.size()) + return m_modules[idx].get(); + return NULL; +} + +ModuleSP +ModuleList::GetModuleAtIndex(size_t idx) const +{ + Mutex::Locker locker(m_modules_mutex); + return GetModuleAtIndexUnlocked(idx); +} + +ModuleSP +ModuleList::GetModuleAtIndexUnlocked(size_t idx) const +{ + ModuleSP module_sp; + if (idx < m_modules.size()) + module_sp = m_modules[idx]; + return module_sp; +} + +size_t +ModuleList::FindFunctions (const ConstString &name, + uint32_t name_type_mask, + bool include_symbols, + bool include_inlines, + bool append, + SymbolContextList &sc_list) const +{ + if (!append) + sc_list.Clear(); + + const size_t old_size = sc_list.GetSize(); + + if (name_type_mask & eFunctionNameTypeAuto) + { + ConstString lookup_name; + uint32_t lookup_name_type_mask = 0; + bool match_name_after_lookup = false; + Module::PrepareForFunctionNameLookup (name, name_type_mask, + lookup_name, + lookup_name_type_mask, + match_name_after_lookup); + + Mutex::Locker locker(m_modules_mutex); + collection::const_iterator pos, end = m_modules.end(); + for (pos = m_modules.begin(); pos != end; ++pos) + { + (*pos)->FindFunctions (lookup_name, + NULL, + lookup_name_type_mask, + include_symbols, + include_inlines, + true, + sc_list); + } + + if (match_name_after_lookup) + { + SymbolContext sc; + size_t i = old_size; + while (iFindFunctions (name, NULL, name_type_mask, include_symbols, include_inlines, true, sc_list); + } + } + return sc_list.GetSize() - old_size; +} + +size_t +ModuleList::FindFunctionSymbols (const ConstString &name, + uint32_t name_type_mask, + SymbolContextList& sc_list) +{ + const size_t old_size = sc_list.GetSize(); + + if (name_type_mask & eFunctionNameTypeAuto) + { + ConstString lookup_name; + uint32_t lookup_name_type_mask = 0; + bool match_name_after_lookup = false; + Module::PrepareForFunctionNameLookup (name, name_type_mask, + lookup_name, + lookup_name_type_mask, + match_name_after_lookup); + + Mutex::Locker locker(m_modules_mutex); + collection::const_iterator pos, end = m_modules.end(); + for (pos = m_modules.begin(); pos != end; ++pos) + { + (*pos)->FindFunctionSymbols (lookup_name, + lookup_name_type_mask, + sc_list); + } + + if (match_name_after_lookup) + { + SymbolContext sc; + size_t i = old_size; + while (iFindFunctionSymbols (name, name_type_mask, sc_list); + } + } + + return sc_list.GetSize() - old_size; +} + +size_t +ModuleList::FindCompileUnits (const FileSpec &path, + bool append, + SymbolContextList &sc_list) const +{ + if (!append) + sc_list.Clear(); + + Mutex::Locker locker(m_modules_mutex); + collection::const_iterator pos, end = m_modules.end(); + for (pos = m_modules.begin(); pos != end; ++pos) + { + (*pos)->FindCompileUnits (path, true, sc_list); + } + + return sc_list.GetSize(); +} + +size_t +ModuleList::FindGlobalVariables (const ConstString &name, + bool append, + size_t max_matches, + VariableList& variable_list) const +{ + size_t initial_size = variable_list.GetSize(); + Mutex::Locker locker(m_modules_mutex); + collection::const_iterator pos, end = m_modules.end(); + for (pos = m_modules.begin(); pos != end; ++pos) + { + (*pos)->FindGlobalVariables (name, NULL, append, max_matches, variable_list); + } + return variable_list.GetSize() - initial_size; +} + + +size_t +ModuleList::FindGlobalVariables (const RegularExpression& regex, + bool append, + size_t max_matches, + VariableList& variable_list) const +{ + size_t initial_size = variable_list.GetSize(); + Mutex::Locker locker(m_modules_mutex); + collection::const_iterator pos, end = m_modules.end(); + for (pos = m_modules.begin(); pos != end; ++pos) + { + (*pos)->FindGlobalVariables (regex, append, max_matches, variable_list); + } + return variable_list.GetSize() - initial_size; +} + + +size_t +ModuleList::FindSymbolsWithNameAndType (const ConstString &name, + SymbolType symbol_type, + SymbolContextList &sc_list, + bool append) const +{ + Mutex::Locker locker(m_modules_mutex); + if (!append) + sc_list.Clear(); + size_t initial_size = sc_list.GetSize(); + + collection::const_iterator pos, end = m_modules.end(); + for (pos = m_modules.begin(); pos != end; ++pos) + (*pos)->FindSymbolsWithNameAndType (name, symbol_type, sc_list); + return sc_list.GetSize() - initial_size; +} + +size_t +ModuleList::FindSymbolsMatchingRegExAndType (const RegularExpression ®ex, + lldb::SymbolType symbol_type, + SymbolContextList &sc_list, + bool append) const +{ + Mutex::Locker locker(m_modules_mutex); + if (!append) + sc_list.Clear(); + size_t initial_size = sc_list.GetSize(); + + collection::const_iterator pos, end = m_modules.end(); + for (pos = m_modules.begin(); pos != end; ++pos) + (*pos)->FindSymbolsMatchingRegExAndType (regex, symbol_type, sc_list); + return sc_list.GetSize() - initial_size; +} + +size_t +ModuleList::FindModules (const ModuleSpec &module_spec, ModuleList& matching_module_list) const +{ + size_t existing_matches = matching_module_list.GetSize(); + + Mutex::Locker locker(m_modules_mutex); + collection::const_iterator pos, end = m_modules.end(); + for (pos = m_modules.begin(); pos != end; ++pos) + { + ModuleSP module_sp(*pos); + if (module_sp->MatchesModuleSpec (module_spec)) + matching_module_list.Append(module_sp); + } + return matching_module_list.GetSize() - existing_matches; +} + +ModuleSP +ModuleList::FindModule (const Module *module_ptr) const +{ + ModuleSP module_sp; + + // Scope for "locker" + { + Mutex::Locker locker(m_modules_mutex); + collection::const_iterator pos, end = m_modules.end(); + + for (pos = m_modules.begin(); pos != end; ++pos) + { + if ((*pos).get() == module_ptr) + { + module_sp = (*pos); + break; + } + } + } + return module_sp; + +} + +ModuleSP +ModuleList::FindModule (const UUID &uuid) const +{ + ModuleSP module_sp; + + if (uuid.IsValid()) + { + Mutex::Locker locker(m_modules_mutex); + collection::const_iterator pos, end = m_modules.end(); + + for (pos = m_modules.begin(); pos != end; ++pos) + { + if ((*pos)->GetUUID() == uuid) + { + module_sp = (*pos); + break; + } + } + } + return module_sp; +} + + +size_t +ModuleList::FindTypes (const SymbolContext& sc, const ConstString &name, bool name_is_fully_qualified, size_t max_matches, TypeList& types) const +{ + Mutex::Locker locker(m_modules_mutex); + + size_t total_matches = 0; + collection::const_iterator pos, end = m_modules.end(); + if (sc.module_sp) + { + // The symbol context "sc" contains a module so we want to search that + // one first if it is in our list... + for (pos = m_modules.begin(); pos != end; ++pos) + { + if (sc.module_sp.get() == (*pos).get()) + { + total_matches += (*pos)->FindTypes (sc, name, name_is_fully_qualified, max_matches, types); + + if (total_matches >= max_matches) + break; + } + } + } + + if (total_matches < max_matches) + { + SymbolContext world_sc; + for (pos = m_modules.begin(); pos != end; ++pos) + { + // Search the module if the module is not equal to the one in the symbol + // context "sc". If "sc" contains a empty module shared pointer, then + // the comparisong will always be true (valid_module_ptr != NULL). + if (sc.module_sp.get() != (*pos).get()) + total_matches += (*pos)->FindTypes (world_sc, name, name_is_fully_qualified, max_matches, types); + + if (total_matches >= max_matches) + break; + } + } + + return total_matches; +} + +bool +ModuleList::FindSourceFile (const FileSpec &orig_spec, FileSpec &new_spec) const +{ + Mutex::Locker locker(m_modules_mutex); + collection::const_iterator pos, end = m_modules.end(); + for (pos = m_modules.begin(); pos != end; ++pos) + { + if ((*pos)->FindSourceFile (orig_spec, new_spec)) + return true; + } + return false; +} + + + +ModuleSP +ModuleList::FindFirstModule (const ModuleSpec &module_spec) const +{ + ModuleSP module_sp; + Mutex::Locker locker(m_modules_mutex); + collection::const_iterator pos, end = m_modules.end(); + for (pos = m_modules.begin(); pos != end; ++pos) + { + ModuleSP module_sp(*pos); + if (module_sp->MatchesModuleSpec (module_spec)) + return module_sp; + } + return module_sp; + +} + +size_t +ModuleList::GetSize() const +{ + size_t size = 0; + { + Mutex::Locker locker(m_modules_mutex); + size = m_modules.size(); + } + return size; +} + + +void +ModuleList::Dump(Stream *s) const +{ +// s.Printf("%.*p: ", (int)sizeof(void*) * 2, this); +// s.Indent(); +// s << "ModuleList\n"; + + Mutex::Locker locker(m_modules_mutex); + collection::const_iterator pos, end = m_modules.end(); + for (pos = m_modules.begin(); pos != end; ++pos) + { + (*pos)->Dump(s); + } +} + +void +ModuleList::LogUUIDAndPaths (Log *log, const char *prefix_cstr) +{ + if (log) + { + Mutex::Locker locker(m_modules_mutex); + collection::const_iterator pos, begin = m_modules.begin(), end = m_modules.end(); + for (pos = begin; pos != end; ++pos) + { + Module *module = pos->get(); + const FileSpec &module_file_spec = module->GetFileSpec(); + log->Printf ("%s[%u] %s (%s) \"%s\"", + prefix_cstr ? prefix_cstr : "", + (uint32_t)std::distance (begin, pos), + module->GetUUID().GetAsString().c_str(), + module->GetArchitecture().GetArchitectureName(), + module_file_spec.GetPath().c_str()); + } + } +} + +bool +ModuleList::ResolveFileAddress (lldb::addr_t vm_addr, Address& so_addr) const +{ + Mutex::Locker locker(m_modules_mutex); + collection::const_iterator pos, end = m_modules.end(); + for (pos = m_modules.begin(); pos != end; ++pos) + { + if ((*pos)->ResolveFileAddress (vm_addr, so_addr)) + return true; + } + + return false; +} + +uint32_t +ModuleList::ResolveSymbolContextForAddress (const Address& so_addr, uint32_t resolve_scope, SymbolContext& sc) const +{ + // The address is already section offset so it has a module + uint32_t resolved_flags = 0; + ModuleSP module_sp (so_addr.GetModule()); + if (module_sp) + { + resolved_flags = module_sp->ResolveSymbolContextForAddress (so_addr, + resolve_scope, + sc); + } + else + { + Mutex::Locker locker(m_modules_mutex); + collection::const_iterator pos, end = m_modules.end(); + for (pos = m_modules.begin(); pos != end; ++pos) + { + resolved_flags = (*pos)->ResolveSymbolContextForAddress (so_addr, + resolve_scope, + sc); + if (resolved_flags != 0) + break; + } + } + + return resolved_flags; +} + +uint32_t +ModuleList::ResolveSymbolContextForFilePath +( + const char *file_path, + uint32_t line, + bool check_inlines, + uint32_t resolve_scope, + SymbolContextList& sc_list +) const +{ + FileSpec file_spec(file_path, false); + return ResolveSymbolContextsForFileSpec (file_spec, line, check_inlines, resolve_scope, sc_list); +} + +uint32_t +ModuleList::ResolveSymbolContextsForFileSpec (const FileSpec &file_spec, uint32_t line, bool check_inlines, uint32_t resolve_scope, SymbolContextList& sc_list) const +{ + Mutex::Locker locker(m_modules_mutex); + collection::const_iterator pos, end = m_modules.end(); + for (pos = m_modules.begin(); pos != end; ++pos) + { + (*pos)->ResolveSymbolContextsForFileSpec (file_spec, line, check_inlines, resolve_scope, sc_list); + } + + return sc_list.GetSize(); +} + +size_t +ModuleList::GetIndexForModule (const Module *module) const +{ + if (module) + { + Mutex::Locker locker(m_modules_mutex); + collection::const_iterator pos; + collection::const_iterator begin = m_modules.begin(); + collection::const_iterator end = m_modules.end(); + for (pos = begin; pos != end; ++pos) + { + if ((*pos).get() == module) + return std::distance (begin, pos); + } + } + return LLDB_INVALID_INDEX32; +} + +static ModuleList & +GetSharedModuleList () +{ + // NOTE: Intentionally leak the module list so a program doesn't have to + // cleanup all modules and object files as it exits. This just wastes time + // doing a bunch of cleanup that isn't required. + static ModuleList *g_shared_module_list = NULL; + if (g_shared_module_list == NULL) + g_shared_module_list = new ModuleList(); // <--- Intentional leak!!! + + return *g_shared_module_list; +} + +bool +ModuleList::ModuleIsInCache (const Module *module_ptr) +{ + if (module_ptr) + { + ModuleList &shared_module_list = GetSharedModuleList (); + return shared_module_list.FindModule (module_ptr).get() != NULL; + } + return false; +} + +size_t +ModuleList::FindSharedModules (const ModuleSpec &module_spec, ModuleList &matching_module_list) +{ + return GetSharedModuleList ().FindModules (module_spec, matching_module_list); +} + +size_t +ModuleList::RemoveOrphanSharedModules (bool mandatory) +{ + return GetSharedModuleList ().RemoveOrphans(mandatory); +} + +Error +ModuleList::GetSharedModule +( + const ModuleSpec &module_spec, + ModuleSP &module_sp, + const FileSpecList *module_search_paths_ptr, + ModuleSP *old_module_sp_ptr, + bool *did_create_ptr, + bool always_create +) +{ + ModuleList &shared_module_list = GetSharedModuleList (); + Mutex::Locker locker(shared_module_list.m_modules_mutex); + char path[PATH_MAX]; + + Error error; + + module_sp.reset(); + + if (did_create_ptr) + *did_create_ptr = false; + if (old_module_sp_ptr) + old_module_sp_ptr->reset(); + + const UUID *uuid_ptr = module_spec.GetUUIDPtr(); + const FileSpec &module_file_spec = module_spec.GetFileSpec(); + const ArchSpec &arch = module_spec.GetArchitecture(); + + // Make sure no one else can try and get or create a module while this + // function is actively working on it by doing an extra lock on the + // global mutex list. + if (always_create == false) + { + ModuleList matching_module_list; + const size_t num_matching_modules = shared_module_list.FindModules (module_spec, matching_module_list); + if (num_matching_modules > 0) + { + for (size_t module_idx = 0; module_idx < num_matching_modules; ++module_idx) + { + module_sp = matching_module_list.GetModuleAtIndex(module_idx); + + // Make sure the file for the module hasn't been modified + if (module_sp->FileHasChanged()) + { + if (old_module_sp_ptr && !old_module_sp_ptr->get()) + *old_module_sp_ptr = module_sp; + + Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_MODULES)); + if (log) + log->Printf("module changed: %p, removing from global module list", module_sp.get()); + + shared_module_list.Remove (module_sp); + module_sp.reset(); + } + else + { + // The module matches and the module was not modified from + // when it was last loaded. + return error; + } + } + } + } + + if (module_sp) + return error; + else + { + module_sp.reset (new Module (module_spec)); + // Make sure there are a module and an object file since we can specify + // a valid file path with an architecture that might not be in that file. + // By getting the object file we can guarantee that the architecture matches + if (module_sp) + { + if (module_sp->GetObjectFile()) + { + // If we get in here we got the correct arch, now we just need + // to verify the UUID if one was given + if (uuid_ptr && *uuid_ptr != module_sp->GetUUID()) + module_sp.reset(); + else + { + if (did_create_ptr) + *did_create_ptr = true; + + shared_module_list.ReplaceEquivalent(module_sp); + return error; + } + } + else + module_sp.reset(); + } + } + + // Either the file didn't exist where at the path, or no path was given, so + // we now have to use more extreme measures to try and find the appropriate + // module. + + // Fixup the incoming path in case the path points to a valid file, yet + // the arch or UUID (if one was passed in) don't match. + FileSpec file_spec = Symbols::LocateExecutableObjectFile (module_spec); + + // Don't look for the file if it appears to be the same one we already + // checked for above... + if (file_spec != module_file_spec) + { + if (!file_spec.Exists()) + { + file_spec.GetPath(path, sizeof(path)); + if (path[0] == '\0') + module_file_spec.GetPath(path, sizeof(path)); + if (file_spec.Exists()) + { + std::string uuid_str; + if (uuid_ptr && uuid_ptr->IsValid()) + uuid_str = uuid_ptr->GetAsString(); + + if (arch.IsValid()) + { + if (!uuid_str.empty()) + error.SetErrorStringWithFormat("'%s' does not contain the %s architecture and UUID %s", path, arch.GetArchitectureName(), uuid_str.c_str()); + else + error.SetErrorStringWithFormat("'%s' does not contain the %s architecture.", path, arch.GetArchitectureName()); + } + } + else + { + error.SetErrorStringWithFormat("'%s' does not exist", path); + } + if (error.Fail()) + module_sp.reset(); + return error; + } + + + // Make sure no one else can try and get or create a module while this + // function is actively working on it by doing an extra lock on the + // global mutex list. + ModuleSpec platform_module_spec(module_spec); + platform_module_spec.GetFileSpec() = file_spec; + platform_module_spec.GetPlatformFileSpec() = file_spec; + ModuleList matching_module_list; + if (shared_module_list.FindModules (platform_module_spec, matching_module_list) > 0) + { + module_sp = matching_module_list.GetModuleAtIndex(0); + + // If we didn't have a UUID in mind when looking for the object file, + // then we should make sure the modification time hasn't changed! + if (platform_module_spec.GetUUIDPtr() == NULL) + { + TimeValue file_spec_mod_time(file_spec.GetModificationTime()); + if (file_spec_mod_time.IsValid()) + { + if (file_spec_mod_time != module_sp->GetModificationTime()) + { + if (old_module_sp_ptr) + *old_module_sp_ptr = module_sp; + shared_module_list.Remove (module_sp); + module_sp.reset(); + } + } + } + } + + if (module_sp.get() == NULL) + { + module_sp.reset (new Module (platform_module_spec)); + // Make sure there are a module and an object file since we can specify + // a valid file path with an architecture that might not be in that file. + // By getting the object file we can guarantee that the architecture matches + if (module_sp && module_sp->GetObjectFile()) + { + if (did_create_ptr) + *did_create_ptr = true; + + shared_module_list.ReplaceEquivalent(module_sp); + } + else + { + file_spec.GetPath(path, sizeof(path)); + + if (file_spec) + { + if (arch.IsValid()) + error.SetErrorStringWithFormat("unable to open %s architecture in '%s'", arch.GetArchitectureName(), path); + else + error.SetErrorStringWithFormat("unable to open '%s'", path); + } + else + { + std::string uuid_str; + if (uuid_ptr && uuid_ptr->IsValid()) + uuid_str = uuid_ptr->GetAsString(); + + if (!uuid_str.empty()) + error.SetErrorStringWithFormat("cannot locate a module for UUID '%s'", uuid_str.c_str()); + else + error.SetErrorStringWithFormat("cannot locate a module"); + } + } + } + } + + return error; +} + +bool +ModuleList::RemoveSharedModule (lldb::ModuleSP &module_sp) +{ + return GetSharedModuleList ().Remove (module_sp); +} + +bool +ModuleList::RemoveSharedModuleIfOrphaned (const Module *module_ptr) +{ + return GetSharedModuleList ().RemoveIfOrphaned (module_ptr); +} + +bool +ModuleList::LoadScriptingResourcesInTarget (Target *target, + std::list& errors, + Stream *feedback_stream, + bool continue_on_error) +{ + if (!target) + return false; + Mutex::Locker locker(m_modules_mutex); + for (auto module : m_modules) + { + Error error; + if (module) + { + if (!module->LoadScriptingResourceInTarget(target, error, feedback_stream)) + { + if (error.Fail() && error.AsCString()) + { + error.SetErrorStringWithFormat("unable to load scripting data for module %s - error reported was %s", + module->GetFileSpec().GetFileNameStrippingExtension().GetCString(), + error.AsCString()); + errors.push_back(error); + } + if (!continue_on_error) + return false; + } + } + } + return errors.size() == 0; +} diff --git a/contrib/llvm/tools/lldb/source/Core/Opcode.cpp b/contrib/llvm/tools/lldb/source/Core/Opcode.cpp new file mode 100644 index 00000000000..d9878656e3f --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Core/Opcode.cpp @@ -0,0 +1,134 @@ +//===-- Opcode.cpp ----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Core/Opcode.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +#include "llvm/ADT/Triple.h" +// Project includes +#include "lldb/Core/ArchSpec.h" +#include "lldb/Core/DataBufferHeap.h" +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/Stream.h" +#include "lldb/Host/Endian.h" + + +using namespace lldb; +using namespace lldb_private; + + +int +Opcode::Dump (Stream *s, uint32_t min_byte_width) +{ + int bytes_written = 0; + switch (m_type) + { + case Opcode::eTypeInvalid: + bytes_written = s->PutCString (""); + break; + case Opcode::eType8: + bytes_written = s->Printf ("0x%2.2x", m_data.inst8); + break; + case Opcode::eType16: + bytes_written = s->Printf ("0x%4.4x", m_data.inst16); + break; + case Opcode::eType16_2: + case Opcode::eType32: + bytes_written = s->Printf ("0x%8.8x", m_data.inst32); + break; + + case Opcode::eType64: + bytes_written = s->Printf ("0x%16.16" PRIx64, m_data.inst64); + break; + + case Opcode::eTypeBytes: + { + for (uint32_t i=0; i 0) + bytes_written += s->PutChar (' '); + bytes_written += s->Printf ("%2.2x", m_data.inst.bytes[i]); + } + } + break; + } + + // Add spaces to make sure bytes dispay comes out even in case opcodes + // aren't all the same size + if (bytes_written < min_byte_width) + bytes_written = s->Printf ("%*s", min_byte_width - bytes_written, ""); + return bytes_written; +} + +lldb::ByteOrder +Opcode::GetDataByteOrder () const +{ + switch (m_type) + { + case Opcode::eTypeInvalid: break; + case Opcode::eType8: + case Opcode::eType16: + case Opcode::eType16_2: + case Opcode::eType32: + case Opcode::eType64: return lldb::endian::InlHostByteOrder(); + case Opcode::eTypeBytes: + break; + } + return eByteOrderInvalid; +} + +uint32_t +Opcode::GetData (DataExtractor &data) const +{ + uint32_t byte_size = GetByteSize (); + + DataBufferSP buffer_sp; + if (byte_size > 0) + { + switch (m_type) + { + case Opcode::eTypeInvalid: + break; + + case Opcode::eType8: buffer_sp.reset (new DataBufferHeap (&m_data.inst8, byte_size)); break; + case Opcode::eType16: buffer_sp.reset (new DataBufferHeap (&m_data.inst16, byte_size)); break; + case Opcode::eType16_2: + { + // 32 bit thumb instruction, we need to sizzle this a bit + uint8_t buf[4]; + buf[0] = m_data.inst.bytes[2]; + buf[1] = m_data.inst.bytes[3]; + buf[2] = m_data.inst.bytes[0]; + buf[3] = m_data.inst.bytes[1]; + buffer_sp.reset (new DataBufferHeap (buf, byte_size)); + } + break; + case Opcode::eType32: + buffer_sp.reset (new DataBufferHeap (&m_data.inst32, byte_size)); + break; + case Opcode::eType64: buffer_sp.reset (new DataBufferHeap (&m_data.inst64, byte_size)); break; + case Opcode::eTypeBytes:buffer_sp.reset (new DataBufferHeap (GetOpcodeBytes(), byte_size)); break; + break; + } + } + + if (buffer_sp) + { + data.SetByteOrder(GetDataByteOrder()); + data.SetData (buffer_sp); + return byte_size; + } + data.Clear(); + return 0; +} + + + diff --git a/contrib/llvm/tools/lldb/source/Core/PluginManager.cpp b/contrib/llvm/tools/lldb/source/Core/PluginManager.cpp new file mode 100644 index 00000000000..7a2d3772e6c --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Core/PluginManager.cpp @@ -0,0 +1,2064 @@ +//===-- PluginManager.cpp ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "lldb/Core/PluginManager.h" + +#include + +#include +#include + +#include "lldb/Core/Debugger.h" +#include "lldb/Core/Error.h" +#include "lldb/Host/FileSpec.h" +#include "lldb/Host/Host.h" +#include "lldb/Host/Mutex.h" +#include "lldb/Interpreter/OptionValueProperties.h" + +#include "llvm/ADT/StringRef.h" + +using namespace lldb; +using namespace lldb_private; + +enum PluginAction +{ + ePluginRegisterInstance, + ePluginUnregisterInstance, + ePluginGetInstanceAtIndex +}; + + +typedef bool (*PluginInitCallback) (void); +typedef void (*PluginTermCallback) (void); + +struct PluginInfo +{ + void *plugin_handle; + PluginInitCallback plugin_init_callback; + PluginTermCallback plugin_term_callback; +}; + +typedef std::map PluginTerminateMap; + +static Mutex & +GetPluginMapMutex () +{ + static Mutex g_plugin_map_mutex (Mutex::eMutexTypeRecursive); + return g_plugin_map_mutex; +} + +static PluginTerminateMap & +GetPluginMap () +{ + static PluginTerminateMap g_plugin_map; + return g_plugin_map; +} + +static bool +PluginIsLoaded (const FileSpec &plugin_file_spec) +{ + Mutex::Locker locker (GetPluginMapMutex ()); + PluginTerminateMap &plugin_map = GetPluginMap (); + return plugin_map.find (plugin_file_spec) != plugin_map.end(); +} + +static void +SetPluginInfo (const FileSpec &plugin_file_spec, const PluginInfo &plugin_info) +{ + Mutex::Locker locker (GetPluginMapMutex ()); + PluginTerminateMap &plugin_map = GetPluginMap (); + assert (plugin_map.find (plugin_file_spec) == plugin_map.end()); + plugin_map[plugin_file_spec] = plugin_info; +} + + +static FileSpec::EnumerateDirectoryResult +LoadPluginCallback +( + void *baton, + FileSpec::FileType file_type, + const FileSpec &file_spec +) +{ +// PluginManager *plugin_manager = (PluginManager *)baton; + Error error; + + // If we have a regular file, a symbolic link or unknown file type, try + // and process the file. We must handle unknown as sometimes the directory + // enumeration might be enumerating a file system that doesn't have correct + // file type information. + if (file_type == FileSpec::eFileTypeRegular || + file_type == FileSpec::eFileTypeSymbolicLink || + file_type == FileSpec::eFileTypeUnknown ) + { + FileSpec plugin_file_spec (file_spec); + plugin_file_spec.ResolvePath(); + + if (PluginIsLoaded (plugin_file_spec)) + return FileSpec::eEnumerateDirectoryResultNext; + else + { + PluginInfo plugin_info = { NULL, NULL, NULL }; + uint32_t flags = Host::eDynamicLibraryOpenOptionLazy | + Host::eDynamicLibraryOpenOptionLocal | + Host::eDynamicLibraryOpenOptionLimitGetSymbol; + + plugin_info.plugin_handle = Host::DynamicLibraryOpen (plugin_file_spec, flags, error); + if (plugin_info.plugin_handle) + { + bool success = false; + plugin_info.plugin_init_callback = (PluginInitCallback)Host::DynamicLibraryGetSymbol (plugin_info.plugin_handle, "LLDBPluginInitialize", error); + if (plugin_info.plugin_init_callback) + { + // Call the plug-in "bool LLDBPluginInitialize(void)" function + success = plugin_info.plugin_init_callback(); + } + + if (success) + { + // It is ok for the "LLDBPluginTerminate" symbol to be NULL + plugin_info.plugin_term_callback = (PluginTermCallback)Host::DynamicLibraryGetSymbol (plugin_info.plugin_handle, "LLDBPluginTerminate", error); + } + else + { + // The initialize function returned FALSE which means the + // plug-in might not be compatible, or might be too new or + // too old, or might not want to run on this machine. + Host::DynamicLibraryClose (plugin_info.plugin_handle); + plugin_info.plugin_handle = NULL; + plugin_info.plugin_init_callback = NULL; + } + + // Regardless of success or failure, cache the plug-in load + // in our plug-in info so we don't try to load it again and + // again. + SetPluginInfo (plugin_file_spec, plugin_info); + + return FileSpec::eEnumerateDirectoryResultNext; + } + } + } + + if (file_type == FileSpec::eFileTypeUnknown || + file_type == FileSpec::eFileTypeDirectory || + file_type == FileSpec::eFileTypeSymbolicLink ) + { + // Try and recurse into anything that a directory or symbolic link. + // We must also do this for unknown as sometimes the directory enumeration + // might be enurating a file system that doesn't have correct file type + // information. + return FileSpec::eEnumerateDirectoryResultEnter; + } + + return FileSpec::eEnumerateDirectoryResultNext; +} + + +void +PluginManager::Initialize () +{ +#if 1 + FileSpec dir_spec; + const bool find_directories = true; + const bool find_files = true; + const bool find_other = true; + char dir_path[PATH_MAX]; + if (Host::GetLLDBPath (ePathTypeLLDBSystemPlugins, dir_spec)) + { + if (dir_spec.Exists() && dir_spec.GetPath(dir_path, sizeof(dir_path))) + { + FileSpec::EnumerateDirectory (dir_path, + find_directories, + find_files, + find_other, + LoadPluginCallback, + NULL); + } + } + + if (Host::GetLLDBPath (ePathTypeLLDBUserPlugins, dir_spec)) + { + if (dir_spec.Exists() && dir_spec.GetPath(dir_path, sizeof(dir_path))) + { + FileSpec::EnumerateDirectory (dir_path, + find_directories, + find_files, + find_other, + LoadPluginCallback, + NULL); + } + } +#endif +} + +void +PluginManager::Terminate () +{ + Mutex::Locker locker (GetPluginMapMutex ()); + PluginTerminateMap &plugin_map = GetPluginMap (); + + PluginTerminateMap::const_iterator pos, end = plugin_map.end(); + for (pos = plugin_map.begin(); pos != end; ++pos) + { + // Call the plug-in "void LLDBPluginTerminate (void)" function if there + // is one (if the symbol was not NULL). + if (pos->second.plugin_handle) + { + if (pos->second.plugin_term_callback) + pos->second.plugin_term_callback(); + Host::DynamicLibraryClose (pos->second.plugin_handle); + } + } + plugin_map.clear(); +} + + +#pragma mark ABI + + +struct ABIInstance +{ + ABIInstance() : + name(), + description(), + create_callback(NULL) + { + } + + ConstString name; + std::string description; + ABICreateInstance create_callback; +}; + +typedef std::vector ABIInstances; + +static Mutex & +GetABIInstancesMutex () +{ + static Mutex g_instances_mutex (Mutex::eMutexTypeRecursive); + return g_instances_mutex; +} + +static ABIInstances & +GetABIInstances () +{ + static ABIInstances g_instances; + return g_instances; +} + +bool +PluginManager::RegisterPlugin +( + const ConstString &name, + const char *description, + ABICreateInstance create_callback +) +{ + if (create_callback) + { + ABIInstance instance; + assert ((bool)name); + instance.name = name; + if (description && description[0]) + instance.description = description; + instance.create_callback = create_callback; + Mutex::Locker locker (GetABIInstancesMutex ()); + GetABIInstances ().push_back (instance); + return true; + } + return false; +} + +bool +PluginManager::UnregisterPlugin (ABICreateInstance create_callback) +{ + if (create_callback) + { + Mutex::Locker locker (GetABIInstancesMutex ()); + ABIInstances &instances = GetABIInstances (); + + ABIInstances::iterator pos, end = instances.end(); + for (pos = instances.begin(); pos != end; ++ pos) + { + if (pos->create_callback == create_callback) + { + instances.erase(pos); + return true; + } + } + } + return false; +} + +ABICreateInstance +PluginManager::GetABICreateCallbackAtIndex (uint32_t idx) +{ + Mutex::Locker locker (GetABIInstancesMutex ()); + ABIInstances &instances = GetABIInstances (); + if (idx < instances.size()) + return instances[idx].create_callback; + return NULL; +} + +ABICreateInstance +PluginManager::GetABICreateCallbackForPluginName (const ConstString &name) +{ + if (name) + { + Mutex::Locker locker (GetABIInstancesMutex ()); + ABIInstances &instances = GetABIInstances (); + + ABIInstances::iterator pos, end = instances.end(); + for (pos = instances.begin(); pos != end; ++ pos) + { + if (name == pos->name) + return pos->create_callback; + } + } + return NULL; +} + + +#pragma mark Disassembler + + +struct DisassemblerInstance +{ + DisassemblerInstance() : + name(), + description(), + create_callback(NULL) + { + } + + ConstString name; + std::string description; + DisassemblerCreateInstance create_callback; +}; + +typedef std::vector DisassemblerInstances; + +static Mutex & +GetDisassemblerMutex () +{ + static Mutex g_instances_mutex (Mutex::eMutexTypeRecursive); + return g_instances_mutex; +} + +static DisassemblerInstances & +GetDisassemblerInstances () +{ + static DisassemblerInstances g_instances; + return g_instances; +} + +bool +PluginManager::RegisterPlugin +( + const ConstString &name, + const char *description, + DisassemblerCreateInstance create_callback +) +{ + if (create_callback) + { + DisassemblerInstance instance; + assert ((bool)name); + instance.name = name; + if (description && description[0]) + instance.description = description; + instance.create_callback = create_callback; + Mutex::Locker locker (GetDisassemblerMutex ()); + GetDisassemblerInstances ().push_back (instance); + return true; + } + return false; +} + +bool +PluginManager::UnregisterPlugin (DisassemblerCreateInstance create_callback) +{ + if (create_callback) + { + Mutex::Locker locker (GetDisassemblerMutex ()); + DisassemblerInstances &instances = GetDisassemblerInstances (); + + DisassemblerInstances::iterator pos, end = instances.end(); + for (pos = instances.begin(); pos != end; ++ pos) + { + if (pos->create_callback == create_callback) + { + instances.erase(pos); + return true; + } + } + } + return false; +} + +DisassemblerCreateInstance +PluginManager::GetDisassemblerCreateCallbackAtIndex (uint32_t idx) +{ + Mutex::Locker locker (GetDisassemblerMutex ()); + DisassemblerInstances &instances = GetDisassemblerInstances (); + if (idx < instances.size()) + return instances[idx].create_callback; + return NULL; +} + +DisassemblerCreateInstance +PluginManager::GetDisassemblerCreateCallbackForPluginName (const ConstString &name) +{ + if (name) + { + Mutex::Locker locker (GetDisassemblerMutex ()); + DisassemblerInstances &instances = GetDisassemblerInstances (); + + DisassemblerInstances::iterator pos, end = instances.end(); + for (pos = instances.begin(); pos != end; ++ pos) + { + if (name == pos->name) + return pos->create_callback; + } + } + return NULL; +} + + + +#pragma mark DynamicLoader + + +struct DynamicLoaderInstance +{ + DynamicLoaderInstance() : + name(), + description(), + create_callback(NULL), + debugger_init_callback (NULL) + { + } + + ConstString name; + std::string description; + DynamicLoaderCreateInstance create_callback; + DebuggerInitializeCallback debugger_init_callback; +}; + +typedef std::vector DynamicLoaderInstances; + + +static Mutex & +GetDynamicLoaderMutex () +{ + static Mutex g_instances_mutex (Mutex::eMutexTypeRecursive); + return g_instances_mutex; +} + +static DynamicLoaderInstances & +GetDynamicLoaderInstances () +{ + static DynamicLoaderInstances g_instances; + return g_instances; +} + + +bool +PluginManager::RegisterPlugin +( + const ConstString &name, + const char *description, + DynamicLoaderCreateInstance create_callback, + DebuggerInitializeCallback debugger_init_callback +) +{ + if (create_callback) + { + DynamicLoaderInstance instance; + assert ((bool)name); + instance.name = name; + if (description && description[0]) + instance.description = description; + instance.create_callback = create_callback; + instance.debugger_init_callback = debugger_init_callback; + Mutex::Locker locker (GetDynamicLoaderMutex ()); + GetDynamicLoaderInstances ().push_back (instance); + } + return false; +} + +bool +PluginManager::UnregisterPlugin (DynamicLoaderCreateInstance create_callback) +{ + if (create_callback) + { + Mutex::Locker locker (GetDynamicLoaderMutex ()); + DynamicLoaderInstances &instances = GetDynamicLoaderInstances (); + + DynamicLoaderInstances::iterator pos, end = instances.end(); + for (pos = instances.begin(); pos != end; ++ pos) + { + if (pos->create_callback == create_callback) + { + instances.erase(pos); + return true; + } + } + } + return false; +} + +DynamicLoaderCreateInstance +PluginManager::GetDynamicLoaderCreateCallbackAtIndex (uint32_t idx) +{ + Mutex::Locker locker (GetDynamicLoaderMutex ()); + DynamicLoaderInstances &instances = GetDynamicLoaderInstances (); + if (idx < instances.size()) + return instances[idx].create_callback; + return NULL; +} + +DynamicLoaderCreateInstance +PluginManager::GetDynamicLoaderCreateCallbackForPluginName (const ConstString &name) +{ + if (name) + { + Mutex::Locker locker (GetDynamicLoaderMutex ()); + DynamicLoaderInstances &instances = GetDynamicLoaderInstances (); + + DynamicLoaderInstances::iterator pos, end = instances.end(); + for (pos = instances.begin(); pos != end; ++ pos) + { + if (name == pos->name) + return pos->create_callback; + } + } + return NULL; +} + +#pragma mark EmulateInstruction + + +struct EmulateInstructionInstance +{ + EmulateInstructionInstance() : + name(), + description(), + create_callback(NULL) + { + } + + ConstString name; + std::string description; + EmulateInstructionCreateInstance create_callback; +}; + +typedef std::vector EmulateInstructionInstances; + +static Mutex & +GetEmulateInstructionMutex () +{ + static Mutex g_instances_mutex (Mutex::eMutexTypeRecursive); + return g_instances_mutex; +} + +static EmulateInstructionInstances & +GetEmulateInstructionInstances () +{ + static EmulateInstructionInstances g_instances; + return g_instances; +} + + +bool +PluginManager::RegisterPlugin +( + const ConstString &name, + const char *description, + EmulateInstructionCreateInstance create_callback +) +{ + if (create_callback) + { + EmulateInstructionInstance instance; + assert ((bool)name); + instance.name = name; + if (description && description[0]) + instance.description = description; + instance.create_callback = create_callback; + Mutex::Locker locker (GetEmulateInstructionMutex ()); + GetEmulateInstructionInstances ().push_back (instance); + } + return false; +} + +bool +PluginManager::UnregisterPlugin (EmulateInstructionCreateInstance create_callback) +{ + if (create_callback) + { + Mutex::Locker locker (GetEmulateInstructionMutex ()); + EmulateInstructionInstances &instances = GetEmulateInstructionInstances (); + + EmulateInstructionInstances::iterator pos, end = instances.end(); + for (pos = instances.begin(); pos != end; ++ pos) + { + if (pos->create_callback == create_callback) + { + instances.erase(pos); + return true; + } + } + } + return false; +} + +EmulateInstructionCreateInstance +PluginManager::GetEmulateInstructionCreateCallbackAtIndex (uint32_t idx) +{ + Mutex::Locker locker (GetEmulateInstructionMutex ()); + EmulateInstructionInstances &instances = GetEmulateInstructionInstances (); + if (idx < instances.size()) + return instances[idx].create_callback; + return NULL; +} + +EmulateInstructionCreateInstance +PluginManager::GetEmulateInstructionCreateCallbackForPluginName (const ConstString &name) +{ + if (name) + { + Mutex::Locker locker (GetEmulateInstructionMutex ()); + EmulateInstructionInstances &instances = GetEmulateInstructionInstances (); + + EmulateInstructionInstances::iterator pos, end = instances.end(); + for (pos = instances.begin(); pos != end; ++ pos) + { + if (name == pos->name) + return pos->create_callback; + } + } + return NULL; +} +#pragma mark OperatingSystem + + +struct OperatingSystemInstance +{ + OperatingSystemInstance() : + name(), + description(), + create_callback(NULL) + { + } + + ConstString name; + std::string description; + OperatingSystemCreateInstance create_callback; +}; + +typedef std::vector OperatingSystemInstances; + +static Mutex & +GetOperatingSystemMutex () +{ + static Mutex g_instances_mutex (Mutex::eMutexTypeRecursive); + return g_instances_mutex; +} + +static OperatingSystemInstances & +GetOperatingSystemInstances () +{ + static OperatingSystemInstances g_instances; + return g_instances; +} + +bool +PluginManager::RegisterPlugin (const ConstString &name, + const char *description, + OperatingSystemCreateInstance create_callback) +{ + if (create_callback) + { + OperatingSystemInstance instance; + assert ((bool)name); + instance.name = name; + if (description && description[0]) + instance.description = description; + instance.create_callback = create_callback; + Mutex::Locker locker (GetOperatingSystemMutex ()); + GetOperatingSystemInstances ().push_back (instance); + } + return false; +} + +bool +PluginManager::UnregisterPlugin (OperatingSystemCreateInstance create_callback) +{ + if (create_callback) + { + Mutex::Locker locker (GetOperatingSystemMutex ()); + OperatingSystemInstances &instances = GetOperatingSystemInstances (); + + OperatingSystemInstances::iterator pos, end = instances.end(); + for (pos = instances.begin(); pos != end; ++ pos) + { + if (pos->create_callback == create_callback) + { + instances.erase(pos); + return true; + } + } + } + return false; +} + +OperatingSystemCreateInstance +PluginManager::GetOperatingSystemCreateCallbackAtIndex (uint32_t idx) +{ + Mutex::Locker locker (GetOperatingSystemMutex ()); + OperatingSystemInstances &instances = GetOperatingSystemInstances (); + if (idx < instances.size()) + return instances[idx].create_callback; + return NULL; +} + +OperatingSystemCreateInstance +PluginManager::GetOperatingSystemCreateCallbackForPluginName (const ConstString &name) +{ + if (name) + { + Mutex::Locker locker (GetOperatingSystemMutex ()); + OperatingSystemInstances &instances = GetOperatingSystemInstances (); + + OperatingSystemInstances::iterator pos, end = instances.end(); + for (pos = instances.begin(); pos != end; ++ pos) + { + if (name == pos->name) + return pos->create_callback; + } + } + return NULL; +} + + +#pragma mark LanguageRuntime + + +struct LanguageRuntimeInstance +{ + LanguageRuntimeInstance() : + name(), + description(), + create_callback(NULL) + { + } + + ConstString name; + std::string description; + LanguageRuntimeCreateInstance create_callback; +}; + +typedef std::vector LanguageRuntimeInstances; + +static Mutex & +GetLanguageRuntimeMutex () +{ + static Mutex g_instances_mutex (Mutex::eMutexTypeRecursive); + return g_instances_mutex; +} + +static LanguageRuntimeInstances & +GetLanguageRuntimeInstances () +{ + static LanguageRuntimeInstances g_instances; + return g_instances; +} + +bool +PluginManager::RegisterPlugin +( + const ConstString &name, + const char *description, + LanguageRuntimeCreateInstance create_callback +) +{ + if (create_callback) + { + LanguageRuntimeInstance instance; + assert ((bool)name); + instance.name = name; + if (description && description[0]) + instance.description = description; + instance.create_callback = create_callback; + Mutex::Locker locker (GetLanguageRuntimeMutex ()); + GetLanguageRuntimeInstances ().push_back (instance); + } + return false; +} + +bool +PluginManager::UnregisterPlugin (LanguageRuntimeCreateInstance create_callback) +{ + if (create_callback) + { + Mutex::Locker locker (GetLanguageRuntimeMutex ()); + LanguageRuntimeInstances &instances = GetLanguageRuntimeInstances (); + + LanguageRuntimeInstances::iterator pos, end = instances.end(); + for (pos = instances.begin(); pos != end; ++ pos) + { + if (pos->create_callback == create_callback) + { + instances.erase(pos); + return true; + } + } + } + return false; +} + +LanguageRuntimeCreateInstance +PluginManager::GetLanguageRuntimeCreateCallbackAtIndex (uint32_t idx) +{ + Mutex::Locker locker (GetLanguageRuntimeMutex ()); + LanguageRuntimeInstances &instances = GetLanguageRuntimeInstances (); + if (idx < instances.size()) + return instances[idx].create_callback; + return NULL; +} + +LanguageRuntimeCreateInstance +PluginManager::GetLanguageRuntimeCreateCallbackForPluginName (const ConstString &name) +{ + if (name) + { + Mutex::Locker locker (GetLanguageRuntimeMutex ()); + LanguageRuntimeInstances &instances = GetLanguageRuntimeInstances (); + + LanguageRuntimeInstances::iterator pos, end = instances.end(); + for (pos = instances.begin(); pos != end; ++ pos) + { + if (name == pos->name) + return pos->create_callback; + } + } + return NULL; +} + +#pragma mark ObjectFile + +struct ObjectFileInstance +{ + ObjectFileInstance() : + name(), + description(), + create_callback(NULL), + create_memory_callback (NULL), + get_module_specifications (NULL) + { + } + + ConstString name; + std::string description; + ObjectFileCreateInstance create_callback; + ObjectFileCreateMemoryInstance create_memory_callback; + ObjectFileGetModuleSpecifications get_module_specifications; +}; + +typedef std::vector ObjectFileInstances; + +static Mutex & +GetObjectFileMutex () +{ + static Mutex g_instances_mutex (Mutex::eMutexTypeRecursive); + return g_instances_mutex; +} + +static ObjectFileInstances & +GetObjectFileInstances () +{ + static ObjectFileInstances g_instances; + return g_instances; +} + + +bool +PluginManager::RegisterPlugin (const ConstString &name, + const char *description, + ObjectFileCreateInstance create_callback, + ObjectFileCreateMemoryInstance create_memory_callback, + ObjectFileGetModuleSpecifications get_module_specifications) +{ + if (create_callback) + { + ObjectFileInstance instance; + assert ((bool)name); + instance.name = name; + if (description && description[0]) + instance.description = description; + instance.create_callback = create_callback; + instance.create_memory_callback = create_memory_callback; + instance.get_module_specifications = get_module_specifications; + Mutex::Locker locker (GetObjectFileMutex ()); + GetObjectFileInstances ().push_back (instance); + } + return false; +} + +bool +PluginManager::UnregisterPlugin (ObjectFileCreateInstance create_callback) +{ + if (create_callback) + { + Mutex::Locker locker (GetObjectFileMutex ()); + ObjectFileInstances &instances = GetObjectFileInstances (); + + ObjectFileInstances::iterator pos, end = instances.end(); + for (pos = instances.begin(); pos != end; ++ pos) + { + if (pos->create_callback == create_callback) + { + instances.erase(pos); + return true; + } + } + } + return false; +} + +ObjectFileCreateInstance +PluginManager::GetObjectFileCreateCallbackAtIndex (uint32_t idx) +{ + Mutex::Locker locker (GetObjectFileMutex ()); + ObjectFileInstances &instances = GetObjectFileInstances (); + if (idx < instances.size()) + return instances[idx].create_callback; + return NULL; +} + + +ObjectFileCreateMemoryInstance +PluginManager::GetObjectFileCreateMemoryCallbackAtIndex (uint32_t idx) +{ + Mutex::Locker locker (GetObjectFileMutex ()); + ObjectFileInstances &instances = GetObjectFileInstances (); + if (idx < instances.size()) + return instances[idx].create_memory_callback; + return NULL; +} + +ObjectFileGetModuleSpecifications +PluginManager::GetObjectFileGetModuleSpecificationsCallbackAtIndex (uint32_t idx) +{ + Mutex::Locker locker (GetObjectFileMutex ()); + ObjectFileInstances &instances = GetObjectFileInstances (); + if (idx < instances.size()) + return instances[idx].get_module_specifications; + return NULL; +} + +ObjectFileCreateInstance +PluginManager::GetObjectFileCreateCallbackForPluginName (const ConstString &name) +{ + if (name) + { + Mutex::Locker locker (GetObjectFileMutex ()); + ObjectFileInstances &instances = GetObjectFileInstances (); + + ObjectFileInstances::iterator pos, end = instances.end(); + for (pos = instances.begin(); pos != end; ++ pos) + { + if (name == pos->name) + return pos->create_callback; + } + } + return NULL; +} + + +ObjectFileCreateMemoryInstance +PluginManager::GetObjectFileCreateMemoryCallbackForPluginName (const ConstString &name) +{ + if (name) + { + Mutex::Locker locker (GetObjectFileMutex ()); + ObjectFileInstances &instances = GetObjectFileInstances (); + + ObjectFileInstances::iterator pos, end = instances.end(); + for (pos = instances.begin(); pos != end; ++ pos) + { + if (name == pos->name) + return pos->create_memory_callback; + } + } + return NULL; +} + + + +#pragma mark ObjectContainer + +struct ObjectContainerInstance +{ + ObjectContainerInstance() : + name(), + description(), + create_callback (NULL), + get_module_specifications (NULL) + { + } + + ConstString name; + std::string description; + ObjectContainerCreateInstance create_callback; + ObjectFileGetModuleSpecifications get_module_specifications; + +}; + +typedef std::vector ObjectContainerInstances; + +static Mutex & +GetObjectContainerMutex () +{ + static Mutex g_instances_mutex (Mutex::eMutexTypeRecursive); + return g_instances_mutex; +} + +static ObjectContainerInstances & +GetObjectContainerInstances () +{ + static ObjectContainerInstances g_instances; + return g_instances; +} + +bool +PluginManager::RegisterPlugin (const ConstString &name, + const char *description, + ObjectContainerCreateInstance create_callback, + ObjectFileGetModuleSpecifications get_module_specifications) +{ + if (create_callback) + { + ObjectContainerInstance instance; + assert ((bool)name); + instance.name = name; + if (description && description[0]) + instance.description = description; + instance.create_callback = create_callback; + instance.get_module_specifications = get_module_specifications; + Mutex::Locker locker (GetObjectContainerMutex ()); + GetObjectContainerInstances ().push_back (instance); + } + return false; +} + +bool +PluginManager::UnregisterPlugin (ObjectContainerCreateInstance create_callback) +{ + if (create_callback) + { + Mutex::Locker locker (GetObjectContainerMutex ()); + ObjectContainerInstances &instances = GetObjectContainerInstances (); + + ObjectContainerInstances::iterator pos, end = instances.end(); + for (pos = instances.begin(); pos != end; ++ pos) + { + if (pos->create_callback == create_callback) + { + instances.erase(pos); + return true; + } + } + } + return false; +} + +ObjectContainerCreateInstance +PluginManager::GetObjectContainerCreateCallbackAtIndex (uint32_t idx) +{ + Mutex::Locker locker (GetObjectContainerMutex ()); + ObjectContainerInstances &instances = GetObjectContainerInstances (); + if (idx < instances.size()) + return instances[idx].create_callback; + return NULL; +} + +ObjectContainerCreateInstance +PluginManager::GetObjectContainerCreateCallbackForPluginName (const ConstString &name) +{ + if (name) + { + Mutex::Locker locker (GetObjectContainerMutex ()); + ObjectContainerInstances &instances = GetObjectContainerInstances (); + + ObjectContainerInstances::iterator pos, end = instances.end(); + for (pos = instances.begin(); pos != end; ++ pos) + { + if (name == pos->name) + return pos->create_callback; + } + } + return NULL; +} + +ObjectFileGetModuleSpecifications +PluginManager::GetObjectContainerGetModuleSpecificationsCallbackAtIndex (uint32_t idx) +{ + Mutex::Locker locker (GetObjectContainerMutex ()); + ObjectContainerInstances &instances = GetObjectContainerInstances (); + if (idx < instances.size()) + return instances[idx].get_module_specifications; + return NULL; +} + +#pragma mark LogChannel + +struct LogInstance +{ + LogInstance() : + name(), + description(), + create_callback(NULL) + { + } + + ConstString name; + std::string description; + LogChannelCreateInstance create_callback; +}; + +typedef std::vector LogInstances; + +static Mutex & +GetLogMutex () +{ + static Mutex g_instances_mutex (Mutex::eMutexTypeRecursive); + return g_instances_mutex; +} + +static LogInstances & +GetLogInstances () +{ + static LogInstances g_instances; + return g_instances; +} + + + +bool +PluginManager::RegisterPlugin +( + const ConstString &name, + const char *description, + LogChannelCreateInstance create_callback +) +{ + if (create_callback) + { + LogInstance instance; + assert ((bool)name); + instance.name = name; + if (description && description[0]) + instance.description = description; + instance.create_callback = create_callback; + Mutex::Locker locker (GetLogMutex ()); + GetLogInstances ().push_back (instance); + } + return false; +} + +bool +PluginManager::UnregisterPlugin (LogChannelCreateInstance create_callback) +{ + if (create_callback) + { + Mutex::Locker locker (GetLogMutex ()); + LogInstances &instances = GetLogInstances (); + + LogInstances::iterator pos, end = instances.end(); + for (pos = instances.begin(); pos != end; ++ pos) + { + if (pos->create_callback == create_callback) + { + instances.erase(pos); + return true; + } + } + } + return false; +} + +const char * +PluginManager::GetLogChannelCreateNameAtIndex (uint32_t idx) +{ + Mutex::Locker locker (GetLogMutex ()); + LogInstances &instances = GetLogInstances (); + if (idx < instances.size()) + return instances[idx].name.GetCString(); + return NULL; +} + + +LogChannelCreateInstance +PluginManager::GetLogChannelCreateCallbackAtIndex (uint32_t idx) +{ + Mutex::Locker locker (GetLogMutex ()); + LogInstances &instances = GetLogInstances (); + if (idx < instances.size()) + return instances[idx].create_callback; + return NULL; +} + +LogChannelCreateInstance +PluginManager::GetLogChannelCreateCallbackForPluginName (const ConstString &name) +{ + if (name) + { + Mutex::Locker locker (GetLogMutex ()); + LogInstances &instances = GetLogInstances (); + + LogInstances::iterator pos, end = instances.end(); + for (pos = instances.begin(); pos != end; ++ pos) + { + if (name == pos->name) + return pos->create_callback; + } + } + return NULL; +} + +#pragma mark Platform + +struct PlatformInstance +{ + PlatformInstance() : + name(), + description(), + create_callback(NULL), + debugger_init_callback (NULL) + { + } + + ConstString name; + std::string description; + PlatformCreateInstance create_callback; + DebuggerInitializeCallback debugger_init_callback; +}; + +typedef std::vector PlatformInstances; + +static Mutex & +GetPlatformInstancesMutex () +{ + static Mutex g_platform_instances_mutex (Mutex::eMutexTypeRecursive); + return g_platform_instances_mutex; +} + +static PlatformInstances & +GetPlatformInstances () +{ + static PlatformInstances g_platform_instances; + return g_platform_instances; +} + + +bool +PluginManager::RegisterPlugin (const ConstString &name, + const char *description, + PlatformCreateInstance create_callback, + DebuggerInitializeCallback debugger_init_callback) +{ + if (create_callback) + { + Mutex::Locker locker (GetPlatformInstancesMutex ()); + + PlatformInstance instance; + assert ((bool)name); + instance.name = name; + if (description && description[0]) + instance.description = description; + instance.create_callback = create_callback; + instance.debugger_init_callback = debugger_init_callback; + GetPlatformInstances ().push_back (instance); + return true; + } + return false; +} + + +const char * +PluginManager::GetPlatformPluginNameAtIndex (uint32_t idx) +{ + Mutex::Locker locker (GetPlatformInstancesMutex ()); + PlatformInstances &instances = GetPlatformInstances (); + if (idx < instances.size()) + return instances[idx].name.GetCString(); + return NULL; +} + +const char * +PluginManager::GetPlatformPluginDescriptionAtIndex (uint32_t idx) +{ + Mutex::Locker locker (GetPlatformInstancesMutex ()); + PlatformInstances &instances = GetPlatformInstances (); + if (idx < instances.size()) + return instances[idx].description.c_str(); + return NULL; +} + +bool +PluginManager::UnregisterPlugin (PlatformCreateInstance create_callback) +{ + if (create_callback) + { + Mutex::Locker locker (GetPlatformInstancesMutex ()); + PlatformInstances &instances = GetPlatformInstances (); + + PlatformInstances::iterator pos, end = instances.end(); + for (pos = instances.begin(); pos != end; ++ pos) + { + if (pos->create_callback == create_callback) + { + instances.erase(pos); + return true; + } + } + } + return false; +} + +PlatformCreateInstance +PluginManager::GetPlatformCreateCallbackAtIndex (uint32_t idx) +{ + Mutex::Locker locker (GetPlatformInstancesMutex ()); + PlatformInstances &instances = GetPlatformInstances (); + if (idx < instances.size()) + return instances[idx].create_callback; + return NULL; +} + +PlatformCreateInstance +PluginManager::GetPlatformCreateCallbackForPluginName (const ConstString &name) +{ + if (name) + { + Mutex::Locker locker (GetPlatformInstancesMutex ()); + PlatformInstances &instances = GetPlatformInstances (); + + PlatformInstances::iterator pos, end = instances.end(); + for (pos = instances.begin(); pos != end; ++ pos) + { + if (name == pos->name) + return pos->create_callback; + } + } + return NULL; +} + +size_t +PluginManager::AutoCompletePlatformName (const char *name, StringList &matches) +{ + if (name) + { + Mutex::Locker locker (GetPlatformInstancesMutex ()); + PlatformInstances &instances = GetPlatformInstances (); + llvm::StringRef name_sref(name); + + PlatformInstances::iterator pos, end = instances.end(); + for (pos = instances.begin(); pos != end; ++ pos) + { + llvm::StringRef plugin_name (pos->name.GetCString()); + if (plugin_name.startswith(name_sref)) + matches.AppendString (plugin_name.data()); + } + } + return matches.GetSize(); +} +#pragma mark Process + +struct ProcessInstance +{ + ProcessInstance() : + name(), + description(), + create_callback(NULL), + debugger_init_callback(NULL) + { + } + + ConstString name; + std::string description; + ProcessCreateInstance create_callback; + DebuggerInitializeCallback debugger_init_callback; +}; + +typedef std::vector ProcessInstances; + +static Mutex & +GetProcessMutex () +{ + static Mutex g_instances_mutex (Mutex::eMutexTypeRecursive); + return g_instances_mutex; +} + +static ProcessInstances & +GetProcessInstances () +{ + static ProcessInstances g_instances; + return g_instances; +} + + +bool +PluginManager::RegisterPlugin (const ConstString &name, + const char *description, + ProcessCreateInstance create_callback, + DebuggerInitializeCallback debugger_init_callback) +{ + if (create_callback) + { + ProcessInstance instance; + assert ((bool)name); + instance.name = name; + if (description && description[0]) + instance.description = description; + instance.create_callback = create_callback; + instance.debugger_init_callback = debugger_init_callback; + Mutex::Locker locker (GetProcessMutex ()); + GetProcessInstances ().push_back (instance); + } + return false; +} + +const char * +PluginManager::GetProcessPluginNameAtIndex (uint32_t idx) +{ + Mutex::Locker locker (GetProcessMutex ()); + ProcessInstances &instances = GetProcessInstances (); + if (idx < instances.size()) + return instances[idx].name.GetCString(); + return NULL; +} + +const char * +PluginManager::GetProcessPluginDescriptionAtIndex (uint32_t idx) +{ + Mutex::Locker locker (GetProcessMutex ()); + ProcessInstances &instances = GetProcessInstances (); + if (idx < instances.size()) + return instances[idx].description.c_str(); + return NULL; +} + +bool +PluginManager::UnregisterPlugin (ProcessCreateInstance create_callback) +{ + if (create_callback) + { + Mutex::Locker locker (GetProcessMutex ()); + ProcessInstances &instances = GetProcessInstances (); + + ProcessInstances::iterator pos, end = instances.end(); + for (pos = instances.begin(); pos != end; ++ pos) + { + if (pos->create_callback == create_callback) + { + instances.erase(pos); + return true; + } + } + } + return false; +} + +ProcessCreateInstance +PluginManager::GetProcessCreateCallbackAtIndex (uint32_t idx) +{ + Mutex::Locker locker (GetProcessMutex ()); + ProcessInstances &instances = GetProcessInstances (); + if (idx < instances.size()) + return instances[idx].create_callback; + return NULL; +} + + +ProcessCreateInstance +PluginManager::GetProcessCreateCallbackForPluginName (const ConstString &name) +{ + if (name) + { + Mutex::Locker locker (GetProcessMutex ()); + ProcessInstances &instances = GetProcessInstances (); + + ProcessInstances::iterator pos, end = instances.end(); + for (pos = instances.begin(); pos != end; ++ pos) + { + if (name == pos->name) + return pos->create_callback; + } + } + return NULL; +} + +#pragma mark SymbolFile + +struct SymbolFileInstance +{ + SymbolFileInstance() : + name(), + description(), + create_callback(NULL) + { + } + + ConstString name; + std::string description; + SymbolFileCreateInstance create_callback; +}; + +typedef std::vector SymbolFileInstances; + +static Mutex & +GetSymbolFileMutex () +{ + static Mutex g_instances_mutex (Mutex::eMutexTypeRecursive); + return g_instances_mutex; +} + +static SymbolFileInstances & +GetSymbolFileInstances () +{ + static SymbolFileInstances g_instances; + return g_instances; +} + + +bool +PluginManager::RegisterPlugin +( + const ConstString &name, + const char *description, + SymbolFileCreateInstance create_callback +) +{ + if (create_callback) + { + SymbolFileInstance instance; + assert ((bool)name); + instance.name = name; + if (description && description[0]) + instance.description = description; + instance.create_callback = create_callback; + Mutex::Locker locker (GetSymbolFileMutex ()); + GetSymbolFileInstances ().push_back (instance); + } + return false; +} + +bool +PluginManager::UnregisterPlugin (SymbolFileCreateInstance create_callback) +{ + if (create_callback) + { + Mutex::Locker locker (GetSymbolFileMutex ()); + SymbolFileInstances &instances = GetSymbolFileInstances (); + + SymbolFileInstances::iterator pos, end = instances.end(); + for (pos = instances.begin(); pos != end; ++ pos) + { + if (pos->create_callback == create_callback) + { + instances.erase(pos); + return true; + } + } + } + return false; +} + +SymbolFileCreateInstance +PluginManager::GetSymbolFileCreateCallbackAtIndex (uint32_t idx) +{ + Mutex::Locker locker (GetSymbolFileMutex ()); + SymbolFileInstances &instances = GetSymbolFileInstances (); + if (idx < instances.size()) + return instances[idx].create_callback; + return NULL; +} + +SymbolFileCreateInstance +PluginManager::GetSymbolFileCreateCallbackForPluginName (const ConstString &name) +{ + if (name) + { + Mutex::Locker locker (GetSymbolFileMutex ()); + SymbolFileInstances &instances = GetSymbolFileInstances (); + + SymbolFileInstances::iterator pos, end = instances.end(); + for (pos = instances.begin(); pos != end; ++ pos) + { + if (name == pos->name) + return pos->create_callback; + } + } + return NULL; +} + + + +#pragma mark SymbolVendor + +struct SymbolVendorInstance +{ + SymbolVendorInstance() : + name(), + description(), + create_callback(NULL) + { + } + + ConstString name; + std::string description; + SymbolVendorCreateInstance create_callback; +}; + +typedef std::vector SymbolVendorInstances; + +static Mutex & +GetSymbolVendorMutex () +{ + static Mutex g_instances_mutex (Mutex::eMutexTypeRecursive); + return g_instances_mutex; +} + +static SymbolVendorInstances & +GetSymbolVendorInstances () +{ + static SymbolVendorInstances g_instances; + return g_instances; +} + +bool +PluginManager::RegisterPlugin +( + const ConstString &name, + const char *description, + SymbolVendorCreateInstance create_callback +) +{ + if (create_callback) + { + SymbolVendorInstance instance; + assert ((bool)name); + instance.name = name; + if (description && description[0]) + instance.description = description; + instance.create_callback = create_callback; + Mutex::Locker locker (GetSymbolVendorMutex ()); + GetSymbolVendorInstances ().push_back (instance); + } + return false; +} + +bool +PluginManager::UnregisterPlugin (SymbolVendorCreateInstance create_callback) +{ + if (create_callback) + { + Mutex::Locker locker (GetSymbolVendorMutex ()); + SymbolVendorInstances &instances = GetSymbolVendorInstances (); + + SymbolVendorInstances::iterator pos, end = instances.end(); + for (pos = instances.begin(); pos != end; ++ pos) + { + if (pos->create_callback == create_callback) + { + instances.erase(pos); + return true; + } + } + } + return false; +} + +SymbolVendorCreateInstance +PluginManager::GetSymbolVendorCreateCallbackAtIndex (uint32_t idx) +{ + Mutex::Locker locker (GetSymbolVendorMutex ()); + SymbolVendorInstances &instances = GetSymbolVendorInstances (); + if (idx < instances.size()) + return instances[idx].create_callback; + return NULL; +} + + +SymbolVendorCreateInstance +PluginManager::GetSymbolVendorCreateCallbackForPluginName (const ConstString &name) +{ + if (name) + { + Mutex::Locker locker (GetSymbolVendorMutex ()); + SymbolVendorInstances &instances = GetSymbolVendorInstances (); + + SymbolVendorInstances::iterator pos, end = instances.end(); + for (pos = instances.begin(); pos != end; ++ pos) + { + if (name == pos->name) + return pos->create_callback; + } + } + return NULL; +} + + +#pragma mark UnwindAssembly + +struct UnwindAssemblyInstance +{ + UnwindAssemblyInstance() : + name(), + description(), + create_callback(NULL) + { + } + + ConstString name; + std::string description; + UnwindAssemblyCreateInstance create_callback; +}; + +typedef std::vector UnwindAssemblyInstances; + +static Mutex & +GetUnwindAssemblyMutex () +{ + static Mutex g_instances_mutex (Mutex::eMutexTypeRecursive); + return g_instances_mutex; +} + +static UnwindAssemblyInstances & +GetUnwindAssemblyInstances () +{ + static UnwindAssemblyInstances g_instances; + return g_instances; +} + +bool +PluginManager::RegisterPlugin +( + const ConstString &name, + const char *description, + UnwindAssemblyCreateInstance create_callback +) +{ + if (create_callback) + { + UnwindAssemblyInstance instance; + assert ((bool)name); + instance.name = name; + if (description && description[0]) + instance.description = description; + instance.create_callback = create_callback; + Mutex::Locker locker (GetUnwindAssemblyMutex ()); + GetUnwindAssemblyInstances ().push_back (instance); + } + return false; +} + +bool +PluginManager::UnregisterPlugin (UnwindAssemblyCreateInstance create_callback) +{ + if (create_callback) + { + Mutex::Locker locker (GetUnwindAssemblyMutex ()); + UnwindAssemblyInstances &instances = GetUnwindAssemblyInstances (); + + UnwindAssemblyInstances::iterator pos, end = instances.end(); + for (pos = instances.begin(); pos != end; ++ pos) + { + if (pos->create_callback == create_callback) + { + instances.erase(pos); + return true; + } + } + } + return false; +} + +UnwindAssemblyCreateInstance +PluginManager::GetUnwindAssemblyCreateCallbackAtIndex (uint32_t idx) +{ + Mutex::Locker locker (GetUnwindAssemblyMutex ()); + UnwindAssemblyInstances &instances = GetUnwindAssemblyInstances (); + if (idx < instances.size()) + return instances[idx].create_callback; + return NULL; +} + + +UnwindAssemblyCreateInstance +PluginManager::GetUnwindAssemblyCreateCallbackForPluginName (const ConstString &name) +{ + if (name) + { + Mutex::Locker locker (GetUnwindAssemblyMutex ()); + UnwindAssemblyInstances &instances = GetUnwindAssemblyInstances (); + + UnwindAssemblyInstances::iterator pos, end = instances.end(); + for (pos = instances.begin(); pos != end; ++ pos) + { + if (name == pos->name) + return pos->create_callback; + } + } + return NULL; +} + +void +PluginManager::DebuggerInitialize (Debugger &debugger) +{ + // Initialize the DynamicLoader plugins + { + Mutex::Locker locker (GetDynamicLoaderMutex ()); + DynamicLoaderInstances &instances = GetDynamicLoaderInstances (); + + DynamicLoaderInstances::iterator pos, end = instances.end(); + for (pos = instances.begin(); pos != end; ++ pos) + { + if (pos->debugger_init_callback) + pos->debugger_init_callback (debugger); + } + } + + // Initialize the Platform plugins + { + Mutex::Locker locker (GetPlatformInstancesMutex ()); + PlatformInstances &instances = GetPlatformInstances (); + + PlatformInstances::iterator pos, end = instances.end(); + for (pos = instances.begin(); pos != end; ++ pos) + { + if (pos->debugger_init_callback) + pos->debugger_init_callback (debugger); + } + } + + // Initialize the Process plugins + { + Mutex::Locker locker (GetProcessMutex()); + ProcessInstances &instances = GetProcessInstances(); + + ProcessInstances::iterator pos, end = instances.end(); + for (pos = instances.begin(); pos != end; ++ pos) + { + if (pos->debugger_init_callback) + pos->debugger_init_callback (debugger); + } + } + +} + +// This is the preferred new way to register plugin specific settings. e.g. +// This will put a plugin's settings under e.g. "plugin...SETTINGNAME". +static lldb::OptionValuePropertiesSP +GetDebuggerPropertyForPlugins (Debugger &debugger, + const ConstString &plugin_type_name, + const ConstString &plugin_type_desc, + bool can_create) +{ + lldb::OptionValuePropertiesSP parent_properties_sp (debugger.GetValueProperties()); + if (parent_properties_sp) + { + static ConstString g_property_name("plugin"); + + OptionValuePropertiesSP plugin_properties_sp = parent_properties_sp->GetSubProperty (NULL, g_property_name); + if (!plugin_properties_sp && can_create) + { + plugin_properties_sp.reset (new OptionValueProperties (g_property_name)); + parent_properties_sp->AppendProperty (g_property_name, + ConstString("Settings specify to plugins."), + true, + plugin_properties_sp); + } + + if (plugin_properties_sp) + { + lldb::OptionValuePropertiesSP plugin_type_properties_sp = plugin_properties_sp->GetSubProperty (NULL, plugin_type_name); + if (!plugin_type_properties_sp && can_create) + { + plugin_type_properties_sp.reset (new OptionValueProperties (plugin_type_name)); + plugin_properties_sp->AppendProperty (plugin_type_name, + plugin_type_desc, + true, + plugin_type_properties_sp); + } + return plugin_type_properties_sp; + } + } + return lldb::OptionValuePropertiesSP(); +} + +// This is deprecated way to register plugin specific settings. e.g. +// ".plugin..SETTINGNAME" +// and Platform generic settings would be under "platform.SETTINGNAME". +static lldb::OptionValuePropertiesSP +GetDebuggerPropertyForPluginsOldStyle (Debugger &debugger, + const ConstString &plugin_type_name, + const ConstString &plugin_type_desc, + bool can_create) +{ + static ConstString g_property_name("plugin"); + lldb::OptionValuePropertiesSP parent_properties_sp (debugger.GetValueProperties()); + if (parent_properties_sp) + { + OptionValuePropertiesSP plugin_properties_sp = parent_properties_sp->GetSubProperty (NULL, plugin_type_name); + if (!plugin_properties_sp && can_create) + { + plugin_properties_sp.reset (new OptionValueProperties (plugin_type_name)); + parent_properties_sp->AppendProperty (plugin_type_name, + plugin_type_desc, + true, + plugin_properties_sp); + } + + if (plugin_properties_sp) + { + lldb::OptionValuePropertiesSP plugin_type_properties_sp = plugin_properties_sp->GetSubProperty (NULL, g_property_name); + if (!plugin_type_properties_sp && can_create) + { + plugin_type_properties_sp.reset (new OptionValueProperties (g_property_name)); + plugin_properties_sp->AppendProperty (g_property_name, + ConstString("Settings specific to plugins"), + true, + plugin_type_properties_sp); + } + return plugin_type_properties_sp; + } + } + return lldb::OptionValuePropertiesSP(); +} + + +lldb::OptionValuePropertiesSP +PluginManager::GetSettingForDynamicLoaderPlugin (Debugger &debugger, const ConstString &setting_name) +{ + lldb::OptionValuePropertiesSP properties_sp; + lldb::OptionValuePropertiesSP plugin_type_properties_sp (GetDebuggerPropertyForPlugins (debugger, + ConstString("dynamic-loader"), + ConstString(), // not creating to so we don't need the description + false)); + if (plugin_type_properties_sp) + properties_sp = plugin_type_properties_sp->GetSubProperty (NULL, setting_name); + return properties_sp; +} + +bool +PluginManager::CreateSettingForDynamicLoaderPlugin (Debugger &debugger, + const lldb::OptionValuePropertiesSP &properties_sp, + const ConstString &description, + bool is_global_property) +{ + if (properties_sp) + { + lldb::OptionValuePropertiesSP plugin_type_properties_sp (GetDebuggerPropertyForPlugins (debugger, + ConstString("dynamic-loader"), + ConstString("Settings for dynamic loader plug-ins"), + true)); + if (plugin_type_properties_sp) + { + plugin_type_properties_sp->AppendProperty (properties_sp->GetName(), + description, + is_global_property, + properties_sp); + return true; + } + } + return false; +} + + +lldb::OptionValuePropertiesSP +PluginManager::GetSettingForPlatformPlugin (Debugger &debugger, const ConstString &setting_name) +{ + lldb::OptionValuePropertiesSP properties_sp; + lldb::OptionValuePropertiesSP plugin_type_properties_sp (GetDebuggerPropertyForPluginsOldStyle (debugger, + ConstString("platform"), + ConstString(), // not creating to so we don't need the description + false)); + if (plugin_type_properties_sp) + properties_sp = plugin_type_properties_sp->GetSubProperty (NULL, setting_name); + return properties_sp; +} + +bool +PluginManager::CreateSettingForPlatformPlugin (Debugger &debugger, + const lldb::OptionValuePropertiesSP &properties_sp, + const ConstString &description, + bool is_global_property) +{ + if (properties_sp) + { + lldb::OptionValuePropertiesSP plugin_type_properties_sp (GetDebuggerPropertyForPluginsOldStyle (debugger, + ConstString("platform"), + ConstString("Settings for platform plug-ins"), + true)); + if (plugin_type_properties_sp) + { + plugin_type_properties_sp->AppendProperty (properties_sp->GetName(), + description, + is_global_property, + properties_sp); + return true; + } + } + return false; +} + + +lldb::OptionValuePropertiesSP +PluginManager::GetSettingForProcessPlugin (Debugger &debugger, const ConstString &setting_name) +{ + lldb::OptionValuePropertiesSP properties_sp; + lldb::OptionValuePropertiesSP plugin_type_properties_sp (GetDebuggerPropertyForPlugins (debugger, + ConstString("process"), + ConstString(), // not creating to so we don't need the description + false)); + if (plugin_type_properties_sp) + properties_sp = plugin_type_properties_sp->GetSubProperty (NULL, setting_name); + return properties_sp; +} + +bool +PluginManager::CreateSettingForProcessPlugin (Debugger &debugger, + const lldb::OptionValuePropertiesSP &properties_sp, + const ConstString &description, + bool is_global_property) +{ + if (properties_sp) + { + lldb::OptionValuePropertiesSP plugin_type_properties_sp (GetDebuggerPropertyForPlugins (debugger, + ConstString("process"), + ConstString("Settings for process plug-ins"), + true)); + if (plugin_type_properties_sp) + { + plugin_type_properties_sp->AppendProperty (properties_sp->GetName(), + description, + is_global_property, + properties_sp); + return true; + } + } + return false; +} + diff --git a/contrib/llvm/tools/lldb/source/Core/RegisterValue.cpp b/contrib/llvm/tools/lldb/source/Core/RegisterValue.cpp new file mode 100644 index 00000000000..91f5bea805c --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Core/RegisterValue.cpp @@ -0,0 +1,1272 @@ +//===-- RegisterValue.cpp ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Core/RegisterValue.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/Error.h" +#include "lldb/Core/Scalar.h" +#include "lldb/Core/Stream.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Interpreter/Args.h" + +using namespace lldb; +using namespace lldb_private; + + +bool +RegisterValue::Dump (Stream *s, + const RegisterInfo *reg_info, + bool prefix_with_name, + bool prefix_with_alt_name, + Format format, + uint32_t reg_name_right_align_at) const +{ + DataExtractor data; + if (GetData (data)) + { + bool name_printed = false; + // For simplicity, alignment of the register name printing applies only + // in the most common case where: + // + // prefix_with_name^prefix_with_alt_name is true + // + StreamString format_string; + if (reg_name_right_align_at && (prefix_with_name^prefix_with_alt_name)) + format_string.Printf("%%%us", reg_name_right_align_at); + else + format_string.Printf("%%s"); + const char *fmt = format_string.GetData(); + if (prefix_with_name) + { + if (reg_info->name) + { + s->Printf (fmt, reg_info->name); + name_printed = true; + } + else if (reg_info->alt_name) + { + s->Printf (fmt, reg_info->alt_name); + prefix_with_alt_name = false; + name_printed = true; + } + } + if (prefix_with_alt_name) + { + if (name_printed) + s->PutChar ('/'); + if (reg_info->alt_name) + { + s->Printf (fmt, reg_info->alt_name); + name_printed = true; + } + else if (!name_printed) + { + // No alternate name but we were asked to display a name, so show the main name + s->Printf (fmt, reg_info->name); + name_printed = true; + } + } + if (name_printed) + s->PutCString (" = "); + + if (format == eFormatDefault) + format = reg_info->format; + + data.Dump (s, + 0, // Offset in "data" + format, // Format to use when dumping + reg_info->byte_size, // item_byte_size + 1, // item_count + UINT32_MAX, // num_per_line + LLDB_INVALID_ADDRESS, // base_addr + 0, // item_bit_size + 0); // item_bit_offset + return true; + } + return false; +} + + +bool +RegisterValue::GetData (DataExtractor &data) const +{ + return data.SetData(GetBytes(), GetByteSize(), GetByteOrder()) > 0; +} + + +uint32_t +RegisterValue::GetAsMemoryData (const RegisterInfo *reg_info, + void *dst, + uint32_t dst_len, + lldb::ByteOrder dst_byte_order, + Error &error) const +{ + if (reg_info == NULL) + { + error.SetErrorString ("invalid register info argument."); + return 0; + } + + // ReadRegister should have already been called on tgus object prior to + // calling this. + if (GetType() == eTypeInvalid) + { + // No value has been read into this object... + error.SetErrorStringWithFormat("invalid register value type for register %s", reg_info->name); + return 0; + } + + if (dst_len > kMaxRegisterByteSize) + { + error.SetErrorString ("destination is too big"); + return 0; + } + + const uint32_t src_len = reg_info->byte_size; + + // Extract the register data into a data extractor + DataExtractor reg_data; + if (!GetData(reg_data)) + { + error.SetErrorString ("invalid register value to copy into"); + return 0; + } + + // Prepare a memory buffer that contains some or all of the register value + const uint32_t bytes_copied = reg_data.CopyByteOrderedData (0, // src offset + src_len, // src length + dst, // dst buffer + dst_len, // dst length + dst_byte_order); // dst byte order + if (bytes_copied == 0) + error.SetErrorStringWithFormat("failed to copy data for register write of %s", reg_info->name); + + return bytes_copied; +} + +uint32_t +RegisterValue::SetFromMemoryData (const RegisterInfo *reg_info, + const void *src, + uint32_t src_len, + lldb::ByteOrder src_byte_order, + Error &error) +{ + if (reg_info == NULL) + { + error.SetErrorString ("invalid register info argument."); + return 0; + } + + // Moving from addr into a register + // + // Case 1: src_len == dst_len + // + // |AABBCCDD| Address contents + // |AABBCCDD| Register contents + // + // Case 2: src_len > dst_len + // + // Error! (The register should always be big enough to hold the data) + // + // Case 3: src_len < dst_len + // + // |AABB| Address contents + // |AABB0000| Register contents [on little-endian hardware] + // |0000AABB| Register contents [on big-endian hardware] + if (src_len > kMaxRegisterByteSize) + { + error.SetErrorStringWithFormat ("register buffer is too small to receive %u bytes of data.", src_len); + return 0; + } + + const uint32_t dst_len = reg_info->byte_size; + + if (src_len > dst_len) + { + error.SetErrorStringWithFormat("%u bytes is too big to store in register %s (%u bytes)", src_len, reg_info->name, dst_len); + return 0; + } + + // Use a data extractor to correctly copy and pad the bytes read into the + // register value + DataExtractor src_data (src, src_len, src_byte_order, 4); + + // Given the register info, set the value type of this RegisterValue object + SetType (reg_info); + // And make sure we were able to figure out what that register value was + RegisterValue::Type value_type = GetType(); + if (value_type == eTypeInvalid) + { + // No value has been read into this object... + error.SetErrorStringWithFormat("invalid register value type for register %s", reg_info->name); + return 0; + } + else if (value_type == eTypeBytes) + { + m_data.buffer.byte_order = src_byte_order; + // Make sure to set the buffer length of the destination buffer to avoid + // problems due to uninitalized variables. + m_data.buffer.length = src_len; + } + + const uint32_t bytes_copied = src_data.CopyByteOrderedData (0, // src offset + src_len, // src length + GetBytes(), // dst buffer + GetByteSize(), // dst length + GetByteOrder()); // dst byte order + if (bytes_copied == 0) + error.SetErrorStringWithFormat("failed to copy data for register write of %s", reg_info->name); + + return bytes_copied; +} + +bool +RegisterValue::GetScalarValue (Scalar &scalar) const +{ + switch (m_type) + { + case eTypeInvalid: break; + case eTypeBytes: + { + switch (m_data.buffer.length) + { + default: break; + case 1: scalar = m_data.uint8; return true; + case 2: scalar = m_data.uint16; return true; + case 4: scalar = m_data.uint32; return true; + case 8: scalar = m_data.uint64; return true; + } + } + case eTypeUInt8: scalar = m_data.uint8; return true; + case eTypeUInt16: scalar = m_data.uint16; return true; + case eTypeUInt32: scalar = m_data.uint32; return true; + case eTypeUInt64: scalar = m_data.uint64; return true; +#if defined (ENABLE_128_BIT_SUPPORT) + case eTypeUInt128: break; +#endif + case eTypeFloat: scalar = m_data.ieee_float; return true; + case eTypeDouble: scalar = m_data.ieee_double; return true; + case eTypeLongDouble: scalar = m_data.ieee_long_double; return true; + } + return false; +} + +void +RegisterValue::Clear() +{ + m_type = eTypeInvalid; +} + +RegisterValue::Type +RegisterValue::SetType (const RegisterInfo *reg_info) +{ + m_type = eTypeInvalid; + const uint32_t byte_size = reg_info->byte_size; + switch (reg_info->encoding) + { + case eEncodingInvalid: + break; + + case eEncodingUint: + case eEncodingSint: + if (byte_size == 1) + m_type = eTypeUInt8; + else if (byte_size <= 2) + m_type = eTypeUInt16; + else if (byte_size <= 4) + m_type = eTypeUInt32; + else if (byte_size <= 8) + m_type = eTypeUInt64; +#if defined (ENABLE_128_BIT_SUPPORT) + else if (byte_size <= 16) + m_type = eTypeUInt128; +#endif + break; + + case eEncodingIEEE754: + if (byte_size == sizeof(float)) + m_type = eTypeFloat; + else if (byte_size == sizeof(double)) + m_type = eTypeDouble; + else if (byte_size == sizeof(long double)) + m_type = eTypeLongDouble; + break; + + case eEncodingVector: + m_type = eTypeBytes; + break; + } + return m_type; +} + +Error +RegisterValue::SetValueFromData (const RegisterInfo *reg_info, DataExtractor &src, lldb::offset_t src_offset, bool partial_data_ok) +{ + Error error; + + if (src.GetByteSize() == 0) + { + error.SetErrorString ("empty data."); + return error; + } + + if (reg_info->byte_size == 0) + { + error.SetErrorString ("invalid register info."); + return error; + } + + uint32_t src_len = src.GetByteSize() - src_offset; + + if (!partial_data_ok && (src_len < reg_info->byte_size)) + { + error.SetErrorString ("not enough data."); + return error; + } + + // Cap the data length if there is more than enough bytes for this register + // value + if (src_len > reg_info->byte_size) + src_len = reg_info->byte_size; + + // Zero out the value in case we get partial data... + memset (m_data.buffer.bytes, 0, sizeof (m_data.buffer.bytes)); + + switch (SetType (reg_info)) + { + case eTypeInvalid: + error.SetErrorString(""); + break; + case eTypeUInt8: SetUInt8 (src.GetMaxU32 (&src_offset, src_len)); break; + case eTypeUInt16: SetUInt16 (src.GetMaxU32 (&src_offset, src_len)); break; + case eTypeUInt32: SetUInt32 (src.GetMaxU32 (&src_offset, src_len)); break; + case eTypeUInt64: SetUInt64 (src.GetMaxU64 (&src_offset, src_len)); break; +#if defined (ENABLE_128_BIT_SUPPORT) + case eTypeUInt128: + { + __uint128_t data1 = src.GetU64 (&src_offset); + __uint128_t data2 = src.GetU64 (&src_offset); + if (src.GetByteSize() == eByteOrderBig) + SetUInt128 (data1 << 64 + data2); + else + SetUInt128 (data2 << 64 + data1); + } + break; +#endif + case eTypeFloat: SetFloat (src.GetFloat (&src_offset)); break; + case eTypeDouble: SetDouble(src.GetDouble (&src_offset)); break; + case eTypeLongDouble: SetFloat (src.GetLongDouble (&src_offset)); break; + case eTypeBytes: + { + m_data.buffer.length = reg_info->byte_size; + m_data.buffer.byte_order = src.GetByteOrder(); + assert (m_data.buffer.length <= kMaxRegisterByteSize); + if (m_data.buffer.length > kMaxRegisterByteSize) + m_data.buffer.length = kMaxRegisterByteSize; + if (src.CopyByteOrderedData (src_offset, // offset within "src" to start extracting data + src_len, // src length + m_data.buffer.bytes, // dst buffer + m_data.buffer.length, // dst length + m_data.buffer.byte_order) == 0)// dst byte order + { + error.SetErrorString ("data copy failed data."); + return error; + } + } + } + + return error; +} + +#include "llvm/ADT/StringRef.h" +#include +static inline void StripSpaces(llvm::StringRef &Str) +{ + while (!Str.empty() && isspace(Str[0])) + Str = Str.substr(1); + while (!Str.empty() && isspace(Str.back())) + Str = Str.substr(0, Str.size()-1); +} +static inline void LStrip(llvm::StringRef &Str, char c) +{ + if (!Str.empty() && Str.front() == c) + Str = Str.substr(1); +} +static inline void RStrip(llvm::StringRef &Str, char c) +{ + if (!Str.empty() && Str.back() == c) + Str = Str.substr(0, Str.size()-1); +} +// Helper function for RegisterValue::SetValueFromCString() +static bool +ParseVectorEncoding(const RegisterInfo *reg_info, const char *vector_str, const uint32_t byte_size, RegisterValue *reg_value) +{ + // Example: vector_str = "{0x2c 0x4b 0x2a 0x3e 0xd0 0x4f 0x2a 0x3e 0xac 0x4a 0x2a 0x3e 0x84 0x4f 0x2a 0x3e}". + llvm::StringRef Str(vector_str); + StripSpaces(Str); + LStrip(Str, '{'); + RStrip(Str, '}'); + StripSpaces(Str); + + char Sep = ' '; + + // The first split should give us: + // ('0x2c', '0x4b 0x2a 0x3e 0xd0 0x4f 0x2a 0x3e 0xac 0x4a 0x2a 0x3e 0x84 0x4f 0x2a 0x3e'). + std::pair Pair = Str.split(Sep); + std::vector bytes; + unsigned byte = 0; + + // Using radix auto-sensing by passing 0 as the radix. + // Keep on processing the vector elements as long as the parsing succeeds and the vector size is < byte_size. + while (!Pair.first.getAsInteger(0, byte) && bytes.size() < byte_size) { + bytes.push_back(byte); + Pair = Pair.second.split(Sep); + } + + // Check for vector of exact byte_size elements. + if (bytes.size() != byte_size) + return false; + + reg_value->SetBytes(&(bytes.front()), byte_size, eByteOrderLittle); + return true; +} +Error +RegisterValue::SetValueFromCString (const RegisterInfo *reg_info, const char *value_str) +{ + Error error; + if (reg_info == NULL) + { + error.SetErrorString ("Invalid register info argument."); + return error; + } + + if (value_str == NULL || value_str[0] == '\0') + { + error.SetErrorString ("Invalid c-string value string."); + return error; + } + bool success = false; + const uint32_t byte_size = reg_info->byte_size; + switch (reg_info->encoding) + { + case eEncodingInvalid: + error.SetErrorString ("Invalid encoding."); + break; + + case eEncodingUint: + if (byte_size <= sizeof (uint64_t)) + { + uint64_t uval64 = Args::StringToUInt64(value_str, UINT64_MAX, 0, &success); + if (!success) + error.SetErrorStringWithFormat ("'%s' is not a valid unsigned integer string value", value_str); + else if (!Args::UInt64ValueIsValidForByteSize (uval64, byte_size)) + error.SetErrorStringWithFormat ("value 0x%" PRIx64 " is too large to fit in a %u byte unsigned integer value", uval64, byte_size); + else + { + if (!SetUInt (uval64, reg_info->byte_size)) + error.SetErrorStringWithFormat ("unsupported unsigned integer byte size: %u", byte_size); + } + } + else + { + error.SetErrorStringWithFormat ("unsupported unsigned integer byte size: %u", byte_size); + return error; + } + break; + + case eEncodingSint: + if (byte_size <= sizeof (long long)) + { + uint64_t sval64 = Args::StringToSInt64(value_str, INT64_MAX, 0, &success); + if (!success) + error.SetErrorStringWithFormat ("'%s' is not a valid signed integer string value", value_str); + else if (!Args::SInt64ValueIsValidForByteSize (sval64, byte_size)) + error.SetErrorStringWithFormat ("value 0x%" PRIx64 " is too large to fit in a %u byte signed integer value", sval64, byte_size); + else + { + if (!SetUInt (sval64, reg_info->byte_size)) + error.SetErrorStringWithFormat ("unsupported signed integer byte size: %u", byte_size); + } + } + else + { + error.SetErrorStringWithFormat ("unsupported signed integer byte size: %u", byte_size); + return error; + } + break; + + case eEncodingIEEE754: + if (byte_size == sizeof (float)) + { + if (::sscanf (value_str, "%f", &m_data.ieee_float) == 1) + m_type = eTypeFloat; + else + error.SetErrorStringWithFormat ("'%s' is not a valid float string value", value_str); + } + else if (byte_size == sizeof (double)) + { + if (::sscanf (value_str, "%lf", &m_data.ieee_double) == 1) + m_type = eTypeDouble; + else + error.SetErrorStringWithFormat ("'%s' is not a valid float string value", value_str); + } + else if (byte_size == sizeof (long double)) + { + if (::sscanf (value_str, "%Lf", &m_data.ieee_long_double) == 1) + m_type = eTypeLongDouble; + else + error.SetErrorStringWithFormat ("'%s' is not a valid float string value", value_str); + } + else + { + error.SetErrorStringWithFormat ("unsupported float byte size: %u", byte_size); + return error; + } + break; + + case eEncodingVector: + if (!ParseVectorEncoding(reg_info, value_str, byte_size, this)) + error.SetErrorString ("unrecognized vector encoding string value."); + break; + } + if (error.Fail()) + m_type = eTypeInvalid; + + return error; +} + + +bool +RegisterValue::SignExtend (uint32_t sign_bitpos) +{ + switch (m_type) + { + case eTypeInvalid: + break; + + case eTypeUInt8: + if (sign_bitpos == (8-1)) + return true; + else if (sign_bitpos < (8-1)) + { + uint8_t sign_bit = 1u << sign_bitpos; + if (m_data.uint8 & sign_bit) + { + const uint8_t mask = ~(sign_bit) + 1u; + m_data.uint8 |= mask; + } + return true; + } + break; + + case eTypeUInt16: + if (sign_bitpos == (16-1)) + return true; + else if (sign_bitpos < (16-1)) + { + uint16_t sign_bit = 1u << sign_bitpos; + if (m_data.uint16 & sign_bit) + { + const uint16_t mask = ~(sign_bit) + 1u; + m_data.uint16 |= mask; + } + return true; + } + break; + + case eTypeUInt32: + if (sign_bitpos == (32-1)) + return true; + else if (sign_bitpos < (32-1)) + { + uint32_t sign_bit = 1u << sign_bitpos; + if (m_data.uint32 & sign_bit) + { + const uint32_t mask = ~(sign_bit) + 1u; + m_data.uint32 |= mask; + } + return true; + } + break; + + case eTypeUInt64: + if (sign_bitpos == (64-1)) + return true; + else if (sign_bitpos < (64-1)) + { + uint64_t sign_bit = 1ull << sign_bitpos; + if (m_data.uint64 & sign_bit) + { + const uint64_t mask = ~(sign_bit) + 1ull; + m_data.uint64 |= mask; + } + return true; + } + break; + +#if defined (ENABLE_128_BIT_SUPPORT) + case eTypeUInt128: + if (sign_bitpos == (128-1)) + return true; + else if (sign_bitpos < (128-1)) + { + __uint128_t sign_bit = (__uint128_t)1u << sign_bitpos; + if (m_data.uint128 & sign_bit) + { + const uint128_t mask = ~(sign_bit) + 1u; + m_data.uint128 |= mask; + } + return true; + } + break; +#endif + case eTypeFloat: + case eTypeDouble: + case eTypeLongDouble: + case eTypeBytes: + break; + } + return false; +} + +bool +RegisterValue::CopyValue (const RegisterValue &rhs) +{ + m_type = rhs.m_type; + switch (m_type) + { + case eTypeInvalid: + return false; + case eTypeUInt8: m_data.uint8 = rhs.m_data.uint8; break; + case eTypeUInt16: m_data.uint16 = rhs.m_data.uint16; break; + case eTypeUInt32: m_data.uint32 = rhs.m_data.uint32; break; + case eTypeUInt64: m_data.uint64 = rhs.m_data.uint64; break; +#if defined (ENABLE_128_BIT_SUPPORT) + case eTypeUInt128: m_data.uint128 = rhs.m_data.uint128; break; +#endif + case eTypeFloat: m_data.ieee_float = rhs.m_data.ieee_float; break; + case eTypeDouble: m_data.ieee_double = rhs.m_data.ieee_double; break; + case eTypeLongDouble: m_data.ieee_long_double = rhs.m_data.ieee_long_double; break; + case eTypeBytes: + assert (rhs.m_data.buffer.length <= kMaxRegisterByteSize); + ::memcpy (m_data.buffer.bytes, rhs.m_data.buffer.bytes, kMaxRegisterByteSize); + m_data.buffer.length = rhs.m_data.buffer.length; + m_data.buffer.byte_order = rhs.m_data.buffer.byte_order; + break; + } + return true; +} + +uint16_t +RegisterValue::GetAsUInt16 (uint16_t fail_value, bool *success_ptr) const +{ + if (success_ptr) + *success_ptr = true; + + switch (m_type) + { + default: break; + case eTypeUInt8: return m_data.uint8; + case eTypeUInt16: return m_data.uint16; + case eTypeBytes: + { + switch (m_data.buffer.length) + { + default: break; + case 1: return m_data.uint8; + case 2: return m_data.uint16; + } + } + break; + } + if (success_ptr) + *success_ptr = false; + return fail_value; +} + +uint32_t +RegisterValue::GetAsUInt32 (uint32_t fail_value, bool *success_ptr) const +{ + if (success_ptr) + *success_ptr = true; + switch (m_type) + { + default: break; + case eTypeUInt8: return m_data.uint8; + case eTypeUInt16: return m_data.uint16; + case eTypeUInt32: return m_data.uint32; + case eTypeFloat: + if (sizeof(float) == sizeof(uint32_t)) + return m_data.uint32; + break; + case eTypeDouble: + if (sizeof(double) == sizeof(uint32_t)) + return m_data.uint32; + break; + case eTypeLongDouble: + if (sizeof(long double) == sizeof(uint32_t)) + return m_data.uint32; + break; + case eTypeBytes: + { + switch (m_data.buffer.length) + { + default: break; + case 1: return m_data.uint8; + case 2: return m_data.uint16; + case 4: return m_data.uint32; + } + } + break; + } + if (success_ptr) + *success_ptr = false; + return fail_value; +} + +uint64_t +RegisterValue::GetAsUInt64 (uint64_t fail_value, bool *success_ptr) const +{ + if (success_ptr) + *success_ptr = true; + switch (m_type) + { + default: break; + case eTypeUInt8: return m_data.uint8; + case eTypeUInt16: return m_data.uint16; + case eTypeUInt32: return m_data.uint32; + case eTypeUInt64: return m_data.uint64; + case eTypeFloat: + if (sizeof(float) == sizeof(uint64_t)) + return m_data.uint64; + break; + case eTypeDouble: + if (sizeof(double) == sizeof(uint64_t)) + return m_data.uint64; + break; + case eTypeLongDouble: + if (sizeof(long double) == sizeof(uint64_t)) + return m_data.uint64; + break; + case eTypeBytes: + { + switch (m_data.buffer.length) + { + default: break; + case 1: return m_data.uint8; + case 2: return m_data.uint16; + case 4: return m_data.uint32; + case 8: return m_data.uint64; + } + } + break; + } + if (success_ptr) + *success_ptr = false; + return fail_value; +} + +#if defined (ENABLE_128_BIT_SUPPORT) +__uint128_t +RegisterValue::GetAsUInt128 (__uint128_t fail_value, bool *success_ptr) const +{ + if (success_ptr) + *success_ptr = true; + switch (m_type) + { + default: break; + case eTypeUInt8: return m_data.uint8; + case eTypeUInt16: return m_data.uint16; + case eTypeUInt32: return m_data.uint32; + case eTypeUInt64: return m_data.uint64; + case eTypeUInt128: return m_data.uint128; + case eTypeFloat: + if (sizeof(float) == sizeof(__uint128_t)) + return m_data.uint128; + break; + case eTypeDouble: + if (sizeof(double) == sizeof(__uint128_t)) + return m_data.uint128; + break; + case eTypeLongDouble: + if (sizeof(long double) == sizeof(__uint128_t)) + return m_data.uint128; + break; + case eTypeBytes: + { + switch (m_data.buffer.length) + { + default: + break; + case 1: return m_data.uint8; + case 2: return m_data.uint16; + case 4: return m_data.uint32; + case 8: return m_data.uint64; + case 16: return m_data.uint128; + } + } + break; + } + if (success_ptr) + *success_ptr = false; + return fail_value; +} +#endif +float +RegisterValue::GetAsFloat (float fail_value, bool *success_ptr) const +{ + if (success_ptr) + *success_ptr = true; + switch (m_type) + { + default: break; + case eTypeUInt32: + if (sizeof(float) == sizeof(m_data.uint32)) + return m_data.ieee_float; + break; + case eTypeUInt64: + if (sizeof(float) == sizeof(m_data.uint64)) + return m_data.ieee_float; + break; +#if defined (ENABLE_128_BIT_SUPPORT) + case eTypeUInt128: + if (sizeof(float) == sizeof(m_data.uint128)) + return m_data.ieee_float; + break; +#endif + case eTypeFloat: return m_data.ieee_float; + case eTypeDouble: + if (sizeof(float) == sizeof(double)) + return m_data.ieee_float; + break; + case eTypeLongDouble: + if (sizeof(float) == sizeof(long double)) + return m_data.ieee_float; + break; + } + if (success_ptr) + *success_ptr = false; + return fail_value; +} + +double +RegisterValue::GetAsDouble (double fail_value, bool *success_ptr) const +{ + if (success_ptr) + *success_ptr = true; + switch (m_type) + { + default: + break; + + case eTypeUInt32: + if (sizeof(double) == sizeof(m_data.uint32)) + return m_data.ieee_double; + break; + + case eTypeUInt64: + if (sizeof(double) == sizeof(m_data.uint64)) + return m_data.ieee_double; + break; + +#if defined (ENABLE_128_BIT_SUPPORT) + case eTypeUInt128: + if (sizeof(double) == sizeof(m_data.uint128)) + return m_data.ieee_double; +#endif + case eTypeFloat: return m_data.ieee_float; + case eTypeDouble: return m_data.ieee_double; + + case eTypeLongDouble: + if (sizeof(double) == sizeof(long double)) + return m_data.ieee_double; + break; + } + if (success_ptr) + *success_ptr = false; + return fail_value; +} + +long double +RegisterValue::GetAsLongDouble (long double fail_value, bool *success_ptr) const +{ + if (success_ptr) + *success_ptr = true; + switch (m_type) + { + default: + break; + + case eTypeUInt32: + if (sizeof(long double) == sizeof(m_data.uint32)) + return m_data.ieee_long_double; + break; + + case eTypeUInt64: + if (sizeof(long double) == sizeof(m_data.uint64)) + return m_data.ieee_long_double; + break; + +#if defined (ENABLE_128_BIT_SUPPORT) + case eTypeUInt128: + if (sizeof(long double) == sizeof(m_data.uint128)) + return m_data.ieee_long_double; +#endif + case eTypeFloat: return m_data.ieee_float; + case eTypeDouble: return m_data.ieee_double; + case eTypeLongDouble: return m_data.ieee_long_double; + break; + } + if (success_ptr) + *success_ptr = false; + return fail_value; +} + +const void * +RegisterValue::GetBytes () const +{ + switch (m_type) + { + case eTypeInvalid: break; + case eTypeUInt8: return &m_data.uint8; + case eTypeUInt16: return &m_data.uint16; + case eTypeUInt32: return &m_data.uint32; + case eTypeUInt64: return &m_data.uint64; +#if defined (ENABLE_128_BIT_SUPPORT) + case eTypeUInt128: return &m_data.uint128; +#endif + case eTypeFloat: return &m_data.ieee_float; + case eTypeDouble: return &m_data.ieee_double; + case eTypeLongDouble: return &m_data.ieee_long_double; + case eTypeBytes: return m_data.buffer.bytes; + } + return NULL; +} + +void * +RegisterValue::GetBytes () +{ + switch (m_type) + { + case eTypeInvalid: break; + case eTypeUInt8: return &m_data.uint8; + case eTypeUInt16: return &m_data.uint16; + case eTypeUInt32: return &m_data.uint32; + case eTypeUInt64: return &m_data.uint64; +#if defined (ENABLE_128_BIT_SUPPORT) + case eTypeUInt128: return &m_data.uint128; +#endif + case eTypeFloat: return &m_data.ieee_float; + case eTypeDouble: return &m_data.ieee_double; + case eTypeLongDouble: return &m_data.ieee_long_double; + case eTypeBytes: return m_data.buffer.bytes; + } + return NULL; +} + +uint32_t +RegisterValue::GetByteSize () const +{ + switch (m_type) + { + case eTypeInvalid: break; + case eTypeUInt8: return sizeof(m_data.uint8); + case eTypeUInt16: return sizeof(m_data.uint16); + case eTypeUInt32: return sizeof(m_data.uint32); + case eTypeUInt64: return sizeof(m_data.uint64); +#if defined (ENABLE_128_BIT_SUPPORT) + case eTypeUInt128: return sizeof(m_data.uint128); +#endif + case eTypeFloat: return sizeof(m_data.ieee_float); + case eTypeDouble: return sizeof(m_data.ieee_double); + case eTypeLongDouble: return sizeof(m_data.ieee_long_double); + case eTypeBytes: return m_data.buffer.length; + } + return 0; +} + + +bool +RegisterValue::SetUInt (uint64_t uint, uint32_t byte_size) +{ + if (byte_size == 0) + { + SetUInt64 (uint); + } + else if (byte_size == 1) + { + SetUInt8 (uint); + } + else if (byte_size <= 2) + { + SetUInt16 (uint); + } + else if (byte_size <= 4) + { + SetUInt32 (uint); + } + else if (byte_size <= 8) + { + SetUInt64 (uint); + } +#if defined (ENABLE_128_BIT_SUPPORT) + else if (byte_size <= 16) + { + SetUInt128 (uint); + } +#endif + else + return false; + return true; +} + +void +RegisterValue::SetBytes (const void *bytes, size_t length, lldb::ByteOrder byte_order) +{ + // If this assertion fires off we need to increase the size of + // m_data.buffer.bytes, or make it something that is allocated on + // the heap. Since the data buffer is in a union, we can't make it + // a collection class like SmallVector... + if (bytes && length > 0) + { + assert (length <= sizeof (m_data.buffer.bytes) && "Storing too many bytes in a RegisterValue."); + m_type = eTypeBytes; + m_data.buffer.length = length; + memcpy (m_data.buffer.bytes, bytes, length); + m_data.buffer.byte_order = byte_order; + } + else + { + m_type = eTypeInvalid; + m_data.buffer.length = 0; + } +} + + +bool +RegisterValue::operator == (const RegisterValue &rhs) const +{ + if (m_type == rhs.m_type) + { + switch (m_type) + { + case eTypeInvalid: return true; + case eTypeUInt8: return m_data.uint8 == rhs.m_data.uint8; + case eTypeUInt16: return m_data.uint16 == rhs.m_data.uint16; + case eTypeUInt32: return m_data.uint32 == rhs.m_data.uint32; + case eTypeUInt64: return m_data.uint64 == rhs.m_data.uint64; +#if defined (ENABLE_128_BIT_SUPPORT) + case eTypeUInt128: return m_data.uint128 == rhs.m_data.uint128; +#endif + case eTypeFloat: return m_data.ieee_float == rhs.m_data.ieee_float; + case eTypeDouble: return m_data.ieee_double == rhs.m_data.ieee_double; + case eTypeLongDouble: return m_data.ieee_long_double == rhs.m_data.ieee_long_double; + case eTypeBytes: + if (m_data.buffer.length != rhs.m_data.buffer.length) + return false; + else + { + uint8_t length = m_data.buffer.length; + if (length > kMaxRegisterByteSize) + length = kMaxRegisterByteSize; + return memcmp (m_data.buffer.bytes, rhs.m_data.buffer.bytes, length) == 0; + } + break; + } + } + return false; +} + +bool +RegisterValue::operator != (const RegisterValue &rhs) const +{ + if (m_type != rhs.m_type) + return true; + switch (m_type) + { + case eTypeInvalid: return false; + case eTypeUInt8: return m_data.uint8 != rhs.m_data.uint8; + case eTypeUInt16: return m_data.uint16 != rhs.m_data.uint16; + case eTypeUInt32: return m_data.uint32 != rhs.m_data.uint32; + case eTypeUInt64: return m_data.uint64 != rhs.m_data.uint64; +#if defined (ENABLE_128_BIT_SUPPORT) + case eTypeUInt128: return m_data.uint128 != rhs.m_data.uint128; +#endif + case eTypeFloat: return m_data.ieee_float != rhs.m_data.ieee_float; + case eTypeDouble: return m_data.ieee_double != rhs.m_data.ieee_double; + case eTypeLongDouble: return m_data.ieee_long_double != rhs.m_data.ieee_long_double; + case eTypeBytes: + if (m_data.buffer.length != rhs.m_data.buffer.length) + { + return true; + } + else + { + uint8_t length = m_data.buffer.length; + if (length > kMaxRegisterByteSize) + length = kMaxRegisterByteSize; + return memcmp (m_data.buffer.bytes, rhs.m_data.buffer.bytes, length) != 0; + } + break; + } + return true; +} + +bool +RegisterValue::ClearBit (uint32_t bit) +{ + switch (m_type) + { + case eTypeInvalid: + break; + + case eTypeUInt8: + if (bit < 8) + { + m_data.uint8 &= ~(1u << bit); + return true; + } + break; + + case eTypeUInt16: + if (bit < 16) + { + m_data.uint16 &= ~(1u << bit); + return true; + } + break; + + case eTypeUInt32: + if (bit < 32) + { + m_data.uint32 &= ~(1u << bit); + return true; + } + break; + + case eTypeUInt64: + if (bit < 64) + { + m_data.uint64 &= ~(1ull << (uint64_t)bit); + return true; + } + break; +#if defined (ENABLE_128_BIT_SUPPORT) + case eTypeUInt128: + if (bit < 64) + { + m_data.uint128 &= ~((__uint128_t)1ull << (__uint128_t)bit); + return true; + } +#endif + case eTypeFloat: + case eTypeDouble: + case eTypeLongDouble: + break; + + case eTypeBytes: + if (m_data.buffer.byte_order == eByteOrderBig || m_data.buffer.byte_order == eByteOrderLittle) + { + uint32_t byte_idx; + if (m_data.buffer.byte_order == eByteOrderBig) + byte_idx = m_data.buffer.length - (bit / 8) - 1; + else + byte_idx = bit / 8; + + const uint32_t byte_bit = bit % 8; + if (byte_idx < m_data.buffer.length) + { + m_data.buffer.bytes[byte_idx] &= ~(1u << byte_bit); + return true; + } + } + break; + } + return false; +} + + +bool +RegisterValue::SetBit (uint32_t bit) +{ + switch (m_type) + { + case eTypeInvalid: + break; + + case eTypeUInt8: + if (bit < 8) + { + m_data.uint8 |= (1u << bit); + return true; + } + break; + + case eTypeUInt16: + if (bit < 16) + { + m_data.uint16 |= (1u << bit); + return true; + } + break; + + case eTypeUInt32: + if (bit < 32) + { + m_data.uint32 |= (1u << bit); + return true; + } + break; + + case eTypeUInt64: + if (bit < 64) + { + m_data.uint64 |= (1ull << (uint64_t)bit); + return true; + } + break; +#if defined (ENABLE_128_BIT_SUPPORT) + case eTypeUInt128: + if (bit < 64) + { + m_data.uint128 |= ((__uint128_t)1ull << (__uint128_t)bit); + return true; + } +#endif + case eTypeFloat: + case eTypeDouble: + case eTypeLongDouble: + break; + + case eTypeBytes: + if (m_data.buffer.byte_order == eByteOrderBig || m_data.buffer.byte_order == eByteOrderLittle) + { + uint32_t byte_idx; + if (m_data.buffer.byte_order == eByteOrderBig) + byte_idx = m_data.buffer.length - (bit / 8) - 1; + else + byte_idx = bit / 8; + + const uint32_t byte_bit = bit % 8; + if (byte_idx < m_data.buffer.length) + { + m_data.buffer.bytes[byte_idx] |= (1u << byte_bit); + return true; + } + } + break; + } + return false; +} + diff --git a/contrib/llvm/tools/lldb/source/Core/RegularExpression.cpp b/contrib/llvm/tools/lldb/source/Core/RegularExpression.cpp new file mode 100644 index 00000000000..4ccd7748b13 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Core/RegularExpression.cpp @@ -0,0 +1,279 @@ +//===-- RegularExpression.cpp -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Core/RegularExpression.h" +#include "llvm/ADT/StringRef.h" +#include + +using namespace lldb_private; + +//---------------------------------------------------------------------- +// Default constructor +//---------------------------------------------------------------------- +RegularExpression::RegularExpression() : + m_re(), + m_comp_err (1), + m_preg(), + m_compile_flags(REG_EXTENDED) +{ + memset(&m_preg,0,sizeof(m_preg)); +} + +//---------------------------------------------------------------------- +// Constructor that compiles "re" using "flags" and stores the +// resulting compiled regular expression into this object. +//---------------------------------------------------------------------- +RegularExpression::RegularExpression(const char* re, int flags) : + m_re(), + m_comp_err (1), + m_preg(), + m_compile_flags(flags) +{ + memset(&m_preg,0,sizeof(m_preg)); + Compile(re); +} + +//---------------------------------------------------------------------- +// Constructor that compiles "re" using "flags" and stores the +// resulting compiled regular expression into this object. +//---------------------------------------------------------------------- +RegularExpression::RegularExpression(const char* re) : + m_re(), + m_comp_err (1), + m_preg(), + m_compile_flags(REG_EXTENDED) +{ + memset(&m_preg,0,sizeof(m_preg)); + Compile(re); +} + +RegularExpression::RegularExpression(const RegularExpression &rhs) +{ + memset(&m_preg,0,sizeof(m_preg)); + Compile(rhs.GetText(), rhs.GetCompileFlags()); +} + +const RegularExpression & +RegularExpression::operator= (const RegularExpression &rhs) +{ + if (&rhs != this) + { + Compile (rhs.GetText(), rhs.GetCompileFlags()); + } + return *this; +} +//---------------------------------------------------------------------- +// Destructor +// +// Any previosuly compiled regular expression contained in this +// object will be freed. +//---------------------------------------------------------------------- +RegularExpression::~RegularExpression() +{ + Free(); +} + +//---------------------------------------------------------------------- +// Compile a regular expression using the supplied regular +// expression text and flags. The compied regular expression lives +// in this object so that it can be readily used for regular +// expression matches. Execute() can be called after the regular +// expression is compiled. Any previosuly compiled regular +// expression contained in this object will be freed. +// +// RETURNS +// True of the refular expression compiles successfully, false +// otherwise. +//---------------------------------------------------------------------- +bool +RegularExpression::Compile(const char* re) +{ + return Compile (re, m_compile_flags); +} + +bool +RegularExpression::Compile(const char* re, int flags) +{ + Free(); + m_compile_flags = flags; + + if (re && re[0]) + { + m_re = re; + m_comp_err = ::regcomp (&m_preg, re, flags); + } + else + { + // No valid regular expression + m_comp_err = 1; + } + + return m_comp_err == 0; +} + +//---------------------------------------------------------------------- +// Execute a regular expression match using the compiled regular +// expression that is already in this object against the match +// string "s". If any parens are used for regular expression +// matches "match_count" should indicate the number of regmatch_t +// values that are present in "match_ptr". The regular expression +// will be executed using the "execute_flags". +//--------------------------------------------------------------------- +bool +RegularExpression::Execute(const char* s, Match *match, int execute_flags) const +{ + int err = 1; + if (s != NULL && m_comp_err == 0) + { + if (match) + { + err = ::regexec (&m_preg, + s, + match->GetSize(), + match->GetData(), + execute_flags); + } + else + { + err = ::regexec (&m_preg, + s, + 0, + NULL, + execute_flags); + } + } + + if (err != 0) + { + // The regular expression didn't compile, so clear the matches + if (match) + match->Clear(); + return false; + } + return true; +} + +bool +RegularExpression::Match::GetMatchAtIndex (const char* s, uint32_t idx, std::string& match_str) const +{ + if (idx < m_matches.size()) + { + if (m_matches[idx].rm_eo == m_matches[idx].rm_so) + { + // Matched the empty string... + match_str.clear(); + return true; + } + else if (m_matches[idx].rm_eo > m_matches[idx].rm_so) + { + match_str.assign (s + m_matches[idx].rm_so, + m_matches[idx].rm_eo - m_matches[idx].rm_so); + return true; + } + } + return false; +} + +bool +RegularExpression::Match::GetMatchAtIndex (const char* s, uint32_t idx, llvm::StringRef& match_str) const +{ + if (idx < m_matches.size()) + { + if (m_matches[idx].rm_eo == m_matches[idx].rm_so) + { + // Matched the empty string... + match_str = llvm::StringRef(); + return true; + } + else if (m_matches[idx].rm_eo > m_matches[idx].rm_so) + { + match_str = llvm::StringRef (s + m_matches[idx].rm_so, m_matches[idx].rm_eo - m_matches[idx].rm_so); + return true; + } + } + return false; +} + +bool +RegularExpression::Match::GetMatchSpanningIndices (const char* s, uint32_t idx1, uint32_t idx2, llvm::StringRef& match_str) const +{ + if (idx1 < m_matches.size() && idx2 < m_matches.size()) + { + if (m_matches[idx1].rm_so == m_matches[idx2].rm_eo) + { + // Matched the empty string... + match_str = llvm::StringRef(); + return true; + } + else if (m_matches[idx1].rm_so < m_matches[idx2].rm_eo) + { + match_str = llvm::StringRef (s + m_matches[idx1].rm_so, m_matches[idx2].rm_eo - m_matches[idx1].rm_so); + return true; + } + } + return false; +} + + +//---------------------------------------------------------------------- +// Returns true if the regular expression compiled and is ready +// for execution. +//---------------------------------------------------------------------- +bool +RegularExpression::IsValid () const +{ + return m_comp_err == 0; +} + +//---------------------------------------------------------------------- +// Returns the text that was used to compile the current regular +// expression. +//---------------------------------------------------------------------- +const char* +RegularExpression::GetText () const +{ + if (m_re.empty()) + return NULL; + return m_re.c_str(); +} + +//---------------------------------------------------------------------- +// Free any contained compiled regular expressions. +//---------------------------------------------------------------------- +void +RegularExpression::Free() +{ + if (m_comp_err == 0) + { + m_re.clear(); + regfree(&m_preg); + // Set a compile error since we no longer have a valid regex + m_comp_err = 1; + } +} + +size_t +RegularExpression::GetErrorAsCString (char *err_str, size_t err_str_max_len) const +{ + if (m_comp_err == 0) + { + if (err_str && err_str_max_len) + *err_str = '\0'; + return 0; + } + + return ::regerror (m_comp_err, &m_preg, err_str, err_str_max_len); +} + +bool +RegularExpression::operator < (const RegularExpression& rhs) const +{ + return (m_re < rhs.m_re); +} + diff --git a/contrib/llvm/tools/lldb/source/Core/Scalar.cpp b/contrib/llvm/tools/lldb/source/Core/Scalar.cpp new file mode 100644 index 00000000000..26f74379625 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Core/Scalar.cpp @@ -0,0 +1,2279 @@ +//===-- Scalar.cpp ----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Core/Scalar.h" + +#include +#include + +#include "lldb/Interpreter/Args.h" +#include "lldb/Core/Error.h" +#include "lldb/Core/Stream.h" +#include "lldb/Core/DataExtractor.h" +#include "lldb/Host/Endian.h" + +#include "Plugins/Process/Utility/InstructionUtils.h" + +using namespace lldb; +using namespace lldb_private; + +//---------------------------------------------------------------------- +// Promote to max type currently follows the ANSI C rule for type +// promotion in expressions. +//---------------------------------------------------------------------- +static Scalar::Type +PromoteToMaxType +( + const Scalar& lhs, // The const left hand side object + const Scalar& rhs, // The const right hand side object + Scalar& temp_value, // A modifiable temp value than can be used to hold either the promoted lhs or rhs object + const Scalar* &promoted_lhs_ptr, // Pointer to the resulting possibly promoted value of lhs (at most one of lhs/rhs will get promoted) + const Scalar* &promoted_rhs_ptr // Pointer to the resulting possibly promoted value of rhs (at most one of lhs/rhs will get promoted) +) +{ + Scalar result; + // Initialize the promoted values for both the right and left hand side values + // to be the objects themselves. If no promotion is needed (both right and left + // have the same type), then the temp_value will not get used. + promoted_lhs_ptr = &lhs; + promoted_rhs_ptr = &rhs; + // Extract the types of both the right and left hand side values + Scalar::Type lhs_type = lhs.GetType(); + Scalar::Type rhs_type = rhs.GetType(); + + if (lhs_type > rhs_type) + { + // Right hand side need to be promoted + temp_value = rhs; // Copy right hand side into the temp value + if (temp_value.Promote(lhs_type)) // Promote it + promoted_rhs_ptr = &temp_value; // Update the pointer for the promoted right hand side + } + else if (lhs_type < rhs_type) + { + // Left hand side need to be promoted + temp_value = lhs; // Copy left hand side value into the temp value + if (temp_value.Promote(rhs_type)) // Promote it + promoted_lhs_ptr = &temp_value; // Update the pointer for the promoted left hand side + } + + // Make sure our type promotion worked as exptected + if (promoted_lhs_ptr->GetType() == promoted_rhs_ptr->GetType()) + return promoted_lhs_ptr->GetType(); // Return the resulting max type + + // Return the void type (zero) if we fail to promote either of the values. + return Scalar::e_void; +} + + +//---------------------------------------------------------------------- +// Scalar constructor +//---------------------------------------------------------------------- +Scalar::Scalar() : + m_type(e_void), + m_data() +{ +} + +//---------------------------------------------------------------------- +// Scalar copy constructor +//---------------------------------------------------------------------- +Scalar::Scalar(const Scalar& rhs) : + m_type(rhs.m_type), + m_data(rhs.m_data) // TODO: verify that for C++ this will correctly copy the union?? +{ +} + +//Scalar::Scalar(const RegisterValue& reg) : +// m_type(e_void), +// m_data() +//{ +// switch (reg.info.encoding) +// { +// case eEncodingUint: // unsigned integer +// switch (reg.info.byte_size) +// { +// case 1: m_type = e_uint; m_data.uint = reg.value.uint8; break; +// case 2: m_type = e_uint; m_data.uint = reg.value.uint16; break; +// case 4: m_type = e_uint; m_data.uint = reg.value.uint32; break; +// case 8: m_type = e_ulonglong; m_data.ulonglong = reg.value.uint64; break; +// break; +// } +// break; +// +// case eEncodingSint: // signed integer +// switch (reg.info.byte_size) +// { +// case 1: m_type = e_sint; m_data.sint = reg.value.sint8; break; +// case 2: m_type = e_sint; m_data.sint = reg.value.sint16; break; +// case 4: m_type = e_sint; m_data.sint = reg.value.sint32; break; +// case 8: m_type = e_slonglong; m_data.slonglong = reg.value.sint64; break; +// break; +// } +// break; +// +// case eEncodingIEEE754: // float +// switch (reg.info.byte_size) +// { +// case 4: m_type = e_float; m_data.flt = reg.value.float32; break; +// case 8: m_type = e_double; m_data.dbl = reg.value.float64; break; +// break; +// } +// break; +// case eEncodingVector: // vector registers +// break; +// } +//} + +bool +Scalar::GetData (DataExtractor &data, size_t limit_byte_size) const +{ + size_t byte_size = GetByteSize(); + if (byte_size > 0) + { + if (limit_byte_size < byte_size) + { + if (lldb::endian::InlHostByteOrder() == eByteOrderLittle) + { + // On little endian systems if we want fewer bytes from the + // current type we just specify fewer bytes since the LSByte + // is first... + data.SetData((uint8_t*)&m_data, limit_byte_size, lldb::endian::InlHostByteOrder()); + } + else if (lldb::endian::InlHostByteOrder() == eByteOrderBig) + { + // On big endian systems if we want fewer bytes from the + // current type have to advance our initial byte pointer and + // trim down the number of bytes since the MSByte is first + data.SetData(((uint8_t*)&m_data) + byte_size - limit_byte_size, limit_byte_size, lldb::endian::InlHostByteOrder()); + } + } + else + { + // We want all of the data + data.SetData((uint8_t*)&m_data, byte_size, lldb::endian::InlHostByteOrder()); + } + return true; + } + data.Clear(); + return false; +} + +size_t +Scalar::GetByteSize() const +{ + switch (m_type) + { + case e_void: + break; + case e_sint: return sizeof(m_data.sint); + case e_uint: return sizeof(m_data.uint); + case e_slong: return sizeof(m_data.slong); + case e_ulong: return sizeof(m_data.ulong); + case e_slonglong: return sizeof(m_data.slonglong); + case e_ulonglong: return sizeof(m_data.ulonglong); + case e_float: return sizeof(m_data.flt); + case e_double: return sizeof(m_data.dbl); + case e_long_double: return sizeof(m_data.ldbl); + } + return 0; +} + +bool +Scalar::IsZero() const +{ + switch (m_type) + { + case e_void: + break; + case e_sint: return m_data.sint == 0; + case e_uint: return m_data.uint == 0; + case e_slong: return m_data.slong == 0; + case e_ulong: return m_data.ulong == 0; + case e_slonglong: return m_data.slonglong == 0; + case e_ulonglong: return m_data.ulonglong == 0; + case e_float: return m_data.flt == 0.0f; + case e_double: return m_data.dbl == 0.0; + case e_long_double: return m_data.ldbl == 0.0; + } + return false; +} + +void +Scalar::GetValue (Stream *s, bool show_type) const +{ + if (show_type) + s->Printf("(%s) ", GetTypeAsCString()); + + switch (m_type) + { + case e_void: + break; + case e_sint: s->Printf("%i", m_data.sint); break; + case e_uint: s->Printf("0x%8.8x", m_data.uint); break; + case e_slong: s->Printf("%li", m_data.slong); break; + case e_ulong: s->Printf("0x%8.8lx", m_data.ulong); break; + case e_slonglong: s->Printf("%lli", m_data.slonglong); break; + case e_ulonglong: s->Printf("0x%16.16llx", m_data.ulonglong); break; + case e_float: s->Printf("%f", m_data.flt); break; + case e_double: s->Printf("%g", m_data.dbl); break; + case e_long_double: s->Printf("%Lg", m_data.ldbl); break; + } +} + +const char * +Scalar::GetTypeAsCString() const +{ + switch (m_type) + { + case e_void: return "void"; + case e_sint: return "int"; + case e_uint: return "unsigned int"; + case e_slong: return "long"; + case e_ulong: return "unsigned long"; + case e_slonglong: return "long long"; + case e_ulonglong: return "unsigned long long"; + case e_float: return "float"; + case e_double: return "double"; + case e_long_double: return "long double"; + } + return ""; +} + + + +//---------------------------------------------------------------------- +// Scalar copy constructor +//---------------------------------------------------------------------- +Scalar& +Scalar::operator=(const Scalar& rhs) +{ + if (this != &rhs) + { + m_type = rhs.m_type; + ::memcpy (&m_data, &rhs.m_data, sizeof(m_data)); + } + return *this; +} + +Scalar& +Scalar::operator= (const int v) +{ + m_type = e_sint; + m_data.sint = v; + return *this; +} + + +Scalar& +Scalar::operator= (unsigned int v) +{ + m_type = e_uint; + m_data.uint = v; + return *this; +} + +Scalar& +Scalar::operator= (long v) +{ + m_type = e_slong; + m_data.slong = v; + return *this; +} + +Scalar& +Scalar::operator= (unsigned long v) +{ + m_type = e_ulong; + m_data.ulong = v; + return *this; +} + +Scalar& +Scalar::operator= (long long v) +{ + m_type = e_slonglong; + m_data.slonglong = v; + return *this; +} + +Scalar& +Scalar::operator= (unsigned long long v) +{ + m_type = e_ulonglong; + m_data.ulonglong = v; + return *this; +} + +Scalar& +Scalar::operator= (float v) +{ + m_type = e_float; + m_data.flt = v; + return *this; +} + +Scalar& +Scalar::operator= (double v) +{ + m_type = e_double; + m_data.dbl = v; + return *this; +} + +Scalar& +Scalar::operator= (long double v) +{ + m_type = e_long_double; + m_data.ldbl = v; + return *this; +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +Scalar::~Scalar() +{ +} + +bool +Scalar::Promote(Scalar::Type type) +{ + bool success = false; + switch (m_type) + { + case e_void: + break; + + case e_sint: + switch (type) + { + case e_void: break; + case e_sint: success = true; break; + case e_uint: m_data.uint = m_data.sint; success = true; break; + case e_slong: m_data.slong = m_data.sint; success = true; break; + case e_ulong: m_data.ulong = m_data.sint; success = true; break; + case e_slonglong: m_data.slonglong = m_data.sint; success = true; break; + case e_ulonglong: m_data.ulonglong = m_data.sint; success = true; break; + case e_float: m_data.flt = m_data.sint; success = true; break; + case e_double: m_data.dbl = m_data.sint; success = true; break; + case e_long_double: m_data.ldbl = m_data.sint; success = true; break; + } + break; + + case e_uint: + switch (type) + { + case e_void: + case e_sint: break; + case e_uint: success = true; break; + case e_slong: m_data.slong = m_data.uint; success = true; break; + case e_ulong: m_data.ulong = m_data.uint; success = true; break; + case e_slonglong: m_data.slonglong = m_data.uint; success = true; break; + case e_ulonglong: m_data.ulonglong = m_data.uint; success = true; break; + case e_float: m_data.flt = m_data.uint; success = true; break; + case e_double: m_data.dbl = m_data.uint; success = true; break; + case e_long_double: m_data.ldbl = m_data.uint; success = true; break; + } + break; + + case e_slong: + switch (type) + { + case e_void: + case e_sint: + case e_uint: break; + case e_slong: success = true; break; + case e_ulong: m_data.ulong = m_data.slong; success = true; break; + case e_slonglong: m_data.slonglong = m_data.slong; success = true; break; + case e_ulonglong: m_data.ulonglong = m_data.slong; success = true; break; + case e_float: m_data.flt = m_data.slong; success = true; break; + case e_double: m_data.dbl = m_data.slong; success = true; break; + case e_long_double: m_data.ldbl = m_data.slong; success = true; break; + } + break; + + case e_ulong: + switch (type) + { + case e_void: + case e_sint: + case e_uint: + case e_slong: break; + case e_ulong: success = true; break; + case e_slonglong: m_data.slonglong = m_data.ulong; success = true; break; + case e_ulonglong: m_data.ulonglong = m_data.ulong; success = true; break; + case e_float: m_data.flt = m_data.ulong; success = true; break; + case e_double: m_data.dbl = m_data.ulong; success = true; break; + case e_long_double: m_data.ldbl = m_data.ulong; success = true; break; + } + break; + + case e_slonglong: + switch (type) + { + case e_void: + case e_sint: + case e_uint: + case e_slong: + case e_ulong: break; + case e_slonglong: success = true; break; + case e_ulonglong: m_data.ulonglong = m_data.slonglong; success = true; break; + case e_float: m_data.flt = m_data.slonglong; success = true; break; + case e_double: m_data.dbl = m_data.slonglong; success = true; break; + case e_long_double: m_data.ldbl = m_data.slonglong; success = true; break; + } + break; + + case e_ulonglong: + switch (type) + { + case e_void: + case e_sint: + case e_uint: + case e_slong: + case e_ulong: + case e_slonglong: break; + case e_ulonglong: success = true; break; + case e_float: m_data.flt = m_data.ulonglong; success = true; break; + case e_double: m_data.dbl = m_data.ulonglong; success = true; break; + case e_long_double: m_data.ldbl = m_data.ulonglong; success = true; break; + } + break; + + case e_float: + switch (type) + { + case e_void: + case e_sint: + case e_uint: + case e_slong: + case e_ulong: + case e_slonglong: + case e_ulonglong: break; + case e_float: success = true; break; + case e_double: m_data.dbl = m_data.flt; success = true; break; + case e_long_double: m_data.ldbl = m_data.ulonglong; success = true; break; + } + break; + + case e_double: + switch (type) + { + case e_void: + case e_sint: + case e_uint: + case e_slong: + case e_ulong: + case e_slonglong: + case e_ulonglong: + case e_float: break; + case e_double: success = true; break; + case e_long_double: m_data.ldbl = m_data.dbl; success = true; break; + } + break; + + case e_long_double: + switch (type) + { + case e_void: + case e_sint: + case e_uint: + case e_slong: + case e_ulong: + case e_slonglong: + case e_ulonglong: + case e_float: + case e_double: break; + case e_long_double: success = true; break; + } + break; + } + + if (success) + m_type = type; + return success; +} + +const char * +Scalar::GetValueTypeAsCString (Scalar::Type type) +{ + switch (type) + { + case e_void: return "void"; + case e_sint: return "int"; + case e_uint: return "unsigned int"; + case e_slong: return "long"; + case e_ulong: return "unsigned long"; + case e_slonglong: return "long long"; + case e_ulonglong: return "unsigned long long"; + case e_float: return "float"; + case e_double: return "double"; + case e_long_double: return "long double"; + } + return "???"; +} + + +Scalar::Type +Scalar::GetValueTypeForSignedIntegerWithByteSize (size_t byte_size) +{ + if (byte_size <= sizeof(sint_t)) + return e_sint; + if (byte_size <= sizeof(slong_t)) + return e_slong; + if (byte_size <= sizeof(slonglong_t)) + return e_slonglong; + return e_void; +} + +Scalar::Type +Scalar::GetValueTypeForUnsignedIntegerWithByteSize (size_t byte_size) +{ + if (byte_size <= sizeof(uint_t)) + return e_uint; + if (byte_size <= sizeof(ulong_t)) + return e_ulong; + if (byte_size <= sizeof(ulonglong_t)) + return e_ulonglong; + return e_void; +} + +Scalar::Type +Scalar::GetValueTypeForFloatWithByteSize (size_t byte_size) +{ + if (byte_size == sizeof(float_t)) + return e_float; + if (byte_size == sizeof(double_t)) + return e_double; + if (byte_size == sizeof(long_double_t)) + return e_long_double; + return e_void; +} + +bool +Scalar::Cast(Scalar::Type type) +{ + bool success = false; + switch (m_type) + { + case e_void: + break; + + case e_sint: + switch (type) + { + case e_void: break; + case e_sint: success = true; break; + case e_uint: m_data.uint = m_data.sint; success = true; break; + case e_slong: m_data.slong = m_data.sint; success = true; break; + case e_ulong: m_data.ulong = m_data.sint; success = true; break; + case e_slonglong: m_data.slonglong = m_data.sint; success = true; break; + case e_ulonglong: m_data.ulonglong = m_data.sint; success = true; break; + case e_float: m_data.flt = m_data.sint; success = true; break; + case e_double: m_data.dbl = m_data.sint; success = true; break; + case e_long_double: m_data.ldbl = m_data.sint; success = true; break; + } + break; + + case e_uint: + switch (type) + { + case e_void: + case e_sint: m_data.sint = m_data.uint; success = true; break; + case e_uint: success = true; break; + case e_slong: m_data.slong = m_data.uint; success = true; break; + case e_ulong: m_data.ulong = m_data.uint; success = true; break; + case e_slonglong: m_data.slonglong = m_data.uint; success = true; break; + case e_ulonglong: m_data.ulonglong = m_data.uint; success = true; break; + case e_float: m_data.flt = m_data.uint; success = true; break; + case e_double: m_data.dbl = m_data.uint; success = true; break; + case e_long_double: m_data.ldbl = m_data.uint; success = true; break; + } + break; + + case e_slong: + switch (type) + { + case e_void: + case e_sint: m_data.sint = (sint_t)m_data.slong; success = true; break; + case e_uint: m_data.uint = (uint_t)m_data.slong; success = true; break; + case e_slong: success = true; break; + case e_ulong: m_data.ulong = m_data.slong; success = true; break; + case e_slonglong: m_data.slonglong = m_data.slong; success = true; break; + case e_ulonglong: m_data.ulonglong = m_data.slong; success = true; break; + case e_float: m_data.flt = m_data.slong; success = true; break; + case e_double: m_data.dbl = m_data.slong; success = true; break; + case e_long_double: m_data.ldbl = m_data.slong; success = true; break; + } + break; + + case e_ulong: + switch (type) + { + case e_void: + case e_sint: m_data.sint = (sint_t)m_data.ulong; success = true; break; + case e_uint: m_data.uint = (uint_t)m_data.ulong; success = true; break; + case e_slong: m_data.slong = m_data.ulong; success = true; break; + case e_ulong: success = true; break; + case e_slonglong: m_data.slonglong = m_data.ulong; success = true; break; + case e_ulonglong: m_data.ulonglong = m_data.ulong; success = true; break; + case e_float: m_data.flt = m_data.ulong; success = true; break; + case e_double: m_data.dbl = m_data.ulong; success = true; break; + case e_long_double: m_data.ldbl = m_data.ulong; success = true; break; + } + break; + + case e_slonglong: + switch (type) + { + case e_void: + case e_sint: m_data.sint = (sint_t)m_data.slonglong; success = true; break; + case e_uint: m_data.uint = (uint_t)m_data.slonglong; success = true; break; + case e_slong: m_data.slong = m_data.slonglong; success = true; break; + case e_ulong: m_data.ulong = m_data.slonglong; success = true; break; + case e_slonglong: success = true; break; + case e_ulonglong: m_data.ulonglong = m_data.slonglong; success = true; break; + case e_float: m_data.flt = m_data.slonglong; success = true; break; + case e_double: m_data.dbl = m_data.slonglong; success = true; break; + case e_long_double: m_data.ldbl = m_data.slonglong; success = true; break; + } + break; + + case e_ulonglong: + switch (type) + { + case e_void: + case e_sint: m_data.sint = (sint_t)m_data.ulonglong; success = true; break; + case e_uint: m_data.uint = (uint_t)m_data.ulonglong; success = true; break; + case e_slong: m_data.slong = m_data.ulonglong; success = true; break; + case e_ulong: m_data.ulong = m_data.ulonglong; success = true; break; + case e_slonglong: m_data.slonglong = m_data.ulonglong; success = true; break; + case e_ulonglong: success = true; break; + case e_float: m_data.flt = m_data.ulonglong; success = true; break; + case e_double: m_data.dbl = m_data.ulonglong; success = true; break; + case e_long_double: m_data.ldbl = m_data.ulonglong; success = true; break; + } + break; + + case e_float: + switch (type) + { + case e_void: + case e_sint: m_data.sint = (sint_t)m_data.flt; success = true; break; + case e_uint: m_data.uint = (uint_t)m_data.flt; success = true; break; + case e_slong: m_data.slong = (slong_t)m_data.flt; success = true; break; + case e_ulong: m_data.ulong = (ulong_t)m_data.flt; success = true; break; + case e_slonglong: m_data.slonglong = (slonglong_t)m_data.flt; success = true; break; + case e_ulonglong: m_data.ulonglong = (ulonglong_t)m_data.flt; success = true; break; + case e_float: success = true; break; + case e_double: m_data.dbl = m_data.flt; success = true; break; + case e_long_double: m_data.ldbl = m_data.flt; success = true; break; + } + break; + + case e_double: + switch (type) + { + case e_void: + case e_sint: m_data.sint = (sint_t)m_data.dbl; success = true; break; + case e_uint: m_data.uint = (uint_t)m_data.dbl; success = true; break; + case e_slong: m_data.slong = (slong_t)m_data.dbl; success = true; break; + case e_ulong: m_data.ulong = (ulong_t)m_data.dbl; success = true; break; + case e_slonglong: m_data.slonglong = (slonglong_t)m_data.dbl; success = true; break; + case e_ulonglong: m_data.ulonglong = (ulonglong_t)m_data.dbl; success = true; break; + case e_float: m_data.flt = (float_t)m_data.dbl; success = true; break; + case e_double: success = true; break; + case e_long_double: m_data.ldbl = m_data.dbl; success = true; break; + } + break; + + case e_long_double: + switch (type) + { + case e_void: + case e_sint: m_data.sint = (sint_t)m_data.ldbl; success = true; break; + case e_uint: m_data.uint = (uint_t)m_data.ldbl; success = true; break; + case e_slong: m_data.slong = (slong_t)m_data.ldbl; success = true; break; + case e_ulong: m_data.ulong = (ulong_t)m_data.ldbl; success = true; break; + case e_slonglong: m_data.slonglong = (slonglong_t)m_data.ldbl; success = true; break; + case e_ulonglong: m_data.ulonglong = (ulonglong_t)m_data.ldbl; success = true; break; + case e_float: m_data.flt = (float_t)m_data.ldbl; success = true; break; + case e_double: m_data.dbl = (double_t)m_data.ldbl; success = true; break; + case e_long_double: success = true; break; + } + break; + } + + if (success) + m_type = type; + return success; +} + +bool +Scalar::MakeSigned () +{ + bool success = false; + + switch (m_type) + { + case e_void: break; + case e_sint: success = true; break; + case e_uint: m_type = e_sint; success = true; break; + case e_slong: success = true; break; + case e_ulong: m_type = e_slong; success = true; break; + case e_slonglong: success = true; break; + case e_ulonglong: m_type = e_slonglong; success = true; break; + case e_float: success = true; break; + case e_double: success = true; break; + case e_long_double: success = true; break; + } + + return success; +} + +int +Scalar::SInt(int fail_value) const +{ + switch (m_type) + { + case e_void: break; + case e_sint: return m_data.sint; + case e_uint: return (int)m_data.uint; + case e_slong: return (int)m_data.slong; + case e_ulong: return (int)m_data.ulong; + case e_slonglong: return (int)m_data.slonglong; + case e_ulonglong: return (int)m_data.ulonglong; + case e_float: return (int)m_data.flt; + case e_double: return (int)m_data.dbl; + case e_long_double: return (int)m_data.ldbl; + } + return fail_value; +} + +unsigned int +Scalar::UInt(unsigned int fail_value) const +{ + switch (m_type) + { + case e_void: break; + case e_sint: return (unsigned int)m_data.sint; + case e_uint: return (unsigned int)m_data.uint; + case e_slong: return (unsigned int)m_data.slong; + case e_ulong: return (unsigned int)m_data.ulong; + case e_slonglong: return (unsigned int)m_data.slonglong; + case e_ulonglong: return (unsigned int)m_data.ulonglong; + case e_float: return (unsigned int)m_data.flt; + case e_double: return (unsigned int)m_data.dbl; + case e_long_double: return (unsigned int)m_data.ldbl; + } + return fail_value; +} + + +long +Scalar::SLong(long fail_value) const +{ + switch (m_type) + { + case e_void: break; + case e_sint: return (long)m_data.sint; + case e_uint: return (long)m_data.uint; + case e_slong: return (long)m_data.slong; + case e_ulong: return (long)m_data.ulong; + case e_slonglong: return (long)m_data.slonglong; + case e_ulonglong: return (long)m_data.ulonglong; + case e_float: return (long)m_data.flt; + case e_double: return (long)m_data.dbl; + case e_long_double: return (long)m_data.ldbl; + } + return fail_value; +} + + + +unsigned long +Scalar::ULong(unsigned long fail_value) const +{ + switch (m_type) + { + case e_void: break; + case e_sint: return (unsigned long)m_data.sint; + case e_uint: return (unsigned long)m_data.uint; + case e_slong: return (unsigned long)m_data.slong; + case e_ulong: return (unsigned long)m_data.ulong; + case e_slonglong: return (unsigned long)m_data.slonglong; + case e_ulonglong: return (unsigned long)m_data.ulonglong; + case e_float: return (unsigned long)m_data.flt; + case e_double: return (unsigned long)m_data.dbl; + case e_long_double: return (unsigned long)m_data.ldbl; + } + return fail_value; +} + +uint64_t +Scalar::GetRawBits64(uint64_t fail_value) const +{ + switch (m_type) + { + case e_void: + break; + + case e_sint: + case e_uint: + return m_data.uint; + + case e_slong: + case e_ulong: + return m_data.ulong; + + case e_slonglong: + case e_ulonglong: + return m_data.ulonglong; + + case e_float: + if (sizeof(m_data.flt) == sizeof(m_data.uint)) + return m_data.uint; + else if (sizeof(m_data.flt) == sizeof(m_data.ulong)) + return m_data.ulong; + else if (sizeof(m_data.flt) == sizeof(m_data.ulonglong)) + return m_data.ulonglong; + break; + + case e_double: + if (sizeof(m_data.dbl) == sizeof(m_data.uint)) + return m_data.uint; + else if (sizeof(m_data.dbl) == sizeof(m_data.ulong)) + return m_data.ulong; + else if (sizeof(m_data.dbl) == sizeof(m_data.ulonglong)) + return m_data.ulonglong; + break; + + case e_long_double: + if (sizeof(m_data.ldbl) == sizeof(m_data.uint)) + return m_data.uint; + else if (sizeof(m_data.ldbl) == sizeof(m_data.ulong)) + return m_data.ulong; + else if (sizeof(m_data.ldbl) == sizeof(m_data.ulonglong)) + return m_data.ulonglong; + break; + } + return fail_value; +} + + + +long long +Scalar::SLongLong(long long fail_value) const +{ + switch (m_type) + { + case e_void: break; + case e_sint: return (long long)m_data.sint; + case e_uint: return (long long)m_data.uint; + case e_slong: return (long long)m_data.slong; + case e_ulong: return (long long)m_data.ulong; + case e_slonglong: return (long long)m_data.slonglong; + case e_ulonglong: return (long long)m_data.ulonglong; + case e_float: return (long long)m_data.flt; + case e_double: return (long long)m_data.dbl; + case e_long_double: return (long long)m_data.ldbl; + } + return fail_value; +} + + +unsigned long long +Scalar::ULongLong(unsigned long long fail_value) const +{ + switch (m_type) + { + case e_void: break; + case e_sint: return (unsigned long long)m_data.sint; + case e_uint: return (unsigned long long)m_data.uint; + case e_slong: return (unsigned long long)m_data.slong; + case e_ulong: return (unsigned long long)m_data.ulong; + case e_slonglong: return (unsigned long long)m_data.slonglong; + case e_ulonglong: return (unsigned long long)m_data.ulonglong; + case e_float: return (unsigned long long)m_data.flt; + case e_double: return (unsigned long long)m_data.dbl; + case e_long_double: return (unsigned long long)m_data.ldbl; + } + return fail_value; +} + + +float +Scalar::Float(float fail_value) const +{ + switch (m_type) + { + case e_void: break; + case e_sint: return (float)m_data.sint; + case e_uint: return (float)m_data.uint; + case e_slong: return (float)m_data.slong; + case e_ulong: return (float)m_data.ulong; + case e_slonglong: return (float)m_data.slonglong; + case e_ulonglong: return (float)m_data.ulonglong; + case e_float: return (float)m_data.flt; + case e_double: return (float)m_data.dbl; + case e_long_double: return (float)m_data.ldbl; + } + return fail_value; +} + + +double +Scalar::Double(double fail_value) const +{ + switch (m_type) + { + case e_void: break; + case e_sint: return (double)m_data.sint; + case e_uint: return (double)m_data.uint; + case e_slong: return (double)m_data.slong; + case e_ulong: return (double)m_data.ulong; + case e_slonglong: return (double)m_data.slonglong; + case e_ulonglong: return (double)m_data.ulonglong; + case e_float: return (double)m_data.flt; + case e_double: return (double)m_data.dbl; + case e_long_double: return (double)m_data.ldbl; + } + return fail_value; +} + + +long double +Scalar::LongDouble(long double fail_value) const +{ + switch (m_type) + { + case e_void: break; + case e_sint: return (long double)m_data.sint; + case e_uint: return (long double)m_data.uint; + case e_slong: return (long double)m_data.slong; + case e_ulong: return (long double)m_data.ulong; + case e_slonglong: return (long double)m_data.slonglong; + case e_ulonglong: return (long double)m_data.ulonglong; + case e_float: return (long double)m_data.flt; + case e_double: return (long double)m_data.dbl; + case e_long_double: return (long double)m_data.ldbl; + } + return fail_value; +} + + +Scalar& +Scalar::operator+= (const Scalar& rhs) +{ + Scalar temp_value; + const Scalar* a; + const Scalar* b; + if ((m_type = PromoteToMaxType(*this, rhs, temp_value, a, b)) != Scalar::e_void) + { + switch (m_type) + { + case e_void: break; + case e_sint: m_data.sint = a->m_data.sint + b->m_data.sint; break; + case e_uint: m_data.uint = a->m_data.uint + b->m_data.uint; break; + case e_slong: m_data.slong = a->m_data.slong + b->m_data.slong; break; + case e_ulong: m_data.ulong = a->m_data.ulong + b->m_data.ulong; break; + case e_slonglong: m_data.slonglong = a->m_data.slonglong + b->m_data.slonglong; break; + case e_ulonglong: m_data.ulonglong = a->m_data.ulonglong + b->m_data.ulonglong; break; + case e_float: m_data.flt = a->m_data.flt + b->m_data.flt; break; + case e_double: m_data.dbl = a->m_data.dbl + b->m_data.dbl; break; + case e_long_double: m_data.ldbl = a->m_data.ldbl + b->m_data.ldbl; break; + } + } + return *this; +} + +Scalar& +Scalar::operator<<= (const Scalar& rhs) +{ + switch (m_type) + { + case e_void: + case e_float: + case e_double: + case e_long_double: + m_type = e_void; + break; + + case e_sint: + switch (rhs.m_type) + { + case e_void: + case e_float: + case e_double: + case e_long_double: + m_type = e_void; + break; + case e_sint: m_data.sint <<= rhs.m_data.sint; break; + case e_uint: m_data.sint <<= rhs.m_data.uint; break; + case e_slong: m_data.sint <<= rhs.m_data.slong; break; + case e_ulong: m_data.sint <<= rhs.m_data.ulong; break; + case e_slonglong: m_data.sint <<= rhs.m_data.slonglong; break; + case e_ulonglong: m_data.sint <<= rhs.m_data.ulonglong; break; + } + break; + + case e_uint: + switch (rhs.m_type) + { + case e_void: + case e_float: + case e_double: + case e_long_double: + m_type = e_void; + break; + case e_sint: m_data.uint <<= rhs.m_data.sint; break; + case e_uint: m_data.uint <<= rhs.m_data.uint; break; + case e_slong: m_data.uint <<= rhs.m_data.slong; break; + case e_ulong: m_data.uint <<= rhs.m_data.ulong; break; + case e_slonglong: m_data.uint <<= rhs.m_data.slonglong; break; + case e_ulonglong: m_data.uint <<= rhs.m_data.ulonglong; break; + } + break; + + case e_slong: + switch (rhs.m_type) + { + case e_void: + case e_float: + case e_double: + case e_long_double: + m_type = e_void; + break; + case e_sint: m_data.slong <<= rhs.m_data.sint; break; + case e_uint: m_data.slong <<= rhs.m_data.uint; break; + case e_slong: m_data.slong <<= rhs.m_data.slong; break; + case e_ulong: m_data.slong <<= rhs.m_data.ulong; break; + case e_slonglong: m_data.slong <<= rhs.m_data.slonglong; break; + case e_ulonglong: m_data.slong <<= rhs.m_data.ulonglong; break; + } + break; + + case e_ulong: + switch (rhs.m_type) + { + case e_void: + case e_float: + case e_double: + case e_long_double: + m_type = e_void; + break; + case e_sint: m_data.ulong <<= rhs.m_data.sint; break; + case e_uint: m_data.ulong <<= rhs.m_data.uint; break; + case e_slong: m_data.ulong <<= rhs.m_data.slong; break; + case e_ulong: m_data.ulong <<= rhs.m_data.ulong; break; + case e_slonglong: m_data.ulong <<= rhs.m_data.slonglong; break; + case e_ulonglong: m_data.ulong <<= rhs.m_data.ulonglong; break; + } + break; + case e_slonglong: + switch (rhs.m_type) + { + case e_void: + case e_float: + case e_double: + case e_long_double: + m_type = e_void; + break; + case e_sint: m_data.slonglong <<= rhs.m_data.sint; break; + case e_uint: m_data.slonglong <<= rhs.m_data.uint; break; + case e_slong: m_data.slonglong <<= rhs.m_data.slong; break; + case e_ulong: m_data.slonglong <<= rhs.m_data.ulong; break; + case e_slonglong: m_data.slonglong <<= rhs.m_data.slonglong; break; + case e_ulonglong: m_data.slonglong <<= rhs.m_data.ulonglong; break; + } + break; + + case e_ulonglong: + switch (rhs.m_type) + { + case e_void: + case e_float: + case e_double: + case e_long_double: + m_type = e_void; + break; + case e_sint: m_data.ulonglong <<= rhs.m_data.sint; break; + case e_uint: m_data.ulonglong <<= rhs.m_data.uint; break; + case e_slong: m_data.ulonglong <<= rhs.m_data.slong; break; + case e_ulong: m_data.ulonglong <<= rhs.m_data.ulong; break; + case e_slonglong: m_data.ulonglong <<= rhs.m_data.slonglong; break; + case e_ulonglong: m_data.ulonglong <<= rhs.m_data.ulonglong; break; + } + break; + } + return *this; +} + +bool +Scalar::ShiftRightLogical(const Scalar& rhs) +{ + switch (m_type) + { + case e_void: + case e_float: + case e_double: + case e_long_double: + m_type = e_void; + break; + + case e_sint: + case e_uint: + switch (rhs.m_type) + { + case e_void: + case e_float: + case e_double: + case e_long_double: + m_type = e_void; + break; + case e_sint: m_data.uint >>= rhs.m_data.sint; break; + case e_uint: m_data.uint >>= rhs.m_data.uint; break; + case e_slong: m_data.uint >>= rhs.m_data.slong; break; + case e_ulong: m_data.uint >>= rhs.m_data.ulong; break; + case e_slonglong: m_data.uint >>= rhs.m_data.slonglong; break; + case e_ulonglong: m_data.uint >>= rhs.m_data.ulonglong; break; + } + break; + + case e_slong: + case e_ulong: + switch (rhs.m_type) + { + case e_void: + case e_float: + case e_double: + case e_long_double: + m_type = e_void; + break; + case e_sint: m_data.ulong >>= rhs.m_data.sint; break; + case e_uint: m_data.ulong >>= rhs.m_data.uint; break; + case e_slong: m_data.ulong >>= rhs.m_data.slong; break; + case e_ulong: m_data.ulong >>= rhs.m_data.ulong; break; + case e_slonglong: m_data.ulong >>= rhs.m_data.slonglong; break; + case e_ulonglong: m_data.ulong >>= rhs.m_data.ulonglong; break; + } + break; + + case e_slonglong: + case e_ulonglong: + switch (rhs.m_type) + { + case e_void: + case e_float: + case e_double: + case e_long_double: + m_type = e_void; + break; + case e_sint: m_data.ulonglong >>= rhs.m_data.sint; break; + case e_uint: m_data.ulonglong >>= rhs.m_data.uint; break; + case e_slong: m_data.ulonglong >>= rhs.m_data.slong; break; + case e_ulong: m_data.ulonglong >>= rhs.m_data.ulong; break; + case e_slonglong: m_data.ulonglong >>= rhs.m_data.slonglong; break; + case e_ulonglong: m_data.ulonglong >>= rhs.m_data.ulonglong; break; + } + break; + } + return m_type != e_void; +} + + +Scalar& +Scalar::operator>>= (const Scalar& rhs) +{ + switch (m_type) + { + case e_void: + case e_float: + case e_double: + case e_long_double: + m_type = e_void; + break; + + case e_sint: + switch (rhs.m_type) + { + case e_void: + case e_float: + case e_double: + case e_long_double: + m_type = e_void; + break; + case e_sint: m_data.sint >>= rhs.m_data.sint; break; + case e_uint: m_data.sint >>= rhs.m_data.uint; break; + case e_slong: m_data.sint >>= rhs.m_data.slong; break; + case e_ulong: m_data.sint >>= rhs.m_data.ulong; break; + case e_slonglong: m_data.sint >>= rhs.m_data.slonglong; break; + case e_ulonglong: m_data.sint >>= rhs.m_data.ulonglong; break; + } + break; + + case e_uint: + switch (rhs.m_type) + { + case e_void: + case e_float: + case e_double: + case e_long_double: + m_type = e_void; + break; + case e_sint: m_data.uint >>= rhs.m_data.sint; break; + case e_uint: m_data.uint >>= rhs.m_data.uint; break; + case e_slong: m_data.uint >>= rhs.m_data.slong; break; + case e_ulong: m_data.uint >>= rhs.m_data.ulong; break; + case e_slonglong: m_data.uint >>= rhs.m_data.slonglong; break; + case e_ulonglong: m_data.uint >>= rhs.m_data.ulonglong; break; + } + break; + + case e_slong: + switch (rhs.m_type) + { + case e_void: + case e_float: + case e_double: + case e_long_double: + m_type = e_void; + break; + case e_sint: m_data.slong >>= rhs.m_data.sint; break; + case e_uint: m_data.slong >>= rhs.m_data.uint; break; + case e_slong: m_data.slong >>= rhs.m_data.slong; break; + case e_ulong: m_data.slong >>= rhs.m_data.ulong; break; + case e_slonglong: m_data.slong >>= rhs.m_data.slonglong; break; + case e_ulonglong: m_data.slong >>= rhs.m_data.ulonglong; break; + } + break; + + case e_ulong: + switch (rhs.m_type) + { + case e_void: + case e_float: + case e_double: + case e_long_double: + m_type = e_void; + break; + case e_sint: m_data.ulong >>= rhs.m_data.sint; break; + case e_uint: m_data.ulong >>= rhs.m_data.uint; break; + case e_slong: m_data.ulong >>= rhs.m_data.slong; break; + case e_ulong: m_data.ulong >>= rhs.m_data.ulong; break; + case e_slonglong: m_data.ulong >>= rhs.m_data.slonglong; break; + case e_ulonglong: m_data.ulong >>= rhs.m_data.ulonglong; break; + } + break; + case e_slonglong: + switch (rhs.m_type) + { + case e_void: + case e_float: + case e_double: + case e_long_double: + m_type = e_void; + break; + case e_sint: m_data.slonglong >>= rhs.m_data.sint; break; + case e_uint: m_data.slonglong >>= rhs.m_data.uint; break; + case e_slong: m_data.slonglong >>= rhs.m_data.slong; break; + case e_ulong: m_data.slonglong >>= rhs.m_data.ulong; break; + case e_slonglong: m_data.slonglong >>= rhs.m_data.slonglong; break; + case e_ulonglong: m_data.slonglong >>= rhs.m_data.ulonglong; break; + } + break; + + case e_ulonglong: + switch (rhs.m_type) + { + case e_void: + case e_float: + case e_double: + case e_long_double: + m_type = e_void; + break; + case e_sint: m_data.ulonglong >>= rhs.m_data.sint; break; + case e_uint: m_data.ulonglong >>= rhs.m_data.uint; break; + case e_slong: m_data.ulonglong >>= rhs.m_data.slong; break; + case e_ulong: m_data.ulonglong >>= rhs.m_data.ulong; break; + case e_slonglong: m_data.ulonglong >>= rhs.m_data.slonglong; break; + case e_ulonglong: m_data.ulonglong >>= rhs.m_data.ulonglong; break; + } + break; + } + return *this; +} + + +Scalar& +Scalar::operator&= (const Scalar& rhs) +{ + switch (m_type) + { + case e_void: + case e_float: + case e_double: + case e_long_double: + m_type = e_void; + break; + + case e_sint: + switch (rhs.m_type) + { + case e_void: + case e_float: + case e_double: + case e_long_double: + m_type = e_void; + break; + case e_sint: m_data.sint &= rhs.m_data.sint; break; + case e_uint: m_data.sint &= rhs.m_data.uint; break; + case e_slong: m_data.sint &= rhs.m_data.slong; break; + case e_ulong: m_data.sint &= rhs.m_data.ulong; break; + case e_slonglong: m_data.sint &= rhs.m_data.slonglong; break; + case e_ulonglong: m_data.sint &= rhs.m_data.ulonglong; break; + } + break; + + case e_uint: + switch (rhs.m_type) + { + case e_void: + case e_float: + case e_double: + case e_long_double: + m_type = e_void; + break; + case e_sint: m_data.uint &= rhs.m_data.sint; break; + case e_uint: m_data.uint &= rhs.m_data.uint; break; + case e_slong: m_data.uint &= rhs.m_data.slong; break; + case e_ulong: m_data.uint &= rhs.m_data.ulong; break; + case e_slonglong: m_data.uint &= rhs.m_data.slonglong; break; + case e_ulonglong: m_data.uint &= rhs.m_data.ulonglong; break; + } + break; + + case e_slong: + switch (rhs.m_type) + { + case e_void: + case e_float: + case e_double: + case e_long_double: + m_type = e_void; + break; + case e_sint: m_data.slong &= rhs.m_data.sint; break; + case e_uint: m_data.slong &= rhs.m_data.uint; break; + case e_slong: m_data.slong &= rhs.m_data.slong; break; + case e_ulong: m_data.slong &= rhs.m_data.ulong; break; + case e_slonglong: m_data.slong &= rhs.m_data.slonglong; break; + case e_ulonglong: m_data.slong &= rhs.m_data.ulonglong; break; + } + break; + + case e_ulong: + switch (rhs.m_type) + { + case e_void: + case e_float: + case e_double: + case e_long_double: + m_type = e_void; + break; + case e_sint: m_data.ulong &= rhs.m_data.sint; break; + case e_uint: m_data.ulong &= rhs.m_data.uint; break; + case e_slong: m_data.ulong &= rhs.m_data.slong; break; + case e_ulong: m_data.ulong &= rhs.m_data.ulong; break; + case e_slonglong: m_data.ulong &= rhs.m_data.slonglong; break; + case e_ulonglong: m_data.ulong &= rhs.m_data.ulonglong; break; + } + break; + case e_slonglong: + switch (rhs.m_type) + { + case e_void: + case e_float: + case e_double: + case e_long_double: + m_type = e_void; + break; + case e_sint: m_data.slonglong &= rhs.m_data.sint; break; + case e_uint: m_data.slonglong &= rhs.m_data.uint; break; + case e_slong: m_data.slonglong &= rhs.m_data.slong; break; + case e_ulong: m_data.slonglong &= rhs.m_data.ulong; break; + case e_slonglong: m_data.slonglong &= rhs.m_data.slonglong; break; + case e_ulonglong: m_data.slonglong &= rhs.m_data.ulonglong; break; + } + break; + + case e_ulonglong: + switch (rhs.m_type) + { + case e_void: + case e_float: + case e_double: + case e_long_double: + m_type = e_void; + break; + case e_sint: m_data.ulonglong &= rhs.m_data.sint; break; + case e_uint: m_data.ulonglong &= rhs.m_data.uint; break; + case e_slong: m_data.ulonglong &= rhs.m_data.slong; break; + case e_ulong: m_data.ulonglong &= rhs.m_data.ulong; break; + case e_slonglong: m_data.ulonglong &= rhs.m_data.slonglong; break; + case e_ulonglong: m_data.ulonglong &= rhs.m_data.ulonglong; break; + } + break; + } + return *this; +} + + + +bool +Scalar::AbsoluteValue() +{ + switch (m_type) + { + case e_void: + break; + + case e_sint: + if (m_data.sint < 0) + m_data.sint = -m_data.sint; + return true; + + case e_slong: + if (m_data.slong < 0) + m_data.slong = -m_data.slong; + return true; + + case e_slonglong: + if (m_data.slonglong < 0) + m_data.slonglong = -m_data.slonglong; + return true; + + case e_uint: + case e_ulong: + case e_ulonglong: return true; + case e_float: m_data.flt = fabsf(m_data.flt); return true; + case e_double: m_data.dbl = fabs(m_data.dbl); return true; + case e_long_double: m_data.ldbl = fabsl(m_data.ldbl); return true; + } + return false; +} + + +bool +Scalar::UnaryNegate() +{ + switch (m_type) + { + case e_void: break; + case e_sint: m_data.sint = -m_data.sint; return true; + case e_uint: m_data.uint = -m_data.uint; return true; + case e_slong: m_data.slong = -m_data.slong; return true; + case e_ulong: m_data.ulong = -m_data.ulong; return true; + case e_slonglong: m_data.slonglong = -m_data.slonglong; return true; + case e_ulonglong: m_data.ulonglong = -m_data.ulonglong; return true; + case e_float: m_data.flt = -m_data.flt; return true; + case e_double: m_data.dbl = -m_data.dbl; return true; + case e_long_double: m_data.ldbl = -m_data.ldbl; return true; + } + return false; +} + +bool +Scalar::OnesComplement() +{ + switch (m_type) + { + case e_sint: m_data.sint = ~m_data.sint; return true; + case e_uint: m_data.uint = ~m_data.uint; return true; + case e_slong: m_data.slong = ~m_data.slong; return true; + case e_ulong: m_data.ulong = ~m_data.ulong; return true; + case e_slonglong: m_data.slonglong = ~m_data.slonglong; return true; + case e_ulonglong: m_data.ulonglong = ~m_data.ulonglong; return true; + + case e_void: + case e_float: + case e_double: + case e_long_double: + break; + } + return false; +} + + +const Scalar +lldb_private::operator+ (const Scalar& lhs, const Scalar& rhs) +{ + Scalar result; + Scalar temp_value; + const Scalar* a; + const Scalar* b; + if ((result.m_type = PromoteToMaxType(lhs, rhs, temp_value, a, b)) != Scalar::e_void) + { + switch (result.m_type) + { + case Scalar::e_void: break; + case Scalar::e_sint: result.m_data.sint = a->m_data.sint + b->m_data.sint; break; + case Scalar::e_uint: result.m_data.uint = a->m_data.uint + b->m_data.uint; break; + case Scalar::e_slong: result.m_data.slong = a->m_data.slong + b->m_data.slong; break; + case Scalar::e_ulong: result.m_data.ulong = a->m_data.ulong + b->m_data.ulong; break; + case Scalar::e_slonglong: result.m_data.slonglong = a->m_data.slonglong + b->m_data.slonglong; break; + case Scalar::e_ulonglong: result.m_data.ulonglong = a->m_data.ulonglong + b->m_data.ulonglong; break; + case Scalar::e_float: result.m_data.flt = a->m_data.flt + b->m_data.flt; break; + case Scalar::e_double: result.m_data.dbl = a->m_data.dbl + b->m_data.dbl; break; + case Scalar::e_long_double: result.m_data.ldbl = a->m_data.ldbl + b->m_data.ldbl; break; + } + } + return result; +} + + +const Scalar +lldb_private::operator- (const Scalar& lhs, const Scalar& rhs) +{ + Scalar result; + Scalar temp_value; + const Scalar* a; + const Scalar* b; + if ((result.m_type = PromoteToMaxType(lhs, rhs, temp_value, a, b)) != Scalar::e_void) + { + switch (result.m_type) + { + case Scalar::e_void: break; + case Scalar::e_sint: result.m_data.sint = a->m_data.sint - b->m_data.sint; break; + case Scalar::e_uint: result.m_data.uint = a->m_data.uint - b->m_data.uint; break; + case Scalar::e_slong: result.m_data.slong = a->m_data.slong - b->m_data.slong; break; + case Scalar::e_ulong: result.m_data.ulong = a->m_data.ulong - b->m_data.ulong; break; + case Scalar::e_slonglong: result.m_data.slonglong = a->m_data.slonglong - b->m_data.slonglong; break; + case Scalar::e_ulonglong: result.m_data.ulonglong = a->m_data.ulonglong - b->m_data.ulonglong; break; + case Scalar::e_float: result.m_data.flt = a->m_data.flt - b->m_data.flt; break; + case Scalar::e_double: result.m_data.dbl = a->m_data.dbl - b->m_data.dbl; break; + case Scalar::e_long_double: result.m_data.ldbl = a->m_data.ldbl - b->m_data.ldbl; break; + } + } + return result; +} + +const Scalar +lldb_private::operator/ (const Scalar& lhs, const Scalar& rhs) +{ + Scalar result; + Scalar temp_value; + const Scalar* a; + const Scalar* b; + if ((result.m_type = PromoteToMaxType(lhs, rhs, temp_value, a, b)) != Scalar::e_void) + { + switch (result.m_type) + { + case Scalar::e_void: break; + + case Scalar::e_sint: if (b->m_data.sint != 0) { result.m_data.sint = a->m_data.sint/ b->m_data.sint; return result; } break; + case Scalar::e_uint: if (b->m_data.uint != 0) { result.m_data.uint = a->m_data.uint / b->m_data.uint; return result; } break; + case Scalar::e_slong: if (b->m_data.slong != 0) { result.m_data.slong = a->m_data.slong / b->m_data.slong; return result; } break; + case Scalar::e_ulong: if (b->m_data.ulong != 0) { result.m_data.ulong = a->m_data.ulong / b->m_data.ulong; return result; } break; + case Scalar::e_slonglong: if (b->m_data.slonglong != 0) { result.m_data.slonglong = a->m_data.slonglong / b->m_data.slonglong; return result; } break; + case Scalar::e_ulonglong: if (b->m_data.ulonglong != 0) { result.m_data.ulonglong = a->m_data.ulonglong / b->m_data.ulonglong; return result; } break; + case Scalar::e_float: if (b->m_data.flt != 0.0f) { result.m_data.flt = a->m_data.flt / b->m_data.flt; return result; } break; + case Scalar::e_double: if (b->m_data.dbl != 0.0) { result.m_data.dbl = a->m_data.dbl / b->m_data.dbl; return result; } break; + case Scalar::e_long_double: if (b->m_data.ldbl != 0.0) { result.m_data.ldbl = a->m_data.ldbl / b->m_data.ldbl; return result; } break; + } + } + // For division only, the only way it should make it here is if a promotion failed, + // or if we are trying to do a divide by zero. + result.m_type = Scalar::e_void; + return result; +} + +const Scalar +lldb_private::operator* (const Scalar& lhs, const Scalar& rhs) +{ + Scalar result; + Scalar temp_value; + const Scalar* a; + const Scalar* b; + if ((result.m_type = PromoteToMaxType(lhs, rhs, temp_value, a, b)) != Scalar::e_void) + { + switch (result.m_type) + { + case Scalar::e_void: break; + case Scalar::e_sint: result.m_data.sint = a->m_data.sint * b->m_data.sint; break; + case Scalar::e_uint: result.m_data.uint = a->m_data.uint * b->m_data.uint; break; + case Scalar::e_slong: result.m_data.slong = a->m_data.slong * b->m_data.slong; break; + case Scalar::e_ulong: result.m_data.ulong = a->m_data.ulong * b->m_data.ulong; break; + case Scalar::e_slonglong: result.m_data.slonglong = a->m_data.slonglong * b->m_data.slonglong; break; + case Scalar::e_ulonglong: result.m_data.ulonglong = a->m_data.ulonglong * b->m_data.ulonglong; break; + case Scalar::e_float: result.m_data.flt = a->m_data.flt * b->m_data.flt; break; + case Scalar::e_double: result.m_data.dbl = a->m_data.dbl * b->m_data.dbl; break; + case Scalar::e_long_double: result.m_data.ldbl = a->m_data.ldbl * b->m_data.ldbl; break; + } + } + return result; +} + +const Scalar +lldb_private::operator& (const Scalar& lhs, const Scalar& rhs) +{ + Scalar result; + Scalar temp_value; + const Scalar* a; + const Scalar* b; + if ((result.m_type = PromoteToMaxType(lhs, rhs, temp_value, a, b)) != Scalar::e_void) + { + switch (result.m_type) + { + case Scalar::e_sint: result.m_data.sint = a->m_data.sint & b->m_data.sint; break; + case Scalar::e_uint: result.m_data.uint = a->m_data.uint & b->m_data.uint; break; + case Scalar::e_slong: result.m_data.slong = a->m_data.slong & b->m_data.slong; break; + case Scalar::e_ulong: result.m_data.ulong = a->m_data.ulong & b->m_data.ulong; break; + case Scalar::e_slonglong: result.m_data.slonglong = a->m_data.slonglong & b->m_data.slonglong; break; + case Scalar::e_ulonglong: result.m_data.ulonglong = a->m_data.ulonglong & b->m_data.ulonglong; break; + + case Scalar::e_void: + case Scalar::e_float: + case Scalar::e_double: + case Scalar::e_long_double: + // No bitwise AND on floats, doubles of long doubles + result.m_type = Scalar::e_void; + break; + } + } + return result; +} + +const Scalar +lldb_private::operator| (const Scalar& lhs, const Scalar& rhs) +{ + Scalar result; + Scalar temp_value; + const Scalar* a; + const Scalar* b; + if ((result.m_type = PromoteToMaxType(lhs, rhs, temp_value, a, b)) != Scalar::e_void) + { + switch (result.m_type) + { + case Scalar::e_sint: result.m_data.sint = a->m_data.sint | b->m_data.sint; break; + case Scalar::e_uint: result.m_data.uint = a->m_data.uint | b->m_data.uint; break; + case Scalar::e_slong: result.m_data.slong = a->m_data.slong | b->m_data.slong; break; + case Scalar::e_ulong: result.m_data.ulong = a->m_data.ulong | b->m_data.ulong; break; + case Scalar::e_slonglong: result.m_data.slonglong = a->m_data.slonglong | b->m_data.slonglong; break; + case Scalar::e_ulonglong: result.m_data.ulonglong = a->m_data.ulonglong | b->m_data.ulonglong; break; + + case Scalar::e_void: + case Scalar::e_float: + case Scalar::e_double: + case Scalar::e_long_double: + // No bitwise AND on floats, doubles of long doubles + result.m_type = Scalar::e_void; + break; + } + } + return result; +} + +const Scalar +lldb_private::operator% (const Scalar& lhs, const Scalar& rhs) +{ + Scalar result; + Scalar temp_value; + const Scalar* a; + const Scalar* b; + if ((result.m_type = PromoteToMaxType(lhs, rhs, temp_value, a, b)) != Scalar::e_void) + { + switch (result.m_type) + { + default: break; + case Scalar::e_sint: if (b->m_data.sint != 0) { result.m_data.sint = a->m_data.sint % b->m_data.sint; return result; } break; + case Scalar::e_uint: if (b->m_data.uint != 0) { result.m_data.uint = a->m_data.uint % b->m_data.uint; return result; } break; + case Scalar::e_slong: if (b->m_data.slong != 0) { result.m_data.slong = a->m_data.slong % b->m_data.slong; return result; } break; + case Scalar::e_ulong: if (b->m_data.ulong != 0) { result.m_data.ulong = a->m_data.ulong % b->m_data.ulong; return result; } break; + case Scalar::e_slonglong: if (b->m_data.slonglong != 0) { result.m_data.slonglong = a->m_data.slonglong % b->m_data.slonglong; return result; } break; + case Scalar::e_ulonglong: if (b->m_data.ulonglong != 0) { result.m_data.ulonglong = a->m_data.ulonglong % b->m_data.ulonglong; return result; } break; + } + } + result.m_type = Scalar::e_void; + return result; +} + +const Scalar +lldb_private::operator^ (const Scalar& lhs, const Scalar& rhs) +{ + Scalar result; + Scalar temp_value; + const Scalar* a; + const Scalar* b; + if ((result.m_type = PromoteToMaxType(lhs, rhs, temp_value, a, b)) != Scalar::e_void) + { + switch (result.m_type) + { + case Scalar::e_sint: result.m_data.sint = a->m_data.sint ^ b->m_data.sint; break; + case Scalar::e_uint: result.m_data.uint = a->m_data.uint ^ b->m_data.uint; break; + case Scalar::e_slong: result.m_data.slong = a->m_data.slong ^ b->m_data.slong; break; + case Scalar::e_ulong: result.m_data.ulong = a->m_data.ulong ^ b->m_data.ulong; break; + case Scalar::e_slonglong: result.m_data.slonglong = a->m_data.slonglong ^ b->m_data.slonglong; break; + case Scalar::e_ulonglong: result.m_data.ulonglong = a->m_data.ulonglong ^ b->m_data.ulonglong; break; + + case Scalar::e_void: + case Scalar::e_float: + case Scalar::e_double: + case Scalar::e_long_double: + // No bitwise AND on floats, doubles of long doubles + result.m_type = Scalar::e_void; + break; + } + } + return result; +} + +const Scalar +lldb_private::operator<< (const Scalar& lhs, const Scalar &rhs) +{ + Scalar result = lhs; + result <<= rhs; + return result; +} + +const Scalar +lldb_private::operator>> (const Scalar& lhs, const Scalar &rhs) +{ + Scalar result = lhs; + result >>= rhs; + return result; +} + +// Return the raw unsigned integer without any casting or conversion +unsigned int +Scalar::RawUInt () const +{ + return m_data.uint; +} + +// Return the raw unsigned long without any casting or conversion +unsigned long +Scalar::RawULong () const +{ + return m_data.ulong; +} + +// Return the raw unsigned long long without any casting or conversion +unsigned long long +Scalar::RawULongLong () const +{ + return m_data.ulonglong; +} + + +Error +Scalar::SetValueFromCString (const char *value_str, Encoding encoding, size_t byte_size) +{ + Error error; + if (value_str == NULL || value_str[0] == '\0') + { + error.SetErrorString ("Invalid c-string value string."); + return error; + } + bool success = false; + switch (encoding) + { + case eEncodingInvalid: + error.SetErrorString ("Invalid encoding."); + break; + + case eEncodingUint: + if (byte_size <= sizeof (unsigned long long)) + { + uint64_t uval64 = Args::StringToUInt64(value_str, UINT64_MAX, 0, &success); + if (!success) + error.SetErrorStringWithFormat ("'%s' is not a valid unsigned integer string value", value_str); + else if (!UIntValueIsValidForSize (uval64, byte_size)) + error.SetErrorStringWithFormat ("value 0x%" PRIx64 " is too large to fit in a %zu byte unsigned integer value", uval64, byte_size); + else + { + m_type = Scalar::GetValueTypeForUnsignedIntegerWithByteSize (byte_size); + switch (m_type) + { + case e_uint: m_data.uint = (uint_t)uval64; break; + case e_ulong: m_data.ulong = (ulong_t)uval64; break; + case e_ulonglong: m_data.ulonglong = (ulonglong_t)uval64; break; + default: + error.SetErrorStringWithFormat ("unsupported unsigned integer byte size: %zu", byte_size); + break; + } + } + } + else + { + error.SetErrorStringWithFormat ("unsupported unsigned integer byte size: %zu", byte_size); + return error; + } + break; + + case eEncodingSint: + if (byte_size <= sizeof (long long)) + { + uint64_t sval64 = Args::StringToSInt64(value_str, INT64_MAX, 0, &success); + if (!success) + error.SetErrorStringWithFormat ("'%s' is not a valid signed integer string value", value_str); + else if (!SIntValueIsValidForSize (sval64, byte_size)) + error.SetErrorStringWithFormat ("value 0x%" PRIx64 " is too large to fit in a %zu byte signed integer value", sval64, byte_size); + else + { + m_type = Scalar::GetValueTypeForSignedIntegerWithByteSize (byte_size); + switch (m_type) + { + case e_sint: m_data.sint = (sint_t)sval64; break; + case e_slong: m_data.slong = (slong_t)sval64; break; + case e_slonglong: m_data.slonglong = (slonglong_t)sval64; break; + default: + error.SetErrorStringWithFormat ("unsupported signed integer byte size: %zu", byte_size); + break; + } + } + } + else + { + error.SetErrorStringWithFormat ("unsupported signed integer byte size: %zu", byte_size); + return error; + } + break; + + case eEncodingIEEE754: + if (byte_size == sizeof (float)) + { + if (::sscanf (value_str, "%f", &m_data.flt) == 1) + m_type = e_float; + else + error.SetErrorStringWithFormat ("'%s' is not a valid float string value", value_str); + } + else if (byte_size == sizeof (double)) + { + if (::sscanf (value_str, "%lf", &m_data.dbl) == 1) + m_type = e_double; + else + error.SetErrorStringWithFormat ("'%s' is not a valid float string value", value_str); + } + else if (byte_size == sizeof (long double)) + { + if (::sscanf (value_str, "%Lf", &m_data.ldbl) == 1) + m_type = e_long_double; + else + error.SetErrorStringWithFormat ("'%s' is not a valid float string value", value_str); + } + else + { + error.SetErrorStringWithFormat ("unsupported float byte size: %zu", byte_size); + return error; + } + break; + + case eEncodingVector: + error.SetErrorString ("vector encoding unsupported."); + break; + } + if (error.Fail()) + m_type = e_void; + + return error; +} + +Error +Scalar::SetValueFromData (DataExtractor &data, lldb::Encoding encoding, size_t byte_size) +{ + Error error; + + switch (encoding) + { + case lldb::eEncodingInvalid: + error.SetErrorString ("invalid encoding"); + break; + case lldb::eEncodingVector: + error.SetErrorString ("vector encoding unsupported"); + break; + case lldb::eEncodingUint: + { + lldb::offset_t offset; + + switch (byte_size) + { + case 1: operator=((uint8_t)data.GetU8(&offset)); break; + case 2: operator=((uint16_t)data.GetU16(&offset)); break; + case 4: operator=((uint32_t)data.GetU32(&offset)); break; + case 8: operator=((uint64_t)data.GetU64(&offset)); break; + default: + error.SetErrorStringWithFormat ("unsupported unsigned integer byte size: %zu", byte_size); + break; + } + } + break; + case lldb::eEncodingSint: + { + lldb::offset_t offset; + + switch (byte_size) + { + case 1: operator=((int8_t)data.GetU8(&offset)); break; + case 2: operator=((int16_t)data.GetU16(&offset)); break; + case 4: operator=((int32_t)data.GetU32(&offset)); break; + case 8: operator=((int64_t)data.GetU64(&offset)); break; + default: + error.SetErrorStringWithFormat ("unsupported signed integer byte size: %zu", byte_size); + break; + } + } + break; + case lldb::eEncodingIEEE754: + { + lldb::offset_t offset; + + if (byte_size == sizeof (float)) + operator=((float)data.GetFloat(&offset)); + else if (byte_size == sizeof (double)) + operator=((double)data.GetDouble(&offset)); + else if (byte_size == sizeof (long double)) + operator=((long double)data.GetLongDouble(&offset)); + else + error.SetErrorStringWithFormat ("unsupported float byte size: %zu", byte_size); + } + break; + } + + return error; +} + +bool +Scalar::SignExtend (uint32_t sign_bit_pos) +{ + const uint32_t max_bit_pos = GetByteSize() * 8; + + if (sign_bit_pos < max_bit_pos) + { + switch (m_type) + { + case Scalar::e_void: + case Scalar::e_float: + case Scalar::e_double: + case Scalar::e_long_double: + return false; + + case Scalar::e_sint: + case Scalar::e_uint: + if (max_bit_pos == sign_bit_pos) + return true; + else if (sign_bit_pos < (max_bit_pos-1)) + { + unsigned int sign_bit = 1u << sign_bit_pos; + if (m_data.uint & sign_bit) + { + const unsigned int mask = ~(sign_bit) + 1u; + m_data.uint |= mask; + } + return true; + } + break; + + case Scalar::e_slong: + case Scalar::e_ulong: + if (max_bit_pos == sign_bit_pos) + return true; + else if (sign_bit_pos < (max_bit_pos-1)) + { + unsigned long sign_bit = 1ul << sign_bit_pos; + if (m_data.ulong & sign_bit) + { + const unsigned long mask = ~(sign_bit) + 1ul; + m_data.ulong |= mask; + } + return true; + } + break; + + case Scalar::e_slonglong: + case Scalar::e_ulonglong: + if (max_bit_pos == sign_bit_pos) + return true; + else if (sign_bit_pos < (max_bit_pos-1)) + { + unsigned long long sign_bit = 1ull << sign_bit_pos; + if (m_data.ulonglong & sign_bit) + { + const unsigned long long mask = ~(sign_bit) + 1ull; + m_data.ulonglong |= mask; + } + return true; + } + break; + } + } + return false; +} + +size_t +Scalar::GetAsMemoryData (void *dst, + size_t dst_len, + lldb::ByteOrder dst_byte_order, + Error &error) const +{ + // Get a data extractor that points to the native scalar data + DataExtractor data; + if (!GetData(data)) + { + error.SetErrorString ("invalid scalar value"); + return 0; + } + + const size_t src_len = data.GetByteSize(); + + // Prepare a memory buffer that contains some or all of the register value + const size_t bytes_copied = data.CopyByteOrderedData (0, // src offset + src_len, // src length + dst, // dst buffer + dst_len, // dst length + dst_byte_order); // dst byte order + if (bytes_copied == 0) + error.SetErrorString ("failed to copy data"); + + return bytes_copied; +} + +bool +Scalar::ExtractBitfield (uint32_t bit_size, + uint32_t bit_offset) +{ + if (bit_size == 0) + return true; + + uint32_t msbit = bit_offset + bit_size - 1; + uint32_t lsbit = bit_offset; + switch (m_type) + { + case Scalar::e_void: + break; + + case e_float: + if (sizeof(m_data.flt) == sizeof(sint_t)) + m_data.sint = (sint_t)SignedBits (m_data.sint, msbit, lsbit); + else if (sizeof(m_data.flt) == sizeof(ulong_t)) + m_data.slong = (slong_t)SignedBits (m_data.slong, msbit, lsbit); + else if (sizeof(m_data.flt) == sizeof(ulonglong_t)) + m_data.slonglong = (slonglong_t)SignedBits (m_data.slonglong, msbit, lsbit); + else + return false; + return true; + + case e_double: + if (sizeof(m_data.dbl) == sizeof(sint_t)) + m_data.sint = SignedBits (m_data.sint, msbit, lsbit); + else if (sizeof(m_data.dbl) == sizeof(ulong_t)) + m_data.slong = SignedBits (m_data.slong, msbit, lsbit); + else if (sizeof(m_data.dbl) == sizeof(ulonglong_t)) + m_data.slonglong = SignedBits (m_data.slonglong, msbit, lsbit); + else + return false; + return true; + + case e_long_double: + if (sizeof(m_data.ldbl) == sizeof(sint_t)) + m_data.sint = SignedBits (m_data.sint, msbit, lsbit); + else if (sizeof(m_data.ldbl) == sizeof(ulong_t)) + m_data.slong = SignedBits (m_data.slong, msbit, lsbit); + else if (sizeof(m_data.ldbl) == sizeof(ulonglong_t)) + m_data.slonglong = SignedBits (m_data.slonglong, msbit, lsbit); + else + return false; + return true; + + case Scalar::e_sint: + m_data.sint = (sint_t)SignedBits (m_data.sint, msbit, lsbit); + return true; + + case Scalar::e_uint: + m_data.uint = (uint_t)UnsignedBits (m_data.uint, msbit, lsbit); + return true; + + case Scalar::e_slong: + m_data.slong = (slong_t)SignedBits (m_data.slong, msbit, lsbit); + return true; + + case Scalar::e_ulong: + m_data.ulong = (ulong_t)UnsignedBits (m_data.ulong, msbit, lsbit); + return true; + + case Scalar::e_slonglong: + m_data.slonglong = (slonglong_t)SignedBits (m_data.slonglong, msbit, lsbit); + return true; + + case Scalar::e_ulonglong: + m_data.ulonglong = (ulonglong_t)UnsignedBits (m_data.ulonglong, msbit, lsbit); + return true; + } + return false; +} + + + + + +bool +lldb_private::operator== (const Scalar& lhs, const Scalar& rhs) +{ + // If either entry is void then we can just compare the types + if (lhs.m_type == Scalar::e_void || rhs.m_type == Scalar::e_void) + return lhs.m_type == rhs.m_type; + + Scalar temp_value; + const Scalar* a; + const Scalar* b; + switch (PromoteToMaxType(lhs, rhs, temp_value, a, b)) + { + case Scalar::e_void: break; + case Scalar::e_sint: return a->m_data.sint == b->m_data.sint; + case Scalar::e_uint: return a->m_data.uint == b->m_data.uint; + case Scalar::e_slong: return a->m_data.slong == b->m_data.slong; + case Scalar::e_ulong: return a->m_data.ulong == b->m_data.ulong; + case Scalar::e_slonglong: return a->m_data.slonglong == b->m_data.slonglong; + case Scalar::e_ulonglong: return a->m_data.ulonglong == b->m_data.ulonglong; + case Scalar::e_float: return a->m_data.flt == b->m_data.flt; + case Scalar::e_double: return a->m_data.dbl == b->m_data.dbl; + case Scalar::e_long_double: return a->m_data.ldbl == b->m_data.ldbl; + } + return false; +} + +bool +lldb_private::operator!= (const Scalar& lhs, const Scalar& rhs) +{ + // If either entry is void then we can just compare the types + if (lhs.m_type == Scalar::e_void || rhs.m_type == Scalar::e_void) + return lhs.m_type != rhs.m_type; + + Scalar temp_value; // A temp value that might get a copy of either promoted value + const Scalar* a; + const Scalar* b; + switch (PromoteToMaxType(lhs, rhs, temp_value, a, b)) + { + case Scalar::e_void: break; + case Scalar::e_sint: return a->m_data.sint != b->m_data.sint; + case Scalar::e_uint: return a->m_data.uint != b->m_data.uint; + case Scalar::e_slong: return a->m_data.slong != b->m_data.slong; + case Scalar::e_ulong: return a->m_data.ulong != b->m_data.ulong; + case Scalar::e_slonglong: return a->m_data.slonglong != b->m_data.slonglong; + case Scalar::e_ulonglong: return a->m_data.ulonglong != b->m_data.ulonglong; + case Scalar::e_float: return a->m_data.flt != b->m_data.flt; + case Scalar::e_double: return a->m_data.dbl != b->m_data.dbl; + case Scalar::e_long_double: return a->m_data.ldbl != b->m_data.ldbl; + } + return true; +} + +bool +lldb_private::operator< (const Scalar& lhs, const Scalar& rhs) +{ + if (lhs.m_type == Scalar::e_void || rhs.m_type == Scalar::e_void) + return false; + + Scalar temp_value; + const Scalar* a; + const Scalar* b; + switch (PromoteToMaxType(lhs, rhs, temp_value, a, b)) + { + case Scalar::e_void: break; + case Scalar::e_sint: return a->m_data.sint < b->m_data.sint; + case Scalar::e_uint: return a->m_data.uint < b->m_data.uint; + case Scalar::e_slong: return a->m_data.slong < b->m_data.slong; + case Scalar::e_ulong: return a->m_data.ulong < b->m_data.ulong; + case Scalar::e_slonglong: return a->m_data.slonglong < b->m_data.slonglong; + case Scalar::e_ulonglong: return a->m_data.ulonglong < b->m_data.ulonglong; + case Scalar::e_float: return a->m_data.flt < b->m_data.flt; + case Scalar::e_double: return a->m_data.dbl < b->m_data.dbl; + case Scalar::e_long_double: return a->m_data.ldbl < b->m_data.ldbl; + } + return false; +} + +bool +lldb_private::operator<= (const Scalar& lhs, const Scalar& rhs) +{ + if (lhs.m_type == Scalar::e_void || rhs.m_type == Scalar::e_void) + return false; + + Scalar temp_value; + const Scalar* a; + const Scalar* b; + switch (PromoteToMaxType(lhs, rhs, temp_value, a, b)) + { + case Scalar::e_void: break; + case Scalar::e_sint: return a->m_data.sint <= b->m_data.sint; + case Scalar::e_uint: return a->m_data.uint <= b->m_data.uint; + case Scalar::e_slong: return a->m_data.slong <= b->m_data.slong; + case Scalar::e_ulong: return a->m_data.ulong <= b->m_data.ulong; + case Scalar::e_slonglong: return a->m_data.slonglong <= b->m_data.slonglong; + case Scalar::e_ulonglong: return a->m_data.ulonglong <= b->m_data.ulonglong; + case Scalar::e_float: return a->m_data.flt <= b->m_data.flt; + case Scalar::e_double: return a->m_data.dbl <= b->m_data.dbl; + case Scalar::e_long_double: return a->m_data.ldbl <= b->m_data.ldbl; + } + return false; +} + + +bool +lldb_private::operator> (const Scalar& lhs, const Scalar& rhs) +{ + if (lhs.m_type == Scalar::e_void || rhs.m_type == Scalar::e_void) + return false; + + Scalar temp_value; + const Scalar* a; + const Scalar* b; + switch (PromoteToMaxType(lhs, rhs, temp_value, a, b)) + { + case Scalar::e_void: break; + case Scalar::e_sint: return a->m_data.sint > b->m_data.sint; + case Scalar::e_uint: return a->m_data.uint > b->m_data.uint; + case Scalar::e_slong: return a->m_data.slong > b->m_data.slong; + case Scalar::e_ulong: return a->m_data.ulong > b->m_data.ulong; + case Scalar::e_slonglong: return a->m_data.slonglong > b->m_data.slonglong; + case Scalar::e_ulonglong: return a->m_data.ulonglong > b->m_data.ulonglong; + case Scalar::e_float: return a->m_data.flt > b->m_data.flt; + case Scalar::e_double: return a->m_data.dbl > b->m_data.dbl; + case Scalar::e_long_double: return a->m_data.ldbl > b->m_data.ldbl; + } + return false; +} + +bool +lldb_private::operator>= (const Scalar& lhs, const Scalar& rhs) +{ + if (lhs.m_type == Scalar::e_void || rhs.m_type == Scalar::e_void) + return false; + + Scalar temp_value; + const Scalar* a; + const Scalar* b; + switch (PromoteToMaxType(lhs, rhs, temp_value, a, b)) + { + case Scalar::e_void: break; + case Scalar::e_sint: return a->m_data.sint >= b->m_data.sint; + case Scalar::e_uint: return a->m_data.uint >= b->m_data.uint; + case Scalar::e_slong: return a->m_data.slong >= b->m_data.slong; + case Scalar::e_ulong: return a->m_data.ulong >= b->m_data.ulong; + case Scalar::e_slonglong: return a->m_data.slonglong >= b->m_data.slonglong; + case Scalar::e_ulonglong: return a->m_data.ulonglong >= b->m_data.ulonglong; + case Scalar::e_float: return a->m_data.flt >= b->m_data.flt; + case Scalar::e_double: return a->m_data.dbl >= b->m_data.dbl; + case Scalar::e_long_double: return a->m_data.ldbl >= b->m_data.ldbl; + } + return false; +} + + + + diff --git a/contrib/llvm/tools/lldb/source/Core/SearchFilter.cpp b/contrib/llvm/tools/lldb/source/Core/SearchFilter.cpp new file mode 100644 index 00000000000..54937c0afec --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Core/SearchFilter.cpp @@ -0,0 +1,816 @@ +//===-- SearchFilter.cpp ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes + +#include "lldb/lldb-private.h" +#include "lldb/Core/SearchFilter.h" +#include "lldb/Core/Module.h" +#include "lldb/Symbol/CompileUnit.h" +#include "lldb/Target/Target.h" + +using namespace lldb; +using namespace lldb_private; + +//---------------------------------------------------------------------- +// SearchFilter constructor +//---------------------------------------------------------------------- +Searcher::Searcher () +{ + +} + +Searcher::~Searcher () +{ + +} + +void +Searcher::GetDescription (Stream *s) +{ +} + +//---------------------------------------------------------------------- +// SearchFilter constructor +//---------------------------------------------------------------------- +SearchFilter::SearchFilter(const TargetSP &target_sp) : + m_target_sp (target_sp) +{ +} + +//---------------------------------------------------------------------- +// SearchFilter copy constructor +//---------------------------------------------------------------------- +SearchFilter::SearchFilter(const SearchFilter& rhs) : + m_target_sp (rhs.m_target_sp) +{ +} + +//---------------------------------------------------------------------- +// SearchFilter assignment operator +//---------------------------------------------------------------------- +const SearchFilter& +SearchFilter::operator=(const SearchFilter& rhs) +{ + m_target_sp = rhs.m_target_sp; + return *this; +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +SearchFilter::~SearchFilter() +{ +} + +bool +SearchFilter::ModulePasses (const FileSpec &spec) +{ + return true; +} + +bool +SearchFilter::ModulePasses (const ModuleSP &module_sp) +{ + return true; +} + +bool +SearchFilter::AddressPasses (Address &address) +{ + return true; +} + +bool +SearchFilter::CompUnitPasses (FileSpec &fileSpec) +{ + return true; +} + +bool +SearchFilter::CompUnitPasses (CompileUnit &compUnit) +{ + return true; +} + +uint32_t +SearchFilter::GetFilterRequiredItems() +{ + return (lldb::SymbolContextItem) 0; +} + +void +SearchFilter::GetDescription (Stream *s) +{ +} + +void +SearchFilter::Dump (Stream *s) const +{ + +} + +//---------------------------------------------------------------------- +// UTILITY Functions to help iterate down through the elements of the +// SymbolContext. +//---------------------------------------------------------------------- + +void +SearchFilter::Search (Searcher &searcher) +{ + SymbolContext empty_sc; + + if (!m_target_sp) + return; + empty_sc.target_sp = m_target_sp; + + if (searcher.GetDepth() == Searcher::eDepthTarget) + searcher.SearchCallback (*this, empty_sc, NULL, false); + else + DoModuleIteration(empty_sc, searcher); +} + +void +SearchFilter::SearchInModuleList (Searcher &searcher, ModuleList &modules) +{ + SymbolContext empty_sc; + + if (!m_target_sp) + return; + empty_sc.target_sp = m_target_sp; + + if (searcher.GetDepth() == Searcher::eDepthTarget) + searcher.SearchCallback (*this, empty_sc, NULL, false); + else + { + Mutex::Locker modules_locker(modules.GetMutex()); + const size_t numModules = modules.GetSize(); + + for (size_t i = 0; i < numModules; i++) + { + ModuleSP module_sp(modules.GetModuleAtIndexUnlocked(i)); + if (ModulePasses(module_sp)) + { + if (DoModuleIteration(module_sp, searcher) == Searcher::eCallbackReturnStop) + return; + } + } + } +} + + +Searcher::CallbackReturn +SearchFilter::DoModuleIteration (const lldb::ModuleSP& module_sp, Searcher &searcher) +{ + SymbolContext matchingContext (m_target_sp, module_sp); + return DoModuleIteration(matchingContext, searcher); +} + +Searcher::CallbackReturn +SearchFilter::DoModuleIteration (const SymbolContext &context, Searcher &searcher) +{ + if (searcher.GetDepth () >= Searcher::eDepthModule) + { + if (context.module_sp) + { + if (searcher.GetDepth () == Searcher::eDepthModule) + { + SymbolContext matchingContext(context.module_sp.get()); + searcher.SearchCallback (*this, matchingContext, NULL, false); + } + else + { + return DoCUIteration(context.module_sp, context, searcher); + } + } + else + { + const ModuleList &target_images = m_target_sp->GetImages(); + Mutex::Locker modules_locker(target_images.GetMutex()); + + size_t n_modules = target_images.GetSize(); + for (size_t i = 0; i < n_modules; i++) + { + // If this is the last level supplied, then call the callback directly, + // otherwise descend. + ModuleSP module_sp(target_images.GetModuleAtIndexUnlocked (i)); + if (!ModulePasses (module_sp)) + continue; + + if (searcher.GetDepth () == Searcher::eDepthModule) + { + SymbolContext matchingContext(m_target_sp, module_sp); + + Searcher::CallbackReturn shouldContinue = searcher.SearchCallback (*this, matchingContext, NULL, false); + if (shouldContinue == Searcher::eCallbackReturnStop + || shouldContinue == Searcher::eCallbackReturnPop) + return shouldContinue; + } + else + { + Searcher::CallbackReturn shouldContinue = DoCUIteration(module_sp, context, searcher); + if (shouldContinue == Searcher::eCallbackReturnStop) + return shouldContinue; + else if (shouldContinue == Searcher::eCallbackReturnPop) + continue; + } + } + } + } + return Searcher::eCallbackReturnContinue; +} + +Searcher::CallbackReturn +SearchFilter::DoCUIteration (const ModuleSP &module_sp, const SymbolContext &context, Searcher &searcher) +{ + Searcher::CallbackReturn shouldContinue; + if (context.comp_unit == NULL) + { + const size_t num_comp_units = module_sp->GetNumCompileUnits(); + for (size_t i = 0; i < num_comp_units; i++) + { + CompUnitSP cu_sp (module_sp->GetCompileUnitAtIndex (i)); + if (cu_sp) + { + if (!CompUnitPasses (*(cu_sp.get()))) + continue; + + if (searcher.GetDepth () == Searcher::eDepthCompUnit) + { + SymbolContext matchingContext(m_target_sp, module_sp, cu_sp.get()); + + shouldContinue = searcher.SearchCallback (*this, matchingContext, NULL, false); + + if (shouldContinue == Searcher::eCallbackReturnPop) + return Searcher::eCallbackReturnContinue; + else if (shouldContinue == Searcher::eCallbackReturnStop) + return shouldContinue; + } + else + { + // FIXME Descend to block. + } + } + } + } + else + { + if (CompUnitPasses(*context.comp_unit)) + { + SymbolContext matchingContext (m_target_sp, module_sp, context.comp_unit); + return searcher.SearchCallback (*this, matchingContext, NULL, false); + } + } + return Searcher::eCallbackReturnContinue; +} + +Searcher::CallbackReturn +SearchFilter::DoFunctionIteration (Function *function, const SymbolContext &context, Searcher &searcher) +{ + // FIXME: Implement... + return Searcher::eCallbackReturnContinue; +} + +//---------------------------------------------------------------------- +// SearchFilterForNonModuleSpecificSearches: +// Selects a shared library matching a given file spec, consulting the targets "black list". +//---------------------------------------------------------------------- + + bool + SearchFilterForNonModuleSpecificSearches::ModulePasses (const FileSpec &module_spec) + { + if (m_target_sp->ModuleIsExcludedForNonModuleSpecificSearches (module_spec)) + return false; + else + return true; + } + + bool + SearchFilterForNonModuleSpecificSearches::ModulePasses (const lldb::ModuleSP &module_sp) + { + if (!module_sp) + return true; + else if (m_target_sp->ModuleIsExcludedForNonModuleSpecificSearches (module_sp)) + return false; + else + return true; + } + +//---------------------------------------------------------------------- +// SearchFilterByModule: +// Selects a shared library matching a given file spec +//---------------------------------------------------------------------- + +//---------------------------------------------------------------------- +// SearchFilterByModule constructors +//---------------------------------------------------------------------- + +SearchFilterByModule::SearchFilterByModule (const lldb::TargetSP &target_sp, const FileSpec &module) : + SearchFilter (target_sp), + m_module_spec (module) +{ +} + + +//---------------------------------------------------------------------- +// SearchFilterByModule copy constructor +//---------------------------------------------------------------------- +SearchFilterByModule::SearchFilterByModule(const SearchFilterByModule& rhs) : + SearchFilter (rhs), + m_module_spec (rhs.m_module_spec) +{ +} + +//---------------------------------------------------------------------- +// SearchFilterByModule assignment operator +//---------------------------------------------------------------------- +const SearchFilterByModule& +SearchFilterByModule::operator=(const SearchFilterByModule& rhs) +{ + m_target_sp = rhs.m_target_sp; + m_module_spec = rhs.m_module_spec; + return *this; +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +SearchFilterByModule::~SearchFilterByModule() +{ +} + +bool +SearchFilterByModule::ModulePasses (const ModuleSP &module_sp) +{ + if (module_sp && FileSpec::Equal(module_sp->GetFileSpec(), m_module_spec, false)) + return true; + else + return false; +} + +bool +SearchFilterByModule::ModulePasses (const FileSpec &spec) +{ + // Do a full match only if "spec" has a directory + const bool full_match = spec.GetDirectory(); + return FileSpec::Equal(spec, m_module_spec, full_match); +} + +bool +SearchFilterByModule::AddressPasses (Address &address) +{ + // FIXME: Not yet implemented + return true; +} + + +bool +SearchFilterByModule::CompUnitPasses (FileSpec &fileSpec) +{ + return true; +} + +bool +SearchFilterByModule::CompUnitPasses (CompileUnit &compUnit) +{ + return true; +} + +void +SearchFilterByModule::Search (Searcher &searcher) +{ + if (!m_target_sp) + return; + + if (searcher.GetDepth() == Searcher::eDepthTarget) + { + SymbolContext empty_sc; + empty_sc.target_sp = m_target_sp; + searcher.SearchCallback (*this, empty_sc, NULL, false); + } + + // If the module file spec is a full path, then we can just find the one + // filespec that passes. Otherwise, we need to go through all modules and + // find the ones that match the file name. + + const ModuleList &target_modules = m_target_sp->GetImages(); + Mutex::Locker modules_locker (target_modules.GetMutex()); + + const size_t num_modules = target_modules.GetSize (); + for (size_t i = 0; i < num_modules; i++) + { + Module* module = target_modules.GetModulePointerAtIndexUnlocked(i); + const bool full_match = m_module_spec.GetDirectory(); + if (FileSpec::Equal (m_module_spec, module->GetFileSpec(), full_match)) + { + SymbolContext matchingContext(m_target_sp, module->shared_from_this()); + Searcher::CallbackReturn shouldContinue; + + shouldContinue = DoModuleIteration(matchingContext, searcher); + if (shouldContinue == Searcher::eCallbackReturnStop) + return; + } + } +} + +void +SearchFilterByModule::GetDescription (Stream *s) +{ + s->PutCString(", module = "); + if (s->GetVerbose()) + { + char buffer[2048]; + m_module_spec.GetPath(buffer, 2047); + s->PutCString(buffer); + } + else + { + s->PutCString(m_module_spec.GetFilename().AsCString("")); + } +} + +uint32_t +SearchFilterByModule::GetFilterRequiredItems() +{ + return eSymbolContextModule; +} + +void +SearchFilterByModule::Dump (Stream *s) const +{ + +} +//---------------------------------------------------------------------- +// SearchFilterByModuleList: +// Selects a shared library matching a given file spec +//---------------------------------------------------------------------- + +//---------------------------------------------------------------------- +// SearchFilterByModuleList constructors +//---------------------------------------------------------------------- + +SearchFilterByModuleList::SearchFilterByModuleList (const lldb::TargetSP &target_sp, const FileSpecList &module_list) : + SearchFilter (target_sp), + m_module_spec_list (module_list) +{ +} + + +//---------------------------------------------------------------------- +// SearchFilterByModuleList copy constructor +//---------------------------------------------------------------------- +SearchFilterByModuleList::SearchFilterByModuleList(const SearchFilterByModuleList& rhs) : + SearchFilter (rhs), + m_module_spec_list (rhs.m_module_spec_list) +{ +} + +//---------------------------------------------------------------------- +// SearchFilterByModuleList assignment operator +//---------------------------------------------------------------------- +const SearchFilterByModuleList& +SearchFilterByModuleList::operator=(const SearchFilterByModuleList& rhs) +{ + m_target_sp = rhs.m_target_sp; + m_module_spec_list = rhs.m_module_spec_list; + return *this; +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +SearchFilterByModuleList::~SearchFilterByModuleList() +{ +} + +bool +SearchFilterByModuleList::ModulePasses (const ModuleSP &module_sp) +{ + if (m_module_spec_list.GetSize() == 0) + return true; + + if (module_sp && m_module_spec_list.FindFileIndex(0, module_sp->GetFileSpec(), false) != UINT32_MAX) + return true; + else + return false; +} + +bool +SearchFilterByModuleList::ModulePasses (const FileSpec &spec) +{ + if (m_module_spec_list.GetSize() == 0) + return true; + + if (m_module_spec_list.FindFileIndex(0, spec, true) != UINT32_MAX) + return true; + else + return false; +} + +bool +SearchFilterByModuleList::AddressPasses (Address &address) +{ + // FIXME: Not yet implemented + return true; +} + + +bool +SearchFilterByModuleList::CompUnitPasses (FileSpec &fileSpec) +{ + return true; +} + +bool +SearchFilterByModuleList::CompUnitPasses (CompileUnit &compUnit) +{ + return true; +} + +void +SearchFilterByModuleList::Search (Searcher &searcher) +{ + if (!m_target_sp) + return; + + if (searcher.GetDepth() == Searcher::eDepthTarget) + { + SymbolContext empty_sc; + empty_sc.target_sp = m_target_sp; + searcher.SearchCallback (*this, empty_sc, NULL, false); + } + + // If the module file spec is a full path, then we can just find the one + // filespec that passes. Otherwise, we need to go through all modules and + // find the ones that match the file name. + + const ModuleList &target_modules = m_target_sp->GetImages(); + Mutex::Locker modules_locker (target_modules.GetMutex()); + + const size_t num_modules = target_modules.GetSize (); + for (size_t i = 0; i < num_modules; i++) + { + Module* module = target_modules.GetModulePointerAtIndexUnlocked(i); + if (m_module_spec_list.FindFileIndex(0, module->GetFileSpec(), false) != UINT32_MAX) + { + SymbolContext matchingContext(m_target_sp, module->shared_from_this()); + Searcher::CallbackReturn shouldContinue; + + shouldContinue = DoModuleIteration(matchingContext, searcher); + if (shouldContinue == Searcher::eCallbackReturnStop) + return; + } + } +} + +void +SearchFilterByModuleList::GetDescription (Stream *s) +{ + size_t num_modules = m_module_spec_list.GetSize(); + if (num_modules == 1) + { + s->Printf (", module = "); + if (s->GetVerbose()) + { + char buffer[2048]; + m_module_spec_list.GetFileSpecAtIndex(0).GetPath(buffer, 2047); + s->PutCString(buffer); + } + else + { + s->PutCString(m_module_spec_list.GetFileSpecAtIndex(0).GetFilename().AsCString("")); + } + } + else + { + s->Printf (", modules(%zu) = ", num_modules); + for (size_t i = 0; i < num_modules; i++) + { + if (s->GetVerbose()) + { + char buffer[2048]; + m_module_spec_list.GetFileSpecAtIndex(i).GetPath(buffer, 2047); + s->PutCString(buffer); + } + else + { + s->PutCString(m_module_spec_list.GetFileSpecAtIndex(i).GetFilename().AsCString("")); + } + if (i != num_modules - 1) + s->PutCString (", "); + } + } +} + +uint32_t +SearchFilterByModuleList::GetFilterRequiredItems() +{ + return eSymbolContextModule; +} + +void +SearchFilterByModuleList::Dump (Stream *s) const +{ + +} + +//---------------------------------------------------------------------- +// SearchFilterByModuleListAndCU: +// Selects a shared library matching a given file spec +//---------------------------------------------------------------------- + +//---------------------------------------------------------------------- +// SearchFilterByModuleListAndCU constructors +//---------------------------------------------------------------------- + +SearchFilterByModuleListAndCU::SearchFilterByModuleListAndCU (const lldb::TargetSP &target_sp, + const FileSpecList &module_list, + const FileSpecList &cu_list) : + SearchFilterByModuleList (target_sp, module_list), + m_cu_spec_list (cu_list) +{ +} + + +//---------------------------------------------------------------------- +// SearchFilterByModuleListAndCU copy constructor +//---------------------------------------------------------------------- +SearchFilterByModuleListAndCU::SearchFilterByModuleListAndCU(const SearchFilterByModuleListAndCU& rhs) : + SearchFilterByModuleList (rhs), + m_cu_spec_list (rhs.m_cu_spec_list) +{ +} + +//---------------------------------------------------------------------- +// SearchFilterByModuleListAndCU assignment operator +//---------------------------------------------------------------------- +const SearchFilterByModuleListAndCU& +SearchFilterByModuleListAndCU::operator=(const SearchFilterByModuleListAndCU& rhs) +{ + if (&rhs != this) + { + m_target_sp = rhs.m_target_sp; + m_module_spec_list = rhs.m_module_spec_list; + m_cu_spec_list = rhs.m_cu_spec_list; + } + return *this; +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +SearchFilterByModuleListAndCU::~SearchFilterByModuleListAndCU() +{ +} + +bool +SearchFilterByModuleListAndCU::AddressPasses (Address &address) +{ + return true; +} + + +bool +SearchFilterByModuleListAndCU::CompUnitPasses (FileSpec &fileSpec) +{ + return m_cu_spec_list.FindFileIndex(0, fileSpec, false) != UINT32_MAX; +} + +bool +SearchFilterByModuleListAndCU::CompUnitPasses (CompileUnit &compUnit) +{ + bool in_cu_list = m_cu_spec_list.FindFileIndex(0, compUnit, false) != UINT32_MAX; + if (in_cu_list) + { + ModuleSP module_sp(compUnit.GetModule()); + if (module_sp) + { + bool module_passes = SearchFilterByModuleList::ModulePasses(module_sp); + return module_passes; + } + else + return true; + } + else + return false; +} + +void +SearchFilterByModuleListAndCU::Search (Searcher &searcher) +{ + if (!m_target_sp) + return; + + if (searcher.GetDepth() == Searcher::eDepthTarget) + { + SymbolContext empty_sc; + empty_sc.target_sp = m_target_sp; + searcher.SearchCallback (*this, empty_sc, NULL, false); + } + + // If the module file spec is a full path, then we can just find the one + // filespec that passes. Otherwise, we need to go through all modules and + // find the ones that match the file name. + + ModuleList matching_modules; + const ModuleList &target_images = m_target_sp->GetImages(); + Mutex::Locker modules_locker(target_images.GetMutex()); + + const size_t num_modules = target_images.GetSize (); + bool no_modules_in_filter = m_module_spec_list.GetSize() == 0; + for (size_t i = 0; i < num_modules; i++) + { + lldb::ModuleSP module_sp = target_images.GetModuleAtIndexUnlocked(i); + if (no_modules_in_filter || m_module_spec_list.FindFileIndex(0, module_sp->GetFileSpec(), false) != UINT32_MAX) + { + SymbolContext matchingContext(m_target_sp, module_sp); + Searcher::CallbackReturn shouldContinue; + + if (searcher.GetDepth() == Searcher::eDepthModule) + { + shouldContinue = DoModuleIteration(matchingContext, searcher); + if (shouldContinue == Searcher::eCallbackReturnStop) + return; + } + else + { + const size_t num_cu = module_sp->GetNumCompileUnits(); + for (size_t cu_idx = 0; cu_idx < num_cu; cu_idx++) + { + CompUnitSP cu_sp = module_sp->GetCompileUnitAtIndex(cu_idx); + matchingContext.comp_unit = cu_sp.get(); + if (matchingContext.comp_unit) + { + if (m_cu_spec_list.FindFileIndex(0, *matchingContext.comp_unit, false) != UINT32_MAX) + { + shouldContinue = DoCUIteration(module_sp, matchingContext, searcher); + if (shouldContinue == Searcher::eCallbackReturnStop) + return; + } + } + } + } + } + } +} + +void +SearchFilterByModuleListAndCU::GetDescription (Stream *s) +{ + size_t num_modules = m_module_spec_list.GetSize(); + if (num_modules == 1) + { + s->Printf (", module = "); + if (s->GetVerbose()) + { + char buffer[2048]; + m_module_spec_list.GetFileSpecAtIndex(0).GetPath(buffer, 2047); + s->PutCString(buffer); + } + else + { + s->PutCString(m_module_spec_list.GetFileSpecAtIndex(0).GetFilename().AsCString("")); + } + } + else if (num_modules > 0) + { + s->Printf (", modules(%zd) = ", num_modules); + for (size_t i = 0; i < num_modules; i++) + { + if (s->GetVerbose()) + { + char buffer[2048]; + m_module_spec_list.GetFileSpecAtIndex(i).GetPath(buffer, 2047); + s->PutCString(buffer); + } + else + { + s->PutCString(m_module_spec_list.GetFileSpecAtIndex(i).GetFilename().AsCString("")); + } + if (i != num_modules - 1) + s->PutCString (", "); + } + } +} + +uint32_t +SearchFilterByModuleListAndCU::GetFilterRequiredItems() +{ + return eSymbolContextModule | eSymbolContextCompUnit; +} + +void +SearchFilterByModuleListAndCU::Dump (Stream *s) const +{ + +} + diff --git a/contrib/llvm/tools/lldb/source/Core/Section.cpp b/contrib/llvm/tools/lldb/source/Core/Section.cpp new file mode 100644 index 00000000000..e2a084ceb2f --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Core/Section.cpp @@ -0,0 +1,562 @@ +//===-- Section.cpp ---------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Core/Section.h" +#include "lldb/Core/Module.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Target/Target.h" + +using namespace lldb; +using namespace lldb_private; + +Section::Section (const ModuleSP &module_sp, + ObjectFile *obj_file, + user_id_t sect_id, + const ConstString &name, + SectionType sect_type, + addr_t file_addr, + addr_t byte_size, + lldb::offset_t file_offset, + lldb::offset_t file_size, + uint32_t flags) : + ModuleChild (module_sp), + UserID (sect_id), + Flags (flags), + m_obj_file (obj_file), + m_type (sect_type), + m_parent_wp (), + m_name (name), + m_file_addr (file_addr), + m_byte_size (byte_size), + m_file_offset (file_offset), + m_file_size (file_size), + m_children (), + m_fake (false), + m_encrypted (false), + m_thread_specific (false) +{ +// printf ("Section::Section(%p): module=%p, sect_id = 0x%16.16" PRIx64 ", addr=[0x%16.16" PRIx64 " - 0x%16.16" PRIx64 "), file [0x%16.16" PRIx64 " - 0x%16.16" PRIx64 "), flags = 0x%8.8x, name = %s\n", +// this, module_sp.get(), sect_id, file_addr, file_addr + byte_size, file_offset, file_offset + file_size, flags, name.GetCString()); +} + +Section::Section (const lldb::SectionSP &parent_section_sp, + const ModuleSP &module_sp, + ObjectFile *obj_file, + user_id_t sect_id, + const ConstString &name, + SectionType sect_type, + addr_t file_addr, + addr_t byte_size, + lldb::offset_t file_offset, + lldb::offset_t file_size, + uint32_t flags) : + ModuleChild (module_sp), + UserID (sect_id), + Flags (flags), + m_obj_file (obj_file), + m_type (sect_type), + m_parent_wp (), + m_name (name), + m_file_addr (file_addr), + m_byte_size (byte_size), + m_file_offset (file_offset), + m_file_size (file_size), + m_children (), + m_fake (false), + m_encrypted (false), + m_thread_specific (false) +{ +// printf ("Section::Section(%p): module=%p, sect_id = 0x%16.16" PRIx64 ", addr=[0x%16.16" PRIx64 " - 0x%16.16" PRIx64 "), file [0x%16.16" PRIx64 " - 0x%16.16" PRIx64 "), flags = 0x%8.8x, name = %s.%s\n", +// this, module_sp.get(), sect_id, file_addr, file_addr + byte_size, file_offset, file_offset + file_size, flags, parent_section_sp->GetName().GetCString(), name.GetCString()); + if (parent_section_sp) + m_parent_wp = parent_section_sp; +} + +Section::~Section() +{ +// printf ("Section::~Section(%p)\n", this); +} + +addr_t +Section::GetFileAddress () const +{ + SectionSP parent_sp (GetParent ()); + if (parent_sp) + { + // This section has a parent which means m_file_addr is an offset into + // the parent section, so the file address for this section is the file + // address of the parent plus the offset + return parent_sp->GetFileAddress() + m_file_addr; + } + // This section has no parent, so m_file_addr is the file base address + return m_file_addr; +} + +bool +Section::SetFileAddress (lldb::addr_t file_addr) +{ + SectionSP parent_sp (GetParent ()); + if (parent_sp) + { + if (m_file_addr >= file_addr) + return parent_sp->SetFileAddress (m_file_addr - file_addr); + return false; + } + else + { + // This section has no parent, so m_file_addr is the file base address + m_file_addr = file_addr; + return true; + } +} + +lldb::addr_t +Section::GetOffset () const +{ + // This section has a parent which means m_file_addr is an offset. + SectionSP parent_sp (GetParent ()); + if (parent_sp) + return m_file_addr; + + // This section has no parent, so there is no offset to be had + return 0; +} + +addr_t +Section::GetLoadBaseAddress (Target *target) const +{ + addr_t load_base_addr = LLDB_INVALID_ADDRESS; + SectionSP parent_sp (GetParent ()); + if (parent_sp) + { + load_base_addr = parent_sp->GetLoadBaseAddress (target); + if (load_base_addr != LLDB_INVALID_ADDRESS) + load_base_addr += GetOffset(); + } + else + { + load_base_addr = target->GetSectionLoadList().GetSectionLoadAddress (const_cast
(this)->shared_from_this()); + } + return load_base_addr; +} + +bool +Section::ResolveContainedAddress (addr_t offset, Address &so_addr) const +{ + const size_t num_children = m_children.GetSize(); + if (num_children > 0) + { + for (size_t i=0; iGetOffset(); + if (child_offset <= offset && offset - child_offset < child_section->GetByteSize()) + return child_section->ResolveContainedAddress (offset - child_offset, so_addr); + } + } + so_addr.SetOffset(offset); + so_addr.SetSection(const_cast
(this)->shared_from_this()); + +#ifdef LLDB_CONFIGURATION_DEBUG + // For debug builds, ensure that there are no orphaned (i.e., moduleless) sections. + assert(GetModule().get()); +#endif + return true; +} + +bool +Section::ContainsFileAddress (addr_t vm_addr) const +{ + const addr_t file_addr = GetFileAddress(); + if (file_addr != LLDB_INVALID_ADDRESS) + { + if (file_addr <= vm_addr) + { + const addr_t offset = vm_addr - file_addr; + return offset < GetByteSize(); + } + } + return false; +} + +int +Section::Compare (const Section& a, const Section& b) +{ + if (&a == &b) + return 0; + + const ModuleSP a_module_sp = a.GetModule(); + const ModuleSP b_module_sp = b.GetModule(); + if (a_module_sp == b_module_sp) + { + user_id_t a_sect_uid = a.GetID(); + user_id_t b_sect_uid = b.GetID(); + if (a_sect_uid < b_sect_uid) + return -1; + if (a_sect_uid > b_sect_uid) + return 1; + return 0; + } + else + { + // The modules are different, just compare the module pointers + if (a_module_sp.get() < b_module_sp.get()) + return -1; + else + return 1; // We already know the modules aren't equal + } +} + + +void +Section::Dump (Stream *s, Target *target, uint32_t depth) const +{ +// s->Printf("%.*p: ", (int)sizeof(void*) * 2, this); + s->Indent(); + s->Printf("0x%8.8" PRIx64 " %-16s ", GetID(), GetSectionTypeAsCString (m_type)); + bool resolved = true; + addr_t addr = LLDB_INVALID_ADDRESS; + + if (GetByteSize() == 0) + s->Printf("%39s", ""); + else + { + if (target) + addr = GetLoadBaseAddress (target); + + if (addr == LLDB_INVALID_ADDRESS) + { + if (target) + resolved = false; + addr = GetFileAddress(); + } + + VMRange range(addr, addr + m_byte_size); + range.Dump (s, 0); + } + + s->Printf("%c 0x%8.8" PRIx64 " 0x%8.8" PRIx64 " 0x%8.8x ", resolved ? ' ' : '*', m_file_offset, m_file_size, Get()); + + DumpName (s); + + s->EOL(); + + if (depth > 0) + m_children.Dump(s, target, false, depth - 1); +} + +void +Section::DumpName (Stream *s) const +{ + SectionSP parent_sp (GetParent ()); + if (parent_sp) + { + parent_sp->DumpName (s); + s->PutChar('.'); + } + else + { + // The top most section prints the module basename + const char * name = NULL; + ModuleSP module_sp (GetModule()); + const FileSpec &file_spec = m_obj_file->GetFileSpec(); + + if (m_obj_file) + name = file_spec.GetFilename().AsCString(); + if ((!name || !name[0]) && module_sp) + name = module_sp->GetFileSpec().GetFilename().AsCString(); + if (name && name[0]) + s->Printf("%s.", name); + } + m_name.Dump(s); +} + +bool +Section::IsDescendant (const Section *section) +{ + if (this == section) + return true; + SectionSP parent_sp (GetParent ()); + if (parent_sp) + return parent_sp->IsDescendant (section); + return false; +} + +bool +Section::Slide (addr_t slide_amount, bool slide_children) +{ + if (m_file_addr != LLDB_INVALID_ADDRESS) + { + if (slide_amount == 0) + return true; + + m_file_addr += slide_amount; + + if (slide_children) + m_children.Slide (slide_amount, slide_children); + + return true; + } + return false; +} + +#pragma mark SectionList + +SectionList::SectionList () : + m_sections() +{ +} + + +SectionList::~SectionList () +{ +} + +SectionList & +SectionList::operator = (const SectionList& rhs) +{ + if (this != &rhs) + m_sections = rhs.m_sections; + return *this; +} + +size_t +SectionList::AddSection (const lldb::SectionSP& section_sp) +{ + assert (section_sp.get()); + size_t section_index = m_sections.size(); + m_sections.push_back(section_sp); + return section_index; +} + +// Warning, this can be slow as it's removing items from a std::vector. +bool +SectionList::DeleteSection (size_t idx) +{ + if (idx < m_sections.size()) + { + m_sections.erase (m_sections.begin() + idx); + return true; + } + return false; +} + +size_t +SectionList::FindSectionIndex (const Section* sect) +{ + iterator sect_iter; + iterator begin = m_sections.begin(); + iterator end = m_sections.end(); + for (sect_iter = begin; sect_iter != end; ++sect_iter) + { + if (sect_iter->get() == sect) + { + // The secton was already in this section list + return std::distance (begin, sect_iter); + } + } + return UINT32_MAX; +} + +size_t +SectionList::AddUniqueSection (const lldb::SectionSP& sect_sp) +{ + size_t sect_idx = FindSectionIndex (sect_sp.get()); + if (sect_idx == UINT32_MAX) + { + sect_idx = AddSection (sect_sp); + } + return sect_idx; +} + +bool +SectionList::ReplaceSection (user_id_t sect_id, const lldb::SectionSP& sect_sp, uint32_t depth) +{ + iterator sect_iter, end = m_sections.end(); + for (sect_iter = m_sections.begin(); sect_iter != end; ++sect_iter) + { + if ((*sect_iter)->GetID() == sect_id) + { + *sect_iter = sect_sp; + return true; + } + else if (depth > 0) + { + if ((*sect_iter)->GetChildren().ReplaceSection(sect_id, sect_sp, depth - 1)) + return true; + } + } + return false; +} + +size_t +SectionList::GetNumSections (uint32_t depth) const +{ + size_t count = m_sections.size(); + if (depth > 0) + { + const_iterator sect_iter, end = m_sections.end(); + for (sect_iter = m_sections.begin(); sect_iter != end; ++sect_iter) + { + count += (*sect_iter)->GetChildren().GetNumSections(depth - 1); + } + } + return count; +} + +SectionSP +SectionList::GetSectionAtIndex (size_t idx) const +{ + SectionSP sect_sp; + if (idx < m_sections.size()) + sect_sp = m_sections[idx]; + return sect_sp; +} + +SectionSP +SectionList::FindSectionByName (const ConstString §ion_dstr) const +{ + SectionSP sect_sp; + // Check if we have a valid section string + if (section_dstr && !m_sections.empty()) + { + const_iterator sect_iter; + const_iterator end = m_sections.end(); + for (sect_iter = m_sections.begin(); sect_iter != end && sect_sp.get() == NULL; ++sect_iter) + { + Section *child_section = sect_iter->get(); + assert (child_section); + if (child_section->GetName() == section_dstr) + { + sect_sp = *sect_iter; + } + else + { + sect_sp = child_section->GetChildren().FindSectionByName(section_dstr); + } + } + } + return sect_sp; +} + +SectionSP +SectionList::FindSectionByID (user_id_t sect_id) const +{ + SectionSP sect_sp; + if (sect_id) + { + const_iterator sect_iter; + const_iterator end = m_sections.end(); + for (sect_iter = m_sections.begin(); sect_iter != end && sect_sp.get() == NULL; ++sect_iter) + { + if ((*sect_iter)->GetID() == sect_id) + { + sect_sp = *sect_iter; + break; + } + else + { + sect_sp = (*sect_iter)->GetChildren().FindSectionByID (sect_id); + } + } + } + return sect_sp; +} + + +SectionSP +SectionList::FindSectionByType (SectionType sect_type, bool check_children, size_t start_idx) const +{ + SectionSP sect_sp; + size_t num_sections = m_sections.size(); + for (size_t idx = start_idx; idx < num_sections; ++idx) + { + if (m_sections[idx]->GetType() == sect_type) + { + sect_sp = m_sections[idx]; + break; + } + else if (check_children) + { + sect_sp = m_sections[idx]->GetChildren().FindSectionByType (sect_type, check_children, 0); + if (sect_sp) + break; + } + } + return sect_sp; +} + +SectionSP +SectionList::FindSectionContainingFileAddress (addr_t vm_addr, uint32_t depth) const +{ + SectionSP sect_sp; + const_iterator sect_iter; + const_iterator end = m_sections.end(); + for (sect_iter = m_sections.begin(); sect_iter != end && sect_sp.get() == NULL; ++sect_iter) + { + Section *sect = sect_iter->get(); + if (sect->ContainsFileAddress (vm_addr)) + { + // The file address is in this section. We need to make sure one of our child + // sections doesn't contain this address as well as obeying the depth limit + // that was passed in. + if (depth > 0) + sect_sp = sect->GetChildren().FindSectionContainingFileAddress(vm_addr, depth - 1); + + if (sect_sp.get() == NULL && !sect->IsFake()) + sect_sp = *sect_iter; + } + } + return sect_sp; +} + +bool +SectionList::ContainsSection(user_id_t sect_id) const +{ + return FindSectionByID (sect_id).get() != NULL; +} + +void +SectionList::Dump (Stream *s, Target *target, bool show_header, uint32_t depth) const +{ + bool target_has_loaded_sections = target && !target->GetSectionLoadList().IsEmpty(); + if (show_header && !m_sections.empty()) + { + s->Indent(); + s->Printf( "SectID Type %s Address File Off. File Size Flags Section Name\n", target_has_loaded_sections ? "Load" : "File"); + s->Indent(); + s->PutCString("---------- ---------------- --------------------------------------- ---------- ---------- ---------- ----------------------------\n"); + } + + + const_iterator sect_iter; + const_iterator end = m_sections.end(); + for (sect_iter = m_sections.begin(); sect_iter != end; ++sect_iter) + { + (*sect_iter)->Dump(s, target_has_loaded_sections ? target : NULL, depth); + } + + if (show_header && !m_sections.empty()) + s->IndentLess(); + +} + +size_t +SectionList::Slide (addr_t slide_amount, bool slide_children) +{ + size_t count = 0; + const_iterator pos, end = m_sections.end(); + for (pos = m_sections.begin(); pos != end; ++pos) + { + if ((*pos)->Slide(slide_amount, slide_children)) + ++count; + } + return count; +} diff --git a/contrib/llvm/tools/lldb/source/Core/SourceManager.cpp b/contrib/llvm/tools/lldb/source/Core/SourceManager.cpp new file mode 100644 index 00000000000..9f289348fd9 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Core/SourceManager.cpp @@ -0,0 +1,651 @@ +//===-- SourceManager.cpp ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "lldb/Core/SourceManager.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/DataBuffer.h" +#include "lldb/Core/Debugger.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/Stream.h" +#include "lldb/Symbol/ClangNamespaceDecl.h" +#include "lldb/Symbol/CompileUnit.h" +#include "lldb/Symbol/Function.h" +#include "lldb/Symbol/SymbolContext.h" +#include "lldb/Target/Target.h" + +using namespace lldb; +using namespace lldb_private; + + +static inline bool is_newline_char(char ch) +{ + return ch == '\n' || ch == '\r'; +} + + +//---------------------------------------------------------------------- +// SourceManager constructor +//---------------------------------------------------------------------- +SourceManager::SourceManager(const TargetSP &target_sp) : + m_last_file_sp (), + m_last_line (0), + m_last_count (0), + m_default_set(false), + m_target_wp (target_sp), + m_debugger_wp(target_sp->GetDebugger().shared_from_this()) +{ +} + +SourceManager::SourceManager(const DebuggerSP &debugger_sp) : + m_last_file_sp (), + m_last_line (0), + m_last_count (0), + m_default_set(false), + m_target_wp (), + m_debugger_wp (debugger_sp) +{ +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +SourceManager::~SourceManager() +{ +} + +SourceManager::FileSP +SourceManager::GetFile (const FileSpec &file_spec) +{ + bool same_as_previous = m_last_file_sp && m_last_file_sp->FileSpecMatches (file_spec); + + DebuggerSP debugger_sp (m_debugger_wp.lock()); + FileSP file_sp; + if (same_as_previous) + file_sp = m_last_file_sp; + else if (debugger_sp) + file_sp = debugger_sp->GetSourceFileCache().FindSourceFile (file_spec); + + TargetSP target_sp (m_target_wp.lock()); + + // It the target source path map has been updated, get this file again so we + // can successfully remap the source file + if (target_sp && file_sp && file_sp->GetSourceMapModificationID() != target_sp->GetSourcePathMap().GetModificationID()) + file_sp.reset(); + + // If file_sp is no good or it points to a non-existent file, reset it. + if (!file_sp || !file_sp->GetFileSpec().Exists()) + { + file_sp.reset (new File (file_spec, target_sp.get())); + + if (debugger_sp) + debugger_sp->GetSourceFileCache().AddSourceFile(file_sp); + } + return file_sp; +} + +size_t +SourceManager::DisplaySourceLinesWithLineNumbersUsingLastFile (uint32_t start_line, + uint32_t count, + uint32_t curr_line, + const char* current_line_cstr, + Stream *s, + const SymbolContextList *bp_locs) +{ + if (count == 0) + return 0; + size_t return_value = 0; + if (start_line == 0) + { + if (m_last_line != 0 && m_last_line != UINT32_MAX) + start_line = m_last_line + m_last_count; + else + start_line = 1; + } + + if (!m_default_set) + { + FileSpec tmp_spec; + uint32_t tmp_line; + GetDefaultFileAndLine(tmp_spec, tmp_line); + } + + m_last_line = start_line; + m_last_count = count; + + if (m_last_file_sp.get()) + { + const uint32_t end_line = start_line + count - 1; + for (uint32_t line = start_line; line <= end_line; ++line) + { + if (!m_last_file_sp->LineIsValid (line)) + { + m_last_line = UINT32_MAX; + break; + } + + char prefix[32] = ""; + if (bp_locs) + { + uint32_t bp_count = bp_locs->NumLineEntriesWithLine (line); + + if (bp_count > 0) + ::snprintf (prefix, sizeof (prefix), "[%u] ", bp_count); + else + ::snprintf (prefix, sizeof (prefix), " "); + } + + return_value += s->Printf("%s%2.2s %-4u\t", + prefix, + line == curr_line ? current_line_cstr : "", + line); + size_t this_line_size = m_last_file_sp->DisplaySourceLines (line, 0, 0, s); + if (this_line_size == 0) + { + m_last_line = UINT32_MAX; + break; + } + else + return_value += this_line_size; + } + } + return return_value; +} + +size_t +SourceManager::DisplaySourceLinesWithLineNumbers +( + const FileSpec &file_spec, + uint32_t line, + uint32_t context_before, + uint32_t context_after, + const char* current_line_cstr, + Stream *s, + const SymbolContextList *bp_locs +) +{ + FileSP file_sp (GetFile (file_spec)); + + uint32_t start_line; + uint32_t count = context_before + context_after + 1; + if (line > context_before) + start_line = line - context_before; + else + start_line = 1; + + if (m_last_file_sp.get() != file_sp.get()) + { + if (line == 0) + m_last_line = 0; + m_last_file_sp = file_sp; + } + return DisplaySourceLinesWithLineNumbersUsingLastFile (start_line, count, line, current_line_cstr, s, bp_locs); +} + +size_t +SourceManager::DisplayMoreWithLineNumbers (Stream *s, + uint32_t count, + bool reverse, + const SymbolContextList *bp_locs) +{ + // If we get called before anybody has set a default file and line, then try to figure it out here. + const bool have_default_file_line = m_last_file_sp && m_last_line > 0; + if (!m_default_set) + { + FileSpec tmp_spec; + uint32_t tmp_line; + GetDefaultFileAndLine(tmp_spec, tmp_line); + } + + if (m_last_file_sp) + { + if (m_last_line == UINT32_MAX) + return 0; + + if (reverse && m_last_line == 1) + return 0; + + if (count > 0) + m_last_count = count; + else if (m_last_count == 0) + m_last_count = 10; + + if (m_last_line > 0) + { + if (reverse) + { + // If this is the first time we've done a reverse, then back up one more time so we end + // up showing the chunk before the last one we've shown: + if (m_last_line > m_last_count) + m_last_line -= m_last_count; + else + m_last_line = 1; + } + else if (have_default_file_line) + m_last_line += m_last_count; + } + else + m_last_line = 1; + + return DisplaySourceLinesWithLineNumbersUsingLastFile (m_last_line, m_last_count, UINT32_MAX, "", s, bp_locs); + } + return 0; +} + +bool +SourceManager::SetDefaultFileAndLine (const FileSpec &file_spec, uint32_t line) +{ + FileSP old_file_sp = m_last_file_sp; + m_last_file_sp = GetFile (file_spec); + + m_default_set = true; + if (m_last_file_sp) + { + m_last_line = line; + return true; + } + else + { + m_last_file_sp = old_file_sp; + return false; + } +} + +bool +SourceManager::GetDefaultFileAndLine (FileSpec &file_spec, uint32_t &line) +{ + if (m_last_file_sp) + { + file_spec = m_last_file_sp->GetFileSpec(); + line = m_last_line; + return true; + } + else if (!m_default_set) + { + TargetSP target_sp (m_target_wp.lock()); + + if (target_sp) + { + // If nobody has set the default file and line then try here. If there's no executable, then we + // will try again later when there is one. Otherwise, if we can't find it we won't look again, + // somebody will have to set it (for instance when we stop somewhere...) + Module *executable_ptr = target_sp->GetExecutableModulePointer(); + if (executable_ptr) + { + SymbolContextList sc_list; + ConstString main_name("main"); + bool symbols_okay = false; // Force it to be a debug symbol. + bool inlines_okay = true; + bool append = false; + size_t num_matches = executable_ptr->FindFunctions (main_name, + NULL, + lldb::eFunctionNameTypeBase, + inlines_okay, + symbols_okay, + append, + sc_list); + for (size_t idx = 0; idx < num_matches; idx++) + { + SymbolContext sc; + sc_list.GetContextAtIndex(idx, sc); + if (sc.function) + { + lldb_private::LineEntry line_entry; + if (sc.function->GetAddressRange().GetBaseAddress().CalculateSymbolContextLineEntry (line_entry)) + { + SetDefaultFileAndLine (line_entry.file, + line_entry.line); + file_spec = m_last_file_sp->GetFileSpec(); + line = m_last_line; + return true; + } + } + } + } + } + } + return false; +} + +void +SourceManager::FindLinesMatchingRegex (FileSpec &file_spec, + RegularExpression& regex, + uint32_t start_line, + uint32_t end_line, + std::vector &match_lines) +{ + match_lines.clear(); + FileSP file_sp = GetFile (file_spec); + if (!file_sp) + return; + return file_sp->FindLinesMatchingRegex (regex, start_line, end_line, match_lines); +} + +SourceManager::File::File(const FileSpec &file_spec, Target *target) : + m_file_spec_orig (file_spec), + m_file_spec(file_spec), + m_mod_time (file_spec.GetModificationTime()), + m_source_map_mod_id (0), + m_data_sp(), + m_offsets() +{ + if (!m_mod_time.IsValid()) + { + if (target) + { + m_source_map_mod_id = target->GetSourcePathMap().GetModificationID(); + + if (!file_spec.GetDirectory() && file_spec.GetFilename()) + { + // If this is just a file name, lets see if we can find it in the target: + bool check_inlines = false; + SymbolContextList sc_list; + size_t num_matches = target->GetImages().ResolveSymbolContextForFilePath (file_spec.GetFilename().AsCString(), + 0, + check_inlines, + lldb::eSymbolContextModule | lldb::eSymbolContextCompUnit, + sc_list); + bool got_multiple = false; + if (num_matches != 0) + { + if (num_matches > 1) + { + SymbolContext sc; + FileSpec *test_cu_spec = NULL; + + for (unsigned i = 0; i < num_matches; i++) + { + sc_list.GetContextAtIndex(i, sc); + if (sc.comp_unit) + { + if (test_cu_spec) + { + if (test_cu_spec != static_cast (sc.comp_unit)) + got_multiple = true; + break; + } + else + test_cu_spec = sc.comp_unit; + } + } + } + if (!got_multiple) + { + SymbolContext sc; + sc_list.GetContextAtIndex (0, sc); + m_file_spec = sc.comp_unit; + m_mod_time = m_file_spec.GetModificationTime(); + } + } + } + // Try remapping if m_file_spec does not correspond to an existing file. + if (!m_file_spec.Exists()) + { + FileSpec new_file_spec; + // Check target specific source remappings first, then fall back to + // modules objects can have individual path remappings that were detected + // when the debug info for a module was found. + // then + if (target->GetSourcePathMap().FindFile (m_file_spec, new_file_spec) || + target->GetImages().FindSourceFile (m_file_spec, new_file_spec)) + { + m_file_spec = new_file_spec; + m_mod_time = m_file_spec.GetModificationTime(); + } + } + } + } + + if (m_mod_time.IsValid()) + m_data_sp = m_file_spec.ReadFileContents (); +} + +SourceManager::File::~File() +{ +} + +uint32_t +SourceManager::File::GetLineOffset (uint32_t line) +{ + if (line == 0) + return UINT32_MAX; + + if (line == 1) + return 0; + + if (CalculateLineOffsets (line)) + { + if (line < m_offsets.size()) + return m_offsets[line - 1]; // yes we want "line - 1" in the index + } + return UINT32_MAX; +} + +bool +SourceManager::File::LineIsValid (uint32_t line) +{ + if (line == 0) + return false; + + if (CalculateLineOffsets (line)) + return line < m_offsets.size(); + return false; +} + +size_t +SourceManager::File::DisplaySourceLines (uint32_t line, uint32_t context_before, uint32_t context_after, Stream *s) +{ + // TODO: use host API to sign up for file modifications to anything in our + // source cache and only update when we determine a file has been updated. + // For now we check each time we want to display info for the file. + TimeValue curr_mod_time (m_file_spec.GetModificationTime()); + + if (curr_mod_time.IsValid() && m_mod_time != curr_mod_time) + { + m_mod_time = curr_mod_time; + m_data_sp = m_file_spec.ReadFileContents (); + m_offsets.clear(); + } + + // Sanity check m_data_sp before proceeding. + if (!m_data_sp) + return 0; + + const uint32_t start_line = line <= context_before ? 1 : line - context_before; + const uint32_t start_line_offset = GetLineOffset (start_line); + if (start_line_offset != UINT32_MAX) + { + const uint32_t end_line = line + context_after; + uint32_t end_line_offset = GetLineOffset (end_line + 1); + if (end_line_offset == UINT32_MAX) + end_line_offset = m_data_sp->GetByteSize(); + + assert (start_line_offset <= end_line_offset); + size_t bytes_written = 0; + if (start_line_offset < end_line_offset) + { + size_t count = end_line_offset - start_line_offset; + const uint8_t *cstr = m_data_sp->GetBytes() + start_line_offset; + bytes_written = s->Write(cstr, count); + if (!is_newline_char(cstr[count-1])) + bytes_written += s->EOL(); + } + return bytes_written; + } + return 0; +} + +void +SourceManager::File::FindLinesMatchingRegex (RegularExpression& regex, uint32_t start_line, uint32_t end_line, std::vector &match_lines) +{ + TimeValue curr_mod_time (m_file_spec.GetModificationTime()); + if (m_mod_time != curr_mod_time) + { + m_mod_time = curr_mod_time; + m_data_sp = m_file_spec.ReadFileContents (); + m_offsets.clear(); + } + + match_lines.clear(); + + if (!LineIsValid(start_line) || (end_line != UINT32_MAX && !LineIsValid(end_line))) + return; + if (start_line > end_line) + return; + + for (uint32_t line_no = start_line; line_no < end_line; line_no++) + { + std::string buffer; + if (!GetLine (line_no, buffer)) + break; + if (regex.Execute(buffer.c_str())) + { + match_lines.push_back(line_no); + } + } +} + +bool +SourceManager::File::FileSpecMatches (const FileSpec &file_spec) +{ + return FileSpec::Equal (m_file_spec, file_spec, false); +} + +bool +lldb_private::operator== (const SourceManager::File &lhs, const SourceManager::File &rhs) +{ + if (lhs.m_file_spec == rhs.m_file_spec) + { + if (lhs.m_mod_time.IsValid()) + { + if (rhs.m_mod_time.IsValid()) + return lhs.m_mod_time == rhs.m_mod_time; + else + return false; + } + else if (rhs.m_mod_time.IsValid()) + return false; + else + return true; + } + else + return false; +} + +bool +SourceManager::File::CalculateLineOffsets (uint32_t line) +{ + line = UINT32_MAX; // TODO: take this line out when we support partial indexing + if (line == UINT32_MAX) + { + // Already done? + if (!m_offsets.empty() && m_offsets[0] == UINT32_MAX) + return true; + + if (m_offsets.empty()) + { + if (m_data_sp.get() == NULL) + return false; + + const char *start = (char *)m_data_sp->GetBytes(); + if (start) + { + const char *end = start + m_data_sp->GetByteSize(); + + // Calculate all line offsets from scratch + + // Push a 1 at index zero to indicate the file has been completely indexed. + m_offsets.push_back(UINT32_MAX); + register const char *s; + for (s = start; s < end; ++s) + { + register char curr_ch = *s; + if (is_newline_char (curr_ch)) + { + if (s + 1 < end) + { + register char next_ch = s[1]; + if (is_newline_char (next_ch)) + { + if (curr_ch != next_ch) + ++s; + } + } + m_offsets.push_back(s + 1 - start); + } + } + if (!m_offsets.empty()) + { + if (m_offsets.back() < end - start) + m_offsets.push_back(end - start); + } + return true; + } + } + else + { + // Some lines have been populated, start where we last left off + assert("Not implemented yet" == NULL); + } + + } + else + { + // Calculate all line offsets up to "line" + assert("Not implemented yet" == NULL); + } + return false; +} + +bool +SourceManager::File::GetLine (uint32_t line_no, std::string &buffer) +{ + if (!LineIsValid(line_no)) + return false; + + size_t start_offset = GetLineOffset (line_no); + size_t end_offset = GetLineOffset (line_no + 1); + if (end_offset == UINT32_MAX) + { + end_offset = m_data_sp->GetByteSize(); + } + buffer.assign((char *) m_data_sp->GetBytes() + start_offset, end_offset - start_offset); + + return true; +} + +void +SourceManager::SourceFileCache::AddSourceFile (const FileSP &file_sp) +{ + FileSpec file_spec; + FileCache::iterator pos = m_file_cache.find(file_spec); + if (pos == m_file_cache.end()) + m_file_cache[file_spec] = file_sp; + else + { + if (file_sp != pos->second) + m_file_cache[file_spec] = file_sp; + } +} + +SourceManager::FileSP +SourceManager::SourceFileCache::FindSourceFile (const FileSpec &file_spec) const +{ + FileSP file_sp; + FileCache::const_iterator pos = m_file_cache.find(file_spec); + if (pos != m_file_cache.end()) + file_sp = pos->second; + return file_sp; +} + diff --git a/contrib/llvm/tools/lldb/source/Core/State.cpp b/contrib/llvm/tools/lldb/source/Core/State.cpp new file mode 100644 index 00000000000..7d9ccda532a --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Core/State.cpp @@ -0,0 +1,115 @@ +//===-- State.cpp -----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/State.h" +#include + +using namespace lldb; +using namespace lldb_private; + +const char * +lldb_private::StateAsCString (StateType state) +{ + switch (state) + { + case eStateInvalid: return "invalid"; + case eStateUnloaded: return "unloaded"; + case eStateConnected: return "connected"; + case eStateAttaching: return "attaching"; + case eStateLaunching: return "launching"; + case eStateStopped: return "stopped"; + case eStateRunning: return "running"; + case eStateStepping: return "stepping"; + case eStateCrashed: return "crashed"; + case eStateDetached: return "detached"; + case eStateExited: return "exited"; + case eStateSuspended: return "suspended"; + } + static char unknown_state_string[64]; + snprintf(unknown_state_string, sizeof (unknown_state_string), "StateType = %i", state); + return unknown_state_string; +} + +const char * +lldb_private::GetPermissionsAsCString (uint32_t permissions) +{ + switch (permissions) + { + case 0: return "---"; + case ePermissionsWritable: return "-w-"; + case ePermissionsReadable: return "r--"; + case ePermissionsExecutable: return "--x"; + case ePermissionsReadable | + ePermissionsWritable: return "rw-"; + case ePermissionsReadable | + ePermissionsExecutable: return "r-x"; + case ePermissionsWritable | + ePermissionsExecutable: return "-wx"; + case ePermissionsReadable | + ePermissionsWritable | + ePermissionsExecutable: return "rwx"; + default: + break; + } + return "???"; +} + +bool +lldb_private::StateIsRunningState (StateType state) +{ + switch (state) + { + case eStateAttaching: + case eStateLaunching: + case eStateRunning: + case eStateStepping: + return true; + + case eStateConnected: + case eStateDetached: + case eStateInvalid: + case eStateUnloaded: + case eStateStopped: + case eStateCrashed: + case eStateExited: + case eStateSuspended: + break; + } + return false; +} + +bool +lldb_private::StateIsStoppedState (StateType state, bool must_exist) +{ + switch (state) + { + case eStateInvalid: + case eStateConnected: + case eStateAttaching: + case eStateLaunching: + case eStateRunning: + case eStateStepping: + case eStateDetached: + break; + + case eStateUnloaded: + case eStateExited: + return !must_exist; + + case eStateStopped: + case eStateCrashed: + case eStateSuspended: + return true; + } + return false; +} diff --git a/contrib/llvm/tools/lldb/source/Core/Stream.cpp b/contrib/llvm/tools/lldb/source/Core/Stream.cpp new file mode 100644 index 00000000000..49c15d63c36 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Core/Stream.cpp @@ -0,0 +1,786 @@ +//===-- Stream.cpp ----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Core/Stream.h" +#include "lldb/Host/Endian.h" +#include +#include +#include +#include + +#include + +using namespace lldb; +using namespace lldb_private; + +Stream::Stream (uint32_t flags, uint32_t addr_size, ByteOrder byte_order) : + m_flags (flags), + m_addr_size (addr_size), + m_byte_order (byte_order), + m_indent_level(0) +{ +} + +Stream::Stream () : + m_flags (0), + m_addr_size (4), + m_byte_order (lldb::endian::InlHostByteOrder()), + m_indent_level(0) +{ +} + +//------------------------------------------------------------------ +// Destructor +//------------------------------------------------------------------ +Stream::~Stream () +{ +} + +ByteOrder +Stream::SetByteOrder (ByteOrder byte_order) +{ + ByteOrder old_byte_order = m_byte_order; + m_byte_order = byte_order; + return old_byte_order; +} + +//------------------------------------------------------------------ +// Put an offset "uval" out to the stream using the printf format +// in "format". +//------------------------------------------------------------------ +void +Stream::Offset (uint32_t uval, const char *format) +{ + Printf (format, uval); +} + +//------------------------------------------------------------------ +// Put an SLEB128 "uval" out to the stream using the printf format +// in "format". +//------------------------------------------------------------------ +size_t +Stream::PutSLEB128 (int64_t sval) +{ + size_t bytes_written = 0; + if (m_flags.Test(eBinary)) + { + bool more = true; + while (more) + { + uint8_t byte = sval & 0x7fu; + sval >>= 7; + /* sign bit of byte is 2nd high order bit (0x40) */ + if ((sval == 0 && !(byte & 0x40)) || + (sval == -1 && (byte & 0x40)) ) + more = false; + else + // more bytes to come + byte |= 0x80u; + bytes_written += Write(&byte, 1); + } + } + else + { + bytes_written = Printf ("0x%" PRIi64, sval); + } + + return bytes_written; + +} + +//------------------------------------------------------------------ +// Put an ULEB128 "uval" out to the stream using the printf format +// in "format". +//------------------------------------------------------------------ +size_t +Stream::PutULEB128 (uint64_t uval) +{ + size_t bytes_written = 0; + if (m_flags.Test(eBinary)) + { + do + { + + uint8_t byte = uval & 0x7fu; + uval >>= 7; + if (uval != 0) + { + // more bytes to come + byte |= 0x80u; + } + bytes_written += Write(&byte, 1); + } while (uval != 0); + } + else + { + bytes_written = Printf ("0x%" PRIx64, uval); + } + return bytes_written; +} + +//------------------------------------------------------------------ +// Print a raw NULL terminated C string to the stream. +//------------------------------------------------------------------ +size_t +Stream::PutCString (const char *cstr) +{ + size_t cstr_len = strlen(cstr); + // when in binary mode, emit the NULL terminator + if (m_flags.Test(eBinary)) + ++cstr_len; + return Write (cstr, cstr_len); +} + +//------------------------------------------------------------------ +// Print a double quoted NULL terminated C string to the stream +// using the printf format in "format". +//------------------------------------------------------------------ +void +Stream::QuotedCString (const char *cstr, const char *format) +{ + Printf (format, cstr); +} + +//------------------------------------------------------------------ +// Put an address "addr" out to the stream with optional prefix +// and suffix strings. +//------------------------------------------------------------------ +void +Stream::Address (uint64_t addr, uint32_t addr_size, const char *prefix, const char *suffix) +{ + if (prefix == NULL) + prefix = ""; + if (suffix == NULL) + suffix = ""; +// int addr_width = m_addr_size << 1; +// Printf ("%s0x%0*" PRIx64 "%s", prefix, addr_width, addr, suffix); + Printf ("%s0x%0*" PRIx64 "%s", prefix, addr_size * 2, (uint64_t)addr, suffix); +} + +//------------------------------------------------------------------ +// Put an address range out to the stream with optional prefix +// and suffix strings. +//------------------------------------------------------------------ +void +Stream::AddressRange(uint64_t lo_addr, uint64_t hi_addr, uint32_t addr_size, const char *prefix, const char *suffix) +{ + if (prefix && prefix[0]) + PutCString (prefix); + Address (lo_addr, addr_size, "["); + Address (hi_addr, addr_size, "-", ")"); + if (suffix && suffix[0]) + PutCString (suffix); +} + + +size_t +Stream::PutChar (char ch) +{ + return Write (&ch, 1); +} + + +//------------------------------------------------------------------ +// Print some formatted output to the stream. +//------------------------------------------------------------------ +size_t +Stream::Printf (const char *format, ...) +{ + va_list args; + va_start (args, format); + size_t result = PrintfVarArg(format, args); + va_end (args); + return result; +} + +//------------------------------------------------------------------ +// Print some formatted output to the stream. +//------------------------------------------------------------------ +size_t +Stream::PrintfVarArg (const char *format, va_list args) +{ + char str[1024]; + va_list args_copy; + + va_copy (args_copy, args); + + size_t bytes_written = 0; + // Try and format our string into a fixed buffer first and see if it fits + size_t length = ::vsnprintf (str, sizeof(str), format, args); + if (length < sizeof(str)) + { + // Include the NULL termination byte for binary output + if (m_flags.Test(eBinary)) + length += 1; + // The formatted string fit into our stack based buffer, so we can just + // append that to our packet + bytes_written = Write (str, length); + } + else + { + // Our stack buffer wasn't big enough to contain the entire formatted + // string, so lets let vasprintf create the string for us! + char *str_ptr = NULL; + length = ::vasprintf (&str_ptr, format, args_copy); + if (str_ptr) + { + // Include the NULL termination byte for binary output + if (m_flags.Test(eBinary)) + length += 1; + bytes_written = Write (str_ptr, length); + ::free (str_ptr); + } + } + va_end (args_copy); + return bytes_written; +} + +//------------------------------------------------------------------ +// Print and End of Line character to the stream +//------------------------------------------------------------------ +size_t +Stream::EOL() +{ + return PutChar ('\n'); +} + +//------------------------------------------------------------------ +// Indent the current line using the current indentation level and +// print an optional string following the idenatation spaces. +//------------------------------------------------------------------ +size_t +Stream::Indent(const char *s) +{ + return Printf ("%*.*s%s", m_indent_level, m_indent_level, "", s ? s : ""); +} + +//------------------------------------------------------------------ +// Stream a character "ch" out to this stream. +//------------------------------------------------------------------ +Stream& +Stream::operator<< (char ch) +{ + PutChar (ch); + return *this; +} + +//------------------------------------------------------------------ +// Stream the NULL terminated C string out to this stream. +//------------------------------------------------------------------ +Stream& +Stream::operator<< (const char *s) +{ + Printf ("%s", s); + return *this; +} + +//------------------------------------------------------------------ +// Stream the pointer value out to this stream. +//------------------------------------------------------------------ +Stream& +Stream::operator<< (void *p) +{ + Printf ("0x%.*tx", (int)sizeof(void*) * 2, (ptrdiff_t)p); + return *this; +} + +//------------------------------------------------------------------ +// Stream a uint8_t "uval" out to this stream. +//------------------------------------------------------------------ +Stream& +Stream::operator<< (uint8_t uval) +{ + PutHex8(uval); + return *this; +} + +//------------------------------------------------------------------ +// Stream a uint16_t "uval" out to this stream. +//------------------------------------------------------------------ +Stream& +Stream::operator<< (uint16_t uval) +{ + PutHex16(uval, m_byte_order); + return *this; +} + +//------------------------------------------------------------------ +// Stream a uint32_t "uval" out to this stream. +//------------------------------------------------------------------ +Stream& +Stream::operator<< (uint32_t uval) +{ + PutHex32(uval, m_byte_order); + return *this; +} + +//------------------------------------------------------------------ +// Stream a uint64_t "uval" out to this stream. +//------------------------------------------------------------------ +Stream& +Stream::operator<< (uint64_t uval) +{ + PutHex64(uval, m_byte_order); + return *this; +} + +//------------------------------------------------------------------ +// Stream a int8_t "sval" out to this stream. +//------------------------------------------------------------------ +Stream& +Stream::operator<< (int8_t sval) +{ + Printf ("%i", (int)sval); + return *this; +} + +//------------------------------------------------------------------ +// Stream a int16_t "sval" out to this stream. +//------------------------------------------------------------------ +Stream& +Stream::operator<< (int16_t sval) +{ + Printf ("%i", (int)sval); + return *this; +} + +//------------------------------------------------------------------ +// Stream a int32_t "sval" out to this stream. +//------------------------------------------------------------------ +Stream& +Stream::operator<< (int32_t sval) +{ + Printf ("%i", (int)sval); + return *this; +} + +//------------------------------------------------------------------ +// Stream a int64_t "sval" out to this stream. +//------------------------------------------------------------------ +Stream& +Stream::operator<< (int64_t sval) +{ + Printf ("%" PRIi64, sval); + return *this; +} + +//------------------------------------------------------------------ +// Get the current indentation level +//------------------------------------------------------------------ +int +Stream::GetIndentLevel() const +{ + return m_indent_level; +} + +//------------------------------------------------------------------ +// Set the current indentation level +//------------------------------------------------------------------ +void +Stream::SetIndentLevel(int indent_level) +{ + m_indent_level = indent_level; +} + +//------------------------------------------------------------------ +// Increment the current indentation level +//------------------------------------------------------------------ +void +Stream::IndentMore(int amount) +{ + m_indent_level += amount; +} + +//------------------------------------------------------------------ +// Decrement the current indentation level +//------------------------------------------------------------------ +void +Stream::IndentLess (int amount) +{ + if (m_indent_level >= amount) + m_indent_level -= amount; + else + m_indent_level = 0; +} + +//------------------------------------------------------------------ +// Get the address size in bytes +//------------------------------------------------------------------ +uint32_t +Stream::GetAddressByteSize() const +{ + return m_addr_size; +} + +//------------------------------------------------------------------ +// Set the address size in bytes +//------------------------------------------------------------------ +void +Stream::SetAddressByteSize(uint32_t addr_size) +{ + m_addr_size = addr_size; +} + +//------------------------------------------------------------------ +// Returns true if the verbose flag bit is set in this stream. +//------------------------------------------------------------------ +bool +Stream::GetVerbose() const +{ + return m_flags.Test(eVerbose); +} + +//------------------------------------------------------------------ +// Returns true if the debug flag bit is set in this stream. +//------------------------------------------------------------------ +bool +Stream::GetDebug() const +{ + return m_flags.Test(eDebug); +} + +//------------------------------------------------------------------ +// The flags get accessor +//------------------------------------------------------------------ +Flags& +Stream::GetFlags() +{ + return m_flags; +} + +//------------------------------------------------------------------ +// The flags const get accessor +//------------------------------------------------------------------ +const Flags& +Stream::GetFlags() const +{ + return m_flags; +} + +//------------------------------------------------------------------ +// The byte order get accessor +//------------------------------------------------------------------ + +lldb::ByteOrder +Stream::GetByteOrder() const +{ + return m_byte_order; +} + +size_t +Stream::PrintfAsRawHex8 (const char *format, ...) +{ + va_list args; + va_list args_copy; + va_start (args, format); + va_copy (args, args_copy); // Copy this so we + + char str[1024]; + size_t bytes_written = 0; + // Try and format our string into a fixed buffer first and see if it fits + size_t length = ::vsnprintf (str, sizeof(str), format, args); + if (length < sizeof(str)) + { + // The formatted string fit into our stack based buffer, so we can just + // append that to our packet + for (size_t i=0; i> 4) & 0xf]; + nibble_chars[1] = g_hex_to_ascii_hex_char[(uvalue >> 0) & 0xf]; + bytes_written = Write (nibble_chars, sizeof(nibble_chars)); + } + return bytes_written; +} + +size_t +Stream::PutHex8 (uint8_t uvalue) +{ + return _PutHex8 (uvalue, m_flags.Test(eAddPrefix)); +} + +size_t +Stream::PutHex16 (uint16_t uvalue, ByteOrder byte_order) +{ + if (byte_order == eByteOrderInvalid) + byte_order = m_byte_order; + + bool add_prefix = m_flags.Test(eAddPrefix); + size_t bytes_written = 0; + if (byte_order == eByteOrderLittle) + { + for (size_t byte = 0; byte < sizeof(uvalue); ++byte, add_prefix = false) + bytes_written += _PutHex8 ((uint8_t)(uvalue >> (byte * 8)), add_prefix); + } + else + { + for (size_t byte = sizeof(uvalue)-1; byte < sizeof(uvalue); --byte, add_prefix = false) + bytes_written += _PutHex8 ((uint8_t)(uvalue >> (byte * 8)), add_prefix); + } + return bytes_written; +} + +size_t +Stream::PutHex32(uint32_t uvalue, ByteOrder byte_order) +{ + if (byte_order == eByteOrderInvalid) + byte_order = m_byte_order; + + bool add_prefix = m_flags.Test(eAddPrefix); + size_t bytes_written = 0; + if (byte_order == eByteOrderLittle) + { + for (size_t byte = 0; byte < sizeof(uvalue); ++byte, add_prefix = false) + bytes_written += _PutHex8 ((uint8_t)(uvalue >> (byte * 8)), add_prefix); + } + else + { + for (size_t byte = sizeof(uvalue)-1; byte < sizeof(uvalue); --byte, add_prefix = false) + bytes_written += _PutHex8 ((uint8_t)(uvalue >> (byte * 8)), add_prefix); + } + return bytes_written; +} + +size_t +Stream::PutHex64(uint64_t uvalue, ByteOrder byte_order) +{ + if (byte_order == eByteOrderInvalid) + byte_order = m_byte_order; + + bool add_prefix = m_flags.Test(eAddPrefix); + size_t bytes_written = 0; + if (byte_order == eByteOrderLittle) + { + for (size_t byte = 0; byte < sizeof(uvalue); ++byte, add_prefix = false) + bytes_written += _PutHex8 ((uint8_t)(uvalue >> (byte * 8)), add_prefix); + } + else + { + for (size_t byte = sizeof(uvalue)-1; byte < sizeof(uvalue); --byte, add_prefix = false) + bytes_written += _PutHex8 ((uint8_t)(uvalue >> (byte * 8)), add_prefix); + } + return bytes_written; +} + +size_t +Stream::PutMaxHex64 +( + uint64_t uvalue, + size_t byte_size, + lldb::ByteOrder byte_order +) +{ + switch (byte_size) + { + case 1: return PutHex8 ((uint8_t)uvalue); + case 2: return PutHex16 ((uint16_t)uvalue); + case 4: return PutHex32 ((uint32_t)uvalue); + case 8: return PutHex64 (uvalue); + } + return 0; +} + +size_t +Stream::PutPointer (void *ptr) +{ + return PutRawBytes (&ptr, sizeof(ptr), lldb::endian::InlHostByteOrder(), lldb::endian::InlHostByteOrder()); +} + +size_t +Stream::PutFloat(float f, ByteOrder byte_order) +{ + if (byte_order == eByteOrderInvalid) + byte_order = m_byte_order; + + return PutRawBytes (&f, sizeof(f), lldb::endian::InlHostByteOrder(), byte_order); +} + +size_t +Stream::PutDouble(double d, ByteOrder byte_order) +{ + if (byte_order == eByteOrderInvalid) + byte_order = m_byte_order; + + return PutRawBytes (&d, sizeof(d), lldb::endian::InlHostByteOrder(), byte_order); +} + +size_t +Stream::PutLongDouble(long double ld, ByteOrder byte_order) +{ + if (byte_order == eByteOrderInvalid) + byte_order = m_byte_order; + + return PutRawBytes (&ld, sizeof(ld), lldb::endian::InlHostByteOrder(), byte_order); +} + +size_t +Stream::PutRawBytes (const void *s, size_t src_len, ByteOrder src_byte_order, ByteOrder dst_byte_order) +{ + if (src_byte_order == eByteOrderInvalid) + src_byte_order = m_byte_order; + + if (dst_byte_order == eByteOrderInvalid) + dst_byte_order = m_byte_order; + + size_t bytes_written = 0; + const uint8_t *src = (const uint8_t *)s; + bool binary_was_set = m_flags.Test (eBinary); + if (!binary_was_set) + m_flags.Set (eBinary); + if (src_byte_order == dst_byte_order) + { + for (size_t i = 0; i < src_len; ++i) + bytes_written += _PutHex8 (src[i], false); + } + else + { + for (size_t i = src_len-1; i < src_len; --i) + bytes_written += _PutHex8 (src[i], false); + } + if (!binary_was_set) + m_flags.Clear (eBinary); + + return bytes_written; +} + +size_t +Stream::PutBytesAsRawHex8 (const void *s, size_t src_len, ByteOrder src_byte_order, ByteOrder dst_byte_order) +{ + if (src_byte_order == eByteOrderInvalid) + src_byte_order = m_byte_order; + + if (dst_byte_order == eByteOrderInvalid) + dst_byte_order = m_byte_order; + + size_t bytes_written = 0; + const uint8_t *src = (const uint8_t *)s; + bool binary_is_set = m_flags.Test(eBinary); + m_flags.Clear(eBinary); + if (src_byte_order == dst_byte_order) + { + for (size_t i = 0; i < src_len; ++i) + bytes_written += _PutHex8 (src[i], false); + } + else + { + for (size_t i = src_len-1; i < src_len; --i) + bytes_written += _PutHex8 (src[i], false); + } + if (binary_is_set) + m_flags.Set(eBinary); + + return bytes_written; +} + +size_t +Stream::PutCStringAsRawHex8 (const char *s) +{ + size_t bytes_written = 0; + bool binary_is_set = m_flags.Test(eBinary); + m_flags.Clear(eBinary); + do + { + bytes_written += _PutHex8 (*s, false); + ++s; + } while (*s); + if (binary_is_set) + m_flags.Set(eBinary); + return bytes_written; +} + +void +Stream::UnitTest(Stream *s) +{ + s->PutHex8(0x12); + + s->PutChar(' '); + s->PutHex16(0x3456, lldb::endian::InlHostByteOrder()); + s->PutChar(' '); + s->PutHex16(0x3456, eByteOrderBig); + s->PutChar(' '); + s->PutHex16(0x3456, eByteOrderLittle); + + s->PutChar(' '); + s->PutHex32(0x789abcde, lldb::endian::InlHostByteOrder()); + s->PutChar(' '); + s->PutHex32(0x789abcde, eByteOrderBig); + s->PutChar(' '); + s->PutHex32(0x789abcde, eByteOrderLittle); + + s->PutChar(' '); + s->PutHex64(0x1122334455667788ull, lldb::endian::InlHostByteOrder()); + s->PutChar(' '); + s->PutHex64(0x1122334455667788ull, eByteOrderBig); + s->PutChar(' '); + s->PutHex64(0x1122334455667788ull, eByteOrderLittle); + + const char *hola = "Hello World!!!"; + s->PutChar(' '); + s->PutCString (hola); + + s->PutChar(' '); + s->Write (hola, 5); + + s->PutChar(' '); + s->PutCStringAsRawHex8 (hola); + + s->PutChar(' '); + s->PutCStringAsRawHex8 ("01234"); + + s->PutChar(' '); + s->Printf ("pid=%i", 12733); + + s->PutChar(' '); + s->PrintfAsRawHex8 ("pid=%i", 12733); + s->PutChar('\n'); +} + diff --git a/contrib/llvm/tools/lldb/source/Core/StreamAsynchronousIO.cpp b/contrib/llvm/tools/lldb/source/Core/StreamAsynchronousIO.cpp new file mode 100644 index 00000000000..b9e5cdfde72 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Core/StreamAsynchronousIO.cpp @@ -0,0 +1,52 @@ +//===-- StreamBroadcast.cpp -------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include + +#include "lldb/lldb-private.h" +#include "lldb/Core/Broadcaster.h" +#include "lldb/Core/Event.h" +#include "lldb/Core/StreamAsynchronousIO.h" + +using namespace lldb; +using namespace lldb_private; + + +StreamAsynchronousIO::StreamAsynchronousIO (Broadcaster &broadcaster, uint32_t broadcast_event_type) : + Stream (0, 4, eByteOrderBig), + m_broadcaster (broadcaster), + m_broadcast_event_type (broadcast_event_type), + m_accumulated_data () +{ +} + +StreamAsynchronousIO::~StreamAsynchronousIO () +{ +} + +void +StreamAsynchronousIO::Flush () +{ + if (m_accumulated_data.GetSize() > 0) + { + std::unique_ptr data_bytes_ap (new EventDataBytes); + // Let's swap the bytes to avoid LARGE string copies. + data_bytes_ap->SwapBytes (m_accumulated_data.GetString()); + EventSP new_event_sp (new Event (m_broadcast_event_type, data_bytes_ap.release())); + m_broadcaster.BroadcastEvent (new_event_sp); + m_accumulated_data.Clear(); + } +} + +size_t +StreamAsynchronousIO::Write (const void *s, size_t length) +{ + m_accumulated_data.Write (s, length); + return length; +} diff --git a/contrib/llvm/tools/lldb/source/Core/StreamCallback.cpp b/contrib/llvm/tools/lldb/source/Core/StreamCallback.cpp new file mode 100644 index 00000000000..d144b16755e --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Core/StreamCallback.cpp @@ -0,0 +1,64 @@ +//===-- StreamCallback.cpp -------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include + +#include "lldb/lldb-private.h" +#include "lldb/Core/Broadcaster.h" +#include "lldb/Core/Event.h" +#include "lldb/Core/StreamCallback.h" +#include "lldb/Host/Host.h" + +using namespace lldb; +using namespace lldb_private; + + +StreamCallback::StreamCallback (lldb::LogOutputCallback callback, void *baton) : + Stream (0, 4, eByteOrderBig), + m_callback (callback), + m_baton (baton), + m_accumulated_data (), + m_collection_mutex () +{ +} + +StreamCallback::~StreamCallback () +{ +} + +StreamString & +StreamCallback::FindStreamForThread(lldb::tid_t cur_tid) +{ + Mutex::Locker locker(m_collection_mutex); + collection::iterator iter = m_accumulated_data.find (cur_tid); + if (iter == m_accumulated_data.end()) + { + std::pair ret; + ret = m_accumulated_data.insert(std::pair(cur_tid, StreamString())); + iter = ret.first; + } + return (*iter).second; +} + +void +StreamCallback::Flush () +{ + lldb::tid_t cur_tid = Host::GetCurrentThreadID(); + StreamString &out_stream = FindStreamForThread(cur_tid); + m_callback (out_stream.GetData(), m_baton); + out_stream.Clear(); +} + +size_t +StreamCallback::Write (const void *s, size_t length) +{ + lldb::tid_t cur_tid = Host::GetCurrentThreadID(); + FindStreamForThread(cur_tid).Write (s, length); + return length; +} diff --git a/contrib/llvm/tools/lldb/source/Core/StreamFile.cpp b/contrib/llvm/tools/lldb/source/Core/StreamFile.cpp new file mode 100644 index 00000000000..9a4eb796dbe --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Core/StreamFile.cpp @@ -0,0 +1,72 @@ +//===-- StreamFile.cpp ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Core/StreamFile.h" + +// C Includes +#include +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/Error.h" + + +using namespace lldb; +using namespace lldb_private; + +//---------------------------------------------------------------------- +// StreamFile constructor +//---------------------------------------------------------------------- +StreamFile::StreamFile () : + Stream (), + m_file () +{ +} + +StreamFile::StreamFile (uint32_t flags, uint32_t addr_size, ByteOrder byte_order) : + Stream (flags, addr_size, byte_order), + m_file () +{ +} + +StreamFile::StreamFile (int fd, bool transfer_ownership) : + Stream (), + m_file (fd, transfer_ownership) +{ +} + +StreamFile::StreamFile (FILE *fh, bool transfer_ownership) : + Stream (), + m_file (fh, transfer_ownership) +{ +} + +StreamFile::StreamFile (const char *path) : + Stream (), + m_file (path, File::eOpenOptionWrite | File::eOpenOptionCanCreate, File::ePermissionsDefault) +{ +} + + +StreamFile::~StreamFile() +{ +} + +void +StreamFile::Flush () +{ + m_file.Flush(); +} + +size_t +StreamFile::Write (const void *s, size_t length) +{ + m_file.Write (s, length); + return length; +} diff --git a/contrib/llvm/tools/lldb/source/Core/StreamString.cpp b/contrib/llvm/tools/lldb/source/Core/StreamString.cpp new file mode 100644 index 00000000000..8d7d039fd65 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Core/StreamString.cpp @@ -0,0 +1,100 @@ +//===-- StreamString.cpp ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Core/StreamString.h" +#include + +using namespace lldb; +using namespace lldb_private; + +StreamString::StreamString () : + Stream (0, 4, eByteOrderBig) +{ +} + +StreamString::StreamString(uint32_t flags, uint32_t addr_size, ByteOrder byte_order) : + Stream (flags, addr_size, byte_order), + m_packet () +{ +} + +StreamString::~StreamString() +{ +} + +void +StreamString::Flush () +{ + // Nothing to do when flushing a buffer based stream... +} + +size_t +StreamString::Write (const void *s, size_t length) +{ + m_packet.append ((char *)s, length); + return length; +} + +void +StreamString::Clear() +{ + m_packet.clear(); +} + +bool +StreamString::Empty() const +{ + return GetSize() == 0; +} + +const char * +StreamString::GetData () const +{ + return m_packet.c_str(); +} + +size_t +StreamString::GetSize () const +{ + return m_packet.size(); +} + +std::string & +StreamString::GetString() +{ + return m_packet; +} + +const std::string & +StreamString::GetString() const +{ + return m_packet; +} + +void +StreamString::FillLastLineToColumn (uint32_t column, char fill_char) +{ + const size_t length = m_packet.size(); + size_t last_line_begin_pos = m_packet.find_last_of("\r\n"); + if (last_line_begin_pos == std::string::npos) + { + last_line_begin_pos = 0; + } + else + { + ++last_line_begin_pos; + } + + const size_t line_columns = length - last_line_begin_pos; + if (column > line_columns) + { + m_packet.append(column - line_columns, fill_char); + } +} + diff --git a/contrib/llvm/tools/lldb/source/Core/StringList.cpp b/contrib/llvm/tools/lldb/source/Core/StringList.cpp new file mode 100644 index 00000000000..99497511678 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Core/StringList.cpp @@ -0,0 +1,290 @@ +//===-- StringList.cpp ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Core/StringList.h" + +#include "lldb/Core/StreamString.h" +#include "lldb/Host/FileSpec.h" + +#include + +using namespace lldb_private; + +StringList::StringList () : + m_strings () +{ +} + +StringList::StringList (const char *str) : + m_strings () +{ + if (str) + m_strings.push_back (str); +} + +StringList::StringList (const char **strv, int strc) : + m_strings () +{ + for (int i = 0; i < strc; ++i) + { + if (strv[i]) + m_strings.push_back (strv[i]); + } +} + +StringList::~StringList () +{ +} + +void +StringList::AppendString (const char *str) +{ + if (str) + m_strings.push_back (str); +} + +void +StringList::AppendString (const std::string &s) +{ + m_strings.push_back (s); +} + +void +StringList::AppendString (const char *str, size_t str_len) +{ + if (str) + m_strings.push_back (std::string (str, str_len)); +} + +void +StringList::AppendList (const char **strv, int strc) +{ + for (int i = 0; i < strc; ++i) + { + if (strv[i]) + m_strings.push_back (strv[i]); + } +} + +void +StringList::AppendList (StringList strings) +{ + size_t len = strings.GetSize(); + + for (size_t i = 0; i < len; ++i) + m_strings.push_back (strings.GetStringAtIndex(i)); +} + +bool +StringList::ReadFileLines (FileSpec &input_file) +{ + return input_file.ReadFileLines (m_strings); +} + +size_t +StringList::GetSize () const +{ + return m_strings.size(); +} + +const char * +StringList::GetStringAtIndex (size_t idx) const +{ + if (idx < m_strings.size()) + return m_strings[idx].c_str(); + return NULL; +} + +void +StringList::Join (const char *separator, Stream &strm) +{ + size_t size = GetSize(); + + if (size == 0) + return; + + for (uint32_t i = 0; i < size; ++i) + { + if (i > 0) + strm.PutCString(separator); + strm.PutCString(GetStringAtIndex(i)); + } +} + +void +StringList::Clear () +{ + m_strings.clear(); +} + +void +StringList::LongestCommonPrefix (std::string &common_prefix) +{ + //arg_sstr_collection::iterator pos, end = m_args.end(); + size_t pos = 0; + size_t end = m_strings.size(); + + if (pos == end) + common_prefix.clear(); + else + common_prefix = m_strings[pos]; + + for (++pos; pos != end; ++pos) + { + size_t new_size = strlen (m_strings[pos].c_str()); + + // First trim common_prefix if it is longer than the current element: + if (common_prefix.size() > new_size) + common_prefix.erase (new_size); + + // Then trim it at the first disparity: + + for (size_t i = 0; i < common_prefix.size(); i++) + { + if (m_strings[pos][i] != common_prefix[i]) + { + common_prefix.erase(i); + break; + } + } + + // If we've emptied the common prefix, we're done. + if (common_prefix.empty()) + break; + } +} + +void +StringList::InsertStringAtIndex (size_t idx, const char *str) +{ + if (str) + { + if (idx < m_strings.size()) + m_strings.insert (m_strings.begin() + idx, str); + else + m_strings.push_back (str); + } +} + +void +StringList::DeleteStringAtIndex (size_t idx) +{ + if (idx < m_strings.size()) + m_strings.erase (m_strings.begin() + idx); +} + +size_t +StringList::SplitIntoLines (const char *lines, size_t len) +{ + const size_t orig_size = m_strings.size(); + + if (len == 0) + return 0; + + const char *k_newline_chars = "\r\n"; + const char *p = lines; + const char *end = lines + len; + while (p < end) + { + size_t count = strcspn (p, k_newline_chars); + if (count == 0) + { + if (p[count] == '\r' || p[count] == '\n') + m_strings.push_back(std::string()); + else + break; + } + else + { + if (p + count > end) + count = end - p; + m_strings.push_back(std::string(p, count)); + } + if (p[count] == '\r' && p[count+1] == '\n') + count++; // Skip an extra newline char for the DOS newline + count++; // Skip the newline character + p += count; + } + return m_strings.size() - orig_size; +} + +void +StringList::RemoveBlankLines () +{ + if (GetSize() == 0) + return; + + size_t idx = 0; + while (idx < m_strings.size()) + { + if (m_strings[idx].empty()) + DeleteStringAtIndex(idx); + else + idx++; + } +} + +std::string +StringList::CopyList(const char* item_preamble, + const char* items_sep) +{ + StreamString strm; + for (size_t i = 0; i < GetSize(); i++) + { + if (i && items_sep && items_sep[0]) + strm << items_sep; + if (item_preamble) + strm << item_preamble; + strm << GetStringAtIndex(i); + } + return std::string(strm.GetData()); +} + +StringList& +StringList::operator << (const char* str) +{ + AppendString(str); + return *this; +} + +StringList& +StringList::operator << (StringList strings) +{ + AppendList(strings); + return *this; +} + +size_t +StringList::AutoComplete (const char *s, StringList &matches, size_t &exact_idx) const +{ + matches.Clear(); + exact_idx = SIZE_MAX; + if (s && s[0]) + { + const size_t s_len = strlen (s); + const size_t num_strings = m_strings.size(); + + for (size_t i=0; i +#include +#include + +#include "lldb/Core/Stream.h" +#include "lldb/Host/Mutex.h" + +#include + +using namespace lldb_private; + +#define TIMER_INDENT_AMOUNT 2 +static bool g_quiet = true; +uint32_t Timer::g_depth = 0; +uint32_t Timer::g_display_depth = 0; +FILE * Timer::g_file = NULL; +typedef std::vector TimerStack; +typedef std::map TimerCategoryMap; +static pthread_key_t g_key; + +static Mutex & +GetCategoryMutex() +{ + static Mutex g_category_mutex(Mutex::eMutexTypeNormal); + return g_category_mutex; +} + +static TimerCategoryMap & +GetCategoryMap() +{ + static TimerCategoryMap g_category_map; + return g_category_map; +} + + +static TimerStack * +GetTimerStackForCurrentThread () +{ + void *timer_stack = ::pthread_getspecific (g_key); + if (timer_stack == NULL) + { + ::pthread_setspecific (g_key, new TimerStack); + timer_stack = ::pthread_getspecific (g_key); + } + return (TimerStack *)timer_stack; +} + +void +ThreadSpecificCleanup (void *p) +{ + delete (TimerStack *)p; +} + +void +Timer::SetQuiet (bool value) +{ + g_quiet = value; +} + +void +Timer::Initialize () +{ + Timer::g_file = stdout; + ::pthread_key_create (&g_key, ThreadSpecificCleanup); + +} + +Timer::Timer (const char *category, const char *format, ...) : + m_category (category), + m_total_start (), + m_timer_start (), + m_total_ticks (0), + m_timer_ticks (0) +{ + if (g_depth++ < g_display_depth) + { + if (g_quiet == false) + { + // Indent + ::fprintf (g_file, "%*s", g_depth * TIMER_INDENT_AMOUNT, ""); + // Print formatted string + va_list args; + va_start (args, format); + ::vfprintf (g_file, format, args); + va_end (args); + + // Newline + ::fprintf (g_file, "\n"); + } + TimeValue start_time(TimeValue::Now()); + m_total_start = start_time; + m_timer_start = start_time; + TimerStack *stack = GetTimerStackForCurrentThread (); + if (stack) + { + if (stack->empty() == false) + stack->back()->ChildStarted (start_time); + stack->push_back(this); + } + } +} + + +Timer::~Timer() +{ + if (m_total_start.IsValid()) + { + TimeValue stop_time = TimeValue::Now(); + if (m_total_start.IsValid()) + { + m_total_ticks += (stop_time - m_total_start); + m_total_start.Clear(); + } + if (m_timer_start.IsValid()) + { + m_timer_ticks += (stop_time - m_timer_start); + m_timer_start.Clear(); + } + + TimerStack *stack = GetTimerStackForCurrentThread (); + if (stack) + { + assert (stack->back() == this); + stack->pop_back(); + if (stack->empty() == false) + stack->back()->ChildStopped(stop_time); + } + + const uint64_t total_nsec_uint = GetTotalElapsedNanoSeconds(); + const uint64_t timer_nsec_uint = GetTimerElapsedNanoSeconds(); + const double total_nsec = total_nsec_uint; + const double timer_nsec = timer_nsec_uint; + + if (g_quiet == false) + { + + ::fprintf (g_file, + "%*s%.9f sec (%.9f sec)\n", + (g_depth - 1) *TIMER_INDENT_AMOUNT, "", + total_nsec / 1000000000.0, + timer_nsec / 1000000000.0); + } + + // Keep total results for each category so we can dump results. + Mutex::Locker locker (GetCategoryMutex()); + TimerCategoryMap &category_map = GetCategoryMap(); + category_map[m_category] += timer_nsec_uint; + } + if (g_depth > 0) + --g_depth; +} + +uint64_t +Timer::GetTotalElapsedNanoSeconds() +{ + uint64_t total_ticks = m_total_ticks; + + // If we are currently running, we need to add the current + // elapsed time of the running timer... + if (m_total_start.IsValid()) + total_ticks += (TimeValue::Now() - m_total_start); + + return total_ticks; +} + +uint64_t +Timer::GetTimerElapsedNanoSeconds() +{ + uint64_t timer_ticks = m_timer_ticks; + + // If we are currently running, we need to add the current + // elapsed time of the running timer... + if (m_timer_start.IsValid()) + timer_ticks += (TimeValue::Now() - m_timer_start); + + return timer_ticks; +} + +void +Timer::ChildStarted (const TimeValue& start_time) +{ + if (m_timer_start.IsValid()) + { + m_timer_ticks += (start_time - m_timer_start); + m_timer_start.Clear(); + } +} + +void +Timer::ChildStopped (const TimeValue& stop_time) +{ + if (!m_timer_start.IsValid()) + m_timer_start = stop_time; +} + +void +Timer::SetDisplayDepth (uint32_t depth) +{ + g_display_depth = depth; +} + + +/* binary function predicate: + * - returns whether a person is less than another person + */ +static bool +CategoryMapIteratorSortCriterion (const TimerCategoryMap::const_iterator& lhs, const TimerCategoryMap::const_iterator& rhs) +{ + return lhs->second > rhs->second; +} + + +void +Timer::ResetCategoryTimes () +{ + Mutex::Locker locker (GetCategoryMutex()); + TimerCategoryMap &category_map = GetCategoryMap(); + category_map.clear(); +} + +void +Timer::DumpCategoryTimes (Stream *s) +{ + Mutex::Locker locker (GetCategoryMutex()); + TimerCategoryMap &category_map = GetCategoryMap(); + std::vector sorted_iterators; + TimerCategoryMap::const_iterator pos, end = category_map.end(); + for (pos = category_map.begin(); pos != end; ++pos) + { + sorted_iterators.push_back (pos); + } + std::sort (sorted_iterators.begin(), sorted_iterators.end(), CategoryMapIteratorSortCriterion); + + const size_t count = sorted_iterators.size(); + for (size_t i=0; isecond; + s->Printf("%.9f sec for %s\n", timer_nsec / 1000000000.0, sorted_iterators[i]->first); + } +} diff --git a/contrib/llvm/tools/lldb/source/Core/UUID.cpp b/contrib/llvm/tools/lldb/source/Core/UUID.cpp new file mode 100644 index 00000000000..c1b3eb13d65 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Core/UUID.cpp @@ -0,0 +1,279 @@ +//===-- UUID.cpp ------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Core/UUID.h" +// C Includes +#include +#include +#include + +// C++ Includes +#include + +// Other libraries and framework includes +// Project includes +#include "lldb/Core/Stream.h" + +namespace lldb_private { + +UUID::UUID() : m_num_uuid_bytes(16) +{ + ::memset (m_uuid, 0, sizeof(m_uuid)); +} + +UUID::UUID(const UUID& rhs) +{ + m_num_uuid_bytes = rhs.m_num_uuid_bytes; + ::memcpy (m_uuid, rhs.m_uuid, sizeof (m_uuid)); +} + +UUID::UUID (const void *uuid_bytes, uint32_t num_uuid_bytes) +{ + SetBytes (uuid_bytes, num_uuid_bytes); +} + +const UUID& +UUID::operator=(const UUID& rhs) +{ + if (this != &rhs) + { + m_num_uuid_bytes = rhs.m_num_uuid_bytes; + ::memcpy (m_uuid, rhs.m_uuid, sizeof (m_uuid)); + } + return *this; +} + +UUID::~UUID() +{ +} + +void +UUID::Clear() +{ + m_num_uuid_bytes = 16; + ::memset (m_uuid, 0, sizeof(m_uuid)); +} + +const void * +UUID::GetBytes() const +{ + return m_uuid; +} + +std::string +UUID::GetAsString (const char *separator) const +{ + std::string result; + char buf[256]; + if (!separator) + separator = "-"; + const uint8_t *u = (const uint8_t *)GetBytes(); + if (sizeof (buf) > (size_t)snprintf (buf, + sizeof (buf), + "%2.2X%2.2X%2.2X%2.2X%s%2.2X%2.2X%s%2.2X%2.2X%s%2.2X%2.2X%s%2.2X%2.2X%2.2X%2.2X%2.2X%2.2X", + u[0],u[1],u[2],u[3],separator, + u[4],u[5],separator, + u[6],u[7],separator, + u[8],u[9],separator, + u[10],u[11],u[12],u[13],u[14],u[15])) + { + result.append (buf); + if (m_num_uuid_bytes == 20) + { + if (sizeof (buf) > (size_t)snprintf (buf, sizeof (buf), "%s%2.2X%2.2X%2.2X%2.2X", separator,u[16],u[17],u[18],u[19])) + result.append (buf); + } + } + return result; +} + +void +UUID::Dump (Stream *s) const +{ + const uint8_t *u = (const uint8_t *)GetBytes(); + s->Printf ("%2.2X%2.2X%2.2X%2.2X-%2.2X%2.2X-%2.2X%2.2X-%2.2X%2.2X-%2.2X%2.2X%2.2X%2.2X%2.2X%2.2X", + u[0],u[1],u[2],u[3],u[4],u[5],u[6],u[7],u[8],u[9],u[10],u[11],u[12],u[13],u[14],u[15]); + if (m_num_uuid_bytes == 20) + { + s->Printf ("-%2.2X%2.2X%2.2X%2.2X", u[16],u[17],u[18],u[19]); + } +} + +bool +UUID::SetBytes (const void *uuid_bytes, uint32_t num_uuid_bytes) +{ + if (uuid_bytes) + { + switch (num_uuid_bytes) + { + case 20: + m_num_uuid_bytes = 20; + break; + case 16: + m_num_uuid_bytes = 16; + m_uuid[16] = m_uuid[17] = m_uuid[18] = m_uuid[19] = 0; + break; + default: + // Unsupported UUID byte size + m_num_uuid_bytes = 0; + break; + } + + if (m_num_uuid_bytes > 0) + { + ::memcpy (m_uuid, uuid_bytes, m_num_uuid_bytes); + return true; + } + } + ::memset (m_uuid, 0, sizeof(m_uuid)); + return false; +} + +size_t +UUID::GetByteSize() +{ + return m_num_uuid_bytes; +} + +bool +UUID::IsValid () const +{ + return m_uuid[0] || + m_uuid[1] || + m_uuid[2] || + m_uuid[3] || + m_uuid[4] || + m_uuid[5] || + m_uuid[6] || + m_uuid[7] || + m_uuid[8] || + m_uuid[9] || + m_uuid[10] || + m_uuid[11] || + m_uuid[12] || + m_uuid[13] || + m_uuid[14] || + m_uuid[15] || + m_uuid[16] || + m_uuid[17] || + m_uuid[18] || + m_uuid[19]; +} + +static inline int +xdigit_to_int (char ch) +{ + ch = tolower(ch); + if (ch >= 'a' && ch <= 'f') + return 10 + ch - 'a'; + return ch - '0'; +} + +size_t +UUID::DecodeUUIDBytesFromCString (const char *p, ValueType &uuid_bytes, const char **end, uint32_t num_uuid_bytes) +{ + size_t uuid_byte_idx = 0; + if (p) + { + while (*p) + { + if (isxdigit(p[0]) && isxdigit(p[1])) + { + int hi_nibble = xdigit_to_int(p[0]); + int lo_nibble = xdigit_to_int(p[1]); + // Translate the two hex nibble characters into a byte + uuid_bytes[uuid_byte_idx] = (hi_nibble << 4) + lo_nibble; + + // Skip both hex digits + p += 2; + + // Increment the byte that we are decoding within the UUID value + // and break out if we are done + if (++uuid_byte_idx == num_uuid_bytes) + break; + } + else if (*p == '-') + { + // Skip dashes + p++; + } + else + { + // UUID values can only consist of hex characters and '-' chars + break; + } + } + } + if (end) + *end = p; + // Clear trailing bytes to 0. + for (uint32_t i = uuid_byte_idx; i < sizeof(ValueType); i++) + uuid_bytes[i] = 0; + return uuid_byte_idx; +} +size_t +UUID::SetFromCString (const char *cstr, uint32_t num_uuid_bytes) +{ + if (cstr == NULL) + return 0; + + const char *p = cstr; + + // Skip leading whitespace characters + while (isspace(*p)) + ++p; + + const size_t uuid_byte_idx = UUID::DecodeUUIDBytesFromCString (p, m_uuid, &p, num_uuid_bytes); + + // If we successfully decoded a UUID, return the amount of characters that + // were consumed + if (uuid_byte_idx == num_uuid_bytes) + return p - cstr; + + // Else return zero to indicate we were not able to parse a UUID value + return 0; +} + +} + +bool +lldb_private::operator == (const lldb_private::UUID &lhs, const lldb_private::UUID &rhs) +{ + return ::memcmp (lhs.GetBytes(), rhs.GetBytes(), sizeof (lldb_private::UUID::ValueType)) == 0; +} + +bool +lldb_private::operator != (const lldb_private::UUID &lhs, const lldb_private::UUID &rhs) +{ + return ::memcmp (lhs.GetBytes(), rhs.GetBytes(), sizeof (lldb_private::UUID::ValueType)) != 0; +} + +bool +lldb_private::operator < (const lldb_private::UUID &lhs, const lldb_private::UUID &rhs) +{ + return ::memcmp (lhs.GetBytes(), rhs.GetBytes(), sizeof (lldb_private::UUID::ValueType)) < 0; +} + +bool +lldb_private::operator <= (const lldb_private::UUID &lhs, const lldb_private::UUID &rhs) +{ + return ::memcmp (lhs.GetBytes(), rhs.GetBytes(), sizeof (lldb_private::UUID::ValueType)) <= 0; +} + +bool +lldb_private::operator > (const lldb_private::UUID &lhs, const lldb_private::UUID &rhs) +{ + return ::memcmp (lhs.GetBytes(), rhs.GetBytes(), sizeof (lldb_private::UUID::ValueType)) > 0; +} + +bool +lldb_private::operator >= (const lldb_private::UUID &lhs, const lldb_private::UUID &rhs) +{ + return ::memcmp (lhs.GetBytes(), rhs.GetBytes(), sizeof (lldb_private::UUID::ValueType)) >= 0; +} diff --git a/contrib/llvm/tools/lldb/source/Core/UserID.cpp b/contrib/llvm/tools/lldb/source/Core/UserID.cpp new file mode 100644 index 00000000000..f3d6e5b2218 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Core/UserID.cpp @@ -0,0 +1,23 @@ +//===-- UserID.cpp ----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Core/UserID.h" +#include "lldb/Core/Stream.h" + +#include + +using namespace lldb; +using namespace lldb_private; + +Stream& +lldb_private::operator << (Stream& strm, const UserID& uid) +{ + strm.Printf("{0x%8.8" PRIx64 "}", uid.GetID()); + return strm; +} diff --git a/contrib/llvm/tools/lldb/source/Core/UserSettingsController.cpp b/contrib/llvm/tools/lldb/source/Core/UserSettingsController.cpp new file mode 100644 index 00000000000..63a5dd9ed51 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Core/UserSettingsController.cpp @@ -0,0 +1,111 @@ +//====-- UserSettingsController.cpp ------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include +#include + +#include "lldb/Core/UserSettingsController.h" +#include "lldb/Core/Error.h" +#include "lldb/Core/RegularExpression.h" +#include "lldb/Core/Stream.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Interpreter/OptionValueString.h" + +using namespace lldb; +using namespace lldb_private; + + +lldb::OptionValueSP +Properties::GetPropertyValue (const ExecutionContext *exe_ctx, + const char *path, + bool will_modify, + Error &error) const +{ + OptionValuePropertiesSP properties_sp (GetValueProperties ()); + if (properties_sp) + return properties_sp->GetSubValue(exe_ctx, path, will_modify, error); + return lldb::OptionValueSP(); +} + +Error +Properties::SetPropertyValue (const ExecutionContext *exe_ctx, + VarSetOperationType op, + const char *path, + const char *value) +{ + OptionValuePropertiesSP properties_sp (GetValueProperties ()); + if (properties_sp) + return properties_sp->SetSubValue(exe_ctx, op, path, value); + Error error; + error.SetErrorString ("no properties"); + return error; +} + +void +Properties::DumpAllPropertyValues (const ExecutionContext *exe_ctx, Stream &strm, uint32_t dump_mask) +{ + OptionValuePropertiesSP properties_sp (GetValueProperties ()); + if (properties_sp) + return properties_sp->DumpValue (exe_ctx, strm, dump_mask); +} + +void +Properties::DumpAllDescriptions (CommandInterpreter &interpreter, + Stream &strm) const +{ + strm.PutCString("Top level variables:\n\n"); + + OptionValuePropertiesSP properties_sp (GetValueProperties ()); + if (properties_sp) + return properties_sp->DumpAllDescriptions (interpreter, strm); +} + + + +Error +Properties::DumpPropertyValue (const ExecutionContext *exe_ctx, Stream &strm, const char *property_path, uint32_t dump_mask) +{ + OptionValuePropertiesSP properties_sp (GetValueProperties ()); + if (properties_sp) + { + return properties_sp->DumpPropertyValue (exe_ctx, + strm, + property_path, + dump_mask); + } + Error error; + error.SetErrorString("empty property list"); + return error; +} + +size_t +Properties::Apropos (const char *keyword, std::vector &matching_properties) const +{ + OptionValuePropertiesSP properties_sp (GetValueProperties ()); + if (properties_sp) + { + properties_sp->Apropos (keyword, matching_properties); + } + return matching_properties.size(); +} + + +lldb::OptionValuePropertiesSP +Properties::GetSubProperty (const ExecutionContext *exe_ctx, + const ConstString &name) +{ + OptionValuePropertiesSP properties_sp (GetValueProperties ()); + if (properties_sp) + return properties_sp->GetSubProperty (exe_ctx, name); + return lldb::OptionValuePropertiesSP(); +} + diff --git a/contrib/llvm/tools/lldb/source/Core/VMRange.cpp b/contrib/llvm/tools/lldb/source/Core/VMRange.cpp new file mode 100644 index 00000000000..902489e1ff3 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Core/VMRange.cpp @@ -0,0 +1,112 @@ +//===-- VMRange.cpp ---------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-private.h" + +#include "lldb/Core/Stream.h" +#include "lldb/Core/VMRange.h" +#include + +using namespace lldb; +using namespace lldb_private; + +bool +VMRange::ContainsValue(const VMRange::collection& coll, lldb::addr_t value) +{ + ValueInRangeUnaryPredicate in_range_predicate(value); + VMRange::const_iterator pos; + VMRange::const_iterator end = coll.end(); + pos = std::find_if( coll.begin(), end, in_range_predicate ); + if (pos != end) + return true; + return false; +} + +bool +VMRange::ContainsRange(const VMRange::collection& coll, const VMRange& range) +{ + RangeInRangeUnaryPredicate in_range_predicate(range); + VMRange::const_iterator pos; + VMRange::const_iterator end = coll.end(); + pos = std::find_if( coll.begin(), end, in_range_predicate ); + if (pos != end) + return true; + return false; +} + +size_t +VMRange::FindRangeIndexThatContainsValue (const VMRange::collection& coll, lldb::addr_t value) +{ + ValueInRangeUnaryPredicate in_range_predicate(value); + VMRange::const_iterator begin = coll.begin(); + VMRange::const_iterator end = coll.end(); + VMRange::const_iterator pos = std::find_if (begin, end, in_range_predicate); + if (pos != end) + return std::distance (begin, pos); + return UINT32_MAX; +} + +void +VMRange::Dump(Stream *s, lldb::addr_t offset, uint32_t addr_width) const +{ + s->AddressRange(offset + GetBaseAddress(), offset + GetEndAddress(), addr_width); +} + +bool +lldb_private::operator== (const VMRange& lhs, const VMRange& rhs) +{ + return lhs.GetBaseAddress() == rhs.GetBaseAddress() && lhs.GetEndAddress() == rhs.GetEndAddress(); +} + +bool +lldb_private::operator!= (const VMRange& lhs, const VMRange& rhs) +{ + return lhs.GetBaseAddress() != rhs.GetBaseAddress() || lhs.GetEndAddress() != rhs.GetEndAddress(); +} + +bool +lldb_private::operator< (const VMRange& lhs, const VMRange& rhs) +{ + if (lhs.GetBaseAddress() < rhs.GetBaseAddress()) + return true; + else if (lhs.GetBaseAddress() > rhs.GetBaseAddress()) + return false; + return lhs.GetEndAddress() < rhs.GetEndAddress(); +} + +bool +lldb_private::operator<= (const VMRange& lhs, const VMRange& rhs) +{ + if (lhs.GetBaseAddress() < rhs.GetBaseAddress()) + return true; + else if (lhs.GetBaseAddress() > rhs.GetBaseAddress()) + return false; + return lhs.GetEndAddress() <= rhs.GetEndAddress(); +} + +bool +lldb_private::operator> (const VMRange& lhs, const VMRange& rhs) +{ + if (lhs.GetBaseAddress() > rhs.GetBaseAddress()) + return true; + else if (lhs.GetBaseAddress() < rhs.GetBaseAddress()) + return false; + return lhs.GetEndAddress() > rhs.GetEndAddress(); +} + +bool +lldb_private::operator>= (const VMRange& lhs, const VMRange& rhs) +{ + if (lhs.GetBaseAddress() > rhs.GetBaseAddress()) + return true; + else if (lhs.GetBaseAddress() < rhs.GetBaseAddress()) + return false; + return lhs.GetEndAddress() >= rhs.GetEndAddress(); +} + diff --git a/contrib/llvm/tools/lldb/source/Core/Value.cpp b/contrib/llvm/tools/lldb/source/Core/Value.cpp new file mode 100644 index 00000000000..3fe75d3d4ce --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Core/Value.cpp @@ -0,0 +1,761 @@ +//===-- Value.cpp -----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Core/Value.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/DataBufferHeap.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/State.h" +#include "lldb/Core/Stream.h" +#include "lldb/Symbol/ClangASTType.h" +#include "lldb/Symbol/ClangASTContext.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Symbol/SymbolContext.h" +#include "lldb/Symbol/Type.h" +#include "lldb/Symbol/Variable.h" +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Target.h" + +using namespace lldb; +using namespace lldb_private; + +Value::Value() : + m_value (), + m_vector (), + m_clang_type (), + m_context (NULL), + m_value_type (eValueTypeScalar), + m_context_type (eContextTypeInvalid), + m_data_buffer () +{ +} + +Value::Value(const Scalar& scalar) : + m_value (scalar), + m_vector (), + m_clang_type (), + m_context (NULL), + m_value_type (eValueTypeScalar), + m_context_type (eContextTypeInvalid), + m_data_buffer () +{ +} + + +Value::Value(const uint8_t *bytes, int len) : + m_value (), + m_vector (), + m_clang_type (), + m_context (NULL), + m_value_type (eValueTypeHostAddress), + m_context_type (eContextTypeInvalid), + m_data_buffer () +{ + m_data_buffer.CopyData(bytes, len); + m_value = (uintptr_t)m_data_buffer.GetBytes(); +} + +Value::Value(const Value &v) : + m_value (v.m_value), + m_vector (v.m_vector), + m_clang_type (v.m_clang_type), + m_context (v.m_context), + m_value_type (v.m_value_type), + m_context_type (v.m_context_type), + m_data_buffer () +{ + if ((uintptr_t)v.m_value.ULongLong(LLDB_INVALID_ADDRESS) == (uintptr_t)v.m_data_buffer.GetBytes()) + { + m_data_buffer.CopyData(v.m_data_buffer.GetBytes(), + v.m_data_buffer.GetByteSize()); + + m_value = (uintptr_t)m_data_buffer.GetBytes(); + } +} + +Value & +Value::operator=(const Value &rhs) +{ + if (this != &rhs) + { + m_value = rhs.m_value; + m_vector = rhs.m_vector; + m_clang_type = rhs.m_clang_type; + m_context = rhs.m_context; + m_value_type = rhs.m_value_type; + m_context_type = rhs.m_context_type; + if ((uintptr_t)rhs.m_value.ULongLong(LLDB_INVALID_ADDRESS) == (uintptr_t)rhs.m_data_buffer.GetBytes()) + { + m_data_buffer.CopyData(rhs.m_data_buffer.GetBytes(), + rhs.m_data_buffer.GetByteSize()); + + m_value = (uintptr_t)m_data_buffer.GetBytes(); + } + } + return *this; +} + +void +Value::Dump (Stream* strm) +{ + m_value.GetValue (strm, true); + strm->Printf(", value_type = %s, context = %p, context_type = %s", + Value::GetValueTypeAsCString(m_value_type), + m_context, + Value::GetContextTypeAsCString(m_context_type)); +} + +Value::ValueType +Value::GetValueType() const +{ + return m_value_type; +} + +AddressType +Value::GetValueAddressType () const +{ + switch (m_value_type) + { + default: + case eValueTypeScalar: + break; + case eValueTypeLoadAddress: return eAddressTypeLoad; + case eValueTypeFileAddress: return eAddressTypeFile; + case eValueTypeHostAddress: return eAddressTypeHost; + } + return eAddressTypeInvalid; +} + +RegisterInfo * +Value::GetRegisterInfo() const +{ + if (m_context_type == eContextTypeRegisterInfo) + return static_cast (m_context); + return NULL; +} + +Type * +Value::GetType() +{ + if (m_context_type == eContextTypeLLDBType) + return static_cast (m_context); + return NULL; +} + +void +Value::ResizeData(size_t len) +{ + m_value_type = eValueTypeHostAddress; + m_data_buffer.SetByteSize(len); + m_value = (uintptr_t)m_data_buffer.GetBytes(); +} + +bool +Value::ValueOf(ExecutionContext *exe_ctx) +{ + switch (m_context_type) + { + case eContextTypeInvalid: + case eContextTypeRegisterInfo: // RegisterInfo * + case eContextTypeLLDBType: // Type * + break; + + case eContextTypeVariable: // Variable * + ResolveValue(exe_ctx); + return true; + } + return false; +} + +uint64_t +Value::GetValueByteSize (Error *error_ptr) +{ + uint64_t byte_size = 0; + + switch (m_context_type) + { + case eContextTypeRegisterInfo: // RegisterInfo * + if (GetRegisterInfo()) + byte_size = GetRegisterInfo()->byte_size; + break; + + case eContextTypeInvalid: + case eContextTypeLLDBType: // Type * + case eContextTypeVariable: // Variable * + { + const ClangASTType &ast_type = GetClangType(); + if (ast_type.IsValid()) + byte_size = ast_type.GetByteSize(); + } + break; + } + + if (error_ptr) + { + if (byte_size == 0) + { + if (error_ptr->Success()) + error_ptr->SetErrorString("Unable to determine byte size."); + } + else + { + error_ptr->Clear(); + } + } + return byte_size; +} + +const ClangASTType & +Value::GetClangType () +{ + if (!m_clang_type.IsValid()) + { + switch (m_context_type) + { + case eContextTypeInvalid: + break; + + case eContextTypeRegisterInfo: + break; // TODO: Eventually convert into a clang type? + + case eContextTypeLLDBType: + { + Type *lldb_type = GetType(); + if (lldb_type) + m_clang_type = lldb_type->GetClangForwardType(); + } + break; + + case eContextTypeVariable: + { + Variable *variable = GetVariable(); + if (variable) + { + Type *variable_type = variable->GetType(); + if (variable_type) + m_clang_type = variable_type->GetClangForwardType(); + } + } + break; + } + } + + return m_clang_type; +} + +void +Value::SetClangType (const ClangASTType &clang_type) +{ + m_clang_type = clang_type; +} + +lldb::Format +Value::GetValueDefaultFormat () +{ + switch (m_context_type) + { + case eContextTypeRegisterInfo: + if (GetRegisterInfo()) + return GetRegisterInfo()->format; + break; + + case eContextTypeInvalid: + case eContextTypeLLDBType: + case eContextTypeVariable: + { + const ClangASTType &ast_type = GetClangType(); + if (ast_type.IsValid()) + return ast_type.GetFormat(); + } + break; + + } + + // Return a good default in case we can't figure anything out + return eFormatHex; +} + +bool +Value::GetData (DataExtractor &data) +{ + switch (m_value_type) + { + default: + break; + + case eValueTypeScalar: + if (m_value.GetData (data)) + return true; + break; + + case eValueTypeLoadAddress: + case eValueTypeFileAddress: + case eValueTypeHostAddress: + if (m_data_buffer.GetByteSize()) + { + data.SetData(m_data_buffer.GetBytes(), m_data_buffer.GetByteSize(), data.GetByteOrder()); + return true; + } + break; + } + + return false; + +} + +Error +Value::GetValueAsData (ExecutionContext *exe_ctx, + DataExtractor &data, + uint32_t data_offset, + Module *module) +{ + data.Clear(); + + Error error; + lldb::addr_t address = LLDB_INVALID_ADDRESS; + AddressType address_type = eAddressTypeFile; + Address file_so_addr; + const ClangASTType &ast_type = GetClangType(); + switch (m_value_type) + { + case eValueTypeVector: + if (ast_type.IsValid()) + data.SetAddressByteSize (ast_type.GetPointerByteSize()); + else + data.SetAddressByteSize(sizeof(void *)); + data.SetData(m_vector.bytes, m_vector.length, m_vector.byte_order); + break; + + case eValueTypeScalar: + data.SetByteOrder (lldb::endian::InlHostByteOrder()); + if (ast_type.IsValid()) + data.SetAddressByteSize (ast_type.GetPointerByteSize()); + else + data.SetAddressByteSize(sizeof(void *)); + if (m_value.GetData (data)) + return error; // Success; + error.SetErrorStringWithFormat("extracting data from value failed"); + break; + + case eValueTypeLoadAddress: + if (exe_ctx == NULL) + { + error.SetErrorString ("can't read load address (no execution context)"); + } + else + { + Process *process = exe_ctx->GetProcessPtr(); + if (process == NULL || !process->IsAlive()) + { + Target *target = exe_ctx->GetTargetPtr(); + if (target) + { + // Allow expressions to run and evaluate things when the target + // has memory sections loaded. This allows you to use "target modules load" + // to load your executable and any shared libraries, then execute + // commands where you can look at types in data sections. + const SectionLoadList &target_sections = target->GetSectionLoadList(); + if (!target_sections.IsEmpty()) + { + address = m_value.ULongLong(LLDB_INVALID_ADDRESS); + if (target_sections.ResolveLoadAddress(address, file_so_addr)) + { + address_type = eAddressTypeLoad; + data.SetByteOrder(target->GetArchitecture().GetByteOrder()); + data.SetAddressByteSize(target->GetArchitecture().GetAddressByteSize()); + } + else + address = LLDB_INVALID_ADDRESS; + } +// else +// { +// ModuleSP exe_module_sp (target->GetExecutableModule()); +// if (exe_module_sp) +// { +// address = m_value.ULongLong(LLDB_INVALID_ADDRESS); +// if (address != LLDB_INVALID_ADDRESS) +// { +// if (exe_module_sp->ResolveFileAddress(address, file_so_addr)) +// { +// data.SetByteOrder(target->GetArchitecture().GetByteOrder()); +// data.SetAddressByteSize(target->GetArchitecture().GetAddressByteSize()); +// address_type = eAddressTypeFile; +// } +// else +// { +// address = LLDB_INVALID_ADDRESS; +// } +// } +// } +// } + } + else + { + error.SetErrorString ("can't read load address (invalid process)"); + } + } + else + { + address = m_value.ULongLong(LLDB_INVALID_ADDRESS); + address_type = eAddressTypeLoad; + data.SetByteOrder(process->GetTarget().GetArchitecture().GetByteOrder()); + data.SetAddressByteSize(process->GetTarget().GetArchitecture().GetAddressByteSize()); + } + } + break; + + case eValueTypeFileAddress: + if (exe_ctx == NULL) + { + error.SetErrorString ("can't read file address (no execution context)"); + } + else if (exe_ctx->GetTargetPtr() == NULL) + { + error.SetErrorString ("can't read file address (invalid target)"); + } + else + { + address = m_value.ULongLong(LLDB_INVALID_ADDRESS); + if (address == LLDB_INVALID_ADDRESS) + { + error.SetErrorString ("invalid file address"); + } + else + { + if (module == NULL) + { + // The only thing we can currently lock down to a module so that + // we can resolve a file address, is a variable. + Variable *variable = GetVariable(); + if (variable) + { + SymbolContext var_sc; + variable->CalculateSymbolContext(&var_sc); + module = var_sc.module_sp.get(); + } + } + + if (module) + { + bool resolved = false; + ObjectFile *objfile = module->GetObjectFile(); + if (objfile) + { + Address so_addr(address, objfile->GetSectionList()); + addr_t load_address = so_addr.GetLoadAddress (exe_ctx->GetTargetPtr()); + bool process_launched_and_stopped = exe_ctx->GetProcessPtr() + ? StateIsStoppedState(exe_ctx->GetProcessPtr()->GetState(), true /* must_exist */) + : false; + // Don't use the load address if the process has exited. + if (load_address != LLDB_INVALID_ADDRESS && process_launched_and_stopped) + { + resolved = true; + address = load_address; + address_type = eAddressTypeLoad; + data.SetByteOrder(exe_ctx->GetTargetRef().GetArchitecture().GetByteOrder()); + data.SetAddressByteSize(exe_ctx->GetTargetRef().GetArchitecture().GetAddressByteSize()); + } + else + { + if (so_addr.IsSectionOffset()) + { + resolved = true; + file_so_addr = so_addr; + data.SetByteOrder(objfile->GetByteOrder()); + data.SetAddressByteSize(objfile->GetAddressByteSize()); + } + } + } + if (!resolved) + { + Variable *variable = GetVariable(); + + if (module) + { + if (variable) + error.SetErrorStringWithFormat ("unable to resolve the module for file address 0x%" PRIx64 " for variable '%s' in %s", + address, + variable->GetName().AsCString(""), + module->GetFileSpec().GetPath().c_str()); + else + error.SetErrorStringWithFormat ("unable to resolve the module for file address 0x%" PRIx64 " in %s", + address, + module->GetFileSpec().GetPath().c_str()); + } + else + { + if (variable) + error.SetErrorStringWithFormat ("unable to resolve the module for file address 0x%" PRIx64 " for variable '%s'", + address, + variable->GetName().AsCString("")); + else + error.SetErrorStringWithFormat ("unable to resolve the module for file address 0x%" PRIx64, address); + } + } + } + else + { + // Can't convert a file address to anything valid without more + // context (which Module it came from) + error.SetErrorString ("can't read memory from file address without more context"); + } + } + } + break; + + case eValueTypeHostAddress: + address = m_value.ULongLong(LLDB_INVALID_ADDRESS); + address_type = eAddressTypeHost; + if (exe_ctx) + { + Target *target = exe_ctx->GetTargetPtr(); + if (target) + { + data.SetByteOrder(target->GetArchitecture().GetByteOrder()); + data.SetAddressByteSize(target->GetArchitecture().GetAddressByteSize()); + break; + } + } + // fallback to host settings + data.SetByteOrder(lldb::endian::InlHostByteOrder()); + data.SetAddressByteSize(sizeof(void *)); + break; + } + + // Bail if we encountered any errors + if (error.Fail()) + return error; + + if (address == LLDB_INVALID_ADDRESS) + { + error.SetErrorStringWithFormat ("invalid %s address", address_type == eAddressTypeHost ? "host" : "load"); + return error; + } + + // If we got here, we need to read the value from memory + size_t byte_size = GetValueByteSize (&error); + + // Bail if we encountered any errors getting the byte size + if (error.Fail()) + return error; + + // Make sure we have enough room within "data", and if we don't make + // something large enough that does + if (!data.ValidOffsetForDataOfSize (data_offset, byte_size)) + { + DataBufferSP data_sp(new DataBufferHeap (data_offset + byte_size, '\0')); + data.SetData(data_sp); + } + + uint8_t* dst = const_cast(data.PeekData (data_offset, byte_size)); + if (dst != NULL) + { + if (address_type == eAddressTypeHost) + { + // The address is an address in this process, so just copy it + memcpy (dst, (uint8_t*)NULL + address, byte_size); + } + else if ((address_type == eAddressTypeLoad) || (address_type == eAddressTypeFile)) + { + if (file_so_addr.IsValid()) + { + // We have a file address that we were able to translate into a + // section offset address so we might be able to read this from + // the object files if we don't have a live process. Lets always + // try and read from the process if we have one though since we + // want to read the actual value by setting "prefer_file_cache" + // to false. + const bool prefer_file_cache = false; + if (exe_ctx->GetTargetRef().ReadMemory(file_so_addr, prefer_file_cache, dst, byte_size, error) != byte_size) + { + error.SetErrorStringWithFormat("read memory from 0x%" PRIx64 " failed", (uint64_t)address); + } + } + else + { + // The execution context might have a NULL process, but it + // might have a valid process in the exe_ctx->target, so use + // the ExecutionContext::GetProcess accessor to ensure we + // get the process if there is one. + Process *process = exe_ctx->GetProcessPtr(); + + if (process) + { + const size_t bytes_read = process->ReadMemory(address, dst, byte_size, error); + if (bytes_read != byte_size) + error.SetErrorStringWithFormat("read memory from 0x%" PRIx64 " failed (%u of %u bytes read)", + (uint64_t)address, + (uint32_t)bytes_read, + (uint32_t)byte_size); + } + else + { + error.SetErrorStringWithFormat("read memory from 0x%" PRIx64 " failed (invalid process)", (uint64_t)address); + } + } + } + else + { + error.SetErrorStringWithFormat ("unsupported AddressType value (%i)", address_type); + } + } + else + { + error.SetErrorStringWithFormat ("out of memory"); + } + + return error; +} + +Scalar & +Value::ResolveValue(ExecutionContext *exe_ctx) +{ + const ClangASTType &clang_type = GetClangType(); + if (clang_type.IsValid()) + { + switch (m_value_type) + { + case eValueTypeScalar: // raw scalar value + break; + + default: + case eValueTypeFileAddress: + case eValueTypeLoadAddress: // load address value + case eValueTypeHostAddress: // host address value (for memory in the process that is using liblldb) + { + DataExtractor data; + lldb::addr_t addr = m_value.ULongLong(LLDB_INVALID_ADDRESS); + Error error (GetValueAsData (exe_ctx, data, 0, NULL)); + if (error.Success()) + { + Scalar scalar; + if (clang_type.GetValueAsScalar (data, 0, data.GetByteSize(), scalar)) + { + m_value = scalar; + m_value_type = eValueTypeScalar; + } + else + { + if ((uintptr_t)addr != (uintptr_t)m_data_buffer.GetBytes()) + { + m_value.Clear(); + m_value_type = eValueTypeScalar; + } + } + } + else + { + if ((uintptr_t)addr != (uintptr_t)m_data_buffer.GetBytes()) + { + m_value.Clear(); + m_value_type = eValueTypeScalar; + } + } + } + break; + } + } + return m_value; +} + +Variable * +Value::GetVariable() +{ + if (m_context_type == eContextTypeVariable) + return static_cast (m_context); + return NULL; +} + +void +Value::Clear() +{ + m_value.Clear(); + m_vector.Clear(); + m_clang_type.Clear(); + m_value_type = eValueTypeScalar; + m_context = NULL; + m_context_type = eContextTypeInvalid; + m_data_buffer.Clear(); +} + + +const char * +Value::GetValueTypeAsCString (ValueType value_type) +{ + switch (value_type) + { + case eValueTypeScalar: return "scalar"; + case eValueTypeVector: return "vector"; + case eValueTypeFileAddress: return "file address"; + case eValueTypeLoadAddress: return "load address"; + case eValueTypeHostAddress: return "host address"; + }; + return "???"; +} + +const char * +Value::GetContextTypeAsCString (ContextType context_type) +{ + switch (context_type) + { + case eContextTypeInvalid: return "invalid"; + case eContextTypeRegisterInfo: return "RegisterInfo *"; + case eContextTypeLLDBType: return "Type *"; + case eContextTypeVariable: return "Variable *"; + }; + return "???"; +} + +ValueList::ValueList (const ValueList &rhs) +{ + m_values = rhs.m_values; +} + +const ValueList & +ValueList::operator= (const ValueList &rhs) +{ + m_values = rhs.m_values; + return *this; +} + +void +ValueList::PushValue (const Value &value) +{ + m_values.push_back (value); +} + +size_t +ValueList::GetSize() +{ + return m_values.size(); +} + +Value * +ValueList::GetValueAtIndex (size_t idx) +{ + if (idx < GetSize()) + { + return &(m_values[idx]); + } + else + return NULL; +} + +void +ValueList::Clear () +{ + m_values.clear(); +} + diff --git a/contrib/llvm/tools/lldb/source/Core/ValueObject.cpp b/contrib/llvm/tools/lldb/source/Core/ValueObject.cpp new file mode 100644 index 00000000000..a30cc1306c6 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Core/ValueObject.cpp @@ -0,0 +1,4199 @@ +//===-- ValueObject.cpp -----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "lldb/Core/ValueObject.h" + +// C Includes +#include + +// C++ Includes +// Other libraries and framework includes +#include "llvm/Support/raw_ostream.h" +#include "clang/AST/Type.h" + +// Project includes +#include "lldb/Core/DataBufferHeap.h" +#include "lldb/Core/Debugger.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Core/ValueObjectCast.h" +#include "lldb/Core/ValueObjectChild.h" +#include "lldb/Core/ValueObjectConstResult.h" +#include "lldb/Core/ValueObjectDynamicValue.h" +#include "lldb/Core/ValueObjectList.h" +#include "lldb/Core/ValueObjectMemory.h" +#include "lldb/Core/ValueObjectSyntheticFilter.h" + +#include "lldb/DataFormatters/DataVisualization.h" + +#include "lldb/Host/Endian.h" + +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Interpreter/ScriptInterpreterPython.h" + +#include "lldb/Symbol/ClangASTType.h" +#include "lldb/Symbol/ClangASTContext.h" +#include "lldb/Symbol/Type.h" + +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/LanguageRuntime.h" +#include "lldb/Target/ObjCLanguageRuntime.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_utility; + +static user_id_t g_value_obj_uid = 0; + +//---------------------------------------------------------------------- +// ValueObject constructor +//---------------------------------------------------------------------- +ValueObject::ValueObject (ValueObject &parent) : + UserID (++g_value_obj_uid), // Unique identifier for every value object + m_parent (&parent), + m_root (NULL), + m_update_point (parent.GetUpdatePoint ()), + m_name (), + m_data (), + m_value (), + m_error (), + m_value_str (), + m_old_value_str (), + m_location_str (), + m_summary_str (), + m_object_desc_str (), + m_manager(parent.GetManager()), + m_children (), + m_synthetic_children (), + m_dynamic_value (NULL), + m_synthetic_value(NULL), + m_deref_valobj(NULL), + m_format (eFormatDefault), + m_last_format (eFormatDefault), + m_last_format_mgr_revision(0), + m_type_summary_sp(), + m_type_format_sp(), + m_synthetic_children_sp(), + m_user_id_of_forced_summary(), + m_address_type_of_ptr_or_ref_children(eAddressTypeInvalid), + m_value_is_valid (false), + m_value_did_change (false), + m_children_count_valid (false), + m_old_value_valid (false), + m_is_deref_of_parent (false), + m_is_array_item_for_pointer(false), + m_is_bitfield_for_scalar(false), + m_is_child_at_offset(false), + m_is_getting_summary(false), + m_did_calculate_complete_objc_class_type(false) +{ + m_manager->ManageObject(this); +} + +//---------------------------------------------------------------------- +// ValueObject constructor +//---------------------------------------------------------------------- +ValueObject::ValueObject (ExecutionContextScope *exe_scope, + AddressType child_ptr_or_ref_addr_type) : + UserID (++g_value_obj_uid), // Unique identifier for every value object + m_parent (NULL), + m_root (NULL), + m_update_point (exe_scope), + m_name (), + m_data (), + m_value (), + m_error (), + m_value_str (), + m_old_value_str (), + m_location_str (), + m_summary_str (), + m_object_desc_str (), + m_manager(), + m_children (), + m_synthetic_children (), + m_dynamic_value (NULL), + m_synthetic_value(NULL), + m_deref_valobj(NULL), + m_format (eFormatDefault), + m_last_format (eFormatDefault), + m_last_format_mgr_revision(0), + m_type_summary_sp(), + m_type_format_sp(), + m_synthetic_children_sp(), + m_user_id_of_forced_summary(), + m_address_type_of_ptr_or_ref_children(child_ptr_or_ref_addr_type), + m_value_is_valid (false), + m_value_did_change (false), + m_children_count_valid (false), + m_old_value_valid (false), + m_is_deref_of_parent (false), + m_is_array_item_for_pointer(false), + m_is_bitfield_for_scalar(false), + m_is_child_at_offset(false), + m_is_getting_summary(false), + m_did_calculate_complete_objc_class_type(false) +{ + m_manager = new ValueObjectManager(); + m_manager->ManageObject (this); +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +ValueObject::~ValueObject () +{ +} + +bool +ValueObject::UpdateValueIfNeeded (bool update_format) +{ + + bool did_change_formats = false; + + if (update_format) + did_change_formats = UpdateFormatsIfNeeded(); + + // If this is a constant value, then our success is predicated on whether + // we have an error or not + if (GetIsConstant()) + { + // if you were asked to update your formatters, but did not get a chance to do it + // clear your own values (this serves the purpose of faking a stop-id for frozen + // objects (which are regarded as constant, but could have changes behind their backs + // because of the frozen-pointer depth limit) + // TODO: decouple summary from value and then remove this code and only force-clear the summary + if (update_format && !did_change_formats) + ClearUserVisibleData(eClearUserVisibleDataItemsSummary); + return m_error.Success(); + } + + bool first_update = m_update_point.IsFirstEvaluation(); + + if (m_update_point.NeedsUpdating()) + { + m_update_point.SetUpdated(); + + // Save the old value using swap to avoid a string copy which + // also will clear our m_value_str + if (m_value_str.empty()) + { + m_old_value_valid = false; + } + else + { + m_old_value_valid = true; + m_old_value_str.swap (m_value_str); + ClearUserVisibleData(eClearUserVisibleDataItemsValue); + } + + ClearUserVisibleData(); + + if (IsInScope()) + { + const bool value_was_valid = GetValueIsValid(); + SetValueDidChange (false); + + m_error.Clear(); + + // Call the pure virtual function to update the value + bool success = UpdateValue (); + + SetValueIsValid (success); + + if (first_update) + SetValueDidChange (false); + else if (!m_value_did_change && success == false) + { + // The value wasn't gotten successfully, so we mark this + // as changed if the value used to be valid and now isn't + SetValueDidChange (value_was_valid); + } + } + else + { + m_error.SetErrorString("out of scope"); + } + } + return m_error.Success(); +} + +bool +ValueObject::UpdateFormatsIfNeeded() +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_TYPES)); + if (log) + log->Printf("[%s %p] checking for FormatManager revisions. ValueObject rev: %d - Global rev: %d", + GetName().GetCString(), + this, + m_last_format_mgr_revision, + DataVisualization::GetCurrentRevision()); + + bool any_change = false; + + if ( (m_last_format_mgr_revision != DataVisualization::GetCurrentRevision())) + { + SetValueFormat(DataVisualization::ValueFormats::GetFormat (*this, eNoDynamicValues)); + SetSummaryFormat(DataVisualization::GetSummaryFormat (*this, GetDynamicValueType())); +#ifndef LLDB_DISABLE_PYTHON + SetSyntheticChildren(DataVisualization::GetSyntheticChildren (*this, GetDynamicValueType())); +#endif + + m_last_format_mgr_revision = DataVisualization::GetCurrentRevision(); + + any_change = true; + } + + return any_change; + +} + +void +ValueObject::SetNeedsUpdate () +{ + m_update_point.SetNeedsUpdate(); + // We have to clear the value string here so ConstResult children will notice if their values are + // changed by hand (i.e. with SetValueAsCString). + ClearUserVisibleData(eClearUserVisibleDataItemsValue); +} + +void +ValueObject::ClearDynamicTypeInformation () +{ + m_did_calculate_complete_objc_class_type = false; + m_last_format_mgr_revision = 0; + m_override_type = ClangASTType(); + SetValueFormat(lldb::TypeFormatImplSP()); + SetSummaryFormat(lldb::TypeSummaryImplSP()); + SetSyntheticChildren(lldb::SyntheticChildrenSP()); +} + +ClangASTType +ValueObject::MaybeCalculateCompleteType () +{ + ClangASTType clang_type(GetClangTypeImpl()); + + if (m_did_calculate_complete_objc_class_type) + { + if (m_override_type.IsValid()) + return m_override_type; + else + return clang_type; + } + + ClangASTType class_type; + bool is_pointer_type = false; + + if (clang_type.IsObjCObjectPointerType(&class_type)) + { + is_pointer_type = true; + } + else if (clang_type.IsObjCObjectOrInterfaceType()) + { + class_type = clang_type; + } + else + { + return clang_type; + } + + m_did_calculate_complete_objc_class_type = true; + + if (class_type) + { + ConstString class_name (class_type.GetConstTypeName()); + + if (class_name) + { + ProcessSP process_sp(GetUpdatePoint().GetExecutionContextRef().GetProcessSP()); + + if (process_sp) + { + ObjCLanguageRuntime *objc_language_runtime(process_sp->GetObjCLanguageRuntime()); + + if (objc_language_runtime) + { + TypeSP complete_objc_class_type_sp = objc_language_runtime->LookupInCompleteClassCache(class_name); + + if (complete_objc_class_type_sp) + { + ClangASTType complete_class(complete_objc_class_type_sp->GetClangFullType()); + + if (complete_class.GetCompleteType()) + { + if (is_pointer_type) + { + m_override_type = complete_class.GetPointerType(); + } + else + { + m_override_type = complete_class; + } + + if (m_override_type.IsValid()) + return m_override_type; + } + } + } + } + } + } + return clang_type; +} + +ClangASTType +ValueObject::GetClangType () +{ + return MaybeCalculateCompleteType(); +} + +DataExtractor & +ValueObject::GetDataExtractor () +{ + UpdateValueIfNeeded(false); + return m_data; +} + +const Error & +ValueObject::GetError() +{ + UpdateValueIfNeeded(false); + return m_error; +} + +const ConstString & +ValueObject::GetName() const +{ + return m_name; +} + +const char * +ValueObject::GetLocationAsCString () +{ + return GetLocationAsCStringImpl(m_value, + m_data); +} + +const char * +ValueObject::GetLocationAsCStringImpl (const Value& value, + const DataExtractor& data) +{ + if (UpdateValueIfNeeded(false)) + { + if (m_location_str.empty()) + { + StreamString sstr; + + Value::ValueType value_type = value.GetValueType(); + + switch (value_type) + { + case Value::eValueTypeScalar: + case Value::eValueTypeVector: + if (value.GetContextType() == Value::eContextTypeRegisterInfo) + { + RegisterInfo *reg_info = value.GetRegisterInfo(); + if (reg_info) + { + if (reg_info->name) + m_location_str = reg_info->name; + else if (reg_info->alt_name) + m_location_str = reg_info->alt_name; + if (m_location_str.empty()) + m_location_str = (reg_info->encoding == lldb::eEncodingVector) ? "vector" : "scalar"; + } + } + if (m_location_str.empty()) + m_location_str = (value_type == Value::eValueTypeVector) ? "vector" : "scalar"; + break; + + case Value::eValueTypeLoadAddress: + case Value::eValueTypeFileAddress: + case Value::eValueTypeHostAddress: + { + uint32_t addr_nibble_size = data.GetAddressByteSize() * 2; + sstr.Printf("0x%*.*llx", addr_nibble_size, addr_nibble_size, value.GetScalar().ULongLong(LLDB_INVALID_ADDRESS)); + m_location_str.swap(sstr.GetString()); + } + break; + } + } + } + return m_location_str.c_str(); +} + +Value & +ValueObject::GetValue() +{ + return m_value; +} + +const Value & +ValueObject::GetValue() const +{ + return m_value; +} + +bool +ValueObject::ResolveValue (Scalar &scalar) +{ + if (UpdateValueIfNeeded(false)) // make sure that you are up to date before returning anything + { + ExecutionContext exe_ctx (GetExecutionContextRef()); + Value tmp_value(m_value); + scalar = tmp_value.ResolveValue(&exe_ctx); + if (scalar.IsValid()) + { + const uint32_t bitfield_bit_size = GetBitfieldBitSize(); + if (bitfield_bit_size) + return scalar.ExtractBitfield (bitfield_bit_size, GetBitfieldBitOffset()); + return true; + } + } + return false; +} + +bool +ValueObject::GetValueIsValid () const +{ + return m_value_is_valid; +} + + +void +ValueObject::SetValueIsValid (bool b) +{ + m_value_is_valid = b; +} + +bool +ValueObject::GetValueDidChange () +{ + GetValueAsCString (); + return m_value_did_change; +} + +void +ValueObject::SetValueDidChange (bool value_changed) +{ + m_value_did_change = value_changed; +} + +ValueObjectSP +ValueObject::GetChildAtIndex (size_t idx, bool can_create) +{ + ValueObjectSP child_sp; + // We may need to update our value if we are dynamic + if (IsPossibleDynamicType ()) + UpdateValueIfNeeded(false); + if (idx < GetNumChildren()) + { + // Check if we have already made the child value object? + if (can_create && !m_children.HasChildAtIndex(idx)) + { + // No we haven't created the child at this index, so lets have our + // subclass do it and cache the result for quick future access. + m_children.SetChildAtIndex(idx,CreateChildAtIndex (idx, false, 0)); + } + + ValueObject* child = m_children.GetChildAtIndex(idx); + if (child != NULL) + return child->GetSP(); + } + return child_sp; +} + +ValueObjectSP +ValueObject::GetChildAtIndexPath (const std::initializer_list& idxs, + size_t* index_of_error) +{ + if (idxs.size() == 0) + return GetSP(); + ValueObjectSP root(GetSP()); + for (size_t idx : idxs) + { + root = root->GetChildAtIndex(idx, true); + if (!root) + { + if (index_of_error) + *index_of_error = idx; + return root; + } + } + return root; +} + +ValueObjectSP +ValueObject::GetChildAtIndexPath (const std::initializer_list< std::pair >& idxs, + size_t* index_of_error) +{ + if (idxs.size() == 0) + return GetSP(); + ValueObjectSP root(GetSP()); + for (std::pair idx : idxs) + { + root = root->GetChildAtIndex(idx.first, idx.second); + if (!root) + { + if (index_of_error) + *index_of_error = idx.first; + return root; + } + } + return root; +} + +lldb::ValueObjectSP +ValueObject::GetChildAtIndexPath (const std::vector &idxs, + size_t* index_of_error) +{ + if (idxs.size() == 0) + return GetSP(); + ValueObjectSP root(GetSP()); + for (size_t idx : idxs) + { + root = root->GetChildAtIndex(idx, true); + if (!root) + { + if (index_of_error) + *index_of_error = idx; + return root; + } + } + return root; +} + +lldb::ValueObjectSP +ValueObject::GetChildAtIndexPath (const std::vector< std::pair > &idxs, + size_t* index_of_error) +{ + if (idxs.size() == 0) + return GetSP(); + ValueObjectSP root(GetSP()); + for (std::pair idx : idxs) + { + root = root->GetChildAtIndex(idx.first, idx.second); + if (!root) + { + if (index_of_error) + *index_of_error = idx.first; + return root; + } + } + return root; +} + +size_t +ValueObject::GetIndexOfChildWithName (const ConstString &name) +{ + bool omit_empty_base_classes = true; + return GetClangType().GetIndexOfChildWithName (name.GetCString(), omit_empty_base_classes); +} + +ValueObjectSP +ValueObject::GetChildMemberWithName (const ConstString &name, bool can_create) +{ + // when getting a child by name, it could be buried inside some base + // classes (which really aren't part of the expression path), so we + // need a vector of indexes that can get us down to the correct child + ValueObjectSP child_sp; + + // We may need to update our value if we are dynamic + if (IsPossibleDynamicType ()) + UpdateValueIfNeeded(false); + + std::vector child_indexes; + bool omit_empty_base_classes = true; + const size_t num_child_indexes = GetClangType().GetIndexOfChildMemberWithName (name.GetCString(), + omit_empty_base_classes, + child_indexes); + if (num_child_indexes > 0) + { + std::vector::const_iterator pos = child_indexes.begin (); + std::vector::const_iterator end = child_indexes.end (); + + child_sp = GetChildAtIndex(*pos, can_create); + for (++pos; pos != end; ++pos) + { + if (child_sp) + { + ValueObjectSP new_child_sp(child_sp->GetChildAtIndex (*pos, can_create)); + child_sp = new_child_sp; + } + else + { + child_sp.reset(); + } + + } + } + return child_sp; +} + + +size_t +ValueObject::GetNumChildren () +{ + UpdateValueIfNeeded(); + if (!m_children_count_valid) + { + SetNumChildren (CalculateNumChildren()); + } + return m_children.GetChildrenCount(); +} + +bool +ValueObject::MightHaveChildren() +{ + bool has_children = false; + const uint32_t type_info = GetTypeInfo(); + if (type_info) + { + if (type_info & (ClangASTType::eTypeHasChildren | + ClangASTType::eTypeIsPointer | + ClangASTType::eTypeIsReference)) + has_children = true; + } + else + { + has_children = GetNumChildren () > 0; + } + return has_children; +} + +// Should only be called by ValueObject::GetNumChildren() +void +ValueObject::SetNumChildren (size_t num_children) +{ + m_children_count_valid = true; + m_children.SetChildrenCount(num_children); +} + +void +ValueObject::SetName (const ConstString &name) +{ + m_name = name; +} + +ValueObject * +ValueObject::CreateChildAtIndex (size_t idx, bool synthetic_array_member, int32_t synthetic_index) +{ + ValueObject *valobj = NULL; + + bool omit_empty_base_classes = true; + bool ignore_array_bounds = synthetic_array_member; + std::string child_name_str; + uint32_t child_byte_size = 0; + int32_t child_byte_offset = 0; + uint32_t child_bitfield_bit_size = 0; + uint32_t child_bitfield_bit_offset = 0; + bool child_is_base_class = false; + bool child_is_deref_of_parent = false; + + const bool transparent_pointers = synthetic_array_member == false; + ClangASTType child_clang_type; + + ExecutionContext exe_ctx (GetExecutionContextRef()); + + child_clang_type = GetClangType().GetChildClangTypeAtIndex (&exe_ctx, + GetName().GetCString(), + idx, + transparent_pointers, + omit_empty_base_classes, + ignore_array_bounds, + child_name_str, + child_byte_size, + child_byte_offset, + child_bitfield_bit_size, + child_bitfield_bit_offset, + child_is_base_class, + child_is_deref_of_parent); + if (child_clang_type) + { + if (synthetic_index) + child_byte_offset += child_byte_size * synthetic_index; + + ConstString child_name; + if (!child_name_str.empty()) + child_name.SetCString (child_name_str.c_str()); + + valobj = new ValueObjectChild (*this, + child_clang_type, + child_name, + child_byte_size, + child_byte_offset, + child_bitfield_bit_size, + child_bitfield_bit_offset, + child_is_base_class, + child_is_deref_of_parent, + eAddressTypeInvalid); + //if (valobj) + // valobj->SetAddressTypeOfChildren(eAddressTypeInvalid); + } + + return valobj; +} + +bool +ValueObject::GetSummaryAsCString (TypeSummaryImpl* summary_ptr, + std::string& destination) +{ + destination.clear(); + + // ideally we would like to bail out if passing NULL, but if we do so + // we end up not providing the summary for function pointers anymore + if (/*summary_ptr == NULL ||*/ m_is_getting_summary) + return false; + + m_is_getting_summary = true; + + // this is a hot path in code and we prefer to avoid setting this string all too often also clearing out other + // information that we might care to see in a crash log. might be useful in very specific situations though. + /*Host::SetCrashDescriptionWithFormat("Trying to fetch a summary for %s %s. Summary provider's description is %s", + GetTypeName().GetCString(), + GetName().GetCString(), + summary_ptr->GetDescription().c_str());*/ + + if (UpdateValueIfNeeded (false)) + { + if (summary_ptr) + { + if (HasSyntheticValue()) + m_synthetic_value->UpdateValueIfNeeded(); // the summary might depend on the synthetic children being up-to-date (e.g. ${svar%#}) + summary_ptr->FormatObject(this, destination); + } + else + { + ClangASTType clang_type = GetClangType(); + + // Do some default printout for function pointers + if (clang_type) + { + if (clang_type.IsFunctionPointerType ()) + { + StreamString sstr; + AddressType func_ptr_address_type = eAddressTypeInvalid; + addr_t func_ptr_address = GetPointerValue (&func_ptr_address_type); + if (func_ptr_address != 0 && func_ptr_address != LLDB_INVALID_ADDRESS) + { + switch (func_ptr_address_type) + { + case eAddressTypeInvalid: + case eAddressTypeFile: + break; + + case eAddressTypeLoad: + { + ExecutionContext exe_ctx (GetExecutionContextRef()); + + Address so_addr; + Target *target = exe_ctx.GetTargetPtr(); + if (target && target->GetSectionLoadList().IsEmpty() == false) + { + if (target->GetSectionLoadList().ResolveLoadAddress(func_ptr_address, so_addr)) + { + so_addr.Dump (&sstr, + exe_ctx.GetBestExecutionContextScope(), + Address::DumpStyleResolvedDescription, + Address::DumpStyleSectionNameOffset); + } + } + } + break; + + case eAddressTypeHost: + break; + } + } + if (sstr.GetSize() > 0) + { + destination.assign (1, '('); + destination.append (sstr.GetData(), sstr.GetSize()); + destination.append (1, ')'); + } + } + } + } + } + m_is_getting_summary = false; + return !destination.empty(); +} + +const char * +ValueObject::GetSummaryAsCString () +{ + if (UpdateValueIfNeeded(true) && m_summary_str.empty()) + { + GetSummaryAsCString(GetSummaryFormat().get(), + m_summary_str); + } + if (m_summary_str.empty()) + return NULL; + return m_summary_str.c_str(); +} + +bool +ValueObject::IsCStringContainer(bool check_pointer) +{ + ClangASTType pointee_or_element_clang_type; + const Flags type_flags (GetTypeInfo (&pointee_or_element_clang_type)); + bool is_char_arr_ptr (type_flags.AnySet (ClangASTType::eTypeIsArray | ClangASTType::eTypeIsPointer) && + pointee_or_element_clang_type.IsCharType ()); + if (!is_char_arr_ptr) + return false; + if (!check_pointer) + return true; + if (type_flags.Test(ClangASTType::eTypeIsArray)) + return true; + addr_t cstr_address = LLDB_INVALID_ADDRESS; + AddressType cstr_address_type = eAddressTypeInvalid; + cstr_address = GetAddressOf (true, &cstr_address_type); + return (cstr_address != LLDB_INVALID_ADDRESS); +} + +size_t +ValueObject::GetPointeeData (DataExtractor& data, + uint32_t item_idx, + uint32_t item_count) +{ + ClangASTType pointee_or_element_clang_type; + const uint32_t type_info = GetTypeInfo (&pointee_or_element_clang_type); + const bool is_pointer_type = type_info & ClangASTType::eTypeIsPointer; + const bool is_array_type = type_info & ClangASTType::eTypeIsArray; + if (!(is_pointer_type || is_array_type)) + return 0; + + if (item_count == 0) + return 0; + + const uint64_t item_type_size = pointee_or_element_clang_type.GetByteSize(); + const uint64_t bytes = item_count * item_type_size; + const uint64_t offset = item_idx * item_type_size; + + if (item_idx == 0 && item_count == 1) // simply a deref + { + if (is_pointer_type) + { + Error error; + ValueObjectSP pointee_sp = Dereference(error); + if (error.Fail() || pointee_sp.get() == NULL) + return 0; + return pointee_sp->GetDataExtractor().Copy(data); + } + else + { + ValueObjectSP child_sp = GetChildAtIndex(0, true); + if (child_sp.get() == NULL) + return 0; + return child_sp->GetDataExtractor().Copy(data); + } + return true; + } + else /* (items > 1) */ + { + Error error; + lldb_private::DataBufferHeap* heap_buf_ptr = NULL; + lldb::DataBufferSP data_sp(heap_buf_ptr = new lldb_private::DataBufferHeap()); + + AddressType addr_type; + lldb::addr_t addr = is_pointer_type ? GetPointerValue(&addr_type) : GetAddressOf(true, &addr_type); + + switch (addr_type) + { + case eAddressTypeFile: + { + ModuleSP module_sp (GetModule()); + if (module_sp) + { + addr = addr + offset; + Address so_addr; + module_sp->ResolveFileAddress(addr, so_addr); + ExecutionContext exe_ctx (GetExecutionContextRef()); + Target* target = exe_ctx.GetTargetPtr(); + if (target) + { + heap_buf_ptr->SetByteSize(bytes); + size_t bytes_read = target->ReadMemory(so_addr, false, heap_buf_ptr->GetBytes(), bytes, error); + if (error.Success()) + { + data.SetData(data_sp); + return bytes_read; + } + } + } + } + break; + case eAddressTypeLoad: + { + ExecutionContext exe_ctx (GetExecutionContextRef()); + Process *process = exe_ctx.GetProcessPtr(); + if (process) + { + heap_buf_ptr->SetByteSize(bytes); + size_t bytes_read = process->ReadMemory(addr + offset, heap_buf_ptr->GetBytes(), bytes, error); + if (error.Success()) + { + data.SetData(data_sp); + return bytes_read; + } + } + } + break; + case eAddressTypeHost: + { + const uint64_t max_bytes = GetClangType().GetByteSize(); + if (max_bytes > offset) + { + size_t bytes_read = std::min(max_bytes - offset, bytes); + heap_buf_ptr->CopyData((uint8_t*)(addr + offset), bytes_read); + data.SetData(data_sp); + return bytes_read; + } + } + break; + case eAddressTypeInvalid: + break; + } + } + return 0; +} + +uint64_t +ValueObject::GetData (DataExtractor& data) +{ + UpdateValueIfNeeded(false); + ExecutionContext exe_ctx (GetExecutionContextRef()); + Error error = m_value.GetValueAsData(&exe_ctx, data, 0, GetModule().get()); + if (error.Fail()) + { + if (m_data.GetByteSize()) + { + data = m_data; + return data.GetByteSize(); + } + else + { + return 0; + } + } + data.SetAddressByteSize(m_data.GetAddressByteSize()); + data.SetByteOrder(m_data.GetByteOrder()); + return data.GetByteSize(); +} + +bool +ValueObject::SetData (DataExtractor &data, Error &error) +{ + error.Clear(); + // Make sure our value is up to date first so that our location and location + // type is valid. + if (!UpdateValueIfNeeded(false)) + { + error.SetErrorString("unable to read value"); + return false; + } + + uint64_t count = 0; + const Encoding encoding = GetClangType().GetEncoding(count); + + const size_t byte_size = GetByteSize(); + + Value::ValueType value_type = m_value.GetValueType(); + + switch (value_type) + { + case Value::eValueTypeScalar: + { + Error set_error = m_value.GetScalar().SetValueFromData(data, encoding, byte_size); + + if (!set_error.Success()) + { + error.SetErrorStringWithFormat("unable to set scalar value: %s", set_error.AsCString()); + return false; + } + } + break; + case Value::eValueTypeLoadAddress: + { + // If it is a load address, then the scalar value is the storage location + // of the data, and we have to shove this value down to that load location. + ExecutionContext exe_ctx (GetExecutionContextRef()); + Process *process = exe_ctx.GetProcessPtr(); + if (process) + { + addr_t target_addr = m_value.GetScalar().ULongLong(LLDB_INVALID_ADDRESS); + size_t bytes_written = process->WriteMemory(target_addr, + data.GetDataStart(), + byte_size, + error); + if (!error.Success()) + return false; + if (bytes_written != byte_size) + { + error.SetErrorString("unable to write value to memory"); + return false; + } + } + } + break; + case Value::eValueTypeHostAddress: + { + // If it is a host address, then we stuff the scalar as a DataBuffer into the Value's data. + DataBufferSP buffer_sp (new DataBufferHeap(byte_size, 0)); + m_data.SetData(buffer_sp, 0); + data.CopyByteOrderedData (0, + byte_size, + const_cast(m_data.GetDataStart()), + byte_size, + m_data.GetByteOrder()); + m_value.GetScalar() = (uintptr_t)m_data.GetDataStart(); + } + break; + case Value::eValueTypeFileAddress: + case Value::eValueTypeVector: + break; + } + + // If we have reached this point, then we have successfully changed the value. + SetNeedsUpdate(); + return true; +} + +// will compute strlen(str), but without consuming more than +// maxlen bytes out of str (this serves the purpose of reading +// chunks of a string without having to worry about +// missing NULL terminators in the chunk) +// of course, if strlen(str) > maxlen, the function will return +// maxlen_value (which should be != maxlen, because that allows you +// to know whether strlen(str) == maxlen or strlen(str) > maxlen) +static uint32_t +strlen_or_inf (const char* str, + uint32_t maxlen, + uint32_t maxlen_value) +{ + uint32_t len = 0; + if (str) + { + while(*str) + { + len++;str++; + if (len >= maxlen) + return maxlen_value; + } + } + return len; +} + +size_t +ValueObject::ReadPointedString (Stream& s, + Error& error, + uint32_t max_length, + bool honor_array, + Format item_format) +{ + ExecutionContext exe_ctx (GetExecutionContextRef()); + Target* target = exe_ctx.GetTargetPtr(); + + if (!target) + { + s << ""; + error.SetErrorString("no target to read from"); + return 0; + } + + if (max_length == 0) + max_length = target->GetMaximumSizeOfStringSummary(); + + size_t bytes_read = 0; + size_t total_bytes_read = 0; + + ClangASTType clang_type = GetClangType(); + ClangASTType elem_or_pointee_clang_type; + const Flags type_flags (GetTypeInfo (&elem_or_pointee_clang_type)); + if (type_flags.AnySet (ClangASTType::eTypeIsArray | ClangASTType::eTypeIsPointer) && + elem_or_pointee_clang_type.IsCharType ()) + { + addr_t cstr_address = LLDB_INVALID_ADDRESS; + AddressType cstr_address_type = eAddressTypeInvalid; + + size_t cstr_len = 0; + bool capped_data = false; + if (type_flags.Test (ClangASTType::eTypeIsArray)) + { + // We have an array + uint64_t array_size = 0; + if (clang_type.IsArrayType(NULL, &array_size, NULL)) + { + cstr_len = array_size; + if (cstr_len > max_length) + { + capped_data = true; + cstr_len = max_length; + } + } + cstr_address = GetAddressOf (true, &cstr_address_type); + } + else + { + // We have a pointer + cstr_address = GetPointerValue (&cstr_address_type); + } + + if (cstr_address == 0 || cstr_address == LLDB_INVALID_ADDRESS) + { + s << ""; + error.SetErrorString("invalid address"); + return 0; + } + + Address cstr_so_addr (cstr_address); + DataExtractor data; + if (cstr_len > 0 && honor_array) + { + // I am using GetPointeeData() here to abstract the fact that some ValueObjects are actually frozen pointers in the host + // but the pointed-to data lives in the debuggee, and GetPointeeData() automatically takes care of this + GetPointeeData(data, 0, cstr_len); + + if ((bytes_read = data.GetByteSize()) > 0) + { + total_bytes_read = bytes_read; + s << '"'; + data.Dump (&s, + 0, // Start offset in "data" + item_format, + 1, // Size of item (1 byte for a char!) + bytes_read, // How many bytes to print? + UINT32_MAX, // num per line + LLDB_INVALID_ADDRESS,// base address + 0, // bitfield bit size + 0); // bitfield bit offset + if (capped_data) + s << "..."; + s << '"'; + } + } + else + { + cstr_len = max_length; + const size_t k_max_buf_size = 64; + + size_t offset = 0; + + int cstr_len_displayed = -1; + bool capped_cstr = false; + // I am using GetPointeeData() here to abstract the fact that some ValueObjects are actually frozen pointers in the host + // but the pointed-to data lives in the debuggee, and GetPointeeData() automatically takes care of this + while ((bytes_read = GetPointeeData(data, offset, k_max_buf_size)) > 0) + { + total_bytes_read += bytes_read; + const char *cstr = data.PeekCStr(0); + size_t len = strlen_or_inf (cstr, k_max_buf_size, k_max_buf_size+1); + if (len > k_max_buf_size) + len = k_max_buf_size; + if (cstr && cstr_len_displayed < 0) + s << '"'; + + if (cstr_len_displayed < 0) + cstr_len_displayed = len; + + if (len == 0) + break; + cstr_len_displayed += len; + if (len > bytes_read) + len = bytes_read; + if (len > cstr_len) + len = cstr_len; + + data.Dump (&s, + 0, // Start offset in "data" + item_format, + 1, // Size of item (1 byte for a char!) + len, // How many bytes to print? + UINT32_MAX, // num per line + LLDB_INVALID_ADDRESS,// base address + 0, // bitfield bit size + 0); // bitfield bit offset + + if (len < k_max_buf_size) + break; + + if (len >= cstr_len) + { + capped_cstr = true; + break; + } + + cstr_len -= len; + offset += len; + } + + if (cstr_len_displayed >= 0) + { + s << '"'; + if (capped_cstr) + s << "..."; + } + } + } + else + { + error.SetErrorString("not a string object"); + s << ""; + } + return total_bytes_read; +} + +const char * +ValueObject::GetObjectDescription () +{ + + if (!UpdateValueIfNeeded (true)) + return NULL; + + if (!m_object_desc_str.empty()) + return m_object_desc_str.c_str(); + + ExecutionContext exe_ctx (GetExecutionContextRef()); + Process *process = exe_ctx.GetProcessPtr(); + if (process == NULL) + return NULL; + + StreamString s; + + LanguageType language = GetObjectRuntimeLanguage(); + LanguageRuntime *runtime = process->GetLanguageRuntime(language); + + if (runtime == NULL) + { + // Aw, hell, if the things a pointer, or even just an integer, let's try ObjC anyway... + ClangASTType clang_type = GetClangType(); + if (clang_type) + { + bool is_signed; + if (clang_type.IsIntegerType (is_signed) || clang_type.IsPointerType ()) + { + runtime = process->GetLanguageRuntime(eLanguageTypeObjC); + } + } + } + + if (runtime && runtime->GetObjectDescription(s, *this)) + { + m_object_desc_str.append (s.GetData()); + } + + if (m_object_desc_str.empty()) + return NULL; + else + return m_object_desc_str.c_str(); +} + +bool +ValueObject::GetValueAsCString (lldb::Format format, + std::string& destination) +{ + if (GetClangType().IsAggregateType () == false && UpdateValueIfNeeded(false)) + { + const Value::ContextType context_type = m_value.GetContextType(); + + if (context_type == Value::eContextTypeRegisterInfo) + { + const RegisterInfo *reg_info = m_value.GetRegisterInfo(); + if (reg_info) + { + ExecutionContext exe_ctx (GetExecutionContextRef()); + + StreamString reg_sstr; + m_data.Dump (®_sstr, + 0, + format, + reg_info->byte_size, + 1, + UINT32_MAX, + LLDB_INVALID_ADDRESS, + 0, + 0, + exe_ctx.GetBestExecutionContextScope()); + destination.swap(reg_sstr.GetString()); + } + } + else + { + ClangASTType clang_type = GetClangType (); + if (clang_type) + { + // put custom bytes to display in this DataExtractor to override the default value logic + lldb_private::DataExtractor special_format_data; + if (format == eFormatCString) + { + Flags type_flags(clang_type.GetTypeInfo(NULL)); + if (type_flags.Test(ClangASTType::eTypeIsPointer) && !type_flags.Test(ClangASTType::eTypeIsObjC)) + { + // if we are dumping a pointer as a c-string, get the pointee data as a string + TargetSP target_sp(GetTargetSP()); + if (target_sp) + { + size_t max_len = target_sp->GetMaximumSizeOfStringSummary(); + Error error; + DataBufferSP buffer_sp(new DataBufferHeap(max_len+1,0)); + Address address(GetPointerValue()); + if (target_sp->ReadCStringFromMemory(address, (char*)buffer_sp->GetBytes(), max_len, error) && error.Success()) + special_format_data.SetData(buffer_sp); + } + } + } + + StreamString sstr; + ExecutionContext exe_ctx (GetExecutionContextRef()); + clang_type.DumpTypeValue (&sstr, // The stream to use for display + format, // Format to display this type with + special_format_data.GetByteSize() ? + special_format_data: m_data, // Data to extract from + 0, // Byte offset into "m_data" + GetByteSize(), // Byte size of item in "m_data" + GetBitfieldBitSize(), // Bitfield bit size + GetBitfieldBitOffset(), // Bitfield bit offset + exe_ctx.GetBestExecutionContextScope()); + // Don't set the m_error to anything here otherwise + // we won't be able to re-format as anything else. The + // code for ClangASTType::DumpTypeValue() should always + // return something, even if that something contains + // an error messsage. "m_error" is used to detect errors + // when reading the valid object, not for formatting errors. + if (sstr.GetString().empty()) + destination.clear(); + else + destination.swap(sstr.GetString()); + } + } + return !destination.empty(); + } + else + return false; +} + +const char * +ValueObject::GetValueAsCString () +{ + if (UpdateValueIfNeeded(true)) + { + lldb::Format my_format = GetFormat(); + if (my_format == lldb::eFormatDefault) + { + if (m_type_format_sp) + my_format = m_type_format_sp->GetFormat(); + else + { + if (m_is_bitfield_for_scalar) + my_format = eFormatUnsigned; + else + { + if (m_value.GetContextType() == Value::eContextTypeRegisterInfo) + { + const RegisterInfo *reg_info = m_value.GetRegisterInfo(); + if (reg_info) + my_format = reg_info->format; + } + else + { + my_format = GetClangType().GetFormat(); + } + } + } + } + if (my_format != m_last_format || m_value_str.empty()) + { + m_last_format = my_format; + if (GetValueAsCString(my_format, m_value_str)) + { + if (!m_value_did_change && m_old_value_valid) + { + // The value was gotten successfully, so we consider the + // value as changed if the value string differs + SetValueDidChange (m_old_value_str != m_value_str); + } + } + } + } + if (m_value_str.empty()) + return NULL; + return m_value_str.c_str(); +} + +// if > 8bytes, 0 is returned. this method should mostly be used +// to read address values out of pointers +uint64_t +ValueObject::GetValueAsUnsigned (uint64_t fail_value, bool *success) +{ + // If our byte size is zero this is an aggregate type that has children + if (!GetClangType().IsAggregateType()) + { + Scalar scalar; + if (ResolveValue (scalar)) + { + if (success) + *success = true; + return scalar.ULongLong(fail_value); + } + // fallthrough, otherwise... + } + + if (success) + *success = false; + return fail_value; +} + +// if any more "special cases" are added to ValueObject::DumpPrintableRepresentation() please keep +// this call up to date by returning true for your new special cases. We will eventually move +// to checking this call result before trying to display special cases +bool +ValueObject::HasSpecialPrintableRepresentation(ValueObjectRepresentationStyle val_obj_display, + Format custom_format) +{ + Flags flags(GetTypeInfo()); + if (flags.AnySet(ClangASTType::eTypeIsArray | ClangASTType::eTypeIsPointer) + && val_obj_display == ValueObject::eValueObjectRepresentationStyleValue) + { + if (IsCStringContainer(true) && + (custom_format == eFormatCString || + custom_format == eFormatCharArray || + custom_format == eFormatChar || + custom_format == eFormatVectorOfChar)) + return true; + + if (flags.Test(ClangASTType::eTypeIsArray)) + { + if ((custom_format == eFormatBytes) || + (custom_format == eFormatBytesWithASCII)) + return true; + + if ((custom_format == eFormatVectorOfChar) || + (custom_format == eFormatVectorOfFloat32) || + (custom_format == eFormatVectorOfFloat64) || + (custom_format == eFormatVectorOfSInt16) || + (custom_format == eFormatVectorOfSInt32) || + (custom_format == eFormatVectorOfSInt64) || + (custom_format == eFormatVectorOfSInt8) || + (custom_format == eFormatVectorOfUInt128) || + (custom_format == eFormatVectorOfUInt16) || + (custom_format == eFormatVectorOfUInt32) || + (custom_format == eFormatVectorOfUInt64) || + (custom_format == eFormatVectorOfUInt8)) + return true; + } + } + return false; +} + +bool +ValueObject::DumpPrintableRepresentation(Stream& s, + ValueObjectRepresentationStyle val_obj_display, + Format custom_format, + PrintableRepresentationSpecialCases special) +{ + + Flags flags(GetTypeInfo()); + + bool allow_special = ((special & ePrintableRepresentationSpecialCasesAllow) == ePrintableRepresentationSpecialCasesAllow); + bool only_special = ((special & ePrintableRepresentationSpecialCasesOnly) == ePrintableRepresentationSpecialCasesOnly); + + if (allow_special) + { + if (flags.AnySet(ClangASTType::eTypeIsArray | ClangASTType::eTypeIsPointer) + && val_obj_display == ValueObject::eValueObjectRepresentationStyleValue) + { + // when being asked to get a printable display an array or pointer type directly, + // try to "do the right thing" + + if (IsCStringContainer(true) && + (custom_format == eFormatCString || + custom_format == eFormatCharArray || + custom_format == eFormatChar || + custom_format == eFormatVectorOfChar)) // print char[] & char* directly + { + Error error; + ReadPointedString(s, + error, + 0, + (custom_format == eFormatVectorOfChar) || + (custom_format == eFormatCharArray)); + return !error.Fail(); + } + + if (custom_format == eFormatEnum) + return false; + + // this only works for arrays, because I have no way to know when + // the pointed memory ends, and no special \0 end of data marker + if (flags.Test(ClangASTType::eTypeIsArray)) + { + if ((custom_format == eFormatBytes) || + (custom_format == eFormatBytesWithASCII)) + { + const size_t count = GetNumChildren(); + + s << '['; + for (size_t low = 0; low < count; low++) + { + + if (low) + s << ','; + + ValueObjectSP child = GetChildAtIndex(low,true); + if (!child.get()) + { + s << ""; + continue; + } + child->DumpPrintableRepresentation(s, ValueObject::eValueObjectRepresentationStyleValue, custom_format); + } + + s << ']'; + + return true; + } + + if ((custom_format == eFormatVectorOfChar) || + (custom_format == eFormatVectorOfFloat32) || + (custom_format == eFormatVectorOfFloat64) || + (custom_format == eFormatVectorOfSInt16) || + (custom_format == eFormatVectorOfSInt32) || + (custom_format == eFormatVectorOfSInt64) || + (custom_format == eFormatVectorOfSInt8) || + (custom_format == eFormatVectorOfUInt128) || + (custom_format == eFormatVectorOfUInt16) || + (custom_format == eFormatVectorOfUInt32) || + (custom_format == eFormatVectorOfUInt64) || + (custom_format == eFormatVectorOfUInt8)) // arrays of bytes, bytes with ASCII or any vector format should be printed directly + { + const size_t count = GetNumChildren(); + + Format format = FormatManager::GetSingleItemFormat(custom_format); + + s << '['; + for (size_t low = 0; low < count; low++) + { + + if (low) + s << ','; + + ValueObjectSP child = GetChildAtIndex(low,true); + if (!child.get()) + { + s << ""; + continue; + } + child->DumpPrintableRepresentation(s, ValueObject::eValueObjectRepresentationStyleValue, format); + } + + s << ']'; + + return true; + } + } + + if ((custom_format == eFormatBoolean) || + (custom_format == eFormatBinary) || + (custom_format == eFormatChar) || + (custom_format == eFormatCharPrintable) || + (custom_format == eFormatComplexFloat) || + (custom_format == eFormatDecimal) || + (custom_format == eFormatHex) || + (custom_format == eFormatHexUppercase) || + (custom_format == eFormatFloat) || + (custom_format == eFormatOctal) || + (custom_format == eFormatOSType) || + (custom_format == eFormatUnicode16) || + (custom_format == eFormatUnicode32) || + (custom_format == eFormatUnsigned) || + (custom_format == eFormatPointer) || + (custom_format == eFormatComplexInteger) || + (custom_format == eFormatComplex) || + (custom_format == eFormatDefault)) // use the [] operator + return false; + } + } + + if (only_special) + return false; + + bool var_success = false; + + { + const char *cstr = NULL; + + // this is a local stream that we are using to ensure that the data pointed to by cstr survives + // long enough for us to copy it to its destination - it is necessary to have this temporary storage + // area for cases where our desired output is not backed by some other longer-term storage + StreamString strm; + + if (custom_format != eFormatInvalid) + SetFormat(custom_format); + + switch(val_obj_display) + { + case eValueObjectRepresentationStyleValue: + cstr = GetValueAsCString(); + break; + + case eValueObjectRepresentationStyleSummary: + cstr = GetSummaryAsCString(); + break; + + case eValueObjectRepresentationStyleLanguageSpecific: + cstr = GetObjectDescription(); + break; + + case eValueObjectRepresentationStyleLocation: + cstr = GetLocationAsCString(); + break; + + case eValueObjectRepresentationStyleChildrenCount: + strm.Printf("%zu", GetNumChildren()); + cstr = strm.GetString().c_str(); + break; + + case eValueObjectRepresentationStyleType: + cstr = GetTypeName().AsCString(); + break; + + case eValueObjectRepresentationStyleName: + cstr = GetName().AsCString(); + break; + + case eValueObjectRepresentationStyleExpressionPath: + GetExpressionPath(strm, false); + cstr = strm.GetString().c_str(); + break; + } + + if (!cstr) + { + if (val_obj_display == eValueObjectRepresentationStyleValue) + cstr = GetSummaryAsCString(); + else if (val_obj_display == eValueObjectRepresentationStyleSummary) + { + if (GetClangType().IsAggregateType()) + { + strm.Printf("%s @ %s", GetTypeName().AsCString(), GetLocationAsCString()); + cstr = strm.GetString().c_str(); + } + else + cstr = GetValueAsCString(); + } + } + + if (cstr) + s.PutCString(cstr); + else + { + if (m_error.Fail()) + s.Printf("<%s>", m_error.AsCString()); + else if (val_obj_display == eValueObjectRepresentationStyleSummary) + s.PutCString(""); + else if (val_obj_display == eValueObjectRepresentationStyleValue) + s.PutCString(""); + else if (val_obj_display == eValueObjectRepresentationStyleLanguageSpecific) + s.PutCString(""); // edit this if we have other runtimes that support a description + else + s.PutCString(""); + } + + // we should only return false here if we could not do *anything* + // even if we have an error message as output, that's a success + // from our callers' perspective, so return true + var_success = true; + + if (custom_format != eFormatInvalid) + SetFormat(eFormatDefault); + } + + return var_success; +} + +addr_t +ValueObject::GetAddressOf (bool scalar_is_load_address, AddressType *address_type) +{ + if (!UpdateValueIfNeeded(false)) + return LLDB_INVALID_ADDRESS; + + switch (m_value.GetValueType()) + { + case Value::eValueTypeScalar: + case Value::eValueTypeVector: + if (scalar_is_load_address) + { + if(address_type) + *address_type = eAddressTypeLoad; + return m_value.GetScalar().ULongLong(LLDB_INVALID_ADDRESS); + } + break; + + case Value::eValueTypeLoadAddress: + case Value::eValueTypeFileAddress: + case Value::eValueTypeHostAddress: + { + if(address_type) + *address_type = m_value.GetValueAddressType (); + return m_value.GetScalar().ULongLong(LLDB_INVALID_ADDRESS); + } + break; + } + if (address_type) + *address_type = eAddressTypeInvalid; + return LLDB_INVALID_ADDRESS; +} + +addr_t +ValueObject::GetPointerValue (AddressType *address_type) +{ + addr_t address = LLDB_INVALID_ADDRESS; + if(address_type) + *address_type = eAddressTypeInvalid; + + if (!UpdateValueIfNeeded(false)) + return address; + + switch (m_value.GetValueType()) + { + case Value::eValueTypeScalar: + case Value::eValueTypeVector: + address = m_value.GetScalar().ULongLong(LLDB_INVALID_ADDRESS); + break; + + case Value::eValueTypeHostAddress: + case Value::eValueTypeLoadAddress: + case Value::eValueTypeFileAddress: + { + lldb::offset_t data_offset = 0; + address = m_data.GetPointer(&data_offset); + } + break; + } + + if (address_type) + *address_type = GetAddressTypeOfChildren(); + + return address; +} + +bool +ValueObject::SetValueFromCString (const char *value_str, Error& error) +{ + error.Clear(); + // Make sure our value is up to date first so that our location and location + // type is valid. + if (!UpdateValueIfNeeded(false)) + { + error.SetErrorString("unable to read value"); + return false; + } + + uint64_t count = 0; + const Encoding encoding = GetClangType().GetEncoding (count); + + const size_t byte_size = GetByteSize(); + + Value::ValueType value_type = m_value.GetValueType(); + + if (value_type == Value::eValueTypeScalar) + { + // If the value is already a scalar, then let the scalar change itself: + m_value.GetScalar().SetValueFromCString (value_str, encoding, byte_size); + } + else if (byte_size <= Scalar::GetMaxByteSize()) + { + // If the value fits in a scalar, then make a new scalar and again let the + // scalar code do the conversion, then figure out where to put the new value. + Scalar new_scalar; + error = new_scalar.SetValueFromCString (value_str, encoding, byte_size); + if (error.Success()) + { + switch (value_type) + { + case Value::eValueTypeLoadAddress: + { + // If it is a load address, then the scalar value is the storage location + // of the data, and we have to shove this value down to that load location. + ExecutionContext exe_ctx (GetExecutionContextRef()); + Process *process = exe_ctx.GetProcessPtr(); + if (process) + { + addr_t target_addr = m_value.GetScalar().ULongLong(LLDB_INVALID_ADDRESS); + size_t bytes_written = process->WriteScalarToMemory (target_addr, + new_scalar, + byte_size, + error); + if (!error.Success()) + return false; + if (bytes_written != byte_size) + { + error.SetErrorString("unable to write value to memory"); + return false; + } + } + } + break; + case Value::eValueTypeHostAddress: + { + // If it is a host address, then we stuff the scalar as a DataBuffer into the Value's data. + DataExtractor new_data; + new_data.SetByteOrder (m_data.GetByteOrder()); + + DataBufferSP buffer_sp (new DataBufferHeap(byte_size, 0)); + m_data.SetData(buffer_sp, 0); + bool success = new_scalar.GetData(new_data); + if (success) + { + new_data.CopyByteOrderedData (0, + byte_size, + const_cast(m_data.GetDataStart()), + byte_size, + m_data.GetByteOrder()); + } + m_value.GetScalar() = (uintptr_t)m_data.GetDataStart(); + + } + break; + case Value::eValueTypeFileAddress: + case Value::eValueTypeScalar: + case Value::eValueTypeVector: + break; + } + } + else + { + return false; + } + } + else + { + // We don't support setting things bigger than a scalar at present. + error.SetErrorString("unable to write aggregate data type"); + return false; + } + + // If we have reached this point, then we have successfully changed the value. + SetNeedsUpdate(); + return true; +} + +bool +ValueObject::GetDeclaration (Declaration &decl) +{ + decl.Clear(); + return false; +} + +ConstString +ValueObject::GetTypeName() +{ + return GetClangType().GetConstTypeName(); +} + +ConstString +ValueObject::GetQualifiedTypeName() +{ + return GetClangType().GetConstQualifiedTypeName(); +} + + +LanguageType +ValueObject::GetObjectRuntimeLanguage () +{ + return GetClangType().GetMinimumLanguage (); +} + +void +ValueObject::AddSyntheticChild (const ConstString &key, ValueObject *valobj) +{ + m_synthetic_children[key] = valobj; +} + +ValueObjectSP +ValueObject::GetSyntheticChild (const ConstString &key) const +{ + ValueObjectSP synthetic_child_sp; + std::map::const_iterator pos = m_synthetic_children.find (key); + if (pos != m_synthetic_children.end()) + synthetic_child_sp = pos->second->GetSP(); + return synthetic_child_sp; +} + +uint32_t +ValueObject::GetTypeInfo (ClangASTType *pointee_or_element_clang_type) +{ + return GetClangType().GetTypeInfo (pointee_or_element_clang_type); +} + +bool +ValueObject::IsPointerType () +{ + return GetClangType().IsPointerType(); +} + +bool +ValueObject::IsArrayType () +{ + return GetClangType().IsArrayType (NULL, NULL, NULL); +} + +bool +ValueObject::IsScalarType () +{ + return GetClangType().IsScalarType (); +} + +bool +ValueObject::IsIntegerType (bool &is_signed) +{ + return GetClangType().IsIntegerType (is_signed); +} + +bool +ValueObject::IsPointerOrReferenceType () +{ + return GetClangType().IsPointerOrReferenceType (); +} + +bool +ValueObject::IsPossibleDynamicType () +{ + ExecutionContext exe_ctx (GetExecutionContextRef()); + Process *process = exe_ctx.GetProcessPtr(); + if (process) + return process->IsPossibleDynamicValue(*this); + else + return GetClangType().IsPossibleDynamicType (NULL, true, true); +} + +bool +ValueObject::IsObjCNil () +{ + const uint32_t mask = ClangASTType::eTypeIsObjC | ClangASTType::eTypeIsPointer; + bool isObjCpointer = (((GetClangType().GetTypeInfo(NULL)) & mask) == mask); + if (!isObjCpointer) + return false; + bool canReadValue = true; + bool isZero = GetValueAsUnsigned(0,&canReadValue) == 0; + return canReadValue && isZero; +} + +ValueObjectSP +ValueObject::GetSyntheticArrayMember (size_t index, bool can_create) +{ + const uint32_t type_info = GetTypeInfo (); + if (type_info & ClangASTType::eTypeIsArray) + return GetSyntheticArrayMemberFromArray(index, can_create); + + if (type_info & ClangASTType::eTypeIsPointer) + return GetSyntheticArrayMemberFromPointer(index, can_create); + + return ValueObjectSP(); + +} + +ValueObjectSP +ValueObject::GetSyntheticArrayMemberFromPointer (size_t index, bool can_create) +{ + ValueObjectSP synthetic_child_sp; + if (IsPointerType ()) + { + char index_str[64]; + snprintf(index_str, sizeof(index_str), "[%zu]", index); + ConstString index_const_str(index_str); + // Check if we have already created a synthetic array member in this + // valid object. If we have we will re-use it. + synthetic_child_sp = GetSyntheticChild (index_const_str); + if (!synthetic_child_sp) + { + ValueObject *synthetic_child; + // We haven't made a synthetic array member for INDEX yet, so + // lets make one and cache it for any future reference. + synthetic_child = CreateChildAtIndex(0, true, index); + + // Cache the value if we got one back... + if (synthetic_child) + { + AddSyntheticChild(index_const_str, synthetic_child); + synthetic_child_sp = synthetic_child->GetSP(); + synthetic_child_sp->SetName(ConstString(index_str)); + synthetic_child_sp->m_is_array_item_for_pointer = true; + } + } + } + return synthetic_child_sp; +} + +// This allows you to create an array member using and index +// that doesn't not fall in the normal bounds of the array. +// Many times structure can be defined as: +// struct Collection +// { +// uint32_t item_count; +// Item item_array[0]; +// }; +// The size of the "item_array" is 1, but many times in practice +// there are more items in "item_array". + +ValueObjectSP +ValueObject::GetSyntheticArrayMemberFromArray (size_t index, bool can_create) +{ + ValueObjectSP synthetic_child_sp; + if (IsArrayType ()) + { + char index_str[64]; + snprintf(index_str, sizeof(index_str), "[%zu]", index); + ConstString index_const_str(index_str); + // Check if we have already created a synthetic array member in this + // valid object. If we have we will re-use it. + synthetic_child_sp = GetSyntheticChild (index_const_str); + if (!synthetic_child_sp) + { + ValueObject *synthetic_child; + // We haven't made a synthetic array member for INDEX yet, so + // lets make one and cache it for any future reference. + synthetic_child = CreateChildAtIndex(0, true, index); + + // Cache the value if we got one back... + if (synthetic_child) + { + AddSyntheticChild(index_const_str, synthetic_child); + synthetic_child_sp = synthetic_child->GetSP(); + synthetic_child_sp->SetName(ConstString(index_str)); + synthetic_child_sp->m_is_array_item_for_pointer = true; + } + } + } + return synthetic_child_sp; +} + +ValueObjectSP +ValueObject::GetSyntheticBitFieldChild (uint32_t from, uint32_t to, bool can_create) +{ + ValueObjectSP synthetic_child_sp; + if (IsScalarType ()) + { + char index_str[64]; + snprintf(index_str, sizeof(index_str), "[%i-%i]", from, to); + ConstString index_const_str(index_str); + // Check if we have already created a synthetic array member in this + // valid object. If we have we will re-use it. + synthetic_child_sp = GetSyntheticChild (index_const_str); + if (!synthetic_child_sp) + { + // We haven't made a synthetic array member for INDEX yet, so + // lets make one and cache it for any future reference. + ValueObjectChild *synthetic_child = new ValueObjectChild (*this, + GetClangType(), + index_const_str, + GetByteSize(), + 0, + to-from+1, + from, + false, + false, + eAddressTypeInvalid); + + // Cache the value if we got one back... + if (synthetic_child) + { + AddSyntheticChild(index_const_str, synthetic_child); + synthetic_child_sp = synthetic_child->GetSP(); + synthetic_child_sp->SetName(ConstString(index_str)); + synthetic_child_sp->m_is_bitfield_for_scalar = true; + } + } + } + return synthetic_child_sp; +} + +ValueObjectSP +ValueObject::GetSyntheticChildAtOffset(uint32_t offset, const ClangASTType& type, bool can_create) +{ + + ValueObjectSP synthetic_child_sp; + + char name_str[64]; + snprintf(name_str, sizeof(name_str), "@%i", offset); + ConstString name_const_str(name_str); + + // Check if we have already created a synthetic array member in this + // valid object. If we have we will re-use it. + synthetic_child_sp = GetSyntheticChild (name_const_str); + + if (synthetic_child_sp.get()) + return synthetic_child_sp; + + if (!can_create) + return ValueObjectSP(); + + ValueObjectChild *synthetic_child = new ValueObjectChild(*this, + type, + name_const_str, + type.GetByteSize(), + offset, + 0, + 0, + false, + false, + eAddressTypeInvalid); + if (synthetic_child) + { + AddSyntheticChild(name_const_str, synthetic_child); + synthetic_child_sp = synthetic_child->GetSP(); + synthetic_child_sp->SetName(name_const_str); + synthetic_child_sp->m_is_child_at_offset = true; + } + return synthetic_child_sp; +} + +// your expression path needs to have a leading . or -> +// (unless it somehow "looks like" an array, in which case it has +// a leading [ symbol). while the [ is meaningful and should be shown +// to the user, . and -> are just parser design, but by no means +// added information for the user.. strip them off +static const char* +SkipLeadingExpressionPathSeparators(const char* expression) +{ + if (!expression || !expression[0]) + return expression; + if (expression[0] == '.') + return expression+1; + if (expression[0] == '-' && expression[1] == '>') + return expression+2; + return expression; +} + +ValueObjectSP +ValueObject::GetSyntheticExpressionPathChild(const char* expression, bool can_create) +{ + ValueObjectSP synthetic_child_sp; + ConstString name_const_string(expression); + // Check if we have already created a synthetic array member in this + // valid object. If we have we will re-use it. + synthetic_child_sp = GetSyntheticChild (name_const_string); + if (!synthetic_child_sp) + { + // We haven't made a synthetic array member for expression yet, so + // lets make one and cache it for any future reference. + synthetic_child_sp = GetValueForExpressionPath(expression, + NULL, NULL, NULL, + GetValueForExpressionPathOptions().DontAllowSyntheticChildren()); + + // Cache the value if we got one back... + if (synthetic_child_sp.get()) + { + // FIXME: this causes a "real" child to end up with its name changed to the contents of expression + AddSyntheticChild(name_const_string, synthetic_child_sp.get()); + synthetic_child_sp->SetName(ConstString(SkipLeadingExpressionPathSeparators(expression))); + } + } + return synthetic_child_sp; +} + +void +ValueObject::CalculateSyntheticValue (bool use_synthetic) +{ + if (use_synthetic == false) + return; + + TargetSP target_sp(GetTargetSP()); + if (target_sp && (target_sp->GetEnableSyntheticValue() == false || target_sp->GetSuppressSyntheticValue() == true)) + { + m_synthetic_value = NULL; + return; + } + + lldb::SyntheticChildrenSP current_synth_sp(m_synthetic_children_sp); + + if (!UpdateFormatsIfNeeded() && m_synthetic_value) + return; + + if (m_synthetic_children_sp.get() == NULL) + return; + + if (current_synth_sp == m_synthetic_children_sp && m_synthetic_value) + return; + + m_synthetic_value = new ValueObjectSynthetic(*this, m_synthetic_children_sp); +} + +void +ValueObject::CalculateDynamicValue (DynamicValueType use_dynamic) +{ + if (use_dynamic == eNoDynamicValues) + return; + + if (!m_dynamic_value && !IsDynamic()) + { + ExecutionContext exe_ctx (GetExecutionContextRef()); + Process *process = exe_ctx.GetProcessPtr(); + if (process && process->IsPossibleDynamicValue(*this)) + { + ClearDynamicTypeInformation (); + m_dynamic_value = new ValueObjectDynamicValue (*this, use_dynamic); + } + } +} + +ValueObjectSP +ValueObject::GetDynamicValue (DynamicValueType use_dynamic) +{ + if (use_dynamic == eNoDynamicValues) + return ValueObjectSP(); + + if (!IsDynamic() && m_dynamic_value == NULL) + { + CalculateDynamicValue(use_dynamic); + } + if (m_dynamic_value) + return m_dynamic_value->GetSP(); + else + return ValueObjectSP(); +} + +ValueObjectSP +ValueObject::GetStaticValue() +{ + return GetSP(); +} + +lldb::ValueObjectSP +ValueObject::GetNonSyntheticValue () +{ + return GetSP(); +} + +ValueObjectSP +ValueObject::GetSyntheticValue (bool use_synthetic) +{ + if (use_synthetic == false) + return ValueObjectSP(); + + CalculateSyntheticValue(use_synthetic); + + if (m_synthetic_value) + return m_synthetic_value->GetSP(); + else + return ValueObjectSP(); +} + +bool +ValueObject::HasSyntheticValue() +{ + UpdateFormatsIfNeeded(); + + if (m_synthetic_children_sp.get() == NULL) + return false; + + CalculateSyntheticValue(true); + + if (m_synthetic_value) + return true; + else + return false; +} + +bool +ValueObject::GetBaseClassPath (Stream &s) +{ + if (IsBaseClass()) + { + bool parent_had_base_class = GetParent() && GetParent()->GetBaseClassPath (s); + ClangASTType clang_type = GetClangType(); + std::string cxx_class_name; + bool this_had_base_class = clang_type.GetCXXClassName (cxx_class_name); + if (this_had_base_class) + { + if (parent_had_base_class) + s.PutCString("::"); + s.PutCString(cxx_class_name.c_str()); + } + return parent_had_base_class || this_had_base_class; + } + return false; +} + + +ValueObject * +ValueObject::GetNonBaseClassParent() +{ + if (GetParent()) + { + if (GetParent()->IsBaseClass()) + return GetParent()->GetNonBaseClassParent(); + else + return GetParent(); + } + return NULL; +} + +void +ValueObject::GetExpressionPath (Stream &s, bool qualify_cxx_base_classes, GetExpressionPathFormat epformat) +{ + const bool is_deref_of_parent = IsDereferenceOfParent (); + + if (is_deref_of_parent && epformat == eGetExpressionPathFormatDereferencePointers) + { + // this is the original format of GetExpressionPath() producing code like *(a_ptr).memberName, which is entirely + // fine, until you put this into StackFrame::GetValueForVariableExpressionPath() which prefers to see a_ptr->memberName. + // the eHonorPointers mode is meant to produce strings in this latter format + s.PutCString("*("); + } + + ValueObject* parent = GetParent(); + + if (parent) + parent->GetExpressionPath (s, qualify_cxx_base_classes, epformat); + + // if we are a deref_of_parent just because we are synthetic array + // members made up to allow ptr[%d] syntax to work in variable + // printing, then add our name ([%d]) to the expression path + if (m_is_array_item_for_pointer && epformat == eGetExpressionPathFormatHonorPointers) + s.PutCString(m_name.AsCString()); + + if (!IsBaseClass()) + { + if (!is_deref_of_parent) + { + ValueObject *non_base_class_parent = GetNonBaseClassParent(); + if (non_base_class_parent) + { + ClangASTType non_base_class_parent_clang_type = non_base_class_parent->GetClangType(); + if (non_base_class_parent_clang_type) + { + if (parent && parent->IsDereferenceOfParent() && epformat == eGetExpressionPathFormatHonorPointers) + { + s.PutCString("->"); + } + else + { + const uint32_t non_base_class_parent_type_info = non_base_class_parent_clang_type.GetTypeInfo(); + + if (non_base_class_parent_type_info & ClangASTType::eTypeIsPointer) + { + s.PutCString("->"); + } + else if ((non_base_class_parent_type_info & ClangASTType::eTypeHasChildren) && + !(non_base_class_parent_type_info & ClangASTType::eTypeIsArray)) + { + s.PutChar('.'); + } + } + } + } + + const char *name = GetName().GetCString(); + if (name) + { + if (qualify_cxx_base_classes) + { + if (GetBaseClassPath (s)) + s.PutCString("::"); + } + s.PutCString(name); + } + } + } + + if (is_deref_of_parent && epformat == eGetExpressionPathFormatDereferencePointers) + { + s.PutChar(')'); + } +} + +ValueObjectSP +ValueObject::GetValueForExpressionPath(const char* expression, + const char** first_unparsed, + ExpressionPathScanEndReason* reason_to_stop, + ExpressionPathEndResultType* final_value_type, + const GetValueForExpressionPathOptions& options, + ExpressionPathAftermath* final_task_on_target) +{ + + const char* dummy_first_unparsed; + ExpressionPathScanEndReason dummy_reason_to_stop = ValueObject::eExpressionPathScanEndReasonUnknown; + ExpressionPathEndResultType dummy_final_value_type = ValueObject::eExpressionPathEndResultTypeInvalid; + ExpressionPathAftermath dummy_final_task_on_target = ValueObject::eExpressionPathAftermathNothing; + + ValueObjectSP ret_val = GetValueForExpressionPath_Impl(expression, + first_unparsed ? first_unparsed : &dummy_first_unparsed, + reason_to_stop ? reason_to_stop : &dummy_reason_to_stop, + final_value_type ? final_value_type : &dummy_final_value_type, + options, + final_task_on_target ? final_task_on_target : &dummy_final_task_on_target); + + if (!final_task_on_target || *final_task_on_target == ValueObject::eExpressionPathAftermathNothing) + return ret_val; + + if (ret_val.get() && ((final_value_type ? *final_value_type : dummy_final_value_type) == eExpressionPathEndResultTypePlain)) // I can only deref and takeaddress of plain objects + { + if ( (final_task_on_target ? *final_task_on_target : dummy_final_task_on_target) == ValueObject::eExpressionPathAftermathDereference) + { + Error error; + ValueObjectSP final_value = ret_val->Dereference(error); + if (error.Fail() || !final_value.get()) + { + if (reason_to_stop) + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonDereferencingFailed; + if (final_value_type) + *final_value_type = ValueObject::eExpressionPathEndResultTypeInvalid; + return ValueObjectSP(); + } + else + { + if (final_task_on_target) + *final_task_on_target = ValueObject::eExpressionPathAftermathNothing; + return final_value; + } + } + if (*final_task_on_target == ValueObject::eExpressionPathAftermathTakeAddress) + { + Error error; + ValueObjectSP final_value = ret_val->AddressOf(error); + if (error.Fail() || !final_value.get()) + { + if (reason_to_stop) + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonTakingAddressFailed; + if (final_value_type) + *final_value_type = ValueObject::eExpressionPathEndResultTypeInvalid; + return ValueObjectSP(); + } + else + { + if (final_task_on_target) + *final_task_on_target = ValueObject::eExpressionPathAftermathNothing; + return final_value; + } + } + } + return ret_val; // final_task_on_target will still have its original value, so you know I did not do it +} + +int +ValueObject::GetValuesForExpressionPath(const char* expression, + ValueObjectListSP& list, + const char** first_unparsed, + ExpressionPathScanEndReason* reason_to_stop, + ExpressionPathEndResultType* final_value_type, + const GetValueForExpressionPathOptions& options, + ExpressionPathAftermath* final_task_on_target) +{ + const char* dummy_first_unparsed; + ExpressionPathScanEndReason dummy_reason_to_stop; + ExpressionPathEndResultType dummy_final_value_type; + ExpressionPathAftermath dummy_final_task_on_target = ValueObject::eExpressionPathAftermathNothing; + + ValueObjectSP ret_val = GetValueForExpressionPath_Impl(expression, + first_unparsed ? first_unparsed : &dummy_first_unparsed, + reason_to_stop ? reason_to_stop : &dummy_reason_to_stop, + final_value_type ? final_value_type : &dummy_final_value_type, + options, + final_task_on_target ? final_task_on_target : &dummy_final_task_on_target); + + if (!ret_val.get()) // if there are errors, I add nothing to the list + return 0; + + if ( (reason_to_stop ? *reason_to_stop : dummy_reason_to_stop) != eExpressionPathScanEndReasonArrayRangeOperatorMet) + { + // I need not expand a range, just post-process the final value and return + if (!final_task_on_target || *final_task_on_target == ValueObject::eExpressionPathAftermathNothing) + { + list->Append(ret_val); + return 1; + } + if (ret_val.get() && (final_value_type ? *final_value_type : dummy_final_value_type) == eExpressionPathEndResultTypePlain) // I can only deref and takeaddress of plain objects + { + if (*final_task_on_target == ValueObject::eExpressionPathAftermathDereference) + { + Error error; + ValueObjectSP final_value = ret_val->Dereference(error); + if (error.Fail() || !final_value.get()) + { + if (reason_to_stop) + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonDereferencingFailed; + if (final_value_type) + *final_value_type = ValueObject::eExpressionPathEndResultTypeInvalid; + return 0; + } + else + { + *final_task_on_target = ValueObject::eExpressionPathAftermathNothing; + list->Append(final_value); + return 1; + } + } + if (*final_task_on_target == ValueObject::eExpressionPathAftermathTakeAddress) + { + Error error; + ValueObjectSP final_value = ret_val->AddressOf(error); + if (error.Fail() || !final_value.get()) + { + if (reason_to_stop) + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonTakingAddressFailed; + if (final_value_type) + *final_value_type = ValueObject::eExpressionPathEndResultTypeInvalid; + return 0; + } + else + { + *final_task_on_target = ValueObject::eExpressionPathAftermathNothing; + list->Append(final_value); + return 1; + } + } + } + } + else + { + return ExpandArraySliceExpression(first_unparsed ? *first_unparsed : dummy_first_unparsed, + first_unparsed ? first_unparsed : &dummy_first_unparsed, + ret_val, + list, + reason_to_stop ? reason_to_stop : &dummy_reason_to_stop, + final_value_type ? final_value_type : &dummy_final_value_type, + options, + final_task_on_target ? final_task_on_target : &dummy_final_task_on_target); + } + // in any non-covered case, just do the obviously right thing + list->Append(ret_val); + return 1; +} + +ValueObjectSP +ValueObject::GetValueForExpressionPath_Impl(const char* expression_cstr, + const char** first_unparsed, + ExpressionPathScanEndReason* reason_to_stop, + ExpressionPathEndResultType* final_result, + const GetValueForExpressionPathOptions& options, + ExpressionPathAftermath* what_next) +{ + ValueObjectSP root = GetSP(); + + if (!root.get()) + return ValueObjectSP(); + + *first_unparsed = expression_cstr; + + while (true) + { + + const char* expression_cstr = *first_unparsed; // hide the top level expression_cstr + + ClangASTType root_clang_type = root->GetClangType(); + ClangASTType pointee_clang_type; + Flags pointee_clang_type_info; + + Flags root_clang_type_info(root_clang_type.GetTypeInfo(&pointee_clang_type)); + if (pointee_clang_type) + pointee_clang_type_info.Reset(pointee_clang_type.GetTypeInfo()); + + if (!expression_cstr || *expression_cstr == '\0') + { + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonEndOfString; + return root; + } + + switch (*expression_cstr) + { + case '-': + { + if (options.m_check_dot_vs_arrow_syntax && + root_clang_type_info.Test(ClangASTType::eTypeIsPointer) ) // if you are trying to use -> on a non-pointer and I must catch the error + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonArrowInsteadOfDot; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return ValueObjectSP(); + } + if (root_clang_type_info.Test(ClangASTType::eTypeIsObjC) && // if yo are trying to extract an ObjC IVar when this is forbidden + root_clang_type_info.Test(ClangASTType::eTypeIsPointer) && + options.m_no_fragile_ivar) + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonFragileIVarNotAllowed; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return ValueObjectSP(); + } + if (expression_cstr[1] != '>') + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonUnexpectedSymbol; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return ValueObjectSP(); + } + expression_cstr++; // skip the - + } + case '.': // or fallthrough from -> + { + if (options.m_check_dot_vs_arrow_syntax && *expression_cstr == '.' && + root_clang_type_info.Test(ClangASTType::eTypeIsPointer)) // if you are trying to use . on a pointer and I must catch the error + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonDotInsteadOfArrow; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return ValueObjectSP(); + } + expression_cstr++; // skip . + const char *next_separator = strpbrk(expression_cstr+1,"-.["); + ConstString child_name; + if (!next_separator) // if no other separator just expand this last layer + { + child_name.SetCString (expression_cstr); + ValueObjectSP child_valobj_sp = root->GetChildMemberWithName(child_name, true); + + if (child_valobj_sp.get()) // we know we are done, so just return + { + *first_unparsed = ""; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonEndOfString; + *final_result = ValueObject::eExpressionPathEndResultTypePlain; + return child_valobj_sp; + } + else if (options.m_no_synthetic_children == false) // let's try with synthetic children + { + if (root->IsSynthetic()) + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonNoSuchChild; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return ValueObjectSP(); + } + + child_valobj_sp = root->GetSyntheticValue(); + if (child_valobj_sp.get()) + child_valobj_sp = child_valobj_sp->GetChildMemberWithName(child_name, true); + } + + // if we are here and options.m_no_synthetic_children is true, child_valobj_sp is going to be a NULL SP, + // so we hit the "else" branch, and return an error + if(child_valobj_sp.get()) // if it worked, just return + { + *first_unparsed = ""; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonEndOfString; + *final_result = ValueObject::eExpressionPathEndResultTypePlain; + return child_valobj_sp; + } + else + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonNoSuchChild; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return ValueObjectSP(); + } + } + else // other layers do expand + { + child_name.SetCStringWithLength(expression_cstr, next_separator - expression_cstr); + ValueObjectSP child_valobj_sp = root->GetChildMemberWithName(child_name, true); + if (child_valobj_sp.get()) // store the new root and move on + { + root = child_valobj_sp; + *first_unparsed = next_separator; + *final_result = ValueObject::eExpressionPathEndResultTypePlain; + continue; + } + else if (options.m_no_synthetic_children == false) // let's try with synthetic children + { + if (root->IsSynthetic()) + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonNoSuchChild; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return ValueObjectSP(); + } + + child_valobj_sp = root->GetSyntheticValue(true); + if (child_valobj_sp) + child_valobj_sp = child_valobj_sp->GetChildMemberWithName(child_name, true); + } + + // if we are here and options.m_no_synthetic_children is true, child_valobj_sp is going to be a NULL SP, + // so we hit the "else" branch, and return an error + if(child_valobj_sp.get()) // if it worked, move on + { + root = child_valobj_sp; + *first_unparsed = next_separator; + *final_result = ValueObject::eExpressionPathEndResultTypePlain; + continue; + } + else + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonNoSuchChild; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return ValueObjectSP(); + } + } + break; + } + case '[': + { + if (!root_clang_type_info.Test(ClangASTType::eTypeIsArray) && !root_clang_type_info.Test(ClangASTType::eTypeIsPointer) && !root_clang_type_info.Test(ClangASTType::eTypeIsVector)) // if this is not a T[] nor a T* + { + if (!root_clang_type_info.Test(ClangASTType::eTypeIsScalar)) // if this is not even a scalar... + { + if (options.m_no_synthetic_children) // ...only chance left is synthetic + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonRangeOperatorInvalid; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return ValueObjectSP(); + } + } + else if (!options.m_allow_bitfields_syntax) // if this is a scalar, check that we can expand bitfields + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonRangeOperatorNotAllowed; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return ValueObjectSP(); + } + } + if (*(expression_cstr+1) == ']') // if this is an unbounded range it only works for arrays + { + if (!root_clang_type_info.Test(ClangASTType::eTypeIsArray)) + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonEmptyRangeNotAllowed; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return ValueObjectSP(); + } + else // even if something follows, we cannot expand unbounded ranges, just let the caller do it + { + *first_unparsed = expression_cstr+2; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonArrayRangeOperatorMet; + *final_result = ValueObject::eExpressionPathEndResultTypeUnboundedRange; + return root; + } + } + const char *separator_position = ::strchr(expression_cstr+1,'-'); + const char *close_bracket_position = ::strchr(expression_cstr+1,']'); + if (!close_bracket_position) // if there is no ], this is a syntax error + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonUnexpectedSymbol; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return ValueObjectSP(); + } + if (!separator_position || separator_position > close_bracket_position) // if no separator, this is either [] or [N] + { + char *end = NULL; + unsigned long index = ::strtoul (expression_cstr+1, &end, 0); + if (!end || end != close_bracket_position) // if something weird is in our way return an error + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonUnexpectedSymbol; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return ValueObjectSP(); + } + if (end - expression_cstr == 1) // if this is [], only return a valid value for arrays + { + if (root_clang_type_info.Test(ClangASTType::eTypeIsArray)) + { + *first_unparsed = expression_cstr+2; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonArrayRangeOperatorMet; + *final_result = ValueObject::eExpressionPathEndResultTypeUnboundedRange; + return root; + } + else + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonEmptyRangeNotAllowed; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return ValueObjectSP(); + } + } + // from here on we do have a valid index + if (root_clang_type_info.Test(ClangASTType::eTypeIsArray)) + { + ValueObjectSP child_valobj_sp = root->GetChildAtIndex(index, true); + if (!child_valobj_sp) + child_valobj_sp = root->GetSyntheticArrayMemberFromArray(index, true); + if (!child_valobj_sp) + if (root->HasSyntheticValue() && root->GetSyntheticValue()->GetNumChildren() > index) + child_valobj_sp = root->GetSyntheticValue()->GetChildAtIndex(index, true); + if (child_valobj_sp) + { + root = child_valobj_sp; + *first_unparsed = end+1; // skip ] + *final_result = ValueObject::eExpressionPathEndResultTypePlain; + continue; + } + else + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonNoSuchChild; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return ValueObjectSP(); + } + } + else if (root_clang_type_info.Test(ClangASTType::eTypeIsPointer)) + { + if (*what_next == ValueObject::eExpressionPathAftermathDereference && // if this is a ptr-to-scalar, I am accessing it by index and I would have deref'ed anyway, then do it now and use this as a bitfield + pointee_clang_type_info.Test(ClangASTType::eTypeIsScalar)) + { + Error error; + root = root->Dereference(error); + if (error.Fail() || !root.get()) + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonDereferencingFailed; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return ValueObjectSP(); + } + else + { + *what_next = eExpressionPathAftermathNothing; + continue; + } + } + else + { + if (root->GetClangType().GetMinimumLanguage() == eLanguageTypeObjC + && pointee_clang_type_info.AllClear(ClangASTType::eTypeIsPointer) + && root->HasSyntheticValue() + && options.m_no_synthetic_children == false) + { + root = root->GetSyntheticValue()->GetChildAtIndex(index, true); + } + else + root = root->GetSyntheticArrayMemberFromPointer(index, true); + if (!root.get()) + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonNoSuchChild; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return ValueObjectSP(); + } + else + { + *first_unparsed = end+1; // skip ] + *final_result = ValueObject::eExpressionPathEndResultTypePlain; + continue; + } + } + } + else if (root_clang_type_info.Test(ClangASTType::eTypeIsScalar)) + { + root = root->GetSyntheticBitFieldChild(index, index, true); + if (!root.get()) + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonNoSuchChild; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return ValueObjectSP(); + } + else // we do not know how to expand members of bitfields, so we just return and let the caller do any further processing + { + *first_unparsed = end+1; // skip ] + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonBitfieldRangeOperatorMet; + *final_result = ValueObject::eExpressionPathEndResultTypeBitfield; + return root; + } + } + else if (root_clang_type_info.Test(ClangASTType::eTypeIsVector)) + { + root = root->GetChildAtIndex(index, true); + if (!root.get()) + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonNoSuchChild; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return ValueObjectSP(); + } + else + { + *first_unparsed = end+1; // skip ] + *final_result = ValueObject::eExpressionPathEndResultTypePlain; + continue; + } + } + else if (options.m_no_synthetic_children == false) + { + if (root->HasSyntheticValue()) + root = root->GetSyntheticValue(); + else if (!root->IsSynthetic()) + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonSyntheticValueMissing; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return ValueObjectSP(); + } + // if we are here, then root itself is a synthetic VO.. should be good to go + + if (!root.get()) + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonSyntheticValueMissing; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return ValueObjectSP(); + } + root = root->GetChildAtIndex(index, true); + if (!root.get()) + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonNoSuchChild; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return ValueObjectSP(); + } + else + { + *first_unparsed = end+1; // skip ] + *final_result = ValueObject::eExpressionPathEndResultTypePlain; + continue; + } + } + else + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonNoSuchChild; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return ValueObjectSP(); + } + } + else // we have a low and a high index + { + char *end = NULL; + unsigned long index_lower = ::strtoul (expression_cstr+1, &end, 0); + if (!end || end != separator_position) // if something weird is in our way return an error + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonUnexpectedSymbol; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return ValueObjectSP(); + } + unsigned long index_higher = ::strtoul (separator_position+1, &end, 0); + if (!end || end != close_bracket_position) // if something weird is in our way return an error + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonUnexpectedSymbol; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return ValueObjectSP(); + } + if (index_lower > index_higher) // swap indices if required + { + unsigned long temp = index_lower; + index_lower = index_higher; + index_higher = temp; + } + if (root_clang_type_info.Test(ClangASTType::eTypeIsScalar)) // expansion only works for scalars + { + root = root->GetSyntheticBitFieldChild(index_lower, index_higher, true); + if (!root.get()) + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonNoSuchChild; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return ValueObjectSP(); + } + else + { + *first_unparsed = end+1; // skip ] + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonBitfieldRangeOperatorMet; + *final_result = ValueObject::eExpressionPathEndResultTypeBitfield; + return root; + } + } + else if (root_clang_type_info.Test(ClangASTType::eTypeIsPointer) && // if this is a ptr-to-scalar, I am accessing it by index and I would have deref'ed anyway, then do it now and use this as a bitfield + *what_next == ValueObject::eExpressionPathAftermathDereference && + pointee_clang_type_info.Test(ClangASTType::eTypeIsScalar)) + { + Error error; + root = root->Dereference(error); + if (error.Fail() || !root.get()) + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonDereferencingFailed; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return ValueObjectSP(); + } + else + { + *what_next = ValueObject::eExpressionPathAftermathNothing; + continue; + } + } + else + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonArrayRangeOperatorMet; + *final_result = ValueObject::eExpressionPathEndResultTypeBoundedRange; + return root; + } + } + break; + } + default: // some non-separator is in the way + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonUnexpectedSymbol; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return ValueObjectSP(); + break; + } + } + } +} + +int +ValueObject::ExpandArraySliceExpression(const char* expression_cstr, + const char** first_unparsed, + ValueObjectSP root, + ValueObjectListSP& list, + ExpressionPathScanEndReason* reason_to_stop, + ExpressionPathEndResultType* final_result, + const GetValueForExpressionPathOptions& options, + ExpressionPathAftermath* what_next) +{ + if (!root.get()) + return 0; + + *first_unparsed = expression_cstr; + + while (true) + { + + const char* expression_cstr = *first_unparsed; // hide the top level expression_cstr + + ClangASTType root_clang_type = root->GetClangType(); + ClangASTType pointee_clang_type; + Flags pointee_clang_type_info; + Flags root_clang_type_info(root_clang_type.GetTypeInfo(&pointee_clang_type)); + if (pointee_clang_type) + pointee_clang_type_info.Reset(pointee_clang_type.GetTypeInfo()); + + if (!expression_cstr || *expression_cstr == '\0') + { + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonEndOfString; + list->Append(root); + return 1; + } + + switch (*expression_cstr) + { + case '[': + { + if (!root_clang_type_info.Test(ClangASTType::eTypeIsArray) && !root_clang_type_info.Test(ClangASTType::eTypeIsPointer)) // if this is not a T[] nor a T* + { + if (!root_clang_type_info.Test(ClangASTType::eTypeIsScalar)) // if this is not even a scalar, this syntax is just plain wrong! + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonRangeOperatorInvalid; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return 0; + } + else if (!options.m_allow_bitfields_syntax) // if this is a scalar, check that we can expand bitfields + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonRangeOperatorNotAllowed; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return 0; + } + } + if (*(expression_cstr+1) == ']') // if this is an unbounded range it only works for arrays + { + if (!root_clang_type_info.Test(ClangASTType::eTypeIsArray)) + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonEmptyRangeNotAllowed; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return 0; + } + else // expand this into list + { + const size_t max_index = root->GetNumChildren() - 1; + for (size_t index = 0; index < max_index; index++) + { + ValueObjectSP child = + root->GetChildAtIndex(index, true); + list->Append(child); + } + *first_unparsed = expression_cstr+2; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonRangeOperatorExpanded; + *final_result = ValueObject::eExpressionPathEndResultTypeValueObjectList; + return max_index; // tell me number of items I added to the VOList + } + } + const char *separator_position = ::strchr(expression_cstr+1,'-'); + const char *close_bracket_position = ::strchr(expression_cstr+1,']'); + if (!close_bracket_position) // if there is no ], this is a syntax error + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonUnexpectedSymbol; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return 0; + } + if (!separator_position || separator_position > close_bracket_position) // if no separator, this is either [] or [N] + { + char *end = NULL; + unsigned long index = ::strtoul (expression_cstr+1, &end, 0); + if (!end || end != close_bracket_position) // if something weird is in our way return an error + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonUnexpectedSymbol; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return 0; + } + if (end - expression_cstr == 1) // if this is [], only return a valid value for arrays + { + if (root_clang_type_info.Test(ClangASTType::eTypeIsArray)) + { + const size_t max_index = root->GetNumChildren() - 1; + for (size_t index = 0; index < max_index; index++) + { + ValueObjectSP child = + root->GetChildAtIndex(index, true); + list->Append(child); + } + *first_unparsed = expression_cstr+2; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonRangeOperatorExpanded; + *final_result = ValueObject::eExpressionPathEndResultTypeValueObjectList; + return max_index; // tell me number of items I added to the VOList + } + else + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonEmptyRangeNotAllowed; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return 0; + } + } + // from here on we do have a valid index + if (root_clang_type_info.Test(ClangASTType::eTypeIsArray)) + { + root = root->GetChildAtIndex(index, true); + if (!root.get()) + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonNoSuchChild; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return 0; + } + else + { + list->Append(root); + *first_unparsed = end+1; // skip ] + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonRangeOperatorExpanded; + *final_result = ValueObject::eExpressionPathEndResultTypeValueObjectList; + return 1; + } + } + else if (root_clang_type_info.Test(ClangASTType::eTypeIsPointer)) + { + if (*what_next == ValueObject::eExpressionPathAftermathDereference && // if this is a ptr-to-scalar, I am accessing it by index and I would have deref'ed anyway, then do it now and use this as a bitfield + pointee_clang_type_info.Test(ClangASTType::eTypeIsScalar)) + { + Error error; + root = root->Dereference(error); + if (error.Fail() || !root.get()) + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonDereferencingFailed; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return 0; + } + else + { + *what_next = eExpressionPathAftermathNothing; + continue; + } + } + else + { + root = root->GetSyntheticArrayMemberFromPointer(index, true); + if (!root.get()) + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonNoSuchChild; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return 0; + } + else + { + list->Append(root); + *first_unparsed = end+1; // skip ] + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonRangeOperatorExpanded; + *final_result = ValueObject::eExpressionPathEndResultTypeValueObjectList; + return 1; + } + } + } + else /*if (ClangASTContext::IsScalarType(root_clang_type))*/ + { + root = root->GetSyntheticBitFieldChild(index, index, true); + if (!root.get()) + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonNoSuchChild; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return 0; + } + else // we do not know how to expand members of bitfields, so we just return and let the caller do any further processing + { + list->Append(root); + *first_unparsed = end+1; // skip ] + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonRangeOperatorExpanded; + *final_result = ValueObject::eExpressionPathEndResultTypeValueObjectList; + return 1; + } + } + } + else // we have a low and a high index + { + char *end = NULL; + unsigned long index_lower = ::strtoul (expression_cstr+1, &end, 0); + if (!end || end != separator_position) // if something weird is in our way return an error + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonUnexpectedSymbol; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return 0; + } + unsigned long index_higher = ::strtoul (separator_position+1, &end, 0); + if (!end || end != close_bracket_position) // if something weird is in our way return an error + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonUnexpectedSymbol; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return 0; + } + if (index_lower > index_higher) // swap indices if required + { + unsigned long temp = index_lower; + index_lower = index_higher; + index_higher = temp; + } + if (root_clang_type_info.Test(ClangASTType::eTypeIsScalar)) // expansion only works for scalars + { + root = root->GetSyntheticBitFieldChild(index_lower, index_higher, true); + if (!root.get()) + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonNoSuchChild; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return 0; + } + else + { + list->Append(root); + *first_unparsed = end+1; // skip ] + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonRangeOperatorExpanded; + *final_result = ValueObject::eExpressionPathEndResultTypeValueObjectList; + return 1; + } + } + else if (root_clang_type_info.Test(ClangASTType::eTypeIsPointer) && // if this is a ptr-to-scalar, I am accessing it by index and I would have deref'ed anyway, then do it now and use this as a bitfield + *what_next == ValueObject::eExpressionPathAftermathDereference && + pointee_clang_type_info.Test(ClangASTType::eTypeIsScalar)) + { + Error error; + root = root->Dereference(error); + if (error.Fail() || !root.get()) + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonDereferencingFailed; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return 0; + } + else + { + *what_next = ValueObject::eExpressionPathAftermathNothing; + continue; + } + } + else + { + for (unsigned long index = index_lower; + index <= index_higher; index++) + { + ValueObjectSP child = + root->GetChildAtIndex(index, true); + list->Append(child); + } + *first_unparsed = end+1; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonRangeOperatorExpanded; + *final_result = ValueObject::eExpressionPathEndResultTypeValueObjectList; + return index_higher-index_lower+1; // tell me number of items I added to the VOList + } + } + break; + } + default: // some non-[ separator, or something entirely wrong, is in the way + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonUnexpectedSymbol; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return 0; + break; + } + } + } +} + +static void +DumpValueObject_Impl (Stream &s, + ValueObject *valobj, + const ValueObject::DumpValueObjectOptions& options, + uint32_t ptr_depth, + uint32_t curr_depth) +{ + if (valobj) + { + bool update_success = valobj->UpdateValueIfNeeded (true); + + const char *root_valobj_name = + options.m_root_valobj_name.empty() ? + valobj->GetName().AsCString() : + options.m_root_valobj_name.c_str(); + + if (update_success && options.m_use_dynamic != eNoDynamicValues) + { + ValueObject *dynamic_value = valobj->GetDynamicValue(options.m_use_dynamic).get(); + if (dynamic_value) + valobj = dynamic_value; + } + + ClangASTType clang_type = valobj->GetClangType(); + const Flags type_flags (clang_type.GetTypeInfo ()); + const char *err_cstr = NULL; + const bool has_children = type_flags.Test (ClangASTType::eTypeHasChildren); + const bool has_value = type_flags.Test (ClangASTType::eTypeHasValue); + + const bool print_valobj = options.m_flat_output == false || has_value; + + if (print_valobj) + { + if (options.m_show_location) + { + s.Printf("%s: ", valobj->GetLocationAsCString()); + } + + s.Indent(); + + bool show_type = true; + // if we are at the root-level and been asked to hide the root's type, then hide it + if (curr_depth == 0 && options.m_hide_root_type) + show_type = false; + else + // otherwise decide according to the usual rules (asked to show types - always at the root level) + show_type = options.m_show_types || (curr_depth == 0 && !options.m_flat_output); + + if (show_type) + { + // Some ValueObjects don't have types (like registers sets). Only print + // the type if there is one to print + ConstString qualified_type_name(valobj->GetQualifiedTypeName()); + if (qualified_type_name) + s.Printf("(%s) ", qualified_type_name.GetCString()); + } + + if (options.m_flat_output) + { + // If we are showing types, also qualify the C++ base classes + const bool qualify_cxx_base_classes = options.m_show_types; + if (!options.m_hide_name) + { + valobj->GetExpressionPath(s, qualify_cxx_base_classes); + s.PutCString(" ="); + } + } + else if (!options.m_hide_name) + { + const char *name_cstr = root_valobj_name ? root_valobj_name : valobj->GetName().AsCString(""); + s.Printf ("%s =", name_cstr); + } + + if (!options.m_scope_already_checked && !valobj->IsInScope()) + { + err_cstr = "out of scope"; + } + } + + std::string summary_str; + std::string value_str; + const char *val_cstr = NULL; + const char *sum_cstr = NULL; + TypeSummaryImpl* entry = options.m_summary_sp ? options.m_summary_sp.get() : valobj->GetSummaryFormat().get(); + + if (options.m_omit_summary_depth > 0) + entry = NULL; + + bool is_nil = valobj->IsObjCNil(); + + if (err_cstr == NULL) + { + if (options.m_format != eFormatDefault && options.m_format != valobj->GetFormat()) + { + valobj->GetValueAsCString(options.m_format, + value_str); + } + else + { + val_cstr = valobj->GetValueAsCString(); + if (val_cstr) + value_str = val_cstr; + } + err_cstr = valobj->GetError().AsCString(); + } + + if (err_cstr) + { + s.Printf (" <%s>\n", err_cstr); + } + else + { + const bool is_ref = type_flags.Test (ClangASTType::eTypeIsReference); + if (print_valobj) + { + if (is_nil) + sum_cstr = "nil"; + else if (options.m_omit_summary_depth == 0) + { + if (options.m_summary_sp) + { + valobj->GetSummaryAsCString(entry, summary_str); + sum_cstr = summary_str.c_str(); + } + else + sum_cstr = valobj->GetSummaryAsCString(); + } + + // Make sure we have a value and make sure the summary didn't + // specify that the value should not be printed - and do not print + // the value if this thing is nil + // (but show the value if the user passes a format explicitly) + if (!is_nil && !value_str.empty() && (entry == NULL || (entry->DoesPrintValue() || options.m_format != eFormatDefault) || sum_cstr == NULL) && !options.m_hide_value) + s.Printf(" %s", value_str.c_str()); + + if (sum_cstr) + s.Printf(" %s", sum_cstr); + + // let's avoid the overly verbose no description error for a nil thing + if (options.m_use_objc && !is_nil) + { + if (!options.m_hide_value || !options.m_hide_name) + s.Printf(" "); + const char *object_desc = valobj->GetObjectDescription(); + if (object_desc) + s.Printf("%s\n", object_desc); + else + s.Printf ("[no Objective-C description available]\n"); + return; + } + } + + if (curr_depth < options.m_max_depth) + { + // We will show children for all concrete types. We won't show + // pointer contents unless a pointer depth has been specified. + // We won't reference contents unless the reference is the + // root object (depth of zero). + bool print_children = true; + + // Use a new temporary pointer depth in case we override the + // current pointer depth below... + uint32_t curr_ptr_depth = ptr_depth; + + const bool is_ptr = type_flags.Test (ClangASTType::eTypeIsPointer); + if (is_ptr || is_ref) + { + // We have a pointer or reference whose value is an address. + // Make sure that address is not NULL + AddressType ptr_address_type; + if (valobj->GetPointerValue (&ptr_address_type) == 0) + print_children = false; + + else if (is_ref && curr_depth == 0) + { + // If this is the root object (depth is zero) that we are showing + // and it is a reference, and no pointer depth has been supplied + // print out what it references. Don't do this at deeper depths + // otherwise we can end up with infinite recursion... + curr_ptr_depth = 1; + } + + if (curr_ptr_depth == 0) + print_children = false; + } + + if (print_children && (!entry || entry->DoesPrintChildren() || !sum_cstr)) + { + ValueObjectSP synth_valobj_sp = valobj->GetSyntheticValue (options.m_use_synthetic); + ValueObject* synth_valobj = (synth_valobj_sp ? synth_valobj_sp.get() : valobj); + + size_t num_children = synth_valobj->GetNumChildren(); + bool print_dotdotdot = false; + if (num_children) + { + if (options.m_flat_output) + { + if (print_valobj) + s.EOL(); + } + else + { + if (print_valobj) + s.PutCString(is_ref ? ": {\n" : " {\n"); + s.IndentMore(); + } + + const size_t max_num_children = valobj->GetTargetSP()->GetMaximumNumberOfChildrenToDisplay(); + + if (num_children > max_num_children && !options.m_ignore_cap) + { + num_children = max_num_children; + print_dotdotdot = true; + } + + ValueObject::DumpValueObjectOptions child_options(options); + child_options.SetFormat(options.m_format).SetSummary().SetRootValueObjectName(); + child_options.SetScopeChecked(true).SetHideName(options.m_hide_name).SetHideValue(options.m_hide_value) + .SetOmitSummaryDepth(child_options.m_omit_summary_depth > 1 ? child_options.m_omit_summary_depth - 1 : 0); + for (size_t idx=0; idxGetChildAtIndex(idx, true)); + if (child_sp.get()) + { + DumpValueObject_Impl (s, + child_sp.get(), + child_options, + (is_ptr || is_ref) ? curr_ptr_depth - 1 : curr_ptr_depth, + curr_depth + 1); + } + } + + if (!options.m_flat_output) + { + if (print_dotdotdot) + { + ExecutionContext exe_ctx (valobj->GetExecutionContextRef()); + Target *target = exe_ctx.GetTargetPtr(); + if (target) + target->GetDebugger().GetCommandInterpreter().ChildrenTruncated(); + s.Indent("...\n"); + } + s.IndentLess(); + s.Indent("}\n"); + } + } + else if (has_children) + { + // Aggregate, no children... + if (print_valobj) + s.PutCString(" {}\n"); + } + else + { + if (print_valobj) + s.EOL(); + } + + } + else + { + s.EOL(); + } + } + else + { + if (has_children && print_valobj) + { + s.PutCString("{...}\n"); + } + } + } + } +} + +void +ValueObject::LogValueObject (Log *log, + ValueObject *valobj) +{ + if (log && valobj) + return LogValueObject (log, valobj, DumpValueObjectOptions::DefaultOptions()); +} + +void +ValueObject::LogValueObject (Log *log, + ValueObject *valobj, + const DumpValueObjectOptions& options) +{ + if (log && valobj) + { + StreamString s; + ValueObject::DumpValueObject (s, valobj, options); + if (s.GetSize()) + log->PutCString(s.GetData()); + } +} + +void +ValueObject::DumpValueObject (Stream &s, + ValueObject *valobj) +{ + + if (!valobj) + return; + + DumpValueObject_Impl(s, + valobj, + DumpValueObjectOptions::DefaultOptions(), + 0, + 0); +} + +void +ValueObject::DumpValueObject (Stream &s, + ValueObject *valobj, + const DumpValueObjectOptions& options) +{ + DumpValueObject_Impl(s, + valobj, + options, + options.m_max_ptr_depth, // max pointer depth allowed, we will go down from here + 0 // current object depth is 0 since we are just starting + ); +} + +ValueObjectSP +ValueObject::CreateConstantValue (const ConstString &name) +{ + ValueObjectSP valobj_sp; + + if (UpdateValueIfNeeded(false) && m_error.Success()) + { + ExecutionContext exe_ctx (GetExecutionContextRef()); + + DataExtractor data; + data.SetByteOrder (m_data.GetByteOrder()); + data.SetAddressByteSize(m_data.GetAddressByteSize()); + + if (IsBitfield()) + { + Value v(Scalar(GetValueAsUnsigned(UINT64_MAX))); + m_error = v.GetValueAsData (&exe_ctx, data, 0, GetModule().get()); + } + else + m_error = m_value.GetValueAsData (&exe_ctx, data, 0, GetModule().get()); + + valobj_sp = ValueObjectConstResult::Create (exe_ctx.GetBestExecutionContextScope(), + GetClangType(), + name, + data, + GetAddressOf()); + } + + if (!valobj_sp) + { + valobj_sp = ValueObjectConstResult::Create (NULL, m_error); + } + return valobj_sp; +} + +ValueObjectSP +ValueObject::Dereference (Error &error) +{ + if (m_deref_valobj) + return m_deref_valobj->GetSP(); + + const bool is_pointer_type = IsPointerType(); + if (is_pointer_type) + { + bool omit_empty_base_classes = true; + bool ignore_array_bounds = false; + + std::string child_name_str; + uint32_t child_byte_size = 0; + int32_t child_byte_offset = 0; + uint32_t child_bitfield_bit_size = 0; + uint32_t child_bitfield_bit_offset = 0; + bool child_is_base_class = false; + bool child_is_deref_of_parent = false; + const bool transparent_pointers = false; + ClangASTType clang_type = GetClangType(); + ClangASTType child_clang_type; + + ExecutionContext exe_ctx (GetExecutionContextRef()); + + child_clang_type = clang_type.GetChildClangTypeAtIndex (&exe_ctx, + GetName().GetCString(), + 0, + transparent_pointers, + omit_empty_base_classes, + ignore_array_bounds, + child_name_str, + child_byte_size, + child_byte_offset, + child_bitfield_bit_size, + child_bitfield_bit_offset, + child_is_base_class, + child_is_deref_of_parent); + if (child_clang_type && child_byte_size) + { + ConstString child_name; + if (!child_name_str.empty()) + child_name.SetCString (child_name_str.c_str()); + + m_deref_valobj = new ValueObjectChild (*this, + child_clang_type, + child_name, + child_byte_size, + child_byte_offset, + child_bitfield_bit_size, + child_bitfield_bit_offset, + child_is_base_class, + child_is_deref_of_parent, + eAddressTypeInvalid); + } + } + + if (m_deref_valobj) + { + error.Clear(); + return m_deref_valobj->GetSP(); + } + else + { + StreamString strm; + GetExpressionPath(strm, true); + + if (is_pointer_type) + error.SetErrorStringWithFormat("dereference failed: (%s) %s", GetTypeName().AsCString(""), strm.GetString().c_str()); + else + error.SetErrorStringWithFormat("not a pointer type: (%s) %s", GetTypeName().AsCString(""), strm.GetString().c_str()); + return ValueObjectSP(); + } +} + +ValueObjectSP +ValueObject::AddressOf (Error &error) +{ + if (m_addr_of_valobj_sp) + return m_addr_of_valobj_sp; + + AddressType address_type = eAddressTypeInvalid; + const bool scalar_is_load_address = false; + addr_t addr = GetAddressOf (scalar_is_load_address, &address_type); + error.Clear(); + if (addr != LLDB_INVALID_ADDRESS) + { + switch (address_type) + { + case eAddressTypeInvalid: + { + StreamString expr_path_strm; + GetExpressionPath(expr_path_strm, true); + error.SetErrorStringWithFormat("'%s' is not in memory", expr_path_strm.GetString().c_str()); + } + break; + + case eAddressTypeFile: + case eAddressTypeLoad: + case eAddressTypeHost: + { + ClangASTType clang_type = GetClangType(); + if (clang_type) + { + std::string name (1, '&'); + name.append (m_name.AsCString("")); + ExecutionContext exe_ctx (GetExecutionContextRef()); + m_addr_of_valobj_sp = ValueObjectConstResult::Create (exe_ctx.GetBestExecutionContextScope(), + clang_type.GetPointerType(), + ConstString (name.c_str()), + addr, + eAddressTypeInvalid, + m_data.GetAddressByteSize()); + } + } + break; + } + } + else + { + StreamString expr_path_strm; + GetExpressionPath(expr_path_strm, true); + error.SetErrorStringWithFormat("'%s' doesn't have a valid address", expr_path_strm.GetString().c_str()); + } + + return m_addr_of_valobj_sp; +} + +ValueObjectSP +ValueObject::Cast (const ClangASTType &clang_ast_type) +{ + return ValueObjectCast::Create (*this, GetName(), clang_ast_type); +} + +ValueObjectSP +ValueObject::CastPointerType (const char *name, ClangASTType &clang_ast_type) +{ + ValueObjectSP valobj_sp; + AddressType address_type; + addr_t ptr_value = GetPointerValue (&address_type); + + if (ptr_value != LLDB_INVALID_ADDRESS) + { + Address ptr_addr (ptr_value); + ExecutionContext exe_ctx (GetExecutionContextRef()); + valobj_sp = ValueObjectMemory::Create (exe_ctx.GetBestExecutionContextScope(), + name, + ptr_addr, + clang_ast_type); + } + return valobj_sp; +} + +ValueObjectSP +ValueObject::CastPointerType (const char *name, TypeSP &type_sp) +{ + ValueObjectSP valobj_sp; + AddressType address_type; + addr_t ptr_value = GetPointerValue (&address_type); + + if (ptr_value != LLDB_INVALID_ADDRESS) + { + Address ptr_addr (ptr_value); + ExecutionContext exe_ctx (GetExecutionContextRef()); + valobj_sp = ValueObjectMemory::Create (exe_ctx.GetBestExecutionContextScope(), + name, + ptr_addr, + type_sp); + } + return valobj_sp; +} + +ValueObject::EvaluationPoint::EvaluationPoint () : + m_mod_id(), + m_exe_ctx_ref(), + m_needs_update (true), + m_first_update (true) +{ +} + +ValueObject::EvaluationPoint::EvaluationPoint (ExecutionContextScope *exe_scope, bool use_selected): + m_mod_id(), + m_exe_ctx_ref(), + m_needs_update (true), + m_first_update (true) +{ + ExecutionContext exe_ctx(exe_scope); + TargetSP target_sp (exe_ctx.GetTargetSP()); + if (target_sp) + { + m_exe_ctx_ref.SetTargetSP (target_sp); + ProcessSP process_sp (exe_ctx.GetProcessSP()); + if (!process_sp) + process_sp = target_sp->GetProcessSP(); + + if (process_sp) + { + m_mod_id = process_sp->GetModID(); + m_exe_ctx_ref.SetProcessSP (process_sp); + + ThreadSP thread_sp (exe_ctx.GetThreadSP()); + + if (!thread_sp) + { + if (use_selected) + thread_sp = process_sp->GetThreadList().GetSelectedThread(); + } + + if (thread_sp) + { + m_exe_ctx_ref.SetThreadSP(thread_sp); + + StackFrameSP frame_sp (exe_ctx.GetFrameSP()); + if (!frame_sp) + { + if (use_selected) + frame_sp = thread_sp->GetSelectedFrame(); + } + if (frame_sp) + m_exe_ctx_ref.SetFrameSP(frame_sp); + } + } + } +} + +ValueObject::EvaluationPoint::EvaluationPoint (const ValueObject::EvaluationPoint &rhs) : + m_mod_id(), + m_exe_ctx_ref(rhs.m_exe_ctx_ref), + m_needs_update (true), + m_first_update (true) +{ +} + +ValueObject::EvaluationPoint::~EvaluationPoint () +{ +} + +// This function checks the EvaluationPoint against the current process state. If the current +// state matches the evaluation point, or the evaluation point is already invalid, then we return +// false, meaning "no change". If the current state is different, we update our state, and return +// true meaning "yes, change". If we did see a change, we also set m_needs_update to true, so +// future calls to NeedsUpdate will return true. +// exe_scope will be set to the current execution context scope. + +bool +ValueObject::EvaluationPoint::SyncWithProcessState() +{ + + // Start with the target, if it is NULL, then we're obviously not going to get any further: + ExecutionContext exe_ctx(m_exe_ctx_ref.Lock()); + + if (exe_ctx.GetTargetPtr() == NULL) + return false; + + // If we don't have a process nothing can change. + Process *process = exe_ctx.GetProcessPtr(); + if (process == NULL) + return false; + + // If our stop id is the current stop ID, nothing has changed: + ProcessModID current_mod_id = process->GetModID(); + + // If the current stop id is 0, either we haven't run yet, or the process state has been cleared. + // In either case, we aren't going to be able to sync with the process state. + if (current_mod_id.GetStopID() == 0) + return false; + + bool changed = false; + const bool was_valid = m_mod_id.IsValid(); + if (was_valid) + { + if (m_mod_id == current_mod_id) + { + // Everything is already up to date in this object, no need to + // update the execution context scope. + changed = false; + } + else + { + m_mod_id = current_mod_id; + m_needs_update = true; + changed = true; + } + } + + // Now re-look up the thread and frame in case the underlying objects have gone away & been recreated. + // That way we'll be sure to return a valid exe_scope. + // If we used to have a thread or a frame but can't find it anymore, then mark ourselves as invalid. + + if (m_exe_ctx_ref.HasThreadRef()) + { + ThreadSP thread_sp (m_exe_ctx_ref.GetThreadSP()); + if (thread_sp) + { + if (m_exe_ctx_ref.HasFrameRef()) + { + StackFrameSP frame_sp (m_exe_ctx_ref.GetFrameSP()); + if (!frame_sp) + { + // We used to have a frame, but now it is gone + SetInvalid(); + changed = was_valid; + } + } + } + else + { + // We used to have a thread, but now it is gone + SetInvalid(); + changed = was_valid; + } + + } + return changed; +} + +void +ValueObject::EvaluationPoint::SetUpdated () +{ + ProcessSP process_sp(m_exe_ctx_ref.GetProcessSP()); + if (process_sp) + m_mod_id = process_sp->GetModID(); + m_first_update = false; + m_needs_update = false; +} + + + +void +ValueObject::ClearUserVisibleData(uint32_t clear_mask) +{ + if ((clear_mask & eClearUserVisibleDataItemsValue) == eClearUserVisibleDataItemsValue) + m_value_str.clear(); + + if ((clear_mask & eClearUserVisibleDataItemsLocation) == eClearUserVisibleDataItemsLocation) + m_location_str.clear(); + + if ((clear_mask & eClearUserVisibleDataItemsSummary) == eClearUserVisibleDataItemsSummary) + { + m_summary_str.clear(); + } + + if ((clear_mask & eClearUserVisibleDataItemsDescription) == eClearUserVisibleDataItemsDescription) + m_object_desc_str.clear(); + + if ((clear_mask & eClearUserVisibleDataItemsSyntheticChildren) == eClearUserVisibleDataItemsSyntheticChildren) + { + if (m_synthetic_value) + m_synthetic_value = NULL; + } +} + +SymbolContextScope * +ValueObject::GetSymbolContextScope() +{ + if (m_parent) + { + if (!m_parent->IsPointerOrReferenceType()) + return m_parent->GetSymbolContextScope(); + } + return NULL; +} + +lldb::ValueObjectSP +ValueObject::CreateValueObjectFromExpression (const char* name, + const char* expression, + const ExecutionContext& exe_ctx) +{ + lldb::ValueObjectSP retval_sp; + lldb::TargetSP target_sp(exe_ctx.GetTargetSP()); + if (!target_sp) + return retval_sp; + if (!expression || !*expression) + return retval_sp; + target_sp->EvaluateExpression (expression, + exe_ctx.GetFrameSP().get(), + retval_sp); + if (retval_sp && name && *name) + retval_sp->SetName(ConstString(name)); + return retval_sp; +} + +lldb::ValueObjectSP +ValueObject::CreateValueObjectFromAddress (const char* name, + uint64_t address, + const ExecutionContext& exe_ctx, + ClangASTType type) +{ + if (type) + { + ClangASTType pointer_type(type.GetPointerType()); + if (pointer_type) + { + lldb::DataBufferSP buffer(new lldb_private::DataBufferHeap(&address,sizeof(lldb::addr_t))); + lldb::ValueObjectSP ptr_result_valobj_sp(ValueObjectConstResult::Create (exe_ctx.GetBestExecutionContextScope(), + pointer_type, + ConstString(name), + buffer, + lldb::endian::InlHostByteOrder(), + exe_ctx.GetAddressByteSize())); + if (ptr_result_valobj_sp) + { + ptr_result_valobj_sp->GetValue().SetValueType(Value::eValueTypeLoadAddress); + Error err; + ptr_result_valobj_sp = ptr_result_valobj_sp->Dereference(err); + if (ptr_result_valobj_sp && name && *name) + ptr_result_valobj_sp->SetName(ConstString(name)); + } + return ptr_result_valobj_sp; + } + } + return lldb::ValueObjectSP(); +} + +lldb::ValueObjectSP +ValueObject::CreateValueObjectFromData (const char* name, + DataExtractor& data, + const ExecutionContext& exe_ctx, + ClangASTType type) +{ + lldb::ValueObjectSP new_value_sp; + new_value_sp = ValueObjectConstResult::Create (exe_ctx.GetBestExecutionContextScope(), + type, + ConstString(name), + data, + LLDB_INVALID_ADDRESS); + new_value_sp->SetAddressTypeOfChildren(eAddressTypeLoad); + if (new_value_sp && name && *name) + new_value_sp->SetName(ConstString(name)); + return new_value_sp; +} + +ModuleSP +ValueObject::GetModule () +{ + ValueObject* root(GetRoot()); + if (root != this) + return root->GetModule(); + return lldb::ModuleSP(); +} + +ValueObject* +ValueObject::GetRoot () +{ + if (m_root) + return m_root; + ValueObject* parent = m_parent; + if (!parent) + return (m_root = this); + while (parent->m_parent) + { + if (parent->m_root) + return (m_root = parent->m_root); + parent = parent->m_parent; + } + return (m_root = parent); +} + +AddressType +ValueObject::GetAddressTypeOfChildren() +{ + if (m_address_type_of_ptr_or_ref_children == eAddressTypeInvalid) + { + ValueObject* root(GetRoot()); + if (root != this) + return root->GetAddressTypeOfChildren(); + } + return m_address_type_of_ptr_or_ref_children; +} + +lldb::DynamicValueType +ValueObject::GetDynamicValueType () +{ + ValueObject* with_dv_info = this; + while (with_dv_info) + { + if (with_dv_info->HasDynamicValueTypeInfo()) + return with_dv_info->GetDynamicValueTypeImpl(); + with_dv_info = with_dv_info->m_parent; + } + return lldb::eNoDynamicValues; +} + +lldb::Format +ValueObject::GetFormat () const +{ + const ValueObject* with_fmt_info = this; + while (with_fmt_info) + { + if (with_fmt_info->m_format != lldb::eFormatDefault) + return with_fmt_info->m_format; + with_fmt_info = with_fmt_info->m_parent; + } + return m_format; +} diff --git a/contrib/llvm/tools/lldb/source/Core/ValueObjectCast.cpp b/contrib/llvm/tools/lldb/source/Core/ValueObjectCast.cpp new file mode 100644 index 00000000000..4f4f8cc681d --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Core/ValueObjectCast.cpp @@ -0,0 +1,129 @@ +//===-- ValueObjectDynamicValue.cpp ---------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + + +#include "lldb/Core/ValueObjectCast.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/Log.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/ValueObjectList.h" +#include "lldb/Core/Value.h" +#include "lldb/Core/ValueObject.h" + +#include "lldb/Symbol/ClangASTType.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Symbol/SymbolContext.h" +#include "lldb/Symbol/Type.h" +#include "lldb/Symbol/Variable.h" + +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/LanguageRuntime.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" + +using namespace lldb_private; + +lldb::ValueObjectSP +ValueObjectCast::Create (ValueObject &parent, + const ConstString &name, + const ClangASTType &cast_type) +{ + ValueObjectCast *cast_valobj_ptr = new ValueObjectCast (parent, name, cast_type); + return cast_valobj_ptr->GetSP(); +} + +ValueObjectCast::ValueObjectCast +( + ValueObject &parent, + const ConstString &name, + const ClangASTType &cast_type +) : + ValueObject(parent), + m_cast_type (cast_type) +{ + SetName (name); + //m_value.SetContext (Value::eContextTypeClangType, cast_type.GetOpaqueQualType()); + m_value.SetClangType (cast_type); +} + +ValueObjectCast::~ValueObjectCast() +{ +} + +ClangASTType +ValueObjectCast::GetClangTypeImpl () +{ + return m_cast_type; +} + +size_t +ValueObjectCast::CalculateNumChildren() +{ + return GetClangType().GetNumChildren (true); +} + +uint64_t +ValueObjectCast::GetByteSize() +{ + return m_value.GetValueByteSize(NULL); +} + +lldb::ValueType +ValueObjectCast::GetValueType() const +{ + // Let our parent answer global, local, argument, etc... + return m_parent->GetValueType(); +} + +bool +ValueObjectCast::UpdateValue () +{ + SetValueIsValid (false); + m_error.Clear(); + + if (m_parent->UpdateValueIfNeeded(false)) + { + Value old_value(m_value); + m_update_point.SetUpdated(); + m_value = m_parent->GetValue(); + ClangASTType clang_type (GetClangType()); + //m_value.SetContext (Value::eContextTypeClangType, clang_type); + m_value.SetClangType (clang_type); + SetAddressTypeOfChildren(m_parent->GetAddressTypeOfChildren()); + if (clang_type.IsAggregateType ()) + { + // this value object represents an aggregate type whose + // children have values, but this object does not. So we + // say we are changed if our location has changed. + SetValueDidChange (m_value.GetValueType() != old_value.GetValueType() || m_value.GetScalar() != old_value.GetScalar()); + } + ExecutionContext exe_ctx (GetExecutionContextRef()); + m_error = m_value.GetValueAsData(&exe_ctx, m_data, 0, GetModule().get()); + SetValueDidChange (m_parent->GetValueDidChange()); + return true; + } + + // The dynamic value failed to get an error, pass the error along + if (m_error.Success() && m_parent->GetError().Fail()) + m_error = m_parent->GetError(); + SetValueIsValid (false); + return false; +} + +bool +ValueObjectCast::IsInScope () +{ + return m_parent->IsInScope(); +} diff --git a/contrib/llvm/tools/lldb/source/Core/ValueObjectChild.cpp b/contrib/llvm/tools/lldb/source/Core/ValueObjectChild.cpp new file mode 100644 index 00000000000..23add1ccf0e --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Core/ValueObjectChild.cpp @@ -0,0 +1,234 @@ +//===-- ValueObjectChild.cpp ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Core/ValueObjectChild.h" + +#include "lldb/Core/Module.h" +#include "lldb/Core/ValueObjectList.h" + +#include "lldb/Symbol/ClangASTType.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Symbol/SymbolContext.h" +#include "lldb/Symbol/Type.h" +#include "lldb/Symbol/Variable.h" + +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Target.h" + +using namespace lldb_private; + +ValueObjectChild::ValueObjectChild +( + ValueObject &parent, + const ClangASTType &clang_type, + const ConstString &name, + uint64_t byte_size, + int32_t byte_offset, + uint32_t bitfield_bit_size, + uint32_t bitfield_bit_offset, + bool is_base_class, + bool is_deref_of_parent, + AddressType child_ptr_or_ref_addr_type +) : + ValueObject (parent), + m_clang_type (clang_type), + m_byte_size (byte_size), + m_byte_offset (byte_offset), + m_bitfield_bit_size (bitfield_bit_size), + m_bitfield_bit_offset (bitfield_bit_offset), + m_is_base_class (is_base_class), + m_is_deref_of_parent (is_deref_of_parent) +{ + m_name = name; + SetAddressTypeOfChildren(child_ptr_or_ref_addr_type); +} + +ValueObjectChild::~ValueObjectChild() +{ +} + +lldb::ValueType +ValueObjectChild::GetValueType() const +{ + return m_parent->GetValueType(); +} + +size_t +ValueObjectChild::CalculateNumChildren() +{ + return GetClangType().GetNumChildren (true); +} + +ConstString +ValueObjectChild::GetTypeName() +{ + if (m_type_name.IsEmpty()) + { + m_type_name = GetClangType().GetConstTypeName (); + if (m_type_name) + { + if (m_bitfield_bit_size > 0) + { + const char *clang_type_name = m_type_name.AsCString(); + if (clang_type_name) + { + std::vector bitfield_type_name (strlen(clang_type_name) + 32, 0); + ::snprintf (&bitfield_type_name.front(), bitfield_type_name.size(), "%s:%u", clang_type_name, m_bitfield_bit_size); + m_type_name.SetCString(&bitfield_type_name.front()); + } + } + } + } + return m_type_name; +} + +ConstString +ValueObjectChild::GetQualifiedTypeName() +{ + ConstString qualified_name = GetClangType().GetConstTypeName(); + if (qualified_name) + { + if (m_bitfield_bit_size > 0) + { + const char *clang_type_name = qualified_name.AsCString(); + if (clang_type_name) + { + std::vector bitfield_type_name (strlen(clang_type_name) + 32, 0); + ::snprintf (&bitfield_type_name.front(), bitfield_type_name.size(), "%s:%u", clang_type_name, m_bitfield_bit_size); + qualified_name.SetCString(&bitfield_type_name.front()); + } + } + } + return qualified_name; +} + +bool +ValueObjectChild::UpdateValue () +{ + m_error.Clear(); + SetValueIsValid (false); + ValueObject* parent = m_parent; + if (parent) + { + if (parent->UpdateValueIfNeeded(false)) + { + m_value.SetClangType(GetClangType()); + + // Copy the parent scalar value and the scalar value type + m_value.GetScalar() = parent->GetValue().GetScalar(); + Value::ValueType value_type = parent->GetValue().GetValueType(); + m_value.SetValueType (value_type); + + if (parent->GetClangType().IsPointerOrReferenceType ()) + { + lldb::addr_t addr = parent->GetPointerValue (); + m_value.GetScalar() = addr; + + if (addr == LLDB_INVALID_ADDRESS) + { + m_error.SetErrorString ("parent address is invalid."); + } + else if (addr == 0) + { + m_error.SetErrorString ("parent is NULL"); + } + else + { + m_value.GetScalar() += m_byte_offset; + AddressType addr_type = parent->GetAddressTypeOfChildren(); + + switch (addr_type) + { + case eAddressTypeFile: + { + lldb::ProcessSP process_sp (GetProcessSP()); + if (process_sp && process_sp->IsAlive() == true) + m_value.SetValueType (Value::eValueTypeLoadAddress); + else + m_value.SetValueType(Value::eValueTypeFileAddress); + } + break; + case eAddressTypeLoad: + m_value.SetValueType (Value::eValueTypeLoadAddress); + break; + case eAddressTypeHost: + m_value.SetValueType(Value::eValueTypeHostAddress); + break; + case eAddressTypeInvalid: + // TODO: does this make sense? + m_value.SetValueType(Value::eValueTypeScalar); + break; + } + } + } + else + { + switch (value_type) + { + case Value::eValueTypeLoadAddress: + case Value::eValueTypeFileAddress: + case Value::eValueTypeHostAddress: + { + lldb::addr_t addr = m_value.GetScalar().ULongLong(LLDB_INVALID_ADDRESS); + if (addr == LLDB_INVALID_ADDRESS) + { + m_error.SetErrorString ("parent address is invalid."); + } + else if (addr == 0) + { + m_error.SetErrorString ("parent is NULL"); + } + else + { + // Set this object's scalar value to the address of its + // value by adding its byte offset to the parent address + m_value.GetScalar() += GetByteOffset(); + } + } + break; + + case Value::eValueTypeScalar: + // TODO: What if this is a register value? Do we try and + // extract the child value from within the parent data? + // Probably... + default: + m_error.SetErrorString ("parent has invalid value."); + break; + } + } + + if (m_error.Success()) + { + ExecutionContext exe_ctx (GetExecutionContextRef().Lock()); + m_error = m_value.GetValueAsData (&exe_ctx, m_data, 0, GetModule().get()); + } + } + else + { + m_error.SetErrorStringWithFormat("parent failed to evaluate: %s", parent->GetError().AsCString()); + } + } + else + { + m_error.SetErrorString("ValueObjectChild has a NULL parent ValueObject."); + } + + return m_error.Success(); +} + + +bool +ValueObjectChild::IsInScope () +{ + ValueObject* root(GetRoot()); + if (root) + return root->IsInScope (); + return false; +} diff --git a/contrib/llvm/tools/lldb/source/Core/ValueObjectConstResult.cpp b/contrib/llvm/tools/lldb/source/Core/ValueObjectConstResult.cpp new file mode 100644 index 00000000000..d6d86381358 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Core/ValueObjectConstResult.cpp @@ -0,0 +1,354 @@ +//===-- ValueObjectConstResult.cpp ------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Core/ValueObjectConstResult.h" + +#include "lldb/Core/ValueObjectChild.h" +#include "lldb/Core/ValueObjectConstResultChild.h" +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/ValueObjectDynamicValue.h" +#include "lldb/Core/ValueObjectList.h" + +#include "lldb/Symbol/ClangASTType.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Symbol/SymbolContext.h" +#include "lldb/Symbol/Type.h" +#include "lldb/Symbol/Variable.h" + +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Target.h" + +using namespace lldb; +using namespace lldb_private; + +ValueObjectSP +ValueObjectConstResult::Create (ExecutionContextScope *exe_scope, + ByteOrder byte_order, + uint32_t addr_byte_size, + lldb::addr_t address) +{ + return (new ValueObjectConstResult (exe_scope, + byte_order, + addr_byte_size, + address))->GetSP(); +} + +ValueObjectConstResult::ValueObjectConstResult (ExecutionContextScope *exe_scope, + ByteOrder byte_order, + uint32_t addr_byte_size, + lldb::addr_t address) : + ValueObject (exe_scope), + m_type_name (), + m_byte_size (0), + m_impl(this, address) +{ + SetIsConstant (); + SetValueIsValid(true); + m_data.SetByteOrder(byte_order); + m_data.SetAddressByteSize(addr_byte_size); + SetAddressTypeOfChildren(eAddressTypeLoad); +} + +ValueObjectSP +ValueObjectConstResult::Create +( + ExecutionContextScope *exe_scope, + const ClangASTType &clang_type, + const ConstString &name, + const DataExtractor &data, + lldb::addr_t address +) +{ + return (new ValueObjectConstResult (exe_scope, + clang_type, + name, + data, + address))->GetSP(); +} + +ValueObjectConstResult::ValueObjectConstResult (ExecutionContextScope *exe_scope, + const ClangASTType &clang_type, + const ConstString &name, + const DataExtractor &data, + lldb::addr_t address) : + ValueObject (exe_scope), + m_type_name (), + m_byte_size (0), + m_impl(this, address) +{ + m_data = data; + + if (!m_data.GetSharedDataBuffer()) + { + DataBufferSP shared_data_buffer(new DataBufferHeap(data.GetDataStart(), data.GetByteSize())); + m_data.SetData(shared_data_buffer); + } + + m_value.GetScalar() = (uintptr_t)m_data.GetDataStart(); + m_value.SetValueType(Value::eValueTypeHostAddress); + m_value.SetClangType(clang_type); + m_name = name; + SetIsConstant (); + SetValueIsValid(true); + SetAddressTypeOfChildren(eAddressTypeLoad); +} + +ValueObjectSP +ValueObjectConstResult::Create (ExecutionContextScope *exe_scope, + const ClangASTType &clang_type, + const ConstString &name, + const lldb::DataBufferSP &data_sp, + lldb::ByteOrder data_byte_order, + uint32_t data_addr_size, + lldb::addr_t address) +{ + return (new ValueObjectConstResult (exe_scope, + clang_type, + name, + data_sp, + data_byte_order, + data_addr_size, + address))->GetSP(); +} + +ValueObjectSP +ValueObjectConstResult::Create (ExecutionContextScope *exe_scope, + Value &value, + const ConstString &name) +{ + return (new ValueObjectConstResult (exe_scope, value, name))->GetSP(); +} + +ValueObjectConstResult::ValueObjectConstResult (ExecutionContextScope *exe_scope, + const ClangASTType &clang_type, + const ConstString &name, + const lldb::DataBufferSP &data_sp, + lldb::ByteOrder data_byte_order, + uint32_t data_addr_size, + lldb::addr_t address) : + ValueObject (exe_scope), + m_type_name (), + m_byte_size (0), + m_impl(this, address) +{ + m_data.SetByteOrder(data_byte_order); + m_data.SetAddressByteSize(data_addr_size); + m_data.SetData(data_sp); + m_value.GetScalar() = (uintptr_t)data_sp->GetBytes(); + m_value.SetValueType(Value::eValueTypeHostAddress); + //m_value.SetContext(Value::eContextTypeClangType, clang_type); + m_value.SetClangType (clang_type); + m_name = name; + SetIsConstant (); + SetValueIsValid(true); + SetAddressTypeOfChildren(eAddressTypeLoad); +} + +ValueObjectSP +ValueObjectConstResult::Create (ExecutionContextScope *exe_scope, + const ClangASTType &clang_type, + const ConstString &name, + lldb::addr_t address, + AddressType address_type, + uint32_t addr_byte_size) +{ + return (new ValueObjectConstResult (exe_scope, + clang_type, + name, + address, + address_type, + addr_byte_size))->GetSP(); +} + +ValueObjectConstResult::ValueObjectConstResult (ExecutionContextScope *exe_scope, + const ClangASTType &clang_type, + const ConstString &name, + lldb::addr_t address, + AddressType address_type, + uint32_t addr_byte_size) : + ValueObject (exe_scope), + m_type_name (), + m_byte_size (0), + m_impl(this, address) +{ + m_value.GetScalar() = address; + m_data.SetAddressByteSize(addr_byte_size); + m_value.GetScalar().GetData (m_data, addr_byte_size); + //m_value.SetValueType(Value::eValueTypeHostAddress); + switch (address_type) + { + case eAddressTypeInvalid: m_value.SetValueType(Value::eValueTypeScalar); break; + case eAddressTypeFile: m_value.SetValueType(Value::eValueTypeFileAddress); break; + case eAddressTypeLoad: m_value.SetValueType(Value::eValueTypeLoadAddress); break; + case eAddressTypeHost: m_value.SetValueType(Value::eValueTypeHostAddress); break; + } +// m_value.SetContext(Value::eContextTypeClangType, clang_type); + m_value.SetClangType (clang_type); + m_name = name; + SetIsConstant (); + SetValueIsValid(true); + SetAddressTypeOfChildren(eAddressTypeLoad); +} + +ValueObjectSP +ValueObjectConstResult::Create +( + ExecutionContextScope *exe_scope, + const Error& error +) +{ + return (new ValueObjectConstResult (exe_scope, + error))->GetSP(); +} + +ValueObjectConstResult::ValueObjectConstResult (ExecutionContextScope *exe_scope, + const Error& error) : + ValueObject (exe_scope), + m_type_name (), + m_byte_size (0), + m_impl(this) +{ + m_error = error; + SetIsConstant (); +} + +ValueObjectConstResult::ValueObjectConstResult (ExecutionContextScope *exe_scope, + const Value &value, + const ConstString &name) : + ValueObject (exe_scope), + m_type_name (), + m_byte_size (0), + m_impl(this) +{ + m_value = value; + m_value.GetData(m_data); +} + +ValueObjectConstResult::~ValueObjectConstResult() +{ +} + +ClangASTType +ValueObjectConstResult::GetClangTypeImpl() +{ + return m_value.GetClangType(); +} + +lldb::ValueType +ValueObjectConstResult::GetValueType() const +{ + return eValueTypeConstResult; +} + +uint64_t +ValueObjectConstResult::GetByteSize() +{ + if (m_byte_size == 0) + m_byte_size = GetClangType().GetByteSize(); + return m_byte_size; +} + +void +ValueObjectConstResult::SetByteSize (size_t size) +{ + m_byte_size = size; +} + +size_t +ValueObjectConstResult::CalculateNumChildren() +{ + return GetClangType().GetNumChildren (true); +} + +ConstString +ValueObjectConstResult::GetTypeName() +{ + if (m_type_name.IsEmpty()) + m_type_name = GetClangType().GetConstTypeName (); + return m_type_name; +} + +bool +ValueObjectConstResult::UpdateValue () +{ + // Const value is always valid + SetValueIsValid (true); + return true; +} + + +bool +ValueObjectConstResult::IsInScope () +{ + // A const result value is always in scope since it serializes all + // information needed to contain the constant value. + return true; +} + +lldb::ValueObjectSP +ValueObjectConstResult::Dereference (Error &error) +{ + return m_impl.Dereference(error); +} + +lldb::ValueObjectSP +ValueObjectConstResult::GetSyntheticChildAtOffset(uint32_t offset, const ClangASTType& type, bool can_create) +{ + return m_impl.GetSyntheticChildAtOffset(offset, type, can_create); +} + +lldb::ValueObjectSP +ValueObjectConstResult::AddressOf (Error &error) +{ + return m_impl.AddressOf(error); +} + +lldb::addr_t +ValueObjectConstResult::GetAddressOf (bool scalar_is_load_address, + AddressType *address_type) +{ + return m_impl.GetAddressOf(scalar_is_load_address, address_type); +} + +ValueObject * +ValueObjectConstResult::CreateChildAtIndex (size_t idx, bool synthetic_array_member, int32_t synthetic_index) +{ + return m_impl.CreateChildAtIndex(idx, synthetic_array_member, synthetic_index); +} + +size_t +ValueObjectConstResult::GetPointeeData (DataExtractor& data, + uint32_t item_idx, + uint32_t item_count) +{ + return m_impl.GetPointeeData(data, item_idx, item_count); +} + +lldb::ValueObjectSP +ValueObjectConstResult::GetDynamicValue (lldb::DynamicValueType use_dynamic) +{ + // Always recalculate dynamic values for const results as the memory that + // they might point to might have changed at any time. + if (use_dynamic != eNoDynamicValues) + { + if (!IsDynamic()) + { + ExecutionContext exe_ctx (GetExecutionContextRef()); + Process *process = exe_ctx.GetProcessPtr(); + if (process && process->IsPossibleDynamicValue(*this)) + m_dynamic_value = new ValueObjectDynamicValue (*this, use_dynamic); + } + if (m_dynamic_value) + return m_dynamic_value->GetSP(); + } + return ValueObjectSP(); +} + diff --git a/contrib/llvm/tools/lldb/source/Core/ValueObjectConstResultChild.cpp b/contrib/llvm/tools/lldb/source/Core/ValueObjectConstResultChild.cpp new file mode 100644 index 00000000000..64425ea5096 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Core/ValueObjectConstResultChild.cpp @@ -0,0 +1,80 @@ +//===-- ValueObjectConstResultChild.cpp ------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Core/ValueObjectConstResultChild.h" + +#include "lldb/Core/ValueObjectConstResult.h" +#include "lldb/Core/ValueObjectList.h" + +#include "lldb/Symbol/ClangASTContext.h" + +using namespace lldb_private; + +ValueObjectConstResultChild::ValueObjectConstResultChild +( + ValueObject &parent, + const ClangASTType &clang_type, + const ConstString &name, + uint32_t byte_size, + int32_t byte_offset, + uint32_t bitfield_bit_size, + uint32_t bitfield_bit_offset, + bool is_base_class, + bool is_deref_of_parent +) : + ValueObjectChild (parent, + clang_type, + name, + byte_size, + byte_offset, + bitfield_bit_size, + bitfield_bit_offset, + is_base_class, + is_deref_of_parent, + eAddressTypeLoad), + m_impl(this) +{ + m_name = name; +} + +ValueObjectConstResultChild::~ValueObjectConstResultChild() +{ +} + +lldb::ValueObjectSP +ValueObjectConstResultChild::Dereference (Error &error) +{ + return m_impl.Dereference(error); +} + +lldb::ValueObjectSP +ValueObjectConstResultChild::GetSyntheticChildAtOffset(uint32_t offset, const ClangASTType& type, bool can_create) +{ + return m_impl.GetSyntheticChildAtOffset(offset, type, can_create); +} + +lldb::ValueObjectSP +ValueObjectConstResultChild::AddressOf (Error &error) +{ + return m_impl.AddressOf(error); +} + +ValueObject * +ValueObjectConstResultChild::CreateChildAtIndex (size_t idx, bool synthetic_array_member, int32_t synthetic_index) +{ + return m_impl.CreateChildAtIndex(idx, synthetic_array_member, synthetic_index); +} + +size_t +ValueObjectConstResultChild::GetPointeeData (DataExtractor& data, + uint32_t item_idx, + uint32_t item_count) +{ + return m_impl.GetPointeeData(data, item_idx, item_count); +} diff --git a/contrib/llvm/tools/lldb/source/Core/ValueObjectConstResultImpl.cpp b/contrib/llvm/tools/lldb/source/Core/ValueObjectConstResultImpl.cpp new file mode 100644 index 00000000000..e0757f60cdb --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Core/ValueObjectConstResultImpl.cpp @@ -0,0 +1,236 @@ +//===-- ValueObjectConstResultImpl.cpp ---------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Core/ValueObjectConstResultImpl.h" + +#include "lldb/Core/ValueObjectChild.h" +#include "lldb/Core/ValueObjectConstResult.h" +#include "lldb/Core/ValueObjectConstResultChild.h" +#include "lldb/Core/ValueObjectMemory.h" +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/ValueObjectList.h" + +#include "lldb/Symbol/ClangASTType.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Symbol/SymbolContext.h" +#include "lldb/Symbol/Type.h" +#include "lldb/Symbol/Variable.h" + +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Target.h" + +using namespace lldb; +using namespace lldb_private; + +// this macro enables a simpler implementation for some method calls in this object that relies only upon +// ValueObject knowning how to set the address type of its children correctly. the alternative implementation +// relies on being able to create a target copy of the frozen object, which makes it less bug-prone but less +// efficient as well. once we are confident the faster implementation is bug-free, this macro (and the slower +// implementations) can go +#define TRIVIAL_IMPL 1 + +ValueObjectConstResultImpl::ValueObjectConstResultImpl (ValueObject* valobj, + lldb::addr_t live_address) : + m_impl_backend(valobj), + m_live_address(live_address), + m_live_address_type(eAddressTypeLoad), + m_load_addr_backend(), + m_address_of_backend() +{ +} + +lldb::ValueObjectSP +ValueObjectConstResultImpl::DerefOnTarget() +{ + if (m_load_addr_backend.get() == NULL) + { + lldb::addr_t tgt_address = m_impl_backend->GetPointerValue(); + ExecutionContext exe_ctx (m_impl_backend->GetExecutionContextRef()); + m_load_addr_backend = ValueObjectConstResult::Create (exe_ctx.GetBestExecutionContextScope(), + m_impl_backend->GetClangType(), + m_impl_backend->GetName(), + tgt_address, + eAddressTypeLoad, + exe_ctx.GetAddressByteSize()); + } + return m_load_addr_backend; +} + +lldb::ValueObjectSP +ValueObjectConstResultImpl::Dereference (Error &error) +{ + if (m_impl_backend == NULL) + return lldb::ValueObjectSP(); + +#if defined (TRIVIAL_IMPL) && TRIVIAL_IMPL == 1 + return m_impl_backend->ValueObject::Dereference(error); +#else + m_impl_backend->UpdateValueIfNeeded(false); + + if (NeedsDerefOnTarget()) + return DerefOnTarget()->Dereference(error); + else + return m_impl_backend->ValueObject::Dereference(error); +#endif +} + +ValueObject * +ValueObjectConstResultImpl::CreateChildAtIndex (size_t idx, bool synthetic_array_member, int32_t synthetic_index) +{ + if (m_impl_backend == NULL) + return NULL; + + m_impl_backend->UpdateValueIfNeeded(false); + + ValueObjectConstResultChild *valobj = NULL; + + bool omit_empty_base_classes = true; + bool ignore_array_bounds = synthetic_array_member; + std::string child_name_str; + uint32_t child_byte_size = 0; + int32_t child_byte_offset = 0; + uint32_t child_bitfield_bit_size = 0; + uint32_t child_bitfield_bit_offset = 0; + bool child_is_base_class = false; + bool child_is_deref_of_parent = false; + + const bool transparent_pointers = synthetic_array_member == false; + ClangASTType clang_type = m_impl_backend->GetClangType(); + ClangASTType child_clang_type; + + ExecutionContext exe_ctx (m_impl_backend->GetExecutionContextRef()); + + child_clang_type = clang_type.GetChildClangTypeAtIndex (&exe_ctx, + m_impl_backend->GetName().GetCString(), + idx, + transparent_pointers, + omit_empty_base_classes, + ignore_array_bounds, + child_name_str, + child_byte_size, + child_byte_offset, + child_bitfield_bit_size, + child_bitfield_bit_offset, + child_is_base_class, + child_is_deref_of_parent); + if (child_clang_type && child_byte_size) + { + if (synthetic_index) + child_byte_offset += child_byte_size * synthetic_index; + + ConstString child_name; + if (!child_name_str.empty()) + child_name.SetCString (child_name_str.c_str()); + + valobj = new ValueObjectConstResultChild (*m_impl_backend, + child_clang_type, + child_name, + child_byte_size, + child_byte_offset, + child_bitfield_bit_size, + child_bitfield_bit_offset, + child_is_base_class, + child_is_deref_of_parent); + valobj->m_impl.SetLiveAddress(m_live_address+child_byte_offset); + } + + return valobj; +} + +lldb::ValueObjectSP +ValueObjectConstResultImpl::GetSyntheticChildAtOffset (uint32_t offset, const ClangASTType& type, bool can_create) +{ + if (m_impl_backend == NULL) + return lldb::ValueObjectSP(); + +#if defined (TRIVIAL_IMPL) && TRIVIAL_IMPL == 1 + return m_impl_backend->ValueObject::GetSyntheticChildAtOffset(offset, type, can_create); +#else + m_impl_backend->UpdateValueIfNeeded(false); + + if (NeedsDerefOnTarget()) + return DerefOnTarget()->GetSyntheticChildAtOffset(offset, type, can_create); + else + return m_impl_backend->ValueObject::GetSyntheticChildAtOffset(offset, type, can_create); +#endif +} + +lldb::ValueObjectSP +ValueObjectConstResultImpl::AddressOf (Error &error) +{ + if (m_address_of_backend.get() != NULL) + return m_address_of_backend; + + if (m_impl_backend == NULL) + return lldb::ValueObjectSP(); + if (m_live_address != LLDB_INVALID_ADDRESS) + { + ClangASTType clang_type(m_impl_backend->GetClangType()); + + lldb::DataBufferSP buffer(new lldb_private::DataBufferHeap(&m_live_address,sizeof(lldb::addr_t))); + + std::string new_name("&"); + new_name.append(m_impl_backend->GetName().AsCString("")); + ExecutionContext exe_ctx (m_impl_backend->GetExecutionContextRef()); + m_address_of_backend = ValueObjectConstResult::Create (exe_ctx.GetBestExecutionContextScope(), + clang_type.GetPointerType(), + ConstString(new_name.c_str()), + buffer, + lldb::endian::InlHostByteOrder(), + exe_ctx.GetAddressByteSize()); + + m_address_of_backend->GetValue().SetValueType(Value::eValueTypeScalar); + m_address_of_backend->GetValue().GetScalar() = m_live_address; + + return m_address_of_backend; + } + else + return lldb::ValueObjectSP(); +} + +lldb::addr_t +ValueObjectConstResultImpl::GetAddressOf (bool scalar_is_load_address, + AddressType *address_type) +{ + + if (m_impl_backend == NULL) + return 0; + + if (m_live_address == LLDB_INVALID_ADDRESS) + { + return m_impl_backend->ValueObject::GetAddressOf (scalar_is_load_address, + address_type); + } + + if (address_type) + *address_type = m_live_address_type; + + return m_live_address; +} + +size_t +ValueObjectConstResultImpl::GetPointeeData (DataExtractor& data, + uint32_t item_idx, + uint32_t item_count) +{ + if (m_impl_backend == NULL) + return 0; +#if defined (TRIVIAL_IMPL) && TRIVIAL_IMPL == 1 + return m_impl_backend->ValueObject::GetPointeeData(data, item_idx, item_count); +#else + m_impl_backend->UpdateValueIfNeeded(false); + + if (NeedsDerefOnTarget() && m_impl_backend->IsPointerType()) + return DerefOnTarget()->GetPointeeData(data, item_idx, item_count); + else + return m_impl_backend->ValueObject::GetPointeeData(data, item_idx, item_count); +#endif +} diff --git a/contrib/llvm/tools/lldb/source/Core/ValueObjectDynamicValue.cpp b/contrib/llvm/tools/lldb/source/Core/ValueObjectDynamicValue.cpp new file mode 100644 index 00000000000..977cc4cd313 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Core/ValueObjectDynamicValue.cpp @@ -0,0 +1,372 @@ +//===-- ValueObjectDynamicValue.cpp ---------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + + +#include "lldb/Core/ValueObjectDynamicValue.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/Log.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/ValueObjectList.h" +#include "lldb/Core/Value.h" +#include "lldb/Core/ValueObject.h" + +#include "lldb/Symbol/ClangASTType.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Symbol/SymbolContext.h" +#include "lldb/Symbol/Type.h" +#include "lldb/Symbol/Variable.h" + +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/LanguageRuntime.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" + +using namespace lldb_private; + +ValueObjectDynamicValue::ValueObjectDynamicValue (ValueObject &parent, lldb::DynamicValueType use_dynamic) : + ValueObject(parent), + m_address (), + m_dynamic_type_info(), + m_use_dynamic (use_dynamic) +{ + SetName (parent.GetName()); +} + +ValueObjectDynamicValue::~ValueObjectDynamicValue() +{ + m_owning_valobj_sp.reset(); +} + +ClangASTType +ValueObjectDynamicValue::GetClangTypeImpl () +{ + if (m_dynamic_type_info.HasTypeSP()) + return m_value.GetClangType(); + else + return m_parent->GetClangType(); +} + +ConstString +ValueObjectDynamicValue::GetTypeName() +{ + const bool success = UpdateValueIfNeeded(false); + if (success) + { + if (m_dynamic_type_info.HasTypeSP()) + return GetClangType().GetConstTypeName(); + if (m_dynamic_type_info.HasName()) + return m_dynamic_type_info.GetName(); + } + return m_parent->GetTypeName(); +} + +ConstString +ValueObjectDynamicValue::GetQualifiedTypeName() +{ + const bool success = UpdateValueIfNeeded(false); + if (success) + { + if (m_dynamic_type_info.HasTypeSP()) + return GetClangType().GetConstQualifiedTypeName (); + if (m_dynamic_type_info.HasName()) + return m_dynamic_type_info.GetName(); + } + return m_parent->GetTypeName(); +} + +size_t +ValueObjectDynamicValue::CalculateNumChildren() +{ + const bool success = UpdateValueIfNeeded(false); + if (success && m_dynamic_type_info.HasTypeSP()) + return GetClangType().GetNumChildren (true); + else + return m_parent->GetNumChildren(); +} + +uint64_t +ValueObjectDynamicValue::GetByteSize() +{ + const bool success = UpdateValueIfNeeded(false); + if (success && m_dynamic_type_info.HasTypeSP()) + return m_value.GetValueByteSize(NULL); + else + return m_parent->GetByteSize(); +} + +lldb::ValueType +ValueObjectDynamicValue::GetValueType() const +{ + return m_parent->GetValueType(); +} + +bool +ValueObjectDynamicValue::UpdateValue () +{ + SetValueIsValid (false); + m_error.Clear(); + + if (!m_parent->UpdateValueIfNeeded(false)) + { + // The dynamic value failed to get an error, pass the error along + if (m_error.Success() && m_parent->GetError().Fail()) + m_error = m_parent->GetError(); + return false; + } + + // Setting our type_sp to NULL will route everything back through our + // parent which is equivalent to not using dynamic values. + if (m_use_dynamic == lldb::eNoDynamicValues) + { + m_dynamic_type_info.Clear(); + return true; + } + + ExecutionContext exe_ctx (GetExecutionContextRef()); + Target *target = exe_ctx.GetTargetPtr(); + if (target) + { + m_data.SetByteOrder(target->GetArchitecture().GetByteOrder()); + m_data.SetAddressByteSize(target->GetArchitecture().GetAddressByteSize()); + } + + // First make sure our Type and/or Address haven't changed: + Process *process = exe_ctx.GetProcessPtr(); + if (!process) + return false; + + TypeAndOrName class_type_or_name; + Address dynamic_address; + bool found_dynamic_type = false; + + lldb::LanguageType known_type = m_parent->GetObjectRuntimeLanguage(); + if (known_type != lldb::eLanguageTypeUnknown && known_type != lldb::eLanguageTypeC) + { + LanguageRuntime *runtime = process->GetLanguageRuntime (known_type); + if (runtime) + found_dynamic_type = runtime->GetDynamicTypeAndAddress (*m_parent, m_use_dynamic, class_type_or_name, dynamic_address); + } + else + { + LanguageRuntime *cpp_runtime = process->GetLanguageRuntime (lldb::eLanguageTypeC_plus_plus); + if (cpp_runtime) + found_dynamic_type = cpp_runtime->GetDynamicTypeAndAddress (*m_parent, m_use_dynamic, class_type_or_name, dynamic_address); + + if (!found_dynamic_type) + { + LanguageRuntime *objc_runtime = process->GetLanguageRuntime (lldb::eLanguageTypeObjC); + if (objc_runtime) + found_dynamic_type = objc_runtime->GetDynamicTypeAndAddress (*m_parent, m_use_dynamic, class_type_or_name, dynamic_address); + } + } + + // Getting the dynamic value may have run the program a bit, and so marked us as needing updating, but we really + // don't... + + m_update_point.SetUpdated(); + + // If we don't have a dynamic type, then make ourselves just a echo of our parent. + // Or we could return false, and make ourselves an echo of our parent? + if (!found_dynamic_type) + { + if (m_dynamic_type_info) + SetValueDidChange(true); + ClearDynamicTypeInformation(); + m_dynamic_type_info.Clear(); + m_value = m_parent->GetValue(); + m_error = m_value.GetValueAsData (&exe_ctx, m_data, 0, GetModule().get()); + return m_error.Success(); + } + + Value old_value(m_value); + + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_TYPES)); + + bool has_changed_type = false; + + if (!m_dynamic_type_info) + { + m_dynamic_type_info = class_type_or_name; + has_changed_type = true; + } + else if (class_type_or_name != m_dynamic_type_info) + { + // We are another type, we need to tear down our children... + m_dynamic_type_info = class_type_or_name; + SetValueDidChange (true); + has_changed_type = true; + } + + if (has_changed_type) + ClearDynamicTypeInformation (); + + if (!m_address.IsValid() || m_address != dynamic_address) + { + if (m_address.IsValid()) + SetValueDidChange (true); + + // We've moved, so we should be fine... + m_address = dynamic_address; + lldb::TargetSP target_sp (GetTargetSP()); + lldb::addr_t load_address = m_address.GetLoadAddress(target_sp.get()); + m_value.GetScalar() = load_address; + } + + ClangASTType corrected_type; + if (m_dynamic_type_info.HasTypeSP()) + { + // The type will always be the type of the dynamic object. If our parent's type was a pointer, + // then our type should be a pointer to the type of the dynamic object. If a reference, then the original type + // should be okay... + ClangASTType orig_type = m_dynamic_type_info.GetTypeSP()->GetClangForwardType(); + corrected_type = orig_type; + if (m_parent->IsPointerType()) + corrected_type = orig_type.GetPointerType (); + else if (m_parent->IsPointerOrReferenceType()) + corrected_type = orig_type.GetLValueReferenceType (); + } + else /*if (m_dynamic_type_info.HasName())*/ + { + // If we are here we need to adjust our dynamic type name to include the correct & or * symbol + std::string type_name_buf (m_dynamic_type_info.GetName().GetCString()); + if (m_parent->IsPointerType()) + type_name_buf.append(" *"); + else if (m_parent->IsPointerOrReferenceType()) + type_name_buf.append(" &"); + corrected_type = m_parent->GetClangType(); + m_dynamic_type_info.SetName(type_name_buf.c_str()); + } + + //m_value.SetContext (Value::eContextTypeClangType, corrected_type); + m_value.SetClangType (corrected_type); + + // Our address is the location of the dynamic type stored in memory. It isn't a load address, + // because we aren't pointing to the LOCATION that stores the pointer to us, we're pointing to us... + m_value.SetValueType(Value::eValueTypeScalar); + + if (has_changed_type && log) + log->Printf("[%s %p] has a new dynamic type %s", + GetName().GetCString(), + this, + GetTypeName().GetCString()); + + if (m_address.IsValid() && m_dynamic_type_info) + { + // The variable value is in the Scalar value inside the m_value. + // We can point our m_data right to it. + m_error = m_value.GetValueAsData (&exe_ctx, m_data, 0, GetModule().get()); + if (m_error.Success()) + { + if (GetClangType().IsAggregateType ()) + { + // this value object represents an aggregate type whose + // children have values, but this object does not. So we + // say we are changed if our location has changed. + SetValueDidChange (m_value.GetValueType() != old_value.GetValueType() || m_value.GetScalar() != old_value.GetScalar()); + } + + SetValueIsValid (true); + return true; + } + } + + // We get here if we've failed above... + SetValueIsValid (false); + return false; +} + + + +bool +ValueObjectDynamicValue::IsInScope () +{ + return m_parent->IsInScope(); +} + +bool +ValueObjectDynamicValue::SetValueFromCString (const char *value_str, Error& error) +{ + if (!UpdateValueIfNeeded(false)) + { + error.SetErrorString("unable to read value"); + return false; + } + + uint64_t my_value = GetValueAsUnsigned(UINT64_MAX); + uint64_t parent_value = m_parent->GetValueAsUnsigned(UINT64_MAX); + + if (my_value == UINT64_MAX || parent_value == UINT64_MAX) + { + error.SetErrorString("unable to read value"); + return false; + } + + // if we are at an offset from our parent, in order to set ourselves correctly we would need + // to change the new value so that it refers to the correct dynamic type. we choose not to deal + // with that - if anything more than a value overwrite is required, you should be using the + // expression parser instead of the value editing facility + if (my_value != parent_value) + { + // but NULL'ing out a value should always be allowed + if (strcmp(value_str,"0")) + { + error.SetErrorString("unable to modify dynamic value, use 'expression' command"); + return false; + } + } + + bool ret_val = m_parent->SetValueFromCString(value_str,error); + SetNeedsUpdate(); + return ret_val; +} + +bool +ValueObjectDynamicValue::SetData (DataExtractor &data, Error &error) +{ + if (!UpdateValueIfNeeded(false)) + { + error.SetErrorString("unable to read value"); + return false; + } + + uint64_t my_value = GetValueAsUnsigned(UINT64_MAX); + uint64_t parent_value = m_parent->GetValueAsUnsigned(UINT64_MAX); + + if (my_value == UINT64_MAX || parent_value == UINT64_MAX) + { + error.SetErrorString("unable to read value"); + return false; + } + + // if we are at an offset from our parent, in order to set ourselves correctly we would need + // to change the new value so that it refers to the correct dynamic type. we choose not to deal + // with that - if anything more than a value overwrite is required, you should be using the + // expression parser instead of the value editing facility + if (my_value != parent_value) + { + // but NULL'ing out a value should always be allowed + lldb::offset_t offset = 0; + + if (data.GetPointer(&offset) != 0) + { + error.SetErrorString("unable to modify dynamic value, use 'expression' command"); + return false; + } + } + + bool ret_val = m_parent->SetData(data, error); + SetNeedsUpdate(); + return ret_val; +} diff --git a/contrib/llvm/tools/lldb/source/Core/ValueObjectList.cpp b/contrib/llvm/tools/lldb/source/Core/ValueObjectList.cpp new file mode 100644 index 00000000000..180d4a0eaf1 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Core/ValueObjectList.cpp @@ -0,0 +1,166 @@ +//===-- ValueObjectList.cpp -------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Core/ValueObjectList.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/ValueObjectChild.h" +#include "lldb/Core/ValueObjectRegister.h" +#include "lldb/Core/ValueObjectVariable.h" +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/Process.h" + +using namespace lldb; +using namespace lldb_private; + +ValueObjectList::ValueObjectList () : + m_value_objects() +{ +} + +ValueObjectList::ValueObjectList (const ValueObjectList &rhs) : + m_value_objects(rhs.m_value_objects) +{ +} + + +ValueObjectList::~ValueObjectList () +{ +} + +const ValueObjectList & +ValueObjectList::operator = (const ValueObjectList &rhs) +{ + if (this != &rhs) + m_value_objects = rhs.m_value_objects; + return *this; +} + +void +ValueObjectList::Append (const ValueObjectSP &val_obj_sp) +{ + m_value_objects.push_back(val_obj_sp); +} + +void +ValueObjectList::Append (const ValueObjectList &valobj_list) +{ + std::copy(valobj_list.m_value_objects.begin(), // source begin + valobj_list.m_value_objects.end(), // source end + back_inserter(m_value_objects)); // destination + +} + + +size_t +ValueObjectList::GetSize() const +{ + return m_value_objects.size(); +} + +void +ValueObjectList::Resize (size_t size) +{ + m_value_objects.resize (size); +} + +lldb::ValueObjectSP +ValueObjectList::GetValueObjectAtIndex (size_t idx) +{ + lldb::ValueObjectSP valobj_sp; + if (idx < m_value_objects.size()) + valobj_sp = m_value_objects[idx]; + return valobj_sp; +} + +lldb::ValueObjectSP +ValueObjectList::RemoveValueObjectAtIndex (size_t idx) +{ + lldb::ValueObjectSP valobj_sp; + if (idx < m_value_objects.size()) + { + valobj_sp = m_value_objects[idx]; + m_value_objects.erase (m_value_objects.begin() + idx); + } + return valobj_sp; +} + +void +ValueObjectList::SetValueObjectAtIndex (size_t idx, const ValueObjectSP &valobj_sp) +{ + if (idx >= m_value_objects.size()) + m_value_objects.resize (idx + 1); + m_value_objects[idx] = valobj_sp; +} + +ValueObjectSP +ValueObjectList::FindValueObjectByValueName (const char *name) +{ + ConstString name_const_str(name); + ValueObjectSP val_obj_sp; + collection::iterator pos, end = m_value_objects.end(); + for (pos = m_value_objects.begin(); pos != end; ++pos) + { + ValueObject *valobj = (*pos).get(); + if (valobj && valobj->GetName() == name_const_str) + { + val_obj_sp = *pos; + break; + } + } + return val_obj_sp; +} + +ValueObjectSP +ValueObjectList::FindValueObjectByUID (lldb::user_id_t uid) +{ + ValueObjectSP valobj_sp; + collection::iterator pos, end = m_value_objects.end(); + + for (pos = m_value_objects.begin(); pos != end; ++pos) + { + // Watch out for NULL objects in our list as the list + // might get resized to a specific size and lazily filled in + ValueObject *valobj = (*pos).get(); + if (valobj && valobj->GetID() == uid) + { + valobj_sp = *pos; + break; + } + } + return valobj_sp; +} + + +ValueObjectSP +ValueObjectList::FindValueObjectByPointer (ValueObject *find_valobj) +{ + ValueObjectSP valobj_sp; + collection::iterator pos, end = m_value_objects.end(); + + for (pos = m_value_objects.begin(); pos != end; ++pos) + { + ValueObject *valobj = (*pos).get(); + if (valobj && valobj == find_valobj) + { + valobj_sp = *pos; + break; + } + } + return valobj_sp; +} + +void +ValueObjectList::Swap (ValueObjectList &value_object_list) +{ + m_value_objects.swap (value_object_list.m_value_objects); +} diff --git a/contrib/llvm/tools/lldb/source/Core/ValueObjectMemory.cpp b/contrib/llvm/tools/lldb/source/Core/ValueObjectMemory.cpp new file mode 100644 index 00000000000..42fd0e8fffb --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Core/ValueObjectMemory.cpp @@ -0,0 +1,275 @@ +//===-- ValueObjectMemory.cpp ---------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + + +#include "lldb/Core/ValueObjectMemory.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/Module.h" +#include "lldb/Core/ValueObjectList.h" +#include "lldb/Core/Value.h" +#include "lldb/Core/ValueObject.h" + +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Symbol/SymbolContext.h" +#include "lldb/Symbol/Type.h" +#include "lldb/Symbol/Variable.h" + +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" + +using namespace lldb; +using namespace lldb_private; + +ValueObjectSP +ValueObjectMemory::Create (ExecutionContextScope *exe_scope, + const char *name, + const Address &address, + lldb::TypeSP &type_sp) +{ + return (new ValueObjectMemory (exe_scope, name, address, type_sp))->GetSP(); +} + +ValueObjectSP +ValueObjectMemory::Create (ExecutionContextScope *exe_scope, + const char *name, + const Address &address, + const ClangASTType &ast_type) +{ + return (new ValueObjectMemory (exe_scope, name, address, ast_type))->GetSP(); +} + +ValueObjectMemory::ValueObjectMemory (ExecutionContextScope *exe_scope, + const char *name, + const Address &address, + lldb::TypeSP &type_sp) : + ValueObject(exe_scope), + m_address (address), + m_type_sp(type_sp), + m_clang_type() +{ + // Do not attempt to construct one of these objects with no variable! + assert (m_type_sp.get() != NULL); + SetName (ConstString(name)); + m_value.SetContext(Value::eContextTypeLLDBType, m_type_sp.get()); + TargetSP target_sp (GetTargetSP()); + lldb::addr_t load_address = m_address.GetLoadAddress(target_sp.get()); + if (load_address != LLDB_INVALID_ADDRESS) + { + m_value.SetValueType(Value::eValueTypeLoadAddress); + m_value.GetScalar() = load_address; + } + else + { + lldb::addr_t file_address = m_address.GetFileAddress(); + if (file_address != LLDB_INVALID_ADDRESS) + { + m_value.SetValueType(Value::eValueTypeFileAddress); + m_value.GetScalar() = file_address; + } + else + { + m_value.GetScalar() = m_address.GetOffset(); + m_value.SetValueType (Value::eValueTypeScalar); + } + } +} + +ValueObjectMemory::ValueObjectMemory (ExecutionContextScope *exe_scope, + const char *name, + const Address &address, + const ClangASTType &ast_type) : + ValueObject(exe_scope), + m_address (address), + m_type_sp(), + m_clang_type(ast_type) +{ + // Do not attempt to construct one of these objects with no variable! + assert (m_clang_type.GetASTContext()); + assert (m_clang_type.GetOpaqueQualType()); + + TargetSP target_sp (GetTargetSP()); + + SetName (ConstString(name)); +// m_value.SetContext(Value::eContextTypeClangType, m_clang_type.GetOpaqueQualType()); + m_value.SetClangType(m_clang_type); + lldb::addr_t load_address = m_address.GetLoadAddress (target_sp.get()); + if (load_address != LLDB_INVALID_ADDRESS) + { + m_value.SetValueType(Value::eValueTypeLoadAddress); + m_value.GetScalar() = load_address; + } + else + { + lldb::addr_t file_address = m_address.GetFileAddress(); + if (file_address != LLDB_INVALID_ADDRESS) + { + m_value.SetValueType(Value::eValueTypeFileAddress); + m_value.GetScalar() = file_address; + } + else + { + m_value.GetScalar() = m_address.GetOffset(); + m_value.SetValueType (Value::eValueTypeScalar); + } + } +} + +ValueObjectMemory::~ValueObjectMemory() +{ +} + +ClangASTType +ValueObjectMemory::GetClangTypeImpl () +{ + if (m_type_sp) + return m_type_sp->GetClangForwardType(); + return m_clang_type; +} + +ConstString +ValueObjectMemory::GetTypeName() +{ + if (m_type_sp) + return m_type_sp->GetName(); + return m_clang_type.GetConstTypeName(); +} + +size_t +ValueObjectMemory::CalculateNumChildren() +{ + if (m_type_sp) + return m_type_sp->GetNumChildren(true); + const bool omit_empty_base_classes = true; + return m_clang_type.GetNumChildren (omit_empty_base_classes); +} + +uint64_t +ValueObjectMemory::GetByteSize() +{ + if (m_type_sp) + return m_type_sp->GetByteSize(); + return m_clang_type.GetByteSize (); +} + +lldb::ValueType +ValueObjectMemory::GetValueType() const +{ + // RETHINK: Should this be inherited from somewhere? + return lldb::eValueTypeVariableGlobal; +} + +bool +ValueObjectMemory::UpdateValue () +{ + SetValueIsValid (false); + m_error.Clear(); + + ExecutionContext exe_ctx (GetExecutionContextRef()); + + Target *target = exe_ctx.GetTargetPtr(); + if (target) + { + m_data.SetByteOrder(target->GetArchitecture().GetByteOrder()); + m_data.SetAddressByteSize(target->GetArchitecture().GetAddressByteSize()); + } + + Value old_value(m_value); + if (m_address.IsValid()) + { + Value::ValueType value_type = m_value.GetValueType(); + + switch (value_type) + { + default: + assert(!"Unhandled expression result value kind..."); + break; + + case Value::eValueTypeScalar: + // The variable value is in the Scalar value inside the m_value. + // We can point our m_data right to it. + m_error = m_value.GetValueAsData (&exe_ctx, m_data, 0, GetModule().get()); + break; + + case Value::eValueTypeFileAddress: + case Value::eValueTypeLoadAddress: + case Value::eValueTypeHostAddress: + // The DWARF expression result was an address in the inferior + // process. If this variable is an aggregate type, we just need + // the address as the main value as all child variable objects + // will rely upon this location and add an offset and then read + // their own values as needed. If this variable is a simple + // type, we read all data for it into m_data. + // Make sure this type has a value before we try and read it + + // If we have a file address, convert it to a load address if we can. + if (value_type == Value::eValueTypeFileAddress && exe_ctx.GetProcessPtr()) + { + lldb::addr_t load_addr = m_address.GetLoadAddress(target); + if (load_addr != LLDB_INVALID_ADDRESS) + { + m_value.SetValueType(Value::eValueTypeLoadAddress); + m_value.GetScalar() = load_addr; + } + } + + if (GetClangType().IsAggregateType()) + { + // this value object represents an aggregate type whose + // children have values, but this object does not. So we + // say we are changed if our location has changed. + SetValueDidChange (value_type != old_value.GetValueType() || m_value.GetScalar() != old_value.GetScalar()); + } + else + { + // Copy the Value and set the context to use our Variable + // so it can extract read its value into m_data appropriately + Value value(m_value); + if (m_type_sp) + value.SetContext(Value::eContextTypeLLDBType, m_type_sp.get()); + else + { + //value.SetContext(Value::eContextTypeClangType, m_clang_type.GetOpaqueQualType()); + value.SetClangType(m_clang_type); + } + + m_error = value.GetValueAsData(&exe_ctx, m_data, 0, GetModule().get()); + } + break; + } + + SetValueIsValid (m_error.Success()); + } + return m_error.Success(); +} + + + +bool +ValueObjectMemory::IsInScope () +{ + // FIXME: Maybe try to read the memory address, and if that works, then + // we are in scope? + return true; +} + + +lldb::ModuleSP +ValueObjectMemory::GetModule() +{ + return m_address.GetModule(); +} + + diff --git a/contrib/llvm/tools/lldb/source/Core/ValueObjectRegister.cpp b/contrib/llvm/tools/lldb/source/Core/ValueObjectRegister.cpp new file mode 100644 index 00000000000..4f21457519e --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Core/ValueObjectRegister.cpp @@ -0,0 +1,431 @@ +//===-- ValueObjectRegister.cpp ---------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + + +#include "lldb/Core/ValueObjectRegister.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/Module.h" +#include "lldb/Symbol/ClangASTType.h" +#include "lldb/Symbol/ClangASTContext.h" +#include "lldb/Symbol/TypeList.h" +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" + +using namespace lldb; +using namespace lldb_private; + +#pragma mark ValueObjectRegisterContext + +ValueObjectRegisterContext::ValueObjectRegisterContext (ValueObject &parent, RegisterContextSP ®_ctx) : + ValueObject (parent), + m_reg_ctx_sp (reg_ctx) +{ + assert (reg_ctx); + m_name.SetCString("Registers"); + SetValueIsValid (true); +} + +ValueObjectRegisterContext::~ValueObjectRegisterContext() +{ +} + +ClangASTType +ValueObjectRegisterContext::GetClangTypeImpl () +{ + return ClangASTType(); +} + +ConstString +ValueObjectRegisterContext::GetTypeName() +{ + return ConstString(); +} + +ConstString +ValueObjectRegisterContext::GetQualifiedTypeName() +{ + return ConstString(); +} + +size_t +ValueObjectRegisterContext::CalculateNumChildren() +{ + return m_reg_ctx_sp->GetRegisterSetCount(); +} + +uint64_t +ValueObjectRegisterContext::GetByteSize() +{ + return 0; +} + +bool +ValueObjectRegisterContext::UpdateValue () +{ + m_error.Clear(); + ExecutionContext exe_ctx(GetExecutionContextRef()); + StackFrame *frame = exe_ctx.GetFramePtr(); + if (frame) + m_reg_ctx_sp = frame->GetRegisterContext(); + else + m_reg_ctx_sp.reset(); + + if (m_reg_ctx_sp.get() == NULL) + { + SetValueIsValid (false); + m_error.SetErrorToGenericError(); + } + else + SetValueIsValid (true); + + return m_error.Success(); +} + +ValueObject * +ValueObjectRegisterContext::CreateChildAtIndex (size_t idx, bool synthetic_array_member, int32_t synthetic_index) +{ + ValueObject *new_valobj = NULL; + + const size_t num_children = GetNumChildren(); + if (idx < num_children) + { + ExecutionContext exe_ctx(GetExecutionContextRef()); + new_valobj = new ValueObjectRegisterSet(exe_ctx.GetBestExecutionContextScope(), m_reg_ctx_sp, idx); + } + + return new_valobj; +} + + +#pragma mark - +#pragma mark ValueObjectRegisterSet + +ValueObjectSP +ValueObjectRegisterSet::Create (ExecutionContextScope *exe_scope, lldb::RegisterContextSP ®_ctx_sp, uint32_t set_idx) +{ + return (new ValueObjectRegisterSet (exe_scope, reg_ctx_sp, set_idx))->GetSP(); +} + + +ValueObjectRegisterSet::ValueObjectRegisterSet (ExecutionContextScope *exe_scope, lldb::RegisterContextSP ®_ctx, uint32_t reg_set_idx) : + ValueObject (exe_scope), + m_reg_ctx_sp (reg_ctx), + m_reg_set (NULL), + m_reg_set_idx (reg_set_idx) +{ + assert (reg_ctx); + m_reg_set = reg_ctx->GetRegisterSet(m_reg_set_idx); + if (m_reg_set) + { + m_name.SetCString (m_reg_set->name); + } +} + +ValueObjectRegisterSet::~ValueObjectRegisterSet() +{ +} + +ClangASTType +ValueObjectRegisterSet::GetClangTypeImpl () +{ + return ClangASTType(); +} + +ConstString +ValueObjectRegisterSet::GetTypeName() +{ + return ConstString(); +} + +ConstString +ValueObjectRegisterSet::GetQualifiedTypeName() +{ + return ConstString(); +} + +size_t +ValueObjectRegisterSet::CalculateNumChildren() +{ + const RegisterSet *reg_set = m_reg_ctx_sp->GetRegisterSet(m_reg_set_idx); + if (reg_set) + return reg_set->num_registers; + return 0; +} + +uint64_t +ValueObjectRegisterSet::GetByteSize() +{ + return 0; +} + +bool +ValueObjectRegisterSet::UpdateValue () +{ + m_error.Clear(); + SetValueDidChange (false); + ExecutionContext exe_ctx(GetExecutionContextRef()); + StackFrame *frame = exe_ctx.GetFramePtr(); + if (frame == NULL) + m_reg_ctx_sp.reset(); + else + { + m_reg_ctx_sp = frame->GetRegisterContext (); + if (m_reg_ctx_sp) + { + const RegisterSet *reg_set = m_reg_ctx_sp->GetRegisterSet (m_reg_set_idx); + if (reg_set == NULL) + m_reg_ctx_sp.reset(); + else if (m_reg_set != reg_set) + { + SetValueDidChange (true); + m_name.SetCString(reg_set->name); + } + } + } + if (m_reg_ctx_sp) + { + SetValueIsValid (true); + } + else + { + SetValueIsValid (false); + m_error.SetErrorToGenericError (); + m_children.Clear(); + } + return m_error.Success(); +} + + +ValueObject * +ValueObjectRegisterSet::CreateChildAtIndex (size_t idx, bool synthetic_array_member, int32_t synthetic_index) +{ + ValueObject *valobj = NULL; + if (m_reg_ctx_sp && m_reg_set) + { + const size_t num_children = GetNumChildren(); + if (idx < num_children) + valobj = new ValueObjectRegister(*this, m_reg_ctx_sp, m_reg_set->registers[idx]); + } + return valobj; +} + +lldb::ValueObjectSP +ValueObjectRegisterSet::GetChildMemberWithName (const ConstString &name, bool can_create) +{ + ValueObject *valobj = NULL; + if (m_reg_ctx_sp && m_reg_set) + { + const RegisterInfo *reg_info = m_reg_ctx_sp->GetRegisterInfoByName (name.AsCString()); + if (reg_info != NULL) + valobj = new ValueObjectRegister(*this, m_reg_ctx_sp, reg_info->kinds[eRegisterKindLLDB]); + } + if (valobj) + return valobj->GetSP(); + else + return ValueObjectSP(); +} + +size_t +ValueObjectRegisterSet::GetIndexOfChildWithName (const ConstString &name) +{ + if (m_reg_ctx_sp && m_reg_set) + { + const RegisterInfo *reg_info = m_reg_ctx_sp->GetRegisterInfoByName (name.AsCString()); + if (reg_info != NULL) + return reg_info->kinds[eRegisterKindLLDB]; + } + return UINT32_MAX; +} + +#pragma mark - +#pragma mark ValueObjectRegister + +void +ValueObjectRegister::ConstructObject (uint32_t reg_num) +{ + const RegisterInfo *reg_info = m_reg_ctx_sp->GetRegisterInfoAtIndex (reg_num); + if (reg_info) + { + m_reg_info = *reg_info; + if (reg_info->name) + m_name.SetCString(reg_info->name); + else if (reg_info->alt_name) + m_name.SetCString(reg_info->alt_name); + } +} + +ValueObjectRegister::ValueObjectRegister (ValueObject &parent, lldb::RegisterContextSP ®_ctx_sp, uint32_t reg_num) : + ValueObject (parent), + m_reg_ctx_sp (reg_ctx_sp), + m_reg_info (), + m_reg_value (), + m_type_name (), + m_clang_type () +{ + assert (reg_ctx_sp.get()); + ConstructObject(reg_num); +} + +ValueObjectSP +ValueObjectRegister::Create (ExecutionContextScope *exe_scope, lldb::RegisterContextSP ®_ctx_sp, uint32_t reg_num) +{ + return (new ValueObjectRegister (exe_scope, reg_ctx_sp, reg_num))->GetSP(); +} + +ValueObjectRegister::ValueObjectRegister (ExecutionContextScope *exe_scope, lldb::RegisterContextSP ®_ctx, uint32_t reg_num) : + ValueObject (exe_scope), + m_reg_ctx_sp (reg_ctx), + m_reg_info (), + m_reg_value (), + m_type_name (), + m_clang_type () +{ + assert (reg_ctx); + ConstructObject(reg_num); +} + +ValueObjectRegister::~ValueObjectRegister() +{ +} + +ClangASTType +ValueObjectRegister::GetClangTypeImpl () +{ + if (!m_clang_type.IsValid()) + { + ExecutionContext exe_ctx (GetExecutionContextRef()); + Target *target = exe_ctx.GetTargetPtr(); + if (target) + { + Module *exe_module = target->GetExecutableModulePointer(); + if (exe_module) + { + m_clang_type = exe_module->GetClangASTContext().GetBuiltinTypeForEncodingAndBitSize (m_reg_info.encoding, + m_reg_info.byte_size * 8); + } + } + } + return m_clang_type; +} + +ConstString +ValueObjectRegister::GetTypeName() +{ + if (m_type_name.IsEmpty()) + m_type_name = GetClangType().GetConstTypeName (); + return m_type_name; +} + +size_t +ValueObjectRegister::CalculateNumChildren() +{ + return GetClangType().GetNumChildren(true); +} + +uint64_t +ValueObjectRegister::GetByteSize() +{ + return m_reg_info.byte_size; +} + +bool +ValueObjectRegister::UpdateValue () +{ + m_error.Clear(); + ExecutionContext exe_ctx(GetExecutionContextRef()); + StackFrame *frame = exe_ctx.GetFramePtr(); + if (frame == NULL) + { + m_reg_ctx_sp.reset(); + m_reg_value.Clear(); + } + + + if (m_reg_ctx_sp) + { + if (m_reg_ctx_sp->ReadRegister (&m_reg_info, m_reg_value)) + { + if (m_reg_value.GetData (m_data)) + { + Process *process = exe_ctx.GetProcessPtr(); + if (process) + m_data.SetAddressByteSize(process->GetAddressByteSize()); + m_value.SetContext(Value::eContextTypeRegisterInfo, (void *)&m_reg_info); + m_value.SetValueType(Value::eValueTypeHostAddress); + m_value.GetScalar() = (uintptr_t)m_data.GetDataStart(); + SetValueIsValid (true); + return true; + } + } + } + + SetValueIsValid (false); + m_error.SetErrorToGenericError (); + return false; +} + +bool +ValueObjectRegister::SetValueFromCString (const char *value_str, Error& error) +{ + // The new value will be in the m_data. Copy that into our register value. + error = m_reg_value.SetValueFromCString (&m_reg_info, value_str); + if (error.Success()) + { + if (m_reg_ctx_sp->WriteRegister (&m_reg_info, m_reg_value)) + { + SetNeedsUpdate(); + return true; + } + else + return false; + } + else + return false; +} + +bool +ValueObjectRegister::SetData (DataExtractor &data, Error &error) +{ + error = m_reg_value.SetValueFromData(&m_reg_info, data, 0, false); + if (error.Success()) + { + if (m_reg_ctx_sp->WriteRegister (&m_reg_info, m_reg_value)) + { + SetNeedsUpdate(); + return true; + } + else + return false; + } + else + return false; +} + +bool +ValueObjectRegister::ResolveValue (Scalar &scalar) +{ + if (UpdateValueIfNeeded(false)) // make sure that you are up to date before returning anything + return m_reg_value.GetScalarValue(scalar); + return false; +} + +void +ValueObjectRegister::GetExpressionPath (Stream &s, bool qualify_cxx_base_classes, GetExpressionPathFormat epformat) +{ + s.Printf("$%s", m_reg_info.name); +} + + diff --git a/contrib/llvm/tools/lldb/source/Core/ValueObjectSyntheticFilter.cpp b/contrib/llvm/tools/lldb/source/Core/ValueObjectSyntheticFilter.cpp new file mode 100644 index 00000000000..522ca082a69 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Core/ValueObjectSyntheticFilter.cpp @@ -0,0 +1,270 @@ +//===-- ValueObjectSyntheticFilter.cpp -----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "lldb/Core/ValueObjectSyntheticFilter.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/ValueObject.h" +#include "lldb/DataFormatters/FormatClasses.h" + +using namespace lldb_private; + +class DummySyntheticFrontEnd : public SyntheticChildrenFrontEnd +{ +public: + DummySyntheticFrontEnd(ValueObject &backend) : + SyntheticChildrenFrontEnd(backend) + {} + + size_t + CalculateNumChildren() + { + return 0; + } + + lldb::ValueObjectSP + GetChildAtIndex (size_t idx) + { + return lldb::ValueObjectSP(); + } + + size_t + GetIndexOfChildWithName (const ConstString &name) + { + return UINT32_MAX; + } + + bool + MightHaveChildren () + { + return true; + } + + bool + Update() + { + return false; + } + +}; + +ValueObjectSynthetic::ValueObjectSynthetic (ValueObject &parent, lldb::SyntheticChildrenSP filter) : + ValueObject(parent), + m_synth_sp(filter), + m_children_byindex(), + m_name_toindex(), + m_synthetic_children_count(UINT32_MAX), + m_parent_type_name(parent.GetTypeName()), + m_might_have_children(eLazyBoolCalculate) +{ +#ifdef LLDB_CONFIGURATION_DEBUG + std::string new_name(parent.GetName().AsCString()); + new_name += "$$__synth__"; + SetName (ConstString(new_name.c_str())); +#else + SetName(parent.GetName()); +#endif + CopyParentData(); + CreateSynthFilter(); +} + +ValueObjectSynthetic::~ValueObjectSynthetic() +{ +} + +ClangASTType +ValueObjectSynthetic::GetClangTypeImpl () +{ + return m_parent->GetClangType(); +} + +ConstString +ValueObjectSynthetic::GetTypeName() +{ + return m_parent->GetTypeName(); +} + +ConstString +ValueObjectSynthetic::GetQualifiedTypeName() +{ + return m_parent->GetQualifiedTypeName(); +} + +size_t +ValueObjectSynthetic::CalculateNumChildren() +{ + UpdateValueIfNeeded(); + if (m_synthetic_children_count < UINT32_MAX) + return m_synthetic_children_count; + return (m_synthetic_children_count = m_synth_filter_ap->CalculateNumChildren()); +} + +lldb::ValueObjectSP +ValueObjectSynthetic::GetDynamicValue (lldb::DynamicValueType valueType) +{ + if (!m_parent) + return lldb::ValueObjectSP(); + if (IsDynamic() && GetDynamicValueType() == valueType) + return GetSP(); + return m_parent->GetDynamicValue(valueType); +} + +bool +ValueObjectSynthetic::MightHaveChildren() +{ + if (m_might_have_children == eLazyBoolCalculate) + m_might_have_children = (m_synth_filter_ap->MightHaveChildren() ? eLazyBoolYes : eLazyBoolNo); + return (m_might_have_children == eLazyBoolNo ? false : true); +} + +uint64_t +ValueObjectSynthetic::GetByteSize() +{ + return m_parent->GetByteSize(); +} + +lldb::ValueType +ValueObjectSynthetic::GetValueType() const +{ + return m_parent->GetValueType(); +} + +void +ValueObjectSynthetic::CreateSynthFilter () +{ + m_synth_filter_ap = (m_synth_sp->GetFrontEnd(*m_parent)); + if (!m_synth_filter_ap.get()) + m_synth_filter_ap.reset(new DummySyntheticFrontEnd(*m_parent)); +} + +bool +ValueObjectSynthetic::UpdateValue () +{ + SetValueIsValid (false); + m_error.Clear(); + + if (!m_parent->UpdateValueIfNeeded(false)) + { + // our parent could not update.. as we are meaningless without a parent, just stop + if (m_parent->GetError().Fail()) + m_error = m_parent->GetError(); + return false; + } + + // regenerate the synthetic filter if our typename changes + // + ConstString new_parent_type_name = m_parent->GetTypeName(); + if (new_parent_type_name != m_parent_type_name) + { + m_parent_type_name = new_parent_type_name; + CreateSynthFilter(); + } + + // let our backend do its update + if (m_synth_filter_ap->Update() == false) + { + // filter said that cached values are stale + m_children_byindex.clear(); + m_name_toindex.clear(); + // usually, an object's value can change but this does not alter its children count + // for a synthetic VO that might indeed happen, so we need to tell the upper echelons + // that they need to come back to us asking for children + m_children_count_valid = false; + m_synthetic_children_count = UINT32_MAX; + m_might_have_children = eLazyBoolCalculate; + } + + CopyParentData(); + + SetValueIsValid(true); + return true; +} + +lldb::ValueObjectSP +ValueObjectSynthetic::GetChildAtIndex (size_t idx, bool can_create) +{ + UpdateValueIfNeeded(); + + ByIndexIterator iter = m_children_byindex.find(idx); + + if (iter == m_children_byindex.end()) + { + if (can_create && m_synth_filter_ap.get() != NULL) + { + lldb::ValueObjectSP synth_guy = m_synth_filter_ap->GetChildAtIndex (idx); + if (!synth_guy) + return synth_guy; + m_children_byindex[idx]= synth_guy.get(); + return synth_guy; + } + else + return lldb::ValueObjectSP(); + } + else + return iter->second->GetSP(); +} + +lldb::ValueObjectSP +ValueObjectSynthetic::GetChildMemberWithName (const ConstString &name, bool can_create) +{ + UpdateValueIfNeeded(); + + uint32_t index = GetIndexOfChildWithName(name); + + if (index == UINT32_MAX) + return lldb::ValueObjectSP(); + + return GetChildAtIndex(index, can_create); +} + +size_t +ValueObjectSynthetic::GetIndexOfChildWithName (const ConstString &name) +{ + UpdateValueIfNeeded(); + + NameToIndexIterator iter = m_name_toindex.find(name.GetCString()); + + if (iter == m_name_toindex.end() && m_synth_filter_ap.get() != NULL) + { + uint32_t index = m_synth_filter_ap->GetIndexOfChildWithName (name); + if (index == UINT32_MAX) + return index; + m_name_toindex[name.GetCString()] = index; + return index; + } + else if (iter == m_name_toindex.end() && m_synth_filter_ap.get() == NULL) + return UINT32_MAX; + else /*if (iter != m_name_toindex.end())*/ + return iter->second; +} + +bool +ValueObjectSynthetic::IsInScope () +{ + return m_parent->IsInScope(); +} + +lldb::ValueObjectSP +ValueObjectSynthetic::GetNonSyntheticValue () +{ + return m_parent->GetSP(); +} + +void +ValueObjectSynthetic::CopyParentData () +{ + m_value = m_parent->GetValue(); + ExecutionContext exe_ctx (GetExecutionContextRef()); + m_error = m_value.GetValueAsData (&exe_ctx, m_data, 0, GetModule().get()); +} diff --git a/contrib/llvm/tools/lldb/source/Core/ValueObjectVariable.cpp b/contrib/llvm/tools/lldb/source/Core/ValueObjectVariable.cpp new file mode 100644 index 00000000000..38c0d91324a --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Core/ValueObjectVariable.cpp @@ -0,0 +1,386 @@ +//===-- ValueObjectVariable.cpp ---------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + + +#include "lldb/Core/ValueObjectVariable.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/Module.h" +#include "lldb/Core/RegisterValue.h" +#include "lldb/Core/ValueObjectList.h" +#include "lldb/Core/Value.h" + +#include "lldb/Symbol/Function.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Symbol/SymbolContext.h" +#include "lldb/Symbol/SymbolContextScope.h" +#include "lldb/Symbol/Type.h" +#include "lldb/Symbol/Variable.h" + +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" + + +using namespace lldb_private; + +lldb::ValueObjectSP +ValueObjectVariable::Create (ExecutionContextScope *exe_scope, const lldb::VariableSP &var_sp) +{ + return (new ValueObjectVariable (exe_scope, var_sp))->GetSP(); +} + +ValueObjectVariable::ValueObjectVariable (ExecutionContextScope *exe_scope, const lldb::VariableSP &var_sp) : + ValueObject(exe_scope), + m_variable_sp(var_sp) +{ + // Do not attempt to construct one of these objects with no variable! + assert (m_variable_sp.get() != NULL); + m_name = var_sp->GetName(); +} + +ValueObjectVariable::~ValueObjectVariable() +{ +} + +ClangASTType +ValueObjectVariable::GetClangTypeImpl () +{ + Type *var_type = m_variable_sp->GetType(); + if (var_type) + return var_type->GetClangForwardType(); + return ClangASTType(); +} + +ConstString +ValueObjectVariable::GetTypeName() +{ + Type * var_type = m_variable_sp->GetType(); + if (var_type) + return var_type->GetName(); + return ConstString(); +} + +ConstString +ValueObjectVariable::GetQualifiedTypeName() +{ + Type * var_type = m_variable_sp->GetType(); + if (var_type) + return var_type->GetQualifiedName(); + return ConstString(); +} + +size_t +ValueObjectVariable::CalculateNumChildren() +{ + ClangASTType type(GetClangType()); + + if (!type.IsValid()) + return 0; + + const bool omit_empty_base_classes = true; + return type.GetNumChildren(omit_empty_base_classes); +} + +uint64_t +ValueObjectVariable::GetByteSize() +{ + ClangASTType type(GetClangType()); + + if (!type.IsValid()) + return 0; + + return type.GetByteSize(); +} + +lldb::ValueType +ValueObjectVariable::GetValueType() const +{ + if (m_variable_sp) + return m_variable_sp->GetScope(); + return lldb::eValueTypeInvalid; +} + +bool +ValueObjectVariable::UpdateValue () +{ + SetValueIsValid (false); + m_error.Clear(); + + Variable *variable = m_variable_sp.get(); + DWARFExpression &expr = variable->LocationExpression(); + + if (variable->GetLocationIsConstantValueData()) + { + // expr doesn't contain DWARF bytes, it contains the constant variable + // value bytes themselves... + if (expr.GetExpressionData(m_data)) + m_value.SetContext(Value::eContextTypeVariable, variable); + else + m_error.SetErrorString ("empty constant data"); + // constant bytes can't be edited - sorry + m_resolved_value.SetContext(Value::eContextTypeInvalid, NULL); + } + else + { + lldb::addr_t loclist_base_load_addr = LLDB_INVALID_ADDRESS; + ExecutionContext exe_ctx (GetExecutionContextRef()); + + Target *target = exe_ctx.GetTargetPtr(); + if (target) + { + m_data.SetByteOrder(target->GetArchitecture().GetByteOrder()); + m_data.SetAddressByteSize(target->GetArchitecture().GetAddressByteSize()); + } + + if (expr.IsLocationList()) + { + SymbolContext sc; + variable->CalculateSymbolContext (&sc); + if (sc.function) + loclist_base_load_addr = sc.function->GetAddressRange().GetBaseAddress().GetLoadAddress (target); + } + Value old_value(m_value); + if (expr.Evaluate (&exe_ctx, NULL, NULL, NULL, loclist_base_load_addr, NULL, m_value, &m_error)) + { + m_resolved_value = m_value; + m_value.SetContext(Value::eContextTypeVariable, variable); + + Value::ValueType value_type = m_value.GetValueType(); + + switch (value_type) + { + case Value::eValueTypeFileAddress: + SetAddressTypeOfChildren(eAddressTypeFile); + break; + case Value::eValueTypeHostAddress: + SetAddressTypeOfChildren(eAddressTypeHost); + break; + case Value::eValueTypeLoadAddress: + case Value::eValueTypeScalar: + case Value::eValueTypeVector: + SetAddressTypeOfChildren(eAddressTypeLoad); + break; + } + + switch (value_type) + { + case Value::eValueTypeVector: + // fall through + case Value::eValueTypeScalar: + // The variable value is in the Scalar value inside the m_value. + // We can point our m_data right to it. + m_error = m_value.GetValueAsData (&exe_ctx, m_data, 0, GetModule().get()); + break; + + case Value::eValueTypeFileAddress: + case Value::eValueTypeLoadAddress: + case Value::eValueTypeHostAddress: + // The DWARF expression result was an address in the inferior + // process. If this variable is an aggregate type, we just need + // the address as the main value as all child variable objects + // will rely upon this location and add an offset and then read + // their own values as needed. If this variable is a simple + // type, we read all data for it into m_data. + // Make sure this type has a value before we try and read it + + // If we have a file address, convert it to a load address if we can. + Process *process = exe_ctx.GetProcessPtr(); + if (value_type == Value::eValueTypeFileAddress && process && process->IsAlive()) + { + lldb::addr_t file_addr = m_value.GetScalar().ULongLong(LLDB_INVALID_ADDRESS); + if (file_addr != LLDB_INVALID_ADDRESS) + { + SymbolContext var_sc; + variable->CalculateSymbolContext(&var_sc); + if (var_sc.module_sp) + { + ObjectFile *objfile = var_sc.module_sp->GetObjectFile(); + if (objfile) + { + Address so_addr(file_addr, objfile->GetSectionList()); + lldb::addr_t load_addr = so_addr.GetLoadAddress (target); + if (load_addr != LLDB_INVALID_ADDRESS) + { + m_value.SetValueType(Value::eValueTypeLoadAddress); + m_value.GetScalar() = load_addr; + } + } + } + } + } + + if (GetClangType().IsAggregateType()) + { + // this value object represents an aggregate type whose + // children have values, but this object does not. So we + // say we are changed if our location has changed. + SetValueDidChange (value_type != old_value.GetValueType() || m_value.GetScalar() != old_value.GetScalar()); + } + else + { + // Copy the Value and set the context to use our Variable + // so it can extract read its value into m_data appropriately + Value value(m_value); + value.SetContext(Value::eContextTypeVariable, variable); + m_error = value.GetValueAsData(&exe_ctx, m_data, 0, GetModule().get()); + } + break; + } + + SetValueIsValid (m_error.Success()); + } + else + { + // could not find location, won't allow editing + m_resolved_value.SetContext(Value::eContextTypeInvalid, NULL); + } + } + return m_error.Success(); +} + + + +bool +ValueObjectVariable::IsInScope () +{ + const ExecutionContextRef &exe_ctx_ref = GetExecutionContextRef(); + if (exe_ctx_ref.HasFrameRef()) + { + ExecutionContext exe_ctx (exe_ctx_ref); + StackFrame *frame = exe_ctx.GetFramePtr(); + if (frame) + { + return m_variable_sp->IsInScope (frame); + } + else + { + // This ValueObject had a frame at one time, but now we + // can't locate it, so return false since we probably aren't + // in scope. + return false; + } + } + // We have a variable that wasn't tied to a frame, which + // means it is a global and is always in scope. + return true; + +} + +lldb::ModuleSP +ValueObjectVariable::GetModule() +{ + if (m_variable_sp) + { + SymbolContextScope *sc_scope = m_variable_sp->GetSymbolContextScope(); + if (sc_scope) + { + return sc_scope->CalculateSymbolContextModule(); + } + } + return lldb::ModuleSP(); +} + +SymbolContextScope * +ValueObjectVariable::GetSymbolContextScope() +{ + if (m_variable_sp) + return m_variable_sp->GetSymbolContextScope(); + return NULL; +} + +bool +ValueObjectVariable::GetDeclaration (Declaration &decl) +{ + if (m_variable_sp) + { + decl = m_variable_sp->GetDeclaration(); + return true; + } + return false; +} + +const char * +ValueObjectVariable::GetLocationAsCString () +{ + if (m_resolved_value.GetContextType() == Value::eContextTypeRegisterInfo) + return GetLocationAsCStringImpl(m_resolved_value, + m_data); + else + return ValueObject::GetLocationAsCString(); +} + +bool +ValueObjectVariable::SetValueFromCString (const char *value_str, Error& error) +{ + if (m_resolved_value.GetContextType() == Value::eContextTypeRegisterInfo) + { + RegisterInfo *reg_info = m_resolved_value.GetRegisterInfo(); + ExecutionContext exe_ctx(GetExecutionContextRef()); + RegisterContext *reg_ctx = exe_ctx.GetRegisterContext(); + RegisterValue reg_value; + if (!reg_info || !reg_ctx) + { + error.SetErrorString("unable to retrieve register info"); + return false; + } + error = reg_value.SetValueFromCString(reg_info, value_str); + if (error.Fail()) + return false; + if (reg_ctx->WriteRegister (reg_info, reg_value)) + { + SetNeedsUpdate(); + return true; + } + else + { + error.SetErrorString("unable to write back to register"); + return false; + } + } + else + return ValueObject::SetValueFromCString(value_str, error); +} + +bool +ValueObjectVariable::SetData (DataExtractor &data, Error &error) +{ + if (m_resolved_value.GetContextType() == Value::eContextTypeRegisterInfo) + { + RegisterInfo *reg_info = m_resolved_value.GetRegisterInfo(); + ExecutionContext exe_ctx(GetExecutionContextRef()); + RegisterContext *reg_ctx = exe_ctx.GetRegisterContext(); + RegisterValue reg_value; + if (!reg_info || !reg_ctx) + { + error.SetErrorString("unable to retrieve register info"); + return false; + } + error = reg_value.SetValueFromData(reg_info, data, 0, false); + if (error.Fail()) + return false; + if (reg_ctx->WriteRegister (reg_info, reg_value)) + { + SetNeedsUpdate(); + return true; + } + else + { + error.SetErrorString("unable to write back to register"); + return false; + } + } + else + return ValueObject::SetData(data, error); +} diff --git a/contrib/llvm/tools/lldb/source/DataFormatters/CF.cpp b/contrib/llvm/tools/lldb/source/DataFormatters/CF.cpp new file mode 100644 index 00000000000..a4b7a1235ff --- /dev/null +++ b/contrib/llvm/tools/lldb/source/DataFormatters/CF.cpp @@ -0,0 +1,299 @@ +//===-- CF.cpp ----------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "lldb/DataFormatters/CXXFormatterFunctions.h" + +#include "lldb/Core/DataBufferHeap.h" +#include "lldb/Core/Error.h" +#include "lldb/Core/Stream.h" +#include "lldb/Core/ValueObject.h" +#include "lldb/Core/ValueObjectConstResult.h" +#include "lldb/Host/Endian.h" +#include "lldb/Symbol/ClangASTContext.h" +#include "lldb/Target/ObjCLanguageRuntime.h" +#include "lldb/Target/Target.h" + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::formatters; + +bool +lldb_private::formatters::CFAbsoluteTimeSummaryProvider (ValueObject& valobj, Stream& stream) +{ + time_t epoch = GetOSXEpoch(); + epoch = epoch + (time_t)valobj.GetValueAsUnsigned(0); + tm *tm_date = localtime(&epoch); + if (!tm_date) + return false; + std::string buffer(1024,0); + if (strftime (&buffer[0], 1023, "%Z", tm_date) == 0) + return false; + stream.Printf("%04d-%02d-%02d %02d:%02d:%02d %s", tm_date->tm_year+1900, tm_date->tm_mon+1, tm_date->tm_mday, tm_date->tm_hour, tm_date->tm_min, tm_date->tm_sec, buffer.c_str()); + return true; +} + +bool +lldb_private::formatters::CFBagSummaryProvider (ValueObject& valobj, Stream& stream) +{ + ProcessSP process_sp = valobj.GetProcessSP(); + if (!process_sp) + return false; + + ObjCLanguageRuntime* runtime = (ObjCLanguageRuntime*)process_sp->GetLanguageRuntime(lldb::eLanguageTypeObjC); + + if (!runtime) + return false; + + ObjCLanguageRuntime::ClassDescriptorSP descriptor(runtime->GetClassDescriptor(valobj)); + + if (!descriptor.get() || !descriptor->IsValid()) + return false; + + uint32_t ptr_size = process_sp->GetAddressByteSize(); + + lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0); + + if (!valobj_addr) + return false; + + uint32_t count = 0; + + bool is_type_ok = false; // check to see if this is a CFBag we know about + if (descriptor->IsCFType()) + { + ConstString type_name(valobj.GetTypeName()); + if (type_name == ConstString("__CFBag") || type_name == ConstString("const struct __CFBag")) + { + if (valobj.IsPointerType()) + is_type_ok = true; + } + } + + if (is_type_ok == false) + { + StackFrameSP frame_sp(valobj.GetFrameSP()); + if (!frame_sp) + return false; + ValueObjectSP count_sp; + StreamString expr; + expr.Printf("(int)CFBagGetCount((void*)0x%" PRIx64 ")",valobj.GetPointerValue()); + if (process_sp->GetTarget().EvaluateExpression(expr.GetData(), frame_sp.get(), count_sp) != eExecutionCompleted) + return false; + if (!count_sp) + return false; + count = count_sp->GetValueAsUnsigned(0); + } + else + { + uint32_t offset = 2*ptr_size+4 + valobj_addr; + Error error; + count = process_sp->ReadUnsignedIntegerFromMemory(offset, 4, 0, error); + if (error.Fail()) + return false; + } + stream.Printf("@\"%u value%s\"", + count,(count == 1 ? "" : "s")); + return true; +} + +bool +lldb_private::formatters::CFBitVectorSummaryProvider (ValueObject& valobj, Stream& stream) +{ + ProcessSP process_sp = valobj.GetProcessSP(); + if (!process_sp) + return false; + + ObjCLanguageRuntime* runtime = (ObjCLanguageRuntime*)process_sp->GetLanguageRuntime(lldb::eLanguageTypeObjC); + + if (!runtime) + return false; + + ObjCLanguageRuntime::ClassDescriptorSP descriptor(runtime->GetClassDescriptor(valobj)); + + if (!descriptor.get() || !descriptor->IsValid()) + return false; + + uint32_t ptr_size = process_sp->GetAddressByteSize(); + + lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0); + + if (!valobj_addr) + return false; + + uint32_t count = 0; + + bool is_type_ok = false; // check to see if this is a CFBag we know about + if (descriptor->IsCFType()) + { + ConstString type_name(valobj.GetTypeName()); + if (type_name == ConstString("__CFMutableBitVector") || type_name == ConstString("__CFBitVector") || type_name == ConstString("CFMutableBitVectorRef") || type_name == ConstString("CFBitVectorRef")) + { + if (valobj.IsPointerType()) + is_type_ok = true; + } + } + + if (is_type_ok == false) + return false; + + Error error; + count = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr+2*ptr_size, ptr_size, 0, error); + if (error.Fail()) + return false; + uint64_t num_bytes = count / 8 + ((count & 7) ? 1 : 0); + addr_t data_ptr = process_sp->ReadPointerFromMemory(valobj_addr+2*ptr_size+2*ptr_size, error); + if (error.Fail()) + return false; + // make sure we do not try to read huge amounts of data + if (num_bytes > 1024) + num_bytes = 1024; + DataBufferSP buffer_sp(new DataBufferHeap(num_bytes,0)); + num_bytes = process_sp->ReadMemory(data_ptr, buffer_sp->GetBytes(), num_bytes, error); + if (error.Fail() || num_bytes == 0) + return false; + uint8_t *bytes = buffer_sp->GetBytes(); + for (int byte_idx = 0; byte_idx < num_bytes-1; byte_idx++) + { + uint8_t byte = bytes[byte_idx]; + bool bit0 = (byte & 1) == 1; + bool bit1 = (byte & 2) == 2; + bool bit2 = (byte & 4) == 4; + bool bit3 = (byte & 8) == 8; + bool bit4 = (byte & 16) == 16; + bool bit5 = (byte & 32) == 32; + bool bit6 = (byte & 64) == 64; + bool bit7 = (byte & 128) == 128; + stream.Printf("%c%c%c%c %c%c%c%c ", + (bit7 ? '1' : '0'), + (bit6 ? '1' : '0'), + (bit5 ? '1' : '0'), + (bit4 ? '1' : '0'), + (bit3 ? '1' : '0'), + (bit2 ? '1' : '0'), + (bit1 ? '1' : '0'), + (bit0 ? '1' : '0')); + count -= 8; + } + { + // print the last byte ensuring we do not print spurious bits + uint8_t byte = bytes[num_bytes-1]; + bool bit0 = (byte & 1) == 1; + bool bit1 = (byte & 2) == 2; + bool bit2 = (byte & 4) == 4; + bool bit3 = (byte & 8) == 8; + bool bit4 = (byte & 16) == 16; + bool bit5 = (byte & 32) == 32; + bool bit6 = (byte & 64) == 64; + bool bit7 = (byte & 128) == 128; + if (count) + { + stream.Printf("%c",bit7 ? '1' : '0'); + count -= 1; + } + if (count) + { + stream.Printf("%c",bit6 ? '1' : '0'); + count -= 1; + } + if (count) + { + stream.Printf("%c",bit5 ? '1' : '0'); + count -= 1; + } + if (count) + { + stream.Printf("%c",bit4 ? '1' : '0'); + count -= 1; + } + if (count) + { + stream.Printf("%c",bit3 ? '1' : '0'); + count -= 1; + } + if (count) + { + stream.Printf("%c",bit2 ? '1' : '0'); + count -= 1; + } + if (count) + { + stream.Printf("%c",bit1 ? '1' : '0'); + count -= 1; + } + if (count) + stream.Printf("%c",bit0 ? '1' : '0'); + } + return true; +} + +bool +lldb_private::formatters::CFBinaryHeapSummaryProvider (ValueObject& valobj, Stream& stream) +{ + ProcessSP process_sp = valobj.GetProcessSP(); + if (!process_sp) + return false; + + ObjCLanguageRuntime* runtime = (ObjCLanguageRuntime*)process_sp->GetLanguageRuntime(lldb::eLanguageTypeObjC); + + if (!runtime) + return false; + + ObjCLanguageRuntime::ClassDescriptorSP descriptor(runtime->GetClassDescriptor(valobj)); + + if (!descriptor.get() || !descriptor->IsValid()) + return false; + + uint32_t ptr_size = process_sp->GetAddressByteSize(); + + lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0); + + if (!valobj_addr) + return false; + + uint32_t count = 0; + + bool is_type_ok = false; // check to see if this is a CFBinaryHeap we know about + if (descriptor->IsCFType()) + { + ConstString type_name(valobj.GetTypeName()); + if (type_name == ConstString("__CFBinaryHeap") || type_name == ConstString("const struct __CFBinaryHeap")) + { + if (valobj.IsPointerType()) + is_type_ok = true; + } + } + + if (is_type_ok == false) + { + StackFrameSP frame_sp(valobj.GetFrameSP()); + if (!frame_sp) + return false; + ValueObjectSP count_sp; + StreamString expr; + expr.Printf("(int)CFBinaryHeapGetCount((void*)0x%" PRIx64 ")",valobj.GetPointerValue()); + if (process_sp->GetTarget().EvaluateExpression(expr.GetData(), frame_sp.get(), count_sp) != eExecutionCompleted) + return false; + if (!count_sp) + return false; + count = count_sp->GetValueAsUnsigned(0); + } + else + { + uint32_t offset = 2*ptr_size; + Error error; + count = process_sp->ReadUnsignedIntegerFromMemory(offset, 4, 0, error); + if (error.Fail()) + return false; + } + stream.Printf("@\"%u item%s\"", + count,(count == 1 ? "" : "s")); + return true; +} diff --git a/contrib/llvm/tools/lldb/source/DataFormatters/CXXFormatterFunctions.cpp b/contrib/llvm/tools/lldb/source/DataFormatters/CXXFormatterFunctions.cpp new file mode 100644 index 00000000000..fba92170d83 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/DataFormatters/CXXFormatterFunctions.cpp @@ -0,0 +1,1351 @@ +//===-- CXXFormatterFunctions.cpp---------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "lldb/DataFormatters/CXXFormatterFunctions.h" + +#include "llvm/Support/ConvertUTF.h" + +#include "lldb/Core/DataBufferHeap.h" +#include "lldb/Core/Error.h" +#include "lldb/Core/Stream.h" +#include "lldb/Core/ValueObject.h" +#include "lldb/Core/ValueObjectConstResult.h" +#include "lldb/Host/Endian.h" +#include "lldb/Symbol/ClangASTContext.h" +#include "lldb/Target/ObjCLanguageRuntime.h" +#include "lldb/Target/Target.h" + +#include + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::formatters; + +bool +lldb_private::formatters::ExtractValueFromObjCExpression (ValueObject &valobj, + const char* target_type, + const char* selector, + uint64_t &value) +{ + if (!target_type || !*target_type) + return false; + if (!selector || !*selector) + return false; + StreamString expr; + expr.Printf("(%s)[(id)0x%" PRIx64 " %s]",target_type,valobj.GetPointerValue(),selector); + ExecutionContext exe_ctx (valobj.GetExecutionContextRef()); + lldb::ValueObjectSP result_sp; + Target* target = exe_ctx.GetTargetPtr(); + StackFrame* stack_frame = exe_ctx.GetFramePtr(); + if (!target || !stack_frame) + return false; + + EvaluateExpressionOptions options; + options.SetCoerceToId(false) + .SetUnwindOnError(true) + .SetKeepInMemory(true); + + target->EvaluateExpression(expr.GetData(), + stack_frame, + result_sp, + options); + if (!result_sp) + return false; + value = result_sp->GetValueAsUnsigned(0); + return true; +} + +bool +lldb_private::formatters::ExtractSummaryFromObjCExpression (ValueObject &valobj, + const char* target_type, + const char* selector, + Stream &stream) +{ + if (!target_type || !*target_type) + return false; + if (!selector || !*selector) + return false; + StreamString expr; + expr.Printf("(%s)[(id)0x%" PRIx64 " %s]",target_type,valobj.GetPointerValue(),selector); + ExecutionContext exe_ctx (valobj.GetExecutionContextRef()); + lldb::ValueObjectSP result_sp; + Target* target = exe_ctx.GetTargetPtr(); + StackFrame* stack_frame = exe_ctx.GetFramePtr(); + if (!target || !stack_frame) + return false; + + EvaluateExpressionOptions options; + options.SetCoerceToId(false) + .SetUnwindOnError(true) + .SetKeepInMemory(true) + .SetUseDynamic(lldb::eDynamicCanRunTarget); + + target->EvaluateExpression(expr.GetData(), + stack_frame, + result_sp, + options); + if (!result_sp) + return false; + stream.Printf("%s",result_sp->GetSummaryAsCString()); + return true; +} + +lldb::ValueObjectSP +lldb_private::formatters::CallSelectorOnObject (ValueObject &valobj, + const char* return_type, + const char* selector, + uint64_t index) +{ + lldb::ValueObjectSP valobj_sp; + if (!return_type || !*return_type) + return valobj_sp; + if (!selector || !*selector) + return valobj_sp; + StreamString expr_path_stream; + valobj.GetExpressionPath(expr_path_stream, false); + StreamString expr; + expr.Printf("(%s)[%s %s:%" PRId64 "]",return_type,expr_path_stream.GetData(),selector,index); + ExecutionContext exe_ctx (valobj.GetExecutionContextRef()); + lldb::ValueObjectSP result_sp; + Target* target = exe_ctx.GetTargetPtr(); + StackFrame* stack_frame = exe_ctx.GetFramePtr(); + if (!target || !stack_frame) + return valobj_sp; + + EvaluateExpressionOptions options; + options.SetCoerceToId(false) + .SetUnwindOnError(true) + .SetKeepInMemory(true) + .SetUseDynamic(lldb::eDynamicCanRunTarget); + + target->EvaluateExpression(expr.GetData(), + stack_frame, + valobj_sp, + options); + return valobj_sp; +} + +lldb::ValueObjectSP +lldb_private::formatters::CallSelectorOnObject (ValueObject &valobj, + const char* return_type, + const char* selector, + const char* key) +{ + lldb::ValueObjectSP valobj_sp; + if (!return_type || !*return_type) + return valobj_sp; + if (!selector || !*selector) + return valobj_sp; + if (!key || !*key) + return valobj_sp; + StreamString expr_path_stream; + valobj.GetExpressionPath(expr_path_stream, false); + StreamString expr; + expr.Printf("(%s)[%s %s:%s]",return_type,expr_path_stream.GetData(),selector,key); + ExecutionContext exe_ctx (valobj.GetExecutionContextRef()); + lldb::ValueObjectSP result_sp; + Target* target = exe_ctx.GetTargetPtr(); + StackFrame* stack_frame = exe_ctx.GetFramePtr(); + if (!target || !stack_frame) + return valobj_sp; + + EvaluateExpressionOptions options; + options.SetCoerceToId(false) + .SetUnwindOnError(true) + .SetKeepInMemory(true) + .SetUseDynamic(lldb::eDynamicCanRunTarget); + + target->EvaluateExpression(expr.GetData(), + stack_frame, + valobj_sp, + options); + return valobj_sp; +} + +// use this call if you already have an LLDB-side buffer for the data +template +static bool +DumpUTFBufferToStream (ConversionResult (*ConvertFunction) (const SourceDataType**, + const SourceDataType*, + UTF8**, + UTF8*, + ConversionFlags), + DataExtractor& data, + Stream& stream, + char prefix_token = '@', + char quote = '"', + uint32_t sourceSize = 0) +{ + if (prefix_token != 0) + stream.Printf("%c",prefix_token); + if (quote != 0) + stream.Printf("%c",quote); + if (data.GetByteSize() && data.GetDataStart() && data.GetDataEnd()) + { + const int bufferSPSize = data.GetByteSize(); + if (sourceSize == 0) + { + const int origin_encoding = 8*sizeof(SourceDataType); + sourceSize = bufferSPSize/(origin_encoding / 4); + } + + SourceDataType *data_ptr = (SourceDataType*)data.GetDataStart(); + SourceDataType *data_end_ptr = data_ptr + sourceSize; + + while (data_ptr < data_end_ptr) + { + if (!*data_ptr) + { + data_end_ptr = data_ptr; + break; + } + data_ptr++; + } + + data_ptr = (SourceDataType*)data.GetDataStart(); + + lldb::DataBufferSP utf8_data_buffer_sp; + UTF8* utf8_data_ptr = nullptr; + UTF8* utf8_data_end_ptr = nullptr; + + if (ConvertFunction) + { + utf8_data_buffer_sp.reset(new DataBufferHeap(4*bufferSPSize,0)); + utf8_data_ptr = (UTF8*)utf8_data_buffer_sp->GetBytes(); + utf8_data_end_ptr = utf8_data_ptr + utf8_data_buffer_sp->GetByteSize(); + ConvertFunction ( (const SourceDataType**)&data_ptr, data_end_ptr, &utf8_data_ptr, utf8_data_end_ptr, lenientConversion ); + utf8_data_ptr = (UTF8*)utf8_data_buffer_sp->GetBytes(); // needed because the ConvertFunction will change the value of the data_ptr + } + else + { + // just copy the pointers - the cast is necessary to make the compiler happy + // but this should only happen if we are reading UTF8 data + utf8_data_ptr = (UTF8*)data_ptr; + utf8_data_end_ptr = (UTF8*)data_end_ptr; + } + + // since we tend to accept partial data (and even partially malformed data) + // we might end up with no NULL terminator before the end_ptr + // hence we need to take a slower route and ensure we stay within boundaries + for (;utf8_data_ptr != utf8_data_end_ptr; utf8_data_ptr++) + { + if (!*utf8_data_ptr) + break; + stream.Printf("%c",*utf8_data_ptr); + } + } + if (quote != 0) + stream.Printf("%c",quote); + return true; +} + +template +class ReadUTFBufferAndDumpToStreamOptions +{ +public: + typedef ConversionResult (*ConvertFunctionType) (const SourceDataType**, + const SourceDataType*, + UTF8**, + UTF8*, + ConversionFlags); + + ReadUTFBufferAndDumpToStreamOptions () : + m_conversion_function(NULL), + m_location(0), + m_process_sp(), + m_stream(NULL), + m_prefix_token('@'), + m_quote('"'), + m_source_size(0), + m_needs_zero_termination(true) + { + } + + ReadUTFBufferAndDumpToStreamOptions& + SetConversionFunction (ConvertFunctionType f) + { + m_conversion_function = f; + return *this; + } + + ConvertFunctionType + GetConversionFunction () const + { + return m_conversion_function; + } + + ReadUTFBufferAndDumpToStreamOptions& + SetLocation (uint64_t l) + { + m_location = l; + return *this; + } + + uint64_t + GetLocation () const + { + return m_location; + } + + ReadUTFBufferAndDumpToStreamOptions& + SetProcessSP (ProcessSP p) + { + m_process_sp = p; + return *this; + } + + ProcessSP + GetProcessSP () const + { + return m_process_sp; + } + + ReadUTFBufferAndDumpToStreamOptions& + SetStream (Stream* s) + { + m_stream = s; + return *this; + } + + Stream* + GetStream () const + { + return m_stream; + } + + ReadUTFBufferAndDumpToStreamOptions& + SetPrefixToken (char p) + { + m_prefix_token = p; + return *this; + } + + char + GetPrefixToken () const + { + return m_prefix_token; + } + + ReadUTFBufferAndDumpToStreamOptions& + SetQuote (char q) + { + m_quote = q; + return *this; + } + + char + GetQuote () const + { + return m_quote; + } + + ReadUTFBufferAndDumpToStreamOptions& + SetSourceSize (uint32_t s) + { + m_source_size = s; + return *this; + } + + uint32_t + GetSourceSize () const + { + return m_source_size; + } + + ReadUTFBufferAndDumpToStreamOptions& + SetNeedsZeroTermination (bool z) + { + m_needs_zero_termination = z; + return *this; + } + + bool + GetNeedsZeroTermination () const + { + return m_needs_zero_termination; + } + +private: + ConvertFunctionType m_conversion_function; + uint64_t m_location; + ProcessSP m_process_sp; + Stream* m_stream; + char m_prefix_token; + char m_quote; + uint32_t m_source_size; + bool m_needs_zero_termination; +}; + +template +static bool +ReadUTFBufferAndDumpToStream (const ReadUTFBufferAndDumpToStreamOptions& options) +{ + if (options.GetLocation() == 0 || options.GetLocation() == LLDB_INVALID_ADDRESS) + return false; + + ProcessSP process_sp(options.GetProcessSP()); + + if (!process_sp) + return false; + + const int type_width = sizeof(SourceDataType); + const int origin_encoding = 8 * type_width ; + if (origin_encoding != 8 && origin_encoding != 16 && origin_encoding != 32) + return false; + // if not UTF8, I need a conversion function to return proper UTF8 + if (origin_encoding != 8 && !options.GetConversionFunction()) + return false; + + if (!options.GetStream()) + return false; + + uint32_t sourceSize = options.GetSourceSize(); + bool needs_zero_terminator = options.GetNeedsZeroTermination(); + + if (!sourceSize) + { + sourceSize = process_sp->GetTarget().GetMaximumSizeOfStringSummary(); + needs_zero_terminator = true; + } + else + sourceSize = std::min(sourceSize,process_sp->GetTarget().GetMaximumSizeOfStringSummary()); + + const int bufferSPSize = sourceSize * type_width; + + lldb::DataBufferSP buffer_sp(new DataBufferHeap(bufferSPSize,0)); + + if (!buffer_sp->GetBytes()) + return false; + + Error error; + char *buffer = reinterpret_cast(buffer_sp->GetBytes()); + + size_t data_read = 0; + if (needs_zero_terminator) + data_read = process_sp->ReadStringFromMemory(options.GetLocation(), buffer, bufferSPSize, error, type_width); + else + data_read = process_sp->ReadMemoryFromInferior(options.GetLocation(), (char*)buffer_sp->GetBytes(), bufferSPSize, error); + + if (error.Fail() || data_read == 0) + { + options.GetStream()->Printf("unable to read data"); + return true; + } + + DataExtractor data(buffer_sp, process_sp->GetByteOrder(), process_sp->GetAddressByteSize()); + + return DumpUTFBufferToStream(options.GetConversionFunction(), data, *options.GetStream(), options.GetPrefixToken(), options.GetQuote(), sourceSize); +} + +bool +lldb_private::formatters::Char16StringSummaryProvider (ValueObject& valobj, Stream& stream) +{ + ProcessSP process_sp = valobj.GetProcessSP(); + if (!process_sp) + return false; + + lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0); + + if (!valobj_addr) + return false; + + ReadUTFBufferAndDumpToStreamOptions options; + options.SetLocation(valobj_addr); + options.SetConversionFunction(ConvertUTF16toUTF8); + options.SetProcessSP(process_sp); + options.SetStream(&stream); + options.SetPrefixToken('u'); + + if (!ReadUTFBufferAndDumpToStream(options)) + { + stream.Printf("Summary Unavailable"); + return true; + } + + return true; +} + +bool +lldb_private::formatters::Char32StringSummaryProvider (ValueObject& valobj, Stream& stream) +{ + ProcessSP process_sp = valobj.GetProcessSP(); + if (!process_sp) + return false; + + lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0); + + if (!valobj_addr) + return false; + + ReadUTFBufferAndDumpToStreamOptions options; + options.SetLocation(valobj_addr); + options.SetConversionFunction(ConvertUTF32toUTF8); + options.SetProcessSP(process_sp); + options.SetStream(&stream); + options.SetPrefixToken('U'); + + if (!ReadUTFBufferAndDumpToStream(options)) + { + stream.Printf("Summary Unavailable"); + return true; + } + + return true; +} + +bool +lldb_private::formatters::WCharStringSummaryProvider (ValueObject& valobj, Stream& stream) +{ + ProcessSP process_sp = valobj.GetProcessSP(); + if (!process_sp) + return false; + + lldb::addr_t data_addr = 0; + + if (valobj.IsPointerType()) + data_addr = valobj.GetValueAsUnsigned(0); + else if (valobj.IsArrayType()) + data_addr = valobj.GetAddressOf(); + + if (data_addr == 0 || data_addr == LLDB_INVALID_ADDRESS) + return false; + + clang::ASTContext* ast = valobj.GetClangType().GetASTContext(); + + if (!ast) + return false; + + ClangASTType wchar_clang_type = ClangASTContext::GetBasicType(ast, lldb::eBasicTypeWChar); + const uint32_t wchar_size = wchar_clang_type.GetBitSize(); + + switch (wchar_size) + { + case 8: + { + // utf 8 + + ReadUTFBufferAndDumpToStreamOptions options; + options.SetLocation(data_addr); + options.SetConversionFunction(nullptr); + options.SetProcessSP(process_sp); + options.SetStream(&stream); + options.SetPrefixToken('L'); + + return ReadUTFBufferAndDumpToStream(options); + } + case 16: + { + // utf 16 + ReadUTFBufferAndDumpToStreamOptions options; + options.SetLocation(data_addr); + options.SetConversionFunction(ConvertUTF16toUTF8); + options.SetProcessSP(process_sp); + options.SetStream(&stream); + options.SetPrefixToken('L'); + + return ReadUTFBufferAndDumpToStream(options); + } + case 32: + { + // utf 32 + ReadUTFBufferAndDumpToStreamOptions options; + options.SetLocation(data_addr); + options.SetConversionFunction(ConvertUTF32toUTF8); + options.SetProcessSP(process_sp); + options.SetStream(&stream); + options.SetPrefixToken('L'); + + return ReadUTFBufferAndDumpToStream(options); + } + default: + stream.Printf("size for wchar_t is not valid"); + return true; + } + return true; +} + +bool +lldb_private::formatters::Char16SummaryProvider (ValueObject& valobj, Stream& stream) +{ + DataExtractor data; + valobj.GetData(data); + + std::string value; + valobj.GetValueAsCString(lldb::eFormatUnicode16, value); + if (!value.empty()) + stream.Printf("%s ", value.c_str()); + + return DumpUTFBufferToStream(ConvertUTF16toUTF8,data,stream, 'u','\'',1); +} + +bool +lldb_private::formatters::Char32SummaryProvider (ValueObject& valobj, Stream& stream) +{ + DataExtractor data; + valobj.GetData(data); + + std::string value; + valobj.GetValueAsCString(lldb::eFormatUnicode32, value); + if (!value.empty()) + stream.Printf("%s ", value.c_str()); + + return DumpUTFBufferToStream(ConvertUTF32toUTF8,data,stream, 'U','\'',1); +} + +bool +lldb_private::formatters::WCharSummaryProvider (ValueObject& valobj, Stream& stream) +{ + DataExtractor data; + valobj.GetData(data); + + clang::ASTContext* ast = valobj.GetClangType().GetASTContext(); + + if (!ast) + return false; + + ClangASTType wchar_clang_type = ClangASTContext::GetBasicType(ast, lldb::eBasicTypeWChar); + const uint32_t wchar_size = wchar_clang_type.GetBitSize(); + std::string value; + + switch (wchar_size) + { + case 8: + // utf 8 + valobj.GetValueAsCString(lldb::eFormatChar, value); + if (!value.empty()) + stream.Printf("%s ", value.c_str()); + return DumpUTFBufferToStream(nullptr, + data, + stream, + 'L', + '\'', + 1); + case 16: + // utf 16 + valobj.GetValueAsCString(lldb::eFormatUnicode16, value); + if (!value.empty()) + stream.Printf("%s ", value.c_str()); + return DumpUTFBufferToStream(ConvertUTF16toUTF8, + data, + stream, + 'L', + '\'', + 1); + case 32: + // utf 32 + valobj.GetValueAsCString(lldb::eFormatUnicode32, value); + if (!value.empty()) + stream.Printf("%s ", value.c_str()); + return DumpUTFBufferToStream(ConvertUTF32toUTF8, + data, + stream, + 'L', + '\'', + 1); + default: + stream.Printf("size for wchar_t is not valid"); + return true; + } + return true; +} + +// the field layout in a libc++ string (cap, side, data or data, size, cap) +enum LibcxxStringLayoutMode +{ + eLibcxxStringLayoutModeCSD = 0, + eLibcxxStringLayoutModeDSC = 1, + eLibcxxStringLayoutModeInvalid = 0xffff +}; + +// this function abstracts away the layout and mode details of a libc++ string +// and returns the address of the data and the size ready for callers to consume +static bool +ExtractLibcxxStringInfo (ValueObject& valobj, + ValueObjectSP &location_sp, + uint64_t& size) +{ + ValueObjectSP D(valobj.GetChildAtIndexPath({0,0,0,0})); + if (!D) + return false; + + ValueObjectSP layout_decider(D->GetChildAtIndexPath({0,0})); + + // this child should exist + if (!layout_decider) + return false; + + ConstString g_data_name("__data_"); + ConstString g_size_name("__size_"); + bool short_mode = false; // this means the string is in short-mode and the data is stored inline + LibcxxStringLayoutMode layout = (layout_decider->GetName() == g_data_name) ? eLibcxxStringLayoutModeDSC : eLibcxxStringLayoutModeCSD; + uint64_t size_mode_value = 0; + + if (layout == eLibcxxStringLayoutModeDSC) + { + ValueObjectSP size_mode(D->GetChildAtIndexPath({1,1,0})); + if (!size_mode) + return false; + + if (size_mode->GetName() != g_size_name) + { + // we are hitting the padding structure, move along + size_mode = D->GetChildAtIndexPath({1,1,1}); + if (!size_mode) + return false; + } + + size_mode_value = (size_mode->GetValueAsUnsigned(0)); + short_mode = ((size_mode_value & 0x80) == 0); + } + else + { + ValueObjectSP size_mode(D->GetChildAtIndexPath({1,0,0})); + if (!size_mode) + return false; + + size_mode_value = (size_mode->GetValueAsUnsigned(0)); + short_mode = ((size_mode_value & 1) == 0); + } + + if (short_mode) + { + ValueObjectSP s(D->GetChildAtIndex(1, true)); + if (!s) + return false; + location_sp = s->GetChildAtIndex((layout == eLibcxxStringLayoutModeDSC) ? 0 : 1, true); + size = (layout == eLibcxxStringLayoutModeDSC) ? size_mode_value : ((size_mode_value >> 1) % 256); + return (location_sp.get() != nullptr); + } + else + { + ValueObjectSP l(D->GetChildAtIndex(0, true)); + if (!l) + return false; + // we can use the layout_decider object as the data pointer + location_sp = (layout == eLibcxxStringLayoutModeDSC) ? layout_decider : l->GetChildAtIndex(2, true); + ValueObjectSP size_vo(l->GetChildAtIndex(1, true)); + if (!size_vo || !location_sp) + return false; + size = size_vo->GetValueAsUnsigned(0); + return true; + } +} + +bool +lldb_private::formatters::LibcxxWStringSummaryProvider (ValueObject& valobj, Stream& stream) +{ + uint64_t size = 0; + ValueObjectSP location_sp((ValueObject*)nullptr); + if (!ExtractLibcxxStringInfo(valobj, location_sp, size)) + return false; + if (size == 0) + { + stream.Printf("L\"\""); + return true; + } + if (!location_sp) + return false; + return WCharStringSummaryProvider(*location_sp.get(), stream); +} + +bool +lldb_private::formatters::LibcxxStringSummaryProvider (ValueObject& valobj, Stream& stream) +{ + uint64_t size = 0; + ValueObjectSP location_sp((ValueObject*)nullptr); + if (!ExtractLibcxxStringInfo(valobj, location_sp, size)) + return false; + if (size == 0) + { + stream.Printf("\"\""); + return true; + } + if (!location_sp) + return false; + Error error; + if (location_sp->ReadPointedString(stream, + error, + 0, // max length is decided by the settings + false) == 0) // do not honor array (terminates on first 0 byte even for a char[]) + stream.Printf("\"\""); // if nothing was read, print an empty string + return error.Success(); +} + +bool +lldb_private::formatters::ObjCClassSummaryProvider (ValueObject& valobj, Stream& stream) +{ + ProcessSP process_sp = valobj.GetProcessSP(); + if (!process_sp) + return false; + + ObjCLanguageRuntime* runtime = (ObjCLanguageRuntime*)process_sp->GetLanguageRuntime(lldb::eLanguageTypeObjC); + + if (!runtime) + return false; + + ObjCLanguageRuntime::ClassDescriptorSP descriptor(runtime->GetClassDescriptorFromISA(valobj.GetValueAsUnsigned(0))); + + if (!descriptor.get() || !descriptor->IsValid()) + return false; + + const char* class_name = descriptor->GetClassName().GetCString(); + + if (!class_name || !*class_name) + return false; + + stream.Printf("%s",class_name); + return true; +} + +class ObjCClassSyntheticChildrenFrontEnd : public SyntheticChildrenFrontEnd +{ +public: + ObjCClassSyntheticChildrenFrontEnd (lldb::ValueObjectSP valobj_sp) : + SyntheticChildrenFrontEnd(*valobj_sp.get()) + { + } + + virtual size_t + CalculateNumChildren () + { + return 0; + } + + virtual lldb::ValueObjectSP + GetChildAtIndex (size_t idx) + { + return lldb::ValueObjectSP(); + } + + virtual bool + Update() + { + return false; + } + + virtual bool + MightHaveChildren () + { + return false; + } + + virtual size_t + GetIndexOfChildWithName (const ConstString &name) + { + return UINT32_MAX; + } + + virtual + ~ObjCClassSyntheticChildrenFrontEnd () + { + } +}; + +SyntheticChildrenFrontEnd* +lldb_private::formatters::ObjCClassSyntheticFrontEndCreator (CXXSyntheticChildren*, lldb::ValueObjectSP valobj_sp) +{ + return new ObjCClassSyntheticChildrenFrontEnd(valobj_sp); +} + +template +bool +lldb_private::formatters::NSDataSummaryProvider (ValueObject& valobj, Stream& stream) +{ + ProcessSP process_sp = valobj.GetProcessSP(); + if (!process_sp) + return false; + + ObjCLanguageRuntime* runtime = (ObjCLanguageRuntime*)process_sp->GetLanguageRuntime(lldb::eLanguageTypeObjC); + + if (!runtime) + return false; + + ObjCLanguageRuntime::ClassDescriptorSP descriptor(runtime->GetClassDescriptor(valobj)); + + if (!descriptor.get() || !descriptor->IsValid()) + return false; + + bool is_64bit = (process_sp->GetAddressByteSize() == 8); + lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0); + + if (!valobj_addr) + return false; + + uint64_t value = 0; + + const char* class_name = descriptor->GetClassName().GetCString(); + + if (!class_name || !*class_name) + return false; + + if (!strcmp(class_name,"NSConcreteData") || + !strcmp(class_name,"NSConcreteMutableData") || + !strcmp(class_name,"__NSCFData")) + { + uint32_t offset = (is_64bit ? 16 : 8); + Error error; + value = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + offset, is_64bit ? 8 : 4, 0, error); + if (error.Fail()) + return false; + } + else + { + if (!ExtractValueFromObjCExpression(valobj, "int", "length", value)) + return false; + } + + stream.Printf("%s%" PRIu64 " byte%s%s", + (needs_at ? "@\"" : ""), + value, + (value != 1 ? "s" : ""), + (needs_at ? "\"" : "")); + + return true; +} + +static bool +ReadAsciiBufferAndDumpToStream (lldb::addr_t location, + lldb::ProcessSP& process_sp, + Stream& dest, + uint32_t size = 0, + Error* error = NULL, + size_t *data_read = NULL, + char prefix_token = '@', + char quote = '"') +{ + Error my_error; + size_t my_data_read; + if (!process_sp || location == 0) + return false; + + if (!size) + size = process_sp->GetTarget().GetMaximumSizeOfStringSummary(); + else + size = std::min(size,process_sp->GetTarget().GetMaximumSizeOfStringSummary()); + + lldb::DataBufferSP buffer_sp(new DataBufferHeap(size,0)); + + my_data_read = process_sp->ReadCStringFromMemory(location, (char*)buffer_sp->GetBytes(), size, my_error); + + if (error) + *error = my_error; + if (data_read) + *data_read = my_data_read; + + if (my_error.Fail()) + return false; + + dest.Printf("%c%c",prefix_token,quote); + + if (my_data_read) + dest.Printf("%s",(char*)buffer_sp->GetBytes()); + + dest.Printf("%c",quote); + + return true; +} + +bool +lldb_private::formatters::NSStringSummaryProvider (ValueObject& valobj, Stream& stream) +{ + ProcessSP process_sp = valobj.GetProcessSP(); + if (!process_sp) + return false; + + ObjCLanguageRuntime* runtime = (ObjCLanguageRuntime*)process_sp->GetLanguageRuntime(lldb::eLanguageTypeObjC); + + if (!runtime) + return false; + + ObjCLanguageRuntime::ClassDescriptorSP descriptor(runtime->GetClassDescriptor(valobj)); + + if (!descriptor.get() || !descriptor->IsValid()) + return false; + + uint32_t ptr_size = process_sp->GetAddressByteSize(); + + lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0); + + if (!valobj_addr) + return false; + + const char* class_name = descriptor->GetClassName().GetCString(); + + if (!class_name || !*class_name) + return false; + + uint64_t info_bits_location = valobj_addr + ptr_size; + if (process_sp->GetByteOrder() != lldb::eByteOrderLittle) + info_bits_location += 3; + + Error error; + + uint8_t info_bits = process_sp->ReadUnsignedIntegerFromMemory(info_bits_location, 1, 0, error); + if (error.Fail()) + return false; + + bool is_mutable = (info_bits & 1) == 1; + bool is_inline = (info_bits & 0x60) == 0; + bool has_explicit_length = (info_bits & (1 | 4)) != 4; + bool is_unicode = (info_bits & 0x10) == 0x10; + bool is_special = strcmp(class_name,"NSPathStore2") == 0; + bool has_null = (info_bits & 8) == 8; + + size_t explicit_length = 0; + if (!has_null && has_explicit_length && !is_special) + { + lldb::addr_t explicit_length_offset = 2*ptr_size; + if (is_mutable and not is_inline) + explicit_length_offset = explicit_length_offset + ptr_size; // notInlineMutable.length; + else if (is_inline) + explicit_length = explicit_length + 0; // inline1.length; + else if (not is_inline and not is_mutable) + explicit_length_offset = explicit_length_offset + ptr_size; // notInlineImmutable1.length; + else + explicit_length_offset = 0; + + if (explicit_length_offset) + { + explicit_length_offset = valobj_addr + explicit_length_offset; + explicit_length = process_sp->ReadUnsignedIntegerFromMemory(explicit_length_offset, 4, 0, error); + } + } + + if (strcmp(class_name,"NSString") && + strcmp(class_name,"CFStringRef") && + strcmp(class_name,"CFMutableStringRef") && + strcmp(class_name,"__NSCFConstantString") && + strcmp(class_name,"__NSCFString") && + strcmp(class_name,"NSCFConstantString") && + strcmp(class_name,"NSCFString") && + strcmp(class_name,"NSPathStore2")) + { + // not one of us - but tell me class name + stream.Printf("class name = %s",class_name); + return true; + } + + if (is_mutable) + { + uint64_t location = 2 * ptr_size + valobj_addr; + location = process_sp->ReadPointerFromMemory(location, error); + if (error.Fail()) + return false; + if (has_explicit_length and is_unicode) + { + ReadUTFBufferAndDumpToStreamOptions options; + options.SetConversionFunction(ConvertUTF16toUTF8); + options.SetLocation(location); + options.SetProcessSP(process_sp); + options.SetStream(&stream); + options.SetPrefixToken('@'); + options.SetQuote('"'); + options.SetSourceSize(explicit_length); + options.SetNeedsZeroTermination(false); + return ReadUTFBufferAndDumpToStream (options); + } + else + return ReadAsciiBufferAndDumpToStream(location+1,process_sp,stream, explicit_length); + } + else if (is_inline && has_explicit_length && !is_unicode && !is_special && !is_mutable) + { + uint64_t location = 3 * ptr_size + valobj_addr; + return ReadAsciiBufferAndDumpToStream(location,process_sp,stream,explicit_length); + } + else if (is_unicode) + { + uint64_t location = valobj_addr + 2*ptr_size; + if (is_inline) + { + if (!has_explicit_length) + { + stream.Printf("found new combo"); + return true; + } + else + location += ptr_size; + } + else + { + location = process_sp->ReadPointerFromMemory(location, error); + if (error.Fail()) + return false; + } + ReadUTFBufferAndDumpToStreamOptions options; + options.SetConversionFunction(ConvertUTF16toUTF8); + options.SetLocation(location); + options.SetProcessSP(process_sp); + options.SetStream(&stream); + options.SetPrefixToken('@'); + options.SetQuote('"'); + options.SetSourceSize(explicit_length); + options.SetNeedsZeroTermination(has_explicit_length == false); + return ReadUTFBufferAndDumpToStream (options); + } + else if (is_special) + { + uint64_t location = valobj_addr + (ptr_size == 8 ? 12 : 8); + ReadUTFBufferAndDumpToStreamOptions options; + options.SetConversionFunction(ConvertUTF16toUTF8); + options.SetLocation(location); + options.SetProcessSP(process_sp); + options.SetStream(&stream); + options.SetPrefixToken('@'); + options.SetQuote('"'); + options.SetSourceSize(explicit_length); + options.SetNeedsZeroTermination(has_explicit_length == false); + return ReadUTFBufferAndDumpToStream (options); + } + else if (is_inline) + { + uint64_t location = valobj_addr + 2*ptr_size; + if (!has_explicit_length) + location++; + return ReadAsciiBufferAndDumpToStream(location,process_sp,stream,explicit_length); + } + else + { + uint64_t location = valobj_addr + 2*ptr_size; + location = process_sp->ReadPointerFromMemory(location, error); + if (error.Fail()) + return false; + if (has_explicit_length && !has_null) + explicit_length++; // account for the fact that there is no NULL and we need to have one added + return ReadAsciiBufferAndDumpToStream(location,process_sp,stream,explicit_length); + } + + stream.Printf("class name = %s",class_name); + return true; + +} + +bool +lldb_private::formatters::NSAttributedStringSummaryProvider (ValueObject& valobj, Stream& stream) +{ + TargetSP target_sp(valobj.GetTargetSP()); + if (!target_sp) + return false; + uint32_t addr_size = target_sp->GetArchitecture().GetAddressByteSize(); + uint64_t pointer_value = valobj.GetValueAsUnsigned(0); + if (!pointer_value) + return false; + pointer_value += addr_size; + ClangASTType type(valobj.GetClangType()); + ExecutionContext exe_ctx(target_sp,false); + ValueObjectSP child_ptr_sp(valobj.CreateValueObjectFromAddress("string_ptr", pointer_value, exe_ctx, type)); + if (!child_ptr_sp) + return false; + DataExtractor data; + child_ptr_sp->GetData(data); + ValueObjectSP child_sp(child_ptr_sp->CreateValueObjectFromData("string_data", data, exe_ctx, type)); + child_sp->GetValueAsUnsigned(0); + if (child_sp) + return NSStringSummaryProvider(*child_sp, stream); + return false; +} + +bool +lldb_private::formatters::NSMutableAttributedStringSummaryProvider (ValueObject& valobj, Stream& stream) +{ + return NSAttributedStringSummaryProvider(valobj, stream); +} + +bool +lldb_private::formatters::RuntimeSpecificDescriptionSummaryProvider (ValueObject& valobj, Stream& stream) +{ + stream.Printf("%s",valobj.GetObjectDescription()); + return true; +} + +bool +lldb_private::formatters::ObjCBOOLSummaryProvider (ValueObject& valobj, Stream& stream) +{ + const uint32_t type_info = valobj.GetClangType().GetTypeInfo(); + + ValueObjectSP real_guy_sp = valobj.GetSP(); + + if (type_info & ClangASTType::eTypeIsPointer) + { + Error err; + real_guy_sp = valobj.Dereference(err); + if (err.Fail() || !real_guy_sp) + return false; + } + else if (type_info & ClangASTType::eTypeIsReference) + { + real_guy_sp = valobj.GetChildAtIndex(0, true); + if (!real_guy_sp) + return false; + } + uint64_t value = real_guy_sp->GetValueAsUnsigned(0); + if (value == 0) + { + stream.Printf("NO"); + return true; + } + stream.Printf("YES"); + return true; +} + +template +bool +lldb_private::formatters::ObjCSELSummaryProvider (ValueObject& valobj, Stream& stream) +{ + lldb::ValueObjectSP valobj_sp; + + ClangASTType charstar (valobj.GetClangType().GetBasicTypeFromAST(eBasicTypeChar).GetPointerType()); + + if (!charstar) + return false; + + ExecutionContext exe_ctx(valobj.GetExecutionContextRef()); + + if (is_sel_ptr) + { + lldb::addr_t data_address = valobj.GetValueAsUnsigned(LLDB_INVALID_ADDRESS); + if (data_address == LLDB_INVALID_ADDRESS) + return false; + valobj_sp = ValueObject::CreateValueObjectFromAddress("text", data_address, exe_ctx, charstar); + } + else + { + DataExtractor data; + valobj.GetData(data); + valobj_sp = ValueObject::CreateValueObjectFromData("text", data, exe_ctx, charstar); + } + + if (!valobj_sp) + return false; + + stream.Printf("%s",valobj_sp->GetSummaryAsCString()); + return true; +} + +// POSIX has an epoch on Jan-1-1970, but Cocoa prefers Jan-1-2001 +// this call gives the POSIX equivalent of the Cocoa epoch +time_t +lldb_private::formatters::GetOSXEpoch () +{ + static time_t epoch = 0; + if (!epoch) + { + tzset(); + tm tm_epoch; + tm_epoch.tm_sec = 0; + tm_epoch.tm_hour = 0; + tm_epoch.tm_min = 0; + tm_epoch.tm_mon = 0; + tm_epoch.tm_mday = 1; + tm_epoch.tm_year = 2001-1900; // for some reason, we need to subtract 1900 from this field. not sure why. + tm_epoch.tm_isdst = -1; + tm_epoch.tm_gmtoff = 0; + tm_epoch.tm_zone = NULL; + epoch = timegm(&tm_epoch); + } + return epoch; +} + +size_t +lldb_private::formatters::ExtractIndexFromString (const char* item_name) +{ + if (!item_name || !*item_name) + return UINT32_MAX; + if (*item_name != '[') + return UINT32_MAX; + item_name++; + char* endptr = NULL; + unsigned long int idx = ::strtoul(item_name, &endptr, 0); + if (idx == 0 && endptr == item_name) + return UINT32_MAX; + if (idx == ULONG_MAX) + return UINT32_MAX; + return idx; +} + +lldb_private::formatters::VectorIteratorSyntheticFrontEnd::VectorIteratorSyntheticFrontEnd (lldb::ValueObjectSP valobj_sp, + ConstString item_name) : +SyntheticChildrenFrontEnd(*valobj_sp.get()), +m_exe_ctx_ref(), +m_item_name(item_name), +m_item_sp() +{ + if (valobj_sp) + Update(); +} + +bool +lldb_private::formatters::VectorIteratorSyntheticFrontEnd::Update() +{ + m_item_sp.reset(); + + ValueObjectSP valobj_sp = m_backend.GetSP(); + if (!valobj_sp) + return false; + + if (!valobj_sp) + return false; + + ValueObjectSP item_ptr(valobj_sp->GetChildMemberWithName(m_item_name,true)); + if (!item_ptr) + return false; + if (item_ptr->GetValueAsUnsigned(0) == 0) + return false; + Error err; + m_exe_ctx_ref = valobj_sp->GetExecutionContextRef(); + m_item_sp = ValueObject::CreateValueObjectFromAddress("item", item_ptr->GetValueAsUnsigned(0), m_exe_ctx_ref, item_ptr->GetClangType().GetPointeeType()); + if (err.Fail()) + m_item_sp.reset(); + return false; +} + +size_t +lldb_private::formatters::VectorIteratorSyntheticFrontEnd::CalculateNumChildren () +{ + return 1; +} + +lldb::ValueObjectSP +lldb_private::formatters::VectorIteratorSyntheticFrontEnd::GetChildAtIndex (size_t idx) +{ + if (idx == 0) + return m_item_sp; + return lldb::ValueObjectSP(); +} + +bool +lldb_private::formatters::VectorIteratorSyntheticFrontEnd::MightHaveChildren () +{ + return true; +} + +size_t +lldb_private::formatters::VectorIteratorSyntheticFrontEnd::GetIndexOfChildWithName (const ConstString &name) +{ + if (name == ConstString("item")) + return 0; + return UINT32_MAX; +} + +lldb_private::formatters::VectorIteratorSyntheticFrontEnd::~VectorIteratorSyntheticFrontEnd () +{ +} + +template bool +lldb_private::formatters::NSDataSummaryProvider (ValueObject&, Stream&) ; + +template bool +lldb_private::formatters::NSDataSummaryProvider (ValueObject&, Stream&) ; + +template bool +lldb_private::formatters::ObjCSELSummaryProvider (ValueObject&, Stream&) ; + +template bool +lldb_private::formatters::ObjCSELSummaryProvider (ValueObject&, Stream&) ; diff --git a/contrib/llvm/tools/lldb/source/DataFormatters/Cocoa.cpp b/contrib/llvm/tools/lldb/source/DataFormatters/Cocoa.cpp new file mode 100644 index 00000000000..555954db0bb --- /dev/null +++ b/contrib/llvm/tools/lldb/source/DataFormatters/Cocoa.cpp @@ -0,0 +1,565 @@ +//===-- Cocoa.cpp -------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "lldb/DataFormatters/CXXFormatterFunctions.h" + +#include "lldb/Core/DataBufferHeap.h" +#include "lldb/Core/Error.h" +#include "lldb/Core/Stream.h" +#include "lldb/Core/ValueObject.h" +#include "lldb/Core/ValueObjectConstResult.h" +#include "lldb/Host/Endian.h" +#include "lldb/Symbol/ClangASTContext.h" +#include "lldb/Target/ObjCLanguageRuntime.h" +#include "lldb/Target/Target.h" + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::formatters; + +bool +lldb_private::formatters::NSBundleSummaryProvider (ValueObject& valobj, Stream& stream) +{ + ProcessSP process_sp = valobj.GetProcessSP(); + if (!process_sp) + return false; + + ObjCLanguageRuntime* runtime = (ObjCLanguageRuntime*)process_sp->GetLanguageRuntime(lldb::eLanguageTypeObjC); + + if (!runtime) + return false; + + ObjCLanguageRuntime::ClassDescriptorSP descriptor(runtime->GetClassDescriptor(valobj)); + + if (!descriptor.get() || !descriptor->IsValid()) + return false; + + uint32_t ptr_size = process_sp->GetAddressByteSize(); + + lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0); + + if (!valobj_addr) + return false; + + const char* class_name = descriptor->GetClassName().GetCString(); + + if (!class_name || !*class_name) + return false; + + if (!strcmp(class_name,"NSBundle")) + { + uint64_t offset = 5 * ptr_size; + ValueObjectSP text(valobj.GetSyntheticChildAtOffset(offset, valobj.GetClangType().GetBasicTypeFromAST(lldb::eBasicTypeObjCID), true)); + + StreamString summary_stream; + bool was_nsstring_ok = NSStringSummaryProvider(*text.get(), summary_stream); + if (was_nsstring_ok && summary_stream.GetSize() > 0) + { + stream.Printf("%s",summary_stream.GetData()); + return true; + } + } + // this is either an unknown subclass or an NSBundle that comes from [NSBundle mainBundle] + // which is encoded differently and needs to be handled by running code + return ExtractSummaryFromObjCExpression(valobj, "NSString*", "bundlePath", stream); +} + +bool +lldb_private::formatters::NSTimeZoneSummaryProvider (ValueObject& valobj, Stream& stream) +{ + ProcessSP process_sp = valobj.GetProcessSP(); + if (!process_sp) + return false; + + ObjCLanguageRuntime* runtime = (ObjCLanguageRuntime*)process_sp->GetLanguageRuntime(lldb::eLanguageTypeObjC); + + if (!runtime) + return false; + + ObjCLanguageRuntime::ClassDescriptorSP descriptor(runtime->GetClassDescriptor(valobj)); + + if (!descriptor.get() || !descriptor->IsValid()) + return false; + + uint32_t ptr_size = process_sp->GetAddressByteSize(); + + lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0); + + if (!valobj_addr) + return false; + + const char* class_name = descriptor->GetClassName().GetCString(); + + if (!class_name || !*class_name) + return false; + + if (!strcmp(class_name,"__NSTimeZone")) + { + uint64_t offset = ptr_size; + ValueObjectSP text(valobj.GetSyntheticChildAtOffset(offset, valobj.GetClangType(), true)); + StreamString summary_stream; + bool was_nsstring_ok = NSStringSummaryProvider(*text.get(), summary_stream); + if (was_nsstring_ok && summary_stream.GetSize() > 0) + { + stream.Printf("%s",summary_stream.GetData()); + return true; + } + } + return ExtractSummaryFromObjCExpression(valobj, "NSString*", "name", stream); +} + +bool +lldb_private::formatters::NSNotificationSummaryProvider (ValueObject& valobj, Stream& stream) +{ + ProcessSP process_sp = valobj.GetProcessSP(); + if (!process_sp) + return false; + + ObjCLanguageRuntime* runtime = (ObjCLanguageRuntime*)process_sp->GetLanguageRuntime(lldb::eLanguageTypeObjC); + + if (!runtime) + return false; + + ObjCLanguageRuntime::ClassDescriptorSP descriptor(runtime->GetClassDescriptor(valobj)); + + if (!descriptor.get() || !descriptor->IsValid()) + return false; + + uint32_t ptr_size = process_sp->GetAddressByteSize(); + + lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0); + + if (!valobj_addr) + return false; + + const char* class_name = descriptor->GetClassName().GetCString(); + + if (!class_name || !*class_name) + return false; + + if (!strcmp(class_name,"NSConcreteNotification")) + { + uint64_t offset = ptr_size; + ValueObjectSP text(valobj.GetSyntheticChildAtOffset(offset, valobj.GetClangType(), true)); + StreamString summary_stream; + bool was_nsstring_ok = NSStringSummaryProvider(*text.get(), summary_stream); + if (was_nsstring_ok && summary_stream.GetSize() > 0) + { + stream.Printf("%s",summary_stream.GetData()); + return true; + } + } + // this is either an unknown subclass or an NSBundle that comes from [NSBundle mainBundle] + // which is encoded differently and needs to be handled by running code + return ExtractSummaryFromObjCExpression(valobj, "NSString*", "name", stream); +} + +bool +lldb_private::formatters::NSMachPortSummaryProvider (ValueObject& valobj, Stream& stream) +{ + ProcessSP process_sp = valobj.GetProcessSP(); + if (!process_sp) + return false; + + ObjCLanguageRuntime* runtime = (ObjCLanguageRuntime*)process_sp->GetLanguageRuntime(lldb::eLanguageTypeObjC); + + if (!runtime) + return false; + + ObjCLanguageRuntime::ClassDescriptorSP descriptor(runtime->GetClassDescriptor(valobj)); + + if (!descriptor.get() || !descriptor->IsValid()) + return false; + + uint32_t ptr_size = process_sp->GetAddressByteSize(); + + lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0); + + if (!valobj_addr) + return false; + + const char* class_name = descriptor->GetClassName().GetCString(); + + if (!class_name || !*class_name) + return false; + + uint64_t port_number = 0; + + do + { + if (!strcmp(class_name,"NSMachPort")) + { + uint64_t offset = (ptr_size == 4 ? 12 : 20); + Error error; + port_number = process_sp->ReadUnsignedIntegerFromMemory(offset+valobj_addr, 4, 0, error); + if (error.Success()) + break; + } + if (!ExtractValueFromObjCExpression(valobj, "int", "machPort", port_number)) + return false; + } while (false); + + stream.Printf("mach port: %u",(uint32_t)(port_number & 0x00000000FFFFFFFF)); + return true; +} + +bool +lldb_private::formatters::NSIndexSetSummaryProvider (ValueObject& valobj, Stream& stream) +{ + ProcessSP process_sp = valobj.GetProcessSP(); + if (!process_sp) + return false; + + ObjCLanguageRuntime* runtime = (ObjCLanguageRuntime*)process_sp->GetLanguageRuntime(lldb::eLanguageTypeObjC); + + if (!runtime) + return false; + + ObjCLanguageRuntime::ClassDescriptorSP descriptor(runtime->GetClassDescriptor(valobj)); + + if (!descriptor.get() || !descriptor->IsValid()) + return false; + + uint32_t ptr_size = process_sp->GetAddressByteSize(); + + lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0); + + if (!valobj_addr) + return false; + + const char* class_name = descriptor->GetClassName().GetCString(); + + if (!class_name || !*class_name) + return false; + + uint64_t count = 0; + + do { + if (!strcmp(class_name,"NSIndexSet") || !strcmp(class_name,"NSMutableIndexSet")) + { + Error error; + uint32_t mode = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr+ptr_size, 4, 0, error); + if (error.Fail()) + return false; + // this means the set is empty - count = 0 + if ((mode & 1) == 1) + { + count = 0; + break; + } + if ((mode & 2) == 2) + mode = 1; // this means the set only has one range + else + mode = 2; // this means the set has multiple ranges + if (mode == 1) + { + count = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr+3*ptr_size, ptr_size, 0, error); + if (error.Fail()) + return false; + } + else + { + // read a pointer to the data at 2*ptr_size + count = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr+2*ptr_size, ptr_size, 0, error); + if (error.Fail()) + return false; + // read the data at 2*ptr_size from the first location + count = process_sp->ReadUnsignedIntegerFromMemory(count+2*ptr_size, ptr_size, 0, error); + if (error.Fail()) + return false; + } + } + else + { + if (!ExtractValueFromObjCExpression(valobj, "unsigned long long int", "count", count)) + return false; + } + } while (false); + stream.Printf("%" PRIu64 " index%s", + count, + (count == 1 ? "" : "es")); + return true; +} + +bool +lldb_private::formatters::NSNumberSummaryProvider (ValueObject& valobj, Stream& stream) +{ + ProcessSP process_sp = valobj.GetProcessSP(); + if (!process_sp) + return false; + + ObjCLanguageRuntime* runtime = (ObjCLanguageRuntime*)process_sp->GetLanguageRuntime(lldb::eLanguageTypeObjC); + + if (!runtime) + return false; + + ObjCLanguageRuntime::ClassDescriptorSP descriptor(runtime->GetClassDescriptor(valobj)); + + if (!descriptor.get() || !descriptor->IsValid()) + return false; + + uint32_t ptr_size = process_sp->GetAddressByteSize(); + + lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0); + + if (!valobj_addr) + return false; + + const char* class_name = descriptor->GetClassName().GetCString(); + + if (!class_name || !*class_name) + return false; + + if (!strcmp(class_name,"NSNumber") || !strcmp(class_name,"__NSCFNumber")) + { + uint64_t value = 0; + uint64_t i_bits = 0; + if (descriptor->GetTaggedPointerInfo(&i_bits,&value)) + { + switch (i_bits) + { + case 0: + stream.Printf("(char)%hhd",(char)value); + break; + case 1: + case 4: + stream.Printf("(short)%hd",(short)value); + break; + case 2: + case 8: + stream.Printf("(int)%d",(int)value); + break; + case 3: + case 12: + stream.Printf("(long)%" PRId64,value); + break; + default: + stream.Printf("unexpected value:(info=%" PRIu64 ", value=%" PRIu64,i_bits,value); + break; + } + return true; + } + else + { + Error error; + uint8_t data_type = (process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + ptr_size, 1, 0, error) & 0x1F); + uint64_t data_location = valobj_addr + 2*ptr_size; + uint64_t value = 0; + if (error.Fail()) + return false; + switch (data_type) + { + case 1: // 0B00001 + value = process_sp->ReadUnsignedIntegerFromMemory(data_location, 1, 0, error); + if (error.Fail()) + return false; + stream.Printf("(char)%hhd",(char)value); + break; + case 2: // 0B0010 + value = process_sp->ReadUnsignedIntegerFromMemory(data_location, 2, 0, error); + if (error.Fail()) + return false; + stream.Printf("(short)%hd",(short)value); + break; + case 3: // 0B0011 + value = process_sp->ReadUnsignedIntegerFromMemory(data_location, 4, 0, error); + if (error.Fail()) + return false; + stream.Printf("(int)%d",(int)value); + break; + case 17: // 0B10001 + data_location += 8; + case 4: // 0B0100 + value = process_sp->ReadUnsignedIntegerFromMemory(data_location, 8, 0, error); + if (error.Fail()) + return false; + stream.Printf("(long)%" PRId64,value); + break; + case 5: // 0B0101 + { + uint32_t flt_as_int = process_sp->ReadUnsignedIntegerFromMemory(data_location, 4, 0, error); + if (error.Fail()) + return false; + float flt_value = *((float*)&flt_as_int); + stream.Printf("(float)%f",flt_value); + break; + } + case 6: // 0B0110 + { + uint64_t dbl_as_lng = process_sp->ReadUnsignedIntegerFromMemory(data_location, 8, 0, error); + if (error.Fail()) + return false; + double dbl_value = *((double*)&dbl_as_lng); + stream.Printf("(double)%g",dbl_value); + break; + } + default: + stream.Printf("unexpected value: dt=%d",data_type); + break; + } + return true; + } + } + else + { + return ExtractSummaryFromObjCExpression(valobj, "NSString*", "stringValue", stream); + } +} + +bool +lldb_private::formatters::NSURLSummaryProvider (ValueObject& valobj, Stream& stream) +{ + ProcessSP process_sp = valobj.GetProcessSP(); + if (!process_sp) + return false; + + ObjCLanguageRuntime* runtime = (ObjCLanguageRuntime*)process_sp->GetLanguageRuntime(lldb::eLanguageTypeObjC); + + if (!runtime) + return false; + + ObjCLanguageRuntime::ClassDescriptorSP descriptor(runtime->GetClassDescriptor(valobj)); + + if (!descriptor.get() || !descriptor->IsValid()) + return false; + + uint32_t ptr_size = process_sp->GetAddressByteSize(); + + lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0); + + if (!valobj_addr) + return false; + + const char* class_name = descriptor->GetClassName().GetCString(); + + if (!class_name || !*class_name) + return false; + + if (strcmp(class_name, "NSURL") == 0) + { + uint64_t offset_text = ptr_size + ptr_size + 8; // ISA + pointer + 8 bytes of data (even on 32bit) + uint64_t offset_base = offset_text + ptr_size; + ClangASTType type(valobj.GetClangType()); + ValueObjectSP text(valobj.GetSyntheticChildAtOffset(offset_text, type, true)); + ValueObjectSP base(valobj.GetSyntheticChildAtOffset(offset_base, type, true)); + if (!text) + return false; + if (text->GetValueAsUnsigned(0) == 0) + return false; + StreamString summary; + if (!NSStringSummaryProvider(*text, summary)) + return false; + if (base && base->GetValueAsUnsigned(0)) + { + if (summary.GetSize() > 0) + summary.GetString().resize(summary.GetSize()-1); + summary.Printf(" -- "); + StreamString base_summary; + if (NSURLSummaryProvider(*base, base_summary) && base_summary.GetSize() > 0) + summary.Printf("%s",base_summary.GetSize() > 2 ? base_summary.GetData() + 2 : base_summary.GetData()); + } + if (summary.GetSize()) + { + stream.Printf("%s",summary.GetData()); + return true; + } + } + else + { + return ExtractSummaryFromObjCExpression(valobj, "NSString*", "description", stream); + } + return false; +} + +bool +lldb_private::formatters::NSDateSummaryProvider (ValueObject& valobj, Stream& stream) +{ + ProcessSP process_sp = valobj.GetProcessSP(); + if (!process_sp) + return false; + + ObjCLanguageRuntime* runtime = (ObjCLanguageRuntime*)process_sp->GetLanguageRuntime(lldb::eLanguageTypeObjC); + + if (!runtime) + return false; + + ObjCLanguageRuntime::ClassDescriptorSP descriptor(runtime->GetClassDescriptor(valobj)); + + if (!descriptor.get() || !descriptor->IsValid()) + return false; + + uint32_t ptr_size = process_sp->GetAddressByteSize(); + + lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0); + + if (!valobj_addr) + return false; + + uint64_t date_value_bits = 0; + double date_value = 0.0; + + const char* class_name = descriptor->GetClassName().GetCString(); + + if (!class_name || !*class_name) + return false; + + if (strcmp(class_name,"NSDate") == 0 || + strcmp(class_name,"__NSDate") == 0 || + strcmp(class_name,"__NSTaggedDate") == 0) + { + uint64_t info_bits=0,value_bits = 0; + if (descriptor->GetTaggedPointerInfo(&info_bits,&value_bits)) + { + date_value_bits = ((value_bits << 8) | (info_bits << 4)); + date_value = *((double*)&date_value_bits); + } + else + { + Error error; + date_value_bits = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr+ptr_size, 8, 0, error); + date_value = *((double*)&date_value_bits); + if (error.Fail()) + return false; + } + } + else if (!strcmp(class_name,"NSCalendarDate")) + { + Error error; + date_value_bits = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr+2*ptr_size, 8, 0, error); + date_value = *((double*)&date_value_bits); + if (error.Fail()) + return false; + } + else + { + if (ExtractValueFromObjCExpression(valobj, "NSTimeInterval", "ExtractValueFromObjCExpression", date_value_bits) == false) + return false; + date_value = *((double*)&date_value_bits); + } + if (date_value == -63114076800) + { + stream.Printf("0001-12-30 00:00:00 +0000"); + return true; + } + // this snippet of code assumes that time_t == seconds since Jan-1-1970 + // this is generally true and POSIXly happy, but might break if a library + // vendor decides to get creative + time_t epoch = GetOSXEpoch(); + epoch = epoch + (time_t)date_value; + tm *tm_date = localtime(&epoch); + if (!tm_date) + return false; + std::string buffer(1024,0); + if (strftime (&buffer[0], 1023, "%Z", tm_date) == 0) + return false; + stream.Printf("%04d-%02d-%02d %02d:%02d:%02d %s", tm_date->tm_year+1900, tm_date->tm_mon+1, tm_date->tm_mday, tm_date->tm_hour, tm_date->tm_min, tm_date->tm_sec, buffer.c_str()); + return true; +} diff --git a/contrib/llvm/tools/lldb/source/DataFormatters/DataVisualization.cpp b/contrib/llvm/tools/lldb/source/DataFormatters/DataVisualization.cpp new file mode 100644 index 00000000000..c1ef359049b --- /dev/null +++ b/contrib/llvm/tools/lldb/source/DataFormatters/DataVisualization.cpp @@ -0,0 +1,279 @@ +//===-- DataVisualization.cpp ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "lldb/DataFormatters/DataVisualization.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes + +#include "lldb/Core/Debugger.h" + +using namespace lldb; +using namespace lldb_private; + +static FormatManager& +GetFormatManager() +{ + static FormatManager g_format_manager; + return g_format_manager; +} + +void +DataVisualization::ForceUpdate () +{ + GetFormatManager().Changed(); +} + +uint32_t +DataVisualization::GetCurrentRevision () +{ + return GetFormatManager().GetCurrentRevision(); +} + +lldb::TypeFormatImplSP +DataVisualization::ValueFormats::GetFormat (ValueObject& valobj, lldb::DynamicValueType use_dynamic) +{ + lldb::TypeFormatImplSP entry; + GetFormatManager().GetValueNavigator().Get(valobj, entry, use_dynamic); + return entry; +} + +lldb::TypeFormatImplSP +DataVisualization::ValueFormats::GetFormat (const ConstString &type) +{ + lldb::TypeFormatImplSP entry; + GetFormatManager().GetValueNavigator().Get(type, entry); + return entry; +} + +void +DataVisualization::ValueFormats::Add (const ConstString &type, const lldb::TypeFormatImplSP &entry) +{ + GetFormatManager().GetValueNavigator().Add(FormatManager::GetValidTypeName(type),entry); +} + +bool +DataVisualization::ValueFormats::Delete (const ConstString &type) +{ + return GetFormatManager().GetValueNavigator().Delete(type); +} + +void +DataVisualization::ValueFormats::Clear () +{ + GetFormatManager().GetValueNavigator().Clear(); +} + +void +DataVisualization::ValueFormats::LoopThrough (TypeFormatImpl::ValueCallback callback, void* callback_baton) +{ + GetFormatManager().GetValueNavigator().LoopThrough(callback, callback_baton); +} + +size_t +DataVisualization::ValueFormats::GetCount () +{ + return GetFormatManager().GetValueNavigator().GetCount(); +} + +lldb::TypeNameSpecifierImplSP +DataVisualization::ValueFormats::GetTypeNameSpecifierForFormatAtIndex (size_t index) +{ + return GetFormatManager().GetValueNavigator().GetTypeNameSpecifierAtIndex(index); +} + +lldb::TypeFormatImplSP +DataVisualization::ValueFormats::GetFormatAtIndex (size_t index) +{ + return GetFormatManager().GetValueNavigator().GetAtIndex(index); +} + +lldb::TypeSummaryImplSP +DataVisualization::GetSummaryFormat (ValueObject& valobj, + lldb::DynamicValueType use_dynamic) +{ + return GetFormatManager().GetSummaryFormat(valobj, use_dynamic); +} + +lldb::TypeSummaryImplSP +DataVisualization::GetSummaryForType (lldb::TypeNameSpecifierImplSP type_sp) +{ + return GetFormatManager().GetSummaryForType(type_sp); +} + +#ifndef LLDB_DISABLE_PYTHON +lldb::SyntheticChildrenSP +DataVisualization::GetSyntheticChildren (ValueObject& valobj, + lldb::DynamicValueType use_dynamic) +{ + return GetFormatManager().GetSyntheticChildren(valobj, use_dynamic); +} +#endif + +#ifndef LLDB_DISABLE_PYTHON +lldb::SyntheticChildrenSP +DataVisualization::GetSyntheticChildrenForType (lldb::TypeNameSpecifierImplSP type_sp) +{ + return GetFormatManager().GetSyntheticChildrenForType(type_sp); +} +#endif + +lldb::TypeFilterImplSP +DataVisualization::GetFilterForType (lldb::TypeNameSpecifierImplSP type_sp) +{ + return GetFormatManager().GetFilterForType(type_sp); +} + +#ifndef LLDB_DISABLE_PYTHON +lldb::ScriptedSyntheticChildrenSP +DataVisualization::GetSyntheticForType (lldb::TypeNameSpecifierImplSP type_sp) +{ + return GetFormatManager().GetSyntheticForType(type_sp); +} +#endif + +bool +DataVisualization::AnyMatches (ConstString type_name, + TypeCategoryImpl::FormatCategoryItems items, + bool only_enabled, + const char** matching_category, + TypeCategoryImpl::FormatCategoryItems* matching_type) +{ + return GetFormatManager().AnyMatches(type_name, + items, + only_enabled, + matching_category, + matching_type); +} + +bool +DataVisualization::Categories::GetCategory (const ConstString &category, lldb::TypeCategoryImplSP &entry, + bool allow_create) +{ + entry = GetFormatManager().GetCategory(category, allow_create); + return (entry.get() != NULL); +} + +void +DataVisualization::Categories::Add (const ConstString &category) +{ + GetFormatManager().GetCategory(category); +} + +bool +DataVisualization::Categories::Delete (const ConstString &category) +{ + GetFormatManager().DisableCategory(category); + return GetFormatManager().DeleteCategory(category); +} + +void +DataVisualization::Categories::Clear () +{ + GetFormatManager().ClearCategories(); +} + +void +DataVisualization::Categories::Clear (const ConstString &category) +{ + GetFormatManager().GetCategory(category)->Clear(eFormatCategoryItemSummary | eFormatCategoryItemRegexSummary); +} + +void +DataVisualization::Categories::Enable (const ConstString& category, + TypeCategoryMap::Position pos) +{ + if (GetFormatManager().GetCategory(category)->IsEnabled()) + GetFormatManager().DisableCategory(category); + GetFormatManager().EnableCategory(category, pos); +} + +void +DataVisualization::Categories::Disable (const ConstString& category) +{ + if (GetFormatManager().GetCategory(category)->IsEnabled() == true) + GetFormatManager().DisableCategory(category); +} + +void +DataVisualization::Categories::Enable (const lldb::TypeCategoryImplSP& category, + TypeCategoryMap::Position pos) +{ + if (category.get()) + { + if (category->IsEnabled()) + GetFormatManager().DisableCategory(category); + GetFormatManager().EnableCategory(category, pos); + } +} + +void +DataVisualization::Categories::Disable (const lldb::TypeCategoryImplSP& category) +{ + if (category.get() && category->IsEnabled() == true) + GetFormatManager().DisableCategory(category); +} + +void +DataVisualization::Categories::LoopThrough (FormatManager::CategoryCallback callback, void* callback_baton) +{ + GetFormatManager().LoopThroughCategories(callback, callback_baton); +} + +uint32_t +DataVisualization::Categories::GetCount () +{ + return GetFormatManager().GetCategoriesCount(); +} + +lldb::TypeCategoryImplSP +DataVisualization::Categories::GetCategoryAtIndex (size_t index) +{ + return GetFormatManager().GetCategoryAtIndex(index); +} + +bool +DataVisualization::NamedSummaryFormats::GetSummaryFormat (const ConstString &type, lldb::TypeSummaryImplSP &entry) +{ + return GetFormatManager().GetNamedSummaryNavigator().Get(type,entry); +} + +void +DataVisualization::NamedSummaryFormats::Add (const ConstString &type, const lldb::TypeSummaryImplSP &entry) +{ + GetFormatManager().GetNamedSummaryNavigator().Add(FormatManager::GetValidTypeName(type),entry); +} + +bool +DataVisualization::NamedSummaryFormats::Delete (const ConstString &type) +{ + return GetFormatManager().GetNamedSummaryNavigator().Delete(type); +} + +void +DataVisualization::NamedSummaryFormats::Clear () +{ + GetFormatManager().GetNamedSummaryNavigator().Clear(); +} + +void +DataVisualization::NamedSummaryFormats::LoopThrough (TypeSummaryImpl::SummaryCallback callback, void* callback_baton) +{ + GetFormatManager().GetNamedSummaryNavigator().LoopThrough(callback, callback_baton); +} + +uint32_t +DataVisualization::NamedSummaryFormats::GetCount () +{ + return GetFormatManager().GetNamedSummaryNavigator().GetCount(); +} diff --git a/contrib/llvm/tools/lldb/source/DataFormatters/FormatCache.cpp b/contrib/llvm/tools/lldb/source/DataFormatters/FormatCache.cpp new file mode 100644 index 00000000000..af7b1c386c3 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/DataFormatters/FormatCache.cpp @@ -0,0 +1,169 @@ +//===-- FormatCache.cpp ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +// C Includes + +// C++ Includes + +// Other libraries and framework includes + +// Project includes +#include "lldb/DataFormatters/FormatCache.h" + +using namespace lldb; +using namespace lldb_private; + +FormatCache::Entry::Entry () : +m_summary_cached(false), +m_synthetic_cached(false), +m_summary_sp(), +m_synthetic_sp() +{} + +FormatCache::Entry::Entry (lldb::TypeSummaryImplSP summary_sp) : +m_synthetic_cached(false), +m_synthetic_sp() +{ + SetSummary (summary_sp); +} + +FormatCache::Entry::Entry (lldb::SyntheticChildrenSP synthetic_sp) : +m_summary_cached(false), +m_summary_sp() +{ + SetSynthetic (synthetic_sp); +} + +FormatCache::Entry::Entry (lldb::TypeSummaryImplSP summary_sp,lldb::SyntheticChildrenSP synthetic_sp) +{ + SetSummary (summary_sp); + SetSynthetic (synthetic_sp); +} + +bool +FormatCache::Entry::IsSummaryCached () +{ + return m_summary_cached; +} + +bool +FormatCache::Entry::IsSyntheticCached () +{ + return m_synthetic_cached; +} + +lldb::TypeSummaryImplSP +FormatCache::Entry::GetSummary () +{ + return m_summary_sp; +} + +lldb::SyntheticChildrenSP +FormatCache::Entry::GetSynthetic () +{ + return m_synthetic_sp; +} + +void +FormatCache::Entry::SetSummary (lldb::TypeSummaryImplSP summary_sp) +{ + m_summary_cached = true; + m_summary_sp = summary_sp; +} + +void +FormatCache::Entry::SetSynthetic (lldb::SyntheticChildrenSP synthetic_sp) +{ + m_synthetic_cached = true; + m_synthetic_sp = synthetic_sp; +} + +FormatCache::FormatCache () : +m_map(), +m_mutex (Mutex::eMutexTypeRecursive) +#ifdef LLDB_CONFIGURATION_DEBUG +,m_cache_hits(0),m_cache_misses(0) +#endif +{ +} + +FormatCache::Entry& +FormatCache::GetEntry (const ConstString& type) +{ + auto i = m_map.find(type), + e = m_map.end(); + if (i != e) + return i->second; + m_map[type] = FormatCache::Entry(); + return m_map[type]; +} + +bool +FormatCache::GetSummary (const ConstString& type,lldb::TypeSummaryImplSP& summary_sp) +{ + Mutex::Locker lock(m_mutex); + auto entry = GetEntry(type); + if (entry.IsSummaryCached()) + { +#ifdef LLDB_CONFIGURATION_DEBUG + m_cache_hits++; +#endif + summary_sp = entry.GetSummary(); + return true; + } +#ifdef LLDB_CONFIGURATION_DEBUG + m_cache_misses++; +#endif + summary_sp.reset(); + return false; +} + +bool +FormatCache::GetSynthetic (const ConstString& type,lldb::SyntheticChildrenSP& synthetic_sp) +{ + Mutex::Locker lock(m_mutex); + auto entry = GetEntry(type); + if (entry.IsSyntheticCached()) + { +#ifdef LLDB_CONFIGURATION_DEBUG + m_cache_hits++; +#endif + synthetic_sp = entry.GetSynthetic(); + return true; + } +#ifdef LLDB_CONFIGURATION_DEBUG + m_cache_misses++; +#endif + synthetic_sp.reset(); + return false; +} + +void +FormatCache::SetSummary (const ConstString& type,lldb::TypeSummaryImplSP& summary_sp) +{ + Mutex::Locker lock(m_mutex); + GetEntry(type).SetSummary(summary_sp); +} + +void +FormatCache::SetSynthetic (const ConstString& type,lldb::SyntheticChildrenSP& synthetic_sp) +{ + Mutex::Locker lock(m_mutex); + GetEntry(type).SetSynthetic(synthetic_sp); +} + +void +FormatCache::Clear () +{ + Mutex::Locker lock(m_mutex); + m_map.clear(); +} + diff --git a/contrib/llvm/tools/lldb/source/DataFormatters/FormatClasses.cpp b/contrib/llvm/tools/lldb/source/DataFormatters/FormatClasses.cpp new file mode 100644 index 00000000000..c67f86a7493 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/DataFormatters/FormatClasses.cpp @@ -0,0 +1,33 @@ +//===-- FormatClasses.cpp ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +// C Includes + +// C++ Includes + +// Other libraries and framework includes + +// Project includes +#include "lldb/lldb-public.h" +#include "lldb/lldb-enumerations.h" + +#include "lldb/Core/Debugger.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Core/Timer.h" +#include "lldb/DataFormatters/FormatClasses.h" +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Symbol/ClangASTType.h" +#include "lldb/Target/StackFrame.h" +#include "lldb/Target/Target.h" + +using namespace lldb; +using namespace lldb_private; + diff --git a/contrib/llvm/tools/lldb/source/DataFormatters/FormatManager.cpp b/contrib/llvm/tools/lldb/source/DataFormatters/FormatManager.cpp new file mode 100644 index 00000000000..eeae8bc9a19 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/DataFormatters/FormatManager.cpp @@ -0,0 +1,1083 @@ +//===-- FormatManager.cpp -------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "lldb/DataFormatters/FormatManager.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes + +#include "lldb/Core/Debugger.h" +#include "lldb/DataFormatters/CXXFormatterFunctions.h" +#include "lldb/Interpreter/ScriptInterpreterPython.h" +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/Platform.h" + +using namespace lldb; +using namespace lldb_private; + + +struct FormatInfo +{ + Format format; + const char format_char; // One or more format characters that can be used for this format. + const char *format_name; // Long format name that can be used to specify the current format +}; + +static FormatInfo +g_format_infos[] = +{ + { eFormatDefault , '\0' , "default" }, + { eFormatBoolean , 'B' , "boolean" }, + { eFormatBinary , 'b' , "binary" }, + { eFormatBytes , 'y' , "bytes" }, + { eFormatBytesWithASCII , 'Y' , "bytes with ASCII" }, + { eFormatChar , 'c' , "character" }, + { eFormatCharPrintable , 'C' , "printable character" }, + { eFormatComplexFloat , 'F' , "complex float" }, + { eFormatCString , 's' , "c-string" }, + { eFormatDecimal , 'd' , "decimal" }, + { eFormatEnum , 'E' , "enumeration" }, + { eFormatHex , 'x' , "hex" }, + { eFormatHexUppercase , 'X' , "uppercase hex" }, + { eFormatFloat , 'f' , "float" }, + { eFormatOctal , 'o' , "octal" }, + { eFormatOSType , 'O' , "OSType" }, + { eFormatUnicode16 , 'U' , "unicode16" }, + { eFormatUnicode32 , '\0' , "unicode32" }, + { eFormatUnsigned , 'u' , "unsigned decimal" }, + { eFormatPointer , 'p' , "pointer" }, + { eFormatVectorOfChar , '\0' , "char[]" }, + { eFormatVectorOfSInt8 , '\0' , "int8_t[]" }, + { eFormatVectorOfUInt8 , '\0' , "uint8_t[]" }, + { eFormatVectorOfSInt16 , '\0' , "int16_t[]" }, + { eFormatVectorOfUInt16 , '\0' , "uint16_t[]" }, + { eFormatVectorOfSInt32 , '\0' , "int32_t[]" }, + { eFormatVectorOfUInt32 , '\0' , "uint32_t[]" }, + { eFormatVectorOfSInt64 , '\0' , "int64_t[]" }, + { eFormatVectorOfUInt64 , '\0' , "uint64_t[]" }, + { eFormatVectorOfFloat32, '\0' , "float32[]" }, + { eFormatVectorOfFloat64, '\0' , "float64[]" }, + { eFormatVectorOfUInt128, '\0' , "uint128_t[]" }, + { eFormatComplexInteger , 'I' , "complex integer" }, + { eFormatCharArray , 'a' , "character array" }, + { eFormatAddressInfo , 'A' , "address" }, + { eFormatHexFloat , '\0' , "hex float" }, + { eFormatInstruction , 'i' , "instruction" }, + { eFormatVoid , 'v' , "void" } +}; + +static uint32_t +g_num_format_infos = sizeof(g_format_infos)/sizeof(FormatInfo); + +static bool +GetFormatFromFormatChar (char format_char, Format &format) +{ + for (uint32_t i=0; i= eFormatDefault && format < kNumFormats) + return g_format_infos[format].format_name; + return NULL; +} + +lldb::TypeSummaryImplSP +FormatManager::GetSummaryForType (lldb::TypeNameSpecifierImplSP type_sp) +{ + if (!type_sp) + return lldb::TypeSummaryImplSP(); + lldb::TypeSummaryImplSP summary_chosen_sp; + uint32_t num_categories = m_categories_map.GetCount(); + lldb::TypeCategoryImplSP category_sp; + uint32_t prio_category = UINT32_MAX; + for (uint32_t category_id = 0; + category_id < num_categories; + category_id++) + { + category_sp = GetCategoryAtIndex(category_id); + if (category_sp->IsEnabled() == false) + continue; + lldb::TypeSummaryImplSP summary_current_sp = category_sp->GetSummaryForType(type_sp); + if (summary_current_sp && (summary_chosen_sp.get() == NULL || (prio_category > category_sp->GetEnabledPosition()))) + { + prio_category = category_sp->GetEnabledPosition(); + summary_chosen_sp = summary_current_sp; + } + } + return summary_chosen_sp; +} + +lldb::TypeFilterImplSP +FormatManager::GetFilterForType (lldb::TypeNameSpecifierImplSP type_sp) +{ + if (!type_sp) + return lldb::TypeFilterImplSP(); + lldb::TypeFilterImplSP filter_chosen_sp; + uint32_t num_categories = m_categories_map.GetCount(); + lldb::TypeCategoryImplSP category_sp; + uint32_t prio_category = UINT32_MAX; + for (uint32_t category_id = 0; + category_id < num_categories; + category_id++) + { + category_sp = GetCategoryAtIndex(category_id); + if (category_sp->IsEnabled() == false) + continue; + lldb::TypeFilterImplSP filter_current_sp((TypeFilterImpl*)category_sp->GetFilterForType(type_sp).get()); + if (filter_current_sp && (filter_chosen_sp.get() == NULL || (prio_category > category_sp->GetEnabledPosition()))) + { + prio_category = category_sp->GetEnabledPosition(); + filter_chosen_sp = filter_current_sp; + } + } + return filter_chosen_sp; +} + +#ifndef LLDB_DISABLE_PYTHON +lldb::ScriptedSyntheticChildrenSP +FormatManager::GetSyntheticForType (lldb::TypeNameSpecifierImplSP type_sp) +{ + if (!type_sp) + return lldb::ScriptedSyntheticChildrenSP(); + lldb::ScriptedSyntheticChildrenSP synth_chosen_sp; + uint32_t num_categories = m_categories_map.GetCount(); + lldb::TypeCategoryImplSP category_sp; + uint32_t prio_category = UINT32_MAX; + for (uint32_t category_id = 0; + category_id < num_categories; + category_id++) + { + category_sp = GetCategoryAtIndex(category_id); + if (category_sp->IsEnabled() == false) + continue; + lldb::ScriptedSyntheticChildrenSP synth_current_sp((ScriptedSyntheticChildren*)category_sp->GetSyntheticForType(type_sp).get()); + if (synth_current_sp && (synth_chosen_sp.get() == NULL || (prio_category > category_sp->GetEnabledPosition()))) + { + prio_category = category_sp->GetEnabledPosition(); + synth_chosen_sp = synth_current_sp; + } + } + return synth_chosen_sp; +} +#endif + +#ifndef LLDB_DISABLE_PYTHON +lldb::SyntheticChildrenSP +FormatManager::GetSyntheticChildrenForType (lldb::TypeNameSpecifierImplSP type_sp) +{ + if (!type_sp) + return lldb::SyntheticChildrenSP(); + lldb::TypeFilterImplSP filter_sp = GetFilterForType(type_sp); + lldb::ScriptedSyntheticChildrenSP synth_sp = GetSyntheticForType(type_sp); + if (filter_sp->GetRevision() > synth_sp->GetRevision()) + return lldb::SyntheticChildrenSP(filter_sp.get()); + else + return lldb::SyntheticChildrenSP(synth_sp.get()); +} +#endif + +lldb::TypeCategoryImplSP +FormatManager::GetCategory (const ConstString& category_name, + bool can_create) +{ + if (!category_name) + return GetCategory(m_default_category_name); + lldb::TypeCategoryImplSP category; + if (m_categories_map.Get(category_name, category)) + return category; + + if (!can_create) + return lldb::TypeCategoryImplSP(); + + m_categories_map.Add(category_name,lldb::TypeCategoryImplSP(new TypeCategoryImpl(this, category_name))); + return GetCategory(category_name); +} + +lldb::Format +FormatManager::GetSingleItemFormat(lldb::Format vector_format) +{ + switch(vector_format) + { + case eFormatVectorOfChar: + return eFormatCharArray; + + case eFormatVectorOfSInt8: + case eFormatVectorOfSInt16: + case eFormatVectorOfSInt32: + case eFormatVectorOfSInt64: + return eFormatDecimal; + + case eFormatVectorOfUInt8: + case eFormatVectorOfUInt16: + case eFormatVectorOfUInt32: + case eFormatVectorOfUInt64: + case eFormatVectorOfUInt128: + return eFormatHex; + + case eFormatVectorOfFloat32: + case eFormatVectorOfFloat64: + return eFormatFloat; + + default: + return lldb::eFormatInvalid; + } +} + +ConstString +FormatManager::GetValidTypeName (const ConstString& type) +{ + return ::GetValidTypeName_Impl(type); +} + +ConstString +GetTypeForCache (ValueObject& valobj, + lldb::DynamicValueType use_dynamic) +{ + if (use_dynamic == lldb::eNoDynamicValues) + { + if (valobj.IsDynamic()) + { + if (valobj.GetStaticValue()) + return valobj.GetStaticValue()->GetQualifiedTypeName(); + else + return ConstString(); + } + else + return valobj.GetQualifiedTypeName(); + } + if (valobj.IsDynamic()) + return valobj.GetQualifiedTypeName(); + if (valobj.GetDynamicValue(use_dynamic)) + return valobj.GetDynamicValue(use_dynamic)->GetQualifiedTypeName(); + return ConstString(); +} + +lldb::TypeSummaryImplSP +FormatManager::GetSummaryFormat (ValueObject& valobj, + lldb::DynamicValueType use_dynamic) +{ + TypeSummaryImplSP retval; + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_TYPES)); + ConstString valobj_type(GetTypeForCache(valobj, use_dynamic)); + if (valobj_type) + { + if (log) + log->Printf("\n\n[FormatManager::GetSummaryFormat] Looking into cache for type %s", valobj_type.AsCString("")); + if (m_format_cache.GetSummary(valobj_type,retval)) + { + if (log) + { + log->Printf("[FormatManager::GetSummaryFormat] Cache search success. Returning."); + if (log->GetDebug()) + log->Printf("[FormatManager::GetSummaryFormat] Cache hits: %" PRIu64 " - Cache Misses: %" PRIu64, m_format_cache.GetCacheHits(), m_format_cache.GetCacheMisses()); + } + return retval; + } + if (log) + log->Printf("[FormatManager::GetSummaryFormat] Cache search failed. Going normal route"); + } + retval = m_categories_map.GetSummaryFormat(valobj, use_dynamic); + if (valobj_type) + { + if (log) + log->Printf("[FormatManager::GetSummaryFormat] Caching %p for type %s",retval.get(),valobj_type.AsCString("")); + m_format_cache.SetSummary(valobj_type,retval); + } + if (log && log->GetDebug()) + log->Printf("[FormatManager::GetSummaryFormat] Cache hits: %" PRIu64 " - Cache Misses: %" PRIu64, m_format_cache.GetCacheHits(), m_format_cache.GetCacheMisses()); + return retval; +} + +#ifndef LLDB_DISABLE_PYTHON +lldb::SyntheticChildrenSP +FormatManager::GetSyntheticChildren (ValueObject& valobj, + lldb::DynamicValueType use_dynamic) +{ + SyntheticChildrenSP retval; + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_TYPES)); + ConstString valobj_type(GetTypeForCache(valobj, use_dynamic)); + if (valobj_type) + { + if (log) + log->Printf("\n\n[FormatManager::GetSyntheticChildren] Looking into cache for type %s", valobj_type.AsCString("")); + if (m_format_cache.GetSynthetic(valobj_type,retval)) + { + if (log) + { + log->Printf("[FormatManager::GetSyntheticChildren] Cache search success. Returning."); + if (log->GetDebug()) + log->Printf("[FormatManager::GetSyntheticChildren] Cache hits: %" PRIu64 " - Cache Misses: %" PRIu64, m_format_cache.GetCacheHits(), m_format_cache.GetCacheMisses()); + } + return retval; + } + if (log) + log->Printf("[FormatManager::GetSyntheticChildren] Cache search failed. Going normal route"); + } + retval = m_categories_map.GetSyntheticChildren(valobj, use_dynamic); + if (valobj_type) + { + if (log) + log->Printf("[FormatManager::GetSyntheticChildren] Caching %p for type %s",retval.get(),valobj_type.AsCString("")); + m_format_cache.SetSynthetic(valobj_type,retval); + } + if (log && log->GetDebug()) + log->Printf("[FormatManager::GetSyntheticChildren] Cache hits: %" PRIu64 " - Cache Misses: %" PRIu64, m_format_cache.GetCacheHits(), m_format_cache.GetCacheMisses()); + return retval; +} +#endif + +FormatManager::FormatManager() : + m_format_cache(), + m_value_nav("format",this), + m_named_summaries_map(this), + m_last_revision(0), + m_categories_map(this), + m_default_category_name(ConstString("default")), + m_system_category_name(ConstString("system")), + m_gnu_cpp_category_name(ConstString("gnu-libstdc++")), + m_libcxx_category_name(ConstString("libcxx")), + m_objc_category_name(ConstString("objc")), + m_corefoundation_category_name(ConstString("CoreFoundation")), + m_coregraphics_category_name(ConstString("CoreGraphics")), + m_coreservices_category_name(ConstString("CoreServices")), + m_vectortypes_category_name(ConstString("VectorTypes")), + m_appkit_category_name(ConstString("AppKit")) +{ + LoadSystemFormatters(); + LoadLibStdcppFormatters(); + LoadLibcxxFormatters(); + LoadObjCFormatters(); + + EnableCategory(m_objc_category_name,TypeCategoryMap::Last); + EnableCategory(m_corefoundation_category_name,TypeCategoryMap::Last); + EnableCategory(m_appkit_category_name,TypeCategoryMap::Last); + EnableCategory(m_coreservices_category_name,TypeCategoryMap::Last); + EnableCategory(m_coregraphics_category_name,TypeCategoryMap::Last); + EnableCategory(m_gnu_cpp_category_name,TypeCategoryMap::Last); + EnableCategory(m_libcxx_category_name,TypeCategoryMap::Last); + EnableCategory(m_vectortypes_category_name,TypeCategoryMap::Last); + EnableCategory(m_system_category_name,TypeCategoryMap::Last); +} + +static void +AddStringSummary(TypeCategoryImpl::SharedPointer category_sp, + const char* string, + ConstString type_name, + TypeSummaryImpl::Flags flags, + bool regex = false) +{ + lldb::TypeSummaryImplSP summary_sp(new StringSummaryFormat(flags, + string)); + + if (regex) + category_sp->GetRegexSummaryNavigator()->Add(RegularExpressionSP(new RegularExpression(type_name.AsCString())),summary_sp); + else + category_sp->GetSummaryNavigator()->Add(type_name, summary_sp); +} + +#ifndef LLDB_DISABLE_PYTHON +static void +AddScriptSummary(TypeCategoryImpl::SharedPointer category_sp, + const char* funct_name, + ConstString type_name, + TypeSummaryImpl::Flags flags, + bool regex = false) +{ + + std::string code(" "); + code.append(funct_name).append("(valobj,internal_dict)"); + + lldb::TypeSummaryImplSP summary_sp(new ScriptSummaryFormat(flags, + funct_name, + code.c_str())); + if (regex) + category_sp->GetRegexSummaryNavigator()->Add(RegularExpressionSP(new RegularExpression(type_name.AsCString())),summary_sp); + else + category_sp->GetSummaryNavigator()->Add(type_name, summary_sp); +} +#endif + +#ifndef LLDB_DISABLE_PYTHON +static void +AddCXXSummary (TypeCategoryImpl::SharedPointer category_sp, + CXXFunctionSummaryFormat::Callback funct, + const char* description, + ConstString type_name, + TypeSummaryImpl::Flags flags, + bool regex = false) +{ + lldb::TypeSummaryImplSP summary_sp(new CXXFunctionSummaryFormat(flags,funct,description)); + if (regex) + category_sp->GetRegexSummaryNavigator()->Add(RegularExpressionSP(new RegularExpression(type_name.AsCString())),summary_sp); + else + category_sp->GetSummaryNavigator()->Add(type_name, summary_sp); +} +#endif + +#ifndef LLDB_DISABLE_PYTHON +static void AddCXXSynthetic (TypeCategoryImpl::SharedPointer category_sp, + CXXSyntheticChildren::CreateFrontEndCallback generator, + const char* description, + ConstString type_name, + ScriptedSyntheticChildren::Flags flags, + bool regex = false) +{ + lldb::SyntheticChildrenSP synth_sp(new CXXSyntheticChildren(flags,description,generator)); + if (regex) + category_sp->GetRegexSyntheticNavigator()->Add(RegularExpressionSP(new RegularExpression(type_name.AsCString())), synth_sp); + else + category_sp->GetSyntheticNavigator()->Add(type_name,synth_sp); +} +#endif + +void +FormatManager::LoadLibStdcppFormatters() +{ + TypeSummaryImpl::Flags stl_summary_flags; + stl_summary_flags.SetCascades(true) + .SetSkipPointers(false) + .SetSkipReferences(false) + .SetDontShowChildren(true) + .SetDontShowValue(true) + .SetShowMembersOneLiner(false) + .SetHideItemNames(false); + + lldb::TypeSummaryImplSP std_string_summary_sp(new StringSummaryFormat(stl_summary_flags, + "${var._M_dataplus._M_p}")); + + TypeCategoryImpl::SharedPointer gnu_category_sp = GetCategory(m_gnu_cpp_category_name); + + gnu_category_sp->GetSummaryNavigator()->Add(ConstString("std::string"), + std_string_summary_sp); + gnu_category_sp->GetSummaryNavigator()->Add(ConstString("std::basic_string"), + std_string_summary_sp); + gnu_category_sp->GetSummaryNavigator()->Add(ConstString("std::basic_string,std::allocator >"), + std_string_summary_sp); + gnu_category_sp->GetSummaryNavigator()->Add(ConstString("std::basic_string, std::allocator >"), + std_string_summary_sp); + + // making sure we force-pick the summary for printing wstring (_M_p is a wchar_t*) + lldb::TypeSummaryImplSP std_wstring_summary_sp(new StringSummaryFormat(stl_summary_flags, + "${var._M_dataplus._M_p%S}")); + + gnu_category_sp->GetSummaryNavigator()->Add(ConstString("std::wstring"), + std_wstring_summary_sp); + gnu_category_sp->GetSummaryNavigator()->Add(ConstString("std::basic_string"), + std_wstring_summary_sp); + gnu_category_sp->GetSummaryNavigator()->Add(ConstString("std::basic_string,std::allocator >"), + std_wstring_summary_sp); + gnu_category_sp->GetSummaryNavigator()->Add(ConstString("std::basic_string, std::allocator >"), + std_wstring_summary_sp); + + +#ifndef LLDB_DISABLE_PYTHON + + SyntheticChildren::Flags stl_synth_flags; + stl_synth_flags.SetCascades(true).SetSkipPointers(false).SetSkipReferences(false); + + gnu_category_sp->GetRegexSyntheticNavigator()->Add(RegularExpressionSP(new RegularExpression("^std::vector<.+>(( )?&)?$")), + SyntheticChildrenSP(new ScriptedSyntheticChildren(stl_synth_flags, + "lldb.formatters.cpp.gnu_libstdcpp.StdVectorSynthProvider"))); + gnu_category_sp->GetRegexSyntheticNavigator()->Add(RegularExpressionSP(new RegularExpression("^std::map<.+> >(( )?&)?$")), + SyntheticChildrenSP(new ScriptedSyntheticChildren(stl_synth_flags, + "lldb.formatters.cpp.gnu_libstdcpp.StdMapSynthProvider"))); + gnu_category_sp->GetRegexSyntheticNavigator()->Add(RegularExpressionSP(new RegularExpression("^std::list<.+>(( )?&)?$")), + SyntheticChildrenSP(new ScriptedSyntheticChildren(stl_synth_flags, + "lldb.formatters.cpp.gnu_libstdcpp.StdListSynthProvider"))); + + stl_summary_flags.SetDontShowChildren(false);stl_summary_flags.SetSkipPointers(true); + gnu_category_sp->GetRegexSummaryNavigator()->Add(RegularExpressionSP(new RegularExpression("^std::vector<.+>(( )?&)?$")), + TypeSummaryImplSP(new StringSummaryFormat(stl_summary_flags, + "size=${svar%#}"))); + gnu_category_sp->GetRegexSummaryNavigator()->Add(RegularExpressionSP(new RegularExpression("^std::map<.+> >(( )?&)?$")), + TypeSummaryImplSP(new StringSummaryFormat(stl_summary_flags, + "size=${svar%#}"))); + gnu_category_sp->GetRegexSummaryNavigator()->Add(RegularExpressionSP(new RegularExpression("^std::list<.+>(( )?&)?$")), + TypeSummaryImplSP(new StringSummaryFormat(stl_summary_flags, + "size=${svar%#}"))); + + AddCXXSynthetic(gnu_category_sp, lldb_private::formatters::LibStdcppVectorIteratorSyntheticFrontEndCreator, "std::vector iterator synthetic children", ConstString("^__gnu_cxx::__normal_iterator<.+>$"), stl_synth_flags, true); + + AddCXXSynthetic(gnu_category_sp, lldb_private::formatters::LibstdcppMapIteratorSyntheticFrontEndCreator, "std::map iterator synthetic children", ConstString("^std::_Rb_tree_iterator<.+>$"), stl_synth_flags, true); + + gnu_category_sp->GetSummaryNavigator()->Add(ConstString("std::vector >"), + TypeSummaryImplSP(new StringSummaryFormat(stl_summary_flags, "size=${svar%#}"))); + + gnu_category_sp->GetSyntheticNavigator()->Add(ConstString("std::vector >"), + SyntheticChildrenSP(new CXXSyntheticChildren(stl_synth_flags,"libc++ std::vector synthetic children",lldb_private::formatters::LibstdcppVectorBoolSyntheticFrontEndCreator))); + +#endif +} + +void +FormatManager::LoadLibcxxFormatters() +{ + TypeSummaryImpl::Flags stl_summary_flags; + stl_summary_flags.SetCascades(true) + .SetSkipPointers(false) + .SetSkipReferences(false) + .SetDontShowChildren(true) + .SetDontShowValue(true) + .SetShowMembersOneLiner(false) + .SetHideItemNames(false); + +#ifndef LLDB_DISABLE_PYTHON + //std::string code(" lldb.formatters.cpp.libcxx.stdstring_SummaryProvider(valobj,internal_dict)"); + //lldb::TypeSummaryImplSP std_string_summary_sp(new ScriptSummaryFormat(stl_summary_flags, "lldb.formatters.cpp.libcxx.stdstring_SummaryProvider",code.c_str())); + + lldb::TypeSummaryImplSP std_string_summary_sp(new CXXFunctionSummaryFormat(stl_summary_flags, lldb_private::formatters::LibcxxStringSummaryProvider, "std::string summary provider")); + lldb::TypeSummaryImplSP std_wstring_summary_sp(new CXXFunctionSummaryFormat(stl_summary_flags, lldb_private::formatters::LibcxxWStringSummaryProvider, "std::wstring summary provider")); + + TypeCategoryImpl::SharedPointer libcxx_category_sp = GetCategory(m_libcxx_category_name); + + libcxx_category_sp->GetSummaryNavigator()->Add(ConstString("std::__1::string"), + std_string_summary_sp); + libcxx_category_sp->GetSummaryNavigator()->Add(ConstString("std::__1::basic_string, std::__1::allocator >"), + std_string_summary_sp); + + libcxx_category_sp->GetSummaryNavigator()->Add(ConstString("std::__1::wstring"), + std_wstring_summary_sp); + libcxx_category_sp->GetSummaryNavigator()->Add(ConstString("std::__1::basic_string, std::__1::allocator >"), + std_wstring_summary_sp); + + SyntheticChildren::Flags stl_synth_flags; + stl_synth_flags.SetCascades(true).SetSkipPointers(false).SetSkipReferences(false); + + AddCXXSynthetic(libcxx_category_sp, lldb_private::formatters::LibcxxStdVectorSyntheticFrontEndCreator, "libc++ std::vector synthetic children", ConstString("^std::__1::vector<.+>(( )?&)?$"), stl_synth_flags, true); + AddCXXSynthetic(libcxx_category_sp, lldb_private::formatters::LibcxxStdListSyntheticFrontEndCreator, "libc++ std::list synthetic children", ConstString("^std::__1::list<.+>(( )?&)?$"), stl_synth_flags, true); + AddCXXSynthetic(libcxx_category_sp, lldb_private::formatters::LibcxxStdMapSyntheticFrontEndCreator, "libc++ std::map synthetic children", ConstString("^std::__1::map<.+> >(( )?&)?$"), stl_synth_flags, true); + AddCXXSynthetic(libcxx_category_sp, lldb_private::formatters::LibcxxVectorBoolSyntheticFrontEndCreator, "libc++ std::vector synthetic children", ConstString("std::__1::vector >"), stl_synth_flags); + AddCXXSynthetic(libcxx_category_sp, lldb_private::formatters::LibcxxStdMapSyntheticFrontEndCreator, "libc++ std::set synthetic children", ConstString("^std::__1::set<.+> >(( )?&)?$"), stl_synth_flags, true); + AddCXXSynthetic(libcxx_category_sp, lldb_private::formatters::LibcxxStdMapSyntheticFrontEndCreator, "libc++ std::multiset synthetic children", ConstString("^std::__1::multiset<.+> >(( )?&)?$"), stl_synth_flags, true); + AddCXXSynthetic(libcxx_category_sp, lldb_private::formatters::LibcxxStdMapSyntheticFrontEndCreator, "libc++ std::multimap synthetic children", ConstString("^std::__1::multimap<.+> >(( )?&)?$"), stl_synth_flags, true); + + libcxx_category_sp->GetRegexSyntheticNavigator()->Add(RegularExpressionSP(new RegularExpression("^(std::__1::)deque<.+>(( )?&)?$")), + SyntheticChildrenSP(new ScriptedSyntheticChildren(stl_synth_flags, + "lldb.formatters.cpp.libcxx.stddeque_SynthProvider"))); + + AddCXXSynthetic(libcxx_category_sp, lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEndCreator, "shared_ptr synthetic children", ConstString("^(std::__1::)shared_ptr<.+>(( )?&)?$"), stl_synth_flags, true); + AddCXXSynthetic(libcxx_category_sp, lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEndCreator, "weak_ptr synthetic children", ConstString("^(std::__1::)weak_ptr<.+>(( )?&)?$"), stl_synth_flags, true); + + stl_summary_flags.SetDontShowChildren(false);stl_summary_flags.SetSkipPointers(false); + + AddCXXSummary(libcxx_category_sp, lldb_private::formatters::LibcxxContainerSummaryProvider, "libc++ std::vector summary provider", ConstString("^std::__1::vector<.+>(( )?&)?$"), stl_summary_flags, true); + AddCXXSummary(libcxx_category_sp, lldb_private::formatters::LibcxxContainerSummaryProvider, "libc++ std::list summary provider", ConstString("^std::__1::list<.+>(( )?&)?$"), stl_summary_flags, true); + AddCXXSummary(libcxx_category_sp, lldb_private::formatters::LibcxxContainerSummaryProvider, "libc++ std::map summary provider", ConstString("^std::__1::map<.+>(( )?&)?$"), stl_summary_flags, true); + AddCXXSummary(libcxx_category_sp, lldb_private::formatters::LibcxxContainerSummaryProvider, "libc++ std::deque summary provider", ConstString("^std::__1::deque<.+>(( )?&)?$"), stl_summary_flags, true); + AddCXXSummary(libcxx_category_sp, lldb_private::formatters::LibcxxContainerSummaryProvider, "libc++ std::vector summary provider", ConstString("std::__1::vector >"), stl_summary_flags); + AddCXXSummary(libcxx_category_sp, lldb_private::formatters::LibcxxContainerSummaryProvider, "libc++ std::set summary provider", ConstString("^std::__1::set<.+>(( )?&)?$"), stl_summary_flags, true); + AddCXXSummary(libcxx_category_sp, lldb_private::formatters::LibcxxContainerSummaryProvider, "libc++ std::multiset summary provider", ConstString("^std::__1::multiset<.+>(( )?&)?$"), stl_summary_flags, true); + AddCXXSummary(libcxx_category_sp, lldb_private::formatters::LibcxxContainerSummaryProvider, "libc++ std::multimap summary provider", ConstString("^std::__1::multimap<.+>(( )?&)?$"), stl_summary_flags, true); + + stl_summary_flags.SetSkipPointers(true); + AddStringSummary(libcxx_category_sp, "{${var.__ptr_%S}} (strong=${var.count} weak=${var.weak_count})}", ConstString("^std::__1::shared_ptr<.+>(( )?&)?$"), stl_summary_flags, true); + AddStringSummary(libcxx_category_sp, "{${var.__ptr_%S}} (strong=${var.count} weak=${var.weak_count})}", ConstString("^std::__1::weak_ptr<.+>(( )?&)?$"), stl_summary_flags, true); + + AddCXXSynthetic(libcxx_category_sp, lldb_private::formatters::LibCxxVectorIteratorSyntheticFrontEndCreator, "std::vector iterator synthetic children", ConstString("^std::__1::__wrap_iter<.+>$"), stl_synth_flags, true); + + AddCXXSynthetic(libcxx_category_sp, lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEndCreator, "std::map iterator synthetic children", ConstString("^std::__1::__map_iterator<.+>$"), stl_synth_flags, true); + +#endif +} + +void +FormatManager::LoadSystemFormatters() +{ + + TypeSummaryImpl::Flags string_flags; + string_flags.SetCascades(true) + .SetSkipPointers(true) + .SetSkipReferences(false) + .SetDontShowChildren(true) + .SetDontShowValue(false) + .SetShowMembersOneLiner(false) + .SetHideItemNames(false); + + lldb::TypeSummaryImplSP string_format(new StringSummaryFormat(string_flags, "${var%s}")); + + + lldb::TypeSummaryImplSP string_array_format(new StringSummaryFormat(TypeSummaryImpl::Flags().SetCascades(false) + .SetSkipPointers(true) + .SetSkipReferences(false) + .SetDontShowChildren(true) + .SetDontShowValue(true) + .SetShowMembersOneLiner(false) + .SetHideItemNames(false), + "${var%s}")); + + lldb::RegularExpressionSP any_size_char_arr(new RegularExpression("char \\[[0-9]+\\]")); + + TypeCategoryImpl::SharedPointer sys_category_sp = GetCategory(m_system_category_name); + + sys_category_sp->GetSummaryNavigator()->Add(ConstString("char *"), string_format); + sys_category_sp->GetSummaryNavigator()->Add(ConstString("unsigned char *"), string_format); + sys_category_sp->GetRegexSummaryNavigator()->Add(any_size_char_arr, string_array_format); + + lldb::TypeSummaryImplSP ostype_summary(new StringSummaryFormat(TypeSummaryImpl::Flags().SetCascades(false) + .SetSkipPointers(true) + .SetSkipReferences(true) + .SetDontShowChildren(true) + .SetDontShowValue(false) + .SetShowMembersOneLiner(false) + .SetHideItemNames(false), + "${var%O}")); + + sys_category_sp->GetSummaryNavigator()->Add(ConstString("OSType"), ostype_summary); + +#ifndef LLDB_DISABLE_PYTHON + // FIXME because of a bug in the FormatNavigator we need to add a summary for both X* and const X* () + AddCXXSummary(sys_category_sp, lldb_private::formatters::Char16StringSummaryProvider, "char16_t * summary provider", ConstString("char16_t *"), string_flags); + + AddCXXSummary(sys_category_sp, lldb_private::formatters::Char32StringSummaryProvider, "char32_t * summary provider", ConstString("char32_t *"), string_flags); + + AddCXXSummary(sys_category_sp, lldb_private::formatters::WCharStringSummaryProvider, "wchar_t * summary provider", ConstString("wchar_t *"), string_flags); + + AddCXXSummary(sys_category_sp, lldb_private::formatters::Char16StringSummaryProvider, "unichar * summary provider", ConstString("unichar *"), string_flags); + + TypeSummaryImpl::Flags widechar_flags; + widechar_flags.SetDontShowValue(true) + .SetSkipPointers(true) + .SetSkipReferences(false) + .SetCascades(true) + .SetDontShowChildren(true) + .SetHideItemNames(true) + .SetShowMembersOneLiner(false); + + AddCXXSummary(sys_category_sp, lldb_private::formatters::Char16SummaryProvider, "char16_t summary provider", ConstString("char16_t"), widechar_flags); + AddCXXSummary(sys_category_sp, lldb_private::formatters::Char32SummaryProvider, "char32_t summary provider", ConstString("char32_t"), widechar_flags); + AddCXXSummary(sys_category_sp, lldb_private::formatters::WCharSummaryProvider, "wchar_t summary provider", ConstString("wchar_t"), widechar_flags); + + AddCXXSummary(sys_category_sp, lldb_private::formatters::Char16SummaryProvider, "unichar summary provider", ConstString("unichar"), widechar_flags); + +#endif +} + +void +FormatManager::LoadObjCFormatters() +{ + TypeSummaryImpl::Flags objc_flags; + objc_flags.SetCascades(false) + .SetSkipPointers(true) + .SetSkipReferences(true) + .SetDontShowChildren(true) + .SetDontShowValue(true) + .SetShowMembersOneLiner(false) + .SetHideItemNames(false); + + TypeCategoryImpl::SharedPointer objc_category_sp = GetCategory(m_objc_category_name); + + lldb::TypeSummaryImplSP ObjC_BOOL_summary(new CXXFunctionSummaryFormat(objc_flags, lldb_private::formatters::ObjCBOOLSummaryProvider,"")); + objc_category_sp->GetSummaryNavigator()->Add(ConstString("BOOL"), + ObjC_BOOL_summary); + objc_category_sp->GetSummaryNavigator()->Add(ConstString("BOOL &"), + ObjC_BOOL_summary); + objc_category_sp->GetSummaryNavigator()->Add(ConstString("BOOL *"), + ObjC_BOOL_summary); + +#ifndef LLDB_DISABLE_PYTHON + // we need to skip pointers here since we are special casing a SEL* when retrieving its value + objc_flags.SetSkipPointers(true); + AddCXXSummary(objc_category_sp, lldb_private::formatters::ObjCSELSummaryProvider, "SEL summary provider", ConstString("SEL"), objc_flags); + AddCXXSummary(objc_category_sp, lldb_private::formatters::ObjCSELSummaryProvider, "SEL summary provider", ConstString("struct objc_selector"), objc_flags); + AddCXXSummary(objc_category_sp, lldb_private::formatters::ObjCSELSummaryProvider, "SEL summary provider", ConstString("objc_selector"), objc_flags); + AddCXXSummary(objc_category_sp, lldb_private::formatters::ObjCSELSummaryProvider, "SEL summary provider", ConstString("objc_selector *"), objc_flags); + AddCXXSummary(objc_category_sp, lldb_private::formatters::ObjCSELSummaryProvider, "SEL summary provider", ConstString("SEL *"), objc_flags); + + AddCXXSummary(objc_category_sp, lldb_private::formatters::ObjCClassSummaryProvider, "Class summary provider", ConstString("Class"), objc_flags); + + SyntheticChildren::Flags class_synth_flags; + class_synth_flags.SetCascades(true).SetSkipPointers(false).SetSkipReferences(false); + + AddCXXSynthetic(objc_category_sp, lldb_private::formatters::ObjCClassSyntheticFrontEndCreator, "Class synthetic children", ConstString("Class"), class_synth_flags); +#endif // LLDB_DISABLE_PYTHON + + objc_flags.SetSkipPointers(false); + objc_flags.SetCascades(true); + objc_flags.SetSkipReferences(false); + + AddStringSummary (objc_category_sp, + "${var.__FuncPtr%A}", + ConstString("__block_literal_generic"), + objc_flags); + + TypeCategoryImpl::SharedPointer corefoundation_category_sp = GetCategory(m_corefoundation_category_name); + + AddStringSummary(corefoundation_category_sp, + "${var.years} years, ${var.months} months, ${var.days} days, ${var.hours} hours, ${var.minutes} minutes ${var.seconds} seconds", + ConstString("CFGregorianUnits"), + objc_flags); + AddStringSummary(corefoundation_category_sp, + "location=${var.location} length=${var.length}", + ConstString("CFRange"), + objc_flags); + AddStringSummary(corefoundation_category_sp, + "(x=${var.x}, y=${var.y})", + ConstString("NSPoint"), + objc_flags); + AddStringSummary(corefoundation_category_sp, + "location=${var.location}, length=${var.length}", + ConstString("NSRange"), + objc_flags); + AddStringSummary(corefoundation_category_sp, + "${var.origin}, ${var.size}", + ConstString("NSRect"), + objc_flags); + AddStringSummary(corefoundation_category_sp, + "(${var.origin}, ${var.size}), ...", + ConstString("NSRectArray"), + objc_flags); + AddStringSummary(objc_category_sp, + "(width=${var.width}, height=${var.height})", + ConstString("NSSize"), + objc_flags); + + TypeCategoryImpl::SharedPointer coregraphics_category_sp = GetCategory(m_coregraphics_category_name); + + AddStringSummary(coregraphics_category_sp, + "(width=${var.width}, height=${var.height})", + ConstString("CGSize"), + objc_flags); + AddStringSummary(coregraphics_category_sp, + "(x=${var.x}, y=${var.y})", + ConstString("CGPoint"), + objc_flags); + AddStringSummary(coregraphics_category_sp, + "origin=${var.origin} size=${var.size}", + ConstString("CGRect"), + objc_flags); + + TypeCategoryImpl::SharedPointer coreservices_category_sp = GetCategory(m_coreservices_category_name); + + AddStringSummary(coreservices_category_sp, + "red=${var.red} green=${var.green} blue=${var.blue}", + ConstString("RGBColor"), + objc_flags); + AddStringSummary(coreservices_category_sp, + "(t=${var.top}, l=${var.left}, b=${var.bottom}, r=${var.right})", + ConstString("Rect"), + objc_flags); + AddStringSummary(coreservices_category_sp, + "(v=${var.v}, h=${var.h})", + ConstString("Point"), + objc_flags); + AddStringSummary(coreservices_category_sp, + "${var.month}/${var.day}/${var.year} ${var.hour} :${var.minute} :${var.second} dayOfWeek:${var.dayOfWeek}", + ConstString("DateTimeRect *"), + objc_flags); + AddStringSummary(coreservices_category_sp, + "${var.ld.month}/${var.ld.day}/${var.ld.year} ${var.ld.hour} :${var.ld.minute} :${var.ld.second} dayOfWeek:${var.ld.dayOfWeek}", + ConstString("LongDateRect"), + objc_flags); + AddStringSummary(coreservices_category_sp, + "(x=${var.x}, y=${var.y})", + ConstString("HIPoint"), + objc_flags); + AddStringSummary(coreservices_category_sp, + "origin=${var.origin} size=${var.size}", + ConstString("HIRect"), + objc_flags); + + TypeCategoryImpl::SharedPointer appkit_category_sp = GetCategory(m_appkit_category_name); + + TypeSummaryImpl::Flags appkit_flags; + appkit_flags.SetCascades(true) + .SetSkipPointers(false) + .SetSkipReferences(false) + .SetDontShowChildren(true) + .SetDontShowValue(false) + .SetShowMembersOneLiner(false) + .SetHideItemNames(false); + + appkit_flags.SetDontShowChildren(false); + + +#ifndef LLDB_DISABLE_PYTHON + AddCXXSummary(appkit_category_sp, lldb_private::formatters::NSArraySummaryProvider, "NSArray summary provider", ConstString("NSArray"), appkit_flags); + AddCXXSummary(appkit_category_sp, lldb_private::formatters::NSArraySummaryProvider, "NSArray summary provider", ConstString("NSMutableArray"), appkit_flags); + AddCXXSummary(appkit_category_sp, lldb_private::formatters::NSArraySummaryProvider, "NSArray summary provider", ConstString("__NSArrayI"), appkit_flags); + AddCXXSummary(appkit_category_sp, lldb_private::formatters::NSArraySummaryProvider, "NSArray summary provider", ConstString("__NSArrayM"), appkit_flags); + AddCXXSummary(appkit_category_sp, lldb_private::formatters::NSArraySummaryProvider, "NSArray summary provider", ConstString("__NSCFArray"), appkit_flags); + AddCXXSummary(appkit_category_sp, lldb_private::formatters::NSArraySummaryProvider, "NSArray summary provider", ConstString("CFArrayRef"), appkit_flags); + AddCXXSummary(appkit_category_sp, lldb_private::formatters::NSArraySummaryProvider, "NSArray summary provider", ConstString("CFMutableArrayRef"), appkit_flags); + + AddCXXSummary(appkit_category_sp, lldb_private::formatters::NSDictionarySummaryProvider, "NSDictionary summary provider", ConstString("NSDictionary"), appkit_flags); + AddCXXSummary(appkit_category_sp, lldb_private::formatters::NSDictionarySummaryProvider, "NSDictionary summary provider", ConstString("NSMutableDictionary"), appkit_flags); + AddCXXSummary(appkit_category_sp, lldb_private::formatters::NSDictionarySummaryProvider, "NSDictionary summary provider", ConstString("__NSCFDictionary"), appkit_flags); + AddCXXSummary(appkit_category_sp, lldb_private::formatters::NSDictionarySummaryProvider, "NSDictionary summary provider", ConstString("__NSDictionaryI"), appkit_flags); + AddCXXSummary(appkit_category_sp, lldb_private::formatters::NSDictionarySummaryProvider, "NSDictionary summary provider", ConstString("__NSDictionaryM"), appkit_flags); + AddCXXSummary(appkit_category_sp, lldb_private::formatters::NSDictionarySummaryProvider, "NSDictionary summary provider", ConstString("CFDictionaryRef"), appkit_flags); + AddCXXSummary(appkit_category_sp, lldb_private::formatters::NSDictionarySummaryProvider, "NSDictionary summary provider", ConstString("CFMutableDictionaryRef"), appkit_flags); + + AddCXXSummary(appkit_category_sp, lldb_private::formatters::NSSetSummaryProvider, "NSSet summary", ConstString("NSSet"), appkit_flags); + AddCXXSummary(appkit_category_sp, lldb_private::formatters::NSSetSummaryProvider, "NSMutableSet summary", ConstString("NSMutableSet"), appkit_flags); + AddCXXSummary(appkit_category_sp, lldb_private::formatters::NSSetSummaryProvider, "CFSetRef summary", ConstString("CFSetRef"), appkit_flags); + AddCXXSummary(appkit_category_sp, lldb_private::formatters::NSSetSummaryProvider, "CFMutableSetRef summary", ConstString("CFMutableSetRef"), appkit_flags); + AddCXXSummary(appkit_category_sp, lldb_private::formatters::NSSetSummaryProvider, "__NSCFSet summary", ConstString("__NSCFSet"), appkit_flags); + AddCXXSummary(appkit_category_sp, lldb_private::formatters::NSSetSummaryProvider, "__NSSetI summary", ConstString("__NSSetI"), appkit_flags); + AddCXXSummary(appkit_category_sp, lldb_private::formatters::NSSetSummaryProvider, "__NSSetM summary", ConstString("__NSSetM"), appkit_flags); + AddCXXSummary(appkit_category_sp, lldb_private::formatters::NSSetSummaryProvider, "NSCountedSet summary", ConstString("NSCountedSet"), appkit_flags); + AddCXXSummary(appkit_category_sp, lldb_private::formatters::NSSetSummaryProvider, "NSMutableSet summary", ConstString("NSMutableSet"), appkit_flags); + AddCXXSummary(appkit_category_sp, lldb_private::formatters::NSSetSummaryProvider, "NSOrderedSet summary", ConstString("NSOrderedSet"), appkit_flags); + AddCXXSummary(appkit_category_sp, lldb_private::formatters::NSSetSummaryProvider, "__NSOrderedSetI summary", ConstString("__NSOrderedSetI"), appkit_flags); + AddCXXSummary(appkit_category_sp, lldb_private::formatters::NSSetSummaryProvider, "__NSOrderedSetM summary", ConstString("__NSOrderedSetM"), appkit_flags); + + // AddSummary(appkit_category_sp, "${var.key%@} -> ${var.value%@}", ConstString("$_lldb_typegen_nspair"), appkit_flags); + + appkit_flags.SetDontShowChildren(true); + + AddCXXSynthetic(appkit_category_sp, lldb_private::formatters::NSArraySyntheticFrontEndCreator, "NSArray synthetic children", ConstString("__NSArrayM"), ScriptedSyntheticChildren::Flags()); + AddCXXSynthetic(appkit_category_sp, lldb_private::formatters::NSArraySyntheticFrontEndCreator, "NSArray synthetic children", ConstString("__NSArrayI"), ScriptedSyntheticChildren::Flags()); + AddCXXSynthetic(appkit_category_sp, lldb_private::formatters::NSArraySyntheticFrontEndCreator, "NSArray synthetic children", ConstString("NSArray"), ScriptedSyntheticChildren::Flags()); + AddCXXSynthetic(appkit_category_sp, lldb_private::formatters::NSArraySyntheticFrontEndCreator, "NSArray synthetic children", ConstString("NSMutableArray"), ScriptedSyntheticChildren::Flags()); + AddCXXSynthetic(appkit_category_sp, lldb_private::formatters::NSArraySyntheticFrontEndCreator, "NSArray synthetic children", ConstString("__NSCFArray"), ScriptedSyntheticChildren::Flags()); + AddCXXSynthetic(appkit_category_sp, lldb_private::formatters::NSArraySyntheticFrontEndCreator, "NSArray synthetic children", ConstString("CFMutableArrayRef"), ScriptedSyntheticChildren::Flags()); + AddCXXSynthetic(appkit_category_sp, lldb_private::formatters::NSArraySyntheticFrontEndCreator, "NSArray synthetic children", ConstString("CFArrayRef"), ScriptedSyntheticChildren::Flags()); + + AddCXXSynthetic(appkit_category_sp, lldb_private::formatters::NSDictionarySyntheticFrontEndCreator, "NSDictionary synthetic children", ConstString("__NSDictionaryM"), ScriptedSyntheticChildren::Flags()); + AddCXXSynthetic(appkit_category_sp, lldb_private::formatters::NSDictionarySyntheticFrontEndCreator, "NSDictionary synthetic children", ConstString("__NSDictionaryI"), ScriptedSyntheticChildren::Flags()); + AddCXXSynthetic(appkit_category_sp, lldb_private::formatters::NSDictionarySyntheticFrontEndCreator, "NSDictionary synthetic children", ConstString("NSDictionary"), ScriptedSyntheticChildren::Flags()); + AddCXXSynthetic(appkit_category_sp, lldb_private::formatters::NSDictionarySyntheticFrontEndCreator, "NSDictionary synthetic children", ConstString("NSMutableDictionary"), ScriptedSyntheticChildren::Flags()); + AddCXXSynthetic(appkit_category_sp, lldb_private::formatters::NSDictionarySyntheticFrontEndCreator, "NSDictionary synthetic children", ConstString("CFDictionaryRef"), ScriptedSyntheticChildren::Flags()); + AddCXXSynthetic(appkit_category_sp, lldb_private::formatters::NSDictionarySyntheticFrontEndCreator, "NSDictionary synthetic children", ConstString("CFMutableDictionaryRef"), ScriptedSyntheticChildren::Flags()); + + AddCXXSynthetic(appkit_category_sp, lldb_private::formatters::NSSetSyntheticFrontEndCreator, "NSSet synthetic children", ConstString("NSSet"), ScriptedSyntheticChildren::Flags()); + AddCXXSynthetic(appkit_category_sp, lldb_private::formatters::NSSetSyntheticFrontEndCreator, "__NSSetI synthetic children", ConstString("__NSSetI"), ScriptedSyntheticChildren::Flags()); + AddCXXSynthetic(appkit_category_sp, lldb_private::formatters::NSSetSyntheticFrontEndCreator, "__NSSetM synthetic children", ConstString("__NSSetM"), ScriptedSyntheticChildren::Flags()); + AddCXXSynthetic(appkit_category_sp, lldb_private::formatters::NSSetSyntheticFrontEndCreator, "NSMutableSet synthetic children", ConstString("NSMutableSet"), ScriptedSyntheticChildren::Flags()); + AddCXXSynthetic(appkit_category_sp, lldb_private::formatters::NSSetSyntheticFrontEndCreator, "NSOrderedSet synthetic children", ConstString("NSOrderedSet"), ScriptedSyntheticChildren::Flags()); + AddCXXSynthetic(appkit_category_sp, lldb_private::formatters::NSSetSyntheticFrontEndCreator, "__NSOrderedSetI synthetic children", ConstString("__NSOrderedSetI"), ScriptedSyntheticChildren::Flags()); + AddCXXSynthetic(appkit_category_sp, lldb_private::formatters::NSSetSyntheticFrontEndCreator, "__NSOrderedSetM synthetic children", ConstString("__NSOrderedSetM"), ScriptedSyntheticChildren::Flags()); + + AddCXXSummary(appkit_category_sp,lldb_private::formatters::CFBagSummaryProvider, "CFBag summary provider", ConstString("CFBagRef"), appkit_flags); + AddCXXSummary(appkit_category_sp,lldb_private::formatters::CFBagSummaryProvider, "CFBag summary provider", ConstString("__CFBag"), appkit_flags); + AddCXXSummary(appkit_category_sp,lldb_private::formatters::CFBagSummaryProvider, "CFBag summary provider", ConstString("const struct __CFBag"), appkit_flags); + AddCXXSummary(appkit_category_sp,lldb_private::formatters::CFBagSummaryProvider, "CFBag summary provider", ConstString("CFMutableBagRef"), appkit_flags); + + AddCXXSummary(appkit_category_sp,lldb_private::formatters::CFBinaryHeapSummaryProvider, "CFBinaryHeap summary provider", ConstString("CFBinaryHeapRef"), appkit_flags); + AddCXXSummary(appkit_category_sp,lldb_private::formatters::CFBinaryHeapSummaryProvider, "CFBinaryHeap summary provider", ConstString("__CFBinaryHeap"), appkit_flags); + + AddCXXSummary(appkit_category_sp, lldb_private::formatters::NSStringSummaryProvider, "NSString summary provider", ConstString("NSString"), appkit_flags); + AddCXXSummary(appkit_category_sp, lldb_private::formatters::NSStringSummaryProvider, "NSString summary provider", ConstString("CFStringRef"), appkit_flags); + AddCXXSummary(appkit_category_sp, lldb_private::formatters::NSStringSummaryProvider, "NSString summary provider", ConstString("CFMutableStringRef"), appkit_flags); + AddCXXSummary(appkit_category_sp, lldb_private::formatters::NSStringSummaryProvider, "NSString summary provider", ConstString("NSMutableString"), appkit_flags); + AddCXXSummary(appkit_category_sp, lldb_private::formatters::NSStringSummaryProvider, "NSString summary provider", ConstString("__NSCFConstantString"), appkit_flags); + AddCXXSummary(appkit_category_sp, lldb_private::formatters::NSStringSummaryProvider, "NSString summary provider", ConstString("__NSCFString"), appkit_flags); + AddCXXSummary(appkit_category_sp, lldb_private::formatters::NSStringSummaryProvider, "NSString summary provider", ConstString("NSCFConstantString"), appkit_flags); + AddCXXSummary(appkit_category_sp, lldb_private::formatters::NSStringSummaryProvider, "NSString summary provider", ConstString("NSCFString"), appkit_flags); + AddCXXSummary(appkit_category_sp, lldb_private::formatters::NSStringSummaryProvider, "NSString summary provider", ConstString("NSPathStore2"), appkit_flags); + + AddCXXSummary(appkit_category_sp, lldb_private::formatters::NSAttributedStringSummaryProvider, "NSAttributedString summary provider", ConstString("NSAttributedString"), appkit_flags); + AddCXXSummary(appkit_category_sp, lldb_private::formatters::NSMutableAttributedStringSummaryProvider, "NSMutableAttributedString summary provider", ConstString("NSMutableAttributedString"), appkit_flags); + AddCXXSummary(appkit_category_sp, lldb_private::formatters::NSMutableAttributedStringSummaryProvider, "NSMutableAttributedString summary provider", ConstString("NSConcreteMutableAttributedString"), appkit_flags); + + AddCXXSummary(appkit_category_sp, lldb_private::formatters::NSBundleSummaryProvider, "NSBundle summary provider", ConstString("NSBundle"), appkit_flags); + + AddCXXSummary(appkit_category_sp, lldb_private::formatters::NSDataSummaryProvider, "NSData summary provider", ConstString("NSData"), appkit_flags); + AddCXXSummary(appkit_category_sp, lldb_private::formatters::NSDataSummaryProvider, "NSData summary provider", ConstString("NSConcreteData"), appkit_flags); + AddCXXSummary(appkit_category_sp, lldb_private::formatters::NSDataSummaryProvider, "NSData summary provider", ConstString("NSConcreteMutableData"), appkit_flags); + AddCXXSummary(appkit_category_sp, lldb_private::formatters::NSDataSummaryProvider, "NSData summary provider", ConstString("__NSCFData"), appkit_flags); + AddCXXSummary(appkit_category_sp, lldb_private::formatters::NSDataSummaryProvider, "NSData summary provider", ConstString("CFDataRef"), appkit_flags); + AddCXXSummary(appkit_category_sp, lldb_private::formatters::NSDataSummaryProvider, "NSData summary provider", ConstString("CFMutableDataRef"), appkit_flags); + + AddCXXSummary(appkit_category_sp, lldb_private::formatters::NSMachPortSummaryProvider, "NSMachPort summary provider", ConstString("NSMachPort"), appkit_flags); + + AddCXXSummary(appkit_category_sp, lldb_private::formatters::NSNotificationSummaryProvider, "NSNotification summary provider", ConstString("NSNotification"), appkit_flags); + AddCXXSummary(appkit_category_sp, lldb_private::formatters::NSNotificationSummaryProvider, "NSNotification summary provider", ConstString("NSConcreteNotification"), appkit_flags); + + AddStringSummary(appkit_category_sp, "domain: ${var._domain} - code: ${var._code}", ConstString("NSError"), appkit_flags); + AddStringSummary(appkit_category_sp,"name:${var.name%S} reason:${var.reason%S}",ConstString("NSException"),appkit_flags); + + AddCXXSummary(appkit_category_sp, lldb_private::formatters::NSNumberSummaryProvider, "NSNumber summary provider", ConstString("NSNumber"), appkit_flags); + AddCXXSummary(appkit_category_sp, lldb_private::formatters::NSNumberSummaryProvider, "NSNumber summary provider", ConstString("__NSCFBoolean"), appkit_flags); + AddCXXSummary(appkit_category_sp, lldb_private::formatters::NSNumberSummaryProvider, "NSNumber summary provider", ConstString("__NSCFNumber"), appkit_flags); + AddCXXSummary(appkit_category_sp, lldb_private::formatters::NSNumberSummaryProvider, "NSNumber summary provider", ConstString("NSCFBoolean"), appkit_flags); + AddCXXSummary(appkit_category_sp, lldb_private::formatters::NSNumberSummaryProvider, "NSNumber summary provider", ConstString("NSCFNumber"), appkit_flags); + + AddCXXSummary(appkit_category_sp, lldb_private::formatters::RuntimeSpecificDescriptionSummaryProvider, "NSDecimalNumber summary provider", ConstString("NSDecimalNumber"), appkit_flags); + AddCXXSummary(appkit_category_sp, lldb_private::formatters::RuntimeSpecificDescriptionSummaryProvider, "NSHost summary provider", ConstString("NSHost"), appkit_flags); + AddCXXSummary(appkit_category_sp, lldb_private::formatters::RuntimeSpecificDescriptionSummaryProvider, "NSTask summary provider", ConstString("NSTask"), appkit_flags); + AddCXXSummary(appkit_category_sp, lldb_private::formatters::RuntimeSpecificDescriptionSummaryProvider, "NSValue summary provider", ConstString("NSValue"), appkit_flags); + + AddCXXSummary(appkit_category_sp, lldb_private::formatters::NSURLSummaryProvider, "NSURL summary provider", ConstString("NSURL"), appkit_flags); + AddCXXSummary(appkit_category_sp, lldb_private::formatters::NSURLSummaryProvider, "NSURL summary provider", ConstString("CFURLRef"), appkit_flags); + + AddCXXSummary(appkit_category_sp, lldb_private::formatters::NSDateSummaryProvider, "NSDate summary provider", ConstString("NSDate"), appkit_flags); + AddCXXSummary(appkit_category_sp, lldb_private::formatters::NSDateSummaryProvider, "NSDate summary provider", ConstString("__NSDate"), appkit_flags); + AddCXXSummary(appkit_category_sp, lldb_private::formatters::NSDateSummaryProvider, "NSDate summary provider", ConstString("__NSTaggedDate"), appkit_flags); + AddCXXSummary(appkit_category_sp, lldb_private::formatters::NSDateSummaryProvider, "NSDate summary provider", ConstString("NSCalendarDate"), appkit_flags); + + AddCXXSummary(appkit_category_sp, lldb_private::formatters::NSTimeZoneSummaryProvider, "NSTimeZone summary provider", ConstString("NSTimeZone"), appkit_flags); + AddCXXSummary(appkit_category_sp, lldb_private::formatters::NSTimeZoneSummaryProvider, "NSTimeZone summary provider", ConstString("CFTimeZoneRef"), appkit_flags); + AddCXXSummary(appkit_category_sp, lldb_private::formatters::NSTimeZoneSummaryProvider, "NSTimeZone summary provider", ConstString("__NSTimeZone"), appkit_flags); + + // CFAbsoluteTime is actually a double rather than a pointer to an object + // we do not care about the numeric value, since it is probably meaningless to users + appkit_flags.SetDontShowValue(true); + AddCXXSummary(appkit_category_sp, lldb_private::formatters::CFAbsoluteTimeSummaryProvider, "CFAbsoluteTime summary provider", ConstString("CFAbsoluteTime"), appkit_flags); + appkit_flags.SetDontShowValue(false); + + AddCXXSummary(appkit_category_sp, lldb_private::formatters::NSIndexSetSummaryProvider, "NSIndexSet summary provider", ConstString("NSIndexSet"), appkit_flags); + AddCXXSummary(appkit_category_sp, lldb_private::formatters::NSIndexSetSummaryProvider, "NSIndexSet summary provider", ConstString("NSMutableIndexSet"), appkit_flags); + + AddStringSummary(appkit_category_sp, + "@\"${var.month%d}/${var.day%d}/${var.year%d} ${var.hour%d}:${var.minute%d}:${var.second}\"", + ConstString("CFGregorianDate"), + appkit_flags); + + AddCXXSummary(appkit_category_sp, lldb_private::formatters::CFBitVectorSummaryProvider, "CFBitVector summary provider", ConstString("CFBitVectorRef"), appkit_flags); + AddCXXSummary(appkit_category_sp, lldb_private::formatters::CFBitVectorSummaryProvider, "CFBitVector summary provider", ConstString("CFMutableBitVectorRef"), appkit_flags); + AddCXXSummary(appkit_category_sp, lldb_private::formatters::CFBitVectorSummaryProvider, "CFBitVector summary provider", ConstString("__CFBitVector"), appkit_flags); + AddCXXSummary(appkit_category_sp, lldb_private::formatters::CFBitVectorSummaryProvider, "CFBitVector summary provider", ConstString("__CFMutableBitVector"), appkit_flags); +#endif // LLDB_DISABLE_PYTHON + + TypeCategoryImpl::SharedPointer vectors_category_sp = GetCategory(m_vectortypes_category_name); + + TypeSummaryImpl::Flags vector_flags; + vector_flags.SetCascades(true) + .SetSkipPointers(true) + .SetSkipReferences(false) + .SetDontShowChildren(true) + .SetDontShowValue(false) + .SetShowMembersOneLiner(true) + .SetHideItemNames(true); + + AddStringSummary(vectors_category_sp, + "${var.uint128}", + ConstString("builtin_type_vec128"), + objc_flags); + + AddStringSummary(vectors_category_sp, + "", + ConstString("float [4]"), + vector_flags); + AddStringSummary(vectors_category_sp, + "", + ConstString("int32_t [4]"), + vector_flags); + AddStringSummary(vectors_category_sp, + "", + ConstString("int16_t [8]"), + vector_flags); + AddStringSummary(vectors_category_sp, + "", + ConstString("vDouble"), + vector_flags); + AddStringSummary(vectors_category_sp, + "", + ConstString("vFloat"), + vector_flags); + AddStringSummary(vectors_category_sp, + "", + ConstString("vSInt8"), + vector_flags); + AddStringSummary(vectors_category_sp, + "", + ConstString("vSInt16"), + vector_flags); + AddStringSummary(vectors_category_sp, + "", + ConstString("vSInt32"), + vector_flags); + AddStringSummary(vectors_category_sp, + "", + ConstString("vUInt16"), + vector_flags); + AddStringSummary(vectors_category_sp, + "", + ConstString("vUInt8"), + vector_flags); + AddStringSummary(vectors_category_sp, + "", + ConstString("vUInt16"), + vector_flags); + AddStringSummary(vectors_category_sp, + "", + ConstString("vUInt32"), + vector_flags); + AddStringSummary(vectors_category_sp, + "", + ConstString("vBool32"), + vector_flags); +} diff --git a/contrib/llvm/tools/lldb/source/DataFormatters/LibCxx.cpp b/contrib/llvm/tools/lldb/source/DataFormatters/LibCxx.cpp new file mode 100644 index 00000000000..cdc57f6bd93 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/DataFormatters/LibCxx.cpp @@ -0,0 +1,519 @@ +//===-- LibCxx.cpp ----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "lldb/DataFormatters/CXXFormatterFunctions.h" + +#include "lldb/Core/DataBufferHeap.h" +#include "lldb/Core/Debugger.h" +#include "lldb/Core/Error.h" +#include "lldb/Core/Stream.h" +#include "lldb/Core/ValueObject.h" +#include "lldb/Core/ValueObjectConstResult.h" +#include "lldb/Host/Endian.h" +#include "lldb/Symbol/ClangASTContext.h" +#include "lldb/Target/ObjCLanguageRuntime.h" +#include "lldb/Target/Target.h" + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::formatters; + +lldb_private::formatters::LibcxxVectorBoolSyntheticFrontEnd::LibcxxVectorBoolSyntheticFrontEnd (lldb::ValueObjectSP valobj_sp) : +SyntheticChildrenFrontEnd(*valobj_sp.get()), +m_exe_ctx_ref(), +m_count(0), +m_base_data_address(0), +m_options() +{ + if (valobj_sp) + Update(); + m_options.SetCoerceToId(false) + .SetUnwindOnError(true) + .SetKeepInMemory(true) + .SetUseDynamic(lldb::eDynamicCanRunTarget); +} + +size_t +lldb_private::formatters::LibcxxVectorBoolSyntheticFrontEnd::CalculateNumChildren () +{ + return m_count; +} + +lldb::ValueObjectSP +lldb_private::formatters::LibcxxVectorBoolSyntheticFrontEnd::GetChildAtIndex (size_t idx) +{ + if (idx >= m_count) + return ValueObjectSP(); + if (m_base_data_address == 0 || m_count == 0) + return ValueObjectSP(); + size_t byte_idx = (idx >> 3); // divide by 8 to get byte index + size_t bit_index = (idx & 7); // efficient idx % 8 for bit index + lldb::addr_t byte_location = m_base_data_address + byte_idx; + ProcessSP process_sp(m_exe_ctx_ref.GetProcessSP()); + if (!process_sp) + return ValueObjectSP(); + uint8_t byte = 0; + uint8_t mask = 0; + Error err; + size_t bytes_read = process_sp->ReadMemory(byte_location, &byte, 1, err); + if (err.Fail() || bytes_read == 0) + return ValueObjectSP(); + switch (bit_index) + { + case 0: + mask = 1; break; + case 1: + mask = 2; break; + case 2: + mask = 4; break; + case 3: + mask = 8; break; + case 4: + mask = 16; break; + case 5: + mask = 32; break; + case 6: + mask = 64; break; + case 7: + mask = 128; break; + default: + return ValueObjectSP(); + } + bool bit_set = ((byte & mask) != 0); + Target& target(process_sp->GetTarget()); + ValueObjectSP retval_sp; + if (bit_set) + target.EvaluateExpression("(bool)true", NULL, retval_sp); + else + target.EvaluateExpression("(bool)false", NULL, retval_sp); + StreamString name; name.Printf("[%zu]",idx); + if (retval_sp) + retval_sp->SetName(ConstString(name.GetData())); + return retval_sp; +} + +/*(std::__1::vector >) vBool = { + __begin_ = 0x00000001001000e0 + __size_ = 56 + __cap_alloc_ = { + std::__1::__libcpp_compressed_pair_imp > = { + __first_ = 1 + } + } + }*/ + +bool +lldb_private::formatters::LibcxxVectorBoolSyntheticFrontEnd::Update() +{ + ValueObjectSP valobj_sp = m_backend.GetSP(); + if (!valobj_sp) + return false; + if (!valobj_sp) + return false; + m_exe_ctx_ref = valobj_sp->GetExecutionContextRef(); + ValueObjectSP size_sp(valobj_sp->GetChildMemberWithName(ConstString("__size_"), true)); + if (!size_sp) + return false; + m_count = size_sp->GetValueAsUnsigned(0); + if (!m_count) + return true; + ValueObjectSP begin_sp(valobj_sp->GetChildMemberWithName(ConstString("__begin_"), true)); + if (!begin_sp) + { + m_count = 0; + return false; + } + m_base_data_address = begin_sp->GetValueAsUnsigned(0); + if (!m_base_data_address) + { + m_count = 0; + return false; + } + return false; +} + +bool +lldb_private::formatters::LibcxxVectorBoolSyntheticFrontEnd::MightHaveChildren () +{ + return true; +} + +size_t +lldb_private::formatters::LibcxxVectorBoolSyntheticFrontEnd::GetIndexOfChildWithName (const ConstString &name) +{ + if (!m_count || !m_base_data_address) + return UINT32_MAX; + const char* item_name = name.GetCString(); + uint32_t idx = ExtractIndexFromString(item_name); + if (idx < UINT32_MAX && idx >= CalculateNumChildren()) + return UINT32_MAX; + return idx; +} + +lldb_private::formatters::LibcxxVectorBoolSyntheticFrontEnd::~LibcxxVectorBoolSyntheticFrontEnd () +{} + +SyntheticChildrenFrontEnd* +lldb_private::formatters::LibcxxVectorBoolSyntheticFrontEndCreator (CXXSyntheticChildren*, lldb::ValueObjectSP valobj_sp) +{ + if (!valobj_sp) + return NULL; + return (new LibcxxVectorBoolSyntheticFrontEnd(valobj_sp)); +} + +/* + (lldb) fr var ibeg --raw --ptr-depth 1 + (std::__1::__map_iterator, std::__1::allocator > >, std::__1::__tree_node, std::__1::allocator > >, void *> *, long> >) ibeg = { + __i_ = { + __ptr_ = 0x0000000100103870 { + std::__1::__tree_node_base = { + std::__1::__tree_end_node *> = { + __left_ = 0x0000000000000000 + } + __right_ = 0x0000000000000000 + __parent_ = 0x00000001001038b0 + __is_black_ = true + } + __value_ = { + first = 0 + second = { std::string } + */ + +lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEnd::LibCxxMapIteratorSyntheticFrontEnd (lldb::ValueObjectSP valobj_sp) : +SyntheticChildrenFrontEnd(*valobj_sp.get()), +m_pair_ptr() +{ + if (valobj_sp) + Update(); +} + +bool +lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEnd::Update() +{ + ValueObjectSP valobj_sp = m_backend.GetSP(); + if (!valobj_sp) + return false; + + TargetSP target_sp(valobj_sp->GetTargetSP()); + + if (!target_sp) + return false; + + if (!valobj_sp) + return false; + + // this must be a ValueObject* because it is a child of the ValueObject we are producing children for + // it if were a ValueObjectSP, we would end up with a loop (iterator -> synthetic -> child -> parent == iterator) + // and that would in turn leak memory by never allowing the ValueObjects to die and free their memory + m_pair_ptr = valobj_sp->GetValueForExpressionPath(".__i_.__ptr_->__value_", + NULL, + NULL, + NULL, + ValueObject::GetValueForExpressionPathOptions().DontCheckDotVsArrowSyntax().DontAllowSyntheticChildren(), + NULL).get(); + + return false; +} + +size_t +lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEnd::CalculateNumChildren () +{ + return 2; +} + +lldb::ValueObjectSP +lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEnd::GetChildAtIndex (size_t idx) +{ + if (!m_pair_ptr) + return lldb::ValueObjectSP(); + return m_pair_ptr->GetChildAtIndex(idx, true); +} + +bool +lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEnd::MightHaveChildren () +{ + return true; +} + +size_t +lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEnd::GetIndexOfChildWithName (const ConstString &name) +{ + if (name == ConstString("first")) + return 0; + if (name == ConstString("second")) + return 1; + return UINT32_MAX; +} + +lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEnd::~LibCxxMapIteratorSyntheticFrontEnd () +{ + // this will be deleted when its parent dies (since it's a child object) + //delete m_pair_ptr; +} + +SyntheticChildrenFrontEnd* +lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEndCreator (CXXSyntheticChildren*, lldb::ValueObjectSP valobj_sp) +{ + if (!valobj_sp) + return NULL; + return (new LibCxxMapIteratorSyntheticFrontEnd(valobj_sp)); +} + +/* + (lldb) fr var ibeg --raw --ptr-depth 1 -T + (std::__1::__wrap_iter) ibeg = { + (std::__1::__wrap_iter::iterator_type) __i = 0x00000001001037a0 { + (int) *__i = 1 + } + } +*/ + +SyntheticChildrenFrontEnd* +lldb_private::formatters::LibCxxVectorIteratorSyntheticFrontEndCreator (CXXSyntheticChildren*, lldb::ValueObjectSP valobj_sp) +{ + static ConstString g_item_name; + if (!g_item_name) + g_item_name.SetCString("__i"); + if (!valobj_sp) + return NULL; + return (new VectorIteratorSyntheticFrontEnd(valobj_sp,g_item_name)); +} + +lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEnd::LibcxxSharedPtrSyntheticFrontEnd (lldb::ValueObjectSP valobj_sp) : +SyntheticChildrenFrontEnd(*valobj_sp.get()), +m_cntrl(NULL), +m_count_sp(), +m_weak_count_sp(), +m_ptr_size(0), +m_byte_order(lldb::eByteOrderInvalid) +{ + if (valobj_sp) + Update(); +} + +size_t +lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEnd::CalculateNumChildren () +{ + return (m_cntrl ? 1 : 0); +} + +lldb::ValueObjectSP +lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEnd::GetChildAtIndex (size_t idx) +{ + if (!m_cntrl) + return lldb::ValueObjectSP(); + + ValueObjectSP valobj_sp = m_backend.GetSP(); + if (!valobj_sp) + return lldb::ValueObjectSP(); + + if (idx == 0) + return valobj_sp->GetChildMemberWithName(ConstString("__ptr_"), true); + + if (idx > 2) + return lldb::ValueObjectSP(); + + if (idx == 1) + { + if (!m_count_sp) + { + ValueObjectSP shared_owners_sp(m_cntrl->GetChildMemberWithName(ConstString("__shared_owners_"),true)); + if (!shared_owners_sp) + return lldb::ValueObjectSP(); + uint64_t count = 1 + shared_owners_sp->GetValueAsUnsigned(0); + DataExtractor data(&count, 8, m_byte_order, m_ptr_size); + m_count_sp = ValueObject::CreateValueObjectFromData("count", data, valobj_sp->GetExecutionContextRef(), shared_owners_sp->GetClangType()); + } + return m_count_sp; + } + else /* if (idx == 2) */ + { + if (!m_weak_count_sp) + { + ValueObjectSP shared_weak_owners_sp(m_cntrl->GetChildMemberWithName(ConstString("__shared_weak_owners_"),true)); + if (!shared_weak_owners_sp) + return lldb::ValueObjectSP(); + uint64_t count = 1 + shared_weak_owners_sp->GetValueAsUnsigned(0); + DataExtractor data(&count, 8, m_byte_order, m_ptr_size); + m_weak_count_sp = ValueObject::CreateValueObjectFromData("count", data, valobj_sp->GetExecutionContextRef(), shared_weak_owners_sp->GetClangType()); + } + return m_weak_count_sp; + } +} + +bool +lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEnd::Update() +{ + m_count_sp.reset(); + m_weak_count_sp.reset(); + m_cntrl = NULL; + + ValueObjectSP valobj_sp = m_backend.GetSP(); + if (!valobj_sp) + return false; + + TargetSP target_sp(valobj_sp->GetTargetSP()); + if (!target_sp) + return false; + + m_byte_order = target_sp->GetArchitecture().GetByteOrder(); + m_ptr_size = target_sp->GetArchitecture().GetAddressByteSize(); + + lldb::ValueObjectSP cntrl_sp(valobj_sp->GetChildMemberWithName(ConstString("__cntrl_"),true)); + + m_cntrl = cntrl_sp.get(); // need to store the raw pointer to avoid a circular dependency + return false; +} + +bool +lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEnd::MightHaveChildren () +{ + return true; +} + +size_t +lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEnd::GetIndexOfChildWithName (const ConstString &name) +{ + if (name == ConstString("__ptr_")) + return 0; + if (name == ConstString("count")) + return 1; + if (name == ConstString("weak_count")) + return 2; + return UINT32_MAX; +} + +lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEnd::~LibcxxSharedPtrSyntheticFrontEnd () +{} + +SyntheticChildrenFrontEnd* +lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEndCreator (CXXSyntheticChildren*, lldb::ValueObjectSP valobj_sp) +{ + if (!valobj_sp) + return NULL; + return (new LibcxxSharedPtrSyntheticFrontEnd(valobj_sp)); +} + +lldb_private::formatters::LibcxxStdVectorSyntheticFrontEnd::LibcxxStdVectorSyntheticFrontEnd (lldb::ValueObjectSP valobj_sp) : + SyntheticChildrenFrontEnd(*valobj_sp.get()), + m_start(NULL), + m_finish(NULL), + m_element_type(), + m_element_size(0), + m_children() +{ + if (valobj_sp) + Update(); +} + +size_t +lldb_private::formatters::LibcxxStdVectorSyntheticFrontEnd::CalculateNumChildren () +{ + if (!m_start || !m_finish) + return 0; + uint64_t start_val = m_start->GetValueAsUnsigned(0); + uint64_t finish_val = m_finish->GetValueAsUnsigned(0); + + if (start_val == 0 || finish_val == 0) + return 0; + + if (start_val >= finish_val) + return 0; + + size_t num_children = (finish_val - start_val); + if (num_children % m_element_size) + return 0; + return num_children/m_element_size; +} + +lldb::ValueObjectSP +lldb_private::formatters::LibcxxStdVectorSyntheticFrontEnd::GetChildAtIndex (size_t idx) +{ + if (!m_start || !m_finish) + return lldb::ValueObjectSP(); + + auto cached = m_children.find(idx); + if (cached != m_children.end()) + return cached->second; + + uint64_t offset = idx * m_element_size; + offset = offset + m_start->GetValueAsUnsigned(0); + StreamString name; + name.Printf("[%zu]",idx); + ValueObjectSP child_sp = ValueObject::CreateValueObjectFromAddress(name.GetData(), offset, m_backend.GetExecutionContextRef(), m_element_type); + m_children[idx] = child_sp; + return child_sp; +} + +bool +lldb_private::formatters::LibcxxStdVectorSyntheticFrontEnd::Update() +{ + m_start = m_finish = NULL; + m_children.clear(); + ValueObjectSP data_type_finder_sp(m_backend.GetChildMemberWithName(ConstString("__end_cap_"),true)); + if (!data_type_finder_sp) + return false; + data_type_finder_sp = data_type_finder_sp->GetChildMemberWithName(ConstString("__first_"),true); + if (!data_type_finder_sp) + return false; + m_element_type = data_type_finder_sp->GetClangType().GetPointeeType(); + m_element_size = m_element_type.GetByteSize(); + + if (m_element_size > 0) + { + // store raw pointers or end up with a circular dependency + m_start = m_backend.GetChildMemberWithName(ConstString("__begin_"),true).get(); + m_finish = m_backend.GetChildMemberWithName(ConstString("__end_"),true).get(); + } + return false; +} + +bool +lldb_private::formatters::LibcxxStdVectorSyntheticFrontEnd::MightHaveChildren () +{ + return true; +} + +size_t +lldb_private::formatters::LibcxxStdVectorSyntheticFrontEnd::GetIndexOfChildWithName (const ConstString &name) +{ + if (!m_start || !m_finish) + return UINT32_MAX; + return ExtractIndexFromString(name.GetCString()); +} + +lldb_private::formatters::LibcxxStdVectorSyntheticFrontEnd::~LibcxxStdVectorSyntheticFrontEnd () +{ + // these need to stay around because they are child objects who will follow their parent's life cycle + // delete m_start; + // delete m_finish; +} + +SyntheticChildrenFrontEnd* +lldb_private::formatters::LibcxxStdVectorSyntheticFrontEndCreator (CXXSyntheticChildren*, lldb::ValueObjectSP valobj_sp) +{ + if (!valobj_sp) + return NULL; + return (new LibcxxStdVectorSyntheticFrontEnd(valobj_sp)); +} + +bool +lldb_private::formatters::LibcxxContainerSummaryProvider (ValueObject& valobj, Stream& stream) +{ + if (valobj.IsPointerType()) + { + uint64_t value = valobj.GetValueAsUnsigned(0); + if (!value) + return false; + stream.Printf("0x%016" PRIx64 " ", value); + } + return Debugger::FormatPrompt("size=${svar%#}", NULL, NULL, NULL, stream, &valobj); +} diff --git a/contrib/llvm/tools/lldb/source/DataFormatters/LibCxxList.cpp b/contrib/llvm/tools/lldb/source/DataFormatters/LibCxxList.cpp new file mode 100644 index 00000000000..de2ca1b2045 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/DataFormatters/LibCxxList.cpp @@ -0,0 +1,310 @@ +//===-- LibCxxList.cpp -------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "lldb/DataFormatters/CXXFormatterFunctions.h" + +#include "lldb/Core/DataBufferHeap.h" +#include "lldb/Core/Error.h" +#include "lldb/Core/Stream.h" +#include "lldb/Core/ValueObject.h" +#include "lldb/Core/ValueObjectConstResult.h" +#include "lldb/Host/Endian.h" +#include "lldb/Symbol/ClangASTContext.h" +#include "lldb/Target/ObjCLanguageRuntime.h" +#include "lldb/Target/Target.h" + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::formatters; + +class ListEntry +{ +public: + ListEntry () {} + ListEntry (ValueObjectSP entry_sp) : m_entry_sp(entry_sp) {} + ListEntry (const ListEntry& rhs) : m_entry_sp(rhs.m_entry_sp) {} + ListEntry (ValueObject* entry) : m_entry_sp(entry ? entry->GetSP() : ValueObjectSP()) {} + + ValueObjectSP + next () + { + if (!m_entry_sp) + return m_entry_sp; + return m_entry_sp->GetChildMemberWithName(ConstString("__next_"), true); + } + + ValueObjectSP + prev () + { + if (!m_entry_sp) + return m_entry_sp; + return m_entry_sp->GetChildMemberWithName(ConstString("__prev_"), true); + } + + uint64_t + value () + { + if (!m_entry_sp) + return 0; + return m_entry_sp->GetValueAsUnsigned(0); + } + + bool + null() + { + return (value() == 0); + } + + ValueObjectSP + GetEntry () + { + return m_entry_sp; + } + + void + SetEntry (ValueObjectSP entry) + { + m_entry_sp = entry; + } + + bool + operator == (const ListEntry& rhs) const + { + return (rhs.m_entry_sp.get() == m_entry_sp.get()); + } + +private: + ValueObjectSP m_entry_sp; +}; + +class ListIterator +{ +public: + ListIterator () {} + ListIterator (ListEntry entry) : m_entry(entry) {} + ListIterator (ValueObjectSP entry) : m_entry(entry) {} + ListIterator (const ListIterator& rhs) : m_entry(rhs.m_entry) {} + ListIterator (ValueObject* entry) : m_entry(entry) {} + + ValueObjectSP + value () + { + return m_entry.GetEntry(); + } + + ValueObjectSP + advance (size_t count) + { + if (count == 0) + return m_entry.GetEntry(); + if (count == 1) + { + next (); + return m_entry.GetEntry(); + } + while (count > 0) + { + next (); + count--; + if (m_entry.null()) + return lldb::ValueObjectSP(); + } + return m_entry.GetEntry(); + } + + bool + operator == (const ListIterator& rhs) const + { + return (rhs.m_entry == m_entry); + } + +protected: + void + next () + { + m_entry.SetEntry(m_entry.next()); + } + + void + prev () + { + m_entry.SetEntry(m_entry.prev()); + } +private: + ListEntry m_entry; +}; + +lldb_private::formatters::LibcxxStdListSyntheticFrontEnd::LibcxxStdListSyntheticFrontEnd (lldb::ValueObjectSP valobj_sp) : +SyntheticChildrenFrontEnd(*valobj_sp.get()), +m_list_capping_size(0), +m_node_address(), +m_head(NULL), +m_tail(NULL), +m_element_type(), +m_count(UINT32_MAX), +m_children() +{ + if (valobj_sp) + Update(); +} + +bool +lldb_private::formatters::LibcxxStdListSyntheticFrontEnd::HasLoop() +{ + if (g_use_loop_detect == false) + return false; + ListEntry slow(m_head); + ListEntry fast1(m_head); + ListEntry fast2(m_head); + while (slow.next() && slow.next()->GetValueAsUnsigned(0) != m_node_address) + { + auto slow_value = slow.value(); + fast1.SetEntry(fast2.next()); + fast2.SetEntry(fast1.next()); + if (fast1.value() == slow_value || fast2.value() == slow_value) + return true; + slow.SetEntry(slow.next()); + } + return false; +} + +size_t +lldb_private::formatters::LibcxxStdListSyntheticFrontEnd::CalculateNumChildren () +{ + if (m_count != UINT32_MAX) + return m_count; + if (!m_head || !m_tail || m_node_address == 0) + return 0; + ValueObjectSP size_alloc(m_backend.GetChildMemberWithName(ConstString("__size_alloc_"), true)); + if (size_alloc) + { + ValueObjectSP first(size_alloc->GetChildMemberWithName(ConstString("__first_"), true)); + if (first) + { + m_count = first->GetValueAsUnsigned(UINT32_MAX); + } + } + if (m_count != UINT32_MAX) + { + if (!HasLoop()) + return m_count; + return m_count = 0; + } + else + { + uint64_t next_val = m_head->GetValueAsUnsigned(0); + uint64_t prev_val = m_tail->GetValueAsUnsigned(0); + if (next_val == 0 || prev_val == 0) + return 0; + if (next_val == m_node_address) + return 0; + if (next_val == prev_val) + return 1; + if (HasLoop()) + return 0; + uint64_t size = 2; + ListEntry current(m_head); + while (current.next() && current.next()->GetValueAsUnsigned(0) != m_node_address) + { + size++; + current.SetEntry(current.next()); + if (size > m_list_capping_size) + break; + } + return m_count = (size-1); + } +} + +lldb::ValueObjectSP +lldb_private::formatters::LibcxxStdListSyntheticFrontEnd::GetChildAtIndex (size_t idx) +{ + if (idx >= CalculateNumChildren()) + return lldb::ValueObjectSP(); + + if (!m_head || !m_tail || m_node_address == 0) + return lldb::ValueObjectSP(); + + auto cached = m_children.find(idx); + if (cached != m_children.end()) + return cached->second; + + ListIterator current(m_head); + ValueObjectSP current_sp(current.advance(idx)); + if (!current_sp) + return lldb::ValueObjectSP(); + current_sp = current_sp->GetChildMemberWithName(ConstString("__value_"), true); + if (!current_sp) + return lldb::ValueObjectSP(); + // we need to copy current_sp into a new object otherwise we will end up with all items named __value_ + DataExtractor data; + current_sp->GetData(data); + StreamString name; + name.Printf("[%zu]",idx); + return (m_children[idx] = ValueObject::CreateValueObjectFromData(name.GetData(), data, m_backend.GetExecutionContextRef(), m_element_type)); +} + +bool +lldb_private::formatters::LibcxxStdListSyntheticFrontEnd::Update() +{ + m_head = m_tail = NULL; + m_node_address = 0; + m_count = UINT32_MAX; + Error err; + ValueObjectSP backend_addr(m_backend.AddressOf(err)); + m_list_capping_size = 0; + if (m_backend.GetTargetSP()) + m_list_capping_size = m_backend.GetTargetSP()->GetMaximumNumberOfChildrenToDisplay(); + if (m_list_capping_size == 0) + m_list_capping_size = 255; + if (err.Fail() || backend_addr.get() == NULL) + return false; + m_node_address = backend_addr->GetValueAsUnsigned(0); + if (!m_node_address || m_node_address == LLDB_INVALID_ADDRESS) + return false; + ValueObjectSP impl_sp(m_backend.GetChildMemberWithName(ConstString("__end_"),true)); + if (!impl_sp) + return false; + ClangASTType list_type = m_backend.GetClangType(); + if (list_type.IsReferenceType()) + list_type = list_type.GetNonReferenceType(); + + if (list_type.GetNumTemplateArguments() == 0) + return false; + lldb::TemplateArgumentKind kind; + m_element_type = list_type.GetTemplateArgument(0, kind); + m_head = impl_sp->GetChildMemberWithName(ConstString("__next_"), true).get(); + m_tail = impl_sp->GetChildMemberWithName(ConstString("__prev_"), true).get(); + return false; +} + +bool +lldb_private::formatters::LibcxxStdListSyntheticFrontEnd::MightHaveChildren () +{ + return true; +} + +size_t +lldb_private::formatters::LibcxxStdListSyntheticFrontEnd::GetIndexOfChildWithName (const ConstString &name) +{ + return ExtractIndexFromString(name.GetCString()); +} + +lldb_private::formatters::LibcxxStdListSyntheticFrontEnd::~LibcxxStdListSyntheticFrontEnd () +{} + +SyntheticChildrenFrontEnd* +lldb_private::formatters::LibcxxStdListSyntheticFrontEndCreator (CXXSyntheticChildren*, lldb::ValueObjectSP valobj_sp) +{ + if (!valobj_sp) + return NULL; + return (new LibcxxStdListSyntheticFrontEnd(valobj_sp)); +} + diff --git a/contrib/llvm/tools/lldb/source/DataFormatters/LibCxxMap.cpp b/contrib/llvm/tools/lldb/source/DataFormatters/LibCxxMap.cpp new file mode 100644 index 00000000000..5daa40f15f3 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/DataFormatters/LibCxxMap.cpp @@ -0,0 +1,409 @@ +//===-- LibCxxList.cpp -------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "lldb/DataFormatters/CXXFormatterFunctions.h" + +#include "lldb/Core/DataBufferHeap.h" +#include "lldb/Core/Error.h" +#include "lldb/Core/Stream.h" +#include "lldb/Core/ValueObject.h" +#include "lldb/Core/ValueObjectConstResult.h" +#include "lldb/Host/Endian.h" +#include "lldb/Symbol/ClangASTContext.h" +#include "lldb/Target/ObjCLanguageRuntime.h" +#include "lldb/Target/Target.h" + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::formatters; + +class MapEntry +{ +public: + MapEntry () {} + MapEntry (ValueObjectSP entry_sp) : m_entry_sp(entry_sp) {} + MapEntry (const MapEntry& rhs) : m_entry_sp(rhs.m_entry_sp) {} + MapEntry (ValueObject* entry) : m_entry_sp(entry ? entry->GetSP() : ValueObjectSP()) {} + + ValueObjectSP + left () + { + if (!m_entry_sp) + return m_entry_sp; + return m_entry_sp->GetChildMemberWithName(ConstString("__left_"), true); + } + + ValueObjectSP + right () + { + if (!m_entry_sp) + return m_entry_sp; + return m_entry_sp->GetChildMemberWithName(ConstString("__right_"), true); + } + + ValueObjectSP + parent () + { + if (!m_entry_sp) + return m_entry_sp; + return m_entry_sp->GetChildMemberWithName(ConstString("__parent_"), true); + } + + uint64_t + value () + { + if (!m_entry_sp) + return 0; + return m_entry_sp->GetValueAsUnsigned(0); + } + + bool + error () + { + if (!m_entry_sp) + return true; + return m_entry_sp->GetError().Fail(); + } + + bool + null() + { + return (value() == 0); + } + + ValueObjectSP + GetEntry () + { + return m_entry_sp; + } + + void + SetEntry (ValueObjectSP entry) + { + m_entry_sp = entry; + } + + bool + operator == (const MapEntry& rhs) const + { + return (rhs.m_entry_sp.get() == m_entry_sp.get()); + } + +private: + ValueObjectSP m_entry_sp; +}; + +class MapIterator +{ +public: + MapIterator () {} + MapIterator (MapEntry entry, size_t depth = 0) : m_entry(entry), m_max_depth(depth), m_error(false) {} + MapIterator (ValueObjectSP entry, size_t depth = 0) : m_entry(entry), m_max_depth(depth), m_error(false) {} + MapIterator (const MapIterator& rhs) : m_entry(rhs.m_entry),m_max_depth(rhs.m_max_depth), m_error(false) {} + MapIterator (ValueObject* entry, size_t depth = 0) : m_entry(entry), m_max_depth(depth), m_error(false) {} + + ValueObjectSP + value () + { + return m_entry.GetEntry(); + } + + ValueObjectSP + advance (size_t count) + { + if (m_error) + return lldb::ValueObjectSP(); + if (count == 0) + return m_entry.GetEntry(); + if (count == 1) + { + next (); + return m_entry.GetEntry(); + } + size_t steps = 0; + while (count > 0) + { + if (m_error) + return lldb::ValueObjectSP(); + next (); + count--; + if (m_entry.null()) + return lldb::ValueObjectSP(); + steps++; + if (steps > m_max_depth) + return lldb::ValueObjectSP(); + } + return m_entry.GetEntry(); + } +protected: + void + next () + { + m_entry.SetEntry(increment(m_entry.GetEntry())); + } + +private: + ValueObjectSP + tree_min (ValueObjectSP x_sp) + { + MapEntry x(x_sp); + if (x.null()) + return ValueObjectSP(); + MapEntry left(x.left()); + size_t steps = 0; + while (left.null() == false) + { + if (left.error()) + { + m_error = true; + return lldb::ValueObjectSP(); + } + x.SetEntry(left.GetEntry()); + left.SetEntry(x.left()); + steps++; + if (steps > m_max_depth) + return lldb::ValueObjectSP(); + } + return x.GetEntry(); + } + + ValueObjectSP + tree_max (ValueObjectSP x_sp) + { + MapEntry x(x_sp); + if (x.null()) + return ValueObjectSP(); + MapEntry right(x.right()); + size_t steps = 0; + while (right.null() == false) + { + if (right.error()) + return lldb::ValueObjectSP(); + x.SetEntry(right.GetEntry()); + right.SetEntry(x.right()); + steps++; + if (steps > m_max_depth) + return lldb::ValueObjectSP(); + } + return x.GetEntry(); + } + + bool + is_left_child (ValueObjectSP x_sp) + { + MapEntry x(x_sp); + if (x.null()) + return false; + MapEntry rhs(x.parent()); + rhs.SetEntry(rhs.left()); + return x.value() == rhs.value(); + } + + ValueObjectSP + increment (ValueObjectSP x_sp) + { + MapEntry node(x_sp); + if (node.null()) + return ValueObjectSP(); + MapEntry right(node.right()); + if (right.null() == false) + return tree_min(right.GetEntry()); + size_t steps = 0; + while (!is_left_child(node.GetEntry())) + { + if (node.error()) + { + m_error = true; + return lldb::ValueObjectSP(); + } + node.SetEntry(node.parent()); + steps++; + if (steps > m_max_depth) + return lldb::ValueObjectSP(); + } + return node.parent(); + } + + MapEntry m_entry; + size_t m_max_depth; + bool m_error; +}; + +lldb_private::formatters::LibcxxStdMapSyntheticFrontEnd::LibcxxStdMapSyntheticFrontEnd (lldb::ValueObjectSP valobj_sp) : +SyntheticChildrenFrontEnd(*valobj_sp.get()), +m_tree(NULL), +m_root_node(NULL), +m_element_type(), +m_skip_size(UINT32_MAX), +m_count(UINT32_MAX), +m_children() +{ + if (valobj_sp) + Update(); +} + +size_t +lldb_private::formatters::LibcxxStdMapSyntheticFrontEnd::CalculateNumChildren () +{ + if (m_count != UINT32_MAX) + return m_count; + if (m_tree == NULL) + return 0; + ValueObjectSP m_item(m_tree->GetChildMemberWithName(ConstString("__pair3_"), true)); + if (!m_item) + return 0; + m_item = m_item->GetChildMemberWithName(ConstString("__first_"), true); + if (!m_item) + return 0; + m_count = m_item->GetValueAsUnsigned(0); + return m_count; +} + +bool +lldb_private::formatters::LibcxxStdMapSyntheticFrontEnd::GetDataType() +{ + if (m_element_type.GetOpaqueQualType() && m_element_type.GetASTContext()) + return true; + m_element_type.Clear(); + ValueObjectSP deref; + Error error; + deref = m_root_node->Dereference(error); + if (!deref || error.Fail()) + return false; + deref = deref->GetChildMemberWithName(ConstString("__value_"), true); + if (!deref) + return false; + m_element_type = deref->GetClangType(); + return true; +} + +void +lldb_private::formatters::LibcxxStdMapSyntheticFrontEnd::GetValueOffset (const lldb::ValueObjectSP& node) +{ + if (m_skip_size != UINT32_MAX) + return; + if (!node) + return; + ClangASTType node_type(node->GetClangType()); + uint64_t bit_offset; + if (node_type.GetIndexOfFieldWithName("__value_", NULL, &bit_offset) == UINT32_MAX) + return; + m_skip_size = bit_offset / 8u; +} + +lldb::ValueObjectSP +lldb_private::formatters::LibcxxStdMapSyntheticFrontEnd::GetChildAtIndex (size_t idx) +{ + if (idx >= CalculateNumChildren()) + return lldb::ValueObjectSP(); + if (m_tree == NULL || m_root_node == NULL) + return lldb::ValueObjectSP(); + + auto cached = m_children.find(idx); + if (cached != m_children.end()) + return cached->second; + + bool need_to_skip = (idx > 0); + MapIterator iterator(m_root_node, CalculateNumChildren()); + ValueObjectSP iterated_sp(iterator.advance(idx)); + if (iterated_sp.get() == NULL) + { + // this tree is garbage - stop + m_tree = NULL; // this will stop all future searches until an Update() happens + return iterated_sp; + } + if (GetDataType()) + { + if (!need_to_skip) + { + Error error; + iterated_sp = iterated_sp->Dereference(error); + if (!iterated_sp || error.Fail()) + { + m_tree = NULL; + return lldb::ValueObjectSP(); + } + GetValueOffset(iterated_sp); + iterated_sp = iterated_sp->GetChildMemberWithName(ConstString("__value_"), true); + if (!iterated_sp) + { + m_tree = NULL; + return lldb::ValueObjectSP(); + } + } + else + { + // because of the way our debug info is made, we need to read item 0 first + // so that we can cache information used to generate other elements + if (m_skip_size == UINT32_MAX) + GetChildAtIndex(0); + if (m_skip_size == UINT32_MAX) + { + m_tree = NULL; + return lldb::ValueObjectSP(); + } + iterated_sp = iterated_sp->GetSyntheticChildAtOffset(m_skip_size, m_element_type, true); + if (!iterated_sp) + { + m_tree = NULL; + return lldb::ValueObjectSP(); + } + } + } + else + { + m_tree = NULL; + return lldb::ValueObjectSP(); + } + // at this point we have a valid + // we need to copy current_sp into a new object otherwise we will end up with all items named __value_ + DataExtractor data; + iterated_sp->GetData(data); + StreamString name; + name.Printf("[%zu]",idx); + return (m_children[idx] = ValueObject::CreateValueObjectFromData(name.GetData(), data, m_backend.GetExecutionContextRef(), m_element_type)); +} + +bool +lldb_private::formatters::LibcxxStdMapSyntheticFrontEnd::Update() +{ + m_count = UINT32_MAX; + m_tree = m_root_node = NULL; + m_children.clear(); + m_tree = m_backend.GetChildMemberWithName(ConstString("__tree_"), true).get(); + if (!m_tree) + return false; + m_root_node = m_tree->GetChildMemberWithName(ConstString("__begin_node_"), true).get(); + return false; +} + +bool +lldb_private::formatters::LibcxxStdMapSyntheticFrontEnd::MightHaveChildren () +{ + return true; +} + +size_t +lldb_private::formatters::LibcxxStdMapSyntheticFrontEnd::GetIndexOfChildWithName (const ConstString &name) +{ + return ExtractIndexFromString(name.GetCString()); +} + +lldb_private::formatters::LibcxxStdMapSyntheticFrontEnd::~LibcxxStdMapSyntheticFrontEnd () +{} + +SyntheticChildrenFrontEnd* +lldb_private::formatters::LibcxxStdMapSyntheticFrontEndCreator (CXXSyntheticChildren*, lldb::ValueObjectSP valobj_sp) +{ + if (!valobj_sp) + return NULL; + return (new LibcxxStdMapSyntheticFrontEnd(valobj_sp)); +} diff --git a/contrib/llvm/tools/lldb/source/DataFormatters/LibStdcpp.cpp b/contrib/llvm/tools/lldb/source/DataFormatters/LibStdcpp.cpp new file mode 100644 index 00000000000..e0f23cc35e3 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/DataFormatters/LibStdcpp.cpp @@ -0,0 +1,331 @@ +//===-- LibStdcpp.cpp ---------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "lldb/DataFormatters/CXXFormatterFunctions.h" + +#include "lldb/Core/DataBufferHeap.h" +#include "lldb/Core/Error.h" +#include "lldb/Core/Stream.h" +#include "lldb/Core/ValueObject.h" +#include "lldb/Core/ValueObjectConstResult.h" +#include "lldb/Host/Endian.h" +#include "lldb/Symbol/ClangASTContext.h" +#include "lldb/Target/ObjCLanguageRuntime.h" +#include "lldb/Target/Target.h" + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::formatters; + +lldb_private::formatters::LibstdcppVectorBoolSyntheticFrontEnd::LibstdcppVectorBoolSyntheticFrontEnd (lldb::ValueObjectSP valobj_sp) : +SyntheticChildrenFrontEnd(*valobj_sp.get()), +m_exe_ctx_ref(), +m_count(0), +m_base_data_address(0), +m_options() +{ + if (valobj_sp) + Update(); + m_options.SetCoerceToId(false) + .SetUnwindOnError(true) + .SetKeepInMemory(true) + .SetUseDynamic(lldb::eDynamicCanRunTarget); +} + +size_t +lldb_private::formatters::LibstdcppVectorBoolSyntheticFrontEnd::CalculateNumChildren () +{ + return m_count; +} + +lldb::ValueObjectSP +lldb_private::formatters::LibstdcppVectorBoolSyntheticFrontEnd::GetChildAtIndex (size_t idx) +{ + if (idx >= m_count) + return ValueObjectSP(); + if (m_base_data_address == 0 || m_count == 0) + return ValueObjectSP(); + size_t byte_idx = (idx >> 3); // divide by 8 to get byte index + size_t bit_index = (idx & 7); // efficient idx % 8 for bit index + lldb::addr_t byte_location = m_base_data_address + byte_idx; + ProcessSP process_sp(m_exe_ctx_ref.GetProcessSP()); + if (!process_sp) + return ValueObjectSP(); + uint8_t byte = 0; + uint8_t mask = 0; + Error err; + size_t bytes_read = process_sp->ReadMemory(byte_location, &byte, 1, err); + if (err.Fail() || bytes_read == 0) + return ValueObjectSP(); + switch (bit_index) + { + case 0: + mask = 1; break; + case 1: + mask = 2; break; + case 2: + mask = 4; break; + case 3: + mask = 8; break; + case 4: + mask = 16; break; + case 5: + mask = 32; break; + case 6: + mask = 64; break; + case 7: + mask = 128; break; + default: + return ValueObjectSP(); + } + bool bit_set = ((byte & mask) != 0); + Target& target(process_sp->GetTarget()); + ValueObjectSP retval_sp; + if (bit_set) + target.EvaluateExpression("(bool)true", NULL, retval_sp); + else + target.EvaluateExpression("(bool)false", NULL, retval_sp); + StreamString name; name.Printf("[%zu]",idx); + if (retval_sp) + retval_sp->SetName(ConstString(name.GetData())); + return retval_sp; +} + +/*((std::vector >) vBool = { + (std::_Bvector_base >) std::_Bvector_base > = { + (std::_Bvector_base >::_Bvector_impl) _M_impl = { + (std::_Bit_iterator) _M_start = { + (std::_Bit_iterator_base) std::_Bit_iterator_base = { + (_Bit_type *) _M_p = 0x0016b160 + (unsigned int) _M_offset = 0 + } + } + (std::_Bit_iterator) _M_finish = { + (std::_Bit_iterator_base) std::_Bit_iterator_base = { + (_Bit_type *) _M_p = 0x0016b16c + (unsigned int) _M_offset = 16 + } + } + (_Bit_type *) _M_end_of_storage = 0x0016b170 + } + } + } + */ + +bool +lldb_private::formatters::LibstdcppVectorBoolSyntheticFrontEnd::Update() +{ + ValueObjectSP valobj_sp = m_backend.GetSP(); + if (!valobj_sp) + return false; + if (!valobj_sp) + return false; + m_exe_ctx_ref = valobj_sp->GetExecutionContextRef(); + + ValueObjectSP m_impl_sp(valobj_sp->GetChildMemberWithName(ConstString("_M_impl"), true)); + if (!m_impl_sp) + return false; + + ValueObjectSP m_start_sp(m_impl_sp->GetChildMemberWithName(ConstString("_M_start"), true)); + ValueObjectSP m_finish_sp(m_impl_sp->GetChildMemberWithName(ConstString("_M_finish"), true)); + + ValueObjectSP start_p_sp, finish_p_sp, finish_offset_sp; + + if (!m_start_sp || !m_finish_sp) + return false; + + start_p_sp = m_start_sp->GetChildMemberWithName(ConstString("_M_p"), true); + finish_p_sp = m_finish_sp->GetChildMemberWithName(ConstString("_M_p"), true); + finish_offset_sp = m_finish_sp->GetChildMemberWithName(ConstString("_M_offset"), true); + + if (!start_p_sp || !finish_offset_sp || !finish_p_sp) + return false; + + m_base_data_address = start_p_sp->GetValueAsUnsigned(0); + if (!m_base_data_address) + return false; + + lldb::addr_t end_data_address(finish_p_sp->GetValueAsUnsigned(0)); + if (!end_data_address) + return false; + + if (end_data_address < m_base_data_address) + return false; + else + m_count = finish_offset_sp->GetValueAsUnsigned(0) + (end_data_address-m_base_data_address)*8; + + return true; +} + +bool +lldb_private::formatters::LibstdcppVectorBoolSyntheticFrontEnd::MightHaveChildren () +{ + return true; +} + +size_t +lldb_private::formatters::LibstdcppVectorBoolSyntheticFrontEnd::GetIndexOfChildWithName (const ConstString &name) +{ + if (!m_count || !m_base_data_address) + return UINT32_MAX; + const char* item_name = name.GetCString(); + uint32_t idx = ExtractIndexFromString(item_name); + if (idx < UINT32_MAX && idx >= CalculateNumChildren()) + return UINT32_MAX; + return idx; +} + +lldb_private::formatters::LibstdcppVectorBoolSyntheticFrontEnd::~LibstdcppVectorBoolSyntheticFrontEnd () +{} + +SyntheticChildrenFrontEnd* +lldb_private::formatters::LibstdcppVectorBoolSyntheticFrontEndCreator (CXXSyntheticChildren*, lldb::ValueObjectSP valobj_sp) +{ + if (!valobj_sp) + return NULL; + return (new LibstdcppVectorBoolSyntheticFrontEnd(valobj_sp)); +} + +/* + (std::_Rb_tree_iterator, std::allocator > > >) ibeg = { + (_Base_ptr) _M_node = 0x0000000100103910 { + (std::_Rb_tree_color) _M_color = _S_black + (std::_Rb_tree_node_base::_Base_ptr) _M_parent = 0x00000001001038c0 + (std::_Rb_tree_node_base::_Base_ptr) _M_left = 0x0000000000000000 + (std::_Rb_tree_node_base::_Base_ptr) _M_right = 0x0000000000000000 + } + } + */ + +lldb_private::formatters::LibstdcppMapIteratorSyntheticFrontEnd::LibstdcppMapIteratorSyntheticFrontEnd (lldb::ValueObjectSP valobj_sp) : + SyntheticChildrenFrontEnd(*valobj_sp.get()), + m_exe_ctx_ref(), + m_pair_address(0), + m_pair_type(), + m_options(), + m_pair_sp() +{ + if (valobj_sp) + Update(); + m_options.SetCoerceToId(false) + .SetUnwindOnError(true) + .SetKeepInMemory(true) + .SetUseDynamic(lldb::eDynamicCanRunTarget); +} + +bool +lldb_private::formatters::LibstdcppMapIteratorSyntheticFrontEnd::Update() +{ + ValueObjectSP valobj_sp = m_backend.GetSP(); + if (!valobj_sp) + return false; + + TargetSP target_sp(valobj_sp->GetTargetSP()); + + if (!target_sp) + return false; + + bool is_64bit = (target_sp->GetArchitecture().GetAddressByteSize() == 8); + + if (!valobj_sp) + return false; + m_exe_ctx_ref = valobj_sp->GetExecutionContextRef(); + + ValueObjectSP _M_node_sp(valobj_sp->GetChildMemberWithName(ConstString("_M_node"), true)); + if (!_M_node_sp) + return false; + + m_pair_address = _M_node_sp->GetValueAsUnsigned(0); + if (m_pair_address == 0) + return false; + + m_pair_address += (is_64bit ? 32 : 16); + + ClangASTType my_type(valobj_sp->GetClangType()); + if (my_type.GetNumTemplateArguments() >= 1) + { + TemplateArgumentKind kind; + ClangASTType pair_type = my_type.GetTemplateArgument(0, kind); + if (kind != eTemplateArgumentKindType && kind != eTemplateArgumentKindTemplate && kind != eTemplateArgumentKindTemplateExpansion) + return false; + m_pair_type = pair_type; + } + else + return false; + + return true; +} + +size_t +lldb_private::formatters::LibstdcppMapIteratorSyntheticFrontEnd::CalculateNumChildren () +{ + return 2; +} + +lldb::ValueObjectSP +lldb_private::formatters::LibstdcppMapIteratorSyntheticFrontEnd::GetChildAtIndex (size_t idx) +{ + if (m_pair_address != 0 && m_pair_type) + { + if (!m_pair_sp) + m_pair_sp = ValueObject::CreateValueObjectFromAddress("pair", m_pair_address, m_exe_ctx_ref, m_pair_type); + if (m_pair_sp) + return m_pair_sp->GetChildAtIndex(idx, true); + } + return lldb::ValueObjectSP(); +} + +bool +lldb_private::formatters::LibstdcppMapIteratorSyntheticFrontEnd::MightHaveChildren () +{ + return true; +} + +size_t +lldb_private::formatters::LibstdcppMapIteratorSyntheticFrontEnd::GetIndexOfChildWithName (const ConstString &name) +{ + if (name == ConstString("first")) + return 0; + if (name == ConstString("second")) + return 1; + return UINT32_MAX; +} + +lldb_private::formatters::LibstdcppMapIteratorSyntheticFrontEnd::~LibstdcppMapIteratorSyntheticFrontEnd () +{} + +SyntheticChildrenFrontEnd* +lldb_private::formatters::LibstdcppMapIteratorSyntheticFrontEndCreator (CXXSyntheticChildren*, lldb::ValueObjectSP valobj_sp) +{ + if (!valobj_sp) + return NULL; + return (new LibstdcppMapIteratorSyntheticFrontEnd(valobj_sp)); +} + +/* + (lldb) fr var ibeg --ptr-depth 1 + (__gnu_cxx::__normal_iterator > >) ibeg = { + _M_current = 0x00000001001037a0 { + *_M_current = 1 + } + } + */ + +SyntheticChildrenFrontEnd* +lldb_private::formatters::LibStdcppVectorIteratorSyntheticFrontEndCreator (CXXSyntheticChildren*, lldb::ValueObjectSP valobj_sp) +{ + static ConstString g_item_name; + if (!g_item_name) + g_item_name.SetCString("_M_current"); + if (!valobj_sp) + return NULL; + return (new VectorIteratorSyntheticFrontEnd(valobj_sp,g_item_name)); +} diff --git a/contrib/llvm/tools/lldb/source/DataFormatters/NSArray.cpp b/contrib/llvm/tools/lldb/source/DataFormatters/NSArray.cpp new file mode 100644 index 00000000000..d8ee9bfa8a4 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/DataFormatters/NSArray.cpp @@ -0,0 +1,371 @@ +//===-- NSArray.cpp ---------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "lldb/DataFormatters/CXXFormatterFunctions.h" + +#include "lldb/Core/DataBufferHeap.h" +#include "lldb/Core/Error.h" +#include "lldb/Core/Stream.h" +#include "lldb/Core/ValueObject.h" +#include "lldb/Core/ValueObjectConstResult.h" +#include "lldb/Host/Endian.h" +#include "lldb/Symbol/ClangASTContext.h" +#include "lldb/Target/ObjCLanguageRuntime.h" +#include "lldb/Target/Target.h" + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::formatters; + +bool +lldb_private::formatters::NSArraySummaryProvider (ValueObject& valobj, Stream& stream) +{ + ProcessSP process_sp = valobj.GetProcessSP(); + if (!process_sp) + return false; + + ObjCLanguageRuntime* runtime = (ObjCLanguageRuntime*)process_sp->GetLanguageRuntime(lldb::eLanguageTypeObjC); + + if (!runtime) + return false; + + ObjCLanguageRuntime::ClassDescriptorSP descriptor(runtime->GetClassDescriptor(valobj)); + + if (!descriptor.get() || !descriptor->IsValid()) + return false; + + uint32_t ptr_size = process_sp->GetAddressByteSize(); + + lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0); + + if (!valobj_addr) + return false; + + uint64_t value = 0; + + const char* class_name = descriptor->GetClassName().GetCString(); + + if (!class_name || !*class_name) + return false; + + if (!strcmp(class_name,"__NSArrayI")) + { + Error error; + value = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + ptr_size, ptr_size, 0, error); + if (error.Fail()) + return false; + } + else if (!strcmp(class_name,"__NSArrayM")) + { + Error error; + value = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + ptr_size, ptr_size, 0, error); + if (error.Fail()) + return false; + } + else if (!strcmp(class_name,"__NSCFArray")) + { + Error error; + value = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + 2 * ptr_size, ptr_size, 0, error); + if (error.Fail()) + return false; + } + else + { + if (!ExtractValueFromObjCExpression(valobj, "int", "count", value)) + return false; + } + + stream.Printf("@\"%" PRIu64 " object%s\"", + value, + value == 1 ? "" : "s"); + return true; +} + +lldb_private::formatters::NSArrayMSyntheticFrontEnd::NSArrayMSyntheticFrontEnd (lldb::ValueObjectSP valobj_sp) : + SyntheticChildrenFrontEnd(*valobj_sp.get()), + m_exe_ctx_ref(), + m_ptr_size(8), + m_data_32(NULL), + m_data_64(NULL) +{ + if (valobj_sp) + { + clang::ASTContext *ast = valobj_sp->GetClangType().GetASTContext(); + if (ast) + m_id_type = ClangASTType(ast, ast->ObjCBuiltinIdTy); + } +} + +size_t +lldb_private::formatters::NSArrayMSyntheticFrontEnd::CalculateNumChildren () +{ + if (m_data_32) + return m_data_32->_used; + if (m_data_64) + return m_data_64->_used; + return 0; +} + +lldb::ValueObjectSP +lldb_private::formatters::NSArrayMSyntheticFrontEnd::GetChildAtIndex (size_t idx) +{ + if (!m_data_32 && !m_data_64) + return lldb::ValueObjectSP(); + if (idx >= CalculateNumChildren()) + return lldb::ValueObjectSP(); + lldb::addr_t object_at_idx = (m_data_32 ? m_data_32->_data : m_data_64->_data); + size_t pyhs_idx = idx; + pyhs_idx += (m_data_32 ? m_data_32->offset : m_data_64->offset); + if ((m_data_32 ? m_data_32->_size : m_data_64->_size) <= pyhs_idx) + pyhs_idx -= (m_data_32 ? m_data_32->_size : m_data_64->_size); + object_at_idx += (pyhs_idx * m_ptr_size); + StreamString idx_name; + idx_name.Printf("[%zu]",idx); + lldb::ValueObjectSP retval_sp = ValueObject::CreateValueObjectFromAddress(idx_name.GetData(), + object_at_idx, + m_exe_ctx_ref, + m_id_type); + m_children.push_back(retval_sp); + return retval_sp; +} + +bool +lldb_private::formatters::NSArrayMSyntheticFrontEnd::Update() +{ + m_children.clear(); + ValueObjectSP valobj_sp = m_backend.GetSP(); + m_ptr_size = 0; + delete m_data_32; + m_data_32 = NULL; + delete m_data_64; + m_data_64 = NULL; + if (!valobj_sp) + return false; + m_exe_ctx_ref = valobj_sp->GetExecutionContextRef(); + Error error; + error.Clear(); + lldb::ProcessSP process_sp(valobj_sp->GetProcessSP()); + if (!process_sp) + return false; + m_ptr_size = process_sp->GetAddressByteSize(); + uint64_t data_location = valobj_sp->GetValueAsUnsigned(0) + m_ptr_size; + if (m_ptr_size == 4) + { + m_data_32 = new DataDescriptor_32(); + process_sp->ReadMemory (data_location, m_data_32, sizeof(DataDescriptor_32), error); + } + else + { + m_data_64 = new DataDescriptor_64(); + process_sp->ReadMemory (data_location, m_data_64, sizeof(DataDescriptor_64), error); + } + if (error.Fail()) + return false; + return false; +} + +bool +lldb_private::formatters::NSArrayMSyntheticFrontEnd::MightHaveChildren () +{ + return true; +} + +size_t +lldb_private::formatters::NSArrayMSyntheticFrontEnd::GetIndexOfChildWithName (const ConstString &name) +{ + if (!m_data_32 && !m_data_64) + return UINT32_MAX; + const char* item_name = name.GetCString(); + uint32_t idx = ExtractIndexFromString(item_name); + if (idx < UINT32_MAX && idx >= CalculateNumChildren()) + return UINT32_MAX; + return idx; +} + +lldb_private::formatters::NSArrayMSyntheticFrontEnd::~NSArrayMSyntheticFrontEnd () +{ + delete m_data_32; + m_data_32 = NULL; + delete m_data_64; + m_data_64 = NULL; +} + +lldb_private::formatters::NSArrayISyntheticFrontEnd::NSArrayISyntheticFrontEnd (lldb::ValueObjectSP valobj_sp) : + SyntheticChildrenFrontEnd (*valobj_sp.get()), + m_exe_ctx_ref (), + m_ptr_size (8), + m_items (0), + m_data_ptr (0) +{ + if (valobj_sp) + { + clang::ASTContext *ast = valobj_sp->GetClangType().GetASTContext(); + if (ast) + m_id_type = ClangASTType(ast, ast->ObjCBuiltinIdTy); + } +} + +lldb_private::formatters::NSArrayISyntheticFrontEnd::~NSArrayISyntheticFrontEnd () +{ +} + +size_t +lldb_private::formatters::NSArrayISyntheticFrontEnd::GetIndexOfChildWithName (const ConstString &name) +{ + const char* item_name = name.GetCString(); + uint32_t idx = ExtractIndexFromString(item_name); + if (idx < UINT32_MAX && idx >= CalculateNumChildren()) + return UINT32_MAX; + return idx; +} + +size_t +lldb_private::formatters::NSArrayISyntheticFrontEnd::CalculateNumChildren () +{ + return m_items; +} + +bool +lldb_private::formatters::NSArrayISyntheticFrontEnd::Update() +{ + m_ptr_size = 0; + m_items = 0; + m_data_ptr = 0; + m_children.clear(); + ValueObjectSP valobj_sp = m_backend.GetSP(); + if (!valobj_sp) + return false; + m_exe_ctx_ref = valobj_sp->GetExecutionContextRef(); + Error error; + error.Clear(); + lldb::ProcessSP process_sp(valobj_sp->GetProcessSP()); + if (!process_sp) + return false; + m_ptr_size = process_sp->GetAddressByteSize(); + uint64_t data_location = valobj_sp->GetValueAsUnsigned(0) + m_ptr_size; + m_items = process_sp->ReadPointerFromMemory(data_location, error); + if (error.Fail()) + return false; + m_data_ptr = data_location+m_ptr_size; + return false; +} + +bool +lldb_private::formatters::NSArrayISyntheticFrontEnd::MightHaveChildren () +{ + return true; +} + +lldb::ValueObjectSP +lldb_private::formatters::NSArrayISyntheticFrontEnd::GetChildAtIndex (size_t idx) +{ + if (idx >= CalculateNumChildren()) + return lldb::ValueObjectSP(); + lldb::addr_t object_at_idx = m_data_ptr; + object_at_idx += (idx * m_ptr_size); + ProcessSP process_sp = m_exe_ctx_ref.GetProcessSP(); + if (!process_sp) + return lldb::ValueObjectSP(); + Error error; + if (error.Fail()) + return lldb::ValueObjectSP(); + StreamString idx_name; + idx_name.Printf("[%zu]",idx); + lldb::ValueObjectSP retval_sp = ValueObject::CreateValueObjectFromAddress(idx_name.GetData(), object_at_idx, m_exe_ctx_ref, m_id_type); + m_children.push_back(retval_sp); + return retval_sp; +} + +SyntheticChildrenFrontEnd* lldb_private::formatters::NSArraySyntheticFrontEndCreator (CXXSyntheticChildren*, lldb::ValueObjectSP valobj_sp) +{ + lldb::ProcessSP process_sp (valobj_sp->GetProcessSP()); + if (!process_sp) + return NULL; + ObjCLanguageRuntime *runtime = (ObjCLanguageRuntime*)process_sp->GetLanguageRuntime(lldb::eLanguageTypeObjC); + if (!runtime) + return NULL; + + if (!valobj_sp->IsPointerType()) + { + Error error; + valobj_sp = valobj_sp->AddressOf(error); + if (error.Fail() || !valobj_sp) + return NULL; + } + + ObjCLanguageRuntime::ClassDescriptorSP descriptor(runtime->GetClassDescriptor(*valobj_sp.get())); + + if (!descriptor.get() || !descriptor->IsValid()) + return NULL; + + const char* class_name = descriptor->GetClassName().GetCString(); + + if (!class_name || !*class_name) + return NULL; + + if (!strcmp(class_name,"__NSArrayI")) + { + return (new NSArrayISyntheticFrontEnd(valobj_sp)); + } + else if (!strcmp(class_name,"__NSArrayM")) + { + return (new NSArrayMSyntheticFrontEnd(valobj_sp)); + } + else + { + return (new NSArrayCodeRunningSyntheticFrontEnd(valobj_sp)); + } +} + +lldb_private::formatters::NSArrayCodeRunningSyntheticFrontEnd::NSArrayCodeRunningSyntheticFrontEnd (lldb::ValueObjectSP valobj_sp) : +SyntheticChildrenFrontEnd(*valobj_sp.get()) +{} + +size_t +lldb_private::formatters::NSArrayCodeRunningSyntheticFrontEnd::CalculateNumChildren () +{ + uint64_t count = 0; + if (ExtractValueFromObjCExpression(m_backend, "int", "count", count)) + return count; + return 0; +} + +lldb::ValueObjectSP +lldb_private::formatters::NSArrayCodeRunningSyntheticFrontEnd::GetChildAtIndex (size_t idx) +{ + StreamString idx_name; + idx_name.Printf("[%zu]",idx); + lldb::ValueObjectSP valobj_sp = CallSelectorOnObject(m_backend,"id","objectAtIndex:",idx); + if (valobj_sp) + valobj_sp->SetName(ConstString(idx_name.GetData())); + return valobj_sp; +} + +bool +lldb_private::formatters::NSArrayCodeRunningSyntheticFrontEnd::Update() +{ + return false; +} + +bool +lldb_private::formatters::NSArrayCodeRunningSyntheticFrontEnd::MightHaveChildren () +{ + return true; +} + +size_t +lldb_private::formatters::NSArrayCodeRunningSyntheticFrontEnd::GetIndexOfChildWithName (const ConstString &name) +{ + return 0; +} + +lldb_private::formatters::NSArrayCodeRunningSyntheticFrontEnd::~NSArrayCodeRunningSyntheticFrontEnd () +{} diff --git a/contrib/llvm/tools/lldb/source/DataFormatters/NSDictionary.cpp b/contrib/llvm/tools/lldb/source/DataFormatters/NSDictionary.cpp new file mode 100644 index 00000000000..05a5dda39e5 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/DataFormatters/NSDictionary.cpp @@ -0,0 +1,579 @@ +//===-- NSDictionary.cpp ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "lldb/DataFormatters/CXXFormatterFunctions.h" + +#include "lldb/Core/DataBufferHeap.h" +#include "lldb/Core/Error.h" +#include "lldb/Core/Stream.h" +#include "lldb/Core/ValueObject.h" +#include "lldb/Core/ValueObjectConstResult.h" +#include "lldb/Host/Endian.h" +#include "lldb/Symbol/ClangASTContext.h" +#include "lldb/Target/ObjCLanguageRuntime.h" +#include "lldb/Target/Target.h" + +#include "clang/AST/DeclCXX.h" + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::formatters; + +static ClangASTType +GetLLDBNSPairType (TargetSP target_sp) +{ + ClangASTType clang_type; + + ClangASTContext *target_ast_context = target_sp->GetScratchClangASTContext(); + + if (target_ast_context) + { + clang::ASTContext *ast = target_ast_context->getASTContext(); + + if (ast) + { + const char* type_name = "__lldb_autogen_nspair"; + + clang::IdentifierInfo &myIdent = ast->Idents.get(type_name); + clang::DeclarationName myName = ast->DeclarationNames.getIdentifier(&myIdent); + + clang::DeclContext::lookup_const_result result = ast->getTranslationUnitDecl()->lookup(myName); + + for (clang::NamedDecl *named_decl : result) + { + if (const clang::CXXRecordDecl *record_decl = llvm::dyn_cast(named_decl)) + { + clang_type.SetClangType(ast, clang::QualType(record_decl->getTypeForDecl(), 0)); + break; + } + else + { + // somebody else (the user?) has defined a type with the magic name already - fail!!! + return clang_type; + } + } + + if (!clang_type) + { + clang_type = target_ast_context->CreateRecordType(NULL, lldb::eAccessPublic, type_name, clang::TTK_Struct, lldb::eLanguageTypeC); + + if (clang_type) + { + clang_type.StartTagDeclarationDefinition(); + ClangASTType id_clang_type = target_ast_context->GetBasicType (eBasicTypeObjCID); + clang_type.AddFieldToRecordType("key", id_clang_type, lldb::eAccessPublic, 0); + clang_type.AddFieldToRecordType("value", id_clang_type, lldb::eAccessPublic, 0); + clang_type.CompleteTagDeclarationDefinition(); + } + } + } + } + return clang_type; +} + +template +bool +lldb_private::formatters::NSDictionarySummaryProvider (ValueObject& valobj, Stream& stream) +{ + ProcessSP process_sp = valobj.GetProcessSP(); + if (!process_sp) + return false; + + ObjCLanguageRuntime* runtime = (ObjCLanguageRuntime*)process_sp->GetLanguageRuntime(lldb::eLanguageTypeObjC); + + if (!runtime) + return false; + + ObjCLanguageRuntime::ClassDescriptorSP descriptor(runtime->GetClassDescriptor(valobj)); + + if (!descriptor.get() || !descriptor->IsValid()) + return false; + + uint32_t ptr_size = process_sp->GetAddressByteSize(); + bool is_64bit = (ptr_size == 8); + + lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0); + + if (!valobj_addr) + return false; + + uint64_t value = 0; + + const char* class_name = descriptor->GetClassName().GetCString(); + + if (!class_name || !*class_name) + return false; + + if (!strcmp(class_name,"__NSDictionaryI")) + { + Error error; + value = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + ptr_size, ptr_size, 0, error); + if (error.Fail()) + return false; + value &= (is_64bit ? ~0xFC00000000000000UL : ~0xFC000000U); + } + else if (!strcmp(class_name,"__NSDictionaryM")) + { + Error error; + value = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + ptr_size, ptr_size, 0, error); + if (error.Fail()) + return false; + value &= (is_64bit ? ~0xFC00000000000000UL : ~0xFC000000U); + } + /*else if (!strcmp(class_name,"__NSCFDictionary")) + { + Error error; + value = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + (is_64bit ? 20 : 12), 4, 0, error); + if (error.Fail()) + return false; + if (is_64bit) + value &= ~0x0f1f000000000000UL; + }*/ + else + { + if (!ExtractValueFromObjCExpression(valobj, "int", "count", value)) + return false; + } + + stream.Printf("%s%" PRIu64 " %s%s", + (name_entries ? "@\"" : ""), + value, + (name_entries ? (value == 1 ? "entry" : "entries") : (value == 1 ? "key/value pair" : "key/value pairs")), + (name_entries ? "\"" : "")); + return true; +} + +SyntheticChildrenFrontEnd* lldb_private::formatters::NSDictionarySyntheticFrontEndCreator (CXXSyntheticChildren*, lldb::ValueObjectSP valobj_sp) +{ + + lldb::ProcessSP process_sp (valobj_sp->GetProcessSP()); + if (!process_sp) + return NULL; + ObjCLanguageRuntime *runtime = (ObjCLanguageRuntime*)process_sp->GetLanguageRuntime(lldb::eLanguageTypeObjC); + if (!runtime) + return NULL; + + if (!valobj_sp->IsPointerType()) + { + Error error; + valobj_sp = valobj_sp->AddressOf(error); + if (error.Fail() || !valobj_sp) + return NULL; + } + + ObjCLanguageRuntime::ClassDescriptorSP descriptor(runtime->GetClassDescriptor(*valobj_sp.get())); + + if (!descriptor.get() || !descriptor->IsValid()) + return NULL; + + const char* class_name = descriptor->GetClassName().GetCString(); + + if (!class_name || !*class_name) + return NULL; + + if (!strcmp(class_name,"__NSDictionaryI")) + { + return (new NSDictionaryISyntheticFrontEnd(valobj_sp)); + } + else if (!strcmp(class_name,"__NSDictionaryM")) + { + return (new NSDictionaryMSyntheticFrontEnd(valobj_sp)); + } + else + { + return (new NSDictionaryCodeRunningSyntheticFrontEnd(valobj_sp)); + } +} + +lldb_private::formatters::NSDictionaryCodeRunningSyntheticFrontEnd::NSDictionaryCodeRunningSyntheticFrontEnd (lldb::ValueObjectSP valobj_sp) : +SyntheticChildrenFrontEnd(*valobj_sp.get()) +{} + +size_t +lldb_private::formatters::NSDictionaryCodeRunningSyntheticFrontEnd::CalculateNumChildren () +{ + uint64_t count = 0; + if (ExtractValueFromObjCExpression(m_backend, "int", "count", count)) + return count; + return 0; +} + +lldb::ValueObjectSP +lldb_private::formatters::NSDictionaryCodeRunningSyntheticFrontEnd::GetChildAtIndex (size_t idx) +{ + StreamString idx_name; + idx_name.Printf("[%zu]",idx); + StreamString key_fetcher_expr; + key_fetcher_expr.Printf("(id)[(NSArray*)[(id)0x%" PRIx64 " allKeys] objectAtIndex:%zu]",m_backend.GetPointerValue(),idx); + StreamString value_fetcher_expr; + value_fetcher_expr.Printf("(id)[(id)0x%" PRIx64 " objectForKey:(%s)]",m_backend.GetPointerValue(),key_fetcher_expr.GetData()); + StreamString object_fetcher_expr; + object_fetcher_expr.Printf("struct __lldb_autogen_nspair { id key; id value; } _lldb_valgen_item; _lldb_valgen_item.key = %s; _lldb_valgen_item.value = %s; _lldb_valgen_item;",key_fetcher_expr.GetData(),value_fetcher_expr.GetData()); + lldb::ValueObjectSP child_sp; + m_backend.GetTargetSP()->EvaluateExpression(object_fetcher_expr.GetData(), m_backend.GetFrameSP().get(), child_sp, + EvaluateExpressionOptions().SetKeepInMemory(true)); + if (child_sp) + child_sp->SetName(ConstString(idx_name.GetData())); + return child_sp; +} + +bool +lldb_private::formatters::NSDictionaryCodeRunningSyntheticFrontEnd::Update() +{ + return false; +} + +bool +lldb_private::formatters::NSDictionaryCodeRunningSyntheticFrontEnd::MightHaveChildren () +{ + return true; +} + +size_t +lldb_private::formatters::NSDictionaryCodeRunningSyntheticFrontEnd::GetIndexOfChildWithName (const ConstString &name) +{ + return 0; +} + +lldb_private::formatters::NSDictionaryCodeRunningSyntheticFrontEnd::~NSDictionaryCodeRunningSyntheticFrontEnd () +{} + +lldb_private::formatters::NSDictionaryISyntheticFrontEnd::NSDictionaryISyntheticFrontEnd (lldb::ValueObjectSP valobj_sp) : +SyntheticChildrenFrontEnd(*valobj_sp.get()), +m_exe_ctx_ref(), +m_ptr_size(8), +m_order(lldb::eByteOrderInvalid), +m_data_32(NULL), +m_data_64(NULL), +m_pair_type() +{ +} + +lldb_private::formatters::NSDictionaryISyntheticFrontEnd::~NSDictionaryISyntheticFrontEnd () +{ + delete m_data_32; + m_data_32 = NULL; + delete m_data_64; + m_data_64 = NULL; +} + +size_t +lldb_private::formatters::NSDictionaryISyntheticFrontEnd::GetIndexOfChildWithName (const ConstString &name) +{ + const char* item_name = name.GetCString(); + uint32_t idx = ExtractIndexFromString(item_name); + if (idx < UINT32_MAX && idx >= CalculateNumChildren()) + return UINT32_MAX; + return idx; +} + +size_t +lldb_private::formatters::NSDictionaryISyntheticFrontEnd::CalculateNumChildren () +{ + if (!m_data_32 && !m_data_64) + return 0; + return (m_data_32 ? m_data_32->_used : m_data_64->_used); +} + +bool +lldb_private::formatters::NSDictionaryISyntheticFrontEnd::Update() +{ + m_children.clear(); + delete m_data_32; + m_data_32 = NULL; + delete m_data_64; + m_data_64 = NULL; + m_ptr_size = 0; + ValueObjectSP valobj_sp = m_backend.GetSP(); + if (!valobj_sp) + return false; + m_exe_ctx_ref = valobj_sp->GetExecutionContextRef(); + Error error; + error.Clear(); + lldb::ProcessSP process_sp(valobj_sp->GetProcessSP()); + if (!process_sp) + return false; + m_ptr_size = process_sp->GetAddressByteSize(); + m_order = process_sp->GetByteOrder(); + uint64_t data_location = valobj_sp->GetValueAsUnsigned(0) + m_ptr_size; + if (m_ptr_size == 4) + { + m_data_32 = new DataDescriptor_32(); + process_sp->ReadMemory (data_location, m_data_32, sizeof(DataDescriptor_32), error); + } + else + { + m_data_64 = new DataDescriptor_64(); + process_sp->ReadMemory (data_location, m_data_64, sizeof(DataDescriptor_64), error); + } + if (error.Fail()) + return false; + m_data_ptr = data_location + m_ptr_size; + return false; +} + +bool +lldb_private::formatters::NSDictionaryISyntheticFrontEnd::MightHaveChildren () +{ + return true; +} + +lldb::ValueObjectSP +lldb_private::formatters::NSDictionaryISyntheticFrontEnd::GetChildAtIndex (size_t idx) +{ + uint32_t num_children = CalculateNumChildren(); + + if (idx >= num_children) + return lldb::ValueObjectSP(); + + if (m_children.empty()) + { + // do the scan phase + lldb::addr_t key_at_idx = 0, val_at_idx = 0; + + uint32_t tries = 0; + uint32_t test_idx = 0; + + while(tries < num_children) + { + key_at_idx = m_data_ptr + (2*test_idx * m_ptr_size); + val_at_idx = key_at_idx + m_ptr_size; + ProcessSP process_sp = m_exe_ctx_ref.GetProcessSP(); + if (!process_sp) + return lldb::ValueObjectSP(); + Error error; + key_at_idx = process_sp->ReadPointerFromMemory(key_at_idx, error); + if (error.Fail()) + return lldb::ValueObjectSP(); + val_at_idx = process_sp->ReadPointerFromMemory(val_at_idx, error); + if (error.Fail()) + return lldb::ValueObjectSP(); + + test_idx++; + + if (!key_at_idx || !val_at_idx) + continue; + tries++; + + DictionaryItemDescriptor descriptor = {key_at_idx,val_at_idx,lldb::ValueObjectSP()}; + + m_children.push_back(descriptor); + } + } + + if (idx >= m_children.size()) // should never happen + return lldb::ValueObjectSP(); + + DictionaryItemDescriptor &dict_item = m_children[idx]; + if (!dict_item.valobj_sp) + { + if (!m_pair_type.IsValid()) + { + TargetSP target_sp(m_backend.GetTargetSP()); + if (!target_sp) + return ValueObjectSP(); + m_pair_type = GetLLDBNSPairType(target_sp); + } + if (!m_pair_type.IsValid()) + return ValueObjectSP(); + + DataBufferSP buffer_sp(new DataBufferHeap(2*m_ptr_size,0)); + + if (m_ptr_size == 8) + { + uint64_t *data_ptr = (uint64_t *)buffer_sp->GetBytes(); + *data_ptr = dict_item.key_ptr; + *(data_ptr+1) = dict_item.val_ptr; + } + else + { + uint32_t *data_ptr = (uint32_t *)buffer_sp->GetBytes(); + *data_ptr = dict_item.key_ptr; + *(data_ptr+1) = dict_item.val_ptr; + } + + StreamString idx_name; + idx_name.Printf("[%zu]",idx); + DataExtractor data(buffer_sp, m_order, m_ptr_size); + dict_item.valobj_sp = ValueObject::CreateValueObjectFromData(idx_name.GetData(), data, m_exe_ctx_ref, m_pair_type); + } + return dict_item.valobj_sp; +} + +lldb_private::formatters::NSDictionaryMSyntheticFrontEnd::NSDictionaryMSyntheticFrontEnd (lldb::ValueObjectSP valobj_sp) : +SyntheticChildrenFrontEnd(*valobj_sp.get()), +m_exe_ctx_ref(), +m_ptr_size(8), +m_order(lldb::eByteOrderInvalid), +m_data_32(NULL), +m_data_64(NULL), +m_pair_type() +{ +} + +lldb_private::formatters::NSDictionaryMSyntheticFrontEnd::~NSDictionaryMSyntheticFrontEnd () +{ + delete m_data_32; + m_data_32 = NULL; + delete m_data_64; + m_data_64 = NULL; +} + +size_t +lldb_private::formatters::NSDictionaryMSyntheticFrontEnd::GetIndexOfChildWithName (const ConstString &name) +{ + const char* item_name = name.GetCString(); + uint32_t idx = ExtractIndexFromString(item_name); + if (idx < UINT32_MAX && idx >= CalculateNumChildren()) + return UINT32_MAX; + return idx; +} + +size_t +lldb_private::formatters::NSDictionaryMSyntheticFrontEnd::CalculateNumChildren () +{ + if (!m_data_32 && !m_data_64) + return 0; + return (m_data_32 ? m_data_32->_used : m_data_64->_used); +} + +bool +lldb_private::formatters::NSDictionaryMSyntheticFrontEnd::Update() +{ + m_children.clear(); + ValueObjectSP valobj_sp = m_backend.GetSP(); + m_ptr_size = 0; + delete m_data_32; + m_data_32 = NULL; + delete m_data_64; + m_data_64 = NULL; + if (!valobj_sp) + return false; + m_exe_ctx_ref = valobj_sp->GetExecutionContextRef(); + Error error; + error.Clear(); + lldb::ProcessSP process_sp(valobj_sp->GetProcessSP()); + if (!process_sp) + return false; + m_ptr_size = process_sp->GetAddressByteSize(); + m_order = process_sp->GetByteOrder(); + uint64_t data_location = valobj_sp->GetValueAsUnsigned(0) + m_ptr_size; + if (m_ptr_size == 4) + { + m_data_32 = new DataDescriptor_32(); + process_sp->ReadMemory (data_location, m_data_32, sizeof(DataDescriptor_32), error); + } + else + { + m_data_64 = new DataDescriptor_64(); + process_sp->ReadMemory (data_location, m_data_64, sizeof(DataDescriptor_64), error); + } + if (error.Fail()) + return false; + return false; +} + +bool +lldb_private::formatters::NSDictionaryMSyntheticFrontEnd::MightHaveChildren () +{ + return true; +} + +lldb::ValueObjectSP +lldb_private::formatters::NSDictionaryMSyntheticFrontEnd::GetChildAtIndex (size_t idx) +{ + lldb::addr_t m_keys_ptr = (m_data_32 ? m_data_32->_keys_addr : m_data_64->_keys_addr); + lldb::addr_t m_values_ptr = (m_data_32 ? m_data_32->_objs_addr : m_data_64->_objs_addr); + + uint32_t num_children = CalculateNumChildren(); + + if (idx >= num_children) + return lldb::ValueObjectSP(); + + if (m_children.empty()) + { + // do the scan phase + lldb::addr_t key_at_idx = 0, val_at_idx = 0; + + uint32_t tries = 0; + uint32_t test_idx = 0; + + while(tries < num_children) + { + key_at_idx = m_keys_ptr + (test_idx * m_ptr_size); + val_at_idx = m_values_ptr + (test_idx * m_ptr_size);; + ProcessSP process_sp = m_exe_ctx_ref.GetProcessSP(); + if (!process_sp) + return lldb::ValueObjectSP(); + Error error; + key_at_idx = process_sp->ReadPointerFromMemory(key_at_idx, error); + if (error.Fail()) + return lldb::ValueObjectSP(); + val_at_idx = process_sp->ReadPointerFromMemory(val_at_idx, error); + if (error.Fail()) + return lldb::ValueObjectSP(); + + test_idx++; + + if (!key_at_idx || !val_at_idx) + continue; + tries++; + + DictionaryItemDescriptor descriptor = {key_at_idx,val_at_idx,lldb::ValueObjectSP()}; + + m_children.push_back(descriptor); + } + } + + if (idx >= m_children.size()) // should never happen + return lldb::ValueObjectSP(); + + DictionaryItemDescriptor &dict_item = m_children[idx]; + if (!dict_item.valobj_sp) + { + if (!m_pair_type.IsValid()) + { + TargetSP target_sp(m_backend.GetTargetSP()); + if (!target_sp) + return ValueObjectSP(); + m_pair_type = GetLLDBNSPairType(target_sp); + } + if (!m_pair_type.IsValid()) + return ValueObjectSP(); + + DataBufferSP buffer_sp(new DataBufferHeap(2*m_ptr_size,0)); + + if (m_ptr_size == 8) + { + uint64_t *data_ptr = (uint64_t *)buffer_sp->GetBytes(); + *data_ptr = dict_item.key_ptr; + *(data_ptr+1) = dict_item.val_ptr; + } + else + { + uint32_t *data_ptr = (uint32_t *)buffer_sp->GetBytes(); + *data_ptr = dict_item.key_ptr; + *(data_ptr+1) = dict_item.val_ptr; + } + + StreamString idx_name; + idx_name.Printf("[%zu]",idx); + DataExtractor data(buffer_sp, m_order, m_ptr_size); + dict_item.valobj_sp = ValueObject::CreateValueObjectFromData(idx_name.GetData(), data, m_exe_ctx_ref, m_pair_type); + } + return dict_item.valobj_sp; +} + +template bool +lldb_private::formatters::NSDictionarySummaryProvider (ValueObject&, Stream&) ; + +template bool +lldb_private::formatters::NSDictionarySummaryProvider (ValueObject&, Stream&) ; diff --git a/contrib/llvm/tools/lldb/source/DataFormatters/NSSet.cpp b/contrib/llvm/tools/lldb/source/DataFormatters/NSSet.cpp new file mode 100644 index 00000000000..02eb2bfc124 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/DataFormatters/NSSet.cpp @@ -0,0 +1,513 @@ +//===-- NSSet.cpp ------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "lldb/DataFormatters/CXXFormatterFunctions.h" + +#include "lldb/Core/DataBufferHeap.h" +#include "lldb/Core/Error.h" +#include "lldb/Core/Stream.h" +#include "lldb/Core/ValueObject.h" +#include "lldb/Core/ValueObjectConstResult.h" +#include "lldb/Host/Endian.h" +#include "lldb/Symbol/ClangASTContext.h" +#include "lldb/Target/ObjCLanguageRuntime.h" +#include "lldb/Target/Target.h" + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::formatters; + +template +bool +lldb_private::formatters::NSSetSummaryProvider (ValueObject& valobj, Stream& stream) +{ + ProcessSP process_sp = valobj.GetProcessSP(); + if (!process_sp) + return false; + + ObjCLanguageRuntime* runtime = (ObjCLanguageRuntime*)process_sp->GetLanguageRuntime(lldb::eLanguageTypeObjC); + + if (!runtime) + return false; + + ObjCLanguageRuntime::ClassDescriptorSP descriptor(runtime->GetClassDescriptor(valobj)); + + if (!descriptor.get() || !descriptor->IsValid()) + return false; + + uint32_t ptr_size = process_sp->GetAddressByteSize(); + bool is_64bit = (ptr_size == 8); + + lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0); + + if (!valobj_addr) + return false; + + uint64_t value = 0; + + const char* class_name = descriptor->GetClassName().GetCString(); + + if (!class_name || !*class_name) + return false; + + if (!strcmp(class_name,"__NSSetI")) + { + Error error; + value = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + ptr_size, ptr_size, 0, error); + if (error.Fail()) + return false; + value &= (is_64bit ? ~0xFC00000000000000UL : ~0xFC000000U); + } + else if (!strcmp(class_name,"__NSSetM")) + { + Error error; + value = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + ptr_size, ptr_size, 0, error); + if (error.Fail()) + return false; + value &= (is_64bit ? ~0xFC00000000000000UL : ~0xFC000000U); + } + /*else if (!strcmp(class_name,"__NSCFSet")) + { + Error error; + value = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + (is_64bit ? 20 : 12), 4, 0, error); + if (error.Fail()) + return false; + if (is_64bit) + value &= ~0x1fff000000000000UL; + } + else if (!strcmp(class_name,"NSCountedSet")) + { + Error error; + value = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + ptr_size, ptr_size, 0, error); + if (error.Fail()) + return false; + value = process_sp->ReadUnsignedIntegerFromMemory(value + (is_64bit ? 20 : 12), 4, 0, error); + if (error.Fail()) + return false; + if (is_64bit) + value &= ~0x1fff000000000000UL; + }*/ + else + { + if (!ExtractValueFromObjCExpression(valobj, "int", "count", value)) + return false; + } + + stream.Printf("%s%" PRIu64 " %s%s", + (cf_style ? "@\"" : ""), + value, + (cf_style ? (value == 1 ? "value" : "values") : (value == 1 ? "object" : "objects")), + (cf_style ? "\"" : "")); + return true; +} + +SyntheticChildrenFrontEnd* lldb_private::formatters::NSSetSyntheticFrontEndCreator (CXXSyntheticChildren*, lldb::ValueObjectSP valobj_sp) +{ + lldb::ProcessSP process_sp (valobj_sp->GetProcessSP()); + if (!process_sp) + return NULL; + ObjCLanguageRuntime *runtime = (ObjCLanguageRuntime*)process_sp->GetLanguageRuntime(lldb::eLanguageTypeObjC); + if (!runtime) + return NULL; + + if (!valobj_sp->IsPointerType()) + { + Error error; + valobj_sp = valobj_sp->AddressOf(error); + if (error.Fail() || !valobj_sp) + return NULL; + } + + ObjCLanguageRuntime::ClassDescriptorSP descriptor(runtime->GetClassDescriptor(*valobj_sp.get())); + + if (!descriptor.get() || !descriptor->IsValid()) + return NULL; + + const char* class_name = descriptor->GetClassName().GetCString(); + + if (!class_name || !*class_name) + return NULL; + + if (!strcmp(class_name,"__NSSetI")) + { + return (new NSSetISyntheticFrontEnd(valobj_sp)); + } + else if (!strcmp(class_name,"__NSSetM")) + { + return (new NSSetMSyntheticFrontEnd(valobj_sp)); + } + else if ((!strcmp(class_name,"__NSOrderedSetI")) || (!strcmp(class_name,"__NSOrderedSetM"))) + { + return new NSOrderedSetSyntheticFrontEnd(valobj_sp); // this runs code + } + else + { + return /*(new NSSetCodeRunningSyntheticFrontEnd(valobj_sp))*/ NULL; + } +} + +lldb_private::formatters::NSSetISyntheticFrontEnd::NSSetISyntheticFrontEnd (lldb::ValueObjectSP valobj_sp) : +SyntheticChildrenFrontEnd(*valobj_sp.get()), +m_exe_ctx_ref(), +m_ptr_size(8), +m_data_32(NULL), +m_data_64(NULL) +{ + if (valobj_sp) + Update(); +} + +lldb_private::formatters::NSSetISyntheticFrontEnd::~NSSetISyntheticFrontEnd () +{ + delete m_data_32; + m_data_32 = NULL; + delete m_data_64; + m_data_64 = NULL; +} + +size_t +lldb_private::formatters::NSSetISyntheticFrontEnd::GetIndexOfChildWithName (const ConstString &name) +{ + const char* item_name = name.GetCString(); + uint32_t idx = ExtractIndexFromString(item_name); + if (idx < UINT32_MAX && idx >= CalculateNumChildren()) + return UINT32_MAX; + return idx; +} + +size_t +lldb_private::formatters::NSSetISyntheticFrontEnd::CalculateNumChildren () +{ + if (!m_data_32 && !m_data_64) + return 0; + return (m_data_32 ? m_data_32->_used : m_data_64->_used); +} + +bool +lldb_private::formatters::NSSetISyntheticFrontEnd::Update() +{ + m_children.clear(); + delete m_data_32; + m_data_32 = NULL; + delete m_data_64; + m_data_64 = NULL; + m_ptr_size = 0; + ValueObjectSP valobj_sp = m_backend.GetSP(); + if (!valobj_sp) + return false; + if (!valobj_sp) + return false; + m_exe_ctx_ref = valobj_sp->GetExecutionContextRef(); + Error error; + if (valobj_sp->IsPointerType()) + { + valobj_sp = valobj_sp->Dereference(error); + if (error.Fail() || !valobj_sp) + return false; + } + error.Clear(); + lldb::ProcessSP process_sp(valobj_sp->GetProcessSP()); + if (!process_sp) + return false; + m_ptr_size = process_sp->GetAddressByteSize(); + uint64_t data_location = valobj_sp->GetAddressOf() + m_ptr_size; + if (m_ptr_size == 4) + { + m_data_32 = new DataDescriptor_32(); + process_sp->ReadMemory (data_location, m_data_32, sizeof(DataDescriptor_32), error); + } + else + { + m_data_64 = new DataDescriptor_64(); + process_sp->ReadMemory (data_location, m_data_64, sizeof(DataDescriptor_64), error); + } + if (error.Fail()) + return false; + m_data_ptr = data_location + m_ptr_size; + return false; +} + +bool +lldb_private::formatters::NSSetISyntheticFrontEnd::MightHaveChildren () +{ + return true; +} + +lldb::ValueObjectSP +lldb_private::formatters::NSSetISyntheticFrontEnd::GetChildAtIndex (size_t idx) +{ + uint32_t num_children = CalculateNumChildren(); + + if (idx >= num_children) + return lldb::ValueObjectSP(); + + if (m_children.empty()) + { + // do the scan phase + lldb::addr_t obj_at_idx = 0; + + uint32_t tries = 0; + uint32_t test_idx = 0; + + while(tries < num_children) + { + obj_at_idx = m_data_ptr + (test_idx * m_ptr_size); + ProcessSP process_sp = m_exe_ctx_ref.GetProcessSP(); + if (!process_sp) + return lldb::ValueObjectSP(); + Error error; + obj_at_idx = process_sp->ReadPointerFromMemory(obj_at_idx, error); + if (error.Fail()) + return lldb::ValueObjectSP(); + + test_idx++; + + if (!obj_at_idx) + continue; + tries++; + + SetItemDescriptor descriptor = {obj_at_idx,lldb::ValueObjectSP()}; + + m_children.push_back(descriptor); + } + } + + if (idx >= m_children.size()) // should never happen + return lldb::ValueObjectSP(); + + SetItemDescriptor &set_item = m_children[idx]; + if (!set_item.valobj_sp) + { + // make the new ValueObject + StreamString expr; + expr.Printf("(id)%" PRIu64,set_item.item_ptr); + StreamString idx_name; + idx_name.Printf("[%zu]",idx); + set_item.valobj_sp = ValueObject::CreateValueObjectFromExpression(idx_name.GetData(), expr.GetData(), m_exe_ctx_ref); + } + return set_item.valobj_sp; +} + +lldb_private::formatters::NSSetMSyntheticFrontEnd::NSSetMSyntheticFrontEnd (lldb::ValueObjectSP valobj_sp) : +SyntheticChildrenFrontEnd(*valobj_sp.get()), +m_exe_ctx_ref(), +m_ptr_size(8), +m_data_32(NULL), +m_data_64(NULL) +{ + if (valobj_sp) + Update (); +} + +lldb_private::formatters::NSSetMSyntheticFrontEnd::~NSSetMSyntheticFrontEnd () +{ + delete m_data_32; + m_data_32 = NULL; + delete m_data_64; + m_data_64 = NULL; +} + +size_t +lldb_private::formatters::NSSetMSyntheticFrontEnd::GetIndexOfChildWithName (const ConstString &name) +{ + const char* item_name = name.GetCString(); + uint32_t idx = ExtractIndexFromString(item_name); + if (idx < UINT32_MAX && idx >= CalculateNumChildren()) + return UINT32_MAX; + return idx; +} + +size_t +lldb_private::formatters::NSSetMSyntheticFrontEnd::CalculateNumChildren () +{ + if (!m_data_32 && !m_data_64) + return 0; + return (m_data_32 ? m_data_32->_used : m_data_64->_used); +} + +bool +lldb_private::formatters::NSSetMSyntheticFrontEnd::Update() +{ + m_children.clear(); + ValueObjectSP valobj_sp = m_backend.GetSP(); + m_ptr_size = 0; + delete m_data_32; + m_data_32 = NULL; + delete m_data_64; + m_data_64 = NULL; + if (!valobj_sp) + return false; + if (!valobj_sp) + return false; + m_exe_ctx_ref = valobj_sp->GetExecutionContextRef(); + Error error; + if (valobj_sp->IsPointerType()) + { + valobj_sp = valobj_sp->Dereference(error); + if (error.Fail() || !valobj_sp) + return false; + } + error.Clear(); + lldb::ProcessSP process_sp(valobj_sp->GetProcessSP()); + if (!process_sp) + return false; + m_ptr_size = process_sp->GetAddressByteSize(); + uint64_t data_location = valobj_sp->GetAddressOf() + m_ptr_size; + if (m_ptr_size == 4) + { + m_data_32 = new DataDescriptor_32(); + process_sp->ReadMemory (data_location, m_data_32, sizeof(DataDescriptor_32), error); + } + else + { + m_data_64 = new DataDescriptor_64(); + process_sp->ReadMemory (data_location, m_data_64, sizeof(DataDescriptor_64), error); + } + if (error.Fail()) + return false; + return false; +} + +bool +lldb_private::formatters::NSSetMSyntheticFrontEnd::MightHaveChildren () +{ + return true; +} + +lldb::ValueObjectSP +lldb_private::formatters::NSSetMSyntheticFrontEnd::GetChildAtIndex (size_t idx) +{ + lldb::addr_t m_objs_addr = (m_data_32 ? m_data_32->_objs_addr : m_data_64->_objs_addr); + + uint32_t num_children = CalculateNumChildren(); + + if (idx >= num_children) + return lldb::ValueObjectSP(); + + if (m_children.empty()) + { + // do the scan phase + lldb::addr_t obj_at_idx = 0; + + uint32_t tries = 0; + uint32_t test_idx = 0; + + while(tries < num_children) + { + obj_at_idx = m_objs_addr + (test_idx * m_ptr_size); + ProcessSP process_sp = m_exe_ctx_ref.GetProcessSP(); + if (!process_sp) + return lldb::ValueObjectSP(); + Error error; + obj_at_idx = process_sp->ReadPointerFromMemory(obj_at_idx, error); + if (error.Fail()) + return lldb::ValueObjectSP(); + + test_idx++; + + if (!obj_at_idx) + continue; + tries++; + + SetItemDescriptor descriptor = {obj_at_idx,lldb::ValueObjectSP()}; + + m_children.push_back(descriptor); + } + } + + if (idx >= m_children.size()) // should never happen + return lldb::ValueObjectSP(); + + SetItemDescriptor &set_item = m_children[idx]; + if (!set_item.valobj_sp) + { + // make the new ValueObject + StreamString expr; + expr.Printf("(id)%" PRIu64,set_item.item_ptr); + StreamString idx_name; + idx_name.Printf("[%zu]",idx); + set_item.valobj_sp = ValueObject::CreateValueObjectFromExpression(idx_name.GetData(), expr.GetData(), m_exe_ctx_ref); + } + return set_item.valobj_sp; +} + +lldb_private::formatters::NSOrderedSetSyntheticFrontEnd::NSOrderedSetSyntheticFrontEnd (lldb::ValueObjectSP valobj_sp) : +SyntheticChildrenFrontEnd(*valobj_sp.get()), +m_count(UINT32_MAX), +m_children() +{} + +size_t +lldb_private::formatters::NSOrderedSetSyntheticFrontEnd::CalculateNumChildren () +{ + if (m_count != UINT32_MAX) + return m_count; + uint64_t count_temp; + if (ExtractValueFromObjCExpression(m_backend,"unsigned int","count",count_temp)) + return (m_count = count_temp); + return (m_count = 0); +} + +lldb::ValueObjectSP +lldb_private::formatters::NSOrderedSetSyntheticFrontEnd::GetChildAtIndex (size_t idx) +{ + auto iter = m_children.find(idx); + if (iter == m_children.end()) + { + lldb::ValueObjectSP retval_sp; + if (idx <= m_count) + { + retval_sp = CallSelectorOnObject(m_backend, "id", "objectAtIndex", idx); + if (retval_sp) + { + StreamString idx_name; + idx_name.Printf("[%zu]",idx); + retval_sp->SetName(ConstString(idx_name.GetData())); + } + m_children[idx] = retval_sp; + } + return retval_sp; + } + else + return iter->second; +} + +bool +lldb_private::formatters::NSOrderedSetSyntheticFrontEnd::Update() +{ + return false; +} + +bool +lldb_private::formatters::NSOrderedSetSyntheticFrontEnd::MightHaveChildren () +{ + return true; +} + +size_t +lldb_private::formatters::NSOrderedSetSyntheticFrontEnd::GetIndexOfChildWithName (const ConstString &name) +{ + const char* item_name = name.GetCString(); + uint32_t idx = ExtractIndexFromString(item_name); + if (idx < UINT32_MAX && idx >= CalculateNumChildren()) + return UINT32_MAX; + return idx; +} + +lldb_private::formatters::NSOrderedSetSyntheticFrontEnd::~NSOrderedSetSyntheticFrontEnd () +{ +} + +template bool +lldb_private::formatters::NSSetSummaryProvider (ValueObject& valobj, Stream& stream); + +template bool +lldb_private::formatters::NSSetSummaryProvider (ValueObject& valobj, Stream& stream); diff --git a/contrib/llvm/tools/lldb/source/DataFormatters/TypeCategory.cpp b/contrib/llvm/tools/lldb/source/DataFormatters/TypeCategory.cpp new file mode 100644 index 00000000000..c887be5da49 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/DataFormatters/TypeCategory.cpp @@ -0,0 +1,382 @@ +//===-- TypeCategory.cpp -------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "lldb/DataFormatters/TypeCategory.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes + +using namespace lldb; +using namespace lldb_private; + +TypeCategoryImpl::TypeCategoryImpl(IFormatChangeListener* clist, + ConstString name) : +m_summary_nav(new SummaryNavigator("summary",clist)), +m_regex_summary_nav(new RegexSummaryNavigator("regex-summary",clist)), +m_filter_nav(new FilterNavigator("filter",clist)), +m_regex_filter_nav(new RegexFilterNavigator("regex-filter",clist)), +#ifndef LLDB_DISABLE_PYTHON +m_synth_nav(new SynthNavigator("synth",clist)), +m_regex_synth_nav(new RegexSynthNavigator("regex-synth",clist)), +#endif +m_enabled(false), +m_change_listener(clist), +m_mutex(Mutex::eMutexTypeRecursive), +m_name(name) +{} + +bool +TypeCategoryImpl::Get (ValueObject& valobj, + lldb::TypeSummaryImplSP& entry, + lldb::DynamicValueType use_dynamic, + uint32_t* reason) +{ + if (!IsEnabled()) + return false; + if (GetSummaryNavigator()->Get(valobj, entry, use_dynamic, reason)) + return true; + bool regex = GetRegexSummaryNavigator()->Get(valobj, entry, use_dynamic, reason); + if (regex && reason) + *reason |= lldb_private::eFormatterChoiceCriterionRegularExpressionSummary; + return regex; +} + +bool +TypeCategoryImpl::Get(ValueObject& valobj, + lldb::SyntheticChildrenSP& entry_sp, + lldb::DynamicValueType use_dynamic, + uint32_t* reason) +{ + if (!IsEnabled()) + return false; + TypeFilterImpl::SharedPointer filter_sp; + uint32_t reason_filter = 0; + bool regex_filter = false; + // first find both Filter and Synth, and then check which is most recent + + if (!GetFilterNavigator()->Get(valobj, filter_sp, use_dynamic, &reason_filter)) + regex_filter = GetRegexFilterNavigator()->Get (valobj, filter_sp, use_dynamic, &reason_filter); + +#ifndef LLDB_DISABLE_PYTHON + bool regex_synth = false; + uint32_t reason_synth = 0; + bool pick_synth = false; + ScriptedSyntheticChildren::SharedPointer synth; + if (!GetSyntheticNavigator()->Get(valobj, synth, use_dynamic, &reason_synth)) + regex_synth = GetRegexSyntheticNavigator()->Get (valobj, synth, use_dynamic, &reason_synth); + if (!filter_sp.get() && !synth.get()) + return false; + else if (!filter_sp.get() && synth.get()) + pick_synth = true; + + else if (filter_sp.get() && !synth.get()) + pick_synth = false; + + else /*if (filter_sp.get() && synth.get())*/ + { + if (filter_sp->GetRevision() > synth->GetRevision()) + pick_synth = false; + else + pick_synth = true; + } + if (pick_synth) + { + if (regex_synth && reason) + *reason |= lldb_private::eFormatterChoiceCriterionRegularExpressionFilter; + entry_sp = synth; + return true; + } + else + { + if (regex_filter && reason) + *reason |= lldb_private::eFormatterChoiceCriterionRegularExpressionFilter; + entry_sp = filter_sp; + return true; + } + +#else + if (filter_sp) + { + entry_sp = filter_sp; + return true; + } +#endif + + return false; + +} + +void +TypeCategoryImpl::Clear (FormatCategoryItems items) +{ + if ( (items & eFormatCategoryItemSummary) == eFormatCategoryItemSummary ) + m_summary_nav->Clear(); + if ( (items & eFormatCategoryItemRegexSummary) == eFormatCategoryItemRegexSummary ) + m_regex_summary_nav->Clear(); + if ( (items & eFormatCategoryItemFilter) == eFormatCategoryItemFilter ) + m_filter_nav->Clear(); + if ( (items & eFormatCategoryItemRegexFilter) == eFormatCategoryItemRegexFilter ) + m_regex_filter_nav->Clear(); +#ifndef LLDB_DISABLE_PYTHON + if ( (items & eFormatCategoryItemSynth) == eFormatCategoryItemSynth ) + m_synth_nav->Clear(); + if ( (items & eFormatCategoryItemRegexSynth) == eFormatCategoryItemRegexSynth ) + m_regex_synth_nav->Clear(); +#endif +} + +bool +TypeCategoryImpl::Delete (ConstString name, + FormatCategoryItems items) +{ + bool success = false; + if ( (items & eFormatCategoryItemSummary) == eFormatCategoryItemSummary ) + success = m_summary_nav->Delete(name) || success; + if ( (items & eFormatCategoryItemRegexSummary) == eFormatCategoryItemRegexSummary ) + success = m_regex_summary_nav->Delete(name) || success; + if ( (items & eFormatCategoryItemFilter) == eFormatCategoryItemFilter ) + success = m_filter_nav->Delete(name) || success; + if ( (items & eFormatCategoryItemRegexFilter) == eFormatCategoryItemRegexFilter ) + success = m_regex_filter_nav->Delete(name) || success; +#ifndef LLDB_DISABLE_PYTHON + if ( (items & eFormatCategoryItemSynth) == eFormatCategoryItemSynth ) + success = m_synth_nav->Delete(name) || success; + if ( (items & eFormatCategoryItemRegexSynth) == eFormatCategoryItemRegexSynth ) + success = m_regex_synth_nav->Delete(name) || success; +#endif + return success; +} + +uint32_t +TypeCategoryImpl::GetCount (FormatCategoryItems items) +{ + uint32_t count = 0; + if ( (items & eFormatCategoryItemSummary) == eFormatCategoryItemSummary ) + count += m_summary_nav->GetCount(); + if ( (items & eFormatCategoryItemRegexSummary) == eFormatCategoryItemRegexSummary ) + count += m_regex_summary_nav->GetCount(); + if ( (items & eFormatCategoryItemFilter) == eFormatCategoryItemFilter ) + count += m_filter_nav->GetCount(); + if ( (items & eFormatCategoryItemRegexFilter) == eFormatCategoryItemRegexFilter ) + count += m_regex_filter_nav->GetCount(); +#ifndef LLDB_DISABLE_PYTHON + if ( (items & eFormatCategoryItemSynth) == eFormatCategoryItemSynth ) + count += m_synth_nav->GetCount(); + if ( (items & eFormatCategoryItemRegexSynth) == eFormatCategoryItemRegexSynth ) + count += m_regex_synth_nav->GetCount(); +#endif + return count; +} + +bool +TypeCategoryImpl::AnyMatches(ConstString type_name, + FormatCategoryItems items, + bool only_enabled, + const char** matching_category, + FormatCategoryItems* matching_type) +{ + if (!IsEnabled() && only_enabled) + return false; + + lldb::TypeSummaryImplSP summary; + TypeFilterImpl::SharedPointer filter; +#ifndef LLDB_DISABLE_PYTHON + ScriptedSyntheticChildren::SharedPointer synth; +#endif + + if ( (items & eFormatCategoryItemSummary) == eFormatCategoryItemSummary ) + { + if (m_summary_nav->Get(type_name, summary)) + { + if (matching_category) + *matching_category = m_name.GetCString(); + if (matching_type) + *matching_type = eFormatCategoryItemSummary; + return true; + } + } + if ( (items & eFormatCategoryItemRegexSummary) == eFormatCategoryItemRegexSummary ) + { + if (m_regex_summary_nav->Get(type_name, summary)) + { + if (matching_category) + *matching_category = m_name.GetCString(); + if (matching_type) + *matching_type = eFormatCategoryItemRegexSummary; + return true; + } + } + if ( (items & eFormatCategoryItemFilter) == eFormatCategoryItemFilter ) + { + if (m_filter_nav->Get(type_name, filter)) + { + if (matching_category) + *matching_category = m_name.GetCString(); + if (matching_type) + *matching_type = eFormatCategoryItemFilter; + return true; + } + } + if ( (items & eFormatCategoryItemRegexFilter) == eFormatCategoryItemRegexFilter ) + { + if (m_regex_filter_nav->Get(type_name, filter)) + { + if (matching_category) + *matching_category = m_name.GetCString(); + if (matching_type) + *matching_type = eFormatCategoryItemRegexFilter; + return true; + } + } +#ifndef LLDB_DISABLE_PYTHON + if ( (items & eFormatCategoryItemSynth) == eFormatCategoryItemSynth ) + { + if (m_synth_nav->Get(type_name, synth)) + { + if (matching_category) + *matching_category = m_name.GetCString(); + if (matching_type) + *matching_type = eFormatCategoryItemSynth; + return true; + } + } + if ( (items & eFormatCategoryItemRegexSynth) == eFormatCategoryItemRegexSynth ) + { + if (m_regex_synth_nav->Get(type_name, synth)) + { + if (matching_category) + *matching_category = m_name.GetCString(); + if (matching_type) + *matching_type = eFormatCategoryItemRegexSynth; + return true; + } + } +#endif + return false; +} + +TypeCategoryImpl::SummaryNavigator::MapValueType +TypeCategoryImpl::GetSummaryForType (lldb::TypeNameSpecifierImplSP type_sp) +{ + SummaryNavigator::MapValueType retval; + + if (type_sp) + { + if (type_sp->IsRegex()) + m_regex_summary_nav->GetExact(ConstString(type_sp->GetName()),retval); + else + m_summary_nav->GetExact(ConstString(type_sp->GetName()),retval); + } + + return retval; +} + +TypeCategoryImpl::FilterNavigator::MapValueType +TypeCategoryImpl::GetFilterForType (lldb::TypeNameSpecifierImplSP type_sp) +{ + FilterNavigator::MapValueType retval; + + if (type_sp) + { + if (type_sp->IsRegex()) + m_regex_filter_nav->GetExact(ConstString(type_sp->GetName()),retval); + else + m_filter_nav->GetExact(ConstString(type_sp->GetName()),retval); + } + + return retval; +} + +#ifndef LLDB_DISABLE_PYTHON +TypeCategoryImpl::SynthNavigator::MapValueType +TypeCategoryImpl::GetSyntheticForType (lldb::TypeNameSpecifierImplSP type_sp) +{ + SynthNavigator::MapValueType retval; + + if (type_sp) + { + if (type_sp->IsRegex()) + m_regex_synth_nav->GetExact(ConstString(type_sp->GetName()),retval); + else + m_synth_nav->GetExact(ConstString(type_sp->GetName()),retval); + } + + return retval; +} +#endif + +lldb::TypeNameSpecifierImplSP +TypeCategoryImpl::GetTypeNameSpecifierForSummaryAtIndex (size_t index) +{ + if (index < m_summary_nav->GetCount()) + return m_summary_nav->GetTypeNameSpecifierAtIndex(index); + else + return m_regex_summary_nav->GetTypeNameSpecifierAtIndex(index-m_summary_nav->GetCount()); +} + +TypeCategoryImpl::SummaryNavigator::MapValueType +TypeCategoryImpl::GetSummaryAtIndex (size_t index) +{ + if (index < m_summary_nav->GetCount()) + return m_summary_nav->GetAtIndex(index); + else + return m_regex_summary_nav->GetAtIndex(index-m_summary_nav->GetCount()); +} + +TypeCategoryImpl::FilterNavigator::MapValueType +TypeCategoryImpl::GetFilterAtIndex (size_t index) +{ + if (index < m_filter_nav->GetCount()) + return m_filter_nav->GetAtIndex(index); + else + return m_regex_filter_nav->GetAtIndex(index-m_filter_nav->GetCount()); +} + +lldb::TypeNameSpecifierImplSP +TypeCategoryImpl::GetTypeNameSpecifierForFilterAtIndex (size_t index) +{ + if (index < m_filter_nav->GetCount()) + return m_filter_nav->GetTypeNameSpecifierAtIndex(index); + else + return m_regex_filter_nav->GetTypeNameSpecifierAtIndex(index-m_filter_nav->GetCount()); +} + +#ifndef LLDB_DISABLE_PYTHON +TypeCategoryImpl::SynthNavigator::MapValueType +TypeCategoryImpl::GetSyntheticAtIndex (size_t index) +{ + if (index < m_synth_nav->GetCount()) + return m_synth_nav->GetAtIndex(index); + else + return m_regex_synth_nav->GetAtIndex(index-m_synth_nav->GetCount()); +} + +lldb::TypeNameSpecifierImplSP +TypeCategoryImpl::GetTypeNameSpecifierForSyntheticAtIndex (size_t index) +{ + if (index < m_synth_nav->GetCount()) + return m_synth_nav->GetTypeNameSpecifierAtIndex(index); + else + return m_regex_synth_nav->GetTypeNameSpecifierAtIndex(index - m_synth_nav->GetCount()); +} +#endif + +void +TypeCategoryImpl::Enable (bool value, uint32_t position) +{ + Mutex::Locker locker(m_mutex); + m_enabled = value; + m_enabled_position = position; + if (m_change_listener) + m_change_listener->Changed(); +} diff --git a/contrib/llvm/tools/lldb/source/DataFormatters/TypeCategoryMap.cpp b/contrib/llvm/tools/lldb/source/DataFormatters/TypeCategoryMap.cpp new file mode 100644 index 00000000000..4b7222adbec --- /dev/null +++ b/contrib/llvm/tools/lldb/source/DataFormatters/TypeCategoryMap.cpp @@ -0,0 +1,285 @@ +//===-- TypeCategoryMap.cpp ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "lldb/DataFormatters/TypeCategoryMap.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes + +using namespace lldb; +using namespace lldb_private; + +TypeCategoryMap::TypeCategoryMap (IFormatChangeListener* lst) : +m_map_mutex(Mutex::eMutexTypeRecursive), +listener(lst), +m_map(), +m_active_categories() +{ + ConstString default_cs("default"); + lldb::TypeCategoryImplSP default_sp = lldb::TypeCategoryImplSP(new TypeCategoryImpl(listener, default_cs)); + Add(default_cs,default_sp); + Enable(default_cs,First); +} + +void +TypeCategoryMap::Add (KeyType name, const ValueSP& entry) +{ + Mutex::Locker locker(m_map_mutex); + m_map[name] = entry; + if (listener) + listener->Changed(); +} + +bool +TypeCategoryMap::Delete (KeyType name) +{ + Mutex::Locker locker(m_map_mutex); + MapIterator iter = m_map.find(name); + if (iter == m_map.end()) + return false; + m_map.erase(name); + Disable(name); + if (listener) + listener->Changed(); + return true; +} + +bool +TypeCategoryMap::Enable (KeyType category_name, Position pos) +{ + Mutex::Locker locker(m_map_mutex); + ValueSP category; + if (!Get(category_name,category)) + return false; + return Enable(category, pos); +} + +bool +TypeCategoryMap::Disable (KeyType category_name) +{ + Mutex::Locker locker(m_map_mutex); + ValueSP category; + if (!Get(category_name,category)) + return false; + return Disable(category); +} + +bool +TypeCategoryMap::Enable (ValueSP category, Position pos) +{ + Mutex::Locker locker(m_map_mutex); + if (category.get()) + { + Position pos_w = pos; + if (pos == First || m_active_categories.size() == 0) + m_active_categories.push_front(category); + else if (pos == Last || pos == m_active_categories.size()) + m_active_categories.push_back(category); + else if (pos < m_active_categories.size()) + { + ActiveCategoriesList::iterator iter = m_active_categories.begin(); + while (pos_w) + { + pos_w--,iter++; + } + m_active_categories.insert(iter,category); + } + else + return false; + category->Enable(true, + pos); + return true; + } + return false; +} + +bool +TypeCategoryMap::Disable (ValueSP category) +{ + Mutex::Locker locker(m_map_mutex); + if (category.get()) + { + m_active_categories.remove_if(delete_matching_categories(category)); + category->Disable(); + return true; + } + return false; +} + +void +TypeCategoryMap::Clear () +{ + Mutex::Locker locker(m_map_mutex); + m_map.clear(); + m_active_categories.clear(); + if (listener) + listener->Changed(); +} + +bool +TypeCategoryMap::Get (KeyType name, ValueSP& entry) +{ + Mutex::Locker locker(m_map_mutex); + MapIterator iter = m_map.find(name); + if (iter == m_map.end()) + return false; + entry = iter->second; + return true; +} + +bool +TypeCategoryMap::Get (uint32_t pos, ValueSP& entry) +{ + Mutex::Locker locker(m_map_mutex); + MapIterator iter = m_map.begin(); + MapIterator end = m_map.end(); + while (pos > 0) + { + iter++; + pos--; + if (iter == end) + return false; + } + entry = iter->second; + return false; +} + +bool +TypeCategoryMap::AnyMatches (ConstString type_name, + TypeCategoryImpl::FormatCategoryItems items, + bool only_enabled, + const char** matching_category, + TypeCategoryImpl::FormatCategoryItems* matching_type) +{ + Mutex::Locker locker(m_map_mutex); + + MapIterator pos, end = m_map.end(); + for (pos = m_map.begin(); pos != end; pos++) + { + if (pos->second->AnyMatches(type_name, + items, + only_enabled, + matching_category, + matching_type)) + return true; + } + return false; +} + +lldb::TypeSummaryImplSP +TypeCategoryMap::GetSummaryFormat (ValueObject& valobj, + lldb::DynamicValueType use_dynamic) +{ + Mutex::Locker locker(m_map_mutex); + + uint32_t reason_why; + ActiveCategoriesIterator begin, end = m_active_categories.end(); + + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_TYPES)); + + for (begin = m_active_categories.begin(); begin != end; begin++) + { + lldb::TypeCategoryImplSP category_sp = *begin; + lldb::TypeSummaryImplSP current_format; + if (log) + log->Printf("\n[CategoryMap::GetSummaryFormat] Trying to use category %s", category_sp->GetName()); + if (!category_sp->Get(valobj, current_format, use_dynamic, &reason_why)) + continue; + return current_format; + } + if (log) + log->Printf("[CategoryMap::GetSummaryFormat] nothing found - returning empty SP"); + return lldb::TypeSummaryImplSP(); +} + +#ifndef LLDB_DISABLE_PYTHON +lldb::SyntheticChildrenSP +TypeCategoryMap::GetSyntheticChildren (ValueObject& valobj, + lldb::DynamicValueType use_dynamic) +{ + Mutex::Locker locker(m_map_mutex); + + uint32_t reason_why; + + ActiveCategoriesIterator begin, end = m_active_categories.end(); + + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_TYPES)); + + for (begin = m_active_categories.begin(); begin != end; begin++) + { + lldb::TypeCategoryImplSP category_sp = *begin; + lldb::SyntheticChildrenSP current_format; + if (log) + log->Printf("\n[CategoryMap::GetSyntheticChildren] Trying to use category %s", category_sp->GetName()); + if (!category_sp->Get(valobj, current_format, use_dynamic, &reason_why)) + continue; + return current_format; + } + if (log) + log->Printf("[CategoryMap::GetSyntheticChildren] nothing found - returning empty SP"); + return lldb::SyntheticChildrenSP(); +} +#endif + +void +TypeCategoryMap::LoopThrough(CallbackType callback, void* param) +{ + if (callback) + { + Mutex::Locker locker(m_map_mutex); + + // loop through enabled categories in respective order + { + ActiveCategoriesIterator begin, end = m_active_categories.end(); + for (begin = m_active_categories.begin(); begin != end; begin++) + { + lldb::TypeCategoryImplSP category = *begin; + ConstString type = ConstString(category->GetName()); + if (!callback(param, category)) + break; + } + } + + // loop through disabled categories in just any order + { + MapIterator pos, end = m_map.end(); + for (pos = m_map.begin(); pos != end; pos++) + { + if (pos->second->IsEnabled()) + continue; + KeyType type = pos->first; + if (!callback(param, pos->second)) + break; + } + } + } +} + +TypeCategoryImplSP +TypeCategoryMap::GetAtIndex (uint32_t index) +{ + Mutex::Locker locker(m_map_mutex); + + if (index < m_map.size()) + { + MapIterator pos, end = m_map.end(); + for (pos = m_map.begin(); pos != end; pos++) + { + if (index == 0) + return pos->second; + index--; + } + } + + return TypeCategoryImplSP(); +} diff --git a/contrib/llvm/tools/lldb/source/DataFormatters/TypeFormat.cpp b/contrib/llvm/tools/lldb/source/DataFormatters/TypeFormat.cpp new file mode 100644 index 00000000000..279704f2af1 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/DataFormatters/TypeFormat.cpp @@ -0,0 +1,52 @@ +//===-- TypeFormat.cpp ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +// C Includes + +// C++ Includes + +// Other libraries and framework includes + +// Project includes +#include "lldb/lldb-public.h" +#include "lldb/lldb-enumerations.h" + +#include "lldb/Core/Debugger.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Core/Timer.h" +#include "lldb/DataFormatters/TypeFormat.h" +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Symbol/ClangASTType.h" +#include "lldb/Target/StackFrame.h" +#include "lldb/Target/Target.h" + +using namespace lldb; +using namespace lldb_private; + +TypeFormatImpl::TypeFormatImpl (lldb::Format f, + const Flags& flags) : +m_flags(flags), +m_format (f) +{ +} + +std::string +TypeFormatImpl::GetDescription() +{ + StreamString sstr; + sstr.Printf ("%s%s%s%s\n", + FormatManager::GetFormatAsCString (GetFormat()), + Cascades() ? "" : " (not cascading)", + SkipsPointers() ? " (skip pointers)" : "", + SkipsReferences() ? " (skip references)" : ""); + return sstr.GetString(); +} + diff --git a/contrib/llvm/tools/lldb/source/DataFormatters/TypeSummary.cpp b/contrib/llvm/tools/lldb/source/DataFormatters/TypeSummary.cpp new file mode 100644 index 00000000000..8c4d3f71c05 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/DataFormatters/TypeSummary.cpp @@ -0,0 +1,250 @@ +//===-- TypeSummary.cpp ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +// C Includes + +// C++ Includes + +// Other libraries and framework includes + +// Project includes +#include "lldb/lldb-public.h" +#include "lldb/lldb-enumerations.h" + +#include "lldb/Core/Debugger.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Core/Timer.h" +#include "lldb/DataFormatters/TypeSummary.h" +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Symbol/ClangASTType.h" +#include "lldb/Target/StackFrame.h" +#include "lldb/Target/Target.h" + +#include "lldb/Host/Host.h" + +using namespace lldb; +using namespace lldb_private; + +TypeSummaryImpl::TypeSummaryImpl (const TypeSummaryImpl::Flags& flags) : +m_flags(flags) +{ +} + + +StringSummaryFormat::StringSummaryFormat (const TypeSummaryImpl::Flags& flags, + const char *format_cstr) : +TypeSummaryImpl(flags), +m_format() +{ + if (format_cstr) + m_format.assign(format_cstr); +} + +bool +StringSummaryFormat::FormatObject (ValueObject *valobj, + std::string& retval) +{ + if (!valobj) + { + retval.assign("NULL ValueObject"); + return false; + } + + StreamString s; + ExecutionContext exe_ctx (valobj->GetExecutionContextRef()); + SymbolContext sc; + StackFrame *frame = exe_ctx.GetFramePtr(); + if (frame) + sc = frame->GetSymbolContext(lldb::eSymbolContextEverything); + + if (IsOneliner()) + { + ValueObject* object; + + ValueObjectSP synth_valobj = valobj->GetSyntheticValue(); + if (synth_valobj) + object = synth_valobj.get(); + else + object = valobj; + + const uint32_t num_children = object->GetNumChildren(); + if (num_children) + { + s.PutChar('('); + + for (uint32_t idx=0; idxGetChildAtIndex(idx, true)); + if (child_sp.get()) + { + if (idx) + s.PutCString(", "); + if (!HideNames()) + { + s.PutCString(child_sp.get()->GetName().AsCString()); + s.PutCString(" = "); + } + child_sp.get()->DumpPrintableRepresentation(s, + ValueObject::eValueObjectRepresentationStyleSummary, + lldb::eFormatInvalid, + ValueObject::ePrintableRepresentationSpecialCasesDisable); + } + } + + s.PutChar(')'); + + retval.assign(s.GetString()); + return true; + } + else + { + retval.assign("error: oneliner for no children"); + return false; + } + + } + else + { + if (Debugger::FormatPrompt(m_format.c_str(), &sc, &exe_ctx, &sc.line_entry.range.GetBaseAddress(), s, valobj)) + { + retval.assign(s.GetString()); + return true; + } + else + { + retval.assign("error: summary string parsing error"); + return false; + } + } +} + +std::string +StringSummaryFormat::GetDescription () +{ + StreamString sstr; + + sstr.Printf ("`%s`%s%s%s%s%s%s%s", m_format.c_str(), + Cascades() ? "" : " (not cascading)", + !DoesPrintChildren() ? "" : " (show children)", + !DoesPrintValue() ? " (hide value)" : "", + IsOneliner() ? " (one-line printout)" : "", + SkipsPointers() ? " (skip pointers)" : "", + SkipsReferences() ? " (skip references)" : "", + HideNames() ? " (hide member names)" : ""); + return sstr.GetString(); +} + +CXXFunctionSummaryFormat::CXXFunctionSummaryFormat (const TypeSummaryImpl::Flags& flags, + Callback impl, + const char* description) : +TypeSummaryImpl(flags), +m_impl(impl), +m_description(description ? description : "") +{ +} + +bool +CXXFunctionSummaryFormat::FormatObject (ValueObject *valobj, + std::string& dest) +{ + dest.clear(); + StreamString stream; + if (!m_impl || m_impl(*valobj,stream) == false) + return false; + dest.assign(stream.GetData()); + return true; +} + +std::string +CXXFunctionSummaryFormat::GetDescription () +{ + StreamString sstr; + sstr.Printf ("`%s (%p) `%s%s%s%s%s%s%s", m_description.c_str(),m_impl, + Cascades() ? "" : " (not cascading)", + !DoesPrintChildren() ? "" : " (show children)", + !DoesPrintValue() ? " (hide value)" : "", + IsOneliner() ? " (one-line printout)" : "", + SkipsPointers() ? " (skip pointers)" : "", + SkipsReferences() ? " (skip references)" : "", + HideNames() ? " (hide member names)" : ""); + return sstr.GetString(); +} + +#ifndef LLDB_DISABLE_PYTHON + + +ScriptSummaryFormat::ScriptSummaryFormat (const TypeSummaryImpl::Flags& flags, + const char * function_name, + const char * python_script) : +TypeSummaryImpl(flags), +m_function_name(), +m_python_script(), +m_script_function_sp() +{ + if (function_name) + m_function_name.assign(function_name); + if (python_script) + m_python_script.assign(python_script); +} + +bool +ScriptSummaryFormat::FormatObject (ValueObject *valobj, + std::string& retval) +{ + Timer scoped_timer (__PRETTY_FUNCTION__, __PRETTY_FUNCTION__); + + if (!valobj) + return false; + + Host::SetCrashDescriptionWithFormat("[Python summary] Name: %s - Function: %s", + valobj->GetName().AsCString("unknown"), + m_function_name.c_str()); + + TargetSP target_sp(valobj->GetTargetSP()); + + if (!target_sp) + { + retval.assign("error: no target"); + return false; + } + + ScriptInterpreter *script_interpreter = target_sp->GetDebugger().GetCommandInterpreter().GetScriptInterpreter(); + + if (!script_interpreter) + { + retval.assign("error: no ScriptInterpreter"); + return false; + } + + return script_interpreter->GetScriptedSummary(m_function_name.c_str(), + valobj->GetSP(), + m_script_function_sp, + retval); + +} + +std::string +ScriptSummaryFormat::GetDescription () +{ + StreamString sstr; + sstr.Printf ("%s%s%s%s%s%s%s\n%s", Cascades() ? "" : " (not cascading)", + !DoesPrintChildren() ? "" : " (show children)", + !DoesPrintValue() ? " (hide value)" : "", + IsOneliner() ? " (one-line printout)" : "", + SkipsPointers() ? " (skip pointers)" : "", + SkipsReferences() ? " (skip references)" : "", + HideNames() ? " (hide member names)" : "", + m_python_script.c_str()); + return sstr.GetString(); + +} + +#endif // #ifndef LLDB_DISABLE_PYTHON diff --git a/contrib/llvm/tools/lldb/source/DataFormatters/TypeSynthetic.cpp b/contrib/llvm/tools/lldb/source/DataFormatters/TypeSynthetic.cpp new file mode 100644 index 00000000000..972be486b78 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/DataFormatters/TypeSynthetic.cpp @@ -0,0 +1,114 @@ +//===-- TypeSynthetic.cpp ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +// C Includes + +// C++ Includes + +// Other libraries and framework includes + +// Project includes +#include "lldb/lldb-public.h" +#include "lldb/lldb-enumerations.h" + +#include "lldb/Core/Debugger.h" +#include "lldb/Core/StreamString.h" +#include "lldb/DataFormatters/TypeSynthetic.h" +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Symbol/ClangASTType.h" +#include "lldb/Target/StackFrame.h" +#include "lldb/Target/Target.h" + +using namespace lldb; +using namespace lldb_private; + +std::string +TypeFilterImpl::GetDescription() +{ + StreamString sstr; + sstr.Printf("%s%s%s {\n", + Cascades() ? "" : " (not cascading)", + SkipsPointers() ? " (skip pointers)" : "", + SkipsReferences() ? " (skip references)" : ""); + + for (size_t i = 0; i < GetCount(); i++) + { + sstr.Printf(" %s\n", + GetExpressionPathAtIndex(i)); + } + + sstr.Printf("}"); + return sstr.GetString(); +} + +std::string +CXXSyntheticChildren::GetDescription() +{ + StreamString sstr; + sstr.Printf("%s%s%s Generator at %p - %s", + Cascades() ? "" : " (not cascading)", + SkipsPointers() ? " (skip pointers)" : "", + SkipsReferences() ? " (skip references)" : "", + m_create_callback, + m_description.c_str()); + + return sstr.GetString(); +} + +#ifndef LLDB_DISABLE_PYTHON + +ScriptedSyntheticChildren::FrontEnd::FrontEnd(std::string pclass, ValueObject &backend) : +SyntheticChildrenFrontEnd(backend), +m_python_class(pclass), +m_wrapper_sp(), +m_interpreter(NULL) +{ + if (backend == LLDB_INVALID_UID) + return; + + TargetSP target_sp = backend.GetTargetSP(); + + if (!target_sp) + return; + + m_interpreter = target_sp->GetDebugger().GetCommandInterpreter().GetScriptInterpreter(); + + if (m_interpreter != NULL) + m_wrapper_sp = m_interpreter->CreateSyntheticScriptedProvider(m_python_class.c_str(), backend.GetSP()); +} + +ScriptedSyntheticChildren::FrontEnd::~FrontEnd() +{ +} + +lldb::ValueObjectSP +ScriptedSyntheticChildren::FrontEnd::GetChildAtIndex (size_t idx) +{ + if (!m_wrapper_sp || !m_interpreter) + return lldb::ValueObjectSP(); + + return m_interpreter->GetChildAtIndex(m_wrapper_sp, idx); +} + +std::string +ScriptedSyntheticChildren::GetDescription() +{ + StreamString sstr; + sstr.Printf("%s%s%s Python class %s", + Cascades() ? "" : " (not cascading)", + SkipsPointers() ? " (skip pointers)" : "", + SkipsReferences() ? " (skip references)" : "", + m_python_class.c_str()); + + return sstr.GetString(); +} + +#endif // #ifndef LLDB_DISABLE_PYTHON diff --git a/contrib/llvm/tools/lldb/source/Expression/ASTDumper.cpp b/contrib/llvm/tools/lldb/source/Expression/ASTDumper.cpp new file mode 100644 index 00000000000..5210d149e66 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Expression/ASTDumper.cpp @@ -0,0 +1,132 @@ +//===-- ASTDumper.cpp -------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Core/Log.h" +#include "lldb/Expression/ASTDumper.h" +#include "lldb/Symbol/ClangASTType.h" + +#include "llvm/Support/raw_ostream.h" + +using namespace lldb_private; + +ASTDumper::ASTDumper (clang::Decl *decl) +{ + clang::DeclContext *decl_ctx = llvm::dyn_cast(decl); + + bool has_external_lexical_storage; + bool has_external_visible_storage; + + if (decl_ctx) + { + has_external_lexical_storage = decl_ctx->hasExternalLexicalStorage(); + has_external_visible_storage = decl_ctx->hasExternalVisibleStorage(); + decl_ctx->setHasExternalLexicalStorage(false); + decl_ctx->setHasExternalVisibleStorage(false); + } + + llvm::raw_string_ostream os(m_dump); + decl->print (os); + os.flush(); + + if (decl_ctx) + { + decl_ctx->setHasExternalLexicalStorage(has_external_lexical_storage); + decl_ctx->setHasExternalVisibleStorage(has_external_visible_storage); + } +} + +ASTDumper::ASTDumper (clang::DeclContext *decl_ctx) +{ + bool has_external_lexical_storage = decl_ctx->hasExternalLexicalStorage(); + bool has_external_visible_storage = decl_ctx->hasExternalVisibleStorage(); + + decl_ctx->setHasExternalLexicalStorage(false); + decl_ctx->setHasExternalVisibleStorage(false); + + if (clang::Decl *decl = llvm::dyn_cast(decl_ctx)) + { + llvm::raw_string_ostream os(m_dump); + decl->print (os); + os.flush(); + } + else + { + m_dump.assign(""); + } + + decl_ctx->setHasExternalLexicalStorage(has_external_lexical_storage); + decl_ctx->setHasExternalVisibleStorage(has_external_visible_storage); +} + +ASTDumper::ASTDumper (const clang::Type *type) +{ + m_dump = clang::QualType(type, 0).getAsString(); +} + +ASTDumper::ASTDumper (clang::QualType type) +{ + m_dump = type.getAsString(); +} + +ASTDumper::ASTDumper (lldb::clang_type_t type) +{ + m_dump = clang::QualType::getFromOpaquePtr(type).getAsString(); +} + +ASTDumper::ASTDumper (const ClangASTType &clang_type) +{ + m_dump = clang_type.GetQualType().getAsString(); +} + + +const char * +ASTDumper::GetCString() +{ + return m_dump.c_str(); +} + +void ASTDumper::ToSTDERR() +{ + fprintf(stderr, "%s\n", m_dump.c_str()); +} + +void ASTDumper::ToLog(Log *log, const char *prefix) +{ + size_t len = m_dump.length() + 1; + + char *alloc = (char*)malloc(len); + char *str = alloc; + + memcpy(str, m_dump.c_str(), len); + + char *end = NULL; + + end = strchr(str, '\n'); + + while (end) + { + *end = '\0'; + + log->Printf("%s%s", prefix, str); + + *end = '\n'; + + str = end + 1; + end = strchr(str, '\n'); + } + + log->Printf("%s%s", prefix, str); + + free(alloc); +} + +void ASTDumper::ToStream(lldb::StreamSP &stream) +{ + stream->PutCString(m_dump.c_str()); +} diff --git a/contrib/llvm/tools/lldb/source/Expression/ASTResultSynthesizer.cpp b/contrib/llvm/tools/lldb/source/Expression/ASTResultSynthesizer.cpp new file mode 100644 index 00000000000..76c2577af53 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Expression/ASTResultSynthesizer.cpp @@ -0,0 +1,512 @@ +//===-- ASTResultSynthesizer.cpp --------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "stdlib.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/Decl.h" +#include "clang/AST/DeclCXX.h" +#include "clang/AST/DeclGroup.h" +#include "clang/AST/DeclObjC.h" +#include "clang/AST/Expr.h" +#include "clang/AST/Stmt.h" +#include "clang/Parse/Parser.h" +#include "clang/Sema/SemaDiagnostic.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/raw_ostream.h" +#include "lldb/Core/Log.h" +#include "lldb/Expression/ClangPersistentVariables.h" +#include "lldb/Expression/ASTResultSynthesizer.h" +#include "lldb/Symbol/ClangASTContext.h" +#include "lldb/Symbol/ClangASTImporter.h" +#include "lldb/Target/Target.h" + +using namespace llvm; +using namespace clang; +using namespace lldb_private; + +ASTResultSynthesizer::ASTResultSynthesizer(ASTConsumer *passthrough, + Target &target) : + m_ast_context (NULL), + m_passthrough (passthrough), + m_passthrough_sema (NULL), + m_target (target), + m_sema (NULL) +{ + if (!m_passthrough) + return; + + m_passthrough_sema = dyn_cast(passthrough); +} + +ASTResultSynthesizer::~ASTResultSynthesizer() +{ +} + +void +ASTResultSynthesizer::Initialize(ASTContext &Context) +{ + m_ast_context = &Context; + + if (m_passthrough) + m_passthrough->Initialize(Context); +} + +void +ASTResultSynthesizer::TransformTopLevelDecl(Decl* D) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + if (NamedDecl *named_decl = dyn_cast(D)) + { + if (log && log->GetVerbose()) + { + if (named_decl->getIdentifier()) + log->Printf("TransformTopLevelDecl(%s)", named_decl->getIdentifier()->getNameStart()); + else if (ObjCMethodDecl *method_decl = dyn_cast(D)) + log->Printf("TransformTopLevelDecl(%s)", method_decl->getSelector().getAsString().c_str()); + else + log->Printf("TransformTopLevelDecl()"); + } + + } + + if (LinkageSpecDecl *linkage_spec_decl = dyn_cast(D)) + { + RecordDecl::decl_iterator decl_iterator; + + for (decl_iterator = linkage_spec_decl->decls_begin(); + decl_iterator != linkage_spec_decl->decls_end(); + ++decl_iterator) + { + TransformTopLevelDecl(*decl_iterator); + } + } + else if (ObjCMethodDecl *method_decl = dyn_cast(D)) + { + if (m_ast_context && + !method_decl->getSelector().getAsString().compare("$__lldb_expr:")) + { + RecordPersistentTypes(method_decl); + SynthesizeObjCMethodResult(method_decl); + } + } + else if (FunctionDecl *function_decl = dyn_cast(D)) + { + if (m_ast_context && + !function_decl->getNameInfo().getAsString().compare("$__lldb_expr")) + { + RecordPersistentTypes(function_decl); + SynthesizeFunctionResult(function_decl); + } + } +} + +bool +ASTResultSynthesizer::HandleTopLevelDecl(DeclGroupRef D) +{ + DeclGroupRef::iterator decl_iterator; + + for (decl_iterator = D.begin(); + decl_iterator != D.end(); + ++decl_iterator) + { + Decl *decl = *decl_iterator; + + TransformTopLevelDecl(decl); + } + + if (m_passthrough) + return m_passthrough->HandleTopLevelDecl(D); + return true; +} + +bool +ASTResultSynthesizer::SynthesizeFunctionResult (FunctionDecl *FunDecl) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + if (!m_sema) + return false; + + FunctionDecl *function_decl = FunDecl; + + if (!function_decl) + return false; + + if (log && log->GetVerbose()) + { + std::string s; + raw_string_ostream os(s); + + function_decl->print(os); + + os.flush(); + + log->Printf ("Untransformed function AST:\n%s", s.c_str()); + } + + Stmt *function_body = function_decl->getBody(); + CompoundStmt *compound_stmt = dyn_cast(function_body); + + bool ret = SynthesizeBodyResult (compound_stmt, + function_decl); + + if (log && log->GetVerbose()) + { + std::string s; + raw_string_ostream os(s); + + function_decl->print(os); + + os.flush(); + + log->Printf ("Transformed function AST:\n%s", s.c_str()); + } + + return ret; +} + +bool +ASTResultSynthesizer::SynthesizeObjCMethodResult (ObjCMethodDecl *MethodDecl) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + if (!m_sema) + return false; + + if (!MethodDecl) + return false; + + if (log && log->GetVerbose()) + { + std::string s; + raw_string_ostream os(s); + + MethodDecl->print(os); + + os.flush(); + + log->Printf ("Untransformed method AST:\n%s", s.c_str()); + } + + Stmt *method_body = MethodDecl->getBody(); + + if (!method_body) + return false; + + CompoundStmt *compound_stmt = dyn_cast(method_body); + + bool ret = SynthesizeBodyResult (compound_stmt, + MethodDecl); + + if (log && log->GetVerbose()) + { + std::string s; + raw_string_ostream os(s); + + MethodDecl->print(os); + + os.flush(); + + log->Printf("Transformed method AST:\n%s", s.c_str()); + } + + return ret; +} + +bool +ASTResultSynthesizer::SynthesizeBodyResult (CompoundStmt *Body, + DeclContext *DC) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + ASTContext &Ctx(*m_ast_context); + + if (!Body) + return false; + + if (Body->body_empty()) + return false; + + Stmt **last_stmt_ptr = Body->body_end() - 1; + Stmt *last_stmt = *last_stmt_ptr; + + while (dyn_cast(last_stmt)) + { + if (last_stmt_ptr != Body->body_begin()) + { + last_stmt_ptr--; + last_stmt = *last_stmt_ptr; + } + else + { + return false; + } + } + + Expr *last_expr = dyn_cast(last_stmt); + + if (!last_expr) + // No auxiliary variable necessary; expression returns void + return true; + + // In C++11, last_expr can be a LValueToRvalue implicit cast. Strip that off if that's the + // case. + + do { + ImplicitCastExpr *implicit_cast = dyn_cast(last_expr); + + if (!implicit_cast) + break; + + if (implicit_cast->getCastKind() != CK_LValueToRValue) + break; + + last_expr = implicit_cast->getSubExpr(); + } while (0); + + // is_lvalue is used to record whether the expression returns an assignable Lvalue or an + // Rvalue. This is relevant because they are handled differently. + // + // For Lvalues + // + // - In AST result synthesis (here!) the expression E is transformed into an initialization + // T *$__lldb_expr_result_ptr = &E. + // + // - In structure allocation, a pointer-sized slot is allocated in the struct that is to be + // passed into the expression. + // + // - In IR transformations, reads and writes to $__lldb_expr_result_ptr are redirected at + // an entry in the struct ($__lldb_arg) passed into the expression. (Other persistent + // variables are treated similarly, having been materialized as references, but in those + // cases the value of the reference itself is never modified.) + // + // - During materialization, $0 (the result persistent variable) is ignored. + // + // - During dematerialization, $0 is marked up as a load address with value equal to the + // contents of the structure entry. + // + // For Rvalues + // + // - In AST result synthesis the expression E is transformed into an initialization + // static T $__lldb_expr_result = E. + // + // - In structure allocation, a pointer-sized slot is allocated in the struct that is to be + // passed into the expression. + // + // - In IR transformations, an instruction is inserted at the beginning of the function to + // dereference the pointer resident in the slot. Reads and writes to $__lldb_expr_result + // are redirected at that dereferenced version. Guard variables for the static variable + // are excised. + // + // - During materialization, $0 (the result persistent variable) is populated with the location + // of a newly-allocated area of memory. + // + // - During dematerialization, $0 is ignored. + + bool is_lvalue = + (last_expr->getValueKind() == VK_LValue || last_expr->getValueKind() == VK_XValue) && + (last_expr->getObjectKind() == OK_Ordinary); + + QualType expr_qual_type = last_expr->getType(); + const clang::Type *expr_type = expr_qual_type.getTypePtr(); + + if (!expr_type) + return false; + + if (expr_type->isVoidType()) + return true; + + if (log) + { + std::string s = expr_qual_type.getAsString(); + + log->Printf("Last statement is an %s with type: %s", (is_lvalue ? "lvalue" : "rvalue"), s.c_str()); + } + + clang::VarDecl *result_decl = NULL; + + if (is_lvalue) + { + IdentifierInfo *result_ptr_id; + + if (expr_type->isFunctionType()) + result_ptr_id = &Ctx.Idents.get("$__lldb_expr_result"); // functions actually should be treated like function pointers + else + result_ptr_id = &Ctx.Idents.get("$__lldb_expr_result_ptr"); + + m_sema->RequireCompleteType(SourceLocation(), expr_qual_type, clang::diag::err_incomplete_type); + + QualType ptr_qual_type; + + if (expr_qual_type->getAs() != NULL) + ptr_qual_type = Ctx.getObjCObjectPointerType(expr_qual_type); + else + ptr_qual_type = Ctx.getPointerType(expr_qual_type); + + result_decl = VarDecl::Create(Ctx, + DC, + SourceLocation(), + SourceLocation(), + result_ptr_id, + ptr_qual_type, + NULL, + SC_Static); + + if (!result_decl) + return false; + + ExprResult address_of_expr = m_sema->CreateBuiltinUnaryOp(SourceLocation(), UO_AddrOf, last_expr); + + m_sema->AddInitializerToDecl(result_decl, address_of_expr.take(), true, false); + } + else + { + IdentifierInfo &result_id = Ctx.Idents.get("$__lldb_expr_result"); + + result_decl = VarDecl::Create(Ctx, + DC, + SourceLocation(), + SourceLocation(), + &result_id, + expr_qual_type, + NULL, + SC_Static); + + if (!result_decl) + return false; + + m_sema->AddInitializerToDecl(result_decl, last_expr, true, false); + } + + DC->addDecl(result_decl); + + /////////////////////////////// + // call AddInitializerToDecl + // + + //m_sema->AddInitializerToDecl(result_decl, last_expr); + + ///////////////////////////////// + // call ConvertDeclToDeclGroup + // + + Sema::DeclGroupPtrTy result_decl_group_ptr; + + result_decl_group_ptr = m_sema->ConvertDeclToDeclGroup(result_decl); + + //////////////////////// + // call ActOnDeclStmt + // + + StmtResult result_initialization_stmt_result(m_sema->ActOnDeclStmt(result_decl_group_ptr, + SourceLocation(), + SourceLocation())); + + //////////////////////////////////////////////// + // replace the old statement with the new one + // + + *last_stmt_ptr = reinterpret_cast(result_initialization_stmt_result.take()); + + return true; +} + +void +ASTResultSynthesizer::HandleTranslationUnit(ASTContext &Ctx) +{ + if (m_passthrough) + m_passthrough->HandleTranslationUnit(Ctx); +} + +void +ASTResultSynthesizer::RecordPersistentTypes(DeclContext *FunDeclCtx) +{ + typedef DeclContext::specific_decl_iterator TypeDeclIterator; + + for (TypeDeclIterator i = TypeDeclIterator(FunDeclCtx->decls_begin()), + e = TypeDeclIterator(FunDeclCtx->decls_end()); + i != e; + ++i) + { + MaybeRecordPersistentType(*i); + } +} + +void +ASTResultSynthesizer::MaybeRecordPersistentType(TypeDecl *D) +{ + if (!D->getIdentifier()) + return; + + StringRef name = D->getName(); + + if (name.size() == 0 || name[0] != '$') + return; + + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + ConstString name_cs(name.str().c_str()); + + if (log) + log->Printf ("Recording persistent type %s\n", name_cs.GetCString()); + + Decl *D_scratch = m_target.GetClangASTImporter()->DeportDecl(m_target.GetScratchClangASTContext()->getASTContext(), + m_ast_context, + D); + + if (TypeDecl *TypeDecl_scratch = dyn_cast(D_scratch)) + m_target.GetPersistentVariables().RegisterPersistentType(name_cs, TypeDecl_scratch); +} + +void +ASTResultSynthesizer::HandleTagDeclDefinition(TagDecl *D) +{ + if (m_passthrough) + m_passthrough->HandleTagDeclDefinition(D); +} + +void +ASTResultSynthesizer::CompleteTentativeDefinition(VarDecl *D) +{ + if (m_passthrough) + m_passthrough->CompleteTentativeDefinition(D); +} + +void +ASTResultSynthesizer::HandleVTable(CXXRecordDecl *RD, bool DefinitionRequired) +{ + if (m_passthrough) + m_passthrough->HandleVTable(RD, DefinitionRequired); +} + +void +ASTResultSynthesizer::PrintStats() +{ + if (m_passthrough) + m_passthrough->PrintStats(); +} + +void +ASTResultSynthesizer::InitializeSema(Sema &S) +{ + m_sema = &S; + + if (m_passthrough_sema) + m_passthrough_sema->InitializeSema(S); +} + +void +ASTResultSynthesizer::ForgetSema() +{ + m_sema = NULL; + + if (m_passthrough_sema) + m_passthrough_sema->ForgetSema(); +} diff --git a/contrib/llvm/tools/lldb/source/Expression/ASTStructExtractor.cpp b/contrib/llvm/tools/lldb/source/Expression/ASTStructExtractor.cpp new file mode 100644 index 00000000000..d1f21923deb --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Expression/ASTStructExtractor.cpp @@ -0,0 +1,220 @@ +//===-- ASTStructExtractor.cpp ----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "stdlib.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/Decl.h" +#include "clang/AST/DeclCXX.h" +#include "clang/AST/DeclGroup.h" +#include "clang/AST/Expr.h" +#include "clang/AST/RecordLayout.h" +#include "clang/AST/Stmt.h" +#include "clang/Parse/Parser.h" +#include "clang/Sema/Sema.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/raw_ostream.h" +#include "lldb/Core/Log.h" +#include "lldb/Expression/ASTStructExtractor.h" + +using namespace llvm; +using namespace clang; +using namespace lldb_private; + +ASTStructExtractor::ASTStructExtractor(ASTConsumer *passthrough, + const char *struct_name, + ClangFunction &function) : + m_ast_context (NULL), + m_passthrough (passthrough), + m_passthrough_sema (NULL), + m_sema (NULL), + m_action (NULL), + m_function (function), + m_struct_name (struct_name) +{ + if (!m_passthrough) + return; + + m_passthrough_sema = dyn_cast(passthrough); +} + +ASTStructExtractor::~ASTStructExtractor() +{ +} + +void +ASTStructExtractor::Initialize(ASTContext &Context) +{ + m_ast_context = &Context; + + if (m_passthrough) + m_passthrough->Initialize(Context); +} + +void +ASTStructExtractor::ExtractFromFunctionDecl(FunctionDecl *F) +{ + if (!F->hasBody()) + return; + + Stmt *body_stmt = F->getBody(); + CompoundStmt *body_compound_stmt = dyn_cast(body_stmt); + + if (!body_compound_stmt) + return; // do we have to handle this? + + RecordDecl *struct_decl = NULL; + + StringRef desired_name(m_struct_name.c_str()); + + for (CompoundStmt::const_body_iterator bi = body_compound_stmt->body_begin(), be = body_compound_stmt->body_end(); + bi != be; + ++bi) + { + Stmt *curr_stmt = *bi; + DeclStmt *curr_decl_stmt = dyn_cast(curr_stmt); + if (!curr_decl_stmt) + continue; + DeclGroupRef decl_group = curr_decl_stmt->getDeclGroup(); + for (Decl *candidate_decl : decl_group) + { + RecordDecl *candidate_record_decl = dyn_cast(candidate_decl); + if (!candidate_record_decl) + continue; + if (candidate_record_decl->getName() == desired_name) + { + struct_decl = candidate_record_decl; + break; + } + } + if (struct_decl) + break; + } + + if (!struct_decl) + return; + + const ASTRecordLayout* struct_layout(&m_ast_context->getASTRecordLayout (struct_decl)); + + if (!struct_layout) + return; + + m_function.m_struct_size = struct_layout->getSize().getQuantity(); // TODO Store m_struct_size as CharUnits + m_function.m_return_offset = struct_layout->getFieldOffset(struct_layout->getFieldCount() - 1) / 8; + m_function.m_return_size = struct_layout->getDataSize().getQuantity() - m_function.m_return_offset; + + for (unsigned field_index = 0, num_fields = struct_layout->getFieldCount(); + field_index < num_fields; + ++field_index) + { + m_function.m_member_offsets.push_back(struct_layout->getFieldOffset(field_index) / 8); + } + + m_function.m_struct_valid = true; +} + +void +ASTStructExtractor::ExtractFromTopLevelDecl(Decl* D) +{ + LinkageSpecDecl *linkage_spec_decl = dyn_cast(D); + + if (linkage_spec_decl) + { + RecordDecl::decl_iterator decl_iterator; + + for (decl_iterator = linkage_spec_decl->decls_begin(); + decl_iterator != linkage_spec_decl->decls_end(); + ++decl_iterator) + { + ExtractFromTopLevelDecl(*decl_iterator); + } + } + + FunctionDecl *function_decl = dyn_cast(D); + + if (m_ast_context && + function_decl && + !m_function.m_wrapper_function_name.compare(function_decl->getNameAsString().c_str())) + { + ExtractFromFunctionDecl(function_decl); + } +} + +bool +ASTStructExtractor::HandleTopLevelDecl(DeclGroupRef D) +{ + DeclGroupRef::iterator decl_iterator; + + for (decl_iterator = D.begin(); + decl_iterator != D.end(); + ++decl_iterator) + { + Decl *decl = *decl_iterator; + + ExtractFromTopLevelDecl(decl); + } + + if (m_passthrough) + return m_passthrough->HandleTopLevelDecl(D); + return true; +} + +void +ASTStructExtractor::HandleTranslationUnit(ASTContext &Ctx) +{ + if (m_passthrough) + m_passthrough->HandleTranslationUnit(Ctx); +} + +void +ASTStructExtractor::HandleTagDeclDefinition(TagDecl *D) +{ + if (m_passthrough) + m_passthrough->HandleTagDeclDefinition(D); +} + +void +ASTStructExtractor::CompleteTentativeDefinition(VarDecl *D) +{ + if (m_passthrough) + m_passthrough->CompleteTentativeDefinition(D); +} + +void +ASTStructExtractor::HandleVTable(CXXRecordDecl *RD, bool DefinitionRequired) +{ + if (m_passthrough) + m_passthrough->HandleVTable(RD, DefinitionRequired); +} + +void +ASTStructExtractor::PrintStats() +{ + if (m_passthrough) + m_passthrough->PrintStats(); +} + +void +ASTStructExtractor::InitializeSema(Sema &S) +{ + m_sema = &S; + m_action = reinterpret_cast(m_sema); + + if (m_passthrough_sema) + m_passthrough_sema->InitializeSema(S); +} + +void +ASTStructExtractor::ForgetSema() +{ + m_sema = NULL; + m_action = NULL; + + if (m_passthrough_sema) + m_passthrough_sema->ForgetSema(); +} diff --git a/contrib/llvm/tools/lldb/source/Expression/ClangASTSource.cpp b/contrib/llvm/tools/lldb/source/Expression/ClangASTSource.cpp new file mode 100644 index 00000000000..49513d740f3 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Expression/ClangASTSource.cpp @@ -0,0 +1,1866 @@ +//===-- ClangASTSource.cpp ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + + +#include "clang/AST/ASTContext.h" +#include "clang/AST/RecordLayout.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/ModuleList.h" +#include "lldb/Expression/ASTDumper.h" +#include "lldb/Expression/ClangASTSource.h" +#include "lldb/Expression/ClangExpression.h" +#include "lldb/Symbol/ClangNamespaceDecl.h" +#include "lldb/Symbol/Function.h" +#include "lldb/Symbol/SymbolVendor.h" +#include "lldb/Target/ObjCLanguageRuntime.h" +#include "lldb/Target/Target.h" + +using namespace clang; +using namespace lldb_private; + +ClangASTSource::~ClangASTSource() +{ + m_ast_importer->ForgetDestination(m_ast_context); + + // We are in the process of destruction, don't create clang ast context on demand + // by passing false to Target::GetScratchClangASTContext(create_on_demand). + ClangASTContext *scratch_clang_ast_context = m_target->GetScratchClangASTContext(false); + + if (!scratch_clang_ast_context) + return; + + clang::ASTContext *scratch_ast_context = scratch_clang_ast_context->getASTContext(); + + if (!scratch_ast_context) + return; + + if (m_ast_context != scratch_ast_context) + m_ast_importer->ForgetSource(scratch_ast_context, m_ast_context); +} + +void +ClangASTSource::StartTranslationUnit(ASTConsumer *Consumer) +{ + if (!m_ast_context) + return; + + m_ast_context->getTranslationUnitDecl()->setHasExternalVisibleStorage(); + m_ast_context->getTranslationUnitDecl()->setHasExternalLexicalStorage(); +} + +// The core lookup interface. +bool +ClangASTSource::FindExternalVisibleDeclsByName +( + const DeclContext *decl_ctx, + DeclarationName clang_decl_name +) +{ + if (!m_ast_context) + { + SetNoExternalVisibleDeclsForName(decl_ctx, clang_decl_name); + return false; + } + + if (GetImportInProgress()) + { + SetNoExternalVisibleDeclsForName(decl_ctx, clang_decl_name); + return false; + } + + std::string decl_name (clang_decl_name.getAsString()); + +// if (m_decl_map.DoingASTImport ()) +// return DeclContext::lookup_result(); +// + switch (clang_decl_name.getNameKind()) { + // Normal identifiers. + case DeclarationName::Identifier: + { + clang::IdentifierInfo *identifier_info = clang_decl_name.getAsIdentifierInfo(); + + if (!identifier_info || + identifier_info->getBuiltinID() != 0) + { + SetNoExternalVisibleDeclsForName(decl_ctx, clang_decl_name); + return false; + } + } + break; + + // Operator names. Not important for now. + case DeclarationName::CXXOperatorName: + case DeclarationName::CXXLiteralOperatorName: + SetNoExternalVisibleDeclsForName(decl_ctx, clang_decl_name); + return false; + + // Using directives found in this context. + // Tell Sema we didn't find any or we'll end up getting asked a *lot*. + case DeclarationName::CXXUsingDirective: + SetNoExternalVisibleDeclsForName(decl_ctx, clang_decl_name); + return false; + + case DeclarationName::ObjCZeroArgSelector: + case DeclarationName::ObjCOneArgSelector: + case DeclarationName::ObjCMultiArgSelector: + { + llvm::SmallVector method_decls; + + NameSearchContext method_search_context (*this, method_decls, clang_decl_name, decl_ctx); + + FindObjCMethodDecls(method_search_context); + + SetExternalVisibleDeclsForName (decl_ctx, clang_decl_name, method_decls); + return (method_decls.size() > 0); + } + // These aren't possible in the global context. + case DeclarationName::CXXConstructorName: + case DeclarationName::CXXDestructorName: + case DeclarationName::CXXConversionFunctionName: + SetNoExternalVisibleDeclsForName(decl_ctx, clang_decl_name); + return false; + } + + + if (!GetLookupsEnabled()) + { + // Wait until we see a '$' at the start of a name before we start doing + // any lookups so we can avoid lookup up all of the builtin types. + if (!decl_name.empty() && decl_name[0] == '$') + { + SetLookupsEnabled (true); + } + else + { + SetNoExternalVisibleDeclsForName(decl_ctx, clang_decl_name); + return false; + } + } + + ConstString const_decl_name(decl_name.c_str()); + + const char *uniqued_const_decl_name = const_decl_name.GetCString(); + if (m_active_lookups.find (uniqued_const_decl_name) != m_active_lookups.end()) + { + // We are currently looking up this name... + SetNoExternalVisibleDeclsForName(decl_ctx, clang_decl_name); + return false; + } + m_active_lookups.insert(uniqued_const_decl_name); +// static uint32_t g_depth = 0; +// ++g_depth; +// printf("[%5u] FindExternalVisibleDeclsByName() \"%s\"\n", g_depth, uniqued_const_decl_name); + llvm::SmallVector name_decls; + NameSearchContext name_search_context(*this, name_decls, clang_decl_name, decl_ctx); + FindExternalVisibleDecls(name_search_context); + SetExternalVisibleDeclsForName (decl_ctx, clang_decl_name, name_decls); +// --g_depth; + m_active_lookups.erase (uniqued_const_decl_name); + return (name_decls.size() != 0); +} + +void +ClangASTSource::CompleteType (TagDecl *tag_decl) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + static unsigned int invocation_id = 0; + unsigned int current_id = invocation_id++; + + if (log) + { + log->Printf(" CompleteTagDecl[%u] on (ASTContext*)%p Completing (TagDecl*)%p named %s", + current_id, + m_ast_context, + tag_decl, + tag_decl->getName().str().c_str()); + + log->Printf(" CTD[%u] Before:", current_id); + ASTDumper dumper((Decl*)tag_decl); + dumper.ToLog(log, " [CTD] "); + } + + if (!m_ast_importer->CompleteTagDecl (tag_decl)) + { + // We couldn't complete the type. Maybe there's a definition + // somewhere else that can be completed. + + if (log) + log->Printf(" CTD[%u] Type could not be completed in the module in which it was first found.", current_id); + + bool found = false; + + DeclContext *decl_ctx = tag_decl->getDeclContext(); + + if (const NamespaceDecl *namespace_context = dyn_cast(decl_ctx)) + { + ClangASTImporter::NamespaceMapSP namespace_map = m_ast_importer->GetNamespaceMap(namespace_context); + + if (log && log->GetVerbose()) + log->Printf(" CTD[%u] Inspecting namespace map %p (%d entries)", + current_id, + namespace_map.get(), + (int)namespace_map->size()); + + if (!namespace_map) + return; + + for (ClangASTImporter::NamespaceMap::iterator i = namespace_map->begin(), e = namespace_map->end(); + i != e && !found; + ++i) + { + if (log) + log->Printf(" CTD[%u] Searching namespace %s in module %s", + current_id, + i->second.GetNamespaceDecl()->getNameAsString().c_str(), + i->first->GetFileSpec().GetFilename().GetCString()); + + TypeList types; + + SymbolContext null_sc; + ConstString name(tag_decl->getName().str().c_str()); + + i->first->FindTypesInNamespace(null_sc, name, &i->second, UINT32_MAX, types); + + for (uint32_t ti = 0, te = types.GetSize(); + ti != te && !found; + ++ti) + { + lldb::TypeSP type = types.GetTypeAtIndex(ti); + + if (!type) + continue; + + ClangASTType clang_type (type->GetClangFullType()); + + if (!clang_type) + continue; + + const TagType *tag_type = clang_type.GetQualType()->getAs(); + + if (!tag_type) + continue; + + TagDecl *candidate_tag_decl = const_cast(tag_type->getDecl()); + + if (m_ast_importer->CompleteTagDeclWithOrigin (tag_decl, candidate_tag_decl)) + found = true; + } + } + } + else + { + TypeList types; + + SymbolContext null_sc; + ConstString name(tag_decl->getName().str().c_str()); + ClangNamespaceDecl namespace_decl; + + const ModuleList &module_list = m_target->GetImages(); + + bool exact_match = false; + module_list.FindTypes (null_sc, name, exact_match, UINT32_MAX, types); + + for (uint32_t ti = 0, te = types.GetSize(); + ti != te && !found; + ++ti) + { + lldb::TypeSP type = types.GetTypeAtIndex(ti); + + if (!type) + continue; + + ClangASTType clang_type (type->GetClangFullType()); + + if (!clang_type) + continue; + + const TagType *tag_type = clang_type.GetQualType()->getAs(); + + if (!tag_type) + continue; + + TagDecl *candidate_tag_decl = const_cast(tag_type->getDecl()); + + if (m_ast_importer->CompleteTagDeclWithOrigin (tag_decl, candidate_tag_decl)) + found = true; + } + } + } + + if (log) + { + log->Printf(" [CTD] After:"); + ASTDumper dumper((Decl*)tag_decl); + dumper.ToLog(log, " [CTD] "); + } +} + +void +ClangASTSource::CompleteType (clang::ObjCInterfaceDecl *interface_decl) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + if (log) + { + log->Printf(" [CompleteObjCInterfaceDecl] on (ASTContext*)%p Completing an ObjCInterfaceDecl named %s", m_ast_context, interface_decl->getName().str().c_str()); + log->Printf(" [COID] Before:"); + ASTDumper dumper((Decl*)interface_decl); + dumper.ToLog(log, " [COID] "); + } + + m_ast_importer->CompleteObjCInterfaceDecl (interface_decl); + + if (log) + { + log->Printf(" [COID] After:"); + ASTDumper dumper((Decl*)interface_decl); + dumper.ToLog(log, " [COID] "); + } +} + +clang::ObjCInterfaceDecl * +ClangASTSource::GetCompleteObjCInterface (clang::ObjCInterfaceDecl *interface_decl) +{ + lldb::ProcessSP process(m_target->GetProcessSP()); + + if (!process) + return NULL; + + ObjCLanguageRuntime *language_runtime(process->GetObjCLanguageRuntime()); + + if (!language_runtime) + return NULL; + + ConstString class_name(interface_decl->getNameAsString().c_str()); + + lldb::TypeSP complete_type_sp(language_runtime->LookupInCompleteClassCache(class_name)); + + if (!complete_type_sp) + return NULL; + + TypeFromUser complete_type = TypeFromUser(complete_type_sp->GetClangFullType()); + lldb::clang_type_t complete_opaque_type = complete_type.GetOpaqueQualType(); + + if (!complete_opaque_type) + return NULL; + + const clang::Type *complete_clang_type = QualType::getFromOpaquePtr(complete_opaque_type).getTypePtr(); + const ObjCInterfaceType *complete_interface_type = dyn_cast(complete_clang_type); + + if (!complete_interface_type) + return NULL; + + ObjCInterfaceDecl *complete_iface_decl(complete_interface_type->getDecl()); + + return complete_iface_decl; +} + +clang::ExternalLoadResult +ClangASTSource::FindExternalLexicalDecls (const DeclContext *decl_context, + bool (*predicate)(Decl::Kind), + llvm::SmallVectorImpl &decls) +{ + ClangASTMetrics::RegisterLexicalQuery(); + + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + const Decl *context_decl = dyn_cast(decl_context); + + if (!context_decl) + return ELR_Failure; + + static unsigned int invocation_id = 0; + unsigned int current_id = invocation_id++; + + if (log) + { + if (const NamedDecl *context_named_decl = dyn_cast(context_decl)) + log->Printf("FindExternalLexicalDecls[%u] on (ASTContext*)%p in '%s' (%sDecl*)%p with %s predicate", + current_id, + m_ast_context, + context_named_decl->getNameAsString().c_str(), + context_decl->getDeclKindName(), + context_decl, + (predicate ? "non-null" : "null")); + else if(context_decl) + log->Printf("FindExternalLexicalDecls[%u] on (ASTContext*)%p in (%sDecl*)%p with %s predicate", + current_id, + m_ast_context, + context_decl->getDeclKindName(), + context_decl, + (predicate ? "non-null" : "null")); + else + log->Printf("FindExternalLexicalDecls[%u] on (ASTContext*)%p in a NULL context with %s predicate", + current_id, + m_ast_context, + (predicate ? "non-null" : "null")); + } + + Decl *original_decl = NULL; + ASTContext *original_ctx = NULL; + + if (!m_ast_importer->ResolveDeclOrigin(context_decl, &original_decl, &original_ctx)) + return ELR_Failure; + + if (log) + { + log->Printf(" FELD[%u] Original decl (ASTContext*)%p (Decl*)%p:", current_id, original_ctx, original_decl); + ASTDumper(original_decl).ToLog(log, " "); + } + + if (ObjCInterfaceDecl *original_iface_decl = dyn_cast(original_decl)) + { + ObjCInterfaceDecl *complete_iface_decl = GetCompleteObjCInterface(original_iface_decl); + + if (complete_iface_decl && (complete_iface_decl != original_iface_decl)) + { + original_decl = complete_iface_decl; + original_ctx = &complete_iface_decl->getASTContext(); + + m_ast_importer->SetDeclOrigin(context_decl, original_iface_decl); + } + } + + if (TagDecl *original_tag_decl = dyn_cast(original_decl)) + { + ExternalASTSource *external_source = original_ctx->getExternalSource(); + + if (external_source) + external_source->CompleteType (original_tag_decl); + } + + const DeclContext *original_decl_context = dyn_cast(original_decl); + + if (!original_decl_context) + return ELR_Failure; + + for (TagDecl::decl_iterator iter = original_decl_context->decls_begin(); + iter != original_decl_context->decls_end(); + ++iter) + { + Decl *decl = *iter; + + if (!predicate || predicate(decl->getKind())) + { + if (log) + { + ASTDumper ast_dumper(decl); + if (const NamedDecl *context_named_decl = dyn_cast(context_decl)) + log->Printf(" FELD[%d] Adding [to %sDecl %s] lexical %sDecl %s", current_id, context_named_decl->getDeclKindName(), context_named_decl->getNameAsString().c_str(), decl->getDeclKindName(), ast_dumper.GetCString()); + else + log->Printf(" FELD[%d] Adding lexical %sDecl %s", current_id, decl->getDeclKindName(), ast_dumper.GetCString()); + } + + Decl *copied_decl = m_ast_importer->CopyDecl(m_ast_context, original_ctx, decl); + + if (!copied_decl) + continue; + + if (FieldDecl *copied_field = dyn_cast(copied_decl)) + { + QualType copied_field_type = copied_field->getType(); + + m_ast_importer->RequireCompleteType(copied_field_type); + } + + decls.push_back(copied_decl); + + DeclContext *decl_context_non_const = const_cast(decl_context); + + if (copied_decl->getDeclContext() != decl_context) + { + if (copied_decl->getDeclContext()->containsDecl(copied_decl)) + copied_decl->getDeclContext()->removeDecl(copied_decl); + copied_decl->setDeclContext(decl_context_non_const); + } + + if (!decl_context_non_const->containsDecl(copied_decl)) + decl_context_non_const->addDeclInternal(copied_decl); + } + } + + return ELR_AlreadyLoaded; +} + +void +ClangASTSource::FindExternalVisibleDecls (NameSearchContext &context) +{ + assert (m_ast_context); + + ClangASTMetrics::RegisterVisibleQuery(); + + const ConstString name(context.m_decl_name.getAsString().c_str()); + + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + static unsigned int invocation_id = 0; + unsigned int current_id = invocation_id++; + + if (log) + { + if (!context.m_decl_context) + log->Printf("ClangASTSource::FindExternalVisibleDecls[%u] on (ASTContext*)%p for '%s' in a NULL DeclContext", current_id, m_ast_context, name.GetCString()); + else if (const NamedDecl *context_named_decl = dyn_cast(context.m_decl_context)) + log->Printf("ClangASTSource::FindExternalVisibleDecls[%u] on (ASTContext*)%p for '%s' in '%s'", current_id, m_ast_context, name.GetCString(), context_named_decl->getNameAsString().c_str()); + else + log->Printf("ClangASTSource::FindExternalVisibleDecls[%u] on (ASTContext*)%p for '%s' in a '%s'", current_id, m_ast_context, name.GetCString(), context.m_decl_context->getDeclKindName()); + } + + context.m_namespace_map.reset(new ClangASTImporter::NamespaceMap); + + if (const NamespaceDecl *namespace_context = dyn_cast(context.m_decl_context)) + { + ClangASTImporter::NamespaceMapSP namespace_map = m_ast_importer->GetNamespaceMap(namespace_context); + + if (log && log->GetVerbose()) + log->Printf(" CAS::FEVD[%u] Inspecting namespace map %p (%d entries)", + current_id, + namespace_map.get(), + (int)namespace_map->size()); + + if (!namespace_map) + return; + + for (ClangASTImporter::NamespaceMap::iterator i = namespace_map->begin(), e = namespace_map->end(); + i != e; + ++i) + { + if (log) + log->Printf(" CAS::FEVD[%u] Searching namespace %s in module %s", + current_id, + i->second.GetNamespaceDecl()->getNameAsString().c_str(), + i->first->GetFileSpec().GetFilename().GetCString()); + + FindExternalVisibleDecls(context, + i->first, + i->second, + current_id); + } + } + else if (isa(context.m_decl_context)) + { + FindObjCPropertyAndIvarDecls(context); + } + else if (!isa(context.m_decl_context)) + { + // we shouldn't be getting FindExternalVisibleDecls calls for these + return; + } + else + { + ClangNamespaceDecl namespace_decl; + + if (log) + log->Printf(" CAS::FEVD[%u] Searching the root namespace", current_id); + + FindExternalVisibleDecls(context, + lldb::ModuleSP(), + namespace_decl, + current_id); + } + + if (!context.m_namespace_map->empty()) + { + if (log && log->GetVerbose()) + log->Printf(" CAS::FEVD[%u] Registering namespace map %p (%d entries)", + current_id, + context.m_namespace_map.get(), + (int)context.m_namespace_map->size()); + + NamespaceDecl *clang_namespace_decl = AddNamespace(context, context.m_namespace_map); + + if (clang_namespace_decl) + clang_namespace_decl->setHasExternalVisibleStorage(); + } +} + +void +ClangASTSource::FindExternalVisibleDecls (NameSearchContext &context, + lldb::ModuleSP module_sp, + ClangNamespaceDecl &namespace_decl, + unsigned int current_id) +{ + assert (m_ast_context); + + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + SymbolContextList sc_list; + + const ConstString name(context.m_decl_name.getAsString().c_str()); + + const char *name_unique_cstr = name.GetCString(); + + static ConstString id_name("id"); + static ConstString Class_name("Class"); + + if (name == id_name || name == Class_name) + return; + + if (name_unique_cstr == NULL) + return; + + // The ClangASTSource is not responsible for finding $-names. + if (name_unique_cstr[0] == '$') + return; + + if (module_sp && namespace_decl) + { + ClangNamespaceDecl found_namespace_decl; + + SymbolVendor *symbol_vendor = module_sp->GetSymbolVendor(); + + if (symbol_vendor) + { + SymbolContext null_sc; + + found_namespace_decl = symbol_vendor->FindNamespace(null_sc, name, &namespace_decl); + + if (found_namespace_decl) + { + context.m_namespace_map->push_back(std::pair(module_sp, found_namespace_decl)); + + if (log) + log->Printf(" CAS::FEVD[%u] Found namespace %s in module %s", + current_id, + name.GetCString(), + module_sp->GetFileSpec().GetFilename().GetCString()); + } + } + } + else + { + const ModuleList &target_images = m_target->GetImages(); + Mutex::Locker modules_locker (target_images.GetMutex()); + + for (size_t i = 0, e = target_images.GetSize(); i < e; ++i) + { + lldb::ModuleSP image = target_images.GetModuleAtIndexUnlocked(i); + + if (!image) + continue; + + ClangNamespaceDecl found_namespace_decl; + + SymbolVendor *symbol_vendor = image->GetSymbolVendor(); + + if (!symbol_vendor) + continue; + + SymbolContext null_sc; + + found_namespace_decl = symbol_vendor->FindNamespace(null_sc, name, &namespace_decl); + + if (found_namespace_decl) + { + context.m_namespace_map->push_back(std::pair(image, found_namespace_decl)); + + if (log) + log->Printf(" CAS::FEVD[%u] Found namespace %s in module %s", + current_id, + name.GetCString(), + image->GetFileSpec().GetFilename().GetCString()); + } + } + } + + do + { + TypeList types; + SymbolContext null_sc; + const bool exact_match = false; + + if (module_sp && namespace_decl) + module_sp->FindTypesInNamespace(null_sc, name, &namespace_decl, 1, types); + else + m_target->GetImages().FindTypes(null_sc, name, exact_match, 1, types); + + if (types.GetSize()) + { + lldb::TypeSP type_sp = types.GetTypeAtIndex(0); + + if (log) + { + const char *name_string = type_sp->GetName().GetCString(); + + log->Printf(" CAS::FEVD[%u] Matching type found for \"%s\": %s", + current_id, + name.GetCString(), + (name_string ? name_string : "")); + } + + ClangASTType full_type = type_sp->GetClangFullType(); + + ClangASTType copied_clang_type (GuardedCopyType(full_type)); + + if (!copied_clang_type) + { + if (log) + log->Printf(" CAS::FEVD[%u] - Couldn't export a type", + current_id); + + break; + } + + context.AddTypeDecl(copied_clang_type); + } + else + { + do + { + // Couldn't find any types elsewhere. Try the Objective-C runtime if one exists. + + lldb::ProcessSP process(m_target->GetProcessSP()); + + if (!process) + break; + + ObjCLanguageRuntime *language_runtime(process->GetObjCLanguageRuntime()); + + if (!language_runtime) + break; + + TypeVendor *type_vendor = language_runtime->GetTypeVendor(); + + if (!type_vendor) + break; + + bool append = false; + uint32_t max_matches = 1; + std::vector types; + + if (!type_vendor->FindTypes(name, + append, + max_matches, + types)) + break; + + if (log) + { + log->Printf(" CAS::FEVD[%u] Matching type found for \"%s\" in the runtime", + current_id, + name.GetCString()); + } + + ClangASTType copied_clang_type (GuardedCopyType(types[0])); + + if (!copied_clang_type) + { + if (log) + log->Printf(" CAS::FEVD[%u] - Couldn't export a type from the runtime", + current_id); + + break; + } + + context.AddTypeDecl(copied_clang_type); + } + while(0); + } + + } while(0); +} + +template class TaggedASTDecl { +public: + TaggedASTDecl() : decl(NULL) { } + TaggedASTDecl(D *_decl) : decl(_decl) { } + bool IsValid() const { return (decl != NULL); } + bool IsInvalid() const { return !IsValid(); } + D *operator->() const { return decl; } + D *decl; +}; + +template class TD, class D1> +TD +DynCast(TD source) +{ + return TD (dyn_cast(source.decl)); +} + +template class DeclFromParser; +template class DeclFromUser; + +template class DeclFromParser : public TaggedASTDecl { +public: + DeclFromParser() : TaggedASTDecl() { } + DeclFromParser(D *_decl) : TaggedASTDecl(_decl) { } + + DeclFromUser GetOrigin(ClangASTImporter *importer); +}; + +template class DeclFromUser : public TaggedASTDecl { +public: + DeclFromUser() : TaggedASTDecl() { } + DeclFromUser(D *_decl) : TaggedASTDecl(_decl) { } + + DeclFromParser Import(ClangASTImporter *importer, ASTContext &dest_ctx); +}; + +template +DeclFromUser +DeclFromParser::GetOrigin(ClangASTImporter *importer) +{ + DeclFromUser <> origin_decl; + importer->ResolveDeclOrigin(this->decl, &origin_decl.decl, NULL); + if (origin_decl.IsInvalid()) + return DeclFromUser(); + return DeclFromUser(dyn_cast(origin_decl.decl)); +} + +template +DeclFromParser +DeclFromUser::Import(ClangASTImporter *importer, ASTContext &dest_ctx) +{ + DeclFromParser <> parser_generic_decl(importer->CopyDecl(&dest_ctx, &this->decl->getASTContext(), this->decl)); + if (parser_generic_decl.IsInvalid()) + return DeclFromParser(); + return DeclFromParser(dyn_cast(parser_generic_decl.decl)); +} + +static bool +FindObjCMethodDeclsWithOrigin (unsigned int current_id, + NameSearchContext &context, + ObjCInterfaceDecl *original_interface_decl, + clang::ASTContext *ast_context, + ClangASTImporter *ast_importer, + const char *log_info) +{ + const DeclarationName &decl_name(context.m_decl_name); + clang::ASTContext *original_ctx = &original_interface_decl->getASTContext(); + + Selector original_selector; + + if (decl_name.isObjCZeroArgSelector()) + { + IdentifierInfo *ident = &original_ctx->Idents.get(decl_name.getAsString()); + original_selector = original_ctx->Selectors.getSelector(0, &ident); + } + else if (decl_name.isObjCOneArgSelector()) + { + const std::string &decl_name_string = decl_name.getAsString(); + std::string decl_name_string_without_colon(decl_name_string.c_str(), decl_name_string.length() - 1); + IdentifierInfo *ident = &original_ctx->Idents.get(decl_name_string_without_colon.c_str()); + original_selector = original_ctx->Selectors.getSelector(1, &ident); + } + else + { + SmallVector idents; + + clang::Selector sel = decl_name.getObjCSelector(); + + unsigned num_args = sel.getNumArgs(); + + for (unsigned i = 0; + i != num_args; + ++i) + { + idents.push_back(&original_ctx->Idents.get(sel.getNameForSlot(i))); + } + + original_selector = original_ctx->Selectors.getSelector(num_args, idents.data()); + } + + DeclarationName original_decl_name(original_selector); + + ObjCInterfaceDecl::lookup_result result = original_interface_decl->lookup(original_decl_name); + + if (result.empty()) + return false; + + if (!result[0]) + return false; + + ObjCMethodDecl *result_method = dyn_cast(result[0]); + + if (!result_method) + return false; + + Decl *copied_decl = ast_importer->CopyDecl(ast_context, &result_method->getASTContext(), result_method); + + if (!copied_decl) + return false; + + ObjCMethodDecl *copied_method_decl = dyn_cast(copied_decl); + + if (!copied_method_decl) + return false; + + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + if (log) + { + ASTDumper dumper((Decl*)copied_method_decl); + log->Printf(" CAS::FOMD[%d] found (%s) %s", current_id, log_info, dumper.GetCString()); + } + + context.AddNamedDecl(copied_method_decl); + + return true; +} + +void +ClangASTSource::FindObjCMethodDecls (NameSearchContext &context) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + static unsigned int invocation_id = 0; + unsigned int current_id = invocation_id++; + + const DeclarationName &decl_name(context.m_decl_name); + const DeclContext *decl_ctx(context.m_decl_context); + + const ObjCInterfaceDecl *interface_decl = dyn_cast(decl_ctx); + + if (!interface_decl) + return; + + do + { + Decl *original_decl = NULL; + ASTContext *original_ctx = NULL; + + m_ast_importer->ResolveDeclOrigin(interface_decl, &original_decl, &original_ctx); + + if (!original_decl) + break; + + ObjCInterfaceDecl *original_interface_decl = dyn_cast(original_decl); + + if (FindObjCMethodDeclsWithOrigin(current_id, + context, + original_interface_decl, + m_ast_context, + m_ast_importer, + "at origin")) + return; // found it, no need to look any further + } while (0); + + StreamString ss; + + if (decl_name.isObjCZeroArgSelector()) + { + ss.Printf("%s", decl_name.getAsString().c_str()); + } + else if (decl_name.isObjCOneArgSelector()) + { + ss.Printf("%s", decl_name.getAsString().c_str()); + } + else + { + clang::Selector sel = decl_name.getObjCSelector(); + + for (unsigned i = 0, e = sel.getNumArgs(); + i != e; + ++i) + { + llvm::StringRef r = sel.getNameForSlot(i); + ss.Printf("%s:", r.str().c_str()); + } + } + ss.Flush(); + + ConstString selector_name(ss.GetData()); + + if (log) + log->Printf("ClangASTSource::FindObjCMethodDecls[%d] on (ASTContext*)%p for selector [%s %s]", + current_id, + m_ast_context, + interface_decl->getNameAsString().c_str(), + selector_name.AsCString()); + SymbolContextList sc_list; + + const bool include_symbols = false; + const bool include_inlines = false; + const bool append = false; + + std::string interface_name = interface_decl->getNameAsString(); + + do + { + StreamString ms; + ms.Printf("-[%s %s]", interface_name.c_str(), selector_name.AsCString()); + ms.Flush(); + ConstString instance_method_name(ms.GetData()); + + m_target->GetImages().FindFunctions(instance_method_name, lldb::eFunctionNameTypeFull, include_symbols, include_inlines, append, sc_list); + + if (sc_list.GetSize()) + break; + + ms.Clear(); + ms.Printf("+[%s %s]", interface_name.c_str(), selector_name.AsCString()); + ms.Flush(); + ConstString class_method_name(ms.GetData()); + + m_target->GetImages().FindFunctions(class_method_name, lldb::eFunctionNameTypeFull, include_symbols, include_inlines, append, sc_list); + + if (sc_list.GetSize()) + break; + + // Fall back and check for methods in categories. If we find methods this way, we need to check that they're actually in + // categories on the desired class. + + SymbolContextList candidate_sc_list; + + m_target->GetImages().FindFunctions(selector_name, lldb::eFunctionNameTypeSelector, include_symbols, include_inlines, append, candidate_sc_list); + + for (uint32_t ci = 0, ce = candidate_sc_list.GetSize(); + ci != ce; + ++ci) + { + SymbolContext candidate_sc; + + if (!candidate_sc_list.GetContextAtIndex(ci, candidate_sc)) + continue; + + if (!candidate_sc.function) + continue; + + const char *candidate_name = candidate_sc.function->GetName().AsCString(); + + const char *cursor = candidate_name; + + if (*cursor != '+' && *cursor != '-') + continue; + + ++cursor; + + if (*cursor != '[') + continue; + + ++cursor; + + size_t interface_len = interface_name.length(); + + if (strncmp(cursor, interface_name.c_str(), interface_len)) + continue; + + cursor += interface_len; + + if (*cursor == ' ' || *cursor == '(') + sc_list.Append(candidate_sc); + } + } + while (0); + + if (sc_list.GetSize()) + { + // We found a good function symbol. Use that. + + for (uint32_t i = 0, e = sc_list.GetSize(); + i != e; + ++i) + { + SymbolContext sc; + + if (!sc_list.GetContextAtIndex(i, sc)) + continue; + + if (!sc.function) + continue; + + DeclContext *function_ctx = sc.function->GetClangDeclContext(); + + if (!function_ctx) + continue; + + ObjCMethodDecl *method_decl = dyn_cast(function_ctx); + + if (!method_decl) + continue; + + ObjCInterfaceDecl *found_interface_decl = method_decl->getClassInterface(); + + if (!found_interface_decl) + continue; + + if (found_interface_decl->getName() == interface_decl->getName()) + { + Decl *copied_decl = m_ast_importer->CopyDecl(m_ast_context, &method_decl->getASTContext(), method_decl); + + if (!copied_decl) + continue; + + ObjCMethodDecl *copied_method_decl = dyn_cast(copied_decl); + + if (!copied_method_decl) + continue; + + if (log) + { + ASTDumper dumper((Decl*)copied_method_decl); + log->Printf(" CAS::FOMD[%d] found (in symbols) %s", current_id, dumper.GetCString()); + } + + context.AddNamedDecl(copied_method_decl); + } + } + + return; + } + + // Try the debug information. + + do + { + ObjCInterfaceDecl *complete_interface_decl = GetCompleteObjCInterface(const_cast(interface_decl)); + + if (!complete_interface_decl) + break; + + // We found the complete interface. The runtime never needs to be queried in this scenario. + + DeclFromUser complete_iface_decl(complete_interface_decl); + + if (complete_interface_decl == interface_decl) + break; // already checked this one + + if (log) + log->Printf("CAS::FOPD[%d] trying origin (ObjCInterfaceDecl*)%p/(ASTContext*)%p...", + current_id, + complete_interface_decl, + &complete_iface_decl->getASTContext()); + + FindObjCMethodDeclsWithOrigin(current_id, + context, + complete_interface_decl, + m_ast_context, + m_ast_importer, + "in debug info"); + + return; + } + while (0); + + do + { + // Check the runtime only if the debug information didn't have a complete interface. + + lldb::ProcessSP process(m_target->GetProcessSP()); + + if (!process) + break; + + ObjCLanguageRuntime *language_runtime(process->GetObjCLanguageRuntime()); + + if (!language_runtime) + break; + + TypeVendor *type_vendor = language_runtime->GetTypeVendor(); + + if (!type_vendor) + break; + + ConstString interface_name(interface_decl->getNameAsString().c_str()); + bool append = false; + uint32_t max_matches = 1; + std::vector types; + + if (!type_vendor->FindTypes(interface_name, + append, + max_matches, + types)) + break; + + const clang::Type *runtime_clang_type = QualType::getFromOpaquePtr(types[0].GetOpaqueQualType()).getTypePtr(); + + const ObjCInterfaceType *runtime_interface_type = dyn_cast(runtime_clang_type); + + if (!runtime_interface_type) + break; + + ObjCInterfaceDecl *runtime_interface_decl = runtime_interface_type->getDecl(); + + FindObjCMethodDeclsWithOrigin(current_id, + context, + runtime_interface_decl, + m_ast_context, + m_ast_importer, + "in runtime"); + } + while(0); +} + +static bool +FindObjCPropertyAndIvarDeclsWithOrigin (unsigned int current_id, + NameSearchContext &context, + clang::ASTContext &ast_context, + ClangASTImporter *ast_importer, + DeclFromUser &origin_iface_decl) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + if (origin_iface_decl.IsInvalid()) + return false; + + std::string name_str = context.m_decl_name.getAsString(); + StringRef name(name_str.c_str()); + IdentifierInfo &name_identifier(origin_iface_decl->getASTContext().Idents.get(name)); + + DeclFromUser origin_property_decl(origin_iface_decl->FindPropertyDeclaration(&name_identifier)); + + bool found = false; + + if (origin_property_decl.IsValid()) + { + DeclFromParser parser_property_decl(origin_property_decl.Import(ast_importer, ast_context)); + if (parser_property_decl.IsValid()) + { + if (log) + { + ASTDumper dumper((Decl*)parser_property_decl.decl); + log->Printf(" CAS::FOPD[%d] found %s", current_id, dumper.GetCString()); + } + + context.AddNamedDecl(parser_property_decl.decl); + found = true; + } + } + + DeclFromUser origin_ivar_decl(origin_iface_decl->getIvarDecl(&name_identifier)); + + if (origin_ivar_decl.IsValid()) + { + DeclFromParser parser_ivar_decl(origin_ivar_decl.Import(ast_importer, ast_context)); + if (parser_ivar_decl.IsValid()) + { + if (log) + { + ASTDumper dumper((Decl*)parser_ivar_decl.decl); + log->Printf(" CAS::FOPD[%d] found %s", current_id, dumper.GetCString()); + } + + context.AddNamedDecl(parser_ivar_decl.decl); + found = true; + } + } + + return found; +} + +void +ClangASTSource::FindObjCPropertyAndIvarDecls (NameSearchContext &context) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + static unsigned int invocation_id = 0; + unsigned int current_id = invocation_id++; + + DeclFromParser parser_iface_decl(cast(context.m_decl_context)); + DeclFromUser origin_iface_decl(parser_iface_decl.GetOrigin(m_ast_importer)); + + ConstString class_name(parser_iface_decl->getNameAsString().c_str()); + + if (log) + log->Printf("ClangASTSource::FindObjCPropertyAndIvarDecls[%d] on (ASTContext*)%p for '%s.%s'", + current_id, + m_ast_context, + parser_iface_decl->getNameAsString().c_str(), + context.m_decl_name.getAsString().c_str()); + + if (FindObjCPropertyAndIvarDeclsWithOrigin(current_id, + context, + *m_ast_context, + m_ast_importer, + origin_iface_decl)) + return; + + if (log) + log->Printf("CAS::FOPD[%d] couldn't find the property on origin (ObjCInterfaceDecl*)%p/(ASTContext*)%p, searching elsewhere...", + current_id, + origin_iface_decl.decl, + &origin_iface_decl->getASTContext()); + + SymbolContext null_sc; + TypeList type_list; + + do + { + ObjCInterfaceDecl *complete_interface_decl = GetCompleteObjCInterface(const_cast(parser_iface_decl.decl)); + + if (!complete_interface_decl) + break; + + // We found the complete interface. The runtime never needs to be queried in this scenario. + + DeclFromUser complete_iface_decl(complete_interface_decl); + + if (complete_iface_decl.decl == origin_iface_decl.decl) + break; // already checked this one + + if (log) + log->Printf("CAS::FOPD[%d] trying origin (ObjCInterfaceDecl*)%p/(ASTContext*)%p...", + current_id, + complete_iface_decl.decl, + &complete_iface_decl->getASTContext()); + + FindObjCPropertyAndIvarDeclsWithOrigin(current_id, + context, + *m_ast_context, + m_ast_importer, + complete_iface_decl); + + return; + } + while(0); + + do + { + // Check the runtime only if the debug information didn't have a complete interface. + + lldb::ProcessSP process(m_target->GetProcessSP()); + + if (!process) + return; + + ObjCLanguageRuntime *language_runtime(process->GetObjCLanguageRuntime()); + + if (!language_runtime) + return; + + TypeVendor *type_vendor = language_runtime->GetTypeVendor(); + + if (!type_vendor) + break; + + bool append = false; + uint32_t max_matches = 1; + std::vector types; + + if (!type_vendor->FindTypes(class_name, + append, + max_matches, + types)) + break; + + const clang::Type *runtime_clang_type = QualType::getFromOpaquePtr(types[0].GetOpaqueQualType()).getTypePtr(); + + const ObjCInterfaceType *runtime_interface_type = dyn_cast(runtime_clang_type); + + if (!runtime_interface_type) + break; + + DeclFromUser runtime_iface_decl(runtime_interface_type->getDecl()); + + if (log) + log->Printf("CAS::FOPD[%d] trying runtime (ObjCInterfaceDecl*)%p/(ASTContext*)%p...", + current_id, + runtime_iface_decl.decl, + &runtime_iface_decl->getASTContext()); + + if (FindObjCPropertyAndIvarDeclsWithOrigin(current_id, + context, + *m_ast_context, + m_ast_importer, + runtime_iface_decl)) + return; + } + while(0); +} + +typedef llvm::DenseMap FieldOffsetMap; +typedef llvm::DenseMap BaseOffsetMap; + +template +static bool +ImportOffsetMap (llvm::DenseMap &destination_map, + llvm::DenseMap &source_map, + ClangASTImporter *importer, + ASTContext &dest_ctx) +{ + typedef llvm::DenseMap MapType; + + for (typename MapType::iterator fi = source_map.begin(), fe = source_map.end(); + fi != fe; + ++fi) + { + DeclFromUser user_decl(const_cast(fi->first)); + DeclFromParser parser_decl(user_decl.Import(importer, dest_ctx)); + if (parser_decl.IsInvalid()) + return false; + destination_map.insert(std::pair(parser_decl.decl, fi->second)); + } + + return true; +} + +template bool ExtractBaseOffsets (const ASTRecordLayout &record_layout, + DeclFromUser &record, + BaseOffsetMap &base_offsets) +{ + for (CXXRecordDecl::base_class_const_iterator + bi = (IsVirtual ? record->vbases_begin() : record->bases_begin()), + be = (IsVirtual ? record->vbases_end() : record->bases_end()); + bi != be; + ++bi) + { + if (!IsVirtual && bi->isVirtual()) + continue; + + const clang::Type *origin_base_type = bi->getType().getTypePtr(); + const clang::RecordType *origin_base_record_type = origin_base_type->getAs(); + + if (!origin_base_record_type) + return false; + + DeclFromUser origin_base_record(origin_base_record_type->getDecl()); + + if (origin_base_record.IsInvalid()) + return false; + + DeclFromUser origin_base_cxx_record(DynCast(origin_base_record)); + + if (origin_base_cxx_record.IsInvalid()) + return false; + + CharUnits base_offset; + + if (IsVirtual) + base_offset = record_layout.getVBaseClassOffset(origin_base_cxx_record.decl); + else + base_offset = record_layout.getBaseClassOffset(origin_base_cxx_record.decl); + + base_offsets.insert(std::pair(origin_base_cxx_record.decl, base_offset)); + } + + return true; +} + +bool +ClangASTSource::layoutRecordType(const RecordDecl *record, + uint64_t &size, + uint64_t &alignment, + FieldOffsetMap &field_offsets, + BaseOffsetMap &base_offsets, + BaseOffsetMap &virtual_base_offsets) +{ + ClangASTMetrics::RegisterRecordLayout(); + + static unsigned int invocation_id = 0; + unsigned int current_id = invocation_id++; + + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + if (log) + { + log->Printf("LayoutRecordType[%u] on (ASTContext*)%p for (RecordDecl*)%p [name = '%s']", + current_id, + m_ast_context, + record, + record->getNameAsString().c_str()); + } + + + DeclFromParser parser_record(record); + DeclFromUser origin_record(parser_record.GetOrigin(m_ast_importer)); + + if (origin_record.IsInvalid()) + return false; + + FieldOffsetMap origin_field_offsets; + BaseOffsetMap origin_base_offsets; + BaseOffsetMap origin_virtual_base_offsets; + + ClangASTContext::GetCompleteDecl(&origin_record->getASTContext(), const_cast(origin_record.decl)); + + if (!origin_record.decl->getDefinition()) + return false; + + const ASTRecordLayout &record_layout(origin_record->getASTContext().getASTRecordLayout(origin_record.decl)); + + int field_idx = 0, field_count = record_layout.getFieldCount(); + + for (RecordDecl::field_iterator fi = origin_record->field_begin(), fe = origin_record->field_end(); + fi != fe; + ++fi) + { + if (field_idx >= field_count) + return false; // Layout didn't go well. Bail out. + + uint64_t field_offset = record_layout.getFieldOffset(field_idx); + + origin_field_offsets.insert(std::pair(*fi, field_offset)); + + field_idx++; + } + + ASTContext &parser_ast_context(record->getASTContext()); + + DeclFromUser origin_cxx_record(DynCast(origin_record)); + + if (origin_cxx_record.IsValid()) + { + if (!ExtractBaseOffsets(record_layout, origin_cxx_record, origin_base_offsets) || + !ExtractBaseOffsets(record_layout, origin_cxx_record, origin_virtual_base_offsets)) + return false; + } + + if (!ImportOffsetMap(field_offsets, origin_field_offsets, m_ast_importer, parser_ast_context) || + !ImportOffsetMap(base_offsets, origin_base_offsets, m_ast_importer, parser_ast_context) || + !ImportOffsetMap(virtual_base_offsets, origin_virtual_base_offsets, m_ast_importer, parser_ast_context)) + return false; + + size = record_layout.getSize().getQuantity() * m_ast_context->getCharWidth(); + alignment = record_layout.getAlignment().getQuantity() * m_ast_context->getCharWidth(); + + if (log) + { + log->Printf("LRT[%u] returned:", current_id); + log->Printf("LRT[%u] Original = (RecordDecl*)%p", current_id, origin_record.decl); + log->Printf("LRT[%u] Size = %" PRId64, current_id, size); + log->Printf("LRT[%u] Alignment = %" PRId64, current_id, alignment); + log->Printf("LRT[%u] Fields:", current_id); + for (RecordDecl::field_iterator fi = record->field_begin(), fe = record->field_end(); + fi != fe; + ++fi) + { + log->Printf("LRT[%u] (FieldDecl*)%p, Name = '%s', Offset = %" PRId64 " bits", + current_id, + *fi, + fi->getNameAsString().c_str(), + field_offsets[*fi]); + } + DeclFromParser parser_cxx_record = DynCast(parser_record); + if (parser_cxx_record.IsValid()) + { + log->Printf("LRT[%u] Bases:", current_id); + for (CXXRecordDecl::base_class_const_iterator bi = parser_cxx_record->bases_begin(), be = parser_cxx_record->bases_end(); + bi != be; + ++bi) + { + bool is_virtual = bi->isVirtual(); + + QualType base_type = bi->getType(); + const RecordType *base_record_type = base_type->getAs(); + DeclFromParser base_record(base_record_type->getDecl()); + DeclFromParser base_cxx_record = DynCast(base_record); + + log->Printf("LRT[%u] %s(CXXRecordDecl*)%p, Name = '%s', Offset = %" PRId64 " chars", + current_id, + (is_virtual ? "Virtual " : ""), + base_cxx_record.decl, + base_cxx_record.decl->getNameAsString().c_str(), + (is_virtual ? virtual_base_offsets[base_cxx_record.decl].getQuantity() : + base_offsets[base_cxx_record.decl].getQuantity())); + } + } + else + { + log->Printf("LRD[%u] Not a CXXRecord, so no bases", current_id); + } + } + + return true; +} + +void +ClangASTSource::CompleteNamespaceMap (ClangASTImporter::NamespaceMapSP &namespace_map, + const ConstString &name, + ClangASTImporter::NamespaceMapSP &parent_map) const +{ + static unsigned int invocation_id = 0; + unsigned int current_id = invocation_id++; + + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + if (log) + { + if (parent_map && parent_map->size()) + log->Printf("CompleteNamespaceMap[%u] on (ASTContext*)%p Searching for namespace %s in namespace %s", + current_id, + m_ast_context, + name.GetCString(), + parent_map->begin()->second.GetNamespaceDecl()->getDeclName().getAsString().c_str()); + else + log->Printf("CompleteNamespaceMap[%u] on (ASTContext*)%p Searching for namespace %s", + current_id, + m_ast_context, + name.GetCString()); + } + + + if (parent_map) + { + for (ClangASTImporter::NamespaceMap::iterator i = parent_map->begin(), e = parent_map->end(); + i != e; + ++i) + { + ClangNamespaceDecl found_namespace_decl; + + lldb::ModuleSP module_sp = i->first; + ClangNamespaceDecl module_parent_namespace_decl = i->second; + + SymbolVendor *symbol_vendor = module_sp->GetSymbolVendor(); + + if (!symbol_vendor) + continue; + + SymbolContext null_sc; + + found_namespace_decl = symbol_vendor->FindNamespace(null_sc, name, &module_parent_namespace_decl); + + if (!found_namespace_decl) + continue; + + namespace_map->push_back(std::pair(module_sp, found_namespace_decl)); + + if (log) + log->Printf(" CMN[%u] Found namespace %s in module %s", + current_id, + name.GetCString(), + module_sp->GetFileSpec().GetFilename().GetCString()); + } + } + else + { + const ModuleList &target_images = m_target->GetImages(); + Mutex::Locker modules_locker(target_images.GetMutex()); + + ClangNamespaceDecl null_namespace_decl; + + for (size_t i = 0, e = target_images.GetSize(); i < e; ++i) + { + lldb::ModuleSP image = target_images.GetModuleAtIndexUnlocked(i); + + if (!image) + continue; + + ClangNamespaceDecl found_namespace_decl; + + SymbolVendor *symbol_vendor = image->GetSymbolVendor(); + + if (!symbol_vendor) + continue; + + SymbolContext null_sc; + + found_namespace_decl = symbol_vendor->FindNamespace(null_sc, name, &null_namespace_decl); + + if (!found_namespace_decl) + continue; + + namespace_map->push_back(std::pair(image, found_namespace_decl)); + + if (log) + log->Printf(" CMN[%u] Found namespace %s in module %s", + current_id, + name.GetCString(), + image->GetFileSpec().GetFilename().GetCString()); + } + } +} + +NamespaceDecl * +ClangASTSource::AddNamespace (NameSearchContext &context, ClangASTImporter::NamespaceMapSP &namespace_decls) +{ + if (!namespace_decls) + return NULL; + + const ClangNamespaceDecl &namespace_decl = namespace_decls->begin()->second; + + Decl *copied_decl = m_ast_importer->CopyDecl(m_ast_context, namespace_decl.GetASTContext(), namespace_decl.GetNamespaceDecl()); + + if (!copied_decl) + return NULL; + + NamespaceDecl *copied_namespace_decl = dyn_cast(copied_decl); + + if (!copied_namespace_decl) + return NULL; + + context.m_decls.push_back(copied_namespace_decl); + + m_ast_importer->RegisterNamespaceMap(copied_namespace_decl, namespace_decls); + + return dyn_cast(copied_decl); +} + +ClangASTType +ClangASTSource::GuardedCopyType (const ClangASTType &src_type) +{ + ClangASTMetrics::RegisterLLDBImport(); + + SetImportInProgress(true); + + QualType copied_qual_type = m_ast_importer->CopyType (m_ast_context, src_type.GetASTContext(), src_type.GetQualType()); + + SetImportInProgress(false); + + if (copied_qual_type.getAsOpaquePtr() && copied_qual_type->getCanonicalTypeInternal().isNull()) + // this shouldn't happen, but we're hardening because the AST importer seems to be generating bad types + // on occasion. + return ClangASTType(); + + return ClangASTType(m_ast_context, copied_qual_type); +} + +clang::NamedDecl * +NameSearchContext::AddVarDecl(const ClangASTType &type) +{ + assert (type && "Type for variable must be valid!"); + + if (!type.IsValid()) + return NULL; + + IdentifierInfo *ii = m_decl_name.getAsIdentifierInfo(); + + clang::ASTContext *ast = type.GetASTContext(); + + clang::NamedDecl *Decl = VarDecl::Create(*ast, + const_cast(m_decl_context), + SourceLocation(), + SourceLocation(), + ii, + type.GetQualType(), + 0, + SC_Static); + m_decls.push_back(Decl); + + return Decl; +} + +clang::NamedDecl * +NameSearchContext::AddFunDecl (const ClangASTType &type) +{ + assert (type && "Type for variable must be valid!"); + + if (!type.IsValid()) + return NULL; + + if (m_function_types.count(type)) + return NULL; + + m_function_types.insert(type); + + QualType qual_type (type.GetQualType()); + + clang::ASTContext *ast = type.GetASTContext(); + + const bool isInlineSpecified = false; + const bool hasWrittenPrototype = true; + const bool isConstexprSpecified = false; + + clang::FunctionDecl *func_decl = FunctionDecl::Create (*ast, + const_cast(m_decl_context), + SourceLocation(), + SourceLocation(), + m_decl_name.getAsIdentifierInfo(), + qual_type, + NULL, + SC_Static, + isInlineSpecified, + hasWrittenPrototype, + isConstexprSpecified); + + // We have to do more than just synthesize the FunctionDecl. We have to + // synthesize ParmVarDecls for all of the FunctionDecl's arguments. To do + // this, we raid the function's FunctionProtoType for types. + + const FunctionProtoType *func_proto_type = qual_type.getTypePtr()->getAs(); + + if (func_proto_type) + { + unsigned NumArgs = func_proto_type->getNumArgs(); + unsigned ArgIndex; + + SmallVector parm_var_decls; + + for (ArgIndex = 0; ArgIndex < NumArgs; ++ArgIndex) + { + QualType arg_qual_type (func_proto_type->getArgType(ArgIndex)); + + parm_var_decls.push_back(ParmVarDecl::Create (*ast, + const_cast(m_decl_context), + SourceLocation(), + SourceLocation(), + NULL, + arg_qual_type, + NULL, + SC_Static, + NULL)); + } + + func_decl->setParams(ArrayRef(parm_var_decls)); + } + else + { + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + if (log) + log->Printf("Function type wasn't a FunctionProtoType"); + } + + m_decls.push_back(func_decl); + + return func_decl; +} + +clang::NamedDecl * +NameSearchContext::AddGenericFunDecl() +{ + FunctionProtoType::ExtProtoInfo proto_info; + + proto_info.Variadic = true; + + QualType generic_function_type(m_ast_source.m_ast_context->getFunctionType (m_ast_source.m_ast_context->UnknownAnyTy, // result + ArrayRef(), // argument types + proto_info)); + + return AddFunDecl(ClangASTType (m_ast_source.m_ast_context, generic_function_type)); +} + +clang::NamedDecl * +NameSearchContext::AddTypeDecl(const ClangASTType &clang_type) +{ + if (clang_type) + { + QualType qual_type = clang_type.GetQualType(); + + if (const TypedefType *typedef_type = llvm::dyn_cast(qual_type)) + { + TypedefNameDecl *typedef_name_decl = typedef_type->getDecl(); + + m_decls.push_back(typedef_name_decl); + + return (NamedDecl*)typedef_name_decl; + } + else if (const TagType *tag_type = qual_type->getAs()) + { + TagDecl *tag_decl = tag_type->getDecl(); + + m_decls.push_back(tag_decl); + + return tag_decl; + } + else if (const ObjCObjectType *objc_object_type = qual_type->getAs()) + { + ObjCInterfaceDecl *interface_decl = objc_object_type->getInterface(); + + m_decls.push_back((NamedDecl*)interface_decl); + + return (NamedDecl*)interface_decl; + } + } + return NULL; +} + +void +NameSearchContext::AddLookupResult (clang::DeclContextLookupConstResult result) +{ + for (clang::NamedDecl *decl : result) + m_decls.push_back (decl); +} + +void +NameSearchContext::AddNamedDecl (clang::NamedDecl *decl) +{ + m_decls.push_back (decl); +} diff --git a/contrib/llvm/tools/lldb/source/Expression/ClangExpressionDeclMap.cpp b/contrib/llvm/tools/lldb/source/Expression/ClangExpressionDeclMap.cpp new file mode 100644 index 00000000000..072e7819cd0 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Expression/ClangExpressionDeclMap.cpp @@ -0,0 +1,1956 @@ +//===-- ClangExpressionDeclMap.cpp -----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Expression/ClangExpressionDeclMap.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "clang/AST/ASTContext.h" +#include "clang/AST/DeclarationName.h" +#include "clang/AST/Decl.h" +#include "lldb/lldb-private.h" +#include "lldb/Core/Address.h" +#include "lldb/Core/Error.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/RegisterValue.h" +#include "lldb/Core/ValueObjectConstResult.h" +#include "lldb/Core/ValueObjectVariable.h" +#include "lldb/Expression/ASTDumper.h" +#include "lldb/Expression/ClangASTSource.h" +#include "lldb/Expression/ClangPersistentVariables.h" +#include "lldb/Expression/Materializer.h" +#include "lldb/Host/Endian.h" +#include "lldb/Symbol/ClangASTContext.h" +#include "lldb/Symbol/ClangNamespaceDecl.h" +#include "lldb/Symbol/CompileUnit.h" +#include "lldb/Symbol/Function.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Symbol/SymbolContext.h" +#include "lldb/Symbol/SymbolVendor.h" +#include "lldb/Symbol/Type.h" +#include "lldb/Symbol/TypeList.h" +#include "lldb/Symbol/Variable.h" +#include "lldb/Symbol/VariableList.h" +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/ObjCLanguageRuntime.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/StackFrame.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" + +using namespace lldb; +using namespace lldb_private; +using namespace clang; + +ClangExpressionDeclMap::ClangExpressionDeclMap (bool keep_result_in_memory, ExecutionContext &exe_ctx) : + ClangASTSource (exe_ctx.GetTargetSP()), + m_found_entities (), + m_struct_members (), + m_keep_result_in_memory (keep_result_in_memory), + m_parser_vars (), + m_struct_vars () +{ + EnableStructVars(); +} + +ClangExpressionDeclMap::~ClangExpressionDeclMap() +{ + // Note: The model is now that the parser's AST context and all associated + // data does not vanish until the expression has been executed. This means + // that valuable lookup data (like namespaces) doesn't vanish, but + + DidParse(); + DisableStructVars(); +} + +bool +ClangExpressionDeclMap::WillParse(ExecutionContext &exe_ctx, + Materializer *materializer) +{ + ClangASTMetrics::ClearLocalCounters(); + + EnableParserVars(); + m_parser_vars->m_exe_ctx = exe_ctx; + + Target *target = exe_ctx.GetTargetPtr(); + if (exe_ctx.GetFramePtr()) + m_parser_vars->m_sym_ctx = exe_ctx.GetFramePtr()->GetSymbolContext(lldb::eSymbolContextEverything); + else if (exe_ctx.GetThreadPtr() && exe_ctx.GetThreadPtr()->GetStackFrameAtIndex(0)) + m_parser_vars->m_sym_ctx = exe_ctx.GetThreadPtr()->GetStackFrameAtIndex(0)->GetSymbolContext(lldb::eSymbolContextEverything); + else if (exe_ctx.GetProcessPtr()) + { + m_parser_vars->m_sym_ctx.Clear(true); + m_parser_vars->m_sym_ctx.target_sp = exe_ctx.GetTargetSP(); + } + else if (target) + { + m_parser_vars->m_sym_ctx.Clear(true); + m_parser_vars->m_sym_ctx.target_sp = exe_ctx.GetTargetSP(); + } + + if (target) + { + m_parser_vars->m_persistent_vars = &target->GetPersistentVariables(); + + if (!target->GetScratchClangASTContext()) + return false; + } + + m_parser_vars->m_target_info = GetTargetInfo(); + m_parser_vars->m_materializer = materializer; + + return true; +} + +void +ClangExpressionDeclMap::DidParse() +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + if (log) + ClangASTMetrics::DumpCounters(log); + + if (m_parser_vars.get()) + { + for (size_t entity_index = 0, num_entities = m_found_entities.GetSize(); + entity_index < num_entities; + ++entity_index) + { + ClangExpressionVariableSP var_sp(m_found_entities.GetVariableAtIndex(entity_index)); + if (var_sp) + var_sp->DisableParserVars(GetParserID()); + } + + for (size_t pvar_index = 0, num_pvars = m_parser_vars->m_persistent_vars->GetSize(); + pvar_index < num_pvars; + ++pvar_index) + { + ClangExpressionVariableSP pvar_sp(m_parser_vars->m_persistent_vars->GetVariableAtIndex(pvar_index)); + if (pvar_sp) + pvar_sp->DisableParserVars(GetParserID()); + } + + DisableParserVars(); + } +} + +// Interface for IRForTarget + +ClangExpressionDeclMap::TargetInfo +ClangExpressionDeclMap::GetTargetInfo() +{ + assert (m_parser_vars.get()); + + TargetInfo ret; + + ExecutionContext &exe_ctx = m_parser_vars->m_exe_ctx; + + Process *process = exe_ctx.GetProcessPtr(); + if (process) + { + ret.byte_order = process->GetByteOrder(); + ret.address_byte_size = process->GetAddressByteSize(); + } + else + { + Target *target = exe_ctx.GetTargetPtr(); + if (target) + { + ret.byte_order = target->GetArchitecture().GetByteOrder(); + ret.address_byte_size = target->GetArchitecture().GetAddressByteSize(); + } + } + + return ret; +} + +bool +ClangExpressionDeclMap::AddPersistentVariable +( + const NamedDecl *decl, + const ConstString &name, + TypeFromParser parser_type, + bool is_result, + bool is_lvalue +) +{ + assert (m_parser_vars.get()); + + if (m_parser_vars->m_materializer && is_result) + { + Error err; + + ExecutionContext &exe_ctx = m_parser_vars->m_exe_ctx; + Target *target = exe_ctx.GetTargetPtr(); + if (target == NULL) + return false; + + ASTContext *context(target->GetScratchClangASTContext()->getASTContext()); + + TypeFromUser user_type(m_ast_importer->DeportType(context, + parser_type.GetASTContext(), + parser_type.GetOpaqueQualType()), + context); + + uint32_t offset = m_parser_vars->m_materializer->AddResultVariable(user_type, is_lvalue, m_keep_result_in_memory, err); + + ClangExpressionVariableSP var_sp = m_found_entities.CreateVariable(exe_ctx.GetBestExecutionContextScope(), + name, + user_type, + m_parser_vars->m_target_info.byte_order, + m_parser_vars->m_target_info.address_byte_size); + + if (!var_sp) + return false; + + var_sp->EnableParserVars(GetParserID()); + + ClangExpressionVariable::ParserVars *parser_vars = var_sp->GetParserVars(GetParserID()); + + parser_vars->m_named_decl = decl; + parser_vars->m_parser_type = parser_type; + + var_sp->EnableJITVars(GetParserID()); + + ClangExpressionVariable::JITVars *jit_vars = var_sp->GetJITVars(GetParserID()); + + jit_vars->m_offset = offset; + + return true; + } + + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + ExecutionContext &exe_ctx = m_parser_vars->m_exe_ctx; + Target *target = exe_ctx.GetTargetPtr(); + if (target == NULL) + return false; + + ASTContext *context(target->GetScratchClangASTContext()->getASTContext()); + + TypeFromUser user_type(m_ast_importer->DeportType(context, + parser_type.GetASTContext(), + parser_type.GetOpaqueQualType()), + context); + + if (!user_type.GetOpaqueQualType()) + { + if (log) + log->Printf("Persistent variable's type wasn't copied successfully"); + return false; + } + + if (!m_parser_vars->m_target_info.IsValid()) + return false; + + ClangExpressionVariableSP var_sp = m_parser_vars->m_persistent_vars->CreatePersistentVariable (exe_ctx.GetBestExecutionContextScope (), + name, + user_type, + m_parser_vars->m_target_info.byte_order, + m_parser_vars->m_target_info.address_byte_size); + + if (!var_sp) + return false; + + var_sp->m_frozen_sp->SetHasCompleteType(); + + if (is_result) + var_sp->m_flags |= ClangExpressionVariable::EVNeedsFreezeDry; + else + var_sp->m_flags |= ClangExpressionVariable::EVKeepInTarget; // explicitly-declared persistent variables should persist + + if (is_lvalue) + { + var_sp->m_flags |= ClangExpressionVariable::EVIsProgramReference; + } + else + { + var_sp->m_flags |= ClangExpressionVariable::EVIsLLDBAllocated; + var_sp->m_flags |= ClangExpressionVariable::EVNeedsAllocation; + } + + if (m_keep_result_in_memory) + { + var_sp->m_flags |= ClangExpressionVariable::EVKeepInTarget; + } + + if (log) + log->Printf("Created persistent variable with flags 0x%hx", var_sp->m_flags); + + var_sp->EnableParserVars(GetParserID()); + + ClangExpressionVariable::ParserVars *parser_vars = var_sp->GetParserVars(GetParserID()); + + parser_vars->m_named_decl = decl; + parser_vars->m_parser_type = parser_type; + + return true; +} + +bool +ClangExpressionDeclMap::AddValueToStruct +( + const NamedDecl *decl, + const ConstString &name, + llvm::Value *value, + size_t size, + off_t alignment +) +{ + assert (m_struct_vars.get()); + assert (m_parser_vars.get()); + + bool is_persistent_variable = false; + + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + m_struct_vars->m_struct_laid_out = false; + + if (m_struct_members.GetVariable(decl, GetParserID())) + return true; + + ClangExpressionVariableSP var_sp (m_found_entities.GetVariable(decl, GetParserID())); + + if (!var_sp) + { + var_sp = m_parser_vars->m_persistent_vars->GetVariable(decl, GetParserID()); + is_persistent_variable = true; + } + + if (!var_sp) + return false; + + if (log) + log->Printf("Adding value for (NamedDecl*)%p [%s - %s] to the structure", + decl, + name.GetCString(), + var_sp->GetName().GetCString()); + + // We know entity->m_parser_vars is valid because we used a parser variable + // to find it + + ClangExpressionVariable::ParserVars *parser_vars = var_sp->GetParserVars(GetParserID()); + + parser_vars->m_llvm_value = value; + + if (ClangExpressionVariable::JITVars *jit_vars = var_sp->GetJITVars(GetParserID())) + { + // We already laid this out; do not touch + + if (log) + log->Printf("Already placed at 0x%llx", (unsigned long long)jit_vars->m_offset); + } + + var_sp->EnableJITVars(GetParserID()); + + ClangExpressionVariable::JITVars *jit_vars = var_sp->GetJITVars(GetParserID()); + + jit_vars->m_alignment = alignment; + jit_vars->m_size = size; + + m_struct_members.AddVariable(var_sp); + + if (m_parser_vars->m_materializer) + { + uint32_t offset = 0; + + Error err; + + if (is_persistent_variable) + { + offset = m_parser_vars->m_materializer->AddPersistentVariable(var_sp, err); + } + else + { + if (const lldb_private::Symbol *sym = parser_vars->m_lldb_sym) + offset = m_parser_vars->m_materializer->AddSymbol(*sym, err); + else if (const RegisterInfo *reg_info = var_sp->GetRegisterInfo()) + offset = m_parser_vars->m_materializer->AddRegister(*reg_info, err); + else if (parser_vars->m_lldb_var) + offset = m_parser_vars->m_materializer->AddVariable(parser_vars->m_lldb_var, err); + } + + if (!err.Success()) + return false; + + if (log) + log->Printf("Placed at 0x%llx", (unsigned long long)offset); + + jit_vars->m_offset = offset; // TODO DoStructLayout() should not change this. + } + + return true; +} + +bool +ClangExpressionDeclMap::DoStructLayout () +{ + assert (m_struct_vars.get()); + + if (m_struct_vars->m_struct_laid_out) + return true; + + if (!m_parser_vars->m_materializer) + return false; + + m_struct_vars->m_struct_alignment = m_parser_vars->m_materializer->GetStructAlignment(); + m_struct_vars->m_struct_size = m_parser_vars->m_materializer->GetStructByteSize(); + m_struct_vars->m_struct_laid_out = true; + return true; +} + +bool ClangExpressionDeclMap::GetStructInfo +( + uint32_t &num_elements, + size_t &size, + off_t &alignment +) +{ + assert (m_struct_vars.get()); + + if (!m_struct_vars->m_struct_laid_out) + return false; + + num_elements = m_struct_members.GetSize(); + size = m_struct_vars->m_struct_size; + alignment = m_struct_vars->m_struct_alignment; + + return true; +} + +bool +ClangExpressionDeclMap::GetStructElement +( + const NamedDecl *&decl, + llvm::Value *&value, + off_t &offset, + ConstString &name, + uint32_t index +) +{ + assert (m_struct_vars.get()); + + if (!m_struct_vars->m_struct_laid_out) + return false; + + if (index >= m_struct_members.GetSize()) + return false; + + ClangExpressionVariableSP member_sp(m_struct_members.GetVariableAtIndex(index)); + + if (!member_sp) + return false; + + ClangExpressionVariable::ParserVars *parser_vars = member_sp->GetParserVars(GetParserID()); + ClangExpressionVariable::JITVars *jit_vars = member_sp->GetJITVars(GetParserID()); + + if (!parser_vars || + !jit_vars || + !member_sp->GetValueObject()) + return false; + + decl = parser_vars->m_named_decl; + value = parser_vars->m_llvm_value; + offset = jit_vars->m_offset; + name = member_sp->GetName(); + + return true; +} + +bool +ClangExpressionDeclMap::GetFunctionInfo +( + const NamedDecl *decl, + uint64_t &ptr +) +{ + ClangExpressionVariableSP entity_sp(m_found_entities.GetVariable(decl, GetParserID())); + + if (!entity_sp) + return false; + + // We know m_parser_vars is valid since we searched for the variable by + // its NamedDecl + + ClangExpressionVariable::ParserVars *parser_vars = entity_sp->GetParserVars(GetParserID()); + + ptr = parser_vars->m_lldb_value.GetScalar().ULongLong(); + + return true; +} + +static void +FindCodeSymbolInContext +( + const ConstString &name, + SymbolContext &sym_ctx, + SymbolContextList &sc_list +) +{ + SymbolContextList temp_sc_list; + if (sym_ctx.module_sp) + sym_ctx.module_sp->FindSymbolsWithNameAndType(name, eSymbolTypeAny, temp_sc_list); + + if (!sc_list.GetSize() && sym_ctx.target_sp) + sym_ctx.target_sp->GetImages().FindSymbolsWithNameAndType(name, eSymbolTypeAny, temp_sc_list); + + unsigned temp_sc_list_size = temp_sc_list.GetSize(); + for (unsigned i = 0; i < temp_sc_list_size; i++) + { + SymbolContext sym_ctx; + temp_sc_list.GetContextAtIndex(i, sym_ctx); + if (sym_ctx.symbol) + { + switch (sym_ctx.symbol->GetType()) + { + case eSymbolTypeCode: + case eSymbolTypeResolver: + sc_list.Append(sym_ctx); + break; + + default: + break; + } + } + } +} + +bool +ClangExpressionDeclMap::GetFunctionAddress +( + const ConstString &name, + uint64_t &func_addr +) +{ + assert (m_parser_vars.get()); + + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + ExecutionContext &exe_ctx = m_parser_vars->m_exe_ctx; + Target *target = exe_ctx.GetTargetPtr(); + // Back out in all cases where we're not fully initialized + if (target == NULL) + return false; + if (!m_parser_vars->m_sym_ctx.target_sp) + return false; + + SymbolContextList sc_list; + + FindCodeSymbolInContext(name, m_parser_vars->m_sym_ctx, sc_list); + + if (!sc_list.GetSize()) + { + // We occasionally get debug information in which a const function is reported + // as non-const, so the mangled name is wrong. This is a hack to compensate. + + if (!strncmp(name.GetCString(), "_ZN", 3) && + strncmp(name.GetCString(), "_ZNK", 4)) + { + std::string fixed_scratch("_ZNK"); + fixed_scratch.append(name.GetCString() + 3); + ConstString fixed_name(fixed_scratch.c_str()); + + if (log) + log->Printf("Failed to find symbols given non-const name %s; trying %s", name.GetCString(), fixed_name.GetCString()); + + FindCodeSymbolInContext(fixed_name, m_parser_vars->m_sym_ctx, sc_list); + } + } + + if (!sc_list.GetSize()) + return false; + + SymbolContext sym_ctx; + sc_list.GetContextAtIndex(0, sym_ctx); + + const Address *func_so_addr = NULL; + bool is_indirect_function = false; + + if (sym_ctx.function) + func_so_addr = &sym_ctx.function->GetAddressRange().GetBaseAddress(); + else if (sym_ctx.symbol) { + func_so_addr = &sym_ctx.symbol->GetAddress(); + is_indirect_function = sym_ctx.symbol->IsIndirect(); + } else + return false; + + if (!func_so_addr || !func_so_addr->IsValid()) + return false; + + func_addr = func_so_addr->GetCallableLoadAddress (target, is_indirect_function); + + return true; +} + +addr_t +ClangExpressionDeclMap::GetSymbolAddress (Target &target, Process *process, const ConstString &name, lldb::SymbolType symbol_type) +{ + SymbolContextList sc_list; + + target.GetImages().FindSymbolsWithNameAndType(name, symbol_type, sc_list); + + const uint32_t num_matches = sc_list.GetSize(); + addr_t symbol_load_addr = LLDB_INVALID_ADDRESS; + + for (uint32_t i=0; iGetAddress(); + + if (!sym_address || !sym_address->IsValid()) + continue; + + if (sym_address) + { + switch (sym_ctx.symbol->GetType()) + { + case eSymbolTypeCode: + case eSymbolTypeTrampoline: + symbol_load_addr = sym_address->GetCallableLoadAddress (&target); + break; + + case eSymbolTypeResolver: + symbol_load_addr = sym_address->GetCallableLoadAddress (&target, true); + break; + + case eSymbolTypeData: + case eSymbolTypeRuntime: + case eSymbolTypeVariable: + case eSymbolTypeLocal: + case eSymbolTypeParam: + case eSymbolTypeInvalid: + case eSymbolTypeAbsolute: + case eSymbolTypeException: + case eSymbolTypeSourceFile: + case eSymbolTypeHeaderFile: + case eSymbolTypeObjectFile: + case eSymbolTypeCommonBlock: + case eSymbolTypeBlock: + case eSymbolTypeVariableType: + case eSymbolTypeLineEntry: + case eSymbolTypeLineHeader: + case eSymbolTypeScopeBegin: + case eSymbolTypeScopeEnd: + case eSymbolTypeAdditional: + case eSymbolTypeCompiler: + case eSymbolTypeInstrumentation: + case eSymbolTypeUndefined: + case eSymbolTypeObjCClass: + case eSymbolTypeObjCMetaClass: + case eSymbolTypeObjCIVar: + symbol_load_addr = sym_address->GetLoadAddress (&target); + break; + } + } + } + + if (symbol_load_addr == LLDB_INVALID_ADDRESS && process) + { + ObjCLanguageRuntime *runtime = process->GetObjCLanguageRuntime(); + + if (runtime) + { + symbol_load_addr = runtime->LookupRuntimeSymbol(name); + } + } + + return symbol_load_addr; +} + +addr_t +ClangExpressionDeclMap::GetSymbolAddress (const ConstString &name, lldb::SymbolType symbol_type) +{ + assert (m_parser_vars.get()); + + if (!m_parser_vars->m_exe_ctx.GetTargetPtr()) + return false; + + return GetSymbolAddress(m_parser_vars->m_exe_ctx.GetTargetRef(), m_parser_vars->m_exe_ctx.GetProcessPtr(), name, symbol_type); +} + +const Symbol * +ClangExpressionDeclMap::FindGlobalDataSymbol (Target &target, + const ConstString &name) +{ + SymbolContextList sc_list; + + target.GetImages().FindSymbolsWithNameAndType(name, eSymbolTypeAny, sc_list); + + const uint32_t matches = sc_list.GetSize(); + for (uint32_t i=0; iGetAddress(); + + if (sym_address && sym_address->IsValid()) + { + switch (symbol->GetType()) + { + case eSymbolTypeData: + case eSymbolTypeRuntime: + case eSymbolTypeAbsolute: + case eSymbolTypeObjCClass: + case eSymbolTypeObjCMetaClass: + case eSymbolTypeObjCIVar: + if (symbol->GetDemangledNameIsSynthesized()) + { + // If the demangled name was synthesized, then don't use it + // for expressions. Only let the symbol match if the mangled + // named matches for these symbols. + if (symbol->GetMangled().GetMangledName() != name) + break; + } + return symbol; + + case eSymbolTypeCode: // We already lookup functions elsewhere + case eSymbolTypeVariable: + case eSymbolTypeLocal: + case eSymbolTypeParam: + case eSymbolTypeTrampoline: + case eSymbolTypeInvalid: + case eSymbolTypeException: + case eSymbolTypeSourceFile: + case eSymbolTypeHeaderFile: + case eSymbolTypeObjectFile: + case eSymbolTypeCommonBlock: + case eSymbolTypeBlock: + case eSymbolTypeVariableType: + case eSymbolTypeLineEntry: + case eSymbolTypeLineHeader: + case eSymbolTypeScopeBegin: + case eSymbolTypeScopeEnd: + case eSymbolTypeAdditional: + case eSymbolTypeCompiler: + case eSymbolTypeInstrumentation: + case eSymbolTypeUndefined: + case eSymbolTypeResolver: + break; + } + } + } + } + + return NULL; +} + +lldb::VariableSP +ClangExpressionDeclMap::FindGlobalVariable +( + Target &target, + ModuleSP &module, + const ConstString &name, + ClangNamespaceDecl *namespace_decl, + TypeFromUser *type +) +{ + VariableList vars; + + if (module && namespace_decl) + module->FindGlobalVariables (name, namespace_decl, true, -1, vars); + else + target.GetImages().FindGlobalVariables(name, true, -1, vars); + + if (vars.GetSize()) + { + if (type) + { + for (size_t i = 0; i < vars.GetSize(); ++i) + { + VariableSP var_sp = vars.GetVariableAtIndex(i); + + if (ClangASTContext::AreTypesSame(*type, var_sp->GetType()->GetClangFullType())) + return var_sp; + } + } + else + { + return vars.GetVariableAtIndex(0); + } + } + + return VariableSP(); +} + +// Interface for ClangASTSource + +void +ClangExpressionDeclMap::FindExternalVisibleDecls (NameSearchContext &context) +{ + assert (m_ast_context); + + ClangASTMetrics::RegisterVisibleQuery(); + + const ConstString name(context.m_decl_name.getAsString().c_str()); + + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + if (GetImportInProgress()) + { + if (log && log->GetVerbose()) + log->Printf("Ignoring a query during an import"); + return; + } + + static unsigned int invocation_id = 0; + unsigned int current_id = invocation_id++; + + if (log) + { + if (!context.m_decl_context) + log->Printf("ClangExpressionDeclMap::FindExternalVisibleDecls[%u] for '%s' in a NULL DeclContext", current_id, name.GetCString()); + else if (const NamedDecl *context_named_decl = dyn_cast(context.m_decl_context)) + log->Printf("ClangExpressionDeclMap::FindExternalVisibleDecls[%u] for '%s' in '%s'", current_id, name.GetCString(), context_named_decl->getNameAsString().c_str()); + else + log->Printf("ClangExpressionDeclMap::FindExternalVisibleDecls[%u] for '%s' in a '%s'", current_id, name.GetCString(), context.m_decl_context->getDeclKindName()); + } + + if (const NamespaceDecl *namespace_context = dyn_cast(context.m_decl_context)) + { + ClangASTImporter::NamespaceMapSP namespace_map = m_ast_importer->GetNamespaceMap(namespace_context); + + if (log && log->GetVerbose()) + log->Printf(" CEDM::FEVD[%u] Inspecting (NamespaceMap*)%p (%d entries)", + current_id, + namespace_map.get(), + (int)namespace_map->size()); + + if (!namespace_map) + return; + + for (ClangASTImporter::NamespaceMap::iterator i = namespace_map->begin(), e = namespace_map->end(); + i != e; + ++i) + { + if (log) + log->Printf(" CEDM::FEVD[%u] Searching namespace %s in module %s", + current_id, + i->second.GetNamespaceDecl()->getNameAsString().c_str(), + i->first->GetFileSpec().GetFilename().GetCString()); + + FindExternalVisibleDecls(context, + i->first, + i->second, + current_id); + } + } + else if (isa(context.m_decl_context)) + { + ClangNamespaceDecl namespace_decl; + + if (log) + log->Printf(" CEDM::FEVD[%u] Searching the root namespace", current_id); + + FindExternalVisibleDecls(context, + lldb::ModuleSP(), + namespace_decl, + current_id); + } + + if (!context.m_found.variable) + ClangASTSource::FindExternalVisibleDecls(context); +} + +void +ClangExpressionDeclMap::FindExternalVisibleDecls (NameSearchContext &context, + lldb::ModuleSP module_sp, + ClangNamespaceDecl &namespace_decl, + unsigned int current_id) +{ + assert (m_ast_context); + + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + SymbolContextList sc_list; + + const ConstString name(context.m_decl_name.getAsString().c_str()); + + const char *name_unique_cstr = name.GetCString(); + + if (name_unique_cstr == NULL) + return; + + static ConstString id_name("id"); + static ConstString Class_name("Class"); + + if (name == id_name || name == Class_name) + return; + + // Only look for functions by name out in our symbols if the function + // doesn't start with our phony prefix of '$' + Target *target = m_parser_vars->m_exe_ctx.GetTargetPtr(); + StackFrame *frame = m_parser_vars->m_exe_ctx.GetFramePtr(); + if (name_unique_cstr[0] == '$' && !namespace_decl) + { + static ConstString g_lldb_class_name ("$__lldb_class"); + + if (name == g_lldb_class_name) + { + // Clang is looking for the type of "this" + + if (frame == NULL) + return; + + SymbolContext sym_ctx = frame->GetSymbolContext(lldb::eSymbolContextFunction); + + if (!sym_ctx.function) + return; + + // Get the block that defines the function + Block *function_block = sym_ctx.GetFunctionBlock(); + + if (!function_block) + return; + + clang::DeclContext *decl_context = function_block->GetClangDeclContext(); + + if (!decl_context) + return; + + clang::CXXMethodDecl *method_decl = llvm::dyn_cast(decl_context); + + if (method_decl) + { + clang::CXXRecordDecl *class_decl = method_decl->getParent(); + + QualType class_qual_type(class_decl->getTypeForDecl(), 0); + + TypeFromUser class_user_type (class_qual_type.getAsOpaquePtr(), + &class_decl->getASTContext()); + + if (log) + { + ASTDumper ast_dumper(class_qual_type); + log->Printf(" CEDM::FEVD[%u] Adding type for $__lldb_class: %s", current_id, ast_dumper.GetCString()); + } + + TypeFromParser class_type = CopyClassType(class_user_type, current_id); + + if (!class_type.IsValid()) + return; + + TypeSourceInfo *type_source_info = m_ast_context->getTrivialTypeSourceInfo(QualType::getFromOpaquePtr(class_type.GetOpaqueQualType())); + + if (!type_source_info) + return; + + TypedefDecl *typedef_decl = TypedefDecl::Create(*m_ast_context, + m_ast_context->getTranslationUnitDecl(), + SourceLocation(), + SourceLocation(), + context.m_decl_name.getAsIdentifierInfo(), + type_source_info); + + + if (!typedef_decl) + return; + + context.AddNamedDecl(typedef_decl); + + if (method_decl->isInstance()) + { + // self is a pointer to the object + + QualType class_pointer_type = method_decl->getASTContext().getPointerType(class_qual_type); + + TypeFromUser self_user_type(class_pointer_type.getAsOpaquePtr(), + &method_decl->getASTContext()); + + m_struct_vars->m_object_pointer_type = self_user_type; + } + } + else + { + // This branch will get hit if we are executing code in the context of a function that + // claims to have an object pointer (through DW_AT_object_pointer?) but is not formally a + // method of the class. In that case, just look up the "this" variable in the the current + // scope and use its type. + // FIXME: This code is formally correct, but clang doesn't currently emit DW_AT_object_pointer + // for C++ so it hasn't actually been tested. + + VariableList *vars = frame->GetVariableList(false); + + lldb::VariableSP this_var = vars->FindVariable(ConstString("this")); + + if (this_var && + this_var->IsInScope(frame) && + this_var->LocationIsValidForFrame (frame)) + { + Type *this_type = this_var->GetType(); + + if (!this_type) + return; + + ClangASTType pointee_type = this_type->GetClangForwardType().GetPointeeType(); + + if (pointee_type.IsValid()) + { + if (log) + { + ASTDumper ast_dumper(this_type->GetClangFullType()); + log->Printf(" FEVD[%u] Adding type for $__lldb_objc_class: %s", current_id, ast_dumper.GetCString()); + } + + TypeFromUser class_user_type(pointee_type); + AddOneType(context, class_user_type, current_id); + + + TypeFromUser this_user_type(this_type->GetClangFullType()); + m_struct_vars->m_object_pointer_type = this_user_type; + return; + } + } + } + + return; + } + + static ConstString g_lldb_objc_class_name ("$__lldb_objc_class"); + if (name == g_lldb_objc_class_name) + { + // Clang is looking for the type of "*self" + + if (!frame) + return; + + SymbolContext sym_ctx = frame->GetSymbolContext(lldb::eSymbolContextFunction); + + if (!sym_ctx.function) + return; + + // Get the block that defines the function + Block *function_block = sym_ctx.GetFunctionBlock(); + + if (!function_block) + return; + + clang::DeclContext *decl_context = function_block->GetClangDeclContext(); + + if (!decl_context) + return; + + clang::ObjCMethodDecl *method_decl = llvm::dyn_cast(decl_context); + + if (method_decl) + { + ObjCInterfaceDecl* self_interface = method_decl->getClassInterface(); + + if (!self_interface) + return; + + const clang::Type *interface_type = self_interface->getTypeForDecl(); + + if (!interface_type) + return; // This is unlikely, but we have seen crashes where this occurred + + TypeFromUser class_user_type(QualType(interface_type, 0).getAsOpaquePtr(), + &method_decl->getASTContext()); + + if (log) + { + ASTDumper ast_dumper(interface_type); + log->Printf(" FEVD[%u] Adding type for $__lldb_objc_class: %s", current_id, ast_dumper.GetCString()); + } + + AddOneType(context, class_user_type, current_id); + + if (method_decl->isInstanceMethod()) + { + // self is a pointer to the object + + QualType class_pointer_type = method_decl->getASTContext().getObjCObjectPointerType(QualType(interface_type, 0)); + + TypeFromUser self_user_type(class_pointer_type.getAsOpaquePtr(), + &method_decl->getASTContext()); + + m_struct_vars->m_object_pointer_type = self_user_type; + } + else + { + // self is a Class pointer + QualType class_type = method_decl->getASTContext().getObjCClassType(); + + TypeFromUser self_user_type(class_type.getAsOpaquePtr(), + &method_decl->getASTContext()); + + m_struct_vars->m_object_pointer_type = self_user_type; + } + + return; + } + else + { + // This branch will get hit if we are executing code in the context of a function that + // claims to have an object pointer (through DW_AT_object_pointer?) but is not formally a + // method of the class. In that case, just look up the "self" variable in the the current + // scope and use its type. + + VariableList *vars = frame->GetVariableList(false); + + lldb::VariableSP self_var = vars->FindVariable(ConstString("self")); + + if (self_var && + self_var->IsInScope(frame) && + self_var->LocationIsValidForFrame (frame)) + { + Type *self_type = self_var->GetType(); + + if (!self_type) + return; + + ClangASTType self_clang_type = self_type->GetClangFullType(); + + if (self_clang_type.IsObjCClassType()) + { + return; + } + else if (self_clang_type.IsObjCObjectPointerType()) + { + self_clang_type = self_clang_type.GetPointeeType(); + + if (!self_clang_type) + return; + + if (log) + { + ASTDumper ast_dumper(self_type->GetClangFullType()); + log->Printf(" FEVD[%u] Adding type for $__lldb_objc_class: %s", current_id, ast_dumper.GetCString()); + } + + TypeFromUser class_user_type (self_clang_type); + + AddOneType(context, class_user_type, current_id); + + TypeFromUser self_user_type(self_type->GetClangFullType()); + + m_struct_vars->m_object_pointer_type = self_user_type; + return; + } + } + } + + return; + } + + // any other $__lldb names should be weeded out now + if (!::strncmp(name_unique_cstr, "$__lldb", sizeof("$__lldb") - 1)) + return; + + do + { + if (!target) + break; + + ClangASTContext *scratch_clang_ast_context = target->GetScratchClangASTContext(); + + if (!scratch_clang_ast_context) + break; + + ASTContext *scratch_ast_context = scratch_clang_ast_context->getASTContext(); + + if (!scratch_ast_context) + break; + + TypeDecl *ptype_type_decl = m_parser_vars->m_persistent_vars->GetPersistentType(name); + + if (!ptype_type_decl) + break; + + Decl *parser_ptype_decl = m_ast_importer->CopyDecl(m_ast_context, scratch_ast_context, ptype_type_decl); + + if (!parser_ptype_decl) + break; + + TypeDecl *parser_ptype_type_decl = dyn_cast(parser_ptype_decl); + + if (!parser_ptype_type_decl) + break; + + if (log) + log->Printf(" CEDM::FEVD[%u] Found persistent type %s", current_id, name.GetCString()); + + context.AddNamedDecl(parser_ptype_type_decl); + } while (0); + + ClangExpressionVariableSP pvar_sp(m_parser_vars->m_persistent_vars->GetVariable(name)); + + if (pvar_sp) + { + AddOneVariable(context, pvar_sp, current_id); + return; + } + + const char *reg_name(&name.GetCString()[1]); + + if (m_parser_vars->m_exe_ctx.GetRegisterContext()) + { + const RegisterInfo *reg_info(m_parser_vars->m_exe_ctx.GetRegisterContext()->GetRegisterInfoByName(reg_name)); + + if (reg_info) + { + if (log) + log->Printf(" CEDM::FEVD[%u] Found register %s", current_id, reg_info->name); + + AddOneRegister(context, reg_info, current_id); + } + } + } + else + { + ValueObjectSP valobj; + VariableSP var; + Error err; + + if (frame && !namespace_decl) + { + valobj = frame->GetValueForVariableExpressionPath(name_unique_cstr, + eNoDynamicValues, + StackFrame::eExpressionPathOptionCheckPtrVsMember || + StackFrame::eExpressionPathOptionsAllowDirectIVarAccess || + StackFrame::eExpressionPathOptionsNoFragileObjcIvar || + StackFrame::eExpressionPathOptionsNoSyntheticChildren || + StackFrame::eExpressionPathOptionsNoSyntheticArrayRange, + var, + err); + + // If we found a variable in scope, no need to pull up function names + if (err.Success() && var) + { + AddOneVariable(context, var, valobj, current_id); + context.m_found.variable = true; + return; + } + } + + if (target) + { + var = FindGlobalVariable (*target, + module_sp, + name, + &namespace_decl, + NULL); + + if (var) + { + valobj = ValueObjectVariable::Create(target, var); + AddOneVariable(context, var, valobj, current_id); + context.m_found.variable = true; + return; + } + } + + if (!context.m_found.variable) + { + const bool include_inlines = false; + const bool append = false; + + if (namespace_decl && module_sp) + { + const bool include_symbols = false; + + module_sp->FindFunctions(name, + &namespace_decl, + eFunctionNameTypeBase, + include_symbols, + include_inlines, + append, + sc_list); + } + else if (target && !namespace_decl) + { + const bool include_symbols = true; + + // TODO Fix FindFunctions so that it doesn't return + // instance methods for eFunctionNameTypeBase. + + target->GetImages().FindFunctions(name, + eFunctionNameTypeFull, + include_symbols, + include_inlines, + append, + sc_list); + } + + if (sc_list.GetSize()) + { + Symbol *generic_symbol = NULL; + Symbol *non_extern_symbol = NULL; + + for (uint32_t index = 0, num_indices = sc_list.GetSize(); + index < num_indices; + ++index) + { + SymbolContext sym_ctx; + sc_list.GetContextAtIndex(index, sym_ctx); + + if (sym_ctx.function) + { + clang::DeclContext *decl_ctx = sym_ctx.function->GetClangDeclContext(); + + if (!decl_ctx) + continue; + + // Filter out class/instance methods. + if (dyn_cast(decl_ctx)) + continue; + if (dyn_cast(decl_ctx)) + continue; + + AddOneFunction(context, sym_ctx.function, NULL, current_id); + context.m_found.function_with_type_info = true; + context.m_found.function = true; + } + else if (sym_ctx.symbol) + { + if (sym_ctx.symbol->IsExternal()) + generic_symbol = sym_ctx.symbol; + else + non_extern_symbol = sym_ctx.symbol; + } + } + + if (!context.m_found.function_with_type_info) + { + if (generic_symbol) + { + AddOneFunction (context, NULL, generic_symbol, current_id); + context.m_found.function = true; + } + else if (non_extern_symbol) + { + AddOneFunction (context, NULL, non_extern_symbol, current_id); + context.m_found.function = true; + } + } + } + + if (target && !context.m_found.variable && !namespace_decl) + { + // We couldn't find a non-symbol variable for this. Now we'll hunt for a generic + // data symbol, and -- if it is found -- treat it as a variable. + + const Symbol *data_symbol = FindGlobalDataSymbol(*target, name); + + if (data_symbol) + { + AddOneGenericVariable(context, *data_symbol, current_id); + context.m_found.variable = true; + } + } + } + } +} + +static clang_type_t +MaybePromoteToBlockPointerType +( + ASTContext *ast_context, + clang_type_t candidate_type +) +{ + if (!candidate_type) + return candidate_type; + + QualType candidate_qual_type = QualType::getFromOpaquePtr(candidate_type); + + const PointerType *candidate_pointer_type = dyn_cast(candidate_qual_type); + + if (!candidate_pointer_type) + return candidate_type; + + QualType pointee_qual_type = candidate_pointer_type->getPointeeType(); + + const RecordType *pointee_record_type = dyn_cast(pointee_qual_type); + + if (!pointee_record_type) + return candidate_type; + + RecordDecl *pointee_record_decl = pointee_record_type->getDecl(); + + if (!pointee_record_decl->isRecord()) + return candidate_type; + + if (!pointee_record_decl->getName().startswith(llvm::StringRef("__block_literal_"))) + return candidate_type; + + QualType generic_function_type = ast_context->getFunctionNoProtoType(ast_context->UnknownAnyTy); + QualType block_pointer_type = ast_context->getBlockPointerType(generic_function_type); + + return block_pointer_type.getAsOpaquePtr(); +} + +bool +ClangExpressionDeclMap::GetVariableValue (VariableSP &var, + lldb_private::Value &var_location, + TypeFromUser *user_type, + TypeFromParser *parser_type) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + Type *var_type = var->GetType(); + + if (!var_type) + { + if (log) + log->PutCString("Skipped a definition because it has no type"); + return false; + } + + ClangASTType var_clang_type = var_type->GetClangFullType(); + + if (!var_clang_type) + { + if (log) + log->PutCString("Skipped a definition because it has no Clang type"); + return false; + } + + // commented out because of + ASTContext *ast = var_type->GetClangASTContext().getASTContext(); + + if (!ast) + { + if (log) + log->PutCString("There is no AST context for the current execution context"); + return false; + } + //var_clang_type = MaybePromoteToBlockPointerType (ast, var_clang_type); + + DWARFExpression &var_location_expr = var->LocationExpression(); + + lldb::addr_t loclist_base_load_addr = LLDB_INVALID_ADDRESS; + + Target *target = m_parser_vars->m_exe_ctx.GetTargetPtr(); + + if (var_location_expr.IsLocationList()) + { + SymbolContext var_sc; + var->CalculateSymbolContext (&var_sc); + loclist_base_load_addr = var_sc.function->GetAddressRange().GetBaseAddress().GetLoadAddress (target); + } + Error err; + + if (var->GetLocationIsConstantValueData()) + { + DataExtractor const_value_extractor; + + if (var_location_expr.GetExpressionData(const_value_extractor)) + { + var_location = Value(const_value_extractor.GetDataStart(), const_value_extractor.GetByteSize()); + var_location.SetValueType(Value::eValueTypeHostAddress); + } + else + { + if (log) + log->Printf("Error evaluating constant variable: %s", err.AsCString()); + return false; + } + } + + ClangASTType type_to_use = GuardedCopyType(var_clang_type); + + if (!type_to_use) + { + if (log) + log->Printf("Couldn't copy a variable's type into the parser's AST context"); + + return false; + } + + if (parser_type) + *parser_type = TypeFromParser(type_to_use); + + if (var_location.GetContextType() == Value::eContextTypeInvalid) + var_location.SetClangType(type_to_use); + + if (var_location.GetValueType() == Value::eValueTypeFileAddress) + { + SymbolContext var_sc; + var->CalculateSymbolContext(&var_sc); + + if (!var_sc.module_sp) + return false; + + Address so_addr(var_location.GetScalar().ULongLong(), var_sc.module_sp->GetSectionList()); + + lldb::addr_t load_addr = so_addr.GetLoadAddress(target); + + if (load_addr != LLDB_INVALID_ADDRESS) + { + var_location.GetScalar() = load_addr; + var_location.SetValueType(Value::eValueTypeLoadAddress); + } + } + + if (user_type) + *user_type = TypeFromUser(var_clang_type); + + return true; +} + +void +ClangExpressionDeclMap::AddOneVariable (NameSearchContext &context, VariableSP var, ValueObjectSP valobj, unsigned int current_id) +{ + assert (m_parser_vars.get()); + + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + TypeFromUser ut; + TypeFromParser pt; + Value var_location; + + if (!GetVariableValue (var, var_location, &ut, &pt)) + return; + + clang::QualType parser_opaque_type = QualType::getFromOpaquePtr(pt.GetOpaqueQualType()); + + if (parser_opaque_type.isNull()) + return; + + if (const clang::Type *parser_type = parser_opaque_type.getTypePtr()) + { + if (const TagType *tag_type = dyn_cast(parser_type)) + CompleteType(tag_type->getDecl()); + } + + + bool is_reference = pt.IsReferenceType(); + + NamedDecl *var_decl = NULL; + if (is_reference) + var_decl = context.AddVarDecl(pt); + else + var_decl = context.AddVarDecl(pt.GetLValueReferenceType()); + + std::string decl_name(context.m_decl_name.getAsString()); + ConstString entity_name(decl_name.c_str()); + ClangExpressionVariableSP entity(m_found_entities.CreateVariable (valobj)); + + assert (entity.get()); + entity->EnableParserVars(GetParserID()); + ClangExpressionVariable::ParserVars *parser_vars = entity->GetParserVars(GetParserID()); + parser_vars->m_parser_type = pt; + parser_vars->m_named_decl = var_decl; + parser_vars->m_llvm_value = NULL; + parser_vars->m_lldb_value = var_location; + parser_vars->m_lldb_var = var; + + if (is_reference) + entity->m_flags |= ClangExpressionVariable::EVTypeIsReference; + + if (log) + { + ASTDumper orig_dumper(ut.GetOpaqueQualType()); + ASTDumper ast_dumper(var_decl); + log->Printf(" CEDM::FEVD[%u] Found variable %s, returned %s (original %s)", current_id, decl_name.c_str(), ast_dumper.GetCString(), orig_dumper.GetCString()); + } +} + +void +ClangExpressionDeclMap::AddOneVariable(NameSearchContext &context, + ClangExpressionVariableSP &pvar_sp, + unsigned int current_id) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + TypeFromUser user_type (pvar_sp->GetTypeFromUser()); + + TypeFromParser parser_type (GuardedCopyType(user_type)); + + if (!parser_type.GetOpaqueQualType()) + { + if (log) + log->Printf(" CEDM::FEVD[%u] Couldn't import type for pvar %s", current_id, pvar_sp->GetName().GetCString()); + return; + } + + NamedDecl *var_decl = context.AddVarDecl(parser_type.GetLValueReferenceType()); + + pvar_sp->EnableParserVars(GetParserID()); + ClangExpressionVariable::ParserVars *parser_vars = pvar_sp->GetParserVars(GetParserID()); + parser_vars->m_parser_type = parser_type; + parser_vars->m_named_decl = var_decl; + parser_vars->m_llvm_value = NULL; + parser_vars->m_lldb_value.Clear(); + + if (log) + { + ASTDumper ast_dumper(var_decl); + log->Printf(" CEDM::FEVD[%u] Added pvar %s, returned %s", current_id, pvar_sp->GetName().GetCString(), ast_dumper.GetCString()); + } +} + +void +ClangExpressionDeclMap::AddOneGenericVariable(NameSearchContext &context, + const Symbol &symbol, + unsigned int current_id) +{ + assert(m_parser_vars.get()); + + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + Target *target = m_parser_vars->m_exe_ctx.GetTargetPtr(); + + if (target == NULL) + return; + + ASTContext *scratch_ast_context = target->GetScratchClangASTContext()->getASTContext(); + + TypeFromUser user_type (ClangASTContext::GetBasicType(scratch_ast_context, eBasicTypeVoid).GetPointerType().GetLValueReferenceType()); + TypeFromParser parser_type (ClangASTContext::GetBasicType(m_ast_context, eBasicTypeVoid).GetPointerType().GetLValueReferenceType()); + NamedDecl *var_decl = context.AddVarDecl(parser_type); + + std::string decl_name(context.m_decl_name.getAsString()); + ConstString entity_name(decl_name.c_str()); + ClangExpressionVariableSP entity(m_found_entities.CreateVariable (m_parser_vars->m_exe_ctx.GetBestExecutionContextScope (), + entity_name, + user_type, + m_parser_vars->m_target_info.byte_order, + m_parser_vars->m_target_info.address_byte_size)); + assert (entity.get()); + + entity->EnableParserVars(GetParserID()); + ClangExpressionVariable::ParserVars *parser_vars = entity->GetParserVars(GetParserID()); + + const Address &symbol_address = symbol.GetAddress(); + lldb::addr_t symbol_load_addr = symbol_address.GetLoadAddress(target); + + //parser_vars->m_lldb_value.SetContext(Value::eContextTypeClangType, user_type.GetOpaqueQualType()); + parser_vars->m_lldb_value.SetClangType(user_type); + parser_vars->m_lldb_value.GetScalar() = symbol_load_addr; + parser_vars->m_lldb_value.SetValueType(Value::eValueTypeLoadAddress); + + parser_vars->m_parser_type = parser_type; + parser_vars->m_named_decl = var_decl; + parser_vars->m_llvm_value = NULL; + parser_vars->m_lldb_sym = &symbol; + + if (log) + { + ASTDumper ast_dumper(var_decl); + + log->Printf(" CEDM::FEVD[%u] Found variable %s, returned %s", current_id, decl_name.c_str(), ast_dumper.GetCString()); + } +} + +bool +ClangExpressionDeclMap::ResolveUnknownTypes() +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + Target *target = m_parser_vars->m_exe_ctx.GetTargetPtr(); + + ASTContext *scratch_ast_context = target->GetScratchClangASTContext()->getASTContext(); + + for (size_t index = 0, num_entities = m_found_entities.GetSize(); + index < num_entities; + ++index) + { + ClangExpressionVariableSP entity = m_found_entities.GetVariableAtIndex(index); + + ClangExpressionVariable::ParserVars *parser_vars = entity->GetParserVars(GetParserID()); + + if (entity->m_flags & ClangExpressionVariable::EVUnknownType) + { + const NamedDecl *named_decl = parser_vars->m_named_decl; + const VarDecl *var_decl = dyn_cast(named_decl); + + if (!var_decl) + { + if (log) + log->Printf("Entity of unknown type does not have a VarDecl"); + return false; + } + + if (log) + { + ASTDumper ast_dumper(const_cast(var_decl)); + log->Printf("Variable of unknown type now has Decl %s", ast_dumper.GetCString()); + } + + QualType var_type = var_decl->getType(); + TypeFromParser parser_type(var_type.getAsOpaquePtr(), &var_decl->getASTContext()); + + lldb::clang_type_t copied_type = m_ast_importer->CopyType(scratch_ast_context, &var_decl->getASTContext(), var_type.getAsOpaquePtr()); + + if (!copied_type) + { + if (log) + log->Printf("ClangExpressionDeclMap::ResolveUnknownType - Couldn't import the type for a variable"); + + return (bool) lldb::ClangExpressionVariableSP(); + } + + TypeFromUser user_type(copied_type, scratch_ast_context); + +// parser_vars->m_lldb_value.SetContext(Value::eContextTypeClangType, user_type.GetOpaqueQualType()); + parser_vars->m_lldb_value.SetClangType(user_type); + parser_vars->m_parser_type = parser_type; + + entity->SetClangType(user_type); + + entity->m_flags &= ~(ClangExpressionVariable::EVUnknownType); + } + } + + return true; +} + +void +ClangExpressionDeclMap::AddOneRegister (NameSearchContext &context, + const RegisterInfo *reg_info, + unsigned int current_id) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + ClangASTType clang_type = ClangASTContext::GetBuiltinTypeForEncodingAndBitSize (m_ast_context, + reg_info->encoding, + reg_info->byte_size * 8); + + if (!clang_type) + { + if (log) + log->Printf(" Tried to add a type for %s, but couldn't get one", context.m_decl_name.getAsString().c_str()); + return; + } + + TypeFromParser parser_clang_type (clang_type); + + NamedDecl *var_decl = context.AddVarDecl(parser_clang_type); + + ClangExpressionVariableSP entity(m_found_entities.CreateVariable (m_parser_vars->m_exe_ctx.GetBestExecutionContextScope(), + m_parser_vars->m_target_info.byte_order, + m_parser_vars->m_target_info.address_byte_size)); + assert (entity.get()); + + std::string decl_name(context.m_decl_name.getAsString()); + entity->SetName (ConstString (decl_name.c_str())); + entity->SetRegisterInfo (reg_info); + entity->EnableParserVars(GetParserID()); + ClangExpressionVariable::ParserVars *parser_vars = entity->GetParserVars(GetParserID()); + parser_vars->m_parser_type = parser_clang_type; + parser_vars->m_named_decl = var_decl; + parser_vars->m_llvm_value = NULL; + parser_vars->m_lldb_value.Clear(); + entity->m_flags |= ClangExpressionVariable::EVBareRegister; + + if (log) + { + ASTDumper ast_dumper(var_decl); + log->Printf(" CEDM::FEVD[%d] Added register %s, returned %s", current_id, context.m_decl_name.getAsString().c_str(), ast_dumper.GetCString()); + } +} + +void +ClangExpressionDeclMap::AddOneFunction (NameSearchContext &context, + Function* function, + Symbol* symbol, + unsigned int current_id) +{ + assert (m_parser_vars.get()); + + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + NamedDecl *function_decl = NULL; + const Address *fun_address = NULL; + ClangASTType function_clang_type; + + bool is_indirect_function = false; + + if (function) + { + Type *function_type = function->GetType(); + + if (!function_type) + { + if (log) + log->PutCString(" Skipped a function because it has no type"); + return; + } + + function_clang_type = function_type->GetClangFullType(); + + if (!function_clang_type) + { + if (log) + log->PutCString(" Skipped a function because it has no Clang type"); + return; + } + + fun_address = &function->GetAddressRange().GetBaseAddress(); + + ClangASTType copied_function_type = GuardedCopyType(function_clang_type); + if (copied_function_type) + { + function_decl = context.AddFunDecl(copied_function_type); + + if (!function_decl) + { + if (log) + { + log->Printf (" Failed to create a function decl for '%s' {0x%8.8" PRIx64 "}", + function_type->GetName().GetCString(), + function_type->GetID()); + } + + return; + } + } + else + { + // We failed to copy the type we found + if (log) + { + log->Printf (" Failed to import the function type '%s' {0x%8.8" PRIx64 "} into the expression parser AST contenxt", + function_type->GetName().GetCString(), + function_type->GetID()); + } + + return; + } + } + else if (symbol) + { + fun_address = &symbol->GetAddress(); + function_decl = context.AddGenericFunDecl(); + is_indirect_function = symbol->IsIndirect(); + } + else + { + if (log) + log->PutCString(" AddOneFunction called with no function and no symbol"); + return; + } + + Target *target = m_parser_vars->m_exe_ctx.GetTargetPtr(); + + lldb::addr_t load_addr = fun_address->GetCallableLoadAddress(target, is_indirect_function); + + ClangExpressionVariableSP entity(m_found_entities.CreateVariable (m_parser_vars->m_exe_ctx.GetBestExecutionContextScope (), + m_parser_vars->m_target_info.byte_order, + m_parser_vars->m_target_info.address_byte_size)); + assert (entity.get()); + + std::string decl_name(context.m_decl_name.getAsString()); + entity->SetName(ConstString(decl_name.c_str())); + entity->SetClangType (function_clang_type); + entity->EnableParserVars(GetParserID()); + + ClangExpressionVariable::ParserVars *parser_vars = entity->GetParserVars(GetParserID()); + + if (load_addr != LLDB_INVALID_ADDRESS) + { + parser_vars->m_lldb_value.SetValueType(Value::eValueTypeLoadAddress); + parser_vars->m_lldb_value.GetScalar() = load_addr; + } + else + { + // We have to try finding a file address. + + lldb::addr_t file_addr = fun_address->GetFileAddress(); + + parser_vars->m_lldb_value.SetValueType(Value::eValueTypeFileAddress); + parser_vars->m_lldb_value.GetScalar() = file_addr; + } + + + parser_vars->m_named_decl = function_decl; + parser_vars->m_llvm_value = NULL; + + if (log) + { + ASTDumper ast_dumper(function_decl); + + StreamString ss; + + fun_address->Dump(&ss, m_parser_vars->m_exe_ctx.GetBestExecutionContextScope(), Address::DumpStyleResolvedDescription); + + log->Printf(" CEDM::FEVD[%u] Found %s function %s (description %s), returned %s", + current_id, + (function ? "specific" : "generic"), + decl_name.c_str(), + ss.GetData(), + ast_dumper.GetCString()); + } +} + +TypeFromParser +ClangExpressionDeclMap::CopyClassType(TypeFromUser &ut, + unsigned int current_id) +{ + ClangASTType copied_clang_type = GuardedCopyType(ut); + + if (!copied_clang_type) + { + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + if (log) + log->Printf("ClangExpressionDeclMap::CopyClassType - Couldn't import the type"); + + return TypeFromParser(); + } + + if (copied_clang_type.IsAggregateType() && copied_clang_type.GetCompleteType ()) + { + ClangASTType void_clang_type = ClangASTContext::GetBasicType(m_ast_context, eBasicTypeVoid); + ClangASTType void_ptr_clang_type = void_clang_type.GetPointerType(); + + ClangASTType method_type = ClangASTContext::CreateFunctionType (m_ast_context, + void_clang_type, + &void_ptr_clang_type, + 1, + false, + copied_clang_type.GetTypeQualifiers()); + + const bool is_virtual = false; + const bool is_static = false; + const bool is_inline = false; + const bool is_explicit = false; + const bool is_attr_used = true; + const bool is_artificial = false; + + copied_clang_type.AddMethodToCXXRecordType ("$__lldb_expr", + method_type, + lldb::eAccessPublic, + is_virtual, + is_static, + is_inline, + is_explicit, + is_attr_used, + is_artificial); + } + + return TypeFromParser(copied_clang_type); +} + +void +ClangExpressionDeclMap::AddOneType(NameSearchContext &context, + TypeFromUser &ut, + unsigned int current_id) +{ + ClangASTType copied_clang_type = GuardedCopyType(ut); + + if (!copied_clang_type) + { + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + if (log) + log->Printf("ClangExpressionDeclMap::AddOneType - Couldn't import the type"); + + return; + } + + context.AddTypeDecl(copied_clang_type); +} diff --git a/contrib/llvm/tools/lldb/source/Expression/ClangExpressionParser.cpp b/contrib/llvm/tools/lldb/source/Expression/ClangExpressionParser.cpp new file mode 100644 index 00000000000..d026f2f3c12 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Expression/ClangExpressionParser.cpp @@ -0,0 +1,598 @@ +//===-- ClangExpressionParser.cpp -------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "lldb/Expression/ClangExpressionParser.h" + +#include "lldb/Core/ArchSpec.h" +#include "lldb/Core/DataBufferHeap.h" +#include "lldb/Core/Debugger.h" +#include "lldb/Core/Disassembler.h" +#include "lldb/Core/Stream.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Expression/ClangASTSource.h" +#include "lldb/Expression/ClangExpression.h" +#include "lldb/Expression/ClangExpressionDeclMap.h" +#include "lldb/Expression/IRExecutionUnit.h" +#include "lldb/Expression/IRDynamicChecks.h" +#include "lldb/Expression/IRInterpreter.h" +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/ObjCLanguageRuntime.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Target.h" + +#include "clang/AST/ASTContext.h" +#include "clang/AST/ExternalASTSource.h" +#include "clang/Basic/FileManager.h" +#include "clang/Basic/TargetInfo.h" +#include "clang/Basic/Version.h" +#include "clang/CodeGen/CodeGenAction.h" +#include "clang/CodeGen/ModuleBuilder.h" +#include "clang/Driver/CC1Options.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/CompilerInvocation.h" +#include "clang/Frontend/FrontendActions.h" +#include "clang/Frontend/FrontendDiagnostic.h" +#include "clang/Frontend/FrontendPluginRegistry.h" +#include "clang/Frontend/TextDiagnosticBuffer.h" +#include "clang/Frontend/TextDiagnosticPrinter.h" +#include "clang/Lex/Preprocessor.h" +#include "clang/Parse/ParseAST.h" +#include "clang/Rewrite/Frontend/FrontendActions.h" +#include "clang/Sema/SemaConsumer.h" +#include "clang/StaticAnalyzer/Frontend/FrontendActions.h" + +#include "llvm/ADT/StringRef.h" +#include "llvm/ExecutionEngine/ExecutionEngine.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/PathV1.h" +#include "llvm/Support/TargetSelect.h" + +#if defined(__FreeBSD__) +#define USE_STANDARD_JIT +#endif + +#if defined (USE_STANDARD_JIT) +#include "llvm/ExecutionEngine/JIT.h" +#else +#include "llvm/ExecutionEngine/MCJIT.h" +#endif +#include "llvm/IR/LLVMContext.h" +#include "llvm/IR/Module.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/DynamicLibrary.h" +#include "llvm/Support/Host.h" +#include "llvm/Support/Signals.h" + +using namespace clang; +using namespace llvm; +using namespace lldb_private; + +//===----------------------------------------------------------------------===// +// Utility Methods for Clang +//===----------------------------------------------------------------------===// + +std::string GetBuiltinIncludePath(const char *Argv0) { + llvm::sys::Path P = + llvm::sys::Path::GetMainExecutable(Argv0, + (void*)(intptr_t) GetBuiltinIncludePath); + + if (!P.isEmpty()) { + P.eraseComponent(); // Remove /clang from foo/bin/clang + P.eraseComponent(); // Remove /bin from foo/bin + + // Get foo/lib/clang//include + P.appendComponent("lib"); + P.appendComponent("clang"); + P.appendComponent(CLANG_VERSION_STRING); + P.appendComponent("include"); + } + + return P.str(); +} + + +//===----------------------------------------------------------------------===// +// Main driver for Clang +//===----------------------------------------------------------------------===// + +static void LLVMErrorHandler(void *UserData, const std::string &Message) { + DiagnosticsEngine &Diags = *static_cast(UserData); + + Diags.Report(diag::err_fe_error_backend) << Message; + + // We cannot recover from llvm errors. + assert(0); +} + +static FrontendAction *CreateFrontendBaseAction(CompilerInstance &CI) { + using namespace clang::frontend; + + switch (CI.getFrontendOpts().ProgramAction) { + default: + llvm_unreachable("Invalid program action!"); + + case ASTDump: return new ASTDumpAction(); + case ASTPrint: return new ASTPrintAction(); + case ASTDumpXML: return new ASTDumpXMLAction(); + case ASTView: return new ASTViewAction(); + case DumpRawTokens: return new DumpRawTokensAction(); + case DumpTokens: return new DumpTokensAction(); + case EmitAssembly: return new EmitAssemblyAction(); + case EmitBC: return new EmitBCAction(); + case EmitHTML: return new HTMLPrintAction(); + case EmitLLVM: return new EmitLLVMAction(); + case EmitLLVMOnly: return new EmitLLVMOnlyAction(); + case EmitCodeGenOnly: return new EmitCodeGenOnlyAction(); + case EmitObj: return new EmitObjAction(); + case FixIt: return new FixItAction(); + case GeneratePCH: return new GeneratePCHAction(); + case GeneratePTH: return new GeneratePTHAction(); + case InitOnly: return new InitOnlyAction(); + case ParseSyntaxOnly: return new SyntaxOnlyAction(); + + case PluginAction: { + for (FrontendPluginRegistry::iterator it = + FrontendPluginRegistry::begin(), ie = FrontendPluginRegistry::end(); + it != ie; ++it) { + if (it->getName() == CI.getFrontendOpts().ActionName) { + llvm::OwningPtr P(it->instantiate()); + if (!P->ParseArgs(CI, CI.getFrontendOpts().PluginArgs)) + return 0; + return P.take(); + } + } + + CI.getDiagnostics().Report(diag::err_fe_invalid_plugin_name) + << CI.getFrontendOpts().ActionName; + return 0; + } + + case PrintDeclContext: return new DeclContextPrintAction(); + case PrintPreamble: return new PrintPreambleAction(); + case PrintPreprocessedInput: return new PrintPreprocessedAction(); + case RewriteMacros: return new RewriteMacrosAction(); + case RewriteObjC: return new RewriteObjCAction(); + case RewriteTest: return new RewriteTestAction(); + //case RunAnalysis: return new AnalysisAction(); + case RunPreprocessorOnly: return new PreprocessOnlyAction(); + } +} + +static FrontendAction *CreateFrontendAction(CompilerInstance &CI) { + // Create the underlying action. + FrontendAction *Act = CreateFrontendBaseAction(CI); + if (!Act) + return 0; + + // If there are any AST files to merge, create a frontend action + // adaptor to perform the merge. + if (!CI.getFrontendOpts().ASTMergeFiles.empty()) + Act = new ASTMergeAction(Act, CI.getFrontendOpts().ASTMergeFiles); + + return Act; +} + +//===----------------------------------------------------------------------===// +// Implementation of ClangExpressionParser +//===----------------------------------------------------------------------===// + +ClangExpressionParser::ClangExpressionParser (ExecutionContextScope *exe_scope, + ClangExpression &expr) : + m_expr (expr), + m_compiler (), + m_code_generator () +{ + // Initialize targets first, so that --version shows registered targets. + static struct InitializeLLVM { + InitializeLLVM() { + llvm::InitializeAllTargets(); + llvm::InitializeAllAsmPrinters(); + llvm::InitializeAllTargetMCs(); + llvm::InitializeAllDisassemblers(); + + llvm::DisablePrettyStackTrace = true; + } + } InitializeLLVM; + + // 1. Create a new compiler instance. + m_compiler.reset(new CompilerInstance()); + + // 2. Install the target. + + lldb::TargetSP target_sp; + if (exe_scope) + target_sp = exe_scope->CalculateTarget(); + + // TODO: figure out what to really do when we don't have a valid target. + // Sometimes this will be ok to just use the host target triple (when we + // evaluate say "2+3", but other expressions like breakpoint conditions + // and other things that _are_ target specific really shouldn't just be + // using the host triple. This needs to be fixed in a better way. + if (target_sp && target_sp->GetArchitecture().IsValid()) + { + std::string triple = target_sp->GetArchitecture().GetTriple().str(); + + int dash_count = 0; + for (size_t i = 0; i < triple.size(); ++i) + { + if (triple[i] == '-') + dash_count++; + if (dash_count == 3) + { + triple.resize(i); + break; + } + } + + m_compiler->getTargetOpts().Triple = triple; + } + else + { + m_compiler->getTargetOpts().Triple = llvm::sys::getDefaultTargetTriple(); + } + + if (target_sp->GetArchitecture().GetMachine() == llvm::Triple::x86 || + target_sp->GetArchitecture().GetMachine() == llvm::Triple::x86_64) + { + m_compiler->getTargetOpts().Features.push_back("+sse"); + m_compiler->getTargetOpts().Features.push_back("+sse2"); + } + + if (m_compiler->getTargetOpts().Triple.find("ios") != std::string::npos) + m_compiler->getTargetOpts().ABI = "apcs-gnu"; + + m_compiler->createDiagnostics(); + + // Create the target instance. + m_compiler->setTarget(TargetInfo::CreateTargetInfo(m_compiler->getDiagnostics(), + &m_compiler->getTargetOpts())); + + assert (m_compiler->hasTarget()); + + // 3. Set options. + + lldb::LanguageType language = expr.Language(); + + switch (language) + { + case lldb::eLanguageTypeC: + break; + case lldb::eLanguageTypeObjC: + m_compiler->getLangOpts().ObjC1 = true; + m_compiler->getLangOpts().ObjC2 = true; + break; + case lldb::eLanguageTypeC_plus_plus: + m_compiler->getLangOpts().CPlusPlus = true; + m_compiler->getLangOpts().CPlusPlus11 = true; + break; + case lldb::eLanguageTypeObjC_plus_plus: + default: + m_compiler->getLangOpts().ObjC1 = true; + m_compiler->getLangOpts().ObjC2 = true; + m_compiler->getLangOpts().CPlusPlus = true; + m_compiler->getLangOpts().CPlusPlus11 = true; + break; + } + + m_compiler->getLangOpts().Bool = true; + m_compiler->getLangOpts().WChar = true; + m_compiler->getLangOpts().Blocks = true; + m_compiler->getLangOpts().DebuggerSupport = true; // Features specifically for debugger clients + if (expr.DesiredResultType() == ClangExpression::eResultTypeId) + m_compiler->getLangOpts().DebuggerCastResultToId = true; + + // Spell checking is a nice feature, but it ends up completing a + // lot of types that we didn't strictly speaking need to complete. + // As a result, we spend a long time parsing and importing debug + // information. + m_compiler->getLangOpts().SpellChecking = false; + + lldb::ProcessSP process_sp; + if (exe_scope) + process_sp = exe_scope->CalculateProcess(); + + if (process_sp && m_compiler->getLangOpts().ObjC1) + { + if (process_sp->GetObjCLanguageRuntime()) + { + if (process_sp->GetObjCLanguageRuntime()->GetRuntimeVersion() == eAppleObjC_V2) + m_compiler->getLangOpts().ObjCRuntime.set(ObjCRuntime::MacOSX, VersionTuple(10, 7)); + else + m_compiler->getLangOpts().ObjCRuntime.set(ObjCRuntime::FragileMacOSX, VersionTuple(10, 7)); + + if (process_sp->GetObjCLanguageRuntime()->HasNewLiteralsAndIndexing()) + m_compiler->getLangOpts().DebuggerObjCLiteral = true; + } + } + + m_compiler->getLangOpts().ThreadsafeStatics = false; + m_compiler->getLangOpts().AccessControl = false; // Debuggers get universal access + m_compiler->getLangOpts().DollarIdents = true; // $ indicates a persistent variable name + + // Set CodeGen options + m_compiler->getCodeGenOpts().EmitDeclMetadata = true; + m_compiler->getCodeGenOpts().InstrumentFunctions = false; + + // Disable some warnings. + m_compiler->getDiagnostics().setDiagnosticGroupMapping("unused-value", clang::diag::MAP_IGNORE, SourceLocation()); + m_compiler->getDiagnostics().setDiagnosticGroupMapping("odr", clang::diag::MAP_IGNORE, SourceLocation()); + + // Inform the target of the language options + // + // FIXME: We shouldn't need to do this, the target should be immutable once + // created. This complexity should be lifted elsewhere. + m_compiler->getTarget().setForcedLangOptions(m_compiler->getLangOpts()); + + // 4. Set up the diagnostic buffer for reporting errors + + m_compiler->getDiagnostics().setClient(new clang::TextDiagnosticBuffer); + + // 5. Set up the source management objects inside the compiler + + clang::FileSystemOptions file_system_options; + m_file_manager.reset(new clang::FileManager(file_system_options)); + + if (!m_compiler->hasSourceManager()) + m_compiler->createSourceManager(*m_file_manager.get()); + + m_compiler->createFileManager(); + m_compiler->createPreprocessor(); + + // 6. Most of this we get from the CompilerInstance, but we + // also want to give the context an ExternalASTSource. + m_selector_table.reset(new SelectorTable()); + m_builtin_context.reset(new Builtin::Context()); + + std::unique_ptr ast_context(new ASTContext(m_compiler->getLangOpts(), + m_compiler->getSourceManager(), + &m_compiler->getTarget(), + m_compiler->getPreprocessor().getIdentifierTable(), + *m_selector_table.get(), + *m_builtin_context.get(), + 0)); + + ClangExpressionDeclMap *decl_map = m_expr.DeclMap(); + + if (decl_map) + { + llvm::OwningPtr ast_source(decl_map->CreateProxy()); + decl_map->InstallASTContext(ast_context.get()); + ast_context->setExternalSource(ast_source); + } + + m_compiler->setASTContext(ast_context.release()); + + std::string module_name("$__lldb_module"); + + m_llvm_context.reset(new LLVMContext()); + m_code_generator.reset(CreateLLVMCodeGen(m_compiler->getDiagnostics(), + module_name, + m_compiler->getCodeGenOpts(), + m_compiler->getTargetOpts(), + *m_llvm_context)); +} + +ClangExpressionParser::~ClangExpressionParser() +{ +} + +unsigned +ClangExpressionParser::Parse (Stream &stream) +{ + TextDiagnosticBuffer *diag_buf = static_cast(m_compiler->getDiagnostics().getClient()); + + diag_buf->FlushDiagnostics (m_compiler->getDiagnostics()); + + MemoryBuffer *memory_buffer = MemoryBuffer::getMemBufferCopy(m_expr.Text(), __FUNCTION__); + m_compiler->getSourceManager().createMainFileIDForMemBuffer (memory_buffer); + + diag_buf->BeginSourceFile(m_compiler->getLangOpts(), &m_compiler->getPreprocessor()); + + ASTConsumer *ast_transformer = m_expr.ASTTransformer(m_code_generator.get()); + + if (ast_transformer) + ParseAST(m_compiler->getPreprocessor(), ast_transformer, m_compiler->getASTContext()); + else + ParseAST(m_compiler->getPreprocessor(), m_code_generator.get(), m_compiler->getASTContext()); + + diag_buf->EndSourceFile(); + + TextDiagnosticBuffer::const_iterator diag_iterator; + + int num_errors = 0; + + for (diag_iterator = diag_buf->warn_begin(); + diag_iterator != diag_buf->warn_end(); + ++diag_iterator) + stream.Printf("warning: %s\n", (*diag_iterator).second.c_str()); + + num_errors = 0; + + for (diag_iterator = diag_buf->err_begin(); + diag_iterator != diag_buf->err_end(); + ++diag_iterator) + { + num_errors++; + stream.Printf("error: %s\n", (*diag_iterator).second.c_str()); + } + + for (diag_iterator = diag_buf->note_begin(); + diag_iterator != diag_buf->note_end(); + ++diag_iterator) + stream.Printf("note: %s\n", (*diag_iterator).second.c_str()); + + if (!num_errors) + { + if (m_expr.DeclMap() && !m_expr.DeclMap()->ResolveUnknownTypes()) + { + stream.Printf("error: Couldn't infer the type of a variable\n"); + num_errors++; + } + } + + return num_errors; +} + +static bool FindFunctionInModule (ConstString &mangled_name, + llvm::Module *module, + const char *orig_name) +{ + for (llvm::Module::iterator fi = module->getFunctionList().begin(), fe = module->getFunctionList().end(); + fi != fe; + ++fi) + { + if (fi->getName().str().find(orig_name) != std::string::npos) + { + mangled_name.SetCString(fi->getName().str().c_str()); + return true; + } + } + + return false; +} + +Error +ClangExpressionParser::PrepareForExecution (lldb::addr_t &func_addr, + lldb::addr_t &func_end, + std::unique_ptr &execution_unit_ap, + ExecutionContext &exe_ctx, + bool &can_interpret, + ExecutionPolicy execution_policy) +{ + func_addr = LLDB_INVALID_ADDRESS; + func_end = LLDB_INVALID_ADDRESS; + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + std::unique_ptr execution_engine_ap; + + Error err; + + std::unique_ptr module_ap (m_code_generator->ReleaseModule()); + + if (!module_ap.get()) + { + err.SetErrorToGenericError(); + err.SetErrorString("IR doesn't contain a module"); + return err; + } + + // Find the actual name of the function (it's often mangled somehow) + + ConstString function_name; + + if (!FindFunctionInModule(function_name, module_ap.get(), m_expr.FunctionName())) + { + err.SetErrorToGenericError(); + err.SetErrorStringWithFormat("Couldn't find %s() in the module", m_expr.FunctionName()); + return err; + } + else + { + if (log) + log->Printf("Found function %s for %s", function_name.AsCString(), m_expr.FunctionName()); + } + + m_execution_unit.reset(new IRExecutionUnit(m_llvm_context, // handed off here + module_ap, // handed off here + function_name, + exe_ctx.GetTargetSP(), + m_compiler->getTargetOpts().Features)); + + ClangExpressionDeclMap *decl_map = m_expr.DeclMap(); // result can be NULL + + if (decl_map) + { + Stream *error_stream = NULL; + Target *target = exe_ctx.GetTargetPtr(); + if (target) + error_stream = &target->GetDebugger().GetErrorStream(); + + IRForTarget ir_for_target(decl_map, + m_expr.NeedsVariableResolution(), + *m_execution_unit, + error_stream, + function_name.AsCString()); + + bool ir_can_run = ir_for_target.runOnModule(*m_execution_unit->GetModule()); + + Error interpret_error; + + can_interpret = IRInterpreter::CanInterpret(*m_execution_unit->GetModule(), *m_execution_unit->GetFunction(), interpret_error); + + Process *process = exe_ctx.GetProcessPtr(); + + if (!ir_can_run) + { + err.SetErrorString("The expression could not be prepared to run in the target"); + return err; + } + + if (!can_interpret && execution_policy == eExecutionPolicyNever) + { + err.SetErrorStringWithFormat("Can't run the expression locally: %s", interpret_error.AsCString()); + return err; + } + + if (!process && execution_policy == eExecutionPolicyAlways) + { + err.SetErrorString("Expression needed to run in the target, but the target can't be run"); + return err; + } + + if (execution_policy == eExecutionPolicyAlways || !can_interpret) + { + if (m_expr.NeedsValidation() && process) + { + if (!process->GetDynamicCheckers()) + { + DynamicCheckerFunctions *dynamic_checkers = new DynamicCheckerFunctions(); + + StreamString install_errors; + + if (!dynamic_checkers->Install(install_errors, exe_ctx)) + { + if (install_errors.GetString().empty()) + err.SetErrorString ("couldn't install checkers, unknown error"); + else + err.SetErrorString (install_errors.GetString().c_str()); + + return err; + } + + process->SetDynamicCheckers(dynamic_checkers); + + if (log) + log->Printf("== [ClangUserExpression::Evaluate] Finished installing dynamic checkers =="); + } + + IRDynamicChecks ir_dynamic_checks(*process->GetDynamicCheckers(), function_name.AsCString()); + + if (!ir_dynamic_checks.runOnModule(*m_execution_unit->GetModule())) + { + err.SetErrorToGenericError(); + err.SetErrorString("Couldn't add dynamic checks to the expression"); + return err; + } + } + + m_execution_unit->GetRunnableInfo(err, func_addr, func_end); + } + } + else + { + m_execution_unit->GetRunnableInfo(err, func_addr, func_end); + } + + execution_unit_ap.reset (m_execution_unit.release()); + + return err; +} diff --git a/contrib/llvm/tools/lldb/source/Expression/ClangExpressionVariable.cpp b/contrib/llvm/tools/lldb/source/Expression/ClangExpressionVariable.cpp new file mode 100644 index 00000000000..0d355ce341c --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Expression/ClangExpressionVariable.cpp @@ -0,0 +1,136 @@ +//===-- ClangExpressionVariable.cpp -----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Expression/ClangExpressionVariable.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "clang/AST/ASTContext.h" +#include "lldb/Core/ConstString.h" +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/Stream.h" +#include "lldb/Core/Value.h" +#include "lldb/Core/ValueObjectConstResult.h" +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/Process.h" + +using namespace lldb_private; +using namespace clang; + +ClangExpressionVariable::ClangExpressionVariable(ExecutionContextScope *exe_scope, lldb::ByteOrder byte_order, uint32_t addr_byte_size) : + m_parser_vars(), + m_jit_vars (), + m_flags (EVNone), + m_frozen_sp (ValueObjectConstResult::Create (exe_scope, byte_order, addr_byte_size)) +{ +} + +ClangExpressionVariable::ClangExpressionVariable (const lldb::ValueObjectSP &valobj_sp) : + m_parser_vars(), + m_jit_vars (), + m_flags (EVNone), + m_frozen_sp (valobj_sp) +{ +} + +//---------------------------------------------------------------------- +/// Return the variable's size in bytes +//---------------------------------------------------------------------- +size_t +ClangExpressionVariable::GetByteSize () +{ + return m_frozen_sp->GetByteSize(); +} + +const ConstString & +ClangExpressionVariable::GetName () +{ + return m_frozen_sp->GetName(); +} + +lldb::ValueObjectSP +ClangExpressionVariable::GetValueObject() +{ + return m_frozen_sp; +} + +RegisterInfo * +ClangExpressionVariable::GetRegisterInfo() +{ + return m_frozen_sp->GetValue().GetRegisterInfo(); +} + +void +ClangExpressionVariable::SetRegisterInfo (const RegisterInfo *reg_info) +{ + return m_frozen_sp->GetValue().SetContext (Value::eContextTypeRegisterInfo, const_cast(reg_info)); +} + +ClangASTType +ClangExpressionVariable::GetClangType() +{ + return m_frozen_sp->GetClangType(); +} + +void +ClangExpressionVariable::SetClangType(const ClangASTType &clang_type) +{ + m_frozen_sp->GetValue().SetClangType(clang_type); +} + + +TypeFromUser +ClangExpressionVariable::GetTypeFromUser() +{ + TypeFromUser tfu (m_frozen_sp->GetClangType()); + return tfu; +} + +uint8_t * +ClangExpressionVariable::GetValueBytes() +{ + const size_t byte_size = m_frozen_sp->GetByteSize(); + if (byte_size > 0) + { + if (m_frozen_sp->GetDataExtractor().GetByteSize() < byte_size) + { + m_frozen_sp->GetValue().ResizeData(byte_size); + m_frozen_sp->GetValue().GetData (m_frozen_sp->GetDataExtractor()); + } + return const_cast(m_frozen_sp->GetDataExtractor().GetDataStart()); + } + return NULL; +} + +void +ClangExpressionVariable::SetName (const ConstString &name) +{ + m_frozen_sp->SetName (name); +} + +void +ClangExpressionVariable::ValueUpdated () +{ + m_frozen_sp->ValueUpdated (); +} + +void +ClangExpressionVariable::TransferAddress (bool force) +{ + if (m_live_sp.get() == NULL) + return; + + if (m_frozen_sp.get() == NULL) + return; + + if (force || (m_frozen_sp->GetLiveAddress() == LLDB_INVALID_ADDRESS)) + m_frozen_sp->SetLiveAddress(m_live_sp->GetLiveAddress()); +} diff --git a/contrib/llvm/tools/lldb/source/Expression/ClangFunction.cpp b/contrib/llvm/tools/lldb/source/Expression/ClangFunction.cpp new file mode 100644 index 00000000000..177138df801 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Expression/ClangFunction.cpp @@ -0,0 +1,633 @@ +//===-- ClangFunction.cpp ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + + +// C Includes +// C++ Includes +// Other libraries and framework includes +#include "clang/AST/ASTContext.h" +#include "clang/AST/RecordLayout.h" +#include "clang/CodeGen/CodeGenAction.h" +#include "clang/CodeGen/ModuleBuilder.h" +#include "clang/Frontend/CompilerInstance.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/Triple.h" +#include "llvm/ExecutionEngine/ExecutionEngine.h" +#include "llvm/IR/Module.h" + +// Project includes +#include "lldb/Expression/ASTStructExtractor.h" +#include "lldb/Expression/ClangExpressionParser.h" +#include "lldb/Expression/ClangFunction.h" +#include "lldb/Expression/IRExecutionUnit.h" +#include "lldb/Symbol/Type.h" +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/State.h" +#include "lldb/Core/ValueObject.h" +#include "lldb/Core/ValueObjectList.h" +#include "lldb/Interpreter/CommandReturnObject.h" +#include "lldb/Symbol/ClangASTContext.h" +#include "lldb/Symbol/Function.h" +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" +#include "lldb/Target/ThreadPlan.h" +#include "lldb/Target/ThreadPlanCallFunction.h" +#include "lldb/Core/Log.h" + +using namespace lldb_private; + +//---------------------------------------------------------------------- +// ClangFunction constructor +//---------------------------------------------------------------------- +ClangFunction::ClangFunction +( + ExecutionContextScope &exe_scope, + const ClangASTType &return_type, + const Address& functionAddress, + const ValueList &arg_value_list +) : + m_function_ptr (NULL), + m_function_addr (functionAddress), + m_function_return_type(return_type), + m_wrapper_function_name ("__lldb_caller_function"), + m_wrapper_struct_name ("__lldb_caller_struct"), + m_wrapper_args_addrs (), + m_arg_values (arg_value_list), + m_compiled (false), + m_JITted (false) +{ + m_jit_process_wp = lldb::ProcessWP(exe_scope.CalculateProcess()); + // Can't make a ClangFunction without a process. + assert (m_jit_process_wp.lock()); +} + +ClangFunction::ClangFunction +( + ExecutionContextScope &exe_scope, + Function &function, + ClangASTContext *ast_context, + const ValueList &arg_value_list +) : + m_function_ptr (&function), + m_function_addr (), + m_function_return_type (), + m_clang_ast_context (ast_context), + m_wrapper_function_name ("__lldb_function_caller"), + m_wrapper_struct_name ("__lldb_caller_struct"), + m_wrapper_args_addrs (), + m_arg_values (arg_value_list), + m_compiled (false), + m_JITted (false) +{ + m_jit_process_wp = lldb::ProcessWP(exe_scope.CalculateProcess()); + // Can't make a ClangFunction without a process. + assert (m_jit_process_wp.lock()); + + m_function_addr = m_function_ptr->GetAddressRange().GetBaseAddress(); + m_function_return_type = m_function_ptr->GetClangType().GetFunctionReturnType(); +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +ClangFunction::~ClangFunction() +{ +} + +unsigned +ClangFunction::CompileFunction (Stream &errors) +{ + if (m_compiled) + return 0; + + // FIXME: How does clang tell us there's no return value? We need to handle that case. + unsigned num_errors = 0; + + std::string return_type_str (m_function_return_type.GetTypeName()); + + // Cons up the function we're going to wrap our call in, then compile it... + // We declare the function "extern "C"" because the compiler might be in C++ + // mode which would mangle the name and then we couldn't find it again... + m_wrapper_function_text.clear(); + m_wrapper_function_text.append ("extern \"C\" void "); + m_wrapper_function_text.append (m_wrapper_function_name); + m_wrapper_function_text.append (" (void *input)\n{\n struct "); + m_wrapper_function_text.append (m_wrapper_struct_name); + m_wrapper_function_text.append (" \n {\n"); + m_wrapper_function_text.append (" "); + m_wrapper_function_text.append (return_type_str); + m_wrapper_function_text.append (" (*fn_ptr) ("); + + // Get the number of arguments. If we have a function type and it is prototyped, + // trust that, otherwise use the values we were given. + + // FIXME: This will need to be extended to handle Variadic functions. We'll need + // to pull the defined arguments out of the function, then add the types from the + // arguments list for the variable arguments. + + uint32_t num_args = UINT32_MAX; + bool trust_function = false; + // GetArgumentCount returns -1 for an unprototyped function. + ClangASTType function_clang_type; + if (m_function_ptr) + { + function_clang_type = m_function_ptr->GetClangType(); + if (function_clang_type) + { + int num_func_args = function_clang_type.GetFunctionArgumentCount(); + if (num_func_args >= 0) + { + trust_function = true; + num_args = num_func_args; + } + } + } + + if (num_args == UINT32_MAX) + num_args = m_arg_values.GetSize(); + + std::string args_buffer; // This one stores the definition of all the args in "struct caller". + std::string args_list_buffer; // This one stores the argument list called from the structure. + for (size_t i = 0; i < num_args; i++) + { + std::string type_name; + + if (trust_function) + { + type_name = function_clang_type.GetFunctionArgumentTypeAtIndex(i).GetTypeName(); + } + else + { + ClangASTType clang_qual_type = m_arg_values.GetValueAtIndex(i)->GetClangType (); + if (clang_qual_type) + { + type_name = clang_qual_type.GetTypeName(); + } + else + { + errors.Printf("Could not determine type of input value %lu.", i); + return 1; + } + } + + m_wrapper_function_text.append (type_name); + if (i < num_args - 1) + m_wrapper_function_text.append (", "); + + char arg_buf[32]; + args_buffer.append (" "); + args_buffer.append (type_name); + snprintf(arg_buf, 31, "arg_%" PRIu64, (uint64_t)i); + args_buffer.push_back (' '); + args_buffer.append (arg_buf); + args_buffer.append (";\n"); + + args_list_buffer.append ("__lldb_fn_data->"); + args_list_buffer.append (arg_buf); + if (i < num_args - 1) + args_list_buffer.append (", "); + + } + m_wrapper_function_text.append (");\n"); // Close off the function calling prototype. + + m_wrapper_function_text.append (args_buffer); + + m_wrapper_function_text.append (" "); + m_wrapper_function_text.append (return_type_str); + m_wrapper_function_text.append (" return_value;"); + m_wrapper_function_text.append ("\n };\n struct "); + m_wrapper_function_text.append (m_wrapper_struct_name); + m_wrapper_function_text.append ("* __lldb_fn_data = (struct "); + m_wrapper_function_text.append (m_wrapper_struct_name); + m_wrapper_function_text.append (" *) input;\n"); + + m_wrapper_function_text.append (" __lldb_fn_data->return_value = __lldb_fn_data->fn_ptr ("); + m_wrapper_function_text.append (args_list_buffer); + m_wrapper_function_text.append (");\n}\n"); + + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + if (log) + log->Printf ("Expression: \n\n%s\n\n", m_wrapper_function_text.c_str()); + + // Okay, now compile this expression + + lldb::ProcessSP jit_process_sp(m_jit_process_wp.lock()); + if (jit_process_sp) + { + m_parser.reset(new ClangExpressionParser(jit_process_sp.get(), *this)); + + num_errors = m_parser->Parse (errors); + } + else + { + errors.Printf("no process - unable to inject function"); + num_errors = 1; + } + + m_compiled = (num_errors == 0); + + if (!m_compiled) + return num_errors; + + return num_errors; +} + +bool +ClangFunction::WriteFunctionWrapper (ExecutionContext &exe_ctx, Stream &errors) +{ + Process *process = exe_ctx.GetProcessPtr(); + + if (!process) + return false; + + lldb::ProcessSP jit_process_sp(m_jit_process_wp.lock()); + + if (process != jit_process_sp.get()) + return false; + + if (!m_compiled) + return false; + + if (m_JITted) + return true; + + bool can_interpret = false; // should stay that way + + Error jit_error (m_parser->PrepareForExecution (m_jit_start_addr, + m_jit_end_addr, + m_execution_unit_ap, + exe_ctx, + can_interpret, + eExecutionPolicyAlways)); + + if (!jit_error.Success()) + return false; + + if (process && m_jit_start_addr) + m_jit_process_wp = lldb::ProcessWP(process->shared_from_this()); + + m_JITted = true; + + return true; +} + +bool +ClangFunction::WriteFunctionArguments (ExecutionContext &exe_ctx, lldb::addr_t &args_addr_ref, Stream &errors) +{ + return WriteFunctionArguments(exe_ctx, args_addr_ref, m_function_addr, m_arg_values, errors); +} + +// FIXME: Assure that the ValueList we were passed in is consistent with the one that defined this function. + +bool +ClangFunction::WriteFunctionArguments (ExecutionContext &exe_ctx, + lldb::addr_t &args_addr_ref, + Address function_address, + ValueList &arg_values, + Stream &errors) +{ + // All the information to reconstruct the struct is provided by the + // StructExtractor. + if (!m_struct_valid) + { + errors.Printf("Argument information was not correctly parsed, so the function cannot be called."); + return false; + } + + Error error; + using namespace clang; + ExecutionResults return_value = eExecutionSetupError; + + Process *process = exe_ctx.GetProcessPtr(); + + if (process == NULL) + return return_value; + + lldb::ProcessSP jit_process_sp(m_jit_process_wp.lock()); + + if (process != jit_process_sp.get()) + return false; + + if (args_addr_ref == LLDB_INVALID_ADDRESS) + { + args_addr_ref = process->AllocateMemory(m_struct_size, lldb::ePermissionsReadable|lldb::ePermissionsWritable, error); + if (args_addr_ref == LLDB_INVALID_ADDRESS) + return false; + m_wrapper_args_addrs.push_back (args_addr_ref); + } + else + { + // Make sure this is an address that we've already handed out. + if (find (m_wrapper_args_addrs.begin(), m_wrapper_args_addrs.end(), args_addr_ref) == m_wrapper_args_addrs.end()) + { + return false; + } + } + + // TODO: verify fun_addr needs to be a callable address + Scalar fun_addr (function_address.GetCallableLoadAddress(exe_ctx.GetTargetPtr())); + uint64_t first_offset = m_member_offsets[0]; + process->WriteScalarToMemory(args_addr_ref + first_offset, fun_addr, process->GetAddressByteSize(), error); + + // FIXME: We will need to extend this for Variadic functions. + + Error value_error; + + size_t num_args = arg_values.GetSize(); + if (num_args != m_arg_values.GetSize()) + { + errors.Printf ("Wrong number of arguments - was: %lu should be: %lu", num_args, m_arg_values.GetSize()); + return false; + } + + for (size_t i = 0; i < num_args; i++) + { + // FIXME: We should sanity check sizes. + + uint64_t offset = m_member_offsets[i+1]; // Clang sizes are in bytes. + Value *arg_value = arg_values.GetValueAtIndex(i); + + // FIXME: For now just do scalars: + + // Special case: if it's a pointer, don't do anything (the ABI supports passing cstrings) + + if (arg_value->GetValueType() == Value::eValueTypeHostAddress && + arg_value->GetContextType() == Value::eContextTypeInvalid && + arg_value->GetClangType().IsPointerType()) + continue; + + const Scalar &arg_scalar = arg_value->ResolveValue(&exe_ctx); + + if (!process->WriteScalarToMemory(args_addr_ref + offset, arg_scalar, arg_scalar.GetByteSize(), error)) + return false; + } + + return true; +} + +bool +ClangFunction::InsertFunction (ExecutionContext &exe_ctx, lldb::addr_t &args_addr_ref, Stream &errors) +{ + using namespace clang; + + if (CompileFunction(errors) != 0) + return false; + if (!WriteFunctionWrapper(exe_ctx, errors)) + return false; + if (!WriteFunctionArguments(exe_ctx, args_addr_ref, errors)) + return false; + + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP)); + if (log) + log->Printf ("Call Address: 0x%" PRIx64 " Struct Address: 0x%" PRIx64 ".\n", m_jit_start_addr, args_addr_ref); + + return true; +} + +ThreadPlan * +ClangFunction::GetThreadPlanToCallFunction (ExecutionContext &exe_ctx, + lldb::addr_t func_addr, + lldb::addr_t &args_addr, + Stream &errors, + bool stop_others, + bool unwind_on_error, + bool ignore_breakpoints, + lldb::addr_t *this_arg, + lldb::addr_t *cmd_arg) +{ + Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_EXPRESSIONS | LIBLLDB_LOG_STEP)); + + if (log) + log->Printf("-- [ClangFunction::GetThreadPlanToCallFunction] Creating thread plan to call function --"); + + // FIXME: Use the errors Stream for better error reporting. + Thread *thread = exe_ctx.GetThreadPtr(); + if (thread == NULL) + { + errors.Printf("Can't call a function without a valid thread."); + return NULL; + } + + // Okay, now run the function: + + Address wrapper_address (func_addr); + ThreadPlan *new_plan = new ThreadPlanCallFunction (*thread, + wrapper_address, + ClangASTType(), + args_addr, + stop_others, + unwind_on_error, + ignore_breakpoints, + this_arg, + cmd_arg); + new_plan->SetIsMasterPlan(true); + new_plan->SetOkayToDiscard (false); + return new_plan; +} + +bool +ClangFunction::FetchFunctionResults (ExecutionContext &exe_ctx, lldb::addr_t args_addr, Value &ret_value) +{ + // Read the return value - it is the last field in the struct: + // FIXME: How does clang tell us there's no return value? We need to handle that case. + // FIXME: Create our ThreadPlanCallFunction with the return ClangASTType, and then use GetReturnValueObject + // to fetch the value. That way we can fetch any values we need. + + Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_EXPRESSIONS | LIBLLDB_LOG_STEP)); + + if (log) + log->Printf("-- [ClangFunction::FetchFunctionResults] Fetching function results --"); + + Process *process = exe_ctx.GetProcessPtr(); + + if (process == NULL) + return false; + + lldb::ProcessSP jit_process_sp(m_jit_process_wp.lock()); + + if (process != jit_process_sp.get()) + return false; + + Error error; + ret_value.GetScalar() = process->ReadUnsignedIntegerFromMemory (args_addr + m_return_offset, m_return_size, 0, error); + + if (error.Fail()) + return false; + + ret_value.SetClangType(m_function_return_type); + ret_value.SetValueType(Value::eValueTypeScalar); + return true; +} + +void +ClangFunction::DeallocateFunctionResults (ExecutionContext &exe_ctx, lldb::addr_t args_addr) +{ + std::list::iterator pos; + pos = std::find(m_wrapper_args_addrs.begin(), m_wrapper_args_addrs.end(), args_addr); + if (pos != m_wrapper_args_addrs.end()) + m_wrapper_args_addrs.erase(pos); + + exe_ctx.GetProcessRef().DeallocateMemory(args_addr); +} + +ExecutionResults +ClangFunction::ExecuteFunction(ExecutionContext &exe_ctx, Stream &errors, Value &results) +{ + return ExecuteFunction (exe_ctx, errors, 1000, true, results); +} + +ExecutionResults +ClangFunction::ExecuteFunction(ExecutionContext &exe_ctx, Stream &errors, bool stop_others, Value &results) +{ + const bool try_all_threads = false; + const bool unwind_on_error = true; + const bool ignore_breakpoints = true; + return ExecuteFunction (exe_ctx, NULL, errors, stop_others, 0UL, try_all_threads, + unwind_on_error, ignore_breakpoints, results); +} + +ExecutionResults +ClangFunction::ExecuteFunction( + ExecutionContext &exe_ctx, + Stream &errors, + uint32_t timeout_usec, + bool try_all_threads, + Value &results) +{ + const bool stop_others = true; + const bool unwind_on_error = true; + const bool ignore_breakpoints = true; + return ExecuteFunction (exe_ctx, NULL, errors, stop_others, timeout_usec, + try_all_threads, unwind_on_error, ignore_breakpoints, results); +} + +// This is the static function +ExecutionResults +ClangFunction::ExecuteFunction ( + ExecutionContext &exe_ctx, + lldb::addr_t function_address, + lldb::addr_t &void_arg, + bool stop_others, + bool try_all_threads, + bool unwind_on_error, + bool ignore_breakpoints, + uint32_t timeout_usec, + Stream &errors, + lldb::addr_t *this_arg) +{ + Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_EXPRESSIONS | LIBLLDB_LOG_STEP)); + + if (log) + log->Printf("== [ClangFunction::ExecuteFunction] Executing function =="); + + lldb::ThreadPlanSP call_plan_sp (ClangFunction::GetThreadPlanToCallFunction (exe_ctx, + function_address, + void_arg, + errors, + stop_others, + unwind_on_error, + ignore_breakpoints, + this_arg)); + if (!call_plan_sp) + return eExecutionSetupError; + + // we need to make sure we record the fact that we are running an expression here + // otherwise this fact will fail to be recorded when fetching an Objective-C object description + if (exe_ctx.GetProcessPtr()) + exe_ctx.GetProcessPtr()->SetRunningUserExpression(true); + + ExecutionResults results = exe_ctx.GetProcessRef().RunThreadPlan (exe_ctx, call_plan_sp, + stop_others, + try_all_threads, + unwind_on_error, + ignore_breakpoints, + timeout_usec, + errors); + + if (log) + { + if (results != eExecutionCompleted) + { + log->Printf("== [ClangFunction::ExecuteFunction] Execution completed abnormally =="); + } + else + { + log->Printf("== [ClangFunction::ExecuteFunction] Execution completed normally =="); + } + } + + if (exe_ctx.GetProcessPtr()) + exe_ctx.GetProcessPtr()->SetRunningUserExpression(false); + + return results; +} + +ExecutionResults +ClangFunction::ExecuteFunction( + ExecutionContext &exe_ctx, + lldb::addr_t *args_addr_ptr, + Stream &errors, + bool stop_others, + uint32_t timeout_usec, + bool try_all_threads, + bool unwind_on_error, + bool ignore_breakpoints, + Value &results) +{ + using namespace clang; + ExecutionResults return_value = eExecutionSetupError; + + lldb::addr_t args_addr; + + if (args_addr_ptr != NULL) + args_addr = *args_addr_ptr; + else + args_addr = LLDB_INVALID_ADDRESS; + + if (CompileFunction(errors) != 0) + return eExecutionSetupError; + + if (args_addr == LLDB_INVALID_ADDRESS) + { + if (!InsertFunction(exe_ctx, args_addr, errors)) + return eExecutionSetupError; + } + + return_value = ClangFunction::ExecuteFunction (exe_ctx, + m_jit_start_addr, + args_addr, + stop_others, + try_all_threads, + unwind_on_error, + ignore_breakpoints, + timeout_usec, + errors); + + if (args_addr_ptr != NULL) + *args_addr_ptr = args_addr; + + if (return_value != eExecutionCompleted) + return return_value; + + FetchFunctionResults(exe_ctx, args_addr, results); + + if (args_addr_ptr == NULL) + DeallocateFunctionResults(exe_ctx, args_addr); + + return eExecutionCompleted; +} + +clang::ASTConsumer * +ClangFunction::ASTTransformer (clang::ASTConsumer *passthrough) +{ + return new ASTStructExtractor(passthrough, m_wrapper_struct_name.c_str(), *this); +} diff --git a/contrib/llvm/tools/lldb/source/Expression/ClangPersistentVariables.cpp b/contrib/llvm/tools/lldb/source/Expression/ClangPersistentVariables.cpp new file mode 100644 index 00000000000..db062d2e20b --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Expression/ClangPersistentVariables.cpp @@ -0,0 +1,89 @@ +//===-- ClangPersistentVariables.cpp ----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Expression/ClangPersistentVariables.h" +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Core/Value.h" + +#include "llvm/ADT/StringMap.h" + +using namespace lldb; +using namespace lldb_private; + +ClangPersistentVariables::ClangPersistentVariables () : + ClangExpressionVariableList(), + m_next_persistent_variable_id (0) +{ +} + +ClangExpressionVariableSP +ClangPersistentVariables::CreatePersistentVariable (const lldb::ValueObjectSP &valobj_sp) +{ + ClangExpressionVariableSP var_sp (CreateVariable(valobj_sp)); + return var_sp; +} + +ClangExpressionVariableSP +ClangPersistentVariables::CreatePersistentVariable (ExecutionContextScope *exe_scope, + const ConstString &name, + const TypeFromUser& user_type, + lldb::ByteOrder byte_order, + uint32_t addr_byte_size) +{ + ClangExpressionVariableSP var_sp (GetVariable(name)); + + if (!var_sp) + var_sp = CreateVariable(exe_scope, name, user_type, byte_order, addr_byte_size); + + return var_sp; +} + +void +ClangPersistentVariables::RemovePersistentVariable (lldb::ClangExpressionVariableSP variable) +{ + RemoveVariable(variable); + + const char *name = variable->GetName().AsCString(); + + if (*name != '$') + return; + name++; + + if (strtoul(name, NULL, 0) == m_next_persistent_variable_id - 1) + m_next_persistent_variable_id--; +} + +ConstString +ClangPersistentVariables::GetNextPersistentVariableName () +{ + char name_cstr[256]; + ::snprintf (name_cstr, sizeof(name_cstr), "$%u", m_next_persistent_variable_id++); + ConstString name(name_cstr); + return name; +} + +void +ClangPersistentVariables::RegisterPersistentType (const ConstString &name, + clang::TypeDecl *type_decl) +{ + m_persistent_types.insert(std::pair(name.GetCString(), type_decl)); +} + +clang::TypeDecl * +ClangPersistentVariables::GetPersistentType (const ConstString &name) +{ + PersistentTypeMap::const_iterator i = m_persistent_types.find(name.GetCString()); + + if (i == m_persistent_types.end()) + return NULL; + else + return i->second; +} diff --git a/contrib/llvm/tools/lldb/source/Expression/ClangUserExpression.cpp b/contrib/llvm/tools/lldb/source/Expression/ClangUserExpression.cpp new file mode 100644 index 00000000000..7f09f6fa094 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Expression/ClangUserExpression.cpp @@ -0,0 +1,1091 @@ +//===-- ClangUserExpression.cpp ---------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// C Includes +#include +#if HAVE_SYS_TYPES_H +# include +#endif + +// C++ Includes +#include +#include +#include + +#include "lldb/Core/ConstString.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/StreamFile.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Core/ValueObjectConstResult.h" +#include "lldb/Expression/ASTResultSynthesizer.h" +#include "lldb/Expression/ClangExpressionDeclMap.h" +#include "lldb/Expression/ClangExpressionParser.h" +#include "lldb/Expression/ClangFunction.h" +#include "lldb/Expression/ClangUserExpression.h" +#include "lldb/Expression/ExpressionSourceCode.h" +#include "lldb/Expression/IRExecutionUnit.h" +#include "lldb/Expression/IRInterpreter.h" +#include "lldb/Expression/Materializer.h" +#include "lldb/Host/Host.h" +#include "lldb/Symbol/Block.h" +#include "lldb/Symbol/ClangASTContext.h" +#include "lldb/Symbol/Function.h" +#include "lldb/Symbol/Type.h" +#include "lldb/Symbol/ClangExternalASTSourceCommon.h" +#include "lldb/Symbol/VariableList.h" +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/StackFrame.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/ThreadPlan.h" +#include "lldb/Target/ThreadPlanCallUserExpression.h" + +#include "clang/AST/DeclCXX.h" +#include "clang/AST/DeclObjC.h" + +using namespace lldb_private; + +ClangUserExpression::ClangUserExpression (const char *expr, + const char *expr_prefix, + lldb::LanguageType language, + ResultType desired_type) : + ClangExpression (), + m_stack_frame_bottom (LLDB_INVALID_ADDRESS), + m_stack_frame_top (LLDB_INVALID_ADDRESS), + m_expr_text (expr), + m_expr_prefix (expr_prefix ? expr_prefix : ""), + m_language (language), + m_transformed_text (), + m_desired_type (desired_type), + m_enforce_valid_object (true), + m_cplusplus (false), + m_objectivec (false), + m_static_method(false), + m_needs_object_ptr (false), + m_const_object (false), + m_target (NULL), + m_can_interpret (false), + m_materialized_address (LLDB_INVALID_ADDRESS) +{ + switch (m_language) + { + case lldb::eLanguageTypeC_plus_plus: + m_allow_cxx = true; + break; + case lldb::eLanguageTypeObjC: + m_allow_objc = true; + break; + case lldb::eLanguageTypeObjC_plus_plus: + default: + m_allow_cxx = true; + m_allow_objc = true; + break; + } +} + +ClangUserExpression::~ClangUserExpression () +{ +} + +clang::ASTConsumer * +ClangUserExpression::ASTTransformer (clang::ASTConsumer *passthrough) +{ + ClangASTContext *clang_ast_context = m_target->GetScratchClangASTContext(); + + if (!clang_ast_context) + return NULL; + + if (!m_result_synthesizer.get()) + m_result_synthesizer.reset(new ASTResultSynthesizer(passthrough, + *m_target)); + + return m_result_synthesizer.get(); +} + +void +ClangUserExpression::ScanContext(ExecutionContext &exe_ctx, Error &err) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + if (log) + log->Printf("ClangUserExpression::ScanContext()"); + + m_target = exe_ctx.GetTargetPtr(); + + if (!(m_allow_cxx || m_allow_objc)) + { + if (log) + log->Printf(" [CUE::SC] Settings inhibit C++ and Objective-C"); + return; + } + + StackFrame *frame = exe_ctx.GetFramePtr(); + if (frame == NULL) + { + if (log) + log->Printf(" [CUE::SC] Null stack frame"); + return; + } + + SymbolContext sym_ctx = frame->GetSymbolContext(lldb::eSymbolContextFunction | lldb::eSymbolContextBlock); + + if (!sym_ctx.function) + { + if (log) + log->Printf(" [CUE::SC] Null function"); + return; + } + + // Find the block that defines the function represented by "sym_ctx" + Block *function_block = sym_ctx.GetFunctionBlock(); + + if (!function_block) + { + if (log) + log->Printf(" [CUE::SC] Null function block"); + return; + } + + clang::DeclContext *decl_context = function_block->GetClangDeclContext(); + + if (!decl_context) + { + if (log) + log->Printf(" [CUE::SC] Null decl context"); + return; + } + + if (clang::CXXMethodDecl *method_decl = llvm::dyn_cast(decl_context)) + { + if (m_allow_cxx && method_decl->isInstance()) + { + if (m_enforce_valid_object) + { + lldb::VariableListSP variable_list_sp (function_block->GetBlockVariableList (true)); + + const char *thisErrorString = "Stopped in a C++ method, but 'this' isn't available; pretending we are in a generic context"; + + if (!variable_list_sp) + { + err.SetErrorString(thisErrorString); + return; + } + + lldb::VariableSP this_var_sp (variable_list_sp->FindVariable(ConstString("this"))); + + if (!this_var_sp || + !this_var_sp->IsInScope(frame) || + !this_var_sp->LocationIsValidForFrame (frame)) + { + err.SetErrorString(thisErrorString); + return; + } + } + + m_cplusplus = true; + m_needs_object_ptr = true; + } + } + else if (clang::ObjCMethodDecl *method_decl = llvm::dyn_cast(decl_context)) + { + if (m_allow_objc) + { + if (m_enforce_valid_object) + { + lldb::VariableListSP variable_list_sp (function_block->GetBlockVariableList (true)); + + const char *selfErrorString = "Stopped in an Objective-C method, but 'self' isn't available; pretending we are in a generic context"; + + if (!variable_list_sp) + { + err.SetErrorString(selfErrorString); + return; + } + + lldb::VariableSP self_variable_sp = variable_list_sp->FindVariable(ConstString("self")); + + if (!self_variable_sp || + !self_variable_sp->IsInScope(frame) || + !self_variable_sp->LocationIsValidForFrame (frame)) + { + err.SetErrorString(selfErrorString); + return; + } + } + + m_objectivec = true; + m_needs_object_ptr = true; + + if (!method_decl->isInstanceMethod()) + m_static_method = true; + } + } + else if (clang::FunctionDecl *function_decl = llvm::dyn_cast(decl_context)) + { + // We might also have a function that said in the debug information that it captured an + // object pointer. The best way to deal with getting to the ivars at present it by pretending + // that this is a method of a class in whatever runtime the debug info says the object pointer + // belongs to. Do that here. + + ClangASTMetadata *metadata = ClangASTContext::GetMetadata (&decl_context->getParentASTContext(), function_decl); + if (metadata && metadata->HasObjectPtr()) + { + lldb::LanguageType language = metadata->GetObjectPtrLanguage(); + if (language == lldb::eLanguageTypeC_plus_plus) + { + if (m_enforce_valid_object) + { + lldb::VariableListSP variable_list_sp (function_block->GetBlockVariableList (true)); + + const char *thisErrorString = "Stopped in a context claiming to capture a C++ object pointer, but 'this' isn't available; pretending we are in a generic context"; + + if (!variable_list_sp) + { + err.SetErrorString(thisErrorString); + return; + } + + lldb::VariableSP this_var_sp (variable_list_sp->FindVariable(ConstString("this"))); + + if (!this_var_sp || + !this_var_sp->IsInScope(frame) || + !this_var_sp->LocationIsValidForFrame (frame)) + { + err.SetErrorString(thisErrorString); + return; + } + } + + m_cplusplus = true; + m_needs_object_ptr = true; + } + else if (language == lldb::eLanguageTypeObjC) + { + if (m_enforce_valid_object) + { + lldb::VariableListSP variable_list_sp (function_block->GetBlockVariableList (true)); + + const char *selfErrorString = "Stopped in a context claiming to capture an Objective-C object pointer, but 'self' isn't available; pretending we are in a generic context"; + + if (!variable_list_sp) + { + err.SetErrorString(selfErrorString); + return; + } + + lldb::VariableSP self_variable_sp = variable_list_sp->FindVariable(ConstString("self")); + + if (!self_variable_sp || + !self_variable_sp->IsInScope(frame) || + !self_variable_sp->LocationIsValidForFrame (frame)) + { + err.SetErrorString(selfErrorString); + return; + } + + Type *self_type = self_variable_sp->GetType(); + + if (!self_type) + { + err.SetErrorString(selfErrorString); + return; + } + + ClangASTType self_clang_type = self_type->GetClangForwardType(); + + if (!self_clang_type) + { + err.SetErrorString(selfErrorString); + return; + } + + if (self_clang_type.IsObjCClassType()) + { + return; + } + else if (self_clang_type.IsObjCObjectPointerType()) + { + m_objectivec = true; + m_needs_object_ptr = true; + } + else + { + err.SetErrorString(selfErrorString); + return; + } + } + else + { + m_objectivec = true; + m_needs_object_ptr = true; + } + } + } + } +} + +void +ClangUserExpression::InstallContext (ExecutionContext &exe_ctx) +{ + m_process_wp = exe_ctx.GetProcessSP(); + + lldb::StackFrameSP frame_sp = exe_ctx.GetFrameSP(); + + if (frame_sp) + m_address = frame_sp->GetFrameCodeAddress(); +} + +bool +ClangUserExpression::LockAndCheckContext (ExecutionContext &exe_ctx, + lldb::TargetSP &target_sp, + lldb::ProcessSP &process_sp, + lldb::StackFrameSP &frame_sp) +{ + lldb::ProcessSP expected_process_sp = m_process_wp.lock(); + process_sp = exe_ctx.GetProcessSP(); + + if (process_sp != expected_process_sp) + return false; + + process_sp = exe_ctx.GetProcessSP(); + target_sp = exe_ctx.GetTargetSP(); + frame_sp = exe_ctx.GetFrameSP(); + + if (m_address.IsValid()) + { + if (!frame_sp) + return false; + else + return (0 == Address::CompareLoadAddress(m_address, frame_sp->GetFrameCodeAddress(), target_sp.get())); + } + + return true; +} + +bool +ClangUserExpression::MatchesContext (ExecutionContext &exe_ctx) +{ + lldb::TargetSP target_sp; + lldb::ProcessSP process_sp; + lldb::StackFrameSP frame_sp; + + return LockAndCheckContext(exe_ctx, target_sp, process_sp, frame_sp); +} + +// This is a really nasty hack, meant to fix Objective-C expressions of the form +// (int)[myArray count]. Right now, because the type information for count is +// not available, [myArray count] returns id, which can't be directly cast to +// int without causing a clang error. +static void +ApplyObjcCastHack(std::string &expr) +{ +#define OBJC_CAST_HACK_FROM "(int)[" +#define OBJC_CAST_HACK_TO "(int)(long long)[" + + size_t from_offset; + + while ((from_offset = expr.find(OBJC_CAST_HACK_FROM)) != expr.npos) + expr.replace(from_offset, sizeof(OBJC_CAST_HACK_FROM) - 1, OBJC_CAST_HACK_TO); + +#undef OBJC_CAST_HACK_TO +#undef OBJC_CAST_HACK_FROM +} + +// Another hack, meant to allow use of unichar despite it not being available in +// the type information. Although we could special-case it in type lookup, +// hopefully we'll figure out a way to #include the same environment as is +// present in the original source file rather than try to hack specific type +// definitions in as needed. +static void +ApplyUnicharHack(std::string &expr) +{ +#define UNICHAR_HACK_FROM "unichar" +#define UNICHAR_HACK_TO "unsigned short" + + size_t from_offset; + + while ((from_offset = expr.find(UNICHAR_HACK_FROM)) != expr.npos) + expr.replace(from_offset, sizeof(UNICHAR_HACK_FROM) - 1, UNICHAR_HACK_TO); + +#undef UNICHAR_HACK_TO +#undef UNICHAR_HACK_FROM +} + +bool +ClangUserExpression::Parse (Stream &error_stream, + ExecutionContext &exe_ctx, + lldb_private::ExecutionPolicy execution_policy, + bool keep_result_in_memory) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + Error err; + + InstallContext(exe_ctx); + + ScanContext(exe_ctx, err); + + if (!err.Success()) + { + error_stream.Printf("warning: %s\n", err.AsCString()); + } + + StreamString m_transformed_stream; + + //////////////////////////////////// + // Generate the expression + // + + ApplyObjcCastHack(m_expr_text); + //ApplyUnicharHack(m_expr_text); + + std::unique_ptr source_code (ExpressionSourceCode::CreateWrapped(m_expr_prefix.c_str(), m_expr_text.c_str())); + + lldb::LanguageType lang_type; + + if (m_cplusplus) + lang_type = lldb::eLanguageTypeC_plus_plus; + else if(m_objectivec) + lang_type = lldb::eLanguageTypeObjC; + else + lang_type = lldb::eLanguageTypeC; + + if (!source_code->GetText(m_transformed_text, lang_type, m_const_object, m_static_method)) + { + error_stream.PutCString ("error: couldn't construct expression body"); + return false; + } + + if (log) + log->Printf("Parsing the following code:\n%s", m_transformed_text.c_str()); + + //////////////////////////////////// + // Set up the target and compiler + // + + Target *target = exe_ctx.GetTargetPtr(); + + if (!target) + { + error_stream.PutCString ("error: invalid target\n"); + return false; + } + + ////////////////////////// + // Parse the expression + // + + m_materializer_ap.reset(new Materializer()); + + m_expr_decl_map.reset(new ClangExpressionDeclMap(keep_result_in_memory, exe_ctx)); + + class OnExit + { + public: + typedef std::function Callback; + + OnExit (Callback const &callback) : + m_callback(callback) + { + } + + ~OnExit () + { + m_callback(); + } + private: + Callback m_callback; + }; + + OnExit on_exit([this]() { m_expr_decl_map.reset(); }); + + if (!m_expr_decl_map->WillParse(exe_ctx, m_materializer_ap.get())) + { + error_stream.PutCString ("error: current process state is unsuitable for expression parsing\n"); + return false; + } + + Process *process = exe_ctx.GetProcessPtr(); + ExecutionContextScope *exe_scope = process; + + if (!exe_scope) + exe_scope = exe_ctx.GetTargetPtr(); + + ClangExpressionParser parser(exe_scope, *this); + + unsigned num_errors = parser.Parse (error_stream); + + if (num_errors) + { + error_stream.Printf ("error: %d errors parsing expression\n", num_errors); + + m_expr_decl_map->DidParse(); + + return false; + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Prepare the output of the parser for execution, evaluating it statically if possible + // + + Error jit_error = parser.PrepareForExecution (m_jit_start_addr, + m_jit_end_addr, + m_execution_unit_ap, + exe_ctx, + m_can_interpret, + execution_policy); + + if (jit_error.Success()) + { + if (process && m_jit_start_addr != LLDB_INVALID_ADDRESS) + m_jit_process_wp = lldb::ProcessWP(process->shared_from_this()); + return true; + } + else + { + const char *error_cstr = jit_error.AsCString(); + if (error_cstr && error_cstr[0]) + error_stream.Printf ("error: %s\n", error_cstr); + else + error_stream.Printf ("error: expression can't be interpreted or run\n"); + return false; + } +} + +static lldb::addr_t +GetObjectPointer (lldb::StackFrameSP frame_sp, + ConstString &object_name, + Error &err) +{ + err.Clear(); + + if (!frame_sp) + { + err.SetErrorStringWithFormat("Couldn't load '%s' because the context is incomplete", object_name.AsCString()); + return LLDB_INVALID_ADDRESS; + } + + lldb::VariableSP var_sp; + lldb::ValueObjectSP valobj_sp; + + valobj_sp = frame_sp->GetValueForVariableExpressionPath(object_name.AsCString(), + lldb::eNoDynamicValues, + StackFrame::eExpressionPathOptionCheckPtrVsMember || + StackFrame::eExpressionPathOptionsAllowDirectIVarAccess || + StackFrame::eExpressionPathOptionsNoFragileObjcIvar || + StackFrame::eExpressionPathOptionsNoSyntheticChildren || + StackFrame::eExpressionPathOptionsNoSyntheticArrayRange, + var_sp, + err); + + if (!err.Success()) + return LLDB_INVALID_ADDRESS; + + lldb::addr_t ret = valobj_sp->GetValueAsUnsigned(LLDB_INVALID_ADDRESS); + + if (ret == LLDB_INVALID_ADDRESS) + { + err.SetErrorStringWithFormat("Couldn't load '%s' because its value couldn't be evaluated", object_name.AsCString()); + return LLDB_INVALID_ADDRESS; + } + + return ret; +} + +bool +ClangUserExpression::PrepareToExecuteJITExpression (Stream &error_stream, + ExecutionContext &exe_ctx, + lldb::addr_t &struct_address, + lldb::addr_t &object_ptr, + lldb::addr_t &cmd_ptr) +{ + lldb::TargetSP target; + lldb::ProcessSP process; + lldb::StackFrameSP frame; + + if (!LockAndCheckContext(exe_ctx, + target, + process, + frame)) + { + error_stream.Printf("The context has changed before we could JIT the expression!\n"); + return false; + } + + if (m_jit_start_addr != LLDB_INVALID_ADDRESS || m_can_interpret) + { + if (m_needs_object_ptr) + { + ConstString object_name; + + if (m_cplusplus) + { + object_name.SetCString("this"); + } + else if (m_objectivec) + { + object_name.SetCString("self"); + } + else + { + error_stream.Printf("Need object pointer but don't know the language\n"); + return false; + } + + Error object_ptr_error; + + object_ptr = GetObjectPointer(frame, object_name, object_ptr_error); + + if (!object_ptr_error.Success()) + { + error_stream.Printf("warning: couldn't get required object pointer (substituting NULL): %s\n", object_ptr_error.AsCString()); + object_ptr = 0; + } + + if (m_objectivec) + { + ConstString cmd_name("_cmd"); + + cmd_ptr = GetObjectPointer(frame, cmd_name, object_ptr_error); + + if (!object_ptr_error.Success()) + { + error_stream.Printf("warning: couldn't get cmd pointer (substituting NULL): %s\n", object_ptr_error.AsCString()); + cmd_ptr = 0; + } + } + } + + if (m_materialized_address == LLDB_INVALID_ADDRESS) + { + Error alloc_error; + + IRMemoryMap::AllocationPolicy policy = m_can_interpret ? IRMemoryMap::eAllocationPolicyHostOnly : IRMemoryMap::eAllocationPolicyMirror; + + m_materialized_address = m_execution_unit_ap->Malloc(m_materializer_ap->GetStructByteSize(), + m_materializer_ap->GetStructAlignment(), + lldb::ePermissionsReadable | lldb::ePermissionsWritable, + policy, + alloc_error); + + if (!alloc_error.Success()) + { + error_stream.Printf("Couldn't allocate space for materialized struct: %s\n", alloc_error.AsCString()); + return false; + } + } + + struct_address = m_materialized_address; + + if (m_can_interpret && m_stack_frame_bottom == LLDB_INVALID_ADDRESS) + { + Error alloc_error; + + const size_t stack_frame_size = 512 * 1024; + + m_stack_frame_bottom = m_execution_unit_ap->Malloc(stack_frame_size, + 8, + lldb::ePermissionsReadable | lldb::ePermissionsWritable, + IRMemoryMap::eAllocationPolicyHostOnly, + alloc_error); + + m_stack_frame_top = m_stack_frame_bottom + stack_frame_size; + + if (!alloc_error.Success()) + { + error_stream.Printf("Couldn't allocate space for the stack frame: %s\n", alloc_error.AsCString()); + return false; + } + } + + Error materialize_error; + + m_dematerializer_sp = m_materializer_ap->Materialize(frame, *m_execution_unit_ap, struct_address, materialize_error); + + if (!materialize_error.Success()) + { + error_stream.Printf("Couldn't materialize struct: %s\n", materialize_error.AsCString()); + return false; + } + } + return true; +} + +ThreadPlan * +ClangUserExpression::GetThreadPlanToExecuteJITExpression (Stream &error_stream, + ExecutionContext &exe_ctx) +{ + lldb::addr_t struct_address; + + lldb::addr_t object_ptr = 0; + lldb::addr_t cmd_ptr = 0; + + PrepareToExecuteJITExpression (error_stream, exe_ctx, struct_address, object_ptr, cmd_ptr); + + // FIXME: This should really return a ThreadPlanCallUserExpression, in order to make sure that we don't release the + // ClangUserExpression resources before the thread plan finishes execution in the target. But because we are + // forcing unwind_on_error to be true here, in practical terms that can't happen. + + const bool stop_others = true; + const bool unwind_on_error = true; + const bool ignore_breakpoints = false; + return ClangFunction::GetThreadPlanToCallFunction (exe_ctx, + m_jit_start_addr, + struct_address, + error_stream, + stop_others, + unwind_on_error, + ignore_breakpoints, + (m_needs_object_ptr ? &object_ptr : NULL), + (m_needs_object_ptr && m_objectivec) ? &cmd_ptr : NULL); +} + +bool +ClangUserExpression::FinalizeJITExecution (Stream &error_stream, + ExecutionContext &exe_ctx, + lldb::ClangExpressionVariableSP &result, + lldb::addr_t function_stack_bottom, + lldb::addr_t function_stack_top) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + if (log) + log->Printf("-- [ClangUserExpression::FinalizeJITExecution] Dematerializing after execution --"); + + if (!m_dematerializer_sp) + { + error_stream.Printf ("Couldn't apply expression side effects : no dematerializer is present"); + return false; + } + + Error dematerialize_error; + + m_dematerializer_sp->Dematerialize(dematerialize_error, result, function_stack_bottom, function_stack_top); + + if (!dematerialize_error.Success()) + { + error_stream.Printf ("Couldn't apply expression side effects : %s\n", dematerialize_error.AsCString("unknown error")); + return false; + } + + if (result) + result->TransferAddress(); + + m_dematerializer_sp.reset(); + + return true; +} + +ExecutionResults +ClangUserExpression::Execute (Stream &error_stream, + ExecutionContext &exe_ctx, + bool unwind_on_error, + bool ignore_breakpoints, + ClangUserExpression::ClangUserExpressionSP &shared_ptr_to_me, + lldb::ClangExpressionVariableSP &result, + bool run_others, + uint32_t timeout_usec) +{ + // The expression log is quite verbose, and if you're just tracking the execution of the + // expression, it's quite convenient to have these logs come out with the STEP log as well. + Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_EXPRESSIONS | LIBLLDB_LOG_STEP)); + + if (m_jit_start_addr != LLDB_INVALID_ADDRESS || m_can_interpret) + { + lldb::addr_t struct_address = LLDB_INVALID_ADDRESS; + + lldb::addr_t object_ptr = 0; + lldb::addr_t cmd_ptr = 0; + + if (!PrepareToExecuteJITExpression (error_stream, exe_ctx, struct_address, object_ptr, cmd_ptr)) + { + error_stream.Printf("Errored out in %s, couldn't PrepareToExecuteJITExpression", __FUNCTION__); + return eExecutionSetupError; + } + + lldb::addr_t function_stack_bottom = LLDB_INVALID_ADDRESS; + lldb::addr_t function_stack_top = LLDB_INVALID_ADDRESS; + + if (m_can_interpret) + { + llvm::Module *module = m_execution_unit_ap->GetModule(); + llvm::Function *function = m_execution_unit_ap->GetFunction(); + + if (!module || !function) + { + error_stream.Printf("Supposed to interpret, but nothing is there"); + return eExecutionSetupError; + } + + Error interpreter_error; + + llvm::SmallVector args; + + if (m_needs_object_ptr) + { + args.push_back(object_ptr); + + if (m_objectivec) + args.push_back(cmd_ptr); + } + + args.push_back(struct_address); + + function_stack_bottom = m_stack_frame_bottom; + function_stack_top = m_stack_frame_top; + + IRInterpreter::Interpret (*module, + *function, + args, + *m_execution_unit_ap.get(), + interpreter_error, + function_stack_bottom, + function_stack_top); + + if (!interpreter_error.Success()) + { + error_stream.Printf("Supposed to interpret, but failed: %s", interpreter_error.AsCString()); + return eExecutionDiscarded; + } + } + else + { + const bool stop_others = true; + const bool try_all_threads = run_others; + + Address wrapper_address (m_jit_start_addr); + lldb::ThreadPlanSP call_plan_sp(new ThreadPlanCallUserExpression (exe_ctx.GetThreadRef(), + wrapper_address, + struct_address, + stop_others, + unwind_on_error, + ignore_breakpoints, + (m_needs_object_ptr ? &object_ptr : NULL), + ((m_needs_object_ptr && m_objectivec) ? &cmd_ptr : NULL), + shared_ptr_to_me)); + + if (!call_plan_sp || !call_plan_sp->ValidatePlan (&error_stream)) + return eExecutionSetupError; + + lldb::addr_t function_stack_pointer = static_cast(call_plan_sp.get())->GetFunctionStackPointer(); + + function_stack_bottom = function_stack_pointer - Host::GetPageSize(); + function_stack_top = function_stack_pointer; + + if (log) + log->Printf("-- [ClangUserExpression::Execute] Execution of expression begins --"); + + if (exe_ctx.GetProcessPtr()) + exe_ctx.GetProcessPtr()->SetRunningUserExpression(true); + + ExecutionResults execution_result = exe_ctx.GetProcessRef().RunThreadPlan (exe_ctx, + call_plan_sp, + stop_others, + try_all_threads, + unwind_on_error, + ignore_breakpoints, + timeout_usec, + error_stream); + + if (exe_ctx.GetProcessPtr()) + exe_ctx.GetProcessPtr()->SetRunningUserExpression(false); + + if (log) + log->Printf("-- [ClangUserExpression::Execute] Execution of expression completed --"); + + if (execution_result == eExecutionInterrupted || execution_result == eExecutionHitBreakpoint) + { + const char *error_desc = NULL; + + if (call_plan_sp) + { + lldb::StopInfoSP real_stop_info_sp = call_plan_sp->GetRealStopInfo(); + if (real_stop_info_sp) + error_desc = real_stop_info_sp->GetDescription(); + } + if (error_desc) + error_stream.Printf ("Execution was interrupted, reason: %s.", error_desc); + else + error_stream.Printf ("Execution was interrupted."); + + if ((execution_result == eExecutionInterrupted && unwind_on_error) + || (execution_result == eExecutionHitBreakpoint && ignore_breakpoints)) + error_stream.Printf ("\nThe process has been returned to the state before expression evaluation."); + else + error_stream.Printf ("\nThe process has been left at the point where it was interrupted, use \"thread return -x\" to return to the state before expression evaluation."); + + return execution_result; + } + else if (execution_result != eExecutionCompleted) + { + error_stream.Printf ("Couldn't execute function; result was %s\n", Process::ExecutionResultAsCString (execution_result)); + return execution_result; + } + } + + if (FinalizeJITExecution (error_stream, exe_ctx, result, function_stack_bottom, function_stack_top)) + { + return eExecutionCompleted; + } + else + { + return eExecutionSetupError; + } + } + else + { + error_stream.Printf("Expression can't be run, because there is no JIT compiled function"); + return eExecutionSetupError; + } +} + +ExecutionResults +ClangUserExpression::Evaluate (ExecutionContext &exe_ctx, + lldb_private::ExecutionPolicy execution_policy, + lldb::LanguageType language, + ResultType desired_type, + bool unwind_on_error, + bool ignore_breakpoints, + const char *expr_cstr, + const char *expr_prefix, + lldb::ValueObjectSP &result_valobj_sp, + bool run_others, + uint32_t timeout_usec) +{ + Error error; + return EvaluateWithError (exe_ctx, + execution_policy, + language, + desired_type, + unwind_on_error, + ignore_breakpoints, + expr_cstr, + expr_prefix, + result_valobj_sp, + error, + run_others, + timeout_usec); +} + +ExecutionResults +ClangUserExpression::EvaluateWithError (ExecutionContext &exe_ctx, + lldb_private::ExecutionPolicy execution_policy, + lldb::LanguageType language, + ResultType desired_type, + bool unwind_on_error, + bool ignore_breakpoints, + const char *expr_cstr, + const char *expr_prefix, + lldb::ValueObjectSP &result_valobj_sp, + Error &error, + bool run_others, + uint32_t timeout_usec) +{ + Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_EXPRESSIONS | LIBLLDB_LOG_STEP)); + + ExecutionResults execution_results = eExecutionSetupError; + + Process *process = exe_ctx.GetProcessPtr(); + + if (process == NULL || process->GetState() != lldb::eStateStopped) + { + if (execution_policy == eExecutionPolicyAlways) + { + if (log) + log->Printf("== [ClangUserExpression::Evaluate] Expression may not run, but is not constant =="); + + error.SetErrorString ("expression needed to run but couldn't"); + + return execution_results; + } + } + + if (process == NULL || !process->CanJIT()) + execution_policy = eExecutionPolicyNever; + + ClangUserExpressionSP user_expression_sp (new ClangUserExpression (expr_cstr, expr_prefix, language, desired_type)); + + StreamString error_stream; + + if (log) + log->Printf("== [ClangUserExpression::Evaluate] Parsing expression %s ==", expr_cstr); + + const bool keep_expression_in_memory = true; + + if (!user_expression_sp->Parse (error_stream, exe_ctx, execution_policy, keep_expression_in_memory)) + { + if (error_stream.GetString().empty()) + error.SetErrorString ("expression failed to parse, unknown error"); + else + error.SetErrorString (error_stream.GetString().c_str()); + } + else + { + lldb::ClangExpressionVariableSP expr_result; + + if (execution_policy == eExecutionPolicyNever && + !user_expression_sp->CanInterpret()) + { + if (log) + log->Printf("== [ClangUserExpression::Evaluate] Expression may not run, but is not constant =="); + + if (error_stream.GetString().empty()) + error.SetErrorString ("expression needed to run but couldn't"); + } + else + { + error_stream.GetString().clear(); + + if (log) + log->Printf("== [ClangUserExpression::Evaluate] Executing expression =="); + + execution_results = user_expression_sp->Execute (error_stream, + exe_ctx, + unwind_on_error, + ignore_breakpoints, + user_expression_sp, + expr_result, + run_others, + timeout_usec); + + if (execution_results != eExecutionCompleted) + { + if (log) + log->Printf("== [ClangUserExpression::Evaluate] Execution completed abnormally =="); + + if (error_stream.GetString().empty()) + error.SetErrorString ("expression failed to execute, unknown error"); + else + error.SetErrorString (error_stream.GetString().c_str()); + } + else + { + if (expr_result) + { + result_valobj_sp = expr_result->GetValueObject(); + + if (log) + log->Printf("== [ClangUserExpression::Evaluate] Execution completed normally with result %s ==", result_valobj_sp->GetValueAsCString()); + } + else + { + if (log) + log->Printf("== [ClangUserExpression::Evaluate] Execution completed normally with no result =="); + + error.SetError(ClangUserExpression::kNoResult, lldb::eErrorTypeGeneric); + } + } + } + } + + if (result_valobj_sp.get() == NULL) + result_valobj_sp = ValueObjectConstResult::Create (NULL, error); + + return execution_results; +} diff --git a/contrib/llvm/tools/lldb/source/Expression/ClangUtilityFunction.cpp b/contrib/llvm/tools/lldb/source/Expression/ClangUtilityFunction.cpp new file mode 100644 index 00000000000..c911c279993 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Expression/ClangUtilityFunction.cpp @@ -0,0 +1,169 @@ +//===-- ClangUserExpression.cpp -------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// C Includes +#include +#if HAVE_SYS_TYPES_H +# include +#endif + +// C++ Includes + +#include "lldb/Core/ConstString.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Stream.h" +#include "lldb/Core/StreamFile.h" +#include "lldb/Expression/ClangExpressionDeclMap.h" +#include "lldb/Expression/ClangExpressionParser.h" +#include "lldb/Expression/ClangUtilityFunction.h" +#include "lldb/Expression/ExpressionSourceCode.h" +#include "lldb/Expression/IRExecutionUnit.h" +#include "lldb/Host/Host.h" +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/Target.h" + +using namespace lldb_private; + +//------------------------------------------------------------------ +/// Constructor +/// +/// @param[in] text +/// The text of the function. Must be a full translation unit. +/// +/// @param[in] name +/// The name of the function, as used in the text. +//------------------------------------------------------------------ +ClangUtilityFunction::ClangUtilityFunction (const char *text, + const char *name) : + ClangExpression (), + m_function_text (ExpressionSourceCode::g_expression_prefix), + m_function_name (name) +{ + if (text && text[0]) + m_function_text.append (text); +} + +ClangUtilityFunction::~ClangUtilityFunction () +{ +} + +//------------------------------------------------------------------ +/// Install the utility function into a process +/// +/// @param[in] error_stream +/// A stream to print parse errors and warnings to. +/// +/// @param[in] exe_ctx +/// The execution context to install the utility function to. +/// +/// @return +/// True on success (no errors); false otherwise. +//------------------------------------------------------------------ +bool +ClangUtilityFunction::Install (Stream &error_stream, + ExecutionContext &exe_ctx) +{ + if (m_jit_start_addr != LLDB_INVALID_ADDRESS) + { + error_stream.PutCString("error: already installed\n"); + return false; + } + + //////////////////////////////////// + // Set up the target and compiler + // + + Target *target = exe_ctx.GetTargetPtr(); + + if (!target) + { + error_stream.PutCString ("error: invalid target\n"); + return false; + } + + Process *process = exe_ctx.GetProcessPtr(); + + if (!process) + { + error_stream.PutCString ("error: invalid process\n"); + return false; + } + + ////////////////////////// + // Parse the expression + // + + bool keep_result_in_memory = false; + + m_expr_decl_map.reset(new ClangExpressionDeclMap(keep_result_in_memory, exe_ctx)); + + if (!m_expr_decl_map->WillParse(exe_ctx, NULL)) + { + error_stream.PutCString ("error: current process state is unsuitable for expression parsing\n"); + return false; + } + + ClangExpressionParser parser(exe_ctx.GetBestExecutionContextScope(), *this); + + unsigned num_errors = parser.Parse (error_stream); + + if (num_errors) + { + error_stream.Printf ("error: %d errors parsing expression\n", num_errors); + + m_expr_decl_map.reset(); + + return false; + } + + ////////////////////////////////// + // JIT the output of the parser + // + + bool can_interpret = false; // should stay that way + + Error jit_error = parser.PrepareForExecution (m_jit_start_addr, + m_jit_end_addr, + m_execution_unit_ap, + exe_ctx, + can_interpret, + eExecutionPolicyAlways); + + if (m_jit_start_addr != LLDB_INVALID_ADDRESS) + m_jit_process_wp = lldb::ProcessWP(process->shared_from_this()); + +#if 0 + // jingham: look here + StreamFile logfile ("/tmp/exprs.txt", "a"); + logfile.Printf ("0x%16.16" PRIx64 ": func = %s, source =\n%s\n", + m_jit_start_addr, + m_function_name.c_str(), + m_function_text.c_str()); +#endif + + m_expr_decl_map->DidParse(); + + m_expr_decl_map.reset(); + + if (jit_error.Success()) + { + return true; + } + else + { + const char *error_cstr = jit_error.AsCString(); + if (error_cstr && error_cstr[0]) + error_stream.Printf ("error: %s\n", error_cstr); + else + error_stream.Printf ("error: expression can't be interpreted or run\n"); + return false; + } +} + + diff --git a/contrib/llvm/tools/lldb/source/Expression/DWARFExpression.cpp b/contrib/llvm/tools/lldb/source/Expression/DWARFExpression.cpp new file mode 100644 index 00000000000..e2ae19e5ac7 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Expression/DWARFExpression.cpp @@ -0,0 +1,2691 @@ +//===-- DWARFExpression.cpp -------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Expression/DWARFExpression.h" + +#include + +#include "lldb/Core/DataEncoder.h" +#include "lldb/Core/dwarf.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/RegisterValue.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Core/Scalar.h" +#include "lldb/Core/Value.h" +#include "lldb/Core/VMRange.h" + +#include "lldb/Expression/ClangExpressionDeclMap.h" +#include "lldb/Expression/ClangExpressionVariable.h" + +#include "lldb/Host/Endian.h" +#include "lldb/Host/Host.h" + +#include "lldb/lldb-private-log.h" + +#include "lldb/Symbol/ClangASTType.h" +#include "lldb/Symbol/ClangASTContext.h" +#include "lldb/Symbol/Type.h" + +#include "lldb/Target/ABI.h" +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/StackFrame.h" +#include "lldb/Target/StackID.h" + +using namespace lldb; +using namespace lldb_private; + +const char * +DW_OP_value_to_name (uint32_t val) +{ + static char invalid[100]; + switch (val) { + case 0x03: return "DW_OP_addr"; + case 0x06: return "DW_OP_deref"; + case 0x08: return "DW_OP_const1u"; + case 0x09: return "DW_OP_const1s"; + case 0x0a: return "DW_OP_const2u"; + case 0x0b: return "DW_OP_const2s"; + case 0x0c: return "DW_OP_const4u"; + case 0x0d: return "DW_OP_const4s"; + case 0x0e: return "DW_OP_const8u"; + case 0x0f: return "DW_OP_const8s"; + case 0x10: return "DW_OP_constu"; + case 0x11: return "DW_OP_consts"; + case 0x12: return "DW_OP_dup"; + case 0x13: return "DW_OP_drop"; + case 0x14: return "DW_OP_over"; + case 0x15: return "DW_OP_pick"; + case 0x16: return "DW_OP_swap"; + case 0x17: return "DW_OP_rot"; + case 0x18: return "DW_OP_xderef"; + case 0x19: return "DW_OP_abs"; + case 0x1a: return "DW_OP_and"; + case 0x1b: return "DW_OP_div"; + case 0x1c: return "DW_OP_minus"; + case 0x1d: return "DW_OP_mod"; + case 0x1e: return "DW_OP_mul"; + case 0x1f: return "DW_OP_neg"; + case 0x20: return "DW_OP_not"; + case 0x21: return "DW_OP_or"; + case 0x22: return "DW_OP_plus"; + case 0x23: return "DW_OP_plus_uconst"; + case 0x24: return "DW_OP_shl"; + case 0x25: return "DW_OP_shr"; + case 0x26: return "DW_OP_shra"; + case 0x27: return "DW_OP_xor"; + case 0x2f: return "DW_OP_skip"; + case 0x28: return "DW_OP_bra"; + case 0x29: return "DW_OP_eq"; + case 0x2a: return "DW_OP_ge"; + case 0x2b: return "DW_OP_gt"; + case 0x2c: return "DW_OP_le"; + case 0x2d: return "DW_OP_lt"; + case 0x2e: return "DW_OP_ne"; + case 0x30: return "DW_OP_lit0"; + case 0x31: return "DW_OP_lit1"; + case 0x32: return "DW_OP_lit2"; + case 0x33: return "DW_OP_lit3"; + case 0x34: return "DW_OP_lit4"; + case 0x35: return "DW_OP_lit5"; + case 0x36: return "DW_OP_lit6"; + case 0x37: return "DW_OP_lit7"; + case 0x38: return "DW_OP_lit8"; + case 0x39: return "DW_OP_lit9"; + case 0x3a: return "DW_OP_lit10"; + case 0x3b: return "DW_OP_lit11"; + case 0x3c: return "DW_OP_lit12"; + case 0x3d: return "DW_OP_lit13"; + case 0x3e: return "DW_OP_lit14"; + case 0x3f: return "DW_OP_lit15"; + case 0x40: return "DW_OP_lit16"; + case 0x41: return "DW_OP_lit17"; + case 0x42: return "DW_OP_lit18"; + case 0x43: return "DW_OP_lit19"; + case 0x44: return "DW_OP_lit20"; + case 0x45: return "DW_OP_lit21"; + case 0x46: return "DW_OP_lit22"; + case 0x47: return "DW_OP_lit23"; + case 0x48: return "DW_OP_lit24"; + case 0x49: return "DW_OP_lit25"; + case 0x4a: return "DW_OP_lit26"; + case 0x4b: return "DW_OP_lit27"; + case 0x4c: return "DW_OP_lit28"; + case 0x4d: return "DW_OP_lit29"; + case 0x4e: return "DW_OP_lit30"; + case 0x4f: return "DW_OP_lit31"; + case 0x50: return "DW_OP_reg0"; + case 0x51: return "DW_OP_reg1"; + case 0x52: return "DW_OP_reg2"; + case 0x53: return "DW_OP_reg3"; + case 0x54: return "DW_OP_reg4"; + case 0x55: return "DW_OP_reg5"; + case 0x56: return "DW_OP_reg6"; + case 0x57: return "DW_OP_reg7"; + case 0x58: return "DW_OP_reg8"; + case 0x59: return "DW_OP_reg9"; + case 0x5a: return "DW_OP_reg10"; + case 0x5b: return "DW_OP_reg11"; + case 0x5c: return "DW_OP_reg12"; + case 0x5d: return "DW_OP_reg13"; + case 0x5e: return "DW_OP_reg14"; + case 0x5f: return "DW_OP_reg15"; + case 0x60: return "DW_OP_reg16"; + case 0x61: return "DW_OP_reg17"; + case 0x62: return "DW_OP_reg18"; + case 0x63: return "DW_OP_reg19"; + case 0x64: return "DW_OP_reg20"; + case 0x65: return "DW_OP_reg21"; + case 0x66: return "DW_OP_reg22"; + case 0x67: return "DW_OP_reg23"; + case 0x68: return "DW_OP_reg24"; + case 0x69: return "DW_OP_reg25"; + case 0x6a: return "DW_OP_reg26"; + case 0x6b: return "DW_OP_reg27"; + case 0x6c: return "DW_OP_reg28"; + case 0x6d: return "DW_OP_reg29"; + case 0x6e: return "DW_OP_reg30"; + case 0x6f: return "DW_OP_reg31"; + case 0x70: return "DW_OP_breg0"; + case 0x71: return "DW_OP_breg1"; + case 0x72: return "DW_OP_breg2"; + case 0x73: return "DW_OP_breg3"; + case 0x74: return "DW_OP_breg4"; + case 0x75: return "DW_OP_breg5"; + case 0x76: return "DW_OP_breg6"; + case 0x77: return "DW_OP_breg7"; + case 0x78: return "DW_OP_breg8"; + case 0x79: return "DW_OP_breg9"; + case 0x7a: return "DW_OP_breg10"; + case 0x7b: return "DW_OP_breg11"; + case 0x7c: return "DW_OP_breg12"; + case 0x7d: return "DW_OP_breg13"; + case 0x7e: return "DW_OP_breg14"; + case 0x7f: return "DW_OP_breg15"; + case 0x80: return "DW_OP_breg16"; + case 0x81: return "DW_OP_breg17"; + case 0x82: return "DW_OP_breg18"; + case 0x83: return "DW_OP_breg19"; + case 0x84: return "DW_OP_breg20"; + case 0x85: return "DW_OP_breg21"; + case 0x86: return "DW_OP_breg22"; + case 0x87: return "DW_OP_breg23"; + case 0x88: return "DW_OP_breg24"; + case 0x89: return "DW_OP_breg25"; + case 0x8a: return "DW_OP_breg26"; + case 0x8b: return "DW_OP_breg27"; + case 0x8c: return "DW_OP_breg28"; + case 0x8d: return "DW_OP_breg29"; + case 0x8e: return "DW_OP_breg30"; + case 0x8f: return "DW_OP_breg31"; + case 0x90: return "DW_OP_regx"; + case 0x91: return "DW_OP_fbreg"; + case 0x92: return "DW_OP_bregx"; + case 0x93: return "DW_OP_piece"; + case 0x94: return "DW_OP_deref_size"; + case 0x95: return "DW_OP_xderef_size"; + case 0x96: return "DW_OP_nop"; + case 0x97: return "DW_OP_push_object_address"; + case 0x98: return "DW_OP_call2"; + case 0x99: return "DW_OP_call4"; + case 0x9a: return "DW_OP_call_ref"; +// case DW_OP_APPLE_array_ref: return "DW_OP_APPLE_array_ref"; +// case DW_OP_APPLE_extern: return "DW_OP_APPLE_extern"; + case DW_OP_APPLE_uninit: return "DW_OP_APPLE_uninit"; +// case DW_OP_APPLE_assign: return "DW_OP_APPLE_assign"; +// case DW_OP_APPLE_address_of: return "DW_OP_APPLE_address_of"; +// case DW_OP_APPLE_value_of: return "DW_OP_APPLE_value_of"; +// case DW_OP_APPLE_deref_type: return "DW_OP_APPLE_deref_type"; +// case DW_OP_APPLE_expr_local: return "DW_OP_APPLE_expr_local"; +// case DW_OP_APPLE_constf: return "DW_OP_APPLE_constf"; +// case DW_OP_APPLE_scalar_cast: return "DW_OP_APPLE_scalar_cast"; +// case DW_OP_APPLE_clang_cast: return "DW_OP_APPLE_clang_cast"; +// case DW_OP_APPLE_clear: return "DW_OP_APPLE_clear"; +// case DW_OP_APPLE_error: return "DW_OP_APPLE_error"; + default: + snprintf (invalid, sizeof(invalid), "Unknown DW_OP constant: 0x%x", val); + return invalid; + } +} + + +//---------------------------------------------------------------------- +// DWARFExpression constructor +//---------------------------------------------------------------------- +DWARFExpression::DWARFExpression() : + m_data(), + m_reg_kind (eRegisterKindDWARF), + m_loclist_slide (LLDB_INVALID_ADDRESS) +{ +} + +DWARFExpression::DWARFExpression(const DWARFExpression& rhs) : + m_data(rhs.m_data), + m_reg_kind (rhs.m_reg_kind), + m_loclist_slide(rhs.m_loclist_slide) +{ +} + + +DWARFExpression::DWARFExpression(const DataExtractor& data, lldb::offset_t data_offset, lldb::offset_t data_length) : + m_data(data, data_offset, data_length), + m_reg_kind (eRegisterKindDWARF), + m_loclist_slide(LLDB_INVALID_ADDRESS) +{ +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +DWARFExpression::~DWARFExpression() +{ +} + + +bool +DWARFExpression::IsValid() const +{ + return m_data.GetByteSize() > 0; +} + +void +DWARFExpression::SetOpcodeData (const DataExtractor& data) +{ + m_data = data; +} + +void +DWARFExpression::CopyOpcodeData (const DataExtractor& data, lldb::offset_t data_offset, lldb::offset_t data_length) +{ + const uint8_t *bytes = data.PeekData(data_offset, data_length); + if (bytes) + { + m_data.SetData(DataBufferSP(new DataBufferHeap(bytes, data_length))); + m_data.SetByteOrder(data.GetByteOrder()); + m_data.SetAddressByteSize(data.GetAddressByteSize()); + } +} + +void +DWARFExpression::SetOpcodeData (const DataExtractor& data, lldb::offset_t data_offset, lldb::offset_t data_length) +{ + m_data.SetData(data, data_offset, data_length); +} + +void +DWARFExpression::DumpLocation (Stream *s, lldb::offset_t offset, lldb::offset_t length, lldb::DescriptionLevel level, ABI *abi) const +{ + if (!m_data.ValidOffsetForDataOfSize(offset, length)) + return; + const lldb::offset_t start_offset = offset; + const lldb::offset_t end_offset = offset + length; + while (m_data.ValidOffset(offset) && offset < end_offset) + { + const lldb::offset_t op_offset = offset; + const uint8_t op = m_data.GetU8(&offset); + + switch (level) + { + default: + break; + + case lldb::eDescriptionLevelBrief: + if (offset > start_offset) + s->PutChar(' '); + break; + + case lldb::eDescriptionLevelFull: + case lldb::eDescriptionLevelVerbose: + if (offset > start_offset) + s->EOL(); + s->Indent(); + if (level == lldb::eDescriptionLevelFull) + break; + // Fall through for verbose and print offset and DW_OP prefix.. + s->Printf("0x%8.8" PRIx64 ": %s", op_offset, op >= DW_OP_APPLE_uninit ? "DW_OP_APPLE_" : "DW_OP_"); + break; + } + + switch (op) + { + case DW_OP_addr: *s << "DW_OP_addr(" << m_data.GetAddress(&offset) << ") "; break; // 0x03 1 address + case DW_OP_deref: *s << "DW_OP_deref"; break; // 0x06 + case DW_OP_const1u: s->Printf("DW_OP_const1u(0x%2.2x) ", m_data.GetU8(&offset)); break; // 0x08 1 1-byte constant + case DW_OP_const1s: s->Printf("DW_OP_const1s(0x%2.2x) ", m_data.GetU8(&offset)); break; // 0x09 1 1-byte constant + case DW_OP_const2u: s->Printf("DW_OP_const2u(0x%4.4x) ", m_data.GetU16(&offset)); break; // 0x0a 1 2-byte constant + case DW_OP_const2s: s->Printf("DW_OP_const2s(0x%4.4x) ", m_data.GetU16(&offset)); break; // 0x0b 1 2-byte constant + case DW_OP_const4u: s->Printf("DW_OP_const4u(0x%8.8x) ", m_data.GetU32(&offset)); break; // 0x0c 1 4-byte constant + case DW_OP_const4s: s->Printf("DW_OP_const4s(0x%8.8x) ", m_data.GetU32(&offset)); break; // 0x0d 1 4-byte constant + case DW_OP_const8u: s->Printf("DW_OP_const8u(0x%16.16" PRIx64 ") ", m_data.GetU64(&offset)); break; // 0x0e 1 8-byte constant + case DW_OP_const8s: s->Printf("DW_OP_const8s(0x%16.16" PRIx64 ") ", m_data.GetU64(&offset)); break; // 0x0f 1 8-byte constant + case DW_OP_constu: s->Printf("DW_OP_constu(0x%" PRIx64 ") ", m_data.GetULEB128(&offset)); break; // 0x10 1 ULEB128 constant + case DW_OP_consts: s->Printf("DW_OP_consts(0x%" PRId64 ") ", m_data.GetSLEB128(&offset)); break; // 0x11 1 SLEB128 constant + case DW_OP_dup: s->PutCString("DW_OP_dup"); break; // 0x12 + case DW_OP_drop: s->PutCString("DW_OP_drop"); break; // 0x13 + case DW_OP_over: s->PutCString("DW_OP_over"); break; // 0x14 + case DW_OP_pick: s->Printf("DW_OP_pick(0x%2.2x) ", m_data.GetU8(&offset)); break; // 0x15 1 1-byte stack index + case DW_OP_swap: s->PutCString("DW_OP_swap"); break; // 0x16 + case DW_OP_rot: s->PutCString("DW_OP_rot"); break; // 0x17 + case DW_OP_xderef: s->PutCString("DW_OP_xderef"); break; // 0x18 + case DW_OP_abs: s->PutCString("DW_OP_abs"); break; // 0x19 + case DW_OP_and: s->PutCString("DW_OP_and"); break; // 0x1a + case DW_OP_div: s->PutCString("DW_OP_div"); break; // 0x1b + case DW_OP_minus: s->PutCString("DW_OP_minus"); break; // 0x1c + case DW_OP_mod: s->PutCString("DW_OP_mod"); break; // 0x1d + case DW_OP_mul: s->PutCString("DW_OP_mul"); break; // 0x1e + case DW_OP_neg: s->PutCString("DW_OP_neg"); break; // 0x1f + case DW_OP_not: s->PutCString("DW_OP_not"); break; // 0x20 + case DW_OP_or: s->PutCString("DW_OP_or"); break; // 0x21 + case DW_OP_plus: s->PutCString("DW_OP_plus"); break; // 0x22 + case DW_OP_plus_uconst: // 0x23 1 ULEB128 addend + s->Printf("DW_OP_plus_uconst(0x%" PRIx64 ") ", m_data.GetULEB128(&offset)); + break; + + case DW_OP_shl: s->PutCString("DW_OP_shl"); break; // 0x24 + case DW_OP_shr: s->PutCString("DW_OP_shr"); break; // 0x25 + case DW_OP_shra: s->PutCString("DW_OP_shra"); break; // 0x26 + case DW_OP_xor: s->PutCString("DW_OP_xor"); break; // 0x27 + case DW_OP_skip: s->Printf("DW_OP_skip(0x%4.4x)", m_data.GetU16(&offset)); break; // 0x2f 1 signed 2-byte constant + case DW_OP_bra: s->Printf("DW_OP_bra(0x%4.4x)", m_data.GetU16(&offset)); break; // 0x28 1 signed 2-byte constant + case DW_OP_eq: s->PutCString("DW_OP_eq"); break; // 0x29 + case DW_OP_ge: s->PutCString("DW_OP_ge"); break; // 0x2a + case DW_OP_gt: s->PutCString("DW_OP_gt"); break; // 0x2b + case DW_OP_le: s->PutCString("DW_OP_le"); break; // 0x2c + case DW_OP_lt: s->PutCString("DW_OP_lt"); break; // 0x2d + case DW_OP_ne: s->PutCString("DW_OP_ne"); break; // 0x2e + + case DW_OP_lit0: // 0x30 + case DW_OP_lit1: // 0x31 + case DW_OP_lit2: // 0x32 + case DW_OP_lit3: // 0x33 + case DW_OP_lit4: // 0x34 + case DW_OP_lit5: // 0x35 + case DW_OP_lit6: // 0x36 + case DW_OP_lit7: // 0x37 + case DW_OP_lit8: // 0x38 + case DW_OP_lit9: // 0x39 + case DW_OP_lit10: // 0x3A + case DW_OP_lit11: // 0x3B + case DW_OP_lit12: // 0x3C + case DW_OP_lit13: // 0x3D + case DW_OP_lit14: // 0x3E + case DW_OP_lit15: // 0x3F + case DW_OP_lit16: // 0x40 + case DW_OP_lit17: // 0x41 + case DW_OP_lit18: // 0x42 + case DW_OP_lit19: // 0x43 + case DW_OP_lit20: // 0x44 + case DW_OP_lit21: // 0x45 + case DW_OP_lit22: // 0x46 + case DW_OP_lit23: // 0x47 + case DW_OP_lit24: // 0x48 + case DW_OP_lit25: // 0x49 + case DW_OP_lit26: // 0x4A + case DW_OP_lit27: // 0x4B + case DW_OP_lit28: // 0x4C + case DW_OP_lit29: // 0x4D + case DW_OP_lit30: // 0x4E + case DW_OP_lit31: s->Printf("DW_OP_lit%i", op - DW_OP_lit0); break; // 0x4f + + case DW_OP_reg0: // 0x50 + case DW_OP_reg1: // 0x51 + case DW_OP_reg2: // 0x52 + case DW_OP_reg3: // 0x53 + case DW_OP_reg4: // 0x54 + case DW_OP_reg5: // 0x55 + case DW_OP_reg6: // 0x56 + case DW_OP_reg7: // 0x57 + case DW_OP_reg8: // 0x58 + case DW_OP_reg9: // 0x59 + case DW_OP_reg10: // 0x5A + case DW_OP_reg11: // 0x5B + case DW_OP_reg12: // 0x5C + case DW_OP_reg13: // 0x5D + case DW_OP_reg14: // 0x5E + case DW_OP_reg15: // 0x5F + case DW_OP_reg16: // 0x60 + case DW_OP_reg17: // 0x61 + case DW_OP_reg18: // 0x62 + case DW_OP_reg19: // 0x63 + case DW_OP_reg20: // 0x64 + case DW_OP_reg21: // 0x65 + case DW_OP_reg22: // 0x66 + case DW_OP_reg23: // 0x67 + case DW_OP_reg24: // 0x68 + case DW_OP_reg25: // 0x69 + case DW_OP_reg26: // 0x6A + case DW_OP_reg27: // 0x6B + case DW_OP_reg28: // 0x6C + case DW_OP_reg29: // 0x6D + case DW_OP_reg30: // 0x6E + case DW_OP_reg31: // 0x6F + { + uint32_t reg_num = op - DW_OP_reg0; + if (abi) + { + RegisterInfo reg_info; + if (abi->GetRegisterInfoByKind(m_reg_kind, reg_num, reg_info)) + { + if (reg_info.name) + { + s->PutCString (reg_info.name); + break; + } + else if (reg_info.alt_name) + { + s->PutCString (reg_info.alt_name); + break; + } + } + } + s->Printf("DW_OP_reg%u", reg_num); break; + } + break; + + case DW_OP_breg0: + case DW_OP_breg1: + case DW_OP_breg2: + case DW_OP_breg3: + case DW_OP_breg4: + case DW_OP_breg5: + case DW_OP_breg6: + case DW_OP_breg7: + case DW_OP_breg8: + case DW_OP_breg9: + case DW_OP_breg10: + case DW_OP_breg11: + case DW_OP_breg12: + case DW_OP_breg13: + case DW_OP_breg14: + case DW_OP_breg15: + case DW_OP_breg16: + case DW_OP_breg17: + case DW_OP_breg18: + case DW_OP_breg19: + case DW_OP_breg20: + case DW_OP_breg21: + case DW_OP_breg22: + case DW_OP_breg23: + case DW_OP_breg24: + case DW_OP_breg25: + case DW_OP_breg26: + case DW_OP_breg27: + case DW_OP_breg28: + case DW_OP_breg29: + case DW_OP_breg30: + case DW_OP_breg31: + { + uint32_t reg_num = op - DW_OP_breg0; + int64_t reg_offset = m_data.GetSLEB128(&offset); + if (abi) + { + RegisterInfo reg_info; + if (abi->GetRegisterInfoByKind(m_reg_kind, reg_num, reg_info)) + { + if (reg_info.name) + { + s->Printf("[%s%+" PRIi64 "]", reg_info.name, reg_offset); + break; + } + else if (reg_info.alt_name) + { + s->Printf("[%s%+" PRIi64 "]", reg_info.alt_name, reg_offset); + break; + } + } + } + s->Printf("DW_OP_breg%i(0x%" PRIx64 ")", reg_num, reg_offset); + } + break; + + case DW_OP_regx: // 0x90 1 ULEB128 register + { + uint32_t reg_num = m_data.GetULEB128(&offset); + if (abi) + { + RegisterInfo reg_info; + if (abi->GetRegisterInfoByKind(m_reg_kind, reg_num, reg_info)) + { + if (reg_info.name) + { + s->PutCString (reg_info.name); + break; + } + else if (reg_info.alt_name) + { + s->PutCString (reg_info.alt_name); + break; + } + } + } + s->Printf("DW_OP_regx(%" PRIu32 ")", reg_num); break; + } + break; + case DW_OP_fbreg: // 0x91 1 SLEB128 offset + s->Printf("DW_OP_fbreg(%" PRIi64 ")",m_data.GetSLEB128(&offset)); + break; + case DW_OP_bregx: // 0x92 2 ULEB128 register followed by SLEB128 offset + { + uint32_t reg_num = m_data.GetULEB128(&offset); + int64_t reg_offset = m_data.GetSLEB128(&offset); + if (abi) + { + RegisterInfo reg_info; + if (abi->GetRegisterInfoByKind(m_reg_kind, reg_num, reg_info)) + { + if (reg_info.name) + { + s->Printf("[%s%+" PRIi64 "]", reg_info.name, reg_offset); + break; + } + else if (reg_info.alt_name) + { + s->Printf("[%s%+" PRIi64 "]", reg_info.alt_name, reg_offset); + break; + } + } + } + s->Printf("DW_OP_bregx(reg=%" PRIu32 ",offset=%" PRIi64 ")", reg_num, reg_offset); + } + break; + case DW_OP_piece: // 0x93 1 ULEB128 size of piece addressed + s->Printf("DW_OP_piece(0x%" PRIx64 ")", m_data.GetULEB128(&offset)); + break; + case DW_OP_deref_size: // 0x94 1 1-byte size of data retrieved + s->Printf("DW_OP_deref_size(0x%2.2x)", m_data.GetU8(&offset)); + break; + case DW_OP_xderef_size: // 0x95 1 1-byte size of data retrieved + s->Printf("DW_OP_xderef_size(0x%2.2x)", m_data.GetU8(&offset)); + break; + case DW_OP_nop: s->PutCString("DW_OP_nop"); break; // 0x96 + case DW_OP_push_object_address: s->PutCString("DW_OP_push_object_address"); break; // 0x97 DWARF3 + case DW_OP_call2: // 0x98 DWARF3 1 2-byte offset of DIE + s->Printf("DW_OP_call2(0x%4.4x)", m_data.GetU16(&offset)); + break; + case DW_OP_call4: // 0x99 DWARF3 1 4-byte offset of DIE + s->Printf("DW_OP_call4(0x%8.8x)", m_data.GetU32(&offset)); + break; + case DW_OP_call_ref: // 0x9a DWARF3 1 4- or 8-byte offset of DIE + s->Printf("DW_OP_call_ref(0x%8.8" PRIx64 ")", m_data.GetAddress(&offset)); + break; +// case DW_OP_form_tls_address: s << "form_tls_address"; break; // 0x9b DWARF3 +// case DW_OP_call_frame_cfa: s << "call_frame_cfa"; break; // 0x9c DWARF3 +// case DW_OP_bit_piece: // 0x9d DWARF3 2 +// s->Printf("DW_OP_bit_piece(0x%x, 0x%x)", m_data.GetULEB128(&offset), m_data.GetULEB128(&offset)); +// break; +// case DW_OP_lo_user: s->PutCString("DW_OP_lo_user"); break; // 0xe0 +// case DW_OP_hi_user: s->PutCString("DW_OP_hi_user"); break; // 0xff +// case DW_OP_APPLE_extern: +// s->Printf("DW_OP_APPLE_extern(%" PRIu64 ")", m_data.GetULEB128(&offset)); +// break; +// case DW_OP_APPLE_array_ref: +// s->PutCString("DW_OP_APPLE_array_ref"); +// break; + case DW_OP_APPLE_uninit: + s->PutCString("DW_OP_APPLE_uninit"); // 0xF0 + break; +// case DW_OP_APPLE_assign: // 0xF1 - pops value off and assigns it to second item on stack (2nd item must have assignable context) +// s->PutCString("DW_OP_APPLE_assign"); +// break; +// case DW_OP_APPLE_address_of: // 0xF2 - gets the address of the top stack item (top item must be a variable, or have value_type that is an address already) +// s->PutCString("DW_OP_APPLE_address_of"); +// break; +// case DW_OP_APPLE_value_of: // 0xF3 - pops the value off the stack and pushes the value of that object (top item must be a variable, or expression local) +// s->PutCString("DW_OP_APPLE_value_of"); +// break; +// case DW_OP_APPLE_deref_type: // 0xF4 - gets the address of the top stack item (top item must be a variable, or a clang type) +// s->PutCString("DW_OP_APPLE_deref_type"); +// break; +// case DW_OP_APPLE_expr_local: // 0xF5 - ULEB128 expression local index +// s->Printf("DW_OP_APPLE_expr_local(%" PRIu64 ")", m_data.GetULEB128(&offset)); +// break; +// case DW_OP_APPLE_constf: // 0xF6 - 1 byte float size, followed by constant float data +// { +// uint8_t float_length = m_data.GetU8(&offset); +// s->Printf("DW_OP_APPLE_constf(<%u> ", float_length); +// m_data.Dump(s, offset, eFormatHex, float_length, 1, UINT32_MAX, DW_INVALID_ADDRESS, 0, 0); +// s->PutChar(')'); +// // Consume the float data +// m_data.GetData(&offset, float_length); +// } +// break; +// case DW_OP_APPLE_scalar_cast: +// s->Printf("DW_OP_APPLE_scalar_cast(%s)", Scalar::GetValueTypeAsCString ((Scalar::Type)m_data.GetU8(&offset))); +// break; +// case DW_OP_APPLE_clang_cast: +// { +// clang::Type *clang_type = (clang::Type *)m_data.GetMaxU64(&offset, sizeof(void*)); +// s->Printf("DW_OP_APPLE_clang_cast(%p)", clang_type); +// } +// break; +// case DW_OP_APPLE_clear: +// s->PutCString("DW_OP_APPLE_clear"); +// break; +// case DW_OP_APPLE_error: // 0xFF - Stops expression evaluation and returns an error (no args) +// s->PutCString("DW_OP_APPLE_error"); +// break; + } + } +} + +void +DWARFExpression::SetLocationListSlide (addr_t slide) +{ + m_loclist_slide = slide; +} + +int +DWARFExpression::GetRegisterKind () +{ + return m_reg_kind; +} + +void +DWARFExpression::SetRegisterKind (RegisterKind reg_kind) +{ + m_reg_kind = reg_kind; +} + +bool +DWARFExpression::IsLocationList() const +{ + return m_loclist_slide != LLDB_INVALID_ADDRESS; +} + +void +DWARFExpression::GetDescription (Stream *s, lldb::DescriptionLevel level, addr_t location_list_base_addr, ABI *abi) const +{ + if (IsLocationList()) + { + // We have a location list + lldb::offset_t offset = 0; + uint32_t count = 0; + addr_t curr_base_addr = location_list_base_addr; + while (m_data.ValidOffset(offset)) + { + lldb::addr_t begin_addr_offset = m_data.GetAddress(&offset); + lldb::addr_t end_addr_offset = m_data.GetAddress(&offset); + if (begin_addr_offset < end_addr_offset) + { + if (count > 0) + s->PutCString(", "); + VMRange addr_range(curr_base_addr + begin_addr_offset, curr_base_addr + end_addr_offset); + addr_range.Dump(s, 0, 8); + s->PutChar('{'); + lldb::offset_t location_length = m_data.GetU16(&offset); + DumpLocation (s, offset, location_length, level, abi); + s->PutChar('}'); + offset += location_length; + } + else if (begin_addr_offset == 0 && end_addr_offset == 0) + { + // The end of the location list is marked by both the start and end offset being zero + break; + } + else + { + if ((m_data.GetAddressByteSize() == 4 && (begin_addr_offset == UINT32_MAX)) || + (m_data.GetAddressByteSize() == 8 && (begin_addr_offset == UINT64_MAX))) + { + curr_base_addr = end_addr_offset + location_list_base_addr; + // We have a new base address + if (count > 0) + s->PutCString(", "); + *s << "base_addr = " << end_addr_offset; + } + } + + count++; + } + } + else + { + // We have a normal location that contains DW_OP location opcodes + DumpLocation (s, 0, m_data.GetByteSize(), level, abi); + } +} + +static bool +ReadRegisterValueAsScalar +( + RegisterContext *reg_ctx, + uint32_t reg_kind, + uint32_t reg_num, + Error *error_ptr, + Value &value +) +{ + if (reg_ctx == NULL) + { + if (error_ptr) + error_ptr->SetErrorStringWithFormat("No register context in frame.\n"); + } + else + { + uint32_t native_reg = reg_ctx->ConvertRegisterKindToRegisterNumber(reg_kind, reg_num); + if (native_reg == LLDB_INVALID_REGNUM) + { + if (error_ptr) + error_ptr->SetErrorStringWithFormat("Unable to convert register kind=%u reg_num=%u to a native register number.\n", reg_kind, reg_num); + } + else + { + const RegisterInfo *reg_info = reg_ctx->GetRegisterInfoAtIndex(native_reg); + RegisterValue reg_value; + if (reg_ctx->ReadRegister (reg_info, reg_value)) + { + if (reg_value.GetScalarValue(value.GetScalar())) + { + value.SetValueType (Value::eValueTypeScalar); + value.SetContext (Value::eContextTypeRegisterInfo, + const_cast(reg_info)); + if (error_ptr) + error_ptr->Clear(); + return true; + } + else + { + // If we get this error, then we need to implement a value + // buffer in the dwarf expression evaluation function... + if (error_ptr) + error_ptr->SetErrorStringWithFormat ("register %s can't be converted to a scalar value", + reg_info->name); + } + } + else + { + if (error_ptr) + error_ptr->SetErrorStringWithFormat("register %s is not available", reg_info->name); + } + } + } + return false; +} + +//bool +//DWARFExpression::LocationListContainsLoadAddress (Process* process, const Address &addr) const +//{ +// return LocationListContainsLoadAddress(process, addr.GetLoadAddress(process)); +//} +// +//bool +//DWARFExpression::LocationListContainsLoadAddress (Process* process, addr_t load_addr) const +//{ +// if (load_addr == LLDB_INVALID_ADDRESS) +// return false; +// +// if (IsLocationList()) +// { +// lldb::offset_t offset = 0; +// +// addr_t loc_list_base_addr = m_loclist_slide.GetLoadAddress(process); +// +// if (loc_list_base_addr == LLDB_INVALID_ADDRESS) +// return false; +// +// while (m_data.ValidOffset(offset)) +// { +// // We need to figure out what the value is for the location. +// addr_t lo_pc = m_data.GetAddress(&offset); +// addr_t hi_pc = m_data.GetAddress(&offset); +// if (lo_pc == 0 && hi_pc == 0) +// break; +// else +// { +// lo_pc += loc_list_base_addr; +// hi_pc += loc_list_base_addr; +// +// if (lo_pc <= load_addr && load_addr < hi_pc) +// return true; +// +// offset += m_data.GetU16(&offset); +// } +// } +// } +// return false; +//} + +static offset_t +GetOpcodeDataSize (const DataExtractor &data, const lldb::offset_t data_offset, const uint8_t op) +{ + lldb::offset_t offset = data_offset; + switch (op) + { + case DW_OP_addr: + case DW_OP_call_ref: // 0x9a 1 address sized offset of DIE (DWARF3) + return data.GetAddressByteSize(); + + // Opcodes with no arguments + case DW_OP_deref: // 0x06 + case DW_OP_dup: // 0x12 + case DW_OP_drop: // 0x13 + case DW_OP_over: // 0x14 + case DW_OP_swap: // 0x16 + case DW_OP_rot: // 0x17 + case DW_OP_xderef: // 0x18 + case DW_OP_abs: // 0x19 + case DW_OP_and: // 0x1a + case DW_OP_div: // 0x1b + case DW_OP_minus: // 0x1c + case DW_OP_mod: // 0x1d + case DW_OP_mul: // 0x1e + case DW_OP_neg: // 0x1f + case DW_OP_not: // 0x20 + case DW_OP_or: // 0x21 + case DW_OP_plus: // 0x22 + case DW_OP_shl: // 0x24 + case DW_OP_shr: // 0x25 + case DW_OP_shra: // 0x26 + case DW_OP_xor: // 0x27 + case DW_OP_eq: // 0x29 + case DW_OP_ge: // 0x2a + case DW_OP_gt: // 0x2b + case DW_OP_le: // 0x2c + case DW_OP_lt: // 0x2d + case DW_OP_ne: // 0x2e + case DW_OP_lit0: // 0x30 + case DW_OP_lit1: // 0x31 + case DW_OP_lit2: // 0x32 + case DW_OP_lit3: // 0x33 + case DW_OP_lit4: // 0x34 + case DW_OP_lit5: // 0x35 + case DW_OP_lit6: // 0x36 + case DW_OP_lit7: // 0x37 + case DW_OP_lit8: // 0x38 + case DW_OP_lit9: // 0x39 + case DW_OP_lit10: // 0x3A + case DW_OP_lit11: // 0x3B + case DW_OP_lit12: // 0x3C + case DW_OP_lit13: // 0x3D + case DW_OP_lit14: // 0x3E + case DW_OP_lit15: // 0x3F + case DW_OP_lit16: // 0x40 + case DW_OP_lit17: // 0x41 + case DW_OP_lit18: // 0x42 + case DW_OP_lit19: // 0x43 + case DW_OP_lit20: // 0x44 + case DW_OP_lit21: // 0x45 + case DW_OP_lit22: // 0x46 + case DW_OP_lit23: // 0x47 + case DW_OP_lit24: // 0x48 + case DW_OP_lit25: // 0x49 + case DW_OP_lit26: // 0x4A + case DW_OP_lit27: // 0x4B + case DW_OP_lit28: // 0x4C + case DW_OP_lit29: // 0x4D + case DW_OP_lit30: // 0x4E + case DW_OP_lit31: // 0x4f + case DW_OP_reg0: // 0x50 + case DW_OP_reg1: // 0x51 + case DW_OP_reg2: // 0x52 + case DW_OP_reg3: // 0x53 + case DW_OP_reg4: // 0x54 + case DW_OP_reg5: // 0x55 + case DW_OP_reg6: // 0x56 + case DW_OP_reg7: // 0x57 + case DW_OP_reg8: // 0x58 + case DW_OP_reg9: // 0x59 + case DW_OP_reg10: // 0x5A + case DW_OP_reg11: // 0x5B + case DW_OP_reg12: // 0x5C + case DW_OP_reg13: // 0x5D + case DW_OP_reg14: // 0x5E + case DW_OP_reg15: // 0x5F + case DW_OP_reg16: // 0x60 + case DW_OP_reg17: // 0x61 + case DW_OP_reg18: // 0x62 + case DW_OP_reg19: // 0x63 + case DW_OP_reg20: // 0x64 + case DW_OP_reg21: // 0x65 + case DW_OP_reg22: // 0x66 + case DW_OP_reg23: // 0x67 + case DW_OP_reg24: // 0x68 + case DW_OP_reg25: // 0x69 + case DW_OP_reg26: // 0x6A + case DW_OP_reg27: // 0x6B + case DW_OP_reg28: // 0x6C + case DW_OP_reg29: // 0x6D + case DW_OP_reg30: // 0x6E + case DW_OP_reg31: // 0x6F + case DW_OP_nop: // 0x96 + case DW_OP_push_object_address: // 0x97 DWARF3 + case DW_OP_form_tls_address: // 0x9b DWARF3 + case DW_OP_call_frame_cfa: // 0x9c DWARF3 + case DW_OP_stack_value: // 0x9f DWARF4 + return 0; + + // Opcodes with a single 1 byte arguments + case DW_OP_const1u: // 0x08 1 1-byte constant + case DW_OP_const1s: // 0x09 1 1-byte constant + case DW_OP_pick: // 0x15 1 1-byte stack index + case DW_OP_deref_size: // 0x94 1 1-byte size of data retrieved + case DW_OP_xderef_size: // 0x95 1 1-byte size of data retrieved + return 1; + + // Opcodes with a single 2 byte arguments + case DW_OP_const2u: // 0x0a 1 2-byte constant + case DW_OP_const2s: // 0x0b 1 2-byte constant + case DW_OP_skip: // 0x2f 1 signed 2-byte constant + case DW_OP_bra: // 0x28 1 signed 2-byte constant + case DW_OP_call2: // 0x98 1 2-byte offset of DIE (DWARF3) + return 2; + + // Opcodes with a single 4 byte arguments + case DW_OP_const4u: // 0x0c 1 4-byte constant + case DW_OP_const4s: // 0x0d 1 4-byte constant + case DW_OP_call4: // 0x99 1 4-byte offset of DIE (DWARF3) + return 4; + + // Opcodes with a single 8 byte arguments + case DW_OP_const8u: // 0x0e 1 8-byte constant + case DW_OP_const8s: // 0x0f 1 8-byte constant + return 8; + + // All opcodes that have a single ULEB (signed or unsigned) argument + case DW_OP_constu: // 0x10 1 ULEB128 constant + case DW_OP_consts: // 0x11 1 SLEB128 constant + case DW_OP_plus_uconst: // 0x23 1 ULEB128 addend + case DW_OP_breg0: // 0x70 1 ULEB128 register + case DW_OP_breg1: // 0x71 1 ULEB128 register + case DW_OP_breg2: // 0x72 1 ULEB128 register + case DW_OP_breg3: // 0x73 1 ULEB128 register + case DW_OP_breg4: // 0x74 1 ULEB128 register + case DW_OP_breg5: // 0x75 1 ULEB128 register + case DW_OP_breg6: // 0x76 1 ULEB128 register + case DW_OP_breg7: // 0x77 1 ULEB128 register + case DW_OP_breg8: // 0x78 1 ULEB128 register + case DW_OP_breg9: // 0x79 1 ULEB128 register + case DW_OP_breg10: // 0x7a 1 ULEB128 register + case DW_OP_breg11: // 0x7b 1 ULEB128 register + case DW_OP_breg12: // 0x7c 1 ULEB128 register + case DW_OP_breg13: // 0x7d 1 ULEB128 register + case DW_OP_breg14: // 0x7e 1 ULEB128 register + case DW_OP_breg15: // 0x7f 1 ULEB128 register + case DW_OP_breg16: // 0x80 1 ULEB128 register + case DW_OP_breg17: // 0x81 1 ULEB128 register + case DW_OP_breg18: // 0x82 1 ULEB128 register + case DW_OP_breg19: // 0x83 1 ULEB128 register + case DW_OP_breg20: // 0x84 1 ULEB128 register + case DW_OP_breg21: // 0x85 1 ULEB128 register + case DW_OP_breg22: // 0x86 1 ULEB128 register + case DW_OP_breg23: // 0x87 1 ULEB128 register + case DW_OP_breg24: // 0x88 1 ULEB128 register + case DW_OP_breg25: // 0x89 1 ULEB128 register + case DW_OP_breg26: // 0x8a 1 ULEB128 register + case DW_OP_breg27: // 0x8b 1 ULEB128 register + case DW_OP_breg28: // 0x8c 1 ULEB128 register + case DW_OP_breg29: // 0x8d 1 ULEB128 register + case DW_OP_breg30: // 0x8e 1 ULEB128 register + case DW_OP_breg31: // 0x8f 1 ULEB128 register + case DW_OP_regx: // 0x90 1 ULEB128 register + case DW_OP_fbreg: // 0x91 1 SLEB128 offset + case DW_OP_piece: // 0x93 1 ULEB128 size of piece addressed + data.Skip_LEB128(&offset); + return offset - data_offset; + + // All opcodes that have a 2 ULEB (signed or unsigned) arguments + case DW_OP_bregx: // 0x92 2 ULEB128 register followed by SLEB128 offset + case DW_OP_bit_piece: // 0x9d ULEB128 bit size, ULEB128 bit offset (DWARF3); + data.Skip_LEB128(&offset); + data.Skip_LEB128(&offset); + return offset - data_offset; + + case DW_OP_implicit_value: // 0x9e ULEB128 size followed by block of that size (DWARF4) + { + uint64_t block_len = data.Skip_LEB128(&offset); + offset += block_len; + return offset - data_offset; + } + + default: + break; + } + return LLDB_INVALID_OFFSET; +} + +lldb::addr_t +DWARFExpression::GetLocation_DW_OP_addr (uint32_t op_addr_idx, bool &error) const +{ + error = false; + if (IsLocationList()) + return LLDB_INVALID_ADDRESS; + lldb::offset_t offset = 0; + uint32_t curr_op_addr_idx = 0; + while (m_data.ValidOffset(offset)) + { + const uint8_t op = m_data.GetU8(&offset); + + if (op == DW_OP_addr) + { + const lldb::addr_t op_file_addr = m_data.GetAddress(&offset); + if (curr_op_addr_idx == op_addr_idx) + return op_file_addr; + else + ++curr_op_addr_idx; + } + else + { + const offset_t op_arg_size = GetOpcodeDataSize (m_data, offset, op); + if (op_arg_size == LLDB_INVALID_OFFSET) + { + error = true; + break; + } + offset += op_arg_size; + } + } + return LLDB_INVALID_ADDRESS; +} + +bool +DWARFExpression::Update_DW_OP_addr (lldb::addr_t file_addr) +{ + if (IsLocationList()) + return false; + lldb::offset_t offset = 0; + while (m_data.ValidOffset(offset)) + { + const uint8_t op = m_data.GetU8(&offset); + + if (op == DW_OP_addr) + { + const uint32_t addr_byte_size = m_data.GetAddressByteSize(); + // We have to make a copy of the data as we don't know if this + // data is from a read only memory mapped buffer, so we duplicate + // all of the data first, then modify it, and if all goes well, + // we then replace the data for this expression + + // So first we copy the data into a heap buffer + std::unique_ptr head_data_ap (new DataBufferHeap (m_data.GetDataStart(), + m_data.GetByteSize())); + + // Make en encoder so we can write the address into the buffer using + // the correct byte order (endianness) + DataEncoder encoder (head_data_ap->GetBytes(), + head_data_ap->GetByteSize(), + m_data.GetByteOrder(), + addr_byte_size); + + // Replace the address in the new buffer + if (encoder.PutMaxU64 (offset, addr_byte_size, file_addr) == UINT32_MAX) + return false; + + // All went well, so now we can reset the data using a shared + // pointer to the heap data so "m_data" will now correctly + // manage the heap data. + m_data.SetData (DataBufferSP (head_data_ap.release())); + return true; + } + else + { + const offset_t op_arg_size = GetOpcodeDataSize (m_data, offset, op); + if (op_arg_size == LLDB_INVALID_OFFSET) + break; + offset += op_arg_size; + } + } + return false; +} + +bool +DWARFExpression::LocationListContainsAddress (lldb::addr_t loclist_base_addr, lldb::addr_t addr) const +{ + if (addr == LLDB_INVALID_ADDRESS) + return false; + + if (IsLocationList()) + { + lldb::offset_t offset = 0; + + if (loclist_base_addr == LLDB_INVALID_ADDRESS) + return false; + + while (m_data.ValidOffset(offset)) + { + // We need to figure out what the value is for the location. + addr_t lo_pc = m_data.GetAddress(&offset); + addr_t hi_pc = m_data.GetAddress(&offset); + if (lo_pc == 0 && hi_pc == 0) + break; + else + { + lo_pc += loclist_base_addr - m_loclist_slide; + hi_pc += loclist_base_addr - m_loclist_slide; + + if (lo_pc <= addr && addr < hi_pc) + return true; + + offset += m_data.GetU16(&offset); + } + } + } + return false; +} + +bool +DWARFExpression::GetLocation (addr_t base_addr, addr_t pc, lldb::offset_t &offset, lldb::offset_t &length) +{ + offset = 0; + if (!IsLocationList()) + { + length = m_data.GetByteSize(); + return true; + } + + if (base_addr != LLDB_INVALID_ADDRESS && pc != LLDB_INVALID_ADDRESS) + { + addr_t curr_base_addr = base_addr; + + while (m_data.ValidOffset(offset)) + { + // We need to figure out what the value is for the location. + addr_t lo_pc = m_data.GetAddress(&offset); + addr_t hi_pc = m_data.GetAddress(&offset); + if (lo_pc == 0 && hi_pc == 0) + { + break; + } + else + { + lo_pc += curr_base_addr - m_loclist_slide; + hi_pc += curr_base_addr - m_loclist_slide; + + length = m_data.GetU16(&offset); + + if (length > 0 && lo_pc <= pc && pc < hi_pc) + return true; + + offset += length; + } + } + } + offset = LLDB_INVALID_OFFSET; + length = 0; + return false; +} + +bool +DWARFExpression::DumpLocationForAddress (Stream *s, + lldb::DescriptionLevel level, + addr_t base_addr, + addr_t address, + ABI *abi) +{ + lldb::offset_t offset = 0; + lldb::offset_t length = 0; + + if (GetLocation (base_addr, address, offset, length)) + { + if (length > 0) + { + DumpLocation(s, offset, length, level, abi); + return true; + } + } + return false; +} + +bool +DWARFExpression::Evaluate +( + ExecutionContextScope *exe_scope, + ClangExpressionVariableList *expr_locals, + ClangExpressionDeclMap *decl_map, + lldb::addr_t loclist_base_load_addr, + const Value* initial_value_ptr, + Value& result, + Error *error_ptr +) const +{ + ExecutionContext exe_ctx (exe_scope); + return Evaluate(&exe_ctx, expr_locals, decl_map, NULL, loclist_base_load_addr, initial_value_ptr, result, error_ptr); +} + +bool +DWARFExpression::Evaluate +( + ExecutionContext *exe_ctx, + ClangExpressionVariableList *expr_locals, + ClangExpressionDeclMap *decl_map, + RegisterContext *reg_ctx, + lldb::addr_t loclist_base_load_addr, + const Value* initial_value_ptr, + Value& result, + Error *error_ptr +) const +{ + if (IsLocationList()) + { + lldb::offset_t offset = 0; + addr_t pc; + StackFrame *frame = NULL; + if (reg_ctx) + pc = reg_ctx->GetPC(); + else + { + frame = exe_ctx->GetFramePtr(); + if (!frame) + return false; + RegisterContextSP reg_ctx_sp = frame->GetRegisterContext(); + if (!reg_ctx_sp) + return false; + pc = reg_ctx_sp->GetPC(); + } + + if (loclist_base_load_addr != LLDB_INVALID_ADDRESS) + { + if (pc == LLDB_INVALID_ADDRESS) + { + if (error_ptr) + error_ptr->SetErrorString("Invalid PC in frame."); + return false; + } + + addr_t curr_loclist_base_load_addr = loclist_base_load_addr; + + while (m_data.ValidOffset(offset)) + { + // We need to figure out what the value is for the location. + addr_t lo_pc = m_data.GetAddress(&offset); + addr_t hi_pc = m_data.GetAddress(&offset); + if (lo_pc == 0 && hi_pc == 0) + { + break; + } + else + { + lo_pc += curr_loclist_base_load_addr - m_loclist_slide; + hi_pc += curr_loclist_base_load_addr - m_loclist_slide; + + uint16_t length = m_data.GetU16(&offset); + + if (length > 0 && lo_pc <= pc && pc < hi_pc) + { + return DWARFExpression::Evaluate (exe_ctx, expr_locals, decl_map, reg_ctx, m_data, offset, length, m_reg_kind, initial_value_ptr, result, error_ptr); + } + offset += length; + } + } + } + if (error_ptr) + error_ptr->SetErrorString ("variable not available"); + return false; + } + + // Not a location list, just a single expression. + return DWARFExpression::Evaluate (exe_ctx, expr_locals, decl_map, reg_ctx, m_data, 0, m_data.GetByteSize(), m_reg_kind, initial_value_ptr, result, error_ptr); +} + + + +bool +DWARFExpression::Evaluate +( + ExecutionContext *exe_ctx, + ClangExpressionVariableList *expr_locals, + ClangExpressionDeclMap *decl_map, + RegisterContext *reg_ctx, + const DataExtractor& opcodes, + const lldb::offset_t opcodes_offset, + const lldb::offset_t opcodes_length, + const uint32_t reg_kind, + const Value* initial_value_ptr, + Value& result, + Error *error_ptr +) +{ + + if (opcodes_length == 0) + { + if (error_ptr) + error_ptr->SetErrorString ("no location, value may have been optimized out"); + return false; + } + std::vector stack; + + Process *process = NULL; + StackFrame *frame = NULL; + + if (exe_ctx) + { + process = exe_ctx->GetProcessPtr(); + frame = exe_ctx->GetFramePtr(); + } + if (reg_ctx == NULL && frame) + reg_ctx = frame->GetRegisterContext().get(); + + if (initial_value_ptr) + stack.push_back(*initial_value_ptr); + + lldb::offset_t offset = opcodes_offset; + const lldb::offset_t end_offset = opcodes_offset + opcodes_length; + Value tmp; + uint32_t reg_num; + + // Make sure all of the data is available in opcodes. + if (!opcodes.ValidOffsetForDataOfSize(opcodes_offset, opcodes_length)) + { + if (error_ptr) + error_ptr->SetErrorString ("invalid offset and/or length for opcodes buffer."); + return false; + } + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS)); + + + while (opcodes.ValidOffset(offset) && offset < end_offset) + { + const lldb::offset_t op_offset = offset; + const uint8_t op = opcodes.GetU8(&offset); + + if (log && log->GetVerbose()) + { + size_t count = stack.size(); + log->Printf("Stack before operation has %lu values:", count); + for (size_t i=0; iPrintf(" %s", new_value.GetData()); + } + log->Printf("0x%8.8" PRIx64 ": %s", op_offset, DW_OP_value_to_name(op)); + } + switch (op) + { + //---------------------------------------------------------------------- + // The DW_OP_addr operation has a single operand that encodes a machine + // address and whose size is the size of an address on the target machine. + //---------------------------------------------------------------------- + case DW_OP_addr: + stack.push_back(Scalar(opcodes.GetAddress(&offset))); + stack.back().SetValueType (Value::eValueTypeFileAddress); + break; + + //---------------------------------------------------------------------- + // The DW_OP_addr_sect_offset4 is used for any location expressions in + // shared libraries that have a location like: + // DW_OP_addr(0x1000) + // If this address resides in a shared library, then this virtual + // address won't make sense when it is evaluated in the context of a + // running process where shared libraries have been slid. To account for + // this, this new address type where we can store the section pointer + // and a 4 byte offset. + //---------------------------------------------------------------------- +// case DW_OP_addr_sect_offset4: +// { +// result_type = eResultTypeFileAddress; +// lldb::Section *sect = (lldb::Section *)opcodes.GetMaxU64(&offset, sizeof(void *)); +// lldb::addr_t sect_offset = opcodes.GetU32(&offset); +// +// Address so_addr (sect, sect_offset); +// lldb::addr_t load_addr = so_addr.GetLoadAddress(); +// if (load_addr != LLDB_INVALID_ADDRESS) +// { +// // We successfully resolve a file address to a load +// // address. +// stack.push_back(load_addr); +// break; +// } +// else +// { +// // We were able +// if (error_ptr) +// error_ptr->SetErrorStringWithFormat ("Section %s in %s is not currently loaded.\n", sect->GetName().AsCString(), sect->GetModule()->GetFileSpec().GetFilename().AsCString()); +// return false; +// } +// } +// break; + + //---------------------------------------------------------------------- + // OPCODE: DW_OP_deref + // OPERANDS: none + // DESCRIPTION: Pops the top stack entry and treats it as an address. + // The value retrieved from that address is pushed. The size of the + // data retrieved from the dereferenced address is the size of an + // address on the target machine. + //---------------------------------------------------------------------- + case DW_OP_deref: + { + Value::ValueType value_type = stack.back().GetValueType(); + switch (value_type) + { + case Value::eValueTypeHostAddress: + { + void *src = (void *)stack.back().GetScalar().ULongLong(); + intptr_t ptr; + ::memcpy (&ptr, src, sizeof(void *)); + stack.back().GetScalar() = ptr; + stack.back().ClearContext(); + } + break; + case Value::eValueTypeLoadAddress: + if (exe_ctx) + { + if (process) + { + lldb::addr_t pointer_addr = stack.back().GetScalar().ULongLong(LLDB_INVALID_ADDRESS); + uint8_t addr_bytes[sizeof(lldb::addr_t)]; + uint32_t addr_size = process->GetAddressByteSize(); + Error error; + if (process->ReadMemory(pointer_addr, &addr_bytes, addr_size, error) == addr_size) + { + DataExtractor addr_data(addr_bytes, sizeof(addr_bytes), process->GetByteOrder(), addr_size); + lldb::offset_t addr_data_offset = 0; + stack.back().GetScalar() = addr_data.GetPointer(&addr_data_offset); + stack.back().ClearContext(); + } + else + { + if (error_ptr) + error_ptr->SetErrorStringWithFormat ("Failed to dereference pointer from 0x%" PRIx64 " for DW_OP_deref: %s\n", + pointer_addr, + error.AsCString()); + return false; + } + } + else + { + if (error_ptr) + error_ptr->SetErrorStringWithFormat ("NULL process for DW_OP_deref.\n"); + return false; + } + } + else + { + if (error_ptr) + error_ptr->SetErrorStringWithFormat ("NULL execution context for DW_OP_deref.\n"); + return false; + } + break; + + default: + break; + } + + } + break; + + //---------------------------------------------------------------------- + // OPCODE: DW_OP_deref_size + // OPERANDS: 1 + // 1 - uint8_t that specifies the size of the data to dereference. + // DESCRIPTION: Behaves like the DW_OP_deref operation: it pops the top + // stack entry and treats it as an address. The value retrieved from that + // address is pushed. In the DW_OP_deref_size operation, however, the + // size in bytes of the data retrieved from the dereferenced address is + // specified by the single operand. This operand is a 1-byte unsigned + // integral constant whose value may not be larger than the size of an + // address on the target machine. The data retrieved is zero extended + // to the size of an address on the target machine before being pushed + // on the expression stack. + //---------------------------------------------------------------------- + case DW_OP_deref_size: + { + uint8_t size = opcodes.GetU8(&offset); + Value::ValueType value_type = stack.back().GetValueType(); + switch (value_type) + { + case Value::eValueTypeHostAddress: + { + void *src = (void *)stack.back().GetScalar().ULongLong(); + intptr_t ptr; + ::memcpy (&ptr, src, sizeof(void *)); + // I can't decide whether the size operand should apply to the bytes in their + // lldb-host endianness or the target endianness.. I doubt this'll ever come up + // but I'll opt for assuming big endian regardless. + switch (size) + { + case 1: ptr = ptr & 0xff; break; + case 2: ptr = ptr & 0xffff; break; + case 3: ptr = ptr & 0xffffff; break; + case 4: ptr = ptr & 0xffffffff; break; + // the casts are added to work around the case where intptr_t is a 32 bit quantity; + // presumably we won't hit the 5..7 cases if (void*) is 32-bits in this program. + case 5: ptr = (intptr_t) ptr & 0xffffffffffULL; break; + case 6: ptr = (intptr_t) ptr & 0xffffffffffffULL; break; + case 7: ptr = (intptr_t) ptr & 0xffffffffffffffULL; break; + default: break; + } + stack.back().GetScalar() = ptr; + stack.back().ClearContext(); + } + break; + case Value::eValueTypeLoadAddress: + if (exe_ctx) + { + if (process) + { + lldb::addr_t pointer_addr = stack.back().GetScalar().ULongLong(LLDB_INVALID_ADDRESS); + uint8_t addr_bytes[sizeof(lldb::addr_t)]; + Error error; + if (process->ReadMemory(pointer_addr, &addr_bytes, size, error) == size) + { + DataExtractor addr_data(addr_bytes, sizeof(addr_bytes), process->GetByteOrder(), size); + lldb::offset_t addr_data_offset = 0; + switch (size) + { + case 1: stack.back().GetScalar() = addr_data.GetU8(&addr_data_offset); break; + case 2: stack.back().GetScalar() = addr_data.GetU16(&addr_data_offset); break; + case 4: stack.back().GetScalar() = addr_data.GetU32(&addr_data_offset); break; + case 8: stack.back().GetScalar() = addr_data.GetU64(&addr_data_offset); break; + default: stack.back().GetScalar() = addr_data.GetPointer(&addr_data_offset); + } + stack.back().ClearContext(); + } + else + { + if (error_ptr) + error_ptr->SetErrorStringWithFormat ("Failed to dereference pointer from 0x%" PRIx64 " for DW_OP_deref: %s\n", + pointer_addr, + error.AsCString()); + return false; + } + } + else + { + if (error_ptr) + error_ptr->SetErrorStringWithFormat ("NULL process for DW_OP_deref.\n"); + return false; + } + } + else + { + if (error_ptr) + error_ptr->SetErrorStringWithFormat ("NULL execution context for DW_OP_deref.\n"); + return false; + } + break; + + default: + break; + } + + } + break; + + //---------------------------------------------------------------------- + // OPCODE: DW_OP_xderef_size + // OPERANDS: 1 + // 1 - uint8_t that specifies the size of the data to dereference. + // DESCRIPTION: Behaves like the DW_OP_xderef operation: the entry at + // the top of the stack is treated as an address. The second stack + // entry is treated as an "address space identifier" for those + // architectures that support multiple address spaces. The top two + // stack elements are popped, a data item is retrieved through an + // implementation-defined address calculation and pushed as the new + // stack top. In the DW_OP_xderef_size operation, however, the size in + // bytes of the data retrieved from the dereferenced address is + // specified by the single operand. This operand is a 1-byte unsigned + // integral constant whose value may not be larger than the size of an + // address on the target machine. The data retrieved is zero extended + // to the size of an address on the target machine before being pushed + // on the expression stack. + //---------------------------------------------------------------------- + case DW_OP_xderef_size: + if (error_ptr) + error_ptr->SetErrorString("Unimplemented opcode: DW_OP_xderef_size."); + return false; + //---------------------------------------------------------------------- + // OPCODE: DW_OP_xderef + // OPERANDS: none + // DESCRIPTION: Provides an extended dereference mechanism. The entry at + // the top of the stack is treated as an address. The second stack entry + // is treated as an "address space identifier" for those architectures + // that support multiple address spaces. The top two stack elements are + // popped, a data item is retrieved through an implementation-defined + // address calculation and pushed as the new stack top. The size of the + // data retrieved from the dereferenced address is the size of an address + // on the target machine. + //---------------------------------------------------------------------- + case DW_OP_xderef: + if (error_ptr) + error_ptr->SetErrorString("Unimplemented opcode: DW_OP_xderef."); + return false; + + //---------------------------------------------------------------------- + // All DW_OP_constXXX opcodes have a single operand as noted below: + // + // Opcode Operand 1 + // --------------- ---------------------------------------------------- + // DW_OP_const1u 1-byte unsigned integer constant + // DW_OP_const1s 1-byte signed integer constant + // DW_OP_const2u 2-byte unsigned integer constant + // DW_OP_const2s 2-byte signed integer constant + // DW_OP_const4u 4-byte unsigned integer constant + // DW_OP_const4s 4-byte signed integer constant + // DW_OP_const8u 8-byte unsigned integer constant + // DW_OP_const8s 8-byte signed integer constant + // DW_OP_constu unsigned LEB128 integer constant + // DW_OP_consts signed LEB128 integer constant + //---------------------------------------------------------------------- + case DW_OP_const1u : stack.push_back(Scalar(( uint8_t)opcodes.GetU8 (&offset))); break; + case DW_OP_const1s : stack.push_back(Scalar(( int8_t)opcodes.GetU8 (&offset))); break; + case DW_OP_const2u : stack.push_back(Scalar((uint16_t)opcodes.GetU16 (&offset))); break; + case DW_OP_const2s : stack.push_back(Scalar(( int16_t)opcodes.GetU16 (&offset))); break; + case DW_OP_const4u : stack.push_back(Scalar((uint32_t)opcodes.GetU32 (&offset))); break; + case DW_OP_const4s : stack.push_back(Scalar(( int32_t)opcodes.GetU32 (&offset))); break; + case DW_OP_const8u : stack.push_back(Scalar((uint64_t)opcodes.GetU64 (&offset))); break; + case DW_OP_const8s : stack.push_back(Scalar(( int64_t)opcodes.GetU64 (&offset))); break; + case DW_OP_constu : stack.push_back(Scalar(opcodes.GetULEB128 (&offset))); break; + case DW_OP_consts : stack.push_back(Scalar(opcodes.GetSLEB128 (&offset))); break; + + //---------------------------------------------------------------------- + // OPCODE: DW_OP_dup + // OPERANDS: none + // DESCRIPTION: duplicates the value at the top of the stack + //---------------------------------------------------------------------- + case DW_OP_dup: + if (stack.empty()) + { + if (error_ptr) + error_ptr->SetErrorString("Expression stack empty for DW_OP_dup."); + return false; + } + else + stack.push_back(stack.back()); + break; + + //---------------------------------------------------------------------- + // OPCODE: DW_OP_drop + // OPERANDS: none + // DESCRIPTION: pops the value at the top of the stack + //---------------------------------------------------------------------- + case DW_OP_drop: + if (stack.empty()) + { + if (error_ptr) + error_ptr->SetErrorString("Expression stack empty for DW_OP_drop."); + return false; + } + else + stack.pop_back(); + break; + + //---------------------------------------------------------------------- + // OPCODE: DW_OP_over + // OPERANDS: none + // DESCRIPTION: Duplicates the entry currently second in the stack at + // the top of the stack. + //---------------------------------------------------------------------- + case DW_OP_over: + if (stack.size() < 2) + { + if (error_ptr) + error_ptr->SetErrorString("Expression stack needs at least 2 items for DW_OP_over."); + return false; + } + else + stack.push_back(stack[stack.size() - 2]); + break; + + + //---------------------------------------------------------------------- + // OPCODE: DW_OP_pick + // OPERANDS: uint8_t index into the current stack + // DESCRIPTION: The stack entry with the specified index (0 through 255, + // inclusive) is pushed on the stack + //---------------------------------------------------------------------- + case DW_OP_pick: + { + uint8_t pick_idx = opcodes.GetU8(&offset); + if (pick_idx < stack.size()) + stack.push_back(stack[pick_idx]); + else + { + if (error_ptr) + error_ptr->SetErrorStringWithFormat("Index %u out of range for DW_OP_pick.\n", pick_idx); + return false; + } + } + break; + + //---------------------------------------------------------------------- + // OPCODE: DW_OP_swap + // OPERANDS: none + // DESCRIPTION: swaps the top two stack entries. The entry at the top + // of the stack becomes the second stack entry, and the second entry + // becomes the top of the stack + //---------------------------------------------------------------------- + case DW_OP_swap: + if (stack.size() < 2) + { + if (error_ptr) + error_ptr->SetErrorString("Expression stack needs at least 2 items for DW_OP_swap."); + return false; + } + else + { + tmp = stack.back(); + stack.back() = stack[stack.size() - 2]; + stack[stack.size() - 2] = tmp; + } + break; + + //---------------------------------------------------------------------- + // OPCODE: DW_OP_rot + // OPERANDS: none + // DESCRIPTION: Rotates the first three stack entries. The entry at + // the top of the stack becomes the third stack entry, the second + // entry becomes the top of the stack, and the third entry becomes + // the second entry. + //---------------------------------------------------------------------- + case DW_OP_rot: + if (stack.size() < 3) + { + if (error_ptr) + error_ptr->SetErrorString("Expression stack needs at least 3 items for DW_OP_rot."); + return false; + } + else + { + size_t last_idx = stack.size() - 1; + Value old_top = stack[last_idx]; + stack[last_idx] = stack[last_idx - 1]; + stack[last_idx - 1] = stack[last_idx - 2]; + stack[last_idx - 2] = old_top; + } + break; + + //---------------------------------------------------------------------- + // OPCODE: DW_OP_abs + // OPERANDS: none + // DESCRIPTION: pops the top stack entry, interprets it as a signed + // value and pushes its absolute value. If the absolute value can not be + // represented, the result is undefined. + //---------------------------------------------------------------------- + case DW_OP_abs: + if (stack.empty()) + { + if (error_ptr) + error_ptr->SetErrorString("Expression stack needs at least 1 item for DW_OP_abs."); + return false; + } + else if (stack.back().ResolveValue(exe_ctx).AbsoluteValue() == false) + { + if (error_ptr) + error_ptr->SetErrorString("Failed to take the absolute value of the first stack item."); + return false; + } + break; + + //---------------------------------------------------------------------- + // OPCODE: DW_OP_and + // OPERANDS: none + // DESCRIPTION: pops the top two stack values, performs a bitwise and + // operation on the two, and pushes the result. + //---------------------------------------------------------------------- + case DW_OP_and: + if (stack.size() < 2) + { + if (error_ptr) + error_ptr->SetErrorString("Expression stack needs at least 2 items for DW_OP_and."); + return false; + } + else + { + tmp = stack.back(); + stack.pop_back(); + stack.back().ResolveValue(exe_ctx) = stack.back().ResolveValue(exe_ctx) & tmp.ResolveValue(exe_ctx); + } + break; + + //---------------------------------------------------------------------- + // OPCODE: DW_OP_div + // OPERANDS: none + // DESCRIPTION: pops the top two stack values, divides the former second + // entry by the former top of the stack using signed division, and + // pushes the result. + //---------------------------------------------------------------------- + case DW_OP_div: + if (stack.size() < 2) + { + if (error_ptr) + error_ptr->SetErrorString("Expression stack needs at least 2 items for DW_OP_div."); + return false; + } + else + { + tmp = stack.back(); + if (tmp.ResolveValue(exe_ctx).IsZero()) + { + if (error_ptr) + error_ptr->SetErrorString("Divide by zero."); + return false; + } + else + { + stack.pop_back(); + stack.back() = stack.back().ResolveValue(exe_ctx) / tmp.ResolveValue(exe_ctx); + if (!stack.back().ResolveValue(exe_ctx).IsValid()) + { + if (error_ptr) + error_ptr->SetErrorString("Divide failed."); + return false; + } + } + } + break; + + //---------------------------------------------------------------------- + // OPCODE: DW_OP_minus + // OPERANDS: none + // DESCRIPTION: pops the top two stack values, subtracts the former top + // of the stack from the former second entry, and pushes the result. + //---------------------------------------------------------------------- + case DW_OP_minus: + if (stack.size() < 2) + { + if (error_ptr) + error_ptr->SetErrorString("Expression stack needs at least 2 items for DW_OP_minus."); + return false; + } + else + { + tmp = stack.back(); + stack.pop_back(); + stack.back().ResolveValue(exe_ctx) = stack.back().ResolveValue(exe_ctx) - tmp.ResolveValue(exe_ctx); + } + break; + + //---------------------------------------------------------------------- + // OPCODE: DW_OP_mod + // OPERANDS: none + // DESCRIPTION: pops the top two stack values and pushes the result of + // the calculation: former second stack entry modulo the former top of + // the stack. + //---------------------------------------------------------------------- + case DW_OP_mod: + if (stack.size() < 2) + { + if (error_ptr) + error_ptr->SetErrorString("Expression stack needs at least 2 items for DW_OP_mod."); + return false; + } + else + { + tmp = stack.back(); + stack.pop_back(); + stack.back().ResolveValue(exe_ctx) = stack.back().ResolveValue(exe_ctx) % tmp.ResolveValue(exe_ctx); + } + break; + + + //---------------------------------------------------------------------- + // OPCODE: DW_OP_mul + // OPERANDS: none + // DESCRIPTION: pops the top two stack entries, multiplies them + // together, and pushes the result. + //---------------------------------------------------------------------- + case DW_OP_mul: + if (stack.size() < 2) + { + if (error_ptr) + error_ptr->SetErrorString("Expression stack needs at least 2 items for DW_OP_mul."); + return false; + } + else + { + tmp = stack.back(); + stack.pop_back(); + stack.back().ResolveValue(exe_ctx) = stack.back().ResolveValue(exe_ctx) * tmp.ResolveValue(exe_ctx); + } + break; + + //---------------------------------------------------------------------- + // OPCODE: DW_OP_neg + // OPERANDS: none + // DESCRIPTION: pops the top stack entry, and pushes its negation. + //---------------------------------------------------------------------- + case DW_OP_neg: + if (stack.empty()) + { + if (error_ptr) + error_ptr->SetErrorString("Expression stack needs at least 1 item for DW_OP_neg."); + return false; + } + else + { + if (stack.back().ResolveValue(exe_ctx).UnaryNegate() == false) + { + if (error_ptr) + error_ptr->SetErrorString("Unary negate failed."); + return false; + } + } + break; + + //---------------------------------------------------------------------- + // OPCODE: DW_OP_not + // OPERANDS: none + // DESCRIPTION: pops the top stack entry, and pushes its bitwise + // complement + //---------------------------------------------------------------------- + case DW_OP_not: + if (stack.empty()) + { + if (error_ptr) + error_ptr->SetErrorString("Expression stack needs at least 1 item for DW_OP_not."); + return false; + } + else + { + if (stack.back().ResolveValue(exe_ctx).OnesComplement() == false) + { + if (error_ptr) + error_ptr->SetErrorString("Logical NOT failed."); + return false; + } + } + break; + + //---------------------------------------------------------------------- + // OPCODE: DW_OP_or + // OPERANDS: none + // DESCRIPTION: pops the top two stack entries, performs a bitwise or + // operation on the two, and pushes the result. + //---------------------------------------------------------------------- + case DW_OP_or: + if (stack.size() < 2) + { + if (error_ptr) + error_ptr->SetErrorString("Expression stack needs at least 2 items for DW_OP_or."); + return false; + } + else + { + tmp = stack.back(); + stack.pop_back(); + stack.back().ResolveValue(exe_ctx) = stack.back().ResolveValue(exe_ctx) | tmp.ResolveValue(exe_ctx); + } + break; + + //---------------------------------------------------------------------- + // OPCODE: DW_OP_plus + // OPERANDS: none + // DESCRIPTION: pops the top two stack entries, adds them together, and + // pushes the result. + //---------------------------------------------------------------------- + case DW_OP_plus: + if (stack.size() < 2) + { + if (error_ptr) + error_ptr->SetErrorString("Expression stack needs at least 2 items for DW_OP_plus."); + return false; + } + else + { + tmp = stack.back(); + stack.pop_back(); + stack.back().ResolveValue(exe_ctx) = stack.back().ResolveValue(exe_ctx) + tmp.ResolveValue(exe_ctx); + } + break; + + //---------------------------------------------------------------------- + // OPCODE: DW_OP_plus_uconst + // OPERANDS: none + // DESCRIPTION: pops the top stack entry, adds it to the unsigned LEB128 + // constant operand and pushes the result. + //---------------------------------------------------------------------- + case DW_OP_plus_uconst: + if (stack.empty()) + { + if (error_ptr) + error_ptr->SetErrorString("Expression stack needs at least 1 item for DW_OP_plus_uconst."); + return false; + } + else + { + const uint64_t uconst_value = opcodes.GetULEB128(&offset); + // Implicit conversion from a UINT to a Scalar... + stack.back().ResolveValue(exe_ctx) += uconst_value; + if (!stack.back().ResolveValue(exe_ctx).IsValid()) + { + if (error_ptr) + error_ptr->SetErrorString("DW_OP_plus_uconst failed."); + return false; + } + } + break; + + //---------------------------------------------------------------------- + // OPCODE: DW_OP_shl + // OPERANDS: none + // DESCRIPTION: pops the top two stack entries, shifts the former + // second entry left by the number of bits specified by the former top + // of the stack, and pushes the result. + //---------------------------------------------------------------------- + case DW_OP_shl: + if (stack.size() < 2) + { + if (error_ptr) + error_ptr->SetErrorString("Expression stack needs at least 2 items for DW_OP_shl."); + return false; + } + else + { + tmp = stack.back(); + stack.pop_back(); + stack.back().ResolveValue(exe_ctx) <<= tmp.ResolveValue(exe_ctx); + } + break; + + //---------------------------------------------------------------------- + // OPCODE: DW_OP_shr + // OPERANDS: none + // DESCRIPTION: pops the top two stack entries, shifts the former second + // entry right logically (filling with zero bits) by the number of bits + // specified by the former top of the stack, and pushes the result. + //---------------------------------------------------------------------- + case DW_OP_shr: + if (stack.size() < 2) + { + if (error_ptr) + error_ptr->SetErrorString("Expression stack needs at least 2 items for DW_OP_shr."); + return false; + } + else + { + tmp = stack.back(); + stack.pop_back(); + if (stack.back().ResolveValue(exe_ctx).ShiftRightLogical(tmp.ResolveValue(exe_ctx)) == false) + { + if (error_ptr) + error_ptr->SetErrorString("DW_OP_shr failed."); + return false; + } + } + break; + + //---------------------------------------------------------------------- + // OPCODE: DW_OP_shra + // OPERANDS: none + // DESCRIPTION: pops the top two stack entries, shifts the former second + // entry right arithmetically (divide the magnitude by 2, keep the same + // sign for the result) by the number of bits specified by the former + // top of the stack, and pushes the result. + //---------------------------------------------------------------------- + case DW_OP_shra: + if (stack.size() < 2) + { + if (error_ptr) + error_ptr->SetErrorString("Expression stack needs at least 2 items for DW_OP_shra."); + return false; + } + else + { + tmp = stack.back(); + stack.pop_back(); + stack.back().ResolveValue(exe_ctx) >>= tmp.ResolveValue(exe_ctx); + } + break; + + //---------------------------------------------------------------------- + // OPCODE: DW_OP_xor + // OPERANDS: none + // DESCRIPTION: pops the top two stack entries, performs the bitwise + // exclusive-or operation on the two, and pushes the result. + //---------------------------------------------------------------------- + case DW_OP_xor: + if (stack.size() < 2) + { + if (error_ptr) + error_ptr->SetErrorString("Expression stack needs at least 2 items for DW_OP_xor."); + return false; + } + else + { + tmp = stack.back(); + stack.pop_back(); + stack.back().ResolveValue(exe_ctx) = stack.back().ResolveValue(exe_ctx) ^ tmp.ResolveValue(exe_ctx); + } + break; + + + //---------------------------------------------------------------------- + // OPCODE: DW_OP_skip + // OPERANDS: int16_t + // DESCRIPTION: An unconditional branch. Its single operand is a 2-byte + // signed integer constant. The 2-byte constant is the number of bytes + // of the DWARF expression to skip forward or backward from the current + // operation, beginning after the 2-byte constant. + //---------------------------------------------------------------------- + case DW_OP_skip: + { + int16_t skip_offset = (int16_t)opcodes.GetU16(&offset); + lldb::offset_t new_offset = offset + skip_offset; + if (new_offset >= opcodes_offset && new_offset < end_offset) + offset = new_offset; + else + { + if (error_ptr) + error_ptr->SetErrorString("Invalid opcode offset in DW_OP_skip."); + return false; + } + } + break; + + //---------------------------------------------------------------------- + // OPCODE: DW_OP_bra + // OPERANDS: int16_t + // DESCRIPTION: A conditional branch. Its single operand is a 2-byte + // signed integer constant. This operation pops the top of stack. If + // the value popped is not the constant 0, the 2-byte constant operand + // is the number of bytes of the DWARF expression to skip forward or + // backward from the current operation, beginning after the 2-byte + // constant. + //---------------------------------------------------------------------- + case DW_OP_bra: + { + tmp = stack.back(); + stack.pop_back(); + int16_t bra_offset = (int16_t)opcodes.GetU16(&offset); + Scalar zero(0); + if (tmp.ResolveValue(exe_ctx) != zero) + { + lldb::offset_t new_offset = offset + bra_offset; + if (new_offset >= opcodes_offset && new_offset < end_offset) + offset = new_offset; + else + { + if (error_ptr) + error_ptr->SetErrorString("Invalid opcode offset in DW_OP_bra."); + return false; + } + } + } + break; + + //---------------------------------------------------------------------- + // OPCODE: DW_OP_eq + // OPERANDS: none + // DESCRIPTION: pops the top two stack values, compares using the + // equals (==) operator. + // STACK RESULT: push the constant value 1 onto the stack if the result + // of the operation is true or the constant value 0 if the result of the + // operation is false. + //---------------------------------------------------------------------- + case DW_OP_eq: + if (stack.size() < 2) + { + if (error_ptr) + error_ptr->SetErrorString("Expression stack needs at least 2 items for DW_OP_eq."); + return false; + } + else + { + tmp = stack.back(); + stack.pop_back(); + stack.back().ResolveValue(exe_ctx) = stack.back().ResolveValue(exe_ctx) == tmp.ResolveValue(exe_ctx); + } + break; + + //---------------------------------------------------------------------- + // OPCODE: DW_OP_ge + // OPERANDS: none + // DESCRIPTION: pops the top two stack values, compares using the + // greater than or equal to (>=) operator. + // STACK RESULT: push the constant value 1 onto the stack if the result + // of the operation is true or the constant value 0 if the result of the + // operation is false. + //---------------------------------------------------------------------- + case DW_OP_ge: + if (stack.size() < 2) + { + if (error_ptr) + error_ptr->SetErrorString("Expression stack needs at least 2 items for DW_OP_ge."); + return false; + } + else + { + tmp = stack.back(); + stack.pop_back(); + stack.back().ResolveValue(exe_ctx) = stack.back().ResolveValue(exe_ctx) >= tmp.ResolveValue(exe_ctx); + } + break; + + //---------------------------------------------------------------------- + // OPCODE: DW_OP_gt + // OPERANDS: none + // DESCRIPTION: pops the top two stack values, compares using the + // greater than (>) operator. + // STACK RESULT: push the constant value 1 onto the stack if the result + // of the operation is true or the constant value 0 if the result of the + // operation is false. + //---------------------------------------------------------------------- + case DW_OP_gt: + if (stack.size() < 2) + { + if (error_ptr) + error_ptr->SetErrorString("Expression stack needs at least 2 items for DW_OP_gt."); + return false; + } + else + { + tmp = stack.back(); + stack.pop_back(); + stack.back().ResolveValue(exe_ctx) = stack.back().ResolveValue(exe_ctx) > tmp.ResolveValue(exe_ctx); + } + break; + + //---------------------------------------------------------------------- + // OPCODE: DW_OP_le + // OPERANDS: none + // DESCRIPTION: pops the top two stack values, compares using the + // less than or equal to (<=) operator. + // STACK RESULT: push the constant value 1 onto the stack if the result + // of the operation is true or the constant value 0 if the result of the + // operation is false. + //---------------------------------------------------------------------- + case DW_OP_le: + if (stack.size() < 2) + { + if (error_ptr) + error_ptr->SetErrorString("Expression stack needs at least 2 items for DW_OP_le."); + return false; + } + else + { + tmp = stack.back(); + stack.pop_back(); + stack.back().ResolveValue(exe_ctx) = stack.back().ResolveValue(exe_ctx) <= tmp.ResolveValue(exe_ctx); + } + break; + + //---------------------------------------------------------------------- + // OPCODE: DW_OP_lt + // OPERANDS: none + // DESCRIPTION: pops the top two stack values, compares using the + // less than (<) operator. + // STACK RESULT: push the constant value 1 onto the stack if the result + // of the operation is true or the constant value 0 if the result of the + // operation is false. + //---------------------------------------------------------------------- + case DW_OP_lt: + if (stack.size() < 2) + { + if (error_ptr) + error_ptr->SetErrorString("Expression stack needs at least 2 items for DW_OP_lt."); + return false; + } + else + { + tmp = stack.back(); + stack.pop_back(); + stack.back().ResolveValue(exe_ctx) = stack.back().ResolveValue(exe_ctx) < tmp.ResolveValue(exe_ctx); + } + break; + + //---------------------------------------------------------------------- + // OPCODE: DW_OP_ne + // OPERANDS: none + // DESCRIPTION: pops the top two stack values, compares using the + // not equal (!=) operator. + // STACK RESULT: push the constant value 1 onto the stack if the result + // of the operation is true or the constant value 0 if the result of the + // operation is false. + //---------------------------------------------------------------------- + case DW_OP_ne: + if (stack.size() < 2) + { + if (error_ptr) + error_ptr->SetErrorString("Expression stack needs at least 2 items for DW_OP_ne."); + return false; + } + else + { + tmp = stack.back(); + stack.pop_back(); + stack.back().ResolveValue(exe_ctx) = stack.back().ResolveValue(exe_ctx) != tmp.ResolveValue(exe_ctx); + } + break; + + //---------------------------------------------------------------------- + // OPCODE: DW_OP_litn + // OPERANDS: none + // DESCRIPTION: encode the unsigned literal values from 0 through 31. + // STACK RESULT: push the unsigned literal constant value onto the top + // of the stack. + //---------------------------------------------------------------------- + case DW_OP_lit0: + case DW_OP_lit1: + case DW_OP_lit2: + case DW_OP_lit3: + case DW_OP_lit4: + case DW_OP_lit5: + case DW_OP_lit6: + case DW_OP_lit7: + case DW_OP_lit8: + case DW_OP_lit9: + case DW_OP_lit10: + case DW_OP_lit11: + case DW_OP_lit12: + case DW_OP_lit13: + case DW_OP_lit14: + case DW_OP_lit15: + case DW_OP_lit16: + case DW_OP_lit17: + case DW_OP_lit18: + case DW_OP_lit19: + case DW_OP_lit20: + case DW_OP_lit21: + case DW_OP_lit22: + case DW_OP_lit23: + case DW_OP_lit24: + case DW_OP_lit25: + case DW_OP_lit26: + case DW_OP_lit27: + case DW_OP_lit28: + case DW_OP_lit29: + case DW_OP_lit30: + case DW_OP_lit31: + stack.push_back(Scalar(op - DW_OP_lit0)); + break; + + //---------------------------------------------------------------------- + // OPCODE: DW_OP_regN + // OPERANDS: none + // DESCRIPTION: Push the value in register n on the top of the stack. + //---------------------------------------------------------------------- + case DW_OP_reg0: + case DW_OP_reg1: + case DW_OP_reg2: + case DW_OP_reg3: + case DW_OP_reg4: + case DW_OP_reg5: + case DW_OP_reg6: + case DW_OP_reg7: + case DW_OP_reg8: + case DW_OP_reg9: + case DW_OP_reg10: + case DW_OP_reg11: + case DW_OP_reg12: + case DW_OP_reg13: + case DW_OP_reg14: + case DW_OP_reg15: + case DW_OP_reg16: + case DW_OP_reg17: + case DW_OP_reg18: + case DW_OP_reg19: + case DW_OP_reg20: + case DW_OP_reg21: + case DW_OP_reg22: + case DW_OP_reg23: + case DW_OP_reg24: + case DW_OP_reg25: + case DW_OP_reg26: + case DW_OP_reg27: + case DW_OP_reg28: + case DW_OP_reg29: + case DW_OP_reg30: + case DW_OP_reg31: + { + reg_num = op - DW_OP_reg0; + + if (ReadRegisterValueAsScalar (reg_ctx, reg_kind, reg_num, error_ptr, tmp)) + stack.push_back(tmp); + else + return false; + } + break; + //---------------------------------------------------------------------- + // OPCODE: DW_OP_regx + // OPERANDS: + // ULEB128 literal operand that encodes the register. + // DESCRIPTION: Push the value in register on the top of the stack. + //---------------------------------------------------------------------- + case DW_OP_regx: + { + reg_num = opcodes.GetULEB128(&offset); + if (ReadRegisterValueAsScalar (reg_ctx, reg_kind, reg_num, error_ptr, tmp)) + stack.push_back(tmp); + else + return false; + } + break; + + //---------------------------------------------------------------------- + // OPCODE: DW_OP_bregN + // OPERANDS: + // SLEB128 offset from register N + // DESCRIPTION: Value is in memory at the address specified by register + // N plus an offset. + //---------------------------------------------------------------------- + case DW_OP_breg0: + case DW_OP_breg1: + case DW_OP_breg2: + case DW_OP_breg3: + case DW_OP_breg4: + case DW_OP_breg5: + case DW_OP_breg6: + case DW_OP_breg7: + case DW_OP_breg8: + case DW_OP_breg9: + case DW_OP_breg10: + case DW_OP_breg11: + case DW_OP_breg12: + case DW_OP_breg13: + case DW_OP_breg14: + case DW_OP_breg15: + case DW_OP_breg16: + case DW_OP_breg17: + case DW_OP_breg18: + case DW_OP_breg19: + case DW_OP_breg20: + case DW_OP_breg21: + case DW_OP_breg22: + case DW_OP_breg23: + case DW_OP_breg24: + case DW_OP_breg25: + case DW_OP_breg26: + case DW_OP_breg27: + case DW_OP_breg28: + case DW_OP_breg29: + case DW_OP_breg30: + case DW_OP_breg31: + { + reg_num = op - DW_OP_breg0; + + if (ReadRegisterValueAsScalar (reg_ctx, reg_kind, reg_num, error_ptr, tmp)) + { + int64_t breg_offset = opcodes.GetSLEB128(&offset); + tmp.ResolveValue(exe_ctx) += (uint64_t)breg_offset; + tmp.ClearContext(); + stack.push_back(tmp); + stack.back().SetValueType (Value::eValueTypeLoadAddress); + } + else + return false; + } + break; + //---------------------------------------------------------------------- + // OPCODE: DW_OP_bregx + // OPERANDS: 2 + // ULEB128 literal operand that encodes the register. + // SLEB128 offset from register N + // DESCRIPTION: Value is in memory at the address specified by register + // N plus an offset. + //---------------------------------------------------------------------- + case DW_OP_bregx: + { + reg_num = opcodes.GetULEB128(&offset); + + if (ReadRegisterValueAsScalar (reg_ctx, reg_kind, reg_num, error_ptr, tmp)) + { + int64_t breg_offset = opcodes.GetSLEB128(&offset); + tmp.ResolveValue(exe_ctx) += (uint64_t)breg_offset; + tmp.ClearContext(); + stack.push_back(tmp); + stack.back().SetValueType (Value::eValueTypeLoadAddress); + } + else + return false; + } + break; + + case DW_OP_fbreg: + if (exe_ctx) + { + if (frame) + { + Scalar value; + if (frame->GetFrameBaseValue(value, error_ptr)) + { + int64_t fbreg_offset = opcodes.GetSLEB128(&offset); + value += fbreg_offset; + stack.push_back(value); + stack.back().SetValueType (Value::eValueTypeLoadAddress); + } + else + return false; + } + else + { + if (error_ptr) + error_ptr->SetErrorString ("Invalid stack frame in context for DW_OP_fbreg opcode."); + return false; + } + } + else + { + if (error_ptr) + error_ptr->SetErrorStringWithFormat ("NULL execution context for DW_OP_fbreg.\n"); + return false; + } + + break; + + //---------------------------------------------------------------------- + // OPCODE: DW_OP_nop + // OPERANDS: none + // DESCRIPTION: A place holder. It has no effect on the location stack + // or any of its values. + //---------------------------------------------------------------------- + case DW_OP_nop: + break; + + //---------------------------------------------------------------------- + // OPCODE: DW_OP_piece + // OPERANDS: 1 + // ULEB128: byte size of the piece + // DESCRIPTION: The operand describes the size in bytes of the piece of + // the object referenced by the DWARF expression whose result is at the + // top of the stack. If the piece is located in a register, but does not + // occupy the entire register, the placement of the piece within that + // register is defined by the ABI. + // + // Many compilers store a single variable in sets of registers, or store + // a variable partially in memory and partially in registers. + // DW_OP_piece provides a way of describing how large a part of a + // variable a particular DWARF expression refers to. + //---------------------------------------------------------------------- + case DW_OP_piece: + if (error_ptr) + error_ptr->SetErrorString ("Unimplemented opcode DW_OP_piece."); + return false; + + //---------------------------------------------------------------------- + // OPCODE: DW_OP_push_object_address + // OPERANDS: none + // DESCRIPTION: Pushes the address of the object currently being + // evaluated as part of evaluation of a user presented expression. + // This object may correspond to an independent variable described by + // its own DIE or it may be a component of an array, structure, or class + // whose address has been dynamically determined by an earlier step + // during user expression evaluation. + //---------------------------------------------------------------------- + case DW_OP_push_object_address: + if (error_ptr) + error_ptr->SetErrorString ("Unimplemented opcode DW_OP_push_object_address."); + return false; + + //---------------------------------------------------------------------- + // OPCODE: DW_OP_call2 + // OPERANDS: + // uint16_t compile unit relative offset of a DIE + // DESCRIPTION: Performs subroutine calls during evaluation + // of a DWARF expression. The operand is the 2-byte unsigned offset + // of a debugging information entry in the current compilation unit. + // + // Operand interpretation is exactly like that for DW_FORM_ref2. + // + // This operation transfers control of DWARF expression evaluation + // to the DW_AT_location attribute of the referenced DIE. If there is + // no such attribute, then there is no effect. Execution of the DWARF + // expression of a DW_AT_location attribute may add to and/or remove from + // values on the stack. Execution returns to the point following the call + // when the end of the attribute is reached. Values on the stack at the + // time of the call may be used as parameters by the called expression + // and values left on the stack by the called expression may be used as + // return values by prior agreement between the calling and called + // expressions. + //---------------------------------------------------------------------- + case DW_OP_call2: + if (error_ptr) + error_ptr->SetErrorString ("Unimplemented opcode DW_OP_call2."); + return false; + //---------------------------------------------------------------------- + // OPCODE: DW_OP_call4 + // OPERANDS: 1 + // uint32_t compile unit relative offset of a DIE + // DESCRIPTION: Performs a subroutine call during evaluation of a DWARF + // expression. For DW_OP_call4, the operand is a 4-byte unsigned offset + // of a debugging information entry in the current compilation unit. + // + // Operand interpretation DW_OP_call4 is exactly like that for + // DW_FORM_ref4. + // + // This operation transfers control of DWARF expression evaluation + // to the DW_AT_location attribute of the referenced DIE. If there is + // no such attribute, then there is no effect. Execution of the DWARF + // expression of a DW_AT_location attribute may add to and/or remove from + // values on the stack. Execution returns to the point following the call + // when the end of the attribute is reached. Values on the stack at the + // time of the call may be used as parameters by the called expression + // and values left on the stack by the called expression may be used as + // return values by prior agreement between the calling and called + // expressions. + //---------------------------------------------------------------------- + case DW_OP_call4: + if (error_ptr) + error_ptr->SetErrorString ("Unimplemented opcode DW_OP_call4."); + return false; + + //---------------------------------------------------------------------- + // OPCODE: DW_OP_stack_value + // OPERANDS: None + // DESCRIPTION: Specifies that the object does not exist in memory but + // rather is a constant value. The value from the top of the stack is + // the value to be used. This is the actual object value and not the + // location. + //---------------------------------------------------------------------- + case DW_OP_stack_value: + stack.back().SetValueType(Value::eValueTypeScalar); + break; + + //---------------------------------------------------------------------- + // OPCODE: DW_OP_call_frame_cfa + // OPERANDS: None + // DESCRIPTION: Specifies a DWARF expression that pushes the value of + // the canonical frame address consistent with the call frame information + // located in .debug_frame (or in the FDEs of the eh_frame section). + //---------------------------------------------------------------------- + case DW_OP_call_frame_cfa: + if (frame) + { + // Note that we don't have to parse FDEs because this DWARF expression + // is commonly evaluated with a valid stack frame. + StackID id = frame->GetStackID(); + addr_t cfa = id.GetCallFrameAddress(); + if (cfa != LLDB_INVALID_ADDRESS) + { + stack.push_back(Scalar(cfa)); + stack.back().SetValueType (Value::eValueTypeHostAddress); + } + else + if (error_ptr) + error_ptr->SetErrorString ("Stack frame does not include a canonical frame address for DW_OP_call_frame_cfa opcode."); + } + else + { + if (error_ptr) + error_ptr->SetErrorString ("Invalid stack frame in context for DW_OP_call_frame_cfa opcode."); + return false; + } + break; + default: + if (log) + log->Printf("Unhandled opcode %s in DWARFExpression.", DW_OP_value_to_name(op)); + break; + } + } + + if (stack.empty()) + { + if (error_ptr) + error_ptr->SetErrorString ("Stack empty after evaluation."); + return false; + } + else if (log && log->GetVerbose()) + { + size_t count = stack.size(); + log->Printf("Stack after operation has %lu values:", count); + for (size_t i=0; iPrintf(" %s", new_value.GetData()); + } + } + + result = stack.back(); + return true; // Return true on success +} + diff --git a/contrib/llvm/tools/lldb/source/Expression/ExpressionSourceCode.cpp b/contrib/llvm/tools/lldb/source/Expression/ExpressionSourceCode.cpp new file mode 100644 index 00000000000..aef3b9e301e --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Expression/ExpressionSourceCode.cpp @@ -0,0 +1,142 @@ +//===-- ExpressionSourceCode.cpp --------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Expression/ExpressionSourceCode.h" + +#include "lldb/Core/StreamString.h" + +using namespace lldb_private; + +const char * +ExpressionSourceCode::g_expression_prefix = R"( +#undef NULL +#undef Nil +#undef nil +#undef YES +#undef NO +#define NULL (__null) +#define Nil (__null) +#define nil (__null) +#define YES ((BOOL)1) +#define NO ((BOOL)0) +typedef signed char BOOL; +typedef signed __INT8_TYPE__ int8_t; +typedef unsigned __INT8_TYPE__ uint8_t; +typedef signed __INT16_TYPE__ int16_t; +typedef unsigned __INT16_TYPE__ uint16_t; +typedef signed __INT32_TYPE__ int32_t; +typedef unsigned __INT32_TYPE__ uint32_t; +typedef signed __INT64_TYPE__ int64_t; +typedef unsigned __INT64_TYPE__ uint64_t; +typedef signed __INTPTR_TYPE__ intptr_t; +typedef unsigned __INTPTR_TYPE__ uintptr_t; +typedef __SIZE_TYPE__ size_t; +typedef __PTRDIFF_TYPE__ ptrdiff_t; +typedef unsigned short unichar; +)"; + + +bool ExpressionSourceCode::GetText (std::string &text, lldb::LanguageType wrapping_language, bool const_object, bool static_method) const +{ + if (m_wrap) + { + switch (wrapping_language) + { + default: + return false; + case lldb::eLanguageTypeC: + case lldb::eLanguageTypeC_plus_plus: + case lldb::eLanguageTypeObjC: + break; + } + + StreamString wrap_stream; + + switch (wrapping_language) + { + default: + break; + case lldb::eLanguageTypeC: + wrap_stream.Printf("%s \n" + "%s \n" + "void \n" + "%s(void *$__lldb_arg) \n" + "{ \n" + " %s; \n" + "} \n", + g_expression_prefix, + m_prefix.c_str(), + m_name.c_str(), + m_body.c_str()); + break; + case lldb::eLanguageTypeC_plus_plus: + wrap_stream.Printf("%s \n" + "%s \n" + "void \n" + "$__lldb_class::%s(void *$__lldb_arg) %s\n" + "{ \n" + " %s; \n" + "} \n", + g_expression_prefix, + m_prefix.c_str(), + m_name.c_str(), + (const_object ? "const" : ""), + m_body.c_str()); + break; + case lldb::eLanguageTypeObjC: + if (static_method) + { + wrap_stream.Printf("%s \n" + "%s \n" + "@interface $__lldb_objc_class ($__lldb_category) \n" + "+(void)%s:(void *)$__lldb_arg; \n" + "@end \n" + "@implementation $__lldb_objc_class ($__lldb_category) \n" + "+(void)%s:(void *)$__lldb_arg \n" + "{ \n" + " %s; \n" + "} \n" + "@end \n", + g_expression_prefix, + m_prefix.c_str(), + m_name.c_str(), + m_name.c_str(), + m_body.c_str()); + } + else + { + wrap_stream.Printf("%s \n" + "%s \n" + "@interface $__lldb_objc_class ($__lldb_category) \n" + "-(void)%s:(void *)$__lldb_arg; \n" + "@end \n" + "@implementation $__lldb_objc_class ($__lldb_category) \n" + "-(void)%s:(void *)$__lldb_arg \n" + "{ \n" + " %s; \n" + "} \n" + "@end \n", + g_expression_prefix, + m_prefix.c_str(), + m_name.c_str(), + m_name.c_str(), + m_body.c_str()); + } + break; + } + + text = wrap_stream.GetString(); + } + else + { + text.append(m_body); + } + + return true; +} diff --git a/contrib/llvm/tools/lldb/source/Expression/IRDynamicChecks.cpp b/contrib/llvm/tools/lldb/source/Expression/IRDynamicChecks.cpp new file mode 100644 index 00000000000..4030f149ab2 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Expression/IRDynamicChecks.cpp @@ -0,0 +1,659 @@ +//===-- IRDynamicChecks.cpp -------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Expression/IRDynamicChecks.h" + +#include "lldb/Core/ConstString.h" +#include "lldb/Core/Log.h" +#include "lldb/Expression/ClangUtilityFunction.h" +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/ObjCLanguageRuntime.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/StackFrame.h" + +#include "llvm/Support/raw_ostream.h" +#include "llvm/IR/Constants.h" +#include "llvm/IR/Function.h" +#include "llvm/IR/Instructions.h" +#include "llvm/IR/Module.h" +#include "llvm/IR/Value.h" + +using namespace llvm; +using namespace lldb_private; + +static char ID; + +#define VALID_POINTER_CHECK_NAME "$__lldb_valid_pointer_check" +#define VALID_OBJC_OBJECT_CHECK_NAME "$__lldb_objc_object_check" + +static const char g_valid_pointer_check_text[] = +"extern \"C\" void\n" +"$__lldb_valid_pointer_check (unsigned char *$__lldb_arg_ptr)\n" +"{\n" +" unsigned char $__lldb_local_val = *$__lldb_arg_ptr;\n" +"}"; + +DynamicCheckerFunctions::DynamicCheckerFunctions () +{ +} + +DynamicCheckerFunctions::~DynamicCheckerFunctions () +{ +} + +bool +DynamicCheckerFunctions::Install(Stream &error_stream, + ExecutionContext &exe_ctx) +{ + m_valid_pointer_check.reset(new ClangUtilityFunction(g_valid_pointer_check_text, + VALID_POINTER_CHECK_NAME)); + if (!m_valid_pointer_check->Install(error_stream, exe_ctx)) + return false; + + Process *process = exe_ctx.GetProcessPtr(); + + if (process) + { + ObjCLanguageRuntime *objc_language_runtime = process->GetObjCLanguageRuntime(); + + if (objc_language_runtime) + { + m_objc_object_check.reset(objc_language_runtime->CreateObjectChecker(VALID_OBJC_OBJECT_CHECK_NAME)); + + if (!m_objc_object_check->Install(error_stream, exe_ctx)) + return false; + } + } + + return true; +} + +bool +DynamicCheckerFunctions::DoCheckersExplainStop (lldb::addr_t addr, Stream &message) +{ + // FIXME: We have to get the checkers to know why they scotched the call in more detail, + // so we can print a better message here. + if (m_valid_pointer_check.get() != NULL && m_valid_pointer_check->ContainsAddress(addr)) + { + message.Printf ("Attempted to dereference an invalid pointer."); + return true; + } + else if (m_objc_object_check.get() != NULL && m_objc_object_check->ContainsAddress(addr)) + { + message.Printf ("Attempted to dereference an invalid ObjC Object or send it an unrecognized selector"); + return true; + } + return false; +} + + +static std::string +PrintValue(llvm::Value *V, bool truncate = false) +{ + std::string s; + raw_string_ostream rso(s); + V->print(rso); + rso.flush(); + if (truncate) + s.resize(s.length() - 1); + return s; +} + +//---------------------------------------------------------------------- +/// @class Instrumenter IRDynamicChecks.cpp +/// @brief Finds and instruments individual LLVM IR instructions +/// +/// When instrumenting LLVM IR, it is frequently desirable to first search +/// for instructions, and then later modify them. This way iterators +/// remain intact, and multiple passes can look at the same code base without +/// treading on each other's toes. +/// +/// The Instrumenter class implements this functionality. A client first +/// calls Inspect on a function, which populates a list of instructions to +/// be instrumented. Then, later, when all passes' Inspect functions have +/// been called, the client calls Instrument, which adds the desired +/// instrumentation. +/// +/// A subclass of Instrumenter must override InstrumentInstruction, which +/// is responsible for adding whatever instrumentation is necessary. +/// +/// A subclass of Instrumenter may override: +/// +/// - InspectInstruction [default: does nothing] +/// +/// - InspectBasicBlock [default: iterates through the instructions in a +/// basic block calling InspectInstruction] +/// +/// - InspectFunction [default: iterates through the basic blocks in a +/// function calling InspectBasicBlock] +//---------------------------------------------------------------------- +class Instrumenter { +public: + //------------------------------------------------------------------ + /// Constructor + /// + /// @param[in] module + /// The module being instrumented. + //------------------------------------------------------------------ + Instrumenter (llvm::Module &module, + DynamicCheckerFunctions &checker_functions) : + m_module(module), + m_checker_functions(checker_functions), + m_i8ptr_ty(NULL) + { + } + + virtual~Instrumenter () + { + } + + //------------------------------------------------------------------ + /// Inspect a function to find instructions to instrument + /// + /// @param[in] function + /// The function to inspect. + /// + /// @return + /// True on success; false on error. + //------------------------------------------------------------------ + bool Inspect (llvm::Function &function) + { + return InspectFunction(function); + } + + //------------------------------------------------------------------ + /// Instrument all the instructions found by Inspect() + /// + /// @return + /// True on success; false on error. + //------------------------------------------------------------------ + bool Instrument () + { + for (InstIterator ii = m_to_instrument.begin(), last_ii = m_to_instrument.end(); + ii != last_ii; + ++ii) + { + if (!InstrumentInstruction(*ii)) + return false; + } + + return true; + } +protected: + //------------------------------------------------------------------ + /// Add instrumentation to a single instruction + /// + /// @param[in] inst + /// The instruction to be instrumented. + /// + /// @return + /// True on success; false otherwise. + //------------------------------------------------------------------ + virtual bool InstrumentInstruction(llvm::Instruction *inst) = 0; + + //------------------------------------------------------------------ + /// Register a single instruction to be instrumented + /// + /// @param[in] inst + /// The instruction to be instrumented. + //------------------------------------------------------------------ + void RegisterInstruction(llvm::Instruction &i) + { + m_to_instrument.push_back(&i); + } + + //------------------------------------------------------------------ + /// Determine whether a single instruction is interesting to + /// instrument, and, if so, call RegisterInstruction + /// + /// @param[in] i + /// The instruction to be inspected. + /// + /// @return + /// False if there was an error scanning; true otherwise. + //------------------------------------------------------------------ + virtual bool InspectInstruction(llvm::Instruction &i) + { + return true; + } + + //------------------------------------------------------------------ + /// Scan a basic block to see if any instructions are interesting + /// + /// @param[in] bb + /// The basic block to be inspected. + /// + /// @return + /// False if there was an error scanning; true otherwise. + //------------------------------------------------------------------ + virtual bool InspectBasicBlock(llvm::BasicBlock &bb) + { + for (llvm::BasicBlock::iterator ii = bb.begin(), last_ii = bb.end(); + ii != last_ii; + ++ii) + { + if (!InspectInstruction(*ii)) + return false; + } + + return true; + } + + //------------------------------------------------------------------ + /// Scan a function to see if any instructions are interesting + /// + /// @param[in] f + /// The function to be inspected. + /// + /// @return + /// False if there was an error scanning; true otherwise. + //------------------------------------------------------------------ + virtual bool InspectFunction(llvm::Function &f) + { + for (llvm::Function::iterator bbi = f.begin(), last_bbi = f.end(); + bbi != last_bbi; + ++bbi) + { + if (!InspectBasicBlock(*bbi)) + return false; + } + + return true; + } + + //------------------------------------------------------------------ + /// Build a function pointer for a function with signature + /// void (*)(uint8_t*) with a given address + /// + /// @param[in] start_address + /// The address of the function. + /// + /// @return + /// The function pointer, for use in a CallInst. + //------------------------------------------------------------------ + llvm::Value *BuildPointerValidatorFunc(lldb::addr_t start_address) + { + IntegerType *intptr_ty = llvm::Type::getIntNTy(m_module.getContext(), + (m_module.getPointerSize() == llvm::Module::Pointer64) ? 64 : 32); + + llvm::Type *param_array[1]; + + param_array[0] = const_cast(GetI8PtrTy()); + + ArrayRef params(param_array, 1); + + FunctionType *fun_ty = FunctionType::get(llvm::Type::getVoidTy(m_module.getContext()), params, true); + PointerType *fun_ptr_ty = PointerType::getUnqual(fun_ty); + Constant *fun_addr_int = ConstantInt::get(intptr_ty, start_address, false); + return ConstantExpr::getIntToPtr(fun_addr_int, fun_ptr_ty); + } + + //------------------------------------------------------------------ + /// Build a function pointer for a function with signature + /// void (*)(uint8_t*, uint8_t*) with a given address + /// + /// @param[in] start_address + /// The address of the function. + /// + /// @return + /// The function pointer, for use in a CallInst. + //------------------------------------------------------------------ + llvm::Value *BuildObjectCheckerFunc(lldb::addr_t start_address) + { + IntegerType *intptr_ty = llvm::Type::getIntNTy(m_module.getContext(), + (m_module.getPointerSize() == llvm::Module::Pointer64) ? 64 : 32); + + llvm::Type *param_array[2]; + + param_array[0] = const_cast(GetI8PtrTy()); + param_array[1] = const_cast(GetI8PtrTy()); + + ArrayRef params(param_array, 2); + + FunctionType *fun_ty = FunctionType::get(llvm::Type::getVoidTy(m_module.getContext()), params, true); + PointerType *fun_ptr_ty = PointerType::getUnqual(fun_ty); + Constant *fun_addr_int = ConstantInt::get(intptr_ty, start_address, false); + return ConstantExpr::getIntToPtr(fun_addr_int, fun_ptr_ty); + } + + PointerType *GetI8PtrTy() + { + if (!m_i8ptr_ty) + m_i8ptr_ty = llvm::Type::getInt8PtrTy(m_module.getContext()); + + return m_i8ptr_ty; + } + + typedef std::vector InstVector; + typedef InstVector::iterator InstIterator; + + InstVector m_to_instrument; ///< List of instructions the inspector found + llvm::Module &m_module; ///< The module which is being instrumented + DynamicCheckerFunctions &m_checker_functions; ///< The dynamic checker functions for the process +private: + PointerType *m_i8ptr_ty; +}; + +class ValidPointerChecker : public Instrumenter +{ +public: + ValidPointerChecker (llvm::Module &module, + DynamicCheckerFunctions &checker_functions) : + Instrumenter(module, checker_functions), + m_valid_pointer_check_func(NULL) + { + } + + virtual ~ValidPointerChecker () + { + } +private: + bool InstrumentInstruction(llvm::Instruction *inst) + { + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + if (log) + log->Printf("Instrumenting load/store instruction: %s\n", + PrintValue(inst).c_str()); + + if (!m_valid_pointer_check_func) + m_valid_pointer_check_func = BuildPointerValidatorFunc(m_checker_functions.m_valid_pointer_check->StartAddress()); + + llvm::Value *dereferenced_ptr = NULL; + + if (llvm::LoadInst *li = dyn_cast (inst)) + dereferenced_ptr = li->getPointerOperand(); + else if (llvm::StoreInst *si = dyn_cast (inst)) + dereferenced_ptr = si->getPointerOperand(); + else + return false; + + // Insert an instruction to cast the loaded value to int8_t* + + BitCastInst *bit_cast = new BitCastInst(dereferenced_ptr, + GetI8PtrTy(), + "", + inst); + + // Insert an instruction to call the helper with the result + + llvm::Value *arg_array[1]; + + arg_array[0] = bit_cast; + + llvm::ArrayRef args(arg_array, 1); + + CallInst::Create(m_valid_pointer_check_func, + args, + "", + inst); + + return true; + } + + bool InspectInstruction(llvm::Instruction &i) + { + if (dyn_cast (&i) || + dyn_cast (&i)) + RegisterInstruction(i); + + return true; + } + + llvm::Value *m_valid_pointer_check_func; +}; + +class ObjcObjectChecker : public Instrumenter +{ +public: + ObjcObjectChecker(llvm::Module &module, + DynamicCheckerFunctions &checker_functions) : + Instrumenter(module, checker_functions), + m_objc_object_check_func(NULL) + { + } + + virtual + ~ObjcObjectChecker () + { + } + + enum msgSend_type + { + eMsgSend = 0, + eMsgSendSuper, + eMsgSendSuper_stret, + eMsgSend_fpret, + eMsgSend_stret + }; + + std::map msgSend_types; + +private: + bool InstrumentInstruction(llvm::Instruction *inst) + { + CallInst *call_inst = dyn_cast(inst); + + if (!call_inst) + return false; // call_inst really shouldn't be NULL, because otherwise InspectInstruction wouldn't have registered it + + if (!m_objc_object_check_func) + m_objc_object_check_func = BuildObjectCheckerFunc(m_checker_functions.m_objc_object_check->StartAddress()); + + // id objc_msgSend(id theReceiver, SEL theSelector, ...) + + llvm::Value *target_object; + llvm::Value *selector; + + switch (msgSend_types[inst]) + { + case eMsgSend: + case eMsgSend_fpret: + target_object = call_inst->getArgOperand(0); + selector = call_inst->getArgOperand(1); + break; + case eMsgSend_stret: + target_object = call_inst->getArgOperand(1); + selector = call_inst->getArgOperand(2); + break; + case eMsgSendSuper: + case eMsgSendSuper_stret: + return true; + } + + // These objects should always be valid according to Sean Calannan + assert (target_object); + assert (selector); + + // Insert an instruction to cast the receiver id to int8_t* + + BitCastInst *bit_cast = new BitCastInst(target_object, + GetI8PtrTy(), + "", + inst); + + // Insert an instruction to call the helper with the result + + llvm::Value *arg_array[2]; + + arg_array[0] = bit_cast; + arg_array[1] = selector; + + ArrayRef args(arg_array, 2); + + CallInst::Create(m_objc_object_check_func, + args, + "", + inst); + + return true; + } + + bool InspectInstruction(llvm::Instruction &i) + { + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + CallInst *call_inst = dyn_cast(&i); + + if (call_inst) + { + // This metadata is set by IRForTarget::MaybeHandleCall(). + + MDNode *metadata = call_inst->getMetadata("lldb.call.realName"); + + if (!metadata) + return true; + + if (metadata->getNumOperands() != 1) + { + if (log) + log->Printf("Function call metadata has %d operands for [%p] %s", metadata->getNumOperands(), call_inst, PrintValue(call_inst).c_str()); + return false; + } + + MDString *real_name = dyn_cast(metadata->getOperand(0)); + + if (!real_name) + { + if (log) + log->Printf("Function call metadata is not an MDString for [%p] %s", call_inst, PrintValue(call_inst).c_str()); + return false; + } + + std::string name_str = real_name->getString(); + const char* name_cstr = name_str.c_str(); + + if (log) + log->Printf("Found call to %s: %s\n", name_cstr, PrintValue(call_inst).c_str()); + + if (name_str.find("objc_msgSend") == std::string::npos) + return true; + + if (!strcmp(name_cstr, "objc_msgSend")) + { + RegisterInstruction(i); + msgSend_types[&i] = eMsgSend; + return true; + } + + if (!strcmp(name_cstr, "objc_msgSend_stret")) + { + RegisterInstruction(i); + msgSend_types[&i] = eMsgSend_stret; + return true; + } + + if (!strcmp(name_cstr, "objc_msgSend_fpret")) + { + RegisterInstruction(i); + msgSend_types[&i] = eMsgSend_fpret; + return true; + } + + if (!strcmp(name_cstr, "objc_msgSendSuper")) + { + RegisterInstruction(i); + msgSend_types[&i] = eMsgSendSuper; + return true; + } + + if (!strcmp(name_cstr, "objc_msgSendSuper_stret")) + { + RegisterInstruction(i); + msgSend_types[&i] = eMsgSendSuper_stret; + return true; + } + + if (log) + log->Printf("Function name '%s' contains 'objc_msgSend' but is not handled", name_str.c_str()); + + return true; + } + + return true; + } + + llvm::Value *m_objc_object_check_func; +}; + +IRDynamicChecks::IRDynamicChecks(DynamicCheckerFunctions &checker_functions, + const char *func_name) : + ModulePass(ID), + m_func_name(func_name), + m_checker_functions(checker_functions) +{ +} + +IRDynamicChecks::~IRDynamicChecks() +{ +} + +bool +IRDynamicChecks::runOnModule(llvm::Module &M) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + llvm::Function* function = M.getFunction(StringRef(m_func_name.c_str())); + + if (!function) + { + if (log) + log->Printf("Couldn't find %s() in the module", m_func_name.c_str()); + + return false; + } + + if (m_checker_functions.m_valid_pointer_check.get()) + { + ValidPointerChecker vpc(M, m_checker_functions); + + if (!vpc.Inspect(*function)) + return false; + + if (!vpc.Instrument()) + return false; + } + + if (m_checker_functions.m_objc_object_check.get()) + { + ObjcObjectChecker ooc(M, m_checker_functions); + + if (!ooc.Inspect(*function)) + return false; + + if (!ooc.Instrument()) + return false; + } + + if (log && log->GetVerbose()) + { + std::string s; + raw_string_ostream oss(s); + + M.print(oss, NULL); + + oss.flush(); + + log->Printf ("Module after dynamic checks: \n%s", s.c_str()); + } + + return true; +} + +void +IRDynamicChecks::assignPassManager(PMStack &PMS, + PassManagerType T) +{ +} + +PassManagerType +IRDynamicChecks::getPotentialPassManagerType() const +{ + return PMT_ModulePassManager; +} diff --git a/contrib/llvm/tools/lldb/source/Expression/IRExecutionUnit.cpp b/contrib/llvm/tools/lldb/source/Expression/IRExecutionUnit.cpp new file mode 100644 index 00000000000..a2b25948a58 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Expression/IRExecutionUnit.cpp @@ -0,0 +1,726 @@ +//===-- IRExecutionUnit.cpp -------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// C Includes +// C++ Includes +// Other libraries and framework includes +#include "llvm/ExecutionEngine/ExecutionEngine.h" +#include "llvm/IR/LLVMContext.h" +#include "llvm/IR/Module.h" +#include "llvm/Support/SourceMgr.h" +// Project includes +#include "lldb/Core/DataBufferHeap.h" +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/Disassembler.h" +#include "lldb/Core/Log.h" +#include "lldb/Expression/IRExecutionUnit.h" +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/Target.h" + +using namespace lldb_private; + +IRExecutionUnit::IRExecutionUnit (std::unique_ptr &context_ap, + std::unique_ptr &module_ap, + ConstString &name, + const lldb::TargetSP &target_sp, + std::vector &cpu_features) : + IRMemoryMap(target_sp), + m_context_ap(context_ap.release()), + m_module_ap(module_ap.release()), + m_module(m_module_ap.get()), + m_cpu_features(cpu_features), + m_name(name), + m_did_jit(false), + m_function_load_addr(LLDB_INVALID_ADDRESS), + m_function_end_load_addr(LLDB_INVALID_ADDRESS) +{ +} + +lldb::addr_t +IRExecutionUnit::WriteNow (const uint8_t *bytes, + size_t size, + Error &error) +{ + lldb::addr_t allocation_process_addr = Malloc (size, + 8, + lldb::ePermissionsWritable | lldb::ePermissionsReadable, + eAllocationPolicyMirror, + error); + + if (!error.Success()) + return LLDB_INVALID_ADDRESS; + + WriteMemory(allocation_process_addr, bytes, size, error); + + if (!error.Success()) + { + Error err; + Free (allocation_process_addr, err); + + return LLDB_INVALID_ADDRESS; + } + + if (Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)) + { + DataBufferHeap my_buffer(size, 0); + Error err; + ReadMemory(my_buffer.GetBytes(), allocation_process_addr, size, err); + + if (err.Success()) + { + DataExtractor my_extractor(my_buffer.GetBytes(), my_buffer.GetByteSize(), lldb::eByteOrderBig, 8); + + StreamString ss; + + my_extractor.Dump(&ss, 0, lldb::eFormatBytesWithASCII, 1, my_buffer.GetByteSize(), 32, allocation_process_addr, 0, 0); + + log->PutCString(ss.GetData()); + } + } + + return allocation_process_addr; +} + +void +IRExecutionUnit::FreeNow (lldb::addr_t allocation) +{ + if (allocation == LLDB_INVALID_ADDRESS) + return; + + Error err; + + Free(allocation, err); +} + +Error +IRExecutionUnit::DisassembleFunction (Stream &stream, + lldb::ProcessSP &process_wp) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + ExecutionContext exe_ctx(process_wp); + + Error ret; + + ret.Clear(); + + lldb::addr_t func_local_addr = LLDB_INVALID_ADDRESS; + lldb::addr_t func_remote_addr = LLDB_INVALID_ADDRESS; + + for (JittedFunction &function : m_jitted_functions) + { + if (strstr(function.m_name.c_str(), m_name.AsCString())) + { + func_local_addr = function.m_local_addr; + func_remote_addr = function.m_remote_addr; + } + } + + if (func_local_addr == LLDB_INVALID_ADDRESS) + { + ret.SetErrorToGenericError(); + ret.SetErrorStringWithFormat("Couldn't find function %s for disassembly", m_name.AsCString()); + return ret; + } + + if (log) + log->Printf("Found function, has local address 0x%" PRIx64 " and remote address 0x%" PRIx64, (uint64_t)func_local_addr, (uint64_t)func_remote_addr); + + std::pair func_range; + + func_range = GetRemoteRangeForLocal(func_local_addr); + + if (func_range.first == 0 && func_range.second == 0) + { + ret.SetErrorToGenericError(); + ret.SetErrorStringWithFormat("Couldn't find code range for function %s", m_name.AsCString()); + return ret; + } + + if (log) + log->Printf("Function's code range is [0x%" PRIx64 "+0x%" PRIx64 "]", func_range.first, func_range.second); + + Target *target = exe_ctx.GetTargetPtr(); + if (!target) + { + ret.SetErrorToGenericError(); + ret.SetErrorString("Couldn't find the target"); + return ret; + } + + lldb::DataBufferSP buffer_sp(new DataBufferHeap(func_range.second, 0)); + + Process *process = exe_ctx.GetProcessPtr(); + Error err; + process->ReadMemory(func_remote_addr, buffer_sp->GetBytes(), buffer_sp->GetByteSize(), err); + + if (!err.Success()) + { + ret.SetErrorToGenericError(); + ret.SetErrorStringWithFormat("Couldn't read from process: %s", err.AsCString("unknown error")); + return ret; + } + + ArchSpec arch(target->GetArchitecture()); + + const char *plugin_name = NULL; + const char *flavor_string = NULL; + lldb::DisassemblerSP disassembler_sp = Disassembler::FindPlugin(arch, flavor_string, plugin_name); + + if (!disassembler_sp) + { + ret.SetErrorToGenericError(); + ret.SetErrorStringWithFormat("Unable to find disassembler plug-in for %s architecture.", arch.GetArchitectureName()); + return ret; + } + + if (!process) + { + ret.SetErrorToGenericError(); + ret.SetErrorString("Couldn't find the process"); + return ret; + } + + DataExtractor extractor(buffer_sp, + process->GetByteOrder(), + target->GetArchitecture().GetAddressByteSize()); + + if (log) + { + log->Printf("Function data has contents:"); + extractor.PutToLog (log, + 0, + extractor.GetByteSize(), + func_remote_addr, + 16, + DataExtractor::TypeUInt8); + } + + disassembler_sp->DecodeInstructions (Address (func_remote_addr), extractor, 0, UINT32_MAX, false, false); + + InstructionList &instruction_list = disassembler_sp->GetInstructionList(); + const uint32_t max_opcode_byte_size = instruction_list.GetMaxOpcocdeByteSize(); + + for (size_t instruction_index = 0, num_instructions = instruction_list.GetSize(); + instruction_index < num_instructions; + ++instruction_index) + { + Instruction *instruction = instruction_list.GetInstructionAtIndex(instruction_index).get(); + instruction->Dump (&stream, + max_opcode_byte_size, + true, + true, + &exe_ctx); + stream.PutChar('\n'); + } + // FIXME: The DisassemblerLLVMC has a reference cycle and won't go away if it has any active instructions. + // I'll fix that but for now, just clear the list and it will go away nicely. + disassembler_sp->GetInstructionList().Clear(); + return ret; +} + +static void ReportInlineAsmError(const llvm::SMDiagnostic &diagnostic, void *Context, unsigned LocCookie) +{ + Error *err = static_cast(Context); + + if (err && err->Success()) + { + err->SetErrorToGenericError(); + err->SetErrorStringWithFormat("Inline assembly error: %s", diagnostic.getMessage().str().c_str()); + } +} + +void +IRExecutionUnit::GetRunnableInfo(Error &error, + lldb::addr_t &func_addr, + lldb::addr_t &func_end) +{ + lldb::ProcessSP process_sp(GetProcessWP().lock()); + + func_addr = LLDB_INVALID_ADDRESS; + func_end = LLDB_INVALID_ADDRESS; + + if (!process_sp) + { + error.SetErrorToGenericError(); + error.SetErrorString("Couldn't write the JIT compiled code into the process because the process is invalid"); + return; + } + + if (m_did_jit) + { + func_addr = m_function_load_addr; + func_end = m_function_end_load_addr; + + return; + }; + + m_did_jit = true; + + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + std::string error_string; + + if (log) + { + std::string s; + llvm::raw_string_ostream oss(s); + + m_module->print(oss, NULL); + + oss.flush(); + + log->Printf ("Module being sent to JIT: \n%s", s.c_str()); + } + + llvm::Triple triple(m_module->getTargetTriple()); + llvm::Function *function = m_module->getFunction (m_name.AsCString()); + llvm::Reloc::Model relocModel; + llvm::CodeModel::Model codeModel; + + if (triple.isOSBinFormatELF()) + { + relocModel = llvm::Reloc::Static; + // This will be small for 32-bit and large for 64-bit. + codeModel = llvm::CodeModel::JITDefault; + } + else + { + relocModel = llvm::Reloc::PIC_; + codeModel = llvm::CodeModel::Small; + } + + m_module_ap->getContext().setInlineAsmDiagnosticHandler(ReportInlineAsmError, &error); + + llvm::EngineBuilder builder(m_module_ap.get()); + + builder.setEngineKind(llvm::EngineKind::JIT) + .setErrorStr(&error_string) + .setRelocationModel(relocModel) + .setJITMemoryManager(new MemoryManager(*this)) + .setOptLevel(llvm::CodeGenOpt::Less) + .setAllocateGVsWithCode(true) + .setCodeModel(codeModel) + .setUseMCJIT(true); + + llvm::StringRef mArch; + llvm::StringRef mCPU; + llvm::SmallVector mAttrs; + + for (std::string &feature : m_cpu_features) + mAttrs.push_back(feature); + + llvm::TargetMachine *target_machine = builder.selectTarget(triple, + mArch, + mCPU, + mAttrs); + + m_execution_engine_ap.reset(builder.create(target_machine)); + + if (!m_execution_engine_ap.get()) + { + error.SetErrorToGenericError(); + error.SetErrorStringWithFormat("Couldn't JIT the function: %s", error_string.c_str()); + return; + } + else + { + m_module_ap.release(); // ownership was transferred + } + + m_execution_engine_ap->DisableLazyCompilation(); + + // We don't actually need the function pointer here, this just forces it to get resolved. + + void *fun_ptr = m_execution_engine_ap->getPointerToFunction(function); + + if (!error.Success()) + { + // We got an error through our callback! + return; + } + + if (!function) + { + error.SetErrorToGenericError(); + error.SetErrorStringWithFormat("Couldn't find '%s' in the JITted module", m_name.AsCString()); + return; + } + + if (!fun_ptr) + { + error.SetErrorToGenericError(); + error.SetErrorStringWithFormat("'%s' was in the JITted module but wasn't lowered", m_name.AsCString()); + return; + } + + m_jitted_functions.push_back (JittedFunction(m_name.AsCString(), (lldb::addr_t)fun_ptr)); + + CommitAllocations(process_sp); + ReportAllocations(*m_execution_engine_ap); + WriteData(process_sp); + + for (JittedFunction &jitted_function : m_jitted_functions) + { + jitted_function.m_remote_addr = GetRemoteAddressForLocal (jitted_function.m_local_addr); + + if (!jitted_function.m_name.compare(m_name.AsCString())) + { + AddrRange func_range = GetRemoteRangeForLocal(jitted_function.m_local_addr); + m_function_end_load_addr = func_range.first + func_range.second; + m_function_load_addr = jitted_function.m_remote_addr; + } + } + + if (log) + { + log->Printf("Code can be run in the target."); + + StreamString disassembly_stream; + + Error err = DisassembleFunction(disassembly_stream, process_sp); + + if (!err.Success()) + { + log->Printf("Couldn't disassemble function : %s", err.AsCString("unknown error")); + } + else + { + log->Printf("Function disassembly:\n%s", disassembly_stream.GetData()); + } + } + + func_addr = m_function_load_addr; + func_end = m_function_end_load_addr; + + return; +} + +IRExecutionUnit::~IRExecutionUnit () +{ + m_module_ap.reset(); + m_execution_engine_ap.reset(); + m_context_ap.reset(); +} + +IRExecutionUnit::MemoryManager::MemoryManager (IRExecutionUnit &parent) : + m_default_mm_ap (llvm::JITMemoryManager::CreateDefaultMemManager()), + m_parent (parent) +{ +} + +void +IRExecutionUnit::MemoryManager::setMemoryWritable () +{ + m_default_mm_ap->setMemoryWritable(); +} + +void +IRExecutionUnit::MemoryManager::setMemoryExecutable () +{ + m_default_mm_ap->setMemoryExecutable(); +} + + +uint8_t * +IRExecutionUnit::MemoryManager::startFunctionBody(const llvm::Function *F, + uintptr_t &ActualSize) +{ + return m_default_mm_ap->startFunctionBody(F, ActualSize); +} + +uint8_t * +IRExecutionUnit::MemoryManager::allocateStub(const llvm::GlobalValue* F, + unsigned StubSize, + unsigned Alignment) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + uint8_t *return_value = m_default_mm_ap->allocateStub(F, StubSize, Alignment); + + m_parent.m_records.push_back(AllocationRecord((uintptr_t)return_value, + lldb::ePermissionsReadable | lldb::ePermissionsWritable, + StubSize, + Alignment)); + + if (log) + { + log->Printf("IRExecutionUnit::allocateStub (F=%p, StubSize=%u, Alignment=%u) = %p", + F, StubSize, Alignment, return_value); + } + + return return_value; +} + +void +IRExecutionUnit::MemoryManager::endFunctionBody(const llvm::Function *F, + uint8_t *FunctionStart, + uint8_t *FunctionEnd) +{ + m_default_mm_ap->endFunctionBody(F, FunctionStart, FunctionEnd); +} + +uint8_t * +IRExecutionUnit::MemoryManager::allocateSpace(intptr_t Size, unsigned Alignment) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + uint8_t *return_value = m_default_mm_ap->allocateSpace(Size, Alignment); + + m_parent.m_records.push_back(AllocationRecord((uintptr_t)return_value, + lldb::ePermissionsReadable | lldb::ePermissionsWritable, + Size, + Alignment)); + + if (log) + { + log->Printf("IRExecutionUnit::allocateSpace(Size=%" PRIu64 ", Alignment=%u) = %p", + (uint64_t)Size, Alignment, return_value); + } + + return return_value; +} + +uint8_t * +IRExecutionUnit::MemoryManager::allocateCodeSection(uintptr_t Size, + unsigned Alignment, + unsigned SectionID) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + uint8_t *return_value = m_default_mm_ap->allocateCodeSection(Size, Alignment, SectionID); + + m_parent.m_records.push_back(AllocationRecord((uintptr_t)return_value, + lldb::ePermissionsReadable | lldb::ePermissionsExecutable, + Size, + Alignment, + SectionID)); + + if (log) + { + log->Printf("IRExecutionUnit::allocateCodeSection(Size=0x%" PRIx64 ", Alignment=%u, SectionID=%u) = %p", + (uint64_t)Size, Alignment, SectionID, return_value); + } + + return return_value; +} + +uint8_t * +IRExecutionUnit::MemoryManager::allocateDataSection(uintptr_t Size, + unsigned Alignment, + unsigned SectionID, + bool IsReadOnly) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + uint8_t *return_value = m_default_mm_ap->allocateDataSection(Size, Alignment, SectionID, IsReadOnly); + + m_parent.m_records.push_back(AllocationRecord((uintptr_t)return_value, + lldb::ePermissionsReadable | lldb::ePermissionsWritable, + Size, + Alignment, + SectionID)); + if (log) + { + log->Printf("IRExecutionUnit::allocateDataSection(Size=0x%" PRIx64 ", Alignment=%u, SectionID=%u) = %p", + (uint64_t)Size, Alignment, SectionID, return_value); + } + + return return_value; +} + +uint8_t * +IRExecutionUnit::MemoryManager::allocateGlobal(uintptr_t Size, + unsigned Alignment) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + uint8_t *return_value = m_default_mm_ap->allocateGlobal(Size, Alignment); + + m_parent.m_records.push_back(AllocationRecord((uintptr_t)return_value, + lldb::ePermissionsReadable | lldb::ePermissionsWritable, + Size, + Alignment)); + + if (log) + { + log->Printf("IRExecutionUnit::allocateGlobal(Size=0x%" PRIx64 ", Alignment=%u) = %p", + (uint64_t)Size, Alignment, return_value); + } + + return return_value; +} + +void +IRExecutionUnit::MemoryManager::deallocateFunctionBody(void *Body) +{ + m_default_mm_ap->deallocateFunctionBody(Body); +} + +uint8_t* +IRExecutionUnit::MemoryManager::startExceptionTable(const llvm::Function* F, + uintptr_t &ActualSize) +{ + return m_default_mm_ap->startExceptionTable(F, ActualSize); +} + +void +IRExecutionUnit::MemoryManager::endExceptionTable(const llvm::Function *F, + uint8_t *TableStart, + uint8_t *TableEnd, + uint8_t* FrameRegister) +{ + m_default_mm_ap->endExceptionTable(F, TableStart, TableEnd, FrameRegister); +} + +void +IRExecutionUnit::MemoryManager::deallocateExceptionTable(void *ET) +{ + m_default_mm_ap->deallocateExceptionTable (ET); +} + +lldb::addr_t +IRExecutionUnit::GetRemoteAddressForLocal (lldb::addr_t local_address) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + for (AllocationRecord &record : m_records) + { + if (local_address >= record.m_host_address && + local_address < record.m_host_address + record.m_size) + { + if (record.m_process_address == LLDB_INVALID_ADDRESS) + return LLDB_INVALID_ADDRESS; + + lldb::addr_t ret = record.m_process_address + (local_address - record.m_host_address); + + if (log) + { + log->Printf("IRExecutionUnit::GetRemoteAddressForLocal() found 0x%" PRIx64 " in [0x%" PRIx64 "..0x%" PRIx64 "], and returned 0x%" PRIx64 " from [0x%" PRIx64 "..0x%" PRIx64 "].", + local_address, + (uint64_t)record.m_host_address, + (uint64_t)record.m_host_address + (uint64_t)record.m_size, + ret, + record.m_process_address, + record.m_process_address + record.m_size); + } + + return ret; + } + } + + return LLDB_INVALID_ADDRESS; +} + +IRExecutionUnit::AddrRange +IRExecutionUnit::GetRemoteRangeForLocal (lldb::addr_t local_address) +{ + for (AllocationRecord &record : m_records) + { + if (local_address >= record.m_host_address && + local_address < record.m_host_address + record.m_size) + { + if (record.m_process_address == LLDB_INVALID_ADDRESS) + return AddrRange(0, 0); + + return AddrRange(record.m_process_address, record.m_size); + } + } + + return AddrRange (0, 0); +} + +bool +IRExecutionUnit::CommitAllocations (lldb::ProcessSP &process_sp) +{ + bool ret = true; + + lldb_private::Error err; + + for (AllocationRecord &record : m_records) + { + if (record.m_process_address != LLDB_INVALID_ADDRESS) + continue; + + + record.m_process_address = Malloc(record.m_size, + record.m_alignment, + record.m_permissions, + eAllocationPolicyProcessOnly, + err); + + if (!err.Success()) + { + ret = false; + break; + } + } + + if (!ret) + { + for (AllocationRecord &record : m_records) + { + if (record.m_process_address != LLDB_INVALID_ADDRESS) + { + Free(record.m_process_address, err); + record.m_process_address = LLDB_INVALID_ADDRESS; + } + } + } + + return ret; +} + +void +IRExecutionUnit::ReportAllocations (llvm::ExecutionEngine &engine) +{ + for (AllocationRecord &record : m_records) + { + if (record.m_process_address == LLDB_INVALID_ADDRESS) + continue; + + if (record.m_section_id == eSectionIDInvalid) + continue; + + engine.mapSectionAddress((void*)record.m_host_address, record.m_process_address); + } + + // Trigger re-application of relocations. + engine.finalizeObject(); +} + +bool +IRExecutionUnit::WriteData (lldb::ProcessSP &process_sp) +{ + for (AllocationRecord &record : m_records) + { + if (record.m_process_address == LLDB_INVALID_ADDRESS) + return false; + + lldb_private::Error err; + + WriteMemory (record.m_process_address, (uint8_t*)record.m_host_address, record.m_size, err); + } + + return true; +} + +void +IRExecutionUnit::AllocationRecord::dump (Log *log) +{ + if (!log) + return; + + log->Printf("[0x%llx+0x%llx]->0x%llx (alignment %d, section ID %d)", + (unsigned long long)m_host_address, + (unsigned long long)m_size, + (unsigned long long)m_process_address, + (unsigned)m_alignment, + (unsigned)m_section_id); +} diff --git a/contrib/llvm/tools/lldb/source/Expression/IRForTarget.cpp b/contrib/llvm/tools/lldb/source/Expression/IRForTarget.cpp new file mode 100644 index 00000000000..dc27b65548e --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Expression/IRForTarget.cpp @@ -0,0 +1,2865 @@ +//===-- IRForTarget.cpp -----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Expression/IRForTarget.h" + +#include "llvm/Support/raw_ostream.h" +#include "llvm/IR/Constants.h" +#include "llvm/IR/DataLayout.h" +#include "llvm/IR/InstrTypes.h" +#include "llvm/IR/Instructions.h" +#include "llvm/IR/Intrinsics.h" +#include "llvm/IR/Module.h" +#include "llvm/PassManager.h" +#include "llvm/Transforms/IPO.h" +#include "llvm/IR/ValueSymbolTable.h" + +#include "clang/AST/ASTContext.h" + +#include "lldb/Core/dwarf.h" +#include "lldb/Core/ConstString.h" +#include "lldb/Core/DataBufferHeap.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Scalar.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Expression/ClangExpressionDeclMap.h" +#include "lldb/Expression/IRExecutionUnit.h" +#include "lldb/Expression/IRInterpreter.h" +#include "lldb/Host/Endian.h" +#include "lldb/Symbol/ClangASTContext.h" +#include "lldb/Symbol/ClangASTType.h" + +#include + +using namespace llvm; + +static char ID; + +IRForTarget::StaticDataAllocator::StaticDataAllocator(lldb_private::IRExecutionUnit &execution_unit) : + m_execution_unit(execution_unit), + m_stream_string(lldb_private::Stream::eBinary, execution_unit.GetAddressByteSize(), execution_unit.GetByteOrder()), + m_allocation(LLDB_INVALID_ADDRESS) +{ +} + +IRForTarget::FunctionValueCache::FunctionValueCache(Maker const &maker) : + m_maker(maker), + m_values() +{ +} + +IRForTarget::FunctionValueCache::~FunctionValueCache() +{ +} + +llvm::Value *IRForTarget::FunctionValueCache::GetValue(llvm::Function *function) +{ + if (!m_values.count(function)) + { + llvm::Value *ret = m_maker(function); + m_values[function] = ret; + return ret; + } + return m_values[function]; +} + +lldb::addr_t IRForTarget::StaticDataAllocator::Allocate() +{ + lldb_private::Error err; + + if (m_allocation != LLDB_INVALID_ADDRESS) + { + m_execution_unit.FreeNow(m_allocation); + m_allocation = LLDB_INVALID_ADDRESS; + } + + m_allocation = m_execution_unit.WriteNow((const uint8_t*)m_stream_string.GetData(), m_stream_string.GetSize(), err); + + return m_allocation; +} + +static llvm::Value *FindEntryInstruction (llvm::Function *function) +{ + if (function->empty()) + return NULL; + + return function->getEntryBlock().getFirstNonPHIOrDbg(); +} + +IRForTarget::IRForTarget (lldb_private::ClangExpressionDeclMap *decl_map, + bool resolve_vars, + lldb_private::IRExecutionUnit &execution_unit, + lldb_private::Stream *error_stream, + const char *func_name) : + ModulePass(ID), + m_resolve_vars(resolve_vars), + m_func_name(func_name), + m_module(NULL), + m_decl_map(decl_map), + m_data_allocator(execution_unit), + m_CFStringCreateWithBytes(NULL), + m_sel_registerName(NULL), + m_error_stream(error_stream), + m_result_store(NULL), + m_result_is_pointer(false), + m_reloc_placeholder(NULL), + m_entry_instruction_finder (FindEntryInstruction) +{ +} + +/* Handy utility functions used at several places in the code */ + +static std::string +PrintValue(const Value *value, bool truncate = false) +{ + std::string s; + if (value) + { + raw_string_ostream rso(s); + value->print(rso); + rso.flush(); + if (truncate) + s.resize(s.length() - 1); + } + return s; +} + +static std::string +PrintType(const llvm::Type *type, bool truncate = false) +{ + std::string s; + raw_string_ostream rso(s); + type->print(rso); + rso.flush(); + if (truncate) + s.resize(s.length() - 1); + return s; +} + +IRForTarget::~IRForTarget() +{ +} + +bool +IRForTarget::FixFunctionLinkage(llvm::Function &llvm_function) +{ + llvm_function.setLinkage(GlobalValue::ExternalLinkage); + + std::string name = llvm_function.getName().str(); + + return true; +} + +bool +IRForTarget::GetFunctionAddress (llvm::Function *fun, + uint64_t &fun_addr, + lldb_private::ConstString &name, + Constant **&value_ptr) +{ + lldb_private::Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + fun_addr = LLDB_INVALID_ADDRESS; + name.Clear(); + value_ptr = NULL; + + if (fun->isIntrinsic()) + { + Intrinsic::ID intrinsic_id = (Intrinsic::ID)fun->getIntrinsicID(); + + switch (intrinsic_id) + { + default: + if (log) + log->Printf("Unresolved intrinsic \"%s\"", Intrinsic::getName(intrinsic_id).c_str()); + + if (m_error_stream) + m_error_stream->Printf("Internal error [IRForTarget]: Call to unhandled compiler intrinsic '%s'\n", Intrinsic::getName(intrinsic_id).c_str()); + + return false; + case Intrinsic::memcpy: + { + static lldb_private::ConstString g_memcpy_str ("memcpy"); + name = g_memcpy_str; + } + break; + case Intrinsic::memset: + { + static lldb_private::ConstString g_memset_str ("memset"); + name = g_memset_str; + } + break; + } + + if (log && name) + log->Printf("Resolved intrinsic name \"%s\"", name.GetCString()); + } + else + { + name.SetCStringWithLength (fun->getName().data(), fun->getName().size()); + } + + // Find the address of the function. + + clang::NamedDecl *fun_decl = DeclForGlobal (fun); + + if (fun_decl) + { + if (!m_decl_map->GetFunctionInfo (fun_decl, fun_addr)) + { + lldb_private::ConstString altnernate_name; + bool found_it = m_decl_map->GetFunctionAddress (name, fun_addr); + if (!found_it) + { + // Check for an alternate mangling for "std::basic_string" + // that is part of the itanium C++ name mangling scheme + const char *name_cstr = name.GetCString(); + if (name_cstr && strncmp(name_cstr, "_ZNKSbIcE", strlen("_ZNKSbIcE")) == 0) + { + std::string alternate_mangling("_ZNKSs"); + alternate_mangling.append (name_cstr + strlen("_ZNKSbIcE")); + altnernate_name.SetCString(alternate_mangling.c_str()); + found_it = m_decl_map->GetFunctionAddress (altnernate_name, fun_addr); + } + } + + if (!found_it) + { + lldb_private::Mangled mangled_name(name); + lldb_private::Mangled alt_mangled_name(altnernate_name); + if (log) + { + if (alt_mangled_name) + log->Printf("Function \"%s\" (alternate name \"%s\") has no address", + mangled_name.GetName().GetCString(), + alt_mangled_name.GetName().GetCString()); + else + log->Printf("Function \"%s\" had no address", + mangled_name.GetName().GetCString()); + } + + if (m_error_stream) + { + if (alt_mangled_name) + m_error_stream->Printf("error: call to a function '%s' (alternate name '%s') that is not present in the target\n", + mangled_name.GetName().GetCString(), + alt_mangled_name.GetName().GetCString()); + else if (mangled_name.GetMangledName()) + m_error_stream->Printf("error: call to a function '%s' ('%s') that is not present in the target\n", + mangled_name.GetName().GetCString(), + mangled_name.GetMangledName().GetCString()); + else + m_error_stream->Printf("error: call to a function '%s' that is not present in the target\n", + mangled_name.GetName().GetCString()); + } + return false; + } + } + } + else + { + if (!m_decl_map->GetFunctionAddress (name, fun_addr)) + { + if (log) + log->Printf ("Metadataless function \"%s\" had no address", name.GetCString()); + + if (m_error_stream) + m_error_stream->Printf("Error [IRForTarget]: Call to a symbol-only function '%s' that is not present in the target\n", name.GetCString()); + + return false; + } + } + + if (log) + log->Printf("Found \"%s\" at 0x%" PRIx64, name.GetCString(), fun_addr); + + return true; +} + +llvm::Constant * +IRForTarget::BuildFunctionPointer (llvm::Type *type, + uint64_t ptr) +{ + IntegerType *intptr_ty = Type::getIntNTy(m_module->getContext(), + (m_module->getPointerSize() == Module::Pointer64) ? 64 : 32); + PointerType *fun_ptr_ty = PointerType::getUnqual(type); + Constant *fun_addr_int = ConstantInt::get(intptr_ty, ptr, false); + return ConstantExpr::getIntToPtr(fun_addr_int, fun_ptr_ty); +} + +void +IRForTarget::RegisterFunctionMetadata(LLVMContext &context, + llvm::Value *function_ptr, + const char *name) +{ + for (Value::use_iterator i = function_ptr->use_begin(), e = function_ptr->use_end(); + i != e; + ++i) + { + Value *user = *i; + + if (Instruction *user_inst = dyn_cast(user)) + { + MDString* md_name = MDString::get(context, StringRef(name)); + + MDNode *metadata = MDNode::get(context, md_name); + + user_inst->setMetadata("lldb.call.realName", metadata); + } + else + { + RegisterFunctionMetadata (context, user, name); + } + } +} + +bool +IRForTarget::ResolveFunctionPointers(llvm::Module &llvm_module) +{ + lldb_private::Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + for (llvm::Module::iterator fi = llvm_module.begin(); + fi != llvm_module.end(); + ++fi) + { + Function *fun = fi; + + bool is_decl = fun->isDeclaration(); + + if (log) + log->Printf("Examining %s function %s", (is_decl ? "declaration" : "non-declaration"), fun->getName().str().c_str()); + + if (!is_decl) + continue; + + if (fun->hasNUses(0)) + continue; // ignore + + uint64_t addr = LLDB_INVALID_ADDRESS; + lldb_private::ConstString name; + Constant **value_ptr = NULL; + + if (!GetFunctionAddress(fun, + addr, + name, + value_ptr)) + return false; // GetFunctionAddress reports its own errors + + Constant *value = BuildFunctionPointer(fun->getFunctionType(), addr); + + RegisterFunctionMetadata (llvm_module.getContext(), fun, name.AsCString()); + + if (value_ptr) + *value_ptr = value; + + fun->replaceAllUsesWith(value); + } + + return true; +} + + +clang::NamedDecl * +IRForTarget::DeclForGlobal (const GlobalValue *global_val, Module *module) +{ + NamedMDNode *named_metadata = module->getNamedMetadata("clang.global.decl.ptrs"); + + if (!named_metadata) + return NULL; + + unsigned num_nodes = named_metadata->getNumOperands(); + unsigned node_index; + + for (node_index = 0; + node_index < num_nodes; + ++node_index) + { + MDNode *metadata_node = named_metadata->getOperand(node_index); + + if (!metadata_node) + return NULL; + + if (metadata_node->getNumOperands() != 2) + continue; + + if (metadata_node->getOperand(0) != global_val) + continue; + + ConstantInt *constant_int = dyn_cast(metadata_node->getOperand(1)); + + if (!constant_int) + return NULL; + + uintptr_t ptr = constant_int->getZExtValue(); + + return reinterpret_cast(ptr); + } + + return NULL; +} + +clang::NamedDecl * +IRForTarget::DeclForGlobal (GlobalValue *global_val) +{ + return DeclForGlobal(global_val, m_module); +} + +bool +IRForTarget::CreateResultVariable (llvm::Function &llvm_function) +{ + lldb_private::Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + if (!m_resolve_vars) + return true; + + // Find the result variable. If it doesn't exist, we can give up right here. + + ValueSymbolTable& value_symbol_table = m_module->getValueSymbolTable(); + + std::string result_name_str; + const char *result_name = NULL; + + for (ValueSymbolTable::iterator vi = value_symbol_table.begin(), ve = value_symbol_table.end(); + vi != ve; + ++vi) + { + result_name_str = vi->first().str(); + const char *value_name = result_name_str.c_str(); + + if (strstr(value_name, "$__lldb_expr_result_ptr") && + strncmp(value_name, "_ZGV", 4)) + { + result_name = value_name; + m_result_is_pointer = true; + break; + } + + if (strstr(value_name, "$__lldb_expr_result") && + strncmp(value_name, "_ZGV", 4)) + { + result_name = value_name; + m_result_is_pointer = false; + break; + } + } + + if (!result_name) + { + if (log) + log->PutCString("Couldn't find result variable"); + + return true; + } + + if (log) + log->Printf("Result name: \"%s\"", result_name); + + Value *result_value = m_module->getNamedValue(result_name); + + if (!result_value) + { + if (log) + log->PutCString("Result variable had no data"); + + if (m_error_stream) + m_error_stream->Printf("Internal error [IRForTarget]: Result variable's name (%s) exists, but not its definition\n", result_name); + + return false; + } + + if (log) + log->Printf("Found result in the IR: \"%s\"", PrintValue(result_value, false).c_str()); + + GlobalVariable *result_global = dyn_cast(result_value); + + if (!result_global) + { + if (log) + log->PutCString("Result variable isn't a GlobalVariable"); + + if (m_error_stream) + m_error_stream->Printf("Internal error [IRForTarget]: Result variable (%s) is defined, but is not a global variable\n", result_name); + + return false; + } + + clang::NamedDecl *result_decl = DeclForGlobal (result_global); + if (!result_decl) + { + if (log) + log->PutCString("Result variable doesn't have a corresponding Decl"); + + if (m_error_stream) + m_error_stream->Printf("Internal error [IRForTarget]: Result variable (%s) does not have a corresponding Clang entity\n", result_name); + + return false; + } + + if (log) + { + std::string decl_desc_str; + raw_string_ostream decl_desc_stream(decl_desc_str); + result_decl->print(decl_desc_stream); + decl_desc_stream.flush(); + + log->Printf("Found result decl: \"%s\"", decl_desc_str.c_str()); + } + + clang::VarDecl *result_var = dyn_cast(result_decl); + if (!result_var) + { + if (log) + log->PutCString("Result variable Decl isn't a VarDecl"); + + if (m_error_stream) + m_error_stream->Printf("Internal error [IRForTarget]: Result variable (%s)'s corresponding Clang entity isn't a variable\n", result_name); + + return false; + } + + // Get the next available result name from m_decl_map and create the persistent + // variable for it + + // If the result is an Lvalue, it is emitted as a pointer; see + // ASTResultSynthesizer::SynthesizeBodyResult. + if (m_result_is_pointer) + { + clang::QualType pointer_qual_type = result_var->getType(); + const clang::Type *pointer_type = pointer_qual_type.getTypePtr(); + + const clang::PointerType *pointer_pointertype = pointer_type->getAs(); + const clang::ObjCObjectPointerType *pointer_objcobjpointertype = pointer_type->getAs(); + + if (pointer_pointertype) + { + clang::QualType element_qual_type = pointer_pointertype->getPointeeType(); + + m_result_type = lldb_private::TypeFromParser(element_qual_type.getAsOpaquePtr(), + &result_decl->getASTContext()); + } + else if (pointer_objcobjpointertype) + { + clang::QualType element_qual_type = clang::QualType(pointer_objcobjpointertype->getObjectType(), 0); + + m_result_type = lldb_private::TypeFromParser(element_qual_type.getAsOpaquePtr(), + &result_decl->getASTContext()); + } + else + { + if (log) + log->PutCString("Expected result to have pointer type, but it did not"); + + if (m_error_stream) + m_error_stream->Printf("Internal error [IRForTarget]: Lvalue result (%s) is not a pointer variable\n", result_name); + + return false; + } + } + else + { + m_result_type = lldb_private::TypeFromParser(result_var->getType().getAsOpaquePtr(), + &result_decl->getASTContext()); + } + + if (m_result_type.GetBitSize() == 0) + { + lldb_private::StreamString type_desc_stream; + m_result_type.DumpTypeDescription(&type_desc_stream); + + if (log) + log->Printf("Result type has size 0"); + + if (m_error_stream) + m_error_stream->Printf("Error [IRForTarget]: Size of result type '%s' couldn't be determined\n", + type_desc_stream.GetData()); + return false; + } + + if (log) + { + lldb_private::StreamString type_desc_stream; + m_result_type.DumpTypeDescription(&type_desc_stream); + + log->Printf("Result decl type: \"%s\"", type_desc_stream.GetData()); + } + + m_result_name = lldb_private::ConstString("$RESULT_NAME"); + + if (log) + log->Printf("Creating a new result global: \"%s\" with size 0x%" PRIx64, + m_result_name.GetCString(), + m_result_type.GetByteSize()); + + // Construct a new result global and set up its metadata + + GlobalVariable *new_result_global = new GlobalVariable((*m_module), + result_global->getType()->getElementType(), + false, /* not constant */ + GlobalValue::ExternalLinkage, + NULL, /* no initializer */ + m_result_name.GetCString ()); + + // It's too late in compilation to create a new VarDecl for this, but we don't + // need to. We point the metadata at the old VarDecl. This creates an odd + // anomaly: a variable with a Value whose name is something like $0 and a + // Decl whose name is $__lldb_expr_result. This condition is handled in + // ClangExpressionDeclMap::DoMaterialize, and the name of the variable is + // fixed up. + + ConstantInt *new_constant_int = ConstantInt::get(llvm::Type::getInt64Ty(m_module->getContext()), + reinterpret_cast(result_decl), + false); + + llvm::Value* values[2]; + values[0] = new_result_global; + values[1] = new_constant_int; + + ArrayRef value_ref(values, 2); + + MDNode *persistent_global_md = MDNode::get(m_module->getContext(), value_ref); + NamedMDNode *named_metadata = m_module->getNamedMetadata("clang.global.decl.ptrs"); + named_metadata->addOperand(persistent_global_md); + + if (log) + log->Printf("Replacing \"%s\" with \"%s\"", + PrintValue(result_global).c_str(), + PrintValue(new_result_global).c_str()); + + if (result_global->hasNUses(0)) + { + // We need to synthesize a store for this variable, because otherwise + // there's nothing to put into its equivalent persistent variable. + + BasicBlock &entry_block(llvm_function.getEntryBlock()); + Instruction *first_entry_instruction(entry_block.getFirstNonPHIOrDbg()); + + if (!first_entry_instruction) + return false; + + if (!result_global->hasInitializer()) + { + if (log) + log->Printf("Couldn't find initializer for unused variable"); + + if (m_error_stream) + m_error_stream->Printf("Internal error [IRForTarget]: Result variable (%s) has no writes and no initializer\n", result_name); + + return false; + } + + Constant *initializer = result_global->getInitializer(); + + StoreInst *synthesized_store = new StoreInst(initializer, + new_result_global, + first_entry_instruction); + + if (log) + log->Printf("Synthesized result store \"%s\"\n", PrintValue(synthesized_store).c_str()); + } + else + { + result_global->replaceAllUsesWith(new_result_global); + } + + if (!m_decl_map->AddPersistentVariable(result_decl, + m_result_name, + m_result_type, + true, + m_result_is_pointer)) + return false; + + result_global->eraseFromParent(); + + return true; +} + +#if 0 +static void DebugUsers(Log *log, Value *value, uint8_t depth) +{ + if (!depth) + return; + + depth--; + + if (log) + log->Printf(" ", value->getNumUses()); + + for (Value::use_iterator ui = value->use_begin(), ue = value->use_end(); + ui != ue; + ++ui) + { + if (log) + log->Printf(" %s", *ui, PrintValue(*ui).c_str()); + DebugUsers(log, *ui, depth); + } + + if (log) + log->Printf(" "); +} +#endif + +bool +IRForTarget::RewriteObjCConstString (llvm::GlobalVariable *ns_str, + llvm::GlobalVariable *cstr) +{ + lldb_private::Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + Type *ns_str_ty = ns_str->getType(); + + Type *i8_ptr_ty = Type::getInt8PtrTy(m_module->getContext()); + IntegerType *intptr_ty = Type::getIntNTy(m_module->getContext(), + (m_module->getPointerSize() + == Module::Pointer64) ? 64 : 32); + Type *i32_ty = Type::getInt32Ty(m_module->getContext()); + Type *i8_ty = Type::getInt8Ty(m_module->getContext()); + + if (!m_CFStringCreateWithBytes) + { + lldb::addr_t CFStringCreateWithBytes_addr; + + static lldb_private::ConstString g_CFStringCreateWithBytes_str ("CFStringCreateWithBytes"); + + if (!m_decl_map->GetFunctionAddress (g_CFStringCreateWithBytes_str, CFStringCreateWithBytes_addr)) + { + if (log) + log->PutCString("Couldn't find CFStringCreateWithBytes in the target"); + + if (m_error_stream) + m_error_stream->Printf("Error [IRForTarget]: Rewriting an Objective-C constant string requires CFStringCreateWithBytes\n"); + + return false; + } + + if (log) + log->Printf("Found CFStringCreateWithBytes at 0x%" PRIx64, CFStringCreateWithBytes_addr); + + // Build the function type: + // + // CFStringRef CFStringCreateWithBytes ( + // CFAllocatorRef alloc, + // const UInt8 *bytes, + // CFIndex numBytes, + // CFStringEncoding encoding, + // Boolean isExternalRepresentation + // ); + // + // We make the following substitutions: + // + // CFStringRef -> i8* + // CFAllocatorRef -> i8* + // UInt8 * -> i8* + // CFIndex -> long (i32 or i64, as appropriate; we ask the module for its pointer size for now) + // CFStringEncoding -> i32 + // Boolean -> i8 + + Type *arg_type_array[5]; + + arg_type_array[0] = i8_ptr_ty; + arg_type_array[1] = i8_ptr_ty; + arg_type_array[2] = intptr_ty; + arg_type_array[3] = i32_ty; + arg_type_array[4] = i8_ty; + + ArrayRef CFSCWB_arg_types(arg_type_array, 5); + + llvm::Type *CFSCWB_ty = FunctionType::get(ns_str_ty, CFSCWB_arg_types, false); + + // Build the constant containing the pointer to the function + PointerType *CFSCWB_ptr_ty = PointerType::getUnqual(CFSCWB_ty); + Constant *CFSCWB_addr_int = ConstantInt::get(intptr_ty, CFStringCreateWithBytes_addr, false); + m_CFStringCreateWithBytes = ConstantExpr::getIntToPtr(CFSCWB_addr_int, CFSCWB_ptr_ty); + } + + ConstantDataSequential *string_array = NULL; + + if (cstr) + string_array = dyn_cast(cstr->getInitializer()); + + Constant *alloc_arg = Constant::getNullValue(i8_ptr_ty); + Constant *bytes_arg = cstr ? ConstantExpr::getBitCast(cstr, i8_ptr_ty) : Constant::getNullValue(i8_ptr_ty); + Constant *numBytes_arg = ConstantInt::get(intptr_ty, cstr ? string_array->getNumElements() - 1 : 0, false); + Constant *encoding_arg = ConstantInt::get(i32_ty, 0x0600, false); /* 0x0600 is kCFStringEncodingASCII */ + Constant *isExternal_arg = ConstantInt::get(i8_ty, 0x0, false); /* 0x0 is false */ + + Value *argument_array[5]; + + argument_array[0] = alloc_arg; + argument_array[1] = bytes_arg; + argument_array[2] = numBytes_arg; + argument_array[3] = encoding_arg; + argument_array[4] = isExternal_arg; + + ArrayRef CFSCWB_arguments(argument_array, 5); + + FunctionValueCache CFSCWB_Caller ([this, &CFSCWB_arguments] (llvm::Function *function)->llvm::Value * { + return CallInst::Create(m_CFStringCreateWithBytes, + CFSCWB_arguments, + "CFStringCreateWithBytes", + llvm::cast(m_entry_instruction_finder.GetValue(function))); + }); + + if (!UnfoldConstant(ns_str, CFSCWB_Caller, m_entry_instruction_finder)) + { + if (log) + log->PutCString("Couldn't replace the NSString with the result of the call"); + + if (m_error_stream) + m_error_stream->Printf("Error [IRForTarget]: Couldn't replace an Objective-C constant string with a dynamic string\n"); + + return false; + } + + ns_str->eraseFromParent(); + + return true; +} + +bool +IRForTarget::RewriteObjCConstStrings() +{ + lldb_private::Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + ValueSymbolTable& value_symbol_table = m_module->getValueSymbolTable(); + + for (ValueSymbolTable::iterator vi = value_symbol_table.begin(), ve = value_symbol_table.end(); + vi != ve; + ++vi) + { + std::string value_name = vi->first().str(); + const char *value_name_cstr = value_name.c_str(); + + if (strstr(value_name_cstr, "_unnamed_cfstring_")) + { + Value *nsstring_value = vi->second; + + GlobalVariable *nsstring_global = dyn_cast(nsstring_value); + + if (!nsstring_global) + { + if (log) + log->PutCString("NSString variable is not a GlobalVariable"); + + if (m_error_stream) + m_error_stream->Printf("Internal error [IRForTarget]: An Objective-C constant string is not a global variable\n"); + + return false; + } + + if (!nsstring_global->hasInitializer()) + { + if (log) + log->PutCString("NSString variable does not have an initializer"); + + if (m_error_stream) + m_error_stream->Printf("Internal error [IRForTarget]: An Objective-C constant string does not have an initializer\n"); + + return false; + } + + ConstantStruct *nsstring_struct = dyn_cast(nsstring_global->getInitializer()); + + if (!nsstring_struct) + { + if (log) + log->PutCString("NSString variable's initializer is not a ConstantStruct"); + + if (m_error_stream) + m_error_stream->Printf("Internal error [IRForTarget]: An Objective-C constant string is not a structure constant\n"); + + return false; + } + + // We expect the following structure: + // + // struct { + // int *isa; + // int flags; + // char *str; + // long length; + // }; + + if (nsstring_struct->getNumOperands() != 4) + { + if (log) + log->Printf("NSString variable's initializer structure has an unexpected number of members. Should be 4, is %d", nsstring_struct->getNumOperands()); + + if (m_error_stream) + m_error_stream->Printf("Internal error [IRForTarget]: The struct for an Objective-C constant string is not as expected\n"); + + return false; + } + + Constant *nsstring_member = nsstring_struct->getOperand(2); + + if (!nsstring_member) + { + if (log) + log->PutCString("NSString initializer's str element was empty"); + + if (m_error_stream) + m_error_stream->Printf("Internal error [IRForTarget]: An Objective-C constant string does not have a string initializer\n"); + + return false; + } + + ConstantExpr *nsstring_expr = dyn_cast(nsstring_member); + + if (!nsstring_expr) + { + if (log) + log->PutCString("NSString initializer's str element is not a ConstantExpr"); + + if (m_error_stream) + m_error_stream->Printf("Internal error [IRForTarget]: An Objective-C constant string's string initializer is not constant\n"); + + return false; + } + + if (nsstring_expr->getOpcode() != Instruction::GetElementPtr) + { + if (log) + log->Printf("NSString initializer's str element is not a GetElementPtr expression, it's a %s", nsstring_expr->getOpcodeName()); + + if (m_error_stream) + m_error_stream->Printf("Internal error [IRForTarget]: An Objective-C constant string's string initializer is not an array\n"); + + return false; + } + + Constant *nsstring_cstr = nsstring_expr->getOperand(0); + + GlobalVariable *cstr_global = dyn_cast(nsstring_cstr); + + if (!cstr_global) + { + if (log) + log->PutCString("NSString initializer's str element is not a GlobalVariable"); + + if (m_error_stream) + m_error_stream->Printf("Internal error [IRForTarget]: An Objective-C constant string's string initializer doesn't point to a global\n"); + + return false; + } + + if (!cstr_global->hasInitializer()) + { + if (log) + log->PutCString("NSString initializer's str element does not have an initializer"); + + if (m_error_stream) + m_error_stream->Printf("Internal error [IRForTarget]: An Objective-C constant string's string initializer doesn't point to initialized data\n"); + + return false; + } + + /* + if (!cstr_array) + { + if (log) + log->PutCString("NSString initializer's str element is not a ConstantArray"); + + if (m_error_stream) + m_error_stream->Printf("Internal error [IRForTarget]: An Objective-C constant string's string initializer doesn't point to an array\n"); + + return false; + } + + if (!cstr_array->isCString()) + { + if (log) + log->PutCString("NSString initializer's str element is not a C string array"); + + if (m_error_stream) + m_error_stream->Printf("Internal error [IRForTarget]: An Objective-C constant string's string initializer doesn't point to a C string\n"); + + return false; + } + */ + + ConstantDataArray *cstr_array = dyn_cast(cstr_global->getInitializer()); + + if (log) + { + if (cstr_array) + log->Printf("Found NSString constant %s, which contains \"%s\"", value_name_cstr, cstr_array->getAsString().str().c_str()); + else + log->Printf("Found NSString constant %s, which contains \"\"", value_name_cstr); + } + + if (!cstr_array) + cstr_global = NULL; + + if (!RewriteObjCConstString(nsstring_global, cstr_global)) + { + if (log) + log->PutCString("Error rewriting the constant string"); + + // We don't print an error message here because RewriteObjCConstString has done so for us. + + return false; + } + } + } + + for (ValueSymbolTable::iterator vi = value_symbol_table.begin(), ve = value_symbol_table.end(); + vi != ve; + ++vi) + { + std::string value_name = vi->first().str(); + const char *value_name_cstr = value_name.c_str(); + + if (!strcmp(value_name_cstr, "__CFConstantStringClassReference")) + { + GlobalVariable *gv = dyn_cast(vi->second); + + if (!gv) + { + if (log) + log->PutCString("__CFConstantStringClassReference is not a global variable"); + + if (m_error_stream) + m_error_stream->Printf("Internal error [IRForTarget]: Found a CFConstantStringClassReference, but it is not a global object\n"); + + return false; + } + + gv->eraseFromParent(); + + break; + } + } + + return true; +} + +static bool IsObjCSelectorRef (Value *value) +{ + GlobalVariable *global_variable = dyn_cast(value); + + if (!global_variable || !global_variable->hasName() || !global_variable->getName().startswith("\01L_OBJC_SELECTOR_REFERENCES_")) + return false; + + return true; +} + +// This function does not report errors; its callers are responsible. +bool +IRForTarget::RewriteObjCSelector (Instruction* selector_load) +{ + lldb_private::Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + LoadInst *load = dyn_cast(selector_load); + + if (!load) + return false; + + // Unpack the message name from the selector. In LLVM IR, an objc_msgSend gets represented as + // + // %tmp = load i8** @"\01L_OBJC_SELECTOR_REFERENCES_" ; + // %call = call i8* (i8*, i8*, ...)* @objc_msgSend(i8* %obj, i8* %tmp, ...) ; + // + // where %obj is the object pointer and %tmp is the selector. + // + // @"\01L_OBJC_SELECTOR_REFERENCES_" is a pointer to a character array called @"\01L_OBJC_llvm_moduleETH_VAR_NAllvm_moduleE_". + // @"\01L_OBJC_llvm_moduleETH_VAR_NAllvm_moduleE_" contains the string. + + // Find the pointer's initializer (a ConstantExpr with opcode GetElementPtr) and get the string from its target + + GlobalVariable *_objc_selector_references_ = dyn_cast(load->getPointerOperand()); + + if (!_objc_selector_references_ || !_objc_selector_references_->hasInitializer()) + return false; + + Constant *osr_initializer = _objc_selector_references_->getInitializer(); + + ConstantExpr *osr_initializer_expr = dyn_cast(osr_initializer); + + if (!osr_initializer_expr || osr_initializer_expr->getOpcode() != Instruction::GetElementPtr) + return false; + + Value *osr_initializer_base = osr_initializer_expr->getOperand(0); + + if (!osr_initializer_base) + return false; + + // Find the string's initializer (a ConstantArray) and get the string from it + + GlobalVariable *_objc_meth_var_name_ = dyn_cast(osr_initializer_base); + + if (!_objc_meth_var_name_ || !_objc_meth_var_name_->hasInitializer()) + return false; + + Constant *omvn_initializer = _objc_meth_var_name_->getInitializer(); + + ConstantDataArray *omvn_initializer_array = dyn_cast(omvn_initializer); + + if (!omvn_initializer_array->isString()) + return false; + + std::string omvn_initializer_string = omvn_initializer_array->getAsString(); + + if (log) + log->Printf("Found Objective-C selector reference \"%s\"", omvn_initializer_string.c_str()); + + // Construct a call to sel_registerName + + if (!m_sel_registerName) + { + lldb::addr_t sel_registerName_addr; + + static lldb_private::ConstString g_sel_registerName_str ("sel_registerName"); + if (!m_decl_map->GetFunctionAddress (g_sel_registerName_str, sel_registerName_addr)) + return false; + + if (log) + log->Printf("Found sel_registerName at 0x%" PRIx64, sel_registerName_addr); + + // Build the function type: struct objc_selector *sel_registerName(uint8_t*) + + // The below code would be "more correct," but in actuality what's required is uint8_t* + //Type *sel_type = StructType::get(m_module->getContext()); + //Type *sel_ptr_type = PointerType::getUnqual(sel_type); + Type *sel_ptr_type = Type::getInt8PtrTy(m_module->getContext()); + + Type *type_array[1]; + + type_array[0] = llvm::Type::getInt8PtrTy(m_module->getContext()); + + ArrayRef srN_arg_types(type_array, 1); + + llvm::Type *srN_type = FunctionType::get(sel_ptr_type, srN_arg_types, false); + + // Build the constant containing the pointer to the function + IntegerType *intptr_ty = Type::getIntNTy(m_module->getContext(), + (m_module->getPointerSize() == Module::Pointer64) ? 64 : 32); + PointerType *srN_ptr_ty = PointerType::getUnqual(srN_type); + Constant *srN_addr_int = ConstantInt::get(intptr_ty, sel_registerName_addr, false); + m_sel_registerName = ConstantExpr::getIntToPtr(srN_addr_int, srN_ptr_ty); + } + + Value *argument_array[1]; + + Constant *omvn_pointer = ConstantExpr::getBitCast(_objc_meth_var_name_, Type::getInt8PtrTy(m_module->getContext())); + + argument_array[0] = omvn_pointer; + + ArrayRef srN_arguments(argument_array, 1); + + CallInst *srN_call = CallInst::Create(m_sel_registerName, + srN_arguments, + "sel_registerName", + selector_load); + + // Replace the load with the call in all users + + selector_load->replaceAllUsesWith(srN_call); + + selector_load->eraseFromParent(); + + return true; +} + +bool +IRForTarget::RewriteObjCSelectors (BasicBlock &basic_block) +{ + lldb_private::Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + BasicBlock::iterator ii; + + typedef SmallVector InstrList; + typedef InstrList::iterator InstrIterator; + + InstrList selector_loads; + + for (ii = basic_block.begin(); + ii != basic_block.end(); + ++ii) + { + Instruction &inst = *ii; + + if (LoadInst *load = dyn_cast(&inst)) + if (IsObjCSelectorRef(load->getPointerOperand())) + selector_loads.push_back(&inst); + } + + InstrIterator iter; + + for (iter = selector_loads.begin(); + iter != selector_loads.end(); + ++iter) + { + if (!RewriteObjCSelector(*iter)) + { + if (m_error_stream) + m_error_stream->Printf("Internal error [IRForTarget]: Couldn't change a static reference to an Objective-C selector to a dynamic reference\n"); + + if (log) + log->PutCString("Couldn't rewrite a reference to an Objective-C selector"); + + return false; + } + } + + return true; +} + +// This function does not report errors; its callers are responsible. +bool +IRForTarget::RewritePersistentAlloc (llvm::Instruction *persistent_alloc) +{ + lldb_private::Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + AllocaInst *alloc = dyn_cast(persistent_alloc); + + MDNode *alloc_md = alloc->getMetadata("clang.decl.ptr"); + + if (!alloc_md || !alloc_md->getNumOperands()) + return false; + + ConstantInt *constant_int = dyn_cast(alloc_md->getOperand(0)); + + if (!constant_int) + return false; + + // We attempt to register this as a new persistent variable with the DeclMap. + + uintptr_t ptr = constant_int->getZExtValue(); + + clang::VarDecl *decl = reinterpret_cast(ptr); + + lldb_private::TypeFromParser result_decl_type (decl->getType().getAsOpaquePtr(), + &decl->getASTContext()); + + StringRef decl_name (decl->getName()); + lldb_private::ConstString persistent_variable_name (decl_name.data(), decl_name.size()); + if (!m_decl_map->AddPersistentVariable(decl, persistent_variable_name, result_decl_type, false, false)) + return false; + + GlobalVariable *persistent_global = new GlobalVariable((*m_module), + alloc->getType(), + false, /* not constant */ + GlobalValue::ExternalLinkage, + NULL, /* no initializer */ + alloc->getName().str().c_str()); + + // What we're going to do here is make believe this was a regular old external + // variable. That means we need to make the metadata valid. + + NamedMDNode *named_metadata = m_module->getOrInsertNamedMetadata("clang.global.decl.ptrs"); + + llvm::Value* values[2]; + values[0] = persistent_global; + values[1] = constant_int; + + ArrayRef value_ref(values, 2); + + MDNode *persistent_global_md = MDNode::get(m_module->getContext(), value_ref); + named_metadata->addOperand(persistent_global_md); + + // Now, since the variable is a pointer variable, we will drop in a load of that + // pointer variable. + + LoadInst *persistent_load = new LoadInst (persistent_global, "", alloc); + + if (log) + log->Printf("Replacing \"%s\" with \"%s\"", + PrintValue(alloc).c_str(), + PrintValue(persistent_load).c_str()); + + alloc->replaceAllUsesWith(persistent_load); + alloc->eraseFromParent(); + + return true; +} + +bool +IRForTarget::RewritePersistentAllocs(llvm::BasicBlock &basic_block) +{ + if (!m_resolve_vars) + return true; + + lldb_private::Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + BasicBlock::iterator ii; + + typedef SmallVector InstrList; + typedef InstrList::iterator InstrIterator; + + InstrList pvar_allocs; + + for (ii = basic_block.begin(); + ii != basic_block.end(); + ++ii) + { + Instruction &inst = *ii; + + if (AllocaInst *alloc = dyn_cast(&inst)) + { + llvm::StringRef alloc_name = alloc->getName(); + + if (alloc_name.startswith("$") && + !alloc_name.startswith("$__lldb")) + { + if (alloc_name.find_first_of("0123456789") == 1) + { + if (log) + log->Printf("Rejecting a numeric persistent variable."); + + if (m_error_stream) + m_error_stream->Printf("Error [IRForTarget]: Names starting with $0, $1, ... are reserved for use as result names\n"); + + return false; + } + + pvar_allocs.push_back(alloc); + } + } + } + + InstrIterator iter; + + for (iter = pvar_allocs.begin(); + iter != pvar_allocs.end(); + ++iter) + { + if (!RewritePersistentAlloc(*iter)) + { + if (m_error_stream) + m_error_stream->Printf("Internal error [IRForTarget]: Couldn't rewrite the creation of a persistent variable\n"); + + if (log) + log->PutCString("Couldn't rewrite the creation of a persistent variable"); + + return false; + } + } + + return true; +} + +bool +IRForTarget::MaterializeInitializer (uint8_t *data, Constant *initializer) +{ + if (!initializer) + return true; + + lldb_private::Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + if (log && log->GetVerbose()) + log->Printf(" MaterializeInitializer(%p, %s)", data, PrintValue(initializer).c_str()); + + Type *initializer_type = initializer->getType(); + + if (ConstantInt *int_initializer = dyn_cast(initializer)) + { + memcpy (data, int_initializer->getValue().getRawData(), m_target_data->getTypeStoreSize(initializer_type)); + return true; + } + else if (ConstantDataArray *array_initializer = dyn_cast(initializer)) + { + if (array_initializer->isString()) + { + std::string array_initializer_string = array_initializer->getAsString(); + memcpy (data, array_initializer_string.c_str(), m_target_data->getTypeStoreSize(initializer_type)); + } + else + { + ArrayType *array_initializer_type = array_initializer->getType(); + Type *array_element_type = array_initializer_type->getElementType(); + + size_t element_size = m_target_data->getTypeAllocSize(array_element_type); + + for (unsigned i = 0; i < array_initializer->getNumOperands(); ++i) + { + Value *operand_value = array_initializer->getOperand(i); + Constant *operand_constant = dyn_cast(operand_value); + + if (!operand_constant) + return false; + + if (!MaterializeInitializer(data + (i * element_size), operand_constant)) + return false; + } + } + return true; + } + else if (ConstantStruct *struct_initializer = dyn_cast(initializer)) + { + StructType *struct_initializer_type = struct_initializer->getType(); + const StructLayout *struct_layout = m_target_data->getStructLayout(struct_initializer_type); + + for (unsigned i = 0; + i < struct_initializer->getNumOperands(); + ++i) + { + if (!MaterializeInitializer(data + struct_layout->getElementOffset(i), struct_initializer->getOperand(i))) + return false; + } + return true; + } + else if (isa(initializer)) + { + memset(data, 0, m_target_data->getTypeStoreSize(initializer_type)); + return true; + } + return false; +} + +bool +IRForTarget::MaterializeInternalVariable (GlobalVariable *global_variable) +{ + if (GlobalVariable::isExternalLinkage(global_variable->getLinkage())) + return false; + + if (global_variable == m_reloc_placeholder) + return true; + + uint64_t offset = m_data_allocator.GetStream().GetSize(); + + llvm::Type *variable_type = global_variable->getType(); + + Constant *initializer = global_variable->getInitializer(); + + llvm::Type *initializer_type = initializer->getType(); + + size_t size = m_target_data->getTypeAllocSize(initializer_type); + size_t align = m_target_data->getPrefTypeAlignment(initializer_type); + + const size_t mask = (align - 1); + uint64_t aligned_offset = (offset + mask) & ~mask; + m_data_allocator.GetStream().PutNHex8(aligned_offset - offset, 0); + offset = aligned_offset; + + lldb_private::DataBufferHeap data(size, '\0'); + + if (initializer) + if (!MaterializeInitializer(data.GetBytes(), initializer)) + return false; + + m_data_allocator.GetStream().Write(data.GetBytes(), data.GetByteSize()); + + Constant *new_pointer = BuildRelocation(variable_type, offset); + + global_variable->replaceAllUsesWith(new_pointer); + + global_variable->eraseFromParent(); + + return true; +} + +// This function does not report errors; its callers are responsible. +bool +IRForTarget::MaybeHandleVariable (Value *llvm_value_ptr) +{ + lldb_private::Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + if (log) + log->Printf("MaybeHandleVariable (%s)", PrintValue(llvm_value_ptr).c_str()); + + if (ConstantExpr *constant_expr = dyn_cast(llvm_value_ptr)) + { + switch (constant_expr->getOpcode()) + { + default: + break; + case Instruction::GetElementPtr: + case Instruction::BitCast: + Value *s = constant_expr->getOperand(0); + if (!MaybeHandleVariable(s)) + return false; + } + } + else if (GlobalVariable *global_variable = dyn_cast(llvm_value_ptr)) + { + if (!GlobalValue::isExternalLinkage(global_variable->getLinkage())) + return MaterializeInternalVariable(global_variable); + + clang::NamedDecl *named_decl = DeclForGlobal(global_variable); + + if (!named_decl) + { + if (IsObjCSelectorRef(llvm_value_ptr)) + return true; + + if (!global_variable->hasExternalLinkage()) + return true; + + if (log) + log->Printf("Found global variable \"%s\" without metadata", global_variable->getName().str().c_str()); + + return false; + } + + std::string name (named_decl->getName().str()); + + clang::ValueDecl *value_decl = dyn_cast(named_decl); + if (value_decl == NULL) + return false; + + lldb_private::ClangASTType clang_type(&value_decl->getASTContext(), value_decl->getType()); + + const Type *value_type = NULL; + + if (name[0] == '$') + { + // The $__lldb_expr_result name indicates the the return value has allocated as + // a static variable. Per the comment at ASTResultSynthesizer::SynthesizeBodyResult, + // accesses to this static variable need to be redirected to the result of dereferencing + // a pointer that is passed in as one of the arguments. + // + // Consequently, when reporting the size of the type, we report a pointer type pointing + // to the type of $__lldb_expr_result, not the type itself. + // + // We also do this for any user-declared persistent variables. + clang_type = clang_type.GetPointerType(); + value_type = PointerType::get(global_variable->getType(), 0); + } + else + { + value_type = global_variable->getType(); + } + + const uint64_t value_size = clang_type.GetByteSize(); + off_t value_alignment = (clang_type.GetTypeBitAlign() + 7ull) / 8ull; + + if (log) + { + log->Printf("Type of \"%s\" is [clang \"%s\", llvm \"%s\"] [size %" PRIu64 ", align %" PRId64 "]", + name.c_str(), + clang_type.GetQualType().getAsString().c_str(), + PrintType(value_type).c_str(), + value_size, + value_alignment); + } + + + if (named_decl && !m_decl_map->AddValueToStruct(named_decl, + lldb_private::ConstString (name.c_str()), + llvm_value_ptr, + value_size, + value_alignment)) + { + if (!global_variable->hasExternalLinkage()) + return true; + else if (HandleSymbol (global_variable)) + return true; + else + return false; + } + } + else if (dyn_cast(llvm_value_ptr)) + { + if (log) + log->Printf("Function pointers aren't handled right now"); + + return false; + } + + return true; +} + +// This function does not report errors; its callers are responsible. +bool +IRForTarget::HandleSymbol (Value *symbol) +{ + lldb_private::Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + lldb_private::ConstString name(symbol->getName().str().c_str()); + + lldb::addr_t symbol_addr = m_decl_map->GetSymbolAddress (name, lldb::eSymbolTypeAny); + + if (symbol_addr == LLDB_INVALID_ADDRESS) + { + if (log) + log->Printf ("Symbol \"%s\" had no address", name.GetCString()); + + return false; + } + + if (log) + log->Printf("Found \"%s\" at 0x%" PRIx64, name.GetCString(), symbol_addr); + + Type *symbol_type = symbol->getType(); + IntegerType *intptr_ty = Type::getIntNTy(m_module->getContext(), + (m_module->getPointerSize() == Module::Pointer64) ? 64 : 32); + + Constant *symbol_addr_int = ConstantInt::get(intptr_ty, symbol_addr, false); + + Value *symbol_addr_ptr = ConstantExpr::getIntToPtr(symbol_addr_int, symbol_type); + + if (log) + log->Printf("Replacing %s with %s", PrintValue(symbol).c_str(), PrintValue(symbol_addr_ptr).c_str()); + + symbol->replaceAllUsesWith(symbol_addr_ptr); + + return true; +} + +bool +IRForTarget::MaybeHandleCallArguments (CallInst *Old) +{ + lldb_private::Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + if (log) + log->Printf("MaybeHandleCallArguments(%s)", PrintValue(Old).c_str()); + + for (unsigned op_index = 0, num_ops = Old->getNumArgOperands(); + op_index < num_ops; + ++op_index) + if (!MaybeHandleVariable(Old->getArgOperand(op_index))) // conservatively believe that this is a store + { + if (m_error_stream) + m_error_stream->Printf("Internal error [IRForTarget]: Couldn't rewrite one of the arguments of a function call.\n"); + + return false; + } + + return true; +} + +bool +IRForTarget::HandleObjCClass(Value *classlist_reference) +{ + lldb_private::Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + GlobalVariable *global_variable = dyn_cast(classlist_reference); + + if (!global_variable) + return false; + + Constant *initializer = global_variable->getInitializer(); + + if (!initializer) + return false; + + if (!initializer->hasName()) + return false; + + StringRef name(initializer->getName()); + lldb_private::ConstString name_cstr(name.str().c_str()); + lldb::addr_t class_ptr = m_decl_map->GetSymbolAddress(name_cstr, lldb::eSymbolTypeObjCClass); + + if (log) + log->Printf("Found reference to Objective-C class %s (0x%llx)", name_cstr.AsCString(), (unsigned long long)class_ptr); + + if (class_ptr == LLDB_INVALID_ADDRESS) + return false; + + if (global_variable->use_begin() == global_variable->use_end()) + return false; + + SmallVector load_instructions; + + for (Value::use_iterator i = global_variable->use_begin(), e = global_variable->use_end(); + i != e; + ++i) + { + if (LoadInst *load_instruction = dyn_cast(*i)) + load_instructions.push_back(load_instruction); + } + + if (load_instructions.empty()) + return false; + + IntegerType *intptr_ty = Type::getIntNTy(m_module->getContext(), + (m_module->getPointerSize() + == Module::Pointer64) ? 64 : 32); + + Constant *class_addr = ConstantInt::get(intptr_ty, (uint64_t)class_ptr); + + for (LoadInst *load_instruction : load_instructions) + { + Constant *class_bitcast = ConstantExpr::getIntToPtr(class_addr, load_instruction->getType()); + + load_instruction->replaceAllUsesWith(class_bitcast); + + load_instruction->eraseFromParent(); + } + + return true; +} + +bool +IRForTarget::RemoveCXAAtExit (BasicBlock &basic_block) +{ + BasicBlock::iterator ii; + + std::vector calls_to_remove; + + for (ii = basic_block.begin(); + ii != basic_block.end(); + ++ii) + { + Instruction &inst = *ii; + + CallInst *call = dyn_cast(&inst); + + // MaybeHandleCallArguments handles error reporting; we are silent here + if (!call) + continue; + + bool remove = false; + + llvm::Function *func = call->getCalledFunction(); + + if (func && func->getName() == "__cxa_atexit") + remove = true; + + llvm::Value *val = call->getCalledValue(); + + if (val && val->getName() == "__cxa_atexit") + remove = true; + + if (remove) + calls_to_remove.push_back(call); + } + + for (std::vector::iterator ci = calls_to_remove.begin(), ce = calls_to_remove.end(); + ci != ce; + ++ci) + { + (*ci)->eraseFromParent(); + } + + return true; +} + +bool +IRForTarget::ResolveCalls(BasicBlock &basic_block) +{ + ///////////////////////////////////////////////////////////////////////// + // Prepare the current basic block for execution in the remote process + // + + BasicBlock::iterator ii; + + for (ii = basic_block.begin(); + ii != basic_block.end(); + ++ii) + { + Instruction &inst = *ii; + + CallInst *call = dyn_cast(&inst); + + // MaybeHandleCallArguments handles error reporting; we are silent here + if (call && !MaybeHandleCallArguments(call)) + return false; + } + + return true; +} + +bool +IRForTarget::ResolveExternals (Function &llvm_function) +{ + lldb_private::Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + for (Module::global_iterator global = m_module->global_begin(), end = m_module->global_end(); + global != end; + ++global) + { + if (!global) + { + if (m_error_stream) + m_error_stream->Printf("Internal error [IRForTarget]: global variable is NULL"); + + return false; + } + + std::string global_name = (*global).getName().str(); + + if (log) + log->Printf("Examining %s, DeclForGlobalValue returns %p", + global_name.c_str(), + DeclForGlobal(global)); + + if (global_name.find("OBJC_IVAR") == 0) + { + if (!HandleSymbol(global)) + { + if (m_error_stream) + m_error_stream->Printf("Error [IRForTarget]: Couldn't find Objective-C indirect ivar symbol %s\n", global_name.c_str()); + + return false; + } + } + else if (global_name.find("OBJC_CLASSLIST_REFERENCES_$") != global_name.npos) + { + if (!HandleObjCClass(global)) + { + if (m_error_stream) + m_error_stream->Printf("Error [IRForTarget]: Couldn't resolve the class for an Objective-C static method call\n"); + + return false; + } + } + else if (global_name.find("OBJC_CLASSLIST_SUP_REFS_$") != global_name.npos) + { + if (!HandleObjCClass(global)) + { + if (m_error_stream) + m_error_stream->Printf("Error [IRForTarget]: Couldn't resolve the class for an Objective-C static method call\n"); + + return false; + } + } + else if (DeclForGlobal(global)) + { + if (!MaybeHandleVariable (global)) + { + if (m_error_stream) + m_error_stream->Printf("Internal error [IRForTarget]: Couldn't rewrite external variable %s\n", global_name.c_str()); + + return false; + } + } + } + + return true; +} + +bool +IRForTarget::ReplaceStrings () +{ + lldb_private::Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + typedef std::map OffsetsTy; + + OffsetsTy offsets; + + for (Module::global_iterator gi = m_module->global_begin(), ge = m_module->global_end(); + gi != ge; + ++gi) + { + GlobalVariable *gv = gi; + + if (!gv->hasInitializer()) + continue; + + Constant *gc = gv->getInitializer(); + + std::string str; + + if (gc->isNullValue()) + { + Type *gc_type = gc->getType(); + + ArrayType *gc_array_type = dyn_cast(gc_type); + + if (!gc_array_type) + continue; + + Type *gc_element_type = gc_array_type->getElementType(); + + IntegerType *gc_integer_type = dyn_cast(gc_element_type); + + if (gc_integer_type->getBitWidth() != 8) + continue; + + str = ""; + } + else + { + ConstantDataArray *gc_array = dyn_cast(gc); + + if (!gc_array) + continue; + + if (!gc_array->isCString()) + continue; + + if (log) + log->Printf("Found a GlobalVariable with string initializer %s", PrintValue(gc).c_str()); + + str = gc_array->getAsString(); + } + + offsets[gv] = m_data_allocator.GetStream().GetSize(); + + m_data_allocator.GetStream().Write(str.c_str(), str.length() + 1); + } + + Type *char_ptr_ty = Type::getInt8PtrTy(m_module->getContext()); + + for (OffsetsTy::iterator oi = offsets.begin(), oe = offsets.end(); + oi != oe; + ++oi) + { + GlobalVariable *gv = oi->first; + size_t offset = oi->second; + + Constant *new_initializer = BuildRelocation(char_ptr_ty, offset); + + if (log) + log->Printf("Replacing GV %s with %s", PrintValue(gv).c_str(), PrintValue(new_initializer).c_str()); + + for (GlobalVariable::use_iterator ui = gv->use_begin(), ue = gv->use_end(); + ui != ue; + ++ui) + { + if (log) + log->Printf("Found use %s", PrintValue(*ui).c_str()); + + ConstantExpr *const_expr = dyn_cast(*ui); + StoreInst *store_inst = dyn_cast(*ui); + + if (const_expr) + { + if (const_expr->getOpcode() != Instruction::GetElementPtr) + { + if (log) + log->Printf("Use (%s) of string variable is not a GetElementPtr constant", PrintValue(const_expr).c_str()); + + return false; + } + + Constant *bit_cast = ConstantExpr::getBitCast(new_initializer, const_expr->getOperand(0)->getType()); + Constant *new_gep = const_expr->getWithOperandReplaced(0, bit_cast); + + const_expr->replaceAllUsesWith(new_gep); + } + else if (store_inst) + { + Constant *bit_cast = ConstantExpr::getBitCast(new_initializer, store_inst->getValueOperand()->getType()); + + store_inst->setOperand(0, bit_cast); + } + else + { + if (log) + log->Printf("Use (%s) of string variable is neither a constant nor a store", PrintValue(const_expr).c_str()); + + return false; + } + } + + gv->eraseFromParent(); + } + + return true; +} + +bool +IRForTarget::ReplaceStaticLiterals (llvm::BasicBlock &basic_block) +{ + lldb_private::Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + typedef SmallVector ConstantList; + typedef SmallVector UserList; + typedef ConstantList::iterator ConstantIterator; + typedef UserList::iterator UserIterator; + + ConstantList static_constants; + UserList static_users; + + for (BasicBlock::iterator ii = basic_block.begin(), ie = basic_block.end(); + ii != ie; + ++ii) + { + llvm::Instruction &inst = *ii; + + for (Instruction::op_iterator oi = inst.op_begin(), oe = inst.op_end(); + oi != oe; + ++oi) + { + Value *operand_val = oi->get(); + + ConstantFP *operand_constant_fp = dyn_cast(operand_val); + + if (operand_constant_fp/* && operand_constant_fp->getType()->isX86_FP80Ty()*/) + { + static_constants.push_back(operand_val); + static_users.push_back(ii); + } + } + } + + ConstantIterator constant_iter; + UserIterator user_iter; + + for (constant_iter = static_constants.begin(), user_iter = static_users.begin(); + constant_iter != static_constants.end(); + ++constant_iter, ++user_iter) + { + Value *operand_val = *constant_iter; + llvm::Instruction *inst = *user_iter; + + ConstantFP *operand_constant_fp = dyn_cast(operand_val); + + if (operand_constant_fp) + { + Type *operand_type = operand_constant_fp->getType(); + + APFloat operand_apfloat = operand_constant_fp->getValueAPF(); + APInt operand_apint = operand_apfloat.bitcastToAPInt(); + + const uint8_t* operand_raw_data = (const uint8_t*)operand_apint.getRawData(); + size_t operand_data_size = operand_apint.getBitWidth() / 8; + + if (log) + { + std::string s; + raw_string_ostream ss(s); + for (size_t index = 0; + index < operand_data_size; + ++index) + { + ss << (uint32_t)operand_raw_data[index]; + ss << " "; + } + ss.flush(); + + log->Printf("Found ConstantFP with size %lu and raw data %s", operand_data_size, s.c_str()); + } + + lldb_private::DataBufferHeap data(operand_data_size, 0); + + if (lldb::endian::InlHostByteOrder() != m_data_allocator.GetStream().GetByteOrder()) + { + uint8_t *data_bytes = data.GetBytes(); + + for (size_t index = 0; + index < operand_data_size; + ++index) + { + data_bytes[index] = operand_raw_data[operand_data_size - (1 + index)]; + } + } + else + { + memcpy(data.GetBytes(), operand_raw_data, operand_data_size); + } + + uint64_t offset = m_data_allocator.GetStream().GetSize(); + + size_t align = m_target_data->getPrefTypeAlignment(operand_type); + + const size_t mask = (align - 1); + uint64_t aligned_offset = (offset + mask) & ~mask; + m_data_allocator.GetStream().PutNHex8(aligned_offset - offset, 0); + offset = aligned_offset; + + m_data_allocator.GetStream().Write(data.GetBytes(), operand_data_size); + + llvm::Type *fp_ptr_ty = operand_constant_fp->getType()->getPointerTo(); + + Constant *new_pointer = BuildRelocation(fp_ptr_ty, aligned_offset); + + llvm::LoadInst *fp_load = new llvm::LoadInst(new_pointer, "fp_load", inst); + + operand_constant_fp->replaceAllUsesWith(fp_load); + } + } + + return true; +} + +static bool isGuardVariableRef(Value *V) +{ + Constant *Old = NULL; + + if (!(Old = dyn_cast(V))) + return false; + + ConstantExpr *CE = NULL; + + if ((CE = dyn_cast(V))) + { + if (CE->getOpcode() != Instruction::BitCast) + return false; + + Old = CE->getOperand(0); + } + + GlobalVariable *GV = dyn_cast(Old); + + if (!GV || !GV->hasName() || !GV->getName().startswith("_ZGV")) + return false; + + return true; +} + +void +IRForTarget::TurnGuardLoadIntoZero(llvm::Instruction* guard_load) +{ + Constant* zero(ConstantInt::get(Type::getInt8Ty(m_module->getContext()), 0, true)); + + Value::use_iterator ui; + + for (ui = guard_load->use_begin(); + ui != guard_load->use_end(); + ++ui) + { + if (isa(*ui)) + { + // do nothing for the moment + } + else + { + ui->replaceUsesOfWith(guard_load, zero); + } + } + + guard_load->eraseFromParent(); +} + +static void ExciseGuardStore(Instruction* guard_store) +{ + guard_store->eraseFromParent(); +} + +bool +IRForTarget::RemoveGuards(BasicBlock &basic_block) +{ + /////////////////////////////////////////////////////// + // Eliminate any reference to guard variables found. + // + + BasicBlock::iterator ii; + + typedef SmallVector InstrList; + typedef InstrList::iterator InstrIterator; + + InstrList guard_loads; + InstrList guard_stores; + + for (ii = basic_block.begin(); + ii != basic_block.end(); + ++ii) + { + Instruction &inst = *ii; + + if (LoadInst *load = dyn_cast(&inst)) + if (isGuardVariableRef(load->getPointerOperand())) + guard_loads.push_back(&inst); + + if (StoreInst *store = dyn_cast(&inst)) + if (isGuardVariableRef(store->getPointerOperand())) + guard_stores.push_back(&inst); + } + + InstrIterator iter; + + for (iter = guard_loads.begin(); + iter != guard_loads.end(); + ++iter) + TurnGuardLoadIntoZero(*iter); + + for (iter = guard_stores.begin(); + iter != guard_stores.end(); + ++iter) + ExciseGuardStore(*iter); + + return true; +} + +// This function does not report errors; its callers are responsible. +bool +IRForTarget::UnfoldConstant(Constant *old_constant, + FunctionValueCache &value_maker, + FunctionValueCache &entry_instruction_finder) +{ + lldb_private::Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + Value::use_iterator ui; + + SmallVector users; + + // We do this because the use list might change, invalidating our iterator. + // Much better to keep a work list ourselves. + for (ui = old_constant->use_begin(); + ui != old_constant->use_end(); + ++ui) + users.push_back(*ui); + + for (size_t i = 0; + i < users.size(); + ++i) + { + User *user = users[i]; + + if (Constant *constant = dyn_cast(user)) + { + // synthesize a new non-constant equivalent of the constant + + if (ConstantExpr *constant_expr = dyn_cast(constant)) + { + switch (constant_expr->getOpcode()) + { + default: + if (log) + log->Printf("Unhandled constant expression type: \"%s\"", PrintValue(constant_expr).c_str()); + return false; + case Instruction::BitCast: + { + FunctionValueCache bit_cast_maker ([&value_maker, &entry_instruction_finder, old_constant, constant_expr] (llvm::Function *function)->llvm::Value* { + // UnaryExpr + // OperandList[0] is value + + if (constant_expr->getOperand(0) != old_constant) + return constant_expr; + + return new BitCastInst(value_maker.GetValue(function), + constant_expr->getType(), + "", + llvm::cast(entry_instruction_finder.GetValue(function))); + }); + + if (!UnfoldConstant(constant_expr, bit_cast_maker, entry_instruction_finder)) + return false; + } + break; + case Instruction::GetElementPtr: + { + // GetElementPtrConstantExpr + // OperandList[0] is base + // OperandList[1]... are indices + + FunctionValueCache get_element_pointer_maker ([&value_maker, &entry_instruction_finder, old_constant, constant_expr] (llvm::Function *function)->llvm::Value* { + Value *ptr = constant_expr->getOperand(0); + + if (ptr == old_constant) + ptr = value_maker.GetValue(function); + + std::vector index_vector; + + unsigned operand_index; + unsigned num_operands = constant_expr->getNumOperands(); + + for (operand_index = 1; + operand_index < num_operands; + ++operand_index) + { + Value *operand = constant_expr->getOperand(operand_index); + + if (operand == old_constant) + operand = value_maker.GetValue(function); + + index_vector.push_back(operand); + } + + ArrayRef indices(index_vector); + + return GetElementPtrInst::Create(ptr, indices, "", llvm::cast(entry_instruction_finder.GetValue(function))); + }); + + if (!UnfoldConstant(constant_expr, get_element_pointer_maker, entry_instruction_finder)) + return false; + } + break; + } + } + else + { + if (log) + log->Printf("Unhandled constant type: \"%s\"", PrintValue(constant).c_str()); + return false; + } + } + else + { + if (Instruction *inst = llvm::dyn_cast(user)) + { + inst->replaceUsesOfWith(old_constant, value_maker.GetValue(inst->getParent()->getParent())); + } + else + { + if (log) + log->Printf("Unhandled non-constant type: \"%s\"", PrintValue(user).c_str()); + return false; + } + } + } + + if (!isa(old_constant)) + { + old_constant->destroyConstant(); + } + + return true; +} + +bool +IRForTarget::ReplaceVariables (Function &llvm_function) +{ + if (!m_resolve_vars) + return true; + + lldb_private::Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + m_decl_map->DoStructLayout(); + + if (log) + log->Printf("Element arrangement:"); + + uint32_t num_elements; + uint32_t element_index; + + size_t size; + off_t alignment; + + if (!m_decl_map->GetStructInfo (num_elements, size, alignment)) + return false; + + Function::arg_iterator iter(llvm_function.getArgumentList().begin()); + + if (iter == llvm_function.getArgumentList().end()) + { + if (m_error_stream) + m_error_stream->Printf("Internal error [IRForTarget]: Wrapper takes no arguments (should take at least a struct pointer)"); + + return false; + } + + Argument *argument = iter; + + if (argument->getName().equals("this")) + { + ++iter; + + if (iter == llvm_function.getArgumentList().end()) + { + if (m_error_stream) + m_error_stream->Printf("Internal error [IRForTarget]: Wrapper takes only 'this' argument (should take a struct pointer too)"); + + return false; + } + + argument = iter; + } + else if (argument->getName().equals("self")) + { + ++iter; + + if (iter == llvm_function.getArgumentList().end()) + { + if (m_error_stream) + m_error_stream->Printf("Internal error [IRForTarget]: Wrapper takes only 'self' argument (should take '_cmd' and a struct pointer too)"); + + return false; + } + + if (!iter->getName().equals("_cmd")) + { + if (m_error_stream) + m_error_stream->Printf("Internal error [IRForTarget]: Wrapper takes '%s' after 'self' argument (should take '_cmd')", iter->getName().str().c_str()); + + return false; + } + + ++iter; + + if (iter == llvm_function.getArgumentList().end()) + { + if (m_error_stream) + m_error_stream->Printf("Internal error [IRForTarget]: Wrapper takes only 'self' and '_cmd' arguments (should take a struct pointer too)"); + + return false; + } + + argument = iter; + } + + if (!argument->getName().equals("$__lldb_arg")) + { + if (m_error_stream) + m_error_stream->Printf("Internal error [IRForTarget]: Wrapper takes an argument named '%s' instead of the struct pointer", argument->getName().str().c_str()); + + return false; + } + + if (log) + log->Printf("Arg: \"%s\"", PrintValue(argument).c_str()); + + BasicBlock &entry_block(llvm_function.getEntryBlock()); + Instruction *FirstEntryInstruction(entry_block.getFirstNonPHIOrDbg()); + + if (!FirstEntryInstruction) + { + if (m_error_stream) + m_error_stream->Printf("Internal error [IRForTarget]: Couldn't find the first instruction in the wrapper for use in rewriting"); + + return false; + } + + LLVMContext &context(m_module->getContext()); + IntegerType *offset_type(Type::getInt32Ty(context)); + + if (!offset_type) + { + if (m_error_stream) + m_error_stream->Printf("Internal error [IRForTarget]: Couldn't produce an offset type"); + + return false; + } + + for (element_index = 0; element_index < num_elements; ++element_index) + { + const clang::NamedDecl *decl = NULL; + Value *value = NULL; + off_t offset; + lldb_private::ConstString name; + + if (!m_decl_map->GetStructElement (decl, value, offset, name, element_index)) + { + if (m_error_stream) + m_error_stream->Printf("Internal error [IRForTarget]: Structure information is incomplete"); + + return false; + } + + if (log) + log->Printf(" \"%s\" (\"%s\") placed at %" PRId64, + name.GetCString(), + decl->getNameAsString().c_str(), + offset); + + if (value) + { + if (log) + log->Printf(" Replacing [%s]", PrintValue(value).c_str()); + + FunctionValueCache body_result_maker ([this, name, offset_type, offset, argument, value] (llvm::Function *function)->llvm::Value * { + // Per the comment at ASTResultSynthesizer::SynthesizeBodyResult, in cases where the result + // variable is an rvalue, we have to synthesize a dereference of the appropriate structure + // entry in order to produce the static variable that the AST thinks it is accessing. + + llvm::Instruction *entry_instruction = llvm::cast(m_entry_instruction_finder.GetValue(function)); + + ConstantInt *offset_int(ConstantInt::get(offset_type, offset, true)); + GetElementPtrInst *get_element_ptr = GetElementPtrInst::Create(argument, + offset_int, + "", + entry_instruction); + + if (name == m_result_name && !m_result_is_pointer) + { + BitCastInst *bit_cast = new BitCastInst(get_element_ptr, + value->getType()->getPointerTo(), + "", + entry_instruction); + + LoadInst *load = new LoadInst(bit_cast, "", entry_instruction); + + return load; + } + else + { + BitCastInst *bit_cast = new BitCastInst(get_element_ptr, value->getType(), "", entry_instruction); + + return bit_cast; + } + }); + + if (Constant *constant = dyn_cast(value)) + { + UnfoldConstant(constant, body_result_maker, m_entry_instruction_finder); + } + else if (Instruction *instruction = dyn_cast(value)) + { + value->replaceAllUsesWith(body_result_maker.GetValue(instruction->getParent()->getParent())); + } + else + { + if (log) + log->Printf("Unhandled non-constant type: \"%s\"", PrintValue(value).c_str()); + return false; + } + + if (GlobalVariable *var = dyn_cast(value)) + var->eraseFromParent(); + } + } + + if (log) + log->Printf("Total structure [align %" PRId64 ", size %lu]", alignment, size); + + return true; +} + +llvm::Constant * +IRForTarget::BuildRelocation(llvm::Type *type, uint64_t offset) +{ + IntegerType *intptr_ty = Type::getIntNTy(m_module->getContext(), + (m_module->getPointerSize() == Module::Pointer64) ? 64 : 32); + + llvm::Constant *offset_int = ConstantInt::get(intptr_ty, offset); + + llvm::Constant *offset_array[1]; + + offset_array[0] = offset_int; + + llvm::ArrayRef offsets(offset_array, 1); + + llvm::Constant *reloc_getelementptr = ConstantExpr::getGetElementPtr(m_reloc_placeholder, offsets); + llvm::Constant *reloc_getbitcast = ConstantExpr::getBitCast(reloc_getelementptr, type); + + return reloc_getbitcast; +} + +bool +IRForTarget::CompleteDataAllocation () +{ + lldb_private::Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + if (!m_data_allocator.GetStream().GetSize()) + return true; + + lldb::addr_t allocation = m_data_allocator.Allocate(); + + if (log) + { + if (allocation) + log->Printf("Allocated static data at 0x%llx", (unsigned long long)allocation); + else + log->Printf("Failed to allocate static data"); + } + + if (!allocation || allocation == LLDB_INVALID_ADDRESS) + return false; + + IntegerType *intptr_ty = Type::getIntNTy(m_module->getContext(), + (m_module->getPointerSize() == Module::Pointer64) ? 64 : 32); + + Constant *relocated_addr = ConstantInt::get(intptr_ty, (uint64_t)allocation); + Constant *relocated_bitcast = ConstantExpr::getIntToPtr(relocated_addr, llvm::Type::getInt8PtrTy(m_module->getContext())); + + m_reloc_placeholder->replaceAllUsesWith(relocated_bitcast); + + m_reloc_placeholder->eraseFromParent(); + + return true; +} + +bool +IRForTarget::StripAllGVs (Module &llvm_module) +{ + lldb_private::Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + std::vector global_vars; + std::seterased_vars; + + bool erased = true; + + while (erased) + { + erased = false; + + for (Module::global_iterator gi = llvm_module.global_begin(), ge = llvm_module.global_end(); + gi != ge; + ++gi) + { + GlobalVariable *global_var = dyn_cast(gi); + + global_var->removeDeadConstantUsers(); + + if (global_var->use_empty()) + { + if (log) + log->Printf("Did remove %s", + PrintValue(global_var).c_str()); + global_var->eraseFromParent(); + erased = true; + break; + } + } + } + + for (Module::global_iterator gi = llvm_module.global_begin(), ge = llvm_module.global_end(); + gi != ge; + ++gi) + { + GlobalVariable *global_var = dyn_cast(gi); + + GlobalValue::use_iterator ui = global_var->use_begin(); + + if (log) + log->Printf("Couldn't remove %s because of %s", + PrintValue(global_var).c_str(), + PrintValue(*ui).c_str()); + } + + return true; +} + +bool +IRForTarget::runOnModule (Module &llvm_module) +{ + lldb_private::Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + m_module = &llvm_module; + m_target_data.reset(new DataLayout(m_module)); + + if (log) + { + std::string s; + raw_string_ostream oss(s); + + m_module->print(oss, NULL); + + oss.flush(); + + log->Printf("Module as passed in to IRForTarget: \n\"%s\"", s.c_str()); + } + + Function* main_function = m_module->getFunction(StringRef(m_func_name.c_str())); + + if (!main_function) + { + if (log) + log->Printf("Couldn't find \"%s()\" in the module", m_func_name.c_str()); + + if (m_error_stream) + m_error_stream->Printf("Internal error [IRForTarget]: Couldn't find wrapper '%s' in the module", m_func_name.c_str()); + + return false; + } + + if (!FixFunctionLinkage (*main_function)) + { + if (log) + log->Printf("Couldn't fix the linkage for the function"); + + return false; + } + + llvm::Type *intptr_ty = Type::getInt8Ty(m_module->getContext()); + + m_reloc_placeholder = new llvm::GlobalVariable((*m_module), + intptr_ty, + false /* IsConstant */, + GlobalVariable::InternalLinkage, + Constant::getNullValue(intptr_ty), + "reloc_placeholder", + NULL /* InsertBefore */, + GlobalVariable::NotThreadLocal /* ThreadLocal */, + 0 /* AddressSpace */); + + //////////////////////////////////////////////////////////// + // Replace $__lldb_expr_result with a persistent variable + // + + if (!CreateResultVariable(*main_function)) + { + if (log) + log->Printf("CreateResultVariable() failed"); + + // CreateResultVariable() reports its own errors, so we don't do so here + + return false; + } + + if (log && log->GetVerbose()) + { + std::string s; + raw_string_ostream oss(s); + + m_module->print(oss, NULL); + + oss.flush(); + + log->Printf("Module after creating the result variable: \n\"%s\"", s.c_str()); + } + + for (Module::iterator fi = m_module->begin(), fe = m_module->end(); + fi != fe; + ++fi) + { + llvm::Function *function = fi; + + if (function->begin() == function->end()) + continue; + + Function::iterator bbi; + + for (bbi = function->begin(); + bbi != function->end(); + ++bbi) + { + if (!RemoveGuards(*bbi)) + { + if (log) + log->Printf("RemoveGuards() failed"); + + // RemoveGuards() reports its own errors, so we don't do so here + + return false; + } + + if (!RewritePersistentAllocs(*bbi)) + { + if (log) + log->Printf("RewritePersistentAllocs() failed"); + + // RewritePersistentAllocs() reports its own errors, so we don't do so here + + return false; + } + + if (!RemoveCXAAtExit(*bbi)) + { + if (log) + log->Printf("RemoveCXAAtExit() failed"); + + // RemoveCXAAtExit() reports its own errors, so we don't do so here + + return false; + } + } + } + + /////////////////////////////////////////////////////////////////////////////// + // Fix all Objective-C constant strings to use NSStringWithCString:encoding: + // + + if (!RewriteObjCConstStrings()) + { + if (log) + log->Printf("RewriteObjCConstStrings() failed"); + + // RewriteObjCConstStrings() reports its own errors, so we don't do so here + + return false; + } + + /////////////////////////////// + // Resolve function pointers + // + + if (!ResolveFunctionPointers(llvm_module)) + { + if (log) + log->Printf("ResolveFunctionPointers() failed"); + + // ResolveFunctionPointers() reports its own errors, so we don't do so here + + return false; + } + + for (Module::iterator fi = m_module->begin(), fe = m_module->end(); + fi != fe; + ++fi) + { + llvm::Function *function = fi; + + for (llvm::Function::iterator bbi = function->begin(), bbe = function->end(); + bbi != bbe; + ++bbi) + { + if (!RewriteObjCSelectors(*bbi)) + { + if (log) + log->Printf("RewriteObjCSelectors() failed"); + + // RewriteObjCSelectors() reports its own errors, so we don't do so here + + return false; + } + } + } + + for (Module::iterator fi = m_module->begin(), fe = m_module->end(); + fi != fe; + ++fi) + { + llvm::Function *function = fi; + + for (llvm::Function::iterator bbi = function->begin(), bbe = function->end(); + bbi != bbe; + ++bbi) + { + if (!ResolveCalls(*bbi)) + { + if (log) + log->Printf("ResolveCalls() failed"); + + // ResolveCalls() reports its own errors, so we don't do so here + + return false; + } + + if (!ReplaceStaticLiterals(*bbi)) + { + if (log) + log->Printf("ReplaceStaticLiterals() failed"); + + return false; + } + } + } + + //////////////////////////////////////////////////////////////////////// + // Run function-level passes that only make sense on the main function + // + + if (!ResolveExternals(*main_function)) + { + if (log) + log->Printf("ResolveExternals() failed"); + + // ResolveExternals() reports its own errors, so we don't do so here + + return false; + } + + if (!ReplaceVariables(*main_function)) + { + if (log) + log->Printf("ReplaceVariables() failed"); + + // ReplaceVariables() reports its own errors, so we don't do so here + + return false; + } + + if (!ReplaceStrings()) + { + if (log) + log->Printf("ReplaceStrings() failed"); + + return false; + } + + if (!CompleteDataAllocation()) + { + if (log) + log->Printf("CompleteDataAllocation() failed"); + + return false; + } + + if (!StripAllGVs(llvm_module)) + { + if (log) + log->Printf("StripAllGVs() failed"); + } + + if (log && log->GetVerbose()) + { + std::string s; + raw_string_ostream oss(s); + + m_module->print(oss, NULL); + + oss.flush(); + + log->Printf("Module after preparing for execution: \n\"%s\"", s.c_str()); + } + + return true; +} + +void +IRForTarget::assignPassManager (PMStack &pass_mgr_stack, PassManagerType pass_mgr_type) +{ +} + +PassManagerType +IRForTarget::getPotentialPassManagerType() const +{ + return PMT_ModulePassManager; +} diff --git a/contrib/llvm/tools/lldb/source/Expression/IRInterpreter.cpp b/contrib/llvm/tools/lldb/source/Expression/IRInterpreter.cpp new file mode 100644 index 00000000000..dcbf584cfb6 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Expression/IRInterpreter.cpp @@ -0,0 +1,1379 @@ +//===-- IRInterpreter.cpp ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/Error.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Scalar.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Expression/IRMemoryMap.h" +#include "lldb/Expression/IRInterpreter.h" + +#include "llvm/IR/Constants.h" +#include "llvm/IR/DataLayout.h" +#include "llvm/IR/Function.h" +#include "llvm/IR/Instructions.h" +#include "llvm/IR/Module.h" +#include "llvm/Support/raw_ostream.h" + +#include + +using namespace llvm; + +static std::string +PrintValue(const Value *value, bool truncate = false) +{ + std::string s; + raw_string_ostream rso(s); + value->print(rso); + rso.flush(); + if (truncate) + s.resize(s.length() - 1); + + size_t offset; + while ((offset = s.find('\n')) != s.npos) + s.erase(offset, 1); + while (s[0] == ' ' || s[0] == '\t') + s.erase(0, 1); + + return s; +} + +static std::string +PrintType(const Type *type, bool truncate = false) +{ + std::string s; + raw_string_ostream rso(s); + type->print(rso); + rso.flush(); + if (truncate) + s.resize(s.length() - 1); + return s; +} + +class InterpreterStackFrame +{ +public: + typedef std::map ValueMap; + + ValueMap m_values; + DataLayout &m_target_data; + lldb_private::IRMemoryMap &m_memory_map; + const BasicBlock *m_bb; + BasicBlock::const_iterator m_ii; + BasicBlock::const_iterator m_ie; + + lldb::addr_t m_frame_process_address; + size_t m_frame_size; + lldb::addr_t m_stack_pointer; + + lldb::ByteOrder m_byte_order; + size_t m_addr_byte_size; + + InterpreterStackFrame (DataLayout &target_data, + lldb_private::IRMemoryMap &memory_map, + lldb::addr_t stack_frame_bottom, + lldb::addr_t stack_frame_top) : + m_target_data (target_data), + m_memory_map (memory_map) + { + m_byte_order = (target_data.isLittleEndian() ? lldb::eByteOrderLittle : lldb::eByteOrderBig); + m_addr_byte_size = (target_data.getPointerSize(0)); + + m_frame_process_address = stack_frame_bottom; + m_frame_size = stack_frame_top - stack_frame_bottom; + m_stack_pointer = stack_frame_top; + } + + ~InterpreterStackFrame () + { + } + + void Jump (const BasicBlock *bb) + { + m_bb = bb; + m_ii = m_bb->begin(); + m_ie = m_bb->end(); + } + + std::string SummarizeValue (const Value *value) + { + lldb_private::StreamString ss; + + ss.Printf("%s", PrintValue(value).c_str()); + + ValueMap::iterator i = m_values.find(value); + + if (i != m_values.end()) + { + lldb::addr_t addr = i->second; + + ss.Printf(" 0x%llx", (unsigned long long)addr); + } + + return ss.GetString(); + } + + bool AssignToMatchType (lldb_private::Scalar &scalar, uint64_t u64value, Type *type) + { + size_t type_size = m_target_data.getTypeStoreSize(type); + + switch (type_size) + { + case 1: + scalar = (uint8_t)u64value; + break; + case 2: + scalar = (uint16_t)u64value; + break; + case 4: + scalar = (uint32_t)u64value; + break; + case 8: + scalar = (uint64_t)u64value; + break; + default: + return false; + } + + return true; + } + + bool EvaluateValue (lldb_private::Scalar &scalar, const Value *value, Module &module) + { + const Constant *constant = dyn_cast(value); + + if (constant) + { + APInt value_apint; + + if (!ResolveConstantValue(value_apint, constant)) + return false; + + return AssignToMatchType(scalar, value_apint.getLimitedValue(), value->getType()); + } + else + { + lldb::addr_t process_address = ResolveValue(value, module); + size_t value_size = m_target_data.getTypeStoreSize(value->getType()); + + lldb_private::DataExtractor value_extractor; + lldb_private::Error extract_error; + + m_memory_map.GetMemoryData(value_extractor, process_address, value_size, extract_error); + + if (!extract_error.Success()) + return false; + + lldb::offset_t offset = 0; + if (value_size == 1 || value_size == 2 || value_size == 4 || value_size == 8) + { + uint64_t u64value = value_extractor.GetMaxU64(&offset, value_size); + return AssignToMatchType(scalar, u64value, value->getType()); + } + } + + return false; + } + + bool AssignValue (const Value *value, lldb_private::Scalar &scalar, Module &module) + { + lldb::addr_t process_address = ResolveValue (value, module); + + if (process_address == LLDB_INVALID_ADDRESS) + return false; + + lldb_private::Scalar cast_scalar; + + if (!AssignToMatchType(cast_scalar, scalar.GetRawBits64(0), value->getType())) + return false; + + size_t value_byte_size = m_target_data.getTypeStoreSize(value->getType()); + + lldb_private::DataBufferHeap buf(value_byte_size, 0); + + lldb_private::Error get_data_error; + + if (!cast_scalar.GetAsMemoryData(buf.GetBytes(), buf.GetByteSize(), m_byte_order, get_data_error)) + return false; + + lldb_private::Error write_error; + + m_memory_map.WriteMemory(process_address, buf.GetBytes(), buf.GetByteSize(), write_error); + + return write_error.Success(); + } + + bool ResolveConstantValue (APInt &value, const Constant *constant) + { + switch (constant->getValueID()) + { + default: + break; + case Value::ConstantIntVal: + if (const ConstantInt *constant_int = dyn_cast(constant)) + { + value = constant_int->getValue(); + return true; + } + break; + case Value::ConstantFPVal: + if (const ConstantFP *constant_fp = dyn_cast(constant)) + { + value = constant_fp->getValueAPF().bitcastToAPInt(); + return true; + } + break; + case Value::ConstantExprVal: + if (const ConstantExpr *constant_expr = dyn_cast(constant)) + { + switch (constant_expr->getOpcode()) + { + default: + return false; + case Instruction::IntToPtr: + case Instruction::PtrToInt: + case Instruction::BitCast: + return ResolveConstantValue(value, constant_expr->getOperand(0)); + case Instruction::GetElementPtr: + { + ConstantExpr::const_op_iterator op_cursor = constant_expr->op_begin(); + ConstantExpr::const_op_iterator op_end = constant_expr->op_end(); + + Constant *base = dyn_cast(*op_cursor); + + if (!base) + return false; + + if (!ResolveConstantValue(value, base)) + return false; + + op_cursor++; + + if (op_cursor == op_end) + return true; // no offset to apply! + + SmallVector indices (op_cursor, op_end); + + uint64_t offset = m_target_data.getIndexedOffset(base->getType(), indices); + + const bool is_signed = true; + value += APInt(value.getBitWidth(), offset, is_signed); + + return true; + } + } + } + break; + case Value::ConstantPointerNullVal: + if (isa(constant)) + { + value = APInt(m_target_data.getPointerSizeInBits(), 0); + return true; + } + break; + } + return false; + } + + bool MakeArgument(const Argument *value, uint64_t address) + { + lldb::addr_t data_address = Malloc(value->getType()); + + if (data_address == LLDB_INVALID_ADDRESS) + return false; + + lldb_private::Error write_error; + + m_memory_map.WritePointerToMemory(data_address, address, write_error); + + if (!write_error.Success()) + { + lldb_private::Error free_error; + m_memory_map.Free(data_address, free_error); + return false; + } + + m_values[value] = data_address; + + lldb_private::Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + if (log) + { + log->Printf("Made an allocation for argument %s", PrintValue(value).c_str()); + log->Printf(" Data region : %llx", (unsigned long long)address); + log->Printf(" Ref region : %llx", (unsigned long long)data_address); + } + + return true; + } + + bool ResolveConstant (lldb::addr_t process_address, const Constant *constant) + { + APInt resolved_value; + + if (!ResolveConstantValue(resolved_value, constant)) + return false; + + const uint64_t *raw_data = resolved_value.getRawData(); + + size_t constant_size = m_target_data.getTypeStoreSize(constant->getType()); + + lldb_private::Error write_error; + + m_memory_map.WriteMemory(process_address, (uint8_t*)raw_data, constant_size, write_error); + + return write_error.Success(); + } + + lldb::addr_t Malloc (size_t size, uint8_t byte_alignment) + { + lldb::addr_t ret = m_stack_pointer; + + ret -= size; + ret -= (ret % byte_alignment); + + if (ret < m_frame_process_address) + return LLDB_INVALID_ADDRESS; + + m_stack_pointer = ret; + return ret; + } + + lldb::addr_t MallocPointer () + { + return Malloc(m_target_data.getPointerSize(), m_target_data.getPointerPrefAlignment()); + } + + lldb::addr_t Malloc (llvm::Type *type) + { + lldb_private::Error alloc_error; + + return Malloc(m_target_data.getTypeAllocSize(type), m_target_data.getPrefTypeAlignment(type)); + } + + std::string PrintData (lldb::addr_t addr, llvm::Type *type) + { + size_t length = m_target_data.getTypeStoreSize(type); + + lldb_private::DataBufferHeap buf(length, 0); + + lldb_private::Error read_error; + + m_memory_map.ReadMemory(buf.GetBytes(), addr, length, read_error); + + if (!read_error.Success()) + return std::string(""); + + lldb_private::StreamString ss; + + for (size_t i = 0; i < length; i++) + { + if ((!(i & 0xf)) && i) + ss.Printf("%02hhx - ", buf.GetBytes()[i]); + else + ss.Printf("%02hhx ", buf.GetBytes()[i]); + } + + return ss.GetString(); + } + + lldb::addr_t ResolveValue (const Value *value, Module &module) + { + ValueMap::iterator i = m_values.find(value); + + if (i != m_values.end()) + return i->second; + + // Fall back and allocate space [allocation type Alloca] + + lldb::addr_t data_address = Malloc(value->getType()); + + if (const Constant *constant = dyn_cast(value)) + { + if (!ResolveConstant (data_address, constant)) + { + lldb_private::Error free_error; + m_memory_map.Free(data_address, free_error); + return LLDB_INVALID_ADDRESS; + } + } + + m_values[value] = data_address; + return data_address; + } +}; + +static const char *unsupported_opcode_error = "Interpreter doesn't handle one of the expression's opcodes"; +static const char *unsupported_operand_error = "Interpreter doesn't handle one of the expression's operands"; +//static const char *interpreter_initialization_error = "Interpreter couldn't be initialized"; +static const char *interpreter_internal_error = "Interpreter encountered an internal error"; +static const char *bad_value_error = "Interpreter couldn't resolve a value during execution"; +static const char *memory_allocation_error = "Interpreter couldn't allocate memory"; +static const char *memory_write_error = "Interpreter couldn't write to memory"; +static const char *memory_read_error = "Interpreter couldn't read from memory"; +static const char *infinite_loop_error = "Interpreter ran for too many cycles"; +//static const char *bad_result_error = "Result of expression is in bad memory"; + +bool +IRInterpreter::CanInterpret (llvm::Module &module, + llvm::Function &function, + lldb_private::Error &error) +{ + lldb_private::Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + bool saw_function_with_body = false; + + for (Module::iterator fi = module.begin(), fe = module.end(); + fi != fe; + ++fi) + { + if (fi->begin() != fi->end()) + { + if (saw_function_with_body) + return false; + saw_function_with_body = true; + } + } + + for (Function::iterator bbi = function.begin(), bbe = function.end(); + bbi != bbe; + ++bbi) + { + for (BasicBlock::iterator ii = bbi->begin(), ie = bbi->end(); + ii != ie; + ++ii) + { + switch (ii->getOpcode()) + { + default: + { + if (log) + log->Printf("Unsupported instruction: %s", PrintValue(ii).c_str()); + error.SetErrorToGenericError(); + error.SetErrorString(unsupported_opcode_error); + return false; + } + case Instruction::Add: + case Instruction::Alloca: + case Instruction::BitCast: + case Instruction::Br: + case Instruction::GetElementPtr: + break; + case Instruction::ICmp: + { + ICmpInst *icmp_inst = dyn_cast(ii); + + if (!icmp_inst) + { + error.SetErrorToGenericError(); + error.SetErrorString(interpreter_internal_error); + return false; + } + + switch (icmp_inst->getPredicate()) + { + default: + { + if (log) + log->Printf("Unsupported ICmp predicate: %s", PrintValue(ii).c_str()); + + error.SetErrorToGenericError(); + error.SetErrorString(unsupported_opcode_error); + return false; + } + case CmpInst::ICMP_EQ: + case CmpInst::ICMP_NE: + case CmpInst::ICMP_UGT: + case CmpInst::ICMP_UGE: + case CmpInst::ICMP_ULT: + case CmpInst::ICMP_ULE: + case CmpInst::ICMP_SGT: + case CmpInst::ICMP_SGE: + case CmpInst::ICMP_SLT: + case CmpInst::ICMP_SLE: + break; + } + } + break; + case Instruction::And: + case Instruction::AShr: + case Instruction::IntToPtr: + case Instruction::PtrToInt: + case Instruction::Load: + case Instruction::LShr: + case Instruction::Mul: + case Instruction::Or: + case Instruction::Ret: + case Instruction::SDiv: + case Instruction::SExt: + case Instruction::Shl: + case Instruction::SRem: + case Instruction::Store: + case Instruction::Sub: + case Instruction::UDiv: + case Instruction::URem: + case Instruction::Xor: + case Instruction::ZExt: + break; + } + + for (int oi = 0, oe = ii->getNumOperands(); + oi != oe; + ++oi) + { + Value *operand = ii->getOperand(oi); + Type *operand_type = operand->getType(); + + switch (operand_type->getTypeID()) + { + default: + break; + case Type::VectorTyID: + { + if (log) + log->Printf("Unsupported operand type: %s", PrintType(operand_type).c_str()); + error.SetErrorString(unsupported_operand_error); + return false; + } + } + } + } + + } + + return true;} + +bool +IRInterpreter::Interpret (llvm::Module &module, + llvm::Function &function, + llvm::ArrayRef args, + lldb_private::IRMemoryMap &memory_map, + lldb_private::Error &error, + lldb::addr_t stack_frame_bottom, + lldb::addr_t stack_frame_top) +{ + lldb_private::Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + if (log) + { + std::string s; + raw_string_ostream oss(s); + + module.print(oss, NULL); + + oss.flush(); + + log->Printf("Module as passed in to IRInterpreter::Interpret: \n\"%s\"", s.c_str()); + } + + DataLayout data_layout(&module); + + InterpreterStackFrame frame(data_layout, memory_map, stack_frame_bottom, stack_frame_top); + + if (frame.m_frame_process_address == LLDB_INVALID_ADDRESS) + { + error.SetErrorString("Couldn't allocate stack frame"); + } + + int arg_index = 0; + + for (llvm::Function::arg_iterator ai = function.arg_begin(), ae = function.arg_end(); + ai != ae; + ++ai, ++arg_index) + { + if (args.size() < arg_index) + { + error.SetErrorString ("Not enough arguments passed in to function"); + return false; + } + + lldb::addr_t ptr = args[arg_index]; + + frame.MakeArgument(ai, ptr); + } + + uint32_t num_insts = 0; + + frame.Jump(function.begin()); + + while (frame.m_ii != frame.m_ie && (++num_insts < 4096)) + { + const Instruction *inst = frame.m_ii; + + if (log) + log->Printf("Interpreting %s", PrintValue(inst).c_str()); + + switch (inst->getOpcode()) + { + default: + break; + case Instruction::Add: + case Instruction::Sub: + case Instruction::Mul: + case Instruction::SDiv: + case Instruction::UDiv: + case Instruction::SRem: + case Instruction::URem: + case Instruction::Shl: + case Instruction::LShr: + case Instruction::AShr: + case Instruction::And: + case Instruction::Or: + case Instruction::Xor: + { + const BinaryOperator *bin_op = dyn_cast(inst); + + if (!bin_op) + { + if (log) + log->Printf("getOpcode() returns %s, but instruction is not a BinaryOperator", inst->getOpcodeName()); + error.SetErrorToGenericError(); + error.SetErrorString(interpreter_internal_error); + return false; + } + + Value *lhs = inst->getOperand(0); + Value *rhs = inst->getOperand(1); + + lldb_private::Scalar L; + lldb_private::Scalar R; + + if (!frame.EvaluateValue(L, lhs, module)) + { + if (log) + log->Printf("Couldn't evaluate %s", PrintValue(lhs).c_str()); + error.SetErrorToGenericError(); + error.SetErrorString(bad_value_error); + return false; + } + + if (!frame.EvaluateValue(R, rhs, module)) + { + if (log) + log->Printf("Couldn't evaluate %s", PrintValue(rhs).c_str()); + error.SetErrorToGenericError(); + error.SetErrorString(bad_value_error); + return false; + } + + lldb_private::Scalar result; + + switch (inst->getOpcode()) + { + default: + break; + case Instruction::Add: + result = L + R; + break; + case Instruction::Mul: + result = L * R; + break; + case Instruction::Sub: + result = L - R; + break; + case Instruction::SDiv: + L.MakeSigned(); + R.MakeSigned(); + result = L / R; + break; + case Instruction::UDiv: + result = L.GetRawBits64(0) / R.GetRawBits64(1); + break; + case Instruction::SRem: + L.MakeSigned(); + R.MakeSigned(); + result = L % R; + break; + case Instruction::URem: + result = L.GetRawBits64(0) % R.GetRawBits64(1); + break; + case Instruction::Shl: + result = L << R; + break; + case Instruction::AShr: + result = L >> R; + break; + case Instruction::LShr: + result = L; + result.ShiftRightLogical(R); + break; + case Instruction::And: + result = L & R; + break; + case Instruction::Or: + result = L | R; + break; + case Instruction::Xor: + result = L ^ R; + break; + } + + frame.AssignValue(inst, result, module); + + if (log) + { + log->Printf("Interpreted a %s", inst->getOpcodeName()); + log->Printf(" L : %s", frame.SummarizeValue(lhs).c_str()); + log->Printf(" R : %s", frame.SummarizeValue(rhs).c_str()); + log->Printf(" = : %s", frame.SummarizeValue(inst).c_str()); + } + } + break; + case Instruction::Alloca: + { + const AllocaInst *alloca_inst = dyn_cast(inst); + + if (!alloca_inst) + { + if (log) + log->Printf("getOpcode() returns Alloca, but instruction is not an AllocaInst"); + error.SetErrorToGenericError(); + error.SetErrorString(interpreter_internal_error); + return false; + } + + if (alloca_inst->isArrayAllocation()) + { + if (log) + log->Printf("AllocaInsts are not handled if isArrayAllocation() is true"); + error.SetErrorToGenericError(); + error.SetErrorString(unsupported_opcode_error); + return false; + } + + // The semantics of Alloca are: + // Create a region R of virtual memory of type T, backed by a data buffer + // Create a region P of virtual memory of type T*, backed by a data buffer + // Write the virtual address of R into P + + Type *T = alloca_inst->getAllocatedType(); + Type *Tptr = alloca_inst->getType(); + + lldb::addr_t R = frame.Malloc(T); + + if (R == LLDB_INVALID_ADDRESS) + { + if (log) + log->Printf("Couldn't allocate memory for an AllocaInst"); + error.SetErrorToGenericError(); + error.SetErrorString(memory_allocation_error); + return false; + } + + lldb::addr_t P = frame.Malloc(Tptr); + + if (P == LLDB_INVALID_ADDRESS) + { + if (log) + log->Printf("Couldn't allocate the result pointer for an AllocaInst"); + error.SetErrorToGenericError(); + error.SetErrorString(memory_allocation_error); + return false; + } + + lldb_private::Error write_error; + + memory_map.WritePointerToMemory(P, R, write_error); + + if (!write_error.Success()) + { + if (log) + log->Printf("Couldn't write the result pointer for an AllocaInst"); + error.SetErrorToGenericError(); + error.SetErrorString(memory_write_error); + lldb_private::Error free_error; + memory_map.Free(P, free_error); + memory_map.Free(R, free_error); + return false; + } + + frame.m_values[alloca_inst] = P; + + if (log) + { + log->Printf("Interpreted an AllocaInst"); + log->Printf(" R : 0x%" PRIx64, R); + log->Printf(" P : 0x%" PRIx64, P); + } + } + break; + case Instruction::BitCast: + case Instruction::ZExt: + { + const CastInst *cast_inst = dyn_cast(inst); + + if (!cast_inst) + { + if (log) + log->Printf("getOpcode() returns %s, but instruction is not a BitCastInst", cast_inst->getOpcodeName()); + error.SetErrorToGenericError(); + error.SetErrorString(interpreter_internal_error); + return false; + } + + Value *source = cast_inst->getOperand(0); + + lldb_private::Scalar S; + + if (!frame.EvaluateValue(S, source, module)) + { + if (log) + log->Printf("Couldn't evaluate %s", PrintValue(source).c_str()); + error.SetErrorToGenericError(); + error.SetErrorString(bad_value_error); + return false; + } + + frame.AssignValue(inst, S, module); + } + break; + case Instruction::SExt: + { + const CastInst *cast_inst = dyn_cast(inst); + + if (!cast_inst) + { + if (log) + log->Printf("getOpcode() returns %s, but instruction is not a BitCastInst", cast_inst->getOpcodeName()); + error.SetErrorToGenericError(); + error.SetErrorString(interpreter_internal_error); + return false; + } + + Value *source = cast_inst->getOperand(0); + + lldb_private::Scalar S; + + if (!frame.EvaluateValue(S, source, module)) + { + if (log) + log->Printf("Couldn't evaluate %s", PrintValue(source).c_str()); + error.SetErrorToGenericError(); + error.SetErrorString(bad_value_error); + return false; + } + + S.MakeSigned(); + + lldb_private::Scalar S_signextend(S.SLongLong()); + + frame.AssignValue(inst, S_signextend, module); + } + break; + case Instruction::Br: + { + const BranchInst *br_inst = dyn_cast(inst); + + if (!br_inst) + { + if (log) + log->Printf("getOpcode() returns Br, but instruction is not a BranchInst"); + error.SetErrorToGenericError(); + error.SetErrorString(interpreter_internal_error); + return false; + } + + if (br_inst->isConditional()) + { + Value *condition = br_inst->getCondition(); + + lldb_private::Scalar C; + + if (!frame.EvaluateValue(C, condition, module)) + { + if (log) + log->Printf("Couldn't evaluate %s", PrintValue(condition).c_str()); + error.SetErrorToGenericError(); + error.SetErrorString(bad_value_error); + return false; + } + + if (C.GetRawBits64(0)) + frame.Jump(br_inst->getSuccessor(0)); + else + frame.Jump(br_inst->getSuccessor(1)); + + if (log) + { + log->Printf("Interpreted a BrInst with a condition"); + log->Printf(" cond : %s", frame.SummarizeValue(condition).c_str()); + } + } + else + { + frame.Jump(br_inst->getSuccessor(0)); + + if (log) + { + log->Printf("Interpreted a BrInst with no condition"); + } + } + } + continue; + case Instruction::GetElementPtr: + { + const GetElementPtrInst *gep_inst = dyn_cast(inst); + + if (!gep_inst) + { + if (log) + log->Printf("getOpcode() returns GetElementPtr, but instruction is not a GetElementPtrInst"); + error.SetErrorToGenericError(); + error.SetErrorString(interpreter_internal_error); + return false; + } + + const Value *pointer_operand = gep_inst->getPointerOperand(); + Type *pointer_type = pointer_operand->getType(); + + lldb_private::Scalar P; + + if (!frame.EvaluateValue(P, pointer_operand, module)) + { + if (log) + log->Printf("Couldn't evaluate %s", PrintValue(pointer_operand).c_str()); + error.SetErrorToGenericError(); + error.SetErrorString(bad_value_error); + return false; + } + + typedef SmallVector IndexVector; + typedef IndexVector::iterator IndexIterator; + + SmallVector indices (gep_inst->idx_begin(), + gep_inst->idx_end()); + + SmallVector const_indices; + + for (IndexIterator ii = indices.begin(), ie = indices.end(); + ii != ie; + ++ii) + { + ConstantInt *constant_index = dyn_cast(*ii); + + if (!constant_index) + { + lldb_private::Scalar I; + + if (!frame.EvaluateValue(I, *ii, module)) + { + if (log) + log->Printf("Couldn't evaluate %s", PrintValue(*ii).c_str()); + error.SetErrorToGenericError(); + error.SetErrorString(bad_value_error); + return false; + } + + if (log) + log->Printf("Evaluated constant index %s as %llu", PrintValue(*ii).c_str(), I.ULongLong(LLDB_INVALID_ADDRESS)); + + constant_index = cast(ConstantInt::get((*ii)->getType(), I.ULongLong(LLDB_INVALID_ADDRESS))); + } + + const_indices.push_back(constant_index); + } + + uint64_t offset = data_layout.getIndexedOffset(pointer_type, const_indices); + + lldb_private::Scalar Poffset = P + offset; + + frame.AssignValue(inst, Poffset, module); + + if (log) + { + log->Printf("Interpreted a GetElementPtrInst"); + log->Printf(" P : %s", frame.SummarizeValue(pointer_operand).c_str()); + log->Printf(" Poffset : %s", frame.SummarizeValue(inst).c_str()); + } + } + break; + case Instruction::ICmp: + { + const ICmpInst *icmp_inst = dyn_cast(inst); + + if (!icmp_inst) + { + if (log) + log->Printf("getOpcode() returns ICmp, but instruction is not an ICmpInst"); + error.SetErrorToGenericError(); + error.SetErrorString(interpreter_internal_error); + return false; + } + + CmpInst::Predicate predicate = icmp_inst->getPredicate(); + + Value *lhs = inst->getOperand(0); + Value *rhs = inst->getOperand(1); + + lldb_private::Scalar L; + lldb_private::Scalar R; + + if (!frame.EvaluateValue(L, lhs, module)) + { + if (log) + log->Printf("Couldn't evaluate %s", PrintValue(lhs).c_str()); + error.SetErrorToGenericError(); + error.SetErrorString(bad_value_error); + return false; + } + + if (!frame.EvaluateValue(R, rhs, module)) + { + if (log) + log->Printf("Couldn't evaluate %s", PrintValue(rhs).c_str()); + error.SetErrorToGenericError(); + error.SetErrorString(bad_value_error); + return false; + } + + lldb_private::Scalar result; + + switch (predicate) + { + default: + return false; + case CmpInst::ICMP_EQ: + result = (L == R); + break; + case CmpInst::ICMP_NE: + result = (L != R); + break; + case CmpInst::ICMP_UGT: + result = (L.GetRawBits64(0) > R.GetRawBits64(0)); + break; + case CmpInst::ICMP_UGE: + result = (L.GetRawBits64(0) >= R.GetRawBits64(0)); + break; + case CmpInst::ICMP_ULT: + result = (L.GetRawBits64(0) < R.GetRawBits64(0)); + break; + case CmpInst::ICMP_ULE: + result = (L.GetRawBits64(0) <= R.GetRawBits64(0)); + break; + case CmpInst::ICMP_SGT: + L.MakeSigned(); + R.MakeSigned(); + result = (L > R); + break; + case CmpInst::ICMP_SGE: + L.MakeSigned(); + R.MakeSigned(); + result = (L >= R); + break; + case CmpInst::ICMP_SLT: + L.MakeSigned(); + R.MakeSigned(); + result = (L < R); + break; + case CmpInst::ICMP_SLE: + L.MakeSigned(); + R.MakeSigned(); + result = (L <= R); + break; + } + + frame.AssignValue(inst, result, module); + + if (log) + { + log->Printf("Interpreted an ICmpInst"); + log->Printf(" L : %s", frame.SummarizeValue(lhs).c_str()); + log->Printf(" R : %s", frame.SummarizeValue(rhs).c_str()); + log->Printf(" = : %s", frame.SummarizeValue(inst).c_str()); + } + } + break; + case Instruction::IntToPtr: + { + const IntToPtrInst *int_to_ptr_inst = dyn_cast(inst); + + if (!int_to_ptr_inst) + { + if (log) + log->Printf("getOpcode() returns IntToPtr, but instruction is not an IntToPtrInst"); + error.SetErrorToGenericError(); + error.SetErrorString(interpreter_internal_error); + return false; + } + + Value *src_operand = int_to_ptr_inst->getOperand(0); + + lldb_private::Scalar I; + + if (!frame.EvaluateValue(I, src_operand, module)) + { + if (log) + log->Printf("Couldn't evaluate %s", PrintValue(src_operand).c_str()); + error.SetErrorToGenericError(); + error.SetErrorString(bad_value_error); + return false; + } + + frame.AssignValue(inst, I, module); + + if (log) + { + log->Printf("Interpreted an IntToPtr"); + log->Printf(" Src : %s", frame.SummarizeValue(src_operand).c_str()); + log->Printf(" = : %s", frame.SummarizeValue(inst).c_str()); + } + } + break; + case Instruction::PtrToInt: + { + const PtrToIntInst *ptr_to_int_inst = dyn_cast(inst); + + if (!ptr_to_int_inst) + { + if (log) + log->Printf("getOpcode() returns PtrToInt, but instruction is not an PtrToIntInst"); + error.SetErrorToGenericError(); + error.SetErrorString(interpreter_internal_error); + return false; + } + + Value *src_operand = ptr_to_int_inst->getOperand(0); + + lldb_private::Scalar I; + + if (!frame.EvaluateValue(I, src_operand, module)) + { + if (log) + log->Printf("Couldn't evaluate %s", PrintValue(src_operand).c_str()); + error.SetErrorToGenericError(); + error.SetErrorString(bad_value_error); + return false; + } + + frame.AssignValue(inst, I, module); + + if (log) + { + log->Printf("Interpreted a PtrToInt"); + log->Printf(" Src : %s", frame.SummarizeValue(src_operand).c_str()); + log->Printf(" = : %s", frame.SummarizeValue(inst).c_str()); + } + } + break; + case Instruction::Load: + { + const LoadInst *load_inst = dyn_cast(inst); + + if (!load_inst) + { + if (log) + log->Printf("getOpcode() returns Load, but instruction is not a LoadInst"); + error.SetErrorToGenericError(); + error.SetErrorString(interpreter_internal_error); + return false; + } + + // The semantics of Load are: + // Create a region D that will contain the loaded data + // Resolve the region P containing a pointer + // Dereference P to get the region R that the data should be loaded from + // Transfer a unit of type type(D) from R to D + + const Value *pointer_operand = load_inst->getPointerOperand(); + + Type *pointer_ty = pointer_operand->getType(); + PointerType *pointer_ptr_ty = dyn_cast(pointer_ty); + if (!pointer_ptr_ty) + { + if (log) + log->Printf("getPointerOperand()->getType() is not a PointerType"); + error.SetErrorToGenericError(); + error.SetErrorString(interpreter_internal_error); + return false; + } + Type *target_ty = pointer_ptr_ty->getElementType(); + + lldb::addr_t D = frame.ResolveValue(load_inst, module); + lldb::addr_t P = frame.ResolveValue(pointer_operand, module); + + if (D == LLDB_INVALID_ADDRESS) + { + if (log) + log->Printf("LoadInst's value doesn't resolve to anything"); + error.SetErrorToGenericError(); + error.SetErrorString(bad_value_error); + return false; + } + + if (P == LLDB_INVALID_ADDRESS) + { + if (log) + log->Printf("LoadInst's pointer doesn't resolve to anything"); + error.SetErrorToGenericError(); + error.SetErrorString(bad_value_error); + return false; + } + + lldb::addr_t R; + lldb_private::Error read_error; + memory_map.ReadPointerFromMemory(&R, P, read_error); + + if (!read_error.Success()) + { + if (log) + log->Printf("Couldn't read the address to be loaded for a LoadInst"); + error.SetErrorToGenericError(); + error.SetErrorString(memory_read_error); + return false; + } + + size_t target_size = data_layout.getTypeStoreSize(target_ty); + lldb_private::DataBufferHeap buffer(target_size, 0); + + read_error.Clear(); + memory_map.ReadMemory(buffer.GetBytes(), R, buffer.GetByteSize(), read_error); + if (!read_error.Success()) + { + if (log) + log->Printf("Couldn't read from a region on behalf of a LoadInst"); + error.SetErrorToGenericError(); + error.SetErrorString(memory_read_error); + return false; + } + + lldb_private::Error write_error; + memory_map.WriteMemory(D, buffer.GetBytes(), buffer.GetByteSize(), write_error); + if (!write_error.Success()) + { + if (log) + log->Printf("Couldn't write to a region on behalf of a LoadInst"); + error.SetErrorToGenericError(); + error.SetErrorString(memory_read_error); + return false; + } + + if (log) + { + log->Printf("Interpreted a LoadInst"); + log->Printf(" P : 0x%" PRIx64, P); + log->Printf(" R : 0x%" PRIx64, R); + log->Printf(" D : 0x%" PRIx64, D); + } + } + break; + case Instruction::Ret: + { + return true; + } + case Instruction::Store: + { + const StoreInst *store_inst = dyn_cast(inst); + + if (!store_inst) + { + if (log) + log->Printf("getOpcode() returns Store, but instruction is not a StoreInst"); + error.SetErrorToGenericError(); + error.SetErrorString(interpreter_internal_error); + return false; + } + + // The semantics of Store are: + // Resolve the region D containing the data to be stored + // Resolve the region P containing a pointer + // Dereference P to get the region R that the data should be stored in + // Transfer a unit of type type(D) from D to R + + const Value *value_operand = store_inst->getValueOperand(); + const Value *pointer_operand = store_inst->getPointerOperand(); + + Type *pointer_ty = pointer_operand->getType(); + PointerType *pointer_ptr_ty = dyn_cast(pointer_ty); + if (!pointer_ptr_ty) + return false; + Type *target_ty = pointer_ptr_ty->getElementType(); + + lldb::addr_t D = frame.ResolveValue(value_operand, module); + lldb::addr_t P = frame.ResolveValue(pointer_operand, module); + + if (D == LLDB_INVALID_ADDRESS) + { + if (log) + log->Printf("StoreInst's value doesn't resolve to anything"); + error.SetErrorToGenericError(); + error.SetErrorString(bad_value_error); + return false; + } + + if (P == LLDB_INVALID_ADDRESS) + { + if (log) + log->Printf("StoreInst's pointer doesn't resolve to anything"); + error.SetErrorToGenericError(); + error.SetErrorString(bad_value_error); + return false; + } + + lldb::addr_t R; + lldb_private::Error read_error; + memory_map.ReadPointerFromMemory(&R, P, read_error); + + if (!read_error.Success()) + { + if (log) + log->Printf("Couldn't read the address to be loaded for a LoadInst"); + error.SetErrorToGenericError(); + error.SetErrorString(memory_read_error); + return false; + } + + size_t target_size = data_layout.getTypeStoreSize(target_ty); + lldb_private::DataBufferHeap buffer(target_size, 0); + + read_error.Clear(); + memory_map.ReadMemory(buffer.GetBytes(), D, buffer.GetByteSize(), read_error); + if (!read_error.Success()) + { + if (log) + log->Printf("Couldn't read from a region on behalf of a StoreInst"); + error.SetErrorToGenericError(); + error.SetErrorString(memory_read_error); + return false; + } + + lldb_private::Error write_error; + memory_map.WriteMemory(R, buffer.GetBytes(), buffer.GetByteSize(), write_error); + if (!write_error.Success()) + { + if (log) + log->Printf("Couldn't write to a region on behalf of a StoreInst"); + error.SetErrorToGenericError(); + error.SetErrorString(memory_write_error); + return false; + } + + if (log) + { + log->Printf("Interpreted a StoreInst"); + log->Printf(" D : 0x%" PRIx64, D); + log->Printf(" P : 0x%" PRIx64, P); + log->Printf(" R : 0x%" PRIx64, R); + } + } + break; + } + + ++frame.m_ii; + } + + if (num_insts >= 4096) + { + error.SetErrorToGenericError(); + error.SetErrorString(infinite_loop_error); + return false; + } + + return false; +} diff --git a/contrib/llvm/tools/lldb/source/Expression/IRMemoryMap.cpp b/contrib/llvm/tools/lldb/source/Expression/IRMemoryMap.cpp new file mode 100644 index 00000000000..ef362baff16 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Expression/IRMemoryMap.cpp @@ -0,0 +1,758 @@ +//===-- IRMemoryMap.cpp -----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Core/DataBufferHeap.h" +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/Error.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Scalar.h" +#include "lldb/Expression/IRMemoryMap.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Target.h" + +using namespace lldb_private; + +IRMemoryMap::IRMemoryMap (lldb::TargetSP target_sp) : + m_target_wp(target_sp) +{ + if (target_sp) + m_process_wp = target_sp->GetProcessSP(); +} + +IRMemoryMap::~IRMemoryMap () +{ + lldb::ProcessSP process_sp = m_process_wp.lock(); + + if (process_sp) + { + AllocationMap::iterator iter; + + Error err; + + while ((iter = m_allocations.begin()) != m_allocations.end()) + { + err.Clear(); + if (iter->second.m_leak) + m_allocations.erase(iter); + else + Free(iter->first, err); + } + } +} + +lldb::addr_t +IRMemoryMap::FindSpace (size_t size) +{ + lldb::TargetSP target_sp = m_target_wp.lock(); + lldb::ProcessSP process_sp = m_process_wp.lock(); + + lldb::addr_t ret = LLDB_INVALID_ADDRESS; + + if (process_sp && process_sp->CanJIT() && process_sp->IsAlive()) + { + Error alloc_error; + + ret = process_sp->AllocateMemory(size, lldb::ePermissionsReadable | lldb::ePermissionsWritable, alloc_error); + + if (!alloc_error.Success()) + return LLDB_INVALID_ADDRESS; + else + return ret; + } + + for (int iterations = 0; iterations < 16; ++iterations) + { + lldb::addr_t candidate = LLDB_INVALID_ADDRESS; + + switch (target_sp->GetArchitecture().GetAddressByteSize()) + { + case 4: + { + uint32_t random_data = random(); + candidate = random_data; + candidate &= ~0xfffull; + break; + } + case 8: + { + uint32_t random_low = random(); + uint32_t random_high = random(); + candidate = random_high; + candidate <<= 32ull; + candidate |= random_low; + candidate &= ~0xfffull; + break; + } + } + + if (IntersectsAllocation(candidate, size)) + continue; + + ret = candidate; + + return ret; + } + + return ret; +} + +IRMemoryMap::AllocationMap::iterator +IRMemoryMap::FindAllocation (lldb::addr_t addr, size_t size) +{ + if (addr == LLDB_INVALID_ADDRESS) + return m_allocations.end(); + + AllocationMap::iterator iter = m_allocations.lower_bound (addr); + + if (iter == m_allocations.end() || + iter->first > addr) + { + if (iter == m_allocations.begin()) + return m_allocations.end(); + iter--; + } + + if (iter->first <= addr && iter->first + iter->second.m_size >= addr + size) + return iter; + + return m_allocations.end(); +} + +bool +IRMemoryMap::IntersectsAllocation (lldb::addr_t addr, size_t size) +{ + if (addr == LLDB_INVALID_ADDRESS) + return false; + + AllocationMap::iterator iter = m_allocations.lower_bound (addr); + + if (iter == m_allocations.end() || + iter->first > addr) + { + if (iter == m_allocations.begin()) + return false; + + iter--; + } + + while (iter != m_allocations.end() && iter->second.m_process_alloc < addr + size) + { + if (iter->second.m_process_start + iter->second.m_size > addr) + return true; + + ++iter; + } + + return false; +} + +lldb::ByteOrder +IRMemoryMap::GetByteOrder() +{ + lldb::ProcessSP process_sp = m_process_wp.lock(); + + if (process_sp) + return process_sp->GetByteOrder(); + + lldb::TargetSP target_sp = m_target_wp.lock(); + + if (target_sp) + return target_sp->GetArchitecture().GetByteOrder(); + + return lldb::eByteOrderInvalid; +} + +uint32_t +IRMemoryMap::GetAddressByteSize() +{ + lldb::ProcessSP process_sp = m_process_wp.lock(); + + if (process_sp) + return process_sp->GetAddressByteSize(); + + lldb::TargetSP target_sp = m_target_wp.lock(); + + if (target_sp) + return target_sp->GetArchitecture().GetAddressByteSize(); + + return UINT32_MAX; +} + +ExecutionContextScope * +IRMemoryMap::GetBestExecutionContextScope() +{ + lldb::ProcessSP process_sp = m_process_wp.lock(); + + if (process_sp) + return process_sp.get(); + + lldb::TargetSP target_sp = m_target_wp.lock(); + + if (target_sp) + return target_sp.get(); + + return NULL; +} + +IRMemoryMap::Allocation::Allocation (lldb::addr_t process_alloc, + lldb::addr_t process_start, + size_t size, + uint32_t permissions, + uint8_t alignment, + AllocationPolicy policy) : + m_process_alloc (process_alloc), + m_process_start (process_start), + m_size (size), + m_permissions (permissions), + m_alignment (alignment), + m_policy (policy), + m_leak (false) +{ + switch (policy) + { + default: + assert (0 && "We cannot reach this!"); + case eAllocationPolicyHostOnly: + m_data.SetByteSize(size); + memset(m_data.GetBytes(), 0, size); + break; + case eAllocationPolicyProcessOnly: + break; + case eAllocationPolicyMirror: + m_data.SetByteSize(size); + memset(m_data.GetBytes(), 0, size); + break; + } +} + +lldb::addr_t +IRMemoryMap::Malloc (size_t size, uint8_t alignment, uint32_t permissions, AllocationPolicy policy, Error &error) +{ + error.Clear(); + + lldb::ProcessSP process_sp; + lldb::addr_t allocation_address = LLDB_INVALID_ADDRESS; + lldb::addr_t aligned_address = LLDB_INVALID_ADDRESS; + + size_t alignment_mask = alignment - 1; + size_t allocation_size; + + if (size == 0) + allocation_size = alignment; + else + allocation_size = (size & alignment_mask) ? ((size + alignment) & (~alignment_mask)) : size; + + switch (policy) + { + default: + error.SetErrorToGenericError(); + error.SetErrorString("Couldn't malloc: invalid allocation policy"); + return LLDB_INVALID_ADDRESS; + case eAllocationPolicyHostOnly: + allocation_address = FindSpace(allocation_size); + if (allocation_address == LLDB_INVALID_ADDRESS) + { + error.SetErrorToGenericError(); + error.SetErrorString("Couldn't malloc: address space is full"); + return LLDB_INVALID_ADDRESS; + } + break; + case eAllocationPolicyMirror: + process_sp = m_process_wp.lock(); + if (process_sp && process_sp->CanJIT() && process_sp->IsAlive()) + { + allocation_address = process_sp->AllocateMemory(allocation_size, permissions, error); + if (!error.Success()) + return LLDB_INVALID_ADDRESS; + } + else + { + policy = eAllocationPolicyHostOnly; + allocation_address = FindSpace(allocation_size); + if (allocation_address == LLDB_INVALID_ADDRESS) + { + error.SetErrorToGenericError(); + error.SetErrorString("Couldn't malloc: address space is full"); + return LLDB_INVALID_ADDRESS; + } + } + break; + case eAllocationPolicyProcessOnly: + process_sp = m_process_wp.lock(); + if (process_sp) + { + if (process_sp->CanJIT() && process_sp->IsAlive()) + { + allocation_address = process_sp->AllocateMemory(allocation_size, permissions, error); + if (!error.Success()) + return LLDB_INVALID_ADDRESS; + } + else + { + error.SetErrorToGenericError(); + error.SetErrorString("Couldn't malloc: process doesn't support allocating memory"); + return LLDB_INVALID_ADDRESS; + } + } + else + { + error.SetErrorToGenericError(); + error.SetErrorString("Couldn't malloc: process doesn't exist, and this memory must be in the process"); + return LLDB_INVALID_ADDRESS; + } + break; + } + + + lldb::addr_t mask = alignment - 1; + aligned_address = (allocation_address + mask) & (~mask); + + m_allocations[aligned_address] = Allocation(allocation_address, + aligned_address, + allocation_size, + permissions, + alignment, + policy); + + if (lldb_private::Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)) + { + const char * policy_string; + + switch (policy) + { + default: + policy_string = ""; + break; + case eAllocationPolicyHostOnly: + policy_string = "eAllocationPolicyHostOnly"; + break; + case eAllocationPolicyProcessOnly: + policy_string = "eAllocationPolicyProcessOnly"; + break; + case eAllocationPolicyMirror: + policy_string = "eAllocationPolicyMirror"; + break; + } + + log->Printf("IRMemoryMap::Malloc (%" PRIu64 ", 0x%" PRIx64 ", 0x%" PRIx64 ", %s) -> 0x%" PRIx64, + (uint64_t)allocation_size, + (uint64_t)alignment, + (uint64_t)permissions, + policy_string, + aligned_address); + } + + return aligned_address; +} + +void +IRMemoryMap::Leak (lldb::addr_t process_address, Error &error) +{ + error.Clear(); + + AllocationMap::iterator iter = m_allocations.find(process_address); + + if (iter == m_allocations.end()) + { + error.SetErrorToGenericError(); + error.SetErrorString("Couldn't leak: allocation doesn't exist"); + return; + } + + Allocation &allocation = iter->second; + + allocation.m_leak = true; +} + +void +IRMemoryMap::Free (lldb::addr_t process_address, Error &error) +{ + error.Clear(); + + AllocationMap::iterator iter = m_allocations.find(process_address); + + if (iter == m_allocations.end()) + { + error.SetErrorToGenericError(); + error.SetErrorString("Couldn't free: allocation doesn't exist"); + return; + } + + Allocation &allocation = iter->second; + + switch (allocation.m_policy) + { + default: + case eAllocationPolicyHostOnly: + { + lldb::ProcessSP process_sp = m_process_wp.lock(); + if (process_sp) + { + if (process_sp->CanJIT() && process_sp->IsAlive()) + process_sp->DeallocateMemory(allocation.m_process_alloc); // FindSpace allocated this for real + } + + break; + } + case eAllocationPolicyMirror: + case eAllocationPolicyProcessOnly: + { + lldb::ProcessSP process_sp = m_process_wp.lock(); + if (process_sp) + process_sp->DeallocateMemory(allocation.m_process_alloc); + } + } + + if (lldb_private::Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)) + { + log->Printf("IRMemoryMap::Free (0x%" PRIx64 ") freed [0x%" PRIx64 "..0x%" PRIx64 ")", + (uint64_t)process_address, + iter->second.m_process_start, + iter->second.m_process_start + iter->second.m_size); + } + + m_allocations.erase(iter); +} + +void +IRMemoryMap::WriteMemory (lldb::addr_t process_address, const uint8_t *bytes, size_t size, Error &error) +{ + error.Clear(); + + AllocationMap::iterator iter = FindAllocation(process_address, size); + + if (iter == m_allocations.end()) + { + lldb::ProcessSP process_sp = m_process_wp.lock(); + + if (process_sp) + { + process_sp->WriteMemory(process_address, bytes, size, error); + return; + } + + error.SetErrorToGenericError(); + error.SetErrorString("Couldn't write: no allocation contains the target range and the process doesn't exist"); + return; + } + + Allocation &allocation = iter->second; + + uint64_t offset = process_address - allocation.m_process_start; + + lldb::ProcessSP process_sp; + + switch (allocation.m_policy) + { + default: + error.SetErrorToGenericError(); + error.SetErrorString("Couldn't write: invalid allocation policy"); + return; + case eAllocationPolicyHostOnly: + if (!allocation.m_data.GetByteSize()) + { + error.SetErrorToGenericError(); + error.SetErrorString("Couldn't write: data buffer is empty"); + return; + } + ::memcpy (allocation.m_data.GetBytes() + offset, bytes, size); + break; + case eAllocationPolicyMirror: + if (!allocation.m_data.GetByteSize()) + { + error.SetErrorToGenericError(); + error.SetErrorString("Couldn't write: data buffer is empty"); + return; + } + ::memcpy (allocation.m_data.GetBytes() + offset, bytes, size); + process_sp = m_process_wp.lock(); + if (process_sp) + { + process_sp->WriteMemory(process_address, bytes, size, error); + if (!error.Success()) + return; + } + break; + case eAllocationPolicyProcessOnly: + process_sp = m_process_wp.lock(); + if (process_sp) + { + process_sp->WriteMemory(process_address, bytes, size, error); + if (!error.Success()) + return; + } + break; + } + + if (lldb_private::Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)) + { + log->Printf("IRMemoryMap::WriteMemory (0x%" PRIx64 ", 0x%" PRIx64 ", 0x%" PRId64 ") went to [0x%" PRIx64 "..0x%" PRIx64 ")", + (uint64_t)process_address, + (uint64_t)bytes, + (uint64_t)size, + (uint64_t)allocation.m_process_start, + (uint64_t)allocation.m_process_start + (uint64_t)allocation.m_size); + } +} + +void +IRMemoryMap::WriteScalarToMemory (lldb::addr_t process_address, Scalar &scalar, size_t size, Error &error) +{ + error.Clear(); + + if (size == UINT32_MAX) + size = scalar.GetByteSize(); + + if (size > 0) + { + uint8_t buf[32]; + const size_t mem_size = scalar.GetAsMemoryData (buf, size, GetByteOrder(), error); + if (mem_size > 0) + { + return WriteMemory(process_address, buf, mem_size, error); + } + else + { + error.SetErrorToGenericError(); + error.SetErrorString ("Couldn't write scalar: failed to get scalar as memory data"); + } + } + else + { + error.SetErrorToGenericError(); + error.SetErrorString ("Couldn't write scalar: its size was zero"); + } + return; +} + +void +IRMemoryMap::WritePointerToMemory (lldb::addr_t process_address, lldb::addr_t address, Error &error) +{ + error.Clear(); + + Scalar scalar(address); + + WriteScalarToMemory(process_address, scalar, GetAddressByteSize(), error); +} + +void +IRMemoryMap::ReadMemory (uint8_t *bytes, lldb::addr_t process_address, size_t size, Error &error) +{ + error.Clear(); + + AllocationMap::iterator iter = FindAllocation(process_address, size); + + if (iter == m_allocations.end()) + { + lldb::ProcessSP process_sp = m_process_wp.lock(); + + if (process_sp) + { + process_sp->ReadMemory(process_address, bytes, size, error); + return; + } + + lldb::TargetSP target_sp = m_target_wp.lock(); + + if (target_sp) + { + Address absolute_address(process_address); + target_sp->ReadMemory(absolute_address, false, bytes, size, error); + return; + } + + error.SetErrorToGenericError(); + error.SetErrorString("Couldn't read: no allocation contains the target range, and neither the process nor the target exist"); + return; + } + + Allocation &allocation = iter->second; + + uint64_t offset = process_address - allocation.m_process_start; + + lldb::ProcessSP process_sp; + + switch (allocation.m_policy) + { + default: + error.SetErrorToGenericError(); + error.SetErrorString("Couldn't read: invalid allocation policy"); + return; + case eAllocationPolicyHostOnly: + if (!allocation.m_data.GetByteSize()) + { + error.SetErrorToGenericError(); + error.SetErrorString("Couldn't read: data buffer is empty"); + return; + } + ::memcpy (bytes, allocation.m_data.GetBytes() + offset, size); + break; + case eAllocationPolicyMirror: + process_sp = m_process_wp.lock(); + if (process_sp) + { + process_sp->ReadMemory(process_address, bytes, size, error); + if (!error.Success()) + return; + } + else + { + if (!allocation.m_data.GetByteSize()) + { + error.SetErrorToGenericError(); + error.SetErrorString("Couldn't read: data buffer is empty"); + return; + } + ::memcpy (bytes, allocation.m_data.GetBytes() + offset, size); + } + break; + case eAllocationPolicyProcessOnly: + process_sp = m_process_wp.lock(); + if (process_sp) + { + process_sp->ReadMemory(process_address, bytes, size, error); + if (!error.Success()) + return; + } + break; + } + + if (lldb_private::Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)) + { + log->Printf("IRMemoryMap::ReadMemory (0x%" PRIx64 ", 0x%" PRIx64 ", 0x%" PRId64 ") came from [0x%" PRIx64 "..0x%" PRIx64 ")", + (uint64_t)process_address, + (uint64_t)bytes, + (uint64_t)size, + (uint64_t)allocation.m_process_start, + (uint64_t)allocation.m_process_start + (uint64_t)allocation.m_size); + } +} + +void +IRMemoryMap::ReadScalarFromMemory (Scalar &scalar, lldb::addr_t process_address, size_t size, Error &error) +{ + error.Clear(); + + if (size > 0) + { + DataBufferHeap buf(size, 0); + ReadMemory(buf.GetBytes(), process_address, size, error); + + if (!error.Success()) + return; + + DataExtractor extractor(buf.GetBytes(), buf.GetByteSize(), GetByteOrder(), GetAddressByteSize()); + + lldb::offset_t offset = 0; + + switch (size) + { + default: + error.SetErrorToGenericError(); + error.SetErrorStringWithFormat("Couldn't read scalar: unsupported size %" PRIu64, (uint64_t)size); + return; + case 1: scalar = extractor.GetU8(&offset); break; + case 2: scalar = extractor.GetU16(&offset); break; + case 4: scalar = extractor.GetU32(&offset); break; + case 8: scalar = extractor.GetU64(&offset); break; + } + } + else + { + error.SetErrorToGenericError(); + error.SetErrorString ("Couldn't read scalar: its size was zero"); + } + return; +} + +void +IRMemoryMap::ReadPointerFromMemory (lldb::addr_t *address, lldb::addr_t process_address, Error &error) +{ + error.Clear(); + + Scalar pointer_scalar; + ReadScalarFromMemory(pointer_scalar, process_address, GetAddressByteSize(), error); + + if (!error.Success()) + return; + + *address = pointer_scalar.ULongLong(); + + return; +} + +void +IRMemoryMap::GetMemoryData (DataExtractor &extractor, lldb::addr_t process_address, size_t size, Error &error) +{ + error.Clear(); + + if (size > 0) + { + AllocationMap::iterator iter = FindAllocation(process_address, size); + + if (iter == m_allocations.end()) + { + error.SetErrorToGenericError(); + error.SetErrorStringWithFormat("Couldn't find an allocation containing [0x%" PRIx64 "..0x%" PRIx64 ")", process_address, process_address + size); + return; + } + + Allocation &allocation = iter->second; + + switch (allocation.m_policy) + { + default: + error.SetErrorToGenericError(); + error.SetErrorString("Couldn't get memory data: invalid allocation policy"); + return; + case eAllocationPolicyProcessOnly: + error.SetErrorToGenericError(); + error.SetErrorString("Couldn't get memory data: memory is only in the target"); + return; + case eAllocationPolicyMirror: + { + lldb::ProcessSP process_sp = m_process_wp.lock(); + + if (!allocation.m_data.GetByteSize()) + { + error.SetErrorToGenericError(); + error.SetErrorString("Couldn't get memory data: data buffer is empty"); + return; + } + if (process_sp) + { + process_sp->ReadMemory(allocation.m_process_start, allocation.m_data.GetBytes(), allocation.m_data.GetByteSize(), error); + if (!error.Success()) + return; + uint64_t offset = process_address - allocation.m_process_start; + extractor = DataExtractor(allocation.m_data.GetBytes() + offset, size, GetByteOrder(), GetAddressByteSize()); + return; + } + } + case eAllocationPolicyHostOnly: + if (!allocation.m_data.GetByteSize()) + { + error.SetErrorToGenericError(); + error.SetErrorString("Couldn't get memory data: data buffer is empty"); + return; + } + uint64_t offset = process_address - allocation.m_process_start; + extractor = DataExtractor(allocation.m_data.GetBytes() + offset, size, GetByteOrder(), GetAddressByteSize()); + return; + } + } + else + { + error.SetErrorToGenericError(); + error.SetErrorString ("Couldn't get memory data: its size was zero"); + return; + } +} + + diff --git a/contrib/llvm/tools/lldb/source/Expression/Materializer.cpp b/contrib/llvm/tools/lldb/source/Expression/Materializer.cpp new file mode 100644 index 00000000000..8a1900ebb73 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Expression/Materializer.cpp @@ -0,0 +1,1414 @@ +//===-- Materializer.cpp ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Core/Log.h" +#include "lldb/Core/RegisterValue.h" +#include "lldb/Core/ValueObjectConstResult.h" +#include "lldb/Core/ValueObjectVariable.h" +#include "lldb/Expression/ClangExpressionVariable.h" +#include "lldb/Expression/Materializer.h" +#include "lldb/Symbol/ClangASTContext.h" +#include "lldb/Symbol/Symbol.h" +#include "lldb/Symbol/Type.h" +#include "lldb/Symbol/Variable.h" +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/StackFrame.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" + +using namespace lldb_private; + +uint32_t +Materializer::AddStructMember (Entity &entity) +{ + uint32_t size = entity.GetSize(); + uint32_t alignment = entity.GetAlignment(); + + uint32_t ret; + + if (m_current_offset == 0) + m_struct_alignment = alignment; + + if (m_current_offset % alignment) + m_current_offset += (alignment - (m_current_offset % alignment)); + + ret = m_current_offset; + + m_current_offset += size; + + return ret; +} + +void +Materializer::Entity::SetSizeAndAlignmentFromType (ClangASTType &type) +{ + m_size = type.GetByteSize(); + + uint32_t bit_alignment = type.GetTypeBitAlign(); + + if (bit_alignment % 8) + { + bit_alignment += 8; + bit_alignment &= ~((uint32_t)0x111u); + } + + m_alignment = bit_alignment / 8; +} + +class EntityPersistentVariable : public Materializer::Entity +{ +public: + EntityPersistentVariable (lldb::ClangExpressionVariableSP &persistent_variable_sp) : + Entity(), + m_persistent_variable_sp(persistent_variable_sp) + { + // Hard-coding to maximum size of a pointer since persistent variables are materialized by reference + m_size = 8; + m_alignment = 8; + } + + void MakeAllocation (IRMemoryMap &map, Error &err) + { + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + // Allocate a spare memory area to store the persistent variable's contents. + + Error allocate_error; + + lldb::addr_t mem = map.Malloc(m_persistent_variable_sp->GetByteSize(), + 8, + lldb::ePermissionsReadable | lldb::ePermissionsWritable, + IRMemoryMap::eAllocationPolicyMirror, + allocate_error); + + if (!allocate_error.Success()) + { + err.SetErrorStringWithFormat("couldn't allocate a memory area to store %s: %s", m_persistent_variable_sp->GetName().GetCString(), allocate_error.AsCString()); + return; + } + + if (log) + log->Printf("Allocated %s (0x%" PRIx64 ") sucessfully", m_persistent_variable_sp->GetName().GetCString(), mem); + + // Put the location of the spare memory into the live data of the ValueObject. + + m_persistent_variable_sp->m_live_sp = ValueObjectConstResult::Create (map.GetBestExecutionContextScope(), + m_persistent_variable_sp->GetTypeFromUser(), + m_persistent_variable_sp->GetName(), + mem, + eAddressTypeLoad, + m_persistent_variable_sp->GetByteSize()); + + // Clear the flag if the variable will never be deallocated. + + if (m_persistent_variable_sp->m_flags & ClangExpressionVariable::EVKeepInTarget) + { + Error leak_error; + map.Leak(mem, leak_error); + m_persistent_variable_sp->m_flags &= ~ClangExpressionVariable::EVNeedsAllocation; + } + + // Write the contents of the variable to the area. + + Error write_error; + + map.WriteMemory (mem, + m_persistent_variable_sp->GetValueBytes(), + m_persistent_variable_sp->GetByteSize(), + write_error); + + if (!write_error.Success()) + { + err.SetErrorStringWithFormat ("couldn't write %s to the target: %s", m_persistent_variable_sp->GetName().AsCString(), + write_error.AsCString()); + return; + } + } + + void DestroyAllocation (IRMemoryMap &map, Error &err) + { + Error deallocate_error; + + map.Free((lldb::addr_t)m_persistent_variable_sp->m_live_sp->GetValue().GetScalar().ULongLong(), deallocate_error); + + m_persistent_variable_sp->m_live_sp.reset(); + + if (!deallocate_error.Success()) + { + err.SetErrorStringWithFormat ("couldn't deallocate memory for %s: %s", m_persistent_variable_sp->GetName().GetCString(), deallocate_error.AsCString()); + } + } + + void Materialize (lldb::StackFrameSP &frame_sp, IRMemoryMap &map, lldb::addr_t process_address, Error &err) + { + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + const lldb::addr_t load_addr = process_address + m_offset; + + if (log) + { + log->Printf("EntityPersistentVariable::Materialize [address = 0x%" PRIx64 ", m_name = %s, m_flags = 0x%hx]", + (uint64_t)load_addr, + m_persistent_variable_sp->GetName().AsCString(), + m_persistent_variable_sp->m_flags); + } + + if (m_persistent_variable_sp->m_flags & ClangExpressionVariable::EVNeedsAllocation) + { + MakeAllocation(map, err); + m_persistent_variable_sp->m_flags |= ClangExpressionVariable::EVIsLLDBAllocated; + + if (!err.Success()) + return; + } + + if ((m_persistent_variable_sp->m_flags & ClangExpressionVariable::EVIsProgramReference && m_persistent_variable_sp->m_live_sp) || + m_persistent_variable_sp->m_flags & ClangExpressionVariable::EVIsLLDBAllocated) + { + Error write_error; + + map.WriteScalarToMemory(load_addr, + m_persistent_variable_sp->m_live_sp->GetValue().GetScalar(), + map.GetAddressByteSize(), + write_error); + + if (!write_error.Success()) + { + err.SetErrorStringWithFormat("couldn't write the location of %s to memory: %s", m_persistent_variable_sp->GetName().AsCString(), write_error.AsCString()); + } + } + else + { + err.SetErrorStringWithFormat("no materialization happened for persistent variable %s", m_persistent_variable_sp->GetName().AsCString()); + return; + } + } + + void Dematerialize (lldb::StackFrameSP &frame_sp, + IRMemoryMap &map, + lldb::addr_t process_address, + lldb::addr_t frame_top, + lldb::addr_t frame_bottom, + Error &err) + { + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + const lldb::addr_t load_addr = process_address + m_offset; + + if (log) + { + log->Printf("EntityPersistentVariable::Dematerialize [address = 0x%" PRIx64 ", m_name = %s, m_flags = 0x%hx]", + (uint64_t)process_address + m_offset, + m_persistent_variable_sp->GetName().AsCString(), + m_persistent_variable_sp->m_flags); + } + + if ((m_persistent_variable_sp->m_flags & ClangExpressionVariable::EVIsLLDBAllocated) || + (m_persistent_variable_sp->m_flags & ClangExpressionVariable::EVIsProgramReference)) + { + if (m_persistent_variable_sp->m_flags & ClangExpressionVariable::EVIsProgramReference && + !m_persistent_variable_sp->m_live_sp) + { + // If the reference comes from the program, then the ClangExpressionVariable's + // live variable data hasn't been set up yet. Do this now. + + lldb::addr_t location; + Error read_error; + + map.ReadPointerFromMemory(&location, load_addr, read_error); + + if (!read_error.Success()) + { + err.SetErrorStringWithFormat("couldn't read the address of program-allocated variable %s: %s", m_persistent_variable_sp->GetName().GetCString(), read_error.AsCString()); + return; + } + + m_persistent_variable_sp->m_live_sp = ValueObjectConstResult::Create (map.GetBestExecutionContextScope (), + m_persistent_variable_sp->GetTypeFromUser(), + m_persistent_variable_sp->GetName(), + location, + eAddressTypeLoad, + m_persistent_variable_sp->GetByteSize()); + + if (frame_top != LLDB_INVALID_ADDRESS && + frame_bottom != LLDB_INVALID_ADDRESS && + location >= frame_bottom && + location <= frame_top) + { + // If the variable is resident in the stack frame created by the expression, + // then it cannot be relied upon to stay around. We treat it as needing + // reallocation. + m_persistent_variable_sp->m_flags |= ClangExpressionVariable::EVIsLLDBAllocated; + m_persistent_variable_sp->m_flags |= ClangExpressionVariable::EVNeedsAllocation; + m_persistent_variable_sp->m_flags |= ClangExpressionVariable::EVNeedsFreezeDry; + m_persistent_variable_sp->m_flags &= ~ClangExpressionVariable::EVIsProgramReference; + } + } + + lldb::addr_t mem = m_persistent_variable_sp->m_live_sp->GetValue().GetScalar().ULongLong(); + + if (!m_persistent_variable_sp->m_live_sp) + { + err.SetErrorStringWithFormat("couldn't find the memory area used to store %s", m_persistent_variable_sp->GetName().GetCString()); + return; + } + + if (m_persistent_variable_sp->m_live_sp->GetValue().GetValueAddressType() != eAddressTypeLoad) + { + err.SetErrorStringWithFormat("the address of the memory area for %s is in an incorrect format", m_persistent_variable_sp->GetName().GetCString()); + return; + } + + if (m_persistent_variable_sp->m_flags & ClangExpressionVariable::EVNeedsFreezeDry || + m_persistent_variable_sp->m_flags & ClangExpressionVariable::EVKeepInTarget) + { + if (log) + log->Printf("Dematerializing %s from 0x%" PRIx64 " (size = %llu)", m_persistent_variable_sp->GetName().GetCString(), (uint64_t)mem, (unsigned long long)m_persistent_variable_sp->GetByteSize()); + + // Read the contents of the spare memory area + + m_persistent_variable_sp->ValueUpdated (); + + Error read_error; + + map.ReadMemory(m_persistent_variable_sp->GetValueBytes(), + mem, + m_persistent_variable_sp->GetByteSize(), + read_error); + + if (!read_error.Success()) + { + err.SetErrorStringWithFormat ("couldn't read the contents of %s from memory: %s", m_persistent_variable_sp->GetName().GetCString(), read_error.AsCString()); + return; + } + + m_persistent_variable_sp->m_flags &= ~ClangExpressionVariable::EVNeedsFreezeDry; + } + } + else + { + err.SetErrorStringWithFormat("no dematerialization happened for persistent variable %s", m_persistent_variable_sp->GetName().AsCString()); + return; + } + + lldb::ProcessSP process_sp = map.GetBestExecutionContextScope()->CalculateProcess(); + if (!process_sp || + !process_sp->CanJIT()) + { + // Allocations are not persistent so persistent variables cannot stay materialized. + + m_persistent_variable_sp->m_flags |= ClangExpressionVariable::EVNeedsAllocation; + + DestroyAllocation(map, err); + if (!err.Success()) + return; + } + else if (m_persistent_variable_sp->m_flags & ClangExpressionVariable::EVNeedsAllocation && + !(m_persistent_variable_sp->m_flags & ClangExpressionVariable::EVKeepInTarget)) + { + DestroyAllocation(map, err); + if (!err.Success()) + return; + } + } + + void DumpToLog (IRMemoryMap &map, lldb::addr_t process_address, Log *log) + { + StreamString dump_stream; + + Error err; + + const lldb::addr_t load_addr = process_address + m_offset; + + dump_stream.Printf("0x%" PRIx64 ": EntityPersistentVariable (%s)\n", load_addr, m_persistent_variable_sp->GetName().AsCString()); + + { + dump_stream.Printf("Pointer:\n"); + + DataBufferHeap data (m_size, 0); + + map.ReadMemory(data.GetBytes(), load_addr, m_size, err); + + if (!err.Success()) + { + dump_stream.Printf(" \n"); + } + else + { + DataExtractor extractor (data.GetBytes(), data.GetByteSize(), map.GetByteOrder(), map.GetAddressByteSize()); + + extractor.DumpHexBytes(&dump_stream, data.GetBytes(), data.GetByteSize(), 16, load_addr); + + dump_stream.PutChar('\n'); + } + } + + { + dump_stream.Printf("Target:\n"); + + lldb::addr_t target_address; + + map.ReadPointerFromMemory (&target_address, load_addr, err); + + if (!err.Success()) + { + dump_stream.Printf(" \n"); + } + else + { + DataBufferHeap data (m_persistent_variable_sp->GetByteSize(), 0); + + map.ReadMemory(data.GetBytes(), target_address, m_persistent_variable_sp->GetByteSize(), err); + + if (!err.Success()) + { + dump_stream.Printf(" \n"); + } + else + { + DataExtractor extractor (data.GetBytes(), data.GetByteSize(), map.GetByteOrder(), map.GetAddressByteSize()); + + extractor.DumpHexBytes(&dump_stream, data.GetBytes(), data.GetByteSize(), 16, target_address); + + dump_stream.PutChar('\n'); + } + } + } + + log->PutCString(dump_stream.GetData()); + } + + void Wipe (IRMemoryMap &map, lldb::addr_t process_address) + { + } +private: + lldb::ClangExpressionVariableSP m_persistent_variable_sp; +}; + +uint32_t +Materializer::AddPersistentVariable (lldb::ClangExpressionVariableSP &persistent_variable_sp, Error &err) +{ + EntityVector::iterator iter = m_entities.insert(m_entities.end(), EntityUP()); + iter->reset (new EntityPersistentVariable (persistent_variable_sp)); + uint32_t ret = AddStructMember(**iter); + (*iter)->SetOffset(ret); + return ret; +} + +class EntityVariable : public Materializer::Entity +{ +public: + EntityVariable (lldb::VariableSP &variable_sp) : + Entity(), + m_variable_sp(variable_sp), + m_is_reference(false), + m_temporary_allocation(LLDB_INVALID_ADDRESS), + m_temporary_allocation_size(0) + { + // Hard-coding to maximum size of a pointer since all variables are materialized by reference + m_size = 8; + m_alignment = 8; + m_is_reference = m_variable_sp->GetType()->GetClangForwardType().IsReferenceType(); + } + + void Materialize (lldb::StackFrameSP &frame_sp, IRMemoryMap &map, lldb::addr_t process_address, Error &err) + { + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + const lldb::addr_t load_addr = process_address + m_offset; + if (log) + { + log->Printf("EntityVariable::Materialize [address = 0x%" PRIx64 ", m_variable_sp = %s]", + (uint64_t)load_addr, + m_variable_sp->GetName().AsCString()); + } + + ExecutionContextScope *scope = frame_sp.get(); + + if (!scope) + scope = map.GetBestExecutionContextScope(); + + lldb::ValueObjectSP valobj_sp = ValueObjectVariable::Create(scope, m_variable_sp); + + if (!valobj_sp) + { + err.SetErrorStringWithFormat("couldn't get a value object for variable %s", m_variable_sp->GetName().AsCString()); + return; + } + + if (m_is_reference) + { + DataExtractor valobj_extractor; + valobj_sp->GetData(valobj_extractor); + lldb::offset_t offset = 0; + lldb::addr_t reference_addr = valobj_extractor.GetAddress(&offset); + + Error write_error; + map.WritePointerToMemory(load_addr, reference_addr, write_error); + + if (!write_error.Success()) + { + err.SetErrorStringWithFormat("couldn't write the contents of reference variable %s to memory: %s", m_variable_sp->GetName().AsCString(), write_error.AsCString()); + return; + } + } + else + { + Error get_address_error; + lldb::ValueObjectSP addr_of_valobj_sp = valobj_sp->AddressOf(get_address_error); + if (get_address_error.Success()) + { + DataExtractor valobj_extractor; + addr_of_valobj_sp->GetData(valobj_extractor); + lldb::offset_t offset = 0; + lldb::addr_t addr_of_valobj_addr = valobj_extractor.GetAddress(&offset); + + Error write_error; + map.WritePointerToMemory(load_addr, addr_of_valobj_addr, write_error); + + if (!write_error.Success()) + { + err.SetErrorStringWithFormat("couldn't write the address of variable %s to memory: %s", m_variable_sp->GetName().AsCString(), write_error.AsCString()); + return; + } + } + else + { + DataExtractor data; + valobj_sp->GetData(data); + + if (m_temporary_allocation != LLDB_INVALID_ADDRESS) + { + err.SetErrorStringWithFormat("trying to create a temporary region for %s but one exists", m_variable_sp->GetName().AsCString()); + return; + } + + if (data.GetByteSize() != m_variable_sp->GetType()->GetByteSize()) + { + if (data.GetByteSize() == 0 && m_variable_sp->LocationExpression().IsValid() == false) + { + err.SetErrorStringWithFormat("the variable '%s' has no location, it may have been optimized out", m_variable_sp->GetName().AsCString()); + } + else + { + err.SetErrorStringWithFormat("size of variable %s disagrees with the ValueObject's size", m_variable_sp->GetName().AsCString()); + } + return; + } + + size_t bit_align = m_variable_sp->GetType()->GetClangLayoutType().GetTypeBitAlign(); + size_t byte_align = (bit_align + 7) / 8; + + Error alloc_error; + + m_temporary_allocation = map.Malloc(data.GetByteSize(), byte_align, lldb::ePermissionsReadable | lldb::ePermissionsWritable, IRMemoryMap::eAllocationPolicyMirror, alloc_error); + m_temporary_allocation_size = data.GetByteSize(); + + if (!alloc_error.Success()) + { + err.SetErrorStringWithFormat("couldn't allocate a temporary region for %s: %s", m_variable_sp->GetName().AsCString(), alloc_error.AsCString()); + return; + } + + Error write_error; + + map.WriteMemory(m_temporary_allocation, data.GetDataStart(), data.GetByteSize(), write_error); + + if (!write_error.Success()) + { + err.SetErrorStringWithFormat("couldn't write to the temporary region for %s: %s", m_variable_sp->GetName().AsCString(), write_error.AsCString()); + return; + } + + Error pointer_write_error; + + map.WritePointerToMemory(load_addr, m_temporary_allocation, pointer_write_error); + + if (!pointer_write_error.Success()) + { + err.SetErrorStringWithFormat("couldn't write the address of the temporary region for %s: %s", m_variable_sp->GetName().AsCString(), pointer_write_error.AsCString()); + } + } + } + } + + void Dematerialize (lldb::StackFrameSP &frame_sp, + IRMemoryMap &map, + lldb::addr_t process_address, + lldb::addr_t frame_top, + lldb::addr_t frame_bottom, + Error &err) + { + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + const lldb::addr_t load_addr = process_address + m_offset; + if (log) + { + log->Printf("EntityVariable::Dematerialize [address = 0x%" PRIx64 ", m_variable_sp = %s]", + (uint64_t)load_addr, + m_variable_sp->GetName().AsCString()); + } + + if (m_temporary_allocation != LLDB_INVALID_ADDRESS) + { + ExecutionContextScope *scope = frame_sp.get(); + + if (!scope) + scope = map.GetBestExecutionContextScope(); + + lldb::ValueObjectSP valobj_sp = ValueObjectVariable::Create(scope, m_variable_sp); + + if (!valobj_sp) + { + err.SetErrorStringWithFormat("couldn't get a value object for variable %s", m_variable_sp->GetName().AsCString()); + return; + } + + lldb_private::DataExtractor data; + + Error extract_error; + + map.GetMemoryData(data, m_temporary_allocation, valobj_sp->GetByteSize(), extract_error); + + if (!extract_error.Success()) + { + err.SetErrorStringWithFormat("couldn't get the data for variable %s", m_variable_sp->GetName().AsCString()); + return; + } + + Error set_error; + + valobj_sp->SetData(data, set_error); + + if (!set_error.Success()) + { + err.SetErrorStringWithFormat("couldn't write the new contents of %s back into the variable", m_variable_sp->GetName().AsCString()); + return; + } + + Error free_error; + + map.Free(m_temporary_allocation, free_error); + + if (!free_error.Success()) + { + err.SetErrorStringWithFormat("couldn't free the temporary region for %s: %s", m_variable_sp->GetName().AsCString(), free_error.AsCString()); + return; + } + + m_temporary_allocation = LLDB_INVALID_ADDRESS; + m_temporary_allocation_size = 0; + } + } + + void DumpToLog (IRMemoryMap &map, lldb::addr_t process_address, Log *log) + { + StreamString dump_stream; + + const lldb::addr_t load_addr = process_address + m_offset; + dump_stream.Printf("0x%" PRIx64 ": EntityVariable\n", load_addr); + + Error err; + + lldb::addr_t ptr = LLDB_INVALID_ADDRESS; + + { + dump_stream.Printf("Pointer:\n"); + + DataBufferHeap data (m_size, 0); + + map.ReadMemory(data.GetBytes(), load_addr, m_size, err); + + if (!err.Success()) + { + dump_stream.Printf(" \n"); + } + else + { + DataExtractor extractor (data.GetBytes(), data.GetByteSize(), map.GetByteOrder(), map.GetAddressByteSize()); + + extractor.DumpHexBytes(&dump_stream, data.GetBytes(), data.GetByteSize(), 16, load_addr); + + lldb::offset_t offset; + + ptr = extractor.GetPointer(&offset); + + dump_stream.PutChar('\n'); + } + } + + if (m_temporary_allocation == LLDB_INVALID_ADDRESS) + { + dump_stream.Printf("Points to process memory:\n"); + } + else + { + dump_stream.Printf("Temporary allocation:\n"); + } + + if (ptr == LLDB_INVALID_ADDRESS) + { + dump_stream.Printf(" \n"); + } + else + { + DataBufferHeap data (m_temporary_allocation_size, 0); + + map.ReadMemory(data.GetBytes(), m_temporary_allocation, m_temporary_allocation_size, err); + + if (!err.Success()) + { + dump_stream.Printf(" \n"); + } + else + { + DataExtractor extractor (data.GetBytes(), data.GetByteSize(), map.GetByteOrder(), map.GetAddressByteSize()); + + extractor.DumpHexBytes(&dump_stream, data.GetBytes(), data.GetByteSize(), 16, load_addr); + + dump_stream.PutChar('\n'); + } + } + + log->PutCString(dump_stream.GetData()); + } + + void Wipe (IRMemoryMap &map, lldb::addr_t process_address) + { + if (m_temporary_allocation != LLDB_INVALID_ADDRESS) + { + Error free_error; + + map.Free(m_temporary_allocation, free_error); + + m_temporary_allocation = LLDB_INVALID_ADDRESS; + m_temporary_allocation_size = 0; + } + + } +private: + lldb::VariableSP m_variable_sp; + bool m_is_reference; + lldb::addr_t m_temporary_allocation; + size_t m_temporary_allocation_size; +}; + +uint32_t +Materializer::AddVariable (lldb::VariableSP &variable_sp, Error &err) +{ + EntityVector::iterator iter = m_entities.insert(m_entities.end(), EntityUP()); + iter->reset (new EntityVariable (variable_sp)); + uint32_t ret = AddStructMember(**iter); + (*iter)->SetOffset(ret); + return ret; +} + +class EntityResultVariable : public Materializer::Entity +{ +public: + EntityResultVariable (const TypeFromUser &type, bool is_program_reference, bool keep_in_memory) : + Entity(), + m_type(type), + m_is_program_reference(is_program_reference), + m_keep_in_memory(keep_in_memory), + m_temporary_allocation(LLDB_INVALID_ADDRESS), + m_temporary_allocation_size(0) + { + // Hard-coding to maximum size of a pointer since all results are materialized by reference + m_size = 8; + m_alignment = 8; + } + + void Materialize (lldb::StackFrameSP &frame_sp, IRMemoryMap &map, lldb::addr_t process_address, Error &err) + { + if (!m_is_program_reference) + { + if (m_temporary_allocation != LLDB_INVALID_ADDRESS) + { + err.SetErrorString("Trying to create a temporary region for the result but one exists"); + return; + } + + const lldb::addr_t load_addr = process_address + m_offset; + + size_t byte_size = m_type.GetByteSize(); + size_t bit_align = m_type.GetTypeBitAlign(); + size_t byte_align = (bit_align + 7) / 8; + + Error alloc_error; + + m_temporary_allocation = map.Malloc(byte_size, byte_align, lldb::ePermissionsReadable | lldb::ePermissionsWritable, IRMemoryMap::eAllocationPolicyMirror, alloc_error); + m_temporary_allocation_size = byte_size; + + if (!alloc_error.Success()) + { + err.SetErrorStringWithFormat("couldn't allocate a temporary region for the result: %s", alloc_error.AsCString()); + return; + } + + Error pointer_write_error; + + map.WritePointerToMemory(load_addr, m_temporary_allocation, pointer_write_error); + + if (!pointer_write_error.Success()) + { + err.SetErrorStringWithFormat("couldn't write the address of the temporary region for the result: %s", pointer_write_error.AsCString()); + } + } + } + + void Dematerialize (lldb::StackFrameSP &frame_sp, + IRMemoryMap &map, + lldb::addr_t process_address, + lldb::addr_t frame_top, + lldb::addr_t frame_bottom, + Error &err) + { + err.SetErrorString("Tried to detmaterialize a result variable with the normal Dematerialize method"); + } + + void Dematerialize (lldb::ClangExpressionVariableSP &result_variable_sp, + lldb::StackFrameSP &frame_sp, + IRMemoryMap &map, + lldb::addr_t process_address, + lldb::addr_t frame_top, + lldb::addr_t frame_bottom, + Error &err) + { + err.Clear(); + + ExecutionContextScope *exe_scope = map.GetBestExecutionContextScope(); + + if (!exe_scope) + { + err.SetErrorString("Couldn't dematerialize a result variable: invalid execution context scope"); + return; + } + + lldb::addr_t address; + Error read_error; + const lldb::addr_t load_addr = process_address + m_offset; + + map.ReadPointerFromMemory (&address, load_addr, read_error); + + if (!read_error.Success()) + { + err.SetErrorString("Couldn't dematerialize a result variable: couldn't read its address"); + return; + } + + lldb::TargetSP target_sp = exe_scope->CalculateTarget(); + + if (!target_sp) + { + err.SetErrorString("Couldn't dematerialize a result variable: no target"); + return; + } + + ConstString name = target_sp->GetPersistentVariables().GetNextPersistentVariableName(); + + lldb::ClangExpressionVariableSP ret; + + ret = target_sp->GetPersistentVariables().CreateVariable(exe_scope, + name, + m_type, + map.GetByteOrder(), + map.GetAddressByteSize()); + + if (!ret) + { + err.SetErrorStringWithFormat("couldn't dematerialize a result variable: failed to make persistent variable %s", name.AsCString()); + return; + } + + lldb::ProcessSP process_sp = map.GetBestExecutionContextScope()->CalculateProcess(); + + bool can_persist = (m_is_program_reference && process_sp && process_sp->CanJIT() && !(address >= frame_bottom && address < frame_top)); + + if (can_persist && m_keep_in_memory) + { + ret->m_live_sp = ValueObjectConstResult::Create(exe_scope, + m_type, + name, + address, + eAddressTypeLoad, + ret->GetByteSize()); + } + + ret->ValueUpdated(); + + const size_t pvar_byte_size = ret->GetByteSize(); + uint8_t *pvar_data = ret->GetValueBytes(); + + map.ReadMemory(pvar_data, address, pvar_byte_size, read_error); + + if (!read_error.Success()) + { + err.SetErrorString("Couldn't dematerialize a result variable: couldn't read its memory"); + return; + } + + result_variable_sp = ret; + + if (!can_persist || !m_keep_in_memory) + { + ret->m_flags |= ClangExpressionVariable::EVNeedsAllocation; + + if (m_temporary_allocation != LLDB_INVALID_ADDRESS) + { + Error free_error; + map.Free(m_temporary_allocation, free_error); + } + } + else + { + ret->m_flags |= ClangExpressionVariable::EVIsLLDBAllocated; + } + + m_temporary_allocation = LLDB_INVALID_ADDRESS; + m_temporary_allocation_size = 0; + } + + void DumpToLog (IRMemoryMap &map, lldb::addr_t process_address, Log *log) + { + StreamString dump_stream; + + const lldb::addr_t load_addr = process_address + m_offset; + + dump_stream.Printf("0x%" PRIx64 ": EntityResultVariable\n", load_addr); + + Error err; + + lldb::addr_t ptr = LLDB_INVALID_ADDRESS; + + { + dump_stream.Printf("Pointer:\n"); + + DataBufferHeap data (m_size, 0); + + map.ReadMemory(data.GetBytes(), load_addr, m_size, err); + + if (!err.Success()) + { + dump_stream.Printf(" \n"); + } + else + { + DataExtractor extractor (data.GetBytes(), data.GetByteSize(), map.GetByteOrder(), map.GetAddressByteSize()); + + extractor.DumpHexBytes(&dump_stream, data.GetBytes(), data.GetByteSize(), 16, load_addr); + + lldb::offset_t offset; + + ptr = extractor.GetPointer(&offset); + + dump_stream.PutChar('\n'); + } + } + + if (m_temporary_allocation == LLDB_INVALID_ADDRESS) + { + dump_stream.Printf("Points to process memory:\n"); + } + else + { + dump_stream.Printf("Temporary allocation:\n"); + } + + if (ptr == LLDB_INVALID_ADDRESS) + { + dump_stream.Printf(" \n"); + } + else + { + DataBufferHeap data (m_temporary_allocation_size, 0); + + map.ReadMemory(data.GetBytes(), m_temporary_allocation, m_temporary_allocation_size, err); + + if (!err.Success()) + { + dump_stream.Printf(" \n"); + } + else + { + DataExtractor extractor (data.GetBytes(), data.GetByteSize(), map.GetByteOrder(), map.GetAddressByteSize()); + + extractor.DumpHexBytes(&dump_stream, data.GetBytes(), data.GetByteSize(), 16, load_addr); + + dump_stream.PutChar('\n'); + } + } + + log->PutCString(dump_stream.GetData()); + } + + void Wipe (IRMemoryMap &map, lldb::addr_t process_address) + { + if (!m_keep_in_memory && m_temporary_allocation != LLDB_INVALID_ADDRESS) + { + Error free_error; + + map.Free(m_temporary_allocation, free_error); + } + + m_temporary_allocation = LLDB_INVALID_ADDRESS; + m_temporary_allocation_size = 0; + } +private: + TypeFromUser m_type; + bool m_is_program_reference; + bool m_keep_in_memory; + + lldb::addr_t m_temporary_allocation; + size_t m_temporary_allocation_size; +}; + +uint32_t +Materializer::AddResultVariable (const TypeFromUser &type, bool is_program_reference, bool keep_in_memory, Error &err) +{ + EntityVector::iterator iter = m_entities.insert(m_entities.end(), EntityUP()); + iter->reset (new EntityResultVariable (type, is_program_reference, keep_in_memory)); + uint32_t ret = AddStructMember(**iter); + (*iter)->SetOffset(ret); + m_result_entity = iter->get(); + return ret; +} + +class EntitySymbol : public Materializer::Entity +{ +public: + EntitySymbol (const Symbol &symbol) : + Entity(), + m_symbol(symbol) + { + // Hard-coding to maximum size of a symbol + m_size = 8; + m_alignment = 8; + } + + void Materialize (lldb::StackFrameSP &frame_sp, IRMemoryMap &map, lldb::addr_t process_address, Error &err) + { + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + const lldb::addr_t load_addr = process_address + m_offset; + + if (log) + { + log->Printf("EntitySymbol::Materialize [address = 0x%" PRIx64 ", m_symbol = %s]", + (uint64_t)load_addr, + m_symbol.GetName().AsCString()); + } + + Address &sym_address = m_symbol.GetAddress(); + + ExecutionContextScope *exe_scope = map.GetBestExecutionContextScope(); + + lldb::TargetSP target_sp; + + if (exe_scope) + target_sp = map.GetBestExecutionContextScope()->CalculateTarget(); + + if (!target_sp) + { + err.SetErrorStringWithFormat("couldn't resolve symbol %s because there is no target", m_symbol.GetName().AsCString()); + return; + } + + lldb::addr_t resolved_address = sym_address.GetLoadAddress(target_sp.get()); + + if (resolved_address == LLDB_INVALID_ADDRESS) + resolved_address = sym_address.GetFileAddress(); + + Error pointer_write_error; + + map.WritePointerToMemory(load_addr, resolved_address, pointer_write_error); + + if (!pointer_write_error.Success()) + { + err.SetErrorStringWithFormat("couldn't write the address of symbol %s: %s", m_symbol.GetName().AsCString(), pointer_write_error.AsCString()); + return; + } + } + + void Dematerialize (lldb::StackFrameSP &frame_sp, + IRMemoryMap &map, + lldb::addr_t process_address, + lldb::addr_t frame_top, + lldb::addr_t frame_bottom, + Error &err) + { + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + const lldb::addr_t load_addr = process_address + m_offset; + + if (log) + { + log->Printf("EntitySymbol::Dematerialize [address = 0x%" PRIx64 ", m_symbol = %s]", + (uint64_t)load_addr, + m_symbol.GetName().AsCString()); + } + + // no work needs to be done + } + + void DumpToLog (IRMemoryMap &map, lldb::addr_t process_address, Log *log) + { + StreamString dump_stream; + + Error err; + + const lldb::addr_t load_addr = process_address + m_offset; + + dump_stream.Printf("0x%" PRIx64 ": EntitySymbol (%s)\n", load_addr, m_symbol.GetName().AsCString()); + + { + dump_stream.Printf("Pointer:\n"); + + DataBufferHeap data (m_size, 0); + + map.ReadMemory(data.GetBytes(), load_addr, m_size, err); + + if (!err.Success()) + { + dump_stream.Printf(" \n"); + } + else + { + DataExtractor extractor (data.GetBytes(), data.GetByteSize(), map.GetByteOrder(), map.GetAddressByteSize()); + + extractor.DumpHexBytes(&dump_stream, data.GetBytes(), data.GetByteSize(), 16, load_addr); + + dump_stream.PutChar('\n'); + } + } + + log->PutCString(dump_stream.GetData()); + } + + void Wipe (IRMemoryMap &map, lldb::addr_t process_address) + { + } +private: + Symbol m_symbol; +}; + +uint32_t +Materializer::AddSymbol (const Symbol &symbol_sp, Error &err) +{ + EntityVector::iterator iter = m_entities.insert(m_entities.end(), EntityUP()); + iter->reset (new EntitySymbol (symbol_sp)); + uint32_t ret = AddStructMember(**iter); + (*iter)->SetOffset(ret); + return ret; +} + +class EntityRegister : public Materializer::Entity +{ +public: + EntityRegister (const RegisterInfo ®ister_info) : + Entity(), + m_register_info(register_info) + { + // Hard-coding alignment conservatively + m_size = m_register_info.byte_size; + m_alignment = m_register_info.byte_size; + } + + void Materialize (lldb::StackFrameSP &frame_sp, IRMemoryMap &map, lldb::addr_t process_address, Error &err) + { + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + const lldb::addr_t load_addr = process_address + m_offset; + + if (log) + { + log->Printf("EntityRegister::Materialize [address = 0x%" PRIx64 ", m_register_info = %s]", + (uint64_t)load_addr, + m_register_info.name); + } + + RegisterValue reg_value; + + if (!frame_sp.get()) + { + err.SetErrorStringWithFormat("couldn't materialize register %s without a stack frame", m_register_info.name); + return; + } + + lldb::RegisterContextSP reg_context_sp = frame_sp->GetRegisterContext(); + + if (!reg_context_sp->ReadRegister(&m_register_info, reg_value)) + { + err.SetErrorStringWithFormat("couldn't read the value of register %s", m_register_info.name); + return; + } + + DataExtractor register_data; + + if (!reg_value.GetData(register_data)) + { + err.SetErrorStringWithFormat("couldn't get the data for register %s", m_register_info.name); + return; + } + + if (register_data.GetByteSize() != m_register_info.byte_size) + { + err.SetErrorStringWithFormat("data for register %s had size %llu but we expected %llu", m_register_info.name, (unsigned long long)register_data.GetByteSize(), (unsigned long long)m_register_info.byte_size); + return; + } + + m_register_contents.reset(new DataBufferHeap(register_data.GetDataStart(), register_data.GetByteSize())); + + Error write_error; + + map.WriteMemory(load_addr, register_data.GetDataStart(), register_data.GetByteSize(), write_error); + + if (!write_error.Success()) + { + err.SetErrorStringWithFormat("couldn't write the contents of register %s: %s", m_register_info.name, write_error.AsCString()); + return; + } + } + + void Dematerialize (lldb::StackFrameSP &frame_sp, + IRMemoryMap &map, + lldb::addr_t process_address, + lldb::addr_t frame_top, + lldb::addr_t frame_bottom, + Error &err) + { + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + const lldb::addr_t load_addr = process_address + m_offset; + + if (log) + { + log->Printf("EntityRegister::Dematerialize [address = 0x%" PRIx64 ", m_register_info = %s]", + (uint64_t)load_addr, + m_register_info.name); + } + + Error extract_error; + + DataExtractor register_data; + + if (!frame_sp.get()) + { + err.SetErrorStringWithFormat("couldn't dematerialize register %s without a stack frame", m_register_info.name); + return; + } + + lldb::RegisterContextSP reg_context_sp = frame_sp->GetRegisterContext(); + + map.GetMemoryData(register_data, load_addr, m_register_info.byte_size, extract_error); + + if (!extract_error.Success()) + { + err.SetErrorStringWithFormat("couldn't get the data for register %s: %s", m_register_info.name, extract_error.AsCString()); + return; + } + + if (!memcmp(register_data.GetDataStart(), m_register_contents->GetBytes(), register_data.GetByteSize())) + { + // No write required, and in particular we avoid errors if the register wasn't writable + + m_register_contents.reset(); + return; + } + + m_register_contents.reset(); + + RegisterValue register_value (const_cast(register_data.GetDataStart()), register_data.GetByteSize(), register_data.GetByteOrder()); + + if (!reg_context_sp->WriteRegister(&m_register_info, register_value)) + { + err.SetErrorStringWithFormat("couldn't write the value of register %s", m_register_info.name); + return; + } + } + + void DumpToLog (IRMemoryMap &map, lldb::addr_t process_address, Log *log) + { + StreamString dump_stream; + + Error err; + + const lldb::addr_t load_addr = process_address + m_offset; + + + dump_stream.Printf("0x%" PRIx64 ": EntityRegister (%s)\n", load_addr, m_register_info.name); + + { + dump_stream.Printf("Value:\n"); + + DataBufferHeap data (m_size, 0); + + map.ReadMemory(data.GetBytes(), load_addr, m_size, err); + + if (!err.Success()) + { + dump_stream.Printf(" \n"); + } + else + { + DataExtractor extractor (data.GetBytes(), data.GetByteSize(), map.GetByteOrder(), map.GetAddressByteSize()); + + extractor.DumpHexBytes(&dump_stream, data.GetBytes(), data.GetByteSize(), 16, load_addr); + + dump_stream.PutChar('\n'); + } + } + + log->PutCString(dump_stream.GetData()); + } + + void Wipe (IRMemoryMap &map, lldb::addr_t process_address) + { + } +private: + RegisterInfo m_register_info; + lldb::DataBufferSP m_register_contents; +}; + +uint32_t +Materializer::AddRegister (const RegisterInfo ®ister_info, Error &err) +{ + EntityVector::iterator iter = m_entities.insert(m_entities.end(), EntityUP()); + iter->reset (new EntityRegister (register_info)); + uint32_t ret = AddStructMember(**iter); + (*iter)->SetOffset(ret); + return ret; +} + +Materializer::Materializer () : + m_dematerializer_wp(), + m_result_entity(NULL), + m_current_offset(0), + m_struct_alignment(8) +{ +} + +Materializer::~Materializer () +{ + DematerializerSP dematerializer_sp = m_dematerializer_wp.lock(); + + if (dematerializer_sp) + dematerializer_sp->Wipe(); +} + +Materializer::DematerializerSP +Materializer::Materialize (lldb::StackFrameSP &frame_sp, IRMemoryMap &map, lldb::addr_t process_address, Error &error) +{ + ExecutionContextScope *exe_scope = frame_sp.get(); + + if (!exe_scope) + exe_scope = map.GetBestExecutionContextScope(); + + DematerializerSP dematerializer_sp = m_dematerializer_wp.lock(); + + if (dematerializer_sp) + { + error.SetErrorToGenericError(); + error.SetErrorString("Couldn't materialize: already materialized"); + } + + DematerializerSP ret(new Dematerializer(*this, frame_sp, map, process_address)); + + if (!exe_scope) + { + error.SetErrorToGenericError(); + error.SetErrorString("Couldn't materialize: target doesn't exist"); + } + + for (EntityUP &entity_up : m_entities) + { + entity_up->Materialize(frame_sp, map, process_address, error); + + if (!error.Success()) + return DematerializerSP(); + } + + if (Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)) + { + log->Printf("Materializer::Materialize (frame_sp = %p, process_address = 0x%" PRIx64 ") materialized:", frame_sp.get(), process_address); + for (EntityUP &entity_up : m_entities) + entity_up->DumpToLog(map, process_address, log); + } + + m_dematerializer_wp = ret; + + return ret; +} + +void +Materializer::Dematerializer::Dematerialize (Error &error, lldb::ClangExpressionVariableSP &result_sp, lldb::addr_t frame_bottom, lldb::addr_t frame_top) +{ + lldb::StackFrameSP frame_sp; + + lldb::ThreadSP thread_sp = m_thread_wp.lock(); + if (thread_sp) + frame_sp = thread_sp->GetFrameWithStackID(m_stack_id); + + ExecutionContextScope *exe_scope = m_map->GetBestExecutionContextScope(); + + if (!IsValid()) + { + error.SetErrorToGenericError(); + error.SetErrorString("Couldn't dematerialize: invalid dematerializer"); + } + + if (!exe_scope) + { + error.SetErrorToGenericError(); + error.SetErrorString("Couldn't dematerialize: target is gone"); + } + else + { + if (Log *log =lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)) + { + log->Printf("Materializer::Dematerialize (frame_sp = %p, process_address = 0x%" PRIx64 ") about to dematerialize:", frame_sp.get(), m_process_address); + for (EntityUP &entity_up : m_materializer->m_entities) + entity_up->DumpToLog(*m_map, m_process_address, log); + } + + for (EntityUP &entity_up : m_materializer->m_entities) + { + if (entity_up.get() == m_materializer->m_result_entity) + { + static_cast(m_materializer->m_result_entity)->Dematerialize (result_sp, frame_sp, *m_map, m_process_address, frame_top, frame_bottom, error); + } + else + { + entity_up->Dematerialize (frame_sp, *m_map, m_process_address, frame_top, frame_bottom, error); + } + + if (!error.Success()) + break; + } + } + + Wipe(); +} + +void +Materializer::Dematerializer::Wipe () +{ + if (!IsValid()) + return; + + for (EntityUP &entity_up : m_materializer->m_entities) + { + entity_up->Wipe (*m_map, m_process_address); + } + + m_materializer = NULL; + m_map = NULL; + m_process_address = LLDB_INVALID_ADDRESS; +} diff --git a/contrib/llvm/tools/lldb/source/Host/common/Condition.cpp b/contrib/llvm/tools/lldb/source/Host/common/Condition.cpp new file mode 100644 index 00000000000..daa729cadca --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Host/common/Condition.cpp @@ -0,0 +1,106 @@ +//===-- Condition.cpp -------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include + +#include "lldb/Host/Condition.h" +#include "lldb/Host/TimeValue.h" + + +using namespace lldb_private; + +//---------------------------------------------------------------------- +// Default constructor +// +// The default constructor will initialize a new pthread condition +// and maintain the condition in the object state. +//---------------------------------------------------------------------- +Condition::Condition () : + m_condition() +{ + ::pthread_cond_init (&m_condition, NULL); +} + +//---------------------------------------------------------------------- +// Destructor +// +// Destroys the pthread condition that the object owns. +//---------------------------------------------------------------------- +Condition::~Condition () +{ + ::pthread_cond_destroy (&m_condition); +} + +//---------------------------------------------------------------------- +// Unblock all threads waiting for a condition variable +//---------------------------------------------------------------------- +int +Condition::Broadcast () +{ + return ::pthread_cond_broadcast (&m_condition); +} + +//---------------------------------------------------------------------- +// Get accessor to the pthread condition object +//---------------------------------------------------------------------- +pthread_cond_t * +Condition::GetCondition () +{ + return &m_condition; +} + +//---------------------------------------------------------------------- +// Unblocks one thread waiting for the condition variable +//---------------------------------------------------------------------- +int +Condition::Signal () +{ + return ::pthread_cond_signal (&m_condition); +} + +//---------------------------------------------------------------------- +// The Wait() function atomically blocks the current thread +// waiting on the owned condition variable, and unblocks the mutex +// specified by "mutex". The waiting thread unblocks only after +// another thread calls Signal(), or Broadcast() with the same +// condition variable, or if "abstime" is valid (non-NULL) this +// function will return when the system time reaches the time +// specified in "abstime". If "abstime" is NULL this function will +// wait for an infinite amount of time for the condition variable +// to be signaled or broadcasted. +// +// The current thread re-acquires the lock on "mutex". +//---------------------------------------------------------------------- +int +Condition::Wait (Mutex &mutex, const TimeValue *abstime, bool *timed_out) +{ + int err = 0; + do + { + if (abstime && abstime->IsValid()) + { + struct timespec abstime_ts = abstime->GetAsTimeSpec(); + err = ::pthread_cond_timedwait (&m_condition, mutex.GetMutex(), &abstime_ts); + } + else + err = ::pthread_cond_wait (&m_condition, mutex.GetMutex()); + } while (err == EINTR); + + if (timed_out != NULL) + { + if (err == ETIMEDOUT) + *timed_out = true; + else + *timed_out = false; + } + + + return err; +} + diff --git a/contrib/llvm/tools/lldb/source/Host/common/DynamicLibrary.cpp b/contrib/llvm/tools/lldb/source/Host/common/DynamicLibrary.cpp new file mode 100644 index 00000000000..315a675895f --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Host/common/DynamicLibrary.cpp @@ -0,0 +1,33 @@ +//===-- DynamicLibrary.cpp ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Core/Error.h" +#include "lldb/Host/DynamicLibrary.h" + +using namespace lldb_private; + +DynamicLibrary::DynamicLibrary (const FileSpec& spec, uint32_t options) : m_filespec(spec) +{ + Error err; + m_handle = Host::DynamicLibraryOpen (spec,options,err); + if (err.Fail()) + m_handle = NULL; +} + +bool +DynamicLibrary::IsValid () +{ + return m_handle != NULL; +} + +DynamicLibrary::~DynamicLibrary () +{ + if (m_handle) + Host::DynamicLibraryClose (m_handle); +} diff --git a/contrib/llvm/tools/lldb/source/Host/common/File.cpp b/contrib/llvm/tools/lldb/source/Host/common/File.cpp new file mode 100644 index 00000000000..c0d3c290f4c --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Host/common/File.cpp @@ -0,0 +1,716 @@ +//===-- FileSpec.cpp --------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + + +#include "lldb/Host/File.h" + +#include +#include +#include +#include +#include + +#include "lldb/Core/DataBufferHeap.h" +#include "lldb/Core/Error.h" +#include "lldb/Host/Config.h" +#include "lldb/Host/FileSpec.h" + +using namespace lldb; +using namespace lldb_private; + +static const char * +GetStreamOpenModeFromOptions (uint32_t options) +{ + if (options & File::eOpenOptionAppend) + { + if (options & File::eOpenOptionRead) + { + if (options & File::eOpenOptionCanCreateNewOnly) + return "a+x"; + else + return "a+"; + } + else if (options & File::eOpenOptionWrite) + { + if (options & File::eOpenOptionCanCreateNewOnly) + return "ax"; + else + return "a"; + } + } + else if (options & File::eOpenOptionRead && options & File::eOpenOptionWrite) + { + if (options & File::eOpenOptionCanCreate) + { + if (options & File::eOpenOptionCanCreateNewOnly) + return "w+x"; + else + return "w+"; + } + else + return "r+"; + } + else if (options & File::eOpenOptionRead) + { + return "r"; + } + else if (options & File::eOpenOptionWrite) + { + return "w"; + } + return NULL; +} + +int File::kInvalidDescriptor = -1; +FILE * File::kInvalidStream = NULL; + +File::File(const char *path, uint32_t options, uint32_t permissions) : + m_descriptor (kInvalidDescriptor), + m_stream (kInvalidStream), + m_options (0), + m_owned (false) +{ + Open (path, options, permissions); +} + +File::File (const File &rhs) : + m_descriptor (kInvalidDescriptor), + m_stream (kInvalidStream), + m_options (0), + m_owned (false) +{ + Duplicate (rhs); +} + + +File & +File::operator = (const File &rhs) +{ + if (this != &rhs) + Duplicate (rhs); + return *this; +} + +File::~File() +{ + Close (); +} + + +int +File::GetDescriptor() const +{ + if (DescriptorIsValid()) + return m_descriptor; + + // Don't open the file descriptor if we don't need to, just get it from the + // stream if we have one. + if (StreamIsValid()) + return fileno (m_stream); + + // Invalid descriptor and invalid stream, return invalid descriptor. + return kInvalidDescriptor; +} + +void +File::SetDescriptor (int fd, bool transfer_ownership) +{ + if (IsValid()) + Close(); + m_descriptor = fd; + m_owned = transfer_ownership; +} + + +FILE * +File::GetStream () +{ + if (!StreamIsValid()) + { + if (DescriptorIsValid()) + { + const char *mode = GetStreamOpenModeFromOptions (m_options); + if (mode) + { + do + { + m_stream = ::fdopen (m_descriptor, mode); + } while (m_stream == NULL && errno == EINTR); + } + } + } + return m_stream; +} + + +void +File::SetStream (FILE *fh, bool transfer_ownership) +{ + if (IsValid()) + Close(); + m_stream = fh; + m_owned = transfer_ownership; +} + +Error +File::Duplicate (const File &rhs) +{ + Error error; + if (IsValid ()) + Close(); + + if (rhs.DescriptorIsValid()) + { + m_descriptor = ::fcntl(rhs.GetDescriptor(), F_DUPFD); + if (!DescriptorIsValid()) + error.SetErrorToErrno(); + else + { + m_options = rhs.m_options; + m_owned = true; + } + } + else + { + error.SetErrorString ("invalid file to duplicate"); + } + return error; +} + +Error +File::Open (const char *path, uint32_t options, uint32_t permissions) +{ + Error error; + if (IsValid()) + Close (); + + int oflag = 0; + const bool read = options & eOpenOptionRead; + const bool write = options & eOpenOptionWrite; + if (write) + { + if (read) + oflag |= O_RDWR; + else + oflag |= O_WRONLY; + + if (options & eOpenOptionAppend) + oflag |= O_APPEND; + + if (options & eOpenOptionTruncate) + oflag |= O_TRUNC; + + if (options & eOpenOptionCanCreate) + oflag |= O_CREAT; + + if (options & eOpenOptionCanCreateNewOnly) + oflag |= O_CREAT | O_EXCL; + } + else if (read) + { + oflag |= O_RDONLY; + } + + if (options & eOpenOptionNonBlocking) + oflag |= O_NONBLOCK; + + mode_t mode = 0; + if (oflag & O_CREAT) + { + if (permissions & ePermissionsUserRead) mode |= S_IRUSR; + if (permissions & ePermissionsUserWrite) mode |= S_IWUSR; + if (permissions & ePermissionsUserExecute) mode |= S_IXUSR; + if (permissions & ePermissionsGroupRead) mode |= S_IRGRP; + if (permissions & ePermissionsGroupWrite) mode |= S_IWGRP; + if (permissions & ePermissionsGroupExecute) mode |= S_IXGRP; + if (permissions & ePermissionsWorldRead) mode |= S_IROTH; + if (permissions & ePermissionsWorldWrite) mode |= S_IWOTH; + if (permissions & ePermissionsWorldExecute) mode |= S_IXOTH; + } + + do + { + m_descriptor = ::open(path, oflag, mode); + } while (m_descriptor < 0 && errno == EINTR); + + if (!DescriptorIsValid()) + error.SetErrorToErrno(); + else + m_owned = true; + + return error; +} + +Error +File::Close () +{ + Error error; + if (IsValid ()) + { + if (m_owned) + { + if (StreamIsValid()) + { + if (::fclose (m_stream) == EOF) + error.SetErrorToErrno(); + } + + if (DescriptorIsValid()) + { + if (::close (m_descriptor) != 0) + error.SetErrorToErrno(); + } + } + m_descriptor = kInvalidDescriptor; + m_stream = kInvalidStream; + m_options = 0; + m_owned = false; + } + return error; +} + + +Error +File::GetFileSpec (FileSpec &file_spec) const +{ + Error error; +#ifdef LLDB_CONFIG_FCNTL_GETPATH_SUPPORTED + if (IsValid ()) + { + char path[PATH_MAX]; + if (::fcntl(GetDescriptor(), F_GETPATH, path) == -1) + error.SetErrorToErrno(); + else + file_spec.SetFile (path, false); + } + else + { + error.SetErrorString("invalid file handle"); + } +#elif defined(__linux__) + char proc[64]; + char path[PATH_MAX]; + if (::snprintf(proc, sizeof(proc), "/proc/self/fd/%d", GetDescriptor()) < 0) + error.SetErrorString ("cannot resolve file descriptor"); + else + { + ssize_t len; + if ((len = ::readlink(proc, path, sizeof(path) - 1)) == -1) + error.SetErrorToErrno(); + else + { + path[len] = '\0'; + file_spec.SetFile (path, false); + } + } +#else + error.SetErrorString ("File::GetFileSpec is not supported on this platform"); +#endif + + if (error.Fail()) + file_spec.Clear(); + return error; +} + +off_t +File::SeekFromStart (off_t offset, Error *error_ptr) +{ + off_t result = 0; + if (DescriptorIsValid()) + { + result = ::lseek (m_descriptor, offset, SEEK_SET); + + if (error_ptr) + { + if (result == -1) + error_ptr->SetErrorToErrno(); + else + error_ptr->Clear(); + } + } + else if (StreamIsValid ()) + { + result = ::fseek(m_stream, offset, SEEK_SET); + + if (error_ptr) + { + if (result == -1) + error_ptr->SetErrorToErrno(); + else + error_ptr->Clear(); + } + } + else if (error_ptr) + { + error_ptr->SetErrorString("invalid file handle"); + } + return result; +} + +off_t +File::SeekFromCurrent (off_t offset, Error *error_ptr) +{ + off_t result = -1; + if (DescriptorIsValid()) + { + result = ::lseek (m_descriptor, offset, SEEK_CUR); + + if (error_ptr) + { + if (result == -1) + error_ptr->SetErrorToErrno(); + else + error_ptr->Clear(); + } + } + else if (StreamIsValid ()) + { + result = ::fseek(m_stream, offset, SEEK_CUR); + + if (error_ptr) + { + if (result == -1) + error_ptr->SetErrorToErrno(); + else + error_ptr->Clear(); + } + } + else if (error_ptr) + { + error_ptr->SetErrorString("invalid file handle"); + } + return result; +} + +off_t +File::SeekFromEnd (off_t offset, Error *error_ptr) +{ + off_t result = -1; + if (DescriptorIsValid()) + { + result = ::lseek (m_descriptor, offset, SEEK_END); + + if (error_ptr) + { + if (result == -1) + error_ptr->SetErrorToErrno(); + else + error_ptr->Clear(); + } + } + else if (StreamIsValid ()) + { + result = ::fseek(m_stream, offset, SEEK_END); + + if (error_ptr) + { + if (result == -1) + error_ptr->SetErrorToErrno(); + else + error_ptr->Clear(); + } + } + else if (error_ptr) + { + error_ptr->SetErrorString("invalid file handle"); + } + return result; +} + +Error +File::Flush () +{ + Error error; + if (StreamIsValid()) + { + int err = 0; + do + { + err = ::fflush (m_stream); + } while (err == EOF && errno == EINTR); + + if (err == EOF) + error.SetErrorToErrno(); + } + else if (!DescriptorIsValid()) + { + error.SetErrorString("invalid file handle"); + } + return error; +} + + +Error +File::Sync () +{ + Error error; + if (DescriptorIsValid()) + { + int err = 0; + do + { + err = ::fsync (m_descriptor); + } while (err == -1 && errno == EINTR); + + if (err == -1) + error.SetErrorToErrno(); + } + else + { + error.SetErrorString("invalid file handle"); + } + return error; +} + +Error +File::Read (void *buf, size_t &num_bytes) +{ + Error error; + ssize_t bytes_read = -1; + if (DescriptorIsValid()) + { + do + { + bytes_read = ::read (m_descriptor, buf, num_bytes); + } while (bytes_read < 0 && errno == EINTR); + + if (bytes_read == -1) + { + error.SetErrorToErrno(); + num_bytes = 0; + } + else + num_bytes = bytes_read; + } + else if (StreamIsValid()) + { + bytes_read = ::fread (buf, 1, num_bytes, m_stream); + + if (bytes_read == 0) + { + if (::feof(m_stream)) + error.SetErrorString ("feof"); + else if (::ferror (m_stream)) + error.SetErrorString ("ferror"); + num_bytes = 0; + } + else + num_bytes = bytes_read; + } + else + { + num_bytes = 0; + error.SetErrorString("invalid file handle"); + } + return error; +} + +Error +File::Write (const void *buf, size_t &num_bytes) +{ + Error error; + ssize_t bytes_written = -1; + if (DescriptorIsValid()) + { + do + { + bytes_written = ::write (m_descriptor, buf, num_bytes); + } while (bytes_written < 0 && errno == EINTR); + + if (bytes_written == -1) + { + error.SetErrorToErrno(); + num_bytes = 0; + } + else + num_bytes = bytes_written; + } + else if (StreamIsValid()) + { + bytes_written = ::fwrite (buf, 1, num_bytes, m_stream); + + if (bytes_written == 0) + { + if (::feof(m_stream)) + error.SetErrorString ("feof"); + else if (::ferror (m_stream)) + error.SetErrorString ("ferror"); + num_bytes = 0; + } + else + num_bytes = bytes_written; + + } + else + { + num_bytes = 0; + error.SetErrorString("invalid file handle"); + } + return error; +} + + +Error +File::Read (void *buf, size_t &num_bytes, off_t &offset) +{ + Error error; + int fd = GetDescriptor(); + if (fd != kInvalidDescriptor) + { + ssize_t bytes_read = -1; + do + { + bytes_read = ::pread (fd, buf, num_bytes, offset); + } while (bytes_read < 0 && errno == EINTR); + + if (bytes_read < 0) + { + num_bytes = 0; + error.SetErrorToErrno(); + } + else + { + offset += bytes_read; + num_bytes = bytes_read; + } + } + else + { + num_bytes = 0; + error.SetErrorString("invalid file handle"); + } + return error; +} + +Error +File::Read (size_t &num_bytes, off_t &offset, bool null_terminate, DataBufferSP &data_buffer_sp) +{ + Error error; + + if (num_bytes > 0) + { + int fd = GetDescriptor(); + if (fd != kInvalidDescriptor) + { + struct stat file_stats; + if (::fstat (fd, &file_stats) == 0) + { + if (file_stats.st_size > offset) + { + const size_t bytes_left = file_stats.st_size - offset; + if (num_bytes > bytes_left) + num_bytes = bytes_left; + + std::unique_ptr data_heap_ap; + data_heap_ap.reset(new DataBufferHeap(num_bytes + (null_terminate ? 1 : 0), '\0')); + + if (data_heap_ap.get()) + { + error = Read (data_heap_ap->GetBytes(), num_bytes, offset); + if (error.Success()) + { + // Make sure we read exactly what we asked for and if we got + // less, adjust the array + if (num_bytes < data_heap_ap->GetByteSize()) + data_heap_ap->SetByteSize(num_bytes); + data_buffer_sp.reset(data_heap_ap.release()); + return error; + } + } + } + else + error.SetErrorString("file is empty"); + } + else + error.SetErrorToErrno(); + } + else + error.SetErrorString("invalid file handle"); + } + else + error.SetErrorString("invalid file handle"); + + num_bytes = 0; + data_buffer_sp.reset(); + return error; +} + +Error +File::Write (const void *buf, size_t &num_bytes, off_t &offset) +{ + Error error; + int fd = GetDescriptor(); + if (fd != kInvalidDescriptor) + { + ssize_t bytes_written = -1; + do + { + bytes_written = ::pwrite (m_descriptor, buf, num_bytes, offset); + } while (bytes_written < 0 && errno == EINTR); + + if (bytes_written < 0) + { + num_bytes = 0; + error.SetErrorToErrno(); + } + else + { + offset += bytes_written; + num_bytes = bytes_written; + } + } + else + { + num_bytes = 0; + error.SetErrorString("invalid file handle"); + } + return error; +} + +//------------------------------------------------------------------ +// Print some formatted output to the stream. +//------------------------------------------------------------------ +size_t +File::Printf (const char *format, ...) +{ + va_list args; + va_start (args, format); + size_t result = PrintfVarArg (format, args); + va_end (args); + return result; +} + +//------------------------------------------------------------------ +// Print some formatted output to the stream. +//------------------------------------------------------------------ +size_t +File::PrintfVarArg (const char *format, va_list args) +{ + size_t result = 0; + if (DescriptorIsValid()) + { + char *s = NULL; + result = vasprintf(&s, format, args); + if (s != NULL) + { + if (result > 0) + { + size_t s_len = result; + Write (s, s_len); + result = s_len; + } + free (s); + } + } + else if (StreamIsValid()) + { + result = ::vfprintf (m_stream, format, args); + } + return result; +} diff --git a/contrib/llvm/tools/lldb/source/Host/common/FileSpec.cpp b/contrib/llvm/tools/lldb/source/Host/common/FileSpec.cpp new file mode 100644 index 00000000000..025cf6f4330 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Host/common/FileSpec.cpp @@ -0,0 +1,1060 @@ +//===-- FileSpec.cpp --------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + + +#include +#include +#include +#include +#include +#include +#include + +#include "lldb/Host/Config.h" // Have to include this before we test the define... +#ifdef LLDB_CONFIG_TILDE_RESOLVES_TO_USER +#include +#endif + +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/Program.h" + +#include "lldb/Host/File.h" +#include "lldb/Host/FileSpec.h" +#include "lldb/Core/DataBufferHeap.h" +#include "lldb/Core/DataBufferMemoryMap.h" +#include "lldb/Core/RegularExpression.h" +#include "lldb/Core/Stream.h" +#include "lldb/Host/Host.h" +#include "lldb/Utility/CleanUp.h" + +using namespace lldb; +using namespace lldb_private; + +static bool +GetFileStats (const FileSpec *file_spec, struct stat *stats_ptr) +{ + char resolved_path[PATH_MAX]; + if (file_spec->GetPath (resolved_path, sizeof(resolved_path))) + return ::stat (resolved_path, stats_ptr) == 0; + return false; +} + +#ifdef LLDB_CONFIG_TILDE_RESOLVES_TO_USER + +static const char* +GetCachedGlobTildeSlash() +{ + static std::string g_tilde; + if (g_tilde.empty()) + { + struct passwd *user_entry; + user_entry = getpwuid(geteuid()); + if (user_entry != NULL) + g_tilde = user_entry->pw_dir; + + if (g_tilde.empty()) + return NULL; + } + return g_tilde.c_str(); +} + +#endif // #ifdef LLDB_CONFIG_TILDE_RESOLVES_TO_USER + +// Resolves the username part of a path of the form ~user/other/directories, and +// writes the result into dst_path. +// Returns 0 if there WAS a ~ in the path but the username couldn't be resolved. +// Otherwise returns the number of characters copied into dst_path. If the return +// is >= dst_len, then the resolved path is too long... +size_t +FileSpec::ResolveUsername (const char *src_path, char *dst_path, size_t dst_len) +{ + if (src_path == NULL || src_path[0] == '\0') + return 0; + +#ifdef LLDB_CONFIG_TILDE_RESOLVES_TO_USER + + char user_home[PATH_MAX]; + const char *user_name; + + + // If there's no ~, then just copy src_path straight to dst_path (they may be the same string...) + if (src_path[0] != '~') + { + size_t len = strlen (src_path); + if (len >= dst_len) + { + ::bcopy (src_path, dst_path, dst_len - 1); + dst_path[dst_len] = '\0'; + } + else + ::bcopy (src_path, dst_path, len + 1); + + return len; + } + + const char *first_slash = ::strchr (src_path, '/'); + char remainder[PATH_MAX]; + + if (first_slash == NULL) + { + // The whole name is the username (minus the ~): + user_name = src_path + 1; + remainder[0] = '\0'; + } + else + { + size_t user_name_len = first_slash - src_path - 1; + ::memcpy (user_home, src_path + 1, user_name_len); + user_home[user_name_len] = '\0'; + user_name = user_home; + + ::strcpy (remainder, first_slash); + } + + if (user_name == NULL) + return 0; + // User name of "" means the current user... + + struct passwd *user_entry; + const char *home_dir = NULL; + + if (user_name[0] == '\0') + { + home_dir = GetCachedGlobTildeSlash(); + } + else + { + user_entry = ::getpwnam (user_name); + if (user_entry != NULL) + home_dir = user_entry->pw_dir; + } + + if (home_dir == NULL) + return 0; + else + return ::snprintf (dst_path, dst_len, "%s%s", home_dir, remainder); +#else + // Resolving home directories is not supported, just copy the path... + return ::snprintf (dst_path, dst_len, "%s", src_path); +#endif // #ifdef LLDB_CONFIG_TILDE_RESOLVES_TO_USER +} + +size_t +FileSpec::ResolvePartialUsername (const char *partial_name, StringList &matches) +{ +#ifdef LLDB_CONFIG_TILDE_RESOLVES_TO_USER + size_t extant_entries = matches.GetSize(); + + setpwent(); + struct passwd *user_entry; + const char *name_start = partial_name + 1; + std::set name_list; + + while ((user_entry = getpwent()) != NULL) + { + if (strstr(user_entry->pw_name, name_start) == user_entry->pw_name) + { + std::string tmp_buf("~"); + tmp_buf.append(user_entry->pw_name); + tmp_buf.push_back('/'); + name_list.insert(tmp_buf); + } + } + std::set::iterator pos, end = name_list.end(); + for (pos = name_list.begin(); pos != end; pos++) + { + matches.AppendString((*pos).c_str()); + } + return matches.GetSize() - extant_entries; +#else + // Resolving home directories is not supported, just copy the path... + return 0; +#endif // #ifdef LLDB_CONFIG_TILDE_RESOLVES_TO_USER +} + + + +size_t +FileSpec::Resolve (const char *src_path, char *dst_path, size_t dst_len) +{ + if (src_path == NULL || src_path[0] == '\0') + return 0; + + // Glob if needed for ~/, otherwise copy in case src_path is same as dst_path... + char unglobbed_path[PATH_MAX]; +#ifdef LLDB_CONFIG_TILDE_RESOLVES_TO_USER + if (src_path[0] == '~') + { + size_t return_count = ResolveUsername(src_path, unglobbed_path, sizeof(unglobbed_path)); + + // If we couldn't find the user referred to, or the resultant path was too long, + // then just copy over the src_path. + if (return_count == 0 || return_count >= sizeof(unglobbed_path)) + ::snprintf (unglobbed_path, sizeof(unglobbed_path), "%s", src_path); + } + else +#endif // #ifdef LLDB_CONFIG_TILDE_RESOLVES_TO_USER + { + ::snprintf(unglobbed_path, sizeof(unglobbed_path), "%s", src_path); + } + + // Now resolve the path if needed + char resolved_path[PATH_MAX]; + if (::realpath (unglobbed_path, resolved_path)) + { + // Success, copy the resolved path + return ::snprintf(dst_path, dst_len, "%s", resolved_path); + } + else + { + // Failed, just copy the unglobbed path + return ::snprintf(dst_path, dst_len, "%s", unglobbed_path); + } +} + +FileSpec::FileSpec() : + m_directory(), + m_filename() +{ +} + +//------------------------------------------------------------------ +// Default constructor that can take an optional full path to a +// file on disk. +//------------------------------------------------------------------ +FileSpec::FileSpec(const char *pathname, bool resolve_path) : + m_directory(), + m_filename(), + m_is_resolved(false) +{ + if (pathname && pathname[0]) + SetFile(pathname, resolve_path); +} + +//------------------------------------------------------------------ +// Copy constructor +//------------------------------------------------------------------ +FileSpec::FileSpec(const FileSpec& rhs) : + m_directory (rhs.m_directory), + m_filename (rhs.m_filename), + m_is_resolved (rhs.m_is_resolved) +{ +} + +//------------------------------------------------------------------ +// Copy constructor +//------------------------------------------------------------------ +FileSpec::FileSpec(const FileSpec* rhs) : + m_directory(), + m_filename() +{ + if (rhs) + *this = *rhs; +} + +//------------------------------------------------------------------ +// Virtual destrcuctor in case anyone inherits from this class. +//------------------------------------------------------------------ +FileSpec::~FileSpec() +{ +} + +//------------------------------------------------------------------ +// Assignment operator. +//------------------------------------------------------------------ +const FileSpec& +FileSpec::operator= (const FileSpec& rhs) +{ + if (this != &rhs) + { + m_directory = rhs.m_directory; + m_filename = rhs.m_filename; + m_is_resolved = rhs.m_is_resolved; + } + return *this; +} + +//------------------------------------------------------------------ +// Update the contents of this object with a new path. The path will +// be split up into a directory and filename and stored as uniqued +// string values for quick comparison and efficient memory usage. +//------------------------------------------------------------------ +void +FileSpec::SetFile (const char *pathname, bool resolve) +{ + m_filename.Clear(); + m_directory.Clear(); + m_is_resolved = false; + if (pathname == NULL || pathname[0] == '\0') + return; + + char resolved_path[PATH_MAX]; + bool path_fit = true; + + if (resolve) + { + path_fit = (FileSpec::Resolve (pathname, resolved_path, sizeof(resolved_path)) < sizeof(resolved_path) - 1); + m_is_resolved = path_fit; + } + else + { + // Copy the path because "basename" and "dirname" want to muck with the + // path buffer + if (::strlen (pathname) > sizeof(resolved_path) - 1) + path_fit = false; + else + ::strcpy (resolved_path, pathname); + } + + + if (path_fit) + { + char *filename = ::basename (resolved_path); + if (filename) + { + m_filename.SetCString (filename); + // Truncate the basename off the end of the resolved path + + // Only attempt to get the dirname if it looks like we have a path + if (strchr(resolved_path, '/')) + { + char *directory = ::dirname (resolved_path); + + // Make sure we didn't get our directory resolved to "." without having + // specified + if (directory) + m_directory.SetCString(directory); + else + { + char *last_resolved_path_slash = strrchr(resolved_path, '/'); + if (last_resolved_path_slash) + { + *last_resolved_path_slash = '\0'; + m_directory.SetCString(resolved_path); + } + } + } + } + else + m_directory.SetCString(resolved_path); + } +} + +//---------------------------------------------------------------------- +// Convert to pointer operator. This allows code to check any FileSpec +// objects to see if they contain anything valid using code such as: +// +// if (file_spec) +// {} +//---------------------------------------------------------------------- +FileSpec::operator bool() const +{ + return m_filename || m_directory; +} + +//---------------------------------------------------------------------- +// Logical NOT operator. This allows code to check any FileSpec +// objects to see if they are invalid using code such as: +// +// if (!file_spec) +// {} +//---------------------------------------------------------------------- +bool +FileSpec::operator!() const +{ + return !m_directory && !m_filename; +} + +//------------------------------------------------------------------ +// Equal to operator +//------------------------------------------------------------------ +bool +FileSpec::operator== (const FileSpec& rhs) const +{ + if (m_filename == rhs.m_filename) + { + if (m_directory == rhs.m_directory) + return true; + + // TODO: determine if we want to keep this code in here. + // The code below was added to handle a case where we were + // trying to set a file and line breakpoint and one path + // was resolved, and the other not and the directory was + // in a mount point that resolved to a more complete path: + // "/tmp/a.c" == "/private/tmp/a.c". I might end up pulling + // this out... + if (IsResolved() && rhs.IsResolved()) + { + // Both paths are resolved, no need to look further... + return false; + } + + FileSpec resolved_lhs(*this); + + // If "this" isn't resolved, resolve it + if (!IsResolved()) + { + if (resolved_lhs.ResolvePath()) + { + // This path wasn't resolved but now it is. Check if the resolved + // directory is the same as our unresolved directory, and if so, + // we can mark this object as resolved to avoid more future resolves + m_is_resolved = (m_directory == resolved_lhs.m_directory); + } + else + return false; + } + + FileSpec resolved_rhs(rhs); + if (!rhs.IsResolved()) + { + if (resolved_rhs.ResolvePath()) + { + // rhs's path wasn't resolved but now it is. Check if the resolved + // directory is the same as rhs's unresolved directory, and if so, + // we can mark this object as resolved to avoid more future resolves + rhs.m_is_resolved = (rhs.m_directory == resolved_rhs.m_directory); + } + else + return false; + } + + // If we reach this point in the code we were able to resolve both paths + // and since we only resolve the paths if the basenames are equal, then + // we can just check if both directories are equal... + return resolved_lhs.GetDirectory() == resolved_rhs.GetDirectory(); + } + return false; +} + +//------------------------------------------------------------------ +// Not equal to operator +//------------------------------------------------------------------ +bool +FileSpec::operator!= (const FileSpec& rhs) const +{ + return !(*this == rhs); +} + +//------------------------------------------------------------------ +// Less than operator +//------------------------------------------------------------------ +bool +FileSpec::operator< (const FileSpec& rhs) const +{ + return FileSpec::Compare(*this, rhs, true) < 0; +} + +//------------------------------------------------------------------ +// Dump a FileSpec object to a stream +//------------------------------------------------------------------ +Stream& +lldb_private::operator << (Stream &s, const FileSpec& f) +{ + f.Dump(&s); + return s; +} + +//------------------------------------------------------------------ +// Clear this object by releasing both the directory and filename +// string values and making them both the empty string. +//------------------------------------------------------------------ +void +FileSpec::Clear() +{ + m_directory.Clear(); + m_filename.Clear(); +} + +//------------------------------------------------------------------ +// Compare two FileSpec objects. If "full" is true, then both +// the directory and the filename must match. If "full" is false, +// then the directory names for "a" and "b" are only compared if +// they are both non-empty. This allows a FileSpec object to only +// contain a filename and it can match FileSpec objects that have +// matching filenames with different paths. +// +// Return -1 if the "a" is less than "b", 0 if "a" is equal to "b" +// and "1" if "a" is greater than "b". +//------------------------------------------------------------------ +int +FileSpec::Compare(const FileSpec& a, const FileSpec& b, bool full) +{ + int result = 0; + + // If full is true, then we must compare both the directory and filename. + + // If full is false, then if either directory is empty, then we match on + // the basename only, and if both directories have valid values, we still + // do a full compare. This allows for matching when we just have a filename + // in one of the FileSpec objects. + + if (full || (a.m_directory && b.m_directory)) + { + result = ConstString::Compare(a.m_directory, b.m_directory); + if (result) + return result; + } + return ConstString::Compare (a.m_filename, b.m_filename); +} + +bool +FileSpec::Equal (const FileSpec& a, const FileSpec& b, bool full) +{ + if (!full && (a.GetDirectory().IsEmpty() || b.GetDirectory().IsEmpty())) + return a.m_filename == b.m_filename; + else + return a == b; +} + + + +//------------------------------------------------------------------ +// Dump the object to the supplied stream. If the object contains +// a valid directory name, it will be displayed followed by a +// directory delimiter, and the filename. +//------------------------------------------------------------------ +void +FileSpec::Dump(Stream *s) const +{ + static ConstString g_slash_only ("/"); + if (s) + { + m_directory.Dump(s); + if (m_directory && m_directory != g_slash_only) + s->PutChar('/'); + m_filename.Dump(s); + } +} + +//------------------------------------------------------------------ +// Returns true if the file exists. +//------------------------------------------------------------------ +bool +FileSpec::Exists () const +{ + struct stat file_stats; + return GetFileStats (this, &file_stats); +} + +bool +FileSpec::ResolveExecutableLocation () +{ + if (!m_directory) + { + const char *file_cstr = m_filename.GetCString(); + if (file_cstr) + { + const std::string file_str (file_cstr); + llvm::sys::Path path = llvm::sys::Program::FindProgramByName (file_str); + const std::string &path_str = path.str(); + llvm::StringRef dir_ref = llvm::sys::path::parent_path(path_str); + //llvm::StringRef dir_ref = path.getDirname(); + if (! dir_ref.empty()) + { + // FindProgramByName returns "." if it can't find the file. + if (strcmp (".", dir_ref.data()) == 0) + return false; + + m_directory.SetCString (dir_ref.data()); + if (Exists()) + return true; + else + { + // If FindProgramByName found the file, it returns the directory + filename in its return results. + // We need to separate them. + FileSpec tmp_file (dir_ref.data(), false); + if (tmp_file.Exists()) + { + m_directory = tmp_file.m_directory; + return true; + } + } + } + } + } + + return false; +} + +bool +FileSpec::ResolvePath () +{ + if (m_is_resolved) + return true; // We have already resolved this path + + char path_buf[PATH_MAX]; + if (!GetPath (path_buf, PATH_MAX)) + return false; + // SetFile(...) will set m_is_resolved correctly if it can resolve the path + SetFile (path_buf, true); + return m_is_resolved; +} + +uint64_t +FileSpec::GetByteSize() const +{ + struct stat file_stats; + if (GetFileStats (this, &file_stats)) + return file_stats.st_size; + return 0; +} + +FileSpec::FileType +FileSpec::GetFileType () const +{ + struct stat file_stats; + if (GetFileStats (this, &file_stats)) + { + mode_t file_type = file_stats.st_mode & S_IFMT; + switch (file_type) + { + case S_IFDIR: return eFileTypeDirectory; + case S_IFIFO: return eFileTypePipe; + case S_IFREG: return eFileTypeRegular; + case S_IFSOCK: return eFileTypeSocket; + case S_IFLNK: return eFileTypeSymbolicLink; + default: + break; + } + return eFileTypeUnknown; + } + return eFileTypeInvalid; +} + +TimeValue +FileSpec::GetModificationTime () const +{ + TimeValue mod_time; + struct stat file_stats; + if (GetFileStats (this, &file_stats)) + mod_time.OffsetWithSeconds(file_stats.st_mtime); + return mod_time; +} + +//------------------------------------------------------------------ +// Directory string get accessor. +//------------------------------------------------------------------ +ConstString & +FileSpec::GetDirectory() +{ + return m_directory; +} + +//------------------------------------------------------------------ +// Directory string const get accessor. +//------------------------------------------------------------------ +const ConstString & +FileSpec::GetDirectory() const +{ + return m_directory; +} + +//------------------------------------------------------------------ +// Filename string get accessor. +//------------------------------------------------------------------ +ConstString & +FileSpec::GetFilename() +{ + return m_filename; +} + +//------------------------------------------------------------------ +// Filename string const get accessor. +//------------------------------------------------------------------ +const ConstString & +FileSpec::GetFilename() const +{ + return m_filename; +} + +//------------------------------------------------------------------ +// Extract the directory and path into a fixed buffer. This is +// needed as the directory and path are stored in separate string +// values. +//------------------------------------------------------------------ +size_t +FileSpec::GetPath(char *path, size_t path_max_len) const +{ + if (path_max_len) + { + const char *dirname = m_directory.GetCString(); + const char *filename = m_filename.GetCString(); + if (dirname) + { + if (filename) + return ::snprintf (path, path_max_len, "%s/%s", dirname, filename); + else + return ::snprintf (path, path_max_len, "%s", dirname); + } + else if (filename) + { + return ::snprintf (path, path_max_len, "%s", filename); + } + } + if (path) + path[0] = '\0'; + return 0; +} + +std::string +FileSpec::GetPath (void) const +{ + static ConstString g_slash_only ("/"); + std::string path; + const char *dirname = m_directory.GetCString(); + const char *filename = m_filename.GetCString(); + if (dirname) + { + path.append (dirname); + if (filename && m_directory != g_slash_only) + path.append ("/"); + } + if (filename) + path.append (filename); + return path; +} + +ConstString +FileSpec::GetFileNameExtension () const +{ + if (m_filename) + { + const char *filename = m_filename.GetCString(); + const char* dot_pos = strrchr(filename, '.'); + if (dot_pos && dot_pos[1] != '\0') + return ConstString(dot_pos+1); + } + return ConstString(); +} + +ConstString +FileSpec::GetFileNameStrippingExtension () const +{ + const char *filename = m_filename.GetCString(); + if (filename == NULL) + return ConstString(); + + const char* dot_pos = strrchr(filename, '.'); + if (dot_pos == NULL) + return m_filename; + + return ConstString(filename, dot_pos-filename); +} + +//------------------------------------------------------------------ +// Returns a shared pointer to a data buffer that contains all or +// part of the contents of a file. The data is memory mapped and +// will lazily page in data from the file as memory is accessed. +// The data that is mappped will start "file_offset" bytes into the +// file, and "file_size" bytes will be mapped. If "file_size" is +// greater than the number of bytes available in the file starting +// at "file_offset", the number of bytes will be appropriately +// truncated. The final number of bytes that get mapped can be +// verified using the DataBuffer::GetByteSize() function. +//------------------------------------------------------------------ +DataBufferSP +FileSpec::MemoryMapFileContents(off_t file_offset, size_t file_size) const +{ + DataBufferSP data_sp; + std::unique_ptr mmap_data(new DataBufferMemoryMap()); + if (mmap_data.get()) + { + const size_t mapped_length = mmap_data->MemoryMapFromFileSpec (this, file_offset, file_size); + if (((file_size == SIZE_MAX) && (mapped_length > 0)) || (mapped_length >= file_size)) + data_sp.reset(mmap_data.release()); + } + return data_sp; +} + + +//------------------------------------------------------------------ +// Return the size in bytes that this object takes in memory. This +// returns the size in bytes of this object, not any shared string +// values it may refer to. +//------------------------------------------------------------------ +size_t +FileSpec::MemorySize() const +{ + return m_filename.MemorySize() + m_directory.MemorySize(); +} + + +size_t +FileSpec::ReadFileContents (off_t file_offset, void *dst, size_t dst_len, Error *error_ptr) const +{ + Error error; + size_t bytes_read = 0; + char resolved_path[PATH_MAX]; + if (GetPath(resolved_path, sizeof(resolved_path))) + { + File file; + error = file.Open(resolved_path, File::eOpenOptionRead); + if (error.Success()) + { + off_t file_offset_after_seek = file_offset; + bytes_read = dst_len; + error = file.Read(dst, bytes_read, file_offset_after_seek); + } + } + else + { + error.SetErrorString("invalid file specification"); + } + if (error_ptr) + *error_ptr = error; + return bytes_read; +} + +//------------------------------------------------------------------ +// Returns a shared pointer to a data buffer that contains all or +// part of the contents of a file. The data copies into a heap based +// buffer that lives in the DataBuffer shared pointer object returned. +// The data that is cached will start "file_offset" bytes into the +// file, and "file_size" bytes will be mapped. If "file_size" is +// greater than the number of bytes available in the file starting +// at "file_offset", the number of bytes will be appropriately +// truncated. The final number of bytes that get mapped can be +// verified using the DataBuffer::GetByteSize() function. +//------------------------------------------------------------------ +DataBufferSP +FileSpec::ReadFileContents (off_t file_offset, size_t file_size, Error *error_ptr) const +{ + Error error; + DataBufferSP data_sp; + char resolved_path[PATH_MAX]; + if (GetPath(resolved_path, sizeof(resolved_path))) + { + File file; + error = file.Open(resolved_path, File::eOpenOptionRead); + if (error.Success()) + { + const bool null_terminate = false; + error = file.Read (file_size, file_offset, null_terminate, data_sp); + } + } + else + { + error.SetErrorString("invalid file specification"); + } + if (error_ptr) + *error_ptr = error; + return data_sp; +} + +DataBufferSP +FileSpec::ReadFileContentsAsCString(Error *error_ptr) +{ + Error error; + DataBufferSP data_sp; + char resolved_path[PATH_MAX]; + if (GetPath(resolved_path, sizeof(resolved_path))) + { + File file; + error = file.Open(resolved_path, File::eOpenOptionRead); + if (error.Success()) + { + off_t offset = 0; + size_t length = SIZE_MAX; + const bool null_terminate = true; + error = file.Read (length, offset, null_terminate, data_sp); + } + } + else + { + error.SetErrorString("invalid file specification"); + } + if (error_ptr) + *error_ptr = error; + return data_sp; +} + +size_t +FileSpec::ReadFileLines (STLStringArray &lines) +{ + lines.clear(); + char path[PATH_MAX]; + if (GetPath(path, sizeof(path))) + { + std::ifstream file_stream (path); + + if (file_stream) + { + std::string line; + while (getline (file_stream, line)) + lines.push_back (line); + } + } + return lines.size(); +} + +FileSpec::EnumerateDirectoryResult +FileSpec::EnumerateDirectory +( + const char *dir_path, + bool find_directories, + bool find_files, + bool find_other, + EnumerateDirectoryCallbackType callback, + void *callback_baton +) +{ + if (dir_path && dir_path[0]) + { + lldb_utility::CleanUp dir_path_dir (opendir(dir_path), NULL, closedir); + if (dir_path_dir.is_valid()) + { + long path_max = fpathconf (dirfd (dir_path_dir.get()), _PC_NAME_MAX); +#if defined (__APPLE_) && defined (__DARWIN_MAXPATHLEN) + if (path_max < __DARWIN_MAXPATHLEN) + path_max = __DARWIN_MAXPATHLEN; +#endif + struct dirent *buf, *dp; + buf = (struct dirent *) malloc (offsetof (struct dirent, d_name) + path_max + 1); + + while (buf && readdir_r(dir_path_dir.get(), buf, &dp) == 0 && dp) + { + // Only search directories + if (dp->d_type == DT_DIR || dp->d_type == DT_UNKNOWN) + { + size_t len = strlen(dp->d_name); + + if (len == 1 && dp->d_name[0] == '.') + continue; + + if (len == 2 && dp->d_name[0] == '.' && dp->d_name[1] == '.') + continue; + } + + bool call_callback = false; + FileSpec::FileType file_type = eFileTypeUnknown; + + switch (dp->d_type) + { + default: + case DT_UNKNOWN: file_type = eFileTypeUnknown; call_callback = true; break; + case DT_FIFO: file_type = eFileTypePipe; call_callback = find_other; break; + case DT_CHR: file_type = eFileTypeOther; call_callback = find_other; break; + case DT_DIR: file_type = eFileTypeDirectory; call_callback = find_directories; break; + case DT_BLK: file_type = eFileTypeOther; call_callback = find_other; break; + case DT_REG: file_type = eFileTypeRegular; call_callback = find_files; break; + case DT_LNK: file_type = eFileTypeSymbolicLink; call_callback = find_other; break; + case DT_SOCK: file_type = eFileTypeSocket; call_callback = find_other; break; +#if !defined(__OpenBSD__) + case DT_WHT: file_type = eFileTypeOther; call_callback = find_other; break; +#endif + } + + if (call_callback) + { + char child_path[PATH_MAX]; + const int child_path_len = ::snprintf (child_path, sizeof(child_path), "%s/%s", dir_path, dp->d_name); + if (child_path_len < (int)(sizeof(child_path) - 1)) + { + // Don't resolve the file type or path + FileSpec child_path_spec (child_path, false); + + EnumerateDirectoryResult result = callback (callback_baton, file_type, child_path_spec); + + switch (result) + { + case eEnumerateDirectoryResultNext: + // Enumerate next entry in the current directory. We just + // exit this switch and will continue enumerating the + // current directory as we currently are... + break; + + case eEnumerateDirectoryResultEnter: // Recurse into the current entry if it is a directory or symlink, or next if not + if (FileSpec::EnumerateDirectory (child_path, + find_directories, + find_files, + find_other, + callback, + callback_baton) == eEnumerateDirectoryResultQuit) + { + // The subdirectory returned Quit, which means to + // stop all directory enumerations at all levels. + if (buf) + free (buf); + return eEnumerateDirectoryResultQuit; + } + break; + + case eEnumerateDirectoryResultExit: // Exit from the current directory at the current level. + // Exit from this directory level and tell parent to + // keep enumerating. + if (buf) + free (buf); + return eEnumerateDirectoryResultNext; + + case eEnumerateDirectoryResultQuit: // Stop directory enumerations at any level + if (buf) + free (buf); + return eEnumerateDirectoryResultQuit; + } + } + } + } + if (buf) + { + free (buf); + } + } + } + // By default when exiting a directory, we tell the parent enumeration + // to continue enumerating. + return eEnumerateDirectoryResultNext; +} + +//------------------------------------------------------------------ +/// Returns true if the filespec represents an implementation source +/// file (files with a ".c", ".cpp", ".m", ".mm" (many more) +/// extension). +/// +/// @return +/// \b true if the filespec represents an implementation source +/// file, \b false otherwise. +//------------------------------------------------------------------ +bool +FileSpec::IsSourceImplementationFile () const +{ + ConstString extension (GetFileNameExtension()); + if (extension) + { + static RegularExpression g_source_file_regex ("^(c|m|mm|cpp|c\\+\\+|cxx|cc|cp|s|asm|f|f77|f90|f95|f03|for|ftn|fpp|ada|adb|ads)$", + REG_EXTENDED | REG_ICASE); + return g_source_file_regex.Execute (extension.GetCString()); + } + return false; +} + +bool +FileSpec::IsRelativeToCurrentWorkingDirectory () const +{ + const char *directory = m_directory.GetCString(); + if (directory && directory[0]) + { + // If the path doesn't start with '/' or '~', return true + switch (directory[0]) + { + case '/': + case '~': + return false; + default: + return true; + } + } + else if (m_filename) + { + // No directory, just a basename, return true + return true; + } + return false; +} diff --git a/contrib/llvm/tools/lldb/source/Host/common/Host.cpp b/contrib/llvm/tools/lldb/source/Host/common/Host.cpp new file mode 100644 index 00000000000..a7bad0063ef --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Host/common/Host.cpp @@ -0,0 +1,1568 @@ +//===-- Host.cpp ------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +// C includes +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined (__APPLE__) + +#include +#include +#include +#include + +#endif + +#if defined (__linux__) || defined (__FreeBSD__) || defined (__FreeBSD_kernel__) +#include +#include +#endif + +#if defined (__FreeBSD__) +#include +#endif + +#include "lldb/Host/Host.h" +#include "lldb/Core/ArchSpec.h" +#include "lldb/Core/ConstString.h" +#include "lldb/Core/Debugger.h" +#include "lldb/Core/Error.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Core/ThreadSafeSTLMap.h" +#include "lldb/Host/Config.h" +#include "lldb/Host/Endian.h" +#include "lldb/Host/FileSpec.h" +#include "lldb/Host/Mutex.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/TargetList.h" + +#include "llvm/ADT/SmallString.h" +#include "llvm/Support/Host.h" +#include "llvm/Support/MachO.h" +#include "llvm/Support/raw_ostream.h" + + + + + +using namespace lldb; +using namespace lldb_private; + + +#if !defined (__APPLE__) +struct MonitorInfo +{ + lldb::pid_t pid; // The process ID to monitor + Host::MonitorChildProcessCallback callback; // The callback function to call when "pid" exits or signals + void *callback_baton; // The callback baton for the callback function + bool monitor_signals; // If true, call the callback when "pid" gets signaled. +}; + +static void * +MonitorChildProcessThreadFunction (void *arg); + +lldb::thread_t +Host::StartMonitoringChildProcess +( + Host::MonitorChildProcessCallback callback, + void *callback_baton, + lldb::pid_t pid, + bool monitor_signals +) +{ + lldb::thread_t thread = LLDB_INVALID_HOST_THREAD; + MonitorInfo * info_ptr = new MonitorInfo(); + + info_ptr->pid = pid; + info_ptr->callback = callback; + info_ptr->callback_baton = callback_baton; + info_ptr->monitor_signals = monitor_signals; + + char thread_name[256]; + ::snprintf (thread_name, sizeof(thread_name), "", pid); + thread = ThreadCreate (thread_name, + MonitorChildProcessThreadFunction, + info_ptr, + NULL); + + return thread; +} + +//------------------------------------------------------------------ +// Scoped class that will disable thread canceling when it is +// constructed, and exception safely restore the previous value it +// when it goes out of scope. +//------------------------------------------------------------------ +class ScopedPThreadCancelDisabler +{ +public: + ScopedPThreadCancelDisabler() + { + // Disable the ability for this thread to be cancelled + int err = ::pthread_setcancelstate (PTHREAD_CANCEL_DISABLE, &m_old_state); + if (err != 0) + m_old_state = -1; + + } + + ~ScopedPThreadCancelDisabler() + { + // Restore the ability for this thread to be cancelled to what it + // previously was. + if (m_old_state != -1) + ::pthread_setcancelstate (m_old_state, 0); + } +private: + int m_old_state; // Save the old cancelability state. +}; + +static void * +MonitorChildProcessThreadFunction (void *arg) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS)); + const char *function = __FUNCTION__; + if (log) + log->Printf ("%s (arg = %p) thread starting...", function, arg); + + MonitorInfo *info = (MonitorInfo *)arg; + + const Host::MonitorChildProcessCallback callback = info->callback; + void * const callback_baton = info->callback_baton; + const lldb::pid_t pid = info->pid; + const bool monitor_signals = info->monitor_signals; + + delete info; + + int status = -1; +#if defined (__FreeBSD__) || defined (__FreeBSD_kernel__) + #define __WALL 0 +#endif + const int options = __WALL; + + while (1) + { + log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS); + if (log) + log->Printf("%s ::wait_pid (pid = %" PRIu64 ", &status, options = %i)...", function, pid, options); + + // Wait for all child processes + ::pthread_testcancel (); + // Get signals from all children with same process group of pid + const lldb::pid_t wait_pid = ::waitpid (-1*pid, &status, options); + ::pthread_testcancel (); + + if (wait_pid == -1) + { + if (errno == EINTR) + continue; + else + { + if (log) + log->Printf ("%s (arg = %p) thread exiting because waitpid failed (%s)...", __FUNCTION__, arg, strerror(errno)); + break; + } + } + else if (wait_pid > 0) + { + bool exited = false; + int signal = 0; + int exit_status = 0; + const char *status_cstr = NULL; + if (WIFSTOPPED(status)) + { + signal = WSTOPSIG(status); + status_cstr = "STOPPED"; + } + else if (WIFEXITED(status)) + { + exit_status = WEXITSTATUS(status); + status_cstr = "EXITED"; + exited = true; + } + else if (WIFSIGNALED(status)) + { + signal = WTERMSIG(status); + status_cstr = "SIGNALED"; + if (wait_pid == pid) { + exited = true; + exit_status = -1; + } + } + else + { + status_cstr = "(\?\?\?)"; + } + + // Scope for pthread_cancel_disabler + { + ScopedPThreadCancelDisabler pthread_cancel_disabler; + + log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS); + if (log) + log->Printf ("%s ::waitpid (pid = %" PRIu64 ", &status, options = %i) => pid = %" PRIu64 ", status = 0x%8.8x (%s), signal = %i, exit_state = %i", + function, + wait_pid, + options, + pid, + status, + status_cstr, + signal, + exit_status); + + if (exited || (signal != 0 && monitor_signals)) + { + bool callback_return = false; + if (callback) + callback_return = callback (callback_baton, wait_pid, exited, signal, exit_status); + + // If our process exited, then this thread should exit + if (exited && wait_pid == pid) + { + if (log) + log->Printf ("%s (arg = %p) thread exiting because pid received exit signal...", __FUNCTION__, arg); + break; + } + // If the callback returns true, it means this process should + // exit + if (callback_return) + { + if (log) + log->Printf ("%s (arg = %p) thread exiting because callback returned true...", __FUNCTION__, arg); + break; + } + } + } + } + } + + log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS); + if (log) + log->Printf ("%s (arg = %p) thread exiting...", __FUNCTION__, arg); + + return NULL; +} + + +void +Host::SystemLog (SystemLogType type, const char *format, va_list args) +{ + vfprintf (stderr, format, args); +} + +#endif // #if !defined (__APPLE__) + +void +Host::SystemLog (SystemLogType type, const char *format, ...) +{ + va_list args; + va_start (args, format); + SystemLog (type, format, args); + va_end (args); +} + +size_t +Host::GetPageSize() +{ + return ::getpagesize(); +} + +const ArchSpec & +Host::GetArchitecture (SystemDefaultArchitecture arch_kind) +{ + static bool g_supports_32 = false; + static bool g_supports_64 = false; + static ArchSpec g_host_arch_32; + static ArchSpec g_host_arch_64; + +#if defined (__APPLE__) + + // Apple is different in that it can support both 32 and 64 bit executables + // in the same operating system running concurrently. Here we detect the + // correct host architectures for both 32 and 64 bit including if 64 bit + // executables are supported on the system. + + if (g_supports_32 == false && g_supports_64 == false) + { + // All apple systems support 32 bit execution. + g_supports_32 = true; + uint32_t cputype, cpusubtype; + uint32_t is_64_bit_capable = false; + size_t len = sizeof(cputype); + ArchSpec host_arch; + // These will tell us about the kernel architecture, which even on a 64 + // bit machine can be 32 bit... + if (::sysctlbyname("hw.cputype", &cputype, &len, NULL, 0) == 0) + { + len = sizeof (cpusubtype); + if (::sysctlbyname("hw.cpusubtype", &cpusubtype, &len, NULL, 0) != 0) + cpusubtype = CPU_TYPE_ANY; + + len = sizeof (is_64_bit_capable); + if (::sysctlbyname("hw.cpu64bit_capable", &is_64_bit_capable, &len, NULL, 0) == 0) + { + if (is_64_bit_capable) + g_supports_64 = true; + } + + if (is_64_bit_capable) + { +#if defined (__i386__) || defined (__x86_64__) + if (cpusubtype == CPU_SUBTYPE_486) + cpusubtype = CPU_SUBTYPE_I386_ALL; +#endif + if (cputype & CPU_ARCH_ABI64) + { + // We have a 64 bit kernel on a 64 bit system + g_host_arch_32.SetArchitecture (eArchTypeMachO, ~(CPU_ARCH_MASK) & cputype, cpusubtype); + g_host_arch_64.SetArchitecture (eArchTypeMachO, cputype, cpusubtype); + } + else + { + // We have a 32 bit kernel on a 64 bit system + g_host_arch_32.SetArchitecture (eArchTypeMachO, cputype, cpusubtype); + cputype |= CPU_ARCH_ABI64; + g_host_arch_64.SetArchitecture (eArchTypeMachO, cputype, cpusubtype); + } + } + else + { + g_host_arch_32.SetArchitecture (eArchTypeMachO, cputype, cpusubtype); + g_host_arch_64.Clear(); + } + } + } + +#else // #if defined (__APPLE__) + + if (g_supports_32 == false && g_supports_64 == false) + { + llvm::Triple triple(llvm::sys::getDefaultTargetTriple()); + + g_host_arch_32.Clear(); + g_host_arch_64.Clear(); + + // If the OS is Linux, "unknown" in the vendor slot isn't what we want + // for the default triple. It's probably an artifact of config.guess. + if (triple.getOS() == llvm::Triple::Linux && triple.getVendor() == llvm::Triple::UnknownVendor) + triple.setVendorName(""); + + switch (triple.getArch()) + { + default: + g_host_arch_32.SetTriple(triple); + g_supports_32 = true; + break; + + case llvm::Triple::x86_64: + g_host_arch_64.SetTriple(triple); + g_supports_64 = true; + g_host_arch_32.SetTriple(triple.get32BitArchVariant()); + g_supports_32 = true; + break; + + case llvm::Triple::sparcv9: + case llvm::Triple::ppc64: + g_host_arch_64.SetTriple(triple); + g_supports_64 = true; + break; + } + + g_supports_32 = g_host_arch_32.IsValid(); + g_supports_64 = g_host_arch_64.IsValid(); + } + +#endif // #else for #if defined (__APPLE__) + + if (arch_kind == eSystemDefaultArchitecture32) + return g_host_arch_32; + else if (arch_kind == eSystemDefaultArchitecture64) + return g_host_arch_64; + + if (g_supports_64) + return g_host_arch_64; + + return g_host_arch_32; +} + +const ConstString & +Host::GetVendorString() +{ + static ConstString g_vendor; + if (!g_vendor) + { + const ArchSpec &host_arch = GetArchitecture (eSystemDefaultArchitecture); + const llvm::StringRef &str_ref = host_arch.GetTriple().getVendorName(); + g_vendor.SetCStringWithLength(str_ref.data(), str_ref.size()); + } + return g_vendor; +} + +const ConstString & +Host::GetOSString() +{ + static ConstString g_os_string; + if (!g_os_string) + { + const ArchSpec &host_arch = GetArchitecture (eSystemDefaultArchitecture); + const llvm::StringRef &str_ref = host_arch.GetTriple().getOSName(); + g_os_string.SetCStringWithLength(str_ref.data(), str_ref.size()); + } + return g_os_string; +} + +const ConstString & +Host::GetTargetTriple() +{ + static ConstString g_host_triple; + if (!(g_host_triple)) + { + const ArchSpec &host_arch = GetArchitecture (eSystemDefaultArchitecture); + g_host_triple.SetCString(host_arch.GetTriple().getTriple().c_str()); + } + return g_host_triple; +} + +lldb::pid_t +Host::GetCurrentProcessID() +{ + return ::getpid(); +} + +lldb::tid_t +Host::GetCurrentThreadID() +{ +#if defined (__APPLE__) + // Calling "mach_port_deallocate()" bumps the reference count on the thread + // port, so we need to deallocate it. mach_task_self() doesn't bump the ref + // count. + thread_port_t thread_self = mach_thread_self(); + mach_port_deallocate(mach_task_self(), thread_self); + return thread_self; +#elif defined(__FreeBSD__) + return lldb::tid_t(pthread_getthreadid_np()); +#elif defined(__linux__) + return lldb::tid_t(syscall(SYS_gettid)); +#else + return lldb::tid_t(pthread_self()); +#endif +} + +lldb::thread_t +Host::GetCurrentThread () +{ + return lldb::thread_t(pthread_self()); +} + +const char * +Host::GetSignalAsCString (int signo) +{ + switch (signo) + { + case SIGHUP: return "SIGHUP"; // 1 hangup + case SIGINT: return "SIGINT"; // 2 interrupt + case SIGQUIT: return "SIGQUIT"; // 3 quit + case SIGILL: return "SIGILL"; // 4 illegal instruction (not reset when caught) + case SIGTRAP: return "SIGTRAP"; // 5 trace trap (not reset when caught) + case SIGABRT: return "SIGABRT"; // 6 abort() +#if (defined(_POSIX_C_SOURCE) && !defined(_DARWIN_C_SOURCE)) + case SIGPOLL: return "SIGPOLL"; // 7 pollable event ([XSR] generated, not supported) +#endif +#if !defined(_POSIX_C_SOURCE) + case SIGEMT: return "SIGEMT"; // 7 EMT instruction +#endif + case SIGFPE: return "SIGFPE"; // 8 floating point exception + case SIGKILL: return "SIGKILL"; // 9 kill (cannot be caught or ignored) + case SIGBUS: return "SIGBUS"; // 10 bus error + case SIGSEGV: return "SIGSEGV"; // 11 segmentation violation + case SIGSYS: return "SIGSYS"; // 12 bad argument to system call + case SIGPIPE: return "SIGPIPE"; // 13 write on a pipe with no one to read it + case SIGALRM: return "SIGALRM"; // 14 alarm clock + case SIGTERM: return "SIGTERM"; // 15 software termination signal from kill + case SIGURG: return "SIGURG"; // 16 urgent condition on IO channel + case SIGSTOP: return "SIGSTOP"; // 17 sendable stop signal not from tty + case SIGTSTP: return "SIGTSTP"; // 18 stop signal from tty + case SIGCONT: return "SIGCONT"; // 19 continue a stopped process + case SIGCHLD: return "SIGCHLD"; // 20 to parent on child stop or exit + case SIGTTIN: return "SIGTTIN"; // 21 to readers pgrp upon background tty read + case SIGTTOU: return "SIGTTOU"; // 22 like TTIN for output if (tp->t_local<OSTOP) +#if !defined(_POSIX_C_SOURCE) + case SIGIO: return "SIGIO"; // 23 input/output possible signal +#endif + case SIGXCPU: return "SIGXCPU"; // 24 exceeded CPU time limit + case SIGXFSZ: return "SIGXFSZ"; // 25 exceeded file size limit + case SIGVTALRM: return "SIGVTALRM"; // 26 virtual time alarm + case SIGPROF: return "SIGPROF"; // 27 profiling time alarm +#if !defined(_POSIX_C_SOURCE) + case SIGWINCH: return "SIGWINCH"; // 28 window size changes + case SIGINFO: return "SIGINFO"; // 29 information request +#endif + case SIGUSR1: return "SIGUSR1"; // 30 user defined signal 1 + case SIGUSR2: return "SIGUSR2"; // 31 user defined signal 2 + default: + break; + } + return NULL; +} + +void +Host::WillTerminate () +{ +} + +#if !defined (__APPLE__) && !defined (__FreeBSD__) && !defined (__FreeBSD_kernel__) && !defined (__linux__) // see macosx/Host.mm + +void +Host::ThreadCreated (const char *thread_name) +{ +} + +void +Host::Backtrace (Stream &strm, uint32_t max_frames) +{ + // TODO: Is there a way to backtrace the current process on other systems? +} + +size_t +Host::GetEnvironment (StringList &env) +{ + // TODO: Is there a way to the host environment for this process on other systems? + return 0; +} + +#endif // #if !defined (__APPLE__) && !defined (__FreeBSD__) && !defined (__FreeBSD_kernel__) && !defined (__linux__) + +struct HostThreadCreateInfo +{ + std::string thread_name; + thread_func_t thread_fptr; + thread_arg_t thread_arg; + + HostThreadCreateInfo (const char *name, thread_func_t fptr, thread_arg_t arg) : + thread_name (name ? name : ""), + thread_fptr (fptr), + thread_arg (arg) + { + } +}; + +static thread_result_t +ThreadCreateTrampoline (thread_arg_t arg) +{ + HostThreadCreateInfo *info = (HostThreadCreateInfo *)arg; + Host::ThreadCreated (info->thread_name.c_str()); + thread_func_t thread_fptr = info->thread_fptr; + thread_arg_t thread_arg = info->thread_arg; + + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_THREAD)); + if (log) + log->Printf("thread created"); + + delete info; + return thread_fptr (thread_arg); +} + +lldb::thread_t +Host::ThreadCreate +( + const char *thread_name, + thread_func_t thread_fptr, + thread_arg_t thread_arg, + Error *error +) +{ + lldb::thread_t thread = LLDB_INVALID_HOST_THREAD; + + // Host::ThreadCreateTrampoline will delete this pointer for us. + HostThreadCreateInfo *info_ptr = new HostThreadCreateInfo (thread_name, thread_fptr, thread_arg); + + int err = ::pthread_create (&thread, NULL, ThreadCreateTrampoline, info_ptr); + if (err == 0) + { + if (error) + error->Clear(); + return thread; + } + + if (error) + error->SetError (err, eErrorTypePOSIX); + + return LLDB_INVALID_HOST_THREAD; +} + +bool +Host::ThreadCancel (lldb::thread_t thread, Error *error) +{ + int err = ::pthread_cancel (thread); + if (error) + error->SetError(err, eErrorTypePOSIX); + return err == 0; +} + +bool +Host::ThreadDetach (lldb::thread_t thread, Error *error) +{ + int err = ::pthread_detach (thread); + if (error) + error->SetError(err, eErrorTypePOSIX); + return err == 0; +} + +bool +Host::ThreadJoin (lldb::thread_t thread, thread_result_t *thread_result_ptr, Error *error) +{ + int err = ::pthread_join (thread, thread_result_ptr); + if (error) + error->SetError(err, eErrorTypePOSIX); + return err == 0; +} + +bool +Host::SetThreadName (lldb::pid_t pid, lldb::tid_t tid, const char *name) +{ +#if defined(__APPLE__) && MAC_OS_X_VERSION_MAX_ALLOWED > MAC_OS_X_VERSION_10_5 + lldb::pid_t curr_pid = Host::GetCurrentProcessID(); + lldb::tid_t curr_tid = Host::GetCurrentThreadID(); + if (pid == LLDB_INVALID_PROCESS_ID) + pid = curr_pid; + + if (tid == LLDB_INVALID_THREAD_ID) + tid = curr_tid; + + // Set the pthread name if possible + if (pid == curr_pid && tid == curr_tid) + { + if (::pthread_setname_np (name) == 0) + return true; + } + return false; +#elif defined (__FreeBSD__) + lldb::pid_t curr_pid = Host::GetCurrentProcessID(); + lldb::tid_t curr_tid = Host::GetCurrentThreadID(); + if (pid == LLDB_INVALID_PROCESS_ID) + pid = curr_pid; + + if (tid == LLDB_INVALID_THREAD_ID) + tid = curr_tid; + + // Set the pthread name if possible + if (pid == curr_pid && tid == curr_tid) + { + ::pthread_set_name_np (::pthread_self(), name); + return true; + } + return false; +#elif defined (__linux__) || defined (__GLIBC__) + void *fn = dlsym (RTLD_DEFAULT, "pthread_setname_np"); + if (fn) + { + lldb::pid_t curr_pid = Host::GetCurrentProcessID(); + lldb::tid_t curr_tid = Host::GetCurrentThreadID(); + if (pid == LLDB_INVALID_PROCESS_ID) + pid = curr_pid; + + if (tid == LLDB_INVALID_THREAD_ID) + tid = curr_tid; + + if (pid == curr_pid && tid == curr_tid) + { + int (*pthread_setname_np_func)(pthread_t thread, const char *name); + *reinterpret_cast (&pthread_setname_np_func) = fn; + + if (pthread_setname_np_func (::pthread_self(), name) == 0) + return true; + } + } + return false; +#else + return false; +#endif +} + +bool +Host::SetShortThreadName (lldb::pid_t pid, lldb::tid_t tid, + const char *thread_name, size_t len) +{ + char *namebuf = (char *)::malloc (len + 1); + + // Thread names are coming in like '' and + // ''. So just chopping the end of the string + // off leads to a lot of similar named threads. Go through the thread name + // and search for the last dot and use that. + const char *lastdot = ::strrchr (thread_name, '.'); + + if (lastdot && lastdot != thread_name) + thread_name = lastdot + 1; + ::strncpy (namebuf, thread_name, len); + namebuf[len] = 0; + + int namebuflen = strlen(namebuf); + if (namebuflen > 0) + { + if (namebuf[namebuflen - 1] == '(' || namebuf[namebuflen - 1] == '>') + { + // Trim off trailing '(' and '>' characters for a bit more cleanup. + namebuflen--; + namebuf[namebuflen] = 0; + } + return Host::SetThreadName (pid, tid, namebuf); + } + return false; +} + +FileSpec +Host::GetProgramFileSpec () +{ + static FileSpec g_program_filespec; + if (!g_program_filespec) + { +#if defined (__APPLE__) + char program_fullpath[PATH_MAX]; + // If DST is NULL, then return the number of bytes needed. + uint32_t len = sizeof(program_fullpath); + int err = _NSGetExecutablePath (program_fullpath, &len); + if (err == 0) + g_program_filespec.SetFile (program_fullpath, false); + else if (err == -1) + { + char *large_program_fullpath = (char *)::malloc (len + 1); + + err = _NSGetExecutablePath (large_program_fullpath, &len); + if (err == 0) + g_program_filespec.SetFile (large_program_fullpath, false); + + ::free (large_program_fullpath); + } +#elif defined (__linux__) + char exe_path[PATH_MAX]; + ssize_t len = readlink("/proc/self/exe", exe_path, sizeof(exe_path) - 1); + if (len > 0) { + exe_path[len] = 0; + g_program_filespec.SetFile(exe_path, false); + } +#elif defined (__FreeBSD__) || defined (__FreeBSD_kernel__) + int exe_path_mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, getpid() }; + size_t exe_path_size; + if (sysctl(exe_path_mib, 4, NULL, &exe_path_size, NULL, 0) == 0) + { + char *exe_path = new char[exe_path_size]; + if (sysctl(exe_path_mib, 4, exe_path, &exe_path_size, NULL, 0) == 0) + g_program_filespec.SetFile(exe_path, false); + delete[] exe_path; + } +#endif + } + return g_program_filespec; +} + +FileSpec +Host::GetModuleFileSpecForHostAddress (const void *host_addr) +{ + FileSpec module_filespec; + Dl_info info; + if (::dladdr (host_addr, &info)) + { + if (info.dli_fname) + module_filespec.SetFile(info.dli_fname, true); + } + return module_filespec; +} + +#if !defined (__APPLE__) // see Host.mm + +bool +Host::GetBundleDirectory (const FileSpec &file, FileSpec &bundle) +{ + bundle.Clear(); + return false; +} + +bool +Host::ResolveExecutableInBundle (FileSpec &file) +{ + return false; +} +#endif + +// Opaque info that tracks a dynamic library that was loaded +struct DynamicLibraryInfo +{ + DynamicLibraryInfo (const FileSpec &fs, int o, void *h) : + file_spec (fs), + open_options (o), + handle (h) + { + } + + const FileSpec file_spec; + uint32_t open_options; + void * handle; +}; + +void * +Host::DynamicLibraryOpen (const FileSpec &file_spec, uint32_t options, Error &error) +{ + char path[PATH_MAX]; + if (file_spec.GetPath(path, sizeof(path))) + { + int mode = 0; + + if (options & eDynamicLibraryOpenOptionLazy) + mode |= RTLD_LAZY; + else + mode |= RTLD_NOW; + + + if (options & eDynamicLibraryOpenOptionLocal) + mode |= RTLD_LOCAL; + else + mode |= RTLD_GLOBAL; + +#ifdef LLDB_CONFIG_DLOPEN_RTLD_FIRST_SUPPORTED + if (options & eDynamicLibraryOpenOptionLimitGetSymbol) + mode |= RTLD_FIRST; +#endif + + void * opaque = ::dlopen (path, mode); + + if (opaque) + { + error.Clear(); + return new DynamicLibraryInfo (file_spec, options, opaque); + } + else + { + error.SetErrorString(::dlerror()); + } + } + else + { + error.SetErrorString("failed to extract path"); + } + return NULL; +} + +Error +Host::DynamicLibraryClose (void *opaque) +{ + Error error; + if (opaque == NULL) + { + error.SetErrorString ("invalid dynamic library handle"); + } + else + { + DynamicLibraryInfo *dylib_info = (DynamicLibraryInfo *) opaque; + if (::dlclose (dylib_info->handle) != 0) + { + error.SetErrorString(::dlerror()); + } + + dylib_info->open_options = 0; + dylib_info->handle = 0; + delete dylib_info; + } + return error; +} + +void * +Host::DynamicLibraryGetSymbol (void *opaque, const char *symbol_name, Error &error) +{ + if (opaque == NULL) + { + error.SetErrorString ("invalid dynamic library handle"); + } + else + { + DynamicLibraryInfo *dylib_info = (DynamicLibraryInfo *) opaque; + + void *symbol_addr = ::dlsym (dylib_info->handle, symbol_name); + if (symbol_addr) + { +#ifndef LLDB_CONFIG_DLOPEN_RTLD_FIRST_SUPPORTED + // This host doesn't support limiting searches to this shared library + // so we need to verify that the match came from this shared library + // if it was requested in the Host::DynamicLibraryOpen() function. + if (dylib_info->open_options & eDynamicLibraryOpenOptionLimitGetSymbol) + { + FileSpec match_dylib_spec (Host::GetModuleFileSpecForHostAddress (symbol_addr)); + if (match_dylib_spec != dylib_info->file_spec) + { + char dylib_path[PATH_MAX]; + if (dylib_info->file_spec.GetPath (dylib_path, sizeof(dylib_path))) + error.SetErrorStringWithFormat ("symbol not found in \"%s\"", dylib_path); + else + error.SetErrorString ("symbol not found"); + return NULL; + } + } +#endif + error.Clear(); + return symbol_addr; + } + else + { + error.SetErrorString(::dlerror()); + } + } + return NULL; +} + +bool +Host::GetLLDBPath (PathType path_type, FileSpec &file_spec) +{ + // To get paths related to LLDB we get the path to the executable that + // contains this function. On MacOSX this will be "LLDB.framework/.../LLDB", + // on linux this is assumed to be the "lldb" main executable. If LLDB on + // linux is actually in a shared library (liblldb.so) then this function will + // need to be modified to "do the right thing". + + switch (path_type) + { + case ePathTypeLLDBShlibDir: + { + static ConstString g_lldb_so_dir; + if (!g_lldb_so_dir) + { + FileSpec lldb_file_spec (Host::GetModuleFileSpecForHostAddress ((void *)Host::GetLLDBPath)); + g_lldb_so_dir = lldb_file_spec.GetDirectory(); + } + file_spec.GetDirectory() = g_lldb_so_dir; + return file_spec.GetDirectory(); + } + break; + + case ePathTypeSupportExecutableDir: + { + static ConstString g_lldb_support_exe_dir; + if (!g_lldb_support_exe_dir) + { + FileSpec lldb_file_spec; + if (GetLLDBPath (ePathTypeLLDBShlibDir, lldb_file_spec)) + { + char raw_path[PATH_MAX]; + char resolved_path[PATH_MAX]; + lldb_file_spec.GetPath(raw_path, sizeof(raw_path)); + +#if defined (__APPLE__) + char *framework_pos = ::strstr (raw_path, "LLDB.framework"); + if (framework_pos) + { + framework_pos += strlen("LLDB.framework"); +#if !defined (__arm__) + ::strncpy (framework_pos, "/Resources", PATH_MAX - (framework_pos - raw_path)); +#endif + } +#endif + FileSpec::Resolve (raw_path, resolved_path, sizeof(resolved_path)); + g_lldb_support_exe_dir.SetCString(resolved_path); + } + } + file_spec.GetDirectory() = g_lldb_support_exe_dir; + return file_spec.GetDirectory(); + } + break; + + case ePathTypeHeaderDir: + { + static ConstString g_lldb_headers_dir; + if (!g_lldb_headers_dir) + { +#if defined (__APPLE__) + FileSpec lldb_file_spec; + if (GetLLDBPath (ePathTypeLLDBShlibDir, lldb_file_spec)) + { + char raw_path[PATH_MAX]; + char resolved_path[PATH_MAX]; + lldb_file_spec.GetPath(raw_path, sizeof(raw_path)); + + char *framework_pos = ::strstr (raw_path, "LLDB.framework"); + if (framework_pos) + { + framework_pos += strlen("LLDB.framework"); + ::strncpy (framework_pos, "/Headers", PATH_MAX - (framework_pos - raw_path)); + } + FileSpec::Resolve (raw_path, resolved_path, sizeof(resolved_path)); + g_lldb_headers_dir.SetCString(resolved_path); + } +#else + // TODO: Anyone know how we can determine this for linux? Other systems?? + g_lldb_headers_dir.SetCString ("/opt/local/include/lldb"); +#endif + } + file_spec.GetDirectory() = g_lldb_headers_dir; + return file_spec.GetDirectory(); + } + break; + +#ifndef LLDB_DISABLE_PYTHON + case ePathTypePythonDir: + { + static ConstString g_lldb_python_dir; + if (!g_lldb_python_dir) + { + FileSpec lldb_file_spec; + if (GetLLDBPath (ePathTypeLLDBShlibDir, lldb_file_spec)) + { + char raw_path[PATH_MAX]; + char resolved_path[PATH_MAX]; + lldb_file_spec.GetPath(raw_path, sizeof(raw_path)); + +#if defined (__APPLE__) + char *framework_pos = ::strstr (raw_path, "LLDB.framework"); + if (framework_pos) + { + framework_pos += strlen("LLDB.framework"); + ::strncpy (framework_pos, "/Resources/Python", PATH_MAX - (framework_pos - raw_path)); + } +#else + llvm::SmallString<256> python_version_dir; + llvm::raw_svector_ostream os(python_version_dir); + os << "/python" << PY_MAJOR_VERSION << '.' << PY_MINOR_VERSION << "/site-packages"; + os.flush(); + + // We may get our string truncated. Should we protect + // this with an assert? + + ::strncat(raw_path, python_version_dir.c_str(), + sizeof(raw_path) - strlen(raw_path) - 1); + +#endif + FileSpec::Resolve (raw_path, resolved_path, sizeof(resolved_path)); + g_lldb_python_dir.SetCString(resolved_path); + } + } + file_spec.GetDirectory() = g_lldb_python_dir; + return file_spec.GetDirectory(); + } + break; +#endif + + case ePathTypeLLDBSystemPlugins: // System plug-ins directory + { +#if defined (__APPLE__) || defined(__linux__) + static ConstString g_lldb_system_plugin_dir; + static bool g_lldb_system_plugin_dir_located = false; + if (!g_lldb_system_plugin_dir_located) + { + g_lldb_system_plugin_dir_located = true; +#if defined (__APPLE__) + FileSpec lldb_file_spec; + if (GetLLDBPath (ePathTypeLLDBShlibDir, lldb_file_spec)) + { + char raw_path[PATH_MAX]; + char resolved_path[PATH_MAX]; + lldb_file_spec.GetPath(raw_path, sizeof(raw_path)); + + char *framework_pos = ::strstr (raw_path, "LLDB.framework"); + if (framework_pos) + { + framework_pos += strlen("LLDB.framework"); + ::strncpy (framework_pos, "/Resources/PlugIns", PATH_MAX - (framework_pos - raw_path)); + FileSpec::Resolve (raw_path, resolved_path, sizeof(resolved_path)); + g_lldb_system_plugin_dir.SetCString(resolved_path); + } + return false; + } +#elif defined (__linux__) + FileSpec lldb_file_spec("/usr/lib/lldb", true); + if (lldb_file_spec.Exists()) + { + g_lldb_system_plugin_dir.SetCString(lldb_file_spec.GetPath().c_str()); + } +#endif // __APPLE__ || __linux__ + } + + if (g_lldb_system_plugin_dir) + { + file_spec.GetDirectory() = g_lldb_system_plugin_dir; + return true; + } +#else + // TODO: where would system LLDB plug-ins be located on other systems? + return false; +#endif + } + break; + + case ePathTypeLLDBUserPlugins: // User plug-ins directory + { +#if defined (__APPLE__) + static ConstString g_lldb_user_plugin_dir; + if (!g_lldb_user_plugin_dir) + { + char user_plugin_path[PATH_MAX]; + if (FileSpec::Resolve ("~/Library/Application Support/LLDB/PlugIns", + user_plugin_path, + sizeof(user_plugin_path))) + { + g_lldb_user_plugin_dir.SetCString(user_plugin_path); + } + } + file_spec.GetDirectory() = g_lldb_user_plugin_dir; + return file_spec.GetDirectory(); +#elif defined (__linux__) + static ConstString g_lldb_user_plugin_dir; + if (!g_lldb_user_plugin_dir) + { + // XDG Base Directory Specification + // http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html + // If XDG_DATA_HOME exists, use that, otherwise use ~/.local/share/lldb. + FileSpec lldb_file_spec; + const char *xdg_data_home = getenv("XDG_DATA_HOME"); + if (xdg_data_home && xdg_data_home[0]) + { + std::string user_plugin_dir (xdg_data_home); + user_plugin_dir += "/lldb"; + lldb_file_spec.SetFile (user_plugin_dir.c_str(), true); + } + else + { + const char *home_dir = getenv("HOME"); + if (home_dir && home_dir[0]) + { + std::string user_plugin_dir (home_dir); + user_plugin_dir += "/.local/share/lldb"; + lldb_file_spec.SetFile (user_plugin_dir.c_str(), true); + } + } + + if (lldb_file_spec.Exists()) + g_lldb_user_plugin_dir.SetCString(lldb_file_spec.GetPath().c_str()); + } + file_spec.GetDirectory() = g_lldb_user_plugin_dir; + return file_spec.GetDirectory(); +#endif + // TODO: where would user LLDB plug-ins be located on other systems? + return false; + } + } + + return false; +} + + +bool +Host::GetHostname (std::string &s) +{ + char hostname[PATH_MAX]; + hostname[sizeof(hostname) - 1] = '\0'; + if (::gethostname (hostname, sizeof(hostname) - 1) == 0) + { + struct hostent* h = ::gethostbyname (hostname); + if (h) + s.assign (h->h_name); + else + s.assign (hostname); + return true; + } + return false; +} + +const char * +Host::GetUserName (uint32_t uid, std::string &user_name) +{ + struct passwd user_info; + struct passwd *user_info_ptr = &user_info; + char user_buffer[PATH_MAX]; + size_t user_buffer_size = sizeof(user_buffer); + if (::getpwuid_r (uid, + &user_info, + user_buffer, + user_buffer_size, + &user_info_ptr) == 0) + { + if (user_info_ptr) + { + user_name.assign (user_info_ptr->pw_name); + return user_name.c_str(); + } + } + user_name.clear(); + return NULL; +} + +const char * +Host::GetGroupName (uint32_t gid, std::string &group_name) +{ + char group_buffer[PATH_MAX]; + size_t group_buffer_size = sizeof(group_buffer); + struct group group_info; + struct group *group_info_ptr = &group_info; + // Try the threadsafe version first + if (::getgrgid_r (gid, + &group_info, + group_buffer, + group_buffer_size, + &group_info_ptr) == 0) + { + if (group_info_ptr) + { + group_name.assign (group_info_ptr->gr_name); + return group_name.c_str(); + } + } + else + { + // The threadsafe version isn't currently working + // for me on darwin, but the non-threadsafe version + // is, so I am calling it below. + group_info_ptr = ::getgrgid (gid); + if (group_info_ptr) + { + group_name.assign (group_info_ptr->gr_name); + return group_name.c_str(); + } + } + group_name.clear(); + return NULL; +} + +#if !defined (__APPLE__) && !defined (__FreeBSD__) && !defined (__FreeBSD_kernel__) // see macosx/Host.mm +bool +Host::GetOSBuildString (std::string &s) +{ + s.clear(); + return false; +} + +bool +Host::GetOSKernelDescription (std::string &s) +{ + s.clear(); + return false; +} +#endif + +uint32_t +Host::GetUserID () +{ + return getuid(); +} + +uint32_t +Host::GetGroupID () +{ + return getgid(); +} + +uint32_t +Host::GetEffectiveUserID () +{ + return geteuid(); +} + +uint32_t +Host::GetEffectiveGroupID () +{ + return getegid(); +} + +#if !defined (__APPLE__) && !defined(__linux__) +uint32_t +Host::FindProcesses (const ProcessInstanceInfoMatch &match_info, ProcessInstanceInfoList &process_infos) +{ + process_infos.Clear(); + return process_infos.GetSize(); +} +#endif // #if !defined (__APPLE__) && !defined(__linux__) + +#if !defined (__APPLE__) && !defined (__FreeBSD__) && !defined (__FreeBSD_kernel__) && !defined(__linux__) +bool +Host::GetProcessInfo (lldb::pid_t pid, ProcessInstanceInfo &process_info) +{ + process_info.Clear(); + return false; +} +#endif + +#if !defined(__linux__) +bool +Host::FindProcessThreads (const lldb::pid_t pid, TidMap &tids_to_attach) +{ + return false; +} +#endif + +lldb::TargetSP +Host::GetDummyTarget (lldb_private::Debugger &debugger) +{ + static TargetSP g_dummy_target_sp; + + // FIXME: Maybe the dummy target should be per-Debugger + if (!g_dummy_target_sp || !g_dummy_target_sp->IsValid()) + { + ArchSpec arch(Target::GetDefaultArchitecture()); + if (!arch.IsValid()) + arch = Host::GetArchitecture (); + Error err = debugger.GetTargetList().CreateTarget(debugger, + NULL, + arch.GetTriple().getTriple().c_str(), + false, + NULL, + g_dummy_target_sp); + } + + return g_dummy_target_sp; +} + +struct ShellInfo +{ + ShellInfo () : + process_reaped (false), + can_delete (false), + pid (LLDB_INVALID_PROCESS_ID), + signo(-1), + status(-1) + { + } + + lldb_private::Predicate process_reaped; + lldb_private::Predicate can_delete; + lldb::pid_t pid; + int signo; + int status; +}; + +static bool +MonitorShellCommand (void *callback_baton, + lldb::pid_t pid, + bool exited, // True if the process did exit + int signo, // Zero for no signal + int status) // Exit value of process if signal is zero +{ + ShellInfo *shell_info = (ShellInfo *)callback_baton; + shell_info->pid = pid; + shell_info->signo = signo; + shell_info->status = status; + // Let the thread running Host::RunShellCommand() know that the process + // exited and that ShellInfo has been filled in by broadcasting to it + shell_info->process_reaped.SetValue(1, eBroadcastAlways); + // Now wait for a handshake back from that thread running Host::RunShellCommand + // so we know that we can delete shell_info_ptr + shell_info->can_delete.WaitForValueEqualTo(true); + // Sleep a bit to allow the shell_info->can_delete.SetValue() to complete... + usleep(1000); + // Now delete the shell info that was passed into this function + delete shell_info; + return true; +} + +Error +Host::RunShellCommand (const char *command, + const char *working_dir, + int *status_ptr, + int *signo_ptr, + std::string *command_output_ptr, + uint32_t timeout_sec, + const char *shell) +{ + Error error; + ProcessLaunchInfo launch_info; + if (shell && shell[0]) + { + // Run the command in a shell + launch_info.SetShell(shell); + launch_info.GetArguments().AppendArgument(command); + const bool localhost = true; + const bool will_debug = false; + const bool first_arg_is_full_shell_command = true; + launch_info.ConvertArgumentsForLaunchingInShell (error, + localhost, + will_debug, + first_arg_is_full_shell_command); + } + else + { + // No shell, just run it + Args args (command); + const bool first_arg_is_executable = true; + launch_info.SetArguments(args, first_arg_is_executable); + } + + if (working_dir) + launch_info.SetWorkingDirectory(working_dir); + char output_file_path_buffer[L_tmpnam]; + const char *output_file_path = NULL; + if (command_output_ptr) + { + // Create a temporary file to get the stdout/stderr and redirect the + // output of the command into this file. We will later read this file + // if all goes well and fill the data into "command_output_ptr" + output_file_path = ::tmpnam(output_file_path_buffer); + launch_info.AppendSuppressFileAction (STDIN_FILENO, true, false); + launch_info.AppendOpenFileAction(STDOUT_FILENO, output_file_path, false, true); + launch_info.AppendDuplicateFileAction(STDOUT_FILENO, STDERR_FILENO); + } + else + { + launch_info.AppendSuppressFileAction (STDIN_FILENO, true, false); + launch_info.AppendSuppressFileAction (STDOUT_FILENO, false, true); + launch_info.AppendSuppressFileAction (STDERR_FILENO, false, true); + } + + // The process monitor callback will delete the 'shell_info_ptr' below... + std::unique_ptr shell_info_ap (new ShellInfo()); + + const bool monitor_signals = false; + launch_info.SetMonitorProcessCallback(MonitorShellCommand, shell_info_ap.get(), monitor_signals); + + error = LaunchProcess (launch_info); + const lldb::pid_t pid = launch_info.GetProcessID(); + if (pid != LLDB_INVALID_PROCESS_ID) + { + // The process successfully launched, so we can defer ownership of + // "shell_info" to the MonitorShellCommand callback function that will + // get called when the process dies. We release the unique pointer as it + // doesn't need to delete the ShellInfo anymore. + ShellInfo *shell_info = shell_info_ap.release(); + TimeValue timeout_time(TimeValue::Now()); + timeout_time.OffsetWithSeconds(timeout_sec); + bool timed_out = false; + shell_info->process_reaped.WaitForValueEqualTo(true, &timeout_time, &timed_out); + if (timed_out) + { + error.SetErrorString("timed out waiting for shell command to complete"); + + // Kill the process since it didn't complete withint the timeout specified + ::kill (pid, SIGKILL); + // Wait for the monitor callback to get the message + timeout_time = TimeValue::Now(); + timeout_time.OffsetWithSeconds(1); + timed_out = false; + shell_info->process_reaped.WaitForValueEqualTo(true, &timeout_time, &timed_out); + } + else + { + if (status_ptr) + *status_ptr = shell_info->status; + + if (signo_ptr) + *signo_ptr = shell_info->signo; + + if (command_output_ptr) + { + command_output_ptr->clear(); + FileSpec file_spec(output_file_path, File::eOpenOptionRead); + uint64_t file_size = file_spec.GetByteSize(); + if (file_size > 0) + { + if (file_size > command_output_ptr->max_size()) + { + error.SetErrorStringWithFormat("shell command output is too large to fit into a std::string"); + } + else + { + command_output_ptr->resize(file_size); + file_spec.ReadFileContents(0, &((*command_output_ptr)[0]), command_output_ptr->size(), &error); + } + } + } + } + shell_info->can_delete.SetValue(true, eBroadcastAlways); + } + else + { + error.SetErrorString("failed to get process ID"); + } + + if (output_file_path) + ::unlink (output_file_path); + // Handshake with the monitor thread, or just let it know in advance that + // it can delete "shell_info" in case we timed out and were not able to kill + // the process... + return error; +} + + +uint32_t +Host::GetNumberCPUS () +{ + static uint32_t g_num_cores = UINT32_MAX; + if (g_num_cores == UINT32_MAX) + { +#if defined(__APPLE__) or defined (__linux__) or defined (__FreeBSD__) or defined (__FreeBSD_kernel__) + + g_num_cores = ::sysconf(_SC_NPROCESSORS_ONLN); + +#elif defined(_WIN32) || defined(__WIN32__) || defined(__CYGWIN__) + + // Header file for this might need to be included at the top of this file + SYSTEM_INFO system_info; + ::GetSystemInfo (&system_info); + g_num_cores = system_info.dwNumberOfProcessors; + +#else + + // Assume POSIX support if a host specific case has not been supplied above + g_num_cores = 0; + int num_cores = 0; + size_t num_cores_len = sizeof(num_cores); +#ifdef HW_AVAILCPU + int mib[] = { CTL_HW, HW_AVAILCPU }; +#else + int mib[] = { CTL_HW, HW_NCPU }; +#endif + + /* get the number of CPUs from the system */ + if (sysctl(mib, sizeof(mib)/sizeof(int), &num_cores, &num_cores_len, NULL, 0) == 0 && (num_cores > 0)) + { + g_num_cores = num_cores; + } + else + { + mib[1] = HW_NCPU; + num_cores_len = sizeof(num_cores); + if (sysctl(mib, sizeof(mib)/sizeof(int), &num_cores, &num_cores_len, NULL, 0) == 0 && (num_cores > 0)) + { + if (num_cores > 0) + g_num_cores = num_cores; + } + } +#endif + } + return g_num_cores; +} + + + +#if !defined (__APPLE__) +bool +Host::OpenFileInExternalEditor (const FileSpec &file_spec, uint32_t line_no) +{ + return false; +} + +void +Host::SetCrashDescriptionWithFormat (const char *format, ...) +{ +} + +void +Host::SetCrashDescription (const char *description) +{ +} + +lldb::pid_t +LaunchApplication (const FileSpec &app_file_spec) +{ + return LLDB_INVALID_PROCESS_ID; +} + +#endif diff --git a/contrib/llvm/tools/lldb/source/Host/common/Mutex.cpp b/contrib/llvm/tools/lldb/source/Host/common/Mutex.cpp new file mode 100644 index 00000000000..39cd8c6adb4 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Host/common/Mutex.cpp @@ -0,0 +1,390 @@ +//===-- Mutex.cpp -----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Host/Mutex.h" +#include "lldb/Host/Host.h" + +#include +#include +#include + +#if 0 +// This logging is way too verbose to enable even for a log channel. +// This logging can be enabled by changing the "#if 0", but should be +// reverted prior to checking in. +#include +#define DEBUG_LOG(fmt, ...) printf(fmt, ## __VA_ARGS__) +#else +#define DEBUG_LOG(fmt, ...) +#endif + +// Enable extra mutex error checking +#ifdef LLDB_CONFIGURATION_DEBUG +#define ENABLE_MUTEX_ERROR_CHECKING 1 +#include +#endif + +#if ENABLE_MUTEX_ERROR_CHECKING +#include + +enum MutexAction +{ + eMutexActionInitialized, + eMutexActionDestroyed, + eMutexActionAssertInitialized +}; + +static bool +error_check_mutex (pthread_mutex_t *m, MutexAction action) +{ + typedef std::set mutex_set; + static pthread_mutex_t g_mutex_set_mutex = PTHREAD_MUTEX_INITIALIZER; + static mutex_set g_initialized_mutex_set; + static mutex_set g_destroyed_mutex_set; + + bool success = true; + int err; + // Manually call lock so we don't to any of this error checking + err = ::pthread_mutex_lock (&g_mutex_set_mutex); + assert(err == 0); + switch (action) + { + case eMutexActionInitialized: + // Make sure this isn't already in our initialized mutex set... + assert (g_initialized_mutex_set.find(m) == g_initialized_mutex_set.end()); + // Remove this from the destroyed set in case it was ever in there + g_destroyed_mutex_set.erase(m); + // Add the mutex to the initialized set + g_initialized_mutex_set.insert(m); + break; + + case eMutexActionDestroyed: + // Make sure this isn't already in our destroyed mutex set... + assert (g_destroyed_mutex_set.find(m) == g_destroyed_mutex_set.end()); + // Remove this from the initialized so we can put it into the destroyed set + g_initialized_mutex_set.erase(m); + // Add the mutex to the destroyed set + g_destroyed_mutex_set.insert(m); + break; + case eMutexActionAssertInitialized: + // This function will return true if "m" is in the initialized mutex set + success = g_initialized_mutex_set.find(m) != g_initialized_mutex_set.end(); + assert (success); + break; + } + // Manually call unlock so we don't to any of this error checking + err = ::pthread_mutex_unlock (&g_mutex_set_mutex); + assert(err == 0); + return success; +} + +#endif + +using namespace lldb_private; + +//---------------------------------------------------------------------- +// Default constructor. +// +// This will create a scoped mutex locking object that doesn't have +// a mutex to lock. One will need to be provided using the Reset() +// method. +//---------------------------------------------------------------------- +Mutex::Locker::Locker () : + m_mutex_ptr(NULL) +{ +} + +//---------------------------------------------------------------------- +// Constructor with a Mutex object. +// +// This will create a scoped mutex locking object that extracts the +// mutex owned by "m" and locks it. +//---------------------------------------------------------------------- +Mutex::Locker::Locker (Mutex& m) : + m_mutex_ptr(NULL) +{ + Lock (m); +} + +//---------------------------------------------------------------------- +// Constructor with a Mutex object pointer. +// +// This will create a scoped mutex locking object that extracts the +// mutex owned by "m" and locks it. +//---------------------------------------------------------------------- +Mutex::Locker::Locker (Mutex* m) : + m_mutex_ptr(NULL) +{ + if (m) + Lock (m); +} + +//---------------------------------------------------------------------- +// Destructor +// +// Unlocks any owned mutex object (if it is valid). +//---------------------------------------------------------------------- +Mutex::Locker::~Locker () +{ + Unlock(); +} + +//---------------------------------------------------------------------- +// Unlock the current mutex in this object (if this owns a valid +// mutex) and lock the new "mutex" object if it is non-NULL. +//---------------------------------------------------------------------- +void +Mutex::Locker::Lock (Mutex &mutex) +{ + // We already have this mutex locked or both are NULL... + if (m_mutex_ptr == &mutex) + return; + + Unlock (); + + m_mutex_ptr = &mutex; + m_mutex_ptr->Lock(); +} + +void +Mutex::Locker::Unlock () +{ + if (m_mutex_ptr) + { + m_mutex_ptr->Unlock (); + m_mutex_ptr = NULL; + } +} + +bool +Mutex::Locker::TryLock (Mutex &mutex, const char *failure_message) +{ + // We already have this mutex locked! + if (m_mutex_ptr == &mutex) + return true; + + Unlock (); + + if (mutex.TryLock(failure_message) == 0) + m_mutex_ptr = &mutex; + + return m_mutex_ptr != NULL; +} + +//---------------------------------------------------------------------- +// Default constructor. +// +// Creates a pthread mutex with no attributes. +//---------------------------------------------------------------------- +Mutex::Mutex () : + m_mutex() +{ + int err; + err = ::pthread_mutex_init (&m_mutex, NULL); +#if ENABLE_MUTEX_ERROR_CHECKING + if (err == 0) + error_check_mutex (&m_mutex, eMutexActionInitialized); +#endif + assert(err == 0); +} + +//---------------------------------------------------------------------- +// Default constructor. +// +// Creates a pthread mutex with "type" as the mutex type. +//---------------------------------------------------------------------- +Mutex::Mutex (Mutex::Type type) : + m_mutex() +{ + int err; + ::pthread_mutexattr_t attr; + err = ::pthread_mutexattr_init (&attr); + assert(err == 0); + switch (type) + { + case eMutexTypeNormal: +#if ENABLE_MUTEX_ERROR_CHECKING + err = ::pthread_mutexattr_settype (&attr, PTHREAD_MUTEX_ERRORCHECK); +#else + err = ::pthread_mutexattr_settype (&attr, PTHREAD_MUTEX_NORMAL); +#endif + break; + + case eMutexTypeRecursive: + err = ::pthread_mutexattr_settype (&attr, PTHREAD_MUTEX_RECURSIVE); + break; + } + assert(err == 0); + err = ::pthread_mutex_init (&m_mutex, &attr); +#if ENABLE_MUTEX_ERROR_CHECKING + if (err == 0) + error_check_mutex (&m_mutex, eMutexActionInitialized); +#endif + assert(err == 0); + err = ::pthread_mutexattr_destroy (&attr); + assert(err == 0); +} + +//---------------------------------------------------------------------- +// Destructor. +// +// Destroys the mutex owned by this object. +//---------------------------------------------------------------------- +Mutex::~Mutex() +{ + int err = ::pthread_mutex_destroy (&m_mutex); + assert(err == 0); +#if ENABLE_MUTEX_ERROR_CHECKING + if (err == 0) + error_check_mutex (&m_mutex, eMutexActionDestroyed); + else + { + Host::SetCrashDescriptionWithFormat ("%s error: pthread_mutex_destroy() => err = %i (%s)", __PRETTY_FUNCTION__, err, strerror(err)); + assert(err == 0); + } + memset (&m_mutex, '\xba', sizeof(m_mutex)); +#endif +} + +//---------------------------------------------------------------------- +// Mutex get accessor. +//---------------------------------------------------------------------- +pthread_mutex_t * +Mutex::GetMutex() +{ + return &m_mutex; +} + +//---------------------------------------------------------------------- +// Locks the mutex owned by this object, if the mutex is already +// locked, the calling thread will block until the mutex becomes +// available. +// +// RETURNS +// The error code from the pthread_mutex_lock() function call. +//---------------------------------------------------------------------- +int +Mutex::Lock() +{ + DEBUG_LOG ("[%4.4" PRIx64 "/%4.4" PRIx64 "] pthread_mutex_lock (%p)...\n", Host::GetCurrentProcessID(), Host::GetCurrentThreadID(), &m_mutex); + +#if ENABLE_MUTEX_ERROR_CHECKING + error_check_mutex (&m_mutex, eMutexActionAssertInitialized); +#endif + + int err = ::pthread_mutex_lock (&m_mutex); + + +#if ENABLE_MUTEX_ERROR_CHECKING + if (err) + { + Host::SetCrashDescriptionWithFormat ("%s error: pthread_mutex_lock(%p) => err = %i (%s)", __PRETTY_FUNCTION__, &m_mutex, err, strerror(err)); + assert(err == 0); + } +#endif + DEBUG_LOG ("[%4.4" PRIx64 "/%4.4" PRIx64 "] pthread_mutex_lock (%p) => %i\n", Host::GetCurrentProcessID(), Host::GetCurrentThreadID(), &m_mutex, err); + return err; +} + +//---------------------------------------------------------------------- +// Attempts to lock the mutex owned by this object without blocking. +// If the mutex is already locked, TryLock() will not block waiting +// for the mutex, but will return an error condition. +// +// RETURNS +// The error code from the pthread_mutex_trylock() function call. +//---------------------------------------------------------------------- +int +Mutex::TryLock(const char *failure_message) +{ +#if ENABLE_MUTEX_ERROR_CHECKING + error_check_mutex (&m_mutex, eMutexActionAssertInitialized); +#endif + + int err = ::pthread_mutex_trylock (&m_mutex); + DEBUG_LOG ("[%4.4" PRIx64 "/%4.4" PRIx64 "] pthread_mutex_trylock (%p) => %i\n", Host::GetCurrentProcessID(), Host::GetCurrentThreadID(), &m_mutex, err); + return err; +} + +//---------------------------------------------------------------------- +// If the current thread holds the lock on the owned mutex, then +// Unlock() will unlock the mutex. Calling Unlock() on this object +// that the calling thread does not hold will result in undefined +// behavior. +// +// RETURNS +// The error code from the pthread_mutex_unlock() function call. +//---------------------------------------------------------------------- +int +Mutex::Unlock() +{ +#if ENABLE_MUTEX_ERROR_CHECKING + error_check_mutex (&m_mutex, eMutexActionAssertInitialized); +#endif + + int err = ::pthread_mutex_unlock (&m_mutex); + +#if ENABLE_MUTEX_ERROR_CHECKING + if (err) + { + Host::SetCrashDescriptionWithFormat ("%s error: pthread_mutex_unlock(%p) => err = %i (%s)", __PRETTY_FUNCTION__, &m_mutex, err, strerror(err)); + assert(err == 0); + } +#endif + DEBUG_LOG ("[%4.4" PRIx64 "/%4.4" PRIx64 "] pthread_mutex_unlock (%p) => %i\n", Host::GetCurrentProcessID(), Host::GetCurrentThreadID(), &m_mutex, err); + return err; +} + +#ifdef LLDB_CONFIGURATION_DEBUG +int +TrackingMutex::Unlock () +{ + if (!m_failure_message.empty()) + Host::SetCrashDescriptionWithFormat ("Unlocking lock (on thread %p) that thread: %p failed to get: %s", + pthread_self(), + m_thread_that_tried, + m_failure_message.c_str()); + assert (m_failure_message.empty()); + return Mutex::Unlock(); +} + +int +LoggingMutex::Lock () +{ + printf("locking mutex %p by [%4.4" PRIx64 "/%4.4" PRIx64 "]...", this, Host::GetCurrentProcessID(), Host::GetCurrentThreadID()); + int x = Mutex::Lock(); + m_locked = true; + printf("%d\n",x); + return x; +} + +int +LoggingMutex::Unlock () +{ + printf("unlocking mutex %p by [%4.4" PRIx64 "/%4.4" PRIx64 "]...", this, Host::GetCurrentProcessID(), Host::GetCurrentThreadID()); + int x = Mutex::Unlock(); + m_locked = false; + printf("%d\n",x); + return x; +} + +int +LoggingMutex::TryLock (const char *failure_message) +{ + printf("trylocking mutex %p by [%4.4" PRIx64 "/%4.4" PRIx64 "]...", this, Host::GetCurrentProcessID(), Host::GetCurrentThreadID()); + int x = Mutex::TryLock(failure_message); + if (x == 0) + m_locked = true; + printf("%d\n",x); + return x; +} + +#endif + + diff --git a/contrib/llvm/tools/lldb/source/Host/common/SocketAddress.cpp b/contrib/llvm/tools/lldb/source/Host/common/SocketAddress.cpp new file mode 100644 index 00000000000..a22dc7a01c1 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Host/common/SocketAddress.cpp @@ -0,0 +1,260 @@ +//===-- SocketAddress.cpp ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Host/SocketAddress.h" +#include + +// C Includes +#include +#include + +// C++ Includes +// Other libraries and framework includes +// Project includes + +using namespace lldb_private; + +//---------------------------------------------------------------------- +// SocketAddress constructor +//---------------------------------------------------------------------- +SocketAddress::SocketAddress () +{ + Clear (); +} + +SocketAddress::SocketAddress (const struct sockaddr &s) +{ + m_socket_addr.sa = s; +} + + +SocketAddress::SocketAddress (const struct sockaddr_in &s) +{ + m_socket_addr.sa_ipv4 = s; +} + + +SocketAddress::SocketAddress (const struct sockaddr_in6 &s) +{ + m_socket_addr.sa_ipv6 = s; +} + + +SocketAddress::SocketAddress (const struct sockaddr_storage &s) +{ + m_socket_addr.sa_storage = s; +} + +//---------------------------------------------------------------------- +// SocketAddress copy constructor +//---------------------------------------------------------------------- +SocketAddress::SocketAddress (const SocketAddress& rhs) : + m_socket_addr (rhs.m_socket_addr) +{ +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +SocketAddress::~SocketAddress() +{ +} + +void +SocketAddress::Clear () +{ + memset (&m_socket_addr, 0, sizeof(m_socket_addr)); +} + +bool +SocketAddress::IsValid () const +{ + return GetLength () != 0; +} + +static socklen_t +GetFamilyLength (sa_family_t family) +{ + switch (family) + { + case AF_INET: return sizeof(struct sockaddr_in); + case AF_INET6: return sizeof(struct sockaddr_in6); + } + assert(0 && "Unsupported address family"); +} + +socklen_t +SocketAddress::GetLength () const +{ +#if defined(__APPLE__) + return m_socket_addr.sa.sa_len; +#else + return GetFamilyLength (GetFamily()); +#endif +} + +socklen_t +SocketAddress::GetMaxLength () +{ + return sizeof (sockaddr_t); +} + +sa_family_t +SocketAddress::GetFamily () const +{ + return m_socket_addr.sa.sa_family; +} + +void +SocketAddress::SetFamily (sa_family_t family) +{ + m_socket_addr.sa.sa_family = family; +#if defined(__APPLE__) + m_socket_addr.sa.sa_len = GetFamilyLength (family); +#endif +} + +in_port_t +SocketAddress::GetPort () const +{ + switch (GetFamily()) + { + case AF_INET: return m_socket_addr.sa_ipv4.sin_port; + case AF_INET6: return m_socket_addr.sa_ipv6.sin6_port; + } + return 0; +} + +bool +SocketAddress::SetPort (in_port_t port) +{ + switch (GetFamily()) + { + case AF_INET: + m_socket_addr.sa_ipv4.sin_port = htons(port); + return true; + + case AF_INET6: + m_socket_addr.sa_ipv6.sin6_port = htons(port); + return true; + } + return false; +} + +//---------------------------------------------------------------------- +// SocketAddress assignment operator +//---------------------------------------------------------------------- +const SocketAddress& +SocketAddress::operator=(const SocketAddress& rhs) +{ + if (this != &rhs) + m_socket_addr = rhs.m_socket_addr; + return *this; +} + +const SocketAddress& +SocketAddress::operator=(const struct addrinfo *addr_info) +{ + Clear(); + if (addr_info && + addr_info->ai_addr && + addr_info->ai_addrlen > 0&& + addr_info->ai_addrlen <= sizeof m_socket_addr) + { + ::memcpy (&m_socket_addr, + addr_info->ai_addr, + addr_info->ai_addrlen); + } + return *this; +} + +const SocketAddress& +SocketAddress::operator=(const struct sockaddr &s) +{ + m_socket_addr.sa = s; + return *this; +} + +const SocketAddress& +SocketAddress::operator=(const struct sockaddr_in &s) +{ + m_socket_addr.sa_ipv4 = s; + return *this; +} + +const SocketAddress& +SocketAddress::operator=(const struct sockaddr_in6 &s) +{ + m_socket_addr.sa_ipv6 = s; + return *this; +} + +const SocketAddress& +SocketAddress::operator=(const struct sockaddr_storage &s) +{ + m_socket_addr.sa_storage = s; + return *this; +} + +bool +SocketAddress::SetAddress (const struct addrinfo *hints_ptr, + const char *host, + const char *service, + struct addrinfo *addr_info_ptr) +{ + struct addrinfo *service_info_list = NULL; + int err = ::getaddrinfo (host, service, hints_ptr, &service_info_list); + if (err == 0 && service_info_list) + { + if (addr_info_ptr) + *addr_info_ptr = *service_info_list; + *this = service_info_list; + } + else + Clear(); + + :: freeaddrinfo (service_info_list); + + const bool is_valid = IsValid(); + if (!is_valid) + { + if (addr_info_ptr) + ::memset (addr_info_ptr, 0, sizeof(struct addrinfo)); + } + return is_valid; +} + + +bool +SocketAddress::SetToLocalhost (sa_family_t family, in_port_t port) +{ + switch (family) + { + case AF_INET: + SetFamily (AF_INET); + if (SetPort (port)) + { + m_socket_addr.sa_ipv4.sin_addr.s_addr = htonl (INADDR_ANY); + return true; + } + break; + + case AF_INET6: + SetFamily (AF_INET6); + if (SetPort (port)) + { + m_socket_addr.sa_ipv6.sin6_addr = in6addr_any; + return true; + } + break; + + } + Clear(); + return false; +} diff --git a/contrib/llvm/tools/lldb/source/Host/common/Symbols.cpp b/contrib/llvm/tools/lldb/source/Host/common/Symbols.cpp new file mode 100644 index 00000000000..7189269677d --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Host/common/Symbols.cpp @@ -0,0 +1,163 @@ +//===-- Symbols.cpp ---------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Host/Symbols.h" +#include "lldb/Core/ArchSpec.h" +#include "lldb/Core/DataBuffer.h" +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/ModuleSpec.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Core/Timer.h" +#include "lldb/Core/UUID.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Target/Target.h" + +using namespace lldb; +using namespace lldb_private; + +#if defined (__linux__) || defined (__FreeBSD__) + +FileSpec +Symbols::LocateExecutableObjectFile (const ModuleSpec &module_spec) +{ + // FIXME + return FileSpec(); +} + +FileSpec +Symbols::LocateExecutableSymbolFile (const ModuleSpec &module_spec) +{ + const char *symbol_filename = module_spec.GetSymbolFileSpec().GetFilename().AsCString(); + if (!symbol_filename || !symbol_filename[0]) + return FileSpec(); + + FileSpecList debug_file_search_paths (Target::GetDefaultDebugFileSearchPaths()); + + // Add module directory. + const ConstString &file_dir = module_spec.GetFileSpec().GetDirectory(); + debug_file_search_paths.AppendIfUnique (FileSpec(file_dir.AsCString("."), true)); + + // Add current working directory. + debug_file_search_paths.AppendIfUnique (FileSpec(".", true)); + + // Add /usr/lib/debug directory. + debug_file_search_paths.AppendIfUnique (FileSpec("/usr/lib/debug", true)); + + std::string uuid_str; + const UUID &module_uuid = module_spec.GetUUID(); + if (module_uuid.IsValid()) + { + // Some debug files are stored in the .build-id directory like this: + // /usr/lib/debug/.build-id/ff/e7fe727889ad82bb153de2ad065b2189693315.debug + uuid_str = module_uuid.GetAsString(""); + uuid_str.insert (2, 1, '/'); + uuid_str = uuid_str + ".debug"; + } + + // Get full path to our module. Needed to check debug files like this: + // /usr/lib/debug/usr/lib/libboost_date_time.so.1.46.1 + std::string module_filename = module_spec.GetFileSpec().GetPath(); + + size_t num_directories = debug_file_search_paths.GetSize(); + for (size_t idx = 0; idx < num_directories; ++idx) + { + FileSpec dirspec = debug_file_search_paths.GetFileSpecAtIndex (idx); + dirspec.ResolvePath(); + if (!dirspec.Exists() || !dirspec.IsDirectory()) + continue; + + std::vector files; + std::string dirname = dirspec.GetPath(); + + files.push_back (dirname + "/" + symbol_filename); + files.push_back (dirname + "/.debug/" + symbol_filename); + files.push_back (dirname + "/.build-id/" + uuid_str); + files.push_back (dirname + module_filename); + + const uint32_t num_files = files.size(); + for (size_t idx_file = 0; idx_file < num_files; ++idx_file) + { + const std::string &filename = files[idx_file]; + FileSpec file_spec (filename.c_str(), true); + + if (file_spec == module_spec.GetFileSpec()) + continue; + + if (file_spec.Exists()) + { + lldb_private::ModuleSpecList specs; + const size_t num_specs = ObjectFile::GetModuleSpecifications (file_spec, 0, 0, specs); + assert (num_specs <= 1 && "Symbol Vendor supports only a single architecture"); + if (num_specs == 1) + { + ModuleSpec mspec; + if (specs.GetModuleSpecAtIndex (0, mspec)) + { + if (mspec.GetUUID() == module_uuid) + return file_spec; + } + } + } + } + } + + return FileSpec(); +} + +FileSpec +Symbols::FindSymbolFileInBundle (const FileSpec& symfile_bundle, + const lldb_private::UUID *uuid, + const ArchSpec *arch) +{ + // FIXME + return FileSpec(); +} + +bool +Symbols::DownloadObjectAndSymbolFile (ModuleSpec &module_spec, bool force_lookup) +{ + // Fill in the module_spec.GetFileSpec() for the object file and/or the + // module_spec.GetSymbolFileSpec() for the debug symbols file. + return false; +} + +#elif !defined (__APPLE__) + +FileSpec +Symbols::LocateExecutableObjectFile (const ModuleSpec &module_spec) +{ + // FIXME + return FileSpec(); +} + +FileSpec +Symbols::LocateExecutableSymbolFile (const ModuleSpec &module_spec) +{ + // FIXME + return FileSpec(); +} + +FileSpec +Symbols::FindSymbolFileInBundle (const FileSpec& symfile_bundle, + const lldb_private::UUID *uuid, + const ArchSpec *arch) +{ + return FileSpec(); +} + +bool +Symbols::DownloadObjectAndSymbolFile (ModuleSpec &module_spec, bool force_lookup) +{ + // Fill in the module_spec.GetFileSpec() for the object file and/or the + // module_spec.GetSymbolFileSpec() for the debug symbols file. + return false; +} + +#endif diff --git a/contrib/llvm/tools/lldb/source/Host/common/Terminal.cpp b/contrib/llvm/tools/lldb/source/Host/common/Terminal.cpp new file mode 100644 index 00000000000..89d21cf0bf6 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Host/common/Terminal.cpp @@ -0,0 +1,307 @@ +//===-- Terminal.cpp --------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Host/Terminal.h" +#include "lldb/Host/Config.h" + +#include +#include +#include + +#ifdef LLDB_CONFIG_TERMIOS_SUPPORTED +#include +#endif + + +using namespace lldb_private; + +bool +Terminal::IsATerminal () const +{ + return m_fd >= 0 && ::isatty (m_fd); +} + + +bool +Terminal::SetEcho (bool enabled) +{ + if (FileDescriptorIsValid()) + { +#ifdef LLDB_CONFIG_TERMIOS_SUPPORTED + if (IsATerminal ()) + { + struct termios fd_termios; + if (::tcgetattr(m_fd, &fd_termios) == 0) + { + bool set_corectly = false; + if (enabled) + { + if (fd_termios.c_lflag & ECHO) + set_corectly = true; + else + fd_termios.c_lflag |= ECHO; + } + else + { + if (fd_termios.c_lflag & ECHO) + fd_termios.c_lflag &= ~ECHO; + else + set_corectly = true; + } + + if (set_corectly) + return true; + return ::tcsetattr (m_fd, TCSANOW, &fd_termios) == 0; + } + } +#endif // #ifdef LLDB_CONFIG_TERMIOS_SUPPORTED + } + return false; +} + +bool +Terminal::SetCanonical (bool enabled) +{ + if (FileDescriptorIsValid()) + { +#ifdef LLDB_CONFIG_TERMIOS_SUPPORTED + if (IsATerminal ()) + { + struct termios fd_termios; + if (::tcgetattr(m_fd, &fd_termios) == 0) + { + bool set_corectly = false; + if (enabled) + { + if (fd_termios.c_lflag & ICANON) + set_corectly = true; + else + fd_termios.c_lflag |= ICANON; + } + else + { + if (fd_termios.c_lflag & ICANON) + fd_termios.c_lflag &= ~ICANON; + else + set_corectly = true; + } + + if (set_corectly) + return true; + return ::tcsetattr (m_fd, TCSANOW, &fd_termios) == 0; + } + } +#endif // #ifdef LLDB_CONFIG_TERMIOS_SUPPORTED + } + return false; +} + +//---------------------------------------------------------------------- +// Default constructor +//---------------------------------------------------------------------- +TerminalState::TerminalState() : + m_tty(), + m_tflags(-1), + m_termios_ap(), + m_process_group(-1) +{ +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +TerminalState::~TerminalState() +{ +} + +void +TerminalState::Clear () +{ + m_tty.Clear(); + m_tflags = -1; + m_termios_ap.reset(); + m_process_group = -1; +} + +//---------------------------------------------------------------------- +// Save the current state of the TTY for the file descriptor "fd" +// and if "save_process_group" is true, attempt to save the process +// group info for the TTY. +//---------------------------------------------------------------------- +bool +TerminalState::Save (int fd, bool save_process_group) +{ + m_tty.SetFileDescriptor(fd); + if (m_tty.IsATerminal()) + { + m_tflags = ::fcntl (fd, F_GETFL, 0); +#ifdef LLDB_CONFIG_TERMIOS_SUPPORTED + if (m_termios_ap.get() == NULL) + m_termios_ap.reset (new struct termios); + int err = ::tcgetattr (fd, m_termios_ap.get()); + if (err != 0) + m_termios_ap.reset(); +#endif // #ifdef LLDB_CONFIG_TERMIOS_SUPPORTED + if (save_process_group) + m_process_group = ::tcgetpgrp (0); + else + m_process_group = -1; + } + else + { + m_tty.Clear(); + m_tflags = -1; + m_termios_ap.reset(); + m_process_group = -1; + } + return IsValid(); +} + +//---------------------------------------------------------------------- +// Restore the state of the TTY using the cached values from a +// previous call to Save(). +//---------------------------------------------------------------------- +bool +TerminalState::Restore () const +{ + if (IsValid()) + { + const int fd = m_tty.GetFileDescriptor(); + if (TFlagsIsValid()) + fcntl (fd, F_SETFL, m_tflags); + +#ifdef LLDB_CONFIG_TERMIOS_SUPPORTED + if (TTYStateIsValid()) + tcsetattr (fd, TCSANOW, m_termios_ap.get()); +#endif // #ifdef LLDB_CONFIG_TERMIOS_SUPPORTED + + if (ProcessGroupIsValid()) + { + // Save the original signal handler. + void (*saved_sigttou_callback) (int) = NULL; + saved_sigttou_callback = (void (*)(int)) signal (SIGTTOU, SIG_IGN); + // Set the process group + tcsetpgrp (fd, m_process_group); + // Restore the original signal handler. + signal (SIGTTOU, saved_sigttou_callback); + } + return true; + } + return false; +} + + + + +//---------------------------------------------------------------------- +// Returns true if this object has valid saved TTY state settings +// that can be used to restore a previous state. +//---------------------------------------------------------------------- +bool +TerminalState::IsValid() const +{ + return m_tty.FileDescriptorIsValid () && (TFlagsIsValid() || TTYStateIsValid()); +} + +//---------------------------------------------------------------------- +// Returns true if m_tflags is valid +//---------------------------------------------------------------------- +bool +TerminalState::TFlagsIsValid() const +{ + return m_tflags != -1; +} + +//---------------------------------------------------------------------- +// Returns true if m_ttystate is valid +//---------------------------------------------------------------------- +bool +TerminalState::TTYStateIsValid() const +{ + return m_termios_ap.get() != 0; +} + +//---------------------------------------------------------------------- +// Returns true if m_process_group is valid +//---------------------------------------------------------------------- +bool +TerminalState::ProcessGroupIsValid() const +{ + return m_process_group != -1; +} + +//------------------------------------------------------------------ +// Constructor +//------------------------------------------------------------------ +TerminalStateSwitcher::TerminalStateSwitcher () : + m_currentState(UINT32_MAX) +{ +} + +//------------------------------------------------------------------ +// Destructor +//------------------------------------------------------------------ +TerminalStateSwitcher::~TerminalStateSwitcher () +{ +} + +//------------------------------------------------------------------ +// Returns the number of states that this switcher contains +//------------------------------------------------------------------ +uint32_t +TerminalStateSwitcher::GetNumberOfStates() const +{ + return sizeof(m_ttystates)/sizeof(TerminalState); +} + +//------------------------------------------------------------------ +// Restore the state at index "idx". +// +// Returns true if the restore was successful, false otherwise. +//------------------------------------------------------------------ +bool +TerminalStateSwitcher::Restore (uint32_t idx) const +{ + const uint32_t num_states = GetNumberOfStates(); + if (idx >= num_states) + return false; + + // See if we already are in this state? + if (m_currentState < num_states && (idx == m_currentState) && m_ttystates[idx].IsValid()) + return true; + + // Set the state to match the index passed in and only update the + // current state if there are no errors. + if (m_ttystates[idx].Restore()) + { + m_currentState = idx; + return true; + } + + // We failed to set the state. The tty state was invalid or not + // initialized. + return false; +} + +//------------------------------------------------------------------ +// Save the state at index "idx" for file descriptor "fd" and +// save the process group if requested. +// +// Returns true if the restore was successful, false otherwise. +//------------------------------------------------------------------ +bool +TerminalStateSwitcher::Save (uint32_t idx, int fd, bool save_process_group) +{ + const uint32_t num_states = GetNumberOfStates(); + if (idx < num_states) + return m_ttystates[idx].Save(fd, save_process_group); + return false; +} + + diff --git a/contrib/llvm/tools/lldb/source/Host/common/TimeValue.cpp b/contrib/llvm/tools/lldb/source/Host/common/TimeValue.cpp new file mode 100644 index 00000000000..303ac94058b --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Host/common/TimeValue.cpp @@ -0,0 +1,210 @@ +//===-- TimeValue.cpp -------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Host/TimeValue.h" + +// C Includes +#include +#include +#include +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/Stream.h" + + +using namespace lldb_private; + +//---------------------------------------------------------------------- +// TimeValue constructor +//---------------------------------------------------------------------- +TimeValue::TimeValue() : + m_nano_seconds (0) +{ +} + +//---------------------------------------------------------------------- +// TimeValue copy constructor +//---------------------------------------------------------------------- +TimeValue::TimeValue(const TimeValue& rhs) : + m_nano_seconds (rhs.m_nano_seconds) +{ +} + +TimeValue::TimeValue(const struct timespec& ts) : + m_nano_seconds ((uint64_t) ts.tv_sec * NanoSecPerSec + ts.tv_nsec) +{ +} + +TimeValue::TimeValue(const struct timeval& tv) : + m_nano_seconds ((uint64_t) tv.tv_sec * NanoSecPerSec + (uint64_t) tv.tv_usec * NanoSecPerMicroSec) +{ +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +TimeValue::~TimeValue() +{ +} + + +uint64_t +TimeValue::GetAsNanoSecondsSinceJan1_1970() const +{ + return m_nano_seconds; +} + +uint64_t +TimeValue::GetAsMicroSecondsSinceJan1_1970() const +{ + return m_nano_seconds / NanoSecPerMicroSec; +} + +uint64_t +TimeValue::GetAsSecondsSinceJan1_1970() const +{ + return m_nano_seconds / NanoSecPerSec; +} + + + +struct timespec +TimeValue::GetAsTimeSpec () const +{ + struct timespec ts; + ts.tv_sec = m_nano_seconds / NanoSecPerSec; + ts.tv_nsec = m_nano_seconds % NanoSecPerSec; + return ts; +} + +struct timeval +TimeValue::GetAsTimeVal () const +{ + struct timeval tv; + tv.tv_sec = m_nano_seconds / NanoSecPerSec; + tv.tv_usec = (m_nano_seconds % NanoSecPerSec) / NanoSecPerMicroSec; + return tv; +} + +void +TimeValue::Clear () +{ + m_nano_seconds = 0; +} + +bool +TimeValue::IsValid () const +{ + return m_nano_seconds != 0; +} + +void +TimeValue::OffsetWithSeconds (uint64_t sec) +{ + m_nano_seconds += sec * NanoSecPerSec; +} + +void +TimeValue::OffsetWithMicroSeconds (uint64_t usec) +{ + m_nano_seconds += usec * NanoSecPerMicroSec; +} + +void +TimeValue::OffsetWithNanoSeconds (uint64_t nsec) +{ + m_nano_seconds += nsec; +} + +TimeValue +TimeValue::Now() +{ + struct timeval tv; + gettimeofday(&tv, NULL); + TimeValue now(tv); + return now; +} + +//---------------------------------------------------------------------- +// TimeValue assignment operator +//---------------------------------------------------------------------- +const TimeValue& +TimeValue::operator=(const TimeValue& rhs) +{ + m_nano_seconds = rhs.m_nano_seconds; + return *this; +} + +void +TimeValue::Dump (Stream *s, uint32_t width) const +{ + if (s == NULL) + return; + + char time_buf[32]; + time_t time = GetAsSecondsSinceJan1_1970(); + char *time_cstr = ::ctime_r(&time, time_buf); + if (time_cstr) + { + char *newline = ::strpbrk(time_cstr, "\n\r"); + if (newline) + *newline = '\0'; + if (width > 0) + s->Printf("%-*s", width, time_cstr); + else + s->PutCString(time_cstr); + } + else if (width > 0) + s->Printf("%-*s", width, ""); +} + +bool +lldb_private::operator == (const TimeValue &lhs, const TimeValue &rhs) +{ + return lhs.GetAsNanoSecondsSinceJan1_1970() == rhs.GetAsNanoSecondsSinceJan1_1970(); +} + +bool +lldb_private::operator != (const TimeValue &lhs, const TimeValue &rhs) +{ + return lhs.GetAsNanoSecondsSinceJan1_1970() != rhs.GetAsNanoSecondsSinceJan1_1970(); +} + +bool +lldb_private::operator < (const TimeValue &lhs, const TimeValue &rhs) +{ + return lhs.GetAsNanoSecondsSinceJan1_1970() < rhs.GetAsNanoSecondsSinceJan1_1970(); +} + +bool +lldb_private::operator <= (const TimeValue &lhs, const TimeValue &rhs) +{ + return lhs.GetAsNanoSecondsSinceJan1_1970() <= rhs.GetAsNanoSecondsSinceJan1_1970(); +} + +bool +lldb_private::operator > (const TimeValue &lhs, const TimeValue &rhs) +{ + return lhs.GetAsNanoSecondsSinceJan1_1970() > rhs.GetAsNanoSecondsSinceJan1_1970(); +} + +bool +lldb_private::operator >= (const TimeValue &lhs, const TimeValue &rhs) +{ + return lhs.GetAsNanoSecondsSinceJan1_1970() >= rhs.GetAsNanoSecondsSinceJan1_1970(); +} + +uint64_t +lldb_private::operator - (const TimeValue &lhs, const TimeValue &rhs) +{ + return lhs.GetAsNanoSecondsSinceJan1_1970() - rhs.GetAsNanoSecondsSinceJan1_1970(); +} + + diff --git a/contrib/llvm/tools/lldb/source/Host/freebsd/Host.cpp b/contrib/llvm/tools/lldb/source/Host/freebsd/Host.cpp new file mode 100644 index 00000000000..405e7eacf5a --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Host/freebsd/Host.cpp @@ -0,0 +1,337 @@ +//===-- source/Host/freebsd/Host.cpp ------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// C Includes +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + + +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/Error.h" +#include "lldb/Host/Endian.h" +#include "lldb/Host/Host.h" +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/StreamFile.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Target/Process.h" + +#include "lldb/Core/DataBufferHeap.h" +#include "lldb/Core/DataExtractor.h" +#include "llvm/Support/Host.h" + + +extern "C" { + extern char **environ; +} + +using namespace lldb; +using namespace lldb_private; + + +class FreeBSDThread +{ +public: + FreeBSDThread(const char *thread_name) + { + Host::SetThreadName (LLDB_INVALID_PROCESS_ID, LLDB_INVALID_THREAD_ID, thread_name); + } + static void PThreadDestructor (void *v) + { + delete (FreeBSDThread*)v; + } +}; + +static pthread_once_t g_thread_create_once = PTHREAD_ONCE_INIT; +static pthread_key_t g_thread_create_key = 0; + +static void +InitThreadCreated() +{ + ::pthread_key_create (&g_thread_create_key, FreeBSDThread::PThreadDestructor); +} + +void +Host::ThreadCreated (const char *thread_name) +{ + ::pthread_once (&g_thread_create_once, InitThreadCreated); + if (g_thread_create_key) + { + ::pthread_setspecific (g_thread_create_key, new FreeBSDThread(thread_name)); + } + + Host::SetShortThreadName (LLDB_INVALID_PROCESS_ID, LLDB_INVALID_THREAD_ID, thread_name, 16); +} + +std::string +Host::GetThreadName (lldb::pid_t pid, lldb::tid_t tid) +{ + std::string thread_name; + return thread_name; +} + +void +Host::Backtrace (Stream &strm, uint32_t max_frames) +{ + char backtrace_path[] = "/tmp/lldb-backtrace-tmp-XXXXXX"; + int backtrace_fd = ::mkstemp (backtrace_path); + if (backtrace_fd != -1) + { + std::vector frame_buffer (max_frames, NULL); + int count = ::backtrace (&frame_buffer[0], frame_buffer.size()); + ::backtrace_symbols_fd (&frame_buffer[0], count, backtrace_fd); + + const off_t buffer_size = ::lseek(backtrace_fd, 0, SEEK_CUR); + + if (::lseek(backtrace_fd, 0, SEEK_SET) == 0) + { + char *buffer = (char *)::malloc (buffer_size); + if (buffer) + { + ssize_t bytes_read = ::read (backtrace_fd, buffer, buffer_size); + if (bytes_read > 0) + strm.Write(buffer, bytes_read); + ::free (buffer); + } + } + ::close (backtrace_fd); + ::unlink (backtrace_path); + } +} + +size_t +Host::GetEnvironment (StringList &env) +{ + char *v; + char **var = environ; + for (; var != NULL && *var != NULL; ++var) { + v = strchr(*var, (int)'-'); + if (v == NULL) + continue; + env.AppendString(v); + } + return env.GetSize(); +} + +bool +Host::GetOSVersion(uint32_t &major, + uint32_t &minor, + uint32_t &update) +{ + struct utsname un; + int status; + + if (uname(&un) < 0) + return false; + + status = sscanf(un.release, "%u.%u", &major, &minor); + return status == 2; +} + +Error +Host::LaunchProcess (ProcessLaunchInfo &launch_info) +{ + Error error; + assert(!"Not implemented yet!!!"); + return error; +} + +bool +Host::GetOSBuildString (std::string &s) +{ + int mib[2] = { CTL_KERN, KERN_OSREV }; + char cstr[PATH_MAX]; + size_t cstr_len = sizeof(cstr); + if (::sysctl (mib, 2, cstr, &cstr_len, NULL, 0) == 0) + { + s.assign (cstr, cstr_len); + return true; + } + s.clear(); + return false; +} + +bool +Host::GetOSKernelDescription (std::string &s) +{ + int mib[2] = { CTL_KERN, KERN_VERSION }; + char cstr[PATH_MAX]; + size_t cstr_len = sizeof(cstr); + if (::sysctl (mib, 2, cstr, &cstr_len, NULL, 0) == 0) + { + s.assign (cstr, cstr_len); + return true; + } + s.clear(); + return false; +} + +static bool +GetFreeBSDProcessArgs (const ProcessInstanceInfoMatch *match_info_ptr, + ProcessInstanceInfo &process_info) +{ + if (process_info.ProcessIDIsValid()) { + int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_ARGS, (int)process_info.GetProcessID() }; + + char arg_data[8192]; + size_t arg_data_size = sizeof(arg_data); + if (::sysctl (mib, 4, arg_data, &arg_data_size , NULL, 0) == 0) + { + DataExtractor data (arg_data, arg_data_size, lldb::endian::InlHostByteOrder(), sizeof(void *)); + lldb::offset_t offset = 0; + const char *cstr; + + cstr = data.GetCStr (&offset); + if (cstr) + { + process_info.GetExecutableFile().SetFile(cstr, false); + + if (!(match_info_ptr == NULL || + NameMatches (process_info.GetExecutableFile().GetFilename().GetCString(), + match_info_ptr->GetNameMatchType(), + match_info_ptr->GetProcessInfo().GetName()))) + return false; + + Args &proc_args = process_info.GetArguments(); + while (1) + { + const uint8_t *p = data.PeekData(offset, 1); + while ((p != NULL) && (*p == '\0') && offset < arg_data_size) + { + ++offset; + p = data.PeekData(offset, 1); + } + if (p == NULL || offset >= arg_data_size) + return true; + + cstr = data.GetCStr(&offset); + if (cstr) + proc_args.AppendArgument(cstr); + else + return true; + } + } + } + } + return false; +} + +static bool +GetFreeBSDProcessCPUType (ProcessInstanceInfo &process_info) +{ + if (process_info.ProcessIDIsValid()) { + process_info.GetArchitecture() = Host::GetArchitecture (Host::eSystemDefaultArchitecture); + return true; + } + process_info.GetArchitecture().Clear(); + return false; +} + +static bool +GetFreeBSDProcessUserAndGroup(ProcessInstanceInfo &process_info) +{ + struct kinfo_proc proc_kinfo; + size_t proc_kinfo_size; + + if (process_info.ProcessIDIsValid()) + { + int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, + (int)process_info.GetProcessID() }; + proc_kinfo_size = sizeof(struct kinfo_proc); + + if (::sysctl (mib, 4, &proc_kinfo, &proc_kinfo_size, NULL, 0) == 0) + { + if (proc_kinfo_size > 0) + { + process_info.SetParentProcessID (proc_kinfo.ki_ppid); + process_info.SetUserID (proc_kinfo.ki_ruid); + process_info.SetGroupID (proc_kinfo.ki_rgid); + process_info.SetEffectiveUserID (proc_kinfo.ki_uid); + if (proc_kinfo.ki_ngroups > 0) + process_info.SetEffectiveGroupID (proc_kinfo.ki_groups[0]); + else + process_info.SetEffectiveGroupID (UINT32_MAX); + return true; + } + } + } + process_info.SetParentProcessID (LLDB_INVALID_PROCESS_ID); + process_info.SetUserID (UINT32_MAX); + process_info.SetGroupID (UINT32_MAX); + process_info.SetEffectiveUserID (UINT32_MAX); + process_info.SetEffectiveGroupID (UINT32_MAX); + return false; +} + +bool +Host::GetProcessInfo (lldb::pid_t pid, ProcessInstanceInfo &process_info) +{ + process_info.SetProcessID(pid); + if (GetFreeBSDProcessArgs(NULL, process_info)) { + // should use libprocstat instead of going right into sysctl? + GetFreeBSDProcessCPUType(process_info); + GetFreeBSDProcessUserAndGroup(process_info); + return true; + } + process_info.Clear(); + return false; +} + +lldb::DataBufferSP +Host::GetAuxvData(lldb_private::Process *process) +{ + int mib[2] = { CTL_KERN, KERN_PS_STRINGS }; + void *ps_strings_addr, *auxv_addr; + size_t ps_strings_size = sizeof(void *); + Elf_Auxinfo aux_info[AT_COUNT]; + struct ps_strings ps_strings; + struct ptrace_io_desc pid; + DataBufferSP buf_sp; + std::unique_ptr buf_ap(new DataBufferHeap(1024, 0)); + + if (::sysctl(mib, 2, &ps_strings_addr, &ps_strings_size, NULL, 0) == 0) { + pid.piod_op = PIOD_READ_D; + pid.piod_addr = &ps_strings; + pid.piod_offs = ps_strings_addr; + pid.piod_len = sizeof(ps_strings); + if (::ptrace(PT_IO, process->GetID(), (caddr_t)&pid, 0)) { + perror("failed to fetch ps_strings"); + buf_ap.release(); + goto done; + } + + auxv_addr = ps_strings.ps_envstr + ps_strings.ps_nenvstr + 1; + + pid.piod_addr = aux_info; + pid.piod_offs = auxv_addr; + pid.piod_len = sizeof(aux_info); + if (::ptrace(PT_IO, process->GetID(), (caddr_t)&pid, 0)) { + perror("failed to fetch aux_info"); + buf_ap.release(); + goto done; + } + memcpy(buf_ap->GetBytes(), aux_info, pid.piod_len); + buf_sp.reset(buf_ap.release()); + } else { + perror("sysctl failed on ps_strings"); + } + + done: + return buf_sp; +} diff --git a/contrib/llvm/tools/lldb/source/Interpreter/Args.cpp b/contrib/llvm/tools/lldb/source/Interpreter/Args.cpp new file mode 100644 index 00000000000..e6d20435803 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Interpreter/Args.cpp @@ -0,0 +1,1789 @@ +//===-- Args.cpp ------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +// C Includes +#include +#include +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Interpreter/Args.h" +#include "lldb/Core/Stream.h" +#include "lldb/Core/StreamFile.h" +#include "lldb/Core/StreamString.h" +#include "lldb/DataFormatters/FormatManager.h" +#include "lldb/Interpreter/Options.h" +#include "lldb/Interpreter/CommandReturnObject.h" +#include "lldb/Target/Process.h" +//#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/StackFrame.h" +#include "lldb/Target/Target.h" +//#include "lldb/Target/Thread.h" + +using namespace lldb; +using namespace lldb_private; + +//---------------------------------------------------------------------- +// Args constructor +//---------------------------------------------------------------------- +Args::Args (const char *command) : + m_args(), + m_argv(), + m_args_quote_char() +{ + if (command) + SetCommandString (command); +} + + +Args::Args (const char *command, size_t len) : + m_args(), + m_argv(), + m_args_quote_char() +{ + if (command && len) + SetCommandString (command, len); +} + +//---------------------------------------------------------------------- +// We have to be very careful on the copy constructor of this class +// to make sure we copy all of the string values, but we can't copy the +// rhs.m_argv into m_argv since it will point to the "const char *" c +// strings in rhs.m_args. We need to copy the string list and update our +// own m_argv appropriately. +//---------------------------------------------------------------------- +Args::Args (const Args &rhs) : + m_args (rhs.m_args), + m_argv (), + m_args_quote_char(rhs.m_args_quote_char) +{ + UpdateArgvFromArgs(); +} + +//---------------------------------------------------------------------- +// We have to be very careful on the copy constructor of this class +// to make sure we copy all of the string values, but we can't copy the +// rhs.m_argv into m_argv since it will point to the "const char *" c +// strings in rhs.m_args. We need to copy the string list and update our +// own m_argv appropriately. +//---------------------------------------------------------------------- +const Args & +Args::operator= (const Args &rhs) +{ + // Make sure we aren't assigning to self + if (this != &rhs) + { + m_args = rhs.m_args; + m_args_quote_char = rhs.m_args_quote_char; + UpdateArgvFromArgs(); + } + return *this; +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +Args::~Args () +{ +} + +void +Args::Dump (Stream *s) +{ + const size_t argc = m_argv.size(); + for (size_t i=0; iIndent(); + const char *arg_cstr = m_argv[i]; + if (arg_cstr) + s->Printf("argv[%zi]=\"%s\"\n", i, arg_cstr); + else + s->Printf("argv[%zi]=NULL\n", i); + } + s->EOL(); +} + +bool +Args::GetCommandString (std::string &command) const +{ + command.clear(); + const size_t argc = GetArgumentCount(); + for (size_t i=0; i 0) + command += ' '; + command += m_argv[i]; + } + return argc > 0; +} + +bool +Args::GetQuotedCommandString (std::string &command) const +{ + command.clear (); + const size_t argc = GetArgumentCount(); + for (size_t i = 0; i < argc; ++i) + { + if (i > 0) + command.append (1, ' '); + char quote_char = GetArgumentQuoteCharAtIndex(i); + if (quote_char) + { + command.append (1, quote_char); + command.append (m_argv[i]); + command.append (1, quote_char); + } + else + command.append (m_argv[i]); + } + return argc > 0; +} + +void +Args::SetCommandString (const char *command, size_t len) +{ + // Use std::string to make sure we get a NULL terminated string we can use + // as "command" could point to a string within a large string.... + std::string null_terminated_command(command, len); + SetCommandString(null_terminated_command.c_str()); +} + +void +Args::SetCommandString (const char *command) +{ + m_args.clear(); + m_argv.clear(); + m_args_quote_char.clear(); + + if (command && command[0]) + { + static const char *k_space_separators = " \t"; + static const char *k_space_separators_with_slash_and_quotes = " \t \\'\""; + const char *arg_end = NULL; + const char *arg_pos; + for (arg_pos = command; + arg_pos && arg_pos[0]; + arg_pos = arg_end) + { + // Skip any leading space separators + const char *arg_start = ::strspn (arg_pos, k_space_separators) + arg_pos; + + // If there were only space separators to the end of the line, then + // we're done. + if (*arg_start == '\0') + break; + + // Arguments can be split into multiple discontiguous pieces, + // for example: + // "Hello ""World" + // this would result in a single argument "Hello World" (without/ + // the quotes) since the quotes would be removed and there is + // not space between the strings. So we need to keep track of the + // current start of each argument piece in "arg_piece_start" + const char *arg_piece_start = arg_start; + arg_pos = arg_piece_start; + + std::string arg; + // Since we can have multiple quotes that form a single command + // in a command like: "Hello "world'!' (which will make a single + // argument "Hello world!") we remember the first quote character + // we encounter and use that for the quote character. + char first_quote_char = '\0'; + char quote_char = '\0'; + bool arg_complete = false; + + do + { + arg_end = ::strcspn (arg_pos, k_space_separators_with_slash_and_quotes) + arg_pos; + + switch (arg_end[0]) + { + default: + assert (!"Unhandled case statement, we must handle this..."); + break; + + case '\0': + // End of C string + if (arg_piece_start && arg_piece_start[0]) + arg.append (arg_piece_start); + arg_complete = true; + break; + + case '\\': + // Backslash character + switch (arg_end[1]) + { + case '\0': + arg.append (arg_piece_start); + ++arg_end; + arg_complete = true; + break; + + default: + if (quote_char == '\0') + { + arg.append (arg_piece_start, arg_end - arg_piece_start); + if (arg_end[1] != '\0') + { + arg.append (arg_end + 1, 1); + arg_pos = arg_end + 2; + arg_piece_start = arg_pos; + } + } + else + arg_pos = arg_end + 2; + break; + } + break; + + case '"': + case '\'': + case '`': + // Quote characters + if (quote_char) + { + // We found a quote character while inside a quoted + // character argument. If it matches our current quote + // character, this ends the effect of the quotes. If it + // doesn't we ignore it. + if (quote_char == arg_end[0]) + { + arg.append (arg_piece_start, arg_end - arg_piece_start); + // Clear the quote character and let parsing + // continue (we need to watch for things like: + // "Hello ""World" + // "Hello "World + // "Hello "'World' + // All of which will result in a single argument "Hello World" + quote_char = '\0'; // Note that we are no longer inside quotes + arg_pos = arg_end + 1; // Skip the quote character + arg_piece_start = arg_pos; // Note we are starting from later in the string + } + else + { + // different quote, skip it and keep going + arg_pos = arg_end + 1; + } + } + else + { + // We found the start of a quote scope. + // Make sure there isn't a string that precedes + // the start of a quote scope like: + // Hello" World" + // If so, then add the "Hello" to the arg + if (arg_end > arg_piece_start) + arg.append (arg_piece_start, arg_end - arg_piece_start); + + // Enter into a quote scope + quote_char = arg_end[0]; + + if (first_quote_char == '\0') + first_quote_char = quote_char; + + arg_pos = arg_end; + ++arg_pos; // Skip the quote character + arg_piece_start = arg_pos; // Note we are starting from later in the string + + // Skip till the next quote character + const char *end_quote = ::strchr (arg_piece_start, quote_char); + while (end_quote && end_quote[-1] == '\\') + { + // Don't skip the quote character if it is + // preceded by a '\' character + end_quote = ::strchr (end_quote + 1, quote_char); + } + + if (end_quote) + { + if (end_quote > arg_piece_start) + arg.append (arg_piece_start, end_quote - arg_piece_start); + + // If the next character is a space or the end of + // string, this argument is complete... + if (end_quote[1] == ' ' || end_quote[1] == '\t' || end_quote[1] == '\0') + { + arg_complete = true; + arg_end = end_quote + 1; + } + else + { + arg_pos = end_quote + 1; + arg_piece_start = arg_pos; + } + quote_char = '\0'; + } + else + { + // Consume the rest of the string as there was no terminating quote + arg.append(arg_piece_start); + arg_end = arg_piece_start + strlen(arg_piece_start); + arg_complete = true; + } + } + break; + + case ' ': + case '\t': + if (quote_char) + { + // We are currently processing a quoted character and found + // a space character, skip any spaces and keep trying to find + // the end of the argument. + arg_pos = ::strspn (arg_end, k_space_separators) + arg_end; + } + else + { + // We are not inside any quotes, we just found a space after an + // argument + if (arg_end > arg_piece_start) + arg.append (arg_piece_start, arg_end - arg_piece_start); + arg_complete = true; + } + break; + } + } while (!arg_complete); + + m_args.push_back(arg); + m_args_quote_char.push_back (first_quote_char); + } + UpdateArgvFromArgs(); + } +} + +void +Args::UpdateArgsAfterOptionParsing() +{ + // Now m_argv might be out of date with m_args, so we need to fix that + arg_cstr_collection::const_iterator argv_pos, argv_end = m_argv.end(); + arg_sstr_collection::iterator args_pos; + arg_quote_char_collection::iterator quotes_pos; + + for (argv_pos = m_argv.begin(), args_pos = m_args.begin(), quotes_pos = m_args_quote_char.begin(); + argv_pos != argv_end && args_pos != m_args.end(); + ++argv_pos) + { + const char *argv_cstr = *argv_pos; + if (argv_cstr == NULL) + break; + + while (args_pos != m_args.end()) + { + const char *args_cstr = args_pos->c_str(); + if (args_cstr == argv_cstr) + { + // We found the argument that matches the C string in the + // vector, so we can now look for the next one + ++args_pos; + ++quotes_pos; + break; + } + else + { + quotes_pos = m_args_quote_char.erase (quotes_pos); + args_pos = m_args.erase (args_pos); + } + } + } + + if (args_pos != m_args.end()) + m_args.erase (args_pos, m_args.end()); + + if (quotes_pos != m_args_quote_char.end()) + m_args_quote_char.erase (quotes_pos, m_args_quote_char.end()); +} + +void +Args::UpdateArgvFromArgs() +{ + m_argv.clear(); + arg_sstr_collection::const_iterator pos, end = m_args.end(); + for (pos = m_args.begin(); pos != end; ++pos) + m_argv.push_back(pos->c_str()); + m_argv.push_back(NULL); + // Make sure we have enough arg quote chars in the array + if (m_args_quote_char.size() < m_args.size()) + m_args_quote_char.resize (m_argv.size()); +} + +size_t +Args::GetArgumentCount() const +{ + if (m_argv.empty()) + return 0; + return m_argv.size() - 1; +} + +const char * +Args::GetArgumentAtIndex (size_t idx) const +{ + if (idx < m_argv.size()) + return m_argv[idx]; + return NULL; +} + +char +Args::GetArgumentQuoteCharAtIndex (size_t idx) const +{ + if (idx < m_args_quote_char.size()) + return m_args_quote_char[idx]; + return '\0'; +} + +char ** +Args::GetArgumentVector() +{ + if (!m_argv.empty()) + return (char **)&m_argv[0]; + return NULL; +} + +const char ** +Args::GetConstArgumentVector() const +{ + if (!m_argv.empty()) + return (const char **)&m_argv[0]; + return NULL; +} + +void +Args::Shift () +{ + // Don't pop the last NULL terminator from the argv array + if (m_argv.size() > 1) + { + m_argv.erase(m_argv.begin()); + m_args.pop_front(); + if (!m_args_quote_char.empty()) + m_args_quote_char.erase(m_args_quote_char.begin()); + } +} + +const char * +Args::Unshift (const char *arg_cstr, char quote_char) +{ + m_args.push_front(arg_cstr); + m_argv.insert(m_argv.begin(), m_args.front().c_str()); + m_args_quote_char.insert(m_args_quote_char.begin(), quote_char); + return GetArgumentAtIndex (0); +} + +void +Args::AppendArguments (const Args &rhs) +{ + const size_t rhs_argc = rhs.GetArgumentCount(); + for (size_t i=0; i 0 && pos != end; ++pos) + --i; + + pos = m_args.insert(pos, arg_cstr); + + if (idx >= m_args_quote_char.size()) + { + m_args_quote_char.resize(idx + 1); + m_args_quote_char[idx] = quote_char; + } + else + m_args_quote_char.insert(m_args_quote_char.begin() + idx, quote_char); + + UpdateArgvFromArgs(); + return GetArgumentAtIndex(idx); +} + +const char * +Args::ReplaceArgumentAtIndex (size_t idx, const char *arg_cstr, char quote_char) +{ + // Since we are using a std::list to hold onto the copied C string and + // we don't have direct access to the elements, we have to iterate to + // find the value. + arg_sstr_collection::iterator pos, end = m_args.end(); + size_t i = idx; + for (pos = m_args.begin(); i > 0 && pos != end; ++pos) + --i; + + if (pos != end) + { + pos->assign(arg_cstr); + assert(idx < m_argv.size() - 1); + m_argv[idx] = pos->c_str(); + if (idx >= m_args_quote_char.size()) + m_args_quote_char.resize(idx + 1); + m_args_quote_char[idx] = quote_char; + return GetArgumentAtIndex(idx); + } + return NULL; +} + +void +Args::DeleteArgumentAtIndex (size_t idx) +{ + // Since we are using a std::list to hold onto the copied C string and + // we don't have direct access to the elements, we have to iterate to + // find the value. + arg_sstr_collection::iterator pos, end = m_args.end(); + size_t i = idx; + for (pos = m_args.begin(); i > 0 && pos != end; ++pos) + --i; + + if (pos != end) + { + m_args.erase (pos); + assert(idx < m_argv.size() - 1); + m_argv.erase(m_argv.begin() + idx); + if (idx < m_args_quote_char.size()) + m_args_quote_char.erase(m_args_quote_char.begin() + idx); + } +} + +void +Args::SetArguments (size_t argc, const char **argv) +{ + // m_argv will be rebuilt in UpdateArgvFromArgs() below, so there is + // no need to clear it here. + m_args.clear(); + m_args_quote_char.clear(); + + // First copy each string + for (size_t i=0; iOptionSeen (val); + + // Lookup the long option index + if (long_options_index == -1) + { + for (int i=0; + long_options[i].name || long_options[i].has_arg || long_options[i].flag || long_options[i].val; + ++i) + { + if (long_options[i].val == val) + { + long_options_index = i; + break; + } + } + } + // Call the callback with the option + if (long_options_index >= 0) + { + error = options.SetOptionValue(long_options_index, + long_options[long_options_index].has_arg == no_argument ? NULL : optarg); + } + else + { + error.SetErrorStringWithFormat("invalid option with value '%i'", val); + } + if (error.Fail()) + break; + } + + // Update our ARGV now that get options has consumed all the options + m_argv.erase(m_argv.begin(), m_argv.begin() + optind); + UpdateArgsAfterOptionParsing (); + return error; +} + +void +Args::Clear () +{ + m_args.clear (); + m_argv.clear (); + m_args_quote_char.clear(); +} + +int32_t +Args::StringToSInt32 (const char *s, int32_t fail_value, int base, bool *success_ptr) +{ + if (s && s[0]) + { + char *end = NULL; + const long sval = ::strtol (s, &end, base); + if (*end == '\0') + { + if (success_ptr) + *success_ptr = ((sval <= INT32_MAX) && (sval >= INT32_MIN)); + return (int32_t)sval; // All characters were used, return the result + } + } + if (success_ptr) *success_ptr = false; + return fail_value; +} + +uint32_t +Args::StringToUInt32 (const char *s, uint32_t fail_value, int base, bool *success_ptr) +{ + if (s && s[0]) + { + char *end = NULL; + const unsigned long uval = ::strtoul (s, &end, base); + if (*end == '\0') + { + if (success_ptr) + *success_ptr = (uval <= UINT32_MAX); + return (uint32_t)uval; // All characters were used, return the result + } + } + if (success_ptr) *success_ptr = false; + return fail_value; +} + + +int64_t +Args::StringToSInt64 (const char *s, int64_t fail_value, int base, bool *success_ptr) +{ + if (s && s[0]) + { + char *end = NULL; + int64_t uval = ::strtoll (s, &end, base); + if (*end == '\0') + { + if (success_ptr) *success_ptr = true; + return uval; // All characters were used, return the result + } + } + if (success_ptr) *success_ptr = false; + return fail_value; +} + +uint64_t +Args::StringToUInt64 (const char *s, uint64_t fail_value, int base, bool *success_ptr) +{ + if (s && s[0]) + { + char *end = NULL; + uint64_t uval = ::strtoull (s, &end, base); + if (*end == '\0') + { + if (success_ptr) *success_ptr = true; + return uval; // All characters were used, return the result + } + } + if (success_ptr) *success_ptr = false; + return fail_value; +} + +lldb::addr_t +Args::StringToAddress (const ExecutionContext *exe_ctx, const char *s, lldb::addr_t fail_value, Error *error_ptr) +{ + bool error_set = false; + if (s && s[0]) + { + char *end = NULL; + lldb::addr_t addr = ::strtoull (s, &end, 0); + if (*end == '\0') + { + if (error_ptr) + error_ptr->Clear(); + return addr; // All characters were used, return the result + } + // Try base 16 with no prefix... + addr = ::strtoull (s, &end, 16); + if (*end == '\0') + { + if (error_ptr) + error_ptr->Clear(); + return addr; // All characters were used, return the result + } + + if (exe_ctx) + { + Target *target = exe_ctx->GetTargetPtr(); + if (target) + { + lldb::ValueObjectSP valobj_sp; + EvaluateExpressionOptions options; + options.SetCoerceToId(false); + options.SetUnwindOnError(true); + options.SetKeepInMemory(false); + options.SetRunOthers(true); + + ExecutionResults expr_result = target->EvaluateExpression(s, + exe_ctx->GetFramePtr(), + valobj_sp, + options); + + bool success = false; + if (expr_result == eExecutionCompleted) + { + // Get the address to watch. + addr = valobj_sp->GetValueAsUnsigned(fail_value, &success); + if (success) + { + if (error_ptr) + error_ptr->Clear(); + return addr; + } + else + { + if (error_ptr) + { + error_set = true; + error_ptr->SetErrorStringWithFormat("address expression \"%s\" resulted in a value whose type can't be converted to an address: %s", s, valobj_sp->GetTypeName().GetCString()); + } + } + + } + else + { + // Since the compiler can't handle things like "main + 12" we should + // try to do this for now. The compliler doesn't like adding offsets + // to function pointer types. + static RegularExpression g_symbol_plus_offset_regex("^(.*)([-\\+])[[:space:]]*(0x[0-9A-Fa-f]+|[0-9]+)[[:space:]]*$"); + RegularExpression::Match regex_match(3); + if (g_symbol_plus_offset_regex.Execute(s, ®ex_match)) + { + uint64_t offset = 0; + bool add = true; + std::string name; + std::string str; + if (regex_match.GetMatchAtIndex(s, 1, name)) + { + if (regex_match.GetMatchAtIndex(s, 2, str)) + { + add = str[0] == '+'; + + if (regex_match.GetMatchAtIndex(s, 3, str)) + { + offset = Args::StringToUInt64(str.c_str(), 0, 0, &success); + + if (success) + { + Error error; + addr = StringToAddress (exe_ctx, name.c_str(), LLDB_INVALID_ADDRESS, &error); + if (addr != LLDB_INVALID_ADDRESS) + { + if (add) + return addr + offset; + else + return addr - offset; + } + } + } + } + } + } + + if (error_ptr) + { + error_set = true; + error_ptr->SetErrorStringWithFormat("address expression \"%s\" evaluation failed", s); + } + } + } + } + } + if (error_ptr) + { + if (!error_set) + error_ptr->SetErrorStringWithFormat("invalid address expression \"%s\"", s); + } + return fail_value; +} + +const char * +Args::StripSpaces (std::string &s, bool leading, bool trailing, bool return_null_if_empty) +{ + static const char *k_white_space = " \t\v"; + if (!s.empty()) + { + if (leading) + { + size_t pos = s.find_first_not_of (k_white_space); + if (pos == std::string::npos) + s.clear(); + else if (pos > 0) + s.erase(0, pos); + } + + if (trailing) + { + size_t rpos = s.find_last_not_of(k_white_space); + if (rpos != std::string::npos && rpos + 1 < s.size()) + s.erase(rpos + 1); + } + } + if (return_null_if_empty && s.empty()) + return NULL; + return s.c_str(); +} + +bool +Args::StringToBoolean (const char *s, bool fail_value, bool *success_ptr) +{ + if (s && s[0]) + { + if (::strcasecmp (s, "false") == 0 || + ::strcasecmp (s, "off") == 0 || + ::strcasecmp (s, "no") == 0 || + ::strcmp (s, "0") == 0) + { + if (success_ptr) + *success_ptr = true; + return false; + } + else + if (::strcasecmp (s, "true") == 0 || + ::strcasecmp (s, "on") == 0 || + ::strcasecmp (s, "yes") == 0 || + ::strcmp (s, "1") == 0) + { + if (success_ptr) *success_ptr = true; + return true; + } + } + if (success_ptr) *success_ptr = false; + return fail_value; +} + +const char * +Args::StringToVersion (const char *s, uint32_t &major, uint32_t &minor, uint32_t &update) +{ + major = UINT32_MAX; + minor = UINT32_MAX; + update = UINT32_MAX; + + if (s && s[0]) + { + char *pos = NULL; + unsigned long uval32 = ::strtoul (s, &pos, 0); + if (pos == s) + return s; + major = uval32; + if (*pos == '\0') + { + return pos; // Decoded major and got end of string + } + else if (*pos == '.') + { + const char *minor_cstr = pos + 1; + uval32 = ::strtoul (minor_cstr, &pos, 0); + if (pos == minor_cstr) + return pos; // Didn't get any digits for the minor version... + minor = uval32; + if (*pos == '.') + { + const char *update_cstr = pos + 1; + uval32 = ::strtoul (update_cstr, &pos, 0); + if (pos == update_cstr) + return pos; + update = uval32; + } + return pos; + } + } + return 0; +} + +const char * +Args::GetShellSafeArgument (const char *unsafe_arg, std::string &safe_arg) +{ + safe_arg.assign (unsafe_arg); + size_t prev_pos = 0; + while (prev_pos < safe_arg.size()) + { + // Escape spaces and quotes + size_t pos = safe_arg.find_first_of(" '\"", prev_pos); + if (pos != std::string::npos) + { + safe_arg.insert (pos, 1, '\\'); + prev_pos = pos + 2; + } + else + break; + } + return safe_arg.c_str(); +} + + +int64_t +Args::StringToOptionEnum (const char *s, OptionEnumValueElement *enum_values, int32_t fail_value, Error &error) +{ + if (enum_values) + { + if (s && s[0]) + { + for (int i = 0; enum_values[i].string_value != NULL ; i++) + { + if (strstr(enum_values[i].string_value, s) == enum_values[i].string_value) + { + error.Clear(); + return enum_values[i].value; + } + } + } + + StreamString strm; + strm.PutCString ("invalid enumeration value, valid values are: "); + for (int i = 0; enum_values[i].string_value != NULL; i++) + { + strm.Printf ("%s\"%s\"", + i > 0 ? ", " : "", + enum_values[i].string_value); + } + error.SetErrorString(strm.GetData()); + } + else + { + error.SetErrorString ("invalid enumeration argument"); + } + return fail_value; +} + +ScriptLanguage +Args::StringToScriptLanguage (const char *s, ScriptLanguage fail_value, bool *success_ptr) +{ + if (s && s[0]) + { + if ((::strcasecmp (s, "python") == 0) || + (::strcasecmp (s, "default") == 0 && eScriptLanguagePython == eScriptLanguageDefault)) + { + if (success_ptr) *success_ptr = true; + return eScriptLanguagePython; + } + if (::strcasecmp (s, "none")) + { + if (success_ptr) *success_ptr = true; + return eScriptLanguageNone; + } + } + if (success_ptr) *success_ptr = false; + return fail_value; +} + +Error +Args::StringToFormat +( + const char *s, + lldb::Format &format, + size_t *byte_size_ptr +) +{ + format = eFormatInvalid; + Error error; + + if (s && s[0]) + { + if (byte_size_ptr) + { + if (isdigit (s[0])) + { + char *format_char = NULL; + unsigned long byte_size = ::strtoul (s, &format_char, 0); + if (byte_size != ULONG_MAX) + *byte_size_ptr = byte_size; + s = format_char; + } + else + *byte_size_ptr = 0; + } + + const bool partial_match_ok = true; + if (!FormatManager::GetFormatFromCString (s, partial_match_ok, format)) + { + StreamString error_strm; + error_strm.Printf ("Invalid format character or name '%s'. Valid values are:\n", s); + for (Format f = eFormatDefault; f < kNumFormats; f = Format(f+1)) + { + char format_char = FormatManager::GetFormatAsFormatChar(f); + if (format_char) + error_strm.Printf ("'%c' or ", format_char); + + error_strm.Printf ("\"%s\"", FormatManager::GetFormatAsCString(f)); + error_strm.EOL(); + } + + if (byte_size_ptr) + error_strm.PutCString ("An optional byte size can precede the format character.\n"); + error.SetErrorString(error_strm.GetString().c_str()); + } + + if (error.Fail()) + return error; + } + else + { + error.SetErrorStringWithFormat("%s option string", s ? "empty" : "invalid"); + } + return error; +} + +lldb::Encoding +Args::StringToEncoding (const char *s, lldb::Encoding fail_value) +{ + if (s && s[0]) + { + if (strcmp(s, "uint") == 0) + return eEncodingUint; + else if (strcmp(s, "sint") == 0) + return eEncodingSint; + else if (strcmp(s, "ieee754") == 0) + return eEncodingIEEE754; + else if (strcmp(s, "vector") == 0) + return eEncodingVector; + } + return fail_value; +} + +uint32_t +Args::StringToGenericRegister (const char *s) +{ + if (s && s[0]) + { + if (strcmp(s, "pc") == 0) + return LLDB_REGNUM_GENERIC_PC; + else if (strcmp(s, "sp") == 0) + return LLDB_REGNUM_GENERIC_SP; + else if (strcmp(s, "fp") == 0) + return LLDB_REGNUM_GENERIC_FP; + else if (strcmp(s, "ra") == 0) + return LLDB_REGNUM_GENERIC_RA; + else if (strcmp(s, "flags") == 0) + return LLDB_REGNUM_GENERIC_FLAGS; + else if (strncmp(s, "arg", 3) == 0) + { + if (s[3] && s[4] == '\0') + { + switch (s[3]) + { + case '1': return LLDB_REGNUM_GENERIC_ARG1; + case '2': return LLDB_REGNUM_GENERIC_ARG2; + case '3': return LLDB_REGNUM_GENERIC_ARG3; + case '4': return LLDB_REGNUM_GENERIC_ARG4; + case '5': return LLDB_REGNUM_GENERIC_ARG5; + case '6': return LLDB_REGNUM_GENERIC_ARG6; + case '7': return LLDB_REGNUM_GENERIC_ARG7; + case '8': return LLDB_REGNUM_GENERIC_ARG8; + } + } + } + } + return LLDB_INVALID_REGNUM; +} + + +void +Args::LongestCommonPrefix (std::string &common_prefix) +{ + arg_sstr_collection::iterator pos, end = m_args.end(); + pos = m_args.begin(); + if (pos == end) + common_prefix.clear(); + else + common_prefix = (*pos); + + for (++pos; pos != end; ++pos) + { + size_t new_size = (*pos).size(); + + // First trim common_prefix if it is longer than the current element: + if (common_prefix.size() > new_size) + common_prefix.erase (new_size); + + // Then trim it at the first disparity: + + for (size_t i = 0; i < common_prefix.size(); i++) + { + if ((*pos)[i] != common_prefix[i]) + { + common_prefix.erase(i); + break; + } + } + + // If we've emptied the common prefix, we're done. + if (common_prefix.empty()) + break; + } +} + +size_t +Args::FindArgumentIndexForOption (struct option *long_options, int long_options_index) +{ + char short_buffer[3]; + char long_buffer[255]; + ::snprintf (short_buffer, sizeof (short_buffer), "-%c", long_options[long_options_index].val); + ::snprintf (long_buffer, sizeof (long_buffer), "--%s", long_options[long_options_index].name); + size_t end = GetArgumentCount (); + size_t idx = 0; + while (idx < end) + { + if ((::strncmp (GetArgumentAtIndex (idx), short_buffer, strlen (short_buffer)) == 0) + || (::strncmp (GetArgumentAtIndex (idx), long_buffer, strlen (long_buffer)) == 0)) + { + return idx; + } + ++idx; + } + + return end; +} + +bool +Args::IsPositionalArgument (const char *arg) +{ + if (arg == NULL) + return false; + + bool is_positional = true; + char *cptr = (char *) arg; + + if (cptr[0] == '%') + { + ++cptr; + while (isdigit (cptr[0])) + ++cptr; + if (cptr[0] != '\0') + is_positional = false; + } + else + is_positional = false; + + return is_positional; +} + +void +Args::ParseAliasOptions (Options &options, + CommandReturnObject &result, + OptionArgVector *option_arg_vector, + std::string &raw_input_string) +{ + StreamString sstr; + int i; + struct option *long_options = options.GetLongOptions(); + + if (long_options == NULL) + { + result.AppendError ("invalid long options"); + result.SetStatus (eReturnStatusFailed); + return; + } + + for (i = 0; long_options[i].name != NULL; ++i) + { + if (long_options[i].flag == NULL) + { + sstr << (char) long_options[i].val; + switch (long_options[i].has_arg) + { + default: + case no_argument: + break; + case required_argument: + sstr << ":"; + break; + case optional_argument: + sstr << "::"; + break; + } + } + } + +#ifdef __GLIBC__ + optind = 0; +#else + optreset = 1; + optind = 1; +#endif + int val; + while (1) + { + int long_options_index = -1; + val = ::getopt_long_only (GetArgumentCount(), + GetArgumentVector(), + sstr.GetData(), + long_options, + &long_options_index); + + if (val == -1) + break; + + if (val == '?') + { + result.AppendError ("unknown or ambiguous option"); + result.SetStatus (eReturnStatusFailed); + break; + } + + if (val == 0) + continue; + + ((Options *) &options)->OptionSeen (val); + + // Look up the long option index + if (long_options_index == -1) + { + for (int j = 0; + long_options[j].name || long_options[j].has_arg || long_options[j].flag || long_options[j].val; + ++j) + { + if (long_options[j].val == val) + { + long_options_index = j; + break; + } + } + } + + // See if the option takes an argument, and see if one was supplied. + if (long_options_index >= 0) + { + StreamString option_str; + option_str.Printf ("-%c", val); + + switch (long_options[long_options_index].has_arg) + { + case no_argument: + option_arg_vector->push_back (OptionArgPair (std::string (option_str.GetData()), + OptionArgValue (no_argument, ""))); + result.SetStatus (eReturnStatusSuccessFinishNoResult); + break; + case required_argument: + if (optarg != NULL) + { + option_arg_vector->push_back (OptionArgPair (std::string (option_str.GetData()), + OptionArgValue (required_argument, + std::string (optarg)))); + result.SetStatus (eReturnStatusSuccessFinishNoResult); + } + else + { + result.AppendErrorWithFormat ("Option '%s' is missing argument specifier.\n", + option_str.GetData()); + result.SetStatus (eReturnStatusFailed); + } + break; + case optional_argument: + if (optarg != NULL) + { + option_arg_vector->push_back (OptionArgPair (std::string (option_str.GetData()), + OptionArgValue (optional_argument, + std::string (optarg)))); + result.SetStatus (eReturnStatusSuccessFinishNoResult); + } + else + { + option_arg_vector->push_back (OptionArgPair (std::string (option_str.GetData()), + OptionArgValue (optional_argument, ""))); + result.SetStatus (eReturnStatusSuccessFinishNoResult); + } + break; + default: + result.AppendErrorWithFormat ("error with options table; invalid value in has_arg field for option '%c'.\n", val); + result.SetStatus (eReturnStatusFailed); + break; + } + } + else + { + result.AppendErrorWithFormat ("Invalid option with value '%c'.\n", val); + result.SetStatus (eReturnStatusFailed); + } + + if (long_options_index >= 0) + { + // Find option in the argument list; also see if it was supposed to take an argument and if one was + // supplied. Remove option (and argument, if given) from the argument list. Also remove them from + // the raw_input_string, if one was passed in. + size_t idx = FindArgumentIndexForOption (long_options, long_options_index); + if (idx < GetArgumentCount()) + { + if (raw_input_string.size() > 0) + { + const char *tmp_arg = GetArgumentAtIndex (idx); + size_t pos = raw_input_string.find (tmp_arg); + if (pos != std::string::npos) + raw_input_string.erase (pos, strlen (tmp_arg)); + } + ReplaceArgumentAtIndex (idx, ""); + if ((long_options[long_options_index].has_arg != no_argument) + && (optarg != NULL) + && (idx+1 < GetArgumentCount()) + && (strcmp (optarg, GetArgumentAtIndex(idx+1)) == 0)) + { + if (raw_input_string.size() > 0) + { + const char *tmp_arg = GetArgumentAtIndex (idx+1); + size_t pos = raw_input_string.find (tmp_arg); + if (pos != std::string::npos) + raw_input_string.erase (pos, strlen (tmp_arg)); + } + ReplaceArgumentAtIndex (idx+1, ""); + } + } + } + + if (!result.Succeeded()) + break; + } +} + +void +Args::ParseArgsForCompletion +( + Options &options, + OptionElementVector &option_element_vector, + uint32_t cursor_index +) +{ + StreamString sstr; + struct option *long_options = options.GetLongOptions(); + option_element_vector.clear(); + + if (long_options == NULL) + { + return; + } + + // Leading : tells getopt to return a : for a missing option argument AND + // to suppress error messages. + + sstr << ":"; + for (int i = 0; long_options[i].name != NULL; ++i) + { + if (long_options[i].flag == NULL) + { + sstr << (char) long_options[i].val; + switch (long_options[i].has_arg) + { + default: + case no_argument: + break; + case required_argument: + sstr << ":"; + break; + case optional_argument: + sstr << "::"; + break; + } + } + } + +#ifdef __GLIBC__ + optind = 0; +#else + optreset = 1; + optind = 1; +#endif + opterr = 0; + + int val; + const OptionDefinition *opt_defs = options.GetDefinitions(); + + // Fooey... getopt_long_only permutes the GetArgumentVector to move the options to the front. + // So we have to build another Arg and pass that to getopt_long_only so it doesn't + // change the one we have. + + std::vector dummy_vec (GetArgumentVector(), GetArgumentVector() + GetArgumentCount() + 1); + + bool failed_once = false; + uint32_t dash_dash_pos = -1; + + while (1) + { + bool missing_argument = false; + int long_options_index = -1; + + val = ::getopt_long_only (dummy_vec.size() - 1, + (char *const *) &dummy_vec.front(), + sstr.GetData(), + long_options, + &long_options_index); + + if (val == -1) + { + // When we're completing a "--" which is the last option on line, + if (failed_once) + break; + + failed_once = true; + + // If this is a bare "--" we mark it as such so we can complete it successfully later. + // Handling the "--" is a little tricky, since that may mean end of options or arguments, or the + // user might want to complete options by long name. I make this work by checking whether the + // cursor is in the "--" argument, and if so I assume we're completing the long option, otherwise + // I let it pass to getopt_long_only which will terminate the option parsing. + // Note, in either case we continue parsing the line so we can figure out what other options + // were passed. This will be useful when we come to restricting completions based on what other + // options we've seen on the line. + + if (optind < dummy_vec.size() - 1 + && (strcmp (dummy_vec[optind-1], "--") == 0)) + { + dash_dash_pos = optind - 1; + if (optind - 1 == cursor_index) + { + option_element_vector.push_back (OptionArgElement (OptionArgElement::eBareDoubleDash, optind - 1, + OptionArgElement::eBareDoubleDash)); + continue; + } + else + break; + } + else + break; + } + else if (val == '?') + { + option_element_vector.push_back (OptionArgElement (OptionArgElement::eUnrecognizedArg, optind - 1, + OptionArgElement::eUnrecognizedArg)); + continue; + } + else if (val == 0) + { + continue; + } + else if (val == ':') + { + // This is a missing argument. + val = optopt; + missing_argument = true; + } + + ((Options *) &options)->OptionSeen (val); + + // Look up the long option index + if (long_options_index == -1) + { + for (int j = 0; + long_options[j].name || long_options[j].has_arg || long_options[j].flag || long_options[j].val; + ++j) + { + if (long_options[j].val == val) + { + long_options_index = j; + break; + } + } + } + + // See if the option takes an argument, and see if one was supplied. + if (long_options_index >= 0) + { + int opt_defs_index = -1; + for (int i = 0; ; i++) + { + if (opt_defs[i].short_option == 0) + break; + else if (opt_defs[i].short_option == val) + { + opt_defs_index = i; + break; + } + } + + switch (long_options[long_options_index].has_arg) + { + case no_argument: + option_element_vector.push_back (OptionArgElement (opt_defs_index, optind - 1, 0)); + break; + case required_argument: + if (optarg != NULL) + { + int arg_index; + if (missing_argument) + arg_index = -1; + else + arg_index = optind - 1; + + option_element_vector.push_back (OptionArgElement (opt_defs_index, optind - 2, arg_index)); + } + else + { + option_element_vector.push_back (OptionArgElement (opt_defs_index, optind - 1, -1)); + } + break; + case optional_argument: + if (optarg != NULL) + { + option_element_vector.push_back (OptionArgElement (opt_defs_index, optind - 2, optind - 1)); + } + else + { + option_element_vector.push_back (OptionArgElement (opt_defs_index, optind - 2, optind - 1)); + } + break; + default: + // The options table is messed up. Here we'll just continue + option_element_vector.push_back (OptionArgElement (OptionArgElement::eUnrecognizedArg, optind - 1, + OptionArgElement::eUnrecognizedArg)); + break; + } + } + else + { + option_element_vector.push_back (OptionArgElement (OptionArgElement::eUnrecognizedArg, optind - 1, + OptionArgElement::eUnrecognizedArg)); + } + } + + // Finally we have to handle the case where the cursor index points at a single "-". We want to mark that in + // the option_element_vector, but only if it is not after the "--". But it turns out that getopt_long_only just ignores + // an isolated "-". So we have to look it up by hand here. We only care if it is AT the cursor position. + + if ((dash_dash_pos == -1 || cursor_index < dash_dash_pos) + && strcmp (GetArgumentAtIndex(cursor_index), "-") == 0) + { + option_element_vector.push_back (OptionArgElement (OptionArgElement::eBareDash, cursor_index, + OptionArgElement::eBareDash)); + + } +} + +void +Args::EncodeEscapeSequences (const char *src, std::string &dst) +{ + dst.clear(); + if (src) + { + for (const char *p = src; *p != '\0'; ++p) + { + size_t non_special_chars = ::strcspn (p, "\\"); + if (non_special_chars > 0) + { + dst.append(p, non_special_chars); + p += non_special_chars; + if (*p == '\0') + break; + } + + if (*p == '\\') + { + ++p; // skip the slash + switch (*p) + { + case 'a' : dst.append(1, '\a'); break; + case 'b' : dst.append(1, '\b'); break; + case 'f' : dst.append(1, '\f'); break; + case 'n' : dst.append(1, '\n'); break; + case 'r' : dst.append(1, '\r'); break; + case 't' : dst.append(1, '\t'); break; + case 'v' : dst.append(1, '\v'); break; + case '\\': dst.append(1, '\\'); break; + case '\'': dst.append(1, '\''); break; + case '"' : dst.append(1, '"'); break; + case '0' : + // 1 to 3 octal chars + { + // Make a string that can hold onto the initial zero char, + // up to 3 octal digits, and a terminating NULL. + char oct_str[5] = { '\0', '\0', '\0', '\0', '\0' }; + + int i; + for (i=0; (p[i] >= '0' && p[i] <= '7') && i<4; ++i) + oct_str[i] = p[i]; + + // We don't want to consume the last octal character since + // the main for loop will do this for us, so we advance p by + // one less than i (even if i is zero) + p += i - 1; + unsigned long octal_value = ::strtoul (oct_str, NULL, 8); + if (octal_value <= UINT8_MAX) + { + dst.append(1, (char)octal_value); + } + } + break; + + case 'x': + // hex number in the format + if (isxdigit(p[1])) + { + ++p; // Skip the 'x' + + // Make a string that can hold onto two hex chars plus a + // NULL terminator + char hex_str[3] = { *p, '\0', '\0' }; + if (isxdigit(p[1])) + { + ++p; // Skip the first of the two hex chars + hex_str[1] = *p; + } + + unsigned long hex_value = strtoul (hex_str, NULL, 16); + if (hex_value <= UINT8_MAX) + dst.append (1, (char)hex_value); + } + else + { + dst.append(1, 'x'); + } + break; + + default: + // Just desensitize any other character by just printing what + // came after the '\' + dst.append(1, *p); + break; + + } + } + } + } +} + + +void +Args::ExpandEscapedCharacters (const char *src, std::string &dst) +{ + dst.clear(); + if (src) + { + for (const char *p = src; *p != '\0'; ++p) + { + if (isprint8(*p)) + dst.append(1, *p); + else + { + switch (*p) + { + case '\a': dst.append("\\a"); break; + case '\b': dst.append("\\b"); break; + case '\f': dst.append("\\f"); break; + case '\n': dst.append("\\n"); break; + case '\r': dst.append("\\r"); break; + case '\t': dst.append("\\t"); break; + case '\v': dst.append("\\v"); break; + case '\'': dst.append("\\'"); break; + case '"': dst.append("\\\""); break; + case '\\': dst.append("\\\\"); break; + default: + { + // Just encode as octal + dst.append("\\0"); + char octal_str[32]; + snprintf(octal_str, sizeof(octal_str), "%o", *p); + dst.append(octal_str); + } + break; + } + } + } + } +} + diff --git a/contrib/llvm/tools/lldb/source/Interpreter/CommandHistory.cpp b/contrib/llvm/tools/lldb/source/Interpreter/CommandHistory.cpp new file mode 100644 index 00000000000..33971e3959c --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Interpreter/CommandHistory.cpp @@ -0,0 +1,143 @@ +//===-- CommandHistory.cpp --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Interpreter/CommandHistory.h" +#include "lldb/Interpreter/Args.h" + +using namespace lldb; +using namespace lldb_private; + + +CommandHistory::CommandHistory () : + m_mutex(Mutex::eMutexTypeRecursive), + m_history() +{} + +CommandHistory::~CommandHistory () +{} + +size_t +CommandHistory::GetSize () const +{ + Mutex::Locker locker(m_mutex); + return m_history.size(); +} + +bool +CommandHistory::IsEmpty () const +{ + Mutex::Locker locker(m_mutex); + return m_history.empty(); +} + +const char* +CommandHistory::FindString (const char* input_str) const +{ + Mutex::Locker locker(m_mutex); + if (!input_str) + return NULL; + if (input_str[0] != g_repeat_char) + return NULL; + if (input_str[1] == '-') + { + bool success; + size_t idx = Args::StringToUInt32 (input_str+2, 0, 0, &success); + if (!success) + return NULL; + if (idx > m_history.size()) + return NULL; + idx = m_history.size() - idx; + return m_history[idx].c_str(); + + } + else if (input_str[1] == g_repeat_char) + { + if (m_history.empty()) + return NULL; + else + return m_history.back().c_str(); + } + else + { + bool success; + uint32_t idx = Args::StringToUInt32 (input_str+1, 0, 0, &success); + if (!success) + return NULL; + if (idx >= m_history.size()) + return NULL; + return m_history[idx].c_str(); + } +} + +const char* +CommandHistory::GetStringAtIndex (size_t idx) const +{ + Mutex::Locker locker(m_mutex); + if (idx < m_history.size()) + return m_history[idx].c_str(); + return NULL; +} + +const char* +CommandHistory::operator [] (size_t idx) const +{ + return GetStringAtIndex(idx); +} + +const char* +CommandHistory::GetRecentmostString () const +{ + Mutex::Locker locker(m_mutex); + if (m_history.empty()) + return NULL; + return m_history.back().c_str(); +} + +void +CommandHistory::AppendString (const std::string& str, + bool reject_if_dupe) +{ + Mutex::Locker locker(m_mutex); + if (reject_if_dupe) + { + if (!m_history.empty()) + { + if (str == m_history.back()) + return; + } + } + m_history.push_back(std::string(str)); +} + +void +CommandHistory::Clear () +{ + Mutex::Locker locker(m_mutex); + m_history.clear(); +} + +void +CommandHistory::Dump (Stream& stream, + size_t start_idx, + size_t stop_idx) const +{ + Mutex::Locker locker(m_mutex); + stop_idx = std::min(stop_idx, m_history.size() - 1); + for (size_t counter = start_idx; + counter <= stop_idx; + counter++) + { + const std::string hist_item = m_history[counter]; + if (!hist_item.empty()) + { + stream.Indent(); + stream.Printf ("%4zu: %s\n", counter, hist_item.c_str()); + } + } +} diff --git a/contrib/llvm/tools/lldb/source/Interpreter/CommandInterpreter.cpp b/contrib/llvm/tools/lldb/source/Interpreter/CommandInterpreter.cpp new file mode 100644 index 00000000000..db2f2fafbef --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Interpreter/CommandInterpreter.cpp @@ -0,0 +1,2882 @@ +//===-- CommandInterpreter.cpp ----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include +#include + +#include +#include + +#include "CommandObjectScript.h" +#include "lldb/Interpreter/CommandObjectRegexCommand.h" + +#include "../Commands/CommandObjectApropos.h" +#include "../Commands/CommandObjectArgs.h" +#include "../Commands/CommandObjectBreakpoint.h" +#include "../Commands/CommandObjectDisassemble.h" +#include "../Commands/CommandObjectExpression.h" +#include "../Commands/CommandObjectFrame.h" +#include "../Commands/CommandObjectHelp.h" +#include "../Commands/CommandObjectLog.h" +#include "../Commands/CommandObjectMemory.h" +#include "../Commands/CommandObjectPlatform.h" +#include "../Commands/CommandObjectPlugin.h" +#include "../Commands/CommandObjectProcess.h" +#include "../Commands/CommandObjectQuit.h" +#include "../Commands/CommandObjectRegister.h" +#include "../Commands/CommandObjectSettings.h" +#include "../Commands/CommandObjectSource.h" +#include "../Commands/CommandObjectCommands.h" +#include "../Commands/CommandObjectSyntax.h" +#include "../Commands/CommandObjectTarget.h" +#include "../Commands/CommandObjectThread.h" +#include "../Commands/CommandObjectType.h" +#include "../Commands/CommandObjectVersion.h" +#include "../Commands/CommandObjectWatchpoint.h" + + +#include "lldb/Core/Debugger.h" +#include "lldb/Core/InputReader.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Stream.h" +#include "lldb/Core/Timer.h" + +#include "lldb/Host/Host.h" + +#include "lldb/Interpreter/Args.h" +#include "lldb/Interpreter/CommandCompletions.h" +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Interpreter/CommandReturnObject.h" +#include "lldb/Interpreter/Options.h" +#include "lldb/Interpreter/ScriptInterpreterNone.h" +#include "lldb/Interpreter/ScriptInterpreterPython.h" + + +#include "lldb/Target/Process.h" +#include "lldb/Target/Thread.h" +#include "lldb/Target/TargetList.h" + +#include "lldb/Utility/CleanUp.h" + +using namespace lldb; +using namespace lldb_private; + + +static PropertyDefinition +g_properties[] = +{ + { "expand-regex-aliases", OptionValue::eTypeBoolean, true, false, NULL, NULL, "If true, regular expression alias commands will show the expanded command that will be executed. This can be used to debug new regular expression alias commands." }, + { "prompt-on-quit", OptionValue::eTypeBoolean, true, true, NULL, NULL, "If true, LLDB will prompt you before quitting if there are any live processes being debugged. If false, LLDB will quit without asking in any case." }, + { "stop-command-source-on-error", OptionValue::eTypeBoolean, true, true, NULL, NULL, "If true, LLDB will stop running a 'command source' script upon encountering an error." }, + { NULL , OptionValue::eTypeInvalid, true, 0 , NULL, NULL, NULL } +}; + +enum +{ + ePropertyExpandRegexAliases = 0, + ePropertyPromptOnQuit = 1, + ePropertyStopCmdSourceOnError = 2 +}; + +ConstString & +CommandInterpreter::GetStaticBroadcasterClass () +{ + static ConstString class_name ("lldb.commandInterpreter"); + return class_name; +} + +CommandInterpreter::CommandInterpreter +( + Debugger &debugger, + ScriptLanguage script_language, + bool synchronous_execution +) : + Broadcaster (&debugger, "lldb.command-interpreter"), + Properties(OptionValuePropertiesSP(new OptionValueProperties(ConstString("interpreter")))), + m_debugger (debugger), + m_synchronous_execution (synchronous_execution), + m_skip_lldbinit_files (false), + m_skip_app_init_files (false), + m_script_interpreter_ap (), + m_comment_char ('#'), + m_batch_command_mode (false), + m_truncation_warning(eNoTruncation), + m_command_source_depth (0) +{ + debugger.SetScriptLanguage (script_language); + SetEventName (eBroadcastBitThreadShouldExit, "thread-should-exit"); + SetEventName (eBroadcastBitResetPrompt, "reset-prompt"); + SetEventName (eBroadcastBitQuitCommandReceived, "quit"); + CheckInWithManager (); + m_collection_sp->Initialize (g_properties); +} + +bool +CommandInterpreter::GetExpandRegexAliases () const +{ + const uint32_t idx = ePropertyExpandRegexAliases; + return m_collection_sp->GetPropertyAtIndexAsBoolean (NULL, idx, g_properties[idx].default_uint_value != 0); +} + +bool +CommandInterpreter::GetPromptOnQuit () const +{ + const uint32_t idx = ePropertyPromptOnQuit; + return m_collection_sp->GetPropertyAtIndexAsBoolean (NULL, idx, g_properties[idx].default_uint_value != 0); +} + +bool +CommandInterpreter::GetStopCmdSourceOnError () const +{ + const uint32_t idx = ePropertyStopCmdSourceOnError; + return m_collection_sp->GetPropertyAtIndexAsBoolean (NULL, idx, g_properties[idx].default_uint_value != 0); +} + +void +CommandInterpreter::Initialize () +{ + Timer scoped_timer (__PRETTY_FUNCTION__, __PRETTY_FUNCTION__); + + CommandReturnObject result; + + LoadCommandDictionary (); + + // Set up some initial aliases. + CommandObjectSP cmd_obj_sp = GetCommandSPExact ("quit", false); + if (cmd_obj_sp) + { + AddAlias ("q", cmd_obj_sp); + AddAlias ("exit", cmd_obj_sp); + } + + cmd_obj_sp = GetCommandSPExact ("_regexp-attach",false); + if (cmd_obj_sp) + { + AddAlias ("attach", cmd_obj_sp); + } + + cmd_obj_sp = GetCommandSPExact ("process detach",false); + if (cmd_obj_sp) + { + AddAlias ("detach", cmd_obj_sp); + } + + cmd_obj_sp = GetCommandSPExact ("process continue", false); + if (cmd_obj_sp) + { + AddAlias ("c", cmd_obj_sp); + AddAlias ("continue", cmd_obj_sp); + } + + cmd_obj_sp = GetCommandSPExact ("_regexp-break",false); + if (cmd_obj_sp) + AddAlias ("b", cmd_obj_sp); + + cmd_obj_sp = GetCommandSPExact ("_regexp-tbreak",false); + if (cmd_obj_sp) + AddAlias ("tbreak", cmd_obj_sp); + + cmd_obj_sp = GetCommandSPExact ("thread step-inst", false); + if (cmd_obj_sp) + { + AddAlias ("stepi", cmd_obj_sp); + AddAlias ("si", cmd_obj_sp); + } + + cmd_obj_sp = GetCommandSPExact ("thread step-inst-over", false); + if (cmd_obj_sp) + { + AddAlias ("nexti", cmd_obj_sp); + AddAlias ("ni", cmd_obj_sp); + } + + cmd_obj_sp = GetCommandSPExact ("thread step-in", false); + if (cmd_obj_sp) + { + AddAlias ("s", cmd_obj_sp); + AddAlias ("step", cmd_obj_sp); + } + + cmd_obj_sp = GetCommandSPExact ("thread step-over", false); + if (cmd_obj_sp) + { + AddAlias ("n", cmd_obj_sp); + AddAlias ("next", cmd_obj_sp); + } + + cmd_obj_sp = GetCommandSPExact ("thread step-out", false); + if (cmd_obj_sp) + { + AddAlias ("finish", cmd_obj_sp); + } + + cmd_obj_sp = GetCommandSPExact ("frame select", false); + if (cmd_obj_sp) + { + AddAlias ("f", cmd_obj_sp); + } + + cmd_obj_sp = GetCommandSPExact ("thread select", false); + if (cmd_obj_sp) + { + AddAlias ("t", cmd_obj_sp); + } + + cmd_obj_sp = GetCommandSPExact ("_regexp-list", false); + if (cmd_obj_sp) + { + AddAlias ("l", cmd_obj_sp); + AddAlias ("list", cmd_obj_sp); + } + + cmd_obj_sp = GetCommandSPExact ("_regexp-env", false); + if (cmd_obj_sp) + { + AddAlias ("env", cmd_obj_sp); + } + + cmd_obj_sp = GetCommandSPExact ("memory read", false); + if (cmd_obj_sp) + AddAlias ("x", cmd_obj_sp); + + cmd_obj_sp = GetCommandSPExact ("_regexp-up", false); + if (cmd_obj_sp) + AddAlias ("up", cmd_obj_sp); + + cmd_obj_sp = GetCommandSPExact ("_regexp-down", false); + if (cmd_obj_sp) + AddAlias ("down", cmd_obj_sp); + + cmd_obj_sp = GetCommandSPExact ("_regexp-display", false); + if (cmd_obj_sp) + AddAlias ("display", cmd_obj_sp); + + cmd_obj_sp = GetCommandSPExact ("disassemble", false); + if (cmd_obj_sp) + AddAlias ("dis", cmd_obj_sp); + + cmd_obj_sp = GetCommandSPExact ("disassemble", false); + if (cmd_obj_sp) + AddAlias ("di", cmd_obj_sp); + + + + cmd_obj_sp = GetCommandSPExact ("_regexp-undisplay", false); + if (cmd_obj_sp) + AddAlias ("undisplay", cmd_obj_sp); + + cmd_obj_sp = GetCommandSPExact ("_regexp-bt", false); + if (cmd_obj_sp) + AddAlias ("bt", cmd_obj_sp); + + cmd_obj_sp = GetCommandSPExact ("target create", false); + if (cmd_obj_sp) + AddAlias ("file", cmd_obj_sp); + + cmd_obj_sp = GetCommandSPExact ("target modules", false); + if (cmd_obj_sp) + AddAlias ("image", cmd_obj_sp); + + + OptionArgVectorSP alias_arguments_vector_sp (new OptionArgVector); + + cmd_obj_sp = GetCommandSPExact ("expression", false); + if (cmd_obj_sp) + { + ProcessAliasOptionsArgs (cmd_obj_sp, "--", alias_arguments_vector_sp); + AddAlias ("p", cmd_obj_sp); + AddAlias ("print", cmd_obj_sp); + AddAlias ("call", cmd_obj_sp); + AddOrReplaceAliasOptions ("p", alias_arguments_vector_sp); + AddOrReplaceAliasOptions ("print", alias_arguments_vector_sp); + AddOrReplaceAliasOptions ("call", alias_arguments_vector_sp); + + alias_arguments_vector_sp.reset (new OptionArgVector); + ProcessAliasOptionsArgs (cmd_obj_sp, "-O -- ", alias_arguments_vector_sp); + AddAlias ("po", cmd_obj_sp); + AddOrReplaceAliasOptions ("po", alias_arguments_vector_sp); + } + + cmd_obj_sp = GetCommandSPExact ("process kill", false); + if (cmd_obj_sp) + { + AddAlias ("kill", cmd_obj_sp); + } + + cmd_obj_sp = GetCommandSPExact ("process launch", false); + if (cmd_obj_sp) + { + alias_arguments_vector_sp.reset (new OptionArgVector); +#if defined (__arm__) + ProcessAliasOptionsArgs (cmd_obj_sp, "--", alias_arguments_vector_sp); +#else + ProcessAliasOptionsArgs (cmd_obj_sp, "--shell=/bin/bash --", alias_arguments_vector_sp); +#endif + AddAlias ("r", cmd_obj_sp); + AddAlias ("run", cmd_obj_sp); + AddOrReplaceAliasOptions ("r", alias_arguments_vector_sp); + AddOrReplaceAliasOptions ("run", alias_arguments_vector_sp); + } + + cmd_obj_sp = GetCommandSPExact ("target symbols add", false); + if (cmd_obj_sp) + { + AddAlias ("add-dsym", cmd_obj_sp); + } + + cmd_obj_sp = GetCommandSPExact ("breakpoint set", false); + if (cmd_obj_sp) + { + alias_arguments_vector_sp.reset (new OptionArgVector); + ProcessAliasOptionsArgs (cmd_obj_sp, "--func-regex %1", alias_arguments_vector_sp); + AddAlias ("rbreak", cmd_obj_sp); + AddOrReplaceAliasOptions("rbreak", alias_arguments_vector_sp); + } +} + +const char * +CommandInterpreter::ProcessEmbeddedScriptCommands (const char *arg) +{ + // This function has not yet been implemented. + + // Look for any embedded script command + // If found, + // get interpreter object from the command dictionary, + // call execute_one_command on it, + // get the results as a string, + // substitute that string for current stuff. + + return arg; +} + + +void +CommandInterpreter::LoadCommandDictionary () +{ + Timer scoped_timer (__PRETTY_FUNCTION__, __PRETTY_FUNCTION__); + + lldb::ScriptLanguage script_language = m_debugger.GetScriptLanguage(); + + m_command_dict["apropos"] = CommandObjectSP (new CommandObjectApropos (*this)); + m_command_dict["breakpoint"]= CommandObjectSP (new CommandObjectMultiwordBreakpoint (*this)); + m_command_dict["command"] = CommandObjectSP (new CommandObjectMultiwordCommands (*this)); + m_command_dict["disassemble"] = CommandObjectSP (new CommandObjectDisassemble (*this)); + m_command_dict["expression"]= CommandObjectSP (new CommandObjectExpression (*this)); + m_command_dict["frame"] = CommandObjectSP (new CommandObjectMultiwordFrame (*this)); + m_command_dict["help"] = CommandObjectSP (new CommandObjectHelp (*this)); + m_command_dict["log"] = CommandObjectSP (new CommandObjectLog (*this)); + m_command_dict["memory"] = CommandObjectSP (new CommandObjectMemory (*this)); + m_command_dict["platform"] = CommandObjectSP (new CommandObjectPlatform (*this)); + m_command_dict["plugin"] = CommandObjectSP (new CommandObjectPlugin (*this)); + m_command_dict["process"] = CommandObjectSP (new CommandObjectMultiwordProcess (*this)); + m_command_dict["quit"] = CommandObjectSP (new CommandObjectQuit (*this)); + m_command_dict["register"] = CommandObjectSP (new CommandObjectRegister (*this)); + m_command_dict["script"] = CommandObjectSP (new CommandObjectScript (*this, script_language)); + m_command_dict["settings"] = CommandObjectSP (new CommandObjectMultiwordSettings (*this)); + m_command_dict["source"] = CommandObjectSP (new CommandObjectMultiwordSource (*this)); + m_command_dict["target"] = CommandObjectSP (new CommandObjectMultiwordTarget (*this)); + m_command_dict["thread"] = CommandObjectSP (new CommandObjectMultiwordThread (*this)); + m_command_dict["type"] = CommandObjectSP (new CommandObjectType (*this)); + m_command_dict["version"] = CommandObjectSP (new CommandObjectVersion (*this)); + m_command_dict["watchpoint"]= CommandObjectSP (new CommandObjectMultiwordWatchpoint (*this)); + + const char *break_regexes[][2] = {{"^(.*[^[:space:]])[[:space:]]*:[[:space:]]*([[:digit:]]+)[[:space:]]*$", "breakpoint set --file '%1' --line %2"}, + {"^([[:digit:]]+)[[:space:]]*$", "breakpoint set --line %1"}, + {"^\\*?(0x[[:xdigit:]]+)[[:space:]]*$", "breakpoint set --address %1"}, + {"^[\"']?([-+]?\\[.*\\])[\"']?[[:space:]]*$", "breakpoint set --name '%1'"}, + {"^(-.*)$", "breakpoint set %1"}, + {"^(.*[^[:space:]])`(.*[^[:space:]])[[:space:]]*$", "breakpoint set --name '%2' --shlib '%1'"}, + {"^\\&(.*[^[:space:]])[[:space:]]*$", "breakpoint set --name '%1' --skip-prologue=0"}, + {"^(.*[^[:space:]])[[:space:]]*$", "breakpoint set --name '%1'"}}; + + size_t num_regexes = sizeof break_regexes/sizeof(char *[2]); + + std::unique_ptr + break_regex_cmd_ap(new CommandObjectRegexCommand (*this, + "_regexp-break", + "Set a breakpoint using a regular expression to specify the location, where is in decimal and
is in hex.", + "_regexp-break [:]\n_regexp-break []\n_regexp-break [
]\n_regexp-break <...>", + 2, + CommandCompletions::eSymbolCompletion | + CommandCompletions::eSourceFileCompletion)); + + if (break_regex_cmd_ap.get()) + { + bool success = true; + for (size_t i = 0; i < num_regexes; i++) + { + success = break_regex_cmd_ap->AddRegexCommand (break_regexes[i][0], break_regexes[i][1]); + if (!success) + break; + } + success = break_regex_cmd_ap->AddRegexCommand("^$", "breakpoint list --full"); + + if (success) + { + CommandObjectSP break_regex_cmd_sp(break_regex_cmd_ap.release()); + m_command_dict[break_regex_cmd_sp->GetCommandName ()] = break_regex_cmd_sp; + } + } + + std::unique_ptr + tbreak_regex_cmd_ap(new CommandObjectRegexCommand (*this, + "_regexp-tbreak", + "Set a one shot breakpoint using a regular expression to specify the location, where is in decimal and
is in hex.", + "_regexp-tbreak [:]\n_regexp-break []\n_regexp-break [
]\n_regexp-break <...>", + 2, + CommandCompletions::eSymbolCompletion | + CommandCompletions::eSourceFileCompletion)); + + if (tbreak_regex_cmd_ap.get()) + { + bool success = true; + for (size_t i = 0; i < num_regexes; i++) + { + // If you add a resultant command string longer than 1024 characters be sure to increase the size of this buffer. + char buffer[1024]; + int num_printed = snprintf(buffer, 1024, "%s %s", break_regexes[i][1], "-o"); + assert (num_printed < 1024); + success = tbreak_regex_cmd_ap->AddRegexCommand (break_regexes[i][0], buffer); + if (!success) + break; + } + success = tbreak_regex_cmd_ap->AddRegexCommand("^$", "breakpoint list --full"); + + if (success) + { + CommandObjectSP tbreak_regex_cmd_sp(tbreak_regex_cmd_ap.release()); + m_command_dict[tbreak_regex_cmd_sp->GetCommandName ()] = tbreak_regex_cmd_sp; + } + } + + std::unique_ptr + attach_regex_cmd_ap(new CommandObjectRegexCommand (*this, + "_regexp-attach", + "Attach to a process id if in decimal, otherwise treat the argument as a process name to attach to.", + "_regexp-attach []\n_regexp-attach []", + 2)); + if (attach_regex_cmd_ap.get()) + { + if (attach_regex_cmd_ap->AddRegexCommand("^([0-9]+)[[:space:]]*$", "process attach --pid %1") && + attach_regex_cmd_ap->AddRegexCommand("^(-.*|.* -.*)$", "process attach %1") && // Any options that are specified get passed to 'process attach' + attach_regex_cmd_ap->AddRegexCommand("^(.+)$", "process attach --name '%1'") && + attach_regex_cmd_ap->AddRegexCommand("^$", "process attach")) + { + CommandObjectSP attach_regex_cmd_sp(attach_regex_cmd_ap.release()); + m_command_dict[attach_regex_cmd_sp->GetCommandName ()] = attach_regex_cmd_sp; + } + } + + std::unique_ptr + down_regex_cmd_ap(new CommandObjectRegexCommand (*this, + "_regexp-down", + "Go down \"n\" frames in the stack (1 frame by default).", + "_regexp-down [n]", 2)); + if (down_regex_cmd_ap.get()) + { + if (down_regex_cmd_ap->AddRegexCommand("^$", "frame select -r -1") && + down_regex_cmd_ap->AddRegexCommand("^([0-9]+)$", "frame select -r -%1")) + { + CommandObjectSP down_regex_cmd_sp(down_regex_cmd_ap.release()); + m_command_dict[down_regex_cmd_sp->GetCommandName ()] = down_regex_cmd_sp; + } + } + + std::unique_ptr + up_regex_cmd_ap(new CommandObjectRegexCommand (*this, + "_regexp-up", + "Go up \"n\" frames in the stack (1 frame by default).", + "_regexp-up [n]", 2)); + if (up_regex_cmd_ap.get()) + { + if (up_regex_cmd_ap->AddRegexCommand("^$", "frame select -r 1") && + up_regex_cmd_ap->AddRegexCommand("^([0-9]+)$", "frame select -r %1")) + { + CommandObjectSP up_regex_cmd_sp(up_regex_cmd_ap.release()); + m_command_dict[up_regex_cmd_sp->GetCommandName ()] = up_regex_cmd_sp; + } + } + + std::unique_ptr + display_regex_cmd_ap(new CommandObjectRegexCommand (*this, + "_regexp-display", + "Add an expression evaluation stop-hook.", + "_regexp-display expression", 2)); + if (display_regex_cmd_ap.get()) + { + if (display_regex_cmd_ap->AddRegexCommand("^(.+)$", "target stop-hook add -o \"expr -- %1\"")) + { + CommandObjectSP display_regex_cmd_sp(display_regex_cmd_ap.release()); + m_command_dict[display_regex_cmd_sp->GetCommandName ()] = display_regex_cmd_sp; + } + } + + std::unique_ptr + undisplay_regex_cmd_ap(new CommandObjectRegexCommand (*this, + "_regexp-undisplay", + "Remove an expression evaluation stop-hook.", + "_regexp-undisplay stop-hook-number", 2)); + if (undisplay_regex_cmd_ap.get()) + { + if (undisplay_regex_cmd_ap->AddRegexCommand("^([0-9]+)$", "target stop-hook delete %1")) + { + CommandObjectSP undisplay_regex_cmd_sp(undisplay_regex_cmd_ap.release()); + m_command_dict[undisplay_regex_cmd_sp->GetCommandName ()] = undisplay_regex_cmd_sp; + } + } + + std::unique_ptr + connect_gdb_remote_cmd_ap(new CommandObjectRegexCommand (*this, + "gdb-remote", + "Connect to a remote GDB server. If no hostname is provided, localhost is assumed.", + "gdb-remote [:]", 2)); + if (connect_gdb_remote_cmd_ap.get()) + { + if (connect_gdb_remote_cmd_ap->AddRegexCommand("^([^:]+:[[:digit:]]+)$", "process connect --plugin gdb-remote connect://%1") && + connect_gdb_remote_cmd_ap->AddRegexCommand("^([[:digit:]]+)$", "process connect --plugin gdb-remote connect://localhost:%1")) + { + CommandObjectSP command_sp(connect_gdb_remote_cmd_ap.release()); + m_command_dict[command_sp->GetCommandName ()] = command_sp; + } + } + + std::unique_ptr + connect_kdp_remote_cmd_ap(new CommandObjectRegexCommand (*this, + "kdp-remote", + "Connect to a remote KDP server. udp port 41139 is the default port number.", + "kdp-remote [:]", 2)); + if (connect_kdp_remote_cmd_ap.get()) + { + if (connect_kdp_remote_cmd_ap->AddRegexCommand("^([^:]+:[[:digit:]]+)$", "process connect --plugin kdp-remote udp://%1") && + connect_kdp_remote_cmd_ap->AddRegexCommand("^(.+)$", "process connect --plugin kdp-remote udp://%1:41139")) + { + CommandObjectSP command_sp(connect_kdp_remote_cmd_ap.release()); + m_command_dict[command_sp->GetCommandName ()] = command_sp; + } + } + + std::unique_ptr + bt_regex_cmd_ap(new CommandObjectRegexCommand (*this, + "_regexp-bt", + "Show a backtrace. An optional argument is accepted; if that argument is a number, it specifies the number of frames to display. If that argument is 'all', full backtraces of all threads are displayed.", + "bt [|all]", 2)); + if (bt_regex_cmd_ap.get()) + { + // accept but don't document "bt -c " -- before bt was a regex command if you wanted to backtrace + // three frames you would do "bt -c 3" but the intention is to have this emulate the gdb "bt" command and + // so now "bt 3" is the preferred form, in line with gdb. + if (bt_regex_cmd_ap->AddRegexCommand("^([[:digit:]]+)$", "thread backtrace -c %1") && + bt_regex_cmd_ap->AddRegexCommand("^-c ([[:digit:]]+)$", "thread backtrace -c %1") && + bt_regex_cmd_ap->AddRegexCommand("^all$", "thread backtrace all") && + bt_regex_cmd_ap->AddRegexCommand("^$", "thread backtrace")) + { + CommandObjectSP command_sp(bt_regex_cmd_ap.release()); + m_command_dict[command_sp->GetCommandName ()] = command_sp; + } + } + + std::unique_ptr + list_regex_cmd_ap(new CommandObjectRegexCommand (*this, + "_regexp-list", + "Implements the GDB 'list' command in all of its forms except FILE:FUNCTION and maps them to the appropriate 'source list' commands.", + "_regexp-list []\n_regexp-attach [:]\n_regexp-attach [:]", + 2, + CommandCompletions::eSourceFileCompletion)); + if (list_regex_cmd_ap.get()) + { + if (list_regex_cmd_ap->AddRegexCommand("^([0-9]+)[[:space:]]*$", "source list --line %1") && + list_regex_cmd_ap->AddRegexCommand("^(.*[^[:space:]])[[:space:]]*:[[:space:]]*([[:digit:]]+)[[:space:]]*$", "source list --file '%1' --line %2") && + list_regex_cmd_ap->AddRegexCommand("^\\*?(0x[[:xdigit:]]+)[[:space:]]*$", "source list --address %1") && + list_regex_cmd_ap->AddRegexCommand("^-[[:space:]]*$", "source list --reverse") && + list_regex_cmd_ap->AddRegexCommand("^-([[:digit:]]+)[[:space:]]*$", "source list --reverse --count %1") && + list_regex_cmd_ap->AddRegexCommand("^(.+)$", "source list --name \"%1\"") && + list_regex_cmd_ap->AddRegexCommand("^$", "source list")) + { + CommandObjectSP list_regex_cmd_sp(list_regex_cmd_ap.release()); + m_command_dict[list_regex_cmd_sp->GetCommandName ()] = list_regex_cmd_sp; + } + } + + std::unique_ptr + env_regex_cmd_ap(new CommandObjectRegexCommand (*this, + "_regexp-env", + "Implements a shortcut to viewing and setting environment variables.", + "_regexp-env\n_regexp-env FOO=BAR", 2)); + if (env_regex_cmd_ap.get()) + { + if (env_regex_cmd_ap->AddRegexCommand("^$", "settings show target.env-vars") && + env_regex_cmd_ap->AddRegexCommand("^([A-Za-z_][A-Za-z_0-9]*=.*)$", "settings set target.env-vars %1")) + { + CommandObjectSP env_regex_cmd_sp(env_regex_cmd_ap.release()); + m_command_dict[env_regex_cmd_sp->GetCommandName ()] = env_regex_cmd_sp; + } + } + +} + +int +CommandInterpreter::GetCommandNamesMatchingPartialString (const char *cmd_str, bool include_aliases, + StringList &matches) +{ + CommandObject::AddNamesMatchingPartialString (m_command_dict, cmd_str, matches); + + if (include_aliases) + { + CommandObject::AddNamesMatchingPartialString (m_alias_dict, cmd_str, matches); + } + + return matches.GetSize(); +} + +CommandObjectSP +CommandInterpreter::GetCommandSP (const char *cmd_cstr, bool include_aliases, bool exact, StringList *matches) +{ + CommandObject::CommandMap::iterator pos; + CommandObjectSP command_sp; + + std::string cmd(cmd_cstr); + + if (HasCommands()) + { + pos = m_command_dict.find(cmd); + if (pos != m_command_dict.end()) + command_sp = pos->second; + } + + if (include_aliases && HasAliases()) + { + pos = m_alias_dict.find(cmd); + if (pos != m_alias_dict.end()) + command_sp = pos->second; + } + + if (HasUserCommands()) + { + pos = m_user_dict.find(cmd); + if (pos != m_user_dict.end()) + command_sp = pos->second; + } + + if (!exact && !command_sp) + { + // We will only get into here if we didn't find any exact matches. + + CommandObjectSP user_match_sp, alias_match_sp, real_match_sp; + + StringList local_matches; + if (matches == NULL) + matches = &local_matches; + + unsigned int num_cmd_matches = 0; + unsigned int num_alias_matches = 0; + unsigned int num_user_matches = 0; + + // Look through the command dictionaries one by one, and if we get only one match from any of + // them in toto, then return that, otherwise return an empty CommandObjectSP and the list of matches. + + if (HasCommands()) + { + num_cmd_matches = CommandObject::AddNamesMatchingPartialString (m_command_dict, cmd_cstr, *matches); + } + + if (num_cmd_matches == 1) + { + cmd.assign(matches->GetStringAtIndex(0)); + pos = m_command_dict.find(cmd); + if (pos != m_command_dict.end()) + real_match_sp = pos->second; + } + + if (include_aliases && HasAliases()) + { + num_alias_matches = CommandObject::AddNamesMatchingPartialString (m_alias_dict, cmd_cstr, *matches); + + } + + if (num_alias_matches == 1) + { + cmd.assign(matches->GetStringAtIndex (num_cmd_matches)); + pos = m_alias_dict.find(cmd); + if (pos != m_alias_dict.end()) + alias_match_sp = pos->second; + } + + if (HasUserCommands()) + { + num_user_matches = CommandObject::AddNamesMatchingPartialString (m_user_dict, cmd_cstr, *matches); + } + + if (num_user_matches == 1) + { + cmd.assign (matches->GetStringAtIndex (num_cmd_matches + num_alias_matches)); + + pos = m_user_dict.find (cmd); + if (pos != m_user_dict.end()) + user_match_sp = pos->second; + } + + // If we got exactly one match, return that, otherwise return the match list. + + if (num_user_matches + num_cmd_matches + num_alias_matches == 1) + { + if (num_cmd_matches) + return real_match_sp; + else if (num_alias_matches) + return alias_match_sp; + else + return user_match_sp; + } + } + else if (matches && command_sp) + { + matches->AppendString (cmd_cstr); + } + + + return command_sp; +} + +bool +CommandInterpreter::AddCommand (const char *name, const lldb::CommandObjectSP &cmd_sp, bool can_replace) +{ + if (name && name[0]) + { + std::string name_sstr(name); + bool found = (m_command_dict.find (name_sstr) != m_command_dict.end()); + if (found && !can_replace) + return false; + if (found && m_command_dict[name_sstr]->IsRemovable() == false) + return false; + m_command_dict[name_sstr] = cmd_sp; + return true; + } + return false; +} + +bool +CommandInterpreter::AddUserCommand (std::string name, + const lldb::CommandObjectSP &cmd_sp, + bool can_replace) +{ + if (!name.empty()) + { + + const char* name_cstr = name.c_str(); + + // do not allow replacement of internal commands + if (CommandExists(name_cstr)) + { + if (can_replace == false) + return false; + if (m_command_dict[name]->IsRemovable() == false) + return false; + } + + if (UserCommandExists(name_cstr)) + { + if (can_replace == false) + return false; + if (m_user_dict[name]->IsRemovable() == false) + return false; + } + + m_user_dict[name] = cmd_sp; + return true; + } + return false; +} + +CommandObjectSP +CommandInterpreter::GetCommandSPExact (const char *cmd_cstr, bool include_aliases) +{ + Args cmd_words (cmd_cstr); // Break up the command string into words, in case it's a multi-word command. + CommandObjectSP ret_val; // Possibly empty return value. + + if (cmd_cstr == NULL) + return ret_val; + + if (cmd_words.GetArgumentCount() == 1) + return GetCommandSP(cmd_cstr, include_aliases, true, NULL); + else + { + // We have a multi-word command (seemingly), so we need to do more work. + // First, get the cmd_obj_sp for the first word in the command. + CommandObjectSP cmd_obj_sp = GetCommandSP (cmd_words.GetArgumentAtIndex (0), include_aliases, true, NULL); + if (cmd_obj_sp.get() != NULL) + { + // Loop through the rest of the words in the command (everything passed in was supposed to be part of a + // command name), and find the appropriate sub-command SP for each command word.... + size_t end = cmd_words.GetArgumentCount(); + for (size_t j= 1; j < end; ++j) + { + if (cmd_obj_sp->IsMultiwordObject()) + { + cmd_obj_sp = cmd_obj_sp->GetSubcommandSP (cmd_words.GetArgumentAtIndex (j)); + if (cmd_obj_sp.get() == NULL) + // The sub-command name was invalid. Fail and return the empty 'ret_val'. + return ret_val; + } + else + // We have more words in the command name, but we don't have a multiword object. Fail and return + // empty 'ret_val'. + return ret_val; + } + // We successfully looped through all the command words and got valid command objects for them. Assign the + // last object retrieved to 'ret_val'. + ret_val = cmd_obj_sp; + } + } + return ret_val; +} + +CommandObject * +CommandInterpreter::GetCommandObjectExact (const char *cmd_cstr, bool include_aliases) +{ + return GetCommandSPExact (cmd_cstr, include_aliases).get(); +} + +CommandObject * +CommandInterpreter::GetCommandObject (const char *cmd_cstr, StringList *matches) +{ + CommandObject *command_obj = GetCommandSP (cmd_cstr, false, true, matches).get(); + + // If we didn't find an exact match to the command string in the commands, look in + // the aliases. + + if (command_obj) + return command_obj; + + command_obj = GetCommandSP (cmd_cstr, true, true, matches).get(); + + if (command_obj) + return command_obj; + + // If there wasn't an exact match then look for an inexact one in just the commands + command_obj = GetCommandSP(cmd_cstr, false, false, NULL).get(); + + // Finally, if there wasn't an inexact match among the commands, look for an inexact + // match in both the commands and aliases. + + if (command_obj) + { + if (matches) + matches->AppendString(command_obj->GetCommandName()); + return command_obj; + } + + return GetCommandSP(cmd_cstr, true, false, matches).get(); +} + +bool +CommandInterpreter::CommandExists (const char *cmd) +{ + return m_command_dict.find(cmd) != m_command_dict.end(); +} + +bool +CommandInterpreter::ProcessAliasOptionsArgs (lldb::CommandObjectSP &cmd_obj_sp, + const char *options_args, + OptionArgVectorSP &option_arg_vector_sp) +{ + bool success = true; + OptionArgVector *option_arg_vector = option_arg_vector_sp.get(); + + if (!options_args || (strlen (options_args) < 1)) + return true; + + std::string options_string (options_args); + Args args (options_args); + CommandReturnObject result; + // Check to see if the command being aliased can take any command options. + Options *options = cmd_obj_sp->GetOptions (); + if (options) + { + // See if any options were specified as part of the alias; if so, handle them appropriately. + options->NotifyOptionParsingStarting (); + args.Unshift ("dummy_arg"); + args.ParseAliasOptions (*options, result, option_arg_vector, options_string); + args.Shift (); + if (result.Succeeded()) + options->VerifyPartialOptions (result); + if (!result.Succeeded() && result.GetStatus() != lldb::eReturnStatusStarted) + { + result.AppendError ("Unable to create requested alias.\n"); + return false; + } + } + + if (!options_string.empty()) + { + if (cmd_obj_sp->WantsRawCommandString ()) + option_arg_vector->push_back (OptionArgPair ("", + OptionArgValue (-1, + options_string))); + else + { + const size_t argc = args.GetArgumentCount(); + for (size_t i = 0; i < argc; ++i) + if (strcmp (args.GetArgumentAtIndex (i), "") != 0) + option_arg_vector->push_back + (OptionArgPair ("", + OptionArgValue (-1, + std::string (args.GetArgumentAtIndex (i))))); + } + } + + return success; +} + +bool +CommandInterpreter::GetAliasFullName (const char *cmd, std::string &full_name) +{ + bool exact_match = (m_alias_dict.find(cmd) != m_alias_dict.end()); + if (exact_match) + { + full_name.assign(cmd); + return exact_match; + } + else + { + StringList matches; + size_t num_alias_matches; + num_alias_matches = CommandObject::AddNamesMatchingPartialString (m_alias_dict, cmd, matches); + if (num_alias_matches == 1) + { + // Make sure this isn't shadowing a command in the regular command space: + StringList regular_matches; + const bool include_aliases = false; + const bool exact = false; + CommandObjectSP cmd_obj_sp(GetCommandSP (cmd, include_aliases, exact, ®ular_matches)); + if (cmd_obj_sp || regular_matches.GetSize() > 0) + return false; + else + { + full_name.assign (matches.GetStringAtIndex(0)); + return true; + } + } + else + return false; + } +} + +bool +CommandInterpreter::AliasExists (const char *cmd) +{ + return m_alias_dict.find(cmd) != m_alias_dict.end(); +} + +bool +CommandInterpreter::UserCommandExists (const char *cmd) +{ + return m_user_dict.find(cmd) != m_user_dict.end(); +} + +void +CommandInterpreter::AddAlias (const char *alias_name, CommandObjectSP& command_obj_sp) +{ + command_obj_sp->SetIsAlias (true); + m_alias_dict[alias_name] = command_obj_sp; +} + +bool +CommandInterpreter::RemoveAlias (const char *alias_name) +{ + CommandObject::CommandMap::iterator pos = m_alias_dict.find(alias_name); + if (pos != m_alias_dict.end()) + { + m_alias_dict.erase(pos); + return true; + } + return false; +} +bool +CommandInterpreter::RemoveUser (const char *alias_name) +{ + CommandObject::CommandMap::iterator pos = m_user_dict.find(alias_name); + if (pos != m_user_dict.end()) + { + m_user_dict.erase(pos); + return true; + } + return false; +} + +void +CommandInterpreter::GetAliasHelp (const char *alias_name, const char *command_name, StreamString &help_string) +{ + help_string.Printf ("'%s", command_name); + OptionArgVectorSP option_arg_vector_sp = GetAliasOptions (alias_name); + + if (option_arg_vector_sp) + { + OptionArgVector *options = option_arg_vector_sp.get(); + for (size_t i = 0; i < options->size(); ++i) + { + OptionArgPair cur_option = (*options)[i]; + std::string opt = cur_option.first; + OptionArgValue value_pair = cur_option.second; + std::string value = value_pair.second; + if (opt.compare("") == 0) + { + help_string.Printf (" %s", value.c_str()); + } + else + { + help_string.Printf (" %s", opt.c_str()); + if ((value.compare ("") != 0) + && (value.compare ("first.size(); + if (max_len < len) + max_len = len; + } + return max_len; +} + +void +CommandInterpreter::GetHelp (CommandReturnObject &result, + uint32_t cmd_types) +{ + CommandObject::CommandMap::const_iterator pos; + size_t max_len = FindLongestCommandWord (m_command_dict); + + if ( (cmd_types & eCommandTypesBuiltin) == eCommandTypesBuiltin ) + { + + result.AppendMessage("The following is a list of built-in, permanent debugger commands:"); + result.AppendMessage(""); + + for (pos = m_command_dict.begin(); pos != m_command_dict.end(); ++pos) + { + OutputFormattedHelpText (result.GetOutputStream(), pos->first.c_str(), "--", pos->second->GetHelp(), + max_len); + } + result.AppendMessage(""); + + } + + if (!m_alias_dict.empty() && ( (cmd_types & eCommandTypesAliases) == eCommandTypesAliases )) + { + result.AppendMessage("The following is a list of your current command abbreviations " + "(see 'help command alias' for more info):"); + result.AppendMessage(""); + max_len = FindLongestCommandWord (m_alias_dict); + + for (pos = m_alias_dict.begin(); pos != m_alias_dict.end(); ++pos) + { + StreamString sstr; + StreamString translation_and_help; + std::string entry_name = pos->first; + std::string second_entry = pos->second.get()->GetCommandName(); + GetAliasHelp (pos->first.c_str(), pos->second->GetCommandName(), sstr); + + translation_and_help.Printf ("(%s) %s", sstr.GetData(), pos->second->GetHelp()); + OutputFormattedHelpText (result.GetOutputStream(), pos->first.c_str(), "--", + translation_and_help.GetData(), max_len); + } + result.AppendMessage(""); + } + + if (!m_user_dict.empty() && ( (cmd_types & eCommandTypesUserDef) == eCommandTypesUserDef )) + { + result.AppendMessage ("The following is a list of your current user-defined commands:"); + result.AppendMessage(""); + max_len = FindLongestCommandWord (m_user_dict); + for (pos = m_user_dict.begin(); pos != m_user_dict.end(); ++pos) + { + OutputFormattedHelpText (result.GetOutputStream(), pos->first.c_str(), "--", pos->second->GetHelp(), + max_len); + } + result.AppendMessage(""); + } + + result.AppendMessage("For more information on any particular command, try 'help '."); +} + +CommandObject * +CommandInterpreter::GetCommandObjectForCommand (std::string &command_string) +{ + // This function finds the final, lowest-level, alias-resolved command object whose 'Execute' function will + // eventually be invoked by the given command line. + + CommandObject *cmd_obj = NULL; + std::string white_space (" \t\v"); + size_t start = command_string.find_first_not_of (white_space); + size_t end = 0; + bool done = false; + while (!done) + { + if (start != std::string::npos) + { + // Get the next word from command_string. + end = command_string.find_first_of (white_space, start); + if (end == std::string::npos) + end = command_string.size(); + std::string cmd_word = command_string.substr (start, end - start); + + if (cmd_obj == NULL) + // Since cmd_obj is NULL we are on our first time through this loop. Check to see if cmd_word is a valid + // command or alias. + cmd_obj = GetCommandObject (cmd_word.c_str()); + else if (cmd_obj->IsMultiwordObject ()) + { + // Our current object is a multi-word object; see if the cmd_word is a valid sub-command for our object. + CommandObject *sub_cmd_obj = cmd_obj->GetSubcommandObject (cmd_word.c_str()); + if (sub_cmd_obj) + cmd_obj = sub_cmd_obj; + else // cmd_word was not a valid sub-command word, so we are donee + done = true; + } + else + // We have a cmd_obj and it is not a multi-word object, so we are done. + done = true; + + // If we didn't find a valid command object, or our command object is not a multi-word object, or + // we are at the end of the command_string, then we are done. Otherwise, find the start of the + // next word. + + if (!cmd_obj || !cmd_obj->IsMultiwordObject() || end >= command_string.size()) + done = true; + else + start = command_string.find_first_not_of (white_space, end); + } + else + // Unable to find any more words. + done = true; + } + + if (end == command_string.size()) + command_string.clear(); + else + command_string = command_string.substr(end); + + return cmd_obj; +} + +static const char *k_white_space = " \t\v"; +static const char *k_valid_command_chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_"; +static void +StripLeadingSpaces (std::string &s) +{ + if (!s.empty()) + { + size_t pos = s.find_first_not_of (k_white_space); + if (pos == std::string::npos) + s.clear(); + else if (pos == 0) + return; + s.erase (0, pos); + } +} + +static size_t +FindArgumentTerminator (const std::string &s) +{ + const size_t s_len = s.size(); + size_t offset = 0; + while (offset < s_len) + { + size_t pos = s.find ("--", offset); + if (pos == std::string::npos) + break; + if (pos > 0) + { + if (isspace(s[pos-1])) + { + // Check if the string ends "\s--" (where \s is a space character) + // or if we have "\s--\s". + if ((pos + 2 >= s_len) || isspace(s[pos+2])) + { + return pos; + } + } + } + offset = pos + 2; + } + return std::string::npos; +} + +static bool +ExtractCommand (std::string &command_string, std::string &command, std::string &suffix, char "e_char) +{ + command.clear(); + suffix.clear(); + StripLeadingSpaces (command_string); + + bool result = false; + quote_char = '\0'; + + if (!command_string.empty()) + { + const char first_char = command_string[0]; + if (first_char == '\'' || first_char == '"') + { + quote_char = first_char; + const size_t end_quote_pos = command_string.find (quote_char, 1); + if (end_quote_pos == std::string::npos) + { + command.swap (command_string); + command_string.erase (); + } + else + { + command.assign (command_string, 1, end_quote_pos - 1); + if (end_quote_pos + 1 < command_string.size()) + command_string.erase (0, command_string.find_first_not_of (k_white_space, end_quote_pos + 1)); + else + command_string.erase (); + } + } + else + { + const size_t first_space_pos = command_string.find_first_of (k_white_space); + if (first_space_pos == std::string::npos) + { + command.swap (command_string); + command_string.erase(); + } + else + { + command.assign (command_string, 0, first_space_pos); + command_string.erase(0, command_string.find_first_not_of (k_white_space, first_space_pos)); + } + } + result = true; + } + + + if (!command.empty()) + { + // actual commands can't start with '-' or '_' + if (command[0] != '-' && command[0] != '_') + { + size_t pos = command.find_first_not_of(k_valid_command_chars); + if (pos > 0 && pos != std::string::npos) + { + suffix.assign (command.begin() + pos, command.end()); + command.erase (pos); + } + } + } + + return result; +} + +CommandObject * +CommandInterpreter::BuildAliasResult (const char *alias_name, + std::string &raw_input_string, + std::string &alias_result, + CommandReturnObject &result) +{ + CommandObject *alias_cmd_obj = NULL; + Args cmd_args (raw_input_string.c_str()); + alias_cmd_obj = GetCommandObject (alias_name); + StreamString result_str; + + if (alias_cmd_obj) + { + std::string alias_name_str = alias_name; + if ((cmd_args.GetArgumentCount() == 0) + || (alias_name_str.compare (cmd_args.GetArgumentAtIndex(0)) != 0)) + cmd_args.Unshift (alias_name); + + result_str.Printf ("%s", alias_cmd_obj->GetCommandName ()); + OptionArgVectorSP option_arg_vector_sp = GetAliasOptions (alias_name); + + if (option_arg_vector_sp.get()) + { + OptionArgVector *option_arg_vector = option_arg_vector_sp.get(); + + for (size_t i = 0; i < option_arg_vector->size(); ++i) + { + OptionArgPair option_pair = (*option_arg_vector)[i]; + OptionArgValue value_pair = option_pair.second; + int value_type = value_pair.first; + std::string option = option_pair.first; + std::string value = value_pair.second; + if (option.compare ("") == 0) + result_str.Printf (" %s", value.c_str()); + else + { + result_str.Printf (" %s", option.c_str()); + if (value_type != optional_argument) + result_str.Printf (" "); + if (value.compare ("") != 0) + { + int index = GetOptionArgumentPosition (value.c_str()); + if (index == 0) + result_str.Printf ("%s", value.c_str()); + else if (index >= cmd_args.GetArgumentCount()) + { + + result.AppendErrorWithFormat + ("Not enough arguments provided; you need at least %d arguments to use this alias.\n", + index); + result.SetStatus (eReturnStatusFailed); + return alias_cmd_obj; + } + else + { + size_t strpos = raw_input_string.find (cmd_args.GetArgumentAtIndex (index)); + if (strpos != std::string::npos) + raw_input_string = raw_input_string.erase (strpos, + strlen (cmd_args.GetArgumentAtIndex (index))); + result_str.Printf ("%s", cmd_args.GetArgumentAtIndex (index)); + } + } + } + } + } + + alias_result = result_str.GetData(); + } + return alias_cmd_obj; +} + +Error +CommandInterpreter::PreprocessCommand (std::string &command) +{ + // The command preprocessor needs to do things to the command + // line before any parsing of arguments or anything else is done. + // The only current stuff that gets proprocessed is anyting enclosed + // in backtick ('`') characters is evaluated as an expression and + // the result of the expression must be a scalar that can be substituted + // into the command. An example would be: + // (lldb) memory read `$rsp + 20` + Error error; // Error for any expressions that might not evaluate + size_t start_backtick; + size_t pos = 0; + while ((start_backtick = command.find ('`', pos)) != std::string::npos) + { + if (start_backtick > 0 && command[start_backtick-1] == '\\') + { + // The backtick was preceeded by a '\' character, remove the slash + // and don't treat the backtick as the start of an expression + command.erase(start_backtick-1, 1); + // No need to add one to start_backtick since we just deleted a char + pos = start_backtick; + } + else + { + const size_t expr_content_start = start_backtick + 1; + const size_t end_backtick = command.find ('`', expr_content_start); + if (end_backtick == std::string::npos) + return error; + else if (end_backtick == expr_content_start) + { + // Empty expression (two backticks in a row) + command.erase (start_backtick, 2); + } + else + { + std::string expr_str (command, expr_content_start, end_backtick - expr_content_start); + + ExecutionContext exe_ctx(GetExecutionContext()); + Target *target = exe_ctx.GetTargetPtr(); + // Get a dummy target to allow for calculator mode while processing backticks. + // This also helps break the infinite loop caused when target is null. + if (!target) + target = Host::GetDummyTarget(GetDebugger()).get(); + if (target) + { + ValueObjectSP expr_result_valobj_sp; + + EvaluateExpressionOptions options; + options.SetCoerceToId(false) + .SetUnwindOnError(true) + .SetIgnoreBreakpoints(true) + .SetKeepInMemory(false) + .SetRunOthers(true) + .SetTimeoutUsec(0); + + ExecutionResults expr_result = target->EvaluateExpression (expr_str.c_str(), + exe_ctx.GetFramePtr(), + expr_result_valobj_sp, + options); + + if (expr_result == eExecutionCompleted) + { + Scalar scalar; + if (expr_result_valobj_sp->ResolveValue (scalar)) + { + command.erase (start_backtick, end_backtick - start_backtick + 1); + StreamString value_strm; + const bool show_type = false; + scalar.GetValue (&value_strm, show_type); + size_t value_string_size = value_strm.GetSize(); + if (value_string_size) + { + command.insert (start_backtick, value_strm.GetData(), value_string_size); + pos = start_backtick + value_string_size; + continue; + } + else + { + error.SetErrorStringWithFormat("expression value didn't result in a scalar value for the expression '%s'", expr_str.c_str()); + } + } + else + { + error.SetErrorStringWithFormat("expression value didn't result in a scalar value for the expression '%s'", expr_str.c_str()); + } + } + else + { + if (expr_result_valobj_sp) + error = expr_result_valobj_sp->GetError(); + if (error.Success()) + { + + switch (expr_result) + { + case eExecutionSetupError: + error.SetErrorStringWithFormat("expression setup error for the expression '%s'", expr_str.c_str()); + break; + case eExecutionCompleted: + break; + case eExecutionDiscarded: + error.SetErrorStringWithFormat("expression discarded for the expression '%s'", expr_str.c_str()); + break; + case eExecutionInterrupted: + error.SetErrorStringWithFormat("expression interrupted for the expression '%s'", expr_str.c_str()); + break; + case eExecutionHitBreakpoint: + error.SetErrorStringWithFormat("expression hit breakpoint for the expression '%s'", expr_str.c_str()); + break; + case eExecutionTimedOut: + error.SetErrorStringWithFormat("expression timed out for the expression '%s'", expr_str.c_str()); + break; + } + } + } + } + } + if (error.Fail()) + break; + } + } + return error; +} + + +bool +CommandInterpreter::HandleCommand (const char *command_line, + LazyBool lazy_add_to_history, + CommandReturnObject &result, + ExecutionContext *override_context, + bool repeat_on_empty_command, + bool no_context_switching) + +{ + + bool done = false; + CommandObject *cmd_obj = NULL; + bool wants_raw_input = false; + std::string command_string (command_line); + std::string original_command_string (command_line); + + Log *log (lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_COMMANDS)); + Host::SetCrashDescriptionWithFormat ("HandleCommand(command = \"%s\")", command_line); + + // Make a scoped cleanup object that will clear the crash description string + // on exit of this function. + lldb_utility::CleanUp crash_description_cleanup(NULL, Host::SetCrashDescription); + + if (log) + log->Printf ("Processing command: %s", command_line); + + Timer scoped_timer (__PRETTY_FUNCTION__, "Handling command: %s.", command_line); + + if (!no_context_switching) + UpdateExecutionContext (override_context); + + bool add_to_history; + if (lazy_add_to_history == eLazyBoolCalculate) + add_to_history = (m_command_source_depth == 0); + else + add_to_history = (lazy_add_to_history == eLazyBoolYes); + + bool empty_command = false; + bool comment_command = false; + if (command_string.empty()) + empty_command = true; + else + { + const char *k_space_characters = "\t\n\v\f\r "; + + size_t non_space = command_string.find_first_not_of (k_space_characters); + // Check for empty line or comment line (lines whose first + // non-space character is the comment character for this interpreter) + if (non_space == std::string::npos) + empty_command = true; + else if (command_string[non_space] == m_comment_char) + comment_command = true; + else if (command_string[non_space] == CommandHistory::g_repeat_char) + { + const char *history_string = m_command_history.FindString(command_string.c_str() + non_space); + if (history_string == NULL) + { + result.AppendErrorWithFormat ("Could not find entry: %s in history", command_string.c_str()); + result.SetStatus(eReturnStatusFailed); + return false; + } + add_to_history = false; + command_string = history_string; + original_command_string = history_string; + } + } + + if (empty_command) + { + if (repeat_on_empty_command) + { + if (m_command_history.IsEmpty()) + { + result.AppendError ("empty command"); + result.SetStatus(eReturnStatusFailed); + return false; + } + else + { + command_line = m_repeat_command.c_str(); + command_string = command_line; + original_command_string = command_line; + if (m_repeat_command.empty()) + { + result.AppendErrorWithFormat("No auto repeat.\n"); + result.SetStatus (eReturnStatusFailed); + return false; + } + } + add_to_history = false; + } + else + { + result.SetStatus (eReturnStatusSuccessFinishNoResult); + return true; + } + } + else if (comment_command) + { + result.SetStatus (eReturnStatusSuccessFinishNoResult); + return true; + } + + + Error error (PreprocessCommand (command_string)); + + if (error.Fail()) + { + result.AppendError (error.AsCString()); + result.SetStatus(eReturnStatusFailed); + return false; + } + // Phase 1. + + // Before we do ANY kind of argument processing, etc. we need to figure out what the real/final command object + // is for the specified command, and whether or not it wants raw input. This gets complicated by the fact that + // the user could have specified an alias, and in translating the alias there may also be command options and/or + // even data (including raw text strings) that need to be found and inserted into the command line as part of + // the translation. So this first step is plain look-up & replacement, resulting in three things: 1). the command + // object whose Execute method will actually be called; 2). a revised command string, with all substitutions & + // replacements taken care of; 3). whether or not the Execute function wants raw input or not. + + StreamString revised_command_line; + size_t actual_cmd_name_len = 0; + std::string next_word; + StringList matches; + while (!done) + { + char quote_char = '\0'; + std::string suffix; + ExtractCommand (command_string, next_word, suffix, quote_char); + if (cmd_obj == NULL) + { + std::string full_name; + if (GetAliasFullName(next_word.c_str(), full_name)) + { + std::string alias_result; + cmd_obj = BuildAliasResult (full_name.c_str(), command_string, alias_result, result); + revised_command_line.Printf ("%s", alias_result.c_str()); + if (cmd_obj) + { + wants_raw_input = cmd_obj->WantsRawCommandString (); + actual_cmd_name_len = strlen (cmd_obj->GetCommandName()); + } + } + else + { + cmd_obj = GetCommandObject (next_word.c_str(), &matches); + if (cmd_obj) + { + actual_cmd_name_len += next_word.length(); + revised_command_line.Printf ("%s", next_word.c_str()); + wants_raw_input = cmd_obj->WantsRawCommandString (); + } + else + { + revised_command_line.Printf ("%s", next_word.c_str()); + } + } + } + else + { + if (cmd_obj->IsMultiwordObject ()) + { + CommandObject *sub_cmd_obj = cmd_obj->GetSubcommandObject (next_word.c_str()); + if (sub_cmd_obj) + { + actual_cmd_name_len += next_word.length() + 1; + revised_command_line.Printf (" %s", next_word.c_str()); + cmd_obj = sub_cmd_obj; + wants_raw_input = cmd_obj->WantsRawCommandString (); + } + else + { + if (quote_char) + revised_command_line.Printf (" %c%s%s%c", quote_char, next_word.c_str(), suffix.c_str(), quote_char); + else + revised_command_line.Printf (" %s%s", next_word.c_str(), suffix.c_str()); + done = true; + } + } + else + { + if (quote_char) + revised_command_line.Printf (" %c%s%s%c", quote_char, next_word.c_str(), suffix.c_str(), quote_char); + else + revised_command_line.Printf (" %s%s", next_word.c_str(), suffix.c_str()); + done = true; + } + } + + if (cmd_obj == NULL) + { + const size_t num_matches = matches.GetSize(); + if (matches.GetSize() > 1) { + StreamString error_msg; + error_msg.Printf ("Ambiguous command '%s'. Possible matches:\n", next_word.c_str()); + + for (uint32_t i = 0; i < num_matches; ++i) { + error_msg.Printf ("\t%s\n", matches.GetStringAtIndex(i)); + } + result.AppendRawError (error_msg.GetString().c_str()); + } else { + // We didn't have only one match, otherwise we wouldn't get here. + assert(num_matches == 0); + result.AppendErrorWithFormat ("'%s' is not a valid command.\n", next_word.c_str()); + } + result.SetStatus (eReturnStatusFailed); + return false; + } + + if (cmd_obj->IsMultiwordObject ()) + { + if (!suffix.empty()) + { + + result.AppendErrorWithFormat ("command '%s' did not recognize '%s%s%s' as valid (subcommand might be invalid).\n", + cmd_obj->GetCommandName(), + next_word.empty() ? "" : next_word.c_str(), + next_word.empty() ? " -- " : " ", + suffix.c_str()); + result.SetStatus (eReturnStatusFailed); + return false; + } + } + else + { + // If we found a normal command, we are done + done = true; + if (!suffix.empty()) + { + switch (suffix[0]) + { + case '/': + // GDB format suffixes + { + Options *command_options = cmd_obj->GetOptions(); + if (command_options && command_options->SupportsLongOption("gdb-format")) + { + std::string gdb_format_option ("--gdb-format="); + gdb_format_option += (suffix.c_str() + 1); + + bool inserted = false; + std::string &cmd = revised_command_line.GetString(); + size_t arg_terminator_idx = FindArgumentTerminator (cmd); + if (arg_terminator_idx != std::string::npos) + { + // Insert the gdb format option before the "--" that terminates options + gdb_format_option.append(1,' '); + cmd.insert(arg_terminator_idx, gdb_format_option); + inserted = true; + } + + if (!inserted) + revised_command_line.Printf (" %s", gdb_format_option.c_str()); + + if (wants_raw_input && FindArgumentTerminator(cmd) == std::string::npos) + revised_command_line.PutCString (" --"); + } + else + { + result.AppendErrorWithFormat ("the '%s' command doesn't support the --gdb-format option\n", + cmd_obj->GetCommandName()); + result.SetStatus (eReturnStatusFailed); + return false; + } + } + break; + + default: + result.AppendErrorWithFormat ("unknown command shorthand suffix: '%s'\n", + suffix.c_str()); + result.SetStatus (eReturnStatusFailed); + return false; + + } + } + } + if (command_string.length() == 0) + done = true; + + } + + if (!command_string.empty()) + revised_command_line.Printf (" %s", command_string.c_str()); + + // End of Phase 1. + // At this point cmd_obj should contain the CommandObject whose Execute method will be called, if the command + // specified was valid; revised_command_line contains the complete command line (including command name(s)), + // fully translated with all substitutions & translations taken care of (still in raw text format); and + // wants_raw_input specifies whether the Execute method expects raw input or not. + + + if (log) + { + log->Printf ("HandleCommand, cmd_obj : '%s'", cmd_obj ? cmd_obj->GetCommandName() : ""); + log->Printf ("HandleCommand, revised_command_line: '%s'", revised_command_line.GetData()); + log->Printf ("HandleCommand, wants_raw_input:'%s'", wants_raw_input ? "True" : "False"); + } + + // Phase 2. + // Take care of things like setting up the history command & calling the appropriate Execute method on the + // CommandObject, with the appropriate arguments. + + if (cmd_obj != NULL) + { + if (add_to_history) + { + Args command_args (revised_command_line.GetData()); + const char *repeat_command = cmd_obj->GetRepeatCommand(command_args, 0); + if (repeat_command != NULL) + m_repeat_command.assign(repeat_command); + else + m_repeat_command.assign(original_command_string.c_str()); + + m_command_history.AppendString (original_command_string); + } + + command_string = revised_command_line.GetData(); + std::string command_name (cmd_obj->GetCommandName()); + std::string remainder; + if (actual_cmd_name_len < command_string.length()) + remainder = command_string.substr (actual_cmd_name_len); // Note: 'actual_cmd_name_len' may be considerably shorter + // than cmd_obj->GetCommandName(), because name completion + // allows users to enter short versions of the names, + // e.g. 'br s' for 'breakpoint set'. + + // Remove any initial spaces + std::string white_space (" \t\v"); + size_t pos = remainder.find_first_not_of (white_space); + if (pos != 0 && pos != std::string::npos) + remainder.erase(0, pos); + + if (log) + log->Printf ("HandleCommand, command line after removing command name(s): '%s'", remainder.c_str()); + + cmd_obj->Execute (remainder.c_str(), result); + } + else + { + // We didn't find the first command object, so complete the first argument. + Args command_args (revised_command_line.GetData()); + StringList matches; + int num_matches; + int cursor_index = 0; + int cursor_char_position = strlen (command_args.GetArgumentAtIndex(0)); + bool word_complete; + num_matches = HandleCompletionMatches (command_args, + cursor_index, + cursor_char_position, + 0, + -1, + word_complete, + matches); + + if (num_matches > 0) + { + std::string error_msg; + error_msg.assign ("ambiguous command '"); + error_msg.append(command_args.GetArgumentAtIndex(0)); + error_msg.append ("'."); + + error_msg.append (" Possible completions:"); + for (int i = 0; i < num_matches; i++) + { + error_msg.append ("\n\t"); + error_msg.append (matches.GetStringAtIndex (i)); + } + error_msg.append ("\n"); + result.AppendRawError (error_msg.c_str()); + } + else + result.AppendErrorWithFormat ("Unrecognized command '%s'.\n", command_args.GetArgumentAtIndex (0)); + + result.SetStatus (eReturnStatusFailed); + } + + if (log) + log->Printf ("HandleCommand, command %s", (result.Succeeded() ? "succeeded" : "did not succeed")); + + return result.Succeeded(); +} + +int +CommandInterpreter::HandleCompletionMatches (Args &parsed_line, + int &cursor_index, + int &cursor_char_position, + int match_start_point, + int max_return_elements, + bool &word_complete, + StringList &matches) +{ + int num_command_matches = 0; + bool look_for_subcommand = false; + + // For any of the command completions a unique match will be a complete word. + word_complete = true; + + if (cursor_index == -1) + { + // We got nothing on the command line, so return the list of commands + bool include_aliases = true; + num_command_matches = GetCommandNamesMatchingPartialString ("", include_aliases, matches); + } + else if (cursor_index == 0) + { + // The cursor is in the first argument, so just do a lookup in the dictionary. + CommandObject *cmd_obj = GetCommandObject (parsed_line.GetArgumentAtIndex(0), &matches); + num_command_matches = matches.GetSize(); + + if (num_command_matches == 1 + && cmd_obj && cmd_obj->IsMultiwordObject() + && matches.GetStringAtIndex(0) != NULL + && strcmp (parsed_line.GetArgumentAtIndex(0), matches.GetStringAtIndex(0)) == 0) + { + look_for_subcommand = true; + num_command_matches = 0; + matches.DeleteStringAtIndex(0); + parsed_line.AppendArgument (""); + cursor_index++; + cursor_char_position = 0; + } + } + + if (cursor_index > 0 || look_for_subcommand) + { + // We are completing further on into a commands arguments, so find the command and tell it + // to complete the command. + // First see if there is a matching initial command: + CommandObject *command_object = GetCommandObject (parsed_line.GetArgumentAtIndex(0)); + if (command_object == NULL) + { + return 0; + } + else + { + parsed_line.Shift(); + cursor_index--; + num_command_matches = command_object->HandleCompletion (parsed_line, + cursor_index, + cursor_char_position, + match_start_point, + max_return_elements, + word_complete, + matches); + } + } + + return num_command_matches; + +} + +int +CommandInterpreter::HandleCompletion (const char *current_line, + const char *cursor, + const char *last_char, + int match_start_point, + int max_return_elements, + StringList &matches) +{ + // We parse the argument up to the cursor, so the last argument in parsed_line is + // the one containing the cursor, and the cursor is after the last character. + + Args parsed_line(current_line, last_char - current_line); + Args partial_parsed_line(current_line, cursor - current_line); + + // Don't complete comments, and if the line we are completing is just the history repeat character, + // substitute the appropriate history line. + const char *first_arg = parsed_line.GetArgumentAtIndex(0); + if (first_arg) + { + if (first_arg[0] == m_comment_char) + return 0; + else if (first_arg[0] == CommandHistory::g_repeat_char) + { + const char *history_string = m_command_history.FindString (first_arg); + if (history_string != NULL) + { + matches.Clear(); + matches.InsertStringAtIndex(0, history_string); + return -2; + } + else + return 0; + + } + } + + + int num_args = partial_parsed_line.GetArgumentCount(); + int cursor_index = partial_parsed_line.GetArgumentCount() - 1; + int cursor_char_position; + + if (cursor_index == -1) + cursor_char_position = 0; + else + cursor_char_position = strlen (partial_parsed_line.GetArgumentAtIndex(cursor_index)); + + if (cursor > current_line && cursor[-1] == ' ') + { + // We are just after a space. If we are in an argument, then we will continue + // parsing, but if we are between arguments, then we have to complete whatever the next + // element would be. + // We can distinguish the two cases because if we are in an argument (e.g. because the space is + // protected by a quote) then the space will also be in the parsed argument... + + const char *current_elem = partial_parsed_line.GetArgumentAtIndex(cursor_index); + if (cursor_char_position == 0 || current_elem[cursor_char_position - 1] != ' ') + { + parsed_line.InsertArgumentAtIndex(cursor_index + 1, "", '"'); + cursor_index++; + cursor_char_position = 0; + } + } + + int num_command_matches; + + matches.Clear(); + + // Only max_return_elements == -1 is supported at present: + assert (max_return_elements == -1); + bool word_complete; + num_command_matches = HandleCompletionMatches (parsed_line, + cursor_index, + cursor_char_position, + match_start_point, + max_return_elements, + word_complete, + matches); + + if (num_command_matches <= 0) + return num_command_matches; + + if (num_args == 0) + { + // If we got an empty string, insert nothing. + matches.InsertStringAtIndex(0, ""); + } + else + { + // Now figure out if there is a common substring, and if so put that in element 0, otherwise + // put an empty string in element 0. + std::string command_partial_str; + if (cursor_index >= 0) + command_partial_str.assign(parsed_line.GetArgumentAtIndex(cursor_index), + parsed_line.GetArgumentAtIndex(cursor_index) + cursor_char_position); + + std::string common_prefix; + matches.LongestCommonPrefix (common_prefix); + const size_t partial_name_len = command_partial_str.size(); + + // If we matched a unique single command, add a space... + // Only do this if the completer told us this was a complete word, however... + if (num_command_matches == 1 && word_complete) + { + char quote_char = parsed_line.GetArgumentQuoteCharAtIndex(cursor_index); + if (quote_char != '\0') + common_prefix.push_back(quote_char); + + common_prefix.push_back(' '); + } + common_prefix.erase (0, partial_name_len); + matches.InsertStringAtIndex(0, common_prefix.c_str()); + } + return num_command_matches; +} + + +CommandInterpreter::~CommandInterpreter () +{ +} + +const char * +CommandInterpreter::GetPrompt () +{ + return m_debugger.GetPrompt(); +} + +void +CommandInterpreter::SetPrompt (const char *new_prompt) +{ + m_debugger.SetPrompt (new_prompt); +} + +size_t +CommandInterpreter::GetConfirmationInputReaderCallback +( + void *baton, + InputReader &reader, + lldb::InputReaderAction action, + const char *bytes, + size_t bytes_len +) +{ + File &out_file = reader.GetDebugger().GetOutputFile(); + bool *response_ptr = (bool *) baton; + + switch (action) + { + case eInputReaderActivate: + if (out_file.IsValid()) + { + if (reader.GetPrompt()) + { + out_file.Printf ("%s", reader.GetPrompt()); + out_file.Flush (); + } + } + break; + + case eInputReaderDeactivate: + break; + + case eInputReaderReactivate: + if (out_file.IsValid() && reader.GetPrompt()) + { + out_file.Printf ("%s", reader.GetPrompt()); + out_file.Flush (); + } + break; + + case eInputReaderAsynchronousOutputWritten: + break; + + case eInputReaderGotToken: + if (bytes_len == 0) + { + reader.SetIsDone(true); + } + else if (bytes[0] == 'y' || bytes[0] == 'Y') + { + *response_ptr = true; + reader.SetIsDone(true); + } + else if (bytes[0] == 'n' || bytes[0] == 'N') + { + *response_ptr = false; + reader.SetIsDone(true); + } + else + { + if (out_file.IsValid() && !reader.IsDone() && reader.GetPrompt()) + { + out_file.Printf ("Please answer \"y\" or \"n\".\n%s", reader.GetPrompt()); + out_file.Flush (); + } + } + break; + + case eInputReaderInterrupt: + case eInputReaderEndOfFile: + *response_ptr = false; // Assume ^C or ^D means cancel the proposed action + reader.SetIsDone (true); + break; + + case eInputReaderDone: + break; + } + + return bytes_len; + +} + +bool +CommandInterpreter::Confirm (const char *message, bool default_answer) +{ + // Check AutoConfirm first: + if (m_debugger.GetAutoConfirm()) + return default_answer; + + InputReaderSP reader_sp (new InputReader(GetDebugger())); + bool response = default_answer; + if (reader_sp) + { + std::string prompt(message); + prompt.append(": ["); + if (default_answer) + prompt.append ("Y/n] "); + else + prompt.append ("y/N] "); + + Error err (reader_sp->Initialize (CommandInterpreter::GetConfirmationInputReaderCallback, + &response, // baton + eInputReaderGranularityLine, // token size, to pass to callback function + NULL, // end token + prompt.c_str(), // prompt + true)); // echo input + if (err.Success()) + { + GetDebugger().PushInputReader (reader_sp); + } + reader_sp->WaitOnReaderIsDone(); + } + return response; +} + +OptionArgVectorSP +CommandInterpreter::GetAliasOptions (const char *alias_name) +{ + OptionArgMap::iterator pos; + OptionArgVectorSP ret_val; + + std::string alias (alias_name); + + if (HasAliasOptions()) + { + pos = m_alias_options.find (alias); + if (pos != m_alias_options.end()) + ret_val = pos->second; + } + + return ret_val; +} + +void +CommandInterpreter::RemoveAliasOptions (const char *alias_name) +{ + OptionArgMap::iterator pos = m_alias_options.find(alias_name); + if (pos != m_alias_options.end()) + { + m_alias_options.erase (pos); + } +} + +void +CommandInterpreter::AddOrReplaceAliasOptions (const char *alias_name, OptionArgVectorSP &option_arg_vector_sp) +{ + m_alias_options[alias_name] = option_arg_vector_sp; +} + +bool +CommandInterpreter::HasCommands () +{ + return (!m_command_dict.empty()); +} + +bool +CommandInterpreter::HasAliases () +{ + return (!m_alias_dict.empty()); +} + +bool +CommandInterpreter::HasUserCommands () +{ + return (!m_user_dict.empty()); +} + +bool +CommandInterpreter::HasAliasOptions () +{ + return (!m_alias_options.empty()); +} + +void +CommandInterpreter::BuildAliasCommandArgs (CommandObject *alias_cmd_obj, + const char *alias_name, + Args &cmd_args, + std::string &raw_input_string, + CommandReturnObject &result) +{ + OptionArgVectorSP option_arg_vector_sp = GetAliasOptions (alias_name); + + bool wants_raw_input = alias_cmd_obj->WantsRawCommandString(); + + // Make sure that the alias name is the 0th element in cmd_args + std::string alias_name_str = alias_name; + if (alias_name_str.compare (cmd_args.GetArgumentAtIndex(0)) != 0) + cmd_args.Unshift (alias_name); + + Args new_args (alias_cmd_obj->GetCommandName()); + if (new_args.GetArgumentCount() == 2) + new_args.Shift(); + + if (option_arg_vector_sp.get()) + { + if (wants_raw_input) + { + // We have a command that both has command options and takes raw input. Make *sure* it has a + // " -- " in the right place in the raw_input_string. + size_t pos = raw_input_string.find(" -- "); + if (pos == std::string::npos) + { + // None found; assume it goes at the beginning of the raw input string + raw_input_string.insert (0, " -- "); + } + } + + OptionArgVector *option_arg_vector = option_arg_vector_sp.get(); + const size_t old_size = cmd_args.GetArgumentCount(); + std::vector used (old_size + 1, false); + + used[0] = true; + + for (size_t i = 0; i < option_arg_vector->size(); ++i) + { + OptionArgPair option_pair = (*option_arg_vector)[i]; + OptionArgValue value_pair = option_pair.second; + int value_type = value_pair.first; + std::string option = option_pair.first; + std::string value = value_pair.second; + if (option.compare ("") == 0) + { + if (!wants_raw_input + || (value.compare("--") != 0)) // Since we inserted this above, make sure we don't insert it twice + new_args.AppendArgument (value.c_str()); + } + else + { + if (value_type != optional_argument) + new_args.AppendArgument (option.c_str()); + if (value.compare ("") != 0) + { + int index = GetOptionArgumentPosition (value.c_str()); + if (index == 0) + { + // value was NOT a positional argument; must be a real value + if (value_type != optional_argument) + new_args.AppendArgument (value.c_str()); + else + { + char buffer[255]; + ::snprintf (buffer, sizeof (buffer), "%s%s", option.c_str(), value.c_str()); + new_args.AppendArgument (buffer); + } + + } + else if (index >= cmd_args.GetArgumentCount()) + { + result.AppendErrorWithFormat + ("Not enough arguments provided; you need at least %d arguments to use this alias.\n", + index); + result.SetStatus (eReturnStatusFailed); + return; + } + else + { + // Find and remove cmd_args.GetArgumentAtIndex(i) from raw_input_string + size_t strpos = raw_input_string.find (cmd_args.GetArgumentAtIndex (index)); + if (strpos != std::string::npos) + { + raw_input_string = raw_input_string.erase (strpos, strlen (cmd_args.GetArgumentAtIndex (index))); + } + + if (value_type != optional_argument) + new_args.AppendArgument (cmd_args.GetArgumentAtIndex (index)); + else + { + char buffer[255]; + ::snprintf (buffer, sizeof(buffer), "%s%s", option.c_str(), + cmd_args.GetArgumentAtIndex (index)); + new_args.AppendArgument (buffer); + } + used[index] = true; + } + } + } + } + + for (size_t j = 0; j < cmd_args.GetArgumentCount(); ++j) + { + if (!used[j] && !wants_raw_input) + new_args.AppendArgument (cmd_args.GetArgumentAtIndex (j)); + } + + cmd_args.Clear(); + cmd_args.SetArguments (new_args.GetArgumentCount(), (const char **) new_args.GetArgumentVector()); + } + else + { + result.SetStatus (eReturnStatusSuccessFinishNoResult); + // This alias was not created with any options; nothing further needs to be done, unless it is a command that + // wants raw input, in which case we need to clear the rest of the data from cmd_args, since its in the raw + // input string. + if (wants_raw_input) + { + cmd_args.Clear(); + cmd_args.SetArguments (new_args.GetArgumentCount(), (const char **) new_args.GetArgumentVector()); + } + return; + } + + result.SetStatus (eReturnStatusSuccessFinishNoResult); + return; +} + + +int +CommandInterpreter::GetOptionArgumentPosition (const char *in_string) +{ + int position = 0; // Any string that isn't an argument position, i.e. '%' followed by an integer, gets a position + // of zero. + + char *cptr = (char *) in_string; + + // Does it start with '%' + if (cptr[0] == '%') + { + ++cptr; + + // Is the rest of it entirely digits? + if (isdigit (cptr[0])) + { + const char *start = cptr; + while (isdigit (cptr[0])) + ++cptr; + + // We've gotten to the end of the digits; are we at the end of the string? + if (cptr[0] == '\0') + position = atoi (start); + } + } + + return position; +} + +void +CommandInterpreter::SourceInitFile (bool in_cwd, CommandReturnObject &result) +{ + FileSpec init_file; + if (in_cwd) + { + // In the current working directory we don't load any program specific + // .lldbinit files, we only look for a "./.lldbinit" file. + if (m_skip_lldbinit_files) + return; + + init_file.SetFile ("./.lldbinit", true); + } + else + { + // If we aren't looking in the current working directory we are looking + // in the home directory. We will first see if there is an application + // specific ".lldbinit" file whose name is "~/.lldbinit" followed by a + // "-" and the name of the program. If this file doesn't exist, we fall + // back to just the "~/.lldbinit" file. We also obey any requests to not + // load the init files. + const char *init_file_path = "~/.lldbinit"; + + if (m_skip_app_init_files == false) + { + FileSpec program_file_spec (Host::GetProgramFileSpec()); + const char *program_name = program_file_spec.GetFilename().AsCString(); + + if (program_name) + { + char program_init_file_name[PATH_MAX]; + ::snprintf (program_init_file_name, sizeof(program_init_file_name), "%s-%s", init_file_path, program_name); + init_file.SetFile (program_init_file_name, true); + if (!init_file.Exists()) + init_file.Clear(); + } + } + + if (!init_file && !m_skip_lldbinit_files) + init_file.SetFile (init_file_path, true); + } + + // If the file exists, tell HandleCommand to 'source' it; this will do the actual broadcasting + // of the commands back to any appropriate listener (see CommandObjectSource::Execute for more details). + + if (init_file.Exists()) + { + ExecutionContext *exe_ctx = NULL; // We don't have any context yet. + bool stop_on_continue = true; + bool stop_on_error = false; + bool echo_commands = false; + bool print_results = false; + + HandleCommandsFromFile (init_file, exe_ctx, stop_on_continue, stop_on_error, echo_commands, print_results, eLazyBoolNo, result); + } + else + { + // nothing to be done if the file doesn't exist + result.SetStatus(eReturnStatusSuccessFinishNoResult); + } +} + +PlatformSP +CommandInterpreter::GetPlatform (bool prefer_target_platform) +{ + PlatformSP platform_sp; + if (prefer_target_platform) + { + ExecutionContext exe_ctx(GetExecutionContext()); + Target *target = exe_ctx.GetTargetPtr(); + if (target) + platform_sp = target->GetPlatform(); + } + + if (!platform_sp) + platform_sp = m_debugger.GetPlatformList().GetSelectedPlatform(); + return platform_sp; +} + +void +CommandInterpreter::HandleCommands (const StringList &commands, + ExecutionContext *override_context, + bool stop_on_continue, + bool stop_on_error, + bool echo_commands, + bool print_results, + LazyBool add_to_history, + CommandReturnObject &result) +{ + size_t num_lines = commands.GetSize(); + + // If we are going to continue past a "continue" then we need to run the commands synchronously. + // Make sure you reset this value anywhere you return from the function. + + bool old_async_execution = m_debugger.GetAsyncExecution(); + + // If we've been given an execution context, set it at the start, but don't keep resetting it or we will + // cause series of commands that change the context, then do an operation that relies on that context to fail. + + if (override_context != NULL) + UpdateExecutionContext (override_context); + + if (!stop_on_continue) + { + m_debugger.SetAsyncExecution (false); + } + + for (size_t idx = 0; idx < num_lines; idx++) + { + const char *cmd = commands.GetStringAtIndex(idx); + if (cmd[0] == '\0') + continue; + + if (echo_commands) + { + result.AppendMessageWithFormat ("%s %s\n", + GetPrompt(), + cmd); + } + + CommandReturnObject tmp_result; + // If override_context is not NULL, pass no_context_switching = true for + // HandleCommand() since we updated our context already. + + // We might call into a regex or alias command, in which case the add_to_history will get lost. This + // m_command_source_depth dingus is the way we turn off adding to the history in that case, so set it up here. + if (!add_to_history) + m_command_source_depth++; + bool success = HandleCommand(cmd, add_to_history, tmp_result, + NULL, /* override_context */ + true, /* repeat_on_empty_command */ + override_context != NULL /* no_context_switching */); + if (!add_to_history) + m_command_source_depth--; + + if (print_results) + { + if (tmp_result.Succeeded()) + result.AppendMessageWithFormat("%s", tmp_result.GetOutputData()); + } + + if (!success || !tmp_result.Succeeded()) + { + const char *error_msg = tmp_result.GetErrorData(); + if (error_msg == NULL || error_msg[0] == '\0') + error_msg = ".\n"; + if (stop_on_error) + { + result.AppendErrorWithFormat("Aborting reading of commands after command #%zu: '%s' failed with %s", + idx, cmd, error_msg); + result.SetStatus (eReturnStatusFailed); + m_debugger.SetAsyncExecution (old_async_execution); + return; + } + else if (print_results) + { + result.AppendMessageWithFormat ("Command #%zu '%s' failed with %s", + idx + 1, + cmd, + error_msg); + } + } + + if (result.GetImmediateOutputStream()) + result.GetImmediateOutputStream()->Flush(); + + if (result.GetImmediateErrorStream()) + result.GetImmediateErrorStream()->Flush(); + + // N.B. Can't depend on DidChangeProcessState, because the state coming into the command execution + // could be running (for instance in Breakpoint Commands. + // So we check the return value to see if it is has running in it. + if ((tmp_result.GetStatus() == eReturnStatusSuccessContinuingNoResult) + || (tmp_result.GetStatus() == eReturnStatusSuccessContinuingResult)) + { + if (stop_on_continue) + { + // If we caused the target to proceed, and we're going to stop in that case, set the + // status in our real result before returning. This is an error if the continue was not the + // last command in the set of commands to be run. + if (idx != num_lines - 1) + result.AppendErrorWithFormat("Aborting reading of commands after command #%zu: '%s' continued the target.\n", + idx + 1, cmd); + else + result.AppendMessageWithFormat ("Command #%zu '%s' continued the target.\n", idx + 1, cmd); + + result.SetStatus(tmp_result.GetStatus()); + m_debugger.SetAsyncExecution (old_async_execution); + + return; + } + } + + } + + result.SetStatus (eReturnStatusSuccessFinishResult); + m_debugger.SetAsyncExecution (old_async_execution); + + return; +} + +void +CommandInterpreter::HandleCommandsFromFile (FileSpec &cmd_file, + ExecutionContext *context, + bool stop_on_continue, + bool stop_on_error, + bool echo_command, + bool print_result, + LazyBool add_to_history, + CommandReturnObject &result) +{ + if (cmd_file.Exists()) + { + bool success; + StringList commands; + success = commands.ReadFileLines(cmd_file); + if (!success) + { + result.AppendErrorWithFormat ("Error reading commands from file: %s.\n", cmd_file.GetFilename().AsCString()); + result.SetStatus (eReturnStatusFailed); + return; + } + m_command_source_depth++; + HandleCommands (commands, context, stop_on_continue, stop_on_error, echo_command, print_result, add_to_history, result); + m_command_source_depth--; + } + else + { + result.AppendErrorWithFormat ("Error reading commands from file %s - file not found.\n", + cmd_file.GetFilename().AsCString()); + result.SetStatus (eReturnStatusFailed); + return; + } +} + +ScriptInterpreter * +CommandInterpreter::GetScriptInterpreter (bool can_create) +{ + if (m_script_interpreter_ap.get() != NULL) + return m_script_interpreter_ap.get(); + + if (!can_create) + return NULL; + + // + // we need to protect the initialization of the script interpreter + // otherwise we could end up with two threads both trying to create + // their instance of it, and for some languages (e.g. Python) + // this is a bulletproof recipe for disaster! + // this needs to be a function-level static because multiple Debugger instances living in the same process + // still need to be isolated and not try to initialize Python concurrently + static Mutex g_interpreter_mutex(Mutex::eMutexTypeRecursive); + Mutex::Locker interpreter_lock(g_interpreter_mutex); + + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_OBJECT)); + if (log) + log->Printf("Initializing the ScriptInterpreter now\n"); + + lldb::ScriptLanguage script_lang = GetDebugger().GetScriptLanguage(); + switch (script_lang) + { + case eScriptLanguagePython: +#ifndef LLDB_DISABLE_PYTHON + m_script_interpreter_ap.reset (new ScriptInterpreterPython (*this)); + break; +#else + // Fall through to the None case when python is disabled +#endif + case eScriptLanguageNone: + m_script_interpreter_ap.reset (new ScriptInterpreterNone (*this)); + break; + }; + + return m_script_interpreter_ap.get(); +} + + + +bool +CommandInterpreter::GetSynchronous () +{ + return m_synchronous_execution; +} + +void +CommandInterpreter::SetSynchronous (bool value) +{ + m_synchronous_execution = value; +} + +void +CommandInterpreter::OutputFormattedHelpText (Stream &strm, + const char *word_text, + const char *separator, + const char *help_text, + size_t max_word_len) +{ + const uint32_t max_columns = m_debugger.GetTerminalWidth(); + + int indent_size = max_word_len + strlen (separator) + 2; + + strm.IndentMore (indent_size); + + StreamString text_strm; + text_strm.Printf ("%-*s %s %s", (int)max_word_len, word_text, separator, help_text); + + size_t len = text_strm.GetSize(); + const char *text = text_strm.GetData(); + if (text[len - 1] == '\n') + { + text_strm.EOL(); + len = text_strm.GetSize(); + } + + if (len < max_columns) + { + // Output it as a single line. + strm.Printf ("%s", text); + } + else + { + // We need to break it up into multiple lines. + bool first_line = true; + int text_width; + size_t start = 0; + size_t end = start; + const size_t final_end = strlen (text); + + while (end < final_end) + { + if (first_line) + text_width = max_columns - 1; + else + text_width = max_columns - indent_size - 1; + + // Don't start the 'text' on a space, since we're already outputting the indentation. + if (!first_line) + { + while ((start < final_end) && (text[start] == ' ')) + start++; + } + + end = start + text_width; + if (end > final_end) + end = final_end; + else + { + // If we're not at the end of the text, make sure we break the line on white space. + while (end > start + && text[end] != ' ' && text[end] != '\t' && text[end] != '\n') + end--; + assert (end > 0); + } + + const size_t sub_len = end - start; + if (start != 0) + strm.EOL(); + if (!first_line) + strm.Indent(); + else + first_line = false; + assert (start <= final_end); + assert (start + sub_len <= final_end); + if (sub_len > 0) + strm.Write (text + start, sub_len); + start = end + 1; + } + } + strm.EOL(); + strm.IndentLess(indent_size); +} + +void +CommandInterpreter::OutputHelpText (Stream &strm, + const char *word_text, + const char *separator, + const char *help_text, + uint32_t max_word_len) +{ + int indent_size = max_word_len + strlen (separator) + 2; + + strm.IndentMore (indent_size); + + StreamString text_strm; + text_strm.Printf ("%-*s %s %s", max_word_len, word_text, separator, help_text); + + const uint32_t max_columns = m_debugger.GetTerminalWidth(); + + size_t len = text_strm.GetSize(); + const char *text = text_strm.GetData(); + + uint32_t chars_left = max_columns; + + for (uint32_t i = 0; i < len; i++) + { + if ((text[i] == ' ' && ::strchr((text+i+1), ' ') && chars_left < ::strchr((text+i+1), ' ')-(text+i)) || text[i] == '\n') + { + chars_left = max_columns - indent_size; + strm.EOL(); + strm.Indent(); + } + else + { + strm.PutChar(text[i]); + chars_left--; + } + + } + + strm.EOL(); + strm.IndentLess(indent_size); +} + +void +CommandInterpreter::FindCommandsForApropos (const char *search_word, StringList &commands_found, + StringList &commands_help, bool search_builtin_commands, bool search_user_commands) +{ + CommandObject::CommandMap::const_iterator pos; + + if (search_builtin_commands) + { + for (pos = m_command_dict.begin(); pos != m_command_dict.end(); ++pos) + { + const char *command_name = pos->first.c_str(); + CommandObject *cmd_obj = pos->second.get(); + + if (cmd_obj->HelpTextContainsWord (search_word)) + { + commands_found.AppendString (command_name); + commands_help.AppendString (cmd_obj->GetHelp()); + } + + if (cmd_obj->IsMultiwordObject()) + cmd_obj->AproposAllSubCommands (command_name, + search_word, + commands_found, + commands_help); + + } + } + + if (search_user_commands) + { + for (pos = m_user_dict.begin(); pos != m_user_dict.end(); ++pos) + { + const char *command_name = pos->first.c_str(); + CommandObject *cmd_obj = pos->second.get(); + + if (cmd_obj->HelpTextContainsWord (search_word)) + { + commands_found.AppendString (command_name); + commands_help.AppendString (cmd_obj->GetHelp()); + } + + if (cmd_obj->IsMultiwordObject()) + cmd_obj->AproposAllSubCommands (command_name, + search_word, + commands_found, + commands_help); + + } + } +} + + +void +CommandInterpreter::UpdateExecutionContext (ExecutionContext *override_context) +{ + if (override_context != NULL) + { + m_exe_ctx_ref = *override_context; + } + else + { + const bool adopt_selected = true; + m_exe_ctx_ref.SetTargetPtr (m_debugger.GetSelectedTarget().get(), adopt_selected); + } +} diff --git a/contrib/llvm/tools/lldb/source/Interpreter/CommandObject.cpp b/contrib/llvm/tools/lldb/source/Interpreter/CommandObject.cpp new file mode 100644 index 00000000000..291dc401357 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Interpreter/CommandObject.cpp @@ -0,0 +1,1174 @@ +//===-- CommandObject.cpp ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "lldb/Interpreter/CommandObject.h" + +#include +#include + +#include +#include +#include + +#include "lldb/Core/Address.h" +#include "lldb/Core/ArchSpec.h" +#include "lldb/Interpreter/Options.h" + +// These are for the Sourcename completers. +// FIXME: Make a separate file for the completers. +#include "lldb/Host/FileSpec.h" +#include "lldb/Core/FileSpecList.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Target.h" + +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Interpreter/CommandReturnObject.h" +#include "lldb/Interpreter/ScriptInterpreter.h" +#include "lldb/Interpreter/ScriptInterpreterPython.h" + +using namespace lldb; +using namespace lldb_private; + +//------------------------------------------------------------------------- +// CommandObject +//------------------------------------------------------------------------- + +CommandObject::CommandObject +( + CommandInterpreter &interpreter, + const char *name, + const char *help, + const char *syntax, + uint32_t flags +) : + m_interpreter (interpreter), + m_cmd_name (name), + m_cmd_help_short (), + m_cmd_help_long (), + m_cmd_syntax (), + m_is_alias (false), + m_flags (flags), + m_arguments(), + m_command_override_callback (NULL), + m_command_override_baton (NULL) +{ + if (help && help[0]) + m_cmd_help_short = help; + if (syntax && syntax[0]) + m_cmd_syntax = syntax; +} + +CommandObject::~CommandObject () +{ +} + +const char * +CommandObject::GetHelp () +{ + return m_cmd_help_short.c_str(); +} + +const char * +CommandObject::GetHelpLong () +{ + return m_cmd_help_long.c_str(); +} + +const char * +CommandObject::GetSyntax () +{ + if (m_cmd_syntax.length() == 0) + { + StreamString syntax_str; + syntax_str.Printf ("%s", GetCommandName()); + if (GetOptions() != NULL) + syntax_str.Printf (" "); + if (m_arguments.size() > 0) + { + syntax_str.Printf (" "); + if (WantsRawCommandString() && GetOptions() && GetOptions()->NumCommandOptions()) + syntax_str.Printf("-- "); + GetFormattedCommandArguments (syntax_str); + } + m_cmd_syntax = syntax_str.GetData (); + } + + return m_cmd_syntax.c_str(); +} + +const char * +CommandObject::GetCommandName () +{ + return m_cmd_name.c_str(); +} + +void +CommandObject::SetCommandName (const char *name) +{ + m_cmd_name = name; +} + +void +CommandObject::SetHelp (const char *cstr) +{ + m_cmd_help_short = cstr; +} + +void +CommandObject::SetHelpLong (const char *cstr) +{ + m_cmd_help_long = cstr; +} + +void +CommandObject::SetHelpLong (std::string str) +{ + m_cmd_help_long = str; +} + +void +CommandObject::SetSyntax (const char *cstr) +{ + m_cmd_syntax = cstr; +} + +Options * +CommandObject::GetOptions () +{ + // By default commands don't have options unless this virtual function + // is overridden by base classes. + return NULL; +} + +bool +CommandObject::ParseOptions +( + Args& args, + CommandReturnObject &result +) +{ + // See if the subclass has options? + Options *options = GetOptions(); + if (options != NULL) + { + Error error; + options->NotifyOptionParsingStarting(); + + // ParseOptions calls getopt_long_only, which always skips the zero'th item in the array and starts at position 1, + // so we need to push a dummy value into position zero. + args.Unshift("dummy_string"); + error = args.ParseOptions (*options); + + // The "dummy_string" will have already been removed by ParseOptions, + // so no need to remove it. + + if (error.Success()) + error = options->NotifyOptionParsingFinished(); + + if (error.Success()) + { + if (options->VerifyOptions (result)) + return true; + } + else + { + const char *error_cstr = error.AsCString(); + if (error_cstr) + { + // We got an error string, lets use that + result.AppendError(error_cstr); + } + else + { + // No error string, output the usage information into result + options->GenerateOptionUsage (result.GetErrorStream(), this); + } + } + result.SetStatus (eReturnStatusFailed); + return false; + } + return true; +} + + + +bool +CommandObject::CheckRequirements (CommandReturnObject &result) +{ +#ifdef LLDB_CONFIGURATION_DEBUG + // Nothing should be stored in m_exe_ctx between running commands as m_exe_ctx + // has shared pointers to the target, process, thread and frame and we don't + // want any CommandObject instances to keep any of these objects around + // longer than for a single command. Every command should call + // CommandObject::Cleanup() after it has completed + assert (m_exe_ctx.GetTargetPtr() == NULL); + assert (m_exe_ctx.GetProcessPtr() == NULL); + assert (m_exe_ctx.GetThreadPtr() == NULL); + assert (m_exe_ctx.GetFramePtr() == NULL); +#endif + + // Lock down the interpreter's execution context prior to running the + // command so we guarantee the selected target, process, thread and frame + // can't go away during the execution + m_exe_ctx = m_interpreter.GetExecutionContext(); + + const uint32_t flags = GetFlags().Get(); + if (flags & (eFlagRequiresTarget | + eFlagRequiresProcess | + eFlagRequiresThread | + eFlagRequiresFrame | + eFlagTryTargetAPILock )) + { + + if ((flags & eFlagRequiresTarget) && !m_exe_ctx.HasTargetScope()) + { + result.AppendError (GetInvalidTargetDescription()); + return false; + } + + if ((flags & eFlagRequiresProcess) && !m_exe_ctx.HasProcessScope()) + { + result.AppendError (GetInvalidProcessDescription()); + return false; + } + + if ((flags & eFlagRequiresThread) && !m_exe_ctx.HasThreadScope()) + { + result.AppendError (GetInvalidThreadDescription()); + return false; + } + + if ((flags & eFlagRequiresFrame) && !m_exe_ctx.HasFrameScope()) + { + result.AppendError (GetInvalidFrameDescription()); + return false; + } + + if ((flags & eFlagRequiresRegContext) && (m_exe_ctx.GetRegisterContext() == NULL)) + { + result.AppendError (GetInvalidRegContextDescription()); + return false; + } + + if (flags & eFlagTryTargetAPILock) + { + Target *target = m_exe_ctx.GetTargetPtr(); + if (target) + { + if (m_api_locker.TryLock (target->GetAPIMutex(), NULL) == false) + { + result.AppendError ("failed to get API lock"); + return false; + } + } + } + } + + if (GetFlags().AnySet (CommandObject::eFlagProcessMustBeLaunched | CommandObject::eFlagProcessMustBePaused)) + { + Process *process = m_interpreter.GetExecutionContext().GetProcessPtr(); + if (process == NULL) + { + // A process that is not running is considered paused. + if (GetFlags().Test(CommandObject::eFlagProcessMustBeLaunched)) + { + result.AppendError ("Process must exist."); + result.SetStatus (eReturnStatusFailed); + return false; + } + } + else + { + StateType state = process->GetState(); + switch (state) + { + case eStateInvalid: + case eStateSuspended: + case eStateCrashed: + case eStateStopped: + break; + + case eStateConnected: + case eStateAttaching: + case eStateLaunching: + case eStateDetached: + case eStateExited: + case eStateUnloaded: + if (GetFlags().Test(CommandObject::eFlagProcessMustBeLaunched)) + { + result.AppendError ("Process must be launched."); + result.SetStatus (eReturnStatusFailed); + return false; + } + break; + + case eStateRunning: + case eStateStepping: + if (GetFlags().Test(CommandObject::eFlagProcessMustBePaused)) + { + result.AppendError ("Process is running. Use 'process interrupt' to pause execution."); + result.SetStatus (eReturnStatusFailed); + return false; + } + } + } + } + return true; +} + +void +CommandObject::Cleanup () +{ + m_exe_ctx.Clear(); + m_api_locker.Unlock(); +} + + +class CommandDictCommandPartialMatch +{ + public: + CommandDictCommandPartialMatch (const char *match_str) + { + m_match_str = match_str; + } + bool operator() (const std::pair map_element) const + { + // A NULL or empty string matches everything. + if (m_match_str == NULL || *m_match_str == '\0') + return true; + + return map_element.first.find (m_match_str, 0) == 0; + } + + private: + const char *m_match_str; +}; + +int +CommandObject::AddNamesMatchingPartialString (CommandObject::CommandMap &in_map, const char *cmd_str, + StringList &matches) +{ + int number_added = 0; + CommandDictCommandPartialMatch matcher(cmd_str); + + CommandObject::CommandMap::iterator matching_cmds = std::find_if (in_map.begin(), in_map.end(), matcher); + + while (matching_cmds != in_map.end()) + { + ++number_added; + matches.AppendString((*matching_cmds).first.c_str()); + matching_cmds = std::find_if (++matching_cmds, in_map.end(), matcher);; + } + return number_added; +} + +int +CommandObject::HandleCompletion +( + Args &input, + int &cursor_index, + int &cursor_char_position, + int match_start_point, + int max_return_elements, + bool &word_complete, + StringList &matches +) +{ + // Default implmentation of WantsCompletion() is !WantsRawCommandString(). + // Subclasses who want raw command string but desire, for example, + // argument completion should override WantsCompletion() to return true, + // instead. + if (WantsRawCommandString() && !WantsCompletion()) + { + // FIXME: Abstract telling the completion to insert the completion character. + matches.Clear(); + return -1; + } + else + { + // Can we do anything generic with the options? + Options *cur_options = GetOptions(); + CommandReturnObject result; + OptionElementVector opt_element_vector; + + if (cur_options != NULL) + { + // Re-insert the dummy command name string which will have been + // stripped off: + input.Unshift ("dummy-string"); + cursor_index++; + + + // I stick an element on the end of the input, because if the last element is + // option that requires an argument, getopt_long_only will freak out. + + input.AppendArgument (""); + + input.ParseArgsForCompletion (*cur_options, opt_element_vector, cursor_index); + + input.DeleteArgumentAtIndex(input.GetArgumentCount() - 1); + + bool handled_by_options; + handled_by_options = cur_options->HandleOptionCompletion (input, + opt_element_vector, + cursor_index, + cursor_char_position, + match_start_point, + max_return_elements, + word_complete, + matches); + if (handled_by_options) + return matches.GetSize(); + } + + // If we got here, the last word is not an option or an option argument. + return HandleArgumentCompletion (input, + cursor_index, + cursor_char_position, + opt_element_vector, + match_start_point, + max_return_elements, + word_complete, + matches); + } +} + +bool +CommandObject::HelpTextContainsWord (const char *search_word) +{ + std::string options_usage_help; + + bool found_word = false; + + const char *short_help = GetHelp(); + const char *long_help = GetHelpLong(); + const char *syntax_help = GetSyntax(); + + if (short_help && strcasestr (short_help, search_word)) + found_word = true; + else if (long_help && strcasestr (long_help, search_word)) + found_word = true; + else if (syntax_help && strcasestr (syntax_help, search_word)) + found_word = true; + + if (!found_word + && GetOptions() != NULL) + { + StreamString usage_help; + GetOptions()->GenerateOptionUsage (usage_help, this); + if (usage_help.GetSize() > 0) + { + const char *usage_text = usage_help.GetData(); + if (strcasestr (usage_text, search_word)) + found_word = true; + } + } + + return found_word; +} + +int +CommandObject::GetNumArgumentEntries () +{ + return m_arguments.size(); +} + +CommandObject::CommandArgumentEntry * +CommandObject::GetArgumentEntryAtIndex (int idx) +{ + if (idx < m_arguments.size()) + return &(m_arguments[idx]); + + return NULL; +} + +CommandObject::ArgumentTableEntry * +CommandObject::FindArgumentDataByType (CommandArgumentType arg_type) +{ + const ArgumentTableEntry *table = CommandObject::GetArgumentTable(); + + for (int i = 0; i < eArgTypeLastArg; ++i) + if (table[i].arg_type == arg_type) + return (ArgumentTableEntry *) &(table[i]); + + return NULL; +} + +void +CommandObject::GetArgumentHelp (Stream &str, CommandArgumentType arg_type, CommandInterpreter &interpreter) +{ + const ArgumentTableEntry* table = CommandObject::GetArgumentTable(); + ArgumentTableEntry *entry = (ArgumentTableEntry *) &(table[arg_type]); + + // The table is *supposed* to be kept in arg_type order, but someone *could* have messed it up... + + if (entry->arg_type != arg_type) + entry = CommandObject::FindArgumentDataByType (arg_type); + + if (!entry) + return; + + StreamString name_str; + name_str.Printf ("<%s>", entry->arg_name); + + if (entry->help_function) + { + const char* help_text = entry->help_function(); + if (!entry->help_function.self_formatting) + { + interpreter.OutputFormattedHelpText (str, name_str.GetData(), "--", help_text, + name_str.GetSize()); + } + else + { + interpreter.OutputHelpText(str, name_str.GetData(), "--", help_text, + name_str.GetSize()); + } + } + else + interpreter.OutputFormattedHelpText (str, name_str.GetData(), "--", entry->help_text, name_str.GetSize()); +} + +const char * +CommandObject::GetArgumentName (CommandArgumentType arg_type) +{ + ArgumentTableEntry *entry = (ArgumentTableEntry *) &(CommandObject::GetArgumentTable()[arg_type]); + + // The table is *supposed* to be kept in arg_type order, but someone *could* have messed it up... + + if (entry->arg_type != arg_type) + entry = CommandObject::FindArgumentDataByType (arg_type); + + if (entry) + return entry->arg_name; + + StreamString str; + str << "Arg name for type (" << arg_type << ") not in arg table!"; + return str.GetData(); +} + +bool +CommandObject::IsPairType (ArgumentRepetitionType arg_repeat_type) +{ + if ((arg_repeat_type == eArgRepeatPairPlain) + || (arg_repeat_type == eArgRepeatPairOptional) + || (arg_repeat_type == eArgRepeatPairPlus) + || (arg_repeat_type == eArgRepeatPairStar) + || (arg_repeat_type == eArgRepeatPairRange) + || (arg_repeat_type == eArgRepeatPairRangeOptional)) + return true; + + return false; +} + +static CommandObject::CommandArgumentEntry +OptSetFiltered(uint32_t opt_set_mask, CommandObject::CommandArgumentEntry &cmd_arg_entry) +{ + CommandObject::CommandArgumentEntry ret_val; + for (unsigned i = 0; i < cmd_arg_entry.size(); ++i) + if (opt_set_mask & cmd_arg_entry[i].arg_opt_set_association) + ret_val.push_back(cmd_arg_entry[i]); + return ret_val; +} + +// Default parameter value of opt_set_mask is LLDB_OPT_SET_ALL, which means take +// all the argument data into account. On rare cases where some argument sticks +// with certain option sets, this function returns the option set filtered args. +void +CommandObject::GetFormattedCommandArguments (Stream &str, uint32_t opt_set_mask) +{ + int num_args = m_arguments.size(); + for (int i = 0; i < num_args; ++i) + { + if (i > 0) + str.Printf (" "); + CommandArgumentEntry arg_entry = + opt_set_mask == LLDB_OPT_SET_ALL ? m_arguments[i] + : OptSetFiltered(opt_set_mask, m_arguments[i]); + int num_alternatives = arg_entry.size(); + + if ((num_alternatives == 2) + && IsPairType (arg_entry[0].arg_repetition)) + { + const char *first_name = GetArgumentName (arg_entry[0].arg_type); + const char *second_name = GetArgumentName (arg_entry[1].arg_type); + switch (arg_entry[0].arg_repetition) + { + case eArgRepeatPairPlain: + str.Printf ("<%s> <%s>", first_name, second_name); + break; + case eArgRepeatPairOptional: + str.Printf ("[<%s> <%s>]", first_name, second_name); + break; + case eArgRepeatPairPlus: + str.Printf ("<%s> <%s> [<%s> <%s> [...]]", first_name, second_name, first_name, second_name); + break; + case eArgRepeatPairStar: + str.Printf ("[<%s> <%s> [<%s> <%s> [...]]]", first_name, second_name, first_name, second_name); + break; + case eArgRepeatPairRange: + str.Printf ("<%s_1> <%s_1> ... <%s_n> <%s_n>", first_name, second_name, first_name, second_name); + break; + case eArgRepeatPairRangeOptional: + str.Printf ("[<%s_1> <%s_1> ... <%s_n> <%s_n>]", first_name, second_name, first_name, second_name); + break; + // Explicitly test for all the rest of the cases, so if new types get added we will notice the + // missing case statement(s). + case eArgRepeatPlain: + case eArgRepeatOptional: + case eArgRepeatPlus: + case eArgRepeatStar: + case eArgRepeatRange: + // These should not be reached, as they should fail the IsPairType test above. + break; + } + } + else + { + StreamString names; + for (int j = 0; j < num_alternatives; ++j) + { + if (j > 0) + names.Printf (" | "); + names.Printf ("%s", GetArgumentName (arg_entry[j].arg_type)); + } + switch (arg_entry[0].arg_repetition) + { + case eArgRepeatPlain: + str.Printf ("<%s>", names.GetData()); + break; + case eArgRepeatPlus: + str.Printf ("<%s> [<%s> [...]]", names.GetData(), names.GetData()); + break; + case eArgRepeatStar: + str.Printf ("[<%s> [<%s> [...]]]", names.GetData(), names.GetData()); + break; + case eArgRepeatOptional: + str.Printf ("[<%s>]", names.GetData()); + break; + case eArgRepeatRange: + str.Printf ("<%s_1> .. <%s_n>", names.GetData(), names.GetData()); + break; + // Explicitly test for all the rest of the cases, so if new types get added we will notice the + // missing case statement(s). + case eArgRepeatPairPlain: + case eArgRepeatPairOptional: + case eArgRepeatPairPlus: + case eArgRepeatPairStar: + case eArgRepeatPairRange: + case eArgRepeatPairRangeOptional: + // These should not be hit, as they should pass the IsPairType test above, and control should + // have gone into the other branch of the if statement. + break; + } + } + } +} + +CommandArgumentType +CommandObject::LookupArgumentName (const char *arg_name) +{ + CommandArgumentType return_type = eArgTypeLastArg; + + std::string arg_name_str (arg_name); + size_t len = arg_name_str.length(); + if (arg_name[0] == '<' + && arg_name[len-1] == '>') + arg_name_str = arg_name_str.substr (1, len-2); + + const ArgumentTableEntry *table = GetArgumentTable(); + for (int i = 0; i < eArgTypeLastArg; ++i) + if (arg_name_str.compare (table[i].arg_name) == 0) + return_type = g_arguments_data[i].arg_type; + + return return_type; +} + +static const char * +RegisterNameHelpTextCallback () +{ + return "Register names can be specified using the architecture specific names. " + "They can also be specified using generic names. Not all generic entities have " + "registers backing them on all architectures. When they don't the generic name " + "will return an error.\n" + "The generic names defined in lldb are:\n" + "\n" + "pc - program counter register\n" + "ra - return address register\n" + "fp - frame pointer register\n" + "sp - stack pointer register\n" + "flags - the flags register\n" + "arg{1-6} - integer argument passing registers.\n"; +} + +static const char * +BreakpointIDHelpTextCallback () +{ + return "Breakpoint ID's consist major and minor numbers; the major number " + "corresponds to the single entity that was created with a 'breakpoint set' " + "command; the minor numbers correspond to all the locations that were actually " + "found/set based on the major breakpoint. A full breakpoint ID might look like " + "3.14, meaning the 14th location set for the 3rd breakpoint. You can specify " + "all the locations of a breakpoint by just indicating the major breakpoint " + "number. A valid breakpoint id consists either of just the major id number, " + "or the major number, a dot, and the location number (e.g. 3 or 3.2 could " + "both be valid breakpoint ids)."; +} + +static const char * +BreakpointIDRangeHelpTextCallback () +{ + return "A 'breakpoint id list' is a manner of specifying multiple breakpoints. " + "This can be done through several mechanisms. The easiest way is to just " + "enter a space-separated list of breakpoint ids. To specify all the " + "breakpoint locations under a major breakpoint, you can use the major " + "breakpoint number followed by '.*', eg. '5.*' means all the locations under " + "breakpoint 5. You can also indicate a range of breakpoints by using " + " - . The start-bp-id and end-bp-id for a range can " + "be any valid breakpoint ids. It is not legal, however, to specify a range " + "using specific locations that cross major breakpoint numbers. I.e. 3.2 - 3.7" + " is legal; 2 - 5 is legal; but 3.2 - 4.4 is not legal."; +} + +static const char * +GDBFormatHelpTextCallback () +{ + return "A GDB format consists of a repeat count, a format letter and a size letter. " + "The repeat count is optional and defaults to 1. The format letter is optional " + "and defaults to the previous format that was used. The size letter is optional " + "and defaults to the previous size that was used.\n" + "\n" + "Format letters include:\n" + "o - octal\n" + "x - hexadecimal\n" + "d - decimal\n" + "u - unsigned decimal\n" + "t - binary\n" + "f - float\n" + "a - address\n" + "i - instruction\n" + "c - char\n" + "s - string\n" + "T - OSType\n" + "A - float as hex\n" + "\n" + "Size letters include:\n" + "b - 1 byte (byte)\n" + "h - 2 bytes (halfword)\n" + "w - 4 bytes (word)\n" + "g - 8 bytes (giant)\n" + "\n" + "Example formats:\n" + "32xb - show 32 1 byte hexadecimal integer values\n" + "16xh - show 16 2 byte hexadecimal integer values\n" + "64 - show 64 2 byte hexadecimal integer values (format and size from the last format)\n" + "dw - show 1 4 byte decimal integer value\n" + ; +} + +static const char * +FormatHelpTextCallback () +{ + + static char* help_text_ptr = NULL; + + if (help_text_ptr) + return help_text_ptr; + + StreamString sstr; + sstr << "One of the format names (or one-character names) that can be used to show a variable's value:\n"; + for (Format f = eFormatDefault; f < kNumFormats; f = Format(f+1)) + { + if (f != eFormatDefault) + sstr.PutChar('\n'); + + char format_char = FormatManager::GetFormatAsFormatChar(f); + if (format_char) + sstr.Printf("'%c' or ", format_char); + + sstr.Printf ("\"%s\"", FormatManager::GetFormatAsCString(f)); + } + + sstr.Flush(); + + std::string data = sstr.GetString(); + + help_text_ptr = new char[data.length()+1]; + + data.copy(help_text_ptr, data.length()); + + return help_text_ptr; +} + +static const char * +LanguageTypeHelpTextCallback () +{ + static char* help_text_ptr = NULL; + + if (help_text_ptr) + return help_text_ptr; + + StreamString sstr; + sstr << "One of the following languages:\n"; + + for (unsigned int l = eLanguageTypeUnknown; l < eNumLanguageTypes; ++l) + { + sstr << " " << LanguageRuntime::GetNameForLanguageType(static_cast(l)) << "\n"; + } + + sstr.Flush(); + + std::string data = sstr.GetString(); + + help_text_ptr = new char[data.length()+1]; + + data.copy(help_text_ptr, data.length()); + + return help_text_ptr; +} + +static const char * +SummaryStringHelpTextCallback() +{ + return + "A summary string is a way to extract information from variables in order to present them using a summary.\n" + "Summary strings contain static text, variables, scopes and control sequences:\n" + " - Static text can be any sequence of non-special characters, i.e. anything but '{', '}', '$', or '\\'.\n" + " - Variables are sequences of characters beginning with ${, ending with } and that contain symbols in the format described below.\n" + " - Scopes are any sequence of text between { and }. Anything included in a scope will only appear in the output summary if there were no errors.\n" + " - Control sequences are the usual C/C++ '\\a', '\\n', ..., plus '\\$', '\\{' and '\\}'.\n" + "A summary string works by copying static text verbatim, turning control sequences into their character counterpart, expanding variables and trying to expand scopes.\n" + "A variable is expanded by giving it a value other than its textual representation, and the way this is done depends on what comes after the ${ marker.\n" + "The most common sequence if ${var followed by an expression path, which is the text one would type to access a member of an aggregate types, given a variable of that type" + " (e.g. if type T has a member named x, which has a member named y, and if t is of type T, the expression path would be .x.y and the way to fit that into a summary string would be" + " ${var.x.y}). You can also use ${*var followed by an expression path and in that case the object referred by the path will be dereferenced before being displayed." + " If the object is not a pointer, doing so will cause an error. For additional details on expression paths, you can type 'help expr-path'. \n" + "By default, summary strings attempt to display the summary for any variable they reference, and if that fails the value. If neither can be shown, nothing is displayed." + "In a summary string, you can also use an array index [n], or a slice-like range [n-m]. This can have two different meanings depending on what kind of object the expression" + " path refers to:\n" + " - if it is a scalar type (any basic type like int, float, ...) the expression is a bitfield, i.e. the bits indicated by the indexing operator are extracted out of the number" + " and displayed as an individual variable\n" + " - if it is an array or pointer the array items indicated by the indexing operator are shown as the result of the variable. if the expression is an array, real array items are" + " printed; if it is a pointer, the pointer-as-array syntax is used to obtain the values (this means, the latter case can have no range checking)\n" + "If you are trying to display an array for which the size is known, you can also use [] instead of giving an exact range. This has the effect of showing items 0 thru size - 1.\n" + "Additionally, a variable can contain an (optional) format code, as in ${var.x.y%code}, where code can be any of the valid formats described in 'help format', or one of the" + " special symbols only allowed as part of a variable:\n" + " %V: show the value of the object by default\n" + " %S: show the summary of the object by default\n" + " %@: show the runtime-provided object description (for Objective-C, it calls NSPrintForDebugger; for C/C++ it does nothing)\n" + " %L: show the location of the object (memory address or a register name)\n" + " %#: show the number of children of the object\n" + " %T: show the type of the object\n" + "Another variable that you can use in summary strings is ${svar . This sequence works exactly like ${var, including the fact that ${*svar is an allowed sequence, but uses" + " the object's synthetic children provider instead of the actual objects. For instance, if you are using STL synthetic children providers, the following summary string would" + " count the number of actual elements stored in an std::list:\n" + "type summary add -s \"${svar%#}\" -x \"std::list<\""; +} + +static const char * +ExprPathHelpTextCallback() +{ + return + "An expression path is the sequence of symbols that is used in C/C++ to access a member variable of an aggregate object (class).\n" + "For instance, given a class:\n" + " class foo {\n" + " int a;\n" + " int b; .\n" + " foo* next;\n" + " };\n" + "the expression to read item b in the item pointed to by next for foo aFoo would be aFoo.next->b.\n" + "Given that aFoo could just be any object of type foo, the string '.next->b' is the expression path, because it can be attached to any foo instance to achieve the effect.\n" + "Expression paths in LLDB include dot (.) and arrow (->) operators, and most commands using expression paths have ways to also accept the star (*) operator.\n" + "The meaning of these operators is the same as the usual one given to them by the C/C++ standards.\n" + "LLDB also has support for indexing ([ ]) in expression paths, and extends the traditional meaning of the square brackets operator to allow bitfield extraction:\n" + "for objects of native types (int, float, char, ...) saying '[n-m]' as an expression path (where n and m are any positive integers, e.g. [3-5]) causes LLDB to extract" + " bits n thru m from the value of the variable. If n == m, [n] is also allowed as a shortcut syntax. For arrays and pointers, expression paths can only contain one index" + " and the meaning of the operation is the same as the one defined by C/C++ (item extraction). Some commands extend bitfield-like syntax for arrays and pointers with the" + " meaning of array slicing (taking elements n thru m inside the array or pointed-to memory)."; +} + +void +CommandObject::GenerateHelpText (CommandReturnObject &result) +{ + GenerateHelpText(result.GetOutputStream()); + + result.SetStatus (eReturnStatusSuccessFinishNoResult); +} + +void +CommandObject::GenerateHelpText (Stream &output_strm) +{ + CommandInterpreter& interpreter = GetCommandInterpreter(); + if (GetOptions() != NULL) + { + if (WantsRawCommandString()) + { + std::string help_text (GetHelp()); + help_text.append (" This command takes 'raw' input (no need to quote stuff)."); + interpreter.OutputFormattedHelpText (output_strm, "", "", help_text.c_str(), 1); + } + else + interpreter.OutputFormattedHelpText (output_strm, "", "", GetHelp(), 1); + output_strm.Printf ("\nSyntax: %s\n", GetSyntax()); + GetOptions()->GenerateOptionUsage (output_strm, this); + const char *long_help = GetHelpLong(); + if ((long_help != NULL) + && (strlen (long_help) > 0)) + output_strm.Printf ("\n%s", long_help); + if (WantsRawCommandString() && !WantsCompletion()) + { + // Emit the message about using ' -- ' between the end of the command options and the raw input + // conditionally, i.e., only if the command object does not want completion. + interpreter.OutputFormattedHelpText (output_strm, "", "", + "\nIMPORTANT NOTE: Because this command takes 'raw' input, if you use any command options" + " you must use ' -- ' between the end of the command options and the beginning of the raw input.", 1); + } + else if (GetNumArgumentEntries() > 0 + && GetOptions() + && GetOptions()->NumCommandOptions() > 0) + { + // Also emit a warning about using "--" in case you are using a command that takes options and arguments. + interpreter.OutputFormattedHelpText (output_strm, "", "", + "\nThis command takes options and free-form arguments. If your arguments resemble" + " option specifiers (i.e., they start with a - or --), you must use ' -- ' between" + " the end of the command options and the beginning of the arguments.", 1); + } + } + else if (IsMultiwordObject()) + { + if (WantsRawCommandString()) + { + std::string help_text (GetHelp()); + help_text.append (" This command takes 'raw' input (no need to quote stuff)."); + interpreter.OutputFormattedHelpText (output_strm, "", "", help_text.c_str(), 1); + } + else + interpreter.OutputFormattedHelpText (output_strm, "", "", GetHelp(), 1); + GenerateHelpText (output_strm); + } + else + { + const char *long_help = GetHelpLong(); + if ((long_help != NULL) + && (strlen (long_help) > 0)) + output_strm.Printf ("%s", long_help); + else if (WantsRawCommandString()) + { + std::string help_text (GetHelp()); + help_text.append (" This command takes 'raw' input (no need to quote stuff)."); + interpreter.OutputFormattedHelpText (output_strm, "", "", help_text.c_str(), 1); + } + else + interpreter.OutputFormattedHelpText (output_strm, "", "", GetHelp(), 1); + output_strm.Printf ("\nSyntax: %s\n", GetSyntax()); + } +} + +void +CommandObject::AddIDsArgumentData(CommandArgumentEntry &arg, CommandArgumentType ID, CommandArgumentType IDRange) +{ + CommandArgumentData id_arg; + CommandArgumentData id_range_arg; + + // Create the first variant for the first (and only) argument for this command. + id_arg.arg_type = ID; + id_arg.arg_repetition = eArgRepeatOptional; + + // Create the second variant for the first (and only) argument for this command. + id_range_arg.arg_type = IDRange; + id_range_arg.arg_repetition = eArgRepeatOptional; + + // The first (and only) argument for this command could be either an id or an id_range. + // Push both variants into the entry for the first argument for this command. + arg.push_back(id_arg); + arg.push_back(id_range_arg); +} + +const char * +CommandObject::GetArgumentTypeAsCString (const lldb::CommandArgumentType arg_type) +{ + if (arg_type >=0 && arg_type < eArgTypeLastArg) + return g_arguments_data[arg_type].arg_name; + return NULL; + +} + +const char * +CommandObject::GetArgumentDescriptionAsCString (const lldb::CommandArgumentType arg_type) +{ + if (arg_type >=0 && arg_type < eArgTypeLastArg) + return g_arguments_data[arg_type].help_text; + return NULL; +} + +bool +CommandObjectParsed::Execute (const char *args_string, CommandReturnObject &result) +{ + CommandOverrideCallback command_callback = GetOverrideCallback(); + bool handled = false; + Args cmd_args (args_string); + if (command_callback) + { + Args full_args (GetCommandName ()); + full_args.AppendArguments(cmd_args); + handled = command_callback (GetOverrideCallbackBaton(), full_args.GetConstArgumentVector()); + } + if (!handled) + { + for (size_t i = 0; i < cmd_args.GetArgumentCount(); ++i) + { + const char *tmp_str = cmd_args.GetArgumentAtIndex (i); + if (tmp_str[0] == '`') // back-quote + cmd_args.ReplaceArgumentAtIndex (i, m_interpreter.ProcessEmbeddedScriptCommands (tmp_str)); + } + + if (CheckRequirements(result)) + { + if (ParseOptions (cmd_args, result)) + { + // Call the command-specific version of 'Execute', passing it the already processed arguments. + handled = DoExecute (cmd_args, result); + } + } + + Cleanup(); + } + return handled; +} + +bool +CommandObjectRaw::Execute (const char *args_string, CommandReturnObject &result) +{ + CommandOverrideCallback command_callback = GetOverrideCallback(); + bool handled = false; + if (command_callback) + { + std::string full_command (GetCommandName ()); + full_command += ' '; + full_command += args_string; + const char *argv[2] = { NULL, NULL }; + argv[0] = full_command.c_str(); + handled = command_callback (GetOverrideCallbackBaton(), argv); + } + if (!handled) + { + if (CheckRequirements(result)) + handled = DoExecute (args_string, result); + + Cleanup(); + } + return handled; +} + +static +const char *arch_helper() +{ + static StreamString g_archs_help; + if (g_archs_help.Empty()) + { + StringList archs; + ArchSpec::AutoComplete(NULL, archs); + g_archs_help.Printf("These are the supported architecture names:\n"); + archs.Join("\n", g_archs_help); + } + return g_archs_help.GetData(); +} + +CommandObject::ArgumentTableEntry +CommandObject::g_arguments_data[] = +{ + { eArgTypeAddress, "address", CommandCompletions::eNoCompletion, { NULL, false }, "A valid address in the target program's execution space." }, + { eArgTypeAddressOrExpression, "address-expression", CommandCompletions::eNoCompletion, { NULL, false }, "An expression that resolves to an address." }, + { eArgTypeAliasName, "alias-name", CommandCompletions::eNoCompletion, { NULL, false }, "The name of an abbreviation (alias) for a debugger command." }, + { eArgTypeAliasOptions, "options-for-aliased-command", CommandCompletions::eNoCompletion, { NULL, false }, "Command options to be used as part of an alias (abbreviation) definition. (See 'help commands alias' for more information.)" }, + { eArgTypeArchitecture, "arch", CommandCompletions::eArchitectureCompletion, { arch_helper, true }, "The architecture name, e.g. i386 or x86_64." }, + { eArgTypeBoolean, "boolean", CommandCompletions::eNoCompletion, { NULL, false }, "A Boolean value: 'true' or 'false'" }, + { eArgTypeBreakpointID, "breakpt-id", CommandCompletions::eNoCompletion, { BreakpointIDHelpTextCallback, false }, NULL }, + { eArgTypeBreakpointIDRange, "breakpt-id-list", CommandCompletions::eNoCompletion, { BreakpointIDRangeHelpTextCallback, false }, NULL }, + { eArgTypeByteSize, "byte-size", CommandCompletions::eNoCompletion, { NULL, false }, "Number of bytes to use." }, + { eArgTypeClassName, "class-name", CommandCompletions::eNoCompletion, { NULL, false }, "Then name of a class from the debug information in the program." }, + { eArgTypeCommandName, "cmd-name", CommandCompletions::eNoCompletion, { NULL, false }, "A debugger command (may be multiple words), without any options or arguments." }, + { eArgTypeCount, "count", CommandCompletions::eNoCompletion, { NULL, false }, "An unsigned integer." }, + { eArgTypeDirectoryName, "directory", CommandCompletions::eDiskDirectoryCompletion, { NULL, false }, "A directory name." }, + { eArgTypeDisassemblyFlavor, "disassembly-flavor", CommandCompletions::eNoCompletion, { NULL, false }, "A disassembly flavor recognized by your disassembly plugin. Currently the only valid options are \"att\" and \"intel\" for Intel targets" }, + { eArgTypeEndAddress, "end-address", CommandCompletions::eNoCompletion, { NULL, false }, "Help text goes here." }, + { eArgTypeExpression, "expr", CommandCompletions::eNoCompletion, { NULL, false }, "Help text goes here." }, + { eArgTypeExpressionPath, "expr-path", CommandCompletions::eNoCompletion, { ExprPathHelpTextCallback, true }, NULL }, + { eArgTypeExprFormat, "expression-format", CommandCompletions::eNoCompletion, { NULL, false }, "[ [bool|b] | [bin] | [char|c] | [oct|o] | [dec|i|d|u] | [hex|x] | [float|f] | [cstr|s] ]" }, + { eArgTypeFilename, "filename", CommandCompletions::eDiskFileCompletion, { NULL, false }, "The name of a file (can include path)." }, + { eArgTypeFormat, "format", CommandCompletions::eNoCompletion, { FormatHelpTextCallback, true }, NULL }, + { eArgTypeFrameIndex, "frame-index", CommandCompletions::eNoCompletion, { NULL, false }, "Index into a thread's list of frames." }, + { eArgTypeFullName, "fullname", CommandCompletions::eNoCompletion, { NULL, false }, "Help text goes here." }, + { eArgTypeFunctionName, "function-name", CommandCompletions::eNoCompletion, { NULL, false }, "The name of a function." }, + { eArgTypeFunctionOrSymbol, "function-or-symbol", CommandCompletions::eNoCompletion, { NULL, false }, "The name of a function or symbol." }, + { eArgTypeGDBFormat, "gdb-format", CommandCompletions::eNoCompletion, { GDBFormatHelpTextCallback, true }, NULL }, + { eArgTypeIndex, "index", CommandCompletions::eNoCompletion, { NULL, false }, "An index into a list." }, + { eArgTypeLanguage, "language", CommandCompletions::eNoCompletion, { LanguageTypeHelpTextCallback, true }, NULL }, + { eArgTypeLineNum, "linenum", CommandCompletions::eNoCompletion, { NULL, false }, "Line number in a source file." }, + { eArgTypeLogCategory, "log-category", CommandCompletions::eNoCompletion, { NULL, false }, "The name of a category within a log channel, e.g. all (try \"log list\" to see a list of all channels and their categories." }, + { eArgTypeLogChannel, "log-channel", CommandCompletions::eNoCompletion, { NULL, false }, "The name of a log channel, e.g. process.gdb-remote (try \"log list\" to see a list of all channels and their categories)." }, + { eArgTypeMethod, "method", CommandCompletions::eNoCompletion, { NULL, false }, "A C++ method name." }, + { eArgTypeName, "name", CommandCompletions::eNoCompletion, { NULL, false }, "Help text goes here." }, + { eArgTypeNewPathPrefix, "new-path-prefix", CommandCompletions::eNoCompletion, { NULL, false }, "Help text goes here." }, + { eArgTypeNumLines, "num-lines", CommandCompletions::eNoCompletion, { NULL, false }, "The number of lines to use." }, + { eArgTypeNumberPerLine, "number-per-line", CommandCompletions::eNoCompletion, { NULL, false }, "The number of items per line to display." }, + { eArgTypeOffset, "offset", CommandCompletions::eNoCompletion, { NULL, false }, "Help text goes here." }, + { eArgTypeOldPathPrefix, "old-path-prefix", CommandCompletions::eNoCompletion, { NULL, false }, "Help text goes here." }, + { eArgTypeOneLiner, "one-line-command", CommandCompletions::eNoCompletion, { NULL, false }, "A command that is entered as a single line of text." }, + { eArgTypePid, "pid", CommandCompletions::eNoCompletion, { NULL, false }, "The process ID number." }, + { eArgTypePlugin, "plugin", CommandCompletions::eNoCompletion, { NULL, false }, "Help text goes here." }, + { eArgTypeProcessName, "process-name", CommandCompletions::eNoCompletion, { NULL, false }, "The name of the process." }, + { eArgTypePythonClass, "python-class", CommandCompletions::eNoCompletion, { NULL, false }, "The name of a Python class." }, + { eArgTypePythonFunction, "python-function", CommandCompletions::eNoCompletion, { NULL, false }, "The name of a Python function." }, + { eArgTypePythonScript, "python-script", CommandCompletions::eNoCompletion, { NULL, false }, "Source code written in Python." }, + { eArgTypeQueueName, "queue-name", CommandCompletions::eNoCompletion, { NULL, false }, "The name of the thread queue." }, + { eArgTypeRegisterName, "register-name", CommandCompletions::eNoCompletion, { RegisterNameHelpTextCallback, true }, NULL }, + { eArgTypeRegularExpression, "regular-expression", CommandCompletions::eNoCompletion, { NULL, false }, "A regular expression." }, + { eArgTypeRunArgs, "run-args", CommandCompletions::eNoCompletion, { NULL, false }, "Arguments to be passed to the target program when it starts executing." }, + { eArgTypeRunMode, "run-mode", CommandCompletions::eNoCompletion, { NULL, false }, "Help text goes here." }, + { eArgTypeScriptedCommandSynchronicity, "script-cmd-synchronicity", CommandCompletions::eNoCompletion, { NULL, false }, "The synchronicity to use to run scripted commands with regard to LLDB event system." }, + { eArgTypeScriptLang, "script-language", CommandCompletions::eNoCompletion, { NULL, false }, "The scripting language to be used for script-based commands. Currently only Python is valid." }, + { eArgTypeSearchWord, "search-word", CommandCompletions::eNoCompletion, { NULL, false }, "The word for which you wish to search for information about." }, + { eArgTypeSelector, "selector", CommandCompletions::eNoCompletion, { NULL, false }, "An Objective-C selector name." }, + { eArgTypeSettingIndex, "setting-index", CommandCompletions::eNoCompletion, { NULL, false }, "An index into a settings variable that is an array (try 'settings list' to see all the possible settings variables and their types)." }, + { eArgTypeSettingKey, "setting-key", CommandCompletions::eNoCompletion, { NULL, false }, "A key into a settings variables that is a dictionary (try 'settings list' to see all the possible settings variables and their types)." }, + { eArgTypeSettingPrefix, "setting-prefix", CommandCompletions::eNoCompletion, { NULL, false }, "The name of a settable internal debugger variable up to a dot ('.'), e.g. 'target.process.'" }, + { eArgTypeSettingVariableName, "setting-variable-name", CommandCompletions::eNoCompletion, { NULL, false }, "The name of a settable internal debugger variable. Type 'settings list' to see a complete list of such variables." }, + { eArgTypeShlibName, "shlib-name", CommandCompletions::eNoCompletion, { NULL, false }, "The name of a shared library." }, + { eArgTypeSourceFile, "source-file", CommandCompletions::eSourceFileCompletion, { NULL, false }, "The name of a source file.." }, + { eArgTypeSortOrder, "sort-order", CommandCompletions::eNoCompletion, { NULL, false }, "Specify a sort order when dumping lists." }, + { eArgTypeStartAddress, "start-address", CommandCompletions::eNoCompletion, { NULL, false }, "Help text goes here." }, + { eArgTypeSummaryString, "summary-string", CommandCompletions::eNoCompletion, { SummaryStringHelpTextCallback, true }, NULL }, + { eArgTypeSymbol, "symbol", CommandCompletions::eSymbolCompletion, { NULL, false }, "Any symbol name (function name, variable, argument, etc.)" }, + { eArgTypeThreadID, "thread-id", CommandCompletions::eNoCompletion, { NULL, false }, "Thread ID number." }, + { eArgTypeThreadIndex, "thread-index", CommandCompletions::eNoCompletion, { NULL, false }, "Index into the process' list of threads." }, + { eArgTypeThreadName, "thread-name", CommandCompletions::eNoCompletion, { NULL, false }, "The thread's name." }, + { eArgTypeUnsignedInteger, "unsigned-integer", CommandCompletions::eNoCompletion, { NULL, false }, "An unsigned integer." }, + { eArgTypeUnixSignal, "unix-signal", CommandCompletions::eNoCompletion, { NULL, false }, "A valid Unix signal name or number (e.g. SIGKILL, KILL or 9)." }, + { eArgTypeVarName, "variable-name", CommandCompletions::eNoCompletion, { NULL, false }, "The name of a variable in your program." }, + { eArgTypeValue, "value", CommandCompletions::eNoCompletion, { NULL, false }, "A value could be anything, depending on where and how it is used." }, + { eArgTypeWidth, "width", CommandCompletions::eNoCompletion, { NULL, false }, "Help text goes here." }, + { eArgTypeNone, "none", CommandCompletions::eNoCompletion, { NULL, false }, "No help available for this." }, + { eArgTypePlatform, "platform-name", CommandCompletions::ePlatformPluginCompletion, { NULL, false }, "The name of an installed platform plug-in . Type 'platform list' to see a complete list of installed platforms." }, + { eArgTypeWatchpointID, "watchpt-id", CommandCompletions::eNoCompletion, { NULL, false }, "Watchpoint IDs are positive integers." }, + { eArgTypeWatchpointIDRange, "watchpt-id-list", CommandCompletions::eNoCompletion, { NULL, false }, "For example, '1-3' or '1 to 3'." }, + { eArgTypeWatchType, "watch-type", CommandCompletions::eNoCompletion, { NULL, false }, "Specify the type for a watchpoint." } +}; + +const CommandObject::ArgumentTableEntry* +CommandObject::GetArgumentTable () +{ + // If this assertion fires, then the table above is out of date with the CommandArgumentType enumeration + assert ((sizeof (CommandObject::g_arguments_data) / sizeof (CommandObject::ArgumentTableEntry)) == eArgTypeLastArg); + return CommandObject::g_arguments_data; +} + + diff --git a/contrib/llvm/tools/lldb/source/Interpreter/CommandObjectRegexCommand.cpp b/contrib/llvm/tools/lldb/source/Interpreter/CommandObjectRegexCommand.cpp new file mode 100644 index 00000000000..59cf1f05fb6 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Interpreter/CommandObjectRegexCommand.cpp @@ -0,0 +1,150 @@ +//===-- CommandObjectRegexCommand.cpp ---------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "lldb/Interpreter/CommandObjectRegexCommand.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Interpreter/CommandReturnObject.h" + +using namespace lldb; +using namespace lldb_private; + +//---------------------------------------------------------------------- +// CommandObjectRegexCommand constructor +//---------------------------------------------------------------------- +CommandObjectRegexCommand::CommandObjectRegexCommand +( + CommandInterpreter &interpreter, + const char *name, + const char *help, + const char *syntax, + uint32_t max_matches, + uint32_t completion_type_mask +) : + CommandObjectRaw (interpreter, name, help, syntax), + m_max_matches (max_matches), + m_completion_type_mask (completion_type_mask), + m_entries () +{ +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +CommandObjectRegexCommand::~CommandObjectRegexCommand() +{ +} + + +bool +CommandObjectRegexCommand::DoExecute +( + const char *command, + CommandReturnObject &result +) +{ + if (command) + { + EntryCollection::const_iterator pos, end = m_entries.end(); + for (pos = m_entries.begin(); pos != end; ++pos) + { + RegularExpression::Match regex_match(m_max_matches); + + if (pos->regex.Execute (command, ®ex_match)) + { + std::string new_command(pos->command); + std::string match_str; + char percent_var[8]; + size_t idx, percent_var_idx; + for (uint32_t match_idx=1; match_idx <= m_max_matches; ++match_idx) + { + if (regex_match.GetMatchAtIndex (command, match_idx, match_str)) + { + const int percent_var_len = ::snprintf (percent_var, sizeof(percent_var), "%%%u", match_idx); + for (idx = 0; (percent_var_idx = new_command.find(percent_var, idx)) != std::string::npos; ) + { + new_command.erase(percent_var_idx, percent_var_len); + new_command.insert(percent_var_idx, match_str); + idx += percent_var_idx + match_str.size(); + } + } + } + // Interpret the new command and return this as the result! + if (m_interpreter.GetExpandRegexAliases()) + result.GetOutputStream().Printf("%s\n", new_command.c_str()); + // Pass in true for "no context switching". The command that called us should have set up the context + // appropriately, we shouldn't have to redo that. + return m_interpreter.HandleCommand(new_command.c_str(), eLazyBoolCalculate, result, NULL, true, true); + } + } + result.SetStatus(eReturnStatusFailed); + if (GetSyntax() != NULL) + result.AppendError (GetSyntax()); + else + result.AppendErrorWithFormat ("Command contents '%s' failed to match any regular expression in the '%s' regex command.\n", + command, + m_cmd_name.c_str()); + return false; + } + result.AppendError("empty command passed to regular expression command"); + result.SetStatus(eReturnStatusFailed); + return false; +} + + +bool +CommandObjectRegexCommand::AddRegexCommand (const char *re_cstr, const char *command_cstr) +{ + m_entries.resize(m_entries.size() + 1); + // Only add the regular expression if it compiles + if (m_entries.back().regex.Compile (re_cstr, REG_EXTENDED)) + { + m_entries.back().command.assign (command_cstr); + return true; + } + // The regex didn't compile... + m_entries.pop_back(); + return false; +} + +int +CommandObjectRegexCommand::HandleCompletion (Args &input, + int &cursor_index, + int &cursor_char_position, + int match_start_point, + int max_return_elements, + bool &word_complete, + StringList &matches) +{ + if (m_completion_type_mask) + { + std::string completion_str (input.GetArgumentAtIndex (cursor_index), cursor_char_position); + CommandCompletions::InvokeCommonCompletionCallbacks (m_interpreter, + m_completion_type_mask, + completion_str.c_str(), + match_start_point, + max_return_elements, + NULL, + word_complete, + matches); + return matches.GetSize(); + } + else + { + matches.Clear(); + word_complete = false; + } + return 0; +} diff --git a/contrib/llvm/tools/lldb/source/Interpreter/CommandObjectScript.cpp b/contrib/llvm/tools/lldb/source/Interpreter/CommandObjectScript.cpp new file mode 100644 index 00000000000..aff507d9858 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Interpreter/CommandObjectScript.cpp @@ -0,0 +1,93 @@ +//===-- CommandObjectScript.cpp ---------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "CommandObjectScript.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes + +#include "lldb/Core/Debugger.h" + +#include "lldb/DataFormatters/DataVisualization.h" + +#include "lldb/Interpreter/Args.h" +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Interpreter/CommandReturnObject.h" +#include "lldb/Interpreter/ScriptInterpreter.h" + +using namespace lldb; +using namespace lldb_private; + +//------------------------------------------------------------------------- +// CommandObjectScript +//------------------------------------------------------------------------- + +CommandObjectScript::CommandObjectScript (CommandInterpreter &interpreter, ScriptLanguage script_lang) : + CommandObjectRaw (interpreter, + "script", + "Pass an expression to the script interpreter for evaluation and return the results. Drop into the interactive interpreter if no expression is given.", + "script []") +{ +} + +CommandObjectScript::~CommandObjectScript () +{ +} + +bool +CommandObjectScript::DoExecute +( + const char *command, + CommandReturnObject &result +) +{ +#ifdef LLDB_DISABLE_PYTHON + // if we ever support languages other than Python this simple #ifdef won't work + result.AppendError("your copy of LLDB does not support scripting."); + result.SetStatus (eReturnStatusFailed); + return false; +#else + if (m_interpreter.GetDebugger().GetScriptLanguage() == lldb::eScriptLanguageNone) + { + result.AppendError("the script-lang setting is set to none - scripting not available"); + result.SetStatus (eReturnStatusFailed); + return false; + } + + ScriptInterpreter *script_interpreter = m_interpreter.GetScriptInterpreter (); + + if (script_interpreter == NULL) + { + result.AppendError("no script interpreter"); + result.SetStatus (eReturnStatusFailed); + return false; + } + + DataVisualization::ForceUpdate(); // script might change Python code we use for formatting.. make sure we keep up to date with it + + if (command == NULL || command[0] == '\0') + { + script_interpreter->ExecuteInterpreterLoop (); + result.SetStatus (eReturnStatusSuccessFinishNoResult); + return result.Succeeded(); + } + + // We can do better when reporting the status of one-liner script execution. + if (script_interpreter->ExecuteOneLine (command, &result)) + result.SetStatus(eReturnStatusSuccessFinishNoResult); + else + result.SetStatus(eReturnStatusFailed); + + return result.Succeeded(); +#endif +} diff --git a/contrib/llvm/tools/lldb/source/Interpreter/CommandObjectScript.h b/contrib/llvm/tools/lldb/source/Interpreter/CommandObjectScript.h new file mode 100644 index 00000000000..fd55fc44a46 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Interpreter/CommandObjectScript.h @@ -0,0 +1,42 @@ +//===-- CommandObjectScript.h -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_CommandObjectScript_h_ +#define liblldb_CommandObjectScript_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Interpreter/CommandObject.h" + +namespace lldb_private { + +//------------------------------------------------------------------------- +// CommandObjectScript +//------------------------------------------------------------------------- + +class CommandObjectScript : public CommandObjectRaw +{ +public: + + CommandObjectScript (CommandInterpreter &interpreter, + lldb::ScriptLanguage script_lang); + + virtual + ~CommandObjectScript (); + +protected: + virtual bool + DoExecute (const char *command, CommandReturnObject &result); +}; + +} // namespace lldb_private + +#endif // liblldb_CommandObjectScript_h_ diff --git a/contrib/llvm/tools/lldb/source/Interpreter/CommandReturnObject.cpp b/contrib/llvm/tools/lldb/source/Interpreter/CommandReturnObject.cpp new file mode 100644 index 00000000000..9c63753a23f --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Interpreter/CommandReturnObject.cpp @@ -0,0 +1,219 @@ +//===-- CommandReturnObject.cpp ---------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Interpreter/CommandReturnObject.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/Error.h" +#include "lldb/Core/StreamString.h" + +using namespace lldb; +using namespace lldb_private; + +static void +DumpStringToStreamWithNewline (Stream &strm, const std::string &s, bool add_newline_if_empty) +{ + bool add_newline = false; + if (s.empty()) + { + add_newline = add_newline_if_empty; + } + else + { + // We already checked for empty above, now make sure there is a newline + // in the error, and if there isn't one, add one. + strm.Write(s.c_str(), s.size()); + + const char last_char = *s.rbegin(); + add_newline = last_char != '\n' && last_char != '\r'; + + } + if (add_newline) + strm.EOL(); +} + + +CommandReturnObject::CommandReturnObject () : + m_out_stream (), + m_err_stream (), + m_status (eReturnStatusStarted), + m_did_change_process_state (false) +{ +} + +CommandReturnObject::~CommandReturnObject () +{ +} + +void +CommandReturnObject::AppendErrorWithFormat (const char *format, ...) +{ + if (!format) + return; + va_list args; + va_start (args, format); + StreamString sstrm; + sstrm.PrintfVarArg(format, args); + va_end (args); + + + const std::string &s = sstrm.GetString(); + if (!s.empty()) + { + Stream &error_strm = GetErrorStream(); + error_strm.PutCString ("error: "); + DumpStringToStreamWithNewline (error_strm, s, false); + } +} + +void +CommandReturnObject::AppendMessageWithFormat (const char *format, ...) +{ + if (!format) + return; + va_list args; + va_start (args, format); + StreamString sstrm; + sstrm.PrintfVarArg(format, args); + va_end (args); + + GetOutputStream().Printf("%s", sstrm.GetData()); +} + +void +CommandReturnObject::AppendWarningWithFormat (const char *format, ...) +{ + if (!format) + return; + va_list args; + va_start (args, format); + StreamString sstrm; + sstrm.PrintfVarArg(format, args); + va_end (args); + + GetErrorStream().Printf("warning: %s", sstrm.GetData()); +} + +void +CommandReturnObject::AppendMessage (const char *in_string) +{ + if (!in_string) + return; + GetOutputStream().Printf("%s\n", in_string); +} + +void +CommandReturnObject::AppendWarning (const char *in_string) +{ + if (!in_string || *in_string == '\0') + return; + GetErrorStream().Printf("warning: %s\n", in_string); +} + +// Similar to AppendWarning, but do not prepend 'warning: ' to message, and +// don't append "\n" to the end of it. + +void +CommandReturnObject::AppendRawWarning (const char *in_string) +{ + if (in_string && in_string[0]) + GetErrorStream().PutCString(in_string); +} + +void +CommandReturnObject::AppendError (const char *in_string) +{ + if (!in_string || *in_string == '\0') + return; + GetErrorStream().Printf ("error: %s\n", in_string); +} + +void +CommandReturnObject::SetError (const Error &error, const char *fallback_error_cstr) +{ + const char *error_cstr = error.AsCString(); + if (error_cstr == NULL) + error_cstr = fallback_error_cstr; + SetError(error_cstr); +} + +void +CommandReturnObject::SetError (const char *error_cstr) +{ + if (error_cstr) + { + AppendError (error_cstr); + SetStatus (eReturnStatusFailed); + } +} + +// Similar to AppendError, but do not prepend 'Error: ' to message, and +// don't append "\n" to the end of it. + +void +CommandReturnObject::AppendRawError (const char *in_string) +{ + if (in_string && in_string[0]) + GetErrorStream().PutCString(in_string); +} + +void +CommandReturnObject::SetStatus (ReturnStatus status) +{ + m_status = status; +} + +ReturnStatus +CommandReturnObject::GetStatus () +{ + return m_status; +} + +bool +CommandReturnObject::Succeeded () +{ + return m_status <= eReturnStatusSuccessContinuingResult; +} + +bool +CommandReturnObject::HasResult () +{ + return (m_status == eReturnStatusSuccessFinishResult || + m_status == eReturnStatusSuccessContinuingResult); +} + +void +CommandReturnObject::Clear() +{ + lldb::StreamSP stream_sp; + stream_sp = m_out_stream.GetStreamAtIndex (eStreamStringIndex); + if (stream_sp) + static_cast(stream_sp.get())->Clear(); + stream_sp = m_err_stream.GetStreamAtIndex (eStreamStringIndex); + if (stream_sp) + static_cast(stream_sp.get())->Clear(); + m_status = eReturnStatusStarted; + m_did_change_process_state = false; +} + +bool +CommandReturnObject::GetDidChangeProcessState () +{ + return m_did_change_process_state; +} + +void +CommandReturnObject::SetDidChangeProcessState (bool b) +{ + m_did_change_process_state = b; +} + diff --git a/contrib/llvm/tools/lldb/source/Interpreter/OptionGroupArchitecture.cpp b/contrib/llvm/tools/lldb/source/Interpreter/OptionGroupArchitecture.cpp new file mode 100644 index 00000000000..af103bb0bd9 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Interpreter/OptionGroupArchitecture.cpp @@ -0,0 +1,86 @@ +//===-- OptionGroupArchitecture.cpp -----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Interpreter/OptionGroupArchitecture.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Utility/Utils.h" + +using namespace lldb; +using namespace lldb_private; + +OptionGroupArchitecture::OptionGroupArchitecture() : + m_arch_str () +{ +} + +OptionGroupArchitecture::~OptionGroupArchitecture () +{ +} + +static OptionDefinition +g_option_table[] = +{ + { LLDB_OPT_SET_1 , false, "arch" , 'a', required_argument, NULL, 0, eArgTypeArchitecture , "Specify the architecture for the target."}, +}; + +uint32_t +OptionGroupArchitecture::GetNumDefinitions () +{ + return llvm::array_lengthof(g_option_table); +} + +const OptionDefinition * +OptionGroupArchitecture::GetDefinitions () +{ + return g_option_table; +} + +bool +OptionGroupArchitecture::GetArchitecture (Platform *platform, ArchSpec &arch) +{ + if (m_arch_str.empty()) + arch.Clear(); + else + arch.SetTriple(m_arch_str.c_str(), platform); + return arch.IsValid(); +} + + +Error +OptionGroupArchitecture::SetOptionValue (CommandInterpreter &interpreter, + uint32_t option_idx, + const char *option_arg) +{ + Error error; + const int short_option = g_option_table[option_idx].short_option; + + switch (short_option) + { + case 'a': + m_arch_str.assign (option_arg); + break; + + default: + error.SetErrorStringWithFormat ("unrecognized option '%c'", short_option); + break; + } + + return error; +} + +void +OptionGroupArchitecture::OptionParsingStarting (CommandInterpreter &interpreter) +{ + m_arch_str.clear(); +} + diff --git a/contrib/llvm/tools/lldb/source/Interpreter/OptionGroupBoolean.cpp b/contrib/llvm/tools/lldb/source/Interpreter/OptionGroupBoolean.cpp new file mode 100644 index 00000000000..5b5b38478b0 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Interpreter/OptionGroupBoolean.cpp @@ -0,0 +1,67 @@ +//===-- OptionGroupBoolean.cpp ----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Interpreter/OptionGroupBoolean.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes + +using namespace lldb; +using namespace lldb_private; + +OptionGroupBoolean::OptionGroupBoolean (uint32_t usage_mask, + bool required, + const char *long_option, + int short_option, + const char *usage_text, + bool default_value, + bool no_argument_toggle_default) : + m_value (default_value, default_value) +{ + m_option_definition.usage_mask = usage_mask; + m_option_definition.required = required; + m_option_definition.long_option = long_option; + m_option_definition.short_option = short_option; + m_option_definition.option_has_arg = no_argument_toggle_default ? no_argument : required_argument; + m_option_definition.enum_values = NULL; + m_option_definition.completion_type = 0; + m_option_definition.argument_type = eArgTypeBoolean; + m_option_definition.usage_text = usage_text; +} + +OptionGroupBoolean::~OptionGroupBoolean () +{ +} + +Error +OptionGroupBoolean::SetOptionValue (CommandInterpreter &interpreter, + uint32_t option_idx, + const char *option_arg) +{ + Error error; + if (m_option_definition.option_has_arg == no_argument) + { + // Not argument, toggle the default value and mark the option as having been set + m_value.SetCurrentValue (!m_value.GetDefaultValue()); + m_value.SetOptionWasSet (); + } + else + { + error = m_value.SetValueFromCString (option_arg); + } + return error; +} + +void +OptionGroupBoolean::OptionParsingStarting (CommandInterpreter &interpreter) +{ + m_value.Clear(); +} diff --git a/contrib/llvm/tools/lldb/source/Interpreter/OptionGroupFile.cpp b/contrib/llvm/tools/lldb/source/Interpreter/OptionGroupFile.cpp new file mode 100644 index 00000000000..6867395789c --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Interpreter/OptionGroupFile.cpp @@ -0,0 +1,97 @@ +//===-- OptionGroupFile.cpp -------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Interpreter/OptionGroupFile.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes + +using namespace lldb; +using namespace lldb_private; + +OptionGroupFile::OptionGroupFile (uint32_t usage_mask, + bool required, + const char *long_option, + int short_option, + uint32_t completion_type, + lldb::CommandArgumentType argument_type, + const char *usage_text) : + m_file () +{ + m_option_definition.usage_mask = usage_mask; + m_option_definition.required = required; + m_option_definition.long_option = long_option; + m_option_definition.short_option = short_option; + m_option_definition.option_has_arg = required_argument; + m_option_definition.enum_values = NULL; + m_option_definition.completion_type = completion_type; + m_option_definition.argument_type = argument_type; + m_option_definition.usage_text = usage_text; +} + +OptionGroupFile::~OptionGroupFile () +{ +} + +Error +OptionGroupFile::SetOptionValue (CommandInterpreter &interpreter, + uint32_t option_idx, + const char *option_arg) +{ + Error error (m_file.SetValueFromCString (option_arg)); + return error; +} + +void +OptionGroupFile::OptionParsingStarting (CommandInterpreter &interpreter) +{ + m_file.Clear(); +} + + +OptionGroupFileList::OptionGroupFileList (uint32_t usage_mask, + bool required, + const char *long_option, + int short_option, + uint32_t completion_type, + lldb::CommandArgumentType argument_type, + const char *usage_text) : + m_file_list () +{ + m_option_definition.usage_mask = usage_mask; + m_option_definition.required = required; + m_option_definition.long_option = long_option; + m_option_definition.short_option = short_option; + m_option_definition.option_has_arg = required_argument; + m_option_definition.enum_values = NULL; + m_option_definition.completion_type = completion_type; + m_option_definition.argument_type = argument_type; + m_option_definition.usage_text = usage_text; +} + +OptionGroupFileList::~OptionGroupFileList () +{ +} + +Error +OptionGroupFileList::SetOptionValue (CommandInterpreter &interpreter, + uint32_t option_idx, + const char *option_arg) +{ + Error error (m_file_list.SetValueFromCString (option_arg)); + return error; +} + +void +OptionGroupFileList::OptionParsingStarting (CommandInterpreter &interpreter) +{ + m_file_list.Clear(); +} diff --git a/contrib/llvm/tools/lldb/source/Interpreter/OptionGroupFormat.cpp b/contrib/llvm/tools/lldb/source/Interpreter/OptionGroupFormat.cpp new file mode 100644 index 00000000000..790cbb668fa --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Interpreter/OptionGroupFormat.cpp @@ -0,0 +1,249 @@ +//===-- OptionGroupFormat.cpp -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "lldb/Interpreter/OptionGroupFormat.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/ArchSpec.h" +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/Target.h" +#include "lldb/Utility/Utils.h" + +using namespace lldb; +using namespace lldb_private; + +OptionGroupFormat::OptionGroupFormat (lldb::Format default_format, + uint64_t default_byte_size, + uint64_t default_count) : + m_format (default_format, default_format), + m_byte_size (default_byte_size, default_byte_size), + m_count (default_count, default_count), + m_prev_gdb_format('x'), + m_prev_gdb_size('w') +{ +} + +OptionGroupFormat::~OptionGroupFormat () +{ +} + +static OptionDefinition +g_option_table[] = +{ +{ LLDB_OPT_SET_1, false, "format" ,'f', required_argument, NULL, 0, eArgTypeFormat , "Specify a format to be used for display."}, +{ LLDB_OPT_SET_2, false, "gdb-format",'G', required_argument, NULL, 0, eArgTypeGDBFormat, "Specify a format using a GDB format specifier string."}, +{ LLDB_OPT_SET_3, false, "size" ,'s', required_argument, NULL, 0, eArgTypeByteSize , "The size in bytes to use when displaying with the selected format."}, +{ LLDB_OPT_SET_4, false, "count" ,'c', required_argument, NULL, 0, eArgTypeCount , "The number of total items to display."}, +}; + +uint32_t +OptionGroupFormat::GetNumDefinitions () +{ + if (m_byte_size.GetDefaultValue() < UINT64_MAX) + { + if (m_count.GetDefaultValue() < UINT64_MAX) + return 4; + else + return 3; + } + return 2; +} + +const OptionDefinition * +OptionGroupFormat::GetDefinitions () +{ + return g_option_table; +} + +Error +OptionGroupFormat::SetOptionValue (CommandInterpreter &interpreter, + uint32_t option_idx, + const char *option_arg) +{ + Error error; + const int short_option = g_option_table[option_idx].short_option; + + switch (short_option) + { + case 'f': + error = m_format.SetValueFromCString (option_arg); + break; + + case 'c': + if (m_count.GetDefaultValue() == 0) + { + error.SetErrorString ("--count option is disabled"); + } + else + { + error = m_count.SetValueFromCString (option_arg); + if (m_count.GetCurrentValue() == 0) + error.SetErrorStringWithFormat("invalid --count option value '%s'", option_arg); + } + break; + + case 's': + if (m_byte_size.GetDefaultValue() == 0) + { + error.SetErrorString ("--size option is disabled"); + } + else + { + error = m_byte_size.SetValueFromCString (option_arg); + if (m_byte_size.GetCurrentValue() == 0) + error.SetErrorStringWithFormat("invalid --size option value '%s'", option_arg); + } + break; + + case 'G': + { + char *end = NULL; + const char *gdb_format_cstr = option_arg; + uint64_t count = 0; + if (::isdigit (gdb_format_cstr[0])) + { + count = strtoull (gdb_format_cstr, &end, 0); + + if (option_arg != end) + gdb_format_cstr = end; // We have a valid count, advance the string position + else + count = 0; + } + + Format format = eFormatDefault; + uint32_t byte_size = 0; + + while (ParserGDBFormatLetter (interpreter, gdb_format_cstr[0], format, byte_size)) + { + ++gdb_format_cstr; + } + + // We the first character of the "gdb_format_cstr" is not the + // NULL terminator, we didn't consume the entire string and + // something is wrong. Also, if none of the format, size or count + // was specified correctly, then abort. + if (gdb_format_cstr[0] || (format == eFormatInvalid && byte_size == 0 && count == 0)) + { + // Nothing got set correctly + error.SetErrorStringWithFormat ("invalid gdb format string '%s'", option_arg); + return error; + } + + // At least one of the format, size or count was set correctly. + // Anything that wasn't set correctly should be set to the + // previous default + if (format == eFormatInvalid) + ParserGDBFormatLetter (interpreter, m_prev_gdb_format, format, byte_size); + + const bool byte_size_enabled = m_byte_size.GetDefaultValue() < UINT64_MAX; + const bool count_enabled = m_count.GetDefaultValue() < UINT64_MAX; + if (byte_size_enabled) + { + // Byte size is enabled + if (byte_size == 0) + ParserGDBFormatLetter (interpreter, m_prev_gdb_size, format, byte_size); + } + else + { + // Byte size is disabled, make sure it wasn't specified + if (byte_size > 0) + { + error.SetErrorString ("this command doesn't support specifying a byte size"); + return error; + } + } + + if (count_enabled) + { + // Count is enabled and was not set, set it to the default for gdb format statements (which is 1). + if (count == 0) + count = 1; + } + else + { + // Count is disabled, make sure it wasn't specified + if (count > 0) + { + error.SetErrorString ("this command doesn't support specifying a count"); + return error; + } + } + + m_format.SetCurrentValue (format); + m_format.SetOptionWasSet (); + if (byte_size_enabled) + { + m_byte_size.SetCurrentValue (byte_size); + m_byte_size.SetOptionWasSet (); + } + if (count_enabled) + { + m_count.SetCurrentValue(count); + m_count.SetOptionWasSet (); + } + } + break; + + default: + error.SetErrorStringWithFormat ("unrecognized option '%c'", short_option); + break; + } + + return error; +} + +bool +OptionGroupFormat::ParserGDBFormatLetter (CommandInterpreter &interpreter, char format_letter, Format &format, uint32_t &byte_size) +{ + m_has_gdb_format = true; + switch (format_letter) + { + case 'o': format = eFormatOctal; m_prev_gdb_format = format_letter; return true; + case 'x': format = eFormatHex; m_prev_gdb_format = format_letter; return true; + case 'd': format = eFormatDecimal; m_prev_gdb_format = format_letter; return true; + case 'u': format = eFormatUnsigned; m_prev_gdb_format = format_letter; return true; + case 't': format = eFormatBinary; m_prev_gdb_format = format_letter; return true; + case 'f': format = eFormatFloat; m_prev_gdb_format = format_letter; return true; + case 'a': format = eFormatAddressInfo; + { + ExecutionContext exe_ctx(interpreter.GetExecutionContext()); + Target *target = exe_ctx.GetTargetPtr(); + if (target) + byte_size = target->GetArchitecture().GetAddressByteSize(); + m_prev_gdb_format = format_letter; + return true; + } + case 'i': format = eFormatInstruction; m_prev_gdb_format = format_letter; return true; + case 'c': format = eFormatChar; m_prev_gdb_format = format_letter; return true; + case 's': format = eFormatCString; m_prev_gdb_format = format_letter; return true; + case 'T': format = eFormatOSType; m_prev_gdb_format = format_letter; return true; + case 'A': format = eFormatHexFloat; m_prev_gdb_format = format_letter; return true; + case 'b': byte_size = 1; m_prev_gdb_size = format_letter; return true; + case 'h': byte_size = 2; m_prev_gdb_size = format_letter; return true; + case 'w': byte_size = 4; m_prev_gdb_size = format_letter; return true; + case 'g': byte_size = 8; m_prev_gdb_size = format_letter; return true; + default: break; + } + return false; +} + +void +OptionGroupFormat::OptionParsingStarting (CommandInterpreter &interpreter) +{ + m_format.Clear(); + m_byte_size.Clear(); + m_count.Clear(); + m_has_gdb_format = false; +} diff --git a/contrib/llvm/tools/lldb/source/Interpreter/OptionGroupOutputFile.cpp b/contrib/llvm/tools/lldb/source/Interpreter/OptionGroupOutputFile.cpp new file mode 100644 index 00000000000..aa01bf54964 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Interpreter/OptionGroupOutputFile.cpp @@ -0,0 +1,81 @@ +//===-- OptionGroupOutputFile.cpp -------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Interpreter/OptionGroupOutputFile.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Utility/Utils.h" + +using namespace lldb; +using namespace lldb_private; + +OptionGroupOutputFile::OptionGroupOutputFile() : + m_file (), + m_append (false, false) +{ +} + +OptionGroupOutputFile::~OptionGroupOutputFile () +{ +} + +static OptionDefinition +g_option_table[] = +{ + { LLDB_OPT_SET_1 , false, "outfile", 'o', required_argument, NULL, 0, eArgTypeFilename , "Specify a path for capturing command output."}, + { LLDB_OPT_SET_1 , false, "append-outfile" , 'apnd', no_argument, NULL, 0, eArgTypeNone , "Append to the the file specified with '--outfile '."}, +}; + +uint32_t +OptionGroupOutputFile::GetNumDefinitions () +{ + return llvm::array_lengthof(g_option_table); +} + +const OptionDefinition * +OptionGroupOutputFile::GetDefinitions () +{ + return g_option_table; +} + +Error +OptionGroupOutputFile::SetOptionValue (CommandInterpreter &interpreter, + uint32_t option_idx, + const char *option_arg) +{ + Error error; + const int short_option = g_option_table[option_idx].short_option; + + switch (short_option) + { + case 'o': + error = m_file.SetValueFromCString (option_arg); + break; + + case 'apnd': + m_append.SetCurrentValue(true); + break; + + default: + error.SetErrorStringWithFormat ("unrecognized option '%c'", short_option); + break; + } + + return error; +} + +void +OptionGroupOutputFile::OptionParsingStarting (CommandInterpreter &interpreter) +{ + m_file.Clear(); + m_append.Clear(); +} diff --git a/contrib/llvm/tools/lldb/source/Interpreter/OptionGroupPlatform.cpp b/contrib/llvm/tools/lldb/source/Interpreter/OptionGroupPlatform.cpp new file mode 100644 index 00000000000..a54edafd019 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Interpreter/OptionGroupPlatform.cpp @@ -0,0 +1,149 @@ +//===-- OptionGroupPlatform.cpp ---------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "lldb/Interpreter/OptionGroupPlatform.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Target/Platform.h" +#include "lldb/Utility/Utils.h" + +using namespace lldb; +using namespace lldb_private; + +PlatformSP +OptionGroupPlatform::CreatePlatformWithOptions (CommandInterpreter &interpreter, + const ArchSpec &arch, + bool make_selected, + Error& error, + ArchSpec &platform_arch) const +{ + PlatformSP platform_sp; + + if (!m_platform_name.empty()) + { + platform_sp = Platform::Create (m_platform_name.c_str(), error); + if (platform_sp) + { + if (platform_arch.IsValid() && !platform_sp->IsCompatibleArchitecture(arch, false, &platform_arch)) + { + error.SetErrorStringWithFormat ("platform '%s' doesn't support '%s'", + platform_sp->GetName().GetCString(), + arch.GetTriple().getTriple().c_str()); + platform_sp.reset(); + return platform_sp; + } + } + } + else if (arch.IsValid()) + { + platform_sp = Platform::Create (arch, &platform_arch, error); + } + + if (platform_sp) + { + interpreter.GetDebugger().GetPlatformList().Append (platform_sp, make_selected); + if (m_os_version_major != UINT32_MAX) + { + platform_sp->SetOSVersion (m_os_version_major, + m_os_version_minor, + m_os_version_update); + } + + if (m_sdk_sysroot) + platform_sp->SetSDKRootDirectory (m_sdk_sysroot); + + if (m_sdk_build) + platform_sp->SetSDKBuild (m_sdk_build); + } + + return platform_sp; +} + +void +OptionGroupPlatform::OptionParsingStarting (CommandInterpreter &interpreter) +{ + m_platform_name.clear(); + m_sdk_sysroot.Clear(); + m_sdk_build.Clear(); + m_os_version_major = UINT32_MAX; + m_os_version_minor = UINT32_MAX; + m_os_version_update = UINT32_MAX; +} + +static OptionDefinition +g_option_table[] = +{ + { LLDB_OPT_SET_ALL, false, "platform", 'p', required_argument, NULL, 0, eArgTypePlatform, "Specify name of the platform to use for this target, creating the platform if necessary."}, + { LLDB_OPT_SET_ALL, false, "version" , 'v', required_argument, NULL, 0, eArgTypeNone, "Specify the initial SDK version to use prior to connecting." }, + { LLDB_OPT_SET_ALL, false, "build" , 'b', required_argument, NULL, 0, eArgTypeNone, "Specify the initial SDK build number." }, + { LLDB_OPT_SET_ALL, false, "sysroot" , 'S', required_argument, NULL, 0, eArgTypeFilename, "Specify the SDK root directory that contains a root of all remote system files." } +}; + +const OptionDefinition* +OptionGroupPlatform::GetDefinitions () +{ + if (m_include_platform_option) + return g_option_table; + return g_option_table + 1; +} + +uint32_t +OptionGroupPlatform::GetNumDefinitions () +{ + if (m_include_platform_option) + return llvm::array_lengthof(g_option_table); + return llvm::array_lengthof(g_option_table) - 1; +} + + +Error +OptionGroupPlatform::SetOptionValue (CommandInterpreter &interpreter, + uint32_t option_idx, + const char *option_arg) +{ + Error error; + if (!m_include_platform_option) + ++option_idx; + + const int short_option = g_option_table[option_idx].short_option; + + switch (short_option) + { + case 'p': + m_platform_name.assign (option_arg); + break; + + case 'v': + if (Args::StringToVersion (option_arg, + m_os_version_major, + m_os_version_minor, + m_os_version_update) == option_arg) + error.SetErrorStringWithFormat ("invalid version string '%s'", option_arg); + break; + + case 'b': + m_sdk_build.SetCString (option_arg); + break; + + case 'S': + m_sdk_sysroot.SetCString (option_arg); + break; + + default: + error.SetErrorStringWithFormat ("unrecognized option '%c'", short_option); + break; + } + return error; +} diff --git a/contrib/llvm/tools/lldb/source/Interpreter/OptionGroupString.cpp b/contrib/llvm/tools/lldb/source/Interpreter/OptionGroupString.cpp new file mode 100644 index 00000000000..ee9623967c6 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Interpreter/OptionGroupString.cpp @@ -0,0 +1,58 @@ +//===-- OptionGroupString.cpp ----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Interpreter/OptionGroupString.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes + +using namespace lldb; +using namespace lldb_private; + +OptionGroupString::OptionGroupString (uint32_t usage_mask, + bool required, + const char *long_option, + int short_option, + uint32_t completion_type, + lldb::CommandArgumentType argument_type, + const char *usage_text, + const char *default_value) : + m_value (default_value, default_value) +{ + m_option_definition.usage_mask = usage_mask; + m_option_definition.required = required; + m_option_definition.long_option = long_option; + m_option_definition.short_option = short_option; + m_option_definition.option_has_arg = required_argument; + m_option_definition.enum_values = NULL; + m_option_definition.completion_type = completion_type; + m_option_definition.argument_type = argument_type; + m_option_definition.usage_text = usage_text; +} + +OptionGroupString::~OptionGroupString () +{ +} + +Error +OptionGroupString::SetOptionValue (CommandInterpreter &interpreter, + uint32_t option_idx, + const char *option_arg) +{ + Error error (m_value.SetValueFromCString (option_arg)); + return error; +} + +void +OptionGroupString::OptionParsingStarting (CommandInterpreter &interpreter) +{ + m_value.Clear(); +} diff --git a/contrib/llvm/tools/lldb/source/Interpreter/OptionGroupUInt64.cpp b/contrib/llvm/tools/lldb/source/Interpreter/OptionGroupUInt64.cpp new file mode 100644 index 00000000000..e6996f70255 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Interpreter/OptionGroupUInt64.cpp @@ -0,0 +1,58 @@ +//===-- OptionGroupUInt64.cpp ----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Interpreter/OptionGroupUInt64.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes + +using namespace lldb; +using namespace lldb_private; + +OptionGroupUInt64::OptionGroupUInt64 (uint32_t usage_mask, + bool required, + const char *long_option, + int short_option, + uint32_t completion_type, + lldb::CommandArgumentType argument_type, + const char *usage_text, + uint64_t default_value) : + m_value (default_value, default_value) +{ + m_option_definition.usage_mask = usage_mask; + m_option_definition.required = required; + m_option_definition.long_option = long_option; + m_option_definition.short_option = short_option; + m_option_definition.option_has_arg = required_argument; + m_option_definition.enum_values = NULL; + m_option_definition.completion_type = completion_type; + m_option_definition.argument_type = argument_type; + m_option_definition.usage_text = usage_text; +} + +OptionGroupUInt64::~OptionGroupUInt64 () +{ +} + +Error +OptionGroupUInt64::SetOptionValue (CommandInterpreter &interpreter, + uint32_t option_idx, + const char *option_arg) +{ + Error error (m_value.SetValueFromCString (option_arg)); + return error; +} + +void +OptionGroupUInt64::OptionParsingStarting (CommandInterpreter &interpreter) +{ + m_value.Clear(); +} diff --git a/contrib/llvm/tools/lldb/source/Interpreter/OptionGroupUUID.cpp b/contrib/llvm/tools/lldb/source/Interpreter/OptionGroupUUID.cpp new file mode 100644 index 00000000000..14bdc8494e4 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Interpreter/OptionGroupUUID.cpp @@ -0,0 +1,76 @@ +//===-- OptionGroupUUID.cpp -------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Interpreter/OptionGroupUUID.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Utility/Utils.h" + +using namespace lldb; +using namespace lldb_private; + +OptionGroupUUID::OptionGroupUUID() : + m_uuid () +{ +} + +OptionGroupUUID::~OptionGroupUUID () +{ +} + +static OptionDefinition +g_option_table[] = +{ + { LLDB_OPT_SET_1 , false, "uuid", 'u', required_argument, NULL, 0, eArgTypeNone, "A module UUID value."}, +}; + +uint32_t +OptionGroupUUID::GetNumDefinitions () +{ + return llvm::array_lengthof(g_option_table); +} + +const OptionDefinition * +OptionGroupUUID::GetDefinitions () +{ + return g_option_table; +} + +Error +OptionGroupUUID::SetOptionValue (CommandInterpreter &interpreter, + uint32_t option_idx, + const char *option_arg) +{ + Error error; + const int short_option = g_option_table[option_idx].short_option; + + switch (short_option) + { + case 'u': + error = m_uuid.SetValueFromCString (option_arg); + if (error.Success()) + m_uuid.SetOptionWasSet(); + break; + + default: + error.SetErrorStringWithFormat ("unrecognized option '%c'", short_option); + break; + } + + return error; +} + +void +OptionGroupUUID::OptionParsingStarting (CommandInterpreter &interpreter) +{ + m_uuid.Clear(); +} diff --git a/contrib/llvm/tools/lldb/source/Interpreter/OptionGroupValueObjectDisplay.cpp b/contrib/llvm/tools/lldb/source/Interpreter/OptionGroupValueObjectDisplay.cpp new file mode 100644 index 00000000000..22a7f37740d --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Interpreter/OptionGroupValueObjectDisplay.cpp @@ -0,0 +1,181 @@ +//===-- OptionGroupValueObjectDisplay.cpp -----------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "lldb/Interpreter/OptionGroupValueObjectDisplay.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Target/Target.h" +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Utility/Utils.h" + +using namespace lldb; +using namespace lldb_private; + +OptionGroupValueObjectDisplay::OptionGroupValueObjectDisplay() +{ +} + +OptionGroupValueObjectDisplay::~OptionGroupValueObjectDisplay () +{ +} + +static OptionDefinition +g_option_table[] = +{ + { LLDB_OPT_SET_1, false, "dynamic-type", 'd', required_argument, g_dynamic_value_types, 0, eArgTypeNone, "Show the object as its full dynamic type, not its static type, if available."}, + { LLDB_OPT_SET_1, false, "synthetic-type", 'S', required_argument, NULL, 0, eArgTypeBoolean, "Show the object obeying its synthetic provider, if available."}, + { LLDB_OPT_SET_1, false, "depth", 'D', required_argument, NULL, 0, eArgTypeCount, "Set the max recurse depth when dumping aggregate types (default is infinity)."}, + { LLDB_OPT_SET_1, false, "flat", 'F', no_argument, NULL, 0, eArgTypeNone, "Display results in a flat format that uses expression paths for each variable or member."}, + { LLDB_OPT_SET_1, false, "location", 'L', no_argument, NULL, 0, eArgTypeNone, "Show variable location information."}, + { LLDB_OPT_SET_1, false, "object-description", 'O', no_argument, NULL, 0, eArgTypeNone, "Print as an Objective-C object."}, + { LLDB_OPT_SET_1, false, "ptr-depth", 'P', required_argument, NULL, 0, eArgTypeCount, "The number of pointers to be traversed when dumping values (default is zero)."}, + { LLDB_OPT_SET_1, false, "show-types", 'T', no_argument, NULL, 0, eArgTypeNone, "Show variable types when dumping values."}, + { LLDB_OPT_SET_1, false, "no-summary-depth", 'Y', optional_argument, NULL, 0, eArgTypeCount, "Set the depth at which omitting summary information stops (default is 1)."}, + { LLDB_OPT_SET_1, false, "raw-output", 'R', no_argument, NULL, 0, eArgTypeNone, "Don't use formatting options."}, + { LLDB_OPT_SET_1, false, "show-all-children", 'A', no_argument, NULL, 0, eArgTypeNone, "Ignore the upper bound on the number of children to show."}, + { 0, false, NULL, 0, 0, NULL, 0, eArgTypeNone, NULL } +}; + +uint32_t +OptionGroupValueObjectDisplay::GetNumDefinitions () +{ + return llvm::array_lengthof(g_option_table); +} + +const OptionDefinition * +OptionGroupValueObjectDisplay::GetDefinitions () +{ + return g_option_table; +} + + +Error +OptionGroupValueObjectDisplay::SetOptionValue (CommandInterpreter &interpreter, + uint32_t option_idx, + const char *option_arg) +{ + Error error; + const int short_option = g_option_table[option_idx].short_option; + bool success = false; + + switch (short_option) + { + case 'd': + { + int32_t result; + result = Args::StringToOptionEnum (option_arg, g_dynamic_value_types, 2, error); + if (error.Success()) + use_dynamic = (lldb::DynamicValueType) result; + } + break; + case 'T': show_types = true; break; + case 'L': show_location= true; break; + case 'F': flat_output = true; break; + case 'O': use_objc = true; break; + case 'R': be_raw = true; break; + case 'A': ignore_cap = true; break; + + case 'D': + max_depth = Args::StringToUInt32 (option_arg, UINT32_MAX, 0, &success); + if (!success) + error.SetErrorStringWithFormat("invalid max depth '%s'", option_arg); + break; + + case 'P': + ptr_depth = Args::StringToUInt32 (option_arg, 0, 0, &success); + if (!success) + error.SetErrorStringWithFormat("invalid pointer depth '%s'", option_arg); + break; + + case 'Y': + if (option_arg) + { + no_summary_depth = Args::StringToUInt32 (option_arg, 0, 0, &success); + if (!success) + error.SetErrorStringWithFormat("invalid pointer depth '%s'", option_arg); + } + else + no_summary_depth = 1; + break; + + case 'S': + use_synth = Args::StringToBoolean(option_arg, true, &success); + if (!success) + error.SetErrorStringWithFormat("invalid synthetic-type '%s'", option_arg); + break; + default: + error.SetErrorStringWithFormat ("unrecognized option '%c'", short_option); + break; + } + + return error; +} + +void +OptionGroupValueObjectDisplay::OptionParsingStarting (CommandInterpreter &interpreter) +{ + // If these defaults change, be sure to modify AnyOptionWasSet(). + show_types = false; + no_summary_depth = 0; + show_location = false; + flat_output = false; + use_objc = false; + max_depth = UINT32_MAX; + ptr_depth = 0; + use_synth = true; + be_raw = false; + ignore_cap = false; + + Target *target = interpreter.GetExecutionContext().GetTargetPtr(); + if (target != NULL) + use_dynamic = target->GetPreferDynamicValue(); + else + { + // If we don't have any targets, then dynamic values won't do us much good. + use_dynamic = lldb::eNoDynamicValues; + } +} + +ValueObject::DumpValueObjectOptions +OptionGroupValueObjectDisplay::GetAsDumpOptions (bool objc_is_compact, + lldb::Format format, + lldb::TypeSummaryImplSP summary_sp) +{ + ValueObject::DumpValueObjectOptions options; + options.SetMaximumPointerDepth(ptr_depth); + if (use_objc) + options.SetShowSummary(false); + else + options.SetOmitSummaryDepth(no_summary_depth); + options.SetMaximumDepth(max_depth) + .SetShowTypes(show_types) + .SetShowLocation(show_location) + .SetUseObjectiveC(use_objc) + .SetUseDynamicType(use_dynamic) + .SetUseSyntheticValue(use_synth) + .SetFlatOutput(flat_output) + .SetIgnoreCap(ignore_cap) + .SetFormat(format) + .SetSummary(summary_sp); + + if (objc_is_compact) + options.SetHideRootType(use_objc) + .SetHideName(use_objc) + .SetHideValue(use_objc); + + if (be_raw) + options.SetRawDisplay(true); + + return options; +} diff --git a/contrib/llvm/tools/lldb/source/Interpreter/OptionGroupVariable.cpp b/contrib/llvm/tools/lldb/source/Interpreter/OptionGroupVariable.cpp new file mode 100644 index 00000000000..316747eff03 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Interpreter/OptionGroupVariable.cpp @@ -0,0 +1,144 @@ +//===-- OptionGroupVariable.cpp -----------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "lldb/Interpreter/OptionGroupVariable.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/Error.h" +#include "lldb/DataFormatters/DataVisualization.h" +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Target/Target.h" +#include "lldb/Utility/Utils.h" + +using namespace lldb; +using namespace lldb_private; + +// if you add any options here, remember to update the counters in OptionGroupVariable::GetNumDefinitions() +static OptionDefinition +g_option_table[] = +{ + { LLDB_OPT_SET_1 | LLDB_OPT_SET_2, false, "no-args", 'a', no_argument, NULL, 0, eArgTypeNone, "Omit function arguments."}, + { LLDB_OPT_SET_1 | LLDB_OPT_SET_2, false, "no-locals", 'l', no_argument, NULL, 0, eArgTypeNone, "Omit local variables."}, + { LLDB_OPT_SET_1 | LLDB_OPT_SET_2, false, "show-globals", 'g', no_argument, NULL, 0, eArgTypeNone, "Show the current frame source file global and static variables."}, + { LLDB_OPT_SET_1 | LLDB_OPT_SET_2, false, "show-declaration",'c', no_argument, NULL, 0, eArgTypeNone, "Show variable declaration information (source file and line where the variable was declared)."}, + { LLDB_OPT_SET_1 | LLDB_OPT_SET_2, false, "regex", 'r', no_argument, NULL, 0, eArgTypeRegularExpression, "The argument for name lookups are regular expressions."}, + { LLDB_OPT_SET_1 | LLDB_OPT_SET_2, false, "scope", 's', no_argument, NULL, 0, eArgTypeNone, "Show variable scope (argument, local, global, static)."}, + { LLDB_OPT_SET_1, false, "summary", 'y', required_argument, NULL, 0, eArgTypeName, "Specify the summary that the variable output should use."}, + { LLDB_OPT_SET_2, false, "summary-string", 'z', required_argument, NULL, 0, eArgTypeName, "Specify a summary string to use to format the variable output."}, +}; + +static Error +ValidateNamedSummary (const char* str, void*) +{ + if (!str || !str[0]) + return Error("must specify a valid named summary"); + TypeSummaryImplSP summary_sp; + if (DataVisualization::NamedSummaryFormats::GetSummaryFormat(ConstString(str), summary_sp) == false) + return Error("must specify a valid named summary"); + return Error(); +} + +static Error +ValidateSummaryString (const char* str, void*) +{ + if (!str || !str[0]) + return Error("must specify a non-empty summary string"); + return Error(); +} + +OptionGroupVariable::OptionGroupVariable (bool show_frame_options) : + OptionGroup(), + include_frame_options (show_frame_options), + summary(ValidateNamedSummary), + summary_string(ValidateSummaryString) +{ +} + +OptionGroupVariable::~OptionGroupVariable () +{ +} + +Error +OptionGroupVariable::SetOptionValue (CommandInterpreter &interpreter, + uint32_t option_idx, + const char *option_arg) +{ + Error error; + if (!include_frame_options) + option_idx += 3; + const int short_option = g_option_table[option_idx].short_option; + switch (short_option) + { + case 'r': use_regex = true; break; + case 'a': show_args = false; break; + case 'l': show_locals = false; break; + case 'g': show_globals = true; break; + case 'c': show_decl = true; break; + case 's': + show_scope = true; + break; + case 'y': + error = summary.SetCurrentValue(option_arg); + break; + case 'z': + error = summary_string.SetCurrentValue(option_arg); + break; + default: + error.SetErrorStringWithFormat("unrecognized short option '%c'", short_option); + break; + } + + return error; +} + +void +OptionGroupVariable::OptionParsingStarting (CommandInterpreter &interpreter) +{ + show_args = true; // Frame option only + show_locals = true; // Frame option only + show_globals = false; // Frame option only + show_decl = false; + use_regex = false; + show_scope = false; + summary.Clear(); + summary_string.Clear(); +} + +#define NUM_FRAME_OPTS 3 + +const OptionDefinition* +OptionGroupVariable::GetDefinitions () +{ + // Show the "--no-args", "--no-locals" and "--show-globals" + // options if we are showing frame specific options + if (include_frame_options) + return g_option_table; + + // Skip the "--no-args", "--no-locals" and "--show-globals" + // options if we are not showing frame specific options (globals only) + return &g_option_table[NUM_FRAME_OPTS]; +} + +uint32_t +OptionGroupVariable::GetNumDefinitions () +{ + // Count the "--no-args", "--no-locals" and "--show-globals" + // options if we are showing frame specific options. + if (include_frame_options) + return llvm::array_lengthof(g_option_table); + else + return llvm::array_lengthof(g_option_table) - NUM_FRAME_OPTS; +} + + diff --git a/contrib/llvm/tools/lldb/source/Interpreter/OptionGroupWatchpoint.cpp b/contrib/llvm/tools/lldb/source/Interpreter/OptionGroupWatchpoint.cpp new file mode 100644 index 00000000000..9eef37a3ae6 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Interpreter/OptionGroupWatchpoint.cpp @@ -0,0 +1,121 @@ +//===-- OptionGroupWatchpoint.cpp -------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Interpreter/OptionGroupWatchpoint.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-enumerations.h" +#include "lldb/Interpreter/Args.h" +#include "lldb/Utility/Utils.h" + +using namespace lldb; +using namespace lldb_private; + +static OptionEnumValueElement g_watch_type[] = +{ + { OptionGroupWatchpoint::eWatchRead, "read", "Watch for read"}, + { OptionGroupWatchpoint::eWatchWrite, "write", "Watch for write"}, + { OptionGroupWatchpoint::eWatchReadWrite, "read_write", "Watch for read/write"}, + { 0, NULL, NULL } +}; + +static OptionEnumValueElement g_watch_size[] = +{ + { 1, "1", "Watch for byte size of 1"}, + { 2, "2", "Watch for byte size of 2"}, + { 4, "4", "Watch for byte size of 4"}, + { 8, "8", "Watch for byte size of 8"}, + { 0, NULL, NULL } +}; + +static OptionDefinition +g_option_table[] = +{ + { LLDB_OPT_SET_1, false, "watch", 'w', required_argument, g_watch_type, 0, eArgTypeWatchType, "Specify the type of watching to perform."}, + { LLDB_OPT_SET_1, false, "xsize", 'x', required_argument, g_watch_size, 0, eArgTypeByteSize, "Number of bytes to use to watch a region."} +}; + + +bool +OptionGroupWatchpoint::IsWatchSizeSupported(uint32_t watch_size) +{ + for (uint32_t i = 0; i < llvm::array_lengthof(g_watch_size); ++i) + { + if (g_watch_size[i].value == 0) + break; + if (watch_size == g_watch_size[i].value) + return true; + } + return false; +} + +OptionGroupWatchpoint::OptionGroupWatchpoint () : + OptionGroup() +{ +} + +OptionGroupWatchpoint::~OptionGroupWatchpoint () +{ +} + +Error +OptionGroupWatchpoint::SetOptionValue (CommandInterpreter &interpreter, + uint32_t option_idx, + const char *option_arg) +{ + Error error; + const int short_option = g_option_table[option_idx].short_option; + switch (short_option) + { + case 'w': + { + WatchType tmp_watch_type; + tmp_watch_type = (WatchType) Args::StringToOptionEnum(option_arg, g_option_table[option_idx].enum_values, 0, error); + if (error.Success()) + { + watch_type = tmp_watch_type; + watch_type_specified = true; + } + break; + } + case 'x': + watch_size = (uint32_t) Args::StringToOptionEnum(option_arg, g_option_table[option_idx].enum_values, 0, error); + break; + + default: + error.SetErrorStringWithFormat("unrecognized short option '%c'", short_option); + break; + } + + return error; +} + +void +OptionGroupWatchpoint::OptionParsingStarting (CommandInterpreter &interpreter) +{ + watch_type_specified = false; + watch_type = eWatchInvalid; + watch_size = 0; +} + + +const OptionDefinition* +OptionGroupWatchpoint::GetDefinitions () +{ + return g_option_table; +} + +uint32_t +OptionGroupWatchpoint::GetNumDefinitions () +{ + return llvm::array_lengthof(g_option_table); +} diff --git a/contrib/llvm/tools/lldb/source/Interpreter/OptionValue.cpp b/contrib/llvm/tools/lldb/source/Interpreter/OptionValue.cpp new file mode 100644 index 00000000000..1f6b03ddac1 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Interpreter/OptionValue.cpp @@ -0,0 +1,633 @@ +//===-- OptionValue.cpp -----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Interpreter/OptionValue.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/StringList.h" +#include "lldb/Interpreter/OptionValues.h" + +using namespace lldb; +using namespace lldb_private; + + +//------------------------------------------------------------------------- +// Get this value as a uint64_t value if it is encoded as a boolean, +// uint64_t or int64_t. Other types will cause "fail_value" to be +// returned +//------------------------------------------------------------------------- +uint64_t +OptionValue::GetUInt64Value (uint64_t fail_value, bool *success_ptr) +{ + if (success_ptr) + *success_ptr = true; + switch (GetType()) + { + case OptionValue::eTypeBoolean: return static_cast(this)->GetCurrentValue(); + case OptionValue::eTypeSInt64: return static_cast(this)->GetCurrentValue(); + case OptionValue::eTypeUInt64: return static_cast(this)->GetCurrentValue(); + default: + break; + } + if (success_ptr) + *success_ptr = false; + return fail_value; +} + +Error +OptionValue::SetSubValue (const ExecutionContext *exe_ctx, + VarSetOperationType op, + const char *name, + const char *value) +{ + Error error; + error.SetErrorStringWithFormat("SetSubValue is not supported"); + return error; +} + + +OptionValueBoolean * +OptionValue::GetAsBoolean () +{ + if (GetType () == OptionValue::eTypeBoolean) + return static_cast(this); + return NULL; +} + +const OptionValueBoolean * +OptionValue::GetAsBoolean () const +{ + if (GetType () == OptionValue::eTypeBoolean) + return static_cast(this); + return NULL; +} + + +OptionValueFileSpec * +OptionValue::GetAsFileSpec () +{ + if (GetType () == OptionValue::eTypeFileSpec) + return static_cast(this); + return NULL; + +} + +const OptionValueFileSpec * +OptionValue::GetAsFileSpec () const +{ + if (GetType () == OptionValue::eTypeFileSpec) + return static_cast(this); + return NULL; + +} + +OptionValueFileSpecList * +OptionValue::GetAsFileSpecList () +{ + if (GetType () == OptionValue::eTypeFileSpecList) + return static_cast(this); + return NULL; + +} + +const OptionValueFileSpecList * +OptionValue::GetAsFileSpecList () const +{ + if (GetType () == OptionValue::eTypeFileSpecList) + return static_cast(this); + return NULL; + +} + +OptionValueArch * +OptionValue::GetAsArch () +{ + if (GetType () == OptionValue::eTypeArch) + return static_cast(this); + return NULL; +} + + +const OptionValueArch * +OptionValue::GetAsArch () const +{ + if (GetType () == OptionValue::eTypeArch) + return static_cast(this); + return NULL; +} + +OptionValueArray * +OptionValue::GetAsArray () +{ + if (GetType () == OptionValue::eTypeArray) + return static_cast(this); + return NULL; +} + + +const OptionValueArray * +OptionValue::GetAsArray () const +{ + if (GetType () == OptionValue::eTypeArray) + return static_cast(this); + return NULL; +} + +OptionValueArgs * +OptionValue::GetAsArgs () +{ + if (GetType () == OptionValue::eTypeArgs) + return static_cast(this); + return NULL; +} + + +const OptionValueArgs * +OptionValue::GetAsArgs () const +{ + if (GetType () == OptionValue::eTypeArgs) + return static_cast(this); + return NULL; +} + +OptionValueDictionary * +OptionValue::GetAsDictionary () +{ + if (GetType () == OptionValue::eTypeDictionary) + return static_cast(this); + return NULL; +} + +const OptionValueDictionary * +OptionValue::GetAsDictionary () const +{ + if (GetType () == OptionValue::eTypeDictionary) + return static_cast(this); + return NULL; +} + +OptionValueEnumeration * +OptionValue::GetAsEnumeration () +{ + if (GetType () == OptionValue::eTypeEnum) + return static_cast(this); + return NULL; +} + +const OptionValueEnumeration * +OptionValue::GetAsEnumeration () const +{ + if (GetType () == OptionValue::eTypeEnum) + return static_cast(this); + return NULL; +} + +OptionValueFormat * +OptionValue::GetAsFormat () +{ + if (GetType () == OptionValue::eTypeFormat) + return static_cast(this); + return NULL; +} + +const OptionValueFormat * +OptionValue::GetAsFormat () const +{ + if (GetType () == OptionValue::eTypeFormat) + return static_cast(this); + return NULL; +} + +OptionValuePathMappings * +OptionValue::GetAsPathMappings () +{ + if (GetType () == OptionValue::eTypePathMap) + return static_cast(this); + return NULL; +} + +const OptionValuePathMappings * +OptionValue::GetAsPathMappings () const +{ + if (GetType () == OptionValue::eTypePathMap) + return static_cast(this); + return NULL; +} + +OptionValueProperties * +OptionValue::GetAsProperties () +{ + if (GetType () == OptionValue::eTypeProperties) + return static_cast(this); + return NULL; +} + +const OptionValueProperties * +OptionValue::GetAsProperties () const +{ + if (GetType () == OptionValue::eTypeProperties) + return static_cast(this); + return NULL; +} + +OptionValueRegex * +OptionValue::GetAsRegex () +{ + if (GetType () == OptionValue::eTypeRegex) + return static_cast(this); + return NULL; +} + +const OptionValueRegex * +OptionValue::GetAsRegex () const +{ + if (GetType () == OptionValue::eTypeRegex) + return static_cast(this); + return NULL; +} + +OptionValueSInt64 * +OptionValue::GetAsSInt64 () +{ + if (GetType () == OptionValue::eTypeSInt64) + return static_cast(this); + return NULL; +} + +const OptionValueSInt64 * +OptionValue::GetAsSInt64 () const +{ + if (GetType () == OptionValue::eTypeSInt64) + return static_cast(this); + return NULL; +} + +OptionValueString * +OptionValue::GetAsString () +{ + if (GetType () == OptionValue::eTypeString) + return static_cast(this); + return NULL; +} + +const OptionValueString * +OptionValue::GetAsString () const +{ + if (GetType () == OptionValue::eTypeString) + return static_cast(this); + return NULL; +} + +OptionValueUInt64 * +OptionValue::GetAsUInt64 () +{ + if (GetType () == OptionValue::eTypeUInt64) + return static_cast(this); + return NULL; +} + +const OptionValueUInt64 * +OptionValue::GetAsUInt64 () const +{ + if (GetType () == OptionValue::eTypeUInt64) + return static_cast(this); + return NULL; +} + +OptionValueUUID * +OptionValue::GetAsUUID () +{ + if (GetType () == OptionValue::eTypeUUID) + return static_cast(this); + return NULL; + +} + +const OptionValueUUID * +OptionValue::GetAsUUID () const +{ + if (GetType () == OptionValue::eTypeUUID) + return static_cast(this); + return NULL; + +} + +bool +OptionValue::GetBooleanValue (bool fail_value) const +{ + const OptionValueBoolean *option_value = GetAsBoolean (); + if (option_value) + return option_value->GetCurrentValue(); + return fail_value; +} + +bool +OptionValue::SetBooleanValue (bool new_value) +{ + OptionValueBoolean *option_value = GetAsBoolean (); + if (option_value) + { + option_value->SetCurrentValue(new_value); + return true; + } + return false; +} + +int64_t +OptionValue::GetEnumerationValue (int64_t fail_value) const +{ + const OptionValueEnumeration *option_value = GetAsEnumeration(); + if (option_value) + return option_value->GetCurrentValue(); + return fail_value; +} + +bool +OptionValue::SetEnumerationValue (int64_t value) +{ + OptionValueEnumeration *option_value = GetAsEnumeration(); + if (option_value) + { + option_value->SetCurrentValue(value); + return true; + } + return false; +} + +FileSpec +OptionValue::GetFileSpecValue () const +{ + const OptionValueFileSpec *option_value = GetAsFileSpec (); + if (option_value) + return option_value->GetCurrentValue(); + return FileSpec(); +} + + +bool +OptionValue::SetFileSpecValue (const FileSpec &file_spec) +{ + OptionValueFileSpec *option_value = GetAsFileSpec (); + if (option_value) + { + option_value->SetCurrentValue(file_spec, false); + return true; + } + return false; +} + +FileSpecList +OptionValue::GetFileSpecListValue () const +{ + const OptionValueFileSpecList *option_value = GetAsFileSpecList (); + if (option_value) + return option_value->GetCurrentValue(); + return FileSpecList(); +} + + +lldb::Format +OptionValue::GetFormatValue (lldb::Format fail_value) const +{ + const OptionValueFormat *option_value = GetAsFormat (); + if (option_value) + return option_value->GetCurrentValue(); + return fail_value; +} + +bool +OptionValue::SetFormatValue (lldb::Format new_value) +{ + OptionValueFormat *option_value = GetAsFormat (); + if (option_value) + { + option_value->SetCurrentValue(new_value); + return true; + } + return false; +} + +const RegularExpression * +OptionValue::GetRegexValue () const +{ + const OptionValueRegex *option_value = GetAsRegex (); + if (option_value) + return option_value->GetCurrentValue(); + return NULL; +} + + +int64_t +OptionValue::GetSInt64Value (int64_t fail_value) const +{ + const OptionValueSInt64 *option_value = GetAsSInt64 (); + if (option_value) + return option_value->GetCurrentValue(); + return fail_value; +} + +bool +OptionValue::SetSInt64Value (int64_t new_value) +{ + OptionValueSInt64 *option_value = GetAsSInt64 (); + if (option_value) + { + option_value->SetCurrentValue(new_value); + return true; + } + return false; +} + +const char * +OptionValue::GetStringValue (const char *fail_value) const +{ + const OptionValueString *option_value = GetAsString (); + if (option_value) + return option_value->GetCurrentValue(); + return fail_value; +} + +bool +OptionValue::SetStringValue (const char *new_value) +{ + OptionValueString *option_value = GetAsString (); + if (option_value) + { + option_value->SetCurrentValue(new_value); + return true; + } + return false; +} + +uint64_t +OptionValue::GetUInt64Value (uint64_t fail_value) const +{ + const OptionValueUInt64 *option_value = GetAsUInt64 (); + if (option_value) + return option_value->GetCurrentValue(); + return fail_value; +} + +bool +OptionValue::SetUInt64Value (uint64_t new_value) +{ + OptionValueUInt64 *option_value = GetAsUInt64 (); + if (option_value) + { + option_value->SetCurrentValue(new_value); + return true; + } + return false; +} + +UUID +OptionValue::GetUUIDValue () const +{ + const OptionValueUUID *option_value = GetAsUUID(); + if (option_value) + return option_value->GetCurrentValue(); + return UUID(); +} + +bool +OptionValue::SetUUIDValue (const UUID &uuid) +{ + OptionValueUUID *option_value = GetAsUUID(); + if (option_value) + { + option_value->SetCurrentValue(uuid); + return true; + } + return false; +} + +const char * +OptionValue::GetBuiltinTypeAsCString (Type t) +{ + switch (t) + { + case eTypeInvalid: return "invalid"; + case eTypeArch: return "arch"; + case eTypeArgs: return "arguments"; + case eTypeArray: return "array"; + case eTypeBoolean: return "boolean"; + case eTypeDictionary: return "dictionary"; + case eTypeEnum: return "enum"; + case eTypeFileSpec: return "file"; + case eTypeFileSpecList: return "file-list"; + case eTypeFormat: return "format"; + case eTypePathMap: return "path-map"; + case eTypeProperties: return "properties"; + case eTypeRegex: return "regex"; + case eTypeSInt64: return "int"; + case eTypeString: return "string"; + case eTypeUInt64: return "unsigned"; + case eTypeUUID: return "uuid"; + } + return NULL; +} + + +lldb::OptionValueSP +OptionValue::CreateValueFromCStringForTypeMask (const char *value_cstr, uint32_t type_mask, Error &error) +{ + // If only 1 bit is set in the type mask for a dictionary or array + // then we know how to decode a value from a cstring + lldb::OptionValueSP value_sp; + switch (type_mask) + { + case 1u << eTypeArch: value_sp.reset(new OptionValueArch()); break; + case 1u << eTypeBoolean: value_sp.reset(new OptionValueBoolean(false)); break; + case 1u << eTypeFileSpec: value_sp.reset(new OptionValueFileSpec()); break; + case 1u << eTypeFormat: value_sp.reset(new OptionValueFormat(eFormatInvalid)); break; + case 1u << eTypeSInt64: value_sp.reset(new OptionValueSInt64()); break; + case 1u << eTypeString: value_sp.reset(new OptionValueString()); break; + case 1u << eTypeUInt64: value_sp.reset(new OptionValueUInt64()); break; + case 1u << eTypeUUID: value_sp.reset(new OptionValueUUID()); break; + } + + if (value_sp) + error = value_sp->SetValueFromCString (value_cstr, eVarSetOperationAssign); + else + error.SetErrorString("unsupported type mask"); + return value_sp; +} + +bool +OptionValue::DumpQualifiedName (Stream &strm) const +{ + bool dumped_something = false; + lldb::OptionValueSP m_parent_sp(m_parent_wp.lock()); + if (m_parent_sp) + { + if (m_parent_sp->DumpQualifiedName(strm)) + dumped_something = true; + } + ConstString name (GetName()); + if (name) + { + if (dumped_something) + strm.PutChar('.'); + else + dumped_something = true; + strm << name; + } + return dumped_something; +} + +size_t +OptionValue::AutoComplete (CommandInterpreter &interpreter, + const char *s, + int match_start_point, + int max_return_elements, + bool &word_complete, + StringList &matches) +{ + word_complete = false; + matches.Clear(); + return matches.GetSize(); +} + +Error +OptionValue::SetValueFromCString (const char *value, VarSetOperationType op) +{ + Error error; + switch (op) + { + case eVarSetOperationReplace: + error.SetErrorStringWithFormat ("%s objects do not support the 'replace' operation", GetTypeAsCString()); + break; + case eVarSetOperationInsertBefore: + error.SetErrorStringWithFormat ("%s objects do not support the 'insert-before' operation", GetTypeAsCString()); + break; + case eVarSetOperationInsertAfter: + error.SetErrorStringWithFormat ("%s objects do not support the 'insert-after' operation", GetTypeAsCString()); + break; + case eVarSetOperationRemove: + error.SetErrorStringWithFormat ("%s objects do not support the 'remove' operation", GetTypeAsCString()); + break; + case eVarSetOperationAppend: + error.SetErrorStringWithFormat ("%s objects do not support the 'append' operation", GetTypeAsCString()); + break; + case eVarSetOperationClear: + error.SetErrorStringWithFormat ("%s objects do not support the 'clear' operation", GetTypeAsCString()); + break; + case eVarSetOperationAssign: + error.SetErrorStringWithFormat ("%s objects do not support the 'assign' operation", GetTypeAsCString()); + break; + case eVarSetOperationInvalid: + error.SetErrorStringWithFormat ("invalid operation performed on a %s object", GetTypeAsCString()); + break; + } + return error; +} + diff --git a/contrib/llvm/tools/lldb/source/Interpreter/OptionValueArch.cpp b/contrib/llvm/tools/lldb/source/Interpreter/OptionValueArch.cpp new file mode 100644 index 00000000000..92fedffe75e --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Interpreter/OptionValueArch.cpp @@ -0,0 +1,111 @@ +//===-- OptionValueArch.cpp ---------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "lldb/Interpreter/OptionValueArch.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/State.h" +#include "lldb/DataFormatters/FormatManager.h" +#include "lldb/Interpreter/Args.h" +#include "lldb/Interpreter/CommandCompletions.h" + +using namespace lldb; +using namespace lldb_private; + +void +OptionValueArch::DumpValue (const ExecutionContext *exe_ctx, Stream &strm, uint32_t dump_mask) +{ + if (dump_mask & eDumpOptionType) + strm.Printf ("(%s)", GetTypeAsCString ()); + if (dump_mask & eDumpOptionValue) + { + if (dump_mask & eDumpOptionType) + strm.PutCString (" = "); + + if (m_current_value.IsValid()) + { + const char *arch_name = m_current_value.GetArchitectureName(); + if (arch_name) + strm.PutCString (arch_name); + } + } +} + +Error +OptionValueArch::SetValueFromCString (const char *value_cstr, VarSetOperationType op) +{ + Error error; + switch (op) + { + case eVarSetOperationClear: + Clear(); + break; + + case eVarSetOperationReplace: + case eVarSetOperationAssign: + if (value_cstr && value_cstr[0]) + { + if (m_current_value.SetTriple (value_cstr)) + m_value_was_set = true; + else + error.SetErrorStringWithFormat("unsupported architecture '%s'", value_cstr); + } + else + { + error.SetErrorString("invalid value string"); + } + break; + + case eVarSetOperationInsertBefore: + case eVarSetOperationInsertAfter: + case eVarSetOperationRemove: + case eVarSetOperationAppend: + case eVarSetOperationInvalid: + error = OptionValue::SetValueFromCString (value_cstr, op); + break; + } + return error; +} + +lldb::OptionValueSP +OptionValueArch::DeepCopy () const +{ + return OptionValueSP(new OptionValueArch(*this)); +} + + +size_t +OptionValueArch::AutoComplete (CommandInterpreter &interpreter, + const char *s, + int match_start_point, + int max_return_elements, + bool &word_complete, + StringList &matches) +{ + word_complete = false; + matches.Clear(); + CommandCompletions::InvokeCommonCompletionCallbacks (interpreter, + CommandCompletions::eArchitectureCompletion, + s, + match_start_point, + max_return_elements, + NULL, + word_complete, + matches); + return matches.GetSize(); +} + + + + diff --git a/contrib/llvm/tools/lldb/source/Interpreter/OptionValueArgs.cpp b/contrib/llvm/tools/lldb/source/Interpreter/OptionValueArgs.cpp new file mode 100644 index 00000000000..e28d884581f --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Interpreter/OptionValueArgs.cpp @@ -0,0 +1,38 @@ +//===-- OptionValueArgs.cpp -------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Interpreter/OptionValueArgs.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Interpreter/Args.h" + +using namespace lldb; +using namespace lldb_private; + +size_t +OptionValueArgs::GetArgs (Args &args) +{ + const uint32_t size = m_values.size(); + std::vector argv; + for (uint32_t i = 0; iGetStringValue (); + if (string_value) + argv.push_back(string_value); + } + + if (argv.empty()) + args.Clear(); + else + args.SetArguments(argv.size(), &argv[0]); + return args.GetArgumentCount(); +} diff --git a/contrib/llvm/tools/lldb/source/Interpreter/OptionValueArray.cpp b/contrib/llvm/tools/lldb/source/Interpreter/OptionValueArray.cpp new file mode 100644 index 00000000000..9a015580bd6 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Interpreter/OptionValueArray.cpp @@ -0,0 +1,350 @@ +//===-- OptionValueArray.cpp ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Interpreter/OptionValueArray.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/Stream.h" +#include "lldb/Interpreter/Args.h" + +using namespace lldb; +using namespace lldb_private; + +void +OptionValueArray::DumpValue (const ExecutionContext *exe_ctx, Stream &strm, uint32_t dump_mask) +{ + const Type array_element_type = ConvertTypeMaskToType (m_type_mask); + if (dump_mask & eDumpOptionType) + { + if ((GetType() == eTypeArray) && (m_type_mask != eTypeInvalid)) + strm.Printf ("(%s of %ss)", GetTypeAsCString(), GetBuiltinTypeAsCString(array_element_type)); + else + strm.Printf ("(%s)", GetTypeAsCString()); + } + if (dump_mask & eDumpOptionValue) + { + if (dump_mask & eDumpOptionType) + strm.Printf (" =%s", (m_values.size() > 0) ? "\n" : ""); + strm.IndentMore(); + const uint32_t size = m_values.size(); + for (uint32_t i = 0; iDumpValue(exe_ctx, strm, dump_mask | extra_dump_options); + break; + + case eTypeBoolean: + case eTypeEnum: + case eTypeFileSpec: + case eTypeFormat: + case eTypeSInt64: + case eTypeString: + case eTypeUInt64: + case eTypeUUID: + // No need to show the type for dictionaries of simple items + m_values[i]->DumpValue(exe_ctx, strm, (dump_mask & (~eDumpOptionType)) | extra_dump_options); + break; + } + if (i < (size - 1)) + strm.EOL(); + } + strm.IndentLess(); + } +} + +Error +OptionValueArray::SetValueFromCString (const char *value, VarSetOperationType op) +{ + Args args(value); + return SetArgs (args, op); +} + + +lldb::OptionValueSP +OptionValueArray::GetSubValue (const ExecutionContext *exe_ctx, + const char *name, + bool will_modify, + Error &error) const +{ + if (name && name[0] == '[') + { + const char *end_bracket = strchr (name+1, ']'); + if (end_bracket) + { + const char *sub_value = NULL; + if (end_bracket[1]) + sub_value = end_bracket + 1; + std::string index_str (name+1, end_bracket); + const size_t array_count = m_values.size(); + int32_t idx = Args::StringToSInt32(index_str.c_str(), INT32_MAX, 0, NULL); + if (idx != INT32_MAX) + { + ; + uint32_t new_idx = UINT32_MAX; + if (idx < 0) + { + // Access from the end of the array if the index is negative + new_idx = array_count - idx; + } + else + { + // Just a standard index + new_idx = idx; + } + + if (new_idx < array_count) + { + if (m_values[new_idx]) + { + if (sub_value) + return m_values[new_idx]->GetSubValue (exe_ctx, sub_value, will_modify, error); + else + return m_values[new_idx]; + } + } + else + { + if (array_count == 0) + error.SetErrorStringWithFormat("index %i is not valid for an empty array", idx); + else if (idx > 0) + error.SetErrorStringWithFormat("index %i out of range, valid values are 0 through %" PRIu64, idx, (uint64_t)(array_count - 1)); + else + error.SetErrorStringWithFormat("negative index %i out of range, valid values are -1 through -%" PRIu64, idx, (uint64_t)array_count); + } + } + } + } + else + { + error.SetErrorStringWithFormat("invalid value path '%s', %s values only support '[]' subvalues where is a positive or negative array index", name, GetTypeAsCString()); + } + return OptionValueSP(); +} + + +size_t +OptionValueArray::GetArgs (Args &args) const +{ + const uint32_t size = m_values.size(); + std::vector argv; + for (uint32_t i = 0; iGetStringValue (); + if (string_value) + argv.push_back(string_value); + } + + if (argv.empty()) + args.Clear(); + else + args.SetArguments(argv.size(), &argv[0]); + return args.GetArgumentCount(); +} + +Error +OptionValueArray::SetArgs (const Args &args, VarSetOperationType op) +{ + Error error; + const size_t argc = args.GetArgumentCount(); + switch (op) + { + case eVarSetOperationInvalid: + error.SetErrorString("unsupported operation"); + break; + + case eVarSetOperationInsertBefore: + case eVarSetOperationInsertAfter: + if (argc > 1) + { + uint32_t idx = Args::StringToUInt32(args.GetArgumentAtIndex(0), UINT32_MAX); + const uint32_t count = GetSize(); + if (idx > count) + { + error.SetErrorStringWithFormat("invalid insert array index %u, index must be 0 through %u", idx, count); + } + else + { + if (op == eVarSetOperationInsertAfter) + ++idx; + for (size_t i=1; i= m_values.size()) + m_values.push_back(value_sp); + else + m_values.insert(m_values.begin() + idx, value_sp); + } + else + { + error.SetErrorString("array of complex types must subclass OptionValueArray"); + return error; + } + } + } + } + else + { + error.SetErrorString("insert operation takes an array index followed by one or more values"); + } + break; + + case eVarSetOperationRemove: + if (argc > 0) + { + const uint32_t size = m_values.size(); + std::vector remove_indexes; + bool all_indexes_valid = true; + size_t i; + for (i=0; i= size) + { + all_indexes_valid = false; + break; + } + else + remove_indexes.push_back(idx); + } + + if (all_indexes_valid) + { + size_t num_remove_indexes = remove_indexes.size(); + if (num_remove_indexes) + { + // Sort and then erase in reverse so indexes are always valid + if (num_remove_indexes > 1) + { + std::sort(remove_indexes.begin(), remove_indexes.end()); + for (std::vector::const_reverse_iterator pos = remove_indexes.rbegin(), end = remove_indexes.rend(); pos != end; ++pos) + { + m_values.erase(m_values.begin() + *pos); + } + } + else + { + // Only one index + m_values.erase(m_values.begin() + remove_indexes.front()); + } + } + } + else + { + error.SetErrorStringWithFormat("invalid array index '%s', aborting remove operation", args.GetArgumentAtIndex(i)); + } + } + else + { + error.SetErrorString("remove operation takes one or more array indices"); + } + break; + + case eVarSetOperationClear: + Clear (); + break; + + case eVarSetOperationReplace: + if (argc > 1) + { + uint32_t idx = Args::StringToUInt32(args.GetArgumentAtIndex(0), UINT32_MAX); + const uint32_t count = GetSize(); + if (idx > count) + { + error.SetErrorStringWithFormat("invalid replace array index %u, index must be 0 through %u", idx, count); + } + else + { + for (size_t i=1; iAppendValue (m_values[i]->DeepCopy()); + } + return copied_value_sp; +} + + + diff --git a/contrib/llvm/tools/lldb/source/Interpreter/OptionValueBoolean.cpp b/contrib/llvm/tools/lldb/source/Interpreter/OptionValueBoolean.cpp new file mode 100644 index 00000000000..6471943ee5d --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Interpreter/OptionValueBoolean.cpp @@ -0,0 +1,135 @@ +//===-- OptionValueBoolean.cpp ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Interpreter/OptionValueBoolean.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/Stream.h" +#include "lldb/Core/StringList.h" +#include "lldb/Interpreter/Args.h" + +using namespace lldb; +using namespace lldb_private; + +void +OptionValueBoolean::DumpValue (const ExecutionContext *exe_ctx, Stream &strm, uint32_t dump_mask) +{ + if (dump_mask & eDumpOptionType) + strm.Printf ("(%s)", GetTypeAsCString ()); +// if (dump_mask & eDumpOptionName) +// DumpQualifiedName (strm); + if (dump_mask & eDumpOptionValue) + { + if (dump_mask & eDumpOptionType) + strm.PutCString (" = "); + strm.PutCString (m_current_value ? "true" : "false"); + } +} + +Error +OptionValueBoolean::SetValueFromCString (const char *value_cstr, + VarSetOperationType op) +{ + Error error; + switch (op) + { + case eVarSetOperationClear: + Clear(); + break; + + case eVarSetOperationReplace: + case eVarSetOperationAssign: + { + bool success = false; + bool value = Args::StringToBoolean(value_cstr, false, &success); + if (success) + { + m_value_was_set = true; + m_current_value = value; + } + else + { + if (value_cstr == NULL) + error.SetErrorString ("invalid boolean string value: NULL"); + else if (value_cstr[0] == '\0') + error.SetErrorString ("invalid boolean string value "); + else + error.SetErrorStringWithFormat ("invalid boolean string value: '%s'", value_cstr); + } + } + break; + + case eVarSetOperationInsertBefore: + case eVarSetOperationInsertAfter: + case eVarSetOperationRemove: + case eVarSetOperationAppend: + case eVarSetOperationInvalid: + error = OptionValue::SetValueFromCString (value_cstr, op); + break; + } + return error; +} + +lldb::OptionValueSP +OptionValueBoolean::DeepCopy () const +{ + return OptionValueSP(new OptionValueBoolean(*this)); +} + +size_t +OptionValueBoolean::AutoComplete (CommandInterpreter &interpreter, + const char *s, + int match_start_point, + int max_return_elements, + bool &word_complete, + StringList &matches) +{ + word_complete = false; + matches.Clear(); + struct StringEntry { + const char *string; + const size_t length; + }; + static const StringEntry g_autocomplete_entries[] = + { + { "true" , 4 }, + { "false", 5 }, + { "on" , 2 }, + { "off" , 3 }, + { "yes" , 3 }, + { "no" , 2 }, + { "1" , 1 }, + { "0" , 1 }, + }; + const size_t k_num_autocomplete_entries = sizeof(g_autocomplete_entries)/sizeof(StringEntry); + + if (s && s[0]) + { + const size_t s_len = strlen(s); + for (size_t i=0; iDumpValue(exe_ctx, strm, (dump_mask & (~eDumpOptionType)) | extra_dump_options); + break; + } + } + strm.IndentLess(); + } + +} + +size_t +OptionValueDictionary::GetArgs (Args &args) const +{ + args.Clear(); + collection::const_iterator pos, end = m_values.end(); + for (pos = m_values.begin(); pos != end; ++pos) + { + StreamString strm; + strm.Printf("%s=", pos->first.GetCString()); + pos->second->DumpValue(NULL, strm, eDumpOptionValue|eDumpOptionRaw); + args.AppendArgument(strm.GetString().c_str()); + } + return args.GetArgumentCount(); +} + +Error +OptionValueDictionary::SetArgs (const Args &args, VarSetOperationType op) +{ + Error error; + const size_t argc = args.GetArgumentCount(); + switch (op) + { + case eVarSetOperationClear: + Clear(); + break; + + case eVarSetOperationAppend: + case eVarSetOperationReplace: + case eVarSetOperationAssign: + if (argc > 0) + { + for (size_t i=0; i kvp(key_and_value.split('=')); + llvm::StringRef key = kvp.first; + bool key_valid = false; + if (!key.empty()) + { + if (key.front() == '[') + { + // Key name starts with '[', so the the key value must be in single or double quotes like: + // [''] + // [""] + if ((key.size() > 2) && (key.back() == ']')) + { + // Strip leading '[' and trailing ']' + key = key.substr(1, key.size()-2); + const char quote_char = key.front(); + if ((quote_char == '\'') || (quote_char == '"')) + { + if ((key.size() > 2) && (key.back() == quote_char)) + { + // Strip the quotes + key = key.substr(1, key.size()-2); + key_valid = true; + } + } + else + { + // square brackets, no quotes + key_valid = true; + } + } + } + else + { + // No square brackets or quotes + key_valid = true; + } + } + if (!key_valid) + { + error.SetErrorStringWithFormat("invalid key \"%s\", the key must be a bare string or surrounded by brackets with optional quotes: [] or [''] or [\"\"]", kvp.first.str().c_str()); + return error; + } + + lldb::OptionValueSP value_sp (CreateValueFromCStringForTypeMask (kvp.second.data(), + m_type_mask, + error)); + if (value_sp) + { + if (error.Fail()) + return error; + m_value_was_set = true; + SetValueForKey (ConstString(key), value_sp, true); + } + else + { + error.SetErrorString("dictionaries that can contain multiple types must subclass OptionValueArray"); + } + } + else + { + error.SetErrorString("empty argument"); + } + } + } + else + { + error.SetErrorString("assign operation takes one or more key=value arguments"); + } + break; + + case eVarSetOperationRemove: + if (argc > 0) + { + for (size_t i=0; i'] where is a string that doesn't contain quotes", name); + return value_sp; + } + } + else + { + error.SetErrorString ("missing '] key name terminator, key name started with ['"); + return value_sp; + } + break; + case '"': + ++key_start; + key_end = strchr(key_start, '"'); + if (key_end) + { + if (key_end[1] == ']') + { + if (key_end[2]) + sub_name = key_end + 2; + break; + } + error.SetErrorStringWithFormat ("invalid value path '%s', double quoted key names must be formatted as [\"\"] where is a string that doesn't contain quotes", name); + return value_sp; + } + else + { + error.SetErrorString ("missing \"] key name terminator, key name started with [\""); + return value_sp; + } + break; + + default: + key_end = strchr(key_start, ']'); + if (key_end) + { + if (key_end[1]) + sub_name = key_end + 1; + } + else + { + error.SetErrorString ("missing ] key name terminator, key name started with ["); + return value_sp; + } + break; + } + + if (key_start && key_end) + { + key.SetCStringWithLength (key_start, key_end - key_start); + + value_sp = GetValueForKey (key); + if (value_sp) + { + if (sub_name) + return value_sp->GetSubValue (exe_ctx, sub_name, will_modify, error); + } + else + { + error.SetErrorStringWithFormat("dictionary does not contain a value for the key name '%s'", key.GetCString()); + } + } + } + if (!value_sp && error.AsCString() == NULL) + { + error.SetErrorStringWithFormat ("invalid value path '%s', %s values only support '[]' subvalues where a string value optionally delimitted by single or double quotes", + name, + GetTypeAsCString()); + } + } + return value_sp; +} + +Error +OptionValueDictionary::SetSubValue (const ExecutionContext *exe_ctx, VarSetOperationType op, const char *name, const char *value) +{ + Error error; + const bool will_modify = true; + lldb::OptionValueSP value_sp (GetSubValue (exe_ctx, name, will_modify, error)); + if (value_sp) + error = value_sp->SetValueFromCString(value, op); + else + { + if (error.AsCString() == NULL) + error.SetErrorStringWithFormat("invalid value path '%s'", name); + } + return error; +} + + +lldb::OptionValueSP +OptionValueDictionary::GetValueForKey (const ConstString &key) const +{ + lldb::OptionValueSP value_sp; + collection::const_iterator pos = m_values.find (key); + if (pos != m_values.end()) + value_sp = pos->second; + return value_sp; +} + +const char * +OptionValueDictionary::GetStringValueForKey (const ConstString &key) +{ + collection::const_iterator pos = m_values.find (key); + if (pos != m_values.end()) + { + OptionValueString *string_value = pos->second->GetAsString(); + if (string_value) + return string_value->GetCurrentValue(); + } + return NULL; +} + + +bool +OptionValueDictionary::SetStringValueForKey (const ConstString &key, + const char *value, + bool can_replace) +{ + collection::const_iterator pos = m_values.find (key); + if (pos != m_values.end()) + { + if (!can_replace) + return false; + if (pos->second->GetType() == OptionValue::eTypeString) + { + pos->second->SetValueFromCString(value); + return true; + } + } + m_values[key] = OptionValueSP (new OptionValueString (value)); + return true; + +} + +bool +OptionValueDictionary::SetValueForKey (const ConstString &key, + const lldb::OptionValueSP &value_sp, + bool can_replace) +{ + // Make sure the value_sp object is allowed to contain + // values of the type passed in... + if (value_sp && (m_type_mask & value_sp->GetTypeAsMask())) + { + if (!can_replace) + { + collection::const_iterator pos = m_values.find (key); + if (pos != m_values.end()) + return false; + } + m_values[key] = value_sp; + return true; + } + return false; +} + +bool +OptionValueDictionary::DeleteValueForKey (const ConstString &key) +{ + collection::iterator pos = m_values.find (key); + if (pos != m_values.end()) + { + m_values.erase(pos); + return true; + } + return false; +} + +lldb::OptionValueSP +OptionValueDictionary::DeepCopy () const +{ + OptionValueDictionary *copied_dict = new OptionValueDictionary (m_type_mask, m_raw_value_dump); + lldb::OptionValueSP copied_value_sp(copied_dict); + collection::const_iterator pos, end = m_values.end(); + for (pos = m_values.begin(); pos != end; ++pos) + { + StreamString strm; + strm.Printf("%s=", pos->first.GetCString()); + copied_dict->SetValueForKey (pos->first, pos->second->DeepCopy(), true); + } + return copied_value_sp; +} + diff --git a/contrib/llvm/tools/lldb/source/Interpreter/OptionValueEnumeration.cpp b/contrib/llvm/tools/lldb/source/Interpreter/OptionValueEnumeration.cpp new file mode 100644 index 00000000000..f282235d58e --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Interpreter/OptionValueEnumeration.cpp @@ -0,0 +1,166 @@ +//===-- OptionValueEnumeration.cpp ------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Interpreter/OptionValueEnumeration.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/StringList.h" + +using namespace lldb; +using namespace lldb_private; + +OptionValueEnumeration::OptionValueEnumeration (const OptionEnumValueElement *enumerators, + enum_type value) : + OptionValue(), + m_current_value (value), + m_default_value (value), + m_enumerations () +{ + SetEnumerations(enumerators); +} + +OptionValueEnumeration::~OptionValueEnumeration() +{ +} + +void +OptionValueEnumeration::DumpValue (const ExecutionContext *exe_ctx, Stream &strm, uint32_t dump_mask) +{ + if (dump_mask & eDumpOptionType) + strm.Printf ("(%s)", GetTypeAsCString ()); + if (dump_mask & eDumpOptionValue) + { + if (dump_mask & eDumpOptionType) + strm.PutCString (" = "); + const size_t count = m_enumerations.GetSize (); + for (size_t i=0; ivalue.value; + } + else + { + StreamString error_strm; + error_strm.Printf("invalid enumeration value '%s'", value); + const size_t count = m_enumerations.GetSize (); + if (count) + { + error_strm.Printf(", valid values are: %s", m_enumerations.GetCStringAtIndex(0)); + for (size_t i=1; i 0 ? "\n" : ""); + strm.IndentMore(); + const uint32_t size = m_current_value.GetSize(); + for (uint32_t i = 0; i 1) + { + uint32_t idx = Args::StringToUInt32(args.GetArgumentAtIndex(0), UINT32_MAX); + const uint32_t count = m_current_value.GetSize(); + if (idx > count) + { + error.SetErrorStringWithFormat("invalid file list index %u, index must be 0 through %u", idx, count); + } + else + { + for (size_t i=1; i 0) + { + m_value_was_set = true; + for (size_t i=0; i 1) + { + uint32_t idx = Args::StringToUInt32(args.GetArgumentAtIndex(0), UINT32_MAX); + const uint32_t count = m_current_value.GetSize(); + if (idx > count) + { + error.SetErrorStringWithFormat("invalid insert file list index %u, index must be 0 through %u", idx, count); + } + else + { + if (op == eVarSetOperationInsertAfter) + ++idx; + for (size_t i=1; i 0) + { + std::vector remove_indexes; + bool all_indexes_valid = true; + size_t i; + for (i=0; all_indexes_valid && i 0) ? "\n" : ""); + m_path_mappings.Dump(&strm); + } +} + +Error +OptionValuePathMappings::SetValueFromCString (const char *value, VarSetOperationType op) +{ + Error error; + Args args(value); + const size_t argc = args.GetArgumentCount(); + + switch (op) + { + case eVarSetOperationClear: + Clear (); + break; + + case eVarSetOperationReplace: + // Must be at least one index + 1 pair of paths, and the pair count must be even + if (argc >= 3 && (((argc - 1) & 1) == 0)) + { + uint32_t idx = Args::StringToUInt32(args.GetArgumentAtIndex(0), UINT32_MAX); + const uint32_t count = m_path_mappings.GetSize(); + if (idx > count) + { + error.SetErrorStringWithFormat("invalid file list index %u, index must be 0 through %u", idx, count); + } + else + { + for (size_t i=1; i= 3 && (((argc - 1) & 1) == 0)) + { + uint32_t idx = Args::StringToUInt32(args.GetArgumentAtIndex(0), UINT32_MAX); + const uint32_t count = m_path_mappings.GetSize(); + if (idx > count) + { + error.SetErrorStringWithFormat("invalid file list index %u, index must be 0 through %u", idx, count); + } + else + { + if (op == eVarSetOperationInsertAfter) + ++idx; + for (size_t i=1; i 0) + { + std::vector remove_indexes; + bool all_indexes_valid = true; + size_t i; + for (i=0; all_indexes_valid && iDeepCopy()); + m_properties[i].SetOptionValue(new_value_sp); + } + } +} + + + +size_t +OptionValueProperties::GetNumProperties() const +{ + return m_properties.size(); +} + + +void +OptionValueProperties::Initialize (const PropertyDefinition *defs) +{ + for (size_t i=0; defs[i].name; ++i) + { + Property property(defs[i]); + assert(property.IsValid()); + m_name_to_index.Append(property.GetName().GetCString(),m_properties.size()); + property.GetValue()->SetParent(shared_from_this()); + m_properties.push_back(property); + } + m_name_to_index.Sort(); +} + +void +OptionValueProperties::AppendProperty(const ConstString &name, + const ConstString &desc, + bool is_global, + const OptionValueSP &value_sp) +{ + Property property(name, desc, is_global, value_sp); + m_name_to_index.Append(name.GetCString(),m_properties.size()); + m_properties.push_back(property); + value_sp->SetParent (shared_from_this()); + m_name_to_index.Sort(); +} + + + +//bool +//OptionValueProperties::GetQualifiedName (Stream &strm) +//{ +// bool dumped_something = false; +//// lldb::OptionValuePropertiesSP parent_sp(GetParent ()); +//// if (parent_sp) +//// { +//// parent_sp->GetQualifiedName (strm); +//// strm.PutChar('.'); +//// dumped_something = true; +//// } +// if (m_name) +// { +// strm << m_name; +// dumped_something = true; +// } +// return dumped_something; +//} +// +lldb::OptionValueSP +OptionValueProperties::GetValueForKey (const ExecutionContext *exe_ctx, + const ConstString &key, + bool will_modify) const +{ + lldb::OptionValueSP value_sp; + size_t idx = m_name_to_index.Find (key.GetCString(), SIZE_MAX); + if (idx < m_properties.size()) + value_sp = GetPropertyAtIndex(exe_ctx, will_modify, idx)->GetValue(); + return value_sp; +} + +lldb::OptionValueSP +OptionValueProperties::GetSubValue (const ExecutionContext *exe_ctx, + const char *name, + bool will_modify, + Error &error) const +{ + lldb::OptionValueSP value_sp; + + if (name && name[0]) + { + const char *sub_name = NULL; + ConstString key; + size_t key_len = ::strcspn (name, ".[{"); + + if (name[key_len]) + { + key.SetCStringWithLength (name, key_len); + sub_name = name + key_len; + } + else + key.SetCString (name); + + value_sp = GetValueForKey (exe_ctx, key, will_modify); + if (sub_name && value_sp) + { + switch (sub_name[0]) + { + case '.': + return value_sp->GetSubValue (exe_ctx, sub_name + 1, will_modify, error); + + case '{': + // Predicate matching for predicates like + // "{}" + // strings are parsed by the current OptionValueProperties subclass + // to mean whatever they want to. For instance a subclass of + // OptionValueProperties for a lldb_private::Target might implement: + // "target.run-args{arch==i386}" -- only set run args if the arch is i386 + // "target.run-args{path=/tmp/a/b/c/a.out}" -- only set run args if the path matches + // "target.run-args{basename==test&&arch==x86_64}" -- only set run args if exectable basename is "test" and arch is "x86_64" + if (sub_name[1]) + { + const char *predicate_start = sub_name + 1; + const char *predicate_end = strchr(predicate_start, '}'); + if (predicate_end) + { + std::string predicate(predicate_start, predicate_end); + if (PredicateMatches(exe_ctx, predicate.c_str())) + { + if (predicate_end[1]) + { + // Still more subvalue string to evaluate + return value_sp->GetSubValue (exe_ctx, predicate_end + 1, will_modify, error); + } + else + { + // We have a match! + break; + } + } + } + } + // Predicate didn't match or wasn't correctly formed + value_sp.reset(); + break; + + case '[': + // Array or dictionary access for subvalues like: + // "[12]" -- access 12th array element + // "['hello']" -- dictionary access of key named hello + return value_sp->GetSubValue (exe_ctx, sub_name, will_modify, error); + + default: + value_sp.reset(); + break; + } + } + } + return value_sp; +} + +Error +OptionValueProperties::SetSubValue (const ExecutionContext *exe_ctx, + VarSetOperationType op, + const char *name, + const char *value) +{ + Error error; + const bool will_modify = true; + lldb::OptionValueSP value_sp (GetSubValue (exe_ctx, name, will_modify, error)); + if (value_sp) + error = value_sp->SetValueFromCString(value, op); + else + { + if (error.AsCString() == NULL) + error.SetErrorStringWithFormat("invalid value path '%s'", name); + } + return error; +} + + +ConstString +OptionValueProperties::GetPropertyNameAtIndex (uint32_t idx) const +{ + const Property *property = GetPropertyAtIndex(NULL, false, idx); + if (property) + return property->GetName(); + return ConstString(); + +} + +const char * +OptionValueProperties::GetPropertyDescriptionAtIndex (uint32_t idx) const +{ + const Property *property = GetPropertyAtIndex(NULL, false, idx); + if (property) + return property->GetDescription(); + return NULL; +} + +uint32_t +OptionValueProperties::GetPropertyIndex (const ConstString &name) const +{ + return m_name_to_index.Find (name.GetCString(), SIZE_MAX); +} + +const Property * +OptionValueProperties::GetProperty (const ExecutionContext *exe_ctx, bool will_modify, const ConstString &name) const +{ + return GetPropertyAtIndex (exe_ctx, will_modify, m_name_to_index.Find (name.GetCString(), SIZE_MAX)); +} + +const Property * +OptionValueProperties::GetPropertyAtIndex (const ExecutionContext *exe_ctx, bool will_modify, uint32_t idx) const +{ + return ProtectedGetPropertyAtIndex (idx); +} + +lldb::OptionValueSP +OptionValueProperties::GetPropertyValueAtIndex (const ExecutionContext *exe_ctx, + bool will_modify, + uint32_t idx) const +{ + const Property *setting = GetPropertyAtIndex (exe_ctx, will_modify, idx); + if (setting) + return setting->GetValue(); + return OptionValueSP(); +} + +OptionValuePathMappings * +OptionValueProperties::GetPropertyAtIndexAsOptionValuePathMappings (const ExecutionContext *exe_ctx, bool will_modify, uint32_t idx) const +{ + OptionValueSP value_sp(GetPropertyValueAtIndex (exe_ctx, will_modify, idx)); + if (value_sp) + return value_sp->GetAsPathMappings(); + return NULL; +} + +OptionValueFileSpecList * +OptionValueProperties::GetPropertyAtIndexAsOptionValueFileSpecList (const ExecutionContext *exe_ctx, bool will_modify, uint32_t idx) const +{ + OptionValueSP value_sp(GetPropertyValueAtIndex (exe_ctx, will_modify, idx)); + if (value_sp) + return value_sp->GetAsFileSpecList(); + return NULL; +} + +OptionValueArch * +OptionValueProperties::GetPropertyAtIndexAsOptionValueArch (const ExecutionContext *exe_ctx, uint32_t idx) const +{ + const Property *property = GetPropertyAtIndex (exe_ctx, false, idx); + if (property) + return property->GetValue()->GetAsArch(); + return NULL; +} + +bool +OptionValueProperties::GetPropertyAtIndexAsArgs (const ExecutionContext *exe_ctx, uint32_t idx, Args &args) const +{ + const Property *property = GetPropertyAtIndex (exe_ctx, false, idx); + if (property) + { + OptionValue *value = property->GetValue().get(); + if (value) + { + const OptionValueArray *array = value->GetAsArray(); + if (array) + return array->GetArgs(args); + else + { + const OptionValueDictionary *dict = value->GetAsDictionary(); + if (dict) + return dict->GetArgs(args); + } + } + } + return false; +} + +bool +OptionValueProperties::SetPropertyAtIndexFromArgs (const ExecutionContext *exe_ctx, uint32_t idx, const Args &args) +{ + const Property *property = GetPropertyAtIndex (exe_ctx, true, idx); + if (property) + { + OptionValue *value = property->GetValue().get(); + if (value) + { + OptionValueArray *array = value->GetAsArray(); + if (array) + return array->SetArgs(args, eVarSetOperationAssign).Success(); + else + { + OptionValueDictionary *dict = value->GetAsDictionary(); + if (dict) + return dict->SetArgs(args, eVarSetOperationAssign).Success(); + } + } + } + return false; +} + +bool +OptionValueProperties::GetPropertyAtIndexAsBoolean (const ExecutionContext *exe_ctx, uint32_t idx, bool fail_value) const +{ + const Property *property = GetPropertyAtIndex (exe_ctx, false, idx); + if (property) + { + OptionValue *value = property->GetValue().get(); + if (value) + return value->GetBooleanValue(fail_value); + } + return fail_value; +} + +bool +OptionValueProperties::SetPropertyAtIndexAsBoolean (const ExecutionContext *exe_ctx, uint32_t idx, bool new_value) +{ + const Property *property = GetPropertyAtIndex (exe_ctx, true, idx); + if (property) + { + OptionValue *value = property->GetValue().get(); + if (value) + { + value->SetBooleanValue(new_value); + return true; + } + } + return false; +} + +OptionValueDictionary * +OptionValueProperties::GetPropertyAtIndexAsOptionValueDictionary (const ExecutionContext *exe_ctx, uint32_t idx) const +{ + const Property *property = GetPropertyAtIndex (exe_ctx, false, idx); + if (property) + return property->GetValue()->GetAsDictionary(); + return NULL; +} + +int64_t +OptionValueProperties::GetPropertyAtIndexAsEnumeration (const ExecutionContext *exe_ctx, uint32_t idx, int64_t fail_value) const +{ + const Property *property = GetPropertyAtIndex (exe_ctx, false, idx); + if (property) + { + OptionValue *value = property->GetValue().get(); + if (value) + return value->GetEnumerationValue(fail_value); + } + return fail_value; +} + +bool +OptionValueProperties::SetPropertyAtIndexAsEnumeration (const ExecutionContext *exe_ctx, uint32_t idx, int64_t new_value) +{ + const Property *property = GetPropertyAtIndex (exe_ctx, true, idx); + if (property) + { + OptionValue *value = property->GetValue().get(); + if (value) + return value->SetEnumerationValue(new_value); + } + return false; +} + + +OptionValueFileSpec * +OptionValueProperties::GetPropertyAtIndexAsOptionValueFileSpec (const ExecutionContext *exe_ctx, bool will_modify, uint32_t idx) const +{ + const Property *property = GetPropertyAtIndex (exe_ctx, false, idx); + if (property) + { + OptionValue *value = property->GetValue().get(); + if (value) + return value->GetAsFileSpec(); + } + return NULL; +} + + +FileSpec +OptionValueProperties::GetPropertyAtIndexAsFileSpec (const ExecutionContext *exe_ctx, uint32_t idx) const +{ + const Property *property = GetPropertyAtIndex (exe_ctx, false, idx); + if (property) + { + OptionValue *value = property->GetValue().get(); + if (value) + return value->GetFileSpecValue(); + } + return FileSpec(); +} + + +bool +OptionValueProperties::SetPropertyAtIndexAsFileSpec (const ExecutionContext *exe_ctx, uint32_t idx, const FileSpec &new_file_spec) +{ + const Property *property = GetPropertyAtIndex (exe_ctx, true, idx); + if (property) + { + OptionValue *value = property->GetValue().get(); + if (value) + return value->SetFileSpecValue(new_file_spec); + } + return false; +} + +const RegularExpression * +OptionValueProperties::GetPropertyAtIndexAsOptionValueRegex (const ExecutionContext *exe_ctx, uint32_t idx) const +{ + const Property *property = GetPropertyAtIndex (exe_ctx, false, idx); + if (property) + { + OptionValue *value = property->GetValue().get(); + if (value) + return value->GetRegexValue(); + } + return NULL; +} + +OptionValueSInt64 * +OptionValueProperties::GetPropertyAtIndexAsOptionValueSInt64 (const ExecutionContext *exe_ctx, uint32_t idx) const +{ + const Property *property = GetPropertyAtIndex (exe_ctx, false, idx); + if (property) + { + OptionValue *value = property->GetValue().get(); + if (value) + return value->GetAsSInt64(); + } + return NULL; +} + +int64_t +OptionValueProperties::GetPropertyAtIndexAsSInt64 (const ExecutionContext *exe_ctx, uint32_t idx, int64_t fail_value) const +{ + const Property *property = GetPropertyAtIndex (exe_ctx, false, idx); + if (property) + { + OptionValue *value = property->GetValue().get(); + if (value) + return value->GetSInt64Value(fail_value); + } + return fail_value; +} + +bool +OptionValueProperties::SetPropertyAtIndexAsSInt64 (const ExecutionContext *exe_ctx, uint32_t idx, int64_t new_value) +{ + const Property *property = GetPropertyAtIndex (exe_ctx, true, idx); + if (property) + { + OptionValue *value = property->GetValue().get(); + if (value) + return value->SetSInt64Value(new_value); + } + return false; +} + +const char * +OptionValueProperties::GetPropertyAtIndexAsString (const ExecutionContext *exe_ctx, uint32_t idx, const char *fail_value) const +{ + const Property *property = GetPropertyAtIndex (exe_ctx, false, idx); + if (property) + { + OptionValue *value = property->GetValue().get(); + if (value) + return value->GetStringValue(fail_value); + } + return fail_value; +} + +bool +OptionValueProperties::SetPropertyAtIndexAsString (const ExecutionContext *exe_ctx, uint32_t idx, const char *new_value) +{ + const Property *property = GetPropertyAtIndex (exe_ctx, true, idx); + if (property) + { + OptionValue *value = property->GetValue().get(); + if (value) + return value->SetStringValue(new_value); + } + return false; +} + +OptionValueString * +OptionValueProperties::GetPropertyAtIndexAsOptionValueString (const ExecutionContext *exe_ctx, bool will_modify, uint32_t idx) const +{ + OptionValueSP value_sp(GetPropertyValueAtIndex (exe_ctx, will_modify, idx)); + if (value_sp) + return value_sp->GetAsString(); + return NULL; +} + + +uint64_t +OptionValueProperties::GetPropertyAtIndexAsUInt64 (const ExecutionContext *exe_ctx, uint32_t idx, uint64_t fail_value) const +{ + const Property *property = GetPropertyAtIndex (exe_ctx, false, idx); + if (property) + { + OptionValue *value = property->GetValue().get(); + if (value) + return value->GetUInt64Value(fail_value); + } + return fail_value; +} + +bool +OptionValueProperties::SetPropertyAtIndexAsUInt64 (const ExecutionContext *exe_ctx, uint32_t idx, uint64_t new_value) +{ + const Property *property = GetPropertyAtIndex (exe_ctx, true, idx); + if (property) + { + OptionValue *value = property->GetValue().get(); + if (value) + return value->SetUInt64Value(new_value); + } + return false; +} + +bool +OptionValueProperties::Clear () +{ + const size_t num_properties = m_properties.size(); + for (size_t i=0; iClear(); + return true; +} + + +Error +OptionValueProperties::SetValueFromCString (const char *value, VarSetOperationType op) +{ + Error error; + +// Args args(value_cstr); +// const size_t argc = args.GetArgumentCount(); + switch (op) + { + case eVarSetOperationClear: + Clear (); + break; + + case eVarSetOperationReplace: + case eVarSetOperationAssign: + case eVarSetOperationRemove: + case eVarSetOperationInsertBefore: + case eVarSetOperationInsertAfter: + case eVarSetOperationAppend: + case eVarSetOperationInvalid: + error = OptionValue::SetValueFromCString (value, op); + break; + } + + return error; +} + +void +OptionValueProperties::DumpValue (const ExecutionContext *exe_ctx, Stream &strm, uint32_t dump_mask) +{ + const size_t num_properties = m_properties.size(); + for (size_t i=0; iGetValue().get(); + assert (option_value); + const bool transparent_value = option_value->ValueIsTransparent (); + property->Dump (exe_ctx, + strm, + dump_mask); + if (!transparent_value) + strm.EOL(); + } + } +} + +Error +OptionValueProperties::DumpPropertyValue (const ExecutionContext *exe_ctx, + Stream &strm, + const char *property_path, + uint32_t dump_mask) +{ + Error error; + const bool will_modify = false; + lldb::OptionValueSP value_sp (GetSubValue (exe_ctx, property_path, will_modify, error)); + if (value_sp) + { + if (!value_sp->ValueIsTransparent ()) + { + if (dump_mask & eDumpOptionName) + strm.PutCString (property_path); + if (dump_mask & ~eDumpOptionName) + strm.PutChar (' '); + } + value_sp->DumpValue (exe_ctx, strm, dump_mask); + } + return error; +} + +lldb::OptionValueSP +OptionValueProperties::DeepCopy () const +{ + assert(!"this shouldn't happen"); +} + +const Property * +OptionValueProperties::GetPropertyAtPath (const ExecutionContext *exe_ctx, + bool will_modify, + const char *name) const +{ + const Property *property = NULL; + if (name && name[0]) + { + const char *sub_name = NULL; + ConstString key; + size_t key_len = ::strcspn (name, ".[{"); + + if (name[key_len]) + { + key.SetCStringWithLength (name, key_len); + sub_name = name + key_len; + } + else + key.SetCString (name); + + property = GetProperty (exe_ctx, will_modify, key); + if (sub_name && property) + { + if (sub_name[0] == '.') + { + OptionValueProperties *sub_properties = property->GetValue()->GetAsProperties(); + if (sub_properties) + return sub_properties->GetPropertyAtPath(exe_ctx, will_modify, sub_name + 1); + } + property = NULL; + } + } + return property; +} + +void +OptionValueProperties::DumpAllDescriptions (CommandInterpreter &interpreter, + Stream &strm) const +{ + size_t max_name_len = 0; + const size_t num_properties = m_properties.size(); + for (size_t i=0; i(property->GetName().GetLength(), max_name_len); + } + for (size_t i=0; iDumpDescription (interpreter, strm, max_name_len, false); + } +} + +void +OptionValueProperties::Apropos (const char *keyword, std::vector &matching_properties) const +{ + const size_t num_properties = m_properties.size(); + StreamString strm; + for (size_t i=0; iGetValue()->GetAsProperties(); + if (properties) + { + properties->Apropos (keyword, matching_properties); + } + else + { + bool match = false; + const char *name = property->GetName().GetCString(); + if (name && ::strcasestr(name, keyword)) + match = true; + else + { + const char *desc = property->GetDescription(); + if (desc && ::strcasestr(desc, keyword)) + match = true; + } + if (match) + { + matching_properties.push_back (property); + } + } + } + } +} + +lldb::OptionValuePropertiesSP +OptionValueProperties::GetSubProperty (const ExecutionContext *exe_ctx, + const ConstString &name) +{ + lldb::OptionValueSP option_value_sp(GetValueForKey(exe_ctx, name, false)); + if (option_value_sp) + { + OptionValueProperties *ov_properties = option_value_sp->GetAsProperties (); + if (ov_properties) + return ov_properties->shared_from_this(); + } + return lldb::OptionValuePropertiesSP(); +} + + + diff --git a/contrib/llvm/tools/lldb/source/Interpreter/OptionValueRegex.cpp b/contrib/llvm/tools/lldb/source/Interpreter/OptionValueRegex.cpp new file mode 100644 index 00000000000..f1ba0ed04d6 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Interpreter/OptionValueRegex.cpp @@ -0,0 +1,86 @@ +//===-- OptionValueRegex.cpp ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Interpreter/OptionValueRegex.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/Stream.h" + +using namespace lldb; +using namespace lldb_private; + +void +OptionValueRegex::DumpValue (const ExecutionContext *exe_ctx, Stream &strm, uint32_t dump_mask) +{ + if (dump_mask & eDumpOptionType) + strm.Printf ("(%s)", GetTypeAsCString ()); + if (dump_mask & eDumpOptionValue) + { + if (dump_mask & eDumpOptionType) + strm.PutCString (" = "); + if (m_regex.IsValid()) + { + const char *regex_text = m_regex.GetText(); + if (regex_text && regex_text[0]) + strm.Printf ("%s", regex_text); + } + else + { + + } + } +} + +Error +OptionValueRegex::SetValueFromCString (const char *value_cstr, + VarSetOperationType op) +{ + Error error; + switch (op) + { + case eVarSetOperationInvalid: + case eVarSetOperationInsertBefore: + case eVarSetOperationInsertAfter: + case eVarSetOperationRemove: + case eVarSetOperationAppend: + error = OptionValue::SetValueFromCString (value_cstr, op); + break; + + case eVarSetOperationClear: + Clear(); + break; + + case eVarSetOperationReplace: + case eVarSetOperationAssign: + if (m_regex.Compile (value_cstr, m_regex.GetCompileFlags())) + { + m_value_was_set = true; + } + else + { + char regex_error[1024]; + if (m_regex.GetErrorAsCString(regex_error, sizeof(regex_error))) + error.SetErrorString (regex_error); + else + error.SetErrorStringWithFormat ("regex error %u", m_regex.GetErrorCode()); + } + break; + } + return error; +} + + +lldb::OptionValueSP +OptionValueRegex::DeepCopy () const +{ + return OptionValueSP(new OptionValueRegex(m_regex.GetText(), m_regex.GetCompileFlags())); +} diff --git a/contrib/llvm/tools/lldb/source/Interpreter/OptionValueSInt64.cpp b/contrib/llvm/tools/lldb/source/Interpreter/OptionValueSInt64.cpp new file mode 100644 index 00000000000..04bf9306ade --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Interpreter/OptionValueSInt64.cpp @@ -0,0 +1,89 @@ +//===-- OptionValueSInt64.cpp -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Interpreter/OptionValueSInt64.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/Stream.h" +#include "lldb/Interpreter/Args.h" + +using namespace lldb; +using namespace lldb_private; + +void +OptionValueSInt64::DumpValue (const ExecutionContext *exe_ctx, Stream &strm, uint32_t dump_mask) +{ + //printf ("%p: DumpValue (exe_ctx=%p, strm, mask) m_current_value = %" PRIi64 "\n", this, exe_ctx, m_current_value); + if (dump_mask & eDumpOptionType) + strm.Printf ("(%s)", GetTypeAsCString ()); +// if (dump_mask & eDumpOptionName) +// DumpQualifiedName (strm); + if (dump_mask & eDumpOptionValue) + { + if (dump_mask & eDumpOptionType) + strm.PutCString (" = "); + strm.Printf ("%" PRIi64, m_current_value); + } +} + +Error +OptionValueSInt64::SetValueFromCString (const char *value_cstr, VarSetOperationType op) +{ + //printf ("%p: SetValueFromCString (s=\"%s\", op=%i)\n", this, value_cstr, op); + Error error; + switch (op) + { + case eVarSetOperationClear: + Clear(); + break; + + case eVarSetOperationReplace: + case eVarSetOperationAssign: + { + bool success = false; + int64_t value = Args::StringToSInt64 (value_cstr, 0, 0, &success); + if (success) + { + if (value >= m_min_value && value <= m_max_value) + { + m_value_was_set = true; + m_current_value = value; + } + else + error.SetErrorStringWithFormat ("%" PRIi64 " is out of range, valid values must be between %" PRIi64 " and %" PRIi64 ".", + value, + m_min_value, + m_max_value); + } + else + { + error.SetErrorStringWithFormat ("invalid int64_t string value: '%s'", value_cstr); + } + } + break; + + case eVarSetOperationInsertBefore: + case eVarSetOperationInsertAfter: + case eVarSetOperationRemove: + case eVarSetOperationAppend: + case eVarSetOperationInvalid: + error = OptionValue::SetValueFromCString (value_cstr, op); + break; + } + return error; +} + +lldb::OptionValueSP +OptionValueSInt64::DeepCopy () const +{ + return OptionValueSP(new OptionValueSInt64(*this)); +} diff --git a/contrib/llvm/tools/lldb/source/Interpreter/OptionValueString.cpp b/contrib/llvm/tools/lldb/source/Interpreter/OptionValueString.cpp new file mode 100644 index 00000000000..df047bd9899 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Interpreter/OptionValueString.cpp @@ -0,0 +1,186 @@ +//===-- OptionValueString.cpp ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Interpreter/OptionValueString.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/Stream.h" +#include "lldb/Interpreter/Args.h" + +using namespace lldb; +using namespace lldb_private; + +void +OptionValueString::DumpValue (const ExecutionContext *exe_ctx, Stream &strm, uint32_t dump_mask) +{ + if (dump_mask & eDumpOptionType) + strm.Printf ("(%s)", GetTypeAsCString ()); + if (dump_mask & eDumpOptionValue) + { + if (dump_mask & eDumpOptionType) + strm.PutCString (" = "); + if (!m_current_value.empty() || m_value_was_set) + { + if (m_options.Test (eOptionEncodeCharacterEscapeSequences)) + { + std::string expanded_escape_value; + Args::ExpandEscapedCharacters(m_current_value.c_str(), expanded_escape_value); + if (dump_mask & eDumpOptionRaw) + strm.Printf ("%s", expanded_escape_value.c_str()); + else + strm.Printf ("\"%s\"", expanded_escape_value.c_str()); + } + else + { + if (dump_mask & eDumpOptionRaw) + strm.Printf ("%s", m_current_value.c_str()); + else + strm.Printf ("\"%s\"", m_current_value.c_str()); + } + } + } +} + +Error +OptionValueString::SetValueFromCString (const char *value_cstr, + VarSetOperationType op) +{ + Error error; + + std::string value_str_no_quotes; + if (value_cstr) + { + switch (value_cstr[0]) + { + case '"': + case '\'': + { + size_t len = strlen(value_cstr); + if (len <= 1 || value_cstr[len-1] != value_cstr[0]) + { + error.SetErrorString("mismatched quotes"); + return error; + } + value_str_no_quotes.assign (value_cstr + 1, len - 2); + value_cstr = value_str_no_quotes.c_str(); + } + break; + } + } + + switch (op) + { + case eVarSetOperationInvalid: + case eVarSetOperationInsertBefore: + case eVarSetOperationInsertAfter: + case eVarSetOperationRemove: + if (m_validator) + { + error = m_validator(value_cstr,m_validator_baton); + if (error.Fail()) + return error; + } + error = OptionValue::SetValueFromCString (value_cstr, op); + break; + + case eVarSetOperationAppend: + { + std::string new_value(m_current_value); + if (value_cstr && value_cstr[0]) + { + if (m_options.Test (eOptionEncodeCharacterEscapeSequences)) + { + std::string str; + Args::EncodeEscapeSequences (value_cstr, str); + new_value.append(str); + } + else + new_value.append(value_cstr); + } + if (m_validator) + { + error = m_validator(new_value.c_str(),m_validator_baton); + if (error.Fail()) + return error; + } + m_current_value.assign(new_value); + } + break; + + case eVarSetOperationClear: + Clear (); + break; + + case eVarSetOperationReplace: + case eVarSetOperationAssign: + if (m_validator) + { + error = m_validator(value_cstr,m_validator_baton); + if (error.Fail()) + return error; + } + m_value_was_set = true; + if (m_options.Test (eOptionEncodeCharacterEscapeSequences)) + { + Args::EncodeEscapeSequences (value_cstr, m_current_value); + } + else + { + SetCurrentValue (value_cstr); + } + break; + } + return error; +} + + +lldb::OptionValueSP +OptionValueString::DeepCopy () const +{ + return OptionValueSP(new OptionValueString(*this)); +} + +Error +OptionValueString::SetCurrentValue (const char *value) +{ + if (m_validator) + { + Error error(m_validator(value,m_validator_baton)); + if (error.Fail()) + return error; + } + if (value && value[0]) + m_current_value.assign (value); + else + m_current_value.clear(); + return Error(); +} + +Error +OptionValueString::AppendToCurrentValue (const char *value) +{ + if (value && value[0]) + { + if (m_validator) + { + std::string new_value(m_current_value); + new_value.append(value); + Error error(m_validator(value,m_validator_baton)); + if (error.Fail()) + return error; + m_current_value.assign(new_value); + } + else + m_current_value.append (value); + } + return Error(); +} diff --git a/contrib/llvm/tools/lldb/source/Interpreter/OptionValueUInt64.cpp b/contrib/llvm/tools/lldb/source/Interpreter/OptionValueUInt64.cpp new file mode 100644 index 00000000000..56b3a1c7470 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Interpreter/OptionValueUInt64.cpp @@ -0,0 +1,89 @@ +//===-- OptionValueUInt64.cpp ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Interpreter/OptionValueUInt64.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/Stream.h" +#include "lldb/Interpreter/Args.h" + +using namespace lldb; +using namespace lldb_private; + +lldb::OptionValueSP +OptionValueUInt64::Create (const char *value_cstr, Error &error) +{ + lldb::OptionValueSP value_sp (new OptionValueUInt64()); + error = value_sp->SetValueFromCString (value_cstr); + if (error.Fail()) + value_sp.reset(); + return value_sp; +} + + +void +OptionValueUInt64::DumpValue (const ExecutionContext *exe_ctx, Stream &strm, uint32_t dump_mask) +{ + if (dump_mask & eDumpOptionType) + strm.Printf ("(%s)", GetTypeAsCString ()); + if (dump_mask & eDumpOptionValue) + { + if (dump_mask & eDumpOptionType) + strm.PutCString (" = "); + strm.Printf ("%" PRIu64, m_current_value); + } +} + +Error +OptionValueUInt64::SetValueFromCString (const char *value_cstr, VarSetOperationType op) +{ + Error error; + switch (op) + { + case eVarSetOperationClear: + Clear (); + break; + + case eVarSetOperationReplace: + case eVarSetOperationAssign: + { + bool success = false; + uint64_t value = Args::StringToUInt64 (value_cstr, 0, 0, &success); + if (success) + { + m_value_was_set = true; + m_current_value = value; + } + else + { + error.SetErrorStringWithFormat ("invalid uint64_t string value: '%s'", value_cstr); + } + } + break; + + case eVarSetOperationInsertBefore: + case eVarSetOperationInsertAfter: + case eVarSetOperationRemove: + case eVarSetOperationAppend: + case eVarSetOperationInvalid: + error = OptionValue::SetValueFromCString (value_cstr, op); + break; + } + return error; +} + +lldb::OptionValueSP +OptionValueUInt64::DeepCopy () const +{ + return OptionValueSP(new OptionValueUInt64(*this)); +} + diff --git a/contrib/llvm/tools/lldb/source/Interpreter/OptionValueUUID.cpp b/contrib/llvm/tools/lldb/source/Interpreter/OptionValueUUID.cpp new file mode 100644 index 00000000000..340f1e5e998 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Interpreter/OptionValueUUID.cpp @@ -0,0 +1,123 @@ +//===-- OptionValueUUID.cpp ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "lldb/Interpreter/OptionValueUUID.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/Module.h" +#include "lldb/Core/Stream.h" +#include "lldb/Core/StringList.h" +#include "lldb/Interpreter/CommandInterpreter.h" + +using namespace lldb; +using namespace lldb_private; + +void +OptionValueUUID::DumpValue (const ExecutionContext *exe_ctx, Stream &strm, uint32_t dump_mask) +{ + if (dump_mask & eDumpOptionType) + strm.Printf ("(%s)", GetTypeAsCString ()); + if (dump_mask & eDumpOptionValue) + { + if (dump_mask & eDumpOptionType) + strm.PutCString (" = "); + m_uuid.Dump (&strm); + } +} + +Error +OptionValueUUID::SetValueFromCString (const char *value_cstr, + VarSetOperationType op) +{ + Error error; + switch (op) + { + case eVarSetOperationClear: + Clear(); + break; + + case eVarSetOperationReplace: + case eVarSetOperationAssign: + { + if (m_uuid.SetFromCString(value_cstr) == 0) + error.SetErrorStringWithFormat ("invalid uuid string value '%s'", value_cstr); + else + m_value_was_set = true; + } + break; + + case eVarSetOperationInsertBefore: + case eVarSetOperationInsertAfter: + case eVarSetOperationRemove: + case eVarSetOperationAppend: + case eVarSetOperationInvalid: + error = OptionValue::SetValueFromCString (value_cstr, op); + break; + } + return error; +} + +lldb::OptionValueSP +OptionValueUUID::DeepCopy () const +{ + return OptionValueSP(new OptionValueUUID(*this)); +} + +size_t +OptionValueUUID::AutoComplete (CommandInterpreter &interpreter, + const char *s, + int match_start_point, + int max_return_elements, + bool &word_complete, + StringList &matches) +{ + word_complete = false; + matches.Clear(); + ExecutionContext exe_ctx(interpreter.GetExecutionContext()); + Target *target = exe_ctx.GetTargetPtr(); + if (target) + { + const size_t num_modules = target->GetImages().GetSize(); + if (num_modules > 0) + { + UUID::ValueType uuid_bytes; + const size_t num_bytes_decoded = UUID::DecodeUUIDBytesFromCString(s, uuid_bytes, NULL); + for (size_t i=0; iGetImages().GetModuleAtIndex(i)); + if (module_sp) + { + const UUID &module_uuid = module_sp->GetUUID(); + if (module_uuid.IsValid()) + { + bool add_uuid = false; + if (num_bytes_decoded == 0) + add_uuid = true; + else + add_uuid = ::memcmp(module_uuid.GetBytes(), uuid_bytes, num_bytes_decoded) == 0; + if (add_uuid) + { + std::string uuid_str; + uuid_str = module_uuid.GetAsString(); + if (!uuid_str.empty()) + matches.AppendString(uuid_str.c_str()); + } + } + } + } + } + } + return matches.GetSize(); +} + diff --git a/contrib/llvm/tools/lldb/source/Interpreter/Options.cpp b/contrib/llvm/tools/lldb/source/Interpreter/Options.cpp new file mode 100644 index 00000000000..293d7535663 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Interpreter/Options.cpp @@ -0,0 +1,1077 @@ +//===-- Options.cpp ---------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "lldb/Interpreter/Options.h" + +// C Includes +// C++ Includes +#include +#include +#include + +// Other libraries and framework includes +// Project includes +#include "lldb/Interpreter/CommandObject.h" +#include "lldb/Interpreter/CommandReturnObject.h" +#include "lldb/Interpreter/CommandCompletions.h" +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Target/Target.h" + +using namespace lldb; +using namespace lldb_private; + +//------------------------------------------------------------------------- +// Options +//------------------------------------------------------------------------- +Options::Options (CommandInterpreter &interpreter) : + m_interpreter (interpreter), + m_getopt_table () +{ + BuildValidOptionSets(); +} + +Options::~Options () +{ +} + +void +Options::NotifyOptionParsingStarting () +{ + m_seen_options.clear(); + // Let the subclass reset its option values + OptionParsingStarting (); +} + +Error +Options::NotifyOptionParsingFinished () +{ + return OptionParsingFinished (); +} + +void +Options::OptionSeen (int option_idx) +{ + m_seen_options.insert (option_idx); +} + +// Returns true is set_a is a subset of set_b; Otherwise returns false. + +bool +Options::IsASubset (const OptionSet& set_a, const OptionSet& set_b) +{ + bool is_a_subset = true; + OptionSet::const_iterator pos_a; + OptionSet::const_iterator pos_b; + + // set_a is a subset of set_b if every member of set_a is also a member of set_b + + for (pos_a = set_a.begin(); pos_a != set_a.end() && is_a_subset; ++pos_a) + { + pos_b = set_b.find(*pos_a); + if (pos_b == set_b.end()) + is_a_subset = false; + } + + return is_a_subset; +} + +// Returns the set difference set_a - set_b, i.e. { x | ElementOf (x, set_a) && !ElementOf (x, set_b) } + +size_t +Options::OptionsSetDiff (const OptionSet& set_a, const OptionSet& set_b, OptionSet& diffs) +{ + size_t num_diffs = 0; + OptionSet::const_iterator pos_a; + OptionSet::const_iterator pos_b; + + for (pos_a = set_a.begin(); pos_a != set_a.end(); ++pos_a) + { + pos_b = set_b.find(*pos_a); + if (pos_b == set_b.end()) + { + ++num_diffs; + diffs.insert(*pos_a); + } + } + + return num_diffs; +} + +// Returns the union of set_a and set_b. Does not put duplicate members into the union. + +void +Options::OptionsSetUnion (const OptionSet &set_a, const OptionSet &set_b, OptionSet &union_set) +{ + OptionSet::const_iterator pos; + OptionSet::iterator pos_union; + + // Put all the elements of set_a into the union. + + for (pos = set_a.begin(); pos != set_a.end(); ++pos) + union_set.insert(*pos); + + // Put all the elements of set_b that are not already there into the union. + for (pos = set_b.begin(); pos != set_b.end(); ++pos) + { + pos_union = union_set.find(*pos); + if (pos_union == union_set.end()) + union_set.insert(*pos); + } +} + +bool +Options::VerifyOptions (CommandReturnObject &result) +{ + bool options_are_valid = false; + + int num_levels = GetRequiredOptions().size(); + if (num_levels) + { + for (int i = 0; i < num_levels && !options_are_valid; ++i) + { + // This is the correct set of options if: 1). m_seen_options contains all of m_required_options[i] + // (i.e. all the required options at this level are a subset of m_seen_options); AND + // 2). { m_seen_options - m_required_options[i] is a subset of m_options_options[i] (i.e. all the rest of + // m_seen_options are in the set of optional options at this level. + + // Check to see if all of m_required_options[i] are a subset of m_seen_options + if (IsASubset (GetRequiredOptions()[i], m_seen_options)) + { + // Construct the set difference: remaining_options = {m_seen_options} - {m_required_options[i]} + OptionSet remaining_options; + OptionsSetDiff (m_seen_options, GetRequiredOptions()[i], remaining_options); + // Check to see if remaining_options is a subset of m_optional_options[i] + if (IsASubset (remaining_options, GetOptionalOptions()[i])) + options_are_valid = true; + } + } + } + else + { + options_are_valid = true; + } + + if (options_are_valid) + { + result.SetStatus (eReturnStatusSuccessFinishNoResult); + } + else + { + result.AppendError ("invalid combination of options for the given command"); + result.SetStatus (eReturnStatusFailed); + } + + return options_are_valid; +} + +// This is called in the Options constructor, though we could call it lazily if that ends up being +// a performance problem. + +void +Options::BuildValidOptionSets () +{ + // Check to see if we already did this. + if (m_required_options.size() != 0) + return; + + // Check to see if there are any options. + int num_options = NumCommandOptions (); + if (num_options == 0) + return; + + const OptionDefinition *opt_defs = GetDefinitions(); + m_required_options.resize(1); + m_optional_options.resize(1); + + // First count the number of option sets we've got. Ignore LLDB_ALL_OPTION_SETS... + + uint32_t num_option_sets = 0; + + for (int i = 0; i < num_options; i++) + { + uint32_t this_usage_mask = opt_defs[i].usage_mask; + if (this_usage_mask == LLDB_OPT_SET_ALL) + { + if (num_option_sets == 0) + num_option_sets = 1; + } + else + { + for (uint32_t j = 0; j < LLDB_MAX_NUM_OPTION_SETS; j++) + { + if (this_usage_mask & (1 << j)) + { + if (num_option_sets <= j) + num_option_sets = j + 1; + } + } + } + } + + if (num_option_sets > 0) + { + m_required_options.resize(num_option_sets); + m_optional_options.resize(num_option_sets); + + for (int i = 0; i < num_options; ++i) + { + for (uint32_t j = 0; j < num_option_sets; j++) + { + if (opt_defs[i].usage_mask & 1 << j) + { + if (opt_defs[i].required) + m_required_options[j].insert(opt_defs[i].short_option); + else + m_optional_options[j].insert(opt_defs[i].short_option); + } + } + } + } +} + +uint32_t +Options::NumCommandOptions () +{ + const OptionDefinition *opt_defs = GetDefinitions (); + if (opt_defs == NULL) + return 0; + + int i = 0; + + if (opt_defs != NULL) + { + while (opt_defs[i].long_option != NULL) + ++i; + } + + return i; +} + +struct option * +Options::GetLongOptions () +{ + // Check to see if this has already been done. + if (m_getopt_table.empty()) + { + // Check to see if there are any options. + const uint32_t num_options = NumCommandOptions(); + if (num_options == 0) + return NULL; + + uint32_t i; + const OptionDefinition *opt_defs = GetDefinitions(); + + std::map option_seen; + + m_getopt_table.resize(num_options + 1); + for (i = 0; i < num_options; ++i) + { + const int short_opt = opt_defs[i].short_option; + + m_getopt_table[i].name = opt_defs[i].long_option; + m_getopt_table[i].has_arg = opt_defs[i].option_has_arg; + m_getopt_table[i].flag = NULL; + m_getopt_table[i].val = short_opt; + + if (option_seen.find(short_opt) == option_seen.end()) + { + option_seen[short_opt] = i; + } + else if (short_opt) + { + m_getopt_table[i].val = 0; + std::map::const_iterator pos = option_seen.find(short_opt); + StreamString strm; + if (isprint8(short_opt)) + Host::SystemLog (Host::eSystemLogError, "option[%u] --%s has a short option -%c that conflicts with option[%u] --%s, short option won't be used for --%s\n", + i, + opt_defs[i].long_option, + short_opt, + pos->second, + m_getopt_table[pos->second].name, + opt_defs[i].long_option); + else + Host::SystemLog (Host::eSystemLogError, "option[%u] --%s has a short option 0x%x that conflicts with option[%u] --%s, short option won't be used for --%s\n", + i, + opt_defs[i].long_option, + short_opt, + pos->second, + m_getopt_table[pos->second].name, + opt_defs[i].long_option); + } + } + + //getopt_long_only requires a NULL final entry in the table: + + m_getopt_table[i].name = NULL; + m_getopt_table[i].has_arg = 0; + m_getopt_table[i].flag = NULL; + m_getopt_table[i].val = 0; + } + + if (m_getopt_table.empty()) + return NULL; + + return &m_getopt_table.front(); +} + + +// This function takes INDENT, which tells how many spaces to output at the front of each line; SPACES, which is +// a string containing 80 spaces; and TEXT, which is the text that is to be output. It outputs the text, on +// multiple lines if necessary, to RESULT, with INDENT spaces at the front of each line. It breaks lines on spaces, +// tabs or newlines, shortening the line if necessary to not break in the middle of a word. It assumes that each +// output line should contain a maximum of OUTPUT_MAX_COLUMNS characters. + + +void +Options::OutputFormattedUsageText +( + Stream &strm, + const char *text, + uint32_t output_max_columns +) +{ + int len = strlen (text); + + // Will it all fit on one line? + + if ((len + strm.GetIndentLevel()) < output_max_columns) + { + // Output it as a single line. + strm.Indent (text); + strm.EOL(); + } + else + { + // We need to break it up into multiple lines. + + int text_width = output_max_columns - strm.GetIndentLevel() - 1; + int start = 0; + int end = start; + int final_end = strlen (text); + int sub_len; + + while (end < final_end) + { + // Don't start the 'text' on a space, since we're already outputting the indentation. + while ((start < final_end) && (text[start] == ' ')) + start++; + + end = start + text_width; + if (end > final_end) + end = final_end; + else + { + // If we're not at the end of the text, make sure we break the line on white space. + while (end > start + && text[end] != ' ' && text[end] != '\t' && text[end] != '\n') + end--; + } + + sub_len = end - start; + if (start != 0) + strm.EOL(); + strm.Indent(); + assert (start < final_end); + assert (start + sub_len <= final_end); + strm.Write(text + start, sub_len); + start = end + 1; + } + strm.EOL(); + } +} + +bool +Options::SupportsLongOption (const char *long_option) +{ + if (long_option && long_option[0]) + { + const OptionDefinition *opt_defs = GetDefinitions (); + if (opt_defs) + { + const char *long_option_name = long_option; + if (long_option[0] == '-' && long_option[1] == '-') + long_option_name += 2; + + for (uint32_t i = 0; opt_defs[i].long_option; ++i) + { + if (strcmp(opt_defs[i].long_option, long_option_name) == 0) + return true; + } + } + } + return false; +} + +enum OptionDisplayType +{ + eDisplayBestOption, + eDisplayShortOption, + eDisplayLongOption +}; + +static bool +PrintOption (const OptionDefinition &opt_def, + OptionDisplayType display_type, + const char *header, + const char *footer, + bool show_optional, + Stream &strm) +{ + const bool has_short_option = isprint8(opt_def.short_option) != 0; + + if (display_type == eDisplayShortOption && !has_short_option) + return false; + + if (header && header[0]) + strm.PutCString(header); + + if (show_optional && !opt_def.required) + strm.PutChar('['); + const bool show_short_option = has_short_option && display_type != eDisplayLongOption; + if (show_short_option) + strm.Printf ("-%c", opt_def.short_option); + else + strm.Printf ("--%s", opt_def.long_option); + switch (opt_def.option_has_arg) + { + case no_argument: + break; + case required_argument: + strm.Printf (" <%s>", CommandObject::GetArgumentName (opt_def.argument_type)); + break; + + case optional_argument: + strm.Printf ("%s[<%s>]", + show_short_option ? "" : "=", + CommandObject::GetArgumentName (opt_def.argument_type)); + break; + } + if (show_optional && !opt_def.required) + strm.PutChar(']'); + if (footer && footer[0]) + strm.PutCString(footer); + return true; +} + +void +Options::GenerateOptionUsage +( + Stream &strm, + CommandObject *cmd +) +{ + const uint32_t screen_width = m_interpreter.GetDebugger().GetTerminalWidth(); + + const OptionDefinition *opt_defs = GetDefinitions(); + const uint32_t save_indent_level = strm.GetIndentLevel(); + const char *name; + + StreamString arguments_str; + + if (cmd) + { + name = cmd->GetCommandName(); + cmd->GetFormattedCommandArguments (arguments_str); + } + else + name = ""; + + strm.PutCString ("\nCommand Options Usage:\n"); + + strm.IndentMore(2); + + // First, show each usage level set of options, e.g. [options-for-level-0] + // [options-for-level-1] + // etc. + + const uint32_t num_options = NumCommandOptions(); + if (num_options == 0) + return; + + uint32_t num_option_sets = GetRequiredOptions().size(); + + uint32_t i; + + for (uint32_t opt_set = 0; opt_set < num_option_sets; ++opt_set) + { + uint32_t opt_set_mask; + + opt_set_mask = 1 << opt_set; + if (opt_set > 0) + strm.Printf ("\n"); + strm.Indent (name); + + // Different option sets may require different args. + StreamString args_str; + if (cmd) + cmd->GetFormattedCommandArguments(args_str, opt_set_mask); + + // First go through and print all options that take no arguments as + // a single string. If a command has "-a" "-b" and "-c", this will show + // up as [-abc] + + std::set options; + std::set::const_iterator options_pos, options_end; + for (i = 0; i < num_options; ++i) + { + if (opt_defs[i].usage_mask & opt_set_mask && isprint8(opt_defs[i].short_option)) + { + // Add current option to the end of out_stream. + + if (opt_defs[i].required == true && + opt_defs[i].option_has_arg == no_argument) + { + options.insert (opt_defs[i].short_option); + } + } + } + + if (options.empty() == false) + { + // We have some required options with no arguments + strm.PutCString(" -"); + for (i=0; i<2; ++i) + for (options_pos = options.begin(), options_end = options.end(); + options_pos != options_end; + ++options_pos) + { + if (i==0 && ::islower (*options_pos)) + continue; + if (i==1 && ::isupper (*options_pos)) + continue; + strm << (char)*options_pos; + } + } + + for (i = 0, options.clear(); i < num_options; ++i) + { + if (opt_defs[i].usage_mask & opt_set_mask && isprint8(opt_defs[i].short_option)) + { + // Add current option to the end of out_stream. + + if (opt_defs[i].required == false && + opt_defs[i].option_has_arg == no_argument) + { + options.insert (opt_defs[i].short_option); + } + } + } + + if (options.empty() == false) + { + // We have some required options with no arguments + strm.PutCString(" [-"); + for (i=0; i<2; ++i) + for (options_pos = options.begin(), options_end = options.end(); + options_pos != options_end; + ++options_pos) + { + if (i==0 && ::islower (*options_pos)) + continue; + if (i==1 && ::isupper (*options_pos)) + continue; + strm << (char)*options_pos; + } + strm.PutChar(']'); + } + + // First go through and print the required options (list them up front). + + for (i = 0; i < num_options; ++i) + { + if (opt_defs[i].usage_mask & opt_set_mask && isprint8(opt_defs[i].short_option)) + { + if (opt_defs[i].required && opt_defs[i].option_has_arg != no_argument) + PrintOption (opt_defs[i], eDisplayBestOption, " ", NULL, true, strm); + } + } + + // Now go through again, and this time only print the optional options. + + for (i = 0; i < num_options; ++i) + { + if (opt_defs[i].usage_mask & opt_set_mask) + { + // Add current option to the end of out_stream. + + if (!opt_defs[i].required && opt_defs[i].option_has_arg != no_argument) + PrintOption (opt_defs[i], eDisplayBestOption, " ", NULL, true, strm); + } + } + + if (args_str.GetSize() > 0) + { + if (cmd->WantsRawCommandString()) + strm.Printf(" --"); + + strm.Printf (" %s", args_str.GetData()); + } + } + + if (cmd && + cmd->WantsRawCommandString() && + arguments_str.GetSize() > 0) + { + strm.PutChar('\n'); + strm.Indent(name); + strm.Printf(" %s", arguments_str.GetData()); + } + + strm.Printf ("\n\n"); + + // Now print out all the detailed information about the various options: long form, short form and help text: + // --long_name ( -short ) + // help text + + // This variable is used to keep track of which options' info we've printed out, because some options can be in + // more than one usage level, but we only want to print the long form of its information once. + + std::multimap options_seen; + strm.IndentMore (5); + + // Put the unique command options in a vector & sort it, so we can output them alphabetically (by short_option) + // when writing out detailed help for each option. + + for (i = 0; i < num_options; ++i) + options_seen.insert(std::make_pair(opt_defs[i].short_option, i)); + + // Go through the unique'd and alphabetically sorted vector of options, find the table entry for each option + // and write out the detailed help information for that option. + + bool first_option_printed = false;; + + for (auto pos : options_seen) + { + i = pos.second; + //Print out the help information for this option. + + // Put a newline separation between arguments + if (first_option_printed) + strm.EOL(); + else + first_option_printed = true; + + CommandArgumentType arg_type = opt_defs[i].argument_type; + + StreamString arg_name_str; + arg_name_str.Printf ("<%s>", CommandObject::GetArgumentName (arg_type)); + + strm.Indent (); + if (opt_defs[i].short_option && isprint8(opt_defs[i].short_option)) + { + PrintOption (opt_defs[i], eDisplayShortOption, NULL, NULL, false, strm); + PrintOption (opt_defs[i], eDisplayLongOption, " ( ", " )", false, strm); + } + else + { + // Short option is not printable, just print long option + PrintOption (opt_defs[i], eDisplayLongOption, NULL, NULL, false, strm); + } + strm.EOL(); + + strm.IndentMore (5); + + if (opt_defs[i].usage_text) + OutputFormattedUsageText (strm, + opt_defs[i].usage_text, + screen_width); + if (opt_defs[i].enum_values != NULL) + { + strm.Indent (); + strm.Printf("Values: "); + for (int k = 0; opt_defs[i].enum_values[k].string_value != NULL; k++) + { + if (k == 0) + strm.Printf("%s", opt_defs[i].enum_values[k].string_value); + else + strm.Printf(" | %s", opt_defs[i].enum_values[k].string_value); + } + strm.EOL(); + } + strm.IndentLess (5); + } + + // Restore the indent level + strm.SetIndentLevel (save_indent_level); +} + +// This function is called when we have been given a potentially incomplete set of +// options, such as when an alias has been defined (more options might be added at +// at the time the alias is invoked). We need to verify that the options in the set +// m_seen_options are all part of a set that may be used together, but m_seen_options +// may be missing some of the "required" options. + +bool +Options::VerifyPartialOptions (CommandReturnObject &result) +{ + bool options_are_valid = false; + + int num_levels = GetRequiredOptions().size(); + if (num_levels) + { + for (int i = 0; i < num_levels && !options_are_valid; ++i) + { + // In this case we are treating all options as optional rather than required. + // Therefore a set of options is correct if m_seen_options is a subset of the + // union of m_required_options and m_optional_options. + OptionSet union_set; + OptionsSetUnion (GetRequiredOptions()[i], GetOptionalOptions()[i], union_set); + if (IsASubset (m_seen_options, union_set)) + options_are_valid = true; + } + } + + return options_are_valid; +} + +bool +Options::HandleOptionCompletion +( + Args &input, + OptionElementVector &opt_element_vector, + int cursor_index, + int char_pos, + int match_start_point, + int max_return_elements, + bool &word_complete, + lldb_private::StringList &matches +) +{ + word_complete = true; + + // For now we just scan the completions to see if the cursor position is in + // an option or its argument. Otherwise we'll call HandleArgumentCompletion. + // In the future we can use completion to validate options as well if we want. + + const OptionDefinition *opt_defs = GetDefinitions(); + + std::string cur_opt_std_str (input.GetArgumentAtIndex(cursor_index)); + cur_opt_std_str.erase(char_pos); + const char *cur_opt_str = cur_opt_std_str.c_str(); + + for (size_t i = 0; i < opt_element_vector.size(); i++) + { + int opt_pos = opt_element_vector[i].opt_pos; + int opt_arg_pos = opt_element_vector[i].opt_arg_pos; + int opt_defs_index = opt_element_vector[i].opt_defs_index; + if (opt_pos == cursor_index) + { + // We're completing the option itself. + + if (opt_defs_index == OptionArgElement::eBareDash) + { + // We're completing a bare dash. That means all options are open. + // FIXME: We should scan the other options provided and only complete options + // within the option group they belong to. + char opt_str[3] = {'-', 'a', '\0'}; + + for (int j = 0 ; opt_defs[j].short_option != 0 ; j++) + { + opt_str[1] = opt_defs[j].short_option; + matches.AppendString (opt_str); + } + return true; + } + else if (opt_defs_index == OptionArgElement::eBareDoubleDash) + { + std::string full_name ("--"); + for (int j = 0 ; opt_defs[j].short_option != 0 ; j++) + { + full_name.erase(full_name.begin() + 2, full_name.end()); + full_name.append (opt_defs[j].long_option); + matches.AppendString (full_name.c_str()); + } + return true; + } + else if (opt_defs_index != OptionArgElement::eUnrecognizedArg) + { + // We recognized it, if it an incomplete long option, complete it anyway (getopt_long_only is + // happy with shortest unique string, but it's still a nice thing to do.) Otherwise return + // The string so the upper level code will know this is a full match and add the " ". + if (cur_opt_str && strlen (cur_opt_str) > 2 + && cur_opt_str[0] == '-' && cur_opt_str[1] == '-' + && strcmp (opt_defs[opt_defs_index].long_option, cur_opt_str) != 0) + { + std::string full_name ("--"); + full_name.append (opt_defs[opt_defs_index].long_option); + matches.AppendString(full_name.c_str()); + return true; + } + else + { + matches.AppendString(input.GetArgumentAtIndex(cursor_index)); + return true; + } + } + else + { + // FIXME - not handling wrong options yet: + // Check to see if they are writing a long option & complete it. + // I think we will only get in here if the long option table has two elements + // that are not unique up to this point. getopt_long_only does shortest unique match + // for long options already. + + if (cur_opt_str && strlen (cur_opt_str) > 2 + && cur_opt_str[0] == '-' && cur_opt_str[1] == '-') + { + for (int j = 0 ; opt_defs[j].short_option != 0 ; j++) + { + if (strstr(opt_defs[j].long_option, cur_opt_str + 2) == opt_defs[j].long_option) + { + std::string full_name ("--"); + full_name.append (opt_defs[j].long_option); + // The options definitions table has duplicates because of the + // way the grouping information is stored, so only add once. + bool duplicate = false; + for (size_t k = 0; k < matches.GetSize(); k++) + { + if (matches.GetStringAtIndex(k) == full_name) + { + duplicate = true; + break; + } + } + if (!duplicate) + matches.AppendString(full_name.c_str()); + } + } + } + return true; + } + + + } + else if (opt_arg_pos == cursor_index) + { + // Okay the cursor is on the completion of an argument. + // See if it has a completion, otherwise return no matches. + + if (opt_defs_index != -1) + { + HandleOptionArgumentCompletion (input, + cursor_index, + strlen (input.GetArgumentAtIndex(cursor_index)), + opt_element_vector, + i, + match_start_point, + max_return_elements, + word_complete, + matches); + return true; + } + else + { + // No completion callback means no completions... + return true; + } + + } + else + { + // Not the last element, keep going. + continue; + } + } + return false; +} + +bool +Options::HandleOptionArgumentCompletion +( + Args &input, + int cursor_index, + int char_pos, + OptionElementVector &opt_element_vector, + int opt_element_index, + int match_start_point, + int max_return_elements, + bool &word_complete, + lldb_private::StringList &matches +) +{ + const OptionDefinition *opt_defs = GetDefinitions(); + std::unique_ptr filter_ap; + + int opt_arg_pos = opt_element_vector[opt_element_index].opt_arg_pos; + int opt_defs_index = opt_element_vector[opt_element_index].opt_defs_index; + + // See if this is an enumeration type option, and if so complete it here: + + OptionEnumValueElement *enum_values = opt_defs[opt_defs_index].enum_values; + if (enum_values != NULL) + { + bool return_value = false; + std::string match_string(input.GetArgumentAtIndex (opt_arg_pos), input.GetArgumentAtIndex (opt_arg_pos) + char_pos); + for (int i = 0; enum_values[i].string_value != NULL; i++) + { + if (strstr(enum_values[i].string_value, match_string.c_str()) == enum_values[i].string_value) + { + matches.AppendString (enum_values[i].string_value); + return_value = true; + } + } + return return_value; + } + + // If this is a source file or symbol type completion, and there is a + // -shlib option somewhere in the supplied arguments, then make a search filter + // for that shared library. + // FIXME: Do we want to also have an "OptionType" so we don't have to match string names? + + uint32_t completion_mask = opt_defs[opt_defs_index].completion_type; + + if (completion_mask == 0) + { + lldb::CommandArgumentType option_arg_type = opt_defs[opt_defs_index].argument_type; + if (option_arg_type != eArgTypeNone) + { + CommandObject::ArgumentTableEntry *arg_entry = CommandObject::FindArgumentDataByType (opt_defs[opt_defs_index].argument_type); + if (arg_entry) + completion_mask = arg_entry->completion_type; + } + } + + if (completion_mask & CommandCompletions::eSourceFileCompletion + || completion_mask & CommandCompletions::eSymbolCompletion) + { + for (size_t i = 0; i < opt_element_vector.size(); i++) + { + int cur_defs_index = opt_element_vector[i].opt_defs_index; + int cur_arg_pos = opt_element_vector[i].opt_arg_pos; + const char *cur_opt_name = opt_defs[cur_defs_index].long_option; + + // If this is the "shlib" option and there was an argument provided, + // restrict it to that shared library. + if (strcmp(cur_opt_name, "shlib") == 0 && cur_arg_pos != -1) + { + const char *module_name = input.GetArgumentAtIndex(cur_arg_pos); + if (module_name) + { + FileSpec module_spec(module_name, false); + lldb::TargetSP target_sp = m_interpreter.GetDebugger().GetSelectedTarget(); + // Search filters require a target... + if (target_sp) + filter_ap.reset (new SearchFilterByModule (target_sp, module_spec)); + } + break; + } + } + } + + return CommandCompletions::InvokeCommonCompletionCallbacks (m_interpreter, + completion_mask, + input.GetArgumentAtIndex (opt_arg_pos), + match_start_point, + max_return_elements, + filter_ap.get(), + word_complete, + matches); + +} + + +void +OptionGroupOptions::Append (OptionGroup* group) +{ + const OptionDefinition* group_option_defs = group->GetDefinitions (); + const uint32_t group_option_count = group->GetNumDefinitions(); + for (uint32_t i=0; iGetDefinitions (); + const uint32_t group_option_count = group->GetNumDefinitions(); + for (uint32_t i=0; iSetOptionValue (m_interpreter, + m_option_infos[option_idx].option_index, + option_value); + + } + else + { + error.SetErrorString ("invalid option index"); // Shouldn't happen... + } + return error; +} + +void +OptionGroupOptions::OptionParsingStarting () +{ + std::set group_set; + OptionInfos::iterator pos, end = m_option_infos.end(); + for (pos = m_option_infos.begin(); pos != end; ++pos) + { + OptionGroup* group = pos->option_group; + if (group_set.find(group) == group_set.end()) + { + group->OptionParsingStarting (m_interpreter); + group_set.insert(group); + } + } +} +Error +OptionGroupOptions::OptionParsingFinished () +{ + std::set group_set; + Error error; + OptionInfos::iterator pos, end = m_option_infos.end(); + for (pos = m_option_infos.begin(); pos != end; ++pos) + { + OptionGroup* group = pos->option_group; + if (group_set.find(group) == group_set.end()) + { + error = group->OptionParsingFinished (m_interpreter); + group_set.insert(group); + if (error.Fail()) + return error; + } + } + return error; +} diff --git a/contrib/llvm/tools/lldb/source/Interpreter/Property.cpp b/contrib/llvm/tools/lldb/source/Interpreter/Property.cpp new file mode 100644 index 00000000000..e5cf63ada88 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Interpreter/Property.cpp @@ -0,0 +1,275 @@ +//===-- Property.cpp --------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "lldb/Interpreter/Property.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/UserSettingsController.h" +#include "lldb/Interpreter/Args.h" +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Interpreter/OptionValues.h" + +using namespace lldb; +using namespace lldb_private; + +Property::Property (const PropertyDefinition &definition) : + m_name (definition.name), + m_description (definition.description), + m_value_sp (), + m_is_global (definition.global) +{ + switch (definition.type) + { + case OptionValue::eTypeInvalid: + case OptionValue::eTypeProperties: + break; + case OptionValue::eTypeArch: + // "definition.default_uint_value" is not used + // "definition.default_cstr_value" as a string value that represents the default string value for the architecture/triple + m_value_sp.reset (new OptionValueArch(definition.default_cstr_value)); + break; + + case OptionValue::eTypeArgs: + // "definition.default_uint_value" is always a OptionValue::Type + m_value_sp.reset (new OptionValueArgs()); + break; + + case OptionValue::eTypeArray: + // "definition.default_uint_value" is always a OptionValue::Type + m_value_sp.reset (new OptionValueArray(OptionValue::ConvertTypeToMask((OptionValue::Type)definition.default_uint_value))); + break; + + case OptionValue::eTypeBoolean: + // "definition.default_uint_value" is the default boolean value if + // "definition.default_cstr_value" is NULL, otherwise interpret + // "definition.default_cstr_value" as a string value that represents the default + // value. + if (definition.default_cstr_value) + m_value_sp.reset (new OptionValueBoolean(Args::StringToBoolean (definition.default_cstr_value, false, NULL))); + else + m_value_sp.reset (new OptionValueBoolean(definition.default_uint_value != 0)); + break; + + case OptionValue::eTypeDictionary: + // "definition.default_uint_value" is always a OptionValue::Type + m_value_sp.reset (new OptionValueDictionary(OptionValue::ConvertTypeToMask((OptionValue::Type)definition.default_uint_value))); + break; + + case OptionValue::eTypeEnum: + // "definition.default_uint_value" is the default enumeration value if + // "definition.default_cstr_value" is NULL, otherwise interpret + // "definition.default_cstr_value" as a string value that represents the default + // value. + { + OptionValueEnumeration *enum_value = new OptionValueEnumeration(definition.enum_values, definition.default_uint_value); + m_value_sp.reset (enum_value); + if (definition.default_cstr_value) + { + if (enum_value->SetValueFromCString(definition.default_cstr_value).Success()) + { + enum_value->SetDefaultValue(enum_value->GetCurrentValue()); + // Call Clear() since we don't want the value to appear as + // having been set since we called SetValueFromCString() above. + // Clear will set the current value to the default and clear + // the boolean that says that the value has been set. + enum_value->Clear(); + } + } + } + break; + + case OptionValue::eTypeFileSpec: + // "definition.default_uint_value" represents if the "definition.default_cstr_value" should + // be resolved or not + m_value_sp.reset (new OptionValueFileSpec(FileSpec(definition.default_cstr_value, definition.default_uint_value != 0))); + break; + + case OptionValue::eTypeFileSpecList: + // "definition.default_uint_value" is not used for a OptionValue::eTypeFileSpecList + m_value_sp.reset (new OptionValueFileSpecList()); + break; + + case OptionValue::eTypeFormat: + // "definition.default_uint_value" is the default format enumeration value if + // "definition.default_cstr_value" is NULL, otherwise interpret + // "definition.default_cstr_value" as a string value that represents the default + // value. + { + Format new_format = eFormatInvalid; + if (definition.default_cstr_value) + Args::StringToFormat (definition.default_cstr_value, new_format, NULL); + else + new_format = (Format)definition.default_uint_value; + m_value_sp.reset (new OptionValueFormat(new_format)); + } + break; + + case OptionValue::eTypePathMap: + // "definition.default_uint_value" tells us if notifications should occur for + // path mappings + m_value_sp.reset (new OptionValuePathMappings(definition.default_uint_value != 0)); + break; + + case OptionValue::eTypeRegex: + // "definition.default_uint_value" is used to the regular expression flags + // "definition.default_cstr_value" the default regular expression value + // value. + m_value_sp.reset (new OptionValueRegex(definition.default_cstr_value, definition.default_uint_value)); + break; + + case OptionValue::eTypeSInt64: + // "definition.default_uint_value" is the default integer value if + // "definition.default_cstr_value" is NULL, otherwise interpret + // "definition.default_cstr_value" as a string value that represents the default + // value. + m_value_sp.reset (new OptionValueSInt64(definition.default_cstr_value ? Args::StringToSInt64 (definition.default_cstr_value) : definition.default_uint_value)); + break; + + case OptionValue::eTypeUInt64: + // "definition.default_uint_value" is the default unsigned integer value if + // "definition.default_cstr_value" is NULL, otherwise interpret + // "definition.default_cstr_value" as a string value that represents the default + // value. + m_value_sp.reset (new OptionValueUInt64(definition.default_cstr_value ? Args::StringToUInt64 (definition.default_cstr_value) : definition.default_uint_value)); + break; + + case OptionValue::eTypeUUID: + // "definition.default_uint_value" is not used for a OptionValue::eTypeUUID + // "definition.default_cstr_value" can contain a default UUID value + { + UUID uuid; + if (definition.default_cstr_value) + uuid.SetFromCString (definition.default_cstr_value); + m_value_sp.reset (new OptionValueUUID(uuid)); + } + break; + + case OptionValue::eTypeString: + // "definition.default_uint_value" can contain the string option flags OR'ed together + // "definition.default_cstr_value" can contain a default string value + { + OptionValueString *string_value = new OptionValueString(definition.default_cstr_value); + if (definition.default_uint_value != 0) + string_value->GetOptions().Reset(definition.default_uint_value); + m_value_sp.reset (string_value); + } + break; + } +} + +Property::Property (const ConstString &name, + const ConstString &desc, + bool is_global, + const lldb::OptionValueSP &value_sp) : + m_name (name), + m_description (desc), + m_value_sp (value_sp), + m_is_global (is_global) +{ +} + +bool +Property::DumpQualifiedName(Stream &strm) const +{ + if (m_name) + { + if (m_value_sp->DumpQualifiedName(strm)) + strm.PutChar('.'); + strm << m_name; + return true; + } + return false; +} + + +void +Property::Dump (const ExecutionContext *exe_ctx, Stream &strm, uint32_t dump_mask) const +{ + if (m_value_sp) + { + const bool dump_desc = dump_mask & OptionValue::eDumpOptionDescription; + const bool transparent = m_value_sp->ValueIsTransparent (); + if (dump_desc || !transparent) + { + if ((dump_mask & OptionValue::eDumpOptionName) && m_name) + { + DumpQualifiedName(strm); + if (dump_mask & ~OptionValue::eDumpOptionName) + strm.PutChar(' '); + } + } + if (dump_desc) + { + const char *desc = GetDescription(); + if (desc) + strm.Printf ("-- %s", desc); + + if (transparent && (dump_mask == (OptionValue::eDumpOptionName | OptionValue::eDumpOptionDescription))) + strm.EOL(); + } + m_value_sp->DumpValue(exe_ctx, strm, dump_mask); + } +} + + +void +Property::DumpDescription (CommandInterpreter &interpreter, + Stream &strm, + uint32_t output_width, + bool display_qualified_name) const +{ + if (m_value_sp) + { + const char *desc = GetDescription(); + + if (desc) + { + StreamString qualified_name; + const OptionValueProperties *sub_properties = m_value_sp->GetAsProperties(); + if (sub_properties) + { + strm.EOL(); + + if (m_value_sp->DumpQualifiedName(qualified_name)) + strm.Printf("'%s' variables:\n\n", qualified_name.GetString().c_str()); + sub_properties->DumpAllDescriptions(interpreter, strm); + } + else + { + if (desc) + { + if (display_qualified_name) + { + StreamString qualified_name; + DumpQualifiedName(qualified_name); + interpreter.OutputFormattedHelpText (strm, + qualified_name.GetString().c_str(), + "--", + desc, + output_width); + } + else + { + interpreter.OutputFormattedHelpText (strm, + m_name.GetCString(), + "--", + desc, + output_width); + } + } + } + } + } +} + diff --git a/contrib/llvm/tools/lldb/source/Interpreter/PythonDataObjects.cpp b/contrib/llvm/tools/lldb/source/Interpreter/PythonDataObjects.cpp new file mode 100644 index 00000000000..2a1f348e14b --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Interpreter/PythonDataObjects.cpp @@ -0,0 +1,430 @@ +//===-- PythonDataObjects.cpp ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// In order to guarantee correct working with Python, Python.h *MUST* be +// the *FIRST* header file included here. +#ifdef LLDB_DISABLE_PYTHON + +// Python is disabled in this build + +#else + +#if defined (__APPLE__) +#include +#else +#include +#endif + +#include + +#include "lldb/Core/Stream.h" +#include "lldb/Host/File.h" +#include "lldb/Interpreter/PythonDataObjects.h" +#include "lldb/Interpreter/ScriptInterpreter.h" + +using namespace lldb_private; +using namespace lldb; + +//---------------------------------------------------------------------- +// PythonObject +//---------------------------------------------------------------------- +PythonObject::PythonObject (const lldb::ScriptInterpreterObjectSP &script_object_sp) : + m_py_obj (NULL) +{ + if (script_object_sp) + Reset ((PyObject *)script_object_sp->GetObject()); +} + +void +PythonObject::Dump (Stream &strm) const +{ + if (m_py_obj) + { + FILE *file = ::tmpfile(); + if (file) + { + ::PyObject_Print (m_py_obj, file, 0); + const long length = ftell (file); + if (length) + { + ::rewind(file); + std::vector file_contents (length,'\0'); + const size_t length_read = ::fread (file_contents.data(), 1, file_contents.size(), file); + if (length_read > 0) + strm.Write (file_contents.data(), length_read); + } + ::fclose (file); + } + } + else + strm.PutCString ("NULL"); +} + +PythonString +PythonObject::Repr () +{ + if (!m_py_obj) + return PythonString (); + PyObject *repr = PyObject_Repr(m_py_obj); + if (!repr) + return PythonString (); + return PythonString(repr); +} + +PythonString +PythonObject::Str () +{ + if (!m_py_obj) + return PythonString (); + PyObject *str = PyObject_Str(m_py_obj); + if (!str) + return PythonString (); + return PythonString(str); +} + +//---------------------------------------------------------------------- +// PythonString +//---------------------------------------------------------------------- + +PythonString::PythonString (PyObject *py_obj) : + PythonObject(py_obj) +{ +} + +PythonString::PythonString (const PythonObject &object) : + PythonObject(object.GetPythonObject()) +{ +} + +PythonString::PythonString (const lldb::ScriptInterpreterObjectSP &script_object_sp) : + PythonObject (script_object_sp) +{ +} + +PythonString::PythonString (const char* string) : + PythonObject(PyString_FromString(string)) +{ +} + +PythonString::PythonString () : + PythonObject() +{ +} + +PythonString::~PythonString () +{ +} + +bool +PythonString::Reset (PyObject *py_obj) +{ + if (py_obj && PyString_Check(py_obj)) + return PythonObject::Reset(py_obj); + + PythonObject::Reset(NULL); + return py_obj == NULL; +} + +const char* +PythonString::GetString() const +{ + if (m_py_obj) + return PyString_AsString(m_py_obj); + return NULL; +} + +size_t +PythonString::GetSize() const +{ + if (m_py_obj) + return PyString_Size(m_py_obj); + return 0; +} + +void +PythonString::SetString (const char* string) +{ + PythonObject::Reset(PyString_FromString(string)); +} + +//---------------------------------------------------------------------- +// PythonInteger +//---------------------------------------------------------------------- + +PythonInteger::PythonInteger (PyObject *py_obj) : + PythonObject(py_obj) +{ +} + +PythonInteger::PythonInteger (const PythonObject &object) : + PythonObject(object.GetPythonObject()) +{ +} + +PythonInteger::PythonInteger (const lldb::ScriptInterpreterObjectSP &script_object_sp) : + PythonObject (script_object_sp) +{ +} + +PythonInteger::PythonInteger (int64_t value) : + PythonObject(PyInt_FromLong(value)) +{ +} + + +PythonInteger::~PythonInteger () +{ +} + +bool +PythonInteger::Reset (PyObject *py_obj) +{ + if (py_obj && PyInt_Check(py_obj)) + return PythonObject::Reset(py_obj); + + PythonObject::Reset(NULL); + return py_obj == NULL; +} + +int64_t +PythonInteger::GetInteger() +{ + if (m_py_obj) + return PyInt_AsLong(m_py_obj); + else + return UINT64_MAX; +} + +void +PythonInteger::SetInteger (int64_t value) +{ + PythonObject::Reset(PyInt_FromLong(value)); +} + +//---------------------------------------------------------------------- +// PythonList +//---------------------------------------------------------------------- + +PythonList::PythonList () : + PythonObject(PyList_New(0)) +{ +} + +PythonList::PythonList (uint32_t count) : + PythonObject(PyList_New(count)) +{ +} + +PythonList::PythonList (PyObject *py_obj) : + PythonObject(py_obj) +{ +} + + +PythonList::PythonList (const PythonObject &object) : + PythonObject(object.GetPythonObject()) +{ +} + +PythonList::PythonList (const lldb::ScriptInterpreterObjectSP &script_object_sp) : + PythonObject (script_object_sp) +{ +} + +PythonList::~PythonList () +{ +} + +bool +PythonList::Reset (PyObject *py_obj) +{ + if (py_obj && PyList_Check(py_obj)) + return PythonObject::Reset(py_obj); + + PythonObject::Reset(NULL); + return py_obj == NULL; +} + +uint32_t +PythonList::GetSize() +{ + if (m_py_obj) + return PyList_GET_SIZE(m_py_obj); + return 0; +} + +PythonObject +PythonList::GetItemAtIndex (uint32_t index) +{ + if (m_py_obj) + return PythonObject(PyList_GetItem(m_py_obj, index)); + return NULL; +} + +void +PythonList::SetItemAtIndex (uint32_t index, const PythonObject & object) +{ + if (m_py_obj && object) + PyList_SetItem(m_py_obj, index, object.GetPythonObject()); +} + +void +PythonList::AppendItem (const PythonObject &object) +{ + if (m_py_obj && object) + PyList_Append(m_py_obj, object.GetPythonObject()); +} + +//---------------------------------------------------------------------- +// PythonDictionary +//---------------------------------------------------------------------- + +PythonDictionary::PythonDictionary () : + PythonObject(PyDict_New()) +{ +} + +PythonDictionary::PythonDictionary (PyObject *py_obj) : + PythonObject(py_obj) +{ +} + + +PythonDictionary::PythonDictionary (const PythonObject &object) : + PythonObject(object.GetPythonObject()) +{ +} + +PythonDictionary::PythonDictionary (const lldb::ScriptInterpreterObjectSP &script_object_sp) : + PythonObject (script_object_sp) +{ +} + +PythonDictionary::~PythonDictionary () +{ +} + +bool +PythonDictionary::Reset (PyObject *py_obj) +{ + if (py_obj && PyDict_Check(py_obj)) + return PythonObject::Reset(py_obj); + + PythonObject::Reset(NULL); + return py_obj == NULL; +} + +uint32_t +PythonDictionary::GetSize() +{ + if (m_py_obj) + return PyDict_Size(m_py_obj); + return 0; +} + +PythonObject +PythonDictionary::GetItemForKey (const char *key) const +{ + if (key && key[0]) + { + PythonString python_key(key); + return GetItemForKey(python_key); + } + return NULL; +} + + +PythonObject +PythonDictionary::GetItemForKey (const PythonString &key) const +{ + if (m_py_obj && key) + return PythonObject(PyDict_GetItem(m_py_obj, key.GetPythonObject())); + return PythonObject(); +} + + +const char * +PythonDictionary::GetItemForKeyAsString (const PythonString &key, const char *fail_value) const +{ + if (m_py_obj && key) + { + PyObject *py_obj = PyDict_GetItem(m_py_obj, key.GetPythonObject()); + if (py_obj && PyString_Check(py_obj)) + return PyString_AsString(py_obj); + } + return fail_value; +} + +int64_t +PythonDictionary::GetItemForKeyAsInteger (const PythonString &key, int64_t fail_value) const +{ + if (m_py_obj && key) + { + PyObject *py_obj = PyDict_GetItem(m_py_obj, key.GetPythonObject()); + if (py_obj) + { + if (PyInt_Check(py_obj)) + return PyInt_AsLong(py_obj); + + if (PyLong_Check(py_obj)) + return PyLong_AsLong(py_obj); + } + } + return fail_value; +} + +PythonList +PythonDictionary::GetKeys () const +{ + if (m_py_obj) + return PythonList(PyDict_Keys(m_py_obj)); + return PythonList(); +} + +PythonString +PythonDictionary::GetKeyAtPosition (uint32_t pos) const +{ + PyObject *key, *value; + Py_ssize_t pos_iter = 0; + + if (m_py_obj) + { + while (PyDict_Next(m_py_obj, &pos_iter, &key, &value)) + { + if (pos-- == 0) + return PythonString(key); + } + } + return PythonString(); +} + +PythonObject +PythonDictionary::GetValueAtPosition (uint32_t pos) const +{ + PyObject *key, *value; + Py_ssize_t pos_iter = 0; + + if (!m_py_obj) + return NULL; + + while (PyDict_Next(m_py_obj, &pos_iter, &key, &value)) { + if (pos-- == 0) + return PythonObject(value); + } + return PythonObject(); +} + +void +PythonDictionary::SetItemForKey (const PythonString &key, const PythonObject &value) +{ + if (m_py_obj && key && value) + PyDict_SetItem(m_py_obj, key.GetPythonObject(), value.GetPythonObject()); +} + +#endif diff --git a/contrib/llvm/tools/lldb/source/Interpreter/ScriptInterpreter.cpp b/contrib/llvm/tools/lldb/source/Interpreter/ScriptInterpreter.cpp new file mode 100644 index 00000000000..67314731df2 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Interpreter/ScriptInterpreter.cpp @@ -0,0 +1,105 @@ +//===-- ScriptInterpreter.cpp -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "lldb/Interpreter/ScriptInterpreter.h" + +#include +#include +#include + +#include "lldb/Core/Error.h" +#include "lldb/Core/Stream.h" +#include "lldb/Core/StringList.h" +#include "lldb/Interpreter/CommandReturnObject.h" +#include "lldb/Interpreter/ScriptInterpreterPython.h" +#include "lldb/Utility/PseudoTerminal.h" + +using namespace lldb; +using namespace lldb_private; + +ScriptInterpreter::ScriptInterpreter (CommandInterpreter &interpreter, lldb::ScriptLanguage script_lang) : + m_interpreter (interpreter), + m_script_lang (script_lang) +{ +} + +ScriptInterpreter::~ScriptInterpreter () +{ +} + +CommandInterpreter & +ScriptInterpreter::GetCommandInterpreter () +{ + return m_interpreter; +} + +void +ScriptInterpreter::CollectDataForBreakpointCommandCallback +( + BreakpointOptions *bp_options, + CommandReturnObject &result +) +{ + result.SetStatus (eReturnStatusFailed); + result.AppendError ("ScriptInterpreter::GetScriptCommands(StringList &) is not implemented."); +} + +void +ScriptInterpreter::CollectDataForWatchpointCommandCallback +( + WatchpointOptions *bp_options, + CommandReturnObject &result +) +{ + result.SetStatus (eReturnStatusFailed); + result.AppendError ("ScriptInterpreter::GetScriptCommands(StringList &) is not implemented."); +} + +std::string +ScriptInterpreter::LanguageToString (lldb::ScriptLanguage language) +{ + std::string return_value; + + switch (language) + { + case eScriptLanguageNone: + return_value = "None"; + break; + case eScriptLanguagePython: + return_value = "Python"; + break; + } + + return return_value; +} + +std::unique_ptr +ScriptInterpreter::AcquireInterpreterLock () +{ + return std::unique_ptr(new ScriptInterpreterLocker()); +} + +void +ScriptInterpreter::InitializeInterpreter (SWIGInitCallback python_swig_init_callback) +{ +#ifndef LLDB_DISABLE_PYTHON + ScriptInterpreterPython::InitializeInterpreter (python_swig_init_callback); +#endif // #ifndef LLDB_DISABLE_PYTHON +} + +void +ScriptInterpreter::TerminateInterpreter () +{ +#ifndef LLDB_DISABLE_PYTHON + ScriptInterpreterPython::TerminateInterpreter (); +#endif // #ifndef LLDB_DISABLE_PYTHON +} + diff --git a/contrib/llvm/tools/lldb/source/Interpreter/ScriptInterpreterNone.cpp b/contrib/llvm/tools/lldb/source/Interpreter/ScriptInterpreterNone.cpp new file mode 100644 index 00000000000..6a4411494c4 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Interpreter/ScriptInterpreterNone.cpp @@ -0,0 +1,43 @@ +//===-- ScriptInterpreterNone.cpp -------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "lldb/Interpreter/ScriptInterpreterNone.h" +#include "lldb/Core/Stream.h" +#include "lldb/Core/StringList.h" +#include "lldb/Core/Debugger.h" +#include "lldb/Interpreter/CommandInterpreter.h" + +using namespace lldb; +using namespace lldb_private; + +ScriptInterpreterNone::ScriptInterpreterNone (CommandInterpreter &interpreter) : + ScriptInterpreter (interpreter, eScriptLanguageNone) +{ +} + +ScriptInterpreterNone::~ScriptInterpreterNone () +{ +} + +bool +ScriptInterpreterNone::ExecuteOneLine (const char *command, CommandReturnObject *, const ExecuteScriptOptions&) +{ + m_interpreter.GetDebugger().GetErrorStream().PutCString ("error: there is no embedded script interpreter in this mode.\n"); + return false; +} + +void +ScriptInterpreterNone::ExecuteInterpreterLoop () +{ + m_interpreter.GetDebugger().GetErrorStream().PutCString ("error: there is no embedded script interpreter in this mode.\n"); +} + + diff --git a/contrib/llvm/tools/lldb/source/Interpreter/ScriptInterpreterPython.cpp b/contrib/llvm/tools/lldb/source/Interpreter/ScriptInterpreterPython.cpp new file mode 100644 index 00000000000..9d9b8d93fb4 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Interpreter/ScriptInterpreterPython.cpp @@ -0,0 +1,3166 @@ +//===-- ScriptInterpreterPython.cpp -----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// In order to guarantee correct working with Python, Python.h *MUST* be +// the *FIRST* header file included here. +#ifdef LLDB_DISABLE_PYTHON + +// Python is disabled in this build + +#else + +#if defined (__APPLE__) +#include +#else +#include +#endif + +#include "lldb/Interpreter/ScriptInterpreterPython.h" + +#include +#include + +#include + +#include "lldb/API/SBValue.h" +#include "lldb/Breakpoint/BreakpointLocation.h" +#include "lldb/Breakpoint/StoppointCallbackContext.h" +#include "lldb/Breakpoint/WatchpointOptions.h" +#include "lldb/Core/Debugger.h" +#include "lldb/Core/Timer.h" +#include "lldb/Host/Host.h" +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Interpreter/CommandReturnObject.h" +#include "lldb/Target/Thread.h" + +using namespace lldb; +using namespace lldb_private; + + +static ScriptInterpreter::SWIGInitCallback g_swig_init_callback = NULL; +static ScriptInterpreter::SWIGBreakpointCallbackFunction g_swig_breakpoint_callback = NULL; +static ScriptInterpreter::SWIGWatchpointCallbackFunction g_swig_watchpoint_callback = NULL; +static ScriptInterpreter::SWIGPythonTypeScriptCallbackFunction g_swig_typescript_callback = NULL; +static ScriptInterpreter::SWIGPythonCreateSyntheticProvider g_swig_synthetic_script = NULL; +static ScriptInterpreter::SWIGPythonCalculateNumChildren g_swig_calc_children = NULL; +static ScriptInterpreter::SWIGPythonGetChildAtIndex g_swig_get_child_index = NULL; +static ScriptInterpreter::SWIGPythonGetIndexOfChildWithName g_swig_get_index_child = NULL; +static ScriptInterpreter::SWIGPythonCastPyObjectToSBValue g_swig_cast_to_sbvalue = NULL; +static ScriptInterpreter::SWIGPythonUpdateSynthProviderInstance g_swig_update_provider = NULL; +static ScriptInterpreter::SWIGPythonMightHaveChildrenSynthProviderInstance g_swig_mighthavechildren_provider = NULL; +static ScriptInterpreter::SWIGPythonCallCommand g_swig_call_command = NULL; +static ScriptInterpreter::SWIGPythonCallModuleInit g_swig_call_module_init = NULL; +static ScriptInterpreter::SWIGPythonCreateOSPlugin g_swig_create_os_plugin = NULL; +static ScriptInterpreter::SWIGPythonScriptKeyword_Process g_swig_run_script_keyword_process = NULL; +static ScriptInterpreter::SWIGPythonScriptKeyword_Thread g_swig_run_script_keyword_thread = NULL; +static ScriptInterpreter::SWIGPythonScriptKeyword_Target g_swig_run_script_keyword_target = NULL; +static ScriptInterpreter::SWIGPythonScriptKeyword_Frame g_swig_run_script_keyword_frame = NULL; + +// these are the Pythonic implementations of the required callbacks +// these are scripting-language specific, which is why they belong here +// we still need to use function pointers to them instead of relying +// on linkage-time resolution because the SWIG stuff and this file +// get built at different times +extern "C" bool +LLDBSwigPythonBreakpointCallbackFunction (const char *python_function_name, + const char *session_dictionary_name, + const lldb::StackFrameSP& sb_frame, + const lldb::BreakpointLocationSP& sb_bp_loc); + +extern "C" bool +LLDBSwigPythonWatchpointCallbackFunction (const char *python_function_name, + const char *session_dictionary_name, + const lldb::StackFrameSP& sb_frame, + const lldb::WatchpointSP& sb_wp); + +extern "C" bool +LLDBSwigPythonCallTypeScript (const char *python_function_name, + void *session_dictionary, + const lldb::ValueObjectSP& valobj_sp, + void** pyfunct_wrapper, + std::string& retval); + +extern "C" void* +LLDBSwigPythonCreateSyntheticProvider (const char *python_class_name, + const char *session_dictionary_name, + const lldb::ValueObjectSP& valobj_sp); + + +extern "C" uint32_t +LLDBSwigPython_CalculateNumChildren (void *implementor); + +extern "C" void * +LLDBSwigPython_GetChildAtIndex (void *implementor, uint32_t idx); + +extern "C" int +LLDBSwigPython_GetIndexOfChildWithName (void *implementor, const char* child_name); + +extern "C" void * +LLDBSWIGPython_CastPyObjectToSBValue (void* data); + +extern "C" bool +LLDBSwigPython_UpdateSynthProviderInstance (void* implementor); + +extern "C" bool +LLDBSwigPython_MightHaveChildrenSynthProviderInstance (void* implementor); + +extern "C" bool +LLDBSwigPythonCallCommand (const char *python_function_name, + const char *session_dictionary_name, + lldb::DebuggerSP& debugger, + const char* args, + lldb_private::CommandReturnObject &cmd_retobj); + +extern "C" bool +LLDBSwigPythonCallModuleInit (const char *python_module_name, + const char *session_dictionary_name, + lldb::DebuggerSP& debugger); + +extern "C" void* +LLDBSWIGPythonCreateOSPlugin (const char *python_class_name, + const char *session_dictionary_name, + const lldb::ProcessSP& process_sp); + +extern "C" bool +LLDBSWIGPythonRunScriptKeywordProcess (const char* python_function_name, + const char* session_dictionary_name, + lldb::ProcessSP& process, + std::string& output); + +extern "C" bool +LLDBSWIGPythonRunScriptKeywordThread (const char* python_function_name, + const char* session_dictionary_name, + lldb::ThreadSP& thread, + std::string& output); + +extern "C" bool +LLDBSWIGPythonRunScriptKeywordTarget (const char* python_function_name, + const char* session_dictionary_name, + lldb::TargetSP& target, + std::string& output); + +extern "C" bool +LLDBSWIGPythonRunScriptKeywordFrame (const char* python_function_name, + const char* session_dictionary_name, + lldb::StackFrameSP& frame, + std::string& output); + +static int +_check_and_flush (FILE *stream) +{ + int prev_fail = ferror (stream); + return fflush (stream) || prev_fail ? EOF : 0; +} + +ScriptInterpreterPython::Locker::Locker (ScriptInterpreterPython *py_interpreter, + uint16_t on_entry, + uint16_t on_leave, + FILE* wait_msg_handle) : + ScriptInterpreterLocker (), + m_teardown_session( (on_leave & TearDownSession) == TearDownSession ), + m_python_interpreter(py_interpreter), + m_tmp_fh(wait_msg_handle) +{ + if (m_python_interpreter && !m_tmp_fh) + m_tmp_fh = (m_python_interpreter->m_dbg_stdout ? m_python_interpreter->m_dbg_stdout : stdout); + + DoAcquireLock(); + if ((on_entry & InitSession) == InitSession) + { + if (DoInitSession((on_entry & InitGlobals) == InitGlobals) == false) + { + // Don't teardown the session if we didn't init it. + m_teardown_session = false; + } + } +} + +bool +ScriptInterpreterPython::Locker::DoAcquireLock() +{ + Log *log (lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_SCRIPT | LIBLLDB_LOG_VERBOSE)); + m_GILState = PyGILState_Ensure(); + if (log) + log->Printf("Ensured PyGILState. Previous state = %slocked\n", m_GILState == PyGILState_UNLOCKED ? "un" : ""); + return true; +} + +bool +ScriptInterpreterPython::Locker::DoInitSession(bool init_lldb_globals) +{ + if (!m_python_interpreter) + return false; + return m_python_interpreter->EnterSession (init_lldb_globals); +} + +bool +ScriptInterpreterPython::Locker::DoFreeLock() +{ + Log *log (lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_SCRIPT | LIBLLDB_LOG_VERBOSE)); + if (log) + log->Printf("Releasing PyGILState. Returning to state = %slocked\n", m_GILState == PyGILState_UNLOCKED ? "un" : ""); + PyGILState_Release(m_GILState); + return true; +} + +bool +ScriptInterpreterPython::Locker::DoTearDownSession() +{ + if (!m_python_interpreter) + return false; + m_python_interpreter->LeaveSession (); + return true; +} + +ScriptInterpreterPython::Locker::~Locker() +{ + if (m_teardown_session) + DoTearDownSession(); + DoFreeLock(); +} + +ScriptInterpreterPython::PythonInputReaderManager::PythonInputReaderManager (ScriptInterpreterPython *interpreter) : +m_interpreter(interpreter), +m_debugger_sp(), +m_reader_sp(), +m_error(false) +{ + if (m_interpreter == NULL) + { + m_error = true; + return; + } + + m_debugger_sp = m_interpreter->GetCommandInterpreter().GetDebugger().shared_from_this(); + + if (!m_debugger_sp) + { + m_error = true; + return; + } + + m_reader_sp = InputReaderSP(new InputReader(*m_debugger_sp.get())); + + if (!m_reader_sp) + { + m_error = true; + return; + } + + Error error (m_reader_sp->Initialize (ScriptInterpreterPython::PythonInputReaderManager::InputReaderCallback, + m_interpreter, // baton + eInputReaderGranularityLine, // token size, to pass to callback function + NULL, // end token + NULL, // prompt + true)); // echo input + if (error.Fail()) + m_error = true; + else + { + m_debugger_sp->PushInputReader (m_reader_sp); + m_interpreter->m_embedded_thread_input_reader_sp = m_reader_sp; + } +} + +ScriptInterpreterPython::PythonInputReaderManager::~PythonInputReaderManager() +{ + // Nothing to do if either m_interpreter or m_reader_sp is invalid. + if (!m_interpreter || !m_reader_sp) + return; + + m_reader_sp->SetIsDone (true); + if (m_debugger_sp) + m_debugger_sp->PopInputReader(m_reader_sp); + + // Only mess with m_interpreter's counterpart if, indeed, they are the same object. + if (m_reader_sp.get() == m_interpreter->m_embedded_thread_input_reader_sp.get()) + { + m_interpreter->m_embedded_thread_pty.CloseSlaveFileDescriptor(); + m_interpreter->m_embedded_thread_input_reader_sp.reset(); + } +} + +size_t +ScriptInterpreterPython::PythonInputReaderManager::InputReaderCallback (void *baton, + InputReader &reader, + InputReaderAction notification, + const char *bytes, + size_t bytes_len) +{ + lldb::thread_t embedded_interpreter_thread; + Log *log (lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_SCRIPT)); + + if (baton == NULL) + return 0; + + ScriptInterpreterPython *script_interpreter = (ScriptInterpreterPython *) baton; + + if (script_interpreter->m_script_lang != eScriptLanguagePython) + return 0; + + switch (notification) + { + case eInputReaderActivate: + { + // Save terminal settings if we can + int input_fd = reader.GetDebugger().GetInputFile().GetDescriptor(); + if (input_fd == File::kInvalidDescriptor) + input_fd = STDIN_FILENO; + + script_interpreter->SaveTerminalState(input_fd); + + char error_str[1024]; + if (script_interpreter->m_embedded_thread_pty.OpenFirstAvailableMaster (O_RDWR|O_NOCTTY, error_str, + sizeof(error_str))) + { + if (log) + log->Printf ("ScriptInterpreterPython::NonInteractiveInputReaderCallback, Activate, succeeded in opening master pty (fd = %d).", + script_interpreter->m_embedded_thread_pty.GetMasterFileDescriptor()); + { + StreamString run_string; + char error_str[1024]; + const char *pty_slave_name = script_interpreter->m_embedded_thread_pty.GetSlaveName (error_str, sizeof (error_str)); + if (pty_slave_name != NULL && PyThreadState_GetDict() != NULL) + { + ScriptInterpreterPython::Locker locker(script_interpreter, + ScriptInterpreterPython::Locker::AcquireLock | ScriptInterpreterPython::Locker::InitSession | ScriptInterpreterPython::Locker::InitGlobals, + ScriptInterpreterPython::Locker::FreeAcquiredLock); + run_string.Printf ("run_one_line (%s, 'save_stderr = sys.stderr')", script_interpreter->m_dictionary_name.c_str()); + PyRun_SimpleString (run_string.GetData()); + run_string.Clear (); + + run_string.Printf ("run_one_line (%s, 'sys.stderr = sys.stdout')", script_interpreter->m_dictionary_name.c_str()); + PyRun_SimpleString (run_string.GetData()); + run_string.Clear (); + + run_string.Printf ("run_one_line (%s, 'save_stdin = sys.stdin')", script_interpreter->m_dictionary_name.c_str()); + PyRun_SimpleString (run_string.GetData()); + run_string.Clear (); + + run_string.Printf ("run_one_line (%s, \"sys.stdin = open ('%s', 'r')\")", script_interpreter->m_dictionary_name.c_str(), + pty_slave_name); + PyRun_SimpleString (run_string.GetData()); + run_string.Clear (); + } + } + embedded_interpreter_thread = Host::ThreadCreate ("", + ScriptInterpreterPython::PythonInputReaderManager::RunPythonInputReader, + script_interpreter, NULL); + if (IS_VALID_LLDB_HOST_THREAD(embedded_interpreter_thread)) + { + if (log) + log->Printf ("ScriptInterpreterPython::NonInteractiveInputReaderCallback, Activate, succeeded in creating thread (thread_t = %p)", (void *)embedded_interpreter_thread); + Error detach_error; + Host::ThreadDetach (embedded_interpreter_thread, &detach_error); + } + else + { + if (log) + log->Printf ("ScriptInterpreterPython::NonInteractiveInputReaderCallback, Activate, failed in creating thread"); + reader.SetIsDone (true); + } + } + else + { + if (log) + log->Printf ("ScriptInterpreterPython::NonInteractiveInputReaderCallback, Activate, failed to open master pty "); + reader.SetIsDone (true); + } + } + break; + + case eInputReaderDeactivate: + // When another input reader is pushed, don't leave the session... + //script_interpreter->LeaveSession (); + break; + + case eInputReaderReactivate: +// { +// ScriptInterpreterPython::Locker locker(script_interpreter, +// ScriptInterpreterPython::Locker::AcquireLock | ScriptInterpreterPython::Locker::InitSession, +// ScriptInterpreterPython::Locker::FreeAcquiredLock); +// } + break; + + case eInputReaderAsynchronousOutputWritten: + break; + + case eInputReaderInterrupt: + { + PyThreadState* state = _PyThreadState_Current; + if (!state) + state = script_interpreter->m_command_thread_state; + if (state) + { + long tid = state->thread_id; + _PyThreadState_Current = state; + int num_threads = PyThreadState_SetAsyncExc(tid, PyExc_KeyboardInterrupt); + if (log) + log->Printf("ScriptInterpreterPython::NonInteractiveInputReaderCallback, eInputReaderInterrupt, tid = %ld, num_threads = %d, state = %p", + tid,num_threads,state); + } + else if (log) + log->Printf("ScriptInterpreterPython::NonInteractiveInputReaderCallback, eInputReaderInterrupt, state = NULL"); + } + break; + + case eInputReaderEndOfFile: + reader.SetIsDone(true); + break; + + case eInputReaderGotToken: + if (script_interpreter->m_embedded_thread_pty.GetMasterFileDescriptor() != -1) + { + if (log) + log->Printf ("ScriptInterpreterPython::NonInteractiveInputReaderCallback, GotToken, bytes='%s', byte_len = %lu", bytes, + bytes_len); + if (bytes && bytes_len) + ::write (script_interpreter->m_embedded_thread_pty.GetMasterFileDescriptor(), bytes, bytes_len); + ::write (script_interpreter->m_embedded_thread_pty.GetMasterFileDescriptor(), "\n", 1); + } + else + { + if (log) + log->Printf ("ScriptInterpreterPython::NonInteractiveInputReaderCallback, GotToken, bytes='%s', byte_len = %lu, Master File Descriptor is bad.", + bytes, + bytes_len); + reader.SetIsDone (true); + } + break; + + case eInputReaderDone: + { + StreamString run_string; + char error_str[1024]; + const char *pty_slave_name = script_interpreter->m_embedded_thread_pty.GetSlaveName (error_str, sizeof (error_str)); + if (pty_slave_name != NULL && PyThreadState_GetDict() != NULL) + { + ScriptInterpreterPython::Locker locker(script_interpreter, + ScriptInterpreterPython::Locker::AcquireLock, + ScriptInterpreterPython::Locker::FreeAcquiredLock); + run_string.Printf ("run_one_line (%s, 'sys.stdin = save_stdin; sys.stderr = save_stderr')", script_interpreter->m_dictionary_name.c_str()); + PyRun_SimpleString (run_string.GetData()); + run_string.Clear(); + } + // Restore terminal settings if they were validly saved + if (log) + log->Printf ("ScriptInterpreterPython::NonInteractiveInputReaderCallback, Done, closing down input reader."); + + script_interpreter->RestoreTerminalState (); + + script_interpreter->m_embedded_thread_pty.CloseMasterFileDescriptor(); + } + break; + } + + return bytes_len; +} + +ScriptInterpreterPython::ScriptInterpreterPython (CommandInterpreter &interpreter) : + ScriptInterpreter (interpreter, eScriptLanguagePython), + m_embedded_thread_pty (), + m_embedded_python_pty (), + m_embedded_thread_input_reader_sp (), + m_embedded_python_input_reader_sp (), + m_dbg_stdout (interpreter.GetDebugger().GetOutputFile().GetStream()), + m_new_sysout (NULL), + m_old_sysout (NULL), + m_old_syserr (NULL), + m_run_one_line (NULL), + m_dictionary_name (interpreter.GetDebugger().GetInstanceName().AsCString()), + m_terminal_state (), + m_session_is_active (false), + m_valid_session (true), + m_command_thread_state (NULL) +{ + + static int g_initialized = false; + + if (!g_initialized) + { + g_initialized = true; + ScriptInterpreterPython::InitializePrivate (); + } + + m_dictionary_name.append("_dict"); + StreamString run_string; + run_string.Printf ("%s = dict()", m_dictionary_name.c_str()); + + Locker locker(this, + ScriptInterpreterPython::Locker::AcquireLock, + ScriptInterpreterPython::Locker::FreeAcquiredLock); + PyRun_SimpleString (run_string.GetData()); + + run_string.Clear(); + + // Importing 'lldb' module calls SBDebugger::Initialize, which calls Debugger::Initialize, which increments a + // global debugger ref-count; therefore we need to check the ref-count before and after importing lldb, and if the + // ref-count increased we need to call Debugger::Terminate here to decrement the ref-count so that when the final + // call to Debugger::Terminate is made, the ref-count has the correct value. + // + // Bonus question: Why doesn't the ref-count always increase? Because sometimes lldb has already been imported, in + // which case the code inside it, including the call to SBDebugger::Initialize(), does not get executed. + + int old_count = Debugger::TestDebuggerRefCount(); + + run_string.Printf ("run_one_line (%s, 'import copy, os, re, sys, uuid, lldb')", m_dictionary_name.c_str()); + PyRun_SimpleString (run_string.GetData()); + + // WARNING: temporary code that loads Cocoa formatters - this should be done on a per-platform basis rather than loading the whole set + // and letting the individual formatter classes exploit APIs to check whether they can/cannot do their task + run_string.Clear(); + run_string.Printf ("run_one_line (%s, 'import lldb.formatters, lldb.formatters.cpp, pydoc')", m_dictionary_name.c_str()); + PyRun_SimpleString (run_string.GetData()); + + int new_count = Debugger::TestDebuggerRefCount(); + + if (new_count > old_count) + Debugger::Terminate(); + + run_string.Clear(); + run_string.Printf ("run_one_line (%s, 'lldb.debugger_unique_id = %" PRIu64 "; pydoc.pager = pydoc.plainpager')", m_dictionary_name.c_str(), + interpreter.GetDebugger().GetID()); + PyRun_SimpleString (run_string.GetData()); + + if (m_dbg_stdout != NULL) + { + m_new_sysout = PyFile_FromFile (m_dbg_stdout, (char *) "", (char *) "w", _check_and_flush); + } + + // get the output file handle from the debugger (if any) + File& out_file = interpreter.GetDebugger().GetOutputFile(); + if (out_file.IsValid()) + ResetOutputFileHandle(out_file.GetStream()); +} + +ScriptInterpreterPython::~ScriptInterpreterPython () +{ + Debugger &debugger = GetCommandInterpreter().GetDebugger(); + + if (m_embedded_thread_input_reader_sp.get() != NULL) + { + m_embedded_thread_input_reader_sp->SetIsDone (true); + m_embedded_thread_pty.CloseSlaveFileDescriptor(); + const InputReaderSP reader_sp = m_embedded_thread_input_reader_sp; + debugger.PopInputReader (reader_sp); + m_embedded_thread_input_reader_sp.reset(); + } + + if (m_embedded_python_input_reader_sp.get() != NULL) + { + m_embedded_python_input_reader_sp->SetIsDone (true); + m_embedded_python_pty.CloseSlaveFileDescriptor(); + const InputReaderSP reader_sp = m_embedded_python_input_reader_sp; + debugger.PopInputReader (reader_sp); + m_embedded_python_input_reader_sp.reset(); + } + + if (m_new_sysout) + { + Locker locker(this, + ScriptInterpreterPython::Locker::AcquireLock, + ScriptInterpreterPython::Locker::FreeLock); + Py_XDECREF ((PyObject*)m_new_sysout); + } +} + +void +ScriptInterpreterPython::ResetOutputFileHandle (FILE *fh) +{ + if (fh == NULL) + return; + + m_dbg_stdout = fh; + + Locker locker(this, + ScriptInterpreterPython::Locker::AcquireLock, + ScriptInterpreterPython::Locker::FreeAcquiredLock); + + m_new_sysout = PyFile_FromFile (m_dbg_stdout, (char *) "", (char *) "w", _check_and_flush); +} + +void +ScriptInterpreterPython::SaveTerminalState (int fd) +{ + // Python mucks with the terminal state of STDIN. If we can possibly avoid + // this by setting the file handles up correctly prior to entering the + // interpreter we should. For now we save and restore the terminal state + // on the input file handle. + m_terminal_state.Save (fd, false); +} + +void +ScriptInterpreterPython::RestoreTerminalState () +{ + // Python mucks with the terminal state of STDIN. If we can possibly avoid + // this by setting the file handles up correctly prior to entering the + // interpreter we should. For now we save and restore the terminal state + // on the input file handle. + m_terminal_state.Restore(); +} + +void +ScriptInterpreterPython::LeaveSession () +{ + Log *log (lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_SCRIPT)); + if (log) + log->PutCString("ScriptInterpreterPython::LeaveSession()"); + + // checking that we have a valid thread state - since we use our own threading and locking + // in some (rare) cases during cleanup Python may end up believing we have no thread state + // and PyImport_AddModule will crash if that is the case - since that seems to only happen + // when destroying the SBDebugger, we can make do without clearing up stdout and stderr + + // rdar://problem/11292882 + // When the current thread state is NULL, PyThreadState_Get() issues a fatal error. + if (PyThreadState_GetDict()) + { + PyObject *sysmod = PyImport_AddModule ("sys"); + PyObject *sysdict = PyModule_GetDict (sysmod); + + if (m_new_sysout && sysmod && sysdict) + { + if (m_old_sysout) + PyDict_SetItemString (sysdict, "stdout", (PyObject*)m_old_sysout); + if (m_old_syserr) + PyDict_SetItemString (sysdict, "stderr", (PyObject*)m_old_syserr); + } + } + + m_session_is_active = false; +} + +bool +ScriptInterpreterPython::EnterSession (bool init_lldb_globals) +{ + // If we have already entered the session, without having officially 'left' it, then there is no need to + // 'enter' it again. + Log *log (lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_SCRIPT)); + if (m_session_is_active) + { + if (log) + log->Printf("ScriptInterpreterPython::EnterSession(init_lldb_globals=%i) session is already active, returning without doing anything", init_lldb_globals); + return false; + } + + if (log) + log->Printf("ScriptInterpreterPython::EnterSession(init_lldb_globals=%i)", init_lldb_globals); + + + m_session_is_active = true; + + StreamString run_string; + + if (init_lldb_globals) + { + run_string.Printf ( "run_one_line (%s, 'lldb.debugger_unique_id = %" PRIu64, m_dictionary_name.c_str(), GetCommandInterpreter().GetDebugger().GetID()); + run_string.Printf ( "; lldb.debugger = lldb.SBDebugger.FindDebuggerWithID (%" PRIu64 ")", GetCommandInterpreter().GetDebugger().GetID()); + run_string.PutCString ("; lldb.target = lldb.debugger.GetSelectedTarget()"); + run_string.PutCString ("; lldb.process = lldb.target.GetProcess()"); + run_string.PutCString ("; lldb.thread = lldb.process.GetSelectedThread ()"); + run_string.PutCString ("; lldb.frame = lldb.thread.GetSelectedFrame ()"); + run_string.PutCString ("')"); + } + else + { + // If we aren't initing the globals, we should still always set the debugger (since that is always unique.) + run_string.Printf ( "run_one_line (%s, \"lldb.debugger_unique_id = %" PRIu64, m_dictionary_name.c_str(), GetCommandInterpreter().GetDebugger().GetID()); + run_string.Printf ( "; lldb.debugger = lldb.SBDebugger.FindDebuggerWithID (%" PRIu64 ")", GetCommandInterpreter().GetDebugger().GetID()); + run_string.PutCString ("\")"); + } + + PyRun_SimpleString (run_string.GetData()); + run_string.Clear(); + + PyObject *sysmod = PyImport_AddModule ("sys"); + PyObject *sysdict = PyModule_GetDict (sysmod); + + if (m_new_sysout && sysmod && sysdict) + { + m_old_sysout = PyDict_GetItemString(sysdict, "stdout"); + m_old_syserr = PyDict_GetItemString(sysdict, "stderr"); + if (m_new_sysout) + { + PyDict_SetItemString (sysdict, "stdout", (PyObject*)m_new_sysout); + PyDict_SetItemString (sysdict, "stderr", (PyObject*)m_new_sysout); + } + } + + if (PyErr_Occurred()) + PyErr_Clear (); + + return true; +} + +static PyObject* +FindSessionDictionary (const char* dict_name) +{ + static std::map g_dict_map; + + ConstString dict(dict_name); + + std::map::iterator iter = g_dict_map.find(dict); + + if (iter != g_dict_map.end()) + return iter->second; + + PyObject *main_mod = PyImport_AddModule ("__main__"); + if (main_mod != NULL) + { + PyObject *main_dict = PyModule_GetDict (main_mod); + if ((main_dict != NULL) + && PyDict_Check (main_dict)) + { + // Go through the main dictionary looking for the correct python script interpreter dictionary + PyObject *key, *value; + Py_ssize_t pos = 0; + + while (PyDict_Next (main_dict, &pos, &key, &value)) + { + // We have stolen references to the key and value objects in the dictionary; we need to increment + // them now so that Python's garbage collector doesn't collect them out from under us. + Py_INCREF (key); + Py_INCREF (value); + if (strcmp (PyString_AsString (key), dict_name) == 0) + { + g_dict_map[dict] = value; + return value; + } + } + } + } + return NULL; +} + +static std::string +GenerateUniqueName (const char* base_name_wanted, + uint32_t& functions_counter, + void* name_token = NULL) +{ + StreamString sstr; + + if (!base_name_wanted) + return std::string(); + + if (!name_token) + sstr.Printf ("%s_%d", base_name_wanted, functions_counter++); + else + sstr.Printf ("%s_%p", base_name_wanted, name_token); + + return sstr.GetString(); +} + +bool +ScriptInterpreterPython::ExecuteOneLine (const char *command, CommandReturnObject *result, const ExecuteScriptOptions &options) +{ + if (!m_valid_session) + return false; + + // We want to call run_one_line, passing in the dictionary and the command string. We cannot do this through + // PyRun_SimpleString here because the command string may contain escaped characters, and putting it inside + // another string to pass to PyRun_SimpleString messes up the escaping. So we use the following more complicated + // method to pass the command string directly down to Python. + + Locker locker(this, + ScriptInterpreterPython::Locker::AcquireLock | ScriptInterpreterPython::Locker::InitSession | (options.GetSetLLDBGlobals() ? ScriptInterpreterPython::Locker::InitGlobals : 0), + ScriptInterpreterPython::Locker::FreeAcquiredLock | ScriptInterpreterPython::Locker::TearDownSession); + + bool success = false; + + if (command) + { + // Find the correct script interpreter dictionary in the main module. + PyObject *script_interpreter_dict = FindSessionDictionary(m_dictionary_name.c_str()); + if (script_interpreter_dict != NULL) + { + PyObject *pfunc = (PyObject*)m_run_one_line; + PyObject *pmod = PyImport_AddModule ("lldb.embedded_interpreter"); + if (pmod != NULL) + { + PyObject *pmod_dict = PyModule_GetDict (pmod); + if ((pmod_dict != NULL) + && PyDict_Check (pmod_dict)) + { + if (!pfunc) + { + PyObject *key, *value; + Py_ssize_t pos = 0; + + while (PyDict_Next (pmod_dict, &pos, &key, &value)) + { + Py_INCREF (key); + Py_INCREF (value); + if (strcmp (PyString_AsString (key), "run_one_line") == 0) + { + pfunc = value; + break; + } + } + m_run_one_line = pfunc; + } + + if (pfunc && PyCallable_Check (pfunc)) + { + PyObject *pargs = Py_BuildValue("(Os)",script_interpreter_dict,command); + if (pargs != NULL) + { + PyObject *pvalue = NULL; + { // scope for PythonInputReaderManager + PythonInputReaderManager py_input(options.GetEnableIO() ? this : NULL); + pvalue = PyObject_CallObject (pfunc, pargs); + } + Py_XDECREF (pargs); + if (pvalue != NULL) + { + Py_XDECREF (pvalue); + success = true; + } + else if (options.GetMaskoutErrors() && PyErr_Occurred ()) + { + PyErr_Print(); + PyErr_Clear(); + } + } + } + } + } + Py_INCREF (script_interpreter_dict); + } + + if (success) + return true; + + // The one-liner failed. Append the error message. + if (result) + result->AppendErrorWithFormat ("python failed attempting to evaluate '%s'\n", command); + return false; + } + + if (result) + result->AppendError ("empty command passed to python\n"); + return false; +} + +size_t +ScriptInterpreterPython::InputReaderCallback +( + void *baton, + InputReader &reader, + InputReaderAction notification, + const char *bytes, + size_t bytes_len +) +{ + lldb::thread_t embedded_interpreter_thread; + Log *log (lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_SCRIPT)); + + if (baton == NULL) + return 0; + + ScriptInterpreterPython *script_interpreter = (ScriptInterpreterPython *) baton; + + if (script_interpreter->m_script_lang != eScriptLanguagePython) + return 0; + + switch (notification) + { + case eInputReaderActivate: + { + StreamSP out_stream = reader.GetDebugger().GetAsyncOutputStream(); + bool batch_mode = reader.GetDebugger().GetCommandInterpreter().GetBatchCommandMode(); + if (!batch_mode) + { + out_stream->Printf ("Python Interactive Interpreter. To exit, type 'quit()', 'exit()' or Ctrl-D.\n"); + out_stream->Flush(); + } + + // Save terminal settings if we can + int input_fd = reader.GetDebugger().GetInputFile().GetDescriptor(); + if (input_fd == File::kInvalidDescriptor) + input_fd = STDIN_FILENO; + + script_interpreter->SaveTerminalState(input_fd); + + { + ScriptInterpreterPython::Locker locker(script_interpreter, + ScriptInterpreterPython::Locker::AcquireLock | ScriptInterpreterPython::Locker::InitSession | ScriptInterpreterPython::Locker::InitGlobals, + ScriptInterpreterPython::Locker::FreeAcquiredLock); + } + + char error_str[1024]; + if (script_interpreter->m_embedded_python_pty.OpenFirstAvailableMaster (O_RDWR|O_NOCTTY, error_str, + sizeof(error_str))) + { + if (log) + log->Printf ("ScriptInterpreterPython::InputReaderCallback, Activate, succeeded in opening master pty (fd = %d).", + script_interpreter->m_embedded_python_pty.GetMasterFileDescriptor()); + embedded_interpreter_thread = Host::ThreadCreate ("", + ScriptInterpreterPython::RunEmbeddedPythonInterpreter, + script_interpreter, NULL); + if (IS_VALID_LLDB_HOST_THREAD(embedded_interpreter_thread)) + { + if (log) + log->Printf ("ScriptInterpreterPython::InputReaderCallback, Activate, succeeded in creating thread (thread_t = %p)", (void *)embedded_interpreter_thread); + Error detach_error; + Host::ThreadDetach (embedded_interpreter_thread, &detach_error); + } + else + { + if (log) + log->Printf ("ScriptInterpreterPython::InputReaderCallback, Activate, failed in creating thread"); + reader.SetIsDone (true); + } + } + else + { + if (log) + log->Printf ("ScriptInterpreterPython::InputReaderCallback, Activate, failed to open master pty "); + reader.SetIsDone (true); + } + } + break; + + case eInputReaderDeactivate: + // When another input reader is pushed, don't leave the session... + //script_interpreter->LeaveSession (); + break; + + case eInputReaderReactivate: + { + ScriptInterpreterPython::Locker locker (script_interpreter, + ScriptInterpreterPython::Locker::AcquireLock | ScriptInterpreterPython::Locker::InitSession, + ScriptInterpreterPython::Locker::FreeAcquiredLock); + } + break; + + case eInputReaderAsynchronousOutputWritten: + break; + + case eInputReaderInterrupt: + ::write (script_interpreter->m_embedded_python_pty.GetMasterFileDescriptor(), "raise KeyboardInterrupt\n", 24); + break; + + case eInputReaderEndOfFile: + ::write (script_interpreter->m_embedded_python_pty.GetMasterFileDescriptor(), "quit()\n", 7); + break; + + case eInputReaderGotToken: + if (script_interpreter->m_embedded_python_pty.GetMasterFileDescriptor() != -1) + { + if (log) + log->Printf ("ScriptInterpreterPython::InputReaderCallback, GotToken, bytes='%s', byte_len = %lu", bytes, + bytes_len); + if (bytes && bytes_len) + { + if ((int) bytes[0] == 4) + ::write (script_interpreter->m_embedded_python_pty.GetMasterFileDescriptor(), "quit()", 6); + else + ::write (script_interpreter->m_embedded_python_pty.GetMasterFileDescriptor(), bytes, bytes_len); + } + ::write (script_interpreter->m_embedded_python_pty.GetMasterFileDescriptor(), "\n", 1); + } + else + { + if (log) + log->Printf ("ScriptInterpreterPython::InputReaderCallback, GotToken, bytes='%s', byte_len = %lu, Master File Descriptor is bad.", + bytes, + bytes_len); + reader.SetIsDone (true); + } + + break; + + case eInputReaderDone: + { + Locker locker(script_interpreter, + ScriptInterpreterPython::Locker::AcquireLock, + ScriptInterpreterPython::Locker::FreeAcquiredLock); + script_interpreter->LeaveSession (); + } + + // Restore terminal settings if they were validly saved + if (log) + log->Printf ("ScriptInterpreterPython::InputReaderCallback, Done, closing down input reader."); + + script_interpreter->RestoreTerminalState (); + + script_interpreter->m_embedded_python_pty.CloseMasterFileDescriptor(); + break; + } + + return bytes_len; +} + + +void +ScriptInterpreterPython::ExecuteInterpreterLoop () +{ + Timer scoped_timer (__PRETTY_FUNCTION__, __PRETTY_FUNCTION__); + + Debugger &debugger = GetCommandInterpreter().GetDebugger(); + + // At the moment, the only time the debugger does not have an input file handle is when this is called + // directly from Python, in which case it is both dangerous and unnecessary (not to mention confusing) to + // try to embed a running interpreter loop inside the already running Python interpreter loop, so we won't + // do it. + + if (!debugger.GetInputFile().IsValid()) + return; + + InputReaderSP reader_sp (new InputReader(debugger)); + if (reader_sp) + { + Error error (reader_sp->Initialize (ScriptInterpreterPython::InputReaderCallback, + this, // baton + eInputReaderGranularityLine, // token size, to pass to callback function + NULL, // end token + NULL, // prompt + true)); // echo input + + if (error.Success()) + { + debugger.PushInputReader (reader_sp); + m_embedded_python_input_reader_sp = reader_sp; + } + } +} + +bool +ScriptInterpreterPython::ExecuteOneLineWithReturn (const char *in_string, + ScriptInterpreter::ScriptReturnType return_type, + void *ret_value, + const ExecuteScriptOptions &options) +{ + + Locker locker(this, + ScriptInterpreterPython::Locker::AcquireLock | ScriptInterpreterPython::Locker::InitSession | (options.GetSetLLDBGlobals() ? ScriptInterpreterPython::Locker::InitGlobals : 0), + ScriptInterpreterPython::Locker::FreeAcquiredLock | ScriptInterpreterPython::Locker::TearDownSession); + + PyObject *py_return = NULL; + PyObject *mainmod = PyImport_AddModule ("__main__"); + PyObject *globals = PyModule_GetDict (mainmod); + PyObject *locals = NULL; + PyObject *py_error = NULL; + bool ret_success = false; + bool should_decrement_locals = false; + int success; + + locals = FindSessionDictionary(m_dictionary_name.c_str()); + + if (locals == NULL) + { + locals = PyObject_GetAttrString (globals, m_dictionary_name.c_str()); + should_decrement_locals = true; + } + + if (locals == NULL) + { + locals = globals; + should_decrement_locals = false; + } + + py_error = PyErr_Occurred(); + if (py_error != NULL) + PyErr_Clear(); + + if (in_string != NULL) + { + { // scope for PythonInputReaderManager + PythonInputReaderManager py_input(options.GetEnableIO() ? this : NULL); + py_return = PyRun_String (in_string, Py_eval_input, globals, locals); + if (py_return == NULL) + { + py_error = PyErr_Occurred (); + if (py_error != NULL) + PyErr_Clear (); + + py_return = PyRun_String (in_string, Py_single_input, globals, locals); + } + } + + if (locals != NULL + && should_decrement_locals) + Py_XDECREF (locals); + + if (py_return != NULL) + { + switch (return_type) + { + case eScriptReturnTypeCharPtr: // "char *" + { + const char format[3] = "s#"; + success = PyArg_Parse (py_return, format, (char **) ret_value); + break; + } + case eScriptReturnTypeCharStrOrNone: // char* or NULL if py_return == Py_None + { + const char format[3] = "z"; + success = PyArg_Parse (py_return, format, (char **) ret_value); + break; + } + case eScriptReturnTypeBool: + { + const char format[2] = "b"; + success = PyArg_Parse (py_return, format, (bool *) ret_value); + break; + } + case eScriptReturnTypeShortInt: + { + const char format[2] = "h"; + success = PyArg_Parse (py_return, format, (short *) ret_value); + break; + } + case eScriptReturnTypeShortIntUnsigned: + { + const char format[2] = "H"; + success = PyArg_Parse (py_return, format, (unsigned short *) ret_value); + break; + } + case eScriptReturnTypeInt: + { + const char format[2] = "i"; + success = PyArg_Parse (py_return, format, (int *) ret_value); + break; + } + case eScriptReturnTypeIntUnsigned: + { + const char format[2] = "I"; + success = PyArg_Parse (py_return, format, (unsigned int *) ret_value); + break; + } + case eScriptReturnTypeLongInt: + { + const char format[2] = "l"; + success = PyArg_Parse (py_return, format, (long *) ret_value); + break; + } + case eScriptReturnTypeLongIntUnsigned: + { + const char format[2] = "k"; + success = PyArg_Parse (py_return, format, (unsigned long *) ret_value); + break; + } + case eScriptReturnTypeLongLong: + { + const char format[2] = "L"; + success = PyArg_Parse (py_return, format, (long long *) ret_value); + break; + } + case eScriptReturnTypeLongLongUnsigned: + { + const char format[2] = "K"; + success = PyArg_Parse (py_return, format, (unsigned long long *) ret_value); + break; + } + case eScriptReturnTypeFloat: + { + const char format[2] = "f"; + success = PyArg_Parse (py_return, format, (float *) ret_value); + break; + } + case eScriptReturnTypeDouble: + { + const char format[2] = "d"; + success = PyArg_Parse (py_return, format, (double *) ret_value); + break; + } + case eScriptReturnTypeChar: + { + const char format[2] = "c"; + success = PyArg_Parse (py_return, format, (char *) ret_value); + break; + } + } + Py_XDECREF (py_return); + if (success) + ret_success = true; + else + ret_success = false; + } + } + + py_error = PyErr_Occurred(); + if (py_error != NULL) + { + ret_success = false; + if (options.GetMaskoutErrors()) + { + if (PyErr_GivenExceptionMatches (py_error, PyExc_SyntaxError)) + PyErr_Print (); + PyErr_Clear(); + } + } + + return ret_success; +} + +bool +ScriptInterpreterPython::ExecuteMultipleLines (const char *in_string, const ExecuteScriptOptions &options) +{ + + + Locker locker(this, + ScriptInterpreterPython::Locker::AcquireLock | ScriptInterpreterPython::Locker::InitSession | (options.GetSetLLDBGlobals() ? ScriptInterpreterPython::Locker::InitGlobals : 0), + ScriptInterpreterPython::Locker::FreeAcquiredLock | ScriptInterpreterPython::Locker::TearDownSession); + + bool success = false; + PyObject *py_return = NULL; + PyObject *mainmod = PyImport_AddModule ("__main__"); + PyObject *globals = PyModule_GetDict (mainmod); + PyObject *locals = NULL; + PyObject *py_error = NULL; + bool should_decrement_locals = false; + + locals = FindSessionDictionary(m_dictionary_name.c_str()); + + if (locals == NULL) + { + locals = PyObject_GetAttrString (globals, m_dictionary_name.c_str()); + should_decrement_locals = true; + } + + if (locals == NULL) + { + locals = globals; + should_decrement_locals = false; + } + + py_error = PyErr_Occurred(); + if (py_error != NULL) + PyErr_Clear(); + + if (in_string != NULL) + { + struct _node *compiled_node = PyParser_SimpleParseString (in_string, Py_file_input); + if (compiled_node) + { + PyCodeObject *compiled_code = PyNode_Compile (compiled_node, "temp.py"); + if (compiled_code) + { + { // scope for PythonInputReaderManager + PythonInputReaderManager py_input(options.GetEnableIO() ? this : NULL); + py_return = PyEval_EvalCode (compiled_code, globals, locals); + } + if (py_return != NULL) + { + success = true; + Py_XDECREF (py_return); + } + if (locals && should_decrement_locals) + Py_XDECREF (locals); + } + } + } + + py_error = PyErr_Occurred (); + if (py_error != NULL) + { + success = false; + if (options.GetMaskoutErrors()) + { + if (PyErr_GivenExceptionMatches (py_error, PyExc_SyntaxError)) + PyErr_Print (); + PyErr_Clear(); + } + } + + return success; +} + +static const char *g_reader_instructions = "Enter your Python command(s). Type 'DONE' to end."; + +static const char *g_bkpt_command_reader_instructions = "Enter your Python command(s). Type 'DONE' to end.\n" + "def function(frame,bp_loc,internal_dict):\n" + " \"\"\"frame: the SBFrame for the location at which you stopped\n" + " bp_loc: an SBBreakpointLocation for the breakpoint location information\n" + " internal_dict: an LLDB support object not to be used\"\"\""; + +size_t +ScriptInterpreterPython::GenerateBreakpointOptionsCommandCallback +( + void *baton, + InputReader &reader, + InputReaderAction notification, + const char *bytes, + size_t bytes_len +) +{ + static StringList commands_in_progress; + + switch (notification) + { + case eInputReaderActivate: + { + + StreamSP out_stream = reader.GetDebugger().GetAsyncOutputStream(); + bool batch_mode = reader.GetDebugger().GetCommandInterpreter().GetBatchCommandMode(); + commands_in_progress.Clear(); + if (!batch_mode) + { + out_stream->Printf ("%s\n", g_bkpt_command_reader_instructions); + if (reader.GetPrompt()) + out_stream->Printf ("%s", reader.GetPrompt()); + out_stream->Flush (); + } + } + break; + + case eInputReaderDeactivate: + break; + + case eInputReaderReactivate: + { + StreamSP out_stream = reader.GetDebugger().GetAsyncOutputStream(); + bool batch_mode = reader.GetDebugger().GetCommandInterpreter().GetBatchCommandMode(); + if (reader.GetPrompt() && !batch_mode) + { + out_stream->Printf ("%s", reader.GetPrompt()); + out_stream->Flush (); + } + } + break; + + case eInputReaderAsynchronousOutputWritten: + break; + + case eInputReaderGotToken: + { + StreamSP out_stream = reader.GetDebugger().GetAsyncOutputStream(); + bool batch_mode = reader.GetDebugger().GetCommandInterpreter().GetBatchCommandMode(); + std::string temp_string (bytes, bytes_len); + commands_in_progress.AppendString (temp_string.c_str()); + if (!reader.IsDone() && reader.GetPrompt() && !batch_mode) + { + out_stream->Printf ("%s", reader.GetPrompt()); + out_stream->Flush (); + } + } + break; + + case eInputReaderEndOfFile: + case eInputReaderInterrupt: + // Control-c (SIGINT) & control-d both mean finish & exit. + reader.SetIsDone(true); + + // Control-c (SIGINT) ALSO means cancel; do NOT create a breakpoint command. + if (notification == eInputReaderInterrupt) + commands_in_progress.Clear(); + + // Fall through here... + + case eInputReaderDone: + { + bool batch_mode = notification == eInputReaderDone ? + reader.GetDebugger().GetCommandInterpreter().GetBatchCommandMode() : + true; + BreakpointOptions *bp_options = (BreakpointOptions *)baton; + std::unique_ptr data_ap(new BreakpointOptions::CommandData()); + data_ap->user_source.AppendList (commands_in_progress); + if (data_ap.get()) + { + ScriptInterpreter *interpreter = reader.GetDebugger().GetCommandInterpreter().GetScriptInterpreter(); + if (interpreter) + { + if (interpreter->GenerateBreakpointCommandCallbackData (data_ap->user_source, + data_ap->script_source)) + { + BatonSP baton_sp (new BreakpointOptions::CommandBaton (data_ap.release())); + bp_options->SetCallback (ScriptInterpreterPython::BreakpointCallbackFunction, baton_sp); + } + else if (!batch_mode) + { + StreamSP out_stream = reader.GetDebugger().GetAsyncOutputStream(); + out_stream->Printf ("Warning: No command attached to breakpoint.\n"); + out_stream->Flush(); + } + } + else + { + if (!batch_mode) + { + StreamSP out_stream = reader.GetDebugger().GetAsyncOutputStream(); + out_stream->Printf ("Warning: Unable to find script intepreter; no command attached to breakpoint.\n"); + out_stream->Flush(); + } + } + } + } + break; + + } + + return bytes_len; +} + +size_t +ScriptInterpreterPython::GenerateWatchpointOptionsCommandCallback +( + void *baton, + InputReader &reader, + InputReaderAction notification, + const char *bytes, + size_t bytes_len +) +{ + static StringList commands_in_progress; + + switch (notification) + { + case eInputReaderActivate: + { + StreamSP out_stream = reader.GetDebugger().GetAsyncOutputStream(); + bool batch_mode = reader.GetDebugger().GetCommandInterpreter().GetBatchCommandMode(); + + commands_in_progress.Clear(); + if (!batch_mode) + { + out_stream->Printf ("%s\n", g_reader_instructions); + if (reader.GetPrompt()) + out_stream->Printf ("%s", reader.GetPrompt()); + out_stream->Flush (); + } + } + break; + + case eInputReaderDeactivate: + break; + + case eInputReaderReactivate: + { + StreamSP out_stream = reader.GetDebugger().GetAsyncOutputStream(); + bool batch_mode = reader.GetDebugger().GetCommandInterpreter().GetBatchCommandMode(); + if (reader.GetPrompt() && !batch_mode) + { + out_stream->Printf ("%s", reader.GetPrompt()); + out_stream->Flush (); + } + } + break; + + case eInputReaderAsynchronousOutputWritten: + break; + + case eInputReaderGotToken: + { + StreamSP out_stream = reader.GetDebugger().GetAsyncOutputStream(); + bool batch_mode = reader.GetDebugger().GetCommandInterpreter().GetBatchCommandMode(); + std::string temp_string (bytes, bytes_len); + commands_in_progress.AppendString (temp_string.c_str()); + if (!reader.IsDone() && reader.GetPrompt() && !batch_mode) + { + out_stream->Printf ("%s", reader.GetPrompt()); + out_stream->Flush (); + } + } + break; + + case eInputReaderEndOfFile: + case eInputReaderInterrupt: + // Control-c (SIGINT) & control-d both mean finish & exit. + reader.SetIsDone(true); + + // Control-c (SIGINT) ALSO means cancel; do NOT create a breakpoint command. + if (notification == eInputReaderInterrupt) + commands_in_progress.Clear(); + + // Fall through here... + + case eInputReaderDone: + { + bool batch_mode = notification == eInputReaderDone ? + reader.GetDebugger().GetCommandInterpreter().GetBatchCommandMode() : + true; + WatchpointOptions *wp_options = (WatchpointOptions *)baton; + std::unique_ptr data_ap(new WatchpointOptions::CommandData()); + data_ap->user_source.AppendList (commands_in_progress); + if (data_ap.get()) + { + ScriptInterpreter *interpreter = reader.GetDebugger().GetCommandInterpreter().GetScriptInterpreter(); + if (interpreter) + { + if (interpreter->GenerateWatchpointCommandCallbackData (data_ap->user_source, + data_ap->script_source)) + { + BatonSP baton_sp (new WatchpointOptions::CommandBaton (data_ap.release())); + wp_options->SetCallback (ScriptInterpreterPython::WatchpointCallbackFunction, baton_sp); + } + else if (!batch_mode) + { + StreamSP out_stream = reader.GetDebugger().GetAsyncOutputStream(); + out_stream->Printf ("Warning: No command attached to breakpoint.\n"); + out_stream->Flush(); + } + } + else + { + if (!batch_mode) + { + StreamSP out_stream = reader.GetDebugger().GetAsyncOutputStream(); + out_stream->Printf ("Warning: Unable to find script intepreter; no command attached to breakpoint.\n"); + out_stream->Flush(); + } + } + } + } + break; + + } + + return bytes_len; +} + +void +ScriptInterpreterPython::CollectDataForBreakpointCommandCallback (BreakpointOptions *bp_options, + CommandReturnObject &result) +{ + Debugger &debugger = GetCommandInterpreter().GetDebugger(); + + InputReaderSP reader_sp (new InputReader (debugger)); + + if (reader_sp) + { + Error err = reader_sp->Initialize ( + ScriptInterpreterPython::GenerateBreakpointOptionsCommandCallback, + bp_options, // baton + eInputReaderGranularityLine, // token size, for feeding data to callback function + "DONE", // end token + " ", // prompt + true); // echo input + + if (err.Success()) + debugger.PushInputReader (reader_sp); + else + { + result.AppendError (err.AsCString()); + result.SetStatus (eReturnStatusFailed); + } + } + else + { + result.AppendError("out of memory"); + result.SetStatus (eReturnStatusFailed); + } +} + +void +ScriptInterpreterPython::CollectDataForWatchpointCommandCallback (WatchpointOptions *wp_options, + CommandReturnObject &result) +{ + Debugger &debugger = GetCommandInterpreter().GetDebugger(); + + InputReaderSP reader_sp (new InputReader (debugger)); + + if (reader_sp) + { + Error err = reader_sp->Initialize ( + ScriptInterpreterPython::GenerateWatchpointOptionsCommandCallback, + wp_options, // baton + eInputReaderGranularityLine, // token size, for feeding data to callback function + "DONE", // end token + "> ", // prompt + true); // echo input + + if (err.Success()) + debugger.PushInputReader (reader_sp); + else + { + result.AppendError (err.AsCString()); + result.SetStatus (eReturnStatusFailed); + } + } + else + { + result.AppendError("out of memory"); + result.SetStatus (eReturnStatusFailed); + } +} + +// Set a Python one-liner as the callback for the breakpoint. +void +ScriptInterpreterPython::SetBreakpointCommandCallback (BreakpointOptions *bp_options, + const char *oneliner) +{ + std::unique_ptr data_ap(new BreakpointOptions::CommandData()); + + // It's necessary to set both user_source and script_source to the oneliner. + // The former is used to generate callback description (as in breakpoint command list) + // while the latter is used for Python to interpret during the actual callback. + + data_ap->user_source.AppendString (oneliner); + data_ap->script_source.assign (oneliner); + + if (GenerateBreakpointCommandCallbackData (data_ap->user_source, data_ap->script_source)) + { + BatonSP baton_sp (new BreakpointOptions::CommandBaton (data_ap.release())); + bp_options->SetCallback (ScriptInterpreterPython::BreakpointCallbackFunction, baton_sp); + } + + return; +} + +// Set a Python one-liner as the callback for the watchpoint. +void +ScriptInterpreterPython::SetWatchpointCommandCallback (WatchpointOptions *wp_options, + const char *oneliner) +{ + std::unique_ptr data_ap(new WatchpointOptions::CommandData()); + + // It's necessary to set both user_source and script_source to the oneliner. + // The former is used to generate callback description (as in watchpoint command list) + // while the latter is used for Python to interpret during the actual callback. + + data_ap->user_source.AppendString (oneliner); + data_ap->script_source.assign (oneliner); + + if (GenerateWatchpointCommandCallbackData (data_ap->user_source, data_ap->script_source)) + { + BatonSP baton_sp (new WatchpointOptions::CommandBaton (data_ap.release())); + wp_options->SetCallback (ScriptInterpreterPython::WatchpointCallbackFunction, baton_sp); + } + + return; +} + +bool +ScriptInterpreterPython::ExportFunctionDefinitionToInterpreter (StringList &function_def) +{ + // Convert StringList to one long, newline delimited, const char *. + std::string function_def_string(function_def.CopyList()); + + return ExecuteMultipleLines (function_def_string.c_str(), ScriptInterpreter::ExecuteScriptOptions().SetEnableIO(false)); +} + +bool +ScriptInterpreterPython::GenerateFunction(const char *signature, const StringList &input) +{ + int num_lines = input.GetSize (); + if (num_lines == 0) + return false; + + if (!signature || *signature == 0) + return false; + + StreamString sstr; + StringList auto_generated_function; + auto_generated_function.AppendString (signature); + auto_generated_function.AppendString (" global_dict = globals()"); // Grab the global dictionary + auto_generated_function.AppendString (" new_keys = internal_dict.keys()"); // Make a list of keys in the session dict + auto_generated_function.AppendString (" old_keys = global_dict.keys()"); // Save list of keys in global dict + auto_generated_function.AppendString (" global_dict.update (internal_dict)"); // Add the session dictionary to the + // global dictionary. + + // Wrap everything up inside the function, increasing the indentation. + + auto_generated_function.AppendString(" if True:"); + for (int i = 0; i < num_lines; ++i) + { + sstr.Clear (); + sstr.Printf (" %s", input.GetStringAtIndex (i)); + auto_generated_function.AppendString (sstr.GetData()); + } + auto_generated_function.AppendString (" for key in new_keys:"); // Iterate over all the keys from session dict + auto_generated_function.AppendString (" internal_dict[key] = global_dict[key]"); // Update session dict values + auto_generated_function.AppendString (" if key not in old_keys:"); // If key was not originally in global dict + auto_generated_function.AppendString (" del global_dict[key]"); // ...then remove key/value from global dict + + // Verify that the results are valid Python. + + if (!ExportFunctionDefinitionToInterpreter (auto_generated_function)) + return false; + + return true; + +} + +bool +ScriptInterpreterPython::GenerateTypeScriptFunction (StringList &user_input, std::string& output, void* name_token) +{ + static uint32_t num_created_functions = 0; + user_input.RemoveBlankLines (); + StreamString sstr; + + // Check to see if we have any data; if not, just return. + if (user_input.GetSize() == 0) + return false; + + // Take what the user wrote, wrap it all up inside one big auto-generated Python function, passing in the + // ValueObject as parameter to the function. + + std::string auto_generated_function_name(GenerateUniqueName("lldb_autogen_python_type_print_func", num_created_functions, name_token)); + sstr.Printf ("def %s (valobj, internal_dict):", auto_generated_function_name.c_str()); + + if (!GenerateFunction(sstr.GetData(), user_input)) + return false; + + // Store the name of the auto-generated function to be called. + output.assign(auto_generated_function_name); + return true; +} + +bool +ScriptInterpreterPython::GenerateScriptAliasFunction (StringList &user_input, std::string &output) +{ + static uint32_t num_created_functions = 0; + user_input.RemoveBlankLines (); + StreamString sstr; + + // Check to see if we have any data; if not, just return. + if (user_input.GetSize() == 0) + return false; + + std::string auto_generated_function_name(GenerateUniqueName("lldb_autogen_python_cmd_alias_func", num_created_functions)); + + sstr.Printf ("def %s (debugger, args, result, internal_dict):", auto_generated_function_name.c_str()); + + if (!GenerateFunction(sstr.GetData(),user_input)) + return false; + + // Store the name of the auto-generated function to be called. + output.assign(auto_generated_function_name); + return true; +} + + +bool +ScriptInterpreterPython::GenerateTypeSynthClass (StringList &user_input, std::string &output, void* name_token) +{ + static uint32_t num_created_classes = 0; + user_input.RemoveBlankLines (); + int num_lines = user_input.GetSize (); + StreamString sstr; + + // Check to see if we have any data; if not, just return. + if (user_input.GetSize() == 0) + return false; + + // Wrap all user input into a Python class + + std::string auto_generated_class_name(GenerateUniqueName("lldb_autogen_python_type_synth_class",num_created_classes,name_token)); + + StringList auto_generated_class; + + // Create the function name & definition string. + + sstr.Printf ("class %s:", auto_generated_class_name.c_str()); + auto_generated_class.AppendString (sstr.GetData()); + + // Wrap everything up inside the class, increasing the indentation. + // we don't need to play any fancy indentation tricks here because there is no + // surrounding code whose indentation we need to honor + for (int i = 0; i < num_lines; ++i) + { + sstr.Clear (); + sstr.Printf (" %s", user_input.GetStringAtIndex (i)); + auto_generated_class.AppendString (sstr.GetData()); + } + + + // Verify that the results are valid Python. + // (even though the method is ExportFunctionDefinitionToInterpreter, a class will actually be exported) + // (TODO: rename that method to ExportDefinitionToInterpreter) + if (!ExportFunctionDefinitionToInterpreter (auto_generated_class)) + return false; + + // Store the name of the auto-generated class + + output.assign(auto_generated_class_name); + return true; +} + +lldb::ScriptInterpreterObjectSP +ScriptInterpreterPython::OSPlugin_CreatePluginObject (const char *class_name, lldb::ProcessSP process_sp) +{ + if (class_name == NULL || class_name[0] == '\0') + return lldb::ScriptInterpreterObjectSP(); + + if (!process_sp) + return lldb::ScriptInterpreterObjectSP(); + + void* ret_val; + + { + Locker py_lock(this,Locker::AcquireLock,Locker::FreeLock); + ret_val = g_swig_create_os_plugin (class_name, + m_dictionary_name.c_str(), + process_sp); + } + + return MakeScriptObject(ret_val); +} + +lldb::ScriptInterpreterObjectSP +ScriptInterpreterPython::OSPlugin_RegisterInfo (lldb::ScriptInterpreterObjectSP os_plugin_object_sp) +{ + Locker py_lock(this,Locker::AcquireLock,Locker::FreeLock); + + static char callee_name[] = "get_register_info"; + + if (!os_plugin_object_sp) + return lldb::ScriptInterpreterObjectSP(); + + PyObject* implementor = (PyObject*)os_plugin_object_sp->GetObject(); + + if (implementor == NULL || implementor == Py_None) + return lldb::ScriptInterpreterObjectSP(); + + PyObject* pmeth = PyObject_GetAttrString(implementor, callee_name); + + if (PyErr_Occurred()) + { + PyErr_Clear(); + } + + if (pmeth == NULL || pmeth == Py_None) + { + Py_XDECREF(pmeth); + return lldb::ScriptInterpreterObjectSP(); + } + + if (PyCallable_Check(pmeth) == 0) + { + if (PyErr_Occurred()) + { + PyErr_Clear(); + } + + Py_XDECREF(pmeth); + return lldb::ScriptInterpreterObjectSP(); + } + + if (PyErr_Occurred()) + { + PyErr_Clear(); + } + + Py_XDECREF(pmeth); + + // right now we know this function exists and is callable.. + PyObject* py_return = PyObject_CallMethod(implementor, callee_name, NULL); + + // if it fails, print the error but otherwise go on + if (PyErr_Occurred()) + { + PyErr_Print(); + PyErr_Clear(); + } + + return MakeScriptObject(py_return); +} + +lldb::ScriptInterpreterObjectSP +ScriptInterpreterPython::OSPlugin_ThreadsInfo (lldb::ScriptInterpreterObjectSP os_plugin_object_sp) +{ + Locker py_lock(this,Locker::AcquireLock,Locker::FreeLock); + + static char callee_name[] = "get_thread_info"; + + if (!os_plugin_object_sp) + return lldb::ScriptInterpreterObjectSP(); + + PyObject* implementor = (PyObject*)os_plugin_object_sp->GetObject(); + + if (implementor == NULL || implementor == Py_None) + return lldb::ScriptInterpreterObjectSP(); + + PyObject* pmeth = PyObject_GetAttrString(implementor, callee_name); + + if (PyErr_Occurred()) + { + PyErr_Clear(); + } + + if (pmeth == NULL || pmeth == Py_None) + { + Py_XDECREF(pmeth); + return lldb::ScriptInterpreterObjectSP(); + } + + if (PyCallable_Check(pmeth) == 0) + { + if (PyErr_Occurred()) + { + PyErr_Clear(); + } + + Py_XDECREF(pmeth); + return lldb::ScriptInterpreterObjectSP(); + } + + if (PyErr_Occurred()) + { + PyErr_Clear(); + } + + Py_XDECREF(pmeth); + + // right now we know this function exists and is callable.. + PyObject* py_return = PyObject_CallMethod(implementor, callee_name, NULL); + + // if it fails, print the error but otherwise go on + if (PyErr_Occurred()) + { + PyErr_Print(); + PyErr_Clear(); + } + + return MakeScriptObject(py_return); +} + +// GetPythonValueFormatString provides a system independent type safe way to +// convert a variable's type into a python value format. Python value formats +// are defined in terms of builtin C types and could change from system to +// as the underlying typedef for uint* types, size_t, off_t and other values +// change. + +template +const char *GetPythonValueFormatString(T t) +{ + assert(!"Unhandled type passed to GetPythonValueFormatString(T), make a specialization of GetPythonValueFormatString() to support this type."); + return NULL; +} +template <> const char *GetPythonValueFormatString (char *) { return "s"; } +template <> const char *GetPythonValueFormatString (char) { return "b"; } +template <> const char *GetPythonValueFormatString (unsigned char) { return "B"; } +template <> const char *GetPythonValueFormatString (short) { return "h"; } +template <> const char *GetPythonValueFormatString (unsigned short) { return "H"; } +template <> const char *GetPythonValueFormatString (int) { return "i"; } +template <> const char *GetPythonValueFormatString (unsigned int) { return "I"; } +template <> const char *GetPythonValueFormatString (long) { return "l"; } +template <> const char *GetPythonValueFormatString (unsigned long) { return "k"; } +template <> const char *GetPythonValueFormatString (long long) { return "L"; } +template <> const char *GetPythonValueFormatString (unsigned long long) { return "K"; } +template <> const char *GetPythonValueFormatString (float t) { return "f"; } +template <> const char *GetPythonValueFormatString (double t) { return "d"; } + +lldb::ScriptInterpreterObjectSP +ScriptInterpreterPython::OSPlugin_RegisterContextData (lldb::ScriptInterpreterObjectSP os_plugin_object_sp, + lldb::tid_t tid) +{ + Locker py_lock(this,Locker::AcquireLock,Locker::FreeLock); + + static char callee_name[] = "get_register_data"; + static char *param_format = const_cast(GetPythonValueFormatString(tid)); + + if (!os_plugin_object_sp) + return lldb::ScriptInterpreterObjectSP(); + + PyObject* implementor = (PyObject*)os_plugin_object_sp->GetObject(); + + if (implementor == NULL || implementor == Py_None) + return lldb::ScriptInterpreterObjectSP(); + + PyObject* pmeth = PyObject_GetAttrString(implementor, callee_name); + + if (PyErr_Occurred()) + { + PyErr_Clear(); + } + + if (pmeth == NULL || pmeth == Py_None) + { + Py_XDECREF(pmeth); + return lldb::ScriptInterpreterObjectSP(); + } + + if (PyCallable_Check(pmeth) == 0) + { + if (PyErr_Occurred()) + { + PyErr_Clear(); + } + + Py_XDECREF(pmeth); + return lldb::ScriptInterpreterObjectSP(); + } + + if (PyErr_Occurred()) + { + PyErr_Clear(); + } + + Py_XDECREF(pmeth); + + // right now we know this function exists and is callable.. + PyObject* py_return = PyObject_CallMethod(implementor, callee_name, param_format, tid); + + // if it fails, print the error but otherwise go on + if (PyErr_Occurred()) + { + PyErr_Print(); + PyErr_Clear(); + } + + return MakeScriptObject(py_return); +} + +lldb::ScriptInterpreterObjectSP +ScriptInterpreterPython::OSPlugin_CreateThread (lldb::ScriptInterpreterObjectSP os_plugin_object_sp, + lldb::tid_t tid, + lldb::addr_t context) +{ + Locker py_lock(this,Locker::AcquireLock,Locker::FreeLock); + + static char callee_name[] = "create_thread"; + std::string param_format; + param_format += GetPythonValueFormatString(tid); + param_format += GetPythonValueFormatString(context); + + if (!os_plugin_object_sp) + return lldb::ScriptInterpreterObjectSP(); + + PyObject* implementor = (PyObject*)os_plugin_object_sp->GetObject(); + + if (implementor == NULL || implementor == Py_None) + return lldb::ScriptInterpreterObjectSP(); + + PyObject* pmeth = PyObject_GetAttrString(implementor, callee_name); + + if (PyErr_Occurred()) + { + PyErr_Clear(); + } + + if (pmeth == NULL || pmeth == Py_None) + { + Py_XDECREF(pmeth); + return lldb::ScriptInterpreterObjectSP(); + } + + if (PyCallable_Check(pmeth) == 0) + { + if (PyErr_Occurred()) + { + PyErr_Clear(); + } + + Py_XDECREF(pmeth); + return lldb::ScriptInterpreterObjectSP(); + } + + if (PyErr_Occurred()) + { + PyErr_Clear(); + } + + Py_XDECREF(pmeth); + + // right now we know this function exists and is callable.. + PyObject* py_return = PyObject_CallMethod(implementor, callee_name, ¶m_format[0], tid, context); + + // if it fails, print the error but otherwise go on + if (PyErr_Occurred()) + { + PyErr_Print(); + PyErr_Clear(); + } + + return MakeScriptObject(py_return); +} + +lldb::ScriptInterpreterObjectSP +ScriptInterpreterPython::CreateSyntheticScriptedProvider (const char *class_name, + lldb::ValueObjectSP valobj) +{ + if (class_name == NULL || class_name[0] == '\0') + return lldb::ScriptInterpreterObjectSP(); + + if (!valobj.get()) + return lldb::ScriptInterpreterObjectSP(); + + ExecutionContext exe_ctx (valobj->GetExecutionContextRef()); + Target *target = exe_ctx.GetTargetPtr(); + + if (!target) + return lldb::ScriptInterpreterObjectSP(); + + Debugger &debugger = target->GetDebugger(); + ScriptInterpreter *script_interpreter = debugger.GetCommandInterpreter().GetScriptInterpreter(); + ScriptInterpreterPython *python_interpreter = (ScriptInterpreterPython *) script_interpreter; + + if (!script_interpreter) + return lldb::ScriptInterpreterObjectSP(); + + void* ret_val; + + { + Locker py_lock(this); + ret_val = g_swig_synthetic_script (class_name, + python_interpreter->m_dictionary_name.c_str(), + valobj); + } + + return MakeScriptObject(ret_val); +} + +bool +ScriptInterpreterPython::GenerateTypeScriptFunction (const char* oneliner, std::string& output, void* name_token) +{ + StringList input; + input.SplitIntoLines(oneliner, strlen(oneliner)); + return GenerateTypeScriptFunction(input, output, name_token); +} + +bool +ScriptInterpreterPython::GenerateTypeSynthClass (const char* oneliner, std::string& output, void* name_token) +{ + StringList input; + input.SplitIntoLines(oneliner, strlen(oneliner)); + return GenerateTypeSynthClass(input, output, name_token); +} + + +bool +ScriptInterpreterPython::GenerateBreakpointCommandCallbackData (StringList &user_input, std::string& output) +{ + static uint32_t num_created_functions = 0; + user_input.RemoveBlankLines (); + StreamString sstr; + + if (user_input.GetSize() == 0) + return false; + + std::string auto_generated_function_name(GenerateUniqueName("lldb_autogen_python_bp_callback_func_",num_created_functions)); + sstr.Printf ("def %s (frame, bp_loc, internal_dict):", auto_generated_function_name.c_str()); + + if (!GenerateFunction(sstr.GetData(), user_input)) + return false; + + // Store the name of the auto-generated function to be called. + output.assign(auto_generated_function_name); + return true; +} + +bool +ScriptInterpreterPython::GenerateWatchpointCommandCallbackData (StringList &user_input, std::string& output) +{ + static uint32_t num_created_functions = 0; + user_input.RemoveBlankLines (); + StreamString sstr; + + if (user_input.GetSize() == 0) + return false; + + std::string auto_generated_function_name(GenerateUniqueName("lldb_autogen_python_wp_callback_func_",num_created_functions)); + sstr.Printf ("def %s (frame, wp, internal_dict):", auto_generated_function_name.c_str()); + + if (!GenerateFunction(sstr.GetData(), user_input)) + return false; + + // Store the name of the auto-generated function to be called. + output.assign(auto_generated_function_name); + return true; +} + +bool +ScriptInterpreterPython::GetScriptedSummary (const char *python_function_name, + lldb::ValueObjectSP valobj, + lldb::ScriptInterpreterObjectSP& callee_wrapper_sp, + std::string& retval) +{ + + Timer scoped_timer (__PRETTY_FUNCTION__, __PRETTY_FUNCTION__); + + if (!valobj.get()) + { + retval.assign(""); + return false; + } + + void* old_callee = (callee_wrapper_sp ? callee_wrapper_sp->GetObject() : NULL); + void* new_callee = old_callee; + + bool ret_val; + if (python_function_name + && *python_function_name) + { + { + Locker py_lock(this); + { + Timer scoped_timer ("g_swig_typescript_callback","g_swig_typescript_callback"); + ret_val = g_swig_typescript_callback (python_function_name, + FindSessionDictionary(m_dictionary_name.c_str()), + valobj, + &new_callee, + retval); + } + } + } + else + { + retval.assign(""); + return false; + } + + if (new_callee && old_callee != new_callee) + callee_wrapper_sp = MakeScriptObject(new_callee); + + return ret_val; + +} + +bool +ScriptInterpreterPython::BreakpointCallbackFunction +( + void *baton, + StoppointCallbackContext *context, + user_id_t break_id, + user_id_t break_loc_id +) +{ + BreakpointOptions::CommandData *bp_option_data = (BreakpointOptions::CommandData *) baton; + const char *python_function_name = bp_option_data->script_source.c_str(); + + if (!context) + return true; + + ExecutionContext exe_ctx (context->exe_ctx_ref); + Target *target = exe_ctx.GetTargetPtr(); + + if (!target) + return true; + + Debugger &debugger = target->GetDebugger(); + ScriptInterpreter *script_interpreter = debugger.GetCommandInterpreter().GetScriptInterpreter(); + ScriptInterpreterPython *python_interpreter = (ScriptInterpreterPython *) script_interpreter; + + if (!script_interpreter) + return true; + + if (python_function_name != NULL + && python_function_name[0] != '\0') + { + const StackFrameSP stop_frame_sp (exe_ctx.GetFrameSP()); + BreakpointSP breakpoint_sp = target->GetBreakpointByID (break_id); + if (breakpoint_sp) + { + const BreakpointLocationSP bp_loc_sp (breakpoint_sp->FindLocationByID (break_loc_id)); + + if (stop_frame_sp && bp_loc_sp) + { + bool ret_val = true; + { + Locker py_lock(python_interpreter); + ret_val = g_swig_breakpoint_callback (python_function_name, + python_interpreter->m_dictionary_name.c_str(), + stop_frame_sp, + bp_loc_sp); + } + return ret_val; + } + } + } + // We currently always true so we stop in case anything goes wrong when + // trying to call the script function + return true; +} + +bool +ScriptInterpreterPython::WatchpointCallbackFunction +( + void *baton, + StoppointCallbackContext *context, + user_id_t watch_id +) +{ + WatchpointOptions::CommandData *wp_option_data = (WatchpointOptions::CommandData *) baton; + const char *python_function_name = wp_option_data->script_source.c_str(); + + if (!context) + return true; + + ExecutionContext exe_ctx (context->exe_ctx_ref); + Target *target = exe_ctx.GetTargetPtr(); + + if (!target) + return true; + + Debugger &debugger = target->GetDebugger(); + ScriptInterpreter *script_interpreter = debugger.GetCommandInterpreter().GetScriptInterpreter(); + ScriptInterpreterPython *python_interpreter = (ScriptInterpreterPython *) script_interpreter; + + if (!script_interpreter) + return true; + + if (python_function_name != NULL + && python_function_name[0] != '\0') + { + const StackFrameSP stop_frame_sp (exe_ctx.GetFrameSP()); + WatchpointSP wp_sp = target->GetWatchpointList().FindByID (watch_id); + if (wp_sp) + { + if (stop_frame_sp && wp_sp) + { + bool ret_val = true; + { + Locker py_lock(python_interpreter); + ret_val = g_swig_watchpoint_callback (python_function_name, + python_interpreter->m_dictionary_name.c_str(), + stop_frame_sp, + wp_sp); + } + return ret_val; + } + } + } + // We currently always true so we stop in case anything goes wrong when + // trying to call the script function + return true; +} + +lldb::thread_result_t +ScriptInterpreterPython::RunEmbeddedPythonInterpreter (lldb::thread_arg_t baton) +{ + ScriptInterpreterPython *script_interpreter = (ScriptInterpreterPython *) baton; + + Log *log (lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_SCRIPT)); + + if (log) + log->Printf ("%p ScriptInterpreterPython::RunEmbeddedPythonInterpreter () thread starting...", baton); + + char error_str[1024]; + const char *pty_slave_name = script_interpreter->m_embedded_python_pty.GetSlaveName (error_str, sizeof (error_str)); + + if (pty_slave_name != NULL) + { + StreamString run_string; + + // Ensure we have the GIL before running any Python code. + // Since we're only running a few one-liners and then dropping to the interpreter (which will release the GIL when needed), + // we can just release the GIL after finishing our work. + // If finer-grained locking is desirable, we can lock and unlock the GIL only when calling a python function. + Locker locker(script_interpreter, + ScriptInterpreterPython::Locker::AcquireLock | ScriptInterpreterPython::Locker::InitSession | ScriptInterpreterPython::Locker::InitGlobals, + ScriptInterpreterPython::Locker::FreeAcquiredLock | ScriptInterpreterPython::Locker::TearDownSession); + + run_string.Printf ("run_one_line (%s, 'save_stderr = sys.stderr')", script_interpreter->m_dictionary_name.c_str()); + PyRun_SimpleString (run_string.GetData()); + run_string.Clear (); + + run_string.Printf ("run_one_line (%s, 'sys.stderr = sys.stdout')", script_interpreter->m_dictionary_name.c_str()); + PyRun_SimpleString (run_string.GetData()); + run_string.Clear (); + + run_string.Printf ("run_one_line (%s, 'save_stdin = sys.stdin')", script_interpreter->m_dictionary_name.c_str()); + PyRun_SimpleString (run_string.GetData()); + run_string.Clear (); + + run_string.Printf ("run_one_line (%s, \"sys.stdin = open ('%s', 'r')\")", script_interpreter->m_dictionary_name.c_str(), + pty_slave_name); + PyRun_SimpleString (run_string.GetData()); + run_string.Clear (); + + // The following call drops into the embedded interpreter loop and stays there until the + // user chooses to exit from the Python interpreter. + // This embedded interpreter will, as any Python code that performs I/O, unlock the GIL before + // a system call that can hang, and lock it when the syscall has returned. + + // We need to surround the call to the embedded interpreter with calls to PyGILState_Ensure and + // PyGILState_Release (using the Locker above). This is because Python has a global lock which must be held whenever we want + // to touch any Python objects. Otherwise, if the user calls Python code, the interpreter state will be off, + // and things could hang (it's happened before). + + run_string.Printf ("run_python_interpreter (%s)", script_interpreter->m_dictionary_name.c_str()); + PyRun_SimpleString (run_string.GetData()); + run_string.Clear (); + + run_string.Printf ("run_one_line (%s, 'sys.stdin = save_stdin')", script_interpreter->m_dictionary_name.c_str()); + PyRun_SimpleString (run_string.GetData()); + run_string.Clear(); + + run_string.Printf ("run_one_line (%s, 'sys.stderr = save_stderr')", script_interpreter->m_dictionary_name.c_str()); + PyRun_SimpleString (run_string.GetData()); + run_string.Clear(); + } + + if (script_interpreter->m_embedded_python_input_reader_sp) + script_interpreter->m_embedded_python_input_reader_sp->SetIsDone (true); + + script_interpreter->m_embedded_python_pty.CloseSlaveFileDescriptor(); + + log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_SCRIPT); + if (log) + log->Printf ("%p ScriptInterpreterPython::RunEmbeddedPythonInterpreter () thread exiting...", baton); + + + // Clean up the input reader and make the debugger pop it off the stack. + Debugger &debugger = script_interpreter->GetCommandInterpreter().GetDebugger(); + const InputReaderSP reader_sp = script_interpreter->m_embedded_python_input_reader_sp; + if (reader_sp) + { + debugger.PopInputReader (reader_sp); + script_interpreter->m_embedded_python_input_reader_sp.reset(); + } + + return NULL; +} + +lldb::thread_result_t +ScriptInterpreterPython::PythonInputReaderManager::RunPythonInputReader (lldb::thread_arg_t baton) +{ + ScriptInterpreterPython *script_interpreter = (ScriptInterpreterPython *) baton; + + const InputReaderSP reader_sp = script_interpreter->m_embedded_thread_input_reader_sp; + + if (reader_sp) + reader_sp->WaitOnReaderIsDone(); + + return NULL; +} + +size_t +ScriptInterpreterPython::CalculateNumChildren (const lldb::ScriptInterpreterObjectSP& implementor_sp) +{ + if (!implementor_sp) + return 0; + + void* implementor = implementor_sp->GetObject(); + + if (!implementor) + return 0; + + if (!g_swig_calc_children) + return 0; + + uint32_t ret_val = 0; + + { + Locker py_lock(this); + ret_val = g_swig_calc_children (implementor); + } + + return ret_val; +} + +lldb::ValueObjectSP +ScriptInterpreterPython::GetChildAtIndex (const lldb::ScriptInterpreterObjectSP& implementor_sp, uint32_t idx) +{ + if (!implementor_sp) + return lldb::ValueObjectSP(); + + void* implementor = implementor_sp->GetObject(); + + if (!implementor) + return lldb::ValueObjectSP(); + + if (!g_swig_get_child_index || !g_swig_cast_to_sbvalue) + return lldb::ValueObjectSP(); + + void* child_ptr = NULL; + lldb::SBValue* value_sb = NULL; + lldb::ValueObjectSP ret_val; + + { + Locker py_lock(this); + child_ptr = g_swig_get_child_index (implementor,idx); + if (child_ptr != NULL && child_ptr != Py_None) + { + value_sb = (lldb::SBValue*)g_swig_cast_to_sbvalue(child_ptr); + if (value_sb == NULL) + Py_XDECREF(child_ptr); + else + ret_val = value_sb->GetSP(); + } + else + { + Py_XDECREF(child_ptr); + } + } + + return ret_val; +} + +int +ScriptInterpreterPython::GetIndexOfChildWithName (const lldb::ScriptInterpreterObjectSP& implementor_sp, const char* child_name) +{ + if (!implementor_sp) + return UINT32_MAX; + + void* implementor = implementor_sp->GetObject(); + + if (!implementor) + return UINT32_MAX; + + if (!g_swig_get_index_child) + return UINT32_MAX; + + int ret_val = UINT32_MAX; + + { + Locker py_lock(this); + ret_val = g_swig_get_index_child (implementor, child_name); + } + + return ret_val; +} + +bool +ScriptInterpreterPython::UpdateSynthProviderInstance (const lldb::ScriptInterpreterObjectSP& implementor_sp) +{ + bool ret_val = false; + + if (!implementor_sp) + return ret_val; + + void* implementor = implementor_sp->GetObject(); + + if (!implementor) + return ret_val; + + if (!g_swig_update_provider) + return ret_val; + + { + Locker py_lock(this); + ret_val = g_swig_update_provider (implementor); + } + + return ret_val; +} + +bool +ScriptInterpreterPython::MightHaveChildrenSynthProviderInstance (const lldb::ScriptInterpreterObjectSP& implementor_sp) +{ + bool ret_val = false; + + if (!implementor_sp) + return ret_val; + + void* implementor = implementor_sp->GetObject(); + + if (!implementor) + return ret_val; + + if (!g_swig_mighthavechildren_provider) + return ret_val; + + { + Locker py_lock(this); + ret_val = g_swig_mighthavechildren_provider (implementor); + } + + return ret_val; +} + +static std::string +ReadPythonBacktrace (PyObject* py_backtrace) +{ + PyObject* traceback_module = NULL, + *stringIO_module = NULL, + *stringIO_builder = NULL, + *stringIO_buffer = NULL, + *printTB = NULL, + *printTB_args = NULL, + *printTB_result = NULL, + *stringIO_getvalue = NULL, + *printTB_string = NULL; + + std::string retval("backtrace unavailable"); + + if (py_backtrace && py_backtrace != Py_None) + { + traceback_module = PyImport_ImportModule("traceback"); + stringIO_module = PyImport_ImportModule("StringIO"); + + if (traceback_module && traceback_module != Py_None && stringIO_module && stringIO_module != Py_None) + { + stringIO_builder = PyObject_GetAttrString(stringIO_module, "StringIO"); + if (stringIO_builder && stringIO_builder != Py_None) + { + stringIO_buffer = PyObject_CallObject(stringIO_builder, NULL); + if (stringIO_buffer && stringIO_buffer != Py_None) + { + printTB = PyObject_GetAttrString(traceback_module, "print_tb"); + if (printTB && printTB != Py_None) + { + printTB_args = Py_BuildValue("OOO",py_backtrace,Py_None,stringIO_buffer); + printTB_result = PyObject_CallObject(printTB, printTB_args); + stringIO_getvalue = PyObject_GetAttrString(stringIO_buffer, "getvalue"); + if (stringIO_getvalue && stringIO_getvalue != Py_None) + { + printTB_string = PyObject_CallObject (stringIO_getvalue,NULL); + if (printTB_string && printTB_string != Py_None && PyString_Check(printTB_string)) + retval.assign(PyString_AsString(printTB_string)); + } + } + } + } + } + } + Py_XDECREF(traceback_module); + Py_XDECREF(stringIO_module); + Py_XDECREF(stringIO_builder); + Py_XDECREF(stringIO_buffer); + Py_XDECREF(printTB); + Py_XDECREF(printTB_args); + Py_XDECREF(printTB_result); + Py_XDECREF(stringIO_getvalue); + Py_XDECREF(printTB_string); + return retval; +} + +bool +ScriptInterpreterPython::RunScriptFormatKeyword (const char* impl_function, + Process* process, + std::string& output, + Error& error) +{ + bool ret_val; + if (!process) + { + error.SetErrorString("no process"); + return false; + } + if (!impl_function || !impl_function[0]) + { + error.SetErrorString("no function to execute"); + return false; + } + if (!g_swig_run_script_keyword_process) + { + error.SetErrorString("internal helper function missing"); + return false; + } + { + ProcessSP process_sp(process->shared_from_this()); + Locker py_lock(this); + ret_val = g_swig_run_script_keyword_process (impl_function, m_dictionary_name.c_str(), process_sp, output); + if (!ret_val) + error.SetErrorString("python script evaluation failed"); + } + return ret_val; +} + +bool +ScriptInterpreterPython::RunScriptFormatKeyword (const char* impl_function, + Thread* thread, + std::string& output, + Error& error) +{ + bool ret_val; + if (!thread) + { + error.SetErrorString("no thread"); + return false; + } + if (!impl_function || !impl_function[0]) + { + error.SetErrorString("no function to execute"); + return false; + } + if (!g_swig_run_script_keyword_thread) + { + error.SetErrorString("internal helper function missing"); + return false; + } + { + ThreadSP thread_sp(thread->shared_from_this()); + Locker py_lock(this); + ret_val = g_swig_run_script_keyword_thread (impl_function, m_dictionary_name.c_str(), thread_sp, output); + if (!ret_val) + error.SetErrorString("python script evaluation failed"); + } + return ret_val; +} + +bool +ScriptInterpreterPython::RunScriptFormatKeyword (const char* impl_function, + Target* target, + std::string& output, + Error& error) +{ + bool ret_val; + if (!target) + { + error.SetErrorString("no thread"); + return false; + } + if (!impl_function || !impl_function[0]) + { + error.SetErrorString("no function to execute"); + return false; + } + if (!g_swig_run_script_keyword_target) + { + error.SetErrorString("internal helper function missing"); + return false; + } + { + TargetSP target_sp(target->shared_from_this()); + Locker py_lock(this); + ret_val = g_swig_run_script_keyword_target (impl_function, m_dictionary_name.c_str(), target_sp, output); + if (!ret_val) + error.SetErrorString("python script evaluation failed"); + } + return ret_val; +} + +bool +ScriptInterpreterPython::RunScriptFormatKeyword (const char* impl_function, + StackFrame* frame, + std::string& output, + Error& error) +{ + bool ret_val; + if (!frame) + { + error.SetErrorString("no frame"); + return false; + } + if (!impl_function || !impl_function[0]) + { + error.SetErrorString("no function to execute"); + return false; + } + if (!g_swig_run_script_keyword_frame) + { + error.SetErrorString("internal helper function missing"); + return false; + } + { + StackFrameSP frame_sp(frame->shared_from_this()); + Locker py_lock(this); + ret_val = g_swig_run_script_keyword_frame (impl_function, m_dictionary_name.c_str(), frame_sp, output); + if (!ret_val) + error.SetErrorString("python script evaluation failed"); + } + return ret_val; +} + +uint64_t replace_all(std::string& str, const std::string& oldStr, const std::string& newStr) +{ + size_t pos = 0; + uint64_t matches = 0; + while((pos = str.find(oldStr, pos)) != std::string::npos) + { + matches++; + str.replace(pos, oldStr.length(), newStr); + pos += newStr.length(); + } + return matches; +} + +bool +ScriptInterpreterPython::LoadScriptingModule (const char* pathname, + bool can_reload, + bool init_session, + lldb_private::Error& error) +{ + if (!pathname || !pathname[0]) + { + error.SetErrorString("invalid pathname"); + return false; + } + + if (!g_swig_call_module_init) + { + error.SetErrorString("internal helper function missing"); + return false; + } + + lldb::DebuggerSP debugger_sp = m_interpreter.GetDebugger().shared_from_this(); + + { + FileSpec target_file(pathname, true); + std::string basename(target_file.GetFilename().GetCString()); + + StreamString command_stream; + + // Before executing Pyton code, lock the GIL. + Locker py_lock (this, + Locker::AcquireLock | (init_session ? Locker::InitSession : 0), + Locker::FreeAcquiredLock | (init_session ? Locker::TearDownSession : 0)); + + if (target_file.GetFileType() == FileSpec::eFileTypeInvalid || + target_file.GetFileType() == FileSpec::eFileTypeUnknown) + { + // if not a valid file of any sort, check if it might be a filename still + // dot can't be used but / and \ can, and if either is found, reject + if (strchr(pathname,'\\') || strchr(pathname,'/')) + { + error.SetErrorString("invalid pathname"); + return false; + } + basename = pathname; // not a filename, probably a package of some sort, let it go through + } + else if (target_file.GetFileType() == FileSpec::eFileTypeDirectory || + target_file.GetFileType() == FileSpec::eFileTypeRegular || + target_file.GetFileType() == FileSpec::eFileTypeSymbolicLink) + { + std::string directory(target_file.GetDirectory().GetCString()); + replace_all(directory,"'","\\'"); + + // now make sure that Python has "directory" in the search path + StreamString command_stream; + command_stream.Printf("if not (sys.path.__contains__('%s')):\n sys.path.insert(1,'%s');\n\n", + directory.c_str(), + directory.c_str()); + bool syspath_retval = ExecuteMultipleLines(command_stream.GetData(), ScriptInterpreter::ExecuteScriptOptions().SetEnableIO(false).SetSetLLDBGlobals(false)); + if (!syspath_retval) + { + error.SetErrorString("Python sys.path handling failed"); + return false; + } + + // strip .py or .pyc extension + ConstString extension = target_file.GetFileNameExtension(); + if (extension) + { + if (::strcmp(extension.GetCString(), "py") == 0) + basename.resize(basename.length()-3); + else if(::strcmp(extension.GetCString(), "pyc") == 0) + basename.resize(basename.length()-4); + } + } + else + { + error.SetErrorString("no known way to import this module specification"); + return false; + } + + // check if the module is already import-ed + command_stream.Clear(); + command_stream.Printf("sys.modules.__contains__('%s')",basename.c_str()); + bool does_contain = false; + int refcount = 0; + // this call will succeed if the module was ever imported in any Debugger in the lifetime of the process + // in which this LLDB framework is living + bool was_imported_globally = (ExecuteOneLineWithReturn(command_stream.GetData(), + ScriptInterpreterPython::eScriptReturnTypeBool, + &does_contain, + ScriptInterpreter::ExecuteScriptOptions().SetEnableIO(false).SetSetLLDBGlobals(false)) && does_contain); + // this call will fail if the module was not imported in this Debugger before + command_stream.Clear(); + command_stream.Printf("sys.getrefcount(%s)",basename.c_str()); + bool was_imported_locally = (ExecuteOneLineWithReturn(command_stream.GetData(), + ScriptInterpreterPython::eScriptReturnTypeInt, + &refcount, + ScriptInterpreter::ExecuteScriptOptions().SetEnableIO(false).SetSetLLDBGlobals(false)) && refcount > 0); + + bool was_imported = (was_imported_globally || was_imported_locally); + + if (was_imported == true && can_reload == false) + { + error.SetErrorString("module already imported"); + return false; + } + + // now actually do the import + command_stream.Clear(); + + if (was_imported) + { + if (!was_imported_locally) + command_stream.Printf("import %s ; reload(%s)",basename.c_str(),basename.c_str()); + else + command_stream.Printf("reload(%s)",basename.c_str()); + } + else + command_stream.Printf("import %s",basename.c_str()); + + bool import_retval = ExecuteMultipleLines(command_stream.GetData(), ScriptInterpreter::ExecuteScriptOptions().SetEnableIO(false).SetSetLLDBGlobals(false).SetMaskoutErrors(false)); + PyObject* py_error = PyErr_Occurred(); // per Python docs: "you do not need to Py_DECREF()" the return of this function + + if (py_error || !import_retval) // check for failure of the import + { + if (py_error) // if we have a Python error.. + { + PyObject *type = NULL,*value = NULL,*traceback = NULL; + PyErr_Fetch (&type,&value,&traceback); + + if (PyErr_GivenExceptionMatches (py_error, PyExc_ImportError)) // and it is an ImportError + { + if (value && value != Py_None) + error.SetErrorString(PyString_AsString(PyObject_Str(value))); + else + error.SetErrorString("ImportError raised by imported module"); + } + else // any other error + { + // get the backtrace + std::string bt = ReadPythonBacktrace(traceback); + + if (value && value != Py_None) + error.SetErrorStringWithFormat("Python error raised while importing module: %s - traceback: %s", PyString_AsString(PyObject_Str(value)),bt.c_str()); + else + error.SetErrorStringWithFormat("Python raised an error while importing module - traceback: %s",bt.c_str()); + } + + Py_XDECREF(type); + Py_XDECREF(value); + Py_XDECREF(traceback); + } + else // we failed but have no error to explain why + { + error.SetErrorString("unknown error while importing module"); + } + + // anyway, clear the error indicator and return false + PyErr_Clear(); + return false; + } + + // if we are here, everything worked + // call __lldb_init_module(debugger,dict) + if (!g_swig_call_module_init (basename.c_str(), + m_dictionary_name.c_str(), + debugger_sp)) + { + error.SetErrorString("calling __lldb_init_module failed"); + return false; + } + return true; + } +} + +lldb::ScriptInterpreterObjectSP +ScriptInterpreterPython::MakeScriptObject (void* object) +{ + return lldb::ScriptInterpreterObjectSP(new ScriptInterpreterPythonObject(object)); +} + +ScriptInterpreterPython::SynchronicityHandler::SynchronicityHandler (lldb::DebuggerSP debugger_sp, + ScriptedCommandSynchronicity synchro) : + m_debugger_sp(debugger_sp), + m_synch_wanted(synchro), + m_old_asynch(debugger_sp->GetAsyncExecution()) +{ + if (m_synch_wanted == eScriptedCommandSynchronicitySynchronous) + m_debugger_sp->SetAsyncExecution(false); + else if (m_synch_wanted == eScriptedCommandSynchronicityAsynchronous) + m_debugger_sp->SetAsyncExecution(true); +} + +ScriptInterpreterPython::SynchronicityHandler::~SynchronicityHandler() +{ + if (m_synch_wanted != eScriptedCommandSynchronicityCurrentValue) + m_debugger_sp->SetAsyncExecution(m_old_asynch); +} + +bool +ScriptInterpreterPython::RunScriptBasedCommand(const char* impl_function, + const char* args, + ScriptedCommandSynchronicity synchronicity, + lldb_private::CommandReturnObject& cmd_retobj, + Error& error) +{ + if (!impl_function) + { + error.SetErrorString("no function to execute"); + return false; + } + + if (!g_swig_call_command) + { + error.SetErrorString("no helper function to run scripted commands"); + return false; + } + + lldb::DebuggerSP debugger_sp = m_interpreter.GetDebugger().shared_from_this(); + + if (!debugger_sp.get()) + { + error.SetErrorString("invalid Debugger pointer"); + return false; + } + + bool ret_val = false; + + std::string err_msg; + + { + Locker py_lock(this, + Locker::AcquireLock | Locker::InitSession, + Locker::FreeLock | Locker::TearDownSession); + + SynchronicityHandler synch_handler(debugger_sp, + synchronicity); + + // we need to save the thread state when we first start the command + // because we might decide to interrupt it while some action is taking + // place outside of Python (e.g. printing to screen, waiting for the network, ...) + // in that case, _PyThreadState_Current will be NULL - and we would be unable + // to set the asynchronous exception - not a desirable situation + m_command_thread_state = _PyThreadState_Current; + + PythonInputReaderManager py_input(this); + + ret_val = g_swig_call_command (impl_function, + m_dictionary_name.c_str(), + debugger_sp, + args, + cmd_retobj); + } + + if (!ret_val) + error.SetErrorString("unable to execute script function"); + else + error.Clear(); + + return ret_val; +} + +// in Python, a special attribute __doc__ contains the docstring +// for an object (function, method, class, ...) if any is defined +// Otherwise, the attribute's value is None +bool +ScriptInterpreterPython::GetDocumentationForItem(const char* item, std::string& dest) +{ + dest.clear(); + if (!item || !*item) + return false; + std::string command(item); + command += ".__doc__"; + + char* result_ptr = NULL; // Python is going to point this to valid data if ExecuteOneLineWithReturn returns successfully + + if (ExecuteOneLineWithReturn (command.c_str(), + ScriptInterpreter::eScriptReturnTypeCharStrOrNone, + &result_ptr, + ScriptInterpreter::ExecuteScriptOptions().SetEnableIO(false))) + { + if (result_ptr) + dest.assign(result_ptr); + return true; + } + else + { + StreamString str_stream; + str_stream.Printf("Function %s was not found. Containing module might be missing.",item); + dest.assign(str_stream.GetData()); + return false; + } +} + +std::unique_ptr +ScriptInterpreterPython::AcquireInterpreterLock () +{ + std::unique_ptr py_lock(new Locker(this, + Locker::AcquireLock | Locker::InitSession, + Locker::FreeLock | Locker::TearDownSession)); + return py_lock; +} + +void +ScriptInterpreterPython::InitializeInterpreter (SWIGInitCallback python_swig_init_callback) +{ + g_swig_init_callback = python_swig_init_callback; + g_swig_breakpoint_callback = LLDBSwigPythonBreakpointCallbackFunction; + g_swig_watchpoint_callback = LLDBSwigPythonWatchpointCallbackFunction; + g_swig_typescript_callback = LLDBSwigPythonCallTypeScript; + g_swig_synthetic_script = LLDBSwigPythonCreateSyntheticProvider; + g_swig_calc_children = LLDBSwigPython_CalculateNumChildren; + g_swig_get_child_index = LLDBSwigPython_GetChildAtIndex; + g_swig_get_index_child = LLDBSwigPython_GetIndexOfChildWithName; + g_swig_cast_to_sbvalue = LLDBSWIGPython_CastPyObjectToSBValue; + g_swig_update_provider = LLDBSwigPython_UpdateSynthProviderInstance; + g_swig_mighthavechildren_provider = LLDBSwigPython_MightHaveChildrenSynthProviderInstance; + g_swig_call_command = LLDBSwigPythonCallCommand; + g_swig_call_module_init = LLDBSwigPythonCallModuleInit; + g_swig_create_os_plugin = LLDBSWIGPythonCreateOSPlugin; + g_swig_run_script_keyword_process = LLDBSWIGPythonRunScriptKeywordProcess; + g_swig_run_script_keyword_thread = LLDBSWIGPythonRunScriptKeywordThread; + g_swig_run_script_keyword_target = LLDBSWIGPythonRunScriptKeywordTarget; + g_swig_run_script_keyword_frame = LLDBSWIGPythonRunScriptKeywordFrame; +} + +void +ScriptInterpreterPython::InitializePrivate () +{ + Timer scoped_timer (__PRETTY_FUNCTION__, __PRETTY_FUNCTION__); + + // Python will muck with STDIN terminal state, so save off any current TTY + // settings so we can restore them. + TerminalState stdin_tty_state; + stdin_tty_state.Save(STDIN_FILENO, false); + + PyGILState_STATE gstate; + Log *log (lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_SCRIPT | LIBLLDB_LOG_VERBOSE)); + bool threads_already_initialized = false; + if (PyEval_ThreadsInitialized ()) { + gstate = PyGILState_Ensure (); + if (log) + log->Printf("Ensured PyGILState. Previous state = %slocked\n", gstate == PyGILState_UNLOCKED ? "un" : ""); + threads_already_initialized = true; + } else { + // InitThreads acquires the GIL if it hasn't been called before. + PyEval_InitThreads (); + } + Py_InitializeEx (0); + + // Initialize SWIG after setting up python + assert (g_swig_init_callback != NULL); + g_swig_init_callback (); + + // Update the path python uses to search for modules to include the current directory. + + PyRun_SimpleString ("import sys"); + PyRun_SimpleString ("sys.path.append ('.')"); + + // Find the module that owns this code and use that path we get to + // set the sys.path appropriately. + + FileSpec file_spec; + char python_dir_path[PATH_MAX]; + if (Host::GetLLDBPath (ePathTypePythonDir, file_spec)) + { + std::string python_path("sys.path.insert(0,\""); + size_t orig_len = python_path.length(); + if (file_spec.GetPath(python_dir_path, sizeof (python_dir_path))) + { + python_path.append (python_dir_path); + python_path.append ("\")"); + PyRun_SimpleString (python_path.c_str()); + python_path.resize (orig_len); + } + + if (Host::GetLLDBPath (ePathTypeLLDBShlibDir, file_spec)) + { + if (file_spec.GetPath(python_dir_path, sizeof (python_dir_path))) + { + python_path.append (python_dir_path); + python_path.append ("\")"); + PyRun_SimpleString (python_path.c_str()); + python_path.resize (orig_len); + } + } + } + + PyRun_SimpleString ("sys.dont_write_bytecode = 1; import lldb.embedded_interpreter; from lldb.embedded_interpreter import run_python_interpreter; from lldb.embedded_interpreter import run_one_line; from termios import *"); + + if (threads_already_initialized) { + if (log) + log->Printf("Releasing PyGILState. Returning to state = %slocked\n", gstate == PyGILState_UNLOCKED ? "un" : ""); + PyGILState_Release (gstate); + } else { + // We initialized the threads in this function, just unlock the GIL. + PyEval_SaveThread(); + } + + stdin_tty_state.Restore(); +} + +//void +//ScriptInterpreterPython::Terminate () +//{ +// // We are intentionally NOT calling Py_Finalize here (this would be the logical place to call it). Calling +// // Py_Finalize here causes test suite runs to seg fault: The test suite runs in Python. It registers +// // SBDebugger::Terminate to be called 'at_exit'. When the test suite Python harness finishes up, it calls +// // Py_Finalize, which calls all the 'at_exit' registered functions. SBDebugger::Terminate calls Debugger::Terminate, +// // which calls lldb::Terminate, which calls ScriptInterpreter::Terminate, which calls +// // ScriptInterpreterPython::Terminate. So if we call Py_Finalize here, we end up with Py_Finalize being called from +// // within Py_Finalize, which results in a seg fault. +// // +// // Since this function only gets called when lldb is shutting down and going away anyway, the fact that we don't +// // actually call Py_Finalize should not cause any problems (everything should shut down/go away anyway when the +// // process exits). +// // +//// Py_Finalize (); +//} + +#endif // #ifdef LLDB_DISABLE_PYTHON diff --git a/contrib/llvm/tools/lldb/source/Interpreter/embedded_interpreter.py b/contrib/llvm/tools/lldb/source/Interpreter/embedded_interpreter.py new file mode 100644 index 00000000000..0e57c1e4aec --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Interpreter/embedded_interpreter.py @@ -0,0 +1,103 @@ +import readline +import code +import sys +import traceback + +class SimpleREPL(code.InteractiveConsole): + def __init__(self, prompt, dict): + code.InteractiveConsole.__init__(self,dict) + self.prompt = prompt + self.loop_exit = False + self.dict = dict + + def interact(self): + try: + sys.ps1 + except AttributeError: + sys.ps1 = ">>> " + try: + sys.ps2 + except AttributeError: + sys.ps2 = "... " + + while not self.loop_exit: + try: + self.read_py_command() + except (SystemExit, EOFError): + # EOF while in Python just breaks out to top level. + self.write('\n') + self.loop_exit = True + break + except KeyboardInterrupt: + self.write("\nKeyboardInterrupt\n") + self.resetbuffer() + more = 0 + except: + traceback.print_exc() + + def process_input (self, in_str): + # Canonicalize the format of the input string + temp_str = in_str + temp_str.strip(' \t') + words = temp_str.split() + temp_str = ('').join(words) + + # Check the input string to see if it was the quit + # command. If so, intercept it, so that it doesn't + # close stdin on us! + if (temp_str.lower() == "quit()" or temp_str.lower() == "exit()"): + self.loop_exit = True + in_str = "raise SystemExit " + return in_str + + def my_raw_input (self, prompt): + stream = sys.stdout + stream.write (prompt) + stream.flush () + try: + line = sys.stdin.readline() + except KeyboardInterrupt: + line = " \n" + except (SystemExit, EOFError): + line = "quit()\n" + if not line: + raise EOFError + if line[-1] == '\n': + line = line[:-1] + return line + + def read_py_command(self): + # Read off a complete Python command. + more = 0 + while 1: + if more: + prompt = sys.ps2 + else: + prompt = sys.ps1 + line = self.my_raw_input(prompt) + # Can be None if sys.stdin was redefined + encoding = getattr(sys.stdin, "encoding", None) + if encoding and not isinstance(line, unicode): + line = line.decode(encoding) + line = self.process_input (line) + more = self.push(line) + if not more: + break + + def one_line (self, input): + line = self.process_input (input) + more = self.push(line) + if more: + self.write ("Input not a complete line.") + self.resetbuffer() + more = 0 + +def run_python_interpreter (dict): + # Pass in the dictionary, for continuity from one session to the next. + repl = SimpleREPL('>>> ', dict) + repl.interact() + +def run_one_line (dict, input_string): + repl = SimpleREPL ('', dict) + repl.one_line (input_string) + diff --git a/contrib/llvm/tools/lldb/source/Plugins/ABI/MacOSX-arm/ABIMacOSX_arm.cpp b/contrib/llvm/tools/lldb/source/Plugins/ABI/MacOSX-arm/ABIMacOSX_arm.cpp new file mode 100644 index 00000000000..4685c3e759e --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Plugins/ABI/MacOSX-arm/ABIMacOSX_arm.cpp @@ -0,0 +1,861 @@ +//===-- ABIMacOSX_arm.cpp --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "ABIMacOSX_arm.h" + +#include "lldb/Core/ConstString.h" +#include "lldb/Core/Error.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/RegisterValue.h" +#include "lldb/Core/Scalar.h" +#include "lldb/Core/Value.h" +#include "lldb/Core/ValueObjectConstResult.h" +#include "lldb/Symbol/ClangASTContext.h" +#include "lldb/Symbol/UnwindPlan.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" + +#include "llvm/ADT/Triple.h" + +#include "Utility/ARM_DWARF_Registers.h" +#include "Utility/ARM_GCC_Registers.h" +#include "Plugins/Process/Utility/ARMDefines.h" + +#include + +using namespace lldb; +using namespace lldb_private; + +static RegisterInfo g_register_infos[] = +{ + // NAME ALT SZ OFF ENCODING FORMAT COMPILER DWARF GENERIC GDB LLDB NATIVE VALUE REGS INVALIDATE REGS + // ========== ======= == === ============= ============ ======================= =================== =========================== ======================= ====================== ========== =============== + { "r0", "arg1", 4, 0, eEncodingUint , eFormatHex, { gcc_r0, dwarf_r0, LLDB_REGNUM_GENERIC_ARG1, gdb_arm_r0, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "r1", "arg2", 4, 0, eEncodingUint , eFormatHex, { gcc_r1, dwarf_r1, LLDB_REGNUM_GENERIC_ARG2, gdb_arm_r1, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "r2", "arg3", 4, 0, eEncodingUint , eFormatHex, { gcc_r2, dwarf_r2, LLDB_REGNUM_GENERIC_ARG3, gdb_arm_r2, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "r3", "arg4", 4, 0, eEncodingUint , eFormatHex, { gcc_r3, dwarf_r3, LLDB_REGNUM_GENERIC_ARG4, gdb_arm_r3, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "r4", NULL, 4, 0, eEncodingUint , eFormatHex, { gcc_r4, dwarf_r4, LLDB_INVALID_REGNUM, gdb_arm_r4, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "r5", NULL, 4, 0, eEncodingUint , eFormatHex, { gcc_r5, dwarf_r5, LLDB_INVALID_REGNUM, gdb_arm_r5, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "r6", NULL, 4, 0, eEncodingUint , eFormatHex, { gcc_r6, dwarf_r6, LLDB_INVALID_REGNUM, gdb_arm_r6, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "r7", NULL, 4, 0, eEncodingUint , eFormatHex, { gcc_r7, dwarf_r7, LLDB_REGNUM_GENERIC_FP, gdb_arm_r7, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "r8", NULL, 4, 0, eEncodingUint , eFormatHex, { gcc_r8, dwarf_r8, LLDB_INVALID_REGNUM, gdb_arm_r8, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "r9", NULL, 4, 0, eEncodingUint , eFormatHex, { gcc_r9, dwarf_r9, LLDB_INVALID_REGNUM, gdb_arm_r9, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "r10", NULL, 4, 0, eEncodingUint , eFormatHex, { gcc_r10, dwarf_r10, LLDB_INVALID_REGNUM, gdb_arm_r10, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "r11", NULL, 4, 0, eEncodingUint , eFormatHex, { gcc_r11, dwarf_r11, LLDB_INVALID_REGNUM, gdb_arm_r11, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "r12", NULL, 4, 0, eEncodingUint , eFormatHex, { gcc_r12, dwarf_r12, LLDB_INVALID_REGNUM, gdb_arm_r12, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "sp", "r13", 4, 0, eEncodingUint , eFormatHex, { gcc_sp, dwarf_sp, LLDB_REGNUM_GENERIC_SP, gdb_arm_sp, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "lr", "r14", 4, 0, eEncodingUint , eFormatHex, { gcc_lr, dwarf_lr, LLDB_REGNUM_GENERIC_RA, gdb_arm_lr, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "pc", "r15", 4, 0, eEncodingUint , eFormatHex, { gcc_pc, dwarf_pc, LLDB_REGNUM_GENERIC_PC, gdb_arm_pc, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "cpsr", "psr", 4, 0, eEncodingUint , eFormatHex, { gcc_cpsr, dwarf_cpsr, LLDB_REGNUM_GENERIC_FLAGS, gdb_arm_cpsr, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "s0", NULL, 4, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s0, LLDB_INVALID_REGNUM, gdb_arm_s0, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "s1", NULL, 4, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s1, LLDB_INVALID_REGNUM, gdb_arm_s1, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "s2", NULL, 4, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s2, LLDB_INVALID_REGNUM, gdb_arm_s2, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "s3", NULL, 4, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s3, LLDB_INVALID_REGNUM, gdb_arm_s3, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "s4", NULL, 4, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s4, LLDB_INVALID_REGNUM, gdb_arm_s4, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "s5", NULL, 4, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s5, LLDB_INVALID_REGNUM, gdb_arm_s5, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "s6", NULL, 4, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s6, LLDB_INVALID_REGNUM, gdb_arm_s6, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "s7", NULL, 4, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s7, LLDB_INVALID_REGNUM, gdb_arm_s7, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "s8", NULL, 4, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s8, LLDB_INVALID_REGNUM, gdb_arm_s8, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "s9", NULL, 4, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s9, LLDB_INVALID_REGNUM, gdb_arm_s9, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "s10", NULL, 4, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s10, LLDB_INVALID_REGNUM, gdb_arm_s10, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "s11", NULL, 4, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s11, LLDB_INVALID_REGNUM, gdb_arm_s11, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "s12", NULL, 4, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s12, LLDB_INVALID_REGNUM, gdb_arm_s12, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "s13", NULL, 4, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s13, LLDB_INVALID_REGNUM, gdb_arm_s13, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "s14", NULL, 4, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s14, LLDB_INVALID_REGNUM, gdb_arm_s14, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "s15", NULL, 4, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s15, LLDB_INVALID_REGNUM, gdb_arm_s15, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "s16", NULL, 4, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s16, LLDB_INVALID_REGNUM, gdb_arm_s16, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "s17", NULL, 4, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s17, LLDB_INVALID_REGNUM, gdb_arm_s17, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "s18", NULL, 4, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s18, LLDB_INVALID_REGNUM, gdb_arm_s18, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "s19", NULL, 4, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s19, LLDB_INVALID_REGNUM, gdb_arm_s19, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "s20", NULL, 4, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s20, LLDB_INVALID_REGNUM, gdb_arm_s20, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "s21", NULL, 4, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s21, LLDB_INVALID_REGNUM, gdb_arm_s21, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "s22", NULL, 4, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s22, LLDB_INVALID_REGNUM, gdb_arm_s22, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "s23", NULL, 4, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s23, LLDB_INVALID_REGNUM, gdb_arm_s23, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "s24", NULL, 4, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s24, LLDB_INVALID_REGNUM, gdb_arm_s24, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "s25", NULL, 4, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s25, LLDB_INVALID_REGNUM, gdb_arm_s25, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "s26", NULL, 4, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s26, LLDB_INVALID_REGNUM, gdb_arm_s26, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "s27", NULL, 4, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s27, LLDB_INVALID_REGNUM, gdb_arm_s27, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "s28", NULL, 4, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s28, LLDB_INVALID_REGNUM, gdb_arm_s28, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "s29", NULL, 4, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s29, LLDB_INVALID_REGNUM, gdb_arm_s29, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "s30", NULL, 4, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s30, LLDB_INVALID_REGNUM, gdb_arm_s30, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "s31", NULL, 4, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s31, LLDB_INVALID_REGNUM, gdb_arm_s31, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "fpscr", NULL, 4, 0, eEncodingUint , eFormatHex , { LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,LLDB_INVALID_REGNUM, gdb_arm_fpscr, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "d0", NULL, 8, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d0, LLDB_INVALID_REGNUM, gdb_arm_d0, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "d1", NULL, 8, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d1, LLDB_INVALID_REGNUM, gdb_arm_d1, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "d2", NULL, 8, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d2, LLDB_INVALID_REGNUM, gdb_arm_d2, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "d3", NULL, 8, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d3, LLDB_INVALID_REGNUM, gdb_arm_d3, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "d4", NULL, 8, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d4, LLDB_INVALID_REGNUM, gdb_arm_d4, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "d5", NULL, 8, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d5, LLDB_INVALID_REGNUM, gdb_arm_d5, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "d6", NULL, 8, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d6, LLDB_INVALID_REGNUM, gdb_arm_d6, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "d7", NULL, 8, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d7, LLDB_INVALID_REGNUM, gdb_arm_d7, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "d8", NULL, 8, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d8, LLDB_INVALID_REGNUM, gdb_arm_d8, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "d9", NULL, 8, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d9, LLDB_INVALID_REGNUM, gdb_arm_d9, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "d10", NULL, 8, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d10, LLDB_INVALID_REGNUM, gdb_arm_d10, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "d11", NULL, 8, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d11, LLDB_INVALID_REGNUM, gdb_arm_d11, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "d12", NULL, 8, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d12, LLDB_INVALID_REGNUM, gdb_arm_d12, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "d13", NULL, 8, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d13, LLDB_INVALID_REGNUM, gdb_arm_d13, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "d14", NULL, 8, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d14, LLDB_INVALID_REGNUM, gdb_arm_d14, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "d15", NULL, 8, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d15, LLDB_INVALID_REGNUM, gdb_arm_d15, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "d16", NULL, 8, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d16, LLDB_INVALID_REGNUM, gdb_arm_d16, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "d17", NULL, 8, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d17, LLDB_INVALID_REGNUM, gdb_arm_d17, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "d18", NULL, 8, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d18, LLDB_INVALID_REGNUM, gdb_arm_d18, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "d19", NULL, 8, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d19, LLDB_INVALID_REGNUM, gdb_arm_d19, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "d20", NULL, 8, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d20, LLDB_INVALID_REGNUM, gdb_arm_d20, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "d21", NULL, 8, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d21, LLDB_INVALID_REGNUM, gdb_arm_d21, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "d22", NULL, 8, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d22, LLDB_INVALID_REGNUM, gdb_arm_d22, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "d23", NULL, 8, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d23, LLDB_INVALID_REGNUM, gdb_arm_d23, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "d24", NULL, 8, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d24, LLDB_INVALID_REGNUM, gdb_arm_d24, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "d25", NULL, 8, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d25, LLDB_INVALID_REGNUM, gdb_arm_d25, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "d26", NULL, 8, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d26, LLDB_INVALID_REGNUM, gdb_arm_d26, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "d27", NULL, 8, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d27, LLDB_INVALID_REGNUM, gdb_arm_d27, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "d28", NULL, 8, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d28, LLDB_INVALID_REGNUM, gdb_arm_d28, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "d29", NULL, 8, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d29, LLDB_INVALID_REGNUM, gdb_arm_d29, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "d30", NULL, 8, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d30, LLDB_INVALID_REGNUM, gdb_arm_d30, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "d31", NULL, 8, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d31, LLDB_INVALID_REGNUM, gdb_arm_d31, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "r8_usr", NULL, 4, 0, eEncodingUint , eFormatHex, { LLDB_INVALID_REGNUM, dwarf_r8_usr, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "r9_usr", NULL, 4, 0, eEncodingUint , eFormatHex, { LLDB_INVALID_REGNUM, dwarf_r9_usr, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "r10_usr", NULL, 4, 0, eEncodingUint , eFormatHex, { LLDB_INVALID_REGNUM, dwarf_r10_usr, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "r11_usr", NULL, 4, 0, eEncodingUint , eFormatHex, { LLDB_INVALID_REGNUM, dwarf_r11_usr, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "r12_usr", NULL, 4, 0, eEncodingUint , eFormatHex, { LLDB_INVALID_REGNUM, dwarf_r12_usr, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "r13_usr", "sp_usr", 4, 0, eEncodingUint , eFormatHex, { LLDB_INVALID_REGNUM, dwarf_r13_usr, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "r14_usr", "lr_usr", 4, 0, eEncodingUint , eFormatHex, { LLDB_INVALID_REGNUM, dwarf_r14_usr, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "r8_fiq", NULL, 4, 0, eEncodingUint , eFormatHex, { LLDB_INVALID_REGNUM, dwarf_r8_fiq, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "r9_fiq", NULL, 4, 0, eEncodingUint , eFormatHex, { LLDB_INVALID_REGNUM, dwarf_r9_fiq, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "r10_fiq", NULL, 4, 0, eEncodingUint , eFormatHex, { LLDB_INVALID_REGNUM, dwarf_r10_fiq, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "r11_fiq", NULL, 4, 0, eEncodingUint , eFormatHex, { LLDB_INVALID_REGNUM, dwarf_r11_fiq, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "r12_fiq", NULL, 4, 0, eEncodingUint , eFormatHex, { LLDB_INVALID_REGNUM, dwarf_r12_fiq, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "r13_fiq", "sp_fiq", 4, 0, eEncodingUint , eFormatHex, { LLDB_INVALID_REGNUM, dwarf_r13_fiq, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "r14_fiq", "lr_fiq", 4, 0, eEncodingUint , eFormatHex, { LLDB_INVALID_REGNUM, dwarf_r14_fiq, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "r13_irq", "sp_irq", 4, 0, eEncodingUint , eFormatHex, { LLDB_INVALID_REGNUM, dwarf_r13_irq, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "r14_irq", "lr_irq", 4, 0, eEncodingUint , eFormatHex, { LLDB_INVALID_REGNUM, dwarf_r14_irq, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "r13_abt", "sp_abt", 4, 0, eEncodingUint , eFormatHex, { LLDB_INVALID_REGNUM, dwarf_r13_abt, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "r14_abt", "lr_abt", 4, 0, eEncodingUint , eFormatHex, { LLDB_INVALID_REGNUM, dwarf_r14_abt, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "r13_und", "sp_und", 4, 0, eEncodingUint , eFormatHex, { LLDB_INVALID_REGNUM, dwarf_r13_und, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "r14_und", "lr_und", 4, 0, eEncodingUint , eFormatHex, { LLDB_INVALID_REGNUM, dwarf_r14_und, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "r13_svc", "sp_svc", 4, 0, eEncodingUint , eFormatHex, { LLDB_INVALID_REGNUM, dwarf_r13_svc, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "r14_svc", "lr_svc", 4, 0, eEncodingUint , eFormatHex, { LLDB_INVALID_REGNUM, dwarf_r14_svc, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM }, NULL, NULL} +}; +static const uint32_t k_num_register_infos = sizeof(g_register_infos)/sizeof(RegisterInfo); +static bool g_register_info_names_constified = false; + +const lldb_private::RegisterInfo * +ABIMacOSX_arm::GetRegisterInfoArray (uint32_t &count) +{ + // Make the C-string names and alt_names for the register infos into const + // C-string values by having the ConstString unique the names in the global + // constant C-string pool. + if (!g_register_info_names_constified) + { + g_register_info_names_constified = true; + for (uint32_t i=0; iConvertRegisterKindToRegisterNumber (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC); + const uint32_t sp_reg_num = reg_ctx->ConvertRegisterKindToRegisterNumber (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_SP); + const uint32_t ra_reg_num = reg_ctx->ConvertRegisterKindToRegisterNumber (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_RA); + + RegisterValue reg_value; + + if (arg1_ptr) + { + reg_value.SetUInt32(*arg1_ptr); + if (!reg_ctx->WriteRegister (reg_ctx->GetRegisterInfoByName("r0"), reg_value)) + return false; + + if (arg2_ptr) + { + reg_value.SetUInt32(*arg2_ptr); + if (!reg_ctx->WriteRegister (reg_ctx->GetRegisterInfoByName("r1"), reg_value)) + return false; + + if (arg3_ptr) + { + reg_value.SetUInt32(*arg3_ptr); + if (!reg_ctx->WriteRegister (reg_ctx->GetRegisterInfoByName("r2"), reg_value)) + return false; + if (arg4_ptr) + { + reg_value.SetUInt32(*arg4_ptr); + const RegisterInfo *reg_info = reg_ctx->GetRegisterInfoByName("r3"); + if (!reg_ctx->WriteRegister (reg_info, reg_value)) + return false; + if (arg5_ptr) + { + // Keep the stack 8 byte aligned, not that we need to + sp -= 8; + sp &= ~(8ull-1ull); + reg_value.SetUInt32(*arg5_ptr); + if (reg_ctx->WriteRegisterValueToMemory (reg_info, sp, reg_info->byte_size, reg_value).Fail()) + return false; + if (arg6_ptr) + { + reg_value.SetUInt32(*arg6_ptr); + if (reg_ctx->WriteRegisterValueToMemory (reg_info, sp + 4, reg_info->byte_size, reg_value).Fail()) + return false; + } + } + } + } + } + } + + + TargetSP target_sp (thread.CalculateTarget()); + Address so_addr; + + // Figure out if our return address is ARM or Thumb by using the + // Address::GetCallableLoadAddress(Target*) which will figure out the ARM + // thumb-ness and set the correct address bits for us. + so_addr.SetLoadAddress (return_addr, target_sp.get()); + return_addr = so_addr.GetCallableLoadAddress (target_sp.get()); + + // Set "lr" to the return address + if (!reg_ctx->WriteRegisterFromUnsigned (ra_reg_num, return_addr)) + return false; + + // Set "sp" to the requested value + if (!reg_ctx->WriteRegisterFromUnsigned (sp_reg_num, sp)) + return false; + + // If bit zero or 1 is set, this must be a thumb function, no need to figure + // this out from the symbols. + so_addr.SetLoadAddress (function_addr, target_sp.get()); + function_addr = so_addr.GetCallableLoadAddress (target_sp.get()); + + const RegisterInfo *cpsr_reg_info = reg_ctx->GetRegisterInfoByName("cpsr"); + const uint32_t curr_cpsr = reg_ctx->ReadRegisterAsUnsigned(cpsr_reg_info, 0); + + // Make a new CPSR and mask out any Thumb IT (if/then) bits + uint32_t new_cpsr = curr_cpsr & ~MASK_CPSR_IT_MASK; + // If bit zero or 1 is set, this must be thumb... + if (function_addr & 1ull) + new_cpsr |= MASK_CPSR_T; // Set T bit in CPSR + else + new_cpsr &= ~MASK_CPSR_T; // Clear T bit in CPSR + + if (new_cpsr != curr_cpsr) + { + if (!reg_ctx->WriteRegisterFromUnsigned (cpsr_reg_info, new_cpsr)) + return false; + } + + function_addr &= ~1ull; // clear bit zero since the CPSR will take care of the mode for us + + // Set "pc" to the address requested + if (!reg_ctx->WriteRegisterFromUnsigned (pc_reg_num, function_addr)) + return false; + + return true; +} + +bool +ABIMacOSX_arm::GetArgumentValues (Thread &thread, + ValueList &values) const +{ + uint32_t num_values = values.GetSize(); + + + ExecutionContext exe_ctx (thread.shared_from_this()); + // For now, assume that the types in the AST values come from the Target's + // scratch AST. + + // Extract the register context so we can read arguments from registers + + RegisterContext *reg_ctx = thread.GetRegisterContext().get(); + + if (!reg_ctx) + return false; + + addr_t sp = 0; + + for (uint32_t value_idx = 0; value_idx < num_values; ++value_idx) + { + // We currently only support extracting values with Clang QualTypes. + // Do we care about others? + Value *value = values.GetValueAtIndex(value_idx); + + if (!value) + return false; + + ClangASTType clang_type = value->GetClangType(); + if (clang_type) + { + bool is_signed = false; + size_t bit_width = 0; + if (clang_type.IsIntegerType (is_signed)) + { + bit_width = clang_type.GetBitSize(); + } + else if (clang_type.IsPointerOrReferenceType ()) + { + bit_width = clang_type.GetBitSize(); + } + else + { + // We only handle integer, pointer and reference types currently... + return false; + } + + if (bit_width <= (exe_ctx.GetProcessRef().GetAddressByteSize() * 8)) + { + if (value_idx < 4) + { + // Arguments 1-4 are in r0-r3... + const RegisterInfo *arg_reg_info = NULL; + // Search by generic ID first, then fall back to by name + uint32_t arg_reg_num = reg_ctx->ConvertRegisterKindToRegisterNumber (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG1 + value_idx); + if (arg_reg_num != LLDB_INVALID_REGNUM) + { + arg_reg_info = reg_ctx->GetRegisterInfoAtIndex(arg_reg_num); + } + else + { + switch (value_idx) + { + case 0: arg_reg_info = reg_ctx->GetRegisterInfoByName("r0"); break; + case 1: arg_reg_info = reg_ctx->GetRegisterInfoByName("r1"); break; + case 2: arg_reg_info = reg_ctx->GetRegisterInfoByName("r2"); break; + case 3: arg_reg_info = reg_ctx->GetRegisterInfoByName("r3"); break; + } + } + + if (arg_reg_info) + { + RegisterValue reg_value; + + if (reg_ctx->ReadRegister(arg_reg_info, reg_value)) + { + if (is_signed) + reg_value.SignExtend(bit_width); + if (!reg_value.GetScalarValue(value->GetScalar())) + return false; + continue; + } + } + return false; + } + else + { + if (sp == 0) + { + // Read the stack pointer if it already hasn't been read + sp = reg_ctx->GetSP(0); + if (sp == 0) + return false; + } + + // Arguments 5 on up are on the stack + const uint32_t arg_byte_size = (bit_width + (8-1)) / 8; + Error error; + if (!exe_ctx.GetProcessRef().ReadScalarIntegerFromMemory(sp, arg_byte_size, is_signed, value->GetScalar(), error)) + return false; + + sp += arg_byte_size; + } + } + } + } + return true; +} + +ValueObjectSP +ABIMacOSX_arm::GetReturnValueObjectImpl (Thread &thread, + lldb_private::ClangASTType &clang_type) const +{ + Value value; + ValueObjectSP return_valobj_sp; + + if (!clang_type) + return return_valobj_sp; + + clang::ASTContext *ast_context = clang_type.GetASTContext(); + if (!ast_context) + return return_valobj_sp; + + //value.SetContext (Value::eContextTypeClangType, clang_type.GetOpaqueQualType()); + value.SetClangType (clang_type); + + RegisterContext *reg_ctx = thread.GetRegisterContext().get(); + if (!reg_ctx) + return return_valobj_sp; + + bool is_signed; + + // Get the pointer to the first stack argument so we have a place to start + // when reading data + + const RegisterInfo *r0_reg_info = reg_ctx->GetRegisterInfoByName("r0", 0); + if (clang_type.IsIntegerType (is_signed)) + { + size_t bit_width = clang_type.GetBitSize(); + + switch (bit_width) + { + default: + return return_valobj_sp; + case 64: + { + const RegisterInfo *r1_reg_info = reg_ctx->GetRegisterInfoByName("r1", 0); + uint64_t raw_value; + raw_value = reg_ctx->ReadRegisterAsUnsigned(r0_reg_info, 0) & UINT32_MAX; + raw_value |= ((uint64_t)(reg_ctx->ReadRegisterAsUnsigned(r1_reg_info, 0) & UINT32_MAX)) << 32; + if (is_signed) + value.GetScalar() = (int64_t)raw_value; + else + value.GetScalar() = (uint64_t)raw_value; + } + break; + case 32: + if (is_signed) + value.GetScalar() = (int32_t)(reg_ctx->ReadRegisterAsUnsigned(r0_reg_info, 0) & UINT32_MAX); + else + value.GetScalar() = (uint32_t)(reg_ctx->ReadRegisterAsUnsigned(r0_reg_info, 0) & UINT32_MAX); + break; + case 16: + if (is_signed) + value.GetScalar() = (int16_t)(reg_ctx->ReadRegisterAsUnsigned(r0_reg_info, 0) & UINT16_MAX); + else + value.GetScalar() = (uint16_t)(reg_ctx->ReadRegisterAsUnsigned(r0_reg_info, 0) & UINT16_MAX); + break; + case 8: + if (is_signed) + value.GetScalar() = (int8_t)(reg_ctx->ReadRegisterAsUnsigned(r0_reg_info, 0) & UINT8_MAX); + else + value.GetScalar() = (uint8_t)(reg_ctx->ReadRegisterAsUnsigned(r0_reg_info, 0) & UINT8_MAX); + break; + } + } + else if (clang_type.IsPointerType ()) + { + uint32_t ptr = thread.GetRegisterContext()->ReadRegisterAsUnsigned(r0_reg_info, 0) & UINT32_MAX; + value.GetScalar() = ptr; + } + else + { + // not handled yet + return return_valobj_sp; + } + + // If we get here, we have a valid Value, so make our ValueObject out of it: + + return_valobj_sp = ValueObjectConstResult::Create(thread.GetStackFrameAtIndex(0).get(), + value, + ConstString("")); + return return_valobj_sp; +} + +Error +ABIMacOSX_arm::SetReturnValueObject(lldb::StackFrameSP &frame_sp, lldb::ValueObjectSP &new_value_sp) +{ + Error error; + if (!new_value_sp) + { + error.SetErrorString("Empty value object for return value."); + return error; + } + + ClangASTType clang_type = new_value_sp->GetClangType(); + if (!clang_type) + { + error.SetErrorString ("Null clang type for return value."); + return error; + } + + Thread *thread = frame_sp->GetThread().get(); + + bool is_signed; + uint32_t count; + bool is_complex; + + RegisterContext *reg_ctx = thread->GetRegisterContext().get(); + + bool set_it_simple = false; + if (clang_type.IsIntegerType (is_signed) || clang_type.IsPointerType()) + { + DataExtractor data; + size_t num_bytes = new_value_sp->GetData(data); + lldb::offset_t offset = 0; + if (num_bytes <= 8) + { + const RegisterInfo *r0_info = reg_ctx->GetRegisterInfoByName("r0", 0); + if (num_bytes <= 4) + { + uint32_t raw_value = data.GetMaxU32(&offset, num_bytes); + + if (reg_ctx->WriteRegisterFromUnsigned (r0_info, raw_value)) + set_it_simple = true; + } + else + { + uint32_t raw_value = data.GetMaxU32(&offset, 4); + + if (reg_ctx->WriteRegisterFromUnsigned (r0_info, raw_value)) + { + const RegisterInfo *r1_info = reg_ctx->GetRegisterInfoByName("r1", 0); + uint32_t raw_value = data.GetMaxU32(&offset, num_bytes - offset); + + if (reg_ctx->WriteRegisterFromUnsigned (r1_info, raw_value)) + set_it_simple = true; + } + } + } + else + { + error.SetErrorString("We don't support returning longer than 64 bit integer values at present."); + } + } + else if (clang_type.IsFloatingPointType (count, is_complex)) + { + if (is_complex) + error.SetErrorString ("We don't support returning complex values at present"); + else + error.SetErrorString ("We don't support returning float values at present"); + } + + if (!set_it_simple) + error.SetErrorString ("We only support setting simple integer return types at present."); + + return error; +} + +bool +ABIMacOSX_arm::CreateFunctionEntryUnwindPlan (UnwindPlan &unwind_plan) +{ + uint32_t reg_kind = unwind_plan.GetRegisterKind(); + uint32_t lr_reg_num = LLDB_INVALID_REGNUM; + uint32_t sp_reg_num = LLDB_INVALID_REGNUM; + uint32_t pc_reg_num = LLDB_INVALID_REGNUM; + + switch (reg_kind) + { + case eRegisterKindDWARF: + case eRegisterKindGCC: + lr_reg_num = dwarf_lr; + sp_reg_num = dwarf_sp; + pc_reg_num = dwarf_pc; + break; + + case eRegisterKindGeneric: + lr_reg_num = LLDB_REGNUM_GENERIC_RA; + sp_reg_num = LLDB_REGNUM_GENERIC_SP; + pc_reg_num = LLDB_REGNUM_GENERIC_PC; + break; + } + + if (lr_reg_num == LLDB_INVALID_REGNUM || + sp_reg_num == LLDB_INVALID_REGNUM || + pc_reg_num == LLDB_INVALID_REGNUM) + return false; + + UnwindPlan::RowSP row(new UnwindPlan::Row); + + // Our Call Frame Address is the stack pointer value + row->SetCFARegister (sp_reg_num); + + // The previous PC is in the LR + row->SetRegisterLocationToRegister(pc_reg_num, lr_reg_num, true); + unwind_plan.AppendRow (row); + + // All other registers are the same. + + unwind_plan.SetSourceName ("arm at-func-entry default"); + unwind_plan.SetSourcedFromCompiler (eLazyBoolNo); + + return true; +} + +bool +ABIMacOSX_arm::CreateDefaultUnwindPlan (UnwindPlan &unwind_plan) +{ + uint32_t fp_reg_num = dwarf_r7; // apple uses r7 for all frames. Normal arm uses r11; + uint32_t pc_reg_num = dwarf_pc; + + UnwindPlan::RowSP row(new UnwindPlan::Row); + const int32_t ptr_size = 4; + + unwind_plan.Clear (); + unwind_plan.SetRegisterKind (eRegisterKindDWARF); + row->SetCFARegister (fp_reg_num); + row->SetCFAOffset (2 * ptr_size); + row->SetOffset (0); + + row->SetRegisterLocationToAtCFAPlusOffset(fp_reg_num, ptr_size * -2, true); + row->SetRegisterLocationToAtCFAPlusOffset(pc_reg_num, ptr_size * -1, true); + + unwind_plan.AppendRow (row); + unwind_plan.SetSourceName ("arm-apple-ios default unwind plan"); + unwind_plan.SetSourcedFromCompiler (eLazyBoolNo); + unwind_plan.SetUnwindPlanValidAtAllInstructions (eLazyBoolNo); + + return true; +} + +// ARMv7 on iOS general purpose reg rules: +// r0-r3 not preserved (used for argument passing) +// r4-r6 preserved +// r7 preserved (frame pointer) +// r8 preserved +// r9 not preserved (usable as volatile scratch register with iOS 3.x and later) +// r10-r11 preserved +// r12 not presrved +// r13 preserved (stack pointer) +// r14 not preserved (link register) +// r15 preserved (pc) +// cpsr not preserved (different rules for different bits) + +// ARMv7 on iOS floating point rules: +// d0-d7 not preserved (aka s0-s15, q0-q3) +// d8-d15 preserved (aka s16-s31, q4-q7) +// d16-d31 not preserved (aka q8-q15) + +bool +ABIMacOSX_arm::RegisterIsVolatile (const RegisterInfo *reg_info) +{ + if (reg_info) + { + // Volatile registers include: r0, r1, r2, r3, r9, r12, r13 + const char *name = reg_info->name; + if (name[0] == 'r') + { + switch (name[1]) + { + case '0': return name[2] == '\0'; // r0 + case '1': + switch (name[2]) + { + case '\0': + return true; // r1 + case '2': + case '3': + return name[2] == '\0'; // r12 - r13 + default: + break; + } + break; + + case '2': return name[2] == '\0'; // r2 + case '3': return name[2] == '\0'; // r3 + case '9': return name[2] == '\0'; // r9 (apple-ios only...) + + break; + } + } + else if (name[0] == 'd') + { + switch (name[1]) + { + case '0': + return name[2] == '\0'; // d0 is volatile + + case '1': + switch (name[2]) + { + case '\0': + return true; // d1 is volatile + case '6': + case '7': + case '8': + case '9': + return name[3] == '\0'; // d16 - d19 are volatile + default: + break; + } + break; + + case '2': + switch (name[2]) + { + case '\0': + return true; // d2 is volatile + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + return name[3] == '\0'; // d20 - d29 are volatile + default: + break; + } + break; + + case '3': + switch (name[2]) + { + case '\0': + return true; // d3 is volatile + case '0': + case '1': + return name[3] == '\0'; // d30 - d31 are volatile + default: + break; + } + case '4': + case '5': + case '6': + case '7': + return name[2] == '\0'; // d4 - d7 are volatile + + default: + break; + } + } + else if (name[0] == 's') + { + switch (name[1]) + { + case '0': + return name[2] == '\0'; // s0 is volatile + + case '1': + switch (name[2]) + { + case '\0': + return true; // s1 is volatile + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + return name[3] == '\0'; // s10 - s15 are volatile + default: + break; + } + break; + + case '2': + switch (name[2]) + { + case '\0': + return true; // s2 is volatile + default: + break; + } + break; + + case '3': + switch (name[2]) + { + case '\0': + return true; // s3 is volatile + default: + break; + } + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + return name[2] == '\0'; // s4 - s9 are volatile + + default: + break; + } + } + else if (name[0] == 's' && name[1] == 'p' && name[2] == '\0') + return true; + } + return false; +} + +void +ABIMacOSX_arm::Initialize() +{ + PluginManager::RegisterPlugin (GetPluginNameStatic(), + "Mac OS X ABI for arm targets", + CreateInstance); +} + +void +ABIMacOSX_arm::Terminate() +{ + PluginManager::UnregisterPlugin (CreateInstance); +} + +lldb_private::ConstString +ABIMacOSX_arm::GetPluginNameStatic() +{ + static ConstString g_name("macosx-arm"); + return g_name; +} + +//------------------------------------------------------------------ +// PluginInterface protocol +//------------------------------------------------------------------ +lldb_private::ConstString +ABIMacOSX_arm::GetPluginName() +{ + return GetPluginNameStatic(); +} + +uint32_t +ABIMacOSX_arm::GetPluginVersion() +{ + return 1; +} + diff --git a/contrib/llvm/tools/lldb/source/Plugins/ABI/MacOSX-arm/ABIMacOSX_arm.h b/contrib/llvm/tools/lldb/source/Plugins/ABI/MacOSX-arm/ABIMacOSX_arm.h new file mode 100644 index 00000000000..27cea85aaf6 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Plugins/ABI/MacOSX-arm/ABIMacOSX_arm.h @@ -0,0 +1,138 @@ +//===-- ABIMacOSX_arm.h ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ABIMacOSX_arm_h_ +#define liblldb_ABIMacOSX_arm_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private.h" +#include "lldb/Target/ABI.h" + +class ABIMacOSX_arm : public lldb_private::ABI +{ +public: + ~ABIMacOSX_arm() { } + + virtual size_t + GetRedZoneSize () const; + + virtual bool + PrepareTrivialCall (lldb_private::Thread &thread, + lldb::addr_t sp, + lldb::addr_t func_addr, + lldb::addr_t returnAddress, + lldb::addr_t *arg1_ptr = NULL, + lldb::addr_t *arg2_ptr = NULL, + lldb::addr_t *arg3_ptr = NULL, + lldb::addr_t *arg4_ptr = NULL, + lldb::addr_t *arg5_ptr = NULL, + lldb::addr_t *arg6_ptr = NULL) const; + + virtual bool + GetArgumentValues (lldb_private::Thread &thread, + lldb_private::ValueList &values) const; + + virtual lldb_private::Error + SetReturnValueObject(lldb::StackFrameSP &frame_sp, lldb::ValueObjectSP &new_value); + +protected: + virtual lldb::ValueObjectSP + GetReturnValueObjectImpl (lldb_private::Thread &thread, + lldb_private::ClangASTType &ast_type) const; + +public: + virtual bool + CreateFunctionEntryUnwindPlan (lldb_private::UnwindPlan &unwind_plan); + + virtual bool + CreateDefaultUnwindPlan (lldb_private::UnwindPlan &unwind_plan); + + virtual bool + RegisterIsVolatile (const lldb_private::RegisterInfo *reg_info); + + virtual bool + StackUsesFrames () + { + return true; + } + + virtual bool + CallFrameAddressIsValid (lldb::addr_t cfa) + { + // Make sure the stack call frame addresses are are 4 byte aligned + if (cfa & (4ull - 1ull)) + return false; // Not 4 byte aligned + if (cfa == 0) + return false; // Zero is not a valid stack address + return true; + } + + virtual bool + CodeAddressIsValid (lldb::addr_t pc) + { + // Just make sure the address is a valid 32 bit address. Bit zero + // might be set due to Thumb function calls, so don't enforce 2 byte + // alignment + return pc <= UINT32_MAX; + } + + virtual lldb::addr_t + FixCodeAddress (lldb::addr_t pc) + { + // ARM uses bit zero to signify a code address is thumb, so we must + // strip bit zero in any code addresses. + return pc & ~(lldb::addr_t)1; + } + + virtual bool + FunctionCallsChangeCFA () + { + return false; + } + + virtual const lldb_private::RegisterInfo * + GetRegisterInfoArray (uint32_t &count); + + //------------------------------------------------------------------ + // Static Functions + //------------------------------------------------------------------ + static void + Initialize(); + + static void + Terminate(); + + static lldb::ABISP + CreateInstance (const lldb_private::ArchSpec &arch); + + static lldb_private::ConstString + GetPluginNameStatic(); + + //------------------------------------------------------------------ + // PluginInterface protocol + //------------------------------------------------------------------ + virtual lldb_private::ConstString + GetPluginName(); + + virtual uint32_t + GetPluginVersion(); + +protected: +private: + ABIMacOSX_arm() : + lldb_private::ABI() + { + // Call CreateInstance instead. + } +}; + +#endif // liblldb_ABIMacOSX_arm_h_ diff --git a/contrib/llvm/tools/lldb/source/Plugins/ABI/MacOSX-i386/ABIMacOSX_i386.cpp b/contrib/llvm/tools/lldb/source/Plugins/ABI/MacOSX-i386/ABIMacOSX_i386.cpp new file mode 100644 index 00000000000..deb531d937a --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Plugins/ABI/MacOSX-i386/ABIMacOSX_i386.cpp @@ -0,0 +1,977 @@ +//===-- ABIMacOSX_i386.cpp --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "ABIMacOSX_i386.h" + +#include "lldb/Core/ConstString.h" +#include "lldb/Core/Error.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/RegisterValue.h" +#include "lldb/Core/Scalar.h" +#include "lldb/Core/ValueObjectConstResult.h" +#include "lldb/Symbol/ClangASTContext.h" +#include "lldb/Symbol/UnwindPlan.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" + +#include "llvm/ADT/Triple.h" + +#include + +using namespace lldb; +using namespace lldb_private; + +enum +{ + gcc_eax = 0, + gcc_ecx, + gcc_edx, + gcc_ebx, + gcc_ebp, + gcc_esp, + gcc_esi, + gcc_edi, + gcc_eip, + gcc_eflags +}; + +enum +{ + dwarf_eax = 0, + dwarf_ecx, + dwarf_edx, + dwarf_ebx, + dwarf_esp, + dwarf_ebp, + dwarf_esi, + dwarf_edi, + dwarf_eip, + dwarf_eflags, + dwarf_stmm0 = 11, + dwarf_stmm1, + dwarf_stmm2, + dwarf_stmm3, + dwarf_stmm4, + dwarf_stmm5, + dwarf_stmm6, + dwarf_stmm7, + dwarf_xmm0 = 21, + dwarf_xmm1, + dwarf_xmm2, + dwarf_xmm3, + dwarf_xmm4, + dwarf_xmm5, + dwarf_xmm6, + dwarf_xmm7, + dwarf_ymm0 = dwarf_xmm0, + dwarf_ymm1 = dwarf_xmm1, + dwarf_ymm2 = dwarf_xmm2, + dwarf_ymm3 = dwarf_xmm3, + dwarf_ymm4 = dwarf_xmm4, + dwarf_ymm5 = dwarf_xmm5, + dwarf_ymm6 = dwarf_xmm6, + dwarf_ymm7 = dwarf_xmm7 +}; + +enum +{ + gdb_eax = 0, + gdb_ecx = 1, + gdb_edx = 2, + gdb_ebx = 3, + gdb_esp = 4, + gdb_ebp = 5, + gdb_esi = 6, + gdb_edi = 7, + gdb_eip = 8, + gdb_eflags = 9, + gdb_cs = 10, + gdb_ss = 11, + gdb_ds = 12, + gdb_es = 13, + gdb_fs = 14, + gdb_gs = 15, + gdb_stmm0 = 16, + gdb_stmm1 = 17, + gdb_stmm2 = 18, + gdb_stmm3 = 19, + gdb_stmm4 = 20, + gdb_stmm5 = 21, + gdb_stmm6 = 22, + gdb_stmm7 = 23, + gdb_fctrl = 24, gdb_fcw = gdb_fctrl, + gdb_fstat = 25, gdb_fsw = gdb_fstat, + gdb_ftag = 26, gdb_ftw = gdb_ftag, + gdb_fiseg = 27, gdb_fpu_cs = gdb_fiseg, + gdb_fioff = 28, gdb_ip = gdb_fioff, + gdb_foseg = 29, gdb_fpu_ds = gdb_foseg, + gdb_fooff = 30, gdb_dp = gdb_fooff, + gdb_fop = 31, + gdb_xmm0 = 32, + gdb_xmm1 = 33, + gdb_xmm2 = 34, + gdb_xmm3 = 35, + gdb_xmm4 = 36, + gdb_xmm5 = 37, + gdb_xmm6 = 38, + gdb_xmm7 = 39, + gdb_mxcsr = 40, + gdb_mm0 = 41, + gdb_mm1 = 42, + gdb_mm2 = 43, + gdb_mm3 = 44, + gdb_mm4 = 45, + gdb_mm5 = 46, + gdb_mm6 = 47, + gdb_mm7 = 48, + gdb_ymm0 = gdb_xmm0, + gdb_ymm1 = gdb_xmm1, + gdb_ymm2 = gdb_xmm2, + gdb_ymm3 = gdb_xmm3, + gdb_ymm4 = gdb_xmm4, + gdb_ymm5 = gdb_xmm5, + gdb_ymm6 = gdb_xmm6, + gdb_ymm7 = gdb_xmm7 +}; + + +static RegisterInfo g_register_infos[] = +{ + // NAME ALT SZ OFF ENCODING FORMAT COMPILER DWARF GENERIC GDB LLDB NATIVE VALUE REGS INVALIDATE REGS + // ====== ======= == === ============= ============ ===================== ===================== ============================ ==================== ====================== ========== =============== + { "eax", NULL, 4, 0, eEncodingUint , eFormatHex , { gcc_eax , dwarf_eax , LLDB_INVALID_REGNUM , gdb_eax , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "ebx" , NULL, 4, 0, eEncodingUint , eFormatHex , { gcc_ebx , dwarf_ebx , LLDB_INVALID_REGNUM , gdb_ebx , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "ecx" , NULL, 4, 0, eEncodingUint , eFormatHex , { gcc_ecx , dwarf_ecx , LLDB_REGNUM_GENERIC_ARG4 , gdb_ecx , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "edx" , NULL, 4, 0, eEncodingUint , eFormatHex , { gcc_edx , dwarf_edx , LLDB_REGNUM_GENERIC_ARG3 , gdb_edx , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "esi" , NULL, 4, 0, eEncodingUint , eFormatHex , { gcc_esi , dwarf_esi , LLDB_REGNUM_GENERIC_ARG2 , gdb_esi , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "edi" , NULL, 4, 0, eEncodingUint , eFormatHex , { gcc_edi , dwarf_edi , LLDB_REGNUM_GENERIC_ARG1 , gdb_edi , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "ebp" , "fp", 4, 0, eEncodingUint , eFormatHex , { gcc_ebp , dwarf_ebp , LLDB_REGNUM_GENERIC_FP , gdb_ebp , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "esp" , "sp", 4, 0, eEncodingUint , eFormatHex , { gcc_esp , dwarf_esp , LLDB_REGNUM_GENERIC_SP , gdb_esp , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "eip" , "pc", 4, 0, eEncodingUint , eFormatHex , { gcc_eip , dwarf_eip , LLDB_REGNUM_GENERIC_PC , gdb_eip , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "eflags", NULL, 4, 0, eEncodingUint , eFormatHex , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_REGNUM_GENERIC_FLAGS , gdb_eflags , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "cs" , NULL, 4, 0, eEncodingUint , eFormatHex , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_cs , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "ss" , NULL, 4, 0, eEncodingUint , eFormatHex , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_ss , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "ds" , NULL, 4, 0, eEncodingUint , eFormatHex , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_ds , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "es" , NULL, 4, 0, eEncodingUint , eFormatHex , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_es , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "fs" , NULL, 4, 0, eEncodingUint , eFormatHex , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_fs , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "gs" , NULL, 4, 0, eEncodingUint , eFormatHex , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_gs , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "stmm0" , NULL, 10, 0, eEncodingVector, eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM , dwarf_stmm0 , LLDB_INVALID_REGNUM , gdb_stmm0 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "stmm1" , NULL, 10, 0, eEncodingVector, eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM , dwarf_stmm1 , LLDB_INVALID_REGNUM , gdb_stmm1 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "stmm2" , NULL, 10, 0, eEncodingVector, eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM , dwarf_stmm2 , LLDB_INVALID_REGNUM , gdb_stmm2 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "stmm3" , NULL, 10, 0, eEncodingVector, eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM , dwarf_stmm3 , LLDB_INVALID_REGNUM , gdb_stmm3 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "stmm4" , NULL, 10, 0, eEncodingVector, eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM , dwarf_stmm4 , LLDB_INVALID_REGNUM , gdb_stmm4 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "stmm5" , NULL, 10, 0, eEncodingVector, eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM , dwarf_stmm5 , LLDB_INVALID_REGNUM , gdb_stmm5 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "stmm6" , NULL, 10, 0, eEncodingVector, eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM , dwarf_stmm6 , LLDB_INVALID_REGNUM , gdb_stmm6 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "stmm7" , NULL, 10, 0, eEncodingVector, eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM , dwarf_stmm7 , LLDB_INVALID_REGNUM , gdb_stmm7 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "fctrl" , NULL, 4, 0, eEncodingUint , eFormatHex , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_fctrl , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "fstat" , NULL, 4, 0, eEncodingUint , eFormatHex , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_fstat , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "ftag" , NULL, 4, 0, eEncodingUint , eFormatHex , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_ftag , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "fiseg" , NULL, 4, 0, eEncodingUint , eFormatHex , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_fiseg , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "fioff" , NULL, 4, 0, eEncodingUint , eFormatHex , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_fioff , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "foseg" , NULL, 4, 0, eEncodingUint , eFormatHex , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_foseg , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "fooff" , NULL, 4, 0, eEncodingUint , eFormatHex , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_fooff , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "fop" , NULL, 4, 0, eEncodingUint , eFormatHex , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_fop , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "xmm0" , NULL, 16, 0, eEncodingVector, eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM , dwarf_xmm0 , LLDB_INVALID_REGNUM , gdb_xmm0 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "xmm1" , NULL, 16, 0, eEncodingVector, eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM , dwarf_xmm1 , LLDB_INVALID_REGNUM , gdb_xmm1 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "xmm2" , NULL, 16, 0, eEncodingVector, eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM , dwarf_xmm2 , LLDB_INVALID_REGNUM , gdb_xmm2 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "xmm3" , NULL, 16, 0, eEncodingVector, eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM , dwarf_xmm3 , LLDB_INVALID_REGNUM , gdb_xmm3 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "xmm4" , NULL, 16, 0, eEncodingVector, eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM , dwarf_xmm4 , LLDB_INVALID_REGNUM , gdb_xmm4 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "xmm5" , NULL, 16, 0, eEncodingVector, eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM , dwarf_xmm5 , LLDB_INVALID_REGNUM , gdb_xmm5 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "xmm6" , NULL, 16, 0, eEncodingVector, eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM , dwarf_xmm6 , LLDB_INVALID_REGNUM , gdb_xmm6 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "xmm7" , NULL, 16, 0, eEncodingVector, eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM , dwarf_xmm7 , LLDB_INVALID_REGNUM , gdb_xmm7 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "mxcsr" , NULL, 4, 0, eEncodingUint , eFormatHex , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_mxcsr , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "ymm0" , NULL, 32, 0, eEncodingVector, eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM , dwarf_ymm0 , LLDB_INVALID_REGNUM , gdb_ymm0 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "ymm1" , NULL, 32, 0, eEncodingVector, eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM , dwarf_ymm1 , LLDB_INVALID_REGNUM , gdb_ymm1 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "ymm2" , NULL, 32, 0, eEncodingVector, eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM , dwarf_ymm2 , LLDB_INVALID_REGNUM , gdb_ymm2 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "ymm3" , NULL, 32, 0, eEncodingVector, eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM , dwarf_ymm3 , LLDB_INVALID_REGNUM , gdb_ymm3 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "ymm4" , NULL, 32, 0, eEncodingVector, eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM , dwarf_ymm4 , LLDB_INVALID_REGNUM , gdb_ymm4 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "ymm5" , NULL, 32, 0, eEncodingVector, eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM , dwarf_ymm5 , LLDB_INVALID_REGNUM , gdb_ymm5 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "ymm6" , NULL, 32, 0, eEncodingVector, eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM , dwarf_ymm6 , LLDB_INVALID_REGNUM , gdb_ymm6 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "ymm7" , NULL, 32, 0, eEncodingVector, eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM , dwarf_ymm7 , LLDB_INVALID_REGNUM , gdb_ymm7 , LLDB_INVALID_REGNUM }, NULL, NULL} +}; + +static const uint32_t k_num_register_infos = sizeof(g_register_infos)/sizeof(RegisterInfo); +static bool g_register_info_names_constified = false; + +const lldb_private::RegisterInfo * +ABIMacOSX_i386::GetRegisterInfoArray (uint32_t &count) +{ + // Make the C-string names and alt_names for the register infos into const + // C-string values by having the ConstString unique the names in the global + // constant C-string pool. + if (!g_register_info_names_constified) + { + g_register_info_names_constified = true; + for (uint32_t i=0; iConvertRegisterKindToRegisterNumber (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC); + uint32_t sp_reg_num = reg_ctx->ConvertRegisterKindToRegisterNumber (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_SP); + + // When writing a register value down to memory, the register info used + // to write memory just needs to have the correct size of a 32 bit register, + // the actual register it pertains to is not important, just the size needs + // to be correct. Here we use "eax"... + const RegisterInfo *reg_info_32 = reg_ctx->GetRegisterInfoByName("eax"); + if (!reg_info_32) + return false; // TODO this should actually never happen + + // Make room for the argument(s) on the stack + + Error error; + RegisterValue reg_value; + + // Write any arguments onto the stack + if (arg1_ptr) + { + sp -= 4; + if (arg2_ptr) + { + sp -= 4; + if (arg3_ptr) + { + sp -= 4; + if (arg4_ptr) + { + sp -= 4; + if (arg5_ptr) + { + sp -= 4; + if (arg6_ptr) + { + sp -= 4; + } + } + } + } + } + } + + // Align the SP + sp &= ~(16ull-1ull); // 16-byte alignment + + if (arg1_ptr) + { + reg_value.SetUInt32(*arg1_ptr); + error = reg_ctx->WriteRegisterValueToMemory (reg_info_32, + sp, + reg_info_32->byte_size, + reg_value); + if (error.Fail()) + return false; + + if (arg2_ptr) + { + reg_value.SetUInt32(*arg2_ptr); + // The register info used to write memory just needs to have the correct + // size of a 32 bit register, the actual register it pertains to is not + // important, just the size needs to be correct. Here we use "eax"... + error = reg_ctx->WriteRegisterValueToMemory (reg_info_32, + sp + 4, + reg_info_32->byte_size, + reg_value); + if (error.Fail()) + return false; + + if (arg3_ptr) + { + reg_value.SetUInt32(*arg3_ptr); + // The register info used to write memory just needs to have the correct + // size of a 32 bit register, the actual register it pertains to is not + // important, just the size needs to be correct. Here we use "eax"... + error = reg_ctx->WriteRegisterValueToMemory (reg_info_32, + sp + 8, + reg_info_32->byte_size, + reg_value); + if (error.Fail()) + return false; + + if (arg4_ptr) + { + reg_value.SetUInt32(*arg4_ptr); + // The register info used to write memory just needs to have the correct + // size of a 32 bit register, the actual register it pertains to is not + // important, just the size needs to be correct. Here we use "eax"... + error = reg_ctx->WriteRegisterValueToMemory (reg_info_32, + sp + 12, + reg_info_32->byte_size, + reg_value); + if (error.Fail()) + return false; + if (arg5_ptr) + { + reg_value.SetUInt32(*arg5_ptr); + // The register info used to write memory just needs to have the correct + // size of a 32 bit register, the actual register it pertains to is not + // important, just the size needs to be correct. Here we use "eax"... + error = reg_ctx->WriteRegisterValueToMemory (reg_info_32, + sp + 16, + reg_info_32->byte_size, + reg_value); + if (error.Fail()) + return false; + if (arg6_ptr) + { + reg_value.SetUInt32(*arg6_ptr); + // The register info used to write memory just needs to have the correct + // size of a 32 bit register, the actual register it pertains to is not + // important, just the size needs to be correct. Here we use "eax"... + error = reg_ctx->WriteRegisterValueToMemory (reg_info_32, + sp + 20, + reg_info_32->byte_size, + reg_value); + if (error.Fail()) + return false; + } + } + } + } + } + } + + + // The return address is pushed onto the stack (yes after we just set the + // alignment above!). + sp -= 4; + reg_value.SetUInt32(return_addr); + error = reg_ctx->WriteRegisterValueToMemory (reg_info_32, + sp, + reg_info_32->byte_size, + reg_value); + if (error.Fail()) + return false; + + // %esp is set to the actual stack value. + + if (!reg_ctx->WriteRegisterFromUnsigned (sp_reg_num, sp)) + return false; + + // %eip is set to the address of the called function. + + if (!reg_ctx->WriteRegisterFromUnsigned (pc_reg_num, func_addr)) + return false; + + return true; +} + +bool +ABIMacOSX_i386::PrepareNormalCall (Thread &thread, + addr_t sp, + addr_t func_addr, + addr_t return_addr, + ValueList &args) const +{ + ExecutionContext exe_ctx (thread.shared_from_this()); + RegisterContext *reg_ctx = thread.GetRegisterContext().get(); + if (!reg_ctx) + return false; + + Process *process = exe_ctx.GetProcessPtr(); + Error error; + uint32_t fp_reg_num = reg_ctx->ConvertRegisterKindToRegisterNumber (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_FP); + uint32_t pc_reg_num = reg_ctx->ConvertRegisterKindToRegisterNumber (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC); + uint32_t sp_reg_num = reg_ctx->ConvertRegisterKindToRegisterNumber (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_SP); + + // Do the argument layout + + std::vector argLayout; // 4-byte chunks, as discussed in the ABI Function Call Guide + + size_t numArgs = args.GetSize(); + size_t index; + + for (index = 0; index < numArgs; ++index) + { + Value *val = args.GetValueAtIndex(index); + + if (!val) + return false; + + switch (val->GetValueType()) + { + case Value::eValueTypeScalar: + { + Scalar &scalar = val->GetScalar(); + switch (scalar.GetType()) + { + case Scalar::e_void: + return false; + case Scalar::e_sint: + case Scalar::e_uint: + case Scalar::e_slong: + case Scalar::e_ulong: + case Scalar::e_slonglong: + case Scalar::e_ulonglong: + { + uint64_t data = scalar.ULongLong(); + + switch (scalar.GetByteSize()) + { + default: + return false; + case 1: + argLayout.push_back((uint32_t)(data & 0xffull)); + break; + case 2: + argLayout.push_back((uint32_t)(data & 0xffffull)); + break; + case 4: + argLayout.push_back((uint32_t)(data & 0xffffffffull)); + break; + case 8: + argLayout.push_back((uint32_t)(data & 0xffffffffull)); + argLayout.push_back((uint32_t)(data >> 32)); + break; + } + } + break; + case Scalar::e_float: + { + float data = scalar.Float(); + uint32_t dataRaw = *((uint32_t*)(&data)); + argLayout.push_back(dataRaw); + } + break; + case Scalar::e_double: + { + double data = scalar.Double(); + uint32_t *dataRaw = ((uint32_t*)(&data)); + argLayout.push_back(dataRaw[0]); + argLayout.push_back(dataRaw[1]); + } + break; + case Scalar::e_long_double: + { + long double data = scalar.Double(); + uint32_t *dataRaw = ((uint32_t*)(&data)); + while ((argLayout.size() * 4) & 0xf) + argLayout.push_back(0); + argLayout.push_back(dataRaw[0]); + argLayout.push_back(dataRaw[1]); + argLayout.push_back(dataRaw[2]); + argLayout.push_back(dataRaw[3]); + } + break; + } + } + break; + case Value::eValueTypeHostAddress: + { + ClangASTType clang_type (val->GetClangType()); + if (clang_type) + { + uint32_t cstr_length = 0; + if (clang_type.IsCStringType (cstr_length)) + { + const char *cstr = (const char*)val->GetScalar().ULongLong(); + cstr_length = strlen(cstr); + + // Push the string onto the stack immediately. + + sp -= (cstr_length + 1); + + if (process->WriteMemory(sp, cstr, cstr_length + 1, error) != (cstr_length + 1)) + return false; + + // Put the address of the string into the argument array. + + argLayout.push_back((uint32_t)(sp & 0xffffffff)); + } + else + { + return false; + } + } + break; + } + break; + case Value::eValueTypeFileAddress: + case Value::eValueTypeLoadAddress: + default: + return false; + } + } + + // Make room for the arguments on the stack + + sp -= 4 * argLayout.size(); + + // Align the SP + + sp &= ~(16ull-1ull); // 16-byte alignment + + // Write the arguments on the stack + + size_t numChunks = argLayout.size(); + + for (index = 0; index < numChunks; ++index) + if (process->WriteMemory(sp + (index * 4), &argLayout[index], sizeof(uint32_t), error) != sizeof(uint32_t)) + return false; + + // The return address is pushed onto the stack. + + sp -= 4; + uint32_t returnAddressU32 = return_addr; + if (process->WriteMemory (sp, &returnAddressU32, sizeof(returnAddressU32), error) != sizeof(returnAddressU32)) + return false; + + // %esp is set to the actual stack value. + + if (!reg_ctx->WriteRegisterFromUnsigned(sp_reg_num, sp)) + return false; + + // %ebp is set to a fake value, in our case 0x0x00000000 + + if (!reg_ctx->WriteRegisterFromUnsigned(fp_reg_num, 0x00000000)) + return false; + + // %eip is set to the address of the called function. + + if (!reg_ctx->WriteRegisterFromUnsigned(pc_reg_num, func_addr)) + return false; + + return true; +} + +static bool +ReadIntegerArgument (Scalar &scalar, + unsigned int bit_width, + bool is_signed, + Process *process, + addr_t ¤t_stack_argument) +{ + + uint32_t byte_size = (bit_width + (8-1))/8; + Error error; + if (process->ReadScalarIntegerFromMemory(current_stack_argument, byte_size, is_signed, scalar, error)) + { + current_stack_argument += byte_size; + return true; + } + return false; +} + +bool +ABIMacOSX_i386::GetArgumentValues (Thread &thread, + ValueList &values) const +{ + unsigned int num_values = values.GetSize(); + unsigned int value_index; + + // Get the pointer to the first stack argument so we have a place to start + // when reading data + + RegisterContext *reg_ctx = thread.GetRegisterContext().get(); + + if (!reg_ctx) + return false; + + addr_t sp = reg_ctx->GetSP(0); + + if (!sp) + return false; + + addr_t current_stack_argument = sp + 4; // jump over return address + + for (value_index = 0; + value_index < num_values; + ++value_index) + { + Value *value = values.GetValueAtIndex(value_index); + + if (!value) + return false; + + // We currently only support extracting values with Clang QualTypes. + // Do we care about others? + ClangASTType clang_type (value->GetClangType()); + if (clang_type) + { + bool is_signed; + + if (clang_type.IsIntegerType (is_signed)) + { + ReadIntegerArgument(value->GetScalar(), + clang_type.GetBitSize(), + is_signed, + thread.GetProcess().get(), + current_stack_argument); + } + else if (clang_type.IsPointerType()) + { + ReadIntegerArgument(value->GetScalar(), + clang_type.GetBitSize(), + false, + thread.GetProcess().get(), + current_stack_argument); + } + } + } + + return true; +} + +Error +ABIMacOSX_i386::SetReturnValueObject(lldb::StackFrameSP &frame_sp, lldb::ValueObjectSP &new_value_sp) +{ + Error error; + if (!new_value_sp) + { + error.SetErrorString("Empty value object for return value."); + return error; + } + + ClangASTType clang_type = new_value_sp->GetClangType(); + if (!clang_type) + { + error.SetErrorString ("Null clang type for return value."); + return error; + } + + Thread *thread = frame_sp->GetThread().get(); + + bool is_signed; + uint32_t count; + bool is_complex; + + RegisterContext *reg_ctx = thread->GetRegisterContext().get(); + + bool set_it_simple = false; + if (clang_type.IsIntegerType (is_signed) || clang_type.IsPointerType()) + { + DataExtractor data; + size_t num_bytes = new_value_sp->GetData(data); + lldb::offset_t offset = 0; + if (num_bytes <= 8) + { + const RegisterInfo *eax_info = reg_ctx->GetRegisterInfoByName("eax", 0); + if (num_bytes <= 4) + { + uint32_t raw_value = data.GetMaxU32(&offset, num_bytes); + + if (reg_ctx->WriteRegisterFromUnsigned (eax_info, raw_value)) + set_it_simple = true; + } + else + { + uint32_t raw_value = data.GetMaxU32(&offset, 4); + + if (reg_ctx->WriteRegisterFromUnsigned (eax_info, raw_value)) + { + const RegisterInfo *edx_info = reg_ctx->GetRegisterInfoByName("edx", 0); + uint32_t raw_value = data.GetMaxU32(&offset, num_bytes - offset); + + if (reg_ctx->WriteRegisterFromUnsigned (edx_info, raw_value)) + set_it_simple = true; + } + } + } + else + { + error.SetErrorString("We don't support returning longer than 64 bit integer values at present."); + } + } + else if (clang_type.IsFloatingPointType (count, is_complex)) + { + if (is_complex) + error.SetErrorString ("We don't support returning complex values at present"); + else + error.SetErrorString ("We don't support returning float values at present"); + } + + if (!set_it_simple) + error.SetErrorString ("We only support setting simple integer return types at present."); + + return error; +} + +ValueObjectSP +ABIMacOSX_i386::GetReturnValueObjectImpl (Thread &thread, + ClangASTType &clang_type) const +{ + Value value; + ValueObjectSP return_valobj_sp; + + if (!clang_type) + return return_valobj_sp; + + //value.SetContext (Value::eContextTypeClangType, clang_type.GetOpaqueQualType()); + value.SetClangType (clang_type); + + RegisterContext *reg_ctx = thread.GetRegisterContext().get(); + if (!reg_ctx) + return return_valobj_sp; + + bool is_signed; + + if (clang_type.IsIntegerType (is_signed)) + { + size_t bit_width = clang_type.GetBitSize(); + + unsigned eax_id = reg_ctx->GetRegisterInfoByName("eax", 0)->kinds[eRegisterKindLLDB]; + unsigned edx_id = reg_ctx->GetRegisterInfoByName("edx", 0)->kinds[eRegisterKindLLDB]; + + switch (bit_width) + { + default: + case 128: + // Scalar can't hold 128-bit literals, so we don't handle this + return return_valobj_sp; + case 64: + uint64_t raw_value; + raw_value = thread.GetRegisterContext()->ReadRegisterAsUnsigned(eax_id, 0) & 0xffffffff; + raw_value |= (thread.GetRegisterContext()->ReadRegisterAsUnsigned(edx_id, 0) & 0xffffffff) << 32; + if (is_signed) + value.GetScalar() = (int64_t)raw_value; + else + value.GetScalar() = (uint64_t)raw_value; + break; + case 32: + if (is_signed) + value.GetScalar() = (int32_t)(thread.GetRegisterContext()->ReadRegisterAsUnsigned(eax_id, 0) & 0xffffffff); + else + value.GetScalar() = (uint32_t)(thread.GetRegisterContext()->ReadRegisterAsUnsigned(eax_id, 0) & 0xffffffff); + break; + case 16: + if (is_signed) + value.GetScalar() = (int16_t)(thread.GetRegisterContext()->ReadRegisterAsUnsigned(eax_id, 0) & 0xffff); + else + value.GetScalar() = (uint16_t)(thread.GetRegisterContext()->ReadRegisterAsUnsigned(eax_id, 0) & 0xffff); + break; + case 8: + if (is_signed) + value.GetScalar() = (int8_t)(thread.GetRegisterContext()->ReadRegisterAsUnsigned(eax_id, 0) & 0xff); + else + value.GetScalar() = (uint8_t)(thread.GetRegisterContext()->ReadRegisterAsUnsigned(eax_id, 0) & 0xff); + break; + } + } + else if (clang_type.IsPointerType ()) + { + unsigned eax_id = reg_ctx->GetRegisterInfoByName("eax", 0)->kinds[eRegisterKindLLDB]; + uint32_t ptr = thread.GetRegisterContext()->ReadRegisterAsUnsigned(eax_id, 0) & 0xffffffff; + value.GetScalar() = ptr; + } + else + { + // not handled yet + return return_valobj_sp; + } + + // If we get here, we have a valid Value, so make our ValueObject out of it: + + return_valobj_sp = ValueObjectConstResult::Create(thread.GetStackFrameAtIndex(0).get(), + value, + ConstString("")); + return return_valobj_sp; +} + +bool +ABIMacOSX_i386::CreateFunctionEntryUnwindPlan (UnwindPlan &unwind_plan) +{ + uint32_t reg_kind = unwind_plan.GetRegisterKind(); + uint32_t sp_reg_num = LLDB_INVALID_REGNUM; + uint32_t pc_reg_num = LLDB_INVALID_REGNUM; + + switch (reg_kind) + { + case eRegisterKindDWARF: + sp_reg_num = dwarf_esp; + pc_reg_num = dwarf_eip; + break; + + case eRegisterKindGCC: + sp_reg_num = gcc_esp; + pc_reg_num = gcc_eip; + break; + + case eRegisterKindGDB: + sp_reg_num = gdb_esp; + pc_reg_num = gdb_eip; + break; + + case eRegisterKindGeneric: + sp_reg_num = LLDB_REGNUM_GENERIC_SP; + pc_reg_num = LLDB_REGNUM_GENERIC_PC; + break; + } + + if (sp_reg_num == LLDB_INVALID_REGNUM || + pc_reg_num == LLDB_INVALID_REGNUM) + return false; + + UnwindPlan::RowSP row(new UnwindPlan::Row); + row->SetCFARegister (sp_reg_num); + row->SetCFAOffset (4); + row->SetRegisterLocationToAtCFAPlusOffset(pc_reg_num, -4, false); + unwind_plan.AppendRow (row); + unwind_plan.SetSourceName ("i386 at-func-entry default"); + unwind_plan.SetSourcedFromCompiler (eLazyBoolNo); + return true; +} + +bool +ABIMacOSX_i386::CreateDefaultUnwindPlan (UnwindPlan &unwind_plan) +{ + uint32_t fp_reg_num = dwarf_ebp; + uint32_t sp_reg_num = dwarf_esp; + uint32_t pc_reg_num = dwarf_eip; + + UnwindPlan::RowSP row(new UnwindPlan::Row); + const int32_t ptr_size = 4; + + unwind_plan.Clear (); + unwind_plan.SetRegisterKind (eRegisterKindDWARF); + row->SetCFARegister (fp_reg_num); + row->SetCFAOffset (2 * ptr_size); + row->SetOffset (0); + + row->SetRegisterLocationToAtCFAPlusOffset(fp_reg_num, ptr_size * -2, true); + row->SetRegisterLocationToAtCFAPlusOffset(pc_reg_num, ptr_size * -1, true); + row->SetRegisterLocationToAtCFAPlusOffset(sp_reg_num, ptr_size * 0, true); + + unwind_plan.AppendRow (row); + unwind_plan.SetSourceName ("i386 default unwind plan"); + unwind_plan.SetSourcedFromCompiler (eLazyBoolNo); + unwind_plan.SetUnwindPlanValidAtAllInstructions (eLazyBoolNo); + return true; +} + +bool +ABIMacOSX_i386::RegisterIsVolatile (const RegisterInfo *reg_info) +{ + return !RegisterIsCalleeSaved (reg_info); +} + +// v. http://developer.apple.com/library/mac/#documentation/developertools/Conceptual/LowLevelABI/130-IA-32_Function_Calling_Conventions/IA32.html#//apple_ref/doc/uid/TP40002492-SW4 + +bool +ABIMacOSX_i386::RegisterIsCalleeSaved (const RegisterInfo *reg_info) +{ + if (reg_info) + { + // Saved registers are ebx, ebp, esi, edi, esp, eip + const char *name = reg_info->name; + if (name[0] == 'e') + { + switch (name[1]) + { + case 'b': + if (name[2] == 'x' || name[2] == 'p') + return name[3] == '\0'; + break; + case 'd': + if (name[2] == 'i') + return name[3] == '\0'; + break; + case 'i': + if (name[2] == 'p') + return name[3] == '\0'; + break; + case 's': + if (name[2] == 'i' || name[2] == 'p') + return name[3] == '\0'; + break; + } + } + if (name[0] == 's' && name[1] == 'p' && name[2] == '\0') // sp + return true; + if (name[0] == 'f' && name[1] == 'p' && name[2] == '\0') // fp + return true; + if (name[0] == 'p' && name[1] == 'c' && name[2] == '\0') // pc + return true; + } + return false; +} + +void +ABIMacOSX_i386::Initialize() +{ + PluginManager::RegisterPlugin (GetPluginNameStatic(), + "Mac OS X ABI for i386 targets", + CreateInstance); +} + +void +ABIMacOSX_i386::Terminate() +{ + PluginManager::UnregisterPlugin (CreateInstance); +} + +lldb_private::ConstString +ABIMacOSX_i386::GetPluginNameStatic () +{ + static ConstString g_short_name("abi.macosx-i386"); + return g_short_name; + +} + +//------------------------------------------------------------------ +// PluginInterface protocol +//------------------------------------------------------------------ +lldb_private::ConstString +ABIMacOSX_i386::GetPluginName() +{ + return GetPluginNameStatic(); +} + +uint32_t +ABIMacOSX_i386::GetPluginVersion() +{ + return 1; +} + diff --git a/contrib/llvm/tools/lldb/source/Plugins/ABI/MacOSX-i386/ABIMacOSX_i386.h b/contrib/llvm/tools/lldb/source/Plugins/ABI/MacOSX-i386/ABIMacOSX_i386.h new file mode 100644 index 00000000000..8c2d945e634 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Plugins/ABI/MacOSX-i386/ABIMacOSX_i386.h @@ -0,0 +1,139 @@ +//===-- ABIMacOSX_i386.h ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ABIMacOSX_i386_h_ +#define liblldb_ABIMacOSX_i386_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private.h" +#include "lldb/Target/ABI.h" +#include "lldb/Core/Value.h" + +class ABIMacOSX_i386 : + public lldb_private::ABI +{ +public: + + ~ABIMacOSX_i386() { } + + virtual size_t + GetRedZoneSize () const; + + virtual bool + PrepareTrivialCall (lldb_private::Thread &thread, + lldb::addr_t sp, + lldb::addr_t func_addr, + lldb::addr_t return_addr, + lldb::addr_t *arg1_ptr = NULL, + lldb::addr_t *arg2_ptr = NULL, + lldb::addr_t *arg3_ptr = NULL, + lldb::addr_t *arg4_ptr = NULL, + lldb::addr_t *arg5_ptr = NULL, + lldb::addr_t *arg6_ptr = NULL) const; + + virtual bool + PrepareNormalCall (lldb_private::Thread &thread, + lldb::addr_t sp, + lldb::addr_t func_addr, + lldb::addr_t return_addr, + lldb_private::ValueList &args) const; + + virtual bool + GetArgumentValues (lldb_private::Thread &thread, + lldb_private::ValueList &values) const; + + virtual lldb_private::Error + SetReturnValueObject(lldb::StackFrameSP &frame_sp, lldb::ValueObjectSP &new_value); + +protected: + virtual lldb::ValueObjectSP + GetReturnValueObjectImpl (lldb_private::Thread &thread, + lldb_private::ClangASTType &ast_type) const; + +public: + + virtual bool + CreateFunctionEntryUnwindPlan (lldb_private::UnwindPlan &unwind_plan); + + virtual bool + CreateDefaultUnwindPlan (lldb_private::UnwindPlan &unwind_plan); + + virtual bool + RegisterIsVolatile (const lldb_private::RegisterInfo *reg_info); + + virtual bool + StackUsesFrames () + { + return true; + } + + virtual bool + CallFrameAddressIsValid (lldb::addr_t cfa) + { + // Make sure the stack call frame addresses are are 8 byte aligned + if (cfa & (8ull - 1ull)) + return false; // Not 8 byte aligned + if (cfa == 0) + return false; // Zero is not a valid stack address + return true; + } + + virtual bool + CodeAddressIsValid (lldb::addr_t pc) + { + // Just make sure the address is a valid 32 bit address. + return pc <= UINT32_MAX; + } + + virtual bool + FunctionCallsChangeCFA () + { + return true; + } + + virtual const lldb_private::RegisterInfo * + GetRegisterInfoArray (uint32_t &count); + + //------------------------------------------------------------------ + // Static Functions + //------------------------------------------------------------------ + static void + Initialize(); + + static void + Terminate(); + + static lldb::ABISP + CreateInstance (const lldb_private::ArchSpec &arch); + + //------------------------------------------------------------------ + // PluginInterface protocol + //------------------------------------------------------------------ + static lldb_private::ConstString + GetPluginNameStatic (); + + virtual lldb_private::ConstString + GetPluginName(); + + virtual uint32_t + GetPluginVersion(); + +protected: + bool + RegisterIsCalleeSaved (const lldb_private::RegisterInfo *reg_info); + +private: + ABIMacOSX_i386() : lldb_private::ABI() { } // Call CreateInstance instead. +}; + + +#endif // liblldb_ABI_h_ diff --git a/contrib/llvm/tools/lldb/source/Plugins/ABI/SysV-x86_64/ABISysV_x86_64.cpp b/contrib/llvm/tools/lldb/source/Plugins/ABI/SysV-x86_64/ABISysV_x86_64.cpp new file mode 100644 index 00000000000..a904d8b649c --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Plugins/ABI/SysV-x86_64/ABISysV_x86_64.cpp @@ -0,0 +1,1288 @@ +//===-- ABISysV_x86_64.cpp --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "ABISysV_x86_64.h" + +#include "lldb/Core/ConstString.h" +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/Error.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/RegisterValue.h" +#include "lldb/Core/Value.h" +#include "lldb/Core/ValueObjectConstResult.h" +#include "lldb/Core/ValueObjectRegister.h" +#include "lldb/Core/ValueObjectMemory.h" +#include "lldb/Symbol/ClangASTContext.h" +#include "lldb/Symbol/UnwindPlan.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/StackFrame.h" +#include "lldb/Target/Thread.h" + +#include "llvm/ADT/Triple.h" + +using namespace lldb; +using namespace lldb_private; + +enum gcc_dwarf_regnums +{ + gcc_dwarf_rax = 0, + gcc_dwarf_rdx, + gcc_dwarf_rcx, + gcc_dwarf_rbx, + gcc_dwarf_rsi, + gcc_dwarf_rdi, + gcc_dwarf_rbp, + gcc_dwarf_rsp, + gcc_dwarf_r8, + gcc_dwarf_r9, + gcc_dwarf_r10, + gcc_dwarf_r11, + gcc_dwarf_r12, + gcc_dwarf_r13, + gcc_dwarf_r14, + gcc_dwarf_r15, + gcc_dwarf_rip, + gcc_dwarf_xmm0, + gcc_dwarf_xmm1, + gcc_dwarf_xmm2, + gcc_dwarf_xmm3, + gcc_dwarf_xmm4, + gcc_dwarf_xmm5, + gcc_dwarf_xmm6, + gcc_dwarf_xmm7, + gcc_dwarf_xmm8, + gcc_dwarf_xmm9, + gcc_dwarf_xmm10, + gcc_dwarf_xmm11, + gcc_dwarf_xmm12, + gcc_dwarf_xmm13, + gcc_dwarf_xmm14, + gcc_dwarf_xmm15, + gcc_dwarf_stmm0, + gcc_dwarf_stmm1, + gcc_dwarf_stmm2, + gcc_dwarf_stmm3, + gcc_dwarf_stmm4, + gcc_dwarf_stmm5, + gcc_dwarf_stmm6, + gcc_dwarf_stmm7, + gcc_dwarf_ymm0, + gcc_dwarf_ymm1, + gcc_dwarf_ymm2, + gcc_dwarf_ymm3, + gcc_dwarf_ymm4, + gcc_dwarf_ymm5, + gcc_dwarf_ymm6, + gcc_dwarf_ymm7, + gcc_dwarf_ymm8, + gcc_dwarf_ymm9, + gcc_dwarf_ymm10, + gcc_dwarf_ymm11, + gcc_dwarf_ymm12, + gcc_dwarf_ymm13, + gcc_dwarf_ymm14, + gcc_dwarf_ymm15 +}; + +enum gdb_regnums +{ + gdb_rax = 0, + gdb_rbx = 1, + gdb_rcx = 2, + gdb_rdx = 3, + gdb_rsi = 4, + gdb_rdi = 5, + gdb_rbp = 6, + gdb_rsp = 7, + gdb_r8 = 8, + gdb_r9 = 9, + gdb_r10 = 10, + gdb_r11 = 11, + gdb_r12 = 12, + gdb_r13 = 13, + gdb_r14 = 14, + gdb_r15 = 15, + gdb_rip = 16, + gdb_rflags = 17, + gdb_cs = 18, + gdb_ss = 19, + gdb_ds = 20, + gdb_es = 21, + gdb_fs = 22, + gdb_gs = 23, + gdb_stmm0 = 24, + gdb_stmm1 = 25, + gdb_stmm2 = 26, + gdb_stmm3 = 27, + gdb_stmm4 = 28, + gdb_stmm5 = 29, + gdb_stmm6 = 30, + gdb_stmm7 = 31, + gdb_fctrl = 32, gdb_fcw = gdb_fctrl, + gdb_fstat = 33, gdb_fsw = gdb_fstat, + gdb_ftag = 34, gdb_ftw = gdb_ftag, + gdb_fiseg = 35, gdb_fpu_cs = gdb_fiseg, + gdb_fioff = 36, gdb_ip = gdb_fioff, + gdb_foseg = 37, gdb_fpu_ds = gdb_foseg, + gdb_fooff = 38, gdb_dp = gdb_fooff, + gdb_fop = 39, + gdb_xmm0 = 40, + gdb_xmm1 = 41, + gdb_xmm2 = 42, + gdb_xmm3 = 43, + gdb_xmm4 = 44, + gdb_xmm5 = 45, + gdb_xmm6 = 46, + gdb_xmm7 = 47, + gdb_xmm8 = 48, + gdb_xmm9 = 49, + gdb_xmm10 = 50, + gdb_xmm11 = 51, + gdb_xmm12 = 52, + gdb_xmm13 = 53, + gdb_xmm14 = 54, + gdb_xmm15 = 55, + gdb_mxcsr = 56, + gdb_ymm0 = 57, + gdb_ymm1 = 58, + gdb_ymm2 = 59, + gdb_ymm3 = 60, + gdb_ymm4 = 61, + gdb_ymm5 = 62, + gdb_ymm6 = 63, + gdb_ymm7 = 64, + gdb_ymm8 = 65, + gdb_ymm9 = 66, + gdb_ymm10 = 67, + gdb_ymm11 = 68, + gdb_ymm12 = 69, + gdb_ymm13 = 70, + gdb_ymm14 = 71, + gdb_ymm15 = 72 +}; + + +static RegisterInfo g_register_infos[] = +{ + // NAME ALT SZ OFF ENCODING FORMAT COMPILER DWARF GENERIC GDB LLDB NATIVE VALUE REGS INVALIDATE REGS + // ======== ======= == === ============= =================== ======================= ===================== =========================== ===================== ====================== ========== =============== + { "rax" , NULL, 8, 0, eEncodingUint , eFormatHex , { gcc_dwarf_rax , gcc_dwarf_rax , LLDB_INVALID_REGNUM , gdb_rax , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "rbx" , NULL, 8, 0, eEncodingUint , eFormatHex , { gcc_dwarf_rbx , gcc_dwarf_rbx , LLDB_INVALID_REGNUM , gdb_rbx , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "rcx" , "arg4", 8, 0, eEncodingUint , eFormatHex , { gcc_dwarf_rcx , gcc_dwarf_rcx , LLDB_REGNUM_GENERIC_ARG4 , gdb_rcx , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "rdx" , "arg3", 8, 0, eEncodingUint , eFormatHex , { gcc_dwarf_rdx , gcc_dwarf_rdx , LLDB_REGNUM_GENERIC_ARG3 , gdb_rdx , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "rsi" , "arg2", 8, 0, eEncodingUint , eFormatHex , { gcc_dwarf_rsi , gcc_dwarf_rsi , LLDB_REGNUM_GENERIC_ARG2 , gdb_rsi , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "rdi" , "arg1", 8, 0, eEncodingUint , eFormatHex , { gcc_dwarf_rdi , gcc_dwarf_rdi , LLDB_REGNUM_GENERIC_ARG1 , gdb_rdi , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "rbp" , "fp", 8, 0, eEncodingUint , eFormatHex , { gcc_dwarf_rbp , gcc_dwarf_rbp , LLDB_REGNUM_GENERIC_FP , gdb_rbp , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "rsp" , "sp", 8, 0, eEncodingUint , eFormatHex , { gcc_dwarf_rsp , gcc_dwarf_rsp , LLDB_REGNUM_GENERIC_SP , gdb_rsp , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "r8" , "arg5", 8, 0, eEncodingUint , eFormatHex , { gcc_dwarf_r8 , gcc_dwarf_r8 , LLDB_REGNUM_GENERIC_ARG5 , gdb_r8 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "r9" , "arg6", 8, 0, eEncodingUint , eFormatHex , { gcc_dwarf_r9 , gcc_dwarf_r9 , LLDB_REGNUM_GENERIC_ARG6 , gdb_r9 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "r10" , NULL, 8, 0, eEncodingUint , eFormatHex , { gcc_dwarf_r10 , gcc_dwarf_r10 , LLDB_INVALID_REGNUM , gdb_r10 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "r11" , NULL, 8, 0, eEncodingUint , eFormatHex , { gcc_dwarf_r11 , gcc_dwarf_r11 , LLDB_INVALID_REGNUM , gdb_r11 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "r12" , NULL, 8, 0, eEncodingUint , eFormatHex , { gcc_dwarf_r12 , gcc_dwarf_r12 , LLDB_INVALID_REGNUM , gdb_r12 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "r13" , NULL, 8, 0, eEncodingUint , eFormatHex , { gcc_dwarf_r13 , gcc_dwarf_r13 , LLDB_INVALID_REGNUM , gdb_r13 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "r14" , NULL, 8, 0, eEncodingUint , eFormatHex , { gcc_dwarf_r14 , gcc_dwarf_r14 , LLDB_INVALID_REGNUM , gdb_r14 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "r15" , NULL, 8, 0, eEncodingUint , eFormatHex , { gcc_dwarf_r15 , gcc_dwarf_r15 , LLDB_INVALID_REGNUM , gdb_r15 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "rip" , "pc", 8, 0, eEncodingUint , eFormatHex , { gcc_dwarf_rip , gcc_dwarf_rip , LLDB_REGNUM_GENERIC_PC , gdb_rip , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "rflags", NULL, 4, 0, eEncodingUint , eFormatHex , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_REGNUM_GENERIC_FLAGS , gdb_rflags , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "cs" , NULL, 4, 0, eEncodingUint , eFormatHex , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_cs , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "ss" , NULL, 4, 0, eEncodingUint , eFormatHex , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_ss , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "ds" , NULL, 4, 0, eEncodingUint , eFormatHex , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_ds , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "es" , NULL, 4, 0, eEncodingUint , eFormatHex , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_es , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "fs" , NULL, 4, 0, eEncodingUint , eFormatHex , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_fs , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "gs" , NULL, 4, 0, eEncodingUint , eFormatHex , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_gs , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "stmm0" , NULL, 10, 0, eEncodingVector, eFormatVectorOfUInt8, { gcc_dwarf_stmm0 , gcc_dwarf_stmm0 , LLDB_INVALID_REGNUM , gdb_stmm0 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "stmm1" , NULL, 10, 0, eEncodingVector, eFormatVectorOfUInt8, { gcc_dwarf_stmm1 , gcc_dwarf_stmm1 , LLDB_INVALID_REGNUM , gdb_stmm1 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "stmm2" , NULL, 10, 0, eEncodingVector, eFormatVectorOfUInt8, { gcc_dwarf_stmm2 , gcc_dwarf_stmm2 , LLDB_INVALID_REGNUM , gdb_stmm2 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "stmm3" , NULL, 10, 0, eEncodingVector, eFormatVectorOfUInt8, { gcc_dwarf_stmm3 , gcc_dwarf_stmm3 , LLDB_INVALID_REGNUM , gdb_stmm3 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "stmm4" , NULL, 10, 0, eEncodingVector, eFormatVectorOfUInt8, { gcc_dwarf_stmm4 , gcc_dwarf_stmm4 , LLDB_INVALID_REGNUM , gdb_stmm4 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "stmm5" , NULL, 10, 0, eEncodingVector, eFormatVectorOfUInt8, { gcc_dwarf_stmm5 , gcc_dwarf_stmm5 , LLDB_INVALID_REGNUM , gdb_stmm5 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "stmm6" , NULL, 10, 0, eEncodingVector, eFormatVectorOfUInt8, { gcc_dwarf_stmm6 , gcc_dwarf_stmm6 , LLDB_INVALID_REGNUM , gdb_stmm6 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "stmm7" , NULL, 10, 0, eEncodingVector, eFormatVectorOfUInt8, { gcc_dwarf_stmm7 , gcc_dwarf_stmm7 , LLDB_INVALID_REGNUM , gdb_stmm7 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "fctrl" , NULL, 4, 0, eEncodingUint , eFormatHex , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_fctrl , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "fstat" , NULL, 4, 0, eEncodingUint , eFormatHex , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_fstat , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "ftag" , NULL, 4, 0, eEncodingUint , eFormatHex , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_ftag , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "fiseg" , NULL, 4, 0, eEncodingUint , eFormatHex , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_fiseg , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "fioff" , NULL, 4, 0, eEncodingUint , eFormatHex , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_fioff , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "foseg" , NULL, 4, 0, eEncodingUint , eFormatHex , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_foseg , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "fooff" , NULL, 4, 0, eEncodingUint , eFormatHex , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_fooff , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "fop" , NULL, 4, 0, eEncodingUint , eFormatHex , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_fop , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "xmm0" , NULL, 16, 0, eEncodingVector, eFormatVectorOfUInt8, { gcc_dwarf_xmm0 , gcc_dwarf_xmm0 , LLDB_INVALID_REGNUM , gdb_xmm0 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "xmm1" , NULL, 16, 0, eEncodingVector, eFormatVectorOfUInt8, { gcc_dwarf_xmm1 , gcc_dwarf_xmm1 , LLDB_INVALID_REGNUM , gdb_xmm1 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "xmm2" , NULL, 16, 0, eEncodingVector, eFormatVectorOfUInt8, { gcc_dwarf_xmm2 , gcc_dwarf_xmm2 , LLDB_INVALID_REGNUM , gdb_xmm2 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "xmm3" , NULL, 16, 0, eEncodingVector, eFormatVectorOfUInt8, { gcc_dwarf_xmm3 , gcc_dwarf_xmm3 , LLDB_INVALID_REGNUM , gdb_xmm3 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "xmm4" , NULL, 16, 0, eEncodingVector, eFormatVectorOfUInt8, { gcc_dwarf_xmm4 , gcc_dwarf_xmm4 , LLDB_INVALID_REGNUM , gdb_xmm4 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "xmm5" , NULL, 16, 0, eEncodingVector, eFormatVectorOfUInt8, { gcc_dwarf_xmm5 , gcc_dwarf_xmm5 , LLDB_INVALID_REGNUM , gdb_xmm5 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "xmm6" , NULL, 16, 0, eEncodingVector, eFormatVectorOfUInt8, { gcc_dwarf_xmm6 , gcc_dwarf_xmm6 , LLDB_INVALID_REGNUM , gdb_xmm6 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "xmm7" , NULL, 16, 0, eEncodingVector, eFormatVectorOfUInt8, { gcc_dwarf_xmm7 , gcc_dwarf_xmm7 , LLDB_INVALID_REGNUM , gdb_xmm7 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "xmm8" , NULL, 16, 0, eEncodingVector, eFormatVectorOfUInt8, { gcc_dwarf_xmm8 , gcc_dwarf_xmm8 , LLDB_INVALID_REGNUM , gdb_xmm8 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "xmm9" , NULL, 16, 0, eEncodingVector, eFormatVectorOfUInt8, { gcc_dwarf_xmm9 , gcc_dwarf_xmm9 , LLDB_INVALID_REGNUM , gdb_xmm9 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "xmm10" , NULL, 16, 0, eEncodingVector, eFormatVectorOfUInt8, { gcc_dwarf_xmm10 , gcc_dwarf_xmm10 , LLDB_INVALID_REGNUM , gdb_xmm10 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "xmm11" , NULL, 16, 0, eEncodingVector, eFormatVectorOfUInt8, { gcc_dwarf_xmm11 , gcc_dwarf_xmm11 , LLDB_INVALID_REGNUM , gdb_xmm11 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "xmm12" , NULL, 16, 0, eEncodingVector, eFormatVectorOfUInt8, { gcc_dwarf_xmm12 , gcc_dwarf_xmm12 , LLDB_INVALID_REGNUM , gdb_xmm12 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "xmm13" , NULL, 16, 0, eEncodingVector, eFormatVectorOfUInt8, { gcc_dwarf_xmm13 , gcc_dwarf_xmm13 , LLDB_INVALID_REGNUM , gdb_xmm13 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "xmm14" , NULL, 16, 0, eEncodingVector, eFormatVectorOfUInt8, { gcc_dwarf_xmm14 , gcc_dwarf_xmm14 , LLDB_INVALID_REGNUM , gdb_xmm14 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "xmm15" , NULL, 16, 0, eEncodingVector, eFormatVectorOfUInt8, { gcc_dwarf_xmm15 , gcc_dwarf_xmm15 , LLDB_INVALID_REGNUM , gdb_xmm15 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "mxcsr" , NULL, 4, 0, eEncodingUint , eFormatHex , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_mxcsr , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "ymm0" , NULL, 32, 0, eEncodingVector, eFormatVectorOfUInt8, { gcc_dwarf_ymm0 , gcc_dwarf_ymm0 , LLDB_INVALID_REGNUM , gdb_ymm0 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "ymm1" , NULL, 32, 0, eEncodingVector, eFormatVectorOfUInt8, { gcc_dwarf_ymm1 , gcc_dwarf_ymm1 , LLDB_INVALID_REGNUM , gdb_ymm1 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "ymm2" , NULL, 32, 0, eEncodingVector, eFormatVectorOfUInt8, { gcc_dwarf_ymm2 , gcc_dwarf_ymm2 , LLDB_INVALID_REGNUM , gdb_ymm2 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "ymm3" , NULL, 32, 0, eEncodingVector, eFormatVectorOfUInt8, { gcc_dwarf_ymm3 , gcc_dwarf_ymm3 , LLDB_INVALID_REGNUM , gdb_ymm3 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "ymm4" , NULL, 32, 0, eEncodingVector, eFormatVectorOfUInt8, { gcc_dwarf_ymm4 , gcc_dwarf_ymm4 , LLDB_INVALID_REGNUM , gdb_ymm4 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "ymm5" , NULL, 32, 0, eEncodingVector, eFormatVectorOfUInt8, { gcc_dwarf_ymm5 , gcc_dwarf_ymm5 , LLDB_INVALID_REGNUM , gdb_ymm5 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "ymm6" , NULL, 32, 0, eEncodingVector, eFormatVectorOfUInt8, { gcc_dwarf_ymm6 , gcc_dwarf_ymm6 , LLDB_INVALID_REGNUM , gdb_ymm6 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "ymm7" , NULL, 32, 0, eEncodingVector, eFormatVectorOfUInt8, { gcc_dwarf_ymm7 , gcc_dwarf_ymm7 , LLDB_INVALID_REGNUM , gdb_ymm7 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "ymm8" , NULL, 32, 0, eEncodingVector, eFormatVectorOfUInt8, { gcc_dwarf_ymm8 , gcc_dwarf_ymm8 , LLDB_INVALID_REGNUM , gdb_ymm8 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "ymm9" , NULL, 32, 0, eEncodingVector, eFormatVectorOfUInt8, { gcc_dwarf_ymm9 , gcc_dwarf_ymm9 , LLDB_INVALID_REGNUM , gdb_ymm9 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "ymm10" , NULL, 32, 0, eEncodingVector, eFormatVectorOfUInt8, { gcc_dwarf_ymm10 , gcc_dwarf_ymm10 , LLDB_INVALID_REGNUM , gdb_ymm10 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "ymm11" , NULL, 32, 0, eEncodingVector, eFormatVectorOfUInt8, { gcc_dwarf_ymm11 , gcc_dwarf_ymm11 , LLDB_INVALID_REGNUM , gdb_ymm11 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "ymm12" , NULL, 32, 0, eEncodingVector, eFormatVectorOfUInt8, { gcc_dwarf_ymm12 , gcc_dwarf_ymm12 , LLDB_INVALID_REGNUM , gdb_ymm12 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "ymm13" , NULL, 32, 0, eEncodingVector, eFormatVectorOfUInt8, { gcc_dwarf_ymm13 , gcc_dwarf_ymm13 , LLDB_INVALID_REGNUM , gdb_ymm13 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "ymm14" , NULL, 32, 0, eEncodingVector, eFormatVectorOfUInt8, { gcc_dwarf_ymm14 , gcc_dwarf_ymm14 , LLDB_INVALID_REGNUM , gdb_ymm14 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "ymm15" , NULL, 32, 0, eEncodingVector, eFormatVectorOfUInt8, { gcc_dwarf_ymm15 , gcc_dwarf_ymm15 , LLDB_INVALID_REGNUM , gdb_ymm15 , LLDB_INVALID_REGNUM }, NULL, NULL} +}; + +static const uint32_t k_num_register_infos = sizeof(g_register_infos)/sizeof(RegisterInfo); +static bool g_register_info_names_constified = false; + +const lldb_private::RegisterInfo * +ABISysV_x86_64::GetRegisterInfoArray (uint32_t &count) +{ + // Make the C-string names and alt_names for the register infos into const + // C-string values by having the ConstString unique the names in the global + // constant C-string pool. + if (!g_register_info_names_constified) + { + g_register_info_names_constified = true; + for (uint32_t i=0; iPutCString(s.GetString().c_str()); + } + + RegisterContext *reg_ctx = thread.GetRegisterContext().get(); + if (!reg_ctx) + return false; + + const RegisterInfo *reg_info = NULL; + if (arg1_ptr) + { + reg_info = reg_ctx->GetRegisterInfoByName("rdi", 0); + if (log) + log->Printf("About to write arg1 (0x%" PRIx64 ") into %s", (uint64_t)*arg1_ptr, reg_info->name); + + if (!reg_ctx->WriteRegisterFromUnsigned (reg_info, *arg1_ptr)) + return false; + + if (arg2_ptr) + { + reg_info = reg_ctx->GetRegisterInfoByName("rsi", 0); + if (log) + log->Printf("About to write arg2 (0x%" PRIx64 ") into %s", (uint64_t)*arg2_ptr, reg_info->name); + if (!reg_ctx->WriteRegisterFromUnsigned (reg_info, *arg2_ptr)) + return false; + + if (arg3_ptr) + { + reg_info = reg_ctx->GetRegisterInfoByName("rdx", 0); + if (log) + log->Printf("About to write arg3 (0x%" PRIx64 ") into %s", (uint64_t)*arg3_ptr, reg_info->name); + if (!reg_ctx->WriteRegisterFromUnsigned (reg_info, *arg3_ptr)) + return false; + + if (arg4_ptr) + { + reg_info = reg_ctx->GetRegisterInfoByName("rcx", 0); + if (log) + log->Printf("About to write arg4 (0x%" PRIx64 ") into %s", (uint64_t)*arg4_ptr, reg_info->name); + if (!reg_ctx->WriteRegisterFromUnsigned (reg_info, *arg4_ptr)) + return false; + + if (arg5_ptr) + { + reg_info = reg_ctx->GetRegisterInfoByName("r8", 0); + if (log) + log->Printf("About to write arg5 (0x%" PRIx64 ") into %s", (uint64_t)*arg5_ptr, reg_info->name); + if (!reg_ctx->WriteRegisterFromUnsigned (reg_info, *arg5_ptr)) + return false; + + if (arg6_ptr) + { + reg_info = reg_ctx->GetRegisterInfoByName("r9", 0); + if (log) + log->Printf("About to write arg6 (0x%" PRIx64 ") into %s", (uint64_t)*arg6_ptr, reg_info->name); + if (!reg_ctx->WriteRegisterFromUnsigned (reg_info, *arg6_ptr)) + return false; + } + } + } + } + } + } + + + // First, align the SP + + if (log) + log->Printf("16-byte aligning SP: 0x%" PRIx64 " to 0x%" PRIx64, (uint64_t)sp, (uint64_t)(sp & ~0xfull)); + + sp &= ~(0xfull); // 16-byte alignment + + // The return address is pushed onto the stack (yes after the alignment...) + sp -= 8; + + RegisterValue reg_value; + reg_value.SetUInt64 (return_addr); + + if (log) + log->Printf("Pushing the return address onto the stack: new SP 0x%" PRIx64 ", return address 0x%" PRIx64, (uint64_t)sp, (uint64_t)return_addr); + + const RegisterInfo *pc_reg_info = reg_ctx->GetRegisterInfoByName("rip"); + Error error (reg_ctx->WriteRegisterValueToMemory(pc_reg_info, sp, pc_reg_info->byte_size, reg_value)); + if (error.Fail()) + return false; + + // %rsp is set to the actual stack value. + + if (log) + log->Printf("Writing SP (0x%" PRIx64 ") down", (uint64_t)sp); + + if (!reg_ctx->WriteRegisterFromUnsigned (reg_ctx->GetRegisterInfoByName("rsp"), sp)) + return false; + + // %rip is set to the address of the called function. + + if (log) + log->Printf("Writing new IP (0x%" PRIx64 ") down", (uint64_t)func_addr); + + if (!reg_ctx->WriteRegisterFromUnsigned (pc_reg_info, func_addr)) + return false; + + return true; +} + +static bool ReadIntegerArgument(Scalar &scalar, + unsigned int bit_width, + bool is_signed, + Thread &thread, + uint32_t *argument_register_ids, + unsigned int ¤t_argument_register, + addr_t ¤t_stack_argument) +{ + if (bit_width > 64) + return false; // Scalar can't hold large integer arguments + + if (current_argument_register < 6) + { + scalar = thread.GetRegisterContext()->ReadRegisterAsUnsigned(argument_register_ids[current_argument_register], 0); + current_argument_register++; + if (is_signed) + scalar.SignExtend (bit_width); + } + else + { + uint32_t byte_size = (bit_width + (8-1))/8; + Error error; + if (thread.GetProcess()->ReadScalarIntegerFromMemory(current_stack_argument, byte_size, is_signed, scalar, error)) + { + current_stack_argument += byte_size; + return true; + } + return false; + } + return true; +} + +bool +ABISysV_x86_64::GetArgumentValues (Thread &thread, + ValueList &values) const +{ + unsigned int num_values = values.GetSize(); + unsigned int value_index; + + // Extract the register context so we can read arguments from registers + + RegisterContext *reg_ctx = thread.GetRegisterContext().get(); + + if (!reg_ctx) + return false; + + // Get the pointer to the first stack argument so we have a place to start + // when reading data + + addr_t sp = reg_ctx->GetSP(0); + + if (!sp) + return false; + + addr_t current_stack_argument = sp + 8; // jump over return address + + uint32_t argument_register_ids[6]; + + argument_register_ids[0] = reg_ctx->GetRegisterInfoByName("rdi", 0)->kinds[eRegisterKindLLDB]; + argument_register_ids[1] = reg_ctx->GetRegisterInfoByName("rsi", 0)->kinds[eRegisterKindLLDB]; + argument_register_ids[2] = reg_ctx->GetRegisterInfoByName("rdx", 0)->kinds[eRegisterKindLLDB]; + argument_register_ids[3] = reg_ctx->GetRegisterInfoByName("rcx", 0)->kinds[eRegisterKindLLDB]; + argument_register_ids[4] = reg_ctx->GetRegisterInfoByName("r8", 0)->kinds[eRegisterKindLLDB]; + argument_register_ids[5] = reg_ctx->GetRegisterInfoByName("r9", 0)->kinds[eRegisterKindLLDB]; + + unsigned int current_argument_register = 0; + + for (value_index = 0; + value_index < num_values; + ++value_index) + { + Value *value = values.GetValueAtIndex(value_index); + + if (!value) + return false; + + // We currently only support extracting values with Clang QualTypes. + // Do we care about others? + ClangASTType clang_type = value->GetClangType(); + if (!clang_type) + return false; + bool is_signed; + + if (clang_type.IsIntegerType (is_signed)) + { + ReadIntegerArgument(value->GetScalar(), + clang_type.GetBitSize(), + is_signed, + thread, + argument_register_ids, + current_argument_register, + current_stack_argument); + } + else if (clang_type.IsPointerType ()) + { + ReadIntegerArgument(value->GetScalar(), + clang_type.GetBitSize(), + false, + thread, + argument_register_ids, + current_argument_register, + current_stack_argument); + } + } + + return true; +} + +Error +ABISysV_x86_64::SetReturnValueObject(lldb::StackFrameSP &frame_sp, lldb::ValueObjectSP &new_value_sp) +{ + Error error; + if (!new_value_sp) + { + error.SetErrorString("Empty value object for return value."); + return error; + } + + ClangASTType clang_type = new_value_sp->GetClangType(); + if (!clang_type) + { + error.SetErrorString ("Null clang type for return value."); + return error; + } + + Thread *thread = frame_sp->GetThread().get(); + + bool is_signed; + uint32_t count; + bool is_complex; + + RegisterContext *reg_ctx = thread->GetRegisterContext().get(); + + bool set_it_simple = false; + if (clang_type.IsIntegerType (is_signed) || clang_type.IsPointerType()) + { + const RegisterInfo *reg_info = reg_ctx->GetRegisterInfoByName("rax", 0); + + DataExtractor data; + size_t num_bytes = new_value_sp->GetData(data); + lldb::offset_t offset = 0; + if (num_bytes <= 8) + { + uint64_t raw_value = data.GetMaxU64(&offset, num_bytes); + + if (reg_ctx->WriteRegisterFromUnsigned (reg_info, raw_value)) + set_it_simple = true; + } + else + { + error.SetErrorString("We don't support returning longer than 64 bit integer values at present."); + } + + } + else if (clang_type.IsFloatingPointType (count, is_complex)) + { + if (is_complex) + error.SetErrorString ("We don't support returning complex values at present"); + else + { + size_t bit_width = clang_type.GetBitSize(); + if (bit_width <= 64) + { + const RegisterInfo *xmm0_info = reg_ctx->GetRegisterInfoByName("xmm0", 0); + RegisterValue xmm0_value; + DataExtractor data; + size_t num_bytes = new_value_sp->GetData(data); + + unsigned char buffer[16]; + ByteOrder byte_order = data.GetByteOrder(); + + data.CopyByteOrderedData (0, num_bytes, buffer, 16, byte_order); + xmm0_value.SetBytes(buffer, 16, byte_order); + reg_ctx->WriteRegister(xmm0_info, xmm0_value); + set_it_simple = true; + } + else + { + // FIXME - don't know how to do 80 bit long doubles yet. + error.SetErrorString ("We don't support returning float values > 64 bits at present"); + } + } + } + + if (!set_it_simple) + { + // Okay we've got a structure or something that doesn't fit in a simple register. + // We should figure out where it really goes, but we don't support this yet. + error.SetErrorString ("We only support setting simple integer and float return types at present."); + } + + return error; +} + + +ValueObjectSP +ABISysV_x86_64::GetReturnValueObjectSimple (Thread &thread, + ClangASTType &return_clang_type) const +{ + ValueObjectSP return_valobj_sp; + Value value; + + if (!return_clang_type) + return return_valobj_sp; + + //value.SetContext (Value::eContextTypeClangType, return_value_type); + value.SetClangType (return_clang_type); + + RegisterContext *reg_ctx = thread.GetRegisterContext().get(); + if (!reg_ctx) + return return_valobj_sp; + + const uint32_t type_flags = return_clang_type.GetTypeInfo (); + if (type_flags & ClangASTType::eTypeIsScalar) + { + value.SetValueType(Value::eValueTypeScalar); + + bool success = false; + if (type_flags & ClangASTType::eTypeIsInteger) + { + // Extract the register context so we can read arguments from registers + + const size_t byte_size = return_clang_type.GetByteSize(); + uint64_t raw_value = thread.GetRegisterContext()->ReadRegisterAsUnsigned(reg_ctx->GetRegisterInfoByName("rax", 0), 0); + const bool is_signed = (type_flags & ClangASTType::eTypeIsSigned) != 0; + switch (byte_size) + { + default: + break; + + case sizeof(uint64_t): + if (is_signed) + value.GetScalar() = (int64_t)(raw_value); + else + value.GetScalar() = (uint64_t)(raw_value); + success = true; + break; + + case sizeof(uint32_t): + if (is_signed) + value.GetScalar() = (int32_t)(raw_value & UINT32_MAX); + else + value.GetScalar() = (uint32_t)(raw_value & UINT32_MAX); + success = true; + break; + + case sizeof(uint16_t): + if (is_signed) + value.GetScalar() = (int16_t)(raw_value & UINT16_MAX); + else + value.GetScalar() = (uint16_t)(raw_value & UINT16_MAX); + success = true; + break; + + case sizeof(uint8_t): + if (is_signed) + value.GetScalar() = (int8_t)(raw_value & UINT8_MAX); + else + value.GetScalar() = (uint8_t)(raw_value & UINT8_MAX); + success = true; + break; + } + } + else if (type_flags & ClangASTType::eTypeIsFloat) + { + if (type_flags & ClangASTType::eTypeIsComplex) + { + // Don't handle complex yet. + } + else + { + const size_t byte_size = return_clang_type.GetByteSize(); + if (byte_size <= sizeof(long double)) + { + const RegisterInfo *xmm0_info = reg_ctx->GetRegisterInfoByName("xmm0", 0); + RegisterValue xmm0_value; + if (reg_ctx->ReadRegister (xmm0_info, xmm0_value)) + { + DataExtractor data; + if (xmm0_value.GetData(data)) + { + lldb::offset_t offset = 0; + if (byte_size == sizeof(float)) + { + value.GetScalar() = (float) data.GetFloat(&offset); + success = true; + } + else if (byte_size == sizeof(double)) + { + value.GetScalar() = (double) data.GetDouble(&offset); + success = true; + } + else if (byte_size == sizeof(long double)) + { + // Don't handle long double since that can be encoded as 80 bit floats... + } + } + } + } + } + } + + if (success) + return_valobj_sp = ValueObjectConstResult::Create (thread.GetStackFrameAtIndex(0).get(), + value, + ConstString("")); + + } + else if (type_flags & ClangASTType::eTypeIsPointer) + { + unsigned rax_id = reg_ctx->GetRegisterInfoByName("rax", 0)->kinds[eRegisterKindLLDB]; + value.GetScalar() = (uint64_t)thread.GetRegisterContext()->ReadRegisterAsUnsigned(rax_id, 0); + value.SetValueType(Value::eValueTypeScalar); + return_valobj_sp = ValueObjectConstResult::Create (thread.GetStackFrameAtIndex(0).get(), + value, + ConstString("")); + } + else if (type_flags & ClangASTType::eTypeIsVector) + { + const size_t byte_size = return_clang_type.GetByteSize(); + if (byte_size > 0) + { + + const RegisterInfo *altivec_reg = reg_ctx->GetRegisterInfoByName("ymm0", 0); + if (altivec_reg == NULL) + { + altivec_reg = reg_ctx->GetRegisterInfoByName("xmm0", 0); + if (altivec_reg == NULL) + altivec_reg = reg_ctx->GetRegisterInfoByName("mm0", 0); + } + + if (altivec_reg) + { + if (byte_size <= altivec_reg->byte_size) + { + ProcessSP process_sp (thread.GetProcess()); + if (process_sp) + { + std::unique_ptr heap_data_ap (new DataBufferHeap(byte_size, 0)); + const ByteOrder byte_order = process_sp->GetByteOrder(); + RegisterValue reg_value; + if (reg_ctx->ReadRegister(altivec_reg, reg_value)) + { + Error error; + if (reg_value.GetAsMemoryData (altivec_reg, + heap_data_ap->GetBytes(), + heap_data_ap->GetByteSize(), + byte_order, + error)) + { + DataExtractor data (DataBufferSP (heap_data_ap.release()), + byte_order, + process_sp->GetTarget().GetArchitecture().GetAddressByteSize()); + return_valobj_sp = ValueObjectConstResult::Create (&thread, + return_clang_type, + ConstString(""), + data); + } + } + } + } + } + } + } + + return return_valobj_sp; +} + +ValueObjectSP +ABISysV_x86_64::GetReturnValueObjectImpl (Thread &thread, ClangASTType &return_clang_type) const +{ + ValueObjectSP return_valobj_sp; + + if (!return_clang_type) + return return_valobj_sp; + + ExecutionContext exe_ctx (thread.shared_from_this()); + return_valobj_sp = GetReturnValueObjectSimple(thread, return_clang_type); + if (return_valobj_sp) + return return_valobj_sp; + + RegisterContextSP reg_ctx_sp = thread.GetRegisterContext(); + if (!reg_ctx_sp) + return return_valobj_sp; + + const size_t bit_width = return_clang_type.GetBitSize(); + if (return_clang_type.IsAggregateType()) + { + Target *target = exe_ctx.GetTargetPtr(); + bool is_memory = true; + if (bit_width <= 128) + { + ByteOrder target_byte_order = target->GetArchitecture().GetByteOrder(); + DataBufferSP data_sp (new DataBufferHeap(16, 0)); + DataExtractor return_ext (data_sp, + target_byte_order, + target->GetArchitecture().GetAddressByteSize()); + + const RegisterInfo *rax_info = reg_ctx_sp->GetRegisterInfoByName("rax", 0); + const RegisterInfo *rdx_info = reg_ctx_sp->GetRegisterInfoByName("rdx", 0); + const RegisterInfo *xmm0_info = reg_ctx_sp->GetRegisterInfoByName("xmm0", 0); + const RegisterInfo *xmm1_info = reg_ctx_sp->GetRegisterInfoByName("xmm1", 0); + + RegisterValue rax_value, rdx_value, xmm0_value, xmm1_value; + reg_ctx_sp->ReadRegister (rax_info, rax_value); + reg_ctx_sp->ReadRegister (rdx_info, rdx_value); + reg_ctx_sp->ReadRegister (xmm0_info, xmm0_value); + reg_ctx_sp->ReadRegister (xmm1_info, xmm1_value); + + DataExtractor rax_data, rdx_data, xmm0_data, xmm1_data; + + rax_value.GetData(rax_data); + rdx_value.GetData(rdx_data); + xmm0_value.GetData(xmm0_data); + xmm1_value.GetData(xmm1_data); + + uint32_t fp_bytes = 0; // Tracks how much of the xmm registers we've consumed so far + uint32_t integer_bytes = 0; // Tracks how much of the rax/rds registers we've consumed so far + + const uint32_t num_children = return_clang_type.GetNumFields (); + + // Since we are in the small struct regime, assume we are not in memory. + is_memory = false; + + for (uint32_t idx = 0; idx < num_children; idx++) + { + std::string name; + uint64_t field_bit_offset = 0; + bool is_signed; + bool is_complex; + uint32_t count; + + ClangASTType field_clang_type = return_clang_type.GetFieldAtIndex (idx, name, &field_bit_offset, NULL, NULL); + const size_t field_bit_width = field_clang_type.GetBitSize(); + + // If there are any unaligned fields, this is stored in memory. + if (field_bit_offset % field_bit_width != 0) + { + is_memory = true; + break; + } + + uint32_t field_byte_width = field_bit_width/8; + uint32_t field_byte_offset = field_bit_offset/8; + + + DataExtractor *copy_from_extractor = NULL; + uint32_t copy_from_offset = 0; + + if (field_clang_type.IsIntegerType (is_signed) || field_clang_type.IsPointerType ()) + { + if (integer_bytes < 8) + { + if (integer_bytes + field_byte_width <= 8) + { + // This is in RAX, copy from register to our result structure: + copy_from_extractor = &rax_data; + copy_from_offset = integer_bytes; + integer_bytes += field_byte_width; + } + else + { + // The next field wouldn't fit in the remaining space, so we pushed it to rdx. + copy_from_extractor = &rdx_data; + copy_from_offset = 0; + integer_bytes = 8 + field_byte_width; + + } + } + else if (integer_bytes + field_byte_width <= 16) + { + copy_from_extractor = &rdx_data; + copy_from_offset = integer_bytes - 8; + integer_bytes += field_byte_width; + } + else + { + // The last field didn't fit. I can't see how that would happen w/o the overall size being + // greater than 16 bytes. For now, return a NULL return value object. + return return_valobj_sp; + } + } + else if (field_clang_type.IsFloatingPointType (count, is_complex)) + { + // Structs with long doubles are always passed in memory. + if (field_bit_width == 128) + { + is_memory = true; + break; + } + else if (field_bit_width == 64) + { + // These have to be in a single xmm register. + if (fp_bytes == 0) + copy_from_extractor = &xmm0_data; + else + copy_from_extractor = &xmm1_data; + + copy_from_offset = 0; + fp_bytes += field_byte_width; + } + else if (field_bit_width == 32) + { + // This one is kind of complicated. If we are in an "eightbyte" with another float, we'll + // be stuffed into an xmm register with it. If we are in an "eightbyte" with one or more ints, + // then we will be stuffed into the appropriate GPR with them. + bool in_gpr; + if (field_byte_offset % 8 == 0) + { + // We are at the beginning of one of the eightbytes, so check the next element (if any) + if (idx == num_children - 1) + in_gpr = false; + else + { + uint64_t next_field_bit_offset = 0; + ClangASTType next_field_clang_type = return_clang_type.GetFieldAtIndex (idx + 1, + name, + &next_field_bit_offset, + NULL, + NULL); + if (next_field_clang_type.IsIntegerType (is_signed)) + in_gpr = true; + else + { + copy_from_offset = 0; + in_gpr = false; + } + } + + } + else if (field_byte_offset % 4 == 0) + { + // We are inside of an eightbyte, so see if the field before us is floating point: + // This could happen if somebody put padding in the structure. + if (idx == 0) + in_gpr = false; + else + { + uint64_t prev_field_bit_offset = 0; + ClangASTType prev_field_clang_type = return_clang_type.GetFieldAtIndex (idx - 1, + name, + &prev_field_bit_offset, + NULL, + NULL); + if (prev_field_clang_type.IsIntegerType (is_signed)) + in_gpr = true; + else + { + copy_from_offset = 4; + in_gpr = false; + } + } + + } + else + { + is_memory = true; + continue; + } + + // Okay, we've figured out whether we are in GPR or XMM, now figure out which one. + if (in_gpr) + { + if (integer_bytes < 8) + { + // This is in RAX, copy from register to our result structure: + copy_from_extractor = &rax_data; + copy_from_offset = integer_bytes; + integer_bytes += field_byte_width; + } + else + { + copy_from_extractor = &rdx_data; + copy_from_offset = integer_bytes - 8; + integer_bytes += field_byte_width; + } + } + else + { + if (fp_bytes < 8) + copy_from_extractor = &xmm0_data; + else + copy_from_extractor = &xmm1_data; + + fp_bytes += field_byte_width; + } + } + } + + // These two tests are just sanity checks. If I somehow get the + // type calculation wrong above it is better to just return nothing + // than to assert or crash. + if (!copy_from_extractor) + return return_valobj_sp; + if (copy_from_offset + field_byte_width > copy_from_extractor->GetByteSize()) + return return_valobj_sp; + + copy_from_extractor->CopyByteOrderedData (copy_from_offset, + field_byte_width, + data_sp->GetBytes() + field_byte_offset, + field_byte_width, + target_byte_order); + } + + if (!is_memory) + { + // The result is in our data buffer. Let's make a variable object out of it: + return_valobj_sp = ValueObjectConstResult::Create (&thread, + return_clang_type, + ConstString(""), + return_ext); + } + } + + + // FIXME: This is just taking a guess, rax may very well no longer hold the return storage location. + // If we are going to do this right, when we make a new frame we should check to see if it uses a memory + // return, and if we are at the first instruction and if so stash away the return location. Then we would + // only return the memory return value if we know it is valid. + + if (is_memory) + { + unsigned rax_id = reg_ctx_sp->GetRegisterInfoByName("rax", 0)->kinds[eRegisterKindLLDB]; + lldb::addr_t storage_addr = (uint64_t)thread.GetRegisterContext()->ReadRegisterAsUnsigned(rax_id, 0); + return_valobj_sp = ValueObjectMemory::Create (&thread, + "", + Address (storage_addr, NULL), + return_clang_type); + } + } + + return return_valobj_sp; +} + +bool +ABISysV_x86_64::CreateFunctionEntryUnwindPlan (UnwindPlan &unwind_plan) +{ + uint32_t reg_kind = unwind_plan.GetRegisterKind(); + uint32_t sp_reg_num = LLDB_INVALID_REGNUM; + uint32_t pc_reg_num = LLDB_INVALID_REGNUM; + + switch (reg_kind) + { + case eRegisterKindDWARF: + case eRegisterKindGCC: + sp_reg_num = gcc_dwarf_rsp; + pc_reg_num = gcc_dwarf_rip; + break; + + case eRegisterKindGDB: + sp_reg_num = gdb_rsp; + pc_reg_num = gdb_rip; + break; + + case eRegisterKindGeneric: + sp_reg_num = LLDB_REGNUM_GENERIC_SP; + pc_reg_num = LLDB_REGNUM_GENERIC_PC; + break; + } + + if (sp_reg_num == LLDB_INVALID_REGNUM || + pc_reg_num == LLDB_INVALID_REGNUM) + return false; + + UnwindPlan::RowSP row(new UnwindPlan::Row); + row->SetCFARegister (sp_reg_num); + row->SetCFAOffset (8); + row->SetRegisterLocationToAtCFAPlusOffset(pc_reg_num, -8, false); + unwind_plan.AppendRow (row); + unwind_plan.SetSourceName ("x86_64 at-func-entry default"); + unwind_plan.SetSourcedFromCompiler (eLazyBoolNo); + return true; +} + +bool +ABISysV_x86_64::CreateDefaultUnwindPlan (UnwindPlan &unwind_plan) +{ + uint32_t reg_kind = unwind_plan.GetRegisterKind(); + uint32_t fp_reg_num = LLDB_INVALID_REGNUM; + uint32_t sp_reg_num = LLDB_INVALID_REGNUM; + uint32_t pc_reg_num = LLDB_INVALID_REGNUM; + + switch (reg_kind) + { + case eRegisterKindDWARF: + case eRegisterKindGCC: + fp_reg_num = gcc_dwarf_rbp; + sp_reg_num = gcc_dwarf_rsp; + pc_reg_num = gcc_dwarf_rip; + break; + + case eRegisterKindGDB: + fp_reg_num = gdb_rbp; + sp_reg_num = gdb_rsp; + pc_reg_num = gdb_rip; + break; + + case eRegisterKindGeneric: + fp_reg_num = LLDB_REGNUM_GENERIC_FP; + sp_reg_num = LLDB_REGNUM_GENERIC_SP; + pc_reg_num = LLDB_REGNUM_GENERIC_PC; + break; + } + + if (fp_reg_num == LLDB_INVALID_REGNUM || + sp_reg_num == LLDB_INVALID_REGNUM || + pc_reg_num == LLDB_INVALID_REGNUM) + return false; + + UnwindPlan::RowSP row(new UnwindPlan::Row); + + const int32_t ptr_size = 8; + row->SetCFARegister (LLDB_REGNUM_GENERIC_FP); + row->SetCFAOffset (2 * ptr_size); + row->SetOffset (0); + + row->SetRegisterLocationToAtCFAPlusOffset(fp_reg_num, ptr_size * -2, true); + row->SetRegisterLocationToAtCFAPlusOffset(pc_reg_num, ptr_size * -1, true); + row->SetRegisterLocationToAtCFAPlusOffset(sp_reg_num, ptr_size * 0, true); + + unwind_plan.AppendRow (row); + unwind_plan.SetSourceName ("x86_64 default unwind plan"); + unwind_plan.SetSourcedFromCompiler (eLazyBoolNo); + unwind_plan.SetUnwindPlanValidAtAllInstructions (eLazyBoolNo); + return true; +} + +bool +ABISysV_x86_64::RegisterIsVolatile (const RegisterInfo *reg_info) +{ + return !RegisterIsCalleeSaved (reg_info); +} + + + +// See "Register Usage" in the +// "System V Application Binary Interface" +// "AMD64 Architecture Processor Supplement" +// (or "x86-64(tm) Architecture Processor Supplement" in earlier revisions) +// (this doc is also commonly referred to as the x86-64/AMD64 psABI) +// Edited by Michael Matz, Jan Hubicka, Andreas Jaeger, and Mark Mitchell +// current version is 0.99.6 released 2012-07-02 at http://refspecs.linuxfoundation.org/elf/x86-64-abi-0.99.pdf + +bool +ABISysV_x86_64::RegisterIsCalleeSaved (const RegisterInfo *reg_info) +{ + if (reg_info) + { + // Preserved registers are : + // rbx, rsp, rbp, r12, r13, r14, r15 + // mxcsr (partially preserved) + // x87 control word + + const char *name = reg_info->name; + if (name[0] == 'r') + { + switch (name[1]) + { + case '1': // r12, r13, r14, r15 + if (name[2] >= '2' && name[2] <= '5') + return name[3] == '\0'; + break; + + default: + break; + } + } + + // Accept shorter-variant versions, rbx/ebx, rip/ eip, etc. + if (name[0] == 'r' || name[0] == 'e') + { + switch (name[1]) + { + case 'b': // rbp, rbx + if (name[2] == 'p' || name[2] == 'x') + return name[3] == '\0'; + break; + + case 'i': // rip + if (name[2] == 'p') + return name[3] == '\0'; + break; + + case 's': // rsp + if (name[2] == 'p') + return name[3] == '\0'; + break; + + } + } + if (name[0] == 's' && name[1] == 'p' && name[2] == '\0') // sp + return true; + if (name[0] == 'f' && name[1] == 'p' && name[2] == '\0') // fp + return true; + if (name[0] == 'p' && name[1] == 'c' && name[2] == '\0') // pc + return true; + } + return false; +} + + + +void +ABISysV_x86_64::Initialize() +{ + PluginManager::RegisterPlugin (GetPluginNameStatic(), + "System V ABI for x86_64 targets", + CreateInstance); +} + +void +ABISysV_x86_64::Terminate() +{ + PluginManager::UnregisterPlugin (CreateInstance); +} + +lldb_private::ConstString +ABISysV_x86_64::GetPluginNameStatic() +{ + static ConstString g_name("sysv-x86_64"); + return g_name; +} + +//------------------------------------------------------------------ +// PluginInterface protocol +//------------------------------------------------------------------ +lldb_private::ConstString +ABISysV_x86_64::GetPluginName() +{ + return GetPluginNameStatic(); +} + +uint32_t +ABISysV_x86_64::GetPluginVersion() +{ + return 1; +} + diff --git a/contrib/llvm/tools/lldb/source/Plugins/ABI/SysV-x86_64/ABISysV_x86_64.h b/contrib/llvm/tools/lldb/source/Plugins/ABI/SysV-x86_64/ABISysV_x86_64.h new file mode 100644 index 00000000000..b10181960e8 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Plugins/ABI/SysV-x86_64/ABISysV_x86_64.h @@ -0,0 +1,138 @@ +//===-- ABISysV_x86_64.h ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ABISysV_x86_64_h_ +#define liblldb_ABISysV_x86_64_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private.h" +#include "lldb/Target/ABI.h" + +class ABISysV_x86_64 : + public lldb_private::ABI +{ +public: + + ~ABISysV_x86_64() + { + } + + virtual size_t + GetRedZoneSize () const; + + virtual bool + PrepareTrivialCall (lldb_private::Thread &thread, + lldb::addr_t sp, + lldb::addr_t functionAddress, + lldb::addr_t returnAddress, + lldb::addr_t *arg1_ptr = NULL, + lldb::addr_t *arg2_ptr = NULL, + lldb::addr_t *arg3_ptr = NULL, + lldb::addr_t *arg4_ptr = NULL, + lldb::addr_t *arg5_ptr = NULL, + lldb::addr_t *arg6_ptr = NULL) const; + + virtual bool + GetArgumentValues (lldb_private::Thread &thread, + lldb_private::ValueList &values) const; + + virtual lldb_private::Error + SetReturnValueObject(lldb::StackFrameSP &frame_sp, lldb::ValueObjectSP &new_value); + +protected: + lldb::ValueObjectSP + GetReturnValueObjectSimple (lldb_private::Thread &thread, + lldb_private::ClangASTType &ast_type) const; + +public: + virtual lldb::ValueObjectSP + GetReturnValueObjectImpl (lldb_private::Thread &thread, + lldb_private::ClangASTType &type) const; + + virtual bool + CreateFunctionEntryUnwindPlan (lldb_private::UnwindPlan &unwind_plan); + + virtual bool + CreateDefaultUnwindPlan (lldb_private::UnwindPlan &unwind_plan); + + virtual bool + RegisterIsVolatile (const lldb_private::RegisterInfo *reg_info); + + virtual bool + StackUsesFrames () + { + return true; + } + + virtual bool + CallFrameAddressIsValid (lldb::addr_t cfa) + { + // Make sure the stack call frame addresses are are 8 byte aligned + if (cfa & (8ull - 1ull)) + return false; // Not 8 byte aligned + if (cfa == 0) + return false; // Zero is not a valid stack address + return true; + } + + virtual bool + CodeAddressIsValid (lldb::addr_t pc) + { + // We have a 64 bit address space, so anything is valid as opcodes + // aren't fixed width... + return true; + } + + virtual bool + FunctionCallsChangeCFA () + { + return true; + } + + virtual const lldb_private::RegisterInfo * + GetRegisterInfoArray (uint32_t &count); + //------------------------------------------------------------------ + // Static Functions + //------------------------------------------------------------------ + static void + Initialize(); + + static void + Terminate(); + + static lldb::ABISP + CreateInstance (const lldb_private::ArchSpec &arch); + + static lldb_private::ConstString + GetPluginNameStatic(); + + //------------------------------------------------------------------ + // PluginInterface protocol + //------------------------------------------------------------------ + virtual lldb_private::ConstString + GetPluginName(); + + virtual uint32_t + GetPluginVersion(); + +protected: + void + CreateRegisterMapIfNeeded (); + + bool + RegisterIsCalleeSaved (const lldb_private::RegisterInfo *reg_info); + +private: + ABISysV_x86_64() : lldb_private::ABI() { } // Call CreateInstance instead. +}; + +#endif // liblldb_ABI_h_ diff --git a/contrib/llvm/tools/lldb/source/Plugins/Disassembler/llvm/DisassemblerLLVMC.cpp b/contrib/llvm/tools/lldb/source/Plugins/Disassembler/llvm/DisassemblerLLVMC.cpp new file mode 100644 index 00000000000..b281f2b4bfc --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Plugins/Disassembler/llvm/DisassemblerLLVMC.cpp @@ -0,0 +1,855 @@ +//===-- DisassemblerLLVMC.cpp -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "DisassemblerLLVMC.h" + +#include "llvm-c/Disassembler.h" +#include "llvm/MC/MCAsmInfo.h" +#include "llvm/MC/MCContext.h" +#include "llvm/MC/MCDisassembler.h" +#include "llvm/MC/MCInst.h" +#include "llvm/MC/MCInstPrinter.h" +#include "llvm/MC/MCInstrInfo.h" +#include "llvm/MC/MCRegisterInfo.h" +#include "llvm/MC/MCSubtargetInfo.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/MemoryObject.h" +#include "llvm/Support/TargetRegistry.h" +#include "llvm/Support/TargetSelect.h" +#include "llvm/ADT/SmallString.h" + + +#include "lldb/Core/Address.h" +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/Stream.h" +#include "lldb/Symbol/SymbolContext.h" +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/StackFrame.h" + +#include + +using namespace lldb; +using namespace lldb_private; + +class InstructionLLVMC : public lldb_private::Instruction +{ +public: + InstructionLLVMC (DisassemblerLLVMC &disasm, + const lldb_private::Address &address, + AddressClass addr_class) : + Instruction (address, addr_class), + m_disasm_sp (disasm.shared_from_this()), + m_does_branch (eLazyBoolCalculate), + m_is_valid (false), + m_using_file_addr (false) + { + } + + virtual + ~InstructionLLVMC () + { + } + + virtual bool + DoesBranch () + { + if (m_does_branch == eLazyBoolCalculate) + { + GetDisassemblerLLVMC().Lock(this, NULL); + DataExtractor data; + if (m_opcode.GetData(data)) + { + bool is_alternate_isa; + lldb::addr_t pc = m_address.GetFileAddress(); + + DisassemblerLLVMC::LLVMCDisassembler *mc_disasm_ptr = GetDisasmToUse (is_alternate_isa); + const uint8_t *opcode_data = data.GetDataStart(); + const size_t opcode_data_len = data.GetByteSize(); + llvm::MCInst inst; + const size_t inst_size = mc_disasm_ptr->GetMCInst (opcode_data, + opcode_data_len, + pc, + inst); + // Be conservative, if we didn't understand the instruction, say it might branch... + if (inst_size == 0) + m_does_branch = eLazyBoolYes; + else + { + const bool can_branch = mc_disasm_ptr->CanBranch(inst); + if (can_branch) + m_does_branch = eLazyBoolYes; + else + m_does_branch = eLazyBoolNo; + } + } + GetDisassemblerLLVMC().Unlock(); + } + return m_does_branch == eLazyBoolYes; + } + + DisassemblerLLVMC::LLVMCDisassembler * + GetDisasmToUse (bool &is_alternate_isa) + { + is_alternate_isa = false; + DisassemblerLLVMC &llvm_disasm = GetDisassemblerLLVMC(); + if (llvm_disasm.m_alternate_disasm_ap.get() != NULL) + { + const AddressClass address_class = GetAddressClass (); + + if (address_class == eAddressClassCodeAlternateISA) + { + is_alternate_isa = true; + return llvm_disasm.m_alternate_disasm_ap.get(); + } + } + return llvm_disasm.m_disasm_ap.get(); + } + + virtual size_t + Decode (const lldb_private::Disassembler &disassembler, + const lldb_private::DataExtractor &data, + lldb::offset_t data_offset) + { + // All we have to do is read the opcode which can be easy for some + // architectures + bool got_op = false; + DisassemblerLLVMC &llvm_disasm = GetDisassemblerLLVMC(); + const ArchSpec &arch = llvm_disasm.GetArchitecture(); + + const uint32_t min_op_byte_size = arch.GetMinimumOpcodeByteSize(); + const uint32_t max_op_byte_size = arch.GetMaximumOpcodeByteSize(); + if (min_op_byte_size == max_op_byte_size) + { + // Fixed size instructions, just read that amount of data. + if (!data.ValidOffsetForDataOfSize(data_offset, min_op_byte_size)) + return false; + + switch (min_op_byte_size) + { + case 1: + m_opcode.SetOpcode8 (data.GetU8 (&data_offset)); + got_op = true; + break; + + case 2: + m_opcode.SetOpcode16 (data.GetU16 (&data_offset)); + got_op = true; + break; + + case 4: + m_opcode.SetOpcode32 (data.GetU32 (&data_offset)); + got_op = true; + break; + + case 8: + m_opcode.SetOpcode64 (data.GetU64 (&data_offset)); + got_op = true; + break; + + default: + m_opcode.SetOpcodeBytes(data.PeekData(data_offset, min_op_byte_size), min_op_byte_size); + got_op = true; + break; + } + } + if (!got_op) + { + bool is_alternate_isa = false; + DisassemblerLLVMC::LLVMCDisassembler *mc_disasm_ptr = GetDisasmToUse (is_alternate_isa); + + const llvm::Triple::ArchType machine = arch.GetMachine(); + if (machine == llvm::Triple::arm || machine == llvm::Triple::thumb) + { + if (machine == llvm::Triple::thumb || is_alternate_isa) + { + uint32_t thumb_opcode = data.GetU16(&data_offset); + if ((thumb_opcode & 0xe000) != 0xe000 || ((thumb_opcode & 0x1800u) == 0)) + { + m_opcode.SetOpcode16 (thumb_opcode); + m_is_valid = true; + } + else + { + thumb_opcode <<= 16; + thumb_opcode |= data.GetU16(&data_offset); + m_opcode.SetOpcode16_2 (thumb_opcode); + m_is_valid = true; + } + } + else + { + m_opcode.SetOpcode32 (data.GetU32(&data_offset)); + m_is_valid = true; + } + } + else + { + // The opcode isn't evenly sized, so we need to actually use the llvm + // disassembler to parse it and get the size. + uint8_t *opcode_data = const_cast(data.PeekData (data_offset, 1)); + const size_t opcode_data_len = data.BytesLeft(data_offset); + const addr_t pc = m_address.GetFileAddress(); + llvm::MCInst inst; + + llvm_disasm.Lock(this, NULL); + const size_t inst_size = mc_disasm_ptr->GetMCInst(opcode_data, + opcode_data_len, + pc, + inst); + llvm_disasm.Unlock(); + if (inst_size == 0) + m_opcode.Clear(); + else + { + m_opcode.SetOpcodeBytes(opcode_data, inst_size); + m_is_valid = true; + } + } + } + return m_opcode.GetByteSize(); + } + + void + AppendComment (std::string &description) + { + if (m_comment.empty()) + m_comment.swap (description); + else + { + m_comment.append(", "); + m_comment.append(description); + } + } + + virtual void + CalculateMnemonicOperandsAndComment (const lldb_private::ExecutionContext *exe_ctx) + { + DataExtractor data; + const AddressClass address_class = GetAddressClass (); + + if (m_opcode.GetData(data)) + { + char out_string[512]; + + DisassemblerLLVMC &llvm_disasm = GetDisassemblerLLVMC(); + + DisassemblerLLVMC::LLVMCDisassembler *mc_disasm_ptr; + + if (address_class == eAddressClassCodeAlternateISA) + mc_disasm_ptr = llvm_disasm.m_alternate_disasm_ap.get(); + else + mc_disasm_ptr = llvm_disasm.m_disasm_ap.get(); + + lldb::addr_t pc = m_address.GetFileAddress(); + m_using_file_addr = true; + + const bool data_from_file = GetDisassemblerLLVMC().m_data_from_file; + bool use_hex_immediates = true; + Disassembler::HexImmediateStyle hex_style = Disassembler::eHexStyleC; + + if (exe_ctx) + { + Target *target = exe_ctx->GetTargetPtr(); + if (target) + { + use_hex_immediates = target->GetUseHexImmediates(); + hex_style = target->GetHexImmediateStyle(); + + if (!data_from_file) + { + const lldb::addr_t load_addr = m_address.GetLoadAddress(target); + if (load_addr != LLDB_INVALID_ADDRESS) + { + pc = load_addr; + m_using_file_addr = false; + } + } + } + } + + llvm_disasm.Lock(this, exe_ctx); + + const uint8_t *opcode_data = data.GetDataStart(); + const size_t opcode_data_len = data.GetByteSize(); + llvm::MCInst inst; + size_t inst_size = mc_disasm_ptr->GetMCInst (opcode_data, + opcode_data_len, + pc, + inst); + + if (inst_size > 0) + { + mc_disasm_ptr->SetStyle(use_hex_immediates, hex_style); + mc_disasm_ptr->PrintMCInst(inst, out_string, sizeof(out_string)); + } + + llvm_disasm.Unlock(); + + if (inst_size == 0) + { + m_comment.assign ("unknown opcode"); + inst_size = m_opcode.GetByteSize(); + StreamString mnemonic_strm; + lldb::offset_t offset = 0; + switch (inst_size) + { + case 1: + { + const uint8_t uval8 = data.GetU8 (&offset); + m_opcode.SetOpcode8 (uval8); + m_opcode_name.assign (".byte"); + mnemonic_strm.Printf("0x%2.2x", uval8); + } + break; + case 2: + { + const uint16_t uval16 = data.GetU16(&offset); + m_opcode.SetOpcode16(uval16); + m_opcode_name.assign (".short"); + mnemonic_strm.Printf("0x%4.4x", uval16); + } + break; + case 4: + { + const uint32_t uval32 = data.GetU32(&offset); + m_opcode.SetOpcode32(uval32); + m_opcode_name.assign (".long"); + mnemonic_strm.Printf("0x%8.8x", uval32); + } + break; + case 8: + { + const uint64_t uval64 = data.GetU64(&offset); + m_opcode.SetOpcode64(uval64); + m_opcode_name.assign (".quad"); + mnemonic_strm.Printf("0x%16.16" PRIx64, uval64); + } + break; + default: + if (inst_size == 0) + return; + else + { + const uint8_t *bytes = data.PeekData(offset, inst_size); + if (bytes == NULL) + return; + m_opcode_name.assign (".byte"); + m_opcode.SetOpcodeBytes(bytes, inst_size); + mnemonic_strm.Printf("0x%2.2x", bytes[0]); + for (uint32_t i=1; iCanBranch(inst); + if (can_branch) + m_does_branch = eLazyBoolYes; + else + m_does_branch = eLazyBoolNo; + + } + } + + if (!s_regex_compiled) + { + ::regcomp(&s_regex, "[ \t]*([^ ^\t]+)[ \t]*([^ ^\t].*)?", REG_EXTENDED); + s_regex_compiled = true; + } + + ::regmatch_t matches[3]; + + if (!::regexec(&s_regex, out_string, sizeof(matches) / sizeof(::regmatch_t), matches, 0)) + { + if (matches[1].rm_so != -1) + m_opcode_name.assign(out_string + matches[1].rm_so, matches[1].rm_eo - matches[1].rm_so); + if (matches[2].rm_so != -1) + m_mnemonics.assign(out_string + matches[2].rm_so, matches[2].rm_eo - matches[2].rm_so); + } + } + } + + bool + IsValid () const + { + return m_is_valid; + } + + bool + UsingFileAddress() const + { + return m_using_file_addr; + } + size_t + GetByteSize () const + { + return m_opcode.GetByteSize(); + } + + DisassemblerLLVMC & + GetDisassemblerLLVMC () + { + return *(DisassemblerLLVMC *)m_disasm_sp.get(); + } +protected: + + DisassemblerSP m_disasm_sp; // for ownership + LazyBool m_does_branch; + bool m_is_valid; + bool m_using_file_addr; + + static bool s_regex_compiled; + static ::regex_t s_regex; +}; + +bool InstructionLLVMC::s_regex_compiled = false; +::regex_t InstructionLLVMC::s_regex; + +DisassemblerLLVMC::LLVMCDisassembler::LLVMCDisassembler (const char *triple, unsigned flavor, DisassemblerLLVMC &owner): + m_is_valid(true) +{ + std::string Error; + const llvm::Target *curr_target = llvm::TargetRegistry::lookupTarget(triple, Error); + if (!curr_target) + { + m_is_valid = false; + return; + } + + m_instr_info_ap.reset(curr_target->createMCInstrInfo()); + m_reg_info_ap.reset (curr_target->createMCRegInfo(triple)); + + std::string features_str; + + m_subtarget_info_ap.reset(curr_target->createMCSubtargetInfo(triple, "", + features_str)); + + m_asm_info_ap.reset(curr_target->createMCAsmInfo(triple)); + + if (m_instr_info_ap.get() == NULL || m_reg_info_ap.get() == NULL || m_subtarget_info_ap.get() == NULL || m_asm_info_ap.get() == NULL) + { + m_is_valid = false; + return; + } + + m_context_ap.reset(new llvm::MCContext(*m_asm_info_ap.get(), *(m_reg_info_ap.get()), 0)); + + m_disasm_ap.reset(curr_target->createMCDisassembler(*m_subtarget_info_ap.get())); + if (m_disasm_ap.get()) + { + m_disasm_ap->setupForSymbolicDisassembly(NULL, + DisassemblerLLVMC::SymbolLookupCallback, + (void *) &owner, + m_context_ap.get()); + + unsigned asm_printer_variant; + if (flavor == ~0U) + asm_printer_variant = m_asm_info_ap->getAssemblerDialect(); + else + { + asm_printer_variant = flavor; + } + + m_instr_printer_ap.reset(curr_target->createMCInstPrinter(asm_printer_variant, + *m_asm_info_ap.get(), + *m_instr_info_ap.get(), + *m_reg_info_ap.get(), + *m_subtarget_info_ap.get())); + if (m_instr_printer_ap.get() == NULL) + { + m_disasm_ap.reset(); + m_is_valid = false; + } + } + else + m_is_valid = false; +} + +DisassemblerLLVMC::LLVMCDisassembler::~LLVMCDisassembler() +{ +} + +namespace { + // This is the memory object we use in GetInstruction. + class LLDBDisasmMemoryObject : public llvm::MemoryObject { + const uint8_t *m_bytes; + uint64_t m_size; + uint64_t m_base_PC; + public: + LLDBDisasmMemoryObject(const uint8_t *bytes, uint64_t size, uint64_t basePC) : + m_bytes(bytes), m_size(size), m_base_PC(basePC) {} + + uint64_t getBase() const { return m_base_PC; } + uint64_t getExtent() const { return m_size; } + + int readByte(uint64_t addr, uint8_t *byte) const { + if (addr - m_base_PC >= m_size) + return -1; + *byte = m_bytes[addr - m_base_PC]; + return 0; + } + }; +} // End Anonymous Namespace + +uint64_t +DisassemblerLLVMC::LLVMCDisassembler::GetMCInst (const uint8_t *opcode_data, + size_t opcode_data_len, + lldb::addr_t pc, + llvm::MCInst &mc_inst) +{ + LLDBDisasmMemoryObject memory_object (opcode_data, opcode_data_len, pc); + llvm::MCDisassembler::DecodeStatus status; + + uint64_t new_inst_size; + status = m_disasm_ap->getInstruction(mc_inst, + new_inst_size, + memory_object, + pc, + llvm::nulls(), + llvm::nulls()); + if (status == llvm::MCDisassembler::Success) + return new_inst_size; + else + return 0; +} + +uint64_t +DisassemblerLLVMC::LLVMCDisassembler::PrintMCInst (llvm::MCInst &mc_inst, + char *dst, + size_t dst_len) +{ + llvm::StringRef unused_annotations; + llvm::SmallString<64> inst_string; + llvm::raw_svector_ostream inst_stream(inst_string); + m_instr_printer_ap->printInst (&mc_inst, inst_stream, unused_annotations); + inst_stream.flush(); + const size_t output_size = std::min(dst_len - 1, inst_string.size()); + std::memcpy(dst, inst_string.data(), output_size); + dst[output_size] = '\0'; + + return output_size; +} + +void +DisassemblerLLVMC::LLVMCDisassembler::SetStyle (bool use_hex_immed, HexImmediateStyle hex_style) +{ + m_instr_printer_ap->setPrintImmHex(use_hex_immed); + switch(hex_style) + { + case eHexStyleC: m_instr_printer_ap->setPrintImmHex(llvm::HexStyle::C); break; + case eHexStyleAsm: m_instr_printer_ap->setPrintImmHex(llvm::HexStyle::Asm); break; + } +} + +bool +DisassemblerLLVMC::LLVMCDisassembler::CanBranch (llvm::MCInst &mc_inst) +{ + return m_instr_info_ap->get(mc_inst.getOpcode()).mayAffectControlFlow(mc_inst, *m_reg_info_ap.get()); +} + +bool +DisassemblerLLVMC::FlavorValidForArchSpec (const lldb_private::ArchSpec &arch, const char *flavor) +{ + llvm::Triple triple = arch.GetTriple(); + if (flavor == NULL || strcmp (flavor, "default") == 0) + return true; + + if (triple.getArch() == llvm::Triple::x86 || triple.getArch() == llvm::Triple::x86_64) + { + if (strcmp (flavor, "intel") == 0 || strcmp (flavor, "att") == 0) + return true; + else + return false; + } + else + return false; +} + + +Disassembler * +DisassemblerLLVMC::CreateInstance (const ArchSpec &arch, const char *flavor) +{ + if (arch.GetTriple().getArch() != llvm::Triple::UnknownArch) + { + std::unique_ptr disasm_ap (new DisassemblerLLVMC(arch, flavor)); + + if (disasm_ap.get() && disasm_ap->IsValid()) + return disasm_ap.release(); + } + return NULL; +} + +DisassemblerLLVMC::DisassemblerLLVMC (const ArchSpec &arch, const char *flavor_string) : + Disassembler(arch, flavor_string), + m_exe_ctx (NULL), + m_inst (NULL), + m_data_from_file (false) +{ + if (!FlavorValidForArchSpec (arch, m_flavor.c_str())) + { + m_flavor.assign("default"); + } + + const char *triple = arch.GetTriple().getTriple().c_str(); + unsigned flavor = ~0U; + + // So far the only supported flavor is "intel" on x86. The base class will set this + // correctly coming in. + if (arch.GetTriple().getArch() == llvm::Triple::x86 + || arch.GetTriple().getArch() == llvm::Triple::x86_64) + { + if (m_flavor == "intel") + { + flavor = 1; + } + else if (m_flavor == "att") + { + flavor = 0; + } + } + + ArchSpec thumb_arch(arch); + if (arch.GetTriple().getArch() == llvm::Triple::arm) + { + std::string thumb_arch_name (thumb_arch.GetTriple().getArchName().str()); + // Replace "arm" with "thumb" so we get all thumb variants correct + if (thumb_arch_name.size() > 3) + { + thumb_arch_name.erase(0,3); + thumb_arch_name.insert(0, "thumb"); + } + else + { + thumb_arch_name = "thumbv7"; + } + thumb_arch.GetTriple().setArchName(llvm::StringRef(thumb_arch_name.c_str())); + } + + // Cortex-M3 devices (e.g. armv7m) can only execute thumb (T2) instructions, + // so hardcode the primary disassembler to thumb mode. + if (arch.GetTriple().getArch() == llvm::Triple::arm + && (arch.GetCore() == ArchSpec::Core::eCore_arm_armv7m || arch.GetCore() == ArchSpec::Core::eCore_arm_armv7em)) + { + triple = thumb_arch.GetTriple().getTriple().c_str(); + } + + m_disasm_ap.reset (new LLVMCDisassembler(triple, flavor, *this)); + if (!m_disasm_ap->IsValid()) + { + // We use m_disasm_ap.get() to tell whether we are valid or not, so if this isn't good for some reason, + // we reset it, and then we won't be valid and FindPlugin will fail and we won't get used. + m_disasm_ap.reset(); + } + + // For arm CPUs that can execute arm or thumb instructions, also create a thumb instruction disassembler. + if (arch.GetTriple().getArch() == llvm::Triple::arm) + { + std::string thumb_triple(thumb_arch.GetTriple().getTriple()); + m_alternate_disasm_ap.reset(new LLVMCDisassembler(thumb_triple.c_str(), flavor, *this)); + if (!m_alternate_disasm_ap->IsValid()) + { + m_disasm_ap.reset(); + m_alternate_disasm_ap.reset(); + } + } +} + +DisassemblerLLVMC::~DisassemblerLLVMC() +{ +} + +size_t +DisassemblerLLVMC::DecodeInstructions (const Address &base_addr, + const DataExtractor& data, + lldb::offset_t data_offset, + size_t num_instructions, + bool append, + bool data_from_file) +{ + if (!append) + m_instruction_list.Clear(); + + if (!IsValid()) + return 0; + + m_data_from_file = data_from_file; + uint32_t data_cursor = data_offset; + const size_t data_byte_size = data.GetByteSize(); + uint32_t instructions_parsed = 0; + Address inst_addr(base_addr); + + while (data_cursor < data_byte_size && instructions_parsed < num_instructions) + { + + AddressClass address_class = eAddressClassCode; + + if (m_alternate_disasm_ap.get() != NULL) + address_class = inst_addr.GetAddressClass (); + + InstructionSP inst_sp(new InstructionLLVMC(*this, + inst_addr, + address_class)); + + if (!inst_sp) + break; + + uint32_t inst_size = inst_sp->Decode(*this, data, data_cursor); + + if (inst_size == 0) + break; + + m_instruction_list.Append(inst_sp); + data_cursor += inst_size; + inst_addr.Slide(inst_size); + instructions_parsed++; + } + + return data_cursor - data_offset; +} + +void +DisassemblerLLVMC::Initialize() +{ + PluginManager::RegisterPlugin (GetPluginNameStatic(), + "Disassembler that uses LLVM MC to disassemble i386, x86_64 and ARM.", + CreateInstance); + + llvm::InitializeAllTargetInfos(); + llvm::InitializeAllTargetMCs(); + llvm::InitializeAllAsmParsers(); + llvm::InitializeAllDisassemblers(); +} + +void +DisassemblerLLVMC::Terminate() +{ + PluginManager::UnregisterPlugin (CreateInstance); +} + + +ConstString +DisassemblerLLVMC::GetPluginNameStatic() +{ + static ConstString g_name("llvm-mc"); + return g_name; +} + +int DisassemblerLLVMC::OpInfoCallback (void *disassembler, + uint64_t pc, + uint64_t offset, + uint64_t size, + int tag_type, + void *tag_bug) +{ + return static_cast(disassembler)->OpInfo (pc, + offset, + size, + tag_type, + tag_bug); +} + +const char *DisassemblerLLVMC::SymbolLookupCallback (void *disassembler, + uint64_t value, + uint64_t *type, + uint64_t pc, + const char **name) +{ + return static_cast(disassembler)->SymbolLookup(value, + type, + pc, + name); +} + +int DisassemblerLLVMC::OpInfo (uint64_t PC, + uint64_t Offset, + uint64_t Size, + int tag_type, + void *tag_bug) +{ + switch (tag_type) + { + default: + break; + case 1: + bzero (tag_bug, sizeof(::LLVMOpInfo1)); + break; + } + return 0; +} + +const char *DisassemblerLLVMC::SymbolLookup (uint64_t value, + uint64_t *type_ptr, + uint64_t pc, + const char **name) +{ + if (*type_ptr) + { + if (m_exe_ctx && m_inst) + { + //std::string remove_this_prior_to_checkin; + Target *target = m_exe_ctx ? m_exe_ctx->GetTargetPtr() : NULL; + Address value_so_addr; + if (m_inst->UsingFileAddress()) + { + ModuleSP module_sp(m_inst->GetAddress().GetModule()); + if (module_sp) + module_sp->ResolveFileAddress(value, value_so_addr); + } + else if (target && !target->GetSectionLoadList().IsEmpty()) + { + target->GetSectionLoadList().ResolveLoadAddress(value, value_so_addr); + } + + if (value_so_addr.IsValid() && value_so_addr.GetSection()) + { + StreamString ss; + + value_so_addr.Dump (&ss, + target, + Address::DumpStyleResolvedDescriptionNoModule, + Address::DumpStyleSectionNameOffset); + + if (!ss.GetString().empty()) + { + m_inst->AppendComment(ss.GetString()); + } + } + } + } + + *type_ptr = LLVMDisassembler_ReferenceType_InOut_None; + *name = NULL; + return NULL; +} + +//------------------------------------------------------------------ +// PluginInterface protocol +//------------------------------------------------------------------ +ConstString +DisassemblerLLVMC::GetPluginName() +{ + return GetPluginNameStatic(); +} + +uint32_t +DisassemblerLLVMC::GetPluginVersion() +{ + return 1; +} + diff --git a/contrib/llvm/tools/lldb/source/Plugins/Disassembler/llvm/DisassemblerLLVMC.h b/contrib/llvm/tools/lldb/source/Plugins/Disassembler/llvm/DisassemblerLLVMC.h new file mode 100644 index 00000000000..c567791866d --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Plugins/Disassembler/llvm/DisassemblerLLVMC.h @@ -0,0 +1,166 @@ +//===-- DisassemblerLLVMC.h -------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_DisassemblerLLVMC_h_ +#define liblldb_DisassemblerLLVMC_h_ + +#include + +#include "llvm-c/Disassembler.h" + +// Opaque references to C++ Objects in LLVM's MC. +namespace llvm +{ + class MCContext; + class MCInst; + class MCInstrInfo; + class MCRegisterInfo; + class MCDisassembler; + class MCInstPrinter; + class MCAsmInfo; + class MCSubtargetInfo; +} + +#include "lldb/Core/Address.h" +#include "lldb/Core/Disassembler.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Host/Mutex.h" + +class InstructionLLVMC; + +class DisassemblerLLVMC : public lldb_private::Disassembler +{ + // Since we need to make two actual MC Disassemblers for ARM (ARM & THUMB), and there's a bit of goo to set up and own + // in the MC disassembler world, I added this class to manage the actual disassemblers. + class LLVMCDisassembler + { + public: + LLVMCDisassembler (const char *triple, unsigned flavor, DisassemblerLLVMC &owner); + + ~LLVMCDisassembler(); + + uint64_t GetMCInst (const uint8_t *opcode_data, size_t opcode_data_len, lldb::addr_t pc, llvm::MCInst &mc_inst); + uint64_t PrintMCInst (llvm::MCInst &mc_inst, char *output_buffer, size_t out_buffer_len); + void SetStyle (bool use_hex_immed, HexImmediateStyle hex_style); + bool CanBranch (llvm::MCInst &mc_inst); + bool IsValid() + { + return m_is_valid; + } + + private: + bool m_is_valid; + std::unique_ptr m_context_ap; + std::unique_ptr m_asm_info_ap; + std::unique_ptr m_subtarget_info_ap; + std::unique_ptr m_instr_info_ap; + std::unique_ptr m_reg_info_ap; + std::unique_ptr m_instr_printer_ap; + std::unique_ptr m_disasm_ap; + }; + +public: + //------------------------------------------------------------------ + // Static Functions + //------------------------------------------------------------------ + static void + Initialize(); + + static void + Terminate(); + + static lldb_private::ConstString + GetPluginNameStatic(); + + static lldb_private::Disassembler * + CreateInstance(const lldb_private::ArchSpec &arch, const char *flavor); + + DisassemblerLLVMC(const lldb_private::ArchSpec &arch, const char *flavor /* = NULL */); + + virtual + ~DisassemblerLLVMC(); + + virtual size_t + DecodeInstructions (const lldb_private::Address &base_addr, + const lldb_private::DataExtractor& data, + lldb::offset_t data_offset, + size_t num_instructions, + bool append, + bool data_from_file); + + //------------------------------------------------------------------ + // PluginInterface protocol + //------------------------------------------------------------------ + virtual lldb_private::ConstString + GetPluginName(); + + virtual uint32_t + GetPluginVersion(); + +protected: + friend class InstructionLLVMC; + + virtual bool + FlavorValidForArchSpec (const lldb_private::ArchSpec &arch, const char *flavor); + + bool + IsValid() + { + return (m_disasm_ap.get() != NULL && m_disasm_ap->IsValid()); + } + + int OpInfo(uint64_t PC, + uint64_t Offset, + uint64_t Size, + int TagType, + void *TagBug); + + const char *SymbolLookup (uint64_t ReferenceValue, + uint64_t *ReferenceType, + uint64_t ReferencePC, + const char **ReferenceName); + + static int OpInfoCallback (void *DisInfo, + uint64_t PC, + uint64_t Offset, + uint64_t Size, + int TagType, + void *TagBug); + + static const char *SymbolLookupCallback(void *DisInfo, + uint64_t ReferenceValue, + uint64_t *ReferenceType, + uint64_t ReferencePC, + const char **ReferenceName); + + void Lock(InstructionLLVMC *inst, + const lldb_private::ExecutionContext *exe_ctx) + { + m_mutex.Lock(); + m_inst = inst; + m_exe_ctx = exe_ctx; + } + + void Unlock() + { + m_inst = NULL; + m_exe_ctx = NULL; + m_mutex.Unlock(); + } + + const lldb_private::ExecutionContext *m_exe_ctx; + InstructionLLVMC *m_inst; + lldb_private::Mutex m_mutex; + bool m_data_from_file; + + std::unique_ptr m_disasm_ap; + std::unique_ptr m_alternate_disasm_ap; +}; + +#endif // liblldb_DisassemblerLLVM_h_ diff --git a/contrib/llvm/tools/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/AuxVector.cpp b/contrib/llvm/tools/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/AuxVector.cpp new file mode 100644 index 00000000000..2604ae67016 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/AuxVector.cpp @@ -0,0 +1,177 @@ +//===-- AuxVector.cpp -------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// C Includes +#include +#include +#include + +// C++ Includes +// Other libraries and framework includes +#include "lldb/Core/DataBufferHeap.h" +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/Log.h" +#include "lldb/Target/Process.h" + +#if defined(__linux__) or defined(__FreeBSD__) +#include "Plugins/Process/elf-core/ProcessElfCore.h" +#endif + +#include "AuxVector.h" + +using namespace lldb; +using namespace lldb_private; + +static bool +GetMaxU64(DataExtractor &data, + lldb::offset_t *offset_ptr, + uint64_t *value, + unsigned int byte_size) +{ + lldb::offset_t saved_offset = *offset_ptr; + *value = data.GetMaxU64(offset_ptr, byte_size); + return *offset_ptr != saved_offset; +} + +static bool +ParseAuxvEntry(DataExtractor &data, + AuxVector::Entry &entry, + lldb::offset_t *offset_ptr, + unsigned int byte_size) +{ + if (!GetMaxU64(data, offset_ptr, &entry.type, byte_size)) + return false; + + if (!GetMaxU64(data, offset_ptr, &entry.value, byte_size)) + return false; + + return true; +} + +DataBufferSP +AuxVector::GetAuxvData() +{ +#if defined(__linux__) or defined(__FreeBSD__) + if (m_process->GetPluginName() == ProcessElfCore::GetPluginNameStatic()) + return static_cast(m_process)->GetAuxvData(); +#endif + return lldb_private::Host::GetAuxvData(m_process); +} + +void +AuxVector::ParseAuxv(DataExtractor &data) +{ + const unsigned int byte_size = m_process->GetAddressByteSize(); + lldb::offset_t offset = 0; + + for (;;) + { + Entry entry; + + if (!ParseAuxvEntry(data, entry, &offset, byte_size)) + break; + + if (entry.type == AT_NULL) + break; + + if (entry.type == AT_IGNORE) + continue; + + m_auxv.push_back(entry); + } +} + +AuxVector::AuxVector(Process *process) + : m_process(process) +{ + DataExtractor data; + Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_DYNAMIC_LOADER)); + + data.SetData(GetAuxvData()); + data.SetByteOrder(m_process->GetByteOrder()); + data.SetAddressByteSize(m_process->GetAddressByteSize()); + + ParseAuxv(data); + + if (log) + DumpToLog(log); +} + +AuxVector::iterator +AuxVector::FindEntry(EntryType type) const +{ + for (iterator I = begin(); I != end(); ++I) + { + if (I->type == static_cast(type)) + return I; + } + + return end(); +} + +void +AuxVector::DumpToLog(Log *log) const +{ + if (!log) + return; + + log->PutCString("AuxVector: "); + for (iterator I = begin(); I != end(); ++I) + { + log->Printf(" %s [%" PRIu64 "]: %" PRIx64, GetEntryName(*I), I->type, I->value); + } +} + +const char * +AuxVector::GetEntryName(EntryType type) +{ + const char *name = "AT_???"; + +#define ENTRY_NAME(_type) _type: name = #_type + switch (type) + { + case ENTRY_NAME(AT_NULL); break; + case ENTRY_NAME(AT_IGNORE); break; + case ENTRY_NAME(AT_EXECFD); break; + case ENTRY_NAME(AT_PHDR); break; + case ENTRY_NAME(AT_PHENT); break; + case ENTRY_NAME(AT_PHNUM); break; + case ENTRY_NAME(AT_PAGESZ); break; + case ENTRY_NAME(AT_BASE); break; + case ENTRY_NAME(AT_FLAGS); break; + case ENTRY_NAME(AT_ENTRY); break; + case ENTRY_NAME(AT_NOTELF); break; + case ENTRY_NAME(AT_UID); break; + case ENTRY_NAME(AT_EUID); break; + case ENTRY_NAME(AT_GID); break; + case ENTRY_NAME(AT_EGID); break; + case ENTRY_NAME(AT_CLKTCK); break; + case ENTRY_NAME(AT_PLATFORM); break; + case ENTRY_NAME(AT_HWCAP); break; + case ENTRY_NAME(AT_FPUCW); break; + case ENTRY_NAME(AT_DCACHEBSIZE); break; + case ENTRY_NAME(AT_ICACHEBSIZE); break; + case ENTRY_NAME(AT_UCACHEBSIZE); break; + case ENTRY_NAME(AT_IGNOREPPC); break; + case ENTRY_NAME(AT_SECURE); break; + case ENTRY_NAME(AT_BASE_PLATFORM); break; + case ENTRY_NAME(AT_RANDOM); break; + case ENTRY_NAME(AT_EXECFN); break; + case ENTRY_NAME(AT_SYSINFO); break; + case ENTRY_NAME(AT_SYSINFO_EHDR); break; + case ENTRY_NAME(AT_L1I_CACHESHAPE); break; + case ENTRY_NAME(AT_L1D_CACHESHAPE); break; + case ENTRY_NAME(AT_L2_CACHESHAPE); break; + case ENTRY_NAME(AT_L3_CACHESHAPE); break; + } +#undef ENTRY_NAME + + return name; +} + diff --git a/contrib/llvm/tools/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/AuxVector.h b/contrib/llvm/tools/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/AuxVector.h new file mode 100644 index 00000000000..2d39eddcacc --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/AuxVector.h @@ -0,0 +1,115 @@ +//===-- AuxVector.h ---------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_AuxVector_H_ +#define liblldb_AuxVector_H_ + +// C Includes +// C++ Includes +#include + +// Other libraries and framework includes +#include "lldb/lldb-forward.h" + +namespace lldb_private { +class DataExtractor; +} + +/// @class AuxVector +/// @brief Represents a processes auxiliary vector. +/// +/// When a process is loaded on Linux a vector of values is placed onto the +/// stack communicating operating system specific information. On construction +/// this class locates and parses this information and provides a simple +/// read-only interface to the entries found. +class AuxVector { + +public: + AuxVector(lldb_private::Process *process); + + struct Entry { + uint64_t type; + uint64_t value; + + Entry() : type(0), value(0) { } + }; + + /// Constants describing the type of entry. + /// On Linux, running "LD_SHOW_AUXV=1 ./executable" will spew AUX information. + enum EntryType { + AT_NULL = 0, ///< End of auxv. + AT_IGNORE = 1, ///< Ignore entry. + AT_EXECFD = 2, ///< File descriptor of program. + AT_PHDR = 3, ///< Program headers. + AT_PHENT = 4, ///< Size of program header. + AT_PHNUM = 5, ///< Number of program headers. + AT_PAGESZ = 6, ///< Page size. + AT_BASE = 7, ///< Interpreter base address. + AT_FLAGS = 8, ///< Flags. + AT_ENTRY = 9, ///< Program entry point. + AT_NOTELF = 10, ///< Set if program is not an ELF. + AT_UID = 11, ///< UID. + AT_EUID = 12, ///< Effective UID. + AT_GID = 13, ///< GID. + AT_EGID = 14, ///< Effective GID. + AT_CLKTCK = 17, ///< Clock frequency (e.g. times(2)). + AT_PLATFORM = 15, ///< String identifying platform. + AT_HWCAP = 16, ///< Machine dependent hints about processor capabilities. + AT_FPUCW = 18, ///< Used FPU control word. + AT_DCACHEBSIZE = 19, ///< Data cache block size. + AT_ICACHEBSIZE = 20, ///< Instruction cache block size. + AT_UCACHEBSIZE = 21, ///< Unified cache block size. + AT_IGNOREPPC = 22, ///< Entry should be ignored. + AT_SECURE = 23, ///< Boolean, was exec setuid-like? + AT_BASE_PLATFORM = 24, ///< String identifying real platforms. + AT_RANDOM = 25, ///< Address of 16 random bytes. + AT_EXECFN = 31, ///< Filename of executable. + AT_SYSINFO = 32, ///< Pointer to the global system page used for system calls and other nice things. + AT_SYSINFO_EHDR = 33, + AT_L1I_CACHESHAPE = 34, ///< Shapes of the caches. + AT_L1D_CACHESHAPE = 35, + AT_L2_CACHESHAPE = 36, + AT_L3_CACHESHAPE = 37, + }; + +private: + typedef std::vector EntryVector; + +public: + typedef EntryVector::const_iterator iterator; + + iterator begin() const { return m_auxv.begin(); } + iterator end() const { return m_auxv.end(); } + + iterator + FindEntry(EntryType type) const; + + static const char * + GetEntryName(const Entry &entry) { + return GetEntryName(static_cast(entry.type)); + } + + static const char * + GetEntryName(EntryType type); + + void + DumpToLog(lldb_private::Log *log) const; + +private: + lldb_private::Process *m_process; + EntryVector m_auxv; + + lldb::DataBufferSP + GetAuxvData(); + + void + ParseAuxv(lldb_private::DataExtractor &data); +}; + +#endif diff --git a/contrib/llvm/tools/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DYLDRendezvous.cpp b/contrib/llvm/tools/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DYLDRendezvous.cpp new file mode 100644 index 00000000000..3e1b52938f4 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DYLDRendezvous.cpp @@ -0,0 +1,336 @@ +//===-- DYLDRendezvous.cpp --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// C Includes +// C++ Includes +// Other libraries and framework includes +#include "lldb/Core/ArchSpec.h" +#include "lldb/Core/Error.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Module.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Target.h" + +#include "DYLDRendezvous.h" + +using namespace lldb; +using namespace lldb_private; + +/// Locates the address of the rendezvous structure. Returns the address on +/// success and LLDB_INVALID_ADDRESS on failure. +static addr_t +ResolveRendezvousAddress(Process *process) +{ + addr_t info_location; + addr_t info_addr; + Error error; + size_t size; + + info_location = process->GetImageInfoAddress(); + + if (info_location == LLDB_INVALID_ADDRESS) + return LLDB_INVALID_ADDRESS; + + info_addr = 0; + size = process->DoReadMemory(info_location, &info_addr, + process->GetAddressByteSize(), error); + if (size != process->GetAddressByteSize() || error.Fail()) + return LLDB_INVALID_ADDRESS; + + if (info_addr == 0) + return LLDB_INVALID_ADDRESS; + + return info_addr; +} + +DYLDRendezvous::DYLDRendezvous(Process *process) + : m_process(process), + m_rendezvous_addr(LLDB_INVALID_ADDRESS), + m_current(), + m_previous(), + m_soentries(), + m_added_soentries(), + m_removed_soentries() +{ + // Cache a copy of the executable path + if (m_process) + { + Module *exe_mod = m_process->GetTarget().GetExecutableModulePointer(); + if (exe_mod) + exe_mod->GetFileSpec().GetPath(m_exe_path, PATH_MAX); + } +} + +bool +DYLDRendezvous::Resolve() +{ + const size_t word_size = 4; + Rendezvous info; + size_t address_size; + size_t padding; + addr_t info_addr; + addr_t cursor; + + address_size = m_process->GetAddressByteSize(); + padding = address_size - word_size; + + if (m_rendezvous_addr == LLDB_INVALID_ADDRESS) + cursor = info_addr = ResolveRendezvousAddress(m_process); + else + cursor = info_addr = m_rendezvous_addr; + + if (cursor == LLDB_INVALID_ADDRESS) + return false; + + if (!(cursor = ReadMemory(cursor, &info.version, word_size))) + return false; + + if (!(cursor = ReadMemory(cursor + padding, &info.map_addr, address_size))) + return false; + + if (!(cursor = ReadMemory(cursor, &info.brk, address_size))) + return false; + + if (!(cursor = ReadMemory(cursor, &info.state, word_size))) + return false; + + if (!(cursor = ReadMemory(cursor + padding, &info.ldbase, address_size))) + return false; + + // The rendezvous was successfully read. Update our internal state. + m_rendezvous_addr = info_addr; + m_previous = m_current; + m_current = info; + + return UpdateSOEntries(); +} + +bool +DYLDRendezvous::IsValid() +{ + return m_rendezvous_addr != LLDB_INVALID_ADDRESS; +} + +bool +DYLDRendezvous::UpdateSOEntries() +{ + SOEntry entry; + + if (m_current.map_addr == 0) + return false; + + // When the previous and current states are consistent this is the first + // time we have been asked to update. Just take a snapshot of the currently + // loaded modules. + if (m_previous.state == eConsistent && m_current.state == eConsistent) + return TakeSnapshot(m_soentries); + + // If we are about to add or remove a shared object clear out the current + // state and take a snapshot of the currently loaded images. + if (m_current.state == eAdd || m_current.state == eDelete) + { + assert(m_previous.state == eConsistent); + m_soentries.clear(); + m_added_soentries.clear(); + m_removed_soentries.clear(); + return TakeSnapshot(m_soentries); + } + assert(m_current.state == eConsistent); + + // Otherwise check the previous state to determine what to expect and update + // accordingly. + if (m_previous.state == eAdd) + return UpdateSOEntriesForAddition(); + else if (m_previous.state == eDelete) + return UpdateSOEntriesForDeletion(); + + return false; +} + +bool +DYLDRendezvous::UpdateSOEntriesForAddition() +{ + SOEntry entry; + iterator pos; + + assert(m_previous.state == eAdd); + + if (m_current.map_addr == 0) + return false; + + for (addr_t cursor = m_current.map_addr; cursor != 0; cursor = entry.next) + { + if (!ReadSOEntryFromMemory(cursor, entry)) + return false; + + // Only add shared libraries and not the executable. + // On Linux this is indicated by an empty path in the entry. + // On FreeBSD it is the name of the executable. + if (entry.path.empty() || ::strcmp(entry.path.c_str(), m_exe_path) == 0) + continue; + + pos = std::find(m_soentries.begin(), m_soentries.end(), entry); + if (pos == m_soentries.end()) + { + m_soentries.push_back(entry); + m_added_soentries.push_back(entry); + } + } + + return true; +} + +bool +DYLDRendezvous::UpdateSOEntriesForDeletion() +{ + SOEntryList entry_list; + iterator pos; + + assert(m_previous.state == eDelete); + + if (!TakeSnapshot(entry_list)) + return false; + + for (iterator I = begin(); I != end(); ++I) + { + pos = std::find(entry_list.begin(), entry_list.end(), *I); + if (pos == entry_list.end()) + m_removed_soentries.push_back(*I); + } + + m_soentries = entry_list; + return true; +} + +bool +DYLDRendezvous::TakeSnapshot(SOEntryList &entry_list) +{ + SOEntry entry; + + if (m_current.map_addr == 0) + return false; + + for (addr_t cursor = m_current.map_addr; cursor != 0; cursor = entry.next) + { + if (!ReadSOEntryFromMemory(cursor, entry)) + return false; + + // Only add shared libraries and not the executable. + // On Linux this is indicated by an empty path in the entry. + // On FreeBSD it is the name of the executable. + if (entry.path.empty() || ::strcmp(entry.path.c_str(), m_exe_path) == 0) + continue; + + entry_list.push_back(entry); + } + + return true; +} + +addr_t +DYLDRendezvous::ReadMemory(addr_t addr, void *dst, size_t size) +{ + size_t bytes_read; + Error error; + + bytes_read = m_process->DoReadMemory(addr, dst, size, error); + if (bytes_read != size || error.Fail()) + return 0; + + return addr + bytes_read; +} + +std::string +DYLDRendezvous::ReadStringFromMemory(addr_t addr) +{ + std::string str; + Error error; + size_t size; + char c; + + if (addr == LLDB_INVALID_ADDRESS) + return std::string(); + + for (;;) { + size = m_process->DoReadMemory(addr, &c, 1, error); + if (size != 1 || error.Fail()) + return std::string(); + if (c == 0) + break; + else { + str.push_back(c); + addr++; + } + } + + return str; +} + +bool +DYLDRendezvous::ReadSOEntryFromMemory(lldb::addr_t addr, SOEntry &entry) +{ + size_t address_size = m_process->GetAddressByteSize(); + + entry.clear(); + + if (!(addr = ReadMemory(addr, &entry.base_addr, address_size))) + return false; + + if (!(addr = ReadMemory(addr, &entry.path_addr, address_size))) + return false; + + if (!(addr = ReadMemory(addr, &entry.dyn_addr, address_size))) + return false; + + if (!(addr = ReadMemory(addr, &entry.next, address_size))) + return false; + + if (!(addr = ReadMemory(addr, &entry.prev, address_size))) + return false; + + entry.path = ReadStringFromMemory(entry.path_addr); + + return true; +} + +void +DYLDRendezvous::DumpToLog(Log *log) const +{ + int state = GetState(); + + if (!log) + return; + + log->PutCString("DYLDRendezvous:"); + log->Printf(" Address: %" PRIx64, GetRendezvousAddress()); + log->Printf(" Version: %" PRIu64, GetVersion()); + log->Printf(" Link : %" PRIx64, GetLinkMapAddress()); + log->Printf(" Break : %" PRIx64, GetBreakAddress()); + log->Printf(" LDBase : %" PRIx64, GetLDBase()); + log->Printf(" State : %s", + (state == eConsistent) ? "consistent" : + (state == eAdd) ? "add" : + (state == eDelete) ? "delete" : "unknown"); + + iterator I = begin(); + iterator E = end(); + + if (I != E) + log->PutCString("DYLDRendezvous SOEntries:"); + + for (int i = 1; I != E; ++I, ++i) + { + log->Printf("\n SOEntry [%d] %s", i, I->path.c_str()); + log->Printf(" Base : %" PRIx64, I->base_addr); + log->Printf(" Path : %" PRIx64, I->path_addr); + log->Printf(" Dyn : %" PRIx64, I->dyn_addr); + log->Printf(" Next : %" PRIx64, I->next); + log->Printf(" Prev : %" PRIx64, I->prev); + } +} diff --git a/contrib/llvm/tools/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DYLDRendezvous.h b/contrib/llvm/tools/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DYLDRendezvous.h new file mode 100644 index 00000000000..67e7228a38d --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DYLDRendezvous.h @@ -0,0 +1,230 @@ +//===-- DYLDRendezvous.h ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_Rendezvous_H_ +#define liblldb_Rendezvous_H_ + +// C Includes +// C++ Includes +#include +#include + +// Other libraries and framework includes +#include "lldb/lldb-defines.h" +#include "lldb/lldb-types.h" + +namespace lldb_private { +class Process; +} + +/// @class DYLDRendezvous +/// @brief Interface to the runtime linker. +/// +/// A structure is present in a processes memory space which is updated by the +/// runtime liker each time a module is loaded or unloaded. This class provides +/// an interface to this structure and maintains a consistent snapshot of the +/// currently loaded modules. +class DYLDRendezvous { + + // This structure is used to hold the contents of the debug rendezvous + // information (struct r_debug) as found in the inferiors memory. Note that + // the layout of this struct is not binary compatible, it is simply large + // enough to hold the information on both 32 and 64 bit platforms. + struct Rendezvous { + uint64_t version; + lldb::addr_t map_addr; + lldb::addr_t brk; + uint64_t state; + lldb::addr_t ldbase; + + Rendezvous() + : version(0), map_addr(0), brk(0), state(0), ldbase(0) { } + }; + +public: + DYLDRendezvous(lldb_private::Process *process); + + /// Update the internal snapshot of runtime linker rendezvous and recompute + /// the currently loaded modules. + /// + /// This method should be called once one start up, then once each time the + /// runtime linker enters the function given by GetBreakAddress(). + /// + /// @returns true on success and false on failure. + /// + /// @see GetBreakAddress(). + bool + Resolve(); + + /// @returns true if this rendezvous has been located in the inferiors + /// address space and false otherwise. + bool + IsValid(); + + /// @returns the address of the rendezvous structure in the inferiors + /// address space. + lldb::addr_t + GetRendezvousAddress() const { return m_rendezvous_addr; } + + /// @returns the version of the rendezvous protocol being used. + uint64_t + GetVersion() const { return m_current.version; } + + /// @returns address in the inferiors address space containing the linked + /// list of shared object descriptors. + lldb::addr_t + GetLinkMapAddress() const { return m_current.map_addr; } + + /// A breakpoint should be set at this address and Resolve called on each + /// hit. + /// + /// @returns the address of a function called by the runtime linker each + /// time a module is loaded/unloaded, or about to be loaded/unloaded. + /// + /// @see Resolve() + lldb::addr_t + GetBreakAddress() const { return m_current.brk; } + + /// Returns the current state of the rendezvous structure. + uint64_t + GetState() const { return m_current.state; } + + /// @returns the base address of the runtime linker in the inferiors address + /// space. + lldb::addr_t + GetLDBase() const { return m_current.ldbase; } + + /// @returns true if modules have been loaded into the inferior since the + /// last call to Resolve(). + bool + ModulesDidLoad() const { return !m_added_soentries.empty(); } + + /// @returns true if modules have been unloaded from the inferior since the + /// last call to Resolve(). + bool + ModulesDidUnload() const { return !m_removed_soentries.empty(); } + + void + DumpToLog(lldb_private::Log *log) const; + + /// @brief Constants describing the state of the rendezvous. + /// + /// @see GetState(). + enum RendezvousState { + eConsistent, + eAdd, + eDelete + }; + + /// @brief Structure representing the shared objects currently loaded into + /// the inferior process. + /// + /// This object is a rough analogue to the struct link_map object which + /// actually lives in the inferiors memory. + struct SOEntry { + lldb::addr_t base_addr; ///< Base address of the loaded object. + lldb::addr_t path_addr; ///< String naming the shared object. + lldb::addr_t dyn_addr; ///< Dynamic section of shared object. + lldb::addr_t next; ///< Address of next so_entry. + lldb::addr_t prev; ///< Address of previous so_entry. + std::string path; ///< File name of shared object. + + SOEntry() { clear(); } + + bool operator ==(const SOEntry &entry) { + return this->path == entry.path; + } + + void clear() { + base_addr = 0; + path_addr = 0; + dyn_addr = 0; + next = 0; + prev = 0; + path.clear(); + } + }; + +protected: + typedef std::list SOEntryList; + +public: + typedef SOEntryList::const_iterator iterator; + + /// Iterators over all currently loaded modules. + iterator begin() const { return m_soentries.begin(); } + iterator end() const { return m_soentries.end(); } + + /// Iterators over all modules loaded into the inferior since the last call + /// to Resolve(). + iterator loaded_begin() const { return m_added_soentries.begin(); } + iterator loaded_end() const { return m_added_soentries.end(); } + + /// Iterators over all modules unloaded from the inferior since the last + /// call to Resolve(). + iterator unloaded_begin() const { return m_removed_soentries.begin(); } + iterator unloaded_end() const { return m_removed_soentries.end(); } + +protected: + lldb_private::Process *m_process; + + // Cached copy of executable pathname + char m_exe_path[PATH_MAX]; + + /// Location of the r_debug structure in the inferiors address space. + lldb::addr_t m_rendezvous_addr; + + /// Current and previous snapshots of the rendezvous structure. + Rendezvous m_current; + Rendezvous m_previous; + + /// List of SOEntry objects corresponding to the current link map state. + SOEntryList m_soentries; + + /// List of SOEntry's added to the link map since the last call to Resolve(). + SOEntryList m_added_soentries; + + /// List of SOEntry's removed from the link map since the last call to + /// Resolve(). + SOEntryList m_removed_soentries; + + /// Reads @p size bytes from the inferiors address space starting at @p + /// addr. + /// + /// @returns addr + size if the read was successful and false otherwise. + lldb::addr_t + ReadMemory(lldb::addr_t addr, void *dst, size_t size); + + /// Reads a null-terminated C string from the memory location starting at @p + /// addr. + std::string + ReadStringFromMemory(lldb::addr_t addr); + + /// Reads an SOEntry starting at @p addr. + bool + ReadSOEntryFromMemory(lldb::addr_t addr, SOEntry &entry); + + /// Updates the current set of SOEntries, the set of added entries, and the + /// set of removed entries. + bool + UpdateSOEntries(); + + bool + UpdateSOEntriesForAddition(); + + bool + UpdateSOEntriesForDeletion(); + + /// Reads the current list of shared objects according to the link map + /// supplied by the runtime linker. + bool + TakeSnapshot(SOEntryList &entry_list); +}; + +#endif diff --git a/contrib/llvm/tools/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.cpp b/contrib/llvm/tools/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.cpp new file mode 100644 index 00000000000..91c7cd3dfca --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.cpp @@ -0,0 +1,481 @@ +//===-- DynamicLoaderPOSIX.h ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// C Includes +// C++ Includes +// Other libraries and framework includes +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/ModuleSpec.h" +#include "lldb/Core/Section.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" +#include "lldb/Target/ThreadPlanRunToAddress.h" +#include "lldb/Breakpoint/BreakpointLocation.h" + +#include "AuxVector.h" +#include "DynamicLoaderPOSIXDYLD.h" + +using namespace lldb; +using namespace lldb_private; + +void +DynamicLoaderPOSIXDYLD::Initialize() +{ + PluginManager::RegisterPlugin(GetPluginNameStatic(), + GetPluginDescriptionStatic(), + CreateInstance); +} + +void +DynamicLoaderPOSIXDYLD::Terminate() +{ +} + +lldb_private::ConstString +DynamicLoaderPOSIXDYLD::GetPluginName() +{ + return GetPluginNameStatic(); +} + +lldb_private::ConstString +DynamicLoaderPOSIXDYLD::GetPluginNameStatic() +{ + static ConstString g_name("linux-dyld"); + return g_name; +} + +const char * +DynamicLoaderPOSIXDYLD::GetPluginDescriptionStatic() +{ + return "Dynamic loader plug-in that watches for shared library " + "loads/unloads in POSIX processes."; +} + +void +DynamicLoaderPOSIXDYLD::GetPluginCommandHelp(const char *command, Stream *strm) +{ +} + +uint32_t +DynamicLoaderPOSIXDYLD::GetPluginVersion() +{ + return 1; +} + +DynamicLoader * +DynamicLoaderPOSIXDYLD::CreateInstance(Process *process, bool force) +{ + bool create = force; + if (!create) + { + const llvm::Triple &triple_ref = process->GetTarget().GetArchitecture().GetTriple(); + if (triple_ref.getOS() == llvm::Triple::Linux || + triple_ref.getOS() == llvm::Triple::FreeBSD) + create = true; + } + + if (create) + return new DynamicLoaderPOSIXDYLD (process); + return NULL; +} + +DynamicLoaderPOSIXDYLD::DynamicLoaderPOSIXDYLD(Process *process) + : DynamicLoader(process), + m_rendezvous(process), + m_load_offset(LLDB_INVALID_ADDRESS), + m_entry_point(LLDB_INVALID_ADDRESS), + m_auxv(), + m_dyld_bid(LLDB_INVALID_BREAK_ID) +{ +} + +DynamicLoaderPOSIXDYLD::~DynamicLoaderPOSIXDYLD() +{ + if (m_dyld_bid != LLDB_INVALID_BREAK_ID) + { + m_process->GetTarget().RemoveBreakpointByID (m_dyld_bid); + m_dyld_bid = LLDB_INVALID_BREAK_ID; + } +} + +void +DynamicLoaderPOSIXDYLD::DidAttach() +{ + ModuleSP executable; + addr_t load_offset; + + m_auxv.reset(new AuxVector(m_process)); + + executable = GetTargetExecutable(); + load_offset = ComputeLoadOffset(); + + if (executable.get() && load_offset != LLDB_INVALID_ADDRESS) + { + ModuleList module_list; + module_list.Append(executable); + UpdateLoadedSections(executable, load_offset); + LoadAllCurrentModules(); + m_process->GetTarget().ModulesDidLoad(module_list); + } +} + +void +DynamicLoaderPOSIXDYLD::DidLaunch() +{ + ModuleSP executable; + addr_t load_offset; + + m_auxv.reset(new AuxVector(m_process)); + + executable = GetTargetExecutable(); + load_offset = ComputeLoadOffset(); + + if (executable.get() && load_offset != LLDB_INVALID_ADDRESS) + { + ModuleList module_list; + module_list.Append(executable); + UpdateLoadedSections(executable, load_offset); + ProbeEntry(); + m_process->GetTarget().ModulesDidLoad(module_list); + } +} + +ModuleSP +DynamicLoaderPOSIXDYLD::GetTargetExecutable() +{ + Target &target = m_process->GetTarget(); + ModuleSP executable = target.GetExecutableModule(); + + if (executable.get()) + { + if (executable->GetFileSpec().Exists()) + { + ModuleSpec module_spec (executable->GetFileSpec(), executable->GetArchitecture()); + ModuleSP module_sp (new Module (module_spec)); + + // Check if the executable has changed and set it to the target executable if they differ. + if (module_sp.get() && module_sp->GetUUID().IsValid() && executable->GetUUID().IsValid()) + { + if (module_sp->GetUUID() != executable->GetUUID()) + executable.reset(); + } + else if (executable->FileHasChanged()) + { + executable.reset(); + } + + if (!executable.get()) + { + executable = target.GetSharedModule(module_spec); + if (executable.get() != target.GetExecutableModulePointer()) + { + // Don't load dependent images since we are in dyld where we will know + // and find out about all images that are loaded + const bool get_dependent_images = false; + target.SetExecutableModule(executable, get_dependent_images); + } + } + } + } + return executable; +} + +Error +DynamicLoaderPOSIXDYLD::ExecutePluginCommand(Args &command, Stream *strm) +{ + return Error(); +} + +Log * +DynamicLoaderPOSIXDYLD::EnablePluginLogging(Stream *strm, Args &command) +{ + return NULL; +} + +Error +DynamicLoaderPOSIXDYLD::CanLoadImage() +{ + return Error(); +} + +void +DynamicLoaderPOSIXDYLD::UpdateLoadedSections(ModuleSP module, addr_t base_addr) +{ + ObjectFile *obj_file = module->GetObjectFile(); + SectionList *sections = obj_file->GetSectionList(); + SectionLoadList &load_list = m_process->GetTarget().GetSectionLoadList(); + const size_t num_sections = sections->GetSize(); + + for (unsigned i = 0; i < num_sections; ++i) + { + SectionSP section_sp (sections->GetSectionAtIndex(i)); + lldb::addr_t new_load_addr = section_sp->GetFileAddress() + base_addr; + lldb::addr_t old_load_addr = load_list.GetSectionLoadAddress(section_sp); + + // If the file address of the section is zero then this is not an + // allocatable/loadable section (property of ELF sh_addr). Skip it. + if (new_load_addr == base_addr) + continue; + + if (old_load_addr == LLDB_INVALID_ADDRESS || + old_load_addr != new_load_addr) + load_list.SetSectionLoadAddress(section_sp, new_load_addr); + } +} + +void +DynamicLoaderPOSIXDYLD::ProbeEntry() +{ + Breakpoint *entry_break; + addr_t entry; + + if ((entry = GetEntryPoint()) == LLDB_INVALID_ADDRESS) + return; + + entry_break = m_process->GetTarget().CreateBreakpoint(entry, true).get(); + entry_break->SetCallback(EntryBreakpointHit, this, true); + entry_break->SetBreakpointKind("shared-library-event"); +} + +// The runtime linker has run and initialized the rendezvous structure once the +// process has hit its entry point. When we hit the corresponding breakpoint we +// interrogate the rendezvous structure to get the load addresses of all +// dependent modules for the process. Similarly, we can discover the runtime +// linker function and setup a breakpoint to notify us of any dynamically loaded +// modules (via dlopen). +bool +DynamicLoaderPOSIXDYLD::EntryBreakpointHit(void *baton, + StoppointCallbackContext *context, + user_id_t break_id, + user_id_t break_loc_id) +{ + DynamicLoaderPOSIXDYLD* dyld_instance; + + dyld_instance = static_cast(baton); + dyld_instance->LoadAllCurrentModules(); + dyld_instance->SetRendezvousBreakpoint(); + return false; // Continue running. +} + +void +DynamicLoaderPOSIXDYLD::SetRendezvousBreakpoint() +{ + addr_t break_addr = m_rendezvous.GetBreakAddress(); + Target &target = m_process->GetTarget(); + + if (m_dyld_bid == LLDB_INVALID_BREAK_ID) + { + Breakpoint *dyld_break = target.CreateBreakpoint (break_addr, true).get(); + dyld_break->SetCallback(RendezvousBreakpointHit, this, true); + dyld_break->SetBreakpointKind ("shared-library-event"); + m_dyld_bid = dyld_break->GetID(); + } + + // Make sure our breakpoint is at the right address. + assert (target.GetBreakpointByID(m_dyld_bid)->FindLocationByAddress(break_addr)->GetBreakpoint().GetID() == m_dyld_bid); +} + +bool +DynamicLoaderPOSIXDYLD::RendezvousBreakpointHit(void *baton, + StoppointCallbackContext *context, + user_id_t break_id, + user_id_t break_loc_id) +{ + DynamicLoaderPOSIXDYLD* dyld_instance; + + dyld_instance = static_cast(baton); + dyld_instance->RefreshModules(); + + // Return true to stop the target, false to just let the target run. + return dyld_instance->GetStopWhenImagesChange(); +} + +void +DynamicLoaderPOSIXDYLD::RefreshModules() +{ + if (!m_rendezvous.Resolve()) + return; + + DYLDRendezvous::iterator I; + DYLDRendezvous::iterator E; + + ModuleList &loaded_modules = m_process->GetTarget().GetImages(); + + if (m_rendezvous.ModulesDidLoad()) + { + ModuleList new_modules; + + E = m_rendezvous.loaded_end(); + for (I = m_rendezvous.loaded_begin(); I != E; ++I) + { + FileSpec file(I->path.c_str(), true); + ModuleSP module_sp = LoadModuleAtAddress(file, I->base_addr); + if (module_sp.get()) + loaded_modules.AppendIfNeeded(module_sp); + } + } + + if (m_rendezvous.ModulesDidUnload()) + { + ModuleList old_modules; + + E = m_rendezvous.unloaded_end(); + for (I = m_rendezvous.unloaded_begin(); I != E; ++I) + { + FileSpec file(I->path.c_str(), true); + ModuleSpec module_spec (file); + ModuleSP module_sp = + loaded_modules.FindFirstModule (module_spec); + if (module_sp.get()) + old_modules.Append(module_sp); + } + loaded_modules.Remove(old_modules); + } +} + +ThreadPlanSP +DynamicLoaderPOSIXDYLD::GetStepThroughTrampolinePlan(Thread &thread, bool stop) +{ + ThreadPlanSP thread_plan_sp; + + StackFrame *frame = thread.GetStackFrameAtIndex(0).get(); + const SymbolContext &context = frame->GetSymbolContext(eSymbolContextSymbol); + Symbol *sym = context.symbol; + + if (sym == NULL || !sym->IsTrampoline()) + return thread_plan_sp; + + const ConstString &sym_name = sym->GetMangled().GetName(Mangled::ePreferMangled); + if (!sym_name) + return thread_plan_sp; + + SymbolContextList target_symbols; + Target &target = thread.GetProcess()->GetTarget(); + const ModuleList &images = target.GetImages(); + + images.FindSymbolsWithNameAndType(sym_name, eSymbolTypeCode, target_symbols); + size_t num_targets = target_symbols.GetSize(); + if (!num_targets) + return thread_plan_sp; + + typedef std::vector AddressVector; + AddressVector addrs; + for (size_t i = 0; i < num_targets; ++i) + { + SymbolContext context; + AddressRange range; + if (target_symbols.GetContextAtIndex(i, context)) + { + context.GetAddressRange(eSymbolContextEverything, 0, false, range); + lldb::addr_t addr = range.GetBaseAddress().GetLoadAddress(&target); + if (addr != LLDB_INVALID_ADDRESS) + addrs.push_back(addr); + } + } + + if (addrs.size() > 0) + { + AddressVector::iterator start = addrs.begin(); + AddressVector::iterator end = addrs.end(); + + std::sort(start, end); + addrs.erase(std::unique(start, end), end); + thread_plan_sp.reset(new ThreadPlanRunToAddress(thread, addrs, stop)); + } + + return thread_plan_sp; +} + +void +DynamicLoaderPOSIXDYLD::LoadAllCurrentModules() +{ + DYLDRendezvous::iterator I; + DYLDRendezvous::iterator E; + ModuleList module_list; + + if (!m_rendezvous.Resolve()) + return; + + for (I = m_rendezvous.begin(), E = m_rendezvous.end(); I != E; ++I) + { + FileSpec file(I->path.c_str(), false); + ModuleSP module_sp = LoadModuleAtAddress(file, I->base_addr); + if (module_sp.get()) + module_list.Append(module_sp); + } + + m_process->GetTarget().ModulesDidLoad(module_list); +} + +ModuleSP +DynamicLoaderPOSIXDYLD::LoadModuleAtAddress(const FileSpec &file, addr_t base_addr) +{ + Target &target = m_process->GetTarget(); + ModuleList &modules = target.GetImages(); + ModuleSP module_sp; + + ModuleSpec module_spec (file, target.GetArchitecture()); + if ((module_sp = modules.FindFirstModule (module_spec))) + { + UpdateLoadedSections(module_sp, base_addr); + } + else if ((module_sp = target.GetSharedModule(module_spec))) + { + UpdateLoadedSections(module_sp, base_addr); + } + + return module_sp; +} + +addr_t +DynamicLoaderPOSIXDYLD::ComputeLoadOffset() +{ + addr_t virt_entry; + + if (m_load_offset != LLDB_INVALID_ADDRESS) + return m_load_offset; + + if ((virt_entry = GetEntryPoint()) == LLDB_INVALID_ADDRESS) + return LLDB_INVALID_ADDRESS; + + ModuleSP module = m_process->GetTarget().GetExecutableModule(); + if (!module) + return LLDB_INVALID_ADDRESS; + + ObjectFile *exe = module->GetObjectFile(); + Address file_entry = exe->GetEntryPointAddress(); + + if (!file_entry.IsValid()) + return LLDB_INVALID_ADDRESS; + + m_load_offset = virt_entry - file_entry.GetFileAddress(); + return m_load_offset; +} + +addr_t +DynamicLoaderPOSIXDYLD::GetEntryPoint() +{ + if (m_entry_point != LLDB_INVALID_ADDRESS) + return m_entry_point; + + if (m_auxv.get() == NULL) + return LLDB_INVALID_ADDRESS; + + AuxVector::iterator I = m_auxv->FindEntry(AuxVector::AT_ENTRY); + + if (I == m_auxv->end()) + return LLDB_INVALID_ADDRESS; + + m_entry_point = static_cast(I->value); + return m_entry_point; +} diff --git a/contrib/llvm/tools/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.h b/contrib/llvm/tools/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.h new file mode 100644 index 00000000000..0476e45d046 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.h @@ -0,0 +1,170 @@ +//===-- DynamicLoaderPOSIX.h ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_DynamicLoaderPOSIX_H_ +#define liblldb_DynamicLoaderPOSIX_H_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +#include "lldb/Breakpoint/StoppointCallbackContext.h" +#include "lldb/Target/DynamicLoader.h" + +#include "DYLDRendezvous.h" + +class AuxVector; + +class DynamicLoaderPOSIXDYLD : public lldb_private::DynamicLoader +{ +public: + + static void + Initialize(); + + static void + Terminate(); + + static lldb_private::ConstString + GetPluginNameStatic(); + + static const char * + GetPluginDescriptionStatic(); + + static lldb_private::DynamicLoader * + CreateInstance(lldb_private::Process *process, bool force); + + DynamicLoaderPOSIXDYLD(lldb_private::Process *process); + + virtual + ~DynamicLoaderPOSIXDYLD(); + + //------------------------------------------------------------------ + // DynamicLoader protocol + //------------------------------------------------------------------ + + virtual void + DidAttach(); + + virtual void + DidLaunch(); + + virtual lldb::ThreadPlanSP + GetStepThroughTrampolinePlan(lldb_private::Thread &thread, + bool stop_others); + + virtual lldb_private::Error + CanLoadImage(); + + //------------------------------------------------------------------ + // PluginInterface protocol + //------------------------------------------------------------------ + virtual lldb_private::ConstString + GetPluginName(); + + virtual uint32_t + GetPluginVersion(); + + virtual void + GetPluginCommandHelp(const char *command, lldb_private::Stream *strm); + + virtual lldb_private::Error + ExecutePluginCommand(lldb_private::Args &command, lldb_private::Stream *strm); + + virtual lldb_private::Log * + EnablePluginLogging(lldb_private::Stream *strm, lldb_private::Args &command); + +protected: + /// Runtime linker rendezvous structure. + DYLDRendezvous m_rendezvous; + + /// Virtual load address of the inferior process. + lldb::addr_t m_load_offset; + + /// Virtual entry address of the inferior process. + lldb::addr_t m_entry_point; + + /// Auxiliary vector of the inferior process. + std::unique_ptr m_auxv; + + /// Rendezvous breakpoint. + lldb::break_id_t m_dyld_bid; + + /// Enables a breakpoint on a function called by the runtime + /// linker each time a module is loaded or unloaded. + void + SetRendezvousBreakpoint(); + + /// Callback routine which updates the current list of loaded modules based + /// on the information supplied by the runtime linker. + static bool + RendezvousBreakpointHit(void *baton, + lldb_private::StoppointCallbackContext *context, + lldb::user_id_t break_id, + lldb::user_id_t break_loc_id); + + /// Helper method for RendezvousBreakpointHit. Updates LLDB's current set + /// of loaded modules. + void + RefreshModules(); + + /// Updates the load address of every allocatable section in @p module. + /// + /// @param module The module to traverse. + /// + /// @param base_addr The virtual base address @p module is loaded at. + void + UpdateLoadedSections(lldb::ModuleSP module, + lldb::addr_t base_addr = 0); + + /// Locates or creates a module given by @p file and updates/loads the + /// resulting module at the virtual base address @p base_addr. + lldb::ModuleSP + LoadModuleAtAddress(const lldb_private::FileSpec &file, lldb::addr_t base_addr); + + /// Resolves the entry point for the current inferior process and sets a + /// breakpoint at that address. + void + ProbeEntry(); + + /// Callback routine invoked when we hit the breakpoint on process entry. + /// + /// This routine is responsible for resolving the load addresses of all + /// dependent modules required by the inferior and setting up the rendezvous + /// breakpoint. + static bool + EntryBreakpointHit(void *baton, + lldb_private::StoppointCallbackContext *context, + lldb::user_id_t break_id, + lldb::user_id_t break_loc_id); + + /// Helper for the entry breakpoint callback. Resolves the load addresses + /// of all dependent modules. + void + LoadAllCurrentModules(); + + /// Computes a value for m_load_offset returning the computed address on + /// success and LLDB_INVALID_ADDRESS on failure. + lldb::addr_t + ComputeLoadOffset(); + + /// Computes a value for m_entry_point returning the computed address on + /// success and LLDB_INVALID_ADDRESS on failure. + lldb::addr_t + GetEntryPoint(); + + /// Checks to see if the target module has changed, updates the target + /// accordingly and returns the target executable module. + lldb::ModuleSP + GetTargetExecutable(); + +private: + DISALLOW_COPY_AND_ASSIGN(DynamicLoaderPOSIXDYLD); +}; + +#endif // liblldb_DynamicLoaderPOSIXDYLD_H_ diff --git a/contrib/llvm/tools/lldb/source/Plugins/DynamicLoader/Static/DynamicLoaderStatic.cpp b/contrib/llvm/tools/lldb/source/Plugins/DynamicLoader/Static/DynamicLoaderStatic.cpp new file mode 100644 index 00000000000..274ba328ad1 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Plugins/DynamicLoader/Static/DynamicLoaderStatic.cpp @@ -0,0 +1,209 @@ +//===-- DynamicLoaderStatic.cpp ---------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Core/Module.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/Section.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Target/Target.h" + +#include "DynamicLoaderStatic.h" + +using namespace lldb; +using namespace lldb_private; + +//---------------------------------------------------------------------- +// Create an instance of this class. This function is filled into +// the plugin info class that gets handed out by the plugin factory and +// allows the lldb to instantiate an instance of this class. +//---------------------------------------------------------------------- +DynamicLoader * +DynamicLoaderStatic::CreateInstance (Process* process, bool force) +{ + bool create = force; + if (!create) + { + const llvm::Triple &triple_ref = process->GetTarget().GetArchitecture().GetTriple(); + const llvm::Triple::OSType os_type = triple_ref.getOS(); + if ((os_type == llvm::Triple::UnknownOS)) + create = true; + } + + if (!create) + { + Module *exe_module = process->GetTarget().GetExecutableModulePointer(); + if (exe_module) + { + ObjectFile *object_file = exe_module->GetObjectFile(); + if (object_file) + { + create = (object_file->GetStrata() == ObjectFile::eStrataRawImage); + } + } + } + + if (create) + return new DynamicLoaderStatic (process); + return NULL; +} + +//---------------------------------------------------------------------- +// Constructor +//---------------------------------------------------------------------- +DynamicLoaderStatic::DynamicLoaderStatic (Process* process) : + DynamicLoader(process) +{ +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +DynamicLoaderStatic::~DynamicLoaderStatic() +{ +} + +//------------------------------------------------------------------ +/// Called after attaching a process. +/// +/// Allow DynamicLoader plug-ins to execute some code after +/// attaching to a process. +//------------------------------------------------------------------ +void +DynamicLoaderStatic::DidAttach () +{ + LoadAllImagesAtFileAddresses(); +} + +//------------------------------------------------------------------ +/// Called after attaching a process. +/// +/// Allow DynamicLoader plug-ins to execute some code after +/// attaching to a process. +//------------------------------------------------------------------ +void +DynamicLoaderStatic::DidLaunch () +{ + LoadAllImagesAtFileAddresses(); +} + +void +DynamicLoaderStatic::LoadAllImagesAtFileAddresses () +{ + const ModuleList &module_list = m_process->GetTarget().GetImages(); + + ModuleList loaded_module_list; + + // Disable JIT for static dynamic loader targets + m_process->SetCanJIT(false); + + Mutex::Locker mutex_locker(module_list.GetMutex()); + + const size_t num_modules = module_list.GetSize(); + for (uint32_t idx = 0; idx < num_modules; ++idx) + { + ModuleSP module_sp (module_list.GetModuleAtIndexUnlocked (idx)); + if (module_sp) + { + bool changed = false; + ObjectFile *image_object_file = module_sp->GetObjectFile(); + if (image_object_file) + { + SectionList *section_list = image_object_file->GetSectionList (); + if (section_list) + { + // All sections listed in the dyld image info structure will all + // either be fixed up already, or they will all be off by a single + // slide amount that is determined by finding the first segment + // that is at file offset zero which also has bytes (a file size + // that is greater than zero) in the object file. + + // Determine the slide amount (if any) + const size_t num_sections = section_list->GetSize(); + size_t sect_idx = 0; + for (sect_idx = 0; sect_idx < num_sections; ++sect_idx) + { + // Iterate through the object file sections to find the + // first section that starts of file offset zero and that + // has bytes in the file... + SectionSP section_sp (section_list->GetSectionAtIndex (sect_idx)); + if (section_sp) + { + if (m_process->GetTarget().GetSectionLoadList().SetSectionLoadAddress (section_sp, section_sp->GetFileAddress())) + changed = true; + } + } + } + } + + if (changed) + loaded_module_list.AppendIfNeeded (module_sp); + } + } + + m_process->GetTarget().ModulesDidLoad (loaded_module_list); +} + +ThreadPlanSP +DynamicLoaderStatic::GetStepThroughTrampolinePlan (Thread &thread, bool stop_others) +{ + return ThreadPlanSP(); +} + +Error +DynamicLoaderStatic::CanLoadImage () +{ + Error error; + error.SetErrorString ("can't load images on with a static debug session"); + return error; +} + +void +DynamicLoaderStatic::Initialize() +{ + PluginManager::RegisterPlugin (GetPluginNameStatic(), + GetPluginDescriptionStatic(), + CreateInstance); +} + +void +DynamicLoaderStatic::Terminate() +{ + PluginManager::UnregisterPlugin (CreateInstance); +} + + +lldb_private::ConstString +DynamicLoaderStatic::GetPluginNameStatic() +{ + static ConstString g_name("static"); + return g_name; +} + +const char * +DynamicLoaderStatic::GetPluginDescriptionStatic() +{ + return "Dynamic loader plug-in that will load any images at the static addresses contained in each image."; +} + + +//------------------------------------------------------------------ +// PluginInterface protocol +//------------------------------------------------------------------ +lldb_private::ConstString +DynamicLoaderStatic::GetPluginName() +{ + return GetPluginNameStatic(); +} + +uint32_t +DynamicLoaderStatic::GetPluginVersion() +{ + return 1; +} + diff --git a/contrib/llvm/tools/lldb/source/Plugins/DynamicLoader/Static/DynamicLoaderStatic.h b/contrib/llvm/tools/lldb/source/Plugins/DynamicLoader/Static/DynamicLoaderStatic.h new file mode 100644 index 00000000000..a99435fa32a --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Plugins/DynamicLoader/Static/DynamicLoaderStatic.h @@ -0,0 +1,88 @@ +//===-- DynamicLoaderStatic.h -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_DynamicLoaderStatic_h_ +#define liblldb_DynamicLoaderStatic_h_ + +// C Includes +// C++ Includes +#include +#include +#include + +// Other libraries and framework includes +#include "llvm/Support/MachO.h" + +#include "lldb/Target/DynamicLoader.h" +#include "lldb/Host/FileSpec.h" +#include "lldb/Core/UUID.h" +#include "lldb/Host/Mutex.h" +#include "lldb/Target/Process.h" + +class DynamicLoaderStatic : public lldb_private::DynamicLoader +{ +public: + //------------------------------------------------------------------ + // Static Functions + //------------------------------------------------------------------ + static void + Initialize(); + + static void + Terminate(); + + static lldb_private::ConstString + GetPluginNameStatic(); + + static const char * + GetPluginDescriptionStatic(); + + static lldb_private::DynamicLoader * + CreateInstance (lldb_private::Process *process, bool force); + + DynamicLoaderStatic (lldb_private::Process *process); + + virtual + ~DynamicLoaderStatic (); + //------------------------------------------------------------------ + /// Called after attaching a process. + /// + /// Allow DynamicLoader plug-ins to execute some code after + /// attaching to a process. + //------------------------------------------------------------------ + virtual void + DidAttach (); + + virtual void + DidLaunch (); + + virtual lldb::ThreadPlanSP + GetStepThroughTrampolinePlan (lldb_private::Thread &thread, + bool stop_others); + + virtual lldb_private::Error + CanLoadImage (); + + //------------------------------------------------------------------ + // PluginInterface protocol + //------------------------------------------------------------------ + virtual lldb_private::ConstString + GetPluginName(); + + virtual uint32_t + GetPluginVersion(); + +private: + void + LoadAllImagesAtFileAddresses (); + + DISALLOW_COPY_AND_ASSIGN (DynamicLoaderStatic); +}; + +#endif // liblldb_DynamicLoaderStatic_h_ diff --git a/contrib/llvm/tools/lldb/source/Plugins/Instruction/ARM/EmulateInstructionARM.cpp b/contrib/llvm/tools/lldb/source/Plugins/Instruction/ARM/EmulateInstructionARM.cpp new file mode 100644 index 00000000000..52a7fb0e8ee --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Plugins/Instruction/ARM/EmulateInstructionARM.cpp @@ -0,0 +1,13625 @@ +//===-- EmulateInstructionARM.cpp -------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include + +#include "EmulateInstructionARM.h" +#include "EmulationStateARM.h" +#include "lldb/Core/ArchSpec.h" +#include "lldb/Core/Address.h" +#include "lldb/Core/ConstString.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/Stream.h" +#include "lldb/Interpreter/OptionValueArray.h" +#include "lldb/Interpreter/OptionValueDictionary.h" +#include "lldb/Symbol/UnwindPlan.h" + +#include "Plugins/Process/Utility/ARMDefines.h" +#include "Plugins/Process/Utility/ARMUtils.h" +#include "Utility/ARM_DWARF_Registers.h" + +#include "llvm/Support/MathExtras.h" // for SignExtend32 template function + // and CountTrailingZeros_32 function + +using namespace lldb; +using namespace lldb_private; + +// Convenient macro definitions. +#define APSR_C Bit32(m_opcode_cpsr, CPSR_C_POS) +#define APSR_V Bit32(m_opcode_cpsr, CPSR_V_POS) + +#define AlignPC(pc_val) (pc_val & 0xFFFFFFFC) + +//---------------------------------------------------------------------- +// +// ITSession implementation +// +//---------------------------------------------------------------------- + +// A8.6.50 +// Valid return values are {1, 2, 3, 4}, with 0 signifying an error condition. +static uint32_t +CountITSize (uint32_t ITMask) { + // First count the trailing zeros of the IT mask. + uint32_t TZ = llvm::CountTrailingZeros_32(ITMask); + if (TZ > 3) + { +#ifdef LLDB_CONFIGURATION_DEBUG + printf("Encoding error: IT Mask '0000'\n"); +#endif + return 0; + } + return (4 - TZ); +} + +// Init ITState. Note that at least one bit is always 1 in mask. +bool ITSession::InitIT(uint32_t bits7_0) +{ + ITCounter = CountITSize(Bits32(bits7_0, 3, 0)); + if (ITCounter == 0) + return false; + + // A8.6.50 IT + unsigned short FirstCond = Bits32(bits7_0, 7, 4); + if (FirstCond == 0xF) + { +#ifdef LLDB_CONFIGURATION_DEBUG + printf("Encoding error: IT FirstCond '1111'\n"); +#endif + return false; + } + if (FirstCond == 0xE && ITCounter != 1) + { +#ifdef LLDB_CONFIGURATION_DEBUG + printf("Encoding error: IT FirstCond '1110' && Mask != '1000'\n"); +#endif + return false; + } + + ITState = bits7_0; + return true; +} + +// Update ITState if necessary. +void ITSession::ITAdvance() +{ + //assert(ITCounter); + --ITCounter; + if (ITCounter == 0) + ITState = 0; + else + { + unsigned short NewITState4_0 = Bits32(ITState, 4, 0) << 1; + SetBits32(ITState, 4, 0, NewITState4_0); + } +} + +// Return true if we're inside an IT Block. +bool ITSession::InITBlock() +{ + return ITCounter != 0; +} + +// Return true if we're the last instruction inside an IT Block. +bool ITSession::LastInITBlock() +{ + return ITCounter == 1; +} + +// Get condition bits for the current thumb instruction. +uint32_t ITSession::GetCond() +{ + if (InITBlock()) + return Bits32(ITState, 7, 4); + else + return COND_AL; +} + +// ARM constants used during decoding +#define REG_RD 0 +#define LDM_REGLIST 1 +#define SP_REG 13 +#define LR_REG 14 +#define PC_REG 15 +#define PC_REGLIST_BIT 0x8000 + +#define ARMv4 (1u << 0) +#define ARMv4T (1u << 1) +#define ARMv5T (1u << 2) +#define ARMv5TE (1u << 3) +#define ARMv5TEJ (1u << 4) +#define ARMv6 (1u << 5) +#define ARMv6K (1u << 6) +#define ARMv6T2 (1u << 7) +#define ARMv7 (1u << 8) +#define ARMv7S (1u << 9) +#define ARMv8 (1u << 10) +#define ARMvAll (0xffffffffu) + +#define ARMV4T_ABOVE (ARMv4T|ARMv5T|ARMv5TE|ARMv5TEJ|ARMv6|ARMv6K|ARMv6T2|ARMv7|ARMv7S|ARMv8) +#define ARMV5_ABOVE (ARMv5T|ARMv5TE|ARMv5TEJ|ARMv6|ARMv6K|ARMv6T2|ARMv7|ARMv7S|ARMv8) +#define ARMV5TE_ABOVE (ARMv5TE|ARMv5TEJ|ARMv6|ARMv6K|ARMv6T2|ARMv7|ARMv7S|ARMv8) +#define ARMV5J_ABOVE (ARMv5TEJ|ARMv6|ARMv6K|ARMv6T2|ARMv7|ARMv7S|ARMv8) +#define ARMV6_ABOVE (ARMv6|ARMv6K|ARMv6T2|ARMv7|ARMv7S|ARMv8) +#define ARMV6T2_ABOVE (ARMv6T2|ARMv7|ARMv7S|ARMv8) +#define ARMV7_ABOVE (ARMv7|ARMv7S|ARMv8) + +#define No_VFP 0 +#define VFPv1 (1u << 1) +#define VFPv2 (1u << 2) +#define VFPv3 (1u << 3) +#define AdvancedSIMD (1u << 4) + +#define VFPv1_ABOVE (VFPv1 | VFPv2 | VFPv3 | AdvancedSIMD) +#define VFPv2_ABOVE (VFPv2 | VFPv3 | AdvancedSIMD) +#define VFPv2v3 (VFPv2 | VFPv3) + +//---------------------------------------------------------------------- +// +// EmulateInstructionARM implementation +// +//---------------------------------------------------------------------- + +void +EmulateInstructionARM::Initialize () +{ + PluginManager::RegisterPlugin (GetPluginNameStatic (), + GetPluginDescriptionStatic (), + CreateInstance); +} + +void +EmulateInstructionARM::Terminate () +{ + PluginManager::UnregisterPlugin (CreateInstance); +} + +ConstString +EmulateInstructionARM::GetPluginNameStatic () +{ + static ConstString g_name("arm"); + return g_name; +} + +const char * +EmulateInstructionARM::GetPluginDescriptionStatic () +{ + return "Emulate instructions for the ARM architecture."; +} + +EmulateInstruction * +EmulateInstructionARM::CreateInstance (const ArchSpec &arch, InstructionType inst_type) +{ + if (EmulateInstructionARM::SupportsEmulatingIntructionsOfTypeStatic(inst_type)) + { + if (arch.GetTriple().getArch() == llvm::Triple::arm) + { + std::unique_ptr emulate_insn_ap (new EmulateInstructionARM (arch)); + + if (emulate_insn_ap.get()) + return emulate_insn_ap.release(); + } + else if (arch.GetTriple().getArch() == llvm::Triple::thumb) + { + std::unique_ptr emulate_insn_ap (new EmulateInstructionARM (arch)); + + if (emulate_insn_ap.get()) + return emulate_insn_ap.release(); + } + } + + return NULL; +} + +bool +EmulateInstructionARM::SetTargetTriple (const ArchSpec &arch) +{ + if (arch.GetTriple().getArch () == llvm::Triple::arm) + return true; + else if (arch.GetTriple().getArch () == llvm::Triple::thumb) + return true; + + return false; +} + +// Write "bits (32) UNKNOWN" to memory address "address". Helper function for many ARM instructions. +bool +EmulateInstructionARM::WriteBits32UnknownToMemory (addr_t address) +{ + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextWriteMemoryRandomBits; + context.SetNoArgs (); + + uint32_t random_data = rand (); + const uint32_t addr_byte_size = GetAddressByteSize(); + + if (!MemAWrite (context, address, random_data, addr_byte_size)) + return false; + + return true; +} + +// Write "bits (32) UNKNOWN" to register n. Helper function for many ARM instructions. +bool +EmulateInstructionARM::WriteBits32Unknown (int n) +{ + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextWriteRegisterRandomBits; + context.SetNoArgs (); + + bool success; + uint32_t data = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_r0 + n, 0, &success); + + if (!success) + return false; + + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + n, data)) + return false; + + return true; +} + +bool +EmulateInstructionARM::GetRegisterInfo (uint32_t reg_kind, uint32_t reg_num, RegisterInfo ®_info) +{ + if (reg_kind == eRegisterKindGeneric) + { + switch (reg_num) + { + case LLDB_REGNUM_GENERIC_PC: reg_kind = eRegisterKindDWARF; reg_num = dwarf_pc; break; + case LLDB_REGNUM_GENERIC_SP: reg_kind = eRegisterKindDWARF; reg_num = dwarf_sp; break; + case LLDB_REGNUM_GENERIC_FP: reg_kind = eRegisterKindDWARF; reg_num = dwarf_r7; break; + case LLDB_REGNUM_GENERIC_RA: reg_kind = eRegisterKindDWARF; reg_num = dwarf_lr; break; + case LLDB_REGNUM_GENERIC_FLAGS: reg_kind = eRegisterKindDWARF; reg_num = dwarf_cpsr; break; + default: return false; + } + } + + if (reg_kind == eRegisterKindDWARF) + return GetARMDWARFRegisterInfo(reg_num, reg_info); + return false; +} + +uint32_t +EmulateInstructionARM::GetFramePointerRegisterNumber () const +{ + if (m_opcode_mode == eModeThumb) + { + switch (m_arch.GetTriple().getOS()) + { + case llvm::Triple::Darwin: + case llvm::Triple::MacOSX: + case llvm::Triple::IOS: + return 7; + default: + break; + } + } + return 11; +} + +uint32_t +EmulateInstructionARM::GetFramePointerDWARFRegisterNumber () const +{ + if (m_opcode_mode == eModeThumb) + { + switch (m_arch.GetTriple().getOS()) + { + case llvm::Triple::Darwin: + case llvm::Triple::MacOSX: + case llvm::Triple::IOS: + return dwarf_r7; + default: + break; + } + } + return dwarf_r11; +} + +// Push Multiple Registers stores multiple registers to the stack, storing to +// consecutive memory locations ending just below the address in SP, and updates +// SP to point to the start of the stored data. +bool +EmulateInstructionARM::EmulatePUSH (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + // ARM pseudo code... + if (ConditionPassed()) + { + EncodingSpecificOperations(); + NullCheckIfThumbEE(13); + address = SP - 4*BitCount(registers); + + for (i = 0 to 14) + { + if (registers == '1') + { + if i == 13 && i != LowestSetBit(registers) // Only possible for encoding A1 + MemA[address,4] = bits(32) UNKNOWN; + else + MemA[address,4] = R[i]; + address = address + 4; + } + } + + if (registers<15> == '1') // Only possible for encoding A1 or A2 + MemA[address,4] = PCStoreValue(); + + SP = SP - 4*BitCount(registers); + } +#endif + + bool conditional = false; + bool success = false; + if (ConditionPassed(opcode, &conditional)) + { + const uint32_t addr_byte_size = GetAddressByteSize(); + const addr_t sp = ReadCoreReg (SP_REG, &success); + if (!success) + return false; + uint32_t registers = 0; + uint32_t Rt; // the source register + switch (encoding) { + case eEncodingT1: + registers = Bits32(opcode, 7, 0); + // The M bit represents LR. + if (Bit32(opcode, 8)) + registers |= (1u << 14); + // if BitCount(registers) < 1 then UNPREDICTABLE; + if (BitCount(registers) < 1) + return false; + break; + case eEncodingT2: + // Ignore bits 15 & 13. + registers = Bits32(opcode, 15, 0) & ~0xa000; + // if BitCount(registers) < 2 then UNPREDICTABLE; + if (BitCount(registers) < 2) + return false; + break; + case eEncodingT3: + Rt = Bits32(opcode, 15, 12); + // if BadReg(t) then UNPREDICTABLE; + if (BadReg(Rt)) + return false; + registers = (1u << Rt); + break; + case eEncodingA1: + registers = Bits32(opcode, 15, 0); + // Instead of return false, let's handle the following case as well, + // which amounts to pushing one reg onto the full descending stacks. + // if BitCount(register_list) < 2 then SEE STMDB / STMFD; + break; + case eEncodingA2: + Rt = Bits32(opcode, 15, 12); + // if t == 13 then UNPREDICTABLE; + if (Rt == dwarf_sp) + return false; + registers = (1u << Rt); + break; + default: + return false; + } + addr_t sp_offset = addr_byte_size * BitCount (registers); + addr_t addr = sp - sp_offset; + uint32_t i; + + EmulateInstruction::Context context; + if (conditional) + context.type = EmulateInstruction::eContextRegisterStore; + else + context.type = EmulateInstruction::eContextPushRegisterOnStack; + RegisterInfo reg_info; + RegisterInfo sp_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_sp, sp_reg); + for (i=0; i<15; ++i) + { + if (BitIsSet (registers, i)) + { + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + i, reg_info); + context.SetRegisterToRegisterPlusOffset (reg_info, sp_reg, addr - sp); + uint32_t reg_value = ReadCoreReg(i, &success); + if (!success) + return false; + if (!MemAWrite (context, addr, reg_value, addr_byte_size)) + return false; + addr += addr_byte_size; + } + } + + if (BitIsSet (registers, 15)) + { + GetRegisterInfo (eRegisterKindDWARF, dwarf_pc, reg_info); + context.SetRegisterToRegisterPlusOffset (reg_info, sp_reg, addr - sp); + const uint32_t pc = ReadCoreReg(PC_REG, &success); + if (!success) + return false; + if (!MemAWrite (context, addr, pc, addr_byte_size)) + return false; + } + + context.type = EmulateInstruction::eContextAdjustStackPointer; + context.SetImmediateSigned (-sp_offset); + + if (!WriteRegisterUnsigned (context, eRegisterKindGeneric, LLDB_REGNUM_GENERIC_SP, sp - sp_offset)) + return false; + } + return true; +} + +// Pop Multiple Registers loads multiple registers from the stack, loading from +// consecutive memory locations staring at the address in SP, and updates +// SP to point just above the loaded data. +bool +EmulateInstructionARM::EmulatePOP (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + // ARM pseudo code... + if (ConditionPassed()) + { + EncodingSpecificOperations(); NullCheckIfThumbEE(13); + address = SP; + for i = 0 to 14 + if registers == '1' then + R[i] = if UnalignedAllowed then MemU[address,4] else MemA[address,4]; address = address + 4; + if registers<15> == '1' then + if UnalignedAllowed then + LoadWritePC(MemU[address,4]); + else + LoadWritePC(MemA[address,4]); + if registers<13> == '0' then SP = SP + 4*BitCount(registers); + if registers<13> == '1' then SP = bits(32) UNKNOWN; + } +#endif + + bool success = false; + + bool conditional = false; + if (ConditionPassed(opcode, &conditional)) + { + const uint32_t addr_byte_size = GetAddressByteSize(); + const addr_t sp = ReadCoreReg (SP_REG, &success); + if (!success) + return false; + uint32_t registers = 0; + uint32_t Rt; // the destination register + switch (encoding) { + case eEncodingT1: + registers = Bits32(opcode, 7, 0); + // The P bit represents PC. + if (Bit32(opcode, 8)) + registers |= (1u << 15); + // if BitCount(registers) < 1 then UNPREDICTABLE; + if (BitCount(registers) < 1) + return false; + break; + case eEncodingT2: + // Ignore bit 13. + registers = Bits32(opcode, 15, 0) & ~0x2000; + // if BitCount(registers) < 2 || (P == '1' && M == '1') then UNPREDICTABLE; + if (BitCount(registers) < 2 || (Bit32(opcode, 15) && Bit32(opcode, 14))) + return false; + // if registers<15> == '1' && InITBlock() && !LastInITBlock() then UNPREDICTABLE; + if (BitIsSet(registers, 15) && InITBlock() && !LastInITBlock()) + return false; + break; + case eEncodingT3: + Rt = Bits32(opcode, 15, 12); + // if t == 13 || (t == 15 && InITBlock() && !LastInITBlock()) then UNPREDICTABLE; + if (Rt == 13) + return false; + if (Rt == 15 && InITBlock() && !LastInITBlock()) + return false; + registers = (1u << Rt); + break; + case eEncodingA1: + registers = Bits32(opcode, 15, 0); + // Instead of return false, let's handle the following case as well, + // which amounts to popping one reg from the full descending stacks. + // if BitCount(register_list) < 2 then SEE LDM / LDMIA / LDMFD; + + // if registers<13> == '1' && ArchVersion() >= 7 then UNPREDICTABLE; + if (BitIsSet(opcode, 13) && ArchVersion() >= ARMv7) + return false; + break; + case eEncodingA2: + Rt = Bits32(opcode, 15, 12); + // if t == 13 then UNPREDICTABLE; + if (Rt == dwarf_sp) + return false; + registers = (1u << Rt); + break; + default: + return false; + } + addr_t sp_offset = addr_byte_size * BitCount (registers); + addr_t addr = sp; + uint32_t i, data; + + EmulateInstruction::Context context; + if (conditional) + context.type = EmulateInstruction::eContextRegisterLoad; + else + context.type = EmulateInstruction::eContextPopRegisterOffStack; + + RegisterInfo sp_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_sp, sp_reg); + + for (i=0; i<15; ++i) + { + if (BitIsSet (registers, i)) + { + context.SetRegisterPlusOffset (sp_reg, addr - sp); + data = MemARead(context, addr, 4, 0, &success); + if (!success) + return false; + if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + i, data)) + return false; + addr += addr_byte_size; + } + } + + if (BitIsSet (registers, 15)) + { + context.SetRegisterPlusOffset (sp_reg, addr - sp); + data = MemARead(context, addr, 4, 0, &success); + if (!success) + return false; + // In ARMv5T and above, this is an interworking branch. + if (!LoadWritePC(context, data)) + return false; + //addr += addr_byte_size; + } + + context.type = EmulateInstruction::eContextAdjustStackPointer; + context.SetImmediateSigned (sp_offset); + + if (!WriteRegisterUnsigned (context, eRegisterKindGeneric, LLDB_REGNUM_GENERIC_SP, sp + sp_offset)) + return false; + } + return true; +} + +// Set r7 or ip to point to saved value residing within the stack. +// ADD (SP plus immediate) +bool +EmulateInstructionARM::EmulateADDRdSPImm (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + // ARM pseudo code... + if (ConditionPassed()) + { + EncodingSpecificOperations(); + (result, carry, overflow) = AddWithCarry(SP, imm32, '0'); + if d == 15 then + ALUWritePC(result); // setflags is always FALSE here + else + R[d] = result; + if setflags then + APSR.N = result<31>; + APSR.Z = IsZeroBit(result); + APSR.C = carry; + APSR.V = overflow; + } +#endif + + bool success = false; + + if (ConditionPassed(opcode)) + { + const addr_t sp = ReadCoreReg (SP_REG, &success); + if (!success) + return false; + uint32_t Rd; // the destination register + uint32_t imm32; + switch (encoding) { + case eEncodingT1: + Rd = 7; + imm32 = Bits32(opcode, 7, 0) << 2; // imm32 = ZeroExtend(imm8:'00', 32) + break; + case eEncodingA1: + Rd = Bits32(opcode, 15, 12); + imm32 = ARMExpandImm(opcode); // imm32 = ARMExpandImm(imm12) + break; + default: + return false; + } + addr_t sp_offset = imm32; + addr_t addr = sp + sp_offset; // a pointer to the stack area + + EmulateInstruction::Context context; + context.type = eContextSetFramePointer; + RegisterInfo sp_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_sp, sp_reg); + context.SetRegisterPlusOffset (sp_reg, sp_offset); + + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + Rd, addr)) + return false; + } + return true; +} + +// Set r7 or ip to the current stack pointer. +// MOV (register) +bool +EmulateInstructionARM::EmulateMOVRdSP (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + // ARM pseudo code... + if (ConditionPassed()) + { + EncodingSpecificOperations(); + result = R[m]; + if d == 15 then + ALUWritePC(result); // setflags is always FALSE here + else + R[d] = result; + if setflags then + APSR.N = result<31>; + APSR.Z = IsZeroBit(result); + // APSR.C unchanged + // APSR.V unchanged + } +#endif + + bool success = false; + + if (ConditionPassed(opcode)) + { + const addr_t sp = ReadCoreReg (SP_REG, &success); + if (!success) + return false; + uint32_t Rd; // the destination register + switch (encoding) { + case eEncodingT1: + Rd = 7; + break; + case eEncodingA1: + Rd = 12; + break; + default: + return false; + } + + EmulateInstruction::Context context; + if (Rd == GetFramePointerRegisterNumber()) + context.type = EmulateInstruction::eContextSetFramePointer; + else + context.type = EmulateInstruction::eContextRegisterPlusOffset; + RegisterInfo sp_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_sp, sp_reg); + context.SetRegisterPlusOffset (sp_reg, 0); + + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + Rd, sp)) + return false; + } + return true; +} + +// Move from high register (r8-r15) to low register (r0-r7). +// MOV (register) +bool +EmulateInstructionARM::EmulateMOVLowHigh (const uint32_t opcode, const ARMEncoding encoding) +{ + return EmulateMOVRdRm (opcode, encoding); +} + +// Move from register to register. +// MOV (register) +bool +EmulateInstructionARM::EmulateMOVRdRm (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + // ARM pseudo code... + if (ConditionPassed()) + { + EncodingSpecificOperations(); + result = R[m]; + if d == 15 then + ALUWritePC(result); // setflags is always FALSE here + else + R[d] = result; + if setflags then + APSR.N = result<31>; + APSR.Z = IsZeroBit(result); + // APSR.C unchanged + // APSR.V unchanged + } +#endif + + bool success = false; + + if (ConditionPassed(opcode)) + { + uint32_t Rm; // the source register + uint32_t Rd; // the destination register + bool setflags; + switch (encoding) { + case eEncodingT1: + Rd = Bit32(opcode, 7) << 3 | Bits32(opcode, 2, 0); + Rm = Bits32(opcode, 6, 3); + setflags = false; + if (Rd == 15 && InITBlock() && !LastInITBlock()) + return false; + break; + case eEncodingT2: + Rd = Bits32(opcode, 2, 0); + Rm = Bits32(opcode, 5, 3); + setflags = true; + if (InITBlock()) + return false; + break; + case eEncodingT3: + Rd = Bits32(opcode, 11, 8); + Rm = Bits32(opcode, 3, 0); + setflags = BitIsSet(opcode, 20); + // if setflags && (BadReg(d) || BadReg(m)) then UNPREDICTABLE; + if (setflags && (BadReg(Rd) || BadReg(Rm))) + return false; + // if !setflags && (d == 15 || m == 15 || (d == 13 && m == 13)) then UNPREDICTABLE; + if (!setflags && (Rd == 15 || Rm == 15 || (Rd == 13 && Rm == 13))) + return false; + break; + case eEncodingA1: + Rd = Bits32(opcode, 15, 12); + Rm = Bits32(opcode, 3, 0); + setflags = BitIsSet(opcode, 20); + + // if Rd == '1111' && S == '1' then SEE SUBS PC, LR and related instructions; + if (Rd == 15 && setflags) + return EmulateSUBSPcLrEtc (opcode, encoding); + break; + default: + return false; + } + uint32_t result = ReadCoreReg(Rm, &success); + if (!success) + return false; + + // The context specifies that Rm is to be moved into Rd. + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextRegisterLoad; + RegisterInfo dwarf_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + Rm, dwarf_reg); + context.SetRegister (dwarf_reg); + + if (!WriteCoreRegOptionalFlags(context, result, Rd, setflags)) + return false; + } + return true; +} + +// Move (immediate) writes an immediate value to the destination register. It +// can optionally update the condition flags based on the value. +// MOV (immediate) +bool +EmulateInstructionARM::EmulateMOVRdImm (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + // ARM pseudo code... + if (ConditionPassed()) + { + EncodingSpecificOperations(); + result = imm32; + if d == 15 then // Can only occur for ARM encoding + ALUWritePC(result); // setflags is always FALSE here + else + R[d] = result; + if setflags then + APSR.N = result<31>; + APSR.Z = IsZeroBit(result); + APSR.C = carry; + // APSR.V unchanged + } +#endif + + if (ConditionPassed(opcode)) + { + uint32_t Rd; // the destination register + uint32_t imm32; // the immediate value to be written to Rd + uint32_t carry = 0; // the carry bit after ThumbExpandImm_C or ARMExpandImm_C. + // for setflags == false, this value is a don't care + // initialized to 0 to silence the static analyzer + bool setflags; + switch (encoding) { + case eEncodingT1: + Rd = Bits32(opcode, 10, 8); + setflags = !InITBlock(); + imm32 = Bits32(opcode, 7, 0); // imm32 = ZeroExtend(imm8, 32) + carry = APSR_C; + + break; + + case eEncodingT2: + Rd = Bits32(opcode, 11, 8); + setflags = BitIsSet(opcode, 20); + imm32 = ThumbExpandImm_C(opcode, APSR_C, carry); + if (BadReg(Rd)) + return false; + + break; + + case eEncodingT3: + { + // d = UInt(Rd); setflags = FALSE; imm32 = ZeroExtend(imm4:i:imm3:imm8, 32); + Rd = Bits32 (opcode, 11, 8); + setflags = false; + uint32_t imm4 = Bits32 (opcode, 19, 16); + uint32_t imm3 = Bits32 (opcode, 14, 12); + uint32_t i = Bit32 (opcode, 26); + uint32_t imm8 = Bits32 (opcode, 7, 0); + imm32 = (imm4 << 12) | (i << 11) | (imm3 << 8) | imm8; + + // if BadReg(d) then UNPREDICTABLE; + if (BadReg (Rd)) + return false; + } + break; + + case eEncodingA1: + // d = UInt(Rd); setflags = (S == Ô1Õ); (imm32, carry) = ARMExpandImm_C(imm12, APSR.C); + Rd = Bits32 (opcode, 15, 12); + setflags = BitIsSet (opcode, 20); + imm32 = ARMExpandImm_C (opcode, APSR_C, carry); + + // if Rd == Ô1111Õ && S == Ô1Õ then SEE SUBS PC, LR and related instructions; + if ((Rd == 15) && setflags) + return EmulateSUBSPcLrEtc (opcode, encoding); + + break; + + case eEncodingA2: + { + // d = UInt(Rd); setflags = FALSE; imm32 = ZeroExtend(imm4:imm12, 32); + Rd = Bits32 (opcode, 15, 12); + setflags = false; + uint32_t imm4 = Bits32 (opcode, 19, 16); + uint32_t imm12 = Bits32 (opcode, 11, 0); + imm32 = (imm4 << 12) | imm12; + + // if d == 15 then UNPREDICTABLE; + if (Rd == 15) + return false; + } + break; + + default: + return false; + } + uint32_t result = imm32; + + // The context specifies that an immediate is to be moved into Rd. + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextImmediate; + context.SetNoArgs (); + + if (!WriteCoreRegOptionalFlags(context, result, Rd, setflags, carry)) + return false; + } + return true; +} + +// MUL multiplies two register values. The least significant 32 bits of the result are written to the destination +// register. These 32 bits do not depend on whether the source register values are considered to be signed values or +// unsigned values. +// +// Optionally, it can update the condition flags based on the result. In the Thumb instruction set, this option is +// limited to only a few forms of the instruction. +bool +EmulateInstructionARM::EmulateMUL (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + if ConditionPassed() then + EncodingSpecificOperations(); + operand1 = SInt(R[n]); // operand1 = UInt(R[n]) produces the same final results + operand2 = SInt(R[m]); // operand2 = UInt(R[m]) produces the same final results + result = operand1 * operand2; + R[d] = result<31:0>; + if setflags then + APSR.N = result<31>; + APSR.Z = IsZeroBit(result); + if ArchVersion() == 4 then + APSR.C = bit UNKNOWN; + // else APSR.C unchanged + // APSR.V always unchanged +#endif + + if (ConditionPassed(opcode)) + { + uint32_t d; + uint32_t n; + uint32_t m; + bool setflags; + + // EncodingSpecificOperations(); + switch (encoding) + { + case eEncodingT1: + // d = UInt(Rdm); n = UInt(Rn); m = UInt(Rdm); setflags = !InITBlock(); + d = Bits32 (opcode, 2, 0); + n = Bits32 (opcode, 5, 3); + m = Bits32 (opcode, 2, 0); + setflags = !InITBlock(); + + // if ArchVersion() < 6 && d == n then UNPREDICTABLE; + if ((ArchVersion() < ARMv6) && (d == n)) + return false; + + break; + + case eEncodingT2: + // d = UInt(Rd); n = UInt(Rn); m = UInt(Rm); setflags = FALSE; + d = Bits32 (opcode, 11, 8); + n = Bits32 (opcode, 19, 16); + m = Bits32 (opcode, 3, 0); + setflags = false; + + // if BadReg(d) || BadReg(n) || BadReg(m) then UNPREDICTABLE; + if (BadReg (d) || BadReg (n) || BadReg (m)) + return false; + + break; + + case eEncodingA1: + // d = UInt(Rd); n = UInt(Rn); m = UInt(Rm); setflags = (S == '1'); + d = Bits32 (opcode, 19, 16); + n = Bits32 (opcode, 3, 0); + m = Bits32 (opcode, 11, 8); + setflags = BitIsSet (opcode, 20); + + // if d == 15 || n == 15 || m == 15 then UNPREDICTABLE; + if ((d == 15) || (n == 15) || (m == 15)) + return false; + + // if ArchVersion() < 6 && d == n then UNPREDICTABLE; + if ((ArchVersion() < ARMv6) && (d == n)) + return false; + + break; + + default: + return false; + } + + bool success = false; + + // operand1 = SInt(R[n]); // operand1 = UInt(R[n]) produces the same final results + uint64_t operand1 = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_r0 + n, 0, &success); + if (!success) + return false; + + // operand2 = SInt(R[m]); // operand2 = UInt(R[m]) produces the same final results + uint64_t operand2 = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_r0 + m, 0, &success); + if (!success) + return false; + + // result = operand1 * operand2; + uint64_t result = operand1 * operand2; + + // R[d] = result<31:0>; + RegisterInfo op1_reg; + RegisterInfo op2_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, op1_reg); + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + m, op2_reg); + + EmulateInstruction::Context context; + context.type = eContextArithmetic; + context.SetRegisterRegisterOperands (op1_reg, op2_reg); + + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + d, (0x0000ffff & result))) + return false; + + // if setflags then + if (setflags) + { + // APSR.N = result<31>; + // APSR.Z = IsZeroBit(result); + m_new_inst_cpsr = m_opcode_cpsr; + SetBit32 (m_new_inst_cpsr, CPSR_N_POS, Bit32 (result, 31)); + SetBit32 (m_new_inst_cpsr, CPSR_Z_POS, result == 0 ? 1 : 0); + if (m_new_inst_cpsr != m_opcode_cpsr) + { + if (!WriteRegisterUnsigned (context, eRegisterKindGeneric, LLDB_REGNUM_GENERIC_FLAGS, m_new_inst_cpsr)) + return false; + } + + // if ArchVersion() == 4 then + // APSR.C = bit UNKNOWN; + } + } + return true; +} + +// Bitwise NOT (immediate) writes the bitwise inverse of an immediate value to the destination register. +// It can optionally update the condition flags based on the value. +bool +EmulateInstructionARM::EmulateMVNImm (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + // ARM pseudo code... + if (ConditionPassed()) + { + EncodingSpecificOperations(); + result = NOT(imm32); + if d == 15 then // Can only occur for ARM encoding + ALUWritePC(result); // setflags is always FALSE here + else + R[d] = result; + if setflags then + APSR.N = result<31>; + APSR.Z = IsZeroBit(result); + APSR.C = carry; + // APSR.V unchanged + } +#endif + + if (ConditionPassed(opcode)) + { + uint32_t Rd; // the destination register + uint32_t imm32; // the output after ThumbExpandImm_C or ARMExpandImm_C + uint32_t carry; // the carry bit after ThumbExpandImm_C or ARMExpandImm_C + bool setflags; + switch (encoding) { + case eEncodingT1: + Rd = Bits32(opcode, 11, 8); + setflags = BitIsSet(opcode, 20); + imm32 = ThumbExpandImm_C(opcode, APSR_C, carry); + break; + case eEncodingA1: + Rd = Bits32(opcode, 15, 12); + setflags = BitIsSet(opcode, 20); + imm32 = ARMExpandImm_C(opcode, APSR_C, carry); + + // if Rd == '1111' && S == '1' then SEE SUBS PC, LR and related instructions; + if (Rd == 15 && setflags) + return EmulateSUBSPcLrEtc (opcode, encoding); + break; + default: + return false; + } + uint32_t result = ~imm32; + + // The context specifies that an immediate is to be moved into Rd. + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextImmediate; + context.SetNoArgs (); + + if (!WriteCoreRegOptionalFlags(context, result, Rd, setflags, carry)) + return false; + } + return true; +} + +// Bitwise NOT (register) writes the bitwise inverse of a register value to the destination register. +// It can optionally update the condition flags based on the result. +bool +EmulateInstructionARM::EmulateMVNReg (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + // ARM pseudo code... + if (ConditionPassed()) + { + EncodingSpecificOperations(); + (shifted, carry) = Shift_C(R[m], shift_t, shift_n, APSR.C); + result = NOT(shifted); + if d == 15 then // Can only occur for ARM encoding + ALUWritePC(result); // setflags is always FALSE here + else + R[d] = result; + if setflags then + APSR.N = result<31>; + APSR.Z = IsZeroBit(result); + APSR.C = carry; + // APSR.V unchanged + } +#endif + + if (ConditionPassed(opcode)) + { + uint32_t Rm; // the source register + uint32_t Rd; // the destination register + ARM_ShifterType shift_t; + uint32_t shift_n; // the shift applied to the value read from Rm + bool setflags; + uint32_t carry; // the carry bit after the shift operation + switch (encoding) { + case eEncodingT1: + Rd = Bits32(opcode, 2, 0); + Rm = Bits32(opcode, 5, 3); + setflags = !InITBlock(); + shift_t = SRType_LSL; + shift_n = 0; + if (InITBlock()) + return false; + break; + case eEncodingT2: + Rd = Bits32(opcode, 11, 8); + Rm = Bits32(opcode, 3, 0); + setflags = BitIsSet(opcode, 20); + shift_n = DecodeImmShiftThumb(opcode, shift_t); + // if (BadReg(d) || BadReg(m)) then UNPREDICTABLE; + if (BadReg(Rd) || BadReg(Rm)) + return false; + break; + case eEncodingA1: + Rd = Bits32(opcode, 15, 12); + Rm = Bits32(opcode, 3, 0); + setflags = BitIsSet(opcode, 20); + shift_n = DecodeImmShiftARM(opcode, shift_t); + break; + default: + return false; + } + bool success = false; + uint32_t value = ReadCoreReg(Rm, &success); + if (!success) + return false; + + uint32_t shifted = Shift_C(value, shift_t, shift_n, APSR_C, carry, &success); + if (!success) + return false; + uint32_t result = ~shifted; + + // The context specifies that an immediate is to be moved into Rd. + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextImmediate; + context.SetNoArgs (); + + if (!WriteCoreRegOptionalFlags(context, result, Rd, setflags, carry)) + return false; + } + return true; +} + +// PC relative immediate load into register, possibly followed by ADD (SP plus register). +// LDR (literal) +bool +EmulateInstructionARM::EmulateLDRRtPCRelative (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + // ARM pseudo code... + if (ConditionPassed()) + { + EncodingSpecificOperations(); NullCheckIfThumbEE(15); + base = Align(PC,4); + address = if add then (base + imm32) else (base - imm32); + data = MemU[address,4]; + if t == 15 then + if address<1:0> == '00' then LoadWritePC(data); else UNPREDICTABLE; + elsif UnalignedSupport() || address<1:0> = '00' then + R[t] = data; + else // Can only apply before ARMv7 + if CurrentInstrSet() == InstrSet_ARM then + R[t] = ROR(data, 8*UInt(address<1:0>)); + else + R[t] = bits(32) UNKNOWN; + } +#endif + + if (ConditionPassed(opcode)) + { + bool success = false; + const uint32_t pc = ReadCoreReg(PC_REG, &success); + if (!success) + return false; + + // PC relative immediate load context + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextRegisterPlusOffset; + RegisterInfo pc_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_pc, pc_reg); + context.SetRegisterPlusOffset (pc_reg, 0); + + uint32_t Rt; // the destination register + uint32_t imm32; // immediate offset from the PC + bool add; // +imm32 or -imm32? + addr_t base; // the base address + addr_t address; // the PC relative address + uint32_t data; // the literal data value from the PC relative load + switch (encoding) { + case eEncodingT1: + Rt = Bits32(opcode, 10, 8); + imm32 = Bits32(opcode, 7, 0) << 2; // imm32 = ZeroExtend(imm8:'00', 32); + add = true; + break; + case eEncodingT2: + Rt = Bits32(opcode, 15, 12); + imm32 = Bits32(opcode, 11, 0) << 2; // imm32 = ZeroExtend(imm12, 32); + add = BitIsSet(opcode, 23); + if (Rt == 15 && InITBlock() && !LastInITBlock()) + return false; + break; + default: + return false; + } + + base = Align(pc, 4); + if (add) + address = base + imm32; + else + address = base - imm32; + + context.SetRegisterPlusOffset(pc_reg, address - base); + data = MemURead(context, address, 4, 0, &success); + if (!success) + return false; + + if (Rt == 15) + { + if (Bits32(address, 1, 0) == 0) + { + // In ARMv5T and above, this is an interworking branch. + if (!LoadWritePC(context, data)) + return false; + } + else + return false; + } + else if (UnalignedSupport() || Bits32(address, 1, 0) == 0) + { + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + Rt, data)) + return false; + } + else // We don't handle ARM for now. + return false; + + } + return true; +} + +// An add operation to adjust the SP. +// ADD (SP plus immediate) +bool +EmulateInstructionARM::EmulateADDSPImm (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + // ARM pseudo code... + if (ConditionPassed()) + { + EncodingSpecificOperations(); + (result, carry, overflow) = AddWithCarry(SP, imm32, '0'); + if d == 15 then // Can only occur for ARM encoding + ALUWritePC(result); // setflags is always FALSE here + else + R[d] = result; + if setflags then + APSR.N = result<31>; + APSR.Z = IsZeroBit(result); + APSR.C = carry; + APSR.V = overflow; + } +#endif + + bool success = false; + + if (ConditionPassed(opcode)) + { + const addr_t sp = ReadCoreReg (SP_REG, &success); + if (!success) + return false; + uint32_t imm32; // the immediate operand + uint32_t d; + //bool setflags = false; // Add this back if/when support eEncodingT3 eEncodingA1 + switch (encoding) + { + case eEncodingT1: + // d = UInt(Rd); setflags = FALSE; imm32 = ZeroExtend(imm8:'00', 32); + d = Bits32 (opcode, 10, 8); + imm32 = (Bits32 (opcode, 7, 0) << 2); + + break; + + case eEncodingT2: + // d = 13; setflags = FALSE; imm32 = ZeroExtend(imm7:'00', 32); + d = 13; + imm32 = ThumbImm7Scaled(opcode); // imm32 = ZeroExtend(imm7:'00', 32) + + break; + + default: + return false; + } + addr_t sp_offset = imm32; + addr_t addr = sp + sp_offset; // the adjusted stack pointer value + + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextAdjustStackPointer; + RegisterInfo sp_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_sp, sp_reg); + context.SetRegisterPlusOffset (sp_reg, sp_offset); + + if (d == 15) + { + if (!ALUWritePC (context, addr)) + return false; + } + else + { + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + d, addr)) + return false; + + // Add this back if/when support eEncodingT3 eEncodingA1 + //if (setflags) + //{ + // APSR.N = result<31>; + // APSR.Z = IsZeroBit(result); + // APSR.C = carry; + // APSR.V = overflow; + //} + } + } + return true; +} + +// An add operation to adjust the SP. +// ADD (SP plus register) +bool +EmulateInstructionARM::EmulateADDSPRm (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + // ARM pseudo code... + if (ConditionPassed()) + { + EncodingSpecificOperations(); + shifted = Shift(R[m], shift_t, shift_n, APSR.C); + (result, carry, overflow) = AddWithCarry(SP, shifted, '0'); + if d == 15 then + ALUWritePC(result); // setflags is always FALSE here + else + R[d] = result; + if setflags then + APSR.N = result<31>; + APSR.Z = IsZeroBit(result); + APSR.C = carry; + APSR.V = overflow; + } +#endif + + bool success = false; + + if (ConditionPassed(opcode)) + { + const addr_t sp = ReadCoreReg (SP_REG, &success); + if (!success) + return false; + uint32_t Rm; // the second operand + switch (encoding) { + case eEncodingT2: + Rm = Bits32(opcode, 6, 3); + break; + default: + return false; + } + int32_t reg_value = ReadCoreReg(Rm, &success); + if (!success) + return false; + + addr_t addr = (int32_t)sp + reg_value; // the adjusted stack pointer value + + EmulateInstruction::Context context; + context.type = eContextArithmetic; + RegisterInfo sp_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_sp, sp_reg); + + RegisterInfo other_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + Rm, other_reg); + context.SetRegisterRegisterOperands (sp_reg, other_reg); + + if (!WriteRegisterUnsigned (context, eRegisterKindGeneric, LLDB_REGNUM_GENERIC_SP, addr)) + return false; + } + return true; +} + +// Branch with Link and Exchange Instruction Sets (immediate) calls a subroutine +// at a PC-relative address, and changes instruction set from ARM to Thumb, or +// from Thumb to ARM. +// BLX (immediate) +bool +EmulateInstructionARM::EmulateBLXImmediate (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + // ARM pseudo code... + if (ConditionPassed()) + { + EncodingSpecificOperations(); + if CurrentInstrSet() == InstrSet_ARM then + LR = PC - 4; + else + LR = PC<31:1> : '1'; + if targetInstrSet == InstrSet_ARM then + targetAddress = Align(PC,4) + imm32; + else + targetAddress = PC + imm32; + SelectInstrSet(targetInstrSet); + BranchWritePC(targetAddress); + } +#endif + + bool success = true; + + if (ConditionPassed(opcode)) + { + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextRelativeBranchImmediate; + const uint32_t pc = ReadCoreReg(PC_REG, &success); + if (!success) + return false; + addr_t lr; // next instruction address + addr_t target; // target address + int32_t imm32; // PC-relative offset + switch (encoding) { + case eEncodingT1: + { + lr = pc | 1u; // return address + uint32_t S = Bit32(opcode, 26); + uint32_t imm10 = Bits32(opcode, 25, 16); + uint32_t J1 = Bit32(opcode, 13); + uint32_t J2 = Bit32(opcode, 11); + uint32_t imm11 = Bits32(opcode, 10, 0); + uint32_t I1 = !(J1 ^ S); + uint32_t I2 = !(J2 ^ S); + uint32_t imm25 = (S << 24) | (I1 << 23) | (I2 << 22) | (imm10 << 12) | (imm11 << 1); + imm32 = llvm::SignExtend32<25>(imm25); + target = pc + imm32; + context.SetISAAndImmediateSigned (eModeThumb, 4 + imm32); + if (InITBlock() && !LastInITBlock()) + return false; + break; + } + case eEncodingT2: + { + lr = pc | 1u; // return address + uint32_t S = Bit32(opcode, 26); + uint32_t imm10H = Bits32(opcode, 25, 16); + uint32_t J1 = Bit32(opcode, 13); + uint32_t J2 = Bit32(opcode, 11); + uint32_t imm10L = Bits32(opcode, 10, 1); + uint32_t I1 = !(J1 ^ S); + uint32_t I2 = !(J2 ^ S); + uint32_t imm25 = (S << 24) | (I1 << 23) | (I2 << 22) | (imm10H << 12) | (imm10L << 2); + imm32 = llvm::SignExtend32<25>(imm25); + target = Align(pc, 4) + imm32; + context.SetISAAndImmediateSigned (eModeARM, 4 + imm32); + if (InITBlock() && !LastInITBlock()) + return false; + break; + } + case eEncodingA1: + lr = pc - 4; // return address + imm32 = llvm::SignExtend32<26>(Bits32(opcode, 23, 0) << 2); + target = Align(pc, 4) + imm32; + context.SetISAAndImmediateSigned (eModeARM, 8 + imm32); + break; + case eEncodingA2: + lr = pc - 4; // return address + imm32 = llvm::SignExtend32<26>(Bits32(opcode, 23, 0) << 2 | Bits32(opcode, 24, 24) << 1); + target = pc + imm32; + context.SetISAAndImmediateSigned (eModeThumb, 8 + imm32); + break; + default: + return false; + } + if (!WriteRegisterUnsigned (context, eRegisterKindGeneric, LLDB_REGNUM_GENERIC_RA, lr)) + return false; + if (!BranchWritePC(context, target)) + return false; + } + return true; +} + +// Branch with Link and Exchange (register) calls a subroutine at an address and +// instruction set specified by a register. +// BLX (register) +bool +EmulateInstructionARM::EmulateBLXRm (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + // ARM pseudo code... + if (ConditionPassed()) + { + EncodingSpecificOperations(); + target = R[m]; + if CurrentInstrSet() == InstrSet_ARM then + next_instr_addr = PC - 4; + LR = next_instr_addr; + else + next_instr_addr = PC - 2; + LR = next_instr_addr<31:1> : '1'; + BXWritePC(target); + } +#endif + + bool success = false; + + if (ConditionPassed(opcode)) + { + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextAbsoluteBranchRegister; + const uint32_t pc = ReadCoreReg(PC_REG, &success); + addr_t lr; // next instruction address + if (!success) + return false; + uint32_t Rm; // the register with the target address + switch (encoding) { + case eEncodingT1: + lr = (pc - 2) | 1u; // return address + Rm = Bits32(opcode, 6, 3); + // if m == 15 then UNPREDICTABLE; + if (Rm == 15) + return false; + if (InITBlock() && !LastInITBlock()) + return false; + break; + case eEncodingA1: + lr = pc - 4; // return address + Rm = Bits32(opcode, 3, 0); + // if m == 15 then UNPREDICTABLE; + if (Rm == 15) + return false; + break; + default: + return false; + } + addr_t target = ReadCoreReg (Rm, &success); + if (!success) + return false; + RegisterInfo dwarf_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + Rm, dwarf_reg); + context.SetRegister (dwarf_reg); + if (!WriteRegisterUnsigned (context, eRegisterKindGeneric, LLDB_REGNUM_GENERIC_RA, lr)) + return false; + if (!BXWritePC(context, target)) + return false; + } + return true; +} + +// Branch and Exchange causes a branch to an address and instruction set specified by a register. +bool +EmulateInstructionARM::EmulateBXRm (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + // ARM pseudo code... + if (ConditionPassed()) + { + EncodingSpecificOperations(); + BXWritePC(R[m]); + } +#endif + + if (ConditionPassed(opcode)) + { + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextAbsoluteBranchRegister; + uint32_t Rm; // the register with the target address + switch (encoding) { + case eEncodingT1: + Rm = Bits32(opcode, 6, 3); + if (InITBlock() && !LastInITBlock()) + return false; + break; + case eEncodingA1: + Rm = Bits32(opcode, 3, 0); + break; + default: + return false; + } + bool success = false; + addr_t target = ReadCoreReg (Rm, &success); + if (!success) + return false; + + RegisterInfo dwarf_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + Rm, dwarf_reg); + context.SetRegister (dwarf_reg); + if (!BXWritePC(context, target)) + return false; + } + return true; +} + +// Branch and Exchange Jazelle attempts to change to Jazelle state. If the attempt fails, it branches to an +// address and instruction set specified by a register as though it were a BX instruction. +// +// TODO: Emulate Jazelle architecture? +// We currently assume that switching to Jazelle state fails, thus treating BXJ as a BX operation. +bool +EmulateInstructionARM::EmulateBXJRm (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + // ARM pseudo code... + if (ConditionPassed()) + { + EncodingSpecificOperations(); + if JMCR.JE == '0' || CurrentInstrSet() == InstrSet_ThumbEE then + BXWritePC(R[m]); + else + if JazelleAcceptsExecution() then + SwitchToJazelleExecution(); + else + SUBARCHITECTURE_DEFINED handler call; + } +#endif + + if (ConditionPassed(opcode)) + { + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextAbsoluteBranchRegister; + uint32_t Rm; // the register with the target address + switch (encoding) { + case eEncodingT1: + Rm = Bits32(opcode, 19, 16); + if (BadReg(Rm)) + return false; + if (InITBlock() && !LastInITBlock()) + return false; + break; + case eEncodingA1: + Rm = Bits32(opcode, 3, 0); + if (Rm == 15) + return false; + break; + default: + return false; + } + bool success = false; + addr_t target = ReadCoreReg (Rm, &success); + if (!success) + return false; + + RegisterInfo dwarf_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + Rm, dwarf_reg); + context.SetRegister (dwarf_reg); + if (!BXWritePC(context, target)) + return false; + } + return true; +} + +// Set r7 to point to some ip offset. +// SUB (immediate) +bool +EmulateInstructionARM::EmulateSUBR7IPImm (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + // ARM pseudo code... + if (ConditionPassed()) + { + EncodingSpecificOperations(); + (result, carry, overflow) = AddWithCarry(SP, NOT(imm32), '1'); + if d == 15 then // Can only occur for ARM encoding + ALUWritePC(result); // setflags is always FALSE here + else + R[d] = result; + if setflags then + APSR.N = result<31>; + APSR.Z = IsZeroBit(result); + APSR.C = carry; + APSR.V = overflow; + } +#endif + + if (ConditionPassed(opcode)) + { + bool success = false; + const addr_t ip = ReadCoreReg (12, &success); + if (!success) + return false; + uint32_t imm32; + switch (encoding) { + case eEncodingA1: + imm32 = ARMExpandImm(opcode); // imm32 = ARMExpandImm(imm12) + break; + default: + return false; + } + addr_t ip_offset = imm32; + addr_t addr = ip - ip_offset; // the adjusted ip value + + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextRegisterPlusOffset; + RegisterInfo dwarf_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_r12, dwarf_reg); + context.SetRegisterPlusOffset (dwarf_reg, -ip_offset); + + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r7, addr)) + return false; + } + return true; +} + +// Set ip to point to some stack offset. +// SUB (SP minus immediate) +bool +EmulateInstructionARM::EmulateSUBIPSPImm (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + // ARM pseudo code... + if (ConditionPassed()) + { + EncodingSpecificOperations(); + (result, carry, overflow) = AddWithCarry(SP, NOT(imm32), '1'); + if d == 15 then // Can only occur for ARM encoding + ALUWritePC(result); // setflags is always FALSE here + else + R[d] = result; + if setflags then + APSR.N = result<31>; + APSR.Z = IsZeroBit(result); + APSR.C = carry; + APSR.V = overflow; + } +#endif + + if (ConditionPassed(opcode)) + { + bool success = false; + const addr_t sp = ReadCoreReg (SP_REG, &success); + if (!success) + return false; + uint32_t imm32; + switch (encoding) { + case eEncodingA1: + imm32 = ARMExpandImm(opcode); // imm32 = ARMExpandImm(imm12) + break; + default: + return false; + } + addr_t sp_offset = imm32; + addr_t addr = sp - sp_offset; // the adjusted stack pointer value + + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextRegisterPlusOffset; + RegisterInfo dwarf_reg; + GetRegisterInfo (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_SP, dwarf_reg); + context.SetRegisterPlusOffset (dwarf_reg, -sp_offset); + + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r12, addr)) + return false; + } + return true; +} + +// This instruction subtracts an immediate value from the SP value, and writes +// the result to the destination register. +// +// If Rd == 13 => A sub operation to adjust the SP -- allocate space for local storage. +bool +EmulateInstructionARM::EmulateSUBSPImm (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + // ARM pseudo code... + if (ConditionPassed()) + { + EncodingSpecificOperations(); + (result, carry, overflow) = AddWithCarry(SP, NOT(imm32), '1'); + if d == 15 then // Can only occur for ARM encoding + ALUWritePC(result); // setflags is always FALSE here + else + R[d] = result; + if setflags then + APSR.N = result<31>; + APSR.Z = IsZeroBit(result); + APSR.C = carry; + APSR.V = overflow; + } +#endif + + bool success = false; + if (ConditionPassed(opcode)) + { + const addr_t sp = ReadCoreReg (SP_REG, &success); + if (!success) + return false; + + uint32_t Rd; + bool setflags; + uint32_t imm32; + switch (encoding) { + case eEncodingT1: + Rd = 13; + setflags = false; + imm32 = ThumbImm7Scaled(opcode); // imm32 = ZeroExtend(imm7:'00', 32) + break; + case eEncodingT2: + Rd = Bits32(opcode, 11, 8); + setflags = BitIsSet(opcode, 20); + imm32 = ThumbExpandImm(opcode); // imm32 = ThumbExpandImm(i:imm3:imm8) + if (Rd == 15 && setflags) + return EmulateCMPImm(opcode, eEncodingT2); + if (Rd == 15 && !setflags) + return false; + break; + case eEncodingT3: + Rd = Bits32(opcode, 11, 8); + setflags = false; + imm32 = ThumbImm12(opcode); // imm32 = ZeroExtend(i:imm3:imm8, 32) + if (Rd == 15) + return false; + break; + case eEncodingA1: + Rd = Bits32(opcode, 15, 12); + setflags = BitIsSet(opcode, 20); + imm32 = ARMExpandImm(opcode); // imm32 = ARMExpandImm(imm12) + + // if Rd == '1111' && S == '1' then SEE SUBS PC, LR and related instructions; + if (Rd == 15 && setflags) + return EmulateSUBSPcLrEtc (opcode, encoding); + break; + default: + return false; + } + AddWithCarryResult res = AddWithCarry(sp, ~imm32, 1); + + EmulateInstruction::Context context; + if (Rd == 13) + { + uint64_t imm64 = imm32; // Need to expand it to 64 bits before attempting to negate it, or the wrong + // value gets passed down to context.SetImmediateSigned. + context.type = EmulateInstruction::eContextAdjustStackPointer; + context.SetImmediateSigned (-imm64); // the stack pointer offset + } + else + { + context.type = EmulateInstruction::eContextImmediate; + context.SetNoArgs (); + } + + if (!WriteCoreRegOptionalFlags(context, res.result, Rd, setflags, res.carry_out, res.overflow)) + return false; + } + return true; +} + +// A store operation to the stack that also updates the SP. +bool +EmulateInstructionARM::EmulateSTRRtSP (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + // ARM pseudo code... + if (ConditionPassed()) + { + EncodingSpecificOperations(); + offset_addr = if add then (R[n] + imm32) else (R[n] - imm32); + address = if index then offset_addr else R[n]; + MemU[address,4] = if t == 15 then PCStoreValue() else R[t]; + if wback then R[n] = offset_addr; + } +#endif + + bool conditional = false; + bool success = false; + if (ConditionPassed(opcode, &conditional)) + { + const uint32_t addr_byte_size = GetAddressByteSize(); + const addr_t sp = ReadCoreReg (SP_REG, &success); + if (!success) + return false; + uint32_t Rt; // the source register + uint32_t imm12; + uint32_t Rn; // This function assumes Rn is the SP, but we should verify that. + + bool index; + bool add; + bool wback; + switch (encoding) { + case eEncodingA1: + Rt = Bits32(opcode, 15, 12); + imm12 = Bits32(opcode, 11, 0); + Rn = Bits32 (opcode, 19, 16); + + if (Rn != 13) // 13 is the SP reg on ARM. Verify that Rn == SP. + return false; + + index = BitIsSet (opcode, 24); + add = BitIsSet (opcode, 23); + wback = (BitIsClear (opcode, 24) || BitIsSet (opcode, 21)); + + if (wback && ((Rn == 15) || (Rn == Rt))) + return false; + break; + default: + return false; + } + addr_t offset_addr; + if (add) + offset_addr = sp + imm12; + else + offset_addr = sp - imm12; + + addr_t addr; + if (index) + addr = offset_addr; + else + addr = sp; + + EmulateInstruction::Context context; + if (conditional) + context.type = EmulateInstruction::eContextRegisterStore; + else + context.type = EmulateInstruction::eContextPushRegisterOnStack; + RegisterInfo sp_reg; + RegisterInfo dwarf_reg; + + GetRegisterInfo (eRegisterKindDWARF, dwarf_sp, sp_reg); + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + Rt, dwarf_reg); + context.SetRegisterToRegisterPlusOffset ( dwarf_reg, sp_reg, addr - sp); + if (Rt != 15) + { + uint32_t reg_value = ReadCoreReg(Rt, &success); + if (!success) + return false; + if (!MemUWrite (context, addr, reg_value, addr_byte_size)) + return false; + } + else + { + const uint32_t pc = ReadCoreReg(PC_REG, &success); + if (!success) + return false; + if (!MemUWrite (context, addr, pc, addr_byte_size)) + return false; + } + + + if (wback) + { + context.type = EmulateInstruction::eContextAdjustStackPointer; + context.SetImmediateSigned (addr - sp); + if (!WriteRegisterUnsigned (context, eRegisterKindGeneric, LLDB_REGNUM_GENERIC_SP, offset_addr)) + return false; + } + } + return true; +} + +// Vector Push stores multiple extension registers to the stack. +// It also updates SP to point to the start of the stored data. +bool +EmulateInstructionARM::EmulateVPUSH (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + // ARM pseudo code... + if (ConditionPassed()) + { + EncodingSpecificOperations(); CheckVFPEnabled(TRUE); NullCheckIfThumbEE(13); + address = SP - imm32; + SP = SP - imm32; + if single_regs then + for r = 0 to regs-1 + MemA[address,4] = S[d+r]; address = address+4; + else + for r = 0 to regs-1 + // Store as two word-aligned words in the correct order for current endianness. + MemA[address,4] = if BigEndian() then D[d+r]<63:32> else D[d+r]<31:0>; + MemA[address+4,4] = if BigEndian() then D[d+r]<31:0> else D[d+r]<63:32>; + address = address+8; + } +#endif + + bool success = false; + bool conditional = false; + if (ConditionPassed(opcode, &conditional)) + { + const uint32_t addr_byte_size = GetAddressByteSize(); + const addr_t sp = ReadCoreReg (SP_REG, &success); + if (!success) + return false; + bool single_regs; + uint32_t d; // UInt(D:Vd) or UInt(Vd:D) starting register + uint32_t imm32; // stack offset + uint32_t regs; // number of registers + switch (encoding) { + case eEncodingT1: + case eEncodingA1: + single_regs = false; + d = Bit32(opcode, 22) << 4 | Bits32(opcode, 15, 12); + imm32 = Bits32(opcode, 7, 0) * addr_byte_size; + // If UInt(imm8) is odd, see "FSTMX". + regs = Bits32(opcode, 7, 0) / 2; + // if regs == 0 || regs > 16 || (d+regs) > 32 then UNPREDICTABLE; + if (regs == 0 || regs > 16 || (d + regs) > 32) + return false; + break; + case eEncodingT2: + case eEncodingA2: + single_regs = true; + d = Bits32(opcode, 15, 12) << 1 | Bit32(opcode, 22); + imm32 = Bits32(opcode, 7, 0) * addr_byte_size; + regs = Bits32(opcode, 7, 0); + // if regs == 0 || regs > 16 || (d+regs) > 32 then UNPREDICTABLE; + if (regs == 0 || regs > 16 || (d + regs) > 32) + return false; + break; + default: + return false; + } + uint32_t start_reg = single_regs ? dwarf_s0 : dwarf_d0; + uint32_t reg_byte_size = single_regs ? addr_byte_size : addr_byte_size * 2; + addr_t sp_offset = imm32; + addr_t addr = sp - sp_offset; + uint32_t i; + + EmulateInstruction::Context context; + if (conditional) + context.type = EmulateInstruction::eContextRegisterStore; + else + context.type = EmulateInstruction::eContextPushRegisterOnStack; + RegisterInfo dwarf_reg; + RegisterInfo sp_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_sp, sp_reg); + for (i=0; i 16 || (d+regs) > 32 then UNPREDICTABLE; + if (regs == 0 || regs > 16 || (d + regs) > 32) + return false; + break; + case eEncodingT2: + case eEncodingA2: + single_regs = true; + d = Bits32(opcode, 15, 12) << 1 | Bit32(opcode, 22); + imm32 = Bits32(opcode, 7, 0) * addr_byte_size; + regs = Bits32(opcode, 7, 0); + // if regs == 0 || regs > 16 || (d+regs) > 32 then UNPREDICTABLE; + if (regs == 0 || regs > 16 || (d + regs) > 32) + return false; + break; + default: + return false; + } + uint32_t start_reg = single_regs ? dwarf_s0 : dwarf_d0; + uint32_t reg_byte_size = single_regs ? addr_byte_size : addr_byte_size * 2; + addr_t sp_offset = imm32; + addr_t addr = sp; + uint32_t i; + uint64_t data; // uint64_t to accomodate 64-bit registers. + + EmulateInstruction::Context context; + if (conditional) + context.type = EmulateInstruction::eContextRegisterLoad; + else + context.type = EmulateInstruction::eContextPopRegisterOffStack; + RegisterInfo dwarf_reg; + RegisterInfo sp_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_sp, sp_reg); + for (i=0; i = firstcond:mask; +#endif + + m_it_session.InitIT(Bits32(opcode, 7, 0)); + return true; +} + +bool +EmulateInstructionARM::EmulateNop (const uint32_t opcode, const ARMEncoding encoding) +{ + // NOP, nothing to do... + return true; +} + +// Branch causes a branch to a target address. +bool +EmulateInstructionARM::EmulateB (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + // ARM pseudo code... + if (ConditionPassed()) + { + EncodingSpecificOperations(); + BranchWritePC(PC + imm32); + } +#endif + + bool success = false; + + if (ConditionPassed(opcode)) + { + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextRelativeBranchImmediate; + const uint32_t pc = ReadCoreReg(PC_REG, &success); + if (!success) + return false; + addr_t target; // target address + int32_t imm32; // PC-relative offset + switch (encoding) { + case eEncodingT1: + // The 'cond' field is handled in EmulateInstructionARM::CurrentCond(). + imm32 = llvm::SignExtend32<9>(Bits32(opcode, 7, 0) << 1); + target = pc + imm32; + context.SetISAAndImmediateSigned (eModeThumb, 4 + imm32); + break; + case eEncodingT2: + imm32 = llvm::SignExtend32<12>(Bits32(opcode, 10, 0)); + target = pc + imm32; + context.SetISAAndImmediateSigned (eModeThumb, 4 + imm32); + break; + case eEncodingT3: + // The 'cond' field is handled in EmulateInstructionARM::CurrentCond(). + { + uint32_t S = Bit32(opcode, 26); + uint32_t imm6 = Bits32(opcode, 21, 16); + uint32_t J1 = Bit32(opcode, 13); + uint32_t J2 = Bit32(opcode, 11); + uint32_t imm11 = Bits32(opcode, 10, 0); + uint32_t imm21 = (S << 20) | (J2 << 19) | (J1 << 18) | (imm6 << 12) | (imm11 << 1); + imm32 = llvm::SignExtend32<21>(imm21); + target = pc + imm32; + context.SetISAAndImmediateSigned (eModeThumb, 4 + imm32); + break; + } + case eEncodingT4: + { + uint32_t S = Bit32(opcode, 26); + uint32_t imm10 = Bits32(opcode, 25, 16); + uint32_t J1 = Bit32(opcode, 13); + uint32_t J2 = Bit32(opcode, 11); + uint32_t imm11 = Bits32(opcode, 10, 0); + uint32_t I1 = !(J1 ^ S); + uint32_t I2 = !(J2 ^ S); + uint32_t imm25 = (S << 24) | (I1 << 23) | (I2 << 22) | (imm10 << 12) | (imm11 << 1); + imm32 = llvm::SignExtend32<25>(imm25); + target = pc + imm32; + context.SetISAAndImmediateSigned (eModeThumb, 4 + imm32); + break; + } + case eEncodingA1: + imm32 = llvm::SignExtend32<26>(Bits32(opcode, 23, 0) << 2); + target = pc + imm32; + context.SetISAAndImmediateSigned (eModeARM, 8 + imm32); + break; + default: + return false; + } + if (!BranchWritePC(context, target)) + return false; + } + return true; +} + +// Compare and Branch on Nonzero and Compare and Branch on Zero compare the value in a register with +// zero and conditionally branch forward a constant value. They do not affect the condition flags. +// CBNZ, CBZ +bool +EmulateInstructionARM::EmulateCB (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + // ARM pseudo code... + EncodingSpecificOperations(); + if nonzero ^ IsZero(R[n]) then + BranchWritePC(PC + imm32); +#endif + + bool success = false; + + // Read the register value from the operand register Rn. + uint32_t reg_val = ReadCoreReg(Bits32(opcode, 2, 0), &success); + if (!success) + return false; + + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextRelativeBranchImmediate; + const uint32_t pc = ReadCoreReg(PC_REG, &success); + if (!success) + return false; + + addr_t target; // target address + uint32_t imm32; // PC-relative offset to branch forward + bool nonzero; + switch (encoding) { + case eEncodingT1: + imm32 = Bit32(opcode, 9) << 6 | Bits32(opcode, 7, 3) << 1; + nonzero = BitIsSet(opcode, 11); + target = pc + imm32; + context.SetISAAndImmediateSigned (eModeThumb, 4 + imm32); + break; + default: + return false; + } + if (nonzero ^ (reg_val == 0)) + if (!BranchWritePC(context, target)) + return false; + + return true; +} + +// Table Branch Byte causes a PC-relative forward branch using a table of single byte offsets. +// A base register provides a pointer to the table, and a second register supplies an index into the table. +// The branch length is twice the value of the byte returned from the table. +// +// Table Branch Halfword causes a PC-relative forward branch using a table of single halfword offsets. +// A base register provides a pointer to the table, and a second register supplies an index into the table. +// The branch length is twice the value of the halfword returned from the table. +// TBB, TBH +bool +EmulateInstructionARM::EmulateTB (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + // ARM pseudo code... + EncodingSpecificOperations(); NullCheckIfThumbEE(n); + if is_tbh then + halfwords = UInt(MemU[R[n]+LSL(R[m],1), 2]); + else + halfwords = UInt(MemU[R[n]+R[m], 1]); + BranchWritePC(PC + 2*halfwords); +#endif + + bool success = false; + + uint32_t Rn; // the base register which contains the address of the table of branch lengths + uint32_t Rm; // the index register which contains an integer pointing to a byte/halfword in the table + bool is_tbh; // true if table branch halfword + switch (encoding) { + case eEncodingT1: + Rn = Bits32(opcode, 19, 16); + Rm = Bits32(opcode, 3, 0); + is_tbh = BitIsSet(opcode, 4); + if (Rn == 13 || BadReg(Rm)) + return false; + if (InITBlock() && !LastInITBlock()) + return false; + break; + default: + return false; + } + + // Read the address of the table from the operand register Rn. + // The PC can be used, in which case the table immediately follows this instruction. + uint32_t base = ReadCoreReg(Rm, &success); + if (!success) + return false; + + // the table index + uint32_t index = ReadCoreReg(Rm, &success); + if (!success) + return false; + + // the offsetted table address + addr_t addr = base + (is_tbh ? index*2 : index); + + // PC-relative offset to branch forward + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextTableBranchReadMemory; + uint32_t offset = MemURead(context, addr, is_tbh ? 2 : 1, 0, &success) * 2; + if (!success) + return false; + + const uint32_t pc = ReadCoreReg(PC_REG, &success); + if (!success) + return false; + + // target address + addr_t target = pc + offset; + context.type = EmulateInstruction::eContextRelativeBranchImmediate; + context.SetISAAndImmediateSigned (eModeThumb, 4 + offset); + + if (!BranchWritePC(context, target)) + return false; + + return true; +} + +// This instruction adds an immediate value to a register value, and writes the result to the destination register. +// It can optionally update the condition flags based on the result. +bool +EmulateInstructionARM::EmulateADDImmThumb (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + if ConditionPassed() then + EncodingSpecificOperations(); + (result, carry, overflow) = AddWithCarry(R[n], imm32, '0'); + R[d] = result; + if setflags then + APSR.N = result<31>; + APSR.Z = IsZeroBit(result); + APSR.C = carry; + APSR.V = overflow; +#endif + + bool success = false; + + if (ConditionPassed(opcode)) + { + uint32_t d; + uint32_t n; + bool setflags; + uint32_t imm32; + uint32_t carry_out; + + //EncodingSpecificOperations(); + switch (encoding) + { + case eEncodingT1: + // d = UInt(Rd); n = UInt(Rn); setflags = !InITBlock(); imm32 = ZeroExtend(imm3, 32); + d = Bits32 (opcode, 2, 0); + n = Bits32 (opcode, 5, 3); + setflags = !InITBlock(); + imm32 = Bits32 (opcode, 8,6); + + break; + + case eEncodingT2: + // d = UInt(Rdn); n = UInt(Rdn); setflags = !InITBlock(); imm32 = ZeroExtend(imm8, 32); + d = Bits32 (opcode, 10, 8); + n = Bits32 (opcode, 10, 8); + setflags = !InITBlock(); + imm32 = Bits32 (opcode, 7, 0); + + break; + + case eEncodingT3: + // if Rd == '1111' && S == '1' then SEE CMN (immediate); + // if Rn == '1101' then SEE ADD (SP plus immediate); + // d = UInt(Rd); n = UInt(Rn); setflags = (S == '1'); imm32 = ThumbExpandImm(i:imm3:imm8); + d = Bits32 (opcode, 11, 8); + n = Bits32 (opcode, 19, 16); + setflags = BitIsSet (opcode, 20); + imm32 = ThumbExpandImm_C (opcode, APSR_C, carry_out); + + // if BadReg(d) || n == 15 then UNPREDICTABLE; + if (BadReg (d) || (n == 15)) + return false; + + break; + + case eEncodingT4: + { + // if Rn == '1111' then SEE ADR; + // if Rn == '1101' then SEE ADD (SP plus immediate); + // d = UInt(Rd); n = UInt(Rn); setflags = FALSE; imm32 = ZeroExtend(i:imm3:imm8, 32); + d = Bits32 (opcode, 11, 8); + n = Bits32 (opcode, 19, 16); + setflags = false; + uint32_t i = Bit32 (opcode, 26); + uint32_t imm3 = Bits32 (opcode, 14, 12); + uint32_t imm8 = Bits32 (opcode, 7, 0); + imm32 = (i << 11) | (imm3 << 8) | imm8; + + // if BadReg(d) then UNPREDICTABLE; + if (BadReg (d)) + return false; + + break; + } + default: + return false; + } + + uint64_t Rn = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_r0 + n, 0, &success); + if (!success) + return false; + + //(result, carry, overflow) = AddWithCarry(R[n], imm32, '0'); + AddWithCarryResult res = AddWithCarry (Rn, imm32, 0); + + RegisterInfo reg_n; + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, reg_n); + + EmulateInstruction::Context context; + context.type = eContextArithmetic; + context.SetRegisterPlusOffset (reg_n, imm32); + + //R[d] = result; + //if setflags then + //APSR.N = result<31>; + //APSR.Z = IsZeroBit(result); + //APSR.C = carry; + //APSR.V = overflow; + if (!WriteCoreRegOptionalFlags (context, res.result, d, setflags, res.carry_out, res.overflow)) + return false; + + } + return true; +} + +// This instruction adds an immediate value to a register value, and writes the result to the destination +// register. It can optionally update the condition flags based on the result. +bool +EmulateInstructionARM::EmulateADDImmARM (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + // ARM pseudo code... + if ConditionPassed() then + EncodingSpecificOperations(); + (result, carry, overflow) = AddWithCarry(R[n], imm32, '0'); + if d == 15 then + ALUWritePC(result); // setflags is always FALSE here + else + R[d] = result; + if setflags then + APSR.N = result<31>; + APSR.Z = IsZeroBit(result); + APSR.C = carry; + APSR.V = overflow; +#endif + + bool success = false; + + if (ConditionPassed(opcode)) + { + uint32_t Rd, Rn; + uint32_t imm32; // the immediate value to be added to the value obtained from Rn + bool setflags; + switch (encoding) + { + case eEncodingA1: + Rd = Bits32(opcode, 15, 12); + Rn = Bits32(opcode, 19, 16); + setflags = BitIsSet(opcode, 20); + imm32 = ARMExpandImm(opcode); // imm32 = ARMExpandImm(imm12) + break; + default: + return false; + } + + // Read the first operand. + uint32_t val1 = ReadCoreReg(Rn, &success); + if (!success) + return false; + + AddWithCarryResult res = AddWithCarry(val1, imm32, 0); + + EmulateInstruction::Context context; + context.type = eContextArithmetic; + RegisterInfo dwarf_reg; + GetRegisterInfo (eRegisterKindDWARF, Rn, dwarf_reg); + context.SetRegisterPlusOffset (dwarf_reg, imm32); + + if (!WriteCoreRegOptionalFlags(context, res.result, Rd, setflags, res.carry_out, res.overflow)) + return false; + } + return true; +} + +// This instruction adds a register value and an optionally-shifted register value, and writes the result +// to the destination register. It can optionally update the condition flags based on the result. +bool +EmulateInstructionARM::EmulateADDReg (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + // ARM pseudo code... + if ConditionPassed() then + EncodingSpecificOperations(); + shifted = Shift(R[m], shift_t, shift_n, APSR.C); + (result, carry, overflow) = AddWithCarry(R[n], shifted, '0'); + if d == 15 then + ALUWritePC(result); // setflags is always FALSE here + else + R[d] = result; + if setflags then + APSR.N = result<31>; + APSR.Z = IsZeroBit(result); + APSR.C = carry; + APSR.V = overflow; +#endif + + bool success = false; + + if (ConditionPassed(opcode)) + { + uint32_t Rd, Rn, Rm; + ARM_ShifterType shift_t; + uint32_t shift_n; // the shift applied to the value read from Rm + bool setflags; + switch (encoding) + { + case eEncodingT1: + Rd = Bits32(opcode, 2, 0); + Rn = Bits32(opcode, 5, 3); + Rm = Bits32(opcode, 8, 6); + setflags = !InITBlock(); + shift_t = SRType_LSL; + shift_n = 0; + break; + case eEncodingT2: + Rd = Rn = Bit32(opcode, 7) << 3 | Bits32(opcode, 2, 0); + Rm = Bits32(opcode, 6, 3); + setflags = false; + shift_t = SRType_LSL; + shift_n = 0; + if (Rn == 15 && Rm == 15) + return false; + if (Rd == 15 && InITBlock() && !LastInITBlock()) + return false; + break; + case eEncodingA1: + Rd = Bits32(opcode, 15, 12); + Rn = Bits32(opcode, 19, 16); + Rm = Bits32(opcode, 3, 0); + setflags = BitIsSet(opcode, 20); + shift_n = DecodeImmShiftARM(opcode, shift_t); + break; + default: + return false; + } + + // Read the first operand. + uint32_t val1 = ReadCoreReg(Rn, &success); + if (!success) + return false; + + // Read the second operand. + uint32_t val2 = ReadCoreReg(Rm, &success); + if (!success) + return false; + + uint32_t shifted = Shift(val2, shift_t, shift_n, APSR_C, &success); + if (!success) + return false; + AddWithCarryResult res = AddWithCarry(val1, shifted, 0); + + EmulateInstruction::Context context; + context.type = eContextArithmetic; + RegisterInfo op1_reg; + RegisterInfo op2_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + Rn, op1_reg); + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + Rm, op2_reg); + context.SetRegisterRegisterOperands (op1_reg, op2_reg); + + if (!WriteCoreRegOptionalFlags(context, res.result, Rd, setflags, res.carry_out, res.overflow)) + return false; + } + return true; +} + +// Compare Negative (immediate) adds a register value and an immediate value. +// It updates the condition flags based on the result, and discards the result. +bool +EmulateInstructionARM::EmulateCMNImm (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + // ARM pseudo code... + if ConditionPassed() then + EncodingSpecificOperations(); + (result, carry, overflow) = AddWithCarry(R[n], imm32, '0'); + APSR.N = result<31>; + APSR.Z = IsZeroBit(result); + APSR.C = carry; + APSR.V = overflow; +#endif + + bool success = false; + + uint32_t Rn; // the first operand + uint32_t imm32; // the immediate value to be compared with + switch (encoding) { + case eEncodingT1: + Rn = Bits32(opcode, 19, 16); + imm32 = ThumbExpandImm(opcode); // imm32 = ThumbExpandImm(i:imm3:imm8) + if (Rn == 15) + return false; + break; + case eEncodingA1: + Rn = Bits32(opcode, 19, 16); + imm32 = ARMExpandImm(opcode); // imm32 = ARMExpandImm(imm12) + break; + default: + return false; + } + // Read the register value from the operand register Rn. + uint32_t reg_val = ReadCoreReg(Rn, &success); + if (!success) + return false; + + AddWithCarryResult res = AddWithCarry(reg_val, imm32, 0); + + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextImmediate; + context.SetNoArgs (); + if (!WriteFlags(context, res.result, res.carry_out, res.overflow)) + return false; + + return true; +} + +// Compare Negative (register) adds a register value and an optionally-shifted register value. +// It updates the condition flags based on the result, and discards the result. +bool +EmulateInstructionARM::EmulateCMNReg (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + // ARM pseudo code... + if ConditionPassed() then + EncodingSpecificOperations(); + shifted = Shift(R[m], shift_t, shift_n, APSR.C); + (result, carry, overflow) = AddWithCarry(R[n], shifted, '0'); + APSR.N = result<31>; + APSR.Z = IsZeroBit(result); + APSR.C = carry; + APSR.V = overflow; +#endif + + bool success = false; + + uint32_t Rn; // the first operand + uint32_t Rm; // the second operand + ARM_ShifterType shift_t; + uint32_t shift_n; // the shift applied to the value read from Rm + switch (encoding) { + case eEncodingT1: + Rn = Bits32(opcode, 2, 0); + Rm = Bits32(opcode, 5, 3); + shift_t = SRType_LSL; + shift_n = 0; + break; + case eEncodingT2: + Rn = Bits32(opcode, 19, 16); + Rm = Bits32(opcode, 3, 0); + shift_n = DecodeImmShiftThumb(opcode, shift_t); + // if n == 15 || BadReg(m) then UNPREDICTABLE; + if (Rn == 15 || BadReg(Rm)) + return false; + break; + case eEncodingA1: + Rn = Bits32(opcode, 19, 16); + Rm = Bits32(opcode, 3, 0); + shift_n = DecodeImmShiftARM(opcode, shift_t); + break; + default: + return false; + } + // Read the register value from register Rn. + uint32_t val1 = ReadCoreReg(Rn, &success); + if (!success) + return false; + + // Read the register value from register Rm. + uint32_t val2 = ReadCoreReg(Rm, &success); + if (!success) + return false; + + uint32_t shifted = Shift(val2, shift_t, shift_n, APSR_C, &success); + if (!success) + return false; + AddWithCarryResult res = AddWithCarry(val1, shifted, 0); + + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextImmediate; + context.SetNoArgs(); + if (!WriteFlags(context, res.result, res.carry_out, res.overflow)) + return false; + + return true; +} + +// Compare (immediate) subtracts an immediate value from a register value. +// It updates the condition flags based on the result, and discards the result. +bool +EmulateInstructionARM::EmulateCMPImm (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + // ARM pseudo code... + if ConditionPassed() then + EncodingSpecificOperations(); + (result, carry, overflow) = AddWithCarry(R[n], NOT(imm32), '1'); + APSR.N = result<31>; + APSR.Z = IsZeroBit(result); + APSR.C = carry; + APSR.V = overflow; +#endif + + bool success = false; + + uint32_t Rn; // the first operand + uint32_t imm32; // the immediate value to be compared with + switch (encoding) { + case eEncodingT1: + Rn = Bits32(opcode, 10, 8); + imm32 = Bits32(opcode, 7, 0); + break; + case eEncodingT2: + Rn = Bits32(opcode, 19, 16); + imm32 = ThumbExpandImm(opcode); // imm32 = ThumbExpandImm(i:imm3:imm8) + if (Rn == 15) + return false; + break; + case eEncodingA1: + Rn = Bits32(opcode, 19, 16); + imm32 = ARMExpandImm(opcode); // imm32 = ARMExpandImm(imm12) + break; + default: + return false; + } + // Read the register value from the operand register Rn. + uint32_t reg_val = ReadCoreReg(Rn, &success); + if (!success) + return false; + + AddWithCarryResult res = AddWithCarry(reg_val, ~imm32, 1); + + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextImmediate; + context.SetNoArgs (); + if (!WriteFlags(context, res.result, res.carry_out, res.overflow)) + return false; + + return true; +} + +// Compare (register) subtracts an optionally-shifted register value from a register value. +// It updates the condition flags based on the result, and discards the result. +bool +EmulateInstructionARM::EmulateCMPReg (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + // ARM pseudo code... + if ConditionPassed() then + EncodingSpecificOperations(); + shifted = Shift(R[m], shift_t, shift_n, APSR.C); + (result, carry, overflow) = AddWithCarry(R[n], NOT(shifted), '1'); + APSR.N = result<31>; + APSR.Z = IsZeroBit(result); + APSR.C = carry; + APSR.V = overflow; +#endif + + bool success = false; + + uint32_t Rn; // the first operand + uint32_t Rm; // the second operand + ARM_ShifterType shift_t; + uint32_t shift_n; // the shift applied to the value read from Rm + switch (encoding) { + case eEncodingT1: + Rn = Bits32(opcode, 2, 0); + Rm = Bits32(opcode, 5, 3); + shift_t = SRType_LSL; + shift_n = 0; + break; + case eEncodingT2: + Rn = Bit32(opcode, 7) << 3 | Bits32(opcode, 2, 0); + Rm = Bits32(opcode, 6, 3); + shift_t = SRType_LSL; + shift_n = 0; + if (Rn < 8 && Rm < 8) + return false; + if (Rn == 15 || Rm == 15) + return false; + break; + case eEncodingA1: + Rn = Bits32(opcode, 19, 16); + Rm = Bits32(opcode, 3, 0); + shift_n = DecodeImmShiftARM(opcode, shift_t); + break; + default: + return false; + } + // Read the register value from register Rn. + uint32_t val1 = ReadCoreReg(Rn, &success); + if (!success) + return false; + + // Read the register value from register Rm. + uint32_t val2 = ReadCoreReg(Rm, &success); + if (!success) + return false; + + uint32_t shifted = Shift(val2, shift_t, shift_n, APSR_C, &success); + if (!success) + return false; + AddWithCarryResult res = AddWithCarry(val1, ~shifted, 1); + + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextImmediate; + context.SetNoArgs(); + if (!WriteFlags(context, res.result, res.carry_out, res.overflow)) + return false; + + return true; +} + +// Arithmetic Shift Right (immediate) shifts a register value right by an immediate number of bits, +// shifting in copies of its sign bit, and writes the result to the destination register. It can +// optionally update the condition flags based on the result. +bool +EmulateInstructionARM::EmulateASRImm (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + // ARM pseudo code... + if ConditionPassed() then + EncodingSpecificOperations(); + (result, carry) = Shift_C(R[m], SRType_ASR, shift_n, APSR.C); + if d == 15 then // Can only occur for ARM encoding + ALUWritePC(result); // setflags is always FALSE here + else + R[d] = result; + if setflags then + APSR.N = result<31>; + APSR.Z = IsZeroBit(result); + APSR.C = carry; + // APSR.V unchanged +#endif + + return EmulateShiftImm (opcode, encoding, SRType_ASR); +} + +// Arithmetic Shift Right (register) shifts a register value right by a variable number of bits, +// shifting in copies of its sign bit, and writes the result to the destination register. +// The variable number of bits is read from the bottom byte of a register. It can optionally update +// the condition flags based on the result. +bool +EmulateInstructionARM::EmulateASRReg (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + // ARM pseudo code... + if ConditionPassed() then + EncodingSpecificOperations(); + shift_n = UInt(R[m]<7:0>); + (result, carry) = Shift_C(R[m], SRType_ASR, shift_n, APSR.C); + R[d] = result; + if setflags then + APSR.N = result<31>; + APSR.Z = IsZeroBit(result); + APSR.C = carry; + // APSR.V unchanged +#endif + + return EmulateShiftReg (opcode, encoding, SRType_ASR); +} + +// Logical Shift Left (immediate) shifts a register value left by an immediate number of bits, +// shifting in zeros, and writes the result to the destination register. It can optionally +// update the condition flags based on the result. +bool +EmulateInstructionARM::EmulateLSLImm (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + // ARM pseudo code... + if ConditionPassed() then + EncodingSpecificOperations(); + (result, carry) = Shift_C(R[m], SRType_LSL, shift_n, APSR.C); + if d == 15 then // Can only occur for ARM encoding + ALUWritePC(result); // setflags is always FALSE here + else + R[d] = result; + if setflags then + APSR.N = result<31>; + APSR.Z = IsZeroBit(result); + APSR.C = carry; + // APSR.V unchanged +#endif + + return EmulateShiftImm (opcode, encoding, SRType_LSL); +} + +// Logical Shift Left (register) shifts a register value left by a variable number of bits, +// shifting in zeros, and writes the result to the destination register. The variable number +// of bits is read from the bottom byte of a register. It can optionally update the condition +// flags based on the result. +bool +EmulateInstructionARM::EmulateLSLReg (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + // ARM pseudo code... + if ConditionPassed() then + EncodingSpecificOperations(); + shift_n = UInt(R[m]<7:0>); + (result, carry) = Shift_C(R[m], SRType_LSL, shift_n, APSR.C); + R[d] = result; + if setflags then + APSR.N = result<31>; + APSR.Z = IsZeroBit(result); + APSR.C = carry; + // APSR.V unchanged +#endif + + return EmulateShiftReg (opcode, encoding, SRType_LSL); +} + +// Logical Shift Right (immediate) shifts a register value right by an immediate number of bits, +// shifting in zeros, and writes the result to the destination register. It can optionally +// update the condition flags based on the result. +bool +EmulateInstructionARM::EmulateLSRImm (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + // ARM pseudo code... + if ConditionPassed() then + EncodingSpecificOperations(); + (result, carry) = Shift_C(R[m], SRType_LSR, shift_n, APSR.C); + if d == 15 then // Can only occur for ARM encoding + ALUWritePC(result); // setflags is always FALSE here + else + R[d] = result; + if setflags then + APSR.N = result<31>; + APSR.Z = IsZeroBit(result); + APSR.C = carry; + // APSR.V unchanged +#endif + + return EmulateShiftImm (opcode, encoding, SRType_LSR); +} + +// Logical Shift Right (register) shifts a register value right by a variable number of bits, +// shifting in zeros, and writes the result to the destination register. The variable number +// of bits is read from the bottom byte of a register. It can optionally update the condition +// flags based on the result. +bool +EmulateInstructionARM::EmulateLSRReg (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + // ARM pseudo code... + if ConditionPassed() then + EncodingSpecificOperations(); + shift_n = UInt(R[m]<7:0>); + (result, carry) = Shift_C(R[m], SRType_LSR, shift_n, APSR.C); + R[d] = result; + if setflags then + APSR.N = result<31>; + APSR.Z = IsZeroBit(result); + APSR.C = carry; + // APSR.V unchanged +#endif + + return EmulateShiftReg (opcode, encoding, SRType_LSR); +} + +// Rotate Right (immediate) provides the value of the contents of a register rotated by a constant value. +// The bits that are rotated off the right end are inserted into the vacated bit positions on the left. +// It can optionally update the condition flags based on the result. +bool +EmulateInstructionARM::EmulateRORImm (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + // ARM pseudo code... + if ConditionPassed() then + EncodingSpecificOperations(); + (result, carry) = Shift_C(R[m], SRType_ROR, shift_n, APSR.C); + if d == 15 then // Can only occur for ARM encoding + ALUWritePC(result); // setflags is always FALSE here + else + R[d] = result; + if setflags then + APSR.N = result<31>; + APSR.Z = IsZeroBit(result); + APSR.C = carry; + // APSR.V unchanged +#endif + + return EmulateShiftImm (opcode, encoding, SRType_ROR); +} + +// Rotate Right (register) provides the value of the contents of a register rotated by a variable number of bits. +// The bits that are rotated off the right end are inserted into the vacated bit positions on the left. +// The variable number of bits is read from the bottom byte of a register. It can optionally update the condition +// flags based on the result. +bool +EmulateInstructionARM::EmulateRORReg (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + // ARM pseudo code... + if ConditionPassed() then + EncodingSpecificOperations(); + shift_n = UInt(R[m]<7:0>); + (result, carry) = Shift_C(R[m], SRType_ROR, shift_n, APSR.C); + R[d] = result; + if setflags then + APSR.N = result<31>; + APSR.Z = IsZeroBit(result); + APSR.C = carry; + // APSR.V unchanged +#endif + + return EmulateShiftReg (opcode, encoding, SRType_ROR); +} + +// Rotate Right with Extend provides the value of the contents of a register shifted right by one place, +// with the carry flag shifted into bit [31]. +// +// RRX can optionally update the condition flags based on the result. +// In that case, bit [0] is shifted into the carry flag. +bool +EmulateInstructionARM::EmulateRRX (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + // ARM pseudo code... + if ConditionPassed() then + EncodingSpecificOperations(); + (result, carry) = Shift_C(R[m], SRType_RRX, 1, APSR.C); + if d == 15 then // Can only occur for ARM encoding + ALUWritePC(result); // setflags is always FALSE here + else + R[d] = result; + if setflags then + APSR.N = result<31>; + APSR.Z = IsZeroBit(result); + APSR.C = carry; + // APSR.V unchanged +#endif + + return EmulateShiftImm (opcode, encoding, SRType_RRX); +} + +bool +EmulateInstructionARM::EmulateShiftImm (const uint32_t opcode, const ARMEncoding encoding, ARM_ShifterType shift_type) +{ +// assert(shift_type == SRType_ASR +// || shift_type == SRType_LSL +// || shift_type == SRType_LSR +// || shift_type == SRType_ROR +// || shift_type == SRType_RRX); + + bool success = false; + + if (ConditionPassed(opcode)) + { + uint32_t Rd; // the destination register + uint32_t Rm; // the first operand register + uint32_t imm5; // encoding for the shift amount + uint32_t carry; // the carry bit after the shift operation + bool setflags; + + // Special case handling! + // A8.6.139 ROR (immediate) -- Encoding T1 + ARMEncoding use_encoding = encoding; + if (shift_type == SRType_ROR && use_encoding == eEncodingT1) + { + // Morph the T1 encoding from the ARM Architecture Manual into T2 encoding to + // have the same decoding of bit fields as the other Thumb2 shift operations. + use_encoding = eEncodingT2; + } + + switch (use_encoding) { + case eEncodingT1: + // Due to the above special case handling! + if (shift_type == SRType_ROR) + return false; + + Rd = Bits32(opcode, 2, 0); + Rm = Bits32(opcode, 5, 3); + setflags = !InITBlock(); + imm5 = Bits32(opcode, 10, 6); + break; + case eEncodingT2: + // A8.6.141 RRX + // There's no imm form of RRX instructions. + if (shift_type == SRType_RRX) + return false; + + Rd = Bits32(opcode, 11, 8); + Rm = Bits32(opcode, 3, 0); + setflags = BitIsSet(opcode, 20); + imm5 = Bits32(opcode, 14, 12) << 2 | Bits32(opcode, 7, 6); + if (BadReg(Rd) || BadReg(Rm)) + return false; + break; + case eEncodingA1: + Rd = Bits32(opcode, 15, 12); + Rm = Bits32(opcode, 3, 0); + setflags = BitIsSet(opcode, 20); + imm5 = Bits32(opcode, 11, 7); + break; + default: + return false; + } + + // A8.6.139 ROR (immediate) + if (shift_type == SRType_ROR && imm5 == 0) + shift_type = SRType_RRX; + + // Get the first operand. + uint32_t value = ReadCoreReg (Rm, &success); + if (!success) + return false; + + // Decode the shift amount if not RRX. + uint32_t amt = (shift_type == SRType_RRX ? 1 : DecodeImmShift(shift_type, imm5)); + + uint32_t result = Shift_C(value, shift_type, amt, APSR_C, carry, &success); + if (!success) + return false; + + // The context specifies that an immediate is to be moved into Rd. + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextImmediate; + context.SetNoArgs (); + + if (!WriteCoreRegOptionalFlags(context, result, Rd, setflags, carry)) + return false; + } + return true; +} + +bool +EmulateInstructionARM::EmulateShiftReg (const uint32_t opcode, const ARMEncoding encoding, ARM_ShifterType shift_type) +{ + // assert(shift_type == SRType_ASR + // || shift_type == SRType_LSL + // || shift_type == SRType_LSR + // || shift_type == SRType_ROR); + + bool success = false; + + if (ConditionPassed(opcode)) + { + uint32_t Rd; // the destination register + uint32_t Rn; // the first operand register + uint32_t Rm; // the register whose bottom byte contains the amount to shift by + uint32_t carry; // the carry bit after the shift operation + bool setflags; + switch (encoding) { + case eEncodingT1: + Rd = Bits32(opcode, 2, 0); + Rn = Rd; + Rm = Bits32(opcode, 5, 3); + setflags = !InITBlock(); + break; + case eEncodingT2: + Rd = Bits32(opcode, 11, 8); + Rn = Bits32(opcode, 19, 16); + Rm = Bits32(opcode, 3, 0); + setflags = BitIsSet(opcode, 20); + if (BadReg(Rd) || BadReg(Rn) || BadReg(Rm)) + return false; + break; + case eEncodingA1: + Rd = Bits32(opcode, 15, 12); + Rn = Bits32(opcode, 3, 0); + Rm = Bits32(opcode, 11, 8); + setflags = BitIsSet(opcode, 20); + if (Rd == 15 || Rn == 15 || Rm == 15) + return false; + break; + default: + return false; + } + + // Get the first operand. + uint32_t value = ReadCoreReg (Rn, &success); + if (!success) + return false; + // Get the Rm register content. + uint32_t val = ReadCoreReg (Rm, &success); + if (!success) + return false; + + // Get the shift amount. + uint32_t amt = Bits32(val, 7, 0); + + uint32_t result = Shift_C(value, shift_type, amt, APSR_C, carry, &success); + if (!success) + return false; + + // The context specifies that an immediate is to be moved into Rd. + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextImmediate; + context.SetNoArgs (); + + if (!WriteCoreRegOptionalFlags(context, result, Rd, setflags, carry)) + return false; + } + return true; +} + +// LDM loads multiple registers from consecutive memory locations, using an +// address from a base register. Optionally the address just above the highest of those locations +// can be written back to the base register. +bool +EmulateInstructionARM::EmulateLDM (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + // ARM pseudo code... + if ConditionPassed() + EncodingSpecificOperations(); NullCheckIfThumbEE (n); + address = R[n]; + + for i = 0 to 14 + if registers == '1' then + R[i] = MemA[address, 4]; address = address + 4; + if registers<15> == '1' then + LoadWritePC (MemA[address, 4]); + + if wback && registers == '0' then R[n] = R[n] + 4 * BitCount (registers); + if wback && registers == '1' then R[n] = bits(32) UNKNOWN; // Only possible for encoding A1 + +#endif + + bool success = false; + bool conditional = false; + if (ConditionPassed(opcode, &conditional)) + { + uint32_t n; + uint32_t registers = 0; + bool wback; + const uint32_t addr_byte_size = GetAddressByteSize(); + switch (encoding) + { + case eEncodingT1: + // n = UInt(Rn); registers = '00000000':register_list; wback = (registers == '0'); + n = Bits32 (opcode, 10, 8); + registers = Bits32 (opcode, 7, 0); + registers = registers & 0x00ff; // Make sure the top 8 bits are zeros. + wback = BitIsClear (registers, n); + // if BitCount(registers) < 1 then UNPREDICTABLE; + if (BitCount(registers) < 1) + return false; + break; + case eEncodingT2: + // if W == '1' && Rn == '1101' then SEE POP; + // n = UInt(Rn); registers = P:M:'0':register_list; wback = (W == '1'); + n = Bits32 (opcode, 19, 16); + registers = Bits32 (opcode, 15, 0); + registers = registers & 0xdfff; // Make sure bit 13 is zero. + wback = BitIsSet (opcode, 21); + + // if n == 15 || BitCount(registers) < 2 || (P == '1' && M == '1') then UNPREDICTABLE; + if ((n == 15) + || (BitCount (registers) < 2) + || (BitIsSet (opcode, 14) && BitIsSet (opcode, 15))) + return false; + + // if registers<15> == '1' && InITBlock() && !LastInITBlock() then UNPREDICTABLE; + if (BitIsSet (registers, 15) && InITBlock() && !LastInITBlock()) + return false; + + // if wback && registers == '1' then UNPREDICTABLE; + if (wback + && BitIsSet (registers, n)) + return false; + break; + + case eEncodingA1: + n = Bits32 (opcode, 19, 16); + registers = Bits32 (opcode, 15, 0); + wback = BitIsSet (opcode, 21); + if ((n == 15) + || (BitCount (registers) < 1)) + return false; + break; + default: + return false; + } + + int32_t offset = 0; + const addr_t base_address = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_r0 + n, 0, &success); + if (!success) + return false; + + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextRegisterPlusOffset; + RegisterInfo dwarf_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, dwarf_reg); + context.SetRegisterPlusOffset (dwarf_reg, offset); + + for (int i = 0; i < 14; ++i) + { + if (BitIsSet (registers, i)) + { + context.type = EmulateInstruction::eContextRegisterPlusOffset; + context.SetRegisterPlusOffset (dwarf_reg, offset); + if (wback && (n == 13)) // Pop Instruction + { + if (conditional) + context.type = EmulateInstruction::eContextRegisterLoad; + else + context.type = EmulateInstruction::eContextPopRegisterOffStack; + } + + // R[i] = MemA [address, 4]; address = address + 4; + uint32_t data = MemARead (context, base_address + offset, addr_byte_size, 0, &success); + if (!success) + return false; + + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + i, data)) + return false; + + offset += addr_byte_size; + } + } + + if (BitIsSet (registers, 15)) + { + //LoadWritePC (MemA [address, 4]); + context.type = EmulateInstruction::eContextRegisterPlusOffset; + context.SetRegisterPlusOffset (dwarf_reg, offset); + uint32_t data = MemARead (context, base_address + offset, addr_byte_size, 0, &success); + if (!success) + return false; + // In ARMv5T and above, this is an interworking branch. + if (!LoadWritePC(context, data)) + return false; + } + + if (wback && BitIsClear (registers, n)) + { + // R[n] = R[n] + 4 * BitCount (registers) + int32_t offset = addr_byte_size * BitCount (registers); + context.type = EmulateInstruction::eContextAdjustBaseRegister; + context.SetRegisterPlusOffset (dwarf_reg, offset); + + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + n, base_address + offset)) + return false; + } + if (wback && BitIsSet (registers, n)) + // R[n] bits(32) UNKNOWN; + return WriteBits32Unknown (n); + } + return true; +} + +// LDMDA loads multiple registers from consecutive memory locations using an address from a base register. +// The consecutive memory locations end at this address and the address just below the lowest of those locations +// can optionally be written back to the base register. +bool +EmulateInstructionARM::EmulateLDMDA (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + // ARM pseudo code... + if ConditionPassed() then + EncodingSpecificOperations(); + address = R[n] - 4*BitCount(registers) + 4; + + for i = 0 to 14 + if registers == '1' then + R[i] = MemA[address,4]; address = address + 4; + + if registers<15> == '1' then + LoadWritePC(MemA[address,4]); + + if wback && registers == '0' then R[n] = R[n] - 4*BitCount(registers); + if wback && registers == '1' then R[n] = bits(32) UNKNOWN; +#endif + + bool success = false; + + if (ConditionPassed(opcode)) + { + uint32_t n; + uint32_t registers = 0; + bool wback; + const uint32_t addr_byte_size = GetAddressByteSize(); + + // EncodingSpecificOperations(); + switch (encoding) + { + case eEncodingA1: + // n = UInt(Rn); registers = register_list; wback = (W == '1'); + n = Bits32 (opcode, 19, 16); + registers = Bits32 (opcode, 15, 0); + wback = BitIsSet (opcode, 21); + + // if n == 15 || BitCount(registers) < 1 then UNPREDICTABLE; + if ((n == 15) || (BitCount (registers) < 1)) + return false; + + break; + + default: + return false; + } + // address = R[n] - 4*BitCount(registers) + 4; + + int32_t offset = 0; + addr_t Rn = ReadCoreReg (n, &success); + + if (!success) + return false; + + addr_t address = Rn - (addr_byte_size * BitCount (registers)) + addr_byte_size; + + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextRegisterPlusOffset; + RegisterInfo dwarf_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, dwarf_reg); + context.SetRegisterPlusOffset (dwarf_reg, offset); + + // for i = 0 to 14 + for (int i = 0; i < 14; ++i) + { + // if registers == '1' then + if (BitIsSet (registers, i)) + { + // R[i] = MemA[address,4]; address = address + 4; + context.SetRegisterPlusOffset (dwarf_reg, Rn - (address + offset)); + uint32_t data = MemARead (context, address + offset, addr_byte_size, 0, &success); + if (!success) + return false; + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + i, data)) + return false; + offset += addr_byte_size; + } + } + + // if registers<15> == '1' then + // LoadWritePC(MemA[address,4]); + if (BitIsSet (registers, 15)) + { + context.SetRegisterPlusOffset (dwarf_reg, offset); + uint32_t data = MemARead (context, address + offset, addr_byte_size, 0, &success); + if (!success) + return false; + // In ARMv5T and above, this is an interworking branch. + if (!LoadWritePC(context, data)) + return false; + } + + // if wback && registers == '0' then R[n] = R[n] - 4*BitCount(registers); + if (wback && BitIsClear (registers, n)) + { + if (!success) + return false; + + offset = (addr_byte_size * BitCount (registers)) * -1; + context.type = EmulateInstruction::eContextAdjustBaseRegister; + context.SetImmediateSigned (offset); + addr_t addr = Rn + offset; + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + n, addr)) + return false; + } + + // if wback && registers == '1' then R[n] = bits(32) UNKNOWN; + if (wback && BitIsSet (registers, n)) + return WriteBits32Unknown (n); + } + return true; +} + +// LDMDB loads multiple registers from consecutive memory locations using an address from a base register. The +// consecutive memory lcoations end just below this address, and the address of the lowest of those locations can +// be optionally written back to the base register. +bool +EmulateInstructionARM::EmulateLDMDB (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + // ARM pseudo code... + if ConditionPassed() then + EncodingSpecificOperations(); NullCheckIfThumbEE(n); + address = R[n] - 4*BitCount(registers); + + for i = 0 to 14 + if registers == '1' then + R[i] = MemA[address,4]; address = address + 4; + if registers<15> == '1' then + LoadWritePC(MemA[address,4]); + + if wback && registers == '0' then R[n] = R[n] - 4*BitCount(registers); + if wback && registers == '1' then R[n] = bits(32) UNKNOWN; // Only possible for encoding A1 +#endif + + bool success = false; + + if (ConditionPassed(opcode)) + { + uint32_t n; + uint32_t registers = 0; + bool wback; + const uint32_t addr_byte_size = GetAddressByteSize(); + switch (encoding) + { + case eEncodingT1: + // n = UInt(Rn); registers = P:M:'0':register_list; wback = (W == '1'); + n = Bits32 (opcode, 19, 16); + registers = Bits32 (opcode, 15, 0); + registers = registers & 0xdfff; // Make sure bit 13 is a zero. + wback = BitIsSet (opcode, 21); + + // if n == 15 || BitCount(registers) < 2 || (P == '1' && M == '1') then UNPREDICTABLE; + if ((n == 15) + || (BitCount (registers) < 2) + || (BitIsSet (opcode, 14) && BitIsSet (opcode, 15))) + return false; + + // if registers<15> == '1' && InITBlock() && !LastInITBlock() then UNPREDICTABLE; + if (BitIsSet (registers, 15) && InITBlock() && !LastInITBlock()) + return false; + + // if wback && registers == '1' then UNPREDICTABLE; + if (wback && BitIsSet (registers, n)) + return false; + + break; + + case eEncodingA1: + // n = UInt(Rn); registers = register_list; wback = (W == '1'); + n = Bits32 (opcode, 19, 16); + registers = Bits32 (opcode, 15, 0); + wback = BitIsSet (opcode, 21); + + // if n == 15 || BitCount(registers) < 1 then UNPREDICTABLE; + if ((n == 15) || (BitCount (registers) < 1)) + return false; + + break; + + default: + return false; + } + + // address = R[n] - 4*BitCount(registers); + + int32_t offset = 0; + addr_t Rn = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_r0 + n, 0, &success); + + if (!success) + return false; + + addr_t address = Rn - (addr_byte_size * BitCount (registers)); + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextRegisterPlusOffset; + RegisterInfo dwarf_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, dwarf_reg); + context.SetRegisterPlusOffset (dwarf_reg, Rn - address); + + for (int i = 0; i < 14; ++i) + { + if (BitIsSet (registers, i)) + { + // R[i] = MemA[address,4]; address = address + 4; + context.SetRegisterPlusOffset (dwarf_reg, Rn - (address + offset)); + uint32_t data = MemARead (context, address + offset, addr_byte_size, 0, &success); + if (!success) + return false; + + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + i, data)) + return false; + + offset += addr_byte_size; + } + } + + // if registers<15> == '1' then + // LoadWritePC(MemA[address,4]); + if (BitIsSet (registers, 15)) + { + context.SetRegisterPlusOffset (dwarf_reg, offset); + uint32_t data = MemARead (context, address + offset, addr_byte_size, 0, &success); + if (!success) + return false; + // In ARMv5T and above, this is an interworking branch. + if (!LoadWritePC(context, data)) + return false; + } + + // if wback && registers == '0' then R[n] = R[n] - 4*BitCount(registers); + if (wback && BitIsClear (registers, n)) + { + if (!success) + return false; + + offset = (addr_byte_size * BitCount (registers)) * -1; + context.type = EmulateInstruction::eContextAdjustBaseRegister; + context.SetImmediateSigned (offset); + addr_t addr = Rn + offset; + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + n, addr)) + return false; + } + + // if wback && registers == '1' then R[n] = bits(32) UNKNOWN; // Only possible for encoding A1 + if (wback && BitIsSet (registers, n)) + return WriteBits32Unknown (n); + } + return true; +} + +// LDMIB loads multiple registers from consecutive memory locations using an address from a base register. The +// consecutive memory locations start just above this address, and thea ddress of the last of those locations can +// optinoally be written back to the base register. +bool +EmulateInstructionARM::EmulateLDMIB (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + if ConditionPassed() then + EncodingSpecificOperations(); + address = R[n] + 4; + + for i = 0 to 14 + if registers == '1' then + R[i] = MemA[address,4]; address = address + 4; + if registers<15> == '1' then + LoadWritePC(MemA[address,4]); + + if wback && registers == '0' then R[n] = R[n] + 4*BitCount(registers); + if wback && registers == '1' then R[n] = bits(32) UNKNOWN; +#endif + + bool success = false; + + if (ConditionPassed(opcode)) + { + uint32_t n; + uint32_t registers = 0; + bool wback; + const uint32_t addr_byte_size = GetAddressByteSize(); + switch (encoding) + { + case eEncodingA1: + // n = UInt(Rn); registers = register_list; wback = (W == '1'); + n = Bits32 (opcode, 19, 16); + registers = Bits32 (opcode, 15, 0); + wback = BitIsSet (opcode, 21); + + // if n == 15 || BitCount(registers) < 1 then UNPREDICTABLE; + if ((n == 15) || (BitCount (registers) < 1)) + return false; + + break; + default: + return false; + } + // address = R[n] + 4; + + int32_t offset = 0; + addr_t Rn = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_r0 + n, 0, &success); + + if (!success) + return false; + + addr_t address = Rn + addr_byte_size; + + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextRegisterPlusOffset; + RegisterInfo dwarf_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, dwarf_reg); + context.SetRegisterPlusOffset (dwarf_reg, offset); + + for (int i = 0; i < 14; ++i) + { + if (BitIsSet (registers, i)) + { + // R[i] = MemA[address,4]; address = address + 4; + + context.SetRegisterPlusOffset (dwarf_reg, offset + addr_byte_size); + uint32_t data = MemARead (context, address + offset, addr_byte_size, 0, &success); + if (!success) + return false; + + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + i, data)) + return false; + + offset += addr_byte_size; + } + } + + // if registers<15> == '1' then + // LoadWritePC(MemA[address,4]); + if (BitIsSet (registers, 15)) + { + context.SetRegisterPlusOffset (dwarf_reg, offset); + uint32_t data = MemARead (context, address + offset, addr_byte_size, 0, &success); + if (!success) + return false; + // In ARMv5T and above, this is an interworking branch. + if (!LoadWritePC(context, data)) + return false; + } + + // if wback && registers == '0' then R[n] = R[n] + 4*BitCount(registers); + if (wback && BitIsClear (registers, n)) + { + if (!success) + return false; + + offset = addr_byte_size * BitCount (registers); + context.type = EmulateInstruction::eContextAdjustBaseRegister; + context.SetImmediateSigned (offset); + addr_t addr = Rn + offset; + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + n, addr)) + return false; + } + + // if wback && registers == '1' then R[n] = bits(32) UNKNOWN; // Only possible for encoding A1 + if (wback && BitIsSet (registers, n)) + return WriteBits32Unknown (n); + } + return true; +} + +// Load Register (immediate) calculates an address from a base register value and +// an immediate offset, loads a word from memory, and writes to a register. +// LDR (immediate, Thumb) +bool +EmulateInstructionARM::EmulateLDRRtRnImm (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + // ARM pseudo code... + if (ConditionPassed()) + { + EncodingSpecificOperations(); NullCheckIfThumbEE(15); + offset_addr = if add then (R[n] + imm32) else (R[n] - imm32); + address = if index then offset_addr else R[n]; + data = MemU[address,4]; + if wback then R[n] = offset_addr; + if t == 15 then + if address<1:0> == '00' then LoadWritePC(data); else UNPREDICTABLE; + elsif UnalignedSupport() || address<1:0> = '00' then + R[t] = data; + else R[t] = bits(32) UNKNOWN; // Can only apply before ARMv7 + } +#endif + + bool success = false; + + if (ConditionPassed(opcode)) + { + uint32_t Rt; // the destination register + uint32_t Rn; // the base register + uint32_t imm32; // the immediate offset used to form the address + addr_t offset_addr; // the offset address + addr_t address; // the calculated address + uint32_t data; // the literal data value from memory load + bool add, index, wback; + switch (encoding) { + case eEncodingT1: + Rt = Bits32(opcode, 2, 0); + Rn = Bits32(opcode, 5, 3); + imm32 = Bits32(opcode, 10, 6) << 2; // imm32 = ZeroExtend(imm5:'00', 32); + // index = TRUE; add = TRUE; wback = FALSE + add = true; + index = true; + wback = false; + + break; + + case eEncodingT2: + // t = UInt(Rt); n = 13; imm32 = ZeroExtend(imm8:'00', 32); + Rt = Bits32 (opcode, 10, 8); + Rn = 13; + imm32 = Bits32 (opcode, 7, 0) << 2; + + // index = TRUE; add = TRUE; wback = FALSE; + index = true; + add = true; + wback = false; + + break; + + case eEncodingT3: + // if Rn == '1111' then SEE LDR (literal); + // t = UInt(Rt); n = UInt(Rn); imm32 = ZeroExtend(imm12, 32); + Rt = Bits32 (opcode, 15, 12); + Rn = Bits32 (opcode, 19, 16); + imm32 = Bits32 (opcode, 11, 0); + + // index = TRUE; add = TRUE; wback = FALSE; + index = true; + add = true; + wback = false; + + // if t == 15 && InITBlock() && !LastInITBlock() then UNPREDICTABLE; + if ((Rt == 15) && InITBlock() && !LastInITBlock()) + return false; + + break; + + case eEncodingT4: + // if Rn == '1111' then SEE LDR (literal); + // if P == '1' && U == '1' && W == '0' then SEE LDRT; + // if Rn == '1101' && P == '0' && U == '1' && W == '1' && imm8 == '00000100' then SEE POP; + // if P == '0' && W == '0' then UNDEFINED; + if (BitIsClear (opcode, 10) && BitIsClear (opcode, 8)) + return false; + + // t = UInt(Rt); n = UInt(Rn); imm32 = ZeroExtend(imm8, 32); + Rt = Bits32 (opcode, 15, 12); + Rn = Bits32 (opcode, 19, 16); + imm32 = Bits32 (opcode, 7, 0); + + // index = (P == '1'); add = (U == '1'); wback = (W == '1'); + index = BitIsSet (opcode, 10); + add = BitIsSet (opcode, 9); + wback = BitIsSet (opcode, 8); + + // if (wback && n == t) || (t == 15 && InITBlock() && !LastInITBlock()) then UNPREDICTABLE; + if ((wback && (Rn == Rt)) || ((Rt == 15) && InITBlock() && !LastInITBlock())) + return false; + + break; + + default: + return false; + } + uint32_t base = ReadCoreReg (Rn, &success); + if (!success) + return false; + if (add) + offset_addr = base + imm32; + else + offset_addr = base - imm32; + + address = (index ? offset_addr : base); + + RegisterInfo base_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + Rn, base_reg); + if (wback) + { + EmulateInstruction::Context ctx; + ctx.type = EmulateInstruction::eContextAdjustBaseRegister; + ctx.SetRegisterPlusOffset (base_reg, (int32_t) (offset_addr - base)); + + if (!WriteRegisterUnsigned (ctx, eRegisterKindDWARF, dwarf_r0 + Rn, offset_addr)) + return false; + } + + // Prepare to write to the Rt register. + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextRegisterLoad; + context.SetRegisterPlusOffset (base_reg, (int32_t) (offset_addr - base)); + + // Read memory from the address. + data = MemURead(context, address, 4, 0, &success); + if (!success) + return false; + + if (Rt == 15) + { + if (Bits32(address, 1, 0) == 0) + { + if (!LoadWritePC(context, data)) + return false; + } + else + return false; + } + else if (UnalignedSupport() || Bits32(address, 1, 0) == 0) + { + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + Rt, data)) + return false; + } + else + WriteBits32Unknown (Rt); + } + return true; +} + +// STM (Store Multiple Increment After) stores multiple registers to consecutive memory locations using an address +// from a base register. The consecutive memory locations start at this address, and teh address just above the last +// of those locations can optionally be written back to the base register. +bool +EmulateInstructionARM::EmulateSTM (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + if ConditionPassed() then + EncodingSpecificOperations(); NullCheckIfThumbEE(n); + address = R[n]; + + for i = 0 to 14 + if registers == '1' then + if i == n && wback && i != LowestSetBit(registers) then + MemA[address,4] = bits(32) UNKNOWN; // Only possible for encodings T1 and A1 + else + MemA[address,4] = R[i]; + address = address + 4; + + if registers<15> == '1' then // Only possible for encoding A1 + MemA[address,4] = PCStoreValue(); + if wback then R[n] = R[n] + 4*BitCount(registers); +#endif + + bool success = false; + + if (ConditionPassed(opcode)) + { + uint32_t n; + uint32_t registers = 0; + bool wback; + const uint32_t addr_byte_size = GetAddressByteSize(); + + // EncodingSpecificOperations(); NullCheckIfThumbEE(n); + switch (encoding) + { + case eEncodingT1: + // n = UInt(Rn); registers = '00000000':register_list; wback = TRUE; + n = Bits32 (opcode, 10, 8); + registers = Bits32 (opcode, 7, 0); + registers = registers & 0x00ff; // Make sure the top 8 bits are zeros. + wback = true; + + // if BitCount(registers) < 1 then UNPREDICTABLE; + if (BitCount (registers) < 1) + return false; + + break; + + case eEncodingT2: + // n = UInt(Rn); registers = '0':M:'0':register_list; wback = (W == '1'); + n = Bits32 (opcode, 19, 16); + registers = Bits32 (opcode, 15, 0); + registers = registers & 0x5fff; // Make sure bits 15 & 13 are zeros. + wback = BitIsSet (opcode, 21); + + // if n == 15 || BitCount(registers) < 2 then UNPREDICTABLE; + if ((n == 15) || (BitCount (registers) < 2)) + return false; + + // if wback && registers == '1' then UNPREDICTABLE; + if (wback && BitIsSet (registers, n)) + return false; + + break; + + case eEncodingA1: + // n = UInt(Rn); registers = register_list; wback = (W == '1'); + n = Bits32 (opcode, 19, 16); + registers = Bits32 (opcode, 15, 0); + wback = BitIsSet (opcode, 21); + + // if n == 15 || BitCount(registers) < 1 then UNPREDICTABLE; + if ((n == 15) || (BitCount (registers) < 1)) + return false; + + break; + + default: + return false; + } + + // address = R[n]; + int32_t offset = 0; + const addr_t address = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_r0 + n, 0, &success); + if (!success) + return false; + + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextRegisterStore; + RegisterInfo base_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, base_reg); + + // for i = 0 to 14 + uint32_t lowest_set_bit = 14; + for (uint32_t i = 0; i < 14; ++i) + { + // if registers == '1' then + if (BitIsSet (registers, i)) + { + if (i < lowest_set_bit) + lowest_set_bit = i; + // if i == n && wback && i != LowestSetBit(registers) then + if ((i == n) && wback && (i != lowest_set_bit)) + // MemA[address,4] = bits(32) UNKNOWN; // Only possible for encodings T1 and A1 + WriteBits32UnknownToMemory (address + offset); + else + { + // MemA[address,4] = R[i]; + uint32_t data = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_r0 + i, 0, &success); + if (!success) + return false; + + RegisterInfo data_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + i, data_reg); + context.SetRegisterToRegisterPlusOffset (data_reg, base_reg, offset); + if (!MemAWrite (context, address + offset, data, addr_byte_size)) + return false; + } + + // address = address + 4; + offset += addr_byte_size; + } + } + + // if registers<15> == '1' then // Only possible for encoding A1 + // MemA[address,4] = PCStoreValue(); + if (BitIsSet (registers, 15)) + { + RegisterInfo pc_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_pc, pc_reg); + context.SetRegisterPlusOffset (pc_reg, 8); + const uint32_t pc = ReadCoreReg (PC_REG, &success); + if (!success) + return false; + + if (!MemAWrite (context, address + offset, pc, addr_byte_size)) + return false; + } + + // if wback then R[n] = R[n] + 4*BitCount(registers); + if (wback) + { + offset = addr_byte_size * BitCount (registers); + context.type = EmulateInstruction::eContextAdjustBaseRegister; + context.SetImmediateSigned (offset); + addr_t data = address + offset; + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + n, data)) + return false; + } + } + return true; +} + +// STMDA (Store Multiple Decrement After) stores multiple registers to consecutive memory locations using an address +// from a base register. The consecutive memory locations end at this address, and the address just below the lowest +// of those locations can optionally be written back to the base register. +bool +EmulateInstructionARM::EmulateSTMDA (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + if ConditionPassed() then + EncodingSpecificOperations(); + address = R[n] - 4*BitCount(registers) + 4; + + for i = 0 to 14 + if registers == '1' then + if i == n && wback && i != LowestSetBit(registers) then + MemA[address,4] = bits(32) UNKNOWN; + else + MemA[address,4] = R[i]; + address = address + 4; + + if registers<15> == '1' then + MemA[address,4] = PCStoreValue(); + + if wback then R[n] = R[n] - 4*BitCount(registers); +#endif + + bool success = false; + + if (ConditionPassed(opcode)) + { + uint32_t n; + uint32_t registers = 0; + bool wback; + const uint32_t addr_byte_size = GetAddressByteSize(); + + // EncodingSpecificOperations(); + switch (encoding) + { + case eEncodingA1: + // n = UInt(Rn); registers = register_list; wback = (W == '1'); + n = Bits32 (opcode, 19, 16); + registers = Bits32 (opcode, 15, 0); + wback = BitIsSet (opcode, 21); + + // if n == 15 || BitCount(registers) < 1 then UNPREDICTABLE; + if ((n == 15) || (BitCount (registers) < 1)) + return false; + break; + default: + return false; + } + + // address = R[n] - 4*BitCount(registers) + 4; + int32_t offset = 0; + addr_t Rn = ReadCoreReg (n, &success); + if (!success) + return false; + + addr_t address = Rn - (addr_byte_size * BitCount (registers)) + 4; + + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextRegisterStore; + RegisterInfo base_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, base_reg); + + // for i = 0 to 14 + uint32_t lowest_bit_set = 14; + for (uint32_t i = 0; i < 14; ++i) + { + // if registers == '1' then + if (BitIsSet (registers, i)) + { + if (i < lowest_bit_set) + lowest_bit_set = i; + //if i == n && wback && i != LowestSetBit(registers) then + if ((i == n) && wback && (i != lowest_bit_set)) + // MemA[address,4] = bits(32) UNKNOWN; + WriteBits32UnknownToMemory (address + offset); + else + { + // MemA[address,4] = R[i]; + uint32_t data = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_r0 + i, 0, &success); + if (!success) + return false; + + RegisterInfo data_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + i, data_reg); + context.SetRegisterToRegisterPlusOffset (data_reg, base_reg, Rn - (address + offset)); + if (!MemAWrite (context, address + offset, data, addr_byte_size)) + return false; + } + + // address = address + 4; + offset += addr_byte_size; + } + } + + // if registers<15> == '1' then + // MemA[address,4] = PCStoreValue(); + if (BitIsSet (registers, 15)) + { + RegisterInfo pc_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_pc, pc_reg); + context.SetRegisterPlusOffset (pc_reg, 8); + const uint32_t pc = ReadCoreReg (PC_REG, &success); + if (!success) + return false; + + if (!MemAWrite (context, address + offset, pc, addr_byte_size)) + return false; + } + + // if wback then R[n] = R[n] - 4*BitCount(registers); + if (wback) + { + offset = (addr_byte_size * BitCount (registers)) * -1; + context.type = EmulateInstruction::eContextAdjustBaseRegister; + context.SetImmediateSigned (offset); + addr_t data = Rn + offset; + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + n, data)) + return false; + } + } + return true; +} + +// STMDB (Store Multiple Decrement Before) stores multiple registers to consecutive memory locations using an address +// from a base register. The consecutive memory locations end just below this address, and the address of the first of +// those locations can optionally be written back to the base register. +bool +EmulateInstructionARM::EmulateSTMDB (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + if ConditionPassed() then + EncodingSpecificOperations(); NullCheckIfThumbEE(n); + address = R[n] - 4*BitCount(registers); + + for i = 0 to 14 + if registers == '1' then + if i == n && wback && i != LowestSetBit(registers) then + MemA[address,4] = bits(32) UNKNOWN; // Only possible for encoding A1 + else + MemA[address,4] = R[i]; + address = address + 4; + + if registers<15> == '1' then // Only possible for encoding A1 + MemA[address,4] = PCStoreValue(); + + if wback then R[n] = R[n] - 4*BitCount(registers); +#endif + + + bool success = false; + + if (ConditionPassed(opcode)) + { + uint32_t n; + uint32_t registers = 0; + bool wback; + const uint32_t addr_byte_size = GetAddressByteSize(); + + // EncodingSpecificOperations(); NullCheckIfThumbEE(n); + switch (encoding) + { + case eEncodingT1: + // if W == '1' && Rn == '1101' then SEE PUSH; + if ((BitIsSet (opcode, 21)) && (Bits32 (opcode, 19, 16) == 13)) + { + // See PUSH + } + // n = UInt(Rn); registers = '0':M:'0':register_list; wback = (W == '1'); + n = Bits32 (opcode, 19, 16); + registers = Bits32 (opcode, 15, 0); + registers = registers & 0x5fff; // Make sure bits 15 & 13 are zeros. + wback = BitIsSet (opcode, 21); + // if n == 15 || BitCount(registers) < 2 then UNPREDICTABLE; + if ((n == 15) || BitCount (registers) < 2) + return false; + // if wback && registers == '1' then UNPREDICTABLE; + if (wback && BitIsSet (registers, n)) + return false; + break; + + case eEncodingA1: + // if W == '1' && Rn == '1101Õ && BitCount(register_list) >= 2 then SEE PUSH; + if (BitIsSet (opcode, 21) && (Bits32 (opcode, 19, 16) == 13) && BitCount (Bits32 (opcode, 15, 0)) >= 2) + { + // See Push + } + // n = UInt(Rn); registers = register_list; wback = (W == '1'); + n = Bits32 (opcode, 19, 16); + registers = Bits32 (opcode, 15, 0); + wback = BitIsSet (opcode, 21); + // if n == 15 || BitCount(registers) < 1 then UNPREDICTABLE; + if ((n == 15) || BitCount (registers) < 1) + return false; + break; + + default: + return false; + } + + // address = R[n] - 4*BitCount(registers); + + int32_t offset = 0; + addr_t Rn = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_r0 + n, 0, &success); + if (!success) + return false; + + addr_t address = Rn - (addr_byte_size * BitCount (registers)); + + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextRegisterStore; + RegisterInfo base_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, base_reg); + + // for i = 0 to 14 + uint32_t lowest_set_bit = 14; + for (uint32_t i = 0; i < 14; ++i) + { + // if registers == '1' then + if (BitIsSet (registers, i)) + { + if (i < lowest_set_bit) + lowest_set_bit = i; + // if i == n && wback && i != LowestSetBit(registers) then + if ((i == n) && wback && (i != lowest_set_bit)) + // MemA[address,4] = bits(32) UNKNOWN; // Only possible for encoding A1 + WriteBits32UnknownToMemory (address + offset); + else + { + // MemA[address,4] = R[i]; + uint32_t data = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_r0 + i, 0, &success); + if (!success) + return false; + + RegisterInfo data_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + i, data_reg); + context.SetRegisterToRegisterPlusOffset (data_reg, base_reg, Rn - (address + offset)); + if (!MemAWrite (context, address + offset, data, addr_byte_size)) + return false; + } + + // address = address + 4; + offset += addr_byte_size; + } + } + + // if registers<15> == '1' then // Only possible for encoding A1 + // MemA[address,4] = PCStoreValue(); + if (BitIsSet (registers, 15)) + { + RegisterInfo pc_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_pc, pc_reg); + context.SetRegisterPlusOffset (pc_reg, 8); + const uint32_t pc = ReadCoreReg (PC_REG, &success); + if (!success) + return false; + + if (!MemAWrite (context, address + offset, pc, addr_byte_size)) + return false; + } + + // if wback then R[n] = R[n] - 4*BitCount(registers); + if (wback) + { + offset = (addr_byte_size * BitCount (registers)) * -1; + context.type = EmulateInstruction::eContextAdjustBaseRegister; + context.SetImmediateSigned (offset); + addr_t data = Rn + offset; + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + n, data)) + return false; + } + } + return true; +} + +// STMIB (Store Multiple Increment Before) stores multiple registers to consecutive memory locations using an address +// from a base register. The consecutive memory locations start just above this address, and the address of the last +// of those locations can optionally be written back to the base register. +bool +EmulateInstructionARM::EmulateSTMIB (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + if ConditionPassed() then + EncodingSpecificOperations(); + address = R[n] + 4; + + for i = 0 to 14 + if registers == '1' then + if i == n && wback && i != LowestSetBit(registers) then + MemA[address,4] = bits(32) UNKNOWN; + else + MemA[address,4] = R[i]; + address = address + 4; + + if registers<15> == '1' then + MemA[address,4] = PCStoreValue(); + + if wback then R[n] = R[n] + 4*BitCount(registers); +#endif + + bool success = false; + + if (ConditionPassed(opcode)) + { + uint32_t n; + uint32_t registers = 0; + bool wback; + const uint32_t addr_byte_size = GetAddressByteSize(); + + // EncodingSpecificOperations(); + switch (encoding) + { + case eEncodingA1: + // n = UInt(Rn); registers = register_list; wback = (W == '1'); + n = Bits32 (opcode, 19, 16); + registers = Bits32 (opcode, 15, 0); + wback = BitIsSet (opcode, 21); + + // if n == 15 || BitCount(registers) < 1 then UNPREDICTABLE; + if ((n == 15) && (BitCount (registers) < 1)) + return false; + break; + default: + return false; + } + // address = R[n] + 4; + + int32_t offset = 0; + addr_t Rn = ReadCoreReg (n, &success); + if (!success) + return false; + + addr_t address = Rn + addr_byte_size; + + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextRegisterStore; + RegisterInfo base_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, base_reg); + + uint32_t lowest_set_bit = 14; + // for i = 0 to 14 + for (uint32_t i = 0; i < 14; ++i) + { + // if registers == '1' then + if (BitIsSet (registers, i)) + { + if (i < lowest_set_bit) + lowest_set_bit = i; + // if i == n && wback && i != LowestSetBit(registers) then + if ((i == n) && wback && (i != lowest_set_bit)) + // MemA[address,4] = bits(32) UNKNOWN; + WriteBits32UnknownToMemory (address + offset); + // else + else + { + // MemA[address,4] = R[i]; + uint32_t data = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_r0 + i, 0, &success); + if (!success) + return false; + + RegisterInfo data_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + i, data_reg); + context.SetRegisterToRegisterPlusOffset (data_reg, base_reg, offset + addr_byte_size); + if (!MemAWrite (context, address + offset, data, addr_byte_size)) + return false; + } + + // address = address + 4; + offset += addr_byte_size; + } + } + + // if registers<15> == '1' then + // MemA[address,4] = PCStoreValue(); + if (BitIsSet (registers, 15)) + { + RegisterInfo pc_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_pc, pc_reg); + context.SetRegisterPlusOffset (pc_reg, 8); + const uint32_t pc = ReadCoreReg (PC_REG, &success); + if (!success) + return false; + + if (!MemAWrite (context, address + offset, pc, addr_byte_size)) + return false; + } + + // if wback then R[n] = R[n] + 4*BitCount(registers); + if (wback) + { + offset = addr_byte_size * BitCount (registers); + context.type = EmulateInstruction::eContextAdjustBaseRegister; + context.SetImmediateSigned (offset); + addr_t data = Rn + offset; + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + n, data)) + return false; + } + } + return true; +} + +// STR (store immediate) calcualtes an address from a base register value and an immediate offset, and stores a word +// from a register to memory. It can use offset, post-indexed, or pre-indexed addressing. +bool +EmulateInstructionARM::EmulateSTRThumb (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + if ConditionPassed() then + EncodingSpecificOperations(); NullCheckIfThumbEE(n); + offset_addr = if add then (R[n] + imm32) else (R[n] - imm32); + address = if index then offset_addr else R[n]; + if UnalignedSupport() || address<1:0> == '00' then + MemU[address,4] = R[t]; + else // Can only occur before ARMv7 + MemU[address,4] = bits(32) UNKNOWN; + if wback then R[n] = offset_addr; +#endif + + bool success = false; + + if (ConditionPassed(opcode)) + { + const uint32_t addr_byte_size = GetAddressByteSize(); + + uint32_t t; + uint32_t n; + uint32_t imm32; + bool index; + bool add; + bool wback; + // EncodingSpecificOperations (); NullCheckIfThumbEE(n); + switch (encoding) + { + case eEncodingT1: + // t = UInt(Rt); n = UInt(Rn); imm32 = ZeroExtend(imm5:'00', 32); + t = Bits32 (opcode, 2, 0); + n = Bits32 (opcode, 5, 3); + imm32 = Bits32 (opcode, 10, 6) << 2; + + // index = TRUE; add = TRUE; wback = FALSE; + index = true; + add = false; + wback = false; + break; + + case eEncodingT2: + // t = UInt(Rt); n = 13; imm32 = ZeroExtend(imm8:'00', 32); + t = Bits32 (opcode, 10, 8); + n = 13; + imm32 = Bits32 (opcode, 7, 0) << 2; + + // index = TRUE; add = TRUE; wback = FALSE; + index = true; + add = true; + wback = false; + break; + + case eEncodingT3: + // if Rn == '1111' then UNDEFINED; + if (Bits32 (opcode, 19, 16) == 15) + return false; + + // t = UInt(Rt); n = UInt(Rn); imm32 = ZeroExtend(imm12, 32); + t = Bits32 (opcode, 15, 12); + n = Bits32 (opcode, 19, 16); + imm32 = Bits32 (opcode, 11, 0); + + // index = TRUE; add = TRUE; wback = FALSE; + index = true; + add = true; + wback = false; + + // if t == 15 then UNPREDICTABLE; + if (t == 15) + return false; + break; + + case eEncodingT4: + // if P == '1' && U == '1' && W == '0' then SEE STRT; + // if Rn == '1101' && P == '1' && U == '0' && W == '1' && imm8 == '00000100' then SEE PUSH; + // if Rn == '1111' || (P == '0' && W == '0') then UNDEFINED; + if ((Bits32 (opcode, 19, 16) == 15) + || (BitIsClear (opcode, 10) && BitIsClear (opcode, 8))) + return false; + + // t = UInt(Rt); n = UInt(Rn); imm32 = ZeroExtend(imm8, 32); + t = Bits32 (opcode, 15, 12); + n = Bits32 (opcode, 19, 16); + imm32 = Bits32 (opcode, 7, 0); + + // index = (P == '1'); add = (U == '1'); wback = (W == '1'); + index = BitIsSet (opcode, 10); + add = BitIsSet (opcode, 9); + wback = BitIsSet (opcode, 8); + + // if t == 15 || (wback && n == t) then UNPREDICTABLE; + if ((t == 15) || (wback && (n == t))) + return false; + break; + + default: + return false; + } + + addr_t offset_addr; + addr_t address; + + // offset_addr = if add then (R[n] + imm32) else (R[n] - imm32); + uint32_t base_address = ReadCoreReg (n, &success); + if (!success) + return false; + + if (add) + offset_addr = base_address + imm32; + else + offset_addr = base_address - imm32; + + // address = if index then offset_addr else R[n]; + if (index) + address = offset_addr; + else + address = base_address; + + EmulateInstruction::Context context; + context.type = eContextRegisterStore; + RegisterInfo base_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, base_reg); + + // if UnalignedSupport() || address<1:0> == '00' then + if (UnalignedSupport () || (BitIsClear (address, 1) && BitIsClear (address, 0))) + { + // MemU[address,4] = R[t]; + uint32_t data = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_r0 + t, 0, &success); + if (!success) + return false; + + RegisterInfo data_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + t, data_reg); + int32_t offset = address - base_address; + context.SetRegisterToRegisterPlusOffset (data_reg, base_reg, offset); + if (!MemUWrite (context, address, data, addr_byte_size)) + return false; + } + else + { + // MemU[address,4] = bits(32) UNKNOWN; + WriteBits32UnknownToMemory (address); + } + + // if wback then R[n] = offset_addr; + if (wback) + { + context.type = eContextRegisterLoad; + context.SetAddress (offset_addr); + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + n, offset_addr)) + return false; + } + } + return true; +} + +// STR (Store Register) calculates an address from a base register value and an offset register value, stores a +// word from a register to memory. The offset register value can optionally be shifted. +bool +EmulateInstructionARM::EmulateSTRRegister (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + if ConditionPassed() then + EncodingSpecificOperations(); NullCheckIfThumbEE(n); + offset = Shift(R[m], shift_t, shift_n, APSR.C); + offset_addr = if add then (R[n] + offset) else (R[n] - offset); + address = if index then offset_addr else R[n]; + if t == 15 then // Only possible for encoding A1 + data = PCStoreValue(); + else + data = R[t]; + if UnalignedSupport() || address<1:0> == '00' || CurrentInstrSet() == InstrSet_ARM then + MemU[address,4] = data; + else // Can only occur before ARMv7 + MemU[address,4] = bits(32) UNKNOWN; + if wback then R[n] = offset_addr; +#endif + + bool success = false; + + if (ConditionPassed(opcode)) + { + const uint32_t addr_byte_size = GetAddressByteSize(); + + uint32_t t; + uint32_t n; + uint32_t m; + ARM_ShifterType shift_t; + uint32_t shift_n; + bool index; + bool add; + bool wback; + + // EncodingSpecificOperations (); NullCheckIfThumbEE(n); + switch (encoding) + { + case eEncodingT1: + // if CurrentInstrSet() == InstrSet_ThumbEE then SEE "Modified operation in ThumbEE"; + // t = UInt(Rt); n = UInt(Rn); m = UInt(Rm); + t = Bits32 (opcode, 2, 0); + n = Bits32 (opcode, 5, 3); + m = Bits32 (opcode, 8, 6); + + // index = TRUE; add = TRUE; wback = FALSE; + index = true; + add = true; + wback = false; + + // (shift_t, shift_n) = (SRType_LSL, 0); + shift_t = SRType_LSL; + shift_n = 0; + break; + + case eEncodingT2: + // if Rn == '1111' then UNDEFINED; + if (Bits32 (opcode, 19, 16) == 15) + return false; + + // t = UInt(Rt); n = UInt(Rn); m = UInt(Rm); + t = Bits32 (opcode, 15, 12); + n = Bits32 (opcode, 19, 16); + m = Bits32 (opcode, 3, 0); + + // index = TRUE; add = TRUE; wback = FALSE; + index = true; + add = true; + wback = false; + + // (shift_t, shift_n) = (SRType_LSL, UInt(imm2)); + shift_t = SRType_LSL; + shift_n = Bits32 (opcode, 5, 4); + + // if t == 15 || BadReg(m) then UNPREDICTABLE; + if ((t == 15) || (BadReg (m))) + return false; + break; + + case eEncodingA1: + { + // if P == '0' && W == '1' then SEE STRT; + // t = UInt(Rt); n = UInt(Rn); m = UInt(Rm); + t = Bits32 (opcode, 15, 12); + n = Bits32 (opcode, 19, 16); + m = Bits32 (opcode, 3, 0); + + // index = (P == '1'); add = (U == '1'); wback = (P == '0') || (W == '1'); + index = BitIsSet (opcode, 24); + add = BitIsSet (opcode, 23); + wback = (BitIsClear (opcode, 24) || BitIsSet (opcode, 21)); + + // (shift_t, shift_n) = DecodeImmShift(type, imm5); + uint32_t typ = Bits32 (opcode, 6, 5); + uint32_t imm5 = Bits32 (opcode, 11, 7); + shift_n = DecodeImmShift(typ, imm5, shift_t); + + // if m == 15 then UNPREDICTABLE; + if (m == 15) + return false; + + // if wback && (n == 15 || n == t) then UNPREDICTABLE; + if (wback && ((n == 15) || (n == t))) + return false; + + break; + } + default: + return false; + } + + addr_t offset_addr; + addr_t address; + int32_t offset = 0; + + addr_t base_address = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_r0 + n, 0, &success); + if (!success) + return false; + + uint32_t Rm_data = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_r0 + m, 0, &success); + if (!success) + return false; + + // offset = Shift(R[m], shift_t, shift_n, APSR.C); + offset = Shift (Rm_data, shift_t, shift_n, APSR_C, &success); + if (!success) + return false; + + // offset_addr = if add then (R[n] + offset) else (R[n] - offset); + if (add) + offset_addr = base_address + offset; + else + offset_addr = base_address - offset; + + // address = if index then offset_addr else R[n]; + if (index) + address = offset_addr; + else + address = base_address; + + uint32_t data; + // if t == 15 then // Only possible for encoding A1 + if (t == 15) + // data = PCStoreValue(); + data = ReadCoreReg (PC_REG, &success); + else + // data = R[t]; + data = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_r0 + t, 0, &success); + + if (!success) + return false; + + EmulateInstruction::Context context; + context.type = eContextRegisterStore; + + // if UnalignedSupport() || address<1:0> == '00' || CurrentInstrSet() == InstrSet_ARM then + if (UnalignedSupport () + || (BitIsClear (address, 1) && BitIsClear (address, 0)) + || CurrentInstrSet() == eModeARM) + { + // MemU[address,4] = data; + + RegisterInfo base_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, base_reg); + + RegisterInfo data_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + t, data_reg); + + context.SetRegisterToRegisterPlusOffset (data_reg, base_reg, address - base_address); + if (!MemUWrite (context, address, data, addr_byte_size)) + return false; + + } + else + // MemU[address,4] = bits(32) UNKNOWN; + WriteBits32UnknownToMemory (address); + + // if wback then R[n] = offset_addr; + if (wback) + { + context.type = eContextRegisterLoad; + context.SetAddress (offset_addr); + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + n, offset_addr)) + return false; + } + + } + return true; +} + +bool +EmulateInstructionARM::EmulateSTRBThumb (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + if ConditionPassed() then + EncodingSpecificOperations(); NullCheckIfThumbEE(n); + offset_addr = if add then (R[n] + imm32) else (R[n] - imm32); + address = if index then offset_addr else R[n]; + MemU[address,1] = R[t]<7:0>; + if wback then R[n] = offset_addr; +#endif + + + bool success = false; + + if (ConditionPassed(opcode)) + { + uint32_t t; + uint32_t n; + uint32_t imm32; + bool index; + bool add; + bool wback; + // EncodingSpecificOperations(); NullCheckIfThumbEE(n); + switch (encoding) + { + case eEncodingT1: + // t = UInt(Rt); n = UInt(Rn); imm32 = ZeroExtend(imm5, 32); + t = Bits32 (opcode, 2, 0); + n = Bits32 (opcode, 5, 3); + imm32 = Bits32 (opcode, 10, 6); + + // index = TRUE; add = TRUE; wback = FALSE; + index = true; + add = true; + wback = false; + break; + + case eEncodingT2: + // if Rn == '1111' then UNDEFINED; + if (Bits32 (opcode, 19, 16) == 15) + return false; + + // t = UInt(Rt); n = UInt(Rn); imm32 = ZeroExtend(imm12, 32); + t = Bits32 (opcode, 15, 12); + n = Bits32 (opcode, 19, 16); + imm32 = Bits32 (opcode, 11, 0); + + // index = TRUE; add = TRUE; wback = FALSE; + index = true; + add = true; + wback = false; + + // if BadReg(t) then UNPREDICTABLE; + if (BadReg (t)) + return false; + break; + + case eEncodingT3: + // if P == '1' && U == '1' && W == '0' then SEE STRBT; + // if Rn == '1111' || (P == '0' && W == '0') then UNDEFINED; + if (Bits32 (opcode, 19, 16) == 15) + return false; + + // t = UInt(Rt); n = UInt(Rn); imm32 = ZeroExtend(imm8, 32); + t = Bits32 (opcode, 15, 12); + n = Bits32 (opcode, 19, 16); + imm32 = Bits32 (opcode, 7, 0); + + // index = (P == '1'); add = (U == '1'); wback = (W == '1'); + index = BitIsSet (opcode, 10); + add = BitIsSet (opcode, 9); + wback = BitIsSet (opcode, 8); + + // if BadReg(t) || (wback && n == t) then UNPREDICTABLE + if ((BadReg (t)) || (wback && (n == t))) + return false; + break; + + default: + return false; + } + + addr_t offset_addr; + addr_t address; + addr_t base_address = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_r0 + n, 0, &success); + if (!success) + return false; + + // offset_addr = if add then (R[n] + imm32) else (R[n] - imm32); + if (add) + offset_addr = base_address + imm32; + else + offset_addr = base_address - imm32; + + // address = if index then offset_addr else R[n]; + if (index) + address = offset_addr; + else + address = base_address; + + // MemU[address,1] = R[t]<7:0> + RegisterInfo base_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, base_reg); + + RegisterInfo data_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + t, data_reg); + + EmulateInstruction::Context context; + context.type = eContextRegisterStore; + context.SetRegisterToRegisterPlusOffset (data_reg, base_reg, address - base_address); + + uint32_t data = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_r0 + t, 0, &success); + if (!success) + return false; + + data = Bits32 (data, 7, 0); + + if (!MemUWrite (context, address, data, 1)) + return false; + + // if wback then R[n] = offset_addr; + if (wback) + { + context.type = eContextRegisterLoad; + context.SetAddress (offset_addr); + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + n, offset_addr)) + return false; + } + + } + + return true; +} + +// STRH (register) calculates an address from a base register value and an offset register value, and stores a +// halfword from a register to memory. The offset register alue can be shifted left by 0, 1, 2, or 3 bits. +bool +EmulateInstructionARM::EmulateSTRHRegister (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + if ConditionPassed() then + EncodingSpecificOperations(); NullCheckIfThumbEE(n); + offset = Shift(R[m], shift_t, shift_n, APSR.C); + offset_addr = if add then (R[n] + offset) else (R[n] - offset); + address = if index then offset_addr else R[n]; + if UnalignedSupport() || address<0> == '0' then + MemU[address,2] = R[t]<15:0>; + else // Can only occur before ARMv7 + MemU[address,2] = bits(16) UNKNOWN; + if wback then R[n] = offset_addr; +#endif + + bool success = false; + + if (ConditionPassed(opcode)) + { + uint32_t t; + uint32_t n; + uint32_t m; + bool index; + bool add; + bool wback; + ARM_ShifterType shift_t; + uint32_t shift_n; + + // EncodingSpecificOperations(); NullCheckIfThumbEE(n); + switch (encoding) + { + case eEncodingT1: + // if CurrentInstrSet() == InstrSet_ThumbEE then SEE "Modified operation in ThumbEE"; + // t = UInt(Rt); n = UInt(Rn); m = UInt(Rm); + t = Bits32 (opcode, 2, 0); + n = Bits32 (opcode, 5, 3); + m = Bits32 (opcode, 8, 6); + + // index = TRUE; add = TRUE; wback = FALSE; + index = true; + add = true; + wback = false; + + // (shift_t, shift_n) = (SRType_LSL, 0); + shift_t = SRType_LSL; + shift_n = 0; + + break; + + case eEncodingT2: + // if Rn == '1111' then UNDEFINED; + // t = UInt(Rt); n = UInt(Rn); m = UInt(Rm); + t = Bits32 (opcode, 15, 12); + n = Bits32 (opcode, 19, 16); + m = Bits32 (opcode, 3, 0); + if (n == 15) + return false; + + // index = TRUE; add = TRUE; wback = FALSE; + index = true; + add = true; + wback = false; + + // (shift_t, shift_n) = (SRType_LSL, UInt(imm2)); + shift_t = SRType_LSL; + shift_n = Bits32 (opcode, 5, 4); + + // if BadReg(t) || BadReg(m) then UNPREDICTABLE; + if (BadReg (t) || BadReg (m)) + return false; + + break; + + case eEncodingA1: + // if P == '0' && W == '1' then SEE STRHT; + // t = UInt(Rt); n = UInt(Rn); m = UInt(Rm); + t = Bits32 (opcode, 15, 12); + n = Bits32 (opcode, 19, 16); + m = Bits32 (opcode, 3, 0); + + // index = (P == '1'); add = (U == '1'); wback = (P == '0') || (W == '1'); + index = BitIsSet (opcode, 24); + add = BitIsSet (opcode, 23); + wback = (BitIsClear (opcode, 24) || BitIsSet (opcode, 21)); + + // (shift_t, shift_n) = (SRType_LSL, 0); + shift_t = SRType_LSL; + shift_n = 0; + + // if t == 15 || m == 15 then UNPREDICTABLE; + if ((t == 15) || (m == 15)) + return false; + + // if wback && (n == 15 || n == t) then UNPREDICTABLE; + if (wback && ((n == 15) || (n == t))) + return false; + + break; + + default: + return false; + } + + uint32_t Rm = ReadCoreReg (m, &success); + if (!success) + return false; + + uint32_t Rn = ReadCoreReg (n, &success); + if (!success) + return false; + + // offset = Shift(R[m], shift_t, shift_n, APSR.C); + uint32_t offset = Shift (Rm, shift_t, shift_n, APSR_C, &success); + if (!success) + return false; + + // offset_addr = if add then (R[n] + offset) else (R[n] - offset); + addr_t offset_addr; + if (add) + offset_addr = Rn + offset; + else + offset_addr = Rn - offset; + + // address = if index then offset_addr else R[n]; + addr_t address; + if (index) + address = offset_addr; + else + address = Rn; + + EmulateInstruction::Context context; + context.type = eContextRegisterStore; + RegisterInfo base_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, base_reg); + RegisterInfo offset_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + m, offset_reg); + + // if UnalignedSupport() || address<0> == '0' then + if (UnalignedSupport() || BitIsClear (address, 0)) + { + // MemU[address,2] = R[t]<15:0>; + uint32_t Rt = ReadCoreReg (t, &success); + if (!success) + return false; + + EmulateInstruction::Context context; + context.type = eContextRegisterStore; + RegisterInfo base_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, base_reg); + RegisterInfo offset_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + m, offset_reg); + RegisterInfo data_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + t, data_reg); + context.SetRegisterToRegisterPlusIndirectOffset (base_reg, offset_reg, data_reg); + + if (!MemUWrite (context, address, Bits32 (Rt, 15, 0), 2)) + return false; + } + else // Can only occur before ARMv7 + { + // MemU[address,2] = bits(16) UNKNOWN; + } + + // if wback then R[n] = offset_addr; + if (wback) + { + context.type = eContextAdjustBaseRegister; + context.SetAddress (offset_addr); + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + n, offset_addr)) + return false; + } + } + + return true; +} + +// Add with Carry (immediate) adds an immediate value and the carry flag value to a register value, +// and writes the result to the destination register. It can optionally update the condition flags +// based on the result. +bool +EmulateInstructionARM::EmulateADCImm (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + // ARM pseudo code... + if ConditionPassed() then + EncodingSpecificOperations(); + (result, carry, overflow) = AddWithCarry(R[n], imm32, APSR.C); + if d == 15 then // Can only occur for ARM encoding + ALUWritePC(result); // setflags is always FALSE here + else + R[d] = result; + if setflags then + APSR.N = result<31>; + APSR.Z = IsZeroBit(result); + APSR.C = carry; + APSR.V = overflow; +#endif + + bool success = false; + + if (ConditionPassed(opcode)) + { + uint32_t Rd, Rn; + uint32_t imm32; // the immediate value to be added to the value obtained from Rn + bool setflags; + switch (encoding) + { + case eEncodingT1: + Rd = Bits32(opcode, 11, 8); + Rn = Bits32(opcode, 19, 16); + setflags = BitIsSet(opcode, 20); + imm32 = ThumbExpandImm(opcode); // imm32 = ThumbExpandImm(i:imm3:imm8) + if (BadReg(Rd) || BadReg(Rn)) + return false; + break; + case eEncodingA1: + Rd = Bits32(opcode, 15, 12); + Rn = Bits32(opcode, 19, 16); + setflags = BitIsSet(opcode, 20); + imm32 = ARMExpandImm(opcode); // imm32 = ARMExpandImm(imm12) + + if (Rd == 15 && setflags) + return EmulateSUBSPcLrEtc (opcode, encoding); + break; + default: + return false; + } + + // Read the first operand. + int32_t val1 = ReadCoreReg(Rn, &success); + if (!success) + return false; + + AddWithCarryResult res = AddWithCarry(val1, imm32, APSR_C); + + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextImmediate; + context.SetNoArgs (); + + if (!WriteCoreRegOptionalFlags(context, res.result, Rd, setflags, res.carry_out, res.overflow)) + return false; + } + return true; +} + +// Add with Carry (register) adds a register value, the carry flag value, and an optionally-shifted +// register value, and writes the result to the destination register. It can optionally update the +// condition flags based on the result. +bool +EmulateInstructionARM::EmulateADCReg (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + // ARM pseudo code... + if ConditionPassed() then + EncodingSpecificOperations(); + shifted = Shift(R[m], shift_t, shift_n, APSR.C); + (result, carry, overflow) = AddWithCarry(R[n], shifted, APSR.C); + if d == 15 then // Can only occur for ARM encoding + ALUWritePC(result); // setflags is always FALSE here + else + R[d] = result; + if setflags then + APSR.N = result<31>; + APSR.Z = IsZeroBit(result); + APSR.C = carry; + APSR.V = overflow; +#endif + + bool success = false; + + if (ConditionPassed(opcode)) + { + uint32_t Rd, Rn, Rm; + ARM_ShifterType shift_t; + uint32_t shift_n; // the shift applied to the value read from Rm + bool setflags; + switch (encoding) + { + case eEncodingT1: + Rd = Rn = Bits32(opcode, 2, 0); + Rm = Bits32(opcode, 5, 3); + setflags = !InITBlock(); + shift_t = SRType_LSL; + shift_n = 0; + break; + case eEncodingT2: + Rd = Bits32(opcode, 11, 8); + Rn = Bits32(opcode, 19, 16); + Rm = Bits32(opcode, 3, 0); + setflags = BitIsSet(opcode, 20); + shift_n = DecodeImmShiftThumb(opcode, shift_t); + if (BadReg(Rd) || BadReg(Rn) || BadReg(Rm)) + return false; + break; + case eEncodingA1: + Rd = Bits32(opcode, 15, 12); + Rn = Bits32(opcode, 19, 16); + Rm = Bits32(opcode, 3, 0); + setflags = BitIsSet(opcode, 20); + shift_n = DecodeImmShiftARM(opcode, shift_t); + + if (Rd == 15 && setflags) + return EmulateSUBSPcLrEtc (opcode, encoding); + break; + default: + return false; + } + + // Read the first operand. + int32_t val1 = ReadCoreReg(Rn, &success); + if (!success) + return false; + + // Read the second operand. + int32_t val2 = ReadCoreReg(Rm, &success); + if (!success) + return false; + + uint32_t shifted = Shift(val2, shift_t, shift_n, APSR_C, &success); + if (!success) + return false; + AddWithCarryResult res = AddWithCarry(val1, shifted, APSR_C); + + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextImmediate; + context.SetNoArgs (); + + if (!WriteCoreRegOptionalFlags(context, res.result, Rd, setflags, res.carry_out, res.overflow)) + return false; + } + return true; +} + +// This instruction adds an immediate value to the PC value to form a PC-relative address, +// and writes the result to the destination register. +bool +EmulateInstructionARM::EmulateADR (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + // ARM pseudo code... + if ConditionPassed() then + EncodingSpecificOperations(); + result = if add then (Align(PC,4) + imm32) else (Align(PC,4) - imm32); + if d == 15 then // Can only occur for ARM encodings + ALUWritePC(result); + else + R[d] = result; +#endif + + bool success = false; + + if (ConditionPassed(opcode)) + { + uint32_t Rd; + uint32_t imm32; // the immediate value to be added/subtracted to/from the PC + bool add; + switch (encoding) + { + case eEncodingT1: + Rd = Bits32(opcode, 10, 8); + imm32 = ThumbImm8Scaled(opcode); // imm32 = ZeroExtend(imm8:'00', 32) + add = true; + break; + case eEncodingT2: + case eEncodingT3: + Rd = Bits32(opcode, 11, 8); + imm32 = ThumbImm12(opcode); // imm32 = ZeroExtend(i:imm3:imm8, 32) + add = (Bits32(opcode, 24, 21) == 0); // 0b0000 => ADD; 0b0101 => SUB + if (BadReg(Rd)) + return false; + break; + case eEncodingA1: + case eEncodingA2: + Rd = Bits32(opcode, 15, 12); + imm32 = ARMExpandImm(opcode); // imm32 = ARMExpandImm(imm12) + add = (Bits32(opcode, 24, 21) == 0x4); // 0b0100 => ADD; 0b0010 => SUB + break; + default: + return false; + } + + // Read the PC value. + uint32_t pc = ReadCoreReg(PC_REG, &success); + if (!success) + return false; + + uint32_t result = (add ? Align(pc, 4) + imm32 : Align(pc, 4) - imm32); + + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextImmediate; + context.SetNoArgs (); + + if (!WriteCoreReg(context, result, Rd)) + return false; + } + return true; +} + +// This instruction performs a bitwise AND of a register value and an immediate value, and writes the result +// to the destination register. It can optionally update the condition flags based on the result. +bool +EmulateInstructionARM::EmulateANDImm (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + // ARM pseudo code... + if ConditionPassed() then + EncodingSpecificOperations(); + result = R[n] AND imm32; + if d == 15 then // Can only occur for ARM encoding + ALUWritePC(result); // setflags is always FALSE here + else + R[d] = result; + if setflags then + APSR.N = result<31>; + APSR.Z = IsZeroBit(result); + APSR.C = carry; + // APSR.V unchanged +#endif + + bool success = false; + + if (ConditionPassed(opcode)) + { + uint32_t Rd, Rn; + uint32_t imm32; // the immediate value to be ANDed to the value obtained from Rn + bool setflags; + uint32_t carry; // the carry bit after ARM/Thumb Expand operation + switch (encoding) + { + case eEncodingT1: + Rd = Bits32(opcode, 11, 8); + Rn = Bits32(opcode, 19, 16); + setflags = BitIsSet(opcode, 20); + imm32 = ThumbExpandImm_C(opcode, APSR_C, carry); // (imm32, carry) = ThumbExpandImm(i:imm3:imm8, APSR.C) + // if Rd == '1111' && S == '1' then SEE TST (immediate); + if (Rd == 15 && setflags) + return EmulateTSTImm(opcode, eEncodingT1); + if (Rd == 13 || (Rd == 15 && !setflags) || BadReg(Rn)) + return false; + break; + case eEncodingA1: + Rd = Bits32(opcode, 15, 12); + Rn = Bits32(opcode, 19, 16); + setflags = BitIsSet(opcode, 20); + imm32 = ARMExpandImm_C(opcode, APSR_C, carry); // (imm32, carry) = ARMExpandImm(imm12, APSR.C) + + if (Rd == 15 && setflags) + return EmulateSUBSPcLrEtc (opcode, encoding); + break; + default: + return false; + } + + // Read the first operand. + uint32_t val1 = ReadCoreReg(Rn, &success); + if (!success) + return false; + + uint32_t result = val1 & imm32; + + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextImmediate; + context.SetNoArgs (); + + if (!WriteCoreRegOptionalFlags(context, result, Rd, setflags, carry)) + return false; + } + return true; +} + +// This instruction performs a bitwise AND of a register value and an optionally-shifted register value, +// and writes the result to the destination register. It can optionally update the condition flags +// based on the result. +bool +EmulateInstructionARM::EmulateANDReg (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + // ARM pseudo code... + if ConditionPassed() then + EncodingSpecificOperations(); + (shifted, carry) = Shift_C(R[m], shift_t, shift_n, APSR.C); + result = R[n] AND shifted; + if d == 15 then // Can only occur for ARM encoding + ALUWritePC(result); // setflags is always FALSE here + else + R[d] = result; + if setflags then + APSR.N = result<31>; + APSR.Z = IsZeroBit(result); + APSR.C = carry; + // APSR.V unchanged +#endif + + bool success = false; + + if (ConditionPassed(opcode)) + { + uint32_t Rd, Rn, Rm; + ARM_ShifterType shift_t; + uint32_t shift_n; // the shift applied to the value read from Rm + bool setflags; + uint32_t carry; + switch (encoding) + { + case eEncodingT1: + Rd = Rn = Bits32(opcode, 2, 0); + Rm = Bits32(opcode, 5, 3); + setflags = !InITBlock(); + shift_t = SRType_LSL; + shift_n = 0; + break; + case eEncodingT2: + Rd = Bits32(opcode, 11, 8); + Rn = Bits32(opcode, 19, 16); + Rm = Bits32(opcode, 3, 0); + setflags = BitIsSet(opcode, 20); + shift_n = DecodeImmShiftThumb(opcode, shift_t); + // if Rd == '1111' && S == '1' then SEE TST (register); + if (Rd == 15 && setflags) + return EmulateTSTReg(opcode, eEncodingT2); + if (Rd == 13 || (Rd == 15 && !setflags) || BadReg(Rn) || BadReg(Rm)) + return false; + break; + case eEncodingA1: + Rd = Bits32(opcode, 15, 12); + Rn = Bits32(opcode, 19, 16); + Rm = Bits32(opcode, 3, 0); + setflags = BitIsSet(opcode, 20); + shift_n = DecodeImmShiftARM(opcode, shift_t); + + if (Rd == 15 && setflags) + return EmulateSUBSPcLrEtc (opcode, encoding); + break; + default: + return false; + } + + // Read the first operand. + uint32_t val1 = ReadCoreReg(Rn, &success); + if (!success) + return false; + + // Read the second operand. + uint32_t val2 = ReadCoreReg(Rm, &success); + if (!success) + return false; + + uint32_t shifted = Shift_C(val2, shift_t, shift_n, APSR_C, carry, &success); + if (!success) + return false; + uint32_t result = val1 & shifted; + + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextImmediate; + context.SetNoArgs (); + + if (!WriteCoreRegOptionalFlags(context, result, Rd, setflags, carry)) + return false; + } + return true; +} + +// Bitwise Bit Clear (immediate) performs a bitwise AND of a register value and the complement of an +// immediate value, and writes the result to the destination register. It can optionally update the +// condition flags based on the result. +bool +EmulateInstructionARM::EmulateBICImm (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + // ARM pseudo code... + if ConditionPassed() then + EncodingSpecificOperations(); + result = R[n] AND NOT(imm32); + if d == 15 then // Can only occur for ARM encoding + ALUWritePC(result); // setflags is always FALSE here + else + R[d] = result; + if setflags then + APSR.N = result<31>; + APSR.Z = IsZeroBit(result); + APSR.C = carry; + // APSR.V unchanged +#endif + + bool success = false; + + if (ConditionPassed(opcode)) + { + uint32_t Rd, Rn; + uint32_t imm32; // the immediate value to be bitwise inverted and ANDed to the value obtained from Rn + bool setflags; + uint32_t carry; // the carry bit after ARM/Thumb Expand operation + switch (encoding) + { + case eEncodingT1: + Rd = Bits32(opcode, 11, 8); + Rn = Bits32(opcode, 19, 16); + setflags = BitIsSet(opcode, 20); + imm32 = ThumbExpandImm_C(opcode, APSR_C, carry); // (imm32, carry) = ThumbExpandImm(i:imm3:imm8, APSR.C) + if (BadReg(Rd) || BadReg(Rn)) + return false; + break; + case eEncodingA1: + Rd = Bits32(opcode, 15, 12); + Rn = Bits32(opcode, 19, 16); + setflags = BitIsSet(opcode, 20); + imm32 = ARMExpandImm_C(opcode, APSR_C, carry); // (imm32, carry) = ARMExpandImm(imm12, APSR.C) + + // if Rd == '1111' && S == '1' then SEE SUBS PC, LR and related instructions; + if (Rd == 15 && setflags) + return EmulateSUBSPcLrEtc (opcode, encoding); + break; + default: + return false; + } + + // Read the first operand. + uint32_t val1 = ReadCoreReg(Rn, &success); + if (!success) + return false; + + uint32_t result = val1 & ~imm32; + + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextImmediate; + context.SetNoArgs (); + + if (!WriteCoreRegOptionalFlags(context, result, Rd, setflags, carry)) + return false; + } + return true; +} + +// Bitwise Bit Clear (register) performs a bitwise AND of a register value and the complement of an +// optionally-shifted register value, and writes the result to the destination register. +// It can optionally update the condition flags based on the result. +bool +EmulateInstructionARM::EmulateBICReg (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + // ARM pseudo code... + if ConditionPassed() then + EncodingSpecificOperations(); + (shifted, carry) = Shift_C(R[m], shift_t, shift_n, APSR.C); + result = R[n] AND NOT(shifted); + if d == 15 then // Can only occur for ARM encoding + ALUWritePC(result); // setflags is always FALSE here + else + R[d] = result; + if setflags then + APSR.N = result<31>; + APSR.Z = IsZeroBit(result); + APSR.C = carry; + // APSR.V unchanged +#endif + + bool success = false; + + if (ConditionPassed(opcode)) + { + uint32_t Rd, Rn, Rm; + ARM_ShifterType shift_t; + uint32_t shift_n; // the shift applied to the value read from Rm + bool setflags; + uint32_t carry; + switch (encoding) + { + case eEncodingT1: + Rd = Rn = Bits32(opcode, 2, 0); + Rm = Bits32(opcode, 5, 3); + setflags = !InITBlock(); + shift_t = SRType_LSL; + shift_n = 0; + break; + case eEncodingT2: + Rd = Bits32(opcode, 11, 8); + Rn = Bits32(opcode, 19, 16); + Rm = Bits32(opcode, 3, 0); + setflags = BitIsSet(opcode, 20); + shift_n = DecodeImmShiftThumb(opcode, shift_t); + if (BadReg(Rd) || BadReg(Rn) || BadReg(Rm)) + return false; + break; + case eEncodingA1: + Rd = Bits32(opcode, 15, 12); + Rn = Bits32(opcode, 19, 16); + Rm = Bits32(opcode, 3, 0); + setflags = BitIsSet(opcode, 20); + shift_n = DecodeImmShiftARM(opcode, shift_t); + + // if Rd == '1111' && S == '1' then SEE SUBS PC, LR and related instructions; + if (Rd == 15 && setflags) + return EmulateSUBSPcLrEtc (opcode, encoding); + break; + default: + return false; + } + + // Read the first operand. + uint32_t val1 = ReadCoreReg(Rn, &success); + if (!success) + return false; + + // Read the second operand. + uint32_t val2 = ReadCoreReg(Rm, &success); + if (!success) + return false; + + uint32_t shifted = Shift_C(val2, shift_t, shift_n, APSR_C, carry, &success); + if (!success) + return false; + uint32_t result = val1 & ~shifted; + + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextImmediate; + context.SetNoArgs (); + + if (!WriteCoreRegOptionalFlags(context, result, Rd, setflags, carry)) + return false; + } + return true; +} + +// LDR (immediate, ARM) calculates an address from a base register value and an immediate offset, loads a word +// from memory, and writes it to a register. It can use offset, post-indexed, or pre-indexed addressing. +bool +EmulateInstructionARM::EmulateLDRImmediateARM (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + if ConditionPassed() then + EncodingSpecificOperations(); + offset_addr = if add then (R[n] + imm32) else (R[n] - imm32); + address = if index then offset_addr else R[n]; + data = MemU[address,4]; + if wback then R[n] = offset_addr; + if t == 15 then + if address<1:0> == '00' then LoadWritePC(data); else UNPREDICTABLE; + elsif UnalignedSupport() || address<1:0> = '00' then + R[t] = data; + else // Can only apply before ARMv7 + R[t] = ROR(data, 8*UInt(address<1:0>)); +#endif + + bool success = false; + + if (ConditionPassed(opcode)) + { + const uint32_t addr_byte_size = GetAddressByteSize(); + + uint32_t t; + uint32_t n; + uint32_t imm32; + bool index; + bool add; + bool wback; + + switch (encoding) + { + case eEncodingA1: + // if Rn == '1111' then SEE LDR (literal); + // if P == '0' && W == '1' then SEE LDRT; + // if Rn == '1101' && P == '0' && U == '1' && W == '0' && imm12 == '000000000100' then SEE POP; + // t == UInt(Rt); n = UInt(Rn); imm32 = ZeroExtend(imm12, 32); + t = Bits32 (opcode, 15, 12); + n = Bits32 (opcode, 19, 16); + imm32 = Bits32 (opcode, 11, 0); + + // index = (P == '1'); add = (U == '1'); wback = (P == '0') || (W == '1'); + index = BitIsSet (opcode, 24); + add = BitIsSet (opcode, 23); + wback = (BitIsClear (opcode, 24) || BitIsSet (opcode, 21)); + + // if wback && n == t then UNPREDICTABLE; + if (wback && (n == t)) + return false; + + break; + + default: + return false; + } + + addr_t address; + addr_t offset_addr; + addr_t base_address = ReadCoreReg (n, &success); + if (!success) + return false; + + // offset_addr = if add then (R[n] + imm32) else (R[n] - imm32); + if (add) + offset_addr = base_address + imm32; + else + offset_addr = base_address - imm32; + + // address = if index then offset_addr else R[n]; + if (index) + address = offset_addr; + else + address = base_address; + + // data = MemU[address,4]; + + RegisterInfo base_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, base_reg); + + EmulateInstruction::Context context; + context.type = eContextRegisterLoad; + context.SetRegisterPlusOffset (base_reg, address - base_address); + + uint64_t data = MemURead (context, address, addr_byte_size, 0, &success); + if (!success) + return false; + + // if wback then R[n] = offset_addr; + if (wback) + { + context.type = eContextAdjustBaseRegister; + context.SetAddress (offset_addr); + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + n, offset_addr)) + return false; + } + + // if t == 15 then + if (t == 15) + { + // if address<1:0> == '00' then LoadWritePC(data); else UNPREDICTABLE; + if (BitIsClear (address, 1) && BitIsClear (address, 0)) + { + // LoadWritePC (data); + context.type = eContextRegisterLoad; + context.SetRegisterPlusOffset (base_reg, address - base_address); + LoadWritePC (context, data); + } + else + return false; + } + // elsif UnalignedSupport() || address<1:0> = '00' then + else if (UnalignedSupport() || (BitIsClear (address, 1) && BitIsClear (address, 0))) + { + // R[t] = data; + context.type = eContextRegisterLoad; + context.SetRegisterPlusOffset (base_reg, address - base_address); + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + t, data)) + return false; + } + // else // Can only apply before ARMv7 + else + { + // R[t] = ROR(data, 8*UInt(address<1:0>)); + data = ROR (data, Bits32 (address, 1, 0), &success); + if (!success) + return false; + context.type = eContextRegisterLoad; + context.SetImmediate (data); + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + t, data)) + return false; + } + + } + return true; +} + +// LDR (register) calculates an address from a base register value and an offset register value, loads a word +// from memory, and writes it to a resgister. The offset register value can optionally be shifted. +bool +EmulateInstructionARM::EmulateLDRRegister (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + if ConditionPassed() then + EncodingSpecificOperations(); NullCheckIfThumbEE(n); + offset = Shift(R[m], shift_t, shift_n, APSR.C); + offset_addr = if add then (R[n] + offset) else (R[n] - offset); + address = if index then offset_addr else R[n]; + data = MemU[address,4]; + if wback then R[n] = offset_addr; + if t == 15 then + if address<1:0> == '00' then LoadWritePC(data); else UNPREDICTABLE; + elsif UnalignedSupport() || address<1:0> = '00' then + R[t] = data; + else // Can only apply before ARMv7 + if CurrentInstrSet() == InstrSet_ARM then + R[t] = ROR(data, 8*UInt(address<1:0>)); + else + R[t] = bits(32) UNKNOWN; +#endif + + bool success = false; + + if (ConditionPassed(opcode)) + { + const uint32_t addr_byte_size = GetAddressByteSize(); + + uint32_t t; + uint32_t n; + uint32_t m; + bool index; + bool add; + bool wback; + ARM_ShifterType shift_t; + uint32_t shift_n; + + switch (encoding) + { + case eEncodingT1: + // if CurrentInstrSet() == InstrSet_ThumbEE then SEE "Modified operation in ThumbEE"; + // t = UInt(Rt); n = UInt(Rn); m = UInt(Rm); + t = Bits32 (opcode, 2, 0); + n = Bits32 (opcode, 5, 3); + m = Bits32 (opcode, 8, 6); + + // index = TRUE; add = TRUE; wback = FALSE; + index = true; + add = true; + wback = false; + + // (shift_t, shift_n) = (SRType_LSL, 0); + shift_t = SRType_LSL; + shift_n = 0; + + break; + + case eEncodingT2: + // if Rn == '1111' then SEE LDR (literal); + // t = UInt(Rt); n = UInt(Rn); m = UInt(Rm); + t = Bits32 (opcode, 15, 12); + n = Bits32 (opcode, 19, 16); + m = Bits32 (opcode, 3, 0); + + // index = TRUE; add = TRUE; wback = FALSE; + index = true; + add = true; + wback = false; + + // (shift_t, shift_n) = (SRType_LSL, UInt(imm2)); + shift_t = SRType_LSL; + shift_n = Bits32 (opcode, 5, 4); + + // if BadReg(m) then UNPREDICTABLE; + if (BadReg (m)) + return false; + + // if t == 15 && InITBlock() && !LastInITBlock() then UNPREDICTABLE; + if ((t == 15) && InITBlock() && !LastInITBlock()) + return false; + + break; + + case eEncodingA1: + { + // if P == '0' && W == '1' then SEE LDRT; + // t = UInt(Rt); n = UInt(Rn); m = UInt(Rm); + t = Bits32 (opcode, 15, 12); + n = Bits32 (opcode, 19, 16); + m = Bits32 (opcode, 3, 0); + + // index = (P == '1'); add = (U == '1'); wback = (P == '0') || (W == '1'); + index = BitIsSet (opcode, 24); + add = BitIsSet (opcode, 23); + wback = (BitIsClear (opcode, 24) || BitIsSet (opcode, 21)); + + // (shift_t, shift_n) = DecodeImmShift(type, imm5); + uint32_t type = Bits32 (opcode, 6, 5); + uint32_t imm5 = Bits32 (opcode, 11, 7); + shift_n = DecodeImmShift (type, imm5, shift_t); + + // if m == 15 then UNPREDICTABLE; + if (m == 15) + return false; + + // if wback && (n == 15 || n == t) then UNPREDICTABLE; + if (wback && ((n == 15) || (n == t))) + return false; + } + break; + + + default: + return false; + } + + uint32_t Rm = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_r0 + m, 0, &success); + if (!success) + return false; + + uint32_t Rn = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_r0 + n, 0, &success); + if (!success) + return false; + + addr_t offset_addr; + addr_t address; + + // offset = Shift(R[m], shift_t, shift_n, APSR.C); -- Note "The APSR is an application level alias for the CPSR". + addr_t offset = Shift (Rm, shift_t, shift_n, Bit32 (m_opcode_cpsr, APSR_C), &success); + if (!success) + return false; + + // offset_addr = if add then (R[n] + offset) else (R[n] - offset); + if (add) + offset_addr = Rn + offset; + else + offset_addr = Rn - offset; + + // address = if index then offset_addr else R[n]; + if (index) + address = offset_addr; + else + address = Rn; + + // data = MemU[address,4]; + RegisterInfo base_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, base_reg); + + EmulateInstruction::Context context; + context.type = eContextRegisterLoad; + context.SetRegisterPlusOffset (base_reg, address - Rn); + + uint64_t data = MemURead (context, address, addr_byte_size, 0, &success); + if (!success) + return false; + + // if wback then R[n] = offset_addr; + if (wback) + { + context.type = eContextAdjustBaseRegister; + context.SetAddress (offset_addr); + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + n, offset_addr)) + return false; + } + + // if t == 15 then + if (t == 15) + { + // if address<1:0> == '00' then LoadWritePC(data); else UNPREDICTABLE; + if (BitIsClear (address, 1) && BitIsClear (address, 0)) + { + context.type = eContextRegisterLoad; + context.SetRegisterPlusOffset (base_reg, address - Rn); + LoadWritePC (context, data); + } + else + return false; + } + // elsif UnalignedSupport() || address<1:0> = '00' then + else if (UnalignedSupport () || (BitIsClear (address, 1) && BitIsClear (address, 0))) + { + // R[t] = data; + context.type = eContextRegisterLoad; + context.SetRegisterPlusOffset (base_reg, address - Rn); + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + t, data)) + return false; + } + else // Can only apply before ARMv7 + { + // if CurrentInstrSet() == InstrSet_ARM then + if (CurrentInstrSet () == eModeARM) + { + // R[t] = ROR(data, 8*UInt(address<1:0>)); + data = ROR (data, Bits32 (address, 1, 0), &success); + if (!success) + return false; + context.type = eContextRegisterLoad; + context.SetImmediate (data); + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + t, data)) + return false; + } + else + { + // R[t] = bits(32) UNKNOWN; + WriteBits32Unknown (t); + } + } + } + return true; +} + +// LDRB (immediate, Thumb) +bool +EmulateInstructionARM::EmulateLDRBImmediate (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + if ConditionPassed() then + EncodingSpecificOperations(); NullCheckIfThumbEE(n); + offset_addr = if add then (R[n] + imm32) else (R[n] - imm32); + address = if index then offset_addr else R[n]; + R[t] = ZeroExtend(MemU[address,1], 32); + if wback then R[n] = offset_addr; +#endif + + bool success = false; + + if (ConditionPassed(opcode)) + { + uint32_t t; + uint32_t n; + uint32_t imm32; + bool index; + bool add; + bool wback; + + // EncodingSpecificOperations(); NullCheckIfThumbEE(n); + switch (encoding) + { + case eEncodingT1: + // t = UInt(Rt); n = UInt(Rn); imm32 = ZeroExtend(imm5, 32); + t = Bits32 (opcode, 2, 0); + n = Bits32 (opcode, 5, 3); + imm32 = Bits32 (opcode, 10, 6); + + // index = TRUE; add = TRUE; wback = FALSE; + index = true; + add = true; + wback= false; + + break; + + case eEncodingT2: + // if Rt == '1111' then SEE PLD; + // if Rn == '1111' then SEE LDRB (literal); + // t = UInt(Rt); n = UInt(Rn); imm32 = ZeroExtend(imm12, 32); + t = Bits32 (opcode, 15, 12); + n = Bits32 (opcode, 19, 16); + imm32 = Bits32 (opcode, 11, 0); + + // index = TRUE; add = TRUE; wback = FALSE; + index = true; + add = true; + wback = false; + + // if t == 13 then UNPREDICTABLE; + if (t == 13) + return false; + + break; + + case eEncodingT3: + // if Rt == '1111' && P == '1' && U == '0' && W == '0' then SEE PLD; + // if Rn == '1111' then SEE LDRB (literal); + // if P == '1' && U == '1' && W == '0' then SEE LDRBT; + // if P == '0' && W == '0' then UNDEFINED; + if (BitIsClear (opcode, 10) && BitIsClear (opcode, 8)) + return false; + + // t = UInt(Rt); n = UInt(Rn); imm32 = ZeroExtend(imm8, 32); + t = Bits32 (opcode, 15, 12); + n = Bits32 (opcode, 19, 16); + imm32 = Bits32 (opcode, 7, 0); + + // index = (P == '1'); add = (U == '1'); wback = (W == '1'); + index = BitIsSet (opcode, 10); + add = BitIsSet (opcode, 9); + wback = BitIsSet (opcode, 8); + + // if BadReg(t) || (wback && n == t) then UNPREDICTABLE; + if (BadReg (t) || (wback && (n == t))) + return false; + + break; + + default: + return false; + } + + uint32_t Rn = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_r0 + n, 0, &success); + if (!success) + return false; + + addr_t address; + addr_t offset_addr; + + // offset_addr = if add then (R[n] + imm32) else (R[n] - imm32); + if (add) + offset_addr = Rn + imm32; + else + offset_addr = Rn - imm32; + + // address = if index then offset_addr else R[n]; + if (index) + address = offset_addr; + else + address = Rn; + + // R[t] = ZeroExtend(MemU[address,1], 32); + RegisterInfo base_reg; + RegisterInfo data_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, base_reg); + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + t, data_reg); + + EmulateInstruction::Context context; + context.type = eContextRegisterLoad; + context.SetRegisterToRegisterPlusOffset (data_reg, base_reg, address - Rn); + + uint64_t data = MemURead (context, address, 1, 0, &success); + if (!success) + return false; + + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + t, data)) + return false; + + // if wback then R[n] = offset_addr; + if (wback) + { + context.type = eContextAdjustBaseRegister; + context.SetAddress (offset_addr); + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + n, offset_addr)) + return false; + } + } + return true; +} + +// LDRB (literal) calculates an address from the PC value and an immediate offset, loads a byte from memory, +// zero-extends it to form a 32-bit word and writes it to a register. +bool +EmulateInstructionARM::EmulateLDRBLiteral (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + if ConditionPassed() then + EncodingSpecificOperations(); NullCheckIfThumbEE(15); + base = Align(PC,4); + address = if add then (base + imm32) else (base - imm32); + R[t] = ZeroExtend(MemU[address,1], 32); +#endif + + bool success = false; + + if (ConditionPassed(opcode)) + { + uint32_t t; + uint32_t imm32; + bool add; + switch (encoding) + { + case eEncodingT1: + // if Rt == '1111' then SEE PLD; + // t = UInt(Rt); imm32 = ZeroExtend(imm12, 32); add = (U == '1'); + t = Bits32 (opcode, 15, 12); + imm32 = Bits32 (opcode, 11, 0); + add = BitIsSet (opcode, 23); + + // if t == 13 then UNPREDICTABLE; + if (t == 13) + return false; + + break; + + case eEncodingA1: + // t == UInt(Rt); imm32 = ZeroExtend(imm12, 32); add = (U == '1'); + t = Bits32 (opcode, 15, 12); + imm32 = Bits32 (opcode, 11, 0); + add = BitIsSet (opcode, 23); + + // if t == 15 then UNPREDICTABLE; + if (t == 15) + return false; + break; + + default: + return false; + } + + // base = Align(PC,4); + uint32_t pc_val = ReadCoreReg (PC_REG, &success); + if (!success) + return false; + + uint32_t base = AlignPC (pc_val); + + addr_t address; + // address = if add then (base + imm32) else (base - imm32); + if (add) + address = base + imm32; + else + address = base - imm32; + + // R[t] = ZeroExtend(MemU[address,1], 32); + EmulateInstruction::Context context; + context.type = eContextRelativeBranchImmediate; + context.SetImmediate (address - base); + + uint64_t data = MemURead (context, address, 1, 0, &success); + if (!success) + return false; + + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + t, data)) + return false; + } + return true; +} + +// LDRB (register) calculates an address from a base register value and an offset rigister value, loads a byte from +// memory, zero-extends it to form a 32-bit word, and writes it to a register. The offset register value can +// optionally be shifted. +bool +EmulateInstructionARM::EmulateLDRBRegister (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + if ConditionPassed() then + EncodingSpecificOperations(); NullCheckIfThumbEE(n); + offset = Shift(R[m], shift_t, shift_n, APSR.C); + offset_addr = if add then (R[n] + offset) else (R[n] - offset); + address = if index then offset_addr else R[n]; + R[t] = ZeroExtend(MemU[address,1],32); + if wback then R[n] = offset_addr; +#endif + + bool success = false; + + if (ConditionPassed(opcode)) + { + uint32_t t; + uint32_t n; + uint32_t m; + bool index; + bool add; + bool wback; + ARM_ShifterType shift_t; + uint32_t shift_n; + + // EncodingSpecificOperations(); NullCheckIfThumbEE(n); + switch (encoding) + { + case eEncodingT1: + // t = UInt(Rt); n = UInt(Rn); m = UInt(Rm); + t = Bits32 (opcode, 2, 0); + n = Bits32 (opcode, 5, 3); + m = Bits32 (opcode, 8, 6); + + // index = TRUE; add = TRUE; wback = FALSE; + index = true; + add = true; + wback = false; + + // (shift_t, shift_n) = (SRType_LSL, 0); + shift_t = SRType_LSL; + shift_n = 0; + break; + + case eEncodingT2: + // if Rt == '1111' then SEE PLD; + // if Rn == '1111' then SEE LDRB (literal); + // t = UInt(Rt); n = UInt(Rn); m = UInt(Rm); + t = Bits32 (opcode, 15, 12); + n = Bits32 (opcode, 19, 16); + m = Bits32 (opcode, 3, 0); + + // index = TRUE; add = TRUE; wback = FALSE; + index = true; + add = true; + wback = false; + + // (shift_t, shift_n) = (SRType_LSL, UInt(imm2)); + shift_t = SRType_LSL; + shift_n = Bits32 (opcode, 5, 4); + + // if t == 13 || BadReg(m) then UNPREDICTABLE; + if ((t == 13) || BadReg (m)) + return false; + break; + + case eEncodingA1: + { + // if P == '0' && W == '1' then SEE LDRBT; + // t = UInt(Rt); n = UInt(Rn); m = UInt(Rm); + t = Bits32 (opcode, 15, 12); + n = Bits32 (opcode, 19, 16); + m = Bits32 (opcode, 3, 0); + + // index = (P == '1'); add = (U == '1'); wback = (P == '0') || (W == '1'); + index = BitIsSet (opcode, 24); + add = BitIsSet (opcode, 23); + wback = (BitIsClear (opcode, 24) || BitIsSet (opcode, 21)); + + // (shift_t, shift_n) = DecodeImmShift(type, imm5); + uint32_t type = Bits32 (opcode, 6, 5); + uint32_t imm5 = Bits32 (opcode, 11, 7); + shift_n = DecodeImmShift (type, imm5, shift_t); + + // if t == 15 || m == 15 then UNPREDICTABLE; + if ((t == 15) || (m == 15)) + return false; + + // if wback && (n == 15 || n == t) then UNPREDICTABLE; + if (wback && ((n == 15) || (n == t))) + return false; + } + break; + + default: + return false; + } + + addr_t offset_addr; + addr_t address; + + // offset = Shift(R[m], shift_t, shift_n, APSR.C); + uint32_t Rm = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_r0 + m, 0, &success); + if (!success) + return false; + + addr_t offset = Shift (Rm, shift_t, shift_n, APSR_C, &success); + if (!success) + return false; + + // offset_addr = if add then (R[n] + offset) else (R[n] - offset); + uint32_t Rn = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_r0 + n, 0, &success); + if (!success) + return false; + + if (add) + offset_addr = Rn + offset; + else + offset_addr = Rn - offset; + + // address = if index then offset_addr else R[n]; + if (index) + address = offset_addr; + else + address = Rn; + + // R[t] = ZeroExtend(MemU[address,1],32); + RegisterInfo base_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, base_reg); + + EmulateInstruction::Context context; + context.type = eContextRegisterLoad; + context.SetRegisterPlusOffset (base_reg, address - Rn); + + uint64_t data = MemURead (context, address, 1, 0, &success); + if (!success) + return false; + + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + t, data)) + return false; + + // if wback then R[n] = offset_addr; + if (wback) + { + context.type = eContextAdjustBaseRegister; + context.SetAddress (offset_addr); + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + n, offset_addr)) + return false; + } + } + return true; +} + +// LDRH (immediate, Thumb) calculates an address from a base register value and an immediate offset, loads a +// halfword from memory, zero-extends it to form a 32-bit word, and writes it to a register. It can use offset, +// post-indexed, or pre-indexed addressing. +bool +EmulateInstructionARM::EmulateLDRHImmediate (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + if ConditionPassed() then + EncodingSpecificOperations(); NullCheckIfThumbEE(n); + offset_addr = if add then (R[n] + imm32) else (R[n] - imm32); + address = if index then offset_addr else R[n]; + data = MemU[address,2]; + if wback then R[n] = offset_addr; + if UnalignedSupport() || address<0> = '0' then + R[t] = ZeroExtend(data, 32); + else // Can only apply before ARMv7 + R[t] = bits(32) UNKNOWN; +#endif + + + bool success = false; + + if (ConditionPassed(opcode)) + { + uint32_t t; + uint32_t n; + uint32_t imm32; + bool index; + bool add; + bool wback; + + // EncodingSpecificOperations(); NullCheckIfThumbEE(n); + switch (encoding) + { + case eEncodingT1: + // t = UInt(Rt); n = UInt(Rn); imm32 = ZeroExtend(imm5:'0', 32); + t = Bits32 (opcode, 2, 0); + n = Bits32 (opcode, 5, 3); + imm32 = Bits32 (opcode, 10, 6) << 1; + + // index = TRUE; add = TRUE; wback = FALSE; + index = true; + add = true; + wback = false; + + break; + + case eEncodingT2: + // if Rt == '1111' then SEE "Unallocated memory hints"; + // if Rn == '1111' then SEE LDRH (literal); + // t = UInt(Rt); n = UInt(Rn); imm32 = ZeroExtend(imm12, 32); + t = Bits32 (opcode, 15, 12); + n = Bits32 (opcode, 19, 16); + imm32 = Bits32 (opcode, 11, 0); + + // index = TRUE; add = TRUE; wback = FALSE; + index = true; + add = true; + wback = false; + + // if t == 13 then UNPREDICTABLE; + if (t == 13) + return false; + break; + + case eEncodingT3: + // if Rn == '1111' then SEE LDRH (literal); + // if Rt == '1111' && P == '1' && U == '0' && W == '0' then SEE "Unallocated memory hints"; + // if P == '1' && U == '1' && W == '0' then SEE LDRHT; + // if P == '0' && W == '0' then UNDEFINED; + if (BitIsClear (opcode, 10) && BitIsClear (opcode, 8)) + return false; + + // t = UInt(Rt); n = UInt(Rn); imm32 = ZeroExtend(imm8, 32); + t = Bits32 (opcode, 15, 12); + n = Bits32 (opcode, 19, 16); + imm32 = Bits32 (opcode, 7, 0); + + // index = (P == '1'); add = (U == '1'); wback = (W == '1'); + index = BitIsSet (opcode, 10); + add = BitIsSet (opcode, 9); + wback = BitIsSet (opcode, 8); + + // if BadReg(t) || (wback && n == t) then UNPREDICTABLE; + if (BadReg (t) || (wback && (n == t))) + return false; + break; + + default: + return false; + } + + // offset_addr = if add then (R[n] + imm32) else (R[n] - imm32); + uint32_t Rn = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_r0 + n, 0, &success); + if (!success) + return false; + + addr_t offset_addr; + addr_t address; + + if (add) + offset_addr = Rn + imm32; + else + offset_addr = Rn - imm32; + + // address = if index then offset_addr else R[n]; + if (index) + address = offset_addr; + else + address = Rn; + + // data = MemU[address,2]; + RegisterInfo base_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, base_reg); + + EmulateInstruction::Context context; + context.type = eContextRegisterLoad; + context.SetRegisterPlusOffset (base_reg, address - Rn); + + uint64_t data = MemURead (context, address, 2, 0, &success); + if (!success) + return false; + + // if wback then R[n] = offset_addr; + if (wback) + { + context.type = eContextAdjustBaseRegister; + context.SetAddress (offset_addr); + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + n, offset_addr)) + return false; + } + + // if UnalignedSupport() || address<0> = '0' then + if (UnalignedSupport () || BitIsClear (address, 0)) + { + // R[t] = ZeroExtend(data, 32); + context.type = eContextRegisterLoad; + context.SetRegisterPlusOffset (base_reg, address - Rn); + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + t, data)) + return false; + } + else // Can only apply before ARMv7 + { + // R[t] = bits(32) UNKNOWN; + WriteBits32Unknown (t); + } + } + return true; +} + +// LDRH (literal) caculates an address from the PC value and an immediate offset, loads a halfword from memory, +// zero-extends it to form a 32-bit word, and writes it to a register. +bool +EmulateInstructionARM::EmulateLDRHLiteral (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + if ConditionPassed() then + EncodingSpecificOperations(); NullCheckIfThumbEE(15); + base = Align(PC,4); + address = if add then (base + imm32) else (base - imm32); + data = MemU[address,2]; + if UnalignedSupport() || address<0> = '0' then + R[t] = ZeroExtend(data, 32); + else // Can only apply before ARMv7 + R[t] = bits(32) UNKNOWN; +#endif + + bool success = false; + + if (ConditionPassed(opcode)) + { + uint32_t t; + uint32_t imm32; + bool add; + + // EncodingSpecificOperations(); NullCheckIfThumbEE(15); + switch (encoding) + { + case eEncodingT1: + // if Rt == '1111' then SEE "Unallocated memory hints"; + // t = UInt(Rt); imm32 = ZeroExtend(imm12, 32); add = (U == '1'); + t = Bits32 (opcode, 15, 12); + imm32 = Bits32 (opcode, 11, 0); + add = BitIsSet (opcode, 23); + + // if t == 13 then UNPREDICTABLE; + if (t == 13) + return false; + + break; + + case eEncodingA1: + { + uint32_t imm4H = Bits32 (opcode, 11, 8); + uint32_t imm4L = Bits32 (opcode, 3, 0); + + // t == UInt(Rt); imm32 = ZeroExtend(imm4H:imm4L, 32); add = (U == '1'); + t = Bits32 (opcode, 15, 12); + imm32 = (imm4H << 4) | imm4L; + add = BitIsSet (opcode, 23); + + // if t == 15 then UNPREDICTABLE; + if (t == 15) + return false; + break; + } + + default: + return false; + } + + // base = Align(PC,4); + uint64_t pc_value = ReadCoreReg (PC_REG, &success); + if (!success) + return false; + + addr_t base = AlignPC (pc_value); + addr_t address; + + // address = if add then (base + imm32) else (base - imm32); + if (add) + address = base + imm32; + else + address = base - imm32; + + // data = MemU[address,2]; + RegisterInfo base_reg; + GetRegisterInfo (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC, base_reg); + + EmulateInstruction::Context context; + context.type = eContextRegisterLoad; + context.SetRegisterPlusOffset (base_reg, address - base); + + uint64_t data = MemURead (context, address, 2, 0, &success); + if (!success) + return false; + + + // if UnalignedSupport() || address<0> = '0' then + if (UnalignedSupport () || BitIsClear (address, 0)) + { + // R[t] = ZeroExtend(data, 32); + context.type = eContextRegisterLoad; + context.SetRegisterPlusOffset (base_reg, address - base); + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + t, data)) + return false; + + } + else // Can only apply before ARMv7 + { + // R[t] = bits(32) UNKNOWN; + WriteBits32Unknown (t); + } + } + return true; +} + +// LDRH (literal) calculates an address from a base register value and an offset register value, loads a halfword +// from memory, zero-extends it to form a 32-bit word, and writes it to a register. The offset register value can +// be shifted left by 0, 1, 2, or 3 bits. +bool +EmulateInstructionARM::EmulateLDRHRegister (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + if ConditionPassed() then + EncodingSpecificOperations(); NullCheckIfThumbEE(n); + offset = Shift(R[m], shift_t, shift_n, APSR.C); + offset_addr = if add then (R[n] + offset) else (R[n] - offset); + address = if index then offset_addr else R[n]; + data = MemU[address,2]; + if wback then R[n] = offset_addr; + if UnalignedSupport() || address<0> = '0' then + R[t] = ZeroExtend(data, 32); + else // Can only apply before ARMv7 + R[t] = bits(32) UNKNOWN; +#endif + + bool success = false; + + if (ConditionPassed(opcode)) + { + uint32_t t; + uint32_t n; + uint32_t m; + bool index; + bool add; + bool wback; + ARM_ShifterType shift_t; + uint32_t shift_n; + + // EncodingSpecificOperations(); NullCheckIfThumbEE(n); + switch (encoding) + { + case eEncodingT1: + // if CurrentInstrSet() == InstrSet_ThumbEE then SEE "Modified operation in ThumbEE"; + // t = UInt(Rt); n = UInt(Rn); m = UInt(Rm); + t = Bits32 (opcode, 2, 0); + n = Bits32 (opcode, 5, 3); + m = Bits32 (opcode, 8, 6); + + // index = TRUE; add = TRUE; wback = FALSE; + index = true; + add = true; + wback = false; + + // (shift_t, shift_n) = (SRType_LSL, 0); + shift_t = SRType_LSL; + shift_n = 0; + + break; + + case eEncodingT2: + // if Rn == '1111' then SEE LDRH (literal); + // if Rt == '1111' then SEE "Unallocated memory hints"; + // t = UInt(Rt); n = UInt(Rn); m = UInt(Rm); + t = Bits32 (opcode, 15, 12); + n = Bits32 (opcode, 19, 16); + m = Bits32 (opcode, 3, 0); + + // index = TRUE; add = TRUE; wback = FALSE; + index = true; + add = true; + wback = false; + + // (shift_t, shift_n) = (SRType_LSL, UInt(imm2)); + shift_t = SRType_LSL; + shift_n = Bits32 (opcode, 5, 4); + + // if t == 13 || BadReg(m) then UNPREDICTABLE; + if ((t == 13) || BadReg (m)) + return false; + break; + + case eEncodingA1: + // if P == '0' && W == '1' then SEE LDRHT; + // t = UInt(Rt); n = UInt(Rn); m = UInt(Rm); + t = Bits32 (opcode, 15, 12); + n = Bits32 (opcode, 19, 16); + m = Bits32 (opcode, 3, 0); + + // index = (P == '1'); add = (U == '1'); wback = (P == '0') || (W == '1'); + index = BitIsSet (opcode, 24); + add = BitIsSet (opcode, 23); + wback = (BitIsClear (opcode, 24) || BitIsSet (opcode, 21)); + + // (shift_t, shift_n) = (SRType_LSL, 0); + shift_t = SRType_LSL; + shift_n = 0; + + // if t == 15 || m == 15 then UNPREDICTABLE; + if ((t == 15) || (m == 15)) + return false; + + // if wback && (n == 15 || n == t) then UNPREDICTABLE; + if (wback && ((n == 15) || (n == t))) + return false; + + break; + + default: + return false; + } + + // offset = Shift(R[m], shift_t, shift_n, APSR.C); + + uint64_t Rm = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_r0 + m, 0, &success); + if (!success) + return false; + + addr_t offset = Shift (Rm, shift_t, shift_n, APSR_C, &success); + if (!success) + return false; + + addr_t offset_addr; + addr_t address; + + // offset_addr = if add then (R[n] + offset) else (R[n] - offset); + uint64_t Rn = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_r0 + n, 0, &success); + if (!success) + return false; + + if (add) + offset_addr = Rn + offset; + else + offset_addr = Rn - offset; + + // address = if index then offset_addr else R[n]; + if (index) + address = offset_addr; + else + address = Rn; + + // data = MemU[address,2]; + RegisterInfo base_reg; + RegisterInfo offset_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, base_reg); + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + m, offset_reg); + + EmulateInstruction::Context context; + context.type = eContextRegisterLoad; + context.SetRegisterPlusIndirectOffset (base_reg, offset_reg); + uint64_t data = MemURead (context, address, 2, 0, &success); + if (!success) + return false; + + // if wback then R[n] = offset_addr; + if (wback) + { + context.type = eContextAdjustBaseRegister; + context.SetAddress (offset_addr); + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + n, offset_addr)) + return false; + } + + // if UnalignedSupport() || address<0> = '0' then + if (UnalignedSupport() || BitIsClear (address, 0)) + { + // R[t] = ZeroExtend(data, 32); + context.type = eContextRegisterLoad; + context.SetRegisterPlusIndirectOffset (base_reg, offset_reg); + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + t, data)) + return false; + } + else // Can only apply before ARMv7 + { + // R[t] = bits(32) UNKNOWN; + WriteBits32Unknown (t); + } + } + return true; +} + +// LDRSB (immediate) calculates an address from a base register value and an immediate offset, loads a byte from +// memory, sign-extends it to form a 32-bit word, and writes it to a register. It can use offset, post-indexed, +// or pre-indexed addressing. +bool +EmulateInstructionARM::EmulateLDRSBImmediate (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + if ConditionPassed() then + EncodingSpecificOperations(); NullCheckIfThumbEE(n); + offset_addr = if add then (R[n] + imm32) else (R[n] - imm32); + address = if index then offset_addr else R[n]; + R[t] = SignExtend(MemU[address,1], 32); + if wback then R[n] = offset_addr; +#endif + + bool success = false; + + if (ConditionPassed(opcode)) + { + uint32_t t; + uint32_t n; + uint32_t imm32; + bool index; + bool add; + bool wback; + + // EncodingSpecificOperations(); NullCheckIfThumbEE(n); + switch (encoding) + { + case eEncodingT1: + // if Rt == '1111' then SEE PLI; + // if Rn == '1111' then SEE LDRSB (literal); + // t = UInt(Rt); n = UInt(Rn); imm32 = ZeroExtend(imm12, 32); + t = Bits32 (opcode, 15, 12); + n = Bits32 (opcode, 19, 16); + imm32 = Bits32 (opcode, 11, 0); + + // index = TRUE; add = TRUE; wback = FALSE; + index = true; + add = true; + wback = false; + + // if t == 13 then UNPREDICTABLE; + if (t == 13) + return false; + + break; + + case eEncodingT2: + // if Rt == '1111' && P == '1' && U == '0' && W == '0' then SEE PLI; + // if Rn == '1111' then SEE LDRSB (literal); + // if P == '1' && U == '1' && W == '0' then SEE LDRSBT; + // if P == '0' && W == '0' then UNDEFINED; + if (BitIsClear (opcode, 10) && BitIsClear (opcode, 8)) + return false; + + // t = UInt(Rt); n = UInt(Rn); imm32 = ZeroExtend(imm8, 32); + t = Bits32 (opcode, 15, 12); + n = Bits32 (opcode, 19, 16); + imm32 = Bits32 (opcode, 7, 0); + + // index = (P == '1'); add = (U == '1'); wback = (W == '1'); + index = BitIsSet (opcode, 10); + add = BitIsSet (opcode, 9); + wback = BitIsSet (opcode, 8); + + // if BadReg(t) || (wback && n == t) then UNPREDICTABLE; + if (((t == 13) || ((t == 15) + && (BitIsClear (opcode, 10) || BitIsSet (opcode, 9) || BitIsSet (opcode, 8)))) + || (wback && (n == t))) + return false; + + break; + + case eEncodingA1: + { + // if Rn == '1111' then SEE LDRSB (literal); + // if P == '0' && W == '1' then SEE LDRSBT; + // t == UInt(Rt); n = UInt(Rn); imm32 = ZeroExtend(imm4H:imm4L, 32); + t = Bits32 (opcode, 15, 12); + n = Bits32 (opcode, 19, 16); + + uint32_t imm4H = Bits32 (opcode, 11, 8); + uint32_t imm4L = Bits32 (opcode, 3, 0); + imm32 = (imm4H << 4) | imm4L; + + // index = (P == '1'); add = (U == '1'); wback = (P == '0') || (W == '1'); + index = BitIsSet (opcode, 24); + add = BitIsSet (opcode, 23); + wback = (BitIsClear (opcode, 24) || BitIsSet (opcode, 21)); + + // if t == 15 || (wback && n == t) then UNPREDICTABLE; + if ((t == 15) || (wback && (n == t))) + return false; + + break; + } + + default: + return false; + } + + uint64_t Rn = ReadCoreReg (n, &success); + if (!success) + return false; + + addr_t offset_addr; + addr_t address; + + // offset_addr = if add then (R[n] + imm32) else (R[n] - imm32); + if (add) + offset_addr = Rn + imm32; + else + offset_addr = Rn - imm32; + + // address = if index then offset_addr else R[n]; + if (index) + address = offset_addr; + else + address = Rn; + + // R[t] = SignExtend(MemU[address,1], 32); + RegisterInfo base_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, base_reg); + + EmulateInstruction::Context context; + context.type = eContextRegisterLoad; + context.SetRegisterPlusOffset (base_reg, address - Rn); + + uint64_t unsigned_data = MemURead (context, address, 1, 0, &success); + if (!success) + return false; + + int64_t signed_data = llvm::SignExtend64<8>(unsigned_data); + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + t, (uint64_t) signed_data)) + return false; + + // if wback then R[n] = offset_addr; + if (wback) + { + context.type = eContextAdjustBaseRegister; + context.SetAddress (offset_addr); + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + n, offset_addr)) + return false; + } + } + + return true; +} + +// LDRSB (literal) calculates an address from the PC value and an immediate offset, loads a byte from memory, +// sign-extends it to form a 32-bit word, and writes tit to a register. +bool +EmulateInstructionARM::EmulateLDRSBLiteral (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + if ConditionPassed() then + EncodingSpecificOperations(); NullCheckIfThumbEE(15); + base = Align(PC,4); + address = if add then (base + imm32) else (base - imm32); + R[t] = SignExtend(MemU[address,1], 32); +#endif + + bool success = false; + + if (ConditionPassed(opcode)) + { + uint32_t t; + uint32_t imm32; + bool add; + + // EncodingSpecificOperations(); NullCheckIfThumbEE(15); + switch (encoding) + { + case eEncodingT1: + // if Rt == '1111' then SEE PLI; + // t = UInt(Rt); imm32 = ZeroExtend(imm12, 32); add = (U == '1'); + t = Bits32 (opcode, 15, 12); + imm32 = Bits32 (opcode, 11, 0); + add = BitIsSet (opcode, 23); + + // if t == 13 then UNPREDICTABLE; + if (t == 13) + return false; + + break; + + case eEncodingA1: + { + // t == UInt(Rt); imm32 = ZeroExtend(imm4H:imm4L, 32); add = (U == '1'); + t = Bits32 (opcode, 15, 12); + uint32_t imm4H = Bits32 (opcode, 11, 8); + uint32_t imm4L = Bits32 (opcode, 3, 0); + imm32 = (imm4H << 4) | imm4L; + add = BitIsSet (opcode, 23); + + // if t == 15 then UNPREDICTABLE; + if (t == 15) + return false; + + break; + } + + default: + return false; + } + + // base = Align(PC,4); + uint64_t pc_value = ReadCoreReg (PC_REG, &success); + if (!success) + return false; + uint64_t base = AlignPC (pc_value); + + // address = if add then (base + imm32) else (base - imm32); + addr_t address; + if (add) + address = base + imm32; + else + address = base - imm32; + + // R[t] = SignExtend(MemU[address,1], 32); + RegisterInfo base_reg; + GetRegisterInfo (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC, base_reg); + + EmulateInstruction::Context context; + context.type = eContextRegisterLoad; + context.SetRegisterPlusOffset (base_reg, address - base); + + uint64_t unsigned_data = MemURead (context, address, 1, 0, &success); + if (!success) + return false; + + int64_t signed_data = llvm::SignExtend64<8>(unsigned_data); + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + t, (uint64_t) signed_data)) + return false; + } + return true; +} + +// LDRSB (register) calculates an address from a base register value and an offset register value, loadsa byte from +// memory, sign-extends it to form a 32-bit word, and writes it to a register. The offset register value can be +// shifted left by 0, 1, 2, or 3 bits. +bool +EmulateInstructionARM::EmulateLDRSBRegister (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + if ConditionPassed() then + EncodingSpecificOperations(); NullCheckIfThumbEE(n); + offset = Shift(R[m], shift_t, shift_n, APSR.C); + offset_addr = if add then (R[n] + offset) else (R[n] - offset); + address = if index then offset_addr else R[n]; + R[t] = SignExtend(MemU[address,1], 32); + if wback then R[n] = offset_addr; +#endif + + bool success = false; + + if (ConditionPassed(opcode)) + { + uint32_t t; + uint32_t n; + uint32_t m; + bool index; + bool add; + bool wback; + ARM_ShifterType shift_t; + uint32_t shift_n; + + // EncodingSpecificOperations(); NullCheckIfThumbEE(n); + switch (encoding) + { + case eEncodingT1: + // t = UInt(Rt); n = UInt(Rn); m = UInt(Rm); + t = Bits32 (opcode, 2, 0); + n = Bits32 (opcode, 5, 3); + m = Bits32 (opcode, 8, 6); + + // index = TRUE; add = TRUE; wback = FALSE; + index = true; + add = true; + wback = false; + + // (shift_t, shift_n) = (SRType_LSL, 0); + shift_t = SRType_LSL; + shift_n = 0; + + break; + + case eEncodingT2: + // if Rt == '1111' then SEE PLI; + // if Rn == '1111' then SEE LDRSB (literal); + // t = UInt(Rt); n = UInt(Rn); m = UInt(Rm); + t = Bits32 (opcode, 15, 12); + n = Bits32 (opcode, 19, 16); + m = Bits32 (opcode, 3, 0); + + // index = TRUE; add = TRUE; wback = FALSE; + index = true; + add = true; + wback = false; + + // (shift_t, shift_n) = (SRType_LSL, UInt(imm2)); + shift_t = SRType_LSL; + shift_n = Bits32 (opcode, 5, 4); + + // if t == 13 || BadReg(m) then UNPREDICTABLE; + if ((t == 13) || BadReg (m)) + return false; + break; + + case eEncodingA1: + // if P == '0' && W == '1' then SEE LDRSBT; + // t = UInt(Rt); n = UInt(Rn); m = UInt(Rm); + t = Bits32 (opcode, 15, 12); + n = Bits32 (opcode, 19, 16); + m = Bits32 (opcode, 3, 0); + + // index = (P == '1'); add = (U == '1'); wback = (P == '0') || (W == '1'); + index = BitIsSet (opcode, 24); + add = BitIsSet (opcode, 23); + wback = BitIsClear (opcode, 24) || BitIsSet (opcode, 21); + + // (shift_t, shift_n) = (SRType_LSL, 0); + shift_t = SRType_LSL; + shift_n = 0; + + // if t == 15 || m == 15 then UNPREDICTABLE; + if ((t == 15) || (m == 15)) + return false; + + // if wback && (n == 15 || n == t) then UNPREDICTABLE; + if (wback && ((n == 15) || (n == t))) + return false; + break; + + default: + return false; + } + + uint64_t Rm = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_r0 + m, 0, &success); + if (!success) + return false; + + // offset = Shift(R[m], shift_t, shift_n, APSR.C); + addr_t offset = Shift (Rm, shift_t, shift_n, APSR_C, &success); + if (!success) + return false; + + addr_t offset_addr; + addr_t address; + + // offset_addr = if add then (R[n] + offset) else (R[n] - offset); + uint64_t Rn = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_r0 + n, 0, &success); + if (!success) + return false; + + if (add) + offset_addr = Rn + offset; + else + offset_addr = Rn - offset; + + // address = if index then offset_addr else R[n]; + if (index) + address = offset_addr; + else + address = Rn; + + // R[t] = SignExtend(MemU[address,1], 32); + RegisterInfo base_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, base_reg); + RegisterInfo offset_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + m, offset_reg); + + EmulateInstruction::Context context; + context.type = eContextRegisterLoad; + context.SetRegisterPlusIndirectOffset (base_reg, offset_reg); + + uint64_t unsigned_data = MemURead (context, address, 1, 0, &success); + if (!success) + return false; + + int64_t signed_data = llvm::SignExtend64<8>(unsigned_data); + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + t, (uint64_t) signed_data)) + return false; + + // if wback then R[n] = offset_addr; + if (wback) + { + context.type = eContextAdjustBaseRegister; + context.SetAddress (offset_addr); + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + n, offset_addr)) + return false; + } + } + return true; +} + +// LDRSH (immediate) calculates an address from a base register value and an immediate offset, loads a halfword from +// memory, sign-extends it to form a 32-bit word, and writes it to a register. It can use offset, post-indexed, or +// pre-indexed addressing. +bool +EmulateInstructionARM::EmulateLDRSHImmediate (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + if ConditionPassed() then + EncodingSpecificOperations(); NullCheckIfThumbEE(n); + offset_addr = if add then (R[n] + imm32) else (R[n] - imm32); + address = if index then offset_addr else R[n]; + data = MemU[address,2]; + if wback then R[n] = offset_addr; + if UnalignedSupport() || address<0> = '0' then + R[t] = SignExtend(data, 32); + else // Can only apply before ARMv7 + R[t] = bits(32) UNKNOWN; +#endif + + bool success = false; + + if (ConditionPassed(opcode)) + { + uint32_t t; + uint32_t n; + uint32_t imm32; + bool index; + bool add; + bool wback; + + // EncodingSpecificOperations(); NullCheckIfThumbEE(n); + switch (encoding) + { + case eEncodingT1: + // if Rn == '1111' then SEE LDRSH (literal); + // if Rt == '1111' then SEE "Unallocated memory hints"; + // t = UInt(Rt); n = UInt(Rn); imm32 = ZeroExtend(imm12, 32); + t = Bits32 (opcode, 15, 12); + n = Bits32 (opcode, 19, 16); + imm32 = Bits32 (opcode, 11, 0); + + // index = TRUE; add = TRUE; wback = FALSE; + index = true; + add = true; + wback = false; + + // if t == 13 then UNPREDICTABLE; + if (t == 13) + return false; + + break; + + case eEncodingT2: + // if Rn == '1111' then SEE LDRSH (literal); + // if Rt == '1111' && P == '1' && U == '0' && W == '0' then SEE "Unallocated memory hints"; + // if P == '1' && U == '1' && W == '0' then SEE LDRSHT; + // if P == '0' && W == '0' then UNDEFINED; + if (BitIsClear (opcode, 10) && BitIsClear (opcode, 8)) + return false; + + // t = UInt(Rt); n = UInt(Rn); imm32 = ZeroExtend(imm8, 32); + t = Bits32 (opcode, 15, 12); + n = Bits32 (opcode, 19, 16); + imm32 = Bits32 (opcode, 7, 0); + + // index = (P == '1'); add = (U == '1'); wback = (W == '1'); + index = BitIsSet (opcode, 10); + add = BitIsSet (opcode, 9); + wback = BitIsSet (opcode, 8); + + // if BadReg(t) || (wback && n == t) then UNPREDICTABLE; + if (BadReg (t) || (wback && (n == t))) + return false; + + break; + + case eEncodingA1: + { + // if Rn == '1111' then SEE LDRSH (literal); + // if P == '0' && W == '1' then SEE LDRSHT; + // t == UInt(Rt); n = UInt(Rn); imm32 = ZeroExtend(imm4H:imm4L, 32); + t = Bits32 (opcode, 15, 12); + n = Bits32 (opcode, 19, 16); + uint32_t imm4H = Bits32 (opcode, 11,8); + uint32_t imm4L = Bits32 (opcode, 3, 0); + imm32 = (imm4H << 4) | imm4L; + + // index = (P == '1'); add = (U == '1'); wback = (P == '0') || (W == '1'); + index = BitIsSet (opcode, 24); + add = BitIsSet (opcode, 23); + wback = BitIsClear (opcode, 24) || BitIsSet (opcode, 21); + + // if t == 15 || (wback && n == t) then UNPREDICTABLE; + if ((t == 15) || (wback && (n == t))) + return false; + + break; + } + + default: + return false; + } + + // offset_addr = if add then (R[n] + imm32) else (R[n] - imm32); + uint64_t Rn = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_r0 + n, 0, &success); + if (!success) + return false; + + addr_t offset_addr; + if (add) + offset_addr = Rn + imm32; + else + offset_addr = Rn - imm32; + + // address = if index then offset_addr else R[n]; + addr_t address; + if (index) + address = offset_addr; + else + address = Rn; + + // data = MemU[address,2]; + RegisterInfo base_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, base_reg); + + EmulateInstruction::Context context; + context.type = eContextRegisterLoad; + context.SetRegisterPlusOffset (base_reg, address - Rn); + + uint64_t data = MemURead (context, address, 2, 0, &success); + if (!success) + return false; + + // if wback then R[n] = offset_addr; + if (wback) + { + context.type = eContextAdjustBaseRegister; + context.SetAddress (offset_addr); + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + n, offset_addr)) + return false; + } + + // if UnalignedSupport() || address<0> = '0' then + if (UnalignedSupport() || BitIsClear (address, 0)) + { + // R[t] = SignExtend(data, 32); + int64_t signed_data = llvm::SignExtend64<16>(data); + context.type = eContextRegisterLoad; + context.SetRegisterPlusOffset (base_reg, address - Rn); + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + t, (uint64_t) signed_data)) + return false; + } + else // Can only apply before ARMv7 + { + // R[t] = bits(32) UNKNOWN; + WriteBits32Unknown (t); + } + } + return true; +} + +// LDRSH (literal) calculates an address from the PC value and an immediate offset, loads a halfword from memory, +// sign-extends it to from a 32-bit word, and writes it to a register. +bool +EmulateInstructionARM::EmulateLDRSHLiteral (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + if ConditionPassed() then + EncodingSpecificOperations(); NullCheckIfThumbEE(15); + base = Align(PC,4); + address = if add then (base + imm32) else (base - imm32); + data = MemU[address,2]; + if UnalignedSupport() || address<0> = '0' then + R[t] = SignExtend(data, 32); + else // Can only apply before ARMv7 + R[t] = bits(32) UNKNOWN; +#endif + + bool success = false; + + if (ConditionPassed(opcode)) + { + uint32_t t; + uint32_t imm32; + bool add; + + // EncodingSpecificOperations(); NullCheckIfThumbEE(15); + switch (encoding) + { + case eEncodingT1: + // if Rt == '1111' then SEE "Unallocated memory hints"; + // t = UInt(Rt); imm32 = ZeroExtend(imm12, 32); add = (U == '1'); + t = Bits32 (opcode, 15, 12); + imm32 = Bits32 (opcode, 11, 0); + add = BitIsSet (opcode, 23); + + // if t == 13 then UNPREDICTABLE; + if (t == 13) + return false; + + break; + + case eEncodingA1: + { + // t == UInt(Rt); imm32 = ZeroExtend(imm4H:imm4L, 32); add = (U == '1'); + t = Bits32 (opcode, 15, 12); + uint32_t imm4H = Bits32 (opcode, 11, 8); + uint32_t imm4L = Bits32 (opcode, 3, 0); + imm32 = (imm4H << 4) | imm4L; + add = BitIsSet (opcode, 23); + + // if t == 15 then UNPREDICTABLE; + if (t == 15) + return false; + + break; + } + default: + return false; + } + + // base = Align(PC,4); + uint64_t pc_value = ReadCoreReg (PC_REG, &success); + if (!success) + return false; + + uint64_t base = AlignPC (pc_value); + + addr_t address; + // address = if add then (base + imm32) else (base - imm32); + if (add) + address = base + imm32; + else + address = base - imm32; + + // data = MemU[address,2]; + RegisterInfo base_reg; + GetRegisterInfo (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC, base_reg); + + EmulateInstruction::Context context; + context.type = eContextRegisterLoad; + context.SetRegisterPlusOffset (base_reg, imm32); + + uint64_t data = MemURead (context, address, 2, 0, &success); + if (!success) + return false; + + // if UnalignedSupport() || address<0> = '0' then + if (UnalignedSupport() || BitIsClear (address, 0)) + { + // R[t] = SignExtend(data, 32); + int64_t signed_data = llvm::SignExtend64<16>(data); + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + t, (uint64_t) signed_data)) + return false; + } + else // Can only apply before ARMv7 + { + // R[t] = bits(32) UNKNOWN; + WriteBits32Unknown (t); + } + } + return true; +} + +// LDRSH (register) calculates an address from a base register value and an offset register value, loads a halfword +// from memory, sign-extends it to form a 32-bit word, and writes it to a register. The offset register value can be +// shifted left by 0, 1, 2, or 3 bits. +bool +EmulateInstructionARM::EmulateLDRSHRegister (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + if ConditionPassed() then + EncodingSpecificOperations(); NullCheckIfThumbEE(n); + offset = Shift(R[m], shift_t, shift_n, APSR.C); + offset_addr = if add then (R[n] + offset) else (R[n] - offset); + address = if index then offset_addr else R[n]; + data = MemU[address,2]; + if wback then R[n] = offset_addr; + if UnalignedSupport() || address<0> = '0' then + R[t] = SignExtend(data, 32); + else // Can only apply before ARMv7 + R[t] = bits(32) UNKNOWN; +#endif + + bool success = false; + + if (ConditionPassed(opcode)) + { + uint32_t t; + uint32_t n; + uint32_t m; + bool index; + bool add; + bool wback; + ARM_ShifterType shift_t; + uint32_t shift_n; + + // EncodingSpecificOperations(); NullCheckIfThumbEE(n); + switch (encoding) + { + case eEncodingT1: + // if CurrentInstrSet() == InstrSet_ThumbEE then SEE "Modified operation in ThumbEE"; + // t = UInt(Rt); n = UInt(Rn); m = UInt(Rm); + t = Bits32 (opcode, 2, 0); + n = Bits32 (opcode, 5, 3); + m = Bits32 (opcode, 8, 6); + + // index = TRUE; add = TRUE; wback = FALSE; + index = true; + add = true; + wback = false; + + // (shift_t, shift_n) = (SRType_LSL, 0); + shift_t = SRType_LSL; + shift_n = 0; + + break; + + case eEncodingT2: + // if Rn == '1111' then SEE LDRSH (literal); + // if Rt == '1111' then SEE "Unallocated memory hints"; + // t = UInt(Rt); n = UInt(Rn); m = UInt(Rm); + t = Bits32 (opcode, 15, 12); + n = Bits32 (opcode, 19, 16); + m = Bits32 (opcode, 3, 0); + + // index = TRUE; add = TRUE; wback = FALSE; + index = true; + add = true; + wback = false; + + // (shift_t, shift_n) = (SRType_LSL, UInt(imm2)); + shift_t = SRType_LSL; + shift_n = Bits32 (opcode, 5, 4); + + // if t == 13 || BadReg(m) then UNPREDICTABLE; + if ((t == 13) || BadReg (m)) + return false; + + break; + + case eEncodingA1: + // if P == '0' && W == '1' then SEE LDRSHT; + // t = UInt(Rt); n = UInt(Rn); m = UInt(Rm); + t = Bits32 (opcode, 15, 12); + n = Bits32 (opcode, 19, 16); + m = Bits32 (opcode, 3, 0); + + // index = (P == '1'); add = (U == '1'); wback = (P == '0') || (W == '1'); + index = BitIsSet (opcode, 24); + add = BitIsSet (opcode, 23); + wback = BitIsClear (opcode, 24) || BitIsSet (opcode, 21); + + // (shift_t, shift_n) = (SRType_LSL, 0); + shift_t = SRType_LSL; + shift_n = 0; + + // if t == 15 || m == 15 then UNPREDICTABLE; + if ((t == 15) || (m == 15)) + return false; + + // if wback && (n == 15 || n == t) then UNPREDICTABLE; + if (wback && ((n == 15) || (n == t))) + return false; + + break; + + default: + return false; + } + + uint64_t Rm = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_r0 + m, 0, &success); + if (!success) + return false; + + uint64_t Rn = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_r0 + n, 0, &success); + if (!success) + return false; + + // offset = Shift(R[m], shift_t, shift_n, APSR.C); + addr_t offset = Shift (Rm, shift_t, shift_n, APSR_C, &success); + if (!success) + return false; + + addr_t offset_addr; + addr_t address; + + // offset_addr = if add then (R[n] + offset) else (R[n] - offset); + if (add) + offset_addr = Rn + offset; + else + offset_addr = Rn - offset; + + // address = if index then offset_addr else R[n]; + if (index) + address = offset_addr; + else + address = Rn; + + // data = MemU[address,2]; + RegisterInfo base_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, base_reg); + + RegisterInfo offset_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + m, offset_reg); + + EmulateInstruction::Context context; + context.type = eContextRegisterLoad; + context.SetRegisterPlusIndirectOffset (base_reg, offset_reg); + + uint64_t data = MemURead (context, address, 2, 0, &success); + if (!success) + return false; + + // if wback then R[n] = offset_addr; + if (wback) + { + context.type = eContextAdjustBaseRegister; + context.SetAddress (offset_addr); + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + n, offset_addr)) + return false; + } + + // if UnalignedSupport() || address<0> = '0' then + if (UnalignedSupport() || BitIsClear (address, 0)) + { + // R[t] = SignExtend(data, 32); + context.type = eContextRegisterLoad; + context.SetRegisterPlusIndirectOffset (base_reg, offset_reg); + + int64_t signed_data = llvm::SignExtend64<16>(data); + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + t, (uint64_t) signed_data)) + return false; + } + else // Can only apply before ARMv7 + { + // R[t] = bits(32) UNKNOWN; + WriteBits32Unknown (t); + } + } + return true; +} + +// SXTB extracts an 8-bit value from a register, sign-extends it to 32 bits, and writes the result to the destination +// register. You can specifiy a rotation by 0, 8, 16, or 24 bits before extracting the 8-bit value. +bool +EmulateInstructionARM::EmulateSXTB (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + if ConditionPassed() then + EncodingSpecificOperations(); + rotated = ROR(R[m], rotation); + R[d] = SignExtend(rotated<7:0>, 32); +#endif + + bool success = false; + + if (ConditionPassed(opcode)) + { + uint32_t d; + uint32_t m; + uint32_t rotation; + + // EncodingSpecificOperations(); + switch (encoding) + { + case eEncodingT1: + // d = UInt(Rd); m = UInt(Rm); rotation = 0; + d = Bits32 (opcode, 2, 0); + m = Bits32 (opcode, 5, 3); + rotation = 0; + + break; + + case eEncodingT2: + // d = UInt(Rd); m = UInt(Rm); rotation = UInt(rotate:'000'); + d = Bits32 (opcode, 11, 8); + m = Bits32 (opcode, 3, 0); + rotation = Bits32 (opcode, 5, 4) << 3; + + // if BadReg(d) || BadReg(m) then UNPREDICTABLE; + if (BadReg (d) || BadReg (m)) + return false; + + break; + + case eEncodingA1: + // d = UInt(Rd); m = UInt(Rm); rotation = UInt(rotate:'000'); + d = Bits32 (opcode, 15, 12); + m = Bits32 (opcode, 3, 0); + rotation = Bits32 (opcode, 11, 10) << 3; + + // if d == 15 || m == 15 then UNPREDICTABLE; + if ((d == 15) || (m == 15)) + return false; + + break; + + default: + return false; + } + + uint64_t Rm = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_r0 + m, 0, &success); + if (!success) + return false; + + // rotated = ROR(R[m], rotation); + uint64_t rotated = ROR (Rm, rotation, &success); + if (!success) + return false; + + // R[d] = SignExtend(rotated<7:0>, 32); + int64_t data = llvm::SignExtend64<8>(rotated); + + RegisterInfo source_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + m, source_reg); + + EmulateInstruction::Context context; + context.type = eContextRegisterLoad; + context.SetRegister (source_reg); + + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + d, (uint64_t) data)) + return false; + } + return true; +} + +// SXTH extracts a 16-bit value from a register, sign-extends it to 32 bits, and writes the result to the destination +// register. You can specify a rotation by 0, 8, 16, or 24 bits before extracting the 16-bit value. +bool +EmulateInstructionARM::EmulateSXTH (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + if ConditionPassed() then + EncodingSpecificOperations(); + rotated = ROR(R[m], rotation); + R[d] = SignExtend(rotated<15:0>, 32); +#endif + + bool success = false; + + if (ConditionPassed(opcode)) + { + uint32_t d; + uint32_t m; + uint32_t rotation; + + // EncodingSpecificOperations(); + switch (encoding) + { + case eEncodingT1: + // d = UInt(Rd); m = UInt(Rm); rotation = 0; + d = Bits32 (opcode, 2, 0); + m = Bits32 (opcode, 5, 3); + rotation = 0; + + break; + + case eEncodingT2: + // d = UInt(Rd); m = UInt(Rm); rotation = UInt(rotate:'000'); + d = Bits32 (opcode, 11, 8); + m = Bits32 (opcode, 3, 0); + rotation = Bits32 (opcode, 5, 4) << 3; + + // if BadReg(d) || BadReg(m) then UNPREDICTABLE; + if (BadReg (d) || BadReg (m)) + return false; + + break; + + case eEncodingA1: + // d = UInt(Rd); m = UInt(Rm); rotation = UInt(rotate:'000'); + d = Bits32 (opcode, 15, 12); + m = Bits32 (opcode, 3, 0); + rotation = Bits32 (opcode, 11, 10) << 3; + + // if d == 15 || m == 15 then UNPREDICTABLE; + if ((d == 15) || (m == 15)) + return false; + + break; + + default: + return false; + } + + uint64_t Rm = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_r0 + m, 0, &success); + if (!success) + return false; + + // rotated = ROR(R[m], rotation); + uint64_t rotated = ROR (Rm, rotation, &success); + if (!success) + return false; + + // R[d] = SignExtend(rotated<15:0>, 32); + RegisterInfo source_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + m, source_reg); + + EmulateInstruction::Context context; + context.type = eContextRegisterLoad; + context.SetRegister (source_reg); + + int64_t data = llvm::SignExtend64<16> (rotated); + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + d, (uint64_t) data)) + return false; + } + + return true; +} + +// UXTB extracts an 8-bit value from a register, zero-extneds it to 32 bits, and writes the result to the destination +// register. You can specify a rotation by 0, 8, 16, or 24 bits before extracting the 8-bit value. +bool +EmulateInstructionARM::EmulateUXTB (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + if ConditionPassed() then + EncodingSpecificOperations(); + rotated = ROR(R[m], rotation); + R[d] = ZeroExtend(rotated<7:0>, 32); +#endif + + bool success = false; + + if (ConditionPassed(opcode)) + { + uint32_t d; + uint32_t m; + uint32_t rotation; + + // EncodingSpecificOperations(); + switch (encoding) + { + case eEncodingT1: + // d = UInt(Rd); m = UInt(Rm); rotation = 0; + d = Bits32 (opcode, 2, 0); + m = Bits32 (opcode, 5, 3); + rotation = 0; + + break; + + case eEncodingT2: + // d = UInt(Rd); m = UInt(Rm); rotation = UInt(rotate:'000'); + d = Bits32 (opcode, 11, 8); + m = Bits32 (opcode, 3, 0); + rotation = Bits32 (opcode, 5, 4) << 3; + + // if BadReg(d) || BadReg(m) then UNPREDICTABLE; + if (BadReg (d) || BadReg (m)) + return false; + + break; + + case eEncodingA1: + // d = UInt(Rd); m = UInt(Rm); rotation = UInt(rotate:'000'); + d = Bits32 (opcode, 15, 12); + m = Bits32 (opcode, 3, 0); + rotation = Bits32 (opcode, 11, 10) << 3; + + // if d == 15 || m == 15 then UNPREDICTABLE; + if ((d == 15) || (m == 15)) + return false; + + break; + + default: + return false; + } + + uint64_t Rm = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_r0 + m, 0, &success); + if (!success) + return false; + + // rotated = ROR(R[m], rotation); + uint64_t rotated = ROR (Rm, rotation, &success); + if (!success) + return false; + + // R[d] = ZeroExtend(rotated<7:0>, 32); + RegisterInfo source_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + m, source_reg); + + EmulateInstruction::Context context; + context.type = eContextRegisterLoad; + context.SetRegister (source_reg); + + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + d, Bits32 (rotated, 7, 0))) + return false; + } + return true; +} + +// UXTH extracts a 16-bit value from a register, zero-extends it to 32 bits, and writes the result to the destination +// register. You can specify a rotation by 0, 8, 16, or 24 bits before extracting the 16-bit value. +bool +EmulateInstructionARM::EmulateUXTH (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + if ConditionPassed() then + EncodingSpecificOperations(); + rotated = ROR(R[m], rotation); + R[d] = ZeroExtend(rotated<15:0>, 32); +#endif + + bool success = false; + + if (ConditionPassed(opcode)) + { + uint32_t d; + uint32_t m; + uint32_t rotation; + + switch (encoding) + { + case eEncodingT1: + // d = UInt(Rd); m = UInt(Rm); rotation = 0; + d = Bits32 (opcode, 2, 0); + m = Bits32 (opcode, 5, 3); + rotation = 0; + + break; + + case eEncodingT2: + // d = UInt(Rd); m = UInt(Rm); rotation = UInt(rotate:'000'); + d = Bits32 (opcode, 11, 8); + m = Bits32 (opcode, 3, 0); + rotation = Bits32 (opcode, 5, 4) << 3; + + // if BadReg(d) || BadReg(m) then UNPREDICTABLE; + if (BadReg (d) || BadReg (m)) + return false; + + break; + + case eEncodingA1: + // d = UInt(Rd); m = UInt(Rm); rotation = UInt(rotate:'000'); + d = Bits32 (opcode, 15, 12); + m = Bits32 (opcode, 3, 0); + rotation = Bits32 (opcode, 11, 10) << 3; + + // if d == 15 || m == 15 then UNPREDICTABLE; + if ((d == 15) || (m == 15)) + return false; + + break; + + default: + return false; + } + + uint64_t Rm = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_r0 + m, 0, &success); + if (!success) + return false; + + // rotated = ROR(R[m], rotation); + uint64_t rotated = ROR (Rm, rotation, &success); + if (!success) + return false; + + // R[d] = ZeroExtend(rotated<15:0>, 32); + RegisterInfo source_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + m, source_reg); + + EmulateInstruction::Context context; + context.type = eContextRegisterLoad; + context.SetRegister (source_reg); + + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + d, Bits32 (rotated, 15, 0))) + return false; + } + return true; +} + +// RFE (Return From Exception) loads the PC and the CPSR from the word at the specified address and the following +// word respectively. +bool +EmulateInstructionARM::EmulateRFE (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + if ConditionPassed() then + EncodingSpecificOperations(); + if !CurrentModeIsPrivileged() || CurrentInstrSet() == InstrSet_ThumbEE then + UNPREDICTABLE; + else + address = if increment then R[n] else R[n]-8; + if wordhigher then address = address+4; + CPSRWriteByInstr(MemA[address+4,4], '1111', TRUE); + BranchWritePC(MemA[address,4]); + if wback then R[n] = if increment then R[n]+8 else R[n]-8; +#endif + + bool success = false; + + if (ConditionPassed(opcode)) + { + uint32_t n; + bool wback; + bool increment; + bool wordhigher; + + // EncodingSpecificOperations(); + switch (encoding) + { + case eEncodingT1: + // n = UInt(Rn); wback = (W == '1'); increment = FALSE; wordhigher = FALSE; + n = Bits32 (opcode, 19, 16); + wback = BitIsSet (opcode, 21); + increment = false; + wordhigher = false; + + // if n == 15 then UNPREDICTABLE; + if (n == 15) + return false; + + // if InITBlock() && !LastInITBlock() then UNPREDICTABLE; + if (InITBlock() && !LastInITBlock()) + return false; + + break; + + case eEncodingT2: + // n = UInt(Rn); wback = (W == '1'); increment = TRUE; wordhigher = FALSE; + n = Bits32 (opcode, 19, 16); + wback = BitIsSet (opcode, 21); + increment = true; + wordhigher = false; + + // if n == 15 then UNPREDICTABLE; + if (n == 15) + return false; + + // if InITBlock() && !LastInITBlock() then UNPREDICTABLE; + if (InITBlock() && !LastInITBlock()) + return false; + + break; + + case eEncodingA1: + // n = UInt(Rn); + n = Bits32 (opcode, 19, 16); + + // wback = (W == '1'); inc = (U == '1'); wordhigher = (P == U); + wback = BitIsSet (opcode, 21); + increment = BitIsSet (opcode, 23); + wordhigher = (Bit32 (opcode, 24) == Bit32 (opcode, 23)); + + // if n == 15 then UNPREDICTABLE; + if (n == 15) + return false; + + break; + + default: + return false; + } + + // if !CurrentModeIsPrivileged() || CurrentInstrSet() == InstrSet_ThumbEE then + if (!CurrentModeIsPrivileged ()) + // UNPREDICTABLE; + return false; + else + { + uint64_t Rn = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_r0 + n, 0, &success); + if (!success) + return false; + + addr_t address; + // address = if increment then R[n] else R[n]-8; + if (increment) + address = Rn; + else + address = Rn - 8; + + // if wordhigher then address = address+4; + if (wordhigher) + address = address + 4; + + // CPSRWriteByInstr(MemA[address+4,4], '1111', TRUE); + RegisterInfo base_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, base_reg); + + EmulateInstruction::Context context; + context.type = eContextReturnFromException; + context.SetRegisterPlusOffset (base_reg, address - Rn); + + uint64_t data = MemARead (context, address + 4, 4, 0, &success); + if (!success) + return false; + + CPSRWriteByInstr (data, 15, true); + + // BranchWritePC(MemA[address,4]); + uint64_t data2 = MemARead (context, address, 4, 0, &success); + if (!success) + return false; + + BranchWritePC (context, data2); + + // if wback then R[n] = if increment then R[n]+8 else R[n]-8; + if (wback) + { + context.type = eContextAdjustBaseRegister; + if (increment) + { + context.SetOffset (8); + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + n, Rn + 8)) + return false; + } + else + { + context.SetOffset (-8); + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + n, Rn - 8)) + return false; + } + } // if wback + } + } // if ConditionPassed() + return true; +} + +// Bitwise Exclusive OR (immediate) performs a bitwise exclusive OR of a register value and an immediate value, +// and writes the result to the destination register. It can optionally update the condition flags based on +// the result. +bool +EmulateInstructionARM::EmulateEORImm (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + // ARM pseudo code... + if ConditionPassed() then + EncodingSpecificOperations(); + result = R[n] EOR imm32; + if d == 15 then // Can only occur for ARM encoding + ALUWritePC(result); // setflags is always FALSE here + else + R[d] = result; + if setflags then + APSR.N = result<31>; + APSR.Z = IsZeroBit(result); + APSR.C = carry; + // APSR.V unchanged +#endif + + bool success = false; + + if (ConditionPassed(opcode)) + { + uint32_t Rd, Rn; + uint32_t imm32; // the immediate value to be ORed to the value obtained from Rn + bool setflags; + uint32_t carry; // the carry bit after ARM/Thumb Expand operation + switch (encoding) + { + case eEncodingT1: + Rd = Bits32(opcode, 11, 8); + Rn = Bits32(opcode, 19, 16); + setflags = BitIsSet(opcode, 20); + imm32 = ThumbExpandImm_C(opcode, APSR_C, carry); // (imm32, carry) = ThumbExpandImm(i:imm3:imm8, APSR.C) + // if Rd == '1111' && S == '1' then SEE TEQ (immediate); + if (Rd == 15 && setflags) + return EmulateTEQImm (opcode, eEncodingT1); + if (Rd == 13 || (Rd == 15 && !setflags) || BadReg(Rn)) + return false; + break; + case eEncodingA1: + Rd = Bits32(opcode, 15, 12); + Rn = Bits32(opcode, 19, 16); + setflags = BitIsSet(opcode, 20); + imm32 = ARMExpandImm_C(opcode, APSR_C, carry); // (imm32, carry) = ARMExpandImm(imm12, APSR.C) + + // if Rd == '1111' && S == '1' then SEE SUBS PC, LR and related instructions; + if (Rd == 15 && setflags) + return EmulateSUBSPcLrEtc (opcode, encoding); + break; + default: + return false; + } + + // Read the first operand. + uint32_t val1 = ReadCoreReg(Rn, &success); + if (!success) + return false; + + uint32_t result = val1 ^ imm32; + + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextImmediate; + context.SetNoArgs (); + + if (!WriteCoreRegOptionalFlags(context, result, Rd, setflags, carry)) + return false; + } + return true; +} + +// Bitwise Exclusive OR (register) performs a bitwise exclusive OR of a register value and an +// optionally-shifted register value, and writes the result to the destination register. +// It can optionally update the condition flags based on the result. +bool +EmulateInstructionARM::EmulateEORReg (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + // ARM pseudo code... + if ConditionPassed() then + EncodingSpecificOperations(); + (shifted, carry) = Shift_C(R[m], shift_t, shift_n, APSR.C); + result = R[n] EOR shifted; + if d == 15 then // Can only occur for ARM encoding + ALUWritePC(result); // setflags is always FALSE here + else + R[d] = result; + if setflags then + APSR.N = result<31>; + APSR.Z = IsZeroBit(result); + APSR.C = carry; + // APSR.V unchanged +#endif + + bool success = false; + + if (ConditionPassed(opcode)) + { + uint32_t Rd, Rn, Rm; + ARM_ShifterType shift_t; + uint32_t shift_n; // the shift applied to the value read from Rm + bool setflags; + uint32_t carry; + switch (encoding) + { + case eEncodingT1: + Rd = Rn = Bits32(opcode, 2, 0); + Rm = Bits32(opcode, 5, 3); + setflags = !InITBlock(); + shift_t = SRType_LSL; + shift_n = 0; + break; + case eEncodingT2: + Rd = Bits32(opcode, 11, 8); + Rn = Bits32(opcode, 19, 16); + Rm = Bits32(opcode, 3, 0); + setflags = BitIsSet(opcode, 20); + shift_n = DecodeImmShiftThumb(opcode, shift_t); + // if Rd == '1111' && S == '1' then SEE TEQ (register); + if (Rd == 15 && setflags) + return EmulateTEQReg (opcode, eEncodingT1); + if (Rd == 13 || (Rd == 15 && !setflags) || BadReg(Rn) || BadReg(Rm)) + return false; + break; + case eEncodingA1: + Rd = Bits32(opcode, 15, 12); + Rn = Bits32(opcode, 19, 16); + Rm = Bits32(opcode, 3, 0); + setflags = BitIsSet(opcode, 20); + shift_n = DecodeImmShiftARM(opcode, shift_t); + + // if Rd == '1111' && S == '1' then SEE SUBS PC, LR and related instructions; + if (Rd == 15 && setflags) + return EmulateSUBSPcLrEtc (opcode, encoding); + break; + default: + return false; + } + + // Read the first operand. + uint32_t val1 = ReadCoreReg(Rn, &success); + if (!success) + return false; + + // Read the second operand. + uint32_t val2 = ReadCoreReg(Rm, &success); + if (!success) + return false; + + uint32_t shifted = Shift_C(val2, shift_t, shift_n, APSR_C, carry, &success); + if (!success) + return false; + uint32_t result = val1 ^ shifted; + + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextImmediate; + context.SetNoArgs (); + + if (!WriteCoreRegOptionalFlags(context, result, Rd, setflags, carry)) + return false; + } + return true; +} + +// Bitwise OR (immediate) performs a bitwise (inclusive) OR of a register value and an immediate value, and +// writes the result to the destination register. It can optionally update the condition flags based +// on the result. +bool +EmulateInstructionARM::EmulateORRImm (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + // ARM pseudo code... + if ConditionPassed() then + EncodingSpecificOperations(); + result = R[n] OR imm32; + if d == 15 then // Can only occur for ARM encoding + ALUWritePC(result); // setflags is always FALSE here + else + R[d] = result; + if setflags then + APSR.N = result<31>; + APSR.Z = IsZeroBit(result); + APSR.C = carry; + // APSR.V unchanged +#endif + + bool success = false; + + if (ConditionPassed(opcode)) + { + uint32_t Rd, Rn; + uint32_t imm32; // the immediate value to be ORed to the value obtained from Rn + bool setflags; + uint32_t carry; // the carry bit after ARM/Thumb Expand operation + switch (encoding) + { + case eEncodingT1: + Rd = Bits32(opcode, 11, 8); + Rn = Bits32(opcode, 19, 16); + setflags = BitIsSet(opcode, 20); + imm32 = ThumbExpandImm_C(opcode, APSR_C, carry); // (imm32, carry) = ThumbExpandImm(i:imm3:imm8, APSR.C) + // if Rn == '1111' then SEE MOV (immediate); + if (Rn == 15) + return EmulateMOVRdImm (opcode, eEncodingT2); + if (BadReg(Rd) || Rn == 13) + return false; + break; + case eEncodingA1: + Rd = Bits32(opcode, 15, 12); + Rn = Bits32(opcode, 19, 16); + setflags = BitIsSet(opcode, 20); + imm32 = ARMExpandImm_C(opcode, APSR_C, carry); // (imm32, carry) = ARMExpandImm(imm12, APSR.C) + + if (Rd == 15 && setflags) + return EmulateSUBSPcLrEtc (opcode, encoding); + break; + default: + return false; + } + + // Read the first operand. + uint32_t val1 = ReadCoreReg(Rn, &success); + if (!success) + return false; + + uint32_t result = val1 | imm32; + + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextImmediate; + context.SetNoArgs (); + + if (!WriteCoreRegOptionalFlags(context, result, Rd, setflags, carry)) + return false; + } + return true; +} + +// Bitwise OR (register) performs a bitwise (inclusive) OR of a register value and an optionally-shifted register +// value, and writes the result to the destination register. It can optionally update the condition flags based +// on the result. +bool +EmulateInstructionARM::EmulateORRReg (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + // ARM pseudo code... + if ConditionPassed() then + EncodingSpecificOperations(); + (shifted, carry) = Shift_C(R[m], shift_t, shift_n, APSR.C); + result = R[n] OR shifted; + if d == 15 then // Can only occur for ARM encoding + ALUWritePC(result); // setflags is always FALSE here + else + R[d] = result; + if setflags then + APSR.N = result<31>; + APSR.Z = IsZeroBit(result); + APSR.C = carry; + // APSR.V unchanged +#endif + + bool success = false; + + if (ConditionPassed(opcode)) + { + uint32_t Rd, Rn, Rm; + ARM_ShifterType shift_t; + uint32_t shift_n; // the shift applied to the value read from Rm + bool setflags; + uint32_t carry; + switch (encoding) + { + case eEncodingT1: + Rd = Rn = Bits32(opcode, 2, 0); + Rm = Bits32(opcode, 5, 3); + setflags = !InITBlock(); + shift_t = SRType_LSL; + shift_n = 0; + break; + case eEncodingT2: + Rd = Bits32(opcode, 11, 8); + Rn = Bits32(opcode, 19, 16); + Rm = Bits32(opcode, 3, 0); + setflags = BitIsSet(opcode, 20); + shift_n = DecodeImmShiftThumb(opcode, shift_t); + // if Rn == '1111' then SEE MOV (register); + if (Rn == 15) + return EmulateMOVRdRm (opcode, eEncodingT3); + if (BadReg(Rd) || Rn == 13 || BadReg(Rm)) + return false; + break; + case eEncodingA1: + Rd = Bits32(opcode, 15, 12); + Rn = Bits32(opcode, 19, 16); + Rm = Bits32(opcode, 3, 0); + setflags = BitIsSet(opcode, 20); + shift_n = DecodeImmShiftARM(opcode, shift_t); + + if (Rd == 15 && setflags) + return EmulateSUBSPcLrEtc (opcode, encoding); + break; + default: + return false; + } + + // Read the first operand. + uint32_t val1 = ReadCoreReg(Rn, &success); + if (!success) + return false; + + // Read the second operand. + uint32_t val2 = ReadCoreReg(Rm, &success); + if (!success) + return false; + + uint32_t shifted = Shift_C(val2, shift_t, shift_n, APSR_C, carry, &success); + if (!success) + return false; + uint32_t result = val1 | shifted; + + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextImmediate; + context.SetNoArgs (); + + if (!WriteCoreRegOptionalFlags(context, result, Rd, setflags, carry)) + return false; + } + return true; +} + +// Reverse Subtract (immediate) subtracts a register value from an immediate value, and writes the result to +// the destination register. It can optionally update the condition flags based on the result. +bool +EmulateInstructionARM::EmulateRSBImm (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + // ARM pseudo code... + if ConditionPassed() then + EncodingSpecificOperations(); + (result, carry, overflow) = AddWithCarry(NOT(R[n]), imm32, '1'); + if d == 15 then // Can only occur for ARM encoding + ALUWritePC(result); // setflags is always FALSE here + else + R[d] = result; + if setflags then + APSR.N = result<31>; + APSR.Z = IsZeroBit(result); + APSR.C = carry; + APSR.V = overflow; +#endif + + bool success = false; + + uint32_t Rd; // the destination register + uint32_t Rn; // the first operand + bool setflags; + uint32_t imm32; // the immediate value to be added to the value obtained from Rn + switch (encoding) { + case eEncodingT1: + Rd = Bits32(opcode, 2, 0); + Rn = Bits32(opcode, 5, 3); + setflags = !InITBlock(); + imm32 = 0; + break; + case eEncodingT2: + Rd = Bits32(opcode, 11, 8); + Rn = Bits32(opcode, 19, 16); + setflags = BitIsSet(opcode, 20); + imm32 = ThumbExpandImm(opcode); // imm32 = ThumbExpandImm(i:imm3:imm8) + if (BadReg(Rd) || BadReg(Rn)) + return false; + break; + case eEncodingA1: + Rd = Bits32(opcode, 15, 12); + Rn = Bits32(opcode, 19, 16); + setflags = BitIsSet(opcode, 20); + imm32 = ARMExpandImm(opcode); // imm32 = ARMExpandImm(imm12) + + // if Rd == '1111' && S == '1' then SEE SUBS PC, LR and related instructions; + if (Rd == 15 && setflags) + return EmulateSUBSPcLrEtc (opcode, encoding); + break; + default: + return false; + } + // Read the register value from the operand register Rn. + uint32_t reg_val = ReadCoreReg(Rn, &success); + if (!success) + return false; + + AddWithCarryResult res = AddWithCarry(~reg_val, imm32, 1); + + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextImmediate; + context.SetNoArgs (); + + if (!WriteCoreRegOptionalFlags(context, res.result, Rd, setflags, res.carry_out, res.overflow)) + return false; + + return true; +} + +// Reverse Subtract (register) subtracts a register value from an optionally-shifted register value, and writes the +// result to the destination register. It can optionally update the condition flags based on the result. +bool +EmulateInstructionARM::EmulateRSBReg (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + // ARM pseudo code... + if ConditionPassed() then + EncodingSpecificOperations(); + shifted = Shift(R[m], shift_t, shift_n, APSR.C); + (result, carry, overflow) = AddWithCarry(NOT(R[n]), shifted, '1'); + if d == 15 then // Can only occur for ARM encoding + ALUWritePC(result); // setflags is always FALSE here + else + R[d] = result; + if setflags then + APSR.N = result<31>; + APSR.Z = IsZeroBit(result); + APSR.C = carry; + APSR.V = overflow; +#endif + + bool success = false; + + uint32_t Rd; // the destination register + uint32_t Rn; // the first operand + uint32_t Rm; // the second operand + bool setflags; + ARM_ShifterType shift_t; + uint32_t shift_n; // the shift applied to the value read from Rm + switch (encoding) { + case eEncodingT1: + Rd = Bits32(opcode, 11, 8); + Rn = Bits32(opcode, 19, 16); + Rm = Bits32(opcode, 3, 0); + setflags = BitIsSet(opcode, 20); + shift_n = DecodeImmShiftThumb(opcode, shift_t); + // if (BadReg(d) || BadReg(m)) then UNPREDICTABLE; + if (BadReg(Rd) || BadReg(Rn) || BadReg(Rm)) + return false; + break; + case eEncodingA1: + Rd = Bits32(opcode, 15, 12); + Rn = Bits32(opcode, 19, 16); + Rm = Bits32(opcode, 3, 0); + setflags = BitIsSet(opcode, 20); + shift_n = DecodeImmShiftARM(opcode, shift_t); + + // if Rd == '1111' && S == '1' then SEE SUBS PC, LR and related instructions; + if (Rd == 15 && setflags) + return EmulateSUBSPcLrEtc (opcode, encoding); + break; + default: + return false; + } + // Read the register value from register Rn. + uint32_t val1 = ReadCoreReg(Rn, &success); + if (!success) + return false; + + // Read the register value from register Rm. + uint32_t val2 = ReadCoreReg(Rm, &success); + if (!success) + return false; + + uint32_t shifted = Shift(val2, shift_t, shift_n, APSR_C, &success); + if (!success) + return false; + AddWithCarryResult res = AddWithCarry(~val1, shifted, 1); + + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextImmediate; + context.SetNoArgs(); + if (!WriteCoreRegOptionalFlags(context, res.result, Rd, setflags, res.carry_out, res.overflow)) + return false; + + return true; +} + +// Reverse Subtract with Carry (immediate) subtracts a register value and the value of NOT (Carry flag) from +// an immediate value, and writes the result to the destination register. It can optionally update the condition +// flags based on the result. +bool +EmulateInstructionARM::EmulateRSCImm (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + // ARM pseudo code... + if ConditionPassed() then + EncodingSpecificOperations(); + (result, carry, overflow) = AddWithCarry(NOT(R[n]), imm32, APSR.C); + if d == 15 then + ALUWritePC(result); // setflags is always FALSE here + else + R[d] = result; + if setflags then + APSR.N = result<31>; + APSR.Z = IsZeroBit(result); + APSR.C = carry; + APSR.V = overflow; +#endif + + bool success = false; + + uint32_t Rd; // the destination register + uint32_t Rn; // the first operand + bool setflags; + uint32_t imm32; // the immediate value to be added to the value obtained from Rn + switch (encoding) { + case eEncodingA1: + Rd = Bits32(opcode, 15, 12); + Rn = Bits32(opcode, 19, 16); + setflags = BitIsSet(opcode, 20); + imm32 = ARMExpandImm(opcode); // imm32 = ARMExpandImm(imm12) + + // if Rd == '1111' && S == '1' then SEE SUBS PC, LR and related instructions; + if (Rd == 15 && setflags) + return EmulateSUBSPcLrEtc (opcode, encoding); + break; + default: + return false; + } + // Read the register value from the operand register Rn. + uint32_t reg_val = ReadCoreReg(Rn, &success); + if (!success) + return false; + + AddWithCarryResult res = AddWithCarry(~reg_val, imm32, APSR_C); + + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextImmediate; + context.SetNoArgs (); + + if (!WriteCoreRegOptionalFlags(context, res.result, Rd, setflags, res.carry_out, res.overflow)) + return false; + + return true; +} + +// Reverse Subtract with Carry (register) subtracts a register value and the value of NOT (Carry flag) from an +// optionally-shifted register value, and writes the result to the destination register. It can optionally update the +// condition flags based on the result. +bool +EmulateInstructionARM::EmulateRSCReg (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + // ARM pseudo code... + if ConditionPassed() then + EncodingSpecificOperations(); + shifted = Shift(R[m], shift_t, shift_n, APSR.C); + (result, carry, overflow) = AddWithCarry(NOT(R[n]), shifted, APSR.C); + if d == 15 then + ALUWritePC(result); // setflags is always FALSE here + else + R[d] = result; + if setflags then + APSR.N = result<31>; + APSR.Z = IsZeroBit(result); + APSR.C = carry; + APSR.V = overflow; +#endif + + bool success = false; + + uint32_t Rd; // the destination register + uint32_t Rn; // the first operand + uint32_t Rm; // the second operand + bool setflags; + ARM_ShifterType shift_t; + uint32_t shift_n; // the shift applied to the value read from Rm + switch (encoding) { + case eEncodingA1: + Rd = Bits32(opcode, 15, 12); + Rn = Bits32(opcode, 19, 16); + Rm = Bits32(opcode, 3, 0); + setflags = BitIsSet(opcode, 20); + shift_n = DecodeImmShiftARM(opcode, shift_t); + + // if Rd == '1111' && S == '1' then SEE SUBS PC, LR and related instructions; + if (Rd == 15 && setflags) + return EmulateSUBSPcLrEtc (opcode, encoding); + break; + default: + return false; + } + // Read the register value from register Rn. + uint32_t val1 = ReadCoreReg(Rn, &success); + if (!success) + return false; + + // Read the register value from register Rm. + uint32_t val2 = ReadCoreReg(Rm, &success); + if (!success) + return false; + + uint32_t shifted = Shift(val2, shift_t, shift_n, APSR_C, &success); + if (!success) + return false; + AddWithCarryResult res = AddWithCarry(~val1, shifted, APSR_C); + + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextImmediate; + context.SetNoArgs(); + if (!WriteCoreRegOptionalFlags(context, res.result, Rd, setflags, res.carry_out, res.overflow)) + return false; + + return true; +} + +// Subtract with Carry (immediate) subtracts an immediate value and the value of +// NOT (Carry flag) from a register value, and writes the result to the destination register. +// It can optionally update the condition flags based on the result. +bool +EmulateInstructionARM::EmulateSBCImm (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + // ARM pseudo code... + if ConditionPassed() then + EncodingSpecificOperations(); + (result, carry, overflow) = AddWithCarry(R[n], NOT(imm32), APSR.C); + if d == 15 then // Can only occur for ARM encoding + ALUWritePC(result); // setflags is always FALSE here + else + R[d] = result; + if setflags then + APSR.N = result<31>; + APSR.Z = IsZeroBit(result); + APSR.C = carry; + APSR.V = overflow; +#endif + + bool success = false; + + uint32_t Rd; // the destination register + uint32_t Rn; // the first operand + bool setflags; + uint32_t imm32; // the immediate value to be added to the value obtained from Rn + switch (encoding) { + case eEncodingT1: + Rd = Bits32(opcode, 11, 8); + Rn = Bits32(opcode, 19, 16); + setflags = BitIsSet(opcode, 20); + imm32 = ThumbExpandImm(opcode); // imm32 = ThumbExpandImm(i:imm3:imm8) + if (BadReg(Rd) || BadReg(Rn)) + return false; + break; + case eEncodingA1: + Rd = Bits32(opcode, 15, 12); + Rn = Bits32(opcode, 19, 16); + setflags = BitIsSet(opcode, 20); + imm32 = ARMExpandImm(opcode); // imm32 = ARMExpandImm(imm12) + + // if Rd == '1111' && S == '1' then SEE SUBS PC, LR and related instructions; + if (Rd == 15 && setflags) + return EmulateSUBSPcLrEtc (opcode, encoding); + break; + default: + return false; + } + // Read the register value from the operand register Rn. + uint32_t reg_val = ReadCoreReg(Rn, &success); + if (!success) + return false; + + AddWithCarryResult res = AddWithCarry(reg_val, ~imm32, APSR_C); + + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextImmediate; + context.SetNoArgs (); + + if (!WriteCoreRegOptionalFlags(context, res.result, Rd, setflags, res.carry_out, res.overflow)) + return false; + + return true; +} + +// Subtract with Carry (register) subtracts an optionally-shifted register value and the value of +// NOT (Carry flag) from a register value, and writes the result to the destination register. +// It can optionally update the condition flags based on the result. +bool +EmulateInstructionARM::EmulateSBCReg (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + // ARM pseudo code... + if ConditionPassed() then + EncodingSpecificOperations(); + shifted = Shift(R[m], shift_t, shift_n, APSR.C); + (result, carry, overflow) = AddWithCarry(R[n], NOT(shifted), APSR.C); + if d == 15 then // Can only occur for ARM encoding + ALUWritePC(result); // setflags is always FALSE here + else + R[d] = result; + if setflags then + APSR.N = result<31>; + APSR.Z = IsZeroBit(result); + APSR.C = carry; + APSR.V = overflow; +#endif + + bool success = false; + + uint32_t Rd; // the destination register + uint32_t Rn; // the first operand + uint32_t Rm; // the second operand + bool setflags; + ARM_ShifterType shift_t; + uint32_t shift_n; // the shift applied to the value read from Rm + switch (encoding) { + case eEncodingT1: + Rd = Rn = Bits32(opcode, 2, 0); + Rm = Bits32(opcode, 5, 3); + setflags = !InITBlock(); + shift_t = SRType_LSL; + shift_n = 0; + break; + case eEncodingT2: + Rd = Bits32(opcode, 11, 8); + Rn = Bits32(opcode, 19, 16); + Rm = Bits32(opcode, 3, 0); + setflags = BitIsSet(opcode, 20); + shift_n = DecodeImmShiftThumb(opcode, shift_t); + if (BadReg(Rd) || BadReg(Rn) || BadReg(Rm)) + return false; + break; + case eEncodingA1: + Rd = Bits32(opcode, 15, 12); + Rn = Bits32(opcode, 19, 16); + Rm = Bits32(opcode, 3, 0); + setflags = BitIsSet(opcode, 20); + shift_n = DecodeImmShiftARM(opcode, shift_t); + + // if Rd == '1111' && S == '1' then SEE SUBS PC, LR and related instructions; + if (Rd == 15 && setflags) + return EmulateSUBSPcLrEtc (opcode, encoding); + break; + default: + return false; + } + // Read the register value from register Rn. + uint32_t val1 = ReadCoreReg(Rn, &success); + if (!success) + return false; + + // Read the register value from register Rm. + uint32_t val2 = ReadCoreReg(Rm, &success); + if (!success) + return false; + + uint32_t shifted = Shift(val2, shift_t, shift_n, APSR_C, &success); + if (!success) + return false; + AddWithCarryResult res = AddWithCarry(val1, ~shifted, APSR_C); + + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextImmediate; + context.SetNoArgs(); + if (!WriteCoreRegOptionalFlags(context, res.result, Rd, setflags, res.carry_out, res.overflow)) + return false; + + return true; +} + +// This instruction subtracts an immediate value from a register value, and writes the result +// to the destination register. It can optionally update the condition flags based on the result. +bool +EmulateInstructionARM::EmulateSUBImmThumb (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + // ARM pseudo code... + if ConditionPassed() then + EncodingSpecificOperations(); + (result, carry, overflow) = AddWithCarry(R[n], NOT(imm32), '1'); + R[d] = result; + if setflags then + APSR.N = result<31>; + APSR.Z = IsZeroBit(result); + APSR.C = carry; + APSR.V = overflow; +#endif + + bool success = false; + + uint32_t Rd; // the destination register + uint32_t Rn; // the first operand + bool setflags; + uint32_t imm32; // the immediate value to be subtracted from the value obtained from Rn + switch (encoding) { + case eEncodingT1: + Rd = Bits32(opcode, 2, 0); + Rn = Bits32(opcode, 5, 3); + setflags = !InITBlock(); + imm32 = Bits32(opcode, 8, 6); // imm32 = ZeroExtend(imm3, 32) + break; + case eEncodingT2: + Rd = Rn = Bits32(opcode, 10, 8); + setflags = !InITBlock(); + imm32 = Bits32(opcode, 7, 0); // imm32 = ZeroExtend(imm8, 32) + break; + case eEncodingT3: + Rd = Bits32(opcode, 11, 8); + Rn = Bits32(opcode, 19, 16); + setflags = BitIsSet(opcode, 20); + imm32 = ThumbExpandImm(opcode); // imm32 = ThumbExpandImm(i:imm3:imm8) + + // if Rd == '1111' && S == '1' then SEE CMP (immediate); + if (Rd == 15 && setflags) + return EmulateCMPImm (opcode, eEncodingT2); + + // if Rn == '1101' then SEE SUB (SP minus immediate); + if (Rn == 13) + return EmulateSUBSPImm (opcode, eEncodingT2); + + // if d == 13 || (d == 15 && S == '0') || n == 15 then UNPREDICTABLE; + if (Rd == 13 || (Rd == 15 && !setflags) || Rn == 15) + return false; + break; + case eEncodingT4: + Rd = Bits32(opcode, 11, 8); + Rn = Bits32(opcode, 19, 16); + setflags = BitIsSet(opcode, 20); + imm32 = ThumbImm12(opcode); // imm32 = ZeroExtend(i:imm3:imm8, 32) + + // if Rn == '1111' then SEE ADR; + if (Rn == 15) + return EmulateADR (opcode, eEncodingT2); + + // if Rn == '1101' then SEE SUB (SP minus immediate); + if (Rn == 13) + return EmulateSUBSPImm (opcode, eEncodingT3); + + if (BadReg(Rd)) + return false; + break; + default: + return false; + } + // Read the register value from the operand register Rn. + uint32_t reg_val = ReadCoreReg(Rn, &success); + if (!success) + return false; + + AddWithCarryResult res = AddWithCarry(reg_val, ~imm32, 1); + + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextImmediate; + context.SetNoArgs (); + + if (!WriteCoreRegOptionalFlags(context, res.result, Rd, setflags, res.carry_out, res.overflow)) + return false; + + return true; +} + +// This instruction subtracts an immediate value from a register value, and writes the result +// to the destination register. It can optionally update the condition flags based on the result. +bool +EmulateInstructionARM::EmulateSUBImmARM (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + // ARM pseudo code... + if ConditionPassed() then + EncodingSpecificOperations(); + (result, carry, overflow) = AddWithCarry(R[n], NOT(imm32), '1'); + if d == 15 then + ALUWritePC(result); // setflags is always FALSE here + else + R[d] = result; + if setflags then + APSR.N = result<31>; + APSR.Z = IsZeroBit(result); + APSR.C = carry; + APSR.V = overflow; +#endif + + bool success = false; + + uint32_t Rd; // the destination register + uint32_t Rn; // the first operand + bool setflags; + uint32_t imm32; // the immediate value to be subtracted from the value obtained from Rn + switch (encoding) { + case eEncodingA1: + Rd = Bits32(opcode, 15, 12); + Rn = Bits32(opcode, 19, 16); + setflags = BitIsSet(opcode, 20); + imm32 = ARMExpandImm(opcode); // imm32 = ARMExpandImm(imm12) + + // if Rn == '1111' && S == '0' then SEE ADR; + if (Rn == 15 && !setflags) + return EmulateADR (opcode, eEncodingA2); + + // if Rn == '1101' then SEE SUB (SP minus immediate); + if (Rn == 13) + return EmulateSUBSPImm (opcode, eEncodingA1); + + // if Rd == '1111' && S == '1' then SEE SUBS PC, LR and related instructions; + if (Rd == 15 && setflags) + return EmulateSUBSPcLrEtc (opcode, encoding); + break; + default: + return false; + } + // Read the register value from the operand register Rn. + uint32_t reg_val = ReadCoreReg(Rn, &success); + if (!success) + return false; + + AddWithCarryResult res = AddWithCarry(reg_val, ~imm32, 1); + + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextImmediate; + context.SetNoArgs (); + + if (!WriteCoreRegOptionalFlags(context, res.result, Rd, setflags, res.carry_out, res.overflow)) + return false; + + return true; +} + +// Test Equivalence (immediate) performs a bitwise exclusive OR operation on a register value and an +// immediate value. It updates the condition flags based on the result, and discards the result. +bool +EmulateInstructionARM::EmulateTEQImm (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + // ARM pseudo code... + if ConditionPassed() then + EncodingSpecificOperations(); + result = R[n] EOR imm32; + APSR.N = result<31>; + APSR.Z = IsZeroBit(result); + APSR.C = carry; + // APSR.V unchanged +#endif + + bool success = false; + + if (ConditionPassed(opcode)) + { + uint32_t Rn; + uint32_t imm32; // the immediate value to be ANDed to the value obtained from Rn + uint32_t carry; // the carry bit after ARM/Thumb Expand operation + switch (encoding) + { + case eEncodingT1: + Rn = Bits32(opcode, 19, 16); + imm32 = ThumbExpandImm_C (opcode, APSR_C, carry); // (imm32, carry) = ThumbExpandImm(i:imm3:imm8, APSR.C) + if (BadReg(Rn)) + return false; + break; + case eEncodingA1: + Rn = Bits32(opcode, 19, 16); + imm32 = ARMExpandImm_C (opcode, APSR_C, carry); // (imm32, carry) = ARMExpandImm(imm12, APSR.C) + break; + default: + return false; + } + + // Read the first operand. + uint32_t val1 = ReadCoreReg(Rn, &success); + if (!success) + return false; + + uint32_t result = val1 ^ imm32; + + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextImmediate; + context.SetNoArgs (); + + if (!WriteFlags(context, result, carry)) + return false; + } + return true; +} + +// Test Equivalence (register) performs a bitwise exclusive OR operation on a register value and an +// optionally-shifted register value. It updates the condition flags based on the result, and discards +// the result. +bool +EmulateInstructionARM::EmulateTEQReg (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + // ARM pseudo code... + if ConditionPassed() then + EncodingSpecificOperations(); + (shifted, carry) = Shift_C(R[m], shift_t, shift_n, APSR.C); + result = R[n] EOR shifted; + APSR.N = result<31>; + APSR.Z = IsZeroBit(result); + APSR.C = carry; + // APSR.V unchanged +#endif + + bool success = false; + + if (ConditionPassed(opcode)) + { + uint32_t Rn, Rm; + ARM_ShifterType shift_t; + uint32_t shift_n; // the shift applied to the value read from Rm + uint32_t carry; + switch (encoding) + { + case eEncodingT1: + Rn = Bits32(opcode, 19, 16); + Rm = Bits32(opcode, 3, 0); + shift_n = DecodeImmShiftThumb(opcode, shift_t); + if (BadReg(Rn) || BadReg(Rm)) + return false; + break; + case eEncodingA1: + Rn = Bits32(opcode, 19, 16); + Rm = Bits32(opcode, 3, 0); + shift_n = DecodeImmShiftARM(opcode, shift_t); + break; + default: + return false; + } + + // Read the first operand. + uint32_t val1 = ReadCoreReg(Rn, &success); + if (!success) + return false; + + // Read the second operand. + uint32_t val2 = ReadCoreReg(Rm, &success); + if (!success) + return false; + + uint32_t shifted = Shift_C(val2, shift_t, shift_n, APSR_C, carry, &success); + if (!success) + return false; + uint32_t result = val1 ^ shifted; + + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextImmediate; + context.SetNoArgs (); + + if (!WriteFlags(context, result, carry)) + return false; + } + return true; +} + +// Test (immediate) performs a bitwise AND operation on a register value and an immediate value. +// It updates the condition flags based on the result, and discards the result. +bool +EmulateInstructionARM::EmulateTSTImm (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + // ARM pseudo code... + if ConditionPassed() then + EncodingSpecificOperations(); + result = R[n] AND imm32; + APSR.N = result<31>; + APSR.Z = IsZeroBit(result); + APSR.C = carry; + // APSR.V unchanged +#endif + + bool success = false; + + if (ConditionPassed(opcode)) + { + uint32_t Rn; + uint32_t imm32; // the immediate value to be ANDed to the value obtained from Rn + uint32_t carry; // the carry bit after ARM/Thumb Expand operation + switch (encoding) + { + case eEncodingT1: + Rn = Bits32(opcode, 19, 16); + imm32 = ThumbExpandImm_C(opcode, APSR_C, carry); // (imm32, carry) = ThumbExpandImm(i:imm3:imm8, APSR.C) + if (BadReg(Rn)) + return false; + break; + case eEncodingA1: + Rn = Bits32(opcode, 19, 16); + imm32 = ARMExpandImm_C(opcode, APSR_C, carry); // (imm32, carry) = ARMExpandImm(imm12, APSR.C) + break; + default: + return false; + } + + // Read the first operand. + uint32_t val1 = ReadCoreReg(Rn, &success); + if (!success) + return false; + + uint32_t result = val1 & imm32; + + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextImmediate; + context.SetNoArgs (); + + if (!WriteFlags(context, result, carry)) + return false; + } + return true; +} + +// Test (register) performs a bitwise AND operation on a register value and an optionally-shifted register value. +// It updates the condition flags based on the result, and discards the result. +bool +EmulateInstructionARM::EmulateTSTReg (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + // ARM pseudo code... + if ConditionPassed() then + EncodingSpecificOperations(); + (shifted, carry) = Shift_C(R[m], shift_t, shift_n, APSR.C); + result = R[n] AND shifted; + APSR.N = result<31>; + APSR.Z = IsZeroBit(result); + APSR.C = carry; + // APSR.V unchanged +#endif + + bool success = false; + + if (ConditionPassed(opcode)) + { + uint32_t Rn, Rm; + ARM_ShifterType shift_t; + uint32_t shift_n; // the shift applied to the value read from Rm + uint32_t carry; + switch (encoding) + { + case eEncodingT1: + Rn = Bits32(opcode, 2, 0); + Rm = Bits32(opcode, 5, 3); + shift_t = SRType_LSL; + shift_n = 0; + break; + case eEncodingT2: + Rn = Bits32(opcode, 19, 16); + Rm = Bits32(opcode, 3, 0); + shift_n = DecodeImmShiftThumb(opcode, shift_t); + if (BadReg(Rn) || BadReg(Rm)) + return false; + break; + case eEncodingA1: + Rn = Bits32(opcode, 19, 16); + Rm = Bits32(opcode, 3, 0); + shift_n = DecodeImmShiftARM(opcode, shift_t); + break; + default: + return false; + } + + // Read the first operand. + uint32_t val1 = ReadCoreReg(Rn, &success); + if (!success) + return false; + + // Read the second operand. + uint32_t val2 = ReadCoreReg(Rm, &success); + if (!success) + return false; + + uint32_t shifted = Shift_C(val2, shift_t, shift_n, APSR_C, carry, &success); + if (!success) + return false; + uint32_t result = val1 & shifted; + + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextImmediate; + context.SetNoArgs (); + + if (!WriteFlags(context, result, carry)) + return false; + } + return true; +} + +// A8.6.216 SUB (SP minus register) +bool +EmulateInstructionARM::EmulateSUBSPReg (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + if ConditionPassed() then + EncodingSpecificOperations(); + shifted = Shift(R[m], shift_t, shift_n, APSR.C); + (result, carry, overflow) = AddWithCarry(SP, NOT(shifted), Ô1Õ); + if d == 15 then // Can only occur for ARM encoding + ALUWritePC(result); // setflags is always FALSE here + else + R[d] = result; + if setflags then + APSR.N = result<31>; + APSR.Z = IsZeroBit(result); + APSR.C = carry; + APSR.V = overflow; +#endif + + bool success = false; + + if (ConditionPassed(opcode)) + { + uint32_t d; + uint32_t m; + bool setflags; + ARM_ShifterType shift_t; + uint32_t shift_n; + + switch (encoding) + { + case eEncodingT1: + // d = UInt(Rd); m = UInt(Rm); setflags = (S == Ô1Õ); + d = Bits32 (opcode, 11, 8); + m = Bits32 (opcode, 3, 0); + setflags = BitIsSet (opcode, 20); + + // (shift_t, shift_n) = DecodeImmShift(type, imm3:imm2); + shift_n = DecodeImmShiftThumb (opcode, shift_t); + + // if d == 13 && (shift_t != SRType_LSL || shift_n > 3) then UNPREDICTABLE; + if ((d == 13) && ((shift_t != SRType_LSL) || (shift_n > 3))) + return false; + + // if d == 15 || BadReg(m) then UNPREDICTABLE; + if ((d == 15) || BadReg (m)) + return false; + break; + + case eEncodingA1: + // d = UInt(Rd); m = UInt(Rm); setflags = (S == Ô1Õ); + d = Bits32 (opcode, 15, 12); + m = Bits32 (opcode, 3, 0); + setflags = BitIsSet (opcode, 20); + + // if Rd == Ô1111Õ && S == Ô1Õ then SEE SUBS PC, LR and related instructions; + if (d == 15 && setflags) + EmulateSUBSPcLrEtc (opcode, encoding); + + // (shift_t, shift_n) = DecodeImmShift(type, imm5); + shift_n = DecodeImmShiftARM (opcode, shift_t); + break; + + default: + return false; + } + + // shifted = Shift(R[m], shift_t, shift_n, APSR.C); + uint32_t Rm = ReadCoreReg (m, &success); + if (!success) + return false; + + uint32_t shifted = Shift (Rm, shift_t, shift_n, APSR_C, &success); + if (!success) + return false; + + // (result, carry, overflow) = AddWithCarry(SP, NOT(shifted), Ô1Õ); + uint32_t sp_val = ReadCoreReg (SP_REG, &success); + if (!success) + return false; + + AddWithCarryResult res = AddWithCarry (sp_val, ~shifted, 1); + + EmulateInstruction::Context context; + context.type = eContextArithmetic; + RegisterInfo sp_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_sp, sp_reg); + RegisterInfo dwarf_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + m, dwarf_reg); + context.SetRegisterRegisterOperands (sp_reg, dwarf_reg); + + if (!WriteCoreRegOptionalFlags(context, res.result, dwarf_r0 + d, setflags, res.carry_out, res.overflow)) + return false; + } + return true; +} + + +// A8.6.7 ADD (register-shifted register) +bool +EmulateInstructionARM::EmulateADDRegShift (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + if ConditionPassed() then + EncodingSpecificOperations(); + shift_n = UInt(R[s]<7:0>); + shifted = Shift(R[m], shift_t, shift_n, APSR.C); + (result, carry, overflow) = AddWithCarry(R[n], shifted, Ô0Õ); + R[d] = result; + if setflags then + APSR.N = result<31>; + APSR.Z = IsZeroBit(result); + APSR.C = carry; + APSR.V = overflow; +#endif + + bool success = false; + + if (ConditionPassed(opcode)) + { + uint32_t d; + uint32_t n; + uint32_t m; + uint32_t s; + bool setflags; + ARM_ShifterType shift_t; + + switch (encoding) + { + case eEncodingA1: + // d = UInt(Rd); n = UInt(Rn); m = UInt(Rm); s = UInt(Rs); + d = Bits32 (opcode, 15, 12); + n = Bits32 (opcode, 19, 16); + m = Bits32 (opcode, 3, 0); + s = Bits32 (opcode, 11, 8); + + // setflags = (S == Ô1Õ); shift_t = DecodeRegShift(type); + setflags = BitIsSet (opcode, 20); + shift_t = DecodeRegShift (Bits32 (opcode, 6, 5)); + + // if d == 15 || n == 15 || m == 15 || s == 15 then UNPREDICTABLE; + if ((d == 15) || (m == 15) || (m == 15) || (s == 15)) + return false; + break; + + default: + return false; + } + + // shift_n = UInt(R[s]<7:0>); + uint32_t Rs = ReadCoreReg (s, &success); + if (!success) + return false; + + uint32_t shift_n = Bits32 (Rs, 7, 0); + + // shifted = Shift(R[m], shift_t, shift_n, APSR.C); + uint32_t Rm = ReadCoreReg (m, &success); + if (!success) + return false; + + uint32_t shifted = Shift (Rm, shift_t, shift_n, APSR_C, &success); + if (!success) + return false; + + // (result, carry, overflow) = AddWithCarry(R[n], shifted, Ô0Õ); + uint32_t Rn = ReadCoreReg (n, &success); + if (!success) + return false; + + AddWithCarryResult res = AddWithCarry (Rn, shifted, 0); + + // R[d] = result; + EmulateInstruction::Context context; + context.type = eContextArithmetic; + RegisterInfo reg_n; + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, reg_n); + RegisterInfo reg_m; + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + m, reg_m); + + context.SetRegisterRegisterOperands (reg_n, reg_m); + + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + d, res.result)) + return false; + + // if setflags then + // APSR.N = result<31>; + // APSR.Z = IsZeroBit(result); + // APSR.C = carry; + // APSR.V = overflow; + if (setflags) + return WriteFlags (context, res.result, res.carry_out, res.overflow); + } + return true; +} + +// A8.6.213 SUB (register) +bool +EmulateInstructionARM::EmulateSUBReg (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + if ConditionPassed() then + EncodingSpecificOperations(); + shifted = Shift(R[m], shift_t, shift_n, APSR.C); + (result, carry, overflow) = AddWithCarry(R[n], NOT(shifted), Ô1Õ); + if d == 15 then // Can only occur for ARM encoding + ALUWritePC(result); // setflags is always FALSE here + else + R[d] = result; + if setflags then + APSR.N = result<31>; + APSR.Z = IsZeroBit(result); + APSR.C = carry; + APSR.V = overflow; +#endif + + bool success = false; + + if (ConditionPassed(opcode)) + { + uint32_t d; + uint32_t n; + uint32_t m; + bool setflags; + ARM_ShifterType shift_t; + uint32_t shift_n; + + switch (encoding) + { + case eEncodingT1: + // d = UInt(Rd); n = UInt(Rn); m = UInt(Rm); setflags = !InITBlock(); + d = Bits32 (opcode, 2, 0); + n = Bits32 (opcode, 5, 3); + m = Bits32 (opcode, 8, 6); + setflags = !InITBlock(); + + // (shift_t, shift_n) = (SRType_LSL, 0); + shift_t = SRType_LSL; + shift_n = 0; + + break; + + case eEncodingT2: + // if Rd == Ô1111Õ && S == Ô1Õ then SEE CMP (register); + // if Rn == Ô1101Õ then SEE SUB (SP minus register); + // d = UInt(Rd); n = UInt(Rn); m = UInt(Rm); setflags = (S == Ô1Õ); + d = Bits32 (opcode, 11, 8); + n = Bits32 (opcode, 19, 16); + m = Bits32 (opcode, 3, 0); + setflags = BitIsSet (opcode, 20); + + // (shift_t, shift_n) = DecodeImmShift(type, imm3:imm2); + shift_n = DecodeImmShiftThumb (opcode, shift_t); + + // if d == 13 || (d == 15 && S == '0') || n == 15 || BadReg(m) then UNPREDICTABLE; + if ((d == 13) || ((d == 15) && BitIsClear (opcode, 20)) || (n == 15) || BadReg (m)) + return false; + + break; + + case eEncodingA1: + // if Rn == Ô1101Õ then SEE SUB (SP minus register); + // d = UInt(Rd); n = UInt(Rn); m = UInt(Rm); setflags = (S == Ô1Õ); + d = Bits32 (opcode, 15, 12); + n = Bits32 (opcode, 19, 16); + m = Bits32 (opcode, 3, 0); + setflags = BitIsSet (opcode, 20); + + // if Rd == Ô1111Õ && S == Ô1Õ then SEE SUBS PC, LR and related instructions; + if ((d == 15) && setflags) + EmulateSUBSPcLrEtc (opcode, encoding); + + // (shift_t, shift_n) = DecodeImmShift(type, imm5); + shift_n = DecodeImmShiftARM (opcode, shift_t); + + break; + + default: + return false; + } + + // shifted = Shift(R[m], shift_t, shift_n, APSR.C); + uint32_t Rm = ReadCoreReg (m, &success); + if (!success) + return false; + + uint32_t shifted = Shift (Rm, shift_t, shift_n, APSR_C, &success); + if (!success) + return false; + + // (result, carry, overflow) = AddWithCarry(R[n], NOT(shifted), Ô1Õ); + uint32_t Rn = ReadCoreReg (n, &success); + if (!success) + return false; + + AddWithCarryResult res = AddWithCarry (Rn, ~shifted, 1); + + // if d == 15 then // Can only occur for ARM encoding + // ALUWritePC(result); // setflags is always FALSE here + // else + // R[d] = result; + // if setflags then + // APSR.N = result<31>; + // APSR.Z = IsZeroBit(result); + // APSR.C = carry; + // APSR.V = overflow; + + EmulateInstruction::Context context; + context.type = eContextArithmetic; + RegisterInfo reg_n; + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, reg_n); + RegisterInfo reg_m; + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + m, reg_m); + context.SetRegisterRegisterOperands (reg_n, reg_m); + + if (!WriteCoreRegOptionalFlags (context, res.result, dwarf_r0 + d, setflags, res.carry_out, res.overflow)) + return false; + } + return true; +} + +// A8.6.202 STREX +// Store Register Exclusive calculates an address from a base register value and an immediate offset, and stores a +// word from a register to memory if the executing processor has exclusive access to the memory addressed. +bool +EmulateInstructionARM::EmulateSTREX (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + if ConditionPassed() then + EncodingSpecificOperations(); NullCheckIfThumbEE(n); + address = R[n] + imm32; + if ExclusiveMonitorsPass(address,4) then + MemA[address,4] = R[t]; + R[d] = 0; + else + R[d] = 1; +#endif + + bool success = false; + + if (ConditionPassed(opcode)) + { + uint32_t d; + uint32_t t; + uint32_t n; + uint32_t imm32; + const uint32_t addr_byte_size = GetAddressByteSize(); + + switch (encoding) + { + case eEncodingT1: + // d = UInt(Rd); t = UInt(Rt); n = UInt(Rn); imm32 = ZeroExtend(imm8:Õ00Õ, 32); + d = Bits32 (opcode, 11, 8); + t = Bits32 (opcode, 15, 12); + n = Bits32 (opcode, 19, 16); + imm32 = Bits32 (opcode, 7, 0) << 2; + + // if BadReg(d) || BadReg(t) || n == 15 then UNPREDICTABLE; + if (BadReg (d) || BadReg (t) || (n == 15)) + return false; + + // if d == n || d == t then UNPREDICTABLE; + if ((d == n) || (d == t)) + return false; + + break; + + case eEncodingA1: + // d = UInt(Rd); t = UInt(Rt); n = UInt(Rn); imm32 = Zeros(32); // Zero offset + d = Bits32 (opcode, 15, 12); + t = Bits32 (opcode, 3, 0); + n = Bits32 (opcode, 19, 16); + imm32 = 0; + + // if d == 15 || t == 15 || n == 15 then UNPREDICTABLE; + if ((d == 15) || (t == 15) || (n == 15)) + return false; + + // if d == n || d == t then UNPREDICTABLE; + if ((d == n) || (d == t)) + return false; + + break; + + default: + return false; + } + + // address = R[n] + imm32; + uint32_t Rn = ReadCoreReg (n, &success); + if (!success) + return false; + + addr_t address = Rn + imm32; + + RegisterInfo base_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, base_reg); + RegisterInfo data_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + t, data_reg); + EmulateInstruction::Context context; + context.type = eContextRegisterStore; + context.SetRegisterToRegisterPlusOffset (data_reg, base_reg, imm32); + + // if ExclusiveMonitorsPass(address,4) then + // if (ExclusiveMonitorsPass (address, addr_byte_size)) -- For now, for the sake of emulation, we will say this + // always return true. + if (true) + { + // MemA[address,4] = R[t]; + uint32_t Rt = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_r0 + t, 0, &success); + if (!success) + return false; + + if (!MemAWrite (context, address, Rt, addr_byte_size)) + return false; + + // R[d] = 0; + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + t, 0)) + return false; + } + else + { + // R[d] = 1; + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + t, 1)) + return false; + } + } + return true; +} + +// A8.6.197 STRB (immediate, ARM) +bool +EmulateInstructionARM::EmulateSTRBImmARM (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + if ConditionPassed() then + EncodingSpecificOperations(); + offset_addr = if add then (R[n] + imm32) else (R[n] - imm32); + address = if index then offset_addr else R[n]; + MemU[address,1] = R[t]<7:0>; + if wback then R[n] = offset_addr; +#endif + + bool success = false; + + if (ConditionPassed(opcode)) + { + uint32_t t; + uint32_t n; + uint32_t imm32; + bool index; + bool add; + bool wback; + + switch (encoding) + { + case eEncodingA1: + // if P == Ô0Õ && W == Ô1Õ then SEE STRBT; + // t = UInt(Rt); n = UInt(Rn); imm32 = ZeroExtend(imm12, 32); + t = Bits32 (opcode, 15, 12); + n = Bits32 (opcode, 19, 16); + imm32 = Bits32 (opcode, 11, 0); + + // index = (P == Ô1Õ); add = (U == Ô1Õ); wback = (P == Ô0Õ) || (W == Ô1Õ); + index = BitIsSet (opcode, 24); + add = BitIsSet (opcode, 23); + wback = BitIsClear (opcode, 24) || BitIsSet (opcode, 21); + + // if t == 15 then UNPREDICTABLE; + if (t == 15) + return false; + + // if wback && (n == 15 || n == t) then UNPREDICTABLE; + if (wback && ((n == 15) || (n == t))) + return false; + + break; + + default: + return false; + } + + // offset_addr = if add then (R[n] + imm32) else (R[n] - imm32); + uint32_t Rn = ReadCoreReg (n, &success); + if (!success) + return false; + + addr_t offset_addr; + if (add) + offset_addr = Rn + imm32; + else + offset_addr = Rn - imm32; + + // address = if index then offset_addr else R[n]; + addr_t address; + if (index) + address = offset_addr; + else + address = Rn; + + // MemU[address,1] = R[t]<7:0>; + uint32_t Rt = ReadCoreReg (t, &success); + if (!success) + return false; + + RegisterInfo base_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, base_reg); + RegisterInfo data_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + t, data_reg); + EmulateInstruction::Context context; + context.type = eContextRegisterStore; + context.SetRegisterToRegisterPlusOffset (data_reg, base_reg, address - Rn); + + if (!MemUWrite (context, address, Bits32 (Rt, 7, 0), 1)) + return false; + + // if wback then R[n] = offset_addr; + if (wback) + { + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + n, offset_addr)) + return false; + } + } + return true; +} + +// A8.6.194 STR (immediate, ARM) +bool +EmulateInstructionARM::EmulateSTRImmARM (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + if ConditionPassed() then + EncodingSpecificOperations(); + offset_addr = if add then (R[n] + imm32) else (R[n] - imm32); + address = if index then offset_addr else R[n]; + MemU[address,4] = if t == 15 then PCStoreValue() else R[t]; + if wback then R[n] = offset_addr; +#endif + + bool success = false; + + if (ConditionPassed(opcode)) + { + uint32_t t; + uint32_t n; + uint32_t imm32; + bool index; + bool add; + bool wback; + + const uint32_t addr_byte_size = GetAddressByteSize(); + + switch (encoding) + { + case eEncodingA1: + // if P == Ô0Õ && W == Ô1Õ then SEE STRT; + // if Rn == Ô1101Õ && P == Ô1Õ && U == Ô0Õ && W == Ô1Õ && imm12 == Ô000000000100Õ then SEE PUSH; + // t = UInt(Rt); n = UInt(Rn); imm32 = ZeroExtend(imm12, 32); + t = Bits32 (opcode, 15, 12); + n = Bits32 (opcode, 19, 16); + imm32 = Bits32 (opcode, 11, 0); + + // index = (P == Ô1Õ); add = (U == Ô1Õ); wback = (P == Ô0Õ) || (W == Ô1Õ); + index = BitIsSet (opcode, 24); + add = BitIsSet (opcode, 23); + wback = BitIsClear (opcode, 24) || BitIsSet (opcode, 21); + + // if wback && (n == 15 || n == t) then UNPREDICTABLE; + if (wback && ((n == 15) || (n == t))) + return false; + + break; + + default: + return false; + } + + // offset_addr = if add then (R[n] + imm32) else (R[n] - imm32); + uint32_t Rn = ReadCoreReg (n, &success); + if (!success) + return false; + + addr_t offset_addr; + if (add) + offset_addr = Rn + imm32; + else + offset_addr = Rn - imm32; + + // address = if index then offset_addr else R[n]; + addr_t address; + if (index) + address = offset_addr; + else + address = Rn; + + RegisterInfo base_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, base_reg); + RegisterInfo data_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + t, data_reg); + EmulateInstruction::Context context; + context.type = eContextRegisterStore; + context.SetRegisterToRegisterPlusOffset (data_reg, base_reg, address - Rn); + + // MemU[address,4] = if t == 15 then PCStoreValue() else R[t]; + uint32_t Rt = ReadCoreReg (t, &success); + if (!success) + return false; + + if (t == 15) + { + uint32_t pc_value = ReadCoreReg (PC_REG, &success); + if (!success) + return false; + + if (!MemUWrite (context, address, pc_value, addr_byte_size)) + return false; + } + else + { + if (!MemUWrite (context, address, Rt, addr_byte_size)) + return false; + } + + // if wback then R[n] = offset_addr; + if (wback) + { + context.type = eContextAdjustBaseRegister; + context.SetImmediate (offset_addr); + + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + n, offset_addr)) + return false; + } + } + return true; +} + +// A8.6.66 LDRD (immediate) +// Load Register Dual (immediate) calculates an address from a base register value and an immediate offset, loads two +// words from memory, and writes them to two registers. It can use offset, post-indexed, or pre-indexed addressing. +bool +EmulateInstructionARM::EmulateLDRDImmediate (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + if ConditionPassed() then + EncodingSpecificOperations(); NullCheckIfThumbEE(n); + offset_addr = if add then (R[n] + imm32) else (R[n] - imm32); + address = if index then offset_addr else R[n]; + R[t] = MemA[address,4]; + R[t2] = MemA[address+4,4]; + if wback then R[n] = offset_addr; +#endif + + bool success = false; + + if (ConditionPassed(opcode)) + { + uint32_t t; + uint32_t t2; + uint32_t n; + uint32_t imm32; + bool index; + bool add; + bool wback; + + switch (encoding) + { + case eEncodingT1: + //if P == Ô0Õ && W == Ô0Õ then SEE ÒRelated encodingsÓ; + //if Rn == Ô1111Õ then SEE LDRD (literal); + //t = UInt(Rt); t2 = UInt(Rt2); n = UInt(Rn); imm32 = ZeroExtend(imm8:Õ00Õ, 32); + t = Bits32 (opcode, 15, 12); + t2 = Bits32 (opcode, 11, 8); + n = Bits32 (opcode, 19, 16); + imm32 = Bits32 (opcode, 7, 0) << 2; + + //index = (P == Ô1Õ); add = (U == Ô1Õ); wback = (W == Ô1Õ); + index = BitIsSet (opcode, 24); + add = BitIsSet (opcode, 23); + wback = BitIsSet (opcode, 21); + + //if wback && (n == t || n == t2) then UNPREDICTABLE; + if (wback && ((n == t) || (n == t2))) + return false; + + //if BadReg(t) || BadReg(t2) || t == t2 then UNPREDICTABLE; + if (BadReg (t) || BadReg (t2) || (t == t2)) + return false; + + break; + + case eEncodingA1: + //if Rn == Ô1111Õ then SEE LDRD (literal); + //if Rt<0> == Ô1Õ then UNPREDICTABLE; + //t = UInt(Rt); t2 = t+1; n = UInt(Rn); imm32 = ZeroExtend(imm4H:imm4L, 32); + t = Bits32 (opcode, 15, 12); + if (BitIsSet (t, 0)) + return false; + t2 = t + 1; + n = Bits32 (opcode, 19, 16); + imm32 = (Bits32 (opcode, 11, 8) << 4) | Bits32 (opcode, 3, 0); + + //index = (P == Ô1Õ); add = (U == Ô1Õ); wback = (P == Ô0Õ) || (W == Ô1Õ); + index = BitIsSet (opcode, 24); + add = BitIsSet (opcode, 23); + wback = BitIsClear (opcode, 24) || BitIsSet (opcode, 21); + + //if P == Ô0Õ && W == Ô1Õ then UNPREDICTABLE; + if (BitIsClear (opcode, 24) && BitIsSet (opcode, 21)) + return false; + + //if wback && (n == t || n == t2) then UNPREDICTABLE; + if (wback && ((n == t) || (n == t2))) + return false; + + //if t2 == 15 then UNPREDICTABLE; + if (t2 == 15) + return false; + + break; + + default: + return false; + } + + //offset_addr = if add then (R[n] + imm32) else (R[n] - imm32); + uint32_t Rn = ReadCoreReg (n, &success); + if (!success) + return false; + + addr_t offset_addr; + if (add) + offset_addr = Rn + imm32; + else + offset_addr = Rn - imm32; + + //address = if index then offset_addr else R[n]; + addr_t address; + if (index) + address = offset_addr; + else + address = Rn; + + //R[t] = MemA[address,4]; + RegisterInfo base_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, base_reg); + + EmulateInstruction::Context context; + context.type = eContextRegisterLoad; + context.SetRegisterPlusOffset (base_reg, address - Rn); + + const uint32_t addr_byte_size = GetAddressByteSize(); + uint32_t data = MemARead (context, address, addr_byte_size, 0, &success); + if (!success) + return false; + + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + t, data)) + return false; + + //R[t2] = MemA[address+4,4]; + + context.SetRegisterPlusOffset (base_reg, (address + 4) - Rn); + data = MemARead (context, address + 4, addr_byte_size, 0, &success); + if (!success) + return false; + + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + t2, data)) + return false; + + //if wback then R[n] = offset_addr; + if (wback) + { + context.type = eContextAdjustBaseRegister; + context.SetAddress (offset_addr); + + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + n, offset_addr)) + return false; + } + } + return true; +} + +// A8.6.68 LDRD (register) +// Load Register Dual (register) calculates an address from a base register value and a register offset, loads two +// words from memory, and writes them to two registers. It can use offset, post-indexed or pre-indexed addressing. +bool +EmulateInstructionARM::EmulateLDRDRegister (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + if ConditionPassed() then + EncodingSpecificOperations(); + offset_addr = if add then (R[n] + R[m]) else (R[n] - R[m]); + address = if index then offset_addr else R[n]; + R[t] = MemA[address,4]; + R[t2] = MemA[address+4,4]; + if wback then R[n] = offset_addr; +#endif + + bool success = false; + + if (ConditionPassed(opcode)) + { + uint32_t t; + uint32_t t2; + uint32_t n; + uint32_t m; + bool index; + bool add; + bool wback; + + switch (encoding) + { + case eEncodingA1: + // if Rt<0> == Ô1Õ then UNPREDICTABLE; + // t = UInt(Rt); t2 = t+1; n = UInt(Rn); m = UInt(Rm); + t = Bits32 (opcode, 15, 12); + if (BitIsSet (t, 0)) + return false; + t2 = t + 1; + n = Bits32 (opcode, 19, 16); + m = Bits32 (opcode, 3, 0); + + // index = (P == Ô1Õ); add = (U == Ô1Õ); wback = (P == Ô0Õ) || (W == Ô1Õ); + index = BitIsSet (opcode, 24); + add = BitIsSet (opcode, 23); + wback = BitIsClear (opcode, 24) || BitIsSet (opcode, 21); + + // if P == Ô0Õ && W == Ô1Õ then UNPREDICTABLE; + if (BitIsClear (opcode, 24) && BitIsSet (opcode, 21)) + return false; + + // if t2 == 15 || m == 15 || m == t || m == t2 then UNPREDICTABLE; + if ((t2 == 15) || (m == 15) || (m == t) || (m == t2)) + return false; + + // if wback && (n == 15 || n == t || n == t2) then UNPREDICTABLE; + if (wback && ((n == 15) || (n == t) || (n == t2))) + return false; + + // if ArchVersion() < 6 && wback && m == n then UNPREDICTABLE; + if ((ArchVersion() < 6) && wback && (m == n)) + return false; + break; + + default: + return false; + } + + uint32_t Rn = ReadCoreReg (n, &success); + if (!success) + return false; + RegisterInfo base_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, base_reg); + + uint32_t Rm = ReadCoreReg (m, &success); + if (!success) + return false; + RegisterInfo offset_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + m, offset_reg); + + // offset_addr = if add then (R[n] + R[m]) else (R[n] - R[m]); + addr_t offset_addr; + if (add) + offset_addr = Rn + Rm; + else + offset_addr = Rn - Rm; + + // address = if index then offset_addr else R[n]; + addr_t address; + if (index) + address = offset_addr; + else + address = Rn; + + EmulateInstruction::Context context; + context.type = eContextRegisterLoad; + context.SetRegisterPlusIndirectOffset (base_reg, offset_reg); + + // R[t] = MemA[address,4]; + const uint32_t addr_byte_size = GetAddressByteSize(); + uint32_t data = MemARead (context, address, addr_byte_size, 0, &success); + if (!success) + return false; + + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + t, data)) + return false; + + // R[t2] = MemA[address+4,4]; + + data = MemARead (context, address + 4, addr_byte_size, 0, &success); + if (!success) + return false; + + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + t2, data)) + return false; + + // if wback then R[n] = offset_addr; + if (wback) + { + context.type = eContextAdjustBaseRegister; + context.SetAddress (offset_addr); + + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + n, offset_addr)) + return false; + } + } + return true; +} + +// A8.6.200 STRD (immediate) +// Store Register Dual (immediate) calculates an address from a base register value and an immediate offset, and +// stores two words from two registers to memory. It can use offset, post-indexed, or pre-indexed addressing. +bool +EmulateInstructionARM::EmulateSTRDImm (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + if ConditionPassed() then + EncodingSpecificOperations(); NullCheckIfThumbEE(n); + offset_addr = if add then (R[n] + imm32) else (R[n] - imm32); + address = if index then offset_addr else R[n]; + MemA[address,4] = R[t]; + MemA[address+4,4] = R[t2]; + if wback then R[n] = offset_addr; +#endif + + bool success = false; + + if (ConditionPassed(opcode)) + { + uint32_t t; + uint32_t t2; + uint32_t n; + uint32_t imm32; + bool index; + bool add; + bool wback; + + switch (encoding) + { + case eEncodingT1: + // if P == Ô0Õ && W == Ô0Õ then SEE ÒRelated encodingsÓ; + // t = UInt(Rt); t2 = UInt(Rt2); n = UInt(Rn); imm32 = ZeroExtend(imm8:Õ00Õ, 32); + t = Bits32 (opcode, 15, 12); + t2 = Bits32 (opcode, 11, 8); + n = Bits32 (opcode, 19, 16); + imm32 = Bits32 (opcode, 7, 0) << 2; + + // index = (P == Ô1Õ); add = (U == Ô1Õ); wback = (W == Ô1Õ); + index = BitIsSet (opcode, 24); + add = BitIsSet (opcode, 23); + wback = BitIsSet (opcode, 21); + + // if wback && (n == t || n == t2) then UNPREDICTABLE; + if (wback && ((n == t) || (n == t2))) + return false; + + // if n == 15 || BadReg(t) || BadReg(t2) then UNPREDICTABLE; + if ((n == 15) || BadReg (t) || BadReg (t2)) + return false; + + break; + + case eEncodingA1: + // if Rt<0> == Ô1Õ then UNPREDICTABLE; + // t = UInt(Rt); t2 = t+1; n = UInt(Rn); imm32 = ZeroExtend(imm4H:imm4L, 32); + t = Bits32 (opcode, 15, 12); + if (BitIsSet (t, 0)) + return false; + + t2 = t + 1; + n = Bits32 (opcode, 19, 16); + imm32 = (Bits32 (opcode, 11, 8) << 4) | Bits32 (opcode, 3, 0); + + // index = (P == Ô1Õ); add = (U == Ô1Õ); wback = (P == Ô0Õ) || (W == Ô1Õ); + index = BitIsSet (opcode, 24); + add = BitIsSet (opcode, 23); + wback = BitIsClear (opcode, 24) || BitIsSet (opcode, 21); + + // if P == Ô0Õ && W == Ô1Õ then UNPREDICTABLE; + if (BitIsClear (opcode, 24) && BitIsSet (opcode, 21)) + return false; + + // if wback && (n == 15 || n == t || n == t2) then UNPREDICTABLE; + if (wback && ((n == 15) || (n == t) || (n == t2))) + return false; + + // if t2 == 15 then UNPREDICTABLE; + if (t2 == 15) + return false; + + break; + + default: + return false; + } + + RegisterInfo base_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, base_reg); + + uint32_t Rn = ReadCoreReg (n, &success); + if (!success) + return false; + + //offset_addr = if add then (R[n] + imm32) else (R[n] - imm32); + addr_t offset_addr; + if (add) + offset_addr = Rn + imm32; + else + offset_addr = Rn - imm32; + + //address = if index then offset_addr else R[n]; + addr_t address; + if (index) + address = offset_addr; + else + address = Rn; + + //MemA[address,4] = R[t]; + RegisterInfo data_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + t, data_reg); + + uint32_t data = ReadCoreReg (t, &success); + if (!success) + return false; + + EmulateInstruction::Context context; + context.type = eContextRegisterStore; + context.SetRegisterToRegisterPlusOffset (data_reg, base_reg, address - Rn); + + const uint32_t addr_byte_size = GetAddressByteSize(); + + if (!MemAWrite (context, address, data, addr_byte_size)) + return false; + + //MemA[address+4,4] = R[t2]; + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + t2, data_reg); + context.SetRegisterToRegisterPlusOffset (data_reg, base_reg, (address + 4) - Rn); + + data = ReadCoreReg (t2, &success); + if (!success) + return false; + + if (!MemAWrite (context, address + 4, data, addr_byte_size)) + return false; + + //if wback then R[n] = offset_addr; + if (wback) + { + context.type = eContextAdjustBaseRegister; + context.SetAddress (offset_addr); + + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + n, offset_addr)) + return false; + } + } + return true; +} + + +// A8.6.201 STRD (register) +bool +EmulateInstructionARM::EmulateSTRDReg (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + if ConditionPassed() then + EncodingSpecificOperations(); + offset_addr = if add then (R[n] + R[m]) else (R[n] - R[m]); + address = if index then offset_addr else R[n]; + MemA[address,4] = R[t]; + MemA[address+4,4] = R[t2]; + if wback then R[n] = offset_addr; +#endif + + bool success = false; + + if (ConditionPassed(opcode)) + { + uint32_t t; + uint32_t t2; + uint32_t n; + uint32_t m; + bool index; + bool add; + bool wback; + + switch (encoding) + { + case eEncodingA1: + // if Rt<0> == Ô1Õ then UNPREDICTABLE; + // t = UInt(Rt); t2 = t+1; n = UInt(Rn); m = UInt(Rm); + t = Bits32 (opcode, 15, 12); + if (BitIsSet (t, 0)) + return false; + + t2 = t+1; + n = Bits32 (opcode, 19, 16); + m = Bits32 (opcode, 3, 0); + + // index = (P == Ô1Õ); add = (U == Ô1Õ); wback = (P == Ô0Õ) || (W == Ô1Õ); + index = BitIsSet (opcode, 24); + add = BitIsSet (opcode, 23); + wback = BitIsClear (opcode, 24) || BitIsSet (opcode, 21); + + // if P == Ô0Õ && W == Ô1Õ then UNPREDICTABLE; + if (BitIsClear (opcode, 24) && BitIsSet (opcode, 21)) + return false; + + // if t2 == 15 || m == 15 then UNPREDICTABLE; + if ((t2 == 15) || (m == 15)) + return false; + + // if wback && (n == 15 || n == t || n == t2) then UNPREDICTABLE; + if (wback && ((n == 15) || (n == t) || (n == t2))) + return false; + + // if ArchVersion() < 6 && wback && m == n then UNPREDICTABLE; + if ((ArchVersion() < 6) && wback && (m == n)) + return false; + + break; + + default: + return false; + } + + RegisterInfo base_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, base_reg); + RegisterInfo offset_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + m, offset_reg); + RegisterInfo data_reg; + + uint32_t Rn = ReadCoreReg (n, &success); + if (!success) + return false; + + uint32_t Rm = ReadCoreReg (m, &success); + if (!success) + return false; + + // offset_addr = if add then (R[n] + R[m]) else (R[n] - R[m]); + addr_t offset_addr; + if (add) + offset_addr = Rn + Rm; + else + offset_addr = Rn - Rm; + + // address = if index then offset_addr else R[n]; + addr_t address; + if (index) + address = offset_addr; + else + address = Rn; + // MemA[address,4] = R[t]; + uint32_t Rt = ReadCoreReg (t, &success); + if (!success) + return false; + + EmulateInstruction::Context context; + context.type = eContextRegisterStore; + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + t, data_reg); + context.SetRegisterToRegisterPlusIndirectOffset (base_reg, offset_reg, data_reg); + + const uint32_t addr_byte_size = GetAddressByteSize(); + + if (!MemAWrite (context, address, Rt, addr_byte_size)) + return false; + + // MemA[address+4,4] = R[t2]; + uint32_t Rt2 = ReadCoreReg (t2, &success); + if (!success) + return false; + + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + t2, data_reg); + + context.SetRegisterToRegisterPlusIndirectOffset (base_reg, offset_reg, data_reg); + + if (!MemAWrite (context, address + 4, Rt2, addr_byte_size)) + return false; + + // if wback then R[n] = offset_addr; + if (wback) + { + context.type = eContextAdjustBaseRegister; + context.SetAddress (offset_addr); + + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + n, offset_addr)) + return false; + + } + } + return true; +} + +// A8.6.319 VLDM +// Vector Load Multiple loads multiple extension registers from consecutive memory locations using an address from +// an ARM core register. +bool +EmulateInstructionARM::EmulateVLDM (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + if ConditionPassed() then + EncodingSpecificOperations(); CheckVFPEnabled(TRUE); NullCheckIfThumbEE(n); + address = if add then R[n] else R[n]-imm32; + if wback then R[n] = if add then R[n]+imm32 else R[n]-imm32; + for r = 0 to regs-1 + if single_regs then + S[d+r] = MemA[address,4]; address = address+4; + else + word1 = MemA[address,4]; word2 = MemA[address+4,4]; address = address+8; + // Combine the word-aligned words in the correct order for current endianness. + D[d+r] = if BigEndian() then word1:word2 else word2:word1; +#endif + + bool success = false; + + if (ConditionPassed(opcode)) + { + bool single_regs; + bool add; + bool wback; + uint32_t d; + uint32_t n; + uint32_t imm32; + uint32_t regs; + + switch (encoding) + { + case eEncodingT1: + case eEncodingA1: + // if P == Ô0Õ && U == Ô0Õ && W == Ô0Õ then SEE ÒRelated encodingsÓ; + // if P == Ô0Õ && U == Ô1Õ && W == Ô1Õ && Rn == Ô1101Õ then SEE VPOP; + // if P == Ô1Õ && W == Ô0Õ then SEE VLDR; + // if P == U && W == Ô1Õ then UNDEFINED; + if ((Bit32 (opcode, 24) == Bit32 (opcode, 23)) && BitIsSet (opcode, 21)) + return false; + + // // Remaining combinations are PUW = 010 (IA without !), 011 (IA with !), 101 (DB with !) + // single_regs = FALSE; add = (U == Ô1Õ); wback = (W == Ô1Õ); + single_regs = false; + add = BitIsSet (opcode, 23); + wback = BitIsSet (opcode, 21); + + // d = UInt(D:Vd); n = UInt(Rn); imm32 = ZeroExtend(imm8:Õ00Õ, 32); + d = (Bit32 (opcode, 22) << 4) | Bits32 (opcode, 15, 12); + n = Bits32 (opcode, 19, 16); + imm32 = Bits32 (opcode, 7, 0) << 2; + + // regs = UInt(imm8) DIV 2; // If UInt(imm8) is odd, see ÒFLDMXÓ. + regs = Bits32 (opcode, 7, 0) / 2; + + // if n == 15 && (wback || CurrentInstrSet() != InstrSet_ARM) then UNPREDICTABLE; + if (n == 15 && (wback || CurrentInstrSet() != eModeARM)) + return false; + + // if regs == 0 || regs > 16 || (d+regs) > 32 then UNPREDICTABLE; + if ((regs == 0) || (regs > 16) || ((d + regs) > 32)) + return false; + + break; + + case eEncodingT2: + case eEncodingA2: + // if P == Ô0Õ && U == Ô0Õ && W == Ô0Õ then SEE ÒRelated encodingsÓ; + // if P == Ô0Õ && U == Ô1Õ && W == Ô1Õ && Rn == Ô1101Õ then SEE VPOP; + // if P == Ô1Õ && W == Ô0Õ then SEE VLDR; + // if P == U && W == Ô1Õ then UNDEFINED; + if ((Bit32 (opcode, 24) == Bit32 (opcode, 23)) && BitIsSet (opcode, 21)) + return false; + + // // Remaining combinations are PUW = 010 (IA without !), 011 (IA with !), 101 (DB with !) + // single_regs = TRUE; add = (U == Ô1Õ); wback = (W == Ô1Õ); d = UInt(Vd:D); n = UInt(Rn); + single_regs = true; + add = BitIsSet (opcode, 23); + wback = BitIsSet (opcode, 21); + d = (Bits32 (opcode, 15, 12) << 1) | Bit32 (opcode, 22); + n = Bits32 (opcode, 19, 16); + + // imm32 = ZeroExtend(imm8:Õ00Õ, 32); regs = UInt(imm8); + imm32 = Bits32 (opcode, 7, 0) << 2; + regs = Bits32 (opcode, 7, 0); + + // if n == 15 && (wback || CurrentInstrSet() != InstrSet_ARM) then UNPREDICTABLE; + if ((n == 15) && (wback || (CurrentInstrSet() != eModeARM))) + return false; + + // if regs == 0 || (d+regs) > 32 then UNPREDICTABLE; + if ((regs == 0) || ((d + regs) > 32)) + return false; + break; + + default: + return false; + } + + RegisterInfo base_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, base_reg); + + uint32_t Rn = ReadCoreReg (n, &success); + if (!success) + return false; + + // address = if add then R[n] else R[n]-imm32; + addr_t address; + if (add) + address = Rn; + else + address = Rn - imm32; + + // if wback then R[n] = if add then R[n]+imm32 else R[n]-imm32; + EmulateInstruction::Context context; + + if (wback) + { + uint32_t value; + if (add) + value = Rn + imm32; + else + value = Rn - imm32; + + context.type = eContextAdjustBaseRegister; + context.SetImmediateSigned (value - Rn); + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + n, value)) + return false; + + } + + const uint32_t addr_byte_size = GetAddressByteSize(); + uint32_t start_reg = single_regs ? dwarf_s0 : dwarf_d0; + + context.type = eContextRegisterLoad; + + // for r = 0 to regs-1 + for (uint32_t r = 0; r < regs; ++r) + { + if (single_regs) + { + // S[d+r] = MemA[address,4]; address = address+4; + context.SetRegisterPlusOffset (base_reg, address - Rn); + + uint32_t data = MemARead (context, address, addr_byte_size, 0, &success); + if (!success) + return false; + + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, start_reg + d + r, data)) + return false; + + address = address + 4; + } + else + { + // word1 = MemA[address,4]; word2 = MemA[address+4,4]; address = address+8; + context.SetRegisterPlusOffset (base_reg, address - Rn); + uint32_t word1 = MemARead (context, address, addr_byte_size, 0, &success); + if (!success) + return false; + + context.SetRegisterPlusOffset (base_reg, (address + 4) - Rn); + uint32_t word2 = MemARead (context, address + 4, addr_byte_size, 0, &success); + if (!success) + return false; + + address = address + 8; + // // Combine the word-aligned words in the correct order for current endianness. + // D[d+r] = if BigEndian() then word1:word2 else word2:word1; + uint64_t data; + if (GetByteOrder() == eByteOrderBig) + { + data = word1; + data = (data << 32) | word2; + } + else + { + data = word2; + data = (data << 32) | word1; + } + + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, start_reg + d + r, data)) + return false; + } + } + } + return true; +} + +// A8.6.399 VSTM +// Vector Store Multiple stores multiple extension registers to consecutive memory locations using an address from an +// ARM core register. +bool +EmulateInstructionARM::EmulateVSTM (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + if ConditionPassed() then + EncodingSpecificOperations(); CheckVFPEnabled(TRUE); NullCheckIfThumbEE(n); + address = if add then R[n] else R[n]-imm32; + if wback then R[n] = if add then R[n]+imm32 else R[n]-imm32; + for r = 0 to regs-1 + if single_regs then + MemA[address,4] = S[d+r]; address = address+4; + else + // Store as two word-aligned words in the correct order for current endianness. + MemA[address,4] = if BigEndian() then D[d+r]<63:32> else D[d+r]<31:0>; + MemA[address+4,4] = if BigEndian() then D[d+r]<31:0> else D[d+r]<63:32>; + address = address+8; +#endif + + bool success = false; + + if (ConditionPassed (opcode)) + { + bool single_regs; + bool add; + bool wback; + uint32_t d; + uint32_t n; + uint32_t imm32; + uint32_t regs; + + switch (encoding) + { + case eEncodingT1: + case eEncodingA1: + // if P == Ô0Õ && U == Ô0Õ && W == Ô0Õ then SEE ÒRelated encodingsÓ; + // if P == Ô1Õ && U == Ô0Õ && W == Ô1Õ && Rn == Ô1101Õ then SEE VPUSH; + // if P == Ô1Õ && W == Ô0Õ then SEE VSTR; + // if P == U && W == Ô1Õ then UNDEFINED; + if ((Bit32 (opcode, 24) == Bit32 (opcode, 23)) && BitIsSet (opcode, 21)) + return false; + + // // Remaining combinations are PUW = 010 (IA without !), 011 (IA with !), 101 (DB with !) + // single_regs = FALSE; add = (U == Ô1Õ); wback = (W == Ô1Õ); + single_regs = false; + add = BitIsSet (opcode, 23); + wback = BitIsSet (opcode, 21); + + // d = UInt(D:Vd); n = UInt(Rn); imm32 = ZeroExtend(imm8:Õ00Õ, 32); + d = (Bit32 (opcode, 22) << 4) | Bits32 (opcode, 15, 12); + n = Bits32 (opcode, 19, 16); + imm32 = Bits32 (opcode, 7, 0) << 2; + + // regs = UInt(imm8) DIV 2; // If UInt(imm8) is odd, see ÒFSTMXÓ. + regs = Bits32 (opcode, 7, 0) / 2; + + // if n == 15 && (wback || CurrentInstrSet() != InstrSet_ARM) then UNPREDICTABLE; + if ((n == 15) && (wback || (CurrentInstrSet() != eModeARM))) + return false; + + // if regs == 0 || regs > 16 || (d+regs) > 32 then UNPREDICTABLE; + if ((regs == 0) || (regs > 16) || ((d + regs) > 32)) + return false; + + break; + + case eEncodingT2: + case eEncodingA2: + // if P == Ô0Õ && U == Ô0Õ && W == Ô0Õ then SEE ÒRelated encodingsÓ; + // if P == Ô1Õ && U == Ô0Õ && W == Ô1Õ && Rn == Ô1101Õ then SEE VPUSH; + // if P == Ô1Õ && W == Ô0Õ then SEE VSTR; + // if P == U && W == Ô1Õ then UNDEFINED; + if ((Bit32 (opcode, 24) == Bit32 (opcode, 23)) && BitIsSet (opcode, 21)) + return false; + + // // Remaining combinations are PUW = 010 (IA without !), 011 (IA with !), 101 (DB with !) + // single_regs = TRUE; add = (U == Ô1Õ); wback = (W == Ô1Õ); d = UInt(Vd:D); n = UInt(Rn); + single_regs = true; + add = BitIsSet (opcode, 23); + wback = BitIsSet (opcode, 21); + d = (Bits32 (opcode, 15, 12) << 1) | Bit32 (opcode, 22); + n = Bits32 (opcode, 19, 16); + + // imm32 = ZeroExtend(imm8:Õ00Õ, 32); regs = UInt(imm8); + imm32 = Bits32 (opcode, 7, 0) << 2; + regs = Bits32 (opcode, 7, 0); + + // if n == 15 && (wback || CurrentInstrSet() != InstrSet_ARM) then UNPREDICTABLE; + if ((n == 15) && (wback || (CurrentInstrSet () != eModeARM))) + return false; + + // if regs == 0 || (d+regs) > 32 then UNPREDICTABLE; + if ((regs == 0) || ((d + regs) > 32)) + return false; + + break; + + default: + return false; + } + + RegisterInfo base_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, base_reg); + + uint32_t Rn = ReadCoreReg (n, &success); + if (!success) + return false; + + // address = if add then R[n] else R[n]-imm32; + addr_t address; + if (add) + address = Rn; + else + address = Rn - imm32; + + EmulateInstruction::Context context; + // if wback then R[n] = if add then R[n]+imm32 else R[n]-imm32; + if (wback) + { + uint32_t value; + if (add) + value = Rn + imm32; + else + value = Rn - imm32; + + context.type = eContextAdjustBaseRegister; + context.SetRegisterPlusOffset (base_reg, value - Rn); + + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + n, value)) + return false; + } + + const uint32_t addr_byte_size = GetAddressByteSize(); + uint32_t start_reg = single_regs ? dwarf_s0 : dwarf_d0; + + context.type = eContextRegisterStore; + // for r = 0 to regs-1 + for (uint32_t r = 0; r < regs; ++r) + { + + if (single_regs) + { + // MemA[address,4] = S[d+r]; address = address+4; + uint32_t data = ReadRegisterUnsigned (eRegisterKindDWARF, start_reg + d + r, 0, &success); + if (!success) + return false; + + RegisterInfo data_reg; + GetRegisterInfo (eRegisterKindDWARF, start_reg + d + r, data_reg); + context.SetRegisterToRegisterPlusOffset (data_reg, base_reg, address - Rn); + if (!MemAWrite (context, address, data, addr_byte_size)) + return false; + + address = address + 4; + } + else + { + // // Store as two word-aligned words in the correct order for current endianness. + // MemA[address,4] = if BigEndian() then D[d+r]<63:32> else D[d+r]<31:0>; + // MemA[address+4,4] = if BigEndian() then D[d+r]<31:0> else D[d+r]<63:32>; + uint64_t data = ReadRegisterUnsigned (eRegisterKindDWARF, start_reg + d + r, 0, &success); + if (!success) + return false; + + RegisterInfo data_reg; + GetRegisterInfo (eRegisterKindDWARF, start_reg + d + r, data_reg); + + if (GetByteOrder() == eByteOrderBig) + { + context.SetRegisterToRegisterPlusOffset (data_reg, base_reg, address - Rn); + if (!MemAWrite (context, address, Bits64 (data, 63, 32), addr_byte_size)) + return false; + + context.SetRegisterToRegisterPlusOffset (data_reg, base_reg, (address + 4) - Rn); + if (!MemAWrite (context, address+ 4, Bits64 (data, 31, 0), addr_byte_size)) + return false; + } + else + { + context.SetRegisterToRegisterPlusOffset (data_reg, base_reg, address - Rn); + if (!MemAWrite (context, address, Bits64 (data, 31, 0), addr_byte_size)) + return false; + + context.SetRegisterToRegisterPlusOffset (data_reg, base_reg, (address + 4) - Rn); + if (!MemAWrite (context, address + 4, Bits64 (data, 63, 32), addr_byte_size)) + return false; + } + // address = address+8; + address = address + 8; + } + } + } + return true; +} + +// A8.6.320 +// This instruciton loads a single extension register fronm memory, using an address from an ARM core register, with +// an optional offset. +bool +EmulateInstructionARM::EmulateVLDR (const uint32_t opcode, ARMEncoding encoding) +{ +#if 0 + if ConditionPassed() then + EncodingSpecificOperations(); CheckVFPEnabled(TRUE); NullCheckIfThumbEE(n); + base = if n == 15 then Align(PC,4) else R[n]; + address = if add then (base + imm32) else (base - imm32); + if single_reg then + S[d] = MemA[address,4]; + else + word1 = MemA[address,4]; word2 = MemA[address+4,4]; + // Combine the word-aligned words in the correct order for current endianness. + D[d] = if BigEndian() then word1:word2 else word2:word1; +#endif + + bool success = false; + + if (ConditionPassed (opcode)) + { + bool single_reg; + bool add; + uint32_t imm32; + uint32_t d; + uint32_t n; + + switch (encoding) + { + case eEncodingT1: + case eEncodingA1: + // single_reg = FALSE; add = (U == Ô1Õ); imm32 = ZeroExtend(imm8:Õ00Õ, 32); + single_reg = false; + add = BitIsSet (opcode, 23); + imm32 = Bits32 (opcode, 7, 0) << 2; + + // d = UInt(D:Vd); n = UInt(Rn); + d = (Bit32 (opcode, 22) << 4) | Bits32 (opcode, 15, 12); + n = Bits32 (opcode, 19, 16); + + break; + + case eEncodingT2: + case eEncodingA2: + // single_reg = TRUE; add = (U == Ô1Õ); imm32 = ZeroExtend(imm8:Õ00Õ, 32); + single_reg = true; + add = BitIsSet (opcode, 23); + imm32 = Bits32 (opcode, 7, 0) << 2; + + // d = UInt(Vd:D); n = UInt(Rn); + d = (Bits32 (opcode, 15, 12) << 1) | Bit32 (opcode, 22); + n = Bits32 (opcode, 19, 16); + + break; + + default: + return false; + } + RegisterInfo base_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, base_reg); + + uint32_t Rn = ReadCoreReg (n, &success); + if (!success) + return false; + + // base = if n == 15 then Align(PC,4) else R[n]; + uint32_t base; + if (n == 15) + base = AlignPC (Rn); + else + base = Rn; + + // address = if add then (base + imm32) else (base - imm32); + addr_t address; + if (add) + address = base + imm32; + else + address = base - imm32; + + const uint32_t addr_byte_size = GetAddressByteSize(); + uint32_t start_reg = single_reg ? dwarf_s0 : dwarf_d0; + + EmulateInstruction::Context context; + context.type = eContextRegisterLoad; + context.SetRegisterPlusOffset (base_reg, address - base); + + if (single_reg) + { + // S[d] = MemA[address,4]; + uint32_t data = MemARead (context, address, addr_byte_size, 0, &success); + if (!success) + return false; + + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, start_reg + d, data)) + return false; + } + else + { + // word1 = MemA[address,4]; word2 = MemA[address+4,4]; + uint32_t word1 = MemARead (context, address, addr_byte_size, 0, &success); + if (!success) + return false; + + context.SetRegisterPlusOffset (base_reg, (address + 4) - base); + uint32_t word2 = MemARead (context, address + 4, addr_byte_size, 0, &success); + if (!success) + return false; + // // Combine the word-aligned words in the correct order for current endianness. + // D[d] = if BigEndian() then word1:word2 else word2:word1; + uint64_t data64; + if (GetByteOrder() == eByteOrderBig) + { + data64 = word1; + data64 = (data64 << 32) | word2; + } + else + { + data64 = word2; + data64 = (data64 << 32) | word1; + } + + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, start_reg + d, data64)) + return false; + } + } + return true; +} + +// A8.6.400 VSTR +// This instruction stores a signle extension register to memory, using an address from an ARM core register, with an +// optional offset. +bool +EmulateInstructionARM::EmulateVSTR (const uint32_t opcode, ARMEncoding encoding) +{ +#if 0 + if ConditionPassed() then + EncodingSpecificOperations(); CheckVFPEnabled(TRUE); NullCheckIfThumbEE(n); + address = if add then (R[n] + imm32) else (R[n] - imm32); + if single_reg then + MemA[address,4] = S[d]; + else + // Store as two word-aligned words in the correct order for current endianness. + MemA[address,4] = if BigEndian() then D[d]<63:32> else D[d]<31:0>; + MemA[address+4,4] = if BigEndian() then D[d]<31:0> else D[d]<63:32>; +#endif + + bool success = false; + + if (ConditionPassed (opcode)) + { + bool single_reg; + bool add; + uint32_t imm32; + uint32_t d; + uint32_t n; + + switch (encoding) + { + case eEncodingT1: + case eEncodingA1: + // single_reg = FALSE; add = (U == Ô1Õ); imm32 = ZeroExtend(imm8:Õ00Õ, 32); + single_reg = false; + add = BitIsSet (opcode, 23); + imm32 = Bits32 (opcode, 7, 0) << 2; + + // d = UInt(D:Vd); n = UInt(Rn); + d = (Bit32 (opcode, 22) << 4) | Bits32 (opcode, 15, 12); + n = Bits32 (opcode, 19, 16); + + // if n == 15 && CurrentInstrSet() != InstrSet_ARM then UNPREDICTABLE; + if ((n == 15) && (CurrentInstrSet() != eModeARM)) + return false; + + break; + + case eEncodingT2: + case eEncodingA2: + // single_reg = TRUE; add = (U == Ô1Õ); imm32 = ZeroExtend(imm8:Õ00Õ, 32); + single_reg = true; + add = BitIsSet (opcode, 23); + imm32 = Bits32 (opcode, 7, 0) << 2; + + // d = UInt(Vd:D); n = UInt(Rn); + d = (Bits32 (opcode, 15, 12) << 1) | Bit32 (opcode, 22); + n = Bits32 (opcode, 19, 16); + + // if n == 15 && CurrentInstrSet() != InstrSet_ARM then UNPREDICTABLE; + if ((n == 15) && (CurrentInstrSet() != eModeARM)) + return false; + + break; + + default: + return false; + } + + RegisterInfo base_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, base_reg); + + uint32_t Rn = ReadCoreReg (n, &success); + if (!success) + return false; + + // address = if add then (R[n] + imm32) else (R[n] - imm32); + addr_t address; + if (add) + address = Rn + imm32; + else + address = Rn - imm32; + + const uint32_t addr_byte_size = GetAddressByteSize(); + uint32_t start_reg = single_reg ? dwarf_s0 : dwarf_d0; + + RegisterInfo data_reg; + GetRegisterInfo (eRegisterKindDWARF, start_reg + d, data_reg); + EmulateInstruction::Context context; + context.type = eContextRegisterStore; + context.SetRegisterToRegisterPlusOffset (data_reg, base_reg, address - Rn); + + if (single_reg) + { + // MemA[address,4] = S[d]; + uint32_t data = ReadRegisterUnsigned (eRegisterKindDWARF, start_reg + d, 0, &success); + if (!success) + return false; + + if (!MemAWrite (context, address, data, addr_byte_size)) + return false; + } + else + { + // // Store as two word-aligned words in the correct order for current endianness. + // MemA[address,4] = if BigEndian() then D[d]<63:32> else D[d]<31:0>; + // MemA[address+4,4] = if BigEndian() then D[d]<31:0> else D[d]<63:32>; + uint64_t data = ReadRegisterUnsigned (eRegisterKindDWARF, start_reg + d, 0, &success); + if (!success) + return false; + + if (GetByteOrder() == eByteOrderBig) + { + if (!MemAWrite (context, address, Bits64 (data, 63, 32), addr_byte_size)) + return false; + + context.SetRegisterToRegisterPlusOffset (data_reg, base_reg, (address + 4) - Rn); + if (!MemAWrite (context, address + 4, Bits64 (data, 31, 0), addr_byte_size)) + return false; + } + else + { + if (!MemAWrite (context, address, Bits64 (data, 31, 0), addr_byte_size)) + return false; + + context.SetRegisterToRegisterPlusOffset (data_reg, base_reg, (address + 4) - Rn); + if (!MemAWrite (context, address + 4, Bits64 (data, 63, 32), addr_byte_size)) + return false; + } + } + } + return true; +} + +// A8.6.307 VLDI1 (multiple single elements) +// This instruction loads elements from memory into one, two, three or four registers, without de-interleaving. Every +// element of each register is loaded. +bool +EmulateInstructionARM::EmulateVLD1Multiple (const uint32_t opcode, ARMEncoding encoding) +{ +#if 0 + if ConditionPassed() then + EncodingSpecificOperations(); CheckAdvSIMDEnabled(); NullCheckIfThumbEE(n); + address = R[n]; if (address MOD alignment) != 0 then GenerateAlignmentException(); + if wback then R[n] = R[n] + (if register_index then R[m] else 8*regs); + for r = 0 to regs-1 + for e = 0 to elements-1 + Elem[D[d+r],e,esize] = MemU[address,ebytes]; + address = address + ebytes; +#endif + + bool success = false; + + if (ConditionPassed (opcode)) + { + uint32_t regs; + uint32_t alignment; + uint32_t ebytes; + uint32_t esize; + uint32_t elements; + uint32_t d; + uint32_t n; + uint32_t m; + bool wback; + bool register_index; + + switch (encoding) + { + case eEncodingT1: + case eEncodingA1: + { + // case type of + // when Ô0111Õ + // regs = 1; if align<1> == Ô1Õ then UNDEFINED; + // when Ô1010Õ + // regs = 2; if align == Ô11Õ then UNDEFINED; + // when Ô0110Õ + // regs = 3; if align<1> == Ô1Õ then UNDEFINED; + // when Ô0010Õ + // regs = 4; + // otherwise + // SEE ÒRelated encodingsÓ; + uint32_t type = Bits32 (opcode, 11, 8); + uint32_t align = Bits32 (opcode, 5, 4); + if (type == 7) // '0111' + { + regs = 1; + if (BitIsSet (align, 1)) + return false; + } + else if (type == 10) // '1010' + { + regs = 2; + if (align == 3) + return false; + + } + else if (type == 6) // '0110' + { + regs = 3; + if (BitIsSet (align, 1)) + return false; + } + else if (type == 2) // '0010' + { + regs = 4; + } + else + return false; + + // alignment = if align == Ô00Õ then 1 else 4 << UInt(align); + if (align == 0) + alignment = 1; + else + alignment = 4 << align; + + // ebytes = 1 << UInt(size); esize = 8 * ebytes; elements = 8 DIV ebytes; + ebytes = 1 << Bits32 (opcode, 7, 6); + esize = 8 * ebytes; + elements = 8 / ebytes; + + // d = UInt(D:Vd); n = UInt(Rn); m = UInt(Rm); + d = (Bit32 (opcode, 22) << 4) | Bits32 (opcode, 15, 12); + n = Bits32 (opcode, 19, 15); + m = Bits32 (opcode, 3, 0); + + // wback = (m != 15); register_index = (m != 15 && m != 13); + wback = (m != 15); + register_index = ((m != 15) && (m != 13)); + + // if d+regs > 32 then UNPREDICTABLE; + if ((d + regs) > 32) + return false; + } + break; + + default: + return false; + } + + RegisterInfo base_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, base_reg); + + uint32_t Rn = ReadCoreReg (n, &success); + if (!success) + return false; + + // address = R[n]; if (address MOD alignment) != 0 then GenerateAlignmentException(); + addr_t address = Rn; + if ((address % alignment) != 0) + return false; + + EmulateInstruction::Context context; + // if wback then R[n] = R[n] + (if register_index then R[m] else 8*regs); + if (wback) + { + uint32_t Rm = ReadCoreReg (m, &success); + if (!success) + return false; + + uint32_t offset; + if (register_index) + offset = Rm; + else + offset = 8 * regs; + + uint32_t value = Rn + offset; + context.type = eContextAdjustBaseRegister; + context.SetRegisterPlusOffset (base_reg, offset); + + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + n, value)) + return false; + + } + + // for r = 0 to regs-1 + for (uint32_t r = 0; r < regs; ++r) + { + // for e = 0 to elements-1 + uint64_t assembled_data = 0; + for (uint32_t e = 0; e < elements; ++e) + { + // Elem[D[d+r],e,esize] = MemU[address,ebytes]; + context.type = eContextRegisterLoad; + context.SetRegisterPlusOffset (base_reg, address - Rn); + uint64_t data = MemURead (context, address, ebytes, 0, &success); + if (!success) + return false; + + assembled_data = (data << (e * esize)) | assembled_data; // New data goes to the left of existing data + + // address = address + ebytes; + address = address + ebytes; + } + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_d0 + d + r, assembled_data)) + return false; + } + } + return true; +} + +// A8.6.308 VLD1 (single element to one lane) +// +bool +EmulateInstructionARM::EmulateVLD1Single (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + if ConditionPassed() then + EncodingSpecificOperations(); CheckAdvSIMDEnabled(); NullCheckIfThumbEE(n); + address = R[n]; if (address MOD alignment) != 0 then GenerateAlignmentException(); + if wback then R[n] = R[n] + (if register_index then R[m] else ebytes); + Elem[D[d],index,esize] = MemU[address,ebytes]; +#endif + + bool success = false; + + if (ConditionPassed (opcode)) + { + uint32_t ebytes; + uint32_t esize; + uint32_t index; + uint32_t alignment; + uint32_t d; + uint32_t n; + uint32_t m; + bool wback; + bool register_index; + + switch (encoding) + { + case eEncodingT1: + case eEncodingA1: + { + uint32_t size = Bits32 (opcode, 11, 10); + uint32_t index_align = Bits32 (opcode, 7, 4); + // if size == Ô11Õ then SEE VLD1 (single element to all lanes); + if (size == 3) + return EmulateVLD1SingleAll (opcode, encoding); + // case size of + if (size == 0) // when '00' + { + // if index_align<0> != Ô0Õ then UNDEFINED; + if (BitIsClear (index_align, 0)) + return false; + + // ebytes = 1; esize = 8; index = UInt(index_align<3:1>); alignment = 1; + ebytes = 1; + esize = 8; + index = Bits32 (index_align, 3, 1); + alignment = 1; + } + else if (size == 1) // when Ô01Õ + { + // if index_align<1> != Ô0Õ then UNDEFINED; + if (BitIsClear (index_align, 1)) + return false; + + // ebytes = 2; esize = 16; index = UInt(index_align<3:2>); + ebytes = 2; + esize = 16; + index = Bits32 (index_align, 3, 2); + + // alignment = if index_align<0> == Ô0Õ then 1 else 2; + if (BitIsClear (index_align, 0)) + alignment = 1; + else + alignment = 2; + } + else if (size == 2) // when Ô10Õ + { + // if index_align<2> != Ô0Õ then UNDEFINED; + if (BitIsClear (index_align, 2)) + return false; + + // if index_align<1:0> != Ô00Õ && index_align<1:0> != Ô11Õ then UNDEFINED; + if ((Bits32 (index_align, 1, 0) != 0) && (Bits32 (index_align, 1, 0) != 3)) + return false; + + // ebytes = 4; esize = 32; index = UInt(index_align<3>); + ebytes = 4; + esize = 32; + index = Bit32 (index_align, 3); + + // alignment = if index_align<1:0> == Ô00Õ then 1 else 4; + if (Bits32 (index_align, 1, 0) == 0) + alignment = 1; + else + alignment = 4; + } + else + { + return false; + } + // d = UInt(D:Vd); n = UInt(Rn); m = UInt(Rm); + d = (Bit32 (opcode, 22) << 4) | Bits32 (opcode, 15, 12); + n = Bits32 (opcode, 19, 16); + m = Bits32 (opcode, 3, 0); + + // wback = (m != 15); register_index = (m != 15 && m != 13); if n == 15 then UNPREDICTABLE; + wback = (m != 15); + register_index = ((m != 15) && (m != 13)); + + if (n == 15) + return false; + + } + break; + + default: + return false; + } + + RegisterInfo base_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, base_reg); + + uint32_t Rn = ReadCoreReg (n, &success); + if (!success) + return false; + + // address = R[n]; if (address MOD alignment) != 0 then GenerateAlignmentException(); + addr_t address = Rn; + if ((address % alignment) != 0) + return false; + + EmulateInstruction::Context context; + // if wback then R[n] = R[n] + (if register_index then R[m] else ebytes); + if (wback) + { + uint32_t Rm = ReadCoreReg (m, &success); + if (!success) + return false; + + uint32_t offset; + if (register_index) + offset = Rm; + else + offset = ebytes; + + uint32_t value = Rn + offset; + + context.type = eContextAdjustBaseRegister; + context.SetRegisterPlusOffset (base_reg, offset); + + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + n, value)) + return false; + } + + // Elem[D[d],index,esize] = MemU[address,ebytes]; + uint32_t element = MemURead (context, address, esize, 0, &success); + if (!success) + return false; + + element = element << (index * esize); + + uint64_t reg_data = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_d0 + d, 0, &success); + if (!success) + return false; + + uint64_t all_ones = -1; + uint64_t mask = all_ones << ((index+1) * esize); // mask is all 1's to left of where 'element' goes, & all 0's + // at element & to the right of element. + if (index > 0) + mask = mask | Bits64 (all_ones, (index * esize) - 1, 0); // add 1's to the right of where 'element' goes. + // now mask should be 0's where element goes & 1's + // everywhere else. + + uint64_t masked_reg = reg_data & mask; // Take original reg value & zero out 'element' bits + reg_data = masked_reg & element; // Put 'element' into those bits in reg_data. + + context.type = eContextRegisterLoad; + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + d, reg_data)) + return false; + } + return true; +} + +// A8.6.391 VST1 (multiple single elements) +// Vector Store (multiple single elements) stores elements to memory from one, two, three, or four regsiters, without +// interleaving. Every element of each register is stored. +bool +EmulateInstructionARM::EmulateVST1Multiple (const uint32_t opcode, ARMEncoding encoding) +{ +#if 0 + if ConditionPassed() then + EncodingSpecificOperations(); CheckAdvSIMDEnabled(); NullCheckIfThumbEE(n); + address = R[n]; if (address MOD alignment) != 0 then GenerateAlignmentException(); + if wback then R[n] = R[n] + (if register_index then R[m] else 8*regs); + for r = 0 to regs-1 + for e = 0 to elements-1 + MemU[address,ebytes] = Elem[D[d+r],e,esize]; + address = address + ebytes; +#endif + + bool success = false; + + if (ConditionPassed (opcode)) + { + uint32_t regs; + uint32_t alignment; + uint32_t ebytes; + uint32_t esize; + uint32_t elements; + uint32_t d; + uint32_t n; + uint32_t m; + bool wback; + bool register_index; + + switch (encoding) + { + case eEncodingT1: + case eEncodingA1: + { + uint32_t type = Bits32 (opcode, 11, 8); + uint32_t align = Bits32 (opcode, 5, 4); + + // case type of + if (type == 7) // when Ô0111Õ + { + // regs = 1; if align<1> == Ô1Õ then UNDEFINED; + regs = 1; + if (BitIsSet (align, 1)) + return false; + } + else if (type == 10) // when Ô1010Õ + { + // regs = 2; if align == Ô11Õ then UNDEFINED; + regs = 2; + if (align == 3) + return false; + } + else if (type == 6) // when Ô0110Õ + { + // regs = 3; if align<1> == Ô1Õ then UNDEFINED; + regs = 3; + if (BitIsSet (align, 1)) + return false; + } + else if (type == 2) // when Ô0010Õ + // regs = 4; + regs = 4; + else // otherwise + // SEE ÒRelated encodingsÓ; + return false; + + // alignment = if align == Ô00Õ then 1 else 4 << UInt(align); + if (align == 0) + alignment = 1; + else + alignment = 4 << align; + + // ebytes = 1 << UInt(size); esize = 8 * ebytes; elements = 8 DIV ebytes; + ebytes = 1 << Bits32 (opcode,7, 6); + esize = 8 * ebytes; + elements = 8 / ebytes; + + // d = UInt(D:Vd); n = UInt(Rn); m = UInt(Rm); + d = (Bit32 (opcode, 22) << 4) | Bits32 (opcode, 15, 12); + n = Bits32 (opcode, 19, 16); + m = Bits32 (opcode, 3, 0); + + // wback = (m != 15); register_index = (m != 15 && m != 13); + wback = (m != 15); + register_index = ((m != 15) && (m != 13)); + + // if d+regs > 32 then UNPREDICTABLE; if n == 15 then UNPREDICTABLE; + if ((d + regs) > 32) + return false; + + if (n == 15) + return false; + + } + break; + + default: + return false; + } + + RegisterInfo base_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, base_reg); + + uint32_t Rn = ReadCoreReg (n, &success); + if (!success) + return false; + + // address = R[n]; if (address MOD alignment) != 0 then GenerateAlignmentException(); + addr_t address = Rn; + if ((address % alignment) != 0) + return false; + + EmulateInstruction::Context context; + // if wback then R[n] = R[n] + (if register_index then R[m] else 8*regs); + if (wback) + { + uint32_t Rm = ReadCoreReg (m, &success); + if (!success) + return false; + + uint32_t offset; + if (register_index) + offset = Rm; + else + offset = 8 * regs; + + context.type = eContextAdjustBaseRegister; + context.SetRegisterPlusOffset (base_reg, offset); + + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + n, Rn + offset)) + return false; + } + + RegisterInfo data_reg; + context.type = eContextRegisterStore; + // for r = 0 to regs-1 + for (uint32_t r = 0; r < regs; ++r) + { + GetRegisterInfo (eRegisterKindDWARF, dwarf_d0 + d + r, data_reg); + uint64_t register_data = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_d0 + d + r, 0, &success); + if (!success) + return false; + + // for e = 0 to elements-1 + for (uint32_t e = 0; e < elements; ++e) + { + // MemU[address,ebytes] = Elem[D[d+r],e,esize]; + uint64_t word = Bits64 (register_data, ((e + 1) * esize) - 1, e * esize); + + context.SetRegisterToRegisterPlusOffset (data_reg, base_reg, address - Rn); + if (!MemUWrite (context, address, word, ebytes)) + return false; + + // address = address + ebytes; + address = address + ebytes; + } + } + } + return true; +} + +// A8.6.392 VST1 (single element from one lane) +// This instruction stores one element to memory from one element of a register. +bool +EmulateInstructionARM::EmulateVST1Single (const uint32_t opcode, ARMEncoding encoding) +{ +#if 0 + if ConditionPassed() then + EncodingSpecificOperations(); CheckAdvSIMDEnabled(); NullCheckIfThumbEE(n); + address = R[n]; if (address MOD alignment) != 0 then GenerateAlignmentException(); + if wback then R[n] = R[n] + (if register_index then R[m] else ebytes); + MemU[address,ebytes] = Elem[D[d],index,esize]; +#endif + + bool success = false; + + if (ConditionPassed (opcode)) + { + uint32_t ebytes; + uint32_t esize; + uint32_t index; + uint32_t alignment; + uint32_t d; + uint32_t n; + uint32_t m; + bool wback; + bool register_index; + + switch (encoding) + { + case eEncodingT1: + case eEncodingA1: + { + uint32_t size = Bits32 (opcode, 11, 10); + uint32_t index_align = Bits32 (opcode, 7, 4); + + // if size == Ô11Õ then UNDEFINED; + if (size == 3) + return false; + + // case size of + if (size == 0) // when Ô00Õ + { + // if index_align<0> != Ô0Õ then UNDEFINED; + if (BitIsClear (index_align, 0)) + return false; + // ebytes = 1; esize = 8; index = UInt(index_align<3:1>); alignment = 1; + ebytes = 1; + esize = 8; + index = Bits32 (index_align, 3, 1); + alignment = 1; + } + else if (size == 1) // when Ô01Õ + { + // if index_align<1> != Ô0Õ then UNDEFINED; + if (BitIsClear (index_align, 1)) + return false; + + // ebytes = 2; esize = 16; index = UInt(index_align<3:2>); + ebytes = 2; + esize = 16; + index = Bits32 (index_align, 3, 2); + + // alignment = if index_align<0> == Ô0Õ then 1 else 2; + if (BitIsClear (index_align, 0)) + alignment = 1; + else + alignment = 2; + } + else if (size == 2) // when Ô10Õ + { + // if index_align<2> != Ô0Õ then UNDEFINED; + if (BitIsClear (index_align, 2)) + return false; + + // if index_align<1:0> != Ô00Õ && index_align<1:0> != Ô11Õ then UNDEFINED; + if ((Bits32 (index_align, 1, 0) != 0) && (Bits32 (index_align, 1, 0) != 3)) + return false; + + // ebytes = 4; esize = 32; index = UInt(index_align<3>); + ebytes = 4; + esize = 32; + index = Bit32 (index_align, 3); + + // alignment = if index_align<1:0> == Ô00Õ then 1 else 4; + if (Bits32 (index_align, 1, 0) == 0) + alignment = 1; + else + alignment = 4; + } + else + { + return false; + } + // d = UInt(D:Vd); n = UInt(Rn); m = UInt(Rm); + d = (Bit32 (opcode, 22) << 4) | Bits32 (opcode, 15, 12); + n = Bits32 (opcode, 19, 16); + m = Bits32 (opcode, 3, 0); + + // wback = (m != 15); register_index = (m != 15 && m != 13); if n == 15 then UNPREDICTABLE; + wback = (m != 15); + register_index = ((m != 15) && (m != 13)); + + if (n == 15) + return false; + } + break; + + default: + return false; + } + + RegisterInfo base_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, base_reg); + + uint32_t Rn = ReadCoreReg (n, &success); + if (!success) + return false; + + // address = R[n]; if (address MOD alignment) != 0 then GenerateAlignmentException(); + addr_t address = Rn; + if ((address % alignment) != 0) + return false; + + EmulateInstruction::Context context; + // if wback then R[n] = R[n] + (if register_index then R[m] else ebytes); + if (wback) + { + uint32_t Rm = ReadCoreReg (m, &success); + if (!success) + return false; + + uint32_t offset; + if (register_index) + offset = Rm; + else + offset = ebytes; + + context.type = eContextAdjustBaseRegister; + context.SetRegisterPlusOffset (base_reg, offset); + + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + n, Rn + offset)) + return false; + } + + // MemU[address,ebytes] = Elem[D[d],index,esize]; + uint64_t register_data = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_d0 + d, 0, &success); + if (!success) + return false; + + uint64_t word = Bits64 (register_data, ((index + 1) * esize) - 1, index * esize); + + RegisterInfo data_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_d0 + d, data_reg); + context.type = eContextRegisterStore; + context.SetRegisterToRegisterPlusOffset (data_reg, base_reg, address - Rn); + + if (!MemUWrite (context, address, word, ebytes)) + return false; + } + return true; +} + +// A8.6.309 VLD1 (single element to all lanes) +// This instruction loads one element from memory into every element of one or two vectors. +bool +EmulateInstructionARM::EmulateVLD1SingleAll (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + if ConditionPassed() then + EncodingSpecificOperations(); CheckAdvSIMDEnabled(); NullCheckIfThumbEE(n); + address = R[n]; if (address MOD alignment) != 0 then GenerateAlignmentException(); + if wback then R[n] = R[n] + (if register_index then R[m] else ebytes); + replicated_element = Replicate(MemU[address,ebytes], elements); + for r = 0 to regs-1 + D[d+r] = replicated_element; +#endif + + bool success = false; + + if (ConditionPassed (opcode)) + { + uint32_t ebytes; + uint32_t elements; + uint32_t regs; + uint32_t alignment; + uint32_t d; + uint32_t n; + uint32_t m; + bool wback; + bool register_index; + + switch (encoding) + { + case eEncodingT1: + case eEncodingA1: + { + //if size == Ô11Õ || (size == Ô00Õ && a == Ô1Õ) then UNDEFINED; + uint32_t size = Bits32 (opcode, 7, 6); + if ((size == 3) || ((size == 0) && BitIsSet (opcode, 4))) + return false; + + //ebytes = 1 << UInt(size); elements = 8 DIV ebytes; regs = if T == Ô0Õ then 1 else 2; + ebytes = 1 << size; + elements = 8 / ebytes; + if (BitIsClear (opcode, 5)) + regs = 1; + else + regs = 2; + + //alignment = if a == Ô0Õ then 1 else ebytes; + if (BitIsClear (opcode, 4)) + alignment = 1; + else + alignment = ebytes; + + //d = UInt(D:Vd); n = UInt(Rn); m = UInt(Rm); + d = (Bit32 (opcode, 22) << 4) | Bits32 (opcode, 15, 12); + n = Bits32 (opcode, 19, 16); + m = Bits32 (opcode, 3, 0); + + //wback = (m != 15); register_index = (m != 15 && m != 13); + wback = (m != 15); + register_index = ((m != 15) && (m != 13)); + + //if d+regs > 32 then UNPREDICTABLE; if n == 15 then UNPREDICTABLE; + if ((d + regs) > 32) + return false; + + if (n == 15) + return false; + } + break; + + default: + return false; + } + + RegisterInfo base_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, base_reg); + + uint32_t Rn = ReadCoreReg (n, &success); + if (!success) + return false; + + // address = R[n]; if (address MOD alignment) != 0 then GenerateAlignmentException(); + addr_t address = Rn; + if ((address % alignment) != 0) + return false; + + EmulateInstruction::Context context; + // if wback then R[n] = R[n] + (if register_index then R[m] else ebytes); + if (wback) + { + uint32_t Rm = ReadCoreReg (m, &success); + if (!success) + return false; + + uint32_t offset; + if (register_index) + offset = Rm; + else + offset = ebytes; + + context.type = eContextAdjustBaseRegister; + context.SetRegisterPlusOffset (base_reg, offset); + + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + n, Rn + offset)) + return false; + } + + // replicated_element = Replicate(MemU[address,ebytes], elements); + + context.type = eContextRegisterLoad; + uint64_t word = MemURead (context, address, ebytes, 0, &success); + if (!success) + return false; + + uint64_t replicated_element = 0; + uint32_t esize = ebytes * 8; + for (uint32_t e = 0; e < elements; ++e) + replicated_element = (replicated_element << esize) | Bits64 (word, esize - 1, 0); + + // for r = 0 to regs-1 + for (uint32_t r = 0; r < regs; ++r) + { + // D[d+r] = replicated_element; + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_d0 + d + r, replicated_element)) + return false; + } + } + return true; +} + +// B6.2.13 SUBS PC, LR and related instructions +//The SUBS PC, LR, #" }, + { 0x0fff0fff, 0x052d0004, ARMvAll, eEncodingA2, No_VFP, eSize32, &EmulateInstructionARM::EmulatePUSH, "push " }, + + // set r7 to point to a stack offset + { 0x0ffff000, 0x028d7000, ARMvAll, eEncodingA1, No_VFP, eSize32, &EmulateInstructionARM::EmulateADDRdSPImm, "add r7, sp, #" }, + { 0x0ffff000, 0x024c7000, ARMvAll, eEncodingA1, No_VFP, eSize32, &EmulateInstructionARM::EmulateSUBR7IPImm, "sub r7, ip, #"}, + // copy the stack pointer to ip + { 0x0fffffff, 0x01a0c00d, ARMvAll, eEncodingA1, No_VFP, eSize32, &EmulateInstructionARM::EmulateMOVRdSP, "mov ip, sp" }, + { 0x0ffff000, 0x028dc000, ARMvAll, eEncodingA1, No_VFP, eSize32, &EmulateInstructionARM::EmulateADDRdSPImm, "add ip, sp, #" }, + { 0x0ffff000, 0x024dc000, ARMvAll, eEncodingA1, No_VFP, eSize32, &EmulateInstructionARM::EmulateSUBIPSPImm, "sub ip, sp, #"}, + + // adjust the stack pointer + { 0x0ffff000, 0x024dd000, ARMvAll, eEncodingA1, No_VFP, eSize32, &EmulateInstructionARM::EmulateSUBSPImm, "sub sp, sp, #"}, + { 0x0fef0010, 0x004d0000, ARMvAll, eEncodingA1, No_VFP, eSize32, &EmulateInstructionARM::EmulateSUBSPReg, "sub{s} , sp, {,}" }, + + // push one register + // if Rn == '1101' && imm12 == '000000000100' then SEE PUSH; + { 0x0e5f0000, 0x040d0000, ARMvAll, eEncodingA1, No_VFP, eSize32, &EmulateInstructionARM::EmulateSTRRtSP, "str Rt, [sp, #-imm12]!" }, + + // vector push consecutive extension register(s) + { 0x0fbf0f00, 0x0d2d0b00, ARMV6T2_ABOVE, eEncodingA1, No_VFP, eSize32, &EmulateInstructionARM::EmulateVPUSH, "vpush.64 "}, + { 0x0fbf0f00, 0x0d2d0a00, ARMV6T2_ABOVE, eEncodingA2, No_VFP, eSize32, &EmulateInstructionARM::EmulateVPUSH, "vpush.32 "}, + + //---------------------------------------------------------------------- + // Epilogue instructions + //---------------------------------------------------------------------- + + { 0x0fff0000, 0x08bd0000, ARMvAll, eEncodingA1, No_VFP, eSize32, &EmulateInstructionARM::EmulatePOP, "pop "}, + { 0x0fff0fff, 0x049d0004, ARMvAll, eEncodingA2, No_VFP, eSize32, &EmulateInstructionARM::EmulatePOP, "pop "}, + { 0x0fbf0f00, 0x0cbd0b00, ARMV6T2_ABOVE, eEncodingA1, No_VFP, eSize32, &EmulateInstructionARM::EmulateVPOP, "vpop.64 "}, + { 0x0fbf0f00, 0x0cbd0a00, ARMV6T2_ABOVE, eEncodingA2, No_VFP, eSize32, &EmulateInstructionARM::EmulateVPOP, "vpop.32 "}, + + //---------------------------------------------------------------------- + // Supervisor Call (previously Software Interrupt) + //---------------------------------------------------------------------- + { 0x0f000000, 0x0f000000, ARMvAll, eEncodingA1, No_VFP, eSize32, &EmulateInstructionARM::EmulateSVC, "svc #imm24"}, + + //---------------------------------------------------------------------- + // Branch instructions + //---------------------------------------------------------------------- + { 0x0f000000, 0x0a000000, ARMvAll, eEncodingA1, No_VFP, eSize32, &EmulateInstructionARM::EmulateB, "b #imm24"}, + // To resolve ambiguity, "blx
-Prev  Up  Next
-dnssec-signzone  Home  named-checkzone $(&9`0`X +MIO`0IO)#Z)($6XSY"ZD6>A>RR?X6VQK"R?T6_!?2%"GB%"CB5!325!6M`F6, +M_%)$0%)$0:T"9<3\O,HM"AWP``#R`ERL?U)#XU)"7$9*_P``D@/I#(B7N`2B +M!%;,FE)#^E)$0\8!````/^B +M%"BR%"FR5!6@H4&B5!0&5?\`PA0ITA0HTE04P,%!PE051F__``#B%"GR%"C@ +MX4'P\4'R5!3B5!6&2O^"%"F2%"B25!2`@4&"5!4&1O\`HA0HLA0ILE05H*%! +MHE041J/_``#"%"G2%"C`P4'0T4'25!3"5!6&G?_B%"CR%"GR5!7@X4'B5!1& +M<_\`@A0IDA0HDE04@(%!@E051I3_``"B%"FR%"B@H4&PL4&R5!2B5!7&:/_" +M%"G2%"C25!3`P4'"5!5&9/\`X@),``#BF(!"F`#BF4!"F`#BF,!"F`#BF0!"F`#,1@%414"(@@$0!0#,@ +M,"(@'?```#9A`&%)`(&[`7(FF]!#$8!W$$>W5W"#\`P#]L@"QB<`1G2(J]6'0'R8K"28K+B +MHH""H`6"8J_B8K%\]9*@B)"3@IJ2LFG=1AD`N`*R*R>R9(*R*:RPT`0':PUP +M^Q#R::S`(`#B*:SI0>(D0L`@`,#^(/)D0L`@`((D0L`@`&)D5\`@`/(D5\`@ +M`.)D0L`@`((D0H)A!,`@`(Q]LFFLP"``@BFLLBHWL*L@LBL`)3/ITB2!PB(0 +MUPP#?/(=\/$Y`/(OP7S^\/!D\%Z3+04=\``,"8%E`0P&#"QVJ`;2)$,;F9Q] +MXBHO\"``S-[":B_R:C!B:C*"HI.":C%\]7(B$7)DC.(BEG(BEY$W`.#>(.#G +M@G/]"./]$-)D4](IK'*O_M#@!`=M#G"-$()IK,`@`((IK()A`,%?`((D0L`@ +M`,#X(/)D0L`@`/(D0L`@`+)D5\`@`/(D5\`@`()D0L`@`/(D0OD!P"``C)[2 +M::S`(`""*:R)`>%E`0P-=JX'\B17&]TF'QF"*B_<&&)J,M*BK@Q.#"_R:B_B +M:C#2:C%\]0SH`#BFT!"F##^V?0*&*0"V/1'BS?T6C@OV;0+V307RS?H6CQ/B +MH(C@XX*"H`+@XH"";MVR2H72*:S0\`0';0YPC1""::S`(`""*:R"80&")$+` +M(`#`."`R9$+`(``R)$+`(`"R9%?`(``R)%?`(`""9$+`(``R)$(Y$<`@`(R? +MTFFLP"``@BFLB1'2+MZR*C;:N[)D3N%E`0P+=JX&\B1#&[N,?X(J+Q:H"5*O +M_^(D0](D0[(D0W:`$[(IY[DQB#&`@!2),?@Q\L_]%M_=1OG_`.*@B.#C@NKB +M\F[=LDJ%LBFLL-`$!VL-<(L0@FFLP"``\BFL^2&")$+`(`#`^"#R9$+`(`#R +M)$+`(``,/_)D5\`@`/(D5\`@`()D0L`@`/(D0ODAP"``C*VR::S`(`""*:R" +M80+2+MZR*C;:N[)D3D;4_P``8FHRXJ+D#$\,*()J+_)J,.)J,8;3_P``TJ"( +MT-."VM*R;=U&RO\``#9A`)T"_0.M`@P+,,!$P.,1TJ.XVM+I$@ +M%D+]6`&-#``%0':`%U@'&T104)%04`122%!B*99RQQ`;B&>TLD;X_QWP`(@1 +M4BK>,BF6(BFW+`1`0V-:(BJ(*`@6Q`1M!"DA0%`4(J,LFDLJ1"@A=I4*(%`$ +M4D10("%!&T1@,D%VDR4@,`0@44$@800@<@0@(P1+1#)$3&)$35`Q07)$3B)$ +M3S`Q03`A03(IEB@8+`9,!$!#8T>V2V+$X%*C+)!+@%!$@&!0%':5"B!0!%)$ +M<"`A01M$8#)!=I,E(#`$(%%!(&$$('($(",$2T0R1&QB1&U0,4%R1&XB1&\P +M,4$P(4$R*98H*$P&0J!@0$-C1[9*8L3`4J,LFDM01(!@4!1VE0H@4`121)`@ +M(4$;1&`R07:3)2`P!"!102!A!"!R!"`C!$M$,D2,8D2-4#%!EQF5&)87)$8X(E$')D'')D'7)D'G)D'Y(C@9<( +M9I(FK)#@!`=I$WSH@(D0@F:LP"``@B:L@F$`P"``2:%(!2F1(;T!@B0G:;$@ +MB"""8X+`(`!B(X+`(`!HL4(D)T)C@DBAP"``(B."P"``*)&,SI)FK,`@`((F +MK(D!P"``#"X`/J:0$*:190$]\':`#P`\IN`0IND1B!$+F),18)%9A!#!^'.0$,#PP8#`GPF(.<"9@QZ$$,&(F!D.[`E[X# +M#`F9@18*$I($8H;:_Y($8@;9_P"R80)0I2!E-P"R(0+R(X'B)1`,3-'C`/>. +M`A;*$G)$8@P)AL__N2&M!274^Y8*$*A1B"$\2PPY=5YY7GUK07E^/RX(?(E$.(C +M@0Q,T>,`]PX"!B$`%OH2D@1BQK;_LF$"4*4@I5,`LB$"DB.!@B40#$S1XP"7 +MB`(6Z@H,&0P:HD1B!JW_``"M!0P>#!RX<8@A#&_2"YRR"YKR2`)YI7FU><5Y +MU7GE>?7"15K0WI/215NR1#)R1#HE1/VX(>(E$)(C@0Q,T>,`YXD(%BH'D@1B +MAIG_D@1B!IC_`)($8D:6_P``D@1B1I3_```6;P22!&*&D?^="(A!#!J7.`*& +ML?\,"D:P_P``H@1M5EH`L@1D%KL)#`(,W`P=TD1HPD1I'?`,&0P>XD1BQH/_ +M#"D,+_)$8D:!_XB!%L@1D@1BAG[_`*T%N''B!$7R!$?R1#SB1#W""V72"V32 +M1#["1#^R"YHE[_VM!67S^JT%)73[N"$,3-'C`%:Z#9($8H+)^Q9HW:+)_E9* +MVT9S_[AQ.&)S*D@1BQF+_1"BR?P6&A`+Z19>$?+)_1;_ +M$.($88+.^Q:8$69N"@NI%NH.XLG]%HX.K05EMOOX.0?SJN'&2!5JBH`&""YFR +M"YJB1#I0I2"0N(/E)?VX(?(C@>(E$`Q,T>,`]XX55BH!N2&M!>5=^ZT%Y5[[ +MN"$,3-'C`)($8D8;_P``D@1B1AG_``""!,B`AP06^.\,*0P:HD1EDD1BD@1B +MAA+_X@2X=^[I\@3(\/<$%@_N1O?_`&8IV4:[_[AQ#$BR"YP,&@P)L)J#DD5; +M@D1B!K__`*T%#"S2H`-R15MR1#QR1#UR1#YR1#_21&+"1#3"1#6EU?JM!26, +M^[@A#$S1XP`&W?\`-D$`#`LRHARBIRR@HH`P,H"R4SZR4S^R4T"RH*CEH`^R +MH@(`.Z;P$*:2HP(,/L+2!_),+.)#@@`YIM`0IM),+0`[IJ`0IJ),+@`YIH`0 +MIO*E`H),+P`_IE`0IK*A`E),,``[ID`0IN&_`4),,0`^IJ`0I@QT#&4L"`PM +MT*J0HE,^HEP9I[@"1EL`@B,HS-BB8RM"8RA"8RF2HFN28RI\_0`^IJ`0IH@" +M#"F0JI""*`NB4S^B7!JGN!;2(RA6[0!28RA"8RFB8ROBHG_B8RI\_0`[IJ`0 +MIJ),-@`[II`0II),-P`[IH`0IH),.``[IN`0IN),.0`[IJ`0IJ),.@`[II`0 +MII),.P`[IH`0IN*D`J'``8),/!9X"8&8`0`XII`0IAN9DEP?`#BFD!"F&YF2 +M7"``.Z:`$*:"3$*,`@DQ&%A@+`#NF@!"F@DQ'%@@+`#JF@!"F@EPEBXB`@T&"4T`` +M.Z:`$*:"3$R]&((BKU:(Z:)BLE)BKT)BL)*B +M<9)BL8:A_P`,#8:@_X*@>()30$;8_P```#FF0!"F@<$!BH2""`!"3$C@B!&" +M4T``/J9`$*9"3$D&S_\`-D$`30*"H0(`.*;@$*9BT@?B1M0`.*;0$*;21M4` +M.*;`$*;"1M8`.*:P$*:R1M<`.*:@$*:B1M@`.*:0$*:21MD`.*9P$*:2H@)R +M1MH`.:8P$*9RT@(R1MLR1Y-F,P0,'_)'DP`XIK`0IK)&W+)'D0`XIJ`0IJ)& +MW:)'D@`YICWP4!"F,J=L4D;>.C0B!E%!XP!21Y!VD@H`-*8@$*8;,R)#?P`X +MIL`0IE&_`<)&WQ8<"P`UIB`0I@QY#&HL#0PC,"*0(E=&(E9P)SUSLB\",(G+R*O_Q;\"=(&VHR=`#BF/?#@ +M$*;B1N0`.*;P$*8,B4*C`O)&Y1;_!0`TIC`0IIM34D>4,D;F`#BFH!"FHD;G +M%LH$`#2FL!"FF\O"1Y6R1N@=\`#2%TPGO1?B)R]67O@B9S*B9R^29S#RH\SR +M9S'&W/\,`@;<_P`R%TPB%TTB5T#PPU#`RBH-!RH(BR +MH:RZLGIRJJ*I`Y(B3))C/L)C/5)C.8(B."*A@(E#*B/"0LZR8Y2RH`B2!Z"2 +M0M2"!Z&"0M6E$NBM`R)A`:5Z``PY#`P,+2T#4L,80&:"LJ"`NK/"2Y322Y8, +M'N)+E9)+DI)+D('%`6!FD$*@]$I#D&81@F=4K09`M"#"H`'2H0"EL@*B8DZ+ +M(E>2Z:'&`;T$PJ`!TJ$`9;$"O018$0P)G51WP```V00"X +M(B +M(4ND^B3B8B+88\*@V+*@X/O=T-1!TF0WB'.ZL^@$^XB`A$&"9#CX8_)N"MAS +MTFX+)4K`T;\_P"2H22:DS*B*CHTHD/K2B"8"B0]J""7J"4VSR"7SR0]?B*2'B:Q[2*2+2:Q_"*2/":QRB*22B +M:QUB0]Z8!289!*("@(SZ'?``#`J&W_\,"[)#WD;Y_P"M!*7E`+(#'J@$L+`T +MLD7YN*JHNON[L,3T^ZJPM$&@I$&B4R/`JH*R4R*@H/2B4R'"!"%Z5`=L(0P, +M#`VRH/B@JJ#@JA&PM("ECP*R$R&B94&PNZ#@NQ$E,0^RH)"AS0'"$R/2$R*J +MI,#,$<#=$=)4#,)4#64O#Z(3(;8Z!`N:1@``#!F0H/2<"@P)&YF@H4%6>O^2 +M18UB0H`=\`P+LD6-8D*`'?``-J$`,3<`'1`4!5$>)J,I!5(`SIL%4@4FHM4FHLDFHT##NR:B:1T@&2:C%2 +M*BM29,)1:`"R*BVR94*R*BRY);(J)K)D0K+4!/)D1/)D1?)E6))D@.)DP`SI +MX5X`B0N294"R*B?@NQ"R9(*2)(*,K<)CK,`@`)(CK,`@`**A7*JBHF$(I07H +MHB<[D=,!TJ"HVM>"'>3"'>.:E[(I?PO,"XBZJJ)DQ9(I?_(G/$"($<#,$9K_ +M\F5'\B29,KB9,O-"`R)=JDQD@A@BXB29,R2"%F29,R2"%J29,R2"%N2 +M9,R2"%R29,R2"%V29,R2"%Z29,R2"%^29,R2H`AVJ3&2#*"+S))DS)(,F9)D +MS)(,FI)DS)(,FY)DS)(,G))DS)(,G9)DS)(,GI)DS)(,GY)DS,*AU,K'R9'" +M#'@,NOE!%LP3'%:!U`'2)Y1V@"4+B,(D@[(DPY@U\B5#XB1#P+L@L)D@D/\@ +M\.X@C*X6:`_H#28N!L;T_P`6J`Z(#8+(_H!J@Z#VP!;O'`PE#!IEM]VB(]VI +M(9(A`B*@"%"9())CW7:`"<(CW/?#@W1#28]UV@!"2 +M(^>9,8@Q@(`4B3'X,28_`@;Z_ZB!9>WGJ)&X!\%R`,)D@K(K)[)D@J(*>B8: +M!-B1(DUZ]J8$#)[GMA52`@RZTB>41L'_ +M*)$,#_)"A`P"'?`)(D@_(DP^@UTB5#PB1#D/\@\.X@X-T@ +MT,P@%IP*%I@*J)&B*A^H"@N(9BK3TB/GV1'($<#`%,D1N!%F.^[H46:N`L8G +M``RXAYX"1B<`#.JGG@+&)@`FC@]F'@(&*`!H0>+.]PR=X&V#9=WG#+KRQO56 +MS]XAT0$4J`<,#=D.RJKEJN>(D;'7`0SLXM0$#`WQT@'R9(`B +M9,#9#L)E0)(D@J@'#"^PF1"2:B>29(+R2'H&>O^(D0P"(DB%##(=\`!6&/:H +M<:(JD`PIDFH`Y3T"1M3_`*T'92T`J5%&P_\,QD;?_P`,ML;=_P"X<:AAL@OZ +MY3L`1MK_`-B1K0<,',)->.4+#QQ6QM7_````-D$`(=@!'?`V00`AV0$=\#9A +M``P=2`)Q-P#RH/\,#&(GK+%)`%'/`6"@!`=F$GSH@(80@F>LP"``,B>L.0'` +M(``Q.0"!S@&"9"=2:X+R9"7"9"KR8X*(0I+"$%'0`0OH%EX/XLCQ%FX.4F0K +M?/."H=2*@O((@>((@,)D,\)D,,)D+C)D+S#_$4#N$3'1`3)D,O#N(%#N(`SO +M\F0TXF0M##7B9"Q29"91T@'B)"M29#'B:\+A:`#R)"WR;D+R)"SY+O(D)O)K +M0O+;!-)K1-)K1=)N6%)K@#)KP`SE,5X`R0]2;D#R)"YFK<:9,*@L\<:D=*@MM>:B^($@I'4 +M`59N!\(BE/%)`(%H``N9TB^#LB_#HB@#@BA#\B]#T+L@L*H@H(@@@/\@%A\& +M%AD&J`R!:`#Q20!F*M&X#"8K(ZT"92@!/0H&S_\`+0,=\`RR'?``P@9^]LP< +MK0*E)@$]"H;(_PRR#"W21(,=\`P2##[B1(,=\``,@AWPJ`(,2\*@C,JJY7GG +MPB*41NO_``!6B?H,+=D,I1`"PB*4QN;_-D$`,J'0.C*2`WX,)4*@`R8I,JT" +MY3<`C$H,$AWP``""(R"("`PI#+IF*`520WY&`@"M`N66"$)#?@PY)CD"9AG- +M+0H=\`RR'?```#9!``P(S +M!*(*@(PJ'?```%($@'T(+!M:PIK,&R4@('0R3()B!'^WL@%]`G<6W7)$@!WP +M#!D@H[!ZJI)*@(;M_R"SL'J[@DN`QNK_#$P@T[!ZW<)-?X;G_P```#9!`%(" +M`3("`@P'+!17$QL;96!@="I54@4$1[83@@(#8D(!"XB"0@,M!1WP(J#_'?"2 +M`@-R0@$+F9)"`RT%'?`V00`,%I("`8("`@P*+`0;>'!P='AF")G&(""8H!0;T_Q::&/I7D;8`DF0X)8_GP5,`#.WBTP0,"_'1 +M`:@'@=(!@F.`\F/`#"^Y#M)D0,)D4_)D6:(J+$Q9+`S`JB"I)+GDF=2"!1S8 +M`0"((["(4W"(L((H3MJ(B?3B!20`[B.P[E-P[K#B+D[:[NGTP@9^<,RPPBQ. +MVLS)]*(%WZ+*_A9J$`P9F;3")YBAW`')(2>\5>@A>3&J9^#BP.D1#`)X(;(5 +M)WIRK02NW@QQ@``JF<,`L(5)S(F@`O,QY,3LF:`TB9_ +M/0EE@XM"F"V('"G +M("69#I&L``P(L<0`@*H1H*(@L*H@HF1,@D4M=H`0N#0+F8S+%LD$PB.4R`PF +M+`0&^O^\Z9&L`#WP=H`0TB1#"YF,O;R9XB.4Z`XF+@2&^?^LR?@##`B"9%/R +M+RSY)!WPN;3&O?_Z5ZC%#"F9"J7=`4::_P#(Q0PKN0SEW`'&[/_HQ0PMV0XE +MW`'X`PP(@F13\B\L^20=\``V80!!90$,188```",U``UIH`0IHD!.`$+1!;C +M_I@!T>,`G#D`/:;`$*8,"LD!N`&R0@"I`2@!'?`,'ND!*`$=\````#9!`)%E +M`0Q%Q@```!:9%@`UIH`0I@N9%AC_H>,``#JFD!"F@<`!`#BFL!"F`#BFH!"F +M8=X!:F*2!G\,%-Q)R`*R4@RB4@VIO+)L"B"B(.49`$)&?[*B`@`[IO`0IJ*E +M`C*B0CHB\D*]`#JFX!"F#`QRH@,RH0+2`KW@X'3B4F`6G0F"`L\6.`B2`K^0 +ME,"20K\`,Z:@$*:@H'0F&G;"0LX`-Z:0$*;V*2X`,Z:P$*8,"0`SIJ`0ID") +M(*"8DP`SIO`0I@R>@-D1\D+0X-T@`#VFP!"F#`(=\,+)_A;<"]+)_18]#`P) +M`#.FH!"FH)23`#.F\!"F#)Z`V1'R0M#@W2``/:;`$*8,`AWP``#"0K^&W_\` +M`$)"SD;A_P``0D*_`#JFT!"FXJ`7HA(7TD+1T-!TUSY_HF8B`#>FD!"F]BD\ +M`#.FX!"FDJ```#>FH!"F]BHU`#.F\!"F`#.FT!"F#)I`N2#"0L[0FY.`B1&@ +MB"``.*8@$*8,`AWP#!(=\"8I1"8Y7`P)!O#_)BI$9CK)`#NFX!"F4)D@1N__ +M````.Z;P$*8,J4;"_P``.Z:`$*8<24:__P"RS>IE>0ZRH@(,#*)F(D;<_P`[ +MII`0I@R)!M[_`#NFT!"F#"J@F2!&WO\````[IN`0IAP)1M?_`#9!`$'+`0P6 +M4J$]2D*2!(`RHB@Z,A8Y"((#%'?H$EIRL@?_`)LC()FP6IF2"0$F*6^R`QP` +MFR-WZPH@J;!:JJ(*`28J_P``K0(, +M#.6@_R"B(+(#',*@`B6@_T;=_P`V00!2H=1:4C(%@$(2#,'?`19#!'(53F(5 +M38(B-Z(B.++$?\"[$,"J`<"($0MF"W>R54.2$@U`=Q'`9A&@B"!P9B"AX`%Q +M:`"2R3^@F1"2542"9T9B9TD=\.%H`#(53M(536(B./(B-T)50\!F`<#_$0O= +M"S-R$@UR541`,Q'`W1%@_R`PW2#R;D;2;DD=\``VH0"2H^@QRP'!O@!Q20`P +M,H!B(R)V@!6R)X8,&`P*P+L0L+:5L*B#C%D+F8P:QOC_PJ(LRL+)D19I2=(, +M(0PE06@`_"V1U`&B(I1V@"8+F<(G@[(GPX@T\B1#XB=#P+L@L(@@@/\@\.X@ +M%FX3%FD3V`HF+1*&]/\`J`(,2\*@C,JJ)0+GHB*4V`K2S?X6O43R`B!7;PF! +MX0&"8Q^&`0``D>(!DF,?TJ("H>,`X3<`B`*1XP%2H0*(6,*E`K'D`8+(^Q8H +M%X'E`0`XIH`0IB;(`PSB'?``.J:`$*;XD8)/SP`UIO`0I@`UIH`0I@`UIO`0 +MI@`UIH`0I@`UIO`0IH*C`@`XIE`0IH'F`5!5H(!5H/@564&:@HE1@@B#4J$" +M\/#T%M@]@A(,]YBFDJ"HFI*98?AA\@]B(/^P\B].:O_R9$0`-::0$*:XD;(+ +MW!;;.8(A"8((TQ;X.9'G`8B1N%$,#_)(V))K,O*D`@`_IK`0II(A"9()W!8I +M10`\IH`0I@`UIK`0I@`UII`0IJQI=H`-`#JF@!"F`#6F\!"FG%_&^O\```!6 +M">Y9"F6'`:(BE$:U_P```(AA#`NY@9(8XO(8X[B1`)D1D/\@\F=3DB,BL@O3 +M#`_R2&"RR_X6^S^"(CH<[YJ(@F=.\F=4B&&8D8(8X_A1LAEK@(`D@F=&LF]# +MLB[P\B[=@J_[@(\0@F[=D@G3DLG^%IE$D0@!D)L0DF[P4M(FLB43#`[I<1;[ +M(&'-`3+2(VIBQB\`#$@`.*;P$*8`.J:`$*8`.J;P$*;QZ`$`/Z:`$*8`/Z:` +M$*;QN```/Z:`$*8`.J;P$*8`.Z:P$*8`.J;P$*:(D?)(S[*C`@`[IK`0IK9[ +M`H:1_X'I`8"+H(@(H`@`DB42/?"LJ8(CWB"B(&"V(.`(`*(C\9(C]R"JH*+: +M))DJ@B/D#!NM`N`(`+(E$AM$MS33Z($;[NF!T>H!`#VFP!"F)AP.@>L!`#BF +M\!"F\L__%N\2F'&B)1,;F9EQISD"!E$`N)&M`@P$0DLBI9P!\F,]\F,\\F,U\F,TXF,QXF,PXF,MXF,L +M\F,[\F,Z\F,W\F,V\F,S\F,RXF,OXF,NXF,KXF,JT>0!`#VFP!"F@J4"`#BF +ML!"FL+!TN8&BH@(`.J:0$*8`.*9`$*9"8_?R)1(,!!;O\X(CWJT"8+8@X`@` +MLB/QF)&B(_<@NZ"RVR2I*Y(9(<(CY)>T"ZT"#`O@#``&`@```*T"#!O@#`#" +M)1(;1,P$`/Z;0$*;2S8%6G>NQXP``.Z:@$*9&J_\``,B1P@PCHB*4/?`6 +M7"/8"M+-_A;M6PP.L6@`F&$,"K(K3-(9X_(I:K#(=-#,@K"P=,J["XNPBH." +M:6[I#^B1#!\,#=).(_).(<(G@9A1LBD\DBD]H<0`@+L1L)D@H)D@H6@`#`B2 +M:DRM`H)B0*4;#K';`>B1V%$,#*).(L)M/<)M/``[IJ`0IJ+*X!8:4PP2'?`M +M"AWP#!(,/_),(AWP`(B1J)@,L@P)F0I22"(=\```^)&0L'2R3]-6^\68D0P( +M@DG8!AG_B`*)$8BHL@.`]Q@-F!'YJ68;!@PKLD.`#"N80?@1F"F(OY"0])DA +MEQ@+F;]F&P8,*PPIDD.`\J"H^O+Y828;.\@"J+S(K/NJH*1!^\S`M/2B7^3` +MQ$&PJH+"7^.2'^.B7^*AS0'`F1&24@R"'^2RH)"JHL"($8)2#27P#9AADAGB +MMCD%"YF&````#!D,"Y"0](RY=H`&&[N0D?2,&8;\_ZA1LDJ!K0)EG/^AXP#" +MI0+A-P#X48A!#!NR3X.8.))O'HA(@F\?1MG^```\IK`0IOB1L+!TLE]K1NC^ +M.&&"(Q`,SYJ(@F=2\F=4#`B"9TB"9T>"9U^2`V`,J[/X!9/X"8)G8)(G8;+2 +M(V#Y(_)KPI">M$"9(Y)KPY(G8AN(8/DC\FO$D)ZT0)DCDFO%@F=@DB=A8/DC +M\FO&D)ZT0)DCDFO'\B=B8(\C\)ZT@FO(@5,`0)DCDFO)@/\0\F.!K0(, +M#=)B0.7@#2B1^%$,#J)"(N)O/>)O/`PB'?"XD9"`=()+TU8XY_B1#`W23]A& +MGO\````\IH`0IIB1@(!T@EEKAI[_HF=2#,BX88)G5`P(@F=(@F='@F=?T@M@ +M#*_S^`73^`F"9V#")V'2TB/`GK1@S"/";<)`F2.2;M&#,(\)M +MQ$"9(Y)MQ8)G8/(G89%3`/#.M&#_(_)MQD#,(\)MQ_(G8I"?$/#.M&#_(_)M +MR$#,(\)MR9)KG,:0_Y@"B`&9$9BIL@.`AQD-F!&)J68;!@PKLD.`#"N($8BX +M]Q@-F!'YN68;!@PKLD.`#"ORH*CZ\OEA)AL\R`*HO,BL^ZJ@I$'[S,"T]*)? +MY,#$0;"J@L)?XY(?XZ)?XJ'-`<"9$9)2#((?Y+*@D*"B@,"($8)2#26O#9AA +MDAGBMCD$"YE&```,&0P+D)#TC.EV@`8;NY"1](Q)AOS_````J%&R2H&M`F5; +M_Z'C`,*E`OA1TJ("#!NR3X-&/?^RH*#RH'A&-O^RH4#RH/`&-/^RH(#RH&#& +M,?^RH+#RH)"&+_^RH6#RH2!&+?^"`B"2H/Z0B!`,&9"((()"(`PB'?"XD0PJ +MHDLB1I?^`.$*`?$W`-AAX.H@XF_PV-T,C-<\`L98_X$W``Q/\/L@\FC=1E7_ +MB`I6&-Q&@?^HD0PIDDHBAG?_R)$,"[),*H9Y_P``-D$`?!9:A#IR,>T!BB=G +M(B:6P@.FLA0@8#0P9J!H!B`D(6`BD/`B$1WP```P(J`H`O`B$1WP```@(&`@ +M8#0P9J!H!B`D(6`BD"`@8/`B$1WPX"(1("/`*`(@(&#P(A$=\````#9!`'P6 +M&W(;DQNE&X1`A+-0I;,PD[,@<;X_PP2'?```!9L +M_UA1@J(DBE7"!2G\')'4`>(E"W:`)@N9\B.#TB/#R#2R)$.B(T/PW2#0S"#` +MNR"PJB`62DX624Z(#B8H$H;T_ZA1J`H,2\*@C,JJY4KFZ+6X#@P"LLO^%AM- +MZ%'RWB8B;QLB;QG"#B#Y`2)NF%=L"YA!@>$!@FE^!@(``+A!H>(!HFM^R%'1 +M[P%A4P!RW`':S,D1HB$%I5,`@@71,6[G4D@48`)DC()E3 +M@)FPDBE.ZIF9])(%(`"9(R"94X"9L)(I3NJ9F?2B"F*`JK"B*D[JJJGTD@7; +M9BEKZ$'"9"6X,2)D%>(N@;(K$)&L`.J[LF-2@B1#"YD6>#X6>3ZH,:(J:J@* +M9BKJN#'H`0S(@F-4DBXPXBXQ@BL6@)D1D.X@`(@1@.X@XF-(LBL6(.L1X+L@ +MLF-'A@4`HB$%97,`%MH+H"H@D```PF05'.F28U2X,0P.LAOCHA>V(D4JL+`D +MLF-&%MH$=H!&G0VM#((5)6)C5=/Z"O/Y"!O=O0GS^@+#^AK#^QVR8\DC^A;# +M^A3#^A4C^A(C^A.IQ"G$DF1,UY@%\L\!TJ``@A>V&^Z'O@.&[/\`F#&2*9F2 +MR?X6F3&B(0"R*C"B*C&`NQ&PJB"QQ`"PJB"B9$RB(04B:D"EAPW(`0P)HD4J +M(FPQ(FPPH@4J)CH()AH%TLK^5DWC+0D=\.A1X@XA!^X-J%$ESP"="KA1R`&& +M`@"H467#!9T*N%'(`28Y,+9)#`P?##B"12KR12F&XO]F*;4&X?^84:(9#9(9 +M#`"J$:"9()F$1HC_(D4I#"EF.P$`.J;P$*:2 +M#%#RSX$67PL,+X")$?"((``XIO`0IJ(,40PM@*H1T*H@`#JF@!"FB2&2%22" +M:T"7.!,,:8;4_P"H4:6_`)T*N%'(`<;0_Z@ALA4EI6$-N!&B:^.R%26B(0+E +M8PWH$;A1HF[BT@LAR`$'[0Z"I0(`.*;P$*;R;#>&!0"H(=(L,J"JH-"JH-*E +M`@`]II`0II):`:*A`@`ZIM`0IA:]$``ZIN`0IJS>=H`/`#JF\!"F@BN]&XB" +M:[V(,*(#_ +M$8#_(``_IN`0IN)5:@`ZIM`0II*B`@`YIH`0IH)%V_*C`@`_IN`0II(%V^)% +MXQ89!-*C`@`]II`0II)%X`NIHD7@"YF0D'06614`&4`,&*'P`:)L)@"(H8)L +M*+#X$?#@8.)L*_+/_Z"($8)L*?)L*I(%V]+)_A9=#N(+M(R.#.D,/_)%*@89 +M`((+(2=H.9(%X@P:D)J##/H[F:"98PPJ@)D1H)D@`#FFX!"FTJ$"`#VFH!"F +MH*!TC$H`.:;0$*;RH0(`/Z;@$*:8$8'L`8)I'()I'8)I(()I(8)I)()I)8)I +M*()I*8)I+()I+0P)5HG;#!W23%*B"R&@H`16J@JH426E`)T*N%'(`09G_P`` +M`%8IL_*@`O)N`"65`.(E"\;(_BD.#"(B12H,LAWP#!(=\`PH@D7;1C?_```` +M5AG"HB$#\F$&HBIJTF$'#"F9"N61``P$+F9"0=!8I!``90`P?X?`!XFPG`/^A\FPLH(\1@FPML/\1"X^" +M;"[P\&#R;"\&MO^!YP&";":&L?^H4:6/!9T*N%'(`48\_RT*'?"1YP&2;"<& +MK?\````V80""H@(`.*:0$*8RHD0Z,H(#Q,P8DD.[0J$"`#2FX!"FLL(4#`^A +M20#1TP&"$Q3"$Q7:T@"($8#,(,)J4Y(#N\(#Q/)+])+)_A;I"!9\%9(M?X(K +M-1SOFHB":D[R:E3"$Q7`P"3":D:LSJ@!=H`1`#2FD!"FT@/$X@.[C&V<63WP +MQOG_`!NJ)BX(\B,L\L\!\F,L5HG^J0$`-*:@$*:2`\(,*H(#Q("9$:"9(!9X +M"P`YIL`0I@`TIN`0IM(#Q#WP%OT*`#2FD!"F\@.[DD.\)B\$DD/1'?`=\``` +MDM(C5EP`@BG*@F$`PBU_@BLU#,7*B()J4E)J5/)J2/)J1_)J7X(+]`RLP_\% +M@_\)\FI@4BIA4,ZT8%4C4FG"0,PCPFG#@BIB&\^`7K1@B".":<1`52-2:<7" +M:F""*F'!4P"`7K1@B".":<9`52-2:<>"*F+`R!"`7K1@B".":(#NZ(36M#,@E+2(\JJ)BXQX@/1K$Z2 +M)\B)=%F`@0M"9)ET2!Q(:"G@.8:`H8G`.82`@8K +M`+T")2`-S0II$6@1:F>F%DSF$@(&*0"M!KT"R2&E'@V8(:"YP*(ES/"[$;)E +MT'JJIAI#YA("!B4`O0*E'`U]"J861.82`@8D`&"F(""R(&4;#:#'P/#,$<)E +MSL:>_^82`L8@`&"@8+T"R2&E&0V@H&"8(<;J_P"F$GR@H&"]`F48#:!P8.86 +MNJ82=[T"8*!@91<-H*!@!N[_`&D1IA)NH*!@O0(E%@V@P&`&U_\`(+!@:1$E +M%0V@P&!&T_^M!B"P8,DA)10-H*!@F"'&U/\@L&!E$PV@<&!&V?^M!B"P8&42 +M#:"@8$;:_ZT&O0+"80)E$0V8(8;*_[T"Y1`-?0J&S_\``*T&O0+E#PT&T?\` +MO0)E#PW-"D:\_P``-D$`#!=BH0(R`B%"HBQ*0B=C-%($V@PH#/E05X,[59!5 +M8X!5$8!5(``UII`0I@`VIH`0IH"`=!98```UIH`0I@`VICWPD!"FH@(6TJ`` +M)BH.4@33LL7_%FL.PL7]%GP-TD35X@(A-VX*4@33"_46[Q06M10R!-R"HP(6 +M(PX`.*:0$*:R!-,,.CWPLLO]%OL+P@(@)VP9T@3<%CT4`#:FX!"F\@3<%C\4 +M`#:F/?`P$*:2!-Q2I0(6*0L`-::P$*9B%&L6)@E2!-.Q\`'!YP$6)032!-P6 +M'0\`.*;@$*9B!-@61@=2TB8+9F!@=&)$V!9F#K)E)@`60`#GH>)E*+#>$="0 +M8))E*PO=H.X1XF4ITF4J4@33)B5D4M(F_.B1"(, +MXAWP```XIL`0IL)$VX;%_P`UIF`0IF!@=&)4:X;1_P"2!-P6&0@`.*;0$*9B +M!-D6EOQ2TB8+9F!@=&)$V19V![)E)P`60`"'H8)E+*"8$9)E+;"($0N8DF4N +M@(!@@F4OAM;_```VIJ`0IH:J_P`XIF`0IF!@=&)$V,;!_\)E)H;,_P```#:F +MD!"FDD3.1JW_`#:FL!"FLD30QJW_`#:FP!"FPD35AI?_`#BF8!"F8&!T8D39 +MQMW_`,)E)\:__P``-D$`#!5BI0)"TB.B(D`RHBPZ,K(3(67H#*)D\;(3(:(B +M0.7J#*)D\(("(0?H#0`VII`0II)D]X8%````LB)`PB3RL+N@P+N@`#:FH!"F +MHEL!HJ$"`#JFP!"F%JP3`#JFT!"FG%UV@`\`.J;@$*;R(S(;__)C,HPN1OK_ +M```ZIH`0IO(#V@PN@/\1X/\@`#^FT!"FTE-F`#JFP!"FLJ("`#NFD!"FPJ," +MDD/3`#RF@!"F\>P!PF0JPF0KPF0NPF0OPF0RPF0S +MPF0VPF0WPF0ZPF0['?`````ZIM`0I@P"P>P!PF0JPF0KPF0NPF0OPF0RPF0S +MPF0VPF0WPF0ZPF0['?``#`+A[`'B9"KB9"OB9"[B9"_B9#+B9#/B9#;B9#?B +M9#KB9#L=\``\II`0II)#V0NYLD/9"YF0D'2LB=)DYP`90`#%H<)D[*#\$?)D +M[;#,$0O\\F3NP,!@PF3O!L+_\F3F1K[_`/)DY\:^_P``-D$`0?$!`#2F,!"F +M@J$"4L$D6`46PP4`.*:0$*8PI\`ZUQ:)!:D%`#2F8!"F0L$H,L$@.`-(!+SF +M`#BFD!"F8*/`:N.\R:D$:`5L!W>F!&+&0&D%IL8$LL;`N058!#WP=Z4$4L5` +M603FQ0$=\,+%P,D$'?!Y!4;K_SD$1O+_V07&Z/\`Z02&[_\````V00#!\0$` +M/*:P$*8,+M*A`J+!)*@*%FL(`#VF@!"F@)(1X)D@%C@(`#FF@!"F#!^P_\#P +M\X+Z]X#_P`O_^0H`/*;`$*9RP2BRP2"X"W@'%BP%`#VF\!"F@)(1X)D@%B\& +M`#FFX!"F#!W`W<#0TX+:V^#=P`O=V0B`VHB*0HGI`1@XL#I"B@'5Z(% +M8"*`(F<`)R0!'?!@\L#Y!QWP>0I&YO^Y!T;S_P```#FF@!"F"_OP\X+Z]_J( +M&XB)"@;?_P```#FF(!"F"XR`@X**BXHB&R(I!T;G_P``-D$`HB)!DB*4#"@; +MJJ)B08D)90``'?``-F$`HJ`!95;;)9CEH3X`T6@`D4D`X6@`#`S":8#":<#) +M#L)M0+(I@KD!B`&@B""":8(=\#9!`'@#G`108&!:1PM$8$00*B0I`RT$'?`J +M9RT':0,=\````#9!``P2'?``-D$`#!(=\``V00`,%0S#)[,##`(=\"T%'?`` +M`#9!``P5#.,GLP,,`AWP+04=\```-D$`#!4<`R>S`PP"'?`M!1WP```V00`, +M%1PC)[,##`(=\"T%'?```#9!``P5'$,GLP,,`AWP+04=\```-D$`#!4<8R>S +M`PP"'?`M!1WP```V00`,`AWP`#9A`*T"Y18`C$HM"AWP``!120!QS0&Q-P"1 +M\@%\N*(K\,(KW3*B*#HR@(P0@FO=@@/70J"H2D*"R/X6J`V!"`&`BA"":_!Z +M`9J2F0%J8H@!@BC-K0*]!^`(`*(F/+'R`9(F0B"JH+JJDFHQ@B8JO0>M +M`N`(`,&T``P+LD9^TB6!UPP"!C<`W,KB`R?.Y";)#)^(#+@P?\D,E%LX$ +MLB1OH48`&[NR9&^2)8$,F*"9$)";09`HDQWP``#Q"@$,C?#Z(/)K\.(B-PQ( +M@(P@YST"!L7_@FO=AL/_`,@)5GSX+0H=\")#)D;I_P"R0RZR)&^A1@`;N[)D +M;Y(E@0R8H)D0D)M!D"B3'?`,LGS]#![B0R?298$=\#9A`)&L`#%H`(+2*XBH +M@F$!=H`1HB-#"YF,^A:9(;(BE+@+)BL(QOG_````%HD@0L(0P4D`\@(AHJ(B +MJJ+B"MWP\030_Q'P[B#B;(V80@P6TLG]%AT2TLGQ%KT1X@(@^`+@X`2(O_BO +M0.X!`(@1@/\@\.X@Z8."%`5R%`211@``B!&`=R!R;,92"M]R!!`,"[!5$7!U +M!-!W$7!5(%)C./(D-.(D,UT+P/\!P.X1\.X@Z6/8!/($%'(*W>($$((*VH!W +M$>#@!)"($3#_$=+-\=!9@X#_($Q=0(X14.X@@'<@/9T\(* +M&H@1`,PCL,Q3(,RPPBQ.BLS)\Y(*(@"9(["94R"9L)(I3HJ9F?-R!/I1]`$@ +M=[!R)T[A\P'1]@&*=WGS\@00PM(C+)_A8N)_+)_18?)B'Z`=)LWB)LY`P" +M'?"R$@V2`B""$@P`NQ&0D`1`F0&PB""0B"")@P:X__'[`?)LY?'\`9(*W>'L +M`5+2)B89,8+)_A;8#68Y`D8D``P"'![!ZP&1_0&R8SC291Z291_"91KB15"R +M2BP=\+)*+`P"'?``8F,5@@00J0$G:!"1_P&A_@&B;-Z2;-]&`P```+$``M'S +M`=)LWK)LWZ@!@@KB#`(,"X+($8"`=()%4("($?"((()E&N)L.^)L.N)L-^)L +M-N)L,^)L,K)*+!WP``"B(I1"PA`,*9D*);;_1GK_D0$"8F,5@@KBDF4? +M@L@1@(!T@D50@(@1\(@@@F4:XFP[XFPZXFPWXFPVXFPSXFPRT@2D%BWUJ0&X +M`0SB##JB2RP=\*D!8F,ELF,5T@KC,@KB'"B"15`WO0G,TRN3DD50A@$`S#TK +MK:)%4+($$"=K"BDQT0("TF4>Q@$`*3&!]`&"91Y!!`(Q`P(H,0P&<@50XFPR +MXFPSXFPZXFP[XFPTXFPUXFP\XFP]8FPO8FPN8FPK8FPJ@'<1.B+P=R`,`W)L +MV@P&95H,HF)#N-6F$T+F&\FPL&"M`R59#*"P8+)B +MNP;P_["P8"58#*#`8,)B0\;U_Z8;$:"@8"57#*#08-)B0T;Q_P```"56#*)B +M0T;N_P``IAL-K08E50R@X&#B8KL&X/^M`V54#*)BNT;=_Z@!#`(,"[)*+!WP +M``P"8F,5XFS>LFSD'?!R;-ZR;.0,`AWP`(@!8F,E@@@:#`+Q!0(`B"."8Q52 +M;-[R;.0=\*@!#`N1!@*2;.6&9O^A!P(,"Z)LY:@!1F/_L0@"J`&R;.4,"P9@ +M_Z@!#`OA"0+B;.7&7/^H`0P+\0H"\FSEAEG_J`$,"X$+`H)LY496_Z@!#`N1 +M#`*2;.4&4_\``#;!`%%H`.'<`0P'@B,3DB,1R-.X\ZT"?.T,$M"[$-#,$-"9 +M$-"($()C$Y)C$9)C'LG3PF,=`PPK=JL88BD< +MLBD=BYF6=@'6BP#`RT/0UE/RH`$]\.IJ5N\91@0```"6"__`QD/0VU,,'T;Y +M_^IJTB9_XB:`S0<+])T$\)>#X_P*T_P"<_P!<_P:D_P6(_P4<_P5<_P2<_P3 +M(_P;R<4F%!:H<[C3B,.@H`2PL+2`@-2S^`ZC^!R)Q9QTR)/8\[(C#L#`!-#0 +MM+"PU-/[#L/['+)E#/(F@.(F?]T'\_T*X_T"(_T!<_T:D_T6(_T4<_T5<_T2 +M<_T3(_T;V<4F%!FH@[(C$8(C$*"@!+"PM("`U+/X#J/X'()E#)QTR*/2(Q.R +M(Q+`P`30T+2PL-33^P[#^QRYQ?(F@.(F?]T'\_T*X_T"<_T!<_T:(_T8D_T6 +M(_T4<_T5<_T2<_T3(_T;V<4F%!>H<[(C'(C#H*`$L+"T@(#4L_@.H_@MX08I`.J-LLT_T+VS@L@@L+8AN8&@NQ&2RT"9H8<_,<";P)+) +M0)72)4.H<=#0!-D1R!'A#0(;JA:<"JEQIZ[A1B@` +M#`J=`WS]#"]VKR6R*1S"*1TF&@XF*AR6NP+6'`%R:1W&`@"(H8KKXFD2*3C`_Q&: +MB!N(P(@!@/\@^3'Y908!`*DAIRL8#!HE]^38,:@AZ&7I$<(A`;$-`J+*`=>< +MX2)#C`PJQL7_-D$`46@`H=P!@L3^#`O="VT+@&23JJ+R*H#B*G\P9K#S_0KC +M_0*S_0&S_1I#_1:S_12S_16S_1*S_1/9Q(F$-+$ +M_O#PM.#@U//^#NG%%NT.XBJ`TBI_G0OC^0K3^0*S^0&S^1I#^1:S^12S^16S +M^1+#^1.9Q8(F%?(F%.+$_H"`M/#PU(/_#OG%%FX,\BJ`XBI_W0OS_0KC_0*S +M_0&S_1I#_1:S_12S_17#_1+#_1/9Q9(F&8(F&/+$_I"0M("`U)/X#HG%%N\) +MXBJ`TBI_D0X"K0OC^@J:(H(B?]/Z`K/Z`X!@P*VL*R3'_!#P+@FA'*F<+, +M,)@)F1.28R+`RJ#(#,DS\@(@XJ$"4L(4!^\*`#ZFH!"FJ2.2(R+"R?D6'!;R +MI3\`/Z:@$*:VN@BBH`#0@H"R2'_Q$`*(,Y@3\,J@R`R2R?R*S,DS%BD2#`?R +M!0PG;Q68$R9)""8Y!8(C`Q9X```^II`0IIE3D:P`04D`=H`1HB2#"YF,ZA:) +M$,(EC\@,)BP'QOG_```6B0]AW@'2)3NH0VIB%@T+@B8PX`@`N#.PR@%PNQ'` +MNR#"H0O`NR``.Z:H0ZJGJ4/B!GZ(4Y(F/+$1`@P?()F@NM+"+=:ZF=(MUY(I +M*+@CP_T(\_T0#`RS_1&C_1*3_1>#_1P,.,/]'>/]'M)DR;(F/)(F.^T,L_X* +MD_X"D6@`P_X!\_X:@_X6\_X4\_X5P_X2P_X3Z/]"=)D8,)D8<)D8L)D5L)D51WP@B8PJJ>B8P3@"`"8,["J`7"9 +M$:"9(**A"Z"9(``YIJA#AM+_DJ("`#FF@>`>&LO\=\`#:LK(K +M(`PJJ0ME'__&O?\`-L$`4J$"`#6FD!"F#!8,!=*B+)D#5KD%VJ+RI#_A$@)R +MH`>RH!1"SF!V@"4`/Z;`$*8]\,>[!&)*(PP,X)R@F`F9$R99#I)C(D",H(@( +MB3.HF3)6<-28QA28Q128Q!9TU)C +M&5)C%5)C$:(I/)(I.U/^!:/X"I/X`E/X`6/X&E/X%F/X%&/X%5/X$E/X$XG' +M6<=B9%_R#PC14P#"TB/S_@GB9&!29&%29&)29%;29%72+/#"+/'3_`C"9TP= +M\```%MHWDL(8\BDZF:&B(P06CR*"(0V"*.7@"`#(,YBQL-H!<,P1T,P@#+W0 +MS"``/*:HP;A#NJJI0]@3X@E^PBD\LBD[B",@_*"S_`CRWR3X+X/\$:/\$O/\ +M%U/\'>/\'K8]`F/\$,)DR:(C(A;J%`O*%IP4TLK^%IU$Z-'R+O#B+O'S_@CB +M9TP=\(("(*+"&*)A"@?H"X*A`@`XIL`0IL)C`@`YIL`0IMJ2MKP$8DDC#`S1 +M$`*(,Z(C(M#2:"+.7@"`#(,[#:`7#,$=#,(-*A"]#,(``\ +MIJC!N$.ZJJE#D=X!V!.((YJ2F;'"*3SB"7Z2*3L@_*#RWR3X+Y/\"(/\$:/\ +M$O/\%U/\'>/\'O8]`D91`+T%V*&8L6/\$,)DR5G#4F,84F,44F,06=-28QE2 +M8Q528Q&"*3N2*3P,/^T%D_X*@_X"4_X!8_X:\_X68_X48_X54_X24_X3Z<=B +M9%_2#?"HT5/[!=/[";)D8%)D85)D8E)D5E)D5;(J\*(J\;/Z"*)G3!WPF-&H +MP[C3LF,9LF,5LF,1HF,0HF,4HF,8HBGQDBGPC06C^`J3^`)3^`%C^!I3^!9C +M^!1C^!53^!)3^!.)Q_C3Z,/8H?#PM.#@U//^#NG'8F1?T@WPS053_`73_`G" +M9&"XTZC#L+"TH*#4L_H.HF1AHF1BHF16HF15HB,BQHW_B-&8P8(HY:JIJ4/@ +M"`#(,["Z`9BQ<*P1L*H@#+NPJB``.J:H0P9U_P``5JKGZ*'B+HX,+=)N`"7? +M_H::_P""H@(`.*;P$*:B(R+@_Z#X#_G!QBG_K06XH8BQPF3)6<-28QA28Q12 +M8Q!9TU)C&5)C%5)C$?(H.X(H/`P^W06#_0KS_0)3_0%C_1KC_19C_11C_153 +M_1)3_1/9QV)D7[(+\)C14_H%L_H)HF1@4F1A4F1B4F164F15HBGPDBGQH_D( +MDF=,'?```,+2(\)A#:(L*K(L,L(L.J65`:)A":C1LBHSPBH[HBHKI90!\L,T +M@=X!G0JH@8J"B;&R*#/"*#72*#;B*#2B"MCY(9D!DL,PF1&"*#'XD>`(`)B! +MHB,B!OS^F,&"+.6JJ:E#X`@`R#.PN@%PK!&PJB"RH0NPJB``.J:H0P9E_P`` +MXB*4#"WRPACYH=D.9`(`*(C%+(C#,(C$&61`;C3J7'"(Q&B +M(Q6ED`'RPV2(T9T*J(&R*.C"*.K2*.OB*.FB"MCY(9D!DL-@F1&"*.;X<>`( +M`)B!!J3^K0*]`\*@`&5?_\BA8F1?P@SP4_4%P_4)4F1@N-.HP["PM*"@U+/Z +M#J)D89(C$8(C$)"0M("`U)/X#H)D8O(C%>(C%/#PM.#@U//^#N)D5M(C&<(C +M&+C1T-"TP,#4T_P.PF15PBOPLBOQP_L(LF=,'?``-N$`4J$"`#6FH!"F#!L, +M!?*B0*D#5GH%^F+A$@)"I#\<37+.8':`)0`TIL`0ICWPQ[T$LD8/#`S@G*"8 +M"9D3)ED.DF,B<*R@J`JI,YSEQO3_#'JB8R*2H0(`.::@$*8,&`P,J0.@R(,F +M'+Q&``"H`T%)`)%H`-'>`>*@B%8:$I(C(J*E/V$3`H+)_1:8,(+)_!8X,``Z +MIL`0IOIBMKP$LD8/#`RA%`+:DG+#,.J"B?%YP9GA>#.2(R*"PS2@K*"H"HG1 +M@LG^>JJI,W+2(Q:X0`O)%FQJ#`F,FN*A`@`^IM`0IME3HJ$"`#JF@!"FDF$) +MB6,6Z%Z"!M0;B()&U.*A`@`^IO`0IOES`#ZFT!"FV8/")_$,&]*B0"K,VLRR +M3.BB)RJR)S+")SKE8`&R)S.IH<(G.Z(G*R5@`;BAV,'R!L3HT:#*(!NOH*!T +M\J`!Y74`HB+#1-(&Q`P?S0H; +MK:"@=-+#0.5R`)&L`#WP=H`4XB2#"YD6+@L6V6?X1O@/\L_^%H\*AOC_VG(, +MB()C(F(G.\(G/*A#B%-C_`BS_!UB!WZC_!*#_!QC_!YM"\)DR5G#4F,84F,4 +M4F,06=-28QE28Q528Q&B)SSJPH(G.Z/V"JT%@_8"4_8!L_8:4_86L_84L_85 +M4_824_834_8;:J$,6Z4Z")^4,%N`( +M`,@SL-H!<,P1T,P@#+W0S"``/*:HD;A#NJJI0]@3N.'X4Y@CPBL\X@M^LBL[ +M((R@@M@DB"BS_`B3_!&C_!*#_!?S_!Q3_!WC_!ZV/0)C_!#"9,G88U9]"*(G +M\,(G\;T%\6@`P_4*H_4"L_4!8_4:L_468_448_45L_42L_43L_4;6<^8TXC# +MR/&0D+2`@-23^`Z)S^C3V,/28QC28Q328Q#B8QGB8Q7B8Q%B9%_"#("S^P7# +M^PFR9&"HTYC#H*"TD)#4H_D.DF1ADF1BDF16DF15@B?PXB?Q@_X(XF],'?"M +M`KT##`SEZ?[H\6)D7^(.@%/U!>/U"5)D8-C3R,/0T+3`P-33_`["9&&R(Q&" +M(Q"PL+2`@-2S^`Z"9&*"9%:HVH*)X18\0H(H,.`(`,@SL-H!<,P1T,P@TJ$+T,P@`#RF +MJ)&X0[JJJ4/8$_A3F"/HX7+2(\(G\;(G\.(.?B",H+/\"(+8)(@HD_P1H_P2 +M@_P74_P=\_P]CT"!LX`_04,'N/\$,)DR5G#4F,84F,44F,06=-28QE2 +M8Q528Q&B)_&2)_`,.*/_"I/_`E/_`>/_&H/_%H%H`./_%./_%5/_$E/_$U/_ +M&_G(XF1?T@:`O053^P73^PFR9&!29&%29&)29%929%6B)_&RHD`JJKJJ4DKH +MDB?P\B?QD_\(\FA,'?",FM*A`@`]IL`0ILE3HB`(`*C# +MLB`( +M`)&L`#WP"YFB)(,6&B86&2:X\;(KMCL$#!JC_!"M`L)DR;T# +M#`QE\OZ(\0P9DF1?@@B`_053_P6#_PGR9&#HT]C#X."TT-#4X_T.TF1APB,1 +MLB,0P,"TL+#4P_L.LF1BHB,5DB,4H*"TD)#4H_D.DF16@B,9\B,8Z.&`@+3P +M\-2#_P[R9%7B+CSRHD`J[OKN4D[HTB?PLB?QP6@`T_L(LFQ,'?``HB?QLJ)` +M*JJZJE)*Z*(G*K(G,L(G.N4#`;(G,ZE!PB<[HB`(`$:0_@"8D8(G +MY:JIJ4/@"`#(,["Z`0P6<*P1L*H@#+NPJB``.J:H0T;#_@``5HG(XB9R#"W9 +M#N4[_M'>`08>_P#2H@(`/::0$*;!$P*H,\"9H)@)!E'^##_M!<)DR5G#4F,8 +M4F,44F,06=-28QE28Q528Q&2)_&")_`,'9/^"H/^`E/^`=/^&O/^%O%H`-/^ +M%-/^%5/^$E/^$U/^&^G/TF1?L@:`K053^@6S^@FB9&!29&%29&)29%929%62 +M)_&BHD`JF:J94DGH@B?PXB?Q@_X(XF],'?"X\;(KPL620O-"X"[ +M$>"[(``[IK`0IL"`=``80`"JH9"!8`N(@(J"BKL;N["`8)"XHP`_II`0IHRI +M`#VF@!"FD/!@@)^3"](6+0@6^0>`_!'@_R``/Z;`$*:0T6`+W=#:@MK,&\S` +M\&"0SZ.Z,SD%,",@L+H1L+!@MZ,'H"H1(".`*06P.A$W(@>@BA&`@L")!;SG +M&R1`)+,@(2$J+/`B$2D&MZ(&H(H1BB(I!C>B`QWP``"@FA&0DL"9!AWPO0D+ +MPL"`=``80`"JH<;8_\T)!N;_`,HD*0;&\?\`-L$`\J$"`#^FP!"F#!<,!,D# +M5FP)LL(84J0_'$WA$@*BHBR@HH!BSF!V@$4`-:;`$*;'O1Z8#G)*(YD3)EDU +MDF,BB`:),\(+D&8L(?8I'@80````X)R@F`F9$R99%I)C(F",H(@(B3.""Y`F +M*"&\A#WPQNS_#'F28R(`/Z;`$*8,&`P)R0/`F(,F&9\&!P``]BG:`#^FP!"F +MPF,A%NS\@B,B6XB"8R)65/S(`U%)`)%H`!8\"*("J&+"&*+*_A9J(:%3`.T' +M\M(FTB\P@B\QR$.R#U+3^`C#^!)S^!VS^!Z"9G)2$86FD@+FA9)2++2(\+2)LG!N='2ROX6/56AK`"8 +MH:+*_^(E@Q8>+18:+?B9\B\`HLK_9B_K@B8ZJ$,6^#Z(T8(HY>`(`,@S\6@` +ML-H!<,P1T,P@#+W0S"``/*:HL;A#NJJI0\C!V!.8([(L,>(,4L(L,""+H(+8 +M)(@HP_L(D_L1H_L2@_L70_L=X_L>MCT"<_L0LF7)HB,B%EHN"]H6#2YF*F:M +M`KT#PJ``)9C^@F7) +M@B\Q\B\PS02#_@KS_@)#_@%S_AI#_A9S_A1S_A5#_A)#_A-S_AGIR4G)(N``NJ/?!F+NJH0_(F.H+2(X)A#19?,((HY>`(`,@S +ML-H!<,P1T,P@TJ$+T,P@`#RFJ+&X0[JJJ4/8$X@CDM(FF<&R*3'B"5*2*3`@ +M^Z#RWR3X+Y/["(/[$:/[$O/[%T/['>/['K8]`G/[$-T$P6@`J,&R9`(`*(C(H:W_@`= +M\)BQ@BCEJJFI0^`(`,@SL+H!<*P1L*H@LJ$+L*H@`#JFJ$.&/?\``#ZFT!"F +MP-V@V`W9L88@_\C1HBPJLBPRPBPZ)68`J5&B(0VR*C/"*CNB*BOE9`#RPS2( +MT9T*J*&R*.C"*.K2*.OB*.FB"MCY(9D!DL,PF1&"*.;X4>`(`,C1J,.R+#;" +M+#HE:P#(T:EAHB,-LBPWPBP[)6H`B-&="O+#1*BALBCHPBCJTBCKXBCIH@K8 +M^2&9`9+#0)D1@BCF^&'@"`"HT;C#PB,0HBHN96T`J7&HT;C3PB,1HBHOI6P` +M\L-4B-&="JBALBCHPBCJTBCKXBCIH@K8^2&9`9+#4)D1@BCF^''@"`"B(Q2R +M(PS"(Q#E:P"XTZF!PB,1HB,5)6L`\L-DB-&="JBALBCHPBCJTBCKXBCIH@K8 +M^2&9`9+#8)D1@BCF^('@"`#&9/XVH0`Y8:*B>HASV&.1%0(,&]#;DYJ2\BF! +MXBF`PBE_DBE^T.^3Z4'0G).941:H&JJ2D@F`\)D1D)O`N&&9`;B+"\0,"1:+ +M&9D1%@P:#`T,#NF1TF$(2&$]`:A1F$'Q%P)1%@(,2(EQ6E+Z\ODQH)G`F2&X +M82A1:$&X:\@QHB5_C)M7/`<3&YO\```"F$@^@H&"]`J4X"J!P8'G$1N'_O0+E-PI]"JG$1M[_(+!@ +M)3<*H&!@:=1&XO\```"F$@^@H&"]`J4U"J!@8&G4QMS_O0+E-`IM"JG4QMG_ +MD*J"IAH>IA(*O0*E,PJIY$;:_P`@L&#E,@J@L&"YY(;6_P```*82#Z"@8+T" +M93$*H,!@R>0&T?^]`J4P"JGDAL[_``"0JH*F&AZF$@J]`F4O"JGT!LW_`""P +M8*4N"J"P8+GT1LG_````IA(/O0*@H&`E+0J@P&#)],;#_[T"92P*J?1&P?\, +M"8:6_P"JDI()@'S[L)F01I;_D?$!`#FFH!"FJ8&RH0*,Z@`[IN`0ILB!P-!@ +MX,V3R8$`.:;P$*;YD1;/XP`[IJ`0IHB1@)!@H(F3B9%&BO\`-F$`4J!W018" +M8M(CDB;-@B;/#$=*0I"(P()A`*(D?[*OB9;J`:*8;924C"JG#!B4```"WJD'")L^R)LV@K(*F&ARF&PEE(0JIPP8>````L+!@ +MI2`*H-!@V<,&&@```*8;#:"@8&4?"J#@8.G#1A4`I1X*J<-&$P"!&`+@^A'P +M\L"*__(O?_#P8/G#Q@T``+"P8*4<"J"`8(G#!@H```"F&PV@H&!E&PJ@D&"9 +MPT8%`*4:"JG#1@,`P1@"(+J@RKNR*W^R8PRB)(#BKXF6R@&G)0*&)@#2)L^R +M)LW0JH*F&G6F&V)E%PJITP8D`.>J0/(FS[(FS?"J@J8:&Z8;".45"JG3AAT` +M`+"P8"45"J"`8(G3QAD```"F&PV@H&#E$PJ@D&"9TP85`"43"JG3!A,`P0," +MX+H1L++`RKNR*T.PL&"YTX8-``"PL&`E$0J@P&#)T\8)````IAL-H*!@Y0\* +MH-!@V=,&!0`E#PJITP8#`/$#`B#JH/KNXBY#Z=.B)'^2KXF6R@&G)0+&)0"( +M`;(FS8"J@J8:J/;@!L*J"LB;-IAH9IAL'90H*J>,& +M'0"PL&"E"0J@P&#)XX89``"F&PV@H&"E"`J@T XP85`.4'"JGC!A,`\0," +MX.H1X.+`^N[B+KO@X&#IXX8-``"PL&#E!0J@\&#YX\8)````IAL-H*!@I00* +MH(!@B>,&!0#E`PJIXP8#`+$#`B":H+J9DBF[F>.B)(#2KXF6R@&G)0+&)0#( +M`;(FS<"J@J8:J/>@!LB;-X*J"IAH9IAL')?\)J?,& +M'0"PL&!E_@F@\&#Y\X89``"F&PV@H&!E_0F@@&")\P85`*7\":GS!A,`L0," +MX)H1D)+`NIF2*;N0D&"9\X8-``"PL&"E^@F@H&"I\\8)````IAL-H*!@9?D) +MH+!@N?,&!0"E^`FI\P8#`-$#`B#*H-K,PBR[R?,RPQ"+1`MW5K?1'?```#9! +M`">C"3D$2T$'?`G)`PWI`,M!!WP+0,=\!WP'?```#9!`''L`7<2%G<3 +M*W<4.">C!C>D&RT#'?`GI`\=\```=Q-H=Q0YEB,$EE0%,"1#'?`GI`\M!!WP +M=Q1:IA(YED0$)R3O'?```">C#.83QJ82\@P$+00=\`#F$L`&`0```);C``P$ +M,"1#'?```#P!=Q,N=Q0[)Z,)-Z0>+0,=\````">D#QWP```G)/@,`S`D +M0QWP```P)$,=\">D#RT$'?!W%"RF$N&6!`(G).\=\```)Z,,YA/#IA+R#`0M +M!!WP`.82P`P$,"1#'?`,`RT#'?`=\```-D$`8>P!#`5@8L!@)8,GHPHW)!,G +MI`XM!!WP`">D`1WP0"-#'?`=\"T#'?`V00`GHPDW)!(GI`TM!!WP)Z0!'?!` +M(T,=\!WP+0,=\#:!``P;DJ``84D`46@`HB+&(D\?(/4B"9H-/^ +M",/^$)+9)`PLF"FS_A&C_A*]`ZT"D_X7@_X=\_X>XF;));O]#`D,&[D#QMK_ +MK0*]`\*@`65__Y&L``N9@B:#%L@\%LD\J)>H"I+)_V8J[,(B0++2)MA#%DP_ +M@BLEN5&M#>`(`(@SZ%&PF@%PB!&0B"`,N9"((``XIMA!^$/ZW=E#H@Y2DB3Q +MXB3P#`L@R:#C^0C"W"3H(\@LL_D0X_D1T_D2P_D7L_D=#"R]`Z/Y'I)FR:T" +MY;']#`D,&P:V_P``@L,TH@?8LB3HPB3JTB3KXB3I\B0JDB0KF0&)(9+#,)D1 +M@B3FX`@`@L,\PB3NTB3OXB3M\B0LN,.2)"VR9"JR).RHTZ)D*Z('V9D!B2&2 +MPSB9$8(DY^`(`)&L`-CCTF0LR//"9"T+F>(F@Q8>+Q89+_(BE/@/DLG_9B_K +M@B)`LM(FV$,6"#."*R6Y4:T-X`@`R%$,&_@S#`FPB@%P_Q&`_R`,N(#_(``_ +MIMA!Z$/JW=E#@B3PHBPQ\B3QZ",@JJ"#_PBBVB2"#%*H*I/_$./_$=/_$J/_ +M%Y/_'8/_'O)FR8C3Z/.HX_C#\F,8\F,4\F,0HF,:HF,6HF,2XF,3XF,7@F,1 +M@F,5@F,9XF,;#"CB+#&B+##]">/_"J/_`I/_`;/_&H/_%K/_%+/_%9/_$I/_ +M$_G%Z-.HP^#@M*"@U./Z#JG%B//XXX"`M/#PU(/_#OG%QF#_`(+#/*('V;(D +M[,(D[M(D[^(D[?(D+)(D+9D!B2&2PSB280&").?@"`"1K`"XX[)D+*CSHF0M +M"YG")H,6W!T6V1W2)PG8#9+)_V8MZ^(B0++2)MA#%HXC@BLEN5&M#>`(`,A1 +M#!OX,PP)L(H!A#ZMW90_(D\>(D\(@CK0_C^@@@ +M_Z#B#%*3^A#RWR3X+X/Z$=/Z$O/Z%Y/Z'>/Z'J)FR8CS^./R8QKR8Q;R8Q*" +M8QN"8Q>"8Q/B)/&B)/"-">/X"J/X`I/X`;/X&K/X%K/X%+/X%9/X$I/X$XG% +M^//HX_#PM.#@U//^#NG%QB'_`(+#-*('V+(DZ,(DZM(DZ^(DZ?(D*I(D*YD! +MB2&2PS"280&").;@"`"1K`"XP[)D*JC3HF0K"YG")H,6'`\6&0_2)PG8#9+) +M_V8MZ^(B0++2)MA#%HX6@BLEN5&M#>`(`,A1#!OX,PP)L(H!A#ZMW90Z(D\8(D\.@C_0J#_P@@JJ""#%*3_Q"BVB2H*N/_$=/_ +M$J/_%Y/_'8/_'O)FR>C#XF,8XF,4XF,0HB3Q@B3P_0FC_PJ#_P*3_P&S_QJ3 +M_Q:S_Q2S_Q63_Q*3_Q/YQ>C3J,/@X+2@H-3C^@ZIQ8;E_@P*J4'&WOX,#=E! +M1O+^`%;)P_(BE`PNZ0_EU?S&"_]6B=&2(I0,*(D)Y=3\QD+_5LGBLB*4#"JI +M"^73_,:'_U:)\=(BE`PLR0WETOS&PO^H0;E1@BLEVJJI0^`(`+@SZ%&PR@%P +MNQ'`NR`,O,"[(``[IMA#1@+_J$&Y48(K)=JJJ4/@"`#(40P;V#,,";#J`7#= +M$>#=(`R^X-T@`#VFV$-&,_^H0;E1@BLEVJJI0^`(`,A1#!O8,PP)L.H!<-T1 +MX-T@#+[@W2``/:;80T9Q_ZA!N5&"*R7:JJE#X`@`R%$,&]@S#`FPZ@%PW1'@ +MW2`,ON#=(``]IMA#1J7_-L$`#!QA20!1:`"B(L9RT@$,"Q8:"T+2(Q9Z&)+2 +M)H(I,*(I,:E!^$&#_PCY0>A!V$/#_AWI0:A!@@E2T_H2J4'X08/_'OE!Z$'B +M9LFR8Q&R8Q6R8QFYT[)C$+)C%+)C&+G#R4&H0=(I,9(I,-/Z"JE!B$&3^`*) +M0?A!L_\!^4'H0G%N<7)`Z(D\-(D\=E!F$&C^0B908A!@F5,'?`\_@`^IJ`0IJ)C +M(-@'0M(CN;%6K0BR9E^Y09A!#*J"!PBC^0690?A!@_\)^4'H0>)F8-(F8="N +MM&#=(])DPD"J(Z)DPY(F8I".M&"9(Y)DQ$"((_A!@F3%\L\!\F9@XB9AX-ZT +M8.XCXF3&0-TCTF3'XB9BH5,`X/T%X(P%X)L%H*X0X-ZT8.XCXF3(0-TCTF3) +M\F>)@F>(DF>'HF>&HB,@%LIX@J$_`#BFT!"F#"[R(R"BH0+9LR8O.K)C`[)C +M`/*B0,P=!@4")AUFDLW^%HDR@LW]%B@VDB>&5GGGPB3PTB3QV4&X0?ZR)"ZH<<(D+]+#0*(*Q.+#1/*@`1NJH*!T97?^V)&H<>(A"K(D+*(*Q<(D +M+?*@`:+*`:"@=*5U_K(D,*AQPB0QTL-(H@K%XL-,#!\;JJ"@=.5S_N(C$.)D +M+M(C$=)D+\(C$L)D,+(C$[)D,08A``"B)/&9<2JJ^JJR2NB""=39@>G!9A@" +M!B0"B,&R).C").K2).OB).FHA!DMDDF"FC_A+I08A!^%.3^!>)0>A!\_XE!F$'3^1Z908A!@F;)^&-67TNB)/&Y09A!@B3PH_D*F4'X08/_`OE!Z$&S +M_@'I0=A!P_T:V4&800PJH_D6F4&(0A!L_X2Z4'80;/] +M$]E!J$&S^ANI09A!F<6(P_C3@(#4B4'H0?#PM//^#NE!V$'9Q?CSF..HPXC3 +M@F,1@F,5@F,9HF,0HF,4D)#4F4'H0:)C&/#PM//^#NE!V$'9Q9CSJ..B8QJB +M8Q:B8Q*28QN28Q>28Q,&-_\`.J;0$*;ZDH+#.(F1V6."PSR)H1;-7LE1F7$, +M"(EA#!JE%.*B)4.@H`2I4:AAF%&Q#0(;JA;)'ZEAIZOA!GT``#JF@!"FTL,P +MXL,T^I*9<8EC%J@&@J$"`#BFD!"FF7,`.*;P$*8,&X*B0*AQ^8/")/$,'Y(* +MU"K,BLP;F9)*U+),Z*(*Q+(D*L(D*QNJH*!T94K^LB0NJ''")"_2PT"B"L3B +MPT0,'QNJH*!TI4C^PB,0PF0NLB,1LF0OAA(```"2)/&(<2J9^IFR2>B""-3I +MP=F!"X@6R&6(P;(DZ,(DZM(DZ^(DZ:AQ\B0JDB0KH@K$F0&)(8B!B1&").;@ +M"`#(P\)D+KC3LF0OD:P`Z,/B9"K8T])D*PN9\B:#%H]*%HE*@B=4B`@+F68H +M[)@'J$,6&52").7@"``,'.@S#`NP^@%P[A'P[B`,O_#N(``^IJBQV$/:JJE# +MTM(F@BTPDBTQF4'X08/_"/E!Z$'X([/^$.E!Z$$@F:#S_A'I0>A!DMDDF"FC +M_A+I08A!^%.3^!>)0>A!\_XE!F$'3^1Z908A!@F;)^&-6 +M/R>2)/&Y08A!\B3PD_@*B4'H0?/^`NE!V$&S_0'90:A!P_H:J4&80;/Y%IE! +MB$'#^!2)0?A!P_\5^4'H0;/^$NE!V$&S_1/90:A!L_H;J4&809G%B,/XTX"` +MU(E!Z$'P\+3S_@[I0=A!V<68TZC#HF,8HF,4HF,0DF,9DF,5DF,1QJG^#!H, +M"PP,R6&R926B914,&F7RX?(E)>(E%:AAP/\1\.X@Z5'848$-`J+*`28=!:)A +M!J>HV0P;LD.,@J$"`#BFD!"FF9,`.*;P$*;8D>BA@J)`J''YH\(D\0P?D@K4 +M*LR*S!N9DDK4LDSHH@K%LB0LPB0MHLH!H*!TI2C^LB0PJ''")#'2PTBB"L7B +MPTP,'Z+*`:"@=.4F_L(C$L)D,+(C$[)D,9&L`.CCXF0LV//29"T+F?(F@Q9? +M+!99+((G5(@(DLG_9BCKF`>H0Q:9-((DY>`(``PE!Z$&2V228*:/^$NE!B$'X4Y/X%XE!Z$'S_ASI0>A!T@U2L_X=Z4&8 +M0=/Y'IE!B$&"9LGX8U8_"9(D\;E!B$'R)/"3^`J)0>A!\_X"Z4'80;/]`=E! +MJ$'#^AJI09A!P_D6F4&(0A!L_X2Z4'80;/]$]E!J$&S +M^ANI09A!F<6(X_CS@(#4B4'H0?#PM//^#NE!V$'9Q9CSJ..B8QJB8Q:B8Q*2 +M8QN28Q>28Q/&-?ZM`KT#PJ`"I:7\#`L,'(8Q_@"M`KT##!REI/P,"PP<1BW^ +MK0*]`PP,I:/\#`L,'$8I_@P+#!KB)X?I8](GB-ESPB>)R8.IH[F3K0*]`PP, +MI:'^LB3PPB3QR4&H09@C#`BS^@BI0?A!HM(FXBHQ@_\0^4'X08A#(.Z@D_\1 +M^4'X0>+>).@N@_\2^4'80Y8Z(GB+T#J7.M`I(GB9F#^:.)D^66_I&L``N9 +MPB:#%@P9%@D9TB=4V`T+F68M[.@'J$,6SAV").7@"`#(,[#:`7#,$=#,(`R] +MT,P@`#RFJ+&X0[JJJ4.2TB;"*3#R*3'Y0;A!P_L(N4&(00P.N"/C^!")08A! +M(/^@L_@1B4&(0?+?)/@OH_@2B4'80B4'X0?)FR=ACZ4$6_1>M`KT##"SEB_P,"PP<1LK]5DG4Z''H3M*@ +M`M)N`*4?_`9-_P!6";:(<8A(\J`"\F@`91[\!M3^`)BQ@B3EJJFI0^`(``P< +MZ#.PV@$,"W"N$="J(`R]T*H@`#JFJ$/&._X`HB3Q*JKZJK)*Z(()U)EQ"X@6 +MR!.(H;(D[,(D[M(D[^(D[:AQ\B0LDB0MH@K%F0&)(8B1B1&").?@"`#(X\)D +M,+CSLF0QQB+_F+&").6JJ:E#X`@`#!SH,[#:`0P+<*X1T*H@#+W0JB``.J:H +M0X8L_YBQ@B3EJJFI0^`(``P>"(I0,+_D()1'\QIK_K0*]`PPL);;\#`L,'$:"_P``R'$,#PP[H@S$ +MLDS4LB0JPB0K&ZJ@H'3EY?T&WOT``-B1Z*$,#\AQ##L;JJ"@=+),U+(D+,(D +M+>7C_0;B_0"8L8(DY:JIJ4/@"`#(,["Z`7"L$;"J(`R[L*H@`#JFJ$/&A_^M +M`KT##"SEKOP,"PP<1FK]``"H<0P[#`^R2M2B"L2R)"K")"L;JJ"@=*7>_09K +M_@```-B1Z*&H<0P[#`^R2M2B"L6R)"S")"T;JJ"@=&7<_0:R_P`V00`,"`PY +M<1D"8J'04L(8,@7PHB4Z:F)"%DYZ:!X)GBQO,PF>*X!"FT@9_#"+,7?(ECO@/G-\R!0@P +M-T$P*8,=\*`0I@PBDD9^@F4Z@F>+@F>*'?``L@4(+0BPMT&,"QWPTB=T`#VF +MP!"F"\S`B8,M"!WP```V00!2H+A:4C(%4`P&H4D`&S,P,'0R15`F@T&"%=IB +M:E62)1+1W`&R%=L;F9)E$I<8(MHBPB*`,B)_&\S"8H#'&P7@$*8=\`!B8H`; +M,S)B?_`0IAWP@!"F8F42'?!B15#&[?\`-D$`#`D,/:'L`7+2(X+2`F+"&+(F +M.D(&\%(8-AN[&T1`0'1"1O!FA`*21O"R9CJWE3+22$Z29CJ29_&29_"B9RJB +M9RNB9RZB9R^B9S*B9S.B9S:B9S>B9SJB9SO(`PPB%HP.'?#B)_'R)CW"(Q2P +M[A'JXL)NZE(G\4(C%;!5$5I20F7KXB?QPB,8L.X1ZN+";NY2)_%"(QFP51%: +M4D)E[^(8-\(G\?"[P`ON&\S"9_'GO%SR(Q#R9RKB(Q'B9RM2(QA29RY"(QE" +M9R_R&#>P7!%:(O&Y__P$*8, +M(AWP`*)G+Z)G+E(G\*)G*Z)G*AM50A@W4F?PDF?Q1ZM#HFWKDGB(NKB9S+"(NO"9S.R(NZR +M9S:B(N^B9S=&S?]2(O)29SI"(O-"9SOR&#>WKUCB(NKB9S+"(NO"9S.R(NZR +M9S:B(N^B9S<&PO\``*)G,J)G,Z)G-J)G-\:]_P#R!@CP]T&\+RT)'?"P3!%* +M0K(DZK)G,J(DZZ)G,U(D[E)G-D(D[T)G-X:R_Z)G,J)G,Z)G-J)G-\:N_P"B +M)]H`.J:`$*8+B("=@RT)'?```#9A``P^D>P!#`KRHD!RPAA"!_"")SKZ8E(6 +M%H+(`:)&U$+$`4!`=$)'\&:$`J)'\()G.H>5-<+2(^)&#J)G.J)L\:)L\))L +M*I)L*Y)L+I)L+Y)L,I)L,Y)L-I)L-Y)L.I)L.[@##"(66R@=\+(G/=ACL+C` +M%DT,@B,B%F@>"\@6'!Z"TB/"*/%2(Q2PS!'*POK,4FQ:0BCQTB,5L$012D+Z +M1-)D6\(H\5(C&+#,$2VB!PB@IT&@+H,=\`""TB/"*/%2(Q2PS!'*POK,4FQ:0BCQTB,5 +ML$012D+Z1-)D6\(H\5(C&+#,$K%))H,I)H,Y)H-I)H-Y)H.I)H.P9Z_](&Z5(F8A9M%-+2$$(M\DI%0,`$ +M0$$AP$0@0F@ZTBWSPB9C#!3:S,#0!,#!(=#,(,)H.T)&U%(6%[\F@VTB5?TF@W!A+_(F@RPB5;PF@S +MLB5>LF@VDB5?DF@W!@S_-D$`#`FA[`&Q20!RH+AZB9AJB9AO")M>B9AZB9A]6;`G@$*8=\```G)2 +M9^I")M7R)NRP1!%*0O)DZ^(FU=(F[[#N$>KBTF[NPB;5LB;PL,P1RL*R;.^" +M&#=R)M6R)N@+B!MWTBWSTF8?XB;7%CX('?`` +M`(>70*)F'J)F'_(F\$(F[X(FZ)(FYYGFB?9"9A+R9A,6DP>PIQ&JHM(JZM)F +M%L(JZ\)F%[(J[K)F&J(J[Z)F&\;L_P``HF83HF82J?:")M2IYI)FU1N(@F;4 +M0B+J0F86\B+K\F87XB+NXF8:TB+OTF8;PB+RPF8>LB+SLF8?1MW_D!"F'?"B +M9A:B9A>B9AJB9ANB9AZB9A_&UO\``*)F%J)F%Z)F&J)F&X;2_P`V00`,"0P] +MH>P!5,L@# +M#"(6[!'22$Z29CJ29_&29_"B9RJB9RNB9RZB9R^B9S*B9S.B9S:B9S>B9SJB +M9SL=\.(G\?(F/<(C%+#N$>KBPF[J4B?Q0B,5L%416E)"9>OB)_'"(QBP[A'J +MXL)N[E(G\4(C&;!5$5I20F7OXA@WPB?Q\+O`"^X;S,)G\>>\7/(C$/)G*N(C +M$>)G*U(C&%)G+D(C&4)G+_(8-[!<$5`B@/\(FCL(L`!8\$^(&".#G0>`M@QWP`.><=?(G +M\+(C$+)G*J(C$:)G*U(C&%)G+D(C&4)G+Q:O^[#L$>KB4B[J4FK0J)G,J)G,Z)G-J)G-Z)G.J)G.P;,_P!"(O)"9SKR(O/R9SOB +M&#>WKDGB(NKB9S+"(NO"9S.R(NZR9S:B(N^B9S>&P/]2(O)29SI"(O-"9SOR +M&#>WKRSB(NKB9S+"(NO"9S.R(NZR9S:B(N^B9S=&M?\``*)G,J)G,Z)G-J)G +M-P:Q_P"B9S*B9S.B9S:B9S<&K?\`(B?:`#*F\!"F"__PG8,M"1WP```V80`Y +M(2D1.!&120`RPQB"`_`,%PP"&XB`@'2"0_!FB`(B0_"((0P$(FE5@@B,46@` +M80T"%C@&>0$;1`P:)=G@LB5#L+`$N0&H`18J`$>FZ`P$P!P(@!P'<1@'<@>67&````9Q0B&T0,&J75X*AEJ0&8`7>9[-(E%<(E)<#= +M$=#,(,D!N`%F&]GH(2).C+$<`F$;`H(C.E@10J'0&XA*16I5\A1.##:"8SJ` +M_\`6/PR!20`,J2)H7_(#\,T"D_P%\_P)PFA@XBAAX-ZT8.XCXF5H0-TCTF5I +MXBAB&\S@WK1@[B/B96I`W2/296O":&"B*&&@GK1@JB.B96RA4P!`F2.296V" +M*&*@J!"`S06`W`6`ZP6`_K1@B"."96Y`_R.2)9?R96^"%$_B9%/29%3"9%6B +M9%(;F9)EEY>8$Z@1PB66(F67NJH;S"P+PF66)=('N"&R*P`F&P+`$*;2!'_, +M7>(CCN@.K.[R`P@,(O#W0?`F@QWPJ!%B1'XB8SHB99X +M(;@+)AL.P!"F'?#2`PC0UT&,+1WP'?#R)8``/Z;@$*8+[N`F@QWP-D$`0>P! +MX1P"HM(!DB)`PJ'.RL*"'$\;F9)B0)"(P)$=`C"P=`P-FC(6R!S!20`,J-)L +M7_(*")T-@_D%\_D)DFQ@@BQA@/ZT8(@C@F.E0/\C\F.F@BQB&YF`_K1@B"." +M8Z=`_R/R8ZB2;&""+&&`_K1@B"."8ZF!4P!`_R/R8ZK"+&*`C!#`GK1@S"/" +M8ZM`F2.28ZSR(]2"8L;"(^:P_Q'Z\L)OZI(CU((CY["9$9J2@FGK\B/4PB/N +ML/\1^O+";^Z2(]2"(^^PF1&:DH)I[_(CU,(CZ+#_$?KRPF_LDB/4@B/IL)D1 +MFI*":>WR(]3"(_"P_Q'Z\L)O\)(CU((C\;"9$9J2@FGQ\B/4DB/GPAJW&Z^B +M8]3'NF^9X\(CYM(CZ>(CZ/(C[X(C[H)C$?)C$NGSTF,0R=.2(_&28Q3"(_#" +M8Q,6FQ.PVA'0TH#B+>KB8Q7"+>O"8Q:R+?*R8QV2+?.28QZ"+>R"8Q?R+>WR +M8QCB+?3B8Q_2+?728R#B(]8+[A9^"O`0IAWP"XR`BL!6Z`G"(^:2(^?2(^GB +M(^B"(^[R(^_R8Q*"8Q'I\])C$)GCR=.2(_'"(_#"8Q.28Q06J_NPVA':TN(M +MZN)C%<(MZ\)C%K(M\K)C'9(M\Y)C'H(M[()C%_(M[?)C&.(M].)C'](M]=)C +M((;@_^JB+`L,/_),@-)B0&6K!T)C%4)C%D)C'4)C'D)C%T)C&((CUD)C'T)C +M("88!9`0IAWP`!WP``#28]3"(]/JHK*@(,+,`<)CTZ6G!](CU+#=$=K2PBWJ +MPF,5LBWKLF,6HBWRHF,=DBWSDF,>@BWL@F,7\BWM\F,8XBWTXF,?TBWUTF,@ +M!K__`$)C%4)C%D)C'4)C'D)C%T)C&$)C'T)C(`:X_P`V@0"AK`"RH*B1:``, +M(X+2*XBHB0%V@!'"*4,+JHS\%OH\TB*4V`TF+0C&^?\````6ZCNZ4M@"PA(, +M\4D`@@(A0A(-8J(D:F+B!ML`1!&`@030B!&`[B#B;XWHO=BM0,P@`.X1X-T@ +MV8G";\:R!MW"`B"PNQ'`Q030S!'`NR"R:3BB(CB"(C?`J@'`B!&@B""):4(" +M)((&V^(&V#!$$8"($9#N$8#N($#N(.)OQ\(&XPP;0M(C]GP+@1X"@(R@B`B@ +M"```H?L!HF3E3%@,!WGIB=GR!AC8`0#_(W#_4R#_L/(O3MK_^?GB!B``[B-P +M[E,@[K#B+D[:[NGYP@5B(,RPPBQ.H=X!VLS)^<(&VZJB"\P6S!4<'-$A`N$@ +M`O$?`G)I.('K`8)J)?)J*^)J+-)J+<)*?,$B`L)J+M$W`')&*L(M\.(MW7RX +M@(X0@FW=\@;;\L_^%D\6D0@!D)P0DFWPJ2&M`K(E%L(D\H(DX+"[H,"[H.`( +M`*T"LB46PB3R@B3AL+N@P+N@X`@`L4D`P;0`TBN!UPP"AI$`W'KB!BO<+O(E +M:O(O`,ROD@8J)CD%)AD"9BFMMDH"ABP`J1%R)-NR)1:"(D`R805P>\"'MQIP +M-R"").*M`C"S(.`(`)(B0!LSES/LLB46.%&R9-G,EPP*HF3QHF3P1@4`K0>R +M%B7EC0>B9/&R%B6M!V60!Z)D\+(E%K>W`D8P`)(E:L(&*_(5XGS^"]NP_\#P +MWH/291D6C!.("8+(_A:H$ZBV#`F9"I)&*[(&,@P`]DRT#'?`M"AWPP?P!\2,"X20"T24"LFD5@@;@ +MTFHLXFHM\FHN@L@1@(!T@DI\@(@1P(@@@FHEP28"PFHK@>P!@F0R@F0S@F0V +M@F0W@F0Z@F0[1I__@0H!@(P@@FWP^-4,B?9"AO,TJ``TF3QPF3P@B3CK0+@ +M"`#R)/*X,>(D\+>)/#=H'J\TAT+V2Z2(D"Y,1N[ESNP +MLB461JO_B`E6&.PH$1WP`#)&*@P)AK#_X4D`\B5O#`F21C(;__)E;_%&`.(N +M@0R=\.X0X.M!X#V3+0,=\*$&`J)DY<9'_\$'`L)DY89%_]$(`M)DY49#_^$) +M`N)DY09!__$*`O)DY<8^_X$+`H)DY88\_Z$,`J)DY48Z_PRRP4D`?/L,'=)& +M*[)L@1WP`+I2XB5J.0ZEO/J1:`"&#/\``#:!`(MSRU.BH<]+D^M#:X.K8VE! +MB2%)89D1JJ)947DQ4J#P%#_$(#_(/)#`>$H`@`^IL`0 +MIM$I`@P9T,S`%IP4AD\`8)J@DBD`X*H1D)!T)EG+RLJR`P'(#,)#`)#`-%"[ +M$,"[(+)#`69)$_*B`@`_IM`0IN$G`N#=H-@-TF1VHB)`PB1_%MH$@B1SH(C` +M5M@(LA,!HB1VNJJ@H/2B4P'@#`"B9'<6>@T,&K@1I3(`#!JX(24R``P:N#&E +M,0`,&KA!93$`#`JX4>4P``P*N&%E,`"&*@``LA,!HB1VNJJ@H/2B4P'@#`"B +M9'<6*@D,&K@192X`#!JX(>4M``P:N#%E+0`,&KA!Y2P`HJ``N%%E+`"BH`"R +M(0;E*P"&&````+(DC*"JH#WPL*J@HMK_HAIWX`P`HF1W%LH"#!JX$:4I``P: +MN"$E*0`,&K@QI2@`#!JX024H`**@`+A1I2<`HJ``LB$&)2<`TB)`XB2,PB1V +MT-V@X-V@TMW_TAUWVLS"4P$,"0OI%G[G'?``HB)`G.KR)'.")':@FJ"G'^2R +M)(RPF:"2V?^2&7>:B()3`0;T_](3`<(D=MK,PE,!AO#_`#9!`)+2)I(I,G*A +M`C"#H)`XH``WIF`0ID*E/PP8#=(``]IL`0 +MIJHRD:P`=H`1@B2#"YF,Z!;)$*(C<:@*)BH'QOG_```6R0^R!0#!+0)PNQ'` +MNR``.Z8,&H(FU>(%`;'>`<(FUN"5!+JRT@M^X.8$@_P(\BL\@A4!H_P0#`4@ +M_Z!Z__(O)Y/\$8/\$O/\%^/\'%/\'0P^T_P>W07"9,F2*SSR*SO!:`"3_0KS +M_0)3_0&C_1KC_1:C_12C_153_1)3_1/9S-(K.[(K/-/["+)L3*)D7Y(#?(T% +M4_@%D_@)@F1@4F1A4F1B4F164F15\@-\&__P\'3R0WPFCS#`$*8=\`"1K`"J +M,@N9TB2#K%VL:>(C<>@."YEF+N[R!0""H0MP_Q&`_R``/Z:&RO\`4D-\@!"F +M'?!6&?Z2(W&R:0`E?/I&]?\`HB-QN0IE>_K&O?\`-D$`O%(A+@(`,J8@$*:\ +M8@PF@%(18%4@`#6F8!"F#!<+0D?6#P`20`"'H8"&P!N(@E,`'?!B4P`=\``` +MD2\"`#FF(!"F5G+\#`JB4P`=\``V@0!BI#_2H._"H/"RH0+Q,`(<3E+2(PP) +M0J(J2D*29=P`.Z:@$*:2`P&@H`3`JA'0F1"@F2"20P&0A`16B`L`-J:@$*8, +M%Z>^+I@/<"($)"((()#`:'J`0`ZIJ`0II$R`@P8 +MD*K`H*B39AH"!MW_!@D``.(#`?$Q`L#N$/#ZH/(O`/)#`)#P-/#N(.)#`=(" +MJ-+-_A8]#9(#`3WP1^DID)`T@LG]%D@*HLG\%NH)O&DF&32RR?X6ZPX=\`PZ +MP)D0H)D@DD,!1VG5#`NR4P*R4P2R4P:R4PBR4P.R4P6R4P>R4PD=\````+(E +M,L(E.J(E*J4C_;(E,\(E.Z`J(*(E*Z4B_9+!$.(EZ=(EZ\(EZK(EZ/+!%(T* +MH@3:^2&9$8D!@B7F_0+@"`"2`P&H4;A!LE,"LE,$LE,&LE,(HE,#HE,%HE,' +MHE,)D)`T1MG_#`S"4P+"4P3"4P;"4PC"4P/"4P7"4P?"4PD=\```MB<"1LG_ +M`#NFH!"FD@,!H*`$D*H1D)!DH)D@D)!TDD,!D-=!%DWPP+D0D-`T6]W0T#30 +MNR"R0P$&O/\``+(E,L(E.J(E*J47_;(E,\(E.Z`J(*(E*Z46_9+!&.(EZ=(E +MZ\(EZK(EZ/+!'(T*H@3:^2&9$8D!@B7F_0+@"`"R)3;")3J8<:AAHE,"DE,# +MI1S]LB4W+0K")3NH<>4;_?+!'+(EZ,(EZM(EZ^(EZ9+!&(T*H@3:F1'Y(8)A +M`((EYB#R(.`(`*(E+K*3`I(A!\AAPE,$DE,%Y1[]LI,#+0K(<:(E+R4>_?+! +M'+(EZ,(EZM(EZ^(EZ9+!&(T*H@3:F1'Y(8)A`((EYB#R(.`(`+*3`L*3!)(A +M!ZAAHE,&DE,')1W]LI,#+0K"DP6H<64<_?+!'+(EZ,(EZM(EZ^(EZ9+!&(T* +MH@3:F1'Y(8D!@B7F_0+@"`"8<:AAHE,(DE,)'?```#9!``P&0M(C4B3R8F3< +M,&.@4%:@D@4!#![@9A%'Z7C!)P*BI3_RH<_2H@*0D#2"R?T6^`FRR?P6FPD` +M.J:0$*;P@H"VN062H`#B2(#A,P*R!0"B!0'@Z:#H#J"@-.J[LD4`9AH.`#VF +M\!"FP/^@\B\`\F3B9-W*N[)5`1WPFD!"FLJ#?@@4!D)`$L)D1L(@0D(@@ +M@D4!`#JFD!"FMKD(DJ``\**`XDJ`\2L"X@4`L@4!\/F@^`^PL#3Z[N)%`&9+ +M#@`]IH`0IL"(H((H`()DW)("("=I**(%`:"@-"9*!+(%`)R;`#>FT!"FXJ"_ +MP@4!T-`$H-T1X,P0T,P@PD4!LB3E%O,+XB39-YY(PA4!HB3`+`*)DW1WPPA4!HB3<\"`` +MRJJ@H/2B50'@"P"B9-T66N8,&DNUI:;_#!JRQ08EIO\,&K+%"*6E_PP:LL4* +M):7_#`K+M:6D_^NU#`HEI/\=\```-H$`H2P"#!P,"ZIR8B?704D`,%.@8%6@ +MD@4!XJ",86@`1^DS#"V0D#2"R?T6F#;RR?P6/S;J,I&L`':`%8(D@PN9%K@) +M%KD)XB-QZ`[BSOX6#@K&^/\``(+2)M(H,*(H,>J2\A4!T_H(T@A2P_H=\_H2 +MT_H>HF3)HB@Q_0R"*#"C_PJM"X/_`K/_`(GUO/^".)F3!WP````5OD`@B-QTF@`)1#Z#`NA+`(,')(GPH(% +M`+"9`7"($9"((`RYD(@@`#BFXM(FT@4!@B?5\BXQZ0&2)];B#E(@_Z"#^0BJ +M_]"%!(/Y$8(5`?(O)]#0-(/Y$O/Y%[/Y'>/Y'J8]`L/Y$))DR9(%`9"0-(P9 +M9AE:F`'RE0.-"Z(I,?#PM)(I,*/X"J*5`I/X`K/X`3%)(:*5`CD1#`Z-#OT..`&I44*5`Z"@ +MU'(C,$!`M)(C,4/Z#D*5!9/_"I*5!W/_`D!`M'*5">/_`>/_&I"0M./_%N/_ +M%./_%>/_$N/_$_G&J<;R(S&PH-1#^@Y2(S!-#O/T"@P?4_0"X_0!X_0:X_06 +MX_04X_05\_02X_032<:IQE(C,4(C,*T.4_H*0_H"P%#4D_4.X_H!G0[C^AKC +M^A;C^A3C^A7C^A+S^A.IQEG&0B,Q<%"THB,P>#%#^`JC^`+00-2A#@)3]`[C +M^`%80>/X&N/X%JHBX_@4J%'C^!7S^!+S^!.)QDG&0B,Q,B,P@B)_0_D*,_D" +M2"$X$>/Y`?/Y&O/Y&./Y%O/Y%//Y%>/Y$N/Y$YG&X`@`LI4%PI4'TI4)@B)_ +M+0JBE0/@"``,'-*5`R"`U*"0M`P+HI4"L.L@D_@.L_X%DI4%@F8,T-"T@I4$ +MPF1?H*#4T_H.\@-\TI4)D)"T@(#4D_@.\_X)T-"T\I4'XF1@HF1AXI4&HI4( +M\/"T@F1BX.#4\_X.H*#4T_H.XF16HF15D@4!D)`THLG[%NH5P@-\&\S`P'3" +M0WPFC!#P$*;B)]72)];C_0C29DP=\+)#?*`0II(GU8(GUI/X"()F3!WP\B?" +M%I\:\I4"#'.`_Q$P_R``/Z:0$*:"E0,,B8"($9"((``XIC`0IO*5!`RC@/\1 +M,/\@`#^FD!"F@I4%#,F`B!&0B"``.*8P$*;RE08,TX#_$3#_(``_II`0IH*5 +M!PSI@(@1D(@@`#BF,!"FD:P`ZC(+F8(D@Q;8$1;9$>(C<>@.DLG_9B[K\@4` +M@2T"IC@"P_D0##V29,F")];B)]6M"X/Z"N/Z`K/Z +M`)D8-)D8=)D8M)D5M)D58:-_P!6 +MR>ZR(W'2:P`ER_D,"Z$L`@P<1K;_````D:P`ZC(+F>(D@YS>G.GR(W'X#PN9 +M9B_N@@4`DJ$+<(@1D(@@`#BFQJ[_`%:9_I(C<=)I`.7&^0P+H2P"#!R&]?\` +M`#9!``PY#`J!-`*Q&0)B(D!RH+A2H=!:4GIR,A?:&V9B9Q*P(H!GDS=!-0*2 +M17["(G.B8HNB8HK`QL#"8G4`-*:P$*9F&U4AO@%V@`X`,J;@$*8`-*;0$*9F +M'4"&^O\``#(BB_(53QLS,F*+-Y\*LB**HF*+&[NR8HK"!7]A*`)1*0+,;-(G +M9M(M`)RM`#:FX!"F#")0[L#@*8,=\``XIO`0I@PB'?`````VIC`0IE<3`PP" +M'?"R(G.B)Q*PJL"B8G4`.*:0$*8,,AWP`#9!`/'D`7+2`4(#`6+2(](B0$!` +M-$+$^19$'Z'J`;$R`I'L`0P.##S2S0&"H=!2%[:`@H#28D#7E6'"2'[B9O'B +M9O"29BJ29BN29BZ29B^29C*29C.29C:29C>29CJ29CL`.J;0$*8Q-@*PW<`6 +M_1``,Z;@$*9F'A$A-P(`,J:`$*8`,Z9`$*8F%/``.J:0$*8,XK"9P!9I#AWP +M`,(B0_(F\>*3!E*AT+#_$?KR6O_B;W9")O'RDP>P1!%*0EI$\F1WXB;Q\I,( +ML.X1ZN):[O)N>D(F\>*3";!$$4I"6D3B9'M"&$]2)O'`S<`+1!O5TF;Q1[U/ +M4I,%0I,$XF8O\F8N4F8K0F8J0A>WL/T1^B)'+`+&+`"29C*29C.29C:29C>2 +M9CJ29CN2"'_1*`(6>0P`/::P$*;!*0(,.@PBP+O`L"J#'?!"%[<+1$>=1T*3 +M!%*3!>)F+_)F+E)F*Y)F.Y)F.D)F*C(7MSC`H8[`))F,I)F,Y)F-I)F +M-X;I_Z(FV9@'H)G`DF;;`#^F@!"F#"(=\.(F\))F+Y)F+I)F*Y)F*@P/\F;Q +MTA>W&^[B9O#7K&^29C*29C.29C:29C>29CJ29CM&U__B(O+B9CI2(O-29CM" +M%[?'I'72(NK29C+"(NO"9C.2(NZ29C9"(N]"9C<&S/\``.(G5.@.5O[R`#JF +M\!"FMY\"QB``#`(=\$(FV4!-P$)FVP`_IB`0I@PR'?``XB+RXF8ZTB+STF8[ +M4A>WQZ5)PB+JPF8RDB+KDF8S0B+N0F8V\B+O\F8W1K7_DF8RDF8SDF8VDF8W +MAK'_L.T1ZN)"+NI"9C(R+NLR9C/R+N[R9C;B+N_B9C>&J?^29C*29C.29C:2 +M9C?&I?\`@B;92`>`1,!"9MLAY`$`,J;P$*8,,AWP-D$`#&@,&G$X`M$Y`@PL +M##MV@"F`F!&PZ2``/J:`$*9PB*"(",#I((!@]9PF`#ZF8!"F@/#T<'^@@(#U +MK$K&\__0^*!R'P&`=Q'`=R``-Z9@$*9B#P#R#P$`9B-I!`#_(_)E`(@$DJ^` +MEY@=LJ8"`#NFP!"FPLS@PF0``#NFH!"FHLK@HF4`@B0`*FAI!"@%.B(I!8@$ +M,J_`AR,'@LA`@F0`*`4G(P@BPD`B90"")`"FV`B2R,"29``B)0#FT@$=\*+" +MP*D%'?`V00`,:`P:<3H"T3L"#"P,.W:`*8"8$;#I(``^IH`0IG"(H(@(P.D@ +M@&#UG"8`/J9@$*:`\/1P?Z"`@/6L2L;S_]#XH'(?`8!W$"R$""9``H!2B"]0PS!&`[A&0JA'@JB#`JB"B9<=R8T!R0WR2 +M0WIY[8G=\@L4`/\C(+'`#N(W#N4R#NL.(N3DKNZ?W" +M!O8@S+#"+$Y*S,G]L@O7"[L6NQ;Q/@*!/0*"8RCR8RZ8`9()ZA;Y$J$_`J)C +M(-@!\AUML3<`D(\!X/\!BO_R8T&B*_#"*]U\ON#L$.)KW=(-U]+-_A:-$($( +M`8"*$()K\*@!8M(!DA:V#`1R2B:LB7'-`7IR@B,HK0)PMR#@"`"M`K(#?((C +M+@P)DD-ZX`@`HA:V&T2G--P,![@!L@O7W%N!P`$`.*;P$*;BH0(`/J;`$*;8 +M`<)-Z0QL`#RFL!"FHB$`H@HGDB*4%AH-V`G2S?X6'0VA:`"(`0P)HBI,PA@C +MZ*B@N'3`NX*@H'2ZJ@OZH/F#^>AY#L@!#!VR#"[23"5R3"<6BPKR(ID;__)B +MF>(E@:(C.H(C.Y'$`("J$:"(()"(()%H`*T"@FE,/Y"+('?@P>X_D0P_D1S0W3^1VS^1Z2:LF")M?R)M:1:`"#_`KS_`+C +M_!I#_!;C_!3C_!73_!+3_!/)R;(FUH(FU[/X"()I3!WP#&N11`)&PO\`LJ`` +MPJ``I0$`DJ$"HF,#1M/_TB*460WE+/FA20`&V_\``#9!`!Q)'`T,C#S+/`\< +M?@S*H(,0)^0HIP,])DAOPLCX%EP+9J@KD#(0C-/2P_P6[2'BP_`67B>7DQ>P +M)3`=\`"G`S`F2&YBR/@6A@O2R/06K0T,`AWP``"0,A"7`BIBP_P6=@Z"P_`6 +M6!"7PB*P)3`=\```D#(0EP(.8L/\%L8-@L/P%M@/E\(N+04=\)`R$!83$*+# +M_!;Z$<+#\!8,%I>3.5#5!!9-+E#C!!8>*BR"("4P'?"0,A`6PPYBP_P6)A&" +MP_`6R!27DSU7Y;M0HP062BC0)3`=\`"0,A"<`\+#_!8,%-+#\!:=&)#CP%:. +M\U#U!%;?(5!C!!9V'2S"("4P'?```)`R$!83#F+#_!9F$H+#\!98%Y>3%5"5 +M!%9)*E"C!!:Z)BS"("4P'?"0,A`64_5BP_P6=A*"P_`6"!B0H\!6RO"P)3`= +M\```4+4$5FOS4-0$%OT4P"4P'?!0Y0167O)0]`06_PW`)3`=\```4&4$5B8: +M4(0$%O@5/$(@)3`=\`!09006YN]0A`06"!6@)3`=\%"E!%;:[E"S!!8K%=`E +M,!WP``!0Q016K.U0XP063A30)3`=\%#U!%:?[/:%`@9(`%>^`H9&`!R"("4P +M'?```%!E!%;FZO:%`@9#`%>^`H9!`!R"("4P'?!090065NE0@P06.!20)3`= +M\```4)4$%ID/4*,$%LH5+((@)3`=\`!09006%@]0A`062!6@)3`=\%"5!!9) +M#U"D!!9Z%O`E,!WP``!0M006ZPY0PP06/!:0)3`=\*`E,!WP``!0U006'0Y0 +MY`06#A#P)3`=\%#U!%8?XE!D!!9F#\`E,!WP``!0A016F!/VA0)&,`!7O@+& +M+@`L0B`E,!WP4&4$5E82]H4"QBL`5[X"1BH`+$(@)3`=\```H"4P'?!790(& +M(0!09`06=@P\0B`E,!WP4&4$5I8/4(0$%K@+/$(@)3`=\``L@B`E,!WP`/`E +M,!WPP"4P'?`#`UA20!QW@'!:`")`WIRS+@, +M:Y%%`@PE##2&&0``V=/9PXA#DB<\HB<[FHB2!WZC^`C3^!#C^!V3^!Z"9LGR +M)SRR)SNM#O/Z"K/Z`N/Z&M/Z%N/Z%./Z%=/Z$M/Z$ZG,V"(I12:``ES/@&V_\` +M-F$`#&N110(,)0PTQ@,````\IK`0IJ"P]:"`])"8H("[$4#+(``\IJ`0II"J +MH*@*4,L@H-#U5FW]H+I!@+L14+L@`#NFD!"F#!QA20"!W@%Q[P$,"Z"0E)DS +MN0.A:`!ZPF;)PBL\LBL[T6@`P_D*L_D"H_D:0_D6H_D4 +MH_D50_D20_D3F"(I12:``EKO@&V_\`-D$`P!"F#`ZBH="RH+BZ +MLI(K$JJB@AO:&YF2:Q*7QW@'1$0(Z,MK"LBS7DB,\@B-!(+N@()F@VIG: +MN[(K*+)LOH)I*/(C/-(:3QO_\F,\]QT,'?`,.()*?N)K$AWP`.)C/.)C0:(C +M.^)C([(C(ANJHF,[Y5<%#!P,"Z"\@[)#?AWP-D$`048"2D*")-W,&(`0I@P, +M4>P!TB)`DJ"X8J'0:J*:DK(9VAO=TF)`T+O`%GL)<3P">G+R)SOB)TFP_Q'Z +M\FK_XF]VTB<[LB=*L-T1VM)JW;)M=Z(:3[(G.PNJ&[NR9SNGNS*").J2).F2 +M9!2"9!46\PNB)-NPJA&JHFJJTBIVTF0AWP-D$`,B)+LJ14/?"M`^4S!8*B +M()%(`J%'`J)B=Y)B=HJ#?/(B:'\B:(`,`AWP````-H$`N'.B(P:PJH*QK@!" +M(DN@JA&PJH"EPM,,"K%)`LT$Y<+3#!KEWM-2U`,,"()E4'(B&G)DWO(B'/)D +MW^(C(-(B%NK=TF3APB,ALB(6RKNR9.*B(R*2(A:JF9)DXX(C(_(B%G%*`(K_ +M\F3DTB>$XJ_QX-T0TF>$P`"FC)QV@`20`*:,&0;]_^`0IN)E+-`0IM)E+<`0 +MIL)E+K`0IK)E+SWP/?"@`*:,FG:`!/``IHP?!OW_P!"FPF4PL!"FLF4QH!"F +MHF4RD!"FDF4S/?`]\(``IHR8=H`$T`"FC!T&_?^@$*:B9320$*:2936`$*:" +M93;P$*8]\#WPX`"FC)YV@`2P`*:,&P;]_\`@`(`0IF`0ID`0IO`0ID$W`&%/ +M`!P-X@7#LJ`@HB,:XL[_X+V#Y2T%?*VB54J"(QR"54OX<_)53.AC\>`!8F4< +MHB3PXLX_\.X0LB3=XE5-#![0VQ#@W2#29-W"!<,L"1;\#[$(`;"Z$+)D\-(# +MX!R.\J"`T-`$TF538-T18-T@\-T@X-T@D)T@DF4;PB,UEVP(XJ8`X.D@XF4; +MH@/AHF52@B>$DJ!`D(@@@F>$\@7!\/`4)C\(LA5,\+L1LE5,PB(APF4@=H`0 +M\B3G^0'H`>#@%.D!V`$F/0(&^O^R)*RA.0"PP`22*B`':Q-\[=#;$-)DK,`@ +M`((DK()A`<`@``PNX.D@XFH@P"``DBH@P"``=H`0TB3GV2&((8"`%(DA^"$F +M/P0&^O\``'S?\/D0\FH@P"``XBH@Z3'`(`",3+)DK((DK':`$+(DY[E!J$&@ +MH!2I09A!)CD"!OK_P3X`PF3:'?#2!<0+W59][_$*`>(57/#Z(/)D\/:.`H:[ +M_PQ8@(L@@F3=QKC_````-N$`B)2&B:QB2)2*2:QF")2.":QKR)23R:QOB!<&B%4V2%5W@X!3BSOT6KE$; +MF="9$9":@C+!((OQXL$80B4@@B4HHJ_`0L0_H$0026%`29#@B*!"Q#^@1!!) +M(4I)0L0_H$0028%`29!"Q#^@1!!)04I)0L0_H$0027%`29"("(F;0L0_H$00 +MTB4H23%*24+$/_#=H-@-H$0029'9NX(E*$!)D$+$/Z!$$$E1,(B@B`B)J](E +M*$+!$(+FQTB2FV;&2).69L8(D]XFQ8B3X:;%")/!)L3(LBCFQ^`_YL>B+Z;'2 +M*QS9L9C;F;&(^XFQ:.MIL4(K$$FQ,BL1.;'R*Q/YL>(K$NFQTBL4V;&8FYFQ +MB+N)L6BK:;%(RV%H`$FQ0B4>,BL7.;'R*QCYL>(K&0Q/Z;$,7M(K&MFQDBL; +MF;&")D:)L6(F1($Y`&FQ88L`0FA*,@7##`1"81$+,S#O@S*CW``>0`#:H3HW +M,F$0V0&8`9)H3&)H3HPZ@B50C)A\^Z(E&M%*`D8&``P6PJ0YP9C!D)`4F<&(P28X`\;X +M_P`,"X(JW8G1Z-'!PP`,+_#N(.)JW7:`#N(JW>G1F-$;NS?I"+<\!:$W`,;Y +M__C1?-@]\(#_$/)JW7:`$,(JY\GAN.&PL!2YX9CA)CD&H3<`1OG_`.(A$N>] +M!0PR'?```-(E&K%+`@P,R0V2*L0A.0`,'["9$)D!B`&":L3R8BX,`AWP``"0 +M$*:294.`$*:"943P$*;R947@$*;B94:B%4HF%@RR)4P,;#WPP+L0LF5,IY8* +MTB5,#"[@W2#294P,"?(##](#"^(##<(##+(#"J(##NK,VKOZJLJ[NJJR$P2B +M94V($Y)E29)E2[#V5("(=8)A$Q9?'+"&!+#+!+":!+#9!,J9L,@$VLRPUP3: +MB,J(D(B`A[H-#(WA-P#RH`'R94G2;K""(1,,&8")DX)E2IRHLA,!HB5+L+`$ +ML*H@S,H,3-$W`.*@`>)E2<)ML/(E3H(E1X)E2(Q/DB5+5FD`K0>]`Z4Z`B(A +M$QLBO$(,!(QT@A,`@L@!@E,`DA5*HB$3/?!GF1",&J>4"\(E3-*@!-#,(,)E +M3*T',+,@)3\"&T0GE,OH\9(%PQP(+`\+F9#X@_HS-YX",B$08B$1HA5*8L8! +M8F$18L8!9[H"QGK_@B505EC>DB$0,)G`5CGLH`"FC)IV@`2P`*:,&P;]_X`0 +MIH)E-_`0IO)E..`0IN)E.=`0IM)E.CWP/?#``*:,G':`!)``IHP9!OW_X!"F +MXF4[T!"FTF4\P!"FPF4]L!"FLF4^/?`]\*``IHR:=H`$\`"FC!\&_?_`$*;" +M93^P$*:R94"@$*:B94&0$*:294(]\#WP@`"F%NCA=H`%T`"F%EWAQOS_``#B +M!<,+[A;.IR$Y``P?\F(N#!(=\((%P8"`%"8X"I(%PCWP9AD"!B``TB4H#!O" +M%5BPW3#292APW:#";>JB)2BPJC"B92G&D?X62@?Q-P`,+@P8@F5)XF^P!I7_ +M`'SM*YG0F1#@F1&0FH(&M_Z"!<16V+>B!<&@T!32S?T6';>"!<(6.`?2%5J2 +M%5C7F3J2)2@+BA;H".#9H-@-V=NB)2CPJJ"H"JG[AN?^\B4HXA58"(H(@(B=O2)2GPW:#8#=G[HB4I +M,*J@J`JIZY(E*4"9H)@)DFL0QLC^,-F@V`W9ZZ(E*$"JH*@*HFL0AL/^TB4I +MX-V@V`W9VZ(E*?"JH*@*J?N2)2@PF:"8"9GK@B4H0(B@B`B":Q"&N/X````V +M00`BI(`=\#9A``P:)4G3D4H`#!W`(``,Z_*AV/KR@B^;TF^:XB]D&XB";YO9 +M#L(IA,D!N1&H$8@!H(@@@FF$P"``'?`````V80"RI%12(DMR(A""PCB)`6B' +MK05X=_MF8&1!"PF53L+N0D+L1LF56NMO295F!30+X`>%.`I*A3+*A9**A6*JENK6: +ME9D5N36I)0QKHB79XF]I@F]HY3S=#`(=\````#9!`+ASHB,&L*J"L:X`0B)+ +MH*H1L*J`9133#`JQ3P+-!*44TPP:I3#3HB(APJ"PTJ*XLB39VM3*PZ)K()(C +M.))$+((C.8)$+?(C.O)$+N(,P.)$+X(,P@PJ#!XF&!;RR/X6_Q^2R/T6>2!F +M2`H,2[)D+88``.)D+?(,Q/)$N+(,Q;)$N9(,QI)$NH(,QX)$N_(,R/)$P((, +MR0P/)A@.DLC^%GD<9C@(HF0OA@``\F0O@@S*@D3!L@S+LD3"H@S,HD3#D@S- +MDD3$@@S.@D3%L@S/LD3&H@/L#$F-`Q:Z!D"D(':I99((\(+($))*-*+*$)(( +MX9)*)9((XI)*)I((XY)*)Y((Y))**)((Y9)**9((YI)**I((YY)**Y((Z))* +M+)((Z9)*+9((ZI)*+I((ZY)*+Y(([))*,)(([9)*,9(([I)*,I(([Y)*,Y(# +M[:T$%KD&DJ`$=JEED@R`PLP0DDITHLH0D@QQDDIED@QRDDIFD@QSDDIGD@QT +MDDIHD@QUDDIID@QVDDIJD@QWDDIKD@QXDDILD@QYDDIMD@QZDDIND@Q[DDIO +MD@Q\DDIPD@Q]DDIQD@Q^DDIRD@Q_DDISHB0OLLK^%DL)@@0LP@0`#!^`S,#` +MSY/"1`&88_N9D)1!DF0VN'.")"W[N["T0;"9@K)D-PN9DF0X)B@%)C@"'?`` +M@@2X%E@`L@V0A[L(HB0OXDUHXD2X@@2Y)BI>%E@`P@V4A[P%XDUHXD2Y\B0M +M9C_,@@2Z%E@`D@V1A[D%XDUHXD2ZHB0O@@2[)BI!%E@`L@V5A[NHXDUHXD2[ +M'?#R1`%&W?\``*)D+4:#_P``XF0O1H__##_R9"U&?_^,:)(-DCWPA[FEXDUH +MXD2Y!N?_C'BB#9.'.@(&V?_B36CB1+L=\#:!`*T"D3D`,B)+@J#_@FF"I3($ +M8J%<:F*M!N5`W:T##`(B8S\E*`!="MQJ,`#$+2HJ#:TP95```` +M``R5D:P`(6@`04D`#"<]\':`&X(D@_@RXB)#"YF`_R#P[B",[HSYPB/'J`PF +M*A/&]O\```!6>0"R(\=Y"R4=!,(CQPR]UQ4$Z`QF+AFM!@P+\J!D@B/9#`F2 +M;`#R:#7E$=VM`R4@`+(C/Z(C.+JJE^]S80;'C`.*@K_(M'PP,PDV`R0\F)`+&W/\&B/\`O0/E&P3&H?^"HJ"* +M@X((@8S8HB,XO0,;JF4:!`PJ1@``#%JR(RV2H(2:DR8[#<@3N"/8,]DCPF,# +MLF,!XBGS&^[B:?.B8\\&O?\,M0P/^0P&<_\`@B/'>0@E_0/&FO\`#`(=\)@, +M5BGUHB-$HF,_QN#_#`G&T_\`PBT?#+4,"[D,QF;_-D$`#"P,&E%1`@P)?/-" +MHJ1M`G(FV2(B+4I&(YTO@/Z#9Z__GRZ`[8 +M%GKNZ?+8#0PE>MW9\K(F+_(&`4(F+;++_A8[">+$_19.#1;?#:)B%:)B%C(F +M-D(F-\@6"S,+1$!$$<`S$4`S(#)B24(F+WR[/(,6%`<+E`P$D$6#T/01,/\0 +M\F)*Z';89L#N`<#=$>#=(-)B1L@,03<`>LS"8D1R8D=R:,4B)/!R)-V!"`&P +MMQ"R9-VB)BV`@A`F.BZ"9/`,`AWPL5("LF8^1J#_``#2IQ_29CZ&G?\F-$.B +M8A6B8A:28B7&VO\,%,;C_S$*`5%/`%)DIE(DIH%<`#`R(%!0=(!5(%)DIC)D +M\/(F-@R.#`+WOA\,2("'(()DW1WP`))B%:)B)9)B%L;)_Z)B%<)B%H;'_PP" +M'?`````V80!@P'10L'1`T'3A4P(P\'0`/J:`$*;PG1$@W9!R#;@`6",,*`LW +M)A<0C-6`HQ&`JB``.J9P$*9R80!"I1\6W!(`-*9@$*9B8D\`=B-R8D^03Z`< +M!BP/?`D@1*`6,Q`6!1!0<6``$T"H`0MW`'>A>JH;JJ!P8%"GHU(D1P`30`!F +MH0"9H19+#U!1(:I54F1'EZ4(`'^A<%6`4F1'9R4+`!-``(^A@%7`4F1'\)41 +MDF1'`#ZF4!"FH@VY`%4C"SHF&A06%0&2H`*`@Q&0B"``.*9P$*9R80$6S`FB +MI1\`.J9@$*9B8E``QB/"8E`7I0<`?Z%Z55)D1V>E +M`D;'_P`30`"/H8"%P()D1X;#_P`6!092)$BJ55)D2)>E"@`30`"?H9I54F1( +M9R4+`!-``*^AH%7`4F1(L@+25MOXPB(O9BR'#`+PU1'29$@=\```@B1(XB(O +M70CBSOY6+O.`42%29$A&RO\`#`+P]1'R9$@=\```@B1(77_PP"'?`V00"15`)1:`!RPFBQ20`,-L(B0M(B +M0PP(30BM"-/Z`,/Z"&/Z'J)KR3(B+:(B0PP;,L/]H_0*8_04@_0:L_0L(-,$%BT)(B\-81T-"TT_P.#`TPWH/3_!S)Q=(B3\(B4&(G2*(G1T(B +M+WSK&^J@ZK.P[A`F)"WP-A$P,2$;\S#SL["_$`P?IAH"!BX`#`_&+````)"@ +M!&"PM+/\#J/\',G%1NW_8$$A&S0,%D`TL["S$`P3.0'F&@,,#_D!,@+!YA0! +M#`:\,]Q9^`%*1D!!(4I,"T2JKZ"A(:JM!A,```"@^I!`1)"H`4I&0$$A2DP; +M1/JJH*$AJJW&"P``W)F@^I!`1)"H`4I&0$$A2DP+1/JJH*$AJJU&!`#X`4I& +M0$$A2DP;1*JOH*$AJJV@,-0,'0P&\$010,"TD&V#P_,.8_,<.<7&#`"JKZ"A +M(:JM#!WF%@$,#6IM#!-@82%J;!OV"V9`;Y,,#T#S@_!&$4`PM*!@U#/V#O/V +M'&G%X,#4B<5`\2$;VJ#:LQMOL*"T0B(OT($A\&^S8&$A\(@1@(#4\&818&"T +M)B0QH_P."_0,&PP.\.N#X_PO\&!P!C^`Z0T`2C_`X,&PP$D$N#T_PT()@&(J2C(J24!`!&!@M#`PU&/S#D/S'#G+6"I0(`.*9P$*9P<'1R0YT6-TBB`ZR& +M"`"B`ZQF+-X'Z@(7:MBBH0(`.J:0$*:B`ZR20ZT]\$?JQW(#G;D!-^H)!VHL +MP@-;N0&L3*T"LJ``P@-HT@-LY3__T@-KLB$`9AT+XB,N\B,M\F,QXF,RH@.L +MN0&@@@16N!,7:@T<^@`ZII`0II)#K@8!`#S\PD.NH@.L!VH0T@-;C*WRH0(` +M/Z;@$*:B`ZR"(Q.@D!2"R/X6Z!.<2<(#K@=J&M(#79T$4_D`G!U3^0T&`P#B +M`ZQ"0ZX,#`?NY`P)#+[#^0&`V1'@W2!`E"``/::B(RF"(RCR`ZRC^0"#^0@' +M;P)3^1"A4P#R`UQS^1'!20",'U/Y%O(#KN(#K=(#G//Y%^/Y'4/Y'M/Y'Y)L +MR8(C%7%H`.%4`H+(_A8(#H++_1:829++_A:Y%`O+%FQ*TB,IP@.L\B,HG03` +ML`3S^0+3^0I3^113^14';#D,.(/Y%E/Y'$/Y&A;[,J"Y(+G'TB,IPB,H#`+3 +M]`##]`A"9TP=\-(C*<(C*`P"T_0`P_0(0F=,'?``#,_P_!#RS_067T+`@@06 +M>`U3^1;&[/\`K0(,&\(#:0P-92G_D@-KN`%F&0NB(S#"(R_"8S.B8S2B`ZQ& +MI_\``+*A`@`[IJ`0IJ"@=*)#K,9>_P``-^H*PB,5O07"S/[`MH,,G=<*`H:J +M_T)C,D)C,4)C+D)C+<:F__++_1;/48++_E:H,O(C*<(#K((C*)T$P+`$P-`$ +M@_D"\_D*4_D44_D5%BT\##S#^193^1Q#^1H6JT2@^2#YQ^(C*=(C*`P"X_0` +MT_0(0F=,'?``"XL6^#R2`UH,&J)#:X++_@P>#`V`WH/20VJ&6/]#^1;&MO\` +MD@+3YAD"AKS_W0(,"\*@F,K"A@D`##BB`VN#^18,"`NJH*O`H(^#@_D:F<"`ZSR+4JB#(`GZ#B9QZ(M2/(M1_"J +M$1N/\(^SH*$A&_J@^K-\ZJ"($*#_$/#PM*(,?X"`U//X#J"@!*/X'(G'1L?_ +M``"@H`2"+4GP_Q'P\+2`@-3S^`ZC^!R)QYG'\BU'HBU(?.@;G_"J$:"A(?"? +MLX"9$!OZD)#4H/JSH@Q_@/\0\/"T\_D.H*`$H_D9QX(C%8"`!(/_'/G'1HC_^I)228`,,AWP`)G'H@.LX)D@ +MH+,$%IL,XB,NTB,MX."TT-#4X_T.0_T_](C*<(C*`P"T_0` +MP_0(0F=,'?``)BP'#`M&FOYF+/>2`UH,"\8]_Z#B!!;>">(C,-(C+^#@M-#0 +MU./]#D/]'-G'F"(RDB(RB#]``C]`A"9TP,`AWP +MT@.LT-8$5FW"#"J2`UK&"/^9Q[(C+J(C+1N+&RJ@*K.PB[-\ZJ`B$*"($("` +MM"`@U(/R#D/R'"G'\B,IXB,H#`+S]`#C]`A"9TP=\``ZIK`0I@9)_@``0_D6 +MQNK^F<>B`ZS@F2"@PP06?`KR(R[B(RWP\+3@X-3S_@Y#_ASIQ](#K-#2!!9M +M$,(C,+(C+\#`M+"PU,/[#D/['+G'F<>"(RVB(RY\ZQOH&_J@^K.`Z+.P[A"P +M_Q#P\+3@X-3S_@Y#_ASIQ\(C+](C,!N,&ZW0K;/`C+.PB!"PJA"@H+2`@-2C +M^`Y#^!R)Q](C*<(C*`P"T_0`P_0(0F=,'?"M`F48_^(C*=(C*`P"X_0`T_0( +M0F=,'?`G:E;B(S#2(R_@X+30T-3C_0Y#_1S9QYG'LB,OPB,P?.H;^QN,P(RS +ML/NSH/\0H(@0@("T\/#4@_\.0_\<^)QR(C*?(C*"/T``P"\_0(0F=,'?``FXD.@&\QVJ`RZJ2N9(*J@TFI' +MTFI(2[L,"0PH9BSDPJ4"`#RFL!"F0J*@LD.AL+!T%NL*HJ$"`#JFT!"FK#V1 +MXP``.::`$*8`.J;P$*:<+W:`#0`YIL`0I@`ZIK`0IHP;QOK_\54"`#^FT!"F +M%JT(#`K!5P+15@(,_P`\IK`0IB:+"_>;%0`]IH`0IH;Z_P`]II`0IJ+*(8;W +M_P#2IA\`/:;`$*:R(QV@K("BRO^G.S'R(R.P_X+B(R;PJH"B8RNG/BB2`UR" +M`V*A20`PF1&L&+*@@+"Y(+)JQZT")0(`+0H=\$K"XDR`##(=\+T"Y8,"AO/_ +MDFK'QO?_``PR'?`V00!"(D1B(C8RPEQ`I""]!N6K`J)C*[T&K00EJ`(,!Z)C +M+%*BH%I2K0*E8/]R0ZBR(R&2(RQ"(RV"(Q\;F1M$0F,MDF,LEY@*HB,K>"X(C*W)C+(+(`8)C*U:I\*(E +M'Z@*5HKZ8+3`5AO\QN?_##+120#R([!\_`P>Z0_";8$=\``V00!2HTQ04H"" +M)3E")3B`(!218`*B82^@@'2`B!%0 +MI2"0(A"`(B"EA`*"KP"R(3"`@A"@('2`(B`6FQ`,"PP&#`0,`PP-#`X,"@P% +M#`S`\#10_P'P(B"08*:,F7:`!(!@IHP8!OW_`'*FDLS^%AD1\LS\%C\:)HPU +M&RP=\`""*DKP(``6Z!&B*DRR$P!0FA`6JQ=7"A0A80+`8*:,3-!@IE:=_P!R +MI@P2'?`,`AWP\&"FC$^`8*96F/\`=::08*:,2?!@IE:?_P!ZICWP@&"FC$B0 +M8*96F?\`?J8]\*!@IHQ*X&"F5I[_`'VF/?#P8*:,3X!@IE:8_P!SICWPD&"F +MC$F@8*96FO\`=*8]\-!@IHQ-X&"F5I[_`':F/?#P8*:,3X!@IE:8_P![IALL +M'?"0(B`&H_\`T,(@@L;]X"(@@"R3QJO_``P+46("P5,`T3P`X6,"<60"\64" +M@L;]%J@P"X86B`^2QOX6*0\,"PP&#`0,`PP-#`X,"@P%#`Q&L_\``+!@IHQ+ +MT&"F5IW_`'6F/?#@8*:,3O!@IE:?_P!ZIALL'?```((J2U:H[9(J259)[5(3 +M`,(J3%#R@RT/)VP"L"\@0A,!/?`'9`+@(B#B"L%F/@+&:09R&ERM!1MWO0>E +M9`(]"G"W(%"E(&5H`H%@`C"0=("9$8""$)"((*`@=)*O`)"($(`B(`:C_P`` +M@68"H"D!@"(@AI__D&"FC$FP8*96F_\`=:8]\/!@IHQ/@&"F5IC_`'JF/?"0 +M8*:,2:!@IE::_P!^ICWPL&"FC$O@8*96GO\`?:8;+!WP``!A@0"!70)@8A"` +MAL`6V%^!7`*`AL`6R'^1@0"70@*&=0!!:0`,"2)A,7*3"[)A,J)A-+(A+@PO +M#!KPNQ!P(2']#8T+L)J#\&(18&?`DF$@L(J3@-Z3D/Z30%\@DI,)0$T@8/63 +MD.$A\%X14%G`4-230I,*43X`,F$S0*$A4&\@\+H1L+3`L/:38B$S(F$LXF$H +M8I8(HF$F4%T@8+$A\#L1,#;`,-6346<",B$TLF$G4/\0P#,1,F$M.KLZJC(A +M+U!=$*"@M,`S$3)A*CHB.N[@X+0@(+1`(A%`[A$Q:`+@52`@_R#A90(B(3&P +ML+3@_Q`P(A#@51`Q"@&P52#PJB"R(3+B(3+Q90(P(B"`[)/6*0`&V`*2824R +M(2B0TB'PW1'7$P6!:0"`[B#6)@"&U0(R(2=@TB'PW1'7$P6!/@"`[B`R(24; +MAH""(1N3,-(AD)(A,)VS8#(ATB$J8(.S,B$@T-%!TF$IVIDPO).0D+0Q9P)` +MF1'="S#N$#(A+9#N(/#N$#`Q(3J(@("T@.X@UB<`AL,"8B$L<+(A\+L1MQ8% +MP6D`P-T@UB0`!KH"PB$F0+(A\+L1MQP%P3X`P-T@0,(A<+(A&X0;EY"2(8"" +M(7";L[%G`D",LPQ,L-T0LB$I.HB`@+2ZF9"0M$"9$9#=(/#=$(#=((8"```, +M#0P.#`H,!0P,#`L,!@P$#`.&^/Z!?P"`DA"282N'@C,,!`P##`X,++(A+\!: +M$5!0M,"[$;!AM+"BM$"J$4!F$0P+T&8@\&80#`U@52`,!@;I_@``88$`@5P" +M8&(0@(;`S!A&E`*!70*'E@(&%P.1@0"70@)&>`+283MR83;"83=BDPK"DPMB +M82-@\2'P?Q%P9L!Q:0*-#L"1!)#GDY$^`$)A-<)A))!>(&#EDU*3#?)A)N)A +M.%!!!$"'DT*3#)"8($)A(D#A(?!N$6!$P$")DV*3#D*3#X)A.F)A(8%J`F"1 +M(?!Y$7!FP$!Q!'#8DW$^`%)A'^)A&G!](D])A/'*3"=(A.T)A'G!A! +MDV$^`))A&=)A.V#=(&*3"&)A.3(A.6!A(?"&$8`SP((A.W)A)6)A)S"-DX)A +M.\`Q(<#:$=)A+3)A+-JOVN[:9F!@M.#@M*"@M,`R(7#Q(<(A.#)A%/)A*!N/ +M@($$VMG0T+1PD@3PB;-P3@B$Y8"(00"(@;0C6 +M*`!&K01R(2>`0B'P1!%'%P6!/@"`,R""(24;1F"2(4!"(6!)LY(A*("#(1MY +M<'(AD'BS@B$JD6<"@()!D#,0BG=P<+1`=Q%P,R!R(2V2(13P,Q!P<2%Z1$!` +MM$`S($(A+()A%9"0!!N$@($$0(FSD6L"30R`29."(2/6*`"&#@5B(2."(2:" +M80]@8B'P9A&2(0]G&06!/@"`1"!B(2."(20B83T;EF`B(9"2(6"2LR(A+("# +M(7J9&V)@8B$@:+,A9P*"(160D+0@1!"*9F!@M((A'4!F$6!$(&(A%R+(`?!$ +M$)!$("`A!)%K`F!@!(`FLR#)DX(A(L!L("(A/=8H`,;Q!,(A%8(A(I(A&I)A +M$(""(?"($8)A$9(A$8(A$)<8!8$^`(!F(#)A/B)A/3(A'R(A(C`S(1N"()(A +M@((A((FS(B$=>HB`@+0;DI"2(2"3LR%G`LJ9D)"T0)D1(&80(6D`D&8@\&80 +MDB$<@&8@@B$6&SDP,02`@`20.+,PLI.2(2$B(3TR(3[6*0`&U022(2&"(1F" +M81*0DB'PF1&281."(1.2(1*'&061/@"0NR"2(2&0@B$;^?#R(9#XLWK_\F$\ +MDB$>\B$&PLB'PNQ&W$P>!:0#HH8#N(-8F``:&!#(A)V"R(?"[ +M$;<3!<$^`,#N(+CAD6T"&X:`@B&P>9-@LB%@B[.R(27!9P(;F[#2(9"2(;"= +ML[(A*L#N$+"Q0;)A*;J9D)"TLB$M0)D1D.X@\.X0L+$ANHB`@+2`[B""(20] +M!]T'UB@`1G($8B$L@$(A\$011Q8%T6D`T-,@@B$CUB@`!F@$PB$F@#(A\#,1 +M-QP%@3X`@-T@PB$D&XS`DB&`@B'`B;.2(2,B83T;*9#"(2`B(9`LLPQ,DB$I +MNB(@(+2:B)%G`H"`M$"($9#=$(#=(/#=$"#=("(A/09[_I%_`($*`9"2$))A +M*X>9`D8Q`Y%;`H(A*U)A&)>8`H:J`X%_`%)A&(="`L::`?(A&*)A-')A-B)A +M/4",!$">!$!O!&FQF<&"83HA;`)]#4"M!*GAD'Z3@-Z370]@4I/BDPWB81^@ +M\I-2842A:0!2DPE2826@AR"@32#@(2%0D2&282@B81WP:1'P(A%@5)-2DPM2821R84/BDP_B81YR(41082%B82S@@2&"81R@IR#P9A'P +M*!$@[L!@5<`B(4-0])/@>I-R843B(4.A/@!2DPQ282*@(B!002%"81KP9!%@ +M5)A*E!$$.J9ZJKJB.IWBXB+JI"0M$"9$:"@M("`M%`S$'!P +MM$!W$5!=$$"($4"J$:!$((`S()!5('#_()T+XB%"Y.]!\T'UBD`AL$# +M@B$<@F$)<'(A\'<1DB$)=QD%L6D`L+P@PB$AUBP`AJL#8`L8O`Y%;`E)A&)>8`L:'`X%_`(="`L:!`4%I`,)A-W)A-K)A12)A +M/;(A&"(A+W*3"W)A),`B$7#!(?",$<)A+(!WP(%L`BK,P)`$D+B3P,&T0,P1 +M@I,)0&L@<+:3@F$E)A)O"($"HN(""T@*(@@I,- +M(B$]@F$?@.$AXF$=T.T@%V@%X6D`X.T@TI,,(F$]T($ATF$B\"@1(-W`(3X` +M("X@T.*3TB$?(B$IT-(ATF$7*MTA9P+0T+1`W1$@+A#0(B#2(2V"81KP(A#: +MV$"/!-#0M"#M($(A&"%L`HFQ@$*3@I,/(B$]@F$>@-$ATF$+%PO)-Q:0#"(1]P>R"0MY-R(1W`PR$;EY"2(7"GB2(2%P<+1P9B#6*0"&G@-R(1F0 +MPB'PS!''%P6!/@"`NR""(2&2(1XB83T;R(`B(<#"(8#"LR(A')"3(1N"@((A +M((FS(6<"DB$5(+L0FH@B(1N`@+1`B!&`NR#PNQ`JS,#`M"(A/<"[(`R,AL7Z +M#`T,#@P*#`4,#`P+#`8,!`P#1L#Z``!R839`7`0B83V"DPF"824B(2]0WI/` +M>A%0O).YH7)A+5%I`(!A(<`B$6)A*")A*BHF4%T@\&81(""T8&C`8-638I,( +M0"(143X`8*$AHF$G>GI072#PJA&@IL"@U9-19P)P<+2M"U!=$"!5("(A/?!5 +M$'!5(-8H`$83`[(A)3(A*+"R(?"[$;<3![%I`*BAL*H@UB8`1A`#,B$G8+(A +M\+L1MQ,%P3X`P*H@8((AXB$E&[:PLB$;SN#2(<#"(>#-L](A+>%G`F"XL]#1 +M(>"J$-J[TB$J#`ZPL+30T4':S,#`M`P-0,P1P*H@#"SPJA"PJB"&A_L`P6P" +M46T"(F$]LB$OP(H1TI,*DI,+DF$DTF$C@F$MP+L1T.$AXF$FLF$JD"$ABHZ` +M@+0B82RZLO#N$?`B$>#=P+"PM$#M!.!UDU(A&$"[$2`IP.!!FP&#"DR*3"O)A&2)A(R!A(6)A)O#F$>`BP*IFX3X`JJ]@ +M8+3Q90+@Y2`@7I/B(2^@H+0B(3W`[A'B82KJF>K=B]V0D+1`F1'A9P+0T+1` +MW1'@51#@S!#0S""052#190*1;0+M!]#,$-!5$&!5(,"J(+#ID^G1UB@`ABD" +MLB$D8B$LL+(A\+L1MQ8'@6D`Z-&`[B"2(2/6*0#&)0+"(2:0LB'PNQ&W'`7! +M/@#`[B"XL=(A(Y%M`AN-L'F3@((AT+(AT(NSTB$D&YW0LB&0DB'0F[.R(2K1 +M9P+-!["Q0=#N$+J9D)"T0)D1D.X@DB$MW0?P[A"0D2&:B("`M(#N(((A'K)A +M*9)A&]8H`,88`C(A'("R(?"[$;<3!=%I`-#<(((A(=8H`(8&`G(A&8"R(?"[ +M$;<7!<$^`,#=(+(A(8(A'@Q,L)(A@&(A&T@;.S`R(4!"(8!&L[`YLV(A&PP+ +M@6<":C-B(2F`W1`P,+1J1`P&2T1`0+1`1!%`W2#PW1`,!##=(`P#QD?Y&VA@ +M@2&)\6!B(?!F$<;P^L(A%1N(@)$ADF$0@((A\(@1@F$11@W[`!N9D($A@F$2 +MD)(A\)D1DF$3!BK[#`L,!@P$#`,,#0P.#`H,!0P,AC/YPJ(`LJ,`L+00QQL" +M!I+Y5^0"AI#YT"(@!H_YLB$EF>$;N[`Q(;"R(?"[$89U^P`;MK`Q(;"R(?"[ +M$49X^QLX,,$A,#(A\#,11I;[&TA`82%`0B'P1!$&C/L``)(A+\"*$7*3"7)A +M)8)A+7!1(<"9$9!!05)A*$)A*4%I`)J5D&`$\%41D)&T4%?`8-Z30$T@8I,( +M4-2303X`8%$A4F$GBH7P51%`32!05L!0U)-19P)`F1&`@+1071"052#P51"` +M52"6UW$R(2BM"^(A*7#2(?"]$>K=T-`$T*R3MQ,%X6D`X*H@EG9Q,B$G8+(A +M\+L1MQ,'P3X`/?#`JB!@@B'B(24;MK"R(1O.X-(AP,(AX,VSTB$MX6<"8+BS +MT-$AX*H0VKO2(2D,#K"PM-K,P,&T#`U`S!'`JB`,+/"J$+"J(,8?_@"9H1M( +M0($AB4%`0B'P1!&&[?L;1D"!(8E10$(A\$01AO'[&VE@@2&)<6!B(?!F$484 +M_!NXL,$AL+(A\+L1AC/\&\S`<2'`PB'PS!'&4OP``&(A)!MF8($AB6%@8B'P +M9A&&_OL`&\C`@2&)@<#"(?#,$48?_!MY<($AB9%P9.M![<6 +M!=%I`-"J(.(A(Y:.7,(A)N"R(?"[$;<#2(1NY +M&\[`PB&PLB'@S;/2(2WA9P*0N+/0T2'@JA#:N](A*0P.L+"TVLS`P;0,#4#, +M$<"J(`PL\*H0L*H@QL#]``!`G`1M#8*3"8)A)9!NDX!1(5)A*&!6(!=H!5%I +M`%!6(')A-H(A+R)A/9)A2T!N!)$^`&#>DVG!P"H1(F$M8I,(P(@1@F$JD)4@ +M@(%!8*$AHF$G*BJ"82GPJA&@IL"@69.A9P*2(24@(+2@I1"0DB&9,8IY<'"T +M@I,-@F$?0'<1<*H@\*H0(%H@@)$AK0V281TB(3V2(4L7:`6A:0"@K2#2(2CH +M,1N-X.`$@($$T(ZST6D`[0N0[)/0WB"`[9."DPR`T2&"82+PG1&0B,"1/@"0 +MFB"`J9."(1^2(2F`@B&"81>:B)%G`H"`M$"($9":$("9(((A+=)A&O"9$(J- +M@("TD*@@EB9',B$G8-(A\-T1UQ,%@3X`@.X@DB$M&X9@TB&`@B%@C;/2(260 +MD2&281N:B)(A*")A/=#3(1LI("(AD"VSTB$7DB$J@("TT-`$D))!DF$5FB*1 +M9P(@(+1`(A&0[A`@[B`B(1WP[A"8P8#N(!N"@($$((VS(6D`D+R3DB$B("L@ +M@+*3W0LB(3V6>3_"(1J0LB'PNQ&W'`7!/@#`W2`,3+(A(F(A'X(A';"2(6!C +M(1M(&SLP,B%`0B&`1K.P.;-B(1L,"X%G`FHS8B$5@-T0,#"T:D0,!D!`M$!$ +M$4#=(/#=$`P$,-T@#`/&!_C!;`*2(1A`W02"DPN"8239X8"Q(;)A+-"K= +MT-&TDF$<0-T1T*H@\*H0L%H@0*0@%V@%H6D`H*0@P6D`N.&!;0+M!Y*3#I)A +M(;#HDX(A)+(A+,#.((""!!O;T-$$L-BST.R3@3X`T6<"D,$A\+P1L)G`@(H@ +MD*B3DB$J@B$ +M\.X0D)$ADF$;FHB`@+2`[B"1:0""(1S`P@20ER`;N+"Q!("\LX(A(;!YD]T' +MEL@B)/=!P9C^QNX +ML#$AL+(A\+L1AN7]&T9`@2&)`4!"(?!$$<8!_!MH8($AB1%@8B'P9A'&(_P; +MN;#!(;"R(?"[$49#_!O)P'$AP,(A\,P1QE_\@B$IK0L;-S"R(8J+,#$A\+L1 +M@(`$@*R3!C?^`!NVL#$AL+(A\+L1ACC^&VF!;0*2(2E@LB%@82&:F_"[$9"0 +M!)!XDZT'QHG^&[ZPP2&PLB'PNQ%&C/X;UM`Q(=#2(?#=$<;A_ANYL,$AL+(A +M\+L1A@#_&[NPP2&PLB'PNQ&&5/\;N+!Q(;"R(?"[$49S_P``-F$`HJ`!)>7. +MY2;9@6@`H4D`#`^1:`#R:H#R:L#Y"9$W`/)H0+(IK+#`!`=K$GSNX.L0XFFL +MP"``TBFLV0'`(`!V@!#B*>?I$=@1T-`4V1&($28X`@;Z_X$Y`!P.(B@@#$W` +M(`#0(B`B:"#`(`#2*"#`(`#R:I[`(`#@W2#2:"#`(`#B*"!L\B#N$.)H("(H +M('R]T"(0(F@@G+SQ/@"R::S`(`""*:R)`<`@`.(J@O#N(.)J@AWP`+$^`)(J +M@K"9())J@AWP```V00"BH`&EV,ZRH;2ZLJ(KI`PHDB+'&ZJB:Z2)":7Q_QWP +M```V00`,&Z&T`'S\D;4`T6\"83D`4J#_#`A!20!Q;@)R8EOB(DMR9()Q:`"" +M8EY28EE29H+28E_29,(R#B7R#B0,YC`S$4#_$3#_(-#_(/)B8/DG\@XEX@XD +M,9``,/\10.X1\.X@T.X@XF)AXF="TF):TF1"HF)EDF)F8F)H@F)BPF)C@F)D +M@F)GLF1$LF1%LF=8HF2`DF3`B0=B9T!2(EQ29)HR9(4=\``V@0"1K`!!:`!Q +M20`,)6T"(J*@*B-V@!NR)X.H-((D0PN9L*H@H(@@C.B,^<(B'\@,)BP01O?_ +M````5GD`TB/'60VEXO_E"=D,[X+7!`P%D;4`H;0`HF>`DF?`60CR9$#B(S]I +M`>DA9SX"QC\`^"'H`6T%*5'P[L#I$:@ALB,VN4&JIJDQI1X`+0JB8T.X0:@Q +M)2$`#!NB8T(,.?T%(_\`H_\(D_\>\F?)XB-#TB,MS07C_`IF/123_!13_!K= +M#+/]'+/]`+/]%D8$``"3_!13_!K=#+/]'+/]`%/]%L(C+PP>HB-")BQP"_SP +MBA&#_0*S_1L,"/".@X/]`=G$PB,O#!\,#B8L6JT%"XR`[X-3^@[C^ARIQ*%4 +M`AMF#!^@S2#)Q,(C+PP.J!$F+#O=!0N,@.^#4_T.X_T/\`-/\",)D3%9Z\BA1:`'&!`"C_0)3_1L&YO]9Q(;K_P!9Q$;S_U)"@9&L +M`#WP=H`;HB>#B#3R)$,+F:"(((#_((SOG#FR(\>X"R8K!\;V_P```(PY8F,_ +M'?#2(\<,+,)M`"7,_V)C/QWP````-D$`#`0@4!2<%0Q'4'?`,'=C<#/`=I<$ +M0D(`&R(PL@0>HF-BDD0F=VL0##(;ZN)C$1WPK0*E*?'&\O^M`@P,)2GQ +MXB,1##(;[N)C$1WP=VL8(*(@L@-2PJ``92?Q\B,1##(;__)C$1WP`*T"#`PE +M)O$&]_\`#!Q\^+(#4K)$)H)$'J)C8JT"I23QDB,1##(;F9)C$1WP````-D$` +MLM("L@M$=VL!'?"M`@P,92+Q'?`````````````````````````````````` +M`````````````````!X#8``>`V``'@-@`!X#8`````#_____`````/____\` +M````_____P````#_____`````/____\`````_____P````#_____`````/__ +M__\`````_____P````#_____`````/____\`````_____P````#_____```` +M`/____\`````_____P````#_____`````/____\`````_____P````#_____ +M`````/____\`````_____P````#_____`````/____\`````_____P````#_ +M____`````/____\`````_____P````#_____`````/____\`````_____P`` +M``#_____`````/____\````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +/```````````>`P`'A0`" +` +end diff --git a/sys/contrib/dev/drm2/radeonkmsfw/TAHITI_ce.bin.uu b/sys/contrib/dev/drm2/radeonkmsfw/TAHITI_ce.bin.uu new file mode 100644 index 00000000000..b2ebe23ba3d --- /dev/null +++ b/sys/contrib/dev/drm2/radeonkmsfw/TAHITI_ce.bin.uu @@ -0,0 +1,194 @@ +begin 644 TAHITI_ce.bin +M?$"``8@```#40`!_?$"``8@```#$(``+Q@P`!93``"0D4``/,10``I5```3, +M0``DS$``)8`````Q%``#E4`!.\Q``";,0``G@````,0@``O,(``%?$$``5!4 +M`"!\08`!!B0``@8U``P&-@`-`4H`$1\0@`!?$)``94` +M``>&@```@``!4(```5"```%0@``!4(```%(15``0?A8`"LU``!'480``E8#_ +MLL0Y(4!\0(`!B````!%4`!!29``@?B8`&LU``!'48@``E8#_J<0@``Z:`/__ +M?$"``8@```!\0,`!%-``'C$4``*50`#K&-0`,!C0`>@8_``T),P`#P3H`&A\ +M08`!?$'``93``!*&@```@``` +M`!J```!_4=P`('V=@!J90``#Q:(``(```'_)H@``@```?\6A``"50``%!9@` +M`<6E``!29``@?B8`&@4H`(-\08`!?$'``94```>&@```@``!4(```5"```%0 +M@``!4(```)/8```1SAD``)5```0%F``!5B``(,X9``"7P/]QQ#DA0'Q`@`&( +M````4=P`('V=@!K8```1SAH``)5```0%F``$5B``(,X:``";P/^]?$"``8@` +M``#8``/VV``#]=@``_38``/SV``#\M@``_'8``/PV``#[]@``^[,0`!_S$`` +M?\Q``']\0,`!S,```=1``'^`````?$#``5!0`"",``#+?-#`&L0@``O$U@`` +M?$.``<0D`!.:```)V&0#],9K`_&:@/__QFL#[@:H``'.I`/NS60#Z]@D`_29 +M0``)WX,``,^@``Z,``#/U$``?YH`_SS&

?16`#IF```_80``IR!``'7U1@!)]C<`/F<#__=@``"E0X``! +M?A9`#II```1]8H`2TH``(X````#0```C@````-B``"G$/`!_S```(-A``"C$ +M#``V$``'8````#,```@V$``*,0, +M`!R8P/__V```*-```![0```?V$``'7Q!``&`````?$#``7Q!``$5&``?S0`` +M$5$4`""9@``#U$T``(````!]34`:&1P`,=16``#$(``.E<#^R,0@``Z:`/__ +M@````'Q`P`%\08`!%-``'C$0``(DU`#_E0#^O\V4`P"`````Q"``$WQ`P`'$ +MTP,`S```$`(0`]@"'`/D`B`$"`(D!%P!]`3P`?@%$`'\!+``B`*X`BP$B +M`````@````(````"`````@````(````"`````@````(````"`````@````(` +M```"`````@````(````"`````@````(````"`````@````(````"`````@`` +M``(````"`````@````(````"`````@````(````"`````@````(````"```` +M`@````(````"`````@````(````"`````@````(````"`````@````(````" +M`````@````(````"`````@````(````"`````@````(````"`````@````(` +M```"`````@````(````"`````@````(````"`````@````(````"`````@`` +:``(````"`````@````(````"`````@````(` +` +end diff --git a/sys/contrib/dev/drm2/radeonkmsfw/TAHITI_mc.bin.uu b/sys/contrib/dev/drm2/radeonkmsfw/TAHITI_mc.bin.uu new file mode 100644 index 00000000000..42dcc68be65 --- /dev/null +++ b/sys/contrib/dev/drm2/radeonkmsfw/TAHITI_mc.bin.uu @@ -0,0 +1,694 @@ +begin 644 TAHITI_mc.bin +M``/HX``"O@$```('``/_```#_P```J`.``'`&0`#Z````^@0``/H(``#Z#`` +M`V`@``-A2``",;@``TEL``-("@`#KPH``K0$``)O]```^BT``K0/``)/%``` +M^@H``ZB,``#"`0`#0"0``K1```*U#P`#2J8``D1!``/P`@`"294``VJF``-* +M40`#0"0``K@"``*Y_@`"1)0``DB!``.H@``"9$@``VI1``-`)@`"OX```KP$ +M``))^0`#\$(``_0```'`00`"OX```TA4``*X#P`"N?```D$8``)"*0`#:%0` +M`TMI``/`C``"9$P``VMI``)$2``#:VD``F$?``)B+``#:%0``BP%``-+3``# +M_P```V%$``-+'``#0"H``K3\``-A0``"0S0``F$>``-K'``"2JX``_!&``-) +MU0`"O!```DS'``/P0@`"O"T``C)S``-`)``"M$```K7P``-*I@`"1$$``_`" +M``)IE0`#:J8``T````/_```#^`$``\1```/$40`"QD4``@````-`$``#_P`` +M`_\```/H(``#Z#```V`0``"1Y0``D@T``_@#``(POP`",;(``T@(``*TGP`" +M0`0``V@(``-(P``#2,8``K3\``*U#P`"010``DF4``)")0`"2J4``D,U``)+ +MM0`#:,```VC&``-(```#2`4``KL_``*\OP`"O^\``)(E``"*)@``@BH``+HH +M``"J+``"01L``KO[``)`#``#:````KS^``)%7P`"1WP``V@%``-($``#2=T` +M`KOQ``*\`@``BBD``*HK``)!&P`"81P``V@0``)%6P`"95P``VG=``-`#0`# +M_P```_\```.A3```B?\``K,"``)#-``#HS```)G^``*S!``"0S0``Z,R``"9 +M_0`#0"D``T````*\(```N<<``D(L``/P!P`"L````((Q``(Z,P`#Z````('E +M``/T```!PEP``T````*\(```\C$``D,\``/P!0`".C,``^@```"!Y0`#]``` +M`<)<``-((0`"L\$``D=S``-H(0`#:HD``T@E``*SP0`"1W,``V@E``-JC0`# +M2"D``TEP``-`$@`"OP<``D`/``.````#``X``Z^(``-)U@`"O!```DS+``/P +M00`"P`\``X0&``.%5@`#I58``F54``)D0``#2>@``V@I``-H+0`#P00``\$5 +M``-IZ``#:>P``TK"``*_P``"2(\``VK"``-JQ@`#Z````^C0``(NE@`#:.0` +M`\'>``(NE@`#:00``^A```*]```",@(``KT!``(R`@`#0"P``_\```*T`@`" +M1`0``*()``-)U@`"M1```D1;``.D1@``H@L``J!.``/P`P`",[<``_0```'! +M!0`#]````=)]``*\```",CT``T`.``#R,@`#Z/```Z"V``-)U0``^@0``K$/ +M``)"%P`"I2```_`!``#Z,@`"0QL``J4C``/P`0``\@0``T````-`+@`"MA`` +M`K0,```J,@`"0B0``Z(B``)#-``";",``D9H``/P`P`"H%X``_`!``/HP``` +MX@,``T`(``*T````>@0``J`D``/P00`"L@@``J#^``/P00`"L@0``K0_``)" +M)```DAH``TJ4``/_```#88```T````/_```"M$```D0$``/P`P`#Z````((Q +M``(BX``#0````_\```*T0``"1!0``_`"``#R,0`"(N```T````/_```"M$`` +M`F$0``)$%``#\`$``BSW``-*E``#_P```V&$``-````#_P```K0"``)$)``# +M\`,``^@```"",0`"(PP``T````/_```"M`(``D0T``/P`@``\C$``B,,``-* +ME``#_P```V&(``(QL@`#0````_\```/_```"1`X``_`#``/H````@C$``B0; +M``(QL@`#0````_\```/_```"1!X``_`"``#R,0`")!L``BTN``-*E``#_P`` +M`V&,``-````#_P```K0"``)$0``#\`L``^@```"",0`")@<``T`L``/_```" +MM"```D1```/P`P`#Z````((Q``(Q*0`#2I0``_\```-AD``#0````_\```*T +M!``"1$```_`#``/H````@C$``B?3``-*E``#_P```V&4``-````#_P```K00 +M``)$0``#\`P``^@```"",0`"*'H```'_``/_```#_P```J0.``/P!```\@`` +M`BAZ``/H````@@```TJ4``/_```#89@``T````/_```"M"```D1```/P`P`# +MZ````((Q``(J"@`#2I0``_\```-AG````@,``_\```*T`0`"1`0``_`$``/H +M````@C$``\'^``(JO````@,``_\```*T`@`"1`0``_`$``/H````@C$``^CP +M``(JO``#0````_\```/_```"1"X``_`#``/H````@C$``C?1``-````#_P`` +M`K00``)$)``#\`,``^@```"",0`"./4``_@#``-*E``#_P```V%@``-````# +M_P```K0"``)$00`#\`H``/(Q``(F!P`#0"P``_\```*T(``"1$```_`"``#R +M,0`",2D``TJ4``/_```#860``T````/_```"M`0``D1!``/P`@``\C$``B?3 +M``-*E``#_P```V%H``-````#_P```K00``)$00`#\`L``/(Q``(H>@```?\` +M`_\```/_```"I`X``_`$``#R```"*'H``^@```""```#2I0``_\```-A;``# +M0````_\```*T(``"1$$``_`"``#R,0`"*@H``TJ4``/_```#87````(#``/_ +M```"M`0``D0$``/P`P``\C$``\'^``(JO````@,``_\```*T"``"1`0``_`# +M``#R,0`#Z/```BJ\``-````#_P```_\```)$/@`#\`(``/(Q``(WT0`#0``` +M`_\```*T$``"1#0``_`"``#R,0`"./4``TJ4``/_```#870``_@#``-````# +M_P```K0(``)@`0`"1`0``_`,``-("``#_P```K1```)@!``#:`@``TF8``*T +M_0`"010``VF8``/H,``#P2X``V@P``(Q1``#Z````^@0``/H,``"L@,``V@P +M``-````#_P```K6```)A$``"114``_`!``(W,``#2I0``_\```-A>``"O`$` +M`C(]```",@`#_P```_\```*A#@`#\`(``^@```""!P`"+2X``T@```-(!0`` +M"B8```(J```2)0``*BP``#HH``/_```#:````V@%``-($``#2=T```HI```J +M*P`#_P```V@0``-IW0`#27$``K(!``/H,``#P08``\$7``-H,``",40``^@` +M``/H$``"L@,``K,```-H,````@L``_\```/_```"H`X``@*!``/T```!P``` +M``'E``-`$@`#0!4```H-``*@#@`#\`(``_0```'"G``"P1X``(H-``)KN@`" +M95L``L9N``/P00`#!FX``V`5``*R#P`"1$(``J44``/P!P`#Z*```^BP``-@ +M$@`#Z````('E``/T```!P&L``T`L``/_```"M"```D!```/P`@`#]````<*H +M``/H````@C$``C$I``#R,0`",2D``^@```"",0`",%X``/(Q``(P7@`#23T` +M`TC```*V"``"1F0``_`"``*U,``"8B4``VC```-HQ``#2QT``T`H``*\$``" +MOP0``DS```/P!0`"9W\``VL=``(Q1``"9WX``VL=``-`(``#_P```_\```+" +M+@`#\$0``L,^``/P0@`"LO\``K/_``-@(``",;(``TE(``-`*@`"M#\``K6` +M``*V0``"1F@``_!%``)#-``"8S4``VE(``)#-``#:4@``^@```/H$``#Z"`` +M`^@P``-DI``#9.0``_0```'````#0`0``&HQ``/H@``#Z)```^B@``/HL``" +MH-X``_`#``-DJ@`#]````_!!``-DZ@``8>D``J#>``/P`P`#Z$```_0```/P +M00`#A.H``]A```/>@``_@#``/4G@`#^P```P_^``("]P``(<,``J#> +M``/P`P``8<(``_0```/P00``8<$``K\(``/<0``#V,```_@#``/4G@`#^P`` +M`P_^``(#!@`#^`,``^P````",@``:C$``_\```*@#@`#\`$``^P```-).``" +MH-X``_!!``-).``"N/X``D(H``-I.``#2=```K0$``)A%``"L@$``K,2``*@ +MW@`#\$$``K,1``-H,``#27$``KCO``*Y!``#P08``F%Y``)#.``#:#```T@) +M``*X_0`"1$@``V@)``(M"``#2:```K01``/!)``"L0\``VF@``-*1``"M/X` +M`F$>``-J1``"010``VI$``*@W@`#\`,``T)A``/T```#\$$``T)=``/_```# +M_P```^A@``-II0`#:&L``KH,``*XP``#W($``]BA``*P^``"L0<``^@@``/H +M,``#_@```]0?``-H:P`#Z/```B/)``(CL@`"(]X``J'^``/P00`#8GH``T)X +M``/_```#_P```J"```'#9P`"H)$``<-G``*@H@`!PV<``J"S``'#9P`"S_X` +M`KP,``*A_``#\$(``_0```'#40``^``-">``# +M_P```_\```*@@``!PWT``J"1``'#?0`"H*(``<-]``*@LP`!PWT``L_^``*\ +M#``"H?P``_!"``/T```!PVH``&'.``#YQ0`#_P```J7/``'#C0`"I?P``<.( +M``/H\``#Z,```_0```'#D``##,\``ZS```/H\``#]````<.0``,/_``#K_`` +M`^C```(CR0`#P?P``B.R``-).``"H-X``_!!``-).``#_P```F(N``-I.``# +M2=```K3[``)!%``"L@$``K,2``*@W@`#\$$``K,1``-H,``#27$``KCO``*Y +M^P`#P08``D%Y``)#.``#:#```T@)``*X_0`"1$@``V@)``*P&``"(_T``BTN +M``/L```#P0\``\$?``/!+P`#P3\``J#>``/P!0`#9+```V2T``-DN``#]``` +M`_!#``-D\``#9/0``V3X``-(R``"M/,``K4$``)`!``"8`4``VC(``)`!``# +M:,@``^P```/!#P`#P1\``\$O``/!/P`"H-X``_`$``-DK``#9+P``_0```/P +M0@`#9.P``V3\``-(R``"M/,``K4$``)`!``"8`4``VC(``)`!``#:,@``^P` +M``*P$``"(_T``TH<``(D#0`#P8P``TH@``(D#0`#C,8``FB,``-*)``")`T` +M`\&<``-**``")`T``XS&``)IG``#2BP``B0-``/!K``#2C```B0-``.,Q@`" +M:JP``THT``(D#0`#P;P``THX``(D#0`#C,8``FN\``/L```#:&L``KD(``*X +MP``#W(```]B0``/H$``#Z"```^@P``/^```#U!X``VAK``/!CP`"OQ```BT$ +M``/!^``#[````^C```.'/``#IW8``X8L``.F:``#A1P``Z5:``.$#``#I$P` +M`FS'``)LQ@`";,4``FS$``/L```#0"0``&HQ``/_```#H`8``J$.``/P`0`` +M@?D``TFA``-)I``"NT```KJ_``)%I0`"H-X``_`#``)#HP`#]````_!!``)C +MLP`#::0``VFA``(R8``#Z````^@@``/H,``"H-X``_`#```)^P`#]````_!! +M```)^@`"N,```VAK``/8```#W(```]0>``*_"``"+00``X#@``"!]@``@?4` +M`^@```"!]```@?,``/'R``-(5``"H-X``_`#``.$Z``#]````_!!``.$Z@`" +M8B0``VA4``-)=0`#B.8``F!H``/!%P`#P2X``\$^``*@W@`#\`$``X,P``-H +M,``#2%0``J#>``/P`P`#A.@``_0```/P00`#A.H``\!$``)")``#:%0``&(+ +M``*_`@`"+00``J#.``/P`@`#]````@2#``(NJ``#Z'```X3F``)&2P`"H&0` +M`_!*``-)T@`#Z&```XJR``.JJ@`"H:X``_`"``,!K@`")?<``_0```'%(@`" +MOP@``D_Y``.O]``#B[(``ZN\``(E.P`#]````<4B``-`#``#_P```K\(``)/ +M\``#\$(``_0```'$E``"OP```KL!``(E.P```@0``_\```/_```"H`X``_!" +M``/T```!Q2(``/'T``#Q\P`"H-X``_`#``!Y^``#]````_!!``!Y]P`#C>(` +M`]S0``/8\``#B>8``FF>``#QXP``0?D``^C0``./X``#P:@``XSM``#AYP`` +MXC```BX1``*W`0``0?D``^B@``#1XP`"H'X``_`!``.H@``##]\``\'9``/! +MJ``#C.T``.'G``#B,``"+A$``BY2``/!!``#P28``V)P``*_`0`#0!$``XC@ +M``*@_@`#\`0``D1(``/P`@`#]````@4(``*W`0``0?D``_\```*@?@`#\`$` +M`ZB```/HT``#C^```\&H``.,[0``X><``.(P``(N$0`"+E(``T)P``.,ZP`# +MC.H``^CP``+(0``#J(```"H)``+*8@`#JJ```J9>``($Y0``:C$``/()``/! +M6``#P7H``V)Q``/T```"!)\``J%>``/P#@``:C$``\&1``/!LP`"H-X``_`# +M``-A+@`#]````_!!``-A,@`"P($``Z@```+"HP`#JB```X50``"J"0`#@>`` +M`P"$``/P)``"H80``_`!``+$00`#!$X``\%4``,"I@`#\"0``J&F``/P`0`" +MQF$``P9N``/!=@`#U%X``F`"``($]P``:C$``K\"``(M!``#27$``X'D``/! +M!@`#HFP``J`N``(%(@`"87$``\$N``/!/@`"H-X``_`!``.#,``#AF```_!' +M``-H,``#C^T``BT$``./[0`"+00``X_M``(M!``#C^T``BT$``-)=0`#P2X` +M`\$&``/!%P`#P3X``J#>``/P`0`#@S```V@P```!Y0`#_P```_\```*E#@`# +M\$@``T`1``*@W@`#\`,``F9N``/T```#\$$``F=^``-@$0`#Z````('E``/L +M```"H/X``<5'``*P#P`"L0\``K(/``*S#P`"H-X``_`#``-C*``#]````_!! +M``-C:``",40``TI```!!\@``2?8``%'U``*\,P`"1`P``D4<``)D10`"1BP` +M`D<\``)F9P`"9$8``J%.``/P`0`"M`$``J".``/P!0`"I$D``_!#``*D2P`# +M\$$``/'T``"A]@`"O,P``D0,``)%'``"1BP``D<\``)D10`"9$8``F1'``*A +M3@`#\`$``K0!``*@C@`#\`4``J1*``/P0P`"I$L``_!!``#Q\P``H?4``^A` +M``"A\@```?0``!'S``*Q#P`"LP4``J7^``(%G0`"H/X``_!!``/H\``"H`X` +M`_`!``+/_@`"L1```J`N``/P`0`"S_$``K.0``*F\P`#\$(``/'E``/L```" +M0P(``_!#``(EW0`#]````<5'``*T$0`##_0``J#>``/P`P``^?$``_0$``/P +M`0``^?```T,I``*@W@`#\$$``T-I``*_=P`");P``^P```-(Y0`"H-X``_!! +M``-)!0`"N!$``J`.``/P`0`"Q$@``DE!``*DDP`#\`$``/'T``*@+@`#\`$` +M`L58``))40`"I),``_`!``#Q\P`"H-X``_`#``-HY0`#]````_!!``-I!0`" +M0`(``J`.``(%1P`"L0(``B7W``/L```"H?X``_!!``/L```#H?8``X_V``.@ +M]@`"H0X``_`'``*\"0`"H$P``_!!``*\#P`#P4P``\%<``,`#@`"H1X``_`' +M``*\"0`"H&P``_!!``*\#P`#P6P``\%\``,!'@`"H-X``_`#``-C*0`#]``` +M`_!!``-C:0`"8@$``@7"``/L```#0RD``J#>``/P00`#0VD``J`.``/P!0`" +MM`\``DS^``/P`0`"M`D``\%4``*@+@`#\`8``K8/``.L]@`"3,X``_`!``*V +M"0`#P78``J#>``/P`P`#8RD``_0```/P00`#8VD``^P```-(Y0`"H-X``_!! +M``-)!0`"L!$``L1```+%4``"H-X``_`#``-HY0`#]````_!!``-I!0`#`1X` +M`@7[``/L````:C$``^@```(N@@``\>\``^@```"![@`#0`P``T`M``*X$``" +MN0@``DH8``.JI@`"2TD``ZNT``*S!``";ZL``_!!``*S`@`"3ZL``_`!``*S +M!@`#0"T```H$``*P0``"0$```_!!``/H,``"01X``_`!``/H,```F>T``BT( +M``*X$``"N?X``J#>``/P"0`#2"$``KH/``)&:@`"1WD``F9H``-H(0`#:HD` +M`_0```/P1P`#2"4``KH/``)&:@`"1WD``F9H``-H)0`#:HT``C`H``*_`P`" +M+_0``K]D``(M!```:C$``_\```/_```"H-X``_`#``*[```#]````_!!``*[ +M0```V>```\$.``*Q$0`"LA```VI$``*Q$``#Z"```VI$``*P_P`"L?\``K+] +M``*S_P`#:&```TI@``*_]P`"L1,``D`/``)@#@`#:F```#'^```Y_0`"O`@` +M`K_^``.&:@`"8B8``J1^``/P`0`"LP0``D`/``)@#``#:F```X_A``(M!``# +M:&L``_\```/^````8B\``%HN``/_```#V,$``]RQ``./XP`"+00``]`?``., +M[P`#C^0``F_^``)``0`"0B,``D`"``),P``#^P$``]`?``,/_@`"!G@``J#> +M``/P`P`#2"$``_0```/P00`#2"4``\#,``(&I``#C.8``L9L``/P00`"QWX` +M`J#>``/P!``#:"$``VJ)``/T```#\$(``V@E``-JC0`"I7X``_`"``/T```! +MQDH``T`1``*P"```\>4``J#>``/P`P`"9F```_0```/P00`"9W```V`1``/T +M```"!]$``X#A``.!!@`"81X``X+G``-J1``#@08``^@@``-J1``","@``T`L +M``!:'0`"M`0``^CP``)$0``#\`$``K\!``*DO@`#\`$``K\$``(O]```\AD` +M`/(7``(R6@`","@```HM```1_@``&?T``K`"``*@W@`#\$$``K`$``."*@`" +MI#X``_`!``*S!``#@18``F`!``*Q,P`#:F```T`*``*P`0`"L0(``K(```*S +M`0`"H"L``_`"``/H(``#P3L``VIH``/!#@`"L@D``D`)``/P`0`#Z"```K`3 +M``*Q`@`#@B8``^@P``-J<``#0"T``&($```2&@`"OT```D]/``/P`P`"H,X` +M`_`!``*R"``"L"$``K$"``*S`0`#:G0```H=``*P`0`#_P```J0>``/P!0`" +ML00``K(```*S`@`#]`0``_`#``*Q`@`"L@\``K,!``-J>``#2GP``_\```*P +M`0`"L0(``K,$``-J?````>X``_\```/_```"P`X``('N``(O[P```AD``T`* +M``-`#0`"H`X``_`%``*P`0`"0`4``X`&``/T!``#\`,``K`$``)`"0`#@`(` +M`K$#``)H`0`#2F0``K2````1X``"0`0``F`(``.B(@`"LP$``VID``(NO0`" +M+LH```'O``/_```#_P```J`.``/P3@`#0"P``KP$``/``/P +M`0`#Z/```]CP``*\"0`"OS\``DL?``(MBP`#Z/```/GO```![@`#_P```_\` +M``+`#@``@>X```GM``-`+@`#_P```J`!``('2@`"L`$``D$(``/P!0```C(` +M`_\```/_```"H`X``@=*``-*=```>AH``_\```/_```#P2\``VIT``(O[P`` +M`AD``T`*``-`#0`"H`X``_`%``*P!``"0`4``X`"``/T!``#\`(``K`0``)` +M"0`"L4,``F@!``-*9``"M(```!'@``)`!``"8`@``Z(B``/!/@`#:F0``BZ] +M``(NR@``0@,``K`"``*@W@`#\$$``X`"``)`"``#\`(``_0```''>@`#0`X` +M`K`0``/_```"00D``J`0``('>@```AD``_\```/_```"P`X``((9``*Q`@`" +MI`$``@<```!!X````<,``_\```/_```#V(```]P```*_"0`"L````]P!``*P +MP``#V`$``BZT```![@`#_P```_\```+`#@``@>X``B_O``-`"@`"L$```_\` +M``)`"0`#H`(``K$#``)H`0`#2F0``K2````1X``"0`0``F`(``.B(@`"LP$` +M`VID``(NO0`"+LH``T`E``!"`P`"L"```D`%``/P2``"L`(``J#>``/P00`# +M@`(``D`(``/P`@`#]````<>_``-`+@`#_P```K`(``)!"``"H!```@>_``*P +M@``"00@``_`%```",@`#_P```_\```*@#@`"![\```(7``/_```#_P```^@0 +M``"*%P`"I`X``@``/P`P`"NV(``_0```/P00`"NV@``_\```/_ +M````V>P``C"E``/H```"+H(``KL"``#:,``#Z````K%@``/H(``#Z#```VF@ +M``/H```"L1```^@@``/H,``#:D0``C`H``*_`@`"+_0``K#_``*Q_P`"LOT` +M`K/_``-H8```"BT``!'^```9_0`"L`H``J#>``/P00`"L`P``X$6``)@`0`# +M@BH``K$,``)B(0`"I#X``_`!``*S!``"L18``VI@``./X0`"+00``VAK``/_ +M```#_@```C`>``-*8```,?X``#G]``*T]@`"M0,``X9J``)B)@`"I'X``_`! +M``*W!``"8S<``P$5``)`!``#:F```T`*``*P`0`"L0(``K(2``/H,``#:F@` +M`K"```*R"0`"0`@``_`!``/H(``"L!,``K$"``.")@`#Z#```VIP``*P(0`" +ML0(``K(!``*S`0`#:G0``K`!``*Q`@`"L@\``K,!``-J>``#2GP``_\```*P +M`@`"L0(``K,$``-J?``",EH``^AP``-*9``"M(```D`$``/H(``#Z#```VID +M``-*9``"M(```!'L``)`!``"M`,``F`$``*S$``#:F0``BZ]``-*9``"M`@` +M`D1```)G=``"I7X``@AX``/H```"+H(``J#>``/P`P`#2"(``_0```/P00`# +M2"8``_\```.,Y@`"RJP``_!!``++O@`"H-X``_`$``-H(@`#:HH``_0```/P +M0@`#:"8``VJ.``/H<``"IKX``_`"``/T```!R#T``T`2``#QY0`"OP0``J#> +M``/P`P`":J\``_0```/P00`":[\``V`2``/T```!R'@``C`>``/L````:C$` +M`/'O``/H````@@8``T`(``-`+0`"N$```KD(``)*"``#JJH``DM)``.KM``" +MLP0``F^K``/P00`"LP(``D^K``/P`0`"LP8``T`M```*!``"L$```D!```/P +M00`#Z#```D$>``/P`0`#Z#```)H%```AY0`#Z%```_\```"B#```J>4``&HQ +M``/_```#_P```J#>``/P`P``6<(``_0```/P0P``6<$``_\```/_````V>`` +M`T`L``!:'0`"M`0``^CP``)$0``#\`$``K\!``)+O@`"H+X``_`#``(O)0`# +M]```````D`$``)@"``#HB(``K,!``-J9``" +M+KT``B[*```![P`#_P```_\```*@#@`#\$X``T`L``*\!``#W,```K_```*@ +MW@`#\`$``K^```/8\``#C.0``K\_``)+'P`"+8L``^CP``#Y[P```@8``_\` +M``/_```"P`X``((&``!:'0``>A<``K@(``)*O@`"H*X``AH``_\```/_```#P2\``VIT``(O[P```A@``T`*``-`#0`"H`X` +M`_`%``*P"``"0`4``X````/T!``#\`,``K`@``)`"0`#H````K%#``)H`0`# +M2F0``K2````1X``"0`0``F`(``.B(@`"LP$``VID``(NO0`"+LH``$(#``*P +M`0`"H-X``_!!``.``@`"0`@``_`"``/T```!R;4``T`*``/_```"L$```D$( +M``*@$``"";4```(8``/_```#_P```L`.``""&``"L0(``J0!``()#````>`` +M`$'#``/_```#_P```]@```/<@``"OP@``K#```/8`0`"L````]P!``(NM``` +M`@8``_\```/_```"P`X``((&``(O[P`#0`H``K"```/_```"0`D``Z`$``*Q +M`P`":`$``TID``*T@```$>```D`$``)@"``#HB(``K,!``*Q```#:F0``BZ] +M``(NR@``0@,``K`!``*@W@`#\$$``X`"``)`"``#\`(``_0```')]P`#0"X` +M`_\```*P"``"00@``J`0``()]P`"L(```D$(``/P!0```C(``_\```/_```" +MH`X``@GW```"%P`#_P```_\```/H$```BA<``J0.``()3P``8<,``%G@``/_ +M```#W,```]BP``*_"``"L,```]@!``*P```#W`$``C)@``-H:P``$=8``K/` +M``/HT``#8G0``BVS``(P'@`#[````&HQ``/_```#_P```J#>``/P`P`"NU,` +M`_0```/P00`"NUL``_\```/_````V>P``T`0``*['P`#2"D``J#>``/P00`# +M2"T``D`+``.O`@`#C/8``FS/``./]@`#2>@``J-,``/P!``#!$P``P5?``/T +M```#\$(``P3$``,%]0`#P00``\$5``*@W@`#\`0``V@I``-IZ``#]````_!" +M``-H+0`#:>P``^@```(NE@`#Z````K%```/H(``#Z#```VI$``(P*``"OP(` +M`B\E```*+0``$?X``!G]``*P`@`"H-X``_!!``*P!``#@18``F`!``."*@`" +ML80``F(A``*D/@`#\`$``K,$``*Q&``#:F```T`*``*P`0`"L0(``K(*``/H +M,``#:F@``K"```*R"``"0`@``_`!``/H(``"L!,``K$"``.")@`#Z#```VIP +M``*P(0`"L0(``K(!``*S`0`#:G0``K`!``*Q`@`"L@\``K,!``-J>``#2GP` +M`_\```*P`@`"L0(``K,$``-J?``",EH``^AP``(O[P`#2F0``K2````1[``" +M0`0``K0#``)@!``"LQ```VID``(NO0`#2F0``K0(``)$0``"9W0``J5^``(* +MN0`#Z````BZ6``*@W@`#\`0``T@J``-)Z``#]````_!"``-(+@`#2>P``KP1 +M``*_$``"R9\``LB,``/P00`#Z(```\$(``/!&0`"H-X``_`$``-H*@`#:>@` +M`_0```/P0@`#:"X``VGL``-*P``"OP<``DW>``/P`0`"OS@``J&.``/P0P`" +M8`\``VK```-JQ``"NP$``-HP``/H<``"3_```_`#``*[_P`"H(L``_`"``/T +M```!RG$``T`2``#QY0`"OR```J#>``/P`P`":J\``_0```/P00`":[\``V`2 +M``(P*``"+^\``^P```!J,0`#Z````(',``*@W@`#\`,``%G"``/T```#\$$` +M`%G!``*P?P`"H?X``_!!``)+L```V>```C):``(P*```"BT``!'^```9_0`" +ML`(``J#>``/P00`"L`0``X$6``)@`0`#@BH``K&```)B(0`"I#X``_`!``*S +M!``#0!4``K$(``*V\``"1$8``F$4``-J8``"L`$``K$"``*R```"LP(``VIH +M``*R"``"L!,``K$"``.")@`#Z#```VIP```2&@`"L"$``K$"``*S`0`#:G0` +M`K`!``*Q`@`"L@\``K,!``-J>``#2GP``_\```*P`0`"L0(``K,$``-J?``" +MO`0``]S```!YX``"O`@``KL8``/8\``"+8L```',``-`)0`"OQ```L`.``"! +MS``"3U\``_`#``(KQ``#]````````G#``-`)@`" +MLA```]@```/<$``"OP@``K#```/8`0`"L````]P!``)"*0`#\$4``Z_P``*@ +MS@`#\`(``_L```/[`0`#T)X``_L```*E+@`#\`$``_L```/4GP`#^P$``J4N +M``/P`0`#^P$``P_^``(+.```````_\```/``/L```"O!$` +M`J#>``/P!``#2,P``TC1``/T```#\$(``TCL``-(\0`"*Z\``J#>``/P!``# +M:,P``VC1``/T```#\$(``VCL``-H\0`"H-X``_`$``-(U``#2-D``_0```/P +M0@`#2/0``TCY``(KKP`"H-X``_`$``-HU``#:-D``_0```/P0@`#:/0``VCY +M``*@W@`#\`,``TC<``/T```#\$$``TC\``*@_@`#\`0``L`,``+!'``#]``` +M`_!"``,`#``#`1P``J#>``/P`P`#:-P``_0```/P00`#:/P``^P```*@_@`# +M\`H``L`,``+!'``"PBP``L,\``+$3``"Q5P``L9L``+'?``#]````_!(``,` +M#``#`1P``P(L``,#/``#!$P``P5<``,&;``#!WP``^P```*P?P`"L?\``K+_ +M``*S_P`#:&```VAK```)U@`#Z````Z`#``/<```#V!```T`Q``*_`P`"O`(` +M`J;\``/P00`#P0<``J#\``/P00`#P08``J#^``/P00`#P04``J'^``/P00`# +MP00``\$0``/!(``#P3```_X```/4'@`#:&L``_L```,/_@`""](``T`U``*_ +M`P`#_P```J;\``/P00`#P0<``J#\``/P00`#P08``J#^``/P00`#P04``J'^ +M``/P00`#P00``\$0``/!(``#P3```_X```/4'@`#:&L``_L```,/_@`""^H` +M`T`X``-`/0`#_P```VJ$``-J@0`#[````TA5``/_```"M\```VA5``-H:P`# +M2`H``K]_``)H^``"LN\``Z"!``*_PP`"0`\``T`E``*S_P`"L?\``D]>``/P +M`@`"N?,``D(I``*\`@`"3%P``_`!``*S^P`#:&```TA```./\@`"O/L``D$< +M``)A'P`#:$```VAK``-)?``"M`0``F$4``-I?``#2I0``_\```-A?``#:&L` +M`C%$``/_```#_@```_\```-!20`#_P```_\```-(9``#B$0``ZB```/H\``# +MW/```KQ```+,R``#V,```]0>``(R9@`#84D``TAD``*T$``"1B0``_`"``/T +M```!S)D``TAD``-(-@`"M$```K4$``)$0``"15@``F1%``',60`"M#P``D1` +M``/P0P`".KT``_0```',*P`#0!0``_\```/_```"PSX``_!!``,#/@`#8!0` +M`K#_``*Q_P`"LO\``K/_``-JD``#^`,``TAD``.,Z@``\@@``DP,``',=0`# +M2#0``KP$``),P``"#&0``.((``-)/0`"OQ```D1/``/P!``"O_\``BT$``*_ +M4``"+00``K\(``(M!``#]``````#:"T``VGM``-(R``"N/L``D(H``-HR``#2)@``K@@``)@"``# +M2+$``VB8``)D2``#:+$``\$.``/!'@`#P2X``\$^``-C,``#8W```V,T``-C +M=``#8S@``V-X``/T```!S/```T@(``*X]P`"0`@``V@(``-(*``"N-\``D,X +M``-(+0`#:"@``VGH``)'>``#:"T``VGM``-(R``"N`0``F(H``-HR``#2)@` +M`KC?``)`"``#2+$``VB8``)$2``#:+$``K`#``/!$``#P2```\$P``-C,``# +M8W```V,T``-C=``#8S@``V-X``-)?``"M/L``D$4``-I?``#:&L``_0```', +M*P`#2,H``K`$``*Q0``#P2@``F@@``-HR@`#P8(``VC*``)H(0`#:,H``\&" +M``-HR@`#[````_@#``,/_@`"#04``^P```(R8``#:&L``\$.``/H$``#Z"`` +M`^@P``.D$P`#V!```]Q```/^```#U!X``VAK``-II``#Z````_X```/4'@`# +M:&L``VFD``*@W@`#\`,``T)@``/T```#\$$``T)<``-"60`"H-X``VFD``/P +M`P`#0F@``_0```/P00`#0F0``VFA``/^```#U!X``VAK``-H:P`#[````C)@ +M``-H:P`#0E8``\$.``/H$``#Z"```^@P``.D$P`#V!```]Q```/4'@`"OP(` +M`BT$``-H:P`#::0``VFB``/H```#_P```]0>``*_`@`"+00``VAK``-II``" +M,F```VAK``/L```#:&L``C)@``*Q#``#Z````Z`#``/<```#V!```^@```/H +M$``#Z"```^@P``/^```#U!X``VAK``/[```#^P```XCC``/X`P`#_@```]0> +M``-H:P`#^P```PB.``(-6@`#[````"'K``/H4``#Z&```^AP``*Y"``"N,`` +M`]R!``/8D0`#0E```_@#``/_```#:D0``^@@``,!'@`#:D0``_X```/47P`# +M:&L``\&/``*_$``"+00``K09``/^```#U%\``VAK``*_$``"+00``\'X``-) +MI0`#_P```_\```/!!``#P14``X+@``)B)@`#P3<``VFD``*Y"``#"9X``@V' +M``-II0`#[````J&^``/P#@`#T!X``P^^``*Z/P`#``X``D`*``,!'@`"01H` +M`P(N``)"*@`#`SX``D,Z``/4'@`##_X``@V0``/[```##,X``@V+``/L```" +MH;X``_`.``/0'@`##[X``KH_``+`#@`"0`H``L$>``)!&@`"PBX``D(J``+# +M/@`"0SH``]0>``,/_@`"#:0``_L```,,S@`"#9\``^P```/0G@`#T!\``KP_ +M``)(C``"29P``DJL``)+O``"1`P``D4<``)&+``"1SP``KP@``+`A``#H``` +M`J-(``/P00`"P`P``L&5``.A$``"HUD``_!!``+!'``"PJ8``Z(@``*C:@`# +M\$$``L(L``+#MP`#HS```J-[``/P00`"PSP``KP'``*CW``#\$X``W)T``-2 +M=@`#_P```_\```/^```#U!X``VAK``/[```#<``&(P``/_```"H;X``_!"``#QY0`#[````J'.``/P00`#"[X` +M`PS.``#9YP``XC```TI```!:"0`#_P```_\```*@O@`#\$(``Z````.B(``# +MT%X``&'\``!9XP`#H@(``D`)``)"*0`"I8X``@X\``,(C@`"H`T``_`+``!! +M^0`"Q$\``\%4``*@O@`#\$8``]1>``+$3P`#P50``]1>``+$3P`#P50``J6N +M``(.3``#"JX``J`M``/P"P``4?D``L9O``/!=@`"H+X``_!&``/47@`"QF\` +M`\%V``/47@`"QF\``\%V``/47@`##,X``@Y-``)KB@`"#A$``^P```-"<``` +M4@D``X_L``.(Z@`#JJ```J2N``/P!@`"H4@``_!!``+$2``"H6@``_!!``+& +M:``#BN```J9/``/P0P`#!$@``P`(``,!&``"IF\``_!#``,&:``#`B@``P,X +M``,*K@`"#F```\%4``/!=@`#U%X``V)P``/L```#P6(``P9N``/<$``#V``` +M`]">``/_```#_P```DB%``)H@P`#P9@``\&H``/!N``#U)X``_L```,&;@`" +M#G4``^P```/!$``#P2```\$P``*@W@`#\`@``VD(``-I#``#:1```VD4``-I +M&``#:1P``_0```/P1@`#:2```VDD``-I*``#:2P``VDP``-I-``#[````\$0 +M``/!(``#P3```J#>``/P!P`#:,P``VC0``-HU``#:-@``VC<``/T```#\$4` +M`VCL``-H\``#:/0``VCX``-H_``#[````TG4``/_```#@.4``D$#``-)T@`# +M_P```D`*``-)P@`"I1```_`!``-)Q@`#[````]">``/[```#_P```_\```/4 +MGP`#^P$``P_^``(.M``#[````_@!``*_!``#2F0``_\```/_```"00\``@Z_ +M``/X`P`#:&L``_\```/_```#_@```^P```-*9``#_P```K\(``)!#P`"H!\` +M`@[K``-`$@`"M1```J4E``/P!``"OP(``FJO``/T```!SN@``L15``*E)``# +M\`0``K\"``)KOP`#]````<[H``+$10`"I20``_`$``*_$``":J\``_0```'. +MZ``"OQ```FN_``#QY0`#8!(``B[L``/L```#2G```_\```/!P@`#0"X``K`! +M``-JJ``#_P```_\```/_```#2JP``K0"``-JJ0`#P````\`1``/`(@`#P#,` +M`TJM``*_D``"H,\``_`%``/`1``"OP\``D]/``/T```#\$$``\#T``*@W@`# +M\`L``T`9``/_```#_P```F1```)E40`"9F(``F=S``-@&0`":J\``_0```/P +M20`#0!T``_\```/_```"9$```F51``)F8@`"9W,``V`=``)KOP`#8"X``^P` +M``+$1@`#:JD``_\```/_```#_P```TJL``/L```",F```VAK```)U@`"L,`` +M`]P```/8$``"H/X``_`$``/H```#Z!```^@@``/H,``"H/X``_!$``*P50`# +MP1```\$@``/!,``"H/X``_`$``*T_P`#P50``\%D``/!=``"H/X``_!$``*T +MJ@`#P50``\%D``/!=``"O`<``J/^``(/G@``0?\``_\```/_```"H(X``@]D +M``*@W@`#\`,``T!"``/T!``#\`$``T!&``#J,0``:@```J3>``/P!``#P(@` +M`\"9``/`J@`#P+L``F`(``)A&0`"8BH``F,[``)D2``"95D``F9J``)G>P`` +M:C$``_\```/_```#_@```]0>``-H:P`#^P```PS.``/^```#U%X``VAK``/[ +M```##,X``@]D``!!_P``8@```_\```*@C@`"#XD``T!*``*DS@`#\`0``\"( +M``/`F0`#P*H``\"[``*DW@`#\`(``\&*``/!FP`"N@\``DJH``.+I@`":JL` +M`KOP``)+N``#I[8``FNW``/T!``#\`,``^A@``/HH``#Z+```K3P``)@2@`# +MP1```\$@``/!,``#:H0``J#^``/P`P`"M/```_0$``/P`0`"M`\``F!+``/! +M$``#P2```\$P``-J@``#[````_X```/4'@`#:&L``_L```,,S@`"N0,``J#Y +M``/P1``#Z$```^A0``/H8``#Z'```_X```/47@`#:&L``_L```,,S@`"#ZH` +M`K#P``*Q_P`"H/D``_!"``/H```#Z!```\$A``/!,0`#:H0``VJ```/L```" +M,F```VAK```)U@`"L,```]P```/8$``"M/\``\%D``/!=``"O`<``_X```*U +M_P`#U%X``VAK``/[```##,X``_X```*U[P`#U%X``VAK``/[```##,X``@_% +M``*U_P`#:H4``K#P``/!$``#P2```\$P``-J@``#2F(``B_O``/H\``"+_L` +M`VIB```)U@`"L,```]P```/8$``"M/\``\%4``/!9``#P70``KP'``/^```# +MU%X``VAK``/[```##,X``@_G``(R6@`#[````TID``*T@``"0`0``VID``/L +M```"L`0``J#P``/P`P`"+R4``_0$``/P`0`"*\0``^@```/````#P1```K+] +M``*S_P`#:&````HM```Q_@``&?T``K`*``*@W@`#\$$``K`,``.!%@`"8`$` +M`K$6``*D/@`#\`$``K,$``.&:@`"L@(``K0$``*C_@`#\`,``J#T``/P`0`" +ML@0``F(F``-J8``#C^$``BT$``-H:P`#_P```_X```/L```#2F```_\```/H +M,``"8`X``VI@``-*9``"M/(``D`$``-J9``#[````K`!``/H$``#Z"```^@P +M``-J8``#[````&HQ``-`!``#_P```K@$``)%X``"H%X``_!$``*@W@`#\$(` +M`T`D``/_````8>D``_\```/_```#W,```X_J``*@W@`#\`$``^CP``/8\``# +MC.0``\&R``-)/0`"H-X``_!!``-)00`#_P```D1N``/P`0`"O`<``D`(``/P +M0P`"+8L``_0```/P00`"+9\``D1N``/P"0`#Z,```ZMB``.E8``"15X``_!# +M``(MBP`#]````_!!``(MGP`#[````&HQ``-`*``#0"$``KA```)(@``#\`,` +M`D9N``/P`0`#[````%'#``*@W@`#\`,``%G"``/T```#\$$``%G!``/_```# +MW*```]BP``-`!@`#_P```_\```)`Z``#\`,``J#>``/P00`#0"8``_\```/_ +M```#24$``J`.``/P0P`"H-X``_!!``/!10`"14X``_!#``.,Y``#]````_!! +M``.,XP`#B9@``ZJ<``*@K@`#\`,``BV+``/T```#\$$``BV?``-`!@`#_P`` +M`_\```-)00`"0(X``J`.``/P0P`"H-X``_!!``/!10`#Z,```ZM"``)%3@`# +M\`<``Z5```)%7@`#\$,``BV+``/T```#\$$``BV?``/L```"H-X``_`#``-( +M(0`#]````_!!``-()0`#_P```KL!``.B9@`#@68``Z$6``.'=@`"8"<``P`+ +M``.G!@`#@@8``F8A``*@W@`#\`0``V@A``-JB0`#]````_!"``-H)0`#:HT` +M`^P```./[0`"+00``^@P``."X``#Z!```^@```-H,``#C^T``BT$``-)G``# +M2=4``T`J``*T$``"1$<``K4"``)%6@`#A50``D1%``.D0``"MO<``D(F``)B +M)``#:9P``^CP``-)<``#Z'```K8!``/!40`#P4```V@Q``*X_``#P5,``\%" +M``)%6``#270``C%$``-H,0`#P5$``\%```(Q1``#:#$``\%3``/!0@`#29@` +M`C%$``-H,0`#P5$``\%```(Q1``#:#$``\%3``/!0@`#29P``C%$``-H,0`# +MP5$``\%```(Q1``#:#$``\%3``/!0@`#2.@``C%$``-H,0`#P5$``\%```*W +M$``",40``V@Q``/!4P`#P4(``C%$``-H,0`#[````KP#``-)/0`#2,```J#> +M``/P00`#2,0``K8$``)&9``#\`0``X;*``/!?``"8B8``F$7``*@W@`#\`,` +M`VC```/T```#\$$``VC$``-`*@`"M@@``X?&``)&:``#\`$``F,W``*@W@`# +M\`,``VC```/T```#\$$``VC$``/L```","X``TD]``-`*``"OP@``KP$``)/ +M#P`"3$P``F#\``/P00`#[````K0!``(R`@`",0D``J'^``/P"0`",<,``C`H +M``-`*``"O`@``_\```)"+``#\$(``_0```/P0@`#Z$```C("``/L```#_P`` +M`_\```/_```#[````V%(``-A00`#848``V)_``/!#@`#Z!```^@@``/H,``# +M:.@``^@```/_```#:.@``T%(``/_```#[````C)@``-H:P``"=8``^@```.@ +M`P`#W````]@0``*\`P`"L/<``J#^``/P00`"L'\``\$0``/!(``#P3```K3_ +M``/!5``#P60``\%T``/^```#U!X``VAK``.@`0`#H1$``Z(A``.C,0`#^P`` +M`_X```/47@`#:&L``_L```,,S@`"$6H``K#_``*\"``#P1```\$@``/!,``# +M:H```J3^``'1A``"H/P``_`"``-JA``#[````K#W``.A`0`#HA$``Z,A``-J +MA``#[````TD]``/_```"N(```F1(``-I/0`"L`$``K$"``*R/P`"0B\``K,` +M``-J:``"L````K$```*R```"LP(``VIT``*Q`0`"L@@``K,```-J>``"L`$` +M`K(```-J?```$>```K#```)`#P`#H`(``F`.``*Q```#HB(``K,!``-J9``" +M,EH``_\```/_```"+KT``KA_``)$2``#:3T``^P```/H```#Z!```K(#``*S +M```#:#```^P```-`+``#_P```_\```/H(``#Z#```V`L``/H```#Z!```V`8 +M``-@'``#[````&HQ``*P`0`#Z!```^@@``/H,``#:F```K\!``(O]``"OQ`` +M`BT$``*\%``#2F```K_W``*Q$P`"0`\``F`.``-J8```,?X``#G]``*\B``" +MM?X``X9J``)B)@`"I'X``_`!``*S!``"0`4``F`,``-J8``#2I8``T`I``*\ +M!P`#_P```D1,``.%1``"Q5X``\'%``/H0``#Z&```^AP``-JD0`"M/\``K5_ +M``*V_P`"M_\``VAA``-H:P`#_P```_X```,)G``#\$4``\"9``,*K@`#\$(` +M`\"J``,+O@`#:I(``X````.@```#:F```K\0``(M!``#[````T`J``-'+``" +MH-X``_!!``-';``"MG\``X5,``)`!@`"29X``_`!``)@!0`#P1```\$@``/! +M,``"H-X``_!$``-G+``#9S```_0```/P0@`#9VP``V=P``-'-``"H-X``_!! +M``-'=``"MM\``X5(``)`!@`"8`4``\$0``/!(``#P3```J#>``/P1``#9S0` +M`V``#2,```J#>``/P00`#2,0``K8_``)")@`" +MML```J!.``/P`0`#Z&```F(F``*@W@`#\`,``VC```/T```#\$$``VC$``/L +M```#29@``K@/``*Y]0`#Z#```\$N``*@S@`#\`(``F`(``)!&0`#:#```T@( +M``*X0``"H,X``_`"``)@"``#:`@``^P```-A4``#854``V%:``-A7P`"+`4` +M`T%0``-!50`#05H``T%?``/_```#_P```^P```*P_P`"L;\``K+_``*S_P`# +M:&```^P```*P?P`"L?\``K+_``*S_P`#:&```^P```+$3@`#\$H``L5>``/P +M2``"QFX``_!&``+'?@`#\$0``K3_``*U_P`"MO\``K?_``/L```"H```^A```*@`0`#\`$``\%.``(T7@`#2`@``KC^``)! +M&``#:`@``KP```(T"0`"-#H``T`!``/H````@C$``D1.``/P`0`")!L``T`! +M``/_````\C$``D5>``/P`0`")!L```(*``.!X``#_P```J00``/P`@`#P4X` +M`C1>``*\`0`"-`D``C0Z``*_`P`"-N0``C-%``(NJ``"OQ```D_[``'2X``` +M>``(N@@`"+J@``X3B``/!R0`#Z````K$#``*R"@`"LRT``D1)``/P +M`0`"LR\``^A0``(N<0`#@.H``BYQ``*X+P`"M`(``D1,``/P`0`"N"T``\&8 +M``/!J``#P;@``V,J``-C:@`"+J@``X'B``.@X0`#HY8``X+D``+"+@`#Z%`` +M`BYQ``.@XP`"+G$``BZH``.`Y0`"0*```\$0``/!(``#P3```V2H``-DZ``# +M@.```H((``)"`@`#@B0``)'4``/L```"+J@``TEP``)$C@`#\$,``K7S``)" +M)0`#:7```^AP``/!;@`#P5,``\%"``-H,0`"1)X``_`&``-)G``#I.$``F(D +M``/!4P`#P4(``V@Q``)$C@`#\$T``TF<``.$Y0`#I><``D`$``)!%0`#I(@` +M`X1```.%1@`"8`4``F$4``/H,``#P2X``V@P``-#I```(=0``K7/``)`!0`" +M8`0``\$0``/!(``#P3```V.D``-CY``"1$0``_`!``(VWP`#@80``Z`8``." +M!@`"8`(``^C0``(NE@`#P=X``BZ6``-)T0`"+J@``^A@``*E$``#\`$``K9` +M``)&=@`#IFH``TB0``.DN@`"H$X``_!!``*T!``"M0,``J!%``/P00`"M`$` +M`K7P``)!%0`"810``K?^``)`!P`"8`8``VB0``-HJ``#:)0``VBL``.@I@`# +MP1```\$@``/!,``#9"0``V1D``.*M@`#2"$``K@#``)F:``"N0\``KO^``)& +M:0`"9FH``D=[``-H(0`#:HD``V@E``-JC0`#2`@``T@A``)A'@`#:`@``TC( +M``*X0``"N3\``F`(``-HR``"0`D``VC(``/L```#0"P``KA```*Y@``#Z+`` +M`TJE``)(&``#\`$``KLP``))&0`#\`(``KK```)KN@`"95L``VJE``-(G``" +MO'L``D`,``-HG``#:*```VBT``-HN``#2)```X3B``.EYP`"014``F$4``*U +M_@`"0`4``VB0``-HJ``#:)0``VBL``-).``"M0,``F(E``*U\P`"0B4``VDX +M``/HP``"-`D``C0Z``/H```"L0,``K()``/H,``#Z%```BYQ``.`Z@`"+G$` +M`K`$``/!$``#P2```\$P``-C)``#8V0``K`@``/!$``#P2```\$P``-C*``# +M8V@``C;?``-("``#2"$``ZCM``)!&``#:`@``ZCK``)&:``#P#X``D53``-H +M(0`#:HD``V@E``-JC0`#Z$```C1>``/H\``"/B(``K\!``(^(@`#[````T`I +M``/_```"N"H``D)>``/P`0`"N&0``J#.``/P00`"N!```\&8``/!J``#P;@` +M`V@`# +M0[(``K`'``*Q*``"0EX``_`!``*Q.``"H,X``_!!``*Q```"2(```FB!``/! +MF``#P:@``\&X``-CL@`#8_(``^P```-)=``#P6X``^AP``.(2``#P%X``X59 +M``)")0`"8B@``VET``/!0@`#P5,``V@Q``/L```"OP,``C;D``-("``"N/X` +M`D$8``-H"``#0"@``KP$``*_`P`"3,(``_`#``(]RP`#]````=2+``-(5``" +MO&```F(L``-H5``#278``KP0``)DK``#P5L``\%N``/H<``#:#$``C%$``-( +M5``"O)\``D(L``-H5``#P4H``V@Q``(SJP`#0D4``C)F``-B10`#[````&HQ +M``(M"```\>4``C`H``(KQ``#0`0``T`*``*\`P`"OP0``D$<``,&\0`"0`\` +M`_`!``/!:@`"Q&X``K4(``(XK@`#0`0``^CP``#Z!@`"LD```D(@``'4\0`# +MCP0``Z_X``#Z`P`#2<$``KP/``)(SP`#J(```XF"``*ZXP`"1$H``F1)``)) +M_@`#B9(``KK[``)%6@`"95D``VG!``.!A@`"8($``BZ6``/H```"H-X``_!! +M``*P0``"L0,``K()``*S"0`"1.\``_`!``*S#P`#Z%```BYQ``*C_``!U/$` +M`^C```#AS@``X>X``C97``-`&``"H-X``_!!``-`'```0>X``&'.``)@`0`" +M8`(``F`#``*A#@`#\$(``LB.``#![@`"N@(``KL?``+,R@``X4``_0` +M``'4J@`"S_X``/H#``#"!@`#]````=2J``/HP```X@,``^@P``(V:P`#W.`` +M`K\(``/8\``"L"```\$0``/!(``#P3```K\1``/4'@`#_P```_L```,/_@`" +M%/T``C97``!B`P`"L````]P```/<`0`#C\(``K!```+`#P`#V````K`_``)! +M#P`"L,```L`!``/8`0`#U%X``]`?``*_$``"H<\``=4>``*@W@`#\`,``\$* +M``/T!``#\`$``\$+``/4'P`#]````=4E``*@W@`#\`,``\$J``/T!``#\`$` +M`\$K``/4'P`"-@\``T`&``*P'P`"L2```\$N``)!&``#\`$``L(N``+,P@`` +MX@,``J7```(5`@`#2I0``_\```-@O``#0`0``]S@``*_"``#V/```]SA``*_ +M+``#V/$``K\(``.L%@`"I<\``_!#``/`S``";,\``LS.``*_"``#T!X``]!? +M``/!B0`#P9H``\&K``+$0``#H$```L`,``/!L``"Q5$``Z%0``+!'``"IAL` +M`_`!``/!L0`"QF(``Z)@``+"+``"IBL``_`!``/!L@`"QW,``Z-P``+#/``" +MICL``_`!``/!LP`#U!X``]2?``/[```#^P$``P_^``(50P`#03@``T%)``-! +M*@`"O`X``J,!``/P`0`#P0$``J,"``/P`0`#P0(``J,#``/P`0`#P0,``J,( +M``/P`0`#P0@``J,)``/P`0`#P0D``^@0``*F#``#\`,``\````.`!@`#H08` +M`\$!``*C10`#\`$``\%%``*C1@`#\`$``\%&``*C1P`#\`$``\%'``*C2@`# +M\`$``\%*``*C2P`#\`$``\%+``/H(``"IDP``_`#``/`1``#A$8``Z)&``/! +M,@`"H-X``_`#``-DJ``#]`0``_`!``-DZ``#W.```K\(``/8\``#T%X``K\$ +M``/<\0`"OX```J#>``/P00`"O\```]CQ``*\$``"N`$``KD#``*Z#P`"H4P` +M`_!%``/`1``"1$H``P0$``/T!``#\`(``P1*``+$0``"H5P``_!%``/`50`" +M15H``P4%``/T!``#\`(``P5:``+%4``"H6P``_!%``/`9@`"1FH``P8&``/T +M!``#\`(``P9J``+&8``"H7P``_!%``/`=P`"1WH``P<'``/T!``#\`(``P=Z +M``+'<``#U%\``_L```/[`0`#T%X``PB.``(5J0`"N`$``\$!``/!$@`#P2,` +M`PF>``(5J0`#1*@``J#>``/P00`#1.@``J%,``/P10`#P$0``D1*``,$!``# +M]`0``_`"``,$2@`"Q$```J%<``/P10`#P%4``D5:``,%%0`#]`0``_`"``,% +M6@`"Q5$``J%L``/P10`#P&8``D9J``,&)@`#]`0``_`"``,&:@`"QF(``J%\ +M``/P10`#P'<``D=Z``,'-P`#]`0``_`"``,'>@`"QW,``]1?``(L]P`"-&L` +M`C`H``(O[P`#[````BZH``.CE@`"-FL``X#E``)#H``"-G0``_0```'6`@`# +MP9```J'/``/P`0`#P9(``V)U``#)S@`#W.```K\(``/8\``#W.$``K\L``/8 +M\0`"OP$``\$$``(V,0`#0G4``K\!``/_```#P04``C8Q``-"=0`"OP$``_\` +M``/!!@`"-C$``T)U``*_`0`#_P```\$'``(V,0`````/4GP`#^P```_L! +M``.@```##_X``A8R``/L```"LQ```J7#``/P!@`#P#P``X,V``.C-@`"-G0` +M`_0```'69``"LP\``LS.``)#/``"-FL``C&X``(L]P`"-&L``K\8``(X7``" +M+NP``^P```*Q!``"L(```J#>``/P00`"L,```K()``/H4``"+G$``^P```/! +M`P`#P1,``\$C``*@W@`#\`,``V2H``/T!``#\`$``V3H``/L```#0`0``KR` +M``/_```"3,```=:>``-!`0`"H-X``_!!``-!!0`"L00``K"```*@W@`#\$$` +M`K#```/<$``#V````\&$``(VNP`#P84``C:[``/!A@`"-KL``\&'``(VNP`" +MH-X``_`#``-DH0`#]`0``_`!``-DX0`#]````=:X``*Q!``"L(```J#>``/P +M00`"L,```]P1``/8`0`"L0$``K`(``*@W@`#\$$``K`L``/<$``#V````K\( +M``(NM``#00$``J#>``/P00`#004``J#>``/P`P`#9*D``_0$``/P`0`#9.D` +M`BSW``(T:P`#[````\&8``/!J``#P;@``K(!``/4G@`#^P```P(N``(6OP`# +M[````K$$``*P@``"H-X``_!!``*PP``#W!```]@```*Q`0`"L`@``J#>``/P +M00`"L"P``]P1``/8`0`"OP@``BZT``-$J``"H-X``_!!``-$Z``"H-X``_`# +M``-A```#]`0``_`!``-A!``#[````TL,``*U_``"014``VL,``/L```#2PX` +M`\#/``)JKP`#:PX``C%$``)*K``#:PX``^P```*_`P`"-N0``TG```-)U0`" +M3`X``_!"``/T```!URD``TG```.'=@`"IG```_!"``/!$@`#Z````Z(6``.! +M%@`#H18``TB1``*\\``"15P``F52``*\!``"0PP``Z,R``*\_@`"1$P``F1# +M``-HD0`#:)4``VBI``-HK0`#P4$``\%1``/!80`#P7$``V0A``-D)0`#9&$` +M`V1E``*\`@`"0@P``X(J``-(G0`"LW\``D1#``)D0@`#:)T``VBA``-HM0`# +M:+D``J$.``/P`@`"-M\``K`0``/!$``#P2```\$P``-CH``#8Z0``V/@``-C +MY``#27P``K3[``)!%``#:7P``VAK``/T```!S`4``C`H``(O[P`#0````_\` +M``/_```#H`P``Z$<``.!$``"81```P\>``/HT``"H/X``_!!``*]`0`#P4``_\```)&8@`"9FH``VGE``/T!``"%W8``J/^``(7R@``T=`` +M`J#>``(7GP``T=$``LW>``/T!``"%U4``$'1``/_```#_P```J"H``(7R@`" +MH:@``_!(``-((0`#`XH``X,P``)G4``_\```)&8@`"9F@``VGE``-()0`#`Z@``X,P +M``)G4``KB```)G>``#8!$` +M`TF8``*U!``"814``VF8``-("``#_P```X3(``)@!``#:`@``_@#``/L```` +M:C$``^@```""#```@<\``CC*``-`)``#_P```_\```)`#@`#\$L``T`0``*\ +M!``#W,```X_J``*@W@`#\`$``^CP``/8\``"O`D``\&Q``(MBP`#0"0``_\` +M``*\`@`"0`P``_!+``-`$``"O`0``]S```*_P``"H-X``_`!``*_@``#V/`` +M`XSD``/!L0`"+8L``BO$``*T!@`"M0@``K8$``(XK@`"OQ@``CA<``(X@P`" +MOP```B_T``-*<``#_P```_\```*RD``#:G```K\3``(X7``".(,``T`D``/_ +M```#_P```D`.``/P2P`#0!```KP$``/``/P`0`#Z/```]CP +M``*\"0`#BQ```BV?``-`)``#_P```KP"``)`#``#\$L``T`0``*\!``#W,`` +M`K_```*@W@`#\`$``K^```/8\``#C.0``XL0``(MGP`#2G```_\```/_```" +MLH```VIP``*_&``".%P``CB#``*_```"+_0``TIP``/_```#_P```K*0``-J +M<``"OQ,``CA<``(X@P`#0"0``_\```/_```"0`X``_!+``-`$``"O`0``]S` +M``./Z@`"H-X``_`!``/H\``#V/```KP)``/!L0`"+8L``T`D``/_```"O`(` +M`D`,``/P2P`#0!(``KP$``/``/P`0`"OX```]CP``.,Y``# +MP;D``BV+``(X[``#[````^@```/````"L;\``\$@``*S_P`#:&```C`H``(O +M[P``"BT``!'^```9_0`"L`(``J#>``/P00`"L`0``X$6``)@`0`#@BH``K&$ +M``)B(0`"I#X``_`!``*S!``#P1\``VI@``-*9``"M(```!'L``)`!``"M`,` +M`F`$``*S$``#:F0``BZ]``*_"``"3_```_`!``#R#``#[`````(,``/_```# +M_P```J4.``/P`0`#[````^@```""#``#0!(``_\```/_```"1(X``J!.``/P +M`0``\>4``K]```*@W@`#\`,``FJO``/T```#\$$``FN_``-@$@`"+NP``'G/ +M``/_```#0"```J#^``/P00`#[````J#>``/P!0`"P`X``_!!``*P_P`#]``` +M`_!#``+!'@`#\$$``K'_``-@(```\<\``^P```*P`0`"L0(``\$D``/H,``# +M:F@``\$E``*P$P`"L0(``X(F``/H,``#:G```K`A``*Q`@`#P28``K,!``-J +M=``"L`$``K$"``*R#P`"LP$``VIX``-*?``#_P```K````*Q```"LP0``VI\ +M``/L```"L`$``K$1``*R$``#:D0``K$0``/H(``#:D0``J#>``/P`P`"NU,` +M`_0```/P00`"NUL``-GL``-("``"M$```('F``)@!``#:`@``T@!``*POP`" +M1$```V@!``/H```"L4```^@@``/H,``#:D0``K`!``/H$``#Z"```^@P``-J +M8``#[````C`H``(O[P``(>8``_\```-("``#_P```\$$``-H"``#[`````HR +M``!J,0`#_P```D`>``/P00`#[````CC*``*_```"+_0``#(:``/_```"M0D` +M`Z9B``+&;@`"Q&X``CBN``-`*``#Z/```J#>``/P00`"OT```/G@``.@#``` +M@<4``T`8``-`'0`#0"X``&'E``/_```#_P```V%0``-A50`#85H``.''``-` +M$```>>```KP$``/``/P00`#0!P``T`M``/_```#U!X``]1? +M``*_`0`#W/```K\```.(P@`"S_@``]CP``/$```#Q!$``\0B``/$,P`"H-X` +M`_!!``/!9P`"M1$``D1E``/$1``"P`0``K4B``)$90`#Q$0``L$4``*U1``" +M1&4``\1$``+")``"M8@``D1E``/$1``"PS0``]0>``!YQ0`#0G4``T)R``)/ +M_@`#\$(``L`!``+"(P`"H00``_!&``*E"0`#\`0``\&,``/!D``#P:P``\&P +M``*C"0`#\$(``\&L``/!L``#8G(``T)&``*A)@`#\$8``J4I``/P!``#P8P` +M`\&2``/!K``#P;(``J,I``/P0@`#P:P``\&R``-B1@`"H/X``=FN``-"3@`" +MH14``_!&``*E&0`#\`0``\&,``/!D0`#P:P``\&Q``*C&0`#\$(``\&L``/! +ML0`#8DX``T)*``*A-P`#\$8``J4Y``/P!``#P8P``\&3``/!K``#P;,``J,Y +M``/P0@`#P:P``\&S``-B2@`#8G0``_L!``*Q$``"S,X``.'.``*@P0`!V3<` +M`T)P``-"10`#_P```L@"``.H@``"RD8``ZJ@``-"3``#0DD``_\```+)`@`# +MJ9```LM&``.KL``".=L``T`0``!YX``"O`0``]S```/8\``"O`D``\&Q``(M +MGP`","@``B_O``-!4``#054``T%:``!AQP`#_P```_\```-@&``#8!T``V`N +M``#AY0`#[````KLA``*[$````<4``_\```/_```"0`X``=H3``.IA``#JZ0` +M`KP/``.(@``"2(P``XJ@``)*K``#2)P``TBA``*@W@`#\$(``TBT``-(N0`# +MP1@``\%:``.,%@`#CU8``F$<``)E7P`"H-X``_`$``-HG``#:*$``_0```/P +M0@`#:+0``VBY``-*Z``#2NT``J#>``/P0@`#2O```TKU``*\[@`"0`P``D1, +M``.(E@`";)@``F`,``.*M@`";+H``F1,``*@W@`#\`0``VKH``-J[0`#]``` +M`_!"``-J\``#:O4``^P```*_!0`"O````J#>``/P00`"O$```]SP``/8P``" +MO(```FB,``)IG``":JP``FN\``/!"``".BL``\$)``(Z*P`#P0H``CHK``/! +M"P`".BL``]2>``/[```#U)X``^P```/!$``#P2```\$P``/4'@`#^P```]0> +M``/[```#[````&HQ``(M"``#Z````K%@``/H(``#Z#```VF@``-)F``"N`\` +M`K7U``*S```#P2X``F`(``)!%0`#:#```K\!``(M!``".,H``C`H``-`+``" +MOP0``_\```)/\``#K_(``^CP``(O]```,AH``_\```*U"0`"Q&X``CBN``/H +M\``"H-X``_!!``*_0```^>```%G'``!YX``"O`0``]S```/8\``"O`D``BV+ +M``/HP```X``/P00`#P:L``&'.``!YQ@`# +MU%\``_L!``/07@`"NP(``J'^``/P00`#P4H``J#^``/P00`#P5H``J#[``/P +M00`#P6H``J;[``/P00`#P7H``]1>``/[```"L!```LS.``*EP``#\`8``.'. +M``/!C``#P:P``CG;``/T```!VFL``K$$``+/_@``^<8``J'Q``':I@`#Z+`` +M`-G.``/!BP`#P:L``CG;``!YX``"O`0``]S```/8\``"O`D``\&^``(MBP`# +MW.```KM```/8L``#]````=IK``(X[``"N`@``KH(``(YVP``6<<``'G@``*\ +M!``#W,```]CP``*\`P`"R[P``KP)``(MGP`#2`@``K1@``)@!``#:`@``TF8 +M``/_```"L@$``^@P``-H,``#[````K0$``)&-``"H&0``=K*``/H\``"/?0` +M`K\!``(]]``#:&L``T))``(R9@`#8DD``^P```-"30`",F8``V)-``-(9``# +M^`$``KT,``))T@`"H-D``_!&``*_`P`".RD``K\#``*]```".PH``^P```*] +M!``"2=(``_`&``*_`0`".RD``K\!``*]```".PH``^P```*]"``"2=(``_`& +M``*_`@`".RD``K\"``*]```".PH``^P```*]8``"2=(``J#9``/P1P`"OP,` +M`CNP``*_`P`"O0$``KP!``(["@`#[````KT@``))T@`#\`<``K\!``([L``" +MOP$``KT!``*\`0`".PH``^P```*]0``"2=(``_`'``*_`@`".[```K\"``*] +M`0`"O`$``CL*``/L```#2&(``DS.``(;'``#@/(``\````.!^``#P!$``DW> +M``/P10`#P````FJ@``)*H0`#]`0``_`#``/`$0`"2J```FJA``-H8@`"L/(` +M`FJ@``*@"@`#\$<``TA```*T"``"M?<``F$4``-H0``"014``VA```-H:P`# +M[````TA4``/H@``"N0,``DO^``/P`0`":(D``KH"``*Y#``"2_H``_`!``)H +MB0`"8S@``VA4``/HD``"M`0``DO^``/P`0`":9H``DOZ``/P`0`":90``TE@ +M``-H:P`#_P```_\```-(90`#_P```DO6``*@O0`!VZ<``DN3``';/@`#0"@` +M`X3P``"B`@`#HB8``C%$``,"+@`!VTT``T@$``*T[P``BBP``D$4``-H!``" +MM8```DO^``/P!0`#2)@``K2_``)#-``"8S4``VB8``)+^@`#\`4``TBP``*T +MOP`"0S0``F,U``-HL``"/>@``K8$``-H,0`#0"@``C%$``)!&@`"&Y```$H! +M``/_```"N(```\"9``.)G``"2_X``_`&``-)/``#2)D``F,X``)D20`#:3P` +M`VB9``)+^@`#\`8``TE```-(L0`"8S@``F1)``-I0``#:+$``DO^``/P!@`# +M2)```TB5``)@"``"9$@``VB0``-HE0`"2_H``_`&``-(J``#2*T``F`(``)D +M2``#:*@``VBM``-`*``#_P```K@$``)!&``#\`$``CQH``-`*``#_P```K@( +M``)!&``#\`$``CTJ``-(0``"N(```DO^``/P`0`"81@``DOZ``/P`0`"8B@` +M`VA```*\`0`#[````TA4``/`B``"0S@``VA4``-"20`",F8``V))``*\```# +M[````T`H``#YNP`"N`@``D$8``/P`0`"/4$``T`H``/_```"N`0``D$8``/P +M`0`"/+<``T`H``/_```"N`(``D$8``(;YP`"2_X``AO*``-)/``#2)D``KA_ +M``)#.``"1$@``VD\``-HF0`"O`(``DO\``(;U``#24```TBQ``*X?P`"0S@` +M`D1(``-I0``#:+$``DO^``(;W0`#2)```TB5``*X?P`"0`@``D1(``-HD``# +M:)4``KP"``)+_``"&^<``TBH``-(K0`"N'\``D`(``)$2``#:*@``VBM``-` +M*``#_P```K@.``)!&``"'`<``CT4``)!%P`#:,@``T@(``*X$``"01@``=P' +M``)+_@`"&_P``T@@``*T`0`"M?P``F$4``-H(``"014``V@@``*\`@`"2_P` +M`AP&``-()``"M`$``K7\``)A%``#:"0``D$5``-H)``"-N0``DO^``(<#@`# +M2)@``K3```)C-``"0S0``VB8``*\`@`"2_P``AP6``-(L``"M,```F,T``)# +M-``#:+```KP#``(]R``#2$```KA_``)+_@`#\`$``D(H``*\`@`"2_P``_`! +M``)!&``#:$```T@(``*X$``"01@``=PQ``-`*``"O`0``_\```),P@`#\`,` +M`CU8``/T```#\$$``CW+``).[@`!W#,``KP(``(]R```>;L``_\```/_```" +M2_X``_`$``-(F``"M#\``D,T``-HF``"O`(``DO\``/P!``#2+```K0_``)# +M-``#:+```TA```*X(``"2($``AQ*``(]Z``"M@(``V@Q``-)?``"N`(``DB` +M``(<40`#27$``CWH``-H,0`#2`0``"(L``/_```#_P```\$4``-H!```>;L` +M`K@```*\`@`#2%0``DO^``/P`0`"N`,``DO\``/P`@`"O`P``FB,``/`B``" +M0S@``VA4``.`^```@@(``^P```-("``"N!```D$8``'<>0`"2_X``_`$``-* +M5``"M`\``F`$``-J5``"NP(``DO[``/P!``#2E@``K0/``)@!``#:E@``DO^ +M``/P!``#2E0``K3P``)@!``#:E0``KL"``)+^P`#\`0``TI8``*T\``"8`0` +M`VI8``-("``"N!```D$8``'``/P`@`"OP`` +M`CWT``*\`@`"2]P``_`"``*_`0`"/?0``\']``/L```"M_P``K8S``*U1``" +MM````TC(``*@_@`#\$$``K0!``*\`@`"H/P``_!!``*T`@`"810``VC(``)@ +M!0`#:,@``_\```)`!@`#:,@``KP"``(]R``#[````T@(``*X$``"01@``=T[ +M``)+_@`#\`0``TB8``*T?P`"0B0``VB8``*[`@`"2[\``_`$``-(L``"M'\` +M`D(D``-HL``#23@``X3R``/`1``"0B0``VDX``/L```#23@``X3R``)B)``# +M:3@``T@(``*X$``"01@``_`!``/L```"2_X``_`$``-(F``"M(```F(D``-H +MF``"NP(``DN_``/P!``#2+```K2```)B)``#:+```^P```-"20`",F8``V)) +M``)!_@`"'64``T.N``*P[P`#_P```DNP``)*L``"2;```DBP``-CK@`"L0(` +M`D'Q``(=<``#0^X``K#O``/_```"2[```DJP``))L``"2+```V/N``-(5``# +MC/@``F(L``-H5``#270``KS?``)$+``#P5,``CWH``-H,0`",40``KP0``)D +M3``#:#$``C%$``-(5``"O)\``D(L``-H5``#270``_\```/!0@`#P5,``CWH +M``-H,0`"0?X``AV3``-#K@`"L!```_\```)KL``":K```FFP``)HL``#8ZX` +M`K$"``)!\0`"'9X``T/N``*P$``#_P```FNP``)JL``":;```FBP``-C[@`" +MO`(``D?^``),_``";,<``K<#``*@?P`#\$$``^C```-(R``"N$```KD_``)@ +M"``"81P``VC(``)`"0`#:,@``\#,``)!'``#:,@``KO\``)*_@`#\`@``T@A +M``/_```"95X``V@A``-JB0`"15L``V@A``-JB0`"N@(``DJO``/P"``#2"4` +M`_\```)E7@`#:"4``VJ-``)%6P`#:"4``VJ-``/L```##,X``=W(``/L```" +M/>@``K8"``-H,0`"M@$``TF>``*\$``"9*P``\%;``-H,0`#29H``KP"``)D +MK``#P5L``V@Q``/_```#270``KP0``)D+``#P5,``V@Q``(Q1``#P,P``D1, +M``-H,0`",40``\%*``/!6P`#:#$``^P```/!;@`"MP,``DW^``/P`@`"O0(` +M`D=]``*]`@`"3?T``_`"``*]`0`"1WT``^P```*X!``"L0(``K"```)/_@`# +M\`$``K#```(^0@`"L0,``K````)/_@`#\`$``K!```(^0@`"L0,``K"```)/ +M_@`#\`$``K#```(^0@`"L00``K````)/_@`#\`$``K!```(^0@`"L00``K"` +M``)/_@`#\`$``K#```(^0@`"3_X``_!'``-'-``#1SD``_\```-G-``#9SD` +M`_0$``/P!0`#1W0``T=Y``/_```#9W0``V=Y``/L```"N`0``^@```/H$``# +MZ"```^@P``)/_@`"'C```V4L``-E,``#930``V4X``-E/``#]````=XU``-E +M;``#97```V5T``-E>``#97P``K$&``*P```"3_X``_`!``*P0``"/D(``K$& +M``*P@``"3_X``_`!``*PP``"/D(``^P```+""``#W!```]@```/<$0`#V"$` +M`]`>``/07P`"O`<``]0>``/47P`#^P```_L```/[`0`#^P$``]`>``/07P`# +:#,X``=Y*``/L```#_P```_\```/_```#_P`` +` +end diff --git a/sys/contrib/dev/drm2/radeonkmsfw/TAHITI_me.bin.uu b/sys/contrib/dev/drm2/radeonkmsfw/TAHITI_me.bin.uu new file mode 100644 index 00000000000..74b24a00065 --- /dev/null +++ b/sys/contrib/dev/drm2/radeonkmsfw/TAHITI_me.bin.uu @@ -0,0 +1,194 @@ +begin 644 TAHITI_me.bin +M?$"``8@```#40`!_?$"``8@```!\0,`!S$``*,Q``"E\0(`!B````-A``![$ +M$``A)1```I4`__Y\00`!Q!P`)L0@`"38```50``"V4&BI,0;``+8```;S8``&L`^``3,`:'TS#T@!,0]``',```%V$`` +M&]A``!]\0(`!B````,Q!+@',02X"S$$N`\Q!+@!\0(`!B````'Q`P`&,``4B +MS$$N`'`&GQ"``$E)``! +MS,``(]%``"31@``ET<``)MA``">60``,Q"@`+)J`___()``M?EZ`#)J`_YP2 +M+``&!NP``0KL``&:P/__V$``)X```&#$*``LFH#__WQ`@`&(````?$#``<00 +M`##,027,?04```````ET```\Z!HKN````` +MEX```\Z!HK^`````SH&BPX`````$&```@``!0P08``'$)``F?$#``<9K`^08 +MT``P)-0`_P:H``'.I`/DFD``$L0X``?80``>Q#P`(2?\``*7P/_^S(``"Y>` +M``+90:*DQ#\``L_``!K`/@`$S`&A],P](`3$.0`!S```!=A``!_-0:#:S(`` +M"\U!+A29@``"S````LU```A\0(`!B````'Q`P`$8T`'H&-0`,!C8`#0%*`%K +M?$(``7Q"0`&5```'AH```(```86```&I@``!OX``!1Z```%[$50`$'X6``K, +M@``+U&$``)6`_HO`.@`$S#DA0,0Y``%\0(`!B````!%4`!!29``@?B8`&LU` +M`!W48@``E8#^@,0@`!V:`/__?$"``8@```#<.@``F4``&R><``&5P``-"[@` +M```%\0@`!",P``08D``$&*``"SAVA_``"```(.P"(`!'X6``K,(0``Q!T``7Q"0`%\0H`!F,`` +M`\WE``"`````SD$A:'@```@``"1X```DR```)1@``"5H```EN```)@@``"97\;@`\4I``( +MEX``'29D`/^```)V?QN`#A2D``B7@``8)F0`_X```G9_&X`,%*0`")N``!,F +M9`#_@``"=G\;@`T4I``(FX``#B9D`/^```)V?QN`#Q2D``B;@``))F0`_X`` +M`G9_&X`.%*0`")N```0F9`#_@``"=A2D``@F9`#_,F@`/!3L``B:@/V7?$-` +M`7Q#@`%\0\`!S```"Y;```;/02%ISX$A:L_!(6O,`2%L@````,_U``"````` +M,F@`/)J`_[/40`!_@````'Q`P`%\00`!P!X``144`!+`(@`"P"8`!)5```3` +M)__[?24`"<`F``!]TH`)?A+`"7TE``I\04`!?$&``@%*`*I',S^",R```N5```)AH```(```K2```+#@``"RH```M&` +M``+7@``"XX```P',P:*D?$"``8@```#`$@@`?$%``7Q"``%]#,`*P!(`!!58 +M``,57``-?='`"1(@`!-^'D`*?DZ`"LZ!HJ3-@:'^?$"``8@````$$"$8Q!0` +M"Y5`___440``S,&BI'Q`@`&(````!!`A!L04``R50/__U%$``,S!HJ1\0(`! +MB````-A```_,P:*DQ!``#YD`__]\0(`!B````!C0`#01%``4Q"0`#99`___, +M02$`?45`"LU!(0',02$"S$$A`\S!HJ1\0(`!B````"38``%\00`!?$%``1&< +M`!!\0@`!?5U`"A5D`!TF9``"E8``"<0\`!^7P/__FD``#<0H`#`6J``"?BH` +M`@``#L8```[2```.V@``#N(```[J```.\ +M4=P`('V>`!J```/]R"``+8```_W((``6@``#_<@@`!>```/]R"``&(```_W( +M(``R@``#_9E```,AW``P@``#PB'<`%#-@2%MS<$A;L@B``"```/]4=P`('V= +M@!K8```CT8``)-A``"?$*``LFH#__\@@`"Z```/]Q!P`,,`R``1]G8`!%9@` +M`LV!)<+,,27#E4```\R```O,,27#Q"$``95``"3$)0`!4F0`('XF`!J```/] +M,:P(`,0T`!"6P``#S````8```_`YK`I\/;`*=YK```27```#S````8```_`Y +MK`K&@```@``$,H``!#R```1*@``$%8``!"?$-``0S(``"\X9``"50``$ +M!9@``58@`"#.&0``E\#[\L`Z``3,.2%`Q#D``<]``!!\0(`!B````#&L"`#$ +M-``0EL```\P```&```0(.:P*?#VP"G?$-``0FL``!)<```/,```!@``$"#FL +M"MP]L`K9FL```Y<```+,```!@``$"%'<`"!]G8`:V```'LP!(7[`*@`$!!0`!$%<``@H+`!`SL``(]A``"<)W``!F<#__\R` +M``O,*2%_Q"4``19L`!]!7``"FL#_^<0T`"R;0/__V```'WQ`@`&(````Q`P` +M$S#0``&5```#S(``"]L!HJ3,0``.Q`P`$L00`!,PU```/1@``7U9P`K80``Q +MF<#_^MA``!/0```R?$"``8@```#`#@$`S````@`_`4`` +M0`.=`$$$60!"!'0`0P1\`$0"F`!%`AD`1@*C`$<"HP!(`J,`2@,3`%(#&@!3 +M`RT`5P,T`%\#60!@`SH`80-,`&@#9@!I`VH`<@-R`',#CP!V`VX`=P-N`'H$ +MM0!]!,X`?@32`(4$UP"&!-P`B@4-`(L%#0`/!1X`#P4>``\%'@`/!1X`#P4> +M``\%'@`/!1X`#P4>``\%'@`/!1X`#P4>``\%'@`/!1X`#P4>``\%'@`/!1X` +M#P4>``\%'@`/!1X`#P4>``\%'@`/!1X`#P4>``\%'@`/!1X`#P4>``\%'@`/ +:!1X`#P4>``\%'@`/!1X`#P4>``\%'@`/!1X` +` +end diff --git a/sys/contrib/dev/drm2/radeonkmsfw/TAHITI_pfp.bin.uu b/sys/contrib/dev/drm2/radeonkmsfw/TAHITI_pfp.bin.uu new file mode 100644 index 00000000000..4d84cdf5bcb --- /dev/null +++ b/sys/contrib/dev/drm2/radeonkmsfw/TAHITI_pfp.bin.uu @@ -0,0 +1,194 @@ +begin 644 TAHITI_pfp.bin +M?$"``8@```#,@```U$```'Q`@`&(````S(```,S```#40```?$"``8@````D +M3``/,.@``B20``*:@``,?$%``5!8`"!]E<`:F,```]'``!F`````F0```]'` +M`!J`````T<``&X````#$(``5QB0`"I9``6-\00`!?$%``S,.A^LT#H?G-0Z*=S4`` +M`,Q```!\0(`!B````'Q#0`$ +MS,.A^LT#H?G-0Z*=S4```,Q```!\0(`!B````'Q`P`$DT``!S,.BG]A``!>5 +M```"V(``%\R```#,P```?$"``8@```!\0,`!S(```,S#HJ+,P```?$"``8@` +M``!\0,`!%-0`'\R```!\00`!E4```LS``!\5&``?S,```,T```"5@``"S0`` +M(,P``'^```.*(`@`?B"(!^C$(``5'*@`$,Z@``1\00`!4%0`('Q!@`$&)``! +MSD``%16<`!A]%0`:S>``"T`:?65`$558`"#,```AS78``,VV``#$(``3F@#__WQ`@`&(````Q"`` +M)'Q`P`%\00`!S.`#_F\``?Y````#80``>Q#P`'I?` +M`'N0````C``##<0@`!5\0,`!P#;_`,00`!;`,#__?/5`"7U1@`E]@8`=?/.` +M"9F```;?@P``SZ``#XP``Q'40`!_@````(P``Q%\0(`!B````'Q`P`$4W``( +ME<``&23<`!!\00`!4%0`()G```/%'0``@``#,WT5`!K%'@``?$(``7Q"0`%\ +M08`!?>7`"7WB@`]!K``"FH#\QP;L``,*[``!FL#__R3<`!"9P``#Q1T``(`` +M`S;%'@``@``#-LR```#,P```U$```'Q`@`&(````Q!P`!,W```#,````S(`` +M`-1```!\0(`!B````'Q`P`$DT``&,1``!L04``^9```(S```$L0D`'Z60``X +MF4``!,0<``3-P```S````,R```#,P```U$```'Q`@`&(````?$#``7Q!``$5 +M&``?S0``(5$4`""9@``#U$T``(````!]34`:&1P`,=16``"5P/R5Q"``$YH` +M__]\0(`!B````'Q`P`%\00`!%-0`'C%8``(DW`#_E4``!)F`_(K-'`,`@``` +M`,R```#,P```S0```'Q`@`&(````Q"``)'Q`P`'$TP,`S(```,S```#,```A +MS2$A07Q`@`&(````U$``?WQ`@`&(````Q"0`?I9```-\0(`!B````(```XX` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M`````P-(``4!7P`&`.P`!P#S``@!`0`)`0@`$`.'`!$`"P`2`"<`$P`K`!8` +M+@`D`#<`)0!Q`"8!&0`7`8$`(@+H`",#%0`G`3,`'P&S`"`!XP`H`5(`*@%" +M`"P`4``O`4P`,0.'`#(!>0`S`X<`-`-/`#4!(``X`)(`-P&/`#P#*``_`6$` +M0`)5`$$"F`!"`KP`0P+$`$0"U`!*`MX`50-+`%\`WP!@`+L`80#6`&D`[P!S +M`/8`=@$$`'?4U`"1%4`!A^EH`*P`J)0'W)``K%%0``%50`$'U!0!Y]34`)$50`&'Z6 +M@`I^@H`>Q!<``GZ6@`F0````Q!<`8`E4``=^T0`0?14`!'R10`^0````P!@` +M`!,P``B0",``)#?!2`"A%4`!!^EH`*C``#`W[.P`'`'HE`C``"0WP4 +M@`H15``8?I:`"HP``P-^SL`!SH,`6I````"6P`"I*NP``<[``#"60`"BQ"`` +M')8``*#,```PD````"@,``&,``(,*#@``8P``EDH.```C``"+9>``?_`.#$! +MQ[D``!>X`!`GK`?_EL```\`D``",``#^S8```L00``.5`/__S```$,`X``&0 +M````P!PPT,V=``#`'##.Q=T``'W5P`)]T<`/E<``-WT"P`J0````Q"L``HP` +M`N7`#H``?X^`"L`.@`!_3T`**#P`H!/\`!`K_`#_C``"7,`<,07%[0``$NP` +M&";L`/_`)```C```_LP``!*0````D````"?0`/\Q$`#_F0#__<`4,1?/U0`` +MD````"@X``",``)9*#@``8P``BTH#```C``"#,P``"V```0RP`J,`,`,)%!\ +MR,`*Q-$``,`*C0#`#"10?,C`"L35``!]%0`*)1`#@'T!0`V0````EL#_TG[! +M``K`'##0S9T``,`<,,[%U0``%2P`!);`_\.,``#^E@#_P9````#,```MS8`` +M#LP#``#`,#!)QPD``,`R`!!\L<`)E<`"T,`P,0''"0``))P``97``NT4K``( +M)NP`_\`D``'`(```C``!4)8``8G-@P``@``$,HP``'O,```.Q`@`-\0(`#.4 +M@``#S8``#HP``DC$"``JE(```\V```Z,``,8Q`@`*Y2```+-@``.Q`@`*)2` +M__+-@``.C``!N(P``ZV```%PQ`@`')2``J[-@P``@``$2":H`/\4Y``$$F0` +M""3<``]^7,`*S,``(\Z```R```0RQ`@`$)2`__/$"``1E(#_\<0H``"6@`$2 +MS8``#B@(``'$#``@S(```#*(`;N4@`!NS`,``(```5O$'P!@"=P`!R@@`!": +M``'W*!0`0(```ZS$'``SE<#_7B$",``+7 +M%J@`"":,`/]\U,`+P!0``<`*B$",``*3$9@`"'Y:0`K`'HD`C``"UQ:H``@F +MC`#_?-3`"\`4``'`"HD`C``"DQ&8`!!^6D`*P!Z)0(P``M<6J``()HP`_WS4 +MP`O`%``!P`J)0(P``I,1F``8?EI`"I````#`&"35?8F`"L69```H'```*!`` +M`,`@``B50``,),@``92```5\D(`$?8F`"@E4``$%W``!"B```13,``$%$``! +MF@#_]B68`/^0````Q"<``)9``$K,`P``@``!6\`8)-5]B8`*Q9D``'T!`!Y] +MD,`)*!P``"@D``#`(``(E4``#"3(``&4@``%?*2`!'V)@`L)5``!!=P``0H@ +M``$4S``!!F0``9H`__8EF`#_D````,`()-?`#H@`?(S`"L35```E5`#_P`Z( +M0'R,P`K$T0``)1``_Q$0``A]44`*P`Z)`'R,P`K$T0``)1``_Q$0`!!]44`* +MP`Z)0'R,P`K$T0``)1``_Q$0`!A]44`*D````"G((F_$B0``*C``#M\07`"690``'C``!1)5` +M``7`#"35?@$`"LT#`!Z,``.WS`,`#I````"7@``$P!`Q&<4=``"9P/_^ET`` +M!,`0,1K%'0``F<#__I````"```-]Q`@`#Y2`___$*P`"C``"Y<`.@`!_CX`* +MP`Z``']/0`HH/`"0$_P`$"O\`/^,``)Q`\`87R-``R9```& +MQ!$Q"WS0P`K,PP`>P!@``(` +M"LU)``"0````!!@``/1=KM,\,<:T\B```````5P,`!0```!0```%BG>0>8>H(\]]+MR/TG;T>2D<,J`#U +MNZ7DB+4]VXH.CGAI44T5```!CWQ(XCF?ZF;JP7SFC;DQAT2V+8SD6[\PG>JP +M;=8$C>&U![WV%@```3,TA:R18(0257X/K?B!TQ0[(V.M`:4-3]975?2,9UW3 +MBE'N7!<```':QR\H[&(X_$#*"(X8`K`_L(-/G*GF\L($8M`SHOZX[6;/T$T8 +M```!R.O__)`]W@6`-OY2&K<]?P$K@CIA;.*8(-92CGE(:O&B:3%=```````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M`````````````````````````````````,5)$-5)(.5),/5)`#0``-S_`&'0 +M_P!A$``!82\`!`"Q@`1@``````````````````````````````````````#% +M"1#5"2#E"3#U"0`U```````````````````````````````````````````` +M````````````````````````R4D`T0D0V4D@Z4DP^4E`@$E0D$E@H$EPL$D` +M-````````````````````````````````````````````````,D)$-D)(.D) +M<-$),/D)0(<)4)<)8*<)<+<)`#4````````````````````````````````` +M``````````````#-20#1"1#=22#M23#]24!`25!026!@27!P28"`29"02:"@ +M2;"P20`T````````````````````````````````S0D0W0D@[0FPT0DP_0E` +M2PE06PE@:PEP>PF`BPF0FPF@JPFPNPD`-0`````````````````````````` +M````#```Y!,``!,``1,``A,``Q,`!!,`!1,0(```21,`2!,0(```:!,`@!,` +M@1,`D!,`D1,`H!,`H1,P(```L1,`LA,`LQ,`PA,`PQ,`T1,`TA,`TQ,`Z!,` +MZA,`\!,`\1,`\A,,\B#F$Q`@`$P"#`-V@@[2

G!^=$@``,P`0,IX3GQ(`(#,`,#(F$0,F$1(.8#,+$#(F$2,F$3 +M;`,P(A`K(B#F$Q+1`1+!@!`@`!`1(!4%`!+!@#(A$2(A$#`#$R`"$SCQ*.$P +M`1,@`!,B(1(R(1,@YA,PL1,0(`#XT>C!V+'(H;B1J(&8<8AA>%%H05@Q2"$X +M$2@!$M$!$L&``#`````V`0$@80!`X@-0Y`,A+@!01!`@1!!61``@;P`=\##T +M0!SR,#+``!-`02\`0$.PG&,,$@`BH2#C$R(D`&(D`19"_-`"`(;O_P`@\`,@ +M\!,H!&@4%@+[T`(`ANK_`#8A`##J`RHS,/`3'?`````V(0#VP@E1+P!0(K`Y +M`DD2'?`````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M``````````````````````````````````````````````````8P```````` +M````````````````````````````````AE[_```````````````````````` +M```````````````0-````````````````````````````````````````!+! +M@"D!(.@#9D("1E#_*`$2T0$2P8`&2?\`````````AD;_`#8A`$!O`##D`R`S +M(##D$T#F$Q`@`!WP`#8A`$!O`##D`R`S("`S,##D$T#F$Q`@`!WP```````` +M`````(8V_P`@TA,A,``Y$DDB63)I0GE2B6*9%)H0E@R2"(X$B#2`Q`R`````#8A`"#J`QWP-B$` +M(/`3`"``'?``-B$`(/(3`"``'?``-B$`(/`#'?`````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M````5P-@````````````````7K(`8%ZR`L@!@7K(`8%ZR`&!>L@!@7K(` +M8,JR`&!>L@!@RK(`8%ZR`&!>L@!@7K(`8%ZR`&!>L@!@7K(`8*S%`&#UQ@!@ +MK,4`8*S%`&"LQ0!@]<8`8%?'`&"LQ0!@K,4`8%?'`&"LQ0!@K,4`8*S%`&#" +MQP!@K,<`8*S%`&"LQ0!@K,4`8*S%`&"LQ0!@S\8`8````````````````.SP +M`&"[\`!@8_(`8"_R`&`/\@!@E_$`8$3Q`&``````"@X4&`X4&!L4&!L>&!L> +M(@8-%!P-%!P@%!P@)1P@)2H``00(!0(#!@D,#0H'"PX/``$($`D"`PH1&"`9 +M$@L$!0P3&B$H,"DB&Q0-!@<.%1PC*C$X.3(K)!T6#Q<>)2PS.CLT+28?)RXU +M/#TV+S<^/Y(B`6"2(@%@JB,!8(XC`6".(P%@Z1\!8.D?`6#I'P%@Z1\!8.D? +M`6#I'P%@CB,!8'(C`6!6(P%@Z1\!8.D?`6#I'P%@Z1\!8.D?`6#I'P%@Z1\! +M8%8C`6`Z(P%@'B,!8.D?`6#I'P%@Z1\!8.D?`6#I'P%@Z1\!8.D?`6`"(P%@ +M`B,!8.8B`6#I'P%@Z1\!8.D?`6#I'P%@Z1\!8.D?`6#I'P%@RB(!8*XB`6`` +M``````$"`P0%!@<("0H+#`T.#Q`1$A,4%187&!D:&QP='1X?("`A(B(C(R0D +M)24E)B8F)R`P<%"PH-#`X/)R\K!RT+ +M+@T0#@,&!0D*'PPC$R45*AHL'"$C(B4D*B@L)P$K`BT$+@@1$1(2%!08&!,& +M%0D:%AP9%R`;(1TB'B06*!DF)BDI`@$#`P0'!0\('P("`P8$#@4>!SX````` +M````````````0(`!08$"0H(#0X,$1(0%184&1H8'1X<(2(@)28D*2HH+2XL, +M3(P-38T.3HX/3X\04)`149$24I(34Y,45)0559465I875Y<86)@969D:6IH; +M6YL<7)P=79T>7IX?7Y\@8*`A8:$B8J(C8Z,D9*0E9:4F9J8G9Z@%6J0,`S9IG,*](!87&!H<'B`C%Q@:'!X@(R89&AP>(",F*1L< +M'B`C)BDM$!$2$Q05%A<1$A,4%187&!(3%!46%Q@9$Q05%A<8&AL4%187&1H; +M'!46%Q@:&QP>%A<8&AL<'A\7&!D;'!X?(04````````````````````````` +M`````(````!@````"`````8````!````L````)`````+````"0````(```!@ +M`0``(`$``!8````2`````P```,`"``!``@``6````!(````$````@`4``(`$ +M``!@`0``$@````4`````````````````````````!0`````````````````` +M```````````````````````!`````0````$````!`````0````$````!```` +M`0````$````!`````0````(````"````C',"8*%S`F#W=@)@[G8"8.5V`F#< +M=@)@TW8"8``````#`````P````,````#````!`````0````$````!`````4` +M`````````````````````````0````(````#``````````$````"`````P`` +M````````````````````````````!`````@````,````$````!0````8```` +M'````"`````D````*````"P````P````-````#@````\````______[___\! +M`````@`````````````````````````!`````0````$````!`````@````(` +M```"`````@````,````#`````P````,````$````!`````0````$````!0`` +M```````````````````````!`````@````,``````````0````(````#```` +M``````$````"`````P`````````!`````@````,``````````0````(````# +M`````````````````````````#P````X````-````#`````L````*````"0` +M```@````'````!@````4````$`````P````(````!`````````!SC0)@P8T" +M8+2-`F"GC0)@FHT"8(V-`F"`C0)@``````,````#`````P````,````$```` +M!`````0````$````!0`````````````````````````!`````@````,````` +M`````0````(````#`````````````````````````/_____^____`0````(` +M````````!`````@````,````$````!0````8````'````"`````D````*``` +M`"P````P````-````#@````\``````````````````````````$````!```` +M`0````$````"`````@````(````"`````P````,````#`````P````0````$ +M````!`````0````%``````````````````````````$````"`````P`````` +M```!`````@````,``````````0````(````#``````````$````"`````P`` +M```````!`````@````,`````````````````````````/````#@````T```` +M,````"P````H````)````"`````<````&````!0````0````#`````@````$ +M``````````';`F`WVP)@+ML"8"7;`F``0`` +M'@$``!\!```?`0``'P$``!\!```?`0``'P$``!\!```?`0``2@```$L```!, +M````30```$X```!/````4````%$```!2````4P```%0```!5````5@```%<` +M``!8````60```%H```!;````7````%T```!>````7P```&````!A````8@`` +M`&,```!D````90```&8```!G````:````&D```!J````:P```&P```!M```` +M;@```&\```!P````<0```'(```!S````=````'4```!V````=P```'@```!Y +M````>@```'L```!\````?0```'X```!_````@````($```""````@P```(0` +M``"%````A@```(<```"(````B0```(H```"+````C````(T```".````CP`` +M`)````"1````D@```),```"4````E0```)8```"7````F````)D```":```` +MFP```)P```"=````G@```)\```"@````H0```*(```"C````I````*4```"F +M````IP```*@```"I````J@```*L```"L````K0```*X```"O````L````+$` +M``"R````LP```+0```"U````M@```+<```"X````N0```+H```"[````O``` +M`+T```"^````OP```,````#!````P@```,,```#$````Q````,4```#%```` +MQ@```,8```#'````R````,D```#*````RP```,P```#-````S@```,\```#0 +M````T0```-(```#3````U````-4```#6````UP```-@```#9````V@```-L` +M``#<````W0```-X```#?````X````.$```#B````XP```.0```#E````Y@`` +M`.<```#H````Z0```.D```#J````Z@```.L```#K````[````.P```#M```` +M[0```.X```#N````[P```.\```#P````\0```/(```#S````]````/0```#U +M````]0```/8```#W````^````/@```#_``````$```$!```"`0```P$```,! +M```$`0``!0$```8!```'`0``"0$```H!```+`0``"P$```P!```,`0``#0$` +M``T!```.`0``#@$```\!```/`0``$`$``!`!```1`0``$0$``!0!```5`0`` +M%@$``!`0``7P$``&`!``!A`0``8@$``&,!``!D`0``90$``&8! +M``!G`0``:`$``&D!``!J`0``:@$``&L!``!K`0``;`$``&P!``!M`0``;0$` +M`&X!``!O`0``<`$``'$!``!R`0``0$``'H!``!Z`0``>P$``'L!``!^`0``?P$``((!``""`0``@P$``(0!``"% +M`0``A0$``(4!``"%`0``A0$``(4!``"%`0``A0$``(8!``"&`0``A@$``(8! +M``"&`0``A@$``(8!``"&`0``AP$``(`(``'D"``!Z`@`` +M>P(``'P"``!]`@``?@(``'\"``"``@``@0(``(("``"#`@``A`(``(4"``"& +M`@``AP(``(@"``")`@``B@(``(L"``",`@``C0(``(X"``"/`@``D`(``)$" +M``"2`@``DP(``)0"``"5`@``E@(``)<"``"8`@``F0(``)H"``";`@``G`(` +M`)T"``">`@``GP(``*`"``"A`@``H@(``*,"``"D`@``I0(``*8"``"G`@`` +MJ`(``*D"``"J`@``JP(``*P"``"M`@``K@(``*\"``"P`@``L0(``+("``"S +M`@``M`(``+4"``"V`@``MP(``+@"``"Y`@``N@(``+L"``"[`@``O`(``+P" +M``"]`@``O0(``+X"``"^`@``OP(``+\"``#``@``P`(``,$"``#!`@``P@(` +M`,("``##`@``Q`(``,4"``#&`@``QP(``,@"``#)`@``R@(``,L"``#,`@`` +MS0(``,X"``#/`@``T`(``-$"``#2`@``TP(``-0"``#5`@``U@(``-<"``#8 +M`@``V0(``-H"``#;`@``W`(``-T"``#>`@``WP(``.`"``#A`@``X@(``.," +M``#D`@``Y0(``.8"``#G`@``Z`(``.D"``#J`@``ZP(``.P"``#M`@``[@(` +M`.\"``#O`@``\`(``/`"``#Q`@``\@(``/,"``#T`@``]0(``/8"``#W`@`` +M^`(``/D"``#Z`@``^P(``/P"``#]`@``_@(``/\"``#_`@````,```$#```" +M`P```P,```0#```%`P``!@,```<#```*`P``"@,```H#```*`P``"P,```L# +M```+`P``"P,```P#```,`P``#`,```P#```,`P``#`,```P#```,`P``0``! +M``\#```0`P``$`,``!$#``!"``(`%@,``!8#```7`P``%P,``!<#```7`P`` +M1@`"`$H``@!.``$`(0,``"(#``!0``$`4@`!`"<#```H`P``*`,``"D#```J +M`P``5``#`%P``P!D``,`;``"`'```@!)`P``2@,``'0``0!-`P``30,``$T# +M``!-`P``30,``$T#``!-`P``30,``$T#``!-`P``30,``$T#``!-`P``30,` +M`$T#``!-`P``#0,```X#```2`P``$P,``!0#```5`P``&`,``!D#```:`P`` +M&P,``!P#```=`P``'@,``!X#```?`P``(`,``",#```D`P``)0,``"8#```K +M`P``+`,``"T#```N`P``+P,``#`#```Q`P``,@,``#,#```T`P``-0,``#8# +M```W`P``.`,``#D#```Z`P``.P,``#P#```]`P``/@,``#\#``!``P``00,` +M`$$#``!"`P``0P,``$0#``!%`P``1@,``$<#``!(`P``2`,``$L#``!,`P`` +M3@,``$```0!1`P``40,``%(#``!"``0`60,``%(``0!<`P``70,``%X#``!> +M`P``7P,``%\#``!?`P``7P,``&`#``!@`P``8`,``&`#``!@`P``8`,``&`# +M``!@`P``8`,``&`#``!@`P``8`,``&`#``!@`P``8`,``&`#``!A`P``80,` +M`%0`!`!D``0`>P,``'L#``![`P``>P,``'0`!`"$``0`E``$`*0`!`"T``0` +MQ``#`,P``P#4``,`W``"`.```@#D``(`Z``"`.P``0#R`P``\P,``/0#``#U +M`P``]0,``/4#``#U`P``]0,``/4#``#U`P``]0,``$\#``!0`P``4P,``%,# +M``!4`P``50,``%8#``!6`P``5@,``%8#``!7`P``5P,``%<#``!7`P``6`,` +M`%@#``!8`P``6`,``%H#``!;`P``8@,``&(#``!B`P``8@,``&(#``!B`P`` +M8@,``&(#``!C`P``9`,``&4#``!F`P``9P,``&@#``!I`P``:@,``&L#``!L +M`P``;0,``&X#``!O`P``<`,``'$#``!R`P``0,``'H#``!\`P``?0,``'X#``!_`P``@`,``($#``""`P``@P,` +M`(0#``"%`P``A@,``(<#``"(`P``B0,``(H#``"+`P``C`,``(T#``".`P`` +MCP,``)`#``"1`P``D@,``),#``"4`P``E0,``)8#``"7`P``F`,``)D#``": +M`P``FP,``)P#``"=`P``G@,``)\#``"@`P``H0,``*(#``"C`P``I`,``*4# +M``"F`P``IP,``*@#``"I`P``J@,``*L#``"L`P``K0,``*X#``"O`P``L`,` +M`+$#``"R`P``LP,``+0#``"U`P``M@,``+<#``"X`P``N0,``+H#``"[`P`` +MO`,``+T#``"^`P``OP,``,`#``#!`P``P@,``,,#``#$`P``Q0,``,8#``#' +M`P``R`,``,@#``#)`P``R0,``,H#``#+`P``S`,``,T#``#.`P``SP,``-`# +M``#1`P``T@,``-,#``#4`P``U0,``-8#``#7`P``V`,``-D#``#:`P``VP,` +M`-P#``#=`P``W@,``-\#``#@`P``X`,``.$#``#B`P``XP,``.0#``#E`P`` +MY@,``.<#``#H`P``Z0,``.H#``#K`P``[`,``.T#``#N`P``[P,``.\#``#P +M`P``\0,``/@#``!```(`_`,``/T#``#^`P``_@,``/X#``#^`P``_P,````$ +M``!$``(`2``"`$P``0`*!```"P0```P$```-!```#00```T$```-!```#@0` +M``X$```.!```#@0```\$```/!```#P0```\$```0!```$`0``!`$```0!``` +M3@`$`%X``0`8!```&00``!H$```:!```&P0``!L$``!@``,`(00``"($```B +M!```:``#`'```P!X``,`@``"`#T$```^!```/P0``#\$``!`!```000``(0` +M`@"(``$`1P0``$<$``!'!```1P0``$@$``!(!```2`0``$@$``#Y`P``^0,` +M`/H#``#[`P```00```($```#!```!`0```4$```&!```!P0```<$```(!``` +M"00``!$$```1!```$00``!$$```2!```$P0``!0$```4!```%00``!4$```5 +M!```%00``!4$```5!```%00``!4$```6!```%P0``!P$```!0#M`@4`[0,%``P#!0`- +M\P4`[1$%`/D0!0#V!P4`!@@%`/KR!0`&"P4`!@X%``?K!0``Y@4`_A`%``?S +M!0#^$04`]@D%``#J!0#Z]P4`_AH%``_Q!0`/]04`#_<%`/,"!0`/_@4`_AP% +M`/_@!0`##`4`Z`(%`/?Z!0`/!04`#PX%`/L+!0`#$04`#QL%`/_D!0`0`@4` +M`QL%`/,%!0`$]@4`$?D%`!'\!0`!#@4`_^8%``<(!0#[&P4`!PH%`!$$!0`1 +M'04`$OP%`!+^!0`2_P4`!PL%`/_J!0`3]@4`$_<%``L%``C[!0`5_04`Z>4%``$?!0#_\@4`%0D% +M`!;_!0`6`@4`%@8%`!?A!0`7Y04``NT%`._\!0`8_P4`^?$%`!@&!0#U!P4` +M]0D%``@$!0`(!04`^?8%``@'!0`:`04`!`L%`.4+!0`)[P4`"?$%`.P1!0`< +M_@4`"?4%``GV!0`%]`4`]NH%`!WQ!0#R^04`_18%`/T9!0#^XP4`'0(%`/[H +M!0`=!`4`'OD%`![_!0#^[@4`_N\%`!_S!0#R_04`[?D%`/[R!0`-_@0`X1\$ +M``+P!`#_"@0`]OP$``/M!`#O`00`#@$$``X#!``#\P0`_PX$`/P+!`#]\00` +M_?,$`/<$!``)!P0`^_@$`.[_!``5``0`%0$$`/OZ!`#X_`0``!X$`!D!!``` +M'P0`\0$$``H'!``%\P0`_1$$``'Q!`#A_P0`^0L$``#M!```[P0`'_T$`/KY +M!``(`P0`]P(#`/OY`P#V`0,`^/T#`/;_`P`'!P,`"P,#`/L$`P`%^`,`Y@`# +M``K^`P#X`0(`"/\"`/P'`@``X0(```D!``D`!@`$_P4`_``$``/_`P#]_P,` +M!@`%`/?_`0#[_@$``!L"``#V`@``]0$`_@H%`.[R!0#X$P4`_A\%``H$!0#X%P4``?(%`/GE!0`*%P4`"^D% +M`.[]!0`+\04`"_4%`.[^!0`+^P4`Y^$%``0;!0`%YP4`!>L%``7L!0`%[P4` +MY^4%`.X*!0#Y^`4`"PD%``L+!0`,YP4`[_L%``7V!0#G^04`Y_\%`./^!0#A +M!04`Z/8%`.\'!0#O%04`Z/T%``P$!0`,!04`#!`%``W@!0`-Y04`#>T%``WQ +M!0#H_P4`Z``%``WZ!0#U#@4`#?P%`/P,!0#\#04`_`X%`/45!0#\$04`_!,% +M`/;C!0`%"04`#N,%``[V!0`%"@4`Z`$%`/WE!0`%#04`_><%`/;V!0`."`4` +M!14%`/;X!0#][@4`#_D%``_[!0`&Z@4`!NP%`/`"!0`&\`4`!O$%``$5!0`& +M]`4`]OH%``\'!0`/#04`_?(%`/D-!0#P`P4`X`$%`!#_!0#A$04``N0%`!`' +M!0`0"@4``NL%`/D3!0#ZY04`^NT%`!'Z!0`1^P4`^O$%`.$4!0`1_@4`Y.`% +M`/KV!0#D[`4`$0(%`.$>!0#I!`4`$08%`!$)!0`1#P4`ZOX%`!$?!0#Q!@4` +M!@D%`/$+!0#Q&P4`$@$%``8-!0`2!04`$@8%`/8(!0`&#P4`$_@%``80!0`& +M$04`$_X%``?A!0`'Z@4`\O8%`/81!0`'[P4`$P<%`/8;!0`4_P4`%``%`!0! +M!0`'\@4`%>T%`!7O!0#J_P4`]_4%`/+[!0#]#`4`Z@H%`!4'!0#]#@4`%O$% +M`.OZ!0`6``4`Z_X%`.O_!0`7X`4`X?X%`/("!0`7Z04`%^P%`!?M!0`7^04` +M%_\%`!<`!0#]%P4``@L%`/(#!0#]'04`&`$%`!@#!0`"#@4`&?D%``(/!0`" +M$`4`_1X%`/T?!0`9!@4`&1T%``/A!0`#X@4``^4%``/G!0`#Z04`!PX%``/J +M!0`;`@4`&P,%`/($!0`;%P4`!Q,%`/[G!0#R"`4`"/<%`!P"!0`4% +M`/[M!0#R"@4`'?(%`!WY!0#S^P4`\_P%``GK!`#Q!00`^@H$`/0'!``-!00` +M#O\$```1!``%]00`]OL$``_]!`#]#P0`_Q4$`/L(!`#]$P0`_O`$`/7Z!``! +MX00``P@$`/+^!`#Y"00``0P$`!("!``3^00`"OL$`.+_!`#O`P0`$P,$`!7_ +M!``![P0`^1$$`/WA!``7`P0`!0@$``;E!``!&@0`!O4$`!O]!``&]@0``1L$ +M``<-!`#M_P0``.D$``CZ!``=Z00`'?T$``#K!`#I``0`#/X$`./C!`#_[00` +M_?0$`/GR!`#X`P,`$``#`/```P#\^`,`$0$#`/;^`P`""`,`_P\#``8'`P`' +M!0,`#``#`.L!`P`#^`,`_O<#``$-`P`!#P,`!/D"`/\;`@`(`@(`^?L"`/O[ +M`@`&^@(`_P@"``0%`@#\^P(``/\$`/__!0`"`P0```4$``("`P#X_P$`!P(! +M``(%!@#[`04`_`,&`/\=`@`$^@(`^0<"``T!`@#Y_P4``P($`./_`@`"]@(` +M!@4"``?Y`@`(_@(`!OD"`.$``0#Y_@$`!P,!`/X&!@`%_08```@!`/S\`0#] +M!@$``.4!``4"!@#_^P4``_L&`/O]!@#W^P,`_/<#``$*`P`.``,`[P`#`.T! +M`P#X!`,`"04#`/@%`P`"\0,``O(#`/[V`P#Z"`,`^`8#`.X``P`<_P,`]0,# +M`/WX`P`'\0,`]_D#``/Q`P`%]P,`_O@"```=`@#S_P(`^`("`/GY`@#Z!@(` +M#0`"``8#`0`$_08`XP`&`/K]`0`$!`$`_O\"`!L`!@#\!`$`'P`!`/H`!0`& +M`@8`'P$#`/_T!`#[ZP0`]``"`/C^`@`!XP(`!0,&`/P&`0`'_@$``.,&`/_Z +M!@`'_P4``_T$`/_^`@``!@4`]P$!`/?C!`#I_00`^0@$`.G_!`#]%00`]/X$ +M`/[Q!`#^]`0`"@4$`/+_!`#\"@0`^0\$`/P/!`#\%@0`]08$`/WI!``+`@0` +M_>T$``L%!`#Z]00`#/D$``S[!`#U"P0`!@H$``/^!`#U#00`^`@$``WY!``- +M^P0``PX$``?V!`#V^00``Q4$``3T!``"X@0``NX$``T'!``"[P0`_?8$`/\, +M!``.`@0`^?4$``X&!`#P`00`\?L$`/\0!`#T`@0``!0$``\#!``/#P0`#Q$$ +M```6!``1X@0`$>T$`!'W!`#_%`0`X?T$`/L-!`#\]@0`!Q$$``<6!`#U]00` +M!`@$`/<*!``3_P0`XOX$``C]!``3`@0``.`$`!7Y!`#X]P0`_A,$`/CX!`#^ +M&P0`]?P$``@&!``9_00`&?\$`/_B!``)[00`&O\$``#H!`#L_P0`Y`$$``#L +M!``"#00`&PD$`/_G!```[@0`[?X$`.GY!``#\@0`^@L$``/T!``%"P0`'0,$ +M``4,!``?X00`^A$$`/T*!``)"@0`#0(#``$3`P`""@,`#_\#``D$`P#V!`,` +M"OT#``/U`P`#]@,`]P4#```,`P`#"P,`"_D#```/`P#G`0,`_PT#``G[`P#T +M_P,`_QH#`/#_`P#M``,`'_\#`/D&`P#^"P,``1T"`/\+`@`)_0(``O<"``?\ +M`@`/``(`"/P"`!H``@`;_P(`"0("``'E`@#C`0(`\0`"`!T!`@`*`0(`]?\! +M`/H#`0`#^@$`_00&`/K_!@`!!@8`_OX#`/\`!``!_P4`!?X&`/_W`0#^"@(` +M^@4"``'Z!@#[`P8`_P0$`/L"!@`#!08`_?<"``0&`@`=_P(``0@"``7Z`@`> +M``(``P8!`!D``0#_"0$``OH&``+Y!@`(``8``P,$`/T"!```^00`_?X$`/SZ +M`@`&\@0`]PD$``#T`P#]_`$`_?D!`/P%`0`*``8``0<&``#Z!0#_^04`_@@! +M``,/`P#^#0,`_A8#`/_A`P``]P8``?D%``KZ`P`/`@,`!P8#``;N`P`(]@,` +M"/@#`!,!`P`7`@,`&``#`/(``P#U!0,`!_4#``?W`P`!$0,`\0,#``GW`P#W +M_`,`^`<#``L'`P#Y]P,`_Q<#`/0!`P#Y!0(`!@8"`/D$`@#[!0$`_04&`.0` +M!@`'`04`Y0`&`/S]!@`&^`(`\?T"``,'`0#B``$`!@0!`/[]!`#]`P0```(& +M```!!`````$````````````````````````````````````````````````` +M`````````````````````````````````````$``!@!-````R``&`"X!!@"> +M````G@```(X!!@#N`08`,@(&`)("!@#L`@8`1`,&`&X!``!N`0``;@$``&X! +M``"F`P8`&@0&`.L!``#K`0``[`$``.P!``#L`0``[`$``'P$!@#L!`8`7@4& +M`,8%!@#C`@``XP(``.,"``#C`@``)`8&`#0#``"D!@8`$`<&`*<#``"G`P`` +M?@<&`,X'!@`'!```!P0``#X(!@!^"`8`2P0``$L$``!+!```2P0``$```P!( +M``,`4``"`%0``@!8``$`6@`#`!X````>````8@`!`"$````B````(@```",` +M```C````(P```",````D````)````"0````D````)````"0````D````)``` +M`"0````D````)````"0````D````)````"0````D````9``"`&@``@!L``$` +M+P```#`````P````,````#`````Q````,@```#,````T````-0```#4````U +M````-0```&X``P!V``$`/````#T````^````/@```'@``P"```,`3````$P` +M``!,````3````$P```!,````3````$P````!`````@````,````$````!0`` +M``8````'````"`````D````*````"P````P````-````#@````\````/```` +M$````!$````2````$P```!0````5````%@```!<````8````&0```!H````: +M````&@```!H````;````&P```!P````=````'P```"`````E````)@```"<` +M```H````*0```"H````K````+````"T````N````-@```#8````W````.``` +M`#D````Y````.0```#D````Z````.P```#\````_````/P```#\```!````` +M00```$(```!#````1````$4```!&````1P```$@```!)````2@```$L```!. +M````3@```$X```!.````3@```$X```!.````3@```$\```!/````3P```$\` +M``!```,`2``#`%```P!8``(`7``"`&```0!P````<````'$```!Q````<@`` +M`&(``0!U````=0```'8```!D``$`>0```'D```!Y````>0```'H```!Z```` +M>@```'H```!Z````>@```'H```!Z````>@```'H```!Z````>@```'H```!Z +M````>@```'H```!Z````>@```'H```!Z````>@```'H```!Z````>@```'H` +M``!Z````>@```'H```!Z````>@```'H```!Z````4````%$```!2````4P`` +M`%0```!5````5@```%<```!8````60```%H```!;````7````%T```!>```` +M7P```&````!A````8@```&,```!D````9````&4```!E````9@```&<```!H +M````:0```&H```!K````;````&T```!N````;P```',```!T````=P```'@` +M``![````>P```'L```![````>P```'L```![````>P```'L```![````>P`` +M`'L```![````>P```'L```![````?````'P```!\````?````$```P""```` +M2``"`$P``@!0``(`5``!`%8``0"2````6``!`%H``0!<``$`7@`!`)L```"; +M````FP```)L```";````FP```)L```";````FP```)L```";````FP```)L` +M``";````FP```)L```"<````G````)P```"<````G````)P```"<````G``` +M`)T```"=````G0```)T```"=````G0```)T```"=````?0```'T```!^```` +M?P```(````"`````@0```($```"#````A````(4```"&````AP```(@```") +M````B@```(L```",````C0```(T```".````CP```)````"1````DP```)0` +M``"5````E@```)<```"8````F0```)H```!```(`H@```*,```!$``(`J``` +M`*@```"H````J````$@``@!,``(`4``"`+0```"U````M@```+<```"W```` +MN````+@```"X````N````+D```"Y````5``!`%8``0"^````OP```,````!8 +M``(`Q````,0```#%````7``"`,H```#*````R@```,H```#*````R@```,H` +M``#*````R@```,H```#*````R@```,H```#*````R@```,H```#*````R@`` +M`,H```#*````R@```,H```#*````R@```,H```#*````R@```,H```#*```` +MR@```,H```#*````GP```)\```"@````H0```*0```"E````I@```*<```"I +M````J@```*L```"L````K0```*X```"O````L````+$```"R````LP```+,` +M``"Z````NP```+P```"]````P0```,$```#"````PP```,8```#'````R``` +M`,D```#+````RP```,L```#+````RP```,L```#+````RP```,L```#+```` +MRP```,L```#+````RP```,L```#+````S````,P```#,````S````,P```#, +M````S````,P```#-````S0```,T```#-````0``"`-$```#2````T@```-,` +M``#3````TP```-,```#3````TP```-,```#3````TP```-,```#3````TP`` +M`-,```#3````TP```-,```#3````TP```-,```#3````TP```-,```#3```` +MTP```-,```#3````TP```-,```#3````TP```-,```#3````S@```,\```#0 +M````T````-0```#4````U````-0```#4````U````-0```#4````U````-0` +M``#4````U````-0```#4````U````-0```#5````U0```-4```#5````0``" +M`$0``0!&``$`2``!`$H``0#A````X@```.(```!,``(`4``"`%0``@!8``(` +M\@```/(```#R````\@```/(```#R````\@```/(```#R````\@```/(```#R +M````\@```/(```#R````\@```%P``0!>``$`]P```/@```#Y````^0```/D` +M``#Y````^@```/H```#Z````^@```/L```#[````^P```/L```#6````U@`` +M`-<```#8````V0```-H```#;````W````-T```#>````WP```.````#C```` +MXP```.0```#E````Y@```.<```#H````Z0```.H```#K````[````.T```#N +M````[P```/````#Q````\P```/0```#U````]@```/P```#\````_````/P` +M``#]````_0```$```@!$``$``P$``$8``@`'`0``!P$```@!```(`0``"`$` +M``@!```)`0``"0$```D!```)`0``"0$```D!```)`0``"0$```H!```*`0`` +M"@$```H!``!*``(`3@`"`%(``@!6``$`&0$``!D!```9`0``&0$``!D!```9 +M`0``&0$``!D!```9`0``&0$``!D!```9`0``&0$``!D!```9`0``&0$``!H! +M```:`0``&P$``%@``0`>`0``'@$``!X!```>`0``'P$``!\!```?`0``'P$` +M`"`!```@`0``(`$``"`!``#^````_P`````!`````0```0$```(!```$`0`` +M!`$```4!```&`0``"P$```P!```-`0``#@$```\!```0`0``$0$``!(!```3 +M`0``%`$``!4!```6`0``%P$``!@!```<`0``'0$``"$!```A`0``(0$``"$! +M```A`0``(0$``"$!```A`0``(0$``"$!```A`0``(0$``"$!```A`0``(0$` +M`"$!```B`0``(@$``"(!```B`0``(@$``"(!```B`0``(@$``"(!```B`0`` +M(@$``"(!```B`0``(@$``"(!```B`0``0``!`"4!```F`0``)P$``"@!```H +M`0``*`$``"@!``!"``(`1@`"`$H``@!.``(`4@`!`%0``0!6``$`/@$``#\! +M```_`0``/P$``#\!```_`0``/P$``#\!```_`0``/P$``#\!```_`0``/P$` +M`#\!```_`0``/P$``#\!```C`0``)`$``"D!```J`0``*P$``"P!```M`0`` +M+@$``"\!```P`0``,0$``#(!```S`0``-`$``#4!```V`0``-P$``#``$`8``!`&T!``!M`0``;0$``&T!``!M`0``;0$` +M`&T!``!M`0``0@$``$,!``!$`0``10$``$8!``!'`0``2`$``$D!``!*`0`` +M2P$``$T!``!.`0``3P$``%`!``!1`0``4@$``%,!``!4`0``50$``%8!``!7 +M`0``5P$``%@!``!9`0``6P$``%P!``!=`0``7@$``&(!``!C`0``:0$``&H! +M``!K`0``;`$``&\!``!P`0``<0$``'(!``!S`0```$``'D!``!Z`0``>P$``'P!``!]`0``?@$``'\!``"` +M`0``@0$``((!``"#`0``A`$``(4!``"&`0``AP$``(@!``")`0``C`$``(T! +M``".`0``CP$``)0!``"5`0``F0$``)D!``":`0``FP$``)\!``"@`0``H0$` +M`*(!``"D`0``I0$``*8!``"G`0``J`$``*D!``"J`0``JP$``*P!``"M`0`` +MK@$``*\!``"P`0``L0$``+(!``"S`0``M@$``+8!``"V`0``M@$``+``$`8``!`.4!``#F`0``YP$``.@!``#I`0``Z@$``.H! +M``#J`0``Z@$``+X!``"_`0``P0$``,(!``##`0``Q`$``,@!``#(`0``R0$` +M`,H!``#-`0``S@$``,\!``#0`0``T0$``-(!``#3`0``U`$``-4!``#6`0`` +MUP$``-@!``#9`0``V@$``-L!``#<`0``W0$``-X!``#?`0``X`$``.$!``#B +M`0``XP$``.0!``#M`0``[@$``$```0!"``$`\P$``/,!``#S`0``\P$``$0` +M`0!&``$`2``!`$H``0!,``$`_@$``/\!`````@```0(```$"```"`@```@(` +M``,"```#`@```P(```,"```$`@``!`(```0"```$`@``!`(```0"```$`@`` +M!`(```4"```%`@``!@(```8"```'`@``3@`"`%(``@!6``(`%`(``!0"```4 +M`@``%`(``!0"```4`@``%`(``!0"``!:``(`7@`"`&(``@!F``$`:``!`&H` +M`0!L``$`*0(``"H"```J`@``*P(``"P"```M`@``+0(``"X"``!N``$`[P$` +M`/`!``#Q`0``\@$``/0!``#U`0``]@$``/`@``'P(``"`"```A`@``(@(``","```D`@``)0(``"8"```G`@``*`(` +M`"\"```P`@``,0(``#$"```Q`@``,0(``$```0!"``$`-@(``#<"```X`@`` +M.`(``#D"```Z`@``1``"`$@``@!,``(`4``"`$L"``!+`@``2P(``$L"``!+ +M`@``2P(``$L"``!+`@``5``"`%@``@!<``(`8``!`&(``0!D``$`9@`!`&`" +M``!A`@``80(``&$"``!A`@``80(``&$"``!A`@``80(``&$"``!A`@``80(` +M`&$"``!A`@``80(``&$"``!A`@``8@(``&("``!B`@``8@(``&,"``!C`@`` +M8P(``&,"``!H``$`9@(``&<"``!H`@``:@`!`&P``0!N``$`<``!`#("```S +M`@``-`(``#4"```[`@``/`(``#T"```^`@``/P(``$`"``!!`@``0@(``$," +M``!$`@``10(``$8"``!'`@``2`(``$D"``!*`@``3`(``$T"``!.`@``3P(` +M`%`"``!1`@``4@(``%,"``!4`@``50(``%8"``!7`@``6`(``%D"``!:`@`` +M6P(``%P"``!=`@``7@(``%\"``!D`@``90(``&D"``!J`@``:P(``&P"``!M +M`@``;@(``&\"``!P`@``<0(``'("``!S`@```(``'@"``!Y`@``>0(``$```@!$``(`2``" +M`$P``@!0``(`5``!`%8``0!8``$`6@`!`)8"``"7`@``EP(``)@"``"8`@`` +MF`(``)@"``"9`@``F0(``)D"``"9`@``7``!`)P"``"=`@``7@`!`*`"``"@ +M`@``H`(``*`"``!@``$`8@`!`*4"``"E`@``I@(``*8"``"F`@``I@(``*8" +M``"F`@``I@(``*8"``"G`@``9``"`*L"``"L`@``K0(``*X"``"O`@``KP(` +M`'H"``![`@``?`(``'T"``!^`@``?P(``(`"``"!`@``@@(``(,"``"$`@`` +MA0(``(8"``"'`@``B`(``(D"``"*`@``BP(``(P"``"-`@``C@(``(\"``"0 +M`@``D0(``)("``"3`@``E`(``)4"``":`@``FP(``)X"``"?`@``H0(``*(" +M``"C`@``I`(``*@"``"H`@``J0(``*H"``"P`@``L`(``+`"``"P`@``L0(` +M`+$"``"Q`@``L0(``+("``!```(`1``"`$@``@!,``(`4``!`,4"``#&`@`` +MQP(``,<"``#'`@``QP(``,@"``#(`@``4@`"`,P"``#-`@``S0(``,X"``#. +M`@``SP(``%8``0#2`@``T@(``-,"``#3`@``TP(``-,"``#3`@``TP(``-," +M``#3`@``6``!`%H``0!<``$`V@(``-L"``#;`@``W`(``-P"``#=`@``W0(` +M`-T"``#=`@``W@(``-X"``#?`@``WP(``.`"``#@`@``X0(``.$"``#B`@`` +MX@(``.("``#B`@``LP(``+0"``"U`@``M@(``+<"``"X`@``N0(``+H"``"[ +M`@``O`(``+T"``"^`@``OP(``,`"``#!`@``P@(``,,"``#$`@``R0(``,D" +M``#*`@``RP(``-`"``#1`@``U`(``-4"``#6`@``UP(``-@"``#9`@``Y`(` +M`.0"``#E`@``Y0(``.8"``!```(`ZP(``.L"``!$``(`2``"`$P``@!0``(` +M5``"`%@``@!<``(`8``!``D#```)`P``"0,```D#```)`P``"0,```D#```) +M`P``"0,```D#```)`P``"0,```D#```)`P``"0,```D#``!B``$`9``!`&8` +M`0!H``$`:@`!`!0#```5`P``%@,``!<#```7`P``&`,``!D#```:`P``&@,` +M`!L#```;`P``;``"`'```0!R``$`=``!`'8``0!X``$`>@`!`'P``@`N`P`` +M+@,``"\#```P`P``,0,``#(#```S`P``,P,``.<"``#H`@``Z0(``.H"``#L +M`@``[0(``.X"``#O`@``\`(``/$"``#R`@``\P(``/0"``#U`@``]@(``/<" +M``#X`@``^0(``/H"``#[`@``_`(``/T"``#^`@``_P(````#```!`P```@,` +M``,#```$`P``!0,```8#```&`P``!P,```@#```*`P``"P,```P#```-`P`` +M#@,```\#```0`P``$0,``!(#```3`P``'`,``!T#```>`P``'@,``!\#```@ +M`P``(0,``"(#```C`P``)`,``"4#```F`P``)P,``"@#```I`P``*@,``"L# +M```K`P``+`,``"T#```U`P``-0,``#4#```U`P``-@,``#8#``!```(`1``" +M`$@``@!,``(`4``"`%0``@!8``(`7``"`&```0!B``$`6@,``%H#``!;`P`` +M6P,``%P#``!<`P``70,``%T#``!>`P``7@,``%X#``!>`P``7P,``%\#``!? +M`P``7P,``&`#``!D``$`9@`!`&@``0!G`P``9P,``&<#``!G`P``:`,``&@# +M``!H`P``:`,``&H``0!K`P``;`,``&P#``!M`P``;0,``&T#``!M`P``;0,` +M`&T#``!M`P``;0,``&T#``!M`P``;0,``&T#``!M`P``;0,``&T#``!M`P`` +M-P,``#@#```Y`P``.@,``#L#```\`P``/0,``#X#```_`P``0`,``$$#``!" +M`P``0P,``$0#``!%`P``1@,``$<#``!(`P``20,``$H#``!+`P``3`,``$T# +M``!.`P``3P,``%`#``!1`P``4@,``%,#``!4`P``50,``%4#``!6`P``5P,` +M`%@#``!9`P``80,``&(#``!C`P``9`,``&4#``!F`P``:0,``&H#``!N`P`` +M;@,``&X#``!N`P``;@,``&X#``!N`P``;@,``&X#``!N`P``;@,``&X#``!N +M`P``;@,``&X#``!N`P``;P,``&\#``!P`P``<`,``'$#``!R`P``0,``'H# +M``![`P``?`,``'T#``!_`P``@`,``($#``""`P``@P,``(0#``"%`P``A@,` +M`(<#``"(`P``B0,``(H#``"+`P``C`,``(T#``".`P``CP,``)`#``"1`P`` +MD@,``),#``"4`P``E0,``)8#``"7`P``F`,``)D#``"9`P``F@,``)L#``"< +M`P``G0,``)X#``"?`P``H`,``*$#``"B`P``HP,``*@#``"H`P``J`,``*@# +M``"I`P``J0,``*H#``!```$`K0,``*T#``"M`P``K0,``*X#``"N`P``K@,` +M`*X#``"O`P``KP,``+`#``"Q`P``L@,``+,#``"T`P``M`,``+4#``"U`P`` +MM0,``+4#``"U`P``M0,``+4#``"U`P``M@,``+8#``"V`P``M@,``+8#``"V +M`P``M@,``+8#``"V`P``M@,``+8#``"V`P``M@,``+8#``"V`P``M@,``+<# +M``!"``$`1``!`$8``0!(``$`2@`!`$P``0!.``$`Q@,``,<#``#(`P``R0,` +M`,H#``#*`P``RP,``,L#``"K`P``K`,``+@#``"Y`P``N@,``+L#``"\`P`` +MO0,``+X#``"_`P``P`,``,$#``#"`P``PP,``,0#``#%`P``S`,``$```@#0 +M`P``T0,``-(#``#2`P``TP,``-,#``#4`P``U`,``-0#``#4`P``1``"`$@` +M`@!,``(`4``"`%0``@!8``(`7``"`&```@!D``$`9@`!`&@``0!J``$`;``! +M`&X``0`!!````@0```,$```$!```!00```4$```&!```!@0```8$```&!``` +M!@0```8$```&!```!@0```8$```&!```!@0```8$```&!```!@0```8$```& +M!```!@0```8$```&!```!@0```8$```&!```!@0```8$```&!```!@0```8$ +M```&!```!@0```8$```&!```!@0``,T#``#-`P``S@,``,\#``#5`P``U@,` +M`-<#``#8`P``V0,``-H#``#;`P``W`,``-T#``#>`P``WP,``.`#``#A`P`` +MX@,``.,#``#D`P``Y0,``.8#``#G`P``Z`,``.D#``#J`P``ZP,``.P#``#M +M`P``[@,``.\#``#P`P``\0,``/(#``#S`P``]`,``/4#``#V`P``]P,``/@# +M``#Y`P``^@,``/L#``#\`P``_0,``/X#``#_`P````0```@$```(!```"`0` +M``@$```(!```"`0```@$```(!```"`0```@$```(!```"`0```@$```(!``` +M"`0```@$```)!```"00```H$```+!```#`0```T$```.!```#@0```\$```/ +M!```#P0```\$```/!```#P0```\$```/!```$`0``!`$```0!```$`0``!`$ +M```0!```$`0``!`$```0!```$`0``!`$```0!```$`0``!`$```0!```$`0` +M`!`$```0!```$`0``!`$```0!```$`0``!`$```0!```$`0``!`$```0!``` +M$`0``!`$```0!```$`0``!`$``!```$`0@`!`$0``0!&``$`2``!`$H``0!, +M``$`'P0``"`$```@!```(`0``"`$```A!```3@`"`"8$```G!```*`0``"@$ +M```H!```*`0``"@$```H!```*`0``"@$```I!```*@0``"L$```L!```+00` +M`"T$```N!```+@0``"\$```O!```+P0``"\$```O!```+P0``"\$```O!``` +M+P0``"\$```O!```+P0``"\$```O!```+P0``"\$```P!```,`0``#`$```P +M!```,00``#$$```Q!```,00``#($```R!```4@`"`%8``@!:``(`7@`"`&(` +M`@!F``(`$00``!($```3!```%`0``!4$```6!```%P0``!@$```9!```&@0` +M`!L$```$#`/[^`P#_`08`_P,#`/H`!``% +M$0,`_.0#`!7U`P#T"@,`#@<#`.[]`P`/XP,`!@P#``;U`P`&%0,`Z1<#``+K +M`P`'Y0,`"^,#`/$'`P`'%0,`&_P#`.7Y`P#[\P,`X`(#`!?_`@`._`(`\P," +M``4)`@`>_@(``@P"`/[R`@#P_0(`\P("`!$#`@#^"0$`^?L!``L`!0#D``4` +M`OD&`/OY`0`!\P$```D%`/G]!@`>_P$`^`,!```&!`#^``$``@$"``+]!``) +M!`(`[/P#`/OU`P`)^0(`!_H"`/[X!@`%]0(`[O\"`.7^`@#M`@(``_,"`!<" +M`@#^#`(`"`0"``@&`@`*^0(`__,!`/L$`0#^"@$`!?L!`/_Q`0#U`08`#?\! +M```,`0`'_`$`]`$!``X!`0`/_P$``_``8``@\"``(0`@`'!@(`^/@"``#^`0#]``(` +M_@(#``,"!`#][P(`^?4"`/H$`0`!'08`__H%```"`0#^`0(`^_\$`/<#`0#] +M$@(`]?D"`/\/`0`$!0$``PD!`!K_`0#]]P$`_0D!`/7]`0#T_@$``?@&``#V +M!0`*`P$`_0X"`.K^`@`=_0(``>H"`!$"`@`#"@(``@T"`./]`@`&"@(`!_8" +M`/WC`@#G`0(`"?L"`.$"`@#^_P(`[0`!`/SY`0#L``$`"/P!`/D#!@#_'08` +M_/\$`/T"!``"`P0`_`$$`/H!!0`#X0(`Y`("`/\:`0`!X@$`!P4!`/7_!@`< +M`0$`]_P"`.\"`@`&_@4`!0$$``("`P#__`0`]P0"`.L#`@`%]P(`_/<"``/M +M`@#^&@(`_N,"`/CZ`@#A'P(`"_D"`!C_`@#H`@(``.X!``P!`0```P(`^O\% +M`/_E!@`!$0$`__0!``0!!```X00`^P$$`/\"`@#__@(`_`\!``7^!0#Y +M_P0`!`@!`.T!`0`!#0$`_Q$!``#P!@`*_P8`^0$$```$`P`'^0$`ZP`!``'E +M!@`=_P4`'P`%`/_D`0#@_@(`_@\"```>!@`%_04`_^(&``H$`@#E_0(`_A<" +M`/WT`@#]`P0``NP"`.K_`@`,`P(`_NX"`!_]`@`#\@(`[`("`.,"`@`!#P$` +M`!,!``<$`0`%!P$``!4!``#J`0#]^`$`\?T!``#T!@#]!P8``?L$`/\$!``` +M^P,`_^,%`/X'!@`,``8`_P4$`.4!!@`,_@$`#O\!```(!0`$^0$``P@!`/H& +M`0`%!@$``08%`/D``P#B``4`&@$!`/KX`@#O_0(`_^$&`/_Y!`#Q^@(`^?8" +M``+B`@`+^P(`!_<"``+H`@`8_@(`_1<"`/<&`@#^'`(`[/\"`/[D`@#J`@(` +M`Q$"`.D"`@#X^P(`&@("`.H!`@#L_@(`%O\"```8`0`"]P$`!OL!`.@``0#V +M_P8``O@&``0$!@#Q``8`!@0&``'Y!````04``/\$`!H`!@`&_`8`!@4!``;Z +M`0#V_0$`_0L!```=!`#Y!`$`\`$!``/X`0``%P$``PL!``'I`0`+_0$`\@(! +M``X"`0`&^`$``_H&``#F!@`<_P8`X0`%`/`"``+E`@`5`@(`$04"`/7\ +M`@#[^`(`Z_T"`!\?`@`2`0$``?(!`/(!`0`5`0$`'/X!`.W_`0``$@$`!/@! +M`/@"!@#[`@4``!H&``8#!@`#_`4`\``&``$<`0`3_@$``.0$``0'`0`+`@$` +M#?X!`/L'`0`-`08`"@$&`/X&!0``\08`_PL&`/3Z`@#Z]@(`]_L"`.?]`@#M +M^0(`Y@$"``_Y`@#]'0(`_N`"`.D#`@`3^0(`]@0"``+O`@#U^P(`^0@"``$5 +M`@``!0,`&`$"`!GC`@#^Z@(`!O`"`!,%`@`3!@(`!O("`!_^`@#\#@(`"/L" +M``/I`@`!Y@(`"OP!`/WV`0#X!@$`_N(!`/GZ`0#_'P$`%``!`/\9`0#]^@8` +M`_\"``#E!``)``0``_4!``8'`0#D_P8`_@@&`/'_!@#I`0$`]OP!``+R`0#D +M_@$`\0(!`/SV`0`7`0$`"OT!``@"!@`.``8`!@(%`!``!@`?_P8`]_\%``@"`/@*`@`:_`(`&OT"``+C`@`1^0(`_?`" +M``OZ`@#D_0(`X0,"`.3\`@`*^P(``Q,"``,=`@#^$`(`\04"`.7I`@`8`@(` +M^0D!`/'^`0#T`@$`#P,!``+U`0`![0$`\`(!``@#`0``\P8`_`,%``$$!``$ +M_@0`^0<"`/D1`@#V`08`_`8&``0&!@`-``8``OL% +M`/S^!``"_`0``1L&``4-`@`$]`(`%_T"`/SU`@`#Y0(`_>4"`!+]`@`0_`(` +M%`("`.+]`@`*^`(``NH"``CX`@`.`P(`#@0"``L&`@`!ZP$`Y`$!``$(!@`` +M\@8`!`($`/L#!0`#]@$``Q<"`/WK`@#]!@8`"0$%`/W\!0#G``8`#/\!`/KY +M`0`'_04``.,#`/#_`0#_%0$`^OL!``(+`0`&^0$`_0@!``D#!@`#^P4`!/T% +M``;_!`#]^P4```L%`/O]!0#^!04`^``$`("`!```'P4`^/\%``H"!@`0!`(` +M]`8"`/T5`@#_%@(`'0$%``GZ`@#G_@(`_A$"``/T`@#S^P(`[OX"`/4'`@#H +M`0(`!PD"``<1`@`(]@(`#@8"`!<&`@`"Y`(`YOX"`/[H`@`"Z0(`!>,"`/GX +M`@`%]@(``1("`/8%`@`,!`(`%/\"`/'[`@`#X@(`_Q(!`/[U`0#X!`$``P$" +M`/8&`0`=_@$``.P!`.G_`0`-`@$``O0!`/_O`0#S_0$`Z0`!`/\>`0#R``8` +M`?$&``'A!@`%`@4``.\&`/;^!@#Z_@4`&_\%`/WI`@`$"@(`#OX!`/_M`0`# +M\0$`%@`!`/WS`0`9_P$`_O0!`!+_`0#C_@$`_^P"``[X`@`4_@(``_`" +M``+M`@#X]@(`_NT"``L$`@#P`P(`!0@"`/4)`@#B`@(`%@$!`/L&`0#I_0$` +M`>X!`/OZ`0#Y!@$`%@(! +M``P"`0`-_0$`$@(!`!K^`0`3_0$`$/X!``$9`0`;_@$`Y@`&``+V!@#[_`8` +M_PT&`/[[!0#X`04`^OT&``'D`0#X"0(`#?H"`/?^!@#B_P8`_/T%``,$!0#^ +M_`0``A4"`/X3`@`7Y0(`%_4"`.'^`@#]X@(`_1\"`/H)`@#\]`(`^>$"`/\8 +M`@`$]0(`]O@"`/'Y`@#^Y0(`%/P"`.(#`@`,^@(`#/P"`!G^`@#Z\@(`'OT" +M``GW`@`'"`(`&0("`/KT`@`."@(`$0<"`.L"`@#P_`(`\QT"`.4&`@`"#@$` +M&0$!`.W]`0#Z!P$``0P!`.W^`0`0_P$`_A(!`.L!`0#G_P$`&``!``+S`0`! +M"P8```X&``()!@`)_@8`!/P%``+_`0`!`04``0,"`/H"!0#\^@8``.T&``D" +M!@#@``8``!D%``/^`P`#``$`%P`!`/H(`0#@_P$`^@4!``,/`0#AX0$`$P,! +M``'P`0`5_P$`_^`!``T#`0#L`0$`_>$!`/WQ`0`$^P8`Y0`$``8&!@`;^P(` +M!AX"``?I`@`:!`(``@H&`/\*!@`$_P,`#P$&```/!@#Q`08`_QP&`/_W!0`! +M"04``OX"``($!```'`0`_00%``;Q`@#YZ0(`$1$"`!+X`@#V^0(`_!8"`!L" +M`@#I!P(`&P4"`.?C`@#\X0(`%_D"`/$&`@#D!@(``PP"`!(#`@#U!`(`_>P" +M``P<`@`/!`(`\@8"`/@(`@#S^0(`^0L"`/__!`!```(`-!@``!`4```0%``` +M"!0```@4``!$``(`+!@```P0```,$```#!````P0```4$```%!```!00```4 +M$```!Q@``$@``@`X&```%Q@```,0```#$````Q````,0```$$```!!````00 +M```$$```3``&``\8```^&```C``"``88``"0``(`&!@``)0`!``"$````A`` +M``(0```"$```*!0``"@4```@%```(!0``!88``"D``,``10```$4```\#``` +M/`P``#P,```\#```/`P``#P,```\#```/`P``#\4```_%```#A@``*P``P`< +M%```'!0``#`4```P%```"P0```L$```B"```/0@``"L(```O"```)`0``"0$ +M```Z"```-P@``",(```%"```.P@``#L(```["```.P@``#L(```["```.P@` +M`#L(```["```.P@``#L(```["```.P@``#L(```["```.P@``#D4```Y%``` +M'1@``"48```9$```&1```!D0```9$```-1```#40```U$```-1```"T0```M +M$```+1```"T0```R!```,@0``#($```R!```,@0``#($```R!```,@0``#($ +M```R!```,@0``#($```R!```,@0``#($```R!```,@0``#($```R!```,@0` +M`#($```R!```,@0``#($```R!```,@0``#($```R!```,@0``#($```R!``` +M,@0```T(```J"```$P0``!,$```?!```'P0``"X(```;"```$@0``!($```2 +M!```$@0``!($```2!```$@0``!($```S"```,P@``#,(```S"```&@P``!H, +M```Q$```*1```!$,```G#```'@@``!X(```V"```-@@``!4(```5"```"0@` +M``D(```F#```(0P```H$```*!```"@0```H$``!```8`'!@``"@4```H%``` +M(!```"`0```@$```(!```"P8```8&```/!0``#P4```0$```$!```!`0```0 +M$`````@````(````"`````@````(````"`````@````(````"`````@````( +M````"`````@````(````"`````@``.(`!@`T&```#!0```P4```D&```,@$& +M``(8```"`@8`,!0``#`4```X%```.!0``!00```4$```%!```!00```(#``` +M"`P```@,```(#```"`P```@,```(#```"`P```0,```$#```!`P```0,```$ +M#```!`P```0,```$#````00```$$```!!````00```$$```!!````00```$$ +M```!!````00```$$```!!````00```$$```!!````00```$$```!!````00` +M``$$```!!````00```$$```!!````00```$$```!!````00```$$```!!``` +M`00```$$```&#```!@P```8,```&#```!@P```8,```&#```!@P```<8```? +M&```0``&``L8```]$```/1```#T0```]$```+A```"X0```N$```+A```#<8 +M```9&```#10```T4```_$```/Q```#\0```_$```@``!`&`8```K&```@@`% +M`'$8``!I&```31@``$48``!U&```71@``%$4``!1%```8A```&(0``!B$``` +M8A```'(0``!R$````0``'@$ +M``!X!```>`0``'@$``!X!```>`0``'@$``!X!```>`0``'@$``!X!```>`0` +M`'@$``!X!```>`0``'@$``!X!```>`0``'@$``!X!```>`0``'@$``!X!``` +M>`0``'@$``!X!```>`0``'@$``!X!```>`0``'@$``!L!```3`0``'`$``!P +M!```<`0``'`$``!P!```<`0``'`$``!P!```<`0``'`$``!P!```<`0``'`$ +M``!P!```<`0``'`$``!##```0PP``$,,``!##```5Q```%<0``!S$```$```'A```!X0```>$```,10` +M`#$4```M%```+10```D0```)$```"1````D0```V$```-A```#80```V$``` +M$@P``!(,```2#```$@P``!(,```2#```$@P``!(,```C&```$Q@``$@8``!` +M``0`!1````40```%$```!1```'0$``!T!```=`0``'0$``!T!```=`0``'0$ +M``!T!```6`@``%@(``!8"```6`@``$<0``!K$```4@P``%(,```*#```"@P` +M``H,```*#```"@P```H,```*#```"@P``#H0```Z$```.A```#H0```R$``` +M,A```#(0```R$```(1```"$0```A$```(1```!T4```=%```-10``#44```# +M#````PP```,,```##````PP```,,```##````PP``!8,```6#```%@P``!8, +M```6#```%@P``!8,```6#```.Q@``$``!@!_&```?A@``'P0``!\$```?!`` +M`'P0```B#```(@P``"(,```B#```(@P``"(,```B#```(@P``%`8``"(``,` +M,Q@``)``!@`O%```+Q0``!<4```7%```7`0``%P$``!$```7A```%X0``!>$```8Q```&,0 +M``!C$```8Q```&H$``!J!```:@0``&H$``!Y#```90P``%D(``!9"```7PP` +M`'<,``!["```>P@``%0$``!4!```5`0``%0$```G!```)P0``"<$```G!``` +M)P0``"<$```G!```)P0``"<$```G!```)P0``"<$```G!```)P0``"<$```G +M!```)P0``"<$```G!```)P0``"<$```G!```)P0``"<$```G!```)P0``"<$ +M```G!```)P0``"<$```G!```)P0``&0(``!D"```9`@``&0(``!D"```9`@` +M`&0(``!D"```9`@``&0(``!D"```9`@``&0(``!D"```9`@``&0(``!Z#``` +M>@P``'H,``!Z#```>@P``'H,``!Z#```>@P``%L8``!6&```9A0``&84``!G +M%```9Q0``&$4``!A%```/@@``#X(```^"```/@@``#X(```^"```/@@``#X( +M```^"```/@@``#X(```^"```/@@``#X(```^"```/@@```X,```.#```#@P` +M``X,```.#```#@P```X,```.#```)A```"80```F$```)A```!$0```1$``` +M$1```!$0```Y$```.1```#D0```Y$```0``!`&@8```E%```)10``"D0```I +M$```*1```"D0```5$```%1```!40```5$```*@P``"H,```J#```*@P``"H, +M```J#```*@P``"H,```:$```&A```!H0```:$```1!@``$(8```/%```#Q0` +M`!L$``!!!```"!`0$Q`3%A86%A86&A@:&QL;&AH:&AL;&QT='2(B(AT='1L; +M'1T@("(B)28E(R,B(R8F*"@H,#`N+C@X.D5%4]SQ_)\4)_V?T?\'`!!7`V`0 +M6`-@)@#X_X#U_)]\]OR?____?P```(```&```/+\GX;W`P``]OR?2!0``&RJ +M`&`````(__\?``````$```!``.[\GP```@"HJ@!@W*@`8````D*($P````@` +M``$`@````(```%+\GP"9`````OR?@):8`/T)_/\`^OR?`/[\GP8``.`&``"@ +M`"[\GP#>_)\`&````.+\G__G______^_```!`/___O_`#P`````_``````0# +M`__@`P+_X```,`,&"__P!```@`$``(#_]___```"@(<3``#___\/````P/_O +M`````!```/[[G^0-``#___\!__]?`'OX_?\```"@````X`$``.`!``"@`P`` +MX`,``*`*``#@"@``H````&(#``"`A`<"``````+___^]____\```$(#_\___ +M`!\```!```#_?_#_`(`)`````/#_O___```(@````X#_\/____\/_P``#X`` +M``:````!@```$8"5`0!`E0``0``.`````,`````P````#`````,`__\_^/__ +M_\<```X`````.`#P`0```,`'G0``0)4#`$"5`@!``(`,````"@````6````$ +M@```#8````R`C0``0!`(`&```/`/`#__`/___X`6```(($X``/___\#W??__ +M`(```````%`@O_\`*00$````>``FI(0`QH2$`"2DD!(``#`&(20$``,@```" +M(```0&\``.AN``#@#P``3`\``(`/``!@NP!@?+X`8$0S(A&\$@``J&L``/__ +M_P#`M@8`*%4!8```4@#,:P``$&X```QL``"L;````/\`__@=``#P_P``0`@` +M`"`__P`WS4+]9KCTC(OQ3P<\;0``P!(`````P#^,#```4`@`8,!K```0#0`` +M/YP`````)``!8```B`\``+0/``#T"P``@`T``*`-``"P%@``:%T``.A=```K +M#0``Q!(``+0+```H#0``M#T```P^``"9/0``K!L```0<``"0#```L`@`8`@, +M``!4;0``-`P``'0,``"H"P``Z`\``$@3``!`$```%3T``)02````(`,``@@` +M`(@,``!`;0``K`P``)`)``"X%```N!(``*Q=``!L:P``J!(``!0/``"(7@`` +MG!(``$`)`&#P;0``$&L``!$/````4@(````X!`#@A@(``,P``'AI``!V+P`` +MV!L``.P-``!&!0!.8@``4&(```YC```08P``_&L``-`.``!@;0``$`\``!@/ +M```\%```5!L``/__O_^`7@````!``)@+``!D7P``\%\``+!=```I#0``]%T` +M``P)``!,#0``1&T```P*```,#0``U$P!8/!,`6!F=XB9__\#^```_`<`_@,` +MM!(``%P]``"<:P```#__`=1K```H@P%@['8!8."L`6!@;0%@@&`!8)AW`6"( +M;P%@.*P!8'23`6!880%@=*`!8-!7`6"LI`%@J)(!8,"N`6#TAP%@'(H!8,B4 +M`6#P"0!@```0^```0/@P"@!@````^```,/@(@`````#@`0``(/@9```0&0`` +M(!L``!`;```@$P`!`!L``(`;``!`&P``D!L``%`;``"@&P``8!L``#`;``"P +M&P``#0``0/"^K=[__```8!$``````&"`$0``H!$``,`1``"D +M$```!!$``````#RH$```"!$``````!#,$```+!$``-`0```P$0``K!````P1 +M`````-S_``#@_P#P_P``7V`!*)0`````_P``_P```0```8N%`@"+A08````` +M_@``$`$,A@(`#(8&`/G_``#P#P``/X`-`$#[`@`L*P``=%X"8```"0``@`0` +M\`P`8)Q<`F`,AP)@H"H``,0E``#,)@``(S__`",___`#`/`/<``'`/@-```L +M*0``,'4```,`_P\#`/$/_____D`K````T`(`^"(```,6``#$)`````!0^-0E +M``"`_P``P/\``,AW`F!D=P)@T"4```(1```"%@``,`T`8*2%`F`"&```$`X` +M8`,1```%$0``N`L``-`-`&`D*0``.",``#"&`F`_"0``1",``#25`F!\M`)@ +MG*4"8&R3`F#@#P!@?(<"8`S/`F!\QP)@!(@"8`4``$#@Q@)@])L"8"3*`F#@ +MQP)@N-`"8*BZ`F!0)@``!-,"8,34`F"$AP)@\(<"8-R'`F#(AP)@M(<"8*"' +M`F",AP)@MPL``"`I```P#@!@D`X`8&@C``#@#@!@T`X`8*`/`&`\)```#"0` +M`!0D``!@)0``F"0``'`C``!H)```J",``'0C``"P$0!@9-L"8,3J`F!+1%/#P-/)N +M+:(N+=(N+9(N+8$]`,#=$8"9$="9(*"9())CY8)B%8)B%V(C]V)N+4(N+5*O +MQQP&4$008$0@0F/W03X`\B/>L3L`PL%D0/\@\FXMTBXMTF/>DB.BHJ<`43\` +MH)D@DFXM@BXM@F.B\B/=\FXM\4$`XBXMT4``#'KP[B#B8]UR;5928\9E@?<, +MBK%"`,+!9*6`]Z*@!K%#`!#!(.5_]Z*AP.6;]PP>@40`@F$8\B$8\F/$XF(N +MTB/`LJ`"L-T@TF$8PB$8PF/`DB(OH34`L)D@DF(OI9CW038`4M$48M$54B4N +M4F8:444`848`QA,`9CP"1MH#O,>")P#,&,:1`\(B+[+1%0Q-T,P@PF(OF`<, +M"J)K$)Q9"^D6WB/RR?X6GS."R?T6."ZBR?P6FEL,&[)G`+)B20P-XM$5>>[2 +M;A"BH8!ED_<,#[+1%:(A&7+1%7CG@B$:HFL=@FL +M`D;&!/+1%0P8@F))HF\01HO_``"2H@.0G,!6B=W8]Z%``.*A`.#=(-GWPBI$ +MPFGF0,,*JD'#!NR8DG&:_^] +M"L%'`-+1%:)M(L>:(Z+!$+*@#.5.+*+!($P+94XL#`Z2T14,"++1%;(K(H)I +M%N)I%\%(`+>\,:(B+]%)`,*OS\"J$!P,P*H@HF(OTBV&X4H`T-1!Y\T1X4L` +M\BX(`-H1\/#T\-T@TFX(DM$5&XN":1"W-@)&4/^B(B]\N["J$*)B+\9,_\`@ +M`,+1%<(L%3WPS!S&S0/`(`#BT10,#9+1%9(I%0Q/@B6+ +M`>T*LB<1HB<2D@X`@@X!\@X"X@X#@)D1D(@@@(@1@/\@@/\1\.X@XF=_)8D! +MLM$5LBLBQH[^````TB$8R`O7'`YV@`?R(1CH"_<>`T;\_P"Q4@`,&8+1%:)H +M$))K39)B2<:5_@PD2TF/_V0?"8DF&B_X`DM$5 +M#!NR8DFB:1!&BOX``&9)`D;;`V9I`@8J!*(CK#WPH,`$!VH"1K<"DM$5=H`0 +M\B/G^0GH">#@%.D)V`DF/0(&^O^150"Q5`#R(B`,2-%3`(#_(`P(\F(@P"`` +MXB(@P"``@FV"L.X@@FD6P"``XF(@X58`P"``\B(@P"``X/\0\F(@P"``\B(@ +MP"``@FV"L/\@@FD6P"``\F(@P"``TB(@DM$5P"``X-T0TF(@P"``@B(@?+O` +M(`"PB!""8B#`(`!V@!#B(^?I&=@9T-`4V1FX&28[`P;Z_P",K(+1%*)CK/(C +MK/)H/TP*Y2;WTB.LLM$5N/NB(B#0P`0';0*&B`*2T17RH0#P^B#R8B#`(`#B +M(B#B:0+`(`!V@!#R(^?Y.>@YX.`4Z3F(.28X`@;Z_^*N_^#J$.)B(,`@`((B +M((DIP"``%HP`TF.L\B.L\FD"@M$5^#OR:!.!0`#2*$D,&GSNX-T0TF$8DB$8 +MR$L,#^%7`,+,_L#Z@PP*D/\@\FA)TB(8DM$5@3\`X-T0TF$8R$N@^B#B(1C" +MS/K`^(/"H(#P[B#B81C2(1C28AC"8ZBB8ZF"*P1V@!#2(^?92QE<"DM$5#"_P^B#R8B#`(`"B(B#`(`!V +M@!#R(^?Y:>AIX.`4Z6F(:28X`@;Z_WS>X.H0XF(@P"``@B(@B7G`(`",;-)C +MK/(CK/E9T4D`DM$5#`@,"J)G?H)I&Y+1%=(MY=F)R$OQ6``F+"[HB?#N(.F) +M@BLUP4D`HM$5@(D$%D@DJ(JB;.62*S7RT1629RB0FA229W[,Z08'`-%9`,B) +MT,P0R8F&\O_BT17B+AL,&0P(X(F#@F\;\B(G-.)B:M(G-=)B:X8%````DF)HTBA+XL[^_(Z&/@#2)S<,B&8="N(K%@SZPJ``5KX`\BLU@/\@\F)G*08D`)JZJ/VR;2'P(`"B*A;EY"O2T17"T17(_-(M(<(L%J#M@N#,P*P\ +MHM$5J/J]#:(J%J7B*]+1%=(M$<(G*1OJX-V"VLS"9RG&$0```*+1%:CZO0VB +M*A9EX"O2T172+1'")RF@W8+:S,)G*08)`/(K%N(G*?KNXF_P``DB$8?*J@F1`,&J"9())CW+._N"=@\"9())K5N`(`/+1%:)O%(RJ@B(ODJ`(D(@@@F(ODM$5L4`` +MXM$5Z/[RH'#"*U;H3GS=?.KBSO[@K8/`JA"B:U:`Z@/R8ZCB(ZG18P"BH`%@ +M[B#B8ZG"(ZFRH'2":1+0S!#"8ZFR8ZAES?;X)R9O`H8@`)+1%7:`$+(CY[F9 +MJ)F@H!2IF8B9)C@2!OK_PM$5#!W28BZB;!!&VOP``,(CK+(B(#WPP-`$P.`$ +M5CY5DM$5#"_P^R#R8B#`(`"R(B#`(`!V@!#B(^?IN:BYH*`4J;F(N28X`@;Z +M_WS8@(L0@F(@P"``\B(@^3\(%E`#)B249^_```TL$0DM$5 +M#`C"T16BT16B*A?"+!:":2/0VJ"Y#1NJTL$@H*!TT,R@N0P,.R6=*PP\DM$5 +MH(!TTM$5LM$5LBLCTBT6#`J":1<;_?#0--)I%M(B+Y+!$':L"<@)2YF\S+J\ +M&ZK!:`"W/#GA20`L"Z*OSZ"M$+"J(*)B+^(NAO%*`.#D0?=.`L9+_,%+`-B, +M`+H1T-#TT+L@N8P&1_P`9CH"1C0##`L,"I+!(!P.=JX*R`E+F18L'+J\&ZKQ +M:0"W/P+&;@"!20`<"Z*OSZ"M$+"J(*)B+X(HAI%*`("$09=(`D8U_,%+`-B, +M`+H1T-#TT+L@N8R&,/R8]S=I`L9D_.%```R(@(D@B??R+D3R9QCB+D7B9Q?& +M)?P``,P7QBX"F`<+F1;Y<[+1%0P?S10``,+_#Y +M(/GWXBU$XF8*3$W`,P9QA,""\G, +M'`8!`@P-V3?H1Q;^">%``(+1%:+1%;+1%9+1%9(I&+(K&:(J$HCHD)O`L*K` +MJ5>99_(H?/EWXBY'Z8?2(AW9E\(B',FGLB/RN;>B(_.IQY(C\9G7\B/U^>?B +M(_;B9P_2(_329Q#"(^O"9Q&R(^RR9Q*B(Z2B9Q.2(Z.29Q3R(E3R9Q7RH`#B +M(E7B9Q;2(E;29Q?"(E?"9QBR(^JR9QFB(Z*B9QJ2(_>29QOR:'W`(`!RT15X +MYPP:##NY!Z)B+D;D^X+1%0P9DF))HF@01N/[``"B;A`,',)B20;@^P#BRO`6 +M7GCQ20"BK\^@K1"B8B_R+X:!2@#P]$&'3P*&Q_O!2P#8C`"Z$=#0]-"[(+F, +MQL+[\M$4?.B`BA""8ZS`(`#B(ZSB;SC`(`#&HOP``++1%'SMT-H0TF.LP"`` +MDB.LDFL_P"``QD']``#RT15\Z("-$()CK,`@`.(CK.DOP"``QG#]?._P_1#R +M8ZS`(`#B(ZSI6<`@`$:B_0``P4``TM$4XBQ$XF$8PBQ%PFTOP"``HB$8LM$4 +MLBLOHMH!)8"ALSHJ`!Y7L!5Y/IHB$8 +MLM$4LBLO9HBI%HF<=AGK[`.A-)BX"QA3\HJ#[AA;\``#RT14,&()B2:)O$$:%^P`` +M.<,&0PZJ0>28BX&?OM\ZJ"L$*)CK,`@`((CK(FIP"``1J7^`+(G(+)A +M'Z(G(+%L`*"C0;"J$*)B%K(G(,%G``P#L+`DP+N@PM$5LFP5=H`0TB(6TF$8 +MPB$8IQP'&S-'$P(&^O_A;0#]`S$W`/<^`D8>_#$W``P8#$FBT15YZID'@F)) +M1F+[``"Q;@"B*3:PJA"&+?R2(ZR0H`0':1;2T11\[N#I$.)CK,`@`,(CK,)M +M/L`@`,%O`'P+TM$5,FTE0FTF,4X`(FTD04\`@B."(B/"^"3B)$*PB!#`R""P +M(A#2(T*P_Q"P[A""8X(B8\+Y)+#=$.)D0M)C0L`@`,)C@L%P`,"R(+)CPL"_ +M(+DDP+X@LF1"P,T@PF-"P"``LB."PB/"N"3")$*R(T+`(`""8X(B8\+Y)"+1 +M%2(B).)D0M)C0L`@`,(C@L+1%+(CPH@D0B1",B-"0M$50B0F,FP^P"``,M$5 +M,B,ES!K&H?R28ZS`(`#B(ZSB;#[`(`!&G?P``)(CK)"@!`=I%H+1%'SKL+D0 +MLF.LP"``\B.L\F@]P"``TM$5(FTDDFTJ4FTG0FTF`)8T`LM$5LBLBQI[Z``#"T14,'=)B +M2:)L$$:M^@``DB.LD*`$!VD6\M$4?.B`B1""8ZS`(`#B(ZSB;SS`(`#2T14B +M;222;2I2;2=";29R;2EB;2@R;25A=``Q3@!Q0]`16?XN!0``<#,#)(,GW +MHBA$HF<4@BA%@F<3!DKZDB.LD*`$!VD6XM$4?._P^1#R8ZS`(`#2(ZS2;CO` +M(`#2T14B;222;2I2;2=";29R;2EB;2@R;25A=@`Q3@!Q=0!!3P!2(X+"(\*X +M))(D0GP-T%40@B-"T,P0<"P@T+L0 +MXF))HFT0!@3Z`/+1%?CO\B]/@J:`BO_R#V$,&/#XD_DW!NC]``"!:0"W.`(& +M'/Y&K/T`#`G"T12R(ZH,JK)L,L+!9':J"MB,XJ($C+T;F>K,#!_R8DD&\/D` +M@M$5@B@?J"BIC(A(@FP5)IGD#`T,/K+1%;(K'^E\TFP6^%OP\`3R;$'B(1NA +M=P#@Z8*J[N)L5H(A&^%``("(H*"(D*+,'()L5_(A&])L+/)L$M)N2;A+XM$5 +MHFX.MDMKMEL4\7@`]SLZ@7@`MS@"1BX`D6$`EYM?#$P,C?%-`.+1%.(N,K+1 +M%;(K'_#N$.)CJMDJR3K(>\F:N(NYJN56&\83``!F:S"2+A\,"`ROV*G2;!.X +M>;)L$+*A`)B)DFP1^9R";"VES1C&"0``]BL"1BP`MCLV)CM3X4T`TM$4TBTR +MLJ``X-T0TF.JLFP)I3L!,3<`@3@`\M$4\B\R#!Z`_R#R8ZKB8DE&K?D``-%N +M`++1%+(K,@QHDBX?T+L0LF.JDFP7B9RE?B7&\?\`#$\,#0QND4T`@M$4@B@R +MPM$5PBP?D(@0@F.JZ2K2:EKB:COY6KA\LFI"F(R2:D.(?/N(@(1!@FI(^(SB +MRFS[__#T0?)J2]).S-).S=).L,)J$"5#)H;;_Q8+];8K`H;2_[*A`)%-`/(N +M'X+1%((H,@Q-#`Z0B!""8ZJ8KY)L$PP9B'^";!#XC_)L$>)L+>FLV4\`.(*`+(G$9(*`?(*`H(*`Z(G$H"9$9#N(`#_$8"(`8#_(/#N(.)G?V4Z +M`++1%;(K(L93^0P+DL%D#*QVK!;BT17B+A_8B>@N\J($^IG@W<`6[0P;NX+1 +M%8(H'XA(@LC^5B@/#`N2P62BH`IVJ@K(B8RL&[O2H@3:F88#````XM$5XBX? +MZ"[IB6:;#H+1%0P/#!F28DGYZ,9,^0QI#`P,/X*B!("+@G+!9(IW^7?"9Q;B +M(1NBT13Q=P#@ZX*"T17Z[N)G5M(A&\)G+-)G$K(CJK)J,+%N`*(J,')H(((H +M'["J$*)CJIF7@F<70"2*3'"T17"+""@F2"2 +M8ZJ&"````-+1%0P>XF))HFT01BOY``#RR_86S_+2H@30VX+"P63:S)A\O%DF +M23,F.3#BT14,'_)B27GNQB#Y@6@`M[@"!LG\QKG\HM$5#`D,&[)B29GJ!AKY +M``P,R3?&`OT`TM$4\M$5#`<,&++1%;(K(H)L%GE\\B\?\FP7@#`(`"R*L6Y`8@!EW@$DFK&.0+(`?$_ +M`,#.!1;,$;*A`.%``"(JJBD1XBY)#`+-`N#@!.##DPO<%ET2D4T`B!&0B!"" +M:JIA3P!13@#A.0!!5`"&#```*2$,J7:I`SWP/?"((7(JYPRM#!EP9.`=R!Y(7:M`SWP/?"((0N[G,@F"QJ\'&8$O]@=T-`$V2'& +M[?\```#H(>P.,FJT=H`+DBJUF2&((0N(%O@)1OO_`-(N($=M-2DA!N;_`.(J +MMCWPEOX)#"B":K1V@`FR*K6Y(9@A)AD"QOO_(FJT\FK&R!'":JI,"F7"]1WP +M`((E@XDAF"&")P#P[B#I`<@!R0VY`9@! +MDFK&!N;_`")JM$;B_P``D6X`B!&0B!"":JJ&M?\``+$U`,*@\,)JJ+)JJ0;: +M_P`V00"BH("ENO61-P"Q0`#`(``,',D"HBM#HFM#@BG%1W@-=H`%TBG%1WT$ +MQOS_``#`(`"BH(`EMO4=\``V00"BH0#EMO6A-P"Q00#`(`"R:L:"*L4]\!=X +M"W:`!9(JQ1=Y`L;\_PP@AX-T@V2&X(;)LYN&#`-$Y``P;P"``H80`B"&@B!")(?@A\FSF +MJ!&!/`"PJB"B;.J";-J2;4KR(A6!A0#P\'2`_R#R;*:R+.6Y,8@QHJ\/\B(E +MH(@0B3&(,?#\-<#_$8#_(/DQN#&R;.6B(B2QA@#QAP"@H'2PJB"B;*:"+.6) +M,;@QHB(E\+L0N3&X,:"L-8"J$;"J(*DQB#&";.7R(B2!-0"AB`#P\'2`_R#R +M;*:R+.6Y,8@Q\B(EH(@0B3&(,?#\-<#_`8#_(/DQN#&R;.6B(B2QB0"@H'2P +MJB"B;*:"(B2A9`"`@'2@B""";*;R(B2!B@#P\'2`_R#R;*:R(A?QBP"!9@"P +ML'3PNR"R;*:B+.6I,?@QLB(@@/\0^3'X,>"[$/"[(+DQJ#&B;.6"(A^AC`"` +M@'2@B""";*;R(Q\6'R."(R#R(AB*__)M3[(C(:(B&+JJHFU:DB,B@B(8FHB" +M;5OR(R.R(ACZN[)M7*(C%J)M3/*B`,`@`)(B?A9Y(A:4(/)M3+(C-I=["Q84 +M(H&-`()M3@8"`!8$(I&.`))M3E&/`**D$H(C'I(C'W(C'3"($:"((("9$>!W +M$9!W((!W(')L\+(C)**AP)(C)%"[$%&0`*"9$+"Y0:&1`*"[`9"60<"9`5"[ +M$%&4`*"9$+"9(*(C)#R+@B,DL*H0L9(`H*-!X*H!L*H0L9,`@(`D`(@1L(@0 +MH(@@D(@@@F973&X,:&5`%"[$+DQF#&!E@"@F1"9,7(C)*&7`)&8`(!W +M$'!QY8@Q4B,D4'6,/H;[_P``HBRLDBT@H+`$!VH2?.B`BA"";*S`(`#R+*SY0<`@``PN +MX)D@DFT@P"``DBT@P"``=H`0XBSGZ5&(48"`%(E1^%$F/P(&^O]\V(")$()M +M(,`@`/(M(/EAP"``G/NB;*R2+*P=\+(LW*"[$/"[P%:K]0;0_X(B>[T#K0+@ +M"``=\+)LJ.(LJ?%&`+)LJ*#N$/#N(.)LJ;)LJ-(LJ=D!QA7_HB,@DB(8JIF2 +M;4^"(R+R(AB*__)M6P9X_X%)`*%9`+(HY;DQF#&@F1"9,;@QLFCEAG?_`(&: +M`()M3L9[_Y&;`))M3H9Y_P``H9P`HFU.QG;_````-F$`D3<`K0(A?P#R*>K1 +M0``,#"#_$/D!(8``Z`'B:>K";4>"*>:)$?@1X8$`(/\@^1'8$8&=`.#=$-D1 +MR!'`B"")$2@1(FGFO0/"H)@A.0#`(`#QA`#H$?#N$.D1V!'2:>;H`0P8\J02 +M@.X@XFGJTB,>XB,?@B,=,-T1\-T@@.X1X(@1X(@@T(@@@FGP\B,@XBH8^N[B +M8D_2(R&"*AC:B()B6O(C(N(J&/KNXF);TB,C\BH8@BI[X7X`T/^`\F),BL?TBL= +M,/\14/\@@#,1X-T1,-T@\-T@TF+P@BLD_#P=##_ +M(/)BIN(J%_&+`)&C`.#@=/#N(.)BII)M3D)LVN`(`!WP``""(R#R*AB*__)I +M&-(C(H(J&-J(@FD:QK3_````-D$`\:4`#`YBH_X,%X&H`)&G`**@__:S#$&D +M`$!#H$@$#!6@!``,!3)B(V)B,')B+H)B*9)B**)B)>)"D>)B*N)B+^)B,>)B +M,N)B,^)B-.)B->)B-N)B-T("6+#%$?#3(+CBLD*0TF(FTF(KP,,@T:8`0$01 +M0$,@X@)9T,P@PF(G,.X10.X@\.X@XF(LXF(M'?`,%0P3AN/_QN+_-H$`,,"T +MMBP.]DP%MBP"1G<`0LS\%G0=#!5A3@!Q3P#'8U#Q.0#H(N)O@M@RTF9"4F9$ +M4F9%N%*R9IJH0J)F@IB"DF;"B)*))TBB0F=".+(R;X#HPN)OP-C2TF9`N.*R +M9H"B(@^B9L"2(A"29P""(A&"9T!!J0#R(A(,"PP#\/2#=I\GTB=#B#>2)L.B +M)H-2)D,;N]"((*!5()!5((!5((S%#`OB(A0;,R8N"$8!```,K[>_[H$W`#WP +M=H`:0BCG0$`4)C0*DB(4#`LF*0W&^O\;NPRJMSH#!O?_`)(HK)"@!`=I$GSM +MT-D0TFBLP"``LBBLN0'`(`#1<``X,M`S(#)F0OBBT/\@\F="Z(+0[B#B9L*X +MDE&#`$%O`-"[(+)G`O(B!,!%@S&J`$#_(/)F@C)FFN(FFC<>"W:`!4(FFC<4 +M`L;\_\(F0K(FPE@G0B=",B:".0$QIP#`(`#X,O)F0NA"XF:"V(+29L+(DLDG +MLB(*LF=",F::4B::-Q4+=H`%TB::-QT"QOS_P"``/?!V@#KH0M(F@MD1PB9" +MR2&R)T*Y,5(FPEE!2"=)4?@1]Y[@^"'H,O>>V5@Q2*)7E-+(0;B"QYO+Z%'8 +MDN<=`P;O_P"<2I)HK,`@`$(HK$D!P"``?/_R9H$=\`!\^()F@1WP``PT`H;[_QWP`#9!`$*A]':D!%@"-Q7_'?`V +M00`L`W:C'8)R!()R1()RA()RQ(+2`2+2`H)X!()X1()XA()XQ!WP-F$`,.H# +M,F$`=H`.D.H#F1&(`4@1@$3`)[0"AOK_'?`V00#!L0#1L`#QIP!!KP!1K@`, +M9N$\``PHL4X`D3D`#`-,>J)I@C)I@*%<`#)I@3)I@S)IA#)IP)&R`#DK.0LY +M&SE+.5N):^)K0H&S`#)K0#)K03)K1#)K13)K2")K3C)K42)K4C)K4S)K5#)K +M5S)K6#)K66)K6V&T`#)K7#)K73)K7U)K@C)K@%&U`#)K@3)KA#)KAC)KBS)K +MC#)KCC)KD3)KDD)KE$%/`#)KEC)KF#)KF?)KFC)KGC)KHO)KIC)KJ.)KPM)K +MP#)KP3)KQ")KQ3)KQC)KQS)KR#)KRC)KR\)KS3)KSC)KTCDD.00Y%#ED.82I +ME#FD.;0YU#GD,F00,F01,F02,F03,F04,F05,F06,F07,F08,F09,F0:,F0; +M,F0<,F0=,F0>,F0?,F0@,F0A,F0B,F0C,F0D,F0E,F0F,F0G,F0H,F0I,F0J +M,F0K,F0L,F0M,F0N,F0O,F0P,F0Q,F0R,F0S,F0TDF0U,F0V,F0W,F0X,F0Y +M,F0Z,F0[,F0\,F0],F0^@F0_8F1",F1`,F1!,F1#(F1$,F1%,F1&(F1',F1( +M,F1),F1*,F1-,F1.4F10,F11,F12,F13,F14,F15,F16,F17,F18,F19'?`V +M00`Q.0`B8UTB(UX=\```-F$!H4X`,3<`03D`8BJ" +MAW@NX;8`:4$`/J:0$*8,!2<9'M)A(,)A'_$_`(&#`+)A'O#W(("&(()A(?)A +M(D8%```=\````#JFD!"F&U4G&?"RH!-7.^K1MP``/:;P(`#`$*:M`67)_\*@ +M`G:`%((CYX)A&?(A&?#P%/)A&>(A&28^`D;X_Z(CK)(D(*"P!`=J$WSNX.H0 +MXF.LP"``TB.LTF$:P"``P/D@\F0@P"``DB0@P"``?-UV@!3R(^?R81OB(1O@ +MX!3B81N"(1LF.`*&^/_0Z1#B9"#`(`"")"""81S`(`",>Z)CK/(CK/)A&I(A +M'Z(A(+%.`-(A(H(A(8)K@O(K@H(A'F)K@N(K@M)KFL(KFG)KFK(KFJ)D2JT! +MDF1,@F1.Y;K_H;8`/?!V@`62(TYW:06&^_\```!V@!?2(^?281W"(1W`P!3" +M81VR(1VRR_T6^^Y&^/\`-D$`(;@`'?`V80"]`0P,#`V"(D^AN0""80!EN@D] +M"K&Y`&6D*0R+#`61N@`I(\+3!J(B):)L)]@!TFPHJ+)"TPZ:D]JJHFPIDF1> +M4DD&HB1>9:$IH;L`+$NJHZ)D4J6@*:T#9>D$XJ#_DM-JL:4`P:8`@;P`\M-@ +M#!J*@Z)(,U)($*&]`-(B)=)O'\)B7[)B8U)I]>)I]N)I]Y&^`-(D7L(D4@PK +M4ET"LDPCHF)ZDF)[#`(=\````#9A`#(B3T*A;`P(B0%*0D"D(&6M_ZT$LJ`` +MY8/_D3<`H3D`P4\`X4X`?/ORH0+R;IC2+IBR;H&R;L&Y'+)L0;)N0;)J@<(I +MK+(J(,#0!`=L$GSO\/P0\FFLP"``@BFLB1'`(``,*("+(()J(,`@`+(J(,`@ +M`':`$/(IY_DAB"&`@!2)(?@A)C\#!OK_`'S?\/L0\FH@P"``@BH@B3'`(`", +M3<)IK((IK/$_`)&:`++3;<(B%L)J2K(K)[)J3))J3H(NFHD!V`&Q5P#PW2#2 +M;IK"+IJH`;"J$*)NFI(NFB6H_QWP`#;!`#$W`&$Y``P)F0'B)AC"(ZRBK_YR +M(D_`T`0';!"@_!#R8ZS`(`""(ZR)$<`@`$%.`(%Z`/(D@H"/(()D@H(D@O)D +M@H(D@H)A`1;=`,)CK,`@`-(CK-D1P"``P;\`X/P%TB=I4J8H6E?PW2#29VG" +M9CR")VF"9CSR!;D]\"9O`I)%N;*A`GS_#`V1P`#AP0"B!;FIH>KGFI>"*7_I +MD9F!&XB":7_21;C21<'21<+2;H3R;H/"))C)`:@!@<(`\3\`L*H@HF28DB28 +MB0'H`<%7`/#N(.)DFM(DFK@!D:8`P+L0LF2:HB2:DF2"@B2"P"``I9C_HJ%L +MJJ*B80OEC?^8@7:`"<(C3LDAN"%W:P5&^_\```#2*8`6'12B(G_EO?^M!R60 +M`!R+?0K(D<`@`.+*Z!:N$_(B?:(L?*)B?(CE#'FHL9"(8X#_(/)B?66+_WSR +MJ+$,"^5A_\%/`")D@2)DP2D<(FQ!(F1!(F:!HB.LDB8@H+`$!VH2?.[@ZA#B +M8ZS`(`#2(ZS90<`@``PNX/D@\F8@P"``DB8@P"``=H`0\B/G^5'84=#0%-E1 +MB%$F.`,&^O\`?-W0V1#29B#`(`"")B")8<`@`(Q+HF.L\B.L#/.2!<(,N`RF +MDLG^D':#AQ=Q''JG%VRRQ^H62PG2Q_8630DF%UV"Q_@,CRT#@"^#)I(XD<,` +M=H`@"YGR)(/8/+(L0Z(D0_#=(-"[(+"J((S:%GD&@@7")B@(!O;_````%HD% +MD@7"DLG^D":#)H(9)I)%-Q(*'!JG$DJRH!2W$@@,`AWP'$)&ZO\,$AWPR)'" +M+'L]\!8\ZZT'9:H!QJK_``P)L-K`#![B;(16?>N9P88%`!P2QM[_``PB'?#B +M1<*E=0E&Y_\,,AWP7$,"K'$`.5@]`P'#!JE?/28H:AQ\J8@@J.X +MBHKZ^I)/P7)H?W)H@')/RG)/R7)/P'G/97(`R)'HP7T*'(T;[M<:`H:(_^G! +M9IZ!AH;_```V@0"X''`,K&\J"`ZN9VJ562*(R2:G^2*(V2:H"2*WJ"R!"BRA`@F1"2;'^2#8"+ +MNXO,\)D0DDYXD@V`DDZ(DBB*DFI]DBB+DFI^DBMY*]TK[B"9$))L?I(-?_"9 +M$)).=Y(-?Y).A[AAB%&B(ZRB:X*2)R62:'V"*(!2U@V2H&06"%N"(S@6*%P+ +MR!9L7M+(_A9]78(E)0P>\J^`\D@]XF9IDJ/H\J#[R&&")26XD[)L<:(#\ZF( +MX@/VXD@\HB4ELJ#]T@/T@@HXV9K"`_7)JL(C.@R-@(!DT,P0P,,$D,P1P(@@ +M@DHXPB4EXB,Z#$VB##G0[A#@X@3P[A&PJA#@JB"B3#GB(SK")24,*(#N$*(, +M.>#A!.#N$?"J$."J(*),.<(E)9J3\BDR\FQ^XBDSHAEHXFQ_]BH3\J"$H/^" +M^OSRWP*B7P#B&6GB7P'RH_`,""(E)4(9:L+6"M(LY:(9>N(9BJ)B>X`D;Y_](LY9(M +M=`P(K`D,"OJ3(B4E=H`3&XBJXD(9AT)N>N(M=$NJ*YGGN`-&^?\`PBSEDBQU +M#`BL*?J3#`K2)25V@!,;B*I-\AF7\F1\XBQU2ZHKF>>X!4;Y_P````P"/`KB +M)28,#)(#_,E!PJ#?F2_\`'T``[J'B8D+88\ASLB,ZT-#T +MP*2TT-1!TE+:P,#T0+L0L+)!P,1!PE+:)H +M=[)H>.)=%L(2V[(EGJ(C'`O,PET7HEL"H@/PHF)*LB,Z@J96BH:PL`2R2(DF +M&@NRROX62R'"ROT6/!_2$N'B$N#0T4'@X4'B4N+24N/R)2;R#TP,'@P-\/$$ +M\-Z#TDB(PB,[P,`$PDB&L@/Z`+LCLF)%H@/[/#T`JB.B8D;R(XKR8C3B(XOB +M8C6R(SG")26H4="[0[),/J(J@"P/O&JRH+^B(SKB)25,#,"J$-(..:"F!*"J +M$;#=$*#=(-)..;(E)<(C.J(+.?#,$,#%!)"J$,"J(*)+.0P,TB=^,4X`HJ%L +M%KT(JB>B)27B"CWBSKX6_A>R"CXT#-((B8QMZ&'B+HX6_A8,':',`+'+ +M`$&E`/@FT.!TH.X1TDB6@@<'<@<&#!TPB!%`=Q&`=R""TP1P[B!`[B`, +MYT%/`.)O9>)O9.)O7O'-`-)D6,)D4\'*`,)D2+)C@*)CP`P+K0*9"')D0/)D +M.&7F_AWP``"X4;(K@%:[]M(%K:HGB7%6/?9")T^B)W\,#N)A`*4\_Z*@`248 +M]*%?`"7Y\R"B(('.`()C@O(C@O)A`*4(_X$W`*(H3JD1F!%WZ?7`(`"BH`#! +MMP""H"!VJ!H`/*:`$*:)`8@!F`&@L$0`&T"0B(&)`8@!JJBI(:T'Y5C_S00, +M"K'$`&7T\PP:91#TB''ASP#8(9*@_L*@`.<=#D'0`$<="+'1`+"MP%:Z[.AA +M#!W2;HY&L/\``/(E)9)//0:9_J(8-K(8-;)8-Z)8.(:#_](E)4PLPDT]AI+^ +M`.(8-?(8-O)8..#A0>)8-P9\_P``@B4EDD@]!HO^HB4E3-F22CU&B/X,#4:D +M_PPM!J/_```V80!\_U'2``P*,J4X0M(&D@3A.C*B8T9:4O)E(XS)@LG]%@@2 +M)FD$'"(=\``,-L'3`)'4`+'5`'%.``P=NK+B*[OI$>(GA@P:#`B0[A#@YI7@ +MBH,6J`D,AH(E'B8(&*(C()(3OANJIQD"!B``@B,?XA/!&XB'GG62(T86Z0[* +MHJ(J?])$X/)D%Q:Z"J(K1+T"Y68`HD3ADB4C()F@DMD&N#F,^PP*HFD5LB)C +M&[NR8F,M!AWPLB)C&[NR8F,M!AWPXBM$X@XXX.4$%FX3@B4>&X@6B!RB(R"2 +M$[X;JJ>9#8(C'^(3P1N(@.[`%OX:'((=\+D!K0*E,02X`<'3``P=?/\+FA9) +M%>($ZMRN)BH8J!&B"@#AU@`<6(6:BT&'?"")2.#"YFH.((H0V(G0^"J(*"(((!F(!9V"Q9Y +M"Z($ZF8JVN($ZA8.#`PV8D3A#*8,"()$XQR)EQ:4H@3I/?`6"@CB!.I6?O2A +M1@#BH`#B1.F2)X&"H`B@F1"0FT&0:)."QOX6R.NB!.&2ROL6N>0+ZA9NY)'4 +M`(+*_5:(XH:._P``TD3@##8&C/^M`N4F!+@!P=,`#!U\_PP)DF4C!N7_X@3+ +MHJ`@YSHB(*(@)0L`H&H@N`'!TP`,'7S_AMW_'';&??^"!.I6B/?&Y/\,X,HAWP```` +M5HKZ#"S"2^JEN`@,+\;F_X(+ZE:H]88(``P8@DO@@@O2XDOIXDOJ%J@4XM(! +MX@Z=%AX4XBV*XL[^XFU_@@O2JJ(6N!0,/C(JCH(*?>)+ZXES@BV>@F,#@@O3 +M@D,A,@O3%H,`@BJ.X@O4XD@BXBJ.@BV7B4Z""]:"3B#B"]:,?H(JCN(MF.)H +M!N(J(>B>K)YF'DGB)((Y88*D-#(JCB#NL(KNXBYLZ1/B)((@[K"*[N(N;>DC +M!@D`````.6'B)(*"I#0R*HX@[J"*[N(NC.E3XB2"(.Z@BN[B+FKI`V8G%.(M +M8((M8:':`(#N$>"((*"((()I3(@B@B@Z!^ACH<,`"ZJ"+(,H.>(I0](L0X`B +M("#N(.#=(!9]!A9Z!M(+ZJ+*_V8MW)(EYYDQB#&`@!2),>@Q9C[NTB7=V4'( +M07S:#`OPS"#"9=V&``"W-@OR)=WY0>A!&[LW;O"(0:"($()EW;(EY[E1J%&@ +MH!2I49A19CGN+0<=\.(MB@ONQJ[_````5@KZ#"_R2^JEH`@,+\;D_^(JF.#@ +M=`:K_P```#9!``P+D=L`%H0`)A0I)B0Z)C1'P*,1JJ*:JI(*?^8I`M89`28I +M828Y"R9)/V99!<(*@!;[`K"K(*>< +M`QWP``#2`BLJW3)-"*)"*QWP````-D$`#!."`BM"`BH,`H!$P$`C@QWP```` +M-H$`8M-MXB8R#`J2I?PP[J":[JE.V/*R)C(,'-+-_CJ[FKO0K(.B2]Z"`CF: +MDYDQ!V@(,*,@L@G/)>G_R#'"#.1F'`OB(0/2H`#23N2ED/ZR`CA1W@!!WP"P +M\006#S*"TQ2"*"^RTPT62#B2*R62"3F0E@06B3?2)C*RH-2PW8+"H`#0TX#2 +MW0[";1RB)C*PJH*QX`"JH[JJY8@!HB8RLJ#4L*J"L>``JJ.PJH"ED`'B)C*B +MH-2@[H+1-0#JX^+>#=)N*,(".#=L&9(F,@P8H)F"#`^:DY+9#?)I*8)"-?EB +M!@4`TB8R#`N@W8)\_-K3TMT-PFTILD(UF/(+Z18^*O+)_A;?*0Q8B2&R)C*B +M)C4PNZ"RVVVB:S:R`CBPD016J0C"`CD'[`)&(`!':P+&'@#2)C+BH-3@W8+: +MT]+=#?(-J^(-K-(-JO#NP.>=60P-O`9R(TJ#4T)F"FI-*F9()*1N[ESL"!MC_/?#&[__-"^T/AO?_LB8R +MPJ#4P+N"L+.`HML-H@JI,,,@L*J@O0*BV@VB*BPE=@'8\A8]#J(F,K*@U+"J +M@JJC@MH-@@BIDMH.H(B@@M@-@B@L@FD<\B8R#!XZ__+?;>)/X-(F,K*@U+#= +M@MK3TMT-P@VI&\S"3:FB)C)!X`"PJH*JHTJJ)5X!HB8RLJ#4L*J"JJ-*JB5F +M`<(F,M*@U-#,@LK#PMP-X@RKT@RJP@RLZMW7K&#X,2@A#`[B3^T=\`""`CF` +M@`06^-:PE`06F=:M`KT#)0<"QE?_````K0,EW0%&JO^B)C(ZNK+;;;(+X)R[ +MLJ#4L*J"JJ.BV@ZB*AP6N@#"R?\6O!72R?X6'18,;NDA!DW_*"$=\((F,I*@ +MU)"(@@P/BH."V`[R:!R&S/\`K0.Y`2E1Y4$!\B8RLJ#4L/^"#`[Z\_+?#N)O +M'-(F,K#=@MK3TMT-XDVJPB8RL,R"$`H**`H*JP,*J@L*J`970#DB8RPB$$ +MTJ#4T)F"FI.:G%J9HFEO@B8RT(B"#`Z*@XJ,6HCB:'_R)C+0_X+Z\_K\6O_B +M;X^R)_+0NX(;(KJS2KNR"RA+S,E!MS*6*%$,"\'B`/'C`(*D1'*D/'ISBH.) +M$?KSJ`$,2#J;RIEVJ#*"*I^2R2"":7>"*I^":7B"*I^":7F"*I^":7J"*I^" +M:7N"*I^":7R"*I^":7V"*I^":7ZH$>)G?]*@C-J[VO]+=Z>7L(;8_B"B(#"S +M("4X`J`J(!WP``#B"A/BSOY6KND&!@#R)C*"H-2`_X+Z\_+?#O(O'/(/$PO_ +M5D_HD@(Y!^D"1B$`HB8RLJ#4L*J"JJ.BV@ZB*ARB"A862N:B)C+-`[*@U+"J +M@KT"JJ.BV@ZB*APE30&B)C*RH-2PJH*QX`"JH[JJ93D!HB8RLJ#4L*J"L>`` +MJJ.PJH`E00$B)C*"H-2`(H(,#"HC(M(.PF(<\B8R?/XP_Z#RWVWB;S;2)C(, +M4CK=TMUMPDW@'?"2)C*BH-2@F8*:DY+9#I(I')()%E;IW8;=_P``-J$`8M-M +MXB8R#`J2I?PP[J":[JE.V/*R)C(,'-+-_CJ[FKO0K(.B2]Z"`CF:DYE1!V@( +M,*,@L@G/Y8G_R%'"#.1F'`GH40P-TD[DI3'^?/>2`CA1W@!!WP"0\006#S:" +MTQ2"*"^RTPT6*#F2*R62"3D,#I"6!!9)./(F,J*@U*#_@OKS\M\.XF\;7)N +M-D8/``"B)C(ZBH+8;8((X)RXLJ#4L*J"JJ.BV@ZB*AP6N@#"R?\6_$O2R?X6 +MG4/B)C7R)C(,:(E!,/^@\M]MXF\VD@(XX@(YD*$$5FH2X+`$%@L2D,0$5JP1 +MTB8R\J#4\-V"VM/2W0V"#:OR#:C2#:IRTVJ`_\#PWH`@:N`)A1D@GM%JD4LB8RPJ#4P+N"NK.RVPVB"ZD+JJ)+J=(G +M\N*@U.#=@MK32MWR#2G2#2CB`CGWG6<'[L7R)_*"H-2`_X+Z\TK_\@\ILJ`` +M%H\F#`KA-`#"K_]V@#?2)C+RH-3PW8+:T]K:6MW2+6^2)_+X+4NJ@@T2YZ\! +MG)C2H-30F8*:DTJ9D@DI&[N7.P)&V/\]\$;P_P#-"^T/1O?_`.#P!!9?"[(F +M,L*@U,"[@K"S@*+;#:(*J3##(+"JH""R(*+:#:(J+.40`=CR%DT=HB8RLJ#4 +ML*J"JJ."V@V""*F2V@Z@B*""V`V"*"R":1SR)C(,'CK_\M]MXD_@\B8RTJ#4 +MT/^"^O/RWPWB#ZD;[N)/J<(F,M#,@LK#PMP-X@RKT@RJP@RLZMW7K#Z(40P" +M(DCM*$$=\```H@(YH*`$%FK0D+0$%@O0K0*]`^6C`88]_P```*T#Y7D!AK#_ +M(*(@,+,@Y>L!H"H@'?`H01WP#`S"0C4&F/^M`[EQ(F$(I>$`@B8R\J#4\(B" +M#`Z*@X+8#N)H''(F,O!W@GISJ(B8R\"*"X<0P/P>,` +M@J0\TJ1$VM.*@XD1V0'*PZ'B``Q(.I^JF7:H,H(KGY+)(()I=X(KGX)I>((K +MGX)I>8(KGX)I>H(KGX)I>X(KGX)I?((KGX)I?8(KGX)I?H@!V!&2H(R:_YK, +MXFU_2]W9$8>=JP;3_K(F,L*@U,"[@@P*NK.RVPZB:QQ&D/\@HB`PLR#EU0&@ +M*B`=\-(F,N*@U.#=@M#3@-+=#M(M'-(-$]+-_U:MNO(".0=O:H(F,I*@U)"( +M@H"#@(+8#H(H'(((%A;(N*(F,LT#LJ#4L*J"O0*JHZ+:#J(J'.7K`/(F,B*@ +MU"#_@@P,^O/RWP[";QSB)C(P[J#BWFUR;C;2)C(,4CK=TMUMPDW@'?``@@H3 +M@LC^5FBSAN/_DB8RHJ#4H)F"FI.2V0Z2*1R2"196*;*&Y/\``#:A`,'D`)*@ +MU.'E`#+2:O(C\NKBZ2&0_X+B+G_Z\LK_XD^!TB/RD-V"#`7:TLK=4DU_LB/R +MD+N"NK+*NU)+@((C\D'?`)"(@HJ"2HB"""D,)EDQ%O@-#`?2(_*RH-2PW8+: +MTMK7TMT-4FT\PB/RL,R"RL+*Q\+<#EG,HB/RL*J"JJ*JIZ+:#:(J+&4:`:(C +M\K*@U+"J@JJBJJ>BV@VB*BR2"A,':17H>M(..?*@_O#=$-)..(C\DMW\J#4\.Z" +MV#'JXDKNX@XI&]W9,>>]`L;6_X8-``"8>H().<*@_L"($()).?AZ4D\U^(KB +M#SG`[A#B3SG8BE)--;AJD@LYP)D0DDLYB&I22#5&Z/\,!\')`.'(`!P)F5'J +MXLK"R6$&!0```!MW^%&(88ON"_]+B(EA^5$6SR"AQP`JMZJ[D@N(TJ#_UQG< +M\B/R@J#4@/^"^O)*__(/*0P,%G_\#`JY`7:`I+(C\M*@U-"[@KJRNKJRVPVR +M*RR2"Q/8>P=I);(N?_@-MQ\8C%L,"P8&````@BZ`5BC_N&&8?;(K?[>9Z`P; +M1@``#`O2(_+RH-3PW8+:TMK:TMT-TBTL\@T3%V\>^(W2+H"(#]<8$=P=DBY_ +MS,F(8=(O!X(H?X>=`F"[(-(C\O*@U/#=@MK2VOKRWPWR+RR2#Q,;S$J-MQD/ +M@@@I2ZJ'/`)&Q_\]\`;5_P=K:-A_P@TY@J#^@,P0#!B`S"#"33G2(_*2H-20 +MW8+:TMK:TMT-TBTLP@T4F`&`S"#"3122"7B\&8(C\M*@U-"(@HJ"BHJ"V`V" +M*"SR"!4,&9#_(/)(%<(C\M#,@LK"RLK"W`W"+"S(?)),-1=K=8(C\K*@U+"( +M@HJ"BHJ"V`V"*"R(B/((.9*@_I#_$`P9D/\@\D@YTB/RL-V"VM+:VM+=#=(M +M+,(-%)@!8,P@PDT4D@EXK.GR(_*P_X+Z\OKZ\M\-\B\LT@\58-T@TD\5PB/R +ML,R"RL+*RL+<#<(L+,B,#!F23#62(_+2H-30F8*:DIJ:DMD-DBDL@@D4@LC] +M5GCAB&GR"#FRH/ZP_Q`,&[#_(/)(.<(C\M#,@LK"RLK"W`W"+"SXC+A\\@\U +ML@LUR&SPNQ"R3#7&=__B(_+RH-3P[H+QX`#JXOKNT@X)#`P6_4P,!ZT.Z4%V +M@#"X2AO,D@L32ZKX:R8Y)P=I#/A[@@\Y!V@$@@\UK"@7:0R8B_().8()-0=O +M`9PHU[PA!O+_``""#SD':-&"#S56N/R801MWLFD4T@X)2YF908;V_Z(."'). +M"J>W%>"GH':`#%)J%+(."!MW2ZJWMP(&^__B(_+RH-3P[H+QX`#JXOKNT@X) +M#`P6'40,#ZT.XF$(=H`PN$H;S)(+$TNJ>&LF.2<':31X>X('.0=H+(('-3WP +MK$B8@1O_LFDDT@X)2YF9@=>\*`;R_P``@@0`\DX+E[\;X+^@/?!V@`Q2:R3"#@@;_TN[ +MQ[\&H>0`QOG_`+(C\O@ATJ#4T+N"\B]_NK*JNX(+@.(+?_#P=(KN]RX"1N(` +M4DM_XB/RT.Z"ZN*J[E).@,(C\M#,@LK"2LS"#"D,#ND1%DP.#`?2(_*RH-2P +MW8+:TMK7TMT-4FT\PB/RL,R"RL+*Q\+<#EG,HB/RL*J"JJ*JIZ+:#:(J+"71 +M`+(C\L*@U,"[@KJRNK>RVPVR*RR2"Q.H$0=I%OA[X@\Y@J#^@.X0XD\YTBL' +M4DTUD@L3%VD5V(O"#3GBH/[@S!#"33F8BU))-9(+$R8Y)U)+%%)+%?(C\H*@ +MU(#_@DMW^O)*__(/*1NJJ1'WN@*&UO^AY`#�#8>\(-.>*@_N#,$,)-.9A[ +M4DDUF(N""3G@B!""23GXBU)/-=AKP@TYX,P0PDTYF&M2235&Z/\``.(C\O*@ +MU/#N@M@AZN*J[O(.@.(.?](M?_KNX-W`V7$671-QY@`,"(F1!BP`D34`D)W` +M%LD)P*^@>JJB*G^R"A/@GQ$':RVX>H(+.0P=PJ#^P(@0T(@@@DLYPB/RX,R" +MP,*`P,^@<,R`PBQ_L@P4T+L@LDP4HB/RPJ#4P*J"JJ*JJ7JJHBI_T@H3%VTK +M^(KB#SF"H/Z`[A`,&(#N(.)/.=(C\L#=@MK2VMEPW8#2+7^R#11@NR"R312B +M(_*RH-2PJH*JHJ"9@'"9@)(I?Z()%"8Z;M`9RXPB/RXJ#4X,R"RL)*G)()*1N[ESL"QKW_/?`&\/_=#OT+QO;_ +MZ&G2#CGRH/[PW1`,'_#=(-)..<;>_P``TB/RXJ#4X-V"X>``VM+JW<(-"0P+ +M#`X6;`6M#=!M(':`,$A*&[N2!!-+JOAD)CDG!VD,^'2"#SD':`2"#S6L*!=I +M#)B$\@DY@@DU!V\!G"C'NQT&\O\``((/.0=HT8(/-5:X_!ON0F842V;"#0F& +M]_^2#0CB30J7OA70KJ!V@`Q2:A2R#0@;[DNJM[X"!OO_XB/R\J#4\.Z"\>`` +MZN+Z[M(."0P,#`\6W06M#N`N(':`++A*&\R2"Q-+JGAK)CDC!VDP>'N"!SD' +M:"B"!S4]\*P(&_^R8B1+(M(."=>\*`;S_P``@@#+@\)-WH(D\I*@U)"(@G'@`(J#@M@-P@BKD@BL@@BJ +M4=\`P)G`EQ@"ABX`#`W"H`#AY@!V@!<``0,``!,"P(\"T`,``1,`(````@," +MP`&B)/*RH-2PJH*JHZJLZJJB*G^2H-0;W8(*%$O,\@H5%A@`G&^")/*0B(** +M@UJ(@@@IUS@"1I4`1A<````0$2#ED0#R)/*"H-2`_X+Z\WK_X@\)#`LI`18N +M+PP-+0^M#W:`%P`!`P``$P+`00+0`@`!$P`@```"`P+``7A*D@<3&[M+JL+) +M_5;L(,AG@@PY!^@"QH``QGX``-(D\N*@U.#=@MK3TMT-X@VIT@VHYYT(,*,@ +M$!$@90X!\B3R@J#4@/^"^O-:_X(/*?(/*(>?27*E_'!S@(('[;*@U*SHHB3R +ML*J"JJ.BV@V2"JD+F9)*J<(D\M*@U-#,@LK#6LS2#"G"#"C7',YQX``&`P`` +MK0,0$2#EN0#&]?\`LB3RXJ#4X+N"NK.BVPW""JFPS*#"W`W"+"PI;*(*J;"J +MH*+:#:(J+'SYDDH1@B3RX(B"BH/RV`WR#ZF`_Z#RWPWR+RR(;_(/$?)(--(D +M\N#=@MK3PMT-P@RIT,R@PMP-PBPL##_R3!.R)/+@NX*ZLZ+;#:(*J;"JH*+: +M#:(J+/)*%)(D\N"9@IJ3@MD-@@BID(B@@M@-@B@L\D@6TB3RX-V"VM.BW0VB +M"JD,'+T#T*J@HMH-HBHL$!$@94\`DB3R@J#4@)F"FI/RV0WR#ZF0_Z#RWPWR +M+RSH0ND_TB3R@-V"VM/"W0W"#*G0S*#"W`W"+"RR`C:R3!*B)/*`JH*JHZ+: +M#9(*J1N9DDJI\B3R@/^"^O-Z_^(/"0P+%@X1#`VM#UT/=H`P*$H;NY("$TNJ +MR&(F.2<':0S([70;R_P`` +M@@PY!VC1@@PU5KC\&]TB911+5>(/"8;W_SWPAE3_`((,-:Q(!VD,R'>"##D' +M:`2"##6<2!=I#)B'P@DY@@DU!VP!C$CGNUX&:/\;W7)B%$LBX@\)!OO_``"2 +M#PC23PJ7O17PK:!V@`QB:A2R#P@;W4NJM[T"!OO_PB3RTJ#4T,R"RL/"W`WB +M#*O2#*K"#*SJW=\J7\^O/B3^T=\"@!<>``@@\(TD\*AST" +M!EO_\*V@=H`18FH4D@\(&]U+JI<]`H95_SWPQOG_#`U&X/\`#`U&\_\````V +M80""TFJ)`8(H\F'?`)*@U)"(@I'&`(J":HB"""D,!)!R@!:8"5'F`#*@`*(G +M?[*@U+"J@JJBJJ-0JH"B*G^E7`#(`<(L\M*@U-#,@LK":LS"#"E+,QM$QS30 +MK0*EW@!2I?R,:EHRT@/M%GT%XB=_0><`\J#4\.Z"ZN)*[N(.@5`R@!9>`((# +M[1;(!)(G?_*@U+*@U+"I@M$U`*JB2JJB*B(@R:!:S!9:`,(L!!;,!.(G?_#N +M@NKB2N[2;AX=\`":'?`V00""`@D,!Q;H!PP*(%(@(+(@=H`P:$4;=T(&$TM5F&8F-"<'9`R8 +M=C().<()-0=C`:PL%V0,2(;2!#GB!#4';0&<+H>W'0;R_P``\@DY,@DU!V_. +M5K/\&ZIB:Q1+NX(""8;W_T(""*)""D>Z&6T*(%J@#`=V@`QR912"`@@;9DM5 +MA[8"!OO_'?`,"D;U_P`V00""`@D,!Q9H"`P)(%(@(*(@=H`L:$4;=T(&$TM5 +MN&8F-",'9#"X=C(+.<(+-3WP!V,CK`P;F6)J)$NJ@@()A[ACZ1*"R?T6 +M&`JX`[DBJ$.I,I(#-I)"$AWPDJ`#,F(&HD,TDD(3P@,Y!VP.DD(6DD(4T@,U +M%BT`DD(5O02M`@P,90D`F(*(/DRX@,VXD(2'?```#ERHD,TD@(3 +MT@(6L)D@D)!TDD(3P@,YL-T@!VP@P@(4\@(5TD(6L,P@PD(4H@,UD@(3L/\@ +MC%KR0A7H8^D2)CDFJ`.I(IA#F3*"`S:"0A(=\``@HB!`M"`E'0#"(P3),K(# +M-K)"$AWP0+0@(*(@I1L`XB,$Z3+2`S;20A(=\#9!`.(B!E*F4%!3@+(>$L(> +M$](>%.(>%18$%8(%@/*@6(#_@H'H``P:^O.*_Z4``JERL@6`0>H`7(^POX*! +MZ0"ZLTJ[H@M4#$=BH/M@JA!PJB"B2U3H8@PJD@6`LAX2PAX3D/^"TAX4^O/B +M'A6*_Z7\`:F"X@6`7(W@W8+:TTK=P@VL8,P0<,P@PDVLJ(*(8KARZ`CI(L@H +MV#CB"#28&)D+R0K9._@X^3KB2S32"#322C3)*LDKF1J9&Y(*.8((.>*@_N"9 +M$("`!)"((()*.8AR^(*2"#GR#SG@F1#P\`20_R#R2#G88LB"@@TU@DPU^'+8 +M;8)/-=ELV6_9$K(,.9*@_0PMD+L0T+L@LDPY^'*(@M(/.8((.9#=$("!!/"( +M$8#=(-)/.=B"R&+R#3C"##C@_Q#`P`3PS"#"33C(D! +MJ8*&N/\`-D$`#`I![`!ARF(*(#K@)J6*PB$,,"XDZB0J)(O@.V`F)/HDY^1KY +M&=DJV2[R"CG"#CF"H/Z`_Q`';`?""3D';`$,&[#@!/#N(.)*.;AR#!S2"S4, +M"IB"C$W2"370K)/(8O*@_:#@=.),-:Q>X@PY@@DTV!*2;!&R;!""3#3P[A#B +M3#FH@MEL^&+R:A*(B"T@XY8-T0TDXYR()23#7(8K(,.6"[$+),.:AB4DHU +M4D(44D(5'?`V@0"M`H+3:HDA@BCRDJ#4(=\`D(B"BH,JB(((*0P$%A@E#`5A +M[0!QQ@""IFF*@XD!>G-J8WDQ<>8`AA,``.A!G(_`GZ"($8DYD@P)P-F@V#U\ +M^Y+)_[)-$9),":E1LJ#_MQX+O0ZM`\*@`J7V_:A1R"'"+/+2H-30S(+*PRK, +MP@PI2U4;1,=R-@JR"O7 +MG,&2"Q,':1>8>X().<*@_L"($()).?A[#`[B3S62"Q,7:1B(B_((.9*@_I#_ +M$/)(.>B+TJ``TDXUD@L39CDTF'OR"3F"H/Z`_Q#R23GH>PP,PDXUZ(O2#CF` +MW1#23CF8B\))-?AKX@\Y@.X0XD\YV&O"334,#,)+%<)+%+@QLBM_PJ#4P+N" +MNK.ZM7J[LBM_L@L1J5&"H/^'&PBM`PP\I>C]J%'(,8(L?]*@U-"(@HJ#BH5Z +MB((H?PP?\D@2XBQ_\>T`T.Z"ZN/Z[D)N1,(L?]#,@M'@`,K#VLP6;.Z2S!`6 +M">[*U;A-X@L1\J#_]QY_@@L3C/B")KN):_(FN_)K!Y(FNY)K".E!#`_R2Q/R +M2Q7R2Q3R2Q;83?(,"=D1#`T+GY9S_ +MB(D;W4KMK#CR#`D+GY<^`L:0_SWP1O7_`/(FN_EKAO;_F$Z")KN)>P;U_P`` +MDB:[F8N&]/^8:XQY^'N,/XB+5N@`DB:[F6N")KN)>_(FN_F+@@DY\J#[\(@0 +M@DDYB'LI82((.?`B$")(.8B+(@@Y\"(0(D@Y^&OR#SDH8?#S!%8O\[@!#!F2 +M2X`&?_\`'?```#9!`'+2:H(G\M'?`**@U*"(@I'&`(J"VHB"""G!X``,"Q;X +M$YI2#`I!-`!BK_\QY@!V@#+B)7_RH-3P[H+JXNKJ.N[B+G^2)_+X+DNJ@@X2 +M1Z\!G#CBH-3@F8*:DMJ9D@DI&[N7NPF&\?]-#VT+QOC_&Z86^@ZR)7_@NX*Z +MLK"VH#J[LBM_TJ#_D@L3H>T`X'81]CDGZ'NXBPOY\+Z#S#NJLK(KN[(+--<; +M*B"B(,*@`Z7*_:'M`,'@``8&`.AKX@XTUQX0L@L1(*(@##SER/VA[0#!X`#R +M)7^RH-2P_X+Z\OKW.O_R+W\,'N)/$M(E?[#=@MK2JMU";422)7^PF8*:DIJ7 +M,)F`DBE_\@D4LJ9;W+^B"1,F.D`':@B"*0>""#D'Z`H7:BRHB:(*.0=J)+JB +MT@J/G*W2)7^R"G#BH-2M!N#=@NT"VM+*W;#+(&4#`!WP'?"ZH@;W__AI\@\Y +M!V^VAO+_FE*BH`#2)7_BH-2RH`#@W8(@XB#:TLK=#`QE```=\``V00`6-0N" +MQ1`6V`I0XJ"H3K(*$='?`#*@_S";P!:Y"<(*$XS\VO:2+W^9:H(O?XEZ\B]_ +M^8H,#,)*$\)*%<)*%,)*%D(%"?A."X2'LD7:YMT"=H`>4-V@F%V(:9E-G&B( +M>9RHV(D;S*P-0@4)*MP+A(>]'X;V_P""+G^):L;W_YA-@BY_B7I&]O\``)(N +M?YF*1O7_G%10Y*#Y/J(%"5#:H-@]?/P+JL)-$:)%"3<;":T&PJ`");+]'?`= +M\)AJC&GX>HPOB(K,^-K&DBQ_F6KR+'_Y>L(L?\F*P@DY\J#[\,P0PDDYF'J" +M"3GPB!""23G(BI(,.?"9$)),.8AJ@@@Y#!F`@P16>/'2U@:23>D=\#9A`"DA +M(<8`#`K8(2HC@B)_DJ7\TBT3,(B@FHBI2!9-!D'?`(+3:IIS>1&),7'>`,8. +M`+(A`L(B?]*@U*(A`-#,@M'@`*(J`,K#VLS=`V69`*(B?[*@U+"J@K'@`*JC +MNJHE3/_8`<@AV%W2;!.<#:A-\>X`V0'V>NKP^J#X#Z`/`)*E_`P*@B)_,(B@ +MFHB(2*QHF"'2H^3:TZEYJ4FI":)MAI(I#Q:Y%^+)_Q;>&?+)_A:_%3"C(*4U +M_QWPJ"&H^K@M%DH5#!S8(0P._0/83>59`*@AF`$,&[)*-9@IF6KR(G^"H-38 +M$8#_@H'D``P<^O.*_^(/@8(/@/(/?PP9X.F#BO_W+@*&UO_"3>T&U?_H,>(N +M\O*@U/#N@NKC2N[B#BH,!E*@`*S^HB)_LJ#4L*J"JJ.JI7JJHBI_Y8W_R#'" +M+/+2H-30S(+*PT#,@,(,*E+%!&+&`<``JJ.ZJB4V_Z(B +M?[*@U+"J@K'@`*JCNJKE/?\&HO^R(0+"(G_2H-2B(0#0S(+1X`"B*@'*P]K, +MW0.EV`"B(G^RH-2PJH*QX`"JH[JJI3K_!I7_`,@AHFUZJ2Q&IO^M"[T#934` +M1JO_LBUZDBUYHFU]^"&PR4/`N\#`F<"2;7FR;7KH#YD?@BUZB2_I/\:9_[@A +MHFUYJ1M&E_\``#9A`-+2:H(M\N'?`)*@U)"(@HJ"ZHB"""D,#!:X!3'&``P+ +MH>8`,#*`=H`7``$#```3`L!(`M`!``$3`"````(#`L`!DB-_\J#4\)F"FI*: +MFZJ9DBE_\@D2@@D4C`^U&%0`] +M\$:F_P"<&.B)&]V<;O(*"M`L*@`B5I_5<4!`P" +M'?``DB-_HJ7\()F@JIF821:I_@P2'?`V00`,"'("*@P)=I06>E)2!0@;1RPF +M-Q4.?0D;B&>T`7T$/?`,$AWP#`(=\#9!`$("*C("*T`CP$>S!"+"(AWP'?`` +M`#9!`)CRPJ#_4>\`%GD*0?``8J97#!I*(R89&&8I)9(B'3J9:IF""74,*["( +M(())=08$``#2(ATZW6K=L@UUH+L@LDUUDB(=.IEJZ>(.=5IS9CY9LB=EL@LT +MQQL4K0/"H`+E7/VR)V6M`PP\L@LT)5S]LB(=#`\ZNVJ[\DMUHB(=.JI*JO)* +MC((B'9*@U)"(@FK#BH-:B/)HFN(B'7S]#%(P[J!*[M)N(2),BAWP#&)*R:), +MC!WP6G.R)V6R"S3'&Q.M`PPLI5;]LB=EK0,,/+(+-.55_0Q2'?`````V80"] +M`B+3:H(B\G'?`)*@U)"(@@P%BH-ZB(((*PP$\J#^%N@,#`UA\0#ATP#!Q@"" +MH-3JX\K#1B$```"2"A.(>@=I$)((.?"9$))(.8AZTD@UD@H3%VD3B(J2"#GP +MF1"22#F"*@C22#62"A-F.3"(>I((.?"9$))(.8AZTD@UB(J2"#GPF1"22#F( +MBM)(-8(J!I((.?"9$))(.8AJTD@UTDH4TDH5HB+R@J#4@*J"2T2JHWJJH@HK +M&U6"H-2GM3.B+'^`JH*JHZJD:JJB*G^8&K>9T9(N?[DA5OGUZ0')$;T#Y4G_ +MN"'($0P-Z`'RH/Z&[/\`'?```#:!`&D!22&]`ED1.5%2UVKR)?(,`R*@U"#_ +M@B'?`$A1^O? +MSB8;1V8KR,(E\M*@U-#,@LK'RJ:BV@ZHRI(*%>+)_18^$>+)_A:>$O@AD?(` +M%H\9FIR2*7\661>@R@\]QZ4Q@T`B#&"*'^Y0JIJ+:#JC* +M)3/_N$&&QO\`R#'"+'^Y0Z"*'^Y0JIJ+:#JC*Y2#_N$&&C_^B)?*R +MH-2PJH*]!ZJGJJ:BV@ZHRN4F_[A!AHC_`,@QPBQ_N4',C+T'I27_N$&&@_]E +M'?^X08:!__@!B#'8.AOO\.^SX.$AX-W`%BW?@BA_N4',N+T'Y2+_N$%&>/\` +M``!E&O^X089U_QWP`#9A`,'&``P$LM-JRL/2+']Q\P"2H-20W8(+HMK3>MVB +M;7^"*_+RH/YAWP"0B()1\0"*@VJ(@@@KX=,`#`(6.`SJXPP-1B```)(*$XAZ +M!VD0D@@Y\)D0DD@YB'K22#62"A,7:1.(BI((.?"9$))(.8(J"-)(-9(*$V8Y +M,(AZD@@Y\)D0DD@YB'K22#6(BI((.?"9$))(.8B*TD@U@BH&D@@Y\)D0DD@Y +MB&K22#722A322A6B*_*"H-2`JH*JHVJJH@HK2R(;1*>T.I(L?Z*@U*"9@IJ3 +MFJ):JJ(J?WJ9DBE_B!J'JD!R1&]`V42_\@1#`WH`?*@_K@A +M1NO_'?```#9!`+T$<=,`\J#^8>8`@@0*G0.H>0PCV/GPRA$;S-"LDR"JP`LJ +M=I@LHBL4P@H4K&T';`R""A7H>@?H!)A.)QEK%VP/P@H5F(H7[`?H22#NP!8^ +M'$N['?``9CSW@@H5F&I6^/Z2*00GF>FE_/YZI:(J?U8*_JT$L@0)#`,,!Q9+ +M,':;%[`!`[``$[++ZK+;`[`!$P`@`+`"`[++`\M4,DB_?F6R" +M+]^)?/(OW_)L"`P)DDP3DDP5DDP4DDP6T@0)#`_H2@N-A[,V,",@HM4,0-*@ +MF%V(:9E-S$B"*M^";`:(>S-C`C(*+5 +M#$#2H)A=B&F93B,W![RU0R2+]^9;((OWXE\\B_?\FP( +MX@DY@J#[@.X0XDDYZ'S2#CF`W1#23CGXC.(/.8#N$.)/.=ALT@TYT-,$5JWK +M#!^"IFF*A?)(@!WP(<8`*B6B(G^RH-2PJH*JI:JG:JJB*G^8:I()-+*@_[<9 +M";(*$0P\K07EQ_RR(G^2H-20NX*ZM;JW:KNR*W\,&J)+$H(B?Y"(@HJ%@M@- +M,F@H%D2\PL00%NR[2J?"*@2R#!'2H/_0V\`670GB#!.<'O+5#)(OWYEL@B_? +MB7SR+]_R;`@,"9),$Y),%9),%)),%M($"0P/Z$H+C8>S-C`C(*+5#$#2H)A= +MB&F93(L"#WPW![RU0R2+]^9;((OWXE\\B_?\FP(X@DY@J#[@.X0XDDY +MZ'S2#CF`W1#23CGXC.(/.8#N$.)/.=ALT@TYT-,$5JWQ#!^"IFF*A?)(@!WP +M````-F$`D@0+K031TP"\^0PO8>8`R//BH/X,`W:9,;(J))(+%*RL!VD0@@L5 +M>'L':`B")P4@B,`6.`T7:1"2"Q47:0J8BX(I!2"(P!8X)$NJ'?!F.?B2"Q6( +M:V8Y\(A8)YCKD@L3!VD2R'NB##G@JA"B3#F8>S))-9(+$Q=I$XB+\@@YX/\0 +M\D@YPBL(,DPUD@L39CDPF'N""3G@B!""23GX>S)/-?B+P@\YX,P0PD\YJ(LR +M2C6H:Y(*.>"9$))*.8(K!C)(-3)+%#)+%=J5DBE_5KGWK03"!`D,!PP+%GPS +M=IP7P`$#P``3PLP=PMP#P`$3`"``P`(#PLP!V$K8;=A=2ZH@W"($()'.<(J),A\,DPUPBHDL@P4\+L0LDP4LBHDD@L5\)D0DDL5DBHD +M@@D39C@2B&G""#G@S!#"2#FR*B2X:S)+-=KEXBY_5D[OLBHDD@L3\)D0DDL3 +M@BHD@@@35OCMK03"!`D,!PP+=IP.V$K8?=A=2ZHG'08;=TN[X+<1(<8`*B6B +M(G_"H-3`JH*JI:JK:JJB*G_H>N(.-+DA\J#_]QX+L@H1##RM!269_+@APB)_ +MDJ#4D,R"RL7*RVK,PBQ_#!JB3!*"(G^0B(**A8+8#7)H*!:4YM+$$!8]YD"K +M@,(J!+(,$>*@_^#KP!;^(?(,$YP?@M4,TBC?V6R2*-^9?((HWX)L"#),$S), +M%3),%#),%M($"0P/Z$H+C8>W-"T'HM4,0-*@F%V(:9E-S#B"*M^);(AYS%B8 +M38(JWXE\V(D;__HGS#V"*M^)C-($"0N=ES+/G'U`C:#I.*($"4#ZH/@_PJ__ +MHLK_PD\1HD0)DJ#_D)O`%HG"[(*$0P\K07E@/RX$<(B?Y*@U)#,@LK%RLMJS,(L?PP: +MHDP2@B)_D(B"BH6"V`UR:"@65,[2Q!`6_B,W"[RU0R2+]^9;((OWX)L +M!_(OW_)L".().8*@^X#N$.)).>A\T@XY@-T0TDXY^(SB#SF`[A#B3SG8;-(- +M.=#3!%8MV0P?@J9IBH7R2(`=\)ALC&G8?(PMZ(S<+O+5#)(OWYEL@B_?@FP' +M\B_?\FP(X@DY@J#[@.X0XDDYZ'S2#CF`W1#23CGXC.(/.8#N$.)/.=ALT@TY +MT-,$5MWK#!^"IFF*A?)(@!WP(<8`*B6B(G_"H-3`JH*JI:JK:JJB*G^8:I() +M-+D!PJ#_QQD-L@H1PJ`#4*4@Y63\N`'"(G^2H-20S(+*QBU0Q`TJ"878AIF4W,.((JWXELB'G,6)A-@BK?B7S8B1O_^B?,/8(JWXF, +MT@0)"YV7,L^(L"#WPW![RU0R2+]^9;((OWXE\\B_?\FP(X@DY +M@J#[@.X0XDDYZ'S2#CF`W1#23CGXC.(/.8#N$.)/.=ALT@TYT-,$5NWQ#!^" +MIFF*A?)(@!WP````-F$`X<8`B/3X=#D!%N@<,M5JDB/RHJ#4H)F"H=\`\'\1 +MFI6JF9()*AMW#`\6:0<,#-'>`.!E@"#GP.+._W:`5K(F?X*@U("[@KJUNKS: +MN[(K?X(+%)A[!V@%DBD$YQDYLB9_@J#4@+N"NK6ZO-J[LBM_&_^""Q1+S)B+ +M%V@%DBD$YQD;LB/R@J#4@+N"NK6JN[(+*K>_%8;H_PP:Q@,```PJ1@(``.IE +M(.?`"^X,"@P,.`$,#?T%O0,E`/_")G_2H-30S(+1X`"H],K%VLR2#`H6R@H, +M'@OZ("?`"R(,"O"N@W:9'-(L%.(-%/A]!VX$@@\UC-@7;@?2+0B2#36\R4O, +M'?"X3R>;ZPP;.6\Y';)/-9(L%*#3D-E?@@D5L(@@@DD5TBP4X@T59C[5B&T, +M'_)(->(L%.AN.5XY;AWPF$TGF;SB+!0,&YB..6TY'K))->(L%*"#D(E9T@X5 +M#"_PW2#23A72+!2R#15F.Y28;0P8@DDU\BP4^&\Y7SEO'?```"`GP`LB=ID, +MTBP4@@T4Z&TF.`-+S!WPD@XU5EG_J$XGFO"(C9A]#!\Y:#EI.6XY'3E8.5DY +M7O)(-;(L%*B+N'NB"C6B2S62+!2(>9AI@@@U@DDU\BP4##NR3Q4=\'T/8<8` +MK0.]!>7=_FIE!K/_`#9!``P#,D(3,D(4,D(5,D(6,D(2.6(Y9\E)2%(Q)0$%!8&%!8E(5,E(20E(3<@(XN4*YD!V`',O0`XIC`0ICD! +M^`'P5I.8`0M55BD`5O7[J`&L*O'Z```_IN`0I@P+Z0'8`<@!N0'0U13`P$32 +M0@'"0@`H`1WP`&D!*`$=\```-D$`G(R)[S!^@!"T@8R`P$R9"0`/*;@ +M$*8,&_'\`.#05.#F!/HBTD)]XD)\`#RFH!"F`#RFD!"F#`92H`#"H(0RH`+@ +MJA&0AA2@B"""4C]"$C]"8B%VHSZ")T.B(B%:B(+8`M(8`FT!"FPB8> +M@J0PTF/+)@PDBJ+B*F$]\!:^$[(3;)(J88(J8K"9@O(CRY"(@(+(`8LM(.\BM=C-_"))W"+`7"9)V2*UU6"?_2!=86W4P` +M.J;P$*;R1=<`.J;@$*;B1=B2)":2"4R0D006B22B`_*BROX6^B,`-Z;`$*;" +M8R^R(R_V.P(&C`""1>D,8AWP``#2*F)6W>M&M?_B!=(6#O"B)<+R)13PJA&@ +M_\`6'^^B912"1>`&NO\`P@7M5ESMT@7(5OWL@DL_TM(%XAW:#`H;[A:^[,T" +M?/[B;&KR'=H;JDO,&__W.O!&K?\``+(D);B+#"Y+NX"[$>"[(``[IJ`0II(# +M^J)C-)P9\J0PTB8C^L+"+)\@W:#ZW<)MB=(D)=(-.:*A`M#1!!8-(7)DG')% +MT_(D)9(DG))D)/(/.;*@`"=O"-(#]\*@`="\@_(#^K)#]IPO`#>FL!"FLF4E +MDB4E/?"V&0*&Q_^R)"7(FQ;,-K(D)=(K"=+-_Q:](_(D)?B?9A\8PB8CLJ0P +M(,RPNLQR;&V2)B,@F;"ZF7)I;M(D)M(-31=M!0`WIO`0II(#\F89"``ZIK`0 +MIK)%T<(D)L(,3%=L!](#\CWP%MT<\B0F\B\1C'^2`_*2R?\6R1L,"\(D)K)# +M^=C\8J0P:F(;W=)F8\(L$+(#\AO,PF9D%DL6"YL6^16R`_(F&P)R9F3"`_(] +M\&8L`G)F8](DG!;=';@E\+L1D@/R\?T`DLG^%EG<`#JFD!"FDD2.D@2.%FG; +MN6$,#`OKZ3'A_@#2IHC:TNKB`#>FH!"FRK+ZNZ)+8*"@=+9J`@9I_YSZ)AH= +M)BHM)DHV)EHS&\Q+[DO=DLK]%CDUMMS,@D7IQEO_`#>FD!"FN#&2;>&7N]P& +M7/\````WIK`0IK)M'L;R_P`WII`0II)N7\;O_P!R8R\`.*:P$*:B)":H"KJJ +MHLH:EKK;/#NGJP+&;/_")":B9F"B8\G"#$S`QT$6O!$`-Z;@$*;B9<'2)<&V +M/0+&8__R)<$+_Q;?-P`XIJ`0IJ)E/I(E/GRJIZD"QES_LB4^IGL"AEK_`#BF +MT!"FTF4_PB4_IZP"!E;_XB4_YGX"AC0`!E/_````.J:0$*:21=/R!=,6SQ^] +M"``ZIM`0IM)%U,(%U,"^D[)DG(9S_P```#JF\!"F%F_I`#>FP!"FD@7.PF9C +MLB9C"YD;N[)F8U;9YP`WIO`0IO)F9-(F9!O=TF9D1IK_#!O&C_\`D@/R"YE6 +M.%/`/(3;)(3;B(C$Z(C1X(#][`B`:"@-``(0)"0L:/W%@`8 +M0))3;](3;Y(C2((CRM!O@I"0-`O_D_<:8E-P"]U`W1%@8/2#]Q[`_Q&!PP!R +M8T<@_R``9J$A3@!B4W'PW2#2;DF"R/^B(L,62A\62!^R!>H+B&8K[@P-'`[R +MH&`<"4P#_(``_IL`0IM(F(_*EW"#=H/#=@,)M(I(D)I()3$=I!9(#]Q8Y&+(F +M(\*EW""[H,J[>0O&%/]R1=@`.J;0$*;21=G"!=D6'+,,"O'_`!P\X>4`^O+Y +M(>HBJ0&GO`T,LAWP`')DG')%U,;W_AR+B"$BPA@;FID!*4&BR!BI$:DA)2PD +MN$$,&``WIM`0IK9]`L9#`-)K@28=`F8]"@`WICWPH!"FHFM]9BT(`#>FP!"F +MPFM^)CT"9FT(`#>FX!"FXFM_9DT(`#>F\!"F\FN`PB2=%LP.N%R,6\T+N%M6 +MB_^8$9E[ +MVT9O_@`WII`0IKE1DFU>AO+_```WII`0IKE1DFZ?QN[_``!6*.$,*J)%ZN4L +M!,:!_P```')E/W)E/L9@_P``@D7I#&(=\```.*:P$*;")B/2I=P@S*#:S+D, +M1K+^Z!'B9)U&QO\``#:A`%T"#!]"I6!RT@TRT@:"`].2!XVR)Y)*0N(D4\A[ +MV#NB"R'`F<#@W<#0WY.0GY.`RL#`SY/0F2#`F2",^(S:H@/4@@LBH(C`@(^3 +M@)D@LB>2P@/6@B1,^$NB"R`,'H#_P,#:P-#>D_#^D_"9(-"9((SLC,K")$VH +M:\"JP*"NDZ"9('S\(4X`XB0\#!L,!MT&X-N#T-D@D0`!%AT$@:8`8F,8V"7A +M-0#B8LB";5^"8H+R(Q2,S_(G)?(//\Q/8F,4LD/@@@/@"X@6N!NB`^$F>D"M +M!:4[`9R:+0H=\```T@/@9AT1XB5CC+YB0^"E(_K&````8F,44*4@@B3J\B0\ +M\F$&\F$%@F0T)=4!%HH`#&(=\`QR'?``D@/.)BD8H@>.F8&,BJT%#`OE.`-6 +M^OV8@;(D%[)D-@O)%OPSDB0V8J#_%ND'6:'13P#"U5QR80EV@%)2+-D,`O(% +M->CU4@4TD/\1L.X1)AE"@BS:DLG^B\RB"#2X^(((-6!ZP#"[$8"J$1"($7"B +M@["J(*"((&"UP`P*L%J#X'4@<'\@@'<@9%V@%)2+/P,`O(%->CU +M4@4TD/\1L.X1)AE!@BS]DLG^B\RB"#2X^(((-6!ZP#"[$8"J$1"($7"B@["J +M(*"((&"UP`P*L%J#X'4@<'\@@'<@N8X_$!`<*@`!8I!?#5@':`'X(M;J(D +M,)@8H)G`F?N2)#"(*)"(P(G[Z.,;S$O=Y[PL1O;_``":U=(M@<)C%Q9]':(G +M)[T%97/[#!NB0^'B`^'BSOH6+N*R0^$,)D/V7P(Z)D%K(4JJ(DZN7R([%/`/#Z$?)D%8(D/Y*A +M&)J5!V@(#!JB285&`0``#`S"2872`^<631;B`^I67MKR)R;R#TP7;PB2H`8` +M.::`$*:B`\X+JA;Z&`S\PF$$P"``D<,`#"62R?_2*T,6G146F17B`^H+F68N +M[OA!P@/2#/CPWQ'0S"#"8E2B)#_")!62)!8`JA&`S!'`F2"@F2"28DB''PSX +M4>AA(/\1\.X@XF)'@B,H)D1T,P1P*H@H)D@DFM*B,@,"LR8 +MXB]!26U^PP;HD/A!HG_`+('CQ:[`%"E +M(+*@`>4"`U8*R,(D&,)D-P8I_Q;.Y@O>H-T1TFLX1IC_``"R%*JB).JB9#\E +MV".B9!:R%*JB).JEVB.Q3P"B9!7&GO_B)R7B#C]6'NF2)#_R)#P,&)#_P/#X +MD_)#X$:?_P``H#!JI08J)B6&) +M44;4_]EAXA->#'_Y0>#A0>KMZ5&&S_\```!67/#")Y_&O_\7Z4_2+!$6W0CB +M+!`6?@BB)R>8#J@*R7'X#9":P)"18/"JP*"A8*>I,K(36J(A!O"[$:6_(ZD1 +MLA-:J&&ENR/2$UK($0Q.T,R"Z4'*RLEA!@X```QO^4$&MO\``+(36JAA/?#P +MNQ%EO".I(;(36JAA9;@CTA->XA-:R"$,7_E!X,R"T-%!RLK:S,EAL4\`R'&( +M88E1QJ;_#&(,&9)#Z1WP````-@$!#"9!`P'RI4`RTFJB(_+Z4I(5O""JH$JJ +MPBIXTJ8,<4X`QYD\DBIZXA6ZEYXSPBI\LB4ZXM(-XF$0P+O`5NMF\BXE@BI^ +M\@\\VI*9\8>?&XCQDBJ`@@C@D(C`%E@RA@(``)+2#=JBJ?&281"M`B6I_`P+ +MX>``\00!TB/R@J#4^O*`W8)B3UO:TNK=P@T(\F$.G0UVG!F(29PHJ&C(>(P* +MN6B,++EXB$GXB(P/N8A+F;)-#<(C\I*@U)#,@LK"PMP-LDRMHB/RD*J"\B$0 +MJJ*BV@VR2JB"(_+R+R60B(*B(^J*@I+8#9()K:FQ\@\\K%GJZ-(.")T.=IT9 +MB$F<**AHR'B,"KEHC"RY>(A)V(B,#;F(2YFR3@V"(1""*)*"""/2K\`+Z!:^ +M,I+(_A99,DJ2#`K"*84,"Q;\3^(C\H*@U(#N@NKBXMX-\DZL@B$0@BB2@@@C +M804!XJ"`PLC^%OPS%L@SDLC]%HE50B/RPJ#4P$2"2D)"U`VR1*GR(_+`_X+Z +M\O+?#K)O'.(C\L#N@NKBXMX-LDZJTB/RP-V"VM+2W0VR3:NB(_+`JH+!WP"J +MHLJJH@HH62$,!!8J!PP%<>$`8=X`HB/R(*J@HMIMHBH^H*2`H*JP(*J@<*J` +M9"*AJ":7B"*AJ":7F"*AJ" +M:7J"*AJ":7N"*AJ":7R"*AJ":7V"*AJ":7X,2+)D?RJ=J-%JF4M$IY2N8B/R +MTJ#4T&:"\34`:F)BU@WR9BCB(_+RI4`@[J#Z[K)N,\(C\M#,@@P:RL+"W`VB +M3*V2(^M\"-(5O("9$))GQ&(CZ\#]`8!F$&#_(&%/`/)F1N(C\F$#`2#NH&KN +MTFYXPB/RHA6Z(,R@:LRB;'J2(_+R(1"")3H@F:!JF8)I?/(O)8(C\NCQ\@\\ +M((B@:HCR:'[R(_+B#N`@_Z!J_^)O@-(C\@PF&]WV+2"1!@$@C:`,)M"FP)J( +M=IH0LFA[LFA]LFA_LFB!LFB#2XA\#B(5P)(E.J(5O-(5O<(5P8(5NH#=$0#\ +M$=#8(*"(@I)GBT(E.@"($8#=($!$`4`B("#_(/)GQM)G4](CZ\#$0<#,`>#= +M$-#,(-%/`,EMHB4?#`LM!O8J`2T+XB4@#$CV+C:"(1#X\8(H)J(?5)((3("J +M$:"K()"1!)"2(*"9())GC"((3?(/VR`@!$#_$2#_(/)GQPP"'?``HB$0F/&B +M*B;"&52R"DR`S!'`R""PL02PLB#`NR"R9XRB"DV2"=L,`J"@!$"9$:"9())G +MQQWP2I*R*846JS_"(1#"+"6B##F"'!?B'!:@H00;B!ONP@P^@.Z"#!C"S/?) +M4:!HD^"V@BR^L+N0D+L1YSP"QF\`@0>[`K)CY<(A$,(L)<(,/<+, +MOA:,,XF!R/&B(_*RH-3"'%FPJH*XH:JBHMH-P+N"H@JHLLL_T+L0BJJPJH), +M#;(C\L$(`5DA(+N@RKO"H`&E:@-8H;C!@LH_>"&A"0$@FZ!R%[^PL'2JF7!5 +M@G*OP%+%/W"($'!5$''<`)EQ@FE_>G*M!V71^HC!4F$1DB/E&XB)D9>X(TAQ +M4B$)PB1_DB$1K0=0L'3*F9)D@.7.^M(CY4+$!%+%`=#\$(`=(A"X(C[$(CZ[(C\J*@U(!$@J"K@M"`8""[H$!-@,J[JJ+( +M@:+:#:(*J`M$@$00RJJ@I((,'&5?`XC!N)'`B!&*@FJ(HFA]PB/EQ[L9=H`2 +MHBA]JJ2B:(&2(^4;NX+($)>[`X;Y_P#"(1#"+)(,&[),(PP+ALO^\B/R#`N" +MH-2`_X+B*8;Z\O+?#>)/K,:]_AP*PB$0PBPEPBQ^'`[@JF.V+`?B(_(]\!8^ +M);(C\N*@U."[@K"R@++;#8(+J!8H(:>X"^(A$.(NDL*@`\).(X(C\K*@U+"( +M@HJ"@M@-L@BH]SL"!J+^\DBHAJ#^VL+)\49M_@P+#$W22",&JOX``$(C\H*@ +MU(!$@HCQ2D)"U`V"".!"!*@+^!M$0$!T%O\@LJ!@@.N#Z3$,'$P-HF$2Z#&R +MIIR(\?(C\I*@U((869#_@KJR\/*`@.Z"\M\-\@^H@J_`XLX_@.X0\*K`H*Z" +MY4P#V#&Q"0$@E*"(\?+*/^*OP!NDJ1'@_Q""&%FZF2JT\FE_@-V"\B$2TLT_ +MX-T0&\_)0>+;#T).D,>Z18T)P0H!K01`[\#*NP=N$$N)PBE_J!$;N\K-PFF` +M0DM_X)%!/?!VF1V2*'\;RHN(FIV2:'ZB2X"2*'XKJBN[FIV2:'_"2W],6:%/ +M`-CAR$'`M!&ZLFJ[PDV?@BM]#`S)ZIG:@L@_DJ_`D(@0@FM]HB/LDB/KV+&@ +MF8+0X&":W0O=X-T0]K0%X4\`@FX/F$&($9>X)X*@`$"OP'::'DJXP,L1RL)J +MS.(L?1N(&[OJ[>)L@?:[!)%/`.GY/?#"(_+2H-30S(*R(1#*PL+<#?),J+(K +MD@P:HDLC#`N&2_[BH,#IH<8;_[CQJ*&R&UFPJH*)@:+*/]"J$$8V_\(C\N*@ +MU.#,@@P*RL+"W`WR3*@&,?X`#!BH\0P/#`RR&EG)P?EJL+L1LLL_T+L0LF/[ +MJ&JB:8/R8_Z&#/\``((A$/)+J((H)8(H?O8H`D9Z_X(C\E8XWN#(@LK"PMP- +ML@RH&[NR3*@&=/\`B/&"&%D;ZND!@,B0H,P1L(@1@L@_RLO`[H+"(^30B!"` +M[I#GO`*&8/^H`49?_P``DJ#`F3'&>_\``))A$_)A%*$+`;AAY24@(_(A%)(A$]*OP+AA!C/_DF$3\F$4H;``N&$E'R/R +M(122(1/2K\"X808L_Y)A$_)A%*$/`;AA91TC\B$4DB$3TJ_`N&$&)?^281/R +M812A$`&X8:4;(_(A%)(A$]*OP+AA!A[_DF$3\F$4H1$!N&'E&2/R(122(1/2 +MK\"X8087_Y)A$_)A%*$2`;AA)1@C\B$4DB$3TJ_`N&$&$/^281/R812A$P&X +M8646(_(A%)(A$]*OP+AA!@G_```V80`,!``TIF`0IE+2!F)E(C(E(@P6MH," +MQFP`B*6,>``TII`0II)E(Z(E([:*`D9G`'*O@++2`KDALBOOPJ$"#`06.PR! +M%`'A%0%)$8J"@*!@JK@`/*:0$*:0D'06>1<`-J:0$*9WJ0+&6`"FZ0)&5P`` +MV2/26!\`-J:0$*9WJ0+&4@"FZ0)&40``^2/R6-\XI1;S!0`\II`0I@P/#"TJ +MN^J[D)!T=JU+O#D`-J;0$*9WK0(&1@"F[0*&1```W2/26Q\`-J;0$*9WK0(& +M0`"F[0*&/@``/2,R6]^&!````#(E(PP-TEO?`!-``-:ATEL?&_\KNS@A^!%K +MB#(C[QO_\F$!-[\"!M+_@@7."XA6J`N8(9(I\`P$%@D+@18!X1<` +MD1D!@=X`<1@!8=\`70(,'PP"^4$I$2'A`&IE>G6*A9J5JJ6I,9D!@F$"R#&8 +M08(&**(,@+)L4K),@;),@K),@ZJ9F4&F&"H,`T(A`J(G?ZJCH*JP4*J@(*J` +MY1_^HF1O#`S"9(_"9'^R!B@;,TM$MR/92W?H`0P)P34`L1H!B!'8(?*@U/IF +M^MU:J+JJV2$;B-@QB1$,"\)M'I)*@/K=V3'GG87H0:8>1:T.D1L!#`C@\"2: +ME7:O!H))@!N9&XB@(R%VHBF"28`;J"NX.\A+V%OH:_A[*(N9HDEYLDEZPDE[ +MTDE\XDE]\DE^(DE_BXABH(PAX@`,#0P(F$%!'`'AXP`RI$3RI#SZ]3HUZN5* +M18"94X)$F@Q(DD2;6ITJF6KN:MVBH`!VJ#*")!F2R2"":7>")!F":7B")!F" +M:7F")!F":7J")!F":7N")!F":7R")!F":7V")!F":7X,2*)O?UJ=*IE+_S>? +MM,+5!@P+LDSA'?``-F$`0M(&@@36#`R,&,)$X3$=`3HRHB,@8M(,%FHL@B9E +M@@@Y@(8$%J@KD@3.9BD'K0(EY?_"H`!2TFVB)"1RT@6,^M(E,K(G:SWP(-V@ +MTMT&LFT5XB9EXBX))AXN(*(@Y5(`PJ``DB"M@Y(E,K*@U+"Y@KJRLML.LBL<%OL\%DI"#!W20XGB`XD6GCWR)3(@ +M_X#RWP;R#\R\O[(F9[(+-(*@_X<;%ZT"PJ`"Y2[ZLB9GK0+"H`.R"S0E+OH, +M#/(E,BK_\M\&PD_,XB4R?/T@[J#BWFW2;C:2)3*BH-2@F8*:DI+9#L)I'((E +M,L)#B2"(@(+8;<)(X,)$X:'<`*JB93/Z#`PL"Z)$RZ"@=*>[!L)$RT8*``#` +MBA&*@H+88,)(@/($R\#_$?#R@/+?8,)/@>(E,M($RR#NH.+>;=)N.9(E,B"I +M@*+:;:(*X&8:"R"YH++;;;(K.;)$R]*@=0P/LB9EH@3+DB;<#!B)$9+)_J"J +MD*)$T*($T.(+/9#X@_JJYST*TBM^]BT$XJ``Z1&R%&#"%&'2%&+B%&.!'@%< +MC_#Z@J(FW/#R@(#_@"7N_;($RRP(MS@%G0N&````#`F21,N0X'3B2C32)M[! +M-P#2#0&R+/`672GQ'P$]\/"[$((F99(:$K)L\-($R_(47,(C'<#=$=K2TMU@ +MTBT?L4\`XAH3VLS":T3")2O`_P%\#=#,$,#_(/)K1L(E*_%.``#N$=#,$,)O +MQ-($W\BDHF9G(-T!0,P!@@@YP)D@D.X@@($$,(@!@-T@X-T@V8O")S+)&I(G +M,YDJ@BTB,=ZMW2:T?"(QZB(QT, +M`LJJHF_%'?```+(E-?++@1:_PM(E,B#=H-+=;=(M-H+-@1:8P0P*UYL"!@K_ +M\B4N#![PKH-&!_\``!9*PRJ)@M@&@@C,%IC"#!F20XE&"/\``%8*R88T_]@1 +MC$WB)3-6_M7X(H$A`?(O.H"[(/#P!%9OU9(7X(*@@)8+9(+$SWP)AD?LLG^5IN\TB;<9AT9!O#^`.(G +M-ND*!H#_@B`/(J\B#_H,K_XB\=TB\?Y[T1T)[`L(%!E[@(V!^PO<"&`@``LBKR(+N@ +MRKNX&]($XQ8])N($Y!9>)/(J\B#_H/+?!O(O&?K[\F:S\F:_DB0OTBKRZ#3B +M9K8@W:#*C8(H(Y<8"(+=!O(FZ_)H'Y(D*!9)\>(J\B#NH.+>!M(N&=)N&\(J +M\B#,H,+X.F3DB=_J#D6^A'2T@K2+>78/0P*%BT1^`D,"PP#=H`,NH^("!NJ2[LZ +M.->Z!`;[_P``%JX%LB=_"ZZI`;(K`Z58(K(G?ZD1PB7RJ`')(;@[950BPJ7P +MN!&((0P-,+N"((B@@M@&HF@=\B=_XB7R#`KX#R#NH.+>!N(N'7:`#*J/B`@; +MW4NJNKC7/@8&^_\```P+DB0HS%FB)W^H&KJZT@3C%MT,X@3D%OX*@B=_\B7R +MB"@@_[#RWP7R+SFZB(K_\F:S\F:_TB7RLB0OZ#3B9K8@W:#*W;)M(Z(E\I(D +M)2"JH,JJF4I&0O\``/(J\B#_H/+?!O(O&?K[\F:R\F:_AFW_`)(J\B"9H(+9 +M!H(H&9+9!8J+@F:RDBDWFIB29K.0B$."9K:"9K]&8_\,`X;`_PNIHF:_QHG_ +MLB:_LF:VLF:RQB3_#`Z&J_\``-(FO])FMM)FL])FLD8?_P#B)?(@[K#BW@7B +M+CFZ[N)FLN)FO\;4_P"")?(@B+""V`7R*#F2)W^Z__)FLI@I@B@Z^IF:B()F +MLX#_0_)FMO)FOX;)_V+2`T8@_P``@MD%TF@]\BKRXB:R(/^@\M\&XF\;QAG_ +M-F$$LJ780=\`#!B2T@JB*>5M`I)A?:B*(M)JDBGD2ZH`&D``J*$6*73"(O+2 +MH-30S(+*QDK,P@PJ#`G,',81`[!6@'+6;8*@`':`I+(G,L*@U,"[@KJVNKBR +MVPVR*SSB"Q0;F<@[%BX'TB4UQ[T)H.S`Z0O&`````,D+LBY`@;5_Y(%]F8I`H;?`A8Y)`Y(!"A()"Y(`Q\90("TB+R#`SBH-3@W8*" +M(7C:UDK=T@TJ@F%W[0$6/04R86\,"1#3H':`/;(G,O*@U/"[@KJVNKFRVPVR +M*SP;S/(+$TN9J"N,OSB5IZ,'&XBR;0#2S02R(O+RH-3PNX*ZMDJ[L@LJM[P$ +MQN[_``""87@R(6\,;+(A>."CH+)A=J)A<#"[P+)A?^7Y`3)A;^(A?Q:3!C)A +M;]T#C0$P\#2BT0&BRB"@KJ!VGP>X"+D*2XA+JM"407:919@(N!BY&K@HN2JX +M.+DZN$BY2KA8N5JX:+EJN'BY>KB(N8JXF+F:N*C8R,BXN:K)NKC8V+EZ +MN(BYBKB8N9JXJ-C(R+BYJLFZN-C9RMCHN=JX^-GJF0JY^J+*0(+(0+T!PB%V +MT2,!HB%]XJ8X#`_R91GR91CJYN)A<0P/HBKDVM;287(E=0'"(78,#]$D`:(A +M?>*F/++1`;++(.KFXF%THBKDT-:`TF%SY7(!XB+R\J#4\.Z"ZN9*[N(.*PP( +M@F%U%BX)#`FBP7^BRA%V@'XR)S*RH-2P,X(Z-CHY,M,.N,.Y"CC##!^R`Q4, +M#LAS!VL5LB%]LBODW0S(;+++_[#O@^#,D,)M!3(G,DNJLJ#4L#."&X@Z-CHY +M,M,..,/B(7T,'\(#%4N9V(,7;`[B+N3(;0ONX.^3X,R0R5WR(O(RH-0P_X+Z +M]DK_\@\K][@%AM[_````@F%U0B%Q(B%RLB%UPJ`#HL%_HLH1I=H!(-(@[03" +M(77RH`&B(7VRP7^RRQ&B*N0E90'"(772(7/B(70,'Z(A?;+!?[++$:(JY*5C +M`=(A>,(A=]#,P%;L"^(A>/8N`D8M`!;.!2TN(@B%]@BCE,B%\#"V"*'(;,S)A +M?(>S/Y(A?9(IY0P(*IF2*79VK1+2(7W2+>6*W=+=`N(=`:J(EQZ$#`B&X/_B +M;&X;NTO,QNW_``"2;&X;NTO,QNK_``#B(7WB+N7B+G,,#_)A>Q9N"@P.@0$! +M#"Q@)Z"*(L85`((<`)*@U)"(@HJ&2IB2"8!+[H+8"W:9*9(HK,()$]AYF(D' +M;`OR+16,7_B5.`WW$TX7;`O"*16,7/B5V`GW'4M+B((A?8(HY3(A>PPL@BAS +M&S,R87N'LS^2(7V2*>4,".J9DBEX=JP2PB%]PBSEBLS"W`+2'`&JB)<=A`P( +MAN#_TF*1&W=+(L;M_P``DF*1&W=+(L;J_P``A@8#NE9RUFT,"':`-+(G +M,L*@U,"[@KJVNKBRVPVR*SS""Q,;F28\'-(B\N*@U.#=@MK62MW2#2I+B-^!?D+A@```,D+LBR:6Y+ +MF:(A?="#07:8,H(JYY+)(()I9H(JYX)I9X(JYX)I:((JYX)I:8(JYX)I:H(J +MYX)I:X(JYX)I;((JYX)I;09^_P```+;(`L:4_Z$!`6"8H("\P+#P)*J9=I\* +MPB%]PBSGPFF12YFB(7VP@T%VF#*"*N>2R2"":8F"*N>":8J"*N>":8N"*N>" +M:8R"*N>":8V"*N>":8Z"*N>":8^"*N>":9`,`AWP`/(%_A:O0@P'*AD#H@.(.@"+"`8+8"W:>*9(HK-()$^AYF(D';0OR +M+A6,7_B5.`[W$TH7;0O2*16,7?B5Z`GW'D=+B#(A?3(CY3(C<@P(#"TW,@+& +M5@"2(7V2*>5PF8"2*79VK1+2(7W2+>6*W=+=`N(=`:J(EQZ$#`B&X/_B;&X; +MNTO,QN[_``"2;&X;NTO,QNO_``""'0"2H-20B(+P(`"*ADKX\@^`@M@+=I\. +MDBBLL@D32XC8:28[/CWPTB%]TBWE2^X,"-(M<@PO&R+7,@(&7@&2(7V2*>7J +MF9(I=G:O$M(A?=(MY8K=TMT"LAT!JHB7&Y\,"$;G__(M%1:O^[@I.`6WD[/R +M(7C2;&Y+S!O_\F%X1NG_``""'0"2H-20B(**ADJ8D@F`2R*"V`MVF2F2**S2 +M"1/H>9B)!VT+\BX5C%_XE3@.]Q-.%VT+TBD5C%WXE>@)]QY+2XB"(7V"*.4R +M(7H,+8(H=!LS,F%ZA[-G:M$M(A?=(MY8K=TMT"XAT! +MJHB7'H0,"(;@_^)L;AN[2\S&[?\``))L;AN[2\S&ZO\```"R91@,#N)E&4:) +M_@P"#`_R91CR91D=\+I6AAG]@B%]@BCE@BAU#`F287D6V)\,#H$!`0PL8">@ +MBB)&%P""'`"2H-20B(**ADJ8D@F`2^Z"V`MVF2R2**S""1/8>9B)!VP-\BT5 +MC'_R)0DR+0#W$U@7;`S"*16,;/B5TBD`]QU32XB"(7V"*.4R(7D,+((H=1LS +M,F%YAS,"!F3^DB%]DBGE#`C@F8"2*7QVK!?"(7W"+.6*S,+<`M(<`:J(EYT" +MAM[_/?`,"(;=_])BD1MW2R*&Z_\`DF*1&W=+(L;H_P``,;A_0P/\F%WQK3]@B,#&@':`,M(G,O*@U/#=@MK6VMC2W0W2+3P;F?(- +M$TN(TBT&)C\7,B+RLJ#4L#.".C9*,S(#*C>Y*(;Q_P``\@TYL@TU!V_>5KO] +M.`WX!3-)L;DO,&[NR87@&\?\`LB%XH2,!#%P]"Z"F@*4M`;(B\L*@ +MU,"[@KJV2KNR"RH,"3)A;A:[!C)A;M$!`0P(8,.@VLQV@#/2)S+BH-3@W8+: +MUMK8TMT-TBT\,J#4&YGB#1."R`32+08F/A/R(O(P_X+Z]DK_\@\J][DG1O'_ +M`*(-.;(--0=JXE;[_?@-Z`7WKMBB(7C2;&Y+S!NJHF%X!O+_`,*@!*(A;C(A +M>+$C`3)A?J`SP&"JH*)A;;"J@#"S(&4C`=(A;L(A?C)A;!:-"#)A;($!`6"C +MH-"0-(JJBH9VF0FR*&ZR:I%+B$NJT)1!=IEEDBANLBAOLFJ2LBAPLFJ3LBAQ +MLFJ4LBARLFJ5LBASLFJ6LBATLFJ7LBAULFJ8LBAVLFJ9LBAWLFJ:LBAXLFJ; +MLBAYLFJLBA\LFJ?LBA]LFJ@DFJ1@LA`HLI`QST"AB(` +MH0$!TB%L@B%MO0VJB-#0-*JF=IT)DBANDFJ12XA+JK"407:999(H;K(H;[)J +MDK(H<+)JD[(H<;)JE+(H+)JF[(H>;)JG+(H>K)JG;(H>[)JGK(H?+)JG[(H?;)JH))JD8+( +M0*+*0,)E&<)E&*(B\K*@U+"J@JJF2JJB"BL,";T,%OH*T0$!#`A@S*#:S':` +M,-(G,C*@U##=@MK6VMC2W0[8S>(-$QN9V&TF/A?B(O+RH-3P[H+JYDKNX@XK +M2XCGN6X&\O^B#346&OZX;;E=HB#"[@AO_ +MNK:ZN++;#KC+\F%X2\RR"Q&R3C2&W_\```"R)1C!(P%@JZ#*JL(A>+"\P`P< +MY0(!HB48LB%X#!S1)`&@N\!@JJ#:JJ4!`<(A>,)E&<)E&`8>_K(E&(9E_0#2 +M(7W2+>72+7,,`A8M">(A=PP(#"U@[J!Z?@P.1@\``((=`)*@U)"(@O`@`(J& +M2OCR#X""V`MVGPZ2**RR"1-+B-AI)CL]/?#"(7W"+.5+[@P(PBQS#"TBP@'' +MLD.2(7V2*>7@F8"2*7AVK1+2(7W2+>6*W=+=`K(=`:J(EQN@#`B&Y__R+146 +MO_NX*3@%MY.TPB%WTF=_2W<;S,)A=X;I_[(A>-(A=])E&;)E&$8Y_>(A?>(N +MY>(N=`P"%FZJ#`?1`0%@RZ`,"-K,#"U&0```XB%]XB[EXBYT#`(6[C`,#@P( +MPB%XT0$!#"]@S*#:S`97````@AT`DJ#4D(B"\"``BH9*V-(-@(+8"W:=#I(H +MK+()$TN(V&DF.SX]\-(A?=(MY4ON#`C2+7(,+QMWUS<"!JP`DB%]DBGEZIF2 +M*79VKQ+2(7W2+>6*W=+=`K(=`:J(EQN?#`A&Y__R+146K_NX*3@%MY.S\B%X +MTFQN2\P;__)A>$;I_P``@AT`DJ#4D(B"2W>*AD"8@)()@"+"`8+8"W:9*9(H +MK-()$^AYF(D';0OR+A6,7_B5.`[W$TH7;0O2*16,7?B5Z`GW'D=+B#(A?3(C +MY3(C=`P(#"TW,@+&9/Z2(7V2*>5PF8"2*7IVK1+2(7W2+>6*W=+=`N(=`:J( +MEQZ$#`B&X/_B;&X;NTO,QN[_``"2;&X;NTO,QNO_``""'0"2H-20B(+P(`"* +MADKX\@^`@M@+=I\.DBBLL@D32XC8:28[/CWPTB%]TBWE2^X,"-(M=`PO&R+7 +M,@)&9P"2(7V2*>7JF9(I>G:O$M(A?=(MY8K=TMT"LAT!JHB7&Y\,"$;G__(M +M%1:O^[@I.`6WD[/R(7C2;&Y+S!O_\F%X1NG_NE8&5/L`B#3FR#34':@[,N_(A>-)L;DO, +M&__R87@R(O*BH-2@,X(Z-DHS,@,J\J#4-SFRLB%XH2,!#`P]"ZJFY(-$QN9V&UF/COB#36\7KAMN5VB)S(PJH*JIJJHHMH.J,JH:J)L +M;O(G,K(A>##_@AN[^O;Z^/+?#OC/LF%X2\SR#Q'R2C32(O+BH-3@W8+:UDK= +MT@TK2X@RH-37.9*R)1C!(P'R(7A@JZ#*JK"_P,*@`27#`-(A>-)E&,8!_+(E +M&`;A_0"R(7BR91B&WOWB(7WB+N7B+G4,`A9>R@P.\B%W#`@,+6#_H'I_A@\` +M@AT`DJ#4D(B"\"``BH9*F)()@(+8"W:9#I(HK+()$TN(V&DF.SX]\,(A?<(L +MY4ON#`C"+'4,+1LBQS("1A7_DB%]DBGEZIF2*7QVK1+2(7W2+>6*W=+=`K(= +M`:J(EQN?#`A&Y__R+146K_NX*3@%MY.SPB%WTF=_2W<;S,)A=T;I_P``TB%] +MTBWETBUT#`<67?0,#@P(PB%XT0$!#"]@S*#:S`80````@AT`DJ#4D(B"\"`` +MBH9*V-(-@(+8"W:=#I(HK+()$TN(V&DF.SX]\-(A?=(MY4ON#`C2+70,+QMW +MUS<"!KS_DB%]DBGEZIF2*7IVKQ+2(7W2+>6*W=+=`K(=`:J(EQN?#`A&Y__R +M+146K_NX*3@%MY.S\B%XTFQN2\P;__)A>$;I_P``@&`P!(2L>4U@8!`%A*QY782,&8\5BAB,%+F9GQA[T2.($H,=`SP!LS +M=I,&.`(BPOPY(AO=HF6`B+%+55FA2XB)L=\`D8S`%##P!O, +M1C$``$A(QY2RHBL@4B@4IQ6MANG_XL/^%@X52,$X@2@Q1[T.T#/`&S-VDP8X +M`B+"_#DB#`,,)':D>EB14B5".E52U0*"%0%66`9"%0`X85*@U%!$@E'?`$HS +M6E-2!2D690B!Y@`,"8HS=I5`@B-_<@@3J&AF-PS"*A6,;$@FXBH`1QYI!V<- +MJ'C"*A6,7$@FZ`I''E@79Q"HB,(J%8R,2";H"D#NP!9.!$LS&YD&#@""H(2* +M,Z'?`)AAJIF2"2E8`3AA5ID(Q@<`0BLA.&&"H-2`1(+)<4HS0>``QRX"AHK_ +MZ$'@S,"&B/^HD:(JO,B1Z,%(H<(LO(CQ6+&@S,`6#.P;W:)D@$N(2U59L8GQ +M2T1)H=>^`D:J_T@AJ($X\4"=H-"JP"NJ2C-RT_YVFA/(`TLSC*R")X#B*!2, +M?HD)2YE+=P:?_U@F2`A7%/,&^_\`4@4IQL+_B''(01N7<#C`ER@"QC<`RL,+ +MS`;7_T(K(3AA@J#4@$2"F.%*,T'@`*B1F`E*,^(#"\(JN:(JO':>*8(C)'(( +M%$LSK$P'9POH>$(.-8PT6%Z7%2<79POHB$(.-8PT6%Z7%1@]\.T*1@0``&8W +M].AH0@XU%L3^4BX%EY7F2,&(P)E@$O,R?%+55FA2XB)L=>T`D9N_W@A2($X\7#MH-!$P"M$>C-RT_YV +ME"-8`TLSIQ4:@B>`P@@UC-Q"*`67E`C"*R!2*!3'%0.)#DON2W<&7_\+PX:? +M_PP"'?`,8@P=TD;U'?``02L!D2,!^&'B)G^!+`%2WP<+[NF!BH^:GYDAB5%* +M_P8+_P``HB8NN%:Y0:D1A@__-F$`G0*!+0$M!5$N`7!8@R897F8I6"DQIA13 +M#`(Y(4D!,#2@#`28`9X!L@QJ'J2RP'`NZ"B:P"29@#8`=AN;P+N@J0N9!M@!UZ0R>"%P=*#&`0``&T1+ +M=S<7(J@'@@H3%VCPJ(K@!0`6BOX;1*@'N`;(,:B*&YO`NZ"I"YD&V`'7(HW7 +M)(7X$?+/_E:/]M>B`D;&_]>D`L;&_T;6_QM$"%P)CC-(-3*(FN;(O6?(O6B"J$>"[ +M$8#_$5=M!((%SHS8DB9#DBD1/?`+F199)`P,X5P`D@75\-P1@@73L-T@H)D@ +M`(@1D(@@@-T@\-T@X-T@V9>B)106>B*"!HI@?LI@@PIC> +MTIC?XIC@:XB0D(1PJA'@NP'`P(1PW1'@[@%`JA`PNQ!`W1`P[A"@F2#0K"`@ +MF1`@JA"0FR"@KB"9]ZGW(3`!D1 +MHI@?LI@@PIC>TIC?XIC@:XB0D(1PJA'@NP'`P(1PW1'@[@%`JA`PNQ!`W1`P +M[A"@F2#0K"`@F1`@JA"0FR"@KB"9]ZGWP@72LB4BH@71XB4CTB9#<*H1P.X1 +MTBT1\+L1X+L@D-T1T,P@P*H@L*H@J:?")D3HA=@2)KFH^)"JP!9:YK((.;"Q +M!!;+Y<(H$*(H$0O9T*R#%AH@C0J)`<:1__@!\B\5%K\9D0(!B#&:B((HEQ;H +M&)@!H@DY@@DXF/F@H02`@`3`B!'@JA&@F2"0B"`,B9"((+A!J`&"9Q32*W+( +M&M#,P,)G$K(K)C1L`@`(96_P``%IC:B!$L#_#=$0Q:HF4BHF4C +M\-UC\)@1\(ECB1$6G02(,>"(@`=M$_)8(/)8'_)8'D)8X$)8WT)8WFN(T+%! +M/?!VFR7R6"#R6!_R6!Y"6.!"6-]"6-[R6"/R6"+R6"%"6.-"6.)"6.'+B+@1 +M%MO3H1ZE-R"B +M1?&&J/^2H,!&J_\``-$"`;@QPBA_VKNR*VC*N[)C3@:W_P```%;(WX(FO(9] +M_P```#9!`*8C0@P*"]-]`@P8#`F@P\`+O':K#Q;$!B84529$/B94)QN(2W<; +MJI<;$B!LH"")H%@(8M;^0A29G]]`@P8#`G7FL8=\`#H%R#YH/@/Z`[X +M#_>NR9T(!O'_`&@7(%F@6`5H!E@%9Z6UG0@&[/\`Z!<@::!H!NA>:%;GIJ&= +M"`;G_P#H%R#YH/@/Z$[X3_>NC9T(!N+_`#9!`*8C0@P*"]-]`@P8#`F@P\`+ +MO':K#R8D;"8T529D/B9T)QN(2W<;JI<;$B!LH"")H%@(8M;^0A29G]] +M`@P8#`G7FL8=\`#H%R#YH/@/Z"[X+_>NR9T(!O'_`&@7(%F@6`5H)E@E9Z6U +MG0@&[/\`Z!<@::!H!N@>:!;GIJ&="`;G_P#H%R#YH/@/Z`[X#_>NC9T(!N+_ +M`#:!`&'&`&IB0B9_#`A2I#P@1+!:1#(D:X)D:](F?T(D:B#=L%K=PBUKPFUJ +MHB9_(*J@6JJB*H9:4K(E(4/_#R@(K_I4'[>7K(,9*@[_*@_=(* +M.0P>?/B"2C3B2C:""CCPW1`,+_#=()"($()*./*@_M#0=/#=$.#=(-)*.<(L +M?WE*B`*M`N`(`(@2K0+@"``6ZOXM"AWP``"B +M)B$6*OZBH$"E`>L,&`P-2L(,+D$W`)%7`#*D-/(DQ#HR^0&0_Q#R9,3B9+2R +M!=*2)1?R(V'"+'_)U2#_H'K_\B\DX@9_D-B#\_P0R=7C_`S3_`_)U1:[`)(& +M?8(C@)/X&()C@%@!DB.`K0(`.::($O`@`.`(`!;*_BT*#`JB9+2BH$!E^.I2 +M9,0=\*(%SA::"`NZ%CL,PB)I%FP-2N+2H0S2;G_R(FF!.@$6#PB)$H;+_Z(% +MWK(%TYHR#(P@NQ'`NR"R8YT6V@:B!4,'1:L0?(EQY)D +M$0O_\F7'TD2:XF04TD22<4X`\4H!84\`PB04H34`/#OL[,&L`)(EZL"9@``Y +MIH`0II'#`()D(G:`$](G@PN9%ET7%DD7X@/JXL[^%KX71OG_`/(B5/9/`H83 +M`;9L`@9@`(+,^PP?#`R`SX/"1)P,B()D%``^IO`0IO)D%@`^ILB!MD\"TD/I +MP!"FPF07`#ZFMDP"TD/IP!"FMDP%TD/IPJ``PF08`#ZFMDP"TD/IX!"FPB05 +MXF09\B7J@B06P_\(@_\0PB07@B08P_\4@_\8X_\<`#^FMDX"TD/I@!"F@F0B +MXB0BTB0BPB0B\B0BX.(4XF0;@B0BT-04TF0<\/@4^4'R9!Z`BA2)48)D'_(D +M(H(D(L#&%,)D'8",%()D((EA@B0B\/`4\F0:@(X4B7&"9"&"`]P6N`6"`]_Y +M,19X7PP?Z2&(,=D1R0$F.`;800P,T/R3B"$,'B8X!MA1#`S0[).($0P=)C@& +MB&$,#(#W*@+&3``L>X9-``#,Z0PMTD/J);?_ +MH34`/#OQ2@$,O@`^IM(EQ<(EK-K,EOQ`QZL^+'V&#P"VSP*&\`#RS.$63V*" +MS/I6V`J2`]P,RJ)D%!8)#.*A`@`^IL`0IL)$E+($E!;K"M)D%@SO\F04QB@` +M^MS2#0#B)<;"):SJS)8L/,)G5<(EK>(EKK($GYA4P_X(L_X1 +MJH[Z[I/X%()GR>G&PB6MDB6NL@2?P_D(L_D2DF9,@B6N\B,8((B@\FAJ'?#A +M2@$,V/+,^?#208)D%/#P%/)$H>+.0.#=H-@-V62M`B4Z`;(EK9(EKJ($G[/Y +M"*/Y$I)F3((EKO(C&""(H/)H:AWP`,%*`2ZJ,>R8; +M:B8K528[(I+)(+(EK9(EKJ($G[/Y"*/Y$I)F3((EKO(C&""(H/)H:AWP^&GH +M6?#PU.#@Y//^#^G&V(G(>=#0U,#`Y-/\#\G&N*F(F;"PU("`Y+/X#XG&!NO_ +MN&F(6;"PU("`Y+/X#XG&AN;_V(G(>=#0U,#`Y-/\#\G&!N+_`#ZFHF6NDF6M +M\!"F\F7'!O?^````/J9\^Y)D$>)$FK)EQZ`0IJ)D%8(D%1N(@F04AO7^`/J< +MLB0B\5P`X@/3P@24^KOC^Q3#^QFR9U:")?[B)!'R):R2"0"*[O/^!-/^"I/^ +M$.)G5<(EK;(EKOA4@@2?P_L(P4L!@_L1JNOS_A3B9\G*N[G&@B6MXB6N\@2? +M@_X(\_X2XF9,PB6NLB,8(,R@LFQJ'?``HB05DB,JH_D(`#FF@!"F(*(@@F0B +MI;0&HJ`!O0)EV0:R):RH4[JJEEHI/#RGK`0L>\8!`+%*`;JZL@L`PB6LJ&/* +MJI;J)SP]IZT$+'G&`0"12@&:FI()`*(D%.(D(H(#T_($E*/^%8/^%//^&>)G +M5M(E_J(D$<(EK-JJP_H$L_H*D_H0HF=5DB6MXB6N@@2?T4P!D_X(@_X1@34` +M\B)%VMZ*[O/^%.)GR=G&PB))R<:R(DVYQJ@RF$*R(E2@H.20D-0+RQ;,)\++ +M_A;L'Y/Z#ZG&V,+(LM#0U,#`Y-/\#\G&LB6M\B6N@@2?L_\(@_\2\F9,XB6N +MTB,8(.Z@TFYJ'?#R(RK20^GB9!729!3C_P@`/Z;B1)L]\.`0IB"B(.)D(N6C +M!J*@`;T"I<@&LB6LJ%.ZJI:Z'#P\IZQY+'L&'P`,#48,__%<`+(D(N(#T\($ +ME/J[X_L4P_L9LF=6@B7^XB01\B6L#`F*[O/^!-/^"I/^$.)G5<(EK;(EKOA4 +M@@2?P_L(P4L!@_L1JNOS_A3B9\G*N[G&@B6MXB6N\@2?@_X(\_X2XF9,PB6N +MLB,8(,R@LFQJ'?"Q2@&ZNK(+`,(EK*ACRJJ6"A0\/:>M!"QYQ@$`D4H!FIJ2 +M"0"B)!3B)"*"`]/R!)2C_A6#_A3S_AGB9U;2)?ZB)!'"):S:JL/Z!+/Z"I/Z +M$*)G59(EK>(EKH($G]%,`9/^"(/^$8$U`/(B1=K>BN[S_A3B9\G9QL(B28!`P(@D1_4J4\6E*B)462%<"BR@&B +M946G&722(Q6"$UJ2R0&28Q67&&RAPP"Q3@!V@!+2!'_"*TT+JM=<"HR:X@/B +M)BX,AOG_S&H,+_)#XJ5%_\($?PQZ\B5(LB4?#!X,#2"[H&J[\-Z#T_H/HF4^ +MP_H1HF4^LBLDL_H4HF4^D!"F@@/AS#B"`^*<&`PR'?"0$*8,(AWPH!"F##(= +M\``,7``\IK`0IHR;T@/&)BW29!$+JJ)CQ_)$FM)D%=)D%`S'84\`'&K"(E3B!=CB1)+' +MNE`L^,>`L:%`A;\+K9.`D9#`/)B5+(D%:(CZK/Z"``Z +MII`0IB"B())D(N4&!@P:O0*E@0;"(\6R(ZP\.M(CQLJ[EKLEMZH$+'S&`0#! +M2@'*R\(,`+(CK((D(MJ[TB04EBLDMZH&+'E&`@```)%*`9J;D@D`L@7/H@24 +MT_@5L_@4H_@9@F=6\B/^TB01XB.L^MWC_03#_0J3_1#29U6R(ZV"(ZZB!)_Q +M3`&S^`BC^!&A-0"2(D7Z^*J(D_@4@F?)^<;B(DGIQM(B3=G&HB)4DB):LLK_ +M%DL=XLK^%FY=%CEO\LG_%A]T9BD"QMT!DB);S!G&!@)F&0)&$0*"R?X66&[2 +M(ZVR(Z["!)_3^PC#^Q*R9DRB(ZZ2)1<@JJ"2:FH=\#P.Q[X+\D7E\F)4\F)5 +MQKC_P/`$5D]7LF)4QK7_`#VFHF.NDF.M@!"F@F/'AH;_```]IGS\DF01TD2: +MPF/'L!"FLF04HB04HF05AH;_#(NR9!0`/::0$*:29!8`/::P$*:"H`RWN`7R +M1>6RH`"R9!<`/:;"H`RWO`+R1>6`$*:"9!@`/:8,SH>^`O)%Y;`0IM(D&)(D +M%;)D&<(CZH(D%N(D%Y/\"(/\$./\%-/\&+/\'``\I@S)M[D"\D7ET!"FTF0B +MPB0BLB0BDB0BXB0BP,`4PF0:@B0BL+(4LF0;\B0BD)04DF0B1'P^A3R9!_Y(?(D(M#<%-)D(/#^%/)D(8(%V!9X!((%V]D!%EA[ +M#!TF/`:($0P,@-R3#!PF.P:((0P+@,N3#!LF.0:(`0P)@+F3#!DF/@0,#O"> +MD_($DI"+$/#]$/#\$(#_$/)$DJT")8<%#!J]`F5L8P``P,AFO_#`E&V89`H;W`=+)_E9-Y.A"V#+@X-30 +MT.3C_0_9QL(B)+(B(\#`U+"PY,/[#[G&HB.M@B.ND@2?H_@(D_@2@F9,\B.N +MXB47(/^@XF]J'?``\B05PB/JX@2:\_P(X_P/`#RFD!"FDF0BTF04\B/'@@7; +M@D22UB\`Q@`"D<,`DLG_@B>#%MA<%ME"\P6^B4+VA;M&^+*_A;. +M$68ZX:(I6A8Z#0OZ%F\(9BK3B$OX.X"`U/#PY(/_#_G&XBLDTBLCX.#4T-#D +MX_T/V<:H:XA;H*#4@(#DH_@/B<;R*R;B*R7P\-3@X.3S_@_IQMB+J'O0T-2@ +MH.33^@^IQH(K*/(K)X"`U/#PY(/_#_G&Z*O8F^#@U-#0Y./]#]G&HBLJ@BLI +MH*#4@(#DH_@/B<8&U/\`XBLDTBLCX.#4T-#DX_T/V<:B*R:"*R6@H-2`@.2C +M^`^)QO(K*.(K)_#PU.#@Y//^#^G&TBLJHBLIT-#4H*#DT_H/J<8&PO\`J$N( +M.Z"@U("`Y*/X#XG&^&OH6_#PU.#@Y//^#^G&V(NH>]#0U*"@Y-/Z#ZG&B*OX +MFX"`U/#PY(/_#_G&!K+_`*(I6A8J!R8:2M+*_E:-ZXA+^#N`@-3P\.2#_P_Y +MQN(K)-(K(^#@U-#0Y./]#]G&J&N(6Z"@U("`Y*/X#XG&\BLFXBLE\/#4X.#D +M\_X/Z<9&G?\``/(K).(K(_#PU.#@Y//^#^G&TBLFHBLET-#4H*#DT_H/J<:& +MD__H2]@[X.#4T-#DX_T/V<:H:XA;H*#4@(#DH_@/B<9&B_\``*(I6A8J!R8: +M2O+*_E;/X=A+J#O0T-2@H.33^@^IQH(K)/(K(X"`U/#PY(/_#_G&Z(O8>^#@ +MU-#0Y./]#]G&HBLH@BLGH*#4@(#DH_@/B<9&=O\``*(K)((K(Z"@U("`Y*/X +M#XG&\BLHXBLG\/#4X.#D\_X/Z<:&;/^(2_@[@(#4\/#D@_\/^<;HB]A[X.#4 +MT-#DX_T/V<9&9/\``*(I6KSJ)AHHTLK^5AW8J$N(.Z"@U("`Y*/X#XG&\BLD +MXBLC\/#4X.#D\_X/Z<:&5__B*R32*R/@X-30T.3C_0_9QH92_XA+^#N`@-3P +M\.2#_P_YQ@9._P`,.9)B5`98_A:9"PNI%DHYLLG^5ML+^$+H,O#PU.#@Y//^ +M#^G&TB(DPB(CT-#4P,#DT_P/R<:&)@#!2@'*R\(,`-(CQK(CK-J[EHLH/#ZW +M+@+&80#2!)J")"*R!<^2!)33^!JS^!23^!F"9U;R(_[B)!&"(ZSZ[H/^!"Q_ +MP_X*\_X0\4X!XF=5TB.MXB.NL@2?F%33_@BS_A&JCOKND_@4@F?)Z<;2(ZV2 +M(ZZR!)_3^0BS^1*29DR"(Z[R)1<@B*#R:&H=\/A"Z#+P\-3@X.3S_@_IQI(B +M7!;I,PN)%L@VHLG^5NJ6LB(4HB(3L+#4H*#DL_H/J<:2(C2"(C.0D-2`@.23 +M^`^)QO(CK=(CKN($G_/]"./]$M)F3,(CKK(E%R#,H+)L:AWP`-A"R#+0T-3` +MP.33_`_)QD9!_NC"V++@X-30T.3C_0_9QL(B++(B*\#`U+"PY,/[#[G&HB.M +M@B.ND@2?H_@(D_@2@F9,\B.NXB47(/^@XF]J'?```((B)/(B(X"`U/#PY(/_ +M#_G&QBO^(CKO($GX/^ +M"//^$N)F3-(CKI(E%R#=H))M:AWP`%:9H[)%YJ6A_J$U`$:+_@#HPMBRX.#4 +MT-#DX_T/V<;"(ZVB(ZZR!)_#^@BS^A*B9DR2(ZZ")1<@F:"":6H=\`#2(BS" +M(BO0T-3`P.33_`_)QK(CK9(CKJ($G[/Y"*/Y$I)F3((CKO(E%R"(H/)H:AWP +M#`I&>_X,#4:!_@`,#,9:_[($FO(D(I(%SX($E+/_&I/_%(/_&?)G5N(C_M(D +M$?(CK.K=\_T$#`[#_0KC_1#A3@'29U6R(ZW2(ZZ2!)^(5+/]")/]$:K]ZMV# +M_Q3R9\G9QK(CK8(CKI($G[/X")/X$H)F3/(CKN(E%R#_H.)O:AWPB"$,"PP, +M#!D,#O#I@]#)@^#,$.@1@+F##`C@B8/B!)*`[A#@NQ#`NQ"R1)*&%O[28D8@ +MHB#RH`_R8E3EG0'"(ZVB(ZZR!)_#^@BS^A*B9DR2(ZZ")1<@F:"":6J0``#B +M(B32(B/@X-30T.3C_0_9QH9&_XA"^#*`@-3P\.2#_P_YQM(CK;(CKL($G]/[ +M",/[$K)F3*(CKI(E%R"JH))J:AWP\B(DXB(C\/#4X.#D\_X/Z<;2(ZVR(Z[" +M!)_3^PC#^Q*R9DRB(ZZ2)1<@JJ"2:FH=\.(B%-(B$^#@U-#0Y./]#]G&PB.M +MHB.NL@2?P_H(L_H2HF9,DB.N@B47()F@@FEJ'?```((B-/(B,X"`U/#PY(/_ +M#_G&TB.MLB.NP@2?T_L(P_L2LF9,HB.NDB47(*J@DFIJ'?``#!J]`N71!:$U +M`(8"_@`V00!"I`Q*0K(DB@`[IC+2`0P/HJ9>JJ*""G7R0Y3R8Q;Y8X)#D^`0 +MI@`_IN#!Y>#9=/)#FM)D:N#A=.)D:\)C$;`0I@S]#-X,'+)C%)(C%((C%!R? +MDF,5A[](PDJ+XF,4XF,5@@I^%K@`DB,4PD.29JD"1AX`HB,4UQI+K0+E"0"" +M)&K2)&OR`Y_A3P"#_0CS_1+2;DS")&NR))4@S*"R;&H=\+(C%!:;!O<;7PN; +MD/`4\D.A\4T!XF,4D))!\)F@F`F98\;F_ZT"I8`!TB1JHB1KP@.?L4\`T_H( +MP_H2HFM,DB1K@B25()F@@FEJ'?""H0(`.*;P$*;R0Y3B`Y0,Z18>]\)C%I)C +M%`;:_])C%`;4_P`,R[)C%,;1_S9A`.(B53*@W$*D6$I"PB24TB)6.C+C_`C3 +M_!``/*:P$*:B`\4,JX"J$;"J(``ZII`0IH(D=`P*%J@+`#JF@!"FB>.8XU*F +M7E!2@+9)!8*@`8)%BY(C'9+)\Q;I";T"#`IEN072)&^B)%;!2@$\.]JJTB1P +MELH(IZL%+'Y&`0``RNKB#@"B)%;R!74,"=JJEFH'IZL$+'D&`0#*FI()`+(C +M*\(#N-(C'?/[%/%.`-/[%B)%BR`\/1-0##^@BS^A'!3P&Q3P#:VH/]%-)OR&(#DY/X"I(DKF/X"]"K@[(# +MG:/X#R"9H*(#GY(I;+/X$*/X$9/X%``XIL)#E,)C%LECH!"F>&6@L72@F70; +M=Q97.K)DKI)DK0P9@B4&\J$"H+'E%A@TTB3'H.`$"]W29,>R8Q&20YK"8Q7" +M8Q06O3CB0Y-Q3@#12@&A-0`\.^(C%&(%V&E!8D.284\`%IXY\B)4#(VV3P7V +MSP+&7P""(E3P(`"VR`H,'N)%Y>)C%,)C%?(B5>(#D_/]"./]#@`]IL(DK8(D +MKO(#DX)B4\)B4A;_`<(DL-(DKX(#G?#,$?#=$=)DK\)DL!9X`-(DK0O=TF,2 +MX!"FK0+B8F)E=@4,&KT")9L%PB3%LB2L/#K2),;*NY:;0[>J!"Q\Q@$`P4H! +MRLO"#`"R)*R"(F+:N](#DY8;0K>J!BQY1@(```"12@&:FY()`+(B5*(#E-/X +M%+/X%:/X&8)G5O(D_M(B48$U`.(DK/K=\4P!X_T$P_T*D_T0TF=5DB)%P@.= +MTB2NL@.?HB)2P_T0L_T1BHVC^`B3^!2"9\GB)*WZW>/]"-G&PB))R<:R(DVY +MQJ@RF$+"(E2@H.20D-0+O!:K.^+,_A:>.9/Z#ZG&B,+XLH"`U/#PY(/_#_G& +MPB,2DB,3L@.=H@.?P_D(L_D0H_D2DF9,D@.3XB,3@B,4%DD=(.Z@TB47PB2P +MLB2O^&/`P4&PL4&R9*_"9+#Y7P +M$*;R8Q@`/*:V3P0,&()%Y9`0II)C&<(C%X(C%>(C%O(#DX/]"((C&//]#N/] +M$,/]%(/]&)/]'``]IN(DKO(DK<(#D_)C$N)C$YS(C(H)C((DQX.X4XF,A@@78%M@$@@7;Z0$6V#L,'B8]!X@1TJ``@.V3 +M#!TF/`:((0P,@-R3#!PF.0:(,0P)@,F3#!DF/P:(`0P/@)^3\@.2D(P0\"`` +M\/X0\/T0@/\0\D.2K0*E800,&KT"97,%LB3%HB2LNJJ66C4\/*B8Q62(Q4+B!N9DF,4 +M@F3'PD.:QBG_`))#E;(E%\(C$^(C%-ACV7/I@R#,H+)L:AWP````/*:R9*Z2 +M9*V`$*:"9,=&$_\``)(#G5;9QK*A`P`[IJ`0IJ)#DT88_\)#D^T,#!G&]OX` +MG0P,#M(#D=)#DT;S_L(#DY%0`(#DX)C$O)C$YS>\B2P +M@B2OX@.=\/\1\(@1@F2O\F2PC&Z")*T+B()C$L`0II'#`,)C(@N9XB>#%EXA +M%EDA\@7F"YEF+^X,OP`_IN(DQ<(DK.K,EKPAQZL_+'S&#P""SN$6V">2SOH6 +M*2&Q2@$,W<+.^<"B0=)C%,#`%,)#H;++0+"JH*@*HF,&K0*E!0&&0?\,&0P> +MQLC^`-K,P@P`\B3&XB2L^NZ6WASGJP0L>08!`-J>D@D`@5P`TB,B\@.3X@.4 +MBMWS_13C_1G29U:R)/[R(Q&")*RZ_X/_!,/_"I/_$/)G5?A3X@.=@B2NT@.? +MLB,2X_@0T_@1T4L!JNBS_@CS_A3B9\FR)*W:B+/X"(G&!B'_#`P&]/X`#`F& +M^OZ3^@^IQL(B%+(B$\#`U+"PY,/[#[G&!AC_``"3^@^IQH85_[%*`;JJH@H` +MPB3&LB2LRKN6:Q4\/;>M!"Q[Q@$`P4H!RKNR"P`,3<$^`/(C(I(#DX(#E,K_ +MD_\4@_\9\F=6XB3^@B,1PB2LG0+JB,/X!*/X"K/X$()G5:A3\@.=@B2NPB,2 +MX@.?\_@0\34`X_@1X4P!^OC#_PBC_Q3R9\G")*WJB*T"P_@(B<;XD_G&Z-/B +M9@QVK2+H2=@YX.#4T-#DX_T/V<:R*EY+JA:+`"8;328K."8[!9+)(`;G_NAI +MV%G@X-30T.3C_0_9QLB)N'G`P-2PL.3#^P^YQHBI^)F`@-3P\.2#_P_YQD;R +M_XAI^%F`@-3P\.2#_P_YQL;M_\B)N'G`P-2PL.3#^P^YQD;I_P``5AG?#"W2 +M1>8E[_VA-0`\.]%*`89W_P``H.`$XD.3QC[_#`R&B?\,"4:._P#X00S(@F,4 +M%A_@LJ$"`#NFH!"FHD.4D@.4%OG>#.P,'=)C%L)C%(9X_PP*1J?_#`M&K?\` +M#`C8,0P)#!\,#.#/@]"?@]@AP)D0R!'0CX,,#<#?@\(#DM#,$,"($)"($()# +MDH85_ZT"#/[B8Q1EA@(&JOX``#9!`)*F"#*A'CHR@@.!FB*2`N:"R`&"0X&` +M@'27F`0,"()#@9(B$J(26I+)`9)B$I<:1;(B%;++`;)B%;<:0*'#`+%.`':` +M$M(#@<(K30NJUUP*C)KB`N(F+@R&^?_,:@PO\D+B9=_]D!"F@@+AS#BB`N*< +M&@PR'?"P$*8,(AWPP!"F##(=\``,7@`^IM`0IHR=\@+&)B_B`W\, +M`@P9H)G`DD-_'?`V80`,#%+2!D+2`PQW@B)$HJ"HJC*"R/X6R#>2`_72`^VR +M`_(663;20^L6>S<,%@P9X@/K@B2NTB47D_<*#!L,"=";@R"(H+(#]>/W"ZJ( +MD_FPD/LPF,LPF,R8R?R0_+"8RO"8RH6_BR0 +M@`2"0^MA3P`<:^(C*G(%W')#ZN<[`D8A`"SXY[@"1A\`''F7G@)&:@+!30$, +MW]+.Z-"R0?)C*M#0%-)#^<"[H+@+LF,2'R(QS2)+#")*\@[J"J[L#!0=#1 +M0=)DL,)DK](E&/)C'8)C'I)#[=)N0!WP#([2(RIQ3@"Q-0""S>H6*"7R(E0\ +M"(>?`L9/`A9-=#P,]SP"!F(`#!W21>G28RK28RO2(E7"`^O3_@C#_@X`/J:" +M)*Z2)*WR`^N28E*"8E.<8!`)%*`9J;D@D` +MXB)BLB)4T_X4L_X5@_X:\_X9XF=6TB)1@B4^LB2LVHBS^`3#^`J3^!""9U61 +M-0"!3`&R(D7R`_7B)*[2`_?"(E+S_A#3_A&:GL/Y"+/Y%))GR?(DK8KN\_X( +MZ<;2(DG9QL(B3`$*:2 +M),>"8RKB(RKB8RL+F9)DQ\)#\@9>_Y)#[;(E&,(C*>(C*M(C'-)C'>)C'B#, +MH*K,LFQ`'?#0@`16&%X,*9)C*L:%_P```#RFTF2NLF2MX!"FXF3'!D/_``"" +M`_56F-*RH0,`.Z:0$*:20^L&2/_"0^MM#`P91B;_`)T,#`;2`^G20^O&(O^= +M#`P&!B'_XF,J`#RF/?"0$*:28RP`/*8,R)>X`O)%Z8`0IH)C+0`\I@S-A[T" +M\D7IT!"FTF,N`#RFDJ`,U[D"\D7ID!"FDF,OTB,KP@/K@B,LT_X(P_X.TB,M +MPB,N@_X0T_X4P_X8D_X<`#ZF#,B7N`+R1>GR)*Z2)*WB`^N28RCR8RF(C.()C-HDQX.X4XF,W@@7<%I@$@@7?Z0$6&&4,'B8]!H@1#`V` +M[9,,'28\!H@A#`R`W),,'"8Y!H@Q#`F`R9,,&28_!H@!#`^`GY/R`^J0C!#P +M_A#P_1"`_Q#R0^JM`B4)!`P:O0)EW@3"),6R)*RBH*C*NY;K7#P]MZU!+'P& +M$0`,#(8U_PP)ACW_```6J5P+Z1:.8/+)_E;OO,A"N#+`P-2PL.3#^P^YQI(B +M)((B(Y"0U("`Y)/X#XG&QNK^P4H!RLO"#`#2),:R)*S:NY:;5SP^MZX&+'U& +M`@```-%*`=K;T@T`D3X`XB,X@@/K\@/LFNZ#_A3S_AGB9U:R)/Z"(R>2)*RZ +MB+%,`9/X!,/X"M/X$()G5?(#]8(DKL$U`.(#]_/X$/(C*./X$'2*5H6+0T+C198"&8MTXA,^#R`@-3P\.2# +M_P_YQN(L)-(L(^#@U-#0Y./]#]G&B&SX7("`U/#PY(/_#_G&XBPFTBPEX.#4 +MT-#DX_T/V<:(C/A\@(#4\/#D@_\/^<;B+"C2+"?@X-30T.3C_0_9QHBL^)R` +M@-3P\.2#_P_YQN(L*M(L*>#@U-#0Y./]#]G&!M3_@BPD\BPC@(#4\/#D@_\/ +M^<;B+";2+"7@X-30T.3C_0_9QH(L*/(L)X"`U/#PY(/_#_G&XBPJTBPIX.#4 +MT-#DX_T/V<9&PO\`B$SX/("`U/#PY(/_#_G&Z&S87.#@U-#0Y./]#]G&B(SX +M?("`U/#PY(/_#_G&Z*S8G.#@U-#0Y./]#]G&1K+_`-(I6A8M!R8=2N+-_E:> +MZ^A,V#S@X-30T.3C_0_9QH(L)/(L(X"`U/#PY(/_#_G&Z&S87.#@U-#0Y./] +M#]G&@BPF\BPE@(#4\/#D@_\/^<:&G?\``.(L)-(L(^#@U-#0Y./]#]G&@BPF +M\BPE@(#4\/#D@_\/^<;&D__H3-@\X.#4T-#DX_T/V<:(;/A<@(#4\/#D@_\/ +M^<:&B_\``-(I6A8M!R8=2O+-_E;?X?A,Z#SP\-3@X.3S_@_IQM(L)((L(]#0 +MU("`Y-/X#XG&^(SH?/#PU.#@Y//^#^G&TBPH@BPGT-#4@(#DT_@/B<:&=O\` +M`/(L).(L(_#PU.#@Y//^#^G&TBPH@BPGT-#4@(#DT_@/B<;&;/_X3.@\\/#4 +MX.#D\_X/Z<;8C(A\T-#4@(#DT_@/B<:&9/\``-(I6KSM)ATH@LW^5BC8B$SX +M/("`U/#PY(/_#_G&XBPDTBPCX.#4T-#DX_T/V<;&5__B+"32+"/@X-30T.3C +M_0_9QL92_XA,^#R`@-3P\.2#_P_YQD9._P#"8RJ"`_*2`^O2(ROR!=_R0^K3 +M_@B3_@Z#_@\`/J;2)*[R)*V2`^OR8RC28RFL"9(DL,(DKX(#]?"9$?#,$<)D +MKY)DL!:8`,(DK3WP"\S"8RC@$*;2),?B8SB632>1PP`]\`N9\B>#%A\:%AD: +M@@7J"YEF*.X,N0`YIM(DQ<(DK-K,ENP:/#['KCXL?$80```,/_)C*D8-_A99 +M"PN)%H@@LLG^5IL+^$+H,O#PU.#@Y//^#^G&TB(DPB(CT-#4P,#DT_P/R<:& +M)0#12@':S,(,`.(DQM(DK.K=ELT5/#_7KP8L>48"````D4H!FIV2"0#R`^N" +M(SCB`_+2`^SS^!3C^!K3^!F"9U;R)/[2(R?B)*SZW>/]!,/]"I/]$-)G58(# +M]?(DKN(#]](C*(/_$./_$8(C&[KOT_X(T4X!@_X4XF?)@B2MVO^#_PCYQD:_ +M_0``F$*(,I"0U("`Y)/X#XG&DB)<%ND2"[D6&Q4F*0+&MOWR(A3B(A/P\-3@ +MX.3S_@_IQM(B-,(B,]#0U,#`Y-/\#\G&AJW]F,*(LI"0U("`Y)/X#XG&!JG] +MPB(DLB(CP,#4L+#DP_L/N<8&%_[B(BS2(BO@X-30T.3C_0_9Q@:?_0S(@F,J +MS!?&F?T`/::P$*:R0^R2`^S,&8:5_?)C+`SLPF,JQI+]``#"8D:M`@S]TF)4 +M9=`!HJ"H1I#]`%99Y@PNXD7JY2[]HJ"HL34`!I7_D(`$@D/KQ@_^#`R&GOX, +M#0:E_@`,#,:E_PP)1JS_N$*8,K"PU)"0Y+/Y#YG&!G_]#`V8,0P/#!P,".", +M@Y#\@Y@A@/\0B!&0W(,,"8"<@X(#ZI"($(#=$/#=$-)#ZH9O_L(B)+(B(\#` +MU+"PY,/[#[G&QFW]XB(4TB(3X.#4T-#DX_T/V<;&:/V"(B3R(B.`@-3P\.2# +M_P_YQH:H_[(B-)(B,["PU)"0Y+/Y#YG&QE[]``P:O0*E=02BH*BQ-0#&9?\` +M`#9!``P+,M(!@B)$0J8D4J.\6E)*0NCD@LC^%B@6D@.=#!K2`Y46^13"(DB= +M"])#D\":@Z(#DPP=#`P,>)/X"N#-@Z/X"^(#G:(E?\/X#\(#GR"JH*(J;./X +M$,/X$:/X%``XIKECLF,6LD.4H!"FXJ$"\@.=LD.:H,`$%F\.PD.3`#NFH('E +MH)ETH+%TLF5_DF5^@F,1\!"F#/L,W/)C%)(C%((C%!R?DF,5A[]WTD3%PF,4 +MPF,5P@2X%KP`\B,4TD.2\L_T%C\,@B,4L(C`%N@)(*(@)0X`H4\`TB,2DB,3 +MP@.=L@.?T_D(P_D0L_D2DFI,D@.3\B,3HB,4%JD$(/^@XB0/TB6!PB6`@B,& +MT-%!P,%!PF6`TF6!B7.I@Y)#E>)O:AWPHB,4%BH(]QIY"_KP@!2"0Z&!30'" +M8Q3P\D&`_Z#X#_EC!MO_DD.5LB,3R&.H]-(C%-F#R7,@NZ"B:VH=\````#ZF +M\!"F\D.3QL/_K0(EJP&&U_^R0Y,,&8:L_YT+@@.1@D.3QJG_```^IJ`0IJ)# +ME)(#E`SL%JGRTF,6PF,41LC_``"R8Q3&P?\,S_)C%,:__P``-D$`DB)6PB)5 +M#(@RT@&R`Y.B`YK#^`BS^`ZC^`^3^!``.*9"I%B"`Y-*0I(D6*(D5Z)C$I)C +M$YS8XB1:\B19T@.=\.X1\/\1\F19XF1:C&WR)%<+__)C$K`0IJ(#H0RK@*H1 +ML*H@`#JFD!"F@B1T#`D6.`P`.:;`$*;)4X(C%-A3\M(&@LCSMDT%XJ`!XD_I +M%I@*(+(@HJ``I4\$T4H!LB1OHB16PJ`SXB1PNJJ6>@FGK`0L>P8!`-JZL@L` +MHB16\B,B@@.4ZJKA3@"6Z@>GK`0L>08!`-J:D@D`P@.3HB,4P_\4H_\5@_\9 +M\FY6TB2HHB,1PB16VJK1-0##^@2S^@J3^A"B;E6"`YVB)%C!3P'R`Y^#^A"" +M(Q+S^A':VOA3RJJ#_0CS_132;LG2)%?!3P#3^@BIS!WPF5.&S_^M`J5E!(;5 +M_P````P+QMO_#`D&XO\``#9!`$*D6$I"HB1OP4H!/#N6"@RGJP4L=T8!``#* +M>G('`*(D<)8:"Z>K!2QV1@$``,IJ8@8`LB)5HB244J#<6E*S^@@`.J:0$*:" +M)'$Q3@"2IE_6R`*:(I'#`':`#H(C@PN9C*B,J:("BR8J#8;Z_P#,:0PKLD*+ +MY>G\#+P`/*9&``":(I%1`>(E*X("=/(%N)KN@_X4@34`\_X9XF-6TB2HPB4: +M\5(!X4\`VLQS_`IC_!#"8U6R)%?2)%BB!<.8Y;/]"*/]$8J-^MV3^!2"8\G9 +MSAWP#`?&T?\,!H;5_P``-D$`0J182D*B)&_!2@$\.Y8*#*>K!2QW1@$``,IZ +M<@<`HB1PEAH+IZL%+'9&`0``RFIB!@"R(E6B))12H-Q:4K/Z"``ZII`0IH(D +M<3%.`)*F7];(`IHBD<,`=H`.@B.#"YF,J(RIH@*+)BH-AOK_`,QI#"NR0HOE +MV_P,O``\ID8``)HBD5$!XB4K@@)T\@6XFNZ#_A2!-0#S_AGB8U;2)*C")1KQ +M4@'A3P#:S'/\"F/\$,)C5;(D5](D6*(%PYCEL_T(H_T1BHWZW9/X%()CR=G. +M'?`,!\;1_PP&AM7_```V00!"I`Q*0K(DB@`[IC+2`0P(HJ9>JJ*2"G6"0Y2" +M8Q:)8Y)#D^`0IAQ?@D.:`#^FX,'EX-ETTF1JX.%TXF1KPF,1L!"F#/T,W@P< +MLF,4DB,4@B,4')^28Q6'OT;"2HOB8Q3B8Q6""GZ,>)(C%,)#DB:I>Z(C%#WP +MUQI+K0+E"0"")&K2)&OR`Y_A3P"#_0CS_1+2;DS")&NR))4@S*"R;&H=\+(C +M%!9[!O<;7@N;D/`4\D.A\4T!XF,4D))!\)F@F`F98T;G_ZT"9>7_TB1JHB1K +MP@.?L4\`T_H(P_H2HFM,DB1K@B25()F@@FEJ'?`<>``XIO`0IO)#E.(#E`SI +M%@[WPF,6DF,4QMG_TF,4QM3_#,NR8Q3&TO\``#9!`.(B53*@W$*D6$I"PB24 +MTB)6.C+C_`C3_!``/*:P$*:B`\4,JX"J$;"J(``ZII`0IH(D=!QI%F@+`#FF +M@!"FB>.2(QU2IEZHXUI2DLGSMDH$#!B"18L6V0FM`J4A!-%*`;(D;Z(D5CP\ +MXB1PNJJ6"@FGK`4L>T8!``#:NK(+`*(D5O(%=0P)ZJJ6J@>GK`0L>08!`-J: +MD@D`PB,KT@.XXB,=\_P4\4X`X_P5T_P9PF]6HB2HXB,:@B16JNZ#_@2S_@J3 +M_A#B;U6(X](D5Z(D6,(#P^$U`-/Z",/Z$=%/`<%/`.KJ@_X4XF_)VJJIS!WP +M#`B)XT;2_ZT"Y2\$AM?_````#`O&W?^&X_\VH0!"T@/R),T`/Z92T@8RT@'B +M!=/B0Y,,#N)#E.)B5N)B1J`0IAP]`#VFH,ETH+%TLF2NPF2MH*'EHF)1D!"F +MDD.:@@.:%DA"XF)4XF)5XF3'84X`T4H!<4\`H34`/#O"(E0,'_)#DNS,P:P` +MDB3JRID`.::`$*:1PP""8R)V@!/B)H,+F19N%Q99%_(%ZO+/_A;/%T;Y_](C +M%/9-`H8O`;9L`@9A`-+-^PP,T,^#TJ`1PD.IEDORA-0`\.]%*`0R_`#^FXB3%PB2LZLR6'$['JVT8"````L4H!NKVR"P#1/@"2 +M(R+B!=/:F=(#E./Y%-/Y&9)F5H(D_O(C$8K_@B2L@_\$P_\*L_\0\F95XB2M +M@B2NT@.?D34`X_@(T_@1V%.:F*J(T_D4#$V29LF)QYT"^)/YQ_T"Z-/IQW:M +M$Z(I7DN9%GH%)AI()BHY)CHB\L\@DB2M\B2N@@.?D_\(@_\2\F=,XB2NTB38 +M(.Z@TFYJ'?"X/[G'J%^IQXA_BT8L``#Q7`#:G+(C(M(%T\(#E/J[T_L4 +MP_L9LF96@B3^TB,1\B2LD@D`BMWS_03C_0J3_1#29E7")*VR)*[X4X(#G\/[ +M",%+`8/[$:K;\_T4TF;)RKNYQX(DK=(DKO(#GX/]"//]$M)G3,(DKK(DV"#, +MH+)L:AWP`*(C%9(E*J/Y"``YIH`0IB"B(()C(J6E`R"B("6_`[(DK*A5NJJ6 +MFB,\/*<8!`)%* +M`9J:D@D`TB,4@B,BP@73H@.4T_@5P_@4H_@9@F96\B3^TB,1XB2L^MWC_02S +M_0J3_1#29E7")*V")*ZB`Y_Q3`'#^`BC^!&A-0"2(D7Z^*J(D_@4@F;)^<8! +M`)%*`9J:D@D`TB,4@B,BP@73H@.4T_@5P_@4H_@9@F96\B3^TB,1XB2L^MWC +M_02S_0J3_1#29E7")*V")*ZB`Y_Q3`'#^`BC^!&A-0"2(D7Z^*J(D_@4@F;) +M^")*W2)*[R`Y^#_0CS_1+29TS")*ZR)-@@S*"R +M;&H=\`P,1N?^#`M&[?X`^&'H40P8#`S8<>#(@PP.T.B##`WPV(/@W1#H00P/ +MX/B#X@.2\.X0X,P0T,P0PD.2QHC^``P+QG?_#`F&??\,"P:C_P`,"8:H_YG' +MTB(3VR +M)*V")*ZB`Y^S^`BC^!*"9TSR)*[B)-@@_Z#B;VH=\`"9QZ(DK?(DKH(#GZ/_ +M"(/_$O)G3.(DKM(DV"#NH-)N:AWPF8`B)$ +M?V*E/&IEDB9%@A;`&YF29D67&'.8TX(32AN9F=.7&&^APP"Q3@!V@!+2!'_" +M*TT+JM=<"XRJX@/")BX-AOG_`,QJ#"_R0\)E(_S"!'\,>O(F2+(F'PP>#`U0 +MNZ!ZN_#>@]/Z#Z)F/L/Z$:)F/K(K)+/Z%*)F/I`0IH(#P(D5](4WAONXF17X-W`%AT-P@M_TM)MTBTTV8.8@X(C$@P5P_D,F8/H@PP/ +M@/6#\_X/Z8-0$*90T'329"[")"Z2(Q,@S*"JS))L(H(D+OB#((B@JHB"*"3! +M3@"APP"#_Q#Y@W:`$O(+?^(L30NJ]UX*C)J"`]8F*`R&^?_,:@PIDD/691'\ +MH@/6%IH$##)0N650R'3"9"U@NQ"R9"P=\,`0I@`WIO`0IM(D6/"`=/"X=/"0 +M!9)D1[)D+8)D+N(D+O#Y96#_$"#NH/)D+`PBJN[2;B(=\```-Z;0$*96S?H, +M`AWPT!"F`#>F@!"FXB18@)!T@,AT@+`%LF1'PF0MDF0N\B0N@(EE8(@0(/^@ +M@F0L##*J_^)O(AWP`#:A`%+2`S(ES0`SI@P,,M(!0M(&\@33\D.3PD.4PF)& +MPF)6H!"F'#X`/J:@L72@V7329:VR9:Z@H>6B8E&0$*:20YJ"`YH<61:('L)B +M5<)B5,)EQW%.`-%*`6%/`*$U`#P[XB)4\@3<\D.2%IXITB,4'%C7.`KV368, +M'=)B5`8<`(+.ZA;H02SYU[D"!ED`''FBS>G,&L;]`<%*`?*@#>+-Z."R0?)C +M%.#@%.)#H<+,0,"[H+(K`+ECK0+E./^2):WR):Z"`Y^3_PB#_Q+R9DSB):[2 +M)=@@[J#2;FH=\```!^X(#"W28E2&`0``XJ`#XF)4DB,5@B0JD_@(`#BF\!"F +M(*(@\F,B90,#(*(@)5L#N%3"):P\.M(EK,J[EBLQMZH%+'P&`@``P4H!RLO" +M#`"X9((C(MJ[TB,4EJLOMZH$+'G&`0"12@&:FY()`+($TZ(#E-/X%;/X%*/X +M&8)G5O(E_M(C$>(EK/K=X_T$P_T*D_T0TF=5LB6M@B6NH@.?\4P!L_@(H_@1 +MH34`DB)%^OBJB)/X%()GR?G&XB))Z<;2(DW9QJ(B5)(B6@NZ%@LIXLK^%FY$ +M%AEM"_D6#RZ"R?X6:"V2(EL6^78+J1;J([+)_A;[(((EK>(EKO(#GX/^"//^ +M$N)F3-(EKL(EV"#=H,)M:AWPDL[0%BEVLB0JPJ`!PD3IP_L(PF,4PF,5`#NF +MH!"FHF,B(*(@)?("K0(E2@.R):RH5+JJEBII/#RGK!PL>\8'```YIO`0IGS] +M\F)4XB)4XF)5TF7'!H+_`+%*`;JZL@L`PB6LJ&3*JI::93P]IZT$+'G&`0"1 +M2@&:FI()`*(C%.(C(H($T_(#E*/^%8/^%//^&>)G5M(E_J(C$<(EK-JJP_H$ +ML_H*D_H0HF=5DB6MXB6N@@.?T4P!D_X(@_X1@34`\B)%VMZ*[O/^%.)GR=G& +MPB))R<:B(DVIQI(B6A9);`NY%CMNPLG^5ESON#*YQJ(B(ZG&DB6M\B6N@@.? +MD_\(@_\2\F9,XB6NTB78(.Z@TFYJ'?``\B,5DB7JX@.:\_D(X_D/`#FF@!"F +M@F,BPF,4XB7'\@3?\D.2EAYTD<,`"YF")X,6F%,6F5/"!.H+F68L[@R^`#ZF +MXB7%PB6LZLR6S%;'JP0L?@8!`-KLX@X`\B7&PB6L^LR6K%7'*P)&O0#2!-.2 +M(R+"`Y2R`YK3^13#^1FS^1J29U:")?[R(Q&2):R*_Y/_!"QXX_\*@_\0@4X! +M\F=5TB6M\B6NP@.?N%/3_PC#_Q&JGXK_L_D4DF?)^<;2):VR):["`Y_3^PC# +M^Q*R9DR2):Z")=@@F:"":6H=\.BRZ<;"(BO)QK(EK9(EKJ(#G[/Y"*/Y$I)F +M3((EKO(EV""(H/)H:AWPPB(KR<:R):V2):ZB`Y^S^0BC^1*29DR"):[R)=@@ +MB*#R:&H=\`P,!C[_``P)QD/_````%DE>)ADNTLG^5GW8Z#+IQL(B(\G&LB6M +MDB6NH@.?L_D(H_D2DF9,@B6N\B78((B@\FAJ'?#"(B/)QK(EK9(EKJ(#G[/Y +M"*/Y$I)F3((EKO(EV""(H/)H:AWPV#+9QN(B(^G&!D?_'!T,B9)C%``]IH`0 +MIH)C%@`]I@S/A[\%XJ`!XD3I@!"F@F,7`#VF#,^'OP62H`&21.GP$*;R8Q@` +M/:8,SO>^!8*@`8)$Z>`0IH(C%>)C&?(EZI(C%](C%H/_"((C&-/_$)/_%(/_ +M&./_'``_I@S-Y[T$#!F21.F`$*:"8R+B(R+2(R*2(R+R(R+@XA3B8QN"(R+0 +MU!328QSP^!3Y0?)C'H"*%(E1@F,?\B,B@B,BD)84DF,=@(P4@F,@B6&"(R+P +M\!3R8QJ`CA2)<8)C(8($W!;H!8($W_DQ%BA'#!_I(8@QV1&9`28X!MA!#`G0 +M^9.((0P>)C@'V%&2H`#0Z9.($0P=)C@(@B$&DJ``@-F3B`$,&28X"HAQ*8$, +M`H"2DRB!@@.2@(\0@.X0D(T0@.X0XD.2K0+E8`*M`B4.`[(EQ:(EK+JJECH_ +M/#RG+`+&.0`L>H8Z````%LD^"]D6O3CBR?X6'CB2(EP6N4,F&3#RR?Y6K[N" +M(A.)QN(B,^G&TB6MLB6NP@.?T_L(P_L2LF9,HB6NDB78(*J@DFIJ'?``XB(S +MZ<;2):VR):["`Y_3^PC#^Q*R9DRB):Z2)=@@JJ"2:FH=\/($T]JV<8&[?\` +MHBE:K&HF&A=F*JB(/HG&\BXC^<;87MG&PBXER<;&Y/_"+B/)QJ(N):G&AN'_ +M^#[YQMA>V<;&WO^B*5JLJB8:&X+*_E;8]O@^^<;2+B/9QLA^R<:B+B>IQ@;6 +M_P"B+B.IQH(N)XG&AM+_V#[9QLA^R<;&S_^B*5J<6B8:"_+*_E8?\X(N`X)F +M#*(N(ZG&QLC_R#[)Q@;'_P!6V:P,+=)$ZB6`^Z$U`#P[T4H!AJ[^Z#+IQD9, +M_@S(@F,4%O^!`#FFL!"FLD.4H@.4S!J&`_X,[`P=TF,6PF,41@#^``P)AFS^ +M#`M&9/X,#H:F_@#R!-.R(R+2`Y3"`YKS^Q33^QG#^QJR9U:")?[2(Q'R):P, +M"8K=\_T$X_T*D_T0TF=5PB6MLB6N^%."`Y_#^PC!3@&#^Q&JV_/]%-)GR(EKO(#GX/^"//^$N)F3-(EKL(EV"#=H,)M:AWPLB(CN<:B):V"):Z2`Y^C +M^`B3^!*"9DSR):[B)=@@_Z#B;VH=\`P*AC__#`P&1O\`R#+)QH8%_P"(80P. +MR'$,&0P/P/F#@.F#\.X0^%$,#"!;>B91N291R(D6/%2`9(%PZ/^$*(E&Y/^$8J.F.7Z[J/X")/X%()CR8(D5_%/ +M`(/^".G/'?`,!X;"_PP&1L;_``PIDDJ`I4W[QN'_`#9!``P),M(!0J8D4J.\ +M@B)$6E)*0H+(_A;(%J(#G0P;T@.5%IH5PB)(K0G20Y/`JX/(Y`P=#'BR`Y.C +M^`JB)7^S^`L,"R"JH,"]@[/X#\(#G;(#GZ(J;,/X$+/X$:/X%``XIIECDF,6 +MDD.4H!"FX@.=H/`$\D.3W"[B`YT<3P#N$?#N(``^IL`0IL)#DQQ8`#BFH,'E +MH.%TH/ET\F5^XF5_DD.:PF,1L!"F#-RR8Q2"(Q3R(Q0,^X)C%19O#:(C%!R) +MISD:"^K@\!3R0Z'Q30'"8Q3@XD'P[J#H#NECA@,`@LKG%A@-TD3%PF,4PF,5 +MD@2XC*FB(Q320Y*BRO06N@G"(Q0]\+<<;""B(&4+`.%/`)(C$M(C$X(#G?(# +MGY/]"(/]$//]$M)N3)(#D[(C$](C%!9I`B"[H*(D#X(E@?(E@,AC@(%!\/%! +M\F6`@F6!R7/9@Y)#E:)K:AWP`))#E;(C$\ACJ/32(Q39@\ES(+N@HFMJ'?"M +M`J49``;D_P```))#DPP:!JK_K0GR`Y'R0Y-&I_\`#,B"8Q2&U/\T8!``#:NK(+`*(D5O(C(H(#E.JJ +MX4X`ENH'IZP$+'D&`0#:FI()`,(#DZ(C%,/_%*/_%8/_&?)N5M(DJ*(C$<(D +M5MJJT34`P_H$L_H*D_H0HFY5@@.=HB18P4\!\@.?@_H0@B,2\_H1VMKX4\JJ +M@_T(\_T4TF[)TB17P4\`T_H(JK!2QW1@$``,IZ<@<`HB1PEBH-IZL'+';&`0````#*:F(&`*(D +M6)(D5X(%MY)E&Z)E')RXPB1:TB19L@7!\,P1\-T1TF19PF1:C$O2R?_291OP +M$*;B)'$Q3@#6;@*1PP"BIFJJHG:`#[(C@PN9C+L6F0?""H`F+`9&^O\`%LD& +M#+T`/::"!;?2)2OR)1WB!;B#_13S_17C_1G28U;")*BR)1J!-0#*NW/["F/[ +M$+)C5:(%P>(D6/%2`9(%PZ/^$*(E&Y/^$8J.F.7Z[J/X")/X%()CR8(D5_%/ +M`(/^".G/'?`,!X;)_PP&QLW_``PIDDJ`)0K[QN'_`#:A``P-,M(!0M(##'^" +M(D12IB1:4H+(_A882I(#G0P:P@.5%EE(LB)(G0W"0Y.PFH."`Y.XY9/_"@P: +M#`FPFH.#_PNB`YV")*Z3_P^2`Y\@B*""*&RC_Q"3_Q&#_Q0`/Z;98])C%M)# +ME+`0INA#P@.:L)%T%MX]H@.0HD.:DF2N\@.='$X,&19?/@P*C$R"`YJ`J8,6 +MBD`,"LA#PLS^%BQ!@@.=`(@1X(@@`#BF\!"F\D.3V4.\.H%7`0`XIO`0ILA# +M\D.0H@.0&\S)0]S*@@.=`(@1X(@@`#BF\!"F\D.3J$/"`Y/"0Y$;JJE#X@.: +ML,'EL*ET%EXXTF)5TF)4HF2MTF3'PF)1#(QQ3@!A3P"A-0`\.^(C%/(%N/F! +M\D.2\4H!%CXZ\B)4MD\%]L\"AET`@B)4\"``ML@*#![B1<7B8Q328Q7B(E;2 +M(E6"`Y/R`YK3_`B#_`[S_`_C_!``/*:")*[2)*WR`Y/28E*"8E,6_P'R)+"" +M)*_B`YWP_Q'PB!&"9*_R9+`6?@"")*T+B()C$I`0IJT"DF)B93H"K0+E4P+" +M),6R)*P\.M(DQLJ[EEM$MZH&+'Q&`@```,%*`48"````D4H!FIN2"0"R(E2B`Y33^!2S^!6C^!F"9U;R)/[2(E&! +M-0#B)*SZW?%,`>/]!,/]"I/]$-)G59(B1<(#G=(DKK(#GZ(B4L/]$+/]$8J- +MH_@(D_@4@F?)XB2M^MWC_0C9QL(B2"(QC3_Q"3_Q2#_QCC_QP`/Z:V3@0,'=)%Q?(DKI(DK>(#DY)C$O)C +M$YR^TB2PXB2O@@.=\-T1\.X1XF2OTF2P%D@`"^GB8Q*`$*:"8R+B(R+2(R*2 +M(R+R(R+@XA3B8QN"(R+0U!328QSP^!3Y0?)C'H"*%(E1@F,?\B,B@B,BD)84 +MDF,=@(P4@F,@B6&"(R+P\!3R8QJ`CA2)<8)C(8(%N!;X!8(%N_DQ%O@V#!_I +M(8@QV1&9`28X!MA!#`G0^9.((0P>)C@&V%$,"=#IDX@1#!TF.`:(80P)@-F3 +MB`$,&28X"HAQ*:$,`H"2DRBA@@.2R9$]\("/$(#N$)"-$(#N$.)#DLF1K0*E +M(P&M`N4K`L(DQ;(DK*B1RKN6.R\\/;2ZJ\>R8;*28K&R8[!9+)((;H_N@Y +MZ<;86=G&R'G)QKB9N<:&^?^(.8G&^%GYQL;V_\@YR<:X>;G&!O3_`-@YV<8& +M\O\`\L[A5J^VK0(,^()C%.5]_T;7_@!6V>(,*9)%QB69^J$U`#P[\4H!AH;_ +M#`R&F?\`#`D&GO\,#(:\_PP)!L/_`.AAV%$,'PP(F''0CX,,#9#?@PP)X)^# +MT)D0V$$,#M#O@](#DLF1X-T0T(@0D(@0@D.2ABS_Z($,S_)C%!:>X!QZ`#JF +MD!"FDD.4@@.4%HC?#.L,',)C%K)C%,9Z_P```#9!``P$DJ8@,J$>.C*"`X&: +M(I("SH+(`8)#@8"`=)>8`D)#@8C"HA).&XB)PH<:1)CR&YF9\I<:5J'#`+%. +M``P5=H`2T@.!PBM-"ZK77`J,FN("RB8N#(;Y_\QJ#"_R0LKEBOJ0$*:"`LG, +M.*("RHR:##(=\+`0I@PB'?#"`W^8`M)$IQP.PJ#[/+>8\U+2!8(35!N9F?.0B,`6*`SR)5>R%=X;__)E5_"[ +MP!:K$I($I[+2;;(K-+F#B(-B(Q(,'Y/X#(F#F(,,"V"_@[/Y#YF#8!"F8(!T +M@F4N\B4NLB,3(/^@JO^R;RR2)2Z(@R"9H*J9DBDNL4X`H<,`D_@0@F,(=H`2 +M@@2G\BM-"ZJ'7PR,NI(#UB8I%8;Y_P``S-H,*J)#UF5Z^L*@^PP-'`ZR`]7, +M2_(#UA:_!H($I2B#8+EE8)'E8*ATHF4MP)D0<+L0LF4LDF05@_(8*8,,,AWP +M``#0$*8`/J;0$*;0D>70N'2R92W`F1#0N65PNQ#0T'3292Z")2ZR92R29!4@ +MB*"JB"(E6")H+/($I=B##"+S_1C9@QWP`/($I18O"``^IH`0IA:(!@PRH@2E +MF(-@Z65@L>5@V'3292W`NQ!P[A#B92RR9!6C^1B9@QWP\!"F`#ZF\!"F@B58 +M\+'E\-ATTF4MP+L0\-EE<-T0\/!T\F4NDB4NTF4LLF05()F@JIF":2PB!*7X +M@R/_&/F###(=\-)$I2B#@@2E@_(8*8,,`AWPF(,,&[)$I:($I0P"H_D8F8,= +M\#:A``P,#!U"T@."(D12IB2BH*BJ,EI2Z.6"R/X6F$F2`_6R`^T6^4>2`_*R +M0^L,>+(#ZY/X"I(DKK/X"PP+X+V#()F@T@/UL_@/JIFR`_>2*4+3^!"S^!&3 +M^!0`.*;"0^S"8RS"8QS0$*;R(QKB`_+0D706;S/R`^CR0_*29*Z"`_4<3PP9 +M%N@S#`L67@#B`_+@N8,6VS4,"X(C&H+(_A;80((#]0"($?"((``XIN`0IN)# +MZ\)C&KR;L57R`_(<6]#9=!9?+<)B5<)B5-)DK<)D +MQ^)B40R-<4X`84\`L34`/#^"!;CB(RJ)@8)#ZA9^.?(B5!Q9]SD/@B)5]D]) +M#!S"8RH&%0```(+.ZA;X2BSY][D"AG8`''NWG@)&%@+!2@$,W]+.Z-"R0?)C +M*M#0%-)#^<+,0,"[H+@+LF,(D +MK/K,X_P$T_P*D_P0PF=5D@/U@B2NPB)%\@/WD_@0D34`XB)2\_@1FICC^0C# +M^1229\F2)*VZB)/X"(G&\B))^<;B(DWIQK(B5)(B6@O+%FPT@LO^%LA.%KEM +MLLG_%CMNPLG^%MQNDB);%@EMTLG_%IUMXLG^%GYNLB,H\B,ID@/U@@/WL_\( +MD_\0@_\2\F9,D@/KTB,I\B,JK(GB(QS")+"R)*\@W:"JW;"Q0<#!0<)DL+)D +MK\CUXF,=\F,>DD/MPFU`'?"20^W8]>(C*8(C*O(C'/)C'8)C'B#NH*KNTFY` +M'?```/+.T!8/;?(#ZX*@`8/]"()%Q8)B5>(B5H)B5//]#N/]$``]II(DKL(D +MK8(#Z\)B4I)B4Q;H`<(DL-(DKY(#]?#,$?#=$=)DK\)DL(QITB2M"]W28RC@ +M$*:M`N)B8J4[`:T"I9,!TB3%PB2LHJ"HL4P!VLR6K&,\/L>N5RQ]AA8`@@/U +M'#L`B!&PB"``.*:29*[P$*;R0_+&+?\`L@/R#`ZPZ8-67LP&"``````[IGS_ +MTF2MXF)1\F3'@!"F@F)4\B)4\F)5QD;_#`O0@`2"0^O&+/_12@':W-(-`.(D +MQL(DK.K,E@Q=/#_'KP0L><8!`)%*`9J#_Q"! +M-0#"(E+C_Q&*C\/X")/X%()GR8(DK;K_@_\(^<;B(DGIQL(B3)#ZX;^_IT,\@/I +M\D/K1MK^PF,J@B,KD@/RX@/K@_T(@B,LX_T.D_T/@_T0`#VFDB2NXB2M@@/K +MXF,HDF,IK`C")+#2)*^2`_7PS!'PW1'29*_"9+`6F0#2)*T]\`O=TF,H@!"F +MXB3'@F,XELY5D<,`/?`+F<(G@Q:<0Q:90](%Q@N99BWN#+X`/J;2),7")*S: +MS)9L2L>O!"Q\Q@$`T4H!VLS"#`#B),;2)*SJW98-2=>O!"QYQ@$`D4H!FIV2 +M"0#R`^N"(SCB`_+2`^SS^!3C^!K3^!F"9U;R)/[2(R?B)*SZW>/]!,/]"I/] +M$-)G58(#]?(DKN(#]](C*(/_$./_$8(C&[KOT_X(T4X!@_X4XF?)@B2MVO^# +M_PCYQ@8[_P`,#<8,_PP)1A+_```6Z4,+B18(1++)_E8;S=@RV<;"(B/)QD8Q +M_QP`0IN)C+0`\I@S)Y[D%\J`! +M\D7%D!"FDF,N`#RF#,B7N`0,',)%Q8T-\!"F\F,OXB,KD@/RP@/KX_@(XB,L +MP_@.D_@/PB,MDB,NX_@0P_@4D_@8\_@<`#BF#,[WO@0,'_)%Q<(DKI(DK8(# +MZY)C*,)C*1;(`?(DL((DK^(#]?#_$?"($8)DK_)DL!9.``N)@F,H@!"F@F,X +MXB,XPB,XDB,X\B,XX.(4XF,Q@B,XP,04PF,R\/@4^4'R8S2`BA2)48)C-?(C +M.((C.)"6%))C,X",%()C-HEA@B,X\/`4\F,P@(X4B7&"8S>"!;@6Z`6"!;OY +M,1;8,PP?Z2&(,O/2Q^!A```)PI +M"XD66"IF*0^X,KG&DB(CF<;&``#(,LG&DB)<%DDH"]D6_2CBR?Y6CK""(A.) +MQO(B,_G&QK[^X4H!ZNWB#@#R),;2)*SZW99=)#PXUZ@$+'W&`0#Q2@'ZW=(- +M`)(#Z_(C.((#[)/_%,/_%8/_&?)G5L(D_I(C)\J9PB2LP_D$X_D*T_D0DF=5 +M@@/UPB2N\@/WT34`@_P0\_P1@B,H\B,;VMR#_0CS_1329\G2)*VZS-/\"-T" +MR<:2(Q_-`IG&#$F"(R.)QH8+`&8K(X@]B<;R+2/YQNA=Z<:R+26YQHA]B<;R +M+2?YQNB=Z<:R+2FYQM+-($O,%KFCLBQ>"YD6^PHF&W`F*S5F.^:R+%JG&N)VYQ@;M_P"R +M+%JL:R8;%V8KJ(@]B<;R+2/YQNA=Z<:R+26YQL;D_^(M(^G&LBTEN<:&X?^( +M/8G&^%WYQL;>_[(L6JRK)AL;XLO^5M[VZ#WIQK(M([G&B'V)QO(M)_G&!M;_ +M`((M(XG&\BTG^<:&TO_H/>G&N'VYQL;/_[(L6IS;)AL3\LO^5A_SN#VYQH(M +M(XG&1LG_``#B+2/IQL;&__@]^<8&Q?\`5MF\#"B"1<9ET?FBH*BQ-0`\/X;N +M_I@RF<8&2OZXLKG&QDS^PB(CR<9&1O[2(BO9QL9(_O@R^<;B(B/IQD9!_IBR +MF<:($XG&!D/^`,B!#,W28RK,'`;N_0`[IO`0IO)#[.(#[,P>QNG]#.@,&9)C +M+()C*H;F_0P-!HG^``P)QH[^N#*YQL8S_@P,QMC^#`E&WOX``,(B(\G&QB[^ +MPF)&K0(,_=)B5&6K_J*@J`8J_@`,#H9K_PP-AG'_Z#+IQH8E_O(B(_G&AB/^ +M@B(3B<:&(?Z2(B.9Q@99_[(B,[G&AAW^Z&'(40P?#`B8<<"/@PP,D,^##`G@ +MGX/`F1#(00P.P.^#P@/JV9'@S!#`B!"0B!""0^K&./^M`F4D`:*@J+$U`#P_ +MQJO^```V00!2H\`,!W)B25I24B5_#!9"T@&V)06"!)P66`Q=`@Q)=JD;HB): +M)CH00B)>/?`6Q`DF%'4F)$XF-`=2Q2!+(AWP```VII`0IIDU`#:F@!"FB44` +M-J8P$*8Y50`VIO`0IOEE`#:FX!"FZ74`-J;0$*;9A0`VIL`0ILF5`#:FL!"F +MN:4&[?\``#:FT!"FV34`-J;`$*;)10`VIK`0IKE5`#:FH!"FJ64&Y/\``#:F +M@!"FB34`-J8P$*8Y10`VIO`0IOEU`#:FX!"FZ84&V_\``#:FH!"FJ34`-J:0 +M$*:910;6_P""(EHF)54F.`<`-Z:P$*:YE,(D&R8\$``WIN`0IMB4H.X1X-T@ +MTF0)\B0<)C\1`#>F4!"F,B0)0%414#,@,F0)@B0=@LC]%@CO`#>FH!"FF)3@ +MJ@&@F2"9E$:W_U*A`B8X#0`UIK`0IK"P=+"VP+F4PB0;)CP5`#6FX!"FV)3@ +MX'3@YL"@[A'@W2#9E/(D'"8_%0`UIG`0ICB4<'!T<';`0'<1<#,@.92")!V" +MR/T6B.@`-::@$*:8E*"@=*"FP."J`:"9()F4QIO_```V00`RT@4,"()B23(C +M+T*@X$I"MB,%@@2\%H@7DB0B'+PF.2,`/*:P$*9A6`%2(EYQ60&R8@.BQ?\6 +M^@K2Q?X6W0SBQ?T6O@Y150'R)",F/R(`-:8P$*9A6@%2(E]Q6P$YLF85`L8C +M`(+%_A;8"I+%_1;Y#:(D),%6`28Z(@`\ICWPL!"F85P!4B)@<5T!LF(3)A5O +M9B4"1B,`TL7]%CT-XB0E05X!)CX?`#2F,!"F45\!0B)A86`!,F(;"_06#Q2" +MQ/X66!0F-`(=\```-J;`$*;"8AT`-::P$*:A80&R8A\`.J:0$*:28B$=\``V +MIM`0IMER!M3_```VIN`0INGR!MS_```VIO`0IO)B%\;C_P`WIC`0ICE2!LO_ +M```WIH`0IHG2!M/_```WII`0II)B%<;:_P`WIM`0IME2`#:FP!"FL6(!R7(` +M.Z:@$*:IDD:]_P```#>F4!"F6=(`-J8P$*;Q8P$Y\@`_IN`0IN)B$0;`_P`` +M-Z:P$*:R8A4`-J:@$*:19`&B8A<`.::`$*:"8AF&PO_"(EHF/`P,Q90$`,Z;P$*;B)!'@_P'P[B#B +M9!&&AO\`-::`$*:"8A\=\``VII`0II)B'1WP`#9!`&*CP`P%4F)):F)")G\, +M%X*A`K8D:I+$_D(B6A9I)R84"R8T"``UIJ`0IJ)B24(B6R84%"8T$0`UIL`0 +MIK(B2:#,$<"[(+)B24(B7"84%B8T$P`UIN`0IM(B23WP0.X1X-T@TF))0B)= +M)A04)C01`#6F,!"F\B))X#,!,/\@\F))4F)-0B:`MB1DDL3^0B):%DDHC*0F +M-`@`-::@$*:B8DU"(EN<-"8T$0`UIL`0IK(B3:#,$<"[(+)B34(B7)PT)C01 +M`#6FX!"FTB)-0.X1X-T@TF)-0B)=G#0F-!$`-:8P$*;R(DW@,P$P_R#R8DU- +M`B!2((*@!':HP&(D6@N6%CD+HL;]%MH*8B1>%G8))A9P)B9)LL;]5KL)`#>F +MH!"FJ34`-Z:0$*:910`WIH`0IHE5`#>F,!"F.64`-Z;P$*;Y=0`WIN`0INF% +M`#>FT!"FV94`-Z;`$*;)I086````-Z;@$*;I-0`WIM`0IME%`#>FP!"FR54` +M-Z:P$*:Y908-````-Z:0$*:9-0`WIH`0IHE%`#>F,!"F.74`-Z;P$*;YA08$ +M````-Z:P$*:Y-0`WIJ`0IJE%4L4@2T1-`@Q,=JS04B):%E4,TL7]%OT+4B)> +M%E4*)A5Z)B50XL7]5MX*`#>FT!"FTF0C`#>FP!"FPF0D`#>FL!"FLF0E`#>F +MH!"FHF0F`#>FD!"FDF0G`#>F@!"F@F0H`#>F,!"F,F0I`#>F\!"F\F0JAA@` +M`#>F@!"F@F0C`#>F,!"F,F0D`#>F\!"F\F0E`#>FX!"FXF0FQ@X``#>FP!"F +MPF0C`#>FL!"FLF0D`#>FH!"FHF0G`#>FD!"FDF0H!@4````WIN`0IN)D(P`W +MICWPT!"FTF0D0L0@2R(=\``F%!$F-`X`.*;P$*;P\'3P]\#R8DE"(ELF%!PF +M-!D`.*9`$*8R(DD]\$!`=$!'P*!$$4`S(#)B24(B7"84&B8T%P`XIJ`0II(B +M2:"@=*"GP$"J$:"9())B24(B70NT%IO8PL3]%CS8`#BFX!"FTB))X.!TX.?` +MX.X!X-T@TF))!EK_G"0F-!``.*;P$*8]\/#P=/#WP/)B34(B6YR4)C07`#BF +M0!"F,B)-0$!T0$?`H$010#,@,F)-0B)RQ/T62]<`.*;0$*;"(DW0T'30U\#@W0'0S"#"8DU& +M5O\`-D$`0J/`#`9B8DE*0C(D?[8C=%(B6B85#B8U"Y*@&0`YIH`0IH)B25(B +M6R85&28U%L%3`0`\IK`0IJ(B23WPH+L1L*H@HF))4B)<)A49)C46\50!`#^F +MX!"FTB))/?!`[A'@W2#28DE2(ETF%1DF-1:!90$`.*90$*8R(DD]\.!5`5`S +M(#)B26)B39(D@+8I:U(B6HS%)C4*'*L`.Z:@$*:B8DU2(EN<928U%.%F`0`^ +MIM`0IL(B3:#=$=#,(,)B35(B7)R%)C4606"9`9"((()B30P&'+G!8@&A6`&Q60%- +M`ET"TJ`$=JTK)C<;0(8!D#@@`#.F\!"F^35R)%X+YQ9.""8G<28W +M2AMF4L4@2T0,!1S(L6D!D6H!H6L!30(,3':L*V(B6ISF)C8<0'4!@/<@`#^F +MX!"FXF0C8B)>"]86S0@F)GDF-DX;54+$($LB'?```+`X(``SIO`0IJ#H(/E5 +M`#ZFT!"FP'@@V74`-Z8P$*8YE0;D_[#H(``^IM`0IME5AN#_``"@."``,Z;P +M$*;Y=8;<_P``H#<@`#.F\!"FD.<@\F0E`#ZFT!"FL,<@TF0G`#RF8!"F8F0I +M1N+_`*#7(``]IL`0IL)D)4;>_P"0]R``/Z;@$*;B9"=&VO\`-D$`#!:BH0)" +M(E12H\`,"9)B29)B35I2"S06(QN"Q/X66`UR(F)P0!1"8EIP$0`VIC`0IC)B$P`VIO`0IO)B%((B +M6IP(`#:FH!"FHF(C`#:FD!"FDF(DLB)_^,]``ZIO`0ICWP +M\/!T\/;`\F)-,B);%J/@`#JF@!"F0B)-@(!T@(;`H(@1@$0@0F)-QGO_)A01 +M`#JFL!"F@$*:I0@`VII(B8I"0%))B6H`0IHFR`#:F +M,B)B,#(4,F);\!"F^<(=\```0B1_]B0"AB\`PL3^%IP-`#BF,!"F,F))`#:F +M0$*:9 +M0@`VIH(B8H"`%()B6C`0IC)B$P`VIO(B8O#T%/)B7.`0IN)B%!WP`#FFT!"F +MT-!TT-;`TF))`#FFP!"FLB))P,!TP,;`H,P1P+L@LF))1K__````-J9R1;PP +M$*8Y,@`VIO(E*6)%M_#P%/)E(>`0INE"'?``.:;`$*;`P'3`QL#"8DD`-J9R +M1;RP$*:Y,@`VIJ(E*6)%MZ"@%*)E(8`0IHE"'?``.:;P$*;P\'3P]L#R8DD` +M.:;@$*;2(DG@X'3@YL!`[A'@W2#28DD&Q_\`-D$`,M(%#`B"8DDR(R\N"O+,S/)J<,8!````EGP%PFIPD<,`H4X`=H`.@BJ#"YF,F+QI +ML@2.)BL$AOK_K+G"!(Z,'!WP`/(B$>("P`R]\_T)X_T(`#VF'?``@@1R"XA6 +M*/:2!(-6&?9&UO\,*J)$CB6=^,;Q_[+,-+)J<$;H_P```#9!`!R(PJ.TRL*B +M+'\`.*:0$*:RH-0RIEPZ,H(#@+HBDF(1O%B2(A^,&;9)"M(B'Q9-">("OJPN +MLB(?#._W&QLFJQB2(A$,^)<($+(#@(RKXJ`7`#ZFT!"FTD+`\B(1K,\<+0`] +MIK`0IKGBF.(N"O+*S/)K?\8!````EHH$HFM_D<,`H4X`,J9J.C)V@`Z"*H,+F8R8K!FR +M`X`F*P2&^O^<:<(#@(P<'?``TB)&#+YPW1'@W2``/:8=\`PNXD.`)87X!O?_ +M`/+*-/)K?P;L_P```#9!`!PMLJ.TNK*B*W\`/:;`$*;"8D.2(D,N"O+*S/)K?\8!````EHH$HFM_D<,`H4X`,J9J.C)V@`Z" +M*H,+F8R8K!FR`X`F*P2&^O^<:<(#@(P<'?``TB)&#+YPW1'@W2``/:8=\`PN +MXD.`)7SX!O?_`/+*-/)K?P;L_P```#9!`"%O`1WP-D$`DJ#^^W+[@T`@8'!T +M08"$01N(H*<1D%<16E2JI)`X$#"'@@NJ"U504A"@HA!WLPW`9Q%J9`MF8&(0 +MA@(``,!C$6ID"V9@8A!@EO":58"8D)"9$:"($8J$FI0+F0N(@((0H(B0D"(0 +M("*0BB):(AWP```V00`A<`$=\#9!`"%Q`1WP-F$`0B)/HJ`!Y7[CK02Q;P%8 +MDFBB@7,!D7(!DF)Z@F)[^V;[55!406!D0>5=&%F$#!N!=`$;9J*@X*JBHF0` +M@&808F0)Y3SNP78!L`'R#5B0=Q&`=R!R;2<,&)T(GMDJ?B6R:B6R&B6R"B6Q^B6QZB6QFB6QBB6Q=0QH*B6Q;`W)"@S!&0 +MW1':TPO=#$JB2X2"28'B:1ZB28`,+C"@8-#:$-)I(8)+A]J-@FDDXDN(BHV* +MW9#E$8)I)^KC"^[R2XG29*G@ZA#JW<#A0=)K&SK,"\S`RA#:_/)K'/K,^HZ" +M:Q[:[N)K'<)DJ%>V$@P,DJ#_P(41BH,+B("J$,8#```,#)*@_\#6$=K3"]W0 +MJA!13P`Q@P"B:U"JZL)K+N)K"\BXG@7H!(:<`,/\@ +M\FV"P"``,BV"(FV:P"``\BV:@FV%,BXL.24B+BLB;<+R+B;R;4+B+B?B;8+` +M(`#2+8+9`<`@`)P+#`*B;*S`(`""+*R)`<`@`!WP#`(=\```-D$`##L,&E(B +M3Y(C.G(C.X(C.$+5`F+5!Y##!(+(_A98&Z)$GJ(3?)"T!*)4M)"E!((3?8)4 +MM?(3?I"&!/)4MI#W!.(3?^)4M](C0'#D!=)EW,)&/L)&.7#5!8)&-O)&,;)& +M.*)&-^)&.M)&,G#F!'#7!'"E!'"T!'##!'#S!0P(@D91\D2?PD8UPD;:LD8T +MLD;9HD8PHD;8@D;5@D;4\D8[TD;6XD;7<-@E<.PE@D;=@D22\D;DXD;FTD;HPD;ELD;G +MHLK]%@H/V&/(<[C3P-T1TE1&TE1,P,P1PE1'PE1-%DL0Z-/[[N#D0>F%N'-\ +M[!N[P+L0N96B(Q:(!="J$:)EF/(H)RP)D/\@\F@GLA1'HA1&L*J"L<4`H*H1 +ML*J`I2'C#`JQ>P'-!>4AXPP:Y3WCD3<`LJ"`\6P`<.D4PB6VXD20X3D`P,-! +MTB(ETF6T\,P0T8\`PFX4LFFHH@;G@@;EL@;FP@;HP(@1L+L1<,P1T,P0@*H1 +MTJ$`T*H0P*H@PJ#@P+L0'`S`B!"PB""@B"""::G2);;0TT'PW1#2;A3"(B71 +M-0#`P"3:S,)EM1WP`+)$GK)&+,:0_\@%F&.(<_C3DE1&DE89@E1'@E8:C'^H +MT_NJH*1!J86R+"?2K]_0NQ"R;"=&R?\`Z&/IA0:__P`V80`R(D]BHB9@8X"" +M!H@]\"98!`P(@D:(04X`J`/1.0#"H/]2H(S";8*R*B=0JH"R9(*2)(+E(>YV +M@`CB)(;@YE7V3@(&_/^B(G^E4NX,:@`ZII`0IJ(C`((B3X)J%_(&>"8_#@%.D1V!$F/0(&^O^" +MTPB"*"&"8GPF)`6,)`P2'?`,`AWP````95@%30H&XO\````V00#E(.Z(`I%. +M`((H)X)I@IBR#`.V*04RR?[&__\,2Z@"V.+"H(S*JC`]WMK0(,!$GR +M2<(E=P!)HCFR'?```#9A`""B(&5.`5%/`('*`'*A_'IR@F5(C'.2!X*2R?X6 +MJ1X,)BP>LJ>3R`+17`#295-B95G"+"RZL@PMT_P$R26B!X_""X62`EK2"WV" +M"X?R"X"3_0&#_0.2!WV"!X+S_07C_08,#^('HL/]$@N(#!RC_123_16`_(/S +M_1WC_1[293BB"WFY$:+*_A;J$PP)#%Z9E3)E1?('@@P&TJ#X\L_^%C\420%' +MLUH,#S!$P+(BE@8!`!O_1Q](#`H66_\ZS\8"``"R94RR(I8;JK>ZY+T*P_L( +MC0OC^!33^!B)Q6)E#)(BEH*@")PF4X,BFL,$`$!V,1HJ_^H*,0 +MHFFLP"``@BFLP"``R`+Q<`#B+"WP[B#B94+`(`#2)4+`(`#"+"W"94+`(`"R +M)4+`(`",Q#)IK,`@`((IK,`@`!WP'?#1K`"B"WV2`EJQ@`"@K).\6:#;DYT- +M1JK_````%H/K\B<7"\,+_\/_"./_%-/_&/G%:<6R)Q<+N\/["+)E3(:E_PSF +M1H3_``"@O9.="X:<_S9!`'SVTJ?=#$L,*`P*#!E"HGA*4I)%-W(%?*FRJ:(` +M=R."13:!?0%P=Y`@=Z"*=[)'"(+2"(((#?*@_N*@_3+(_Q:C&++(_19+&,(B +MW)")(!8,!<(%K#(%LI)%@@"\(W?L!3+#_Q:C(8)%LF)%K;(5/,(5.S(5.C)5 +M4L)54[)55,(%?+(%?C(5/3)55;)%KL)%K+(%@<(%@#(%?S)%K\)%L+)%L<(% +MC((%D@"\(W?L`A:X&$(%G8S49A04L@7$X+L0LD7$!@(`P@7$\,P0PD7$DD<) +M8D6-XA5+@@6?0@6A\A5*D@6><@6@,@5]LA5-PA5,PE5$LE5%,D6=DE5-\D6,0D6?XE5*@D60('>@VG>"!X!62`F-"BP)`@=Z#0=X#"!X`6#!`B!7T6@A(+@A:X +M"V)%?&)%?6)%@:)%?J)%?Z)%@*)%@@P"F,6QK`"R53JR53NR53RR53T;F9G% +M'?```#(%S#<[`H::_["+D$*B>""(H-J(HDB`L@7.HDA_C0JZ,AN[2C/"0\]" +MH"&PL'1'NP*PBR"R![`8T+,@7-AQ,"@D7.<@7,1I3_`#9!`$*GDTI" +M,@1Y#`T60PG98H("74($A0P6%O@*C/0+E!:)$J+$_A8J$[+$_19+$L%^`/U'IQ6,@E_ +M:K.S]0"RR_\`&T``_*$P_\#S]00;9IQ$\@F`2C\S]0P+,P`30`#LH?#NP./U +M$`R$9[0#&Z<,!E)H&$N('?`,!<;M_X($AU($A=ERMB@XG`62Q?\620RBQ?X6 +MZ@VRQ?T6BPS!?P')8@8-``",]`OD%@X)\L3^%J\),L3]%L,(@8`!B7(&T_\` +MC/4+E19)":+%_A;J"K+%_1:+"<&!`L9(`TD#+3_S(3?UT-,_4`&T2,1I(7?Y/U$+9D!1MF*W<,!%)B +M&$LB'?`,!0;X_Z&$`:ERQK?_L84!N7+&M?_!A@')ERQK'_\8@! +M^7+&K_\QB0$Y;J9VOGJ@@N\D@O,<:P``&@C`-DC=^@_=^E\8(:0((B@2HB" +M*#,ZB(GZT@OA@AN2`-TC@-W`5GTTT@O<`)TCT-<$5KTSD(F0((B@2HB"*#,Z +MB(GZAAH``'?I/-"-D""(H$J(@B@S.HB)^I(+X8(;D@"9(X"9P%;Y,=(+W`"= +M(]#7!%8],9")D""(H$J(@B@S.HB)^H8*``#2"ZP`W2/0W9`@W:!*W=(M,SK= +MV?J2"ZP`F2.0F9`@F:!*F9(I,SJ9F?J""ZP`B".`B)`@B*!*B((H,](;%F(; +M%SJ(B?KR:A#R:A'R:A+R:A/R:A22"SB<[=#0Y/);%IQI@LG_%IA#@LG^%JA' +M@LG]%LA+@LG\%FA-K`;2"SE@8.3R6Q><70N-%DA"@LW^%FA&@LW]%HA*@LW\ +M%BA,."`EYB`EL62#$,&&!HDYT&D_T$ +MQH;_`);Y`-*B2""IH-JJHBHY.JJB;%*""ZV2HD@`B",@B*":B((H.3J(@FQ. +M!L#_D@LV%LD?"XD6>"""R?T6&"""R?P6N!Z=#Y/]$<;T_MJ2#"86F=*"&0!) +M,2D1.2$B"0PR&0)"&0$C]@4B&0-B:A6":A9":A=(,3)J&")J&3@A*!%&/O\` +MVI(,)A;9T((9`DDQ*1$Y(2()##(9`$(9`R/V!2(9`6)J&H)J&T)J'$@Q,FH= +M(FH>."$H$48W_P#:D@PF%AG/@AD`23$I$3DA(@D,,AD"0AD!(_8%(AD#8FHE +M@FHF0FHG2#$R:B@B:BDX(2@11C#_`-J2#"866I(,%H;% +M_WJ2#!;&T?]ZD@P6!M[_`'J2#!8&ZO\`@@);.2$,*0P3@).#."$&3_^"&U*" +MV(`6V!'26U,&\O[2&U1PW<`6;1%B6U5&]_X`#`:2HOR:DD:Q_PP&DJ+\FI+& +MO/\,!I*B_)J21LC_#`:2HOR:DL;3_X(;8H+8@!8H$-);8T;@_@#2&V1PW<`6 +MK0]B6V5&Y?X`#!:2HQR:DD:?_PP6DJ, +MN`*B;?#B*S#B;$"R*R^R9<`=\```TEM2AJK^``!B6U2&L?X``":'`B:&%T@=[TD)8HD)9X@6>9CX<\@6%W&^2H`4`.::0$*8]\`N)%G@JHLG^ +M%AHJN+*M`F4L!+(%?0PZB!7BB0EAB0EG&N_\`#+D`/J8,'PP-H@()L@(*@@((HLK]H-^#\@(. +MH@(+T_@&L_@'T@(/L@($H_@,\_@3J++XHM/X%+/X%=("![("!J/_"*C"T_\0 +MT_@6L_\1L@(%H_\<]$I(%?1=I#*(%>*)""F)""P8#``"R`EG2`EC20@JR0@OP +M$*;A.0#B+L'@X&16+NZ2(X&"(A"7"`*&M?^H`J(J-Q8:!*T"9=O^!K/_`%+2 +M`M(E%[(E%@P#"]T+N]/[",/[$L/[%;)D3`:L_P``PF*RXJ,N#&\,6()BK_)B +ML.)BL7SR'?```,)"#[C"V**2(I8;NQO=V:*PL"2YPI>]`L9]_VFBN++"0@ZB +M(I<;N[FRI[L"QEO_QN7_``Q<#!W21:]IHFFRPD6N+0,=\````.(E+]Q.#&H, +M28*CH()E,9)E+Z)E,/(C@?)E,JT")='^QHG_K0*E]OXM`QWP/0JM`N7/_L:% +M_P"QD0$`.Z:0$*:,2=+)@%;-U`P"'?`````V00"B(I<,`PP-G&JM`@P+S0.E +M`P"@VB`F"BVB(I"(I:RRO^13P"BH`&"R/^S^`BC^!*C^!6M`H)I +M3.4<`*T"I>_^'?``-F$`8B*6#`QG,P)&-P`P\&"Q3P"!3@`,'>T$0.V3#`EA +M^0``%4``K:%VI@=R*$T;F7<*#((BLQN(@F*S?/(=\``,!SILH?D`0_8(G0;3 +M]A!3]A%IRW:J!J(H@QMWC+JR(K,;N[)BLWSR'?``#(<,&CD!\&S`#`-@:I.M +M"6/W"./W"5/W$0`WIG`0IG&2`0`WIF/S%-/Z$E/Z'./S$S)H5=/Z'Z)HR7(B +MEF*@"#(A`'J**BH((URP>#`RWJ&,,&QNG?0R@H'3GN@%]"G)&U185!B85 +M4B8E3S&L`$CF@*B0TJ?<(*J@(M((VJI":A^R2H#"2H$R5CXR5C\R5D`R5D&" +M1H221H72%GSB!B7R!B0B`@PB1HCR1H;B1H?21HDM"!WP?/(=\```TD;,1NK_ +M#!G&W/\`LD;,1N?_```V80`A^0!13@!A3P`]\':B')(E0T(F0W@V@B7#,B6# +MD$0@@#,@<#,@0#,@%O/_,3<`=H`0PB/GR0&X`;"P%+D!J`$F.@(&^O\=\``` +M-D$`#!KES.&(`J$^`((H)Y%.`*"((()I@AWP`#9!`"@"84X`4B(Q,B:!09,! +MD3X`4#,00%,04F:!1X,#'?```((B)Y"((()F@AWP`#9!`$&4`6*A9FIB4A:` +M8A9_^U7[9E!4(3T%4F*78&0A8F*62E52!0!*1D($`&/S"%/S$$/S&#)B$1WP +M`#9!`*T"#`NEH0.,*BT*'?!2H@(`-::@$*:1E0%"H?XRIYPZ,DI"HD-_D*J0 +MHAH`HE0U`#6F@!"FK0*+F8)#@Y"(D((8`()4.:4(`(PJ+0H=\(($DPP,HJ$" +M%H@&`#JFD!"FDD.+%CD&`#6FL!"FLD.&`#JFL!"FC&L`.J:P$*8;N[)#B``Z +MIO`0I@R-X@-]\D.*LB(0Y[T?@@24G)B2`W$F&10,&J)#CJ)$@<%.`,(L@<<+ +M#'SR'?#"0X["1(%&^O\,`AWPPD.&1NG_``Q-TD.&!N?_-D$`4M("0@63#`L, +M^1:T!\*E`G*C`F*GH0P8)A09)B1V)C0304X`,B(00B2!?/)'`P$=\`P"'?#B +MH0(`/J:@$*9J8J)&AQ:Z!]*B`@`]ID`0I@RZ#"\6Y``F%'\RQ/X6(PO2Q/T6 +M30F"17R217WB!7RG'J\`-Z9`$*8F=%+R!GA*_QO_\D5X1N;_``"R17BR17RR +M17W&XO^217V"17QJ8H)&AP`WID`0IO9T##(&>$HS&S,R17B&VO\`/*:`$*:" +M17B&U_^R17RR17U&U?\````\II`0II)%>,;1_P`]ID`0IH*@!4!`%!:$`"84 +M228D:R8T4/)%?()%?0;<_P`^IH`0IH!`!`=H`B84=+)%?:)%?`;6_P`]ID`0 +MID!`%(RD)A0LDJ`()B1.)C0_@D5]#&F217R&S?\`#&L,/=)%?+)%?0;*_X)% +M?`R>XD5]1L?_#'\,0S)%??)%?`;$_PRH#$F217R"17W&P/\``))%?0R;LD5\ +MAKW_`))%?/)%?0:[_[)%?0RMTD5\1KC_-D$`F]UQS.=SY-\L?]\D;HPD5[HD5X!O/_,@8`DJ("%I/\`#FF@!"F +M@D;Z1N__``!R1NC"17NB17A&ZO\`.Z;@$*8,'0P*E>G)21W\`-J90$*:,90`VIE`0IAM54D>` +M`#:FH!"FD4X`@B(0HD>!DBF!?/*7"`$=\`P"'?`V00!"H?I*0C($@@P&9 +M)I,D@J,"`#BF0!"F>K(,&29T++(+@!SZ2KNPJF.@F7.20@IB0@L=\-*A`@`] +MIL`0IJQ\X@1^XD(*8D(+'?```(*E`@`XID`0I@P?'/-`,V,P_W/R0@IB0@L= +M\`!ZDI()@))""AWP```V80!"H0*2HH`,!0P;LD()4D((4D(04D(&FH*BV/^" +M(MVB"O^B0@`(`(S*+0H=\```-*:0$*9&\O]BH?C"H#^QEP'2`@V2`@RB +M(A,6'0(F63:PZB#B8PRM`N6N`+SJ+0H=\*T"$!$@9;$`#`(=\```)EETL/H@ +M\F,,(*(@I:P`%KH'H"H@'?``4D()PD((HF,,(*(@):L`%AH#+0H=\*T"N`'" +M(0'ELP!J,I(3/``YIH`0IJT"#*N"0A!EN0"B`X2VFJ:M`B7I_PP"'?``-*;0 +M$*9J,L(3/-)"!@`\IK`0IK)"$(;U_P``4D()PD((HF,,(*(@):4`%AH!+0H= +M\*T"N`'($>6M``P"'?``:N+B#H3VG@P`-*;P$*;R0@8,`AWPK0)EX_\`-*;P +M$*;R0@8,`AWP`#9A`**C0%*A`H(B_T*A^`P&8D(&8D(08D((2D*2!(>20@`(`!8:$2T* +M'?```#6FD!"F1L__(*(@99``D@(0-VD_@B(&(*(@O0'@"``6"A$M"AWP`,*@ +M/[&7`=("#:(B$Y("#!9M%N+)^Q:^%[#Z(/)C#""B(&6)`!8*'2T*'?```""B +M(*6+`(("$"=H0X(B!B"B(+T!X`@`%LH/+0H=\`"2`@W,J:("$+*@W["J$*)" +M$,("#,+,^Q;<#*T"N`'"(0%EC@#&TO\`K0)EAP`,`AWPK0+EA@`]"L("","R +M0>9[.Y:+`]($A#S$0+P0MIT81XP'X@(0\"``C,ZM`N7!_[(""/`@`$"[$!8; +M_"=CO@`UIL`0IL)"!@P"'?```"9[#PRMMRT"YHN[#+[G&P(FJ[-PS"#"0@B& +MZO_R`@W,SX("$)*@[SWPD(@0@D(0H@(,)EIJK0*X`<(A`>6$`,:V_P"R`@W, +MJ\("$-*@]]#,$,)"$.("#.+.^Q8>#*T"N`'"(0%E@@#&O_\`\@((+`B`_R#R +M0@AIPX:>_Y("#@#B!(3VG@+&EO^M +M`J6J_PP"'?`````UII`0IH(4/))"!@`XIO`0IO)"$,;T_V)""<)""*)C#""B +M(*5F`!9:`2T*'?`@HB"R(0#"(0%E;P`,`AWP``"B!(3VF@T`-::P$*:R0@8, +M`AWP`*T"I:3_`#6FL!"FLD(&#`(=\```-F$`DJ0(#!H,!5)"!E)"!U)"$%)" +M"*)""9J"@B@?8J$""X@6V`BHHBJJFIJ2"82R(MVBHOAF&P)&(`"XHBJ[JJNB +M"H3"H?@Q3P"`9(B$Z"9())C +M#""B("5-`!8Z"*`J(!WP`#S\<9\!L@(,T@(-0J'X2D(6W0>"R_L6*`V2!(%W +MZ0RAG`$`.J:0$*9&`@``L9L!`#NFD!"F)AD+PLG^%NP2TLG]%GT)\A0\`#^F +MX!"FK0*RH`SB0A"E5P""!(0]\+:8!*T"98?_H9(B$R"B(/#N(.)C#"4I`!8Z +M`2T*'?"M`F5I_X;`_ZT"Y6C_!MO_B`$H$8/R#"G##`(=\``V00#"T@+"'#0` +M/*:P$*8,'0P,+$@;NZT+M[@(TD(-HLO;A@``PD(-#"LL+:<]*L)"#"`JH"(B +M&"!`=+P4@)01L)D@`#FF4!"F((AT4$%!BD0'91A`0&"&!``L/=<:10Q>XD(, +MR0/)$PP"'?``0$#420,@0'46%`6`A!&PB"``.*;P$*8@*'7P44%:(@=O#"`@ +M8""0M)D3#`(=\``@D+29$PP"'?#"0@Q:Y(#N$;#N(``^IM`0I@P"``5`T*"1 +MH*80T-<0V1.I`QWP21,,`AWP-D$`K0*]`PR<#(WBH?_RH/_E\?\M"AWP-D$` +MK0*]`PRL#)WBH__RH?]E\/\M"AWP-D$`K0*]`PS,#*WAH0'RH__E[O\M"AWP +M-D$`K0*]`PS<#+WAH@'RI_]E[?\M"AWP-D$`PM("PAPT`#RFL!"F#!T,#"Q( +M&[NM"[>X"-)"#:++VX8``,)"#0PK+"VG/2S"0@P@*J`B(AA\YB!`=+PT@)01 +ML)D@`#FF4!"F((AT8$40BD0'91I`0&`&!0`L/=<:1PQ>XD(,R0/)$PP"'?`` +M``!`0-1)`R!`=19T!8"$$;"((``XIO`0IB!8=6`O$%HB!V\,("!@()"TF1,, +M`AWP`""0M)D3#`(=\,)"#%KD@.X1L.X@`#ZFH!"F"]4,`@`-0*#0D?"J$=#6 +M$-D#H*<0J1,=\`!)$PP"'?```#9!`*T"O0,,C`Q]XJ'^\J#^)?'_+0H=\#9! +M`*T"O0,,G`R-XJ/^\J'^I>__+0H=\#9!`*T"O0,,O`R=X:,!\J/^)>[_+0H= +M\#9!`*T"O0,,S`RMX:0!\J?^I>S_+0H=\#9!`#'Y``P$44\`=J,2/?`]\#WP +M/?`]\#WP@B4Z&T3,J)(BLQN9DF*S?/(=\`P"'?`````V00!A3@`R(A!]`D(F +M@0P(#`)'@PZ"H0,`.*8@$*8,&`8````,!%%/`)'Y`%"B`:G%=JD3XB:8TB:8 +MPB:8LB:8(B4[&T0'X@OR)[,,`AO_\F>S'?",R!=B"H*A`@`XIC`0IAWP'?`` +M`#9!`'%.`%(B$(T"8B>!#`D,`F>%#Y*A`P`YIB`0I@P91@````"A^0`S]`QA +M3P`,`R/T&TG&=JH3XB>8TB>8PB>8LB>8(B8[&S,'X@OR*+,,`AO_\FBS'?", +MR1=B"H*A`@`XIE`0IAWP'?```#9!`#("$(+2`NPS@@B1HJ>@JB*=P9ME(?#,LG.P[1I0$`/:;`$*8=\!WP``#QI@$`/Z;@$*8=\`"!IP$`.*8P +M$*8=\``V00`,)PP84J(P,J"0^:`@'0F2`V2`Q&,>0`^IK`0IK)#Z$)#Z@P8 +MX@,4@D/I@@,/%EX@%J@ADJ("`#FF@!"F@D/KL@.O%HL2X@,0C&Z"`Q0]\!9( +M-I(##Q;Y+X(#ZQN(@D5+`#:FD!"FDD/P"YD6"2BR+X'"(A!\^BT$P+L0L"J3 +M%L(.'?""0^6"127"(A"R+X%\^HT$P+L0L(J3%G@'+0@=\((#YB9(*0`VIN`0IM(#$.)#[!9- +M+``VIH`0IO(#$H)#[1:_*P`VIH`0IH)#[H(#Y@N85JG@P:P!`#RFH!"FL:L! +M#`*B0^^ZJJ(*`*)%21WPT@/FTLW\%FW>`#:F\!"F\D/L`#:FX!"F#`+B0^U" +M0^X=\```@@/F)D@9`#:FD!"FDD/L`#:F/?"`$*9"0^Z"0^V"`^8F&`)F.!;! +MK`$`/*:@$*:QJP&B0^^ZJJ(*`*)%2=(#L!;=(H(#Y@OH%LX>\LC]%F\>@J(" +M`#BF@!"F]C@"!G\`#"A&``"LR@`VIJ`0IAN('`F'N?"B(J_<&H)BLK*B2`R, +M#%W28J_"8K"R8K&"0E]\^BT*'?""0E\,"BT*'?`,"$9>_P`66.``-J:`$*:" +M0^D`-J;@$*;B0^H&?/\,"`9Z_Y(##Q:)U0`VIN`0IN)#Z0`VIK`0IK)#ZL90 +M_P"QK0&2IT":DG:H20`\IH`0IH)9>8""0="($()9@0`\IH`0IH)9?8""0="( +M$()9A0`[IH`0IJ(9@8"`U("*(()9@0`[IH`0IJ(9A2N9@(#4@(H@@EF$QD7_ +M````%LC@`#:FX!"FXD/I`#:FL!"FLD/JQGW_#`C&>_\,&$8X_P```+&M`9*G +M0)J2=JA)`#RF@!"F@EEY@()!T(@0@EF!`#RF@!"F@EE]@()!T(@0@EF%`#NF +M@!"FHAF!@(#4@(H@@EF!`#NF@!"FHAF%*YF`@-2`BB""682&2?^R`P\6RPOB +M`^K@IX.-"D8=_PP8QC__``"QK0&2IT":DG:H20`\IH`0IH)9>8""0="($()9 +M@0`\IH`0IH)9?8""0="($()9A0`[IH`0IJ(9@8"`U("*(()9@0`[IH`0IJ(9 +MA2N9@(#4@(H@@EF$QE/_L@,/%AL%X@/JX*>#C0H&)?\``((#YR88!?+(_5;/ +MX`P"'?`,&(9%_X(##ZSHD@/JD*>#C0J&0?^"0E\,"BT*'?`,",9._P`,"`91 +M_T)"7PP"'?`,*(;O_@PH!A+_#"@&-_\`-F$`#$T,.@PK#`9RH0)-`E*GA5I2 +MD@6'PJ)*(:X!O"D,'7&O`2896Q9#"`N#%K@+]D,%MB,"!CT`#`+*E&))-F)) +M-V)).&)).6)).V)9%6)9%AWP%I,>J0'2P_\6[5/V0P6V(P+&O0`,`LKDLDXV +MLDXW8DXX8DXY8DX[8EX58EX6'?``%H,E"X,6B"GV0P6V(P*&TP`,`LJ48DDV +M8DDW8DDX8DDY8DD[8ED58ED6'?"B!%K*-&)#.&)#.6)#.V)3%6)3%A;**\(% +MBQ:L+8(#WY(3D=)#-@"((Y>80Y(#VG?I/0Q*HD,W!@X```""!%K*-&)#.V)3 +M%6)3%A:H,9(%BY+)_Q89,*)#-K)#.+)#.=)#-T"D(*6S_A9:%BT*'?``LD,W +MK02ELOZL6BT*'?#*I&)*-F)*-V)*.&)*.6)*.V):%6):%JT$I;#^%LHR+0H= +M\,*A`@`\IK`0IK)$7;)%J19;5@P*T@55HD1>HD6J%JU*`#*FX!"FXD63\@5? +M%C]*H;`!`#JF@!"F@)$$DD,H@(`$@D,IP@64#,O'NPO1L0$`/::@$*8&`@#A +ML@$`/J:@$*:B196@\'3RS_P6[T:BHP(`.J:0$*:"!96219GPF1&"R/T6J$:Q +MLP&ZN;(;`+)3$,($71:,1O*C`@`_IM`0IN&T`=)%EN#=D-(=`-)3#[*C`@`[ +MII`0IJ&U`9)%FH(%E:"9D)(9`))3$V8X%-*B`@`]IL`0IL)%G'#,D,(<`,)3 +M$D"D(&5^`!:*):`J()```,HTX@/?\A.1`.XC]YX0\@/:=^\*TD,VTD,WQ@$` +M``"R0S:R0S=B0SAB0SEB0SMB4Q5B4Q:M!*6=_A8Z&RT*'?""!54,&9)$71:X +M0@`RIJ`0IJ)%D[(%7Q9+0N&P`0`^IL`0IL#1!-)#*,#`!,)#*8(%E`S/A[\+ +MD;8!`#FFH!"F!@(`H;%G@]D@6+"YD6J4'"`]^"$Y$`S".`S,!6 +MC`N"`]J`AP16^`H,29)#-H8J`(($6LHT8D,[8E,58E,68D1=8D1>LD65%D@\ +MD@6+"YD6R3JB0S;20S>R0SBR0SFM!*6/_A:*)BT*'?``RJ1B2C9B2C=B2CAB +M2CEB2CMB6A5B6A:M!&6-_A9:/"T*'?``P@/?TA.1`,PCUQP"!B4`T@/:=VT" +MQB(`#$B"0S:"0S?&7O\`D@/?HA.1`)DCIYD/H@/:=^H)#$NR0S8&`0```+)# +M-M)#-T95_[)#-M)#-Z)A`$"D(&6'_A9*$RT*'?``K03*Q&),-F),-V),.&), +M.6),.V)<%6)<%B6%_A::02T*'?``HD,WLD,XLD,YTD,VAC[_`*)#-J)#-[)# +M.+)#.88Z_P"R0S:R0S=&//_2!54633\`,J;@$*;B19."!90,SX>_"Y&Q`0`Y +MIJ`0I@8"`*&R`0`ZIJ`0IK+*_*"0=))%E1:+/&8Y&ZT$LJ`!Y?D!G`HM"AWP +M0*0@I6,`%@H!H"H@'?"M!&56_HPZ+0H=\``,`AWPHJ,"`#JFD!"FP@65DD69 +M\)D1PLS]%KP]L;,!L+F`LAL`LE,0\J,"`#^F@!"FD;0!@D66D(B0@A@`@E,/ +M`#^FT!"FXLG`TD6:P@65X-V0TAT`TE,39CP4LJ("`#NFH!"FHD6<<*J0HAH` +MHE,20*0@95$`%FKXH"H@D````,(%518<.``RIM`0IM)%D^(%7Q:N-Y&P`0`Y +MIO`0IO"!!()#*/#P!/)#*;*A`@`[IN`0IL@!TJ`"X,V#PD65`#NFH!"FO!K! +MK`#"4Q62I@(`.::P$*:B$Q6R19>PJB"B4Q4`.::`$*;R$Q6"19B`B!&`_R#R +M4Q7R4Q:M!`P+Y>+0H=\`````PNRM1B33MB715B71:B33:B33?B33CB +M33FM!&5I_A9*&2T*'?``L:P!`#NFD!"FH:L!@@55JIF2"0"20R\6Z"P`,J;` +M$*;"19/2!5\6;2R!L`$`.*;@$*;@\03R0RC@X`3B0RFBH0(`.J:0$*9`I""R +MH`+EWP$66C(M"AWP`&)%D\;5_@``8D,H8D,IQMG^P@64#,O'NS;1M@$`/::@ +M$*;&#`#AN`'JZ>(>`.)3$$;D_@"2H@(`.:;P$*:!N0'R19:`_Y#R'P#R4P_& +MY/X`H;R0SBR0SG&#_\`LD,VLD,WJ0&& +M+?\`\@/?@A.1TD,V`/\C@/_`5A\.@@/:@(<$5H@-J0$,29)#-P8D_T"D(.4V +M`!9:U*`J()```*(%518:'P`RIK`0IK)%DP`WIL`0IA9\'@PMTD65K00,*Z7) +M`1;J'BT*'?``HJ("`#JFD!"FX@65DD69\)D1XL[]%CX`.)3 +M$0`]IL`0IL)%G'#,D,(<`,)3$J4!`!;:J*`J(!WP`-&\`=K9TAT`TE,0QN+_ +M```V00"M`B4*_HPJ+0H=\`P.PJ$"TJ()VM*"#8BBIZ*JHA;(!@`\IK`0IO*B +M`K)*A;"P=!8+!@`_IK`0IK)*@``\IK`0IA:+```\IK`0IK++`;)*@@`\II`0 +MIH(*=PR/DDJ$AS\(XDJ(XDUV1@,`L@V)#!P6Z_["2HC"37;A3@#2(A#B+H%\ +M\N<-`AWP``P"'?#B2H#&Z?\,2\;G_P`V00"M`@P[)94!C"HM"AWP#`RRH@FZ +MLH(+B:*GHJJBO%CR"G<,'0R.]SX9@;T!`#BFD!"FC$DF&3(F*4S"2HC"2W9& +M`0#22HC22W:M`J4C_HS:+0H=\```PDJ(PDMV1OK_K0*E^OV,NBT*'?#22HC2 +M2W9&]?^A3@"2(A"B*H&G"0-\\AWP#`(=\`PLPDJ(TDMVK0(,2R6,`1;Z^BT* +M'?``-D$`8J>3:F)2!GEQO@&"!H>,1285%R8E)HQS)A--]D,"]B,[)C@HD;\! +MF5(=\*QC)A-`]D,AMB,>H<`!J5(=\*P#)A,V]D,;MB,8>5(=\`"QP0&Y4AWP +M`,'"`5(=\-'#`=E2'?``X<0!Z5(=\`#QQ0'Y4AWP`$'&`4E2'?`` +M-D$`H<7YBB*IG*F9()/))"!N+2".(.*J*A +M_JJB)BXID@J!\@I^@4\`DD(']I\)(B(3*<@,`AWP`*T")0O^@4\`(B(3*<@, +M`AWPDBLO)AD@F*(JF9&6`8(B$\(#B;)""<)"!Z)"!J)"!:)"!*)""*)"$)"(("T*B<2IQ!WP +MHD(&TA,ZX@.)XD('HD((HD(0`#VFH!"FL<@!NJJB"@"@D!2@U`2@Y03B0@72 +M0@220@D6N08G:@Z"$ST`.*;P$*:2`@GR0A#`N0'1(0'"`@62(A/0NR##^QBP +MF2"9Q)(""0PE)AD(XLG^%BX))CELH,,$B'(,"ZT"X`@`C-HM"AWP````/*:0 +M$*8&S_^2`X:VF6:B`A`6"@:M`F7X_0P"'?`\_N)""``\IM`0IM)"!``\IK`0 +MIHRK@A,]`#BF\!"F\D(0`#RFH!"FD@.&HD(&]IE(LB(3N<0,`AWP`-(3/``] +MID`0IJT"#`N(<@R,P,00X`@`O"HM"AWP#`(=\```DA,[`#FF0!"FK0*(<@P+ +M4,00X`@`K'HM"AWP`*T")?#]LB(3N<0,`AWPK0(,"XAR#$S`Q!#@"`""X@6>`R2(PHPF8"@F8"2"8#0H@06Z0LG;0NR%#X`.Z:@$*:B0Q#B +M`PG2(Q/R`P7`[@'#_A;S_AC@W2#9PO($B+:?,8(#$*RXK0,EV_T,`AWP2H." +M"(4`B".`O[,`.Z:0$*;2(Q.@Z0'@W2"@W2#9PLG")CD5#`(=\``^II`0IH:\ +M_P`^II`0IH;)_\G"#`(=\#S[LD,(`#ZFH!"FHD,$`#ZFD!"FC,G2%#X`/:8] +M\,`0IL)#$``^IH`0IO($B()#!O:?=)(C$YG"#`(=\``^II`0IH;._\($A0#, +M(\"_LP`[II`0IE*@`B8Y6_(#!19_"0`^IK`0IHRJHA0^`#JF@!"F@D,0\@,) +MXB,3@@,%P/\!D_\6@_\8L_\:\.X@Z<+"`PDF+&C0PP2(1EP&"(A.M`I"(((G&Y77^%EH&+0H=\`#"$SP` +M/*90$*:QF0&B(A/P(`"PJB"IQJT"I7/^%HH'+0H=\`#2`X8\_N)""+:=!*T" +M);/]@J$"`#BF\!"F\D(&-^0(LB(3N<8,`AWPHA,]`#JFD!"FDD(0LB(3N<8, +M`AWPK0*(<@PK0,($X`@`C#HM"AWP`#=D"Z(3/0`ZII`0II)"$+(#AK:;"L(" +M$(Q,(*(@):W]#`(=\*T"#"N(<@R,P,40X`@`C"HM"AWPK0(,*XAR#$S`Q1#@ +M"`",*BT*'?"M`@PKB'(,+,#%$.`(`(PJ+0H=\%#`!(ARK0(,*^`(`!8*^2T* +M'?``-D$`,J'T#`B"0@>"0A""0@B"0@DZ,F(3.X)"!@`VID`0IE')`6%/`**E +MH%I$0@0`JH)2H0)`D!220@D6V0>"*!\+B!88"[BB*KN@JX"B"H3"R?T6W`I6 +MJ@21R@$`.::0$*;Q:0#B(A/2R?Z@B0&`[B#P[B#B9@P6W0RBR?T6>@XW9!S" +M$SX`/*:0$*:R`XB20A"0T'2VFP>,32"B(.6=_0P"'?#QEP'B(A.M`O#N(.G& +MI5O^%NH&+0H=\`""`X@\^9)""+:8!*T")9O]`#6FH!"FHD(&-^0(TB(3V<8, +M`AWPPA,^`#RFL!"FLD(0TB(3V<8,`AWP````-::@$*:2`@G&TO_B$ST`/J90 +M$*;QF0&2(A.L6O#Y(/G&(*(@Y53^O`HM"AWPK0*"(@>RH`%`P@3@"``6BO0M +M"AWP`*'+`:"I(*G&AO3_`*T")5+^K)HM"AWP`*T"#!N(<@R,P,40X`@`K*HM +M"AWP`#6F4!"F(*(@I4_^K,HM"AWPK0*"(@>RH`%`P@3@"``62N\M"AWP`*T" +M#!N(<@Q,P,40X`@`G$HM"AWPK0*(<@P;0,($X`@`G&HM"AWPK0(,&XAR#"S` +MQ1#@"`"`(`!9*ZBT*'?``4,`$B'*M`@P;X`@` +M%@KI+0H=\``V00`,*0P*%N0&HM("HAHT`#JF0!"F@L2Y%G@,(&2@8B888$PT +M8%`TG(6`I1&0JB``.J9P$*9@A'1P44&*50=G`E!08)R4@(01D(@@`#BFL!"F +M8'#5L$%!<$2`!VL"0$!@DM((D@D:]BD%\%41\$014+#40*"TL_H,04\`MB-> +MT4X`#`7"(A#2+8$,!N*A`]>,"@`^IE`0I@P6QO__#`/Q^0!3^ANIQ':O#SWP +M/?`]\#WP4B0[&S,'Y0V"(K,;B()BLPP"'?```(P6%^4##`(=\**A`@`ZII`0 +MI@P"'?"IQ`P"'?!:5H!5$9!5(``UIH`0I@`&0$+!($@$@%"14%<0@$00AM;_ +M```V80"M`KT#S00,G0R.\J'_@J#_B0$E[O\M"AWP-F$`K0*]`\T$#*T,GO*C +M_X*A_XD!9>S_+0H=\#9A`*T"O0/-!`S-#*[QH0&"H_^)`:7J_RT*'?`V80"M +M`KT#S00,W0R^\:(!@J?_B0'EZ/\M"AWP-D$`#"T,#`P*LJ%L%E0(NI*2&7X` +M.:9`$*9:IH*@?$G_+0H=\#9A`*T"O0/-!`S-#)[QH0&" +MH?^)`>7G_RT*'?`V80"M`KT#S00,W0RN\:(!@J/_B0$EYO\M"AWP-D$`/$L, +M!C(B%T*B3**B6*JC2D-B1&-B8C=B8]UB8_]B9(XB(Y@EP!.AS`%,:ZJC9;\3 +M(F.8,*,@95(`L4X`PB,0LBN!QPL"AB``5OH'4J-I\V)#6FG#DD0R:=-B0UN"1&(6+PVL +M"0NY%@L+YDD9IBD6P`"*@`!WPT@2X=^T(X@3( +MX.<$%D[R,*,@(+(@Y9P`(J``'?#RR?X6W_(PHR`@LB"EFP`BH``=\('!`8E3 +M1M[_D<0!F5-&W/\`K0.XL\(CEZ6H^ZT#)DQV2&`YP2`U"2`PA2`L02`CA6)08))=H)#GK8H`@8X`(CA +M@DE_B-&"27Z(P8))?8BQ@DE\B*&"27N(D8))>HB!@DEYB'&"27B(88))=X@Q +M@DF`@D.39C@$#!B"0Y.((8))@8)#D8@1@DF"B`&"28."0Y+B287R283B0Y_" +M28?228;"0Y"R28BX4;))B;+*_/9+`D9'`)&W`+#"02=K"``YIK`0IJ+*_-T) +MP(%!=I@.`#VF@!"F`#VF@!"FHLKX)DH2#"EL"("*L("($9"((``XIO`0IJT" +M)7,`LB2!DB(0MXD"1C4`PB,O5KSC?/)B8R]28S#BHFWB8S'2)('28S(=\*@! +M*!&"(R]((6@QS/AR8R]28S"(08)C,H*A\H)C,5A1B.%B28""27^(T8))?HC! +M@DE]B+&"27R(H8))>XB1@DEZB(&"27F(<8))>(AA@DEW8D.3)C8K0DF!0D.1 +M(DF"HDF#?/*B0Y+B287R283B0Y_"28?228;"0Y"R28A228D=\``,&()#DT)) +M@4)#D2))@J))@WSRHD.2XDF%\DF$XD.?PDF'TDF&PD.0LDF(4DF)'?!6J_"& +MQO\``*)33:)31R"B("5D`,(D@;(B$,<+'M(C+U8-U2*O_V)C+U)C,/*B=_)C +M,>(D@>)C,I````"B4TRB4T8L"*`.(D@=(B$.<-'O(C+U;/SV)C+X*BAU)C,()C,2(D@2)C,B*O +M_Y`````FJAB2(R]6J)#_!;\"P`Z +MIO`0IO)#^19_#+)$6+)$6<$W`/*@@/)LJ.(#^='.`?*A`,">$;"9$(#N$?#N +M$."9(-"9())LJ0`ZIH`0I@P[75_!9*$2T*'?```#JF/?#P$*:M +M`O)#_674_!:*&BT*'?``LD/=4D0]ANG_@@,%%@@CH:X!`#JFD!"FDD/HL@," +M%FLBPJ("`#RFD!"FDD,0DD1;G`G2R?\6#2OBR?X6;B[RR?T6ORR"%"F2%"B2 +M5!2"5!6M`N6D_+(#Z0S*8D1$M[H+P;$!`#RFL!"FA@(`T;(!`#VF/?"P$*;B +MR_P6SAVP\'3R0^KRS_U6WQ"M`@P;Y48`%CH0+0H=\`""`P46F!VAK@$`.J:0 +M$*:20^B2!%N,^0NY%KL@PLG^%APDTLG]%CTAXA0I\A0H\E04XE05K0(EG?QB +M1$8,.J)$1)*A`@`YIH`0IA:H&6)#ZJT"LJ`"Y4``%FH;+0H=\`"R`P46*QG1 +MK@$`/:;`$*;"0^CB`P(6?ACRH@(`/Z;P(`"0$*:20Q"21%N,^0N)%G@CHLG^ +M%MHFLLG]%BLEPA0ITA0HTE04PE05K0+EE?Q21$!21$&M`N7-_!:Z"BT*'?#A +MMP$`/J:P$*:BI@*R0^H`.J;0$*;"%!S20^S0S"#"5!P`.J:0$*;R`^J20^V" +M%!R`F1'RS_V0B"""5!P6'^^M`B64_!8*!2T*'?``X@,%%MX0@:X!`#BF\!"F +M\D/HD@1;C/D+J19Z%[+)_A;;&L+)_1;\%](4*>(4*.)4%-)4%:T"98S\4D1` +M4D1!K0)EQ/R\RBT*'?```/("7*Q_4D/C4D)<1DK_``"2`^D,B)>X!*($5LR: +M4D/Z4D1#Q@$```!R0_IR1$-R0^-R0EP&0/\`P@/I#(O'NU?2!%86'05R0_IR +M1$.&]_\`4D/H!G7_``!21%OB%"GR%"CR5!3B5!5&??\,R)(#Z:&L`*)4')#A0>)4%$9S_P"" +M%"F2%"B25!2`@4&"5!5&E/\``*(4*;(4**"A0;"Q0;)4%*)4%<9H_\(4*=(4 +M*-)4%,#!0<)4%49D_P#B`ESB0^.&V_X````V00#!QP$,"#S]TD((@D()`#RF +ML!"FHJ$"LD(0`#JFD!"F,4\`0M("0@1_DD(&0D('(B(3*<,M"!WP```V00"! +M^@``.*8@$*8`.*90$*8`.*8P$*8`.*9`$*8`,Q&`51%0(B"`1`%`,R`P(B`= +M\```-F$`84X`@=`!*$``YIO`@`,`0IALS-#O#XP(#_$2#_(``_IN`0IC$W`*&G`"(CK,&J`(*O_B!0!`=B$8"" +M$()CK,`@`$(CK$)A`,`@`,)FFL`@`+(FFL`@`*)FFL`@`)(FFID!P"``G$4B +M8ZS`(`#2(ZS9`<`@`!WP5LCXANG_'?!6>/B&Y_\``#:!``Q/#!NBT@*""H9! +M3@`,!18H#,*A`@`\IJ`0II'1`0`YII`0IK9Y&=(BKU8=`?)BL))BLN*B@(*@ +M!8)BK^)BL7SUDJ"(D)."FI*R:=U&&0"X`K(K)[)D@K(IK+#0!`=K#7#[$/)I +MK,`@`.(IK.E!XB1"P"``P/X@\F1"P"``@B1"P"``8F17P"``\B17P"``XF1" +MP"``@B1"@F$$P"``C'VR::S`(`""*:RR*C>PJR"R*P"E%^G2)('"(A#7#`-\ +M\AWP\3D`\B_!?/[P\&3P7I,M!1WP``P)@?D`#`8,+':H!M(D0QN9G'WB*B_P +M(`#,WL)J+_)J,&)J,H*BDX)J,7SU"<_T( +MX_T0TF13TBFL+-_18."_9M`O9-!?+-^A8/$^*@B.#C +M@H*@`N#B@()NW;)*A=(IK-#P!`=M#G"-$()IK,`@`((IK()A`8(D0L`@`,`X +M(#)D0L`@`#(D0L`@`+)D5\`@`#(D5\`@`()D0L`@`#(D0CD1P"``C)_2::S` +M(`""*:R)$=(NWK(J-MJ[LF1.X?D`#`MVK@;R)$,;NXR/@BHO/?`6"`E\]7:` +M$^(IY^DQV#'0T!39,;@QLLO]%EO>1OG_`.*@B.#C@NKB\F[=LDJ%LBFLL-`$ +M!VL-<(L0@FFLP"``\BFL^2&")$+`(`#`^"#R9$+`(`#R)$+`(``,/_)D5\`@ +M`/(D5\`@`()D0L`@`/(D0ODAP"``C*VR::S`(`""*:R"80+2+MZR*C;:N[)D +M3D;6_P``8FHRXJ+D#$\,*()J+_)J,.)J,0;6_P``TJ"(T-."VM*R;=U&S/\` +M`#:!`/T#L9,`P6D`@3<`G0+1?`#`(Q'B*.4I$>DAT-X0X.0T@.X!X-T@,.!$ +MTFCEPFBFZ0'2H[C:V>*@B*(HIL*C+,K)J3&R:*92**:A?0`,"U!0=*!5(%)H +MIJT)1@(``.J[ZLSJJM<:1B(JW;8B\"8B9V8RZ@P$,BK>@BFW\'5!(BF6.HB` +M=Z`60OU8`8T,``5`=H`76`<;1%!0D5!0!%)(4&(IEG+'$!N(9[2R1OC_P=(! +MB#&Q?0"1-P"`@'2PB""X(:(IY<"[$,%\`,"J$+"J(*)IY8)IIAWPB!%2*MXR +M*98B*;V2F+$P%*C+)I+4$2`8%`4=I4*(%`$4D20("%! +M&T1@,D%VDR4@,`0@44$@800@<@0@(P1+1#)$C&)$C5`Q07)$CB)$CS`Q03`A +M03(IEB@X8J!@-S8"AIC_4J,LFDMBPZ!@@!1:1':8"B"`!()$L"`A01M$8#)! +M/?!VDR4@,`0@44$@800@<@0@(P1+1#)$K&)$K5`Q07)$KB)$KS`Q03`A08:% +M_P```#:A`#%.`'$W`%*B3-',`?*G=.*B6,*BK&(B%PP$#`NY@4EQLF(W2`'* +MQNKF^O;:UEI6LD5CV5'Y8>E!\J``X?H`TJ`$@B80LF4LF4?DB.! +MEPAFDB>LD*`$!VD3?.B`B1""9ZS`(`"")ZR"80'`(`!)H4@&*9$ATP&")"=9 +ML2"((()C@L`@`%(C@L`@`%BQ0B0G0F."2*'`(``B(X+`(``HD8S*DF>LP"`` +M@B>LB1'`(``,*@`ZII`0II'Y``P8"Z1"H`"@2(-V@"``/::@$*:I(8@AS+@, +M^``XIJ`0I@P80)B3J"$+F(;1:D!O8T9[)%9P`^ +MIJ`0IH(F$)(C@8"9$(*A`)"HD_;Z."::'9*@"Y<:9O+*]!9O%H+*\Q8(#9+* +M\A9)$_+*\1;/";),`@P8@DP#D@5B#`H,#_F!J7$,#X8``)(%8B8I"((,`H+( +M^U:X[0PB'?``D@(\C,D,*Z*@C*JB)8'H#`(=\,(%9XPL#`(=\+)%9PP"'?"A +MU`$`.J9`$*:2)A""(X&7B#46)!6"!EJB)I<6J!4,'ZT`PP*J7&(@1;H$I(%8@;=_P"2!6)&V__"80-@IB"E.0"RH`#( +M,?(C@9(F$`Q-X?H`]XD"%HH3LD5B#`F&T?\`R3&M!F7-^Y:Z$#Q+F#$,.@P( +M@DD$@FDR@FE4@FEV@FF8@FFZ@FG:Y]N7Q_`P+R#&")A#R(X$,3>'Z`((%1?(%1_)%/.)%/<(+9=(+9-)%/L)%/[(+FJ7G_:T&9>OZK0:E +M;/L,"\@Q#$WA^@!6:@Z2!6*"R?L6"-VBR?Y6ZMK&'Z`)>(158J!+AAP@9:H@N9L@N:#!W2 +M13K`NH.M!F4=_0P+R#&"(X'R)A`,3>'Z`(>/&-Q:R3&M!F55^ZT&95;[#`O" +M(0/2H`3A^@"2!6)&%?\``)(%8D83_P``H@7(H*<$%CKO#"\,&()%9?)%8I(% +M8H8,_Z(%N'?JZ?(%R/#W!!9/[4;W_P!F*=E&N/^X80Q(L@N<#!H,";":@Y)& +M6X)%8H:\_ZT&#"T,/@P,PD9;XD5BTD4TTD4UPD4\PD4]PD4^PD4_9X%M(C*%;M`%)C*$)C*:)C*^*B?^)C*GS] +M`#NFH!"FHDPV`#NFD!"FDDPW`#NF@!"F@DPX`#NFX!"FXDPY`#NFH!"FHDPZ +M`#NFD!"FDDP[`#NF@!"FXJ0"H=8!@DP\%G@)@:T!`#BFD!"F&YF27!\`.*:0 +M$*8;F9)<(``[IH`0IH),0ISH`#ZF0!"F#.A"3$.'%!&2H`^7E`L`.J:`$*:" +M3$2"3$4`.Z:`$*:1^@""3$86&`L`.Z:`$*:"3$<6"`L`.J:`$*:"7"6+B("# +M08)30``[IH`0IH),3)RX`#FF@!"F@DQ-`#FF@!"F@DQ.`#FF/?"`$*:"3$\` +M.Z:0$*:23%"\>0`_IH`0IH),40`^II`0II),4@`^ICWPL!"FDJ9VLDQ3FI)V +MF!,`.J:`$*:"66\`.J:`$*8KF8)9CBT-'?#8`MBMI[T8@B*O5HCIHF*R4F*O +M0F*PDJ)QDF*QAJ'_``P-AJ#_@J!X@E-`1MC_````.:9`$*:!UP&*A(((`$), +M2."($8)30``^ID`0ID),20;/_P`V00!-`H*A`@`XIN`0IF+2!^)&U``XIM`0 +MIM)&U0`XIL`0IL)&U@`XIK`0IK)&UP`XIJ`0IJ)&V``XII`0II)&V0`XIG`0 +MII*B`G)&V@`YIC`0IG+2`C)&VS)'DV8S!`P?\D>3`#BFL!"FLD;1`#BF +MH!"FHD;=HD>2`#FF/?!0$*8RIVQ21MXZ-"(&44'Z`%)'D':2"@`TIB`0IALS +M(D-_`#BFP!"F4=4!PD;?%AP+`#6F(!"F#'D,:BP-#",P(I`B5T8B5G`G/7.R +M)R\6ZPI\\@`UIE`0I@PKPA=-L%604E='4E9Q5[P(PB]%^(G+U9>^")G,J)G+Y)G +M,/*CS/)G,<;<_PP"!MS_`#(73"(732)71S)71@P"AM__DD>4QNG_`))'E1WP +M(F`?)+6/)+6?GK\D3U\D3,\D3+\D3*\D3(\D2Z\D2Y\D2X\D2H +M\D2G\D2F?/NR1,FR1+>R1+:R1*625&B25%^25%:R1*21K`"25&.25&*25&&2 +M5&"25%J25%F25%B25%>25%&25%"25$^RH,"25$ZZM+)D-@P[LD3T#"NR1/:2 +MH*Z:E))D-9*D=)J4HDF!\FD>PFDA#$_R28#*_*)$]_K,H3<`VMSR:23"9$>R +M1/C29#?:^/J(\F0X@F1&^HZ"9#K:[N)D.9(JK-@$X4X`D+`$!VD2?.S`R1#" +M:JS`(`""*JR)`<`@`($Y``P"P4\`,FB"\BTG\FZ"@BTLB2SR+2OR;L+2+2;2 +M;D*,ZY)JK,`@`+(JK+D!P"``'?`,`AWP-J$`L=\!0B)/<4``PJ",J`1B)U)2 +M)U'*JN7AYY%.`(&/`+@$TB,VHJ"`LBLKA_T%H,L@PFG"\B,DXB,DTB,DPB,D +M@/\0\/ET@J'`@.X0P,`D/(B`W1""(Q[@YG30TW06:"^R(QX,2TD!V2&"(QW) +M$=(B'!9X+J(C'0PJJ5&BH5"JI-)JX,(B'L)JX9(B&I)JXX(B(X)JXG(C&G)J +MY7@!0B,60FKVTB,@PB(8@J3$BG?:S,)G-9(C(8(B&)J(@F6'*F9)G.((C'()G.6"$!4(C'T)'\."(`4'@`=(B)6!U +M!=!W`=)JY,C3T>$!0(@0^\S0=Q"`=R#`Q$&!X@')BI(C$F#+!&`Z!-#,`=#, +M$.`S`=%I`$`S$/N9D)1!2$&9FF"F!6"4!4+$E$+L1T(@0T+L0L+,@T>8!4#Q$ +M8#,1T#,0T.X0X(@@TJ,`4.H4@.X1T.X0,.X@X+L@,3D`XJ$(L)D@D&8@L3<` +M4)05@)D1T)D0H)D@D(@@HJ",HF-=@'<@J&%P1"!RH("86I)C7H(B(I*@D.)K +MJ("(`8)KJ8*@>')C7=)C7G*@8))C79*A#H)C7G)C74R'V$K28UYP9B!,S8(B +M'7'G`9)KJ)%``("(`8)KJ4Q(@F-=7(A8F-=0F->TF-=V-QAZ`'(W`#=$6#=$,#`M-#,(,)C7L*A#))C78(J +M-9*A&X)C7G)C77*A*$(J-T)C7N)C7=(J-M)C7L)C7:(J.*)C7H(B%Y)KJ)@Q +M@(@!@FNIPB(DTJ$'TFNH@,P!PFNI +MHB(DP8H`H*!TP*H@HFNFH4X`DBD6DFK='?`,"P9"_PP*QD7_`#:A`.$^`)'I +M`0P,LJ1$T4X`8J340B)/4BV"@BV::F2ZM,)&Z*(B@*)K7Z$Y`'*AB'ITDFH\ +MDB?5DFH\D3\`,BH8\34`68&0,R`R:A@Q-P")D?)J3N)CVE(B&8&%`$P.4%!T +M@%4@4F.F\B?5\FI*XFI,X>H!XFI.\!"F@B)_#`7PB,`6J`^(D?&#`)"(((EA +MB('PB"")488%`/`0II(B?QO,\)G`%FD-@J`3Q[@"1C,`@B/GB0'X`?#P%/D! +MF`%F.>Z2(ZSR*B"04`0':1)\Z(")$()CK,`@`((CK(D1P"``#"B`_R#R:B#` +M(`#R*B#`(`!V@!""(^>)(8@A@(`4B2&((28X`P;Z_P!\V("/$()J(,`@`((J +M(,`@`(Q%DF.L\B.L^(&849)M@H(M@HAA\FV"DBV"F)&";9KR+9J2;9I,"8(M +MFO(GU?)J2I)J3.)J3H``IHRH=H`$\`"FC"\&_?\`=H`3\B/G^3&8,9"0%)DQ +MB#&"R/T6"/)&^?\````,!?)4%/#`]<)4%9`0II)4%I"0]9)4%X`0IH)4&("` +M]8)4&8`0IH)$-(#(=8"0]9)$-L)$-X"(08)$-8`0IH)$.(#(=8"0]9)$.L)$ +M.X"(08)$.8`0IH)$/(#(=8"0]9)$/L)$/X"(08)$/8`0IH)$0(#(=8"0]9)$ +M0L)$0X"(08)$08`0IH)$1(#(=8"0]9)$1L)$1X"(08)$18`0IH)$2(#(=8"0 +M]9)$2L)$2X"(08)$29`0II)$3)#`]<)4)Y"809)$3<`0IL)4*,"(=8)$4\#` +M]<)$4I`0ICWP/?"``*:,F':`!/``IHP?!OW_LF$'P"``D!"F@!"F\!"FP!"F +M\3X`@34`D>L!DFH\@FI.\F/:PB(;\84`P,!T\,P@PF.FDB?2DFI*@B?7@FI, +MXFI.0*0@4FW;4FW<4F?E4DO<94H`4J``HB?4#`NR9^FR9_.B9_72)A7")A;7 +MO`JM!+*@P!`1(*5A`!M59D7FX@0[#$L67AL,#_E6#!F21R62!#FB!#JB1R(+ +MB1:H%Z+)_A:*%MAQ#`H,#,)-2`P9XLK_%LX9%@H>#`FB!$:21R.2!#D+^A9? +M&8+)_1:H&ZAQ#"F22D3(<<(L*!9\'-($/!9=%`P>XD2"D@0^\LG[%M\-@LGS +M%G@-#`JB1(_1K`#B%"?R!#G"%"C@L'3RS_W`H'2`JA$6;Q6PJB#`^'2`_Q'@ +MN'2@H/2B5#WPNR"R5#RPL/2"R^`6&`K,&])4/(+*X!88"LSJTE0]1@(`#`L, +M"J)4/+)4/=($2I"#!,AQD@0]T.`DT/0DT+<$@DQ2LD28\DP>XDP@LDP?T-,$ +MTDP=-VD$##F&``"0D@221L"0H'22%!DF.E>R%!@;R<#,$<)4-1N[P+L1LE0T +M0*0@I=$`=H`0\B/G^4'H0>#@%.E!V$$F/0(&^O^")CF"8GP,`AWP#!JB1(\& +MR?\```"RRN`,#,)4/%;;]0P-TE0]QMG_X@0Y)CX)&YF0D2$+F9)4&:(4&!NY +MLE0U&ZJB5#2&YO_"!#H+S%;LZ,8```!6&NCH<0P:#!W23D@&H?\`\@0[%C\( +M@@1()^AU#"F21(*&JO^B!$NY5A9:Y,($21;\XPQ=V58&CO_B!#GBSOY6GN7& +MEO_RR?U6G^:8<0P(@DE$AIG_L+H@L+#TLE0\PLO@%MSL5AOMO0W25#Q&LO\` +MPLK^5NSCZ'$,'=).1,:.__($.0O_5F_A1H7_`+)$@@:/_PPX@D2"!HW_#`F2 +M1((&B_\``#:!`-%.`)(M@H(MFK`0IG*DR'IR,@?T0J`!%J,/LE("L%#U4E(# +M\!"F,<(`\$AU0D((,/\0\F(#T!"FTE((T.AUXD(3T-#UTD(2D!"F@@(YDD(4 +MD*#UD,A!PD(5HD(6D)AUDD(7)C@+0A(#0(@4@LC^%C@;DM("HBFZ#`462@E] +M`F*@`(&``.':`:*@`':`(K`0ID(2`[#0](#=P-"Z@T#(%"8<$AMFN6?R*;H; +M54MW][5@AO7_``#2`CFPP/7+-28]'``#0$!`L28=/P?D`L+,_K#0]`"\$="[ +M((;Q_P``\@)&9B_KRS7@W!#PW1&PP!4``T!`\+'0S"#P\`1@T2'PW3#@W1'0 +MS"!&\?\'9,(KS$;O_QWP``#B)SX,"K#NP!;.#V$Y`%$W`.$_`/&#`,'J`>#H +M(/#Y(/E1A@4`L!"F,B<^&ZJP,\`6(PU"H!.GM`)&,@`R)>R9DS"9DXP`*:,HW:`!/``IHPO!OW_`':`$_(EY_E!N$&PL!2Y03A!,L/] +M%D/R1OG_````#!1"1_2&??\``#'L`5*A`#`T$%`S(#)2`T:._P``-D$`#!8, +M>H("/3(".4+2!0P%,L/^4F0B4F0;4F0A%F,54F0?4F0@4F0=4F0>L?$!T?`! +M\>X!F9B9!(R95WR95[H9'$U``QF\.X@\>\!\F5=XF5>R)3Q\P'295W`S!%@ +MS"#"95ZHE-'U`;)E7<"J$6"J(*)E7CC$P?0!@F5=<#,@,F5>Z-2A]@'R95W` +M[A%@[B#B95ZXY''X`=)E7<"[(+)E7HCTT?,B00\?D! +MXB01#%OR95W`[A&P[B#B95[")!+0S"#1^@'295W"95ZB)!/` +MJA&PJB"Q^P&R95VB95Z")!9R)!2`B`&`=R"!_`&"95UR95XR)!;`,Q%@,R!A +M_0%B95TR95X=\)($M&)D'V)D((QY4F0=4F0>!JC_8F0=8F0>QJ7_````-D$` +MHJ$PI73G%VH(HJ$P)73G%^KVHJ$P97/G!WH(HJ$PY7+G!_KVH=H`4J1\6E*2 +M)2Q!.0"7ND#2)1:1_@&@W8#291:BH&"B9%W")1;"9%Z")2NR)2R0B(":N[)E +M+()E*^5NYR+2`N(BM:<>"Z*@8.5MY_(BM:>?\W'N`>$]`-'_`;%.`/(%I,(E +M**(KQ&#_$?#,(+#,`=#,$."J$,"J(*)KQ"(E)X(E*)'O`9)D78HB<"(@(F1> +M(B4LLJ,`#`>M`J4R$6T*LJ,`K0)E-1$,2')D7;$``F"0M$"J$;"J$*"9(+*@ +M8*T#DF1>@F1=)3,1P0("\0$"#(V@X'3P[B#B9%[29%W"9%ZR)2B"!:2B)2HZ +MN[)E**<[%Z";P))E*)RHHB4L@BBH3!E7^<'^O:!1@!"HQQ`0H"2 +M)(Y1.0"7N#6B)&^BV@BB9&^BH(RB95V2)&^295Z")(Z"V/B"9([E6^W#+#'P/#,$0O,R7'&```, +M?=EQ:3$Y$5DAL3D`V'&H43#`=/AQ2)&"(E@;GYE!D)$AFHB"8EB1``+F3P,, +M'FDQB#%H@0P_H*!T0$01R:%`=1'!"0*0=Q"01!"IX?#]0Z(A$OGQTLW\2<%R +M81%!!P)Q!@)`9A'9T4"($=$(`I"($)!F$&FQ@F$0800"#`EVKN.(X?C1,B$2 +MZ/%2(1`P.L`P[Y.`[A&@\+0,`S)K7?!5(%)K7E(B8F#N$."((%II#$5P9@'0 +M9A!@:"!2:UW`9B!B:UX,AF)K77)K7C)K73(A$:+*(/`S(#)K7C(B8E)K75@1 +M.CEP,P'0,Q`PB"#`B""":UYB:UU":UYA!`(6-098H3BQ#`B":UWP,R`R:UXR +M(F+@52`,3CHY<#,!XFM=T#,0,#4@P#,@,FM>#(,R:UTQ"@(R:UXXP8)K7?`S +M(#)K7@R#@B)BXFM=X0L"@(F`<(@!T(@0@%4@P%4@4FM>,FM=XFM>2YF"(1*H +M0;AAF`&`BK""81*7.$NB*R'"`HB8,;(+M-+,_A;]#`O<%ET/``M`#(S`P+$6 +MRA&2R1"(D?B!V"$<#N#@L>K=B_^*C()A"?)A"-)A`IDQ&[K(80P-TF$2LFPA +M\B)BZ'&(D:@AN('(,=(A$M)B6\)B7[)B8*)B78)B7OKN&^[B8F+VOD,=\``6 +MV0P)3)I)#5M)3)Y(#0-(3'.(#9/(#0<(3';(3 +M'H(#0J(3'Z)3,8)#9K)3,,)3+_)#9>)#4M)3+I)#9-(3).(#1/(#0_)#9\(# +M4N)#:-)3-@"\(\#'!!;<"%)#0%)#06)#0F)#0V)#1"(C+X&L`()3'()3'8)3 +M'H)3'QLB(F,O#`(=\````)(#4@"Y(W?I!JT"#`PE#@"R`T"M`L*@``"[(R4- +M`)(#018I"689JU)#0%)#06)#0F)#0V)#1"*@`.*@_=(#B,(C+_&L`/)3'/)3 +M'?)3'O)3'QO,X-T0TD.(PF,O'?`````@HB#"H`!E"`!20T!20T%B0T)B0T-B +M0T0B(R^!K`""4QR"4QV"4QZ"4Q\BP@$B8R\,`AWP``"2`XBBH/Z@F1"20X@& +MJO\`L@.(PJ#]P+L0LD.(!J;_`%)#0%)#06)#0F)#0V)#1"(C+^*@_M(#B(&L +M`()3'()3'8)3'H)3'^#=$!LB(F,OTD.(#`(=\```-D$`4@+T#`ABI'57LQ>, +M="84."8D1R8T5#!SD"!WH&IW0@>`C`0=\$T(L@+V+!F"1W\;J[JR,DOWP@+U +MH#!TE[,!30-''-Y"0O8=\`P4#!TP@:G>" +M1X!&[?\,23!SD"!WH&IWDD=_0@>`ANC_````-N$`K0*E_P"AW@&"$A:RH*YR +MPBJG&!S"`CDF/`72`CL6S7*2$A;B$E^0[L!6CFFZ\O)B-8(7`J<8$9(2%Z(2 +M7Y"JP%9::;#"@,)B-M('#R8]!>('$"8>!*T"Y?L`@@=8\@'RS_P6?V7H$=CA#`S)5M(M +MQ><]`H9N`7GQ.!%(L0P-#`X,#PP9F7'Y4>EAV:%`I)`+,PN$B3$Y0?"J$3%. +M`'NJ0$`422&I`0QT1A``````N.&R*]CF>QZM`J6+_Z*A,&7@YE=J"**A,.7? +MYE?J]LCAPBS8IGS@R%;H$=CA&\S)5M(MQ0P/\F8BYST"1E`!T@(]-^T+X@([ +M)AXG\@(\)A\AN`&((;"S019X60O8%JU3XLC^%EY4P/`4%N]4"[NM`B5Z_YBQ +MP0,"B.&R)54,#0P*J<'29AO29AS91H(HQ,"[$+"P];G1ES@"!@L!#`?&$``, +M&1N*X*B3&_K`KY/V.@*2H`"B`DV3^Q:3^Q>C^QBR94R2)AB,.0NYLF88V$;H +MPS#"`A86G##2 +M`A<6O3#H<:"U!+)&C(S^\A((DJ`!\/:4%O\KD(#TB:%V@`>B(]JG!`4]\$;\ +M_P"@DT2V*?/2$@.@S#46K/Z@J#4'[0L,&?9:#0P)!@(````,'J"NDYT*%NG\ +MF#&(1@P?EY@+R$&H5L>:!`P=TF$&X@(5H@(4T@(2#`G"`A.-"=/Y``P=P_D& +MH_D,X_D2J&'HH;/Y&*"MD\T.X,V3P_D9H_D:DF/)F%;2`A;"`A>H1M/X`,/X +M!J/X#)/X%//X'X)CR8Q^#`X,"(EQXF$*HA(#D@(YH-$4H+Q!H/,$N8&@N!0F +M.0?BR_X,'."\@^(2"*#`!`=J"H'C`3WP@.X@XE((X(T$#$DR81$B81`,"N`N +M!#T*(#F3#"DM"H`ID^",!(`B(`R)X(\$@*F3B$8PJB`@JB`X5BB!J9$S^`@C +M^!$X42C!T_@5L_@7\_@9P_@:.B(;(C$W`*/X'(G%(F.YJ#4B(1`R(1&0JA"@ +MHT%F&A5V@`J(-9"($("#0688!PR)!OO_````%LP+F$;<":*A,*6MY@=J"**A +M,"6MY@?J]OP'L0,"HB55L*H0N-'8L:"@];>:`>S+U[HJXB8;X.1!X.K`MDX& +MK0+E5/\,%PP/^=$&!`"BH3`EJ>97Z@<,!X(F&X)F')CAHBG/[$-/[$9#($(#3!/#H$*T.T_L2 +M#!_`SY/#^Q/@KY.`P`2C^Q2M#,"ODZ/[%:T-EX@"QBS_&ZV&*_\`N&*YQ:@U +M#(F0JA"@HT%F&A)V@`K(-9#,$,##068(IOBVA[^@K0*RH,#E%_^&\O\``*$#`I(E5:"9$*@QD)#UISD" +MQJ;^=H`5P0,"LB55P+L0R#&PL/7'.P(&H?X]\,;X_P!X\9('_-+)_1:]%.+) +M_!9N"O+)^A;_$QWP`,"`%(+(_19("ZT"Y2;_AK'^!VP["[L@HB#E)?\&KOX` +MK0)E)?_&J_["$FB0S,!6+);2H,#:TM)B-<95_N(2:)#NP%9NEO*@P/KR\F(V +MQE;^K0)E(O_&G_ZM`N4A_\:=_K+*$(+)$("$(;"T(;FQB1'&9?X`Z!'8X0P, +MR5;2+<7G/0*&V/_HL7GQ"^[I,88=``QO\D?\QE3^DD?\1E/^#%@,"0P:HD?] +MF4:95H)'_!WPP@(\5LR,NM+28C4&,?X+NZT"91O_QH/^Z.'B+MCF?B`@HB!E +M)?^BH3`E>N97:@BBH3"E>>97ZO;R(0[R+]BF?][")@68$8CA&\S)5H(HQ0P* +MHF8BE[@PK0(,"]BQY9$`X0,"TB55X-T0Z#'0T/7GO:9V@!"!`P+R)56`_Q"( +M,?#P]8>_D@;Z_WCQAJK_K0(E6O\=\#9A``R+44\`,A(UTA(TP4X`@J4D("1T("@N"U!+)"IA8T0@OT%J])XJ``X_81,B(U#`1IE4Q6 +M2:79M6G58B(V2>4R`PBR!@CHY0#S(P#K(W?C*W?K5/#OD"#NH.+>!.(N.ZKN +MZ?6R!@@`NR.PNY`@NZ"RVP2R*SNJN[GUQA4`=^LHX#Z0(#.@,M,$,B,[JC,Y +M]?(&"`#_(_#_D"#_H/+?!/(O.ZK_^?7&"@#B`J0`[B/@[I`@[J#BW@3B+CNJ +M[NGUL@*D`+LCL+N0(+N@LML$LBL[JKNY]3("I``S(S`SD"`SH#+3!#(C.ZHS +M.?5"91!"91'B!X@<#[$Y`!;^(@L^%F,U,L[^%D,_XA(\\:P`8@*$G([@X.1" +M4CP6!@$+-A:S.S+&_A931#+&_19S1N(2/6("A9Q^X.#D0E(]C/8+-A:C.C+& +M_A:#0S+&_1:C16("A/*@G!86`>+&_Q9^(#+&_A83,^+&_18^/F("A186`3+& +M_Q9S(N+&_A8>,C+&_18S/6("AA86`>+&_Q9^)#+&_A83,>+&_18^/&("AXSV +M"S86DR;BQOX6/C#RQOT67SL,`]('EN("@@P?#`;BSO_0;X/@/X/2`CKB!XSR +M`D/3_@'2!Y1C_@/S_@5B`H_R"'#3_A)C_A0S_AWS_A[B93C2!X@RW`1B"3_2 +MS?X6O0_2"3WH`M/V!6)E.4)E//(N,_D#,@APXBXM\J!\,_X(XF5"T@*DZ`DX +M&0#=(]#=D"#=H-+=!-(M.\`S$<#N$:JMHF=9TBG],.Z"HJ!XZMW2:?ZB:UTR +M)UDR:U[R:UWB)UKB:U[8:=)E1:@)V!DQ?`'`JA'3^A2B948R94CR"'!B$AFB +M$ACRS_T6WPC`JA%C^@RB94ER!X@,+&("@HR7"^<6'AKRQ_X6_R`,#O("1^/V +M`_#Q!//V#6)E2MC9TF530F59J,EX`L"J(*)E6'(G-')E0#((<#+#_1;3+S(" +M1U=C#4$-`E*A!%)K74)K7AWP#`K"H03":UVB:UX=\`#2`H)6W>_B"3[2"3L6 +MKD0,']#?D^T-X_8$QKG_8L80HLH0H*0A8&0A"V8+JL"J$8;7_P``,J$`,FM= +MX@B0P.X1\.X0XFM>8@*"%K8R"^86?C:F1@(&FP#F)@*&F0!"0H1"0H5"0H9" +M0H>&:/_Z8@PN%L;?,A8`*1%)(7DQ0@8,3%"!@QR%@`B%@-#_@5" +M%@'B91HR91LB91PH$7)E'4)E'G@Q2"$&:_\`^F(,+A;&VS(6`"D122%Y,4(& +M#'(6`B(6`4/^!4(6`^)E)3)E)B)E)R@1#%((09C_P#Z8@PN%J;9 +M\A8`22$I$4(&##(6`B(6`T/^!4(6`>)E*C)E*R)E+/)E+2@10F4N2"&&6_\` +M##X&F/_R(C;R#PKP.\!6HQP,#H;T_@``8J$`8FM=,@B0P#,1\#,0T#,@,FM> +M8@*"%@8E"^86?B&F1@)&:@#F)@+&:`!"0H1"0H5"0H9"0H?&'/\`#!YB(C;& +MLO\,'F(B-L:^_PP>8B(VQLK_#!YB(C;&UO\R"3L,'PPN,.^#!GG_\B(V,B(U +M\@\*,@,*.0'P,\!6LQ8,#H;3_@``,A).,M.`%K,9XE)/!A#_`#(24/`SP!8C +M&>)24484_P`R`CD,%@LS,#:38J$`8FM=X@B0@#,18#,0P.X1\.X0,.X@#",P +M[B#B:UYB`H(6MAX+YA8N'*9&`D9&`.8F`L9$`$)"A$)"A4)"AD)"AX;N_@P. +M8B(UQH3_#`YB(C7&D/\,#F(B-<:<_PP.8B(UQJC_8B(V,A8`,M.`%K,6XE8! +M1NO^``!B(C8R%@+P,\`6\Q7B5@.&[OX``&(B-3(6`#+3@!9#&>)6`4;A_@`` +M8B(U,A8"\#/`%G,8XE8#AN3^``!"0H9"0H="4CQ"4CT,+N)"A.)"A<;,_@#R +M`DH&D/[R"3I"0H9"0H<6/PPR!XP+,Q;S"M)"A0PNXD*$QHC^ +M`.)6`$:0_@``XE8"AI;^``#R"3H6GPLR!XQF$P(&)P#20H4,+@P_\D*$XD*& +MXD*'!GS^```R"3I"0H9"0H<6\PKB!XP6W@G20H0,+_)"A49T_N)6``9\_@#B +M5@*&@O[M#48X_@`X`5;3\>T-AC7^#!\,#M#O@T;L_M)"A`PC##[B0H4R0H8R +M0H?&9?X`[0W&+/X,+PPS,D*$,D*%\D*&\D*'AE_^TD*$#"[B0H7&7/X`#"_R +M0H3R0H7&6?X`TD*$#",,/N)"A3)"AC)"A\94_@`,+PPS,D*$,D*%\D*&\D*' +MQD_^`-)"A0PNXD*$QDS^``PO\D*$\D*%QDG^`#9!`(("]D("]0P)?/*`1,!` +M*9,=\````#9!`%("[,*@`V("]5!0%!:E``N%%N@()B4")C5^#`B2`O:B`O1J +M0()F@RIFR28&B28!":1\R4DXR4D\R4E`R4E%R0J2"0J7" +M$A3B`IGR`IC2#8C20JCR0J;B0J?"4E8M!QWP`'SR'?#"0NQ&ZO\,&,;<_P"B +M0NQ&Y_\``#:!`$DA#`HY,8*C;"D!*`%989AABB*(,:)B@*)B@9%$,#=)JR8IWB"&2 +M:LGM!X/^".E!\_X0Z<;9QMG&V<;9QNQELB95R&%`NQ"PL/4;V]<<*,(B@,#$ +M0<#+P+9,'*@!983^#!6&!````**A,.78Y5?J!PP%TB*`TF*!XB/_`D;9_Y'Y`':`#:(F0PN9H*`4%HKT%EGTQOK_*&$=\```-D$` +MF+*(HC$/`CG"^XC[F9"408"$09"(@I$.`H"(L)"($9J(DJ_`D(@0B=(,`AWP +M-F$`H1`"O0$,#&(B3VD!2*)HD@P-^T3[9F!D04!$0:7``CT*L1`"98T/##4, +M#**@X'*@F+*AO+JR>G*JHJD#DB)0DF,^PF,]4F,Y@B(\(J&`B4,J(\)"SK)C +ME+*@")('H))"U(('H8)"U>5IY:T#(F$!Y7L`##D,#`PM+0-2PQA`9H*RH("Z +ML\)+E-)+E@P>XDN5DDN2DDN0@1$"8&:00J#T2D.09A&"9U2M!D"T(,*@`=*A +M`"6W`J)B3HLB5Y+IH1("O03"H`'2H0#EM0*]!%@1#!RB8SK2H0"A$P*EM`*B +M8SNB8SPBH`BQ%`+2HH#0TX""R\!VHF>2"`#""P#"33+""`'"1?/""`/B"`+B +M1?3B"`7R"`3R1?;R"`)%]\)%]<(+`<)-,\(+`^(+`N)- +M-.(+!?(+!/)--O(+!R(+!N)--R)-.(N[PDTU\DTYDD7RB]V+50P"X14"XF=5 +M'?```#9!`+ASHB,&L*J"L<4`0B)/H*H1L*J`Y7?:#`JQ%@+-!"5XV@P:)93: +M4L00\1<"XB(E2Z3Z).)B(MACPJ#8LJ#@^]W0U$'29#>(<[JSZ`3[B("$08)D +M./AC\FX*V'/2;@ME=@^2!1`,%C=I#[+$*$P,HJ)RJJ0E=0^2!1!':0RRQ&A, +M#**BLJJDY7,/PA0.YBP"AB,`#`H,&W:`!QNJ\+L1QZL#1OS_`)*A))J3,J(J +M.C2B0]S"`]7"9;^R"7AQ&`(62P:""7^"0]+R"7WR0]WB"7[B0]#2"7O20];" +M"7G"0]6R"8&R0]MZM*()@*)#VH()>H)3;/()?/)#U^(I(>)K'M(I(M)K'\(I +M(\)K'*(I)*)K'6)#WI@%)AD$H@*`C/H=\``,"H;?_PP+LD/>1OG_`*T$)>H` +ML@,>J`2PL#2R1?FXJJBZ^[NPQ/3[JK"T0:"D0:)3(\"J@K)3(J"@]*)3(<($ +M(7I4!VPA#`P,#;*@^*"JH."J$;"T@"64`K(3(:)E0;"[H."[$65@#[*@D*$9 +M`L(3(](3(JJDP,P1P-T1TE0,PE0-I5X/HA,AMCH$"YI&```,&9"@])P*#`D; +MF:"A059Z_Y)%C6)"@!WP#`NR18UB0H`=\``VH0`Q-P!R(D_"(ZQM!Z@'P-`$ +M!VP2?.F0G!"28ZS`(`""(ZR)`<`@`.$Y`$$:`K*@_T)J)T%.``P(\1L"\F2" +M@FHJLFHELFZ"^$92QA"Q'`(+GQ9I+)+/\19Y*@P?LFHK6:%2H=5\_EI6D@6` +M4@5_@FHN@FHP@FHSXFHO,)D1X1T"0%41XFHRD%4@#.FP52!2:BU2:BR2:C0, +M.[)J)I$>`I)J,5(J*U)DPE%/`+(J+;)E0K(J++DELBHFLF1"\F1$LJ$`\F1% +M\F58LF28LM0$DF2`XF3`DJ`.X68`@FL`DF5`LBHGX+L0LF2"DB2"C*W"8ZS` +M(`"2(ZS`(`"BH6RJHJF!95SE#`^R)CNA'P+BH*CJYI(>Y-(>XZJFPBI_"]T+ +MF")CCB)C>0W2#`B`'`[A&`[B"(H>)E +M1M)E2>(($!P,R7'@L`16BQF8"`N9%CDDF3,D@R?DF3,HB)_97WEPJ'4RL?)D<(,>`RZ%@P4'%:!(`+2)Y1V@"4+ +MB,(D@[(DPY@U\B5#XB1#P+L@L)D@D/\@\.X@C*X6F`_H#28N!L;T_P`6V`Z( +M#8+(_H!J@Z#VP!8O'0PE#!JE4]JB(]VI(9(A`B*@"%"9())CW7:`"<(CW/?#@W1#28]UV@!"2(^>9,8@Q@(`4B3'X,28_`@;Z_ZB! +MI4/EJ)&X!\&#`,)D@K(K)[)D@J(*>B8:!-B1(DUZ]J8$#)[GMA$A`N)J*T94 +M__B1\@^%%J\6##(=\`P?6:&1(@*2:BL&3O^H,J+*_%8ZX\)DBT:0_RP<#!W0 +MVR#22!#"9(M&C/\`#"[I#256`@RZTB>4AL#_*)$,#_)"A`P"'?``'`:BH1"J +MIZE1I4T`LL<0N6&B2_JVRB'"URO"#"0,NAPF5LSIV)'2#8-63>GXD1P&#`[B +M3X,&HO\`F)%,"@P(@DF#@FDD93[:J&&H"@NJ%NH.K0>`D8G``SJIYX"QB8` +M)HX/9AX"!B@`:''BSO<,G>!M@V4SY0RZ\L;U5H_>(1T"'`8,2\*@C.(GE*@' +M#`W9#LJJY0#EB)&Q(P(,[.+4!`P-\1X"\F2`(F3`V0["94"2)(*H!PPOL)D0 +MDFHGDF2"\DAZ!GG_B)$,`B)(A0PR'?``5ACVJ&&B*I`,*9)J`"5!`D;4_P"M +M!^4M`*E!1L/_#,9&W_\`#+;&W?\`N&&H4;(+^F4\`$;:_P#8D:T'#!S"37CE +M.0\<5L;5_P```#9!`"$D`AWP-D$`(24"'?`V80`,'4@"<3<`\J#_#`QB)ZRQ +M3@!1&P)@H`0'9A)\Z("&$()GK,`@`#(GK#D!P"``,3D`@1H"@F0G4FN"\F0E +MPF0J\F."B$*2PA!1'`(+Z!;>#^+(\1;N#E)D*WSS@J'4BH+R"('B"(#"9#/" +M9##"9"XR9"\P_Q%`[A$Q'0(R9#+P[B!0[B`,[_)D-.)D+0PUXF0L4F0F41X" +MXB0K4F0QXFO"X4\`\B0M\FY"\B0L^2[R)";R:T+2:T3RH0#2:T72;ECR:YCR +MVP12:X`R:\`,Y3%F`,D/4FY`\B0G,/\0\FN"XBN"\28"#%M\_OKR%NH`8F>L +MP"``HB>LHF$`P"``XF_3XF_.XF_-PF\[PF\ZPF\WPF\VPF\YPF\XPF\UPF\T +MPF\SPF\RPF\OPF\NPF\QPF\PPF\MPF\LXDGYTF@EPDB"LDAZ'?``,2$",F0K +MAL+_@2("@F0K1L#_```V00`,,U$G`D*ARV*@C'*B2'IR:F)*0I($@X8&`*T" +MI8(`/0J2!(."R?T6J`L+J19:"[9#`L8K`++)_A;;"L("(,"0!`=L(-(B`-(M +M!29=%P`UIN`0IO*@_I("(";.!?"9$))"()"0!%89^ZT'Y4$`9AH"QB0`@B9Q +MB`B2H*\F*'^B!'T,VZ>YFK<:9,*@L\<:D=*@MM>:B^($@I$@`E9N!\(BE/%. +M`(%/``N9TB^#LB_#HB@#@BA#\B]#T+L@L*H@H(@@@/\@%A\&%AD&J`R!3P#Q +M3@!F*M&X#"8K(ZT")2L!/0H&S_\`+0,=\`RR'?``P@9^]LPS!*(*@(PJ'?`` +M`%($@'T(+!M:PIK,&R4@('0R3()B!'^WL@%]`G<6W7)$@!WP#!D@H[!ZJI)* +M@(;M_R"SL'J[@DN`QNK_#$P@T[!ZW<)-?X;G_P```#9!`%("`3("`@P'+!17 +M$QL;96!@="I54@4$1[83@@(#8D(!"XB"0@,M!1WP(J#_'?"2`@-R0@$+F9)" +M`RT%'?`V00`,%I("`8("`@P*+`0;>'!P='AF" +M)G&(""8H!0;T_Q::&/I7D3DP5P`#.WBTP0,"_$=`J@'@1X"@F.` +M\F/`#"^Y#M)D0,)D4_)D6:(J+$Q9+`S`JB"I)+GDF=2"!1S8`0"((["(4W"( +ML((H3MJ(B?3B!20`[B.P[E-P[K#B+D[:[NGTP@9^<,RPPBQ.VLS)]*(%WZ+* +M_A9J$`P9F;3")YBA*`+)(2>\5>@A>3&J9^#BP.D1#`)X(;(5)WIRK0>ERPX] +M"J)F@+(5)ZT')2NW@QQ@``JF<,`L(5)S(F@`O,QY,3LF:`TB9_/0&````G!0`-::`$*:)`3@!K`.8`0M$%JG^ +MJ`'1^@"L:@`]IL`0ILD!N`&R0@!I`2@!'?```#>F\!"F^0'H`18._6D!#!1& +M\O\,$BD!*`$=\#9A`)'Y``P&#$4,^@`UIH`0IMQX`#JFP!"FR0&X`8PK:0$, +M&0N95BG^#!(=\*'Z```ZII`0IH'6`0`XIK`0I@`XIJ`0IG$J`G!R@-('?T*@ +M`58]`<@"LE(,HE(-J;RYK*T"Y1D`0D=_LJ("`#NF\!"FHJ4",J)".B+R0KT` +M.J;@$*;"H@,RH0+2`KW@X'3B4F`6O0F"`L\66`B2`K^0E,"20K\`,Z:@$*:@ +MH'0F&GAB0LX`/*:0$*;V*3``,Z:P$*8,"0`SIJ`0ID")(*"8DP`SIO`0I@R> +M@-D1\D+0X-T@`#VFP!"F#`(=\```PLG^%IP+TLG]%OT+#`D`,Z:@$*:@E),` +M,Z;P$*8,GH#9$?)"T.#=(``]IL`0I@P"'?```&)"OP;?_P``0D+.QN#_``!" +M0K\`.J;0$*;BH!>B$A?20M'0T'37/GNB9R(`/*:0$*;V*3@`,Z;@$*:2H``` +M/*:@$*;V*C$`,Z;P$*8`,Z;`$*8,FD"Y(&)"SL";DX")$:"((``XIB`0I@P" +M'?`F*4_P`` +M.Z;`$*8,*J"9($;>_P```#NFT!"F'`E&U_\`-D$`01<"#!92H3U*0I($@#*B +M*#HR%CD(@@,4=^@26G*R!_\`FR,@F;!:F9()`28I;[(#'`";(W?K"B"IL%JJ +MH@H!)BISD@2`8D,F#(4F&4/H`MB^Z*[[W=#40?ONX/1!TE,DX.3T\E,CPA,C +MX-V"TE,BP,P1PE(,LA,DK0+`NQ&R4@WE!`"A&0)B1("RH)"JHF5_#BT%8D/I +M'?``#`7&ZO^M`@P,I9__L@?_(*(@PJ`"Y9[_1M[_``"M`@P,)9[_(*(@L@,< +MPJ`"99W_1MW_`#9!`%*AU%I2,@6`0A(,P2L"%D,$#LB?#B#3R)$/B)T/`NR"PB""`_R#P[B`6;A,6:1/8"B8M +M$H;T_P"H`@Q+PJ",RJKE5.2B(I38"M+-_A:]1/("(%=O"8$M`H)C'X8!``"1 +M+@*28Q_2H@*A^@#A-P"(`I$O`E*A`HA8PJ4"L3`"@LC[%B@7@3$"`#BF@!"F +M)L@##.(=\``ZIH`0IOB1@D_/`#6F\!"F`#6F@!"F`#6F\!"F`#6F@!"F`#6F +M\!"F@J,"`#BF4!"F@3("4%6@@%6@^!5909J"B5&""(-2H0+P\/06V#V"$@SW +MF*:2H*B:DIEA^&'R#V(@_[#R+TYJ__)D1``UII`0IKB1L@O<%MLY@B$)@@C3 +M%O@YD3,"B)&X40P/\DC8DFLR\J0"`#^FL!"FDB$)D@G<%BE%`#RF@!"F`#6F +ML!"F`#6FD!"FK&EV@`T`.J:`$*8`-:;P$*:<7\;Z_P```%8)[ED*98(*(@8+8@X`@`HB/QDB/W(*J@HMHDF2J"(^0,&ZT" +MX`@`LB42&T2W--/H@1ONZ8'1-@(`/:;`$*8F'`Z!-P(`.*;P$*;RS_\6[Q*8 +M<:(E$QN9F7&G.0(&40"XD:T"#`1"2R*EEP%6:AS(@1;\"^$V`@`^IM`0I@P$ +M)AT"QMW_#`[Q.`+R8SWR8SSR8S7R8S3B8S'B8S#B8RWB8RSR8SOR8SKR8S?R +M8S;R8S/R8S+B8R_B8R[B8ROB8RK1,`(`/:;`$*:"I0(`.*:P$*:PL'2Y@:*B +M`@`ZII`0I@`XID`0ID)C]_(E$@P$%N_S@B/>K0)@MB#@"`"R(_&8D:(C]R"[ +MH++;)*DKDADAPB/DE[0+K0(,"^`,``8"````K0(,&^`,`,(E$AM$QS3`!K__ +M`-(E$@P$%CWO@B/>(*(@8+8@X`@`LB/QF)&B(_<@NZ"RVR2I*Y(9(<(CY)>T +M"ZT"#`O@#``&`@```*T"#!O@#`#")1(;1,!F%&R*3R2*3VAV@"`NQ&PF2"@F2"A3P`,"))J3*T"@F)`948. +ML2<"Z)'840P,HDXBPFT]PFT\`#NFH!"FHLK@%AI3#!(=\"T*'?`,$@P_\DPB +M'?``B)&HF`RR#`F9"E)((AWP``#XD9"P=+)/TU;[Q9B1#`B"2=@&&?^(`HD1 +MB*BR`X#W&`V8$?FI9AL&#"NR0X`,*YA!^!&8*8B_D)#TF2&7&`N9OV8;!@PK +M#"F20X#RH*CZ\OEA)AL[R`*HO,BL^ZJ@I$'[S,"T]*)?Y,#$0;"J@L)?XY(? +MXZ)?XJ$9`L"9$9)2#((?Y+*@D*JBP(@1@E(-Y1H.F&&2&>*V.04+F88````, +M&0P+D)#TC+EV@`8;NY"1](P9AOS_J%&R2H&M`F6<_Z'Z`,*E`N$W`/A1B$$, +M&[)/@Y@XDF\>B$B";Q]&V?X``#RFL!"F^)&PL'2R7VM&Z/XX88(C$`S/FHB" +M9U+R9U0,"()G2()G1X)G7Y(#8`RKL_@%D_@)@F=@DB=ALM(C8/DC\FO"D)ZT +M0)DCDFO#DB=B&XA@^2/R:\20GK1`F2.2:\6"9V"2)V%@^2/R:\:0GK1`F2.2 +M:\?R)V)@CR/PGK2":\B!7`!`F2.2:\F`_Q#R8YP&XOZ1(0&0FR"2;O""(C<, +MB8_P```#RF@!"F +MF)&`@'2"66N&GO^B9U(,R+AA@F=4#`B"9TB"9T>"9U_2"V`,K_/X!=/X"8)G +M8,(G8=+2(\">M&#,(\)MPD"9(Y)MP\(G8AN(P)ZT8,PCPFW$0)DCDFW%@F=@ +M\B=AD5P`\,ZT8/\C\FW&0,PCPFW'\B=BD)\0\,ZT8/\C\FW(0,PCPFW)DFN< +MQI#_F`*(`9D1F*FR`X"'&0V8$8FI9AL&#"NR0X`,*X@1B+CW&`V8$?FY9AL& +M#"NR0X`,*_*@J/KR^6$F&SS(`JB\R*S[JJ"D0?O,P+3THE_DP,1!L*J"PE_C +MDA_CHE_BH1D"P)D1DE(,@A_DLJ"0H**`P(@1@E(-Y=D-F&&2&>*V.00+F48` +M``P9#`N0D/2,Z7:`!AN[D)'TC$F&_/\```"H4;)*@:T"95O_H?H`PJ4"^%'2 +MH@(,&[)/@T8]_[*@H/*@>$8V_[*A0/*@\`8T_[*@@/*@8,8Q_[*@L/*@D(8O +M_[*A8/*A($8M_X("()*@_I"($`P9D(@@@D(@#"(=\+B1#"JB2R)&E_X`X2$! +M\3<`V&'@ZB#B;_#8W0R,USP"QEC_@3<`#$_P^R#R:-U&5?^("E88W$:!_ZB1 +M#"F22B*&=__(D0P+LDPJAGG_```V00!\%EJ$.G(Q.0**)V*)VK_@BB!\B\D04\`,4X`BO_R9$1V +M@!6R(X8,&@P)T+L0L+:5L)J#C%P+S(QYQOC_#!(=\```%FS_6%&"HB2*5<(% +M*?P/HM;@.#`*RR_X6&TWH4?+>)B)O&R)O +M&<(.(/D!(FZ85VP+F$&!+0*":7X&`@``N$&A+@*B:W[(4=$[`F%<`'+<`=K, +MR1&B(06E4P""!=S"H`%6B`^2(04,#>A!F$D,#^(N@0NI%FH>HLG]%@H>LLGQ +M%JL=B%&(")BXB*@`F1&0B"")A(A1HA@-DA@,`*H1H)D@J#&28\8B9#BXZIC: +MP+L!P)D1L)D@F62R!=N`NQ&R8\@!#,B" +M8U22+C#B+C&"*Q:`F1&0[B``B!&`[B#B8TBR*Q8@ZQ'@NR"R8T>&!0"B(05E +M^`X;L_P"8,9(IF9+)_A:9,:(A`+(J +M,*(J,8"[$;"J(+':`+"J(*)D3*(A!2)J0&6R#<@!#`FB12HB;#$B;#"B!2HF +M.@@F&@72ROY63>,M"1WPZ%'B#B$'[@VH427/`)T*N%'(`88"`*A19<,%G0JX +M4<@!)CDPMDD,#!\,.()%*O)%*8;B_V8IM0;A_YA1HAD-DAD,`*H1H)D@F81& +MB/\B12D,*68YSJ(%*1:J!0Q-`#VFX!"F%NXF@@Q1#"F`B!&0B"``.*:@$*;R +M*YBB:T"GO\R2%227NL:X466Y_JA1)8``5LHLR`&H40P;LDQ2H@HA!VI=T@7; +M)BU7J%%EN@6="KA1R`'&YO\,;0`]IH`0IJ&1`0`ZIO`0II(,4/+/@19?"PPO +M@(D1\(@@`#BF\!"FH@Q1#"V`JA'0JB``.J:`$*:)(9(5)()K0)@1N%&B;N+2 +M"R'(`0?M#H*E`@`XIO`0IO)L-X8%`*@ATBPRH*J@T*J@TJ4"`#VFD!"FDEH! +MHJ$"`#JFT!"F%KT0`#JFX!"FK-YV@`\`.J;P$*:"*[T;B()KO9Q_HJ$"AOG_ +M#"Z+V8#=$>#=(``]IJ`0I@;1_Z*A`@`ZIH`0IO(%X@PH@/\1@/\@`#^FX!"F +MXE5J`#JFT!"FDJ("`#FF@!"F@D7;\J,"`#^FX!"FD@7;XD7C%AD$TJ,"`#VF +MD!"FDD7@"ZFB1>`+F9"0=!99%0`90`P8H3P"HFPF`(BA@FPHL/@1\.!@XFPK +M\L__H(@1@FPI\FPJD@7;TLG^%ET.X@NTC(X,Z0P_\D4J!AD`@@LA)V@YD@7B +M#!J0FH,,^CN9H)EC#"J`F1&@F2``.:;@$*;2H0(`/::@$*:@H'2,2@`YIM`0 +MIO*A`@`_IN`0II@1@3@"@FD<@FD=@FD@@FDA@FDD@FDE@FDH@FDI@FDL@FDM +M#`E6B=L,'=),4J(+(:"@!%:J"JA1):4`G0JX4<@!!F?_````5BFS\J`"\FX` +M)94`XB4+QLC^*0X,(B)%*@RR'?`,$AWP#"B"1=M&-_\```!6&<*B(0/R80:B +M*FK280<,*9D*Y9$`#!S8$+J:)%X0N9 +MD)!T%BD$`!E`#!_A/`+B;"<`_Z'R;"R@CQ&";"VP_Q$+CX)L+O#P8/)L+P:V +M_X$S`H)L)H:Q_ZA1I8\%G0JX4<@!1CS_+0H=\)$S`I)L)P:M_P```#9A`(*B +M`@`XII`0IC*B1#HR@@/$S!B20[M"H0(`-*;@$*:RPA0,#Z%.`-$?`H(3%,(3 +M%=K2`(@1@,P@PFI3D@.[P@/$\DOTDLG^%ND(%GP5DBU_@BLU'.^:B()J3O)J +M5,(3%<#`),)J1JS.J`%V@!$`-*:0$*;2`\3B`[N,;9Q9/?#&^?\`&ZHF+@CR +M(RSRSP'R8RQ6B?ZI`0`TIJ`0II(#P@PJ@@/$@)D1H)D@%G@+`#FFP!"F`#2F +MX!"FT@/$/?`6_0H`-*:0$*;R`[N20[PF+P220]$=\!WP``"2TB-67`""*M&"((X)IQ$!5(U)IQ<)J8((J8<%<`(!> +MM&"((X)IQD!5(U)IQX(J8L#($(!>M&"((X)IR$!5(U)IR<)KP<:]_P`YIM`0 +MIM)36D;0_](;!,@!X@.[HA-:T,R"4M(CRJHF+C'B`]&L3I(ES))ERZ)ES*#Y +MP!9?\I"*P()ES0;'_P#"TB:2*[B9`9FLAJ;_DB7+1O;_`&(ERV":P))ES](+ +M#-#2!!9=[R(ET68"!"T)DF71('$AH*>`YAH"AB<`YA("!BL`O0+E2@W-"FD1 +M:!%J9Z863.82`@8I`*T&O0+)(65)#9@AH+G`HB7,\+L1LF70>JJF&D/F$@(& +M)0"]`F5'#7T*IA9$YA("!B0`8*8@(+(@)48-H,?`\,P1PF7.QI[_YA("QB`` +M8*!@O0+)(65$#:"@8)@AQNK_`*82?*"@8+T")4,-H'!@YA:ZIA)WO0)@H&`E +M0@V@H&`&[O\`:1&F$FZ@H&"]`N5`#:#`8`;7_P`@L&!I$>4_#:#`8$;3_ZT& +M(+!@R2'E/@V@H&"8(<;4_R"P8"4^#:!P8$;9_ZT&(+!@)3T-H*!@1MK_K0:] +M`L)A`B4\#9@AALK_O0*E.PU]"H;/_P``K0:]`J4Z#0;1_P"]`B4Z#AXF4HL-X1T)!@DF4K"]V@[A'B +M92G292I2!-,F)612TB9R15+R`B%B%&L';PDB)3)B4@$,`AWP8F4W#`(=\``R +M`J@RP_Y6X_&"!-P6"`L`-J:0$*:&Q/^R`K0]\!9[\Z)$(@SB'?```#BFP!"F +MPD3;AL7_`#6F8!"F8&!T8E1KAM'_`)($W!89"``XIM`0IF($V1:6_%+2)@MF +M8&!T8D39%G8'LF4G`!9``(>A@F4LH)@1DF4ML(@1"YB292Z`@&""92^&UO\` +M`#:FH!"FAJK_`#BF8!"F8&!T8D38QL'_PF4FALS_````-J:0$*:21,Y&K?\` +M-J:P$*:R1-#&K?\`-J;`$*;"1-6&E_\`.*9@$*9@8'1B1-G&W?\`PF4GQK__ +M```V00`,%6*E`D+2(Z(B0#*B+#HRLA,A)1,-HF3QLA,AHB)`I14-HF3P@@(A +M!^@-`#:FD!"FDF3WA@4```"R(D#")/*PNZ#`NZ``-J:@$*:B6P&BH0(`.J;` +M$*86K!,`.J;0$*:<77:`#P`ZIN`0IO(C,AO_\F,RC"Y&^O\``#JF@!"F\@/: +M#"Z`_Q'@_R``/Z;0$*;24V8`.J;`$*:RH@(`.Z:0$*;"HP*20],`/*:`$*;Q +M,P*2`]/1/`*"0]N\>0`\II`0II)#V`NYLD/8"YF0D'06^1'29.8`&4``A:&" +M9.BPN!&@B!&"9.FP@&""9.L+N[)DZI(#T[+)_A;K"\("M(Q\#.(,/=)#(AWP +M\@(A)^\"AB``D@/:#/N0E8,[F;"98X"9$>"9(``YIK`0I@`ZIH`0IH"`=+P8 +M`#FFL!"F`#JFT!"F#`+!.`+"9"K"9"O"9"["9"_"9#+"9#/"9#;"9#?"9#K" +M9#L=\````#JFT!"F#`+!.`+"9"K"9"O"9"["9"_"9#+"9#/"9#;"9#?"9#K" +M9#L=\``,`N$X`N)D*N)D*^)D+N)D+^)D,N)D,^)D-N)D-^)D.N)D.QWP`#RF +MD!"FDD/9"[FR0]D+F9"0=*R)TF3G`!E``,6APF3LH/P1\F3ML,P1"_SR9.[` +MP&#"9.\&PO_R9.9&OO\`\F3GQK[_```V00!!/0(`-*8P$*:"H0)2P218!1;# +M!0`XII`0IC"GP#K7%HD%J04`-*9@$*9"P2@RP2`X`T@$O.8`.*:0$*9@H\!J +MX[S)J01H!6P'=Z8$8L9`:06FQ@2RQL"Y!5@$/?!WI012Q4!9!.;%`1WPPL7` +MR00=\'D%1NO_.01&\O_9!<;H_P#I!(;O_P```#9!`,$]`@`\IK`0I@PNTJ$" +MHL$DJ`H6:P@`/::`$*:`DA'@F2`6.`@`.::`$*8,'[#_P/#S@OKW@/_`"__Y +M"@`\IL`0IG+!*++!(+@+>`<6+`4`/:;P$*:`DA'@F2`6+P8`.:;@$*8,'<#= +MP-#3@MK;X-W`"]W9!R@*5Z(#:B(I"B>D!&#BP.D**`=7H@5@(H`B9P`G)`$= +M\&#RP/D''?!Y"D;F_[D'1O/_````.::`$*8+^_#S@OKW^H@;B(D*!M__```` +M.:8@$*8+C("#@HJ+BB(;(BD'1N?_```V00"B(D&2(I0,*!NJHF)!B0EE```= +M\``V80"BH`$E[]?EZN*A/@#13P"13@#A3P`,#,)I@,)IP,D.PFU`LBF"N0&( +M`:"((()I@AWP-D$`>`.S +M`PP"'?`M!1WP```V00`,%1P#)[,##`(=\"T%'?```#9!``P5'",GLP,,`AWP +M+04=\```-D$`#!4<0R>S`PP"'?`M!1WP```V00`,%1QC)[,##`(=\"T%'?`` +M`#9!``P"'?``-F$`K0+E%@",2BT*'?```%%.`'$9`K$W`)$^`GRXHBOPPBO= +M,J(H.C*`C!"":]V"`]="H*A*0H+(_A:H#8$?`8"*$()K\'IR82H"FI*9`6IB +MB`&"*,VM`KT'X`@`HB8\L3X"DB9"(*J@NJJ2:C&")BJ]!ZT"X`@`P("0PBT4\`)BAQF*/2+4SR$R,, +M#-#H=/#N@M#0=.K="ZW0K(.IX[D)LD,GX@,N#!_R0R46S@2R)&^A1@`;N[)D +M;Y(E@0R8H)D0D)M!D"B3'?```/$A`0R-\/H@\FOPXB(W#$B`C"#G/0(&Q?^" +M:]V&P_\`R`E6?/@M"AWP(D,F1NG_`+)#+K(D;Z%&`!N[LF1ODB6!#)B@F1"0 +MFT&0*),=\`RR?/T,'N)#)])E@1WP-F$`D<,`,4\`@M(KB*B"80%V@!&B(T,+ +MF8SZ%IDALB*4N`LF*PC&^?\````6B2!"PA#!3@#R`B&BHB*JHN(*W?#Q!-#_ +M$?#N(.)LC9A"#!;2R?T6'1+2R?$6O1'B`B#X`N#@!(B_^*]`[@$`B!&`_R#P +M[B#I@X(4!7(4!)%&``"($8!W(')LQE(*WW($$`P+L%41<'4$T'<1<%4@4F,X +M\B0TXB0S70O`_P'`[A'P[B#I8]@$\@04<@K=X@00@@K:@'<1X.`$D(@1,/\1 +MTLWQT%F#@/\@3%U`CA%0[B"`=R!P_R#P[B#B;,>YX]G3P@H:B!$`S".PS%,@ +MS+#"+$Z*S,GSD@HB`)DCL)E3()FPDBE.BIF9\W($^E%``B!WL'(G3N$_`M%" +M`HIW>?/R!!#"TB-Q00('[Q22"N7V>5?Q0P+P^:#X#ZD!H`\```"Q1`*2"MVR +M;.6Q10(+B18H)JD!XLG^%BXG\LG]%A\F(48"TFS>(FSD#`(=\+(2#9("(((2 +M#`"[$9"0!$"9`;"(()"(((F#!KC_\4<"\FSE\4@"D@K=X3@"4M(F)ADQ@LG^ +M%M@-9CD"1B0`#`(<'L$W`I%)`K)C.-)E'I)E'\)E&N)%4+)*+!WPLDHL#`(= +M\`!B8Q6"!!"I`2=H$)%+`J%*`J)LWI)LWT8#````L4P"T3\"TFS>LFS?J`&" +M"N(,`@P+@L@1@(!T@D50@(@1\(@@@F4:XFP[XFPZXFPWXFPVXFPSXFPRLDHL +M'?```*(BE$+"$`PIF0HEMO]&>O^130)B8Q6""N)R91Z291^"R!&`@'2"15"` +MB!'PB"""91KB;#OB;#KB;#?B;#;B;#/B;#+2!*06+?6I`;@!#.(,.J)++!WP +MJ0%B8R6R8Q72"N,R"N(<*()%4#>]"`,X"`9L`6AP>B)0^R)0U` +MJH*F&C"F&QXEA0RB8D.XU:830N8;R;"P8*T#Y8,,H+!@LF*[!O#_L+!@Y8(, +MH,!@PF)#QO7_IAL1H*!@Y8$,H-!@TF)#1O'_````Y8`,HF)#1N[_``"F&PVM +M!N5_#*#@8.)BNP;@_ZT#)7\,HF*[1MW_J`$,`@P+LDHL'?``#`)B8Q7B;-ZR +M;.0=\')LWK)LY`P"'?``B`%B8R6""!H,`O%1`@"((X)C%5)LWO)LY!WPJ`$, +M"Y%2`I)LY89F_Z%3`@P+HFSEJ`%&8_^Q5`*H`;)LY0P+!F#_J`$,"^%5`N)L +MY<9<_Z@!#`OQ5@+R;.6&6?^H`0P+@5<"@FSE1E;_J`$,"Y%8`I)LY093_P`` +M-L$`44\`X2@"#`>"(Q.2(Q'(T[CSK0)\[0P2T+L0T,P0T)D0T(@0@F,3DF,1 +MDF,>R=/"8QRY\[)C'8)C'V8D/0P,#`W]!YT##"MVJQAB*1RR*1V+F99V`=:+ +M`,#+0]#64_*@`3WPZFI6[QE&!````)8+_\#&0]#;4PP?1OG_ZFK2)G_B)H#- +M!POTG03PEX/C_`K3_`)S_`%S_!J3_!8C_!1S_!5S_!)S_!,C_!O)Q284%JAS +MN-.(PZ"@!+"PM("`U+/X#J/X'(G%G'3(D]CSLB,.P,`$T-"TL+#4T_L.P_L< +MLF4,\B:`XB9_W0?S_0KC_0(C_0%S_1J3_18C_11S_15S_1)S_1,C_1O9Q284 +M&:B#LB,1@B,0H*`$L+"T@(#4L_@.H_@<@F4,G'3(H](C$[(C$L#`!-#0M+"P +MU-/[#L/['+G%\B:`XB9_W0?S_0KC_0)S_0%S_1HC_1B3_18C_11S_15S_1)S +M_1,C_1O9Q284%ZASLB,+")4.HD<#`!,D!N`'160(;JA;+"JF1IZWA +M!BD`ZHVRS3_0O;."R""PMB&Y@:"[$9++0)FAAS\QP)O`DLE`ERXH*1&I80P+ +MN7$,&J58XM(E0ZAQT-`$V1'($>%9`ANJ%IP*J7&GKN%&*``,"IT#?/T,+W:O +M);(I',(I'28:#B8J'):[`M8<`7)I'<8"`(BABNOB:1R*C()I'8N91F'_^*'P +MB\"":1SP_,#R:1U&^O\`EES^TFD=QO?_'?``XAH-1L7_#`NH8;E!N+&2*C>B +M*CC`F1&PJL`+JL"J`:"9()E1F65&`0``J4&G*Q@,&F5.XMA1J$'H9>D!PB$` +ML5D"HLH!UYSA(D.,#!K&U_\,")AAB2&(@?(I-Y(I.,#_$9J(&XC`B`&`_R#Y +M,?EE!@$`J2&G*Q@,&N5)XM@QJ"'H9>D1PB$!L5D"HLH!UYSA(D.,#"K&Q?\V +M00!13P"A*`*"Q/X,"]T+;0N`9).JHO(J@.(J?S!FL//]"N/]`K/]`;/]&D/] +M%K/]%+/]%;/]$K/]$]G%R-:8QL#`M)"0U,/Y#IG%%H@1#!S2*H"2*G^-"]/X +M"I/X`K/X`;/X&D/X%K/X%+/X%I0^(&?HA3DB8\L5T"#!\@F:"ZTL(MUKJ9TBW7DBDHN"/#_0CS_1`, +M#+/]$:/]$I/]%X/]'`PXP_T=X_T>TF3)LB8\DB8[[0RS_@J3_@*13P##_@'S +M_AJ#_A;S_A3S_A7#_A+#_A/IR;(F.X(F/+/X"()I3/)D7^(%]-T,P_T%X_T) +MTF1@PF1APF1BPF16PF15'?"")C"JIZ)C!.`(`)@SL*H!<)D1H)D@HJ$+H)D@ +M`#FFJ$.&TO^2H@(`.:9P$*:"ST"`=Z!X!X:R_QWP`-JRLBL@#"JI"V4?_\:] +M_P`VP0!2H0(`-::0$*8,%@P%TJ(LF0-6N07:HO*D/^%>`G*@![*@%$+.8':` +M)0`_IL`0ICWPQ[L$8DHC#`S@G*"8"9D3)ED.DF,B0(R@B`B),YSEQO3_)D8%)D85)D8E)D5M)D5=(L\,(L\=/\",)G3!WP```6VC>2PACR +M*3J9H:(C!!:/(H(A#8(HY>`(`,@SF+&PV@%PS!'0S"`,O=#,(``\IJC!N$.Z +MJJE#V!/B"7["*3RR*3N((R#\H+/\"/+?)/@O@_P1H_P2\_P74_P=X_P>MCT" +M8_P0PF3)HB,B%NH4"\H6G!32ROX6G43HT?(N\.(N\?/^".)G3!WP@@(@HL(8 +MHF$*!^@+@J$"`#BFP!"FPF,"`#FFP!"FVI*VO`1B22,,#-%<`H@SHB,BT-R@ +MV`VBROR*W=DS%MHL#`[IP:'#``NJ\B2#%M\8%MH8B)F("`NJ9BCMF*&H0Y(I +M.L+2(\)A#19Y)H(LY>`(`,@SL-H!<,P1T,P@TJ$+T,P@`#RFJ,&X0[JJJ4.1 +M*@+8$X@CFI*9L<(I/.()?I(I.R#\H/+?)/@OD_P(@_P1H_P2\_P74_P=X_P> +M]CT"1E$`O078H9BQ8_P0PF3)6<-28QA28Q128Q!9TU)C&5)C%5)C$8(I.Y(I +M/`P_[063_@J#_@)3_@%C_AKS_A9C_A1C_A53_A)3_A/IQV)D7](-\*C14_L% +MT_L)LF1@4F1A4F1B4F164F15LBKPHBKQL_H(HF=,'?"8T:C#N-.R8QFR8Q6R +M8Q&B8Q"B8Q2B8QBB*?&2*?"-!:/X"I/X`E/X`6/X&E/X%F/X%&/X%5/X$E/X +M$XG'^-/HP]BA\/"TX.#4\_X.Z<=B9%_2#?#-!5/\!=/\"<)D8+C3J,.PL+2@ +MH-2S^@ZB9&&B9&*B9%:B9%6B(R+&C?^(T9C!@BCEJJFI0^`(`,@SL+H!F+%P +MK!&PJB`,N["J(``ZIJA#!G7_``!6JN?HH>(NC@PMTFX`)=_^AIK_`(*B`@`X +MIO`0IJ(C(N#_H/@/^<'&*?^M!;BAB+'"9,E9PU)C&%)C%%)C$%G34F,94F,5 +M4F,1\B@[@B@\##[=!8/]"O/]`E/]`6/]&N/]%F/]%&/]%5/]$E/]$]G'8F1? +ML@OPF-%3^@6S^@FB9&!29&%29&)29%929%6B*?"2*?&C^0B29TP=\```PM(C +MPF$-HBPJLBPRPBPZI94!HF$)J-&R*C/"*CNB*BNEE`'RPS2!*@*="JB!BH*) +ML;(H,\(H-=(H-N(H-*(*V/DAF0&2PS"9$8(H,?B1X`@`F(&B(R(&_/Z8P8(L +MY:JIJ4/@"`#(,["Z`7"L$;"J(+*A"["J(``ZIJA#!F7_``#B(I0,+?+"&/FA +MV0YES?Z8H08<_P`_IH`0IN"(H(@(B<%&2?\`R-&B+"JR+#+"+#JEBP&I0:C1 +MLBHSPBH[HBHKI8H!\L,TB-&="JB!LBCHPBCJTBCKXBCIH@K8^2&9`9+#,)D1 +M@BCF^$'@"`#(T:C#LBPVPBPZY9`!R-&I4:C3LBPWPBP[Y8\!B-&="O+#1*B! +MLBCHPBCJTBCKXBCIH@K8^2&9`9+#0))A`8(HYO(A!3WPX`@`J-&XP\(C$*(J +M+N62`:EAJ-&XT\(C$:(J+R62`?+#5(C1G0JH@;(HZ,(HZM(HZ^(HZ:(*V/DA +MF0&2PU"9$8(HYOAAX`@`HB,4LB,,PB,099$!N-.I<<(C$:(C%:60`?+#9(C1 +MG0JH@;(HZ,(HZM(HZ^(HZ:(*V/DAF0&2PV"9$8(HYOAQX`@`F($&I/ZM`KT# +MPJ``95__R*%B9%_"#/!3]07#]0E29&"XTZC#L+"TH*#4L_H.HF1ADB,1@B,0 +MD)"T@(#4D_@.@F1B\B,5XB,4\/"TX.#4\_X.XF16TB,9PB,8N-'0T+3`P-33 +M_`["9%7"*_"R*_'#^PBR9TP=\``VX0!2H0(`-::@$*8,&PP%\J)`J0-6>@7Z +M8N%>`D*D/QQ-J)C(I*A`@`YIJ`0I@P8#`RI`Z#(@R8%X,Y(C(H+#-*"LH*@*B=&"R?YZJJDS/\'K8]`F/\$,)DR=AC5GT(HB?PPB?QO07Q3P## +M]0JC]0*S]0%C]1JS]19C]11C]16S]1*S]1.S]1M9SYC3B,/(\9"0M("`U)/X +M#HG/Z-/8P])C&-)C%-)C$.)C&>)C%>)C$6)D7\(,@+/[!X_P0PF3)6<-28QA28Q128Q!9TU)C&5)C%5)C$:(G\9(G +M\`PXH_\*D_\"4_\!X_\:@_\6@4\`X_\4X_\54_\24_\34_\;^I8<(G.ZC392D!\L-$LB?HPB?JTB?KXB?IDL-`C0JB!L29$?DAB0&")^;X +M8>`(`*(G+KC#PB,0)2T!N-.I<<(C$:(G+R4L`?+#5+(GZ,(GZM(GZ^(GZ9+# +M4(T*H@;$F1'Y(8D!@B?F^''@"`"B(Q2XP\(C$.4K`;C3J8'"(Q&B(Q4E*P'R +MPV2R)^C")^K2)^OB)^F2PV"-"J(&Q)D1^2&)`8(GYOB!X`@`D<,`/?`+F:(D +M@Q8:)A89)KCQLBMRN`L+F68KZLCQPBP>J$,6S":"(0Z"*##@"`"8,["J`7"9 +M$:"9(`RZH)D@`#FFJ$.I0[@3Z%.((]CADB?PPB?Q\BT\T@U^D_P((/^@\M\D +M^"^#_!&C_!+S_!?C_!Q3_!W3_!ZV.P0,&J/\$*T"PF3)O0,,#&7R_HCQ#!F2 +M9%^""(#]!5/_!8/_"?)D8.C3V,/@X+30T-3C_0[29&'"(Q&R(Q#`P+2PL-3# +M^PZR9&*B(Q62(Q2@H+20D-2C^0Z29%:"(QGR(QCHX8"`M/#PU(/_#O)D5>(N +M//*B0"KN^NY23NC2)_"R)_'!3P#3^PBR;$P=\`"B)_&RHD`JJKJJ4DKHHBB")_#B)_&#_@CB;TP=\+CQLBMRHJ`"HFL`)3'^QHC^`)B1@B@PJJFI0^`( +M`,@SL+H!<*P1L*H@LJ$+L*H@`#JFJ$,&]OX```!6F=KH1@PMV0ZE+?Y&9_\` +MDJ("`#FF@!"F8(B@B`B)D<;7_@"(X8(H,*E#X`@`F#.PJ@%PF1&@F2`,NJ"9 +M(``YIJA#!F3_V,'HT0P/&ZH,.[)&U*"@=+(A!&4``$8U_@`V00#Q/0(`/Z:0 +M$*;2H0*,J0`]IJ`0II"`8*"8DPPN#!H+LA9["Q9)"\T+@+L1X+L@`#NFL!"F +MP(!T`!A``*JAD(%@"XB`BH**NQN[L(!@D+BC`#^FD!"FC*D`/::`$*:0\&"` +MGY,+TA8M"!;Y!X#\$>#_(``_IL`0II#18`O=T-J"VLP;S,#P8)#/H[HS.04P +M(R"PNA&PL&"WHP>@*A$@(X`I!;`Z$3%>`J*B+*"B@&+.8':`10`UIL`0IL>]'I@.!A````#@G*"8"9D3)ED6DF,B8(R@B`B),X(+D"8H(;R$/?#&[/\, +M>9)C(@`_IL`0I@P8#`G)`\"8@R89GP8'``#V*=H`/Z;`$*;"8R$6[/R"(R); +MB()C(E94_,@#44X`D4\`%CP(H@*H8L(8HLK^%FHAH5P`[0?RTB;2+S""+S'( +M0[(/4M/X",/X$G/X';/X'H)ER8(O,?(O,,T$@_X*\_X"0_X!<_X:0_X6<_X4 +M<_X50_X20_X3<_X9Z)DH')AH$V#,6O0``/Z;@$*;B8P6B(R(6 +MRD8+BA9X1A::2`N:%DE(LM(CPM(FR<&YT=+*_A8]5:'#`)BAHLK_XB6#%AXM +M%AHM^)GR+P"BRO]F+^N")CJH0Q;X/HC1@BCEX`@`R#/Q3P"PV@%PS!'0S"`, +MO=#,(``\IJBQN$.ZJJE#R,'8$Y@CLBPQX@Q2PBPP((N@@M@DB"C#^PB3^Q&C +M^Q*#^Q=#^QWC^QZV/0)S^Q"R9G)2()4I(I,"#[H/+?)/@OD_L( +M@_L1H_L2\_L70_L=X_L>MCT"<_L0W03!3P"HP;)ER4G#0F,80F,40F,02=-" +M8QE"8Q5"8Q&2*C"B*C$,./T$H_\*D_\"0_\!<_\:@_\6<_\4<_\50_\20_\3 +M^/]"=)E8$)E84)E8D)E5D)E5=(J\*(J\=/Z"*)L3!WP +M``!6BM/")HX,*[D,Y)E8N)E5N)E5>(M\-(M\>/]"-)O +M3!WPJ-&XP\C3PF,9PF,5PF,1LF,0LF,4LF,8LBKQHBKPG02S^0JC^0)#^0%S +M^1I#^19S^11S^15#^1)#^1.9SXC3Z,.`@+3@X-2#_@[IS](&""=M%=ACG`V" +M(Q'B(Q"`@+3@X-2#_@[B;PQR95_"!O"]!$/[!`(`+(&""=K3,AC%GP$ +MPB$-HBPJLBPRPBPZ96X`J4&HT;(J,\(J.Z(J*V5M`/+#1(C!G0JHH;(H*,(H +M*M(H*^(H*:(*V/DAF0&2PT"9$8(H)OA!X`@`HB,BAK?^`!WPF+&"*.6JJ:E# +MX`@`R#.PN@%PK!&PJB"RH0NPJB``.J:H0X8]_P``/J;0$*;`W:#8#=FQAB#_ +MR-&B+"JR+#+"+#HE9@"I4:(A#;(J,\(J.Z(J*^5D`/+#-(C1G0JHH;(HZ,(H +MZM(HZ^(HZ:(*V/DAF0&2PS"9$8(HYOA1X`@`R-&HP[(L-L(L.B5K`,C1J6&B +M(PVR+#?"+#LE:@"(T9T*\L-$J*&R*.C"*.K2*.OB*.FB"MCY(9D!DL-`F1&" +M*.;X8>`(`*C1N,/"(Q"B*BYE;0"I<:C1N-/"(Q&B*B^E;`#RPU2(T9T*J*&R +M*.C"*.K2*.OB*.FB"MCY(9D!DL-0F1&"*.;X<>`(`*(C%+(C#,(C$.5K`+C3 +MJ8'"(Q&B(Q4E:P#RPV2(T9T*J*&R*.C"*.K2*.OB*.FB"MCY(9D!DL-@F1&" +M*.;X@>`(`,9D_C:A`#EAHJ)ZB'/88Y%A`@P;T-N3FI+R*8'B*8#"*7^2*7[0 +M[Y/I0="O0+E:@I]"JG$N(&Z=WG$HB6`8*J" +MYAH"!B$`IA)PO0+E:`IM"JG4V)&H(<1&X?^]`J5B"GT*J<1&WO\@L&#E80J@8&!IU$;B +M_P```*82#Z"@8+T"96`*H&!@:=3&W/^]`J5?"FT*J=3&V?^0JH*F&AZF$@J] +M`F5>"JGD1MK_`""P8*5="J"P8+GDAM;_````IA(/H*!@O0(E7`J@P&#)Y`;1 +M_[T"95L*J>2&SO\``)"J@J8:'J82"KT")5H*J?0&S?\`(+!@95D*H+!@N?1& +MR?\```"F$@^]`J"@8.57"J#`8,GTQL/_O0(E5PJI]$;!_PP)AI;_`*J2D@F` +M?/NPF9!&EO^1/0(`.::@$*:I@;*A`HSJ`#NFX!"FR('`T&#@S9/)@0`YIO`0 +MIOF1%L_C`#NFH!"FB)&`D&"@B9.)D4:*_P`V80!2H'=!8@)BTB.2)LV")L\, +M1TI"D(C`@F$`HB1_LJ^)ENH!IR4"1B<`DB;/LB;-H*F"IAIXIAMEY4T*J<,& +M)0```+>J0<(FS[(FS:"L@J8:'*8;"25,"JG#!AX```"PL&!E2PJ@T PP8: +M````IAL-H*!@)4H*H.!@Z<-&%0!E20JIPT83`(%D`N#Z$?#RP(K_\B]_\/!@ +M^.&(P``EZH]N`&PJH*R)LVF&AFF&P,&$P#Q3P+@ZA'@XL#Z[N(N +MN^#@8.GCA@T``+"P8*4P"J#P8/GCQ@D```"F&PV@H&!E+PJ@@&")XP8%`*4N +M"JGC!@,`L4\"()J@NIF2*;N9XZ(D@-*OB9;*`:D`RT$'?`M`QWP'?`=\```-D$`<3@"=Q(6=Q,K=Q0X)Z,&-Z0; +M+0,=\">D#QWP``!W$VAW%#F6(P265`4P)$,=\">D#RT$'?!W%%JF$CF61`0G +M).\=\```)Z,,YA/&IA+R#`0M!!WP`.82P`8!````EN,`#`0P)$,=\```-R2E +MEA3\#`(=\```)R2@#`,P)$,=\`P"'?`,`RT#'?!P5,`,`E`DDQWP'?`V00!Q +M.`)W$RYW%#LGHPDWI!XM`QWP````)Z0/'?```"C"CD#BT$'?``)Z0! +M'?!`(T,=\!WP+0,=\#9!`">C"3D#2T$'?`GI`$=\$`C0QWP'?`M`QWP +M-H$`#!N2H`!A3@!13P"B(L9RHBQP9`Z(G.U;Z],(D\+(D\#_AWS_A[B9LDEN_T,"0P;N0/&VO^M`KT#PJ`!97__ +MD<,`"YF")H,6R#P6R3RHEZ@*DLG_9BKLPB)`LM(FV$,63#^"*R6Y4:T-X`@` +MB#/H4;":`7"($9"((`RYD(@@`#BFV$'X0_K=V4.B#E*2)/'B)/`,"R#)H./Y +M",+<).@CR"RS^1#C^1'3^1+#^1>S^1T,++T#H_D>DF;)K0+EL?T,"0P;!K;_ +M``""PS2B!]BR).C").K2).OB).GR)"J2)"N9`8DADL,PF1&").;@"`""PSS" +M).[2)._B).WR)"RXPY(D+;)D*K(D[*C3HF0KH@?9F0&)(9+#.)D1@B3GX`@` +MD<,`V./29"S(\\)D+0N9XB:#%AXO%ADO\B*4^`^2R?]F+^N"(D"RTB;80Q8( +M,X(K);E1K0W@"`#(40P;^#,,";"*`7#_$8#_(`RX@/\@`#^FV$'H0^K=V4." +M)/"B+#'R)/'H(R"JH(/_"*+:)((,4J@JD_\0X_\1T_\2H_\7D_\=@_\>\F;) +MB-/H\ZCC^,/R8QCR8Q3R8Q"B8QJB8Q:B8Q+B8Q/B8Q>"8Q&"8Q6"8QGB8QL, +M*.(L,:(L,/T)X_\*H_\"D_\!L_\:@_\6L_\4L_\5D_\2D_\3^<7HTZC#X."T +MH*#4X_H.J<6(\_CC@("T\/#4@_\.^<7&8/\`@L,\H@?9LB3LPB3NTB3OXB3M +M\B0LDB0MF0&)(9+#.))A`8(DY^`(`)'#`+CCLF0LJ/.B9"T+F<(F@Q;<'1;9 +M'=(G"=@-DLG_9BWKXB)`LM(FV$,6CB."*R6Y4:T-X`@`R%$,&_@S#`FPB@%P +M_Q&`_R`,N(#_(``_IMA!Z$/JW=E#\B3QXB3PB".M#^/Z""#_H.(,4I/Z$/+? +M)/@O@_H1T_H2\_H7D_H=X_H>HF;)B//XX_)C&O)C%O)C$H)C&X)C%X)C$^(D +M\:(D\(T)X_@*H_@"D_@!L_@:L_@6L_@4L_@5D_@2D_@3B<7X\^CC\/"TX.#4 +M\_X.Z<7&(?\`@L,TH@?8LB3HPB3JTB3KXB3I\B0JDB0KF0&)(9+#,))A`8(D +MYN`(`)'#`+C#LF0JJ-.B9"L+F<(F@Q8<#Q89#](G"=@-DLG_9BWKXB)`LM(F +MV$,6CA:"*R6Y4:T-X`@`R%$,&_@S#`FPB@%P_Q&`_R`,N(#_(``_IMA!Z$/J +MW=E#HB3Q@B3PZ"/]"H/_"""JH((,4I/_$*+:)*@JX_\1T_\2H_\7D_\=@_\> +M\F;)Z,/B8QCB8Q3B8Q"B)/&")/#]":/_"H/_`I/_`;/_&I/_%K/_%+/_%9/_ +M$I/_$_G%Z-.HP^#@M*"@U./Z#JG%AN7^#`JI0<;>_@P-V4%&\OX`5LG#\B*4 +M#"[I#^75_,8+_U:)T9(BE`PHB0GEU/S&0O]6R>*R(I0,*JD+Y=/\QH?_5HGQ +MTB*4#"S)#>72_,;"_ZA!N5&"*R7:JJE#X`@`N#/H4;#*`7"[$<"[(`R\P+L@ +M`#NFV$-&`O^H0;E1@BLEVJJI0^`(`,A1#!O8,PP)L.H!<-T1X-T@#+[@W2`` +M/:;80T8S_ZA!N5&"*R7:JJE#X`@`R%$,&]@S#`FPZ@%PW1'@W2`,ON#=(``] +MIMA#1G'_J$&Y48(K)=JJJ4/@"`#(40P;V#,,";#J`7#=$>#=(`R^X-T@`#VF +MV$-&I?\VP0`,'&%.`%%/`*(BQG+2`0P+%AH+0M(C%GH8DM(F@BDPHBDQJ4'X +M08/_"/E!Z$'80\/^'>E!J$&""5+3^A*I0?A!@_\>^4'H0>)FR;)C$;)C%;)C +M&;G3LF,0LF,4LF,8NA! +MP_X:Z4'80;/]%ME!J$'#^A2I09A!P_D5F4&(0;/X$HE!^$&S_Q/Y0>A!Z<6Y +MQA!XF9@TB9AT*ZT8-TCTF3"0*HC +MHF3#DB9BD(ZT8)DCDF3$0(@C^$&"9,7RSP'R9F#B)F'@WK1@[B/B9,9`W2/2 +M9,?B)F*A7`#@_07@C`7@FP6@KA#@WK1@[B/B9,A`W2/29,GR9XF"9XB29X>B +M9X:B(R`6RGB"H3\`.*;0$*8,+O(C(**A`MFS)B\ZLF,#LF,`\J)`S!T&!0(F +M'6:2S?X6B3*"S?T6*#:2)X96>>?")/#2)/'90;A!P_L(N4&H0:)E3!WP``"" +MI@(`.*;P$*;Y,ZR?F+. +M`HBALB3LPB3NTB3OXB3M\B0LDB0MF0&)(8B1B1&").?@"`#(P\)D+KC3LF0O +MJ..B9#"8\Y)D,9'#`(C#@F0J^-,+F?)D*^CCXF0LV//29"VB)H,62F$626&R +M)U2X"PN99BOLR`>H0Q;,<((DY>`(``PE! +MZ$&2V228*:/^$NE!B$'X4Y/X%XE!Z$'S_ASI0>A!T@U2L_X=Z4&80=/Y'IE! +MB$&"9LGX8U9?2Z(D\;E!F$&")/"C^0J90?A!@_\"^4'H0;/^`>E!V$'#_1K9 +M09A!#"JC^1:908A!P_@4B4'X0A!\/"T\_X.Z4'80=G%^/.8XZC#B-."8Q&"8Q6"8QFB +M8Q"B8Q20D-290>A!HF,8\/"T\_X.Z4'80=G%F/.HXZ)C&J)C%J)C$I)C&Y)C +M%Y)C$P8W_P`ZIM`0IOJ2@L,XB9'98X+#/(FA%LU>R5&9<0P(B6$,&F5GWZ(E +M0Z"@!*E1J&&84;%9`ANJ%LD?J6&GJ^$&?0``.J:`$*;2PS#BPS3ZDIEQB6,6 +MJ`:"H0(`.*:0$*:9H0Q895((D +MY>`(``PE!Z$&2V228*:/^$NE!B$'X4Y/X +M%XE!Z$'S_ASI0>A!T@U2L_X=Z4&80=/Y'IE!B$&"9LGX8U8_)Y(D\;E!B$'R +M)/"3^`J)0>A!\_X"Z4'80;/]`=E!J$'#^AJI09A!L_D6F4&(0A!L_X2Z4'80;/]$]E!J$&S^ANI09A!F<6(P_C3@(#4B4'H0?#PM//^ +M#NE!V$'9Q9C3J,.B8QBB8Q2B8Q"28QF28Q628Q'&J?X,&@P+#`S)8;)E):)E +M%0P:)47?\B4EXB45J&'`_Q'P[B#I4=A1@5D"HLH!)AT%HF$&IZC9#!NR0XR" +MH0(`.*:0$*:9DP`XIO`0IMB1Z*&"HD"H+#3`P?HLH!H*!T +MY2;^PB,2PF0PLB,3LF0QD<,`Z./B9"S8\])D+0N9\B:#%E\L%EDL@B=4B`B2 +MR?]F*.N8!ZA#%IDT@B3EX`@`#!SH,PP+L/H!<.X1\.X@#+_P[B``/J:HL=A# +MVJJI0]+2)H(M,)(M,9E!^$&#_PCY0>A!^".S_A#I0>A!()F@\_X1Z4'H09+9 +M))@IH_X2Z4&(0?A3D_@7B4'H0?/^'.E!Z$'2#5*S_AWI09A!T_D>F4&(08)F +MR?AC5C\)DB3QN4&(0?(D\)/X"HE!Z$'S_@+I0=A!L_T!V4&H0A!\/"T\_X.Z4'80=G%F/.HXZ)C&J)C%J)C$I)C&Y)C%Y)C$\8U +M_JT"O0/"H`*EI?P,"PP(V7/")XG)@ZFCN9.M`KT##`REH?ZR)/#")/') +M0:A!F",,"+/Z"*E!^$&BTB;B*C&#_Q#Y0?A!B$,@[J"3_Q'Y0?A!XMXDZ"Z# +M_Q+Y0=A!R%/C_1?90;A!P_LA.TJ`"TFX`Y2W\!GG^``P< +M#`@,'[(GA[ECHB>(O0.I)F8/YHXF3Y9;^D<,`"YG")H,6#!D6"1G2 +M)U38#0N99BWLZ`>H0Q;.'8(DY>`(`,@SL-H!<,P1T,P@#+W0S"``/*:HL;A# +MNJJI0Y+2)L(I,/(I,?E!N$'#^PBY08A!#`ZX(^/X$(E!B$$@_Z"S^!&)08A! +M\M\D^"^C^!*)0=A!R%/S_1?90;A!P_LA.TJ`"TFX`I1_\!DW_ +M`%8)MHAQB$CRH`+R:`!E'OP&U/X`F+&").6JJ:E#X`@`#!SH,[#:`0P+<*X1 +MT*H@#+W0JB``.J:H0\8[_@"B)/$JJOJJLDKH@@G4F7$+B!;($XBALB3LPB3N +MTB3OXB3MJ''R)"R2)"VB"L69`8DAB)&)$8(DY^`(`,CCPF0PN/.R9#'&(O^8 +ML8(DY:JIJ4/@"``,'.@SL-H!#`MPKA'0JB`,O="J(``ZIJA#ABS_F+&").6J +MJ:E#X`@`#!SH,[#:`0P+<*X1T*H@#+W0JB``.J:H0X:N_@```%:)YX(BE`PO +M^0@E$?S&FO^M`KT##"PEMOP,"PP<1H+_``#(<0P/##NB#,2R3-2R)"K")"L; +MJJ"@=.7E_0;>_0``V)'HH0P/R'$,.QNJH*!TLDS4LB0LPB0MY>/]!N+]`)BQ +M@B3EJJFI0^`(`,@SL+H!<*P1L*H@#+NPJB``.J:H0\:'_ZT"O0,,+.6N_`P+ +M#!Q&:OT``*AQ##L,#[)*U*(*Q+(D*L(D*QNJH*!TI=[]!FO^````V)'HH:AQ +M##L,#[)*U*(*Q;(D+,(D+1NJH*!T9=S]!K+_`#9!``P(##EQ90)BH=!2PA@R +M!?"B)3IJ8D(63GIR&ZH;,S`P=#)%\&:#`H)%\*)E.J<4-+(GB\(GBJ(63QN[ +MLF>+MYH'@F>+&\S"9XK@$*;2!G\,(LQ=\B6.^`^`0IAWP`&)B@!LS,F)_\!"F'?"` +M$*9B91(=\&)%4,;M_P`V00`,"0P]H3@"5,M)(3I)F.I)G\9)G\*)G*J)G*Z)G+J)G+Z)G +M,J)G,Z)G-J)G-Z)G.J)G.\@##"(6C`X=\.(G\?(F/<(C%+#N$>KBPF[J4B?Q +M0B,5L%416E)"9>OB)_'"(QBP[A'JXL)N[E(G\4(C&;!5$5I20F7OXA@WPB?Q +M\+O`"^X;S,)G\>>\7/(C$/)G*N(C$>)G*U(C&%)G+D(C&4)G+_(8-[!<$5HB +M]RL"ABL`HF\(FCL(L +M`!;\#N(&".#G0>`M@QWP`.><0.(C$.)G*E(C$5)G*T(C&$)G+O(C&?)G+Z)G +M.Z)G.N(8-^N`H8P`*)G,J)G,Z)G-J)G-X;G__`0I@PB'?``HFB9SJB9SL&V?\` +M`$(B\D)G.O(B\_)G.^(8-[>N2>(BZN)G,L(BZ\)G,[(B[K)G-J(B[Z)G-T;- +M_U(B\E)G.D(B\T)G._(8-[>O6.(BZN)G,L(BZ\)G,[(B[K)G-J(B[Z)G-P;" +M_P``HF0F@V\B5?\F@W!AW_PBC:`#RFL!"F"[NPKH,M"AWP +M```B:#)")5M":#/R)5[R:#;2)5_2:#<&$O\B:#+")5O":#.R)5ZR:#:2)5^2 +M:#<&#/\V00`,":$X`K%.`'*@N'IR0@=0868"@M("0L0!0$!T0D=09H0"DD=0 +MDFM5PB<2:F*R%]H;S,)G$L>;,`P]TDA.DF<2J>:I]J)F$J)F$Z)F%J)F%Z)F +M&J)F&\(FUZ)F'J)F'U9L">`0IAWP``!R)M52)NNP=Q%ZJ"9A9"+>M" +M9AXR9AKR+>_R9AOB+?+B9A[2+?/29A_B)M<6/@@=\```AY=`HF8>HF8? +M\B;P0B;O@B;HDB;GF>:)]D)F$O)F$Q:3!["G$:JBTBKJTF86PBKKPF87LBKN +MLF8:HBKOHF8;QNS_``"B9A.B9A*I]H(FU*GFDF;5&XB"9M1"(NI"9A;R(NOR +M9A?B(N[B9AK2(N_29AO"(O+"9AZR(O.R9A]&W?^0$*8=\*)F%J)F%Z)F&J)F +M&Z)F'J)F'\;6_P``HF86HF87HF8:HF8;AM+_`#9!``P)##VA.`)RTB."T@)B +MPABR)CI"!O!2NQM$0$!T0D;P9H0"DD;PLF8ZMY4RR`,,(A;L$=)(3I)F +M.I)G\9)G\*)G*J)G*Z)G+J)G+Z)G,J)G,Z)G-J)G-Z)G.J)G.QWPXB?Q\B8] +MPB,4L.X1ZN+";NI2)_%"(Q6P51%:4D)EZ^(G\<(C&+#N$>KBPF[N4B?Q0B,9 +ML%416E)"9>_B&#?")_'PN\`+[AO,PF?QY[Q<\B,0\F29SJ29SL&Y?\`\!"F#"+22$Z29CJ29_&29_"B9RJB9RNB9RZB9R^B9S*B +M9S.B9S:B9S>B9SJB9SL=\*)G+Z)G+E(G\*)G*Z)G*AM50A@W4F?PDF?Q1ZM" +MHFN2>(BZN)G +M,L(BZ\)G,[(B[K)G-J(B[Z)G-X;`_U(B\E)G.D(B\T)G._(8-[>O+.(BZN)G +M,L(BZ\)G,[(B[K)G-J(B[Z)G-T:U_P``HFJ&6I`9@!=YGLTB45PB4EP-T1T,P@R0&X`68; +MV>@A(DZ,L6@"86<"@B,Z6!%"H=`;B$I%:E7R%$X,-H)C.H#_P!8_#(%.``RI +M(FA?\@/PS0*3_`7S_`G":&#B*&'@WK1@[B/B96A`W2/296GB*&(;S.#>M&#N +M(^)E:D#=(])E:\)H8*(H8:">M&"J(Z)E;*%<`$"9(Y)E;8(H8J"H$(#-!8#< +M!8#K!8#^M&"((X)E;D#_(Y(EE_)E;X(43^)D4])D5,)D5:)D4AN9DF67EY@3 +MJ!'")98B99>ZJAO,+`O"99;E_`>X(;(K`"8;`L`0IM($?\Q=XB..Z`ZL[O(# +M"`PB\/=!\":#'?"H$6)$?B)C.B)EER)EEKJJ#"(L"R7Y![@AN`LF&P[`$*8= +M\-(#"-#708PM'?`=\/(E@``_IN`0I@ONX":#'?`V00!!.`+A:`*BT@&2(D#" +MH<[*PH(<3QN9DF)`D(C`D6D",+!T#`V:,A;(',%.``RHTFQ?\@H(G0V#^07S +M^0F2;&""+&&`_K1@B"."8Z5`_R/R8Z:"+&(;F8#^M&"((X)CIT#_(_)CJ))L +M8((L88#^M&"((X)CJ8%<`$#_(_)CJL(L8H",$,">M&#,(\)CJT"9(Y)CK/(C +MU()BQL(CYK#_$?KRPF_JDB/4@B/GL)D1FI*":>OR(]3"(^ZP_Q'Z\L)O[I(C +MU((C[["9$9J2@FGO\B/4PB/HL/\1^O+";^R2(]2"(^FPF1&:DH)I[?(CU,(C +M\+#_$?KRPF_PDB/4@B/QL)D1FI*":?'R(]22(^?"&K<;KZ)CU,>Z;YGCPB/F +MTB/IXB/H\B/O@B/N@F,1\F,2Z?/28Q#)TY(C\9)C%,(C\,)C$Q:;$[#:$=#2 +M@.(MZN)C%<(MZ\)C%K(M\K)C'9(M\Y)C'H(M[()C%_(M[?)C&.(M].)C'](M +M]=)C(.(CU@ON%GX*\!"F'?`+C("*P%;H"<(CYI(CY](CZ>(CZ((C[O(C[_)C +M$H)C$>GSTF,0F>/)TY(C\<(C\,)C$Y)C%!:K^[#:$=K2XBWJXF,5PBWKPF,6 +MLBWRLF,=DBWSDF,>@BWL@F,7\BWM\F,8XBWTXF,?TBWUTF,@AN#_ZJ(L"PP_ +M\DR`TF)`)=8'0F,50F,60F,=0F,>0F,70F,8@B/60F,?0F,@)A@%D!"F'?`` +M'?```-)CU,(CT^JBLJ`@PLP!PF/39=('TB/4L-T1VM+"+>K"8Q6R+>NR8Q:B +M+?*B8QV2+?.28QZ"+>R"8Q?R+>WR8QCB+?3B8Q_2+?728R`&O_\`0F,50F,6 +M0F,=0F,>0F,70F,80F,?0F,@!KC_`#:!`*'#`+*@J)%/``PC@M(KB*B)`7:` +M$<(I0PNJC/P6^CS2(I38#28M",;Y_P```!;J.[I2V`+"$@SQ3@""`B%"$@UB +MHB1J8N(&VP!$$8"!!-"($8#N(.)OC>B]V*U`S"``[A'@W2#9B<)OQK(&W<(" +M(+"[$<#%!-#,$<"[(+)I.*(B.((B-\"J`<"($:"(((EI0@(D@@;;X@;8,$01 +M@(@1D.X1@.X@0.X@XF_'P@;C#!M"TB/V?`N!:@*`C*"("*`(``"A1P*B9.5, +M6`P'>>F)V?(&&-@!`/\C(&(`#N(W#N4R#NL.(N3MKN +MZ?G"!6(@S+#"+$ZA*@+:S,GYP@;;JJ(+S!;,%1PN(&*]PN\B5J\B\`S*^2!BHF +M.04F&0)F*:VV2@*&+`"I$7(DV[(E%H(B0#)A!7![P(>W&G`W(((DXJT",+,@ +MX`@`DB)`&S.7,^RR)18X4;)DV@])E&1:,$X@) +M@LC^%J@3J+8,"9D*DD8KL@8R#!S"1BD6ZQ+R)6_A3@`;__)E;_%&`.(N@0R= +M\.X0X.M!X#V3+0,=\"T*'?#!2`+Q;P+A<`+1<0*R:16"!N#2:BSB:BWR:BZ" +MR!&`@'2"2GR`B!'`B""":B7!<@+":BN!.`*"9#*"9#."9#:"9#>"9#J"9#M& +MG_^!(0&`C""";?#XU0R)]SD"!J3_#$F0GB"2;=U&H?^((8(H+JT"O0?@"``, +M"M(D\G"WH/@APB3Q#`[B3WX@S*#"W"30NZ"R&P&Y+)(B0*)A!++'`9>[47)A +M`Z(D\<(D\)(6)1NJHF3QIYD*&\S2H`#29/'"9/"")..M`N`(`/(D\K@QXB3Q +MR$&PVZ`@[J`;S,E!XMXD\-V@>KS2'0O9+I(B0+DQ&[N7.["R)19&J_^("588 +M["@1'?``,D8J#`F&L/_A3@#R)6\,"9)&,AO_\F5O\48`XBZ!#)WP[A#@ZT'@ +M/9,M`QWPH5("HF3EQD?_P5,"PF3EAD7_T50"TF3E1D/_X54"XF3E!D'_\58" +M\F3EQC[_@5<"@F3EACS_H5@"HF3E1CK_#++!3@!\^PP=TD8KLFR!'?``NE+B +M)6HY#J6\^I%/`(8,_P``-H$`BW/+4Z*ASTN3ZT-K@ZMC:4&)(4EAF1&JHEE1 +M>3%2H/!RHS^I`9%S`D%E`@P(8LF@0$*`@F1V`#>FH!"FP78"#(NGNS6X`9@& +M#!JB2X"0D'0,"F99,_(#`0QX4/\0@/\@\D,!X70"`#ZFP!"FT74"#!G0S,`6 +MG!2&3P!@FJ"2*0#@JA&0D'0F6`,`*)D=Q9Z#0P:N!&E,@`,&K@A)3(`#!JX,:4Q``P:N$%E,0`, +M"KA1Y3``#`JX864P`(8J``"R$P&B)':ZJJ"@]*)3`>`,`*)D=Q8J"0P:N!%E +M+@`,&K@AY2T`#!JX,64M``P:N$'E+`"BH`"X464L`**@`+(A!N4K`(88```` +MLB2,H*J@/?"PJJ"BVO^B&G?@#`"B9'<6R@(,&K@1I2D`#!JX(24I``P:N#&E +M*``,&KA!)2@`HJ``N%&E)P"BH`"R(08E)P#2(D#B)(S")';0W:#@W:#2W?_2 +M'7?:S,)3`0P)"^D6?N<=\`"B(D"F +M8!"F0J4_#!ARH-]2`P%@8`2P9A%P51!@52!20P$`-*90$*:Q=P+A=P+VM0ZB +M`P"PM:"X"[JJHD,`'?`,#?+2`H)/3^#=H-@-P@,`VLS"0P`=\``V00`,*W%X +M`D%.`**@C'IBDB;7@B;",%.@D%6@%A@4,I4"#'B`,Q&`,R``,Z;P$*;BE0,, +MCX#N$?#N(``^IM`0IL*5!`RM@,P1T,P@`#RFD!"F@I4%#,F`B!&0B"``.*8P +M$*;RE08,TX#_$3#_(``_IN`0IM*5!PSN@-T1X-T@`#VFP!"FJC*1PP!V@!&" +M)(,+F8SH%LD0HB-QJ`HF*@?&^?\``!;)#[(%`,%Y`G"[$<"[(``[I@P:@B;5 +MX@4!L2H"PB;6X)4$NK+2"W[@Y@2#_`CR*SR"%0&C_!`,!2#_H'K_\B\GD_P1 +M@_P2\_P7X_P<4_P=##[3_![=!<)DR9(K//(K.\%/`)/]"O/]`E/]`:/]&N/] +M%J/]%*/]%5/]$E/]$]G,TBL[LBL\T_L(LFQ,HF1?D@-\C053^`63^`F"9&!2 +M9&%29&)29%929%7R`WP;__#P=/)#?":/,,`0IAWP`)'#`*HR"YG2)(.L7:QI +MXB-QZ`X+F68N[O(%`(*A"W#_$8#_(``_IH;*_P!20WR`$*8=\%89_I(C<;)I +M`"5\^D;U_P"B(W&Y"F5[^L:]_P`V00"\4B%Z`@`RIB`0IKQB#":`4A%@52`` +M-:9@$*8,%PM"1]8/`!)``(>A@(;`&XB"4P`=\&)3`!WP``"1>P(`.:8@$*96 +M;]`N`(`)(#`:A1N$&R4P*R4P2R4P:R4PBB4P.B4P6B4P>B4PF0D#1&V?\, +M#,)3`L)3!,)3!L)3",)3`\)3!<)3!\)3"1WP``"V)P)&R?\`.Z:@$*:2`P&@ +MH`20JA&0D&2@F2"0D'220P&0UT$63?#`N1"0T#1;W=#0--"[(+)#`0:\_P`` +MLB4RPB4ZHB4JI1?]LB4SPB4[H"H@HB4KI1;]DL$8XB7ITB7KPB7JLB7H\L$< +MC0JB!-KY(9D1B0&")>;]`N`(`+(E-L(E.IAQJ&&B4P*24P.E'/VR)3;]`N`(`)AQJ&&B4PB24PD=\```-D$`#`9"TB-2)/)B9-PP8Z!05J"2!0$, +M'N!F$4?I>,%S`J*E/_*AS]*B`I"0-(+)_1;X";+)_!:;"0`ZII`0IO""@+:Y +M!9*@`.)(@.%_`K(%`*(%`>#IH.@.H*`TZKNR10!F&@X`/:;P$*;`_Z#R+P#R +M9-RR).46XQB")-DWF"O"%0&B)-S*JJ"@]*)5`>`+`*)DW1WP``#2)-GBU?\W +MG0(=\`#B'G?B50$=\*(D\CWP:JJBVO^B&G?@"P#")/*R)-QJS,+<_\(<=Z)D +MW6K_PP*Z[5EJ__")/*R)-QJS,+<_\(<=\J[LE4! +M'?#"%0&B)-P]\,JJH*#THE4!X`L`HF3='?#"%0&B)-SP(`#*JJ"@]*)5`>`+ +M`*)DW19:Y@P:2[6EIO\,&K+%!B6F_PP:LL4(I:7_#!JRQ0HEI?\,"LNUI:3_ +MZ[4,"B6D_QWP```V@0"A>`(,'`P+JG)B)]=!3@`P4Z!@5:"2!0'BH(QA3P!' +MZ3,,+9"0-(+)_1:8-O+)_!8_-NHRD<,`=H`5@B2#"YD6N`D6N0GB(W'H#N+. +M_A8."L;X_P``@M(FTB@PHB@QZI+R%0'3^@C2"%+#^AWS^A+3^AZB9,FB*#'] +M#((H,*/_"JT+@_\"L_\!P_\:L_\6P_\4P_\5L_\2L_\3^<:YQL)D7]()?(%< +M`+/Z!=/Z":)D8+)D8;)D8K)D5H)D5?()?!O_\/!T\DE\\L_X%C\^\B?5XB?6 +M\_X(XF9,'?````!6^0""(W'2:``E$/H,"Z%X`@PICT"P_D0DF3)D@4!D)`TC!EF&5J8`?*5`XT+ +MHBDQ\/"TDBDPH_@*HI4"D_@"L_@!P_@:L_@6H*#4\_H.P_@4P_@5L_@2L_@3 +MB<:IQL)D7^(#?-T+L_T%X_T)TF1@HF1AHF1BHF16HF15D@4!D)`THLG^5HH: +M64'2E0C"E0:RE01Y,4DAHI4".1$,#HT._0XX`:E10I4#H*#4/Z +M$O/Z$ZG&6<9"(S%P4+2B(S!X,4/X"J/X`M!`U*%:`E/T#N/X`5A!X_@:X_@6 +MJB+C^!2H4>/X%?/X$O/X$XG&2<9"(S$R(S""(G]#^0HS^0)((3@1X_D!\_D: +M\_D8X_D6\_D4\_D5X_D2X_D3F<;@"`"RE07"E0?2E0F"(G\M"J*5`^`(``P< +MTI4#((#4H)"T#`NBE0*PZR"3^`ZS_@62E06"9@S0T+2"E03"9%^@H-33^@[R +M`WS2E0F0D+2`@-23^`[S_@G0T+3RE0?B9&"B9&'BE0:BE0CP\+2"9&+@X-3S +M_@Z@H-33^@[B9%:B9%62!0&0D#2BR?L6ZA7"`WP;S,#`=,)#?":,$/`0IN(G +MU=(GUN/]"-)F3!WPLD-\H!"FDB?5@B?6D_@(@F9,'?#R)\(6GQKRE0(,0)P_Q&`_R`` +M/Z;R%0&2)];2)]6"!0$@Z:#3^0B@[H"`U03B+B>`@#33^1'S^1+2TB;2#5+C +M^1>S^1W3^1ZF.`+#^1`,/9)DR8(GUN(GU:T+@_H*X_H"L_H!P_H:T_H6P_H4 +MP_H5L_H2L_H3J<;"9%^"`WS]"[/_!8/_"?)D8+)D8;)D8K)D5K)D5<:L_P`` +MHB?6PB?5LDE\P_H(HF9,'?"R50*R502R50:R50BR50.R506R50>H`;)5"8T+ +MDBHPHBHQL-#4L_T.H_@*D_@"L_@!P_@:L_@6P_@4P_@5L_@2L_@3P_@9B<;9 +MQL)D7_(#?.T+L_X%\_X)XF1@TF1ATF1BTF16TF15AHW_`%;)[K(C<=)K`"7+ +M^0P+H7@"#!Q&MO\```"1PP#J,@N9XB2#G-Z`(,'(;U_P``-D$`##D,"H&` +M`K%E`F(B0'*@N%*AT%I2>G(R%]H;9F)G$K`B@&>3-T&!`I)%?L(B58<)(?N)F\>)F\))F*I)F*Y)F +M+I)F+Y)F,I)F,Y)F-I)F-Y)F.I)F.P`ZIM`0IC&"`K#=P!;]$``SIN`0IF8> +M$2&#`@`RIH`0I@`SID`0IB84\``ZII`0I@SBL)G`%FD.'?``PB)#\B;QXI,& +M4J'0L/\1^O):_^)O=D(F\?*3![!$$4I"6D3R9'?B)O'RDPBP[A'JXEKN\FYZ +M0B;QXI,)L$012D):1.)D>T(83U(F\<#-P`M$&]729O%'O4]2DP5"DP3B9B_R +M9BY29BM"9BI"%[>P_1'Z(DW-RP%QZ,"ACL`DF8RDF8SDF8VDF8WANG_HB;9F`>@ +MF<"29ML`/Z:`$*8,(AWPXB;PDF8ODF8NDF8KDF8J#`_R9O'2%[<;[N)F\->L +M;Y)F,I)F,Y)F-I)F-Y)F.I)F.T;7_^(B\N)F.E(B\U)F.T(7M\>D==(BZM)F +M,L(BZ\)F,Y(B[I)F-D(B[T)F-P;,_P``XB=4Z`Y6_O(`.J;P$*:WGP+&(``, +M`AWP0B;90$W`0F;;`#^F(!"F##(=\`#B(O+B9CK2(O/29CM2%[?'I4G"(NK" +M9C*2(NN29C-"(NY"9C;R(N_R9C=&M?^29C*29C.29C:29C>&L?^P[1'JXD(N +MZD)F,C(NZS)F,_(N[O)F-N(N[^)F-X:I_Y)F,I)F,Y)F-I)F-\:E_P"")ME( +M!X!$P$)FVR$P`@`RIO`0I@PR'?`V00`,:`P:<80"T84"#"P,.W:`*8"8$;#I +M(``^IH`0IG"(H(@(P.D@@&#UG"8`/J9@$*:`\/1P?Z"`@/6L2L;S_]#XH'(? +M`8!W$"R$"" +M9``H!2F8!"F8@\`\@\!`&8C:00` +M_R/R90"(!)*O@)>8';*F`@`[IL`0IL+,X,)D```[IJ`0IJ+*X*)E`((D`"IH +M:00H!3HB*06(!#*OP(B7B(I3H#B8N$\;S_Z@" +MLJ`$PJ",P*J`I2C(&#?(6`](+U^#A!-#N$>#=(-)EC=%/``#_$?#,(,F- +MPF7&H@O9P@8,,8@"L*H1P,4$T,P1P*H@HFTX\B8SXB8R.C+`_P'`[A'P[B#I +M;<(&$.(+UZ(+U##,$8#N$9"J$>"J(,"J(*)EQW)C0')#?))#>GGMB=WR"Q0` +M_R-P_U,@_[#R+TY*__G]X@L<`.XC<.Y3(.ZPXBY.2N[I_<(&]B#,L,(L3DK, +MR?VR"]<+NQ:[%O&*`H&)`H)C*/)C+I@!D@GJ%OD2H8L"HF,@V`'R'6VQ-P"0 +MCP'@_P&*__)C0:(K\,(KW7R^X.P0XFO=T@W7TLW^%HT0@1\!@(H0@FOPJ`%B +MT@&2%K8,!')*)JR)<1D">G*"(RBM`G"W(.`(`*T"L@-\@B,N#`F20WK@"`"B +M%K8;1*2(I06&@W8"=+-_A8=#:%/`(@!#`FB*DS"&"/HJ*"X=,"[@J"@ +M=+JJ"_J@^8/YZ'D.R`$,';(,+M),)7),)Q:+"O(BF1O_\F*9XB6!HB,Z@B,[ +MD=H`@*H1H(@@D(@@D4\`K0*":4QR8D`E.@:X`0P"HDLF4L^:%.``;;_P``-D$`'$D<#0R,/,L\#QQ^#,J@@Q`GY"BG +M`STF2&_"R/@67`MFJ"N0,A",T]+#_!;M(>+#\!9>)Y>3%[`E,!WP`*<#,"9( +M;F+(^!:&"]+(]!:M#0P"'?```)`R$)<"*F+#_!9V#H+#\!98$)?"(K`E,!WP +M``"0,A"7`@YBP_P6Q@V"P_`6V`^7PBXM!1WPD#(0%A,0HL/\%OH1PL/P%@P6 +MEY,Y4-4$%DTN4.,$%AXJ+((@)3`=\)`R$!;##F+#_!8F$8+#\!;(%)>3/5?E +MNU"C!!9**-`E,!WP`)`R$)P#PL/\%@P4TL/P%IT8D./`5H[S4/4$5M\A4&,$ +M%G8=+,(@)3`=\```D#(0%A,.8L/\%F82@L/P%E@7EY,54)4$5DDJ4*,$%KHF +M+,(@)3`=\)`R$!93]6+#_!9V$H+#\!8(&)"CP%;*\+`E,!WP``!0M016:_-0 +MU`06_13`)3`=\%#E!%9>\E#T!!;_#<`E,!WP``!09016)AI0A`06^!4\0B`E +M,!WP`%!E!!;F[U"$!!8(%:`E,!WP4*4$5MKN4+,$%BL5T"4P'?```%#%!%:L +M[5#C!!9.%-`E,!WP4/4$5I_L]H4"!D@`5[X"AD8`'((@)3`=\```4&4$5N;J +M]H4"!D,`5[X"AD$`'((@)3`=\%!E!!96Z5"#!!8X%)`E,!WP``!0E006F0]0 +MHP06RA4L@B`E,!WP`%!E!!86#U"$!!9(%:`E,!WP4)4$%DD/4*0$%GH6\"4P +M'?```%"U!!;K#E##!!8\%I`E,!WPH"4P'?```%#5!!8=#E#D!!8.$/`E,!WP +M4/4$5A_B4&0$%F8/P"4P'?```%"%!%:8$_:%`D8P`%>^`L8N`"Q"("4P'?!0 +M90165A+VA0+&*P!7O@)&*@`L0B`E,!WP``"@)3`=\%=E`@8A`%!D!!9V##Q" +M("4P'?!09016E@]0A`06N`L\0B`E,!WP`"R"("4P'?``\"4P'?#`)3`=\!S" +M("4P'?`G+,N`QKD9$"#"4,-(89 +M``#9T]G#B$.2)SRB)SN:B)('?J/X"-/X$./X'9/X'H)FR?(G/+(G.ZT.\_H* +ML_H"X_H:T_H6X_H4X_H5T_H2T_H3J*2*>.C^0B2;$P=\`#($0P$21.B +M+!RR+"3"+"@EF?NI`:@1LBHEPBHIHBH=)9C[PL,PTL,T@B`(`)'# +M``N9HB:#%@H)%@D)LB*4N`L+F68K[.@SC00,OW#N$?#N(``^I@P9TB<\R$/H +M(_(G.]K,T@=^\_P(0_P0X_P1T_P>PF;)LB<\HB<[T4\`L_@*H_@"D_@:0_@6 +MD_@4D_@50_@20_@3B(LXL(LX^/\",)M3!WP +M\B*460^ES?C!3P`,#0P>QK/_`%:)]X(BE%)H`"7,^`;;_P`V80`,:Y&1`@PE +M##3&`P```#RFL!"FH+#UH(#TD)B@@+L10,L@`#RFH!"FD*J@J`I0RR"@T/56 +M;?V@ND&`NQ%0NR``.Z:0$*8,'&%.`($J`G$[`@P+H)"4F3.Y`Z%/`'IRBH*) +M$?;9`D8E`.*A`DD3\LG`^3,`/J;0$*:1PP#28P)V@!&")H,+F8SX%ND2TB*4 +MV`TF+0C&^?\````6V1&8,XT+TJ$+<)D1T)D@`#FFF!'80[G#N=/R*3SB*3OZ +MW>/]"/@CX@E^P_T0\_T1X_T>TF;)TBD\DBD[T_@*D_@"P_@:0_@6P_@4P_@5 +ML_@2L_@3B_NR)R6B80#" +M)RFB)QUE>ON"(0'"PS#2PS2"*"&]"J(A`.`(`)'#``N9HB:#%@H)%@D)LB*4 +MN`L+F68K[.@SG00,OW#N$?#N(``^IK@1#!K(0](K/.@C\BL[VLS2"W[S_`A# +M_!#C_!'3_!["9LG"*SRR*SO13P##^0JS^0*C^1I#^1:C^12C^15#^1)#^1.9 +MS8C3^,.`@+3P\-2#_P[YS>(GXL(GX^/\",)M3!WP\B*460^EK_BA3P`,"PP< +MQK/_`%:)]X(BE%)H`"6N^`;;_P`V00#`$*8,#J*AT+*@N+JRDBL2JJ*"&]H; +MF9)K$I<8-3$J`M%=`CHRVL*R+->2(SR"(T$@NZ`@F:#:F=J[LBLHLFR^@FDH +M\B,\TAI/&__R8SSW'0P=\`PX@DI^XFL2'?``XF,\XF-!HB,[XF,CLB,B&ZJB +M8SNE@@4,'`P+H+R#LD-^'?`V00!!D@)*0H(DWPEQB`)Z[,H(DZI(DZ9)D%()D%1;S"Z(D +MV["J$:JB:JK2*G;29!S"*G?"9!VR*GZR9""B*G^B9"$=\+(DV[>:1E)D(%)D +M(<(DZM(DZ=)D%,)D%18#";#K$>KB:N[R+G;R9!SB+G?B9!T=\`P_\DI^PF)` +M4F044F054F0<4F0=4F0@4F0A'?``LB(D +MVU)D%%)D%;#N$>KB:NZ"+G:"9!SR+G?R9!WB+G[B9"#2)SL,'+#=$=K2:MW2 +M+7_29"'"1WP=\%)D'%)D'5)D(%)D(1WP``!29!Q29!T=\`P?,D=\,F04,F05 +M4F0<4F0=4F0@4F0A\D=Z'?`V00`R(D^RI%0]\*T#I5X%@J(@D90"H9,"HF)[ +MDF)ZBH-\\B)H?R)H@`P"'?`````VP0!Q3@!1.0"")X)R)YIB)4ZXK,PF3C +MDB,C@B(8L4X`X5,`FHB"9.3"+H2HH7P?\,P0PFZ$D`"FC)EV@`20`*:,&0;] +M_^`0IO(B?T$W``P,X/_`%E\/D3\`X8,`D)<@X.H@Z;&9P88%`.`0IO(B?QO, +MX/_`%D\-@J`3Q[@"QC(`\B3G^0'H`>#@%.D!F`%F.>[B)*R2)2#@\`0';A)\ +MZ(".$()DK,`@`((DK(D1P"``#"B`F2"292#`(`"2)2#`(`!V@!"").>)(8@A +M@(`4B2&((28X`P;Z_P!\V(")$()E(,`@`((E((DQP"``C&_B9*R2)*R9$>BQ +M^,'B:X*2*X*B:X*"*X+R:YI,#^(KFG)KFI(KFH(B&H)E2O)E3-)E3N``IHRN +M=H`$\`"FC"\&_?\`=H`3XB3GZ4&809"0%)E!B$&"R/T6"/)&^?\`XF8LH!"F +MHF8MD!"FDF8N@!"F@F8O/?`]\/``IHR?=H`$L`"FC!L&_?^`$*:"9C#P$*;R +M9C'@$*;B9C+0$*;29C,]\#WPP`"FC)QV@`20`*:,&0;]_^`0IN)F--`0IM)F +M-<`0IL)F-K`0IO`@`/`@`*``IHR:=H`$\`"FC!\&_?_`(`#`$*:P$*:@$*:0 +M$*8J")"F2P2"0B*"("()J$)(D*/"9H)@)DFH1@B0HP(B@B`B":A.")"B2 +MP1"0B*"("()J$H(D*)+!()"(H(@(@FH4@A1-PA1=DB(;\84`&\R0D'3PF2#` +MS`'`B"#R%$K"%%R29::10`")BAO,P,P1PFH) +ML<(J&,FQDBH9F;&13P""*AJ)L<(J&X(D'LFQHBE&J;&A.0"2*429L8)J2L($ +MPPQ)#%@+S,")@P`80`#_H?)A`/&C`,(A`,)J3/)J3I``IHR9=H`$H`"FC!H& +M_?^`$*:"81O"(H`,"X#,P!9\$($_`)&#`*$Y`("-()">())A&()A&88&`(`0 +MIH)A&\(B@+++`8#,P!;<#9*@$[>Y`@8U`)(EYYG!B,&`@!2)P&(X8"`%(GAB.$F.`:A.0!&^?\`?-B`C!"":B#`(`""*B#`(`", +M0Y)EK,(EK,(A&,)F@L(A&9(F@N)F@H(F@L)FFI(FFM)FFH(FFL(D'L)J2I@! +MDFI,\FI.@`"FC-AV@`2``*:,6*$Y`$;\_P!V@!.")>>)\6#.(D3`PO/?#P[B#B9$S2`@OB`@WR`@^B`@["`@RR`@KZ +MJNK,VKO*N[JJ#`RR$@2B9$V8$L)D2<)D2["&5)"8=9)A'!8X';"&!+#+!+": +M!+#9!,J9L,@$VLRPUP3:B,J(FHB'N@D,C0P>XF1)TF6P\B$<#!CP^)/R9$J< +MGZ(2`9(D2Z"@!*"9(%:Y`+*@!,*@`<)D2;)EL-(D3N(D1^)D2(Q-\B1+5F\` +MK0>]`B4_`H(A'!N(@F$=O(@RH`",B%$IBQ@%B81=BQ@%GN@*&>O^")%!62-Z2(18@FN@ +M`*:,FG:`!+``IHP;!OW_PB$5%IP3#`WB(1OB9#?2816@$*:B9#B0$*:29#F` +M$*:"9#H]\/`@`/``IHR?=H`$L`"FC!L&_?^`$*:"9#OP$*;R9#S@$*;B9#W0 +M$*;29#X]\#WPP`"FC)QV@`20`*:,&0;]_^`0IN)D/]`0IM)D0,`0IL)D0;`0 +MIK)D0CWP/?"@`*86ZN!V@`7P`*867^#&_/\``(($PPN(%GB3H3D`#!(,&9)J +M+AWPL@3!L+`4)CL*P@3"/?!F'`(&(P"B)"@,&)(46("J,*)D*'"JH))JZO(D +M*(#_,/)D*<9`_A8*"`PK#!S"9$FR9;`&D?\KF3"9$."9$9"<@@9H_@``@@3$ +M5MBC@@3!@F$3@(`4@LC]%NBBD@3"%LD(DA1:@A18EYA'@B$3DB0H"X@6R`KP +MB:"("(G:@B0HP(B@B`B)^@:9_@"0$*:29#?&L?^R)"BB%%APNZ"R*^JPJL`6 +M2H@,`AWP``PK")"CRP2#PS*#(#,)J$`9L_@"")"GPB*"("(G:DB0IP)F@F`F9^I(D*(+! +M$("9H)@)F>J2)"B"P2"`F:"8"9)J$$9?_@``-D$`(J2`'?`V80`,&B6XSY%3 +M``P=P"``#.ORH=CZ\H(OF])OFN(O9!N(@F^;V0["*83)`;D1J!&(`:"((()I +MA,`@`!WP````-F$`LJ144B)/'?[9F!D07+'#W!T0663 +M!)&8`@S[V`%\_T*@X(*AO,*BN,K%BH)*0D)EV8)EQQP$#`*-!2)L9@R"\D4` +M\F5$\F4X:75Y9>(-^.)%)-(-^=)%);),D+),D;),DK),D[),E+),E7:B2T)( +M=*()`$)(=;()`4)(=L()`D)(=]()`T)(>.()!$)(>?()!4)(>B()!D)(>S() +M!XN(HD@LLD@MPD@NTD@OXD@P\D@Q(D@R,D@SBYD,#&"W@L)E4["[D)"[$;)E +M5KK;TF59@9D"^`'AF@*2H4RRH62BH5BJI;JUFI69%;DUJ24,:Z(EV>)O:X)O +M:N5EV@P"'?`````V00"X)A@6\LC^%O\?DLC]%GD@9D@*#$NR9"V&``#B9"WR#,3R +M1+BR#,6R1+F2#,:21+J"#,>"1+OR#,CR1,""#,D,#R88#I+(_A9Y'&8X"*)D +M+X8``/)D+X(,RH)$P;(,R[)$PJ(,S*)$PY(,S9)$Q((,SH)$Q;(,S[)$QJ(# +M[`Q)C0,6N@9`I"!VJ662"/""R!"22C2BRA"2".&22B62".*22B:2"..22B>2 +M".222BB2".622BF2".:22BJ2".>22BN2".B22BR2".F22BV2".J22BZ2".N2 +M2B^2".R22C"2".V22C&2".Z22C*2".^22C.2`^VM!!:Y!I*@!':I99(,@,+, +M$))*=*+*$)(,<9)*99(,))*;)(,>9)*;9(,>I)*;I(,>Y)*;Y(,?))*<)(,?9)*<9(,?I)* +M["*(D+^)- +M:.)$N(($N28J7A98`,(-E(>\!>)-:.)$N?(D+68_S(($NA98`)(-D8>Y!>)- +M:.)$NJ(D+X($NR8J0198`+(-E8>[J.)-:.)$NQWP\D0!1MW_``"B9"U&@_\` +M`.)D+T:/_PP_\F0M1G__C&B2#9(]\(>YI>)-:.)$N0;G_XQXH@V3ASH"!MG_ +MXDUHXD2['?`V@0"M`I$Y`#(B3X*@_X)I@B4S!&*A;&IB8*8@I6G:HB)_99O: +MK0,BH``B8S\E*`!="MQJJHE)-K80;'Z +M`.*@K_(M'PP,PDV`R0\F)`+&W/\&B/\`O0-E'`3&H?^"HJ"*@X((@8S8HB,X +MO0,;JN4:!`PJ1@``#%JR(RV2H(2:DR8[#<@3N"/8,]DCPF,#LF,!XBGS&^[B +M:?.B8\\&O?\,M0P/^0P&<_\`@B/'>0@E_0/&FO\`#`(=\)@,5BGUHB-$HF,_ +MQN#_#`G&T_\`PBT?#+4,"[D,QF;_-D$`#"P,&E&=`@P)?/-"HJ1M`G(FV2(B +M+4I&(YTO@/Z#9Z__GRZ`[8%GKNZ?+8#0PE +M>MW9\K(F+_(&`4(F+;++_A8[">+$_19.#1;?#:)B%:)B%C(F-D(F-\@6"S,+ +M1$!$$<`S$4`S(#)B24(F+WR[/(,6%`<+E`P$D$6#T/01,/\0\F)*Z';89L#N +M`<#=$>#=(-)B1L@,03<`>LS"8D1R8D=R:,4B)/!R)-V!'P&PMQ"R9-VB)BV` +M@A`F.BZ"9/`,`AWPL9X"LF8^1J#_``#2IQ_29CZ&G?\F-$.B8A6B8A:28B7& +MVO\,%,;C_S$A`5%8`%)DIE(DIH%D`#`R(%!0=(!5(%)DIC)D\/(F-@R.#`+W +MOA\,2("'(()DW1WP`))B%:)B)9)B%L;)_Z)B%<)B%H;'_PP"'?`````V80!@ +MP'10L'1`T'3AGP(P\'0`/J:`$*;PG1$@W9!R#;@`6",,*`LW)A<0C-6`HQ&` +MJB``.J9P$*9R80!"I1\6W!(`-*9@$*9B8D\`=B-R8D^03Z`A>JH;JJ!P8%"GHU(D1P`30`!FH0"9H19+#U!1 +M(:I54F1'EZ4(`'^A<%6`4F1'9R4+`!-``(^A@%7`4F1'\)41DF1'`#ZF4!"F +MH@VY`%4C"SHF&A06%0&2H`*`@Q&0B"``.*9P$*9R80$6S`FBI1\`.J9@$*9B +M8E``QB/"8E`7I0<`?Z%Z55)D1V>E`D;'_P`30`"/ +MH8"%P()D1X;#_P`6!092)$BJ55)D2)>E"@`30`"?H9I54F1(9R4+`!-``*^A +MH%7`4F1(L@+25MOXPB(O9BR'#`+PU1'29$@=\```@B1(XB(O70CBSOY6+O.` +M42%29$A&RO\`#`+P]1'R9$@=\```@B1(77_PP"'?`V00"1H`)13P!RPFBQ3@`,-L(B0M(B0PP(30BM"-/Z +M`,/Z"&/Z'J)KR3(B+:(B0PP;,L/]H_0*8_04@_0:L_0L(-,$%BT)(B\-81T-"TT_P.#`TPWH/3_!S)Q=(B3\(B4&(G2*(G1T(B+WSK&^J@ZK.P +M[A`F)"WP-A$P,2$;\S#SL["_$`P?IAH"!BX`#`_&+````)"@!&"PM+/\#J/\ +M',G%1NW_8$$A&S0,%D`TL["S$`P3.0'F&@,,#_D!,@+!YA0!#`:\,]Q9^`%* +M1D!!(4I,"T2JKZ"A(:JM!A,```"@^I!`1)"H`4I&0$$A2DP;1/JJH*$AJJW& +M"P``W)F@^I!`1)"H`4I&0$$A2DP+1/JJH*$AJJU&!`#X`4I&0$$A2DP;1*JO +MH*$AJJV@,-0,'0P&\$010,"TD&V#P_,.8_,<.<7&#`"JKZ"A(:JM#!WF%@$, +M#6IM#!-@82%J;!OV"V9`;Y,,#T#S@_!&$4`PM*!@U#/V#O/V'&G%X,#4B<5` +M\2$;VJ#:LQMOL*"T0B(OT($A\&^S8&$A\(@1@(#4\&818&"T)B0QH_P."_0, +M&PP.\.N#X_PO\&!P!C +M^`Z0T`2C_`X,&PP$D$N#T_PT()@&(J2C(J24!`!&!@M#`PU&/S#D/S'#G+6"I0(`.*9P$*9P<'1R0YT6-TBB`ZR&"`"B`ZQF+-X' +MZ@(7:MBBH0(`.J:0$*:B`ZR20ZT]\$?JQW(#G;D!-^H)!VHLP@-;N0&L3*T" +MLJ``P@-HT@-LY3__T@-KLB$`9AT+XB,N\B,M\F,QXF,RH@.LN0&@@@16N!,7 +M:@T<^@`ZII`0II)#K@8!`#S\PD.NH@.L!VH0T@-;C*WRH0(`/Z;@$*:B`ZR" +M(Q.@D!2"R/X6Z!.<2<(#K@=J&M(#79T$4_D`G!U3^0T&`P#B`ZQ"0ZX,#`?N +MY`P)#+[#^0&`V1'@W2!`E"``/::B(RF"(RCR`ZRC^0"#^0@';P)3^1"A7`#R +M`UQS^1'!3@",'U/Y%O(#KN(#K=(#G//Y%^/Y'4/Y'M/Y'Y)LR8(C%7%/`.&@ +M`H+(_A8(#H++_1:829++_A:Y%`O+%FQ*TB,IP@.L\B,HG03`L`3S^0+3^0I3 +M^113^14';#D,.(/Y%E/Y'$/Y&A;[,J"Y(+G'TB,IPB,H#`+3]`##]`A"9TP= +M\-(C*<(C*`P"T_0`P_0(0F=,'?``#,_P_!#RS_067T+`@@06>`U3^1;&[/\` +MK0(,&\(#:0P-92G_D@-KN`%F&0NB(S#"(R_"8S.B8S2B`ZQ&I_\``+*A`@`[ +MIJ`0IJ"@=*)#K,9>_P``-^H*PB,5O07"S/[`MH,,G=<*`H:J_T)C,D)C,4)C +M+D)C+<:F__++_1;/48++_E:H,O(C*<(#K((C*)T$P+`$P-`$@_D"\_D*4_D4 +M4_D5%BT\##S#^193^1Q#^1H6JT2@^2#YQ^(C*=(C*`P"X_0`T_0(0F=,'?`` +M"XL6^#R2`UH,&J)#:X++_@P>#`V`WH/20VJ&6/]#^1;&MO\`D@+3YAD"AKS_ +MW0(,"\*@F,K"A@D`##BB`VN#^18,"`NJH*O`H(^#@_D:F<"`ZSR+4JB#(`GZ#B9QZ(M2/(M1_"J$1N/\(^SH*$A +M&_J@^K-\ZJ"($*#_$/#PM*(,?X"`U//X#J"@!*/X'(G'1L?_``"@H`2"+4GP +M_Q'P\+2`@-3S^`ZC^!R)QYG'\BU'HBU(?.@;G_"J$:"A(?"?LX"9$!OZD)#4 +MH/JSH@Q_@/\0\/"T\_D.H*`$H_D9QX(C%8"`!(/_'/G'1HC_^I)228`,,AWP`)G'H@.LX)D@H+,$%IL,XB,N +MTB,MX."TT-#4X_T.0_T_](C*<(C*`P"T_0`P_0(0F=,'?`` +M)BP'#`M&FOYF+/>2`UH,"\8]_Z#B!!;>">(C,-(C+^#@M-#0U./]#D/]'-G' +MF"(RDB(RB#]``C]`A"9TP,`AWPT@.LT-8$5FW" +M#"J2`UK&"/^9Q[(C+J(C+1N+&RJ@*K.PB[-\ZJ`B$*"($("`M"`@U(/R#D/R +M'"G'\B,IXB,H#`+S]`#C]`A"9TP=\``ZIK`0I@9)_@``0_D6QNK^F<>B`ZS@ +MF2"@PP06?`KR(R[B(RWP\+3@X-3S_@Y#_ASIQ](#K-#2!!9M$,(C,+(C+\#` +MM+"PU,/[#D/['+G'F<>"(RVB(RY\ZQOH&_J@^K.`Z+.P[A"P_Q#P\+3@X-3S +M_@Y#_ASIQ\(C+](C,!N,&ZW0K;/`C+.PB!"PJA"@H+2`@-2C^`Y#^!R)Q](C +M*<(C*`P"T_0`P_0(0F=,'?"M`F48_^(C*=(C*`P"X_0`T_0(0F=,'?`G:E;B +M(S#2(R_@X+30T-3C_0Y#_1S9QYG'LB,OPB,P?.H;^QN,P(RSL/NSH/\0H(@0 +M@("T\/#4@_\.0_\<^)QR(C*?(C*"/T``P"\_0(0F=,'?``FXD.@&\QVJ`RZJ2N9(*J@TFI'TFI(2[L,"0PH +M9BSDPJ4"`#RFL!"F0J*@LD.AL+!T%NL*HJ$"`#JFT!"FK#V1^@``.::`$*8` +M.J;P$*:<+W:`#0`YIL`0I@`ZIK`0IHP;QOK_\:$"`#^FT!"F%JT(#`K!HP+1 +MH@(,_P`\IK`0IB:+"_>;%0`]IH`0IH;Z_P`]II`0IJ+*(8;W_P#2IA\`/:;` +M$*:R(QV@K("BRO^G.S'R(R.P_X+B(R;PJH"B8RNG/BB2`UR"`V*A3@`PF1&L +M&+*@@+"Y(+)JQZT")0(`+0H=\$K"XDR`##(=\+T"980"AO/_DFK'QO?_``PR +M'?`V00!"(D1B(C8RPEQ`I""]!F6L`J)C*[T&K02EJ`(,!Z)C+%*BH%I2K0*E +M8/]R0ZBR(R&2(RQ"(RV"(Q\;F1M$0F,MDF,LEY@*HB,K>"X(C*W)C+(+(`8)C*U:I\*(E'Z@*5HKZ8+3` +M5AO\QN?_##+13@#R([!\_`P>Z0_";8$=\``V00!2HTQ04H"")3E")3B`(!2< +MI(`@IHR8=H`$D""FC!D&_?\`,J8B930,`AWP``""$P&B!7P,A(!@!.!F$8"" +M(4!($&`B($`B(*QJD@5]D)`4P)D1D"(@0B4Z<5H`8A,$0$"D0$01<&808"(@ +M0"(@!NC_`#P)D)@0D"(@!O;_`#;!`@Q%L3X`\34`T6D`HM(#0B+(!(:4" +M0$`4C/0+A!;X#Y+$_A9I$GSR'?```,(J3%(3`)%Z`((J2U#R@Y`O((#RDRT/ +M)VP"L"\@0A,!\4@`D2$!0,`D%HP7)V0"\"(@%V0"D"(@0(`$@F$P!V0"X"(@ +M5V0"T"(@X>$!8@K!T>`!8F$N8&`4%OP4P:8"0(@4X(@!P"(0("@@@:<"1J`*B82^@@'2`B!%0I2"0(A"`(B`E +MA0*"KP"R(3"`@A"@('2`(B`6FQ`,"PP&#`0,`PP-#`X,"@P%#`S`\#10_P'P +M(B"08*:,F7:`!(!@IHP8!OW_`'*FDLS^%AD1\LS\%C\:)HPU&RP=\`""*DKP +M(``6Z!&B*DRR$P!0FA`6JQ=7"A0AJ0+`8*:,3-!@IE:=_P!RI@P2'?`,`AWP +M\&"FC$^`8*96F/\`=::08*:,2?!@IE:?_P!ZICWP@&"FC$B08*96F?\`?J8] +M\*!@IHQ*X&"F5I[_`'VF/?#P8*:,3X!@IE:8_P!SICWPD&"FC$F@8*96FO\` +M=*8]\-!@IHQ-X&"F5I[_`':F/?#P8*:,3X!@IE:8_P![IALL'?"0(B`&H_\` +MT,(@@L;]X"(@@"R3QJO_``P+4:H"P5P`T3P`X:L"@`,"2)A,7*3"[)A,J)A-+(A+@PO#!KPNQ!P(2'] +M#8T+L)J#\&(18&?`DF$@L(J3@-Z3D/Z30%\@DI,)0$T@8/63D.$A\%X14%G` +M4-230I,*43X`,F$S0*$A4&\@\+H1L+3`L/:38B$S(F$LXF$H8I8(HF$F4%T@ +M8+$A\#L1,#;`,-634:X",B$TLF$G4/\0P#,1,F$M.KLZJC(A+U!=$*"@M,`S +M$3)A*CHB.N[@X+0@(+1`(A%`[A$QKP+@52`@_R#AK`(B(3&PL+3@_Q`P(A#@ +M51`Q(0&P52#PJB"R(3+B(3+QK`(P(B"`[)/6*0`&V`*2824R(2B0TB'PW1'7 +M$P6!>@"`[B#6)@"&U0(R(2=@TB'PW1'7$P6!/@"`[B`R(24;AH""(1N3,-(A +MD)(A,)VS8#(ATB$J8(.S,B$@T-%!TF$IVIDPO).0D+0QK@)`F1'="S#N$#(A +M+9#N(/#N$#`Q(3J(@("T@.X@UB<`AL,"8B$L<+(A\+L1MQ8%P7H`P-T@UB0` +M!KH"PB$F0+(A\+L1MQP%P3X`P-T@0,(A<+(A&X0;EY"2(8""(7";L[&N`D", +MLPQ,L-T0LB$I.HB`@+2ZF9"0M$"9$9#=(/#=$(#=((8"```,#0P.#`H,!0P, +M#`L,!@P$#`.&^/Z!D`"`DA"282N'@C,,!`P##`X,++(A+\!:$5!0M,"[$;!A +MM+"BM$"J$4!F$0P+T&8@\&80#`U@52`,!@;I_@``89(`@>$!8&(0@(;`S!A& +ME`*!X`&'E@(&%P.1D@"70@)&>`+283MR83;"83=BDPK"DPMB82-@\2'P?Q%P +M9L!QL`*-#L"1!)#GDY$^`$)A-<)A))!>(&#EDU*3#?)A)N)A.%!!!$"'DT*3 +M#)"8($)A(D#A(?!N$6!$P$")DV*3#D*3#X)A.F)A(8&Q`F"1(?!Y$7!FP$!Q +M!'#8DW$^`%)A'^)A&G!](D])A/'*3"=(A.T)A'G!A!DV$^`))A&=)A +M.V#=(&*3"&)A.3(A.6!A(?"&$8`SP((A.W)A)6)A)S"-DX)A.\`Q(<#:$=)A +M+3)A+-JOVN[:9F!@M.#@M*"@M,`R(7#Q(<(A.#)A%/)A*!N/@($$VMG0T+1P +MD@3PB;-P3@B$Y8"(00"(@;0C6*`!&K01R(2>` +M0B'P1!%'%P6!/@"`,R""(24;1F"2(4!"(6!)LY(A*("#(1MY<'(AD'BS@B$J +MD:X"@()!D#,0BG=P<+1`=Q%P,R!R(2V2(13P,Q!P<2%Z1$!`M$`S($(A+()A +M%9"0!!N$@($$0(FSD;("30R`29."(2/6*`"&#@5B(2."(2:"80]@8B'P9A&2 +M(0]G&06!/@"`1"!B(2."(20B83T;EF`B(9"2(6"2LR(A+("#(7J9&V)@8B$@ +M:+,AK@*"(160D+0@1!"*9F!@M((A'4!F$6!$(&(A%R+(`?!$$)!$("`A!)&R +M`F!@!(`FLR#)DX(A(L!L("(A/=8H`,;Q!,(A%8(A(I(A&I)A$(""(?"($8)A +M$9(A$8(A$)<8!8$^`(!F(#)A/B)A/3(A'R(A(C`S(1N"()(A@((A((FS(B$= +M>HB`@+0;DI"2(2"3LR&N`LJ9D)"T0)D1(&80(7H`D&8@\&80DB$<@&8@@B$6 +M&SDP,02`@`20.+,PLI.2(2$B(3TR(3[6*0`&U022(2&"(1F"81*0DB'PF1&2 +M81."(1.2(1*'&061/@"0NR"2(2&0@B$;^?#R(9#XLWK_\F$\DB$>\B$&PLB'PNQ&W$P>!>@#HH8#N(-8F``:&!#(A)V"R(?"[$;<3!<$^`,#N +M(+CAD;0"&X:`@B&P>9-@LB%@B[.R(27!K@(;F[#2(9"2(;"=L[(A*L#N$+"Q +M0;)A*;J9D)"TLB$M0)D1D.X@\.X0L+$ANHB`@+2`[B""(20]!]T'UB@`1G($ +M8B$L@$(A\$011Q8%T7H`T-,@@B$CUB@`!F@$PB$F@#(A\#,1-QP%@3X`@-T@ +MPB$D&XS`DB&`@B'`B;.2(2,B83T;*9#"(2`B(9`LLPQ,DB$INB(@(+2:B)&N +M`H"`M$"($9#=$(#=(/#=$"#=("(A/09[_I&0`($A`9"2$))A*X>9`D8Q`Y%( +M`((A*U)A&)>8`H:J`X&0`%)A&(="`L::`?(A&*)A-')A-B)A/4",!$">!$!O +M!&FQF<&"83HALP)]#4"M!*GAD'Z3@-Z370]@4I/BDPWB81^@\I-2842A>@!2 +MDPE2826@AR"@32#@(2%0D2&282@B81WP:1'P(A%@5)-2 +MDPM2821R84/BDP_B81YR(41082%B82S@@2&"81R@IR#P9A'P*!$@[L!@5<`B +M(4-0])/@>I-R843B(4.A/@!2DPQ282*@(B!002%"81KP9!%@5)A*E!$$.J9ZJKJB.IWBXB+JI"0M$"9$:"@M("`M%`S$'!PM$!W$5!=$$"( +M$4"J$:!$((`S()!5('#_()T+XB%"@!@:R""(2+6*`#&R@/"(1J`LB'PNQ&W'`7!/@#`9B"R(2*2 +M(1\;B[#"(8""(;",LQNYD,(AL+(AD+RSD:X"PB$ID&80RKN2(1M+N["PM$"[ +M$;!F()J(@("T\&80L;0"@&8@B+&2(1Z`>Y.]!\T'UBD`AL$#@B$<@F$) +M<'(A\'<1DB$)=QD%L7H`L+P@PB$AUBP`AJL#8`L8O +M`Y%(`%)A&)>8`L:'`X&0`(="`L:!`4%Z`,)A-W)A-K)A12)A/;(A&"(A+W*3 +M"W)A),`B$7#!(?",$<)A+(!WP(&S`BK,P)`$D+B3P,&T0,P1@I,)0&L@<+:3 +M@F$E)A)O"($"HN(""T@*(@@I,-(B$]@F$?@.$A +MXF$=T.T@%V@%X7H`X.T@TI,,(F$]T($ATF$B\"@1(-W`(3X`("X@T.*3TB$? +M(B$IT-(ATF$7*MTAK@+0T+1`W1$@+A#0(B#2(2V"81KP(A#:V$"/!-#0M"#M +M($(A&"&S`HFQ@$*3@I,/(B$]@F$>@-$ATF$@#0U""(,4(A*")A +M/8"`!!LD("$$0"BS@I,.07H`/0N0/)-`0R`@-)."82&`02'P)!$@B,`A/@`@ +M+2"`TI."(1XB(2F`@B&"818JB"&N`H"`M$"($2`M$(`B(((A+4)A&?`B$(J$ +M@("T(-@@(B$]UB8`AOP#8$(A@B$GB0'P1!&2(0!'&06!/@"`,R!"(2U@@B$B +M83T;EI"2(2(A)6"8LX(A*$!!(4)A&TJ9(",A&TA`0B&`0K."(2J0D+2`@D&" +M816*1(&N`D!`M$!$$8`S$$`S(((A+$(A%/`S$)`S(!LH0$`$("$$F.&`)+.! +MM`)P1R"02).1>@""(2.0E"`@29,B(3W6*`!&VP-B(2."(2:)$6!B(?!F$9(A +M`6<9!8$^`(!$()(A(S)A/B)A/3(A%QN)D&(A@((AD(:S8B$=*,$P,`0;EI"1 +M!&"3LS%Z`""\DR(A)#`[()"SDS(A+"`C(1N3D)(A,)*S,:X"(B$5;0LP1!`J +MF3(A/I"0M"(A&T"9$9!$()(A(BJ(\$00(B$]@("T@$0@UBD`!KL#PB$:D+(A +M\+L1MQP%P3X`P&8@DB$BLB$6&XF0PB&`@B&0C+/"(1RPL`0;G)"1!,";L[T' +MP;0">+%PO)-Q>@#"(1]P>R"0MY-R(1W`PR$;EY"2(7"GB2(2%P<+1P9B#6*0"&G@-R(1F0PB'PS!''%P6! +M/@"`NR""(2&2(1XB83T;R(`B(<#"(8#"LR(A')"3(1N"@((A((FS(:X"DB$5 +M(+L0FH@B(1N`@+1`B!&`NR#PNQ`JS,#`M"(A/<"[(`R,AL7Z#`T,#@P*#`4, +M#`P+#`8,!`P#1L#Z``!R839`7`0B83V"DPF"824B(2]0WI/`>A%0O).YH7)A +M+5%Z`(!A(<`B$6)A*")A*BHF4%T@\&81(""T8&C`8-638I,(0"(143X`8*$A +MHF$G>GI072#PJA&@IL"@U9-1K@)P<+2M"U!=$"!5("(A/?!5$'!5(-8H`$83 +M`[(A)3(A*+"R(?"[$;<3![%Z`*BAL*H@UB8`1A`#,B$G8+(A\+L1MQ,%P3X` +MP*H@8((AXB$E&[:PLB$;SN#2(<#"(>#-L](A+>&N`F"XL]#1(>"J$-J[TB$J +M#`ZPL+30T4':S,#`M`P-0,P1P*H@#"SPJA"PJB"&A_L`P;,"4;0"(F$]LB$O +MP(H1TI,*DI,+DF$DTF$C@F$MP+L1T.$AXF$FLF$JD"$ABHZ`@+0B82RZLO#N +M$?`B$>#=P+"PM$#M!.!UDU(A&$"[$2`IP.!@!BDPV0@2&"82AB81_P^!'P^@#0 +MVR""(2+6*`"&:0+"(1J`LB'PNQ&W'`7!/@#`W2"R(2*"(1\,3+"2(8!B(1M( +M&SLP,B%`0B&`1K.P.;-B(1L,"X&N`FHS8B$I@-T0,#"T:D0,!DM$0$"T0$01 +M0-T@\-T0#`0PW2`,`\;'^1M(0'$A0$(A\$01!E'[````8F%)0+T$\;,"4B$8 +M(F$]P*H1@I,+@F$DHF$M0"\$*;'-!8"1(9)A+"#/D_#I$;!?D^#HP/%Z`"*3 +M#R)A'O#5(.!=D_#\("#1(=)A'/!M$6`BP"#/DV*3#B$^`&)A(6#Q(2`L(/#O +M$>!FP&#"DR*3"O)A&2)A(R!A(6)A)O#F$>`BP*IFX3X`JJ]@8+3QK`+@Y2`@ +M7I/B(2^@H+0B(3W`[A'B82KJF>K=B]V0D+1`F1'AK@+0T+1`W1'@51#@S!#0 +MS""052#1K`*1M`+M!]#,$-!5$&!5(,"J(+#ID^G1UB@`ABD"LB$D8B$LL+(A +M\+L1MQ8'@7H`Z-&`[B"2(2/6*0#&)0+"(2:0LB'PNQ&W'`7!/@#`[B"XL=(A +M(Y&T`AN-L'F3@((AT+(AT(NSTB$D&YW0LB&0DB'0F[.R(2K1K@+-!["Q0=#N +M$+J9D)"T0)D1D.X@DB$MW0?P[A"0D2&:B("`M(#N(((A'K)A*9)A&]8H`,88 +M`C(A'("R(?"[$;<3!=%Z`-#<(((A(=8H`(8&`G(A&8"R(?"[$;<7!<$^`,#= +M(+(A(8(A'@Q,L)(A@&(A&T@;.S`R(4!"(8!&L[`YLV(A&PP+@:X":C-B(2F` +MW1`P,+1J1`P&2T1`0+1`1!%`W2#PW1`,!##=(`P#QD?Y&VA@@2&)\6!B(?!F +M$<;P^L(A%1N(@)$ADF$0@((A\(@1@F$11@W[`!N9D($A@F$2D)(A\)D1DF$3 +M!BK[#`L,!@P$#`,,#0P.#`H,!0P,AC/YPJ(`LJ,`L+00QQL"!I+Y5^0"AI#Y +MT"(@!H_YLB$EF>$;N[`Q(;"R(?"[$89U^P`;MK`Q(;"R(?"[$49X^QLX,,$A +M,#(A\#,11I;[&TA`82%`0B'P1!$&C/L``)(A+\"*$7*3"7)A)8)A+7!1(<"9 +M$9!!05)A*$)A*4%Z`)J5D&`$\%41D)&T4%?`8-Z30$T@8I,(4-2303X`8%$A +M4F$GBH7P51%`32!05L!0U)-1K@)`F1&`@+1071"052#P51"`52"6UW$R(2BM +M"^(A*7#2(?"]$>K=T-`$T*R3MQ,%X7H`X*H@EG9Q,B$G8+(A\+L1MQ,'P3X` +M/?#`JB!@@B'B(24;MK"R(1O.X-(AP,(AX,VSTB$MX:X"8+BST-$AX*H0VKO2 +M(2D,#K"PM-K,P,&T#`U`S!'`JB`,+/"J$+"J(,8?_@"9H1M(0($AB4%`0B'P +M1!&&[?L;1D"!(8E10$(A\$01AO'[&VE@@2&)<6!B(?!F$484_!NXL,$AL+(A +M\+L1AC/\&\S`<2'`PB'PS!'&4OP``&(A)!MF8($AB6%@8B'P9A&&_OL`&\C` +M@2&)@<#"(?#,$48?_!MY<($AB9%P9.M![<6!=%Z`-"J(.(A +M(Y:.7,(A)N"R(?"[$;<#2(1NY&\[`PB&PLB'@ +MS;/2(2WAK@*0N+/0T2'@JA#:N](A*0P.L+"TVLS`P;0,#4#,$<"J(`PL\*H0 +ML*H@QL#]``!`G`1M#8*3"8)A)9!NDX!1(5)A*&!6(!=H!5%Z`%!6(')A-H(A +M+R)A/9)A2T!N!)$^`&#>DVG!P"H1(F$M8I,(P(@1@F$JD)4@@(%!8*$AHF$G +M*BJ"82GPJA&@IL"@69.AK@*2(24@(+2@I1"0DB&9,8IY<'"T@I,-@F$?0'<1 +M<*H@\*H0(%H@@)$AK0V281TB(3V2(4L7:`6A>@"@K2#2(2CH,1N-X.`$@($$ +MT(ZST7H`[0N0[)/0WB"`[9."DPR`T2&"82+PG1&0B,"1/@"0FB"`J9."(1^2 +M(2F`@B&"81>:B)&N`H"`M$"($9":$("9(((A+=)A&O"9$(J-@("TD*@@EB9' +M,B$G8-(A\-T1UQ,%@3X`@.X@DB$M&X9@TB&`@B%@C;/2(260D2&281N:B)(A +M*")A/=#3(1LI("(AD"VSTB$7DB$J@("TT-`$D))!DF$5FB*1K@(@(+1`(A&0 +M[A`@[B`B(1WP[A"8P8#N(!N"@($$((VS(7H`D+R3DB$B("L@@+*3W0LB(3V6 +M>3_"(1J0LB'PNQ&W'`7!/@#`W2`,3+(A(F(A'X(A';"2(6!C(1M(&SLP,B%` +M0B&`1K.P.;-B(1L,"X&N`FHS8B$5@-T0,#"T:D0,!D!`M$!$$4#=(/#=$`P$ +M,-T@#`/&!_C!LP*2(1A`W02"DPN"8239X8"Q(;)A+-"K=T-&TDF$<0-T1 +MT*H@\*H0L%H@0*0@%V@%H7H`H*0@P7H`N.&!M`+M!Y*3#I)A(;#HDX(A)+(A +M+,#.((""!!O;T-$$L-BST.R3@3X`T:X"D,$A\+P1L)G`@(H@D*B3DB$J@B$< +MPF$9T-H0FHB`@;1`B!&`W2""(2VR(2/PW1"*S,#`M-"L()9[*L(A)K"R(?"[ +M$;<\.X0D)$ADF$; +MFHB`@+2`[B"1>@""(1S`P@20ER`;N+"Q!("\LX(A(;!YD]T'EL@B)/=!P9C^QNXL#$AL+(A\+L1 +MAN7]&T9`@2&)`4!"(?!$$<8!_!MH8($AB1%@8B'P9A'&(_P;N;#!(;"R(?"[ +M$49#_!O)P'$AP,(A\,P1QE_\@B$IK0L;-S"R(8J+,#$A\+L1@(`$@*R3!C?^ +M`!NVL#$AL+(A\+L1ACC^&VF!M`*2(2E@LB%@82&:F_"[$9"0!)!XDZT'QHG^ +M&[ZPP2&PLB'PNQ%&C/X;UM`Q(=#2(?#=$<;A_ANYL,$AL+(A\+L1A@#_&[NP +MP2&PLB'PNQ&&5/\;N+!Q(;"R(?"[$49S_P``-F$`HJ`!I5/+94_6@4\`H4X` +M#`^13P#R:H#R:L#Y"9$W`/)H0+(IK+#`!`=K$GSNX.L0XFFLP"``TBFLV0'` +M(`!V@!#B*>?I$=@1T-`4V1&($28X`@;Z_X$Y`!P.(B@@#$W`(`#0(B`B:"#` +M(`#2*"#`(`#R:I[`(`#@W2#2:"#`(`#B*"!L\B#N$.)H("(H('R]T"(0(F@@ +MG+SQ/@"R::S`(`""*:R)`<`@`.(J@O#N(.)J@AWP`+$^`)(J@K"9())J@AWP +M```V00"BH`$E1\NRH;2ZLJ(KI`PHDB+'&ZJB:Z2)":7Q_QWP```V00"RH0`, +M''S]H0#2(\=9#27B_^4QU@SO@M<$#`61S`"ARP"B9X"29\!9"/)D0.(C/VD! +MZ2%G/@+&/P#X(>@!;04I4?#NP.D1J"&R(S:Y0:JFJ3&E'@`M"J)C0[A!J#$E +M(0`,&Z)C0@PY_04C_P"C_PB3_Q[R9\GB(T/2(RW-!>/\"F8]%)/\%%/\&MT, +ML_TU%E!$ +MP``40``SH3WP=I0(-S(","+`,#%!-S(","+`'?`,`AWP```V00"V(SEM`E#R +M0$#S0$>U)%!$P``40``SH2*@`':4#3)BA)=VLK#!W210>2`U*B(Q&R!";" +M(V+"8V"R1!ZR!!ZB8V*21"9W:Q`,,AOJXF,1'?"M`B7\\,;R_ZT"#`RE^_#B +M(Q$,,AONXF,1'?!W:Q@@HB"R`U+"H`#E^?#R(Q$,,AO_\F,1'?``K0(,#*7X +M\`;W_P`,''SXL@-2LD0F@D0>HF-BK0(E]_"2(Q$,,AN9DF,1'?`````V00"R +MT@*R"T1W:P$=\*T"#`SE]/`=\````````%<#8`!7`V``5P-@`%<#8`````#_ +M____`````/____\`````_____P````#_____`````/____\`````_____P`` +M``#_____`````/____\`````_____P````#_____`````/____\`````____ +M_P````#_____`````/____\`````_____P````#_____`````/____\````` +M_____P````#_____`````/____\`````_____P````#_____`````/____\` +M````_____P````#_____`````/____\`````_____P````#_____`````/__ +M__\`````_____P````#_____`````/____\````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +;``````````````````````````!7`P`(-P`! +` +end diff --git a/sys/contrib/dev/drm2/radeonkmsfw/TURKS_mc.bin.uu b/sys/contrib/dev/drm2/radeonkmsfw/TURKS_mc.bin.uu new file mode 100644 index 00000000000..8ea23315a8a --- /dev/null +++ b/sys/contrib/dev/drm2/radeonkmsfw/TURKS_mc.bin.uu @@ -0,0 +1,539 @@ +begin 644 TURKS_mc.bin +M``/HX``"O@$```(4``/_```#_P```J`.``'`'0`#Z````^@0``/H(``#Z#`` +M`V`8``-@'``#8"```V`H``*P_P`"L?\``K+_``*S_P`#83@``TEL``/_```# +MKPH``K0$``)O]```^CL``K0/``)/%```^A<``T`D``*T0``"M0\``TJF``)$ +M00`#\`(``DF5``-JI@`"+:@``TG4```R0P``(A@``#H5``*U$```FD,``D53 +M``/P"``"H'X``_!&``*@8P`#\$0``/(/``(TOP`#]````<`E``/H````@@\` +M`T`D``*T0``"M?```TJF``)$00`#\`(``FF5``-JI@`#0````_\```/X`0`# +MQ$```\11``+&10`"`````T`0``/_```#_P```^@@``/H,``#8!```_@#``(S +M;``",J@``T@(``*TGP`"0`0``V@(``-((0`"L\$``D=S``-H(0`#:HD``T@E +M``*SP0`"1W,``V@E``-JC0`#2,```TC&``"",0``BC```,(O``#*+@`#Z!`` +M`^B0``-HP``#:,8``TJD``/H0``#_P```D(D``-JI``#2````T@%``*[/P`" +MO+\``K_O``"2,P``BC0``((X``"Z-@``JCH``D$;``*[^P`"0BL``D`,``-H +M```"15\``V@%``-($``#2=T``KOQ``*\`@``BC<``*HY``)!&P`"81P``V@0 +M``)%6P`"95P``VG=``-`#0`#_P```_\```.A3```B@T``K,"``)#-``#HS`` +M`)H,``*S!``"0S0``Z,R``":"P`"L`@``()!``*P"0``@D(``T@I``-)<``" +MOP<``D`/``.````#``X``X0&``.%5@`#I58``F54``)D0``#2>@``V@I``-H +M+0`#P00``\$5``-IZ``#:>P``^@```/HT``",$\``VCD``/!W@`",$\``VD$ +M``-`+``#_P```K0"``)$!```HA8``TG6``*U$``"1%L``Z1&``"B&``"H$X` +M`_`#``(T:@`#]````<#%``/T```!TW(``T`.``#R0``#Z/```Z"V``-)U0`` +M^A$``K$/``)"%P`"I2```_`!``#Z0``"0QL``J4C``/P`0``\A$``T````-` +M+@`"MA```K0,```J0``"0B0``Z(B``)#-``";",``D9H``/P`P`"H%X``_`! +M``/HP```XA```T`(``*T````>A$``J`D``/P00`"L@@``J#^``/P00`"L@0` +M`K0_``)")```DB@``TJ4``/_```#8&P``T````/_```"M$```D0$``/P`P`# +MZ````((_``(B^@`#0````_\```*T0``"1!0``_`"``#R/P`"(OH``T````/_ +M```"M$```D04``/P`0`"+FX``TJ4``/_```#8'```T````/_```"M`(``D0D +M``/P`P`#Z````((_``(C,0`#0````_\```*T`@`"1#0``_`"``#R/P`"(S$` +M`TJ4``/_```#830``C-L``-````#_P```_\```)$#@`#\`,``^@```""/P`" +M)%H``C-L``-````#_P```_\```)$'@`#\`(``/(_``(D6@`"+JD``TJ4``/_ +M```#8'0``K````""*P`#0````_\```*T`@`"1$```_`#``/H````@C\``B89 +M``-*E``#_P```V!X``-````#_P```K0$``)$0``#\`,``^@```""/P`"*)P` +M`TJ4``/_```#8'P``T````/_```"M!```D1```/P#``#Z````((_``(I;``` +M`@T``_\```/_```"I`X``_`$``#R#@`"*6P``^@```""#@`#2I0``_\```-@ +MK``#0````_\```*T(``"1$```_`#``/H````@C\``BN$``-*E``#_P```V"P +M``/H````@C\``K#_``*Q_P`#P2```VA@``-H:``#0````_\```*T$``"1$`` +M`_!"``/T```"`8\``T`-``-`)@`"L4```K(0``*S"``"010``Z$:``)")``# +MHB0``F(A``)#.``#HS```F(C``"2*P`"I!X``_`)``-)/0`"LH```\`B``)$ +M0@`"*6P``TD]``/_```"LH```F1"```"$``#_P```K0!``)$!``#\`0``^@` +M``""/P`#P?X``BQ&```"$``#_P```K0"``)$!``#\`0``^@```""/P`#Z/`` +M`BQ&``-````#_P```_\```)$+@`#\`,``^@```""/P`"-7<``T````/_```" +MM!```D0D``/P`P`#Z````((_``(VJ0`#^`,``TJ4``/_```#8+0``K````"" +M*P`#0````_\```*T`@`"1$$``_`"``#R/P`")AD``TJ4``/_```#8+@``T`` +M``/_```"M`0``D1!``/P`@``\C\``BB<``-*E``#_P```V"\``-````#_P`` +M`K00``)$00`#\`L``/(_``(I;````@T``_\```/_```"I`X``_`$``#R#@`" +M*6P``^@```""#@`#2I0``_\```-@[``#0````_\```*T(``"1$$``_`"``#R +M/P`"*X0``TJ4``/_```#8/```/(_``*P_P`"L?\``\$@``-H8``#:&@``T`` +M``/_```"M!```D1!``/P0@`#]````@(.``-`#0`#0"8``K%```*R$``"LP@` +M`D$4``.A&@`"0B0``Z(D``)B(0`"0S@``Z,P``)B(P``DBL``J0>``/P"0`# +M23T``K*```/`(@`"1$(``BEL``-)/0`#_P```K*```)D0@```A```_\```*T +M!``"1`0``_`#``#R/P`#P?X``BQ&```"$``#_P```K0(``)$!``#\`,``/(_ +M``/H\``"+$8``T````/_```#_P```D0^``/P`@``\C\``C5W``-````#_P`` +M`K00``)$-``#\`(``/(_``(VJ0`#2I0``_\```-@]``#^`,```(8``/_```# +M_P```J`.``/P0@`#]````@)L``-`+``#_P```K0@``)`0``"H`0``_!(``/H +M````@C\``C((``(R30``\C\``C((``(R30`",N0``T````/_```"M`@``D0$ +M``/P#``#2`@``_\```*T0``"8`0``V@(``-)F``"M/T``D$4``-IF``#Z#`` +M`\$N``-H,``",O@``^@```/H$``#Z#```K(#``-H,``#0````_\```*U"``" +M114``_`!``(TUP`#2I0``_\```-@^````D```_\```/_```"H0X``_`"``/H +M````@A0``BZI``-(```#2`4```HT```".```$C,``"HZ```Z-@`#_P```V@` +M``-H!0`#2!```TG=```*-P``*CD``_\```-H$``#:=T``TEQ``*R`0`#Z#`` +M`\$&``/!%P`#:#```TEL``*U%@`"H%$``<*?``*PSP`"L04``K(!``*S```# +M:#```C+X``*PM``#:#```C+X``*PO``#:#```C+X``*PN``#:#```C+X``*P +MRP`#:#```C+X``*PC``#:#```C+X``*PT0`#:#```C+X``/H```#Z!```K(# +M``*S```#:#````(8``/_```#_P```J`.``/P0@`#]````<`````!]``#0!(` +M`T`5```*&P`"H`X``_`"``/T```!PL\``L$>``"*&P`":[H``F5;``+&;@`# +M\$$``L=^``-@%0`"L@\``D1"``*E%``#\`<``^B@``/HL``#8!(``^@```"! +M]``#]````P`#P?```BY[``/!\``"+GL``T`@``/_```# +M_P```L`.``/P10`"P1X``_!#``+"+@`#\$$``L,^``-@(``",VP``TE(``*T +M/P`"M8```D,T``)C-0`#:4@``D,T``-I2``#]````<````-`!```:C\``^B` +M``/HD``#Z*```^BP``*U$``"1!4``_`&``*@W@`#\`,``V2J``/T```#\$$` +M`V3J``/X`0`"M0@``D0%``(#'```8?@``J#>``/P`P`#Z$```_0```/P00`# +MA.H``]A```/?<``_@#``/4G@`#^P```P_^``(#&``#^`$``X7D``)$ +M%0`"`R\``"'Y``*@W@`#\`,``&'Q``/T```#\$$``&'P``*_"``#W$```]C` +M``/X`P`#U)X``_L```,/_@`"`RL``_@#``/L`````A0```I```!J/P`#_P`` +M`D`!``*@#@`#\`$``^P```-).``"H-X``_!!``-*7``"N/X``D(H``*@W@`# +M\`,``VDX``/T```#\$$``VI<``-)T``"M`0``F$4``*R`0`"LQ(``J#>``/P +M00`"LQ$``V@P``-)<0`"N.\``KD$``/!!@`"87D``D,X``-H,``#2`D``KC] +M``)$2``#:`D``BY_``-)H``"M!$``\$D``*Q#P`#::```TI$``*T_@`"81X` +M`VI$``)!%``#:D0``J#>``/P`P`#0F$``_0```/P00`#0ET``_\```/_```# +MZ&```VFE``-H:P`"N@P``KC```/<@0`#V*$``K#X``*Q!P`#Z"```^@P``/^ +M```#U!\``VAK``/H\``"(_\``B/B``(D'``"H?X``_!!``-B>@`#0G@``_\` +M``/_```"H(```<.3``*@D0`!PY,``J"B``'#DP`"H+,``<.3``+/_@`"O`P` +M`J'\``/P0@`#]````<-]``#Z!``#Z/```B/B``(C_P`")!P``T)X``/_```# +M_P```J"```'#J0`"H)$``<.I``*@H@`!PZD``J"S``'#J0`"S_X``KP,``*A +M_``#\$(``_0```'#E@``8@0``/H#``/_```"I<\``<.Y``*E_``!P[0``^CP +M``/HP``#]````<.\``,,SP`#K,```^CP``/T```!P[P``P_\``.O\``#Z,`` +M`B/_``/!_``"(^(``TDX``*@W@`#\$$``TI<``/_```"8BX``J#>``/P`P`# +M:3@``_0```/P00`#:EP``TG0``*T^P`"010``K(!``*S$@`"H-X``_!!``*S +M$0`#:#```TEQ``*X[P`"N?L``\$&``)!>0`"0S@``V@P``-("0`"N/T``D1( +M``-H"0`"L!@``B1```/L```#1+D``J#>``/P00`#1/D``\$/``/!'P`#P2\` +M`\$_``/!3P`#P5\``J#>``/P!0`#9+```V2T``-DN0`#]````_!#``-D\``# +M9/0``V3Y``-(R``"M/,``K4$``)`!``"8`4``VC(``)`!``#:,@``^P```-$ +MN0`"H-X``_!!``-$^0`#P0\``\$?``/!+P`#P3\``\%O``/!?P`"H-X``_`% +M``-DK``#9+P``V2Y``/T```#\$,``V3L``-D_``#9/D``TC(``*T\P`"M00` +M`D`$``)@!0`#:,@``D`$``-HR``#[````K`0``(D0``",O@``C+X``(R^``" +M,O@``C+X``-*'``")$P``\&,``-*(``")$P``XS&``)HC``#2B0``B1,``/! +MG``#2B@``B1,``.,Q@`":9P``THL``(D3``#P:P``THP``(D3``#C,8``FJL +M``-*-``")$P``\&\``-*.``")$P``XS&``)KO``#[````VAK``*Y"``"N,`` +M`]R```/8D``#Z!```^@@``/H,``#_@```]0>``-H:P`#[````^C```.'/``# +MIW8``X8L``.F:``#A1P``Z5:``.$#``#I$P``FS'``)LQ@`";,4``FS$``/L +M````(@\``T`D``/_```"1$X``Z`&``*A#@`#\`$``(('``-)I``#2:$``&H_ +M``.+Z@`#P*L``D6E``*@W@`#\`,``D.C``/T```#\$$``F.S``-II``#::$` +M`K+_``*Q_P`"L'\``VA@``/H```#Z"```^@P``*@W@`#\`,```H)``/T```# +M\$$```H(``*XP``#:&L``]@```/<@``#U!X``K\"``(N>P`#@.```(($``"" +M`P`#Z````(("``""`0``\@```TA4``*@W@`#\`,``X3H``/T```#\$$``X3J +M``)B)``#:%0``TF8``.(Y0`#P3X``J#>``/P`0`#@S```\$N``)@"``#:#`` +M`TEU``.(Y@`"8&@``\$7``/!+@`#P3X``J#>``/P`0`#@S```V@P``-(5``" +MH-X``_`#``.$Z``#]````_!!``.$Z@`#P$0``D(D``-H5``"OP(``BY[```" +M&``#_P```_\```*@#@`#\`(``_0```($UP`",&$``^AP``.$Y@`"1DL``J!D +M``/P3P`#2=(``^A@``.*L@`#JJH``X2F``)D2@`#P50``J#>``/P`P`#:.4` +M`_0```/P00`#:04``_0```'%@@`"OP@``D_Y``.O]``#B[(``ZN\``(EI@`# +M]````@6"``-`#``#_P```K\(``)/\``#\$(``_0```'$]``"OP```KL!``(E +MI@```A$``_\```/_```"H`X``_!"``/T```!Q8(``K$"``*P$0`"Q$```L50 +M``*@W@`#\`,``VCE``/T```#\$$``VD%``,!'@`"!.D``/("``#R`0`"H-X` +M`_`#``!Z!@`#]````_!!``!Z!0`#C>(``]S0``/8\``#B>8``FF>``#Q\@`` +M0@<``^C0``./X``#P:@``XSM``#A]@``XCX``B_*``*W`0``0@<``^B@``#1 +M\@`"H'X``_`!``.H@``##]\``\'9``/!J``#C.T``.'V``#B/@`"+\H``C`+ +M``/!!``#P28``V)P``*_`0`#0!$``XC@``*@_@`#\`0``D1(``/P`@`#]``` +M`@5H``*W`0``0@<``_\```*@?@`#\`$``ZB```/HT``#C^```\&H``.,[0`` +MX?8``.(^``(OR@`",`L``T)P``.,ZP`#C.H``^CP``+(0``#J(```"H6``+* +M8@`#JJ```J9>``(%10``:C\``/(6``/!6``#P7H``V)Q``/T```"!/\``J%> +M``/P#@``:C\``\&1``/!LP`"H-X``_`#``-A+@`#]````_!!``-A,@`"P($` +M`Z@```+"HP`#JB```X50``"J%@`#@>```P"$``/P)``"H80``_`!``+$00`# +M!$X``\%4``,"I@`#\"0``J&F``/P`0`"QF$``P9N``/!=@`#U%X``F`"``(% +M5P``:C\``K\"``(N>P`#27$``X'D``/!!@`#HFP``J`N``(%@@`"87$``\$N +M``/!/@`"H-X``_`!``.#,``#AF```_!'``-H,``#C^T``BY[``./[0`"+GL` +M`X_M``(N>P`#C^T``BY[``-)=0`#P2X``\$&``/!%P`#P3X``J#>``/P`0`# +M@S```V@P``-)F``#_P```\$^``*@W@`#\`$``X,P``/!+@`#:#````H/```! +M]``#_P```D$>``/P`0`#[````J4.``/P2``#0!$``J#>``/P`P`"9FX``_0` +M``/P00`"9WX``V`1``/H````@?0``^P```(R^``#2D```$(```!*!```4@,` +M`KPS``)$#``"11P``F1%``)&+``"1SP``F9G``)D1@`"H4X``_`!``*T`0`" +MH(X``_`%``*D20`#\$,``J1+``/P00``\@(``*($``*\S``"1`P``D4<``)& +M+``"1SP``F1%``)D1@`"9$<``J%.``/P`0`"M`$``J".``/P!0`"I$H``_!# +M``*D2P`#\$$``/(!``"B`P`#Z$```*(```-#*0`"H-X``_!!``-#:0```@(` +M`!(!``*Q#P`"LP<``J7^``(%_``"H`X``_`&``*\#P`"I$$``_`!``*\"0`# +MP4P``\%<``*@+@`#\`8``KP/``*D80`#\`$``KP)``/!;``#P7P``J#>``/P +M`P`#8RD``_0```/P00`#8VD``DP"``*@S@`#\`8``L_^``*\"0`"I?P``_`" +M``/T```!Q:8``TCE``*@W@`#\$$``TD%``*X$0`"H`X``_`!``+$2``"24$` +M`J23``/P`0``\@(``J`N``/P`0`"Q5@``DE1``*DDP`#\`$``/(!``*@W@`# +M\`,``VCE``/T```#\$$``VD%``)``@`"H`X``@6F``/L````:C\``^@```(P +M.P`#29@``K@/``*U]0`"LP$``J#>``/P`0`#@S```\$N``)@"``"014``V@P +M``*_```"+GL``/'_``/H````@?X``T`,``-`+0`"N!```KD(``)*&``#JJ8` +M`DM)``.KM``"LP0``F^K``/P00`"LP(``D^K``/P`0`"LP8``T`M```*$0`" +ML$```D!```/P00`#Z#```D$>``/P`0`#Z#```)G]``(N?P`"O`$``J#>``/P +M"0`#2"$``XS&``.&9@`#IF8``F9L``-H(0`#:HD``_0```/P1P`#2"4``XS& +M``.&9@`#IF8``F9L``-H)0`#:HT``K`!``/H$``#Z"```^@P``-J8``"OP,` +M`C'5``-("``#A.H``('U``)@!``#:`@``K]D``(N>P``:C\``_\```/_```" +MH-X``_`#``*[```#]````_!!``*[0```V>\``\$.``/!'@`#@18``F$>``." +MY@`#:D0``\$>``.!%@`#Z"```VI$``/H```#P````\$0``*R_0`#:&```TI@ +M``*_]P`"L1,``D`/``)@#@`#:F```#(,```Z"P`"O`@``K_^``.&:@`"8B8` +M`J1^``/P`0`"LP0``D`/``)@#``#:F```X_A``(N>P`#:&L``_\```/^```` +M8CT``%H\``/_```#V,$``]RQ``./XP`"+GL``]`?``.,[P`#C^0``F_^``)` +M`0`"0B,``D`"``),P``#^P$``]`?``,/_@`"!J(``J#>``/P`P`#2"$``_0` +M``/P00`#2"4``\#,``(&S@`#C.8``L9L``/P00`"QWX``J#>``/P!``#:"$` +M`VJ)``/T```#\$(``V@E``-JC0`"I7X``_`"``/T```!QG$``T`1``*P"``` +M\?0``J#>``/P`P`"9F```_0```/P00`"9W```V`1``/T```""(,``X#A``.! +M!@`"81X``X+G``-J1``#@08``^@@``-J1``"L`$``^@0``/H(``#Z#```VI@ +M``-`+```6BL``K0$``/H\``"1$```_`!``*_`0`"I+X``_`!``*_!``",=4` +M`/(G``#R)0`"L/\``K&_``/!(``#:&```K`!``/H$``#Z"```^@P``-J8``` +M"CL``!(,```:"P`"L`(``J#>``/P00`"L`0``X(J``*D/@`#\`$``K,$``.! +M%@`"8`$``K$S``-J8``#0`H``K`!``*Q`@`"L@```K,!``*@*P`#\`(``^@@ +M``/!.P`#:F@``\$.```20@`"0`D``_`!``/H(``"L!,``K$"``.")@`#Z#`` +M`VIP``-`+0``8A$``!(H``*_0``"3T\``_`#``*@S@`#\`$``K((``*P(0`" +ML0(``K,!``-J=```"BL``K`!``/_```"I!X``_`%``*Q!``"L@```K,"``/T +M!``#\`,``K$"``*R#P`"LP$``VIX``-*?``#_P```K`!``*Q`@`"LP0``VI\ +M```!_@`#_P```_\```+`#@``@?X``C'0```")P`#0`H``T`-``*@#@`#\`4` +M`K`!``)`!0`#@`8``_0$``/P`P`"L`0``D`)``.``@`"L0,``F@!```"*P`# +M_P```_\```*D#@`#\`L``!(H``*P(P`"L0,``K,!``-J=``#2GP``_\```*P +M`0`"L00``K,$``-J?``#2F0``K2````1[P`"0`0``F`(``.B(@`"LP$``VID +M``(PE0`",*(```'_``/_```#_P```J`.``/P3@`#0"P``KP$``/``/P`0`#Z/```]CP``*\"0`"OS\``DL?``(O"P`#Z/```/G_```!_@`# +M_P```_\```+`#@``@?X``T`E```:$``"L@0``D(@``*@W@`#\$$``Z,R``*Y +M`@`"29,``F(I``*Y@``"294``F(I``*YA@`#Z(```J`I``/P00`#P8X``,': +M```)_0`#0"X``_\```*@`0`"!Z(``K`!``)!"``#\`4```)```/_```#_P`` +M`J`.``('H@`#2G0``'HH``/_```#_P```\$O``-J=``",=````(G``-`"@`# +M0`T``J`.``/P!0`"L`0``D`%``.``@`#]`0``_`"``*P$``"0`D``'G:``*Q +M0P`#_P```J#^``/P00`"L2,``F@!```"*P`#_P```_\```*@#@`"!]0``!(H +M``*P0P`"L00``K,!``-J=``#2GP``_\```*P`0`"L04``K,$``-J?``#2F0` +M`K2````1[P`"0`0``F`(``*X(``"8`@``KB_``)`"``#HB(``\$^``-J9``# +M]`0``_`(``-*9``"M(```!'O``)`!``"8`@``Z(B``/!/@`#:F0``C"5``(P +MH@``0A```K`"``*@W@`#\$$``X`"``)`"``#\`(``_0```'']0`#0`X``K`0 +M``/_```"00D``J`0``(']0```B<``_\```/_```"P`X``((G``*Q`@`"I`$` +M`@``/P1@`#Z/```K$D``+(@0`#V(```L`!``/8 +M@0`",(P```'^``/_```#_P```L`.``"!_@``>=H``C'0``*@_@`#\$4``K`/ +M``/!$``#P2```\$P``-J@``#0`H``K!```/_```"0`D``Z`"``*Q`P`":`$` +M``(K``/_```#_P```J0.``/P"P``$B@``K!#``*Q!0`"LP$``VIT``-*?``# +M_P```K`!``*Q!@`"LP0``VI\``-*9``"M(```!'O``)`!``"8`@``Z(B``*S +M`0`#:F0``C"5``(PH@`#0"4``$(0``*P(``"0`4``_!(``*P`@`"H-X``_!! +M``.``@`"0`@``_`"``/T```!R&4``!'^``*P@``"LP,``J`C``/P0P`"0`4` +M`_`!``(QE@`#0"X``_\```*P"``"00@``J`0``((90`"L(```D$(``/P!0`` +M`D```_\```/_```"H`X``@AE```")0`#_P```_\```/H$```BB4``J0.``(' +M=P```>\``$'Y``/_```#_P```]@```/<@```>D(``K````/8`0`"L`$``]P! +M``-`#0`"O2```_\```)-U0`"IMX``_`#``(O*P`#]`0``_`*``*P?P`"L?\` +M`K+_``-H8``#:&L``!'E``*SP``#Z-```V!,``(O9@`"L`$``^@0``/H(``# +MZ#```VI@``-*9``"M(```D`$``-J9```(?4``_\```-("``#_P```\$$``-H +M"``#[````^@```/H$``"H-X``_`#``-I'``#]`0``_`!``-I-``#[````&H_ +M``/_```#_P```J#>``/P`P`"NV(``_0```/P00`"NV@``_\```/_````V?P` +M`C*.``/H```",#L``KL"``#:/@`#2`$``K"_``)$0``#:`$``TF8``*T#P`" +MM?T``F`$``)!%0`#P2X``\$^``*@W@`#\`$``X,P``-H,``#2`@``K1```"! +M]0`"8`0``V@(``/H```"L6```^@@``/H,``#::```^@```*Q$``#Z"```^@P +M``-J1``"L`$``^@0``/H(``#Z#```VI@``*_`@`",=4``^@```/````#P1`` +M`K+]``-H8```"CL``!(,```:"P`"L`H``J#>``/P00`"L`P``X$6``)@`0`# +M@BH``K$,``)B(0`"I#X``_`!``*S!``"L18``VI@``./X0`"+GL``VAK``/_ +M```#_@```C'^``-*8```,@P``#H+``*T]@`"M0,``X9J``)B)@`"I'X``_`! +M``*W!``"8S<``P$5``)`!``#:F```T`*``*P`0`"L0(``K(2``/H,``#:F@` +M`K"````20@`"0`@``_`!``/H(``"L!,``K$"``.")@`#Z#```VIP``*P(0`" +ML0(``K(!``*S`0`#:G0``K`!``*Q`@`"L@\``K,!``-J>``#2GP``_\```*P +M`@`"L0(``K,$``-J?``"L/\``K&_``/!(``#:&```^AP``-*9``"M(```D`$ +M``/H(``#Z#```VID``-*9``"M(```!'\``)`!``"M`,``F`$``*S$``#:F0` +M`C"5``-*9``"M`@``D1```)G=``"I7X``@E<``/H```",#L``J#>``/P`P`# +M2"(``_0```/P00`#2"8``_\```.,Y@`"RJP``_!!``++O@`"H-X``_`$``-H +M(@`#:HH``_0```/P0@`#:"8``VJ.``/H<``"IKX``_`"``/T```!R2$``T`2 +M``#Q]``"OP0``J#>``/P`P`":J\``_0```/P00`":[\``V`2``/T```!R5P` +M`K`!``/H$``#Z"```^@P``-J8``#2F0``K2```)`!``#:F0``"'U``/_```# +M2`@``_\```/!!``#:`@``^P```!J/P``\?\``^@```""$P`#0`@``T`M``*X +M0``"N0@``DH(``.JJ@`"2TD``ZNT``*S!``";ZL``_!!``*S`@`"3ZL``_`! +M``*S!@`#0"T```H1``*P0``"0$```_!!``/H,``"01X``_`!``/H,```FA(` +M`"'T``/H4``#_P```*(9``"I]```:C\``_\```/_```"H-X``_`#``!9\0`# +M]````_!#``!9\``#_P```_\```#9[P`#2````X3J``/`1``#_P```D`$``-H +M```#2`0``\!^``/_```#_P```D,W``-H!``#0"P``%HK``*T!``#Z/```D1` +M``/P`0`"OP$``DN^``*@O@`#\`,``C#]``/T```!R;4``K_+``(S00``\B8` +M`/(E``*P_P`"L;\``\$@``-H8``"L`$``^@0``/H(``#Z#```VI@```*.P`` +M$@P``!H+``*P`@`"H-X``_!!``*P!``#@18``F`!``-`%0`#@BH``K&```)B +M(0`"I#X``_`!``*S!``"MO```D1&``*Q"``"810``VI@``-`"@`"L`$``K$" +M``*R```"LP$``J`K``/P`@`#Z"```\$[``-J:``"L`$``!)!``)`"0`#\`$` +M`^@@``*P$P`"L0(``X(F``/H,``#:G```T`M``!B$0``$B@``K]```)/3P`# +M\`,``J#.``/P`0`"L@@``K`A``*Q`@`"LP$``VIT```**P`"L`$``_\```)! +M$``"I!X``_`%``*Q!``"L@```K,"``/T!``#\`,``K$"``*R#P`"LP$``VIX +M``-*?``#_P```K`!``*Q`@`"LP0``VI\```**P`#_P```_\```)!'@`"I!X` +M`@I4```"$P`#_P```_\```+`#@``@A,``C'0```")@`#0`H``T`-``*@#@`# +M\`4``K`"``)`!0`#@`0``_0$``/P`P`"L`@``D`)``.````"L0,``F@!```" +M*P`#_P```_\```)`#@`"I`X``_`+```2*``"L",``K$#``*S`0`#:G0``TI\ +M``/_```"L`$``K$$``*S!``#:GP``TID``*T@```$>\``D`$``)@"``#HB(` +M`K,!``-J9``",)4``C"B```!_P`#_P```_\```*@#@`#\$X``T`L``*\!``# +MW,```K_```*@W@`#\`$``K^```/8\``#C.0``K\_``)+'P`"+PL``^CP``#Y +M_P```A,``_\```/_```"P`X``((3``!:*P``>B4``K@(``)*O@`"H*X``B@``_\```/_```#P2\``VIT +M``(QT````B8``T`*``-`#0`"H`X``_`%``*P"``"0`4``X````/T!``#\`,` +M`K`@``)`"0`#H````K%#``)H`0```BL``_\```/_```"0`X``J`.``(*O``` +M$B@``K!#``*Q!``"LP$``VIT``-*?``#_P```K`!``*Q!0`"LP0``VI\``-* +M9``"M(```!'O``)`!``"8`@``K@@``)@"``"N+\``D`(``.B(@`"L00``\$^ +M``-J9``#]`0``_`(``-*9``"M(```!'O``)`!``"8`@``Z(B``*S`0`#:F0` +M`C"5``(PH@``6BL``KH!``/_```"2[H``J"Z``/P0@`#]``````/P00`#@`(``D`(``/P`@`#]````\``D`$ +M``)@"``#HB(``K,!``*Q```#:F0``C"5``(PH@``6BL``KH#``*Y`0`#_P`` +M`DBZ``*DB0`!RU<``KD$``/_```"2+H``J"*``'+.0```B4``K((``*S```# +M_P```J0"``'+5P`"H`X``_!#``":)0`#]````D$``K````/8`0`"L`$``]P!``-`#0`"O4```_\` +M``)-U0`"IMX``_`#``(O*P`#]`0``_`*``*P?P`"L?\``K+_``-H8``#:&L` +M`!'E``*SP``#Z-```V!,``(O9@`"L`$``^@0``/H(``#Z#```VI@``(QT``# +M[````&H_``/_```#_P```J#>``/P`P`"NU,``_0```/P00`"NUL``_\```/_ +M````V?P``T`0``*[#P`#2"D``J#>``/P00`#2"T``K\$``)`"P`#JP(``P_[ +M``.,]@`";,\``X_V``-)Z``"HTP``_`$``,$3``#!5\``_0```/P10`#A.8` +M`X56``.E5@`"950``F1.``/!!``#P14``J#>``/P!``#:"D``VGH``/T```# +M\$(``V@M``-I[``#Z````C!/``-(`0`"L+\``D1```-H`0`#Z````K%```/H +M(``#Z#```VI$``*P`0`#Z!```^@@``/H,``#:F```K\"``(QU0``"CL``!(, +M```:"P`"L`(``J#>``/P00`"L`0``X$6``)@`0`#@BH``K&$``)B(0`"I#X` +M`_`!``*S!``"L1@``VI@``-`"@`"L`$``K$"``*R$@`#Z#```VIH``*P@``` +M$D$``D`(``/P`0`#Z"```K`3``*Q`@`#@B8``^@P``-J<``"L"$``K$"``*R +M`0`"LP$``VIT``*P`0`"L0(``K(/``*S`0`#:G@``TI\``/_```"L`(``K$" +M``*S!``#:GP``^@```/````"L;\``\$@``-H8``#Z'```TID``*T@``"0`0` +M`VID``-*9``"M(```!'\``)`!``"M`,``F`$``*S$``#:F0``C"5``-*9``" +MM`@``D1```)G=``"I7X``@P\``/H```",$\``J#>``/P!``#2"H``TGH``/T +M```#\$(``T@N``-)[``"O!$``K\0``+)GP`"R(P``\$(``/!&0`"H-X``_`$ +M``-H*@`#:>@``_0```/P0@`#:"X``VGL``*[`0``VCX``^AP``.+Y0`#B[8` +M`J6+``/P`@`#]````\``K#_``*QOP`#P2`` +M`VA@``*P`0`#Z!```^@@``/H,``#:F````H[```2#```&@L``K`"``*@W@`# +M\$$``K`$``.!%@`"8`$``X(J``*Q@``"8B$``J0^``/P`0`"LP0``T`5``*Q +M"``"MO```D1&``)A%``#:F```K`!``*Q`@`"L@```K,"``-J:```$?L``K`3 +M``*Q`@`#@B8``^@P``-J<```$B@``K`A``*Q`@`"LP$``VIT``*P`0`"L0(` +M`K(/``*S`0`#:G@``TI\``/_```"L`$``K$"``*S!``#:GP``KP$``/>\``&'[``*[&``#V/```B\+```!V0`#0"4``K\0``+`#@``@=D``D]?``/P +M`P`"+6@``_0```',J0`"OP@``J`.``/P`0`#P?X``C,+``-*@``"H/X``_!! +M``-JA```8>\``K"```/H\``"H<```_!!``/!_@`"+1L``C'0```1[P`#Z!`` +M`K`C``.B(@`"LP$``VID``(PE0``8=D```'O```)^0`#0"8``K(0``/8```# +MW!```'G[``*P```#V`$``K`!``/<`0`"0BD``_!%``.O\``"H,X``_`"``/[ +M```#^P$``]">``/[```"I2X``_`!``/[```#U)\``_L!``*E+@`#\`$``_L! +M``,/_@`"#,L```'9``/_```#_P```L`.``"!V0``8>\``K"```/!_@`"H<`` +M`_!!``/H\``"+1L``C'0```1[P`"L`,``^@0``.B(@`"LP$``VID``(PE0`" +M,*(``T`E```!V0`"L0,``K((``*S$``"0S4``_`"``)")0`#\`(``J4!``', +ME@``8?D``%GO``/_```#W,```]BP``*P```#V`$``K`!``/<`0``>?L``T`- +M``*]0``#_P```DW5``*FW@`#\`,``B\K``/T!``#\`H``K!_``*Q_P`"LO\` +M`VA@``-H:P``$>4``K/```/HT``#8$P``B]F``*P`0`#Z!```^@@``/H,``# +M:F```C'0``/L```"O!$``J#>``/P!``#2,P``TC1``/T```#\$(``TCL``-( +M\0`"+5,``J#>``/P!``#:,P``VC1``/T```#\$(``VCL``-H\0`"H-X``_`$ +M``-(U``#2-D``_0```/P0@`#2/0``TCY``(M4P`"H-X``_`$``-HU``#:-D` +M`_0```/P0@`#:/0``VCY``*@W@`#\`,``TC<``/T```#\$$``TC\``*@_@`# +M\`0``L`,``+!'``#]````_!"``,`#``#`1P``J#>``/P`P`#:-P``_0```/P +M00`#:/P``^P```*@_@`#\`H``L`,``+!'``"PBP``L,\``+$3``"Q5P``L9L +M``+'?``#]````_!(``,`#``#`1P``P(L``,#/``#!$P``P5<``,&;``#!WP` +M`^P```*P?P`"L?\``K+_``-H8``#:&L```GE``/H```#H`,``]P```/8$``# +M0#$``K\#``*\`@`"IOP``_!!``/!!P`"H/P``_!!``/!!@`"H/X``_!!``/! +M!0`"H?X``_!!``/!!``#P1```\$@``/!,``#_@```]0>``-H:P`#^P```P_^ +M``(-=0`#0#4``K\#``/_```"IOP``_!!``/!!P`"H/P``_!!``/!!@`"H/X` +M`_!!``/!!0`"H?X``_!!``/!!``#P1```\$@``/!,``#_@```]0>``-H:P`# +M^P```P_^``(-C0`#0#@``T`]``/_```#:H0``VJ!``/L```#85```V%5``-A +M6@`#85\``TA4``/_```#Z#```Z,S``-H5``#:&L``TA5``/H```#Z'```K+O +M``*Q_P`"L+\``VA@``-H:P`#27P``K0$``)A%``#:7P``TD\``/_```"81X` +M`VD\``-*E``#03D``_\```/_```"IC<``_`'``*F)@`#\`4``J85``/P`P`" +MI00``_`!``-A.``#8/P``_\```/^```#_P```TAD``*T$``"0B0``J`D``/P +M0P`"+A4``_0```'-T``"L/\``K'_``*R_P`"L_\``VJ0``-)/``#P$X``D$4 +M``-I/``#2%0``K4_``)#4P`#:%0``^@```"!]```@AH``((;``-(9``#C.H` +M`/(5``),#``#\$@``T@T``/_```#@`@``Z`,``*@#@`#^`$``@WQ``""%0`# +M^`,``TA5``/H```#AP<``VA5``-)/0`"OQ```D1/``/P!``"O_\``BY[``*_ +M4``"+GL``Z=P``,'?@`"#@<``\`@``/`$``#P````VA@``-H:P`#H!```VA@ +M``-!4``#054``T%:``-!7P`#[````_@#``-)T``"N`@``DDX``*@F``"#F@` +M`TG4``*T$``"0S0``J`T``(.1``#2`@``K@(``)@"``#:`@``T@H``*X(``" +M8S@``T@M``-H*``#:>@``F=X``-H+0`#:>T``TC(``*X^P`"0B@``VC(``-( +MF``"N"```F`(``-(L0`#:)@``F1(``-HL0`#P0X``\$>``/!+@`#P3X``V,P +M``-C<``#8S0``V-T``-C.``#8W@``_0```'.:``#2`@``KCW``)`"``#:`@` +M`T@H``*XWP`"0S@``T@M``-H*``#:>@``D=X``-H+0`#:>T``TC(``*X!``" +M8B@``VC(``-(F``"N-\``D`(``-(L0`#:)@``D1(``-HL0`"L`,``\$0``/! +M(``#P3```V,P``-C<``#8S0``V-T``/!+@`#P3X``V,X``-C>``#27P``K3[ +M``)!%``#:7P``VAK``/L```#2,H``K`$``*Q0``#P2@``F@@``-HR@`#P8(` +M`VC*``)H(0`#:,H``\&"``-HR@`#[````_@#``,/_@`"#GP``^P```/H```# +MP!```\`@``.@$``#:&```VAK``/!#@`#Z!```^@@``/H,``#I!,``]@0``/< +M0``#_@```]0>``-H:P`#::0``^@```/^```#U!X``VAK``-II``"H-X``_`# +M``-"8``#]````_!!``-"7``#0ED``J#>``-II``#\`,``T)H``/T```#\$$` +M`T)D``-IH0`#_@```]0>``-H:P`#:&L``^P```/H```#P"```\`0``.@$``# +M:&```VAK``-"5@`#P0X``^@0``/H(``#Z#```Z03``/8$``#W$```]0>``*_ +M`@`"+GL``VAK``-II``#::(``^@```/_```#U!X``K\"``(N>P`#:&L``VFD +M``/H```#P"```\`0``/````#:&```VAK``/L```#:&L``K!_``*Q_P`"LO\` +M`VA@``*Q#``#Z````Z`#``/<```#V!```^@```/H$``#Z"```^@P``/^```# +MU!X``VAK``/[```#^P```XCC``/X`P`#_@```]0>``-H:P`#^P```PB.``(. +MX``#[````"'Z``/H4``#Z&```^AP``*Y"``"N,```]R!``/8D0`#0E```_@# +M``/_```#:D0``^@@``,!'@`#:D0``_X```/47P`#:&L``K09``/^```#U%\` +M`VAK``-)I0`#_P```_\```/!!``#P14``X+@``)B)@`#P3<``VFD``*Y"``# +M"9X``@\'``-II0`#[````J&^``/P"@`#T!X``P^^``/X`P`#``X``P$>``," +M+@`#`SX``]0>``,/_@`"#Q```_L```,,S@`"#PL``^P```*AO@`#\`H``]`> +M``,/O@`#^`,``L`.``+!'@`"PBX``L,^``/4'@`##_X``@\@``/[```##,X` +M`@\;``/L```#T)X``]`?``*\/P`"2(P``DF<``)*K``"2[P``D0,``)%'``" +M1BP``D<\``*\(``"P(0``Z````*C2``#\$$``L`,``+!E0`#H1```J-9``/P +M00`"P1P``L*F``.B(``"HVH``_!!``+"+``"P[<``Z,P``*C>P`#\$$``L,\ +M``/HT``#C.D``P2```+HC0`#!9$``NF=``,&H@`"ZJT``P>S``+KO0`#8FX` +M`DB,``))G``"2JP``DN\``/4G@`#0FX``F1%``)D1@`"9$<``@]-``!J/P`# +M^P```_L!``,/_@`"#RL``^P```/0G@`#T!\``KP_``)(C``"29P``DJL``)+ +MO``"1`P``D4<``)&+``"1SP``KP@``+`A``#H````J-(``/P00`"P`P``L&5 +M``.A$``"HUD``_!!``+!'``"PJ8``Z(@``*C:@`#\$$``L(L``+#MP`#HS`` +M`J-[``/P00`"PSP``KP'``*CW``#\$X``W!,``-03@`#_P```_\```/^```# +MU!X``VAK``/[```#<$X``U!,``/_```#_P```_0$``/P"``"O`@``J3<``/P +M`0`#:H0``KP)``*DW``#\`$``VJ```*P_P`"L;\``K+_``-H8``#^P```_L! +M``+-W@`"IM\``@]F``-*9``"M(```D`$``-J9``"L````K$```*R```"LP$` +M`VIT``*P,0`"L08``K($``*S```#:G@``TI\``/_```"L`$``K$!``*S```# +M:GP``TID``*T@``"0`0``K0#``)@!``#:F0``C"5``-*9``"M(```D`$``-J +M9```:C\``C-L``/L````6?8``&(^``/_```"H;X``_!"``#Q]``#[````J'. +M``/P00`#"[X``PS.``#9]@``XCX``TI```!:%@`#_P```_\```*@O@`#\$(` +M`Z````.B(``#T%X``&(*``!9\@`#H@(``D`)``)"*0`"I8X``@_U``,(C@`" +MH`T``_`+``!"!P`"Q$\``\%4``*@O@`#\$8``]1>``+$3P`#P50``]1>``+$ +M3P`#P50``J6N``(0!0`#"JX``J`M``/P"P``4@<``L9O``/!=@`"H+X``_!& +M``/47@`"QF\``\%V``/47@`"QF\``\%V``/47@`##,X``A`&``)KB@`"#\H` +M`^P```-"<```4A8``X_L``.(Z@`#JJ```J2N``/P!@`"H4@``_!!``+$2``" +MH6@``_!!``+&:``#BN```J9/``/P0P`#!$@``P`(``,!&``"IF\``_!#``,& +M:``#`B@``P,X``,*K@`"$!D``\%4``/!=@`#U%X``V)P``/L```#P6(``P9N +M``/<$``#V````]">``/_```#_P```DB%``)H@P`#P9@``\&H``/!N``#U)X` +M`_L```,&;@`"$"X``^P```/!$``#P2```\$P``*@W@`#\`@``VD(``-I#``# +M:1```VD4``-I&``#:1P``_0```/P1@`#:2```VDD``-I*``#:2P``VDP``-I +M-``#[````\$0``/!(``#P3```J#>``/P!P`#:,P``VC0``-HU``#:-@``VC< +M``/T```#\$4``VCL``-H\``#:/0``VCX``-H_``#[````TG4``/_```#@.4` +M`D$#``-)T@`#_P```D`*``-)P@`"I1```_`!``-)Q@`#[````K!_``*Q_P`" +MLO\``VA@``-H:P`#Z````^@0``/!+@`"LS```VFD``/H```"L3```^@@``/H +M,``#Z$```Z5#``/80``#W%```_X```/4'@`#:&L``_L```/[```"L!(``^@0 +M``/^```#U!X``VAK``,(C@`"$(8``^P```/0G@`#^P```_\```/_```#U)\` +M`_L!``,/_@`"$(P``^P```/X`0`"OP0``TID``/_```#_P```D$/``(0EP`# +M^`,``VAK``/_```#_P```_X```/L```#2F0``_\```*_"``"00\``J`?``(0 +MPP`#0!(``K40``*E)0`#\`0``K\"``)JKP`#]````=#```+$50`"I20``_`$ +M``*_`@`":[\``_0```'0P``"Q$4``J4D``/P!``"OQ```FJO``/T```!T,`` +M`K\0``)KOP``\?0``V`2``(PQ``#[````TIP``/_```#P<(``T`N``*P`0`# +M:J@``_\```/_```#_P```TJL``*T`@`#:JD``\````/`$0`#P"(``\`S``-* +MK0`"OY```J#/``/P!0`#P$0``K\/``)/3P`#]````_!!``/`]``"H-X``_`+ +M``-`&0`#_P```_\```)D0``"95$``F9B``)G4``K#```/<```#V!```J#^``/P!``#Z````^@0``/H(``#Z#```J#^ +M``/P1``"L%4``\$0``/!(``#P3```J#^``/P!``"M/\``\%4``/!9``#P70` +M`J#^``/P1``"M*H``\%4``/!9``#P70``KP'``*C_@`"$7D``$(-``/_```# +M_P```J".``(1/P`"H-X``_`#``-`0@`#]`0``_`!``-`1@``ZC\``&H.``*D +MW@`#\`0``\"(``/`F0`#P*H``\"[``)@"``"81D``F(J``)C.P`"9$@``F59 +M``)F:@`"9WL``&H_``/_```#_P```_X```/4'@`#:&L``_L```,,S@`#_@`` +M`]1>``-H:P`#^P```PS.``(1/P``0@T``&(.``/_```"H(X``A%D``-`2@`" +MI,X``_`$``/`B``#P)D``\"J``/`NP`"I-X``_`"``/!B@`#P9L``KH/``)* +MJ``#BZ8``FJK``*[\``"2[@``Z>V``)KMP`#]`0``_`#``/H8``#Z*```^BP +M``*T\``"8$H``\$0``/!(``#P3```VJ$``*@_@`#\`,``K3P``/T!``#\`$` +M`K0/``)@2P`#P1```\$@``/!,``#:H```^P```/^```#U!X``VAK``/[```# +M#,X``KD#``*@^0`#\$0``^A```/H4``#Z&```^AP``/^```#U%X``VAK``/[ +M```##,X``A&%``*P\``"L?\``J#Y``/P0@`#Z````^@0``/!(0`#P3$``VJ$ +M``-J@``#[````K!_``*Q_P`"LO\``VA@``-H:P``">4``K#```/<```#V!`` +M`K3_``/!9``#P70``KP'``/^```"M?\``]1>``-H:P`#^P```PS.``/^```" +MM>\``]1>``-H:P`#^P```PS.``(1HP`"M?\``VJ%``*P\``#P1```\$@``/! +M,``#:H```TIB``(QT``#Z/```C'<``-J8@``">4``K#```/<```#V!```K3_ +M``/!5``#P60``\%T``*\!P`#_@```]1>``-H:P`#^P```PS.``(1Q0`"L/\` +M`K&_``/!(``#:&```^P```-*9``"M(```D`$``-J9``#[````K`$``*@\``# +M\`,``C#]``/T!``#\`$``BUH``/H```#P````\$0``*R_0`#:&````H[```R +M#```&@L``K`*``*@W@`#\$$``K`,``.!%@`"8`$``K$6``*D/@`#\`$``K,$ +M``.&:@`"L@(``K0$``*C_@`#\`,``J#T``/P`0`"L@0``F(F``-J8``#C^$` +M`BY[``-H:P`#_P```_X```/L```#2F```_\```/H,``"8`X``VI@``-*9``" +MM/(``D`$``-J9``#[````&H_``-`!``#_P```K@$``)%X``"H%X``_!$``*@ +MW@`#\$(``T`D``/_````8?@``_\```/_```#W,```X_J``*@W@`#\`$``^CP +M``/8\``#C.0``\&R``-)/0`"H-X``_!!``-)00`#_P```D1N``/P`0`"O`<` +M`D`(``/P0P`"+PL``_0```/P00`"+QL``D1N``/P"0`#Z,```ZMB``.E8``" +M15X``_!#``(O"P`#]````_!!``(O&P`#[````TC```-(Q0`#Z!```^A0``*@ +MW@`#\`@``VC````",0``"C```_\```/_```#:,```_0```/P1@`#:,4``"(O +M```J+@`#_P```_\```-HQ0`#[````&H_``/_```#_P```%'Y``*@W@`#\`,` +M`%GQ``/T```#\$$``%GP``/_```#W*```]BP``-`!@`#_P```_\```)`Z``# +M\`,``J#>``/P00`#0"8``_\```/_```#24$``J`.``/P0P`"H-X``_!!``/! +M10`"14X``_!#``.,Y``#]````_!!``.,XP`#B9@``ZJ<``*@K@`#\`,``B\+ +M``/T```#\$$``B\;``-`!@`#_P```_\```-)00`"0(X``J`.``/P0P`"H-X` +M`_!!``/!10`#Z,```ZM"``)%3@`#\`<``Z5```)%7@`#\$,``B\+``/T```# +M\$$``B\;``/L```"H-X``_`#``-((0`#]````_!!``-()0`#_P```KL!``.B +M9@`#@68``Z$6``.'=@`"8"<``P`+``.G!@`#@@8``F8A``*@W@`#\`0``V@A +M``-JB0`#]````_!"``-H)0`#:HT``^P```./[0`"+GL``^@P``."X``#Z!`` +M`^@```-H,``#C^T``BY[``/H\``#27```^AP``*V`0`#P5$``\%```-H,0`" +MN/P``\%3``/!0@`"15@``TET``(R^``#:#$``\%1``/!0``",O@``V@Q``/! +M4P`#P4(``TF8``(R^``#:#$``\%1``/!0``",O@``V@Q``/!4P`#P4(``TF< +M``(R^``#:#$``\%1``/!0``",O@``V@Q``/!4P`#P4(``TCH``(R^``#:#$` +M`\%1``/!0``"MQ```C+X``-H,0`#P5,``\%"``(R^``#:#$``^P```-)/0`" +ML`@``D`$``/P!0`#2J0``_\```/_```"L@,``VJD``*P!``"000``J`0``/P +M1@`#2,```TC%``*Q`P`"M0,``VC```-HQ0`#[````_\```/_```#_P```^P` +M``-A2``#84$``V%&``-B?P`#P0X``^@0``/H(``#Z#```VCH``/H```#_P`` +M`VCH``-!2``#_P```^P```*P?P`"L?\``K+_``-H8``#:&L```GE``/H```# +MH`,``]P```/8$``"O`,``K#W``*@_@`#\$$``K!_``/!$``#P2```\$P``*T +M_P`#P50``\%D``/!=``#_@```]0>``-H:P`#H`$``Z$1``.B(0`#HS$``_L` +M``/^```#U%X``VAK``/[```##,X``A,A``*P_P`"O`@``\$0``/!(``#P3`` +M`VJ```*D_@`!TSL``J#\``/P`@`#:H0``^P```*P]P`#H0$``Z(1``.C(0`# +M:H0``^P```-)/0`#_P```KB```)D2``#:3T``K`!``*Q`@`"LC\``D(O``*S +M```#:F@``K````*Q```"L@```K,"``-J=``"L0$``K((``*S```#:G@``K`! +M``*R```#:GP``!'O``*PP``"0`\``Z`"``)@#@`"L0```Z(B``*S`0`#:F0` +M`K#_``*QOP`#P2```VA@``/_```#_P```C"5``*X?P`"1$@``VD]``/L```# +MZ````^@0``*R`P`"LP```V@P``/L```#Z````((_``(B^@``\C\``B+Z``(S +MJ``"+FX``TF8``*T]P`"010``VF8``/!+@`#Z#```V@P```"%P`#@>```^A` +M``*@`0`#\`$``\%.``(TL@`#2`@``KC^``)!&``#:`@``^@```""/P`")%H` +M`/(_``(D6@```A<``X'@``/_```"I!```_`"``/!3@`"-+(``C0"``-`+``" +MN$```KF```*[_P`#2J4``D@8``/P`0`"N\\``DD9``/P`@`"NC\``DNZ``)% +M6P`#:J4``_0```'";``#2=$``KA```)$>``#A$```TB<``.%X@`"8`0``F`% +M``-HG``#:*```VBT``-HN``#23@``X7A``/`50`"0B4``VDX``-J7``#H.$` +M`X'A``/H,``#@N@``X7E``(P*@`#0Z8``X#H``/_```"2(4``FB```/!F``# +MP:@``\&X``-CI@`#8^8``^@```/HT``",#L``\'>``(P.P`",&$``X3B``/! +MR0`#Z````X'A``.#Y``"PC$``P(N``+#/@`"1$D``_`!``.#Y0`#Z%```C`J +M``.`Z@`","H``XCE``.$X``"1$P``_`"``.(Y``"R(X``\&8``/!J``#P;@` +M`V,J``-C:@`",&$``X'B``.@X0`#HY8``X+D``+"+@`#Z%```C`J``.@XP`" +M,"H``C!A``.`Y0`"0*```\$0``/!(``#P3```V2H``-DZ``#@.```H((``)" +M`@`#@B0``)'C``/L```",&$``TEP``)$C@`#\$,``K7S``)")0`#:7```^AP +M``/!;@`#P5,``\%"``-H,0`"1)X``_`&``-)G``#I.$``F(D``/!4P`#P4(` +M`V@Q``)$C@`#\$T``TF<``.$Y0`#I><``D`$``)!%0`#I(@``X1```.%1@`" +M8`4``F$4``/H,``#P2X``V@P``-#I```(>,``K4/``/_```"0`4``F`$``/! +M$``#P2```\$P``-CI``#8^0``X&$``.@&``#@@8``F`"``/HT``",$\``\'> +M``(P3P`#2)```Z2Z``.EYP`"014``F$4``-HD``#:*@``VB4``-HK``#H*8` +M`\$0``/!(``#P3```V0D``-D9``#BK8``T@A``*X`P`"9F@``KD/``*[_@`" +M1FD``F9J``)'>P`#:"$``VJ)``-H)0`#:HT``T@(``-((0`"81X``V@(``-( +MR``"N$```KD_``)@"``#:,@``D`)``-HR``"95X``V@A``-JB0`#:"4``VJ- +M``)%6P`#:"$``VJ)``-H)0`#:HT``^P```-`+``"N$```KF```/HL``#2J4` +M`D@8``/P`0`"NS```DD9``/P`@`"NL```FNZ``)E6P`#:J4``TB<``-(H0`" +MO'L``D`,``)$3``#:)P``VBA``-(M``#2+D``D`,``)$3``#:+0``VBY``-( +MD``#A.(``Z7G``)!%0`"810``VB0``-HJ``#:)0``VBL``-).``#A>$``F(E +M``-I.``#:EP``Z#A``.!X0`#@^@``X+H``.%Y0`","H``^@```.!X0`#@^0` +M`L(Q``/H,``#Z%```C`J``.`Z@`","H``T@(``-((0`#J.T``D$8``-H"``# +MJ.L``D9H``/`/@`"15,``V@A``-JB0`#:"4``VJ-``/H0``"-+(``^P```-) +M=``#P6X``^AP``.(2``#P%X``X59``)")0`"8B@``VET``/!0@`#P5,``V@Q +M``/L```#2`@``KC^``)!&``#:`@``TA4``*\8``"8BP``VA4``-)=@`"O!`` +M`F2L``/!6P`#P6X``^AP``-H,0`",O@``TA4``*\GP`"0BP``VA4``/!2@`# +M:#$``C14``/L````\=X``/'=``/!_0`#P4``_0$``(5&```T=\``J#>``(5 +M/@``T>```LW>``/T!``"%.\``$'@``/_```#_P```J"H``(590`"H:@``_!( +M``-((0`#`XH``X,P``)G4``_\```)&8@`"9F@``VGE``-()0`#`Z@``X,P``)G``#8!$``\'?``-) +MF``#A>(``\!5``)!%0`#:9@``^@P``/!+@`#:#```T@(``/_```#A,@``F`$ +M``-H"``#^`,``^P```/_```#_P```&H_``/H````@AD``('<``*P`0`"L1$` +M`K(0``-J1``"L1```^@@``-J1``#0"0``J#>``/P`P`"NU,``_0```/P00`" +MNUL``-G\``)`#@`#\$L``T`0``*\!``#W,```X_J``*@W@`#\`$``^CP``/8 +M\``"O`D``\&Q``(O"P`#0"0``_\```*\`@`"0`P``_!+``-`$``"O`0``]S` +M``*_P``"H-X``_`!``*_@``#V/```XSD``/!L0`"+PL``TF8``.(Y0`"M?T` +M`\$^``*@W@`#\`$``X,P``/!+@`"8`@``D$5``-H,``#2`@``K1```"!]0`" +M8`0``V@(``-(`0`"L+\``D1```-H`0`#Z````K%```/H(``#Z#```VI$``-` +M+@`"L`$``^@0``/H(``#Z#```VI@``(M:``"L`$``K$"``*R!@`#Z#```VIH +M```200`"L!,``K$"``.")@`#Z#```VIP``*P(0`"L0(``K($``*S`0`#:G0` +M`K`!``*Q`@`"L@\``K,!``-J>``#2GP``_\```*P```"L0```K,$``-J?``" +MOQ@``C91``(V?@`"OP```C'5``-*<``#_P```_\```*RD``#:G```K\3``(V +M40`"-GX``T`D``/_```#_P```D`.``/P2P`#0!```KP$``/ +M``/P`0`#Z/```]CP``*\"0`#BQ```B\;``-`)``#_P```KP"``)`#``#\$L` +M`T`0``*\!``#W,```K_```*@W@`#\`$``K^```/8\``#C.0``XL0``(O&P`# +M2G```_\```/_```"LH```VIP``*_&``"-E$``C9^``*_```",=4``TIP``/_ +M```#_P```K*0``-J<``"OQ,``C91``(V?@`#0"0``_\```/_```"0`X``_!+ +M``-`$``"O`0``]S```./Z@`"H-X``_`!``/H\``#V/```KP)``/!L0`"+PL` +M`T`D``/_```"O`(``D`,``/P2P`#0!(``KP$``/``/P`0`" +MOX```]CP``.,Y``#P;D``B\+``*P`0`#Z!```^@@``/H,``#:F```TID``*T +M@``"0`0``VID```A]0`#_P```T@(``/_```#P00``V@(``/L```#Z````\`` +M``*QOP`#P2```VA@``*P`0`#Z!```^@@``/H,``#:F```TID``*T@``"0`0` +M`VID```*.P``$@P``!H+``*P`@`"H-X``_!!``*P!``#@18``F`!``."*@`" +ML80``F(A``*D/@`#\`$``K,$``/!'P`#:F```TID``*T@```$?P``D`$``*T +M`P`"8`0``K,0``-J9``",)4``K\(``)/\``#\`$``/(9``/L`````AD``_\` +M``/_```"I0X``_`!``/L```#Z````((9``-`$@`#_P```_\```)$C@`"H$X` +M`_`!``#Q]``"OT```J#>``/P`P`":J\``_0```/P00`":[\``V`2``(PQ``` +M>=P``_\```-`*``"H/X``_!!``/L```"H-X``_`%``+`#@`#\$$``L$>``/T +M```#\$,``L(N``/P00`"PSX``V`H``#QW``#[`````(4```*0```:C\``_\` +M``)``0`"H`X``_`!``/L```#2)P``TBA``-*2@`"H-X``_!#``-(M``#2+D` +M`TI.``*\$``"8BX``F9N``)HC``":9P``J#>``/P!0`#:)P``VBA``-J2@`# +M]````_!#``-HM``#:+D``VI.``/HP```X=L``K`0``*Q```"LA```K,```-B +M=``"L(```]@!``*P```#W`$``K`2``*Q`@`"LA(``K,"``-B=``"L!```K$2 +M``*R$``"LQ(``V)P``-B1``"L0(``K,"``-B3``#8D@``T`L``/_```#_P`` +M`^@@``/H,``#8"P``^@```/H$```8=L``V`8``-@'``#P8P``\&<``/!K``# +MP;P``C=S``(U=P``8=L``_\```*P```#W````K!```./P@`"P`\``]@```-` +M&``"H-X``_!!``-`'``#0"T``_\```/4'@`#U%\``K\```/<\``"O\```XC" +M``+/^``#V/```\0```/$$0`#Q"(``\0S``*U#P`"1&4``Z5F``*@W@`#\$,` +M`K4/``)$=0`#I78``Z9"``.$2@`#IU(``X5:``/$1``#Q&8``L`!``+`!``" +MPB,``L(F``/$%0`#Q#<``]0>``-"=0`#0G(``K\0``*A!``#\$8``J4)``/P +M!``#P8P``\&0``/!K``#P;```J,)``/P0@`#P:P``\&P``-B<@`#0D8``J$F +M``/P1@`"I2D``_`$``/!C``#P9(``\&L``/!L@`"HRD``_!"``/!K``#P;(` +M`V)&``-"3@`"H14``_!&``*E&0`#\`0``\&,``/!D0`#P:P``\&Q``*C&0`# +M\$(``\&L``/!L0`#8DX``T)*``*A-P`#\$8``J4Y``/P!``#P8P``\&3``/! +MK``#P;,``J,Y``/P0@`#P:P``\&S``-B2@`#8G0``_L!``*Q$``"S,X``.'; +M``*@P0`!UN(``T)P``-"10`#_P```L@"``.H@``"RD8``ZJ@``-"3``#0DD` +M`_\```+)`@`#J9```LM&``.KL``"-W,``^P```-(G``#2*$``J#>``/P0@`# +M2+0``TBY``/!&``#P5H``XF6``.+M@`"81D``F5;``*@W@`#\`0``VB<``-H +5H0`#]````_!"``-HM``#:+D``^P` +` +end diff --git a/sys/contrib/dev/drm2/radeonkmsfw/TURKS_me.bin.uu b/sys/contrib/dev/drm2/radeonkmsfw/TURKS_me.bin.uu new file mode 100644 index 00000000000..2eebbc63ef4 --- /dev/null +++ b/sys/contrib/dev/drm2/radeonkmsfw/TURKS_me.bin.uu @@ -0,0 +1,126 @@ +begin 644 TURKS_me.bin +M?$"``*````#,@`!-@````-1``'\``!ET``*@>X`62?@````````(```71_&X`. +M@``!>'\;@`^```%\?QN`#(```8!_&X`-@``!A'\;@!&```&(?QN`$(```8T4 +MI``(FX``&12D``B```&='F0`_YN``!44I``(@``!G1YD`/^;@``1%*0`"(`` +M`9T>9`#_FX``#12D``B```&='F0`_YN```D4I``(@``!G1YD`/^;@``%%*0` +M"(```9T>9`#_%*0`"!YD`/\J:``\FH#^<13L``A\0T``?$.``'Q#P`"6P``' +MS```3<]!(6G/@2%JS\$A:X````#,`2%L@````,_U``#,``!9A``$G2IH`#R: +M@``$R"@`%X````#40`!_EH#_JWX"0`"$``*6P`X``LP``$&```&KS,$P2I0` +M``#(/``@1*``!E0``$`:H`@F>@````````(```B'`$@@`@``"+\@4`!&```(VR!0` +M$H```CW,P:*D@``"1ASH`#^```)N?,&`"QS0`#\I*``&*2P`%GZN@`?('``3 +MFH``/004`"Z`````S,&BI,`2"`!\04``?0S`!\`2``@56``#%5P`#'Q"``!] +MT<`&$B``%'X>0`=^3H`'SH&BI(````#-@:'^R!0`$000(1B50```R!0`$=11 +M``"`````S,&BI,@4`!($$"$&E4```,@4`!+440``@````,S!HJ3,P:*D!!`` +M`5P``)R#0`,<@X`##(/``M4W0`('^W@"='N`50S``` +M8L_Z``":0```R"0`)\@H`",ZJ``"FH#__\@H`"/`,``!R"@`))J```#(*``D +MSP``6U#8``@4W``8P#X0`"'<@`!]_<`'S8$A@,W!(8'`'@`@42``"!4D`!A^ +M?D`'$S``'GYR0`=]74`'S@$A@LY!(8/-02&$?$"``*````#,@`!-A``$G1QT +M``$<>``"FT```\@,`"J$``-"R!0`+)N```,$$``0A``#4LS!HF#-`:!@A``# +M#P08"8"$``2=S```9,@\`"Z$``,=R!``*L@<`"\=W``!R"0`*)7```G(-``Q +MR#@`,,@\`"Y3=``@?[>`)T>X!53,``!BS_H``)I```#()``HR"@`(SJH``*: +M@/__R"@`(\@H`"6:@```R"@`)8```M#`,``"!9C``!#<``@4X``8S=D``,@< +M`"+()``B'=P/_\W9``%^8D`'SED``M@840/8&%$$B````-@840<;^`#PP#8( +M`)>```/`,`"`B````,`J``3/02%\SP$A?7`!YG`__[('``E@````7Q`@`!\0,``?$$``!DH`#"6@``( +MR"@`)\@D`"B:0```R"0`)YI```#()``HS``#X'Q!0`!\08``%1P`'\S``,?- +M``#(E<```\`<@`#-P2`0X8,```5<(`#,``!-@````-P?00!\0,``?$$``'Q! +M0`!\08``S,``R`!#(#``I +M4-``"!!4``*```05?16`(,`>`"#(#``J4-``"`A4!``15``"?5&`(,W``&+4 +M6@``?$"``*````#,@`!-?$#``!S0``,1*``!E0``"@:H!!^>@```?$&``(`` +M!"U\0<``@``$,WQ!P`"```0Y?$'``'Q!@`!\0<``%-0`$`54H`"`````S94` +M`,`B``0%F*``?:&`!\P9``"```0IR!@`!,`B``3-@276(B`EU\PA``"```0I +MR!@`!,V!(6W-P2%N@``$*<@8``-\0,``@````,Q,`^`@`/!)8`#P26``\$E@`/!)8`#P26``\$E@`/!)8`#P26``\$E@`/!)8` +M#P26``\$E@`/!)8`#P26``\$E@`/!)8`#P26``\$E@`/!)8`#P26``\$E@`/ +.!)8`#P26``\$E@`/!)8` +` +end diff --git a/sys/contrib/dev/drm2/radeonkmsfw/TURKS_pfp.bin.uu b/sys/contrib/dev/drm2/radeonkmsfw/TURKS_pfp.bin.uu new file mode 100644 index 00000000000..6d902c16c41 --- /dev/null +++ b/sys/contrib/dev/drm2/radeonkmsfw/TURKS_pfp.bin.uu @@ -0,0 +1,103 @@ +begin 644 TURKS_pfp.bin +M?$"``*`````$*``!@````.`#``#,@`!`U$``0'Q`@`"@````!"@``1!,``&8 +MP``%')```LQ``"F```-?S$``*ID```4`````S$``*X```U_,0``LS$``+8`` +M`U_,0``N'(P``LR``$"8P``$S$``0(```U_,``!2@``#7\P``%2`````S$`# +M_<@0`"[(#``M41``('S0P"=\Q0`@510`(,T``$/-0`!#T,``0\R``$#,``!` +MS$``0'Q`@`"@````!"@``<@0`"S(#``K41``('S0P"=\Q0`@510`(,T``$+- +M0`!"T0``0GQ`P`#(&``#R!P``\@@``.5@/_"R"0``Y7`_\#(*``=FH``!,P` +M`%78``/`S```0,@H`!;`+@`$R#``#1JH`"=^\L`'SL``0,P``$#-@`!`S<`` +M0,X``$"6@``#SD``0-"``%S,@`!`S```0,S``$!\0(``H`````0H``'($``L +MR`P`*U$0`"!\T,`G?,4`(%44`"#-``!"S4``0M%``$)\0,``R!@``\@<``/( +M(``#R"0``Y6`_YG(*``#E<#_E\@L`_U^+P`1EP```W[BP`%\`L`&SL.BGLP` +M`&S-``!MS4``;<@P`!V;```$S```5=@``\#,``!`R"P`%L`R``7(-``-&NP` +M)W\W``?/``!`T$``0,V``$#-P`!`S@``0,Y``$"6P``#SH``0-"``%S,@`!` +MS```0,S``$!\0(``H`````0H``'(&``5'9@``7Q"0`"5@`+.?$*``,@<`"#` +M-\``?$#``'Q!``!\M(`&P#8``QJX`>B7@``'T$`#X(0``V+,``!_R#@#X)N` +M``#(.`/@F<```,@<`"!\M(`'$-0``GUE0`#-0`!#SH``0\T``$/,@`!`SD`` +M0,Z``$#,P`!`X#H``)>`_U3-``!`?$#``(```*)\00``'(P``IC```G($``< +MF0``!,@<``:$``-ES```4L@8`!6```".'9@``L@0`!Z9`/_\R!P`"(0``V7, +M``!4R!@`%8```(X=F``"'(P``L@0`!V8P``.R!0`29D```3('``'A``#9@%5``&(!```7T5``.```".?9&`!IE`__C('``'S<`` +M0,P``$"```#+S```;L`.@(#,``!G!-"`@,P``&C-``/QS0`#\LT``_/-``/T +MS0`#]LT``_?-``/X@```!5`/_WR!0`19E```3(&``* +MS8``0,P``$"```%$V``(0'Q`P`#($`!'F0#__`````#,@`!`S,``0-1``$!\ +M0(``H`````0H``'($`!&V``'0,T``$"$``%;S0``0,@,`$.4P``*R!0`2(`` +M`50$$``#R!``1M@`!\#-``!`A``!6\T``$#(%`!(G4```,P``&K(*``6'J@` +M`9J```/`*0`!B````,`L`5;.@`! +ME,``",@4`!R9```*R!P`"(0``V7,``!4@``!='`)WU8P`-\W,`@5-`` +M((```97,@`!`?$&``,R``$"```&2S8``0,`9___,@`!`S8.BGGQ`P`!\00`` +M?$%``,S#H?K-`Z'YS4.BG9P/]C`````-```_J`````?$#``,R``$"`````U$``0'Q`P`!\ +M00``?$.``'Q#P``$'``"SX``0L_``$+-P`!"!!P`",S``$+-``!"S<``0@0< +M``$$(``!?`)``,@4``/(&``#49@`('U90"?(*``#R"P``\@P``/(-``#4NP` +M('ZN@"=3=``@?S<`)W\K0"%^=D`@5J@`/UT`G?65`(%58`"#,``!;S78``,VV``#((`!!F@```,@@`$&` +M```!?$"``'Q`P`!\00``S,`#_LT``__,P`!"S0``0A44`!\9&`#P)UP``7UV +M``:9@``%?5Y`!LP``$*```-?S```3168``$5+``(F8``,1[L``&6```$%3`` +M#(```U_,``!"!!0`",U``$(?,``!("@``00X``@$/``!R!0``\@8``/('``# +MR"```WU=0`U]H<`-?5U`!Q80`!\5G``??1T`!GT70`9^DH`&FT``$@NX``2; +MP/_R"_P``<@,`_Z:@``)R!`#_YL``0;,``!-!!0`",S``$+-``!"@``"0\U` +M`$*6P`#_S```38```U_,``!.FL```\P``$W,``!.EX#]F>.#``"`````W`,! +M_Y9```3,``!.@``#7\P``$+2``!"R`@``\@,``/($``#R!0``\@8``/('``# +MR"0``\@H``,5_``?%K``'W_SP`84\``??_/`!A5P`!]_\\`&?8B``9?```U] +MS,`!?E$``7Z50`%\D(`,?-3`#)K```-\CT`&)+0``9M``-;,``!-@``#7\P` +M`$[(#`/^R!`#_\S``$*```)OS0``0GQ`P`!\00``?$+``'Q#``#`.P`??$-` +M`'^W@`;`/A``EX#]9GT]``=_/P`'&10`.\P``%N50``3R!0`0#%8``*5@/__ +MR!0`0,P``&,A'(``S,$AA +MA``#8LP``%W((``??$#``,`V_P#($``AP#`__WSU0`9]48`&?8&`"IF```A\ +M\X`&XX,``,^@`$^$``-BS```7H````#40`!_@``#7\P``%Z$``-B?$#``!3< +M``B5P``9'-P`$'Q!``"9P``$4%0`((```QW)'0``?14`)\D>``!\0@``?$)` +M`'Q!@`!]Y<`&?>*`$9J`_-Y!K``%FL````KL``$<`(@+L`","_0`G`8P`'P'Y`"`"*0`H`:X`*0%[`"H!H``K +M`9``+P&I`#(!S0`T`S\`-0%_`#D!]@`\`Q$`/P&[`$$"D@!"`KD`0P+#`$0" +MT`!*`MX`50,S`%8#.@!@`(P`80"Q`&(`V0!C`,,`9`##`&4`PP!F`,,`9P## +M`&@`Y@!I`.\`:@$_`&L!#0!L`0T`;0$-`&X!#0!O`0T`<`$2`',`^P!T`/L` +M=0%F`'L#3`````4````%````!0````4````%````!0````4````%````!0`` +M``4````%````!0````4````%````!0````4````%````!0````4````%```` +9!0````4````%````!0````4````%````!0`` +` +end diff --git a/sys/contrib/dev/drm2/radeonkmsfw/VERDE_ce.bin.uu b/sys/contrib/dev/drm2/radeonkmsfw/VERDE_ce.bin.uu new file mode 100644 index 00000000000..89b637f20e9 --- /dev/null +++ b/sys/contrib/dev/drm2/radeonkmsfw/VERDE_ce.bin.uu @@ -0,0 +1,194 @@ +begin 644 VERDE_ce.bin +M?$"``8@```#40`!_?$"``8@```#$(``+Q@P`!93``"0D4``/,10``I5```3, +M0``DS$``)8`````Q%``#E4`!.\Q``";,0``G@````,0@``O,(``%?$$``5!4 +M`"!\08`!!B0``@8U``P&-@`-`4H`$1\0@`!?$)``94` +M``>&@```@``!4(```5"```%0@``!4(```%(15``0?A8`"LU``!'480``E8#_ +MLL0Y(4!\0(`!B````!%4`!!29``@?B8`&LU``!'48@``E8#_J<0@``Z:`/__ +M?$"``8@```!\0,`!%-``'C$4``*50`#K&-0`,!C0`>@8_``T),P`#P3H`&A\ +M08`!?$'``93``!*&@```@``` +M`!J```!_4=P`('V=@!J90``#Q:(``(```'_)H@``@```?\6A``"50``%!9@` +M`<6E``!29``@?B8`&@4H`(-\08`!?$'``94```>&@```@``!4(```5"```%0 +M@``!4(```)/8```1SAD``)5```0%F``!5B``(,X9``"7P/]QQ#DA0'Q`@`&( +M````4=P`('V=@!K8```1SAH``)5```0%F``$5B``(,X:``";P/^]?$"``8@` +M``#8``/VV``#]=@``_38``/SV``#\M@``_'8``/PV``#[]@``^[,0`!_S$`` +M?\Q``']\0,`!S,```=1``'^`````?$#``5!0`"",``#+?-#`&L0@``O$U@`` +M?$.``<0D`!.:```)V&0#],9K`_&:@/__QFL#[@:H``'.I`/NS60#Z]@D`_29 +M0``)WX,``,^@``Z,``#/U$``?YH`_SS&

?16`#IF```_80``IR!``'7U1@!)]C<`/F<#__=@``"E0X``! +M?A9`#II```1]8H`2TH``(X````#0```C@````-B``"G$/`!_S```(-A``"C$ +M#``V$``'8````#,```@V$``*,0, +M`!R8P/__V```*-```![0```?V$``'7Q!``&`````?$#``7Q!``$5&``?S0`` +M$5$4`""9@``#U$T``(````!]34`:&1P`,=16``#$(``.E<#^R,0@``Z:`/__ +M@````'Q`P`%\08`!%-``'C$0``(DU`#_E0#^O\V4`P"`````Q"``$WQ`P`'$ +MTP,`S```$`(0`]@"'`/D`B`$"`(D!%P!]`3P`?@%$`'\!+``B`*X`BP$B +M`````@````(````"`````@````(````"`````@````(````"`````@````(` +M```"`````@````(````"`````@````(````"`````@````(````"`````@`` +M``(````"`````@````(````"`````@````(````"`````@````(````"```` +M`@````(````"`````@````(````"`````@````(````"`````@````(````" +M`````@````(````"`````@````(````"`````@````(````"`````@````(` +M```"`````@````(````"`````@````(````"`````@````(````"`````@`` +:``(````"`````@````(````"`````@````(` +` +end diff --git a/sys/contrib/dev/drm2/radeonkmsfw/VERDE_mc.bin.uu b/sys/contrib/dev/drm2/radeonkmsfw/VERDE_mc.bin.uu new file mode 100644 index 00000000000..2797c58ea17 --- /dev/null +++ b/sys/contrib/dev/drm2/radeonkmsfw/VERDE_mc.bin.uu @@ -0,0 +1,694 @@ +begin 644 VERDE_mc.bin +M``/HX``"O@$```('``/_```#_P```J`.``'`&0`#Z````^@0``/H(``#Z#`` +M`V`@``-A2``",;@``TEL``-("@`#KPH``K0$``)O]```^BT``K0/``)/%``` +M^@H``ZB,``#"`0`#0"0``K1```*U#P`#2J8``D1!``/P`@`"294``VJF``-* +M40`#0"0``K@"``*Y_@`"1)0``DB!``.H@``"9$@``VI1``-`)@`"OX```KP$ +M``))^0`#\$(``_0```'`00`"OX```TA4``*X#P`"N?```D$8``)"*0`#:%0` +M`TMI``/`C``"9$P``VMI``)$2``#:VD``F$?``)B+``#:%0``BP%``-+3``# +M_P```V%$``-+'``#0"H``K3\``-A0``"0S0``F$>``-K'``"2JX``_!&``-) +MU0`"O!```DS'``/P0@`"O"T``C)S``-`)``"M$```K7P``-*I@`"1$$``_`" +M``)IE0`#:J8``T````/_```#^`$``\1```/$40`"QD4``@````-`$``#_P`` +M`_\```/H(``#Z#```V`0``"1Y0``D@T``_@#``(POP`",;(``T@(``*TGP`" +M0`0``V@(``-(P``#2,8``K3\``*U#P`"010``DF4``)")0`"2J4``D,U``)+ +MM0`#:,```VC&``-(```#2`4``KL_``*\OP`"O^\``)(E``"*)@``@BH``+HH +M``"J+``"01L``KO[``)`#``#:````KS^``)%7P`"1WP``V@%``-($``#2=T` +M`KOQ``*\`@``BBD``*HK``)!&P`"81P``V@0``)%6P`"95P``VG=``-`#0`# +M_P```_\```.A3```B?\``K,"``)#-``#HS```)G^``*S!``"0S0``Z,R``"9 +M_0`#0"D``T````*\(```N<<``D(L``/P!P`"L````((Q``(Z,P`#Z````('E +M``/T```!PEP``T````*\(```\C$``D,\``/P!0`".C,``^@```"!Y0`#]``` +M`<)<``-((0`"L\$``D=S``-H(0`#:HD``T@E``*SP0`"1W,``V@E``-JC0`# +M2"D``TEP``-`$@`"OP<``D`/``.````#``X``Z^(``-)U@`"O!```DS+``/P +M00`"P`\``X0&``.%5@`#I58``F54``)D0``#2>@``V@I``-H+0`#P00``\$5 +M``-IZ``#:>P``TK"``*_P``"2(\``VK"``-JQ@`#Z````^C0``(NE@`#:.0` +M`\'>``(NE@`#:00``^A```*]```",@(``KT!``(R`@`#0"P``_\```*T`@`" +M1`0``*()``-)U@`"M1```D1;``.D1@``H@L``J!.``/P`P`",[<``_0```'! +M!0`#]````=)]``*\```",CT``T`.``#R,@`#Z/```Z"V``-)U0``^@0``K$/ +M``)"%P`"I2```_`!``#Z,@`"0QL``J4C``/P`0``\@0``T````-`+@`"MA`` +M`K0,```J,@`"0B0``Z(B``)#-``";",``D9H``/P`P`"H%X``_`!``/HP``` +MX@,``T`(``*T````>@0``J`D``/P00`"L@@``J#^``/P00`"L@0``K0_``)" +M)```DAH``TJ4``/_```#88```T````/_```"M$```D0$``/P`P`#Z````((Q +M``(BX``#0````_\```*T0``"1!0``_`"``#R,0`"(N```T````/_```"M$`` +M`F$0``)$%``#\`$``BSW``-*E``#_P```V&$``-````#_P```K0"``)$)``# +M\`,``^@```"",0`"(PP``T````/_```"M`(``D0T``/P`@``\C$``B,,``-* +ME``#_P```V&(``(QL@`#0````_\```/_```"1`X``_`#``/H````@C$``B0; +M``(QL@`#0````_\```/_```"1!X``_`"``#R,0`")!L``BTN``-*E``#_P`` +M`V&,``-````#_P```K0"``)$0``#\`L``^@```"",0`")@<``T`L``/_```" +MM"```D1```/P`P`#Z````((Q``(Q*0`#2I0``_\```-AD``#0````_\```*T +M!``"1$```_`#``/H````@C$``B?3``-*E``#_P```V&4``-````#_P```K00 +M``)$0``#\`P``^@```"",0`"*'H```'_``/_```#_P```J0.``/P!```\@`` +M`BAZ``/H````@@```TJ4``/_```#89@``T````/_```"M"```D1```/P`P`# +MZ````((Q``(J"@`#2I0``_\```-AG````@,``_\```*T`0`"1`0``_`$``/H +M````@C$``\'^``(JO````@,``_\```*T`@`"1`0``_`$``/H````@C$``^CP +M``(JO``#0````_\```/_```"1"X``_`#``/H````@C$``C?1``-````#_P`` +M`K00``)$)``#\`,``^@```"",0`"./4``_@#``-*E``#_P```V%@``-````# +M_P```K0"``)$00`#\`H``/(Q``(F!P`#0"P``_\```*T(``"1$```_`"``#R +M,0`",2D``TJ4``/_```#860``T````/_```"M`0``D1!``/P`@``\C$``B?3 +M``-*E``#_P```V%H``-````#_P```K00``)$00`#\`L``/(Q``(H>@```?\` +M`_\```/_```"I`X``_`$``#R```"*'H``^@```""```#2I0``_\```-A;``# +M0````_\```*T(``"1$$``_`"``#R,0`"*@H``TJ4``/_```#87````(#``/_ +M```"M`0``D0$``/P`P``\C$``\'^``(JO````@,``_\```*T"``"1`0``_`# +M``#R,0`#Z/```BJ\``-````#_P```_\```)$/@`#\`(``/(Q``(WT0`#0``` +M`_\```*T$``"1#0``_`"``#R,0`"./4``TJ4``/_```#870``_@#``-````# +M_P```K0(``)@`0`"1`0``_`,``-("``#_P```K1```)@!``#:`@``TF8``*T +M_0`"010``VF8``/H,``#P2X``V@P``(Q1``#Z````^@0``/H,``"L@,``V@P +M``-````#_P```K6```)A$``"114``_`!``(W,``#2I0``_\```-A>``"O`$` +M`C(]```",@`#_P```_\```*A#@`#\`(``^@```""!P`"+2X``T@```-(!0`` +M"B8```(J```2)0``*BP``#HH``/_```#:````V@%``-($``#2=T```HI```J +M*P`#_P```V@0``-IW0`#27$``K(!``/H,``#P08``\$7``-H,``",40``^@` +M``/H$``"L@,``K,```-H,````@L``_\```/_```"H`X``@*!``/T```!P``` +M``'E``-`$@`#0!4```H-``*@#@`#\`(``_0```'"G``"P1X``(H-``)KN@`" +M95L``L9N``/P00`#!FX``V`5``*R#P`"1$(``J44``/P!P`#Z*```^BP``-@ +M$@`#Z````('E``/T```!P&L``T`L``/_```"M"```D!```/P`@`#]````<*H +M``/H````@C$``C$I``#R,0`",2D``^@```"",0`",%X``/(Q``(P7@`#23T` +M`TC```*V"``"1F0``_`"``*U,``"8B4``VC```-HQ``#2QT``T`H``*\$``" +MOP0``DS```/P!0`"9W\``VL=``(Q1``"9WX``VL=``-`(``#_P```_\```+" +M+@`#\$0``L,^``/P0@`"LO\``K/_``-@(``",;(``TE(``-`*@`"M#\``K6` +M``*V0``"1F@``_!%``)#-``"8S4``VE(``)#-``#:4@``^@```/H$``#Z"`` +M`^@P``-DI``#9.0``_0```'````#0`0``&HQ``/H@``#Z)```^B@``/HL``" +MH-X``_`#``-DJ@`#]````_!!``-DZ@``8>D``J#>``/P`P`#Z$```_0```/P +M00`#A.H``]A```/>@``_@#``/4G@`#^P```P_^``("]P``(<,``J#> +M``/P`P``8<(``_0```/P00``8<$``K\(``/<0``#V,```_@#``/4G@`#^P`` +M`P_^``(#!@`#^`,``^P````",@``:C$``_\```*@#@`#\`$``^P```-).``" +MH-X``_!!``-).``"N/X``D(H``-I.``#2=```K0$``)A%``"L@$``K,2``*@ +MW@`#\$$``K,1``-H,``#27$``KCO``*Y!``#P08``F%Y``)#.``#:#```T@) +M``*X_0`"1$@``V@)``(M"``#2:```K01``/!)``"L0\``VF@``-*1``"M/X` +M`F$>``-J1``"010``VI$``*@W@`#\`,``T)A``/T```#\$$``T)=``/_```# +M_P```^A@``-II0`#:&L``KH,``*XP``#W($``]BA``*P^``"L0<``^@@``/H +M,``#_@```]0?``-H:P`#Z/```B/)``(CL@`"(]X``J'^``/P00`#8GH``T)X +M``/_```#_P```J"```'#9P`"H)$``<-G``*@H@`!PV<``J"S``'#9P`"S_X` +M`KP,``*A_``#\$(``_0```'#40``^``-">``# +M_P```_\```*@@``!PWT``J"1``'#?0`"H*(``<-]``*@LP`!PWT``L_^``*\ +M#``"H?P``_!"``/T```!PVH``&'.``#YQ0`#_P```J7/``'#C0`"I?P``<.( +M``/H\``#Z,```_0```'#D``##,\``ZS```/H\``#]````<.0``,/_``#K_`` +M`^C```(CR0`#P?P``B.R``-).``"H-X``_!!``-).``#_P```F(N``-I.``# +M2=```K3[``)!%``"L@$``K,2``*@W@`#\$$``K,1``-H,``#27$``KCO``*Y +M^P`#P08``D%Y``)#.``#:#```T@)``*X_0`"1$@``V@)``*P&``"(_T``BTN +M``/L```#P0\``\$?``/!+P`#P3\``J#>``/P!0`#9+```V2T``-DN``#]``` +M`_!#``-D\``#9/0``V3X``-(R``"M/,``K4$``)`!``"8`4``VC(``)`!``# +M:,@``^P```/!#P`#P1\``\$O``/!/P`"H-X``_`$``-DK``#9+P``_0```/P +M0@`#9.P``V3\``-(R``"M/,``K4$``)`!``"8`4``VC(``)`!``#:,@``^P` +M``*P$``"(_T``TH<``(D#0`#P8P``TH@``(D#0`#C,8``FB,``-*)``")`T` +M`\&<``-**``")`T``XS&``)IG``#2BP``B0-``/!K``#2C```B0-``.,Q@`" +M:JP``THT``(D#0`#P;P``THX``(D#0`#C,8``FN\``/L```#:&L``KD(``*X +MP``#W(```]B0``/H$``#Z"```^@P``/^```#U!X``VAK``/!CP`"OQ```BT$ +M``/!^``#[````^C```.'/``#IW8``X8L``.F:``#A1P``Z5:``.$#``#I$P` +M`FS'``)LQ@`";,4``FS$``/L```#0"0``&HQ``/_```#H`8``J$.``/P`0`` +M@?D``TFA``-)I``"NT```KJ_``)%I0`"H-X``_`#``)#HP`#]````_!!``)C +MLP`#::0``VFA``(R8``#Z````^@@``/H,``"H-X``_`#```)^P`#]````_!! +M```)^@`"N,```VAK``/8```#W(```]0>``*_"``"+00``X#@``"!]@``@?4` +M`^@```"!]```@?,``/'R``-(5``"H-X``_`#``.$Z``#]````_!!``.$Z@`" +M8B0``VA4``-)=0`#B.8``F!H``/!%P`#P2X``\$^``*@W@`#\`$``X,P``-H +M,``#2%0``J#>``/P`P`#A.@``_0```/P00`#A.H``\!$``)")``#:%0``&(+ +M``*_`@`"+00``J#.``/P`@`#]````@2#``(NJ``#Z'```X3F``)&2P`"H&0` +M`_!*``-)T@`#Z&```XJR``.JJ@`"H:X``_`"``,!K@`")?<``_0```'%(@`" +MOP@``D_Y``.O]``#B[(``ZN\``(E.P`#]````<4B``-`#``#_P```K\(``)/ +M\``#\$(``_0```'$E``"OP```KL!``(E.P```@0``_\```/_```"H`X``_!" +M``/T```!Q2(``/'T``#Q\P`"H-X``_`#``!Y^``#]````_!!``!Y]P`#C>(` +M`]S0``/8\``#B>8``FF>``#QXP``0?D``^C0``./X``#P:@``XSM``#AYP`` +MXC```BX1``*W`0``0?D``^B@``#1XP`"H'X``_`!``.H@``##]\``\'9``/! +MJ``#C.T``.'G``#B,``"+A$``BY2``/!!``#P28``V)P``*_`0`#0!$``XC@ +M``*@_@`#\`0``D1(``/P`@`#]````@4(``*W`0``0?D``_\```*@?@`#\`$` +M`ZB```/HT``#C^```\&H``.,[0``X><``.(P``(N$0`"+E(``T)P``.,ZP`# +MC.H``^CP``+(0``#J(```"H)``+*8@`#JJ```J9>``($Y0``:C$``/()``/! +M6``#P7H``V)Q``/T```"!)\``J%>``/P#@``:C$``\&1``/!LP`"H-X``_`# +M``-A+@`#]````_!!``-A,@`"P($``Z@```+"HP`#JB```X50``"J"0`#@>`` +M`P"$``/P)``"H80``_`!``+$00`#!$X``\%4``,"I@`#\"0``J&F``/P`0`" +MQF$``P9N``/!=@`#U%X``F`"``($]P``:C$``K\"``(M!``#27$``X'D``/! +M!@`#HFP``J`N``(%(@`"87$``\$N``/!/@`"H-X``_`!``.#,``#AF```_!' +M``-H,``#C^T``BT$``./[0`"+00``X_M``(M!``#C^T``BT$``-)=0`#P2X` +M`\$&``/!%P`#P3X``J#>``/P`0`#@S```V@P```!Y0`#_P```_\```*E#@`# +M\$@``T`1``*@W@`#\`,``F9N``/T```#\$$``F=^``-@$0`#Z````('E``/L +M```"H/X``<5'``*P#P`"L0\``K(/``*S#P`"H-X``_`#``-C*``#]````_!! +M``-C:``",40``TI```!!\@``2?8``%'U``*\,P`"1`P``D4<``)D10`"1BP` +M`D<\``)F9P`"9$8``J%.``/P`0`"M`$``J".``/P!0`"I$D``_!#``*D2P`# +M\$$``/'T``"A]@`"O,P``D0,``)%'``"1BP``D<\``)D10`"9$8``F1'``*A +M3@`#\`$``K0!``*@C@`#\`4``J1*``/P0P`"I$L``_!!``#Q\P``H?4``^A` +M``"A\@```?0``!'S``*Q#P`"LP4``J7^``(%G0`"H/X``_!!``/H\``"H`X` +M`_`!``+/_@`"L1```J`N``/P`0`"S_$``K.0``*F\P`#\$(``/'E``/L```" +M0P(``_!#``(EW0`#]````<5'``*T$0`##_0``J#>``/P`P``^?$``_0$``/P +M`0``^?```T,I``*@W@`#\$$``T-I``*_=P`");P``^P```-(Y0`"H-X``_!! +M``-)!0`"N!$``J`.``/P`0`"Q$@``DE!``*DDP`#\`$``/'T``*@+@`#\`$` +M`L58``))40`"I),``_`!``#Q\P`"H-X``_`#``-HY0`#]````_!!``-I!0`" +M0`(``J`.``(%1P`"L0(``B7W``/L```"H?X``_!!``/L```#H?8``X_V``.@ +M]@`"H0X``_`'``*\"0`"H$P``_!!``*\#P`#P4P``\%<``,`#@`"H1X``_`' +M``*\"0`"H&P``_!!``*\#P`#P6P``\%\``,!'@`"H-X``_`#``-C*0`#]``` +M`_!!``-C:0`"8@$``@7"``/L```#0RD``J#>``/P00`#0VD``J`.``/P!0`" +MM`\``DS^``/P`0`"M`D``\%4``*@+@`#\`8``K8/``.L]@`"3,X``_`!``*V +M"0`#P78``J#>``/P`P`#8RD``_0```/P00`#8VD``^P```-(Y0`"H-X``_!! +M``-)!0`"L!$``L1```+%4``"H-X``_`#``-HY0`#]````_!!``-I!0`#`1X` +M`@7[``/L````:C$``^@```(N@@``\>\``^@```"![@`#0`P``T`M``*X$``" +MN0@``DH8``.JI@`"2TD``ZNT``*S!``";ZL``_!!``*S`@`"3ZL``_`!``*S +M!@`#0"T```H$``*P0``"0$```_!!``/H,``"01X``_`!``/H,```F>T``BT( +M``*X$``"N?X``J#>``/P"0`#2"$``KH/``)&:@`"1WD``F9H``-H(0`#:HD` +M`_0```/P1P`#2"4``KH/``)&:@`"1WD``F9H``-H)0`#:HT``C`H``*_`P`" +M+_0``K]D``(M!```:C$``_\```/_```"H-X``_`#``*[```#]````_!!``*[ +M0```V>```\$.``*Q$0`"LA```VI$``*Q$``#Z"```VI$``*P_P`"L?\``K+] +M``*S_P`#:&```TI@``*_]P`"L1,``D`/``)@#@`#:F```#'^```Y_0`"O`@` +M`K_^``.&:@`"8B8``J1^``/P`0`"LP0``D`/``)@#``#:F```X_A``(M!``# +M:&L``_\```/^````8B\``%HN``/_```#V,$``]RQ``./XP`"+00``]`?``., +M[P`#C^0``F_^``)``0`"0B,``D`"``),P``#^P$``]`?``,/_@`"!G@``J#> +M``/P`P`#2"$``_0```/P00`#2"4``\#,``(&I``#C.8``L9L``/P00`"QWX` +M`J#>``/P!``#:"$``VJ)``/T```#\$(``V@E``-JC0`"I7X``_`"``/T```! +MQDH``T`1``*P"```\>4``J#>``/P`P`"9F```_0```/P00`"9W```V`1``/T +M```"!]$``X#A``.!!@`"81X``X+G``-J1``#@08``^@@``-J1``","@``T`L +M``!:'0`"M`0``^CP``)$0``#\`$``K\!``*DO@`#\`$``K\$``(O]```\AD` +M`/(7``(R6@`","@```HM```1_@``&?T``K`"``*@W@`#\$$``K`$``."*@`" +MI#X``_`!``*S!``#@18``F`!``*Q,P`#:F```T`*``*P`0`"L0(``K(```*S +M`0`"H"L``_`"``/H(``#P3L``VIH``/!#@`"L@D``D`)``/P`0`#Z"```K`3 +M``*Q`@`#@B8``^@P``-J<``#0"T``&($```2&@`"OT```D]/``/P`P`"H,X` +M`_`!``*R"``"L"$``K$"``*S`0`#:G0```H=``*P`0`#_P```J0>``/P!0`" +ML00``K(```*S`@`#]`0``_`#``*Q`@`"L@\``K,!``-J>``#2GP``_\```*P +M`0`"L0(``K,$``-J?````>X``_\```/_```"P`X``('N``(O[P```AD``T`* +M``-`#0`"H`X``_`%``*P`0`"0`4``X`&``/T!``#\`,``K`$``)`"0`#@`(` +M`K$#``)H`0`#2F0``K2````1X``"0`0``F`(``.B(@`"LP$``VID``(NO0`" +M+LH```'O``/_```#_P```J`.``/P3@`#0"P``KP$``/``/P +M`0`#Z/```]CP``*\"0`"OS\``DL?``(MBP`#Z/```/GO```![@`#_P```_\` +M``+`#@``@>X```GM``-`+@`#_P```J`!``('2@`"L`$``D$(``/P!0```C(` +M`_\```/_```"H`X``@=*``-*=```>AH``_\```/_```#P2\``VIT``(O[P`` +M`AD``T`*``-`#0`"H`X``_`%``*P!``"0`4``X`"``/T!``#\`(``K`0``)` +M"0`"L4,``F@!``-*9``"M(```!'@``)`!``"8`@``Z(B``/!/@`#:F0``BZ] +M``(NR@``0@,``K`"``*@W@`#\$$``X`"``)`"``#\`(``_0```''>@`#0`X` +M`K`0``/_```"00D``J`0``('>@```AD``_\```/_```"P`X``((9``*Q`@`" +MI`$``@<```!!X````<,``_\```/_```#V(```]P```*_"0`"L````]P!``*P +MP``#V`$``BZT```![@`#_P```_\```+`#@``@>X``B_O``-`"@`"L$```_\` +M``)`"0`#H`(``K$#``)H`0`#2F0``K2````1X``"0`0``F`(``.B(@`"LP$` +M`VID``(NO0`"+LH``T`E``!"`P`"L"```D`%``/P2``"L`(``J#>``/P00`# +M@`(``D`(``/P`@`#]````<>_``-`+@`#_P```K`(``)!"``"H!```@>_``*P +M@``"00@``_`%```",@`#_P```_\```*@#@`"![\```(7``/_```#_P```^@0 +M``"*%P`"I`X``@``/P`P`"NV(``_0```/P00`"NV@``_\```/_ +M````V>P``C"E``/H```"+H(``KL"``#:,``#Z````K%@``/H(``#Z#```VF@ +M``/H```"L1```^@@``/H,``#:D0``C`H``*_`@`"+_0``K#_``*Q_P`"LOT` +M`K/_``-H8```"BT``!'^```9_0`"L`H``J#>``/P00`"L`P``X$6``)@`0`# +M@BH``K$,``)B(0`"I#X``_`!``*S!``"L18``VI@``./X0`"+00``VAK``/_ +M```#_@```C`>``-*8```,?X``#G]``*T]@`"M0,``X9J``)B)@`"I'X``_`! +M``*W!``"8S<``P$5``)`!``#:F```T`*``*P`0`"L0(``K(2``/H,``#:F@` +M`K"```*R"0`"0`@``_`!``/H(``"L!,``K$"``.")@`#Z#```VIP``*P(0`" +ML0(``K(!``*S`0`#:G0``K`!``*Q`@`"L@\``K,!``-J>``#2GP``_\```*P +M`@`"L0(``K,$``-J?``",EH``^AP``-*9``"M(```D`$``/H(``#Z#```VID +M``-*9``"M(```!'L``)`!``"M`,``F`$``*S$``#:F0``BZ]``-*9``"M`@` +M`D1```)G=``"I7X``@AX``/H```"+H(``J#>``/P`P`#2"(``_0```/P00`# +M2"8``_\```.,Y@`"RJP``_!!``++O@`"H-X``_`$``-H(@`#:HH``_0```/P +M0@`#:"8``VJ.``/H<``"IKX``_`"``/T```!R#T``T`2``#QY0`"OP0``J#> +M``/P`P`":J\``_0```/P00`":[\``V`2``/T```!R'@``C`>``/L````:C$` +M`/'O``/H````@@8``T`(``-`+0`"N$```KD(``)*"``#JJH``DM)``.KM``" +MLP0``F^K``/P00`"LP(``D^K``/P`0`"LP8``T`M```*!``"L$```D!```/P +M00`#Z#```D$>``/P`0`#Z#```)H%```AY0`#Z%```_\```"B#```J>4``&HQ +M``/_```#_P```J#>``/P`P``6<(``_0```/P0P``6<$``_\```/_````V>`` +M`T`L``!:'0`"M`0``^CP``)$0``#\`$``K\!``)+O@`"H+X``_`#``(O)0`# +M]```````D`$``)@"``#HB(``K,!``-J9``" +M+KT``B[*```![P`#_P```_\```*@#@`#\$X``T`L``*\!``#W,```K_```*@ +MW@`#\`$``K^```/8\``#C.0``K\_``)+'P`"+8L``^CP``#Y[P```@8``_\` +M``/_```"P`X``((&``!:'0``>A<``K@(``)*O@`"H*X``AH``_\```/_```#P2\``VIT``(O[P```A@``T`*``-`#0`"H`X` +M`_`%``*P"``"0`4``X````/T!``#\`,``K`@``)`"0`#H````K%#``)H`0`# +M2F0``K2````1X``"0`0``F`(``.B(@`"LP$``VID``(NO0`"+LH``$(#``*P +M`0`"H-X``_!!``.``@`"0`@``_`"``/T```!R;4``T`*``/_```"L$```D$( +M``*@$``"";4```(8``/_```#_P```L`.``""&``"L0(``J0!``()#````>`` +M`$'#``/_```#_P```]@```/<@``"OP@``K#```/8`0`"L````]P!``(NM``` +M`@8``_\```/_```"P`X``((&``(O[P`#0`H``K"```/_```"0`D``Z`$``*Q +M`P`":`$``TID``*T@```$>```D`$``)@"``#HB(``K,!``*Q```#:F0``BZ] +M``(NR@``0@,``K`!``*@W@`#\$$``X`"``)`"``#\`(``_0```')]P`#0"X` +M`_\```*P"``"00@``J`0``()]P`"L(```D$(``/P!0```C(``_\```/_```" +MH`X``@GW```"%P`#_P```_\```/H$```BA<``J0.``()3P``8<,``%G@``/_ +M```#W,```]BP``*_"``"L,```]@!``*P```#W`$``C)@``-H:P``$=8``K/` +M``/HT``#8G0``BVS``(P'@`#[````&HQ``/_```#_P```J#>``/P`P`"NU,` +M`_0```/P00`"NUL``_\```/_````V>P``T`0``*['P`#2"D``J#>``/P00`# +M2"T``D`+``.O`@`#C/8``FS/``./]@`#2>@``J-,``/P!``#!$P``P5?``/T +M```#\$(``P3$``,%]0`#P00``\$5``*@W@`#\`0``V@I``-IZ``#]````_!" +M``-H+0`#:>P``^@```(NE@`#Z````K%```/H(``#Z#```VI$``(P*``"OP(` +M`B\E```*+0``$?X``!G]``*P`@`"H-X``_!!``*P!``#@18``F`!``."*@`" +ML80``F(A``*D/@`#\`$``K,$``*Q&``#:F```T`*``*P`0`"L0(``K(*``/H +M,``#:F@``K"```*R"``"0`@``_`!``/H(``"L!,``K$"``.")@`#Z#```VIP +M``*P(0`"L0(``K(!``*S`0`#:G0``K`!``*Q`@`"L@\``K,!``-J>``#2GP` +M`_\```*P`@`"L0(``K,$``-J?``",EH``^AP``(O[P`#2F0``K2````1[``" +M0`0``K0#``)@!``"LQ```VID``(NO0`#2F0``K0(``)$0``"9W0``J5^``(* +MN0`#Z````BZ6``*@W@`#\`0``T@J``-)Z``#]````_!"``-(+@`#2>P``KP1 +M``*_$``"R9\``LB,``/P00`#Z(```\$(``/!&0`"H-X``_`$``-H*@`#:>@` +M`_0```/P0@`#:"X``VGL``-*P``"OP<``DW>``/P`0`"OS@``J&.``/P0P`" +M8`\``VK```-JQ``"NP$``-HP``/H<``"3_```_`#``*[_P`"H(L``_`"``/T +M```!RG$``T`2``#QY0`"OR```J#>``/P`P`":J\``_0```/P00`":[\``V`2 +M``(P*``"+^\``^P```!J,0`#Z````(',``*@W@`#\`,``%G"``/T```#\$$` +M`%G!``*P?P`"H?X``_!!``)+L```V>```C):``(P*```"BT``!'^```9_0`" +ML`(``J#>``/P00`"L`0``X$6``)@`0`#@BH``K&```)B(0`"I#X``_`!``*S +M!``#0!4``K$(``*V\``"1$8``F$4``-J8``"L`$``K$"``*R```"LP(``VIH +M``*R"``"L!,``K$"``.")@`#Z#```VIP```2&@`"L"$``K$"``*S`0`#:G0` +M`K`!``*Q`@`"L@\``K,!``-J>``#2GP``_\```*P`0`"L0(``K,$``-J?``" +MO`0``]S```!YX``"O`@``KL8``/8\``"+8L```',``-`)0`"OQ```L`.``"! +MS``"3U\``_`#``(KQ``#]````````G#``-`)@`" +MLA```]@```/<$``"OP@``K#```/8`0`"L````]P!``)"*0`#\$4``Z_P``*@ +MS@`#\`(``_L```/[`0`#T)X``_L```*E+@`#\`$``_L```/4GP`#^P$``J4N +M``/P`0`#^P$``P_^``(+.```````_\```/``/L```"O!$` +M`J#>``/P!``#2,P``TC1``/T```#\$(``TCL``-(\0`"*Z\``J#>``/P!``# +M:,P``VC1``/T```#\$(``VCL``-H\0`"H-X``_`$``-(U``#2-D``_0```/P +M0@`#2/0``TCY``(KKP`"H-X``_`$``-HU``#:-D``_0```/P0@`#:/0``VCY +M``*@W@`#\`,``TC<``/T```#\$$``TC\``*@_@`#\`0``L`,``+!'``#]``` +M`_!"``,`#``#`1P``J#>``/P`P`#:-P``_0```/P00`#:/P``^P```*@_@`# +M\`H``L`,``+!'``"PBP``L,\``+$3``"Q5P``L9L``+'?``#]````_!(``,` +M#``#`1P``P(L``,#/``#!$P``P5<``,&;``#!WP``^P```*P?P`"L?\``K+_ +M``*S_P`#:&```VAK```)U@`#Z````Z`#``/<```#V!```T`Q``*_`P`"O`(` +M`J;\``/P00`#P0<``J#\``/P00`#P08``J#^``/P00`#P04``J'^``/P00`# +MP00``\$0``/!(``#P3```_X```/4'@`#:&L``_L```,/_@`""](``T`U``*_ +M`P`#_P```J;\``/P00`#P0<``J#\``/P00`#P08``J#^``/P00`#P04``J'^ +M``/P00`#P00``\$0``/!(``#P3```_X```/4'@`#:&L``_L```,/_@`""^H` +M`T`X``-`/0`#_P```VJ$``-J@0`#[````TA5``/_```"M\```VA5``-H:P`# +M2`H``K]_``)H^``"LN\``Z"!``*_PP`"0`\``T`E``*S_P`"L?\``D]>``/P +M`@`"N?,``D(I``*\`@`"3%P``_`!``*S^P`#:&```TA```./\@`"O/L``D$< +M``)A'P`#:$```VAK``-)?``"M`0``F$4``-I?``#2I0``_\```-A?``#:&L` +M`C%$``/_```#_@```_\```-!20`#_P```_\```-(9``#B$0``ZB```/H\``# +MW/```KQ```+,R``#V,```]0>``(R9@`#84D``TAD``*T$``"1B0``_`"``/T +M```!S)D``TAD``-(-@`"M$```K4$``)$0``"15@``F1%``',60`"M#P``D1` +M``/P0P`".KT``_0```',*P`#0!0``_\```/_```"PSX``_!!``,#/@`#8!0` +M`K#_``*Q_P`"LO\``K/_``-JD``#^`,``TAD``.,Z@``\@@``DP,``',=0`# +M2#0``KP$``),P``"#&0``.((``-)/0`"OQ```D1/``/P!``"O_\``BT$``*_ +M4``"+00``K\(``(M!``#]``````#:"T``VGM``-(R``"N/L``D(H``-HR``#2)@``K@@``)@"``# +M2+$``VB8``)D2``#:+$``\$.``/!'@`#P2X``\$^``-C,``#8W```V,T``-C +M=``#8S@``V-X``/T```!S/```T@(``*X]P`"0`@``V@(``-(*``"N-\``D,X +M``-(+0`#:"@``VGH``)'>``#:"T``VGM``-(R``"N`0``F(H``-HR``#2)@` +M`KC?``)`"``#2+$``VB8``)$2``#:+$``K`#``/!$``#P2```\$P``-C,``# +M8W```V,T``-C=``#8S@``V-X``-)?``"M/L``D$4``-I?``#:&L``_0```', +M*P`#2,H``K`$``*Q0``#P2@``F@@``-HR@`#P8(``VC*``)H(0`#:,H``\&" +M``-HR@`#[````_@#``,/_@`"#04``^P```(R8``#:&L``\$.``/H$``#Z"`` +M`^@P``.D$P`#V!```]Q```/^```#U!X``VAK``-II``#Z````_X```/4'@`# +M:&L``VFD``*@W@`#\`,``T)@``/T```#\$$``T)<``-"60`"H-X``VFD``/P +M`P`#0F@``_0```/P00`#0F0``VFA``/^```#U!X``VAK``-H:P`#[````C)@ +M``-H:P`#0E8``\$.``/H$``#Z"```^@P``.D$P`#V!```]Q```/4'@`"OP(` +M`BT$``-H:P`#::0``VFB``/H```#_P```]0>``*_`@`"+00``VAK``-II``" +M,F```VAK``/L```#:&L``C)@``*Q#``#Z````Z`#``/<```#V!```^@```/H +M$``#Z"```^@P``/^```#U!X``VAK``/[```#^P```XCC``/X`P`#_@```]0> +M``-H:P`#^P```PB.``(-6@`#[````"'K``/H4``#Z&```^AP``*Y"``"N,`` +M`]R!``/8D0`#0E```_@#``/_```#:D0``^@@``,!'@`#:D0``_X```/47P`# +M:&L``\&/``*_$``"+00``K09``/^```#U%\``VAK``*_$``"+00``\'X``-) +MI0`#_P```_\```/!!``#P14``X+@``)B)@`#P3<``VFD``*Y"``#"9X``@V' +M``-II0`#[````J&^``/P#@`#T!X``P^^``*Z/P`#``X``D`*``,!'@`"01H` +M`P(N``)"*@`#`SX``D,Z``/4'@`##_X``@V0``/[```##,X``@V+``/L```" +MH;X``_`.``/0'@`##[X``KH_``+`#@`"0`H``L$>``)!&@`"PBX``D(J``+# +M/@`"0SH``]0>``,/_@`"#:0``_L```,,S@`"#9\``^P```/0G@`#T!\``KP_ +M``)(C``"29P``DJL``)+O``"1`P``D4<``)&+``"1SP``KP@``+`A``#H``` +M`J-(``/P00`"P`P``L&5``.A$``"HUD``_!!``+!'``"PJ8``Z(@``*C:@`# +M\$$``L(L``+#MP`#HS```J-[``/P00`"PSP``KP'``*CW``#\$X``W)T``-2 +M=@`#_P```_\```/^```#U!X``VAK``/[```#<``&(P``/_```"H;X``_!"``#QY0`#[````J'.``/P00`#"[X` +M`PS.``#9YP``XC```TI```!:"0`#_P```_\```*@O@`#\$(``Z````.B(``# +MT%X``&'\``!9XP`#H@(``D`)``)"*0`"I8X``@X\``,(C@`"H`T``_`+``!! +M^0`"Q$\``\%4``*@O@`#\$8``]1>``+$3P`#P50``]1>``+$3P`#P50``J6N +M``(.3``#"JX``J`M``/P"P``4?D``L9O``/!=@`"H+X``_!&``/47@`"QF\` +M`\%V``/47@`"QF\``\%V``/47@`##,X``@Y-``)KB@`"#A$``^P```-"<``` +M4@D``X_L``.(Z@`#JJ```J2N``/P!@`"H4@``_!!``+$2``"H6@``_!!``+& +M:``#BN```J9/``/P0P`#!$@``P`(``,!&``"IF\``_!#``,&:``#`B@``P,X +M``,*K@`"#F```\%4``/!=@`#U%X``V)P``/L```#P6(``P9N``/<$``#V``` +M`]">``/_```#_P```DB%``)H@P`#P9@``\&H``/!N``#U)X``_L```,&;@`" +M#G4``^P```/!$``#P2```\$P``*@W@`#\`@``VD(``-I#``#:1```VD4``-I +M&``#:1P``_0```/P1@`#:2```VDD``-I*``#:2P``VDP``-I-``#[````\$0 +M``/!(``#P3```J#>``/P!P`#:,P``VC0``-HU``#:-@``VC<``/T```#\$4` +M`VCL``-H\``#:/0``VCX``-H_``#[````TG4``/_```#@.4``D$#``-)T@`# +M_P```D`*``-)P@`"I1```_`!``-)Q@`#[````]">``/[```#_P```_\```/4 +MGP`#^P$``P_^``(.M``#[````_@!``*_!``#2F0``_\```/_```"00\``@Z_ +M``/X`P`#:&L``_\```/_```#_@```^P```-*9``#_P```K\(``)!#P`"H!\` +M`@[K``-`$@`"M1```J4E``/P!``"OP(``FJO``/T```!SN@``L15``*E)``# +M\`0``K\"``)KOP`#]````<[H``+$10`"I20``_`$``*_$``":J\``_0```'. +MZ``"OQ```FN_``#QY0`#8!(``B[L``/L```#2G```_\```/!P@`#0"X``K`! +M``-JJ``#_P```_\```/_```#2JP``K0"``-JJ0`#P````\`1``/`(@`#P#,` +M`TJM``*_D``"H,\``_`%``/`1``"OP\``D]/``/T```#\$$``\#T``*@W@`# +M\`L``T`9``/_```#_P```F1```)E40`"9F(``F=S``-@&0`":J\``_0```/P +M20`#0!T``_\```/_```"9$```F51``)F8@`"9W,``V`=``)KOP`#8"X``^P` +M``+$1@`#:JD``_\```/_```#_P```TJL``/L```",F```VAK```)U@`"L,`` +M`]P```/8$``"H/X``_`$``/H```#Z!```^@@``/H,``"H/X``_!$``*P50`# +MP1```\$@``/!,``"H/X``_`$``*T_P`#P50``\%D``/!=``"H/X``_!$``*T +MJ@`#P50``\%D``/!=``"O`<``J/^``(/G@``0?\``_\```/_```"H(X``@]D +M``*@W@`#\`,``T!"``/T!``#\`$``T!&``#J,0``:@```J3>``/P!``#P(@` +M`\"9``/`J@`#P+L``F`(``)A&0`"8BH``F,[``)D2``"95D``F9J``)G>P`` +M:C$``_\```/_```#_@```]0>``-H:P`#^P```PS.``/^```#U%X``VAK``/[ +M```##,X``@]D``!!_P``8@```_\```*@C@`"#XD``T!*``*DS@`#\`0``\"( +M``/`F0`#P*H``\"[``*DW@`#\`(``\&*``/!FP`"N@\``DJH``.+I@`":JL` +M`KOP``)+N``#I[8``FNW``/T!``#\`,``^A@``/HH``#Z+```K3P``)@2@`# +MP1```\$@``/!,``#:H0``J#^``/P`P`"M/```_0$``/P`0`"M`\``F!+``/! +M$``#P2```\$P``-J@``#[````_X```/4'@`#:&L``_L```,,S@`"N0,``J#Y +M``/P1``#Z$```^A0``/H8``#Z'```_X```/47@`#:&L``_L```,,S@`"#ZH` +M`K#P``*Q_P`"H/D``_!"``/H```#Z!```\$A``/!,0`#:H0``VJ```/L```" +M,F```VAK```)U@`"L,```]P```/8$``"M/\``\%D``/!=``"O`<``_X```*U +M_P`#U%X``VAK``/[```##,X``_X```*U[P`#U%X``VAK``/[```##,X``@_% +M``*U_P`#:H4``K#P``/!$``#P2```\$P``-J@``#2F(``B_O``/H\``"+_L` +M`VIB```)U@`"L,```]P```/8$``"M/\``\%4``/!9``#P70``KP'``/^```# +MU%X``VAK``/[```##,X``@_G``(R6@`#[````TID``*T@``"0`0``VID``/L +M```"L`0``J#P``/P`P`"+R4``_0$``/P`0`"*\0``^@```/````#P1```K+] +M``*S_P`#:&````HM```Q_@``&?T``K`*``*@W@`#\$$``K`,``.!%@`"8`$` +M`K$6``*D/@`#\`$``K,$``.&:@`"L@(``K0$``*C_@`#\`,``J#T``/P`0`" +ML@0``F(F``-J8``#C^$``BT$``-H:P`#_P```_X```/L```#2F```_\```/H +M,``"8`X``VI@``-*9``"M/(``D`$``-J9``#[````K`!``/H$``#Z"```^@P +M``-J8``#[````&HQ``-`!``#_P```K@$``)%X``"H%X``_!$``*@W@`#\$(` +M`T`D``/_````8>D``_\```/_```#W,```X_J``*@W@`#\`$``^CP``/8\``# +MC.0``\&R``-)/0`"H-X``_!!``-)00`#_P```D1N``/P`0`"O`<``D`(``/P +M0P`"+8L``_0```/P00`"+9\``D1N``/P"0`#Z,```ZMB``.E8``"15X``_!# +M``(MBP`#]````_!!``(MGP`#[````&HQ``-`*``#0"$``KA```)(@``#\`,` +M`D9N``/P`0`#[````%'#``*@W@`#\`,``%G"``/T```#\$$``%G!``/_```# +MW*```]BP``-`!@`#_P```_\```)`Z``#\`,``J#>``/P00`#0"8``_\```/_ +M```#24$``J`.``/P0P`"H-X``_!!``/!10`"14X``_!#``.,Y``#]````_!! +M``.,XP`#B9@``ZJ<``*@K@`#\`,``BV+``/T```#\$$``BV?``-`!@`#_P`` +M`_\```-)00`"0(X``J`.``/P0P`"H-X``_!!``/!10`#Z,```ZM"``)%3@`# +M\`<``Z5```)%7@`#\$,``BV+``/T```#\$$``BV?``/L```"H-X``_`#``-( +M(0`#]````_!!``-()0`#_P```KL!``.B9@`#@68``Z$6``.'=@`"8"<``P`+ +M``.G!@`#@@8``F8A``*@W@`#\`0``V@A``-JB0`#]````_!"``-H)0`#:HT` +M`^P```./[0`"+00``^@P``."X``#Z!```^@```-H,``#C^T``BT$``-)G``# +M2=4``T`J``*T$``"1$<``K4"``)%6@`#A50``D1%``.D0``"MO<``D(F``)B +M)``#:9P``^CP``-)<``#Z'```K8!``/!40`#P4```V@Q``*X_``#P5,``\%" +M``)%6``#270``C%$``-H,0`#P5$``\%```(Q1``#:#$``\%3``/!0@`#29@` +M`C%$``-H,0`#P5$``\%```(Q1``#:#$``\%3``/!0@`#29P``C%$``-H,0`# +MP5$``\%```(Q1``#:#$``\%3``/!0@`#2.@``C%$``-H,0`#P5$``\%```*W +M$``",40``V@Q``/!4P`#P4(``C%$``-H,0`#[````KP#``-)/0`#2,```J#> +M``/P00`#2,0``K8$``)&9``#\`0``X;*``/!?``"8B8``F$7``*@W@`#\`,` +M`VC```/T```#\$$``VC$``-`*@`"M@@``X?&``)&:``#\`$``F,W``*@W@`# +M\`,``VC```/T```#\$$``VC$``/L```","X``TD]``-`*``"OP@``KP$``)/ +M#P`"3$P``F#\``/P00`#[````K0!``(R`@`",0D``J'^``/P"0`",<,``C`H +M``-`*``"O`@``_\```)"+``#\$(``_0```/P0@`#Z$```C("``/L```#_P`` +M`_\```/_```#[````V%(``-A00`#848``V)_``/!#@`#Z!```^@@``/H,``# +M:.@``^@```/_```#:.@``T%(``/_```#[````C)@``-H:P``"=8``^@```.@ +M`P`#W````]@0``*\`P`"L/<``J#^``/P00`"L'\``\$0``/!(``#P3```K3_ +M``/!5``#P60``\%T``/^```#U!X``VAK``.@`0`#H1$``Z(A``.C,0`#^P`` +M`_X```/47@`#:&L``_L```,,S@`"$6H``K#_``*\"``#P1```\$@``/!,``# +M:H```J3^``'1A``"H/P``_`"``-JA``#[````K#W``.A`0`#HA$``Z,A``-J +MA``#[````TD]``/_```"N(```F1(``-I/0`"L`$``K$"``*R/P`"0B\``K,` +M``-J:``"L````K$```*R```"LP(``VIT``*Q`0`"L@@``K,```-J>``"L`$` +M`K(```-J?```$>```K#```)`#P`#H`(``F`.``*Q```#HB(``K,!``-J9``" +M,EH``_\```/_```"+KT``KA_``)$2``#:3T``^P```/H```#Z!```K(#``*S +M```#:#```^P```-`+``#_P```_\```/H(``#Z#```V`L``/H```#Z!```V`8 +M``-@'``#[````&HQ``*P`0`#Z!```^@@``/H,``#:F```K\!``(O]``"OQ`` +M`BT$``*\%``#2F```K_W``*Q$P`"0`\``F`.``-J8```,?X``#G]``*\B``" +MM?X``X9J``)B)@`"I'X``_`!``*S!``"0`4``F`,``-J8``#2I8``T`I``*\ +M!P`#_P```D1,``.%1``"Q5X``\'%``/H0``#Z&```^AP``-JD0`"M/\``K5_ +M``*V_P`"M_\``VAA``-H:P`#_P```_X```,)G``#\$4``\"9``,*K@`#\$(` +M`\"J``,+O@`#:I(``X````.@```#:F```K\0``(M!``#[````T`J``-'+``" +MH-X``_!!``-';``"MG\``X5,``)`!@`"29X``_`!``)@!0`#P1```\$@``/! +M,``"H-X``_!$``-G+``#9S```_0```/P0@`#9VP``V=P``-'-``"H-X``_!! +M``-'=``"MM\``X5(``)`!@`"8`4``\$0``/!(``#P3```J#>``/P1``#9S0` +M`V``#2,```J#>``/P00`#2,0``K8_``)")@`" +MML```J!.``/P`0`#Z&```F(F``*@W@`#\`,``VC```/T```#\$$``VC$``/L +M```#29@``K@/``*Y]0`#Z#```\$N``*@S@`#\`(``F`(``)!&0`#:#```T@( +M``*X0``"H,X``_`"``)@"``#:`@``^P```-A4``#854``V%:``-A7P`"+`4` +M`T%0``-!50`#05H``T%?``/_```#_P```^P```*P_P`"L;\``K+_``*S_P`# +M:&```^P```*P?P`"L?\``K+_``*S_P`#:&```^P```+$3@`#\$H``L5>``/P +M2``"QFX``_!&``+'?@`#\$0``K3_``*U_P`"MO\``K?_``/L```"H```^A```*@`0`#\`$``\%.``(T7@`#2`@``KC^``)! +M&``#:`@``KP```(T"0`"-#H``T`!``/H````@C$``D1.``/P`0`")!L``T`! +M``/_````\C$``D5>``/P`0`")!L```(*``.!X``#_P```J00``/P`@`#P4X` +M`C1>``*\`0`"-`D``C0Z``*_`P`"-N0``C-%``(NJ``"OQ```D_[``'2X``` +M>``(N@@`"+J@``X3B``/!R0`#Z````K$#``*R"@`"LRT``D1)``/P +M`0`"LR\``^A0``(N<0`#@.H``BYQ``*X+P`"M`(``D1,``/P`0`"N"T``\&8 +M``/!J``#P;@``V,J``-C:@`"+J@``X'B``.@X0`#HY8``X+D``+"+@`#Z%`` +M`BYQ``.@XP`"+G$``BZH``.`Y0`"0*```\$0``/!(``#P3```V2H``-DZ``# +M@.```H((``)"`@`#@B0``)'4``/L```"+J@``TEP``)$C@`#\$,``K7S``)" +M)0`#:7```^AP``/!;@`#P5,``\%"``-H,0`"1)X``_`&``-)G``#I.$``F(D +M``/!4P`#P4(``V@Q``)$C@`#\$T``TF<``.$Y0`#I><``D`$``)!%0`#I(@` +M`X1```.%1@`"8`4``F$4``/H,``#P2X``V@P``-#I```(=0``K7/``)`!0`" +M8`0``\$0``/!(``#P3```V.D``-CY``"1$0``_`!``(VWP`#@80``Z`8``." +M!@`"8`(``^C0``(NE@`#P=X``BZ6``-)T0`"+J@``^A@``*E$``#\`$``K9` +M``)&=@`#IFH``TB0``.DN@`"H$X``_!!``*T!``"M0,``J!%``/P00`"M`$` +M`K7P``)!%0`"810``K?^``)`!P`"8`8``VB0``-HJ``#:)0``VBL``.@I@`# +MP1```\$@``/!,``#9"0``V1D``.*M@`#2"$``K@#``)F:``"N0\``KO^``)& +M:0`"9FH``D=[``-H(0`#:HD``V@E``-JC0`#2`@``T@A``)A'@`#:`@``TC( +M``*X0``"N3\``F`(``-HR``"0`D``VC(``/L```#0"P``KA```*Y@``#Z+`` +M`TJE``)(&``#\`$``KLP``))&0`#\`(``KK```)KN@`"95L``VJE``-(G``" +MO'L``D`,``-HG``#:*```VBT``-HN``#2)```X3B``.EYP`"014``F$4``*U +M_@`"0`4``VB0``-HJ``#:)0``VBL``-).``"M0,``F(E``*U\P`"0B4``VDX +M``/HP``"-`D``C0Z``/H```"L0,``K()``/H,``#Z%```BYQ``.`Z@`"+G$` +M`K`$``/!$``#P2```\$P``-C)``#8V0``K`@``/!$``#P2```\$P``-C*``# +M8V@``C;?``-("``#2"$``ZCM``)!&``#:`@``ZCK``)&:``#P#X``D53``-H +M(0`#:HD``V@E``-JC0`#Z$```C1>``/H\``"/B(``K\!``(^(@`#[````T`I +M``/_```"N"H``D)>``/P`0`"N&0``J#.``/P00`"N!```\&8``/!J``#P;@` +M`V@`# +M0[(``K`'``*Q*``"0EX``_`!``*Q.``"H,X``_!!``*Q```"2(```FB!``/! +MF``#P:@``\&X``-CL@`#8_(``^P```-)=``#P6X``^AP``.(2``#P%X``X59 +M``)")0`"8B@``VET``/!0@`#P5,``V@Q``/L```"OP,``C;D``-("``"N/X` +M`D$8``-H"``#0"@``KP$``*_`P`"3,(``_`#``(]RP`#]````=2+``-(5``" +MO&```F(L``-H5``#278``KP0``)DK``#P5L``\%N``/H<``#:#$``C%$``-( +M5``"O)\``D(L``-H5``#P4H``V@Q``(SJP`#0D4``C)F``-B10`#[````&HQ +M``(M"```\>4``C`H``(KQ``#0`0``T`*``*\`P`"OP0``D$<``,&\0`"0`\` +M`_`!``/!:@`"Q&X``K4(``(XK@`#0`0``^CP``#Z!@`"LD```D(@``'4\0`# +MCP0``Z_X``#Z`P`#2<$``KP/``)(SP`#J(```XF"``*ZXP`"1$H``F1)``)) +M_@`#B9(``KK[``)%6@`"95D``VG!``.!A@`"8($``BZ6``/H```"H-X``_!! +M``*P0``"L0,``K()``*S"0`"1.\``_`!``*S#P`#Z%```BYQ``*C_``!U/$` +M`^C```#AS@``X>X``C97``-`&``"H-X``_!!``-`'```0>X``&'.``)@`0`" +M8`(``F`#``*A#@`#\$(``LB.``#![@`"N@(``KL?``+,R@``X4``_0` +M``'4J@`"S_X``/H#``#"!@`#]````=2J``/HP```X@,``^@P``(V:P`#W.`` +M`K\(``/8\``"L"```\$0``/!(``#P3```K\1``/4'@`#_P```_L```,/_@`" +M%/T``C97``!B`P`"L````]P```/<`0`#C\(``K!```+`#P`#V````K`_``)! +M#P`"L,```L`!``/8`0`#U%X``]`?``*_$``"H<\``=4>``*@W@`#\`,``\$* +M``/T!``#\`$``\$+``/4'P`#]````=4E``*@W@`#\`,``\$J``/T!``#\`$` +M`\$K``/4'P`"-@\``T`&``*P'P`"L2```\$N``)!&``#\`$``L(N``+,P@`` +MX@,``J7```(5`@`#2I0``_\```-@O``#0`0``]S@``*_"``#V/```]SA``*_ +M+``#V/$``K\(``.L%@`"I<\``_!#``/`S``";,\``LS.``*_"``#T!X``]!? +M``/!B0`#P9H``\&K``+$0``#H$```L`,``/!L``"Q5$``Z%0``+!'``"IAL` +M`_`!``/!L0`"QF(``Z)@``+"+``"IBL``_`!``/!L@`"QW,``Z-P``+#/``" +MICL``_`!``/!LP`#U!X``]2?``/[```#^P$``P_^``(50P`#03@``T%)``-! +M*@`"O`X``J,!``/P`0`#P0$``J,"``/P`0`#P0(``J,#``/P`0`#P0,``J,( +M``/P`0`#P0@``J,)``/P`0`#P0D``^@0``*F#``#\`,``\````.`!@`#H08` +M`\$!``*C10`#\`$``\%%``*C1@`#\`$``\%&``*C1P`#\`$``\%'``*C2@`# +M\`$``\%*``*C2P`#\`$``\%+``/H(``"IDP``_`#``/`1``#A$8``Z)&``/! +M,@`"H-X``_`#``-DJ``#]`0``_`!``-DZ``#W.```K\(``/8\``#T%X``K\$ +M``/<\0`"OX```J#>``/P00`"O\```]CQ``*\$``"N`$``KD#``*Z#P`"H4P` +M`_!%``/`1``"1$H``P0$``/T!``#\`(``P1*``+$0``"H5P``_!%``/`50`" +M15H``P4%``/T!``#\`(``P5:``+%4``"H6P``_!%``/`9@`"1FH``P8&``/T +M!``#\`(``P9J``+&8``"H7P``_!%``/`=P`"1WH``P<'``/T!``#\`(``P=Z +M``+'<``#U%\``_L```/[`0`#T%X``PB.``(5J0`"N`$``\$!``/!$@`#P2,` +M`PF>``(5J0`#1*@``J#>``/P00`#1.@``J%,``/P10`#P$0``D1*``,$!``# +M]`0``_`"``,$2@`"Q$```J%<``/P10`#P%4``D5:``,%%0`#]`0``_`"``,% +M6@`"Q5$``J%L``/P10`#P&8``D9J``,&)@`#]`0``_`"``,&:@`"QF(``J%\ +M``/P10`#P'<``D=Z``,'-P`#]`0``_`"``,'>@`"QW,``]1?``(L]P`"-&L` +M`C`H``(O[P`#[````BZH``.CE@`"-FL``X#E``)#H``"-G0``_0```'6`@`# +MP9```J'/``/P`0`#P9(``V)U``#)S@`#W.```K\(``/8\``#W.$``K\L``/8 +M\0`"OP$``\$$``(V,0`#0G4``K\!``/_```#P04``C8Q``-"=0`"OP$``_\` +M``/!!@`"-C$``T)U``*_`0`#_P```\$'``(V,0`````/4GP`#^P```_L! +M``.@```##_X``A8R``/L```"LQ```J7#``/P!@`#P#P``X,V``.C-@`"-G0` +M`_0```'69``"LP\``LS.``)#/``"-FL``C&X``(L]P`"-&L``K\8``(X7``" +M+NP``^P```*Q!``"L(```J#>``/P00`"L,```K()``/H4``"+G$``^P```/! +M`P`#P1,``\$C``*@W@`#\`,``V2H``/T!``#\`$``V3H``/L```#0`0``KR` +M``/_```"3,```=:>``-!`0`"H-X``_!!``-!!0`"L00``K"```*@W@`#\$$` +M`K#```/<$``#V````\&$``(VNP`#P84``C:[``/!A@`"-KL``\&'``(VNP`" +MH-X``_`#``-DH0`#]`0``_`!``-DX0`#]````=:X``*Q!``"L(```J#>``/P +M00`"L,```]P1``/8`0`"L0$``K`(``*@W@`#\$$``K`L``/<$``#V````K\( +M``(NM``#00$``J#>``/P00`#004``J#>``/P`P`#9*D``_0$``/P`0`#9.D` +M`BSW``(T:P`#[````\&8``/!J``#P;@``K(!``/4G@`#^P```P(N``(6OP`# +M[````K$$``*P@``"H-X``_!!``*PP``#W!```]@```*Q`0`"L`@``J#>``/P +M00`"L"P``]P1``/8`0`"OP@``BZT``-$J``"H-X``_!!``-$Z``"H-X``_`# +M``-A```#]`0``_`!``-A!``#[````TL,``*U_``"014``VL,``/L```#2PX` +M`\#/``)JKP`#:PX``C%$``)*K``#:PX``^P```*_`P`"-N0``TG```-)U0`" +M3`X``_!"``/T```!URD``TG```.'=@`"IG```_!"``/!$@`#Z````Z(6``.! +M%@`#H18``TB1``*\\``"15P``F52``*\!``"0PP``Z,R``*\_@`"1$P``F1# +M``-HD0`#:)4``VBI``-HK0`#P4$``\%1``/!80`#P7$``V0A``-D)0`#9&$` +M`V1E``*\`@`"0@P``X(J``-(G0`"LW\``D1#``)D0@`#:)T``VBA``-HM0`# +M:+D``J$.``/P`@`"-M\``K`0``/!$``#P2```\$P``-CH``#8Z0``V/@``-C +MY``#27P``K3[``)!%``#:7P``VAK``/T```!S`4``C`H``(O[P`#0````_\` +M``/_```#H`P``Z$<``.!$``"81```P\>``/HT``"H/X``_!!``*]`0`#P4``_\```)&8@`"9FH``VGE``/T!``"%W8``J/^``(7R@``T=`` +M`J#>``(7GP``T=$``LW>``/T!``"%U4``$'1``/_```#_P```J"H``(7R@`" +MH:@``_!(``-((0`#`XH``X,P``)G4``_\```)&8@`"9F@``VGE``-()0`#`Z@``X,P +M``)G4``KB```)G>``#8!$` +M`TF8``*U!``"814``VF8``-("``#_P```X3(``)@!``#:`@``_@#``/L```` +M:C$``^@```""#```@<\``CC*``-`)``#_P```_\```)`#@`#\$L``T`0``*\ +M!``#W,```X_J``*@W@`#\`$``^CP``/8\``"O`D``\&Q``(MBP`#0"0``_\` +M``*\`@`"0`P``_!+``-`$``"O`0``]S```*_P``"H-X``_`!``*_@``#V/`` +M`XSD``/!L0`"+8L``BO$``*T!@`"M0@``K8$``(XK@`"OQ@``CA<``(X@P`" +MOP```B_T``-*<``#_P```_\```*RD``#:G```K\3``(X7``".(,``T`D``/_ +M```#_P```D`.``/P2P`#0!```KP$``/``/P`0`#Z/```]CP +M``*\"0`#BQ```BV?``-`)``#_P```KP"``)`#``#\$L``T`0``*\!``#W,`` +M`K_```*@W@`#\`$``K^```/8\``#C.0``XL0``(MGP`#2G```_\```/_```" +MLH```VIP``*_&``".%P``CB#``*_```"+_0``TIP``/_```#_P```K*0``-J +M<``"OQ,``CA<``(X@P`#0"0``_\```/_```"0`X``_!+``-`$``"O`0``]S` +M``./Z@`"H-X``_`!``/H\``#V/```KP)``/!L0`"+8L``T`D``/_```"O`(` +M`D`,``/P2P`#0!(``KP$``/``/P`0`"OX```]CP``.,Y``# +MP;D``BV+``(X[``#[````^@```/````"L;\``\$@``*S_P`#:&```C`H``(O +M[P``"BT``!'^```9_0`"L`(``J#>``/P00`"L`0``X$6``)@`0`#@BH``K&$ +M``)B(0`"I#X``_`!``*S!``#P1\``VI@``-*9``"M(```!'L``)`!``"M`,` +M`F`$``*S$``#:F0``BZ]``*_"``"3_```_`!``#R#``#[`````(,``/_```# +M_P```J4.``/P`0`#[````^@```""#``#0!(``_\```/_```"1(X``J!.``/P +M`0``\>4``K]```*@W@`#\`,``FJO``/T```#\$$``FN_``-@$@`"+NP``'G/ +M``/_```#0"```J#^``/P00`#[````J#>``/P!0`"P`X``_!!``*P_P`#]``` +M`_!#``+!'@`#\$$``K'_``-@(```\<\``^P```*P`0`"L0(``\$D``/H,``# +M:F@``\$E``*P$P`"L0(``X(F``/H,``#:G```K`A``*Q`@`#P28``K,!``-J +M=``"L`$``K$"``*R#P`"LP$``VIX``-*?``#_P```K````*Q```"LP0``VI\ +M``/L```"L`$``K$1``*R$``#:D0``K$0``/H(``#:D0``J#>``/P`P`"NU,` +M`_0```/P00`"NUL``-GL``-("``"M$```('F``)@!``#:`@``T@!``*POP`" +M1$```V@!``/H```"L4```^@@``/H,``#:D0``K`!``/H$``#Z"```^@P``-J +M8``#[````C`H``(O[P``(>8``_\```-("``#_P```\$$``-H"``#[`````HR +M``!J,0`#_P```D`>``/P00`#[````CC*``*_```"+_0``#(:``/_```"M0D` +M`Z9B``+&;@`"Q&X``CBN``-`*``#Z/```J#>``/P00`"OT```/G@``.@#``` +M@<4``T`8``-`'0`#0"X``&'E``/_```#_P```V%0``-A50`#85H``.''``-` +M$```>>```KP$``/``/P00`#0!P``T`M``/_```#U!X``]1? +M``*_`0`#W/```K\```.(P@`"S_@``]CP``/$```#Q!$``\0B``/$,P`"H-X` +M`_!!``/!9P`"M1$``D1E``/$1``"P`0``K4B``)$90`#Q$0``L$4``*U1``" +M1&4``\1$``+")``"M8@``D1E``/$1``"PS0``]0>``!YQ0`#0G4``T)R``)/ +M_@`#\$(``L`!``+"(P`"H00``_!&``*E"0`#\`0``\&,``/!D``#P:P``\&P +M``*C"0`#\$(``\&L``/!L``#8G(``T)&``*A)@`#\$8``J4I``/P!``#P8P` +M`\&2``/!K``#P;(``J,I``/P0@`#P:P``\&R``-B1@`"H/X``=FN``-"3@`" +MH14``_!&``*E&0`#\`0``\&,``/!D0`#P:P``\&Q``*C&0`#\$(``\&L``/! +ML0`#8DX``T)*``*A-P`#\$8``J4Y``/P!``#P8P``\&3``/!K``#P;,``J,Y +M``/P0@`#P:P``\&S``-B2@`#8G0``_L!``*Q$``"S,X``.'.``*@P0`!V3<` +M`T)P``-"10`#_P```L@"``.H@``"RD8``ZJ@``-"3``#0DD``_\```+)`@`# +MJ9```LM&``.KL``".=L``T`0``!YX``"O`0``]S```/8\``"O`D``\&Q``(M +MGP`","@``B_O``-!4``#054``T%:``!AQP`#_P```_\```-@&``#8!T``V`N +M``#AY0`#[````KLA``*[$````<4``_\```/_```"0`X``=H3``.IA``#JZ0` +M`KP/``.(@``"2(P``XJ@``)*K``#2)P``TBA``*@W@`#\$(``TBT``-(N0`# +MP1@``\%:``.,%@`#CU8``F$<``)E7P`"H-X``_`$``-HG``#:*$``_0```/P +M0@`#:+0``VBY``-*Z``#2NT``J#>``/P0@`#2O```TKU``*\[@`"0`P``D1, +M``.(E@`";)@``F`,``.*M@`";+H``F1,``*@W@`#\`0``VKH``-J[0`#]``` +M`_!"``-J\``#:O4``^P```*_!0`"O````J#>``/P00`"O$```]SP``/8P``" +MO(```FB,``)IG``":JP``FN\``/!"``".BL``\$)``(Z*P`#P0H``CHK``/! +M"P`".BL``]2>``/[```#U)X``^P```/!$``#P2```\$P``/4'@`#^P```]0> +M``/[```#[````&HQ``(M"``#Z````K%@``/H(``#Z#```VF@``-)F``"N`\` +M`K7U``*S```#P2X``F`(``)!%0`#:#```K\!``(M!``".,H``C`H``-`+``" +MOP0``_\```)/\``#K_(``^CP``(O]```,AH``_\```*U"0`"Q&X``CBN``/H +M\``"H-X``_!!``*_0```^>```%G'``!YX``"O`0``]S```/8\``"O`D``BV+ +M``/HP```X``/P00`#P:L``&'.``!YQ@`# +MU%\``_L!``/07@`"NP(``J'^``/P00`#P4H``J#^``/P00`#P5H``J#[``/P +M00`#P6H``J;[``/P00`#P7H``]1>``/[```"L!```LS.``*EP``#\`8``.'. +M``/!C``#P:P``CG;``/T```!VFL``K$$``+/_@``^<8``J'Q``':I@`#Z+`` +M`-G.``/!BP`#P:L``CG;``!YX``"O`0``]S```/8\``"O`D``\&^``(MBP`# +MW.```KM```/8L``#]````=IK``(X[``"N`@``KH(``(YVP``6<<``'G@``*\ +M!``#W,```]CP``*\`P`"R[P``KP)``(MGP`#2`@``K1@``)@!``#:`@``TF8 +M``/_```"L@$``^@P``-H,``#[````K0$``)&-``"H&0``=K*``/H\``"/?0` +M`K\!``(]]``#:&L``T))``(R9@`#8DD``^P```-"30`",F8``V)-``-(9``# +M^`$``KT,``))T@`"H-D``_!&``*_`P`".RD``K\#``*]```".PH``^P```*] +M!``"2=(``_`&``*_`0`".RD``K\!``*]```".PH``^P```*]"``"2=(``_`& +M``*_`@`".RD``K\"``*]```".PH``^P```*]8``"2=(``J#9``/P1P`"OP,` +M`CNP``*_`P`"O0$``KP!``(["@`#[````KT@``))T@`#\`<``K\!``([L``" +MOP$``KT!``*\`0`".PH``^P```*]0``"2=(``_`'``*_`@`".[```K\"``*] +M`0`"O`$``CL*``/L```#2&(``DS.``(;'``#@/(``\````.!^``#P!$``DW> +M``/P10`#P````FJ@``)*H0`#]`0``_`#``/`$0`"2J```FJA``-H8@`"L/(` +M`FJ@``*@"@`#\$<``TA```*T"``"M?<``F$4``-H0``"014``VA```-H:P`# +M[````TA4``/H@``"N0,``DO^``/P`0`":(D``KH"``*Y#``"2_H``_`!``)H +MB0`"8S@``VA4``/HD``"M`0``DO^``/P`0`":9H``DOZ``/P`0`":90``TE@ +M``-H:P`#_P```_\```-(90`#_P```DO6``*@O0`!VZ<``DN3``';/@`#0"@` +M`X3P``"B`@`#HB8``C%$``,"+@`!VTT``T@$``*T[P``BBP``D$4``-H!``" +MM8```DO^``/P!0`#2)@``K2_``)#-``"8S4``VB8``)+^@`#\`4``TBP``*T +MOP`"0S0``F,U``-HL``"/>@``K8$``-H,0`#0"@``C%$``)!&@`"&Y```$H! +M``/_```"N(```\"9``.)G``"2_X``_`&``-)/``#2)D``F,X``)D20`#:3P` +M`VB9``)+^@`#\`8``TE```-(L0`"8S@``F1)``-I0``#:+$``DO^``/P!@`# +M2)```TB5``)@"``"9$@``VB0``-HE0`"2_H``_`&``-(J``#2*T``F`(``)D +M2``#:*@``VBM``-`*``#_P```K@$``)!&``#\`$``CQH``-`*``#_P```K@( +M``)!&``#\`$``CTJ``-(0``"N(```DO^``/P`0`"81@``DOZ``/P`0`"8B@` +M`VA```*\`0`#[````TA4``/`B``"0S@``VA4``-"20`",F8``V))``*\```# +M[````T`H``#YNP`"N`@``D$8``/P`0`"/4$``T`H``/_```"N`0``D$8``/P +M`0`"/+<``T`H``/_```"N`(``D$8``(;YP`"2_X``AO*``-)/``#2)D``KA_ +M``)#.``"1$@``VD\``-HF0`"O`(``DO\``(;U``#24```TBQ``*X?P`"0S@` +M`D1(``-I0``#:+$``DO^``(;W0`#2)```TB5``*X?P`"0`@``D1(``-HD``# +M:)4``KP"``)+_``"&^<``TBH``-(K0`"N'\``D`(``)$2``#:*@``VBM``-` +M*``#_P```K@.``)!&``"'`<``CT4``)!%P`#:,@``T@(``*X$``"01@``=P' +M``)+_@`"&_P``T@@``*T`0`"M?P``F$4``-H(``"014``V@@``*\`@`"2_P` +M`AP&``-()``"M`$``K7\``)A%``#:"0``D$5``-H)``"-N0``DO^``(<#@`# +M2)@``K3```)C-``"0S0``VB8``*\`@`"2_P``AP6``-(L``"M,```F,T``)# +M-``#:+```KP#``(]R``#2$```KA_``)+_@`#\`$``D(H``*\`@`"2_P``_`! +M``)!&``#:$```T@(``*X$``"01@``=PQ``-`*``"O`0``_\```),P@`#\`,` +M`CU8``/T```#\$$``CW+``).[@`!W#,``KP(``(]R```>;L``_\```/_```" +M2_X``_`$``-(F``"M#\``D,T``-HF``"O`(``DO\``/P!``#2+```K0_``)# +M-``#:+```TA```*X(``"2($``AQ*``(]Z``"M@(``V@Q``-)?``"N`(``DB` +M``(<40`#27$``CWH``-H,0`#2`0``"(L``/_```#_P```\$4``-H!```>;L` +M`K@```*\`@`#2%0``DO^``/P`0`"N`,``DO\``/P`@`"O`P``FB,``/`B``" +M0S@``VA4``.`^```@@(``^P```-("``"N!```D$8``'<>0`"2_X``_`$``-* +M5``"M`\``F`$``-J5``"NP(``DO[``/P!``#2E@``K0/``)@!``#:E@``DO^ +M``/P!``#2E0``K3P``)@!``#:E0``KL"``)+^P`#\`0``TI8``*T\``"8`0` +M`VI8``-("``"N!```D$8``'``/P`@`"OP`` +M`CWT``*\`@`"2]P``_`"``*_`0`"/?0``\']``/L```"M_P``K8S``*U1``" +MM````TC(``*@_@`#\$$``K0!``*\`@`"H/P``_!!``*T`@`"810``VC(``)@ +M!0`#:,@``_\```)`!@`#:,@``KP"``(]R``#[````T@(``*X$``"01@``=T[ +M``)+_@`#\`0``TB8``*T?P`"0B0``VB8``*[`@`"2[\``_`$``-(L``"M'\` +M`D(D``-HL``#23@``X3R``/`1``"0B0``VDX``/L```#23@``X3R``)B)``# +M:3@``T@(``*X$``"01@``_`!``/L```"2_X``_`$``-(F``"M(```F(D``-H +MF``"NP(``DN_``/P!``#2+```K2```)B)``#:+```^P```-"20`",F8``V)) +M``)!_@`"'64``T.N``*P[P`#_P```DNP``)*L``"2;```DBP``-CK@`"L0(` +M`D'Q``(=<``#0^X``K#O``/_```"2[```DJP``))L``"2+```V/N``-(5``# +MC/@``F(L``-H5``#270``KS?``)$+``#P5,``CWH``-H,0`",40``KP0``)D +M3``#:#$``C%$``-(5``"O)\``D(L``-H5``#270``_\```/!0@`#P5,``CWH +M``-H,0`"0?X``AV3``-#K@`"L!```_\```)KL``":K```FFP``)HL``#8ZX` +M`K$"``)!\0`"'9X``T/N``*P$``#_P```FNP``)JL``":;```FBP``-C[@`" +MO`(``D?^``),_``";,<``K<#``*@?P`#\$$``^C```-(R``"N$```KD_``)@ +M"``"81P``VC(``)`"0`#:,@``\#,``)!'``#:,@``KO\``)*_@`#\`@``T@A +M``/_```"95X``V@A``-JB0`"15L``V@A``-JB0`"N@(``DJO``/P"``#2"4` +M`_\```)E7@`#:"4``VJ-``)%6P`#:"4``VJ-``/L```##,X``=W(``/L```" +M/>@``K8"``-H,0`"M@$``TF>``*\$``"9*P``\%;``-H,0`#29H``KP"``)D +MK``#P5L``V@Q``/_```#270``KP0``)D+``#P5,``V@Q``(Q1``#P,P``D1, +M``-H,0`",40``\%*``/!6P`#:#$``^P```/!;@`"MP,``DW^``/P`@`"O0(` +M`D=]``*]`@`"3?T``_`"``*]`0`"1WT``^P```*X!``"L0(``K"```)/_@`# +M\`$``K#```(^0@`"L0,``K````)/_@`#\`$``K!```(^0@`"L0,``K"```)/ +M_@`#\`$``K#```(^0@`"L00``K````)/_@`#\`$``K!```(^0@`"L00``K"` +M``)/_@`#\`$``K#```(^0@`"3_X``_!'``-'-``#1SD``_\```-G-``#9SD` +M`_0$``/P!0`#1W0``T=Y``/_```#9W0``V=Y``/L```"N`0``^@```/H$``# +MZ"```^@P``)/_@`"'C```V4L``-E,``#930``V4X``-E/``#]````=XU``-E +M;``#97```V5T``-E>``#97P``K$&``*P```"3_X``_`!``*P0``"/D(``K$& +M``*P@``"3_X``_`!``*PP``"/D(``^P```+""``#W!```]@```/<$0`#V"$` +M`]`>``/07P`"O`<``]0>``/47P`#^P```_L```/[`0`#^P$``]`>``/07P`# +:#,X``=Y*``/L```#_P```_\```/_```#_P`` +` +end diff --git a/sys/contrib/dev/drm2/radeonkmsfw/VERDE_me.bin.uu b/sys/contrib/dev/drm2/radeonkmsfw/VERDE_me.bin.uu new file mode 100644 index 00000000000..ad4aba2695d --- /dev/null +++ b/sys/contrib/dev/drm2/radeonkmsfw/VERDE_me.bin.uu @@ -0,0 +1,194 @@ +begin 644 VERDE_me.bin +M?$"``8@```#40`!_?$"``8@```!\0,`!S$``*,Q``"E\0(`!B````-A``![$ +M$``A)1```I4`__Y\00`!Q!P`)L0@`"38```50``"V4&BI,0;``+8```;S8``&L`^``3,`:'TS#T@!,0]``',```%V$`` +M&]A``!]\0(`!B````,Q!+@',02X"S$$N`\Q!+@!\0(`!B````'Q`P`&,``4G +MS$$N`'`&GQ"``$E)``! +MS,``(]%``"31@``ET<``)MA``">60``,Q"@`+)J`___()``M?EZ`#)J`_YP2 +M+``&!NP``0KL``&:P/__V$``)X```&#$*``LFH#__WQ`@`&(````?$#``<00 +M`##,027,?04```````ET```\Z!HKN````` +MEX```\Z!HK^`````SH&BPX`````$&```@``!0P08``'$)``F?$#``<9K`^08 +MT``P)-0`_P:H``'.I`/DFD``$L0X``?80``>Q#P`(2?\``*7P/_^S(``"Y>` +M``+90:*DQ#\``L_``!K`/@`$S`&A],P](`3$.0`!S```!=A``!_-0:#:S(`` +M"\U!+A29@``"S````LU```A\0(`!B````'Q`P`$8T`'H&-0`,!C8`#0%*`%K +M?$(``7Q"0`&5```'AH```(```86```&I@``!OX``!2.```%[$50`$'X6``K, +M@``+U&$``)6`_HO`.@`$S#DA0,0Y``%\0(`!B````!%4`!!29``@?B8`&LU` +M`!W48@``E8#^@,0@`!V:`/__?$"``8@```#<.@``F4``&R><``&5P``-"[@` +M```%\0@`!",P``08D``$&*``"SAVA_``"```(.P"(`!'X6``K,(0``Q!T``7Q"0`%\0H`!F,`` +M`\WE``"`````SD$A:'@```@``"1X```DR```)1@``"5H```EN```)@@``"97\;@`\4I``( +MEX``'29D`/^```)V?QN`#A2D``B7@``8)F0`_X```G9_&X`,%*0`")N``!,F +M9`#_@``"=G\;@`T4I``(FX``#B9D`/^```)V?QN`#Q2D``B;@``))F0`_X`` +M`G9_&X`.%*0`")N```0F9`#_@``"=A2D``@F9`#_,F@`/!3L``B:@/V7?$-` +M`7Q#@`%\0\`!S```"Y;```;/02%ISX$A:L_!(6O,`2%L@````,_U``"````` +M,F@`/)J`_[/40`!_@````'Q`P`%\00`!P!X``144`!+`(@`"P"8`!)5```3` +M)__[?24`"<`F``!]TH`)?A+`"7TE``I\04`!?$&``@%*`*I',S^",R```N5```)AH```(```K2```+#@``"RH```M&` +M``+7@``"XX```P',P:*D?$"``8@```#`$@@`?$%``7Q"``%]#,`*P!(`!!58 +M``,57``-?='`"1(@`!-^'D`*?DZ`"LZ!HJ3-@:'^?$"``8@````$$"$8Q!0` +M"Y5`___440``S,&BI'Q`@`&(````!!`A!L04``R50/__U%$``,S!HJ1\0(`! +MB````-A```_,P:*DQ!``#YD`__]\0(`!B````!C0`#01%``4Q"0`#99`___, +M02$`?45`"LU!(0',02$"S$$A`\S!HJ1\0(`!B````"38``%\00`!?$%``1&< +M`!!\0@`!?5U`"A5D`!TF9``"E8``"<0\`!^7P/__FD``#<0H`#`6J``"?BH` +M`Q#P`(2?\``*7P/_^S(``"]E!HJ3$/P`" +MS\``&L`^``3,`:'TS#T@!,0]``',```%V$``'Y````!\0,`!!#P`(LR```O/ +MP:*DS```!GQ`@`&(````P!(``7Q10`K,@``+U%4``'Q`@`&(````?$#``5!0 +M`"!\T,`:51P`/WQ!0`%\08`!S(``"]#``(.5P``#P!R``,W!(!"5@/RVC``% +M)]V#```%7"``S```"]1=``"```4O?$#``5!0`"!\T,`:?$%``7Q!@`',@``+ +MT,``A)6`_*B,``4GW8,```5Q`P`(23,``&4P/_^QB<# +MY'Q`P`',@``+S,$A?,Q!(7W$&``D?$$``7Q!0`&60``"S8$A>LT!(7[`-@#` +MP"H`!']/0`D17``!!=P``9=``!3,*2%_Q"4``1IL`#Z6P``'$5P``07<``$) +MW``!F<#__\R```N```27%W0`%R@L`&";0``"*"P`0,[``"/80``GQ#0`+)M` +M__^```2O"=P``9G`___,@``+S"DA?\0E``$6;``?$5P``07<``&:P/_X%/P` +M']@``!^;P/M+Q!``)LP0`_=\0(`!B````'Q`P`%\00`!%1@`'U$4`"`9'``Q +MF8``",T``!U]34`:U%8``)7`^SW$(``=F@#__X````#<.@``P"8`!,S!(6E] +M)0`*S0$A:@NX``+,02%KS$$A;)N`__V9P/V_?$"``8@````D3`#_S$P#`'Q` +M@`&(````Q"``)L1/`P#,@``+S.$A18````#,@``+VL&BI'Q`P`%\0(`!B``` +M`,0,`!J8P``#V$``+X``!.'80``P?$$``7T!0`$55``!)50``9E```DE$``! +ME0#[%,@4`#250``%R!@`&Y6```-]E<`.E<#[#L@@`#/2```RV```'L0@`"$F +M(``!E@#__L`F"$#.02%\S`$A?<0H`"3.@2%ZS`$A?L`J``0$%``$05P`""@L +M`$#.P``CV$``)PG<``&9P/__S(``"\PI(7_$)0`!%FP`'T%<``*:P/_YQ#0` +M+)M`___8```??$"``8@```#$#``3,-```94```/,@``+VP&BI,Q```[$#``2 +MQ!``$S#4```]&``!?5G`"MA``#&9P/_ZV$``$]```#)\0(`!B````,`.`0#, +M```!S,$P2H``!2;80``1Q#P`$9?`__V0````V```$<0\`!&;P/_YD````-@` +M`!'$/``1F\#_]7Q`@`&(````V(``'L0\`"$G_``$E\#__L`X`"_`/@`$SX$A +M^,P](?G$.``F!#P`!7_[@`+$/0`!?_O`!2?\``&;P/_VV(``'Y`````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M`````0.<`!```@`1``4`$@`*`!4`)0`6`"L`%P"Q`!L`-``<`$$`'0!O`!X` +M3``A`+L`)`#9`"4`V0`G`.,`*`#6`"H`WP`L`-D`+0#C`"X`YP`O`.T`,`#Q +M`#(!0@`T`0,`-0#C`#@`_`4`` +M0`.B`$$$7@!"!'D`0P2!`$0"F`!%`AD`1@*C`$<"HP!(`J,`2@,3`%(#'P!3 +M`S(`5P,Y`%\#7@!@`S\`80-1`&@#:P!I`V\`<@-W`',#E`!V`W,`=P-S`'H$ +MN@!]!-,`?@37`(4$W`"&!.$`B@42`(L%$@`/!2,`#P4C``\%(P`/!2,`#P4C +M``\%(P`/!2,`#P4C``\%(P`/!2,`#P4C``\%(P`/!2,`#P4C``\%(P`/!2,` +M#P4C``\%(P`/!2,`#P4C``\%(P`/!2,`#P4C``\%(P`/!2,`#P4C``\%(P`/ +:!2,`#P4C``\%(P`/!2,`#P4C``\%(P`/!2,` +` +end diff --git a/sys/contrib/dev/drm2/radeonkmsfw/VERDE_pfp.bin.uu b/sys/contrib/dev/drm2/radeonkmsfw/VERDE_pfp.bin.uu new file mode 100644 index 00000000000..0e91860c87a --- /dev/null +++ b/sys/contrib/dev/drm2/radeonkmsfw/VERDE_pfp.bin.uu @@ -0,0 +1,194 @@ +begin 644 VERDE_pfp.bin +M?$"``8@```#,@```U$```'Q`@`&(````S(```,S```#40```?$"``8@````D +M3``/,.@``B20``*:@``,?$%``5!8`"!]E<`:F,```]'``!F`````F0```]'` +M`!J`````T<``&X````#$(``5QB0`"I9``6M\00`!?$%```_RU\0,`!?$$` +M`8```,G$$``/Q!P`!)D```3,```2S<```,P```#$&``()9@``H```+TDC``" +MQ!``$,08``B8P``(F0``!<0<``7,```3S<```,P````9F``P@```O1F8`#B` +M``"]Q!P`!,W```#,````S(```-1```!\0(`!B````,0<``3-P```S````,@, +M`!G,@```S$```!!0``)]#0`1510`(-$```/80``#S````'Q`@`&(````Q!P` +M!@8U``P&-@`-`4H`:-\ +M0@`!?$)``94```>&@```@``#F(```YB```.8@``#F(```;$15``0?A8`"LU` +M`"'480``E8#^4\0Y(4!\0(`!B````!%4`!!29``@?B8`&LU``"'48@``E8#^ +M2L0@`!.:`/__?$"``8@```!\0,`!?$$``7Q#@`%\0\`!41``(%/\`"!_OX`: +M?-#`&@0<`!#3@``"V(```M#```+-P``"!!P`!`0@``%\`D`!Q!<``\0;``-1 +MF``@?5E`&L0K``/$+P`#Q#,``\0W``,)W``!4NP`('ZN@!I3=``@?S<`&G\K +M0!)^=D`15J@`/U0`F9 +M@``$S````LP```V```.4%9@``14L``@F[``!F8``,Q4P``R6```#S````H`` +M`Y0$%``0S4```B`_=$+N``!Q#\``YN` +M__V`````S```#I9```/,```"@``#E-H```+$"P`#Q`\``\03``/$%P`#Q!L` +M`\0?``/$)P`#Q"L``Q7\`!\6L``??_/`"13P`!]_\\`)%7``'W_SP`E]B(`" +M?8P/__?$"``8@```#$*``DQ#``#\0T``2:@``% +MFP``!,P``!+/0```S````'Q`P`'8:`/WS(```,S```#40```%-``'YD``+J` +M``+(S(```,Q```#,0```S$```'Q`P`',P``6S,```-1```!\0(`!B````'Q` +MP`'$%``=%-P`'1C8`#Q\U,`*S(```,S```"9P``#F8#]$H```Y3-@``D@``# +ME'Q`P`%04``@C``#%WS0P!K$)``5Q!0`)'Q#@`&60``(Q-8``)E``!G?@P`` +MSZ0`#XP``QO40`!_@````,5?`^Z5P/__V%0#\<5;`_25@``#V!0#\9F`__S% +M7P/N/>```IH```?%8P/KF@``!=^#``#/I``/C``#&]1``'\)W``!S=0#[M@4 +M`_&,``,;?$"``8@```#8```>Q#P`'IO``'^0````V$``'L0\`!Z7P`![D``` +M`(P``Q?$(``5?$#``<`V_P#$$``6P#`__WSU0`E]48`)?8&`'7SS@`F9@``& +MWX,``,^@``^,``,;U$``?X````",``,;?$"``8@```!\0,`!%-P`")7``!DD +MW``0?$$``5!4`""9P``#Q1T``(```SU]%0`:Q1X``'Q"``%\0D`!?$&``7WE +MP`E]XH`/0:P``IJ`_+T&[``#"NP``9K`__\DW``0F<```\4=``"```-`Q1X` +M`(```T#,@```S,```-1```!\0(`!B````,0<``3-P```S````,R```#40``` +M?$"``8@```!\0,`!)-``!C$0``;$%``/F0``",P``!+$)`!^ED``.)E```3$ +M'``$S<```,P```#,@```S,```-1```!\0(`!B````'Q`P`%\00`!%1@`'\T` +M`"%1%``@F8```]1-``"`````?4U`&AD<`#'45@``E<#\B\0@`!.:`/__?$"` +M`8@```!\0,`!?$$``134`!XQ6``")-P`_Y5```29@/R`S1P#`(````#,@``` +MS,```,T```!\0(`!B````,0@`"1\0,`!Q-,#`,R```#,P```S```(,0+``V4@``$C``# +MA(P`!4&```!XQ`@`UR2(``24@/_+Q`@`+I2`_\F,``-2@```>":H`/\4Y``$ +M$F0`""3<``]^7,`*S,``(\Z```R```!XS```+,V```[-@``MQ`@`$I2` +M``*,``$#Q`@`$)B```.,``%&C``%@\0(`!&8@``"C``!UL0G``":0`!=@``` +M>,0K``*,``7`P`Z``'^/@`K`#@``?T]`"B@\`*`3_``0*_P`_XP``8'`'#$% +MQ>T``!+L`!@F[`#_P"0``(P`!9_,```2D````"@,``&,``%H*#@``8P``74H +M.```C``!<9>```[`.#$!Q[D``!>X`!`GK`?_EL```\`D``",``6?S8```L00 +M``.5`/__S```$,`X``&0````Q`P`$93```4H.```C``!=2@,``",``%HP#@` +M`)````#$"``/E(#__\0K``*,``7`P`Z``'^/@`K`#@``?T]`"B@\`)`3_``0 +M*_P`_XP``8'`'#$%Q>T``!+L`!@F[`#_P"0``<`@``",``6?S8``$I````#$ +M"``3E(#__\P```+$"``/Q!```YD`___`+#$>QNT``,`D``",``6?Q#@`$9>` +M`````3`$#$9Q1T``)G`__Z70``$P!`Q&L4= +M``"9P/_^D````(P``7C`%#$5SY4``,`4,1;/50``P!0Q&,\5``#`$#$=Q1$` +M`"40``&9```$P!0Q%\_5``"0````)]``_S$0`/^9```$P!0Q%\_5``"0```` +MP!0`",`>@`!]^<`)E<```L`4`!8)5``!?\$`"L`=_P!]'0`)?14`"L`<,1?- +M'0``E4``!\`<,1?%W0``?='`#)7`__V,``%X@``!FI````",``+-F0``*LV` +M`"W$#``2E,```HP``0/$#``0F,```HP``48H.```C``!=2@X``&,``%QP!PB +M<(P`!8S.@P`CQ`\``L03`")^D0`*?-#`"HP``167@``$S```+.```PU``# +MF4``"##4``290``*E,``'LS+``!'N``$!(@``<>.``#,RP``1[@`!`2(``&` +M``'ME,``%<`0``/,RP``1[@`!`2(``''C@``S,L```D0``&9`/_[1[@`!`2( +M``''D@``S0L```C,``&8P/_[1[@`!`2(``''C@``P!```Y3```*```(!S`L` +M``2(``',@P`&D````'W!P`G$$P`(Q`\`"IC```:9```#*`@`D(```AS$"P`M +M@``"*\03``B9```(P`PPQ,3)``"8@``#P!P``9````!0B``(@``"4L0+`"Y0 +MB``(Q`\`+7R,@`J```)2Q(\``)3``"0PT``#E0``!LV#``@$B``!S(,`+<2/ +M``"```(V,-``!)D```8$B``!Q-4``,U+```$B``!@``"*P2(``'`/```Q(\` +M``2(``&4P``1Q),```2(``'$EP``!(@``<_-```'_``!Q1T``,W+``#/S0`` +M!_P``<4A``#."P``!(@``GU_``R7`/_V@``"/7W!P`F0````Q(X``)3`__TP +MT``#E0``".``3,RP`!QXX`",S+``+-@P`'D````,>B``#`##!QS@T``,>B``3`##!P +MS@T``,>B``C`##!RS@T``)````#$.P`&![@``<`,,'''HP``S@T``,`,,'#' +MHP`!S@T``,`,,'+'HP`"S@T``)````",``+!D````,`((8'."0``P`@A@\P) +M``#`""&`S,D``,`((8+-"0``P`@AA,U)``"0````P`@AH,2-```4S``6),P` +M`9C`__V0````P`@AH,2-```4S``"),P``9C`__V0````P`PAML31``#`"A0` +M?0D`"9````#`,"&VS+$``,<)``"0````*#"``!,P`!",``+@*#"@`!,P`!", +M``+@*##``!,P`!",``+@D````"LT(8W-M0``*S0ACL`-___,]0``D````,`P +M(`C`"/_ZS+$``,<-``#,,0``QPT``)````#$)P`*FD```R@(`)"```)HQ`@` +MQ%"(``B```*#Q#@`R)>```[$.P`'FX```HP``H\H.``!C``!<<0G``K$.`#( +MFD``!,0[``:,``*H@``#`U.X``B,``*>D````,V```[`'")PC``%C,Z#`"/$ +M(P`B?J*`"LZ#``&,``/0Q`@`UR2(``24@``&C``$0Y2``$'$(P`!?>'`"8`` +M`Q;/0P`%Q!\``8P``]O`%```S!<`%`54``$Q2``$E(#__8P``^2,``/SC``$ +M&L0G``]^I(`+F(```H```U%\IL`)Q`\`))3```3`,```S`,`)(```RI\JP`) +MP`@Q`\2A``!^,@`*S@D``'YR0`K.0P`/Q`L`"Y2```7`#"37C``$-GT)``J, +M``0LSL,`"\\#``PVR```S(,`#<`D``"7```/?P*`"L`\"O_`+#$%QNT``";L +M`/^,``.EC``$SL`H`!",``2OP`PDUXP`!#;$,P`,?3$`"HP`!"S`#"37C``$ +M-L0O``M^P4`>?14`"8P`!"R,``.$D````,V```[`$#$$Q0D``,03``)\D(`) +MP"`Q`\8-``!\T,`)?(U`"WU.P`E]2P`)P`PDUXP`!#9_$P`)SL,`"\\#``Q^ +MPD`>Q@T``'SDP`E\T,`)S.$``,`D``"7```,?P*`"L`\"O_`+#$%QNT``";L +M`/^,``.EP!`Q`\4-``#$,P`,?/#`"LS1``#`)```Q"L`"Y:```G`/`G_P"PQ +M!<;M```6[``()NP`_XP``Z7`*``8@``#@,`H`!",``3.C``$K\P#``.0```` +MS8``#L03``O`"#$&Q(T``'S`P!Y]#H`)EH``>I0`O-0P`+?2B`#)2```+, +M`P`-P`@Q`\2-``!^@0`>?-#`";```%Q"L`%)J```,',``!@``#J<0W`!K$.P`;D````,0?``+`##$+Q/D` +M`'^?@`G`'``*P`PQ#,3U``!_7,`.E,```GW#0`J0````?X#``<`4```DT``? +MS1<`$!3,``@%5``!,4@``I2`__N0````?X#``<`@`"#`)``!P"@``'SD@`E^ +MBH`!%,P``0H@``&:`/_\?K2`#Y2```/`)```@``#\G]J0`*0````)=``_\T# +M`!@5W``()=``_\T#`!D5W``()=``_\T#`!H5W``()=``_\T#`!O`#```P!0` +M`,`@``'`'```Q=,`&'TA``F9```.Q=,`$'T@@`F8@``$?D"`#I2```D*9``! +MQ>L`%'ZB@`K.GP`4!,P``7ST@`^4@``"D`````7<``$]R``"E(#_[@54``$2 +M(``!/4@`!92`_^F0````P!P``@G<``'`(``(P"@``,7G`!1^IH`*E<``!7ZB +M@`0&(``("=P``8``!!Z0````S8``#L03`![`#"35C``$+,P#``Z0````)10` +M_\`*B`!\C(`*S4D``!44``@E5`#_P`J(0'R,@`K-20``D````,`0``#`"HA` +M?(R`"L25```E5``??14`"A$0``C`"H@`?(R`"L25```E5``??14`"I````#$ +M$P`%ST,`!7]10`^90``@?38``L`(,0/$E0``?4"`'L0/``)\C(`)E(``&<03 +M``]]%,`+E,``%L`4``#`$``!P`P``'R1P`E\W,`!%(@``054``$Q7``0E<#_ +M^WSA``^5```%?@"`"HP`!&A\@(`)D````'S`@`I^#H`"C``$:,`T``I_:T`" +MS8,`),`(``&0````Q!,`#\`<,0/%W0``P!0``7W6``F:```'?19`"99```5] +MU<`*?4)`'GTE``D(B``!$50``95```*8@/_VS0,`#\`,)->,``0LD````,0K +M``+$)P`!?D)`'GZF@`G`"##7Q(T``,`(``@HB``$?,C`"13,``($S`2'A,`` +M`(``!(N```2.@``$DX``!*C.@P`@SH,`(8``!*W.@P`@P`@Q`\2I``#.@P`A +M@``$K<0(`"^8@``#Q"L`#X``!*2,``/0ST,`!8P``]O`'```S!\`%`7<``$Q +MR``"E(#__8P``^3$'P`!C``#\XP`!!K.@P`/Q!,``LT#`"'.@P`@@``$K<03 +M``_-`P`@P`@Q`\2I``#.@P`AC``%P)````#`##$1SPT``,`,`__,P``QP`@` +M!,`0`!`)$``!F0#__X``!+V60``%Q"``')8```/,```QD````,04`#250``) +ME(``!0B(``'`#`/_S,``,8``!,?,```QP"```)````#`##$2Q-$``'TI``R5 +M`/_JP"```,P``#&0````P!P``,`*@`!_B,`)F,``.'](P`F8P``VP`@``<`, +M``#`$```?X%`"GU*``F:```,!1```03,``$0B``!,.``"I8`__I_04`*P`P` +M`#$@``J6`/_VP!P``9`````%(`3GA@```(``!/&```3S@``$]8``!/>```3Y +M@``$^X``!/V```3_@``%`8``!0/`,``(@``%!<`P``>```4%P#``!X``!07` +M,``'@``%!<`P``B```4%P#``"(``!07`,``'@``%!<`P``>```4%P#``!X`` +M!07`,``(@``%!0LP``$3,``&?Q,`"H``!0W`,``6"S```1,P``8K,``*D``` +M`,Z#`!7`+#$%QNT``";L`/_`)```P#P._XP``Z7`/`;_Q"L`%7Z"@![$.P`" +M?KJ`"8P``Z7`/`[_P#8``)=```3`.```C``!@8P`!:S`-```P#J``(P``8&, +M``6LP"P`@8P`!,[`*``0P"0``(P`!*^0````P"PQ!<;I```6J``()JP`_\`D +M``#`/`W_Q"L``HP``Z7`.```P#8``)=```.,``&!C``%K,`T``#`.H``C``! +M@<`L`(&,``3.P"@`&L`D``&,``2OD````,0+``V4@``VP`@P[\2-``"4P``S +MP`@PT,V)``#`"##.Q)4``,03`!R9```#S4,`')````!]4(`"?(R`#YB```*0 +M````P`PDUXP`!#9\$D`*Q!<``L`,,0O$R0``?("`'GR4@`E]",`)Q`L`"\`0 +M``#`%```P!P``7W*``F6```"!50``1'<``$%$``!/2``$)8`__K`"```P!`` +M`<`<``!]#(`)E(```WW1P`H)5``!$1```9E`__O$(P`+?F)`"GW!0![$$P`" +M?5%`"7Y5``G`#"37C``$+,W#``O,`P`,``?*@``%S<`8``',`P`. +MP`W__\S#`![,PP!AP`@Q!,S)``#,`P`#S`,`8XP`!X?`"`4`*`P``,R/`$'` +M"B`@*(@/!03,``',CP!!P`HP,"B('@\$S``!S(\`0<`*0$`HB"@>!,P`` +MB`",``B$",``%50`$"G,)-?$S0``?4U`"9`````PC`!`F,``,SB,`$"4P``4 +M"(@`0'RMP!`5W``&C``''GZ"@!Y^LH`)C``'9B:,`/]_`4`*C``&8GP:0`H6 +MJ``()HP`_W]!0`J,``9B$9@`"'Y:0`K.0P!A@``'9<`,,0G$S0``)-```9D` +M``?$#P!=Q!,`7L07`%5]40`.?-$`"ID``!3`'`!`?Q`\`8<`0,0/%$0``?-#`"7R-``R9```$S,,`'L`8 +M``'-@P`.D````,@)/3;(#3TWE,#X,<@1/3C(%3TYR!T].L@A/3O()3TU!*@` +M,,Z!/8+,P3V#S0$]A,U!/87-P3V&S@$]AQ"(``,$C``8R)8`/)5`^"'(T@`H +M?24``@E4``$$S``HF0#_^\B2`#@%$`!`?0K```3<.-TU\.!X!-PTW3/PX'@$W##=,?#@>`3<+-TO\.!X!-PHW2WP +MX'@$W"3=*_#@>`3<(-TI\.!X!-P`3<%-TC\.!X!-P0 +MW2'PX'@$W`S='_#@>`3<"-T<\.!X!-P$W1GP-!0:,#`4&3`L%!@P*!07,"04 +M%C`@%!4P'!04,!@4$S`4%!(P$!01,`P4$#`"QP'&L"1-,[`D'S/@?N!XX'C@ +M>.!XX'C@>`HD@/`%($0`X"#!!T0D_H!!*L0`A``"`"\D`O%"(0$!0B`#`>@@ +MH@0$$00"!!$%`@01!@($$0<"!!L(`00;2`$$&X@!!!O(`2P`)0!$(CZ!/``B +M`$0B_(!`(<$`X"#!!T`CPP"H((`!`1&$`@$;"@$@(,`'!!$$`@01!0($&P@! +MU`?A_P0;2`%$(OR`!!$$`LD'[_\$&P@!0B%!`$(@0P"H((`!`1&$`@$;"@$@ +M(,`'SW&@`*PO&(&:N!BAT0!@$@78X'C/<:``K"\8@;.XNK@8H;T`8!)DV`HB +M0(``V>X``0`O)@#P2B9``$X`!@!/`"``BB7_#^!X"B)`@`#9S@`!`&P`)``O +M)@#P7``%`"L(-0A*)D``"'$`V`(AOH#@(,4'0GD!X`(AOH#@(,4'0GGK!^__ +M`>`O+0$`0"5%``(F?/$``"```"A``>@@8@,O(`"`+R%+``(AOH#`((8!PB&& +M`.!^$0`@`$H@`!!*($`0#B)"`"\@"Q+.($6`BB7_#P@`!0`O+0$`0"5%``(F +M?/$``"```"A``4HF0`#H("(#+R``@"\A2P`"(;Z`P""&`<(AA@!*)@``0B#^ +MD,X@@@%$('Z0SB&"`>!^*0```.!X_!R(L?P<2+'\'`BQX.!XX'C@ +M>.!XX'C@>.!XX'C@>.!XX'C@>.!XX'C@>&&]C"7_G^WUX'_!Q>!X\<#AQ<]P +M@`#X'DV`SW6``+2%((6WNKBZ!"&!#P,````'N45Y+:"."B`2`-@`A<]Q@`#< +MJU$@@(),B<]P@`!0PC)J-GG'<8``$+]@@59X08`%\I6[8*&KN@3PM;M@H8NZ +M0:`+C:.X%07O_PNMHL'QP)(,S_]%P<]U@`#X'B>%,'`(]#"5%!0.,3!V!/19 +M'8(0T!4!%C!P#O3/<8``!"(\D104#3$P=0;TSW&``%PB6:F`X@STSW6``,0' +MP8V`Y@#9RB!!`"7R(:V.X@3T`=@A\$$H#0('?4$H`02G><]W@`#$!Z"/4R5% +M$4PE`(3&N8WV"B'`#^MRSW```,T;G]NA`2`!BB2##U$E@)$&\@#8#-Q;!,__ +MSW:``-#!%B9-$:>-H*_)=18E31$`I104`#%&K<=Q@`"0O@*U`(D'K0`90@$` +M&T(!Q/'@>/'`M@O/_PC(SW*@`,@?#AH8@`G(#QH8@`K($!H8@`L2`38"R"1X +M$1H8@`S(SW&```PZ+1H8@`"!`>``H<.XC>`I]`O(?]D*N21X+R@!`$X@@@<` +MV`\@@``$(0&`0B*-`AGR"R-`P!?TSW"@`(@@\"!0`\]V@`#<-@"&$'7/=X`` +MX#8&]`"'$G#D#\$&H*8`'P`4B0//_^!X\<#AQ0'9SW"@`+`?.:#/<8``6!T( +M@0"`K,%)P`R!`(#/<8``X"'/=8``#)%*P`J!H;@*H0B%X+@)\E$@P($'](X( +M``8F#V`"&-B+<:EP?@A@#R3:SW"``,`'((`"B8#@$O0$B5$@`(`.\@O(!""` +M#_[__P,+&A@P"\B&N(RXC[B0N`OP"\@%((`/`0``_`L:&#`+R*RX"QH8,-(. +MS_^+<##9D-H>V\H.8`X8N\]PGP"X_P+9-J`HP('@RB'"#\HBP@?*(((/``#J +M',HC@@\``/P`RB0B`.@'X@#*)2(`.@H`!H#@!_1:"^```-C:"Z`.!MBI`N__ +MK,#/<8``G%K@?PAAX'CQP*X(``;/<8``L!CP(0``0'C/<*``T!N`VE"@SW"` +M`#@=`(!1(`""`-D&\L]PGP"X_SV@T<#@?O'`Z@GO_P_9SW6```C(`!8`0``6 +M`$!5)4X4`*72"*`1!&W)<(X(H!$BE1Z5SW&``,`'VF#88`$0A0!,)0"`0*$3 +M]`*%\+C*(<$/RB+!!\H@@0\``.D(#A +MRB1-<.!XZ"`M`L]QH`!0#"6!`1A2`.!^X'CQP%H)S__/<(``^!X#@!B(I<&$ +MX$H@`"`,]`HAP`_K`X`)@%$@@(`,\L8,8`('V`'8`:7/<*``+"`0@`"E +MSW"``&0Q`("!X`WTSW"``'C'SW$``!`G[@GO_P6`$'@"\`#8SW&``.2H![$# +MA8'@#!D$!##T`(&"X,P@XH`$]`'8`*%,%8`0@>`F],]PH``L(-"`SW`!`-0Q +M0,`!V$'`"!P`-!'80\``V(RX1,``V!#9!-H(*+!\<"B#Z__F')%P4$H`0('>4$H`@0G>L:ZSW6` +M`)"^267GN5UE$_04%`XQSW.``-#!:'(V>N""\7`%].*2T7<'\B>*Y[FG:O7S +M`-@H\,:*@.8'](#?SW"``,0'X:C/=X``3!\%CQ!V!/2`V`6O"O#/=X``7"(9 +MCQ!V!/2`V!FOQHHV>P`<@`,'BH>Y`*W/<(``Q`=`B""H`=A'JPS<

/_^!X +MH<'QP`,2`C?7<@```$`!VL(BB@`7NL=R``X``(.Z['-`H^QR`*+Z#*`$*'#1 +MP.!_H<#@>*7@'_()]H/@%?*$X!?RA>`;].!_`=B]X`_R!O:MX!7TX'\"V,S@ +M#_*,($.'#?3@?P;8X'\`V.!_`]C@?P38X'\%V.!_!]@(V.!^X'CQP.'%SW6` +M`(0OJ7!`)8$;&@P@#R[:`=CA!J__81T"$/'`6@Z/_X+@"'6-]PHAP`_K`X`(@,]QI`"T15$@`(`/ +M\D0MOAL`)T`>;)!+D'M[97I3&9B`#9!4&1B`!?!3&9B#5!F8@T0MOALG=PZ7 +M5AD8@`^76!D8@!"751D8@!&75QD8@!*76AD8@!.77!D8@!2761D8@!676QD8 +M@*((8`@J<&4%C__@>/'`(@OO_^'%U@J`!,]P@`#X'@.`&(B!X"[TSW&```C( +MSW*```Q%`()@@6"@`((/'`@>#AQ0#8 +M"?3/<(``1Y$!W;(*[_^I<:EPF02/_^!X\<"6X.'%`-B,]\]U@`"TA:EPD@KO +M_P39"XV#N`NM`=AQ!(__\<":X.'%`-B,]\]U@`"TA01M;@KO_P39"XV"N`NM +M`=A-!(__\<"DP9#@`-G*($(`$_2+<$H*[_\0V0`4`#&$X,P@8H$(],]P@`!0 +MJQ^`];@"\DQP`=BDP-'`X'[QP)8+C_\(=\]P@`#X'@.`&(B$X!IQ2?*$YP#= +MC``E`,H@10//=H``,)%`)@`3]@GO_P39+HZPKE,A```1KD$HP""@N3!P8@`E +M``(@0@!CO_%R5@`&`(#B#O+/<:``T`\0$0"&8;I88!`9&(`E$0"&#W@#\`^. +M`-E3(((@#R&!`"1X+R8'\,]QGP"X_Q"N&('/(.('T"#A!QBA&(&>N!BA&(&^ +MN!BA`=A1`X__X<3\',B^_!Q(ON'`X<'APN'#_!P(L?P<2+'\'(BQ_!S(L?P< +M"++\'$BR_!R(LOP(P@7((!V.!_ +MPB`+`/'`%@JO_THD0`#/=8``^!X5)0,0`(-`)0X5T7#")`(!\"4-$<@5!19$ +M);Z!"?(*(<`/ZW*.V(VX=0>@`'3;R!`-!J5YR!A8`*"#!ME&><@5`!8D>,@= +M&!``@\@0``:&('^.=`H!$AT"C__@>/'`I@F/_\]S@`#$!@2#@.!$],]V@`!$ +ME!,6`I8`WX0J"`D`(8!_@`!(C`*C)(@!W8#AZZ.LHR+R'1[8DPP0!0`$)8$/ +MP/\``$$I!`;/<8``>,<4$08`!2X^`0`AA'\_`/__!"1!`1X>6)`@D(PA@H8! +MV<(A3@`JH^>C)(#/=H``@)#`N2JVSW:``'0E**Y`K@*(I*,!KB#P!(.!X!ST +MY@\`"0#8!*,"@R2(@.$2]">#'.`V>"2(SW"``"0B!X@0<0'9P'G/<(``<"4@ +MH`+8`_`!V`.C.0&O_P'8X'CQP,H(C__/=8``Q`8$A:/!@.``WR?T^@I```'8 +M!*4"A02(@.!2`@$`SW"``'`E`("`X$8"`@#/<(``6!T0@,]R@`"DD`"`(X(9 +M8<]P@`!@)0"`.&"2#B`0`J*`X!H"`0!V\`2%@N`\]`J%@.`/]`P5!!`0%040 +M"B'`#^MRSW```(H,Y06@`(HCC@M"A2>%0"(`!S9X)HA@P2:(`1Q",":(`AQ" +M,">(8<$GB`4<0C`'B(MQ!AP",)8*8`ZH$@``SW"``%@=$(`@@,]P@`!T)2&@ +M@@^@`.6E`]@$I<_P!(6#X#KT0H4GA4`B```6(42!`@1/RSW&``%@=`Y(P +M@<]S@`!T)2"!88,*N&)Y,'`%]PG8"Z6.\`6%@.`-]`2*@."K\L]P@`"DD+X- +M(!`"@(#@H_(%A8#@!O(%V`NE`=@)\,]P@`!P)0"`@."7]`#8E@B`!Y/P!(6! +MX&_TT@E``R*%1X5`(0`'5GA%B."Z&_*#ND6HSW.``,@ZR8//#?F88&IB#!8A1($"`*_*N"P`0@.`0 +M]`HAP`\"A>MR'!4%$`00A`#/<```BPR=!*``BB,0`)X+(!`"V"X+(!`(V"*% +M!(F"X`KT`=@`I0#8#J46"R`06M@BA02)@>`#]`'8`:4'A1SA%GD%B88@_XS* +M(((/```P0U@-8@3*(2(``H4GA1S@-G@%B(8@_H<$\@+8!*4E\`38!*4A\"2% +MA.$!V!WT#Z7/=H``6!T0AG7:'ML@@,]P@`!T)2&@#-F^"B`.&+L$AL]Q@`!H +M)0"`=@A@`2"!!J7DI038`Z4!V+D&;_^CP.!X\$2(SW"``+P&`)`0<@'>#_3/<(``O@9`D,]P@`"`D`J0$'(% +M],2E`-A*\`2)@.`:\L]P@`!P)0"`@.`4],]P@`"DD".`SW"``&0E`(!2#6`& +M.&"`X`;T\@Y@!P#8`=@P\,2E`=@L\`2%@>`L]`*%SW*``/@>(X)D@&BA(X)E +M@!S@::$GA39X)(@#@@#>-+`"V`39C@OO_\ERSW.``("00H4'A4`B`0<6>0J3 +M)(E$@GH,X`S)<\2E`]@#I0'8X05/_PP5!!`0%040"B'`#^MRSW```(D,]0*@ +M`(HC#@'QP%(-3__/=8``Q`8$A8#@H<$P]`'?SW"``'`EX*``V`^E`*4!I0+> +MR7!^#*`$Z7'/<(``D`8`@":`GA$`!J:XGAD8`,EP`-GZ"N__!-JV"J`1R7#/ +M<(``^!XC@$B!-)%3(@``Y@O@#.ESQ*7I<&WP!(6"X!WT`H4$B(#@%_()A8#@ +M%?3/VT#`BW"&""`.&+L:ANFEA"@("0`A@'^``#B.*X"AN2N@ +M!M@$I0#8!?`$A8;@!O(!V'4$;_^AP`;8`Z4`V-;Q\<#^"T__SW6``,0&!(6` +MX*7!"/0"A02(@.`9]`+8!*4$A8'@5/0%A8#@1O3/<(``6!T$@,]Q@``P1P"` +MI@@@$""!@.`S]`#8-_#/<(``6!T$@`#>Q:7/<8``9"4`@+8-(`$@@<]Q@``P +M1P'?!-H`H<]PH``L($`0!P#/<```/'A`P`780'#P```'TJ"N`$V';DI>EP*_#N".`$!=@$V`/P!=B`X`':!/0!V"'P*86! +MX0KR3*4+I0CP!(6"X!;T"86!X`3T`=@/\(#@\/4"A>()H`0#@`AQSW"``!Q" +M0@G`#P#8O@[`!N+Q`-AI`V__I<#QP/H*3__/=8``Q`8$A8#@I<&K]`*%1X4D +M@%9XSW*``"0B!"&!#P`&``"`X0'99XH@$(X`P'EP=@CTSW>``("0ZI?!BO%V +M!/(`W@;PQHK1$#`!=A!P`'?0L=#QD3&Z7`&V03:`-N8<[AS@@C@!-ASSW"``!A'P*CDI>EP +M'O``V,]Q@``81P"I`MDCI1;P!(6!X`'>$?0%A8#@'?3/<(``I)`C@,]P@`!D +M)0"`Y@A@!CA@@.`%\@'8T0%O_Z7`SW"``!A'P*CV#J`$!=@`V`2EH?$%V`NE +M`@W@!LEP`-G/<(``&$<@J.GQ\<`Z"4__SW:``,0&!(:`X'CT`H8$B(#@%/+/ +M<(``<"4`@(#@#O3/<(``I)`J#^`/`H"`X`;R'@I@!P#8'0,``,]P@`!8'1"` +M1X8@@,]P@`!T)0&``GD"AE9X!X`0<8;W`=@$IO4"````AH#@#/)1(T#`"O(" +MV<]PH`#0&S.@]@S@#Q[8SW:``%@=!(;/=8``Q`8`@(H-X`\FA8#@O`(!``2& +MSW&``&@E`("F"B`!(($&I0*%)X4"(]`*&)X8C"!8$$!P6!1!`)``'%B!``06(42``@$`C`@<:\@#9 +M2B3`<"AUJ"#``?`B0`,!Y1EA`]]*)$!Q`-VH(,`!\"+``P'G'64P=<3W&(N" +MN!BK`-W/=X``I)"EIQ@4``%`)4$`$'$GID?WAQ0`!E$@0(`&\@'8F@\@!PRF +M3O#V">`&"X8+R`0@@`____\#"QH8,$(-X`BKI@+8`Z8"AL]R@`!P)22(@.$/ +M]">&'.`V>,]Q@``D(B>)!(@P<`'8P'@`HBGP((*`X07R`=@#IB/P)X8V>!P0 +M!`#/<(``!(,$$`4`SW"``'C'!8`,'P`1!2A^`4`I@'*0<,HBS@?*((X/``"- +M#,HCC@\``$X%B`-N`,HAS@^DIDD&+_\!V`P6!!`0%@40"B'`#^MRSW```(X, +M90-@`(HCU07@>/'`SW"``'`E`("`X!WRSW"``)PJ`("`X!OT_@@`#H#@"/0+ +MR`4@@`\````\"QH8,#()``Z`X`GT"\@%((`/````U`L:&#`+R)"X"QH8,"8* +M0`;1P.!^X'CQP&8-#_\(=<]VH``X+@>&SW$``"0IJ+@'IKX)(`<-V(#E`-_$ +M#Z(1RB`B!,]U@`#8*D45`18'AB5X!Z;/<(``0"(LD,]P@`#X'AZ0$'$+R$4= +MV!,*\@4@@`\```#4"QH8,`O(D+@&\`4@@`\!``#\"QH8,#H)3_\`A;"X4@V@ +M$0"E@.#8#((1104/__'`SW"``$P3#X"`X`_RSW*?`+C_':+/<8``.!T$@0'@ +ML[BUN+BX!*$6HF(/@`"#X`GTSW"``-Q#8@J`#[()X``%V,]P@``X'0"`[[@& +M\@#9SW"?`+C_/:#1P.!^\<#/<(``3!,/@(#@#_+/"SN+6XN+@$H1:B!@^``(?@"?3/<(``W$,&"H`/5@G@``;8SW"``#@=`(#O +MN`;R`-G/<)\`N/\]H-'`X'[QP,]P@`!,$P^`@.`/\L]RGP"X_QVBSW&``#@= +M!($!X+.XM;BXN`2A%J*J#H``A.`)],]P@`#<0ZH)@`_Z".```MC/<(``.!T` +M@.^X!O(`V<]PGP"X_SV@T<#@?O'`SW"``$P3#X"`X`_RSW*?`+C_':+/<8`` +M.!T$@0'@L[BUN+BX!*$6HDX.@`"(X`GTSW"``-Q#3@F`#YX(X``!V,]P@``X +M'0"`[[@&\@#9SW"?`+C_/:#1P.!^\<#/<(``3!,/@(#@#_+/"SN+6XN+@$H1:BE@Q@``38"B4`@,HAP@_*(L('RB""#P``WP[* +M(X(/``!Y`;@`8@#*)&(`SW"``#@=`(#ON`?R`-G/<)\`N/\]H-'`X'[@>.'% +M`=O/.!XX'C@>.!XX'C@>.!XX'C@>.!XX'C@>.!XX'C@ +M>.!XX'C@>.!XX'C@>.!XX'C@>.!XX'C@>.!XX'@&N$4@S0#/<*``[">FH`J` +M`-L`L7ZRX'_!Q>!X\<">#<`"W@E`$98/`!&`V<]PH`#0&S"@T<#@?O'`:@H/ +M_QIP`=\`$!(!%/!:=1+P%2#`(Z"0`A`1`0'GUW4``/O_\']T]@PB@*\``/__ +M"?+/<```^_]2<.SUB0(/_\]V@`!0'0"&`>"!X`"F"?0!V<]PH`#('#&@:@Q@ +M$2AP!KV!O4`I`"2E>,]QH`#L)P:A`(9"($"``*;<]<]QH`#('`#8$:'6\>!X +M\<#/<(``0"X`@('@RB'"#\HBP@?*(((/``"O$\HC@@\``/,!RB0B`%P'(@#* +M)0(!&@@``-'`X'[QP'X(P!`*"\`-T<#@?N!X\<"&"0__SW"``/@>`X#/LFY)7K/<:<`%$A-H46``8`*NLFX9'I% +M>`ZASW&``!`Z#HF&(/\!6VC/<(``^'],J$^),(E`(!,#AB+_`4.ZAB'_`4VH +M0[GJ":`'+JC:<,]P@`!0'0"``>"!X,]Q@`!0'0"A"O0!V<]PH`#('#&@7@M@ +M$2APSW$(`(<0SW"@`.PG)J`#V`#9,B-5(#IQ3"4`HI0`*@!:<$HD`"`:\$`E +M@0$P>0:Y@;D0OR5_SW&@`.PGYJ%`)H$!,'D&N8&Y$+@E>,]QH`#L)P:A0"14 +M(,]P@`#`*B"`8'D&V))P]@`.`!$E`*7T\ZIP=@A@!8IQ&G"J<.(.(`6*<9AP +M0"A`(1!X$+B!N(>XC+C/<:``["<&H4PD`*`F\DPD0*`4](HEQ`:*)H0((O`* +M(<`/ZW+/<```L!.*(\4-"B1`!=T%(`!*)0``"B'`#^MRSW```*XHBB-&!DHD +M``#!!2``"B4`!8HE@@V*)D(/`-\$VY]SZ7"H(``,8;M`+H(A0"P!`5EA=7D` +M(TT!QW&``/220I&P?0:]@;U<>A"Z17W/FHD*1P+IX>N5Z4']#D0`C +MC0&P?0:]7'J!O1"ZI7K/=J``["=&IB.1P+EX>25X$'AH\4(B0""`X+(&[?]` +M(4$@SW$(`(80SW"@`.PG)J#/<(``4!T`@,]Q@`!0'4(@0(``H0?TSW&@`,@< +M`-@1H:$'S_[@>/'``-B-N#(/H`P&&A@P#,R&(/^*"/+/<(``U#8`B(#@B`]" +M!-'`X'[/<0,`0`W/<*``J"`MH,]Q@`!`"$"!`6H`H<]PH``X+@6`!""`#\`` +M``#7<,`````*\DC8SW&?`+C_&J%;H6G8&+@9H<]R@`"Z"?+&N@JZSW"``,!#R@Q@ +M#UEAT<#@?N!X\<#AQ<]S@``41`:#`X`@@,]P@`#4*ZB(2HA$+3X;`"&`?X`` +M4"M5>$R0:'`*NI(,8`]98!XSW"``-0K2(@JB,]S@``41$0J/@L`(8!_@`!0*S5X3)`B@VAP"KI%!&`/ +M66'QP/X-S_[/<8``>$HA@:/!0L'/<8``^!X5(1```!``(,`0#@:`YB\H@0-. +M((T'6/(2;19X`""2#X``$+\&$H`@SW&``-#!%GD`@2*1CN4('$0PRB!A``;R +MBW(.#B__`L&`X#?R`-C/<8``7`=`@0\@0`,O(0H@!"&`H`"A!O2`XL0((@C* +M("((KWAF#F`$$-D`WP0:Q".*(0@``!I`(*EPZ7$6#^`&#]H`$`(@P!(`!@0@ +M0`3`&A@`SW"``%#"MGC@H.&@SW"``'"^M'C@L!`F3I,O*($#3B"-!ZSU?07O +M_J/`X'CQP.'%"'4$\-X*0`]""V`/J7"`X/KUB07/_N!XH\%`P$'!!12!,`#8 +M@>%"P@WR@N$'\H/A#?0AP0#8#R!```,4@3`/($```A2!,`\@0``&%($P@>$. +M\H+A!_*#X0_T(<$#X0\@0``#%($P`^$/($```A2!,`/A#R!```D4@3"!X0[T +M`A2!,`JY3R$"!`,4@3`,N25Z(<$.N45Y)7@@P8'A"/0'%($P(L(&N0BZ17DE +M>.!_H\"CP>'%0L$)%($P0\*#X4'``-@*]H#AR/8*%($P@.'$]H/AP_8!V`<4 +M@C`&%(,P4',&\B+!,'/,(D*``_0!V"'%@>40]`H4@3`CPW!Q2O8+%((P4'', +M(ZJ`A/:`XLH@:0"!X`WTBB')#\]P@``T!R"@@>7_V*SNK6ZN+I$H%:A +MSW:``(!%SW>``,A5`_``%040`85`+0$")7@@AC!P`_(`IO`G0!%`>(#@\_// +M<(``.!T`@%$@@((&\@#9SW"?`+C_/:"Y`\_^HL'AQ4+!02@"`@=Z02@!!$=Y +MSW*``)"^QKDJ8N>Z$O0(%`,QSW6``-#!J7%6>4"!4'`%]$*1<'(&\D>)Y[KW +M\X#8`_`&B<'%X'^BP/'`SW*``!J_,F@V>3%BHL%`P4'`BW`(V9[:'MLR#V`- +M&+NBP-'`X'[@?N!X\<`(R)6X"!H8,`G(F[@)&A@P"\B*N(VXD+@+&A@PSW"` +M`/@>`X`8B('@#/0+R,]Q``!P*ZRX"QH8,`X/H`8/V-'`X'[QP.'%"'4^B,]P +M@`!L%$"`0"4`%`.Y-7E98?8(8`X*VIH/[_^I<.4"S_[@>/'`I<%!P$+!#!P` +M,1`<0#'/<8``?'XT&<`/,!D`#RP9P`XH&8`.)!E`#L]P@`!\?B`80`O/<(`` +M?'X<&``+SW"``'Q^&!C`"L]P@`!\?A08@`K/<(``?'X0&,`(SW"``'Q^#!B` +M",]P@`!\?@@80`C/<8```'Z`&0`(?!G`!W@9@`=T&4`'Z#!0& +M,,ES`-VV#"`&$!0',,]PH`"T#[R@6@^``\]P``"MWEH(``$(V`#9+@]@!IFY +M009`#O'`Q@C/_L]R@`#X)8#ASW6```Q'#O(`H@"%@.`3].X,8`$/V/8+X`<( +MV`'8`*4+\`#>P**R"V`!#]C&"^`'"-C`I?$`S_[@>,]Q@`#\*0"!'-K/`6(H.`,],]P@`"\!@"02'2`)$03`*P>VP/P&-MBH54B0`UX8`6A +M\05@#2APX'CQP`H(S_[/<(``3!,#@(#@#_+/"S +MN+6XN+@$H1:BSW"``&@&0(#/=H``Q`^@A@0B@P\/``#@!".!#P$````2:61X +M!WV@IIAU!"*.#P```$#/=8``P`_@A0.^9'X]><=_X*4$)`T`!"*"#P```(`& +M(T`#17D"N>1^!".##P(```#&>&1Y)G@O*`$`3B!!!(;A#1I8,`?RSW"```"J +M#I"`X"CRSW"``"0'`(C/(`V07T28.XXH+W`=F`X0'="?3/<:``B"`5>:"A%/`& +MV-OQH@Y@#`8:6#.6#X`%@.`*]`#9D;G/<*``T!LQH+H,H`^I&1B`'1F8@)H-8`P& +M&E@S+0:O_@#8X'CQP.'%SW"``&@&`(`$((`/#P``X"\M`1#Z#N__3B5`%`HE +M`(`-\@HAP`_K#HHCQ0HE`^__3B5$%'_8"KC/<:``T!L3H7_8$*'I +M!8_^X'CQP&(-C_X(=NR(*);/<(``B`:R;RASAB/S#[9]0BL1`L=U@``0OV"% +M[;L(<@+R1&CKN8H@PR\$]!X6D!`-CE$@`("D\N.Y/?3KNQ7R_]@'K4HD`'$` +MV:@@@`,H8@`A@P^``/C&]GL$JRAB`>$O>0"K7?!,(0"AD/8*(<`/ZW+/<``` +M+26*(PL$2B1``'D"[_\*)4`$[KD'C3(B0@0`(8$O@`#XQO9Y"?)$J039`"E! +M!"5X!ZT]\$"I#R!`!&/P3"``I);VC"##K\HAP@_*(L('RB""#P``+B7*(X(/ +M``#D`LHD8@`@`N+_RB4"!.X)[__)<`B6[K@%\@*.":T#\`&."*T`A>NX&/(` +MVD>M2B0`<<]Q@`#XQJ@@P`(X8O9X!!@"!``8`@0!XD]Z`8X(K0*.":TL\$PA +M`*'*(0GR!!D"!`39`"E!!"9X!ZW=\0`9`@0`V0\A000F>`>M`8X(K3D$C_[QP-X+ +MC_[/SW6?`+C__85Y8<]S@`!L!N"CW:7/V.E$!I8@`'8L0./_O'`1@N/_L]P@`!,$P^`K,&`X`#?#_+/"SN+6XN+@$H1:BSW&``,@D&8&AN!FASW&```R1`I%!D1"X!7H" +M'(0P,+HH@00`(;$$``&SW6``,@Z42!`@0?T6@C`!`'8W!T`$`&&&(A! +M'1@0Y@X@`1C8SW"``-0K*(C/<(``9"M$*3X+-"!`#E$@`('*(`$'RB$A#,HB +M@0\``)``RB.A![0.(0W`*R$&SW"``#@=`(#ON`7RSW"?`+C__:"U`J_^K,#Q +MP/8)(``!V,]P@`#8*B"`Z[D/\L]P@`#X'@"`Q!``!E$@0($%\E$A@(($V`+R +M`M@J#P``T<#@?O'`#@J/_E$@@,&EP9@/X@/*(*(`"\B0N`L:&#`Z#J_^`-W/ +M<(``W$,F@".!((&,O1(((`^Y80#9SW:``-@J_!Y`$,]PH``L(/"`SW```%0< +M0,`"V$'``=A"P$/!1,$%V03:`-N8<[ASV'-N""`$`"='$P"&B[@`IOT!K_ZE +MP.!X\<"."8_^42"`P:7!&`_B`\H@H@`+R)"X"QH8,+H-K_X`W<]P@`#<0R:` +M(X$@@8R]D@_@#KEA`=K/=H``V"K\'H`0`-G/<*``+"#P@,]P```,'4#``MA! +MP$+"0\%$P2AP!=D$V@ASF'"X<-APZ@_@`P`G1Q,`AJNX`*9]`:_^I<#QP!() +MC_[/=8``V"H`A>NX!?*2"X`%@.`*\@O(!2"`#P```#P+&A@P+@V/_L]SH``X +M+@>#P[B/X`_R'!,$``HAP`_K`'\HC@$?0`A5$@`((-]"8+@`6`X`GRSW"``!1$!H`#@`"`U@G``!X+@`6` +MX`KRSW"``-Q#.@[`#HH-(```V`GP"(Z)X`GT`(51(("`!?0`V)H+H`>,N,D` +MC_[@>/'`0@B/_GIP@>`!W<(E01.!X`'8SW>``/@>((?`>,@1#@8AAT0FOI'( +M$0,&!/1$([Z!$O(*(<`/ZW)`*PT$SW```,L;BB-'#`HDP`2A!:__!26%$\]Q +M@`!`(EZ7+)%0<0?TSW*``,B;08)0<1KR(@H``.H)(`"I<*H)``#P)T`3Q!`! +M!JEP);G`N1H(X```VH(.0`\+R)"X"QH8,/X+C_ZJ#J`0`=CN"6`*`-CF"6`* +M`MC/=J``P"^I%@"6JQ8!EJH6`I8%>:P6`)8`W:T6`Y8%>JX6`)8%>\]P#P`` +M^`0A`8`$(A``!",1`%IU%_(O*D$`3B*`!\]RH``,+?`B`@!1(@""`-H/(@(` +M!/1%?03P!2*2(`8A@8#K]<]P@`!`(BR0'I<0<0_TSW"``-@J`(`$(+Z/```X +M$`7TR@Y`!8#@*/1,(T"@"_2E%@&63R(`(8:X!GFE'EB0#_#/<(``0"(LD!Z7 +M$'$)\J46`)9%)4$1)GBE'AB03"-`H`GRSW"``$`B+)`>EQ!Q!/)."2`1!=A, +M(T"@"_2E%@"6125!$04A`00E>*4>&)`Z\$\B`"&&N,]Q@`#0*TB!!2!`!`5Z +M`-@(H0&'P!`#!H#C+RC!`$X@C0%7T@A<]R@`#L +MD4AP@"$#`"H,X`T#VB"%SW"``-`Z@"$#`Q8,X`V#V@T&3_[@>/'`H<'/<(`` +M2$D`@+8*H`9`P(MP!-F]VA[;P@D@#1B[H<#1P.!^X'C/<(``T"O@?P:`X'C/ +M<(``O"O@?L]P@`!\!^!_`(#@>/'`1@UO_@#92B2`<.!XJ"``"L]U@`#4+'#< +M`B4"$T0I/@P*(%VV2BSW,"`&P&8Z(!VV6BYJ)")0(>`")` +M#L"@!MI$H,]R`@``!T.@9:#FH`'A405/_O'`X<7/<(``?`?/<8``3!-O@4"` +M@.-/>`_RSW&?`+C_?:'//'` +M*@SO_P#8SW"``-@J`(#@N`;R[K@']`C8`_`!V'()``#1P.!^X'CQP%(,3_ZE +MP5$@@,$"W=P)X@/*($(#"\B0N`L:&#!^"*_^`-_/<(``W$,F@".!((&,OU8* +MX`[Y8<]V@`#8*OP>0!,`V<]PH``L($`0!P#/<```L!Q`P$'%0L%#P43!`=@% +MV03:`-N8<[ASV'"N"N`#`"?'`P"&@+@`ID$$;_ZEP/'`T@M/_E$@@,&EP5P) +MX@/*(*(`"\B0N`L:&##^#V_^`-W/<(``W$,F@".!((&,O=8)X`ZY80/8SW:` +M`-@J_!X`$`#9SW"@`"P@\(#/<```:!U`P`+80/'`_@I@!>'%@.`+\@O(!2"` +M#P```-0+&A@P?@]/_L]U@`#8*@"%Y[@&\J>X`*52"Z`.`-C/<(``0"((B(G@ +M"/*(X`[T`(51(`"""O3/<(``%$0&@`.``(`^#(``:0-/_L]Q@`!\!P4&[_\` +MH?'``=G/<(``T"L@H,]S@`#X0P:#`X`@@,]P@`!8'06`0(!H<-6Z\@C@#EEA +MSW"``,`'((`$B:"X!*G1P.!^X'C//'`F@E/_L]P@`!,$P*`!Q(/-H#@#1(.-@$2$#8/\L]RGP"X +M_QVBSW&``#@=!($!X+.XM;BXN`2A%J(&V`T:&##/=:``%`0*I0F%@.`G\@/8 +M$*4$I<]P@``(RJ((8`\#&A@PDMD#R)"YH!A``'(,X`,`V`F%@.`/\B@5!!`D +M%040'M@*(<`/ZW*,N+$&;_^*(P0&!QK8,P$:/<(``.!W*I0"`42"`@`T: +MF#,&\L]QGP"X_P#8':%%`4_^\<#AQ0AU`@S@`!38SW"``/@>`(#$$``&);@B +M#N``P+C^"V`'!-BN#^`,J7!&#(`,]@F`#"4!3_[QP.'%@N"AP2(!+@`(=<(, +MK_\`VL]RH/X4!H#@SW&?`+C_!O1(OQ@>$Y]'8.;_Z+<"#` +MSW&``+2%4R`"`(8@?P](J1QX":GL\8[A3@`%`,]P@`#X'@.`&(B!X!_RSW*` +M`.B"2'`^#F_^!ME`(@`",@YO_@;9#)*!N`RRO_&$X8WWSW*``.B"0"(`!18. +M;_X$V0R2@+@,LK'Q$]@"\!S8\0`1A_\HE +M(0!1)$""-?0`V<]S@`#4*RBK`=I)JTJKC!U$$`K;CAW"$(?@`MN/'<(0!O*( +MX`[T420`@@SR[!4`$6JX$'B0'000"MB4'000!_!DV)`=!!"4'400DAU"$)8= +M@A!5)<`85B7!%?(,H`T+V@"%B;@`I0'8`:7/<8``^!X`@<@0``:&('^."?0! +M@<@0``:&('^.K`X!`PB.A^`(\L]QH``X+@>!J+@'H:$&#_[QP.'%`-C/=8`` +MY*A*)$!Q)(6H((`"`-L/(P,`"R'`@`/T`>`%\&9Y`@F@`"2E!(6`X"`)X0#* +M(&$";08/_N!X"',X8-6[U;DP,=%@@'@R;@B>GIB +M%KC@?T5XX'CQP,(-+_Z8<@AUSW:``$"&]"9`$,]W@`#`A5$@0(+*($$`RB0B +M=,H@(@#H(&("]"8"$%$B0((#\@'@D.!&``8`+;O`N\]R@`!POK1Z0"N%`F"2 +M!+V&)?@3B;T/(T,`8+(`VA9_0*=!I\.YI7D%(4,!%'Y@ML]Q@`!@AA5Y`!D` +M`0+P@-BE!0_^X'[@>/'`X<7/<8``T(I!B<]U@`"`%8#BSW.``)PJ((,&\@'8 +M`*6"N2"C"?``VD"EHKF`X""CG`_""0#8/@QO_PAQ1@C@`0#8:04/_O'`\@PO +M_IAP`Q(!-@"1(8%`X/2YP""B``/@!""`#P``_/_/<:``U`SW6@`,@?42!`@,]P@`#XQR&``\@/\J`5`A#X%0,08GD" +M(E<`=A`!`2\GR"59803PA!`7`>)Q.AC$!1^%$''%]S!XH@D@!@+9`=G/<*`` +MU``4A%0`#R"&``!`2`4/!N!"8`'(0 +M`0&Z$``!`B$4!FX-X`9$P('@"_3/<(``#!T`D('@`=C`>`RX0L`"\$+&`\C/ +M<:``U`=9@(@9@`"D$`$`V:"X&((#NAB$`[>YI!A```/`]K@(\L]RH`!("$`B +M`",'\$`B`"'/04E52`G:,]S``#\_V1YSW.``/C'8X,( +M(<4`SW.@`-0'%:,`&D`%`B(`)0^C`B5``!NC\*//"F`SW*```"J.V-IH*05`!#X%0$0 +M@'`B>$7``=C/<:``U`L0H0/`-;C`N!>X`""!#P`.``#/<(``^,<"@`*X*^`$ +M((`/``#\_R5X['$`H0$2`3;L<""@SW"``/C'(H#L<""H#<@4(@$`,(GL<""H +M['#`L`/(E!`!`.QP(*`-R/`E`0#L<""P['#`L.QPP*#L<,"@!Q(!-NQP(*`# +MR""05!```1"Y)7CL<0"A`Q(#-@*``@0_R,HMPB\]P@`!0P79X`(B&('\, +M''@$N"5X`O"`V.QQ`*D#R#MV,(@S$(``!+DE>.QQ`*D#R!IV/)#L<""P`Q(# +M-L]P@``\19P3`0%O@R:YP+G`NPRY#;ME>2"@#1(!-@`A@`^``"BJP*C/<(`` +MK*DV>#1ZP+("D,`:A`,5)4$`>!H$`,]P@`#X'@2`&I#0&H0#1L#/<(``^,<" +M@,"A@.#*)XX3'@,N`,HACB/)=`_[@`WNCS+_``WOJXRB:" +M'P```0+YN,HF@A\```("_+C*)H(?```!`H#B"O+/$DHU$A@*!#]!X:6(,=$@"&!QH8,!T2`(9* +MP!T2`88$R""@'1(!AB&@'1(!AB*@'1(!AB.@'1(!AB2@5B4`$AX:&(`=$@*& +M0"\`)%!Y!2$5``02`3:&(O,/`!$2`8PB#(`!@4/`%_(:V!;PSW"``/C'"!`$ +M```0!0`*(<`/ZW)7V,]S``",$]$$+_^,N`#>T?`@V)IP`W`0>'(9!```WDP@ +M`*`#]`/(CO0'R`#!!""`#_$``/`0<93T +M`\CI<`,2`3<0N25X['$`H0K`0"%9,`$:&#`$R`,2`39! +MQ0,:&#`$&E@P(8``D`'%-+G`N31X`^!`Y00@@`\``/S_'64-$@$V!_`5(D`P +M#A``!@)]%2)`,`X0``80=7;W`\S/<9\`N/\8H<]PH`#\1%V`!"*^CP`&``!A +M]$P@`*`,\@3(4(A3(L$`AB+^`T2ZQ!B"`#"HSW"@`!0$Q*`'R,]QH`!(+!VA +MSW"``/C'`H!`(%`@$G`,!`%H;3Q!H&!OP'@!J&P\>"_"/+/<8``/#P%@0'@!:$Z=P/(Z7'( +MN0B(#+@E>`,2`3<0N25X['$J=(0D`I$`H4`A33`;\L]QH`#4!X`9P`0#S"IR +MR+H0N$5X['(`HLRA`=@4&1B`S@\@$`'ESW&@_H0`SW"?`+C_-J`#$@(VDA(` +M`>JX!!(!-@;TDA$#`5$C@((V\JJXDAH$`)(1``&JN)H-(`F2&00`$-G/<*`` +MT`\0&%B`)!`!AL]R@``(SD62,'D"ND5Y#!A8@!39$!A8@,]Q@``(SF>11I$8 +MV1"[97H,&)B`$!A8@,]Q@``(SFF12)$0NV5Z#!B8@`;PSW"```C.RJC/QP(*`!Y<]P@`#XQP*`$'6W +M]\]P@`"HDB20E.'`(88/``"3`,]PH`!H+/`@0`#/<8``/$4@@<]UH`#4!R5X +M#:(#V!*EB@A`#%$G0)(%\IX.K_\!P`;P`]@3'1B0%!V8DTP@`*`7\L]PH``L +M(#"`!<`P<`'=RB6&$P0@CT\@````SW```#45[@H`!8#ES"(X'/<(``$#H0B!"X,B&!#P``V`*?N(#A`=G`>0^Y)7C/<:`` +M_$0-H4PC`*`-\L]PH`#T!V`8P`3/<8``/#P#@0'@`Z'/<(``J)(DD)3AP"&& +M#P``DP#/<*``:"SP($``SW&``#Q%(($`VL]VH`#4!R5XSW&@`-0+#:%,IAX) +MX`X&P!D6`);`X*(`#@`-S%$@0(!+\@/=(!Y8DP'8%!X8D`02`38`%@1`!QH8 +M,0`6!4`!&E@Q!,J6),0B5,@P@"&(/X#1+C$&0(`4*G/`##1("-@3(SW&@`"P@L!```2^!9.`P<,H@A0\2*`@`A/?/<``H"``&&A@P +M`-X-S`0@@`\```((@N`*]`02`3:*(`0``@Z@"9@1`0`-R,]Q@``0JL]R@`#D +MJ!1YP+$FDL]P@`#XQP*`&6$P>2:RK=C/<@"[`+M"#R`'!;@#R!J0<@Y@!PT2 +M`3;/=8``J)($E<]V@`!@O?0F`1`&E3!P%/(&":`%!\@*(<`/ZW($E0P5!!'/ +M_]J\#@>%$@0,/QP.'%)_+/<(``^,H1#8#J$!V!49&(`J"R`00=A1($##$_(!V<]P@``(12"@P@R@ +M#@'8SW&``#0]#8$!X`VABB4($B[PSW&@`/Q$'8$Y@00A@H\````(`-T']`0@ +MOH\`!@``&?(`W?JXRB6"'P```0+YN,HE@A\```("@.(*\L]S@`"X/%"#BB4( +M$@'B4*->"P`0!O`#V<]PH``4!"6@_0#O_:EPX'CQP'X(S_T(=<]V@`"(#0". +M@."HP5CTBW?I<,]Q@`"<2B()[_T@V@'8`*X`V(^X"QH<,`#8%1H",,]V@``` +M`-=U``#^RJ"V!?0'P("X1\#/<*``K"\:@%(@``!1(`"`"/(!EH"X`;8'P(&X +M1\#/<(``"$^@B#H/8`2JKL]Q0W6H$D#!BB$:"D'!*XX$ID;`Z7!CP0T<0C// +M<8``?#Y$P<]Q@`#H/47!(-D!VCW;'@Q@#!>["-@J#F`%`=D"V<]P@`!$)22@ +M(0#O_:C`X'CQP+(/K_TO)`<`@.#/BHZ.CS"$A@`'9RB$A`,]U@`!`(LB-B>87],&#Q!8.%E$F0)$1\LR5 +MOI.Q=@WR@>`*\H#@"?0`@\00``91($"!`_0`V8'BRB$A``#>SW6@`+0/W*7Z +M"6`&B'#/<(``1"4$@%$@@(`1\L]P@`!4.`"`@.`+],]P```6"88(P`2!X`7T +MI@L`"PKP`-F>N<]PH`#\1"&@X'C!H-RE@.=$#&(`RB!B`+X/@`2`X`3T!@]` +M#@3P-@]`#L]U@`!<#@"-@.`&]*8-0`T!V`"M)0>/_?'`L@Z/_<]V@`#4*RF. +M:(XP<\]U@`#8*AIP!?0`A>RXD_)*CL]P@`!D*T`@D0%$*SX+)W!5>`:(@>`H +MKE3T`(7/=X``4"M$*3X+0"*I"@N2JP"/"`X0;RN@Z`!0"%C+@`I0B.1"@^"S(A0"Z!X`'8PB`! +M`*8.X`\*KBB.2HY$*3X+`"=`'E5X3)"`XL]P@``41!/RSW.``$`B:(N)XP?R +MB.,-]&"%42,`@@GT1"D^"S(A02Z!X23R"G$E\"UJ"KDB\$0I/@LR(4`NSW>` +M`%`K@.#*(&(`0@[@#PJN"(Y*CD0H/@LO<3AG57A,D(#BSW"``!1$X_,R(4$@ +M@>'?\T`J@0("<@`"P@4(<`W@:%1Z6Z#J_]#B"` +M``BEQ+40A\6U!J5U!:_]"(7QP,]Q@`#DJ``1!`"X<,]R@``4,4`L@``5>!4@ +M0`$P(@8`3"8`@0OT"B'`#^MRBB#,#'$"[_Z*(\4"3"8`@"OR3"9`@!+R3":` +M@!?R3";`@!#R"B'`#^MRBB`,#8HC10M!`N_^"B6``48*``#1P.!^(@D``/SQ +M3"1`@`/8`O(`H0#8!:$$@:"X!*'&"&```]CN\>[Q\<#/#!P!K+,]@2*@>`&]`6*@>`!V`/R`-B`X(`(`@#1P.!^SW"``%"K()!$(0`# +MB.`T]`#;SW*``.2H9:($@E$A@(&@N`2B'O0$DL]Q@``\J0'@)($0>#!P!++, +M]P2*@>`&]`6*@>`!V`/R`-B`X"P``@#/<(``[#0#@(#@"_(#V`KPSW&``.PT +M`X&`X`/R8Z$$V&4!``#@?N!X\<#//'`4@N/_>X/S__/<(``$*E@@,]R@`#DJ*B`8*+/=H``9#$$@JBB +M`-G`AJ"X@>8EH@2B$O2"X\PCXH`3](#ESW"``.PT(Z`%\I(/S_\)\&(*```' +M\`';8*(HHJ"X!*)A`X_]X'CQP.X*K_T`V,]QH``L(%"!SW:``.2H)([/=8`` +M$*F!X0BE!?0ECH'A`O(!V(#@)?0JAH#A'/(&ADX,K_T.((``SW$``!`G,'#0 +M]\]Q@`!XQR6!F2'-"C!P2O<%\,]P```0)PBE`M@(\`#8"/`)AH#@]O4!V`"E +M`=CA`H_]SW*``.2H)((/(0$`)**I!B``"=CQP%H*C_W/=J``+"`0AL]U@`#D +MJ`>ESW"``(Q"_@_@#0#?`!4%$$PE0(`2\DPE@(#,)>*`/?(*(<`/ZW**($P- +MBB,(![$'K_Z*)(,/!)6`X&SRS@S/_\]P@`!XQP6`*(69(,T*,'`!V,(@#@"` +MX%[RSW"``'PTZ:#7<0``$"=O(`L`@.`2\@2-@>`&]`6-@>`!V`/R`-B`X`3R +M^@W/_T;PSW```(@3"*4^#L__/O`$E8#@&_0EE0B%@>'`(($/``"($P/R&WC/ +M<8``>,`&]`6-@>`!V`/R`-B`X`3R@@W/_PCPSW```(@3"*7& +M#<__!)4%M>2U$(:5`:_]!J7@>/'`SW&```2#08'/<8``>,/'`X<4`V,]S@`#D +MJ`"CSW6@`"P@$(4!V<]R@``0J0:C$(4@H@:BSW"``'PT`X@DJXP@@X8DJ@3R +M):HEJ](+(``#V"$!C_W@>`'9SW"``.2HX'\@H,]P@`#X'@.`SW&D`!Q`"(#` +MN!-XP;@2H>!^X'CAQ0#:2B0`=,]U@`#`A<]S@``XADAPJ"```T`C`0(4>4"Q +M%B4!$$"A0:$!X$HDP',`V:@@0`+/<(``<+XT>$"P`>'/<(``6`=!H,]P@`#H +M@DRPX'_!Q>!X!?!"><=P0````,]R@`!XQT6"4'$W]U,@0P5P<<`@C0]````` +MP""-`.!_(G@&\&)Y`B"`#T````#/,=E@G!Q-_=3($(%.F)0`5A +M+;W`O0/P_]TB"H`$@.`(\L]P@`!`(@B(A^`"V`/R`-C/<8``,)%WB<]R@`#( +MFR&",',$\B""@.$$]`'?`_``W\]V@`#X'B"&Q!$!!E$A0($K\H#E*?0CACB) +MA.$E\OH/P`V`Y\]Q@`!$.QCRSW*``)@'`H(!X`*B`-C/`0H:H-C_V&"8`$@.`.\L]P@`!`(@B( +MB.#,)6&0!O2>#^`-`=@^#(`%C"7#GTGR@.<1\L]Q@`"`%0"!@.`+\@#8`*'/ +M<8``G"H`@:*X4@E@"0"AW@E`#<]Q@`!XQP:!12!``0:ASW>``+2%"X]1(,"` +ME`F"_0N/42"`@-@,P@,V#(`#]@F`!(#@Q`HB`,H@(@:`Y0CR`(;$$``&42!` +M@1'TSW&``%PX!(F`X`OR`XF`X$@*X0O*(.$`E@H@`!78L09/_>!XX<7/<8`` +MA!4`B0';@.!AJ23RSW"@`+`?>:#/<(``6!T(@*.!8(`"@1!U`-H8],]P@`"< +M%0"(@.`#]`'8"O`!@0(C#0#7=4P`0$MY]T&I2'"!X`/T8:%"J>!_P<6BH>_Q +M@.`!V,(@#`#/_^`-J`X`3T$]TN\,]R@``PD4AP(@RO_0S9SW&``(05`(F`X`[R +MSW"``%"K`)"&(/P`C"`"@`;T!9)DDF=X`Z%")0`3*@O@!8AQ"B4`D`STSW"` +M`%"K`)"&(/P`C"`"@`P/P?_!!6_]J7#@>/'`F'"X<9S@RB+&!\H@A@\``.,. +MRB.&#P``@P#``J;^RB'&#TPE@('*(L8'RB"&#P``Y`[*(X8/``"$`*`"IO[* +M(<8/`-K/<8``P'^>NA4A00$`@0$J`@%&>-8.8`<`H='`X'Z=!^__!=G@>/'` +MX<4`W<]P@`#`?TH*+_\.'%SW*``(06((H`W>"Y9-C*($$# +MX;G//'`X<4`W<]PH`#`':"@J7!J""``J7'/<(`` +MA!:CH*2@*0-O_::@X'CQP.'%3@@@``AQ@.``W1_RZ@R@#S+8Y@R@#Q[8SW*@ +M`,@??Q(`AL]S@`"@%C"X(8O$N#!P`=G"(4H`@.`@J\?VK:*!X0/T!-@!J]D" +M3_W@>.'%X<;/CN8#]X#F`O0`WL]P@`#(FWB(@.,$](#F!/0`VP3PPH`$V\]P +M@`#X'@.`&(B#X,P@(H',(.*!S"`B@LP@8H(#\@#8%_`*DA!V!_0+DA!SS"$A +M@/?S`=C/<:``R!\-H.!^X'C@ +M?P#8X'[@>.!^X'C@?N!XX'[@>.!^X'C/<8``P#L2@0'@$J$-R,=P@``$O>2RHSW"``(06`H@0<`N\F(((```V*((```N#X`/`=A` +MP('!VMP")P`3:@Z@#"#:J7`DV;S:'MO^#.`+&+L!V%$>&!`2\(#@$/)."2`) +M5!8`%@#?0,>I<"39O-H>V]H,X`L8NU$>V!/I`&_]J<#@>`S:SW&``-0L`N`/ +M>"<9`H``V"@9`H`+$8"`)AF"@`@1@H`I&0*``>`/>`L9`H#/<(``9BM$*CX+ +M,B!`#N!_*AD"@.!XW=C/<8``\"L$J0N)!^`/>`6I4-@&J6_8!ZF:V`BI"=C@ +M?PFI\<`."&_])-JMP<]Q@`",2\8(;_V$P,]R@`!`(@B*A^!\`@(`SW>``-0L +M"!>!D`H7@Y#/=8``9"M`)9`1`VU$*3X+)W5U?6:-SW:``-@J@>-,`@(`;)+/ +MDE!S"/1`A@0BOH\``#@0+`(!``#=J7(+\$0I/@L`(8-_@`!0*U5[ +M;),!XGUE1"D^"S(@1`Z0$#'$(P"!>`D`J]1"@^"S(@02XO +M<,=P@`!0*X#ARB%B`#5X#)`*N$'`SW"``!1$`H!"Q>EQ@B%#!4/`0"3`,(8, +MH`P-VEX.[_\-V)X.S_\J#8`/`=A$P(7!VMP")P`39@R@#"#:A,`DV;S:'MOZ +M"N`+&+L"V%$>&!"6\`+9SW"@`+`?.:!1%@$6@^$(%X"0N'`G]$0H/@LO<#(@ +M`2#'<(``4"L*(D"`RB)B`!4@@P"D%@(7;)-0$!V<(A00`U>*46`1=, +MD#!RSW"``%@=!_0-@`"`4Q8!%A!Q9/+_V`,<`C!$+3X+,B!!+B]PQW"``%`K +M"KV`X`R0"KA!P,]P@`!8'0F`0L4@@``A``%#P.EQ0"3`,((A0P6B +M"Z`,#=I^#>__#=BZ#<__1@R`#P'81,"%P=K<`B<`$X(+H`P@VH3`)-F\VA[; +M&@K@"QB[`]A1'A@0"!>`D$0H/@LR($(N+W#'<(``4"L*(X"`RB-B`!4@P0`L +MD8'BI!Y<$`'9PB%!`#5X#)"E'AP0SW"``%@=#8``@%,>&!`$\%X,S__5!2_] +MK<#@>/'`;@T/_9@0`@`$(H$/````"#MY!"*##P```!`E>\]Q@`#X'J2!Z;I6 +M)4X45B4/%9@0@0`(\H8A_P-$N2]GB;_I<1GP42(`@KP5`A$,\L*Y@"4"&3]E +MZ(\]93"-97_P?T5Y"?##N3QY/V8^9C".Z(]%>8@8P`-E>5T%+_V,&$``\<#A +MQ0/(I!`!`)@0`@!1(0"`'V"6`"`-JL:"(/ +MP`W/B!*]`T2`3<# +MR.2Y>!`"`2'R42%`@,]Q@`#X'B2!5!$!`0GR?A`-`2)]8GT")$,#*_"`$`,! +MSW6``/"]`"-$`'"(=GU@E0`C#0&$$`,!NV,;\*00`0#TN0CR<(C/<8``\+UV +M>6"1!/""$`,!SW&``/@>)(&`$`T!5!$!`3UENV.$$`T!NV.`$`T!N6%^$`T! +M0GTG\(+@(?0#$@TV#EQ#&)"`V`ZF*0,O_7!X\<"Z"@_]SW&``/@>\"$"`%8B100(@E8B +M!`51(,"`BB`(`,H@(0"\&@0`2B0`<@#9J""`#\]U@`!,4/R*+F7D?B\H@0-. +M((,'SW"``#12;V``)4,`X*M$$H\`Y'XO+H$33B:/%^Y@R*O(@E$FP)`/\AV* +MAN'3(*8`+R@!`$X@C0?/<(``<$ZH8!#PSW:``'10+F;.9;R*Q'U8$HX`Q'TO +M+4$33B6.%\A@$*L!X4HD`'(`VZ@@@0#5@2C@#$>2\I +M00!.(8X'R64L&$(``>-*)`!Q`-BH($`%SW&``&Q.?8H)80`D#``!X&1Y+RE! +M`$XA@P?/<8``<$YI82"LP0$/_>!XX<7AQL]SI`"T12D3`(;/<8``R#K(&0`` +M*Q,`ALP9``#/<*4`"`P#@.09```.$P"&$'HPN-09``#0&8``#Q,`AM@9``#/ +M<(``C*O4B+:(Z!F``WB([!E``PV0\!G``"S@`B""`_09@``"($(#8GCX&8`` +M_!D``,'&X'_!Q<]P@`#D1`:``X`@@,]P@`#.'%X<:8<,]R@`"D +M%@6"(()F@LJX$+C*N04A`8`!@LJ[$+O*N`4C!0!G@@*"RKL0N\JX!2,'`&B" +M`X+*N\JX$+L%(P8`)/(`%`X`+RA!`$X@@P<`V`\@P``2?00@0P&D?F5^`!R` +M`]J"I'[%>WJB>8($((X!!"#``:1[Q7MYHGB"I'L$(4a@8HM_UP<;@?\'% +MX'CQP"((#_TZ<`6!H('*N!"XRKT%)0V0`8$F@`1_!R>/ +MDT''%O2`YEOT:@S@!`K8@.!7\@HAP`_KP3:"O"`X`CTSW"``*`J((!@>0W8"R>`DP7R2@NO_PK8"/"`Y@;T&@JO +M_PK85@@``-RE"-Q/!^_\H\#@>/'`X@[O_`#:SW.``*06.X.Z@P#>#R8.$*1Y +M!"9`$$(@`(#*(&(`+R8'\`'=RB"!``?R'(,D>.8.[__%>*EP$0?/_.!X\<#A +MQ:'!`=A`P,]U@`"D%@J%42``@`SRBW`$V6?:/=O&"J`+%[L*A:"X"J7E!N_\ +MH<#@>/'`8@[/_!IP*'5(=VAV.&-FV3W:"@N@"Q>Z@>`)]`IPX@J@"ZEQZ7"R +M"J`+R7&9!L_\X'CQP"X.S_RFP2AU&G)@P`#8`1P",`'8`AP",`,<`C"+<'8) +MH`>!P8#E!?($P0IP8'T%P@/!@.$.]`HAP`_K/'`S@WO_`'9HL'/=8``I!8:A5N%!'H#>__ +M"G"Q!>_\HL#@>.!^X'@`V<]P@`"\+N!_.*#@?N!X\<#B"0`$SW`!`+A4@.`* +M\L]Q@`"D%K@9```;@9&X&Z'/<`$`)%2`X`CRSW&``*06'J$;@8&X&Z'/<``` +MW&.`X`GRSW&``*06E!D``!N!B+@;H<]P``#@8X#@"O+/<8``I!:8&0``&X&) +MN!NASW```.QC@.`)\L]Q@`"D%IP9```;@8JX&Z'/<`$`+%V`X`KRSW&``*06 +MV!D``!N!F;@;H='`X'[QP.'%H<'/__BB,(``C8 +M`-D>#N__*'(`V+T$[_RAP/'`-@SO_`C9SW*MWN^^+@L@`CIPL@\@`"IP@^!( +M\L]P@`#:$YJCW`>>' +MY[@'Q?\J<,]RK=[OOK8*(`(0V3X/(``J<(/@#O+/<:W>[[ZB"B`"*G`&#^__ +M*G"#X,H@(@#I`\_\\<"*"^_\`]JFP1IP_@E@#(/!`\'/<(``D`\4%`[[Y"P`3""G"`VSX* +M(`*8<]()(``*<(/@0/(#P\]P@`"P#T*%\"#!`,"E@.$,%1`0P:4(\L]W@`"X +M#_`GP!"`X`;TP*7!I0#9&?"$*@P#F@A@`"]P#B"!#P````$@I0/`A"@,(_`G +M`1"""&``+W`.(($/`````2&E!(6!X`WT`(41>(P@!XW"]\"E,7F,(0>-P_?! +MI0#8%0/O_*;`X'CQP*X*[_P$VJ;!(@E@#(MQSW```!O2`-VI<88,8`"I<@#! +MSW```!S2=@Q@`*ER`,'/<(``[`T!PA4@00``D0+!!;I*#6``17D#P(#@W``% +M`,]V@``@"-+8"+@9V4(,8```VL]P```BTD`F`1)R"F``!-K/<```(])`)@$3 +M8@I@``#:SW```"#2A,%6"F```-J%Q\]P```ATNEQ1@I@``#:`H87V;8.H`M` +M)@(2`X87V:H.H`M`)@(3!,`7V9X.H`N$P@7`%]F6#J`+Z7("A@#9>@\@`(NY +M`J8#A@#9;@\@`(NY`Z8$P`#9"+AB#R``B[D(=P7``-D(N%(/(`"+N2*&,7D9 +MX04I?@`CAB]R4'1GA!2E^`"]QS"!%@(;W`\`!Y1!U,@?._P/`$'7&]P'9 +MSW"``"`()*``V-T![_RFP/'`<@GO_`G:J<$(=MX/(`R+<38/[_PAP`AQ0M@N +M#&``!;D,%`0P`,')<`;""B6`#ZW>[[XV""`"`L.2#B``R7"#X"KR`,$%PL]P +M@``T#@#=\"!```3!"KH$(H(/#P``_,FY17GZ"F``J7)6"R`/!=@@%`0P`,') +M<`;""B6`#ZW>[[[J#^`!!\,Z#N__R7"#X,H@0@-)`>_\J<#@>/'`L@CO_`+: +MI\&:<$(/(`R#P<]P@`"P2P"`!]E%P,]P```1TIX*8```VL]P```2T@#9D@I@ +M``#:SW```!/2`-F""F```-K/<```%-(`V78*8```VL]P```!1`?99@I@``#: +MSW"@`+0/MWN^^0L>*<`3!`\(>VYAS2B4``$HF``#Z#N`!2B<``(H.[_^*<(/@W/+/ +M=8``(`@(%180#!43$`[80,!!QD+'BG`$P0/"'MN8*<`3!`\+AVYAS +M2B4``$HF``!6#N`!2B<``.8-[_^*<(/@BO(BA:.%+R#'!<]RH`"T#QRB!,+/ +MP"CP]M5?F"FSW:``*@/ +M57Y@IB+TM@V``PHAP`^`X.MR#_+/<*``_$1T$`0`9!`%`,]P``"Q$WT$[_V* +M(TD*SW```*T3BB.)"DHD``!E!._]"B4``8#@'?1R#8`#"B'`#X#@ZW(/\L]P +MH`#\1'00!`!D$`4`SW```+$3.03O_8HCB0S/<```KA.*(\D,WO$"((`E&6$" +M(8&$#_("(((D#'H2#"``+W`$P@(@`2#/<(``D`]5>""@`B7`)+E@`B%!A`_R +M`B5")`QZZ@L@`"]P!,(")0$@SW"``)@/57@@H`#870:O_*?`X'CQP/X-(``` +MV,]P```-T@#9#@A@``#:SW````S2`-D""&```-K/<```%=+/(/(```VL]P```"TJ#9FKG2#R```-H)V(RX`-G&#R```-H4 +MV(RX_]FZ#R```-H`V(RX_]FN#R```-H1V(RX_]FB#R```-H"V(ZX`-F6#R`` +M`-H!V(ZXSW$``/__A@\@``#:SW````O2`-EV#R```-K/<```#=(!V6H/(``` +MVL]P```2T@#96@\@``#:SW```!/2`-E.#R```-K/<```%-(`V3X/(```V@#8 +MT<#@?O'`1@V/_*/!BW$!W;(+(`RI83@S"!B@68/(``/V`#8\02O_*/`\<#AQ:'!BW'N"B`,`=K/=8``[($` +M%`0PSW"``)`-0"4!'Q+:Z@T@``#;`!0$,,]P@`",#:EQ`=K6#2```MO/<(`` +MM`TD;1S:W@T@``##`-BA!*_\H<#@>/'`"@RO_`/:H\&Z<)(*(`R+<0'!SW"` +M`#P.`-_T($X``L'/<(``5`Z`YO0@5`#/<(``(`C@H.&@S":BD,PF8I',)J*1 +MRB7"$P+T`-V!YLPFXI#,)N*1S"8BD@/T`=V$YLPF8I+,)J*2S";BD@+T`MV& +M#<__JG#/[[ZB"N`!R7%B#N__JG"#X';R`,"`X,P@HH%0](#FS"9BD,PF +M(I%*]`+`@.!(],]P@`"0#[5X6G#@H,]P@`"8#[5X>G#@H,]P@`"P#[5X&G#@ +MH,]P@`"X#[5X.G#@H,]P@`"@#[5XX*#/<(``J`^U>."@JG#)<<]SK=[OOBH* +MX`&I`RAJG"I<[[[>">`!BG-.#Z__JG"#X!+R +M`,#/<8``(`A`@02^!KC88!4@``7'<(``])(A@4*P([``V.4"K_RCP.!X\<"D +MP8MQ'@D@#`3:`,`!P02X-7C/<8``]`T0868-(``"P0#``<$$N#5XSW&``!0. +M$&%2#2```\$`V*3`T<#@?O'`H<'Z"R`"BW(`P*'`T<#@?N!XH<'AQ>'&N'#/ +M<(``R)L0$`8`SW"``$`N!8"8<8#@H<&&)/D`NC0&T?;IB%7K/<8``N)1(8=1^"'.&(_T/>WLZ8D&*97A(]UE +M%27-$;YAPHYE>LESAB/]#WM[N6$CB65^*'.&(_T/3"0`@'M[97D3\L]UJ@#@ +M!W.%42,`@`;R2*4)I2JERZ40\`BE2:7*I2NE"O`)ND5XSW*G`!1(`Z()N25^ +MQ*+/<8``C$4`&8`!SW"``)1%`!A``<]P@`"010`8``&AP,'&P<7@?Z'`\<`^ +M"8_\Z@B``X#@J`K!`@#>%_!PW`(E`!-$+CX7+W?>#N`,)W!")0`>U@[@#/A@ +M`-D`)H`?@`!"+""H`>;/=8``U"QK%8"0$':F]\]P@`#`0ZX.P`S/<8``V"H` +M@:&XKKA%`:_\`*'QP,]Q`((!`,]PH`"L+SR@SW"``%0X`("`X`STSW"``*08 +M`(""X`;R9@]``]'`X'ZR"T``:@B@!6_8@.`'].H*X`X*V)X+0`#R\?+QSW*` +M`%0X(((&>>!_(*+@>,]R@`!4.""")7C@?P"BX'@$*(`/```OND(IPG10>D0J +M_@("($`.$'B`X`3R`>)0>H/@0+$#]H#@`_0`V`+P@-C@?N!XM04/_O'`*@B/ +M_#IPSW6``%`=`(4!X('@`*4*]`'9SW"@`,@<,:!B"N`.*'":#B`%!]@:<,]V +MH`#L)^N&F@D@!RIP"Z8`A4(@0(``I0;TSW&@`,@<`-@1H6X+(`4*<#$`K_SI +M]L*)$`$X02O_0HE``3QP#8/3_P(=SIQ@.(:S?=(=?0G@!,5(8$C4@_O +M_PIR8;V`Y0'F-O=M!T_\X'CQP`H/3_RAP0AW@.(:<0#>S_=(=?0G@!,>""`` +MBW$`P!0@C"-AO8#E`+0!YC3W00=O_*'`\<#6#D_\H<$:<,]V@`!0'0"&`>"! +MX"AU`*8*]`'9SW"@`,@<,:`&">`.*'`^#2`%!]@(=QX(H`.SV(#@%?*+<:(+ +M;_T*<``4`#$`I0"&0B!`@`"F!O0`V<]PH`#('#&@"@H@!>EPU09O_*'`423` +M@/'`!?(J#\__`_#B"```T<#@?N!X42/`@/'`!?)"#\__`_#Z"```T<#@?N!X +M\S?=(=?0G@!/P(8$C5@_O_PIR8;V` +MY0'F-O>Q!4_\X'CQP$X-3_P(=X#B&G$`WLWW2'7T)X`3&@@@`/0@@2-AO8#E +M`>8W]XT%3_S@>/'`'@U/_!IPSW:``%`=`(8!X('@*'4`I@GT`=G/<*``R!PQ +MH%(/H`XH<(X+(`4'V#IP;@Y@`Y/8@.`9\K!]0"B/(8&_$+VE?\]PH`#L)^:@ +M`(9"($"``-D`I@;TSW"@`,@<,:!2""`%*G`5!4_\X'C/<8``^!XC@<]R@`#4 +M!S(A@P\``!\#`:(R(8$/```9`V&R2'`@L@C9<]H>V]4`(`L8N^!X\<#/<(`` +M^!X#@`F`42!`@.!^X'CQP%H,3_P( +M=2AV((5"(0&`RB%B`(#A`-@%\OX)X`RI<`'8)(6`YM`A8@#/(2(`T"$A`,\A +M80"`X"2E-`GB#,H@0@.%!$_\X'CQP!(,;_R*(@0.SW"``)`&`(#/=H``T(HF +M@$`F`!2:">`+!.$!AL]U@`#X'B*&R!T8$,]R@``D(LD=6!`AEB>J((X$((`/ +M``8``(#@`=C`>"&J!JH`WC((H`G)<,]P@`!5'?X/[_[`J&8-0`.`X`KRZ@U` +M`X#@!O1B#B_^R7`7\`+8X@J@`0'9-@F@#@+8(X5(@3214R(``&X*X`D!VP#9 +MGKG/<(``^"4@H-4#3_S@>/'`L.#AQ0AU@_:YY/'`+@MO_)AP08'DNK")._)RB<]V@``0O_)M +M]G_F9C3*]KX($84`22#```CRSW:``%#!MG[!C@/P`-['<(``4,&V>`2("",# +M``@C@P,`(T`!22##`Q9M=7C/I(6XA0&` +MI7@$((`/````"`9[`O!C@>B[F!G```#="?*D$0```-V7O9&XE+BD&0``420` +M@!SRSW"``/@>Q(#`NLB&!":.'P!````^OA[FV'I%>_Z[F!G```WRI!$``(4E +M`12,N)&XI!D``)P90`,=\/^[$O*D$0(`A24!%):]F+V-NI&ZI!F``)P90`,D +M@!"!GK@0H0OPE+V6O9P90`,D@!"!GKB?N!"A@0)/_.!X\<`."F_\`]C/=H`` +MN"H@AD!Y@.!M\B"&8'D$V(#@:?(@AF!Y`-AGN(O@"O0!Y`-A"\,]P@`#`*B"`8'D!V(#@`=C`>#CPSW6``,`J((5@>0'8@>`1\B"% +M8'D!V(/@"_(@A6!Y`=B"X`?R((5@>0'8@>#>]0'8'O#/<(``P"H@@&!Y`=B% +MX`'8P'@4\,]P@`#`*B"`8'D!V('@`=C`>`KPSW"``,`J((!@>0'8@^`!V,!X +M@>`7\B"&ZW5@>0#8&G#/<(``P"H@@&!Y`=BX<#?8"B'`#ZERE-O)!F_]"B0` +M!(4!3_S@>,]P@``,D2`0@`"!X,]Q@`#$!@OT`-K/<*``M`]!^SW"```R1(!"``('@SW&``,0&!?0"V`2A`_`!V`6AX'[QP,]Q@``8 +M"`"!@>`.\@HAP`_K#/<8``Q`8%]`38!*$#\`'8 +M!:'@?O'`SW"``$P3"H"`X`_RSW*?`+C_':+/<8``.!T$@0'@L[BUN+BX!*$6 +MHL]P@``4"``0!`#/<8``&`@`$04`3"0`@,%V!?RPN//:+/$O>83A-*@#$@(VC/;/<`,`A`"@&@``BB`(``8:&#`+\(H@$``&&A@PSW`" +M`80`H!H``.!^SW.@`+`?`=I9H\]S@`!8'6B#@.!@@P7R(GMP<(/W`-@"\$AP +MX'[@>,]RH``L('""@.`*\@(C0@#7<@"````&]U!PAO<`V`7P<'!^]P'8X'[Q +MP%8.+_R8<*7!*'>X!".`#_\````8N@5Z;WD(N?_8"+AD>"BX!7E%>0C= +M]"2``R=X1,`V"N`,$!0`,1(4`C%AO4`H`00%>4=Y1,$0%`(Q%"2`,X#E0+`! +MYBGW4R7"!4"G`!0-`0?9!_`0?10G3!``M&&Y%"1`,+M[3[T`D*5[@>%P>WA@ +M,O<$((`/````_Q"X!7I`IR4&+_REP.!X\<`V"```]@@```H)``#1P.!^X'C/ +M<8``="5`(0`#52'"!5!P1O<`V0084`!0<+WWX'[@>/'`.@R@!@#8<@\O_0#8 +MSW"``&!$2@A/_<]P@`!`1$((3_WJ#(_^$@V`"`#8I@L@`X#92@F`#)8-@`** +M"\`,!@C``8X(``,`V.(*[_X(<<]P@`"$%@"(42"`@`CRSW&@`,`=`(&@N`"A +M&@L`"^((``.J"J`!_]@^#T`!BB"%#PAQM@J@!0ART<#@?N!X\<#Z#"_\BB#_ +M#\]UH``X+L>%!Z7/<*``5"X+@-.X!B8`<`\`__^""N`-%MG&#\`!QZ4U!0_\ +MX'CQP'8+H`8!V*X.+_T!V&(.@`[1P.!^X'CQP.'%`-W/<(``7`>@H,]P@`#H +M@JRP,@K@#*EP2@T/_=X/X`NI<)(*``3B"L_]J@Y``8H@!@H(<2(*H`4(<@H. +M;_RI<.(-3_S5!`_\`-G/<*``["``I0GT`=G/<*``R!PQ +MH"X.8`XH<`"%0B!`@`"E!_0`V<]PH`#('#&@G@D``3D$+_RDP/'`H<&+<+X* +MX`T!V88)``&AP-'`X'[@>/'`H<&+<&H*X`T$V0#`42!`@-@/H@;*(*(``,!1 +M(("`8`K""P#`42#`@+@)`@<`P%$@`(&<#L(&,@_@#`'8SW&`KN`!['`@H`'( +M['$`H<]R@``4@(HD@7T`V:@@``+P(D,`['!@H`'A5@D@`0#8H<#1P.!^\<#A +MQ:/!`=A`P,]U@`"D%JEPZ@G@#5S9]@G/_CJ%&X4D>#R%!'F!P$H*+_]!P0'! +M&X4D>$'`525`'\X*+_^I<<]P@``<&,(*+_]`)0$;BW"N#^``!-D6"R__`<`` +MA8#@!?0%A8#@,`P!_UX)S_XY`R_\H\#QP.'%SW"``-!(`("BP4'`@<`!W:X) +MX`VI<4#%BW!N#^``!-D-`R_\HL#@>/'`H<&+<)()X`T!V0#`42#`@2\D!P`` +M'``Q#/('$@4V"B'`#^MRBB#%``$`;_TGVW(-H`%`V#((``$.#D`'H<#1P.!^ +M\$''`?JEP.@G@#0/9!@@``8#F`_("A0+P`(6- +M`B_\`Z7@>.!^X'CQP.'%SW6``"@=J7#2".`-$-D`%0003"1`@!'R3"3`@!_R +M3"0`@1/R"B'`#^MRC]B-N)C;>0)]>!!R^?=1 +M(P"`"?(`%@!!`_``V!4AC```I`'BA>*Z]P/,UW````!``=C"(`H`%[C'<``. +M``"#N)VXG[CL<@"B`1("-NQP0*#"#N```HFAP-'`X'[@>/'`X<7/=8``-`BI +M<"8/H`T(V0"%SW&@`+@>`J$!A0.A'@[``+D`#_P5!L``\<"DP8MP`@^@#1#9 +M`\S7<````$`!V,(@"@`7N,=P``X``(.XG;B?N.QQ`*$!$@$V['`@H`#`42`` +M@`/`!O0"P28-(`$`V@7P^@Y@`@'!"@[``*3`T<#@?@D````%````\<"R#<`` +M406`"^!X\<#AQ;3!BW6I<,X.H`T4V0#`AN#,(.*!!O1F"&`#J7`(<23P@N`' +M]`8)8`.I<`AQ'/"!X`;TA@I@`ZEP"'$6\(/@S"`B@@?TH@\@`ZEP"'$,\(3@ +M!O2B"&`#J7`(<0;PB>`>](HA1``#S-=P````0`'8PB`*`!>XQW``#@``@[B= +MN)^X['(`H@$2`C;L<$"@6@W@`"APL0?O^[3`"B'`#^MR?-B-N'?;B[M*)``` +MO00O_0HE``'@>/'`X<6BP8MUJ7`6#J`-`ME""F`#J7#:#,``=0?O^Z+`\<#R +M#L_[`!800*'!3""`H,HAQ@_*(L8'RB"&#P``CPS*(X8/``",!.'X``1(YF#&`,!&[/<(``*)`: +M@!)P$/(D%H`0@.`C\JEP!-F9VA[;T@J@"AB[`-@D'@(0&?#'=X``.(X+AX&X +M"Z?/<(``Q`8O@(#A`=H$\D2@!-@'\`#9+*!)H"2@!=CF"8`#J0;O^Z'`\<#A +MQ<]P@`#X)0"`!""^CP#````(],]P@``@C`"(C"##CP7R]@NO_0'8SW6``-"* +MJ7`:#:`-4MFR#(`'W@O@`*.%C@P@`:EP"''/<(``L$`F#$`,_MG/<(``((Q= +M!N_[(*C@>/'`SW"``,"0W@R@#0W9J@O``&(.0`;1P.!^X'CQP*X-[_L"V:+! +MP@R@#8MP`Q22,$PB@*"/]@04A3`*(<`/ZW+/<```A`R*(X4),0,O_0HD@`0" +M%(`PSW:``,0&A"H(*2]W(!X"$,]P@`!@A3/=8``2(Q` +M)1$2_66+<*EQI@M@"P+:0"4`$NH+H`U")($A`">`'X``2(P($`4`SW"``'C' +M!8!3)4$%$''*(<8/RB+&!\H@A@\``(4,RB.&#P``A`&``B;]RB2&!'8,H`=* +M<$HD@'``V:@@@`2$*0@)+W`R(`(@@.()\@@5!1`P(00@#"1`@17R`>%`)@`8 +M>@G@``39`=D,&T(@AQ4`%H"XAQT8$`8/8`,H<,T$[_NBP`HAP`_K.!^X'C@?N!XX'[@ +M>.!^X'C@?N!X\<`6#._[!-FCP0#>0L;&"Z`-BW`#S-=P````0`'8PB`*`!>X +M`""!#P`.```&%``Q&W@3X`0@@`\``/S_)7B=N)^X['$`H0$2`3;L<""@`,'L +M<""@!!0!,>QP(+`&%`$Q['`@L`84!#%1)`"`#/(!$@4V"B'`#^MRSW```$\F +M.0$O_6G;&@[@`P'8`L$`Q25X0L#/<*``+"!`$!``P+T!Y0+P`>8&%``Q$':` +M``H`@N4$%``Q@L<6]!MX$'CI`'@$'C:#N`# +MJ7+L<0"I"/#I<8/@`#1 +MP.!^X'CQP*7!BW`&":`-!=D`P%$@`(`5\L]P@`#X'@.`&(B!X`WT`-B:N,]Q +MH`#('P^A`<"D&0``P]@:N`ZAH@^``*7`T<#@?OD`8`8`V.!X\<#/<(``(#AZ +M"*`-*-F"#X``T<#@?N!X\<#AQ0`6`$""X,]U@`"D&`"E'_0`V<]PGP"X_SV@ +M'-D5\,]PH`#(.S:`1"$"!S:`AB'_""5Z-H"&(?\(17G/` +MX>OU*@^``""%A.%>``T`,R9!<(``9$A`)X!R-'@`>#@0!`!8$`4`"B'`#^MR +MSW```)DAO0;O_"_;-@]@`U3842!`@!/RSW&``%0X`(&!N!(.X`T`H0GP1@UO +M_@'85@J``P/PZ@^`!&4!S_O@>/'`+@W`"+H.@`#1P.!^X'CQP$8+8`D`V,]P +M@`#X'L@0`0;`N8'A`=G`>1(*X`T\$(``T<#@?N!X\<#AQ<]U@`#X'@"%Q!`` +M!E$@0($-\@HAP`_K#F`,`=C/<(`` +M0"((B(?@'O0!A<00``91($"!&/(&"T_]SW&``'C'!)`E@0JX,'`.\@HAP`_K +M/'` +MV@M@"0#8/@W/_,]Q@`#DFP*)6@G@#2")T<#@?N!X\<"BP8MPP@Y@#0C9`,"` +MX,]Q@`"@.`"A!_(&%``Q`[$$%``Q`K&N#8``HL#1P.!^\<#"#Z_[@=BAP6#` +M`\P`W\]V@``4"`(`#H0'8`O`"V!IP`,!Z""_\"G'/=8``K#@#$@$W7I6!V&"& +MX@_@#0HD``3/<*``+"`0@$`=0!1,(("@$:5('0`43?(`AHC@$_3/<(``;$/Z +M#``,6@IO_A38:@K@!`38X*;/<(``&`C@H`#8A>`#\@#8"/#/<(``&`@`@(3@ +M^?4!V"\F!_`.\LX+H`,4V(#@"O3/<(``4$,F@".!(($>#0`,`(:`X`/R`-@( +M\,]P@``8"`"`@.#Y]0'8+R8'\`7T.@C``H#@"_+/<(``X`<`@"\H`0#6#6_] +M3B#`!P4'K_NAP/'`G@ZO^X#8H<$#$@$W8,#/$ +M*!\`,B9%'@`F0!Y,)0"``*$-\@HAP`_K">;V\$"[_R*)(,/SW&` +M``2MP@H@"ZAR`(^$*!\`-"%!+L]P@`#AJR`!8`00'A&[)@CX0K'P``(8!_@`!,K4.(4'&,!^7_+W4`)8$?@`#(LP`E +M@A^``$BT6@Q`!WH(;_X4V((/H`0$V$"/`*)(,/'0'O_$HE``#@>/'`X<4@V\]QH`#('&FA`!8`0,]RH``0%`RB`!8% +M0`'=3"4`@,HAP0_*(L$'RB"!#P``+"7*(X$/```)`=0`X?S*)$$#&!I``6@9 +M0`$#V`^BN:%JH?H(@`"5`X_[\<#AQ:W!BW6I/'`V@JO^PS9 +MK,':"6`-BW``%``Q@.`P],]U@`"X*B"%SW:``(0O8'D`V(S@0"2/,!#R((5@ +M>0#8D.`,\B"%8'D`V)'@!O(@A6!Y`-B2X`;TZ7#)<1C:!?#I<,EQ+MHF"``+ +M`=A@'@(0%X:`X`P,X?O*("$``!0`,8'@$_1`)(`PSW6``(0O0"6!&_H/X`HN +MV@'8-X5A'0(0@>'@"\'[%@B``*$"K_NLP/'`)@JO^Q?9M\$R"6`-BW`CP$HB +M0"!3(-``AB#^`TP@`*1"*!$!#!P"-(_V"B'`#^MRO +M_`HE``1(%`4P(,!`*(X@SW6``!"_UGY1(`"`P&5!+4\#P+^^9H8@]P]>](#@ +M#?0*(<`/ZW)SV(VXBB//!%$'K_P*)``$BB!/!0IQ6@_@!*AR`<`"P0IR!@VO +M^V9N@.`]\NEP4@Z@#0IQ#12`,(4@P0`-'`(PBB#_#U/``(:IN`"F$L"&(/L/ +M*+@/KDHD`'0`V:@@``/_VKAA0"B#('9[$N!X8$"H`>$*<$X-H`V+<<]P@`#X +M'O`@P0/`$0`&#R``!,`9&``/CH'@!_2`Y\P@HJ/`"`(.`=\"\`+?<@A@`@IP +M!_"`X,HG@13*)R(2@>=0`@(`((;/<(``^!X#@!B(*'6!X(8E^Q\1\FX+@`*` +MX""&&O+/<(``0"((B(?@%/1!*4`#42``@`[R$\#HN!+""O*&(OL/02H$`D^. +MD'($\JBX4\`3P!+"!GE$>"5X@.4`IH8@^P\+\H#@RB`!!,HA(0`\"J$#RB+A +M`PX>0A0`V,]Q@`!0PA8A`01`A@"A];H!H07T`-B+N`&A]KH%\@&!12``!@&A +M]@KO_(MP#12`,%$@0($A\E@4`#$%MEH4`#$&M@66@.`9\OH*@`*`X!#R!I91 +M($"`"?(J#:_\"G!:"``.!=@2K@#8!;8'\`IP`-F^":`##]H-%(`P42!`@'[R +M4!0%,0*6'MHO(4H!,'DD>"\I`0`"(D``$'A`*`$A)7C/<:``P"^B$0&&$'@0 +M\"\M01`"(D,#`-T/)`A4!X`"NHZZAIJ*FI*:EIKBNN:X!P+JN +M`L$'I@/`**8)IH'`/@T@#0'9`<`'IGIUB_""P"X-(`T"V0&.`\$!W^.N`>`! +MK@+`*:8(IFH.K_N+<@0@``4O)`>@`MDCK@*N`,$AIF_R$FD6>,]R@``0OP!B +M2B$`(`\A42`MN%,@$`"*(%0%B@O@!`IRSW&``%P'0($O(DHD^*X0'D`4!"*` +MH!0>`!0`H0/9(ZY"I@.F!_2`X@7RX@B@!"#8^:X%V`.N(,!Z#N``$-D`P#)H +M-GD`(8(/@``0OXHA"`"BLB"B!MDCK@#9(@]@`P_:`,*`V1)J%GC'<(``$+\H +MJ"FH!]@#KL]P@`#X'O`@``3/\`0`08$(8$$P!A8``#9(*//<(`` +M<+XAHU1XE@W@#:"P@.`'\AH-P`T(V`.N^JY`(U,@(/'`BB!5"P#9>@K@!"AR0@_`"I8*0`#1P.!^X'CQP.'%`!8- +M0`/,`=K7<````$`!R,(BB@`7NL=R``X``$8((`M3)0$0425`D,]Q@`#(.@'8 +MRB`A`/$$;_L`H>!X\<"AP8MP=@L@#0'9`!0%,$PE`(`+]`HAP`_K/'`Z@H`!\]P@`#X'BP0A`!,)`"!"?3) +M$``&42!`@07R@@W``1#P3"1`@`SRSW"``$`B"!"%`$PEP(',)6*"!O3*#H_[ +MT<#@?@HAP`_K8+]P0DA"\````D#"2` +MCP```"0L\H+@5``-`(+@!O2`XB;R@N8D](#B!?+,X4``"0#/<(``P"H@@&!Y +M!M@0=A;WSW"``/@>\"#`!,,0``8!V00@OH\`!@``!"2`+P````C"(4$`*[@0 +M<43W`-@#\`'8#W@#\`'?Z7`$)($O`0``P"ZYSW*``#1:*6(P=P'9PB%-`(#@ +MS"$B@!CR0B%`((#@(`?M_P'E`A"`(,]Q@`#030AA@>`<\@HAP`_K\"#`!.MRBB-8#\,0!`9XV(VX50=O_`HE``4#$(`@ +M"&&"X`GR"B'`#^MR>MB-N(HCF0(2\>X/8`U*<,]P@`#PO18@@`0@D,]R```8 +M%0DA@0!6#R``(+"M`6_[HL#@>/'``!:!0,]P@`!<12"H`!:$0``6@4#/<(`` +M944@J``6@$!0)+Z!RB'"#\HBP@?*(((/``#:%,HC@@\``($'S`9B_,HE(@#/ +M<(``O`8`D(#@!?(:"4`--@A`#>X.``#1P.!^X'C1`^`,`-C@>/'`!@EO^P#9 +M2B0`ET@K@#3_8 +M`,`$%`$Q!Z7N#>`,@KD<'0`4#@X@``$:F#.-`&_[HL#QP`#8:@T@``02@3`$ +M$H4P"B'`#^MR.-B*(P\!I05O_$HD``#QP.'%SW6``-@JJ7"`(``X,/VCN#)]@HAP`_K2FMSW>``&0K52;`&$0I/@LG<,8-X`P+V6F-`V]$ +M*SX+,B!`#HK@RB')#\H@B0\``-$;RB.)#P``:@&J`"D`RB+)!X+@RB'+#\H@ +MBP\``-(;RB.+#P``;`&*`"L`RB++!X'@`=G3]D(@1``*)`!Q*'"H($`#1"L^ +M"P`G01X5>4:)(HDP`/>,H(0`*`X!WRSW"``$`B"(B)X`?RB.`5]`"& +M42``@A'TU@C/_(+@!?+.",_\@>`)],]P@``41`:``X``@%(/3_W>"P``:08/ +M^PHAP`_K!D%H`4!V.!X10#@"`'8 +MX'CQP/8(8`+AQ8#@SW6``-@J#O0$%000"B'`#^MRSW```+X;BB,&!$4#;_RX +M`(#$$``&42!`@1OR[!4`$<]Q@`!XQR6!"K@P +M<,HAP@_*(L('RB""#P``Q!O*(X(/``">`,$'H`P!V.!XN0&@ +M#0'8X'CQP*'!`-E`P0`6`D``%@!`@>(:\@/,UW````!``=C"(`H`%[C'<``. +M``!%(``#G;B?N.QR`*(!$@(V['!`H.QP(*`?\%(((`:+<`/,`=G7<````$`! +MV,(@"@`7N,=P``X``(2XG;B?N.QR`*(!$@(V['!`H.QP(*``PNQP0*"*"B`` +M*'"AP-'`X'[@>/'`7@PO^P+9SW>``'1%"@S@#.EP0(?/=J``["?/=8``P"K@ +MNDOR*X9$(H``AB+_#B*ZH;D4NK2Y!2"#`&5Y*Z8$((`/$``"``0B@@\0``(` +MSW&``.0%17@+H2"%!-Y@>H8X.P`4@A6!Y`=B%X#7T`(=1(,"` +M,?+/<*``1!W%H,.@Q*`I\,]PH`#('`'9/J`+AH&X"Z9J#P`&((5@>0'8A>`3 +M],]P@`#X'@.`"(!1(`"`"_(`V92YSW"``.0%*Z`+AI2X"/#/<(``Y`4`V2N@ +M"X:TN`NF+@D``+D##_O@>/'`SW"``'04^@K@#`+9%@D``-'`X'[@>/'`-@LO +M^P#:"'4H=L]PH`#4"SB`0B$!"(#ARB&,`$`F`!(0<6`/10T#S-=P````0`'8 +MPB`*`!>X`""!#P`.```';@0@@`\``/S_)7B=N)^X['$`H0$2`3;L<""@(KX& +M\.QQ`*$$Y6&^@>8`A3KWX@@``#$##_O@>/'`X<7/`/,UW````!``=C" +M(`H`%[C'<``.``!/(($`G;F?N>QP(*#/<*``%`0#V26@`1("-L]PH`#4"TV@ +MSW"@`$0=-:#@?N!X`]K/<:``%`1%H<]QH`#4"PVASW"@`$0=5:#@?@/:SW&@ +M`!0$1:'/<:``U`L-H>!^`]K/<:``%`1%H<]QH`#\"PRISW"@`$0=5:#@?N!^ +MX'C@?N!XX'[@>.!^X'C@?N!XX'[@>.!^X'C/"2_[N''/<(``*)!H$`0`2B``($PD@(#*(L8'RB"&#P`` +MD0S*(X8/``"W!]`&)OS*(<8/SW"``,0&!X"$+`@)`"&!?X``2(Q,)0"`%GG' +M@3[TSW"``/PEB@ZO_(HA#P_/<(``D"5Z#J_\(-G/<*4`"`R@@%,E39`3\H'E +M$_*"Y13R"B'`#^MRSW```)(,BB.?!YAU:08O_`HE``3_V`;P_]@(N`3P_]@0 +MN,]Q@`#D!0RAK:'.H0#9D;G/<*``T!LQH)X.X`L!V!_PSW.``.0%#H.`X!OT +MSW&``"Q/SW*``/PESW6``'0EBB3#?PIPJ"#``@]A%27#$^>#\"(.``'@_F;' +MH[T`#_LX$P0`"B'`#^MRSW```),,BB,?#.$%+_P*)0`$X'CAQ>'&SW"@`!0$ +M`]DCH`W(SW*``!"K89+/<8```*K$BA0A#0!HM0`@@P^``""J..'`JV*"%7D& +MDF"A`Q(#-L`=!!`$@J`3`0"&(<,/)7B@&P``P<;@?\'%\<#F#\_Z"'9>"B`" +M*'6`X-$E8I,!V`/T`-@$N,]U@`"8QQ1X"66!X1UE"O2*"N`+J7`"#J_]`8T` +MV`"M`845`"_[`*;QP*(.@`AR"R`+`-C/<(``0"(($(0`3"3`@1/R3"0`@A7R +M3"1`@AKR"B'`#^MRSW```,H;N]L%!2_\2B4``((,C_Q6#T`-T<#@?L]P@`#8 +M*@"`42``@@7TO@@/_/7Q_@F/_)X,C_P+R*ZXK[@+&A@P"\B'N`L:&#!N"P_[ +MY?'@>/'`(@_/^L]Q@`!`(@R1SW6``/@>WI4.)@Z0SW"``+@L#I#*)F(0#+$! +MV$H.(```V68+(`@!V)H)C_R!X!CT`(7$$``&42!`@0/R@.80\@'8"'$>#R_] +M"'(+R)"X"QH8,`O(!2"`#P```-0)\`O(KKBON`L:&#`+R(>X"QH8,.H*#_L- +M!\_ZX'CQP((.S_H-$@$VSW>@`+PMSW"``/@>+J<$@`#=1A`1`58@4@16(),$ +M#1(0-U8@%`5&(,`@`Q("-@T:'#"D$@``A+BD&@```9*BP8#@AAI$`PCRSW"` +M``"K]"!``(#@"?(!@NZX!?10(``@+R`((%,@?J!*`P$`SW:``,@Z:18`%@'@ +M!!(#-FD>&!"D&T`#`9*`X$KRSW"```"J-'B`$`$'@.%"]-`0`0%3(<&`%/1R +M$@$!X)(B?[@2@0`B?_!_X!C$`Z02`0"&(?./!O)HO_!_X!C$`W`2#P'@$``! +M(9+B>/%PPB<.$,(AS@-T$@`!.&"X$H$`=!M$`Z"S.&`0>)`;!`"^&P0`$(H0 +MJP&"`:,(B@BK$HH`VA*KEKHR\!8-(`**(`4!#X?WN/KS3X?VNE,BP`(F\H[@ +M2?>G%@`6MKH!X*<>&!`<\&2X!!(!-A!XD!D$``0B@`\```#P++AT&40#H+$0 +MJ:&Q`\B^&40#88"HJ88C_PV$NV&A$H@2J?:Z/@(!``#8EKCUN@02`3:D&0`` +M$O+:":_^`-@$$@$VI!$```0@@@\"````+;H%(@($+R"(($#P`8%1(`"!4O(T +MRE")22#$`/)JSW"``!"_]G_@8/:X,]S@`#0P@!CSW.``%#"5GM!@\]S@`#X +M'F2#>(-E>@0B@@\````(1GB8&0```-B6N/2X08&&(O\-'_*`XE+RF!&"`$`B +M`"E(8,]S@`!0DD#`(,+#NEQZ]"."`%;P"B'`#^MR--B,N%_;!;N*)(,/Q0$O +M_$HE``"8$0,`Z;N<&4`#(_*`XH"XI!D``"SRF!&``,]R@`#X'D."AB#_`T2X +M,B0`((FX0,`@PU2"9'J&(_\#AB+_#D2[>F)/>L]S@`!,3O0C@@`@\%$C`((* +M\H#B"O*8$8(`0"(`*4A@#?"`X@7T`-I(.!X]#W`KK/<8``$+]6>D%A\;D(\K@6`!8!X+@>&!`-\(!P$'B&'000 +M:A8`%@T:'#0!X&H>&!`=`^_ZHL"AP?'`R@K/^@AU1L#HO2APT``A`$AV`[A` +M()`%1"4"%B.Z!"6/'P8````!XD$O0!0$)8$?P````%A@-KG/`%\E,@`0`X8.V]`BF!(P_RSW*``!A20)(%*CX``"&` +M?P``_S\NN%T`(``985D`(``5>5$E0))4`"$`)L6WY2``"P`S:%,E`A#/<(`` +ME$[P((``!2D^``H@P`X!X`?PBN7`*.$`P"BB`,]Q@`#X'B.!P-HT@:1YAB'_ +M#B*Y.GK:>AEB,'@(W/L!S_HS:%,EP!`<>,]R@`!<4O`B```6X04I/@`*(,`. +M`>`4V8,'[__:>>!XSW&``%@=)(%!*((%U;@@@4$I@P75N0)YSW"``'C'8GH% +M@,FZ!2B^`"=QSW"``&!$`X``@.!_.&#/<8``6!TD@2"!02B#!=6X02F"!=6Y +M$'%;8TKWSW*``'C'18)980)Y`>,#\`)Y0"N`!9D'[_\E>/'`F@O/^OX([_I0 +MV47`2B``(,((;_Z&Q4P@`*4$%0$43O<%P-=QK=[OOA4@``0@H$`@4"#R]23< +M+P'/^@HAP`_K\"`!`8HC"PU,)0"`0"$"!GAB)O2H@7IB +MH*))@4&@7(E(J%V)2:@J$8(`2J@K$8(`2Z@L$8(`3*A-D4>P5Y%(L$B!!"*" +M#P`&``"`X@':P'I2J%214Z@H@<"Y+:@<\$PE0(`:]&)B2*%!@$FA2(A&"('@`0@.$.\H#B#/17%@`6.&!7'A@06!8`%CA@6!X8$,]W@`!T!@"'@.`` +MW0/R6!Y8$U@6`!9#PD#`5Q8`%D+!$-F^VD'`BW`>V\(+8`D8NU8>6!-5'E@3 +MH*?-!Z_ZI,#QP%X/C_K/=8``B(($%0400B5!`(7A-@$M`*+!]29!<(``&$A` +M)P!R-'@`>`+8`*4!V<]P@``,'2"P'@L@"2AP`H7/#"!,$``\@ +M0``"I<]P@`"P'#5X0*`8$P4!#!,&`,]P@``D-0#9-*C/<```[*Y`P`6#$!,' +M`$'`&HL[BT"#+@X@"F/#/<(``#AT!V2"HSW"``-`<)X#/<(``5*DOH/X* +M;_T"V$KP!-@`I0#8SW>```P=F@H@"0"WSW:``-`<`H5(AF>&#R"!`,]P@`"P +M'%5X8*`BI>S8,@P@!$"7"!8$$,]P``#LKA@6!1$,%@800,`%AA`6!Q!!P!J. +M.XY`AJX-(`IAAB06@!!(A@#942``@02%#R&!``KR`=O//'`X<4!W<]P@`"(@J"@`-C/<8``#!W>"2`)`+%J#"``J7!1!H_ZX'CQ +MP.'%`-C/=8``B():""```*7F"&_]`M@BA<]R@``,'7?89@L@!$"2(0:/^O'` +MI@V/^@#>SW>```P=P+>."2`)R7#/=8``B(+"I<.EQ*6*(,D`R7$R"R`$0)!X\<#/<8``B((`$04`3"5`@8SW"B'`#^MRSW```$$?F=OI`N_[ +MBB2##P&ASW"``/@<\"!``4!XT<#@?N!X\<`R#8_ZSW6``(B"!!4%$$PE0("B +MP27R3"6`@!#R3"5`@6?R"!4$$`HAP`_K`SW"``%2I+Z`F"6_]`MA+\`38`*4`V<]P@``,'2"PO@@@ +M"2APSW:``-`<`H5(AF>&#R"!`,]P@`"P'%5X(J5@H%H*(`2*((8+"!8$$!@6 +M!1'/<```[*X,%@800,`%AA`6!Q!!P!J..XY`AM(+(`IAAB06@!`!WTB&`-E1 +M(`"!!(4/(8$`"?+/`""@6?#/<(``#AV`V2"HSW"``-`< +M)X#/<(``5*DOH"((;_T"V$?P"I:,(`*`$?0`V,]U@``,';H/X`@`M2*&BB`% +M!&H)(`1`E0'8`*8S\`/8`*8Q\`.&C"##CP'?$O0`V,]U@``,'8H/X`@`M2*& +MBB!%"N"F-@D@!$"5X@E/_!OP`-D/(0$``H8&($"`$O0`V,]U@``,'5H/X`@` +MM2*&BB"%#`H)(`1`E;();_S@I@/P`J:M`X_Z"!8$$`HAP`_K`!XSW"```X=@-D@J,]P@`#0'">`SW"``%2I+Z`N#R_]`M@L\`*%SW&``-`< +M2($G@0\@@``"I<]P@`"P'%5X(*`>\`.%C"##CP':"/(`V0\A`0`"A08@0(`. +M],]P@``,'4"PE@[@"`'8`]CZ"&_\`*4&\`*E!/`!V`"E_0*/^@@5!!`*(<`/ +MZW+/<```11\-`._[BB-("_'`X<7/=8``B((#I?(,[_\%V".%SW*```P=H-@" +M""`$0)+!`H_ZX'CQP#H*C_K/<(``#)$(@,]U@`"(@@#?)[C`N!-XQK@!X`JU +M"-@Z<`#>`H4/)LX3"R"`@RWR!(4+(("#&?+&>`2ESW"```R1"(`/>8'A#_+/ +MA)L#(H209`@`'P&&A!:%3V*EQK@[@`\ER)L!1(`"`"/)7V*EQG@[@ +M`\ER!M@&\('E`MC*(&(`:@O/_T$!C_KQP,H(C_H:<,]V@`!0'0"&`>"!X,]W +MH`#('P"F!O0!V%$?&)#Z"L`,I!<`$,]P@`"\+B:`SW6``,B;8'D`V`&%@.`J +M\B38&-GV"N`,,]J!X`[R!!4%$`HAP`_K`.\@05!1`*(<`/ZW+/<```JRB?V]T%K_L*)``$`(9"($"``*8$ +M]`#841\8D(D`C_KQP"H(C_K/<(``O"X$@(#@)/+/=8``N#3AR?;/=H`` +MB$4`AMK@RB`K`8OVVN%4]L]V@`"(10"&Y.#.]HH@/P_6"X`,((9(%0`1$+D. +M#^__)7@2A0"F/0"/^N!^X'C@?N!XSW"``-0V0(C@N@CRSW&@`*PO&8&*N!FA +M42)`@`?RSW&@`*PO&8&.N!FAX'[/<:``R#L=@8#@"/*"V!2ASW``@!$4#J'@ +M?N!X\<#AQ;3!BW6I<,]Q@`#P2"X(K_I0VA8(X`&I<-4';_JTP.!XSW"``-RK +M;(C/<8``Z(*,(P*`"I%!*`(##/+KN`KT`KMV>\=S@``0OP*3#R"```*S`-C@ +M?PRQX'CQP!X/;_I4:(8B^`-/(D,"4R'"``4BQ`#/H_ABB,/#,H@ +M*0`)]@"2`-T/)4T0BB//#Z9X`+(`V4HD`'3/=H``P(7/`#: +MGKH`V<]PH`#\1$&@X'@AH,]PH`"T#SR@"\@$((`/_O__`PL:&#`+R(>X"QH8 +M,.!^X'CQP&X.3_I(=H#@`=U$]HHE_Q\3>(#A1/:S?3-Y%"$```8(K_H[>:QX +M`!Y`'JT&;_H!V.!X\<#AQ0AR`=V`XHHE_Q^`X43V,WFS?10A@`"N#V_Z.WFL>&$& +M;_HO<.!X\<#AQ<]U@`#H@L]P@`#X'B.`0(4`@1!R'_0"D4*5$'(;]`*%<@JO +M^R.%C"`"@!7RSW*``%@'(8(`VP\C`P`"N&9Y%G@AH@`@@0^``!"_`(&JN(BX +M`*$`V`$&;_H,M>!XSW"?`+C_SW&@_D@'-J#/<*``R!\\@$`0``;/<)\`N/]8 +M&``(2B3`<<]Q```(@:@@``(IV!*X\"!```'AX'[@>/'`X<7/<```___/=8`` +M!(,#I<]P@`!40NH*P`K/<(``<$+B"L`*SW"``!A#U@K`"L]P@``T0\X*P`H` +MV2"E!=@!I2*E(@@O_0;8'@@O_0G8:05/^@?9SW*@`-0'&AI8@(#@#O(9$@&& +M"2!#``\2`88"(,"`>6$/&EB`]O7@?N!X\<#"#$_Z`Q(#-@AW#1(.-L]Q@``` +MJA"+SW*``!"_U'D"N!9X!6(QB2V]@.%88,"]"_(A@^VY"?+/<8``,!ZT>:"1 +M$.6@L260@.'1]F&Y);`0BS)H-GD[8F63@.,Z8@?T)I)1(4"`7`F"^[H.P`N6 +M#B`&#<@#R`'9H!A``,]Q#P#__^(((`#I<*$$3_KQP"X,3_H#W\]VH`#4!Q,> +MV),/%A"6`!8#0``6`4"BP<]PL/X``$#!T[L%>\]RGP"X_].Y)7AVHA:B(,"< +MX`_R"B'`#^MR-=B,N,]S``#T#)AS@0&O^THE````%@%`,'D`%A%`0.%1(0"E +MP"&B``/A!"&-#P``_/\&\,]P```%#5(+@`$9%@"60B4!%!!Q-_<`($`C#QX8 +MD"`>V),9%@"6B."3]Q\6`)9!P"'`G.#*(<(/RB+"!S;8RB."#P``$0W/("(# +MQO4$(8`O````0,$#;_JBP/'`6@MO^LC:@B0#,@AU*';/<8``O$H2#&_ZBW#/ +M<(``3!,-@(#@SW&?`+C_#/(=H<]R@``X'02"`>"SN+6XN+@$HA:ASW"@`!0$ +M`=I$H,]R@`#`.QB"XKT!X!BBSW"@_A`!%J%`+@`4I7@6H")'@$()$#K/":#L__SW:```C.&G#)<,H, +M(`2+<2X.X`S)<)[P`]_/<*``%`3PH.2@`!8$0`<:&#$`%@5``1I8,03*G.`> +M](MPE@D@#`[9),#AOE,@P0"&(/X#1+C$'`(P9,%$)HT4&?*.V%$F`)&0N*`< +M`#!K\H;8D+B@'``P9_#K,HGQ1"$ +M]VAW@"?"$<]UH``8+/`ES1.Q<,HB(@"`X@KR`-@/(,``!B$!@-KU`=@#\`#8 +M@.`D\U$!;_J`)`,RX'CQP.H(3_H:<`(/X`$PV)AP*;A1(`"`RB'"#\HBP@?* +M(((/``#I%,HC@@\``,<`7`9B^\HE(@`LV!8/X`%`*($@`=Z*)0\:Q@[@`3#8 +MF'`IN%$@`(`+\HPF#YHG\N(*H`P!V&&]@.4!YB_WH@[@`3383R`!!96YV@[@ +M`338C@[@`2S8"'6&#N`!--CUN+AP&/(*(<`/ZW+/<```ZQ3CV^T%;_M*)``` +M"B'`#^MRSW```.H4U-O5!6_[2B4``)$`;_I!+0`4\<`F"$_Z"'<`WLEP8@C@ +M!,EQ`]C)=8#G&G`*\D0M/A<`(8!_@``@0<(-@`J`YPKR1"T^%P`A@'^``,A! +MK@V`"D(@0""`X`'E)_?/<(``P)#)=)VP,+R>L,]P@``<"&8*(`;`H"4`3_KQ +MP$8*0`&`X!#RSW"``-@J`(!1(("""O+/<(``I$-F#8`*B@Q@"@#8T<#@?O'` +M1@XO_>'%SW.``,@ZSW&```1%0('T$PT`4'4`V(KW^!,!`#!R!O?\$P$`,'+# +M]P'8W0&)`/%A"6&18` +MEL#@OO<`%@%``!8"0-.YSW"P_@``!7G/=9\`N/\VI5,BP00E>!:E3WJ/'P``_/^`X%8G$1(0\AJE+,`;I0+` +M'J7/<`!L!``9I0;PSW```+4,M@U``1D6`)80=SGW`"$`)`\>&)`#V"`>&)`! +MP`0@@`\```!`308O^J[`X'CQP.(-#_H(=L]PH`!D+O`@C0/3O0T2$#8-&I@S +M]=@%N"X,X`')<0W(SW&@`!0$`-\*H=X)H`C)<%IP`=C/<0``$"?/=2=[3W"R!`Q!?T42,`P"3R$_`O*$$#3B"!!P#8#R!``$`N/I4&?0#8!/1` +M*3Z#`_(!V.H,P`&`Y>WU"B'`#^MR5]B,N(HCGP%*)```Q0)O^PHE``$-&A@T +M]=@%N'H+X`$*<0W(SW&@`!0$"J%9!0_Z\<#Z#`_ZSW"@`%0N*X`'W=.Y+RA! +M`$X@CP?/<*``P"^E$!*&%!`1AL]VH``4!*JFL@F@"(#8\]@%N(#9*@O@`9^Y +M#1(0-O78!;@>"^`!J7&JI@T:6#,$\`/8!::IAH#E&_*`Y?KS02V`D`KR+R0) +M<.!XJ""``0`6`$#@>%,E39`)\B\D27/@>*@@0`$`%H!`X'BIAN?Q\]B*"N`! +M!;C_N.'U]=@%N,(*X`$*<2@>`!24YPT:*(<4#A??I<8`AP@'/<*``&"SP +M($(`E.?*(<4#A??I<8`AP@3/<*``:"PU>`2_0*#'=X``S+L5AS:'!7D7A[B' +M)7@%)0V0RB'"#\HBP@?*(((/``#"(`/\H+@"/*#X`#8RB#A`<`HH0,+\,]P +M@`#PJ0*`!?#/<(``\*D!@`5YF!U`$)X5`!&4'4`0DAT$$((5`!&0%1$1LAT$ +M$`#8@!T$$'X=!!`#R,]VH`#4!T&0@.(0%9(0"O(-R,]Q@```J_0A``"`X!/R +M&18`EKC@3_<-S,]Q@`#`.T8@@`(-&APP&H$!X)<"(``:H0\6%):`X@GR#&)``V`<2#S8!$A`V`!8$0'IP!QH8,0`6 +M!4`!&E@Q!,J`:H1N!`>`;H0,2`38!@>ZX#?)4 +M$0`!4R#`@`?TSW&``#0]&8$!X!FA`A4%$4PE`(`4\@&%[KC*(<(/RB+"!\H@ +MH@O/("(#RB."#P``M0>`!R+[RB1B``"5L'#*(

`9 +MH0\>&)4'&M@S`1H8-(GP!QK8,P$:�`V'0=!!`N"F``J7#/<8``"%H+8705 +M`A'/<8``$%KP(0``>F)0>J05`1!T'800)7BD'0`0!,@!D(#@%/),(T"@#?0! +ME;@5CQ!88""5^&`0>+X=!!!983]G#?"^%0`1"O`@E;@5@!!983A@$'B^'000 +M"'>0'000#Q8`EK0=!!"`%J7`0C3)WS""!A!+R"B'`#^MR0"D-)$`H#@0P +MV(RX`-N+NP4EQ!-)!B_[!2:%%*05`!`(=(0D&I`E\E$@0((>\@/(`9"`X!KR +M#8`1``>`X!+TT!$``6H5CQ`!X,.X^&`/>&H=`A!&#>``J7!J +M'<(3!?`Z#>``J7`/'AB5D0`/^N!X\<`^"`_Z&G``WZ09P`//<(``^!X$@-") +M\*`'R`0@@`\`P```UW``P```*'46]`W(SW&```"J%'D1B8#@#O3/<(``\+W6 +M>"*("(T0<<;V"G#.#*_]J7'A\%$@`*"&\@05!!!1)`"!0/(-R,]R@```JA1Z +M$1*%``]X22#"`')NSW"``!"_=GM@8/:X,HT'\L]P@`!0P=9X`8@"\`#8QW*` +M`%#!UGI$B@@A@0`((0$``"%``4D@P0,6;C5XSW&``-#"`&'/`/P`X7/<8``^!Z8'0`0)($H@00A@0\` +M0```/KE3)`(`'N$X>D5X_KB8'0`0"_*D%0`0C+BD'0`04-B<'0`0>_#_N!/R +MI!4`$(VXI!T`$,]P0`%0`)P=`!#/<(``^!XD@!"!GK@0H6?P!=@4N)P=`!#/ +M<(``^!ZD'<`3)(`0@9ZXG[@0H5GP42!`IT?R`851(`"!-_(RC302@C!)(L(` +MV!@]K@(\L]P@`!0P=9X`8@#\`#8QW*``%#!UGI$B@@A@0`( +M(0``22#!`Q9N-7C/RX`('0(.(`SR#A``"A=!W$$_8. +M(`"I<,]Q@``(6G05`A$)85EA,'ET'400SW&``!!:\"$``*05`1`E>)@5`1!1 +M(4""I!T`$`KR"MEV'400>!U$$("XI!T`$!;P$-G/!U$$(.XI!T`$`3P>!U$$#H([_RI<*05`!!$('Z"C!6!$!GRSW*` +M`/@>0X)4@B1ZAB'_`T2YAB+_#CIBSW&``'1.]"&1`,]Q@`!,3O0AD@`-\,.Y +MSW*``"B2/'GT(E$`SW*``/B1]")2`)@5!1!3(`2`RB""!!;TB!6!$%$E`(+# +MN3QYT2`BA0?RSW"``%"2]"!```;PSW"``/B1]"!``"&%42'`@`7RA!T$$`/P +MA!W$$U$E`((-\D0E`08CN0'A!"6`#P8````QN!EA`O`!V0/(`9"`X"3R#KX=A!`.]`HA +MP`_K?Q@>$=\H+AS"'B@,HAP@_*(L('RB!B +M"\\@(@/*(X(/``"U!LHD(@`,`B+[RB4"`<]P@`!0P=9X`X@&\,]P@`!0P=9X +M`HB,%0$0#K@E>(P=`!#/<(``D`8@@`:!H!``!H#@!_3/<(``9$4`B(#@7?(- +M$@,VAN-9\@"5K^#/%0`1SW.``"`]BKB>'000%I,!X!!X%K,!R.>B!:*8%0`0KKBON+"XF!T` +M$":!H!$!!B\I00!.(8('02K!``[A#R!``*05`1"8'0`0M+FD'4`0GA4!$:>Y +MGAU$$,]Q@`!810"A!""`#___T_:8'0`0#=B8'0(0"?`0V`;P"-@$\`+8`O`! +MV`>BF!4`$+X5`1&6""__`-JD%0(0!"*^CP```#""'0004O*,%0`0G!4!$90= +M`!"2'400[+J`'804`Q(#-@KR%-F0'400*G%^'400>!,.`0KP#MF0'400?AW$ +M$W@3#@%*<<)Y,'FR'400SW&``*RI((&&(7^/#?28%0X0429`D@GT89.`XP7T +MD;J2NJ0=@!`0N25ZI!V`$`0@@`\````0SW*``/@>9()2(``#,(,E>!"C1((0 +M@@0@@0\````0/7DE>!"B%/"8%0$0@!W$$Y0=0!">%0$1?AW$$Y(=1!"^%0$1 +MLAT$$)`=1!"`%0`1?A4"$8(5`1$:8H05`!%983A@$'BP'000I!4`$,]QGP"X +M_Q:AG!4`$!:AK0+/^>!X\`!A +M+;A3(`"`!?3/=8``R#H-\,]Q@`#X'B"!Q!$!!L]U@`#(.E$A0($$]`'9W!U` +M$,]Q@`#X'O`A``#/`/'`F'"X<11X.&#/<8``N$T(88P@PX_*(L$'RB"!#P``K!/*(X$/ +M``"+`1P'X?K*(<$/T<#@?N!X\<#AQ0?9#1I8,,]PH`#4!QH86(`.$`*&SW&` +M`$P3*(&`X0<:F#`0\L]UGP"X_SVESW.``#@=)(,!X;.YM;FXN22C-J7/<:`` +M2"Q>H1\0`(8!&A@P!,J,]P@`!\!R"`SW"``-A:X'_P($``%'@X8,]Q +M@`"@6N!_"&'@>.!_`=C/<(``F#[@?P"`X'C/<8``\#G@?_`A``#QP)AP"B'` +M#^MR"B7`!\]P``"?&7T%[_H[V^!XSW&``,PYX'_P(0``\<"8<`HAP`_K<@HE +MP`?-V`6X607O^D3;SW&```0ZX'_P(0``\<"8<`HAP`_K<@HEP`?/<```H1DQ +M!>_Z3=O@>,]P@`#@/>!_`)C@>,]Q@`#X0P:!`X#/.P3`0%AN`4I/@!`*8!RX']88.!XSW&``!1$!H$#@$"``H%" +M>.!_2"```.!XSW&``!@')('@?R"@$8C@?\*XX'C/<8``0$1&@8#BBB'_#R"@ +M!?(B@B"@`=@"\`+8X'[@>,]Q@`!@1$:!@.**(?\/(*`%\B*"(*`!V`+P`MC@ +M?N!XBB'_#R"@SW.``&!$1H.`XA+R)()1(4"`"_+/<8``J$(P<@?RSW&``,1" +M,'(&]$""4'/Q]0+8!?`B@B"@`=C@?O'`\@G``(#@H`SB""*(`X4`W\]RH``L(#00$0$\$A(`#HZ`X)P` +M*0#*):D0C"(!I)``)0#*)2419):4X\`CA@\``),`SW"@`&@L\"#0`.6B4-A% +M(4$"&-IN#F`,(-OXN,HE(A(N]`/8SW&@`/0'!:&$V@UP0+!"(@`H#7(`LD"& +M#7!`H$*6#7!`L`.%0(`-<$"@`X5"D`UP0+`&ED`H`B7#N`RX@K@%>@UP0*#D +MH0Z.`>`.K@(,H`HJ<`'=$/``W<]V@`"HD@X*(`@$E@#8SW&``,`[#JX>@0'@ +M'J&9!:_YJ7#@>/'`/@V/^1IPA"@("0`A@7^``$B,AQ$-!L]P@`#$!@*`H+V' +M&5@#!(B`X!'R`X&`X`WT"B'`#^MRR=@$N(HCG`X*)``$F0+O^KAU`H&`X!ST +MSW*``$24$Q(`AHP@PX\+\L]P@`!8'02``(`"H1P:&(05\,]P@`!L)0`8``0* +M".`)`-@-\$H+S_Z$*`@I"'$`(8!_@`!(CN(*``H)!8_YX'CQP)X,C_F#X,]V +M@`#`D!IPD_<>ECH6!1$*(<`/ZW(0N`4E!0#/<```@PR*(X4/$0+O^@HD``1` +M*`TAW64EE025$+DE>(#@.?+/<(``Q%KP(`$$1"@^)P`A@'^``(!!+W<@H".5 +M`I40N<8*[_XE>`AQ`">`'X``=$%>"@`*SW"``+A:\"`!!``G@!^``-A`1Y4@ +MH".5`I40NA"Y)7@FE1H.;_M%>8H*S_X(<0`G@!^``,Q`)@H`"EZ6'98`V0\A +M`000ND5X!B!`@`'=';8PN!ZV&?3/<8``G"H`@:"X7@Y@!0"ASW"``%@=!("6 +MVA[;((#/<(```)&BH"&@#-GR#R`(&+L0VL]Q@``<"`"!`"H"!$9X\0.O^0"A +M\<"."X_Y`-W/=H``P)`^E@\E#1`=EA"Y)7@&('Z#0?3/<8``G"H`@8"X`*'/ +M<(``O`;/<8``)"(`D$>)$'(;],]P@`"^!@"008D0*5X';8PN$T#K_D>MN!X +M\<#AQ?()H``H=8#@RB!!`P@+(03*(6$`-0./^3$'S__QP+8*C_D&"&`)`-[/ +M<*``T!L1@.^X"_)F#F`*`=C/<8``-#T)@0'@":$&R%$@`(`#$@TV(_*D%0`0 +M\K@?\L]Q@`#\.`"!@.`9\L"A!?!&"N``BB#'"5$A@,7[\\]PH`#$+`N`4R"! +M!/ZXS"$B@`?RF!4`$&X/K_X`V@,2`3:@$0``\+@)\HH@"``,&APPU@[@!"AP +M+O#TN"+R!\C0B0#:,Q&/``0@@`\!``#P02@-`\]QH``X+@>!#R)"`P'<1G@' +MH0W(=@P@#``L`!#'=X``$+\"OM9^$N??9Z"OBB`0``8:&#`#R*`0@`#$X$`* +MP0L#V<]PH``4!".@)0*/^>!X\<"V"8_YSW6``+2%`87/R&#$_)0(H\%X*9,)`"!1B$!!B&C!?21O^"F +M!/"QNK:Z0*:Z#X`+!O"6ND"F12$!!B&C"XVBN+D!K_D+K>!XX<7AQL]P@`#< +MJTR(C"("@,]S@`"TA1CRRHO/<(``4,(R:C9YQW&``!"_5GB`YD"!H8`&\I6Z +M0*&KO07PM;I`H8N]H:``V`NKP<;@?\'%X'BAP?'`42``@N'%J``A``AU1"4# +M%@0E@A\&````([LQN@'C>F($)8`?P````#:XSW.```!:2F,(8UA@02V"$E(B +M`@#`N@.Z&.*%X,HBC0\!`(D-U2(.`%!Q4@`E``#8[;T8`"$``B&``,]Q'$?' +M<04H?@`*(,`.`_`BN$$M01/`N02Y-'FI27%GM!@U`ECA6&)[L?P*&,)T201B("!D&C!?21OL"A"_"QO8'GMKV@ +MH0?TEKV@H44B`@9!HR(.@`L`V<]P@`"TA2D`K_DKJ/D```#/<8``J!T)@8#@ +M"_('@8'@"?06B0'@#WB0X!:I`_0`V!:IX'[QP)(/3_G/=8``#)$(A>"XK,%: +M\E$@P(%6]"8-K_\`WKH++_P8V(MQJ7`2#>`()-H!V<]PH`"P'SF@SW&``%@= +M"($`@,]W@`"H'4G`#($`@##92L`&AY#:'MM+P(MP@@L@"!B[P;7(I<&EW*W# +MIPH,(``"V,]P@`!XQPH0!`%,)`"`"_(*(<`/ZW**(%\&:=N=!*_ZN':N#F`) +MR7!&AP'9SW.``.@=`(.!XL!Y@.(X8`"C`=C/!!`"AJVXN@VO_P*F@.`/ +M],]P@`!XQP60@.#$]OX(```$\(H(``!*#R`(#=@)!D_YX'C9!L__\<#/<(`` +MQ$[/<8``J!TR"^`(.-HF#R`(`-C1P.!^X'CQP-H.S_\`V8+@S"!B@,H@0@`# +M]`'8#WC1P.!^\<#/<(``J!T@$`4`3"7`@(OW"B'`#^MRBB!?!5;;V0*O^DHD +M@`#/<(``_$[P($`!0'C1P.!^X'CQP"(-3_G/<(``6!T$@`#>EKZ@@`0EC1_` +M_P``W644Y0`ECQ^`````A@NO_JEP"''/<(``M#\B"\`)=@NO_MAE"''/<(`` +MT#\."\`)8@NO_NEP"''/<(``F#_^"L`)SW"``*@=)05O^>"@X'CQP+(,3_G/ +M<(``6!T$@`#=EKW@@`0GCQ_`_P``OV<0YP`GD!^`````&@NO_NEP"''/<(`` +M[#^V"N`)OV?/=H``>,<%EB6&"KCY8?H*K_X.($``"''/<(``1#^2"L`)Y@JO +M_NEP"''/<(``"$!^"N`)OV<%AA]G!98*N,H*K_X.(,`#"''/<(``8#]B"N`) +M`G6R"J_^"G`(<<]P@`!<0$X*P`G/<8``J!T`&0`$!98EA@JXN6&."J_^#B!` +M``AQSW"``'P_)@K`"4T$3_GQP.H+3_G/=H``J!V@A@#?EK_]96(*K_ZI<`AQ +MSW"``'A`_@G@"?UE3@JO_JEP"''/<(``)$#J"<`)&01O^:"F\<"J"T_YSW"` +M`*@=P(``WY:__F8B"J_^R7`(<<]P@`"40+X)X`G^9L]U@`!XQP65)84*N-EA +M`@JO_@X@0`"8<,]P@``,/YH)X`F(<>H)K_[)<)APSW"``$!`A@G@"8AQSW"` +M`*@=P*`%A?YF'F8%E0JXQ@FO_@X@@`,(<<]P@``H/UX)P`F-`T_YX'C/`X`/T"*+@?O'`#@M/^6H,S_^!X`SR"B'`#^MRBB"?!:';BB3# +M#XD`K_JX<\]U@`"H'2.%@>$"A0_T@>``V07R%(V`X`7R/@OO_R:E#/`CI0'8 +M!J4(\(#@!O0!WIH)(`C&I<*ESW"``'C'"A`$`4PD`(`,\@HAP`_K!XX'C@>.!X:2"``6\A/P!I(```]_'QP&8* +M3_D:<,]VH`#0#P#=!_`0%@"6_6'X8!`>&)`C;1)Q<@`-`"46`Y8E%@*6+R3' +M`"46`)9/?P]]3"0`@PB]I7_I]8+GS"?BD\PG(I?*)4(0(?3/=8``3)%)K246 +M`I8*K4NM)18"EFBMC^=,K:)I"/3/<(``69%Z"*_Y#=D-Y9SG"?3/<(``9I%J +M"*_Y#=D-Y1`6`)8"($$C.&`0'AB0*0)O^0'8X'CQP,8)3_FAP0AU*':$Y0#8 +MF/>+<#8(K_D$V0#`UW":"5!O"_+/<:``U`L/@62]N&`/H0'8!O"I<"(/[__) +M<0]X\0%O^:'`X'C/X"QH8,.!^\<#/<(``%`@`@(#@"O+/<8``K#@+@0'@"Z$J""_[`MC1P.!^ +M\<#/_\-M@%?1B]D;W/<(``H$BV#>`(DKTHN"4!;_FE>.'% +M,F@V><]R@``0OR%BSW*``/@>+;G`N?`B0P`H@U$A`(#/<8``\*E!@0GR/(N` +MX<4B@0\```H"`_)%(D(#2B0`=`#;J""``C9H=7D`(8T/@`#0PD"E`>,`W<]S +M@`!0P18C`@"@JJ&J`=DBJ@/9(ZI*)`!QJ7*H(,`!>6(6>:2I`>+@?\'%X'CA +MQ4HD`'@`V*@@``@`V\]U@``X'4"%#R,#``LBP(`/\D&%"R+`@$#:SR+B!\HB +M@0\``-``SR+A!P+P`-K/T"C`>#@?\'%SW"``.1$!H`#@""`SW"` +M`-Q_*:#A`&_\$=C@>/'`M@\O^0'9SW"``$0E,@CO_B2@BB#%#\]VH`#('QD> +M&)`!V`'9*'(H<[H*(`&8<;(*[_L`WPX.S_O/=:``T`_UI<]PH`#`+WH0`8:) +MN8NY>AA8@,]Q@`"`M1`86("&"P`"A@F/_D(,``A`V<]PGP"X_S*@$@_`"H#9 +MSW"@`!0$+*`='5B0[@[`"9X-@`G*#.`)Z7`'V$@>&)""#8`&&@G``>X*0``R +M"@`$Z@P`")(-@`.&"<`($@Q``&H.@`>"#H_[!@T``1(+@`FN"<_^#@H`!1H, +M0`%>#(`&D@Z`!:((0`1�_^3@O`!$8+P`0.#D`(SW```/[*0@Y/^R$'#_GQ +MP+(.#_G/=X``^!X#APB`P+@2#"`*+R``(`#=SW:@`+1'SW"@`(Q$N*``V).X +M=QX8D`C8=QX8D`#8GKA3'AB0X'C/<(``W``0>%,>6)-''AB0SW"``(`#$'A( +M'AB03R"`(T4@``U/(,8'--A$'AB0'-A%'AB0SW"``(06`8A&'AB0SW"``!`Z +MU@U@!0R(2B2`<,]Q@`"8QZ@@0`//<(``\*E!@'1M='L[8P*`0Z,!Y02CSW6` +M``!%`(6`X`/R9!X8D$,>F)&Z"2`*`=@#APB`42``@$"%#O)3(D$`$KE$(@`# +M#K@E>(8B_P,*ND5X$O!(<(8@\P\*N`0B@0\````,!KDE>`0B@0\````P`KDE +M>,]Q@`#8+`$&+_D"H?'`X<40W28,X`&I<`?9"[G/!X\$*0@)`"&.?X``1([/=X``8$1!I@;=I:;/=0(`<`2DID:FYZ8D'H(0 +M`"&-?X``9(Y`I0'ASW"``$24'!C8@,]Q@`#\*0"!'-I`H!C8#@@@``*A704/ +M^3G9SW"E``@,/J#@?O_9SW"``"",(*@`V<]P@`#(B^!_-:#@>`#:@.'*)$UP +MX'CH(.T!_]E<8""L`>+@?O'`X<7/<8``5*G/<(``/%I."J`(2-K/<(``?%+/ +M<8``S`<^"J`("-H`W<]Q@`!D,:&AHJ'/<(``?#2IH(X+H`(#@<]PH``L(,]Q +M@`#L-%"`$(!%H0:A`@X@`JFAT00/^?'``-G//'`,@PO^2#9`-K/=:``R!PII<]QH`"4$UNASW.``&P4 +M8(/S:,]V@`!0JPR&]7]3(,0%\&/[8U,@CP"#YZ3!BW$;]!^&F[@?IC06@!#B +MB_%P"_0H<$`C`01$:ZH.H`E`)@,<#=HJ\!Z&D;B2N!ZFSW"@`,P7*_"%YP[T +M02H"4D`C``3!NEH-[_R((% +M)P`1`*$%@P&A!H,"H0>#`Z$#XL]PH`#,%\]QH`"4$URA`=J`X@?T'X:7N!^F +M(-@*I1CP`,$#VA@86(`!P1D86(`"P1H86(`#P1L86(`4&)B`BA8!$1`86(`$ +MV2>E%AB8@)$#+_FDP.!X\<`B"P_YI!`!`/FYHL%P]"#9SW.@`,@<*:.D$`$` +M42'`@2[R,8C/=:``$!0CN<"Y`[D%X0/:3Z5&A4'"C>$0WLHFXA$&%`\QC"?# +MGPCT!!0/,?%VS"?JD`'>0_8`WH#FZO7%@$5^QZ6QB(8E_!\8O:5ZSW6@`,P7 +M6J`7\$6`SW&@`!`41Z&D$`$`42&`@@GR,8C7NH8A_`\8N45Y.J#/=:``S!<- +MV0':`^$-'9B0#AU8D":`&1U8D">`&AU8D"B`&QU8D`/9%!U8D'`0`0$0'5B0 +M/'`+@HO^039"'4- +M$@XV!M@-&A@PSW>@`!0$"J?/<(``,%K>"(`(`(76"*`(!-D!A3!P`-@#]@'8X'[QP)8( +M#_D(=<]V@`!@1-X/[__)<8#@"?*I<-(/[_]`)@$8@.`#]`#8"O#/<8``0$2Z +M#^__J7"`X/?S`=C%``_Y\<#AQ<]P@`!`(@B(B."AP0?TSW"``-@J`(#MN!_R +MSW*``-0L:!*`@,]S@`!"+(#@$_)K$H"`@.`3]&H2@(!1(,"!"_+/<(``V"H` +M@.&X!?11(`"``_0!V%SP`-A:\('@$_1N$H"`@.`!V,!X8,!2">`'BW!S$H"` +M@>!+]&X2@("`X$?R+/!S$H&`@>$%](,2@("!X`GR@>%*``P`@Q*`@('@0@`, +M`&X2@("`X`7T;Q*`@(#@`_(`V`+P`=A@P`()X`>+<',2@("!X"/T;A*`@(#@ +M!O1O$H"`@.`;\@#88,`7\('A`=UN$H"`PB5!$X#@!?1O$H"`@.`#\@#8`O`! +MV&#`O@C@!XMPJ&.`X.CU(,"Y!^_XH<#@>,]P@`#D!0Z`@.`!V.!_P'CQP.H( +M``"`X`7R7@D``('@!_3/<(``<"4`@(#@`_0`V!3P,@H``(#@#_2F"0``@.`+ +M],]P@`!`(BR0SW"``/@>'I`0<>WU`=C1P.!^\<`&"@``@.`@](8)C_J"X`3R +M`-C1P.!^SW"``$`B"(B'X`WRB.`2],]P@`#X'@&`Q!``!E$@0($(\L]P@`#8 +M*@"`([C`N.CQ`=CF\5T#X``1V.!X\<"X<,]QH`"L+QB!^K@,\@HAP`_K,]P@``4"`"` +MAN`!V.!_P'C/<(``%`@`@(?@`=C@?\!XSW"@`,0L&H#GN`;T42``@0'8`_0` +MV.!^SW"``-@J`8"!X`'8X'_`>,]P@`#8*@&`@N`!V.!_P'C/<(``V"H!@(/@ +M`=C@?\!XSW"``'P'((#/<(``V%KP($``@.`!V.!_P'C@>/'`Q@_O_^'%@.`< +M\L]U@`!`(@B-A^`6],X/S_^`X!+RSW"``/@>'I`LE1!Q#/3/<(``V"H`@`0@ +MOH\``#@0!/0`V`/P`=AU!>_X#WC/<(``?`<@@,]P@`#86O`@0`"!X`'8X'_` +M>.!XSW"``-@J`8"`X`'8X'_`>,]P@`!\!R"`SW"``/Q:\"!``(#@`=C@?\!X +MX'C/<(``?`<@@,]P@`#\6O`@0`"!X`'8X'_`>.!XSW"``)P5`(B`X`?RSW"` +M`(05`8@"\`'8X'[@>%$A0,<%\@G(O;@)&A@P`-F=N<]PH`#0&S&@X'[QP%X, +MS_C/=:``R!\D%0Z6Z+X*\H45`);*"H_[BB`$`"0=&)!1)H"0B`I""YT$S_CQ +MP"H,[_@TV#X*@`#PN,]W@```=A7R,@[@`@#8!@[@`@'8BB80$`#=%@NO_JEP +M%"=,$V&^@.8`M`'E./<.\`#;BB(0`*H+8`1P>!0GS!!ANH#B`+0!XSCW.03/ +M^/'`S@OO^#38H<$`W=X)H`!`Q?"XSW>```!>&?(V#N```=@#W@J^`-B,N+A@ +M$'B+<3(/X``!VA0G3!-AOH#F`+0!Y3/W_@W``!#P!=L*NP/:"KIX93X+8`00 +M>!0G3!-ANH#B`+0!Y3?WS0/O^*'`X'C/<0$`+$G/<(``&!C@?R2@\<#AQ6_8 +ME;C/=:``R!\2'1B0SW`!`$`\%1T8D/()P`F*(`0`#J6A`\_XX'@`V)"XSW&@ +M`,@?%1D8@,]P@`"LJ4:06WI/(@,`6A$"AC@0@`!D>EA@V!D``.!^X'CAQ0#; +MSW*``,"%2B0`=,]U@``XAFAPJ"```D`E`1(4>6"Q`>!(<,]QH``$)0^A5B(` +M!!&A5B(`!1"AX'_!Q>!X\<"R"L_XSW6``/@>!87/=J``Q"=U'AB0#)5V'AB0 +M!X5Y'AB0$)5Z'AB0L@WO_P#?@.`<\G<>V)-X'MB3@![8DX$>V),'A88>&)`0 +ME8<>&)`'A8H>&)`0E8L>&)`%A8@>&)`,E8D>&)`%A80>&)`,E84>&)#!V%`> +M&)"A`L_XX'CAQ0AQP[C/H#B`*71(.&' +M`-@V],]P@`#,JPR`SW&@`,@?9.`>H1#8#J$!V!49&(#>"R`+"]A1(0#&RB`B +M`!OT42!`QQ/RSW&@`-0+%H$X@23@,'!+][8+(`L#V%$C`,`%]%$@@,0#\AC8 +M`O``V(#@RB#B!,]QH`"0(SZ!(*7I`<_X\%28!$$"!:8*X +MBD$KP`#`N!>XQW```(`0!YBB`$`!ZE&/"*(!``'J44\`#8B[@>I1#P`-B, +MN!ZE#/``V(VX'J4(\`/8#+@>I03P`-B.N!ZE@B`!`>T`[_@>I0HAP`_K!XX'C@>.!XX'C@>.!XX'C@ +M>.!XX'C@>.!XX'C@>.!XX'C@>.!XX'C@>.!XX'C@>-'`X'[@>/'`SW&``+@\ +M%Z'@>.!XX'C@>.!XX'C@>.!XX'C@>.!XX'C@>.!XX'C@>.!XX'C@>.!XX'C@ +M>.!XX'C1P.!^X'@A@`#:4R%^@`OR`-J9NE$A0(#*(H(/``"#`,`J8@;/<:`` +M[$9%H6:02",#`T>0$+L$(X,/#P```,BZ17M(D`RZ!"*"#P```/!E>D:A0("# +MXA[T8H#/!^\B +M(X`UHB2`-J(%@!>BSW```%55X'[QP)X.K_@`VZ;!SW&``#@=8*%AH6*ASW:@ +M`+1'+!8!D`HD@`\``%552B0`>&AQJ"!``L]R@`!,$S5Z8*(!X0#9F+F5'EB0 +M2B2`<<]R@``X'0@2!0`+$)```-TY==AUJ7*I<']!4D03,@@0#:#R)"`(CG"/05)$$S +M((%*)@``#R9&``'ESW6``#@="!U`$8#CRB2!#P``K=XV\B6(9(@&(H(!Q;H0 +MN04C@P\````_97D%(8$/`#\``)L>6)!GB":("+ME>6B($+ME>6F(&+ME>9P> +M6)`&(0$2Q;F9'EB0`-F5'IB0F;E,'D"0)(!8'D"0"I"4'AB0;R!#`),>&)". +M#0_^B'!]!:_XIL#AQ>'&SW*@`,!&SW.@`.!&2B0`<@#=J"```Q8@3@,AA@'E +M!!I0`"*&!!M0`#&`SW*@`+1'F!I8@#*`LQI8@!.`M!H8@,]P``!55<'&X'_! +MQ>'%X<8DB,]R@`#06J:(PKDN8@#9#R&!`X#ESW.``*"*0(,%]"9Z0*,7\$5Y +M(*,EB!4CC0,CI2:(18A982:E((",(1"`1/>*(1``(*`CN2&C`(`JN`*C`-G/ +M<*``\#8LH".#):`F@R:@)(,GH">#**`E@RF@*(,JH"&#*Z`B@RV@((,DH,'& +MX'_!Q>!X\<`2#(_X"'>:<;IRVG,*(@`A"B-`(0HA@"'/<```R!LV"F``"B#` +M(?IPSW```,P;)@I``!MPSW````0<&@I``,]VH`#('SMP`=@3I@;8SW6``.@] +M`*7AI0@=`!4,'4`5$!V`%10=@!08'<`4'!U`%`[`(!T`%,]Q@`!8'0FE!($` +M@`JE"($`@`NE#($`@`REH!8`$`VEI!8`$`ZEJ!8`$`^ESW!#=:@2$*6N"6`` +M*-@1I:8)8```V!*E4R?`=1.E`E$Q8`EL]QH`#( +M'!BE%!8`EE,A`C,9I146`)80NAJE)!8`EANE%A8`EARESW"``+@\%X`=I<]P +M@`#H/7@8@`K/<(``Z#U\&,`*SW"``&0^!!@`"\]P@`#H/8080`LH@8@80`#/ +M<8`````D@8P80``O(<<%"+DE>B\A!P9%>9`80`!:"V``)=GY`H_X\<#>"H_X +MSW.``'P^0X,`W<]VH``L(-"&\FKT?W]GQ:<$IT`B0H`FIT.C!O("@Z.C`>`" +MHQ$#C_C/<8``6!T(@0#:0*`,@0'90*#/<*``L!\TH.!^\<"."H_X"\@`W@0@ +M@`____\#"QH8,`X.(`#)<,]U@`#$!A&%@.#4#"(`RB!B`,T"K_C0I?'`A@D` +M`(P@_X_*("$`T<#@?N!X\O^4HE``#&#^_X!A0`,8#@2/*!P6O8`=HB"Z`` +M2'.`X`_T"B'`#^MRSW```-04BB.%!XHDP0I-!Z_Y2B4```04`#%`)($P`=KR +M"J``2'.`X`_T!!0%,0HAP`_K$$HQ0!,)8",`!Y`$=3V"B'`#^MRSW```-44BB.%">D&K_F*),$*'=C/ +M=H``%$<`IKAP`!0`,<]U@`!4ST`M@@"I<7X*H``!VX#@#_0`%`0Q`!8%$`HA +MP`_KG$:6")PB*,`$0CCP#]?_%R/?3AB40C`@0DND0C +M!@)!+L8`#""`H40C`0$BN2_T3"1`@`[T@.',(B&`!_*!X@7P@.(!VL!Z@>(;]$PB`*8!VL(BB@!0<88C_0\GNP7R@.+,(&&@#?0R=\PC +M(8`+\H#G`_*`XP7R,G<#]O%VA?;)=P3P`=D)\`AU`>"P<%H'Q?\`V8H@_P^` +MX03T@.7*($H#C"#_C\H@@0______%?(R)((T@>+/<8``5,\']&)Q%GD"$<`` +M"?""XA9Y!?0&$<```_`'$<``Y0=O^*?`\<#R#,__W@\/_\((0`<�__"B'` +M#^MR/=B*(PL.2B0``!$%K_D*)0`!X'CQP,]P@`#X'@*`PA``!E$@0(#4#D(& +MT<#@?N!X\#*("$!O`PA_\HA80#/<(``[)$`@%$@@(`%\CX,8``0 +MEK2NSW"``.R1H*!-<(8@_`.,(`*`%?3/<8``H!4`@0'@`*'/<(``^!X#@!B( +MA.!8#\'^>@P`!"(.S_H%\(P@`X1H#@'Z109/^.!X\<#.#4_X`-X"W<]W@``X +MCD`G`!N$+@@9,"!`#E$@`(!L".+^RB""`V&]@.4!YC+W#@@@``#8`09/^.!X +M@.#QP!#8"O+2"0_[W@B@`8H@!`#1P.!^G@@/^[((H`&*(`0`[@D`"8+@!O0* +M"B`)`-CR\?#QX'CQP%X-3_C/=H``'`@`W0OP$-BX>`LA`("@".+^RB!"`P'E +M@^4@AK;W@.'*("$`<`WA`LHA`0"5!4_XX'CQP.'%SW6``,0&$(6`X"'TC@D` +M"8+@K`DA"(#@^_,=!4_XX'C/<8``^"4`@==P`(```(P``?P`@==P`$````P``?S@?@': +MSW&``'0E0ZD8H2AP9-EUVA[;O0`@!QB[X'CQP,]Q@`#$!@.AJ@@O^Q'8L@]@ +M`8H@"`#1P.!^\0!Y`-@7\,]Q@`!0JY@1@`!`*`(&AB#]#U(@P`%%N$5XSW*@`(@D +M$*(?@;.X'Z%*\`'8$-O/<:``R!QIH<]S@`!0JY@3C0``VL]V@`#L5<:&0"T! +M%H8E_1]2)!X\<#AQ:'!"'7/<-2Z_LI`P`3P +MN@R@"@'8SW&?`+C_NJ$$V!NABW`>H0#:G;K/<*``T!M1H,]P`&T`$!FA!/#: +M":__,-A1(4#'^_,`P-=PU+K^RN'SG0)O^*'`X'@`V\]RGP"X_QJB>Z(^HL]P +M`&P$`!FBX'[QP`H*;_B8<"AV&@@@`$AU!B"!`XAP4@@@`*5Y60)/^,]Q@``4 +M*F")@.//!X +M\`]XP.!6]\6Z +M8;G%N3!R$O)$(@`()+CP)@`04R(/`0#;#R/#`P'BQ;HP@`,`O>A<`EA47`)9!*1$%HA<` +MENH*;_@!V!4G`!13$`&&%A`!ABH0`(;6"F_X`=@5)T\4F!<`EI`7`)9Q%P"6 +M0"4`&Q:F0"G`(<=P@`"`M5(((``@V58E0!(6ID`I@"&6(`(`QW"``("U.@@@ +M`!#9525`&1:FSW&``""\0"@`(3A@(@@@``395B6`%A:FSW"``&"]#@@@``K9 +M\0.!XX'C@>.!X`>+@?\'%X'CQP%H/#_@(=\]RH/YL`\]PGP"X_U:@@.$`W='W +M*'9:#>__%2=`$^!XX'C@>.!XX'C@>&&^@.8!Y3/WB0.!XX'C@>.!XX'AAOH#F +M`>4S]TD'#_C@>.'%SW6@_M0"`-K/!XX'C@>.!XX'C@>`'BX'_!Q>!X\<"B#@_XZW#/=X``I!@`AU$@0(`9],]U +M@`!,'0"%4B"```"E"/#/<*``J"`-@.3@[``%`(H,[_]4V``5!!"&(/\.D'#Q +M]<]V@`#T)I@6`)91(("`/?(N#<_^SW6``/@>R14`%J6XR1T8$),6`):EN),> +M&)#7%0`6I;C7'1@0#H6EN`ZE`(7($``&AB!_CLH@(@#*(0(`_`NB^,HBH@$! +MA<@0``:&('^.RB!B`,HA(@#@"Z+XRB*B`0"%SW&``%2IQ!``!B6XP+@F""_\ +M"J$Z#X_]?]@*N,]QH`#0&Q.A?]@0H0#8E;@0H<]Q``"<'B8*(``&V,]QH`#P +M-@2!1B#``02AE-C^"^__&-D`AU$@0(!8#2(!RB`B`/$%#_@*(<`/ZW+;V`2X +MBB,%`A4#;_E*)0``X'CQP'(-+_@!V-X+(`D`WL]PI0`(#,]U@`!$)<*@!(51 +M(("`>`I"^<]Q```0![()(``&V`O(!2"`#P$``/P+&A@P!(51(("`$?+/<(`` +M5#@`@(#@"_22#B__BB#&"('@!?2N"8`%#?``V9ZYSW"@`/Q$(:#@>,&@SW"@ +M`+0/W*#^#\_[7@T``4(((`@!V`8([_H!V$T%#_C@>/'`U@P/^(#@B'4`WPGR +M@>`+]`'>SW"``'P5P*@%\,]P@`!\%>"H@.$)\H'A"_0!V<]P@`!Y%2"H!?#/ +M<(``>17@J(#B"?*!X@OT`=G/<(``>Q4@J`7PSW"``'L5X*C/=J``R!_/<(`` +M?!48'MB3`(B`X(HA$``2\L]P@``U'@"(@.`,\L]P`P!`#44>&!`PI@+8&!X8 +MD`/P,:;/<(``>14`B(#@'/+/<(``-AX`B(#@%O+/<`(`3EP@'AB0SW"``"@` +M(1X8D,]P@`#@!2(>&)`8%@"612```Q@>&)#/<(``>Q4`B(#@"/(8%@"6A2`! +M!!@>&)"!XP?T&!8`EHBX&!X8D!@6`):`N!@>&)"`Y1GR`-B4N,]U@``\"`"E +M<=@&N!8*[__\V2"%SW```$P<"@KO_Y^Y&!8`EH6X&!X8D`$$#_C@>(#A\<"8 +M<`7R3"0`B(_VSW"``&@&`!`%``HAP`_K`#92B2`<<]S@`#`?RARJ"#``?`C@``!X@5YX'\O*$$`X<4` +MVDHD@''/=8``P'](,E>@#9GKD9>00A@`!"(`"`RB!B`.!_ +MP<7@>/'`SW&@`,@?I!$"`,]P@`#L-`"`-8'/`!V`+R`(.!X`;TUW$``(@3A/<`V`/P`=B!XLP@8H#8#:'ZRB#A`='` +MX'X"X3!Y06E0<,3V(G@0>`/P`MC/<:``R!\>H1#8#J$!V!49&(#@?N!X\<#A +MQ5#=`-K/`#9SW"``.R1(:#/<(``4*L`@A`0`P +M>0+8%1H8@#^BX'X"X3!Y06E0<,3V(G@0>`/P`MC/<:``R!\?H8H@&`@.H0+8 +M%1D8@.!^`-G/<(``[)$@H"&@X'\BH.'%X<:`X,]Q@`!XQT6!)?+/<:``R!]` +M$0X&SW.``%"K0"B-`D(3``%\D]!^V&"[8V*["",#``)["2+"``+8%1D8@,]P +M@`#X'E^A`X`B@,]P@`#LD2*@P<;@?\'%X'[@>.!_`-CAQ/P'"X]@^ +M#F__BB$$`\]VGP"X__V&"B'`)T#9G[D]IL]QH/X<`#:F4R;`-`4@@`^P_@`` +M%J;/<```1!S^#J__"B#`+WIP&14`EE$@`((0\E@>@!#J__"KC/<``` +M!!Q2#H__SW````@<1@Z/_\]P```,'#X.C__/<```$!PR#H__SW```$0<*@Z/ +M_U$C@*4-\KP5`!:`X`GT02M.)<"^`[Y"?`\W@?P1MX% +M\%3>`_"$W@#8@>#P#`$%DQ4#%LEP"G$J<@HD@`0M!2_Y"B7`!/'`B@_O]P': +MHL'/=H``U"L(CL]U@`!4*T0H/@M`)0`5)W#F#6`'BW$(CH''Z7%$*#X+0"4` +M%B=PS@U@!P':"(Y$*#X+`"5!'D`A``T4X;H-8`>@U@ +M!P':80?O]Z+`X'[@>.!_`=C/@'B*'=`*H`@SW&``!"_&G([9Q9X&F-2BHPBPX\`(Q$`0_+/=H``V"I@A@#= +M\+L/)8T0#O+/'I`0<1/T`(8$(+Z/```X$`WT@@W/_H#@"?+/<:``."X'@:5X +M!Z$,\$86`!:E>$8>&!`&\$46`!:E>$4>&!#_V1(90B!"($(@@.)6!^W_(6_E +M!<_WX'C/<0$`QP//<*``["/(.(`H!V!8.[_Z*(`4#@.`&]`38T<#@?P04$#1$ +MV<]PH`#('"F@S@X@"@'8!@N``;_QX'CQP&H,S_>BP2AV"B2`@`#?SW6@`"P@ +M0!40$``+<:(([_B*(`\-`!0`,5$@`(#Q\P7P`(:`N`"F@<&&"._XBB!/ +M#`04`#%1(("`!/(`AH&X`*:+=8H@CP]J"._XJ7$@P`BX`AP$,(H@SP]6"._X +MJ7$@P0(4`#$E>`(&N!!X$+@% +M((`/``!"_0:G$(4"(``$C"`/B@SWBW$."._XBB!/#P`4`#%1(`"!\?,%\`"& +M@+@`IL]P!@`"_P:G0"2!,.H/K_B*(,\.`A0`,34#[_>BP,]Q@`#X'@"!`J$# +MH<]PH``X+HHA_P\GH,]P@`#8*C$`K_F*(0@`X'CQP"H-[_[AQ8#@6O+/=8`` +MV"H`%000`-E1)`""RB'!#\HBP0?*(($/``#)&\HC@0\``$<%#``A^`$#V'YRB`A`F$"S_>!X,]Q +MIP"(20#:!?0#V`ZA`O!.H>!^X'CAQ>'&SW6``.@LH(T`WH#EP*,2]('@S"$A +M@`[RH.)$]L"C"O#`X@;8!?9"(@`(0[@"X`"CP<;@?\'%X'BX<$#<`"$`@_'` +M#@`D`)AQC"`"@(OV"B'`#^MRSW```,D4#0?O^(HC!PW/<(``.$ST(``!SW&` +M`#A-!"A^`2]P]2$!`4(H`P3!NU*X!"E^`2]Q0BD"!,&Z4KF!X\`@:0"!XL`A +M:0"((#X`B2#!#X@A/@")(<$/@.#6("L(@.'6(2L(@@D``-'`X'[@>/'`]@C/ +M]Z'!.G$`WX#@RB'!#\HBP0?*(($/``#*%,HC@0\``(@"RB3!`'@&X?C*)<$# +MSW&``.PL0+'/<8``[BS@L4PA`*#*)#N__J7%"(5$@3"$`H+`'[?]`(E(@R7`J"2``J7&5 +M`._WH<#@>/'`/@C/]SIPSW"``.@L`(B`X!IQ3O1,(0"@&_+/<8``B`>EB02) +M'64R=SXRB5,`P#=SW>` +M`.DL`-X(\`#9(*_F#N__BMD!YL]^`""!+X``B`+5X +MU'C/)(08X#@4FWL\P+@$'C4>H#ASW.``&R24F/=]0'9W/$!Y:]]@^6J +M!\O_[0>/]_'`D@^/]\]S@`#N+$"34R)-@"'R@N4D],]U@`"(!PFM**TBA<]V +M@`#L+`"6*=T2O<]W@`#I+!4E#!`@I."/@.<&\E8@#PCP?_5](*4!X`"V!O#/ +M=8``B`<+K2JM`>*=!Z_W0+/QP"H/C_<(=AIQSW6``.XLX)4+\,Q_U@CO]T`I +M0'%%N'X-[_\*<2"5C"$0@+3V80>/]^!X\<#J#H_W"'7/<(``Z"P`B'IQ@."A +MP1IR;?1,(P"@&_+/<8``B`?%B02)'F9R=LHAS`_*(LP'RB",#P``S!3*(XP/ +M```\`\HDS`1,!.SXRB6,`THA`"``W@KP`1U2$`:/@.`$]""M`>4!YL]^SW>` +M`(@'`"<`%`:(`>`0=F8`*@`"=T`K@"`4>!4@0`34>,]Q@`!XDC0A$@!,(@"@ +M`-G?\XMQ2G`"VJH/K_\`VX#@#?(!%(`P`1T2$`:/@.#6]0$4@#``K='Q"B'` +M#^MRSW```,T4BB--!`HD@`2]`^_X2B6``$`A42`O(4CP4HA`""+<2IP +M2B``(0IR^@ZO_RIS@.`.]`HAP`_K)(`WP3P`>?O?T$H`0+#N3!W<@`* +M``#>$_!`*8$@-'D*%(`P%2%!`0'FSWX4>;EA`!D$!(`@`B,O(`@D`,!!*`$& +MP[D!X3!VP`?*_X+!"G`"VH8-K_\`VPL4A#`O*`$!3B"%!R\E1P%,)<"`L`?+ +M_PHAP`_K!\G_02@!!<.Y@.$*=;0`+`!*(``@2B(`(`;P0")2("\BAR1!*`$#P[E2 +M<8``#`!*(0`@%/`"OM1^"A2`,!4F3A%`(5$@+R%')!1^`":`'X``>)*@L(`E +M`A.P?0#`02@!!P'A,G&X!\S_,+C#N``@#@2"P:EP`MK"#*__`-L+%(0P+R@! +M`4X@A0/'`S@J/]Z+!0,!!PD`H%`5`*1<% +M`-U`*A,%0"L2!0'>2B6`(:EW!/`*="^_WZ7!,(P"@F'``V!/RA><)\HOG!?((\$HF +M`"`S\`'8`O`"V,]Q@`"$%22!"R$`@`/R`-H"\`':`"!`(\X-[_@J<0HF`*`? +M\DPD`((2\L]P@`#L'18@``%`@`:($'<@](#B'O*I<&!ZJG$*(@"@"_(B=<]P +MH`#0#Q`86(-,(`"@K?7/<:#^B`7/`'AH*#2 +MX80K`@H`)$`.M/>D$`,!SW&D`*`_?:&F$``!'J$(&D`!Z0"/]^!X\58D7#C1P.!^\<"^#B``1]@`VL]QJP"@_UFA!]@:H5BAT<#@?N!^X'C@?N!X +M\<#/<8``O"XX@8#A(`X"`-'`X'[QP,]Q@`"\+CV!@.&4#P(`T<#@?JT&``FI +M!@`)I08`"0#9SW"``,B;(:!Q`F`"(J#QP.'%SW6``,B;H@M@`JEPN'``A8#@ +M$_)*)(!SSW.``-`J`-FH((`"0(-$*;X#,B)"#K!R'O(!X1#P`-E*)(!YSW*` +M`,A2J""``D0IO@,R(D,.L',.\@'A"B'`#^MRSW```(89BB-$`?D$K_A*)``` +MS0=O]RAPX'C/<(``R)M`@(#B(X`+\L]P@`#0*@"`1"F^`PW@,B!`#@CPSW"` +M`-521"F^`S(@0`[@?N!X`-O/<:H`\$-EH<]R```_/T:ASW```#X_!Z&*(!`` +M"*$)V(RX":'/<```%AP*H<]P```?'PNASW```!P6#*&1V`2X#:$$V`ZASW`` +M`#\^#Z%0H7&AX'[@>/'`O@Y/]YX(H`(`W48-(``'V$H.[_\:<,]VI`"X/:P6 +M`!;/=Z4`V,LYV:*XK!X8$`'8K*?V'A@0SW`5`"LKFAX8$(H@Q`"?'A@0&MCS +M'A@0]!X8$&38R!X8$*K8R1X8$&G8S!X8$,#8S1X8$,]PI0`(##Z@-@_/_]H) +M(``*/'`&@@``#X/S__2#0``N@B/^M'` +MX'[@>/'`W@U/]\]P@`!TJ$`@$@8(<<]P@`"0!B"@`-[$J`3?1"X^%PHA0"X` +M(8!_@`!TJ#H++_D;%!4_WX'CQP&X-;_=$ +M*#X'`"&.?X``=*@0K@'?\:XAKD"N8JX#'@(1"2``$QX"$:D%3_?QP#8- +M;_=$*#X'&G`Z<<]Q@`!TJ"]P&F%1BH'B&V$/\@HAP`_KZ:!`9"X>*>!8*:D>*&&0"$/!(#BI7@!IASR +M`8$"',0P,+L$',0P`!P$,""!BW5@>:EP`8:EP`-@`I@&F701O]Z+`X'CQP.X+;_>(X$H +M@5$A`(``W@OR3)4KE5MZ17E3&UB`+954&UB`!?!3&YB#5!N8@RZ55AM8@"^5 +M6!M8@#"551M8@#&55QM8@#*56AM8@#.57!M8@#2561M8@#656QM8@'H-S_]5 +M`D_WX'CQP(0H`@?/<8``N#DHD5,A08#/:EPY[@GN%(@``#*)2(0RB%"`\HAX0'`N!-XPKC/ +MI<.!X\__>:WR">__`Z4$I0;PS@BO +M_HH@B0S/<*``>$4`@`0@@`]P````02@^A0#9\O7/<*``M`\\H,]RJP"@_SFB +M!]@:HCBB]@U``H#F`=C`>`S@<@_O^0'960%/]_'`V@A/]PAWSW6``,B;&(U( +M=A!R.G$:/'`"',`V0+: +MA"L*`@`A@'^``.2;A"D$#P3@V@H@!B=P8;J`X@'A,O?1P.!^\<#/<(``F,NB +M#>_XBB$)#,]P@``8(I8-[_@4V<]P@``\)8H-[_@4V='`X'[QP.H/#_>BP3IP +M&G$`W8(.[_\'V)IP`MFI<%IP>G$`VS1H`G$H=10A`"!HR#EMO8MP0B-!((#AO@?M_T`B +M0"`J"^__BG#1!R_WHL#@>/'`?@\/]SIP6G'/=X``$#H,C\]V@`#(FZ6&AB#_ +M`4.X#B4-D,]P@`"X*B"`RB5B$&!Y!-B`X"/R&HZ`X,PE(9`=\@#8$-T:<`*X +M%7C'<(``0"X@@(#A!_(B@(#A%/)@>2IP8;V`Y4`@0"`O]P#8&JX,CX8@_P%# +MN`6FL@BO]TIP602G:$KH5(L,`(*,`D/`B``#1P.!^\<#AQ<]Q@`#DJ`"!@.``W13RSW*` +M`!"I`*((@:"AI*$(HM()[_D)V,H)[_D#V,]P@`",0EH,@`?/<(``9#&F#:`` +M`X#/<(``)#7]!B_WKZB5!8_Y\<`:""`#X<7/<(``5$(N#*`'`-W/<(``<$(B +M#(`'SW"``(Q"%@R`!\]P@`!D,:*@;@GO^0/8SW"``.PTHZ"U!B_WH:#QP.'% +MSW&@`*PO'(&]@01]SW"``#0>`(B!X`GTSW#`WP$`'*$HV1BY"?#\O3P)`@/V +MO90,POD`V9NYSW"@`-`;,:!M!@_WX'CQP"((``#^"$``T<#@?N!XSW"``&0Q +M`("!X`'8X'_`>/'`T@T/]\]P@`!4J<>`P+Z!Y@'>SW&``-`V`('`?N&X+/2! +MN`"ASW6@`,`O$X7ZN`3R$X6ZN!.E`M@1I<]P@`"X*B"`8'D`V(C@#/32#V`) +M"M@,\,]PH`"H(`V`Y."0]Q"%42``@/CSX@CO_\EP%14`EH"X%1T8D,4%#_=< +M%0000!4%$`HAP`_K!X\<`J#0_W"'7/=J``P"\:ACFX +M4B```%,@$0`4AE$@P(`']#(+[_XDV/*X`-\"\@'?418`EH#@"_2C%@"6!""` +M#P````^,(!"``_0`V`+P`=@:<`0ADD\`!```SW````@<\@K/_C^X4B`#``0@ +M@$\"````UW`"`````=K`>@QPAB`]`(#@`=G`>5$@@,$(\L]P@`#(!P"`@>`` +MV`/T`=@!WN6]RB&!(TPA`*`H\N:]RB=A$(#G(O+COGP/'`(@_/_<]RH`#`+P#9B!I``!."B[@3HL]P +M@`!T%`&0$+A%(``/P!H``,]P@`!X-+X*K_D@H-'`X'[@>/'`G@LO]P#9F[G/ +M<*``T!LQH.(,H```WH#@)_+/<(``3!,Q@(#ASW6``#@=#/+/<)\`N/\]H"2% +M`>&SN;6YN+DDI3:@SW"``,@'((#/<(``I#3P($``0'@`A?&X!?+/<)\`N/_= +MH*4##_?@>/'`*@L/]QIPSW"``%2I!X#/<8``^!Y3(`T`@>4!W2"!Q!$!!L!] +M42%`@1SRSW&``'0489'/<8``S`?`@3SC>69DX1)QCO<*(<`/ZW)^9HH@S`A: +MVPHD``1Q`&_X529%%H/@`-\.],]P@`!T%"&0SW"``,P'`(`\X3A@9.`"(!`@ +MSW"``(06`(A1(("`!_+/<:``P!T`@:"X`*$+R`0@@`_^__\#"QH8,`O(A[@+ +M&A@PK@VO_ZEPSW&?`+C_78'/<(``;`9`H/VA'-D7\,]PH`#(.S:`5H"&(?\( +MAB+_"$5Y5H"&(O\(17G/&^;@A],]P@`"D&`"`42!`@!OT"B'`#^MR"B0`"%$6 +M!9:*($P(D00+8@N`-\D`6!!`*(<`/ZW**((P( +MC=LY!R_XN'.*(!`!$:80AE$@`(#^]12&J[@4IL]P@`"D&`"`@N`2V,`H(@;* +M("$`SR!A!AFFSW&@`,@?&!$`AJ&X&!D8@(H@$``1H0G8"+@/H1.&J;@3IL]P +M@`!4J0>`@^#,(.*!!O1`*(`@G[B('@`0S@F`!\]P@`#0-H4!+_?@H('A\<`$ +M]'(,```$\$X,``#1P.!^\/'` +M/@@O]P#9SW6?`+C_787/=H``;`9`ICVE'-D5\,]SH`#(.S:#1"$"!S:#AB'_ +M""5Z-H.&(?\(17G/`X>OUO@K/_X#@$/0`V"?P.!,$`%@3 +M!0`*(<`/ZW+/<```F2%M!2_X+]L`AAVE/@[@!P#8SW&``,@'((&*($P&:@U@ +M``/:P@Y@``/8%@[/^PC8A@P@`(HA_P\!V`D`#_?@>/'`SW"``,@'`("#X`?T +MM@E/_)8)S_]>#P``T<#@?N!X\SW6``.PT0"4#'*@@0`3/ +M<8``?#35>>.!@>`5(XP#X*0#]`'?XZ$!YL]^`=BA!^_V1!T"$,]P@`#DJ`'9 +M)*@EJ,]Q@`!\-`"1AB`8`*BX`+':V`.ISW```%##`:'/<`$`H(8A!6```J'Q +MP,]R@``D-0Z*@>"AP2GT#XJ$X$WV@.!T#^'_RB!A``^*`>`/>`^JH<#1P.!^ +M`-@-J@ZJ+@@@``^JE@_/_P#8C@KO_XRXSW"M"[ZZ0,"+<`39?=H]V_8*H`47 +MN^;QYO'AQ<]R@`#L-$02@`"!X$`B`PP3]$HD0'$`V:@@``/P(TT`SW"``(@T +M-7@!X:"@+WD`V$0:`@#@?\'%X'@`VL]Q@``D-4^I`=@-J4ZI3*E0J5&I4JE3 +MJ>!_5*G@>/'`0@[O]@':SW&``/@>8X%XBX3C&O0`@<]Q@`!D,<00``8EN%(@ +M```A@<"X`=J`X<]Q@`!XQR:!P'J`X`#])AR`_!*)```SW"@`"P@<(#/=H``[#1%AJ:&`B.`@`#:RB)O``(C +M3X,`W`Y0;R`B.` +M#TX``2`&I@&&@.`6\L]W@`!\-`"&X82&B`-G/<(`` +M5*DPH"6`X'\QH/'`K@S/]L]U@`!D,2&%)7@!I<]Q@`!4J1"!@."AP0;T`=@0 +MH06!$:%2#2_]BW``P<]P`0"P)3!P"_+/<`$`U#$0<0?RSW`!`&Q'$'$$]/8) +M;_P!V`#>SW"``.PTP:!R#V_Y!]AJ#V_Y"-CB#8`"SW"``%1"]@E`!\]P@`!P +M0NX)0`?/<(``^!X`@,00``91($"!!O(.#B``R7`%\"(+8``#A7D$[_:AP/'` +M!@SO]KAPN@WO_RAP@.``V5:`AB+_"$5YSW*@`*@@38+DXHKW@.'K]78. +MK_^H<""%/:8-!,_V.!`$`%@0!0`*(<`/ZW+/<```F2$A`2_X+]O@>/'`SW"` +M``R1"(!1(,"!(?3/<8``?#1"@2&!SW"``-`T0*#/<(``[#0GH,]Q@`#(!R"! +MBB!&`/X(8``"VE8*8``"V#H,(`8"V.H/#_D'\,]P@`!D,3X*8``#@-'`X'[@ +M>/'`'@O/]H/@*'4>],]Q@`!T%"&1SW.``,P'0(,\X3IB(8-DXEEA,'4.]PHA +MP`_K_V +MH-@'\,]Q@`#0-@"!@K@`H=X)S_^:"D``T(;V"._[`=@>#0``Q@P@"3+8SW`` +M@@$`'*5""6_Y`B;`$P#8,@B@`,EQO0+/]O'`X<7/<8``/*D`$80`3"0`@*7! +M#O8#$84`3"5`@`KR"B'`#^MRBB`-`<4'[_?QVTPD@(`;]`.)@.`9]`#8`*G/ +M<8``R`<@@?G8!]VZ#R``J7(6"6``J7#/<(``9#%^"Z__HZ!R"X__8/#/<8`` +MY*@$B8'@#/0%B8'@"O3/<```___2#>__`-F`X%#RSW&``/@>`('$$``&42!` +M@07R`X$8B(3@#/3/<8``R`<@@8H@Q`16#R```MH"V#;P^@M``(P@$(5D``4` +MSW"``&0Q`("!X`;TE@X/^8#@*/(`V<]PH``L(+"`SW`!`+`E0,`!V$'`0L!# +MP43!!MD(<@#;F'.X">_V`-G/` +M`'C'`B8-$,]P@``\J>6'8X`%+_X0-W4!VZ"(PB/.`('EI<$%\@.(@>`/],]P +M@`!D,2.@SW&``,@'(('/V'H.(```V@#82?#/=8``^!X`A<00``91($"!0_*! +MXT'T`X48B(/@/?3/<(``9#$!@(#@-_3/<(``#!T`D('@`=C`>`RXUW`````0 +M*_*P@L]P`0#4,4#``=A!P$+!$=A#P`#8C+A$P"AP#-D!V@ASF'"X<``EAQ\` +M``!]%@\O_-APSW"``.PTSW&``,@'(('(H-C8Z@T@``C:"-A"#P``D0#O]J7` +M\<#AQ0#9SW"@`-`;F[DQH,]P@`!$)02`42"`@![R,@A/_\]U@`#X'DV%/I53 +M(@``Z@X@!`';`(7$$``&42!`@0SR`X48B(3@"/+/<(``5*D'@%$@P(`'\L]P +M@`!D,0.`"_#/<8``R`<@@8H@20=J#2```MH"V,(.```A`,_VX'CQP*(/C_;/ +M\@6%@.`: +M\C""`GG7<0``4,,!V,(@#@"`X";R,@C/_P2%Y:7CIJ"X!*6R#"_Y`-@<\%X+ +M;_D'V!CP@.,&\@:&`GF`X=+V4R"`P02F#O3/<8``R`<@@8H@2P*B#"```MK^ +M#2```MA)!X_V\<#AQ:'!BB#_#U8,(`!`P('@,?3/<(``^!X`@,00``91($"! +M$/+/<(``#!T`D,]Q@`#(!X'@`=C`>`RXUW`````0#_+/<8``R`<@@8H@10=" +M#"```-J>#2```-@Y\""!BB!%""X,(``%VHH-(``%V"_PSW"``'@T0("!X@#9 +M!/0@H"?PSW'`WP$`SW"@`*PO/*#/<`"`__]&"N__`=F`X!?R>@_@!8MP"B4` +MD!'RSW&``,@'((&*(,8!V@L@``/:,@T@``/8J7#Z"N__`,&)!J_VH<#QP+AP +MSW"``,@'`!`$`,]Q@`"H,$`L@``4>!4@0`$`88K@"O0*(<`/ZW**(,T`>0/O +M]Z#;Z@P``-'`X'[@?N!X\<#AQ<]P@`#X'@.`&(B$X`WT"B'`#^MRBB!-`8HC +MA`U*)```00/O][AS!@A```AUSW```+_?E@GO_P#9@>`%](PE$)40]QX++_P! +MV,]Q@`#(!R"!BB!%`B8+(```VH(,(```V-T%C_;@?N!X\ +M(('$$0$&42%`@0/R`>`)HJH-C_>>"X_]@>`+\@"%@N`)\L]P@`#(!P"`@^"L +M#,'_.06/]N!X\<#&#(_VSW"``&0Q`("!X!3RSW6``,@'((6`XX<_(-3_]>#N__`MC/<`$`U#$&"R_\`=G1P.!^X'CQP%(,K_8&V&H/ +M#_G/<(``F!5*)````!@``<]P@`#X'@.`&(B$X`OT"B'`#^MRBB#,#>K;M0'O +M][ASSW"``/`^S@D`!\]PH``L(-"`SW6``.PT((4")D`0UW```"!.!O8V"&_Y +M!]C`I<]P@`!XQP:`42``@"`(8OG*("("S@S@!0#8/02/]N!X\<#AQ>8.+_D( +MV,]U@`#(!P"%A^#,("*"//+/<8``[#1!@8#B!O+/`>\@.!@.`:\@#9SW"` +M`.2H*:`JH`#8*@]O_XRX$/"`X,P@HH$,]`.!@.``V@?RSW"@`"P@$(`"H4.A +M((6'X`!V,!X#+C7 +M<````!`8]/H,[_\!V,]P@`!D,0"`@>`V]`.%@>"`""'YRB!A``.%@.!T""'Y +MRB"A`"KP@.$.],]R@`#DJ`J"":(`V`JBSW"@`"P@$(`&HL]P@``,'0"0@>`! +MV,!X#+C7<````!`0],]P@`!D,0&`@.`*](H@A09&""```MJB"2```MC]`H_V +MSW*``.PT`8*`X`#9!/0#@H#@`_(!V5,@@,$$H@':PB*!``#8@.',(B&``_(! +MV.!_#WC@>.'%X<;/=8``=#'`%0,6O^-']])KU'Z^9@"F(:9"I@%KQ;C`'1@0 +MP<;@?\'%SW&``"0U#8F!X!GTSW"``'PT`)#IN-$@HH((]`#8#JD-J64#K_\/ +MJ0'8#JD/B4(@`("!`J__RB!B`.!^\<#F"8_VV@B/_\]S@`!\-`"3SW*``#RI +M02B!`,"Y(:K/<8``#!T@D8'A`=G`>0RYUW$````0#?2B@\]Q@`#0-*"AH8// +M<8``[#2GH3;PSW&``&0QH(&!Y1+TSW:``.2H)(Z!X07T)8Z!X0'9`O(`V8#A +MRB&"#P``$"<#]"*#@>7/=H``T#0@IA+TSW6``.2H)(V!X07T)8V!X0'9`O(` +MV8#ARB&"#P``$"<#]"&#SW6``.PT)Z6I<<]UH``L(-"%Y8$")LT3_[T"],6A +MYH$")LT3_[T"],:A*(.`X07TSW&``'C'*)$CHB6XP+@:"R_Y`]E=`8_VX'CQ +MP,]Q@`#(!P`1!`"X<,]R@``\-4`L@``6>!4@0`$`8H7@"O0*(<`/ZW**((T` +M70:O]W;;@N``&4`!8_*$X![TSW&``.2H`(&`X%ORSW*``!"I`*((@0BB`-@` +MH02AM@LO^0G8K@LO^0/8SW"``(Q"/@[`!M'`X'Z!X!/T`-G/<*``M`\\H`O( +M!""`#_[__P,+&A@P"\B'N`L:&##L\<]P@`!$)02`42"`@!#RSW"``%0X`("` +MX`KTK@FO_9#8@>`$],X,P`,/\`#:GKH`V<]PH`#\1$&@X'@AH,]PH`"T#SR@ +MSW"``/@>`X`8B(3@!O2*"@_]@.`%](X*0`*\\;SQ\<#R#T_VSW6@`,`O.H7/ +M(3],]S@`#DJ`2+@>`&]`6+@>`!V`/R`-B`X,H@@@\``!`G!?3/ +M<(``?#0"@('BSW.``-`T`*,2],]R@`#DJ`2*@>`%]`6*@>`!V`+R`-B`X,H@ +M@@\``!`G!O3/<(``?#0!@,]R@`#L-`>B^07O_P.!X'CQP,]P```('/(,+_ZA +MP?^X#?+/<*``+"`0@`39?-H]VT#`BW`&"R`%%[NAP-'`X'[@?N!XX'\!V.!^ +MX'CQP.'%SW$#`$`-SW"@`*@@+:#/<:``P"\4@?"X%($,\@0@@`\(````UW`( +M`````=C`>`?PAB!_#X+@`=C`>(#@7_05$0"&H+@5&1B`$?#/<*``J"`-@.3@ +MSW6@`*POC_</'`6@@@`.'% +M'@@@``AUY@@@``AS<'7*(T4#$'/Y!6_VRB#%`/'`X<6AP0#=0,56#J_\BW"" +MX(H@_P\,\L]P@`!`1`.`((``P")X@.#*($P#Q05O]J'`X'CQP*'!`-A`P,]P +M@``\J2&(@>&+(#@RB`L`//QX'CAQ<]Q +M@`!8'22!(('/$@@```)($``:KA( +M(```X'_!Q<]Q@`!@1`N!0(`.@8#@RB"!#_____\*\@*`0GA((```F2`&`$@@ +M``#@?N!X\`&],]R@``0OQGPC@Y/_8#@-_+/ +M<(``0"((B(?@,?3/<(``4*N8$(``SW*``!"_`K@6>`!B[;@C\L]P@`!0JY@0 +M@0#LOQ)I%G@:8A/R`(*(N`"B`=@/JL]P@`#X'@&`P!``!A$@0(#,(:*#2`O" +M"`?P#@S@"*^J@."4"\((SW"``,@'(("'X`4],]P[?Z^ND#`BW`$V7W:/=NB#^`$%[M& +M'D(311Y"$ZH,;_]''D(3_@Z/^`>&)H9"<`(@0@#_N@+T!J9,%H`0@>`&]$P> +M0A,"\`"F:0-O]J'`X'CQP!(+3_8(=L]PH``L(/"`A^8`W8SV"B'`#^MRBB`- +M`HHC"0.8=8D`K_>X!^X'CQP/X);_8`V<]SH`"T#QR#/*///'`=@E/]L]V@`!0'0"&`>"!X`"F +M`-T*]`'9SW"@`,@<,:"B"Z`(*'#/<0$`0FG/<*``["$2(2:D%B.!_ +M"JGQP(8+3_V`X#/RSW"``$`B+)#/<(``^!X>D!!Q*?+/<(``U"MHB$J(1"L^ +M"P`A@'^``&0K57@&B,]Q@`#8*H'@%_0`@8JX$@QO_0"A@.#`#F(&RB`B`,]R +M@`"D0P:"`X`@@,=Q````*,X.H`9(<-'`X'[@>/'`C@A/]L]QIP`42`#=J*$' +M@<]V@`#F$('/ASW#S#__\$*&@V+:AFKCU&A@`SW&D`+@] +MFQ$`!L]W@`!0'0.FIA$`!@2FDA$`!@6FHQ$`!@:F_]B;&5@#IAD8`)(9&`"C +M&1@`SW&D`.S_SW```/__IZ$&H0"'`>"!X`"G"O0!V<]PH`#('#&@3@J@""AP +M!-C^#"_W0"8!$@W8\@PO]T`F@1+/<"@``@'/<:``["<&H8H@C0`&H0"'0B!` +M@`"G!?3/<*``R!RQH"4`3_;@>/'`M@\/]E$@P($-$@\VSW.```"J`Q(--L]Q +M@``0J_1[$8L0$X0`$O(!X`AR,A6%$&>1`AD"`<]V00"#`&:QSW.``#@]`ZD1 +M\$`D0@`Q%8400JG`$P,!`ZG/=B$`@@!FL<]S@``\/;!RQ_?$H0"#`>``HP2! +M5/#/!(`$\AN0`_`:D`H-``>`X#KTSW"@`!0$`]DCH"#8#!H< +M,,]Q@`#`.Q:!`>`6H0/(`-J8$`$`I!`#`)080`">$`$!K+N2&$0`OA`!`:V[ +M@!`-`:08P`"0&$0`?A`!`8`8A``]9;`0`0&B>3!YL!A$`((0`0%^&(0`AB/E +MC[(81`#@"@+]=08/]O'`^@TO]@AS$(DS$8T``=I`JPT2#S;/=H``**KN9L]R +M@`!8JDC.'%SW```.@MSW.``!`'`://<(``#)$`H\]Q@``(R,]R@``,10""H(&@ +MH`""'-V@J`1I`:)5(4`$`Z(8V`*B52'`!06B`8$$HFAP`Z$"@8VX`J'@?\'% +MX'CQP,(,+_8*(%`(@,'`"5`'D\A3P/HH@*(42!`@&&F!?*#N8VY +M**+/<(``?`@"HQC8`J;/<(``_`<`@`P>`!'/<8``Y`="#>_Y(($`V<]RH``L +M(%""@.!AADJCQ/988`JCSW"``-X'`!"'`(0O'P`G=4*-42)`@"]P6/1,(`"@ +M"_3/<8``A+`;8<]P@`#P!V"@2O#/H#B`!"%`$``+``")8,`A"\?`"]P`"%/#@`G +MAA^``&BM1"B^*$`FCP4R)T\>.V/'$O>5!QX*L")8,`IO:$ +M+Q\``"%`+AMCSW"``/`'8*`.E0(@``$.M0Z56&`.M66F!_#/<8``8+`X8`6F +M#I6Y`R_V!*;@>/'`2@L/]@AV`(B,X,]U@`#4+&@5@9"D](#A!_(#CFD5@I`0 +M*#7/1K'0*0(XZ`X&D=0I`DCFH= +M0I#-]@5NJ7&"(4(#I@B@!0W:$A,F``X``B?0%%T2$28*<#8,+_8J<04I/B!<$@(F`B!!+E!QP"!F +M`,]R@`!'+#1N*F*,(L./$O0J<04I/@``(<%T"?"@`F@!^``$`L0*@` +MV`3P`=@`V8#@"_1PW`(E`!-$+CX7)W!&"*`&^6%K%8"0`>80=D('Q?\)\(#A +M!_(`V+X(K_EH'0*0.0(/]N!X\<`2""```MCZ"0``T<#@?O'`U@DO]DHD`'(( +M=\]P@`#X'A4@T`,`$`T@`-[)<-JEJ"!`#<]Q@``L4/0A`@#/<8``^)$4>4"Q +MSW&``#Q2]"$"`,]Q@``HDA1Y0+'/<8``/%#T(0(`SW&```B2%'E`L<]Q@`!, +M4O0A`@#/<8``4)(4>4"QSW&``"12]"$"`,]Q@``8DA1Y`>!`L0B%Y;@%\@39 +M-*4"\-2EY+@'\@G91AU$$"[:!?`4V48=1!`RVENU68U1(`"`66$P>48=1!`: +MX3JU"O(*V%0=!!`&V%8=!!`'V`CP$-A4'0005AV$$P78#Z6F"*`#Z7`\C2AP +M1!U"$(8@`P#FN5@=`A#*(D$`#/)0(<,!;WI$'<(04"##`6]X6!W"$.6Y"/)( +MD0=PA#DN07RI;A8'0(042'`@`7RI+I$'8(0@N<5\N8-[_CI<``0 +M`""Y$``&42!`@/'8P"@B`!:'000`M@:I0/P6AV$$P#8%Z48I:8.+_WI +M<"B%`=I(&)#/=X``^!XCA\]UK`#4`1J!3!X8D(+@`MC*(.(`T!T`D(H@!``/ +MID81``&P'@`01A$``;0>`!`?V`BX#J8(@5$@`(``V(NX"O(0IFH,C_C/<*`` +MI#`!@(2X"O`1IJX,C_C/<*``I#`!@*2XSW&@`*0P`:'/<(``=$4`@."X"O*& +M(/\.(K@4N,]Q@`#D!0NAM@Y/^&H,``'*#T`#3@B``\]P``!555H>&)`!V5D> +M6)#/<*8`*``OH`.'6A`!`<]PI@#H!R:@8@C/_`.'D@[@!`V0`-B,'1B0!]B- +M'1B0`-B+'1B0SW"``+@J((!@>038@.`.\L]Q@`"D%AJ!.X$D>/&X!O(>"Z`` +M`M@%\)8*X`8!V,]RH`#$)P\2`(9CAT0@`0(;@P\:&(`/$@"&H[@/&AB`#Q(` +MA@5Y#QI8@#R#SW"@`#`0)*#/<(``4*L0>(\:&(#/<(``H(;/<8``H)80>!"Y +M)7B0&AB`BB`$`)(:&(`=@T`:`(#/<(``.!Y3&AB`#Q(`AI^X#QH8@`#8$!H` +M@!Z#'!H8@*T&S_7@>.!^X'CQP,]P@`#`?Q@0!``*(<`/ZW+/<```Y0[>V[T# +M+_=*)0``X'C/<8``P'\%@>!_!J&`X/'`--@']!X,C_U0($$$!?`6#(_]3R!! +M!%(,K_TTV-'`X'Z`X/'`]-@(]/H+C_U0(`$`]-@'\.X+C_T(BBZ!!R$ +M,.P1`@`('(0P\!$"`/01`0`*'(0P#!Q$,`#:2':H(``$`]D1(8"#"?(4)($S +MX)$((L(#))$)(T,``>8`)8$1!2F!#P,``"`O<04M/@$W<47WC!`!``'A`O`` +MV;"`C!A``%4@S@53)<$0%"9!$$"Q7!`"`4HD`'0`WZ@@0`+T)L$3,'+*(DL` +M`>=5(,$'D!B$`,*]%"%-`V"U?!`#`4HD`'(`VJ@@P`'T(8T`""-#`P'B!N-P +M>Y(8Q`#M!._UI,#QP'X,[_68<&]_`[^D;\]P@```.;9@HF^U8(GC!N?P8`;T +MZI&,)P*8RB!K`('B"O1T>4&1'66Q/'`(@S/]72`7('8$0T`<'+"(L8`]X$S@#!WPB=&$`+; +MSW&``+@Y]"'$``81!0$`V2Z@^F(%*H(/`P``("]Q!2Q^`S=QC!`.`,+W;J`% +M+7X#-W%-]Y`0`0&4$`(!`N%0<8/VY.;#]P'9+J`A!,_UX'CAQ>'&=(!<@=@1 +M#@`"(L0`4X`W@3P0!P",$`@``B&%`,]Q@`"X.0+:]"&*``81"0'/=8```#E" +ME6&5"A$&`9(0`0&CE0@C0P``)0$!!2F!#P,``"`*)$`.!2J^$PPD0([6]RJ0 +MC"$"B-+V`MDOH)`0`0%^99081``AD#!V;``*`*)Y(;`!V9@80@`P\`4IOA,, +M)$".5O=,)X"`%/*0$`$!E!`.`0+A,'8$]HP@`9G*]\&0`=G=9;%R+Z"8]J&P +MY?%,)X"`#/2AD``FP0`P=)/>F"TX'[@>/'`B@K/]7*`R('8$0(`<';")L80<8`C@7!QPB'& +M``#?SW.``+@Y]"/$`P'=V6$%*8$/`P``("]Q!2R^`#=Q]"-#`^V@Q/<"V2V@ +M!_`%*[X`-W%#]ZV@G0+/]>!X2(%2H$.!4:!<@52@-X'@?S.@X'CQP!X*[_4" +MVPAU*'8!V`"QJ7".#>__385-A:EPR7&"#>__`]M-A:EPR7%V#>__!=M-A:EP +MR7%J#>__!MM.A:EPR7%>#>__"=M.A:EPR7%2#>__!-LU`L_USW"@`"P@$(#/ +M#,("*`$_(0\(#A$?*!X`;T!8J!X`'8`_(`V(#@I`4"^('A!?("V.!_`*+@ +M?O'`8@G/]<]V@`!0'0"&`>"!X`"F`-T*]`'9SW"@`,@<,:"."R`(*'#/<(`` +M8`<@D(:Y$+D%(8(/``#"$L]QH`#L)T:A`9`0N`4@@`\```(3!J$`AD(@0(`` +MI@;TSW"@`,@``I@#="O0! +MV<]PH`#('#&@F@H@""APSW*``(`O`(K/<:``["<0N`4@@`\``,)I!J$!BA"X +M!2"`#P```FH&H0"&0B!`@`"F!?3/<*``R!RQH'D`S_7@>/'`X<4*)0"`SW&` +M`"1'`!$$`##R3"0`@,]PI`"X/0#;&?2;$`T&SW*``"A'H**F$`T&SW*``"Q' +MH**2$`T&SW*``!Q'H**C$`T&SW*``"!'H**;&-@`_]JF&)@`DAB8`*,8F``! +MVL]PH`"T#UR@-_!,)`"`RB+!!\H@@0\``'X9RB.!#P``_`(4!>'VRB'!#\]P +M@``H1T"`SW"D`+@]FQB8`,]R@``L1T""IAB8`,]R@``<1T""DAB8`,]R@``@ +M1T""HQB8`,]P@`!$)02`SW*@`+0/(KC`N!RBH0>O]0`90`'@>/'`%@^O]0#8 +MSW6``+@J((5`>8S@%/3/=H``P"H@AF!Y`MB`X`ST((9@>0/8@.`(]`H-;_U0 +MV%$@@($$\@#8`_`!V"\A!R#/<(``[#;/=X``T"IV"F_Y`*?/<8``-#T4@0'@ +M%*'/<8``4!T`@0'@@>``H0GT`=C/<:``R!P1H?((``C/<8``O"X$@8'@%?0F +M@<]VH`#L)V!Y`-C/<(``R)L8B(#@%_3/<`$`!@$&IL]P$@`&!!;P"B'`#^MR +MSW```(<9BB/%"4HD``#Q`^_V"B4``<]P`0`'`0:FSW`2``<$!J;/<(``R)L@ +M@(#A`X`L\N"'1"B^`\;8DK@&IB"%)W=@>0#8C.`X\B"%8'D`V)#@,O(@A6!Y +M`-B1X"[R((5@>0#8DN`H\L]P.0`",P:FSW`Y`(),!J;/<#D``F8&IL?8E;@9 +M\$0HO@,`(8]_@`#(4L?8DK@&IL]P```",P:FSW```(),!J;/<````F8&IL;8 +ME;@&IO8+3_[/<(``R)L8B,]Q@`#(F^H/8`0@@4PA`*`5\L]P```";@:FSW#! +M`$)N!J;/<`,`PFX&IL]P-@!"EP:FSW`"`$)K!J;//$+@%((`/``#"<0:F +M!H\0N`4@@`\```)R!J8!CQ"X!2"`#P``0G(&I@N/$+@%((`/``""0#8D.`,\B"%8'D`V)'@!O(@A6!Y +M`-B2X`GT#(\0N`4@@`\``,)_!J;/<`$`1FH&IL]WH`#('Z07$!!,(0"@"?+/ +M<%``QG,&IL]P(`#'0#8C.`9\B"%8'D`V)#@$_(@A6!Y`-B1X`_R +M((5@>0#8DN`)\L]P@``&=`:FSW"```=T!J;/<(``QG,&IL]P0`!"=`:FSW"` +M`,=S!J;/<`(`1FH&IL]P$`#&:@:FSW"``,B;6(C/<8``R)L`B"2)@.(!VL!Z +MSW.``,B;L@U@!WF+)-@8V4X.X``',]J!X!7RSW"` +M`#0]4!`$`,]P@`#(FPP0!0`*(<`/ZW+/<```JBCE`._VBB/'#*07`!#/<8`` +M-#T"(``$$Z'/<`(`1VH&IB"%8'D`V(S@%?(@A6!Y`-B0X`_R((5@>0#8D>`+ +M\B"%8'D`V)+@!?+/<&4`PFX&IL]P@`!0'0"`SW&``%`=0B!`@`"A!?0`V%$? +M&)`U`X_UX'CQP,8*C_7/<(``0"X4@(#@B_)6"6_^!]AZ<,]P@``0.@R(AB#_ +M`4.X8;B&X/0`#0#/=H``R)LDAL]R@`#XF3,F`'"``%1(0"(1"P2Y-'E`(A`* +M0"(2!D`B#PA`(@T$.F)`)P%R%'D`><]Q@`#H+TAP5?#/<8``"#`$:E'PSW&` +M`"@P0"(``DOP0"(``\]Q@`#H+^8-+_X`V@2&SW&```@P!+@4>+A@._!`(@`' +MSW&``.@OQ@TO_@#:!(;/<8``*#`$N!1X^&`K\$`B``7/<8``"#"F#2_^`-H$ +MAL]Q@``H,`2X%'A"`)P +M<@TO_@#:!(;/<8``*#`$N!1X(G!>#2_^`=I_^:G#Y`8_UX'CQP,]P@`!` +M+@^`@.`0\L]P@`#(FP2`SW&``$B;`K@4>#A@SW&``$@PA@E/_M'`X'[QP'X) +MK_5$VL]P@`"$4L]Q@`"LJ0X/X`0`W@+=%@@@`,EP8;V`Y0'F.O?!`8_UX'CQ +MP$8)K_4`VL]Q@`#X'A5Y8($$N``@D`^```Q/N1N8``"!!!`/(,]V@`"$4KX8 +MV`.@@4*&BB`'#V&&'67P'8`0[!W`$""!1H;/=8``K*EEACA@^!B``!8FP1/T +M&,``%B7`$P3@!.&J":_U"-H,$``@%GX6?01M)&Z6":_U"-HM`8_UX'CQP,(( +MK_42V:G!"'9R"&`'BW!*)`!Q`-JH((`"%B2`,"B(@>'#]F&Y**@!X@+"````Q/.&#L&,``\!B```"%!L(%PSA@^!B``(/! +M]!C```07$!#/<(``K*D6(``$!.`:":_U"-KCA\]P@`"LJ8?!]G@$X`8)K_4( +MV@#`((6Y&1@`((6Y$0`&42``@`GROAG8`R"%OQ$`!H"X!_"^&1@$((6_$0`& +MH+@V"Z_\OQD8`(#@!?2F"H_\@.`#\@#8`O`!V!!VZ`ZA!\H@@0,`A;D0`091 +M(4"`\=G`*2(!RB&!#P``DP#`*2$!I@UO^H080``E`*_UJ<#QP,(/3_7/=H`` +M$$>`X<]U@``<"!+R((:`X0WT`*7J"R_X#MCR"J_^BB`0``'8`*8.\""%)7@+ +M\*H*+_@.V+X*K_Z*(!```-@`I@"EX0=/]?'`8@]/]<]Q@`"<*@"!H+@`H38/ +M+_L!V,]P@`#`D``0!`!,),"`RB'-#\HBS0?*((T/``"!#,HCC0\``-H`S`2M +M]LHE[0!,)`"`J@`N``#=%&T`(($/@`#`D`>1QI'DD1"X!7X%D4.1$+@%?P*1 +M$+I%>!IP#@EO]\EQ6G#/<(``Q%KP($$#1"T^%PHA0"X`(8!_@``L02"@8@VO +M^@IP"'$`(8`O@``@0?H,P`7Q=H3W@.<9],]P@`"X6O`@00-$+3X7+W8`(8!_ +M@`#402"@+@VO^DIP"'$`)H`?@`#(0<8,P`7/<(``P)``@`'E$'5D!\7_S09/ +M]>!XX'[@>,]Q@`#P+,]P@`"\+N!_(J#\'`BT\<`:<,]P@`#`*B"`8'D!V('@ +MRB'"#\HBP@?*(((/``">&@+90(4'V&!Z`MD"CAT&;_4`KN!^X'C@?N!X\<#/<(`` +MX$0`@(7@?``%`,]PH`"L+QJ`4B```%$@`(`T],]Q@`#`+H<]P@`"L +M*@"`0'@*"$``SW"``*@J`(!`>'X)P`""#<_]G@X/_`;PY@RO_(H@B0S/<*`` +M>$4`@`0@@`]P````02@^A?+USW"``/@>(X!(@3214R(``!8,X`(!VSH(+_@2 +MV-'`X'[@>/'`X<6TP0;PG@RO_(H@B0S/=:``M$=Q%0"6!""`#W````!!*#Z% +M\O6*(/\/;QT8D&L=&)":"*_XBW"&"T_\@.`.\F\5!)9K%066"B'`#^MRSW`` +M`+$340*O]C3;V@M/^`8.``0=!6_UM,#@>$"(`=@`H6BZ`KI5>L=R@`!`+F." +M8Z%A@F&A8H)BH62"9*'@?P"BX'CQP'8,3_7/=X``Y$0&AP.`SW6``-Q_((!) +MA0`B@`\M`,#&`GF`X8(`+`"AP<]V@`!0'0"&`>"!X`"F"O0!V<]PH`#('#&@ +M@@Z@!RAPBW$R"6_V0M@`AD(@0(``I@;T`-G/<*``R!PQH``4!#$$)+Z/```7 +M_\HAP@_*(L('RB""#P``IA/*(R(,B`&B]LHE(@``A8*XA@X@``"E)@@@``'8 +M`(6BN`"E*87'<2T`P,;^">`%Z7`I!&_UH<#@>/'`D@MO]0#:SW&``(A*`(&[ +MP5?`!(E*)`!R>,#/<(``^!X#@`B`P+A`P%\4@##/<8``I!9!P#C`0L!>%(`P +M0\`:@3N!!'DQN<"YJ""``@#;`"2`,&08P@`!XD]ZSW"``-Q_8I#/<(``O`9` +MD%!S,?3/+MKSW.``-Q_*A.#``XC0X/* +M(V(``KME>`+P!]B`X(8%(0!%P,]PH`"T1T<0`(:`X,PA(H!N!0$`SW"``-Q_ +M`!`$`%$D0(#*(<$/RB+!!\H@@0\``*H3RB.!#P``=@!$`*'VRB4A`,]Q@``0 +M.@Z)SW.``-Q_AB#_`4.X*!L"``^)AB#_`4.X*1L"`!")SW&``-Q_0K&&(/\! +M0[@J&0(``-F>N<]PH`"T1U,86(#@>`#94QA8@,8)#__/=H``4!T`A@'@@>`` +MI@GTSW&@`,@<`=@1H88,@`\FZ97K/`ZC +M;@K/_4;``,"`X`ORBB'_#\]PH`"T1V\86(!K&%B``-@#V43`4<%(P,]Q@``$ +M@`AAB."V`"H`1\`(P07`$2!`@)`#`0`'P``D`3!D$8$`@>&``R$`@W`!V608 +M0@`'P<]PH`"T1V`86(#/<(``^!X#@!"YF[DR((`/``#8`I^Y@.`!V,!X#[@E +M>,]QH`"T1U\9&(`&\,((K_R*((D,SW"@`+1'<1``A@0@@`]P````02@^A?+U +M`MD`V!IP!\`1(`"$`@,A`%#!SW"G`!1(7!@`!$P@`*`C\DP@0*`4](H@Q#:* +M(80X'_`<%`0P"B'`#^MRSW```*L3I-M=!F_V2B4```HAP`_K!"X@;B'N(RX +M!J<@AD(A08`&],]RH`#('`#8$:)*)``ABG5`((`Q$'A+P$`A@#$0>$S`0"A` +M(4W`"B:`)`'A@>%AO2"F"/3/<:``R!P!V!&A6@J`!P/`-6T`)1<6+R?()25X +M$'@0N(4@B@`&IT`O@"&!N)>X`"53%@:G+R/()$`K@"&!N)>X!J<+P`:X@;@& +MIPS`!KB!N`:G`(9"($"``*8&],]QH`#('`#8$:&2P)/!E,*5PV(+(`56),0R +M-L"`X`GT`""!+X``A#T0B0'@#W@0J0#`@.`,\C8.#_R!X`CT`-AVP`3`@+@/ +M>$3``,#/,=P@`#TDD*P8[``A@'@@>`` +MI@GTSW&@`,@<`=@1H0()@`<*P0;`0"^"(8&Z!+D&N#A@M7C'<(``])(BD#QY +M$+DE>D:G(I#`N;AY!2&!!"\B2"`CD$`K@B&!NCQY$+DE>D:G`Y#`N+AX!2"` +M!2\F""``AD(@08`)],]RH`#('$HD``!$&@`!0B14($PD`*`D!LW_@>``I@GT +MSW&@`,@<`=@1H7X(@`<+P4`J`"0&N8&Y)7@&IPS`0"X!)`:X@;@E>`:G`(9" +M($"``*8'],]QH`#('`#8$:$0P6&Y@.'P!.W_0"!`(!'!8;F`X0C`3`3M_P'@ +M`(8!X('@`*8)],]QH`#('`'8$:$:"(`'SW`(`(80!J<`AD(@0(``I@?TSW&@ +M`,@<`-@1H:X+S_X&\#X-;_R*((D,SW"@`+1'<1``A@0@@`]P````02@^A?+U +MK@P/^,]P@`#`'9SW"@`,@<,*!+V<]PI``<0"2@X'[@>,]Q`0"X4L]P@`"P*N!_(*#/<8`` +MW'\`@8"XX'\`H>!X\<#&#"_UN'!3(($`SW"``#1:*&"!X,HAP@_*(L('RB"" +M#P``E1G*)((/``#^`#`"8O;*(^('SW:``,`J((9@>0'8@>`1\B"&ZW5@>0'8 +MN'#/<```EAD*(<`/J7(BVP$";_:*)(,/S00O]0'8"=G@?R"@X'CQP,]P@`#` +M*B"`8'D(V!!YSW"``(0*Q@D/]LX-X`,'V'H*S_W^#P``*@@``-'`X'X(<5B) +M`8"`X@*A"?19B8#BPB"B`,`@H0`"H>!^X'[@>/'`_@L/]<]V@`!0'0"&`>"! +MX`"F`-\*]`'9SW"@`,@<,:`N#F`'*'#'V)2XSW6@`.PG!J7/<`,`@BL&I<]P +M`P#"1`:ESW`#``(L!J7/<`,`0D4&I<]Q``#"=,]P`P#"=`:ESW`#`()O!J7/ +M<`,`@FP&I<;8D+@&I2:EU@U@!PK8SW```()L!J7`'"MC/<````BP&I;H- +M8`<*V,]P``!"10:EJ@U@!PK8SW```()O!J6>#6`'"MC/<```@BL&I8X-8`<* +MV,]P``#"1`:E@@U@!PK8SW`3`,8`!J5R#6`',M@`AD(@0(``I@;TSW"@`,@< +M\:!Q`P_U\<`&"P_USW*@`*PO-8)1(0"``-\&\@';SW&@`.PG9J&`X`CTSW"` +M`*PJ`(!`>$KP%8)1(`"`RB'!#\HBP0?*(($/``!_&"! +MX`"F"?0!V<]PH`#('#&@Q@Q@!RAPSW```$(M!J7/<```@D8&I<]P``!"8`:E +M`(9"($"``*8&],]PH`#('/&@K0(/]?'`1@H/]<]P@`"X*B"`H<%@>038@.`S +M\L]V@`!0'0"&`-T!X('@`!Q$,P"F"?0!V<]PH`#('#&@6@Q@!RAPBW$*#^_U +M`-@`AD(@0(``I@;TSW"@`,@@+8`!0! +M,4"%`]A@>L&Y.0(O]:'`\<"^"2_U`]C/=H``N"H@AL]U@`"X-V!YHL&`X`;R +M((9@>038@.`&]$L#(`!(%000`]@:<,]WIP`42,]VH`#L)Q(([_T%V`ZESW"` +M`%`=`(`!X('@SW&``%`=`*$)]`'9SW"@`,@<,:"J"V`'*'`#V%H.[_6I<038 +M4@[O]2)M!=A*#N_U)&T+V$(.[_4F;0_8.@[O]4`E`1(VV#(.[_5`)8$2-]@F +M#N_U0"4!$SC8'@[O]4`E@1,(AP2E#8<%I0Z'!J7/<*<`F$<<@`>E%X<(I1:' +M":7/<*L`H/\8@`NESW"K`*#_&8`,I<]PJP"@_QJ`#:7/<`4`Q@,&IL;8D+@& +MIL]P+``"`0:FSW!:`$(!!J:*((L`!J;/<$``APT&IL]PT0#"#0:FSW#```<. +M!J;/<(``4!T@@('A!O3/%`GFZ"2_U+W`!PD_@SW&``(B!%*57H1BASW!``(<- +M!J;/"&_[$J4RA56%+'@WA2\@0`Y">3EA4@DO]35YX+@<>,`@ +M8@""(,0"SW&``(B!$J43I1:ASW"``%`=`(`!P@'@5:&!X,]Q@`!0'0"A"/3/ +M<:``R!P!V!&AH@E`!P&5$+B%((0`!J8"E1"XA2"%``:F`Y40N(4@BP`&I@25 +M$+B%((\`!J8%E1"X!2"`#P``@@T&I@:5$+@%((`/``#"#0:F!Y40N`4@@`\` +M``(.!J;/<(``4!T`@,]Q@`!0'4(@0(``H0?TSW&@`,@<`-@1H02%*X4(IP6% +M#:<&A0ZG"(47IPF%%J?/<*L`H/\XH"R%.:`MA3J@1@JO_0Z%2!4$$(PD@H!% +M]HPD/X$-]O((8`<*V'H-@`1"($`@@.`"!A4`B(#@!_+/<(``&#@`$`0`B'"M +M!N_THL#@>,]P@`"X-^!_%(#@>,]Q`0#D;,]R`0`(8Y4$[_D`V.!XX'[@>/'` +MSW"``*PJ`(!`>,]P@`"H*@"`0'C1P.!^X'A1(`"`SW*``'@'"_*`X5'8P"@B +M!,H@803`*"$$`O``V.!_`*+QP-X-[_0$V,]U@`"X*B"%8'FAP8'@O?1"#^_[ +MBB"$"('@M_0`V````IPGT`=G/<*``R!PQH.X/(`"N_UR7$@A6!Y`-@`%`4QD."H<88A_`\1](PA`X`>\@HAP`_K@`4`3$`%`4Q3"4`@,PE8H#,):*`RB'"#\HBP@?*(((/``":&_UR7%`A0`4#C$%V,&^8'K) +M<8/F$/(`%`4Q"B'`#^MRSW```)P9BB-%!0T"+_9*)$```(="($"``*<6],]Q +MH`#('`#8$:$0\,]U@`#$*D"%`=A@>@AQ0(4$V&!Z`]E`A0788'H#V94$[_2A +MP.!X\<`R#,_TSW6``%`=`(4!X('@`*4`W@KT`=G/<*``R!PQH%X.(`"!X`"F`-T*]`'9SW"@`,@<,:#6#2`'*'#/ +M<```PBS/<:``["<&H<]P```"1@:ASW```,)?!J$`AD(@0(``I@;TSW"@`,@< +ML:"]`\_T\`('$$``&42!`@0?T`8'$$``&42!` +M@03R1@SO]Q/8SW"``*`J((!@>0O8T<#@?O'`@@SO^XH@B`6`X`WR&@DO_0#8 +MSW"``+@J((!@>038@.#,"P+_T<#@?N!XSW"``/@>`X`(@,]Q@`#PJ5$@`(`# +M\@&)`O`"B>!_`*F`X/'`N'$.]`HAP`_K!XZ"!M`_`@10`$)8(/`0`` +MP"ZZ97I0'1P.!^"B'`#^MRSW```*@9BB,$#NT'[_5*)$``X'CQP.'% +M`-W/<(``A`:F""``H*#/<*<`%$BHH*4"S_3@>/'`H<&X<`#80,!3)8``@>`1 +M\H+@(/*$X"7R"B'`#^MRSW```*L9BB.*"ID'[_6*)(,/SW"``,`J((!@>0'8 +MA.`!V/'`D@G/]`/( +ME!```,]V@`!0'00@D`\!``#``(9!*)`C`>"!X`"F`-T)]`'9SW"@`,@<,:"R +M"R`'*'#/<20`!P'/<*``["(X$H@5$A`(#*(8(/@`#' +M(,HA@0^``((X$H@5$A`(#*(8(/@`"'4\HA@0^``$=7)J#/<00`QS$F +MH`"&0B!`@`"F!O3/<*``R!RQH/D`S_3QP*'!SW&``/@>(X$O*`$`*('`N0`A +M@P\``"+23B"!!RG8$KCP(,``SW*``!/.-'E984#`BW`*#B`$`]JAP-'`X'[@ +M>/'`X<6X<,]P+``&`<]QH`#L)P:ASW*K`*#_&H)3)8T`@>4`VQ'R@N4V\H3E +M5/(*(<`/ZW+/<```@1F*(X4#L07O]8HD@P_/HX6HA5$E +M`)`*\L]U@`"&4Z:ASW4#`((""?#/=8``1E>FH<]U`@""`J:ASW4$`,8QIJ'/ +M=4P`0@&FH<]QIP`42'>A@+@:HJD'C_3QP"X/C_0#R`'=SW:G`!1(E!```*BF +M!""`#P$``,#"#N__+KC_V)NXSW*G`)A''*+/<8``A`8`@0#?@.#*(<(/RB+" +M!\H@@@\``*P9RB."#P``OP#*),(#<`3B]O]*"AX'CQP+X. +MC_3/<*8`G#\9@%$@`(!7\L]V@`"0!@"&1H"@$@`&+R@!`$X@@0=!*=``3""` +MH`GW2'"`(`H`,B``!(#@#_0*(<`/ZW+/<```K1F*(TL"BB2##PD$[_4*)0`$ +MSW6```C.0"7`$MH+;_8)V0#8D@FO_P\@``2`X`#8#R``!`3T'@S/_P3PI@W/ +M_P/(N1"``!MX@+A`A@JM)H*6(4$#`"$`!!B(C"##CP)Q!/)AN`]X&*DF@J`1 +M``:?&1@`P@O/_V$&C_3@>/'`SW"``,`J((!@>0'8@>#*(<(/RB+"!\H@@@\` +M`)T9RB."#P``CP'*)&(`:`/B]/'` +MJ@V/]#IP&WW/<*8`G#]D$!``42``H!7T9@CO]`/88;V,)?^?\_4*(<`/ZW+/ +M<```I"A1VPHD0`05`^_U"B4`!,D%C_3@>/'`:@V/],]QH`"L+SJ!4B$!`%$A +M`("AP0#=@/2`X,]V@`!0'5CR2@F/_\]W@`"X*B"'8'FI<(S@!_(@AV!YJ7"0 +MX""&*_0`A@'@@>``I@KT`=G/<*``R!PQH&(/X`8H<(MQ$@JO]8H@!P4`AD(@ +M0(``I@;TSW"@`,@"!X`"F"?0!V<]PH`#('#&@S@[@!BAPSW"` +M`/@>`X#/<:``["<.@("X!J$`AD(@0(``I@;TSW"@`,@/'` +M;@@``,]P@`"X*B"`8'D#V(#@(`Y"!,]P@`!$)02`42"`@`ORSW&``/@>38$^ +MD5,B```."R`"`=O1P.!^X'CQP.'%SW6``+`W`(51(`"`#/1B"8`#%@M/^^() +M3_CV#<__`(6`N`"E602/]/'`5@VO^XH@!`*`X!'RL@V/_\8/S__/<(``N"H@ +M@&!Y!-B`X`7R;@]/_PX(``#1P.!^X'CQP*X+C_3/=8``L#<`A5$@0(`=],]P +M@`"X*B"`8'D$V(#@%?+^#*_[XMB`X`_R#@IO_0?85@I@!`AV`@T/__X.+_W) +M<`"%@;@`I=$#C_3@>.!^X'CQP%8+C_2D$`$`^+D(\K80`0'/<*``F`,^H)_P +M`!8-0;RP`!8"05VP`!8.0,^@`!8"04`8A```%@)`4:``%@)!2!B$`$0E`A.$ +MXAKR&-MR&,0``!8#0(CBVW(8Q```%@]`]J``%@]!7!C$ +M`ZEWAB?]'(PG`I()]`+C<'MR&,0``!8/00+P`-_ANV`8Q`,$\@`6#T$H=(0D +M#)`$]`#:(_"`XAKT428`D-$A(H(4\M"(J+G/M=R26L`T@#?PO?BH*KFRB8F$.!X%0*O],"EX'CQ +MP,]P@``T#P[9`=H&"R```-O/<(``;`\)V0':]@H@`$ASSW"``&`.*MD`VN8* +M(```V\]P@``(#PO9`-K6"B```=O1P.!^X'CQP`38X@\O^P'9SW"``$DX`(C/ +M<8``2CC&"R``((G1P.!^X'C/<(``-$?U!L`$X'B`X/'`!?3Z"````-DBH-'` +MX'[@>/'`'@F/]$H-C_O/=H``@`=FV")N`=I:"J_\2'.`X`ST"B'`#^MRSW`` +M`+84V=N*)($)//`"%@413"4`@,PE@H\``/__#/0*(<`/ZW+/<```MQ3`0>.8)K_Q("9N`=JV":_\2'.`X`ST"B'`#^MRH9;/<```NA3EVT`EA!#J\;$`C_3/ +M<:``8!T2L121X'Z!X/'`N'$<]$PE`(#$]DPE@(/,]@HAP`_K!0R($`!C"##C\HAP0_* +M(L$'RB"!#P``X13*(X$/``"B`&P%H?7*)"$``K@4>``@@0^``*@0*'#1P.!^ +MZ+@(\@0@OH\````8`=@#]`#8X'\`J>!X\<"B#V_TV'#/=H``O@8`CL]U@`"\ +M!EH/[_\@C2&(X[D&\@'9SW"``%`X;/`"@(#@7/11(0"!,/+/<8``)"(`E4>) +M4'`H]`"608E0<"3TSW"``,`&`(@FB1!Q'O3/<(``^!X.@%$@0($6\L]P@`!, +M.$"`@.+/<(``4#@,\L]QH``L(#"!0GG7<3$!`"UR``4``-DX\(PF`H`J],]P +M@``PD3>(@.$)\L]P@`#(FP&`$'$!VY5'H8\L=R@`#(#Q?PSW"``(`'P9"AC0HAP`_K +M`"KX'_!Q>!X@.#QP`OT3@W/_\]QH``L(#"!QW%) +M:P#2(J#1P.!^X'CQP&8-;_38<0HF@)"(=SW>@ +M`+0/A!R#O+/<(``51W`J,]P@`!,.,"@SW"``%`XP*@O(`<"'*<5 +M!&_TH<#QP*X)S__/<(``O`8`D(#@^`OB_\H@(@#1P.!^X'[@>/'`;@K/_]X) +MS__V#P`&P@V/_&8/0`'1P.!^X'C@?N!X\,]Q@``D(L]P@`"\!@"01XD0""``#W@" +MC2&%P@SO_P':I0%/].!X\<#/<(``^!X\$(8`#8#`N.8([__(<0&(42``@/'`W@\O]/AQH<$(=AER`-W/ +M<*``M`]P$`D`SW"@`+0/O*`%AN..BW%`)(,P_@GO_T`D0C`*AO8/K_]`)$$P +M@.?2]Q0@3!/P)TX#((P@P`$4@C`"%(,P*@KO_U,F!!`!Y?%UL?/'`P@O/_R8+``;1P.!^X'CQP%(/#_0*)`"`SW"```C( +MI&A5($$$!!`&`07R#"2`@8SW"B'`#^MRSW```-L;JMO!!&_U"B6``3(A``$` +M(0,!`"$.`0`A!0%BB\..`"$"`005A0",($.'S".!CP``4`#,)H&?``!O`,PE +M@8\``)H`!_3EBHGG!?3FBHSG$O(*(<`/ZW(8N!"[!2,-``B^Q7W/<```WANU +MVUD$;_4%)44#`8H")@(!0""0``(B`H3*(<0/RB+$!\H@A`\``-\;RB.$#P`` +MO``L!&3URB4$!(#BQO:`<2H,H`,`(0`$`)4`V0(@``0`M<]P@`#0*\D&+_0V +MH.!X\<#AQ<]P@`!4J0#9):#/<(``9#$BH,]Q@`#X'@"!Q!``!E$@0($\\@.! +M&(B$X#CRSW6``.!"`(5"(`"`RB!B`('@$_3>"Z`$J7#/<8``J$(`@4(@`(#* +M(&(`@.`%]"AP-@R@!"*%SW6``/Q"`(5"(`"`RB!B`('@$O2J"Z`$J7#/<8`` +MQ$(`@4(@`(#*(&(`@.`&]"AP`@R@!"*%/08/].'%`-O/6"AX!W$$/`=Q!#@?\'%X'CQP#8*+_P1V(#@/?3/<8``)"+/ +M<(``O`8`D$>)$'(L],]P@`"^!@"008D0`#9G+G/<*``K"\]H.!^ +MX'B%`P_[\<#N#Z_V%=C/<(``7#@DB(#A#O*#X0GT`XB`X`CT_@X``]'`X'[V +M"$`#_?'[\>!XSW&``%PX`XF`X,3V8;@#J>!^X'BAP?'`X<6LP0#92L&0V1BY +M2,'/%$@`((2 +M\@K`"\&$*`0.`"&`?X``F,L"N0C@-'DA8,]PIP"(22^@H@[@!*EP"-P_!"_T +MK,#@>*'!\<"^"R_T"'*MP0C82L"0V!BX2<#/<(``\*F@@`0AC@\!``#`AB7^ +M$R2]#KT+)D"34,&0PQ;RUW8```!`S":"GP```(#,)H*?`0````3T`8`#\`*` +MKKFON;"Y)7@`HQ##"<4$(X$/`0``P"ZY0"D`!N:Z!7U)Q0WR"L`$([Z/```` +M&$4@P`!*P`7RA2`0`4K`Y+H0\IN]SW"@`"P@!8``VP*X;KB`X,H@S`#)N*5X +M2<`&\.BZ!/*=O4G%$,"!Q4+`J7!&""```ML#R`S"SW&``*06N1B"`!J!.X$D +M>%$@`((,\@*ZSW"``*#+5'I!8,]PIP"(22^@F@W@!*EP"-PO`R_TK<#@>/'` +MM@H/]*/!88`(=4##Z;L`V`JE-/($(X`/`0``P"ZXSW*``-!-"F))(H(`8;I+ +MI1)J%'C'<(``D,S*@,]W@`"(J\:E"X#/=H``^!X%I<.&(,#4AO6/!'[D?@F^ +M0"D/`N5^Q7@$(X,/````$&5X!Z4(A1CBGK@(I4NED/!1(H""&O+/<(``6$4` +M@$'`Z+A"P`_RAB#_"2.X`>"!X,GW@N`$]`;88<`E\`?88<`A\"+`8<`?\$'# +MSW*``)`&0()&@IX2`@:&XA3T!".^CP```!@.],]R@`#X'D2"2(($(KZ/``8` +M``3R`=@*I03P"J4`V`'&Z+X?\D+&(L*@XLHB(0`$)H\?`0``P$$OA!-$)@\6 +M([\!YP0FCA\&````,;X`)L43SW:``-!-,B8.$0(F3A$2\%,FPA#/=X``'%)= +M>DIG!":.'P$``,`NOL]W@`#03 +MA"@$#@`A@'^``)C+`KI4>D=@8;Y88.:E`8`$(X,/+P``W2:[Q7M2(\,#!:5G +MI<]P@`#PJ0.`$2!`@`#?#O+/<(``=$4`@."X"O*&('\/'7A`*,\#!/``WX^_ +M@.<<],]V@`"X*B"&8'D`V(S@$?(@AF!Y`-B0X`OR((9@>0#8D>`'\B"&8'D` +MV)+@!/3>#B_\`-@(A05_Z*4=`2_TH\#QP+((#_3/=8``U"H`A<20R7#J"J`` +MAB#\`P"%R7%Z"F``AB'\`YK@SW.``,RK!?0A@X"Y(:-*@P'B2J//1 +M$P&&P[E0<0WTBB4($!,;6(.1$P&&P[E0<0/R$AM8@[T`#_3@>/'`0@@O]`#8 +MSW&@`*@@*('/<:``U`LX@<]QH`#$)U(1`H85$0*&0A$#AOZ[!O(!V,]Q@`!0 +MJV&Q42+`@!IPRB5B%!+T42#`QLHEHA0,],]PH`#0#R`0`88?$`"&$'$`W22RBB"(!<]R`/\`_RX-3_T`EO8*K_TTEI07`!"`X,]R@`#`.P'9&O)M +M@@'C;:)K@GA@"Z+/<(``5!T@J!^&[K@&\L]P@`!P!B"@"/#ON`;RSW"``'0& +M(*#/<*``_"43@&R">&`,HL]R`*`(`.QP0*!O(D,`['!`H`X?6)!""D`&SW`` +M`/]_SW&@``PD`:$;V`2A50?O\ZEPX'\!V/'`X<6AP>8,+_N+<(#@-_0`%`4P +M424`@`SRA@@``,]Q@`!0JT.!SW&``.R10:$G\%$E@(`$\G(.S_\A\%$E0(($ +M\OX-S_\;\%$EP(`<\@C8SW6@`,0G$QT8D!((``&4X`WR`M@\'0"0SW"``%"K +M(X#/<(``[)$AH!G8F>#%\^4&[_.AP`HAP`_K/'`X<7/<(``4*L_@`0A@0___X\X!"6`7P``<,,]Q@`!0JQ^A1"(`4XC@ +MSW6``%"K)/11)4#1(/(J#\__426`TYP=`!`)\L]P@`!,'P6(F!T"$!7P427` +MTPGRSW"``%PB&8B8'0(0"_`#A98*+_4DA9@=`A`%\`#8G!T`$)P5`!"`X,P@ +MXH!X\L]P@`!,$PN`@.`/\L]RGP"X_QVBSW&``#@=!($!X+.XM;BXN`2A%J+/ +M<*``J"`(@!^%42``@0?T427`T@7T@-B8'0(0F!6`$%$@P(%`*`$&"?11(H#3 +M@KD.\H8*0`(:\!^%42*`T[.X'Z7%(8(/````!T4A``;/<8``W*LLB88A_0]2 +M(<$!1;DE>,]QH`"()!"ABB#6`,]QH`#$)WX9&(#/<*``U`L!VE*@!-@0&1B` +MSW6``%"K'X51(("!)?(4E5$@0($A],]PH``L(`^`@.`;]*UQ$@WO^%8E0!6` +M%0`0E+B`'0`0'X60N!^E#?#/<8``1#L/@0'@#Z$0V<]PH`"0(SV@.07O\QG8 +M\<"V#._S`-D(=@&`P;B#X,H@02`%\KH.(`#)L]SH`#$)T$;F(``VHRZ`B9/ +M`/IBR[K7<@````A`+0\#D+]2]P4GCQ%B&]B#C"("@,?WSW&``+@\$H$!X!*A +M`-F=N4;PY7EB&UB`UW(``,`/4@`.``XB@P\````0SW*``,"%%GJ@XR""!!(% +M`$_W`-@/(,``8;A.(P\(`2G"`WAY!7D`+<``!7H6\$(C`P@`V`\@P`!AN'AY +M!2$"`(HA_P\*\,]S@`"X/!.#BB'_#RAR`>`3HP'8SW.``&"2`*L"&P0!(:-" +MH[WQ`-F(`=`!!`)@`2H!T`$`+9SW"@`/0F(Z`EAL]P@`#LD2&@ +M60/O\PIP\<#>"L_S"'95($T$#VXT2!B@`?R!,A6#J__F!```,]P@`#, +MJPR`SW&@`,@?9.`>H1#8#J$!V!49&(`!AH#@!?11(P#`^_,!AL&X@^`F`@(` +M`(5!P`04`#%!*!`#$(91(("!!A01,3WR#XF!\"$$HB0"`'\CZ' +M2B(`()6Y/J<$N,]R@`"HDB:2!2``!#!P"O+/$MHEIP`9:T$I#2&E>T<8V(`-S.NX#O(0VZNX#!K<,`T:'##/\]UH`#`+T<=V)"4X<`AA@\``),`SW.@`&@L\"-!`"NPCQ4!EN>Y(?3/ +MY]J;A@O:FXH+VIN." +M]J;L\P?P2B0``@SPY[G*(2$`0,$!%((PQKG&NEBH.:B(<*$`[_.BP/'`X<7/ +M<8``^!XC@4B!42(`@"ORAB#_`<]R@`#034.X"F(`VX#BRB'!#\HBP0?*(.$' +MSR`A`\HC@0\``&\`RB3!`+0%X?3*)2$`@>+/<*H`#%"Y@<;W@+VYH0'9):`% +M\*"]N:%EH&T`S_/@>/'`\@^/\PAU#A`\@-$@$VA!`" +M`<]P@`!\JC5X*8!982F@&M[!\<]Q@`!$.PV!`>`-H0'9SW"``%0=`=I`J,]P +M@`#,JTZ`!H(!X`:B`O`!V0+:SW"@`/0F0Z!#A<]P@`#LD8#A0:!(#$(#80>O +M\\EPSW.``%"K6!.!`(#A`-H/]#R38KD0N44A0P'/<:``]"9CH<]Q@`#LD4&A +MU0```.!X\<"^#H_S"'8!@,&X@^``W07RO@@@`,EP"'6`Y3+T$(91(("!(O(, +MS,]Q@`#(.E$@0($7\D#8#!H<,%41``8`V@'@51D8``W(SW&```"J%'D#R$"I +MX@FO_Y@0```&\*P1```!X*P9``#/<(``5!T!V2"HSW"``,RK+H`&@0'@!J$" +MV<]PH`#T)B.@(X;/<(``[)$AH)4&K_.I<.!XX'\(V/'`)+E3(<(`B.+/<8`` +MW%16>0GR09!A@03B<'+*("("`_0@@4!YT<#@?N!X\<#AQ0AUSW"``,RK#(#/ +M<:``R!]DX!ZA$-@.H0'8%1D8@`7PI@WO^GG8`86`X`7T42,`P/CS`87!N(/@ +M#_3/<(``5!T!V2"HSW"``,RK+H`&@0'@!J$`V`SP`851(`"``-C*(.$%(851 +M(4"`RB"A!/4%C_/QP'X-C_//=H``S*L!A@0@OH\`<```.O(O*0$`SW"``)@X +M]"!-`"N&3R6`$&8-(`))AH#@$_2,)0.0SW&``,@Z!_2Z$0`&`>"Z&1@`'O"Y +M$0`&`>"Y&1@`&/`!AOZX#_+/<8``$#H,B4^)$'(,\A&)42#`@"@,@0(&\`#9 +MSW"``&"2(*@N"``&806/\_'`[@R/\PAV`8#!N(/@`-\2](#GSW6``%"K2?00 +MAE$@@($[\A"&[K@+\L]P@`!,'P6($_#.#N__R7`(=^OQ$(;ON`?RSW"``%PB +M&8@%\`6&)H92"<_T42#`@9@=`A`(\AZ%E;@>I1^%E[@?I8`5`!`$(+Z/$'`` +M``_TG+B`'0`0,(:.#*_X5B5`%4`F`!*@'0`0`-@%M@'9SW"``%0=(*BT%0$0 +M!H$!X`:A6!6`$(#@&?3"#H_Z@.`%\A"&[;@!V`+T`-C/<8``LJOT(0``/)4X +M8&*X$+B`N,]QH`#T)@.A!O`"V<]PH`#T)B.@)8;/<(``[)$AH&$$K_/I__R7`:<,]PH``L(`:`$'A,(`"@SW6` +M`%"KRBN(`=`!!1(4""0"8`$J`=`!#J\P'9SW"``%0=(*BT%0$0!H$! +MX`:A6!6!$(#ASW"@`/0F$_3/<8``LJM`+H<;Q`MDCH"6&SW"``.R1(:`M`Z_S"G#@>/'`R@J/\\]P@`!,$PN`@.`/ +M\L]RGP"X_QVBSW&``#@=!($!X+.XM;BXN`2A%J+/<*``J"`(@%$E@-//=H`` +M4*L&\L]P@`!,'P6(#O!1)<#3!O+/<(``7"(9B`;P`X8>#Z_T)(:8'@(0'X91 +M(`"!"_11)4#3!_11)4#2!?2`V)@>`A"8%H`0Y[@+\E^&/H:SNI6YE[H^IE^F +M`-D!W1;PG!8!$('A$/0_AE$A0(+/<8``^!XC@2F!!?)$(0T$!?!$(0T"`_`! +MW039&+@E>,]QH`"()!"A'X91(("!'/(4EE$@0($8]$X.0`*`X!3TSW"@`"P@ +M#X"`X`;R#">_ZBB#>#%$@@,0$]%$A`,;X\\]R@`!0JY@2@`#GN`#9 +M"O0"N,]Q@``0OQ9X`6$MN<"YBA(``4\2@@#/`/H0/9SW"@`-0+,:#@>.!X +MX'C@>.!XX'C@>.!XX'C@>.!XX'C@>.!XX'C@>#&@SW6@`,0G%14`EL]QGP"X +M_Q:A,14`EA:A$-@0'1B0B@ZO^HMP@.`7]``4!3!1)8"`#?0*(<`/ZW(-V(RX +MBB-?!P4&K_2*)(,/!-D3'5B0&]D6'5B0S0"O\Z'`\<#/<(``.)+_U&-G/ +M<(``<(*Z#2_U&-G1P.!^X'CQP"H(C_,:<,]UH`#4"Q"%`-^!X*'!0,<.\@HA +MP`_K<@_8C+B*(Y8(BB2##YT%K_0*)0`$SW"@_G@"SW:?`+C_%J98'@`4SW&@ +M`/Q$&8$$(+Z/```((`/T'8%,(,"D"/+.#:_ZBW"`X,H@`B!"(,$@E.%N`0T` +M,B9!<(```$A`)X!R-'@`>,]Q@`#,JTZ!"((!X`BB#H$(@!:F5@S```#9*'!* +M\,]R@`#,JRZ"!X$!X`>A#H('@!:F\_'/`,H0Z"#(`6INGQ +MSW*``,RK+H("@0'@`J$.@@*`)_#/<8``P#L%@0'@!:$?\,]R@`#,JRZ"`X$! +MX`.A#H(#@!:F`=D`V!;PSW&``+@\&H$!X!JA%J;>"F`$`=B]\<]Q@`"X/!2! +M`>`4H1:F`=@(<8#AO`N"`,]P@`!0JQ^`\[@)\L]P@`"TA>NHSW"``.B"[+`# +MV!&EX'C@>.!XX'C@>.!XX'C@>.!XX'C@>.!XX'C@>.!XX'@1I1$';_.AP!B& +MSW*``,RKD+@8IAB&L+@8IBZ"!8$!X`6A#H(%@!:F4@@``,;QSW*``,RK+H($ +M@0'@!*$.@@2`]/'/`1H0Z"$8"Q\<]Q@`#`.PZ!`>`.H93Q +M"B'`#^MR0=B,N(HCV`<>\>!X\<`^#D_SSW"``,RK#(#/=J``R!\0W0'?9.`> +MIJZF%1[8D\]QH/X@`L]PGP"X_S:@*@C@!0G8`]@>IJZF%1[8DP7PF@VO^HH@ +M50[/<*``#"0'@(#@!/)1(P#`]?-)!D_S\<#/<(``S!.^#B_[`=EOV`:X=@XO +M^PC9!]@*N&H.+_L%V='`X'[@>/'`X<7/<8``^!XC@2F!42%`@,H@H@`K]$2X +MSW&``(@XP[@)8>"Y!?)1)8#1'/11(4"`'/+/=8``^!X#A1B(@>`/\OH/3_J` +MX`?RSW"``$`B"(B'X`7R`X48B(+@!O11)8#1!/(!V`/P`-C%!4_SX'CQP$(- +M3_-$(A!337:&)OP337!-<`0E@%\````@02A^@P7RJ@]/^H#@`_0`WP+P`=_/ +M=8``4*L?A?&X!/(`W:3P3"``H/SUA@]/^H#@'O+/<(``0"((B(?@S"!B@A;T +M`86,(/^/$O0DE<]P``#__Q!Q#/0%A8P@_X\(]`R5UW```/__RB5A$(#RSW"` +M`/@>\"#!`PF!42!`@07RSW"``)Q.!/#/<(``J$XXB2I@02X`$<]Q@`"T3@AA +M%GK/<(``7%5(8."X!?(_A88A]H\7\N&X!?(_A5$A@((1\N*X!/)1)0#2!/(! +MW0SPX[@)\L]QH``,)#&!C"'_C_;S`-U1(("!RB4B$,8.3_J`X`CR!"6^WP`` +M`"+*)6(0@.4H\L]Q@`!0JQ^!Z+@/\HPF`I#,)H*?``!0`,PF@I\``-```_23 +MN!^ASW"``/@>`H#"$``&@.`:\HPF`I#,)H*?``!0`!3T3X%%>`^A#_#/<(`` +M^!X#@`F`X;@'](PF`I`&]%$@@($"\@+=(01O\ZEPX'CQP*X+;_,`V<]P@`!, +M$P"`@."BP0_RSW.?`+C_'://+`;``#/<(``6'\YH,]P@``(K""@!-[/<(``6`?`H)D3@`"@N)D; +M`@#/<*``Q"=D&%B`SW8``/]_$QB8@QO>%AB8@QH86("*)_\?SW:@`/Q$_:;Y +MIHHGF!W/=J``4`SBIE&E4*4\&$"`BB(8"$ZE@!4"$*0;0`!1(D"`SW*``.R1 +M6!M"``WR0A``A@0@OH\`P```!?(!@H#@`_("HB&B@!U`$%$A@,//"(X+CW"(+KN"`,`@8?ARNX4R`0`%$@ +M@,6K\L]Q@`!$)0:!`>`/>`:A02F`0\]Q@`!$)4:!SW&@`+0/-X'`N#!R`-V? +M],]QH`"H(":!C"&#CC(!#0`2<)#TSW*``"BL!8+/1!`)\%0>1!,$(8$/__\``"BB#8,&H@0@ +M@`\```#^*;A6'@00'X;KN"/RSW"J```$!(`)HL]P@`!P@B"(@.%D:#;R@.%B +M`"X``A"$`)]Q`-BH((`#]",/`!7=$[WP)<\3SW6``"BK%7T!X."E'?#/<(`` +M.)(@B(#A9&@:\@(0A`"`X#@I2&J`AH"`;06`1`"W0&!`>`!H0SP!""^SV`````$]`3=!O!1(P#` +M`O(#W3H/;_KQV('E1?."Y1;T`MT$(+[/@`$``,HEHA$%]%$C`,#*)>(0@N7T +M\\]PH``P$`.`@.#*)6(1AN5"!`(`SW:``%"K')9"((0`'X;KN"\D"`%\\L]Q +MJ@``!**!SW"E``@,0(#/HFX!7I2 +MIJRC3:,`@4@6CQ"4YPJC&O(%]HKG&/0CN`WPM^<-\N[G$O1%*/X"02G`<%$E +MP)'"(&(`!]T*\$4H_@)!*0!Q^_$BN/GQ`-@(W2&!%Z:4X"NC'+,.],]W@`#X +M'N.'Z(<$)[Z?``8```3RC+I2IN2YRB4B$N&YRB4A$H8A_@]!*0(!31Z"$"B3 +MA^5%>2BS$_2.X`?=D/?/<8``^!XC@801`0`P<`CWSW&@`#`0*($P<`/R"-V' +MY8(++_N(<`7PH@LO^XAP!""`3X`!``#7<``!```` +MV1;T`=A.'@(0SW*``"BLFA:`$$(>1!!-'D(0-Z8IH@2X*)*)N"5X"+)V\$T> +M0A#/<*8`C`,]@%$@P,<$(8`/.````$$HP@2:'H(0!"&"#P```/`LNB6X17C/ +M2BS'+.'Y3*"+:,_],]QI@",`[V!!"6!'P$````PN4X:0@!. +M$H$`@.&IHQSRC>8L]%$@`,8H]!38SW&@`,@?'J$0V`ZA`=@5&1B`"MU1(`#& +MRB7B$5$C`,#*)2(2BN7W\Q7PCN"2]\]Q@`#X'B.!A!$!`#!P"O?/<:``,!`H +M@3!P!/0'W0/P"-V'Y>?TSW:``%"K3A:`$(#@X?+/"5X1">!$!2Y)7B(N$0G`1)!*<&` +M4B!`!1*F6!Y"$,HA@@\``/__RB&!#P``$!\Z<3>&0!Y$$`0B@2__`P#_*+DW +MIG8);_@`VO*_K!X`$#KR2!:"$#*&H.+1(>&"-/($(8./`````0?R1"$-!B.] +M`>6!Y0GW!"&-#P```"37=0```"0@\@0AC0\&````,;V"Y30`#0""Y0GT@.,4 +M\D0A#08CO0'E@N4.](#C`_+,X@KV5X8R(3"_#/<*``,!`(@#>&$'$'W&,!D`!!RQ$H;KH0VAK!8`$"@9@`0=L8?E!/1J"```"'6`Y0* +MHO_*($(#`-C/<:``U`L0H<]P@`#*0!O0`V(^X#!H<,#+9SW"@`,@<*J`1`R_SHL#QP+H* +M#_//<*``U`L8@$(@``A((```SW&``$0E)8&!X8HAF0X(],]Q@`#X'B.!/H&` +M(9D.$'$`W`KPCN`!V,!X!O!1(4"!$O0`V,]Q@`#X'B.!*8$] +M>5(A`0#`N21X@.`(\A^&D;@?I@OP%X;H\5H,``!8%H`0@.`L"P$`@.5X`@(` +MSW6``%"K6!6`$(#@$_("V<]PH`#T)B.@SW&``.R1`-@!H<#9F16`$("XF1T" +M$"AP`O!"V,]QH`#$)[\9&(``V`P9`(`!V!`9&(`?A?&X%`("`!*%-X7:#B_X +M`-JL'0`0'X7KN%GRSW"``/@>0X!(%8$0%((D>$0@`P%$(0`,0B@$`8!SSW"` +M`*P?P;MH8(FX'*5P%8X0]()-%8`0Q'^&)O\3!']$OM]GSW:``$Q.]";/$V(= +MQ!//=X``T")K9XF[?:5T@G05CQ#D>X8G_Q,$>T2_^V/T)L,09!W$$'*%>J54 +M@GNE1'DD>,]Q@`!<3H!P]"$"`,]Q@`"$3O0A``".'800D!V$$)(=!!"4'000 +M`-A.'0(0F/!.%8$0SW"``!PX@.$`@,"X4_*`X`#;RB`B``KT\*X`"".#X``H!\PCMB.QW"``,PBSW>``/B12(AE?MREA"(8AV$$P5[?:5T%8`0P[@<>/0G`!`ZI<]Q@``(DF0=!!!H +M%8`0P[@<>/0A`P!;I8X=Q!#/,]Q@`"<'PEASW*``/B1/*5P%8$0P[D\ +M>?0B00!B'400SW&``,`B"&$=I705@!##N!QX]"(``,]Q@``(DE*%9!T$$$@5 +M@!##N!QX]"$#`%JESW&``%"2]"$``(X=Q!!;I9`=Q!"2'000E!T$$#X/0`'/ +M<(``^!X#@`B`Z[@&\DX5@!"`X-P(P@58%8`0@.`%\K((#_\#\"H-```(=?4' +M[_*I<.!XSW&@`,0G%1$#A@38$QD8@!O8%AD8@`/:SW"@`-0+4:#@>.!XX'C@ +M>.!XX'C@>.!XX'C@>.!XX'C@>.!XX'C@>%&@Y+OA(,('%MA2$0"&X+CA(,$' +MRB#A!5$@0(`$\E$CP(`$\N!_$M@!V<]P@`!4'<]R@`!0J[02`P`@J`:#`>`& +MHQ^"[K@'\L]P@`!P!B"@!_#ON`7RSW"``'0&(*`5V.!^X'C!V!0:`C#/<8`` +M^!X#@1B(`=O/"PB/!``S@&"#``&8:!`!F$@`!`^`$((`/``#\ +M_YVXG[CL"AN`!V,(@`0`8(0$`['`@H.!^X'CQP((. +MS_+/<(``4*LR@%$A0((2\L]Q@`#X'B.!2!""`#2!1'E1(8"`2-K*(H$/``"0 +M``/P#MH`W\]QH`"H(">!K!`-`%EAL7'")440RB7F$K!XB@OO^@K9SW"``'PT +M`)#/=J``Q"=1(`"!!?*,)0.2`_<`W1OPSW"@`+0/_*#/<*L`H/_ZH+8([_T` +MV!D6`):`X`7R`M@0'AB0SW&``+@\&X%JO;A@%-T;H1D6`):`X`?T42$`QN@/ +M(07*(&$`+0;O\JEPX<7/<8``**Q!B<#;%!K",,]S@`#X'F.#$FI'X`0@@`\` +M`/S_:8,JN\"[%[O'.QS`*,%R.QS`*-*),!S`-NH(,`!\"'-`.QP +MH*`!XX#B`-G,]\]P@``HJ_`@0P#L<&"@`>%0<;CW`-G/<*``U`LMH`O,`>`0 +M>`0@@`\``/^_C[@+&APPSW&@`(@D'J'@?\'%X'CQP"8-S_+/=X``4*M`EPAV +M2'&&(?P#0BD%`40B"`.,%P$10BB($$#8SW6@`-`/"B-`@!`=&)#*(V(`K!<` +M$$`KA@7/6$P>68?1!`%]`Z74R#`@!#RSW"``/@>`X`)@%$@`(`]V,`HX@7* +M(*$'P"@A!@KP0"@`$:!PSW*``#@>"&(7N`/A!"&!#P``_/\%((`!)7B=N)^X +M#!T8D`O,`>`0>`0@@`\``/^_C[@+&APP#AV8DR`5`);/<(``^!X#@`B`Z[@/ +M\N2^#?2R#:`%R7#/<(``9*R@V<3:/=MJ"*`!%[N!!,_RX'CQP.'%SW"``(`' +M`)#/<8``^)FHV@'=@"!$"Q!X2@WO^JES@.#*(<$/RB+!!\H@@0\``+44RB.! +M#P``S`#*)"$`;`$A],HE`0'V"$``SW"``$`N.03O\K2@\<``V<]PH`"T#SR@ +M5@^/]@8(#_[2"T_[6@ZO_0#8_]G/<*L`H/\YH#B@T<#@?N!X\<#AQ<]Q@`#X +M'O`A`@!*)$``PQ(!!@]X,B*"#P``'P,$(8,/``8``(#C`=O`>P0AC0]````` +MUW5`````PB0"`=X-;_O`N;T#S_+@>/'`0@O/\HX(H`((=<]Q@`!0JQ^!L+@? +MH<]PGP"X_U@8P`C/`':`-G/<*``M`]3^@@.70`P(``,#IN-KRSW6` +M`-0J`(6*(0@`Y)#/=J``Q"<3'EB0SW&``%"K/X$Z=_&YAB'\(T/R02D!(<.Y +MSW*``-Q4-GH@@D!Y"'49%@"6@.`%\@+8$!X8D,]P``#_?Q,>&)`;V!8>&)`# +MV<]PH`#4"S&@X'C@>.!XX'C@>.!XX'C@>.!XX'C@>.!XX'C@>.!XX'@QH,]P +M@`!8?QF`@.`H#T(!FN4P`P(`SW"```C.%@G@`P#=(0,``+X+;_\J(Y\PGXI7*)<$3+?),(`"@"_+^"B``@<`*)0"0'/0:#.__ +M`<`8\`/9SW"@`-0+,:#@>.!XX'C@>.!XX'C@>.!XX'C@>.!XX'C@>.!XX'C@ +M>#&@`-V:YP?TSW"```C.F@C``X#EH`("`/X-P`'/<(``4*L?@.ZX"/(!V<]P +M@`!P!B"@"?#ON`?R`=G/<(``=`8@H!$6`)8`W5$@@(!!P!CTN@[O^8'`"B4` +MD!+T!!0%,%$E@(`,]`HAP`_K<@K8C+B*(\<*,0;O\XHD@P^`Y3@"`@`$V!,> +M&)`;V!8>&)#/<(``6'\9@(#@$`Y"`1D"``#@N,;RSW:``%"K$H:&(#H`C"`$ +M@F`-Q0'/<:``#"0\@1>&(GADN!!XBAX$$$0B`%.(X`OT'X;QN`7T425`T0'8 +M!?0`V`/PX@I/_X#@G!X`$##R5@T/_PHE`)#>]`W,42#`@1'R'X91(("!#?(O +M((<*C"`"A@?TSW&``%"K'X&8N!^AG@D@`('`"B4`D,3TSW:``%"K'X;PN!7R +MJ!8!$-38X@\@``>H0'?SW"` +M`%0=M!8!$."H!H$!X`:A'X;SN!0)0OH/AH#@(`A"^A^&[K@(\@'9SW"``'`& +M(*`)\.^X!_(!V<]P@`!T!B"@`-C/<:``R!P'H3#8"J$R"N__`<`?AOBX$_(0 +MV`P:'##/<(``",[:#H`##<@`(($/@`"(JA^&X*FXN!^F`):&(/P`C"`"@!OT +MC@K/^8#@%_0#V<]PH`#4"S&@X'C@>.!XX'C@>.!XX'C@>.!XX'C@>.!XX'C@ +M>.!XX'@QH`"69@IO_#261O!1(,"`0<`5WP/TZ74@\`C8SW:@`,0G$QX8D$8( +MS_^4X`AU%O("V#P>`)`A%@&6SW"``.R1(:`1%@"642"`@.?UG@SO^8'`"B4` +MD.'SE>4>],]PH`"0(QZ`!!0$,%$@@(#*(<$/RB+!!\H@80+/("$#RB.!#P`` +MY`0`!.'SRB4A`-H.K_^(<`AUJ7"M!J_RHL#QP%(.C_*AP0AV`-A`P`"F/@SO +M^8MP"B4`D)7TSW"@``0E(H``A@0A@0__`%__!2$"`$"F4R&"`%,@@P!E>H?B +M8/11(H#3SW"``%"K'X!"\OJX:?0$(+Z/`!X```CT`(8/\,]P``"C"<(-S_E1 +M(H#`^?51(@#``(8#\H6X`*;/%$@@,0`IC`/X@3*("((J7!Y!:_RH<#@>/'` +MX<4`W078"[A2"J_ZJ7&R#8_SSW"``%"K'X#KN`WTA.7:``8`42!`Q6GT42`` +MQ57T`>54\`#9G+G/<*``T!LPH`'9SW"D`)A`/*`%V`CP4@D``/X.X`0%V`'8 +M]@[`!(3E;@`&``0@OL\P`````>7*)2(042,`P!KT42!`Q07R42&`PR7R42`` +MQQOT42#`Q>7S42&`P^/SSW"J```$`8"&(#\+@^``W=3U$_#V"```SW&``#P\ +M#H$!X`ZA"?``V9RYSW"@`-`;,*#:"````-G/<*0`F$`\H!'P`-U1(P#`!?1R +M#N`$`=B<\;H(``#/<8``/#P.@0'@#J&!!(_RX'CAQ0S,1"`^BDKRX[@J\H#8 +M#!H<,`W,SW&``,`[SW*@_I0!SW.?`+C_Z[@`W0;R'8$!X!VA2'`'\!6!`>`5 +MH4`B``T6HPW,42#`@`;TSW"@`"P@KZ`-S(8@@@(-&APP(/!1($"!'/**(`0` +M#!H<,,]Q@`#`.Q2!`>`4H0W,`-E&((`"#1H<,,]PH``L("^@SW&@_O0!SW"? +M`+C_-J#@?\'%\/'`+@N/\L]QH`#\1`6!`-Z\N`6ASW6@`-`/U:4# +MW]X-K_+I`SW"``$0E!H`0<03T +MN@_/_P3PG@_/_]'`X'[AQ>'&`-D'V`#:M&FT?<=U@`#XF55]P)6,)@*=`-N% +M]HPFA9+#]O_>P+7!G;SF!?:,)C^10O9AM0'B3WJ,XJ?V8;B`X`'A+WD@]\'& +MX'_!Q?'`B@J/\@#=SW"``#"1_@RO^;2H@.`3\@C>@.7,):*0S"4BD`#92B0`*HT>`'A8+#/=8``^![/=H``=)%`)0`7)&ZN +M#^`!!MI`)0`50":!$IX/X`$&VD`E`!=`)@$4D@_@`0;:+!6`$(3@"?0\%8`0 +M]@AO_BV%/@P/^0Z%42!`@5@(0@/J#(_Y@.#$#L+_SW$``/__SW"``$S++*#Z +M#J_Y*Z`5`H_RX'CQP*()K_(4V<]U@`#P(MC<#@\O]`(E`!//<(``/"4"#R_T +M%-G/=H``^!Y`)@`5`*;`W`(E`!,!I@#=J7"I<5(/[_(&V@'8J7%&#^_R!MH` +MADHD@'"I<0*F`Z:H(,`%%29"$&""BB#�VS`((!X:F@`(*IH`""P!A8`P"" +MP1A8`P""PAA8`QO9SW"``!`Z@0&O\BRH\<#Z"(_R&G`Z<5IRU@FO^7IS"'<$ +M(H,O``8``(#C`=O`>P0BC2]`````UW5`````2B1``,(D`@$`WL]UH`"T#]RE +M@.?/<(``D`8$B`GT+R$'!"\B1P1*"R_[`]@#V'(++_MJ<8#G!?3*"$`#`_#^ +M"$`#SW"``$0E!(!1(("`$?+/<(``5#@`@(#@"_0YV/8)K_F+N('@!?02#<__ +M"O``V9ZYSW"@`/Q$(:#@>,&@W*6A`(_R\M=Y.& +M(_X'1;MHK8#B$_+/@*MX8OCK>*+Y*UCBV6M(XHZ#^`` +M)JV+<*EQ>@BO\@S:`,`!P3H+K_,"PHMPJ7%F"*_R#-H`P`'!C@JO\P+"SW&` +M`#0'`*&B"Z`$R7#I!V_RH\#@>/'`?@]O\E?8SW6``/@>(X7/"[ +M`*(#\E_8`*+BNP/RA;@`HE$C0(`$\H>X`***)@L6RV'980#:@./*(($`SW*E +M`.@/!J(`B<]QH`"D,(#@`8'/(.(`T"#A``&AP@I/]@.%SW&@`,@<3X`"X$BA +M4@V@``(V@N`"M@!6`$*"X@!T"$$`5@!"@N$`=`A`0C:"X$*V0%8`0 +MH+B0'0(04!6`$*"X4!T"$`'BWO&0XD0`!@``(HT/@``X'@"-@+@`K8`5@!"` +MN(`=`A!`%8`0@+A`'0(0$(V`N!"MD!6`$("XD!T"$%`5@!"`N%`=`A`!XM_Q +MYKD1\L]R@``X'@B*@+@(JH@2@`"`N(@:`@!($H``@+@1\(#C$?3/(!."`*"Z(!N"`*`3@@"@NJ`;@@!@$X(` +MH+I@&X(``>#@?\'%\<".#4_R@>#/=H``^!X:<`/T`(8"\`&&Q!``!A4F#11, +M(`"@`=\EN%,@!0`@AE!$_R\<`>#&_R`-D*)`"@ +MH<'*(6$`%?+/<(``1"40$`4`426`@`WT"B'`#^MR?MB-N(HC"`VM`:_S"B0` +M!<]U@`#X'A4E#A4`AA4E4A`D$!4``!(!("`0%@`H$!#,(.*![O7/<(``F$H`@`39O=H>VT#`BW#Z#N``&+L!V'IP`(8( +M@%$@`(`'\BP5@!"$X`38`O0&V#IP`(8H@!20!"&/CP`&```%?P?R]KG")Z(0 +MP">A$"IPBG'R""_UZ7*`X,H@0@3@""+URB'"`TPC`*`%\BH(3_>E\-(,3_D$ +MV<]P@`!$)?8,+_TDH""&R!$`!H8@?XY$]%8(;_R*<-H/8`2*<``2`"#($``& +MAB!_CD'TSW"``$`B+)`>E1!Q!O+2"8_XDG`W](IP"G%*"J_T`=I_V1&YSW"@ +M`+`?-*">#D_Y/@T``8#@"/0+R`4@@`\````\"QH8,'(-``&`X`GT"\@%((`/ +M````U`L:&#`+R)"X"QH8,/H-3_(-\"090`4@AB`9@`4@AB@9Q`4@AAH9!`8: +M#$_Y@.`*\DPD`*``V`3TV@K`!`3PL@C`!`'=T@P@`JEPSW"``"`=(@P@`J"H +M3"1`H!3TSW"``$`B"(B)X,P@XH$$]$P@`*`(](C@"/1N#$_Y@.`$\@H*#_1J +M"0_Y#@\/]P3*D.#,((*/``"S``[R"B'`#^MR`1($-I+8C;B*(XT&K09O\PHE +M``7Z"^`!`-@E`6_RH<#QP/X(3_(`W<]V@`#X'@C?`(;$$`$&02E``5$@`(`. +M\H#ES"6BD,PE(I',)6*1!O2`Y``"0BSW6``)210"<`%21M-@Z@`0;:0"<` +M$T`E@1(J#J`!!MI`)P`50"4!%!H.H`$&VLX6`!;/<:``Q"?/"('1@0 +M!/($V`P:'##6"N_^`-[/<*``_$0E@+RY):!K%0`6C"`"C5`'QO_/<(``.!T` +M@.NX!?+/<)\`N/_=H$H@0"`,S.2X(?3FN$/TAB#_A`=I4H@`"">\`W,4R!` +M@!7R!,@#$@$V`QH8,!H/(`,$&E@PSW"@`/Q$)8#/=:``R!^\N26@`-X&\,]U +MH`#('P#>&_#/<(``A`8`@(#@%?)R\.8.(`,`WL]PH`#\1"6`O+DEH,]P@`"$ +M!@"`@.#/=:``R!]@]`;(!""^CP.`Z%-N]5$@0,5L]2H/#_I,(0"@&_+_A:`5 +M`!`))P`0Y.#-]L]P@`"LJ0"`42!`@`7RWJ6"#Z``$-CDY\?W0!4!%C!Y1@LO +M^A#8BB`(`*`=@!,.I1^%@.`$](H@!``.I28*0`$OV)6X$AT8D,]P`0#`_!4= +M&)#N"P`#SW&``.0V`(&'X-GRSW"@`#@N!8`$((`/P````-=PP````,CR]=@% +MN,]RGP"X_QJB!]@;HFG8&+@9H@'8O?"F"D_]G_$`WHCP%84!X!6ESW"``!`Z +M$8A1(`"`E`@B`\H@8@"`YP7R'(4!X!RE#,SGN`#>6O(-S`0@A`\````8#"2` +MCP````@S\FH,#_<-S%$@P(`_\L]QH``L(`6!)H$*X#!P5@`-``,2`38"V`P: +M'#!0V+H(+_Z8$0$`A@T``\]PH`#\1"6`SW6@`,@?O+DEH$#QBB`$``P:'#`4 +MA8#G`>`4I<7S&X4!X!NEP?$&#F_X"G!1(`"`"/3/=:``R!\$V`8:&#`_\0C8 +MF[@&&A@P)/`#R*`0``#PN,EP,_).#X_W`-B6N"_PZ+@<].FXSW&@`,@?+?0$ +M(+Z/````4`OR42,`P`?RBB`$``ZA!-@&&A@P#P#*""+"`(C@3Z$+]`/9SW"@`$`M,*!)!.__`!J",P'@/03O_P`:`C#QP`8+ +M#_+/<(``K#@`W^B@SW6``-P'`86&('F/ZZ4(]`/89@HO^0NX@.`.]`;9SW"` +M`!0((*#/<(``&`C@H"D"(```V,]P@``8"`"`@.`<`@(``A6%$`,5AA#/=H`` +M3*U`)I(0J'"$*!\`+W,`(X$/@`!4M!4A@0$;D3J10"81&D`F$!4`)L00!!0$ +M`7IF0X*`X-MC2B-`("#R,'!.]PHAP`_K0<4_W"B'`#^MRSW`` +M`'TGBB-&`THD0`#!!R_SN'.`X@_R4''*(<8/RB"&#P``?B?*(X8/``"/`%@.`.],]PH`"P'V08P`3/<(``6!T(@`"`'!W`%`FESW:` +M`/@>`X8E@RB@J'"$*!\``"&`?X``&+0PX/0@@0$#AC2P`X8F@RF@`X8EDRVP +M`M@(<98/;_(`VHX*0``"C80H'P`O<3`@0B`#C<=Q@``8M##A]"$!`%,B``!V +M"*__`-L"C80H'P`O<3`A0"``(8(/@`!(LB.-`KDT>=X+X`!984H,H`$!V.X) +MP``BC4.-A"D?```A@'^``)BS57AL@%$C`(`,\L]SH`"P'V0;P`3/%5X`"""#X``5+0TXC0B +M00X*N3(B0"XHI<]Q@``4"%$@0(`)\L]P@``HK2`0@`"!X`78`O($V`"ASW"` +M`!@(X*`,A8#@RB`A`&0.H?3*(0$``=CY``_R"B'`#^MRSW```'\GBB,'"$HD +M@``U!B_SN'/@>/'`-@G/]P#8T<#@?O'`8@HO^>'%@.`T\L]Q@``PD12)@>`N +M\C>)@.$)\L]P@`#(FP&`$'$!V,!X%/#/\L]Q@`#(FV&! +MI(JQ@J*EW8?!,)`"`+_0"VL]Q@``4"$"ASW>``!E'((^@IH#A +M`-X)],]QH``L(#"!QW$```!]+:`T$`<`SW```%QX0,`$V$'``=U"Q4/&1,:I +M@J+H+;_<$V,]P@``4"*"@`-@`I@#=`=\9\$PD@(`<],]Q@``91P#?X*E/ +M)8$`(:#/<8``K#@&@>"F`>`&H<]P@``4"*"@Z76`Y1P/`0#I<#T&[_&EP`HA +MP`_K$JH@'9P/$`$`0`"B'` +M#^MRSW```(0G?0(O\XHC3@CQP-8,S_'/=H``W`"Z_S3B+`!Q?P*'2$)`:0%O+*"Z_S3B+`!P&&SW6``+0JAB`&``&F`(6` +MX`7R0'@`V`"EZ03O\0'8SW"``!@(`("`X)GT42$`@,]U@`#X'G[T`HY#CH0H +M'P`O<``@@0^``)BS,.'P(8,``=D"NF9Y-'K'2?]VTHD@`#1`"_SN'/@>/'`+@O/ +M\<]P@``8"`"`@.";](()K_,!W0+8:@HO]ZEQSW"``)`&`(#/=X``^!XF@)X1 +M``:FN)X9&``CATB!-)%3(@``X@EO_ZES*@P@``#>`MC)<N<]PH`#\1"&@X'C!H,]PH`"T#]R@Y@R/_4.' +MSW&``!0("8)1($"!#/+/<(``W`<,@(#@!O08BH/@!]@9\L]P@`#AE0'/\0HAP`_K`0#T'S#(:`X,PG89`'],]Q@`"L.`"!`>``H0;9SW"``!0((*``V`"E +MHO`"CB..A"@?```A@'^``)BS,.#P($``42``@`#?+O((AE8)K_4BAHP@$(!, +M`"D`((6!X;0.0?,#AL]S@`"L.(#@Z*,1\B2&`-H`WP\G3Q`&(,"#+R\!$`.F +M3B>!%P'B]?4DIDBC!=G/<(``%`@@H`38`*4`V&CP((6%X=P`#0`S)D%P@`!L +M2$`G`'(T>`!X`HZ$*!\`,B!`+E$@0(!P#D$#`HXCCH0H'P``(8!_@`"8LS#@ +M\"!``,]R@``4"."X!=D(\H"X`Z8`V`2F(*4\\,]SH`"P'P'8&://+@U@ +M`,"ER7`"\`'8H0>/\<]Q@`"L.`>!`>`'H078C_$*(<`/ZW)/V`>XBB-)"DHD +M@`"Y!._RN'//<*``3"X+@-.XO01@`P;9\<#AQ<]UH``X+D>%SW"``*@X`-E` +MH">E<@E@!"#8!X6*N`>E"\@$((`/____`PL:&#`+R(^X"QH8,`O(D+@+&A@P +M/0>/\>!X\<"J#\__SW"``*@X((#/<*``."XGH$()C_W1P.!^X'CQP)H.C_&^ +M#Z_X`=V`X,]V@`#`5]`*.(XZ$*!\``"&`?X``F+,U>"R`@+DLH,]P@`"L +M.(#9*:``V`NF!MG/<(``%`@@H,]Q@``8"`#8`*''\&*.0XZ$*Q\`+W``((T/ +M@`"8LU5]+(53(02`(O3KAH'G'O1/)`,`4B,#`'M[57N`N<=S@`!4M"RE-.,0 +M8\]Q@`"L.(#=J:$*N`#9"*;/<*``+"`#@"NF`J8$V*(TL.2B2``.$"[_*X!X\@H,]P@`"L +M.*F@`MBI<0H*+_((.!^X'C@?N!X\<#AQ12X)7C/=:``."X&I07P +MD@OO^(H@7`@&A?^X^_-=!(_QSW&@`+PM%7E9@3&!SW.@`,`O,'(*]*`3`881 +M(0"``=C"(`$`!+@&\$`B``0B>,.XX'[@>/'`K@N/\<]QH/[$!@#?SW"?`+C_ +M-J#/=J``P"^E'MB3#]T(O07P)@OO^(H@G`*C%@"6I'B,(!"`]_,4'MB3!O`* +M"^_XBB#B:*(,N)VXG[@E>`:BX'[QP"H+C_$(=G(/[_\H=!XX<4PVP#=SW"@`,@<::`#VL]QH`#,%R$9F(!.H:>@:J#@?\'% +M\<#F"J_Q`-G/<*``#"18@,]U@`!0JZUP02J&!X8@]P^8%8,0*;AV>!SW*@`/`7!J(&@0:B!8$&H@2!!J(`V`JBBA4` +M$6BX$'B*'000`)6&(/^,*?0!V!VB)_!.%8`0@.`C](H5`!%,I62X$'B*'000 +M@^8$V4\=0A`,]"L7`99DN!!XBAT$$`S8+:5/'0(0B@UO^(AP"?`%(T,!01_8 +MD!^%L[@?I0T"C_'@>!#:SW&@`,@<2:$!V\]QH`#P%VJAI!`"`.NZ)O("VEVA +MSW.``(B]1(-&H4.#1J%"@T:A08-&H7`0``$`1J%&@$:A +M!8`&H>!^X<4O@,]SH`#P%\]RH`#\%RBC0!`!`2JR,8`HHT@0`0$JLC.`**-0 +M$`$!*K(\D(8A\P^,(0R`!_0V@"BC7!`!`2JRCNH"GH[N`IZ-R$``!.&`0>`BRSW"@`/0')Z`"V<]PH`#( +M'">@X'_!Q?'`F@\/^/H*3_C1P.!^X'C@?N!X\<"""(_QSW6``%"K%X68X,]V +M@`"8QP;R6!6`$(#@!?(:A5N%`_`L]Q_O__/R1X`:8`W^*F1'DMI@[8K@JO^`ZF@.`(\L]Q@``P(F(/ +MH``!V,]Q@``,'U8/H```V!>%E.`&]`'8`:XQ'@(0!?#AKC$>PA-9`(_QX'BA +MP?'`V@]/\3IQ"'9(=P(*;_H`W8'@RB!"(POTSW"```P=`)"!X`'8P'A`*!`# +MR7"&(/P`C"`"A2/TSW"``%"KF!"``.>XRB`B``KT`K@6>,]Q@``0OP!A+;C` +MN,]Q@`!8!R"!42&`@,]Q@`#(JQ1Y!/(@VJV1"O"8VJN1!O#/<(``C*NSD`[: +M`9=`)0$5$'%&]J)X2"```!!X`_``V%IP`-@J<:ESW@L@!)AP"B$`H`3TX@K` +M`SIP3"$`H`#82/0%((`C#7$`L0UQ`!F$!".'#7`@H"B7#7`@L(PF`I45\HPF +M`Y$=\HPF`Y4D\@HAP`_K`/H=8)(`#I`.H0KPSW"``%"KM!`!``V! +M`>`-H<]QH`#T!P#8!*$!V!IPSW&@`,@?^!$"`$)U`B6`$$@@``!?@1!X4'`\ +M``4`0X?/<(``[)%,(0"@0J"@V`^A`-@?H<]P@`!0JQR08KA"#` +M?0R]SW"``(3*!(#//0B`P#/<*`` +MR!^@$`(`<+M0#O=^$`*&H[I^&)B`\'AP>R8.(`04VOBX!/+)<#CP`]C/ +M<:``]`<%H84E`QD-<*"P#7#`L(HB_P\-<$"@SW(``/__#7!`L`/(SW.``!"_ +MSW*``/@>$(@"N!9X`&,MN,"X\"(``*"`#7"@H`/($(@"N!9X`&,MN,"X\"(` +M`$*0#7!`L,2A2@C``P'8<05O\:'`X'CQP/H,3_$:<,]Q@`!@D@")@.`6\L&! +MHH'/<(``6`<"$1$!X(#/<8``N#P+@32_`>`+H3;P:@RO^(H@"PC/<:``Q"<1 +M$0"&42"`@0#?]/-D$0*&9!G8@P+8$QD8@(#B+RB!`$X@@0<3\L]P@`#`A39X +MP("A@,]P@`!`AO0@40#/<(``8(;P($\`"O#/<8``N#P*@>EUZ78Z=P'@"J$$ +M$`$@#7`@H`@0`2$-<""PSW&```BL`(&`X`?R0H$-<$"@`-@`H<]P@`#X'@.` +M"(#KN,H@@@/*(4(#RB+"`Q@/(@3*(T($4R'`(,]Q@`!8!R"!%+]1(8"`#+CE +M>`GR@K@-<0"A#7#`H`UPH*`?\`UQ`*%*)`!TX'BH(,`"1":!$`^Y4R8`$"5X +M#7$`H2*^2B0`=.!XJ"#``D0E@1`/N5,E`!`E>`UQ`*$BO14$3_'@>/'`N@M/ +M\0AV*'4H<$AQ8@@@`&AR@>#*(($#$`@A`,HA00,%!$_QX'@BN0;P[')@H@3@ +M8;F!X6"`.O?/<*``U`MMH`/9SW"@`$0=-:#@?N!X02F!@`KR+R1)<.!XJ""` +M`000`@3L<4"AX'[@>/'`3@M/\:'!"'5(=L]PH`"L+QF`!""`#W````#7<"`` +M```!V,!X+R8'\`#:RB"!`"WR"\P`'$0P3R#!`P(<1#`!X!!X!""`#P``_[^/ +MN`L:'##/<*``U`LX@$(A`0B`XQPH*``P>QP(*`!V#$#;_&AP/'`X<7:#"_Z`-V!X,H@0@,)],]P@``, +M'0"0@>`!V,!X#+B%(`,!`]K/<:``]`=%H0UR`+(#R`#;79`-<$"P`\A1@`UP +M0*`#R$@0`@$-<$"P9*'A`D_QX'CQP&8*3_'/=8``W'[@%0`0`-Z`X-#W1"X^ +M%P`A0',80=K/W`-B=`F_QX!T`$.!X\<`F"D_Q +M(8`*)@"0$(G#N,HAP0_*(L$'RB"A!LHC@0\``*H`SR`A`SSR@.'*(<$/RB+! +M!\H@X0;*(X$/``"K`,\@(0,N\@*XSW&``!"_%G@`8<]Q@``8!RVXP+@,J2.& +M()&&(?P`C"$"@`STSW&``/@>\"$!`+\1``:!N+\9&``!AJ*`@.4(\@&%@.`$ +M\@"%@.`-]`HAP`_K#0``@.`(\@"% +M@-DHH`&%0'@?\`&&()`4R!!QRB'-#\HBS0<=V,HCC0\``,8`SR`M`\`'S?\2 +M#R_XR7#6"R```87/<(``&`=."*`##(B5`4_QX'C/<(``#$7Y!L__X'CQP!() +M;_$`V@AWSW"``-\'((BCP<]P@`#>!P"(`1S",X0H'P``(8!_@``8M"[@]"!` +M`&#!`QP",`'8`AR",,]VH`#('Q.FSW&``%@=#($`@(3:0L`(@0"`#-D>VT'` +MBW#^#.__&+O/T`;_&CP.!XSW"```R1*(#/<)\`N/\`VC:@"-GL<""@`]G/<*``%`0EH`'( +M['$`H<]PH`#4"TV@X'[@>/'`X<6EP0AR`-O/<*``+""P@$#!!MA!P$+#0\-$ +MPP'8'MF8<[AS`"6''P```'T"">``V'.-`&_QI<#@>/'`X<6DP<]P@`#>!R"( +MSW"``-\'8(B$*1\``"&!?X``&+0PX?0AP0``VL]U@`"L.&#!SW&``&"M,"%` +M#L"X`1P",`F%2:4"'`(P"(5(I0'9`QP",,]PH`"P'SF@SW&``%@=#($`@(/: +M0L`(@0"`'MM!P,]P@`#(.CV`"8`X8$/`BW`0V=8+[_\8NP`4A#`+$@$WSW.` +M`!0(7I6#V.X/8`-@@^4'+_&DP/'`I<'/<(``W@<@B,]P@`#?!V"(A"D?```A +M@7^``!BT,.'T(<$``-K/DH+85@]@`V"#I<#1P.!^",B'N`@:&#`) +MR)NX"1H8,`K("AH8,`O(A[@+&A@P#,@,&A@PX'[@>,]Q@`#,]Q@`#,]Q@`"8/N!_`*'@>,]R@`#P.15ZX'\@HO'`F'`*(<`/ +MZW(*)<`'SW```*(9S0-O\E;;X'C/N!_(*+QP)AP"B'`#^MR"B7` +M!\]P``"C&:4#;_)>V^!XSW*```0Z%7K@?R"B\<"8<`HAP`_K<@HEP`?/<``` +MI!E]`V_R9MO@>/'`I!`!`/FY!/2R"@_X!_`@V<]PH`#('"F@`]G/<*``$!0E +MH-'`X'[AQ0.X-7C/<8``4$<"84HD`'0`V:@@P`(6(D``H8!@@"G8$K@!X75X +MH*#@?\'%X'C/<(``D`8`@*'!)H">$0`&AKB>&1@`X'^AP.!XX'[@>,]Q@``8 +M!^!_!*'@>/'`X<7/D!!Q#O3/<(``V"H`@`0@OH\``#@0!O36"P_X +M@.`$]`'8`_``V-'`X'[@>/'`%@P/\<]U@`!@!\R-#8W"OL*X%G[/?J((K_P- +MV`:X@;@0OL5XSW&@`.PG!J$$A<]QI0#H#P:A!84'H44$#_'QP-(+#_'/=J4` +MZ`\FAJ>&SW"``&`'`-\DH*6@7@BO_`W8!KB!N,]QH`#L)P:AYJ9%))$'(5],]P@`"^!@"008D0<@WTSW"``,`& +M`(@FB1!Q!_3/<(``G"H`@`+P`-C@?N!XX<6?X>'&`-T8\I[A`_:`X4/V`-@4 +M\)_A']Y*]DXA_`?@>*@@@`$/)8T38;X1($"``_*E>`+PIG@`H@'8P<;@?\'% +MX'CQP!(++_$HV"8)S_C/=8``O"I`A0AV!""$#P``\/\`V&!Z02P!`4"%`=A$ +M)@038'I!+($`0(4"V&!Z4R9!$/(([_@`V$"%"'=!*`$"`]A@>L"Y0(5!+T$2 +M!-A@>L"YSW$``"S)SW"``+@J(*#/<`$`Z-H)`R_Q`*7QP+H,#_Q6"`_\SW$` +M`%3)SW"``,`J:@RO^R"@SW$!`!#;SW"``,0J(*#1P.!^X'CQP&X*+_%0V'X( +MS_C/=8``S"I`A0AV`-A@>E,F01!`A0'8R7%@>H8A_0_/<0``>,G/<(``R"H@ +MH,]P`0`XVYT"+_$`I?'`X<6R"._Y!]@F#>_["'6N#\__8@X/_)X-K_FI<($" +M#_'@>/'``@H/\<]P@`!<.`2(SW:``!`Z@.#/=X``^!X&]`.'#9`_EA!Q(?+^ +M#*_S%=C/<(``7#@`W:2H`X>QKE8*(``-D,]P@`"X*B"`8'D$V(#@"O+/<(`` +M5#@`@(#@[`VA^\H@H0"_M@4"#_'@>/'`E@DO\9APSW&``!`Z;8D`W4`A`@I* +M),!PX'BH($`#$2-`@P?TSW#_`/__%2),`P"D`>6O?6N!JH%P=0R!U?80=<_V +M$',"V\H@*0#*)6D0RB-L`,H@+`#*):P0%/`!VP+8`-T0\!!SR_80=0#=RB.I +M`,H@:0`(]@'8`MT#\`+8`=T`V_`BSP#P(D4#\"(```(ES@/-H0(@0`$.H0#8 +M#R#``#P9`@`/($`#/1D"`%$!+_$`',(`X'CQP.'%SW&``!`Z#XG/=8``$#H, +MJ9(,X`$"V%$@`,,=]!(.@``,C88@_P%#N!"X3R#!!L]P@`#X'@.`,B"`#P`` +MV`*?N8#@`=C`>`^X)7C/<:``_$0-H1&-A+C]`"_Q$:WQP'H(#_$(=\]P@`#X +M'@.`SW:``%PX"8`EN%,@$``'CH'@T2#L__4?`2 +MC8#@`-DS]`'8!*ZL'4`0,:T5K1:M"M@7K078&*U0VEFM`-J.NDBE2:5'I0/: +M0!V"$`3:01V"$$(=@A!#'0(01!T"$$4=`A`&V$8=`A!''0(02!T"$$D=`A`( +MV$H=`A`,V$L=`A`RV+0=`!#^"2``L!U"$`2.@.`5\@3*D.`1]$P@`*`/\L]R +M@``0.@V*,V@E>`^J#JJT$@``.@T@``.NV0?/\.!X\`AS#:T$((XO````#$J^N';3K00@CR\````P3+_TK00@CB\` +M``!`3KZQ'8(34R"^@,HAP0_*(($/``"1&LHC@0\``((!RB+!!R'R3"0`@"[R +M!"-``!!QRB'"#\H@@@\``)(:RB."#P``C`'*(L('#?0$(8``$'(/\@HAP`_K +M`ZM@.',)B*1!?(3:25X +M#ZT3:V5X$*T.C0RMB@G@`0#8'0;O\#X=!!3@>/'`X<7/<(``6!T$@`+:`=L@ +M@,]P@`!<.$2H`-C/"$'%,]X&[?@L@`'&JSW&``#P\&8$!X!FA!/`V"P``P07/ +M\/'`1@W/\*'!`-Z."R``8,:B"^__BW#/<8``$#JP$8,`@.-`(0`*"_(@PKF) +M\""/`$&!!2I^`S=WP_:SB0;P`=VSJ;`9@@/)@^I +M$(D%(D(!17B&(/\!#1&$`$.X"R0`@,HBP0?*(($/``":&LHC@0\``&8#?`$A +M\LHAP0\&(#Z!RB+"!\H@@@\``)L:RB."#P``9P-<`2+RRB'"#R$$[_"AP.!X +M\<#AQ<]U@``0.A6-(840<4;W%HTBA1!Q1@`%`"V%SW"``%`Z*6#/<(``7#A" +M#N__(ZC/<(``N"H@@&!Y!-B`X+@/8OO*(*(``-@-I0ZE`*4!I0*E&@D@`*P= +M`!`9\#>-0(41C3!RH;@1K87W8@D```_PSW&``%@=)($@@4>%U;E0<47W@;@1 +MK?+Q$@D``)T#S_#@>/'`X<7/=8``$#H1C5$@`($)\@Z-#*W2#J`!`=@1C:2X +M$:UU`\_PX'CQP/8*S_#/=H``$#H1CE$@`(!-\L]R@`!0JS^"YKD+]`"2AB#\ +M`(P@`H!!]%$A`((]\@"&`>``IA".AB#_`9H2C0!#N+%P,_0`V:P6!1!*),!P +M5A($`:@@P`7/<(``H*LT>&"($25`D$`D#PM`+8``%'@U>-A@!?+@X\(GQ1#S +MH`'A0"5``,*XK!X`$`&&`>`!I@"2AB#\`(P@`H`$]`*&`>`"IIH.;_,5V+4" +MS_#@>/'``]G/<(``7#@DJ`#8SW&``!`Z$:D.B4R)$'(%\@RI[@V@`0'8T<#@ +M?N!X\<#/<(``7#@"V22HSW&``!`Z$8F`N*.X#WBAN!&I#HE,B1!R!?(,J;H- +MH`$!V-'`X'[@>/'``MG/<(``7#@DJ,]Q@``0.A&)12!``A&I$(E,B1!R!?(, +MJ8H-H`$!V-'`X'[@>.!^X'CQP+()S_#/8M2&6&`5@-MC+WF0<1UEK**Q]VNBZJ*I`<_P +MX'CQP.'%SW&``/@>(X$MD0';SW6``%PX4R$"`('B1XW`>P>MSW6``!`Z#B(" +M@,HB8@"`X\PB88`&\K(5@A"!XA[T@>``VK(=@A`5]/(.C_\,C2Z-$'$$]$`E +M@A,$\$`EPA,4C0G9@N`NK4+V+ZT`B@RM!/!.".__*'`Y`<_PX'[@>/'`N@CO +M\`#;H<$$N,]R@`"8QQ1X'6*0WAIB`8(8OLBEBB8$$L]R_O__/\FE!'J`X4#" +MRB7!``OR"($$((`/````,$(@!8#*)6(`3"4`@";RSW"``/"I`!`$`,B!!"2& +M#P`'```$)HX?````,"R^8;Y!+@8&0":`$Q$F@(,/(@(`0,)"]`HAP`_K@/P`8,%>D#"SW"``/"I(("+O\*'`X'@*)0"`\<`3\DPE@(`3\DPEP(`4 +M\@HAP`_K!X +M\<#"#H_P"'7/<(``[#0)@(#@"O3/<(``?#0I@`S@\"!.``/P`=Z*(/\/`*7/ +M<8``^!X`@<00``91($"!,_3>"4_Z`*4"#:_XJ7"`X,HEXA&D])X)S_>`X`7T +M`(6,(/^/#/3/<(``P"H@@&!Y`=B!X`7=RB4B$9+PSW&``'04(9'/ +M#T__C06O\*EPX'CQP"(-K_"X<8#@RB'!#\HBP0?*(($/``!+`\HCX0[*)"$` +MH`+A\'2O?/<(``!(,A@`4IO@`G=0`@4"!,)0"` +M$?),)4"`,O),)8"`3O(*(<`/ZW**((T.D-LM`N_QBB2##PX/C_>`X!'RSW"` +M`$`B+)#/<(``^!X>D!!Q!_(")8$?````#.EP!/#I<$(E`16."@`!SW"``,1" +M`"6!'P``B!-^"@`!3?#&#H_W@.#/<8``&$,1\L]P@`!`(DR0SW"``/@>'I`0 +M<@?R*'`")8$?````#`3P*'!")8$70@H``<]P@``T0]OQA@Z/]X#@SW&``.!" +M$?+/<(``0"),D,]P@`#X'AZ0$'('\BAP`B6!'P````P$\"AP0B4!%0(*``'/ +M<(``_$(`)8$?``"($_()``$*<,FXSW&```2#`Z$&AH&X"02O\`:F\<#AQ<]U +M@`",0EH)(`&I<,]P@`#(-""`SW"``,PT`(`)(0$`SW"``.2H"(`)(0(`SW"@ +M`"P@,("I<)X)(`%98=D#C_#QP,]P@``$@P&`@>`%],]P@`!\-`>`SW&``.!" +M((%"(0&`RB%B`(#A$/3/<8``5*DL@8#A"O3/<8``/*F6"N_V(X'Z#>__`MG1 +MP.!^\<`6"X_PSW"``%@=!("@@,]P@`!40@"`0B``@,H@8@"`X#:]!O3/<(`` +M<$*F"``!SW"``#RI(8C/<(``J$*`X<]V@``$@PST((!"(0&`RB%B`(#A!/(@ +MAH#A+O1V"``!SW"``,1";@@``0&&&@KO]@AQ(88/>#!PS/<*(<`/ZW**(`T# +MU]M*)```)0#O\;ASSW&``.!"((%"(0&`RB%B`(#A!_(=92.&R;TP=03R0@WO +M_P#9S0*/\(#BX<7AQE;R0"+#`R2[P[H"\`#:C^*6``T`,R:"<(``D$A`)PUS +M5'T@?<"(`1F2`P'@`1""!`$9D@`!$(($`1F2``$0@@0!&9(``1""!`$9D@`! +M$(($`1F2``$0@@0!&9(``1""!`$9D@`!$(($`1F2``$0@@0!&9(``1""!`$9 +MD@`!$(($`1F2``$0@@0!&9(``1""!`$9D@`!$(($`1F2``$0@@0!&9(`0B-# +M@+/UP<;@?\'%X'B`XEA@66$+\B\DB7#@>*@@P`'_$(*"_QF*@.!^@.+AQ>'& +M)/)C:B*[P;H#\`#:@^(9]S,F@G"``(Q(0"<-!X\<`V":_P4R%"`$XB +M#0'/4.\H+E +M"/*#Y0[TSW"@`#@$:*C/<*``.`1HJ,]PH``X!&BH'0&/\/'`B@B/\+IP>G'Z +M(?PM@\/_X7PSW:``$!%`88`V:H-+_(XV@"& +M'-D@H`&&&-D@L,]Q@`#X'A4A5@,`%@$@4X$-P?"HSW>``&P(*!A`!$5YI+DA +MH`#9,QA"`.EQ(J`*(4"#,1C"!#(8P@0T&,0%RB%B`.(.K_8,X(#E!O3/<8`` +M=)$%\,]Q@`"4D2.FSW```$@1`+%,)4"@&-@"I@3RBB`%`@"Q#,"`X`3TSW`! +M`+#8`:<`%@`@N1``!E$@`(`7\D&&&M@`L@*F`)&'N`"Q`-@+L0&"3"0`H*VX +M`:('\L]P@`"`-@2`,QH"`$PB`*`4\B&&`8&8N`&A`X&?N`.ASW&``$@'`!8` +M(``9!`1`@`&`0:$"H0H-+__)<$T'3_#@>/'`_@Y/\+IP>G'Z<@HB`"$*(4`A +MR'4*),`A"B!`@\]R@``>O\H@8@`(<0*X%G@(8DPC`*`$N(8@_@,%(%``RB', +M#\HBS`?*((P/``"_(&9".N(^X`;%,(@"@#,`!IQ/R(88!@9BX`:$#@9^X`Z'/<8``2`<` +M%@`@`!E$!$"``8!!H0*AH@LO_\EPX05/\/'`X<7/=8``V#T`C8P@PX\/],]R +M@``X0@:"`X`@@,=Q#P``H,8+X`!(/'`SW"``/@>`X`8B(7@#_3/<`$`H(;* +M#T`"*@N/]0AQSW"``(A#C@K``-'`X'[@>/'`1@Q/\`AW&G$Z/'` +MSW*``,0&`H(EB(#A`=@&\@C9S@[O]RNB"/#/<8``<"52#2_Q`*'1P.!^\<`" +M"V_PV'`^""```-W):(#FEO;X<*EW,B:``[#@BO:YX`CV@@_/\S)O.'@%?0'G +M0B='`$PG`(!AOC#W+0-O\*EPX'@(<@/P`>`@B(#A_O7@?T)X\<"R"D_PSW6@ +M`/Q$'84YA68-H`(`W@#8GK@!I>!XP:7%I?T"3_#@>,]QJJJ[N\]PGP"X_S:@ +M-J`VH#:@SW&@`,@[#H&(N`ZA:2!``/[QX'CQP,]P@`#X'@.`&(B$X`[T"B'` +M#^MRBB`,#HHCA0I*)```V0=O\;AS6@WO\@/8SW"``.2H`!`$`$PD`(`+]`HA +MP`_K2"2__`=C1P.!^X'CQP,H)3_#/=8`` +M5*DOA4H@`""`XD5[;AG8`"*%`*$*\&X1``9$>&X9&``< +MV!BX%1D8@*4!3_"`X`'9P'G/<(``U#W@?R"@\<`B"4_PSW"``+@J(("BP6!Y +M!-B`X*H"`0#/<8``4!T`@0'@@>``H0GT`=G/<*``R!PQH#X+H`(H<'H/[_@% +MV,]V@`"X-PZFSW&``%`=`($!X('@`*$*]`'9SW"@`,@<,:`2"Z`"*'`#V,(- +M+_')<038N@TO\2)N!=BR#2_Q)&X+V*H-+_$F;@_8H@TO\4`F`1(VV)8-+_%` +M)H$2-]B.#2_Q0"8!$SC8@@TO\4`F@1//=Z<`%$@(A\]QIP"81P2F#8?/F%X<(IA:'":88@@NF&8(,IAJ"#:;/<`4`Q@,& +MI<;8D+@&I<]P+``"`0:ESW!:`$(!!J6*((L`!J7/<$``APT&I<]PT0#"#0:E +MSW#```<.!J7/<(``4!T`@,]R@`!0'4(@0(``H@;TSW*@`,@<`-@1H@'8"*<` +MV`VG#J?/<%``_P`#/<8``4!T`H0CTSW&@`,@<`=@1H78)@`(! +MEA"XA2"$``:E`I80N(4@A0`&I0.6$+B%((L`!J4$EA"XA2"/``:E!980N`4@ +M@`\``((-!J4&EA"X!2"`#P``P@T&I0>6$+@%((`/```"#@:ESW"``%`=`(#/ +M<8``4!U"($"``*$'],]QH`#('`#8$:$$ABN&"*<%A@VG!H8.IPB&%Z<)AA:G +MSW"K`*#_.*`LACF@+88ZH!H*[_@.AL]P@`!0'<]Q@`!0'0"`0B!`@`"A!_3/ +M<:``R!P`V!&AS08O\*+`\VT#`BW!*"N_^&+NAP-'`X'[@>/'`$@GO\A;8 +M`-C1P.!^X'CQP-H-+_`'V&8,S_C/=J``M`_\AAIP`-@_R%MC1P.!^SW&` +M`/@>`('$$``&42!`@0CT`8'$$``&42!`@0GR#@XO\Q/8"@XO\Q'8Z?'I\?'` +MSW"``)P8`(#/<8``E`<;>$X.[_,@@8#@"?(!V<]P@``@'6H/[_\@J-'`X'[@ +M>/'`J@P/\`AW?=@-N,]Q@`!XQ\6!,@XO\,EQC"`"@,]Q@`"@&`#=A_<=>(P@ +M`H`!Y7SW`"A"`P4JO@//V,]RIP`42!V"OH)L$A``]Q26"'P#_``#3)>$5J@VO]8HA$``(=JEPG@VO]8HA +M$``(=4`H`"*.#:_UBB$(``AW0"H`(H(-K_6*(0@`T7D9X2QY+W&Q>AGB3'HO +MXQ2""#P#_``#3(.$% +MA0,O\`"EX'CQP`H+#_#/,<&@48@0`$&H<]P@`#(!R"`@N$*](H@1@^B#V_Y!-K^"*_Y!-C1P.!^\<#/ +M<8``[#0)@0'@":'/<8``>,<&@8*X!J'/<(``7#@#B(#@K`UA_LH@H0#Z#:_R +M!MC1P.!^X'C_V<]P@``@C""H;R!#`-D`;_$!V;4"+_,1V.!X\<#/<(``^!X# +M@!@0A`!,)`"!#?0*(<`/ZW**($P/BB-'"@4'+_%*)0``SW"@`"P@,(#/<(`` +M\#Z&#V``EB%!#\]P@`!4J0R`@.`2\L]P@``,'0"0@>`!V,!X#+C7<````!`& +M]#H*(```V"'PA@S/^\]P@`"8%0"`@.`7],]Q@`!XQP:!1B!``0:ASW"``,@' +M((""X0GTBB"(!*8.;_D$VOX/;_D$V.8/C_G1P.!^X'CQP,]P@``80P"`0B`` +M@,H@8@"`X`?TSW&``.PT"8$!X`FASW&``'C'!H&"N`:A]@RO\@;8SW"``%2I +M#("`X!GRSW"``&0Q`H"`X!/RSW"```P=`)"!X`'8P'@,N-=P````$`?T"@H@ +M``#8T<#@?L]P@`!<.`.(@.`%]%8,;_X"V/7Q]?'@?N!X\0"?(! +MV'X.K_$&I?8,K_8"V'()C_:"X`WR"B'`#^MRBB#?!8HC!P"*),,/C04O\;AS +M+@F/]L]P@`!XQP60@.#4]@J%"-E!P`N%E-H>VT#`BW`:#*_^&+N`Y@;T)@Z/ +M\0'8!Z4`V`NE)0`O\*+`X'CQP.'%"'7/<(``>,<%D(#@Q/;V"X_V!/"N"X_V +M#@@@`*EP!0`/\.!X\<"*#\_O6@@O]P#>@.#/=8``J!T']`'8`:4>#^__R7`Y +M\`O(!""`#_[__P,+&A@P"\B'N`L:&#`+R)"X"QH8,(X+#_`B#`_WA@NO\@O8 +MSW"``%@=$(`DA0"`QW$````4(GC7<`"```!"]\.E?@BO]L*E@.#T"Z'VRB!A +M`,]P@`!XQP60@.#*((D/``!``%`*"?EE!\_OX'CQP,]P@`#X'@.`&(B$X`[T +M"B'`#^MRBB#,#XHCR@5*)```:00O\;ASSW"@`"P@,(#/<(``\#[N#&``EB%! +M#\]P@`#(!R"`@N$)](H@"@A6#&_Y!-JN#6_Y!-@!V<]P@`!D,2*@SW&``'C' +M!H%&($`!Y@GO^P:A>@V/^='`X'[@>/'`SW"``/@>`X`8B(3@#O0*(<`/ZW** +M(`T/BB.*#THD``#I`R_QN'//<8``>,<$V`:ASW"``&0Q`=DBH,]P@`!<.`.( +M@.`<"F'^RB"A`&H*K_(&V&(*K_((V-'`X'[@>,]P@`#P/LD#0`#@>/'`2@JO +M\A/8SW"``%@=!(`@@,]P@`!($R"@T<#@?N!X"'(`V`D#;_80V>!X"'(!V/T" +M;_8@V>!X"'("V/$";_9`V>!X"'$%`V_V`-@(`X`8$(0`3"0`@0WT"B'`#^MRBB",#XHC"0,=`R_Q2B4``,]P +MH``L(#"`SW"``/`^G@M@`)8A00_/<8``>,<&@48@0`$&H<]P@`#(!R"`@N$) +M](H@B0?Z"F_Y!-I2#&_Y!-@Z#(_YT<#@?N!X\<#/<8``>,<&@8*X!J'/<(`` +M7#@#B(#@"`EA_LH@H0!6":_R!MC1P.!^X'CQP$H)K_(4V`H)#_'1P.!^\<`Z +M":_R%-A"""_Y!-C1P.!^X'@E`:_R&-C@>/'`_]G/<(``V#T:#V__(*CN#X__ +MT<#@?O'`X<7/=8``;"4`%040C"7#CR#T@.`1],]P@``HD&@0!``*(<`/ZW+/ +M<```APPM`B_QBB/'`PAQ@B$(`,]P@`!(C`X@0``V#N_OBB$(";APSW"``$24 +M$Q`"AHPBPX__V07R'!A8@2"E"_`3&%B!(*4`V<]P@`#$!B2@*@]/][D$S^_@ +M>`'9SW"``,0&)*`5!T_WX'CQP"X,[^\"V,]U@`#8*LX.;_$!I8+@#?+"#D_Q +M@.`)\KH.3_$5)0$0%($!X!2ASW"``$`B+)#/=H``^!X>EA!Q&/(+R`0@@`__ +M___#"QH8,`O(A[@+&A@P&@@/\`"&Q!``!E$@0($&\@"%Z[@#V`/R!=C."(_Q +M`-@*#^_XC+@=!,_OX'CQP.'%SW"``$`B"(B'X"WTSW"``/@>`8`>V<`0`P:` +MXR\HP0`"(0(`$O*.X@GR$FH6>,]U@``?OPAE@.`*\A`C@X`O*,$``B$"`/'U +M`=@#\`#8@.`9\J8.S_:`X%0)(@#*("(`$?#R"L_VSW*``-@J((*AN8#@KKD@ +MH@?RV@K/]H#@B`P!]ID#S^_@>/'`X<7/<(``U"M(B"J(1"H^"P`A@'^``&0K +M-7@&B,]U@`#8*H'@%/3/<8``^!X`@<@0``:&('^."O0!@<@0``:&('^.K`[A +M_\H@(0``A:JX00/O[P"EX'CQP+H*S^\(=<]W@`#4+'#<`B-&X`(9$/@`#8*EP1`29"A40N/A`.\IH,3_&`X`KRD@Q/\<]Q@`#8*A5Y"X$!X`NASW"``-@J((#/ +M<(``0"),D,]P@`#X'O"Y'I`0\A!R"_0$(;Z/```X$`7T.@S/]H#@NO(Z#*_P +M`=C/<(``0"(LD,]P@`#X'AZ0$'$+],]P@`#8*@"`!""^CP``.!`#\@'?`O`` +MW\]P@`#X0Q(-;_/I<<]P@`!`(@B(B.`1]`#=`MY$+3X7`"&`?X``G"SN#&_S +MZ7%AOH#F`>4T]^8(S_8:<,]P@`!`(BR0SW"``/@>'I`0<0[TSW"``-@J`(`$ +M(+Z/```X$`3T3"``H#_T"\@$((`/____*PL:&#`+R(>X"QH8,#8-S^_/<(`` +M0"((B(?@*_3/<(``^!X!@,`0#P:`YR\HP0-.((X''_*.YA;RLFZV?<=U@``0 +MOP65@.#.]@:542!`@`CRJ@WO\,]XU@A``@78$JT`V`6U$">/DR\HP0-.((X' +MX_7/<(``0"((B(C@)_3/<(``0"(LD,]P@`#X'AZ0$'$*],]P@`#8*@"`!""^ +MCP``.!`5\L]P@`#X'@&`Q!``!E$@0(',("*@"?+/<(``V"H`@%$@`(`'V`+R +M!MA"#4_QB0#/[_'`SW&``-@J_!$"`(/B1``-`#,F@G"``&!(0">``?H0;8"/"`$0```>"`&0```=CN#$_Q +MT<#@?O'`S@^/[PAUSW"``$P3#X"`X!#RSW*?`+C_':+/<8``.!T$@0'@L[BU +MN+BX!*$6HL]P@`!`(@B(A^#/=H``V"K4](#E2O+X%@`0@.#BA13T]A:`$(#@ +M$/8*(<`/ZW+/<```OQN*(P@&2B0```$%[_`*)0`!8;@.">_P^!X`$)H([_#I +M<,]P@`#`!P"`^!8!$"*H^!8`$(#@(/26"<_V@.`*\M(.``+/<(``P`<@@`2) +M@+@$J<]P@`#4*TB(*HA$*CX+`"&`?X``9"LU>`:(@>!(""'RRB#!`P"&\+B0 +M":+PRB!B`)X)3_&!X`WRE@E/\14F`1`"@0'@`J$`AH>X`*;@\(#E!/(`AJ>X +M`*8+R)"X"QH8,`8+[^]*(``@SW.@`)`C'(-1(,"`#_1P$P0`"B'`#^MRSW`` +M`.4;BB-)"3$$[_`*)0`$;@F/^6X-3_[/<:``T!L3@9"X'*'/<(``.!T`@.^X +M!O+/<)\`N/]T&``$SW>``,`'((<"B8#@HO0$B5$@`(`M\L]PH`!(+@N`SW6@ +M`#@NT[@GA01Y11Y8$">%!GDGI;((8`(0V`"&SW$``,0=D+@`I@>%B+@'I88* +MK_<-V`O(!""`#_[__P,+&A@P"\B.N)"X"QH8,$(*S^\@AP2)H+@$J6[P^!8` +M$(#@XH4-]/86@!"`X`GV"B'`#^MR;]@&N(HCB@@P\6&X=@^O\/@>`!#/<(`` +M^!X!@,00``91($"!"/+X%@`0@.#$#>'TRB#!`\]U@`#4+&@5@)"`X"+R:A6` +MD%$@P($>\C8-C_9J%8&0&G``AL:Y"KF!N`"FSW"``,!#H@L@`/EA3"``H`KT +M$@V/]H#@!O(`AHZXS@[O]0"F_@R/]H#@!_)5%@`6`>!5'A@0^!8`$(#@$O0( +M%8"0"A6!D$0H/@L`(8!_@`!D*S5X!HB!X&`.X?'*(,$#<06/[_'`#@V/[PAW +MSW"``$P3#X"`X`#>#_+/"SN+6XN+@$H1:B(H?/ +M<(``V"K/=8``U"M`&%@`"(W/<8``9RM$*#X+,B%$#DJ-0B1!`%!QR/9`(D4` +M+R5'`0H=0A$%\,JM2B4``$0H/@L`(8!_@`!0*Q4@0@%,DH#B#O2P<EP!/2"#0`"!/!: +M"P`"SW"``#@=`(#ON`;RSW"?`+C_W:!U!(_O\<#AQ0#==@XO]ZEPU@EO\:EP +M2@A/\B(*3_'/<(``@!5A!*_OH*#@>/'`_@Y/]HH/3_9N#D_VT<#@?N!XX<7/ +M`ZA$(()`J_T#:'QP'X+K^]*)$``P(&@ +M@`'?T77")`(!T76A@6&`PB?.$P'>L7/`?K%S`=O"(\X`3"0`@,PF(I#*(V(` +M"_2`XP;T@.;,)R*0!/("VP/P`-N`XQ3R@>,.\H+C&O2@@,"!`8`A@0(EC9.@ +MH@,@0``!HA#P`-@`H@&B#/"@@<"`(8$!@`(EC9.@H@,A`0`AHED#K^]H<.!X +M\<#AQ2:`0(!"(@*`RB)B`(#BRB'"#\HBP@?*(((/```V$$"`$'((\F2""R-`@`7T0((0`!`!V!.F*(4,A4"!`(`` +M(L*#0*$LA0$@P```H0+8$Z8IA4V%`(%`@@`@P(,`H0V%`2+"`$"@!-@3IBJ% +M#H5`@0"``"+"@T"A+H4!(,```*$(V!.F*X4/A4"!`(``(L*#0*$OA0$@P``` +MH02%`(`B""_RZ7$DA0"A!84`@!8(+_+I<26%`*$&A0"`!@@O\NEQ)H4`H0>% +M`(#Z#^_QZ7$GA0"A#]B:N`ZF#]@,N!"FSW"``$!$B@X/_\]U@`!@1'X.+_^I +M<'H.+_]`)0`8<@XO_U8E`!)J#B__5B4`$ST`C^_@>/'`S@]/[PAW(/``AB&& +M(:``H0#8`*;/<*W>`@`!IJ:&!H40=@;TJ7#Z#>__`MD&I::&!X40=@?TJ7#J +M#>__"-D'I2.&8'G)<*X-[__I<`HF`)`)\@.'((`"AB)X@."N!\S_^@TO_^EP +MS0=/[P_8FKC/<:``L!\5H0_8#+@7H>!^\(X%($H,`P-TT@61YAB'_#B*Y.GT$\!3=`MB*$@$!`GD2@@3A +M.@RO]`#:9@E@``(@3@,#V,]QH`#('Q.ASW6``%@="(4`@$+`#(4`@$/`"84` +M@$3`#84`@$7`!(7@@`6%`!`2`$`1``8>9OP1``#/<(``,)%`@"&``-@`(H*# +M`2!``$#"3"%`H$'`BW8.]$H)#_&$P1IPR7`>"^__AL((=@@0`2$,\(+!R7`* +M"^__AL((=L]P@`!XQR20SW*``'C'98(&P@2[4'-`*8`"B/=0<$OW`GI0<+[W +M!O"."V``AL`(_Q`]D(=DIPJ@WO\0/9"'<`P`'!0"#`@$$A`0!`P`3`0<$%P4`@P(!!(0$` +M1,`""&``1<%,(0"@"O0$A<"@"(4`P2"@#(4!P2"@3"&`H!'T!(7`H`B%`,$@ +MH`R%`<$@H`6%X*`)A03!(*`-A07!(*!,(4"@"O0%A>"@"84`P2"@#84!P2"@ +M3"``H`'9P'G/<(``J!TTJ(D%;^^JP.!X\<`R#4_OI<$(=@*+*'68<&3``(L` +M$@8!$1P",'EP`A('`002"`$0%``QY)(&$@4!`"#)`P"1+R%($@<@0`(*"2`` +M$'@`((H!`94O(H@2!R"``O8((``0>``@Q@$"E2\FB`$'((`!X@@@`!!X`"`' +M`@.5+R?(`0<@P`'.""``$'@`)04`!)4O)4@!!R!``;H((``0>!]G!97P?^=X +MJ@@@`!!X)I4A@^Y)7I0>@`B@0(P>0`<1#!'E2=Z7'D/ND5Y,'D` +M(8(!4'I<>0(0008< +MA#`/ND5Y,'D_9_!__'D(',0S#[_E>3!Y.&!I<<:YA;D(N04AP0(@MA!X()4* +M'`0P)W@<>`BX!2```0&V`,`!I@'``J8"P`.F701O[Z7`#WM(N`]XSW*```!8 +M]"(``$`H`0)(N`5Y]"+``#!YX'\G>.!X\<#/`AS*'+/<(``6!T$@`"``B"`#P`"``!)`"``:''A +MQ5,@0@4$((T/P/\``,]P@`!XQP6``B"#``0A@@_`_P``U;DB>*5[17@0<\H@ +MK0`%]Q!S`-C*(&8`X'_!Q>!X\<#AQ=APN'&Z#^__F'((=#:X-KDP<-8@A0\``(``X'\B>.!X\/'``-G/<(``&"H:""``(*#/<(``U#YN#X__T<#@?N!X`-G/B(J+/<(``/$0@H,]P@`#@1""@,;(PLL]P@`#8-N!_(*#@>/'` +MSW&``!@J`(&`X!/T`=@`H0#9SW"``$@3M@_O_R"@SW"``!0?$(B#X#@)(0#* +M(&$!T<#@?N!X\<`V"4_O\@IO]J3!@."("0(`SW"``$@3`(#/<8``V#:B#N__ +M(('/=H``F`$`W0/TH*,@@\]W +M@`#@1$#!((=#PD+`BW!!P1#9HMH>V^X,[_T8NP#8F@WO]XMQSW"``$@3((#/ +M<(``V#:BIK&V(*"PML]P@`!P!J"@F@OO\1/8`(>%X([W7@@@``'8R@K/^<]Q +M@``\/!V!`>`=H07P1@@@``78M0!O[Z3`%=@`VL]QH`#('V\9&`#@V)"X$*$) +MV+`9``"T&0``=-A"&1@``-B:N`^AI!F``,]P``P`&0ZAX'[/%@.`4],]P@``8*@"`@>`.]&H,#_>:X`KRSW"``%@=!(``@`6E +M`=@'I1T`3^_QP.'%SW6``)@'!X6`X!KRSW"``!@J`("!X!3T,@P/]YK@$/+/ +M<(``6!T$@`"`!J7^#.__)84PE3A@$+4`V`>EV0`+],]P +M@`!8'02`((#/<(``F`,]P@`!@1">`@.$'\@.`0(`"@4)X!/#/)($H +M@00AOH\`!@``H<$%]%$A`(`(]`CP!""^CP```!@$\@#8`_`!V,]QI@"D`!>A +MX'^AP.!X\<`R#0_OSW6@`+1'"'8&\+H,;_:*((D,<14`E@0@@`]P````02@^ +MA?3UBB#_#V\=&)!K'1B0`]@/N,]QH`#('Q,9&(`%AED=&)`&AEH=&)`'AEL= +M&)`)AE@=&)`(AE<=&)!`$0$&SW&``'1%(($$((`/````@%$A`(`'](#@!MC* +M(.$!`O``V,]Q@`#X'B.!*('//'`,@P/[SIPSW>``!`Z#(^&(/\! +M0BC0`,]VH`"T1PIU!?"V"V_VBB")#'$6`)8$((`/<````$$H/H7U]4,6`)9& +M(``-0QX8D%<6`):\N+^X5QX8D%\6`):_N%\>&)``V)ZX4QX8D.!X`-A3'AB0 +M#(]@'AB0;@M/^\]P@`"X*B"`8'D$V(#@%O),(4"@"`ZA^%\>&)`&\,(*;_:*((D,<18`E@0@ +M@`]P````02@^A?3UBB#_#V\>&)!K'AB0$_#/<(``^!X#@!"],B"`#P``V`*? +MO8#@`=C`>`^XI7A?'AB0!LB$X"`/H?'*(*$$*0,/[^!X\<#."@_O"'4H=EX, +M+^\!@*"%$+E!+0`4.&!.#"_OR7$0N;!X.&!"#"_O0"Z!$@T#+^\H$D= +M&)!Y`@_OX'B%X/'`F'"+]PHAP`_K""042$`@@WR$!,$```0!0$*(<`/ZW**(-H*70">F*`Y<]S@`!XJM1['O0`)HT?@`!PJOB- +M@N<(]."3^W\CD8"_)'_@LP;P@><$]"*1(+,`V3BMSW6@`,@<^H4@D^1Y++,% +M\"R3,'7#]UEA`_"LL[EBB2'/#P084``!Y@#9SW"``'C'%0$O[R>@X'CQP``6 +M!$`'&A@Q`!8%0`$:6#$$$H$PG.'*(L('RB""#P``W`[*(X(/``#T"A0&(O#* +M(<(/>@^@``[9T<#@?N!X\@!N<`_`;A`/@&X0#%"5# +M$,"S`8+NN!_TR+/0&X0#$(K/<8``$+\"N!9X&V%EDX#C.]F&[9;`0BG)H +M=GMZ8462@.)Y80?T)I%1(4"`W`P"\`W(`""!#X``'*K$J11](I'`'803%7]X'400`Q(!-L"G`8$$((`/````8-=P````(!/T$(G/<8`` +M$+\"N!9X`&'MN,HF8A#/<(``,![4>""0$.$@L`/9SW"@`!0$,*#B"J`!"G`^ +M\'`2#0'@$P$!`B%.`[%V!_?"?:)X$'B`&QP`SW"@`-0'#Q`.A@#=\!N$`W`2 +M`@'`&T0#0GDP>>`;1`#0$P$!`>$P>?`3!0'0&T0`4R5^@,HAP@_*(L('RB#B +M#"SN+6XN+@$ +MH1:B#,S/=J``U`=1(`"``]@@'AB04O(4'AB0`Q(!-@`6!$`'&A@Q`!8%0`$: +M6#$$RIS@RB+"!\H@@@\``-P.RB."#P``]`H(!"+PRB'"#RAP:@V@``[9`Q(! +M-E")4R+``(8B_@,0J42Z`KC$&8(`%GC/-`9!``%\-`1``&\ +M&@0``=B@&@``W@FO^O"*@.#6`R$``Q(--@;(42"`@%0`1I[B>'000!O"%V)"XH!T`$,]P@`#X'@.`&(B$X-X"`@#/ +M<8``J!T,@0\@``$,H<]Q@`#D'0"!`>##`B```*%BD#,5@!`1(P"`)O('R`0@ +M@`\`P```UW``P```%?0(C8#@%/:D%0`0M+BD'0`0DA4`$:>XDAT$$)X5`!&G +MN)X=!!`*\%$A@($(\HW8D+AO`B``H!T`$`;(42``@!@"`0`Z#H__`Q(--@AS +MJ!T`$,]P@`!8'02`L!4'$2"`52=`!M6Y$''/,=#]P78!Z(%@B)XC"`) +MALHA)0"D%0`0"2'!`/*XK!U`$.?RF!6!$,.Y!\@\>00@B`\!``#P#1($-L]P +M@`"LJ18@``%%D*P5`!!!*`@3"2""`(`5#Q%^%0`1^&#/=X``^![DAT87#Q$? +M9P@BP@/B>I@5#Q#HOP#8A_)$)P`6!">!'P8````CN#&Y`>`X8,]Q@```6C(A +M!@`$)X4?P````$$MA04R(4`!02^!$E(A`0#`N0.YP'`8X87@RB&-#P$`B0W5 +M(0X`I!4`$/2X)/(B>H05`1$"(D``2"```$*X02]"$\"Z!+I4>NEQQKE)(<$% +M-'KKO\]Q@`!\4%%A!O)!*0(!%")!``4I/@!!*0!R`-E9\$$JA0!!+T(3P+H$ +MNE1ZZ7#&N$D@P`44>NN_SW"``'Q04&`&\D$H`@$4(@``!2A^`4$I`'*$%0(1 +MZ[]984$O0A/`NA#A!+I!*84`5'KI<<:Y22'!!31ZSW&``'Q046$&\D$I`@$4 +M(D$`!2E^`4$I`7(?\%$G0)+*(0(`&_0#XL]P@`!44/`@00`BN@4IO@`O<%,@ +M`@!88(05`A$=>"?B(KH%*;X`+W%3(0(`66$]><]RH`#$+"^B+J)`*`$6GKE` +M+`\%Y7DE>,`=`!`*HL]Q@`#\.`'8`*$%\&^"L!4'$?!S1?<%V!BXH!T`$,]P +M@`!`!T&`()4)(8$``(B!X`;T&18`EA!Q`-@#]P'8@.`+]`/8&+B@'0`0SW&` +M`,`[$X$!X!.AH!4`$`0@OH\!`0``%?22%0`1E!4!$)`5`A&R%0,1W@U@`4HD +M0``#$@TVH!4!$"5XH!T`$`0@OH\!`0``!O)R#@_U>0-```/,SW&?`+C_&*$& +MR%$@`(#*(D$#RB`A('SRI!4`$/*X-/+/<8``_#@`@8#@`-@T\@#8`*&`%0`1 +M?A4.$1YFSW"``/@>!(!&$``!'F8%\*X(+_:*(,<)42&`Q?OSSW"@`,0L"X!3 +M(($$_KC,(2*`"O*8%0`0U@WO\P#:=+C88`/P`-@#$@(V"?`-R,]Q@`"LJ19Y +M!9&I,=P@``0OV60@./1]@:042!`@`WR@>,( +M]&`2``&$N&`:!``%\!R2C;@%0:!`"`$0$' +M@.$%]#R2BKD\LJ02#0#HO0GR:!(#`5,@P0!Y83!Y:!I$`%$E0)()\FH2@0## +MN#A@#WAJ&@(`!\C/<8``$*L$((`/`,```-=P`,````3T#AD$!`7P`-B+N`>Q +M`9*`X!3R#=`1``%3(,"`"O+P$0$!SW"@`)@#/J"V&D0`I!(` +M``0@OH\````P!_2&(.6/7`QB`,H@@@"*"@`!@.`$\M(+#_VI\`/(I!````0@ +MOH\````PL_+TN"P(P?,#$@$VI!$``.RX3?)R"J_Q`=@#$@$V';'/<(``^![$ +M@,((K_<`W8'@#/3/<(``#!T`D('@`-W/)2$3RB4"%`/8SW&@`/0'!:&%)0(= +M#7"@L`/(79`-<$"P`\A/@."Z!O)"A@UP0*!&E@?P#7!`H`/(0!`"`0UP0+`# +MR%&`#7!`H`/(2!`"`0UP0+`0&0`$`\B4$```42!`@J`/@?:.#T_X=0!```&! +M^+@/\L]P@`!(!P"0';'/<(``3`=`@`&`4:$2H0?PN@FO\0+8`Q(!-AVQ-@M/ +M_0/(=@RO_W@0``&`X#0`0@`#$@,V`8.8$P$`^+B4&T``%?+/=8``",ZI<,8/ +M;_AH<1#8#!H<,`W,H[@-&APP1@VO_ZEP_0<``)X3``&^$P(!DAL$`)`;A``R +M#F`!@A,#`?BX#/(#V<]PH``4!".@BB`0`,T'(``&&A@P`\BD$`$`AB'EC]@* +M0@`#$@XVI!8`$/2XD`(!`!".SW*``/"]%GK/[`6#1%DY;%S_@`N``DC00`"N!9XSW.``!"_`&,$((T/@`,``#>]9;V`Y`!WTHA`"`/(1$@`Q*2`/8++_68%@`0"2!!!)@6`!#M +MN,H@PB-`*`,A='L(`(H0?PSW```/`1/@S/]5$A@,7Z\\]P +MH`#$+`N`!""-#_`'``#^N#2]4R"!!`/R@>4-]P,2#C9*(``@SW&``#P\`8$! +MX`&A`-DP\`"6$.`0XU2%"`\]W@`#XQR"GHJ>8%@`0+@GO\P#: +M`:?/<8``/#P"@0'@`J$`@;A@`*'/<(``^!X#@`F`42!`@`?R#BDL""0,'/(FXCK@9H\]PH`"H(`B`-@]/\*4%``"D%@$0 +MI[B2'@00M+FD'D`0E!8`$)`6`Q'/<:4`K/]`P+`6`A%XH<]S@`#X'F2#5A,# +M`13C8GH#XB*Z6V)Z8D@B0@`%ND4B0@-6H2#"!""`#P```"`EN`4B`@1%>(FX +MCK@9H<]PH`"H(`B``]G/<*``]`P+@?,#R`&`^;@(]#8.;_$$V`,2`38=L8X,;_<` +MWH'@"_3/<(``#!T`D('@`-[/)B$3RB8"%,]UH`#T!QF%@.#*(<(/RB+"!\H@ +MX@S/("(#RB."#P``;@K*)`($P`>B[\HE`@0#R!R0Q7@-<0"Q`\@]D`UP(+`# +MR"^`#7`@H`/(0!`!`0UP(+`#R#&`#7`@H`/(2!`!`0UP(+`#$@$V')&&(/\, +MA.`?\C.!#7`@H`/(4!`!`0UP(+`#R%00`0$-<""P`Q(!-AR1AB#S#XP@#(`) +M]#:!#7`@H`/(7!`!`0UP(+`#$@$V')&&(/T,C"`"@AOT8!$!`0UP(+`#$@$V +MI!$``/>X$?(Y@0UP(*`#$@$VI!$``&09``2X&0($NAD$!+>XI!D``*01```$ +M(+Z/``!`"`?R`8'PN%P,0O$.\#J!#7`@H`,2`3:D$0``AB#SCP3R.X$-<""@ +M`=DKI0/:2*7/`#]*"CSW"@`/Q$/8`9@.NX +M-?0$(;Z/``8``"_TX'C@>.!X42!`PRGR`\C/<:``R!^P$``!EB!!#QZA$-@. +MH0'8%1D8@+H*X`!!V%$@0,,5\L]P@``(10'9(*`#R*00`0":N:080`!"#&__ +M`=C/<8``-#T-@0'@#:'&#```!""^CP8`R@"8<"7RSW"``"@=`X"`X,HAP@_* +M(L('RB#B"L\@(@/*(X(/```_!-`%HN_*)0(!SW&``#0]$($!X!"ASW&@_N@` +MSW"?`+C_-J!M`@```]G/<*``%`0EH`,2`38!@5$@P(`I\J01``#/```C.Z7"2"6_X`Q(!-@/(!A(0-L]V@``L'J`0$0`!V`"F_@UO_ZEP +M@.``V2"F"?*&('Z/VO(#R*`80`0&&A@T`Q(!-I(1``'JN`;RJKB""._YDAD$ +M``,2`C9^$@$!@A(``8`2`P$X8!MC#0F!<'L;8VFA`8)1(,"` +M;O):#R_X@-@&$@$V!"&!#P(``0#7<0(````-$@(W"?3]N`?R3R+!``T:7#`% +M\*.Z4'D-&IPP`Q("-@&"42"`@2SR3R'``HRX$'D-&APP$(HS$H(`!+A%>,]U +M@`"HDL]RH``X+D2"!K40\"\N@1!.)H,7`-X/)LX0QGK/=H``8+WT)LX0T7`) +M\H#B\?7/<```__\$M0+P9+4(V`P:'##/<(``$#H1B%$@0($*\L]P@`!<.-H- +M[_P`B`T2`3<#R`&`_;C/(>(!T"'A`0T:7##/<8``P#L6@0'@%J$O\!#8#!H< +M,`W,H[@-&APPP@UO_^EP`Q("-@&2@.`)\@W(SW&```"K]"$``(#@"_(!@NZX +M"?0-R`':`""!#X``B*I`J0W,4R!`@`GR!!(!-HH@!``F"F_ZF!$!``/(&I#& +M"B_X#1(!-@W,42#`@`[RSW"``!"K`Q(!-@*`F!D```;(:@UO]`T2`C;Y!6_N +MH\#@>/'`X<6IP8MUJ7#/<8``K$A6#F_N)-JI<)(/+_@#$@$V]@@@`:EP]05O +M[JG`\<#AQ0,2`3:B@2"%$@OO_23:@.4-]`HAP`_K%,ES@#@N,HA@@/*(2$`5B3,.2"LX;C*(8(#RB$A`(MT@"1$'B"L42"`@,H@ +M@@/*("$`BW2`)(0>`*P%\%,E_H`L]`#>A"L*(@`A@'^``.2;"B1`+@3@A"D$ +M+P`@4@Y$*SXG`"&`?X``=*B@B.&(D'7,(\&#'_(*(<`/ZW)`+`X$0"L%!(?8 +MC;B(VP4F1!/1`:_O!27%`PHAP`_KSW6@`-0'TJ4.",_\SW"``"P>`("`X,PG(I`$ +M]!,=F),#R*`0```S\%$DP(0#$@$V"O)O($,`H!D``(H@"``&&A@PT_%1)("$ +M`-C/(.(%]?6D$0``^K@%\@78$+CM\8#B"/+/OQ\O`@@`#"N`\E#1``V`\@ +M@``&(0&`[_6`Y27R+RA!`TX@C@<-&I@S]=@%N&8(+_;)<0W(SW&@`!0$"J'/ +M<:``9"[P(0$`T[D*<(H.[_/D>2(,+_K)<`#8#R"``P8E#9#>]<]R@`#D-@"" +M!]F'X`T:6#`>\L]PH``X+@6`!""`#\````#7<,`````.\O78!;C/`"]""BSW"@`!0$*J#=`4_N\<#AQ<]P@``$K<]U +M@`#>!V"-08B$*Q\``"&!?X``A+`"#Z_]`N(@C<]P@`#8-\H+F!_*#Y@ST +M`!:`0`$=$A``%H!``1T2$``6@$``K6T!3^Z`X5;R0"'"`R2ZP[D"\`#9C^&6 +M``T`,R9!<(``?$A`)X-R-'L`>P`6`4`$&%```!8!0`084```%@%`!!A0```6 +M`4`$&%```!8!0`084```%@%`!!A0```6`4`$&%```!8!0`084```%@%`!!A0 +M```6`4`$&%```!8!0`084```%@%`!!A0```6`4`$&%```!8!0`084```%@%` +M!!A0```6`4`$&%``0B)"@+/UX'[@>(#ARB1-<.!XZ""M`0`6`4$"&%0`X'[@ +M>/'`+@A/[@#=SW<```0=2B``(JEV%2*`,PX0`08`V,]RH``4!,JBJ*(GH@2B +M/66(X6BYRB$.`%X.[_7I<$(@4"!,(`"@(.%$@`((.\L]P@`"8!@N( +MBW/)<8(-;_:I<@#``GVM?0`F@!^``)@&'!#!`,]R@`#\]R@`"@@GEB(8F`X7IB"O(0<0_R +M$'$2]H7E5O8!Y:]]"_!")9$0+R%')&&]KWT0\`,2SP``V6IU#?"`Y4HA`"#* +M)6$0!O)")5$0+R%')`'9@.$L\O-N]'\5)T$3SW.``*"".F,`(T4`%2=/%/EC +M(8E!BC!R^V/CB]GV`B)$``,5@@`$O_!_(G@$NB\D"`$")X,0;'@O($8.,@AO +M[HAQ#G@"?PCG[G]$O^U_3"``IH/V"N?M?\EP"G&6#"``Z7(!YL]P@`"8!A*( +MSWX0=K@&S/^%!B_NH<#QP#8.#^XH=8#BS",B@`GR+&TO><]V@`"8!C.N!O#/ +M<8``F`:SJ:EQSW:``)@&M*X`KE6N`@H@`':N`!")`.&(R7`2B-&.$':@`0D` +M1"D^%R]QA"X#$0HF0`X`(4,."B6`#X``&(.@H0N`14*)T`.`")- +M#@H@@!^``"S.`"!$$P`FC1^``)@&3"$`D,PA8I`G]!H3P```V1BM&Q/``$HD +M@'$$"K0"*+WD#K7OP`1/``(#@&/0`VEBM7*T@'8(02B2`<0#9J"#``Q-N +M%'@U>,=P@`"@@D"H0:A"J$.H`>$O>6'P?+D`(8H!.HMLN@`B0!$:B.!R`"(& +M`O8((`#I;5YQW&``*""`*GX +M<0`2@!``$X$0D@@@`.ER`1\"`!4E2P,5(TH#`1.`$`$2@1!Z""``Z7("'P(` +M`!.`$``2@1!F""``Z7(#'P(`0B!($$P@`)`!Y9('[?^O?0'FSW"``)@&$HC/ +M?A!V:`;,_P#9SW"``&1%K00O[B"HX'CQP,]P@`#4QY5B$!Y.&#@?P]XX'B!X/'`N'$< +M]$PE`(#$]DPE@(/.]@HAP`_K!0R($$!C"'#C\HAP0_*(L$'RB"!#P``V!3*(X$/```0 +M`CP!8>_*)"$`SW"``*`2-7C1P.!^X'CQP,]R@`"J!@IJL@D@`"EJ[@T``-8( +M``#/<8``F%H@@<]P@``T#R(((``!VL]Q@`"46B"!SW"``&`.#@@@``#:T<#@ +M?O'`/@L/[AIP@.%(=Y0`+```W3IQ%2!`(X#G0(@"B`SRSW:``'`0%7X"N!1X +MQW"``,@/"_#/=H``H!(5?@*X%'C'<(``J!`AB%$A`(`D\@40P0`BK@80P``# +MKNEPF@P@`$AQ`*Z`X,P@8H#*("$`$_)$*#X'`"&`?X``%(/%$(,`X1"!``(B +MP``0>`>X=@PO[F)Y`:Y"(4$@@.%Z!^W_`>7]`@_N\<"B"@_NSW"``)@&$1"( +M`,]P@`"8!A*($7"T``L`2B8``$HAP!%$+CX'+W"$*`,1)W``(($/@``4@Q\1 +MRP``(($/@``4@QX1R@#X<`#>!M\`)XT/@``4@]5]!XUI<07:F'`F"B``!17# +M$$`N@@!4>H0H`14`(D$.U'G'<8``+,ZX<0"IB'!)<0?:_@D@``85PQ`!'0(` +M8;^`YP'FM`?M_\]^0B%)$$PA`)!`)D8`>@?M_R\FAP%`($@0SW"``)@&$H@O +M(`<2$7!6!\K_/0(/[@+;8*@`V`"I`=C@?P"JX'BAP?'`O@D/[J'!9<((=BAU +MSW"``+X&A<&+@`J +M`""H4R6`$(7@3``*`$8ES1&O?1OP`12`,``F@1^``)C+4FU4>EEA(,(`J40N +MOA8`)4`>1*D4%,$P^&`@J,EP7@@@`*EQ`>6O?5,E@!"%X*/V(?`!%((P$FT4 +M>``F@1^``)C+.&!`J"#"1*C)<#(((`"I<0_P0B4`%@]X`12!,,=V@`"PS`*X +M%'@>9B#`**X,K@C<6P$O[J'`X'C@?N!X\<#B"`_N`-[/<*``M`]P$!``SW"@ +M`+0/W*#/<8``F`92B7&)4',1]L]U@`#$BG_8%"//`+]G`*_!KP'C;WM0"@_N`*4`W0[>SW>``(!'E@CO_ZAG +M8;Z`Y@'EKWTY]\]P@`#"QX*6H`V`\@0``G<%IXX'\.(,``X'CQP/8/S^W/=X``F`8`C_H+[_\SC\]P +M@`!<10`0T`#/<8``)"(4CT>)$'(/]`"/(8DP<`OTSW"``&5%`!#```D@``0O +M(`4@L8\#\`'EKWT2CQ!U`@$)``#>2B*`(\]P@`!=10"(@.`1\D0MOA,`)D`> +MSW&``%Q%`!'"```@@0^``"C/0*E>\,]P@`"X*B"`8'D`V(S@"_3/<(``<$?) +M8`(@0"`->$@@0``#\$@@0"`O(04@SW"``(!'RV`3CZEQ]@QO]56/"2!`!"\A +M!2#/<(``N"H@@&!Y`-@`)9,?@`"T!I#@#/+/<(``N"H@@&!Y`-B,X`03@2`, +M],]Q@`!4",EA!!.`(")X"2!!!`GPSW"``&!'R&`">0DA001$+;X3`"9`'L=P +M@``HSR"H.G`3CZEQ]@ZO_\ER`!'!(`)Y`!E"($(B4B!,(@"@`>84!^W_SWY] +M\?T&S^W@>/'`K@[/[0AUSW"``.LL`(@0=2AW!_3/<(``ZBP`B!!W'/+/=H`` +MF`:I<$`F@1*""&_V0";"$BJ.!&Y^#R_V2XX*CBH.+_8KCL]P@`#K+*"HSW"` +M`.HLX*B]!L_M@.#AQ1KTC"'"C3@`*@`!V$HD@''/.!_P<7AQ>'&`!'-`(#E1/8` +MW:"I@.`<\H#E1?8`V`"I`-W/<(``7$8`D!!UA/:I:*U]H*G/<(``M$44($X# +MH(Z@J@`1P0`T>`&(&O"`Y43V`-V@J<]P@``(1P"0$'6%]JEHK7V@J<]P@`!@ +M1A0@3@.@CJ"J`!'!`#1X`8@`J\'&X'_!Q?'`D@WO[0#8H<$`'`0PSW6``(`' +M`)7/=H``%(/)<8HB!`K"#N_U`=N`X!#T"B'`#^MR`!4$$<]P``#;%(?;B[OI +M`B_OBB4$"@`6A!!,)`"!RB'+#\HBRP?*((L/``#<%,HCBP\``(P`SR/K`KP" +M*^_*)2L`V@I/]8#@RB'"#\HBP@?*(((/``#=%,HC@@\``)(`SR/B`LHD(@"0 +M`B+ORB4B`(MQ1=@!VCH.[_4!VX#@#_0*(<`/ZW+/<```WA25VXN[BB1!`64" +M+^]*)0```!0`,0'9AB#^#\#@P'G/<(``>!4@J!D%[^VAP.!X\<"F#,_M"'77 +M=24``(``V$KWSW&``'C')8$P==#W(GT!X/GQSW"``'C'Q8"IC"`0@,HAQ@_*(L8'RB"&#P``S2+*(^8,RB0F`.0!)N_*)08!%KBQ +M!._MI7B!X,]Q@`!`!P3T`=@`J0&I`(F!X,H@@0\``,0)RB""#P``@`#@?P&A +MSW*``(`O((H982"J(8HX8.!_`:I!B0*X%GC'<(``$+](J"*)X'\IJ,]Q@`#X +M'O`A`@``V0V21+C@N"ZB!/**(0@`+J+AN`3RB[DNHE$@@(`#\HVY+J+@?@T2 +M`C8$(+Z/8````,]S@```JE1[QW*``'"J"'$&\@/(')!1(("""O($(8$/80`` +M`-=Q`0````;T`-@`LP'8'O`,S%$@P($#$@$V#?(R$8$``8LP<`3T`-@!J_+Q +M`>`!JPOP,1&!``"+,'`%]`#8`*OF\0'@`*L"V.!_&*KAQ>'&SW*``(@&@.#` +M(B(!_]T2:19X`""##X``%[^@JP#=2B0`<<]S@`#XQJ@@@`*N8GAE-GC$J*YB +M`>6O?<"HP<;@?\'%X'CQP/H*S^W/<(``I!@`@(+@H<%&\L]V@`!4.,]U@`!8 +M.`"%((80<2KRSW"``$0E!(!1(("`0,$%\D\A``%`P(#A"/3R"F_S`-CF"D_S +M/@V/]8MP!-FAVCW;Z@YO_!>[((:`X0OR`(6`X`?TR@IO\P'8Y@N/]2"&(*6` +MX1+RW@M/\W_8"KC/<:``T!L3H7_8$*$`V)6X$*&J#F_P`=C)`N_MH<#@>/'` +M4@K/[<]Q@`#X'A5Y0($(@@0@@P^`````1"`/`B^[!K]E?P0@@P\``0``02M. +M`^5^++O%>\$2#@;1<\`2#08P\@0@OH^``0``'O+/=H``0"+(CH?F&/2^N`BB +M0($(@@0@@P^`````1"`!`B^[!KEE>00@@`\``0``02A#`R5[++@%>X#EP1K8 +M``SR+RE!`TXA@`<2""``$"4-$(#E^/49`L_M\<"N">_MF'"0X(WW"B'`#^MR +M<=B-N(HCC0LQ!^_N2B4`!$HD`'0`VZ@@P`Y`+(T!=7U`+((`QW6``-#"`(7/ +M<8``$+]6>MVX06$`I?&YT2`B@@CR1"`"!B.Z`>*!X@OWSW*``%#!%B("`4"* +M42(`@`/RGK@3\"VYP+G/=X``^![P)T\04B!.`L$7`18+(8"#!O(HA_ZY[_.? +MN`"E`>-Y`<_MX'C/<8``^![P(0``SW&``*RINQ`"!KH0`P9"H6&AO!`"!KT0 +M``9%H>!_!J'@>,]Q@`#X'O`A``#/<8``K*F^$``&%B$"``*2&K$#DANQ"(HX +M&0(``-C@?QVQ\<#AQ<]SH`"L+QF#\+@9@P#=#/($((`/"````-=P"`````'8 +MP'@'\(8@?P^"X`'8P'B`X!GR&8,$((`/#@```$(@`(#*(&(`@>`/\@HAP`_K +MK:#AB7_&*5ZSW6@`*@@K87DY9+W@.+L +M]0+;SW*``*088*)1($"`SW*``%0X`((1\H&X$/`X$P0`6!,%``HAP`_KA4`B('@(*() +M],]Q@``8.`"!@.##]FJX`*$!V<]P@``@';H)K_T@J`4`S^W@?N!XX'[@>/'` +M>@^/[4H@`"#/<8``U"O/<(``V"K/2R1`>4`(%`@1"H^"R]Q/V/CC_%U/F.O]\]R@`!`(DB*A^)&]$:.QW&``%`K +M@>(!VL(B@0!5>1@1!`%,)(""3O8*(<`/ZW+/<```Q1N*(T0&G03O[DHE@`+L +M$`$!]A"``"QX+W42=97WJ7"&"._M"G&`X7OR"B'`#^MRSW```,8;BB-$"`HD +M``1E!._NN'4*<%X([^VI<8#A9_(*(<`/ZW+/<```QQN*(X0)[O&(XDGT0(!1 +M(@""1?)&CL=Q@`!0*X'B`=K"(H$`57D8$00!3"2`@LHARP_*((L/``#,&\HC +MBP\``"X!;`?K_\HBRP?L$!$!#"!`I!;W*G#V#Z_M"G&`X3/R"B'`#^MRSW`` +M`,\;BB-$#0HD``35`^_N"B5`!`IPS@^O[2IQ@.$?\@HAP`_K!XX'C@>.!X +MX'C@>.!XX'@`@N!^\<#AQ2#=SW.@`,@?L*-#&Q@``-BZ#^__C;BQHPT&C^WQ +MP(H-C^VAP3IP*'8:H1#8#J$!V!49&("Z#N__@=A1(`##%?+/<(``"$4!V2"@`\BD +M$`$`FKFD&$``0@BO_@'8SW&``#0]#8$!X`VA`]G/<*``]`HA#8#J(!V!4:&(`H<`?P`=D$((`/(````%$@`,/,(2&`S"`A +M@!CT42,`P!3TSW```)\7M@O/],]RH`#\1!V"68+KN@#9YO4$(+Z/``8``.#U +MX?%1(P#`%?+/<(``"$4!V2"@`\BD$`$`FKFD&$``I@]O_@'8SW&``#0]#8$! +MX`VA42``PP#8"?3/<8``N#P0@0'@$*$`V)BXT<#@?N!X\<"J"Z_M`-D(=\]P +MH``L($`0$`#/=9\`N/\=A<]V@`!L!CVE`*8,\$8,C^[/<`\`0$("#6_Q"G&! +MX!#RSW"@`-0+&(!"(``(2"```!!W+O<`AAVEN0./[0"&"B'`#^MR7ML=I<]P +M``#.(HHDPP_=`._NN'/QP"8+K^T!V:7!&G#/=8``B`9:=2X*;_^+<$P@0*`` +M%(4P`121,`3T0"42$4PE`(#$]DPE`('-]@HAP`_KG"%]HPDPZ\H]``6`$$`%H]``!:` +M0``6`$%,)`"DA@`*`(#G)_+/<(``;!0`@$`LS2"U?1#@N&"F"6__!-G/<(`` +M;!0`@$PA0*`=93)@.&`%(D($ +M0+`$W0;P@<`$W3H);_^I<0`BC",`'`(5SW"``/@>\"``!![?P!`"!H#B+RF! +M``(G0!`E\C)HSW.``!>_-GDK8Q$C@(,(\@`F@1^``/C&%GD`&0(%`"V!$PLA +MP(`(\@`F@1^``/C&%GD$&0(%$"("@"\I@0`")T`0WO5"(T`@@.#8!LW_C@]/ +M\O$!K^VEP.!XX'[@>/'`G@F/[<]Q@``(R$`A$0%5(4X$SW"``-`K%H``WX#@ +M5B%-`RP*0OD`$00A`"2!`S!U.``&`$"-C")#AP&-$O1"C=#B#O1#C>_B#/1$ +MC8PB@H8(]$6-B>($]+]@`N?"?P+@'64P=:GW@.?*)P$1SW.``/`K!8M`()`` +M521`!`)PC"`(@,HAS0_*(LT'RB"-#P``PAO*(XT/``"0`*0&K>[*)0T$D'?8 +M9T?W`"`!!%H/[_P"),(#!&O99Y8.[_P*<@`1`2'/<(``T"L"<0`91"`M`:_M +M]J#@>/'`X<4(=<]P@`!`(@B(A^#,(&*"!?)J"(_T@.`>\L]P@`#8*@"`42"` +M@ACTV@N/](#@%/+/<8``^!X`@<@0``:&('^."O0!@<@0``:&('^.5`RA_/'`8@B/[>(*C_2`X"3RSW"``$`B"(B' +MX![TSW:``-@J`(;QN!CRSW"@`$@N"X#/=:``."[3N.>%!'\'A>9X!Z6F"B`` +M$-@'A:B_Y7@'I0"&L;@`IG4`C^WQP(X*C_2`X!?RSW"``$`B"(B'X!'TSW.` +M`-@J`(/PN-$@880)],]RH``X+B>"D;B(N2>B`*/1P.!^X'CQP-(/3^W/<*`` +M2"X+@,]UH``X+M.XQX4$?@>%QG@'I3(*(``0V`>%I;[%>`>E$0"/[>!XX<7/ +M<(``^!X!@![9P!`#!H#C+RC!``(A`@`3\H[B"O(2:A9XSW6``!^_"&6!X`OR +M$".#@"\HP0`"(0(`\/4!V`+P`-C@?\'%SW"``-`K%H!"(`"`X'_*(&(`X'CQ +MP.'%SW6``-@J`(7ON`?RK[A""2```*4Y\)H)C_2`X`3R@@]/\C/P`(7RN`7R +MLKCR#V_R`*6."8_T@.`G\L]P@`#4*TB(*HA$*CX+`"&`?X``9"LU>`:(@>`9 +M]!H*C_2`X!7RSW"``$`B"(B'X,P@8H(&\GH.3_2`X`GR`(51((""E`JA_4&3^T`V<]P@`#0*^!_-J#/<8``V"H`@8*X +M`*$`V+$!K_:,N/'`3@Y/[<]P@`#X0P8,[_T`W\]P@``41/H+[_T"W<]P@`#` +M0^X+S_W/<(``I$/F"\_]SW:``-0L<-P")@$31"\^%PH@0"[."^_]`"%`#D(F +M`![""^_]`G!AO8#E`>"R_U`=F`Y?#U@.;=]<]PH`#`+Z48&(44&-B$ +MV01/[>!^X'C@?N!X\<".#$_M"'8&\,]P``!K#AH,C_3/=:``P"^C%0"642`` +M@?7S!\A`'1B0#`J/@.`)\L]Q@``@/1>1`>`0>!>Q`-@*K]D#3^W@>*'!\[A4'@&]-(*[_"!P1KP +MM^$(]!MX$'C""N_P@<$1\)3A!/0<>`KPBN$%]`0`#8SW*I`*3_N:($%`$Q@K@WHAJB"?#HN3?R3"(`H-$@XJ$%\L]UH`#('TSP +MSW*E`*S_SW"``/@>N*($@%80``$4X`(A`R`#XR*[>&-X8$@@0``%N$4@0`,6 +MHD$HP"'`N'=H+,`$(8$/````("6YSW6@`,@?97@E>(FXCK@9HD`5`!8@\"S` +M@.#*(<$/RB+!!\H@(0[/("$#RB,A!<\C(0/*)"$`_`=A[LHEP0`%O:5XSW&E +M`*S_%J'/=:``R!]`%0`6SW:@`+1'5Q8!EDHC`"!*)$`@!"&^CP`H``#/<8`` +M6!TP@2"!PB0")07PLMBR":_TC+AO%@"63"0`H`0@A`^`````!"""#R`````$ +M((,/``8```;R0!4!%H/AA/<`V0/P`=D3%0^6!""^CP`X```$)X\?````@,PA +M(8#`(V$@!2(!`>5Y!2'^@`7T3"-`HIX'SO^`YP7R@.+,(R&`CO)K%A.63",` +MH$_R:G2$)-"1"_+/<8``P#L0@0#=`>`0H9R]8_!1(\"@"O+/<8``P#L1@0'@ +M$:%"W5GP:G2$)`*8"?+/<8``-#T1@0'@$:$.\%$C@*$)\L]Q@``T/02!`>`$ +MH03P4R,^HP3R`-T_\%$C0*,*\H8.S_S/<8``L#T%@0'@!:'T\9X+S_(*(<`/ +MZW)O%@661-B,N.G;C+NE!F_N"B3`!(#B"/+/<8``N#P0@0'@$*'<\8#C*?+Z +MN`ORSW*``#0]+X(`W0'A+Z*1O0KP^;@-\L]R@``T/3*"0MT!X3*BB@NO_VIQ +MF+U(\'$6!)9O%@66"B'`#^MR.=C/\`,2`3;/<(``",H0<0?RSW"``-#*$'$+])(1``&J +MN)(9!`">$0`!JKB>&00`SW&@_F0`SW"?`+C_-J#/<(``R`8`@(#@"/+/<8`` +M="4%@2)P!:'/<8``P#L/@0'@#Z'/<(``R)LA@,]P@`#X'@.`%)`0<0WTSW&` +M`*06&H$[@21X42``@I`-8O7*(&(`J7`(W*L'+^VCP*'!\!XSW"D`)!!38#/<8``9*Q"L1J`42!`Q@.Q!""`#_\````PN`2QSW"``&2L +M`-H(\L]Q@`!0JS*!42&`@@7R0K!#L$2PX']9L.!X\<""#B_MF'#/<(``4*L. +MD,]V@`!DK`"VSW"``%"K2!`%``0ECX\````"`-L$\E$E`((*\L]QI@#H_PN! +M`Z8,@02F!/!DIF.FSW6D`+1%#!4"E@T5`);_V2\@AQ"`YQ"Y!"))`"SR,A4! +MEE,ACP#_9R&V_]GT?PBY[W]$>4`O!A(`)D<``"#($P4G!P)`+P$6!"*"#P#_ +M``!`+P@4.F(`($@2_]D%)P<""+D%(L(!!"!'`/A@`">!`25XY;9/>00B@@__ +M````*+I%>2.V#W@$M@05`)91)0""`K80\D0E``8CN`'@@>#*]\]QI@#H_PV! +M!:8.@0:F!/!FIF6F`-I*)(!P!MF-N:@@0`,IV!*X\"!#`$`F`!]5>`'A8*`! +MXL]P@`!0JP"0.!X`$54F010:ML]P@``\K,H+K_P(VAL5`);/<:4`V,L9IAP5 +M`)8:IAT5`)8;I@Z!'*8/@1VF)A4`EAZFSW"D`)!_'("!!2_M'Z;AQ<]U@`!D +MK`FE*J5XM4NE`=@9M>!_P<5*)`!Z`-FH((`"`-K/<(``9*PU>$"@`>'@?@`` +M`@````!``0``````J!)#=0$&``$``````````.@]@`!\/H```'Z``#@=@`!` +M"(``<`6`@0\:&R(``!LE`@`;0```&W$4!H"!``#`%@\;"R(8!H"!``#`%@\; +M"B(!,B``#&$0,` +M`20```$E```3)<(L$R0$*,`1`D83)`0HP!'"7Q,D!"C`$0]%`"(`7``Y+``` +M9```$R0!`!,E.!S`$0]W$R+@',`1`@`!8@\!$R($",`1_`7`$@0HP!$/$P(B +M``;`$@0HP!$/$PX!("!#QH;(@``&R4"`!M````;<0`````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M``````````"P`H``/`"`````````````*`"````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M````````````#)&```P&```,D8``G-$```D-$10*#1,7&1D9&0D)```DC(`` +M*'D```R1@```````#)&````````HK8``2'D``#"`````````,8```(B(,S,R +M@````*JJBC.`````````-(`````````U@````````#:`````````-X`````` +M```X@````````#F`````````.H`````````[@````````#R`````````/8`` +M`*JJ"@`^@```4X6(B#^````````P,(`````````Q@```FIE853*`````JJJJ +M,X`````````T@````````#6`````````-H`````````W@````````#B````` +M````.8`````````Z@````````#N`````````/(`````````]@```JJH*`#Z` +M``!5F)FJ/X```````%`P@````````#&`````````,H`````````S@``````` +M`#2`````````-8`````````V@````````#>`````````.(`````````Y@``` +M`````#J`````````.X`````````\@````````#V`````````/H`````````_ +M@````````#"`````````,8`````````R@````````#.`````````-(```)IY +M```U@```JJJJJC:`````````-X`````````X@````````#F`````````.H`` +M`*JJJ@H[@````'"9JCR`````````/8`````````^@````````#^````````` +M__\``*4!`0"Y`=\`L0`;`!8!&P"O`!L`%`$;`&P`H`#1`*``;P"#`'$`@P!V +M`(,``!)`-T` +M20!_`%H`Y`!:`*H`/P"K``$`#P$_`!`!`0!Y`&H`W@!J`*@````-`0``I@`W +M`*<``0`+`3<`#`$!``0`"`"<``0``O0```+X```"_````P````"(!```C`0``)`$``"4!```2 +MT@``$](``````0`"``,`+`!D`'0`@`",`*$`!P```````0`"``,``````+<3 +M(@"X%",`N14D`+L6)0"\%R8`O1@G`,`9*`#$&BD`!QL```@<`0`+'0(`#!X# +M`!`?!``B(04`)"(&`"8C!P`H)`@`*B4)`"PF"@`N)PL`,"@,`#0I#0`X*@X` +M/"L/`$`L$`!D+A$`:"\2`&PP$P!P,10`=#(5`'@S%@!\-!<`@#48`(0V&0"( +M-QH`C#@;`)$Z'`"5.QT`F3P>`)T]'P"A/B``I3\A`"1)!@(L2@H"-$L-`3Q, +M#P%D31$!;$X3`71/%0%\4!"2@`'PD +MH`"`)*``A"2@`%`0H`!4$*``2":@`&`0H`!,)J``9!"@`&@0H`!<$*``6!"@ +M`#`0H``\$*``-!"@`"P,H```@:0``8&D``.!I`"()*``C"2@`)`DH`"4)*`` +MF"2@`)PDH`"@)*``I"2@`(!;@```````L@S(`/________\``?__`@/___\$ +M______________________\%_P;_!_\(_PG_"O\+_PS___\-____#O___P__ +M__\0______________________________________________\1____$O__ +M_Q/___\4____%?___Q;___\7____&/___QG___\:____&_____\<____'?__ +M_Q[___\?____(/___R'______________________R(C)/\E)B?__RC___\I +M____________________________________________________________ +M__________________\``0`!`0```````````0```````````````````P`` +M```````!`````````/1"`0``````6'````$```!@]P$``@```-#V`0`#```` +MF%0"``0```#T0@$`!0```.`L`0`&````+/X```<```!H+0$`"``````[```) +M````6&,```H```#4TP``"P```$@T```,````G%0"``T```#4\P``#@```(CT +M```/````8/,``!````!D]```$0```)!3`0`2````^!<"`!,````\)0``%``` +M`&1\`0`5````Y&4!`!8```#$=0$`%P```/CU`0`8````I*8!`!D```"4'0$` +M&@```/!"`0`;`````!0%``````````````````````#_`/\````````'```` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M````````````__________\````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M``````````````````````````````````````B9```(F0``")D``/"````( +MF0``")D``%2!```(F0``")D```B9```(F0``")D```B9```(F0``")D```B9 +M```4B0``6(@``$B(``!PAP``@(@``"R'```(F0``")D``/R.``"PD@``8)0` +M``B9```(F0``")D``'"8```4C@``3(X``+B-```(F0``")D```B9```LF``` +M")D``)B-```(F0``")D```B9```(F0``")D```B9```(F0``")D```B9```( +MF0``")D```B9```(F0``")D```B9```(F0``")D```B9```(F0``")D```B9 +M```(F0``")D```B9```(F0``")D```B9```(F0``")D```B9```(F0``")D` +M`!B"```(F0``")D```B9```(F0``")D``'R5```(F0``")D```B9```(F0`` +M")D```"%```(F0``$(4```R%```$A0``"(4``*!^```(F0``='X```B9```( +MF0``")D```B9```(F0``")D```B9```(F0```'X```B9```(F0``")D```B9 +M```(F0``")D```B9```(F0``")D```B9```(F0``4(,``.R"```(F0``;(,` +M``B9```X@@``\(8```B9```(F0``-(D```B9```(F0``")D```B9```(F0`` +MB(H``&2)```(F0``")D```B9```(F0``")D```B9```(F0``")D```B9```( +MF0``")D``!2%```(F0``")D```B9```@E0``")D```B9```(F0``P)<```B9 +M```DF```O)0```B9```(F0``X'P``'R4```(F0``")D``'B'``"0AP``")D` +M``B9``"\A```Y'X```B9```(F0``")D``%2.```4AP``")D```B9```(F0`` +M")D```B9```(F0``3(8```B9``#LF0``+)P``&";```TG```6)L``%";```\ +MG```+)D```B9```(F0``")D```B9```(F0``")D```B9```(F0``Q(0```B9 +M```(F0``")D```B9```(F0``")D```B9``#0G```Y)T``"A]``!T?0``")D` +M``B9```(F0``")D```B9```8?P``")D```B9```(F0``")D```B9```(F0`` +M")D```B9```(F0``")D```B9```(F0``")D```B9```(F0``")D```B9```( +MF0``")D```B9```(F0``")D```B9```(F0``")D```B9```(F0``")D``!Q_ +M```\@```K'\``$2<``",?0``R(```&"!```(F0``")D```B9```(F0``3($` +M`%"!```(F0``")D``/2````````````````````````````````````````` +M``````````````````````````````````````````````````````````"( +MJP``T*D``/2L``#\JP``"*X``````0#_____`````/__________`0```$`. +M`````````````````````````````````````````0``````T/X````````` +M```````8(*``(""@`$`AH`!((:``'""@`"0@H`!$(:``3"&@`"@@H``P(*`` +M:"&@`'`AH``L(*``-""@`&PAH`!T(:``.""@`#P@H`!X(:``?"&@```````` +M``````````````````````````$````!```````````````````````````` +M````````````````````````]`P```#_`P"4#0```/\%`#@-````_RT`%`T` +M``#_/0"P#````/\$`-@,````_R4`;-<```#_W0!<#0``$!!,```````````` +M``$!`#P\/#P\/#P\/#P\/#P\/#P\/#P\/#P\/#P\/#P\/#P\%145%3P\/#P5 +M%145/#P\/``````````````````````\/#P\/#P\/#P\/#P\/#P\/#P\/#P\ +M/#P\/#P\/#P\/!45%14\/#P\%145%3P\/#P`````````````````````/#P\ +M/#P\/#P\/#P\/#P\/#P\/#P\/#P\/#P\/#P\/#P5%145/#P\/!45%14\/#P\ +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M````_P````$````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M````````````````````````````B`\``#06``#D$@``9!```/07``#<$P`` +M,!4````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M`!@'@`"TD8``&````'21@```````````````````````^!H!`-"Y``#$(0`` +MT+D``-"Y``#0N0``$`<````0`@#4Y```T+D``-"Y```D*0``)"D``"0I```D +M*0``)"D``"0I```D*0``T+D``-"Y``#0N0``T+D``.Q4``#0N0``T+D``-"Y +M``#0N0``T+D``+CD``#0N0``T+D``*C6````````/`(!`$`"`0"T`@``H`(` +M`+A2`0``````.,D``-S:`0!@R0``!-L!`(3)```LVP$`[#:``*"&@``````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````!``(``@`#``0`!``%``8`!@`'``@`"``)``H`"@`+``P`#``- +M``X`#@`/`"8`)P`H`"D`*@!&`$8`1P!(`$@`20!*`$H`2P!,`&@`:0!J`&H` +M:P!L`&P`;0!N`&X`;P!P`'``<0!R`'(```+21@``8````=)&``````````````````'\``````````'\` +M`````````````````````````````/\``/__```!``````````<````````` +M`````````````0(#!`0$!`0%!@<("`@("`D*"PP-``!N.V@[8CM<.VXZ:#IB +M.EPZ;CEH.6(Y7#EN.&@X8CA<.&XW:#=B-UPW;BEH*6(I7"EN*&@H8BA<*&XG +M:"=B)UPG;AEH&6(97!EN&&@88AA<&&X7:!=B%UP7;A9H%F(67!9N%6@58A5< +M%6X4:!1B%%P4;A-H$V(37!-N$F@28A)<$FX1:!%B$5P1;A!H$&(07!!N`F@" +M8@)<`FX!:`%B`5P!;@!H`&(`7`!4````;CMH.V([7#MN.F@Z8CI<.FXY:#EB +M.5PY;BMH*V(K7"MN*F@J8BI<*FXI:"EB*5PI;BAH*&(H7"AN)V@G8B=<)VXF +M:"9B)EPF;B5H)6(E7"5N)&@D8B1<)&XC:"-B(UPC;B)H(F(B7")N(6@A8B%< +M(6X@:"!B(%P@;A)H$F(27!)N$6@18A%<$6X0:!!B$%P05Q!2$$T021!N`6@! +M8@%<`6X`:`!B`%P`5```````````````'0`````````````````````````` +M``````````````````````````````#0;P$`"`````,```!`1(``A`B```0) +M@`"$"8``!`J```H-$10*#1$4&1D9&0H*````````!@8&!@D)"0D`!@````4& +M!P@-#@\0%187&!D````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M````````````````````````````````````````````````#QJLK"4OK*RL +MK*REK'Z13D,,FS8````R````CP",`(H`0P`/`"!H-P```!$`/CH@$0```B4` +M``PO```"+SDY``HE/+I'<8T`!Q`````X` +M```G````#P```"`````0`````@```/,`````````]`````````#U```````` +M```````````````````````````````````````````````````````````` +M!`````4```#A`PX>X0```.$##A[A```````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M`````````````````````````````````````````````.$##A[A````%?9C +M]K#V_/9&]Y#WV/[Y*_IG^J+ZW/H4^TO[@?NV^^K[ +M'/Q-_'W\J_S9_`7],/U9_8+]J?W/_?3]%_XY_EK^>OZ8_K;^TO[M_@;_'O\U +M_TO_8/]S_X7_EO^F_[3_P?_-_]C_X?_I__#_]O_Z__W__________?_Z__;_ +M\/_I_^'_V/_-_\'_M/^F_Y;_A?]S_V#_2_\U_Q[_!O_M_M+^MOZ8_GK^6OXY +M_A?^]/W/_:G]@OU9_3#]!?W9_*O\??Q-_!S\ZONV^X'[2_L4^]SZHOIG^BOZ +M[OFP^7#Y+_GM^*GX9?@?^-CWD/=&]_SVL/9C]G"Y@[J6NZJ\OKW2ON>__,`1 +MPB?#/<13Q6K&@,>7R*_)QLK>R_;,#\XGST#06=%RTHS3IM2_U=K6]-<.V2G: +M1-M?W'K=EMZQW\W@Z>$%XR'D/N5:YG?GD^BPZ+^```>`3P"6@-X!)8%M`;1!^\(#0HK"T@, +M9@V##J$/OA#<$?D2%A0S%5`6;1>)&*89PAK?&_L<%QXS'T\@:B&&(J$CO"37 +M)?(F#"@F*4$J6BMT+(XMIR[`+]DP\3$*,R(T.C51-FDW@#B6.:TZPSO9/.\] +M!#\90"Y!0D)60VI$?47___\`____`?\"`____P0%_PG_!PH&"`L``0$"`0(" +M`P$!`0$!`0$!`@("`@("`@(#`P,#`P,#`P0$!`0$!`0$`0("`@("`@,#`P,# +M`P,#`P,#`P,#!`0$!`0$!`0$!`0$!`0$!`0$!`0$!`0$````H#,``.`U```@ +M,```("\``*`O``"T-```Z"X``-PT``!<-0``.@$"`=4`WP#:`*(`=0!_`(H% +M*@,Y`:@!B@7*`MD`2`$!`P\'"A0W;FH!&@'9`.@`"@&Z`'D`B`#*`4H!X@#Y +M`,H!Z@""`)D`=-%%%^BBBRX`!0(`'!FF0!@_\P`\- +MTB`-"[1`"PW2(`T+M$`+B9W8"0B,P`B)G=@)"(S`"`=^X`<'?N`'P2PI!PJH +M@`H(C,`(!WB`!PB,P`@'>(`'!FF0!K"RU04&:9`&L++5!0540`4%5$`%UAW& +M!`$'#Q\_?___9N8```4&`0(#!```5`!4`&P`8`!<`%0`C`!X``T/!0<)"P$# +M*``H`#0`,``L`"P`1``\`"P`+``\`#0`,``L`%0`1`!5554!2V@O`555507C +M.(X#JJJJ`G$```` +M```$```!>``#A!X``0````,```%Z``.&'@`"````!````7H`!(@>``(````# +M```!>P`$C!\```````0```%\``21'P`!0````P```7X`!)4?``,````$```! +M?P`%EQ\``L````,```&```69(```0````P```8$`!9T@``%````#```!@@`% +MGR```<````,```&#``6A(``#````!````8,`!:4A``!````#```!A0`%$(L! +M```````0BP$``````!"+`0``````$(L!```````0BP$``````!"+`0`````` +M$(L!```````0BP$``````'2$`0`8````/(8!`"````!`C`$`%````#R-`0`4 +M````=(H!``X```!`B0$`#@```$2*`0`4````1(H!`!0```!`(T`E(2$A(4!` +M0$!`!00$`0%`0$!`!05`0`P,0`T,#`$!`05`0`4%``0`!$!```1`0$`%0$!` +M0$`%0$!`!04%`0$!`4`%!04!!0$!0`4%!4`%0`4%!04%``````````!D```` +M`)`!``H```"=BM>9-FNQ%CYT?0(F'^A7OZ[+)C@O[[$%GL_U? +MZD6_(_=3EN1;F\)U'.&N/6I,6FQ!?@+U3X-<:/11--$(^9/B5/%NW%AM>:56:4$<^*$.D&!('^\*!$>+HEXTOSHOY=P("*!:T_ +MO"%(<`3QWV/!=W6O8T(P(!KE#OUMOTR!%!@U)B_#X;ZB-JS(Y[HK,I7FH,"8&=&>?Z-F1'Y4JSN#"\J,*:?BO!T6=JT[VU9D +M3G0>%-N2"@QL2.2X79]NO>]#IL2H.:0Q-].+\C+50XM9;K?:C`%DL=* +MRWO\J-9M.BP``0($!`````0,#`@$#`0$0````(```````0````(``$`````` +M!```0````$``````\&$```$!`@$"`@,````````````````````````````` +M`````````0`````````````````````````````````````````````````` +M``````````````````````!D````*@````X```````$!`````````````0$` +M`````@`!``("`P,#3`,"`%@#`@!D`P(`<`,"`'@#`@"``P(``0$``0(!`0$` +M`````0````````````````````$``````````0````$``````````0`````` +M```!``````````$```````````````$``````````````/____\````````` +M`````````````````````````````````````````````````(`-````(``` +M@`T``(`-````(```@`T````&````!``````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M`````````````````````````````````````!````#T+P(`(""`#P``0`!I +M(```:2!``&D@``!I($``(""`#P``Z`!I(```:2!``&D@``!I($``(""`#P`` +M..AI(```:2!``&D@``!*(```2B$``$HB``!*(P``2B0``$HE``!*)@``2B<` +M`$H@`!!*(0`02B(`$$HC`!!*)``02B4`$$HF`!!*)P`02B``($HA`"!*(@`@ +M2B,`($HD`"!*)0`@2B8`($HG`"!*(``P2B$`,`HD@#^!``!`02R<,$`LG#!" +M)!PT"B*`/X``(%4*(P`W<@O`!DHF`'!I($``2B8`<$HF`'!*)@!P2B8`<``6 +M`'"``)`;0'@@($"'`````````````/P`3<.-TU\.!X +M!-PTW3/PX'@$W##=,?#@>`3<+-TO\.!X!-PHW2WPX'@$W"3=*_#@>`3<(-TI +M\.!X!-P`3<%-TC\.!X!-P0W2'PX'@$W`S='_#@>`3< +M"-T<\.!X!-P$W1GP-!0:,#`4&3`L%!@P*!07,"04%C`@%!4P'!04,!@4$S`4 +M%!(P$!01,`P4$#`"QP'&L"1-,[`D'S/@?N!XX'C@>.!XX'C@>`HD@/`%($0` +MX"#!!T0D_H!!*L0`A``"`"\D`O%"(0$!0B`#`>@@H@0$$00"!!$%`@01!@($ +M$0<"!!L(`00;2`$$&X@!!!O(`2P`)0!$(CZ!/``B`$0B_(!`(<$`X"#!!T`C +MPP"H((`!`1&$`@$;"@$@(,`'!!$$`@01!0($&P@!U`?A_P0;2`%$(OR`!!$$ +M`LD'[_\$&P@!0B%!`$(@0P"H((`!`1&$`@$;"@$@(,`'SW&@`*PO&(&:N!BA +M706@$`78X'C/<:``K"\8@;.XNK@8H4D%H!!DV`HB0(``V>X``0`O)@#P2B9` +M`$X`!@!/`"``BB7_#^!X"B)`@`#9S@`!`&P`)``O)@#P7``%`"L(-0A*)D`` +M"'$`V`(AOH#@(,4'0GD!X`(AOH#@(,4'0GGK!^__`>`O+0$`0"5%``(F?/$` +M`"```"A``>@@8@,O(`"`+R%+``(AOH#`((8!PB&&`.!^$0`@`$H@`!!*($`0 +M#B)"`"\@"Q+.($6`BB7_#P@`!0`O+0$`0"5%``(F?/$``"```"A``4HF0`#H +M("(#+R``@"\A2P`"(;Z`P""&`<(AA@!*)@``0B#^D,X@@@%$('Z0SB&"`>!^ +M"0```.!X"B8`\(H@OP_*(&0`X'\O(`,`X'^*(/\/_!R(L?P<2+'\'`BQX@*)"+A'>$.) +M!WH`AN*&!R"'`$6)!(D0NABX1WA&B0BZ1WA'B0=Z`8:CA@<@B``(B4F)&+@0 +MN@=Z"HD(N`=Z"XE'>$V)!W\,B1"Z&+A'>$Z)"+HOB4=X)W@'?4HC0"%>\/8+ +MH`4`V0AWZG#N"Z`%`=D'?^APX@N@!0+9!W\)<-H+H`4#V2"&YW@'(0<`02L` +M!L8+H`4`V0AS*7"^"Z`%`=D'>TEPL@N@!0+9!WMI<*H+H`4#V2&&9W@'(0@` +M02X`)I8+H`4`V0AS"G"."Z`%`=D'>RIP@@N@!0+9!WN(<'H+H`4#V>*&9W@' +M?T$M`!9J"Z`%`-D(T$O`!(/>!X+H`4"V0=[+R`' +M`A(+H`4#V22&9W@'(04`02@`%@(+H`4`V0AS02\`!`]X\@J@!0'9!WM!+0`2 +M#WCF"J`%`MD'>^]XV@J@!0/99WAEA@=[02\`%LH*H`4`V9AP02@`%`]XO@J@ +M!0'9!R0$`$$O``(/>*X*H`4"V0*(*H`4#V2:&!R```0'(*H`4"V0=]+R#'`68*H`4# +MV:=XIX9"(U.@02T)!$$K$`0'?2#F02T7%$$N`"(O(,<002T*$B\CAQ5!+0$" +M+R1'`T$N%"1!*P("+R5'(2\GQR4O)P<`+R%'$B\BAQ(O(``=_Z'#>":`% +M!-G_V0BY)'@'?PEPS@F@!039#W@@AN=X)WA!*`$&`!I"($$H`00!&D(@02@! +M`@,:`B`"&D(@02L`!IX)H`4$V00@@P\`_P``*7".":`%!-G_V1"Y)'@'>TEP +M?@F@!039_]D(N21X!WMI<&X)H`4$V0]X(89G>"=X02@!!@0:0B!!*`$$!1I" +M($$H`0('&@(@!AI"($$N`"9"":`%!-D$((,/`/\```IP,@F@!039_]D0N21X +M!WLJ<"()H`4$V?_9"+DD>`=[B'`2":`%!-D/>"*&9W@G>$$H`08(&D(@02@! +M!`D:0B!!*`$""QH"(`H:0B!!+0`6X@B@!039!""##P#_``"*<-((H`4$V?_9 +M$+DD>`=[R'#""*`%!-G_V0BY)'@'>ZIPL@B@!039#W@CAF=X)WA!*`$&#!I" +M($$H`00-&D(@02@!`@X:0B"!`>__#QH"(.!X\&((H`4$V0]X'@B@!0'9 +M!WX`A2BX#WA*"*`%!-D/>`8(H`4"V0=^`(TZ"*`%!-D/>/8/8`4#V<=X`*4! +MA3BX(@B@!039#WC>#V`%`-D(=@&%,+@/>`X(H`4$V0]XR@]@!0'9!WX!A2BX +M#WCV#V`%!-D/>+(/8`4"V0=^!(WF#V`%!-D/>*(/8`4#V<=X`:4"A3BXS@]@ +M!039#WB*#V`%`-D(=@*%,+@/>+H/8`4$V0]X=@]@!0'9!WX"A2BX#WBB#V`% +M!-D/>%X/8`4"V0=^"(V2#V`%!-D/>$X/8`4#V<=X`J4#A3BX>@]@!039#W@V +M#V`%`-D(=@.%,+@/>&8/8`4$V0]X(@]@!0'9!WX#A2BX#WA.#V`%!-D/>`H/ +M8`4"V0=^#(T^#V`%!-D/>/H.8`4#V6&[@./'>*X&[?\#I1D`S__QP*X/C_]A +MB4")$+L8NF=Z8HD(NV=Z8XD`WV=Z0*!$B66)&+H0NT=[1HD(ND=[1XEG>D&@ +M2(EIB1BZ$+M'>TJ)"+I'>TN)9WI"H&V)3(D0NQBZ9WINB0B[+XEG>@K=)WI# +MH,.`"'-!+@`4#WBR#F`%!-D@@P0@@`\`_P``!R$$`$$N`!(/>)H.8`4$V00@ +M@`__````!R0$`,]XA@Y@!039!""`#P```/\')`0`02X`%FX.8`4$V0]XSW&` +M`-A4\"'!`P<@``$G>"&#!*,G>"*#!:,G>".#!J-AO2=X!Z-`(P`$@PUUD`'G +M+0>/_^'%"'41\.!XX'C@>.!XX'C@>.!XX'C@>.!XX'C@>.!XX'C@>.!X8;V, +M)?^?[?7@?\'%X'CQP.'%SW"``$P=38#/=8``](X@A;>ZN+H$(8$/`P````>Y +M17DMH/8*8!``V`"%SW&``-#)42"`@DR)SW"!``@.,FHV><=Q@0#("F"!5GA! +M@`7RE;M@H:NZ!/"UNV"AB[I!H`N-H[BA!J__"ZVBP?'`'@Z/_T7!SW6``$P= +M)X45"$$`,)44%`XQ"0Y!$%D=@A#0%0$6'0A!`,]Q@`!8(#R1%!0-,0T-01#/ +M<8``L"!9J8OJSW6``-0&P8V`Y@#9RB!!`"3R(:T+"I$#`=@>\$$H#0('?4$H +M`02G><]W@`#4!J"/4R5%$1\-,@3&N0HAP`_KM`!E"`0`;0@''\?'`2@V/_PC(SW*@`,@?#AH8@`G(#QH8@`K($!H8 +M@`L2`38"R"1X$1H8@`S(SW&``+0Q+1H8@`"!`>``H<.X5PA1`PO(?]D*N21X +M+R@!`$X@@@<`V`\@@``$(0&`0B*-`AGR"R-`P!?TSW"@`(@@\"!0`\]V@`", +M+@"&SW>``)`N#0T!$`"'$G`@#0$&H*8`'P`4'06/_^!XSW&``)Q4X'\(8>!X +M\<#&"4`%SW&``!P7\"$``$!X@-G/<*``T!LPH-'`X'Z`X>":`.)!T`%,]P@`"@-Y()@`[/ +M<(``2#B&"8`.SW"``&0X?@F`#L]W@``DEFV/@..>\DR/4',X`0P`SW"``,`; +M!(`@@,]P@`!$C$"@8:`BH,]P@0`P$PB0*0L"`,]P@0`P$\]Q@``\QVBP`=[/ +M<(``),?,H2.`#0GE``$8`@1CH!"/@.#*(&(``Z41CQ/HD^K/<(``3!T#@`F` +M&PB>`+X(8`('V`'8`:7/<*``+"`0@`"ESW"``!0I`(`="%$`SW"!`#`3SW$` +M`!`G"@VO_P6`$'@#\`#8SW&``,S&![$#A0P9!`1C"%$``(&"X,P@XH`#]`'8 +M`*%,%8`03PA1`,]PH``L(-"`SW`!`%`60,`!V$'`"!P`-!'80\``V(RX1,`` +MV!#9!-H(X*H`38<$L5@!`!X`]X2QT"$`R/A>@!A8#@ +MP`O!!,]P@`#T-T8(@`X!V<]P@``D%""@]@\@`@;8*0.O_Z7`X'BBP?'`O@JO +M_YAR1<%!*`$"!WE!*`($)WK&NL]U@0!("DEE764G"=\!%!0.,<]S@0"(#6AR +M-GK@@@L(P0/BDA,/@!,GBJ=J[PG>@0#8)_#&BH;N@-_/<(``U`;AJ,]W@`"@ +M'06/"PX!$(#8!:\)\,]W@`"P(!F/"PX!$(#8&:_&BC9[`!R``P>*A[D`K<]P +M@`#4!D"((*@!V$>K#-R/`H__H<'QP`,2`C?7<@```$`!VL(BB@`7NL=R``X` +M`(.Z['-`H^QR`*(^#"`$*'#1P.!_H<#@>*7@+?(1]F,(D`B$X"7R!_8OZ&<( +MT0#@?P#8-PA0`5L($0C@?PO8C"!#AQORS.`;\JW@$?(']C<(D`D_"!$*X'\* +MV#,($`PS"%$/X'\#V.!_`=C@?P+8X'\$V.!_!=C@?P;8X'\'V.!_"-C@?PG8 +MX'\,V.!_#=@.V.!^X'CQP.'%SW6``#0GJ7!`)8$;R@G@#2[:`=C1`:__81T" +M$/'`2@F/_Q\(M``(=0HAP`_KV5Z4QF8@`V05!D8 +M@`;P4QF8@U09F(-$+;X;)W<.EU89&(`/EU@9&(`0EU49&(`1EU<9&(`2EUH9 +M&(`3EUP9&(`4EUD9&(`5EUL9&(!""V`'*G!5`(__AN#QP`#8#_3/<(``0)9F +M"^__!MG/<8``M)D`@8*X`*$!V-'`X'[@>/'`@^#AQ0#8&/3/=8``))9`)0`5 +M-@OO_P/9SW"``$P=`X`4D#6-$PD``,]Q@`!$R1^!C;@?H0'8$0"/__'`@>#A +MQ0#8"?3/<(``.Y8!W?X*[_^I<:EP]0=/_^!X\<"6X.'%`-B,]\]U@`#TCJEP +MW@KO_P39"XV#N`NM`=C-!T__\<":X.'%`-B,]\]U@`#TC@1MN@KO_P39"XV" +MN`NM`=BI!T__\<`R#T__H\'/=H``7*TFCD`F#1("N31YCN`]90#8)?2+<(8* +M[_\,V0(4@#"!4QP`=BDP-'`X'Z!X`#9RB!" +M`!3TSW"@`%`,)8#/#Y"\F!_#/<9\`N/\0KAB!SR#B!]`@X0<8H1B! +MGK@8H1B!OK@8H0'8G05/_^!XX<3\',B^_!Q(ON'`X<'APN'#_!P(L?P<2+'\ +M'(BQ_!S(L?P<"++\'$BR_!R(LOP(P@7((!V.!_PB`+`/'`7@QO_THD0`#/=8``3!T5)0,0`(-`)0X5T7#")`(! +M\"4-$<@5!19$);Z!"?(*(<`/ZW*.V(VX<07@`'3;R!`-!J5YR!A8`*"#!ME& +M><@5`!8D>,@=&!``@\@0``:&('^.I`X!$&4$3__@>/'`[@MO_X#:"',]Q16`<@C@$`@<=W("3;<`(@@`^5*(A; +MV&`;8Q=K.;ME>`F)$?@=^`B>/'T(^$C'^9MUE +M0"V.%2J]Y('%?0`PB!Q7T' +M(XX`766D?F=^QW>`:=B8_F;88-=HZ8$YN,5X!R).`[A@!'Y'?@(GCQ^[=%$( +M_F;;8T`K#@,TN^J!Q7L')0X0&V-D?J=^`B>/'P``3Z3^9MIB0"I.!"^ZZX'% +M>@`/'^$)GMK^ +M9MA@U6@[N`5^YH%>9@$=X`B>/'[\_P$SX8'A@0"A#`C>X!7OK@=MC +M!R.``T1XQWC'=UXF45KX8+A@0"B-`S*X!7W@@7UE!R-``\1X9W@")X\?2196 +M./A@6&!`*`(%++A%>.6!N&`')0(09'JG>@(GCQ_0*:/O^F+:8M5J.[K%>NJ! +M&F('((X`I'X'?L=W1`)3%/YFVV-`*TX"-[ME?EYF!R:#$`1[!R./``(E@P]> +M)W\9^V-]94`M@Q,RO65]Y('=90\=[`B>/'RP8.`3[8WA@0"@#!2RX +M!7OI@;MC!R7`$,1XIWC'=^$AYLWX8%A@56@[N`5Z>F('(X``I'@X$0<`!R#/ +M``(G@`_(/"KX^X$`H3@(WN,5XXX%88`<@C@!D?D=^`B>/'RH+>?+^9MUE +M0"V.$^B!,KVE?AYF!R"-`T1]!WW'=UI%[13]9;MC0"L-!2R[97WM@=UE!R9# +M$P1[QWL")X\?'%;[%OMC>F)U:N*!.[IE>KIB!R6#$,1[IWL")X\?$`,(7/MC +M>&!`*$,"-[@%>^>!6V,'(X``I'A'>,=W;V?9`OA@V&!`*(X#,K@%?NR!?F8' +M(X`#1'AG>`(GCQ_5!-;I%?=UE!R6"$P=Z`B>/'T0)H+1?9_MC0"L"!.J! +M,+M%>[MC!R7"$,=Z`B>/'T!!D$-?9_A@0"C"!2FX[8$%>GIB!R.``*=XQW>; +M*,9^^X-1H/+C@@05^7F8'(H`#9W@")X\?7A4&V/A@N&!`*,T"-;CC@05] +MW64')8`31W@")X\?$"M[S_A@>&!`*`,$YH$PN&5XN&`')0,0QWO'=X@$!1W[ +M8WIB0"K#!>F!*;I%>QMC!R#"`*=Z`B>/'RLFQR_Z8MIBU&KL@3RZQ7IZ8@&!`*,,%X($IN&5XLGO88`5[ +MQWL")X\?U@N\W?MC>F)V:N>!.KI%>QMCTGIE>@=ZQWGUEI7IG>@(GCQ]K5%G<^F+:8D`JS@/E@3&ZQ7IR?KIB17ZG?@(G +MCQ]L`\=?_F;88$`H3@4KN.R!Q7A88+)^!7Y'?L=W6V7#6?YFVV/6:SJ[XX%E +M?E)['F;%>P=[`B>/'_-P;C/[8[MC0"N-`NJ!-KME?1)[W66E>\=[`B>/'Q`` +M@PO[8WIB0"K#`^&!,;IE>M)[NF)%>Z=[`B>/'WMZ+Z)_9_A@0"A#!2NXZ($% +M>[)X6V-E>$=XQW>H;T]^^X-9H.K@%?E)X?F;%>&=X`B6/#],!(!D?9_UE +M0"V`$N:!-KVE>-A@/'_Y<[+R_9_IB0"K-`^V!,;JE>M)]&F)% +M?0=]QW<(3J$1OV?[8^2!0"M-!2N[97T2>UUEI7M'>P(GCQ^L"'Z!^V-^9NN! +M=FXZOL5[4GZ[8V5^IWX")X\?Q4++#=]G^&!`*(X"-KCB@05^LG@I@7YFQ7AG +M>,=WURJ[TA]G^F)`*L`#,;H%>G)XVF)%>`(A@0]Y%&\LQW@98;EA0"E`!2NY +M!7D`%`T`66&[8P`6$$'$``"!0!`%EA"!Q````F@0,-!"__#!Q` +M`.!X\<"2"P__"'9(=42`*'<6(D$#)*`+":4`!8`!X`6F02U!%SA@02K!`%,A +M08$%ID`F$!8?\D#<#B$1`^EP/0UD%`)QP@M@#2IRR7#Z#Z__"G$B=P(E310- +M\*H+8`U`VLEPX@^O_T`F`19`YX(E`1#`Y>EP0"8!%G+WB@M@#:ER=0,/_^!X +M\<#/<(``M",`@!OHSW"``$0D`("9Z$(*``V)Z`O(!2"`#P```#P+&A@P,@H` +M#8GH"\@%((`/````U`L:&#`+R)"X"QH8,"X+@`71P.!^X'CQP,8*+_]G>L]U +M@``4!Z"%%25-$<"%1'EG>4,HP`9#+@\2!">/'P#_`/]#+@X6!":.'_\`_P#E +M?L"EV6''<8):F7GM`B__.&#QP'X*+_^8<$`E30//=H``%`?`AL.]\"9/$T`E +M#0+#O?`F31/G?4`ECP##O_`FSQ-3)<``%7X`AN=]9WI$>:=X0RC`!P"F9WD9 +M8<=Q@EJ9>4,LP`:1`B__.&#@>/'`(@HO_YAP0"5-`\]V@``4!\"&P[U`)0\" +MP[_P)L\3\"9-$^=]0"6/`,._\";/$U,EP``5?@"&YWU'>6=Y!WU#+<`7`*89 +M8<=QV6ZAZT,LP`8Y`B__.&#QP,H)+_^8<$`E30//=H``%`?`AL.]0"4/`L._ +M\"9-$_`FSQ-3)<``IW]`)8T`P[WP)DT3%7X`AN=]IW@%(DT`1'FD>T,HP`=E +M>1EA`*8"(8$/Y'`D0T,LP`;9`2__.&#QP&H)+_^8<$`E30//=H``%`?`AL.] +M0"4/`L._\";/$_`F31/G?4`ECP##O_`FSQ-3)<``%7X`AN=]1WEG>0=]0RW` +M%P"F&6$"(8$/G34J/D,LP`9]`2__.&#@>.'%`=O/.!X +MX'C@>.!XX'C@>.!XX'C@>.!XX'C@>.!XX'C@>.!XX'C@>.!XX'C@>.!XX'C@ +M>.!XX'C@>.!XX'@&N$4@S0#/<*``[">FH`J``-L`L7ZRX'_!Q>!X\<"2"X`" +MQ@Z`#WH,@`^`V<]PH`#0&S"@T<#@?O'`?@@/_QIP`=\`$!(!$_!:=1'P%2#` +M(Z"0`A`1`0'GUW4``/O_\']T]AD*@"\``/__SW```/O_W0B!A)T`#__/=H`` +MN!L`A@'@`*85"%$``=G/<*``R!PQH`X/H`\H<`:]@;U`*0`DI7C/<:``["<& +MH0"&0B!`@`"FW?7/<:``R!P`V!&AU_'QP,]P@`#P)0"`@>#*(<(/RB+"!\H@ +M@@\``*\3RB."#P``\P'*)"(`*`&B`,HE`@$:"```T<#@?O'`@@T`#[X,P`S1 +MP.!^X'CQP)X/S_[/<(``3!T#@,]S#P``_"B`SW"``*"PP+DV>$2`((`*NF1Z +MR;DE>L]QIP`42$VA18`!@`JZR;AD>D5X#J'/<8``<.8.B88@_P%;:,]P@``X +MB4RH3XDPB4`@$P.&(O\!0[J&(?\!3:A#N:(/H`8NJ-IPSW"``+@;`(`!X,]Q +M@`"X&P"A%0A1``'9SW"@`,@<,:`"#J`/*'#/<0@`AQ#/<*``["XC+C/<:``["<&H4T,$"`M#%$@ +MBB7$!HHFA`@B\`HAP`_K0`C30''<8``W+!"D;!]!KV!O5QZ$+I%?<]RH`#L)Z:B0I'` +MNGAZY7I0?T.1`".-`;!]!KU<>H&]$+JE>L]VH`#L)T:F(Y'`N7AY)7@0>&SQ +M0B)`((#@O@;M_T`A02#/<0@`AA#/<*``["`;PAB!_#X+@`=C`>,]R@`"`!R""86F,Z,]P +M@`#,!0&(ANA@HA8-X`5(V`/P8*+/L]6$$ +M[_ZCP/'`X<4(=03P:@G`#B'!#KE%>25X(,$5"5$`!Q2!,"+"!KD(ND5Y)7C@?Z/` +MH\'AQ4+!"12!,$/"0<`9"3,!`-@1"5(`"A2!,`D)4@`'"1(!`=@'%((P!A2# +M,!$+@``BP3!SS")"@`/T`=@AQ2$-41`*%($P(\,9"<,`"Q2",%!QS".J@(3V +M@.+*(&D`&PA1`(HAR0_/<(``5`8@H('E_]G*(2(`(:#!Q>!_H\#QP,X*S_Z^ +MP3IQ6G*(=@H@0"'H=9?'Z7!B":``T-D#V&'`!1R"-`L*$2`)W3[P)0I0(%,* +MD"`*(<`/ZW+/<```L0W+VPHD@`3A`V``2B4``)04`#$&'((S0"B!(0L<0C,' +M'`(P2+ABP$`I@""!N"5X$'D)'`(P2+D*'$(P2+UCQ=7QG!2`,$`I`2,+N`5Y +M,'A(N`<<`C"8%``Q!AQ",`;=8L!(N`D<`C!V%($PB,;)<+EA,'EJ":```-I$ +MQT7&@/'`X<6CP0#=0,5!PD+#`MJI@`D0#!`J`'ASW:``'P\J7#)<03PJ7`B;FX) +M8``!V@6%AB#X`8P@!X#V]8MVJ7#)<58)8``(VDHD`'4`V*@@P`-!*($`\"5! +M$%,@0P!.(\(``[I9>1QG(*P!X*EP`-DR"Z``!-K)<`#9)@N@``C:K0'O_J+` +MSW%%9P$C(*#/<_^&7#/<(``%`<@H``0#A`$$`\0"!`'$`P0!A#)AR +MR',>#J__2B4````@#0%#+XD0J7#)<2ERZ',*)(`!`@ZO_THE0`#`<-AP0RZ* +M$*EQ27(IAR27,*)$`"S@VO_THE +MP```($\"0RZ)`.EPR7$I#:__2B7``4%PV'!#+8H0R7%)AR27,*)$`"U@RO_THE``,`($T"0RZ)`*EPR7$IAR27,*)$`"A@RO_THE0`4` +M($T"0RZ)`*EPZ7$IAR27,*)$`"C@NO_THE@`<`($T"0RZ)`*EPR7$IAR27,*)$`"F@JO_THEP`D`($T" +M0RZ)`*EPZ7$IAR27,*)$`"^@FO_THE``P`($T"0RZ)`*EPR7$IAR27,*)$`"!@FO_THE0`X`($T"0RZ) +M`*EPZ7$IAR +M27,*)$`";@BO_XHE@0``($T"0RZ)`*EPR7$I8/;_^*)<$!`"#.`4,MAQ#)<,AQZ'))@]O_XHEP0(`($T"0RZ&`*EP +MZ7'(`&ID$O01``,A0`_``%040`85`+0$")7@@A@<(0```IO`G0!%`>'/HU0=/_@HA +MP`_K'%0L%!*`("!WI!*`$$1WG/.!^X'@(R)6X"!H8,`G(F[@)&A@P"\B*N(VXD+@+&A@PX'[@ +M>/'`X<4(=3Z(SW"``/@20(!`)0`4`[DU>5EAX@^@#`K:O@_O_ZEP,0=/_N!X +M\<"EP4'`0L$,'``Q$!Q`,<]Q@`"PAS09P`\P&0`/+!G`#B@9@`XD&4`.SW"` +M`+"'(!A`"\]P@`"PAQP8``O/<(``L(<8&,`*SW"``+"'%!B`"L]P@`"PAQ`8 +MP`C/<(``L(<,&(`(SW"``+"'"!A`",]Q@``TAX`9``A\&<`'>!F`!W090`=P +M&0`';!D`!V@9@`9D&4`&8!D`!EP9P`58&8`%5!E`!5`9``5,&<`$2!F`!$09 +M0`1`&0`$[Z'.H:VAC*$L&<`"*!F``B090`(@&0`"'!G``1@9@`$4&4`!$!D` +M`6.A:B```]@9``!J(,`"U!D``&H@@`+0&0``:B!``<@9``!J(``!Q!D``&H@ +MP`#`&0``:B"``+P9``!J($``N!D``&H@``"T&0``:B"``B:P,+DGL!F%\+@9A07RCK@9I0?P +MBK@9I07P0@J@!@#8*07`#.!X`-M@J"&H0JC@?P/@\<#6#$_^SW.``+@CSW6` +M`'P[0(4,Z0"CE.K>"2`!#]@Z#:`&"-@!V`"E#/``WL"C".JB""`!#]@&#:`& +M"-C`I0$%3_[QP(X,3_[/<(``R`4`@,]V@`#P#J"&SW>``.P.!"""#P\``.`$ +M(H$/`0```')I1'MG?:"F!"".#P```$"8=:"'`[Y$?@0@@`\```"`QWV@IP0C +M`P$&(L\`Q'VF?SUY)7@"N`0B@@\"````!'H&)X`0+R@!`$X@0@0-&I@P#PJ0 +M`<]P@`#HQPZ0)^C/<(``1`8`B,]Q@`!,'?`A`0"_$0$&4R%!@!GTSW&``$A+ +M!+@!82,*D0'/<(``^,?T($``#>C/`T1HZ[F*(,,O!/0>%I`0#8Y1(`"`HO)Y"=\` +M*PO>`O_8!ZU*)`!Q`-FH((`#*&(`(8,/@0"P$O9[!*LH8@'A+WD`JUOP(PD2 +M(0HAP`_K-,B)"!``A@2^!`+`2 +M]GD(\D2I!-D`*4$$)7@'K3WP0*D/($`$8_`M"!(DC"##K\HAP@_*(L('RB"" +M#P``+B7*(X(/``#D`LHD8@#D`^+_RB4"!+(+[__)<`B6"PB>`P*.":T#\`&. +M"*T`A3$(W@(`VD>M2B0`<<]Q@0"P$J@@P`(X8O9X!!@"!``8`@0!XD]Z`8X( +MK0*.":TL\$PA`*'*(0GR!!D"!`39`"E!!"9X!ZW=\0`9`@0`V0\A000F>`>M`8X( +MK4D"3_[QP.X)3_[/\R["PL%`.T+ +M'L!1(P#`RB`B`!_T.0I1`,]UH`#0#Q`5`Y8I"%0`SW*``!`4GW`C@J@@P`(" +MBB45#Y;!N--HV'\!X`*JYWDCHA`=V)`!V.D!3_[@>,]P@``$K^!_!H#@>,]P +M@`#PKN!^X'[@>/'`8@EO_@;:"'4H=R"PSW:``$P=`X8(X+()H`PD;0.&0"6! +M$J8)H`P&V@.&0"4!%`C@F@F@#`;:`-@!M0RU1"1!0(V`*F0"8`%,(,X`.I<<]P@0#("@Z(-QY"$S4>0A.\M@2X +MAB#^`Q2NBB#_#PNF#]@V'@(04R*`H+VVS"`BH!#R&0C1``O8%:X*<$`G`16: +M"*`,$-H'\`L(D0`*V/;QL:]Q`$_^X'CQP!((;_X&VH(D`STZ$P>4`C`@8LLXGI +M#9,!X!!X#;.%Z`Z3`>`.LU@<1#``V%H<`C`@V%L<`C`!DG3=7!P$,`*27AP$ +M,);'$?`*(<`/ZW+/<```O"'NVYAS70#O_THE```(=0#>F,>/P$P@`*"*(08" +MRB&!#P``"`&.#<__5A2!,(MPN6$P>28.[__)XP+@3>%,@3@`"V%X/ +MX`;)<0T:F#//<:``%`3*H<]R@``8!P""%+X!X`]X`*+%>`NA`866"*`,/-D` +MA8X(H`PZB`*%A@B@#":5O@Z`!,T&#_[@>"AR,0`@``#9X<7AQ@#=@.+*)(EP +MZ"`I`JYAJF#">D]Z@^H!Y0#:2'#!QN!_P<7@>.'%X<9`*0T")7U`+0,4I7LE +M"C0""'53)7Z0!O(!'5(08;K[\4$JC@#!ND(F3I`$'=`0_?4)ZB\DB7#@>*@@ +M0`$!'5(0X'C!QN!_P<7QP-8-+_X&V`T2#C8!$A`V#1H8,,]UH``4!`JE"84' +M$@\V)^@#V!"E!*7/<($`P!,:#:`-`QH8,)+9`\B0N:`80`#N"6`#`-@)A0_H +M*!4$$"05!1`>V`HAP`_K">``%-C/<(``3!T`@,00``8EN'X*X`#`N*X-8`8$V)(/X`NI<-X, +M@`N>"H`+I04/_O'`X<6AP;D(]``(=3(+[_\`VJT($`#/WQ$]@Y\/T)E(//<(``3!T#@!B(\0A0@,]R@``H +MC$APR@]O_@;90"(``KX/;_X&V0R2@;@,LL'QS0D4@<]R@``HC$`B``6B#V_^ +M!-D,DH"X#+*S\;$)T8'/<(``"`>*#V_^!]G/<(``A"2N#R`/`("E\1S8=00O +M_J'`\<#AQ<]P@``HC`R0"P@>`.H-``,%\%$@0(!4#P(#SW"``/2."X@/"%`` +M@N!`"@$$`_!6"P`$SW6``!`'`)4Q")X`SW*``$3)3!*``,]Q@`"4R<&X]"$` +M`,]Q@`"4K0&I2!*```*IV@Q@!0'8`)51(`"!K`\"#P#8]0,O_@"UX'C@?N!X +M\<#AQ0#8SW6``,S&2B1`<22%J""``@#;#R,#``LAP(`#]`'@!?!F>38/8``D +MI02%@.!`#Z$`RB!A`K$##_[@>`AS.N]6Y#0GE`#:X`B-"``KPSW*!`#`3 +M18(!X,FX(GIZ8A:XX']%>.!X\<`&"R_^F'((=<]V@`#`C_0F0!#/=X``0(]1 +M($""RB!!`,HD(G3*("(`Z"`B`O0F`A`)"EX"`>!'"!4$+;O`N\]R@0`H"K1Z +M0"N%`F"2!+V&)?@3B;T/(T,`8+(`VA9_0*=!I\.YI7D%(4,!%'Y@ML]Q@`#@ +MCQ5Y`!D``0+P@-CM`@_^X'[@>/'`9@H/_H(D`S((=EIQ2'4Z`\<`C"I<(3!>@I@#`C: +MAL#)<=(,+_[)P!\+'D`9$@"&"B'`#^MR0]B,N,]S``!$%GT"K_^X +M=0\:6(.=`0_^X'CQP/8(#_ZKP<]P@0"P$P`0$P`'R`0@@`_Q``#P0,`-S`#> +MSW6@`,@?42!`@,]P@0"P$R&``\@/\J`5`A#X%0,08GD"(E<`=A`!`2\GR"59 +M803PA!`7`>)Q.AC$!1^%#PA%`#!X+@T@!0+9`=G/<*``U`,]S@0"P$V.#""#$`,]SH`#4!S6C`!I` +M!0(B`24OHP(D`0`[H_"CSW&``)0N#1("-@"!/0B``,]PH``X+@6`!""`#\`` +M```A"(`/P````/78!;C/I1#8#J4!V!4=&)`'R`0@@`\!``#P +M++@#$@,V!+$/@\ZI`*%`$P`!`K$0BV`3`P%`*`0!P[L%(P,!9K$/J2\C2`'/ +M<(``9,A`(`0)57A)@,]Q@`#HQUMC::"D%0`0^!4"$*!P0GA%P`'8SW*@`-0+ +M$*(#P#6XP+@7N``@@@\`#@``SW"!`+`3`H`"N"O@!""`#P``_/]%>.QR`*(! +M$@(V['!`H,]P@0"P$T*`['!`J`W(%"$"`%"*['!`J.QPP+`#R)00`@#L<$"@ +M#`2X17@"\(#8['(`J@/( +M.W90B#,0@``$N@5Z['!`J`/(&G9``-[5"-Z'+?``WOJXRB:"'P```0+YN,HF@A\```("_+C* +M)H(?```!`@KJSW.``*PS4(.*)@@2`>)0HX8,@`X1\`'9SW"``)`Y(*">"6`- +M*'#/<8``*#0-@8HF"!(!X`VA!2>/DPOR80(@``#>A!(``"$(E`R3"Q]`SW*@ +M`-0'#X(0>!D2`898X.<)!(`*\,]S@``P,R2#BB$0(0'A)*.)"9\@'AI8@QT2 +M`(8'&A@P'1(`ADK`'1(!A@3((*`=$@&&(:`=$@&&(J`=$@&&(Z`=$@&&)*!6 +M)0`2'AH8@!T2`89`+P(D,'@%()4`!!("-H8A\P\`$A(!C"$,@`&"0\`6\AK8 +M%?#/<($`L!,($`0``!`%``HAP`_K"P@1(`/(<_`#P!$(G@7/<:``2`A`(@`C!_!`(@`ASW&@`$P( +M1\$#<$C`!,$"P"5X!245(`C`!^#/<8$`L!,C@00@@`\``/S_""!6``PFP*0N +M`2T`2<`6#0``!2`,2`C<0ND5X['(`H@K`0"%9,`$: +M&#`$R`,2`C8H=D'%`QH8,`0:F#`A@`"0`<4TN<"Y-'@#X$#E!""`#P``_/\= +M90T2`38&\!4B0#`.$``&`GT5(D`P#A``!N\-!9`#S,]QGP"X_QBASW"@`/Q$ +M78`$(KZ/``8``%WT&0@0(`3(4(A3(L$`AB+^`T2ZQ!B"`#"HSW"@`!0$Q*`' +MR,]QH`!(+!VASW"!`+`3`H!`(%`@$G`@!$`/(*8@!X2FHSW&``#`S!H$! +MX`:A0/`*(<`/ZW(H%`4P/-B,N,]S```;%+$#;_]*)$```!0$,$?8"B'`#^MR +MC+C/.QQ*G2$)`*1`*%`(4TP%/+/<:``U`>`&<`$`\PJ +M.QR`*+,H0'8%!D8@&H)H`X!Y0,2`C:2$@`!!!(!-@T(GP*2$0,! +M;0N>`JJXDAH$`)(1``&JN%X.8`B2&00`$-G/<*``T`\0&%B`)!`!AL]R@0#` +M%T62,'D"ND5Y#!A8@!39$!A8@,]Q@0#`%V>11I$8V1"[97H,&)B`$!A8@,]Q +M@0#`%VF12)$0NV5Z#!B8@`;PSW"!`,`7RJC/$AX/K_\!P`?P`]@3'1B0%!V8 +MDRD($"#/<*``+"`P@`7`,'`!V`#4('D// +M<8``3!TC@<]P@`!PYA"($+@R(8$/``#8`I^X@.$!V,]QH`#\1`VA +M&PL0(,]PH`#T!V`8P`3/<8``,#,#@0'@`Z'/<(``2+`DD)3AP"&&#P``DP#/ +M<*``:"SP($``SW&``*PY(($`VL]VH`#4!R5XSW&@`-0+#:%,IHH.(`T&P!D6 +M`);`X*``#@`-S)D(7@`#W2`>6),!V!0>&)`$$@$V`!8$0`<:&#$`%@5``1I8 +M,03*G.#*(L('RB""#P``W`[*(X(/``#T"N``8O_*(<(/*'"6#>`-#MD/%@"6 +M!!(!-K09!``3'EB3$(E3(,(`AB#^`T2XQ!D"`%"ISW`2(```0@T@`PT2`C8$ +MR,]QH``L(+`0``$O@63@,'#*((4/$B@(`(7WSW``*`@`!AH8,`#>#<]P@0"P +M$\"Q(H`&DAEA,'DFLJW8SW(`NP"['@E@!@6X`\@:D)H/8`8-$@$VZ0:O_:O` +M\<#AQ4\(7D//<($`L!,!@,]QH`#('Y8@00\>H1#8#J$!V!49&(!:#6`.0=@G +M"%Y#`=G/<(``D#D@H`X+(`T!V,]Q@``H-`V!`>`-H8HE"!(M\,]QH`#\1!V! +M.8$$(8*/````"`#=!_0$(+Z/``8``!CR`-WZN,HE@A\```$"^;C*)8(?```" +M`@GJSW.``*PS4(.*)0@2`>)0HX8-0`X'\`/9SW"@`!0$):"9!J_]J7#QP!H. +MC_T(=<]V@`"P#`".J,'/"!$`BW?I<,]Q@`!(/KX.K_T@V@'8`*X`V(^X"QH< +M,`#8%1H",,]V@````*"V#PV!'P``_LH'P("X1\#/<*``K"\:@%(@```3"!X` +M`9:`N`&V!\"!N$?`SW"``$!+H(A:">`#JJ[/<4-UJ!)`P8HA&@I!P2N.!*8- +M'$(S1L!CP<]Q@`!P-43!SW&``-PTSW6``-0%`(5%P8#@RB#!`\HA(0C*(F$` +MRB-A#R@-80O`*^$%`(6`X,H@(0)0#:$$RB%A``+9SW"``)@C)*"E!:_]J,#Q +MP#8-K_TO)`<`SW.``$P=*'>$Z"&#`_`@@\01`08EN?`C#0#`N8#GHJ.CH\PA +M(8`!VN<]PH`#\1"&@X'C! +MH-RE@.>P"6(`RB!B`&H*``2%Z&X-P`P#\*(-P`S/=8``A`T`C8;HZ@T`#`'8 +M`*VY!(_]\ES@VO_0X@@``( +MI<2U$(?%M0:EA02O_0B%X'CQP,]Q@`#,Q@`1!`"X<,]R@`#$*$`L@``5>!4@ +M0`$P(@8`&0X1`0HAP`_K_Q\<#/!T)(P`&L@2*#0A1 +M``6*@>`!V`/R`-B`X'@(`@#1P.!^SW"``$3)()!$(0`#8P@1`@#;SW*``,S& +M9:($@J"X!*(]"9\!!)+/<8``),/'`>@J/_>X/S__/<(``^,9@ +M@,]R@`#,QJB`8*+/=H``%"D$@JBB`-G`AJ"X):($HB,.41""X\PCXH`2],]P +M@`"<+".@!.V6#\__"O!*"@``!O`!VV"B***@N`2BB0*/_?'`&@JO_0#8SW&@ +M`"P@4('/=H``S,8DCL]U@`#XQ@BE"PE1`"6."0E0``'8HN@JAASI!H9^"Z_] +M#B"``,]Q```0)R4)!0#/<8$`,!,E@9DAS0H9"$4`!?#/<```$"<(I0+8!_`` +MV`?P"8;XZ`'8`*4!V!4"C_W@>,]R@`#,QB2"#R$!`"2BM08@``G8\<"*"8_] +MSW:@`"P@$(;/=8``S,8'I<]P@`"\-]8.8`P`WP`5!1`G#5``3"6`@,PEXH`\ +M\@HAP`_K`! +MV`/R`-@%Z`H.S_]!\,]P``"($PBE3@[/_SOP!)6:Z"65"(6!X<`@@0\``(@3 +M`_(;>,]Q@0`P$R6!"*69(@$C0T(40`%C8'@`=@#\@#8!>B:#<__!_#/<```B!,(I=X- +MS_\$E06UY+40AM4`K_T&I?'`SW&``$2,08'/<8$`,!,E@04IO@`P<,H@3@`, +M(0#PSW$``!`GW@FO_/'`X<4`V,]S@`#,Q@"C +MSW6@`"P@$(4!V<]R@`#XQ@:C$(4@H@:BSW"``"PL`X@DJXP@@X8DJ@3R):HE +MJ^X+(``#V&$`C_W@>`'9SW"``,S&X'\@H,]P@`!,'0.`SW&D`!Q`"(#`N!-X +MP;@2H>!^X'CAQ0#:2B0`=,]U@`!`C\]S@`"XCTAPJ"```T`C`0(4>4"Q%B4! +M$$"A0:$!X$HDP',`V:@@0`+/<($`*`HT>$"P`>'/<(``F`9!H,]P@``HC$RP +MX'_!Q>!X!?!"><=P0````,]R@0`P$T6"\PI$@%,@0P5P<<`@C0]`````P""- +M`.!_(G@&\&)Y`B"`#T````#/`5A+;W` +MO0/P_]WJ#<`#">C/<(``E"`(B(?@`M@"\@#8SW&``"26=XG/`1H07P$($!X!"AV@G/_<]V@``0!R"6$PE>`)H*@`X`EJ&X$'D` +MMA\)G@&N"H`.SW&``)BM"Y$!X!!X"[$`EJ:X`+8>#<`##^C/<(``E"`(B(C@ +MS"5AD`?T4@Y@#`'8(@S`!(PEPY]&\B,($"#/<8``#!0`@0OH`-@`H<]Q@`!$ +M)`"!HKCV":`(`*&:"P`,SW&!`#`3!H%%($`!!J'/=H``](X+CE$@P(`<#X+] +M"XY1(("`L`M"`SX)``.V#,`#@."T"B(`RB`B!@;M`(?$$``&(0A?`<]Q@``0 +MR02)"N@#B8#@?`WA"LH@X0"."B``%=C!!4_]X<7/<8``$!0`B0';8:DDZ,]P +MH`"P'WF@SW"``,`;"("C@6"``H$`VC$-`1#/<(``*!0`B(/H`=@*\`&!`B,- +M`/<-A9],`$!+0:E(<`<(40!AH4*IX'_!Q:*A[_&`X`'8PB`,`,]R@``0%`"J +M`=@!J@#8`JH!H@*B`Z+@?R2BX'CQP.'%"'43"#0$F'(.V.8*+_\`VH/H$]TL +M\,]R@``DEDAP-@CO_0S9SW&``!`4`(D.Z,]P@`!$R0"0AB#\`(P@`H`&]`62 +M9))G>`.A0B4`$QX*(`6(<0HE`)`,],]P@`!$R0"0AB#\`(P@`H`4#\'_Y01O +M_:EPX'CQP)APN'&;^RB'&#P#:SW&``/2(GKH5(4$!`($! +M*@(!1G@*#J`&`*'1P.!^G0?O_P79X'CQP.'%`-W/<(``](B:#2__'-D;V*8( +M(``%V4HD`'?/<8``,!2H(,`"%B%``P00!0"8=0\-01%`)$T`.01/_0HAP`_K +M_^4]O@>/'`SW"``/2(&!`%`"\L00%,)`"'RB+&!\H@A@\``.(. +MRB.&#P``JP#0!.;^RB'&#\]P@``P%!8@``$`@$!XT<#@?N!X\<#AQ<]P`P!` +M#<]UH`#('T4=&!"J#\__@-@5'1B0P0-/_>!X\<"8<+AQG.#*(L8'RB"&#P`` +MXP[*(X8/``!C`'0$YO[*(<8/3"6`@!X +MX'[@>.!^X'C@?N!XX'\!V.!^X'C@?N!XX'\`V.!^X'C@?N!XX'[@>.!^X'C@ +M?N!XSW&``+0R$H$!X!*A#/'`9@I/_9@0`@`$(H$/````"#MY!"*# +M#P```!`E>\]Q@`!,':2!5B5.%%8E#Q68$($`%0I>`H8A_P-$N2]GB;_I<1GP +M42(`@KP5`A$,\L*Y@"4"&3]EZ(\]93"-97_P?T5Y"?##N3QY/V8^9C".Z(]% +M>8@8P`-E>54";_V,&$``\<#AQ0/(I!`!`)@0`@!1(0"`&N""`"`-JL:/(,@`S/B!<]P@0"P$Z&`!/""$0T!#6"1!/""$`,!SW&``$P=)(&`$`T!5!$!`3UE +MNV.$$`T!NV.`$`T!N6%^$`T!0GTG\$,(D0`#$@TV#EQ#.!X\<"R#P_] +MSW&``$P=\"$"`%8B100(@E8B!`51(,"`BB`(`,H@(0"\&@0`2B0`<@#9J"!` +M#\]U@`",2_R*+F7D?B\H@0-.((,'SW"``'1-;V``)4,`X*M$$H\`Y'XO+H$3 +M3B:/%^Y@R*O(@B$.WA`=BH;ATR"F`"\H`0!.((T'SW"``.Q*J&`1\,]V@`"T +M2RYFSF6\BL1]6!*.`,1]+RU!$TXECA?(8!"K`>%*)`!R`-NH(,`/W(K/<8`` +M4$UO8<]U@`!T3>1^+RB!`TX@CP?O90`EP`#\J$02CP#D?B\N@1-.)H\7[F4D +M&((#R((?#MX0/8J`X],AH0`O*4$`3B&-!\]Q@`#L2JEA$/`$Z\EK`_!H=LYA +M/(K$>5@2C@#$>2\I00!.(8X'R64L&$(``>-*)`!Q`-BH(``%SW&``.A*?8H) +M80`D#``!X&1Y+RE!`$XA@P?/<8``[$II82"LO08/_>'%X<;/'&F'#/\5[>:)X@J1[!"%!@V5X +M&*+?]<'&X'_!Q>!X\<`B#0_].G`%@:"!RK@0N,J]!24-D`&!)H'*N,JY$+D% +M(1```=X9\@0E@),3\B\H`0!.((('\"&!(`#?#R>/$`CI!"<`%$(@`(!@>`1_!R>/DT''%O2Q#A$0^@\@!`K8I0@0``HAP`_K0S8!A0!,1CI'A0`,0L@0(`-\L]P@`!,)&"`SW$``&!< +M#=A@>P3:"/"(Z,]P@`!()""`8'D-V`LG@),%\C()[_\*V`?PA^X"".__"MA6 +M"```W*4(W%\$+_VCP/'`\@LO_0#:SW.``!`5.X.Z@P#>#R8.$*1Y!"9`$$(@ +M`(#*(&(`+R8'\`'=RB"!``?R'(,D>/(.[__%>*EP(00/_>!X\<#AQ:'!`=A` +MP,]U@``0%0J%&P@>`(MP!-EGVCW;6@O@"A>["H6@N`JE^0,O_:'`\/'`0@L/_:;!*'4:0#850,O_:;`X'CQ +MP.8*+_T!V:+!SW6``!`5&H5;A01Z'(4$()"`+?(#\#MY!"!`H/[S+R@!`$X@ +MD0=<'4`4%25-%!V%@.#*(<$/RB+!!\H@@0\``(\3RB.!#P``'`+*)`$$Y`.A +M_LHE000F",__'85`>-X/C_\`V`\@0`0&(!`@L@WO_PIPR0(O_:+`X'C@?N!X +M`=G/<(``;";@?SB@X'[@>/'`H@J``\]P`0"00`GHSW&``!`5N!D``!N!D;@; +MH<]P`0`(0`CHSW&``!`5'J$;@8&X&Z'/<```Q%X*Z,]Q@``0%909```;@8BX +M&Z'/<```R%X*Z,]Q@``0%9@9```;@8FX&Z'/<```U%X*Z,]Q@``0%9P9```; +M@8JX&Z'/<`$`X$@*Z,]Q@``0%=@9```;@9FX&Z'1P.!^\<#AQ:'!SW*``*"P +MSW6``!`5%X4`V0\A`0`8A21X0B``@,H@8@`!VP#9(PA1``C88,`!'$(P`AS" +M,`,4!YM,.%)$!Y[L/U)$J<,]RK=[OOIH)X`$0V3(/(``J[[Z&">`!*G`*#^__*G"#X,H@(@`1`0_]\<"R""_]`]JFP1IPQ@E@"X/! +M`\'/<(``O`X4%`[[Y"P`3""G"`VR()X`&8<\X)(``*<'T(T``#P\]P@`#<#D*%\"#!`,"E +M#!40$,&E".G/=X``Y`[P)\`0ANC`I<&E`-D9\(0J#`.2"&``+W`.(($/```` +M`2"E`\"$*`PC\"8PA!XW#]\&E`-A!`"_]IL#@>/'`V@_O_`3:IL'N"&`+BW'/<```&](` +MW:EQ^@M@`*ER`,'/<```'-+J"V``J7(`P<]P@``4#0'"%2!!``"1`L$%NK8, +M8`!%>0/`@.#:``4`SW:``&`'TM@(N!G9M@M@``#:SW```"+20"8!$O8)8``$ +MVL]P```CTD`F`1/F"6```-K/<```(-*$P=H)8```VH7'SW```"'2Z7'*"6`` +M`-H"AA?9.@_@"D`F`A(#AA?9+@_@"D`F`A,$P!?9(@_@"H3"!<`7V1H/X`KI +M<@*&`-ER#R``B[D"I@.&`-EF#R``B[D#I@3``-D(N%H/(`"+N0AW!<``V0BX +M2@\@`(NY(H8Q>1GA!2E^`".&+W)0=S%Y&>$%*7X`+W',($6`A?<#P`'E-PA% +M@P/`#PA%`P'9SW"``&`')*``V`D'[_RFP.!X\<">#N_\"=JIP0AVJ@\@"XMQ +MU@EO_2'`"'%"V)H+8``%N0P4!#``P6X*8`"I<@X- +MH`T%V"`4!#``P#>_\`MJGP9IP#@\@"X/!SW"``#@_`(`'V47`SW```!'2$@I@``#: +MSW```!+2`-D&"F```-K/<```$](`V?8)8```VL]P```4T@#9Z@E@``#:SW`` +M``%$!]G:"6```-K/<*``M`]P$!<``=G/<*``M`\\H&8,H`T%V+S8G@I@``#9 +MP]B6"F```-F*($0(B@I@``#9BB`$"H(*8```V27%M=AV"F``J7&*((0&;@I@ +M`*EQ`]A`P`3>0<;/=ZW>[[Y"QXIP!,$#PA[;F'-*)0``2B8``.(-H`%*)P`` +MB@[O_XIP@^#9\L]U@`!@!P@5%A`,%1,0#MA`P$'&0L>*<`3!`\(>VYAS2B4` +M`$HF``"F#:`!2B<``$X.[_^*<(/@N_((%1`0#!45$`[80,!!QD+'BG`$P0/" +MX=N8P"C`B%`)<]S@`#D#E5[`*/# +MVU5^8*;/=H``U`Y5?F"F(?0R"4`#"B'`#^MR$.C/<*``_$1T$`0`9!`%`,]P +M``"Q$UT%;_Z*(TD*SW```*T3BB.)"DHD``!)!6_^"B4``9SH\@A``PHAP`_K +MA(,(``O<`3"`B`!(,]P@`"\#E5X(*`")<`DN6`" +M(4&$$/(")4(D#'KJ"R``+W`$P@(E`2#/<(``Q`Y5>""@`-B1`^_\I\#QP(H- +M(```V,]P```-T@#9B@\@``#:SW````S2`-E^#R```-K/<```%=+/#R```-H"V(ZX`-D2 +M#R```-H!V(ZXSW$``/__`@\@``#:SW````O2`-GR#B```-K/<```#=(!V>8. +M(```VL]P```2T@#9U@X@``#:SW```!/2`-G*#B```-K/<```%-(`V;H.(``` +MV@#8T<#@?O'`>@K/_*/!BW$!W88+(`NI83@S"!B@=H.(``/V`#8)0+O_*/`\<#AQ:'!BW'""B`+`=K/=8`` +M+(L`%`0PSW"``+@,0"4!'Q+:;@T@``#;`!0$,,]P@`"T#*EQ`=I:#2```MO/ +M<(``W`PD;1S:7@T@``##`-C5`>_\H<#@>/'`/@GO_`/:H\&Z<&8*(`N+<0'! +MSW"``&0-`-_T($X``L'/<(``?`V`YO0@5`#/<(``8`?@H.&@S":BD,PF8I', +M)J*1RB7"$P+T`-V!YLPFXI#,)N*1S"8BD@/T`=V$YLPF8I+,)J*2S";BD@+T +M`MV&#<__JG#/[[Z2":`!R7%B#N__JG#M"-```,"`X,P@HH%0](#FS"9B +MD,PF(I%*]`+`D0@1`,]P@`"\#K5X6G#@H,]P@`#$#K5X>G#@H,]P@`#<#K5X +M&G#@H,]P@`#D#K5X.G#@H,]P@`#,#K5XX*#/<(``U`ZU>."@JG#)<<]SK=[O +MOAH)H`&I`RAJG"I<[[[."*`!BG-:#Z__JG`I +M"-```,#/<8``8`=`@02^!KC88!4@``7'<(``W+`A@4*P([``V!D`[_RCP.!X +M\<"DP8MQ\@@@"P3:`,`!P02X-7C/<8``'`T08=H,(``"P0#``<$$N#5XSW&` +M`#P-$&'&#"```\$`V*3`T<#@?O'`H<&&"N`!BW(`P*'`T<#@?N!XH<'AQ>'& +MN'#/<(``L+D0$`8`SW"``/`E!8"8<:'!AB3W#^<($`#/<(``_#D`@!\(@0'/ +M<(``!#H`@!,(00'/<(```#H`@,,(``$`'$`Q(,(!%($P\-Y3(L``Q'I3(<<` +M)'Y4>D`NC0&T?;IB%7K/<8``H+)(8=1^"'.&(_T/>WLZ8D&*97A(]UE%27-$;YAPHYE>LESAB/]#WM[N6$CB65^*'.&(_T/>WME>2<,$`#/=:H` +MX`=SA1$+'@!(I0FE*J7+I1#P"*5)I!_H<#QP,]Q +M`((!`,]PH`"L+SR@SW"```@P`("+Z,]P@``0%P"`#PB0`%H+``/1P.!^-@E` +M`)X+X`1OV(?H&@U@#0K8(@E``//Q\_'/.!_`*+@>`0H@`\``"^Z0BG"=%!Z1"K^`@(@0`X0>`/H`>)0>@L(,P%` +ML8/H`-@"\(#8X'[@>(D'3_[QP-(-C_PZ<,]U@`"X&P"%`>``I14(40`!V<]P +MH`#('#&@E@Q@#2APX@N@!`?8&G#/=J``["?KAC8+H`8J<`NF`(5"($"``*4& +M],]QH`#('`#8$:'""*`$"G#9!:_\Z7#QP&X-C_PZ<"AU&G*>"Z`$!]A:<`\( +MGB`^"V`'R-A0()`@3""`H!GR"/8C"!`@10A1(!78$[@-\"4($"0U"!$H=@R@ +M`RIP`*4/\"G8$KCP($`$`*4)\"O8$KC[\<]PH`#L)QF``*52"*`$2G!=!8_\ +M"B'`#^MRSW```(H3>]L*)$`$208O_@HE``3@>/'`Y@R/_`AW.G$:`$Q4A@2-:#^__"G)AO?$-=9`!YAT%C_S@>/'`N@R/_*'!"'<:<2,* +M=```WDAU]">`$QX((`"+<0#`%"",(V&]`+3M#760`>;Q!*_\H<#QP(8,C_RA +MP1IPSW:``+@;`(8!X"AU`*85"%$``=G/<*``R!PQH$(+8`TH<(X*H`0'V`AW +ME@H@`[/8%NB+<4(+K_T*<``4`#$`I0"&0B!`@`"F!_0`V<]PH`#('#&@:@]@ +M!.EPA02O_*'`X'CQP`T,W@`N#\__!/#2"```T<#@?O'`#0O>`$H/S_\$\.X( +M``#1P.!^\<#Z"Z_\"'&.X`'8PB`-``#:SW:@`+0/O(9YH*8.8`1*<+$#C_P*(<`/ZW+/<```B1-*VPHD0`2=!"_^ +M"B4`!.!X\<`Z"X_\"'2'7T)X`3\"&!(UX/[_\*`$QH((`#T(($C8;WS#760 +M`>9-`X_\X'CQP-X*C_P:<,]V@`"X&P"&`>`H=0"F%PA1``'9SW"@`,@<,:"> +M"6`-*'#N"*`$!]@Z.!X\`7*)SW:!`,@*\FWV?^9F-,H($84`22#``!$.GA7/=H$`"`VV?L&.`_`` +MWL=P@0`(#;9X!(@((P,`"".#`P`C0`%)(,,#%FUU>,]S@0"(#@-CSW"!``@. +MMGC/=8``3!VDA;B%`8"E>`0@@`\````(!GL"\&.!Z+N8&<```-T)\J01```` +MW9>]D;B4N*09```[#!X`SW"``$P=Q(#`NLB&!":.'P!````^OA[FV'I%>Y@9 +MP``="YX'I!$``(4E`12,N)&XI!D``)P90`,>\"<+W@>D$0(`A24!%):]F+V- +MNI&ZI!F``)P90`,D@!"!GK@0H0KPE+V6O9P90`,D@!"!GKB?N!"AF0&/_/'` +M*@FO_`/8SW:``&`D((9`>8#@;?(@AF!Y!-C3"!``((9@>0#89[@5"!4#,R8` +M<(``-#Q`)P%R%'D`>0#80O#/<(``:"0@@&!Y`=B`X`'8P'@X\,]U@`!H)""% +M8'D!V",(4``@A6!Y`=@;"-``((5@>0'8#PB0`""%8'D!V,$(48`!V![PSW"` +M`&@D((!@>0'8A>`!V,!X%/#/<(``:"0@@&!Y`=B!X`'8P'@*\,]P@`!H)""` +M8'D!V(/@`=C`>"\(4``@ANMU8'D`V!IPSW"``&@D((!@>0'8N'`WV`HAP`^I +M`,]P@```EB`0 +M@``)"%$``]@A\`+8`*(?\,]P@```EB`0@``M"%$`SW"``"('8(C/<(``4,N$ +M*Q\`,"!`#@KHSW"``"0'`("&(#F/"-@#\@'8`*(`V`"AT<#@?O'`SW"``%@' +M`!`$`,]Q@`!F]Y"+G_V`BX +M9'@HN`5Y17D(W?0D@`,G>$3`9@N@"Q`4`#$2%`(Q8;U`*`$$!7E'>43!$!0" +M,10D@#-`L-<-=9`!YE,EP@5`IP`4#0$'V0?P$'T4)TP0`+1AN10D0#"[>T^] +M`)"E>W![Z0FU@'A@!""`#P```/\0N`5Z0*?U!6_\I<#@>/'`0@A@!@#8E@BO +M_0#8SW"``.@X:@F/_<]P@`#(.&()C_VN#<_^V@P`"`#8_@G@`H#99@E`"U8- +M0`(N#(`+(@F``<(,@`(`V'8*+_\(<8X.0`HR#8`"\@M@`?_8B@A``8H@A0\( +M<5X/X`0(.!^X'CQP.'%`-W/<(``G`:@H,]P@``H +MC*RP*@N@"ZEP6@]/_;8*(`NI<%()@`/:#@_^#@A``8H@!@H(<=X.X`0(``IP\(40`!V%$>&)#^"@`-`=G/<*``["/'`X<6DP8MP_@F@#`39SW6``+@; +M`(4!X`"E%PA1``'9SW"@`,@<,:"N"B`-*'``A4(@0(``I0?T`-G/<*``R!PQ +MH+8-P``M!&_\I,#QP*'!BW"V":`,`=F>#<``H<#1P.!^X'CQP*'!BW!*":`, +M!-D`P%$@0("@"V(&RB"B``#`42"`@`0-P@H`P%$@P(!<#8(&`,!1(`"!1`J" +M!MH/H`L!V,]Q@*[@`>QP(*`!R.QQ`*'/`0<2!38*(<`/ZW**(,4`J0/O_2?;@@Y@ +M`4#82@S``(8)``>AP-'`X'[@>/'`0@I/_,]U@`!\&P*%(X4!WA!QP'ZI<#(( +MH`P#V1X,P``$[@*%`_``A84";_P#I?'`X<7/=8``V`6I<`X(H`P"V?H+P``@ +MA0OISW"@`"P@4(#/<(``X#6^#R`+66%9`D_\X'CQP.'%SW6``)0;J7"&#V`, +M$-D`%000(0Q0`$$,T``I#!`!"B'`#^MRC]B-N)C;_0+O_;AS`84,N`0@@`\! +M``#P`:4,\"&%SW"``,@O(*`CA<]P@``'%""H`\S7<````$`!V,(@"@`7N,=P +M``X``(.XG;B?N.QQ`*$!$@$V['`@H)H+X``!V,T!3_S@>/'``-C/<8``I!L` +MH0&A`J'/<-#^```$H0`6`$``%@!``!8`0``6`$`#S-=P````0`'8PB`*`!>X +MQW``#@``@[B=N)^X['$`H0$2`3;L<""@/@O@``+87@U``M'`X'[@>/'``!8" +M0*'!0,(!%(`P#P@>`,]Q@`"PBP3PSW&``#2M0*%@B0':"/``%@!`%2&,``"D +M`>)]>/4(A8`7"QX``!8`00/P`-@5(8P``*0!XOD*5($#S-=P````0`'8PB`* +M`!>XQW``#@``@[B=N)^X['(`H@$2`C;L<$"@T@K@``*)H<#1P.!^X'CQP.'% +MSW6``'0'J7`2#F`,"-D`A<]QH`"X'@*A`84#H4(*P`"Y`$_\.0+``/'`I,&+ +M<.X-8`P0V0/,UW````!``=C"(`H`%[C'<``.``"#N)VXG[CL<0"A`1(!-NQP +M(*``P%$@`(`#P`;T`L'F#N```-H%\+8.(`(!P2X*P`"DP-'`X'X)````!0`` +M`/'`U@G``!D`P`K@>/'`X<6TP8MUJ7#2#6`,%-D`P(;@S"#B@0;T4@O@`JEP +M"'$C\`\(D0#>"^`"J7`(<1OP$0A1`%X-X`*I<`AQ%?"#X,P@(H('](X*X`*I +M<`AQ"_`1"!$!>@O@`JEP"'$%\#L(40(!V0/,UW````!``=C"(`H`%[C'<``. +M``"#N)VXG[CL<@"B`1("-NQP0*""">``*'"Q!R_\M,`*(<`/ZW)\V(VX=]N+ +MNTHD``!Q`._]"B4``?'`X<6BP8MUJ7`>#6`,`MD>#>`"J7`""<``>0`"4!Z7]RB4E``.&&0C>`#X)@`4(<<]P@`!,-P(,``L"V`/P`=B1!B_\!*7Q +MP!H.#_P`%@1`SW6``#2;=!T`$8PD`8G*("V`,"MD!YAV%SW[Q#@20O@^` +M`"4&#_S/<(``3*NI!J``BB$?!.!X\<"N#0_\`!8$0,]U@`#`F50=`!%,)("" +MRB'-#\HBS0?*((T/``#]*LHCC0\``*H`R`:M_T.!)!2#X``P04/_.!XSW"!`&0:(8#/<(``3*O,&$`` +M+0:@`-39\<`N#2_\!-FCP0#>0L;>"V`,BW`#S-=P````0`'8PB`*`!>X`""! +M#P`.```&%``Q&W@3X`0@@`\``/S_)7B=N)^X['$`H0$2`3;L<""@`,'L<""@ +M!!0!,>QP(+`&%`$Q['`@L`84!#$;#!X``1(%-@HAP`_K$+`SW"@`"P@0!`0`,"]`>4#\`'F!A0`,8$.`Q`$%``Q +M@L.EQ"@F@`ZER['$`J004`#'I<1MX`>`0>/((H`.I/'`SW"``-0OM@A@#"C9]@R``-'`X'[@ +M>/'`X<4`%@!`SW6``!`7`*4/")$``-G/<)\`N/\]H,X,@``@A4$)50$S)D%P +M@`!4/$`G`'(T>`!X>@H@`U38*0A>`,]Q@``(,`"!@;A^#J`,`*$*\+X/K_X! +MV.(*``,$\#(/``0!`P_\\<#:"T`(?@R``-'`X'[@>/'`Y@K@"`#8SW"``$P= +MR!`!!L"Y@>$!V@J@##P0@`#1P.!^X'CQP.'%SW6``$P=`(7$$``&'0A> +M`0HAP`_K/'`HL&+<%(/(`P(V0#`SW&``$@P`*$( +MZ`84`#$#L004`#$"L78+@`"BP-'`X'[@>/'`9@DO_('8H<%@P`/,`-_/=H`` +M6`<"'`0PSW"@`"P@0!`1``"&`1S",Q#HSW&``"0'`(&!N`"ASW&``%0P`X$! +MX`.A`=@#\`+8&G``P`(/;_P*<<]U@`!4,`,2`3=>E8'88(:Z#Z`,"B0`!,]P +MH``L(!"`0!U`%!&E2!T`%)<(D"``AB<($0+/<(``G#A*#L`*W@RO_A380@E@ +M!`38X*;/<(``7`?@H`#8"PA0`0#8"/#/<(``7`<`@/<($8$!V"\F!_`-\@(, +M(`,4V(GHSW"``(`X)H`C@2"!;@[`"@"&`^@`V`?PSW"``%P'`(#[Z`'8+R8' +M\`7TL@Y``@OHSW"``"0'`(`O*`$`!@OO_4X@P`>U`"_\H<#QP$X(+_R`V*'! +M`Q(!-V#`SW.``%@'8(//=8``5#`"'$0P+Z4H``"(' +M(*^I<(0I'P``(8%_@`!`RZ8(8`J_VF"/"B&`+X``7,N$*Q\``"&"?X``0,L% +MDH8@?PP<>%,@@(`(],]Q@``D!P"!AK@`H0**SPA?`(0K'P``(8!_@``\SOH, +M(`P8V0"/A"@?`"]P-"$-($(E!!:,)`>!S/<*(<`/ZW+/<```@2>8VV4`K_V* +M)0`"9`'@"A&PT0``HA +MP`_K`$I<]PH`#4`QR0G@D``0#`2@MO_`+9`Q(!-\]P@`!8!V"`@-@H0#80"2/,",($`,@A6!Y`-@;"!`$((5@>0#8#PA0!""%8'D` +MV`\(D03I<,EQ&-H$\.EPR7$NVI8,``H!V&`>`A`7AH#@T`IA_,H@(0``%``Q +M*0A1`$`D@##/=8``-"=`)8$;:@P@"B[:`=@WA6$=`A"!X:`*0?SJ#4``403O +M^ZS`X'CQP-8+[_L7V;?!Y@D@#(MP(\!*(D`@4R#0`(8@_@-"*!$!)0@R)`P< +M`C0*(<`/ZW)RV(VXBB,/`PHD@`3U!&_]"B4`!$@4!3`@P$`HCB#/=8$`R`K6 +M?E$@`(#`94$M3P/`O[YFAB#W#USTC>@*(<`/ZW)SV(VXBB//!+D$;_T*)``$ +MBB!/!0IQE@U@!*AR`<`"P0IR+@TO_&9N?P@0`.EPU@Y@#`IQ#12`,(4@P0`- +M'`(PBB#_#U/``(:IN`"F$L"&(/L/*+@/KDHD`'0`V*@@``/_VKM@0"B!(#9Y +M$N,[8T"K`>`*<,(-8`R+<<]P@`!,'?`@P0/`$0`&#R``!,`9&``/C@\(40"` +MY\P@HJ-P#8(,`=\"\`+?I@[@`0IP!_"`X,HG@13*)R(2@>>X]""&SW"``$P= +M`X`8B"AUAB7['R$(4`"J"4`"((89Z,]P@`"4(`B()PC1`4$I0`,?"!X`$\`2 +MPA<('@*&(OL/02H$`D^."PH``:BX4\`3P!+"!GE$>"5X`*:&(/L/"^V`X,H@ +M`03*(2$`P`TA`\HBX0,.'D(4`-C/<8$`"`X6(0$$0(8`H0&A"PI?!0#8B[@! +MH0\*G@4!@44@``8!H:8.;_V+<`T4@#`_"%X!6!0`,06V6A0`,0:V!987Z`X) +M0`(.Z`:6$PA>`.H*;_T*<(X,@`P%V!*N`-@%M@?P"G``V4H-(`,/V@T4@#`U +M"%X`4!0`,0*V%.@`W1#8.G`"EA$@0(/*(`($RB%"`R`-(@/*(D(#0B%`(.<( +M=8`!Y0T4@#`/"!X!"G#."^``512!,`T4@#`["-X`-<%6%`(Q"G!V#J_]$L., +M(`*`N'`-]`HAP`_K"V``Z7!I +M`>_[M\#QP/X([_N*(%,)I,$`W:EQ/@M@!*ERSW:!`(@2`(Y*)$`@H:X"'@(5 +M`>``KJ.NH::BIJ2FI::XKKFN`<"ZK@+!!Z8#P"BF"::!P-8.X`L!V0'`!Z9Z +M=8GP@L#&#N`+`MD!C@/!`=_CK@'@`:X"P"FF"*;B#2_\BW($(``%+R0'H`+9 +M(ZX"K@#!(:9M\A)I%GC/0`A@@^!`,@*BB$(`**R(*(&V2.N`-E^ +M"R`##]H`PH#9$FH6>,=P@0#("BBH*:@'V`.NSW"``$P=\"``!,]S@0`(#E9[ +MP!`!!@0A@03`&%@``-D@H\]P@0`H"B&C5'AJ"J`,H+`'Z%H*@`P(V`.N^JY` +M(U,@(/'`BB!5"P#9F@E@!"AR$@T` +M"E()0`#1P.!^X'CQP.'%`!8-0`/,`=K7<````$`!R,(BB@`7NL=R``X``-X- +M(`I3)0$0425`D,]Q@`"\,0'8RB`A`(D'K_L`H>!X\<"AP8MP$@W@"P'9`!0% +M,!D-$0`*(<`/ZW*)V(VX1=LY`&_]2B1``,]Q@`#8QP,90@%`+8`#`J%*),!P +M`-JH((`"`-@/((``"R!`@0/T`>(#\`ZX`:&R"$``H<#1P.!^\<"2#H_[`!82 +M00`6`$'/<8$`R`I`*H`@%G@P(04`HL%!+4`#(PHT)%,@$P`*(<`/ZW)UV(VX +MBB,8`DHD0`"]!R_]2B4``!T-7@(*(<`/ZW)VV(VXBB-8`J$'+_T*)(`$SW"! +M``@-%B"`!!IP2@S@"P+9SW"!`*@)%B"`!#H,X`L"V4`JE2$`)8`O@0"(#BH, +MX`L0V8MP(@S@"P'9`"6`+X$`B`[F#B`&$-D!$(`@(0@2!`HAP`_K0;8 +M+PB$`\]P@`!,'?`@P`3#$``&`=D$(+Z/``8```0D@"\````(PB%!`"NX"PD% +M``#8`O`!V`]X!/`!W^EP!"2!+P$``,`NN<]R@``T5"EB,'4"$(`@SW&``'!*"&$Y"%``"B'`#^MR>=B-N(HC&0`Y +M\0HAP`_/<(``3!WP(,`$ZW**(U@/PQ`$!GC8C;@1!B_]"B4`!0,0@"`(81<( +MD``*(<`/ZW)ZV(VXBB.9`AGQS@E@#$IPSW"!`*@)%B"`!""0SW(``!@5"2&! +M`((.(``@L+4$K_NBP.!X\<``%H%`SW"``,PY(*@`%H1``!:!0,]P@`#5.2"H +M`!:`0%`DOH'*(<(/RB+"!\H@@@\``-H4RB."#P``@0>(!2+]RB4B`,]P@``P +M!@"0!NCR"@`,$@H`#!H.``#1P.!^(0:@"P#8X'CQP!(,K_L`V4HD`'*H($`" +M`!8"0!4B0#`.&)@``>$`%@U``!8.0+H*P`O/<*``%`2LH,]PH`#4"]R@T@T` +M`$$$C_O@>/'`Q@NO^PC9HL$!$@XVSW6@`#@N'!40$&H)X`N+<``4!#``WP0D +MOH_P_P``RB'"#\HBP@?*(((/``"F*,HC@@\``.$&U`0B_E +MY@^@"X*Y'!T`%#X-(``!&I@SF0.O^Z+`\<``V)H,(``$$H$P!!*%,`HAP`_K +M/'`X<6AP1_=BW`.">`+!-EAO?D-59#R#``` +M:0.O^Z'`\<"IP8MPG@G@"Q+9V@P``*G`T<#@?N!X`!8`0``6`$``%H!``!:` +M0``6`$$`%H!``!:`0``6@$``%H!``!8`00`6`$$`%@!`G00``/'`X<6AP0O= +MBW"B".`+!-EAO?D-59"&#```_0*O^Z'`\<"MP8MPA@C@"PW9;@P``*W`T<#@ +M?N!X<02@"P'8X'C@?N!X\<#/<($`#!E>".`+!ME*#```T<#@?N!X\$U`H_[\<#AQ<]U@```>*EPR@^@"Q/9M@L` +M`&X(H`FI<`'9SW"``.`%'0*O^R&HX'CQP*()K_L!V:'!H@^@"XMP`,#/=8`` +MH'L`%040H'`0>)3@RB')#\HBR0?*((D/``"X(;0?@#`ZPX$D#H+```2#R`!`!0`,0"% +M`,$X8`"EF0&O^Z'`\<#/<(``X`4.#Z`+`=D2"P``T<#@?N!X\<#/<(``5'GV +M#J`+2MGZ"@``3@N`"M'`X'[@>/'`SW"``$QX\@Z@"T+9W@H``-'`X'[@>/'` +MSW"``*!YP@Z@"]#9Q@H``-'`X'[@>/'`H<$`V4#!`!8"0``6`$`U"E```\S7 +M<````$`!V,(@"@`7N,=P``X``$4@``.=N)^X['(`H@$2`C;L<$"@['`@H!_P +MV@^@!8MP`\P!V==P````0`'8PB`*`!>XQW``#@``A+B=N)^X['(`H@$2`C;L +M<$"@['`@H`#"['!`H((*(``H<*'`T<#@?N!X\<`R"*_[`MG/=X``Z#G>#J`+ +MZ7!`A\]VH`#L)\]U@`!H))<*'@`KAD0B@`"&(O\.(KJAN12ZM+D%((,`97DK +MI@0@@`\0``(`!"*"#Q```@#/<8``1`5%>`NA((4$WF!YR7`;"-`!((5@>H18.@`4@ +MA6!Y`=AI"%$!`(=A"-X`SW"@`$0=Q:##H,2@*/#/<*``R!P!V3Z@"X:!N`NF +MY@[`!2"%8'D!V"4(40'/<(``3!T#@`B`&0@>``#9E+G/<(``1`4KH`N&E+@) +M\,]P@`!$!0#9*Z`+AK2X"Z8J"0``D0=/^_'`SW"````3U@V@"P+9%@D``-'` +MX'[@>/'`$@]O^P#:"'4H=L]PH`#4"SB`0B$!"(#ARB&,`$`F`!(0<:0/!0P# +MS-=P````0`'8PB`*`!>X`""!#P`.```';@0@@`\``/S_)7B=N)^X['$`H0$2 +M`3;L<""@(KX&\.QQ`*$$Y6&^^0ZUD`"%X@@```T'3_O@>/'`X<7/`/, +MUW````!``=C"(`H`%[C'<``.``!/(($`G;F?N>QP(*#/<*``%`0#V26@`1(" +M-L]PH`#4"TV@SW"@`$0=-:#@?N!X`]K/<:``%`1%H<]QH`#4"PVASW"@`$0= +M5:#@?@/:SW&@`!0$1:'/<:``_`L,J<]PH`!$'56@X'[@?N!XX'[@>.!^X'C@ +M?N!XX'[@>.!^X'C@?N!XX<7AQL]PH``4!`/9(Z`-R,]R@`#XR&&2SW&``.C' +MQ(H4(0T`:+4`((,/@``(R#CAP*MB@A5Y!I)@H0,2`S;`'000!(*@$P$`AB'# +M#R5XH!L``,'&X'_!Q?'`&@U/^PAV&@S@`2AU@.#1)6*3`=@#]`#8!+C/=8$` +M4!,4>`EE'645"5$`B@C@"JEP(@HO_@&-`-@`K0&%205O^P"F\<#*#$_[HL$- +M$@(VSW.@`+PMSW"``$P=3J,D@`#=1A$1`0T2$#=6(08%1B#`(`,2#C8-&APP +MI!8`$(2XI!X`$`&65B&(!%8A1P2&'D03".C/<(``Z,CT((``">@!A@\(GP-0 +M(``@+R`((%,@?J!(`P$`SW"``+PQ:1``!L]Q@`"\,0'@:1D8``02`3:D&4`# +M`9:/"!``SW"``.C'5'B`$`\'?P\1$-`0`P%3(\.`%?1R%@,1X)9B?[@6@Q!B +M?_!_X!C$`Z06`Q"&(_./!?)HO_!_X!C$`W`6#Q'@$``!89;B>/%PPB<.$,(C +MS@-T%@`1&V.X%H`0=!E$`Z"Q>&`0>)`9!`"^&00`$(X`VQ"I`88!H0B."*D2 +MCA*IEKLP\`^#_PC>A6^#4R/``E4+G@4A")4#SW"``+PQIQ``!K:[SW:``+PQ +M`>"G'A@0&O!DN!!XD!D$``0C@`\```#P++@0J7091`.@L:&QOAE$`P&&J*F& +M(/\-A+@!H1*.$JGVNS@"`0``V):XI!D``"D+7@4"">_^`-@$$@$VI!$```0@ +M@P\"````+;L%(P($+R"((#[P`8&E"!X!<(E/>DDBP`#/D&*`_``VL=P@0`(#79X!(@()@X0"":"$$DBP@,6:U5X +MSW*!`(@.`&+/L]S@`!,'62#>(-!@F5Z!"*"#P````A&>)@9```` +MV):X08&&(O\-0P@>!:$*$`"8$8(`0"<`"4A@SW.``$RM0,`@PL.Z7'KT(X(` +M4O`*(<`/ZW(TV(RX7]L%NXHD@P^]`^_\2B4``)@1`P"<&4`#20M>`H"XI!D` +M`"CJF!&``,]R@`!,'4."AB#_`T2X,B8``(FX0,`@PU2"9'J&(_\#AB+_#D2[ +M>F)/>L]S@`#(2O0C@@`>\!,+'@((ZI@1@@!`)P`)2&`+\(7J`-I("X&1@`$/!983!YAAU$$,]Q@`"\,6H1``8-&APT`>!J&1@`=0%O^Z+` +MX'BAP?'`"@E/^PAU1L#HO2APS@`A`$AV`[A`()`%1"4"%B.Z!"6/'P8````! +MXD$O0!0$)8$?P````%A@-KG/$\]R@`!834"2!2H^```A@'\``/\_+KA?`"``&6%7`"``%7E1 +M)4"25@`A`";%M^4B``L`,VA3)0(0SW"``!!+\""```4I/@`*(,`.`>`&\(KE +MP"CA`,`HH@#/<8``3!TC@<#:-(&D>88A_PXBN3IZVGH98C!X"-P_`$_[,VA3 +M)<`0''C/@6`R;H%*+X`)W'/<(``Z#@#@`"`X'\X +M8,]Q@`#`&R2!((%!*(,%U;A!*8(%U;D9"24`6V//F*@HDF!0:!E*H5)%3J"B!P+DMJ!KP.0U1 +M`&)B2*%!@$FA2(A`+8`*4!V<]P@`!X&R"P!@Z@""AP +M`H7/#"!,$``\@0``"I<]P@``<&S5X0*`8$P4!#!,&`,]P@`#4 +M+`#9-*C/<```%*!`P`6#$!,'`$'`&HL[BT"#3@B@"6/#/<(``>AL!V2"H +MSW"``#P;)X#/<(``/,``'@;@@V@"`"WSW:` +M`#P;`H5(AF>&#R"!`,]P@``<&U5X8*`BI>S8B@^@`T"7"!8$$,]P```4H!@6 +M!1$,%@800,`%AA`6!Q!!P!J..XY`ALX/8`EAAB06@!!(A@#942``@02%#R&! +M``KR`=O//'`X<4!W<]P@`#(BZ"@`-C/<8``>!O& +M#*`(`+%>#"``J7`A!0_[X'CQP.'%`-C/=8``R(M:""```*62"._]`M@BA<]R +M@`!X&W?8O@Z@`T"2\00/^_'`=@P/^P#>SW>``'@;P+=V#*`(R7#/=8``R(O" +MI<.EQ*6*(,D`R7&*#J`#0)!X\<#/<8``R(L`$04`&PU4`0HA +MP`_K-'`X'[QP`8,#_O/ +M=8``R(L$%040HL%)#5``(PV0`-$-4`$(%000"B'`#^MRSW```$0?*06O_(HC +M1P;/<(``>AL!V2"HSW"``#P;)X#/<(``/,8`#0`S)D%P@``D +M/$`G@'(T>`!X`H;/<8``/!M(@2>!#R"```*FSW"``!P;57@@H%GPSW"``'H; +M@-D@J,]P@``\&R>`SW"``#S'+Z#6#Z_]`MA'\`J6C"`"@!'T`-C/=8``>!NJ +M"J`(`+4BAHH@!03*#*`#0)4!V`"F,_`#V`"F,?`#AHP@PX\!WQ+T`-C/=8`` +M>!MZ"J`(`+4BAHH@10K@II8,H`-`E7X,S_P;\`#9#R$!``*&!B!`@!+T`-C/ +M=8``>!M*"J`(`+4BAHH@A0QJ#*`#0)5.#._\X*8#\`*FA0(/^P@6!!`*(<`/ +MZW+/<```0Q]9`Z_\BB-&`.!X\<#AQ<]U@`#(BP05!1!")4$`DPF5`3,F07"` +M`"P\0">`AN`V2"HSW"``#P;)X#/<(``/,!#R"```*ESW"``!P;57@@H!WP`X6,(,./`=H)\@#9#R$! +M``*%!B!`@`WTSW"``'@;0+"*":`(`=@#V)H+[_P`I07P`J4#\`'8`*79`0_[ +M"!4$$`HAP`_KX +MP+@3>,:X`>`*M0C8.G``W@*%#R;.$PL@@(,M\@2%"R"`@QGRQG@$I<]P@``` +ME@B`#WDC"5``SW*``-0L,(J&(,,/@+@!X2]Y,*K/<8```)8(H<]P@``<&Q4@ +MT`,`$``@@.#B(`(``H4`V0`80"#&>`*E0B%`(`'GE0AU@.]_2I7/<(``>!L@ +MD('BS"$A@`?T`-K/<(``U"Q4J`&%$0A0`8'A`]C*("(!'@S/_ZT`#_O/A)L#(H209`@`'P&&A!:%3V*EQ$@J@`\ER)L`3"!X`5]BI +M<0(*H`/)<@;8!?"!Y0+8RB!B`'H+S_\A``_[X'CQP*8/S_H:<,]V@`"X&P"& +M`>#/=Z``R!\`IA$(40`!V%$?&)!B#H`+I!<`$,]P@`!L)B:`SW6``+"Y8'D` +MV`&%*>@DV!C98@Z@"S/:'PA0``05!1`*(<`/ZW+/<```=!F:VYD`K_P*)``$ +M)-@!V3H.H`LSVA\(4``$%040"B'`#^MRSW```*LHG]MQ`*_\"B0`!`"&0B!` +M@`"F!?0`V%$?&)!I!\_ZX'CQP`8/S_K/<(``;"8$@"7HSW6``&@O,H7DX/'`SW"``-@%`8!1(`"`(`@"`-'`X'[@>('@ +MA]C*("(`SW&``-PTX'\!H>!XSW"``.`U`(!"(`"`RB!B``?H`=G/<(``!10@ +MJ`SPSW&@`*PO&8'PN!F!SR"B`\\@H0(9H>!^X'C/<(``A"Y`B!$*'@#/<:`` +MK"\9@8JX&:$1"EX`SW&@`*PO&8&.N!FAX'[@>,]QH`#(.QV!!^B"V!2ASW`` +M@!$4#J'@?L]P@`#0R6R(SW&``"B,C","@`J102@"`PSR&0C?`@*[=GO'!X\<#&#>_Z5&B&(O@#3R)#`E,AP@`%(L0`SW*! +M`"@*%'J/X8HC#PS*("D`"?8`D@#=#R5-$(HCSP^F>`"R`-E*)`!TSW:``$"/ +MSW*``+B/SW6``+R/J"#`!!0B0`#DD&1_&0\!$0#?Y+`6)D`0X*#AH$`E`!DU +M>."@`>&U!<_ZX'@`VIZZ`-G/<*``_$1!H.!X(:#/<*``M`\\H`O(!""`#_[_ +M_P,+&A@P"\B'N`L:&##@?N!X\<`6#<_Z2':`X`'=1/:*)?\?$W@)"1,`LWTS +M>10A``"N#N_Z.WFL>``>0!Y5!>_Z`=C@>/'`X<4(<@'=@.'*(<$/RB+!!\H@ +M@0\``)L3RB.!#P``7`#*)"$`"`9A_,HE`0&`XD3V4WJ*)?\?"0D3`#-YLWT4 +M(8``5@[O^CMYK'@)!>_Z+W#@>/'`X<7/=8``*(S/<(``3!TC@$"%`(%#"@$` +M`I%"E3L*`0`"A18-;_PCA8P@`H`5\L]R@`"8!B&"`-L/(P,``KAF>19X(:(` +M(($/@0#("@"!JKB(N`"A`-BI!._Z#+7@>/'`X<7/<```___/=8``1(P#I<]P +M@`"$-WH)P`G/<(``H#=R"<`)SW"``$@X9@G`"<]P@`!D.%X)P`D`V2"E!=@! +MI2*EY@]O_0;8X@]O_0G8403/^@?9SW*@`-0'&AI8@`WH&1(!A@D@0P`/$@&& +M`B#`@'EA#QI8@/7UX'[QP*X+S_H#$@,V"'<-$@XVSW&``.C'$(O/0*X%G@%8C&)+;U88,"]#.DA@Q4)7@//<8``A!RT>:"1$.6@L260(PE2`&&Y +M);`0BS)H-GD[8F63.F*'ZR:242%`@$@,0OSR#,`*%@_@!0W(`\@!V:`80`#/ +M<0\`__^^""``Z7"1`\_Z\<`F"^_Z`]G/+`HO8D+B@ +M'``P`=U"\(C8D+C[\4Z(4''*(((/``"1`,\@(@3Q]0'!$0F>!@'=D-B0N*`< +M`#`N\"*0,Q2`,"T)#@`'R`0@@`\`P```'0B!#P#````BP(#@RB")#P``C0"F +M!^G_SR`I!`K!C"'_CQ+RSW"@`"P@$(`B>-=P`(```,H@A0\``(<`?@?E_\\@ +M)01,(`"@S"4AD&;USW"@`!0$XZ!,(`"@J79B]5,F?I`(\L]PH``4!`F`@.!: +M]64.7A`!VE<)$"`J<2\H00!.((,'E./*)<40A?=H=8`EPA3/<*``:"SP($`# +ME.,/>,HGQ1"$]VAW@"?"$<]UH``8+/`ES1.Q<,HB(@`)Z@#8#R#```8A`8#: +M]0'8`O``V(#@*/.Q`._Z@"0#,O'`3@C/^AIP#@C@`3#8F'`IN%$@`(#*(<(/ +MRB+"!\H@@@\``.D4RB."#P``QP!T`6+\RB4B`"S8'@C@`4`H@2`!WHHE#QK2 +M#Z`!,-B8<"FX&0@>`(PF#YHF\M8.8`L!V&&]YPUUD`'FK@^@`3383R`!!96Y +MX@^@`338G@^@`2S8"'66#Z`!--BX<#,(7@4*(<`/ZW+/<```ZQ3CVP4!;_Q* +M)```"B'`#^MRSW```.H4U-OQ`&_\2B4``/D'K_I!+0`4X'CQP(H/C_H(=P#> +MR7#&"Z`$R7$#V,EU&G`)[T0M/A<`(8!_@`!0-M(,@`D*[T0M/A<`(8!_@`#X +M-KX,@`E"($`@UPAU@`'ESW"``+25R72=L#"\GK#/<(``'`=>"^`%P*"-!X_Z +MX'[@>/'`U@BO_>'%SW.``+PQSW&``(PY0('T$PT`&0VD$`#8^!,!``T)A`#\ +M$P$`,'+#]P'8;0>/^N!X\<#/<(``R`4`$`0``1(%-@HAP`_K/'`N@Z/^L]PH`!4+BN`!]W3N2\H00!.((\'SW"@`,`OI1`2 +MAA00$8;/=J``%`2JIAH/(`B`V//8!;B`V8X.H`&?N0T2$#;UV`6X@@Z@`:EQ +MJJ8-&E@S!/`#V`6FJ88;[7SM02V`D`KR+R0)<.!XJ""``0`6`$#@>%,E39`) +M\B\D27/@>*@@0`$`%H!`X'BIANCQ\]CV#:`!!;C)"-^']=@%N"H.H`$*<2@> +M`!24YPT:*(<4#A??I<8`AP@'/<*``&"SP($(`E.?*(<4#A??I<8`AP@3/ +M<*``:"PU>`2_0*#'=X``3.05AS:'!7D7A[B')7@%)0V0RB'"#\HBP@?*(((/ +M``#"(`D0A``8CN$%H!"&`#P8````QN%A@!"&" +M#P8```'7<@(```'*(*$``_`!V",(4``5")``@^``V,H@X0'`**$#"_#/<(`` +MV,<"@`7PSW"``-C'`8`%>9@=0!">%0`1E!U`$)(=!!""%0`1D!41$;(=!!`` +MV(`=!!!^'000`\C/=J``U`=!D!`5DA`(Z@W(SW&``.C(]"$``!/H&18`EA\( +M%0X-S,]Q@`"T,D8@@`(-&APP&H$!X'\"(``:H0\6%)8)Z@W(SW&``.C(]"$` +M``/H`=@%\`/8$QX8D`#8!Q(/-@$2$#8`%@1`>G`'&A@Q`!8%0`$:6#$$RIS@ +MRB+"!\H@@@\``-P.RB."#P``]`J4!2+\RB'"#ZEP2@K@"@[9'PM1(`3(`9`@ +MZ,]Q@``H-!J!`>`:H1R!`>``U01``%3(,"`"/3/<8``*#09 +M@0'@&:$"%041*0T0``&%[KC*(<(/RB+"!\H@H@O/("(#RB."#P``M0?X!"+\ +MRB1B``"5L'#*(

+X=!!!983]G#O"^ +M%0`1"?`@E;@5@!!983A@$'B^'000"'>0'000#Q8`EK0=!!"F"V`%J7`0C3)W +MS""!A!/R"B'`#^MR0"D-)$`H#@0PV(RX`-N+NP4EQ!/)`R_\!2:%%*05`!`( +M=(0D&I`A\CT(7@(#R`&0&N@-R,]Q@`#HQQ1Y@!$`!Y+HT!$``6H5CQ`!X,.X +M^&`/>&H=`A!6"^``J7!J'<(3!?!*"^``J7`/'AB580*/^N!X\<`."H_Z&G`` +MWZ09P`//<(``3!T$@-")\*`'R`0@@`\`P```*'4S"($/`,````W(SW&``.C' +M%'D1B8_HSW"!`*@)UG@BB`B-#PA#``IPB@_O_:EQW/!1(`"@AO($%000@0P> +M`0W(SW*``.C'%'H1$H4`#WA)(,(`V!@,HT3")X%SW"!``@- +MUG@!B`+P`-C'D2*""&!``@A`0``(4`!22#!`Q9N-7C/<8$`B`X` +M8<]R@`!,'42"SW&!``@.UGE8@B&!17D$(8$/````""9X`_`#A<]Q@`!,'9@= +M`!`D@2B!!"&!#P!````^N5,D`@`>X3AZ17B8'0`0%PB>!Z05`!",N*0=`!!0 +MV)P=`!!X\"L(W@>D%0`0C;BD'0`0SW!``5``G!T`$,]P@`!,'22`$(&>N!"A +M9/`%V!2XG!T`$,]P@`!,':0=P!,D@!"!GKB?N!"A5O"/"%XG`85S"!X!$HTT +M$H$P22'!`')NSW*!`,@*=GMB8A$*G@7/D&*`_``VL=Q@0`(#=9Y +M)(D(($``"""``$D@P0,6;C5XSW*``$P=1(+/<8$`B`X!8<]P@0`(#M9X6((! +M@$5X!""`#P````@&>0+P(X68'4`0#B"BG!W`$P7P!=@4N)P= +M`!`1"!XE`-B1N*0=`!`$\*0=P!-T'<03T@\@`*EPSW&```A4=!4"$0EA66$P +M>70=1!#/<8``$%3P(0``I!4!$"5XF!4!$*0=`!`9"5X""MEV'400>!U$$("X +MI!T`$!7P$-G/H8A_P-$N88B_PXZ8L]Q +M@`#P2O0AD0#/<8``R$KT(9(`#O##N<]R@``DK3QY]")1`,]R@`#TK/0B4@"8 +M%0404R`$@,H@@@05](@5@1!1)0""P[D\>=$@(H4(\L]P@`!,K?0@0``'\,]P +M@`#TK/0@0``AA0L)W@"$'000`_"$'<03'0T>`D0E`@8CN@'B!"6`#P8````Q +MN!IB`_`!V@/(`9`LZ`W(SW&``.C(]"$``(+H`96X%8,0=!4!$00EOH\!``#` +M>6$X8!!XOAT$$`;T3R6%`Y@=0!$$);Z/`0``P`[T"B'`#^MR+-B,N(HC&@G= +M!^_[BB2##P"5WO$["E``@N+,(N*`RB'"#\HBP@?*(&(+SR`B`\HC@@\``+4& +MRB0B`*P'XOO*)0(!SW"!``@-UG@#B`;PSW"!``@-UG@"B(P5`1`.N"5XC!T` +M$,]P@``$!D"`!H*@$``&B.C/<(``U#D`B+4($``-$@,VK0N0`0"5SW&``"@T +MG0@2#,]P@`#HQW1X$8B-"!$`A0P1`'T('B">%0`1SW.``!0TBKB>'000%I,! +MX!!X%K,!R.>A!:&8%0$0KKFON;"YF!U`$`:"H!``!B\H`0!.((('([H.X@\A +M@`"D%0$0F!T`$+2YI!U`$)X5`1&GN9X=1!#/<8``R#D`H00@@`___]/VF!T` +M$`W8F!T"$`KP$-@'\`C8!?`"V`/P`=@'H9@5`!"^%0$12@PO_P#:I!4!$`0A +MOH\````P@AT$$%'RC!4"$)P5`!&4'8`0DAT$$(`=A!0#$@,V%PD>`Q38D!T$ +M$"IP?AT$$'@3#@$)\`[8D!T$$'X=Q!-X$PX!2G#">!!XLAT$$,]P@`"4QP"` +MAB!_CPSTF!4.$!$.7Q)ADX;KD;F2N:0=0!`0N"5XI!T`$`0B@@\````0SW&` +M`$P=9(%2(@(#$(,%>E"C1($0@@0@@0\````0/7DE>!"B$_"8%0$0@!W$$Y0= +M0!">%0$1?AW$$Y(=1!"^%0$1LAT$$)`=1!"`%0`1?A4"$8(5`1$:8H05`!%9 +M83A@$'B]!&_ZL!T$$.!X\`!A +M+;A3(`"`!?3/=8``O#$-\,]Q@`!,'2"!Q!$!!L]U@`"\,5$A0($$]`'9W!U` +M$,]Q@`!,'?`A``#/`!X +MX@F@"0/8?@F@"4#8`-C@'0`0#O#/`\]R@``,(07PSW*``.@= +M0"(#!D`B`0=1)$""RB+"!\H@@@\``,LBRB."#P``K`/`!.+[RB'"#\]V@0"( +M#D`MC0&F9D#&(,4-#AX2PKVJ80[P$0Y>$D0E`1Q$N2ICB;H&\%,EP1`\>2IB +MSW&!``@-%B%!`2*)#KE%>2"@E0-O^J+`\<"8<+AQ%'@X8,]Q@`!82@AAC"## +MC\HBP0?*(($/``"L$\HC@0\``(L!1`3A^\HAP0_1P.!^X'CQP`?8SW&@`-0' +M&AD8@`X1`H8-&A@PSW"@`$@L7J`?$0"&!QJ8,`$:&#`$RIS@S"""CP``D0`& +M\@`6`$``%@!``\S/<9\`N/\8H7W8U@S@`@$2`38$RM'`X'[@>/'`N'$"N<]R +M@0#("C9Y,")$`%$D0(+*(L('RB""#P``RR+*(X(/``"3`[`#XOO*(<(/0"V! +M`<]R@0"(#B%B42%`@HHB"`7*(F$#SW&!``@-%B%!`2*)#KE%>2"@T<#@?O'` +M*@I/^L]R@`"@>_AR0"(&`6,(=```VL]Q@0`D&9AR`!<"`+AP`-V`@JJ&+ +M&GW`O;-]J*H!X$(E0`"W"'6`0"1"`"$"3_K@>/'`M@E/^L]R@`#`&T2"SW6` +M`$2,8H5`@C:[-KI0<]8BC0\``(``P(4]8GYF'0V%$PHAP`_K9O\-A9-88-4!;_H.((`#X'C@?P#8%'@X8,]Q@`"@5.!_"&'@ +M>.!_`=C/<8``F#'@?_`A``#QP)AP"B'`#^MR"B7`!\]P``"?&7$"[_L[V^!X +MSW&``'0QX'_P(0``\<"8<`HAP`_K<@HEP`?-V`6X30+O^T3;SW&``*PQX'_P +M(0``\<"8<`HAP`_K<@HEP`?/<```H1DE`N_[3=O@>!\)$0'/<8``6$4(84`H +M`@(%>D`H`01%>1BX)7@.\,]R@`!80?`B```#\$,H``)AN2]YC"'#C_OUX'[@ +M>,]R@`!81O`B```7"1`!8;DO>8PAPX\4\D,H``+Y\00@@`\```#_02@!`H8@ +MPP\%($(`0"D`!$5X&+DE>.!^X'C/<(``U#3@?P"8X'C/<8``.`8D@>!_(*`1 +MB.!_PKC@>,]Q@`#(.$:!BB'_#R"@!NHB@B"@`=@#\`+8X'[/<8``Z#A&@8HA +M_P\@H`;J(H(@H`'8`_`"V.!^BB'_#R"@SW.``.@X1H,2ZB2"&PE>`,]Q@`#8 +M-P\*0`#/<8``]#<1"D$`0(+E"X&``M@%\"*"(*`!V.!^\<"R#P_ZSP@0`,]V +M@`!(L"^.SW"!``@-SW6``$P=-G@BB`.%`-_/#:`)*G`!W1#P`-W/=H``2+`Z#Z`'!)8`V,]Q@`"T,@ZN'H$! +MX!ZA'0ECH6!1$*(<`/ZW(0N`4E +M!0#/<```@PR*(X4/Z0>O^PHD``1`*`TAW64EE025$+DE>#CHSW"``,Q4\"`! +M!$0H/B<`(8!_@`"P-B]W(*`CE0*5$+FJ#N_^)7@(<0`G@!^``*0V*@P`"<]P +M@`#`5/`@`00`)X`?@``(-D>5(*`CE0*5$+H0N25X)I7Z"B_\17EN#L_^"'$` +M)X`?@`#\->X+``E>EAV6`-D/(0$$$+I%>`8@0(`!W1VV,+@>MA3TSW&``$0D +M`(&@N!8*8`4`H<]P@`#`&P2`((#/<(``])6BH"&@$-K/<8``'`<`@0`J`@1& +M>"$&+_H`H>!X\<"^#2_Z`-H/(@(`SW.``+25'I,]DQ"X!7D&(;Z`-O3/=8`` +M1"0`A8"X`*7/<(``,`;/=8``>"``D,>--PX!$,]P@``R!@"0P8TK#@$0SW"` +M`#0&`(BFC1L-`1`+R`0@@`_^__\#"QH8,`O(A[@+&A@PSW"``,`;!(#/=8`` +M])4`@`"E`-@"I45Y/;,PN9T%+_H^L_'`X<6."J``*'6`X,H@00-8"2$$RB%A +M`(4%#_I1!\__\<`�_Z'@V@"`#=SW"@`-`;$8`7"-X#:@F@"0'8SW&``"@T +M"8$!X`FA!L@#$@(V.P@>`*02```S")X$SW&``*0P`($3Z*"A`0F>1<]PH`#$ +M+`N`4R"!!/ZXS"$B@`?RF!(``(X+[_X`V@,2`3:@$0``%P@>!(H@"``,&APP +MC@W@!"AP+O!)"!X%!\C0B0#:,Q&/``0@@`\!``#P02@-`\]QH``X+@>!#R)" +M`P'<1G@'H0W(#@_@"@`L`!#'=X$`R`H"OM9^$N??9Z"OBB`0``8:&#`#R*`0 +M@`#$X-0*P0H#V<]PH``4!".@A00/^N!XHL'QP$'"8L,*)0`!@<-`)`0R$@@@ +M``':T<#@?Z+`X'CQP.(+#_J")`,\"'_[0=I` +M(P`H`-E^#>_[0=KI<);!`@Q@"*ERZ7!`(P$H]@M@"*ERBB0!<`#9J""`!0`D +M0C!8$H(``"1`,$<&((`U@^O^LEPR7"6 +MP;8/[_I`VA\(#^_ZR7!AO_$/=9`!Y4IP.@^O^LEQ +MH@^O^LEPR7!`(P$H?@_O^D#:R7!*<7(/[_H0VDIP%@^O^LEQ/0,O^H`D`SS@ +M>*+!\:'(0$'H!A"`(MV&@EO^\EPR7"I<1X*K_M`VA\/=!`` +MW?`@02/P(T(C"@JO^\EP8;_Q#W60`>4J<$8(;_O)<>8(;_O)<,EP0"(!*>H) +MK_M`VLEP*G'>":_[%-HJ<"((;_O)<34"+_J`)`,YX'CQP/()#_K/=8``](X! +MA<]S@0`(#D0@!(//<(``T,D,B-)HUG['=H$`R`I`AA9[(8,2\E`BCP7@ID8A +M`08AHPT,$0&1O^"F!?"QNK:Z0*;2#H`*!_"6ND"F12$!!B&C"XVBN/D!+_H+ +MK>'%X<;/<(``T,E,B(PB`H#/<=Q@0#("E9X +M0(&A@`7NE;I`H:N]!/"UND"AB[VAH`#8"ZO!QN!_P<6AP?'`42``@N'%J``A +M``AU1"4#%@0E@A\&````([LQN@'C>F($)8`?P````#:XSW.```!42F,(8UA@ +M02V"$E(B`@#`N@.Z&.*%X,HBC0\!`(D-U2(.`%!Q4@`E``#8[;T8`"$``B&` +M`,]Q'$?'<04H?@`*(,`.`_`BN$$M01/`N02Y-'FI/'`I@I/^N!XX'C@>.!X:2"``6\A/P!I(```]_'QP-(/S_D:<,]VH`#0 +M#P#=!_`0%@"6_6'X8!`>&)`C;6\(1"`E%@.6)18"EB\DQP`E%@"63W\/?0B] +MI7_7#!&#@N?,)^*3S"&)"9!^_Y`=C@>/'`,@_/^8P@!("``"8`"'=Y#Q05SW:``"265B9` +M$XX*;_H"V6@6`!%A"%$`5B:`$WH*;_H$V50F0!UR"F_Z`MEJ%@`110AS`6B_ +M`K@]#P00`-T+\%4F0!>U>%(*;_H$V62_`>6O?6H6`!'M#0*052;`%CH*;_H" +MV6P6`!$-"',!8K\"N`D/!1``V!CP`-T+\%4F0!BU>!8*;_H$V62_`>6O?6P6 +M`!'M#0*0SW&@`-0+#X$?9P'8[Z'A!L_Y\<"@X`AQ`-@)]\]P@``DEC&@W@EO +M^DC@`=C1P.!^X'CQP$(.[_D`VCIPC"`$@$H@0"#"(`TDSW"@`-`/)1``AL]U +M@``DEL]W@`"PED`G4A.4'0(0SW"@`-`/)1``AD`G$Q,"WI4=`A"6%8$0E16` +M$#A@D.#*((D@?PA1(`#8$_!N"6_Z$."6%8$0`>$O>1-I%7@R(@`@EAU"$!YF +M0"1`(`]XFG"5%8`03PP#(!-I%7AB<#X);_H!V985@!#/<8``X)8#N!5X.&`F +M"6_Z!-F6%8`0`[@5>$)P%@EO^@'9EA6`$`.X%7@99RV)!N:5"7*(^&!*(``@ +MSW*@`-`/$!(`A@(A@2,X8!`:&("M!>_Y"G#QP&H-S_FAP0AU*'99#301`-B+ +M<,H(;_H$V0#`-0B`#_(!`%`W"(`/\@4`4!L(@`^:"5!OSW&@`-0+#X%DO;A@ +M#Z$!V!#PJ7!*#>__R7$+\*EPY@WO_\EQ!?`,;;8.[__)<0]X<07O^:'`X'C/ +M.'%,F@V><]R@0#("B%B +MSW*``$P=+;G`N?`B0P`H@U$A`(#/<8``V,=!@0GR/(N`X<4B@0\```H"`_)% +M(D(#2B0`=`#;J""``C9H=7D`(8T/@0"(#D"E`>,`W<]S@0`(#18C`@"@JJ&J +M`=DBJ@/9(ZI*)`!QJ7*H(,`!>6(6>:2I`>+@?\'%X'CAQ4HD`'@`V*@@``@` +MV\]U@`"D&T"%#R,#``LBP(`/\D&%"R+`@$#:SR+B!\HB@0\``-``SR+A!P+P +M`-K/!(5>T"C`>#@?\'%SW"``&PY!H`#@""`SW"``!R)*:!1!Z_\$=C@ +M>/'`.@O/^<]P@`!`R\*($PZ>$`#9SW"``"('(*@=\,]P@`#HTKJ(X[W*(6(` +M]/7/<(``P)D,$`0`'PQ?``HAP`_K<@B^SW```/HJBB.-!SD$;_L`)D4340// +M^>!X\<#:"N_Y`=G/<(``F",DH(H@Q0_/=J``R!\9'AB0*'`HA`!AHFYB[EZ&%B`SW&```#>$!A8 +M@%8-P`'>#8_^O@K`!T#9SW"?`+C_,J`^"0`*@-G/<*``%`0LH!T=6)#N"@`) +M:@C`"+8((`GI<`?82!X8D,(.0`;V"X`!O@E```X)``1F"\`'O@R``^8/``BR +M"D``1@U`!VH*3_RZ#L``3@Z`".X-S_Z^#L`$-@A``;8-0`9^"8`%Q@R`"FX/ +M``1Z#$_^A@K`!'X*P`0:#,`'SW```/[*S@L/_$D"S_G@>/'`U@G/^<]W@`!, +M'0.'"(#`N,H/(`DO(``@`-W/=J``M$?/<*``C$2XH`#8D[AW'AB0"-AW'AB0 +M`-B>N%,>&)#@>%,>6)//<(``/``0>$<>&)#/<(``X`(0>$@>&)!/((`C12`` +M#4\@Q@&)`&)!&'EB3SW"``'#F8@E@!0R(2B2`<,]Q@0!0$Z@@ +M0`//<(``V,=!@'1M='L[8P*`0Z,!Y02CSW6``(@Y`(4$Z&0>&)!#'IB1F@T@ +M"0'8`X<(@$"%'0@>`%,B00`2N40B``,.N"5XAB+_`PJZ17@2\$APAB#S#PJX +M!"*!#P````P&N25X!"*!#P```#`"N25XSW&``(@D,0'O^0*A\<#AQ1#=]@Z@ +M`:EP!]D+N<]RH`#P%S&BSW$``/#_.**RHN8+@`$=`<_YX'@`VH#ARB1-<.@@ +MK0'_V5Q@(*P!XN!^X'CQP.'%SW&``#S'SW"``#Q4W@@@"$C:SW"``+Q-SW&` +M`(P&S@@@"`C:`-W/<8``%"FAH:*ASW"``"PLJ:#B"V`"`X'/<*``+"#/<8`` +MG"Q0@!"`1:$&H0H/X`&IH:$`S_GQP`#9SW*``+25(*+/<(``1"0@H#VR,+D^ +MLM'`X'[@>.!^X'CQP`(([_D@VZ3!SW&@`,@<::'/<:``E!,`VENASW&``/@2 +MH($S:#5YBW`E>V"G)84AIR:%(JO^:3`X'CQP.(.C_FD$`$`HL';"5\& +M(-G/$0 +MWLHFXA$&%`\QC"?#GPGT!!0/,?%VS"?JD`'>0O8`WNONQ8!%?L>EL8B&)?P? +M&+VE>L]UH`#,%UJ@%O!%@,]QH``0%$>AI!`!`!4)G@(QB->ZAB'\#QBY17DZ +MH,]UH`#,%PW9`=H#X0T=F)`.'5B0)H`9'5B0)X`:'5B0*(`;'5B0`]D4'5B0 +MCI!`!`)FYI!A``&4&K_FBP/'`]@VO +M^039"'4-$@XV!M@-&A@PSW>@`!0$"J?/<(``,%3V#\`'`(7N#^`'!-D!A>8/ +MX`( +M<0&%0H4@D`6%K@_@!T)YRJ?M!:_Y#1J8,_'`=@V/^2AVSW>!`+`2.V=DBT`G +M$1&,(\./&G(_9Q+T`-D$\`'A+WG/1#B66&6#>`'$-I$CP"%0"X!$@.Z57I88""P)(\`A0.Y +M-7DR8#A@12*"`D"P2B3`<`'9J""``Q4.0!`R(4,@`(4`V@.[=7MX8$"P`>$O +M>0#8$-T(=L]Q@`#$>!9Y0"!`+PCA-@W@!P;:8;T!;ND-=9`/>!4%C_GQP*X, +MC_FBP0AW*'4:)/>O4*,X0`AG-J=7MP8/CH!!J"(""&`[I5>NEP$.%98`/>$`G`!3/<8``9'DJ#>`'`MJ+<4`@ +M0"]J#.`'`MJ!P0IP0>!>#.`'!-H`WQ#=BW#/<8``Q'CV>0CA1@S@!P;:8;T! +MY^T-=9#O?P02@2``AL]U@`!TL`'"`[DU>0+@.&!`J`02@2``A@.Y-7DX8`#9 +M*!U"$`'!!:6+=Q#@)Z4FA>ER4@Z@"&B%!!*"(""&"(4#NE5Z!.%98>X,X`<% +MV@'`)H7I<@'@0<`%A1#@*@Z@"&F%M0.O^:+`X'CQP%X+C_D(=L]U@`!8!P"% +M*'>&Z(#FXB""`R'PSW"``)PXI@B`",]P@`!<),]Q@``D!\"@`($%?^"ASW&` +M`%0P`H$!X`*A!/!N"P_[`(7^Z,]P@`!@*X,[_P!V`GPBK@:H1J!TP@? +M@*H,[_P!V`#9F[G/<*``T!LQH-H(0`J.#@`*SW"```@P`(!"(`"`RB!B`-'` +MX'[@>/'`X<7/<8``M)5^D5V1$+ME>@'=&PH/`,]Q@`!0-D0H/@?>#V`(`"%` +M#JEP`O``V-T"C_E&@0GJ(X%@@2*"8GDP<`#8`O8!V.!^X'CQP$X*C_D(=<]V +M@`#H.-X/[__)<0?HJ7#2#^__0"8!&(/H`-@)\,]Q@`#(.+X/[_^I<'GH`=B% +M`H_YX'CQP)((```&Z`()```/"%$`SW"``+0C`("#Z`#8$O`*"0``C^CZ"``` +MB^C/<(``E"`LD,]P@`!,'1Z0XPD!@`'8T<#@?N!_`=C@?P#8\<"X<,]QH`"L +M+QB!&0B>!@HAP`_K,]P@`!8!P"`A>`!V.!_P'C/ +M<(``6`<`@(;@`=C@?\!XX'\`V.!_`=C@?P#8X'\!V,]P@``H%`"(!NC/<(`` +M$!0!B`/P`=C@?@T)7D<)R+VX"1H8,`#9G;G/<*``T!LQH.!^X'CQP+8(C_G/ +M=:``R!\D%0Z6%0X>$H45`);B#0_\BB`$`"0=&)!1)H"0&`M""O4`C_GQP((( +MK_DTV#X(@`#/=X```'`O"!X$3@O@`@#8(@O@`@'8BB80$`#="@CO_JEP%"=, +M$V&^`+3U#G60`>4.\`#;BB(0`$8,8`1P>!0GS!!AN@"T]0IU@`'CD0"/^?'` +M)@BO^338H<$`W=X/8`!`Q<]W@```6#,('@22"^```=@#W@J^`-B,N+A@$'B+ +M<88,X``!VA0G3!-AO@"TZPYUD`'E6@O``!#P!=L*NP/:"KIX9=H+8`00>!0G +M3!-AN@"T\PIU@`'E)0"O^:'`X'C/<0$`2#7/<(``A!;@?R2@\<#AQ6_8E;C/ +M=:``R!\2'1B0SW`!`$`\%1T8D&(.P`B*(`0`#J7Y!T_YX'CAQ0#;SW*``$"/ +M2B0`=,]U@`"XCVAPJ"```D`E`1(4>6"Q`>!(<,]QH``$)0^A5B(`!!&A5B(` +M!1"AX'_!Q>!X\<`Z#T_YSW6``$P=!87/=J``Q"=U'AB0#)5V'AB0!X5Y'AB0 +M$)5Z'AB0*@[O_P#?&^AW'MB3>![8DX`>V).!'MB3!X6&'AB0$)6''AB0!X6* +M'AB0$)6+'AB0!86('AB0#)6)'AB0!86$'AB0#)6%'AB0P=A0'AB0*0=/^>'% +M"''#N,]R@`#`C_0B`P#)NW!QRB0B=,H@(@#H("("]"(-`,F]"0E``P'@X'_! +MQ?'`X<4(=<]QH`#$)QD1`(8!VH#@$1$`AL!Z@.(`I=$@X8<`V#/TSW"``,#) +M#(#/<:``R!]DX!ZA$-@.H0'8%1D8@`(-(`H+V%$A`,;*("(`&/0E"%Y'SW&@ +M`-0+%H$X@23@%0A%`-X,(`H#V`D+'T`)")Y$&-@#\`#8@.#*(.($SW&@`)`C +M/H$@I7T&3_G@>/'`_@U/^<]V@`!,'14F`1!`@6F"N(I!*\``P+@7N,=P``"` +M'.2[SR`B!N"[3M_/(*(`RB>"'P``3@&&Y<\G81(I"U\!SW6``)0@&!4$$;Z6 +M&PT!$:&&Q!4-%A$-7Q&@AL05#18'#5X1@;A1(P""SR"B!1NB_*)`@<]P.@1* +M8H@ +M!``>I1GPBB`0`!ZE%?``V(NX'J41\`#8C+@>I0WP`-B-N!ZE"?`#V`RX'J4% +M\`#8CK@>I8(@`0&%!6_Y'J4*(<`/ZW*,V(VXOMN+NTHD``!5!N_Z"B4``>!X +M(8``VE,A?H`+\@#:F;I1(4"`RB*"#P``@P#`*F(&SW&@`.Q&1:%FD$@C`P-' +MD!"[!".##P\```#(ND5[2)`,N@0B@@\```#P97I&H4"`00K1`&*`SW*?`+C_ +M?:)%@$FA1H!*H4>`4:%(@$NA28!,H4J`4J%+@$VA3(!.H4V`4Z%.@$^A3X!0 +MH1"`%*'/<```557@?O'`:@Q/^2&`SW6``*0;`H`@I0#>`:7"I<]PT/X``*X( +MK_\$I<]RGP"X_]VB;R%#`,]PH`#L1B6@&@Y/_B"%&0G>!\]PH`#(.QV`!NC/ +M<```554'\-VB?.G/<```K=YY!$_YX'@BD$@A00%`*0(#(Y!B@,NYC[E%><]R +MGP"X_WVBSW*@`.Q&)Z(C@#6B)(`VH@6`%Z+/<```557@?O'`Q@MO^0#;IL'/ +M<8``I!M@H6&A8J'/=J``M$6)!*)(!QSW*``*0;"!(%``L0D```W3EUV'6I-*)P``#R='`PL@P*$)\A4D03,@@4HC +M`!`/(TL0`_!*(P`0*H@+(<"!!2')$@GR%21!,R"!2B<```\G1P`#\$HG```% +M(L(!D.\5)$$S(($,$`4`/]\*)(`/``"MW@\@2!`$&L(3$0]1$A4D03,@@0#: +M#R)"`!,/$1(5)$$S((%*)@``#R9&``'ESW6``*0;"!U`$8#CRB2!#P``K=XU +M\B6(9(@&(H(!Q;H0N04C@P\````_97D%(8$/`#\``)L>6)!GB":("+ME>6B( +M$+ME>6F(&+ME>9P>6)`&(0$2Q;F9'EB0`-F5'IB0F;E,'D"0)(!8'D"0"I"4 +M'AB0;R!#`),>&)`Z#$_^B'"E`F_YIL#@>.'%X<;/4$&E``(H8$&U``,8#/8&EB`,H"S&EB`$X"T +M&AB`SW```%55P<;@?\'%X<7AQB2(SW*``!A5IHC"N2YB`-D/(8$#SW.``""4 +M0(.$[29Z0*,8\$5Y(*,EB!4CC0,CI2:(18A982:E((",(1"`1?>*(1``(*`C +MN2&C`(`JN`*C`-G/<*``\#8LH".#):`F@R:@)(,GH">#**`E@RF@*(,JH"&# +M*Z`B@RV@((,DH,'&X'_!Q?'`/@E/^:'!^G`;<3MR0,,*(P`A"B1`(0HE@"$* +M)L`ASW>``-0%((<`V(#ARB"!#P``R!OT"$$`6G``AXCHSW```,P;Y@A``#IP +M!/!*(0`@`(>)Z,]P```$'-((0``:<`/P2B``(`'8SW6@`,@?$Z4&V,]V@`#< +M-`"F!![`%0@>`!8`P`P>0!84'L`4&!X`%02F'!Y`%0_`(!Z`%0FFSW"``,`; +M)(`@@2JF*(`@@0R`*Z8`@`RFH!4`$`VFI!4`$`ZFJ!4`$`^FSW!#=:@2$*8` +MAX;H5@A@`"C8`_``V!&F`(>`X`#81`A!`!*F4R?`=1.F`F$Q4`EB\AAP08IA05`)8(N1FF%14`EAJF)!4`EANF%A4`EARFSW"` +M`*PS%X`=IG@>@!I\'L`:@!X`&X0>0!M(%0"6B!X`$,]P@`````2`C!X`$%,@ +M`",0N"5X+R%'!"5XD!X`$,EP1@A@`"79#0!O^:'`X'CQP.X/#_G//'`B@\O^6K8HL&+<0':O@]@`$ASC^@*(<`/ZW+/<```TA2*(\4$BB2!"JT` +M[_I*)0``0"2!,438`=J2#V``2'./Z`HAP`_K*),$*20#O^DHE```$%``Q0"2!,`':+@]@`$ASC^@$%`4Q"B'`#^MRSW`` +M`-04BB-%"!T`[_J*),$*`A0`,<]V@`"$.QMX02C%`$PE@(P`'D`1U/8*(<`/ +MZW+/<```U12*(X4)Z0>O^HHDP0H=V,]V@`"$.P"FN'``%``QSW6!`/P:0"V" +M`*EQN@Y@``';D.@`%`0Q`!8%$`HAP`_K@7P@.(!VL!Z-PI1`$PB`*8!VL(BB@"&(_T/)[L/ +M"8``@.+,(&&@"_0R=\PC(8`)\@/O!>L+"<(C#P["$\EW!/`!V0CP"'4!X&<( +M1($`V8H@_P^$Z8#ERB!*`XP@_X_*(($/_____Q7R,B2"-,]Q@0#\&@\*40!B +M<19Y`A'```GP%GD+"I$`!A'```/P!Q'``$$%+_FGP/'`/@W/_Q8*3_^Z"0`' +M]@P/_PHAP`_K!X\<#/<(``3!T"@,(0``91 +M($"`-`A"!M'`X'[@>/'`H@P/^1IP*'4Z<@#9SW"``%RM(*#/<(``))8Z#N_Z +MBB$,!L]RH`#4"WZ"`"6!'P```"#/<(``V`9B>6"@S+G/<(``P,DOH@R`SW*@ +M`,@?9.`>HA#8#J(!V!4:&(!-<(8@_`/0X,P@@H\``(``$O*,(`.$$_(*(<`/ +MZW(*)(`*SW```#(1BB,:#7$%K_JX/'`X@L/^<]P +MH`#$)U(0`89!$`"&AB#CCP#=!O+KN=$AHH$Z\L]P@`!,'0.`"8#/=H``))8M +M"%X!F@T`!(GH%(Z!X,H@(0&@#B'_RB%A`,]P@`"TF0"`#0B>`#((8``0EK2N +MSW"``+29H*!-<(8@_`.,(`*`#/3/<8``+!0`@0'@]@T@!`"A6@R/^P;PC"`# +MA%@/P?JY`P_YX'[@>/'`1@L/^<]V@``7Q#?20((:`X.!^X'C@?N!XX'[@>.'%X<9! +M+0!4P;@7"!4!,R8`<(``4#Q`)X%R%'D`>0#8%_#/<8``1,F8$8``0"@"!H8@ +M_0]2(,`!1;A%>,]RH`"()!"B'X&SN!^A2O`!V!#;SW&@`,@<::'/<]UH`"()#"E/X,"W40H +M/@T`(8!_@``(YI6Y/Z//<:``\!>]H:2`BA,#`::AHX`4XZ:AHH!3(\.`IJ&A +M@*:AP"`A",`@(@Q@@'.A;&A@@W.A^!`#@G.A_!``@!.A2J'!QN!_P<7@>/'` +M%@HO^0#;>0I2`)]RJ""`#L"0;V%3)DT0`[VX?Z.``>;`L%,F3I"E?^.@*/2A +M@.=]H:#B@$,MSA/G?MUE!"6/'P#_`/\$)8(?_P#_`"B_"+I%?\*@YWZAH-UE +M0RU/%Z&@QW_"H/UE0RV.$*&@YW[BH-UE`-K"H*&@0Z`!XW![_0$/^?'`E@DO +M^0':HL$(=5K88,``V$'`BW%J#^__J7"!QJEPR7%>#^__!-H`E!QLEPU@_O_T&'R7`I/'`X<6AP0AUSW#4NO[*0,`$\)(/H`D!V,]Q +MGP"X_[JA!-@;H8MP'J$`VIVZSW"@`-`;4:#/<`!M`!`9H0$)7D<`P-,(@(_4 +MNO[*]0`O^:'``-O/;$`#_G/<8``O"-@B<]RGP"X_P;KSW'0NO[*/J(: +MH@[KSW"@`#@N!8`$((`/P````/$(@(_`````:M@8N!FB'(+@?N!XX<7/PB>``(+ +M#__/=H``3!W)%@`6I;C)'A@0DQ4`EJ6XDQT8D-<6`!:EN-<>&!`.AJ6X#J8` +MAL@0``:&('^.RB`B`,HA`@`("Z+YRB*B`0&&R!``!H8@?X[*(&(`RB$B`.P* +MHOG*(J(!`(;/<8``/,?$$``&);C`N`JAZ@D/_G_8"KC/<:``T!L3H7_8$*$` +MV)6X$*'/<0``B"`."B``!MC/<:``\#8$@48@P`$$H<]P@`#4!0"`@.#*(($/ +M``"4`.@.X?_*(2$&SW"``!`7`(!1($"`*`PB``,]P@``(,`"`B^BV#&__BB#&"`L(40`Z"8`%#?`` +MV9ZYSW"@`/Q$(:#@>,&@SW"@`+0/W*`Z"8_\Y@L``4(,H`/'`,@[/^(AU`-\*Z!D(40`!WL]P@``)%,"H!O#/<(``"13@J`GI&PE1 +M``'9SW"```84(*@%\,]P@``&%."H"NH9"E$``=G/<(``"!0@J`;PSW"```@4 +MX*C/=J``R!_/<(``"108'MB3`(B*(1``$>C/<(``B1P`B`OHSW`#`$`-11X8 +M$#"F`M@8'AB0`O`QIL]P@``&%`"(&^C/<(``BAP`B!?HSW`"`/(O(!X8D,]P +M@``H`"$>&)#/<(``0`4B'AB0&!8`ED4@``,8'AB0SW"```@4`(@(Z!@6`):% +M(`$$&!X8D`\+40`8%@"6B+@8'AB0&!8`EH"X&!X8D!CM`-B4N,]U@`!\!P"E +M<=@&N"H-[__\V2"%SW```$P<&@WO_Y^Y&!8`EH6X&!X8D&T%S_CQP)AP`^DC +M#!((SW"``,@%`!`%``HAP`_K`#92B2`<<]S@`#TB"ARJ"#``?`C@``!X@5YX'\O*$$`X<4`VDHD@''/ +M=8``](A(,E>@#9GKD9>00A@`!"(`"`RB!B`.!_P<7@>/'` +MSW&@`,@?I!$"`,]P@`"<+`"`-8'/`! +MV`+R`(,-"%$`#0F$#P``B!,`V`/P`=B!XLP@8H`H"*'[RB#A`='`X'X"X3!Y +M06D-"@,`(G@0>`/P`MC/<:``R!\>H1#8#J$!V!49&(#@?N!X\<#AQ5#=`-K/ +M`#9 +MSW"``+29(:#/<(``1,D`@A`0`P>0+8%1H8 +M@#^BX'X"X3!Y06D-"@,`(G@0>`/P`MC/<:``R!\?H8H@&`@.H0+8%1D8@.!^ +M`-G/<(``M)D@H"&@X'\BH.'%X<;/<8$`,!-%@23HSW&@`,@?0!$.!L]S@`!$ +MR4`HC0)"$P`!?)/0?MA@NV-BNP@C`P`">PDBP@`"V!49&(#/<(``3!U?H0.` +M(H#/<(``M)DBH,'&X'_!Q>!X\<`"#@`""''/<(``3#?&",`'`MG/<($`9!HD +MH-'`X'[QP.'%SW"``,"9`X#/=8$`9!H1"%\`%P@>``B-#PA1``380@SO_02E +M#?!^#\_^M@T``@AQSW"``$PW>@C`!P+8!*41`\_XX'CQP.'%SW6``,"9%86` +MX%0/`@)Z%0`6"NB`X,HD#7#@>.@@+0'@>.!XX0+/^/'`SW"!`&0:!(`;"-$` +MSW"``$3)`)"&(/P`C"`"@+@/P?\5V-'`X'[QP,]Q@``B!P")C>C/<(``Z-(: +MB`\(W@`!V/H,(`8`J1?PSW"!`&0:SW&@`"P@,($%@")XSW&``""L`*$H<,]Q +M@`!,JV(*(`?4VAX/S__1P.!^X'CQP.'%"'7/<($`9!H$@`?H(@B``0"E`=@" +M\`#810+/^/'`R@G/^,]P@``DEI40@```W8#@4@`L`,HB3`,S;<]R@`#@EC5Y +M(F($(H(/!P#P_R2Z+RN!`$XC@00#;#R-#`,]Q@``(56YASW&!`'P:MGGA +M@08BPH#E?L&AZ_4!Y:]]O0T"D&T(<@``V9AP$VG/`-C!".##_@! +M``!!*\.$&?(O*($`3B".!\]^`-T/)8T3SW"```!5KF#/<($`?!HV>."`!B-# +M@^5^P*#L]0CPSW"!`'P:-GA@@("[8*!")$```>&E"'6`+WEA`<_XX<3\',B^ +M_!Q(ON'`X<'APN'#_!P(L?P<2+'\'(BQ_!S(L?P<"++\'$BR_!R(LOP'&X%!]@V".__"KA3 +M($$'!]AJ".__"KC/<*``U`L8@$(@``A((```SW.``+PQSW&``'P'((&\&Q@` +M"R%`A,H@(@,N]!\(D2`7"9XEB>A1(4"E9-C*(($/``!<`"#P--@>\(P@!*`9 +M\DP@`*(1\@CV&PA0("<($2&&V!+P%P@0)(P@`:`+]$S8"O!FV`CP/-@&\$;8 +M!/!4V`+PA-B3$P,&Z7')<@HD``0=`6_Z"B5`!.!^X'C@?P'8\<#AQ0AU*'`% +MZ\]Q@```<`3PSW&```!86WIJ"._XM'D5`._X`=C@>,]P@``R!@"0!N@`V<]P +MI``<0#*@X'[@>,]P@``R!@"0!N@#V<]PI``<0#*@X'[@>`#9SW"``%RMX'\@ +MH/'`2@^/^!IP`-[/<($`,!,4$!,`%A`!(<]P@`!X)`"`(*`Y\-UFM'U`(`\A +M`"=2$P`B0",!D$8)8`D*N,]Q@`!X)""!0"X1$0SA(G$`H;AG`I"I9PJX!2G^ +M!"()8`DG<`AQR@NO^@`0`"#/<8``>"0@@4)U!.$B<0"A`8T!Y@4H_@3/<(`` +M>"0`@`C@(G``&$`.%A``(9$.!)`)!X_XX'CQP+(.K_@#V8(D`CLZ<0CHJ0A0 +M`($(D``J=8SPSW6``%RMY8V+<#)O-'D`)5`0"!"0(``E1!`!V:8);_H*<@KP +M,F\T>3IE2(H`)40010H!!`AV)F@GV`"N#]@!K@#8!*X#K@>V"12```'G`JX* +M%(``P;\%KD`D``.J#N`&"-H0V3"N)HU`)D`40"%1)+4/09#EK<3QSW&``%RM +M5A&``%01C0"'N%89`@!4(<`*BW$#Y7(.X`:I"6_Z0(TC +MVD"H`MI!J,]R@`"@>6*2".)J8B)H0J@"C6&X"PC4`P#:(/`S)@!P@`!L/$`G +M@G(4>@!ZBB+^"1;PBB)^"Q+PBB(^"Q#PBB(^#`SPBB)^#`KPBB)^#0;PBB+^ +M#03PBB*^#@&-!]U">`&IG\=`)P`5)@QO^M#9JA2!,)7&R7"Y83!YM@QO^@#: +M0"<`%5'`4L:+<%/`4!Q$,ZH.;_J1P*T%K_B`)`([\',(:*`3_0AB)L)%0$S)D%P@`!`!XJ7"6"J`) +M!-D`B'L(40"I<(H*H`D(V0"(;PA1`(#9SW"``'!Z)K`PN2>P(-@K\,]V@`#A +M!0"..0A1`*EP7@J@"039`(@!V8+@P'D5"%``@N#,((*/``#^``CR`-@)\`X- +MH`&I<`7PJ@K@`:EP`*Z+Z!#8SW&``'!Z"'2&L3"\A[%2"\`)!06/^.!XX'[@ +M>,]Q`0#'`\]PH`#L)R:@X'[\'`BT\<`:<#8,K_\DV)AP42``@,HAP0_*(L$' +MRB"!#P``42;*(X$/```I`9P%(?K*)0$$SW&@`*PO&(&'"!$@#PB>!L]P@`!0 +M)`"`0'CTV`#9S@NO_P':--@`V9&YP@NO_P#:,-B*(08`M@NO_P#:--@`V0/: +MJ@NO_Q2ZP@NO_S#8PK@)"%$``-@#\`38SW(!`,8#SW&@`.PG1J'/"2__BB`%`X?H!-C1P.!_!!00-$39SW"@`,@<*:!: +M"F`)`=C6#4`!PO'QP&X+C_BBP2AV"B2`@`#?SW6@`"P@0!40$``1"Y!2&"#P``@OW/<:``["=& +MH00@@`\````?2+B&N!"X!2"`#P``0OT&H1"%`B``!(P@#XH+]XMQE@FO^8H@ +M#PT`%``QYP@>@`3P`(:`N`"F@<%^":_YBB!/#`04`#$-")X``(:!N`"FBW6* +M((\/8@FO^:EQ(,`(N`(!"X!2"`#P``0OT&IQ"%`B``!(P@#XH+ +M]XMQ!@FO^8H@3P\`%``QYP@>@03P`(:`N`"FSW`&``+_!J=`)($PX@BO^8H@ +MSPX"%``Q00*O^*+`X'C@?N!XX'[@>,]QIP"(20#:"PA1``/8#J$"\$ZAX'[@ +M>.'%X<;/=8``F"2@C0#>P*.1[8'@S"$A@`WR"PH3","C"?#`X@;8!O9"(@`( +M0[@"X`"CP<;@?\'%N'!`W``A`(/QP`X`)`"8<8P@`H"+]@HAP`_K/`(&D`@>+`(6D`B"`^`(D@P0^((3X`B2'!#X#@UB`K +M"(#AUB$K"'8)``#1P.!^X'CQP.8(C_BAP3IQ`-^`X,HAP0_*(L$'RB"!#P`` +MRA3*(X$/``"(`LHDP0`<`B'ZRB7!`\]Q@`"<)$"QSW&``)XDX+%,(0"@RB7. +M$V0`+@#*)LX3&G=:=P7PR7<:=6IP0"!3`(MQ`=K6".__`-L`%`TQ+R/()*EV +M*;W(OK_EV24I%$PB`*#*(,(#RB&"`\HB`@1,#2(`RB-"`\EPW@[O_ZEQ0B%1 +M(+4)=:!`(E(@R7`B"2``J7&)`*_XH<#@>/'`,@B/^#IPSW"``)@D`(@:<9D( +M$0`Y"1`@SW&``,0&I8D$B1UE,G7*(

"?``V2"OZ@[O_XK9`>;/?@`@@2^``,0& +M)HD!:3$.`Q!`*8`@%'BU>-1XSW.``!BP$&-2;6WH`N`0>-1ZSW.```RP4F/@ +MZ0'9W_$!Y:]]L0W2D.D'3_CQP(X/3_C/)."5 +M"_#,?]((K_A`*4!Q1;B*#>__"G$@E8PA$("T]ET'3_C@>/'`Y@Y/^`AUSW"` +M`)@D`(AZ<:'!&G+/"!$`.0L0(,]Q@`#$!L6)!(D>9G)VRB',#\HBS`?*((P/ +M``#,%,HCC`\``#P#RB3,!/P'[/G*)8P#2B$`(`#>"O`!'5(0!H^$Z""M`>4! +MYL]^SW>``,0&`"<`%`:(`>!C#B,0`G=`*X`@%'@5($`$U'C/<8``&+`T(1(` +M`-G%"A"@BW%*<`+:G@ZO_P#;"^@!%(`P`1T2$`:/VN@!%(`P`*W5\0HAP`_K +M0?O^4HE@`!`(5$@+R%')'D)TJ!=!F_XH<``VV"I +M$0AR`&"J#0C3`V"IX']@J@\(D@C`X`7V`=@`J1'PY."&]HP@`H/*(*P`R?:, +M($*$B?:,($*)!_8#V`"I`=C@?P"JX'[QP+H-3_BCP4HA`""+<2IP2B``(0IR +M]@VO_RISC>@*(<`/ZW)3V`:X^]L*)$`$Z0;O^0HE``0@PA,*$@``P$$H`0)3 +M(<0`%0P2`0'9SW"``)@D5P(@`""HSW&``,0&0*D"&0(!02@.`U,FQ1`#&4(! +M3"7`@,HBR0?*((D/``#"%,HCB0\```X!C`;I^$O!`*8$@-'D* +M%(`P%2%!`0'FSWX4>;EA`!D$!(`@`B,O(`@D`,!!*`$&P[D!X<4.0Y""P0IP +M`MJ*#*__`-L+%(0P+R@!`4X@A0&]"4.@,+C#N``@#@2"P:EP +M`MK6"Z__`-L+%(0P+R@!`4X@A06&^`>#E#G60#WAQ`V_XH\#QP/H*3_BBP4#`0<)` +M*!0%0"D7!0#=0"H3!4`K$@4!WDHE@"&I=P3P"G7*=P#`%;@3>!0@P`6B#&_X +M!]D"(%`#`B!`(Y(,;_@.V6)-I +M"A&@3",`H,P@(J`1\@#8$/`*(<`/ZW+/<```,1&*(Q<+2B0``-$"[_D*)0`! +M$]BE`4_X\<#AQ<]P@`!L)@@0!`!,)`"`RB'!#\HBP0?*(($/``!I&/'` +M621<.(MPSW&``"0]?@EO^(HB!`)*)$!X`-FH(``#%B1`,&&`0)`KV!*X`>%5 +M>&"@,'E6)%PXT<#@?O'`N@X@`$?8`-K/<:L`H/]9H0?8&J%8H='`X'[@?N!X +MX'[@>/'`SW&``&PF.(&`X1P.`@#1P.!^\<#/<8``;"8]@8#AA`\"`-'`X'[U +M!4`(\05`".T%0`@`V<]P@`"PN2&@@09@`B*@\<#AQ<]U@`"PN;(/8`*I<+AP +M`(42Z$HD@'//$*(<`/ZW+/<```AAF*(T0!#0'O^4HD +M```M`&_X*'#/<(``L+E`@".`#.K/<(``?"0`@$0IO@,-X#(@0`X)\,]P@``5 +M3D0IO@,R($`.X'X`V\]QJ@#P0V6ASW(``#\_1J'/<```/C\'H8H@$``(H0G8 +MC+@)H<]P```6'`JASW```!\?"Z'/<```'!8,H9'8!+@-H038#J'/<```/SX/ +MH5"A<:'@?N!X\<`B#P_XJ@R@`@#=2@T@``?84@[O_QIPSW:D`+@]K!8`%L]W +MI0#8RVG9H-JBN*P>&!"LIP'8]AX8$,]P%0`K*YH>&!#*'E@0P-C+'A@0Q!Z8 +M$,]R``!N;IL>F!"*(L0`GQZ8$!K:\QZ8$/0>F!!DVL@>F!"JVLD>F!#,'E@0 +MS1X8$#G9SW"E``@,/J`>#\__T@D@``IP&-B5'A@0SW&``'P;H:'(V`*A`*$# +MH<]Q`0"LTL]P@`",%=080`"4V`NG0=G/<*4`S'\MH,]PI``,@**@M08/^.!X +M\<`:"```)@_/_[(-``#N"P_[T<#@?N!X\<`J#@_XSW"``%S&0"`2!@AQSW"` +M``0&(*``WL2H!-]$+CX7"B%`+@`A@'^``%S&L@_O^1S9A"X*$@`AC7^``,RY +MJ7">#^_YBB$*`H0N`A<`(8!_@`#LPQIPB@_O^9S9`"&1)``90"-AOZ$=&!2U +M#W60`>81!@_X\<"^#2_X1"@^!P`ACG^``%S&$*X!W_&N(:Y`KF*N`QX"$7*N +M5@D@`!,>`A'Y!0_X\<"_X1"@^!QIP.G'/<8``7,8OVXHDPP^M!J_Y"B4`!!4(T"#/Z:!`9"X>*>!8*:D>*&& +M0"$/!*5X`:8=Z@&!`AS$,#"[!!S$,``#>_Y`"5`'IH-H`'X90()(`#)<(4$#_C@>/'`&@PO^`':A"@"!P`AC7^` +M`.S#SW"```0&`(`H=B"(`(:9'8(03(4PN`]XB0I1``/:F1V"$($)`0`$V)D= +M`A`0A8WH!=B9'0(0"(82I0.&$:45PAI8`"B5PQI8`"F5Q1I8`"J5HQI8`,]Q@`!, +M'2.!*($`WAD)'@!,E2N56WI%>5,;6(`ME50;6(`&\%,;F(-4&YB#+I56&UB` +M+Y58&UB`,)55&UB`,957&UB`,I5:&UB`,Y5<&UB`-)59&UB`-95;&UB`@@W/ +M_[$"#_CQP(0H`@?/<8``8#$HD5,A08#/:EPY[@GN%(@``#*)2(0RB%"`\HAX0'`N!-XPKC/ +M__>:WR">__`Z4$I<]PH`!X +M10"`!""`#W````!!*#Z%`-GU]<]PH`"T#SR@SW*K`*#_.:('V!JB.*+^"8`" +M@.8!V,!X#.":#J_Z`=G%`0_XX'CQP$()#_@(=\]U@`"PN1B-2'8Z<1IS$PH! +M`(7N&8T+"`$$`-@"\`'8+R('(.EPQ@Y@`LEQ((4`V`\/01`AA3)QS"(AH`+R +M`=@O)@?P&JT7\NEP*G')`S@#@ZO^@'9%0$/^/'`"',`V0+:A"L*`@`A@'^``,RY +MA"D$#P3@J@[@!2=P8;KI"G6``>'1P.!^\<#/<($`4!4Z"N_YBB$)#,]P@`!L +M("X*[_D4V<]P@`"0(R(*[_D4V='`X'[QP%8(#_BBP3IP&G$`W8X.[_\'V)IP +M`MFI<%IP>G$`VS1H`G$H=10A`"!H8MP0B-!(+\)=8!`(D`@0@OO_XIP00`O^*+` +M\<#N#\_W.G!:<<]W@`!PY@R/SW:``+"YI8:&(/\!0[@.)0V0SW"``&`D((#* +M)6(08'D$V"#H&HZ`X,PE(9`<\@#8$-T:<`*X%7C'<(``\"4@@`;I(H`5Z6!Y +M*G!AO>D-=9!`($`@`-@:K@R/AB#_`4.X!:8V#F_X2G#1!\_W"B'`#^MRSW`` +M`&49-]L*)``$N0"O^;ASX'CQP$*0(9!@D!"Z17DIVA*Z%2+#`""C`)#P(@`` +MT<#@?O'`X<7/<8``S,8`@0#=%>C/@S`!L]P@``4*9X*H``#@,]P@`#4+'$'[_>OJ.!XR09/^O'` +M(@H@`^'%SW"``(0W2@S@!@#=SW"``*`W/@S`!L]P@`"\-S(,P`;/<(``%"FB +MH+X*K_H#V,]P@`"<+*.@*0?O]Z&@\<#AQ<]QH`"L+QR!O8$$?<]P@`"('`"( +M$PA1`,]PP-\!`!RA*-D8N0GP_+TX"P(#]KW("X+Z`-F;N<]PH`#0&S&@X0;/ +M]^!X\<`B"```H@X``-'`X'[@>,]P@``4*0"`@>`!V.!_P'CQP$8.S_?/<(`` +M/,?'@,"^@>8!WL]Q@`"`+@"!P'Y1"%\`@;@`H<]UH`"L+QB%#0B>!AB%NK@8 +MI0+8%J7/<(``8"0@@&!Y`-@9"!$"T@R@"`K8"_#/<*``J"`-@.3@BO<5A?4( +M'H`&">__R7!%!L_W%,@8,'/ +M<*``K"\9@(8@/@,#\@#8`_!_Z`'8+R`'@`?R`=K/<8``S`5!J>!^\<#AQ<]Q +M@``4/4"!(8&GP4;!SW&``!0I(H%%PA4D0C#/<:``+""P@<]Q`0!0%D#!`=E! +MP4+!$-E#P43`18(`V`S9"'.8<+AP`"6''P```'TF#>_\V'"M!>_WI\#QP*H, +M#_[/`<]Q@```$Z&1SW&``(P&P($\Y;EF9.$?"00$"B'`#^MRW66*(,P( +M6ML*)``$P05O^54E118A"-$`SW"````309#/<8``C`8@@3SB66%DX0(@4"`+ +MR`0@@`_^__\#"QH8,`O(A[@+&A@P$@^O_^EPSW6@`,`O20]1$!"%-0@?`,]V +M@`!H)""&8'D!V`\(40$@AF!Y`M@=")``0!4$$`HAP`_K`@^#, +M(.*!!O1`*(`@G[B('0`0R@O`!L]Q@`"`+@#8V0/O]P"AX'CQP`T)40#V"P`` +M!/#2"P``T<#@?O'`:@F@`.'%>@BO^AK8SW"``"PL`)#/!X\<`""\_WSW6@`,`O%X4:A<]VH`#('X@5`!`'V!D>&)`! +MV`AQ"'((/'`]@S/_X#@`-G*($$`&?(N"6`'*'#/<8``=`8@@8H@3`;& +M#&```]H:#F```]CZ#$_\"-@B#"``BB'_#P'8T<#@?N!X\<#/<(``=`8`@!,( +MT0"Z#8_\]@O/_^H.``#1P.!^X'CQP$8*[_=*)$!Q`-[/=8``G"Q`)0,.'%SW*``)PL1!*``$`B`PPG"%$`2B1`<0#9 +MJ"```_`C30#/<(``."PU>`'AH*`O>0#81!H"`.!_P<7@>`#:SW&``-0L3ZD! +MV`VI3JE,J5"I4:E2J5.IX']4J>!X\<`*">_W`=K/<8``3!UC@7B+.0L1`0"! +MSW&``!0IQ!``!B6X4B```"&!P+@!VH#ASW&!`#`3)H'`>H#AS"`A@,PB(H!\ +M\H#P$0@>`,]P@``DQP"("0A1`)AR!/!*)```SW"@`"P@<(#/=H``G"Q%AJ:& +M`B.`@`#:RB)O``(C3X,`W2&B +M`-G/<(``/,`88-(`#)<`7P>@I@``.%30>O]Z'` +M\<#:#H_W"'8`W<8-[_\H<(#@RB!!`PP)XO_*(((#*0>/]^!X\<#/<(```)8( +M@$4(WP'/<8``+"Q"@2&!SW"``(`L0*#/<(``G"PGH,]Q@`!T!B"!BB!&`+H( +M8``"VA(*8``"V%(*X`4"V/H+#_H(\,]P@``4*?H)8``#@-'`X'[QP%8.C_A>@_`!GX(;_P! +MV#()[_^I<`'8D@]@`.EQSW6@`*PO'(43"%\&&(6(N!BE%@_O]Z#8!_#/<8`` +M@"X`@8*X`*'""L__4@I``-"&/@AO_`'8^@P``(H,8`@RV,]P`((!`!RE.@TO +M^@(FP!,`V#H/8`#)\,]Q@`#,Q@2)'0A1``6)%0A1`,]P +M``#__SX.[_\`V9T($`#/<8``3!T`@<00``8-"%X!`X$8B!L($0'/<8``=`8@ +M@8H@Q`0>#R```MH"V#3P_@I``&D(A`\``!0$SW"``!0I`(`-"%$`K@H/^BCH +M`-G/<*``+""P@,]P`0!\"D#``=A!P$+`0\%$P0;9"'(`VYASN',`)8EP.!XX'[@>/'` +M8@RO]P#9SW*@`"P@T(+/<(``G"P(@,]W@0`P$P(F#1#/<(``),?EAV.`!2_^ +M$#=U`=N@B,(CS@"EP0L-4!`#B",(40#/<(``%"DCH,]Q@`!T!B"!S]A&#B`` +M`-H`V$?PSW6``$P=`(7$$``&APA>`8,+40`#A1B(>PC1`,]P@``4*0&`M^C/ +M<(``>!L`D('@`=C`>`RX6PB`#P```!"P@L]P`0!0%D#``=A!P$+!$=A#P`#8 +MC+A$P"AP#-D!V@ASF'"X<``EAQ\```!]@@NO_-APSW"``)PLSW&``'0&(('( +MH-C8N@T@``C:"-@.#P``V0.O]Z7`\<#AQ0#9SW"@`-`;F[DQH,]P@`"8(P2` +M.PB>`!X+3__/=8``3!U-A3Z54R(``$X(8`0!VP"%Q!``!AL(7@$#A1B($P@0 +M`<]P@``\QP>`$0C>`,]P@``4*0.`#/#/<8``=`8@@8H@20<^#2```MH"V)(. +M``!M`X_W\<#R"H_WSW*@`"P@,(+/<(``P!L$@,]V@`"<+`"`H(8"(4,#UW,` +M`*`/`-_+]\]S@0`P$Z6#U;A!+8,08GT+"$0#`8:@Z&.&X:9O"U``SW6``,S& +M`(4BV",__!(7EI>.FH+@$I=H(+_H` +MV!OPD@\O^@?8%_`%ZP:&`GDG"5(`4R"`P02F#?3/<8``=`8@@8H@2P*&#"`` +M`MK:#2```MBE`H_WX'CQP.'%H<&*(/\/.@P@`$#`80A1`,]P@`!,'0"`Q!`` +M!B,(7@'/<(``>!L`D,]Q@`!T!H'@`=C`>`RX)0B`#P```!#/<8``=`8@@8H@ +M10/'`X<7/<(``3!T#@!B('P@1`0HAP`_KB#V_\ +M`=C/<8``=`8@@8H@10(*"R```-IB#"```-@Y`8_WX'[@>/'`O@B/]U(*P`;/ +M=8``$!<`A<]VH`"L+Q\(D``8AA<(G@8:AE(@```+"!X`'(8+"!X'*@J/_QR& +M-P@>`,]P@`"$-P"`0B``@,H@8@"1Z,]R@``L+`F"&P@5`<]Q@`!,'2"!Q!$! +M!@L)7@$!X`FBZ@J/^%(-S_T;"%```(43")``SW"``'0&`("#X,@,P?^=`(_W +MX'CQP"H(C_?/<(``%"D`@"T(4`#/=8``=`8@A8#AS"'B@/'`M@]O]P;8J@L/^L]P@``D +M%$HD````&``!SW"``$P=`X`8B!<($0$*(<`/ZW**(,P-ZMO-`"_YN'//<(`` +MQ#7:#$`&SW"@`"P@T(#/=8``G"P@A0(F0!`1#@)P```@3G8,+_H'V,"ESW"! +M`#`3!H!1(`"`8`PB^LH@(@(6"Z`%`-BA!T_WX'CQP.'%)@LO^@C8SW6``'0& +M`(6'X,P@(H(W\L]Q@`"<+$&!!^K/`!V,!X#+@W"($/````$!(-[_\!V,]P@``4*0"` +M:PA1``.%@>#`#.'YRB!A``.%@."T#.'YRB"A`"?PCNG/!L`D('@`=C`>`RX(PB!#P```!#/<(``%"D!@(GH +MBB"%!D8((``"VIH)(``"V'4&3_?@>,]R@`"<+`&"`-F%Z`."@.`"\@'94R"` +MP02B`=K"(H$``-B`X.'%X<;/=8``)"G`%0,6$PO5#])K +MU'Z^9@"F(:9"I@%KQ;C`'1@0P<;@?\'%SW&``-0L#8DS"%$`SW"``"PL`)#I +MN-$@HH((]`#8#JD-J14$K_\/J0'8#JD/B4(@`(`E`Z__RB!B`.!^\#4_W +M_@F/_\]S@``L+`"3SW*``"3'02B!`,"Y(:K/<8``>!L@D8'A`=G`>0RY'PF! +M#P```!"B@\]Q@`"`+*"AH8//<8``G"RGH3;PSW&``!0IH($I#5$0SW:``,S& +M)(X/"5$`)8Z!X0'9`O(`V8#ARB&"#P``$"<#]"*#SW:``(`L(*8I#5$0SW6` +M`,S&)(T/"5$`)8V!X0'9`O(`V8#ARB&"#P``$"<#]"&#SW6``)PL)Z6I<<]U +MH``L(-"%Y8$")LT3"0W?%\6AYH$")LT3"0W?%\:A*(.&Z<]Q@0`P$RB1(Z(E +MN,"X=@_O^0/9U01/]_'`SW&``'0&`!$$`+APSW*``.PL0"R``!9X%2!``0!B +M%0A1`0HAP`_K_X=ML`&4`!P0B0`#T($0'/<8``S,8`@;$($`#/ +MX"QH8,.SQSW"``)@C!(`A +M")X`SW"```@P`("*Z/8)[_V0V`T(40!^#L`##_``VIZZ`-G/<*``_$1!H.!X +M(:#/<*``M`\\H,]P@`!,'0.`&(@+"!$!A@M/_83HH@]``L#QOO'@>/'`<@M/ +M]\]UH`#`+SJ%SW*``(`N`()W"!\`@+@`HL]P@``\Q\>`P+Z!Y@'>P'X-"1X' +M$(4)"!\``-@#\`'8#W@?[C"%'PF?`D`5!!!,%040"B'`#^MRBB!,"64$[_B* +M(X4&$PA1`(H@$`$1I=X)(`@*V(H@$``2I=()(`@%V,]P@`!8)""`8'G)<%4# +M3_?@>,]Q@``4*4"!)PI1`,]S@`#,Q@2+#0A1``6+@>`!V`/R`-B`X,H@@@\` +M`!`G!?3/<(``+"P"@,]S@`"`+`"C*0I1`,]R@`#,Q@2*#PA1``6*@>`!V`+R +M`-B`X,H@@@\``!`G!O3/<(``+"P!@,]R@`"<+`>B`0;O_P.!X'C@?N!XSW*` +M`"PL()(#BH"YI[BBN8:X(++@?P.J\8((``((#@RB!,`P4";_>AP.!X\<"AP0#80,#/<(`` +M),0\.17!.```@7@D/_0/P/@D/ +M_1$(D0"*(/\/H<#1P.!^SW"``.@X`X`@@`#`(GB`X,H@+`#S\>!XX<7/<8`` +MP!LD@2"!SW.``.@X0X/5N:""1H.*(/\/@.(%\@*"HGA((```"2!``&JX2"`` +M`.!_P<7/<8``Z#@+@4"`#H&`X,H@@0______"O("@$)X2"```)D@!@!((``` +MX'[@>/'`M@A/]Z'!"'?/<*``+"!`$!(`SW"``$3)7X``W40G`1.(X4$J@`$: +M`!B2PA>`\]P@`!$R9@0@0`2:19X +M&F(K#QX3`(*(N`"B`=@/JL]P@`!,'0&`P!``!A$@0(#,(:*#B`H""`?PD@H@ +M"*^J@."`"@((SW"``'0&(("'XG/<8``S,8*@0'@ +M"J$F\$86@!!%"%$`SW#M_KZZ0,#/<(``U`4`@(#@RB`!!\HA(0'*(H$/``!] +M`,HC80]0#^$$P"OA!48>0A-%'D(3$@YO_T<>0A,2#(_Y!X8FAD)P`B!"``D* +MWP<&IDP6@!`-"%$`3!Y"$P+P`*:9!R_WH<#@>/'`0@\/]PAVSW"@`"P@\(`= +M#O(1`-T*(<`/ZW**(`T"BB,)`YAU;0#O^+AS-PF0`>X.C__/<(``G"SHH,]P +M@`!T!@"`@.#,(.*!"?+/<(``%"D!@(#@8`N!^<]P@``L+*F@SW"``(`LSW&` +M`!0I(('P((`#^&`/"5$`SW&``"PLJ:'/0"!%PB%`,]P@`!,'0"`Q!``!@<(7@%`H>!^\<`R#B_W`-G/"SW>G`!1( +MQZ?(@@]XT*?C@L]VI`"X/9L>V!/D@J8>V!/E@I(>V!-&@J,>F!#//'`5@T/]\]QIP`42`#=J*$'@<]V +M@``F$('/ASW#S#__\$*&@V+:AFKCU&A@`SW&D`+@]FQ$` +M!L]W@`"X&P.FIA$`!@2FDA$`!@6FHQ$`!@:F_]B;&5@#IAD8`)(9&`"C&1@` +MSW&D`.S_SW```/__IZ$&H0"'`>``IQ4(40`!V<]PH`#('#&@H@O@!RAP!-BR +M"R_X0"8!$@W8I@LO^$`F@1+/<"@``@'/<:``["<&H8H@C0`&H0"'0B!`@`"G +M!?3/<*``R!RQH.T$#_?@>/'`?@P/]U$@P($-$@\VSW.``.C'`Q(--L]Q@`#X +MR/1[$8L0$X0`$O(!X`AR,A6%$&>1`AD"`<]V00"#`&:QSW.``"PT`ZD1\$`D +M0@`Q%8400JG`$P,!`ZG/=B$`@@!FL<]S@``P-!,-A0#$H0"#`>``HP2!4_#/ +M!<]P@0`(#79X`8@#\$AP`"2/#X$`"`UV?^2/"";.$P@F`!"@<$D@S@,6 +M:]5XSW:!`(@.`&;/=H$`"`YV?F&&SW:``$P=Q(;8AL5[!".##P````AF>`+P +M`X4"H9@5@!!HB0T+``!$J6#8&+@$\`#8G;@$H/'`X<4#R*00``!1 +M(`"`SW"``$P=!(`$\AN0`_`:D.H)@`:[Z,]PH``4!`/9(Z`@V`P:'##/<8`` +MM#(6@0'@%J$#R`#:F!`!`*00`P"4&$``GA`!`:R[DAA$`+X0`0&MNX`0#0&D +M&,``D!A$`'X0`0&`&(0`/66P$`$!HGDP>;`81`""$`$!?AB$`(8CY8^R&$0` +M[`M"_4$##_?@>/'`P@HO]PAS$(DS$8T``=I`JPT2#S;/=H``$,CN9L]R@`!` +MR$C@2Z17[`"5"'BB!0"(#!P`E0!Y/(4\#Z*("B&&F#0A>`(.YC;DHHL]P@`"D +M!P*C&-@"IL]P@`!`!P"`#!X`$<]Q@``H!YX*K_H@@0#9SW*@`"P@4()AA@L( +M<@!*HUA@"J//<(``(@<`$(<`A"\?`"=U0HTO<+$*7P`9"!$@SW&``'C.&V'/ +M<(``-`=@H$KPSW*``#0'0()!BD0HOBC'<(``7,L7X$`BA``R($(.+R0'`<]P +M@``X!P+B3WH`$(4`00IR``(E@P"$+Q\`+W``(4\.`">&'X``7,M$*+XH0":/ +M!3(G3QX[8\=S@``TS@CC&V,!X2]YX*O1":*``B6#`(0O'P``(4`N&V//<(`` +M-`=@H`Z5`B```0ZU#I588`ZU9:8'\,]Q@`!4SCA@!:8.E>4`+_<$IN!X\<#A +MQ0AU!HTEC0BX!7DO>"BY+WD(N*>YK+BLN25X#WDHN`BY)7@%K2BX!JT`V2AP +M81T"$"BX8AT"$"AP!ZTHN`BM0"5`%!X*[_@@VD`E0!P`V1(*[_@0VJEP0>`` +MV08*[_@(VJEP4>``V?H)[_@0VL]P@`"`!F"`02L!!"]Y"+E!*P(&17EO>BB[ +M"+IO>V5Z$+I%>2FM*+DJK2BY*ZTHN0&`+*U!*`$$+WE!*`(&"+E%>0]Z*+@/ +M>`BZ17@0N"5X#:THN`ZM*+@/K2BX$*U?V`BX+0`O]P&U\<"J#^_V`-G/=X`` +M@`9EER,+<@"")`(XSW6``""/SW*``"!X*F(\90'A+WGS">*`0*Q$ER$*<@`` +MW<]S@```C\]Q@```>*EAO&,!Y:]]]0VBD""L;I"PXR(!)0``WHMRV@S@!\EQ +M@<64P4`E`!>J#R`%$-I`)0`7R7$2">_X$-H"%``QBW)6)`0S#WDHN`BY)7AD +M:&][SW"```"/_@KO_"B/2B0`=,EPJ""``P`D`C``)`$P8!&!`%`2@@"_"D$` +M`>`/>"*-`8T(N25X#WXHN`]X"+X%?@67G,%`(!`$0"7`$B\@!R0R#R`%$-I6 +M)`$TSW"``""/(@\@!467G,*,P,EQS@Z@`0IS^XW/<(``_`7/<8``^!)@@>A@ +M`[@5>!!CPKA?"%$!SW@C"!,$3B`\!*@@0`,"(($#`"0",(-Q,!&!``'@#W@P +M&D(``">-'X$`L!(DC078`[DU>3MC`+,DC<]P@`#X$D"`C,`#N35Y$.)98:8. +M(`40VB?P`-@]\``GC1^!`+`2)(T!V`.Y-7D[8P"S)(W/<(``^!)`@(S``[DU +M>1/B66%R#B`%R7+/<(``^!(@@$2-$PY1$P.Z57I085EAC+@`L42-SW"``/@2 +M((`(OP.Z57I085EAY7@`L42-SW"``/@2((`#NE5Z4&%988.X`+$!V!D&[_:` +M)`(XX'CQP!(((``"V/8)``#1P.!^\<":#>_V2B0`<@AWSW"``$P=%2#0`P`0 +M#2``WLEPVJ6H($`-SW&``&Q+]"$"`,]Q@`#TK!1Y0+'/<8``?$WT(0(`SW&` +M`"2M%'E`L<]Q@`!\2_0A`@#/<8``!*T4>4"QSW&``(Q-]"$"`,]Q@`!,K11Y +M0+'/<8``9$WT(0(`SW&``!2M%'D!X$"Q"(4+"%X!!-DTI0+PU*4/"!X!"=E& +M'400+MH%\!391AU$$#+:6[59C5EA,'E&'400&N$ZM1<('@`*V%0=!!`&V%8= +M!!`'V`?P$-A4'0005AV$$P78#Z5Z"J`#Z7`\C2AP1!U"$(8@`P#FN5@=`A#* +M(D$`"_)0(<,!;WI$'<(04"##`6]X6!W"$!,)7@%(D0=PA`-"1X! +MI;A8'0(0"PG>`*2Z1!V"$"\/D!"V#*_YZ7``$``@N1``!E$@0(#QV,`H(@'* +M(($/``"3`,`H(0&$'0`0&-B-N!.E"(51(,"`SW"``$P=!?*V$(``B;@#\)T0 +M@``2I<]PH`"L+QF`SW&``)@C,+C`N)(.8`<%H0B%!""^CP`&```+\C:XP+@; +M>`'@6AT$$`+8&J4#\%H=A!,`V!>E&*7>#6_]Z7`HA0':2'-!*0`%-;E2(``` +M4B$!`,"XP+F.#>_]F'(5!,_V\<"R"^_V!]C/=J``R!]('AB0SW>``$P=(X?/ +M=:P`U`$:@4P>&)""X`+8RB#B`-`=`)"*(`0`#Z9&$0`!L!X`$$81``&T'@`0 +M']@(N`ZF"(%1(`"``-B+N`KR$*:""(_YSW"@`*0P`8"$N`KP$:9V"(_YSW"@ +M`*0P`8"DN,]QH`"D,`&ASW"``.@Y`(`5"!X`AB#_#B*X%+C/<8``1`4+H3X+ +M3_G."T`!G@F``QX*@`//<```555:'AB0`=E9'EB0SW"F`"@`+Z`#AUH0`0'/ +M<*8`Z`!+X*X``"V`3P%@\@!@'8SW*@`,0G#Q(` +MAF.'1"`!`AN##QH8@`\2`(:CN`\:&(`/$@"&!7D/&EB`/(//<*``,!`DH,]P +M@`!$R1!XCQH8@,]P@``@D,]Q@``@H!!X$+DE>)`:&("*(`0`DAH8@!V#0!H` +M@,]P@`",'%,:&(`/$@"&G[@/&AB``-@0&@"`'H,<&AB`=0+/]O'`^@G/]FZ0 +M"'?CXY@D03,:<9/W`-F"#Z`'BW*!QB*.`8X(N25X#WHHN`BZ#WD%(9$`#0C> +M`*$#(```V`D($2#W")^!42'`H=$A(J+1(6*B\_,+"!`@XPD>HUX6@1!=%H`0 +M"+DE>`]Y*+@/>`BY!2!2`":.!8X(N25X)XX0N25X*(X8N25X02@!!"]Y02@" +M!@BY17D/>BBX#W@(ND5X$+A*C@5Y"8X(ND5X2XX0ND5X3(X8ND5X02@"!$]Z +M02@#!@BZ97H/>RBX#W@(NV5X$+@%>L]S@`"`!@PHD@%,AA"`-"@$``(-/ +M"02`(*-!H\]S@`!P>B"S,+DALT*S,+I,)$"`0[.?Q<#TR7!-X%8E@1)B"2`% +M$-K)<$W@`-G*"J_X$-J+<"&0BW(O>`BX*+DE>&1H;WO/<(```(\0V;H,K_Q6 +M)0022B0`=`#8J"```QIE&65`$8$`4!*"`#!R;?4!X`]XZ7``V8MR+@Z@!VZ7 +M0"9`&U8E@1/Z""`%$-I6)-,WSW"``""/0",!(>8((`40VI<($"!6)8`3(-F* +M(@0`BW-CXYX(H`$*)(`$BW!CX$`C`26^""`%2G(R)(`_```,`8P@0X`7]3(D@C\``!(!0",`)T`E`1A3(E``5@@@!4IR +M%_!6)8`3(-F*(@0`BW-CXPX(H`$*)(`$BW!CX$`E`1@N""`%2G)$(0`L0B@0 +M`4`E`!@*<#Z__BW#/<(```(\0V8MR8]M^"Z_\ +M5B4$$U8E`!.9\,EP3>!5)4$5X@_@!!#:R7!-X`#92@FO^!#:BW`AD(MR+W@( +MN"BY)7AD:&][SW"```"/$-E>#*_\5B4$$DHD`'0`V*@@@`,:91EE0!&!`%02 +M@@`P``@C0^``""L")53(!``SW"``"26UXB.[L]Q@`#TC@N)AB#_C`3R +MP(D&\,]P@`"PN<&`X@C@`\EPSW*``$3)5A(!`<]S@`"2R4`A!`O/<8``(@?@ +MB<]Q@`!*RX0O'Q`T(4$.AB%_##QY%"-!`(P@_X]@B0(DP0`-\@#;$0ZU$P\C +M`P`)E65X";4%\`6%97@%I0T($"`'E1$)!0`GM2."!((AI0*EQK6I!8_V@.#Q +MP#38!_0"#8_]4"!!!`7P^@R/_4\@000R#:_]--C1P.!^@.#QP/38"/3>#(_] +M4"`!`/38!_#2#(_]"''TV("Y"@V/_='`X'[@>,]P@0!D&B"`SW"``,"9!.D@ +M@`/P(H#/<*``+"`0@#A@SW&``/P&X'\"H>!X\<#.#(_VSW*``*@P8I+/=8`` +M'#U`A:3!0,)"E=@1!`#/=8``,#\$'(0P0(5"PD*5#!R$,+2`7($+"F0#V'(" +M(D8#4X"W@5!UPB6&$,]R@`!@,082!0'($0X`_]H(ND1^*+X`'(0SS!$.`$1^ +M*+X"'(0ST!$.`$HDP'#$>BBZ!!R$,.P1`@`('(0P\!$"`/01`0`*'(0P#!Q$ +M,`#:2':H(,`#`]D5"8X#%"2!,^"1""+"`R21"2-#``'F`"6!$04I@0\#```@ +M+W$%+3X!#0E%#HP0`0`!X0/P`-FP@(P80`!5(,X%4R7!$!0F01!`L5P0`@%* +M)`!T`-^H(``"]";!$S!RRB)+``'G52#!!Y`8A`#"O10A30-@M7P0`P%*)`!R +M`-JH(``"]"&-``@C0P,!X@;C<'N2&,0`_0.O]J3`X'CQP(X+K_:8<&]_`[^D +M;\]P@`"H,+9@HF^U8`;G\&`1"U$"ZI&,)P*8RB!K`!4*40!T>4&1'64C"D(# +M`GI!L0KP%PJ1`'1Y09%88`\.`A`!L0'9F!Q"`*$#C_;@>/'`,@NO]@#9SW"` +M`"2652!$!V@0``&`X,]P@``DEE;R:A`"`3T*4@#/L```/S">"G_(``%`,]#B^PKX!X2]YSF7?":*`Q7@!HP#9!/`! +MHP'9@0D1`,]P@``DEFP0#0%U#7(0`-K/F5(DGUE"P@!`8.Z2+7/@BUSW"``"('`(A^$8,`A"@?`,]P@`!" +MRS(@0`X+(,"`!?)/(H`!$'H(M?CB#/3/<(``;)9,]R@`"PEAEB+8D`(@4`$V\5>``@@@^``,"961*&```@@@^` +M`,"9$F\5>,]U@``@K!ME:),=90L)@0&#NVBUSW&!`'P:UGE\$H``!!$$``L@ +M`($&\D\C``$0>PBU?1*``""!"R!`@`;R3R-``1!["+7/<(``(@<`B'X2@0"$ +M*!\`SW"``$++,B!`#@L@0(`&\D\C@`$0>PBU^.,8]$`E``15(L$%/@IO^,AR +MD.@(E8*X"+6&"N__Z7`(E0'9@+@(M<]P@0!D&BBH`>;/?L]P@``DEI40@``I +M#@*0`>?/<(``P)D5@.]_%0\$D,]P@`#\!NH);_@(V<]P@0!\&MX);_B`V<]P +M@`#`F76`)PMT``#9SW*``""LLFFU?;AB")!=90'A+WF&(`$/[PGD@`BU:0"/ +M]N!X\<#Z#T_V=(!<@=@1#0!P.'%X<9T@%R!V!$.``(BQ`!3@#>! +M/!`'`(P0"``"(84`SW&``&`Q`MKT(8H`!A$)`<]U@`"H,$*5894*$08!DA`! +M`:.5""-#```E`0$%*8$/`P``(`HD0`X%*KX3+0D%<2J0C"$"B-+V`MDOH)`0 +M`0%^99081``AD&<.0Q"B>2&P`=F8&$(`+?`%*;X3+0Q%#BD/D`"0$`$!E!`. +M`0+A#0F"`XP@`9G*]\&0`=G=93,*8@,OH*&PY_$;#Y$`H9``)L$`$PE#`P(E +MC1&AL`'9F!A"`)`0`0&4&$0``-DOH`DBP@`AD`T*0@!!L`'9F!A"`,'&X'_! +MQ>!X(F@`VD"P2B3`=<]P@`"H,*@@@`(6((,`8),4(8P``>)/>F"TX'[@>/'` +M:@Y/]G*`R('8$0(`<';")L80<8`C@7!QPB'&``#?SW.``&`Q]"/$`P'=V6$% +M*8$/`P``("]Q!2R^`/0C0P,-"65P[:`"V2V@!_`%*[X`!PE%#JV@?09/]N!X +M2(%2H$.!4:!<@52@-X'@?S.@X'CQP/X-;_8"VPAU*'8!V`"QJ7!>"N__385- +MA:EPR7%2"N__`]M-A:EPR7%&"N__!=M-A:EPR7$Z"N__!MM.A:EPR7$N"N__ +M"=M.A:EPR7$B"N__!-L5!D_VSW"@`"P@$(#/#*(<(/RB+"!\H@@@\``*@3RB."#P``D`'*)$(#_`7B]\HE +M0@//=H``N!L`A@'@`*87"%$``=G/<*``R!PQH&8+(`!"XA2"-`,]QH`#L)P:A`(9"($"``*8&],]PH`#('+&@P01/]O'` +M4@Q/]L]V@`"X&P"&`>``I@#=%0A1``'9SW"@`,@<,:`*"R`'*'#/0+8 +MB^@@AF!Y`]B'Z)X*;_U0V`L(G@$`V`+P`=@O(0<@SW"``)PNSW>``'PD0@CO +M^0"GSW&``"@T%($!X!2ASW&``+@;`($!X`"A%0A1``'8SW&@`,@<$:%J"0`' +MSW&``&PF!($K"%$`)H'/=J``["=@>0#8SW"``+"Y&(B7Z,]P`0`&`0:FSW`2 +M``8$%O`*(<`/ZW+/<```AQF*(\4)2B0``)4#[_<*)0`!SW`!``"'1"B^`\;8DK@&IB"%)W=@>0#80#84PB0!,]P.0`",P:FSW`Y`(),!J;/<#D` +M`F8&IL?8E;@8\$0HO@,`(8]_@``(3L?8DK@&IL]P```",P:FSW```(),!J;/ +M<````F8&IL;8E;@&IHH/#_[/<(``L+D8B,]Q@`"PN3X/(`0@@2\)$"#/<``` +M`FX&IL]PP0!";@:FSW`#`,)N!J;/<#8`0I<&IL]P`@!":P:FSW`0`(=R!J8% +MCQ"X!2"`#P``0G`&I@2/$+@%((`/``""<`:F`X\0N`4@@`\``,)P!J8"CQ"X +M!2"`#P```G$&I@F/$+@%((`/``!"<0:F"(\0N`4@@`\``()Q!J8'CQ"X!2"` +M#P``PG$&I@:/$+@%((`/```"<@:F`8\0N`4@@`\``$)R!J8+CQ"X!2"`#P`` +M@G,&I@J/$+@%((`/``#"0#8)0@0`R"%8'D`V!D($`0@A6!Y`-@1 +M"%`$((5@>0#8$PB1!`R/$+@%((`/``#"?P:FSW`!`$9J!J;/=Z``R!^D%Q`0 +M%0D0(,]P4`#&0#8*0@0!""%8'D` +MV"$(4`0@A6!Y`-@5")`$SW"```9T!J;/<(``!W0&IL]P@`#&"*`&>8LDV!C9S@[@!C/:+PA0`,]P@``H-%`0!`#/<(`` +ML+D,$`4`"B'`#^MRSW```(H9^0#O]XHC!P0#8(0@0!""%8'D`V!D( +M4`0@A6!Y`-@-")`$SW!E`,)N!J;/<(``N!L`@,]Q@`"X&T(@0(``H03T`-A1 +M'QB0+04`B$`I` +M(A(&0"(/"$`B#00Z8D`G`7(4>0!YSW&``)@G2'!5\,]Q@`"X)P1J4?#/<8`` +MV"=`(@`"2_!`(@`#SW&``)@GC@DO_@#:!(;/<8``N"<$N!1XN&`[\$`B``?/ +M<8``F"=N"2_^`-H$AL]Q@`#8)P2X%'CX8"OP0"(`!<]Q@`"X)TX)+_X`V@2& +MSW&``-@G!+@4>$)P&_!`(@`)SW&``)@G+@DO_@#:!(;/<8``N"<$N!1X`G`: +M"2_^`-H$AL]Q@`#8)P2X%'@B<`8)+_X!VNX(+_YJ/'`SW"``/`E +M#X`1Z,]P@`"PN02`SW&``#"Y`K@4>#A@SW&``/@G&@T/_M'`X'[@>/'`>@TO +M]D3:SW"``,1-SW&``)3'R@U@!`#>`MT6""``R7!AO?D-=9`!YKT%#_;@>/'` +M0@TO]@#:SW&``$P=%7E@@02X`""0#X``1$NY&Y@``($$$`\@SW:``,1-OAC8 +M`Z"!0H:*(`/'`O@PO +M]A+9J<$(=FX+8`:+<$HD`'$`VJ@@@`(6)(`P*(@+"9(`8;DHJ`'B`L(!P\]U +M@`!,'=5]`(6*(0`3@`@TO]@C: +M`,`@A;D9&``@A;D1``85"!X`OAG8`R"%OQ$`!H"X"/"^&1@$((6_$0`&H+@B +M"Z_\OQD8`(3H$@N/_`3H`-@#\`'8$':\">$&RB"!`P"%N1`!!E$A0(#QV<`I +M(@'*(8$/``"3`,`I(0'*#:_ZA!A``"4$+_:IP.!X\<#""P_VSW:``(`[SW6` +M`!P'$ND@AHWI`*7*"._X#M@F#&_^BB`0``'8`*8.\""%)7@+\(H/K_@.V/(+ +M;_Z*(!```-@`I@"EY0,/]O'`9@L/]L]Q@`!$)`"!H+@`H=8+;_L!V,]P@`"T +ME0`0!`!,),"`RB'-#\HBS0?*((T/``"!#,HCC0\``-H`A`2M]\HE[0"G#'0` +M`-T4;0`@@0^``+25!Y'&D>21$+@%?@610Y$0N`5_`I$0ND5X&G#2#^_WR7%: +M<,]P@`#,5/`@00-$+3X7"B%`+@`A@'^``%PV(*`F"^_Z"G`(<0`A@"^``%`V +MI@@`!0<.Q!.8[\]P@`#`5/`@00-$+3X7+W8`(8!_@``$-R"@\@KO^DIP"'$` +M)H`?@`#X-G((``7/<(``M)4`@`'E:0T$D-D"#_;@>.!^X'C/<8``H"3/<(`` +M;";@?R*@_!P(M/'`&G#/<(``:"0@@&!Y`=B!X,HAP@_*(L('RB""#P``GAG* +M(X(/``"H`0'8@>#*(<(/RB+"!\H@@@\``)@9RB."#P``%0'*)&(`0`.B +M]\HE0@,`AIBXF;@`I@#8CK@!I@/8H:ZBK@ZX`J;/=8``;"1`A0;88'H"V4"% +M!]A@>@+9`HXI`B_V`*[@?N!XX'[@>/'`SW"``&@Y`(!S"%0!SW"@`*PO&H!2 +M(```8P@?`,]Q@```+H<]P@`!4)`"`0'C.#P``SW"``%`D`(!`>`X) +MP``V"<_]B@\/_,]PH`!X10"`!""`#W````!!*#Z%]_7/<(``3!TC@$B!-)%3 +M(@``1@Z@`@';,@VO^!+8T<#@?O'`X<6TP<]UH`"T1W$5`)8$((`/<````$$H +M/H7U]8H@_P]O'1B0:QT8D)X/[_B+$"(`=@`H6BZ`KI5>L=R@`#P)6." +M8Z%A@F&A8H)BH62"9*'@?P"BX'CQP)X(#_;/=X``;#D&AP.`SW6``!R)((!) +MA0`B@`\M`,#&`GF!"7(`H<'/=H``N!L`A@'@`*87"%$``=G/<*``R!PQH#8/ +MH`8H<(MQ1@_O]D+8`(9"($"``*8']`#9SW"@`,@<,:``%`0Q!"2^CP``%__* +M(<(/RB+"!\H@@@\``*83RB,B#&@!HO?*)2(``(6"N&8.(```I2(((``!V`"% +MHK@`I2F%QW$M`,#&R@W@!.EP50`O]J'`\<"^#^_U`-K/<8``-#X`@;O!5\`$ +MB4HD`')XP,]P@`!,'0.`"(#`N$#`7Q2`,,]Q@``0%4'`.,!"P%X4@#!#P!J! +M.X$$>3&YP+FH((`"`-L`)(`P9!C"``'B3WK/<(``'(EBD,]P@``P!D"08PN! +M`,]S@`!PY@Z+SW6``!R)AB#_`2@5C1!#N`(@0(.OBW"+RB!B`(8E_Q';;<]U +M@````IA,(40#/ +M<:``R!P!V!&A/@V`!C?`SW>@`.PG$+@%(($/``!"+2:G!2"!#P``@D8%((`/ +M``!"8":G!J?/<`@`AQ`&IP"&0B!`@`"F!_3/<:``R!P`V!&A`,#/<8``H+`6 +M>62!0('/<`\``/P*NP1[R;IE>L]SIP`42$VC18$A@0JZ1'C)N25X#J,Z#H_] +M1L``P`KHBB'_#\]PH`"T1V\86(!K&%B``-@#V43`4<%(P,]Q@`!$B0AAJ0@S +M`D?`",$%P!$@0(!^`P$`!\``)`$P9!&!`('A;@,A`(-P`=ED&$(`!\'/<*`` +MM$=@&%B`SW"``$P=`X`0N9NY,B"`#P``V`*?N8#@`=C`>`^X)7C/<:``M$=? +M&1B`SW"@`+1'<1``A@0@@`]P````02@^A?7U`MD`V!IP!\`1(`"$_`(A`%#! +MSW"G`!1(7!@`!$D($"`K"%$@BB#$-HHAA#@@\!P4!#`*(<`/ZW+/<```JQ.D +MVU$&;_=*)0``"B'`#^MRSW```*XHU]M*)```-09O]PHE``2*(((]BB%"/P'! +M`L`B>$G`!\!Z"Z_["G$Z<`?`I@FO^PIQ2L``A@'@2B(`(`"F%0A1`,]QH`#( +M'`'8$:%^"X`&0"E`(1!X$+B!N(>XC+@&IR"&0B%!@`?TSW*@`,@<`-@1HDHD +M`"&*=4`@@#$0>$O`0"&`,1!X3,!`*$`A3<`*)H`D`>%AO2"F$PE1`,]QH`#( +M'`'8$:$F"X`&`\`U;0`E%Q8O)\@E)7@0>!"XA2"*``:G0"^`(8&XE[@`)5,6 +M!J#00B0'@#W@0J0#` +M"^C*"$_\$PA1``#8=L`$P("X#WA$P`#`SW*``*"P`[@5(``$&6(:8@R"*($2 +MPD[`#<"V>``@E0^``%2)$\#P'8`@]!T`(`G`B")\`"\A`"`$*;X@E@[O^B]P +M#B"!#P````%/P1/`B"!\``0H?@0O<'H.[_H.P0X@@0\````!#\`)(8,/``#_ +M`0D@@@\``/\!2"("`$@C`P`VP%0=F"!5'=@@(0A1``K!&!0$,`2Y0"R``3A@ +MM7C'<(``W+!"L&.P`(8!X`"F%0A1`,]QH`#('`'8$:'."8`&"L$&P$`O@B&! +MN@2Y!K@X8+5XQW"``-RP(I`\>1"Y)7I&IR*0P+FX>04A@00O(D@@(Y!`*X(A +M@;H\>1"Y)7I&IP.0P+BX>`4@@`4O)@@@`(9"($&`"/3/``IA4(40#/<:``R!P!V!&AY@B`!L]P +M"`"&$`:G`(9"($"``*8&],]QH`#('`#8$:&Z"X_^SW"@`+1'<1``A@0@@`]P +M````02@^A?;U^@N/^,]P@```'9SW"@`,@<,*!+V<]PI``<0"2@X'[@>,]Q`0"L/L]P@`!8 +M).!_(*#/<8``'(D`@8"XX'\`H>!X\<`2">_UN'!3(($`SW"``#14*&"!X,HA +MP@_*(L('RB""#P``E1G*)((/``#^`#`"8O?*(^('SW:``&@D((9@>0'8)PA0 +M`""&ZW5@>0'8N'#/<```EAD*(<`/J7(BVP$";_>*)(,/&0'O]0'8"=G@?R"@ +MX'CQP,]P@`!H)""`8'D(V!!YSW"``*P)_@^/]HH-H`,'V&8.C_WN#P``*@@` +M`-'`X'X(<5B)`8`"H8CJ68F`XL(@H@#`(*$``J'@?N!XX'[@>/'`2@C/]<]V +M@`"X&P"&`>``I@#?%0A1``'9SW"@`,@<,:`&#V`&*'#'V)2XSW6@`.PG!J7/ +M<`,`@BL&I<]P`P#"1`:ESW`#``(L!J7/<`,`0D4&I<]Q``#"=,]P`P#"=`:E +MSW`#`()O!J7/<`,`@FP&I<;8D+@&I2:EK@Y@!@K8SW```()L!J6>#F`&"MC/ +M<````BP&I9(.8`8*V,]P``!"10:E@@Y@!@K8SW```()O!J5V#F`&"MC/<``` +M@BL&I68.8`8*V,]P``#"1`:E6@Y@!@K8SW`3`,8`!J5*#F`&,M@`AD(@0(`` +MI@;TSW"@`,@<\:"]!X_U\@+8 +M`!0!,4"%`]A@>L&YB0:O]:'`X'CQP`X.K_4#V,]V@`!@)""&SW6``&@O8'FB +MP0;H((9@>038ANA)`R``2!4$$`/8&G#/=Z<`%$C/=J``["<&#*_]!=@.I<]P +M@`"X&P"``>#/<8``N!L`H1<(40`!V<]PH`#('#&@B@Q@!BAP`]B:#*_VJ7$$ +MV)(,K_8B;078B@RO]B1M"]B"#*_V)FT/V'H,K_9`)0$2-MAR#*_V0"6!$C?8 +M9@RO]D`E`1,XV%X,K_9`)8$3"(<$I0V'!:4.AP:ESW"G`)A''(`'I1>'"*46 +MAPFESW"K`*#_&(`+I<]PJP"@_QF`#*7/<*L`H/\:@`VESW`%`,8#!J;&V)"X +M!J;/<"P``@$&IL]P6@!"`0:FBB"+``:FSW!``(<-!J;/<-$`P@T&IL]PP``' +M#@:FSW"``+@;((`1"5$`SW*@`,@<`-@1H@'8"*<`V`VG#J?/<*<`F$?/`0H@`\``'0)%84WA0)Y#@ZO]2]P`<)/X,]Q@`#(BA2E5Z$8H<]P0`"' +M#0:FSW`1``8.!J;/<(``N!L`@,]Q@`"X&T(@0(``H0?TSW&@`,@<`-@1H8MP +M,@M@!('!-H4`P")XK@MO^Q*E,H55A2QX-X4O($`.0GDY8:8-K_4U>>"X''C` +M(&(`@B#$`L]Q@`#(BA*E$Z46H<]P@`"X&P"``<(!X%6ASW&``+@;`*$5"%$` +MSW&@`,@<`=@1H8(*0`8!E1"XA2"$``:F`I40N(4@A0`&I@.5$+B%((L`!J8$ +ME1"XA2"/``:F!940N`4@@`\``((-!J8&E1"X!2"`#P``P@T&I@>5$+@%((`/ +M```"#@:FSW"``+@;`(#/<8``N!M"($"``*$'],]QH`#('`#8$:$$A2N%"*<% +MA0VG!H4.IPB%%Z<)A1:GSW"K`*#_.*`LA3F@+84ZH$8.;_T.A4@5!!",)(*` +M1?:,)#^!#?;2"6`&"MA^#``$0B!`((#@`@7-_T@5!!",)(*`1?:,)#^!#/8* +M(<`/ZW+/<```M!F*(T4,$00O][ASSW"```<4`(@&Z,]P@`#(+P`0!`"(<`4# +MK_6BP,]P@`!H+^!_%(#@>,]Q`0!H6,]R`0"P3OT$+_H`V.!XX'[@>/'`SW"` +M`%0D`(!`>,]P@`!0)`"`0'C1P.!^X'C/\@HAP`_K@`4`3$`%`4Q3"4`@,PE8H#,):*`RB'"#\HBP@?*(((/``":&@/90(4%V&!Z`]GQ`*_UH<#QP(X( +MC_7/=8``N!L`A0'@`*4`WA4(40`!V<]PH`#('#&@1@\@!BAPSW"```8ASW&@ +M`.PG!J'/<(``1CH&H<]P@`#&4P:ASW"``,8D!J'/<(``!CX&H<]P@`"&5P:A +M`(5"($"``*4&],]PH`#('-&@SW"G`(A)T*"!`(_U"-G/<(``V,?@?R.@\<`& +M"(_USW:``+@;`(8!X`"F`-T5"%$``=G/<*``R!PQH+X.(`8H<,]P``#"+,]Q +MH`#L)P:ASW````)&!J'/<```PE\&H0"&0B!`@`"F!O3/<*``R!RQH!D`C_7Q +MP*8++_@6V/(+``3/<8``3!T`@<00``8/"%\!`8'$$``�A>`98+;_@3V,]P +M@`!()""`8'D+V-'`X'[QP*H-[_N*((@%#NB.#>_\`-C/<(``8"0@@&!Y!-B` +MX$0,`O_1P.!^SW"``$P=`X`(@,]Q@`#8QPD('@`!B0/P`HG@?P"IX'CQP+AQ +MC>@*(<`/ZW+/<```IQF*(\0+70`O]XHD@P_/<8``V,<@@4PE`(`$(8$/``<` +M`$$I`P8`V@L+@0`!X='`X'X*(<`/ +MZW+/<```J!F*(P0."0`O]THD0`#@>/'`X<4`W<]P@`#X!:8((`"@H,]PIP`4 +M2*B@#0=/]>!X\<"AP;AP`-A`P%,E@``G"%``10B0`$\($`$*(<`/ZW+/<``` +MJQF*(XH*M0?O]HHD@P_/<(``:"0@@&!Y`=B$X`'9P'G/<```(M(T>,]Q@0#+ +M%P_PSW```"/2SW&!`,X7!_#/<```)-+/<8$`T15EA0,"+<#(-H`,#VJ'`T<#@?N!X\<#AQ;APSW`L``8!SW&@`.PG +M!J'/_VBB2##\]S@`!,'6.#:(,5"QX`SW.``,8@9J'/$,]U@``&.J:ASW4#`((""?#/=8``QCVFH<]U`@""`J:ASW4$`,8Q +MIJ'/=4H`0@$@\,]U@`!,':.%J(47#1X0SW6``(93IJ'/=0,`@@((\,]U@`!& +M5Z:ASW4"`(("IJ'/=00`QC&FH<]U3`!"`::ASW&G`!1(=Z&`N!JB&01/]>!X +M\<":"T_U`\@!W<]VIP`42)00``"HI@0@@`\!``#`Q@[O_RZX_]B;N,]RIP"8 +M1QRBSW&``/@%`($`WX#@RB'"#\HBP@?*(((/``"L&!X\<`J"T_USW"F`)P_&8"M"!X`SW:```0&`(9& +M@*`2``8O*`$`3B"!!T$IT``1"-4@2'"`(`H`,B``!)#H"B'`#^MRSW```*T9 +MBB-+`HHD@P\M!._V"B4`!,]U@0#`%T`EP!)Z#"_W"=D`V+H)K_\/(``$@.`` +MV`\@``0%]"8,S_\#\+(-S_\#R+D0@``;>("X0(8*K2:"EB%!`P`A``08B(P@ +MPX\"<07R8;@/>!BI)H*@$0`&GQD8`,H+S__1`D_U\<#/<(``:"0@@&!Y`=B! +MX,HAP@_*(L('RB""#P``G1G*(X(/``"/`"D_U.G`;?<]PI@":EP((99"!$$`(8!X`"F%0A1``'9SW"@`,@<,:!F""`&*'"+<78( +M;_:*(`<%`(9"($"``*8&],]PH`#('+&@`!0%,5<-T``*(<`/ZW+/<```B1GJ +MVZ$"[_:8`,]Q@`!, +M'4V!/I%3(@``I@W@`0';T<#@?O'`X<7/=8``8"\`A1L('P`V"4`#%@Q/^T8/ +MC_@"#L__`(6`N`"EW0!/]>!X\<">#J_[BB`$`A'HV@V/_\8/S__/<(``8"0@ +M@&!Y!-@%Z*8/3_\."```T<#@?N!X\<`R"$_USW6``&`O`(4Y"%\`SW"``&`D +M((!@>038%.A.#J_[XM@0Z#8.+_T'V)()X`,(=GX-#_\R"R_]R7``A8&X`*59 +M`$_UX'[@>/'`X<4(=9AQ2'!H<0#:#@@@`*ES10!/]?'`R@\O]?AP620<.-AQ +M&7*X,HA)@`R)`TP`"0.,`'CH*]`K@#9*PAT$"AR`>$O>3(D +M33!"($@0`"1#,+IB3WHR)(XP`"2`,,"KXPAUD*"H3"0`@,HD#7'H("T'`>$O +M>3(D0#``)$,P&F)/>C(DCC``)(TPP*L`K3(D0S!X8`]X,B0#,``5@`!G>`$= +M$@!M!R_U5B0<./'`_@X/]:00`0`5"1X&MA`!`<]PH`"8`SZ@GO``%@U!O+`` +M%@)!7;``%@Y`SZ``%@)!0!B$```6`D!1H``6`D%(&(0`1"4"$S4*$`$8VW(8 +MQ```%@-`S$-L,\![;&)_T< +MC"<"D@GT`N-P>W(8Q```%@]!`O``WV`8Q`,)"UX``!8/02ATA"0,D`3T`-HB +M\)GJ428`D-$A(H(5\M"(J+G/!XNYI!A```#: +M6J!;H.;Q`!8"0%J@`!8"0%N@"-IT$`X!OA`/`<)_8G]"?[@0@@"8N:080`#/ +M<:``F`-"?WIB4'IR&(0`NA`"`?!_3I@0H(!YCA@#>K/<:``+"`P +M@2)ZUW)):P#2`-_#]^*@JN;*)B80X'C%!2_UP*7QP,]P@`!<#@[9`=KJ"B`` +M`-O/<(``E`X)V0':V@H@`$ASSW"``(@-*MD`VLH*(```V\]P@``P#@O9`-JZ +M"B```=O1P.!^X'CQP`38$@AO^P'9SW"``/TO`(C/<8``_B^:"R``((G1P.!^ +MX'C/<(``H#M-`@`$X'CQP(;H[@@```#9(J#1P.!^\<#2#`_UI@R/^\]V@`"\ +M!F;8(FX!VOX,;_Q("``E4>)30B!``"608E%"($`SW"``#0&`(@FB3D)`0#/<(``3!T. +M@"T(7@'/<(```#!`@,]P@``$,`KJSW&@`"P@,(%">7,)A`\Q`0`M`-DV\(PF +M`H`I],]P@``DEC>("NG/<(``L+D!@!!Q`=G`>;[QSW&``/2."XF&(/^,$_+/ +M<(``L+E!@&2)#0K```")$'(`V`/T`=@/><]R@``$,`"J#?``V:+QSW"``+P; +M((C/<(``!#!,Z0'9(*C9`B_U*'#QP$H*#_6AP1IP.G)H=KT)<@``V)IQ%2`- +M(,]Q@`"\!@`5DQ`"%9(0NG#CC2&1`8T!VCA@$'B+<6X*;_Q(Y5'HS"1`@QW*``/0.&/#/<(``O`;!D*&-"B'`#^MRSW`` +M`+L4BB.$```F1!,U`Z_V"B5`!<=R@`#4#P`:P@0#[@*J`O`!JB4('@`,[@.* +M@+@#JA)O%'@;8F.+6&"!NV.HY*H#[B:J`O`EJD(D02!5"76`0"5`(-$!+_6A +MP.!XX<53(`T`H*D$(($/``8``$(A`8`$((`/0````,HA8@`@JM=P0`````'8 +MP'@`J^!_P<7@>/'`C.AN#<__SW&@`"P@,(''<4EK`-(BH-'`X'[QP$8)+_78 +M<0HF@)"(=4.( +M`^$"$(4`(PH?``HAP`_K!X +M\"`'B24(P0$! +MB5,E`A`9"@$`!"6-'P`&``"`Y0':!HG`>AT*``#/<(``O1O`J,]P@```,,"@ +MSW"```0PP*@O(`<"'*/'`I@K/_QH*S__*"D`%H@F/_%X(0`'1P.!^X'C@?N!X\"^__R7"(Z)#9`\B0N:`80`"I"#/=H``,`8`EF>*SW&``+P;+0L! +M`,]P@``R!@"088H="P$`SW"``#0&`(A&BA$*`0#/<(``O1L`B`/P`-@`J0#= +M/@OO_ZEPSW"``#0&0(C/<8``,@8`B2".@.(!VL!ZZ',Z#>__F'7/<(``7`8` +M@`&(SW&``!07"P@>`0'8`*D"\*"IQ0;/],]Q@`!X(,]P@``P!@"01XDQ"@$` +MSW"``#(&`)!!B24*`0#/<(``-`8`B":)%0D!`,]P@`"\&R"(SW"``+T;(*C@ +M?N!X\<`.#L_TSW:``"26%(XI"%$`!-@""2_[`=G/<(``,@8`B,]Q@``P!HH, +M[_\@B0#8%*XM\+:.*^W/=X``_"\`CV&X)0T`$.((S__/<($`,!,%@"%M!2A^ +M`,]P@`"@.XH+X`,O<<]P@``R!B"0SW"``/TOH*\@J,]P@``P!B"0SW"``/XO +M(*@`V!:N-8X)Z<]P@``R!IX([_\`B`#8%:[9!>_T`=C/<*``+"`P@,]P@``` +M,.!_(*#@>/'`5@WO]/AQH<$(=AER`-W/<*``M`]P$`D`SW"@`+0/O*`%AN.. +MBW%`)(,PF@OO_T`D0C`*AJX)[_]`)$$P*0]4$!0@3!/P)TX#((P@P`$4@C`" +M%(,PP@OO_U,F!!`!Y>,-Q),O($<"SW&@`+0/'*%-!>_TH<#@>/'`3@W/_XH/ +M``71P.!^X'CQP.'%SW"``#S'`-DEH,]P@``4*2*@SW&``$P=`('$$``&=PA> +M`0.!&(AO"!`!SW6``!`X`(5"(`"`RB!B`"<(40#V">`#J7#/<8``V#<`@4(@ +M`(#*(&(`A>@H<$H*X`,BA<]U@``L.`"%0B``@,H@8@`G"%$`P@G@`ZEPSW&` +M`/0W`(%"(`"`RB!B`(7H*'`6"N`#(H6Q!,_TX'CAQ0#;SW*``.C'%"(-`&"U +M:+4:8B`:P@#`'<00*!K"`,]Q@`"4QQ9Y(I$P&L(`T!W$$(`=W!!X'400`=F( +M&D(`SW&``(C(%7E@H>`=Q!#P'<00X'_!Q>!X\<`R#^_[$=BYZ,]Q@`!X(,]P +M@``P!@"01XE5"@$`SW"``#(&`)!!B44*`0#/<(``-`8`B":).0D!`,]P@`!$ +M)`"`FNC6"L`"B.@+R`4@@`\````\"QH8,,8*P`*(Z`O(!2"`#P```-0+&A@P +M"\B0N`L:&##""T_[`_!.",_UT<#@?N!X`-F$T`3_O@ +M?N!XX'[@>*'!\<#AQ:S!`-E*P9#9&+E(P<]S@`#8QR"#!""-#P$``,"&(?X# +M)+D.N0LE0)!.P(["%O+7=0```$#,)8*?````@,PE@I\!````!/0A@P/P(H.N +MN*^XL+@%>2"B#L,(P(MU!".!#P$``,`NN4`I`@9%>$C`BB`&!DG`0<.I<`#: +M2@D@``';SW&``!`5&H$[@21X)P@>`@K`"\&$*`0.`"&`?X$`4!4"N0C@-'DA +M8,]PIP"(22^@)@X@!*EP"-SW`N_TK,"AP?'`=@KO]`ARK<$(V$K`D-@8N$G` +MSW"``-C'H(`$(8X/`0``P(8E_A,DO0Z]"R9`DU#!D,,6\M=V````0,PF@I\` +M``"`S":"GP$````$]`&``_`"@*ZYK[FPN25X`*,0PPG%!".!#P$``,`NN4`I +M``8%?4G%'PJ>`0K`!".^CP```!A%(,``2L`%\H4@$`%*P"4*'@&;O<]PH``L +M(`6``-L"N&ZX@.#*(,P`R;BE>$G`!O`)"AX"G;U)Q1#`@<5"P*EP0@@@``+; +M`\@,PL]Q@``0%;D8@@`:@3N!)'@;"!X"`KK/<($`6!54>D%@SW"G`(A)+Z`B +M#2`$J7`(W.L![_2MP/'`<@G/]*/!88`(=4##`-@*I6T+7@($(X`/`0``P"ZX +MSW*``'!*"F))(H(`8;I+I1)J%'C'<($`2!;*@,]W@`!\R<:E"X#/=H``3!T% +MI<.&(,#4AO6/!'[D?@F^0"D/`N5^Q7@$(X,/````$&5X!Z4(A1CBGK@(I4NE +MC_`W"IX"SW"``,@Y`(!!P$+`(0@>`H8@_PDCN`'@%0B4``L(D0`&V&'`)/`' +MV&'`(O`BP&'`'O!!P\]R@``$!D""1H*>$@(&*PJ1`00COH\````8#_3/<$)HX?!@```#&^`";%$\]V@`!P2C(F#A$")DX1 +M$_!3)L(0SW>``%Q-77I*9P0FCA\!``#`+K[/=X``<$K.9V&^UGI+I1,+'@(@ +MQ\]V@`!X2NYF`O`!WH0H!`X`(8!_@0!0%0*Z5'I'8&&^6&#FI0&`!".##R\` +M`-TFN\5[4B/#`P6E9Z7/<(``V,<#@`#?'0A.`,]P@`#H.0"`%0@>`(8@?P\= +M>$`HSP,$\`#?C[^;[\]V@`!@)""&8'D`V"4($`,@AF!Y`-@9"!`$((9@>0#8 +M$0A0!""&8'D`V`L(D02R#2_\`-@(A05_Z*7=!Z_TH\#@>/'`<@^/],]U@`"` +M)`"%Q)#))*H\]SH`#$)Y$3`8;#N1L)@0"*)0@0$QM8@Y$3`8;#N0L)@``2&UB#?0>/ +M].!X\<`"#Z_T`-C/<:``Q"=2$0*&%1$"AD(1`X81"YX'`=C/<8``1,EAL5$B +MP(`:<,HE8A02]%$@P,;*):(4#/3/<*``T`\@$`&&'Q``AA!Q`-W*)6(5Z0T1 +M$,]V@`!$R1^&RP@>!*@6`1"4V&(*8`+)22RBB"( +M!<]R`/\`_XH(3_T`EHH-;_TTEI07`!#/`\]P@`#H!2"@SW"@`/PE$X!L +M@GA@#*+/<@"@"`#L<$"@;R)#`.QP0*`.'UB0E@U`!<]P``#_?\]QH``,)`&A +M&]@$H2D&K_2I<.!XX'\#V/'`X<6AP2X/+_N+<++H`!0%,!T-'@!^"```SW&` +M`$3)0X'/<8``M)E!H23P"PV>`(H.S_\>\`T-7@(:#L__&O`[#=X`"-C/=:`` +MQ"<3'1B0I@W``!T($`4"V#P=`)#/<(``1,DC@,]P@`"TF2&@&=B7"%"&P06O +M]*'`"B'`#^MR%]B,N(HCQP"%!B_VBB2##_'`X<7/<(``1,D_@`0A@0___X\X +M!"6`7P``<,,]Q@`!$R1^A1"(`4\]U@`!$R4,($0(_#5Y1-@_/_YP=`!`3 +M#9Y3SW"``*`=!8B8'0(0%/`5#=Y3SW"``+`@&8B8'0(0#/`#A7H-+_8DA9@= +M`A`$\`#8G!T`$)P5`!"`X,P@XH!>\L]PH`"H(`B`'X41"!\!#0W?4H#8F!T" +M$)@5@!!`*`$&$0C?`8*Y'PJ>4T8-``(;\!^%42*`T[.X'Z7%(8(/````!T4A +M``;/<8``T,DLB88A_0]2(<$!1;DE>,]QH`"()!"ABB#6`,]QH`#$)WX9&(#/ +M<*``U`L!VE*@!-@0&1B`SW6``$3)'X5'")X!%)5#"%\!SW"@`"P@#X";Z*UQ +M^@XO^58E0!6`%0`0E+B`'0`0'X60N!^E#?#/<8``.#(/@0'@#Z$0V<]PH`"0 +M(SV@502O]!G8\<#2"Z_T`-D(=@&`P;B#X,H@02`%\B(.(`#)`1Z%E;@>I7GP>@PO^T\D0`+K +M"!4$SW&``."/F!6#$/`A`0!`*P(&AB/]#U(CPP%%NV5ZSW.@`,0G01N8@`#: +MC+H")D\`^F++NM=R````"$`M#P.0OU+W!2>/$6(;V(.,(@*`Q_?/<8``K#,2 +M@0'@$J$`V9VY1?#E>6(;6(!9#H5P``#`#PXB@P\````0SW*``$"/%GH@@B4+ +M-0@$$@4``-@/(,``8;A.(P\(`2G"`WAY!7D`+<``!7H7\$(C`P@`V`\@P`!A +MN'AY!2$"`(HA_P\+\,]S@`"L,Q.#BB'_#RAR`>`3HP'8SW.```"N`*L"&P0! +M(:-"H[WQ`-F(`=`!!`)@`2H!T`$`+9SW"@`/0F(Z`EAL]P@`"T +MF2&@>0*O]`IPX'CQP`8*C_0(=54@4`0-S*+![;C1(&*`!_($R,(.K_^8$``` +MSW"``,#)#(#/<:``R!]DX!ZA$-@.H0'8%1D8@`&%@^C_"Q[``87!N(/@UO0` +M$``@0<`$%`\Q$(4LOP84$C%U")X!#`\]P@`"P(!F(!O`%A2:%+@H/]N>XF!X"$,HF81`&\CZ&E;D^ +MI@#>!+C/<8``2+!&D>5X$PB``,]R@`"L,PF"`-X!X`FB!)$;"($/``#__P#> +M"?#/<8``.#(-@0#>`>`-H0&5G."(]`00$2`($!`@SW"@`/0F`MDCH".%SW"` +M`+29(:!J"R``J7"`X(#T)N[/T$J`2%E>2:B#1(!-QT)W@(0VJNY#!J<,`T:7##/`QK8K+D-&EPPI0X0$,]V@``0B.`6`Q!%A40K/@<`)D$>0*%,E0'C +M0K'/P!#/FI"AF$!`P90`1$DA`9``00O0R_02H# +M(>5]97U*L<]SH`#`+T<;6(.4XL`BA@\``),`SW6@`&@L\"6"$$NQCQ,"A@GP +MHQ,"AE$B`(&/$P*&!O3U"MZ!!_`(V`SPY[K*(B$`0,(!%(,PQKK&NWBI6:E1 +M`*_THL#@>/'`X<7/<8``3!TC@4B!60H>`(8@_P'/,]S@`!$R5@3@0``VH[I/)-BN1"Y12%#`<]QH`#T)F.ASW&``+29 +M0:')````\`0S,SW&` +M`+PQ,0A>`4#8#!H<,%41``8`V@'@51D8``W(SW&``.C'%'D#R$"IX@JO_Y@0 +M```'\*P1```!X*P9``#/<(``O!L!V2"HSW"``,#)+H`&@0'@!J$"V<]PH`#T +M)B.@(X;/<(``M)DAH$D&;_2I<.!_"-CQP"2Y4R'"`,]Q@``<4%9Y$PH0`D&0 +M88$$XG!RRB`B`@/T((%`>='`X'[@>,]Q@`#`R2R!SW*@`,@?9.$^HA#9+J(! +MV14:6(`A@(3I_0L>P"&`P;DA"=$`SW"``+P;`=D@J,]P@`#`R2Z`!H$!X`:A +M`-D-\"&`42$`@`#9RB'A!0&`42!`@,HAH03@?RAPX'CQP$(-3_3/=H``P,D! +MA@0@OH\`<```.?(O*0$`SW"``$`P]"!-`"N&3R6`$-8((`))AI3HC"4#D,]Q +M@`"\,0CTNA$`!@'@NAD8`!_PN1$`!@'@N1D8`!GP`88A")X'SW&``'#F#(E/ +MB1L*```1B5$@P(`D#D$"!_``V<]P@```KB"H<@P`!2D%3_3@>/'`L@Q/]`AV +M`8#!N`#?)PC1`,]U@`!$R8\/$1`0AG<(G@$0AAD(G@//<(``H!T%B!+PX@[O +M_\EP"'?M\1"&$0C>`\]P@`"P(!F(!O`%AB:&$@W/]9@=`A`1"-X!'H65N!ZE +M'X67N!^E@!4`$`0@OH\0<```#_2@2"\_Z!>@0ANVX`=@"]`#8 +MSW&``*;)]"$``#R5.&!BN!"X@+C/<:``]"8#H0;P`MG/<*``]"8CH"6&SW"` +M`+29(:`M!&_TZ7#QP+H+;_0`V0AV`8#!N(/@RB!!(`7R"@[O_\EP&G#/<*`` +M+"`&@!!X3"``H,]U@`!$R`3R5$PD#`"6&SW"``+29`H"W +M"0$`$(8/")X#SW"``*`=!8@-\!"&#PC>`\]P@`"P(!F(!?`%AB:&^@O/]9@= +M`A"`%0`0!""^CQ!P```*]#X*S_H+Z!"&$PA>`P'?"/``WQCPB@N/^Q3P`-\P +MA@(.[_A6)4`5@!4`$*@5`1">N(`=`!!`)@`2H!T`$-D)7H(!V<]P@`"\&R"H +MM!4!$`:!`>`&H5@5@1#/<*``]":3Z<]Q@`"FR5R5]"'!`UEA8KD0N8"Y"/"T +M%0$0"X$!X`NAR/$"V2.@)8;/<(``M)DAH`$#;_0*<.!X\<">"D_TSW"@`*@@ +M"(#/=H``1,D/#9Y3SW"``*`=!8@,\!$-WE//<(``L"`9B`;P`X8:"^_U)(:8 +M'@(0'X85"!\!$0U?4PT-7U*`V)@>`A"8%H`0%PC>`3^&E[D_ICZ&E;D^I@#9 +M`=T6\)P6`1`E"5$`/X91(4""SW&``$P=(X$I@07R1"$-!`7P1"$-`@/P`=T$ +MV1BX)7C/<:``B"00H1^&,PB>`126*PA?`;()0`*1Z,]PH``L(`^`!>@-S!<( +MW@$?AI"X'Z:M<;X,[_A6)D`5F^T+"IY3A@K``17PAB+_W,]Q@`!$R0_T`8$; +M"!X`F!&``,]Q@0#("@*X%G@`8?ZXH`X"^\]PH`!0#""`SW"``-P&!-H@H,]P +MH`"0(UV@SW*``$3)'X(-"-\$#X*`X`#8*?(7@HH2`@$9803B"PB?1/\)'L;/ +M=8``1,F8%8`0Y[@`VPGT`KC/`-C+;O`NXH5`!%/%8T0SW:``$P= +M\";#$$)YHGAV#"_[3Y.)`4_TX'CQP.'%H<$`V$#`SW&``#@R#X$!X`^A`]G/ +M<*``U`LQH.!XX'C@>.!XX'C@>.!XX'C@>.!XX'C@>.!XX'C@>.!X,:`0V,]U +MH`#$)Q`=&)`Z"N_ZBW"6Z``4!3`=#9\`"B'`#^MR#=B,N(HC7P?U`>_UBB2# +M#P39$QU8D!O9%AU8D`D!;_2AP.!X\<#/<(``-*TN"B_V&-G/<(``L(LB"B_V +M&-G1P.!^X'CQP&8(3_0:<,]UH`#4"Q"%`-ZAP4#&(0A0``HAP`_K<@_8C+B* +M(Y8(BB2##XT![_4*)0`$SW&@`/Q$&8$$(+Z/```((`+T'8$1"-`DD@GO^HMP +M@.#*(`(@0B#!()3A2@$-`#(F07"````\0">``( +MH=H*P```V2AP//#/<(``P,DN@`>!`>`'H?;QSW"``,#)+H`,@0'@#*'N\<]P +M@`#`R2Z``H$!X`*A(O#/<8``M#(%@0'@!:$<\,]P@`#`R2Z``X$!X`.A`=D` +MV!3PSW&``*PS&H$!X!JA_@N@`P'8RO'/<8``K#,4@0'@%*$!V`AQ@.$X"8(` +MSW"``$3)'X`7"-X$SW"``/2.RZC/<(``*(S,L`/8$:7@>.!XX'C@>.!XX'C@ +M>.!XX'C@>.!XX'C@>.!XX'C@>!&E@0`Z""``!*&\\<]P@`#` +MR2Z`$8$!X!&AM/'/<8``M#(.@0'@#J&9\0HAP`_K/'` +ML@X/],]P@`#`R0R`SW6@`,@?$-X!WV3@'J7.I14=V)-&#>`$"=@#V!ZESJ45 +M'=B3SW"@``PD!X`$Z/<+'L#9!@_T\<#AQ<]Q@`!,'2.!*8%1(4"`RB"B`"?T +M1+C/<8``,###N`EA"0D>`#4-GU$U"5X`SW6``$P=`X48B"$(4``^#8_Z".C/ +M<(``E"`(B`T(T`$#A1B(#0B1``D-GE$!V`/P`-B%!@_TX'CQP/X-#_1$(A%3 +M37>&)_P337`$(I!?````0`0E@%\````@02A^@P7RZ@R/^H/H`-X"\`'>SW6` +M`$3)'X4)"%X$`-V\\/T)$:#*#(_Z'NC/<(``E"`(B(?@S"!B@A;T`86,(/^/ +M$O0DE<]P``#__QT)`0`%A8P@_X\(]`R5UW```/__RB5A$)KRSW"``$P=\""` +M`RF`#0E>`<]Q@``82P7PSW&``"1+&(@*84$O`!'/<8``,$L(811ZSW"``)Q0 +M3F`K#AX0'X4(=(0D"9`/].2^T2`B@A_R`86,(/^/&_0$E3,(@0\``/__"O`- +M#EX0'X4C")X""0Z>$`D-'E(!W0SP$P[>$,]PH``,)!&`C"#_C_;S`-WFOLHE +M(A#Z"X_Z".@$);[?````(LHE8A"-#1`0%0[>$1$($2#/<8``1,D?@9.X'Z$1 +M#EX1SW&``$3)'X&-N!^AC"<"D`[TSW&``$3)`8&,(/^/!_0$D0T(@`\``/__ +M`-V,)P*0S">"GP``4``'],]Q@`!$R1^!D[@?H<]P@`!,'0*`PA``!A#HC"<" +MD,PG@I\``%``"/3/+`9``#/<(``C(BYH,]P@`#\R:"@!-[/<(``F`;`H)D1 +M@`"@N)D9`@#/<*``Q"=D&%B#SW8``/]_$QB8@QO>%AB8@QH86(.*)_\?SW:@ +M`/Q$_:;YIHHGF!W/=J``4`SBIG&B<*(\&$"#BB,8"&ZB@!(#`*090`-1(T"` +MSW.``+296!E"`PSR0A``A@0@OH\`P```!/(!@P+H`J.AHX`:0`//$0SW>``$3)M!``H?K8 +MM@XO^P#9(-C/=H``',K^#6`#`*8!V,]RH`#('Q.BSW&``,`;"($`@&R!8(,D +M@4`F$!4`$00`^!("``#9`B"`@`&F`-@#(T,`4!\$$%(?!!!4'P00`B2!`,]P +M@`!,'6*F0X`CIA22SW&E``@,";8(@L"X"+8`$00`4R1%`5,D00!,'T(1@^'* +M(<$/RB+!!\H@807*(X$/``"<"X@#H?7/("$#!"2!#P```.`MN7^'FA]"$!0> +M`!$="]X"!+F!N25X"+8'V`?P`-D5(`P@(*0#\`38`>#S"!2""(+KN*`(@@4? +MARNX4R`%`%$@@,6F\L]Q@`"8(P:!`>`/>`:A02F`0\]Q@`"8(V:!SW&@`+0/ +M-X'`N#!S`-J;],]QH`"H(":!C"&#CB8!#0"P<(WTSW6``!S*!87/=J0`D$'U +MAC:&!""`#P```.`MN.>ESW.``$3)**4-"!X`4!O$`PGP4!N$``0GCQ___P`` +MYZ4/"%X`,+]2&\0#!?!2&X0`\'_GI0T(G@!4&T0`"?!4&X0`!"&!#___```H +MI0V&!J4$((`/````_BFX5AL$`!^#1PC>`L]PJ@``!`2`":7/<(``L(L@B$1H +M->EA"70``A"$`)]Q`-BH((`#]"(/`!7>$[[P)L\3SW:``!S)%7X!X."F'/#/ +M<(``-*T@B$1H&>D"$(0`@.'*)$UPRB`M`.@@K0/T(@\`*=X2OO`FSQ//=H`` +M',D5?@'@X*8AK0(=`A&T$P$``MT!@0'@`:$,\`0@OL]@`````_0$W03P"0L> +M0`/=@>5.\R\-D1`"W00@OL^``0``RB6B$0;T42,`P,HEXA#K#9"0SW"@`#`0 +M`X"`X,HE8A&&Y3($`@#/=H``1,DL]S@````$P=XX?HAP0G +MOI\`!@```_*,NE*FY+G*)2(2X;G*)2$2AB'^#T$I`@%-'H(0*)-%>2BS*0W1 +M$2,(M`,'W<]Q@`!,'2.!A!$!`!,)!`#/<:``,!`H@0D(0``(W8?ERB`!`5P+ +M(?O*(2$`*P,``,]PI@`(!`&`!""`#S`````TN$(>!!!"%@$1&0A?1L]PH`"H +M(`B`&6$P>?X*+_N(<`7P'@LO^XAP!""`3X`!````V3$(@0\``0```=A.'@(0 +MSW*``!S*FA:`$$(>1!!-'D(0-Z8IH@2X*)*)N"5X"+)S\$T>0A#/<*8`C`,] +M@`0A@@\X````02K`!)H>`A`$(8`/````\"6Z++A%>,]R@`!$R1*F#0C>1Q*" +MC+@2HE,APP)($HX`=Z+@OM$AXH<'W0/T"-W/<(``',HIH)H2@0#HD`2YY7DH +ML'RP,H(MH'T-T1'/<:8`C`.]@00E@1\!````,+E.&D(`J:!.$H``&^A;#E$3 +M4P@?1A38SW&@`,@?'J$0V`ZA`=@5&1B`"MU1(`#&RB7B$5$C`,#*)2(2\0V0 +MDA3P)PN4`\]P@`!,'0.`A!```!<(Q`#/<*``,!`(@`L+`0`'W0+P"-V'Y>7T +MSW:``$3)3A:`$(#@W?+/ERAB+] +M#P:Z17E*!$!2Y)7B(N$0G`1)!*<&`4B!`!1*F6!Y"$,HA@@\` +M`/__RB&!#P``$!\Z<3>&0!Y$$`0B@2__`P#_*+DWIBX,K_@`VJP>`!!Q#YX4 +M2!:#$#*&H./1(>&",/($(8*/`````0CR1"$-!B.]`>45#940!"&-#P```"1! +M#8`?````)`0AC0\&````,;TQ#=40%0V1$!3J1"$-!B.]`>4=#9$0`^K,XPKV +M5X8R(3"_#/<*``,!`(@#>&$'$' +MW&,!D`!!RQ$H;KH0VAK!8`$"@9 +M@`0=L0T-T1%Z"```"'6,[28.8``5W:H)+_L(=H#FS"`B@,HE(1"`Y90+HO_* +M($(#`-C/<:``U`L0H<]P@`#0R0V($0@>`,]PH`"()!Z`"QH<,#X,0``,S(8@ +M^8\*](3ES"7BD`;T`-B/N`P:'#`RV<]PH`#('"J@_0/O\Z+`\<"F"\_S`-W/ +M<*``U`L8@$(@``B`X,H@3`//<8``F",E@8'ABB&9#@CTSW&``$P=(X$^@8`A +MF0X0<0#8RB!M!'4($0#/``T2`3=3 +M(7Z`#?+KN3>"!?*@X0'9P'D(\([A`=G`>03P)PM?`0#9SW.``$P=8X-I@WU[ +M4B,#`,"[9'D'Z3^"D;D_H@KP-X+I\?8+``!8$H$`@.$H"P$`@.!^`@(`SW:` +M`$3)6!:`$!+H`MG/<*``]"8CH,]P@`"TF:&@P-F9%H`0@+B9'@(0*'`#\$+8 +MSW&@`,0GOQD8@`'8#!E`@Q`9&(`?AO&X(@("`!*&-X:*":_X`-JL'@`0'X;% +M"-X"SW&``$P=8X%(%H`0-(,$>40A`@%$(`$,0BD$`8!RSW&````>4R)&`#(A +M@0&)N3RF5(-P%H$0SW>``,A*!"&%`$T6@A"&(?\#1+D$)84`H''T)T$08AY$ +M$,]Q@``D(3(A@0&)N3VF=!:!$/2#)'^&(?\#1+E$?S]GSW&``,A*]"'!`V0> +M1!`RACJF=(,[IF1X!'K/<(``V$J`Q!"2 +M'@00E!X$$$X>0A.9\$X6@1#/<(``T"\`@,"XJ0D0`(#@`-O*(2(`"_1RAD@6 +M@1`$(X,/````"'M[PKD`(8T/@`#T'5"-N(W'<8``("'/=X``]*P(B65]O*9P +M%HT097K#O;Q]]"=-$V5X,(EE>3VF=!:!$,.Y/'GT)T$08AY$$UJF:!:#$&0> +M1!#/<8``!*W#NWQ[]"'"`!NFCAZ$$,]R@`!,K?0BPP!L%H`0P[@<>/0A`0"2 +M'L00D!Y$$/0B```]\(#@`-D%]$@6@1##N3QYSW"``/`=*&#/!!#/<(``%"$H8!VF=!:`$,.X''CT(@``SW&```2M4H9D +M'@002!:`$,.X''CT(0,`6J;/<8``3*WT(0``CA[$$%NFD![$$)(>!!"4'@00 +MU@M``<]P@`!,'0.`"(`/"-X"3A:`$(#@V`X"!5@6@!`%Z,(*#_\#\,8*``#= +M`,_SX'C/<:``Q"<5$0.&!-@3&1B`&]@6&1B``]K/<*``U`M1H.!XX'C@>.!X +MX'C@>.!XX'C@>.!XX'C@>.!XX'C@>.!X4:#DN^$@P@<6V%(1`(;@N.$@P0?* +M(.$%"0A>``D+W@#@?Q+8`=G/<(``O!O/`\]P@`#H!2"@%=C@?N!XX'[@>/'`R@^/\\]P@`!$ +MR3*`)PE>`L]Q@`!,'2.!2!""`#2!1'E1(8"`2-K*(H$/``"0``+P#MH`W\]Q +MH`"H(">!K!`-`%EAL7'")440RB7F$K!X7@OO^@K9SW"``"PL`)#/=J``Q"<+ +M"!X!C"4#D@/W`-T:\,]PH`"T#_R@SW"K`*#_^J"V#>_]`-@9%@"6!.@"V!`> +M&)#/<8``K#,;@6J]N&`4W1NA&18`EH?H42$`QM`-803*(&$`?0>O\ZEPX'[@ +M>.!^X'CQP.'%SW"``+P&`)#/<8``X+>HV@'=@"!$"Q!X+@_O^JES@.#*(<$/ +MRB+!!\H@@0\``+44RB.!#P``S`#*)"$`%`!A]O +M\[2@\<``V<]PH`"T#SR@%@I/]YX.#_YJ#D_[`@WO_0#8_]G/<*L`H/\YH#B@ +MT<#@?N!X\<#AQ<]Q@`!,'?`A`@!*)$``PQ(!!@]X,B*"#P``'P,$(8,/``8` +M`(#C`=O`>P0AC0]`````UW5`````PB0"`8((K_O`N;$&C_/@>/'`-@Z/\TH. +M(`((=<]Q@`!$R1^!SW:@`,0GL+@?H1D6`)8`V03H`M@0'AB0SW"@`-0+-Z"> +M#@`!=@H@`P'8!>WB"$``!?"^"$``*@K/^AD6`)8%Z`+8$!X8D$D&C_/@>`': +M`-G/<*``M`]!`+9C!A$``0E@E\``'#' +M/X!%>3^@@.6Z`P(``,#IN-;RSW6``(`D`(6*(0@`Y)#/=J``Q"<3'EB0SW&` +M`$3)/X$Z=X8A_".%"5X$02D!(<.YSW*``!Q0-GH@@D!Y"'49%@"6!.@"V!`> +M&)#/<```_W\3'AB0&]@6'AB0`]G/<*``U`LQH.!XX'C@>.!XX'C@>.!XX'C@ +M>.!XX'C@>.!XX'C@>.!X,:#/<(``C(@9@(#@\`U"`9KE&@,"`,]P@0#`%W8, +M(`,`W0L#``!.#F__*G`:<`"%Y@XO_RIQ;@\O_PAWB.?,)^*5RB7!$ROR&P@0 +M(.X*(`"!P`HE`)`<]((-[_\!P!CP`]G/<*``U`LQH.!XX'C@>.!XX'C@>.!X +MX'C@>.!XX'C@>.!XX'C@>.!X,:``W0\/D1;/<($`P!?Z"P`#@.6.`@(`8@Z` +M`<]P@`!$R1^`$0B>`P'9SW"``.0%(*`)\`\(W@,!V<]P@`#H!2"@$18`E@#= +M0<`Q")\`?@TO^H'`"B4`D!#T!!0%,!T-GP`*(<`/ZW(*V(RXBB/'"C4%+_6* +M)(,/@.4J`@(`!-@3'AB0&]@6'AB0SW"``(R(&8"`X-P,0@$+`@``X+C!\L]V +M@`!$R1*&AB`Z`(P@!(+(#84!SW&@``PD/($7AB)X9+@0>(H>!!!$(@!3%P@1 +M`A^&#PA?!%$E0-$!V`7T`-@#\'(-3_^<'@`0+NC2"$__"B4`D-?T#!*@6`1#4V.8.(`')<@?HS@I`!`OP`@I/_ZSPSW&``"@T'H$!X!ZA +M`=_/<(``O!NT%@$0X*@&@0'@!J$?AO.X#`]"^@^&@.`<#D+Z'X81")X#`=G/ +M<(``Y`4@H`GP#PC>`P'9SW"``.@%(*``V,]QH`#('`>A,-@*H:8+[_\!P!^& +M)P@>!A#8#!H<,,]P@0#`%T8*``,-R``@@0^``'#('X;@J;BX'Z8`EH8@_`", +M(`*`&O22"0_ZF.@#V<]PH`#4"S&@X'C@>.!XX'C@>.!XX'C@>.!XX'C@>.!X +MX'C@>.!XX'@QH`"6@@EO_#260_!!P!7?"PC?`.EU'_`(V,]VH`#$)Q,>&)!: +M"L__"'4K"!`%`M@\'@"0(18!EL]P@`"TF2&@$18`EM,(GX!R"R_Z@<`*)0"0 +MX?,_#5$5SW"@`)`C'H`$%`0P42"`@,HAP0_*(L$'RB!A`L\@(0/*(X$/``#D +M!!0#(?7*)2$`4@GO_XAP"'6I<`D"K_.BP.!X\<"N"8_SH<$(=@#80,``I@X+ +M+_J+<`HE`)"%],]PH``$)2*``(8$(8$/_P!?_P4A`@!`IE,A@@!3((,`97JE +M"M$!SW"``$3)'X!Y"IY3MPB?!@0@OH\`'@``!/0`A@GP`0J?0`"&"PH>0(6X +M`*;/!D\@`0*)N8VYB[F.N2"F'H($ +M((`/`@```%(@0`0JN"5X`*8M\/RYQ2""#P````7D]84@'```IB/P];@`AA_R +MAB`<`(4@&```I@$*'T$`AB\*WD"&N`"F$_!3(0,`4R`"``4COH#*)>$5"?*& +M(7\/AB!_#P4A/H#*):$4SW"``-#)#(C$N$`H`08`AB5X42"`Q`"F1`\B!,H@ +M(@BI7*)2(0+0L?0`D(7D5'"9Y#,P@? +M1]D(WL75"9[#SW"J```$`8"&(#\+`-V["-&`$_"V"@``SW&``#`S#H$!X`ZA +M"?``V9RYSW"@`-`;,*":"@```-G/<*0`F$`\H!#P`-T-"Q]`C@X@!`'8J/%Z +M"@``SW&``#`S#H$!X`ZA$0"/\PS,1"`^BCGR00C>``T2`C>`V,]Q@`"T,@P: +M'#`-"MX"'8$!X!VA!?`5@0'@%:$1"M\``-G/<*``+"`OH`W,AB""`N!_#1H< +M,"\(7@&*(`0`#!H<,,]Q@`"T,A2!`>`4H0W,`-E&((`"#1H<,,]PH``L("^@ +MX'[@?O'`X<4M"!$"*0J?4<]Q@`!$R7^!AB/W#[>!SW*@``PD'((4X`@E`!!< +M@HH1`0$1\)7HSW.``$3)-X///'`SW"``$3)%)!1(,"!SW"``(0D`(`%\@(-0`0#\$X-0`31 +MP.!^X'CQP)8.3_//=H``A"0`AH#@RB!A!2/RSW&``-P&(('/_S-X4`A@#?0(`WA5EAK@R@ +M!""@Z7"E!D_SX'CQP/(+@`3/`QX/8`0!V`C8SW&``'!Z +M"'2&L3"\$@R@!(>Q#O"<$@``%0C1`#22F!*``"7:P[D^"Z_T`-NI!6_S%=C@ +M>/'`-@UO\P/8`=[/=:``M`_O+@%H<]UH`#0#]6E`]\B#J_SZ7#Z +MI<]P@`"8(_JE.05O\\:@\<#/<*``M`\W@,]P@`"8(P:`#0D!`+H/S_\$\)X/ +MS__1P.!^X<7AQ@#9!]@`VK1IM'W'=8``X+=5?<"5C"8"G0#;A?:,)H62P_;_ +MWL"UP9T+#E,?C"8_D4+V8;4!XD]ZSPH2@V&X`>'%"'6`+WG!QN!_P<7QP%H, +M3_,`W<]P@``DEE8+[_FTJ!+H"-Z`Y4=\(HD`7'/<8``Z,>H((`!!!E0`^!X`-E*)`!RSW*``)3'J"#``A8B +M0`!BD,]P@`!@R#1X`>%@L,]U@`!,'<]V@``\F4`E`!&I +MH`""J:``@L`86`,`@L$86`,`@L(86`,;V<]P@`!PYFD#;_,LJ/'`X@I/\QIP +M.G%:^."X`"`_#""X`"SW"``)@C!(`C")X`SW"```@P`("+Z#G8K@CO^8NX"PA1 +M`#(-S_\*\`#9GKG/<*``_$0AH.!XP:#Y)'J&(/X#1+@?"8``"B'`#^MR@]B- +MN(HCT@U*)```40/O]`HE``%(@W]G.[I3(@*`0*]-D\"Z0:T+\O>3AB?_&4._ +MYZUWDX8C_@=%NVBM%.K/@*MX8OCK>*+Y*UCBV6M(XKB +M#.``)JV+<*EQ:@IO\PS:`,`!P48.;_0"PHMPJ7%6"F_S#-H`P`'!F@UO]`+" +MSW&``%0&`*%F#.`#R7#=`6_SH\#QP'();_-7V,]U@`!,'2.%SW*``%@&=Y$` +MH@L+'@!?V`"B"PN>`(6X`*(+"UX`A[@`HHHF"Q;+8=EA`-J`X\H@@0#/`$HD`'3@>*@@``8K")X``""##X``C!P@ +M$X$`@+D@&T(`H!.!`("YH!M"`&`3@0"`N6`;0@`!X!SP2B0`=.!XJ"``!BL( +MG@``((,/@`",'"`3@@"@NB`;@@"@$X(`H+J@&X(`8!."`*"Z8!N"``'@X'_! +MQ?'`C@\/\\]V@`!,'1IP"PA1``"&`O`!AL00``85)@T43"``H`'?);A3(`4` +M((7`?T`A``;$$0$&&PE?`0HAP`_KE@T)``!&#<_X2P@!!`"%Q!`!!@IP);G`N58)K_4` +MVL8-``&)Z`O(!2"`#P```#P+&A@PM@T``8GH"\@%((`/````U`L:&#`+R)"X +M"QH8,`H)C_-.",_WK08/\^!X\<`B#B_S`-D*)`"@H<'*(6$`%/+/<(``F",0 +M$`4`'0V?``HAP`_KO]`HD``7/=8``3!T5)0X5`(85)5(0 +M)!`5```2`2`@$!8`*!`7`4$M3R$I@1H0&`'`OR6Y4R$3`.H+8`,-V8H@B0"* +M<0X([_OI0@0(`WO +M"B'`#^MR?]B-N(HCB0=*)```X0:O]+AS/@R``1H([_?\&X+C_D$V<]P@`"8([X*;_TDH""&R!$`!H8@?XY!]*H.+_R*<,H( +MX`.*<``2`"#($``&AB!_CD#TSW"``)0@+)`>E0\)``#&"L_X;0@!!8IP"G'> +M#F_U`=I_V1&YSW"@`+`?-*!:#(_Y.@L``8CH"\@%((`/````/`L:&#`J"P`! +MB.@+R`4@@`\```#4"QH8,`O(D+@+&A@P?@Y/\PSP)!E`!2"&(!F`!2"&*!G$ +M!2"&&AD$!KH*C_D)Z`#8"PP1(%(.P`,#\#(.P`,!W9()X`&I<,]P@`",&^X( +MX`&@J"D,42#/<(``E"`(B(G@S"#B@0/T$0@1(!$($0*""H_Y!.CN#\_T#@A/ +M^58-C_<$RI#@S"""CP``LP`.\@HAP`_K<@$2!#:2V(VXBB.-!H4$K_0*)0`% +M5@F@`0#820,O\Z'`\<`>"P_S#,P`WGL('@#/<*``R!^P$`(`SW&``$P=(X$" +MXD81`0%AN0@B00`^H!#9+J`!V1486(#/<($`P!,#&A@PSW"!`(@4!@R@`@0: +M&##/<*``_$0E@$H@0""\N26@#,R&(/^!SW"``/@%`(#"(`$D@.``#(+]!""/ +M3S`````>\#D(7@-:#L_^SW"@`/Q$)8"\N26@SW"``#`S#H",(`*-B?>B#Z_U +M&=C/<*``M`_2?`$V,]QH`#('P8:&#`?@8#@BB`,`,H@@@\````" +M#J$#V!6X$AD8@+\"```,$@$W30A>10;(AB#QCR+TSW6``+PQB!4`%@0AOH\` +M``!0`>"('1@0!/($V`P:'#">#>_^`-[/<*``_$0E@+RY):!K%0`6@0B%CP`` +MM``!W@S,TP@?`>4(GP&&(/^%KO)1(P#`>O0&R`0@OH\#@.A3R/61"%_%(@IO +M^@#>SW6@`,@?.0@0(/^%H!4`$`DG`!#DX,SVSW"``)3'`(`-"%X`WJ4N#Z`` +M$-CDY\CW0!4!%C!YU@TO^A#8BB`(`*`=@!,.I1^%A.B*(`0`#J5F"P`!+]B5 +MN!(=&)#/<`$`P/P5'1B0"@B``L]Q@`"L,PR!38$((@``#:'/<(``P!L0@$^! +M8(`.@0)[`,H((L(`B.!/H=+T`]G/<*``0"TPH``:@C/-\`W,4R!`@)_S!,@# +M$@$V`QH8,`0:6#`&"H`"SW"@`/Q$)8"\N26@SW"``/@%`("`X!0*@OV)\0\( +M7\4,S,]U@`"T,C\(W@"`V`P:'#`-S`\(W@(=A0'@':4`W@3P%84!X!6ESW"` +M`'#F$8A1(`"`_`QB`LH@8@`0[QR%`>``;I0S,>PC>`0W,!""$#P```!@Y#(`/````"-8*C_<-S$D(W@#/<*``+"`E +M@`:`"N$Q"$0``Q(!-@+8#!H<,%#8J@TO_I@1`0">\58([_C)0(HA!`#/<*``L!\TH`38!AH8,`W,[[@%\\]QH`"H($B! +MSW&``+S)+Y$P"J_W`-T#R*`0``#P +MN*EP!?+."P_X`-B5N(X*@`.X\;X++_@!V`#8D+CY\0'@`!H",+D'S_+@>/'` +M0@_/\L]P@`!4,`#?Z*#/=8``(`8_KI0?T`]CJ#&_Y"[B-Z`;9SW"` +M`%@'(*#/<(``7`?@H!D"(```V,]P@`!R>*(\8!"B3`!/4';_2X* +M(T8#2B1``+D';_2X&R`&PL>`,]SH`"P'V0;P`3/%5X`"""#X``2-(TXC0B00X*N3(B0"XHI<]Q +M@`!8!Q<(7@#/<(``',L@$(``@>`%V`+R!-@`H<]P@`!'& +M"'4`V,]S@``@!\]Q@`!`R\]R@``,TF*+`_`!X`]XPVF$*Q\`,B9.'AD(@P.$ +M*Q\``").#C#F]"8.$.,-@9,$\(H@_P_!QN!_P<7@>/'`H@T/^`#8T<#@?O'` +M(@MO^>'%,NC/<8``))84B5T(4``WB0GISW"``+"Y`8`0<0'8P'@4\,]R@`#T +MC@N*AB#_C![RSW&``+"Y88&DB@#8"PM``R"*"0M!``'8SW&``$P'`*$5"%$` +MSW"``(`X)H`C@2"!\@G``8T$S_+N#`_X[O'@>,]Q@``D!P"!@+@`H<]Q@`!4 +M,`6!`>`%H0;9SW"``%@'(*``V<]P@`!/'`V@O/\L]P@``B!P"( +MSW.``",'P(L!W80H'P`O<0`A@@^``(S1F'`PXO`B@`/`N('@`"&`#X``#-(P +MX/0@A0/'<8``0,O`?0'FXXG/?CD.XQ/`J_0@CP,M#T$1%.WP(H\#)0\?$`'F +MXXG/?A<.PQ/T((\##P]!$07M\"*/`^L/'I#`JP.)5PX#$/`B@`.$+!\`P+A2 +M(```&WC5>``@@0^``$C2-.$T(4`.SW&``$`'"K@`H<=P````&-H(3_G/@H`#8`*8`W0'?%_`]#)$`SW&``(@[`-_@J4\E@0`AH,]Q +M@`!4,`:!X*8!X`:ASW"``%@'H*#I=8#E7`\!`.EP50+O\J7`"B'`#^MRSW`` +M`'HG*0-O](HCA0+QP.'%SW*``"`'(8*EP2ATA"0&D,]U@`!_RI<#/<(``6`<9 +M#9$`A;DAHL]R@`!4,"J"`>$JH@'9P_$`$`0`"B'`#^MRSW```(0G40)O](HC +M3@CQP/8(S_+/=H``(`31ZQW*``$C2-.(08@JX"*;'<````!@2#B_Y2B!`(,,5`18(=\]P +M@`#HQS1X$8B`X'8.+_G"(`(D@.?,("*@S"`B@$+R`H[/<8``/-*$*!\`+W`3 +M8<]Q@``P!B"10X4W"T$`QW"``$#+98`H@E,C#P!3(0T`'P]!$P.(@>#$(X$/ +M``8``,0A@0\`!@``S"-!@`/R`-D"\`'9"8+//'N":`` +M`=@`A<00``8EN+X,;_7`N(8+;_44V.H/[_H$V"(/```Z#<_Y9/$*(<`/ZW+/ +M<```>2?]VTHD@`"M`&_TN'/QP%8/C_+/<(``7`<`@(#@EO1N":_T`=T"V$H( +MK_>I<<]P@``$!@"`SW>``$P=)H">$0`&IKB>&1@`(X=(@3214R(``"(,;_^I +MN<]PH`#\1"&@ +MX'C!H,]PH`"T#]R@H@K/_4.'SW&``%@'"8(9"%X!SW"``"`'#("&Z!B*@^`' +MV!CRSW"``"`'0HC/<(``4,N$*A\`,"!`#@GHSW"``"`'`8"&(#F/`_*@H0/P +M"-@`H<]P@`!/'`X<7/<8``(`<$$00`SW6``%P'B'2$)`:0"O(!V<]P@`!8!R"@`-@`I43P +M`(6RZ(AT`HF$)(:0A"@?```A@'^``$#+#?(0$`4`"B'`#^MRSW```(TG^08O +M](HC#P`B#F_W!(`(<<]P@`"<.&H+@`'/<8``5#`,@0'@#*&""6_U%-CJ#>_Z +M!-@#V`"E`=@2\"4(T0#/AQ06/ +M\@HAP`_K_Z +M!-@!V<]P@`!8!R"@`-G/<(``7`<@H-'`X'[@>,]P@`!,'0.`#9"&('\.SW*` +M`+@."0B1`2"2)WB`N`"RSW&``$#+!;'/<8``_-+@?P>QX'CQP,H,C_+/=H`` +M(`&('F/"_0` +MV$X*+_F,N`?H#(:`X,PG89`8]`"%@>"H#D'T#(:`X,PG89`(],]Q@`!4,`"! +M`>``H0;9SW"``%@'(*``V`"EG_`"CB..A"@?```A@'^``(S1,.#P($```-]; +M"!X`"(9>#2_V(H:,(!"`2@`I`""%@>%,#D'T`X;/+V]22F2*,%V<]P@`!8!R"@!-@`I0#89?`@ +MA=L)E0$S)D%P@`"`/$`G`'(T>`!X`HZ$*!\`,B!`+E$@0(`T"<$"`HXCCH0H +M'P``(8!_@`",T3#@\"!``,]R@`!8!P79$0@>`("X`Z8`V`2F(*4[\,]SH`"P +M'P'8&://@`@;9\<#AQ<]UH``X +M+D>%SW"``%`P`-E`H">E@@U@`R#8!X6*N`>E"\@$((`/____`PL:&#`+R(^X +M"QH8,`O(D+@+&A@P%0./\N!X\<"J#\__SW"``%`P((#/<*``."XGH*H.C_W1 +MP.!^X'CQP'(*C_*J""_Y`=V`X,]V@``@!P&&P'V&('D/0B``@,H@8@`(N`5] +M`-@&""_YC+B`X`'8P'@0N`4@?H,B\@N&*PA1``*.(XZ$*!\``"&`?X``C-$U +M>"R`@+DLH,]P@`!4,(#9*:``V`NF!MG/<(``6`<@H,]Q@`!BK"!`!"B'`#^MRSW```((GBB-+#DHD@`!U`B_TN',(AJX*+_8BA@OH!=G/ +M<(``6`<@H`38`*<`V$7P`HZ$*!\`+W`992.1'64HZ2..QW"``(S1,.#P($`` +M00@>``*5"KAN"B_V*H8AZ,]R@`"\,1F".((">02"18)">#A@(HZ$*1\`-"!! +M+A4)!`#/<8``5#`!@0'@`:$3\`79SW"``%@'(*`$V`"G`=@-\`B&M@DO]B*& +M"B$`@!(`#P#F"T``J@R/_]T`C_+/#6`! +MH*;/<(``@#B2#4`!SW"``",'H*C/<(``)`>@H,]P@``\!Z"@SW"``%0PJ:`" +MV*EQJ@LO\PAR;0"/\N!XX'[@>.!^X'@4N"5XSW&@`#@N!J$&@0$(W@?@?@#; +MSW&@`,`OI1G8@`_:"+JC$0"&1'B,(!"`_/,4&=B`HQ$`A@L@@(#\]>!^E.#* +M(@4`A?<(#^__*'7)<((/[_^I<=D'3_+@>.'%,-L`W<]PH`#('&F@`]K/ +M<:``S!&(/T$?V)!C#I$0'X40VIJX'Z4(V$\=`A#/<*``R!Q)H`>!SW*@`/`7!J(&@0:B +M!8$&H@2!!J(`V`JBBA4`$6BX$'B*'000`)6&(/^,`_0!V!VBJ@E``ZWHSW"` +M`(0D((``V`*Q)?!.%8`0H^B*%0`13*5DN!!XBAT$$`393QU"$!D.T1`K%P&6 +M9+@0>(H=!!`,V"VE3QT"$`8.K_B(<`GP!2-#`4$?V)`?A;.X'Z59!D_RX'@0 +MVL]QH`#('$FA`=O/<:``\!=JH:00`@!-"MX"`MI=H<]S@``(YD2#1J%#@T:A +M0H-&H4J%P$``!'.!3(,"`!/1`(P`(!/!`(P`,0(!3H4QH0()3H?@0`H)3 +MH?P0`(`3H0_P7)"&(O^,`_1]H4B`1J%'@$:A1H!&H06`!J'@?N'%+X#/C@`MG/<*``R!PGH.!_P<7QP&8(C_@6 +M#(_XT<#@?N!XX'[@>/'`S@Q/\L]U@`!$R1>%SW:!`%`3"P@0!E@5@!`$Z!J% +M6X4$\!R%787/<8``T"\@@1,)'P`RA00A@0\````0)7@E>L]Q_O__/R1X`:8` +MW^*F1'DMI@[8A@OO^`ZF!^C/<8``A"":#F```=C/<8``8!V.#F```-@7A0\( +M$04!V`&N,1X"$`3PX:XQ'L(3J01/\J'!\<`N#$_R.G$(=DAWX@UO^@#=@>#* +M($(C"_3/<(``>!L`D('@`=C`>$`H$`/)<(8@_`",(`*%(_3/<(``1,F8$(`` +MY[C*("(`"O0"N!9XSW&!`,@*`&$MN,"XSW&``)@&((%1(8"`SW&``+S)%'D$ +M\B#:K9$*\)C:JY$&\,]P@`"`R;.0#MH!ET`E`141"0,`HGA((```$'@#\`#8 +M6G``V"IQJ7/"#:`#F'`*(0"@!/2N"P`#.G!,(0"@`-A(]`4@@",-<0"Q#7$` +M&80$(X<-<""@*)<-<""PC"8"E17RC"8#D1WRC"8#E23R"B'`#^MR$]B,N,]S +M``"4"HHD@P^-!._SN'//<(``1,FT$`$`#X$!X`^AQ@D@`.EP$O#/<(``1,FT +M$`$`#H$!X`ZA"O#/<(``1,FT$`$`#8$!X`VASW&@`/0'`-@$H0'8&G#/<:`` +MR!_X$0(`0G4")8`02"```%^!$'@]"(0`0X?/<(``M)E"H*#8#Z$`V!^ASW"` +M`$3)')!BN$)P'Z$"V!49&(`-"1`@42!`QB#8`_*`V`ZAC"8#E0;TSW"``$3) +M')`)\(PF`Y$(],]P@`"\R0^0C@YO^0#9Y@K/_@S,AB#YCQ+TC"8#D`!C+;C`N/`B``"@@`UPH*`#R!"(`K@6>`!C +M+;C`N/`B``!"D`UP0+#$H2H)``,!V-4!;_*AP.!X\$KHC#PSW*@`,0G$1(!A@#? +M]0F>@602`X9D&MB#`MD3&EB`+RG!`$XA@@<3Z\]Q@`!`CU9YP(&A@<]Q@`#` +MC_0AD`#/<8``X(_P(8\`"O#/`(*X#7$`H0UPP*`-<*"@ +M'?`-<0"A2B0`=*@@``-$)H$0#[E3)@`0)7@-<0"A(KY*)`!TJ"#``D0E@1`/ +MN5,E`!`E>`UQ`*$BO9T`3_+@>/'`.@A/\@AV*'4H<$AQ7@@@`&AR@>#*(($# +M$`@A`,HA00.%`$_RX'@BN0;P[')@H@3@8;GY";6`8(#/<*``U`MMH`/9SW"@ +M`$0=-:#@?N!X02F!@`GR+R1)<*@@P`$$$`($['%`H>!^\<#2#P_RH<$(=4AV +MSW"@`*PO&8`$((`/<````-=P(`````'8P'@O)@?P`-K*(($`+?(+S``<1#!/ +M(,$#`AQ$,`'@$'@$((`/``#_OX^X"QH<,,]PH`#4"SB`0B$!"(#ARB&,`$`E +M`!(0<2`(!0,'Y00EC1\``/S_Q7V=O9^]['"@H`#!['`@H`'8M0H(;_H`W8'@RB!"`PGTSW"``'@;`)"!X`'8P'@,N(4@`P$#VL]QH`#T!T6A +M#7(`L@/(`-M=D`UP0+`#R%&`#7!`H`/(2!`"`0UP0+!DH64'#_+@>/'`Z@X/ +M\L]U@``0B.`5`!``WH#@T/=$+CX7`"%`VXX.[_\8N^`5`!`!YN<. +M!)``V"$'+_+@'0`0X'CQP*H.#_(A@`HF`)`0B<.XRB'!#\HBP0?*(*$&RB.! +M#P``J@#/("$#.?*`X`!ASW&``#@&+;C`N`RI(X8@D88A_`",(0*`#/3/<8``3!WP(0$`OQ$` +M!H&XOQD8``&&HH`%[0&%`^@`A8SH"B'`#^MR'-B,N+G;2B1``%D'K_.X/'`K@TO\@#: +M"'?/<(``(P<@B*/!SW"``"('`(@!',(SA"@?```A@'^```S2+N#T($``8,$# +M'`(P`=@"'((PSW:@`,@?$Z;/<8``P!L,@0"`0L`(@0"`0<#/<(``U`4`@(#@ +MRB`!!\HA(0/*(H$/``"$`,HCH0<$#>'_P"LA!L]S@`!8!\]U@`!4,`L2`3=> +ME8388(/*"^`"F'>N#\`"I!8`$!.E<04O\J/`X'@(V>QP(*`#V0#:SW"@`!0$ +M):`!R.QQ`*'/<*``U`M-H.!^X'CQP.'%I<$(<@#;SW"@`"P@L(!`P0;80"N`"8(-A!"_R +MI,#QP*7!SW"``"('((C/<(``(P=@B(0I'P``(8%_@``,TC#A]"'!``#:SW.` +M`,`;8\'/<8``5,LP(4`.`=G`N`T<`C#/<*``L!\YH`R#`(!!P`B#`(!`P`2# +M`(`.'((P#QR",$+`SW"``-0%`(!$P8#@RB`!!\HA(07*(H$/``""`,HCH0X"QH8,`S(#!H8,.!^X'C/<8``'(D` +M@8&XX'\`H>!XSW&``!R)X'\#L>!XSW*``)@Q%7K@?R"B\<"8<`HAP`_K<@HE +MP`?/<```HAD=!*_S5MO@>,]R@`!T,15ZX'\@HO'`F'`*(<`/ZW(*)<`'SW`` +M`*,9]0.O\U[;X'C/N!_(*+QP)AP"B'`#^MR"B7`!\]P``"D&,]Q@`"\.P)A2B0`=`#9J"#``A8B0`"A@&"`*=@2N`'A=7B@H.!_ +MP<7@>,]P@``$!@"`H<$F@)X1``:&N)X9&`#@?Z'`X'C@?N!XSW&``#@&X'\$ +MH>!X\<#AQ<]R@`!,'2.".(D?"1$!"B'`#^MRBB",#HHC!@)*)```'0.O\[AS +M((+$$0$&`<]R@`"$-R""0B$!@,HA8@"OZ8_H"B'`#^MRBB#,#HHCA@-* +M)```Z0*O\PHE``$F@B.!8;B@@<]Q@`#`&R2!(('5N3UESW&!`#`3)8$%*3X` +M)W5(<$(/X`!")8$2SW"``*`W`"6!'P``B!,N#\``R0$/\N!X\;/?B"L(PIR``#=GW+/ +M<8``((^H($`"0"`""*IBO&$!Y:]]0*PQ@""C$H`Y`2_R`:/@>.!_`-C@?P#8 +M\<"^"`_RSW6``*`&S(T-C<*^PK@6?L]^O@CO_`W8!KB!N!"^Q7C/<:``["<& +MH02%SW&E`.@/!J$%A0>A[0`/\O'`>@@/\L]VI0#H#R:&IX;/<(``H`8`WR2@ +MI:!Z"._\#=@&N(&XSW&@`.PG!J'FID4ES1^GIJT`#_+@>,]Q@`!X(,]P@``P +M!@"01XDK"@$`SW"``#(&`)!!B1\*`0#/<(``-`8`B":)#PD!`,]P@`!$)`"` +M`O``V.!^X'CAQ>'&`-TS"=`'"PG3!PL)$P``V!/P&0GS!Q_>3B'\!^!XJ""` +M`0\EC1-AO@D(3@"E>`/PIG@`H@'8P<;@?\'%\<"^#^_Q*-AZ#\_XSW6``&0D +M0(4(=@0@A`\``/#_`-A@>D$L`0%`A0'81"8$$V!Z02R!`$"%`MA@>E,F01!& +M#^_X`-A`A0AW02@!`@/88'K`N4"%02]!$@388'K`N<]Q``#LM\]P@`!@)""@ +MSW`!`$R^M0?O\0"E\<`.#4_\L@A/_,]Q```4N,]P@`!H)`H-[_L@H,]Q`0!T +MOL]P@`!L)""@T<#@?N!X\<`:#^_Q4-C2#L_XSW6``'0D0(4(=@#88'I3)D$0 +M0(4!V,EQ8'J&(?T/SW$``#BXSW"``'`D(*#/<`$`G+Y)!^_Q`*7QP.'%_@SO +M^0?8A@TO_`AUK@_/_[(.3_SV">_YJ7`M!\_QX'C@?N!XX'[@>`4```#QP*(. +M[_$(&)_$?1[]$(($#/'G/=8``<.8MK00@A`\````,0BR` +M`A.M!":$'P```#!"+``#%*T$)H0?````0%,AOH!"+(`#L1T"$`[T"B'`#^MR +MU=@%N%K;BB3##XD';_-*)0``SW"``!#)!(B!X,P@(H#,("*!!O13:25Z3ZU. +MK8#CS"`B@07R4VME>DZM@.?,("*!!/(3;^5X#ZT3:25X$*T.C0RM;@I@`0#8 +M40;O\=^UX'C@?N!XX'[@>.!^X'C@?N!X\<#2#>_Q`-NAP02XSW*!`%`3%'@= +M8I#>&F(!@AB^R*6*)@02SW+^__\_R:4$>H#A0,+*)<$`"_((@00@@`\````P +M0B`%@,HE8@!-#1``SW"``-C'`!`$`,B!!"2&#P`'```$)HX?````,"R^8;Y! +M+@8&0":`$P\B`@!`PH4.CP,*(<`/ZW(^V(RXBB.*#I4&;_.X=L]P@``0R0>( +M@>#/(J$#+_+/#R;.$X8A_P,$)@Z0`-]$N01[#R=/$.1XRB8!$(#CRB.!`PZ[ +M97H#\`&#!7I`PL]P@`#8QR"`BW*&(?X#)+E`*8,#((($(8X/`0``P`LFP)`6 +M\M=V````0,PF@I\```"`S":"GP$````$]`&``_`"@*ZYK[FPN25X`*(`%`0P +M!"2!CP$``,`*]`HAP`_K!X\<#B"\_Q"'7/<(``G"P)@(GHSW"``"PL +M*8`,X/`@3@`"\`'>BB#_#P"ESW&``$P=`('$$``&:0A?`<(*C_H`I;X)+_FI +M<(#@RB7B$:+TL@I/^(;H`(6,(/^/#?3/<(``:"0@@&!Y`=B!X`7=RB4B$9#P +MSW&````3(9'/"H_Z`*7/<8```!,AD<]S@`",!D"#/.$Z8B .(4X5EAY?'/<(`` +MA#<`@,]W@`!$C$(@$(`R":_ZRB!B(`"E`8``-@W)HC/<(``E"`LD,]P@`!,'1Z0$PD```(E@1\````,Z7`$\.EP0B4!%6(/@`#/ +M<(``]#<`)8$?``"($U(/@`!+\'X(3_C/<8``2#@2Z,]P@`"4($R0SW"``$P= +M'I`1"@``*'`")8$?````#`7P*'!")8$7&@^``,]P@`!D.-OQ0@A/^,]Q@``0 +M.!'HSW"``)0@3)#/<(``3!T>D!,*```H<`(E@1\````,!/`H<$(E`17:#H`` +MSW"``"PX`"6!'P``B!/*#H``"G#)N,]Q@`!$C`.A!H:!N#T![_$&IO'`X<7/ +M=8``O#"P@@,]P@`!\+`"`"2$!`,]P@`#,Q@B`"2$"`,]P +MH``L(#"`J7!V#J``66$-`<_Q\<#/<(``1(P!@`\(40#/<(``+"P'@,]Q@``0 +M.""!0B$!@,HA8@"0Z<]Q@``\QRR!BNG/<8``),>J#F_W(X$*#N__`MG1P.!^ +M\__`-D1`,_QX'CAQ>'& +MJPH0`$`BPP,DN\.Z`O``VI4*%00S)H)P@`"T/$`GC7)4?2!]P(@!&9(#`>`! +M$(($`1F2``$0@@0!&9(``1""!`$9D@`!$(($`1F2``$0@@0!&9(``1""!`$9 +MD@`!$(($`1F2``$0@@0!&9(``1""!`$9D@`!$(($`1F2``$0@@0!&9(``1"" +M!`$9D@`!$(($`1F2``$0@@0!&9(``1""!`$9D@!"(T.`L_7!QN!_P<7AQ>'& +M(^IC:B*[P;H"\`#:-0H5`3,F@G"``*`\0">-'%X<:K"A``0"+#`R2[ +MP[H"\`#:E0H5!#,F@G"``*0\0">-+(J1T-4!`1#9`0 +M'0W1$,]PH``X!&BHSW"@`#@$:*C/<*``.`1HJ,T%C_'QP#H-C_&Z<'IQ^G*: +M('PY@]/_W_PSW:``+`Y`88`V;(.;_,XV@"&'-D@H`&& +M&-D@L,]Q@`!,'14A5@,`%@$@4X$-P?"HSW>``)0'*!A`!$5YI+DAH`#9,QA" +M`.EQ(J`*(4"#,1C"!#(8P@0T&,0%RB%B`/H);_<,X(7MSW&``#R9!/#/<8`` +M7)DCIL]P``!($0"Q&-@"I@T-4""*(`4"`+$,P(7HSW`!`"R\`:<`%@`@N1`` +M!BT('@!!AAK8`+("I@"1A[@`L0#8"[$!@JVX`:(1#!`@SW"``#`N!(`S&@(` +M*0H0("&&`8&8N`&A`X&?N`.ASW&``&@&`!8`(``9!`1`@`&`0:$"H48-;__) +M<`T$C_'@>/'`O@N/\;IP>G'Z<@HB`"$*(4`AR'4*),`A"B!`@\]R@0#6"LH@ +M8@`(<0*X%G@(8DPC`*`$N(8@_@,%(%``RB',#\HBS`?*((P/``"_(``)P'$!@"!*2YC;F9N2&@Z7$BH`HA +M0(,H&``%,1C"!#(8P@0T&,0%RB%B`&8(;_<,X(;MSW&``#R9!?#/<8``7)DC +MIJ38`+$0V`*F"PU1(*38C+@`L<]P@`!,'1F0CKB/N`&Q#,`!IRD*$"`AA@&! +MF+@!H0.!G[@#H<]Q@`!H!@`6`"``&40$0(`!@$&A`J'F"V__R7"M`H_QX'CQ +MP.'%SW6``,PT`(V,(,./#_3/!X\<=Q@0#("D"A`-I"L<:IP-A_'0(0SW6``-0&P*W/<($`2`J`V=8+ +M;_,H/'`V@F/\:'!"'?2#2_T&-C/ +M=H``O#$!AJOHSW"@`-0+&(``W4(@``B`X,H@3`.,(`B%2/?!%@`6`>#!'A@0 +M&_"=V``00>4!`!XXPC@H`@ +MAKCWSW*@`-0++:(D>`"F9QW8$V@=&!$`V*$`K_'<'0`0X'AE!N__`-C@>/'` +M-@BO\=AP.@@@``#=R6@K#A(0^'"I=S(F@`,5"!(,$0B3#IX-S_0R;SAX!7T! +MYT(G1P#E#W6`8;YE`*_QJ7`(<@/P`>`@B/[IX']">.!X\<#J#T_QSW6@`/Q$ +M'84YA2H/(`(`W@#8GK@!I>!XP:7%I34`C_'@>,]QH`#(.PZ!B+@.H6D@0`#^ +M\>!X\<#/<(``3!T#@!B('0@1`0HAP`_K!L` +MD('@`=C`>`RX*0B!#P```!#/<*``+"`0@,]Q@`"<+`*A`M@#H<]Q`0",,TX* +M;_\!V-'`X'[QP!H/3_'/=8``/,VX9V``B +MA0"A"_!N$0`&1'AN&1@`'-@8N!49&(#Y!D_QX'B`X`'9P'G/<(``R#3@?R"@ +M\F%X<(IA:'":88 +M@@NF&8(,IAJ"#:;/<`4`Q@,&I<;8D+@&I<]P+``"`0:ESW!:`$(!!J6*((L` +M!J7/<$``APT&I<]PT0#"#0:ESW#```<.!J7/<(``N!L`@,]R@`"X&T(@0(`` +MH@;TSW*@`,@<`-@1H@'8"*<`V`VG#J?/<%``_P`6$+@% +M((`/```"#@:ESW"``+@;`(#/<8``N!M"($"``*$'],]QH`#('`#8$:$$ABN& +M"*<%A@VG!H8.IPB&%Z<)AA:GSW"K`*#_.*`LACF@+88ZH!8/+_D.AL]P@`"X +M&\]Q@`"X&P"`0B!`@`"A!_3/<:``R!P`V!&A'01O\:+`\<"R"T_Q?@@``,]V +M@`#T.5H*+_<`A@AU`(89#0`0Y@EO^ZEP\@FO^Z"FF@\O]!'8?@Q/]L]PH``L +M(#"`SW"``-`&W0-O\2"@\<"Z#^__H<'/<(``T#0`@`398MH>VT#`BW`:"R__ +M&+NAP-'`X'[@>/'`/@_O\Q;8`-C1P.!^X'CQP"H+;_$'V%8)3_G/=J``M`_\ +MAAIP`-@KQX'CQP,]P@``(%P"`SW&``-`&&WC6"^_T(($(Z`'9SW"``(P;=@_O +M_R"HT<#@?O'`!@I/\0AW?=@-N,]Q@0`P$\6!C@MO\(P@`H`!Y7SW`"A"`P4JO@//V,]RIP`42!V"OH)L$A``]Q26"'P#_``#3)>$57@QO]HHA$``(=JEP +M4@QO]HHA$``(=4`H`")"#&_VBB$(``AW0"H`(C8,;_:*(0@`T7D9X2QY+W&Q +M>AGB3'HO<@`9@",/#V(0`!M`(P#8!/#_"(.``=@A`6_Q`!P"(/'`W@A/\0AU +M*':N#^`!"M@!V,]QIP"81QJAG@_@`0K8SW"F`)P_9!`$`%$D`(#*(<$/RB+! +M!\H@@0\``+\9RB.!#P``N`#D`>'RRB4A`,]PIP`42"R`'8``IO>XQ2""#P#_ +M``#3(.$%X0!O\0"EX'CQP&H,[_,&V"8-3_S/<(``3!T#@!B('P@1`0HAP`_K +M"^_Y!-C1P.!^\<#/<8``G"P) +M@0'@":'/<8$`,!,&@8*X!J'/<(``$,D#B(#@X`^A_LH@H0#R#._S!MC1P.!^ +MX'C/<(``!10`B!L(40#/<:``K"\9@?"X&8'/(*(#SR"A`AFAX'ZQ`R_T$=C@ +M>/'`SW"``$P=`X`8$(0`'0P1`0HAP`_K`!V,!X#+@1 +M"($/````$,H((```V"#P\@M/_,]P@``D%`"`ENC/<8$`,!,&@48@0`$&H<]P +M@`!T!B"`%0F1`(H@B`0^">_Y!-J6"N_Y!-B>"0_ZT<#@?O'`SW"``$@X`(!" +M(`"`RB!B`(CHSW&``)PL"8$!X`FASW&!`#`3!H&"N`:AY@OO\P;8SW"``#S' +M#(`8Z,]P@``4*0*`$NC/<(``>!L`D('@`=C`>`RX%0B!#P```!"F""```-C1 +MP.!^SW"``!#)`XB%Z(H.K_X"V/;Q]O'@?N!X\<#/<(``3!T#@!B('0@1`0HA +MP`_KO\KASSW&!`#`3!-@&H<]P@``4*0'9(J#/<(``$,D#B(#@ +MM`VA_LH@H0#&"N_S!MB^"N_S"-C1P.!^X'C/<(``Q#7Q`@``X'CQP*8*[_,3 +MV,]P@`#`&P2`((#/<(``=!(@H-'`X'[@>/'`X<7/<(``P)D#@,]U@0!D&A$( +M7@`$V-(.+_8$I1SP`]@$I0#8"*W/<(``(*SB#N_RU-D`A03H8;@`I28(C_X! +MA0'@`:7/<*``+"`0@.H/;_X%I84%#_$(<@#8X0?O]A#9X'@(<@'8U0?O]B#9 +MX'@(<@+8R0?O]D#9X'@(<=T'[_8`V`AQU0?O]@'8"''-!^_V`MAI!P_R\<#/ +M<(``3!T#@!@0A``=#!$!"B'`#^MRBB",#XHC"0/Y!:_R2B4``,]PH``L(#"` +MSW"``,0U;@H@`)8A00_/<8$`,!,&@48@0`$&H<]P@`!T!B"`%0F1`(H@B0>J +M#J_Y!-H""._Y!-@*#\_YT<#@?O'`SW&!`#`3!H&"N`:ASW"``!#)`XB`X$P, +MH?[*(*$`7@GO\P;8T<#@?N!X\_S%-A^#(_RT<#@?O'`0@GO\Q38G@QO +M^038T<#@?N!X\<#_V<]P@`#,-(8)K_\@J,H)S__1P.!^\<#AQ0#=SW"``$0D +MH*!:#&_VJ7"*(/\/"@^O\JEQ300/\>!XJ0-/]_'`R@LO\4HD0`#`@:"``=_1 +M=<(D`@'1=:&!88#")\X3`=ZQ<\!^L7,!V\(CS@!,)`"`S"8BD,HC8@`*](7K +M@.;,)R*0`_("VP+P`-L4ZR$+4``Y"Y$`H(#`@0&`(8$")8V3H*(#($```:(0 +M\`#8`*(!H@SPH('`@"&!`8`")8V3H*(#(0$`(:*I`R_Q:'#@>/'`X<4F@$"` +M0B("@,HB8@"`XLHAP@_*(L('RB""#P``-A'*(X(/``!W`,HD(@!`$>%!NJB@D*`0GT'#5(0`*-! +M@`L)@0#B"Z__!H`9`P_QX'CQP)H*#_$(=@"`0B`!@,HA8@``V";I)H9!A@'? +M,'(@AD&&0:$@H@"FSW"MW@(``::FAL!_!H41#@$0J7!6""```MD&I::&!X4/ +M#@$0J7!&""``"-D'I07O?@NO_P:&`=BA`@_Q((`0<0(O\>EPX'A`@!4*``!D@@LC0(`%]$""]PH! +M@`#:X'](<.!XSW*@`,@?]!(``+S;&+L$((`/__\`\/0:```+R&5X"QH8,!4: +MV(#//'`C@D/\<]UH`#0&].%$0Z>%L]P@`#H.$H) +M```/#MX6SW"```@Y/@D``!$.'A?/<(``*#DN"0``#PY>%\]P@`!(.2()```1 +M#MX7SW"``,@X$@D``+S8&+@3I:$!#_'@>/'`*@DO\0#;"'?/=J``R!^D%@`0 +MSW6``,`;^&"D'@`0`=@3IBB%#(5`@0"``"+"@T"A+(4!(,```*$"V!.F*85- +MA0"!0((`(,"#`*$-A0$BP@!`H`38$Z8JA0Z%0($`@``BPH-`H2Z%`2#```"A +M"-@3IBN%#X5`@0"``"+"@T"A+X4!(,```*$$A0"`/@FO\^EQ)(4`H06%`(`R +M":_SZ7$EA0"A!H4`@"()K_/I<2:%`*$'A0"`%@FO\^EQ)X4`H0_8FK@.I@_8 +M#+@0IL]P@`#(.)8)C__/=8``Z#B*":__J7"&":__0"4`&'X)K_]6)0`2=@FO +M_U8E`!.9``_QX'CQP"H(#_$(=R#P`(8AAB&@`*$`V`"FSW"MW@(``::FA@:% +M$0X!$*EP^@WO_P+9!J6FA@>%#PX!$*EPZ@WO_PC9!Z4CAF!YR7"N#>__Z7`* +M)@"0"/(#AR"``H8B>*\(4H`&":__Z7`M``_QX'@/V)JXSW&@`+`?%:$/V`RX +M%Z'@?O'`G@_/\,]R@`!$R3^".G"JP0#8(0G>`L]S@`!,'6.#=(-($H$`P-UD +M>88A_PXBN3I]!/`4W0+8BA(!`0)Y$H($X58.K_4`VIH*8``"($\#`]C/<:`` +MR!\3H<]U@`#`&PB%`(!"P`R%`(!#P`F%`(!$P`V%`(!%P`2%P(`%A0`0$@!` +M$0`&`-D?9\]P@``DED"``8``(L*#`2!``$#"0<"+=Q\)42":#8_RA,$:<.EP +M,@OO_X;""'<($`(A"_""P>EP(@OO_X;""'?/<($`,!-$D,]Q@0`P$V6!!L$$ +MNQ<+9`!`*H`"&PA%``)Y_PA$@`;PAL""#&``2'$(<4;!+0^1$$H/;_/)<`AV +M2G`^#V_S!L$&PEIP!,,'P07``"+"@`$@0`!$PA7PE.].#V_SR7`(=DIP0@]O +M\P;!!,-:<`;!!<`'P@(C0X!$PP,@@`!%P!D/4!#/<(``3!T#@!B(A.#,)R&0 +M`-@#]`'8+R8'\$3TR7#:#F_S`]D(=TIPS@YO\P/9"'8`P`'!0"#`@$$A`0!! +MP03!0,`%P$`AP8!!(```1,%""6``1<`5"1$@!(7@H`B%`,$@H`R%`<$@H"4) +MD2`$A>"@"(4`P2"@#(4!P2"@!87`H`F%!,$@H`V%!<$@H!4)42`%A<"@"84` +MP2"@#84!P2"@!0;O\*K`X'CAQ>'&SW"``$P=PX#/<8``6+#/=8```'A4%0`6 +MP:'/`"U!XI&B@BX1W@!M4&)`(E*(0`B"+I'>`*U0XD"B0BZ1W@#M02))8D( +MN2=X`-D$M5,A#P`4(,XC08Y:<2"."+I'>2=X9@H@`!!X()5"(5$@.&`B;Q!X +M%"!"(`"U((I!B@BZ1WDG>$(*(``0>"&51&\X8!!X%""!(`&U0(DAB0BY1WDG +M>"8*(``0>"*5!N`8*(``0>".5.&`0>`.U +M08X@C@BZ1WDG>.X)(``0>"25.&`0>$IQ.&`0>`2U`>%C"76@+WFM!,_P\``@Q@$" +ME2\FB`$'((`!:@D@`!!X`"`'`@.5+R?(`0<@P`%6"2``$'@`)04`!)4O)4@! +M!R!``4()(``0>!]G!97P?^=X,@D@`!!X)I4A@^Y)7I0>@`B@0(P +M>0`<1#!'E2=Z7'D/ND5Y,'D`(8(!4'I<>0(00< +MA#`/ND5Y,'D`(4(!4'I<>083!Y.&!I +M<<:YA;D(N04AP0(@MA!X()4*'`0P)W@<>`BX!2```0&V`,`!I@'``J8"P`.F +MA0/O\*7`\<`2"\_PHL%*(``@`=_/<(``?@8`B`L@P(,H\D`HS2#/=H``6+"T +M?;AFN68$@".!4@PO_P7:N&8"@+IF(6@BH@+@0<"Y9L]P@`!8L*!@(8&^9F2& +MBW)^#>__$.``V0`E@!^``&RP(*C[?^]_0"!0("\@!R2;#Q&2`-G/<(``?@8@ +MJ/4"[_"BP`][2+@/>,]R@```4O0B``!`*`$"2+@%>?0BP``P>>!_)WC@>/'` +MSW*``+@X((*`X`HB`(#QP!?RX@_/_X#@RB'!#\HBP0?*(($/```S$/'`G@G/\`HG`)#/=H$`,!//=8``Z#@/ +M],]P@`"T4,EQY@DO_Q3:<@WO\:EP0"4`&!'P&0^1$/X/3_+)<`#9 +MSW*``-@&(Z(DHB6B)J(GHB*BSW"``,0X(*#/<(``:#D@H#&R,++/<(``B"[@ +M?R"@X'CQP,]Q@`#`(P"!E.@!V`"A`-G/<(``=!*V#^__(*#/<(``:!T0B(/@ +M<`DA`,H@80'1P.!^\<":",_P&@\O]Z3!@.#$"0(`SW"``'02`(#/<8``B"[* +M#N__(('/=H``V`8PEE&666$P<,H@+@#"($T`0H;/<8``Q#C/=X``:#F/Z@WH +M8(<;8V"G8($;8V"ASW.``#@RLH,=9;*CSW.``.0%8(,`W0<+40"@H2"!0\)` +MP2"'0L#/<(``U`4`@$'!@.#*(`$'RB$A!,HB@0\``*(`RB.A!\0/8?[`*R$& +M`-B6#*_XBW'/<(``U`4`@(/HH*<1\`"''P@4"H8)X`$`V`38SW&``'!Z"'2& +ML3"\>@[@`8>QSW"``'02((#/<(``B"ZBIK&V(*"PML]P@`#D!:"@G@MO\Q/8 +M`(<="%0!7@@@``'8Y@V/^L]Q@``P,QV!`>`=H07P1@@@``78W0>O\*3`%=@` +MVL]QH`#('V\9&`#@V)"X$*$)V+`9``"T&0``=-A"&1@``-B:N`^AI!F``,]P +M``P`&0ZAX'[/%D^C/<(``P",`@!\(40`B"L_W +M%PB0!L]P@`#`&P2``(`%I0'8!Z5)!X_PX'CQP.'%SW6``-@&!X49Z,]P@`#` +M(P"`*PA1`.H)S_,]P@`#`(P"`%PA1`,]P@`#`&P2`((#/<(``V`8CH.!^\<#/<(``P",` +M@"4(40#/<(``P!L$@,]R@`#8!@"`!**6#.__(X(QDCA@$;+1P.!^\<`N#J_P +MBB$(``AUSW"@`,@?,*`!V4$86`!6"0``SW:!`#`3`X8EAM6X,'#*(1>!^SW"``.@X)X`&Z0.`0(`"@4)X!?#/`$\@`@*-NI>Z1J,%(((/@`!`.D>C!2"`#X``P%,/\`4@ +M@@^``,`D1J,%(((/@```/D>C!2"`#X``@%<(HX01`(8)HP:%$@SO\R&%^@[O +M_P&%`02/\/'`?@NO\`#:.G#/<(``<.8,B,]VH`"T1T0@`0Y"*=``"G5Q%@&6 +M!"&!#W````!!*3Z%^?5#%@&61B$!#4,>6)!7%@&6O+F_N5<>6)!?%@&6O[E? +M'EB0`-F>N5,>6)#@>%,>F)!@'AB0)@L/_,]P@`!@)""`8'D$V!7H3"%`H%@) +MH?K*($$#SW>``.PY`(\7#0`0SW"``/`E-H!@>0#8`!\"%&8(C_)#%@"612`` +M#9^X0QX8D(T)$"`C"5`@8PF0(`HAP`_K`^XI7A?'AB0<18`E@0@@`]P +M````02@^A?GUBB#_#V\>&)!K'AB0$O#/<(``3!T#@!"],B"`#P``V`*?O8#@ +M`=C`>`^XI7A?'AB0!LB$X&0/(?/*(*$$D0*/\/'`.@J/\`AU*';*"Z_P`8"@ +MA1"Y02T`%#A@N@NO\,EQ$+FP>#A@K@NO\$`N@1)Y`J_P*'#QP`8*K_`$V0#8 +MSW6@`+1'2QT8D`#:D+IW'9B0`=IW'9B0SW*@`(1$&*(`VI&Z=QV8D`+:=QV8 +MD,]RH`"(1!BB`-B2N'<=&)!W'5B0@-AW'1B0`-B>N%0=&)``V)RX5!T8D,]V +M@`"8!">F+/YWM`":-'X`` +M6,CXC1,/D1#@D_M_(Y&`OR1_X+,%\`L/41`BD2"S`-DXK<]UH`#('/J%()/D +M>2RS!/`LDPD)10-9803PK+.Y8HDASP\$&%```>8`V<]P@0`P$P$!K_`GH/'` +M`!8$0`<:&#$`%@5``1I8,002@3"``(C(\"WIA19)Y88;J)I%1(4"`Q`@"\@W(`""!#X``!,C$J11](I'`'803%7]X'400`Q(!-L"G`8$$((`/````8"T(@0\````@$(G/<8$` +MR`H"N!9X`&'MN,HF8A#/<(``A!S4>""0$.$@L`/9SW"@`!0$,*`J">`!"G`] +M\'`2#0'@$P$!`B%.`Q$-A!/"?:)X$'B`&QP`SW"@`-0'#Q`.A@#=\!N$`W`2 +M`@'`&T0#0GDP>>`;1`#0$P$!`>$P>?`3!0'0&T0`4R5^@,HAP@_*(L('RB#B +M#/'`S@YO\`#8 +MSW&``)`Y`*$,S,]VH`#4!U$@`(`#V"`>&)"CP5+R%!X8D`,2`38`%@1`!QH8 +M,0`6!4`!&E@Q!,J&)$`%@!`!QH8,``6!4`!&E@Q!,J&)"4$@``#0A>`D8. +MC_<#$@(V#1(--L]P@`#HQQ0@0P,HDZ'I\(K/<8$`R`JU>`*_]G_A89@2#P`M +MN>Z@]J#`N<]P@`"$'/0@00"\&D0`T!,``00A@0\``/#_P[@E>-`;!``%\-`3 +M``&\&@0``=B@&@``"@YO^_"*@."H`R$``Q(--@;(42"`@9P#`@`AA1$)G@:0 +MV)"XC0,@`*`=`!`"O\]P@0#("D`@@@/V?^MBQ!6"$!$*P`"1V)"X:0,@`*`= +M`!!JA<]RH``L(/""C"/_CPWR8G\7#X4?`(```(?8D+A%`R``H!T`$/"-`K_V +M?^-@!".^CP```!/X8#GR$PM>`HO8D+@A`R``H!T`$%<+'P,%D);H!\@$((`/ +M`,```!4(@0\`P```$=@4N/D"(`"@'0`0B-B0N.T"(`"@'0`0I!4`$+2XI!T` +M$)(5`!&GN)(=!!">%0`1I[C-`B``GAT$$(78D+C!`B``H!T`$&*0,Q6`$$T+ +M#@`'R`0@@`\`P```,0B!#P#````(C2D(4P"D%0`0M+BD'0`0DA4`$:>XDAT$ +M$)X5`!&GN)X=!!`*\!$)G@&-V)"X;0(@`*`=`!`&R%$@`(`8`@$`4@^/_P,2 +M#38(C!8,B +M>(P@"8;*(24`I!4`$`DA@0#RN*P=0!#G\I@5@1##N0?(/'D$((@/`0``\`T2 +M!#;/<(``E,<6(``!99"L%0`002@($PD@SP!^%0`1@!4#$1MCSW"``$P=!(!& +M$``!&V,()\\08G^8%0,0Z+L`V(?R1",`!@0C@0\&````([@QN0'@.&#/<8`` +M`%0R(08`!".%#\````!!+84%,B%``4$K@0)2(0$`P+D#N&"$%0,1'7@GXR*[!2G^`"]Q4R$#`'EA/7G/5Y)7C`'0`0"J//<8``I#`!V`"A!?!/@K`5!Q$/"L4!!=@8N*`= +M`!#/<(``8`9!@""5"2&!``"($0A1`!D6`)80<0#8`_!,]Q@`"D,`"!@.``WBWR`-@`H7X5 +M#A&`%0`1SW&``$P=)('88$81#@$>9@,)GD7/<*``Q"P+@%,@@03^N,PA(H`) +M\I@5`!#J#^_T`-ITN!YF`O``W@,2`38(\`W(SW&``)3'%GG%D:EQ2B``(,]P +MH`#('ZP5`Q"([J05`A"QNJ0=@!`$\`DC@P,#VABZ3Z#X$`(`H6H((T,#0GN@ +M&,```-J8NDZ@"^ZD$0``\;@-S,4@H@3/(&$`#1H<,`&1".@-R,]R@`#HR/0B +M```%Z`&!#PB>`PW,@+@-&APP;@DO^RAP`Q("-KR21"4`$]4($`$-R,]Q@`#H +MQQ1YP!$``6&"I7@,=P@0#("F60(PM2``:0&PA>`!$+40!@$@`!A+A@&@0`!?`%Z3R2BKD\LJ02#0`7#1X2:!(#`5,@ +MP0!Y83!Y:!I$`!4-7A)J$H$`P[@X8`]X:AH"``?(SW&``/C(!""`#P#````/ +M"($/`,````X9!`0$\`#8B[@'L0&2%.@-R,]Q@`#HQQ1YT!$``5,@P(`*\O`1 +M`0'/<*``F`,^H+8:1`"D$@``!""^CP```#`'](8@Y8^L"V(`RB""`&8/P``% +MZ-X/C_VH\`/(I!````0@OH\````PLO+TN%0*P?0#$@$VI!$``)T('@/>#>_R +M`=@#$@$V';'/<(``3!VD@+((;_@`WAL(40#/<(``>!L`D('@`-[/)B$3RB8" +M%`/8SW&@`/0'!:&%)@(=#7#`L`/(79`-<$"P`\A/@`\*'@!"A0UP0*!&E0;P +M#7!`H`/(0!`"`0UP0+`#R%&`#7!`H`/(2!`"`0UP0+`0&0`$`\B4$```42!` +M@O0.0?!L]P@`!H!@"0';'/<(``;`9`@`&`4:$2H0CP +M)@WO\@+8`Q(!-AVQ%@_/_0/(]@RO_W@0``&`X.('`@`#$@,V`8.8$P$`E!M` +M`"T('@;/=8$`P!>I<&8++_EH<1#8#!H<,`W,H[@-&APPP@VO_ZEPJP<``)X3 +M``&^$P(!DAL$`)`;A`"N"Z`!@A,#`1L('@8#V<]PH``4!".@BB`0`'\'(``& +M&A@P`\BD$`$`AB'ECR@*0@`#$@XVI!8`$/2X:`(!`+".SW"!`*@)MGC/K`6`Q%DX^L+I0`)(D$`4FU6>L]W@0#("D)G!"*- +M#X`#```WO66]@.7*)0P4!"*"#Q@````SN@WB2B$`(`\AD2#CB!X,+_:8%@`0 +MF!8"$`D@003MNLH@8B!`*`,A='M(<,:X22#`!11[SW"``+Q+<&`-"MX"02@" +M`10B```HN+AX`^`$((`/``#\_\]R@0"P$P.BSW*@`,0L#:+LH@?(#1(#-@0@ +M@`\!``#P++@8N)VX%+ME>`5Y*J+/<8``*#0(@0'@"*$!"9Y%SW"@`,0L"X`$ +M((T/\`<``#2]4R"!!`L(G@!`+`3(*>BIY@6`!!J"^_T`-H!I\]Q@``P +M,P*!`>`"H0"!'67/<(``3!T#@`F`H*$1"%X`#1!"TN*0>`!"4%@`0D!8# +M$<]QI0"L_T#`L!8"$7BASW.``$P=9(-6$P,!%.-B>@/B(KI;8GIB2")"``6Z +M12)"`U:A(,($((`/````("6X!2("!$5XB;B.N!FA`]G/<*``]`!L`D('@`-[/)B$3RB8"%,]U +MH`#T!QF%@.#*(<(/RB+"!\H@X@S/("(#RB."#P``;@K*)`($!`2B\XI!D``*01```$(+Z/``!`"`?R`8'PN/0/@O(.\#J!#7`@H`,2 +M`3:D$0``AB#SCP3R.X$-<""@`=DKI0/:2*7/.!X4PA>0P/(SW&@ +M`,@?L!```98@00\>H1#8#J$!V!49&(#B#Z``0=@O"%Y#SW"``)`Y`=D@H`/( +MI!`!`)JYI!A``(H-;_\!V,]Q@``H-`V!`>`-H5H,```$(+Z/!@#*`)AP'O+/ +M<(``E!L#@(#@RB'"#\HBP@?*(.(*SR`B`\HC@@\``#\$&`*B\0^Y)7C/<:``_$0-H03P=A$-`0W,4R!`@`GR!L@$$@$VQ@IO]0T2`C;/ +M=H$`P!?)<'(-[_@#$@$V`\@&$A`VSW>``(`^#F__J7``V2"G +M">B&('Z/T_(#R*`80`0&&A@T`Q(!-I(1``$1")X"JKB^#*_ZDAD$``,2`C9^ +M$@$!@A(``8`2`P$X8!MC#Q5Y"8%X8`FA`8+9"-X`.@OO^(#8 +M!A("-@0B@@\"``$`#1(!-QD*@0\"````$0A>!T\AP@`-&IPP!O"CN3!Z#1I< +M,`,2`S8!@UL(G@%/(L`"C+@-&APP,(L0>C,3@`#/=8``2+`$N25XSW&@`#@N +M)($&M1#P+RY!$$XF@Q<`W@\FSA#&><]V@`#@Y?0FSA`1"(`#\>G/<```__\$ +MM0/P9+4(V`P:'##/<(``<.81B!4(7@'/<(``$,D^"J_]`(@-$@(W`\@!@/VX +MSR+B`=`BX0'/<8``M#(6@0T:G#`!X!:A+?`0V`P:'#`-S*.X#1H<,(8.;__) +M<`,2`C8!D@GH#`,]P@`#X +MR`,2`38"@)@9```&R%(,;_4-$@(VN0;O[Z/`\<#AQ:G!BW6I<,]Q@`#0/`X/ +M[^\DVJEP@@OO^`,2`38&"&`!J7"M!N_OJ<#QP.'%`Q(!-J*!((6*#B_^)-J. +M[0HAP`_K`*P%\%,E_H`F]`#=A"H*(B]W +M`">"'X``S+DJ<80I!`\$X@`B3@[)<#(+(`!(V58F`!DF"R``!MG'=X``.+PC +MART)$2"!N2.G3A0!-B2G):<0\`HAP`_K +M!JH-3_,-\$\,7@:J"8_R`Q(!-J`9``"&('Z/`_0`WP+P`=^(<(H+H`!H<0/> +MSW6@`-0'TJ7&#$_]SW"``(`<`("`X,PG(I`#]!,=F),#R*`0```P\`,2`389 +M#-X$;R!#`*`9``"*(`@`!AH8,-GQ422`A`#8SR#B!?7UI!$```L(G@8%V!"X +M[?$)ZL]R@`"L,Q""`>`0HO;Q"B'`#^MR"B4`"#+8SW,``"4)_01O\8RX*'`- +M!,_O\<"D$`$`#PE>`B8)S_W1P.!^*'2$)!*0$?(-"5\&I@P/]@?P(-G/<*`` +MR!PIH`/9SW"@`!`4):#K\>OQ\O`@@`#"N`\E#1``V`\@@``&(0&`[_4D[2\H00-.((X'#1J8,_78 +M!;@B"^_VR7$-R,]QH``4!`JASW&@`&0N\"$!`-.Y"G"*""_UY'G*#N_ZR7`` +MV`\@@`,&)0V0W_7/,[6"B_^ +M`N(@C<]P@``@!T"0A"D?```A@'^``%S+R0+O[T"PX'CQP$X*S^\H=D8AS0`= +M94H((``BN<&^'PY0$!,.D!`=#M$0`!:`0`$=$A``%H!``1T2$``6@$``K8$" +MS^^`X.@@K0$`%H%``1A2`.!^X'BK"1``0"'"`R2ZP[D"\`#9E0D5 +M!#,F07"``)`\0"<#!^@.'*)$UPX'CH(*T!`!8!00(85`#@?N!X\<`N +M"<_O`-W/=P``!!U*(``BJ785(H`S#A`!!@#8SW*@`!0$RJ*HHB>B!*(]98CA +M:+G*(0X``@GO]NEP0B!0("#GU0AUH`'F20'/[\]S@``(.BN+"PI"`"^+"PF# +M``#8X'Z,(`*!X2#!!P38X'\"V/'`L@C/[QIPSW"```@Z,B`2!,]P@``,!M&( +M$HA*(P`@$':AP50!*0``',`T:G<*(<`D`_!Z=40NOA,`(D`NSW&!`.`8,R$- +M`+M],0@S)JU]SW&``!`5&H$[@21X'0@>`L]P@``,!@N(BW/)<9X.+_>I<@#` +M`GVM?0`F@!^```P&'!#!`,]R@`#0-`"*!=KJ#B``J7//<8``V#D@@0#=2B2` +M<2)XJ"``!7-N='NU>\]R@`#@BWEB(8EZ8@OI(0D``"<(0@`O#5,1`>6O?0KP +M0B61$"\A1R1AO:]]$?`#$L\``-EJ=0SP@.5*(0`@RB5A$`7R0B51$"\A1R0! +MV2SI\V[T?Q4G01//`2Z+R0(`0(G@Q!L>"\@1@X^">_OB'$.>`)_".?N?T2_[7\) +M"!(F"N?M?\EP"G%Z#"``Z7(!YL]P@``,!A*(SWX0=L`&S/^1!Z_OH<#@>/'` +M0@^/[RAU@.+,(R*`"?(L;2]YSW:```P&,ZX&\,]Q@``,!K.IJ7'/=H``#`:T +MK@"N5:[Z"2``=JX`$(D`X8C)0.M>/`! +M$\``F>@`VEBM7*T@'8(02B2`<0#9J""``Q-N%'@U>,=P@`#@BT"H0:A"J$.H +M`>$O>5[P?+D`(8H!.HMLN@`B0!$:B.!R`"(&`NX((`#I +M""``Z7(;5YQW&``."+`*GX<0`2@!``$X$0C@@@`.ER`1\" +M`!4E2P,5(TH#`1.`$`$2@1!R""``Z7("'P(``!.`$``2@1!B""``Z7(#'P(` +M0B!($`'EF0AUD*]]`>;/<(``#`82B,]^$'9P!LS_`-G/<(``U#G!!:_O(*CQ +MP,]P@`#0-`"`SW&``-`Y((%-:#!RP"!L`4QY +M5B$!Y.&#@?P]XX'CQP+AQ-0A1``D-4@`=#=(#"B'`#^MRSW```-<4BB.( +M`D4&+_%*)```0"V!`&2Y`"&`#X``G`\=\,]P@``$$S(@00&,(<./RB'!#\HB +MP0?*(($/``#8%,HC@0\``!`""`8A\-'`X'[@>/'`SW*` +M`!X&"FJB"2``*6K*#0``S@@``,]Q@`"85""!SW"``%P.(@@@``':SW&``)14 +M(('/<(``B`T.""```-K1P.!^\,=P@`#T#@OPSW:``,P1%7X"N!1XQW"``-0/(8A+"1X` +M!1#!`"*N!A#```.NZ7""#"``2'$`KH#@S"!B@,H@(0`2\D0H/@<`(8!_@`!4 +MC,40@P#A$($``B+``!!X![B6#:_O8GD!KD(A02"!"76``>4=!(_O\<#""X_O +MSW"```P&$1"(`,]P@``,!A*(JP@"`DHF``!*(<`11"X^!R]PA"@#$2=P`""! +M#X``5(P?$$-#X``5(S5?0>-:7$%VIAP +M%@H@``45PQ!`+H(`5'J$*`$5`")!#M1YQW&!`.07N'$`J8AP27$'VNX)(``& +M%<,0`1T"`&&_`>:W#W60SWY"(4D00"9&`($)=9`O)H``*2.^&!W#3,6 +M(*A3)8`030A3`48ES1&O?1OP`12`,``F@1^!`%`54FU4>EEA(,(`J40NOA8` +M)4`>1*D4%,$P^&`@J,EP7@@@`*EQ`>6O?5,E@!#+"%*!(?`!%((P$FT4>``F +M@1^!`%`5.&!`J"#"1*C)<#(((`"I<0_P0B4`%@]X`12!,,=V@0!H%@*X%'@> +M9B#`**X,K@C$") +M@.**(0\*P"CB``3T1"B^`R]P2@N/[P"E`-T.WL]W@`#L.[X([_^H9V&^`>7Y +M#G60KWW/<(``T#0@@,]P@`#0.2"@+R`'!,]QH`"T#QRAL0&/[PYX+'@I:@#8 +M#R!``"=P6GC@?PX@P`#@>/'`)@F/[\]W@``,!@"/%@SO_S./SW"``,PY`!#0 +M`,]Q@`!X(!2/1XD?"@$``(\AB1<(00#/<(``U3D`$,``"2``!"\@!2"QCP/P +M`>6O?1*/$'7\``D``-Y*(H`CSW"``,TY`(@0Z$0MOA,`)D`>SW&``,PY`!'" +M```@@0^!`.`80*E?\,]P@`!@)""`8'D`V!D($0//<(``W#O)8`(@0"`->$@@ +M0``$\$@@0"`O(04@SW"``.P[RV`3CZEQX@AO]E6/"2!`!"\A!2#/<(``8"0@ +M@&!Y`-@`)9,?@``H!AL($`3/<(``8"0@@&!Y`-@$$X$@&P@1`\]Q@`"$!\EA +M!!.`(")X"2!!!`CPSW"``,P[R&`">0DA001$+;X3`"9`'L=P@0#@&""H.G`3 +MCZEQ)@^O_\ER`!'!(`)Y`!E"($(B4B`!YAD*=:#/?H'Q,0"/[_'`Y@]/[PAU +MSW"``)LD`(@H=Q,-`1#/<(``FB0`B#T/`!#/=H``#`:I<$`F@1*N"2_W0";" +M$BJ.!&ZZ""_W2XX*CFX/[_8KCL]P@`";)*"HSW"``)HDX*CU!T_OX<69Z(PA +MPHT!V%GV2B2`<<]S@``TC:@@P`.A:T0H/@%PU#$`?M$PB0`0'@#W@` +MV`/P8;@/>.!_P<7@>.'%X<8`$`&(&?`+#1,0`-V@ +MJ<]P@`!X.P"0#0T"$*EHK7V@J<]P@`#0.A0@3@.@CJ"J`!'!`#1X`8@`J\'& +MX'_!Q>!X\<#.#F_O`-BAP0`/'`Y@U/[PAUUW4E``"``-A*]\]Q@0`P$R6!)0E%`R)] +M`>#Y\<]P@0`P$\6`J7!2#V_OR7$%+CX0`B5-'HP@$(#*(<8/RB+&!\H@A@\` +M`,TBRB/F#,HD)@#8!N;PRB4&`1:X\05O[Z5XSW&``&`�A1``'8`*D!J0") +M@>#*(($/``#$"!_*:C/<:``+"`P@3A@SW&``)PLX'\%H<]Q@`!,'?`A +M`0!-D42Z#0H>``Z!B;@.H0L*7@`.@8NX#J$-"IX`#H&-N`ZAX'[@>`T2`C8$ +M(+Z/8````,]S@`#HQU1[QW*``%C("'$%\@/(')`7")X"!"&!#V$````3"8$/ +M`0````#8`+,!V!SP#,P#$@$V&PC>`3(1@0`!BPT(00``V`&K\_$!X`&K"_`Q +M$8$``(L+"$$``-@`J^?Q`>``JP+8X'\8JN'%X<;/&4V>,2HKF(!Y:]]P*C! +MQN!_P<7@>/'`*@Q/[\]P@``0%P"`H<&;")``SW:```@PSW6```PP`(4@AF4) +M``#/<(``F",$@$#!#0B>`$\A``%`P(CI7@UO]`#8/@U/]!H-3_;/<(``U`4` +M@(#@RB`!!\HA(0'*(H$/``"A`,HC80^("R']P"OA!2"&">D`A8?H(@UO]`'8 +M\@M/]B"&(*41Z5H.3_1_V`JXSW&@`-`;$Z%_V!"A`-B5N!"AI@@O\@'8[0-O +M[Z'`\\L]V@`"4(,B.,0[1 +M$;ZX"*)`@0B"!""##X````!$(`$"+[L&N65Y!""`#P`!``!!*$,#)7LLN`5[ +MP1K8``KM+RE!`TXA@`<2""``$"4-$/GM00-/[_'`U@IO[YAP&P@4!`HAP`_K +MN!3P+;G`N<]W@`!,'?`G3Q!2($X"P1AY^X +M`*4!XZ$"3^_/<8``3!WP(0``SW&``)3'NQ`"!KH0`P9"H6&AO!`"!KT0``9% +MH>!_!J'@>,]Q@`!,'?`A``#/<8``E,>^$``&%B$"``*2&K$#DANQ"(HX&0(` +M`-C@?QVQ\<#/<(``U`4`@(;HE@EO]E38`_``V$0@`@(;"!X!`-O/<9\`N/]] +MH0+;SW&``!`78*'/<8``"#!1($"``('/(&(`T"!A``"A/0B>`,]Q@`#<.0"! +M,0H``,]P@``'%`"(0*$7"%$`SW&``,@O`($+"%(`:K@`H0'9SW"``(P;-@[O +M_2"HT<#@?N!^X'C@?N!XSW&``*PS7!G`!YVXGKC/<:``R!P-H>!XX'C@>.!X +MX'C@>.!XX'C@?O'`(-O/-L.`9`!V`+P`-@-`6_OH<#@>/'`SW*@ +M`"P@0!($`$`2!0`/"=\"!""^CP`&```@\D$)'P//<0``$"<#\$`2!0#/<*`` +M_$09@.RX`B4``0/T[PA"@!T(@@\``!`G"B'`#^MRBB":"FG;J0'O\(R[T<#@ +M?L]PH`#T!_'`5P@>0R>`&8`P>3A@`[B6($(%SW&@`,@?'J$0V`ZA`=@5&1B` +MU@[O_X'8+P@>0\]P@`"0.0'9(*`#R*00`0":N:080`!^#*_^`=C/<8``*#0- +M@0'@#:$#V<]PH`#T!RJ@T<#@?O'``-D*V,]RH`#('QZB$-@.H@'8%1H8@"AP +M!_`!V00@@`\@````42``P\PA(8#,("&`$O0A"Q]`SW*@`/Q$'8)9@@#9V0K? +M@@0@OH\`!@``YO7G\2T+'D#/<(``D#D!V2"@`\BD$`$`FKFD&$``\@NO_@'8 +MSW&``"@T#8$!X`VA42``PP#8"O3/<8``K#,0@0'@$*$`V)BXT<#@?O'`2@\/ +M[PAVSW"@`"P@L(`+\-()3_#/<`\`0$+>"._RJ7$?"%``SW"@`-0+&(!"(``( +M2"```-\(A(-]!P_O"B'`#^MRSW```,XB7MN*),,/10#O\+ASX'CQP-H.+^\! +MV:7!&G#/=8``_`5:=>8,;_^+<``4A3`!%)$P"PA1($`E$A$+#5(`'0U2`0HA +MP`_KO\$HD0`!,)0"`)@$.`*AP`!:.0``6E$`/##(D>G", +M),.O)?0`%@!!`!:/0``6@$``%@!!A0P3)"COSW"``/@2`(!`+,T@M7T0X+A@ +M;@QO_P39SW"``/@2`(!,(4"@'67,)V&3&O0`V(RX%_`*(<`/ZW+/<```*B6W +MVTHD0`!]!Z_P"B4`!0HAP`_K +MW\`0`@8O*8$``B=`$"3J,FC/2MC$PN.`P`F@1^!`+`2%GD`&0(% +M`"V!$PLAP(`)\@`F@1^!`+`2%GD$&0(%$"("@"\I@0`")T`0X/5"(T`@@.#F +M!LW_>@^/\[D%+^^EP.!^X'C@?N!XX'[@>.!^X'C@?N!XX'[@>.!_`=C@?N!X +MX'[@>.!^X'C@?N!XX'[@>.!^X'CQP$(-#^_/<(``N#'/<8``2.?/<@``8")6 +M#F```("^#4_V+@A``$8+``#/=8``8"0@A6!Y`]@&Z""%8'D$V(3H4@\/_L]P +M@```>`#>R7'.#N_PBB(%!<]P@`!X!L"@SW"``.$%P*C/<*``+"`P@,]P@`#, +M!<]U@`!P>B&@SW"``*![P*"I<,EQD@[O\(HBA`O_V!T%+^\0K5$BP-'/-4@X@S5(.$$-G@`HR*0>Y(E +M"<(`)0M"`"&0>I(9"<(`&0M"``"0.)(-"$(`#0D"``D*WE(!V`/P`-C@?O'` +M1@PO[P#9O@D@``AV"'<#D,]U@`!X!L]Q@`!(00*U<@D@`.EPDNC/<8``0$%F +M"2``Z7`1Z`*5UW```($WS"""CP``@/,)\@.6".`#M@Z6:+@.MA?PSW&``%!! +M$@D@`.EP+I8)Z`.69+DNM@3@`[8(V`*U!_`O>`BX*+DO>05Y(K7)<&8)(``` +MV<]Q@`!0E`>Q`I77<```B(Y@#F'VRB"!`PT$#^_@>/'`G@LO[P#;2X"RD$HD +M@'%H=EUE4R5`$$X@!`$`)$(#J"```@$5CQ3(80'FYW@%>['K2B2`<0#>J"`` +M`@$5CQ3(80'FYW@%>Z7K`-A,)`"`RB0-<>@@+0(!%8\4#F$!X.=^Q7N7ZT`B +M``,#\`S@5B*.`M%PX(#`@N=^Q7M-]^&`P8+G?L5[XH#"@N=^!2.#@^[S`-@6 +M\`&`08('>@4C@X!0Y?GU0"2.`!4.E1$!%8(4R&$!YD=X^0ZTD05[Z^L!V$4# +M#^]`B6"(#0N!`$&)88@)"X```-@'\$*(`HGY"@&``=C@?P]XX'A`D6"0#0N! +M`$&189`)"X```-@'\$*0`I'Y"@&``=C@?P]XX'@(<<]P@`"X,5$#8```@.!X +M2X`#D%A@X'\X8.!X\<#AQ:N``Y"X8#A@2''2"F_]:'+9`@_O2X`#D%A@X'\P +M8.!XSW"``+@Q`(`1`V```-G@>/'`1@H/[\]U@`"$)`"%`-Z$Z*H)````I0GH +M(I"'Z5H-``#Y\;H)````I0;HPI!3)@Z1^O//<8``>`8@@<2Y!29.D`WR+RZ! +M$\]Q@0",'$XF@A?P(8$`0'D(\,X)``"`X.`-HO'*(&(#20(/[\]P`@`L)<]Q +M@0",'`2ASW`"`*`F`Z'/<`(`V"4"H<]P`@!X)`&ASW`"`"@EX'\`H?'`H@D/ +M[ZZ0"'9##7(0XY`+A@S@\&`/>0BY*+@%>2ZV0">`$P[A`[8#:1\-9!!&(-`` +M(@@@`,EP`B4-E`)WU`?O__!_`_``V`*VO0$/[^!X\<#AQ0X-[_\(=3X+(`"I +M<&8*(`"I<+D!#^_@>.'%X<:@@6"`P8%G?6""I'NA@*=^H8+$?:5[HH'"@*=^ +MHH+$?:5[HX`#@0=]`X*D>`4@_H`!V,!XP<;@?\'%X'CQP/H(+^^X<@AU*'?/ +M=H``N#&8" +M&6'MZ0#8%O`R"F``2'`2Z##9(*#NL`#:(!B"`"@8``%`(`$,*Z`&&$0!4[!" +ML"$8@@#U``_O\<#/".P.!^ +M\!P($"(,\`&P^B$UUGPGW!@<)]&0T2$`.!&0T# +M$'AE"?!B@2.!8GT)#4,06&`#\`#8C0`/[^!X\<#/<(``N#%:"6```(`'Z,]P +M@`!X!@"``^@`V`+P`=C1P.!^X'\+@.!_"X#QP-X/S^XZ<$`@$P(H=AIR3",` +MHLHAP0_*(L$'RB"!#P``NR'*(X$/``"R`VTR),,@1"6.$0`0!`':>\&[SW:``&1+]";.$&"2 +M!R,#`0LC@(,/]`+EKGT"XM4-8I`"X`PE@(0!V,(@"P!Y!\_N`-@`(-`D`!@" +M("$1@"!AN`]X(1D"(+[QX'CAQ7*0!?``VE&P`>-P>T%I'PK"`$N`;6*,)<.? +M49#`(F$`]//C"I*!/'`X<4(=<]P@0`,&0"`@0@> +M``Z5YN!X``L`(Y4`VH(@P0=1M3*U&6$P>:(/[_^I<(#@`=@)]`*5XKC,)2*0 +M!/*BN`*U`-A)"%$`SW&``$P=J7#V"N__(X$BE40A`@$2Z`7J+@L@`*EP$N@! +MV,]Q@`!P>@ATAK$PO"(-(`"'L0CP@.+,)2*0!/*BN2*US0;/[O'`X<41"%$` +M,@B/^RH/;_L`V`SP!-@`V2AR0@WO_RASSW&``(0D`*'/=8``A"0`A9(+[_\` +M@%(,(```A8T&S^[QP`X.S^X(=1(.[_]`(!`""';/<(``1,D`D,]W@`!0E!<( +M'@)`)@`4Z7%6#R_]`]I`)H`2"/`$;NEQ1@\O_0/:0"8`%"9O.@\O_0/:SW"` +M`'@&`I`&MP#>#_`!V"\F!_``((PC`*P(\B$5@!`!X`]X(1T"$`'FSW"``.0Y +M((``@2T.!1"$+@(5)W$.E941@@`0X!D(A`#I<%1NQW*!`"09.@SO_Q3AV^@` +MV-KQ(16`$#3H`Y4`W@+@`[4.E6*X#K4"\`'FSW"``.0Y`(``@$$.!1`R((`C +M=^C/>JEP1@WO_W#9<>@"E0\(G@#6"2``J7`4Z`+:SW"``'!Z2'$FL#"Y)[#0 +MJ,8+(`!(<`CP`Y5BN`.U#I4"X`ZU607O[B$5@!#/#R9.$`LF +M@)`*]`'A+WG/<8``>`8`@:&X`*$*\,9Z0*V&(/X#)=IF"B_P`-O1!._N`=C@ +M>/'`X<6CP<]R@``(!V**(8K/<($`R`JCB@`2A``$(X,/``#`_PZ()=I"*X4! +M0,(`VP2XAB'_`$'#0KE"PX8@_@,!VMASL'T^"2_P^'4!V(4$[^ZCP.!_`-CQ +MP`7H(I"DN2*P;@X/](H*;_X`V,]QH`"L+QF!B;@9H0'8T<#@?N!X\<#>"\_N +MSW6``%RM!XT$Z`:-!:T`W@X-+_;)<`6-)HWU"0&`QJW%K<>MSW"``'@&&03O +M[@"`X'CQP,]P@`"@>2"009`'"8,`(;`&#```SW"``'@&`(#1P.!^X'CQP.'% +MSW6``%RM'@PO]D`E`!_^#X_X`-A2'000SW"``'@&U0/O[@"`\0"0!?)`(0\$`_!`(0\"1"`!`Q4)$0((<88A +M_`.,(0*``ME#]@#9&.'HN-$@8H(&V`+T`-@X8`]XV&!`(!`"J7!Z".__+I4" +M(`0$()8`V%$AP($O)`@!RB4!``7R#)93(,4`"B8`!T`D!S'I<"1N0"8"%)() +M[_4*D>1!I$0ND5XC>@*(<`/ +MZW)NV(VXLMM*)```L0-O\`HE``'B"\_S!>T"E:.X`K7%`N_N`=CAQ<]R@`#, +M+V""'.O/=8``1,D?A1D(W@8-A0&C,!6!$#(5@!`(N25X`+,*\!$(G@88E0"S +M&I4!LQN5`K,`V`"BX'_!Q<]Q@`!$R2Z14R'!@`ST)0J>4L]Q@`!$R3^!!"&^ +MCP`8```(\@#:0K#/<9\`N/]7H>!^X'CQP.'%SW6``%RM0(4'ZL]S@`!X!B"# +M17D@HP#9(*6'Z,]P@`!X!@"`!.C*#F_Q#=@5`L_NSW&``$3)/X$5"5X&(Y`$ +MX2.P+I!DN2ZPX'\$V!T)W@8CD`CA([`ND&RY+K`$Z"*0@KDBL`C8X'X3"9X& +M(Y`(X2.P+I!HN2ZP]O'@?P#8X'CQP$X)S^[/<8``<'IGD4:1$+ME>H[J"B'` +M#^MR;MB-N(HCAPE*)```<0)O\$HE``#/=8$`#!D@A<]V@`"$).BYQB*"C___ +M$_X/\@H.S_4`V`"E`(9F#J__`(#:"D_[[@QO\0W8#?`+(0"`((8%\@GI`I&# +MN`3P!>D"D82X`K$]`<_NX'CQP+X(S^X(=\]Q@`!TR5$BP-'/<(``1,D`V@3R +M5)##NF")HHD(NP4ET!`?@,&!SW.``,PO42!`@L]P@```>`+9U2#B#,HA80#5 +M(.$$.G$#N31Y5G@`(8T/@`!8L"&`$PYD$`"C#PY!$`"0"P@"!`#8,/`"A1\. +M`1`4C2OH`X4D;^"EL@DO_07:`(7"J`#8%*T?\`'@VPX!D!2-&PA0``2%)&_@ +MI8X)+_T%V@"%PJ@!V!2M'PX#=````(#@I<]R@`!^!@"*#R!`!.H,[_T`J@'8 +M30#/[O'`Z@^/[@AU.G$:0R1SW.``%BP:'`'H0"#(8,0X'H*[_UC@P&.*G$`K0&.QKB%N`&M`(X"K2#8`ZT!A@&E +M0X<*<$(+[_VI<^D'C^[AQ>'&SW&``*!YSW"``-`T`(!CF0)[#PMR`$`A`@(< +M:P3P&`.>$HDP'$`VZ@@@`%M8@D-`Q`!XP*1"0L``&*Q +MP<;@?\'%X'CAQ13I88"E@(;K0X"$ZJ"@(:`,\$"`>F(+#8$0>6$AH`3P0X`Z +M8D.@`-DEH":@X'_!Q>!X\<`C@`3I)@@```?P$@@@`&"`"",``-'`X'[@>$"` +M(8`$@%EAX'\B>$*`(X``@%EAX'\B>`#;8J!@H&.@8:!FH&6@)Z#@?T2@(8"$ +MZ0.`!.@`V`/P`=C@?D&`%PF$`"*`(*`C@"&@`-DCH"*@!O`B>D&@0(!982"@ +MX'[@>/'`X<4H=2.`"',/Z:(/S_\W"$0#&>U"@R.#IJ,Z8D6C!X-88!;P?@_/ +M_R"#$PA$``OH$'7*($4#!J-!@_#Q`-@#Z0L)10,`V`3P!:,'@Z:CL0:/[O'` +M.@ZO[@AQSW*@`,`OHQ(`A@#=]0@>@0?(0!H8@`W(#0B1`>8/K_XH[A4'@% +M]*(/[_&+<1GP$PG1#1MX$'B2#^_QBW$0\`L)$04<>`GP#0F1`@``#8SW*I`*3_N:(`%`$Q@K@WHAJB2/!="1X"3"$`H-$@XJ%" +M],]RI0"L_\]P@`!,';BB!(!6$``!%.`">P/C(KMX8WA@2"!```6X12!``Q:B +M02C`(<"X=V@HP`0A@0\````@);EE>"5XB;B.N!FB'/`HP(#@RB'!#\HBP0?* +M("$.SR`A`\HC(07/(R$#RB0A`#0&(?#*)<$`!;VE>,]QI0"L_Q:ASW*@`,@? +MSW6@`+1'5Q4`E@#?2B1```0@OH\`*```PB0"`6\5`)8$((4/@`````0@@P\@ +M````!"".#P`&```/#!``0!(!!@L)U```V0+P`=G8<1,2`88$(+Z/`#@```0A +MAP\```"`S"8A@,`G81`%(T$!!2'!`04AOH,$]*`,]Q@`"T,A&! +M`>`1H4+=4/"(=(0D`I@(\L]Q@``H-!&!`>`1H0[P$PR>`<]Q@``H-`2!`>`$ +MH03P4R0^@P3R`-TX\!<,7@/V"P_]SW&``*0T!8$!X`6A]/$*(<`/ZW)O%066 +M1-B,N.G;"04O\(R[".O/<8``K#,0@0'@$*'B\2CN&0B>!L]R@``H-"^"`-T! +MX2^BD;T+\!T(7@;/$RHLX*;_^(<9B]1?!Q%026;Q4%E@HA +MP`_K`1H;#Q$Q(`AO"XRB`A +M`.@)8?_/(*$#:Q4!EE@5`)8+($"`'/)O%0"6SW6@`/0'4R!`@`'8#/()I>!X +M`-@)I<]Q@`"D-`B!`>`(H78/[_T!V`/8"J4%W9B]`_``W9?M%PC>(1T)$2`! +MV<]PH`#T!RR@`]D%\`/9SW"@`/0'):!1(("B*`\"^1?P`Q(!-L]P@0#`$P\) +M``#/<($`B!0;"0$`DA$``:JXDAD$`)X1``&JN)X9!`#/<8``M#(/@0'@#Z'/ +M<(``L+DA@,]P@`!,'0.`%)`="0$`SW&``!`5&H$[@21X42``@HP((O;*(&(` +MJ7`(W*L"K^ZBP.!XH<'QP$(*C^XH=0AV&G($(;Z/`0``P&AW+?0O#1X21"4` +M%B.X(6@$)8`?!@```#&X.&`$)8$?!@```==Q`@```1L]Q@`!$R3*!"PF>`D*P0[!$ +ML.!_6;#@>.'%SW6``%C*":4JI7BU2Z4!V!FUX'_!Q4HD`'H`V:@@@`(`VL]P +M@`!8RC5X0*`!X>!^```1`````$`!``````"H$D-U`08`!0``````````W#2` +M`'`U@``TAX``I!N``(`'@`#0!("!#QH;(@``&R4"`!M````;<0]%`"(`7``Y +M!P``8@9@`&(``%@X8$7`$'!%P!!X1<`0D$7`$&D``&$(`%AN^`\`80``$R4` +M`!,D)!#`$0"`$R0X',`1#P`3(@$`$S`$*,`1#W,3(H(!$S`$*,`1#W03(@(" +M$S`$*,`1#W43(D("$S`$*,`1#Q05(@$`%28/!,B +M!`#*$0```20```$E!@``80]V$R(L2,<1#W@3(@``QA$#``$D```!)0``$R7" +M+!,D!"C`$0)&$R0$*,`1PE\3)`0HP!$/10`B`%P`.2P``&0``!,D`0`3)3@< +MP!$/=Q,BX!S`$0(``6(/`1,B!`C`$5P%P!($*,`1#Q,"(F`%P!($*,`1#Q,' +M(F0%P!($*,`1#Q,$(@(`<7`'``!A_P`3)0(0$R0$*,`1```3)0``$R3(2<<1 +M!@``80``$R4"$!,D!"C`$0``$R5)`!,DR$G'$0]P$R(!`!,P!"C`$0,`$R0` +M`!,E!`C`$0``$R0X1<`1:`7`$A@HP!$/$P,B!```80``6#@``!,D`0`3)3@< +MP!$``!4D````(4@%@($``,`6#QM0(DP%@($``,`6#QL:(E`%@($``,`6#QL9 +M(E0%@($``,`6````A40%@($``,`6#QL$(AP$&V8;`1MH%!S`$`H`&T`$`!MN +M"P``80\<'2(!`!TF^0\`85@%@($``,`6!0`;8M`$@($/&ALB```;)0(`&T`` +M`!MQ9`P`$`#`!A$!``0G_``$9```&R0"`!LE.!S`$=`$@($/&ALB```;)0(` +M&T```!MQ```;)4``&R0P',`1T`2`@0\:&R(``!LE`@`;0```&W'X!8"!``#` +M%@(!$V1"`1,D!"C`$9`Y@($``,`6!@$38@0(P!`$`!-D#UP`(@H``$``!@!P +M&0``80``$R0``!,E``#`%P`(6##((,`0<$7`$!`(P!```!,E`P`3)!P(P!$< +M",`1```3)`0(P!$/%!4B!``5)OO_,#(#`!,D&`C`$0\4%2("`!4F!``P,``` +M$R001<`1&`C`$0`06#`/?!,B"`#,$0``$R4``!,D-$C'$0][$R(!`!,P!"C` +M$0\4%2("`!4F_P`3)0(0$R0$*,`1#Q05(@(`%2:X!H"!``#`%L(L$R0$*,`1 +M`D83)`0HP!'"7Q,D!"C`$0]-$R($$,41`@`3)/`8!8"!`!H;*`\;#B*P!8"!``!`%@$`&R8``$`7 +M&`2`@0\:&R(``!LE`@`;0```&W$````````````````````````````````` +M```````````````````````````````````````````````````````````` +M````````````````````````````````````````````````````````$`*` +M`)0&@````````````"@`@``````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````)#1$4"@T3%QD9&1D)"0```):`````````EH```````!S+ +M@`"\<0``,(`````````Q@```B(@S,S*`````JJJ*,X`````````T@``````` +M`#6`````````-H`````````W@````````#B`````````.8`````````Z@``` +M`````#N`````````/(`````````]@```JJH*`#Z```!3A8B(/X```````#`P +M@````````#&```":F5A5,H````"JJJHS@````````#2`````````-8`````` +M```V@````````#>`````````.(`````````Y@````````#J`````````.X`` +M```````\@````````#V```"JJ@H`/H```%68F:H_@```````4#"````````` +M,8`````````R@````````#.`````````-(`````````U@````````#:````` +M````-X`````````X@````````#F`````````.H`````````[@````````#R` +M````````/8`````````^@````````#^`````````,(`````````Q@``````` +M`#*`````````,X`````````T@```FGD``#6```"JJJJJ-H`````````W@``` +M`````#B`````````.8`````````Z@```JJJJ"CN`````<)FJ/(`````````] +M@````````#Z`````````/X````````#__P``I0$!`+D!WP"Q`!L`%@$;`*\` +M&P`4`1L`;`"@`-$`H`!O`(,`<0"#`'8`@P!S`#,`;@`S`'``,P!R`#,`UP`S +M`-0!!@#0`0``?@`\`.,`/`!X`$D`W0!)`'\`6@#D`%H`J@`_`*L``0`/`3\` +M$`$!`'D`:@#>`&H`J`````T!``"F`#<`IP`!``L!-P`,`0$`!``(`)P!S`"= +M`#,6`'PT%P"`-1@`A#89`(@W&@",.!L`D3H<`)4['0"9/!X`G3T? +M`*$^(`"E/R$`)$D&`BQ*"@(T2PT!/$P/`61-$0%L3A,!=$\5`7Q0%P&$41D! +ME5(=`9U3'P$!!````@4!``,&`@`$!P,`!0@$``8)!0`'"@8`"`L'``D,"``* +M#0D`"PX*``P/"P`-$`P`#A$-``%```0"00$$`T("!`1#`P0%1`0$!D4%!`=& +M!@0(1P<$"4@(!`0````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M````````````````````````@%6```````"R#,@`_________P`!__\"`___ +M_P3______________________P7_!O\'_PC_"?\*_PO_#/___PW___\.____ +M#____Q#______________________________________________Q'___\2 +M____$____Q3___\5____%O___Q?___\8____&?___QK___\;_____QS___\= +M____'O___Q____\@____(?______________________(B,D_R4F)___*/__ +M_RG_________________________________________________________ +M_____________________P```0`!`0`````````!```````````````````# +M``````````$`````````!"L!``````"X:@```0```!#2`0`"````A-$!``,` +M``#L&P(`!`````0K`0`%````?!$!``8```"XY```!P````02`0`(````O#T` +M``D```!`7@``"@```#C!```+````R#<```P```#L'@(`#0```.S=```.```` +M+-X```\```#HW0``$````"C>```1````A#\!`!(```"4Z`$`$P```(0Q```4 +M````[&4!`!4```"(40$`%@```-AA`0`7````\-`!`!@```!P``='L``*QV +M``#\C0``@'8``/R-``#\C0``_(T``/R-``#\C0``_(T``/R-``#\C0``#'8` +M`/R-``#\C0``_(T``/R-``#\C0``_(T``/R-``#\C0``_(T``/R-``#\C0`` +M_(T``/R-``#\C0``_(T``/R-``#\C0``S'T``/R-``#\C0``D'\``/R-``#\ +MC0``_(T``/R-``#\C0``V(```,!_``#\C0``_(T``/R-``#\C0``_(T``/R- +M``#\C0``_(T``/R-``#\C0``_(T``/Q[``#\C0``_(T``/R-``#\C0``_(T` +M`/R-``#\C0``N(P``/R-```8C0``)(H``/R-``#\C0``K'0``.2)``#\C0`` +M_(T``"A^``!`?@``_(T``/R-```T>@``\'8``/R-``#\C0``_(T``*2$``#P +M?0``_(T``/R-``#\C0``_(T``/R-``#\C0``,'T``/R-```DC@``L(X``)". +M``#(C@``7(X``$2.``#0C@``((X``/R-``#\C0``_(T``/R-``#\C0``_(T` +M`/R-``#\C0``/'H``/R-``#\C0``_(T``/R-``#\C0``_(T``/R-``#\D``` +M")(``#1U``"`=0``_(T``/R-``#\C0``_(T``/R-```@=P``_(T``/R-``#\ +MC0``_(T``/R-``#\C0``_(T``/R-``#\C0``_(T``/R-``#\C0``D(\``-2. +M``!`D```))```&2/``#LC@``6)````R0``#\C0``_(T``/R-``#\C0``_(T` +M`/R-``#\C0``_(T``%1W``!`>```X'<``'"0``"8=0``R'@``&!Y``#\C0`` +M_(T``/R-``#\C0``3'D``%!Y``#\C0``_(T``/1X```````````````````` +M```````````````````````````````````````````````````````````` +M``````````````````"XG````)L``!R>```HG0``,)\``````0#_____```` +M`/__________`0```/@3```````````````````````````````````````` +M`@``````T/X``````````!@@H``@(*``0"&@`$@AH``<(*``)""@`$0AH`!, +M(:``*""@`#`@H`!H(:``<"&@`"P@H``T(*``;"&@`'0AH``X(*``/""@`'@A +MH`!\(:``F!$```#_`P!0$P```/\%`-P1````_RT`N!$```#_/0`T$0```/\$ +M`%P1````_R4`R,,```#_W0!L$@``$!!,`#@3````_R(``!(```#_)@#8$@`` +M`/\H`*02````_R``N,(````@``#\P0```/\P``````````````$!`#P\/#P\ +M/#P\/#P\/#P\/#P\/#P\/#P\/#P\/#P\/#P\%145%3P\/#P5%145/#P\/``` +M```````````````````\/#P\/#P\/#P\/#P\/#P\/#P\/#P\/#P\/#P\/#P\ +M/!45%14\/#P\%145%3P\/#P`````````````````````/#P\/#P\/#P\/#P\ +M/#P\/#P\/#P\/#P\/#P\/#P\/#P5%145/#P\/!45%14\/#P\```````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M`````````````````````````````````````0```````````````````(0" +M`0!`J@``H",``$"J``!`J@``0*H``(0,``"DWP$`?-```$"J``!`J@``H#0` +M`*`T``"@-```H#0``*`T``"@-```H#0``$"J``!`J@``0*H``$"J``#(50`` +M0*H``$"J``!`J@``0*H``$"J``!@T```0*H``$"J```\P0```````&CI``!L +MZ0``M`(``*`"``"L/@$``````/BW``!`O@$`(+@``&B^`0!$N```D+X!`+25 +M@`"<+H``()"````````````````````````````````````````````````` +M`````````````0`"``(``P`$``0`!0`&``8`!P`(``@`"0`*``H`"P`,``P` +M#0`.``X`#P`F`"<`*``I`"H`1@!&`$<`2`!(`$D`2@!*`$L`3`!H`&D`:@!J +M`&L`;`!L`&T`;@!N`&\`<`!P`'$`<@!R`',`8!`0`E`"4`)0`!`%4#50-5`P$`!``$``0``0#<`=P!W`$! +M`&,`8P!C``$````````````R``4`,@`%``(`"`!D`*```P`````````````` +M```````````````````````````````````````````````````````````` +M````````````````````*.>```$````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M``````````````````````````````````````````````````#_```````` +M`(P*```````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M``````````````````````````````$````!```````````````````````` +M``````````````````````!@W`$`%0````,```#(.(`````````````````` +MK-L!``4````#````R#B``````````````````*#;`0`*`````P```,@X@``` +M``````````````!LV0$`"@````,```#(.(``````````````````O-H!``H` +M````````Z#B``````````````````+S:`0`*`````````.@X@``````````` +M``````"\V@$`"@````````#H.(``````````````````O-H!``H````````` +MZ#B``````````````````+S:`0`*`````````.@X@`````````````````"\ +MV@$`"@````````#H.(``````````````````O-H!``H`````````Z#B````` +M`````````````+S:`0`*`````````.@X@`````````````````"\V@$`"@`` +M``````#H.(``````````````````O-H!``H`````````Z#B````````````` +M`````+S:`0`*`````````.@X@`````````````````"\V@$`"@````````#H +M.(``````````````````S-L!``H````#````R#B``````````````````"3= +M`0`%`````P```,@X@`````````````````#$V`$`"@````````#H.(`````` +M````````````--D!``H`````````Z#B``````````````````&3G`0`*```` +M`P```,@X@`````````````````"8V0$`"@````````#H.(`````````````` +M````0-H!``H`````````Z#B``````````````````,#:`0`*`````````.@X +M@`````````````````!`VP$`"@````````#H.(``````````````````9-P! +M``H`````````Z#B``````````````````-3<`0`*`````````.@X@``````` +M````````````W0$`!@````````#H.(``````````````````$-T!``8````` +M````Z#B```````````````````````#(.(``R#B``+@@H`!L(*```(`!`/]_ +M_/\``````````.@X@`#H.(``I""@`#@@H``!````_/___P``````````"#F` +M``@Y@`"H(*``/""@`!````#'____```````````H.8``*#F``*P@H`!X(:`` +M0`$``#_^__\``````````$@Y@`!(.8``L""@`'PAH```#```__'__P`````` +M`````````````````````````)#9`0`5`````P```,@X@```````4``````` +M```X!H``?)F``!@````\F8``````````````````.`:``'R9@``8````/)F` +M`````````````````'\``````````'\``````````````````*![@``````` +M`````/__```!``````````<``````````````````````0(#!`0$!`0%!@<( +M"`@("`D*"PP-``!N.V@[8CM<.VXZ:#IB.EPZ;CEH.6(Y7#EN.&@X8CA<.&XW +M:#=B-UPW;BEH*6(I7"EN*&@H8BA<*&XG:"=B)UPG;AEH&6(97!EN&&@88AA< +M&&X7:!=B%UP7;A9H%F(67!9N%6@58A5<%6X4:!1B%%P4;A-H$V(37!-N$F@2 +M8A)<$FX1:!%B$5P1;A!H$&(07!!N`F@"8@)<`FX!:`%B`5P!;@!H`&(`7`!4 +M````;CMH.V([7#MN.F@Z8CI<.FXY:#EB.5PY;BMH*V(K7"MN*F@J8BI<*FXI +M:"EB*5PI;BAH*&(H7"AN)V@G8B=<)VXF:"9B)EPF;B5H)6(E7"5N)&@D8B1< +M)&XC:"-B(UPC;B)H(F(B7")N(6@A8B%<(6X@:"!B(%P@;A)H$F(27!)N$6@1 +M8A%<$6X0:!!B$%P05Q!2$$T021!N`6@!8@%<`6X`:`!B`%P`5``````````` +M````'0```````````````````````````````````````````````````"!< +M`0`(`````P```,@X@`"L!X``+`B``*P(@``L"8``"@T1%`H-$109&1D9"@H` +M```````&!@8&"0D)"0`&````!08'"`T.#Q`5%A<8&0`````````,%)J:'"2: +MFIJ:FI.:;H$_-0F+*P```#(```"/`(P`B@!#``\`(&@W````$0`^.B`1```" +M)0``#"\```(O.3D`"B4\MT=OB@`'%"=B+@```@`7```6#A84`````$)"%P`% +M$`H@,$````8&"A+V%?8&]@GV#/8/]@#V`P"``````%A;8V,Q```,$!08(`@$ +M```\.#0P+"@D(!P8%!`,"`0`"P<#`#LW,R\K)R,?&Q<3#PL'`P`[-S,O*R_S7_2_]@_W/_A?^6_Z;_M/_!_\W_V/_A_^G_\/_V +M__K__?_________]__K_]O_P_^G_X?_8_\W_P?^T_Z;_EO^%_W/_8/]+_S7_ +M'O\&_^W^TOZV_IC^>OY:_CG^%_[T_<_]J?V"_5G],/T%_=G\J_Q]_$W\'/SJ +M^[;[@?M+^Q3[W/JB^F?Z*_KN^;#YWXJ?AE^!_XV/>0]T;W_/:P]F/V +M<+F#NI:[JKR^O=*^Y[_\P!'")\,]Q%/%:L:`QY?(K\G&RM[+]LP/SB?/0-!9 +MT7+2C-.FU+_5VM;TUP[9*=I$VU_<>MV6WK'?S>#IX07C(>0^Y5KF=^>3Z+#I +MS>KJZP?M).Y"[U_P??&:\KCSU?3S]1'W+_A,^6KZB/NF_,3]XOX``!X!/`): +M`W@$E@6T!M$'[P@-"BL+2`QF#8,.H0^^$-P1^1(6%#,54!9M%XD8IAG"&M\; +M^QP7'C,?3R!J(88BH2.\)-`.)8YK3K#.]D\[ST$/QE`+D%"0E9#:D1]1:JJ`P``````JJH#``#X +M````J@/___\``%"G]%%394%^PZ07&I9>)SK+:ZL[\46='ZM8^JR3`^-+5?HP +M(/9M=JV1=LR()4P"]?S7Y4_7RRK%@$0U)H^C8K5)6K'>9QNZ)9@.ZD7AP/Y= +M`G4OPQ+P3(&CET:-QOG3:^=?CP.5G)(5ZWIMO]I94I4M@[[4TR%T6"EIX$E$ +MR,F.:HG"=7AYCO1K/EB9W7&Y)[9/X;X7K8CP9JP@R;0ZSGT82M]C@C$:Y6`S +M49=%?U-BX'=DL82N:[LE+?X>U(CTW.KX@)+ +M/'^,JJU5F!RCKL@/"M2^:>\6&I0@WT_*'*#"RI;\CNFH#`ER"%NTK',^* +MDK1YI_#R!_.AXFE.S?3:9=6^!08?8C31BOZFQ)U3+C2@5?.B,N&*!77K]J0Y +M[(,+JN]@0`:?<5Y1$&Z]^8HA/CT&W9:N!3[=1KWF3;6-5)$%7<1Q;]0&!/\5 +M4&`D^Y@9E^F]ULQ#0(EWGMEGO4+HL(B+B04<*?*'I#T)\R1Z$ +M^`````"#AH`)2.TK,JQP$1Y.U:X])SDM-F39#PHAIEQH +MT51;FSHN-B2Q9PH,#^=7D]*6[K2>D9L;3\7`@*(@W&%I2W=:%AH2'`JZD^+E +M*J#`0^`B/!T7&Q(+#0D.K<>+\KFHMBW(J1X4A1GQ5TP'=:^[W9GN_6!_HY\F +M`?>\]7)2XRMPQUQ"%8T)`(I<3(!'& +MA'TD2H7X/;O2$3+YKFVA*<=++YX=\S"RW.Q2A@W0X\%W;!:S*YFY<*GZ2)01 +M(F3I1\2,_*@:/_"@V"Q]5N^0,R+'3DF'P=$XV?ZBRHPV"]28SX'UIBC>>J4F +MCK?:I+^M/^2=.BP-DGA0F\Q?:F)&?E3"$XWVZ+C8D%[W.2[UK\."OH!=GWR3 +MT&FI+=5OLQ(ESSN9K,BG?1@0;F.";-]!A9;@&WFNRHFD^#96Z5 +MYG[F_ZH(S[PAYN@5[]F;Y[K.-F]*U`F?ZM9\L"FOLJ0Q,2,_*C"4I<;`9J(U +M-[Q.=*;*@ORPT)#@%=BG,TJ8!/'WVNQ!#E#-?R_VD1>-UDUV3;#O0U1-JLS? +M!);DX[71GAN(:DRX'RS!?U%E1@3J7IU=-8P!S4M+;DC-6 +M$.D31]9MC&'7FGH,H3>.%/A9B3P3Z^XGJ4('SK\H^Q&BY+#0D.%]`H\)RPQT6 +M#"7BO(M)/"A!E0W_<0&H.=ZS#`B-G820V*L`C+S3"O?D6`6XLT4&T"P>C\H_#P+!K[T#`1.*:SJ1$4%/9]SJ +ME_+/SO"TYG.6K'0BYZTUA>+Y-^@<==]N1_$:<1TIQ8EOMV(.JAB^&_Q6/DO& +MTGD@FMO`_GC-6O0?W:@SB`?',;$2$%DG@.Q?8%%_J1FU2@TMY7J?D\F<[Z#@ +M.TVN*O6PR.N[/(-3F6$7*P1^NG?6)N%I%&-5(0Q]I6-CQH1\?/B9=W?NC7M[ +M]@WR\O^]:VO6L6]OWE3%Q9%0,#!@`P$!`JEG9\Y]*RM6&?[^YV+7U[7FJZM- +MFG9V[$7*RH^=@H(?0,G)B8=]??H5^OKOZUE9LLE'1XX+\/#[[*VM06?4U+/] +MHJ)?ZJ^O1;^@X,=="PL6"X:&C0M&QLVLFYNW.Y:6K3[ +MH*!;]E)2I$T[.W9AUM:WSK.S?7LI*5(^X^/=<2\O7I>$A!/U4U.F:-'1N0`` +M```L[>W!8"`@0!_\_./(L;%Y[5M;MKYJ:M1&R\N-V;Z^9TLY.7+>2DJ4U$Q, +MF.A86+!*S\^%:]#0NRKO[\7EJJI/%OO[[<5#0X;734V:53,S9I2%A1'/146* +M$/GYZ08"`@2!?W_^\%!0H$0\/'BZGY\EXZBH2_-14:+^HZ-=P$!`@(J/CP6M +MDI(_O)V=(4@X.'`$]?7QW[R\8\&VMG=UVMJO8R$A0C`0$"`:___E#O/S_6W2 +MTK],S?]S[NQ].XN&L\%!0H>=[>I^)>7KP="PL6=MO;K3O@X-M6 +M,C)D3CHZ=!X*"A3;24F2"@8&#&PD)$CD7%RX7<+"GV[3T[WOK*Q#IF)BQ*B1 +MD3FDE94Q-^3DTXMY>?(RY^?50\C(BUDW-VZW;6W:C(V-`635U;'23DZ/!O)25* +MNT+FY:9&&AA=8P<&9)QT=.KF>GB'9$_CXZ[.8F"LS$1$BNVEI +MTG#9V:F)CHX'IY24,[:;FRTB'AX\DH>'%2#IZR["P>_Q45*C6N[MM.A86+/___P#___\!_P(#____!`7_"?\'"@8("P`! +M`0(!`@(#`0$!`0$!`0$"`@("`@("`@,#`P,#`P,#!`0$!`0$!`0!`@("`@(" +M`P,#`P,#`P,#`P,#`P,$!`0$!`0$!`0$!`0$!`0$!`0$!`0$!`0````Z`0(! +MU0#?`-H`H@!U`'\`B@4J`SD!J`&*!0"(`,H!2@'B`/D`R@'J`((`F0!TT447Z**++@`%!P$#!``%`04````% +M!@`"!``%``4```$"`0(#!```!08'"`D*```%``````````$````"`````P`` +M```````$`````@````4``````/\``/___R@`*``P`"P`+``H`#P`-`!``#P` +MC`!L`%@`2`#T`+``?_\'#Q\_`0,P````-@````P````2````&````"0````& +M````"0````4`!P(#!`8&0`.`!L`)``V`$P`:0!V`((`&``V`$P`:`"<`-(`Z +M`$'`"8`30!T`)X`Z`$[`5X!AF0,S!]D*8<@"`9)#,'8<62O, +M.0!!,TC9"J85@"!9*P!!IE:`85ELG=B)G4[LQ$XT2(,T)W9B)QJD01H3.[$3 +M$1B!$0_\P`].[,1.)W9B)QJD01H3.[$3#=(@#8F=V`D(C,`(!W[@!S1(@S0: +MI$$:$1B!$0W2(`T(C,`(!FF0!K"RU04%5$`%)W9B)Q,[L1,-TB`-B9W8"09I +MD`;$3NP$!$9@!`,_\`.JJJJJ&J1!&A,[L1,/_,`/$1B!$0W2(`T*J(`*$SNQ +M$P_\P`\/_,`/#=(@#0NT0`L+M$`+B9W8"0W2(`T*J(`*"JB`"@B,P`@'>(`' +M!WB`!P9ID`8/_,`/#=(@#0NT0`L-TB`-"[1`"XF=V`D(C,`(B9W8"0B,P`@' +M?N`'!W[@!\$L*0<*J(`*"(S`"`=X@`<(C,`(!WB`!P9ID`:PLM4%!FF0!K"R +MU04%5$`%!51`!=8=Q@0!!P\?/W___V;F```%!@$"`P0``%0`5`!L`&``7`!4 +M`(P`>``-#P4'"0L!`R@`*``T`#``+``L`$0`/``L`"P`/``T`#``+`!4`$0` +M5555`4MH+P%5554%XSB.`ZJJJ@)Q',``$````#```!>@`#AAX``@````0` +M``%Z``2('@`"`````P```7L`!(P?```````$```!?``$D1\``4````,```%^ +M``25'P`#````!````7\`!9Z-]@W_O=:QWE214&`#`JG.?589YV*UYDV:[$6/G1]`B8?Z%>_KLLF. +M"_OL06>S_5_J1;\C]U.6Y%N;PG4G2@PH33\E6"_$=ZK,CGNBLRE>:@P)@9T9Y_HV9$?E2K.X,+RHPIQ]-K/"AYI^*\ +M'19VK3O;5F1.=!X4VY(*#&Q(Y+A=GVZ][T.FQ*@YI#$WTXOR,M5#BUENM]J, +M`62QTIS@2;38^JP'\R7/K\J.].E'&!#5;XCP;TIR7"0X\5?'G,[8M(CR2%2#)28?_JGA0>J6/`_A9@`D7&MIE,=?&A+C0 +MPX*P*7=:$1[+>_RHUFTZ+``!`@0$````!`P,"`0,!`1`````@``````!```` +M`@``0``````$``!`````0`````#P80```0$"`0("`P`````````````````` +M```````````````````!```````````````````````````````````````` +M`````````````````````````````````&0````J````#@```````0$````` +M```````!`0`````"``$``@(#`P,!`@3_"!#__R3<`0`PW`$`/-P!`$C<`0!0 +MW`$`6-P!``````$````"````!`````@````0````(````$````"`````&P`` +M`#8$`@0"``````$"$`0(`````A`$"``````!`0`!`@$!`0``````````____ +M_P`````````````````````````````````````````````````````````` +M@`T````@``"`#0``@`T````@``"`#0````8````$```````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M`````````````````````````````````````````````````````````P`` +M`&@(`@`@((`/``!``&D@``!I($``:2```&D@0``@((`/``#H`&D@``!I($`` +M:2```&D@0``@((`/```PZFD@``!I($``:2```$H@``!*(0``2B(``$HC``!* +M)```2B4``$HF``!*)P``2B``$$HA`!!*(@`02B,`$$HD`!!*)0`02B8`$$HG +M`!!*(``@2B$`($HB`"!*(P`@2B0`($HE`"!*)@`@2B<`($H@`#!*(0`P"B2` +M/X$``$!!+)PP0"R<,$(D'#0*(H`_@`#86`HC`#=B#<`&2B8`<&D@0`!*)@!P +M2B8`<$HF`'!*)@!P`!8`<(``K"!`>"`@0(<`````````````_!R(MOP<2+;\ +M'`BV_!S(M?P`3<,-TQ\.!X!-PLW2_PX'@$W"C=+?#@ +M>`3<)-TK\.!X!-P@W2GPX'@$W!S=)_#@>`3<&-TE\.!X!-P4W2/PX'@$W!#= +M(?#@>`3<#-T?\.!X!-P(W1SPX'@$W`3=&?`T%!HP,!09,"P4&#`H%!.!X +MX'C@>.!X"B2`\`4@1`#@(,$'1"3^@$$JQ`"$``(`+R0"\4(A`0%"(`,!Z""B +M!`01!`($$04"!!$&`@01!P($&P@!!!M(`00;B`$$&\@!+``E`$0B/H$\`"(` +M1"+\@$`AP0#@(,$'0"/#`*@@@`$!$80"`1L*`2`@P`<$$00"!!$%`@0;"`'4 +M!^'_!!M(`40B_(`$$00"R0?O_P0;"`%"(4$`0B!#`*@@@`$!$80"`1L*`2`@ +MP`?/<:``K"\8@9JX&*$5`N`/!=C@>,]QH`"L+QB!L[BZN!BA`0+@#V38"B)` +M@`#9[@`!`"\F`/!*)D``3@`&`$\`(`"*)?\/X'@*(D"``-G.``$`;``D`"\F +M`/!<``4`*P@U"$HF0``(<0#8`B&^@.`@Q0=">0'@`B&^@.`@Q0=">>L'[_\! +MX"\M`0!`)44``B9\\0``(```*$`!Z"!B`R\@`(`O(4L``B&^@,`@A@'"(88` +MX'X1`"``2B``$$H@0!`.(D(`+R`+$LX@18"*)?\/"``%`"\M`0!`)44``B9\ +M\0``(```*$`!2B9``.@@(@,O(`"`+R%+``(AOH#`((8!PB&&`$HF``!"(/Z0 +MSB""`40@?I#.(8(!X'X)````X'@*)@#PBB"_#\H@9`#@?R\@`P#@?XH@_P_\ +M'(BQ_!Q(L?P<"+'AP^'"X<'AP`?`'!S`,>'`X'\!P%,B0H'@?$XB`X@6``P` +M`2C,```I@0``*(``X'^%>4XC`P``*,$`X'\">.!X4R)"@>!\3B(#B!8`#``` +M*#,`(`!*)```!R'$`"\F0/!*)0``$``F`"\D!`$. +M($"!`R5!`(#C#@`#``XB0H$#)<,`!2.%@#`!`0!Y"RB.!X +MX'C@>.!XX'C@>.!XX'C@>.!XX'C@>.!X8;V,)?^?[?7@?\'%X'CQP.'%SW"` +M`#PB38#/=8``!(L@A;>ZN+H$(8$/`P````>Y17DMH&(+H`\`V`"%SW&``'"S +M42"`@DR)SW"``(C",FHV><=Q@`!(OV"!5GA!@`7RE;M@H:NZ!/"UNV"AB[I! +MH`N-H[CY`N__"ZWAQ>'&P(!A@*"!`8$`)8V3`2#``*"B`:+!QN!_P<7@>*+! +M\``+0'H(]3)441'PTR!,:Y"B'`#^MRSW`` +M`,T;G]OI!>``BB2##P\-GA$`V`S<)P+/_\]V@``(PA8F31&GC:"OR746)4T1 +M`*44%``Q1JW'<8``R+X"M0")!ZT`&4(!`!M"`4%I"PH1"(HA_P\&\`#9#R&!`&&Y&'G@?RAP\<"R +M".__BB`/"H(D`CJ:<7IR6G.(=:AW"B&`(0H@P"'R"&``GL&*<.H(8`"+<8+& +M:G#B"&``R7%*<-H(8`"$P:EPT@A@`(;!B,7I<,8(8`"I<2IPO@A@`(K!"G"V +M"&``C,&I<(K!8@S@`)#"BW#)<58,X`"2PLEPA,%.#.``E,*&P*EQ0@S@`);" +MF,:0P)+!\@R@`,ERFL>4P);!Y@R@`.ERR7#I<2(,X`"__0"4"$XMPA,%V#>__0"4"%93` +M0"4!$YH,H`#)@R@`,ERE@R@ +M`,EPR7".P88+(`!`)0(7DL!`)0$37@R@`,EREL!`)0$54@R@`.ERR7#I<8H+ +MX`"O_X`D`CK@>,]Q@`"<6.!_ +M"&'@>/'`*@^O_]AP*'9(<8AU,@[O_\EP"'>I<"8.[_^H<0AQ`"Z``P1_)G\` +M*T`#)'AE!Z__Y7CQP/H+@`7/<8``.!SP(0``0'C/<*``T!N`VE"@SW"``,`@ +M`(``V0\('@+/<)\`N/\]H-'`X'[@>&"`0($!@"&!4'/,($&`X2#!!\H@(0`P +M<(;V!/8)"L4`X'\!V(H@_P_@?N!X@.'*)$UPZ"!M`L]QH`!0#"6!`1A2`.!^ +M\SW"``,2PS*$C@`T) +MY0`!&`($8Z`0CX#@RB!B``.E$8\3Z)/JSW"``#PB`X`)@!L(G@#F"^`!!]@! +MV`&ESW"@`"P@$(``I<]P@`"D,@"`'0A1`,]P@`"PQ\]Q```0)Q(/K_\%@!!X +M`_``V,]Q@`!LL`>Q`X4,&00$8PA1``"!@N#,(.*``_0!V`"A3!6`$$\(40#/ +M<*``+"#0@,]P`0"\%D#``=A!P`@<`#01V$/``-B,N$3``-@0V03:"'.8<+AP +M`":''P```'TB#>`$V'!+%8`0`>`/>$L=`A`,CX7H`86`X)0.`07/<(``7$#" +M#H`-`=G/<(``0!D@H!X+X`$&V#$%K_^EP.!XHL'QP,8,K_^8P`<@`,'BH>Y`*W/<(``M`=`B""H`=A' +MJPS!XH<'QP`,2 +M`C?7<@```$`!VL(BB@`7NL=R``X``(.Z['-`H^QR`*(*#6`$*'#1P.!_H<#@ +M>*7@'_()]B\(T``S"!`!.PA1`>!_`=B]X`_R!O8K"%$+X'\"V,S@#_*,($.' +M#?3@?P;8X'\`V.!_`]C@?P38X'\%V.!_!]@(V.!^X'CQP.'%SW6``,0PJ7!` +M)8$;S@_@#"[:`=CI`Z__81T"$/'`8@N/_Q\(M``(=0HAP`_KV5Z4QF8@`V05!D8@`;P4QF8@U09F(-$+;X;)W<.EU89&(`/EU@9 +M&(`0EU49&(`1EU<9&(`2EUH9&(`3EUP9&(`4EUD9&(`5EUL9&(!V#6`'*G!M +M`H__AN#QP`#8#_3/<(``U)9V"^__!MG/<8``=)<`@8*X`*$!V-'`X'[@>(/@ +M\<``V`GTSW"``,R63@OO_P/9`=C1P.!^X'CQP('@X<4`V`GTSW"``,^6`=TN +M"^__J7&I<"T"C__@>/'`EN#AQ0#8C/?/=8``!(NI<`X+[_\$V0N-@[@+K0'8 +M!0*/__'`FN#AQ0#8C/?/=8``!(L$;>H*[_\$V0N-@K@+K0'8X0&/__'`I,&0 +MX`#9RB!"`!/TBW#&"N__$-D`%``QA.#,(&*!"/3/<(``Y+(?@`D(7@5,<`'8 +MI,#1P.!^\<`J"8__"'?/<(``/"(#@!B(&G&-"!`!A.<`W88`)0#*($4#SW:` +M`+B60"8`$W(*[_\$V2Z.L*Y3(0``$:Y!*,`@H+E="&0``B!"`&._50K%`P_J +MSW&@`-`/$!$`AF&Z6&`0&1B`)1$`A@]X`O`/C@#94R""(`\A@0`D>"\F!_#/ +M<9\`N/\0KAB!SR#B!]`@X0<8H1B!GK@8H1B!OK@8H0'8[0"/_^!XX<3\',B^ +M_!Q(ON'`X<'APN'#_!P(L?P<2+'\'(BQ_!S(L?P<"++\'$BR_!R(LOP`AR7[A`H>!_`:'@>.!_`(",(%R"`=C@ +M?\(@"P#QP)X/;_]*)$``SW6``#PB%24#$`"#0"4.%=%PPB0"`?`E#1'(%046 +M1"6^@0GR"B'`#^MRCMB-N'T#H`!TV\@0#0:E><@86`"@@P;91GG(%0`6)'C( +M'1@0`(/($``&AB!_CM`.00^E!T__X'CQP"X/3__/D='MB3#!`%``0E@0_` +M_P``02D$!L]Q@`"PQQ01!@`%+CX!`"&$?S\`__\$)$$!'AY8D""0C"&"A@'9 +MPB%.`"JCYZ,D@,]V@``(EL"Y*K;/=H``N"@HKD"N`HBDHP&N'O`$@SD(40!& +M"D`(`-@$HP*#)(B2Z2>#'.`V>"2(SW"``&@E!X@0<0'9P'G/<(``M"@@H`+8 +M`_`!V`.CQ09O_P'8X'CQP%8.3__/=8``Q`8$A:/!`-^HZ*X*0``!V`2E`H4$ +MB(#@2`(!`,]P@`"T*`"`@.`X`@(`SW"``.`@$(#/( +M!1Q",`>(BW$&'`(P#@]@#*@2``#/<(``X"`0@""`SW"``+@H(:"."Z``Y:4# +MV`2ERO`$A7,(T0!"A2>%0"(`!S9X!8@I"%X!SW&``.`@`Y(P@<]S@`"X*""! +M88,*N&)Y#0D$``G8"Z6+\`6%C>@$BH#@J/+/<(``+)9>#Z`-`H"`X*#R!84% +MZ`78"Z4!V`CPSW"``+0H`("`X)3T`-@&",`&D/`$A=T(40"J"D`#(H5'A4`A +M``=6>$6(.0H>`(.Z1:C/@*(<`/`H7K$2(SW"``+P&`)`! +MWB$*`0#/<(``O@9`D,]P@``(E@J0#0H!`,2E`-A(\`2)&.C/<(``M"@`@)+H +MSW"``"R6(X#/<(``J"@`@+8*X`4X8(;H<@Z@!@#8`=@P\,2E`=@L\`2%60A1 +M``*%SW*``#PB(X)D@&BA(X)E@!S@::$GA39X)(@#@@#>-+`"V`39J@OO_\ER +MSW.```B60H4'A4`B`0<6>0J3)(E$@G(+H`O)<\2E`]@#I0'8A0-/_PP5!!`0 +M%040"B'`#^MRSW```(D,&0=@`(HC#@'QP/8*3__/=8``Q`8$A:'!K^@!W\]P +M@`"T*."@`-@/I0"E`:4"WLEP!@Q@!.EQSW"``)`&`(`F@)X1``:FN)X9&`#) +M<`#9&@OO_P3:2@L@#\EPSW"``#PB(X!(@3214R(``.(*H`OI<\2EZ7!J\`2% +M.0B1``*%!(@6Z`F%E.C/VT#`BW#Z#B`,&+L:ANFEA"@("0`A +M@'^``,"3*X"AN2N@!M@$I0#8!O`$A0\(D`$!V!T";_^AP`;8`Z4`V-7QX'CQ +MP*8)3__/=8``Q`8$A:7!AN@"A02(F.@"V`2E!(6I"%$`!86+"!$`SW"``.`@ +M!(#/<8``J$8`@%X*H`T@@;3H`-@X\,]P@`#@(`2``-[%I<]Q@`"H*`"`K@^@ +M`""!SW&``*A&`=\$V@"ASW"@`"P@0!`'`,]P``!T?4#`!=A!P$+'0\9$QLEP +M!MG)```B6 +MZI?!B@D.P!,`W@;PQHK]"8&#`=[/<8``M"C`H97NSW&``+P&()$C"T$`SW&` +M`+X&()%ABA,+00#/<8``P`8@B4:*"PI```#9`O`!V<4)$``<$`0`SW"``"R6 +M#!@``<]P@`!4B`00!0#/<(``L,<%@`4H?@%`*8!RD'#*(LX'RB".#P``B`S* +M(XX/```!`_@#;@#*(EP +M'?``V,]Q@``T1@"I`MDCI17P!(4!WB$(40`%A9SHSW"``"R6(X#/<(``J"@` +M@%X.H`4X8`;H`=B-!R__I<#/<(``-$;`J`(.8`0%V`#8!*6E\078"Z4^#R`& +MR7``V<]P@``T1B"HZ/'@>/'`\@X/_\]V@`#$!@2&Z0@1``*&!(@3Z,]P@`"T +M*`"`C>C/<(``+);R"*`-`H`'Z+8)H`8`V`4#``#/<(``X"`0@$>&((#/<(`` +MN"@!@`)Y`H96>`>`#PD$``'8!*;=`@```(8+Z!<+7D`"V<]PH`#0&S.@P@Y@ +M#1[8SW:``.`@!(;/=8``Q`8`@%H/8`TFA8#@J`(!``2&SW&``*PH`("R#*`` +M(($&I0*%)X4& +M'.`V>`40A@``VD^F@0X>`,]S@`"X*,]P@``D.KB`)("Y8<]U@`#,F1<5#Y98 +MJUP0!``,$`4``"4%`1@5!);B>0(E!0$5%0^6)!`$``(DQ(,6%0V6!8"B>,HE +M@1`$\@'=N*L-Z4`LCP`-"<0#3R6`$`7P!>A/)4`0#WT8JT$IP``980D)10&" +MO;BK60Y>``"&#NC/<:``+"`F@0Z&(GC/<8``N"@%H4"F!?`!A@/H0:;2#L`# +MS@U`#2<(D`#K=<(-0`T,%@00N'#/<```C`P*(<`/J7(]`6``BB,3"\H-8`T` +MV`*&)X8<=90L)10,8BX*X&*L`W<]W@``LEJ6G&!0``4`E00`/"24`)Z:' +M%``&$0A>``'82@]@!@RF3/!.#"`&"X8+R`0@@`____\#"QH8,.X/X`>KI@+8 +M`Z8"AL]R@`"T*"2(CNDGAAS@-GC/<8``:"4GB02(,'`!V,!X`*(I\"""!>D! +MV`.F(_`GAC9X'!`$`,]P@`!4B`00!0#/<(``L,<%@`P?`!$%*'X!0"F`!X\<#/<(``M"@`@!OHSW"``.`M`("9Z+X- +M``R)Z`O(!2"`#P```#P+&A@PK@T`#(GH"\@%((`/````U`L:&#`+R)"X"QH8 +M,/8,@`71P.!^X'CQP$H+#_](=4"`88#!@0"!6@XO_\EQ`*6=`R__(:7@>$"` +M(8!.(@.``-H#(D(`8*#@?T&@X<4!V\]R@`#D!7ZRX'C@>.!XX'C@>.!XX'C@ +M>.!XX'C@>.!XX'C@>.!XX'C@>.!XX'C@>.!XX'C@>.!XX'C@>.!XX'C@>.!X +MX'C@>`:X12#-`,]PH`#L)Z:@"H``VP"Q?K+@?\'%X'CQP-X(``/>#,`.J@K` +M#H#9SW"@`-`;,*#1P.!^\<"""@__&G`!WP`0$@$3\%IU$?`5(,`CH)`"$!$! +M`>?7=0``^__P?W3V&0J`+P``___/<```^__="(&$H0(/_\]V@`#8(`"&`>`` +MIA4(40`!V<]PH`#('#&@R@W@#BAP!KV!O4`I`"2E>,]QH`#L)P:A`(9"($"` +M`*;=]<]QH`#('`#8$:'7\?'`SW"``(`O`("!X,HAP@_*(L('RB""#P``KQ/* +M(X(/``#S`"T`.1@H`#-'`X'[@>/'` +MH@D/_\]P@``\(@.`SW,/``#\*(#/<(``0)K`N39X1(`@@`JZ9'K)N25ZSW&G +M`!1(3:%%@`&`"KK)N&1Z17@.H<]Q@`!PO0Z)AB#_`5MHSW"```1^3*A/B3") +M0"`3`X8B_P%#NH8A_P%-J$.YV@G@!BZHVG#/<(``V"``@`'@SW&``-@@`*$5 +M"%$``=G/<*``R!PQH+X,X`XH<,]Q"`"'$,]PH`#L)R:@`]@`V3(C52`Z<8T- +M,R):<$HD`"`:\$`E@0$P>0:Y@;D0OR5_SW&@`.PGYJ%`)H$!,'D&N8&Y$+@E +M>,]QH`#L)P:A0"14(,]P@```+B"`8'D&V.\,!2#M#0ZEJG#^#N`$BG$:<*IP +M>@W@!(IQF'!`*$`A$'@0N(&XA[B,N,]QH`#L)P:A30P0("T,42"*)<0&BB:$ +M""+P"B'`#^MRSW```+`3BB/%#0HD0`6%!"``2B4```HAP`_K'KE>E!_ +M0Y$`(XT!L'T&O5QZ@;T0NJ5ZSW:@`.PG1J8CD<"Y>'DE>!!X;/%"(D`@@."^ +M!NW_0"%!(,]Q"`"&$,]PH`#L)R:@SW"``-@@`(#/<8``V"!"($"``*$'],]Q +MH`#('`#8$:')!\_^X'CQP`#8C;BJ#V`+!AH8,`S,AB#_B@CRSW"``!0X`(B` +MX``)0@31P.!^SW$#`$`-SW"@`*@@+:#/<8``Z`A`@0%J`*'/<*``."X%@`0@ +M@`_`````'0B`#\````!(V,]QGP"X_QJA6Z%IV!BX&:'/L]84&[_ZCP/'`X<4(=03P`@@`#6(((`VIB'!#KE%>25X(,$5"5$`!Q2!,"+"!KD(ND5Y +M)7C@?Z/`H\'AQ4+!"12!,$/"0<`9"3,!`-@1"5(`"A2!,`D)4@`'"1(!`=@' +M%((P!A2#,!$+@``BP3!SS")"@`/T`=@AQ2$-41`*%($P(\,9"<,`"Q2",%!Q +MS".J@(3V@.+*(&D`&PA1`(HAR0_/<(``+`<@H('E_]G*(2(`(:#!Q>!_H\#Q +MP`HAP`_K4"!"PB!`$*1$0K``$>)[PK> +M@8#8`_`&B<'%X'^BP.!^X'C@?N!X\<`(R)6X"!H8,`G(F[@)&A@P"\B*N(VX +MD+@+&A@PSW"``#PB`X`8B!D(40`+R,]Q``#X**RX"QH8,$X+(`8/V-'`X'[Q +MP.'%"'4^B,]P@``4&$"`0"4`%`.Y-7E983H)8`P*VIH/[_^I<)T$S_[@>/'` +M(@S/_DAUP8``@"ARS@@O_\EQ`*5Y!._^(:7AQ6"`H($!@"&!`B-#@V"B`R!` +M``&BX'_!Q>!X\<"EP4'`0L$,'``Q$!Q`,<]Q@`!\?#09P`\P&0`/+!G`#B@9 +M@`XD&4`.SW"``'Q\(!A`"\]P@`!\?!P8``O/<(``?'P8&,`*SW"``'Q\%!B` +M"L]P@`!\?!`8P`C/<(``?'P,&(`(SW"``'Q\"!A`",]Q@```?(`9``A\&<`' +M>!F`!W090`=P&0`';!D`!V@9@`9D&4`&8!D`!EP9P`58&8`%5!E`!5`9``5, +M&<`$2!F`!$090`1`&0`$[Z'.H:VAC*$L&<`"*!F``B090`(@&0`"'!G``1@9 +M@`$4&4`!$!D``6.A:B```]@9``!J(,`"U!D``&H@@`+0&0``:B!``<@9``!J +M(``!Q!D``&H@P`#`&0``:B"``+P9``!J($``N!D``&H@``"T&0``:B"``(*H`40%`M@V```C8`-DF#>`%F;GU!4`,\#!*%5(D`-`Z%`(@`'=G@%B!D($0C/<(``O`8`D$AT@"1$$P"L +M'ML#\!C;8J%5(D`->&`%H3T`X`LH<.!X\<"."<_^SW"``/06`X`0Z,]RGP"X +M_QVBSW&``,`@!($!X+.XM;BXN`2A%J+/<(``:`9`@,]V@`!L$Z"&!"*##P\` +M`.`$(X$/`0```!)I9'@'?:"FF'4$(HX/````0,]U@`!H$^"%`[YD?CUYQW_@ +MI00D#0`$(H(/````@`8C0`-%>0*YY'X$(X,/`@```,9X9'DF>"\H`0!.($$$ +M#1I8,`\)D`'/<(``B+$.D"?HSW"``!P'`(C/$CH@3P +M-PD``"APSW.@`!0$"J//.!_`-CQP.'%SW"``&@&`(`$((`/#P``X"\M`1#F#^__3B5`%`HE`(`- +M\@HAP`_K#HHCQ0H=!.__3B5$%'_8"KC/<:``T!L3H7_8$*%A`,_^ +MX'CQP.(/C_[/<(```"X@@`'>8'G)<,]Q@``X"&*!!]^`X,P@XH`-],]U@`"H +M$.&E`-J`X\HC8@!S>\*[8J4,\`#:@./*(V(`EI0>O_@#8@^@`V`3P +M_PA1@`'82-D/(0$`SW"``/P0X'\QL/'`$@^/_@AV[(@HEL]P@`"(!K)O*'.& +M(_,/MGU"*Q$"QW6``$B_8(4(<@D+7@-$:.NYBB##+P3T'A:0$`V.42``@*+R +M>0G?`"L+W@+_V`>M2B0`<0#9J""``RAB`"&##X``,,?V>P2K*&(!X2]Y`*M; +M\",)$B$*(<`/ZW+/<```+26*(PL$2B1``*T"[_\*)4`$[KD'C3(B0@0`(8$O +M@``PQ_9Y"/)$J039`"E!!"5X!ZT]\$"I#R!`!&/P+0@2)(P@PZ_*(<(/RB+" +M!\H@@@\``"XERB."#P``Y`+*)&(`5`+B_\HE`@3J">__R7`(E@L(G@,"C@FM +M`_`!C@BM`(4Q"-X"`-I'K4HD`''/<8``,,>H(,`".&+V>`08`@0`&`($`>)/ +M>@&."*T"C@FM+/!,(0"ARB'*#\H@B@\``"\ERB.*#P```0,^!^K_RB+*!PB6 +M`"&!+X``,,?NN`>-]GD)\@09`@0$V0`I000F>`>MW?$`&0($`-D/(4$$)G@' +MK0&."*WM!8_^\<"2#8_^SW.``)`'8(,`WL]UGP"X__V%>6'/\R["PL%`.T+'L#/<8``;`9@@<]QGP"X_WVA42,` +MP,H@(@`?]#D*40#/`"K>=[8Z40&EB``=AI!8_^X'C/<(``[)C@?P:`X'C/<(``V)C@ +M?N!^X'@H<@D`(```V>'%X<9`*0T")7U`+0,4I7LE"C0""'53)7Z0!O(!'5(0 +M8;K[\4$JC@#!ND(F3I`$'=`0_?4)ZB\DB7#@>*@@0`$!'5(0X'C!QN!_P<7Q +MP.'%SW6``*B1((V,(<./"?('Z,]P@`!(/E(.@`S_V`"MSW"``%"1`-DUH,]P +M@``H&2"@SW&``.`M`(&BN/X)8`D`H0#8"@KO_PAQN02/_O'`X<4`W<]P@`#, +M!Z"@SW"``.`MH*#/<(``2):I=)VP,+R>L%H-(`2I<*EPX@D@"*EQA02/_N!X +M\<`"#(_^SW"``/06`H`'$@\V#1(.-@$2$#80Z,]RGP"X_QVBSW&``,`@!($! +MX+.XM;BXN`2A%J(&V`T:&##/=:``%`0*I0F%)N@#V!"E!*7/<(``0,CN#B`- +M`QH8,)+9`\B0N:`80`!:""`$`-@)A0[H*!4$$"05!1`>V`HAP`_KO +M_XHC!`8'&M@S`1H8-,]P@`#`(,JE`(`-&I@S$0B>`,]QGP"X_P#8':&U`X_^ +M\<#AQ8+@H<$<`2X`"'6V#>__`-K/OQ +M<0E1``H,[_Z+<"#`SW&```2+4R`"`(8@?P](J1QX":GL\5$)E`//<(``/"(# +M@!B(00A0`,]R@``XB$APT@OO_@;90"(``LH+[_X&V0R2@;@,LL'Q'0D4`<]R +M@``XB$`B``6N"^_^!-D,DH"X#+*S\1/8`_`!^X'CQP.'%`-C/=8``;+!*)$!Q)(6H((`"`-L/(P,`"R'`@`/T`>`% +M\&9Y`@]@`"2E!(6`X-`.H0#*(&$"(0*/_N!X"',X8-6[U;D-">4`-K@"(T(` +M"O#/GIB%KC@?T5XX'CQP'8)K_Z8<@AUSW:``,B+]"9` +M$,]W@`!(BU$@0(+*($$`RB0B=,H@(@#H("("]"8"$`D*7@(!X$<(%00MN\"[ +MSW*``*B^M'I`*X4"8)($O88E^!.)O0\C0P!@L@#:%G]`IT&GP[FE>04A0P$4 +M?F"VSW&``.B+%7D`&0`!`O"`V%T!C_[@?N!X\<#AQ<]Q@`!8D$&)SW6``"@9 +MSW.``.`M((,'Z@'8`*6"N2"C"/``VD"EHKF`X""C:`X""0#8>@ZO_PAQA@E@ +M`@#8)0&/_N!X\<"J"*_^F'`#$@$V`)$A@4#@]+G`(*(``^`$((`/``#\_\]Q +MH`#4!P\1#88`(`4!$PTE$0#:#<@5(@,P#A,`!AUE&1$`A@(E0P-[",4`!=T, +MO<]PH`#('[Z@$-VNH`'=%1A8@\]VGP"X_[V&SW"``&P&H*!=IAD1`(8+",4` +M^PL>P,]P@`!L!D"`SW"?`+C_7:`S"QY`#<@5(@(P#A("!L]PGP"X_U:@=J`9 +M$0"&"B'`#^MR0]C/.(/ +MH`4"V0'9SW"@`-0'-*`SH`/?[:`1$`"&SW&@`-0'0`IH.``L$\$<=F)//<*``U`<-$`"&0"\!)!!X!2$5``/((8``$!(! +M0\&X$)@``!V,!X#+A" +MP`/P0L8#R,]QH`#4!UF`B!F``*00`0#9H+@8@@.Z&(0#M[FD&$```\`3")X% +MSW*@`$@(0"(`(P;P0"(`(<]RH`!,"`3!`L,#<&5Y!255("=HSW,``/S_9'G/ +M.QR`*(#$@,V`8,?"!X!4HMPB\]P@`"(P79X +M`(B&('\,''@$N$5X`O"`V.QR`*H#R#MV4(@S$(``!+I%>.QR`*H#R!IV7)#L +M<$"P`Q(#-IP3``%O@R:XP+C`NT`H`@,-N\]P@`#`065Z0*`-$@(V`"*`#X`` +ML+'`J,]P@``TL59X5'G`L0*0P!F$`Q4D@@!X&00`SW"``#PB!(`:D-`9A`-& +MP,]P@``PR`*`P**`X,HGCA,2`RX`RB&.(\EUR7``-[7"-Z'+O`` +MWOJXRB:"'P```0+YN,HF@A\```("_+C*)H(?```!`@GJSW.``!0\4(.*)@@2 +M`>)0H_H/``X2\`'9SW"``+Q!(*":"^`,*'#/<8``D#P-@8HF"!(!X`VA!2>/ +MDP_R;0(@``#>A!(``"D(E`R;"Q]`SW```)`3^@_`!,]RH`#4!P^"$'@9$@&& +M6.#="02`"O#/T?`@V)IP`W`0>'(9!```W@L( +M$2`#R'/P`\`1")X%SW&@`$@(0"(`(P?P0"(`(<]QH`!,"$?!`W!(P`3!`L`E +M>`4E%2`(P`?@SW&``##((X$$((`/``#\_P@@5@`,)L"D+@$M`$G`/@T```4G +M#Y"8]`'9SW"@`-0'%!A8@%4E010/&%B``PH?0@C`SW*@`-0'%:('PP(B`"4` +M&T`%#Z()PP(FP"`;H@/8$*(JP)S@`-F0]`<2#C8`P`0F@A_Q``#P4'"4]`/( +MZ7+(N@(CDR4(B`RX17@#$@(W$+I%>.QR`*(*P$`A63`!&A@P!,@#$@(V*'9! +MQ0,:&#`$&I@P(8``D`'%-+G`N31X`^!`Y00@@`\``/S_'64-$@$V!O`5(D`P +M#A``!@)]%2)`,`X0``;O#060`\S/<9\`N/\8H<]PH`#\1#V`!"&^CP`&``!= +M]!D($"`$R%"(4R+!`(8B_@-$NL08@@`PJ,]PH``4!,2@!\C/<:``2"P=H<]P +M@``PR`*`0"!0(!)P%@7-_POPSW*``)@[(X**(1(@`>$CH@/P.G>J#(`%4R%^ +MH`7T^@L```5_GP\0$(D/7A`#R"F(`>$IJ,]Q@`"8.P:!`>`&H4#P"B'`#^MR +M*!0%,#S8C+C/!@6"@+\!X`6B +MN/$&@H&_`>`&HK3Q$0\>$,]Q@`"8.P6!`>`%H3IW`\CI<7/<:#^A`#/<)\`N/\VH`,2`C:2$@`!!!(!-@\(GP*2 +M$0,!;PN>`JJXDAH$`)(1``&JN+8.8`B2&00`$-G/<*``T`\0&%B`)!`!AL]R +M@`!`S$62,'D"ND5Y#!A8@!39$!A8@,]Q@`!`S&>11I$8V1"[97H,&)B`$!A8 +M@,]Q@`!`S&F12)$0NV5Z#!B8@`7PSW"``$#,RJC/7/<(``,,@"@/,-!)#/<(``,)HDD)3AP"&& +M#P``DP#/<*``:"SP($``SW&``,!!(('/=:``U``VB`]@2I5H-P`H-#UX2 +MN@ZO_P'`!_`#V!,=&)`4'9B3,P@0(,]PH``L(#"`!<`P<`'=RB6&$P0@CT\@ +M````SW```#45>@O`!(#ES"`#,('D//<8``/"(C@<]P@`!PO1"($+@R +M(8$/``#8`I^X@.$!V,]QH`#\1`VA'0L0(,]PH`#T!V`8P`3/<8`` +MF#L#@0'@`Z'/<(``,)HDD)3AP"&&#P``DP#/<*``:"SP($``SW&``,!!(($` +MVL]VH`#4!R5XSW&@`-0+#:%,IMX(X`P&P!D6`);`X)X`#@`-S)L(7@`#W2`> +M6),!V!0>&)`$$@$V`!8$0`<:&#$`%@5``1I8,03*G.#*(L('RB""#P``W`[* +M(X(/``#T"C@"8O_*(<(/*'!>"*`-#MD/%@"6!!(!-K09!``3'EB3$(E3(,(` +MAB#^`T2XQ!D"`%"ISW`2(```4@S@`PT2`C8$R,]QH``L(+`0``$O@63@,'#* +M((4/$B@(`(3WSW``*`@`!AH8,`#>#<"Q)I+/<(``,,@"@!EA,'DFLJW8SW(` +MNP"[%@C@!@6X`\@:D"X/X`8-$@$V<04O_JO`X'CQP.'%3PA>0\]P@``PR`&` +MSW&@`,@?EB!!#QZA$-@.H0'8%1D8@(X((`Y!V"<(7D,!V<]P@`"\02"@U@R@ +M#`'8SW&``)`\#8$!X`VABB4($BWPSW&@`/Q$'8$Y@00A@H\````(`-T']`0@ +MOH\`!@``&/(`W?JXRB6"'P```0+YN,HE@A\```("">K/["-C*#B`%`=D"V<]P@`"(*"2@200O_JC` +M\<#:"R_^+R0'`,]S@``\(BAWA.@A@P/P((/$$0$&);GP(PT`P+F`YZ*CHZ/, +M(2&``=G*(2$`SW6``(0ER(TK#E$2P8/$%@X6'PY>$+*(2$``-[/=:``M`_ +M`,]P@`"4.0"`B^C/<```%@D*"X`$"PA1`#()0`H*\`#9GKG/<*``_$0AH.!X +MP:#@^#T`,`_!R#T`,SW6```02`(V&Z`X(P`L! +MV`"M70,/_O'`\@H/_L]U@`!LL"25$NG/=Z``+"!0AP#>!H5'I7(,+_X.((`` +M"*7$M1"'Q;4&I2D#+_X(A>!X\<#/<8``;+``$00`N'#/2$'@="2,`!K($B@T( +M40`%BH'@`=@#\@#8@.!X"`(`T<#@?L]P@`#DLB"01"$``V,($0(`V\]R@`!L +ML&6B!(*@N`2B/0F?`022SW&``,2P`>`D@1!X'PDE``2R!(H/"%$`!8J!X`'8 +M`O(`V(#@*``"`,]P@``L-@.`"N@#V`GPSW&``"PV`X$"Z&.A!-A9`0``X'[Q +MP,]S@`!LL`03A``-#%$`!8N!X`'8`_(`V(SH!1.%``HAP`_K*` +M//(*(<`/ZW**($P-BB,(!PD$+_^*)(,/!)7/"!``[@S/_\]P@`"PQP6`*(69 +M(,T*,'`!V,(@#@"S"!``SW"``+PUZ:#7<0``$"=O(`L`$>@$C0T(40`%C8'@ +M`=@#\@#8!>@*#L__0?#/<```B!,(I4X.S_\[\`25FN@EE0B%@>'`(($/``"( +M$P/R&WC/<8``L,-=P +M```0)PBE;R`+`!'H!(T-"%$`!8V!X`'8`_(`V`7HF@W/_P?PSW```(@3"*7> +M#<__!)4%M>2U$(9Y!^_]!J7QP,]Q@`!4B$&!SW&``+#')8$%*;X`,'#*($X` +M#"$`\,]Q```0)X((+_[*($4.SW&``,2P!*'1P.!^X'CQP.'%`-C/,&X$J'@?N!XX<4`VDHD`'3/=8``2(O/!*),!S`-FH($`"SW"``*B^-'A`L`'ASW"``%0'0:#/<(``.(A, +ML.!_P<7@>`7P0GG'<$````#/0(@@`]`````SW*``+#'98+O"T2`4R!"!3IB"PN$`#A@!_`" +M((`/0````&)X.&#@?O'`I@W/_<]P@`!PLPR(&0C?`0*XSW&``$B_%G@%82V] +MP+T#\/_=F@U`!`GHSW"``(0E"(B'X`+8`O(`V,]Q@`"XEG>)SW*``%"C(8() +M"T``((*$Z0'?`_``W\]V@``\(B"&Q!$!!E4)7@&H[2.&.(E)"1`!_@D`#,]Q +M@`"@.AGOSW*``)`'`H(!X`*B`-C/`0H7H.#_X`$#NC/<(``A"4(B(C@S"5AD`;TI@D@#`'8 +M#@M`!8PEPY]$\A'OSW&``"@9`($+Z`#8`*'/<8``X"T`@:*X6@J@"`"A^@V` +M"\]Q@`"PQP:!12!``0:ASW>```2+"X]1(,"`I`D"_@N/42"`@%0(`@2B",`# +MH@Q`!(#@M`HB`,H@(@8&[0"&Q!``!B$(7P'/<8``L+($B0KH`XF`X'0)H0K* +M(.$`C@H@`!78J03/_>'%SW&``"P9`(D!VV&I).C/<*``L!]YH,]P@`#@(`B` +MHX%@@`*!`-HQ#0$0SW"``$09`(B#Z`'8"O`!@0(C#0#W#86?3`!`2T&I2'`' +M"%$`8:%"J>!_P<6BH>_Q@.`!V,(@#`#/!X\<#AQ0#=SW"``,!][@TO_QS9&]BF""``!=E*)`!WSW&``$P9 +MJ"#``A8A0`,$$`4`F'4/#4$10"1-`!D#S_T*(<`/ZW)WV`6XK0;O_E/;X'CQ +MP,]P@`#`?1@0!0`O+$$!3"0`A\HBQ@?*((8/``#B#LHCA@\``*L`?`;F_LHA +MQ@_/<(``3!D6(``!`(!`>-'`X'[@>/'`X<7/<`,`0`W/=:``R!]%'1@0J@_/ +M_X#8%1T8D*$"S_W@>/'`F'"X<9S@RB+&!\H@A@\``.,.RB.&#P``8P`@!N;^ +MRB'&#TPE@('*(L8'RB"&#P``Y`[*(X8/``!D```&YO[*(<8/`-K/<(``P'V> +MNA4@0`$@@`$J`@%%>=H,X`8@H-'`X'Z=!^__!=G@>/'`L@GO_?_:SW"``,R9 +M$QB8@!P8F(``WL]Q@`#$!L.ASW"``+`H0*`!VL]P@`"T*$"@S*'0H=&ASZ'` +MH<&A`MW)=\]P@`#`DX0O"!D`($(.2X(G<``AD'^``,R31B+"`$N@,@O@"T`@ +M`"%AO208@B/3#760`><"V`#9I@EO_@3:;@G@!`'8D0'/_>!XX'[@>.!^X'C@ +M?N!XX'\!V.!^X'C@?N!XX'\`V.!^X'C@?N!XX'[@>.!^X'@`V<]PH`#`'2>@ +M)J`MH.!^SW&``!P[$H$!X!*A#\]Q@``\(J2!5B5. +M%%8E#Q68$($`%0I>`H8A_P-$N2]GB;_I<1GP42(`@KP5`A$,\L*Y@"4"&3]E +MZ(\]93"-97_P?T5Y"?##N3QY/V8^9C".Z(]%>8@8P`-E>84`[_V,&$``\<#A +MQ0/(I!`!`)@0`@!1(0"`$*#Z`"`-JL:$X. +M``S/B!<]P@``PR*&`!/""$0T! +M#6"1!/""$`,!SW&``#PB)(&`$`T!5!$!`3UENV.$$`T!NV.`$`T!N6%^$`T! +M0GTG\$,(D0`#$@TV#EQ#.!X\<#B#8_]SW&``#PB\"$"`%8B100(@E8B +M!`51(,"`BB`(`,H@(0"\&@0`2B0`<@#9J"!`#\]U@`",3_R*+F7D?B\H@0-. +M((,'SW"``'11;V``)4,`X*M$$H\`Y'XO+H$33B:/%^Y@R*O(@B$.WA`=BH;A +MTR"F`"\H`0!.((T'SW"``/1-J&`1\,]V@`"T3RYFSF6\BL1]6!*.`,1]+RU! +M$TXECA?(8!"K`>%*)`!R`-NH(,`/W(K/<8``4%%O8<]U@`!T4>1^+RB!`TX@ +MCP?O90`EP`#\J$02CP#D?B\N@1-.)H\7[F4D&((#R((?#MX0/8J`X],AH0`O +M*4$`3B&-!\]Q@`#T3:EA$/`$Z\EK`_!H=LYA/(K$>5@2C@#$>2\I00!.(8X' +MR64L&$(``>-*)`!Q`-BH(``%SW&``/!-?8H)80`D#``!X&1Y+RE!`$XA@P?/ +M<8``]$UI82"L[02/_>'%X<;/'&F'#/\5[>:)X@J1[!"%!@V5X&*+?]<'&X'_!Q>!X\/$`CI!"<`%$(@`(!@>`1_!R>/DT''%O2Q#A$0(@K@ +M!`K8I0@0``HAP`_K0S8!A0! +M,1CI'A0`,0L@0(`-\L]P@`#H+6"`SW$``#!.#=A@>P3:"/"(Z,]P@`#D+2"` +M8'D-V`LG@),%\H(([_\*V`?PA^Y2#Z__"MA6"```W*4(W(\"K_VCP/'`(@JO +M_0#:SW.``"P:.X.Z@P#>#R8.$*1Y!"9`$$(@`(#*(&(`+R8'\`'=RB"!``?R +M'(,D>/(.[__%>*EP40*/_>!X\<#AQ:'!`=A`P,]U@``L&@J%&P@>`(MP!-EG +MVCW;U@Y@"A>["H6@N`JE*0*O_:'`\<"F"8_]&G`H=4AW:'8X8V;9/=H6#V`* +M%[H7"%$`"G#R#F`*J7'I<,(.8`K)<=T!C_W@>/'`<@F/_:;!*'4:0#8A0&O_:;`X'CQP!8)K_T!V:+!SW6``"P:&H5; +MA01Z'(4$()"`+?(#\#MY!"!`H/[S+R@!`$X@D0=<'4`4%25-%!V%@.#*(<$/ +MRB+!!\H@@0\``(\3RB.!#P``'`+*)`$$X`2A_LHE0006",__'85`>+H/C_\` +MV`\@0`0&(!`@L@WO_PIP^0"O_:+`X'CQP+3!!@U@`(MPM,#1P.!^\<"TP38- +MH`"+<+3`T<#@?N!^X'@`V<]P@`#\+^!_.*#QP+3!H@C@`(MPM,#1P.!^X'[@ +M>/'`5@BO_0#9SW:``"P:%X;/=8``?(`/(0$`&88D>$(@`(#*(&(`H<$!WQ<( +M40#/<0$`@*T+V*X.[_]5)<(7-X8`V`\@0``XAB1X0B``@,H@8@``V2,(40`+ +MV&#``1Q",`(C/<8``+!JX&0``&X&1N!NASW`!`*`Q".C/<8``+!H>H1N!@;@; +MH<]P``"T4`KHSW&``"P:E!D``!N!B+@;H<]P``"X4`KHSW&``"P:F!D``!N! +MB;@;H<]P``#44`KHSW&``"P:G!D``!N!BK@;H<]P`0"4.@KHSW&``"P:V!D` +M`!N!F;@;H='`X'[QP.'%H<'/__BB,(``C8`-E^ +M#>__*'(`V&$';_VAP/'`V@YO_0C9SW*MWN^^N@]@`CIPH@\@`"IPD0C0`,]P +M@`#H?0.03B#/`5$/U1'/<(``'`\&#B`!]"#``P#>`-T$V!IP*G#I<[[YV#V`"J7.V#R``*G!-"-``0B!`(-\(=8`!Y0'FTPX4D0'GNP_4D2IP +MSW*MWN^^1@]@`A#9,@\@`"IP'0C0`,]QK=[OOC(/8`(J<`H/[_\J<(/@RB`B +M`)$&3_WQP#(.;_T#VJ;!&G`R"^`*@\$#P<]P@``X$Q04!S``WO`@10#/<(`` +M0!/P($8`SW6``-`'#MC$I4#`!-A!P,]PK=[OOD+`!,(*<(#;S@Y@`IASS@D@ +M``IP?0C0``/#SW"``%@30H7P(,$`P*4,%1`0P:4(Z<]W@`!@$_`GP!"&Z,"E +MP:4`V1GPA"H,`Y((8``O<`X@@0\````!(*4#P(0H#"/P)P$0>@A@`"]P#B"! +M#P````$AI02%&PA1``"%$7B,(`>-PO?`I3%YC"$'C$%*7X`(X8O4W"$6#`\`/"$4#`=G/<(``T``*BW'6#*_](<`(<4+89@P@`06Y#!0$,`#! +MR7`&P@HE@`^MWN^^R@Q@`@+#B@X@`,EP50C0``#!!<+/<(``#`\`W?`@0``$ +MP0JZ!"*"#P\``/S)N45Y.@L@`:ER1@\@#078(!0$,`#!R7`&P@HE@`^MWN^^ +M?@Q@`@?#.@[O_\EP@^#*($(#]0-O_:G`X'CQP%X+;_T"VJ?!FG!Z".`*@\'/ +M<(``6$L`@`?91<#/<```$=+>"B`!`-K/<```$M(`V=(*(`$`VL]P```3T@#9 +MP@H@`0#:SW```!32`-FV"B`!`-K/<````40'V:8*(`$`VL]PH`"T#W`0%P`! +MV<]PH`"T#SR@G@X@#078O-AJ"R`!`-G#V&(+(`$`V8H@1`A6"R`!`-F*(`0* +M3@L@`0#9)<6UV$(+(`&I<8H@A`8Z"R`!J7$#V$#`!-Y!QL]WK=[OOD+'BG`$ +MP0/"'MN8*<`3!`\+AVYAS2B4``$HF```>"V`"2B<``!H. +M[_^*<(/@H?((%1(0#!41$`/80,!!QD+'BG`$P0/"X=N8__BG"#X(?R(H6CA2\@QP7/NH+(``O<`3"`B4! +M(,]P@`!`$U5X(*``V!$!;_VGP/'`5@[@``#8SW````W2`-E6""`!`-K/<``` +M#-(`V4H((`$`VL]P```5TL]Q\P___#8((`$`VL]P```;T@#9*@@@`0#:SW`` +M``+2H-F:N1H((`$`V@G8C+@`V0X((`$`VA38C+C_V0(((`$`V@#8C+C_V?8/ +MX```VA'8C+C_V>H/X```V@+8CK@`V=X/X```V@'8CKC/<0``___.#^```-K/ +M<```"](`V;X/X```VL]P```-T@'9L@_@``#:SW```!+2`-FB#^```-K/<``` +M$](`V98/X```VL]P```4T@#9A@_@``#:`-C1P.!^\<#Z#P_]H\&+<0'=\@R@ +M"JERSW"```1*`(!!P`380@@@`2S9#M@Z""`!`-DAQK78+@@@`#,(**`S"#B@,P@HH',(.*!S"`B@LP@HH+,(.*"`_2"N2]Y +MA.#,(&*!S""B@0.YM7G'<8``0)H`H0`3`"`$H0`0`"`;>`BA`!$`(!MX#*&J<*EQ +MR7(*)(`/K=[OOGH.(`**"J`*!-H`P`'!!+@U>,]Q +M@`#,#A!AI@W@``+!`,`!P02X-7C/<8``[`X089(-X``#P0#8I,#1P.!^\<"A +MP?H/8`*+<@#`H<#1P.!^X'CQP.X,#_VNP;IPFG%Z[[Z.#"`"`<'*"R``R7"#X,H@0@,)!"_]HL#QP)8++_T(V<]RK=[OOFH, +M(`((=G8((`#)<&L(T```V<]U@`#$"""ESW*MWN^^2@P@`LEP.@D@`,EP2PC0 +M`""%0"%!@""E\O,HE!X\<#AQ:'!BW$*"*`*`=K/ +M=8``>($`%`0PSW"``/P/J7$7VAH*X```VP`4!##/<(``]`]5)<$5`]H""N`` +M`MO/<(``+!!6)4$3"]H&"N```,,`V#$#+_VAP/'`L@HO_0#9SW*``'B!SW6` +M`"P:%X5(=P\A`0`9A21X0B``@,H@8@"AP0'>%0A1`,]Q`0"`K1#8!@FO_X`B +M`@`WA0#8#R!``#B%)'A"(`"`RB!B``#9)0A1`!#88,`!'$(P`AR",P,<@C.+ +M<`395B<"%)H(K_\H[[X!IMX*(`*J[[[&"B`"JG"B"2``JG#-"-``JG`/V<]SK=[OOJX*(`("VN(- +M[_^J<`#?2B3`)ZT(T``(%A`0#!81$-IW`_"I=^EU._":=3GP`"3-([U]L'VJ +M<*EQSW.MWN^^;@H@`@K:I@WO_ZIP=0C0``@6$A#/<(```"X@@`P6$Q!@>0'8 +M@>`*<"IQ2G)J<\HD@0\``,T:RB6!#P``XQ?*)((/``!-&/'`!@DO_0S:J<'/=D`?_P#/=3P`/#S/<8``$$JJ"2_]BW#/ +M<(``_`_/<8``0`\7VMX(X```V\]P```+T@P".```-F*(,4/5@C@``#9`-A=`"_]H<#QP.'%H<&+<=X,8`H! +MV@#!SW````/2/7E*#Z```-K2V`BX$]D^#Z```-K/=8``S`C/<```(-*I<7H- +MH``$VL]P```ATB1M;@V@``3:`-@)`"_]H<#QP'8/S_R:<4AWSW"``#@(9!`2 +M`,]P@``X"%P0$``,V0#=*'!Z<,]P@``D"?`@40-*F*U>L=R@`!L@M2J%:H0\!T/41!4:51Z0"R#(71[ +M>F*U>L=R@`!L@M:J%ZHY"9$`C>]`+($A-'F`(0($M7G'<8``;(+4J16I#O`= +M#U$00"R!(31Y@"$"!+5YQW&``&R"UJD7J4(C0"!!"'6``>7A!N_\`-CQP'(. +MS_REP;IP`-C/=H``.`AT%A@01,``V`.FSW"````N((!`(-DP"B*`+P``"-(! +MW6!YJ7`Z<`+8A!X`$$PA@*#,(>*@!/2$'D`3`X:$%@$0,'#J`@8`@.#*(H$O +M```(TH'@RB*!+P``"=(`W4PA@*#,(2*@S"'BH$+T!MU`\!"&"*81A@FF`X:' +MZ`;8T@R@`%8FP1(#A@\(40`(V,(,H`!6)L$2%H;#N`T(=`,6I@O8%J;/<8`` +M)`GP(0``*89(A@QY9!Y`'@QZ!(9H'D`>@^@%A@GH!H:#Z`>&!>B`XLPA(8`& +M]`#8%Z88IAFF&J:J<*EQ'@[O_T.&`>6'Y3("!@#/=X``_$&U?P"'`=J.X,(B +MC@!"IH#B(]G*(0(&&G&`XB;9RB%"!LH,H`!Z<<]P@`!$$,]Q@`"<$`/::@V@ +M``+;`X:!X,H@H0#*(6$$T`RA`,HBH0!,(<"@RB"A`,HA80*\#*$`RB(!`,]Q +MK=[OO@X.X`&J<$8-+_ZJ<(/@XO+/<(``3!#/<8``J!`5VAH-H```V](-+_X# +MAL]P@`!X$,]Q@`#\$#(-H``2VL]QK=[OOLX-X`&J<#H(H`"J<(/@P/(`ASZ& +M`*9`*0`"0"D"!`5ZSW````O217E&#*```-J*)[\=0,=!QPK80L#/<*W>[[Y# +MP*IP(X8*"R``JG"#X)3R$(8^A@2F +M$897:06F0"G``P5Z/7G/<```"])%>>H+H```VD#'0<<*V$+`SW"MWN^^0\"J +M<".&:G)*F$WA3>C1Z6*:*(0\*[@X@`NER&(80%!0PBB$/"A-XV@X@ +M`NER%X80%!38+H```V@`<@#4$',`T"MA"P,]PK=[O +MOD/`JG`CA@IR2G-`)(0B0">%(@HF``%F#.`!"B?`!$X*(`"J<(/@6`7"_PKP +M`X:$%@$0`>`P<"`%Y?\#I@#8<0/O_*7`X'CQP%(+[_P(V<]RK=[OOBH,X`$( +M=SH)(`#I<'D(T```WJ8(H`#)<,]U@``X"-NE(-@>I=^ER7`$N$4@P``EP;@SO_^EP00C0`!N%`>#?"/2`&Z7/<:W>[[[2"^`!Z7`Z +M"2``Z7`A"-``Z7#/[[ZZ"^`!$-G*""``Z7"#X,H@(@`I`\_\\<"Z"N_\ +M!-JDP1IPN@\@"HMQ`L`#PP#=J7$(VDHD0`)V"R_]2B5`!`AQ`<`6"J``J7(* +M<,]RK=[OOFH+X`$`P1H*(``*<&T(T`#/=H``.`C/<```(-)6)D$2.@B@``3: +MSW```"'252;!%"H(H``$VC*&\X9!*<`%P+@8N!-X)7A!+\$5P+D8N3-Y)7\2 +MIL]Q``!H'_.FY@OO_`BX%*;/<0``:!_6"^_\0"\`$A6FJ7!M`N_\I,#@>/'` +MX<6AP8MQ!@\@"@':SW6``/R!`!0$,,]P@`!,$*EQ%=H6":```-L`%`0PSW"` +M`$00525!%0/:_@B@``+;SW"``'@05B4!$Q+:`@F@``##`-@M`N_\H<#QP*X) +M[_P`V<]U@``L&A>%SW:``/R!#R$!`!F%)'A"(`"`RB!B`*'!`=\7"%$`SW$! +M`("M"=@&"&__529"&#>%`-@/($``.(4D>$(@`(#*(&(``-DE"%$`"=A@P`$< +M0C`"',(S`QS",XMP!-E5)D(8E@\O_XHC%0``V)T![_RAP/'`,@GO_`K:JL$( +M=BH.(`J+<0;8@@F@``'!"-AZ":```<$0%`0PR7``P0+""B6`#ZW>[[[>">`! +M`\,Z#N__R7"#"-``SW6``#@(%(48%`0P"J45A0#!"B6`#ZW>[[X"P@VER7"N +M">`!!<,*#N__R7!3"-``%(4@%`0P"Z45A0#!"B6`#ZW>[[X"P@ZER7"&">`! +M!\/>#>__R7`K"-``=(54%080R7`T%000.!4%$&RE)!0',"J%/!V`$;(/[_Q+ +MA0#8W0#O_*K`\<"AP8MQ:@T@"@':TM@(N`'9V@]@``#:`-BAP-'`X'[QP$8( +MS_RIP4#`0<$`V$C`@L5J#^`,J7"$QF(/X`S)<(;'6@_@#.EP`,"+/'`P@^O_`C9&G#/ +M=H``Y`<"V`>F"M@)IL]RK=[OOHX(X`$*<-X,(``*<(/@>_(`WPH-8`#I<,]P +M@```+B"`X*9@>0'8"'6"X,PE(I#,)>*0`_0&V`"F`(:;"-4!SW&``'Q&\"$` +M``'9CN`"IL(A3@"N#F``(Z;AI@+8$::"Y*0!/0!V!&F`-@%\`&& +M`>`!IC&&40A%`,]QK=[OO@H(X`$*<)H-(``*<'<(T`#/<:W>[[[R#Z`!"G`V +M#B``"G!?"-``ZJ;_V`NF"G#/[[[6#Z`!((:6#B``"G"Q"-&`'_``A@'@ +M<0CT@0"FSW&MWN^^L@^@`0IP=@XO_PIP'PC0``IPSW*MWN^^F@^@`1#9[@L@ +M``IP@^#*("(`!0>/_.!X\<"6#H_\&G#/<(```"X@@,]V@`#D!V!Y`=@Z<`*& +M`=V.X,(E3A,!AE(E#1"`X`;8RB`B`M(.8`!`)0$4"G#/[[X^#Z`!0"4! +M%.8+(``*<(/@$@(!`,]P```'TL]Q`P#PP+X-8```VL]P```&T@#9L@U@``#: +M(88*<`3:"B2`#ZW>[[[^#J`!_]OR"&``"G"#X.GRSW```"#252;!%-(+8``$ +MVL]P```ATE8F@1+""V``!-H3AJ(-[_\TA@AWSW````?2SW$$``XY5@U@``#: +MSW````;2`-E*#6```-HAA@IP!-H*)(`/K=[OOI8.H`'_VXH(8``*<(/@M?+/ +M<```(-)5)L$4:@M@``3:SW```"'25B:!$EH+8``$VA.&.@WO_S2&`B#0`XP@ +M!*X!W\HGI1`A"!,@`H:.X`'8PB`.``CH`8:`X`'?RB6A$03R`-VI=V<(4B!C +M"(,O``!\DL]P``!0P_8.K_P*<8#@RB!L`,CVC"`"B,H@A@\``)\`SW&``!Q" +M\"$``!5XS@ZO_(HA#PH=94/8(PWT$@6F`8:+Z`*&CN`!V,(@#@"!X`C=RB>A +M$`/R"-T!WW4.`W0``"3TSW$``%##D@ZO_`IP@.#*(&P`Q_:,(`*(RB"&#P`` +MGP#/<8``'$+P(0``%7AN#J_\BB$/"B&&FNDBAH[A`=G"(4X`*0E1``T(U`!. +M((T#`-\.\$X@S0(`W\]P```+TL]Q(``@(/X+8`#I<@&&!+_]99#H!MC2#&`` +MJ7$"V`K9X@M@``AR'PG1(`+8"=D(\`C8M@Q@`*EQ`M@1V<8+8``"VJ2F`-B1 +M!(_\X'CQP"H,C_RAP3IP`MZ*(`$ISW6``.0'$-@0\.R%`-A`P`IQ^&<5>"8/ +MX`&++<`#``>8:=T`F#Q17#C43(853"-0#@.$&V,H@(@).#&``Z7'D +MI2IPSW*MWN^^N@R@`>EQ8@D@`"IP0PC0`"J%2X4J<`';"B6`#ZW>[[Z:#*`! +MBB3##RX((``J<(\(T8`-\$`FCQ.`X0;8RB`B`OX+8`!`)H$3Y*4`V.$#K_RA +MP/'`>@NO_`3:I,$:<'X((`J+<0#!SW:``.0'88;/<(``;$8$%!$P`-WP(,(` +MSW"``'1&\"#/`,]P```&TEAYR@I@`*ERSW````?2`"G!([H*8`"I<@IPSW*M +MWN^^"@R@`22&M@@@``IP3PC0`"&&`L(*<`HD@`^MWN^^[@N@`0/#X@T@``IP +M,PC0`,]P```@TE4FP13""&``!-K/<```(=)6)H$2L@A@``3:$X:2"N__-(8, +MIJEP)0.O_*3`\<#AQ:'!BW'&#^`)`=K/=8``?(``%`0PSW"``+@1J7$3VM8) +M8```VP`4!##/<(``L!%5)<$4`]J^"6```MO/<(``X!%6)<$2$MK""6```,,` +MV.T"K_RAP/'`8@JO_`':"';/<(``#$H`@*+!0,"!P5X/X`G)<`'"SW&``.0' +MBW/)<,6Z0<*J#:`%(($@P((-H`D'V1IP`12`,'8-H`D'V0AV"G``V0C:R7-* +M)$`"\@KO_$HE0`1:<`(4@#!2#:`)!]D(=0,4@#!&#:`)!]D(=ZEP`-D(VNES +M2B1``L(*[_Q*)4`$.G#/<```"-)*<5X)8```VD'8";@*<5()8``!VL]P```! +M@LEQ0@E@``':SW````G2*G$V"6```-K/<````H*I<28)8``!VL]P```#@NEQ +M&@E@``':`-C=`:_\HL#QP.'%`-@(<0()8``"V@'8`-GZ"&```MH"V`K9[@A@ +M``+:SW"````N((!@>0'8"'6#X,H@H0#*(6$"T`AA`,HBH0#//'`U@BO_`O:SW"``(`1SW&``)@1_@A@`*'!SW6``.0'084% +MV$C9&@E@``\A@0`#A<]V@`!,1HMW%28`$`"07@\@`.EQ`X4`P14F`!``D/(( +M8`#&N0.%SW:``%Q&%28`$`"0.@\@`.EQ`X4`P14F`!``D,X(8`#&N0#8Q0"O +M_*'`\`)R7+/=8``Y`<`A0*X%'@`((\/@`#X@`#` +MR*4`I<]P@```+B"`8'G)<`AV:PC1`,]P```+TD#9D@\@``#:!MAN"&``"'$* +MV&8(8``"V3/8`-EZ#R```MHTV`#9;@\@``+:-]@`V68/(``"VCC8`-E:#R`` +M`MH[V`#94@\@``+:/-@`V48/(``"V@+8"=D^#R```MH,\,]QK=[OOHX(H`$* +M<`H)[_\*<-D(T``"A<]QK=[OOH[@`=C"(`X`@>`*%FPAE``BESW&MWN^^1@B@`0IPG@@@``IPY0C1@$;P00[1 +M$"X(@`&*""``"G!]"-``SW&MWN^^&@B@`0IP0@D@``IP90C0`,]QK=[OO@8( +MH`$*<"H)(``*<$,(T0`F\/(/0`$:"2``"G!!"-``SW&MWN^^W@]@`0IPQ@KO +M_PIP*0C0`,]QK=[OOLH/8`$*<"(((``*66%+A8(/8`&8 +M=A8+[__I<)L(T``,A2J%`MM)A0VEZ7`*)8`/K=[OOEEA2X5:#V`!F';N"N__ +MZ7!S"-``#(4JA0';284.I>EP"B6`#ZW>[[Y">4N%,@]@`9AVQ@KO_^EP2PC0 +M``R%*H4"VTF%#Z7I<`HE@`^MWN^^0GE+A0H/8`&8=IX*[__I<",(T``P%040 +M/!4$$`J%*85`'4`1386J":`%;H4*I +M[[YIA2J%#:7)<'IB!-N.#F`!BB3##R(*[__)<'4(T``,A4N%"B6`#ZW>[[YI +MA2J%#J7)<&)Z`]MB#F`!BB3##_8)[__)<$T(T``,A4N%"B6`#ZW>[[YIA2J% +M#Z7)<&)Z!-LZ#F`!BB3##\X)[__)<"$(T``P%040/!4$$`N%*85`'4`1387: +M"*`%;H4+I0#8E05/_/'`H\&+<28*X`D#V@#!SW```!O2C^D!V8X,(```VL]P +M```$^#"```-H`V*/`T<#@?O'` +MJ@QO_`78H<'/=8``.`A#A4C9_@P@``\A@0`"A<]V@`!41HMW%28`$`"00@L@ +M`.EQ`H4`P14F`!``D-8,(`#&N0*%SW:``&1&%28`$`"0'@L@`.EQ`H4`P14F +M`!``D+(,(`#&N0#8J01O_*'`X'BAP>'%X<:X<,]P@`!0HQ`0!@#/<(``@"\% +M@)AQH<&&)/E,AQP`D?E1Z0"Z-`;1]NF(5>L]Q@`!` +MG$AAU'X(SIB08IE>$ASAB/]#WM[W645)WNY82.)97XHV5Y)PP0`,]UJ@#@!W.%$0L>`$BE":4JI,]RIP`42`.B";DE?L2BSW&``*A$`!F``<]P@`"P1``8 +M0`'/<(``K$0`&``!H<#!QL'%X'^AP/'`SW$`@@$`SW"@`*PO/*#/<(``E#D` +M@(OHSW"``"P<`(`/")``A@G``M'`X'XR"4``#@B@!&_8A^B&#B`,"M@>"4`` +M\_'S\<]R@`"4.2""!GG@?R"BX'C/(*8)H`;(V%`@D"!,(("@&?(( +M]B,($"!%"%$@%=@3N`WP)0@0)#4($2CV"V`#*G``I0_P*=@2N/`@0`0`I0GP +M*]@2N/OQSW"@`.PG&8``I2(-(`1*2'7T)X`3%2&!(UH/[_\*2'7T)X`3'@@@`(MQ`,`4 +M((PC8;T`M.T-=9`!YJ4!;_RAP/'`.@E/_*'!&G#/=H``V"``A@'@*'4`IA4( +M40`!V<]PH`#('#&@K@P@#"AP/@T@!`?8"'?2".`"L]@6Z(MQ\@TO_0IP`!0` +M,0"E`(9"($"``*8']`#9SW"@`,@<,:`Z#"`$Z7`Y`6_\H<#@>/'`#0S>`"X/ +MS_\$\-((``#1P.!^\<`-"]X`2@_/_P3P[@@``-'`X'[QP*X(;_P(<8[@`=C" +M(`T``-K/=J``M`^\AERF`=K6#"`$2'.O?;RF\0!/_/'`<@A/_#IP*'4:8E`$_\X'CQP,(/#_P(=QIQ +M'PIT``#>2'7T)X`3&@@@`/0@@2-AO?,-=9`!Y@$`3_S@>/'`D@\/_!IPSW:` +M`-@@`(8!X"AU`*87"%$``=G/<*``R!PQH`H+(`PH<)X+(`0'V#IP,@^@`I/8 +M&.BP?4`HCR&!OQ"]I7_/<*``["?FH`"&0B!`@`#9`*8%],]PH`#('#&@D@H@ +M!"IPB0/'`T@XO_(HB!`[/<(``D`8`@,]V@`!8D":`0"8`%`8+ +MH`D$X0&&SW6``#PB(H;('1@0SW*``&@ER1U8$"&6)ZH@C@0@@`\`!@``@.`! +MV,!X(:H&J@#>B@P@",EPSW"``-T@N@QO_L"H<@Z``@CH=@Z``H;H;@VO_H.X`L"V".%2($TD5,B``"&#F`(`=L`V9ZYSW"``#PI(*"9 +M!@_\X'CQP.'%"P@R#`AU'0V2'@HAP`_K%<]V@`"(P;9^P8X#\`#>QW"``(C!MG@$B`@C`P`((X,#`"-``4D@ +MPP,6;75XSW.```C#`V//<(``B,*V>,]U@``\(J2%N(4!@*5X!""`#P````@& +M>P+P8X'HNY@9P```W0GRI!$```#=E[V1N)2XI!D``#L,'@#/<(``/"+$@,"Z +MR(8$)HX?`$```#Z^'N;8>D5[F!G``!T+G@>D$0``A24!%(RXD;BD&0``G!E` +M`Q[P)PO>!Z01`@"%)0$4EKV8O8VZD;JD&8``G!E``R2`$(&>N!"A"O"4O9:] +MG!E``R2`$(&>N)^X$*%%!0_\\<#6#"_\`]C/=H``^"T@AD!Y@.!M\B"&8'D$ +MV-,($``@AF!Y`-AGN!4(%0,S)@!P@``T2$`G`7(4>0!Y`-A"\,]P@```+B"` +M8'D!V(#@`=C`>#CPSW6````N((5@>0'8(PA0`""%8'D!V!L(T``@A6!Y`=@/ +M")``((5@>0'8P0A1@`'8'O#/<(```"X@@&!Y`=B%X`'8P'@4\,]P@```+B"` +M8'D!V('@`=C`>`KPSW"````N((!@>0'8@^`!V,!X+PA0`""&ZW5@>0#8&G#/ +M<(```"X@@&!Y`=BX<#?8"B'`#ZERE-L1`&_]"B0`!$T$#_S@>,]P@`"4EB`0 +M@`#/<8``Q`87"%$``-K/<*``M`]!^SW"``)26(!"` +M`,]Q@`#$!@L(40`"V`2A`_`!V`6AX'[/<(``K)$@$(``SW&``,0&"PA1``38 +M!*$#\`'8!:'@?@W(QW"``*2Q-(@!X2]Y-*@="3(!`Q("-L]P`P"$`*`:``"* +M(`@`!AH8,`OPBB`0``8:&##/<`(!A`"@&@``X'[/F]Y"+G_V`BX9'@HN`5Y17D(W?0D@`,G>$3`_@I@"A`4`#$2%`(Q +M8;U`*`$$!7E'>43!$!0",10D@#-`L-<-=9`!YE,EP@5`IP`4#0$'V0?P$'T4 +M)TP0`+1AN10D0#"[>T^]`)"E>W![Z0FU@'A@!""`#P```/\0N`5Z0*?A`B_\ +MI<#@>/'`-@@``-H(``#:"```T<#@?N!XSW&``+@H0"$``U4AP@41"(4``-D$ +M&%``^PB$@.!^X'CQP(8.8`4`V"H++_T`V,]P@``80?X+#_W/<(``^$#V"P_] +MP@L/_L(+0`<`V(H(H`*`V2X*``K2#P`"4@Q`"D(.0`&"#4`"`-C:"&_^"'%B +M#@`)\@U``M8(8`'_V&X-``&*((4/"'&"#*`$"'+1P.!^\<#2"2_\BB#_#\]U +MH``X+L>%!Z7/<*``5"X+@-.X!B8`<`\`___B"F`+%MER"X`!QZ4-`@_\X'C@ +M?N!X\<#AQ0#=SW"``%@'H*#/<(``.(BLL"H+8`JI<`X*#_WN"N`)J7`""D`# +M9@U/_?(,``&*(`8*"'$"#*`$"'+N#6_\J7#_\P0$/_`#9SW"@`.PG*Z#@ +M?O'`SW"``-Q]?@M@"P/9#@K``-'`X'[@>/'`X<6DP8MP9@M@"P39SW6``-@@ +M`(4!X`"E%PA1``'9SW"@`,@<,:".#.`+*'``A4(@0(``I0?T`-G/<*``R!PQ +MH,()P`!5`2_\I,#QP*'!BW`>"V`+`=FJ"<``H<#1P.!^X'CQP*'!BW#*"F`+ +M!-D`P%$@0(!H"J(%RB"B``#`42"`@*P-@@D`P%$@P(`X#,(%`,!1(`"!(`G" +M!4((H`H!V,]Q@*[@`>QP(*`!R.QQ`*'/`0<2!38*(<`/ZW**(,4`G0,O_2?;$@Q@ +M`4#85@C``&((0`:AP-'`X'[@>/'`:@_/^\]U@`"8(`*%(X4!WA!QP'ZI<)H) +M8`L#V2H(P``$[@*%`_``A:T'[_L#I>!^X'CQP.'%SW6``+`@J7`V"6`+$-D` +M%000(0Q0`$$,T``I#!`!"B'`#^MRC]B-N)C;(0,O_;AS`84,N`0@@`\!``#P +M`:4,\"&%SW"``%@Y(*`CA<]P@``B&2"H`\S7<````$`!V,(@"@`7N,=P``X` +M`(.XG;B?N.QQ`*$!$@$V['`@H-8/H``!V"4'S_O@>/'`X<4`V<]R@`#`(""B +M(:(BHL]PT/X```2B`!8-0*"B`!8#0&&B`!8`0``6`$`E#=X7_[M`V,\@X@?* +M(($/``#0`,\@X0?/<9\`N/\=H0;PSW"?`+C_/:`#S-=P````0`'8PB`*`!>X +MQW``#@``@[B=N)^X['$`H0$2`3;L<""@2@^@``'80@X``I4&S_O@>/'``!8" +M0*'!0,(!%(`P#P@>`,]Q@`#`AP3PSW&``,"70*%@B0':"/``%@!`%2&,``"D +M`>)]>/4(A8`7"QX``!8`00/P`-@5(8P``*0!XOD*5($#S-=P````0`'8PB`* +M`!>XQW``#@``@[B=N)^X['(`H@$2`C;L<$"@\@Z@``*)H<#1P.!^X'CQP.'% +MSW6``-P(J7"2#R`+"-D`A<]QH`"X'@*A`84#H4X.@`#A!<_[10:``/'`I,&+ +M<&X/(`L0V0/,UW````!``=C"(`H`%[C'<``.``"#N)VXG[CL<0"A`1(!-NQP +M(*``P%$@`(`#P`;T`L$Z#.```-H%\*()(`(!P3H.@`"DP-'`X'X)````!0`` +M`/'`X@V``*T`@`G@>/'`X<6TP8MUJ7`Z#R`+%-D`P(;@S"#B@0;T0@J@`JEP +M"'$D\`\(D0#>"J`"J7`(<1SP$0A1`%X,H`*I<`AQ%O"#X,P@(H(']'X)H`*I +M<`AQ#/`1"!$!>@J@`JEP"'$&\#T(40**(40``\S7<````$`!V,(@"@`7N,=P +M``X``(.XG;B?N.QR`*(!$@(V['!`H(H-H``H<-D$[_NTP`HAP`_K`A`7\,=W@`#` +MDPN'@;@+I\]P@`#$!B^``=H$Z42@!-@'\`#9+*!)H"2@!=B6"\`"U0/O^Z'` +M\<#AQ<]P@``\*0"`!""^CP#````(],]P@`"HD0"(C"##CP7RO@XO_0'8SW6` +M`%B0J7"*#2`+4MFR#D`&$@R@`*.%H@O@`*EP"''/<(``2#YJ#<`)_MG/<(`` +MJ)&)`^_[(*C@>/'`SW"``$B63@T@"PW9W@N``!8)0`71P.!^X'CQP-H*[_L" +MV:+!,@T@"XMP`Q22,"$*DB`$%(4P"B'`#^MRSW```(0,BB.%"=T&[_P*)(`$ +M`A2`,,]V@`#$!H0J""DO=R`>`A#/<(``Y)/Y8"R)0"`0`P`4%#$`(-,#$>DB +M#R`!0B2`(0'8$;;_V"$>`A!`)@`84@J@``396/``V!&V(1Z"%,]U@`#0D4`E +M$1+]98MPJ7%B#R`)`MI`)0`27@P@"T(D@2$`)X`?@`#0D0@0!0#/<(``L,<% +M@%,E0040<_\ +MBB/&!>!X`!8`0($"@`#QP.'%SW6``+"SJ7#>"R`+`]D!A<]QH`"`)0RA`H4- +MH0"-42``@`#8CK@$\@^A`_`0H4X*@`#A`<_[X'C@?N!XX'[@>.!^X'C@?N!X +MX'[@>/'`2@GO^P39H\$`WD+&.@P@"XMP`\S7<````$`!V,(@"@`7N``@@0\` +M#@``!A0`,1MX$^`$((`/``#\_R5XG;B?N.QQ`*$!$@$V['`@H`#!['`@H`04 +M`3'L<""P!A0!,>QP(+`&%`0Q&PP>``$2!38*(<`/ZW+/<```3R;M!._\:=MF +M#"`#`=@"P0#%)7A"P,]PH``L($`0$`#`O0'E`_`!Y@84`#&!#@,0!!0`,8+' +M+0V1$!MX$'CI<38-(`.I`'@$'@>#2`#J7+L<0"I"/#I +M<1(-(`.I>"2`+!]DN"(``T<#@?N!X\<"EP8MPB@D@"P79 +M`,`M"!X`SW"``#PB`X`8B!T(40``V)JXSW&@`,@?#Z$!P*09``##V!JX#J'J +M#T``I<#1P.!^X'C)`R`%`-C@>/'`SW"``&`Y_@@@"RC9R@]``-'`X'[@>/'` +MX<4`%@!`SW6``"P<`*4]")$``-G/<)\`N/\]H!S9%?#/<*``R#LV@$0A`@C:`AB'_"$5YSW*@`*@@38+DXH_W[>ER#T``((5="54!,R9!<(`` +M8$A`)X!R-'@`>#@0!`!8$`4`"B'`#^MRSW```)DA@0+O_"_;]@C@`E38*0A> +M`,]Q@`"4.0"!@;A2#F`+`*$*\(8,[_T!V$(*P`($\'X*P`.I!H_[\<`.#(`' +M"@]``-'`X'[@>/'`R@D@"`#8SW"``#PBR!`!!L"Y@>$!V`0HAP`_K_\N',6#H_\]@Y@"0#8 +M/@Z``E(.0`#E!8_[X'CQP%X*(`@`V-((S_S/<8``;*,"B:X)8`L@B='`X'[@ +M>/'`HL&+<%(/X`H(V0#`SW&``-0Y`*$(Z`84`#$#L004`#$"L0(.0`"BP-'` +MX'[@>/'`H<&!V&#``\P"'`0P`,#^""_\`MFAP-'`X'[QP*'!@-A@P`/,`AP$ +M,,]PH`#4`QR0>@D``0#`U@@O_`+9>@R@"`+8H<#1P.!^X'CQP`HAP`_K*)(,/T0#O_$HE``#@>/'`X<4@V\]QH`#('&FA`!8`0,]RH``0 +M%`RB`!8%0`'=3"4`@,HAP0_*(L$'RB"!#P``+"7*(X$/```)`8@`X?S*)$$# +M&!I``6@90`$#V`^BN:%JH38-0`#)!(_[\<#AQ:W!BW6I<)(.X`H-V0#`'7A3 +M(`$`1"D^#:EP`"&!?X``"+TR"2`)#=H"#4``E02O^ZW`X'@9`J`+`-C@>/'` +M#@RO^PS9K,%2#N`*BW``%``QK^C/=8``^"T@A<]V@`#$,&!Y`-A`)(\P(P@0 +M`R"%8'D`V!L($`0@A6!Y`-@/"%`$((5@>0#8#PB1!.EPR7$8V@3PZ7#)<2[: +M!@@`"0'88!X"$!>&@.`\""'\RB`A```4`#$I"%$`0"2`,,]U@`#$,$`E@1O: +M#^`(+MH!V#>%81T"$('A#`@!_%(,0`#5`Z_[K,#@>/'`6@NO^Q?9M\&J#>`* +MBW`CP$HB0"!3(-``AB#^`T(H$0$E"#(D#!P"-`HAP`_KO_`HD``2*($\%"G&`$J'(!P`+!"G)Z +M"._[9FY_"!``Z7"&"F`+"G$-%(`PA2#!``T<`C"*(/\/4\``AJFX`*82P(8@ +M^P\HN`^N2B0`=`#8J"```__:NV!`*($@-GD2XSMC0*L!X`IPA@E@"XMQSW"` +M`#PB\"#!`\`1``8/(``$P!D8``^.#PA1`(#GS""BH^`)@@L!WP+P`M_F">`! +M"G`'\(#@RB>!%,HG(A*!Y[CT((;/<(``/"(#@!B(*'6&)?L?(0A0`#H*``(@ +MAAGHSW"``(0E"(@G"-$!02E``Q\('@`3P!+"%P@>`H8B^P]!*@0"3XX+"@`! +MJ+A3P!/`$L(&>41X)7@`IH8@^P\+[8#@RB`!!,HA(0!4#>$"RB+A`PX>0A0` +MV,]Q@`"(PA8A`01`A@"A`:$+"E\%`-B+N`&A#PJ>!0&!12``!@&AA@KO_(MP +M#12`,#\(7@%8%``Q!;9:%``Q!K8%EA?HG@D``@[H!I83"%X`W@RO_`IP_@B` +M"P78$JX`V`6V!_`*<`#9W@S@`@_:#12`,#4(7@!0%``Q`K84Z`#=$-@Z<`*6 +M$2!`@\H@`@3*(4(#M`SB`LHB0@-"(4`@YPAU@`'E#12`,`\('@$*<$8+X`!5 +M%($P#12`,#L(W@`UP584`C$*<(H/[_P2PXP@`H"X<`WT"B'`#^MR=-B-N(HC +MD@_]!*_\2B1``%$EP('*)R(1$@I@"PIP`\S7<````$`!V,(@"@`7N,=P``X` +M`(.XG;B?N.QQ`*$!$@$V['`@H,8)8`#I<.T`K_NWP/'`@@BO^XH@4PFDP0#= +MJ7$N"R`$J7+/=H``",<`CDHD0""AK@(>`A4!X`"NHZZAIJ*FI*:EIKBNN:X! +MP+JN`L$'I@/`**8)IH'`F@K@"@'9`<`'IGIUB?""P(H*X`H"V0&.`\$!W^.N +M`>`!K@+`*:8(IEX+[_N+<@0@``4O)`>@`MDCK@*N`,$AIFWR$FD6>,]R@`!( +MOP!B2B$`(`\A42`MN%,@$`"*(%0%E@H@!`IRSW&``%@'0($O(DHD^*X0'D`4 +M!"*`H!0>`!0`H0/9(ZY"I@.F!O0&ZCH(X`,@V/FN!=@#KB#`X@G@`!#9`,`R +M:#9Y`"&"#X``2+^*(0@`HK(@H@;9(ZX`V1(+X`(/V@#"@-D2:A9XQW"``$B_ +M**@IJ`?8`Z[/<(``/"+P(``$SW.``(C"5GO`$`$&!"&!!,`86```V2"CSW"` +M`*B^(:-4>-H.8`N@L`?HR@Y`"PC8`Z[ZKD`C4R`AP')P\`;-_PG8`ZX#S-=P +M````0`'8PB`*`!>XQW``#@``@[B=N)^X['$`H0$2`3;L<""@4@A@`(IP"M@# +MKC$';_NDP.!X\<"*(%4+`-F*"2`$*'+.#\`(N@\``-'`X'[@>/'`X<4`%@U` +M`\P!VM=P````0`'(PB**`!>ZQW(`#@``H@@@"5,E`1!1)4"0SW&``"0Z`=C* +M("$`#0=O^P"AX'CQP*'!BW#6".`*`=D`%`4P&0T1``HAP`_K0;8+PB$`\]P@``\(O`@P`3# +M$``&`=D$(+Z/``8```0D@"\````(PB%!`"NX"PD%``#8`O`!V`]X!/`!W^EP +M!"2!+P$``,`NN<]R@``T6"EB,'4" +M$(`@SW&``'A-"&$Y"%``"B'`#^MR>=B-N(HC&0`Y\0HAP`_/<(``/"+P(,`$ +MZW**(U@/PQ`$!GC8C;A=`*_\"B4`!0,0@"`(81<(D``*(<`/ZW)ZV(VXBB.9 +M`AGQ8@T@"TIPSW"``"B^%B"`!""0SW(``!@5"2&!`.8,(``@L#4$;_NBP.!X +M\<``%H%`SW"``.!!(*@`%H1``!:!0,]P@`#I02"H`!:`0%`DOH'*(<(/RB+" +M!\H@@@\``-H4RB."#P``@0?4!V+\RB4B`,]P@`"\!@"0!NBR#L`*T@W`"GX, +M``#1P.!^]0&@"@#8X'CQP)(+;_L`V4HD`'*H($`"`!8"0!4B0#`.&)@``>$` +M%@U``!8.0'H.@`K/<*``%`2LH,]PH`#4"]R@-@P``,$#3_O@>/'`1@MO^PC9 +MHL$!$@XVSW6@`#@N'!40$$(-H`J+<``4!#``WP0DOH_P_P``RB'"#\HBP@?* +M(((/``"F*,HC@@\``.$&(`=B_,HEP@!1)$""RB'"#\HBP@?*(((/``"G*,HC +M@@\``.0&_`9B_,HEP@#GI7X*8`L_V`#`!!0!,0>E`@R@"H*Y'!T`%*(+(``! +M&I@S&0-O^Z+`\<``V/X*(``$$H$P!!*%,`HAP`_K/'`X<6AP1_=BW#.#*`*!-EAO?D-59!6"P``Z0)O^Z'`\<"IP8MP7@V@ +M"A+9/@L``*G`T<#@?N!X`!8`0``6`$``%H!``!:`0``6`$$`%H!``!:`0``6 +M@$``%H!``!8`00`6`$$`%@!``0,``/'`X<6AP0O=BW!B#*`*!-EAO?D-59#J +M"@``?0)O^Z'`\<"MP8MP1@R@"@W9T@H``*W`T<#@?N!X10"@"@'8X'C@?N!X +M\<"AP0#90,$`%@)``!8`0#4*4``#S-=P````0`'8PB`*`!>XQW``#@``12`` +M`YVXG[CL<@"B`1("-NQP0*#L<""@'_"J"B`%BW`#S`'9UW````!``=C"(`H` +M%[C'<``.``"$N)VXG[CL<@"B`1("-NQP0*#L<""@`,+L<$"@@@H@`"APH<#1 +MP.!^X'CQP$X);_L"V<]W@`#X03H,H`KI<$"'SW:@`.PGSW6````NEPH>`"N& +M1"*``(8B_PXBNJ&Y%+JTN04@@P!E>2NF!""`#Q```@`$(H(/$``"`,]Q@`#D +M!45X"Z$@A03>8'G)0'8)0A1``"'SW&@`,@< +M$0A>``'8'J'R"4`%!O``V!ZAY@@`!2"%8'D!V&D(40$`AV$(W@#/<*``1!W% +MH,.@Q*`H\,]PH`#('`'9/J`+AH&X"Z:V"4`%((5@>0'8)0A1`<]P@``\(@.` +M"(`9"!X``-F4N<]P@`#D!2N@"X:4N`GPSW"``.0%`-DKH`N&M+@+IBH)``"M +M`$_[\<#/<(``'!@R"Z`*`MD6"0``T<#@?N!X\<`N"&_[`-H(=2AVSW"@`-0+ +M.(!"(0$(@.'*(8P`0"8`$A!QE`T%"P/,UW````!``=C"(`H`%[@`(($/``X` +M``=N!""`#P``_/\E>)VXG[CL<0"A`1(!-NQP(*`BO@;P['$`H03E8;[Y#K60 +M`(7B"```*0!/^^!X\<#AQ<]RH`#4"P/=L:(`VW"B`Q("-]=R````0`':PB** +M`!>ZQW(`#@``12("!IVZG[KL(+&IPP +M['(`H@$2`C;L<$"@['`@H,]PH`"P'P'9.:#/<8``X"`(@4"`['!`H`R!`(!> +M"```SW&@`,@[#H&(N`ZAH0.!^X'C@?N!XX'[@ +M>.!^X'C@?N!XX'[@>,]SH`"H(#&#SW*``+@H`X(X8`.B`=@2H^!^X'CQP%8. +M+_NX<<]P@`"PE6@0!`!*(``@3"2`@,HBQ@?*((8/``"1#,HCA@\``+<'2`)F +M_,HAQ@_/<(``Q`8'@(0L"`D`(8%_@`#0D19YQX%_#1$`SW"``$`I(@FO_(HA +M#P_/<(``U"@6":_\(-G/<*4`"`R@@%,E39`2\BD-4!`K#9`0"B'`#^MRSW`` +M`)(,BB.?!YAUX0%O_`HE``3_V`?P_]@(N`/P_]@0N,]Q@`#D!0RAK:'.H0#9 +MD;G/<*``T!LQH(8/H`D!V!WPSW.``.0%#H.;Z,]Q@`!L3L]R@`!`*<]U@`"X +M*(HDPW\*<*@@P`(/814EPQ/G@_`B#@`!X/YFQZ.Y!0_[.!,$``HAP`_K`EE'645"5$`?@N@":EPE@MO_0&-`-@`K0&%$04O^P"F\<"&#`_[ +M#1(!-L]WH`"\+<]P@``\(BZG!(``W480$0%6(%($5B"3!`T2$#=6(!0%1B#` +M(`,2`C8-&APPI!(``(2XI!H```&2HL&&&D0#".C/<(``B++T($``">@!@@\( +MGP-0(``@+R`((%,@?J`^`P$`SW:``"0Z:18`%@'@!!(#-FD>&!"D&T`#`9*5 +M"!``SW"``(BQ-'B`$`$'A0D1`-`0`0%3(<&`%/1R$@$!X)(B?[@2@0`B?_!_ +MX!C$`Z02`0"&(?./!O)HO_!_X!C$`W`2#P'@$``!(9+B>/%PPB<.$,(AS@-T +M$@`!.&"X$H$`=!M$`Z"S.&`0>)`;!`"^&P0`$(H0JP&"`:,(B@BK$HH`VA*K +MEKHR\-(/H`&*(`4!#X?Y"-Z%3X=3(L`"30J>!1,(E0.G%@`6MKH!X*<>&!`< +M\&2X!!(!-A!XD!D$``0B@`\```#P++AT&40#H+$0J:&Q`\B^&40#88"HJ88C +M_PV$NV&A$H@2J?:Z,@(!``#8EK@$$@$VI!D``"D*7@4>#>_^`-@$$@$VI!$` +M``0@@@\"````+;H%(@($+R"((#_P`8&G"!X!-,I0B4D@Q`#R:L]P@`!(O_9_ +MX&!RB1$(G@7/<(``B,%6>`&(`_``V``DCP^``(C!5G_DCP@CPP,((P``22## +M`Q9J=7C/O0C@@!1\`HAP`_KJ8$8``SW*``#PB0X*&(/\#1+@R)``@B;A`P"##5()D>H8C_P.& +M(O\.1+MZ8D]ZSW.``-!-]"."`!WP%0L>`@GJF!&"`$`B`"E(8`SPA.H`VDAP +M$?"8$8``P[@<>#(C`"!`P"#"SW.``("7P[I<>O0C@@"(&0``F!$``(09A`"0 +M$0$!K@@@``#:!!(!-@,2#3:$$0(!@AD$`,]SH`#('UA@$'BP&00`^!,"`+`5 +M#Q%"?\]R@``\(D2"`"'1(U02!`$`)$\$'V>@$P,`\'\Y"\0#4(*8%0,0"R+` +M@!;T,(E0C3!RT2,B@A;RAB/_"2.[`>,A"Y0``KK/<8``2+]6>D%A$0E>!+@6 +M`!8!X+@>&!`-\(!P$'B&'000:A8`%@T:'#0!X&H>&!`Q`2_[HL"AP?'`W@@/ +M^PAU1L#HO2APS@`A`$AV`[A`()`%1"4"%B.Z!"6/'P8````!XD$O0!0$)8$? +MP````%A@-KG/ +M$\]R@`!844"2!2H^```A@'\``/\_+KA?`"``&6%7`"``%7E1)4"25@`A`";% +MM^4B``L`,VA3)0(0SW"``!A.\""```4I/@`*(,`.`>`&\(KEP"CA`,`HH@#/ +M<8``/"(C@<#:-(&D>88A_PXBN3IZVGH98C!X"-P3``_[,VA3)<`0''C/@6`R;H%*+X`)W'/<(``&$$#@`"`X'\X8,]Q@`#@("2! +M((%!*(,%U;A!*8(%U;D9"24`6V//F*@HDF!0:!E*H5)%3J"B!P+DMJ!KP.0U1`&)B2*%!@$FA +M2(A`+8`*4!V<]P@`"4(""P\@J@!RAP`H7/#"!,$``\@0``"I<]P@``X(#5X0*`8$P4!#!,&`,]P@`!D-@#9-*C/<``` +M0*A`P`6#$!,'`$'`&HL[BT"#6@M@"&/#/<(``EB`!V2"HSW"``%@@)X#/ +M<(``W+`OH)X++_T"V$KP!-@`I0#8SW>``)0@;@J@!P"WSW:``%@@`H5(AF>& +M#R"!`,]P@``X(%5X8*`BI>S8R@]@`T"7"!8$$,]P``!`J!@6!1$,%@800,`% +MAA`6!Q!!P!J..XY`AMH*8`AAAB06@!!(A@#942``@02%#R&!``KR`=O/_ZHL`(%000"B'`#^MRSW`` +M`$(?Q0`O_(HC1`?@>/'`X<4!W<]P@`#8AZ"@`-C/<8``E""R":`'`+%>#"`` +MJ7#U!,_ZX'CQP.'%`-C/=8``V(=:""```*6&"2_]`M@BA<]R@`"4('?8_@Y@ +M`T"2Q03/^O'`2@S/^@#>SW>``)0@P+=B":`'R7#/=8``V(?"I<.EQ*6*(,D` +MR7'*#F`#0)!X\<#/<8``V(<`$04`&PU4`0HAP`_K-'`X'[QP-H+S_K/=8``V(<$%040 +MHL%)#5``(PV0`-$-4`$(%000"B'`#^MRSW```$0?R0?O^XHC1P;/<(``EB`! +MV2"HSW"``%@@)X#/<(``W+`OH,X)+_T"V$SP!-@`I0#9SW"``)0@(+">"*`' +M*'#/=H``6"`"A4B&9X8/(($`SW"``#@@57@BI6"@^@U@`XH@A@L(%@00&!8% +M$<]P``!`J`P6!A!`P`6&$!8'$$'`&HX[CD"&"@E@"&&&)!:`$`'?2(8`V5$@ +M`($$A0\A@0`(\L]R@`!D-O2J!7DDI03P)G@$I7X+8`,`V`3P[@X/_%4#[_JB +MP.!X\<#F"L_ZSW:``-B'!!8%$$(E00"$X>8`#0`S)D%P@``D2$`G@'(T>`!X +M`H;/<8``6"!(@2>!#R"```*FSW"``#@@57@@H%GPSW"``)8@@-D@J,]P@`!8 +M(">`SW"``-RP+Z#*""_]`MA'\`J6C"`"@!'T`-C/=8``E""6#V`'`+4BAHH@ +M!00*#6`#0)4!V`"F,_`#V`"F,?`#AHP@PX\!WQ+T`-C/=8``E"!F#V`'`+4B +MAHH@10K@IM8,8`-`E28.#_P;\`#9#R$!``*&!B!`@!+T`-C/=8``E"`V#V`' +M`+4BAHH@A0RJ#&`#0)7V#2_\X*8#\`*F60+/^@@6!!`*(<`/ZW+/<```0Q_Y +M!>_[BB-&`.!X\<#AQ<]U@`#8AP05!1!")4$`DPF5`3,F07"``"Q(0">`!#R"```*ESW"``#@@57@@H!WP`X6,(,./`=H)\@#9#R$!``*%!B!`@`WT +MSW"``)0@0+!V#F`'`=@#V$(-+_P`I07P`J4#\`'8`*6M`<_Z"!4$$`HAP`_K +MXP+@3>,:X`>`* +MM0C8.G``W@*%#R;.$PL@@(,M\@2%"R"`@QGRQG@$I<]P@`"4E@B`#WDC"5`` +MSW*``&0V,(J&(,,/@+@!X2]Y,*K/<8``E)8(H<]P@``X(!4@T`,`$``@@.#B +M(`(``H4`V0`80"#&>`*E0B%`(`'GE0AU@.]_2I7/<(``E"`@D('BS"$A@`?T +M`-K/<(``9#94J`&%$0A0`8'A`]C*("(!'@S/_X$`S_K/A)L#(H209`@`'P&&A!:%3V*EQ4@I@`\ER)L`3"!X`5]BI<4(*8`/)<@;8 +M!?"!Y0+8RB!B`'H+S__U!X_ZX'CQP'H/C_H:<,]V@`#8(`"&`>#/=Z``R!\` +MIA$(40`!V%$?&)#N"H`*I!<`$,]P@`#\+R:`SW6``%"C8'D`V`&%*>@DV!C9 +M[@J@"C/:'PA0``05!1`*(<`/ZW+/<```=!F:VSD#[_L*)``$)-@!V<8*H`HS +MVA\(4``$%040"B'`#^MRSW```*LHG]L1`^_["B0`!`"&0B!`@`"F!?0`V%$? +M&)`]!X_ZX'CQP-H.C_K/<(``_"\$@"7HSW6``/@X,H7DX.!^X'C@?N!XSW"``!0X0(@1"AX`SW&@`*PO&8&*N!FA$0I> +M`,]QH`"L+QF!CK@9H>!^X'C/<:``R#L=@0?H@M@4H<]P`(`1%`ZAX'[QP.'% +MM,&+=:EPSW&``"!*X@ZO^E#:?@M@`:EPB0:O^K3`X'C/<(``<+-LB,]Q@``X +MB(PC`H`*D4$H`@,,\AD(WP("NW9[QW.``$B_`I,/((```K,`V.!_#+'@>/'` +MT@VO^E1HAB+X`T\B0P)3(<(`!2+$`,]R@`"HOA1ZC^&*(P\,RB`I``GV`)(` +MW0\E31"*(\\/IG@`L@#92B0`=,]V@`!(B\]R@`#`B\]U@`#$BZ@@P`04(D`` +MY)!D?QD/`1$`W^2P%B9`$."@X:!`)0`9-7C@H`'AP06/^N!X`-J>N@#9SW"@ +M`/Q$0:#@>"&@SW"@`+0//*`+R`0@@`_^__\#"QH8,`O(A[@+&A@PX'[@>/'` +M(@V/^DAV@.`!W43VBB7_'Q-X"0D3`+-],WD4(0``N@ZO^CMYK'@`'D`>806O +M^@'8X'BAP?'`X<5"P)AQ2'6`X`#:1/8!VA-X0L`6#Z`'@L`"P`+J$WB"#J_Z +MB'$`I0C<-P6/^N!X\<#AQ0AR`=V`XHHE_Q\)"1,`,WFS?10A@``N#J_Z.WFL>.$$ +MK_HO<.!X\<#AQ<]U@``XB,]P@``\(B.`0(4`@4,*`0`"D4*5.PH!``*%7@^O +M^R.%C"`"@!7RSW*``%0'(8(`VP\C`P`"N&9Y%G@AH@`@@0^``$B_`(&JN(BX +M`*$`V($$K_H,M>!XSW"?`+C_SW&@_O`$-J#/<*``R!\\@$`0``;/<)\`N/]8 +M&``(2B3`<<]Q```(@:@@``(IV!*X\"!```'AX'[@>/'`X<7/<```___/=8`` +M5(@#I<]P@`#L/X8-@`C/<(``"$!^#8`(SW"``+!`<@V`",]P@`#,0&H-@`@` +MV2"E!=@!I2*EG@CO_`;8F@CO_`G8Z0./^@?9SW*@`-0'&AI8@`WH&1(!A@D@ +M0P`/$@&&`B#`@'EA#QI8@/7UX'[QP$8+C_H#$@,V"'<-$@XVSW&``(BQ$(O/ +M0*X%G@%8C&)+;U88,"]#.DA@Q4)7@//<8``="&T>:"1$.6@L260 +M(PE2`&&Y);`0BS)H-GD[8F63.F*'ZR:242%`@%`.@OO>",`),@A@!0W(`\@! +MV:`80`#/<0\`___B""``Z7`I`X_Z\<"V"H_Z`]_/=J``U`<3'MB3#Q80E@`6 +M`T``%@%`HL'/<+#^``!`P=.[!7O/&)`@'MB3&18` +MEB<(%`(?%@"60<`AP)S@RB'"#\HBP@!:ARB`B`,`.P?\:<`W(SW&@`&0NSW*@ +M`#@N\"$!`-.Y!X(D>`0@D0.G\)X.S__/=H``0,P:<,EPN@]@`XMQ]@B@"LEP +MF?`#W\]PH``4!/"@Y*``%@1`!QH8,0`6!4`!&E@Q!,H]"!$'BW!F"^`)#MDD +MP%,@P0"&(/X#1+C$'`(P9,%$)HT4,PY>$([8D+B@'``PU0X>$8;8D+B@'``P +M9/#KH`V`\@P``&(0&`VO4!V`+P`-B`X"OSY0=O^H`D`S+QP((/3_H:<- +M=9`!YK8)H`$TV$\@`065N>H)H`$TV*8)H`$LV`AUG@F@`338N'`S"%X%"B'` +M#^MRSW```.L4X]L%`Z_[2B0```HAP`_K]$+3X7`"&`?X``N#YZ +M"(`("N]$+3X7`"&`?X``8#]F"(`(0B!`(-<(=8`!Y<]P@`!(ELETG;`PO)ZP +MSW"``,P'%@P@!<"@P09/^N!^X'CQP,]P@`!H!@`0!``!$@4V"B'`#^MRSW`` +M`-L.60*O^X_;X'C@?N!X\<`F#D_ZSW"@`%0N*X`'W=.Y+RA!`$X@CP?/<*`` +MP"^E$!*&%!`1AL]VH``4!*JFV@L@!X#8\]@%N(#9S@B@`9^Y#1(0-O78!;C" +M"*`!J7&JI@T:6#,$\`/8!::IAAOM?.U!+8"0"O(O)`EPX'BH((`!`!8`0.!X +M4R5-D`GR+R1)<^!XJ"!``0`6@$#@>*F&Z/'SV#8(H`$%N,D(WX?UV`6X:@B@ +M`0IQ*!X`%)3G#1H8-,HAQ0.%]^EQ@"'"`<]PH``8+/`@0@"4Y\HAQ0.%]^EQ +M@"'"!,]PH`!H+#5X!+]`H,=W@`!,NQ6'-H<%>1>'N(`4E#9#*(<(/RB+" +M!\H@@@\``,(ARB."#P``C0?*)$(#.`&B^\HE(@"`V<]PH`#0&S"@SW"@`,`O +MI1B8A!086(1-!4_Z\<#F#$_ZI!$``"AU42``@`K8RB`A!)@5`1`$(;Z/`0`` +MP'8=!!`P]"T)'@)$(0`&([A!:`0A@`\&````,;A88`0A@@\&```!UW("```! +MRB"A``/P`=@C"%``%0B0`(/@`-C*(.$!P"BA`POPSW"``'BQ`H`%\,]P@`!X +ML0&`!7F8'4`0GA4`$90=0!"2'000@A4`$9`5$1&R'000`-B`'000?AT$$`/( +MSW:@`-0'09`0%9(0".H-R,]Q@`"(LO0A```3Z!D6`)8?"!4.#!_`B``&J$/%A26">H-R,]Q@`"(LO0A```#Z`'8!?`#V!,> +M&)``V`<2#S8!$A`V`!8$0'IP!QH8,0`6!4`!&E@Q!,J`: +MH1N!`>`;H0,2`38!@1T(G@-4$0`!4R#`@`CTSW&``)`\&8$!X!FA`A4%$2D- +M$``!A>ZXRB'"#\HBP@?*(*(+SR`B`\HC@@\``+4'0`=B^\HD8@``E;!PRB', +M#\HBS`?*(.P+SR`L`\HCC`\``+@''`=L^\HD;``0C5,@P0"&(/X#1+C$'0(0 +MI!4`$#"M1PB?!0<2`C8"(L$#`-@/"5```B>!$(PAPX\"]`'8D^@-S,]Q@``< +M.T8@@`(-&APP&8$!X!FA#QX8E0<:V#,!&A@T@_`'&M@S`1H8-`#8=!T$$'8) +M8`"I<,]Q@``(6`MA=!4"$<]Q@``06/`A``!Z8E!ZI!4!$'0=A!`E>*0=`!`$ +MR`&0$^@="U$@`96X%8\06&`@E?A@$'B^'00066$_9P[POA4`$0GP()6X%8`0 +M66$X8!!XOAT$$`AWD!T$$`\6`):T'000\@N@!*EP$(TR=\P@@803\@HAP`_K +M8`1``>2Z-`1``%J%8\0`>##N/A@#WAJ'0(0Q@Z@ +M`*EP:AW"$P7PN@Z@`*EP#QX8E=T!3_K@>/'`B@E/^AIP`-^D&<`#SW"``#PB +M!(#0B?"@!\@$((`/`,```"AU,PB!#P#````-R,]Q@`"(L11Y$8F/Z,]P@``H +MOM9X(H@(C0\(0P`*<%H++_ZI<=SP42``H(;R!!4$$($,'@$-R,]R@`"(L11Z +M$1*%``]X22#"`')NSW"``$B_=GM@8#*-$PB>!<]P@`"(P=9X`8@"\`#8QW*` +M`(C!UGI$B@@A@0`((0$``"%``4D@P0,6;C5XSW&```C#`&'/`/P`X7/<8``/"*8'0`0)($H@00A@0\` +M0```/KE3)`(`'N$X>D5XF!T`$!<(G@>D%0`0C+BD'0`04-B<'0`0>/`K"-X' +MI!4`$(VXI!T`$,]P0`%0`)P=`!#/<(``/"(D@!"!GK@0H63P!=@4N)P=`!#/ +M<(``/"*D'<`3)(`0@9ZXG[@0H5;PCPA>)P&%`1*--!*!,$DAP0!R;L]R +M@`!(OW9[8F(1"IX%SW*``(C!UGI!B@/P`-K'<8``B,'6>22)""!```@@@`!) +M(,$#%FXU>,]R@``\(D2"SW&```C#`6'/<(``B,+6>%B"`8!%>`0@@`\````( +M!GD"\".%F!U`$`W(SW*``,"Q%7H@HIP=P!,%\`78%+B<'0`0$0@>)0#8D;BD +M'0`0!/"D'<`3=!W$$VH.(`"I<,]Q@``(6'05`A$)85EA,'ET'400SW&``!!8 +M\"$``*05`1`E>)@5`1"D'0`0&0E>`@K9=AU$$'@=1!"`N*0=`!`5\!#9SW*` +M`#PB=AU$$$."2((3"MX`"MEX'400@[BD'0`0`_!X'400A@YO_*EPI!4`$$0@ +M?H*,%8$0&/+/?0B40#/+X=!!`/ +M]`HAP`_K;Q/0I0`(+BS"+B@,HAP@_*(L(' +MRB!B"\\@(@/*(X(/``"U!LHD(@`$`F+[RB4"`<]P@`"(P=9X`X@'\,]P@`"( +MP=9X`HB,%0$0#K@E>(P=`!#/<(``D`9`@`:"H!``!H?HSW"``.A!`(BW"!`` +M#1(#-J\+D`$`E<]Q@`"0/)\($@S/<(``B+%T>!&(BP@1`(,,$0!["!X@GA4` +M$<]S@`!\/(JXGAT$$!:3`>`0>!:S`%0$1I[F>'400SW&` +M`-Q!`*$$((`/___3]I@=`!`-V)@=`A`)\!#8!O`(V`3P`M@"\`'8!Z&8%0`0 +MOA4!$08,+_\`VJ05`1`$(;Z/````,((=!!!0\HP5`A"<%0`1E!V`$)(=!!"` +M'804`Q(#-AD)'@,4V)`=!!`J<'X=!!!X$PX!"O`.V)`=!!!^'<03>!,.`4IP +MPG@0>+(=!!#/<(``-+$`@(8@?X\+])@5#A`3#E\289.%ZY&YDKFD'4`0$+@E +M>*0=`!`$(H(/````$,]Q@``\(F2!4B("`Q"#!7I0HT2!$(($(($/````$#UY +M)7@0HA3PF!4!$(`=Q!.4'4`0GA4!$7X=Q!.2'400OA4!$;(=!!"0'400@!4` +M$7X5`A&"%0$1&F*$%0`166$X8!!XL!T$$*05`!#/<9\`N/\6H9P5`!`6H34$ +M#_K@>/'`X@L/^EH/3_S/<(``<+,,B,]Q@`!(OP*X%G@`82VX4R``@`7TSW6` +M`"0Z#?#/<8``/"(@@<01`0;/=8``)#I1(4"!!/0!V=P=0!#/<8``/"+P(0`` +MSW*``.A`(((8B$4)-0%!'1@0,R9!<(``7$A`)P!R-'@`>!8,8`@#V*8+8`A` +MV`#8X!T`$`[PSW.@`*@@,8,"@@#>PJ(X8.`=`!`!V!*CJ0,/^O'`F'"X<11X +M.&#/<8``8$T(88P@PX_*(L$'RB"!#P``K!/*(X$/``"+`2@'(?O*(<$/T<#@ +M?N!X\<#AQ0?9#1I8,,]PH`#4!QH86(`.$`*&SW&``/06*($'&I@P#^G/=9\` +MN/\]I<]S@`#`("2#`>&SN;6YN+DDHS:ESW&@`$@L7J$?$`"&`1H8,`3*G.#, +M((*/``"1``;R`!8`0``6`$`#S,]QGP"X_QBA?=@R#:`"`1(!-OD"+_H$RN!X +M\<"X<0*YSW*``$B_-GDP(D0`421`@LHBP@?*(((/``#+(LHC@@\``),#;`8B +M^\HAP@]`+8$!SW*```C#(6)1(4""BB((!"@_ZSW*``.`@1(+/=8``5(ABA4""-KLVNE!SUB*-#P``@`#` +MA3UB?F8=#843"B'`#^MRBB"-`HHC$`28=O4%+_NX=1YF_PV%DUA@/0(O^@X@ +M@`/@>.!_`-@4>#A@SW&``*!8X'\(8>!XX'\!V,]Q@``$.N!_\"$``/'`F'`* +M(<`/ZW(*)<`'SW```)\9I04O^SO;X'C/<8``X#G@?_`A``#QP)AP"B'`#^MR +M"B7`!\W8!;B!!2_[1-O/<8``&#K@?_`A``#QP)AP"B'`#^MR"B7`!\]P``"A +M&5D%+_M-V^!XSW"``#P]X'\`F.!XSW&``!`')('@?R"@$8C@?\*XX'C/<8`` +M^$!&@8HA_P\@H`;J(H(@H`'8`_`"V.!^SW&``!A!1H&*(?\/(*`&ZB*"(*`! +MV`/P`MC@?HHA_P\@H,]S@``804:#$NHD@AL)7@#/<8``0$`/"D``SW&``%Q` +M$0I!`$""Y0N!@`+8!?`B@B"@`=C@?O'`E@@/^L\($`#/=H``,)HOCL]P@`"( +MP<]U@``\(C9X(H@#A0#?SW*@`"P@-!`1`3P2$@`.CH#@G``I`,HEJ1",(@&D +MD``E`,HE)1%DEI3CP".&#P``DP#/<*``:"SP(-``Y:)0V$4A00(8VNX,(`H@ +MV_BXRB4B$B[T`]C/<:``]`<%H83:#7!`L$(B`"@-<@"R0(8-<$"@0I8-<$"P +M`X5`@`UP0*`#A4*0#7!`L`:60"@")<.X#+B"N`5Z#7!`H.2A#HX!X`ZN3@J@ +M""IP`=T0\`#=SW:``#":<@V@!@26`-C/<8``'#L.KAZ!`>`>H0$`+_JI<.!X +M\<"F#\_Y&G"$*`@)`"&!?X``T)&'$0T&SW"``,0&`H"@O8<96`,$B`_H`X&- +MZ`HAP`_KECH6!1$*(<`/ +MZW(0N`4E!0#/<```@PR*(X4/`0,O^PHD``1`*`TAW64EE025$+DE>#CHSW"` +M`,18\"`!!$0H/B<`(8!_@``8/R]W(*`CE0*5$+DB#^_^)7@(<0`G@!^```P_ +MZ@@`",]P@`"X6/`@`00`)X`?@`!P/D>5(*`CE0*5$+H0N25X)I76#&_[17GF +M#L_^"'$`)X`?@`!D/JX(``A>EAV6`-D/(0$$$+I%>`8@0(`!W1VV,+@>MAKT +MSW&``.`M`(&@N.8+H`0`H<]P@`#@(`2`EMH>VR"`SW"``(B6HJ`AH`S9,@O@ +M!AB[$-K/<8``S`<`@0`J`@1&>&$&[_D`H>!X\<#^#<_Y`-W/=H``2)8^E@\E +M#1`=EA"Y)7@&('Z#0?3/<8``X"T`@8"X`*'/<(``O`;/<8``:"4`D$>)-PH! +M`,]P@`"^!@"008DK"@$`SW"``,`&`(@FB1L)`0`+R`0@@`_^__\#"QH8,`O( +MA[@+&A@PSW"``.`@!(#/<8``B):6VA[;`(``H0#8`J$H<`S9@@K@!AB[`-C: +M"V``@-D^EAV6$+DE>*5X';8PN+T%[_D>MN!X\<#AQ28,8``H=8#@RB!!`P0+ +M80/*(6$`I07/^3$'S__QP"8-S_FJ"*`'`-[/<*``T!L1@!<(W@/*#&`(`=C/ +M<8``D#P)@0'@":$&R`,2#39%"!X`I!4`$#T(G@3/<8``W#D`@1CHP*$%\&X) +MH`"*(,<)^PF>Q<]PH`#$+`N`4R"!!/ZXS"$B@`;RF!4`$-(+[_X`V@,2`3:@ +M$0``%0@>!(H@"``,&APP<@X@!"AP+_!'"!X%!\C0B0#:,Q&/``0@@`\!``#P +M02@-`\]QH``X+@>!#R)"`P'<1G@'H0W(#@S@"0`L`!#'=X``2+\"OM9^$N?? +M9Z"OBB`0``8:&#`#R*`0@`#$X)P/@0D#V<]PH``4!".@F03/^?'`+@S/^<]U +M@``$BP&%SW.``(C"1"`$@\]P@`!PLPR(TFC6?L=V@`!(OT"&%GLA@Q+R4"*/ +M!>"F1B$!!B&C#0P1`9&_X*8%\+&ZMKI`IB(-@`D'\):Z0*9%(0$&(:,+C:*X +M-03O^0NMX<7AQL]P@`!PLTR(C"("@,]S@``$BQ?RRHO/<(``B,(R:C9YQW&` +M`$B_5GA`@:&`!>Z5ND"AJ[T$\+6Z0*&+O:&@`-@+J\'&X'_!Q:'!\-Z8@0E@!_`````-KC/:ERQKI)(L(%5'G/ +M<=Q@`!( +MOZ"!SW.``(C"SW>```2+Y)<6>T"6.%88GNQ_`H8PG1)!&(@(&0:,%])&^ +MP*$+\+&]MKV@H0\/41"6O:"A12("!D&CC@N`"0#9SW"```2+J0+O^2NHX'[@ +M>.!^X'C@?N!XX'\`V.!^X'CQP*((#_K@>.!XX'C@>&D@@`%O(3\`:2```/?Q +M\<`&"L_Y&G#/=J``T`\`W0?P$!8`EOUA^&`0'AB0(VUO"$0@)18#EB46`I8O +M),<`)18`ED]_#WT(O:5_UPP1@X+GS"?BD\PG(I?*)4(0(?3/=8``U)9)K246 +M`I8*K4NM)18"EFBM3*VB:14/T1//<(``X98&"R_Z#=D-Y1,/$1?/<(``[I;V +M"B_Z#=D-Y1`6`)8"($$C.&`0'AB0S0'O^0'8X'CQP&H)S_FAP0AU*':$Y0#8 +MF/>+<,(*+_H$V0#`&PB`#YH)4&_/<:``U`L/@62]N&`/H0'8!O"I<"8/[__) +M<0]XE0'O^:'`X'C//'`SW.``(@&:'`F#"``!-D$:QX,(``$V='`X'X`V,]Q +M@``\!P&I90=@"0"I\<#AQ7(*K_TPV+1H:@JO_3;8!7T8O9&]SW"``)1(L@M@ +M!Y*]*+CI`._YI7CAQ3)H-GG/!XX<5*)`!X`-BH(``(`-O/=8``P"!`A0\C`P`+(L"`#_)! +MA0LBP(!`VL\BX@?*(H$/``#0`,\BX0<"\`#:SW.``/06%7M`HP'@X'_!Q<]P +M@`"<00:``X`@@,]P@`#H?2F@904O_!'8X'CQP'H/K_D!V<]P@`"(*$()+_\D +MH(H@Q0_/=J``R!\9'AB0`=@!V2AR*',:#>``F'%R#._[`-\^#L_[SW6@`-`/ +M]:7/<*``P"]Z$`&&B;F+N7H86(#/<8```+40&%B`P@C``:8*S_YV"L`&0-G/ +M<)\`N/\RH!8*``F`V<]PH``4!"R@'1U8D+8*``AZ"<`'F@@@".EP!]A('AB0 +MF@[`!:X(@`%F#P``7@]``QX+P`;F"@`#N@\`!Y8(0``B#T`&8@C/^TX/P`#^ +M#H`'\@H/_]X,0`3B#``!>@W`!4H(``7"#8`#$@B/_O8/``3N#P`$U@O`!L]P +M``#^RN()C_OE!H_Y\``#PB`X<(@,"XP@\@""\@`"``W<]VH`"T +M1\]PH`",1+B@`-B3N'<>&)`(V'<>&)``V)ZX4QX8D.!XSW"``-P`$'A3'EB3 +M1QX8D,]P@`"``Q!X2!X8D$\@@"-%(``-3R#&!S381!X8D!S811X8D,]P@`#< +M?0&(1AX8D,]P@`!PO=H/H`0,B$HD@'#/<8``T,>H($`#SW"``'BQ08!T;71[ +M.V,"@$.C`>4$H\]U@`"X00"%!.AD'AB00QZ8D7(-(`@!V`.'"(!`A1T('@!3 +M(D$`$KE$(@`##K@E>(8B_P,*ND5X$O!(<(8@\P\*N`0B@0\````,!KDE>`0B +M@0\````P`KDE>,]Q@``8+LD%K_D"H?'`X<40W8H)H`&I<`?9"[G/!X\<`V#:_Y`-K/<(``Q`9#H/_;SW"``,R9 +M$QC8@$HD@'!(<:@@0`>$*0@)`"&.?X``S)//=X``&$%!I@;=I:;/=0$`O,"D +MID:FYZ8D'H(0`"&-?X``[)-`I0'ASW"``,R9'!C8@,]Q@`!`+0"!'-I`H!C8 +M#@@@``*A)06/^3G9SW"E``@,/J#@?O_9SW"``*B1(*@`V<]P@`!0D>!_-:#@ +M>`#:@.'*)$UPZ""M`?_97&`@K`'BX'[@>/'`X<7/<8``W+#/<(``/%C"""`' +M2-K/<(``O%'/<8``O`>R""`'"-H`W<]Q@`"D,J&AHJ'/<(``O#6IH$8(8`(# +M@<]PH``L(,]Q@``L-E"`$(!%H0:A$@O@`:FAF02/^?'``-G//'`^@NO^2#9`-K/=:``R!PII<]QH`"4 +M$UNASW.``!088(/S:,]V@`#DL@R&]7]3(,0%\&/[8U,@CP"DP8MQ.P_1$!^& +MF[@?IC06@!#BBQL(P0,H<$`C`01$:WX*X`=`)@,<#=HJ\!Z&D;B2N!ZFSW"@ +M`,P7*_`A#U$102H"4D`C``3!NF8(K_V((%)P`1`*$%@P&A!H,"H0>#`Z$#XL]PH`#,%\]QH`"4$URA`=J( +MZA^&E[@?IB#8"J49\`#!`]H8&%B``<$9&%B``L$:&%B``\$;&%B`%!B8@(H6 +M`1$0&%B`!-DGI188F(!=`Z_YI,#QP.X*C_FD$`$`HL';"5\&(-G/$0WLHFXA$&%`\Q +MC"?#GPGT!!0/,?%VS"?JD`'>0O8`WNONQ8!%?L>EL8B&)?P?&+VE>L]UH`#, +M%UJ@%O!%@,]QH``0%$>AI!`!`!4)G@(QB->ZAB'\#QBY17DZH,]UH`#,%PW9 +M`=H#X0T=F)`.'5B0)H`9'5B0)X`:'5B0*(`;'5B0`]D4'5B0CI!`!`)FYI!A``'$"K_FBP/'``@JO^039"'4-$@XV +M!M@-&A@PSW>@`!0$"J?/<(``,%@Z#\`&`(4R#^`&!-D!A2H/X`8XV0@5!!`! +MA0`0!0$)#!``%0T%`0HAP`_K@((.+_T!V`GPBK@:H1J!TP@?@'X.+_T!V`#9F[G/<*`` +MT!LQH'8+0`E""4`)SW"``)0Y`(!"(`"`RB!B`-'`X'[@>/'`X<7/<8``2)9^ +MD5V1$+ME>@'=&PH/`,]Q@`"X/D0H/@?:"J`'`"%`#JEP`O``V&4!C_E&@0GJ +M(X%@@2*"8GDP<`#8`O8!V.!^X'CQP-8(C_D(=<]V@``80=X/[__)<0?HJ7#2 +M#^__0"8!&(/H`-@)\,]Q@`#X0+X/[_^I<'GH`=@-`8_YX'C/<(``Y`4.@(#@ +M`=C@?\!X\<"2"```!NB."```#PA1`,]P@`"T*`"`@^@`V!+PC@@``(_H?@@` +M`(OHSW"``(0E+)#/<(``/"(>D.,)`8`!V-'`X'Z1!Z``$=C@>/'`N'#/<:`` +MK"\8@1D(G@8*(<`/ZW**((P)9]LY!*_Z2B0``!6!&P@?``HAP`_K$H45`):"#H_[BB`$`"0=&)!1)H"0 +M_`Y"">D'3_GQP'8/;_DTV`8*@`#/=X```'0O"!X$H@J@`@#8=@J@`@'8BB80 +M$`#=R@_O_JEP%"=,$V&^`+3U#G60`>4.\`#;BB(0``H,X`-P>!0GS!!AN@"T +M]0IU@`'CA0=/^?'`&@]O^338H<$`W:8)H`!`Q<]W@```7#,('@26"N```=@# +MW@J^`-B,N+A@$'B+<8H+X``!VA0G3!-AO@"TZPYUD`'E7@K``!#P!=L*NP/: +M"KIX99X+X`,0>!0G3!-AN@"T\PIU@`'E&0=O^:'`X'C/<`$`U";/<8``+!IA +M&1@`SW```,1052%"!T`A`P,&Z!VC&X&#N!NASW```*10!N@"HAN!@K@;H<]P +M``"44`;H`*(;@8"X&Z'@?N!X\<#AQ6_8E;C/=:``R!\2'1B0SW`!`$`\%1T8 +MD.8(``B*(`0`#J6Q!D_YX'CAQ0#;SW*``$B+2B0`=,]U@`#`BVAPJ"```D`E +M`1(4>6"Q`>!(<,]QH``$)0^A5B(`!!&A5B(`!1"AX'_!Q>!X\<#R#4_YSW6` +M`#PB!87/=J``Q"=U'AB0#)5V'AB0!X5Y'AB0$)5Z'AB0[@WO_P#?&^AW'MB3 +M>![8DX`>V).!'MB3!X6&'AB0$)6''AB0!X6*'AB0$)6+'AB0!86('AB0#)6) +M'AB0!86$'AB0#)6%'AB0P=A0'AB0X05/^>'%"''#N,]R@`#(B_0B`P#)NW!Q +MRB0B=,H@(@#H("("]"(-`,F]"0E``P'@X'_!Q?'`X<4(=<]QH`#$)QD1`(8! +MVH#@$1$`AL!Z@.(`I=$@X8<`V#/TSW"``&"S#(#/<:``R!]DX!ZA$-@.H0'8 +M%1D8@&8(8`D+V%$A`,;*("(`&/0E"%Y'SW&@`-0+%H$X@23@%0A%`$((8`D# +MV`D+'T`)")Y$&-@#\`#8@.#*(.($SW&@`)`C/H$@I34%3_G@>/'`M@Q/^<]V +M@``\(A4F`1!`@6F"N(I!*\``P+@7N,=P``"`'.2[SR`B!N"[3M_/(*(`RB>" +M'P``3@&&Y<\G81(I"U\!SW6``(0E&!4$$;Z6&PT!$:&&Q!4-%A$-7Q&@AL05 +M#18'#5X1@;A1(P""SR"B!1NB_*)`@<]P.@1*8H@!``>I1GPBB`0`!ZE%?``V(NX +M'J41\`#8C+@>I0WP`-B-N!ZE"?`#V`RX'J4%\`#8CK@>I8(@`0$]!&_Y'J4* +M(<`/ZW*,V(VXOMN+NTHD``#9!V_Z"B4``>!X\<#/<8``%#P7H>!XX'C@>.!X +MX'C@>.!XX'C@>.!XX'C@>.!XX'C@>.!XX'C@>.!XX'C@>.!XX'C@>-'`X'[@ +M>/'`SW&``!0\%Z'@>.!XX'C@>.!XX'C@>.!XX'C@>.!XX'C@>.!XX'C@>.!X +MX'C@>.!XX'C@>.!XX'C1P.!^X'@A@`#:4R%^@`OR`-J9NE$A0(#*(H(/``"# +M`,`J8@;/<:``[$9%H6:02",#`T>0$+L$(X,/#P```,BZ17M(D`RZ!"*"#P`` +M`/!E>D:A0(!!"M$`8H#/!^\<"B"D_Y(8#/=8`` +MP"`"@""E`-X!I<*ESW#0_@``H@JO_P2ESW*?`+C_W:)O(4,`SW"@`.Q&):`B +M#(_^`(4M"-X'SW"@`,@['8`0Z,]P@`#T%A^`"^@=H@2%`>"SN+6XN+@6H@2E +M`_#=HL]P``!559T"3_DBD$@A00%`*0(#(Y!B@,NYC[E%><]RGP"X_WVBSW*@ +M`.Q&)Z(C@#6B)(`VH@6`%Z+/<```557@?O'`[@EO^0#;IL'/<8``P"!@H6&A +M8J'/=J``M$6)!*)(!QSW*``,`@"!(%``L0D```W3EUV'6I-*)P``#R='`PL@P*$)\A4D03,@@4HC`!`/(TL0`_!* +M(P`0*H@+(<"!!2')$@GR%21!,R"!2B<```\G1P`#\$HG```%(L(!D.\5)$$S +M(($,$`4`/]\*)(`/``"MW@\@2!`$&L(3$0]1$A4D03,@@0#:#R)"`!,/$1(5 +M)$$S((%*)@``#R9&``'ESW6``,`@"!U`$8#CRB2!#P``K=XU\B6(9(@&(H(! +MQ;H0N04C@P\````_97D%(8$/`#\``)L>6)!GB":("+ME>6B($+ME>6F(&+ME +M>9P>6)`&(0$2Q;F9'EB0`-F5'IB0F;E,'D"0)(!8'D"0"I"4'AB0;R!#`),> +M&)`R"H_^B'#-`&_YIL#@>.'%X<;/4$&E``(H8$&U``,8#/8&EB`,H"S&EB`$X"T&AB`SW```%55 +MP<;@?\'%X<7AQB2(SW*``-!8IHC"N2YB`-D/(8$#SW.``"B00(.$[29Z0*,8 +M\$5Y(*,EB!4CC0,CI2:(18A982:E((",(1"`1?>*(1``(*`CN2&C`(`JN`*C +M`-G/<*``\#8LH".#):`F@R:@)(,GH">#**`E@RF@*(,JH"&#*Z`B@RV@((,D +MH,'&X'_!Q?'`9@\/^0AWFG&Z`':7/ +M<(``1#UX&(`*SW"``$0]?!C`"L]P@`#`/008``O/<(``1#V$&$`+*(&(&$`` +MSW&`````)(&,&$``+R''!0BY)7HO(0<&17F0&$``6@E@`"79308/^?'`,@X/ +M^<]S@`#8/4.#`-W/=J``+"#0AO)J]']_9\6G!*=`(D*`)J=#HP;R`H.CHP'@ +M`J-E!@_YSW&``.`@"($`VD"@#($!V4"@SW"@`+`?-*#@?O'`X@T/^0O(`-X$ +M((`/____`PL:&##F#2``R7#/=8``Q`81A8#@M`PB`,H@8@`A!B_YT*7QP'H) +M``",(/^/RB`A`-'`X'[@>/'`F@TO^6K8HL&+<0':M@B@`$ASC^@*(<`/ZW+/ +M<```TA2*(\4$BB2!"HD!;_I*)0``0"2!,438`=J*"*``2'./Z`HAP`_K*),$*)0%O^DHE```$%``Q0"2!,`':)@B@`$AS +MC^@$%`4Q"B'`#^MRSW```-04BB-%"/D`;_J*),$*`A0`,<]V@``P1AMX02C% +M`$PE@(P`'D`1U/8*(<`/ZW+/<```U12*(X4)Q0!O^HHDP0H=V,]V@``P1@"F +MN'``%``QSW6``(S-0"V"`*EQL@]@``';D.@`%`0Q`!8%$`HAP`_K@7P@.(!VL!Z-PI1`$PB +M`*8!VL(BB@"&(_T/)[L/"8``@.+,(&&@"_0R=\PC(8`)\@/O!>L+"<(C#P[" +M$\EW!/`!V0CP"'4!X&<(1($`V8H@_P^$Z8#ERB!*`XP@_X_*(($/_____Q7R +M,B2"-,]Q@`",S0\*40!B<19Y`A'```GP%GD+"I$`!A'```/P!Q'``%$#+_FG +MP/'`"@W/_X8+3_]."P`&T@A/_PHAP`_K!X +M\<#/<(``/"("@,(0``91($"`Y`^"!='`X'[@>/'`L@H/^1IP*'4Z6"@S+G/<(``8+,OH@R` +MSW*@`,@?9.`>HA#8#J(!V!4:&(!-<(8@_`/0X,P@@H\``(``$O*,(`.$$_(* +M(<`/ZW(*)(`*SW```#(1BB,:#5D&+_JX@XO^RIR!/#>#F_Z"G`;Z,]Q +MGP"X_\]SH/YL`W:A%J'/`78,@`.)Z!2.@>#*("$!B`AA_\HA80#/ +M<(``=)<`@`T(G@`V"F``$):TKL]P@`!TEZ"@37"&(/P#C"`"@!3TSW&``$@9 +M`($!X`"ASW"``#PB`X`8B(3@*`\!_\(,@`.R"P_[!O",(`.$*`]!^K4!#_GQ +MP$()#_D`W@+=SW>``,"30"<`&X0N"!DP($`.42``@'@)(O_*(((#8;WI#760 +M`>8.""```-AU`0_YX'B`X/'`$-@*\D(/#_N2"6`!BB`$`-'`X'X.#@_[9@E@ +M`8H@!`!N"4`'#0B1`(H)8`<`V/+Q\/'@>/'`T@@/^<]V@`#,!P#="_`0V+AX +M"R$`@*0)(O_*($(#`>7Q#?20((:`X/'`X<7/ +M=8``Q`80A9_H$@E`!X+@+`EA!\H@(0`!V!"ED@TO^Q'8O@\@`!#8$:4)Z((- +M+_L0V-H(8`&*(`0`SW```%#;Q@YO_X#9P0`/^>!X\"X/@/(0?*("$`L0?O^*'`\<#/<```($Z2#8`(SW&``#@I`*'/ +M<0``N`O/<(``I"@@H,]P``"($W(-@`C/<8``J"@`H<]P#P!`0F(-@`C/<8`` +MK"@`H0784@V@"`NXSW&``#PM`*'1P.!^X'CAQ>'&02T`5,&X%P@5`3,F`'"` +M`%!(0">!46]I7G/=:``B"0PI3^#`MU$*#X-`"&`?X``"+V5N3^C +MSW&@`/`7O:&D@(H3`P&FH:.`%..FH:*`4R/#@*:AH8"FH<`@(0C`("(,8(!S +MH6QH8(-SH?@0`X)SH?P0`(`3H4JAP<;@?\'%X'CQP.'%H<$(=<]PU+K^RD#` +M!/!^">`(`=C/<9\`N/^ZH038&Z&+_XF'`H=AH((`!(=08@@0.(<$X((`"E>=T%S_C/<8``6"U@ +MB<]RGP"X_P;KSW'0NO[*/J(:H@[KSW"@`#@N!8`$((`/P````/$(@(_````` +M:M@8N!FB'(+@?N!XX<7/.!XX'C@>.!X`>+@?\'%\<"R +M#,_X"'?/.!XX'C@>.!X +MX'AAONL.=9`!Y>$$S_C@>/'`<@S/^`AWSW*@_H`$SW"?`+C_5J`G"70``-TH +M=NX.[__P)T`3X'C@>.!XX'C@>.!X8;[K#G60`>6A!,_XX'CAQ<]UH/X@!`#: +MSW.?`+C_MJ.`X.!XX'C@>.!X`>+@?\'%\<#^ +M"\_XZW#/=X``+!P`AS4(7P#/=8``U"``A5(@@```I0GPSW"@`*@@#8#DX.P` +M!0!F#N__5-@`%000AB#_#N4(`8'/=H``."J8%@"6?PB>`.H*#__/=8``/"+) +M%0`6I;C)'1@0DQ8`EJ6XDQX8D-<5`!:EN-<=&!`.A:6X#J4`A<@0``:&('^. +MRB`B`,HA`@#4"V+YRB*B`0&%R!``!H8@?X[*(&(`RB$B`+@+8OG*(J(!`(7/ +M<8``W+#$$``&);C`N,H([_P*H>8-#_Y_V`JXSW&@`-`;$Z%_V!"A`-B5N!"A +MSW$``(0>#@H@``;8SW&@`/`V!(%&(,`!!*&4V-8-[_\8V0"'42!`@`0((@'* +M("(`40//^`HAP`_K/'`T@KO^`'8)@U@!P#> +MSW"E``@,SW6``(@HPJ`$A5$@@(`@#L+YSW$``#@*F@D@``;8"\@%((`/`0`` +M_`L:&#`$A2,(G@#/<(``E#D`@(OH1@IO_XH@Q@@+"%$`:@@`!0WP`-F>N<]P +MH`#\1"&@X'C!H,]PH`"T#]R@B@C/_`H(``'*"Z`&`=AF#^_Z`=BQ`L_XX'CQ +MP#H*S_B(=0#?"N@9"%$``=[/<(``)!G`J`;PSW"``"09X*@)Z1L)40`!V<]P +M@``A&2"H!?#/<(``(1G@J`KJ&0I1``'9SW"``",9(*@&\,]P@``C&>"HSW:@ +M`,@?SW"``"09&![8DP"(BB$0`!'HSW"``'DA`(@+Z,]P`P!`#44>&!`PI@+8 +M&!X8D`+P,:;/<(``(1D`B!OHSW"``'HA`(@7Z,]P`@!H""`>&)#/<(``*``A +M'AB0SW"``.`%(AX8D!@6`)9%(``#&!X8D,]P@``C&0"(".@8%@"6A2`!!!@> +M&)`/"U$`&!8`EHBX&!X8D!@6`):`N!@>&)`8[0#8E+C/=8``Y`@`I7'8!K@& +M#.___-D@A<]P``!,'/8+[_^?N1@6`):%N!@>&)!U`<_X\<"8<`/I(PP2",]P +M@`!H!@`0!0`*(<`/ZW+/<```V@X%!>_Y>=O/<(``8"T5(``!(*#1P.!^X'@` +MV4HD@''/(%>>!_+RA!`.'%`-I*)(!QSW6``,!] +M2'.H((`!\"7!$`'C)7H`V9ZY&7D$(8``0B``@,H@8@#@?\'%X'CQP,]QH`#( +M'Z01`@#/<(``+#8`@#6!SW.``%2(EB!!#Q!R`-K*(F\``8/5N8'@`=@"\@"# +M#0A1``T)A`\``(@3`-@#\`'8@>+,(&*`4`WA^LH@X0'1P.!^`N$P>4%I#0H# +M`")X$'@#\`+8SW&@`,@?'J$0V`ZA`=@5&1B`X'[@>/'`X<50W0#:SW.@`,@? +MKZ->HP(@0@!>HP':%1N8@$#:3J,$(+[/``(`$+`/P?])`,_XX'@`V<]P@`!T +MER&@SW"``.2R')!BN$@@0``0><]RH`#('Q^"$'@((0$`,'D"V!4:&(`_HN!^ +M`N$P>4%I#0H#`")X$'@#\`+8SW&@`,@?'Z&*(!@(#J$"V!49&(#@?@#9SW"` +M`'27(*`AH.!_(J#AQ>'&SW&``+#'18$DZ,]QH`#('T`1#@;/.!_`-CAQ/P'"X]AJ"*__BB$$`\]VGP"X__V& +M"B'`)T#9G[D]IL]QH/X<`#:F4R;`-`4@@`^P_@``%J;/<```1!SZ".__"B#` +M+WIP&14`EB,('@)8'H`7(14`EB(5`);/<8``W`@`@1:F`8$6IOVF!]C*".__ +M"KA3($$'!]C^".__"KC/<*``U`L8@$(@``A((```SW6``"0ZO!T8$,]P@`#D +M"`"`"R#`A,HF(A/*(&(`7_2#"I$@;]B"".__!KC/<```T!MV",__SW```-0; +M;@C/_\]P``#8&V((S_\'V%X([_\*N,]P```$'%((S__/<```"!Q&",__SW`` +M``P);P5`!:)Z$$K3B7`O@.^ +M7.8!V"'P--X>\(PB!*`9\DPB`*(1\@CV&PI0("<*$2&&WA+P%PH0)(PB`:`+ +M]$S>"O!FW@CP/-X&\$;>!/!4W@+PA-X`V('@$`R!!),5`Q;)<`IQ*G(*)(`$ +M/0'O^0HEP`3@>.!^X'C@?P'8SW*``,0&(H(EB1+ISW&``+"5>H'/<8``[).$ +M*P@),"%!#@T)7P`(V`NB`=@)H@#8!*(%V`.BX'[QP.'%"'4H<`7KSW&```!T +M!/#/<8```%Q;>H(-K_BT>2T%K_@!V.!XSW"``+X&`)`&Z`#9SW"D`!Q`,J#@ +M?N!XSW"``+X&`)`&Z`/9SW"D`!Q`,J#@?N!XX'[@>,]Q`0#'`\]PH`#L)R:@ +MX'[\'`BT\<`:!L]P@`#L+0"`0'CTV`#9D@ZO_P': +M--@`V9&YA@ZO_P#:,-B*(08`>@ZO_P#:--@`V0/:;@ZO_Q2ZA@ZO_S#8PK@) +M"%$``-@#\`38SW(!`,8#SW&@`.PG1J'/O^0HE``0R:`0A@0\``/S_[@VO_RS8$(4"(``$C"`/B@CW +MG@VO_RS8"'?O"!Z`!?``AH"X`*:*#:__--@7"%X%`(8`V0#:@;@`IC384@VO +M_Y6Z,+\"',0S?/`/>1"Y!2&"#P``@OW/<:``["=&H00@@`\````?2+B&N!"X +M!2"`#P``0OT&H1"%`B``!(P@#XH+]XMQ@@]O^8H@#PT`%``QYP@>@`3P`(:` +MN`"F@<%J#V_YBB!/#`04`#$-")X``(:!N`"FBW6*((\/3@]O^:EQ(,`(N`(< +M!#"*(,\/.@]O^:EQ(,$"%``Q)7@"'`0P,O#/<0,`0O[/=Z``["!"X!2"`#P``0OT&IQ"%`B``!(P@#XH+]XMQ\@YO^8H@3P\`%``Q +MYP@>@03P`(:`N`"FSW`&``+_!J=`)($PS@YO^8H@SPX"%``Q,0*O^*+`X'C@ +M?N!XX'[@>,]QIP"(20#:"PA1``/8#J$"\$ZAX'[@>.'%X<;/=8``*"Z@C0#> +MP*.1[8'@S"$A@`WR"PH3","C"?#`X@;8!O9"(@`(0[@"X`"CP<;@?\'%N'!` +MW``A`(/QP`X`)`"8<8P@`H"+]@HAP`_K/` +M(&D`@>+`(6D`B"`^`(D@P0^((3X`B2'!#X#@UB`K"(#AUB$K"'8)``#1P.!^ +MX'CQP-8(C_BAP3IQ`-^`X,HAP0_*(L$'RB"!#P``RA3*(X$/``"(`LHDP0#8 +M!*'YRB7!`\]Q@``L+D"QSW&``"XNX+%,(0"@RB7.$V0`+@#*)LX3&G=:=P7P +MR7<:=6IP0"!3`(MQ`=JN"^__`-L`%`TQ+R/()*EV*;W(OK_EV24I%$PB`*#* +M(,(#RB&"`\HB`@1,#2(`RB-"`\EPW@[O_ZEQ0B%1(+4)=:!`(E(@R7`B"2`` +MJ7%Y`*_XH<#@>/'`(@B/^#IPSW"``"@N`(@:<9D($0`Y"1`@SW&``(`'I8D$ +MB1UE,G7*(

"?``V2"OZ@[O_XK9`>;/?@`@@2^``(`')HD!:3$.`Q!`*8`@%'BU +M>-1XSW.```":$&-2;6WH`N`0>-1ZSW.``/294F/@Z0'9W_$!Y:]]L0W2D-D' +M3_CQP'X/3_C/__"G$@E8PA$("T]DT'3_C@>/'`U@Y/^`AUSW"``"@N`(AZ<:'!&G+/"!$` +M.0L0(,]Q@`"`!\6)!(D>9G)VRB',#\HBS`?*((P/``#,%,HCC`\``#P#RB3, +M!+@"K/G*)8P#2B$`(`#>"O`!'5(0!H^$Z""M`>4!YL]^SW>``(`'`"<`%`:( +M`>!C#B,0`G=`*X`@%'@5($`$U'C/<8```)HT(1(``-G%"A"@BW%*<`+:=@GO +M_P#;"^@!%(`P`1T2$`:/VN@!%(`P`*W5\0HAP`_K@*(<`/ZW)3 +MV`:X^]L*)$`$I0&O^0HE``0@PA,*$@``P$$H`0)3(<0`%0P2`0'9SW"``"@N +M5P(@`""HSW&``(`'0*D"&0(!02@.`U,FQ1`#&4(!3"7`@,HBR0?*((D/``#" +M%,HCB0\```X!2`&I^$O!`*8$@-'D*%(`P%2%!`0'FSWX4>;EA +M`!D$!(`@`B,O(`@D`,!!*`$&P[D!X<4.0Y""P0IP`MIB#Z__`-L+%(0P+R@! +M`4X@A0&]"4.@,+C#N``@#@2"P:EP`MJN#J__`-L+%(0P+R@! +M`4X@A06&^`>#E#G60#WAA`V_XH\#QP.H*3_BBP4#`0<)`*!0%0"D7!0#=0"H3!4`K +M$@4!WDHE@"&I=P3P"G7*=P#`%;@3>!0@P`62#&_X!]D"(%`#`B!`(X(,;_@. +MV/'`621<.(MPSW&``.1(2@EO^(HB!`)*)$!X +M`-FH(``#%B1`,&&`0)`KV!*X`>%5>&"@,'E6)%PXT<#@?O'`@@P@`$?8`-K/ +M<:L`H/]9H0?8&J%8H='`X'[@?N!XX'[@>/'`SW&``/PO.(&`X0@,`@#1P.!^ +M\<#/<8``_"\]@8#A5`T"`-'`X'X9`H`'%0*`!Q$"@`<`V<]P@`!0HR&@`0`@ +M`B*@\<#AQ<]U@`!0HS()(`*I<+AP`(42Z$HD@'//$* +M(<`/ZW+/<```AAF*(T0!I0-O^4HD``#Y!R_X*'#/<(``4*-`@".`#.K/<(`` +M$"X`@$0IO@,-X#(@0`X)\,]P@``54D0IO@,R($`.X'X`V\]QJ@#P0V6ASW(` +M`#\_1J'/<```/C\'H8H@$``(H0G8C+@)H<]P```6'`JASW```!\?"Z'/<``` +M'!8,H9'8!+@-H038#J'/<```/SX/H5"A<:'@?N!X\<#N#@_X*@X@`@#=$@L@ +M``?84@[O_QIPSW:D`+@]K!8`%L]WI0#8RSG9HKBL'A@0`=BLI_8>&!#/&!`:V/,>&!#T'A@09-C('A@0JMC)'A@0:=C,'A@0P-C- +M'A@0SW"E``@,/J`V#\__T@D@``IP&-B5'A@0SW&``)@@H:'(V`*A`*$#H<]Q +M`0`LML]P@`"H&M080`"4V`NG0=G/<*4`S'\MH,]PI``,@**@F08/^.!X\<`: +M"```/@_/_YH+``!2#H_ZT<#@?N!X\<`.#@_XSW"``/RO0"`2!@AQSW"``)`& +M(*``WL2H!-]$+CX7"B%`+@`A@'^``/RO"@FO^1S9A"X*$@`AC7^``&RCJ7#V +M"*_YBB$*`H0N`A<`(8!_@`",K1IPX@BO^9S9`"&1)``90"-AOZ$=&!2U#W60 +M`>;U!0_X\<"B#2_X1"@^!P`ACG^``/RO$*X!W_&N(:Y`KF*N`QX"$7*N5@D@ +M`!,>`A'=!0_X\VXHDPP]=`6_Y"B4`!!4(T"#/Z:!`9"X>*>!8*:D>*&&0"$/ +M!*5X`:8=Z@&!`AS$,#"[!!S$,``.!^X'CQP.'%SW"` +M``@N((`!W6!YJ7#GN">X4B```,HE(A#*(4(#RB'A`<"X$WC"N,]RIP`42`NB +M+*+/<*H`X`>SH%4$#_C@>/'`X<7/<:``R!RH@0BAL@AO^`;8.00O^*EPX'CQ +MP+X+#_@(=L]U@`!0HP"E(:58K;H+[_]YK2H,[_\#I02E!O#F#Z_^BB")#,]P +MH`!X10"`!""`#W````!!*#Z%`-GR]<]PH`"T#SR@SW*K`*#_.:('V!JB.**N +M#0`"@.8!V,!X#.!""F_Z`=F]`P_X\<`^"P_X"'?/=8``4*,8C4AV.G$:#6_Y%-G/<(``@"B2#6_Y%-G1P.!^\+$?^5[\0KT@"#E +M`8$"',0P,+L`'`0P(($$',0P8'F+<$(C02"_"76`0")`(%H-[_^*<#T"+_BB +MP/'`Z@D/^#IP6G'/=X``<+T,C\]V@`!0HZ6&AB#_`4.X#B4-D,]P@`#X+2"` +MRB5B$&!Y!-@@Z!J.@.#,)2&0'/(`V!#=&G`"N!5XQW"``(`O((`&Z2*`%>E@ +M>2IP8;WI#7600"!`(`#8&JX,CX8@_P%#N`6F&@YO^$IPS0$/^`HAP`_K"$"#_KQ +MP*(-H`+AQ<]P@`#L/[H*(`8`W<]P@``(0*X*``;/<(``)$"B"@`&SW"``*0R +MHJ#:#2_Z`]C/<(``+#:CH"4!+_BAH/'`X<7/<:``K"\<@;V!!'W/<(``>"$` +MB!,(40#/<,#?`0`/'`(@@``*X(0`#1P.!^X'C/<(``I#(`@('@`=C@?\!X\0#8&0@1`H8+X`<*V`OPSW"@`*@@#8#DX(_W$(7U +M"!Z`'@OO_\EP%14`EH"X%1T8D#D`#_A<%0000!4%$`HAP`_K@QPAB`]`(#@`=G` +M>1,(GD'/<(``N`<`@('@`-@"]`'8`=[EO_CO/'`X<7/<8``]$E`@2&!I\%&P<]Q@`"D,B*! +M1<(5)$(PSW&@`"P@L('/<0$`O!9`P0'90<%"P1#90\%$P$6"`-@,V0ASF'"X +M<``EAQ\```!]@@XO_=APW0;O]Z?`\<#B"4_^SW*@`,`O`-F(&D``$X*+N!.B +MSW"``!P8`9`0N$4@``_`&@``SW"``+@U8@_O^2"@T<#@?N!X\<`F#N_W`-F; +MN<]PH`#0&S&@4@R@``#>)>C/<(``]!8Q@,]U@`#`(`SISW"?`+C_/:`DA0'A +ML[FUN;BY)*4VH,]P@`"X!R"`SW"``.0U\"!``$!X`(4/"%X$SW"?`+C_W:`Q +M!L_WX'CQP+8-S_<:<,]P@`#`SW&``#PB4R`-`('E`=T@@<01`0;`?3L) +M7@'/<8``'!AAD<]Q@`"\!\"!/.-Y9F3A'PD$!`HAP`_K0'8$0A1`2"%8'D"V!L(D`!`%@00"B'` +M#^MRBB","(W;:0`O^;ASBB`0`1&F$(8!"!\`%(:KN!2FSW"``"P<`(""X!+8 +MP"@B!LH@(0#/(&$&&:;/<:``R!\8$0"&H;@8&1B`BB`0`!&A"=@(N`^A$X:I +MN!.FSW"``-RP!X"#X,P@XH$']$`H@""?N(@>`!":"``&SW"``!`X-03O]^"@ +MX'CQP`T)40!B#```!/`^#```T<#@?O'`_@F@`.'%]@DO^AK8SW"``+PU`)#/ +M!X\"\_WSW6@`,`O%X4:A<]VH`#( +M'X@5`!`'V!D>&)`!V`AQ"'((<_X(+_^8<,H-[_Y4V!4('P'/<(``;`8@@,]P +MGP"X_SV@@!4/$"*_7@[@!>EPSW&``)@[$H'X8!*A`-B('0`0"=@(N`ZF60// +M]_'`\@KO]P#9SW6?`+C_787/=H``;`9`ICVE'-D5\,]SH`#(.S:#1"$"!S:# +MAB'_""5Z-H.&(?\(17G/#&`&`-C/<8``N`<@@8H@3`86#6`` +M`]IJ#F```]C6"X_\"-AR#"``BB'_#P'8P0+/]^!X\<#/<(``N`<`@!,(T0#* +M#<_\W@G/_SH/``#1P.!^X'CQP"H*[_=*)$!Q`-[/=8``+#9`)0,@Z@!!>[ +MYO'F\>'%SW*``"PV1!*``$`B`PPG"%$`2B1`<0#9J"```_`C30#/<(``R#4U +M>`'AH*`O>0#81!H"`.!_P<7@>`#:SW&``&0V3ZD!V`VI3JE,J5"I4:E2J5.I +MX']4J>!X\<#Z"._W`=K/<8``/")C@7B+.0L1`0"!SW&``*0RQ!``!B6X4B`` +M`"&!P+@!VH#ASW&``+#')H'`>H#AS"`A@,PB(H!\\H#P$0@>`,]P@`#$L`"( +M"0A1`)AR!/!*)```SW"@`"P@<(#/=H``+#9%AJ:&`B.`@`#:RB)O``(C3X,` +MW2&B`-G/<(``W+`PH"6`X'\Q +MH/'`;@^/]\]U@`"D,B&%)7@!I<]Q@`#`>(-(`#)<`7PU@I@``.%/0>O]Z'`\<#*#J_WN'#&#>__*'"` +MX`#9RB!!`"?RSW:?`+C_'8;/=8``;`8`I3VF'-D5\,]PH`#(.S:`5H"&(?\( +MAB+_"$5Y5H"&(O\(17G/@SW&``+@'((&*($8`N@A@``+:$@I@``+8`@W@ +M!`+8Z@R/^0CPSW"``*0R^@E@``.`T<#@?O'`Z@V/]RAU/0C1`,]Q@``<&"&1 +MSW.``+P'0(,\X3IB(8-DXEEA(0E$`PHAP`_K@`V`"ISW&``+@'(('YV`?=@@\@`*ERV@A@`*EPSW"``*0R +MX@NO_Z.@U@N/_U[PSW&``&RP!(D="%$`!8D5"%$`SW```/__X@WO_P#9G0@0 +M`,]Q@``\(@"!Q!``!@T(7@$#@1B(&P@1`<]Q@`"X!R"!BB#$!!X/(``"V@+8 +M-/"V"T``:0B$#P``%`3/<(``I#(`@`T(40">"X_Y*.@`V<]PH``L(+"`SW`! +M`.@*0,`!V$'`0L!#P43!!MD(<@#;F'.X02O]Z7`X'C@?N!X\<#V"Z_W`-G/``+#'`B8-$,]P@`#$L.6'8X`%+_X0-W4!VZ"(PB/.`*7! +M"PU0$`.((PA1`,]P@`"D,B.@SW&``+@'(('/V$8.(```V@#81_#/=8``/"(` +MA<00``:'"%X!@PM1``.%&(A["-$`SW"``*0R`8"WZ,]P@`"4(`"0@>`!V,!X +M#+A;"(`/````$+""SW`!`+P60,`!V$'`0L$1V$/``-B,N$3`*'`,V0':"'.8 +M<+AP`"6''P```'U""^_\V'#/<(``+#;/<8``N`<@@`0.%&(@3"!`!SW"``-RP!X`1"-X` +MSW"``*0R`X`,\,]Q@`"X!R"!BB!)!SX-(``"V@+8D@X```$#C_?QP(8*C_?/ +M6EXZ:@N`2ER@FO^0#8&_!&"._Y!]@7\`7K +M!H8">2<)4@!3((#!!*8-],]Q@`"X!R"!BB!+`H8,(``"VMH-(``"V#D"C_?@ +M>/'`X<6AP8H@_P\Z#"``0,!A"%$`SW"``#PB`(#$$``&(PA>`<]P@`"4(`"0 +MSW&``+@'@>`!V,!X#+@E"(`/````$,]Q@`"X!R"!BB!%!R8,(```VGH-(``` +MV#GP((&*($4($@P@``7:9@T@``78+_#/<(``N#5`@`#9"PI1`""@)?#/<<#? +M`0#/<*``K"\\H,]P`(#__W(*[_\!V1?H9@C@!(MP"B4`D!'RSW&``+@'((&* +M(,8!O@L@``/:$@T@``/8J7`>"^__`,%Y`:_WH<#QP+APSW"``+@'`!`$`,]Q +M@`#H,4`L@``4>!4@0`$`814(D0(*(<`/ZW**(,T`Z02O^*#;R@P``-'`X'[@ +M?N!X\<#AQ<]P@``\(@.`&(@?"!$!"B'`#^MRBB!-`8HCA`U*)```L02O^+AS +MV@\```AUSW```+_?P@GO_P#9"PA1`(PE$)40]V(/K_P!V,]Q@`"X!R"!BB!% +M`@H+(```VF(,(```V,T`C_?@?N!X\!AJ&4B````L('@``0'@":)Z"(_X +M7@[/_1L(4```A1,(D`#/<(``N`<`@(/@R`S!_S$`C_?@>/'`O@]/]\]P@`"D +M,@"`+0A0`,]U@`"X!R"%@.',(>*!S"$B@@CRBB#1``#>-@H@`,ERP*7J"H_\ +M\0=/]PHD`(#QP`SR"B'`#^MRBB!-`HHCC@V%`Z_XN'-Z#D__9@[O_P+8SW`! +M`+P63@^O_`'9T<#@?N!X\#(_YSW"``$`92B0````8``'/<(`` +M/"(#@!B(%P@1`0HAP`_K/'`X<7:"Z_Y"-C/=8``N`<`A8?@S"`B@C?RSW&` +M`"PV08$'ZL]RH``L(%""0*'/`!V,!X#+@C"($/````$,]P@`"D,@&`B>B*((4&1@@@``+:F@D@ +M``+8"09/]^!XSW*``"PV`8(`V87H`X*`X`+R`=E3((#!!*(!VL(B@0``V(#A +MS"(A@`+R`=C@?P]XX<7AQL]U@`"T,L`5`Q83"]4/TFO4?KYF`*8AID*F`6O% +MN,`=&!#!QN!_P<7/<8``9#8-B3,(40#/<(``O#4`D.FXT2"B@@CT`-@.J0VI +MN0.O_P^I`=@.J0^)0B``@-4"K__*(&(`X'[QP/(,3_$!VA-O#/<8``I#*@@2D-41#/=H``;+`DC@\)40`ECH'A`=D" +M\@#9@.'*(8(/```0)P/T(H//=H``$#8@IBD-41#/=8``;+`DC0\)40`EC8'A +M`=D"\@#9@.'*(8(/```0)P/T(8//=8``+#8GI:EQSW6@`"P@T(7E@0(FS1,) +M#=\7Q:'F@0(FS1,)#=\7QJ$H@X;ISW&``+#'*)$CHB6XP+@J"*_Y`]EI!$_W +M\<#/<8``N`<`$00`N'#/!X\<`&"T_WSW6@`,`O.H7/!_N,(4?"9\"0!4$$$P5!1`*(<`/ZW**($P)Q09O^(HCA083"%$`BB`0`1&E +M*@X@!PK8BB`0`!*E'@X@!P78SW"``/0M((!@>/'`SW````@!\]P +MH``L(!"`!-E\VCW;0,"+<.H.(`07NZ'`T<#@?N!^X'C@?P'8X'[@>/'`X<7/ +M<0,`0`W/<*``J"`MH,]QH`#`+Q2!\+@4@0SR!""`#P@```#7<`@````!V,!X +M!_"&('\/@N`!V,!XP0@1`!41`(:@N!49&(`1\,]PH`"H(`V`Y.#/=:``K"^. +M]QR%D0A?!@QTA"3"GT+TI@EO_UK8;>A$\(H@B0,."R_^BB')"<]QH`#4"SN! +M_@HO_HH@B0,L8((``( +M@\O_8MP@N"*(/\/#/+/<(`` +M^$`#@""``,`B>(#@RB!,`^$`;_>AP.!X\<"AP0#80,#/<(``Q+`AB(MP)PE1 +M`,]QH``L(#"!SW*``"PV2()">0\.17!.```@5@\/_0/P-@\/_1$(D0"*(/\/ +MH<#1P.!^SW"``!A!`X`@@`#`(GB`X,H@+`#S\>!XX<7/<8``X"`D@2"!SW.` +M`!A!0X/5N:""1H.*(/\/@.(%\@*"HGA((```"2!``&JX2"```.!_P<7/<8`` +M&$$+@4"`#H&`X,H@@0______"O("@$)X2"```)D@!@!((```X'[@>/'`D@\/ +M]Z'!"'?/<*``+"!`$!(`SW"``.2R7X``W40G`1.(X4$J@`$:`!B2PA>`\]P@`#DLI@0@0`2:19X&F(K#QX3`(*( +MN`"B`=@/JL]P@``\(@&`P!``!A$@0(#,(:*#4`X"!P?P6@X@!Z^J@.!(#@(' +MSW"``+@'(("'XG/<8``;+`*@0'@"J$:\$86@!`M +M"%$`SW#M_KZZ0,"+<`39?=H]VY(+(`07NT8>0A-%'D(3%@UO_T<>0A-B#`_Y +M!X8FAD)P`B!"``D*WP<&IDP6@!`-"%$`3!Y"$P+P`*:-!B_WH<#@>/'`-@X/ +M]PAVSW"@`"P@\(`=#O(1`-T*(<`/ZW**(`T"BB,)`YAU+0)O^+AS-PF0`4X. +MC__/<(``+#;HH,]P@`"X!P"`@.#,(.*!"?+/<(``I#(!@(#@L`L!^<]P@`"\ +M-:F@SW"``!`VSW&``*0R(('P((`#^&`/"5$`SW&``+PUJ:'/0"!%PB%`,]P@``\(@"`Q!``!@<(7@%`H>!^\<`F +M#2_W`-G/"SW>G`!1(QZ?(@@]XT*?C@L]VI`"X/9L>V!/D@J8>V!/E@I(>V!-& +M@J,>F!#/``H!"J1S@5GA$B$FI!8C@?PJIX'[@>/'`)@P/]\]QIP`42`#=J*$' +M@<]V@`#H?0>F$('/ASW#S#__\$*&@V+:AFKCU&A@`SW&D`+@] +MFQ$`!L]W@`#8(`.FIA$`!@2FDA$`!@6FHQ$`!@:F_]B;&5@#IAD8`)(9&`"C +M&1@`SW&D`.S_SW```/__IZ$&H0"'`>``IQ4(40`!V<]PH`#('#&@*@_@!BAP +M!-A^""_X0"8!$@W8<@@O^$`F@1+/<"@``@'/<:``["<&H8H@C0`&H0"'0B!` +M@`"G!?3/<*``R!RQH+T##_?@>/'`3@L/]U$@P($-$@\VSW.``(BQ`Q(--L]Q +M@`"8LO1[$8L0$X0`$O(!X`AR,A6%$&>1`AD"`<]V00"#`&:QSW.``)0\`ZD1 +M\$`D0@`Q%8400JG`$P,!`ZG/=B$`@@!FL<]S@`"8/!,-A0#$H0"#`>``HP2! +M4_#/!<]P@`"(P79X`8@#\$AP`"2/#X``B,%V?^2/"";.$P@F`!"@<$D@ +MS@,6:]5XSW:```C#`&;/=H``B,)V?F&&SW:``#PBQ(;8AL5[!".##P````AF +M>`+P`X4"H9@5@!!HB0T+``!$J6#8&+@$\`#8G;@$H9T"#_?@>/'`X<4#R*00 +M``!1(`"`SW"``#PB!(`$\AN0`_`:D(8,@`6[Z,]PH``4!`/9(Z`@V`P:'##/ +M<8``'#L6@0'@%J$#R`#:F!`!`*00`P"4&$``GA`!`:R[DAA$`+X0`0&MNX`0 +M#0&D&,``D!A$`'X0`0&`&(0`/66P$`$!HGDP>;`81`""$`$!?AB$`(8CY8^R +M&$0`L`Y"_1$"#_?@>/'`D@DO]PAS$(DS$8T``=I`JPT2#S;/=H``L+'N9L]R +M@`#@L4C@2Z17[4"QSW&``'Q1]"$"`,]Q@`"PEQ1Y +M0+'/<8``?$_T(0(`SW&``)"7%'E`L<]Q@`",4?0A`@#/<8``V)<4>4"QSW&` +M`&11]"$"`,]Q@`"@EQ1Y`>!`L0B%"PA>`039-*4"\-2E#P@>`0G91AU$$"[: +M!?`4V48=1!`RVENU68U983!Y1AU$$!KA.K47"!X`"MA4'000!MA6'000!]@' +M\!#85!T$$%8=A!,%V`^EQ@A@`^EP/(TH<$0=0A"&(`,`YKE8'0(0RB)!``OR +M4"'#`6]Z1!W"$%`@PP%O>%@=PA`3"5X!2'.&(P,`;WI$'<(0#0D>`:6X6!T" +M$`L)W@"DND0=@A`O#Y`0E@EO^>EP`!``(+D0``91($"`\=C`*"(!RB"!#P`` +MDP#`*"$!A!T`$!C8C;@3I0B%42#`@,]P@``\(@7RMA"``(FX`_"=$(``$J7/ +M<*``K"\9@,]Q@`"(*#"XP+CB#:`&!:$(A00@OH\`!@``"_(VN,"X&W@!X%H= +M!!`"V!JE`_!:'803`-@7I1BE-@JO_>EP*(4!VDAS02D`!36Y4B```%(A`0#` +MN,"YE@PO_IAR)0?/]O'`P@[O]@?8SW:@`,@?2!X8D,]W@``\(B.'SW6L`-0! +M&H%,'AB0@N`"V,H@X@#0'0"0BB`$``^F1A$``;`>`!!&$0`!M!X`$!_8"+@. +MI@B!42``@`#8B[@*\A"F/@T/^<]PH`"D,`&`A+@*\!&F,@T/^<]PH`"D,`&` +MI+C/<:``I#`!H<]P@`#X00"`%0@>`(8@_PXBN!2XSW&``.0%"Z&J#\_XD@@` +M`>H/``-J"$`#SW```%556AX8D`'961Y8D,]PI@`H`"^@`X=:$`$!SW"F`.@' +M)J!V#P_]`X=>"B`$#9``V(P=&)`'V(T=&)``V(L=&)#/<(``^"T@@&!Y!-@- +MZ,]Q@``L&AJ!.X$D>`\(7@1N#V```M@$\&8-8`4!V,]RH`#$)P\2`(9CAT0@ +M`0(;@P\:&(`/$@"&H[@/&AB`#Q(`A@5Y#QI8@#R#SW"@`#`0)*#/<(``Y+(0 +M>(\:&(#/<(``*(S/<8``*)P0>!"Y)7B0&AB`BB`$`)(:&(`=@T`:`(#/<(`` +M?"%3&AB`#Q(`AI^X#QH8@`#8$!H`@!Z#'!H8@(4%S_;@?N!X\<#/<(``P'T8 +M$`0`"B'`#^MRSW```.4.WML9`2_X2B4``.!XSW&``,!]!8'@?P:A@.#QP#38 +M!_1V#\_]4"!!!`7P;@_/_4\@002F#^_]--C1P.!^@.#QP/38"/12#\_]4"`! +M`/38!_!&#\_]"''TV("Y?@_/_='`X'[@>/'`X<6AP6AU1"+``H8B_P-&(L(# +M5'H&N31Y66$5><=Q@`!X@HMP".$V#>_V!-JI<(MQ+@WO]@3:`-C9!._VH<#Q +MP%H,S_:AP0AU`"2.`&)^`B9.$:!R8GH"(@*!`-A`P`WR+'Z+=B]P2'%*#R_\ +MR7)V"J`!R7``P`)]J7"%!._VH<#@>,]PH``L(!"`SW*``&RP!:+/<(``E)8( +M@`#;&0C>`02*"PA1`"6*"0E0``';((((ZP""@>#,("*`$O(/\!#I#PA1``6* +M@>`!V`+R`-B`X#@"PO@-"5```MC@?P"BX'[@>/'`N@O/]L]V@`#8(`"&`>`` +MI@#=%0A1``'9SW"@`,@<,:`J#Z`&*'#/<(``7`<@D(:Y$+D%(8(/``#"$L]Q +MH`#L)T:A`9`0N`4@@`\```(3!J$`AD(@0(``I@;TSW"@`,@/'`X<4*)0"`SW&``$!&`!$$`"_RSW"D`+@]`-LU +M#!$`FQ`-!L]R@`!$1J"BIA`-!L]R@`!(1J"BDA`-!L]R@``X1J"BHQ`-!L]R +M@``\1J"BFQC8`/_:IAB8`)(8F`"C&)@``=K/<*``M`]_V`-C/=8``^"T@A4!Y)P@1`\]V@```+B"& +M8'D"V(OH((9@>0/8A^CF"^_]4-@+")X!`-@"\`'8+R$'(,]P@``L.,]W@``0 +M+EX*K_H`I\]Q@`"0/!2!`>`4H<]Q@`#8(`"!`>``H14(40`!V,]QH`#('!&A +ME@R`!L]Q@`#\+P2!*PA1`":!SW:@`.PG8'D`V,]P@`!0HQB(E^C/<`$`!@$& +MIL]P$@`&!!;P"B'`#^MRSW```(<9BB/%"4HD``#5!._W"B4``<]P`0`'`0:F +MSW`2``<$!J;/<(``4*,@@`.`*^G@AT0HO@/&V)*X!J8@A2=W8'D`V',($`,@ +MA6!Y`-AG"!`$((5@>0#87PA0!""%8'D`V%,(D`3/<#D``C,&IL]P.0""3`:F +MSW`Y``)F!J;'V)6X&/!$*+X#`"&/?X``"%+'V)*X!J;/<````C,&IL]P``"" +M3`:FSW````)F!J;&V)6X!J;Z"X_^SW"``%"C&(C/<8``4*,V">`#(($O"1`@ +MSW````)N!J;/<,$`0FX&IL]P`P#";@:FSW`V`$*7!J;/<`(`0FL&IL]P$`"' +M<@:F!8\0N`4@@`\``$)P!J8$CQ"X!2"`#P``@G`&I@./$+@%((`/``#"<`:F +M`H\0N`4@@`\```)Q!J8)CQ"X!2"`#P``0G$&I@B/$+@%((`/``""<0:F!X\0 +MN`4@@`\``,)Q!J8&CQ"X!2"`#P```G(&I@&/$+@%((`/``!"<@:F"X\0N`4@ +M@`\``()S!J8*CQ"X!2"`#P``PG,&IB"%8'D`V"4($`,@A6!Y`-@9"!`$((5@ +M>0#8$0A0!""%8'D`V!,(D00,CQ"X!2"`#P``PG\&IL]P`0!&:@:FSW>@`,@? +MI!<0$!4)$"#/<%``QG,&IL]P(`#'0#8-0@0`R"%8'D`V"D($`0@ +MA6!Y`-@A"%`$((5@>0#8%0B0!,]P@``&=`:FSW"```=T!J;/<(``QG,&IL]P +M0`!"=`:FSW"``,=S!J;/<`(`1FH&IL]P$`#&:@:FSW"``%"C6(C/<8``4*,` +MB"2)@.(!VL!ZSW.``%"C4@L@!GF+)-@8V?H)H`8SVB\(4`#/<(``D#Q0$`0` +MSW"``%"C#!`%``HAP`_K*(P<'#PD0(,]P!@!":P:FSW`0 +M`,=J!J;/*(\<,I!<` +M$,]Q@`"0/`(@``03H<]P`@!':@:F((5@>0#8+0@0`R"%8'D`V"$($`0@A6!Y +M`-@9"%`$((5@>0#8#0B0!,]P90#";@:FSW"``-@@`(#/<8``V"!"($"``*$$ +M]`#841\8D*$%C_;QP#8-C_;/<(``@"\4@(#@B_)B":_^!]AZ<,]P@`!PO0R( +MAB#_`4.X8;B&X/0`#0#/=H``4*,DAL]R@`"`H3,F`'"``%1(0"(1"P2Y-'E` +M(A`*0"(2!D`B#PA`(@T$.F)`)P%R%'D`><]Q@``H,4AP5?#/<8``2#$$:E'P +MSW&``&@Q0"(``DOP0"(``\]Q@``H,1X(K_X`V@2&SW&``$@Q!+@4>+A@._!` +M(@`'SW&``"@Q_@]O_@#:!(;/<8``:#$$N!1X^&`K\$`B``7/<8``2#'>#V_^ +M`-H$AL]Q@`!H,02X%'A"`)PJ@]O_@#:!(;/<8``:#$$N!1X(G"6#V_^`=I^#V_^:G!I!(_VX'CQP,]P +M@`"`+P^`$>C/<(``4*,$@,]Q@`#0H@*X%'@X8,]Q@`"(,9()C_[1P.!^X'CQ +MP.X+K_9$VL]P@`#$4<]Q@``TL2H((`0`W@+=%@@@`,EP8;WY#760`>8Q!(_V +MX'CQP+8+K_8`VL]Q@``\(A5Y8($$N``@D`^``$Q.N1N8``"!!!`/(,]V@`#$ +M4;X8V`.@@4*&BB`'#V&&'67P'8`0[!W`$""!1H;/=8``-+%EACA@^!B``!8F +MP1/T&,``%B7`$P3@!.$:#*_V"-H,$``@%GX6?01M)&X&#*_V"-J=`X_VX'CQ +MP#(+K_82V:G!"'8B#N`%BW!*)`!Q`-JH((`"%B2`,"B("PF2`&&Y**@!X@+" +M```$Q..&#L&,``\!B```"%!L(%PSA@^!B` +M`(/!]!C```07$!#/<(``-+$6(``$!."*"Z_V"-KCA\]P@``TL8?!]G@$X'8+ +MK_8(V@#`((6Y&1@`((6Y$0`&%0@>`+X9V`,@A;\1``:`N`CPOAD8!""%OQ$` +M!J"XH@HO_;\9&`"$Z)(*#_T$Z`#8`_`!V!!V1`QA!LH@@0,`A;D0`091(4"` +M\=G`*2(!RB&!#P``DP#`*2$!(@MO^X080`"9`J_VJ<#@>/'`-@J/]L]V@``L +M1L]U@`#,!Q+I((:-Z0"E7@CO^`[8J@KO_HH@$``!V`"F#O`@A25X"_`>#Z_X +M#MAV"N_^BB`0``#8`*8`I5D"C_;QP-H)C_;/<8``X"T`@:"X`*$6"R_\`=C/ +M<(``2)8`$`0`3"3`@,HAS0_*(LT'RB"-#P``@0S*(XT/``#:`,0%K??*)>T` +MIPQT``#=%&T`(($/@`!(E@>1QI'DD1"X!7X%D4.1$+@%?P*1$+I%>!IPU@_O +M]\EQ6G#/<(``Q%CP($$#1"T^%PHA0"X`(8!_@`#$/B"@Q@FO^PIP"'$`(8`O +M@`"X/HX+@`0'#L03F._/<(``N%CP($$#1"T^%R]V`"&`?X``;#\@H)()K_M* +M<`AQ`":`'X``8#]:"X`$SW"``$B6`(`!Y6D-!)!-`8_VX'C@?N!XSW&``#`N +MSW"``/POX'\BH/P<"+3QP!IPSW"````N((!@>0'8@>#*(<(/RB+"!\H@@@\` +M`)X9RB."#P``J`'*)&(`S`2B]\HE(@`*<-'`X'\$%!`T\<"B"(_V`-W/=H`` +M>+'/<(```"X@@*"F8'D!V('@RB'"#\HBP@?*(((/``"8&.!^X'CQP,]P@`"800"`?0A4`<]PH`"L +M+QJ`4B```&T('P#/<8``Z'T+@0'@"Z'/<(``\"T`@$!X]@\``,]P@`#L+0"` +M0'@V"<``W@\/_EX)S_P&\!X,+_V*((D,SW"@`'A%`(`$((`/<````$$H/H7R +M]<]P@``\(B.`2($TD5,B``#N#Z`"`=NZ#*_X$MC1P.!^X'CQP.'%M,$&\-8+ +M+_V*((D,SW6@`+1'<14`E@0@@`]P````02@^A?+UBB#_#V\=&)!K'1B0>@@O +M^8MPT@W/_`_H;Q4$EFL5!98*(<`/ZW+/<```L1-5`Z_W--LR"\_XK@]``Z4' +M;_:TP$"(`=@`H6BZ`KI5>L=R@`"`+V."8Z%A@F&A8H)BH62"9*'@?P"BX'CQ +MP/X.3_;/=X``G$$&AP.`SW6``.A]((!)A0`B@`\M`,#&`GF!"7(`H<'/=H`` +MV"``A@'@`*87"%$``=G/<*``R!PQH$X*8`8H<(MQH@MO]T+8`(9"($"``*8' +M]`#9SW"@`,@<,:``%`0Q!"2^CP``%__*(<(/RB+"!\H@@@\``*83RB,B#)0" +MHO?*)2(``(6"N'H.(```I2(((``!V`"%HK@`I2F%QW$M`,#&G@B@!.EPM09O +M]J'`\<`>#F_V`-K/<8``_$D`@;O!5\`$B4HD`')XP,]P@``\(@.`"(#`N$#` +M7Q2`,,]Q@``L&D'`.,!"P%X4@#!#P!J!.X$$>3&YP+FH((`"`-L`)(`P9!C" +M``'B3WK/<(``Z'UBD,]P@`"\!D"08PN!`,]S@`!PO0Z+SW6``.A]AB#_`2@5 +MC1!#N`(@0(.OBW"+RB!B`(8E_Q';;<]U@`#H?2D5C1"&(_\!#B6-D\HE8A"[ +M?:5XNVO/@4A`$7`SW"@`+1' +M1Q``AH#@S"$B@&(%`0#/<(``Z'T`$`0`421`@,HAP0_*(L$'RB"!#P``JA/* +M(X$/``!V`%`!H??*)2$`SW&``'"]#HG/``IA,(40#/<:``R!P!V!&A5@A`!C?`SW>@`.PG +M$+@%(($/``!"+2:G!2"!#P``@D8%((`/``!"8":G!J?/<`@`AQ`&IP"&0B!` +M@`"F!_3/<:``R!P`V!&A`,#/<8``0)H6>62!0('/<`\``/P*NP1[R;IE>L]S +MIP`42$VC18$A@0JZ1'C)N25X#J/.#`_^1L``P`KHBB'_#\]PH`"T1V\86(!K +M&%B``-@#V43`4<%(P,]Q@``0?@AALP@S`D?`",$%P!$@0("(`P$`!\``)`$P +M9!&!`('A>`,A`(-P`=ED&$(`!\'/<*``M$=@&%B`SW"``#PB`X`0N9NY,B"` +M#P``V`*?N8#@`=C`>`^X)7C/<:``M$=?&1B`!O`&""_]BB")#,]PH`"T1W$0 +M`(8$((`/<````$$H/H7R]0+9`-@:<`?`$2``A/H"(0!0P<]PIP`42%P8``1' +M"!`@*0A1((H@Q#:*(80X'_`<%`0P"B'`#^MRSW```*L3I-MQ!V_W2B4```HA +MP`_K!"X@;B'N(RX!J<@AD(A08`&],]RH`#('`#8$:)*)``ABG5`((`Q$'A+ +MP$`A@#$0>$S`0"A`(4W`"B:`)`'A8;T@IA4)40#/<:``R!P!V!&A,@X`!@/` +M-6T`)1<6+R?()25X$'@0N(4@B@`&IT`O@"&!N)>X`"53%@:G+R/()$`K@"&! +MN)>X!J<+P`:X@;@&IPS`!KB!N`:G`(9"($"``*8&],]QH`#('`#8$:&2P)/! +ME,*5PQH,(`16),0R-L"*Z``@@2^``.`\$(D!X`]X$*D`P`SHE@C/_!$(40`` +MV';`!,"`N`]X1,``P,]R@`!`F@.X%2``!!EB&F(,@BB!$L).P`W`MG@`()4/ +M@``@?A/`\!V`(/0=`"`)P(@B?``O(0`@!"F^(!8-K_LO<`X@@0\````!3\$3 +MP(@@?``$*'X$+W#Z#*_[#L$.(($/`````0_`"2&##P``_P$)(((/``#_`4@B +M`@!((P,`-L!4'9@@51W8(!\(40`*P1@4!#`$N4`L@`$X8+5XQW"``'R:0K!C +ML`"&`>``IA,(40#/<:``R!P!V!&AW@P`!@K!!L!`+X(A@;H$N0:X.&"U>,=P +M@`!\FB*0/'D0N25Z1J`:G#,!`+@$D!KB! +MN"5X!J<`AD(@0(``I@?TSW&@`,@<`-@1H1#!8;F`X?@$[?]`($`@$<%AN8#A +M",!6!.W_`>``A@'@`*83"%$`SW&@`,@<`=@1H?8+``;/<`@`AA`&IP"&0B!` +M@`"F!_3/<:``R!P`V!&A'@L/_P;PB@SO_(H@B0S/<*``M$=Q$`"&!""`#W`` +M``!!*#Z%\O46#(_XSW"``.A]!,$,@#A@SW&``.A]#*$-@0'@#:$5`&_VN\`` +MV<]P@``$?BRH+:C@?RZHX'[@>("XSW&@`.PG!J'@?L]P@``'(<]QH`#L)P:A +MSW"``$!_(*#@ +M>/'`SW"````N((!@>0C8$'G/<(``7`M&#`_W6@\@`P?8K@H/_NX/```J"``` +MT<#@?@AQ6(D!@`*AB.I9B8#BPB"B`,`@H0`"H>!^X'C@?N!X\<"6#@_VSW:` +M`-@@`(8!X`"F`-\5"%$``=G/<*``R!PQH`H*(`8H<,?8E+C/=:``["<&I<]P +M`P""*P:ESW`#`,)$!J7/<`,``BP&I<]P`P!"10:ESW$``,)TSW`#`,)T!J7/ +M<`,`@F\&I<]P`P"";`:EQMB0N`:E)J6R"2`&"MC/<```@FP&I:()(`8*V,]P +M```"+`:EE@D@!@K8SW```$)%!J6&"2`&"MC/<```@F\&I7H)(`8*V,]P``"" +M*P:E:@D@!@K8SW```,)$!J5>"2`&"MC/``';SW&@`.PG9J&( +MZ,]P@`#P+0"`0'A*\!6"42``@,HAP0_*(L$'RB"!#P``?QG*(X$/``"J`,HD +MP0!L`6'WRB7!`,]P$P#'`,]UH`#L)P:ESW`0``9I!J7'V)6X!J7/=H``V"`` +MA@'@`*87"%$``=G/<*``R!PQH*8((`8H<,]P``!"+0:ESW```()&!J7/<``` +M0F`&I0"&0B!`@`"F!O3/<*``R!SQH$D%#_;QP.(,#_;/<(``^"T@@*'!8'D$ +MV#+HSW:``-@@`(8`W0'@`!Q$,P"F%0A1``'9SW"@`,@<,:`^""`&*'"+<9() +M+_<`V`"&0B!`@`"F!?3/<*``R!RQH``4`3'/=8``!"Z&(?\,0(5"N6!Z`M@` +M%`$Q0(4#V&!ZP;G5!"_VH<#@>/'`6@PO]@/8SW:``/@M((;/=8``^#A@>:+! +M!N@@AF!Y!-B&Z$D#(`!(%000`]@:<,]WIP`42,]VH`#L)TX(+_X%V`ZESW"` +M`-@@`(`!X,]Q@`#8(`"A%PA1``'9SW"@`,@<,:".#^`%*'`#V.((+_>I<038 +MV@@O]R)M!=C2""_W)&T+V,H(+_E%X<(I1:' +M":7/<*L`H/\8@`NESW"K`*#_&8`,I<]PJP"@_QJ`#:7/<`4`Q@,&IL;8D+@& +MIL]P+``"`0:FSW!:`$(!!J:*((L`!J;/<$``APT&IL]PT0#"#0:FSW#```<. +M!J;/<(``V"`@@!$)40#/%`GE:#"_V+W`!PD_@SW&``)1_%*57H1BASW!``(<- +M!J;/3EA\@LO]C5YX+@<>,`@ +M8@""(,0"SW&``)1_$J43I1:ASW"``-@@`(`!P@'@5:'/<8``V"``H14(40#/ +M<:``R!P!V!&AA@W`!0&5$+B%((0`!J8"E1"XA2"%``:F`Y40N(4@BP`&I@25 +M$+B%((\`!J8%E1"X!2"`#P``@@T&I@:5$+@%((`/``#"#0:F!Y40N`4@@`\` +M``(.!J;/<(``V"``@,]Q@`#8($(@0(``H0?TSW&@`,@<`-@1H02%*X4(IP6% +M#:<&A0ZG"(47IPF%%J?/<*L`H/\XH"R%.:`MA3J@K@SO_0Z%2!4$$(PD@H!% +M]HPD/X$-]M8,X`4*V$H.@`-"($`@@.`"!-'`X'[@>,]R@`!T!QD('@"`X5'8P"@B!,H@803` +M*"$$`_``V.!_`*+@>/'`@@@O]@38SW6``/@M((5@>:'!@>"\]#8(K_R*((0( +M@>"V]`#8`!P$,,]W@`#8(`"'`>``IQ<(40`!V<]PH`#('#&@U@O@!2APBW8` +MV"H-[_;)<2"%8'D`V``4!3&H<88A_`\C"!$$C"$#@![R"B'`#^MRSW```+X9 +MBB,$#"4$+_>*)(,/P.'*(<(/RB+"!\H@@@\``)D9RB."#P``-@$`!"+WRB1B +M`,]U@``$+D"%`-A@>D:Y`!0`,4"%1"`!#`'88'I$N0'8K@SO]LEQ0(4(V&!Z +M`!0!,0`4!3%,)0"`S"5B@,PEHH#*(<(/RB+"!\H@@@\``)H9RB."#P``10&@ +M`R+WRB1B``+8;@SO]LEQ`!0`,4"%4R!0``388'H*<2$(T"``%`4Q"B'`#^MR +MSW```)L9BB.%`VD#+_=*)$``$M@V#._VR7%`A0`4#C$%V,&^8'K)<2,.T!`` +M%`4Q"B'`#^MRSW```)P9BB-%!34#+_=*)$```(="($"``*<7],]QH`#('`#8 +M$:$1\,]U@``$+D"%`=A@>@AQ0(4$V&!Z`]E`A0788'H#V3T'[_6AP/'`V@[/ +M]<]U@`#8(`"%`>``I0#>%0A1``'9SW"@`,@<,:!*"N`%*'#/<(``!B'/<:`` +M["<&H<]P@`!&.@:ASW"``,93!J'/<(``QB0&H<]P@``&/@:ASW"``(97!J$` +MA4(@0(``I0;TSW"@`,@!_(Z#QP%(. +MS_7/=H``V"``A@'@`*8`W14(40`!V<]PH`#('#&@P@G@!2APSW```,(LSW&@ +M`.PG!J'/<````D8&H<]P``#"7P:A`(9"($"``*8&],]PH`#('+&@90;/]?'` +M$@LO^!;8O@V``\]Q@``\(@"!Q!``!@\(7P$!@<00``8-"%X!L@MO^!/8SW"` +M`.0M((!@>0O8T<#@?O'`?@UO_(H@B`4.Z.H+;_T`V,]P@`#X+2"`8'D$V(#@ +M'`P"_]'`X'[/<(``/"(#@`B`SW&``'BQ"0@>``&)`_`"B>!_`*G@>/'`N'&- +MZ`HAP`_K@@;0/P($4`!"6"#P$``,`NNF5Z"PN!``'AT<#@?@HAP`_K +M<]P```BTC1XSW&``$O, +M#_#/<```(]+/<8``3LP'\,]P```DTL]Q@`!1S"G:$KKP(@``#B""#P`!``!` +MPHMPK@A@`P/:H<#1P.!^X'CQP$8,S_4#R)00``#/=H``V"`$()`/`0``P`"& +M02B0(P'@`*8`W1<(40`!V<]PH`#('#&@J@^@!2APSW$D``A@;A$\,]U@``\(J.% +MJ(45#1X0SW6```8ZIJ'/=0,`@@()\,]U@`#&/::ASW4"`(("IJ'/=00`QC&F +MH<]U2@!"`2#PSW6``#PBHX6HA1<-'A#/=8``AE.FH<]U`P""`@CPSW6``$97 +MIJ'/=0(`@@*FH<]U!`#&,::ASW5,`$(!IJ'/<:<`%$AWH8"X&J)E`L_UX'CQ +MP.8)S_4#R`'=SW:G`!1(E!```*BF!""`#P$``,#&#N__+KC_V)NXSW*G`)A' +M'*+/<8``A`8`@0#?@.#*(<(/RB+"!\H@@@\``*P9RB."#P``OP#*),(#J`7B +M]LHEP@/VIKJBZ0'O]:"AX'CQP'8)S_7/<*8`G#\9@*T('@#/=H``D`8`AD:` +MH!(`!B\H`0!.(($'02G0`!$(U2!(<(`@"@`R(``$D.@*(<`/ZW+/<```K1F* +M(TL"BB2##T4%[_8*)0`$SW6``$#,0"7`$CH,+_<)V0#8N@FO_P\@``2`X`#8 +M#R``!`7T)@S/_P/PL@W/_P/(N1"``!MX@+A`A@JM)H*6(4$#`"$`!!B(C"## +MCP)Q!?)AN`]X&*DF@J`1``:?&1@`R@O/_QT!S_7QP,]P@```+B"`8'D!V('@ +MRB'"#\HBP@?*(((/``"=&_U`]AAO8PE +M_Y_S]0HAP`_KK; +MN0/O]IAS`>$@IA4)40`!V<]PH`#('#&@&@N@!2APSW$&``)USW"@`.PG)J`` +MAD(@0(``I@GTSW"@`,@O +M]:'`\0/8@.`8#T(#SW"``(@H!(`9")X`SW&``#PB +M38$^D5,B```N#^`!`=O1P.!^\<#AQ<]U@`#P.`"%&P@?`)X*P`(>#L_[Y@F/ +M^0(.S_\`A8"X`*4I!X_UX'CQP'(.+_R*(`0"$>C:#8__Q@_/_\]P@`#X+2"` +M8'D$V`7HI@]/_PX(``#1P.!^X'CQP'X.C_7/=8``\#@`A3D(7P#/<(``^"T@ +M@&!Y!-@4Z"(.+_SBV!#H?@JO_0?87@M@`PAV5@T/_YH)K_W)<`"%@;@`I:4& +MC_7@?N!X\<`N#H_UI!`!`!4)'@:V$`$!SW"@`)@#/J">\``6#4&\L``6`D%= +ML``6#D#/H``6`D%`&(0``!8"0%&@`!8"04@8A`!$)0(3-0H0`1C;I1 +M)@"0T2$B@A7RT(BHN<]R@`!(OZ080``"OM9^PF(+"IX'B[FD&$```-I:H%N@ +MYO$`%@)`6J``%@)`6Z`(VG00#@&^$`\!PG]B?T)_N!""`)BYI!A``,]QH`"8 +M`T)_>F)0>G(8A`"Z$`(!\']P&,0#I7I/4$K_7`I?'`SW"``-P2#MD!VNH*(```V\]P +M@``4$PG9`=K:"B``2'//<(``"!(JV0#:R@H@``#;SW"``+`2"]D`VKH*(``! +MV]'`X'[@>/'`!-@>"^_[`=G/<(``B3D`B,]Q@`"*.9H+(``@B='`X'[@>,]P +M@`"L1O$%@`/@>/'`ANCN"````-DBH-'`X'[QP`(,C_7B#`_\SW:``'@'9M@B +M;@':%@_O_$ASB^@*(<`/ZW+/<```MA39VXHD@0DY\`(6!1%,)0"`S"6"CP`` +M__\-]`HAP`_KO]HHD@0EGV,EQ`=K*#N_\2'.,Z`HAP`_K +M`0>*8.[_Q(@[O_$ASC.@*(<`/ZW*AEL]P``"Z +M%.7;0"6$$.OQG0./]<]QH`!@'1*Q%)'@?O'`N'$U"%$`"0U2`!D-T@,*(<`/ +MZW*GV`6XG-L=!Z_V2B0``$`M@``4>$(@`0//<(``/'`D@JO]=APSW:` +M`+X&`([/=8``O`9>#^__((TAB!$)W@`!V<]P@`"0.6;P`H"S"!$`70D>`<]Q +M@`!H)0"51XE-"($``)9!B44(@0#/<(``P`8`B":).0D!`,]P@``\(@Z`+0A> +M`<]P@`",.4"`SW"``)`Y"NK/<:``+"`P@4)Y@F/]:'!&G`Z(MQA@SO_$AS$N@`%``Q0"J"(`0@ +M@0\```#_1[E4>C,)$"#'``SN`XJ`N`.J +M$F\4>!MB8XM88(&[8ZCDJ@/N)JH"\"6J0B1!(%4)=8!`)4`@`0&O]:'`X'CA +MQ5,@#0"@J00@@0\`!@``0B$!@`0@@`]`````RB%B`""JUW!``````=C`>`"K +MX'_!Q>!X\<",Z&X-S__/<:``+"`P@<=Q26L`TB*@T<#@?O'`=@BO]=AQ"B:` +MD(AUS",B@`;R0B8&`2\FAP$Z#>__R''/<8``-`<`H27N)(@"N31Y0X@#X0(0 +MA0`C"A\`"B'`#^MRSW```.(4BB.("4HD```]!*_V"B6``0AA&PA?``HAP`_K +MSW>@`+0/$<]Q@`!H)0>))0C!`0&)4R4" +M$!D*`0`$)8T?``8``(#E`=H&B.!^X'CQP(8.3_7/<*`` +M``3,B,]P@`"0.0"(`-V,Z-X+[__)<(CHD-D#R)"YH!A``*EP$O#/<(``,!P` +MB`OHC"8"D`GTD=D#R)"YH!A```#8`O`!V*$&3_7/<8``/"+P(0$`*!&``"B! +ME0;O_P#:X'CQP!H.;_7X<,]R@`!H)<]V@`"\!@"69XK/<8``W"`M"P$`SW"` +M`+X&`)!ABAT+`0#/<(``P`8`B$:*$0H!`,]P@`#=(`"(`_``V`"I`-T^"^__ +MJ7#/<(``P`9`B,]Q@`"^!@")((Z`X@':P'KH/'`X<6AP=APBW5`)$(P0"2#,"AP?@OO_ZEQ`12`,`GH`A2` +M,`7H0B8&`2\FAP$@P`8)[__(<0$4@3`$Z0*(`_`!B.&XT2#B@`/R(P@>`0HA +MP`_K/'`X<7/<(``W+``V26@SW"``*0R(J#/<8``/"(`@<00``9W"%X!`X$8 +MB&\($`'/=8``>$``A4(@`(#*(&(`)PA1`.8,8`.I<,]Q@`!`0`"!0B``@,H@ +M8@"%Z"AP.@U@`R*%SW6``)1``(5"(`"`RB!B`"<(40"R#&`#J7#/<8``7$`` +M@4(@`(#*(&(`A>@H<`8-8`,BA2T#3_7@>.'%`-O/6"AX!W$$/`=Q!#@?\'%X'CQP*8)K_P1V+GHSW&``&@ESW"``+P& +M`)!'B54*`0#/<(``O@8`D$&)10H!`,]P@`#`!@"()HDY"0$`SW"``.`M`(": +MZ$X,0`*(Z`O(!2"`#P```#P+&A@P/@Q``HCH"\@%((`/````U`L:&#`+R)"X +M"QH8,(8+S_L#\$H.#_;1P.!^X'@`V9RYSW"@`*PO/:#@?N!X00#/^^!^X'C@ +M?N!X((``VH#A1?8!VC-Y(*"`(0&`?]S`(00#1[D@H`/J,WD@H.!^H<'QP.'% +MK,$`V4K!D-D8N4C!SW.``'BQ((,$((T/`0``P(8A_@,DN0ZY"R5`D$[`CL(6 +M\M=U````0,PE@I\```"`S"6"GP$````$]"&#`_`B@ZZXK[BPN`5Y(*(.PPC` +MBW4$(X$/`0``P"ZY0"D"!D5X2,"*(`8&22%@SW"G`(A)+Z"J#Z`# +MJ7`(W$\!;_6LP*'!\<#."&_U"'*MP0C82L"0V!BX2<#/<(``>+&@@`0AC@\! +M``#`AB7^$R2]#KT+)D"34,&0PQ;RUW8```!`S":"GP```(#,)H*?`0````3T +M`8`#\`*`KKFON;"Y)7@`HQ##"<4$(X$/`0``P"ZY0"D`!@5]2<4?"IX!"L`$ +M([Z/````&$4@P`!*P`7RA2`0`4K`)0H>`9N]SW"@`"P@!8``VP*X;KB`X,H@ +MS`#)N*5X2<`&\`D*'@*=O4G%$,"!Q4+`J7!"""```ML#R`S"SW&``"P:N1B" +M`!J!.X$D>!L('@("NL]P@`#8R51Z06#/<*<`B$DOH*8.H`.I<`C<0P!O]:W` +M\<#*#P_UH\%A@`AU0,,`V`JE;0M>`@0C@`\!``#`+KC/$T*8DDB@@!A +MNDNE$FH4>,=P@`#(RLJ`SW>``!RSQJ4+@,]V@``\(@6EPX8@P-2&]8\$?N1^ +M";Y`*0\"Y7[%>`0C@P\````097@'I0B%&.*>N`BE2Z6/\#<*G@+/<(``W$$` +M@$'`0L`A"!X"AB#_"2.X`>`5")0`"PB1``;88<`D\`?88<`B\"+`8<`>\$'# +MSW*``)`&0()&@IX2`@8K"I$!!".^CP```!@/],]R@``\(D2"2(($(KZ/``8` +M``7R`=@*I0/P"J4`V`'&00X>$D+&(L*@XLHB(0`$)H\?`0``P$$OA!-$)@\6 +M([\!YP0FCA\&````,;X`)L43SW:``'A-,B8.$0(F3A$3\%,FPA#/=X``7%%= +M>DIG!":.'P$``,`NOL]W@`!X3DNE$PL>`B#'SW:``(!-[F8"\`'> +MA"@$#@`A@'^``-#)`KI4>D=@8;Y88.:E`8`$(X,/+P``W2:[Q7M2(\,#!:5G +MI<]P@`!XL0.``-\="$X`SW"``/A!`(`5"!X`AB!_#QUX0"C/`P3P`-^/OYOO +MSW:``/@M((9@>0#8)0@0`R"&8'D`V!D($`0@AF!Y`-@1"%`$((9@>0#8"PB1 +M!!H,K_P`V`B%!7_HI34&+_6CP.!X\<#*#0_USW6``!0N`(7$D,EP[@F@`(8@ +M_`,`A!*@6`1"4V.H-X`')22R +MBB"(!<]R`/\`_SX/C_T`EO8,[_TTEI07`!#/`\]P@`!T!B"@SW"@`/PE +M$X!L@GA@#*+/<@"@"`#L<$"@;R)#`.QP0*`.'UB0I@@`!<]P``#_?\]QH``, +M)`&A&]@$H7$$+_6I<.!XX'\!V/'`X<6AP;X.K_N+<++H`!0%,!T-'@!^"``` +MSW&``.2R0X'/<8``=)=!H23P"PV>`'H.S_\>\`T-7@(*#L__&O`[#=X`"-C/ +M=:``Q"<3'1B0V@[``!T($`4"V#P=`)#/<(``Y+(C@,]P@`!TER&@&=B7"%"& +M"00O]:'`"B'`#^MR%]B,N(HCQP"9!R_VBB2##_'`X<7/<(``Y+(_@`0A@0__ +M_X\X!"6`7P``<,,]Q@`#DLA^A1"(`4\]U@`#DLD,($0(_#5Y1-@_/_YP= +M`!`3#9Y3SW"``)`B!8B8'0(0%/`5#=Y3SW"``*`E&8B8'0(0#/`#A3(.+_8D +MA9@=`A`$\`#8G!T`$)P5`!"`X,P@XH!Q\L]P@`#T%@N`#^C/"SN+6XN+@$H1:BSW"@`*@@"(`?A0\('P$+#=]2@-B8'0(0F!6` +M$$`H`083"-\!@KD="IY3N@C``1KP'X51(H#3L[@?I<4A@@\````'12$`!L]Q +M@`!PLRR)AB']#U(AP0%%N25XSW&@`(@D$*&*(-8`SW&@`,0G?AD8@,]PH`#4 +M"P':4J`$V!`9&(#/=8``Y+(?A4D(G@$4E4$(7P'/<*``+"`/@)KHK7%2#>_Y +M5B5`%8`5`!"4N(`=`!`?A9"X'Z4,\,]Q@`"@.@^!`>`/H1#9SW"@`)`C/:!U +M`B_U&=C@>/'`\@DO]0#9"'8!@,&X@^#*($$@!?(B#B``R7`:<$P@`*#$]!"& +M42"`@<#R$(;/=8``Y+(/")X#SW"``)`B!8@-\!"&#PC>`\]P@`"@)1F(!?`% +MAB:&P@P/]I@=`A"`%0`0!""^CQ!P```']*UQK@SO^58E0!41AL]Q@`!4!P"A +M02@!`U,AQ0"8%8$002@&!11I!2!$`0\)W@$>A96X'J5Y\.(+K_M/)$`"ZP@5 +M!,]Q@`#HBY@5@Q#P(0$`0"L"!H8C_0]2(\,!1;ME>L]SH`#$)T$;F(``VHRZ +M`B9/`/IBR[K7<@````A`+0\#D+]2]P4GCQ%B&]B#C"("@,?WSW&``!0\$H$! +MX!*A`-F=N47PY7EB&UB`60Z%<```P`\.(H,/````$,]R@`!(BQ9Z(((E"S4( +M!!(%``#8#R#``&&X3B,/"`$IP@-X>05Y`"W```5Z%_!"(P,(`-@/(,``8;AX +M>04A`@"*(?\/"_#/VXT2!B@`?R!,B*#J__F!```,]P +M@`!@LPR`SW&@`,@?9.`>H1#8#J$!V!49&(`!A8/H_PL>P`&%P;B#X-;T`!`` +M($'`!!0/,1"%++\&%!(Q=0B>`0W,=0C>`A"%SW:``.2R$0B>`\]P@`"0(@6( +M#O`0A1$(W@//<(``H"49B`;P!84FA;X*#_;GN)@>`A#*)F$0!O(^AI6Y/J8` +MW@2XSW&``#":1I'E>!,(@`#/`>`)H@21&PB!#P``__\`W@GP +MSW&``*`Z#8$`W@'@#:$!E9S@B/0$$!$@"!`0(,]PH`#T)@+9(Z`CA<]P@`!T +MER&@:@L@`*EP@."`]";NSW*@`,0L'!I`!,]Q@`!PLR`:``0LB4`O`Q,0N9^Y +M)7M!*@$A97DFH@T2`3<="=X"$-JKN0P:G#`-&EPPSW*``)@[)X(!X2>B#1(! +M-PT)'@,:V*RY#1I<,*4.$!#/=H``W'S@%@,0185$*SX'`"9!'D"A3)4!XT*Q +MSW*``'"SK(K@'L`0SW*``#":J*GIJ0H9A`0,&4`$1)(0&0`$$+T,OT$J`R'E +M?65]2K'/@0?P"-@,\.>ZRB(A`$#"`12#,,:ZQKMXJ5FI<0;O +M]*+`X'CQP.'%SW&``#PB(X%(@5D*'@"&(/\!SW*``'A-0[@*8@#;@.+*(<$/ +MRB+!!\H@X0?/("$#RB.!#P``;P#*),$`]`$A]LHE(0#/<*H`#%`3"K0`N8&` +MO;FA`=DEH`3PH+VYH66@+0;/]/'`M@W/]`AU#0C>``W,4R!` +M@`T2`C8=]``B@`^``!"R`=G/=H``<+T@J!&.42``@`@-8@/*($(`$8X7"%X! +MSW"``+"R`XB`X%@*H0'*("$!$-@,&APPSW&``!P[%X$!X!>A`\@-$@$VA!`" +M`<]P@``$LC5X*8!982F@&M[$\<]Q@`"@.@V!`>`-H0'9SW"``-P@`=I`J,]P +M@`!@LTZ`!H(!X`:B`_`!V0+:SW"@`/0F0Z!#A<]P@`!TEX#A0:!$"(("*07O +M],EPX'C/T0AD<(G@$,S,]Q@``D +M.C$(7@%`V`P:'#!5$0`&`-H!X%49&``-R,]Q@`"(L11Y`\A`J:H*K_^8$``` +M!_"L$0```>"L&0``SW"``-P@`=D@J,]P@`!@LRZ`!H$!X`:A`MG/<*``]"8C +MH".&SW"``'27(:!I!._TJ7#@?PC8\<`DN5,AP@#/<8``'%16>1,*$`)!D&&! +M!.)P=@!A8/H^0L>P`&%P;@C"-$`SW"``-P@`=D@J,]P +M@`!@LRZ`!H$!X`:A`-@,\`&%42``@`#8RB#A!2&%42%`@,H@H03-`\_T\ +M!\]Q@`!PO0R)3XD;"@``$8E1(,"`5`_!`0?P`-G/<(``Z)<@J$X/@`0]`\_T +MX'CQP,8*S_0(=@&`P;@`WR<(T0#/=8``Y+*/#Q$0$(9W")X!$(89")X#SW"` +M`)`B!8@2\-8.[__)<`AW[?$0AA$(W@//<(``H"49B`;P!88FAI8-S_68'0(0 +M$0C>`1Z%E;@>I1^%E[@?I8`5`!`$(+Z/$'````_TG+B`'0`0,(9N#:_Y5B5` +M%4`F`!*@'0`0`-@%M@'9SW"``-P@(*BT%0$0!H$!X`:A6!6`$)GH,@I/^P7H +M$(;MN`'8`O0`V,]Q@`!&L_0A```\E3A@8K@0N("XSW&@`/0F`Z$&\`+9SW"@ +M`/0F(Z`EAL]P@`!TER&@00+O].EP\<#.">_T`-D(=@&`P;B#X,H@02`%\OX- +M[__)$P@`*#/=8``Y++*)R(05O0PAF4)G@$\E1,)`P`E +MAL]P@`!TEP*`MPD!`!"&#PB>`\]P@`"0(@6(#?`0A@\(W@//<(``H"49B`7P +M!88FAGX,S_68'0(0@!4`$`0@OH\0<```"O1>"4_["^@0AA,(7@,!WPCP`-\8 +M\(8,#_P4\`#?,(9.#*_Y5B5`%8`5`!"H%0$0GKB`'0`00"8`$J`=`!#9"5Z" +M`=G/<(``W"`@J+05`1`&@0'@!J%8%8$0SW"@`/0FD^G/<8``1K-`+H_T"G#@>/'` +ML@C/],]P@`#T%@N`$.C/"SN+6XN+@$H1:BSW"@ +M`*@@"(#/=H``Y+(1#9Y3SW"``)`B!8@-\`\-WE//<(``H"49B`7P`X9V"^_U +M)(:8'@(0'X83"!\!#PU?4PL-7U*`V)@>`A"8%H`0&PC>`5^&/H:SNI6YE[H^ +MIE^F`-D!W1;PG!8!$"4)40`_AE$A0(+/<8``/"(C@2F!!?)$(0T$!?!$(0T" +M`_`!W039&+@E>,]QH`"()!"A'X8S")X!%)8K"%\!H@K``9'HSW"@`"P@#X`% +MZ`W,%PC>`1^&D+@?IJUQX@JO^58F0!6;[0L*GE/`!%?"&(O_`!A_KBH",+[SW"@`%`,H(#/<(``E`<$ +MV:"@SW"@`)`C/:#/<8``Y+(?@0T(WP0/@8#@`-@O\A>!BA$.`1UE!.8&\-8+ +M;_N*(-X,"0B?1/4)'L;/`%A+;G` +MN8H2``%/$H(`SW.``#PB\"-#``(E@1-">$8,K_M/DVD'C_3@>/'`X<6AP0#8 +M0,#/<8``H#H/@0'@#Z$#V<]PH`#4"S&@X'C@>.!XX'C@>.!XX'C@>.!XX'C@ +M>.!XX'C@>.!XX'@QH,]UH`#$)Q45`);/<9\`N/\6H3$5`)86H1#8$!T8D$X) +M;_N+<)?H`!0%,!L-GP`*(<`/ZW(-V(RXBB-?!Y$"[_6*)(,/!-D3'5B0&]D6 +M'5B0V0:O]*'`\<#/<(``P)=R"2_V&-G/<(``P(=F"2_V&-G1P.!^X'CQP#8. +MC_0:<,]UH`#4"Q"%`-^AP4#'(0A0``HAP`_K<@_8C+B*(Y8(BB2##RD"[_4* +M)0`$SW"@_CP"SW:?`+C_%J98'@`4SW&@`/Q$&8$$(+Z/```((`/T'8$3"-`D +MF@AO^XMP@.#*(`(@0B#!()3A;`$-`#(F07"```!(0"<``(H@Z!"(`6IKH+P```V2AP2?#/!`>`'H0Z"!X`6IO3Q +MSW*``&"S+H(,@0'@#*$.@@R`%J;H\<]R@`!@LRZ"`H$!X`*A#H("@";PSW&` +M`!P[!8$!X`6A(/#/`#H0Z"`X`6I@'9`-@5\,]Q@``4/!J! +M`>`:H1:FW@P@`P'8OO'/<8``%#P4@0'@%*$6I@'8"'&`X6`+@@#/<(``Y+(? +M@!4(W@3/<(``!(OKJ,]P@``XB.RP`]@1I>!XX'C@>.!XX'C@>.!XX'C@>.!X +MX'C@>.!XX'C@>.!X$:4A!:_TH<`8AL]R@`!@LY"X&*88AK"X&*8N@@6!`>`% +MH0Z"!8`6IDX(``#%\<]R@`!@LRZ"!($!X`2A#H($@//QSW*``&"S+H(1@0'@ +M$:$.@A&`L/'/<8``'#L.@0'@#J&5\0HAP`_K"&_[BB!5#L]PH``,)`>`!.CO"Q[`702/]/'`SW"` +M`'07>@^O^P'9;]@&N#(/K_L(V0?8"K@F#Z_[!=G1P.!^X'CQP.'%SW&``#PB +M(X$I@5$A0(#*(*(`)_1$N,]Q@`"\.<.X"6$)"1X`-0V?434)7@#/=8``/"(# +MA1B((0A0`*8+#_L(Z,]P@`"$)0B(#0C0`0.%&(@-")$`"0V>40'8`_``V.$# +MC_3@>/'`7@N/]$0B$%--=H8F_!--<$UP!"6`7P```"!!*'Z#!/):"P_[A.@` +MWP/P`=_/=8``Y+(?A0L(7@0`W9KP_P@1H#H+#_L?Z,]P@`"$)0B(A^#,(&*" +M%_0!A8P@_X\3]"25SW```/__&PD!``6%C"#_CPGT#)77<```___*)6$0>/+/ +M<(``/"+P(,$#"8$/"%X!SW"``"!.!/#/<(``+$XXB2I@02X`$<]Q@``X3@AA +M%GK/<(``G%1(8`\('@`_A88A]H\5\@T(7@`_A2,)G@()")X`"0T>4@'=#/`3 +M"-X`SW&@``PD,8&,(?^/]O,`W5$@@('*)2(0@@H/^P?H!"6^WP```"+*)6(0 +M)^W/<8``Y+(?@1\('@*,)@*0S":"GP``4`#,)H*?``#0``/TD[@?H<]P@``\ +M(@*`PA``!AKHC"8"D,PF@I\``%``$O1/@45X#Z$-\,]P@``\(@.`"8`/"%\` +MC"8"D`3T"0B>`0+=40*O]*EPX'CQP-X)K_0`V<]P@`#T%@"`HL$0Z,]SGP"X +M_QVCSW*``,`@!((!X+.XM;BXN`2B%J//SW"``%0'P*"9$X``H+B9&P(`SW"@`,0G9!A8@,]V +M``#_?Q,8F(,;WA88F(,:&%B`BB?_'\]VH`#\1/VF^::*)Y@=SW:@`%`,XJ91 +MI5"E/!A`@(HB&`A.I8`5`A"D&T``42)`@,]R@`!TEU@;0@`+\D(0`(8$(+Z/ +M`,````7R`8(#Z`*B(:*`'4`0SW*``"0ZSW"``#PB(X`K"9Y#'X.+N!^C52+` +M!;0;```*V!RS&Y$&V98;!`#/<*``R!PIH`KP0"(``[0;```0V!RS&I&6&P0` +MSW&@`-0+$($="%$`"B'`#^MR"]B,N(HCU0"*)(,/E02O];AS`=VPH14(7T;/ +M<*``J"`F@`>`C0(@``/=SW>``.2RM!``H?K8)@CO^P#9(-C/=H`` +MO+.>#N`"`*8!V,]QH`#('Q.ASW.``.`@"(-`)A`50(`,@P`0!``$@\]SGP"X +M_P`0!0#X$0``SW&@`#`0(8$VH\]QH``,)">!`B("@#:C`-D#)$,``B4!``#8 +M4!\$$%(?!!!4'P000:;/<(``/")BID.`(Z84DL]QI0`(#`FV"(+`N`BV`!$$ +M`%,D10%3)$$`3!]"$8/ARB'!#\HBP0?*(&$%RB.!#P``G`NL`Z'USR`A`P0D +M@0\```#@+;E_AYH?0A`4'@`1&PO>`@2Y@;DE>`BV!]@(\`#9%2`,(""D`O`$ +MV`'@]0@4@@B"Z[AH#H($'X!P+@P<@#=FO3/<:``J"`F@8PA@XXH`0T`$G", +M],]R@`"\LP6"SW.D`)!!]8,V@P0@@`\```#@+;CGHL]V@`#DLBBB"P@>`%`> +MQ!,(\%`>1!,$)X\?__\``.>B#0A>`#"_4A[$$P;P4AY$$_!_YZ(+")X`5!Y$ +M$`CP5!Y$$P0A@0___P``**(-@P:B!""`#P```/XIN%8>!!`?AD4(W@+/<*H` +M``0$@`FBSW"``,"'((AD:#3I8PET``(0A`"?<0#8J"#``_0C#P`5W1.]\"7/ +M$\]U@`"\LA5]`>#@I1WPSW"``,"7((AD:!KI`A"$`(#ARB1-<,H@+0#H(.T# +M]",/`"G=$KWP)<\3SW6``+RR%7T!X."E(:H"&@(!M!8!$`+=`8$!X`&A"_`$ +M(+[/8`````3T!-T%\`<+'D`#W3(*+_OQV('E2_,O#9$0`MT$(+[/@`$``,HE +MHA$&]%$C`,#*)>(0ZPV0D,]PH``P$`.`@.#*)6(1AN4R!`(`SW:``.2R')9" +M((0`'X;KN"\D"`%[\L]QJ@``!**!SW"E``@,0(#/HFX!7I2IJRC3:,`@4@6CQ"4YPJC&?(&]C,/D1(CN`[P'0_0 +M'>[G$_1%*/X"02G`<%$EP)'"(&(`!]T+\$4H_@)!*0!Q^O$BN/CQ`-@(W2&! +M%Z8KHQRS'P@1!<]W@``\(N.'Z(<$)[Z?``8```/RC+I2IN2YRB4B$N&YRB4A +M$H8A_@]!*0(!31Z"$"B317DHLRD-T1$C"+0#!]W/<8``/"(C@801`0`3"00` +MSW&@`#`0*($)"$``"-V'YF*:($ +MN"B2B;@E>`BR<_!-'D(0SW"F`(P#/8`$(8`/.````$$HP@2:'H(0!"&"#P`` +M`/`LNB6X17C/BX+[1(>*'!]T# +M]`C=SW.``+RS*:.:$H$`Z),$N>5Y*+,`0A@@\"```` +M)[I%>$0G`AP-ND5XZ7*&(O,/!"&!#S@````.NB6Y17@E>$0G@1`4N25XB+A$ +M)P$202G!@%(@0`42IE@>0A#*(8(/``#__\HA@0\``!`?.G$WAD`>1!`$(H$O +M_P,`_RBY-Z:N"6_Y`-JL'@`0<0^>%$@6@A`RAJ#BT2'A@C#R!"&#CP````$( +M\D0A#08CO0'E%0V5$`0AC0\````D00V`'P```"0$(8T/!@```#&],0W5$!4- +MD1`4ZT0A#08CO0'E'0V1$`/KS.(*]E>&,G+*(HX/`0"(#@`L:'#"6#D`` +M#,R&(/F/"?2$Y&*(9D."/3/<8``/"(C@3Z! +M@"&9#A!Q`-W*)6T4&!O*@X`'8P'@)\([@`=C`>`7P)0E?`0#8SW&``#PB(X$I@3UY +M4B$!`,"Y)'@(Z!^&D;@?I@OP%X;J\4H,``!8%H`0@.`D"P$`@.5T`@(`SW6` +M`.2R6!6`$!3H`MG/<*``]"8CH,]Q@`!TEP#8`:'`V9D5@!"`N)D=`A`H<`/P +M0MC/<:``Q">_&1B``-@,&0"``=@0&1B`'X7QN!("`@`2A3>%&@\O^0#:K!T` +M$!^%M0C>`L]P@``\(D.`2!6!$!2")'A$(`,!1"$`#$(H!`&`<\]P@`#P(L&[ +M:&")N!RE` +M`!0F:V>)NWVE=()T%8\0Y'N&)_\3!'M$O_MC]";#$&0=Q!!RA7JE5()[I41Y +M)'C/<8``X$V`WO"N``@C@^``.0B,([8CL=P@``0)L]W@`"`ETB(97[<.^W'[T +M)XX397H0B&(=A!,%>WVE=!6`$,.X''CT)P`0.J7/<8``D)=D'000:!6`$,.X +M''CT(0,`6Z6.'<00SW.``-B7]",``)(=!!!L%8`0P[@<>/0A`0"0'400]",` +M`#WP@.``V`7T2!6`$,.X''C/<8``X"()8<]R@`"`ESRE/0B``#/<8``D)=2A60=!!!(%8`0P[@< +M>/0A`P!:I<]Q@`#8E_0A``".'<006Z60'<00DAT$$)0=!!"*#L``SW"``#PB +M`X`(@`\(W@).%8`0@.!,"T($6!6`$`7H[@D/_P/P%@T```AU308O]*EPX'C/ +M<:``Q"<5$0.&!-@3&1B`&]@6&1B``]K/<*``U`M1H.!XX'C@>.!XX'C@>.!X +MX'C@>.!XX'C@>.!XX'C@>.!X4:#DN^$@P@<6V%(1`(;@N.$@P0?*(.$%"0A> +M``D+W@#@?Q+8`=G/<(``W"#/`\]P@`!T!B"@%=C@?N!XP=@4&@(PSW&``#PB`X$8B`';SW*` +M`.2RAN`7@L(CP0`,X!@@P`!F&@0`9A(``0/@!""`#P``_/^=N)^X[',`HP7( +M[',`HP.!&(@W@H;@`=C"(`$`&"$!`.QP(*#@?N!X\<#>#`_TSW"``.2R,H`G +M"5X"SW&``#PB(X%($((`-(%$>5$A@(!(VLHB@0\``)```O`.V@#?SW&@`*@@ +M)X&L$`T`66&Q<<(E11#*)>82L'AJ#&_["MG/<(``O#4`D,]VH`#$)PL('@&, +M)0.2`_<`W1KPSW"@`+0/_*#/<*L`H/_ZH'X,[_T`V!D6`)8$Z`+8$!X8D,]Q +M@``4/!N!:KVX8!3=&Z$9%@"6A^A1(0#&D`_A`\H@80"1!"_TJ7#AQ<]Q@`"\ +MLT&)P-L4&L(PSW.``#PB8X,2:D?@!""`#P``_/]I@RJ[P+L7N\=S``X``&5X +M[',`HP7([',`HTHDP',`VZ@@P`'P(H>!_P<7@>/'`B@L/],]W@`#DLD"7"'9(<88A_`-"*04!1"((`XP7`1%" +M*(@00-C/=:``T`\*(T"`$!T8D,HC8@"L%P`00"N&!<]S@`"\LR\D"``=LS@3 +M!P%`+`0$!2<``0P=&)!ABP*[2.,0'=B09A<#$7EA,'EF'T00#0J?`@Z74R#` +M@!'RSW"``#PB`X`)@%$@`(`]V,`HX@7*(*$'P"@A!@GP0"@`$:!PSW*``'PA +M"&(7N`/A!"&!#P``_/\%((`!)7B=N)^X#!T8D`O,`>`0>`0@@`\``/^_C[@+ +M&APP#AV8DR`5`);/<(``/"(#@`B`(0C>`AT.'Q$J"&`$R7#/<(``^+.@V<3: +M/=N>#^``%[OE`@_T\<#AQ<]P@`!X!P"0SW&``("AJ-H!W8`@1`L0>(H-;_NI +M/'`X<7/<8``/"+P(0(`2B1``,,2`08/>#(B@@\``!\# +M!"&##P`&``"`XP';P'L$(8T/0````-=U0````,(D`@$2#._[P+DE`@_TX'CQ +MP*H)#_0J#:`!"'7/<8``Y+(?@;"X'Z'/<)\`N/]8&,`(SW.?`+C_6!L`",]R +MH`#0#Q""SW:@`,0G%J//<(``B"@&@!:C&18`E@#9!.@"V!`>&)`8&EB`0@^` +M``8)H`(!V`7M6@\```7P-@\``'()3_L9%@"6!>@"V!`>&)"9`0_TX'CQP,]P +MGP"X_U@8``C/<)\`N/]8&$`(SW"``(@7U@PO^R/9$@U/_]'`X'[@>`':`-G/ +M<*``M`]!`+9C!A$``0E@E\``'#'/X!% +M>3^@@.6Z`P(``,#IN-;RSW6``!0N`(6*(0@`Y)#/=J``Q"<3'EB0SW&``.2R +M/X$Z=X8A_".%"5X$02D!(<.YSW*``!Q4-GH@@D!Y"'49%@"6!.@"V!`>&)#/ +M<```_W\3'AB0&]@6'AB0`]G/<*``U`LQH.!XX'C@>.!XX'C@>.!XX'C@>.!X +MX'C@>.!XX'C@>.!X,:#/<(``6'T9@(#@8`["`)KE&@,"`,]P@`!`S&8+H`(` +MW0L#```6#&__*G`:<`"%Z@LO_RIQ?@PO_PAWB.?,)^*5RB7!$ROR&P@0(.X* +M(`"!P`HE`)`<]"8,[_\!P!CP`]G/<*``U`LQH.!XX'C@>.!XX'C@>.!XX'C@ +M>.!XX'C@>.!XX'C@>.!X,:``W0\/D1;/<(``0,SJ"H`"@.6.`@(`H@P``<]P +M@`#DLA^`$0B>`P'9SW"``'`&(*`)\`\(W@,!V<]P@`!T!B"@$18`E@#=0<`Q +M")\`Z@FO^H'`"B4`D!#T!!0%,!T-GP`*(<`/ZW(*V(RXBB/'"B4#+_6*)(,/ +M@.4J`@(`!-@3'AB0&]@6'AB0SW"``%A]&8"`X$P-P@`+`@``X+C!\L]V@`#D +MLA*&AB`Z`(P@!(((#`4!SW&@``PD/($7AB)X9+@0>(H>!!!$(@!3%P@1`A^& +M#PA?!%$E0-$!V`7T`-@#\#H+3_^<'@`0+NCB#0__"B4`D-?T#!*@6`1#4V$H/H`#)<@?HN@K``POP1@\/_ZSPSW&``)`\'H$!X!ZA`=_/ +M<(``W""T%@$0X*@&@0'@!J$?AO.X)`S"^@^&@.`P"\+Z'X81")X#`=G/<(`` +M<`8@H`GP#PC>`P'9SW"``'0&(*``V,]QH`#('`>A,-@*H4H*[_\!P!^&)P@> +M!A#8#!H<,,]P@`!`S#8)@`(-R``@@0^``!"R'X;@J;BX'Z8`EH8@_`",(`*` +M&O3"#4_ZF.@#V<]PH`#4"S&@X'C@>.!XX'C@>.!XX'C@>.!XX'C@>.!XX'C@ +M>.!XX'@QH`"6R@VO_#260_!!P!7?"PC?`.EU'_`(V,]VH`#$)Q,>&)!J",__ +M"'4K"!`%`M@\'@"0(18!EL]P@`!TER&@$18`EM,(GX#>#V_Z@<`*)0"0X?,_ +M#5$5SW"@`)`C'H`$%`0P42"`@,HAP0_*(L$'RB!A`L\@(0/*(X$/``#D!`0! +M(?7*)2$`!@^O_XAP"'6I<"T%[_.BP.!X\<#2#,_SH<$(=@#80,``IGH/;_J+ +M<`HE`)"0],]PH``$)2*``(8$(8$/_P!?_P4A`@!`IE,A@@!3((,`97J["M$! +MSW"``.2R'X"#"IY3S0B?!@0@OH\`'@``"?0`A@[PSW```*,)^@B/^OD*G\`` +MA@D*'D"%N`"FSW*``.2R/X(/"5X&B+B+N(ZX`*9&\"L)W@9/(`$"B;F-N8NY +MCKD@IAZ"!""`#P(```!2($`$*K@E>`"F,O#\N<4@@@\````%X_6%(!P``*8H +M\/6X`(8F\H8@'`"%(!@``*8'\,]P``#6"7X(C_KY"A_!`(8M"MY`AK@`IA+P +M4R$#`%,@`@`%([Z`RB7A%0KRAB%_#X8@?P\%(3Z`RB6A%,]P@`!PLPR(Q+A` +M*`$&`(8E>%$@@,0`IOP.H@/*("((J7`%!._SH<#QP.'%`-T%V`NX8@LO^ZEQ +M%@S/],]P@`#DLA^`%0C?`L4-%1'!"%]%GP@?10'E3O``V9RYSW"@`-`;,*`! +MV<]PI`"80#R@!=@'\#X)``#6#J`#!=@!V,X.@`-C#141!""^SS`````!Y14<)GD,S"!]'V0C>Q=4)GL//<*H```0!@(8@/PL`W;L(T8`3 +M\.X(``#/<8``F#L.@0'@#J$)\`#9G+G/<*``T!LPH-((````V<]PI`"80#R@ +M$/``W0T+'T!6#J`#`=BH\;((``#/<8``F#L.@0'@#J$A`\_SX<4,S$0@/HI( +M\E<(W@"`V`P:'#`-S,]Q@``<.\]RH/Y8`<]SGP"X_P#=$0C>`AV!`>`=H4AP +M!_`5@0'@%:%`(@`-%J,-S`\(WP#/<*``+""OH`W,AB""`@T:'#`>\#T(7@&* +M(`0`#!H<,,]Q@``<.Q2!`>`4H0W,`-E&((`"#1H<,,]PH``L("^@SW&@_K@! +MSW"?`+C_-J#@?\'%\<`."N_S`]@!WL]UH`"T#]REX@[/\R((``#/'`U@G/\\]QH`#\1`6!`-Z\N`6ASW6@`-`/ +MU:4#WZ(.[_/I`SW"``(@H!H`- +M"0$`N@_/_P3PG@_/_]'`X'[AQ>'&`-D'V`#:M&FT?<=U@`"`H55]P)6,)@*= +M`-N%]HPFA9+#]O_>P+7!G0L.4Q^,)C^10O9AM0'B3WK/"A*#8;@!X<4(=8`O +M><'&X'_!Q?'`,@G/\P#=SW"``+B6.@EO^K2H$N@(WH#ES"6BD,PE(I',)6*1 +M7`YB_LH@0@-AOND.=9`!Y1WPBB0!<<]Q@`"(L:@@@`$$&5`#X'@`V4HD`'+/ +MG,(=P0B +M@R\`!@``@.,!V\![!"*-+T````#7=4````!*)$``PB0"`0#>SW6@`+0/W*7/ +M<(``D`8$B(KO+R$'!"\B1P3"":_[`]@#V.H)K_MJ<87O?@L``@/PL@L``L]P +M@`"(*`2`(PB>`,]P@`"4.0"`B^@YV/H.+_J+N`L(40`>#<__"O``V9ZYSW"@ +M`/Q$(:#@>,&@W*55!X_S\<`*#X_SH\$(=L]P@``\(O`@@P.*)PL6+9/]8SQZ +M*'"&(?$/PKI'N21ZAB#^`T2X'PF```HAP`_KM=Y.&(_X'1;MHK13J +MSW*``)PY%2(#``"+-7H"K>&+XZWBB^2M8XMEK2.*Y@Y@`":MBW"I<2X/K_,, +MV@#``<'F"._T`L*+<*EQ&@^O\PS:`,`!P3H([_0"PL]Q@``L!P"A5@U@`\EP +MH0:O\Z/`\<`V#J_S5]C/=8``/"(CA<]R@``P!W>1`*(+"QX`7]@`H@L+G@"% +MN`"B"PM>`(>X`***)@L6RV'980#:@./*(($`SW*E`.@/!J(`B<]QH`"D,(#@ +M`8'/(.(`T"#A``&AC@R/]P.%SW&@`,@<3X`"X$BA!@O@`,A@`X7:#6_]#H`A +M!H_SX<7/<(``/"(#@"F`1"&#@`#:)/2)"A4$`"*-#X``?"$`C:"X`*V`%8`0 +MH+B`'0(00!6`$*"X0!T"$!"-H+@0K9`5@!"@N)`=`A!0%8`0H+A0'0(0`>+@ +M\44*%00`(HT/@`!\(0"-@+@`K8`5@!"`N(`=`A!`%8`0@+A`'0(0$(V`N!"M +MD!6`$("XD!T"$%`5@!"`N%`=`A`!XN#Q(PF>`<]R@`!\(0B*@+@(JH@2@`"` +MN(@:`@!($H``@+@0\)+KSW*``'PA"(J@N`BJB!*``*"XB!H"`$@2@`"@N$@: +M`@``V#\)'@!*)`!TX'BH(``&*PB>```@@P^``'PA(!.!`("Y(!M"`*`3@0"` +MN:`;0@!@$X$`@+E@&T(``>`<\$HD`'3@>*@@``8K")X``""##X``?"$@$X(` +MH+H@&X(`H!."`*"ZH!N"`&`3@@"@NF`;@@`!X.!_P<7QP%(,C_//=H``/"(: +M<`L(40``A@+P`8;$$``&%28-%$P@`*`!WR6X4R`%`""%P']`(0`&Q!$!!AL) +M7P$*(<`/ZW*!V(VXBB.-#RD`[_0*)``$BB(+#5E@`!8#0%A@8*``%@!``:$` +M%H!`"*D`%H!`":G/<(``B"@%@!$(40!`A0`6`$$/L@3P`!8`00`6@$`*J0`6 +M@$`+J0`6@$`,J0`6@$``%@!!![$`%@!!"+$`%@!`P@M/_0"%R!``!H8@?XY! +M]/`FP!/($``&AB!_CCOT"G"^#*_X`=G2"J_\+R`'!!8,8`,*<,]P@`"$)2R0 +M'I8-"0``H@F/^4L(`00`A<00`08*<"6YP+EV#V_U`-J``B>@+R`4@@`\` +M```\"QH8,'8-@`")Z`O(!2"`#P```-0+&A@P"\B0N`L:&#"6"<_S]@M/^'$# +MC_/@>/'`Y@JO\P#9"B0`H*'!RB%A`!3RSW"``(@H$!`%`!T-GP`*(<`/ZW)^ +MV(VXBB,(#?4&K_0*)``%SW6``#PB%24.%0"&%252$"00%0``$@$@(!`6`"@0 +M%P%!+4\A*8$:$!@!P+\EN5,A$P#N#.`"#=F*((D`BG$^#2_\Z7)B#"_^BG`` +MA@F`);A3(!``BG"V"Z_X`-E,)`"@1`G!_WD($"`-[PHAP`_K`"P5@!"$X`/8`O0%V#IP(0L1 +M($8)C_ON"(_[SW"``+"R`XB`X#P/(0#*("$`+!6`$(3@RB"!#P``@``X#J'[ +MRB%A`$HC`"!+\`8(K_^*<'H+;_@`V"P5@!"$X,H@@0\``(``$`ZA^\HA(0`A +M#!$@Z@B/^^((C_N.#T_WZ@D/^H#@+`B"]P#8'_`B"(_WU@D/^H3H3@F``Q;P +MSW"``(0E"(B)X,P@XH'P]<]P@``<2@"`!-F]VA[;0,"+<)X.8``8NP'8>G`` +MA@B`#P@>`"P5@!"$X`38`O0&V#IP`(8H@!20!"&/CP`&```%?P?R]KG")Z(0 +MP">A$"IPBG$V"._UZ7*`X,H@0@0D".+URB'"`PT+$"`."D_XG_`^"0_Z!-G/ +M<(``B"@V"6_])*`@AL@1``:&('^.0?1>"*_\BG"B"6`#BG``$@`@R!``!H8@ +M?XY`],]P@`"$)2R0'I4/"0``(@]/^6T(`06*<`IQ_@QO]0':?]D1N<]PH`"P +M'S2@9@H/^OH*@`"(Z`O(!2"`#P```#P+&A@PZ@J``(CH"\@%((`/````U`L: +M&#`+R)"X"QH8,`H/C_,,\"090`4@AB`9@`4@AB@9Q`4@AAH9!`:*"`_Z">@` +MV`L,$2`""(`#`_#F#T`#`=W6"6`!J7#/<(``J"`R"6`!H*@I#%$@SW"``(0E +M"(B)X,P@XH$#]!$($2`1"!$"4@@/^@3H0@[/](X(S_G^"$_X!,J0X,P@@H\` +M`+,`#O(*(<`/ZW(!$@0VDMB-N(HCC085!*_T"B4`!5()(`$`V`T`K_.AP/'` +MW@]/\\]U@`#$!@"%2B``((#@8`B"`<]P@`#T%@.``-X0Z,]RGP"X_QVBSW&` +M`,`@!($!X+.XM;BXN`2A%J(,S'L('@#/A0P2`3=C"%Y%!LB&(/&/+?3/=8``)#J(%0`6!"&^CP```%`! +MX(@=&!`$\@38#!H<,%H,[_X`WL]PH`#\1"6`O+DEH&L5`!97"(6/``"T`,]P +M@`#`(`"`#PC>`L]PGP"X_]V@2B!`(`S,00@?`8,(GP&&(/^%P?)1(P#`K/)1 +M($#%J/0,S,]U@``<.U$@P(#E\H#8#!H<,`W,Z[B>\AV%`>`=I4H@`"";\`W, +M4R!`@!;R!,@#$@$V`QH8,+(*(`($&E@PSW"@`/Q$)8#/=:``R!^\N26@`-X' +M\,]UH`#('P#>&?#/<(``A`8`@!7H;_!^"B`"`-[/<*``_$0E@+RY):#/<(`` +MA`8`@,]UH`#('[\($0`&R`0@OH\#@.A3B*(`0`#J4^"H``+]B5N!(=&)#/<`$`P/P5'1B0C@_` +M`<]Q@``D.`"!A^#2\L]PH``X+@6`!""`#\````#7<,````#!\O78!;C/`5I<]P@`!PO1&(42`` +M@$@,X@'*(&(`!>\KPC>`0W,!""$#P```!AG#(`/````""X/ +M#_@-S'T(W@#/<:``+"`%@2:!"N!5"00``Q(!-@+8#!H<,%#8<@LO_I@1`0`N +M"0`"SW"@`/Q$)8#/=:``R!^\N26@1_&*(`0`#!H<,!2%`>`4I4GO&X4!X!NE +MQ?%F"V_Y"G`3"!\`SW6@`,@?!-@&&A@P1_$(V)NX!AH8,"3P`\B@$```\+C) +M<#/RD@C/^`#8EK@M\#D('P+/<:``R!];"%\"!""^CP```%`)\A,+'D"*(`0` +M#J$$V`8:&#`-S$<(WP//=:``R!\=\4H(;_R*(`0`6@XO^,EU`\B@$```\+BI +M<`7R.@C/^`#8E;B^"@`#!-@&&A@PYO$F"._X`=@`V)"X]?%`$0(&SW&``%RS +M+Y&U"D2`K[@-&APPU/$`V`D(40`'V`"ASW"``,0&`("`X(P+0@'/<8``%#P, +M@4V!""(```VASW"``.`@$(!/@6"`#H$">P#*""+"`$^A&0@1`@/9SW"@`$`M +M,*!C!.__`!J",P'@6P3O_P`:`C#@>.!^X'C@?N!XX'[@>/'`X<44N"5XSW6@ +M`#@N!J4%\!8/[_F*(%P(!H7W"-Z'+0-/\_'`L@I/\\]QH/Z<`P#?SW"?`+C_ +M-J#/=J``P"^E'MB3#]T(O07PW@[O^8H@G`*C%@"6I'B,(!"`]_,4'MB3!O#" +M#N_YBB#B:*(,N)VXG[@E>`:BX'[QP"X*3_,(=G(/[_\H=@:J#@?\'% +M\<#J"6_S`-G/<*``#"18@,]U@`#DLJUP02J&!X8@]P^8%8,0*;AV>(H=!!``E88@_XPH]`'8':(F\$X5@!"BZ(H5`!%,I62X$'B*'000!-E/'4(0 +M&P[1$"L7`99DN!!XBAT$$`S8+:5/'0(0S@RO^8AP"/`%(T,!01_8D!^%L[@? +MI14!3_,0VL]QH`#('$FA`=O/<:``\!=JH:00`@!-"MX"`MI=H<]S@``(O42# +M1J%#@T:A0H-&H4J%P$``!'.!3(,"`!/1`(P`(!/!`(P`,0(!3H4QH0()3 +MH?@0`H)3H?P0`(`3H0_P7)"&(O^,`_1]H4B`1J%'@$:A1H!&H06`!J'@?N'% +M+X#/C@`MG/<*``R!PGH.!_P<7Q +MP.(.3_D^"H_YT<#@?N!XX'[@>/'`C@\/\\]U@`#DLA>%SW:``-#'"P@0!E@5 +M@!`$Z!J%6X4$\!R%787/<8``7#D@@1,)'P`RA00A@0\````0)7@E>L]Q_O__ +M/R1X`:8`W^*F1'DMI@[84@^O^0ZF!^C/<8``="5V#&```=C/<8``4")J#&`` +M`-@7A0\($04!V`&N,1X"$`3PX:XQ'L(3:0#*($(C"_3/<(``E"``D('@`=C`>$`H$`/)<(8@_`",(`*%(_3/<(`` +MY+*8$(``Y[C*("(`"O0"N!9XSW&``$B_`&$MN,"XSW&``%0'((%1(8"`SW&` +M`%RS%'D$\B#:K9$*\)C:JY$&\,]P@``@L[.0#MH!ET`E`141"0,`HGA((``` +M$'@#\`#86G``V"IQJ7,F#R`#F'`*(0"@!/0V"P`#.G!,(0"@`-A(]`4@@",- +M<0"Q#7$`&80$(X<-<""@*)<-<""PC"8"E17RC"8#D1WRC"8#E23R"B'`#^MR +M$]B,N,]S``"4"HHD@P\9`F_TN'//<(``Y+*T$`$`#X$!X`^AQ@D@`.EP$O#/ +M<(``Y+*T$`$`#H$!X`ZA"O#/<(``Y+*T$`$`#8$!X`VASW&@`/0'`-@$H0'8 +M&G#/<:``R!_X$0(`0G4")8`02"```%^!$'@]"(0`0X?/<(``=)="H*#8#Z$` +MV!^ASW"``.2R')!BN$)P'Z$"V!49&(`-"1`@42!`QB#8`_*`V`ZAC"8#E0;T +MSW"``.2R')`)\(PF`Y$(],]P@`!R#"_[`-T5"%$`SW"``)0@`)`!W8'@P'T,O<]P@`"\R`2`SW*``+"7!""` +M#P```!!%($$#0,$@P,.X''CT(@,`SW"@`"P@#X!PNQ4(Y```WO!X<'MB"6`# +M%-H)"!X&R7`X\`/8SW&@`/0'!:&%)0,9#7"@L`UPP+"*(O\/#7!`H,]R``#_ +M_PUP0+`#R,]S@`!(O\]R@``\(A"(`K@6>`!C+;C`N/`B``"@@`UPH*`#R!"( +M`K@6>`!C+;C`N/`B``!"D`UP0+#$H;((``,!V)4$+_.AP.!X\<`>#`_S&G#/ +M<8``Z)<`B17HP8&B@<]P@`!4!P(1$0'@@,]Q@``4/`N!-+\!X`NA,_!&"._Y +MBB`+",]QH`#$)Q$1`(8`W^\(GH%D$0*&9!G8@P+8$QD8@"\H@0!.(($'$NK/ +M<(``2(LV>,"`H8#/<(``R(OT(%$`SW"``.B+\"!/``OPSW&``!0\"H'I=>EV +M.G\`UQ`*%*)`!TJ"#``D0F@1`/N5,F`!`E +M>`UQ`*$BODHD`'2H(``#1"6!$`^Y4R4`$"5X#7$`H2*]10,/\_'`[@H/\PAV +M*'4H<$AQ7@@@`&AR@>#*(($#$`@A`,HA00,Y`P_SX'@BN0;P[')@H@3@8;GY +M";6`8(#/<*``U`MMH`/9SW"@`$0=-:#@?N!X02F!@`GR+R1)<*@@P`$$$`($ +M['%`H>!^\<"&"@_SH<$(=4AVSW"@`*PO&8`$((`/<````-=P(`````'8P'@O +M)@?P`-K*(($`+?(+S``<1#!/(,$#`AQ$,`'@$'@$((`/``#_OX^X"QH<,,]P +MH`#4"SB`0B$!"(#ARB&,`$`E`!(0<:@/Q0('Y00EC1\``/S_Q7V=O9^]['"@ +MH`#!['`@H`'8:0(O\Z'`\<#AQ:()+_L`W8'@RB!"`PGTSW"``)0@`)"!X`'8 +MP'@,N(4@`P$#VL]QH`#T!T6A#7(`L@/(`-M=D`UP0+`#R%&`#7!`H`/(2!`" +M`0UP0+!DH1D"#_/@>/'`G@D/\\]U@`#VXX.[_\8N^`5`!`!YN<.!)``V-4!+_/@'0`0X'CQP%X)#_,A@`HF`)`0 +MB<.XRB'!#\HBP0?*(*$&RB.!#P``J@#/("$#.?*`X`!ASW&``!`'+;C`N`RI(X8@D88A_`", +M(0*`#/3/<8``/"+P(0$`OQ$`!H&XOQD8``&&HH`%[0&%`^@`A8SH"B'`#^MR +M'-B,N+G;2B1``-D$+_2X/'`H\$`V6#!`1P",`,<0C`"'$(P`=G/<*``L!\YH,]Q@`#@ +M(`R!`("$VD+`"($`@`S9'MM!P(MP0@WO_QB[H\#1P.!^SW"``)26*(#/<)\` +MN/\`VC:@"-GL<""@`]G/<*``%`0EH`'(['$`H<]PH`#4"TV@X'[@>/'`X<6E +MP0AR`-O/<*``+""P@$#!!MA!P$+#0\-$PP'8'MF8<[AS`"6''P```'T:#V`` +MV',M`"_SI<#@>`C(A[@(&A@P"X"QH8,`S(#!H8 +M,.!^X'C/<8``Z'T`@8&XX'\`H>!XSW&``.A]X'\#L>!XSW*```0Z%7K@?R"B +M\<"8<`HAP`_K<@HEP`?/<```HAEE`R_T5MO@>,]R@`#@.15ZX'\@HO'`F'`* +M(<`/ZW(*)<`'SW```*,9/0,O]%[;X'C/N!_(*+QP)AP"B'`#^MR +M"B7`!\]P``"D&14#+_1FV^!X\<"D$`$`#0E?!OX+3_D'\"#9SW"@`,@<*:`# +MV<]PH``0%"6@T<#@?N'%`[@U>,]Q@`#(1@)A2B0`=`#9J"#``A8B0`"A@&"` +M*=@2N`'A=7B@H.!_P<7@>,]P@`"0!@"`H<$F@)X1``:&N)X9&`#@?Z'`X'C@ +M?N!XSW&``!`'X'\$H>!X\<#AQ<]R@``\(B.".(D?"1$!"B'`#^MRBB",#HHC +M!@)*)```90(O]+AS((+$$0$&`<]R@`#L/R""0B$!@,HA8@"OZ8_H"B'` +M#^MRBB#,#HHCA@-*)```,0(O]`HE``$F@B.!8;B@@<]Q@`#@("2!(('5N3UE +MSW&``+#')8$%*3X`)W5(<#(((`%")8$2SW"```A``"6!'P``B!,>"``!10;/ +M\N!XX'\`V.!_`-@@@`>YX'\@H/'`N@W/\L]U@`!D%S_+QP'8-S_+/ +M=J4`Z`\FAJ>&SW"``%P'`-\DH*6@*@\O_0W8!KB!N,]QH`#L)P:AYJ9%))*PH!`,]P@`"^!@"008D?"@$`SW"` +M`,`&`(@FB0\)`0#/<(``X"T`@`+P`-C@?N!XX<7AQ@#=,PG0!PL)TP<+"1,` +M`-@3\!D)\P*@@@`$/)8T38;X)"$X`I7@#\*9X`*(!V,'&X'_! +MQ?'`H<$`VD#"N@_O_XMR`,"AP-'`X'[@>/'`H@SO\BC8,@_/^<]U@`#\+4"% +M"'8$((0/``#P_P#88'I!+`$!0(4!V$0F!!-@>D$L@0!`A0+88'I3)D$0_@[O +M^0#80(4(=T$H`0(#V&!ZP+E`A4$O01($V&!ZP+G/<0``A+_/<(``^"T@H,]P +M`0#0H9D$[_(`I?'`I@O/_$H/C_S/<0``K+_/<(```"YZ"V_\(*#/<0$`^*'/ +M<(``!"X@H-'`X'[@>/'`_@OO\E#8B@[/^<]U@``,+D"%"'8`V&!Z4R9!$$"% +M`=C)<6!ZAB']#\]Q``#0O\]P@``(+B"@SW`!`""B+03O\@"E\<#AQ=X/K_H' +MV!X,K_P(=:X/S_]*#<_\]@ZO^JEP$03/\N!XX'[@>.!^X'@%````\<"&"^_R +M"',(=H8C_@-$NPAWAB?Q'T>_1""!`SQYSW6``'"]+:T$((0/````#$(L@`(3 +MK00FA!\````P0BP``Q2M!":$'P```$!3(;Z`0BR``[$=`A`.]`HAP`_K#,("*`S"`B@0;T4VDE>D^M3JV` +MX\P@(H$%\E-K97I.K8#GS"`B@03R$V_E>`^M$VDE>!"M#HT,K9(*8`$`V#4# +M[_+?M>!XX'[@>.!^X'C@?N!XX'[@>/'`M@KO\@#;H<$$N,]R@`#0QQ1X'6*0 +MWAIB`8(8OLBEBB8$$L]R_O__/\FE!'J`X4#"RB7!``OR"($$((`/````,$(@ +M!8#*)6(`30T0`,]P@`!XL0`0!`#(@00DA@\`!P``!":.'P```#`LOF&^02X& +M!D`F@!,/(@(`0,*%#H\#"B'`#^MR/MB,N(HCB@Y%!N_SN';/<(``L+('B('@ +MSR*A`R_RSW.``'BQSW:``.2RFA:!$`.+"R$`@"'R3!:!$`#;4R%.`$0A#P,/ +M(X,#0K\`W@\FSA.&(?\#!"8.D`#?1+D$>P\G3Q#D>,HF`1"`X\HC@0,.NV5Z +M`_`!@P5Z0,+/<(``>+$@@(MRAB'^`R2Y0"F#`R""!"&.#P$``,`+)L"0%O+7 +M=@```$#,)H*?````@,PF@I\!````!/0!@`/P`H"NN:^YL+DE>`"B`!0$,`0D +M@8\!``#`"O0*(<`/ZW)&V(RX;07O\XHCBPD(A2ZY0"D"!@0=`!%%>`BEBB`% +M!@FESW"``#BS!(B`X(H@!0[*(($/``#8`0FEJ7``V@'>3@FO__R +MH<#@>`HE`(#QP!'R)PV0`"D-T``*(<`/ZW**(,T.BB,$!04%[_.*)(,/M@L` +M`-'`X'YF"P``_?$:"P``^?'@>/'`Q@C/\@AUSW"``"PV"8")Z,]P@`"\-2F` +M#.#P($X``O`!WHH@_P\`I<]Q@``\(@"!Q!``!FD(7P'*"(_[`*6*"2_ZJ7"` +MX,HEXA&B]*((3_F&Z`"%C"#_CPWTSW"````N((!@>0'8@>`%W7QSW"``.P_ +M`(#/=X``5(A"(!"`.@]O^\H@8B``I0&'#PB%`\H-K_C)<0AVSW"``.`@!(#/ +M<8``L,<`@"6!26[5N`4IO@`G<&JX((5((```,'#*($8`1/<`I4H@0"#/<8`` +M'!AAD<]Q@`"\!T"!(8$\XP'=>F)DXA3A.F)0<,(E3A.S?5,E39`B\D$(42#/ +M<(``[#]."<``SW"```A`0@G``,]P@`"P0#H)P`#/<(``S$`N"<``(8<1":4# +MR7`6""```=D$\-((S_^9!Z_RJ7#@>/'`+@^O\KAQ@.#*(<$/RB+!!\H@@0\` +M`$L#RB/A#LHD(0`L`^'SRB4!`<]W@`!`0":'(X$@@<]R@`#@($2"0(+/=H`` +ML,=3(DT%-KH`(A``/65%AF&X!2H^`"=U`B5`$(P@%X=*]\]P@`!4B"&`!2F^ +M`"=U`"!0("$-$`!A#5``FPV0``HAP`_KD!,)```")8$?````#.EP!/#I<$(E`16Z",``SW"` +M`%Q``"6!'P``B!.J",``2_!N#@_YSW&``+!`$NC/<(``A"5,D,]P@``\(AZ0 +M$0H``"AP`B6!'P````P%\"AP0B6!%W((P`#/<(``S$#;\3(.#_G/<8``>$`1 +MZ,]P@`"$)4R0SW"``#PB'I`3"@``*'`")8$?````#`3P*'!")0$5,@C``,]P +M@`"40``E@1\``(@3(@C```IPR;C/<8``5(@#H0:&@;@A!J_R!J;QP.'%SW6` +M`"1`C@^@`*EPSW"```@V((#/<(``##8`@`DA`0#/<(``;+`(@`DA`@#/<*`` +M+"`P@*EPS@^@`%EA\06/\O'`SW"``%2(`8`/"%$`SW"``+PU!X#/<8``>$`@ +M@4(A`8#*(6(`D.G/<8``W+`L@8KISW&``,2P)@NO^".!"@[O_P+9T<#@?O'` +M,@V/\L]P@`#@(`2`H(#/<(``[#\`@$(@`(#*(&(`-KV'Z,]P@``(0.(.@`#/ +M<(``Q+`AB,]P@`!`0,]V@`!4B(KI((!"(0&`RB%B``3I((:MZ;8.@`#/<(`` +M7$"N#H```8:R"J_X"'$AA@]X'0D%``HAP`_K$`@@4(A`8#*(6(`!ND=92.&R;T+#4`06@WO_P#9]02/\N!X\-'%X<8CZF-J(KO!N@+P`-HU"A4!,R:" +M<(``@$A`)XUR5'T@?<"`!!F0`P3@!!`"!`09D``$$`($!!F0``00`@0$&9`` +M0B-#@./UP<;@?\'%\<"N"J_R4R%"`$XB#0'/G'ZW/<8``_)8$\,]Q@``C/<`$`$*$!IP`6`""Y$``&+0@>`$&&&M@`L@*F`)&'N`"Q +M`-@+L0&"K;@!HA$,$"#/<(``P#<$@#,:`@`I"A`@(88!@9BX`:$#@9^X`Z'/ +M<8``1`<`%@`@`!D$!$"``8!!H0*A6@]O_\EPU0"/\N!X\<"&"(_RNG!Z`AB3",`H`2XAB#^ +M`P4@4`#*(

&?PSW:` +M`,1!`88`W^EQ,@OO\SC:`(8/'`X<7/=8``-#T`C8P@PX\/],]R +M@`#0/P:"`X`@@,=Q#P``H&X)H`!(/'`!@Y/\@AW&G$ZQP +MX*`#A@B`#0@>``*%@;@"I<]P@`#`!@"(A.@"A8.X`J7/<*``+"`0@,]S@`"L +M.W(=&!!*),!P`-BH(,`%SW&``)@&((F`X0S:RB(A`$0HO@//<8``8,TG.,(X*`((:X]\]RH`#4"RVB +M)'@`IF<=V!-H'1@1`-AA!6_RW!T`$.!X70;O_P#8X'CQP,]R@`#$!@*")8@! +MV`?I"-D>#6_Y*Z('\,]Q@`"T*$();_,`H='`X'[@>/'`R@QO\MAP.@@@``#= +MR6@K#A(0^'"I=S(F@`,5"!(,$0B3#H8.#_8R;SAX!7T!YT(G1P#E#W6`8;[Y +M!&_RJ7`(<@/P`>`@B/[IX']">.!X\!XP:7%I,]QJJJ[N\]PGP"X_S:@-J`VH#:@SW&@`,@[#H&( +MN`ZA:2!``/[QX'CQP,]P@``\(@.`&(@="!$!"B'`#^MRBB`,#HHCA0I*)``` +M)0"O\[AS(@FO]`/8SW"``&RP`!`$`!D,$0`*(<`/ZW**($P.BB,%#/T';_.X +M<\]P@`"4(`"0@>`!V,!X#+@I"($/````$,]PH``L(!"`SW&``"PV`J$"V`.A +MSW$!`!@EP@MO_P'8T<#@?O'`F@M/\L]U@`#4B@ +M/!T`%'((K_0"V*D#3_+QP$H+3_*"#6``"'7/<:``R!]%A0SH;A$.!@*`9(7$ +M>D5[;AG8`"*%`*$+\&X1``9$>&X9&``(#@`=G`><]P +M@``P/>!_(*#QP/(*3_+/<(``^"T@@*+!8'D$V(#@J@(!`,]Q@`#8(`"!`>`` +MH1<(40`!V<]PH`#('#&@4@X@`BAPY@XO^@78SW:``/@X#J;/<8``V"``@0'@ +M`*$5"%$``=G/<*``R!PQH"8.(`(H<`/8>@\O\\EQ!-AR#R_S(FX%V&H/+_,D +M;@O88@\O\R9N#]A:#R_S0"8!$C;83@\O\T`F@1(WV$8/+_-`)@$3.-@Z#R_S +M0":!$\]WIP`42`B'SW&G`)A'!*8-A\]RJP"@_P6F#H?/=:``["<&IAR!!Z87 +MAPBF%H<)IAB""Z89@@RF&H(-IL]P!0#&`P:EQMB0N`:ESW`L``(!!J7/<%H` +M0@$&I8H@BP`&I<]P0`"'#0:ESW#1`,(-!J7/<,``!PX&I<]P@`#8(`"`SW*` +M`-@@0B!`@`"B!O3/F`<$QH,]P@`#8(`"``>#/ +M<8``V"``H14(40#/<:``R!P!V!&AB@P``@&6$+B%((0`!J4"EA"XA2"%``:E +M`Y80N(4@BP`&I026$+B%((\`!J4%EA"X!2"`#P``@@T&I0:6$+@%((`/``#" +M#0:E!Y80N`4@@`\```(.!J7/<(``V"``@,]Q@`#8($(@0(``H0?TSW&@`,@< +M`-@1H02&*X8(IP6&#:<&A@ZG"(87IPF&%J?/<*L`H/\XH"R&.:`MACJ@L@LO +M^@Z&SW"``-@@SW&``-@@`(!"($"``*$'],]QH`#('`#8$:&=`&_RHL#QP#(( +M3_)^"```SW:``*!$<@XO^`"&"'4`AAD-`!#R#Z_[J7`F""_\H*;J#:_T$=@J +M"8_WSW"@`"P@,(#/<(``C`==`&_R(*#QP+H/[_^AP<]P@``X/0"`!-EBVA[; +M0,"+<.8,+_\8NZ'`T<#@?N!X\<#>#&_T%M@`V-'`X'[@>/'`J@\O\@?8T@L/ +M^L]VH`"T#_R&&G``V!RF.@LO_.]_SW6``#@]H@XO_`"E0(7/<8``]$$`H<]Q +M@`"8.TJA&@SO_`NA_*;""B_Z"G#/<(``J"``B#<(40!`A8H@1`3/=8``F"`C +MA1IB.&`0<@'9PB%%``38!>EJ#,_U`(4$\%(,S_4"A3(/X`$#I84'#_+@>/'` +MSW"``#`]`("038$.C/<(``Z"U@@,]Q`0!P +MN0O88'L$VBX-;_06V-'`X'[/<8``/"(`@<00``8/"%\!`8'$$``&%0A>`:H, +MK_03V*(,K_01V.SQZO'@>/'`SW"``"0<`(#/<8``C`<;>&H++_8@@0CH`=G/ +M<(``J"!V#^__(*C1P.!^\<"&#@_R"'=]V`VXSW&``+#'Q8$."&_RR7&,(`*` +MSW&``"@<`-V']QUXC"`"@`'E?/<`*$(#!2J^`\]R@``D'!:X`*'/<8``-#T` +M&D`.A.__V`"I`(F,(,./Z`Z!_Y$&#_+@>/'`#@X/\CIP>G%(=VAV"B0`(0#: +MSW&K`*#_6:$'V!JA6*&6"2`"`=@9V<]PIP"81SJ@@@UO_![8SW*G`!1('8*^ +M@FP2$`!P$A(``*>@IO>XQ2""#P#_``#3(.$%][W%)8(?`/\``-,EX14&":_W +MBB$0``AVJ7#Z"*_WBB$0``AU0"@`(NH(K_>*(0@`"'=`*@`BW@BO]XHA"`#1 +M>1GA+'DO<;%Z&>),>B]R`!F`(P\/8A``&T`C`-@$\/\(@X`!V*$%+_(`'`(@ +M\#0_R"'4H=N8((`(*V`'8SW&G`)A'&J'6""`""MC/<*8`G#]D$`0`420` +M@,HAP0_*(L$'RB"!#P``OQG*(X$/``"X`#`!8?/*)2$`SW"G`!1(+(`=@`"F +M][C%(((/`/\``-,@X05A!2_R`*7@>/'`Y@P/\L]R@``LEL2"C";#GSOR_]DD +MHL"@A"X(&0`AC7^``-"1!(T*($`N`=^2Z`*%SW&``#@I$@NO\R"!"''/<(`` +MX"`$@`"`D@U``(3H`=@<\,]P@`"X*"*-P*@AJ,]QH`"P'_FASW&``.`@,($@ +M@0#=(:!V#"_YZ7``((`O@`#PDZ"H`-C!!`_R\"L_\SW"``#PB +M`X`8B!\($0$*(<`/ZW**(`P/BB,&"THD``!-`&_SN'//<8``O#4)@0L(%0$! +MX`FASW&``+#'!H%&($`!!J'/<(``N`<@@!4)D0"*($8/I@ZO^@3:_@^O^@38 +MT<#@?O'`SW&``"PV"8$!X`FASW&``+#'!H&"N`:ASW"``+"R`XB`X.P(X?[* +M(*$`!@IO]`;8T<#@?N!X_]G/<(``J)$@J&\@0P!I`6_S`=F%`:_T$=C@>/'` +MSW"``#PB`X`8$(0`'0P1`0HAP`_K`!V,!X#+@1"($/ +M````$,H((```V"#P>@G/_,]P@`!`&0"`ENC/<8``L,<&@48@0`$&H<]P@`"X +M!R"`%0F1`(H@B`2N#:_Z!-H&#Z_Z!-C2#L_ZT<#@?O'`SW"``+!``(!"(`"` +MRB!B`(CHSW&``"PV"8$!X`FASW&``+#'!H&"N`:A"@EO]`;8SW"``-RP#(`8 +MZ,]P@`"D,@*`$NC/<(``E"``D('@`=C`>`RX%0B!#P```!"F""```-C1P.!^ +MSW"``+"R`XB%Z*8/K_X"V/;Q]O'@?N!X\<#/<(``/"(#@!B('0@1`0HAP`_K +M`AR`-@=!"_X$-G@>`AR`=@1!"_X(-G@ +M>`AR`M@%!"_X0-G@>`AQ&00O^`#8"'$1!"_X`=@(<0D$+_@"V,$!#_/QP,]P +M@``\(@.`&!"$`!T,$0$*(<`/ZW**((P/BB,)`R$%+_-*)0``SW"@`"P@,(#/ +M<(``+#X^"R``EB%!#\]Q@`"PQP:!1B!``0:ASW"``+@'((`5"9$`BB")!W(+ +MK_H$VLH,K_H$V)8,S_K1P.!^\<#/<8``L,<&@8*X!J'/<(``L+(#B(#@P`VA +M_LH@H0#:#B_T!MC1P.!^X'CQP/_9SW"``#0]/@FO_R"HR@G/_]'`X'[QP.'% +MSW6``+`H`!4%$(PEPX\?])#HSW"``+"5:!`$``HAP`_K#@_TD@M/\\]P@``H&3D`+_*@H.!X +M\<`."T_XF@M/^'X*3_C1P.!^X'CAQ<]RH`#('Z02`P#/<8``Q`8-@1!SPB,& +M`$3W8G@3>[^"#H&[8WA@#J$!V$H:&`#@?\'%SW*@`"P@9H+/<8``Q`8.@6)X +M#J$0@ND`[_8-H?'`5@_O\4HD0`#`@:"``=_1=<(D`@'1=:&!88#")\X3`=ZQ +M<\!^L7,!V\(CS@!,)`"`S"8BD,HC8@`*](7K@.;,)R*0`_("VP+P`-L4ZR$+ +M4``Y"Y$`H(#`@0&`(8$")8V3H*(#($```:(0\`#8`*(!H@SPH('`@"&!`8`" +M)8V3H*(#(0$`(:(U!^_Q:'#@>/'`X<4F@$"`0B("@,HB8@"`XLHAP@_*(L(' +MRB""#P``-A'*(X(/``!W`,HD(@"T`B+SRB4"`6"!%0M``$*`HH-"?0T-4Q!@ +M@_4+08!!@P&C8*!!H`"B1("F@$`E`Q87"EX`1H4&ZJ*"0H!"?0<-4A``HT2` +MIH!`)0,7%PK>`$>%!NJB@D*`0GT'#5(0`*-!@`L)@0#N"J__!H"E!L_QX'CQ +MP"8.S_$(=@"`0B`!@,HA8@``V";I)H9!A@'?,'(@AD&&0:$@H@"FSW"MW@(` +M`::FAL!_!H41#@$0J7!6""```MD&I::&!X4/#@$0J7!&""``"-D'I07OB@JO +M_P:&`=@M!L_Q((`0<EPX'A`@!4*``!D@@LC0(`%]$""]PH!@`#:X'](<.!XSW*@`,@?]!(` +M`+S;&+L$((`/__\`\/0:```+R&5X"QH8,!4:V(#//'`&@W/\<]UH`#0&].%$0Z>%L]P@``804H)```/#MX6SW"``#A!/@D``!$. +M'A?/<(``6$$N"0``#PY>%\]P@`!X02()```1#MX7SW"``/A`$@D``+S8&+@3 +MI2T%S_'@>/'`M@SO\0#;"'?/=J``R!^D%@`0SW6``.`@^&"D'@`0`=@3IBB% +M#(5`@0"``"+"@T"A+(4!(,```*$"V!.F*85-A0"!0((`(,"#`*$-A0$BP@!` +MH`38$Z8JA0Z%0($`@``BPH-`H2Z%`2#```"A"-@3IBN%#X5`@0"``"+"@T"A +M+X4!(,```*$$A0"`)@[O\^EQ)(4`H06%`(`:#N_SZ7$EA0"A!H4`@`H.[_/I +M<2:%`*$'A0"`_@WO\^EQ)X4`H0_8FK@.I@_8#+@0IL]P@`#X0*((C__/=8`` +M&$&6"*__J7"2"*__0"4`&(H(K_]6)0`2@@BO_U8E`!,E!,_QX'CQP+8+S_$( +M=R#P`(8AAB&@`*$`V`"FSW"MW@(``::FA@:%$0X!$*EP^@WO_P+9!J6FA@>% +M#PX!$*EPZ@WO_PC9!Z4CAF!YR7"N#>__Z7`*)@"0"/(#AR"``H8B>*\(4H`2 +M"*__Z7"Y`\_QX'@/V)JXSW&@`+`?%:$/V`RX%Z'@?O'`*@O/\<]R@`#DLC^" +M.G"JP0#8(0G>`L]Q@``\(B.!2!*#`,#=-(%D>88A_PXBN3I]!/`4W0+8BA(! +M`0)Y$H($X0X*[_8`VE()8``"($X#`]C/<:``R!\3H<]U@`#@(`B%`(!"P`R% +M`(!#P`F%`(!$P`V%`(!%P`2%X(`%A0`0$@!`$0`&'F;\$0``SW"``+B60(`A +M@`#8`"*"@P$@0`!`PD'`BW8?"5$@H@T/\X3!&G#)<"X+[_^&P@AV"!`!(0OP +M@L')O\(A(`%\&8+8`"&P`AR1L(M#I$0Z7`N#._S2'$(=TIP(@SO\P;!!L):<`3# +M!\`%P0`BPH`!($``1,(6\)7NZ7`N#._S2'$(=TIP)@SO\P;!!,-:<`;!!<`' +MP@(C0X!$PP,@@`!%P!L.4!#/<(``/"(#@!B(A.#,)B&0`-@"]`'8+R`'H$/T +MZ7"Z"^_S`]D(=DIPL@OO\P/9"'<`P`'!0"#`@$$A`0!`P`3`0<$%P4`@P(!! +M(0$`1,#R#R``1<$7"1$@!(7`H`B%`,$@H`R%`<$@H"<)D2`$A<"@"(4`P2"@ +M#(4!P2"@!87@H`F%!,$@H`V%!<$@H!<)42`%A>"@"84`P2"@#84!P2"@3"`` +MH`'9P'G/<(``$(LTJ'T![_&JP/'`)@G/\:7!"'8"BRAUF'!DP`"+`!(&`1$< +M`C!Y<`(2!P$$$@@!$!0`,>22!A(%`0`@R0,`D2\A2!('($`""@D@`!!X`""* +M`0&5+R*($@<@@`+V""``$'@`(,8!`I4O)H@!!R"``>(((``0>``@!P(#E2\G +MR`$'(,`!S@@@`!!X`"4%``25+R5(`0<@0`&Z""``$'@?9P65\'_G>*H((``0 +M>":5(7`0>`=Y/'H/N25Z4'H`(H$",'D`'$0P1Y4G>EQY#[I%>3!Y`"&"`5!Z +M7'D"'(0P#[I%>3!Y`"'"`5!Z7'D$'(0P#[I%>3!Y`"%"`5!Z7'D&'(0P#[I% +M>3!Y/V?P?_QY"!S$,P^_Y7DP>3A@:7'&N86Y"+D%(<$"(+80>""5"AP$,"=X +M''@(N`4@``$!M@#``:8!P`*F`L`#IE$`[_&EP`][2+@/>,]R@```5O0B``!` +M*`$"2+@%>?0BP``P>>!_)WC@>/'`SW*``.A`((*`X,]Q@`#H0""!`-B#X`HB`(#QP!?R +MX@_/_X#@RB'!#\HBP0?*(($/```S$T5X$'/*(*T`!?<0 +M/'`X<78<+AQN@_O_YAR"'7(<+(/[_^(<1!URB"M``KW +M$'4`V,H@1@&8#^;_RB$&`?T&C_$`V,]Q@`!LL`6A!(&@N`2AM03O\P/8X'@V +MN#:Y,'#6((4/``"``.!_(GC@>/'`5@Z/\0HG`)#/=H``L,?/=8``&$$/],]P +M@`#T5,EQB@HO_Q3:!@CO\JEP0"4`&!'P&0^1$#8)#_/)<6X*+_\4VD`E`!@, +M\,EP6@@@`079J7#:#X_RSW"``/A`S@^/\@26"K@%I@:&AB###P:FX@H@`.EP +M>@Z/\D4&C_'QP*'!"'/>#*_WBW""X`#8!O(`P!!S`=C"(`X`H<#1P.!^X'CQ +MP`#9SW"``%PM&@@@`""@SW"``!`^?@^/_]'`X'[@>`#9SW*``)`'(Z(DHB6B +M)J(GHB*BSW"``/1`(*#/<(``F$$@H#&R,++/<(``&#C@?R"@X'CQP,]Q@`!< +M+0"!E.@!V`"A`-G/<(``\!:V#^__(*#/<(``6"(0B(/@,`DA`,H@80'1P.!^ +M\<`N#8_Q-@TO^*3!@."$"0(`SW"``/`6`(#/<8``&#BF#N__(('/=H``D`

``)A!0,$@AT/"0L"+ +M<$'!$-FBVA[;M@FO_AB[`-AV"*_YBW'/<(``\!8@@,]P@``8.**FL;8@H+"V +MSW"``'`&H*"2">_S$]@`AQT(5`%>""```=A&#`_[SW&``)@['8$!X!VA!?!& +M""``!=BQ!*_QI,`5V`#:SW&@`,@?;QD8`.#8D+@0H0G8L!D``+09``!TV$(9 +M&```V)JX#Z&D&8``SW``#``9#J'@?L]R@``0/B:"(X%AN&"!SW&``/`6(('5 +MN7EASW.``+#'98,%*SX`)W''<0```!`I!J__2'#QP.'%SW6``)`'!X63Z,]P +M@`!<+0"`'PA1`.X*S_@7")`&SW"``.`@!(``@`6E`=@'I1T$C_'@>/'`X<7/ +M=8``D`<'A1GHSW"``%PM`(`K"%$`M@K/^",(D`;/<(``X"`$@`"`!J4*#>__ +M)84PE3A@$+4`V`>EV0./\>!XSW"``%PM`(`7"%$`SW"``.`@!(`@@,]P@`"0 +M!R.@X'[QP,]P@`!<+0"`)0A1`,]P@`#@(`2`SW*``)`'`(`$HK(,[_\C@C&2 +M.&`1LM'`X'[QP`(+K_&*(0@`"'7/<*``R!\PH`'901A8`%8)``#/=H``L,<# +MAB6&U;@P<,HAS0_*(LT'RB"-#P``-1'*(XT/``";`,HD+0#_R"'<:<(#ES"5BD$KTSW6``.`@"(4@AB"@#(4AAB"@`(4EAB"@!(4C +MAB"@H@H/^.4($`#/<(``A"4(B-D(T0$%A<"``(`$)HX?P/\``%,@404$A0"` +MA@RO\PIQU;A%A05^`MO`HL]RH`#('W.BR84"($&$8(9-A4"""@`$`$(IP`<' +M\"27"KD"(4$$&6$`V`(C0X`#(@$`8*8-A3OP=0V1$`27SW6``.`@(84*N`"A +MSW"``#PB`(#$$``&42!`@0F%(/+/<8``A"4HB3D)T0'/<:``R!\!VE.A*(4` +MVR"!3(4"(0&$0((@H`V%`R+"`$"@!(4`@.8+K_,*<26%`*$*\""'(*`-A2&' +M(*`CA@6%(*#Y`8_Q`-F6N<]PH`#0&S.@X'@#"YY%X'[/<(``&$$G@`;I`X!` +M@`*!0G@%\,]P_P___^!^SW&``#PB)($H@00AOH\`!@``H<$$]!,)'P`)\`0@ +MOH\````8`_(`V`+P`=C/<:8`I``7H>!_H<#QP#H)C_'/=:``M$<(=@;P=@TO +M^(H@B0QQ%0"6!""`#W````!!*#Z%]/6*(/\/;QT8D&L=&)`#V`^XSW&@`,@? +M$QD8@`6&61T8D`:&6AT8D`>&6QT8D`F&6!T8D`B&5QT8D$`1`0;/<8``^$$@ +M@00@@`\```"`$0D?`(#@!MC*(.$!`_``V,]Q@``\(B.!*('/&)!7%@"6O+B_N%<>&)!?%@"6O[A?'AB0 +M`-B>N%,>&)#@>`#84QX8D`R/8!X8D+H)C_S/<(``^"T@@&!Y!-@5Z$PA0*"8 +M#^'ZRB!!`\]W@`"<1`"/%PT`$,]P@`"`+S:`8'D`V``?`A2J#L_R0Q8`ED4@ +M``V?N$,>&)"7"1`@(PE0(&T)D"`*(<`/ZW**(%H*BB.-`DHD``"%`Z_R"B5` +M!,]P@``\(@.`$+V;O3(@@`\``-@"G[V`X`'8P'@/N*5X7QX8D`;PB@LO^(H@ +MB0QQ%@"6!""`#W````!!*#Z%]/6*(/\/;QX8D&L>&)`3\,]P@``\(@.`$+TR +M((`/``#8`I^]@.`!V,!X#[BE>%\>&)`&R(3@,`VA\\H@H00]!T_QX'CQP.(. +M3_$(=2AV<@BO\0&`H(40N4$M`!0X8&((K_')<1"YL'@X8%8(K_%`+H$2(0=O +M\2AP\<"N#F_Q!-D`V,]UH`"T1TL=&)``VI"Z=QV8D`':=QV8D,]RH`"$1!BB +M`-J1NG<=F)`"VG<=F)#//'`&PAT`9AP"B'`#^MRBB#:"W?;'0*O\KASSW.` +M`#@&)(-H<#1X()`="1X"$!,$```0!0$*(<`/ZW**(-H*]0&O\G[;)(,!X22C +M`-H'"1$"1*,O(0">F+/YWM`":-'X``^+'XC1,/ +MD1#@D_M_(Y&`OR1_X+,%\`L/41`BD2"S`-DXK<]UH`#('/J%()/D>2RS!/`L +MDPD)10-9803PK+.Y8HDASP\$&%```>8`V<]P@`"PQS4%;_$GH/'``!8$0`<: +M&#$`%@5``1I8,002@3" +M#J``#MG1P.!^X'CQP((,3_$:<`W(SW>``"BR\"WIA +M19)Y88;J)I%1(4"`:`]"\@W(`""!#X``I+'$J11](I'` +M'803%7]X'400`Q(!-L"G`8$$((`/````8"T(@0\````@$(G/<8``2+\"N!9X +M`&'MN,HF8A#/<(``="'4>""0$.$@L`/9SW"@`!0$,*!""V`!"G`]\'`2#0'@ +M$P$!`B%.`Q$-A!/"?:)X$'B`&QP`SW"@`-0'#Q`.A@#=\!N$`W`2`@'`&T0# +M0GDP>>`;1`#0$P$!`>$P>?`3!0'0&T0`4R5^@,HAP@_*(L('RB#B#/'`_@IO\0#8SW&``+Q! +M`*'/<(``]!8!@*/!$.C/"SN+6XN+@$H1:B#,S/ +M=J``U`=1(`"``]@@'AB04_(4'AB0`Q(!-@`6!$`'&A@Q`!8%0`$:6#$$RIS@ +MRB+"!\H@@@\``-P.RB."#P``]`JH!F+RRB'"#RAPT@R@``[9`Q(!-E")4R+` +M`(8B_@,0J42Z`KC$&8(`%GC/` +M`(BQ%"=!$PB1HNAPBL]P@`!(O[5_`KMV>V!@F!(#`"VX;J=VI\"XSW.``'0A +M]",#`+P:Q`#0$0`!!".##P``\/_#N&5XT!D$``;PT!$``;P:!``!V*`:``#J +M"N_[\(J`X-`#(0`#$@TV!LA1(("!P`,"`"&%$PF>!I#8D+BU`R``H!T`$`*_ +MSW"``$B_0"""`_9_ZV+$%8(0$PK``)'8D+B1`R``H!T`$&J%SW*@`"P@\(*, +M(_^/#/)B?Q4/A1\`@```A]B0N&D#(`"@'0`0$!6$$$`LCP#V?^-@!".^CP`` +M`!/X8$OR$0M>`HO8D+A!`R``H!T`$$T+'P,%D)3H!\@$((`/`,```-=P`,`` +M`!'8P"@A!%0`1 +MI[B>'000!O"%V)"XH!T`$,]P@``\(@.`&(B$X-@"`@#/<8``$(L,@0\@``$, +MH<]Q@``X!P"!`>"]`B```*%BD#,5@!!-"PX`!\@$((`/`,```#$(@0\`P``` +M"(TI"%,`I!4`$+2XI!T`$)(5`!&GN)(=!!">%0`1I[B>'000"O`1"9X!C=B0 +MN&T"(`"@'0`0!LA1(`"`&`(!`&8.C_\#$@TV"'.H'0`0SW"``.`@!("P%0<1 +M((!5)T`&U;G/#*(8T/`0")#=4A#@"D%0`020@>!2)ZA!4! +M$0(B0`!((```0KA!+T(3P+H$NE1ZZ7'&N4DAP04T>L]Q@`"\3U%A#0_>$D$I +M`@$4(D$`!2D^`$$I`'(`V5GP02J%`$$O0A/`N@2Z5'KI<,:X22#`!11ZSW"` +M`+Q/4&`-#]X202@"`10B```%*'X!02D`N4`L#P7E>25XP!T`$`JBSW&` +M`-PY`=@`H07P;X*P%0<1#PO%`078&+B@'0`0SW"``#P'08`@E0DA@0``B!$( +M40`9%@"6$'$`V`/W`=B,Z`/8&+B@'0`0SW&``!P[$X$!X!.AH!4`$`0@OH\! +M`0``%/22%0`1E!4!$)`5`A&R%0,10@X@`4HD0``#$@TVH!4!$"5XH!T`$`0@ +MOH\!`0``!?(R"$_W1P-```/,SW&?`+C_&*$&R%$@`(#*(D$#RB`A('CRI!4` +M$&D(G@3/<8``W#D`@8#@`-@R\@#8`*&`%0`1?A4.$1YFSW"``#PB!(!&$``! +M'F8&\(X)[_>*(,<)^0F>Q<]PH`#$+`N`4R"!!/ZXS"$B@`KRF!4`$/(++_8` +MVG2XV&`#\`#8`Q("-@GP#061J7)*(``@SW&@`,@?K!4.$(?H +MI!4#$+&[I!W`$`/P"28.$`/;&+MOH?@1`P"A:P@F31-B?:`90`,`VYB[;J$, +MZ*02``#QN`W,Q2"B!,\@80`-&APP`9()Z`W(SW&``(BR]"$```3H`8(-")X# +M#`U02`P&\$@T!P[NE>U0:Q`"&(/T,C"`"@ACT$(H"N!9XQW"``$B_ +M99`A"U(`!I`="%X`$PM1`&`2``&$N&`:!``$\!R2C;@C0$0,!5!(` +M`<.[97A4&@0`@!$!!X3I/)**N3RRI!(-`!4-'A)H$@,!4R#!`'EA,'EH&D0` +M$PU>$FH2@0##N#A@#WAJ&@(`!\C/<8``F+($((`/`,```!$(@0\`P```#AD$ +M!`7P`-B+N`>Q`9(3Z`W(SW&``(BQ%'G0$0`!4R#`@`GR\!$!`<]PH`"8`SZ@ +MMAI$`*02```$(+Z/````,`CTAB#ECR0,8@#*(((`"@@``03H$@G/_:GP`\BD +M$```!""^CP```#"S\O2X;`[!]0,2`3:D$0``GP@>`XX+;_,!V`,2`38=L<]P +M@``\(L2`E@HO^0#=&0A1`,]P@`"4(`"0@>``W<\E(1/*)0(4`]C/<:``]`<% +MH84E`AT-<*"P`\A=D`UP0+`#R$^`$0H>`$*!`H$:6!_`-<$"@`\A`$`(! +M#7!`L`/(48`-<$"@`\A($`(!#7!`L!`9``0#R)00``!1($""O`U!^#H(#_I7 +M`$```8$?"!X&SW"``$0'`)`=L<]P@`!(!T"``8!1H1*A!_#6"F_S`M@#$@$V +M';%""`_^`\B>#*__>!```8#@%@!"``,2`S8!@Y@3`0"4&T``*P@>!L]U@`!` +MS*EP<@@O^FAQ$-@,&APP#!@/9SW"@`!0$(Z"*(!``KP<@``8:&#`#R*00`0"& +M(>6/I`I"``,2#C:D%@`0]+B*`@$`$([/L]SH``L(&^#A!8-$2"2 +M""-#`Z)[L!8-$63EL7/\`"X`"2-!``*X%GC/PARQKI)(L(%5'O/2JBSW&``)`\"($!X`BA!_#/<```\!$V#8_W^0F> +MQ<]PH`#$+`N`!""-#_`'```TO5,@@00)")X''0V5$`,2#C9*(``@SW&``)@[ +M`8$!X`&A`-DP\`"6$.#E"$6`I!8`$/>XU2%"`\]W@``PR""GHJ>8%@`07@_O +M]0#:`:?/<8``F#L"@0'@`J$`@;A@`*'/<(``/"(#@`F`#PA>``W,1B"``@T: +M'#`#$@XV`=E*(``@DA8`$3GIE!8!$,]R@``PR**2P()`P<]SI0"L_\]R@``\ +M(MBC1()6$@(!%.)"?0/E(KVZ9;IB2")"``6Z12)"`U:C42#`@!!"TN:0>0!"4%@`0D!8#$<]QI0"L_T#`L!8"$7BASW.``#PB9(-6$P,! +M%.-B>@/B(KI;8GIB2")"``6Z12)"`U:A(,($((`/````("6X!2("!$5XB;B. +MN!FASW"@`*@@"(`#V<]PH`#T!R6@# +M&0A1`,]P@`"4(`"0@>``WL\F(1/*)@(4SW6@`/0'&86`X,HAP@_*(L('RB#B +M#,\@(@/*(X(/``!N"LHD`@2,`B+RRB4"!`/(')#%>`UQ`+$#R#V0#7`@L`/( +M+X`-<""@`\A`$`$!#7`@L`/(,8`-<""@`\A($`$!#7`@L`,2`38!3F!#7`@H`,2`3:D$0``9!D`!+@9`@2Z&00$M[BD&0``I!$```0@ +MOH\``$`(!O(!@?"X@`T"\P_P.H$-<""@`Q(!-J01``"&(/./!?([@0UP(*`! +MV2NE`]I(I<]S@``D.`T2#39`@P#8.0V`$,]RH``X+D6"!"*"#\`````?"H`/ +MP````/78!;C/.!XX'A5"%Y#`\C/<:``R!^P$``!EB!!#QZA$-@.H0'8 +M%1D8@$H(X`!!V"T(7D//<(``O$$!V2"@`\BD$`$`FKFD&$``A@QO_P'8SW&` +M`)`\#8$!X`VAH@P```0@OH\&`,H`F'`F\L]P@`"P(`.`@.#*(<(/RB+"!\H@ +MX@K/("(#RB."#P``/P2@`"+RRB4"`<]Q@`"0/!"!`>`0H<]QH/[H`,]PGP"X +M_S:@5P(```/9SW"@`!0$):`#$@$V`8%1"-X`I!$``,]R@``\(E$@`(`$@@/R +MNY`$\(H/+_BZD,]Q@`!PO1&)+0@>``.",(D0N3(@@`\``-@"G[F`X`'8P'@/ +MN"5XSW&@`/Q$#:$$\'81#0$-S%,@0(`)\@;(!!(!-O8.;_8-$@(VSW>``$#, +MZ7!."N_Y`Q(!-@/(!A(0-L]V@`!P(:`0$0`!V`"F-@YO_ZEP`-D@I@GHAB!^ +MC]/R`\B@&$`$!AH8-`,2`3:2$0`!$0B>`JJX'@HO^Y(9!``#$@(V?A(!`8(2 +M``&`$@,!.&`;8PW(SW&```2R%7D)@7![&V-IH0&"V0C>`!8([_F`V`82`38$ +M(8$/`@`!``T2`C<9"8$/`@```!$(7@=/(L$`#1I<,`;PH[I0>0T:G#`#$@(V +M`8);")X!3R'``HRX$'D-&APP$(HS$H(`!+A%>,]U@``PFL]RH``X+D2"!K40 +M\"\N@1!.)H,7`-X/)LX0QGK/=H``X+ST)LX0$0B``_'JSW```/__!+4#\&2U +M"-@,&APPSW"``'"]$8@5"%X!SW"``+"R*@NO_0"(#1(!-P/(`8#]N,\AX@'0 +M(>$!#1I<,,]Q@``<.Q:!`>`6H2WP$-@,&APP#+$# +MB*AQAB'\!T6Y)GA3)!L]P@``\(@2`SW&@`,@?1A`` +M`1^A(-@.H0T,G@92#(_T#?!9#%X&T@[/\@,2`3:@&0``AB!^CP/T`-\"\`'? +MSW"?`+C_6!@`"(API@N@`&AQ`][/=:``U`?2I7X-3_W/<(``<"$`@(#@S"!@78$+CN\0CJSW*``!0\$((!X!"B]_$*(<`/ZW(* +M)0`(,MC//'`I!`!``\)7@(.",_]T<#@?BAT +MA"02D!'R#0E?!OX+#_<'\"#9SW"@`,@<*:`#V<]PH``0%"6@Z_'K\?'`Q@Z/ +M\`HG`)`:<0#=%O+I<2\H00!.((('SW"@``PM3WKP((``PK@/)0T0`-@/((`` +M!B$!@._U).TO*$$#3B".!PT:F#/UV`6X6@GO]\EQ#@PO]N1YL@MO^\EP`-@/((`#!B4-D-_USW*``"0X`(('V0T: +M6#`_"-`!SW"@`#@N!8`$((`/P````",(@`_`````]=@%N,]SGP"X_QJC.Z-I +MV!BX&:,!V`+P`-@'"%$`(*+/<*``%`0JH%D&C_#@>/'`]@V/\"AV1B'-`!UE +M,@@@`"*YP;X?#E`0$PZ0$!T.T1``%H!``1T2$``6@$`!'1(0`!:`0`"M*0:/ +M\*L)$`!`(<(#)+K#N0+P`-F5"14$,R9!<(``<$A`)P-R-'L`>P`6`4`$&%`` +M`!8!0`084```%@%`!!A0```6`4`$&%```!8!0`084```%@%`!!A0```6`4`$ +M&%```!8!0`084```%@%`!!A0```6`4`$&%```!8!0`084```%@%`!!A0```6 +M`4`$&%```!8!0`084```%@%`!!A0```6`4`$&%``0B)"@+/UX'Z`X.@@K0$`%@%!`AA4`.!^X'CQP.X,C_``W<]W```$'4H@`"*I=A4B@#,.$`$& +M`-C/8) +M!8_PSW.``+1$*XL+"D(`+XL+"8,``-C@?HP@`H'A(,$'!-C@?P+8\[5[SW*``/"'>6(AB7IB +M"^DA"0``)PA"`"\-4Q$!Y:]]"O!")9$0+R%')&&]KWT1\`,2SP``V6IU#/"` +MY4HA`"#*)6$0!?)")5$0+R%')`'9+.GS;O1_%2=!$\]S@`#PASIC`"-%`!4G +M3Q3Y8R&)08K[8S<)HP#CBP(B1``#%8(`!+_P?R)X!+HO)`@!`B>#$&QX+R!& +M#OX,K_"(<0YX`G\(Y^Y_1+_M?PD($B8*Y^U_R7`*<7H,(`#I<@'FSW"``)@& +M$HC/?A!VP`;,_U$#K_"AP.!X\<`""X_P*'6`XLPC(H`)\BQM+WG/=H``F`8S +MK@;PSW&``)@&LZFI<<]V@`"8!K2N`*Y5KOH)(`!VK@`0B0#AB,EP$HC1CA!V +MF@$)`$0I/A#5XQW"``/"'0*A!J$*H0Z@!X2]Y7O!\N0`AB@$ZBVRZ`")`$1J(X'(` +M(@8"[@@@`.ER&*T`(D`1&X@[B]X((`#I69"'60KWT!YL]P@`"8!A*(SWX0 +M=G`&S/\`V<]P@`#H08$!K_`@J/'`SW"``#@]`(#/<8``Y$$@@4UH,'+`(&P! +MS"$,@"0+"0#1P.!^X'@">2UY3'E6(0%R1[DX8.!_#WC@>/'`N'$U"%$`"0U2 +M`!T-T@,*(<`/ZW+/<```UQ2*(X@"T02O\4HD``!`+8$`9+D`(8`/@``8%!WP +MSW"``"`8,B!!`8PAPX_*(<$/RB+!!\H@@0\``-@4RB.!#P``$`*4!*'QRB0A +M`,]P@`!(%C5XT<#@?N!X\<#/0&N +M0B%!(($)=8`!Y=T'3_#QP((/3_#/<(``F`81$(@`SW"``)@&$HBK"`("2B8` +M`$HAP!%$+CX'+W"$*`,1)W``(($/@`!DB!\1RP``(($/@`!DB!X1R@#X<`#> +M!M\`)XT/@`!DB-5]!XUI<07:F'`6"B``!17#$$`N@@!4>H0H`14`(D$.U'G' +M<8``9,RX<0"IB'!)<0?:[@D@``85PQ`!'0(`8;\!YK`+;8*@`V`"I`=C@ +M?P"JX'BAP?'`I@Y/\*'!9<((=BAUSW"``+X&A<&+""``J7$!Y:]] +M4R6`$,L(4H$A\`$4@C`2;11X`":!'X``T,DX8$"H(,)$J,EP,@@@`*EQ#_!" +M)0`6#W@!%($PQW:``.C*`K@4>!YF(,`HK@RN"-Q'!F_PH<#@>.!^X'CQP,X- +M3_``WL]PH`"T#W`0$`#/<*``M`_-O>P78[PKC@`*OSW"``&2(09#/=8``[$'`I1OJSW"``/1! +M`(",(!^$T_8G"(,/``"@#T)X0(F`XHHA#PK`*.(`!/1$*+X#+W`*#T_P`*4` +MW0[>SW>``/A&O@CO_ZAG8;X!Y?D.=9"O?<]P@``X/2"`SW"``.1!(*`O(`<$ +MSW&@`+0/'*%Q!4_P#G@L>"EJ`-@/($``)W!:>.!_#B#``.!X\<#F#$_PSW>` +M`)@&`(\6#.__,X_/<(``X$$`$-``SW&``&@E%(]'B1\*`0``CR&)%PA!`,]P +M@`#I00`0P``)(``$+R`%(+&/`_`!Y:]]$H\0=?P`"0``WDHB@"//<(``X4$` +MB!#H1"V^$P`F0![/<8``X$$`$<(``""!#X``8,U`J5_PSW"``/@M((!@>0#8 +M&0@1`\]P@`#H1LE@`B!`(`UX2"!```3P2"!`("\A!2#/<(``^$;+8!./J7&2 +M#B_W58\)($`$+R$%(,]P@`#X+2"`8'D`V``EDQ^``+0&&P@0!,]P@`#X+2"` +M8'D`V`03@2`;"!$#SW&``/P(R6$$$X`@(G@)($$$"/#/<(``V$;(8`)Y"2%! +M!$0MOA,`)D`>QW"``&#-(*@Z`/>`#8`_!AN`]XX'_!Q>!XX<7AQ@`1S0`)#1,0 +M`-V@J1OH#0T3$`#8`*D`W<]P@`!X10"0"PT"$*EHK7V@J<]P@`#01!0@3@.@ +MCJ"J`!'!`#1X`8@9\`L-$Q``W:"ISW"``"1&`)`-#0(0J6BM?:"ISW"``'Q% +M%"!.`Z".H*H`$<$`-'@!B`"KP<;@?\'%X'CQP(X*;_``V*'!`!P$,,]U@`!X +M!P"5SW:``&2(R7&*(@0*E@VO]P';C^@*(<`/ZW(`%001SW```-L4A]N+NVD& +M;_&*)00*`!:$$$PD`('*(4%;_%* +M)0```!0`,0'9AB#^#\#@P'G/<(``(!D@J!D";_"AP.!X\<"F"4_P"'77=24` +M`(``V$KWSW&``+#')8$E"44#(GT!X/GQSW"``+#'Q8"IC"`0@,HAQ@_*(L8'RB"&#P``S2+*(^8,RB0F`&0%9O'*)08!%KBQ`6_P +MI7C/<8``/`<-"%$``=@`J0&I`(F!X,H@@0\``,0)RB""#P``@`#@?P&ASW*` +M`,`P((H982"J(8HX8.!_`:I!B0*X%GC'<(``2+](J"*)X'\IJ,]Q@``\(O`A +M`0!-D42Z#0H>``Z!B;@.H0L*7@`.@8NX#J$-"IX`#H&-N`ZAX'[@>`T2`C8$ +M(+Z/8````,]S@`"(L51[QW*``/BQ"'$%\@/(')`7")X"!"&!#V$````3"8$/ +M`0````#8`+,!V!SP#,P#$@$V&PC>`3(1@0`!BPT(00``V`&K\_$!X`&K"_`Q +M$8$``(L+"$$``-@`J^?Q`>``JP+8X'\8JN'%X<;/&4V>,2HKF(!Y:]]P*C! +MQN!_P<7@>/'`_@\/\,]P@``L'`"`H<&#")``SW:``)0YSW6``)@Y`(4@ADT) +M``#/<(``B"@$@$#!#0B>`$\A``%`P(CI2@FO]0#8/@F/]>8,3_>+<`39H=H] +MV[X,+_T7NR"&">D`A8?H)@FO]0'8E@M/]R"&(*41Z3H*C_5_V`JXSW&@`-`; +M$Z%_V!"A`-B5N!"AL@UO\@'8V0\L]V@`"$)N!3P+;G`N<]W@``\(O`G3Q!2 +M($X"P1AY^X`*4!XXT&#_#/<8``/"+P(0``SW&``#2Q +MNQ`"!KH0`P9"H6&AO!`"!KT0``9%H>!_!J'@>,]Q@``\(O`A``#/<8``-+&^ +M$``&%B$"``*2&K$#DANQ"(HX&0(``-C@?QVQ\<#AQ<]SH`"L+QF#\+@9@P#= +M#/($((`/"````-=P"`````'8P'@'\(8@?P^"X`'8P'@:Z!F#!""`#PX```!" +M(`"`RB!B`"$(4``*(<`/ZW)D$P0`SW```*X-F=N)`6_Q2B4``/X/+_=4V$0@ +M`0)'"!X!SW*?`+C_O:(L]UH`"H(*V%Y.60]^WJ`MO/.!^X'C/<8``%#Q<&<`'SW&``.`@4(&=N)ZX((+/<:``R!P-H>!XX'C@ +M>.!XX'C@>.!XX'@`@N!^\<#AQ2#=SW.@`,@?L*-#&Q@``-BZ#^__C;BQH\D$ +M#_#QP$8,#_"AP3IP*'8:0R>`&8`P>3A@`[B6 +M($(%SW&@`,@?'J$0V`ZA`=@5&1B`N@[O_X'8+P@>0\]P@`"\00'9(*`#R*00 +M`0":N:080`#V"J_^`=C/<8``D#P-@0'@#:$#V<]PH`#T!RJ@T<#@?O'``-D* +MV,]RH`#('QZB$-@.H@'8%1H8@"AP!_`!V00@@`\@````42``P\PA(8#,("&` +M%_0K"Q]`SW```)\7+@^/]L]RH`#\1!V"68(`V=$*WX($(+Z/``8``.+UX?$O +M"QY`SW"``+Q!`=D@H`/(I!`!`)JYI!A``%X*K_X!V,]Q@`"0/`V!`>`-H5$@ +M`,,`V`GTSW&``!0\$($!X!"A`-B8N-'`X'[@>/'`;@HO\`#9"'?/<*``+"!` +M$!``SW6?`+C_'87/=H``;`8]I0"F#/#B"@_QSW`/`$!""@_O\PIQ(0A0`,]P +MH`#4"QB`0B``"$@@``#=",2#`(8=I7T"#_``A@HAP`_K.!^X'C@?N!X +MX'[@>.!_`=C@?N!XX'[@>.!^X'C@?N!XX'[@>.!^X'CQP%((#_`(=@;PSW`` +M`&L.D@R/]L]UH`#`+Z,5`);O"!Z!!\A`'1B0#<@/")$!3@XO_\EP`$D`F@1)>#&_]"MK/<*``U`L8@$(@``A((```L."`#>7_RB`E +M#`/(`Y`EN,"X%[C'<``.``!%(`$+['`@H`$2`3;L<""@((;L<""@(8;L<""@ +M(H;L<""@(X;L<""@)(;L<""@)8;L<""@)H;L<""@)X;L<""@*(;L<""@!_#/ +M<```3@[>"X_VHQ4`EO4('H$'R`0@@`\!``#P++B4X,`@A@\``),`SW&@`&@L +M\"$!`,]P@`#`00"`F@CO]"5X"H\)Z,]Q@`!\/!>1`>`0>!>Q`-@*KZ4'S^_@ +M>*'!\<`>#\_OH\%,P1IP2'4Z[A +M4'@&]$8,K_.!P1KP$0G1#1MX$'@V#*_S@<$1\`D)$04<>`KP"PF1`@0`#8SW*I`*3_N:($%`$Q@K@WHAJB"?!O"1X"3"(`H-$@ +MXJ$%\L]UH`#('TSPSW*E`*S_SW"``#PBN*($@%80``$4X`(A`R`#XR*[>&-X +M8$@@0``%N$4@0`,6HD$HP"'`N'=H+,`$(8$/````("6YSW6@`,@?97@E>(FX +MCK@9HD`5`!8@\"S`@.#*(<$/RB+!!\H@(0[/("$#RB,A!<\C(0/*)"$`2`(A +M\/'P```(#,(2&`P"-A(`4B`0'E>04A_H`$]*,+E*(%[X#BS",A@(CR:Q83 +MEIL+$"!J=(0DT)$*\L]Q@``<.Q"!`-T!X!"AG+U?\!4+WB#/<8``'#L1@0'@ +M$:%"W57P:G2$)`*8"?+/<8``D#P1@0'@$:$-\!4+GB'/<8``D#P$@0'@!*$% +M\%,C/J,#\@#=._`9"UXCE@B/_<]Q@``,/06!`>`%H?7Q^@A/]0HAP`_KK/<8``%#P0@0'@$*'=\2GK%PB>!L]R@`"0 +M/"^"`-T!X2^BD;T*\!\(7@;/$RHJX([_]J<9B]2/!Q%@26 +M;Q8%E@HAP`_K`1H:WQ$Q4` +MEO"XRB`A`,`/H?_/(*$#:Q8!EE@6`)8+($"`(?)O%@"6SW6@`/0'4R!`@`'8 +M#_()I>!X`-@)I<]Q@``,/0B!`>`(H<]QGP"X_Q:AV@MO_@'8`]@*I07=F+T" +M\`#=ENT9"-XA'PH1(`'9SW"@`/0'+*`#V0;P`]G/<*``]``/H<]P@`!0HR&`SW"``#PB`X`4D!\)`0#/<8``+!H:@3N!)'A1(`""G`EB +M]\H@8@"I<`C+$!@`5]R7`*"J_Z +MJ7')<*EQ"G+I1L]Q@`#DLC*!"PF>`D*P +M0[!$L.!_6;#@>/'`;@KO[YAPSW"``.2R#I#/=H``^+,`ML]P@`#DLD@0!0`$ +M)8^/`````@#;`_(7#1X"SW&F`.C_"X$#I@R!!*8#\&2F8Z;/=:0`M$4,%0*6 +M#14`EO_9+R"'$!"Y!"))`"SO,A4!EE,ACP#_9R&V_]GT?PBY[W]$>4`O!A(` +M)D<``"#($P4G!P)`+P$6!"*"#P#_``!`+P@4.F(`($@2_]D%)P<""+D%(L(! +M!"!'`/A@`">!`25XY;9/>00B@@__````*+I%>2.V#W@$M@05`)8"MB,-'@)$ +M)0`&([@!X!<(E`#/<:8`Z/\-@06F#H$&I@/P9J9EI@#:2B2`<`;9C;FH(``# +M*=@2N/`@0P!`)@`?57@!X6"@`>+/<(``Y+(`D#@>`!%5)D$4&K;/<(``T+-& +M#B_]"-H;%0"6SW&E`-C+&:8<%0"6&J8=%0"6&Z8.@1RF#X$=IB85`)8>IL]P +MI`"0?QR`<0'O[Q^FX'CAQ<]U@`#XLPFE*J5XM4NE`=@9M>!_P<5*)`!Z`-FH +M((`"`-K/<(``^+,U>$"@`>'@?N!X`-D@H.!_(:`$`````$`!``````"H$D-U +M`08``0``````````1#V``-@]@```?(``P""``.@(@`!P!8"!#QH;(@``&R4" +M`!M````;<10&@($``,`6#QL+(A@&@($``,`6#QL*(AP&@($``,`6#QL((D`I +M@($"`%QN$0``8?A!Q!`/&PDB``L).0(`"F(#`0IB!`(*8@``"4`$``!A"0`) +M0`(``&$*``E`````80(`"4$`"1HH``#`%@$`&R8``,`7!``=)@$`""?I``AD +M#R`;(AP&@($``,`7<`6`@0\:&R(``!LE`@`;0```&W$/10`B`%P`.0<``&(& +M8`!B``!8.&!%P!!P1<`0>$7`$)!%P!!I``!A"`!8;O@/`&$``!,E```3)"00 +MP!$`@!,D.!S`$0\`$R(!`!,P!"C`$0]S$R*"`1,P!"C`$0]T$R("`A,P!"C` +M$0]U$R)"`A,P!"C`$0\4%2(!`!4F#W(3(@@`S!$/1``B"@``0`!``'`.``!A +M```3)0(`$R3L',`1#W83(A@(RA$)`!-`'`C*$0D`$T`@",H1#W@3(@0`RA$` +M``$D```!)08``&$/=A,B+$C'$0]X$R(``,81`P`!)````24``!,EPBP3)`0H +MP!$"1A,D!"C`$<)?$R0$*,`1#T4`(@!<`#DL``!D```3)`$`$R4X',`1#W<3 +M(N`Q,B`0`3,`0HP!$/%!4B +M`@`5)O\`$R4"$!,D!"C`$0\4%2("`!4F=`>`@0``P!;"+!,D!"C`$0)&$R0$ +M*,`1PE\3)`0HP!$/31,B!!#%$0(`$R3P',`1`0`3).P`````````.(`````````Y@````````#J`````````.X`````` +M```\@````````#V```"JJ@H`/H```%68F:H_@```````4#"`````````,8`` +M```````R@````````#.`````````-(`````````U@````````#:````````` +M-X`````````X@````````#F`````````.H`````````[@````````#R````` +M````/8`````````^@````````#^`````````,(`````````Q@````````#*` +M````````,X`````````T@```FGD``#6```"JJJJJ-H`````````W@``````` +M`#B`````````.8`````````Z@```JJJJ"CN`````<)FJ/(`````````]@``` +M`````#Z`````````/X````````#__P``I0$!`+D!WP"Q`!L`%@$;`*\`&P`4 +M`1L`;`"@`-$`H`!O`(,`<0"#`'8`@P!S`#,`;@`S`'``,P!R`#,`UP`S`-0! +M!@#0`0``?@`\`.,`/`!X`$D`W0!)`'\`6@#D`%H`J@`_`*L``0`/`3\`$`$! +M`'D`:@#>`&H`J`````T!``"F`#<`IP`!``L!-P`,`0$`!``(`)P!S`"=`#,6`'PT%P"`-1@`A#89`(@W&@",.!L`D3H<`)4['0"9/!X`G3T?`*$^ +M(`"E/R$`)$D&`BQ*"@(T2PT!/$P/`61-$0%L3A,!=$\5`7Q0%P&$41D!E5(= +M`9U3'P$!!````@4!``,&`@`$!P,`!0@$``8)!0`'"@8`"`L'``D,"``*#0D` +M"PX*``P/"P`-$`P`#A$-``%```0"00$$`T("!`1#`P0%1`0$!D4%!`=&!@0( +M1P<$"4@(!``````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M``````````````!`'```1!P``$P<``#(&P``S!L``#0,H`!`#*``'!R@`$`@ +MH``H)*``;!"@`!@DH`!X)*``?"2@`(`DH`"$)*``4!"@`%00H`!()J``8!"@ +M`$PFH`!D$*``:!"@`%P0H`!8$*``,!"@`#P0H``T$*``+`R@``"!I``!@:0` +M`X&D`(@DH`",)*``D"2@`)0DH`"8)*``G"2@`*`DH`"D)*``@%F```````"R +M#,@`_________P`!__\"`____P3______________________P7_!O\'_PC_ +M"?\*_PO_#/___PW___\.____#____Q#_____________________________ +M_________________Q'___\2____$____Q3___\5____%O___Q?___\8____ +M&?___QK___\;_____QS___\=____'O___Q____\@____(?______________ +M________(B,D_R4F)___*/___RG_________________________________ +M_____________________________________________P`!``$!```````` +M```!```````````````````#``````````$`````````&"0!```````$=@`` +M`0```)"U`0`"````!+4!``,```#4``(`!````!@D`0`%````Z!$!``8```"P +MZ```!P```'`2`0`(````3"\```D````04```"@````3'```+````2"P```P` +M``#8``(`#0```&#@```.````#.$```\```#LWP``$````.S@```1````$#$! +M`!(`````S`$`$P```#`D```4````<%``!@```#\EH```````````````````````(@` +M`0#4L@``G"$``-2R``#4L@``U+(``#@*```8Q`$`B-$``-2R``#4L@``H"<` +M`*`G``"@)P``H"<``*`G``"@)P``H"<``-2R``#4L@``U+(``-2R``#H1@`` +MU+(``-2R``#4L@``U+(``-2R``!LT0``U+(``-2R```(QP```````!CL```< +M[```M`(``*`"```X,`$`D+\``,2A`0"XOP``[*$!`-R_```4H@$`+#B``"B, +M@````````````````````````````````````````````````````````0`" +M``(``P`$``0`!0`&``8`!P`(``@`"0`*``H`"P`,``P`#0`.``X`#P`F`"<` +M*``I`"H`1@!&`$<`2`!(`$D`2@!*`$L`3`!H`&D`:@!J`&L`;`!L`&T`;@!N +M`&\`<`!P`'$`<@!R`',````@````!````)/D``/#W```` +M````4`$````````D^0``;"8!`,`P@``"`````````"3Y``!X)0$`7`>```0` +M````````+/D``/3X```DHX``*@`````````D^0``]/@``#P]@``$```````` +M``````#\^``````````````!`````````!#Y```````````````````````` +M^/@``````````````````"3Y``"XHP$`````````````````)/D``'BC`0!H +M!X``!`````````!N`&X`:0#``*``4`"``+X`4`%]`#X`````````G`($`.8! +M)0!5`P0`W`%C````;@!N`&D`P`"@`%``@`"^`%`!?0`^`````````)P"!`#F +M`24`50,$`-P!8P`````````!`0``T/\``!72````````_P,``-#_```,T@`` +M`````/\!``#0_P``%=(```H`````_`\`T/\```S2```)`````/X#`-#_```5 +MT@``%```````\#_0_P``#-(``!(``````/P'T/\```;2````````_P$``-#_ +M```'T@```````/\#``#0_P``!M(```D`````_@,`T/\```?2```*`````/P/ +M`-#_```&T@``$@``````_`?0_P``!](``!0``````/`_```````````````` +M`0````$````*````!0````4````&````"@````H````&````!`````4````& +M````!0````4````&````!@````8````&````!P````<````'````"`````@` +M```(````!``````````````````````````!`````@````$````"`````0`` +M``$````#`````P````(````!````!````````````````@````$````!```` +M```````````'```````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M``````````````````````````$`````````+`$``%X!```!`````0````$` +M```!`````P``````````````$`P!`'00`0!D#P$`X!`!`'`0`0"H#@$`W!`! +M`#@-`0`T#0$`8.,6`"#6$P``````$`````"``````*``$"<``.@#``#H`P`` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````"`````@````$` +M```!`````@````4````"`````@````4````"`````@````$````!````!0`` +M``4````"````!0````4`````````!0````(````"```````````````````` +M!0````4`````````!0````(````"````!0````4````%``````````4````" +M````!0````$````!`````@````(````"````!0````4````"````!0````$` +M```!`````@````(````"````!0````4````"`````@````4````!`````@`` +M``4````"````!0````4````$````!0````4````!````!0````4````%```` +M`@````(````%````!0````4````!````!0````4````%`````@````(````` +M```````````````````````````````````````````````````````````` +M``````````````````````````````````````````````,````````````` +M````````````"``````````!$``!`````H```4(`!@(0``(@```#P``!0P`& +M`Q```L````/```%#``8$$``"0````H```40`!@41``!````#P``!10`&!A$` +M`.````/```%%``8'$0`!`````H```48`!@@1``(@```#P``!1P`&"1$``L`` +M``/```%'``8*$0`"0````H```4@`!@L2``!````#P``!20`&#!(``.````/` +M``%)``8-$@`!`````H```4H`!@X2``(````"@``!3``&```````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M`````````0```````````````````````````````0(!`0`"`0`!`@("``$! +M``(!`@$"``(``0(#@("`@("`@(`!@`*`@("`@,``D`#0```````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````!```````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M_P````````","@`````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````!`````0`````````````` +M````````````````````````````````!,`!`!4````#````^$"````````` +M`````````*B_`0`%`````P```/A`@`````````````````"+T!``H`````````&$&````````````````` +M`+B^`0`*`````````!A!@`````````````````"XO@$`"@`````````808`` +M````````````````N+X!``H`````````&$&``````````````````+B^`0`* +M`````````!A!@`````````````````"XO@$`"@`````````808`````````` +M````````N+X!``H`````````&$&``````````````````+B^`0`*```````` +M`!A!@`````````````````"XO@$`"@`````````808`````````````````` +MN+X!``H`````````&$&``````````````````+B^`0`*`````````!A!@``` +M``````````````"XO@$`"@`````````808``````````````````N+X!``H` +M````````&$&``````````````````#S!`0`&`````````!A!@``````````` +M``````"DP`$`!0````,```#X0(``````````````````T+P!``H````````` +M&$&``````````````````$"]`0`*`````````!A!@`````````````````"L +MR@$`"@````,```#X0(``````````````````E+T!``H`````````&$&````` +M`````````````#R^`0`*`````````!A!@`````````````````"\O@$`"@`` +M```````808``````````````````/+\!``H`````````&$&````````````` +M``````C``0`*`````````!A!@`````````````````!XP`$`"@`````````8 +M08```````````````````````/A`@`#X0(``N""@`&P@H```@`$`_W_\_P`` +M````````&$&``!A!@`"D(*``.""@``$```#\____```````````X08``.$&` +M`*@@H``\(*``$````,?___\``````````%A!@`!808``K""@`'@AH`!``0`` +M/_[__P``````````>$&``'A!@`"P(*``?"&@```,``#_\?__```````````` +M````````````````````C+T!`!4````#````^$"``````````````````!`' +M@``\EX``&````/R6@`````````````````!_``````````!_```````````` +M````````````.````&@```!T````@````(P```"=````!P````````#_____ +M`````"T!``#=`0``6@(``+H"```*`P``30,``(<#``"Z`P``Z`,``!$$```W +M!```600``'H$``"8!```M`0``,X$``#G!```_@0``!4%```J!0``/@4``%$% +M``!D!0``=04``(8%``"7!0``IP4``+8%``#%!0``TP4``.$%``#N!0``^P4` +M``@&```4!@``(`8``"L&```W!@``0@8``$P&``!7!@``808``&L&``!U!@`` +M?@8``(@&``"1!@``F@8``*(&``"K!@``M`8``+P&``#$!@``S`8``-0&``#; +M!@``XP8``.H&``#R!@``^08````'```'!P``#@<``!0'```;!P``(@<``"@' +M```N!P``-0<``#L'``!!!P``1P<``$T'``!3!P``6`<``%X'``!D!P``:0<` +M`&\'``!T!P``>0<``'\'``"$!P``B0<``(X'``"3!P``F`<``)T'``"B!P`` +MIP<``*L'``"P!P``M0<``+D'``"^!P``P@<``,<'``#+!P``T`<``-0'``#8 +M!P``W`<``.$'``#E!P``Z0<``.T'``#Q!P``]0<``/D'``#]!P```0@```4( +M```("```#`@``!`(```4"```%P@``!L(```?"```(@@``"8(```I"```+0@` +M`#`(```T"```-P@``#L(```^"```00@``$4(``!("```2P@``$\(``!2"``` +M50@``%@(``!;"```7P@``&((``!E"```:`@``&L(``!N"```<0@``'0(``!W +M"```>@@``'T(``"`"```@@@``(4(``"("```BP@``(X(``"1"```DP@``)8( +M``"9"`````````$`````````!P`````````````````````!`@,$!`0$!`4& +M!P@("`@("0H+#`T``&X[:#MB.UP[;CIH.F(Z7#IN.6@Y8CE<.6XX:#AB.%PX +M;C=H-V(W7#=N*6@I8BE<*6XH:"AB*%PH;B=H)V(G7"=N&6@98AE<&6X8:!AB +M&%P8;A=H%V(77!=N%F@68A9<%FX5:!5B%5P5;A1H%&(47!1N$V@38A-<$VX2 +M:!)B$EP2;A%H$6(17!%N$&@08A!<$&X":`)B`EP";@%H`6(!7`%N`&@`8@!< +M`%0```!N.V@[8CM<.VXZ:#IB.EPZ;CEH.6(Y7#EN*V@K8BM<*VXJ:"IB*EPJ +M;BEH*6(I7"EN*&@H8BA<*&XG:"=B)UPG;B9H)F(F7"9N)6@E8B5<)6XD:"1B +M)%PD;B-H(V(C7"-N(F@B8B)<(FXA:"%B(5PA;B!H(&(@7"!N$F@28A)<$FX1 +M:!%B$5P1;A!H$&(07!!7$%(031!)$&X!:`%B`5P!;@!H`&(`7`!4```````` +M```````=````````````````````````````````````@P```)(```"#```` +MD@```.@```#W````Z````/<`````````"0`````````*````.````&@```!T +M````@````(P```"=````!P`````````'````!P````<````````````````` +M````````\$P!``@````#````^$"``%P)@`#<"8``7`J``-P*@``*#1$4"@T1 +M%!D9&1D*"@````````8&!@8)"0D)``8````%!@<(#0X/$!46%Q@9```````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````/&JRL)2^LK*RLK*6L?I%.0PR;-@```#(` +M``"/`(P`B@!#``\`(&@W````$0`^.B`1```")0``#"\```(O.3D`"B4\MT=O +MB@`'%"=B+@```@`7```%$`H@,$````8&"@TC&R,A````#!`4&"`(!```/#@T +M,"PH)"`<&!00#`@$``L'`P`[-S,O*R`````X````G````#P```"`````0`````@```/,````````` +M]`````````#U````````````````````!`````4```#A`PX>X0```.$##A[A +M``````````H````1```````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M``````````````````````````#A`PX>X0```!7V8_:P]OSV1O>0]]CW'_AE +M^*GX[?@O^7#YL/GN^2OZ9_JB^MSZ%/M+^X'[MOOJ^QS\3?Q]_*O\V?P%_3#] +M6?V"_:G]S_WT_1?^.?Y:_GK^F/ZV_M+^[?X&_Q[_-?]+_V#_<_^%_Y;_IO^T +M_\'_S?_8_^'_Z?_P__;_^O_]__________W_^O_V__#_Z?_A_]C_S?_!_[3_ +MIO^6_X7_<_]@_TO_-?\>_P;_[?[2_K;^F/YZ_EK^.?X7_O3]S_VI_8+]6?TP +M_07]V?RK_'W\3?P<_.K[MON!^TO[%/O<^J+Z9_HK^N[YL/EP^2_Y[?BI^&7X +M'_C8]Y#W1O?\]K#V8_9PN8.ZEKNJO+Z]TK[GO_S`$<(GPSW$4\5JQH#'E\BO +MR<;*WLOVS`_.)\]`T%G1L=_-X.GA +M!>,AY#[E6N9WYY/HL.G-ZNKK!^TD[D+O7_!]\9KRN//5]//U$?`26!;0&T0?O"`T**PM(#&8-@PZA#[X0W!'Y$A84 +M,Q50%FT7B1BF&<(:WQO['!<>,Q]/(&HAAB*A([PDUR7R)@PH)BE!*EHK="R. +M+:(`^0#*`>H` +M@@"9`'3111?HHHLN``4'`0,$``4!!0````4&``($``4`!0```0(!`@,$```% +M!@<("0H```D``````````0````(````#``````````0````"````!0````<' +M!P<'!P<'!P<'!P<'!P<'!P<'!P<'!P<'!P<'!P<'!P<'!P<'!P<'!P<'!P<' +M!P<'!P<'!P<'!P8&!@8&!04%!04$!`0$!`,#`P,#`@("`@(!`0$!`0`````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````H`"@`,``L`"P`*``\`#0` +M0``\`(P`;`!8`$@`]`"P`'__!P\?/P$#,````#8````,````$@```!@````D +M````!@````D````%``<"`P0&!D`#@`;`"0`-@!,`&D`=@""`!@`-@!,`&@`G +M`#2`.@!!P`F`$T`=`">`.@!.P%>`89D#,P?9"G,.IA7F'(`@&20S!W,.IA7F +M'%DKS#D`03-(V0JF%8`@62L`0:96@&%9;)W8B9U.[,1.-$B#-"=V8B<:I$$: +M$SNQ$Q$8@1$/_,`/3NS$3B=V8B<:I$$:$SNQ$PW2(`V)G=@)"(S`"`=^X`(`'"(S`"`=X@`<&:9`&L++5!09I +MD`:PLM4%!51`!0540`76'<8$`0,XC@.JJJH"<1S'`:JJJ@K'<1P'``0``&0````` +M````#P`_``$````/`#\``0````\`/P`!````#P`_``$````/`#\``0````\` +M/P`!````#P`_``(````/`#\``0```"(6``"````#```!60``)!8``0````,` +M``%:```F%@`"````!````5H``"@6``(````#```!6P``*A8``H````,```%< +M```L%P``````!````5P``"X7``"````#```!70``,!<``0````,```%>```T +M%P`"`````P```5\``#87``*````#```!8```.!@```````0```%@```\&``! +M`````P```6(``#X8``(````$```!8@``0!@``@````,```%C``!D&P`"```` +M`P```6\``68;``*````#```!<``!:!P```````0```%P``%L'``!`````P`` +M`7(``6X<``(````$```!<@`!!T``0````,```%V``)\'0`"`````P```7<``WX= +M``*````#```!>``#@!X```````0```%X``.$'@`!`````P```7H``X8>``(` +M```$```!>@`$B!X``@````,```%[``2,'P``````!````7P`!)$?``%````# +M```!?@`$E1\``P````0```%_``67'P`"P````P```8``!9D@``!````#```! +M@0`%G2```4````,```&"``6?(``!P````P```8,`!:$@``,````$```!@P`% +MI2$``$````,```&%``4\90$``````#QE`0``````/&4!```````\90$````` +M`#QE`0``````/&4!```````\90$``````#QE`0``````.%\!`!@```#\8`$` +M(````&AF`0`4````7&=BM>9-FNQ%CYT?0(F'^A7O +MZ[+)C@O[[$%GL_U?ZD6_(_=3EN1;F\)U'.&N/6I,6FQ!?@+U3X-<:/11--$( +M^9/B5/%NW%AM>:56:4$<^*$.D&!('^\*!$>+HE +MXTOSHOY=P("*!:T_O"%(<`3QWV/!=W6O8T(P(!KE#OUMOTR!%!@U)B_#X;ZB +M-JS(Y[HK,I7FH,"8&=&>?Z-F1'Y4JSN#"\J,*:?BO!T6=JT[VU9D3G0>%-N2"@QL2.2X79]NO>]#IL2H.:0Q-].+\C+50XM9 +M;K?:C`%DL=*RWO\J-9M.BP``0($!`````0,#`@$#`0$0````(`````` +M`0````(``$``````!```0````$``````\&$```$!`@$"`@,````````````` +M`````````````````````````0`````````````````````````````````` +M``````````````````````````````````````!D````*@````X```````$! +M`````````````0$``````@`!``("`P,#R+\!`-2_`0#@OP$`[+\!`/2_`0#\ +MOP$``0$``0(!`0$``````````/____\````````````````````````````` +M`````````````````````````````(`-````(```@`T``(`-````(```@`T` +M```&````!``````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M````````````````!@````0````L`0``!P`````````2````!`````L````( +M````!````'P^@``)````!````!P````*````!````.@]@``+````!````-@] +M@``,````!````!P````-````!````$0]@``/````!````!8````.```````` +!```` +` +end +Copyright (c) 2006-2012, Intel Corporation. +All rights reserved. + +Redistribution. Redistribution and use in binary form, without +modification, are permitted provided that the following conditions are +met: + +* Redistributions must reproduce the above copyright notice and the + following disclaimer in the documentation and/or other materials + provided with the distribution. +* Neither the name of Intel Corporation nor the names of its suppliers + may be used to endorse or promote products derived from this software + without specific prior written permission. +* No reverse engineering, decompilation, or disassembly of this software + is permitted. + +Limited patent license. Intel Corporation grants a world-wide, +royalty-free, non-exclusive license under patents it now or hereafter +owns or controls to make, have made, use, import, offer to sell and +sell ("Utilize") this software, but solely to the extent that any +such patent is necessary to Utilize the software alone, or in +combination with an operating system licensed under an approved Open +Source license as listed by the Open Source Initiative at +http://opensource.org/licenses. The patent license shall not apply to +any other combinations which include this software. No hardware per +se is licensed hereunder. + +DISCLAIMER. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 +COPYRIGHT OWNER 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. +begin 644 iwlwifi-6000g2a-18.168.6.1.fw.uu +M`````$E73`HV,#`P9S)A(&9W('8Q."XQ-C@N-BXQ(&)U:6QD(#`*```````` +M`````````````````````````````````````0:H$@`````!``````````$` +M``!07`(`(""`#P``0`!I(```:2!``&D@``!I($``(""`#P``Z`!I(```:2!` +M`&D@``!I($``(""`#P``L/]I(```:2!``&D@``!*(```2B$``$HB``!*(P`` +M2B0``$HE``!*)@``2B<``$H@`!!*(0`02B(`$$HC`!!*)``02B4`$$HF`!!* +M)P`02B``($HA`"!*(@`@2B,`($HD`"!*)0`@2B8`($HG`"!*(``P2B$`,`HD +M@#^!``!`02R<,$`LG#!")!PT"B*`/X``(%L*(P`WS@B`!THF`'!I($``2B8` +M<$HF`'!*)@!P2B8`<``6`'"``"0=0'@@($"'`````````````/P`3<.-TU\.!X!-PTW3/PX'@$W##=,?#@>`3<+-TO\.!X!-PHW2WP +MX'@$W"3=*_#@>`3<(-TI\.!X!-P`3<%-TC\.!X!-P0 +MW2'PX'@$W`S='_#@>`3<"-T<\.!X!-P$W1GP-!0:,#`4&3`L%!@P*!07,"04 +M%C`@%!4P'!04,!@4$S`4%!(P$!01,`P4$#`"QP'&L"1-,[`D'S/@?N!XX'C@ +M>.!XX'C@>`HD@/`%($0`X"#!!T0D_H!!*L0`A``"`"\D`O%"(0$!0B`#`>@@ +MH@0$$00"!!$%`@01!@($$0<"!!L(`00;2`$$&X@!!!O(`2P`)0!$(CZ!/``B +M`$0B_(!`(<$`X"#!!T`CPP"H((`!`1&$`@$;"@$@(,`'!!$$`@01!0($&P@! +MU`?A_P0;2`%$(OR`!!$$`LD'[_\$&P@!0B%!`$(@0P"H((`!`1&$`@$;"@$@ +M(,`'SW&@`*PO&(&:N!BAT0!@$@78X'C/<:``K"\8@;.XNK@8H;T`8!)DV`HB +M0(``V>X``0`O)@#P2B9``$X`!@!/`"``BB7_#^!X"B)`@`#9S@`!`&P`)``O +M)@#P7``%`"L(-0A*)D``"'$`V`(AOH#@(,4'0GD!X`(AOH#@(,4'0GGK!^__ +M`>`O+0$`0"5%``(F?/$``"```"A``>@@8@,O(`"`+R%+``(AOH#`((8!PB&& +M`.!^$0`@`$H@`!!*($`0#B)"`"\@"Q+.($6`BB7_#P@`!0`O+0$`0"5%``(F +M?/$``"```"A``4HF0`#H("(#+R``@"\A2P`"(;Z`P""&`<(AA@!*)@``0B#^ +MD,X@@@%$('Z0SB&"`>!^*0```.!X_!R(L?P<2+'\'`BQX.!XX'C@ +M>.!XX'C@>.!XX'C@>.!XX'C@>.!XX'C@>&&]C"7_G^WUX'_!Q>!X\<#AQ<]P +M@`#X'DV`SW6``+2%((6WNKBZ!"&!#P,````'N45Y+:"."B`2`-@`A<]Q@`#< +MJU$@@(),B<]P@`!0PC)J-GG'<8``$+]@@59X08`%\I6[8*&KN@3PM;M@H8NZ +M0:`+C:.X%07O_PNMHL'QP)(,S_]%P<]U@`#X'B>%,'`(]#"5%!0.,3!V!/19 +M'8(0T!4!%C!P#O3/<8``!"(\D104#3$P=0;TSW&``%PB6:F`X@STSW6``,0' +MP8V`Y@#9RB!!`"7R(:V.X@3T`=@A\$$H#0('?4$H`02G><]W@`#$!Z"/4R5% +M$4PE`(3&N8WV"B'`#^MRSW```,T;G]NA`2`!BB2##U$E@)$&\@#8#-Q;!,__ +MSW:``-#!%B9-$:>-H*_)=18E31$`I104`#%&K<=Q@`"0O@*U`(D'K0`90@$` +M&T(!Q/'@>/'`M@O/_PC(SW*@`,@?#AH8@`G(#QH8@`K($!H8@`L2`38"R"1X +M$1H8@`S(SW&```PZ+1H8@`"!`>``H<.XC>`I]`O(?]D*N21X+R@!`$X@@@<` +MV`\@@``$(0&`0B*-`AGR"R-`P!?TSW"@`(@@\"!0`\]V@`#<-@"&$'7/=X`` +MX#8&]`"'$G#D#\$&H*8`'P`4B0//_^!X\<#AQ0'9SW"@`+`?.:#/<8``6!T( +M@0"`K,%)P`R!`(#/<8``X"'/=8``#)%*P`J!H;@*H0B%X+@)\E$@P($'](X( +M``8F#V`"&-B+<:EP?@A@#R3:SW"``,`'((`"B8#@$O0$B5$@`(`.\@O(!""` +M#_[__P,+&A@P"\B&N(RXC[B0N`OP"\@%((`/`0``_`L:&#`+R*RX"QH8,-(. +MS_^+<##9D-H>V\H.8`X8N\]PGP"X_P+9-J`HP('@RB'"#\HBP@?*(((/``#J +M',HC@@\``/P`RB0B`.@'X@#*)2(`.@H`!H#@!_1:"^```-C:"Z`.!MBI`N__ +MK,#/<8``G%K@?PAAX'CQP*X(``;/<8``L!CP(0``0'C/<*``T!N`VE"@SW"` +M`#@=`(!1(`""`-D&\L]PGP"X_SV@T<#@?O'`Z@GO_P_9SW6```C(`!8`0``6 +M`$!5)4X4`*72"*`1!&W)<(X(H!$BE1Z5SW&``,`'VF#88`$0A0!,)0"`0*$3 +M]`*%\+C*(<$/RB+!!\H@@0\``.D(#A +MRB1-<.!XZ"`M`L]QH`!0#"6!`1A2`.!^X'CQP%H)S__/<(``^!X#@!B(I<&$ +MX$H@`"`,]`HAP`_K`X`)@%$@@(`,\L8,8`('V`'8`:7/<*``+"`0@`"E +MSW"``&0Q`("!X`WTSW"``'C'SW$``!`G[@GO_P6`$'@"\`#8SW&``.2H![$# +MA8'@#!D$!##T`(&"X,P@XH`$]`'8`*%,%8`0@>`F],]PH``L(-"`SW`!`-0Q +M0,`!V$'`"!P`-!'80\``V(RX1,``V!#9!-H(*+!\<"B#Z__F')%P4$H`0('>4$H`@0G>L:ZSW6` +M`)"^267GN5UE$_04%`XQSW.``-#!:'(V>N""\7`%].*2T7<'\B>*Y[FG:O7S +M`-@H\,:*@.8'](#?SW"``,0'X:C/=X``3!\%CQ!V!/2`V`6O"O#/=X``7"(9 +MCQ!V!/2`V!FOQHHV>P`<@`,'BH>Y`*W/<(``Q`=`B""H`=A'JPS<

/_^!X +MH<'QP`,2`C?7<@```$`!VL(BB@`7NL=R``X``(.Z['-`H^QR`*+Z#*`$*'#1 +MP.!_H<#@>*7@'_()]H/@%?*$X!?RA>`;].!_`=B]X`_R!O:MX!7TX'\"V,S@ +M#_*,($.'#?3@?P;8X'\`V.!_`]C@?P38X'\%V.!_!]@(V.!^X'CQP.'%SW6` +M`(0OJ7!`)8$;&@P@#R[:`=CA!J__81T"$/'`6@Z/_X+@"'6-]PHAP`_K`X`(@,]QI`"T15$@`(`/ +M\D0MOAL`)T`>;)!+D'M[97I3&9B`#9!4&1B`!?!3&9B#5!F8@T0MOALG=PZ7 +M5AD8@`^76!D8@!"751D8@!&75QD8@!*76AD8@!.77!D8@!2761D8@!676QD8 +M@*((8`@J<&4%C__@>/'`(@OO_^'%U@J`!,]P@`#X'@.`&(B!X"[TSW&```C( +MSW*```Q%`()@@6"@`((/'`@>#AQ0#8 +M"?3/<(``1Y$!W;(*[_^I<:EPF02/_^!X\<"6X.'%`-B,]\]U@`"TA:EPD@KO +M_P39"XV#N`NM`=AQ!(__\<":X.'%`-B,]\]U@`"TA01M;@KO_P39"XV"N`NM +M`=A-!(__\<"DP9#@`-G*($(`$_2+<$H*[_\0V0`4`#&$X,P@8H$(],]P@`!0 +MJQ^`];@"\DQP`=BDP-'`X'[QP)8+C_\(=\]P@`#X'@.`&(B$X!IQ2?*$YP#= +MC``E`,H@10//=H``,)%`)@`3]@GO_P39+HZPKE,A```1KD$HP""@N3!P8@`E +M``(@0@!CO_%R5@`&`(#B#O+/<:``T`\0$0"&8;I88!`9&(`E$0"&#W@#\`^. +M`-E3(((@#R&!`"1X+R8'\,]QGP"X_Q"N&('/(.('T"#A!QBA&(&>N!BA&(&^ +MN!BA`=A1`X__X<3\',B^_!Q(ON'`X<'APN'#_!P(L?P<2+'\'(BQ_!S(L?P< +M"++\'$BR_!R(LOP(P@7((!V.!_ +MPB`+`/'`%@JO_THD0`#/=8``^!X5)0,0`(-`)0X5T7#")`(!\"4-$<@5!19$ +M);Z!"?(*(<`/ZW*.V(VX=0>@`'3;R!`-!J5YR!A8`*"#!ME&><@5`!8D>,@= +M&!``@\@0``:&('^.=`H!$AT"C__@>/'`I@F/_\]S@`#$!@2#@.!$],]V@`!$ +ME!,6`I8`WX0J"`D`(8!_@`!(C`*C)(@!W8#AZZ.LHR+R'1[8DPP0!0`$)8$/ +MP/\``$$I!`;/<8``>,<4$08`!2X^`0`AA'\_`/__!"1!`1X>6)`@D(PA@H8! +MV<(A3@`JH^>C)(#/=H``@)#`N2JVSW:``'0E**Y`K@*(I*,!KB#P!(.!X!ST +MY@\`"0#8!*,"@R2(@.$2]">#'.`V>"2(SW"``"0B!X@0<0'9P'G/<(``<"4@ +MH`+8`_`!V`.C.0&O_P'8X'CQP,H(C__/=8``Q`8$A:/!@.``WR?T^@I```'8 +M!*4"A02(@.!2`@$`SW"``'`E`("`X$8"`@#/<(``6!T0@,]R@`"DD`"`(X(9 +M8<]P@`!@)0"`.&"2#B`0`J*`X!H"`0!V\`2%@N`\]`J%@.`/]`P5!!`0%040 +M"B'`#^MRSW```(H,Y06@`(HCC@M"A2>%0"(`!S9X)HA@P2:(`1Q",":(`AQ" +M,">(8<$GB`4<0C`'B(MQ!AP",)8*8`ZH$@``SW"``%@=$(`@@,]P@`!T)2&@ +M@@^@`.6E`]@$I<_P!(6#X#KT0H4GA4`B```6(42!`@1/RSW&``%@=`Y(P +M@<]S@`!T)2"!88,*N&)Y,'`%]PG8"Z6.\`6%@.`-]`2*@."K\L]P@`"DD+X- +M(!`"@(#@H_(%A8#@!O(%V`NE`=@)\,]P@`!P)0"`@."7]`#8E@B`!Y/P!(6! +MX&_TT@E``R*%1X5`(0`'5GA%B."Z&_*#ND6HSW.``,@ZR8//#?F88&IB#!8A1($"`*_*N"P`0@.`0 +M]`HAP`\"A>MR'!4%$`00A`#/<```BPR=!*``BB,0`)X+(!`"V"X+(!`(V"*% +M!(F"X`KT`=@`I0#8#J46"R`06M@BA02)@>`#]`'8`:4'A1SA%GD%B88@_XS* +M(((/```P0U@-8@3*(2(``H4GA1S@-G@%B(8@_H<$\@+8!*4E\`38!*4A\"2% +MA.$!V!WT#Z7/=H``6!T0AG7:'ML@@,]P@`!T)2&@#-F^"B`.&+L$AL]Q@`!H +M)0"`=@A@`2"!!J7DI038`Z4!V+D&;_^CP.!X\$2(SW"``+P&`)`0<@'>#_3/<(``O@9`D,]P@`"`D`J0$'(% +M],2E`-A*\`2)@.`:\L]P@`!P)0"`@.`4],]P@`"DD".`SW"``&0E`(!2#6`& +M.&"`X`;T\@Y@!P#8`=@P\,2E`=@L\`2%@>`L]`*%SW*``/@>(X)D@&BA(X)E +M@!S@::$GA39X)(@#@@#>-+`"V`39C@OO_\ERSW.``("00H4'A4`B`0<6>0J3 +M)(E$@GH,X`S)<\2E`]@#I0'8X05/_PP5!!`0%040"B'`#^MRSW```(D,]0*@ +M`(HC#@'QP%(-3__/=8``Q`8$A8#@H<$P]`'?SW"``'`EX*``V`^E`*4!I0+> +MR7!^#*`$Z7'/<(``D`8`@":`GA$`!J:XGAD8`,EP`-GZ"N__!-JV"J`1R7#/ +M<(``^!XC@$B!-)%3(@``Y@O@#.ESQ*7I<&WP!(6"X!WT`H4$B(#@%_()A8#@ +M%?3/VT#`BW"&""`.&+L:ANFEA"@("0`A@'^``#B.*X"AN2N@ +M!M@$I0#8!?`$A8;@!O(!V'4$;_^AP`;8`Z4`V-;Q\<#^"T__SW6``,0&!(6` +MX*7!"/0"A02(@.`9]`+8!*4$A8'@5/0%A8#@1O3/<(``6!T$@,]Q@``P1P"` +MI@@@$""!@.`S]`#8-_#/<(``6!T$@`#>Q:7/<8``9"4`@+8-(`$@@<]Q@``P +M1P'?!-H`H<]PH``L($`0!P#/<```/'A`P`780'#P```'TJ"N`$V';DI>EP*_#N".`$!=@$V`/P!=B`X`':!/0!V"'P*86! +MX0KR3*4+I0CP!(6"X!;T"86!X`3T`=@/\(#@\/4"A>()H`0#@`AQSW"``!Q" +M0@G`#P#8O@[`!N+Q`-AI`V__I<#QP/H*3__/=8``Q`8$A8#@I<&K]`*%1X4D +M@%9XSW*``"0B!"&!#P`&``"`X0'99XH@$(X`P'EP=@CTSW>``("0ZI?!BO%V +M!/(`W@;PQHK1$#`!=A!P`'?0L=#QD3&Z7`&V03:`-N8<[AS@@C@!-ASSW"``!A'P*CDI>EP +M'O``V,]Q@``81P"I`MDCI1;P!(6!X`'>$?0%A8#@'?3/<(``I)`C@,]P@`!D +M)0"`Y@A@!CA@@.`%\@'8T0%O_Z7`SW"``!A'P*CV#J`$!=@`V`2EH?$%V`NE +M`@W@!LEP`-G/<(``&$<@J.GQ\<`Z"4__SW:``,0&!(:`X'CT`H8$B(#@%/+/ +M<(``<"4`@(#@#O3/<(``I)`J#^`/`H"`X`;R'@I@!P#8'0,``,]P@`!8'1"` +M1X8@@,]P@`!T)0&``GD"AE9X!X`0<8;W`=@$IO4"````AH#@#/)1(T#`"O(" +MV<]PH`#0&S.@]@S@#Q[8SW:``%@=!(;/=8``Q`8`@(H-X`\FA8#@O`(!``2& +MSW&``&@E`("F"B`!(($&I0*%)X4"(]`*&)X8C"!8$$!P6!1!`)``'%B!``06(42``@$`C`@<:\@#9 +M2B3`<"AUJ"#``?`B0`,!Y1EA`]]*)$!Q`-VH(,`!\"+``P'G'64P=<3W&(N" +MN!BK`-W/=X``I)"EIQ@4``%`)4$`$'$GID?WAQ0`!E$@0(`&\@'8F@\@!PRF +M3O#V">`&"X8+R`0@@`____\#"QH8,$(-X`BKI@+8`Z8"AL]R@`!P)22(@.$/ +M]">&'.`V>,]Q@``D(B>)!(@P<`'8P'@`HBGP((*`X07R`=@#IB/P)X8V>!P0 +M!`#/<(``!(,$$`4`SW"``'C'!8`,'P`1!2A^`4`I@'*0<,HBS@?*((X/``"- +M#,HCC@\``$X%B`-N`,HAS@^DIDD&+_\!V`P6!!`0%@40"B'`#^MRSW```(X, +M90-@`(HCU07@>/'`SW"``'`E`("`X!WRSW"``)PJ`("`X!OT_@@`#H#@"/0+ +MR`4@@`\````\"QH8,#()``Z`X`GT"\@%((`/````U`L:&#`+R)"X"QH8,"8* +M0`;1P.!^X'CQP&8-#_\(=<]VH``X+@>&SW$``"0IJ+@'IKX)(`<-V(#E`-_$ +M#Z(1RB`B!,]U@`#8*D45`18'AB5X!Z;/<(``0"(LD,]P@`#X'AZ0$'$+R$4= +MV!,*\@4@@`\```#4"QH8,`O(D+@&\`4@@`\!``#\"QH8,#H)3_\`A;"X4@V@ +M$0"E@.#8#((1104/__'`SW"``$P3#X"`X`_RSW*?`+C_':+/<8``.!T$@0'@ +ML[BUN+BX!*$6HF(/@`"#X`GTSW"``-Q#8@J`#[()X``%V,]P@``X'0"`[[@& +M\@#9SW"?`+C_/:#1P.!^\<#/<(``3!,/@(#@#_+/"SN+6XN+@$H1:B!@^``(?@"?3/<(``W$,&"H`/5@G@``;8SW"``#@=`(#O +MN`;R`-G/<)\`N/\]H-'`X'[QP,]P@`!,$P^`@.`/\L]RGP"X_QVBSW&``#@= +M!($!X+.XM;BXN`2A%J*J#H``A.`)],]P@`#<0ZH)@`_Z".```MC/<(``.!T` +M@.^X!O(`V<]PGP"X_SV@T<#@?O'`SW"``$P3#X"`X`_RSW*?`+C_':+/<8`` +M.!T$@0'@L[BUN+BX!*$6HDX.@`"(X`GTSW"``-Q#3@F`#YX(X``!V,]P@``X +M'0"`[[@&\@#9SW"?`+C_/:#1P.!^\<#/<(``3!,/@(#@#_+/"SN+6XN+@$H1:BE@Q@``38"B4`@,HAP@_*(L('RB""#P``WP[* +M(X(/``!Y`;@`8@#*)&(`SW"``#@=`(#ON`?R`-G/<)\`N/\]H-'`X'[@>.'% +M`=O/.!XX'C@>.!XX'C@>.!XX'C@>.!XX'C@>.!XX'C@ +M>.!XX'C@>.!XX'C@>.!XX'C@>.!XX'C@>.!XX'@&N$4@S0#/<*``[">FH`J` +M`-L`L7ZRX'_!Q>!X\<">#<`"W@E`$98/`!&`V<]PH`#0&S"@T<#@?O'`:@H/ +M_QIP`=\`$!(!%/!:=1+P%2#`(Z"0`A`1`0'GUW4``/O_\']T]@PB@*\``/__ +M"?+/<```^_]2<.SUB0(/_\]V@`!0'0"&`>"!X`"F"?0!V<]PH`#('#&@:@Q@ +M$2AP!KV!O4`I`"2E>,]QH`#L)P:A`(9"($"``*;<]<]QH`#('`#8$:'6\>!X +M\<#/<(``0"X`@('@RB'"#\HBP@?*(((/``"O$\HC@@\``/,!RB0B`%P'(@#* +M)0(!&@@``-'`X'[QP'X(P!`*"\`-T<#@?N!X\<"&"0__SW"``/@>`X#/LFY)7K/<:<`%$A-H46``8`*NLFX9'I% +M>`ZASW&``!`Z#HF&(/\!6VC/<(``^'],J$^),(E`(!,#AB+_`4.ZAB'_`4VH +M0[GJ":`'+JC:<,]P@`!0'0"``>"!X,]Q@`!0'0"A"O0!V<]PH`#('#&@7@M@ +M$2APSW$(`(<0SW"@`.PG)J`#V`#9,B-5(#IQ3"4`HI0`*@!:<$HD`"`:\$`E +M@0$P>0:Y@;D0OR5_SW&@`.PGYJ%`)H$!,'D&N8&Y$+@E>,]QH`#L)P:A0"14 +M(,]P@`#`*B"`8'D&V))P]@`.`!$E`*7T\ZIP=@A@!8IQ&G"J<.(.(`6*<9AP +M0"A`(1!X$+B!N(>XC+C/<:``["<&H4PD`*`F\DPD0*`4](HEQ`:*)H0((O`* +M(<`/ZW+/<```L!.*(\4-"B1`!=T%(`!*)0``"B'`#^MRSW```*XHBB-&!DHD +M``#!!2``"B4`!8HE@@V*)D(/`-\$VY]SZ7"H(``,8;M`+H(A0"P!`5EA=7D` +M(TT!QW&``/220I&P?0:]@;U<>A"Z17W/FHD*1P+IX>N5Z4']#D0`C +MC0&P?0:]7'J!O1"ZI7K/=J``["=&IB.1P+EX>25X$'AH\4(B0""`X+(&[?]` +M(4$@SW$(`(80SW"@`.PG)J#/<(``4!T`@,]Q@`!0'4(@0(``H0?TSW&@`,@< +M`-@1H:$'S_[@>/'``-B-N#(/H`P&&A@P#,R&(/^*"/+/<(``U#8`B(#@B`]" +M!-'`X'[/<0,`0`W/<*``J"`MH,]Q@`!`"$"!`6H`H<]PH``X+@6`!""`#\`` +M``#7<,`````*\DC8SW&?`+C_&J%;H6G8&+@9H<]R@`"Z"?+&N@JZSW"``,!#R@Q@ +M#UEAT<#@?N!X\<#AQ<]S@``41`:#`X`@@,]P@`#4*ZB(2HA$+3X;`"&`?X`` +M4"M5>$R0:'`*NI(,8`]98!XSW"``-0K2(@JB,]S@``41$0J/@L`(8!_@`!0*S5X3)`B@VAP"KI%!&`/ +M66'QP/X-S_[/<8``>$HA@:/!0L'/<8``^!X5(1```!``(,`0#@:`YB\H@0-. +M((T'6/(2;19X`""2#X``$+\&$H`@SW&``-#!%GD`@2*1CN4('$0PRB!A``;R +MBW(.#B__`L&`X#?R`-C/<8``7`=`@0\@0`,O(0H@!"&`H`"A!O2`XL0((@C* +M("((KWAF#F`$$-D`WP0:Q".*(0@``!I`(*EPZ7$6#^`&#]H`$`(@P!(`!@0@ +M0`3`&A@`SW"``%#"MGC@H.&@SW"``'"^M'C@L!`F3I,O*($#3B"-!ZSU?07O +M_J/`X'CQP.'%"'4$\-X*0`]""V`/J7"`X/KUB07/_N!XH\%`P$'!!12!,`#8 +M@>%"P@WR@N$'\H/A#?0AP0#8#R!```,4@3`/($```A2!,`\@0``&%($P@>$. +M\H+A!_*#X0_T(<$#X0\@0``#%($P`^$/($```A2!,`/A#R!```D4@3"!X0[T +M`A2!,`JY3R$"!`,4@3`,N25Z(<$.N45Y)7@@P8'A"/0'%($P(L(&N0BZ17DE +M>.!_H\"CP>'%0L$)%($P0\*#X4'``-@*]H#AR/8*%($P@.'$]H/AP_8!V`<4 +M@C`&%(,P4',&\B+!,'/,(D*``_0!V"'%@>40]`H4@3`CPW!Q2O8+%((P4'', +M(ZJ`A/:`XLH@:0"!X`WTBB')#\]P@``T!R"@@>7_V*SNK6ZN+I$H%:A +MSW:``(!%SW>``,A5`_``%040`85`+0$")7@@AC!P`_(`IO`G0!%`>(#@\_// +M<(``.!T`@%$@@((&\@#9SW"?`+C_/:"Y`\_^HL'AQ4+!02@"`@=Z02@!!$=Y +MSW*``)"^QKDJ8N>Z$O0(%`,QSW6``-#!J7%6>4"!4'`%]$*1<'(&\D>)Y[KW +M\X#8`_`&B<'%X'^BP/'`SW*``!J_,F@V>3%BHL%`P4'`BW`(V9[:'MLR#V`- +M&+NBP-'`X'[@?N!X\<`(R)6X"!H8,`G(F[@)&A@P"\B*N(VXD+@+&A@PSW"` +M`/@>`X`8B('@#/0+R,]Q``!P*ZRX"QH8,`X/H`8/V-'`X'[QP.'%"'4^B,]P +M@`!L%$"`0"4`%`.Y-7E98?8(8`X*VIH/[_^I<.4"S_[@>/'`I<%!P$+!#!P` +M,1`<0#'/<8``?'XT&<`/,!D`#RP9P`XH&8`.)!E`#L]P@`!\?B`80`O/<(`` +M?'X<&``+SW"``'Q^&!C`"L]P@`!\?A08@`K/<(``?'X0&,`(SW"``'Q^#!B` +M",]P@`!\?@@80`C/<8```'Z`&0`(?!G`!W@9@`=T&4`'Z#!0& +M,,ES`-VV#"`&$!0',,]PH`"T#[R@6@^``\]P``"MWEH(``$(V`#9+@]@!IFY +M009`#O'`Q@C/_L]R@`#X)8#ASW6```Q'#O(`H@"%@.`3].X,8`$/V/8+X`<( +MV`'8`*4+\`#>P**R"V`!#]C&"^`'"-C`I?$`S_[@>,]Q@`#\*0"!'-K/`6(H.`,],]P@`"\!@"02'2`)$03`*P>VP/P&-MBH54B0`UX8`6A +M\05@#2APX'CQP`H(S_[/<(``3!,#@(#@#_+/"S +MN+6XN+@$H1:BSW"``&@&0(#/=H``Q`^@A@0B@P\/``#@!".!#P$````2:61X +M!WV@IIAU!"*.#P```$#/=8``P`_@A0.^9'X]><=_X*4$)`T`!"*"#P```(`& +M(T`#17D"N>1^!".##P(```#&>&1Y)G@O*`$`3B!!!(;A#1I8,`?RSW"```"J +M#I"`X"CRSW"``"0'`(C/(`V07T28.XXH+W`=F`X0'="?3/<:``B"`5>:"A%/`& +MV-OQH@Y@#`8:6#.6#X`%@.`*]`#9D;G/<*``T!LQH+H,H`^I&1B`'1F8@)H-8`P& +M&E@S+0:O_@#8X'CQP.'%SW"``&@&`(`$((`/#P``X"\M`1#Z#N__3B5`%`HE +M`(`-\@HAP`_K#HHCQ0HE`^__3B5$%'_8"KC/<:``T!L3H7_8$*'I +M!8_^X'CQP&(-C_X(=NR(*);/<(``B`:R;RASAB/S#[9]0BL1`L=U@``0OV"% +M[;L(<@+R1&CKN8H@PR\$]!X6D!`-CE$@`("D\N.Y/?3KNQ7R_]@'K4HD`'$` +MV:@@@`,H8@`A@P^``/C&]GL$JRAB`>$O>0"K7?!,(0"AD/8*(<`/ZW+/<``` +M+26*(PL$2B1``'D"[_\*)4`$[KD'C3(B0@0`(8$O@`#XQO9Y"?)$J039`"E! +M!"5X!ZT]\$"I#R!`!&/P3"``I);VC"##K\HAP@_*(L('RB""#P``+B7*(X(/ +M``#D`LHD8@`@`N+_RB4"!.X)[__)<`B6[K@%\@*.":T#\`&."*T`A>NX&/(` +MVD>M2B0`<<]Q@`#XQJ@@P`(X8O9X!!@"!``8`@0!XD]Z`8X(K0*.":TL\$PA +M`*'*(0GR!!D"!`39`"E!!"9X!ZW=\0`9`@0`V0\A000F>`>M`8X(K3D$C_[QP-X+ +MC_[/SW6?`+C__85Y8<]S@`!L!N"CW:7/V.E$!I8@`'8L0./_O'`1@N/_L]P@`!,$P^`K,&`X`#?#_+/"SN+6XN+@$H1:BSW&``,@D&8&AN!FASW&```R1`I%!D1"X!7H" +M'(0P,+HH@00`(;$$``&SW6``,@Z42!`@0?T6@C`!`'8W!T`$`&&&(A! +M'1@0Y@X@`1C8SW"``-0K*(C/<(``9"M$*3X+-"!`#E$@`('*(`$'RB$A#,HB +M@0\``)``RB.A![0.(0W`*R$&SW"``#@=`(#ON`7RSW"?`+C__:"U`J_^K,#Q +MP/8)(``!V,]P@`#8*B"`Z[D/\L]P@`#X'@"`Q!``!E$@0($%\E$A@(($V`+R +M`M@J#P``T<#@?O'`#@J/_E$@@,&EP9@/X@/*(*(`"\B0N`L:&#`Z#J_^`-W/ +M<(``W$,F@".!((&,O1(((`^Y80#9SW:``-@J_!Y`$,]PH``L(/"`SW```%0< +M0,`"V$'``=A"P$/!1,$%V03:`-N8<[ASV'-N""`$`"='$P"&B[@`IOT!K_ZE +MP.!X\<"."8_^42"`P:7!&`_B`\H@H@`+R)"X"QH8,+H-K_X`W<]P@`#<0R:` +M(X$@@8R]D@_@#KEA`=K/=H``V"K\'H`0`-G/<*``+"#P@,]P```,'4#``MA! +MP$+"0\%$P2AP!=D$V@ASF'"X<-APZ@_@`P`G1Q,`AJNX`*9]`:_^I<#QP!() +MC_[/=8``V"H`A>NX!?*2"X`%@.`*\@O(!2"`#P```#P+&A@P+@V/_L]SH``X +M+@>#P[B/X`_R'!,$``HAP`_K`'\HC@$?0`A5$@`((-]"8+@`6`X`GRSW"``!1$!H`#@`"`U@G``!X+@`6` +MX`KRSW"``-Q#.@[`#HH-(```V`GP"(Z)X`GT`(51(("`!?0`V)H+H`>,N,D` +MC_[@>/'`0@B/_GIP@>`!W<(E01.!X`'8SW>``/@>((?`>,@1#@8AAT0FOI'( +M$0,&!/1$([Z!$O(*(<`/ZW)`*PT$SW```,L;BB-'#`HDP`2A!:__!26%$\]Q +M@`!`(EZ7+)%0<0?TSW*``,B;08)0<1KR(@H``.H)(`"I<*H)``#P)T`3Q!`! +M!JEP);G`N1H(X```VH(.0`\+R)"X"QH8,/X+C_ZJ#J`0`=CN"6`*`-CF"6`* +M`MC/=J``P"^I%@"6JQ8!EJH6`I8%>:P6`)8`W:T6`Y8%>JX6`)8%>\]P#P`` +M^`0A`8`$(A``!",1`%IU%_(O*D$`3B*`!\]RH``,+?`B`@!1(@""`-H/(@(` +M!/1%?03P!2*2(`8A@8#K]<]P@`!`(BR0'I<0<0_TSW"``-@J`(`$(+Z/```X +M$`7TR@Y`!8#@*/1,(T"@"_2E%@&63R(`(8:X!GFE'EB0#_#/<(``0"(LD!Z7 +M$'$)\J46`)9%)4$1)GBE'AB03"-`H`GRSW"``$`B+)`>EQ!Q!/)."2`1!=A, +M(T"@"_2E%@"6125!$04A`00E>*4>&)`Z\$\B`"&&N,]Q@`#0*TB!!2!`!`5Z +M`-@(H0&'P!`#!H#C+RC!`$X@C0%7T@A<]R@`#L +MD4AP@"$#`"H,X`T#VB"%SW"``-`Z@"$#`Q8,X`V#V@T&3_[@>/'`H<'/<(`` +M2$D`@+8*H`9`P(MP!-F]VA[;P@D@#1B[H<#1P.!^X'C/<(``T"O@?P:`X'C/ +M<(``O"O@?L]P@`!\!^!_`(#@>/'`1@UO_@#92B2`<.!XJ"``"L]U@`#4+'#< +M`B4"$T0I/@P*(%VV2BSW,"`&P&8Z(!VV6BYJ)")0(>`")` +M#L"@!MI$H,]R`@``!T.@9:#FH`'A405/_O'`X<7/<(``?`?/<8``3!-O@4"` +M@.-/>`_RSW&?`+C_?:'//'` +M*@SO_P#8SW"``-@J`(#@N`;R[K@']`C8`_`!V'()``#1P.!^X'CQP%(,3_ZE +MP5$@@,$"W=P)X@/*($(#"\B0N`L:&#!^"*_^`-_/<(``W$,F@".!((&,OU8* +MX`[Y8<]V@`#8*OP>0!,`V<]PH``L($`0!P#/<```L!Q`P$'%0L%#P43!`=@% +MV03:`-N8<[ASV'"N"N`#`"?'`P"&@+@`ID$$;_ZEP/'`T@M/_E$@@,&EP5P) +MX@/*(*(`"\B0N`L:&##^#V_^`-W/<(``W$,F@".!((&,O=8)X`ZY80/8SW:` +M`-@J_!X`$`#9SW"@`"P@\(#/<```:!U`P`+80/'`_@I@!>'%@.`+\@O(!2"` +M#P```-0+&A@P?@]/_L]U@`#8*@"%Y[@&\J>X`*52"Z`.`-C/<(``0"((B(G@ +M"/*(X`[T`(51(`"""O3/<(``%$0&@`.``(`^#(``:0-/_L]Q@`!\!P4&[_\` +MH?'``=G/<(``T"L@H,]S@`#X0P:#`X`@@,]P@`!8'06`0(!H<-6Z\@C@#EEA +MSW"``,`'((`$B:"X!*G1P.!^X'C//'`F@E/_L]P@`!,$P*`!Q(/-H#@#1(.-@$2$#8/\L]RGP"X +M_QVBSW&``#@=!($!X+.XM;BXN`2A%J(&V`T:&##/=:``%`0*I0F%@.`G\@/8 +M$*4$I<]P@``(RJ((8`\#&A@PDMD#R)"YH!A``'(,X`,`V`F%@.`/\B@5!!`D +M%040'M@*(<`/ZW*,N+$&;_^*(P0&!QK8,P$:/<(``.!W*I0"`42"`@`T: +MF#,&\L]QGP"X_P#8':%%`4_^\<#AQ0AU`@S@`!38SW"``/@>`(#$$``&);@B +M#N``P+C^"V`'!-BN#^`,J7!&#(`,]@F`#"4!3_[QP.'%@N"AP2(!+@`(=<(, +MK_\`VL]RH/X4!H#@SW&?`+C_!O1(OQ@>$Y]'8.;_Z+<"#` +MSW&``+2%4R`"`(8@?P](J1QX":GL\8[A3@`%`,]P@`#X'@.`&(B!X!_RSW*` +M`.B"2'`^#F_^!ME`(@`",@YO_@;9#)*!N`RRO_&$X8WWSW*``.B"0"(`!18. +M;_X$V0R2@+@,LK'Q$]@"\!S8\0`1A_\HE +M(0!1)$""-?0`V<]S@`#4*RBK`=I)JTJKC!U$$`K;CAW"$(?@`MN/'<(0!O*( +MX`[T420`@@SR[!4`$6JX$'B0'000"MB4'000!_!DV)`=!!"4'400DAU"$)8= +M@A!5)<`85B7!%?(,H`T+V@"%B;@`I0'8`:7/<8``^!X`@<@0``:&('^."?0! +M@<@0``:&('^.K`X!`PB.A^`(\L]QH``X+@>!J+@'H:$&#_[QP.'%`-C/=8`` +MY*A*)$!Q)(6H((`"`-L/(P,`"R'`@`/T`>`%\&9Y`@F@`"2E!(6`X"`)X0#* +M(&$";08/_N!X"',X8-6[U;DP,=%@@'@R;@B>GIB +M%KC@?T5XX'CQP,(-+_Z8<@AUSW:``$"&]"9`$,]W@`#`A5$@0(+*($$`RB0B +M=,H@(@#H(&("]"8"$%$B0((#\@'@D.!&``8`+;O`N\]R@`!POK1Z0"N%`F"2 +M!+V&)?@3B;T/(T,`8+(`VA9_0*=!I\.YI7D%(4,!%'Y@ML]Q@`!@AA5Y`!D` +M`0+P@-BE!0_^X'[@>/'`X<7/<8``T(I!B<]U@`"`%8#BSW.``)PJ((,&\@'8 +M`*6"N2"C"?``VD"EHKF`X""CG`_""0#8/@QO_PAQ1@C@`0#8:04/_O'`\@PO +M_IAP`Q(!-@"1(8%`X/2YP""B``/@!""`#P``_/_/<:``U`SW6@`,@?42!`@,]P@`#XQR&``\@/\J`5`A#X%0,08GD" +M(E<`=A`!`2\GR"59803PA!`7`>)Q.AC$!1^%$''%]S!XH@D@!@+9`=G/<*`` +MU``4A%0`#R"&``!`2`4/!N!"8`'(0 +M`0&Z$``!`B$4!FX-X`9$P('@"_3/<(``#!T`D('@`=C`>`RX0L`"\$+&`\C/ +M<:``U`=9@(@9@`"D$`$`V:"X&((#NAB$`[>YI!A```/`]K@(\L]RH`!("$`B +M`",'\$`B`"'/04E52`G:,]S``#\_V1YSW.``/C'8X,( +M(<4`SW.@`-0'%:,`&D`%`B(`)0^C`B5``!NC\*//"F`SW*```"J.V-IH*05`!#X%0$0 +M@'`B>$7``=C/<:``U`L0H0/`-;C`N!>X`""!#P`.``#/<(``^,<"@`*X*^`$ +M((`/``#\_R5X['$`H0$2`3;L<""@SW"``/C'(H#L<""H#<@4(@$`,(GL<""H +M['#`L`/(E!`!`.QP(*`-R/`E`0#L<""P['#`L.QPP*#L<,"@!Q(!-NQP(*`# +MR""05!```1"Y)7CL<0"A`Q(#-@*``@0_R,HMPB\]P@`!0P79X`(B&('\, +M''@$N"5X`O"`V.QQ`*D#R#MV,(@S$(``!+DE>.QQ`*D#R!IV/)#L<""P`Q(# +M-L]P@``\19P3`0%O@R:YP+G`NPRY#;ME>2"@#1(!-@`A@`^``"BJP*C/<(`` +MK*DV>#1ZP+("D,`:A`,5)4$`>!H$`,]P@`#X'@2`&I#0&H0#1L#/<(``^,<" +M@,"A@.#*)XX3'@,N`,HACB/)=`_[@`WNCS+_``WOJXRB:" +M'P```0+YN,HF@A\```("_+C*)H(?```!`H#B"O+/$DHU$A@*!#]!X:6(,=$@"&!QH8,!T2`(9* +MP!T2`88$R""@'1(!AB&@'1(!AB*@'1(!AB.@'1(!AB2@5B4`$AX:&(`=$@*& +M0"\`)%!Y!2$5``02`3:&(O,/`!$2`8PB#(`!@4/`%_(:V!;PSW"``/C'"!`$ +M```0!0`*(<`/ZW)7V,]S``",$]$$+_^,N`#>T?`@V)IP`W`0>'(9!```WDP@ +M`*`#]`/(CO0'R`#!!""`#_$``/`0<93T +M`\CI<`,2`3<0N25X['$`H0K`0"%9,`$:&#`$R`,2`39! +MQ0,:&#`$&E@P(8``D`'%-+G`N31X`^!`Y00@@`\``/S_'64-$@$V!_`5(D`P +M#A``!@)]%2)`,`X0``80=7;W`\S/<9\`N/\8H<]PH`#\1%V`!"*^CP`&``!A +M]$P@`*`,\@3(4(A3(L$`AB+^`T2ZQ!B"`#"HSW"@`!0$Q*`'R,]QH`!(+!VA +MSW"``/C'`H!`(%`@$G`,!`%H;3Q!H&!OP'@!J&P\>"_"/+/<8``/#P%@0'@!:$Z=P/(Z7'( +MN0B(#+@E>`,2`3<0N25X['$J=(0D`I$`H4`A33`;\L]QH`#4!X`9P`0#S"IR +MR+H0N$5X['(`HLRA`=@4&1B`S@\@$`'ESW&@_H0`SW"?`+C_-J`#$@(VDA(` +M`>JX!!(!-@;TDA$#`5$C@((V\JJXDAH$`)(1``&JN)H-(`F2&00`$-G/<*`` +MT`\0&%B`)!`!AL]R@``(SD62,'D"ND5Y#!A8@!39$!A8@,]Q@``(SF>11I$8 +MV1"[97H,&)B`$!A8@,]Q@``(SFF12)$0NV5Z#!B8@`;PSW"```C.RJC/QP(*`!Y<]P@`#XQP*`$'6W +M]\]P@`"HDB20E.'`(88/``"3`,]PH`!H+/`@0`#/<8``/$4@@<]UH`#4!R5X +M#:(#V!*EB@A`#%$G0)(%\IX.K_\!P`;P`]@3'1B0%!V8DTP@`*`7\L]PH``L +M(#"`!<`P<`'=RB6&$P0@CT\@````SW```#45[@H`!8#ES"(X'/<(``$#H0B!"X,B&!#P``V`*?N(#A`=G`>0^Y)7C/<:`` +M_$0-H4PC`*`-\L]PH`#T!V`8P`3/<8``/#P#@0'@`Z'/<(``J)(DD)3AP"&& +M#P``DP#/<*``:"SP($``SW&``#Q%(($`VL]VH`#4!R5XSW&@`-0+#:%,IAX) +MX`X&P!D6`);`X*(`#@`-S%$@0(!+\@/=(!Y8DP'8%!X8D`02`38`%@1`!QH8 +M,0`6!4`!&E@Q!,J6),0B5,@P@"&(/X#1+C$&0(`4*G/`##1("-@3(SW&@`"P@L!```2^!9.`P<,H@A0\2*`@`A/?/<``H"``&&A@P +M`-X-S`0@@`\```((@N`*]`02`3:*(`0``@Z@"9@1`0`-R,]Q@``0JL]R@`#D +MJ!1YP+$FDL]P@`#XQP*`&6$P>2:RK=C/<@"[`+M"#R`'!;@#R!J0<@Y@!PT2 +M`3;/=8``J)($E<]V@`!@O?0F`1`&E3!P%/(&":`%!\@*(<`/ZW($E0P5!!'/ +M_]J\#@>%$@0,/QP.'%)_+/<(``^,H1#8#J$!V!49&(`J"R`00=A1($##$_(!V<]P@``(12"@P@R@ +M#@'8SW&``#0]#8$!X`VABB4($B[PSW&@`/Q$'8$Y@00A@H\````(`-T']`0@ +MOH\`!@``&?(`W?JXRB6"'P```0+YN,HE@A\```("@.(*\L]S@`"X/%"#BB4( +M$@'B4*->"P`0!O`#V<]PH``4!"6@_0#O_:EPX'CQP'X(S_T(=<]V@`"(#0". +M@."HP5CTBW?I<,]Q@`"<2B()[_T@V@'8`*X`V(^X"QH<,`#8%1H",,]V@``` +M`-=U``#^RJ"V!?0'P("X1\#/<*``K"\:@%(@``!1(`"`"/(!EH"X`;8'P(&X +M1\#/<(``"$^@B#H/8`2JKL]Q0W6H$D#!BB$:"D'!*XX$ID;`Z7!CP0T<0C// +M<8``?#Y$P<]Q@`#H/47!(-D!VCW;'@Q@#!>["-@J#F`%`=D"V<]P@`!$)22@ +M(0#O_:C`X'CQP+(/K_TO)`<`@.#/BHZ.CS"$A@`'9RB$A`,]U@`!`(LB-B>87],&#Q!8.%E$F0)$1\LR5 +MOI.Q=@WR@>`*\H#@"?0`@\00``91($"!`_0`V8'BRB$A``#>SW6@`+0/W*7Z +M"6`&B'#/<(``1"4$@%$@@(`1\L]P@`!4.`"`@.`+],]P```6"88(P`2!X`7T +MI@L`"PKP`-F>N<]PH`#\1"&@X'C!H-RE@.=$#&(`RB!B`+X/@`2`X`3T!@]` +M#@3P-@]`#L]U@`!<#@"-@.`&]*8-0`T!V`"M)0>/_?'`L@Z/_<]V@`#4*RF. +M:(XP<\]U@`#8*AIP!?0`A>RXD_)*CL]P@`!D*T`@D0%$*SX+)W!5>`:(@>`H +MKE3T`(7/=X``4"M$*3X+0"*I"@N2JP"/"`X0;RN@Z`!0"%C+@`I0B.1"@^"S(A0"Z!X`'8PB`! +M`*8.X`\*KBB.2HY$*3X+`"=`'E5X3)"`XL]P@``41!/RSW.``$`B:(N)XP?R +MB.,-]&"%42,`@@GT1"D^"S(A02Z!X23R"G$E\"UJ"KDB\$0I/@LR(4`NSW>` +M`%`K@.#*(&(`0@[@#PJN"(Y*CD0H/@LO<3AG57A,D(#BSW"``!1$X_,R(4$@ +M@>'?\T`J@0("<@`"P@4(<`W@:%1Z6Z#J_]#B"` +M``BEQ+40A\6U!J5U!:_]"(7QP,]Q@`#DJ``1!`"X<,]R@``4,4`L@``5>!4@ +M0`$P(@8`3"8`@0OT"B'`#^MRBB#,#'$"[_Z*(\4"3"8`@"OR3"9`@!+R3":` +M@!?R3";`@!#R"B'`#^MRBB`,#8HC10M!`N_^"B6``48*``#1P.!^(@D``/SQ +M3"1`@`/8`O(`H0#8!:$$@:"X!*'&"&```]CN\>[Q\<#/#!P!K+,]@2*@>`&]`6*@>`!V`/R`-B`X(`(`@#1P.!^SW"``%"K()!$(0`# +MB.`T]`#;SW*``.2H9:($@E$A@(&@N`2B'O0$DL]Q@``\J0'@)($0>#!P!++, +M]P2*@>`&]`6*@>`!V`/R`-B`X"P``@#/<(``[#0#@(#@"_(#V`KPSW&``.PT +M`X&`X`/R8Z$$V&4!``#@?N!X\<#//'`4@N/_>X/S__/<(``$*E@@,]R@`#DJ*B`8*+/=H``9#$$@JBB +M`-G`AJ"X@>8EH@2B$O2"X\PCXH`3](#ESW"``.PT(Z`%\I(/S_\)\&(*```' +M\`';8*(HHJ"X!*)A`X_]X'CQP.X*K_T`V,]QH``L(%"!SW:``.2H)([/=8`` +M$*F!X0BE!?0ECH'A`O(!V(#@)?0JAH#A'/(&ADX,K_T.((``SW$``!`G,'#0 +M]\]Q@`!XQR6!F2'-"C!P2O<%\,]P```0)PBE`M@(\`#8"/`)AH#@]O4!V`"E +M`=CA`H_]SW*``.2H)((/(0$`)**I!B``"=CQP%H*C_W/=J``+"`0AL]U@`#D +MJ`>ESW"``(Q"_@_@#0#?`!4%$$PE0(`2\DPE@(#,)>*`/?(*(<`/ZW**($P- +MBB,(![$'K_Z*)(,/!)6`X&SRS@S/_\]P@`!XQP6`*(69(,T*,'`!V,(@#@"` +MX%[RSW"``'PTZ:#7<0``$"=O(`L`@.`2\@2-@>`&]`6-@>`!V`/R`-B`X`3R +M^@W/_T;PSW```(@3"*4^#L__/O`$E8#@&_0EE0B%@>'`(($/``"($P/R&WC/ +M<8``>,`&]`6-@>`!V`/R`-B`X`3R@@W/_PCPSW```(@3"*7& +M#<__!)4%M>2U$(:5`:_]!J7@>/'`SW&```2#08'/<8``>,/'`X<4`V,]S@`#D +MJ`"CSW6@`"P@$(4!V<]R@``0J0:C$(4@H@:BSW"``'PT`X@DJXP@@X8DJ@3R +M):HEJ](+(``#V"$!C_W@>`'9SW"``.2HX'\@H,]P@`#X'@.`SW&D`!Q`"(#` +MN!-XP;@2H>!^X'CAQ0#:2B0`=,]U@`#`A<]S@``XADAPJ"```T`C`0(4>4"Q +M%B4!$$"A0:$!X$HDP',`V:@@0`+/<(``<+XT>$"P`>'/<(``6`=!H,]P@`#H +M@DRPX'_!Q>!X!?!"><=P0````,]R@`!XQT6"4'$W]U,@0P5P<<`@C0]````` +MP""-`.!_(G@&\&)Y`B"`#T````#/,=E@G!Q-_=3($(%.F)0`5A +M+;W`O0/P_]TB"H`$@.`(\L]P@`!`(@B(A^`"V`/R`-C/<8``,)%WB<]R@`#( +MFR&",',$\B""@.$$]`'?`_``W\]V@`#X'B"&Q!$!!E$A0($K\H#E*?0CACB) +MA.$E\OH/P`V`Y\]Q@`!$.QCRSW*``)@'`H(!X`*B`-C/`0H:H-C_V&"8`$@.`.\L]P@`!`(@B( +MB.#,)6&0!O2>#^`-`=@^#(`%C"7#GTGR@.<1\L]Q@`"`%0"!@.`+\@#8`*'/ +M<8``G"H`@:*X4@E@"0"AW@E`#<]Q@`!XQP:!12!``0:ASW>``+2%"X]1(,"` +ME`F"_0N/42"`@-@,P@,V#(`#]@F`!(#@Q`HB`,H@(@:`Y0CR`(;$$``&42!` +M@1'TSW&``%PX!(F`X`OR`XF`X$@*X0O*(.$`E@H@`!78L09/_>!XX<7/<8`` +MA!4`B0';@.!AJ23RSW"@`+`?>:#/<(``6!T(@*.!8(`"@1!U`-H8],]P@`"< +M%0"(@.`#]`'8"O`!@0(C#0#7=4P`0$MY]T&I2'"!X`/T8:%"J>!_P<6BH>_Q +M@.`!V,(@#`#/_^`-J`X`3T$]TN\,]R@``PD4AP(@RO_0S9SW&``(05`(F`X`[R +MSW"``%"K`)"&(/P`C"`"@`;T!9)DDF=X`Z%")0`3*@O@!8AQ"B4`D`STSW"` +M`%"K`)"&(/P`C"`"@`P/P?_!!6_]J7#@>/'`F'"X<9S@RB+&!\H@A@\``.,. +MRB.&#P``@P#``J;^RB'&#TPE@('*(L8'RB"&#P``Y`[*(X8/``"$`*`"IO[* +M(<8/`-K/<8``P'^>NA4A00$`@0$J`@%&>-8.8`<`H='`X'Z=!^__!=G@>/'` +MX<4`W<]P@`#`?TH*+_\.'%SW*``(06((H`W>"Y9-C*($$# +MX;G//'`X<4`W<]PH`#`':"@J7!J""``J7'/<(`` +MA!:CH*2@*0-O_::@X'CQP.'%3@@@``AQ@.``W1_RZ@R@#S+8Y@R@#Q[8SW*@ +M`,@??Q(`AL]S@`"@%C"X(8O$N#!P`=G"(4H`@.`@J\?VK:*!X0/T!-@!J]D" +M3_W@>.'%X<;/CN8#]X#F`O0`WL]P@`#(FWB(@.,$](#F!/0`VP3PPH`$V\]P +M@`#X'@.`&(B#X,P@(H',(.*!S"`B@LP@8H(#\@#8%_`*DA!V!_0+DA!SS"$A +M@/?S`=C/<:``R!\-H.!^X'C@ +M?P#8X'[@>.!^X'C@?N!XX'[@>.!^X'C/<8``P#L2@0'@$J$-R,=P@``$O>2RHSW"``(06`H@0<`N\F(((```V*((```N#X`/`=A` +MP('!VMP")P`3:@Z@#"#:J7`DV;S:'MO^#.`+&+L!V%$>&!`2\(#@$/)."2`) +M5!8`%@#?0,>I<"39O-H>V]H,X`L8NU$>V!/I`&_]J<#@>`S:SW&``-0L`N`/ +M>"<9`H``V"@9`H`+$8"`)AF"@`@1@H`I&0*``>`/>`L9`H#/<(``9BM$*CX+ +M,B!`#N!_*AD"@.!XW=C/<8``\"L$J0N)!^`/>`6I4-@&J6_8!ZF:V`BI"=C@ +M?PFI\<`."&_])-JMP<]Q@`",2\8(;_V$P,]R@`!`(@B*A^!\`@(`SW>``-0L +M"!>!D`H7@Y#/=8``9"M`)9`1`VU$*3X+)W5U?6:-SW:``-@J@>-,`@(`;)+/ +MDE!S"/1`A@0BOH\``#@0+`(!``#=J7(+\$0I/@L`(8-_@`!0*U5[ +M;),!XGUE1"D^"S(@1`Z0$#'$(P"!>`D`J]1"@^"S(@02XO +M<,=P@`!0*X#ARB%B`#5X#)`*N$'`SW"``!1$`H!"Q>EQ@B%#!4/`0"3`,(8, +MH`P-VEX.[_\-V)X.S_\J#8`/`=A$P(7!VMP")P`39@R@#"#:A,`DV;S:'MOZ +M"N`+&+L"V%$>&!"6\`+9SW"@`+`?.:!1%@$6@^$(%X"0N'`G]$0H/@LO<#(@ +M`2#'<(``4"L*(D"`RB)B`!4@@P"D%@(7;)-0$!V<(A00`U>*46`1=, +MD#!RSW"``%@=!_0-@`"`4Q8!%A!Q9/+_V`,<`C!$+3X+,B!!+B]PQW"``%`K +M"KV`X`R0"KA!P,]P@`!8'0F`0L4@@``A``%#P.EQ0"3`,((A0P6B +M"Z`,#=I^#>__#=BZ#<__1@R`#P'81,"%P=K<`B<`$X(+H`P@VH3`)-F\VA[; +M&@K@"QB[`]A1'A@0"!>`D$0H/@LR($(N+W#'<(``4"L*(X"`RB-B`!4@P0`L +MD8'BI!Y<$`'9PB%!`#5X#)"E'AP0SW"``%@=#8``@%,>&!`$\%X,S__5!2_] +MK<#@>/'`;@T/_9@0`@`$(H$/````"#MY!"*##P```!`E>\]Q@`#X'J2!Z;I6 +M)4X45B4/%9@0@0`(\H8A_P-$N2]GB;_I<1GP42(`@KP5`A$,\L*Y@"4"&3]E +MZ(\]93"-97_P?T5Y"?##N3QY/V8^9C".Z(]%>8@8P`-E>5T%+_V,&$``\<#A +MQ0/(I!`!`)@0`@!1(0"`'V"6`"`-JL:"(/ +MP`W/B!*]`T2`3<# +MR.2Y>!`"`2'R42%`@,]Q@`#X'B2!5!$!`0GR?A`-`2)]8GT")$,#*_"`$`,! +MSW6``/"]`"-$`'"(=GU@E0`C#0&$$`,!NV,;\*00`0#TN0CR<(C/<8``\+UV +M>6"1!/""$`,!SW&``/@>)(&`$`T!5!$!`3UENV.$$`T!NV.`$`T!N6%^$`T! +M0GTG\(+@(?0#$@TV#EQ#&)"`V`ZF*0,O_7!X\<"Z"@_]SW&``/@>\"$"`%8B100(@E8B +M!`51(,"`BB`(`,H@(0"\&@0`2B0`<@#9J""`#\]U@`!,4/R*+F7D?B\H@0-. +M((,'SW"``#12;V``)4,`X*M$$H\`Y'XO+H$33B:/%^Y@R*O(@E$FP)`/\AV* +MAN'3(*8`+R@!`$X@C0?/<(``<$ZH8!#PSW:``'10+F;.9;R*Q'U8$HX`Q'TO +M+4$33B6.%\A@$*L!X4HD`'(`VZ@@@0#5@2C@#$>2\I +M00!.(8X'R64L&$(``>-*)`!Q`-BH($`%SW&``&Q.?8H)80`D#``!X&1Y+RE! +M`$XA@P?/<8``<$YI82"LP0$/_>!XX<7AQL]SI`"T12D3`(;/<8``R#K(&0`` +M*Q,`ALP9``#/<*4`"`P#@.09```.$P"&$'HPN-09``#0&8``#Q,`AM@9``#/ +M<(``C*O4B+:(Z!F``WB([!E``PV0\!G``"S@`B""`_09@``"($(#8GCX&8`` +M_!D``,'&X'_!Q<]P@`#D1`:``X`@@,]P@`#.'%X<:8<,]R@`"D +M%@6"(()F@LJX$+C*N04A`8`!@LJ[$+O*N`4C!0!G@@*"RKL0N\JX!2,'`&B" +M`X+*N\JX$+L%(P8`)/(`%`X`+RA!`$X@@P<`V`\@P``2?00@0P&D?F5^`!R` +M`]J"I'[%>WJB>8($((X!!"#``:1[Q7MYHGB"I'L$(4a@8HM_UP<;@?\'% +MX'CQP"((#_TZ<`6!H('*N!"XRKT%)0V0`8$F@`1_!R>/ +MDT''%O2`YEOT:@S@!`K8@.!7\@HAP`_KP3:"O"`X`CTSW"``*`J((!@>0W8"R>`DP7R2@NO_PK8"/"`Y@;T&@JO +M_PK85@@``-RE"-Q/!^_\H\#@>/'`X@[O_`#:SW.``*06.X.Z@P#>#R8.$*1Y +M!"9`$$(@`(#*(&(`+R8'\`'=RB"!``?R'(,D>.8.[__%>*EP$0?/_.!X\<#A +MQ:'!`=A`P,]U@`"D%@J%42``@`SRBW`$V6?:/=O&"J`+%[L*A:"X"J7E!N_\ +MH<#@>/'`8@[/_!IP*'5(=VAV.&-FV3W:"@N@"Q>Z@>`)]`IPX@J@"ZEQZ7"R +M"J`+R7&9!L_\X'CQP"X.S_RFP2AU&G)@P`#8`1P",`'8`AP",`,<`C"+<'8) +MH`>!P8#E!?($P0IP8'T%P@/!@.$.]`HAP`_K/'`S@WO_`'9HL'/=8``I!8:A5N%!'H#>__ +M"G"Q!>_\HL#@>.!^X'@`V<]P@`"\+N!_.*#@?N!X\<#B"0`$SW`!`+A4@.`* +M\L]Q@`"D%K@9```;@9&X&Z'/<`$`)%2`X`CRSW&``*06'J$;@8&X&Z'/<``` +MW&.`X`GRSW&``*06E!D``!N!B+@;H<]P``#@8X#@"O+/<8``I!:8&0``&X&) +MN!NASW```.QC@.`)\L]Q@`"D%IP9```;@8JX&Z'/<`$`+%V`X`KRSW&``*06 +MV!D``!N!F;@;H='`X'[QP.'%H<'/__BB,(``C8 +M`-D>#N__*'(`V+T$[_RAP/'`-@SO_`C9SW*MWN^^+@L@`CIPL@\@`"IP@^!( +M\L]P@`#:$YJCW`>>' +MY[@'Q?\J<,]RK=[OOK8*(`(0V3X/(``J<(/@#O+/<:W>[[ZB"B`"*G`&#^__ +M*G"#X,H@(@#I`\_\\<"*"^_\`]JFP1IP_@E@#(/!`\'/<(``D`\4%`[[Y"P`3""G"`VSX* +M(`*8<]()(``*<(/@0/(#P\]P@`"P#T*%\"#!`,"E@.$,%1`0P:4(\L]W@`"X +M#_`GP!"`X`;TP*7!I0#9&?"$*@P#F@A@`"]P#B"!#P````$@I0/`A"@,(_`G +M`1"""&``+W`.(($/`````2&E!(6!X`WT`(41>(P@!XW"]\"E,7F,(0>-P_?! +MI0#8%0/O_*;`X'CQP*X*[_P$VJ;!(@E@#(MQSW```!O2`-VI<88,8`"I<@#! +MSW```!S2=@Q@`*ER`,'/<(``[`T!PA4@00``D0+!!;I*#6``17D#P(#@W``% +M`,]V@``@"-+8"+@9V4(,8```VL]P```BTD`F`1)R"F``!-K/<```(])`)@$3 +M8@I@``#:SW```"#2A,%6"F```-J%Q\]P```ATNEQ1@I@``#:`H87V;8.H`M` +M)@(2`X87V:H.H`M`)@(3!,`7V9X.H`N$P@7`%]F6#J`+Z7("A@#9>@\@`(NY +M`J8#A@#9;@\@`(NY`Z8$P`#9"+AB#R``B[D(=P7``-D(N%(/(`"+N2*&,7D9 +MX04I?@`CAB]R4'1GA!2E^`"]QS"!%@(;W`\`!Y1!U,@?._P/`$'7&]P'9 +MSW"``"`()*``V-T![_RFP/'`<@GO_`G:J<$(=MX/(`R+<38/[_PAP`AQ0M@N +M#&``!;D,%`0P`,')<`;""B6`#ZW>[[XV""`"`L.2#B``R7"#X"KR`,$%PL]P +M@``T#@#=\"!```3!"KH$(H(/#P``_,FY17GZ"F``J7)6"R`/!=@@%`0P`,') +M<`;""B6`#ZW>[[[J#^`!!\,Z#N__R7"#X,H@0@-)`>_\J<#@>/'`L@CO_`+: +MI\&:<$(/(`R#P<]P@`"P2P"`!]E%P,]P```1TIX*8```VL]P```2T@#9D@I@ +M``#:SW```!/2`-F""F```-K/<```%-(`V78*8```VL]P```!1`?99@I@``#: +MSW"@`+0/MWN^^0L>*<`3!`\(>VYAS2B4``$HF``#Z#N`!2B<``(H.[_^*<(/@W/+/ +M=8``(`@(%180#!43$`[80,!!QD+'BG`$P0/"'MN8*<`3!`\+AVYAS +M2B4``$HF``!6#N`!2B<``.8-[_^*<(/@BO(BA:.%+R#'!<]RH`"T#QRB!,+/ +MP"CP]M5?F"FSW:``*@/ +M57Y@IB+TM@V``PHAP`^`X.MR#_+/<*``_$1T$`0`9!`%`,]P``"Q$WT$[_V* +M(TD*SW```*T3BB.)"DHD``!E!._]"B4``8#@'?1R#8`#"B'`#X#@ZW(/\L]P +MH`#\1'00!`!D$`4`SW```+$3.03O_8HCB0S/<```KA.*(\D,WO$"((`E&6$" +M(8&$#_("(((D#'H2#"``+W`$P@(@`2#/<(``D`]5>""@`B7`)+E@`B%!A`_R +M`B5")`QZZ@L@`"]P!,(")0$@SW"``)@/57@@H`#870:O_*?`X'CQP/X-(``` +MV,]P```-T@#9#@A@``#:SW````S2`-D""&```-K/<```%=+/(/(```VL]P```"TJ#9FKG2#R```-H)V(RX`-G&#R```-H4 +MV(RX_]FZ#R```-H`V(RX_]FN#R```-H1V(RX_]FB#R```-H"V(ZX`-F6#R`` +M`-H!V(ZXSW$``/__A@\@``#:SW````O2`-EV#R```-K/<```#=(!V6H/(``` +MVL]P```2T@#96@\@``#:SW```!/2`-E.#R```-K/<```%-(`V3X/(```V@#8 +MT<#@?O'`1@V/_*/!BW$!W;(+(`RI83@S"!B@68/(``/V`#8\02O_*/`\<#AQ:'!BW'N"B`,`=K/=8``[($` +M%`0PSW"``)`-0"4!'Q+:Z@T@``#;`!0$,,]P@`",#:EQ`=K6#2```MO/<(`` +MM`TD;1S:W@T@``##`-BA!*_\H<#@>/'`"@RO_`/:H\&Z<)(*(`R+<0'!SW"` +M`#P.`-_T($X``L'/<(``5`Z`YO0@5`#/<(``(`C@H.&@S":BD,PF8I',)J*1 +MRB7"$P+T`-V!YLPFXI#,)N*1S"8BD@/T`=V$YLPF8I+,)J*2S";BD@+T`MV& +M#<__JG#/[[ZB"N`!R7%B#N__JG"#X';R`,"`X,P@HH%0](#FS"9BD,PF +M(I%*]`+`@.!(],]P@`"0#[5X6G#@H,]P@`"8#[5X>G#@H,]P@`"P#[5X&G#@ +MH,]P@`"X#[5X.G#@H,]P@`"@#[5XX*#/<(``J`^U>."@JG#)<<]SK=[OOBH* +MX`&I`RAJG"I<[[[>">`!BG-.#Z__JG"#X!+R +M`,#/<8``(`A`@02^!KC88!4@``7'<(``])(A@4*P([``V.4"K_RCP.!X\<"D +MP8MQ'@D@#`3:`,`!P02X-7C/<8``]`T0868-(``"P0#``<$$N#5XSW&``!0. +M$&%2#2```\$`V*3`T<#@?O'`H<'Z"R`"BW(`P*'`T<#@?N!XH<'AQ>'&N'#/ +M<(``R)L0$`8`SW"``$`N!8"8<8#@H<&&)/D`NC0&T?;IB%7K/<8``N)1(8=1^"'.&(_T/>WLZ8D&*97A(]UE +M%27-$;YAPHYE>LESAB/]#WM[N6$CB65^*'.&(_T/3"0`@'M[97D3\L]UJ@#@ +M!W.%42,`@`;R2*4)I2JERZ40\`BE2:7*I2NE"O`)ND5XSW*G`!1(`Z()N25^ +MQ*+/<8``C$4`&8`!SW"``)1%`!A``<]P@`"010`8``&AP,'&P<7@?Z'`\<`^ +M"8_\Z@B``X#@J`K!`@#>%_!PW`(E`!-$+CX7+W?>#N`,)W!")0`>U@[@#/A@ +M`-D`)H`?@`!"+""H`>;/=8``U"QK%8"0$':F]\]P@`#`0ZX.P`S/<8``V"H` +M@:&XKKA%`:_\`*'QP,]Q`((!`,]PH`"L+SR@SW"``%0X`("`X`STSW"``*08 +M`(""X`;R9@]``]'`X'ZR"T``:@B@!6_8@.`'].H*X`X*V)X+0`#R\?+QSW*` +M`%0X(((&>>!_(*+@>,]R@`!4.""")7C@?P"BX'@$*(`/```OND(IPG10>D0J +M_@("($`.$'B`X`3R`>)0>H/@0+$#]H#@`_0`V`+P@-C@?N!XM04/_O'`*@B/ +M_#IPSW6``%`=`(4!X('@`*4*]`'9SW"@`,@<,:!B"N`.*'":#B`%!]@:<,]V +MH`#L)^N&F@D@!RIP"Z8`A4(@0(``I0;TSW&@`,@<`-@1H6X+(`4*<#$`K_SI +M]L*)$`$X02O_0HE``3QP#8/3_P(=SIQ@.(:S?=(=?0G@!,5(8$C4@_O +M_PIR8;V`Y0'F-O=M!T_\X'CQP`H/3_RAP0AW@.(:<0#>S_=(=?0G@!,>""`` +MBW$`P!0@C"-AO8#E`+0!YC3W00=O_*'`\<#6#D_\H<$:<,]V@`!0'0"&`>"! +MX"AU`*8*]`'9SW"@`,@<,:`&">`.*'`^#2`%!]@(=QX(H`.SV(#@%?*+<:(+ +M;_T*<``4`#$`I0"&0B!`@`"F!O0`V<]PH`#('#&@"@H@!>EPU09O_*'`423` +M@/'`!?(J#\__`_#B"```T<#@?N!X42/`@/'`!?)"#\__`_#Z"```T<#@?N!X +M\S?=(=?0G@!/P(8$C5@_O_PIR8;V` +MY0'F-O>Q!4_\X'CQP$X-3_P(=X#B&G$`WLWW2'7T)X`3&@@@`/0@@2-AO8#E +M`>8W]XT%3_S@>/'`'@U/_!IPSW:``%`=`(8!X('@*'4`I@GT`=G/<*``R!PQ +MH%(/H`XH<(X+(`4'V#IP;@Y@`Y/8@.`9\K!]0"B/(8&_$+VE?\]PH`#L)^:@ +M`(9"($"``-D`I@;TSW"@`,@<,:!2""`%*G`5!4_\X'C/<8``^!XC@<]R@`#4 +M!S(A@P\``!\#`:(R(8$/```9`V&R2'`@L@C9<]H>V]4`(`L8N^!X\<#/<(`` +M^!X#@`F`42!`@.!^X'CQP%H,3_P( +M=2AV((5"(0&`RB%B`(#A`-@%\OX)X`RI<`'8)(6`YM`A8@#/(2(`T"$A`,\A +M80"`X"2E-`GB#,H@0@.%!$_\X'CQP!(,;_R*(@0.SW"``)`&`(#/=H``T(HF +M@$`F`!2:">`+!.$!AL]U@`#X'B*&R!T8$,]R@``D(LD=6!`AEB>J((X$((`/ +M``8``(#@`=C`>"&J!JH`WC((H`G)<,]P@`!5'?X/[_[`J&8-0`.`X`KRZ@U` +M`X#@!O1B#B_^R7`7\`+8X@J@`0'9-@F@#@+8(X5(@3214R(``&X*X`D!VP#9 +MGKG/<(``^"4@H-4#3_S@>/'`L.#AQ0AU@_:YY/'`+@MO_)AP08'DNK")._)RB<]V@``0O_)M +M]G_F9C3*]KX($84`22#```CRSW:``%#!MG[!C@/P`-['<(``4,&V>`2("",# +M``@C@P,`(T`!22##`Q9M=7C/I(6XA0&` +MI7@$((`/````"`9[`O!C@>B[F!G```#="?*D$0```-V7O9&XE+BD&0``420` +M@!SRSW"``/@>Q(#`NLB&!":.'P!````^OA[FV'I%>_Z[F!G```WRI!$``(4E +M`12,N)&XI!D``)P90`,=\/^[$O*D$0(`A24!%):]F+V-NI&ZI!F``)P90`,D +M@!"!GK@0H0OPE+V6O9P90`,D@!"!GKB?N!"A@0)/_.!X\<`."F_\`]C/=H`` +MN"H@AD!Y@.!M\B"&8'D$V(#@:?(@AF!Y`-AGN(O@"O0!Y`-A"\,]P@`#`*B"`8'D!V(#@`=C`>#CPSW6``,`J((5@>0'8@>`1\B"% +M8'D!V(/@"_(@A6!Y`=B"X`?R((5@>0'8@>#>]0'8'O#/<(``P"H@@&!Y`=B% +MX`'8P'@4\,]P@`#`*B"`8'D!V('@`=C`>`KPSW"``,`J((!@>0'8@^`!V,!X +M@>`7\B"&ZW5@>0#8&G#/<(``P"H@@&!Y`=BX<#?8"B'`#ZERE-O)!F_]"B0` +M!(4!3_S@>,]P@``,D2`0@`"!X,]Q@`#$!@OT`-K/<*``M`]!^SW"```R1(!"``('@SW&``,0&!?0"V`2A`_`!V`6AX'[QP,]Q@``8 +M"`"!@>`.\@HAP`_K#/<8``Q`8%]`38!*$#\`'8 +M!:'@?O'`SW"``$P3"H"`X`_RSW*?`+C_':+/<8``.!T$@0'@L[BUN+BX!*$6 +MHL]P@``4"``0!`#/<8``&`@`$04`3"0`@,%V!?RPN//:+/$O>83A-*@#$@(VC/;/<`,`A`"@&@``BB`(``8:&#`+\(H@$``&&A@PSW`" +M`80`H!H``.!^SW.@`+`?`=I9H\]S@`!8'6B#@.!@@P7R(GMP<(/W`-@"\$AP +MX'[@>,]RH``L('""@.`*\@(C0@#7<@"````&]U!PAO<`V`7P<'!^]P'8X'[Q +MP%8.+_R8<*7!*'>X!".`#_\````8N@5Z;WD(N?_8"+AD>"BX!7E%>0C= +M]"2``R=X1,`V"N`,$!0`,1(4`C%AO4`H`00%>4=Y1,$0%`(Q%"2`,X#E0+`! +MYBGW4R7"!4"G`!0-`0?9!_`0?10G3!``M&&Y%"1`,+M[3[T`D*5[@>%P>WA@ +M,O<$((`/````_Q"X!7I`IR4&+_REP.!X\<`V"```]@@```H)``#1P.!^X'C/ +M<8``="5`(0`#52'"!5!P1O<`V0084`!0<+WWX'[@>/'`.@R@!@#8<@\O_0#8 +MSW"``&!$2@A/_<]P@`!`1$((3_WJ#(_^$@V`"`#8I@L@`X#92@F`#)8-@`** +M"\`,!@C``8X(``,`V.(*[_X(<<]P@`"$%@"(42"`@`CRSW&@`,`=`(&@N`"A +M&@L`"^((``.J"J`!_]@^#T`!BB"%#PAQM@J@!0ART<#@?N!X\<#Z#"_\BB#_ +M#\]UH``X+L>%!Z7/<*``5"X+@-.X!B8`<`\`__^""N`-%MG&#\`!QZ4U!0_\ +MX'CQP'8+H`8!V*X.+_T!V&(.@`[1P.!^X'CQP.'%`-W/<(``7`>@H,]P@`#H +M@JRP,@K@#*EP2@T/_=X/X`NI<)(*``3B"L_]J@Y``8H@!@H(<2(*H`4(<@H. +M;_RI<.(-3_S5!`_\`-G/<*``["``I0GT`=G/<*``R!PQ +MH"X.8`XH<`"%0B!`@`"E!_0`V<]PH`#('#&@G@D``3D$+_RDP/'`H<&+<+X* +MX`T!V88)``&AP-'`X'[@>/'`H<&+<&H*X`T$V0#`42!`@-@/H@;*(*(``,!1 +M(("`8`K""P#`42#`@+@)`@<`P%$@`(&<#L(&,@_@#`'8SW&`KN`!['`@H`'( +M['$`H<]R@``4@(HD@7T`V:@@``+P(D,`['!@H`'A5@D@`0#8H<#1P.!^\<#A +MQ:/!`=A`P,]U@`"D%JEPZ@G@#5S9]@G/_CJ%&X4D>#R%!'F!P$H*+_]!P0'! +M&X4D>$'`525`'\X*+_^I<<]P@``<&,(*+_]`)0$;BW"N#^``!-D6"R__`<`` +MA8#@!?0%A8#@,`P!_UX)S_XY`R_\H\#QP.'%SW"``-!(`("BP4'`@<`!W:X) +MX`VI<4#%BW!N#^``!-D-`R_\HL#@>/'`H<&+<)()X`T!V0#`42#`@2\D!P`` +M'``Q#/('$@4V"B'`#^MRBB#%``$`;_TGVW(-H`%`V#((``$.#D`'H<#1P.!^ +M\$''`?JEP.@G@#0/9!@@``8#F`_("A0+P`(6- +M`B_\`Z7@>.!^X'CQP.'%SW6``"@=J7#2".`-$-D`%0003"1`@!'R3"3`@!_R +M3"0`@1/R"B'`#^MRC]B-N)C;>0)]>!!R^?=1 +M(P"`"?(`%@!!`_``V!4AC```I`'BA>*Z]P/,UW````!``=C"(`H`%[C'<``. +M``"#N)VXG[CL<@"B`1("-NQP0*#"#N```HFAP-'`X'[@>/'`X<7/=8``-`BI +M<"8/H`T(V0"%SW&@`+@>`J$!A0.A'@[``+D`#_P5!L``\<"DP8MP`@^@#1#9 +M`\S7<````$`!V,(@"@`7N,=P``X``(.XG;B?N.QQ`*$!$@$V['`@H`#`42`` +M@`/`!O0"P28-(`$`V@7P^@Y@`@'!"@[``*3`T<#@?@D````%````\<"R#<`` +M406`"^!X\<#AQ;3!BW6I<,X.H`T4V0#`AN#,(.*!!O1F"&`#J7`(<23P@N`' +M]`8)8`.I<`AQ'/"!X`;TA@I@`ZEP"'$6\(/@S"`B@@?TH@\@`ZEP"'$,\(3@ +M!O2B"&`#J7`(<0;PB>`>](HA1``#S-=P````0`'8PB`*`!>XQW``#@``@[B= +MN)^X['(`H@$2`C;L<$"@6@W@`"APL0?O^[3`"B'`#^MR?-B-N'?;B[M*)``` +MO00O_0HE``'@>/'`X<6BP8MUJ7`6#J`-`ME""F`#J7#:#,``=0?O^Z+`\<#R +M#L_[`!800*'!3""`H,HAQ@_*(L8'RB"&#P``CPS*(X8/``",!.'X``1(YF#&`,!&[/<(``*)`: +M@!)P$/(D%H`0@.`C\JEP!-F9VA[;T@J@"AB[`-@D'@(0&?#'=X``.(X+AX&X +M"Z?/<(``Q`8O@(#A`=H$\D2@!-@'\`#9+*!)H"2@!=CF"8`#J0;O^Z'`\<#A +MQ<]P@`#X)0"`!""^CP#````(],]P@``@C`"(C"##CP7R]@NO_0'8SW6``-"* +MJ7`:#:`-4MFR#(`'W@O@`*.%C@P@`:EP"''/<(``L$`F#$`,_MG/<(``((Q= +M!N_[(*C@>/'`SW"``,"0W@R@#0W9J@O``&(.0`;1P.!^X'CQP*X-[_L"V:+! +MP@R@#8MP`Q22,$PB@*"/]@04A3`*(<`/ZW+/<```A`R*(X4),0,O_0HD@`0" +M%(`PSW:``,0&A"H(*2]W(!X"$,]P@`!@A3/=8``2(Q` +M)1$2_66+<*EQI@M@"P+:0"4`$NH+H`U")($A`">`'X``2(P($`4`SW"``'C' +M!8!3)4$%$''*(<8/RB+&!\H@A@\``(4,RB.&#P``A`&``B;]RB2&!'8,H`=* +M<$HD@'``V:@@@`2$*0@)+W`R(`(@@.()\@@5!1`P(00@#"1`@17R`>%`)@`8 +M>@G@``39`=D,&T(@AQ4`%H"XAQT8$`8/8`,H<,T$[_NBP`HAP`_K.!^X'C@?N!XX'[@ +M>.!^X'C@?N!X\<`6#._[!-FCP0#>0L;&"Z`-BW`#S-=P````0`'8PB`*`!>X +M`""!#P`.```&%``Q&W@3X`0@@`\``/S_)7B=N)^X['$`H0$2`3;L<""@`,'L +M<""@!!0!,>QP(+`&%`$Q['`@L`84!#%1)`"`#/(!$@4V"B'`#^MRSW```$\F +M.0$O_6G;&@[@`P'8`L$`Q25X0L#/<*``+"!`$!``P+T!Y0+P`>8&%``Q$':` +M``H`@N4$%``Q@L<6]!MX$'CI`'@$'C:#N`# +MJ7+L<0"I"/#I<8/@`#1 +MP.!^X'CQP*7!BW`&":`-!=D`P%$@`(`5\L]P@`#X'@.`&(B!X`WT`-B:N,]Q +MH`#('P^A`<"D&0``P]@:N`ZAH@^``*7`T<#@?OD`8`8`V.!X\<#/<(``(#AZ +M"*`-*-F"#X``T<#@?N!X\<#AQ0`6`$""X,]U@`"D&`"E'_0`V<]PGP"X_SV@ +M'-D5\,]PH`#(.S:`1"$"!S:`AB'_""5Z-H"&(?\(17G/` +MX>OU*@^``""%A.%>``T`,R9!<(``9$A`)X!R-'@`>#@0!`!8$`4`"B'`#^MR +MSW```)DAO0;O_"_;-@]@`U3842!`@!/RSW&``%0X`(&!N!(.X`T`H0GP1@UO +M_@'85@J``P/PZ@^`!&4!S_O@>/'`+@W`"+H.@`#1P.!^X'CQP$8+8`D`V,]P +M@`#X'L@0`0;`N8'A`=G`>1(*X`T\$(``T<#@?N!X\<#AQ<]U@`#X'@"%Q!`` +M!E$@0($-\@HAP`_K#F`,`=C/<(`` +M0"((B(?@'O0!A<00``91($"!&/(&"T_]SW&``'C'!)`E@0JX,'`.\@HAP`_K +M/'` +MV@M@"0#8/@W/_,]Q@`#DFP*)6@G@#2")T<#@?N!X\<"BP8MPP@Y@#0C9`,"` +MX,]Q@`"@.`"A!_(&%``Q`[$$%``Q`K&N#8``HL#1P.!^\<#"#Z_[@=BAP6#` +M`\P`W\]V@``4"`(`#H0'8`O`"V!IP`,!Z""_\"G'/=8``K#@#$@$W7I6!V&"& +MX@_@#0HD``3/<*``+"`0@$`=0!1,(("@$:5('0`43?(`AHC@$_3/<(``;$/Z +M#``,6@IO_A38:@K@!`38X*;/<(``&`C@H`#8A>`#\@#8"/#/<(``&`@`@(3@ +M^?4!V"\F!_`.\LX+H`,4V(#@"O3/<(``4$,F@".!(($>#0`,`(:`X`/R`-@( +M\,]P@``8"`"`@.#Y]0'8+R8'\`7T.@C``H#@"_+/<(``X`<`@"\H`0#6#6_] +M3B#`!P4'K_NAP/'`G@ZO^X#8H<$#$@$W8,#/$ +M*!\`,B9%'@`F0!Y,)0"``*$-\@HAP`_K">;V\$"[_R*)(,/SW&` +M``2MP@H@"ZAR`(^$*!\`-"%!+L]P@`#AJR`!8`00'A&[)@CX0K'P``(8!_@`!,K4.(4'&,!^7_+W4`)8$?@`#(LP`E +M@A^``$BT6@Q`!WH(;_X4V((/H`0$V$"/`*)(,/'0'O_$HE``#@>/'`X<4@V\]QH`#('&FA`!8`0,]RH``0%`RB`!8% +M0`'=3"4`@,HAP0_*(L$'RB"!#P``+"7*(X$/```)`=0`X?S*)$$#&!I``6@9 +M0`$#V`^BN:%JH?H(@`"5`X_[\<#AQ:W!BW6I/'`V@JO^PS9 +MK,':"6`-BW``%``Q@.`P],]U@`"X*B"%SW:``(0O8'D`V(S@0"2/,!#R((5@ +M>0#8D.`,\B"%8'D`V)'@!O(@A6!Y`-B2X`;TZ7#)<1C:!?#I<,EQ+MHF"``+ +M`=A@'@(0%X:`X`P,X?O*("$``!0`,8'@$_1`)(`PSW6``(0O0"6!&_H/X`HN +MV@'8-X5A'0(0@>'@"\'[%@B``*$"K_NLP/'`)@JO^Q?9M\$R"6`-BW`CP$HB +M0"!3(-``AB#^`TP@`*1"*!$!#!P"-(_V"B'`#^MRO +M_`HE``1(%`4P(,!`*(X@SW6``!"_UGY1(`"`P&5!+4\#P+^^9H8@]P]>](#@ +M#?0*(<`/ZW)SV(VXBB//!%$'K_P*)``$BB!/!0IQ6@_@!*AR`<`"P0IR!@VO +M^V9N@.`]\NEP4@Z@#0IQ#12`,(4@P0`-'`(PBB#_#U/``(:IN`"F$L"&(/L/ +M*+@/KDHD`'0`V:@@``/_VKAA0"B#('9[$N!X8$"H`>$*<$X-H`V+<<]P@`#X +M'O`@P0/`$0`&#R``!,`9&``/CH'@!_2`Y\P@HJ/`"`(.`=\"\`+?<@A@`@IP +M!_"`X,HG@13*)R(2@>=0`@(`((;/<(``^!X#@!B(*'6!X(8E^Q\1\FX+@`*` +MX""&&O+/<(``0"((B(?@%/1!*4`#42``@`[R$\#HN!+""O*&(OL/02H$`D^. +MD'($\JBX4\`3P!+"!GE$>"5X@.4`IH8@^P\+\H#@RB`!!,HA(0`\"J$#RB+A +M`PX>0A0`V,]Q@`!0PA8A`01`A@"A];H!H07T`-B+N`&A]KH%\@&!12``!@&A +M]@KO_(MP#12`,%$@0($A\E@4`#$%MEH4`#$&M@66@.`9\OH*@`*`X!#R!I91 +M($"`"?(J#:_\"G!:"``.!=@2K@#8!;8'\`IP`-F^":`##]H-%(`P42!`@'[R +M4!0%,0*6'MHO(4H!,'DD>"\I`0`"(D``$'A`*`$A)7C/<:``P"^B$0&&$'@0 +M\"\M01`"(D,#`-T/)`A4!X`"NHZZAIJ*FI*:EIKBNN:X!P+JN +M`L$'I@/`**8)IH'`/@T@#0'9`<`'IGIUB_""P"X-(`T"V0&.`\$!W^.N`>`! +MK@+`*:8(IFH.K_N+<@0@``4O)`>@`MDCK@*N`,$AIF_R$FD6>,]R@``0OP!B +M2B$`(`\A42`MN%,@$`"*(%0%B@O@!`IRSW&``%P'0($O(DHD^*X0'D`4!"*` +MH!0>`!0`H0/9(ZY"I@.F!_2`X@7RX@B@!"#8^:X%V`.N(,!Z#N``$-D`P#)H +M-GD`(8(/@``0OXHA"`"BLB"B!MDCK@#9(@]@`P_:`,*`V1)J%GC'<(``$+\H +MJ"FH!]@#KL]P@`#X'O`@``3/\`0`08$(8$$P!A8``#9(*//<(`` +M<+XAHU1XE@W@#:"P@.`'\AH-P`T(V`.N^JY`(U,@(/'`BB!5"P#9>@K@!"AR0@_`"I8*0`#1P.!^X'CQP.'%`!8- +M0`/,`=K7<````$`!R,(BB@`7NL=R``X``$8((`M3)0$0425`D,]Q@`#(.@'8 +MRB`A`/$$;_L`H>!X\<"AP8MP=@L@#0'9`!0%,$PE`(`+]`HAP`_K/'`Z@H`!\]P@`#X'BP0A`!,)`"!"?3) +M$``&42!`@07R@@W``1#P3"1`@`SRSW"``$`B"!"%`$PEP(',)6*"!O3*#H_[ +MT<#@?@HAP`_K8+]P0DA"\````D#"2` +MCP```"0L\H+@5``-`(+@!O2`XB;R@N8D](#B!?+,X4``"0#/<(``P"H@@&!Y +M!M@0=A;WSW"``/@>\"#`!,,0``8!V00@OH\`!@``!"2`+P````C"(4$`*[@0 +M<43W`-@#\`'8#W@#\`'?Z7`$)($O`0``P"ZYSW*``#1:*6(P=P'9PB%-`(#@ +MS"$B@!CR0B%`((#@(`?M_P'E`A"`(,]Q@`#030AA@>`<\@HAP`_K\"#`!.MRBB-8#\,0!`9XV(VX50=O_`HE``4#$(`@ +M"&&"X`GR"B'`#^MR>MB-N(HCF0(2\>X/8`U*<,]P@`#PO18@@`0@D,]R```8 +M%0DA@0!6#R``(+"M`6_[HL#@>/'``!:!0,]P@`!<12"H`!:$0``6@4#/<(`` +M944@J``6@$!0)+Z!RB'"#\HBP@?*(((/``#:%,HC@@\``($'S`9B_,HE(@#/ +M<(``O`8`D(#@!?(:"4`--@A`#>X.``#1P.!^X'C1`^`,`-C@>/'`!@EO^P#9 +M2B0`ET@K@#3_8 +M`,`$%`$Q!Z7N#>`,@KD<'0`4#@X@``$:F#.-`&_[HL#QP`#8:@T@``02@3`$ +M$H4P"B'`#^MR.-B*(P\!I05O_$HD``#QP.'%SW6``-@JJ7"`(``X,/VCN#)]@HAP`_K2FMSW>``&0K52;`&$0I/@LG<,8-X`P+V6F-`V]$ +M*SX+,B!`#HK@RB')#\H@B0\``-$;RB.)#P``:@&J`"D`RB+)!X+@RB'+#\H@ +MBP\``-(;RB.+#P``;`&*`"L`RB++!X'@`=G3]D(@1``*)`!Q*'"H($`#1"L^ +M"P`G01X5>4:)(HDP`/>,H(0`*`X!WRSW"``$`B"(B)X`?RB.`5]`"& +M42``@A'TU@C/_(+@!?+.",_\@>`)],]P@``41`:``X``@%(/3_W>"P``:08/ +M^PHAP`_K!D%H`4!V.!X10#@"`'8 +MX'CQP/8(8`+AQ8#@SW6``-@J#O0$%000"B'`#^MRSW```+X;BB,&!$4#;_RX +M`(#$$``&42!`@1OR[!4`$<]Q@`!XQR6!"K@P +M<,HAP@_*(L('RB""#P``Q!O*(X(/``">`,$'H`P!V.!XN0&@ +M#0'8X'CQP*'!`-E`P0`6`D``%@!`@>(:\@/,UW````!``=C"(`H`%[C'<``. +M``!%(``#G;B?N.QR`*(!$@(V['!`H.QP(*`?\%(((`:+<`/,`=G7<````$`! +MV,(@"@`7N,=P``X``(2XG;B?N.QR`*(!$@(V['!`H.QP(*``PNQP0*"*"B`` +M*'"AP-'`X'[@>/'`7@PO^P+9SW>``'1%"@S@#.EP0(?/=J``["?/=8``P"K@ +MNDOR*X9$(H``AB+_#B*ZH;D4NK2Y!2"#`&5Y*Z8$((`/$``"``0B@@\0``(` +MSW&``.0%17@+H2"%!-Y@>H8X.P`4@A6!Y`=B%X#7T`(=1(,"` +M,?+/<*``1!W%H,.@Q*`I\,]PH`#('`'9/J`+AH&X"Z9J#P`&((5@>0'8A>`3 +M],]P@`#X'@.`"(!1(`"`"_(`V92YSW"``.0%*Z`+AI2X"/#/<(``Y`4`V2N@ +M"X:TN`NF+@D``+D##_O@>/'`SW"``'04^@K@#`+9%@D``-'`X'[@>/'`-@LO +M^P#:"'4H=L]PH`#4"SB`0B$!"(#ARB&,`$`F`!(0<6`/10T#S-=P````0`'8 +MPB`*`!>X`""!#P`.```';@0@@`\``/S_)7B=N)^X['$`H0$2`3;L<""@(KX& +M\.QQ`*$$Y6&^@>8`A3KWX@@``#$##_O@>/'`X<7/`/,UW````!``=C" +M(`H`%[C'<``.``!/(($`G;F?N>QP(*#/<*``%`0#V26@`1("-L]PH`#4"TV@ +MSW"@`$0=-:#@?N!X`]K/<:``%`1%H<]QH`#4"PVASW"@`$0=5:#@?@/:SW&@ +M`!0$1:'/<:``U`L-H>!^`]K/<:``%`1%H<]QH`#\"PRISW"@`$0=5:#@?N!^ +MX'C@?N!XX'[@>.!^X'C@?N!XX'[@>.!^X'C/"2_[N''/<(``*)!H$`0`2B``($PD@(#*(L8'RB"&#P`` +MD0S*(X8/``"W!]`&)OS*(<8/SW"``,0&!X"$+`@)`"&!?X``2(Q,)0"`%GG' +M@3[TSW"``/PEB@ZO_(HA#P_/<(``D"5Z#J_\(-G/<*4`"`R@@%,E39`3\H'E +M$_*"Y13R"B'`#^MRSW```)(,BB.?!YAU:08O_`HE``3_V`;P_]@(N`3P_]@0 +MN,]Q@`#D!0RAK:'.H0#9D;G/<*``T!LQH)X.X`L!V!_PSW.``.0%#H.`X!OT +MSW&``"Q/SW*``/PESW6``'0EBB3#?PIPJ"#``@]A%27#$^>#\"(.``'@_F;' +MH[T`#_LX$P0`"B'`#^MRSW```),,BB,?#.$%+_P*)0`$X'CAQ>'&SW"@`!0$ +M`]DCH`W(SW*``!"K89+/<8```*K$BA0A#0!HM0`@@P^``""J..'`JV*"%7D& +MDF"A`Q(#-L`=!!`$@J`3`0"&(<,/)7B@&P``P<;@?\'%\<#F#\_Z"'9>"B`" +M*'6`X-$E8I,!V`/T`-@$N,]U@`"8QQ1X"66!X1UE"O2*"N`+J7`"#J_]`8T` +MV`"M`845`"_[`*;QP*(.@`AR"R`+`-C/<(``0"(($(0`3"3`@1/R3"0`@A7R +M3"1`@AKR"B'`#^MRSW```,H;N]L%!2_\2B4``((,C_Q6#T`-T<#@?L]P@`#8 +M*@"`42``@@7TO@@/_/7Q_@F/_)X,C_P+R*ZXK[@+&A@P"\B'N`L:&#!N"P_[ +MY?'@>/'`(@_/^L]Q@`!`(@R1SW6``/@>WI4.)@Z0SW"``+@L#I#*)F(0#+$! +MV$H.(```V68+(`@!V)H)C_R!X!CT`(7$$``&42!`@0/R@.80\@'8"'$>#R_] +M"'(+R)"X"QH8,`O(!2"`#P```-0)\`O(KKBON`L:&#`+R(>X"QH8,.H*#_L- +M!\_ZX'CQP((.S_H-$@$VSW>@`+PMSW"``/@>+J<$@`#=1A`1`58@4@16(),$ +M#1(0-U8@%`5&(,`@`Q("-@T:'#"D$@``A+BD&@```9*BP8#@AAI$`PCRSW"` +M``"K]"!``(#@"?(!@NZX!?10(``@+R`((%,@?J!*`P$`SW:``,@Z:18`%@'@ +M!!(#-FD>&!"D&T`#`9*`X$KRSW"```"J-'B`$`$'@.%"]-`0`0%3(<&`%/1R +M$@$!X)(B?[@2@0`B?_!_X!C$`Z02`0"&(?./!O)HO_!_X!C$`W`2#P'@$``! +M(9+B>/%PPB<.$,(AS@-T$@`!.&"X$H$`=!M$`Z"S.&`0>)`;!`"^&P0`$(H0 +MJP&"`:,(B@BK$HH`VA*KEKHR\!8-(`**(`4!#X?WN/KS3X?VNE,BP`(F\H[@ +M2?>G%@`6MKH!X*<>&!`<\&2X!!(!-A!XD!D$``0B@`\```#P++AT&40#H+$0 +MJ:&Q`\B^&40#88"HJ88C_PV$NV&A$H@2J?:Z/@(!``#8EKCUN@02`3:D&0`` +M$O+:":_^`-@$$@$VI!$```0@@@\"````+;H%(@($+R"(($#P`8%1(`"!4O(T +MRE")22#$`/)JSW"``!"_]G_@8/:X,]S@`#0P@!CSW.``%#"5GM!@\]S@`#X +M'F2#>(-E>@0B@@\````(1GB8&0```-B6N/2X08&&(O\-'_*`XE+RF!&"`$`B +M`"E(8,]S@`!0DD#`(,+#NEQZ]"."`%;P"B'`#^MR--B,N%_;!;N*)(,/Q0$O +M_$HE``"8$0,`Z;N<&4`#(_*`XH"XI!D``"SRF!&``,]R@`#X'D."AB#_`T2X +M,B0`((FX0,`@PU2"9'J&(_\#AB+_#D2[>F)/>L]S@`!,3O0C@@`@\%$C`((* +M\H#B"O*8$8(`0"(`*4A@#?"`X@7T`-I(.!X]#W`KK/<8``$+]6>D%A\;D(\K@6`!8!X+@>&!`-\(!P$'B&'000 +M:A8`%@T:'#0!X&H>&!`=`^_ZHL"AP?'`R@K/^@AU1L#HO2APT``A`$AV`[A` +M()`%1"4"%B.Z!"6/'P8````!XD$O0!0$)8$?P````%A@-KG/`%\E,@`0`X8.V]`BF!(P_RSW*``!A20)(%*CX``"&` +M?P``_S\NN%T`(``985D`(``5>5$E0))4`"$`)L6WY2``"P`S:%,E`A#/<(`` +ME$[P((``!2D^``H@P`X!X`?PBN7`*.$`P"BB`,]Q@`#X'B.!P-HT@:1YAB'_ +M#B*Y.GK:>AEB,'@(W/L!S_HS:%,EP!`<>,]R@`!<4O`B```6X04I/@`*(,`. +M`>`4V8,'[__:>>!XSW&``%@=)(%!*((%U;@@@4$I@P75N0)YSW"``'C'8GH% +M@,FZ!2B^`"=QSW"``&!$`X``@.!_.&#/<8``6!TD@2"!02B#!=6X02F"!=6Y +M$'%;8TKWSW*``'C'18)980)Y`>,#\`)Y0"N`!9D'[_\E>/'`F@O/^OX([_I0 +MV47`2B``(,((;_Z&Q4P@`*4$%0$43O<%P-=QK=[OOA4@``0@H$`@4"#R]23< +M+P'/^@HAP`_K\"`!`8HC"PU,)0"`0"$"!GAB)O2H@7IB +MH*))@4&@7(E(J%V)2:@J$8(`2J@K$8(`2Z@L$8(`3*A-D4>P5Y%(L$B!!"*" +M#P`&``"`X@':P'I2J%214Z@H@<"Y+:@<\$PE0(`:]&)B2*%!@$FA2(A&"('@`0@.$.\H#B#/17%@`6.&!7'A@06!8`%CA@6!X8$,]W@`!T!@"'@.`` +MW0/R6!Y8$U@6`!9#PD#`5Q8`%D+!$-F^VD'`BW`>V\(+8`D8NU8>6!-5'E@3 +MH*?-!Z_ZI,#QP%X/C_K/=8``B(($%0400B5!`(7A-@$M`*+!]29!<(``&$A` +M)P!R-'@`>`+8`*4!V<]P@``,'2"P'@L@"2AP`H7/#"!,$``\@ +M0``"I<]P@`"P'#5X0*`8$P4!#!,&`,]P@``D-0#9-*C/<```[*Y`P`6#$!,' +M`$'`&HL[BT"#+@X@"F/#/<(``#AT!V2"HSW"``-`<)X#/<(``5*DOH/X* +M;_T"V$KP!-@`I0#8SW>```P=F@H@"0"WSW:``-`<`H5(AF>&#R"!`,]P@`"P +M'%5X8*`BI>S8,@P@!$"7"!8$$,]P``#LKA@6!1$,%@800,`%AA`6!Q!!P!J. +M.XY`AJX-(`IAAB06@!!(A@#942``@02%#R&!``KR`=O//'`X<4!W<]P@`"(@J"@`-C/<8``#!W>"2`)`+%J#"``J7!1!H_ZX'CQ +MP.'%`-C/=8``B():""```*7F"&_]`M@BA<]R@``,'7?89@L@!$"2(0:/^O'` +MI@V/^@#>SW>```P=P+>."2`)R7#/=8``B(+"I<.EQ*6*(,D`R7$R"R`$0)!X\<#/<8``B((`$04`3"5`@8SW"B'`#^MRSW```$$?F=OI`N_[ +MBB2##P&ASW"``/@<\"!``4!XT<#@?N!X\<`R#8_ZSW6``(B"!!4%$$PE0("B +MP27R3"6`@!#R3"5`@6?R"!4$$`HAP`_K`SW"``%2I+Z`F"6_]`MA+\`38`*4`V<]P@``,'2"PO@@@ +M"2APSW:``-`<`H5(AF>&#R"!`,]P@`"P'%5X(J5@H%H*(`2*((8+"!8$$!@6 +M!1'/<```[*X,%@800,`%AA`6!Q!!P!J..XY`AM(+(`IAAB06@!`!WTB&`-E1 +M(`"!!(4/(8$`"?+/`""@6?#/<(``#AV`V2"HSW"``-`< +M)X#/<(``5*DOH"((;_T"V$?P"I:,(`*`$?0`V,]U@``,';H/X`@`M2*&BB`% +M!&H)(`1`E0'8`*8S\`/8`*8Q\`.&C"##CP'?$O0`V,]U@``,'8H/X`@`M2*& +MBB!%"N"F-@D@!$"5X@E/_!OP`-D/(0$``H8&($"`$O0`V,]U@``,'5H/X`@` +MM2*&BB"%#`H)(`1`E;();_S@I@/P`J:M`X_Z"!8$$`HAP`_K`!XSW"```X=@-D@J,]P@`#0'">`SW"``%2I+Z`N#R_]`M@L\`*%SW&``-`< +M2($G@0\@@``"I<]P@`"P'%5X(*`>\`.%C"##CP':"/(`V0\A`0`"A08@0(`. +M],]P@``,'4"PE@[@"`'8`]CZ"&_\`*4&\`*E!/`!V`"E_0*/^@@5!!`*(<`/ +MZW+/<```11\-`._[BB-("_'`X<7/=8``B((#I?(,[_\%V".%SW*```P=H-@" +M""`$0)+!`H_ZX'CQP#H*C_K/<(``#)$(@,]U@`"(@@#?)[C`N!-XQK@!X`JU +M"-@Z<`#>`H4/)LX3"R"`@RWR!(4+(("#&?+&>`2ESW"```R1"(`/>8'A#_+/ +MA)L#(H209`@`'P&&A!:%3V*EQK@[@`\ER)L!1(`"`"/)7V*EQG@[@ +M`\ER!M@&\('E`MC*(&(`:@O/_T$!C_KQP,H(C_H:<,]V@`!0'0"&`>"!X,]W +MH`#('P"F!O0!V%$?&)#Z"L`,I!<`$,]P@`"\+B:`SW6``,B;8'D`V`&%@.`J +M\B38&-GV"N`,,]J!X`[R!!4%$`HAP`_K`.\@05!1`*(<`/ZW+/<```JRB?V]T%K_L*)``$`(9"($"``*8$ +M]`#841\8D(D`C_KQP"H(C_K/<(``O"X$@(#@)/+/=8``N#3AR?;/=H`` +MB$4`AMK@RB`K`8OVVN%4]L]V@`"(10"&Y.#.]HH@/P_6"X`,((9(%0`1$+D. +M#^__)7@2A0"F/0"/^N!^X'C@?N!XSW"``-0V0(C@N@CRSW&@`*PO&8&*N!FA +M42)`@`?RSW&@`*PO&8&.N!FAX'[/<:``R#L=@8#@"/*"V!2ASW``@!$4#J'@ +M?N!X\<#AQ;3!BW6I<,]Q@`#P2"X(K_I0VA8(X`&I<-4';_JTP.!XSW"``-RK +M;(C/<8``Z(*,(P*`"I%!*`(##/+KN`KT`KMV>\=S@``0OP*3#R"```*S`-C@ +M?PRQX'CQP!X/;_I4:(8B^`-/(D,"4R'"``4BQ`#/H_ABB,/#,H@ +M*0`)]@"2`-T/)4T0BB//#Z9X`+(`V4HD`'3/=H``P(7/`#: +MGKH`V<]PH`#\1$&@X'@AH,]PH`"T#SR@"\@$((`/_O__`PL:&#`+R(>X"QH8 +M,.!^X'CQP&X.3_I(=H#@`=U$]HHE_Q\3>(#A1/:S?3-Y%"$```8(K_H[>:QX +M`!Y`'JT&;_H!V.!X\<#AQ0AR`=V`XHHE_Q^`X43V,WFS?10A@`"N#V_Z.WFL>&$& +M;_HO<.!X\<#AQ<]U@`#H@L]P@`#X'B.`0(4`@1!R'_0"D4*5$'(;]`*%<@JO +M^R.%C"`"@!7RSW*``%@'(8(`VP\C`P`"N&9Y%G@AH@`@@0^``!"_`(&JN(BX +M`*$`V`$&;_H,M>!XSW"?`+C_SW&@_D@'-J#/<*``R!\\@$`0``;/<)\`N/]8 +M&``(2B3`<<]Q```(@:@@``(IV!*X\"!```'AX'[@>/'`X<7/<```___/=8`` +M!(,#I<]P@`!40NH*P`K/<(``<$+B"L`*SW"``!A#U@K`"L]P@``T0\X*P`H` +MV2"E!=@!I2*E(@@O_0;8'@@O_0G8:05/^@?9SW*@`-0'&AI8@(#@#O(9$@&& +M"2!#``\2`88"(,"`>6$/&EB`]O7@?N!X\<#"#$_Z`Q(#-@AW#1(.-L]Q@``` +MJA"+SW*``!"_U'D"N!9X!6(QB2V]@.%88,"]"_(A@^VY"?+/<8``,!ZT>:"1 +M$.6@L260@.'1]F&Y);`0BS)H-GD[8F63@.,Z8@?T)I)1(4"`7`F"^[H.P`N6 +M#B`&#<@#R`'9H!A``,]Q#P#__^(((`#I<*$$3_KQP"X,3_H#W\]VH`#4!Q,> +MV),/%A"6`!8#0``6`4"BP<]PL/X``$#!T[L%>\]RGP"X_].Y)7AVHA:B(,"< +MX`_R"B'`#^MR-=B,N,]S``#T#)AS@0&O^THE````%@%`,'D`%A%`0.%1(0"E +MP"&B``/A!"&-#P``_/\&\,]P```%#5(+@`$9%@"60B4!%!!Q-_<`($`C#QX8 +MD"`>V),9%@"6B."3]Q\6`)9!P"'`G.#*(<(/RB+"!S;8RB."#P``$0W/("(# +MQO4$(8`O````0,$#;_JBP/'`6@MO^LC:@B0#,@AU*';/<8``O$H2#&_ZBW#/ +M<(``3!,-@(#@SW&?`+C_#/(=H<]R@``X'02"`>"SN+6XN+@$HA:ASW"@`!0$ +M`=I$H,]R@`#`.QB"XKT!X!BBSW"@_A`!%J%`+@`4I7@6H")'@$()$#K/":#L__SW:```C.&G#)<,H, +M(`2+<2X.X`S)<)[P`]_/<*``%`3PH.2@`!8$0`<:&#$`%@5``1I8,03*G.`> +M](MPE@D@#`[9),#AOE,@P0"&(/X#1+C$'`(P9,%$)HT4&?*.V%$F`)&0N*`< +M`#!K\H;8D+B@'``P9_#K,HGQ1"$ +M]VAW@"?"$<]UH``8+/`ES1.Q<,HB(@"`X@KR`-@/(,``!B$!@-KU`=@#\`#8 +M@.`D\U$!;_J`)`,RX'CQP.H(3_H:<`(/X`$PV)AP*;A1(`"`RB'"#\HBP@?* +M(((/``#I%,HC@@\``,<`7`9B^\HE(@`LV!8/X`%`*($@`=Z*)0\:Q@[@`3#8 +MF'`IN%$@`(`+\HPF#YHG\N(*H`P!V&&]@.4!YB_WH@[@`3383R`!!96YV@[@ +M`338C@[@`2S8"'6&#N`!--CUN+AP&/(*(<`/ZW+/<```ZQ3CV^T%;_M*)``` +M"B'`#^MRSW```.H4U-O5!6_[2B4``)$`;_I!+0`4\<`F"$_Z"'<`WLEP8@C@ +M!,EQ`]C)=8#G&G`*\D0M/A<`(8!_@``@0<(-@`J`YPKR1"T^%P`A@'^``,A! +MK@V`"D(@0""`X`'E)_?/<(``P)#)=)VP,+R>L,]P@``<"&8*(`;`H"4`3_KQ +MP$8*0`&`X!#RSW"``-@J`(!1(("""O+/<(``I$-F#8`*B@Q@"@#8T<#@?O'` +M1@XO_>'%SW.``,@ZSW&```1%0('T$PT`4'4`V(KW^!,!`#!R!O?\$P$`,'+# +M]P'8W0&)`/%A"6&18` +MEL#@OO<`%@%``!8"0-.YSW"P_@``!7G/=9\`N/\VI5,BP00E>!:E3WJ/'P``_/^`X%8G$1(0\AJE+,`;I0+` +M'J7/<`!L!``9I0;PSW```+4,M@U``1D6`)80=SGW`"$`)`\>&)`#V"`>&)`! +MP`0@@`\```!`308O^J[`X'CQP.(-#_H(=L]PH`!D+O`@C0/3O0T2$#8-&I@S +M]=@%N"X,X`')<0W(SW&@`!0$`-\*H=X)H`C)<%IP`=C/<0``$"?/=2=[3W"R!`Q!?T42,`P"3R$_`O*$$#3B"!!P#8#R!``$`N/I4&?0#8!/1` +M*3Z#`_(!V.H,P`&`Y>WU"B'`#^MR5]B,N(HCGP%*)```Q0)O^PHE``$-&A@T +M]=@%N'H+X`$*<0W(SW&@`!0$"J%9!0_Z\<#Z#`_ZSW"@`%0N*X`'W=.Y+RA! +M`$X@CP?/<*``P"^E$!*&%!`1AL]VH``4!*JFL@F@"(#8\]@%N(#9*@O@`9^Y +M#1(0-O78!;@>"^`!J7&JI@T:6#,$\`/8!::IAH#E&_*`Y?KS02V`D`KR+R0) +M<.!XJ""``0`6`$#@>%,E39`)\B\D27/@>*@@0`$`%H!`X'BIAN?Q\]B*"N`! +M!;C_N.'U]=@%N,(*X`$*<2@>`!24YPT:*(<4#A??I<8`AP@'/<*``&"SP +M($(`E.?*(<4#A??I<8`AP@3/<*``:"PU>`2_0*#'=X``S+L5AS:'!7D7A[B' +M)7@%)0V0RB'"#\HBP@?*(((/``#"(`/\H+@"/*#X`#8RB#A`<`HH0,+\,]P +M@`#PJ0*`!?#/<(``\*D!@`5YF!U`$)X5`!&4'4`0DAT$$((5`!&0%1$1LAT$ +M$`#8@!T$$'X=!!`#R,]VH`#4!T&0@.(0%9(0"O(-R,]Q@```J_0A``"`X!/R +M&18`EKC@3_<-S,]Q@`#`.T8@@`(-&APP&H$!X)<"(``:H0\6%):`X@GR#&)``V`<2#S8!$A`V`!8$0'IP!QH8,0`6 +M!4`!&E@Q!,J`:H1N!`>`;H0,2`38!@>ZX#?)4 +M$0`!4R#`@`?TSW&``#0]&8$!X!FA`A4%$4PE`(`4\@&%[KC*(<(/RB+"!\H@ +MH@O/("(#RB."#P``M0>`!R+[RB1B``"5L'#*(

`9 +MH0\>&)4'&M@S`1H8-(GP!QK8,P$:�`V'0=!!`N"F``J7#/<8``"%H+8705 +M`A'/<8``$%KP(0``>F)0>J05`1!T'800)7BD'0`0!,@!D(#@%/),(T"@#?0! +ME;@5CQ!88""5^&`0>+X=!!!983]G#?"^%0`1"O`@E;@5@!!983A@$'B^'000 +M"'>0'000#Q8`EK0=!!"`%J7`0C3)WS""!A!+R"B'`#^MR0"D-)$`H#@0P +MV(RX`-N+NP4EQ!-)!B_[!2:%%*05`!`(=(0D&I`E\E$@0((>\@/(`9"`X!KR +M#8`1``>`X!+TT!$``6H5CQ`!X,.X^&`/>&H=`A!&#>``J7!J +M'<(3!?`Z#>``J7`/'AB5D0`/^N!X\<`^"`_Z&G``WZ09P`//<(``^!X$@-") +M\*`'R`0@@`\`P```UW``P```*'46]`W(SW&```"J%'D1B8#@#O3/<(``\+W6 +M>"*("(T0<<;V"G#.#*_]J7'A\%$@`*"&\@05!!!1)`"!0/(-R,]R@```JA1Z +M$1*%``]X22#"`')NSW"``!"_=GM@8/:X,HT'\L]P@`!0P=9X`8@"\`#8QW*` +M`%#!UGI$B@@A@0`((0$``"%``4D@P0,6;C5XSW&``-#"`&'/`/P`X7/<8``^!Z8'0`0)($H@00A@0\` +M0```/KE3)`(`'N$X>D5X_KB8'0`0"_*D%0`0C+BD'0`04-B<'0`0>_#_N!/R +MI!4`$(VXI!T`$,]P0`%0`)P=`!#/<(``^!XD@!"!GK@0H6?P!=@4N)P=`!#/ +M<(``^!ZD'<`3)(`0@9ZXG[@0H5GP42!`IT?R`851(`"!-_(RC302@C!)(L(` +MV!@]K@(\L]P@`!0P=9X`8@#\`#8QW*``%#!UGI$B@@A@0`( +M(0``22#!`Q9N-7C/RX`('0(.(`SR#A``"A=!W$$_8. +M(`"I<,]Q@``(6G05`A$)85EA,'ET'400SW&``!!:\"$``*05`1`E>)@5`1!1 +M(4""I!T`$`KR"MEV'400>!U$$("XI!T`$!;P$-G/!U$$(.XI!T`$`3P>!U$$#H([_RI<*05`!!$('Z"C!6!$!GRSW*` +M`/@>0X)4@B1ZAB'_`T2YAB+_#CIBSW&``'1.]"&1`,]Q@`!,3O0AD@`-\,.Y +MSW*``"B2/'GT(E$`SW*``/B1]")2`)@5!1!3(`2`RB""!!;TB!6!$%$E`(+# +MN3QYT2`BA0?RSW"``%"2]"!```;PSW"``/B1]"!``"&%42'`@`7RA!T$$`/P +MA!W$$U$E`((-\D0E`08CN0'A!"6`#P8````QN!EA`O`!V0/(`9"`X"3R#KX=A!`.]`HA +MP`_K?Q@>$=\H+AS"'B@,HAP@_*(L('RB!B +M"\\@(@/*(X(/``"U!LHD(@`,`B+[RB4"`<]P@`!0P=9X`X@&\,]P@`!0P=9X +M`HB,%0$0#K@E>(P=`!#/<(``D`8@@`:!H!``!H#@!_3/<(``9$4`B(#@7?(- +M$@,VAN-9\@"5K^#/%0`1SW.``"`]BKB>'000%I,!X!!X%K,!R.>B!:*8%0`0KKBON+"XF!T` +M$":!H!$!!B\I00!.(8('02K!``[A#R!``*05`1"8'0`0M+FD'4`0GA4!$:>Y +MGAU$$,]Q@`!810"A!""`#___T_:8'0`0#=B8'0(0"?`0V`;P"-@$\`+8`O`! +MV`>BF!4`$+X5`1&6""__`-JD%0(0!"*^CP```#""'0004O*,%0`0G!4!$90= +M`!"2'400[+J`'804`Q(#-@KR%-F0'400*G%^'400>!,.`0KP#MF0'400?AW$ +M$W@3#@%*<<)Y,'FR'400SW&``*RI((&&(7^/#?28%0X0429`D@GT89.`XP7T +MD;J2NJ0=@!`0N25ZI!V`$`0@@`\````0SW*``/@>9()2(``#,(,E>!"C1((0 +M@@0@@0\````0/7DE>!"B%/"8%0$0@!W$$Y0=0!">%0$1?AW$$Y(=1!"^%0$1 +MLAT$$)`=1!"`%0`1?A4"$8(5`1$:8H05`!%983A@$'BP'000I!4`$,]QGP"X +M_Q:AG!4`$!:AK0+/^>!X\`!A +M+;A3(`"`!?3/=8``R#H-\,]Q@`#X'B"!Q!$!!L]U@`#(.E$A0($$]`'9W!U` +M$,]Q@`#X'O`A``#/`/'`F'"X<11X.&#/<8``N$T(88P@PX_*(L$'RB"!#P``K!/*(X$/ +M``"+`1P'X?K*(<$/T<#@?N!X\<#AQ0?9#1I8,,]PH`#4!QH86(`.$`*&SW&` +M`$P3*(&`X0<:F#`0\L]UGP"X_SVESW.``#@=)(,!X;.YM;FXN22C-J7/<:`` +M2"Q>H1\0`(8!&A@P!,J,]P@`!\!R"`SW"``-A:X'_P($``%'@X8,]Q +M@`"@6N!_"&'@>.!_`=C/<(``F#[@?P"`X'C/<8``\#G@?_`A``#QP)AP"B'` +M#^MR"B7`!\]P``"?&7T%[_H[V^!XSW&``,PYX'_P(0``\<"8<`HAP`_K<@HE +MP`?-V`6X607O^D3;SW&```0ZX'_P(0``\<"8<`HAP`_K<@HEP`?/<```H1DQ +M!>_Z3=O@>,]P@`#@/>!_`)C@>,]Q@`#X0P:!`X#/.P3`0%AN`4I/@!`*8!RX']88.!XSW&``!1$!H$#@$"``H%" +M>.!_2"```.!XSW&``!@')('@?R"@$8C@?\*XX'C/<8``0$1&@8#BBB'_#R"@ +M!?(B@B"@`=@"\`+8X'[@>,]Q@`!@1$:!@.**(?\/(*`%\B*"(*`!V`+P`MC@ +M?N!XBB'_#R"@SW.``&!$1H.`XA+R)()1(4"`"_+/<8``J$(P<@?RSW&``,1" +M,'(&]$""4'/Q]0+8!?`B@B"@`=C@?O'`\@G``(#@H`SB""*(`X4`W\]RH``L(#00$0$\$A(`#HZ`X)P` +M*0#*):D0C"(!I)``)0#*)2419):4X\`CA@\``),`SW"@`&@L\"#0`.6B4-A% +M(4$"&-IN#F`,(-OXN,HE(A(N]`/8SW&@`/0'!:&$V@UP0+!"(@`H#7(`LD"& +M#7!`H$*6#7!`L`.%0(`-<$"@`X5"D`UP0+`&ED`H`B7#N`RX@K@%>@UP0*#D +MH0Z.`>`.K@(,H`HJ<`'=$/``W<]V@`"HD@X*(`@$E@#8SW&``,`[#JX>@0'@ +M'J&9!:_YJ7#@>/'`/@V/^1IPA"@("0`A@7^``$B,AQ$-!L]P@`#$!@*`H+V' +M&5@#!(B`X!'R`X&`X`WT"B'`#^MRR=@$N(HCG`X*)``$F0+O^KAU`H&`X!ST +MSW*``$24$Q(`AHP@PX\+\L]P@`!8'02``(`"H1P:&(05\,]P@`!L)0`8``0* +M".`)`-@-\$H+S_Z$*`@I"'$`(8!_@`!(CN(*``H)!8_YX'CQP)X,C_F#X,]V +M@`#`D!IPD_<>ECH6!1$*(<`/ZW(0N`4E!0#/<```@PR*(X4/$0+O^@HD``1` +M*`TAW64EE025$+DE>(#@.?+/<(``Q%KP(`$$1"@^)P`A@'^``(!!+W<@H".5 +M`I40N<8*[_XE>`AQ`">`'X``=$%>"@`*SW"``+A:\"`!!``G@!^``-A`1Y4@ +MH".5`I40NA"Y)7@FE1H.;_M%>8H*S_X(<0`G@!^``,Q`)@H`"EZ6'98`V0\A +M`000ND5X!B!`@`'=';8PN!ZV&?3/<8``G"H`@:"X7@Y@!0"ASW"``%@=!("6 +MVA[;((#/<(```)&BH"&@#-GR#R`(&+L0VL]Q@``<"`"!`"H"!$9X\0.O^0"A +M\<"."X_Y`-W/=H``P)`^E@\E#1`=EA"Y)7@&('Z#0?3/<8``G"H`@8"X`*'/ +M<(``O`;/<8``)"(`D$>)$'(;],]P@`"^!@"008D0*5X';8PN$T#K_D>MN!X +M\<#AQ?()H``H=8#@RB!!`P@+(03*(6$`-0./^3$'S__QP+8*C_D&"&`)`-[/ +M<*``T!L1@.^X"_)F#F`*`=C/<8``-#T)@0'@":$&R%$@`(`#$@TV(_*D%0`0 +M\K@?\L]Q@`#\.`"!@.`9\L"A!?!&"N``BB#'"5$A@,7[\\]PH`#$+`N`4R"! +M!/ZXS"$B@`?RF!4`$&X/K_X`V@,2`3:@$0``\+@)\HH@"``,&APPU@[@!"AP +M+O#TN"+R!\C0B0#:,Q&/``0@@`\!``#P02@-`\]QH``X+@>!#R)"`P'<1G@' +MH0W(=@P@#``L`!#'=X``$+\"OM9^$N??9Z"OBB`0``8:&#`#R*`0@`#$X$`* +MP0L#V<]PH``4!".@)0*/^>!X\<"V"8_YSW6``+2%`87/R&#$_)0(H\%X*9,)`"!1B$!!B&C!?21O^"F +M!/"QNK:Z0*:Z#X`+!O"6ND"F12$!!B&C"XVBN+D!K_D+K>!XX<7AQL]P@`#< +MJTR(C"("@,]S@`"TA1CRRHO/<(``4,(R:C9YQW&``!"_5GB`YD"!H8`&\I6Z +M0*&KO07PM;I`H8N]H:``V`NKP<;@?\'%X'BAP?'`42``@N'%J``A``AU1"4# +M%@0E@A\&````([LQN@'C>F($)8`?P````#:XSW.```!:2F,(8UA@02V"$E(B +M`@#`N@.Z&.*%X,HBC0\!`(D-U2(.`%!Q4@`E``#8[;T8`"$``B&``,]Q'$?' +M<04H?@`*(,`.`_`BN$$M01/`N02Y-'FI27%GM!@U`ECA6&)[L?P*&,)T201B("!D&C!?21OL"A"_"QO8'GMKV@ +MH0?TEKV@H44B`@9!HR(.@`L`V<]P@`"TA2D`K_DKJ/D```#/<8``J!T)@8#@ +M"_('@8'@"?06B0'@#WB0X!:I`_0`V!:IX'[QP)(/3_G/=8``#)$(A>"XK,%: +M\E$@P(%6]"8-K_\`WKH++_P8V(MQJ7`2#>`()-H!V<]PH`"P'SF@SW&``%@= +M"($`@,]W@`"H'4G`#($`@##92L`&AY#:'MM+P(MP@@L@"!B[P;7(I<&EW*W# +MIPH,(``"V,]P@`!XQPH0!`%,)`"`"_(*(<`/ZW**(%\&:=N=!*_ZN':N#F`) +MR7!&AP'9SW.``.@=`(.!XL!Y@.(X8`"C`=C/!!`"AJVXN@VO_P*F@.`/ +M],]P@`!XQP60@.#$]OX(```$\(H(``!*#R`(#=@)!D_YX'C9!L__\<#/<(`` +MQ$[/<8``J!TR"^`(.-HF#R`(`-C1P.!^X'CQP-H.S_\`V8+@S"!B@,H@0@`# +M]`'8#WC1P.!^\<#/<(``J!T@$`4`3"7`@(OW"B'`#^MRBB!?!5;;V0*O^DHD +M@`#/<(``_$[P($`!0'C1P.!^X'CQP"(-3_G/<(``6!T$@`#>EKZ@@`0EC1_` +M_P``W644Y0`ECQ^`````A@NO_JEP"''/<(``M#\B"\`)=@NO_MAE"''/<(`` +MT#\."\`)8@NO_NEP"''/<(``F#_^"L`)SW"``*@=)05O^>"@X'CQP+(,3_G/ +M<(``6!T$@`#=EKW@@`0GCQ_`_P``OV<0YP`GD!^`````&@NO_NEP"''/<(`` +M[#^V"N`)OV?/=H``>,<%EB6&"KCY8?H*K_X.($``"''/<(``1#^2"L`)Y@JO +M_NEP"''/<(``"$!^"N`)OV<%AA]G!98*N,H*K_X.(,`#"''/<(``8#]B"N`) +M`G6R"J_^"G`(<<]P@`!<0$X*P`G/<8``J!T`&0`$!98EA@JXN6&."J_^#B!` +M``AQSW"``'P_)@K`"4T$3_GQP.H+3_G/=H``J!V@A@#?EK_]96(*K_ZI<`AQ +MSW"``'A`_@G@"?UE3@JO_JEP"''/<(``)$#J"<`)&01O^:"F\<"J"T_YSW"` +M`*@=P(``WY:__F8B"J_^R7`(<<]P@`"40+X)X`G^9L]U@`!XQP65)84*N-EA +M`@JO_@X@0`"8<,]P@``,/YH)X`F(<>H)K_[)<)APSW"``$!`A@G@"8AQSW"` +M`*@=P*`%A?YF'F8%E0JXQ@FO_@X@@`,(<<]P@``H/UX)P`F-`T_YX'C/`X`/T"*+@?O'`#@M/^6H,S_^!X`SR"B'`#^MRBB"?!:';BB3# +M#XD`K_JX<\]U@`"H'2.%@>$"A0_T@>``V07R%(V`X`7R/@OO_R:E#/`CI0'8 +M!J4(\(#@!O0!WIH)(`C&I<*ESW"``'C'"A`$`4PD`(`,\@HAP`_K!XX'C@>.!X:2"``6\A/P!I(```]_'QP&8* +M3_D:<,]VH`#0#P#=!_`0%@"6_6'X8!`>&)`C;1)Q<@`-`"46`Y8E%@*6+R3' +M`"46`)9/?P]]3"0`@PB]I7_I]8+GS"?BD\PG(I?*)4(0(?3/=8``3)%)K246 +M`I8*K4NM)18"EFBMC^=,K:)I"/3/<(``69%Z"*_Y#=D-Y9SG"?3/<(``9I%J +M"*_Y#=D-Y1`6`)8"($$C.&`0'AB0*0)O^0'8X'CQP,8)3_FAP0AU*':$Y0#8 +MF/>+<#8(K_D$V0#`UW":"5!O"_+/<:``U`L/@62]N&`/H0'8!O"I<"(/[__) +M<0]X\0%O^:'`X'C/X"QH8,.!^\<#/<(``%`@`@(#@"O+/<8``K#@+@0'@"Z$J""_[`MC1P.!^ +M\<#/_\-M@%?1B]D;W/<(``H$BV#>`(DKTHN"4!;_FE>.'% +M,F@V><]R@``0OR%BSW*``/@>+;G`N?`B0P`H@U$A`(#/<8``\*E!@0GR/(N` +MX<4B@0\```H"`_)%(D(#2B0`=`#;J""``C9H=7D`(8T/@`#0PD"E`>,`W<]S +M@`!0P18C`@"@JJ&J`=DBJ@/9(ZI*)`!QJ7*H(,`!>6(6>:2I`>+@?\'%X'CA +MQ4HD`'@`V*@@``@`V\]U@``X'4"%#R,#``LBP(`/\D&%"R+`@$#:SR+B!\HB +M@0\``-``SR+A!P+P`-K/T"C`>#@?\'%SW"``.1$!H`#@""`SW"` +M`-Q_*:#A`&_\$=C@>/'`M@\O^0'9SW"``$0E,@CO_B2@BB#%#\]VH`#('QD> +M&)`!V`'9*'(H<[H*(`&8<;(*[_L`WPX.S_O/=:``T`_UI<]PH`#`+WH0`8:) +MN8NY>AA8@,]Q@`"`M1`86("&"P`"A@F/_D(,``A`V<]PGP"X_S*@$@_`"H#9 +MSW"@`!0$+*`='5B0[@[`"9X-@`G*#.`)Z7`'V$@>&)""#8`&&@G``>X*0``R +M"@`$Z@P`")(-@`.&"<`($@Q``&H.@`>"#H_[!@T``1(+@`FN"<_^#@H`!1H, +M0`%>#(`&D@Z`!:((0`1�_^3@O`!$8+P`0.#D`(SW```/[*0@Y/^R$'#_GQ +MP+(.#_G/=X``^!X#APB`P+@2#"`*+R``(`#=SW:@`+1'SW"@`(Q$N*``V).X +M=QX8D`C8=QX8D`#8GKA3'AB0X'C/<(``W``0>%,>6)-''AB0SW"``(`#$'A( +M'AB03R"`(T4@``U/(,8'--A$'AB0'-A%'AB0SW"``(06`8A&'AB0SW"``!`Z +MU@U@!0R(2B2`<,]Q@`"8QZ@@0`//<(``\*E!@'1M='L[8P*`0Z,!Y02CSW6` +M``!%`(6`X`/R9!X8D$,>F)&Z"2`*`=@#APB`42``@$"%#O)3(D$`$KE$(@`# +M#K@E>(8B_P,*ND5X$O!(<(8@\P\*N`0B@0\````,!KDE>`0B@0\````P`KDE +M>,]Q@`#8+`$&+_D"H?'`X<40W28,X`&I<`?9"[G/!X\$*0@)`"&.?X``1([/=X``8$1!I@;=I:;/=0(`<`2DID:FYZ8D'H(0 +M`"&-?X``9(Y`I0'ASW"``$24'!C8@,]Q@`#\*0"!'-I`H!C8#@@@``*A704/ +M^3G9SW"E``@,/J#@?O_9SW"``"",(*@`V<]P@`#(B^!_-:#@>`#:@.'*)$UP +MX'CH(.T!_]E<8""L`>+@?O'`X<7/<8``5*G/<(``/%I."J`(2-K/<(``?%+/ +M<8``S`<^"J`("-H`W<]Q@`!D,:&AHJ'/<(``?#2IH(X+H`(#@<]PH``L(,]Q +M@`#L-%"`$(!%H0:A`@X@`JFAT00/^?'``-G//'`,@PO^2#9`-K/=:``R!PII<]QH`"4$UNASW.``&P4 +M8(/S:,]V@`!0JPR&]7]3(,0%\&/[8U,@CP"#YZ3!BW$;]!^&F[@?IC06@!#B +MB_%P"_0H<$`C`01$:ZH.H`E`)@,<#=HJ\!Z&D;B2N!ZFSW"@`,P7*_"%YP[T +M02H"4D`C``3!NEH-[_R((% +M)P`1`*$%@P&A!H,"H0>#`Z$#XL]PH`#,%\]QH`"4$URA`=J`X@?T'X:7N!^F +M(-@*I1CP`,$#VA@86(`!P1D86(`"P1H86(`#P1L86(`4&)B`BA8!$1`86(`$ +MV2>E%AB8@)$#+_FDP.!X\<`B"P_YI!`!`/FYHL%P]"#9SW.@`,@<*:.D$`$` +M42'`@2[R,8C/=:``$!0CN<"Y`[D%X0/:3Z5&A4'"C>$0WLHFXA$&%`\QC"?# +MGPCT!!0/,?%VS"?JD`'>0_8`WH#FZO7%@$5^QZ6QB(8E_!\8O:5ZSW6@`,P7 +M6J`7\$6`SW&@`!`41Z&D$`$`42&`@@GR,8C7NH8A_`\8N45Y.J#/=:``S!<- +MV0':`^$-'9B0#AU8D":`&1U8D">`&AU8D"B`&QU8D`/9%!U8D'`0`0$0'5B0 +M/'`+@HO^039"'4- +M$@XV!M@-&A@PSW>@`!0$"J?/<(``,%K>"(`(`(76"*`(!-D!A3!P`-@#]@'8X'[QP)8( +M#_D(=<]V@`!@1-X/[__)<8#@"?*I<-(/[_]`)@$8@.`#]`#8"O#/<8``0$2Z +M#^__J7"`X/?S`=C%``_Y\<#AQ<]P@`!`(@B(B."AP0?TSW"``-@J`(#MN!_R +MSW*``-0L:!*`@,]S@`!"+(#@$_)K$H"`@.`3]&H2@(!1(,"!"_+/<(``V"H` +M@.&X!?11(`"``_0!V%SP`-A:\('@$_1N$H"`@.`!V,!X8,!2">`'BW!S$H"` +M@>!+]&X2@("`X$?R+/!S$H&`@>$%](,2@("!X`GR@>%*``P`@Q*`@('@0@`, +M`&X2@("`X`7T;Q*`@(#@`_(`V`+P`=A@P`()X`>+<',2@("!X"/T;A*`@(#@ +M!O1O$H"`@.`;\@#88,`7\('A`=UN$H"`PB5!$X#@!?1O$H"`@.`#\@#8`O`! +MV&#`O@C@!XMPJ&.`X.CU(,"Y!^_XH<#@>,]P@`#D!0Z`@.`!V.!_P'CQP.H( +M``"`X`7R7@D``('@!_3/<(``<"4`@(#@`_0`V!3P,@H``(#@#_2F"0``@.`+ +M],]P@`!`(BR0SW"``/@>'I`0<>WU`=C1P.!^\<`&"@``@.`@](8)C_J"X`3R +M`-C1P.!^SW"``$`B"(B'X`WRB.`2],]P@`#X'@&`Q!``!E$@0($(\L]P@`#8 +M*@"`([C`N.CQ`=CF\5T#X``1V.!X\<"X<,]QH`"L+QB!^K@,\@HAP`_K,]P@``4"`"` +MAN`!V.!_P'C/<(``%`@`@(?@`=C@?\!XSW"@`,0L&H#GN`;T42``@0'8`_0` +MV.!^SW"``-@J`8"!X`'8X'_`>,]P@`#8*@&`@N`!V.!_P'C/<(``V"H!@(/@ +M`=C@?\!XSW"``'P'((#/<(``V%KP($``@.`!V.!_P'C@>/'`Q@_O_^'%@.`< +M\L]U@`!`(@B-A^`6],X/S_^`X!+RSW"``/@>'I`LE1!Q#/3/<(``V"H`@`0@ +MOH\``#@0!/0`V`/P`=AU!>_X#WC/<(``?`<@@,]P@`#86O`@0`"!X`'8X'_` +M>.!XSW"``-@J`8"`X`'8X'_`>,]P@`!\!R"`SW"``/Q:\"!``(#@`=C@?\!X +MX'C/<(``?`<@@,]P@`#\6O`@0`"!X`'8X'_`>.!XSW"``)P5`(B`X`?RSW"` +M`(05`8@"\`'8X'[@>%$A0,<%\@G(O;@)&A@P`-F=N<]PH`#0&S&@X'[QP%X, +MS_C/=:``R!\D%0Z6Z+X*\H45`);*"H_[BB`$`"0=&)!1)H"0B`I""YT$S_CQ +MP"H,[_@TV#X*@`#PN,]W@```=A7R,@[@`@#8!@[@`@'8BB80$`#=%@NO_JEP +M%"=,$V&^@.8`M`'E./<.\`#;BB(0`*H+8`1P>!0GS!!ANH#B`+0!XSCW.03/ +M^/'`S@OO^#38H<$`W=X)H`!`Q?"XSW>```!>&?(V#N```=@#W@J^`-B,N+A@ +M$'B+<3(/X``!VA0G3!-AOH#F`+0!Y3/W_@W``!#P!=L*NP/:"KIX93X+8`00 +M>!0G3!-ANH#B`+0!Y3?WS0/O^*'`X'C/<0$`+$G/<(``&!C@?R2@\<#AQ6_8 +ME;C/=:``R!\2'1B0SW`!`$`\%1T8D/()P`F*(`0`#J6A`\_XX'@`V)"XSW&@ +M`,@?%1D8@,]P@`"LJ4:06WI/(@,`6A$"AC@0@`!D>EA@V!D``.!^X'CAQ0#; +MSW*``,"%2B0`=,]U@``XAFAPJ"```D`E`1(4>6"Q`>!(<,]QH``$)0^A5B(` +M!!&A5B(`!1"AX'_!Q>!X\<"R"L_XSW6``/@>!87/=J``Q"=U'AB0#)5V'AB0 +M!X5Y'AB0$)5Z'AB0L@WO_P#?@.`<\G<>V)-X'MB3@![8DX$>V),'A88>&)`0 +ME8<>&)`'A8H>&)`0E8L>&)`%A8@>&)`,E8D>&)`%A80>&)`,E84>&)#!V%`> +M&)"A`L_XX'CAQ0AQP[C/H#B`*71(.&' +M`-@V],]P@`#,JPR`SW&@`,@?9.`>H1#8#J$!V!49&(#>"R`+"]A1(0#&RB`B +M`!OT42!`QQ/RSW&@`-0+%H$X@23@,'!+][8+(`L#V%$C`,`%]%$@@,0#\AC8 +M`O``V(#@RB#B!,]QH`"0(SZ!(*7I`<_X\%28!$$"!:8*X +MBD$KP`#`N!>XQW```(`0!YBB`$`!ZE&/"*(!``'J44\`#8B[@>I1#P`-B, +MN!ZE#/``V(VX'J4(\`/8#+@>I03P`-B.N!ZE@B`!`>T`[_@>I0HAP`_K!XX'C@>.!XX'C@>.!XX'C@ +M>.!XX'C@>.!XX'C@>.!XX'C@>.!XX'C@>.!XX'C@>-'`X'[@>/'`SW&``+@\ +M%Z'@>.!XX'C@>.!XX'C@>.!XX'C@>.!XX'C@>.!XX'C@>.!XX'C@>.!XX'C@ +M>.!XX'C1P.!^X'@A@`#:4R%^@`OR`-J9NE$A0(#*(H(/``"#`,`J8@;/<:`` +M[$9%H6:02",#`T>0$+L$(X,/#P```,BZ17M(D`RZ!"*"#P```/!E>D:A0("# +MXA[T8H#/!^\B +M(X`UHB2`-J(%@!>BSW```%55X'[QP)X.K_@`VZ;!SW&``#@=8*%AH6*ASW:@ +M`+1'+!8!D`HD@`\``%552B0`>&AQJ"!``L]R@`!,$S5Z8*(!X0#9F+F5'EB0 +M2B2`<<]R@``X'0@2!0`+$)```-TY==AUJ7*I<']!4D03,@@0#:#R)"`(CG"/05)$$S +M((%*)@``#R9&``'ESW6``#@="!U`$8#CRB2!#P``K=XV\B6(9(@&(H(!Q;H0 +MN04C@P\````_97D%(8$/`#\``)L>6)!GB":("+ME>6B($+ME>6F(&+ME>9P> +M6)`&(0$2Q;F9'EB0`-F5'IB0F;E,'D"0)(!8'D"0"I"4'AB0;R!#`),>&)". +M#0_^B'!]!:_XIL#AQ>'&SW*@`,!&SW.@`.!&2B0`<@#=J"```Q8@3@,AA@'E +M!!I0`"*&!!M0`#&`SW*@`+1'F!I8@#*`LQI8@!.`M!H8@,]P``!55<'&X'_! +MQ>'%X<8DB,]R@`#06J:(PKDN8@#9#R&!`X#ESW.``*"*0(,%]"9Z0*,7\$5Y +M(*,EB!4CC0,CI2:(18A982:E((",(1"`1/>*(1``(*`CN2&C`(`JN`*C`-G/ +M<*``\#8LH".#):`F@R:@)(,GH">#**`E@RF@*(,JH"&#*Z`B@RV@((,DH,'& +MX'_!Q>!X\<`2#(_X"'>:<;IRVG,*(@`A"B-`(0HA@"'/<```R!LV"F``"B#` +M(?IPSW```,P;)@I``!MPSW````0<&@I``,]VH`#('SMP`=@3I@;8SW6``.@] +M`*7AI0@=`!4,'4`5$!V`%10=@!08'<`4'!U`%`[`(!T`%,]Q@`!8'0FE!($` +M@`JE"($`@`NE#($`@`REH!8`$`VEI!8`$`ZEJ!8`$`^ESW!#=:@2$*6N"6`` +M*-@1I:8)8```V!*E4R?`=1.E`E$Q8`EL]QH`#( +M'!BE%!8`EE,A`C,9I146`)80NAJE)!8`EANE%A8`EARESW"``+@\%X`=I<]P +M@`#H/7@8@`K/<(``Z#U\&,`*SW"``&0^!!@`"\]P@`#H/8080`LH@8@80`#/ +M<8`````D@8P80``O(<<%"+DE>B\A!P9%>9`80`!:"V``)=GY`H_X\<#>"H_X +MSW.``'P^0X,`W<]VH``L(-"&\FKT?W]GQ:<$IT`B0H`FIT.C!O("@Z.C`>`" +MHQ$#C_C/<8``6!T(@0#:0*`,@0'90*#/<*``L!\TH.!^\<"."H_X"\@`W@0@ +M@`____\#"QH8,`X.(`#)<,]U@`#$!A&%@.#4#"(`RB!B`,T"K_C0I?'`A@D` +M`(P@_X_*("$`T<#@?N!X\O^4HE``#&#^_X!A0`,8#@2/*!P6O8`=HB"Z`` +M2'.`X`_T"B'`#^MRSW```-04BB.%!XHDP0I-!Z_Y2B4```04`#%`)($P`=KR +M"J``2'.`X`_T!!0%,0HAP`_K$$HQ0!,)8",`!Y`$=3V"B'`#^MRSW```-44BB.%">D&K_F*),$*'=C/ +M=H``%$<`IKAP`!0`,<]U@`!4ST`M@@"I<7X*H``!VX#@#_0`%`0Q`!8%$`HA +MP`_KG$:6")PB*,`$0CCP#]?_%R/?3AB40C`@0DND0C +M!@)!+L8`#""`H40C`0$BN2_T3"1`@`[T@.',(B&`!_*!X@7P@.(!VL!Z@>(;]$PB`*8!VL(BB@!0<88C_0\GNP7R@.+,(&&@#?0R=\PC +M(8`+\H#G`_*`XP7R,G<#]O%VA?;)=P3P`=D)\`AU`>"P<%H'Q?\`V8H@_P^` +MX03T@.7*($H#C"#_C\H@@0______%?(R)((T@>+/<8``5,\']&)Q%GD"$<`` +M"?""XA9Y!?0&$<```_`'$<``Y0=O^*?`\<#R#,__W@\/_\((0`<�__"B'` +M#^MR/=B*(PL.2B0``!$%K_D*)0`!X'CQP,]P@`#X'@*`PA``!E$@0(#4#D(& +MT<#@?N!X\#*("$!O`PA_\HA80#/<(``[)$`@%$@@(`%\CX,8``0 +MEK2NSW"``.R1H*!-<(8@_`.,(`*`%?3/<8``H!4`@0'@`*'/<(``^!X#@!B( +MA.!8#\'^>@P`!"(.S_H%\(P@`X1H#@'Z109/^.!X\<#.#4_X`-X"W<]W@``X +MCD`G`!N$+@@9,"!`#E$@`(!L".+^RB""`V&]@.4!YC+W#@@@``#8`09/^.!X +M@.#QP!#8"O+2"0_[W@B@`8H@!`#1P.!^G@@/^[((H`&*(`0`[@D`"8+@!O0* +M"B`)`-CR\?#QX'CQP%X-3_C/=H``'`@`W0OP$-BX>`LA`("@".+^RB!"`P'E +M@^4@AK;W@.'*("$`<`WA`LHA`0"5!4_XX'CQP.'%SW6``,0&$(6`X"'TC@D` +M"8+@K`DA"(#@^_,=!4_XX'C/<8``^"4`@==P`(```(P``?P`@==P`$````P``?S@?@': +MSW&``'0E0ZD8H2AP9-EUVA[;O0`@!QB[X'CQP,]Q@`#$!@.AJ@@O^Q'8L@]@ +M`8H@"`#1P.!^\0!Y`-@7\,]Q@`!0JY@1@`!`*`(&AB#]#U(@P`%%N$5XSW*@`(@D +M$*(?@;.X'Z%*\`'8$-O/<:``R!QIH<]S@`!0JY@3C0``VL]V@`#L5<:&0"T! +M%H8E_1]2)!X\<#AQ:'!"'7/<-2Z_LI`P`3P +MN@R@"@'8SW&?`+C_NJ$$V!NABW`>H0#:G;K/<*``T!M1H,]P`&T`$!FA!/#: +M":__,-A1(4#'^_,`P-=PU+K^RN'SG0)O^*'`X'@`V\]RGP"X_QJB>Z(^HL]P +M`&P$`!FBX'[QP`H*;_B8<"AV&@@@`$AU!B"!`XAP4@@@`*5Y60)/^,]Q@``4 +M*F")@.//!X +M\`]XP.!6]\6Z +M8;G%N3!R$O)$(@`()+CP)@`04R(/`0#;#R/#`P'BQ;HP@`,`O>A<`EA47`)9!*1$%HA<` +MENH*;_@!V!4G`!13$`&&%A`!ABH0`(;6"F_X`=@5)T\4F!<`EI`7`)9Q%P"6 +M0"4`&Q:F0"G`(<=P@`"`M5(((``@V58E0!(6ID`I@"&6(`(`QW"``("U.@@@ +M`!#9525`&1:FSW&``""\0"@`(3A@(@@@``395B6`%A:FSW"``&"]#@@@``K9 +M\0.!XX'C@>.!X`>+@?\'%X'CQP%H/#_@(=\]RH/YL`\]PGP"X_U:@@.$`W='W +M*'9:#>__%2=`$^!XX'C@>.!XX'C@>&&^@.8!Y3/WB0.!XX'C@>.!XX'AAOH#F +M`>4S]TD'#_C@>.'%SW6@_M0"`-K/!XX'C@>.!XX'C@>`'BX'_!Q>!X\<"B#@_XZW#/=X``I!@`AU$@0(`9],]U +M@`!,'0"%4B"```"E"/#/<*``J"`-@.3@[``%`(H,[_]4V``5!!"&(/\.D'#Q +M]<]V@`#T)I@6`)91(("`/?(N#<_^SW6``/@>R14`%J6XR1T8$),6`):EN),> +M&)#7%0`6I;C7'1@0#H6EN`ZE`(7($``&AB!_CLH@(@#*(0(`_`NB^,HBH@$! +MA<@0``:&('^.RB!B`,HA(@#@"Z+XRB*B`0"%SW&``%2IQ!``!B6XP+@F""_\ +M"J$Z#X_]?]@*N,]QH`#0&Q.A?]@0H0#8E;@0H<]Q``"<'B8*(``&V,]QH`#P +M-@2!1B#``02AE-C^"^__&-D`AU$@0(!8#2(!RB`B`/$%#_@*(<`/ZW+;V`2X +MBB,%`A4#;_E*)0``X'CQP'(-+_@!V-X+(`D`WL]PI0`(#,]U@`!$)<*@!(51 +M(("`>`I"^<]Q```0![()(``&V`O(!2"`#P$``/P+&A@P!(51(("`$?+/<(`` +M5#@`@(#@"_22#B__BB#&"('@!?2N"8`%#?``V9ZYSW"@`/Q$(:#@>,&@SW"@ +M`+0/W*#^#\_[7@T``4(((`@!V`8([_H!V$T%#_C@>/'`U@P/^(#@B'4`WPGR +M@>`+]`'>SW"``'P5P*@%\,]P@`!\%>"H@.$)\H'A"_0!V<]P@`!Y%2"H!?#/ +M<(``>17@J(#B"?*!X@OT`=G/<(``>Q4@J`7PSW"``'L5X*C/=J``R!_/<(`` +M?!48'MB3`(B`X(HA$``2\L]P@``U'@"(@.`,\L]P`P!`#44>&!`PI@+8&!X8 +MD`/P,:;/<(``>14`B(#@'/+/<(``-AX`B(#@%O+/<`(`3EP@'AB0SW"``"@` +M(1X8D,]P@`#@!2(>&)`8%@"612```Q@>&)#/<(``>Q4`B(#@"/(8%@"6A2`! +M!!@>&)"!XP?T&!8`EHBX&!X8D!@6`):`N!@>&)"`Y1GR`-B4N,]U@``\"`"E +M<=@&N!8*[__\V2"%SW```$P<"@KO_Y^Y&!8`EH6X&!X8D`$$#_C@>(#A\<"8 +M<`7R3"0`B(_VSW"``&@&`!`%``HAP`_K`#92B2`<<]S@`#`?RARJ"#``?`C@``!X@5YX'\O*$$`X<4` +MVDHD@''/=8``P'](,E>@#9GKD9>00A@`!"(`"`RB!B`.!_ +MP<7@>/'`SW&@`,@?I!$"`,]P@`#L-`"`-8'/`!V`+R`(.!X`;TUW$``(@3A/<`V`/P`=B!XLP@8H#8#:'ZRB#A`='` +MX'X"X3!Y06E0<,3V(G@0>`/P`MC/<:``R!\>H1#8#J$!V!49&(#@?N!X\<#A +MQ5#=`-K/`#9SW"``.R1(:#/<(``4*L`@A`0`P +M>0+8%1H8@#^BX'X"X3!Y06E0<,3V(G@0>`/P`MC/<:``R!\?H8H@&`@.H0+8 +M%1D8@.!^`-G/<(``[)$@H"&@X'\BH.'%X<:`X,]Q@`!XQT6!)?+/<:``R!]` +M$0X&SW.``%"K0"B-`D(3``%\D]!^V&"[8V*["",#``)["2+"``+8%1D8@,]P +M@`#X'E^A`X`B@,]P@`#LD2*@P<;@?\'%X'[@>.!_`-CAQ/P'"X]@^ +M#F__BB$$`\]VGP"X__V&"B'`)T#9G[D]IL]QH/X<`#:F4R;`-`4@@`^P_@`` +M%J;/<```1!S^#J__"B#`+WIP&14`EE$@`((0\E@>@!#J__"KC/<``` +M!!Q2#H__SW````@<1@Z/_\]P```,'#X.C__/<```$!PR#H__SW```$0<*@Z/ +M_U$C@*4-\KP5`!:`X`GT02M.)<"^`[Y"?`\W@?P1MX% +M\%3>`_"$W@#8@>#P#`$%DQ4#%LEP"G$J<@HD@`0M!2_Y"B7`!/'`B@_O]P': +MHL'/=H``U"L(CL]U@`!4*T0H/@M`)0`5)W#F#6`'BW$(CH''Z7%$*#X+0"4` +M%B=PS@U@!P':"(Y$*#X+`"5!'D`A``T4X;H-8`>@U@ +M!P':80?O]Z+`X'[@>.!_`=C/@'B*'=`*H`@SW&``!"_&G([9Q9X&F-2BHPBPX\`(Q$`0_+/=H``V"I@A@#= +M\+L/)8T0#O+/'I`0<1/T`(8$(+Z/```X$`WT@@W/_H#@"?+/<:``."X'@:5X +M!Z$,\$86`!:E>$8>&!`&\$46`!:E>$4>&!#_V1(90B!"($(@@.)6!^W_(6_E +M!<_WX'C/<0$`QP//<*``["/(.(`H!V!8.[_Z*(`4#@.`&]`38T<#@?P04$#1$ +MV<]PH`#('"F@S@X@"@'8!@N``;_QX'CQP&H,S_>BP2AV"B2`@`#?SW6@`"P@ +M0!40$``+<:(([_B*(`\-`!0`,5$@`(#Q\P7P`(:`N`"F@<&&"._XBB!/ +M#`04`#%1(("`!/(`AH&X`*:+=8H@CP]J"._XJ7$@P`BX`AP$,(H@SP]6"._X +MJ7$@P0(4`#$E>`(&N!!X$+@% +M((`/``!"_0:G$(4"(``$C"`/B@SWBW$."._XBB!/#P`4`#%1(`"!\?,%\`"& +M@+@`IL]P!@`"_P:G0"2!,.H/K_B*(,\.`A0`,34#[_>BP,]Q@`#X'@"!`J$# +MH<]PH``X+HHA_P\GH,]P@`#8*C$`K_F*(0@`X'CQP"H-[_[AQ8#@6O+/=8`` +MV"H`%000`-E1)`""RB'!#\HBP0?*(($/``#)&\HC@0\``$<%#``A^`$#V'YRB`A`F$"S_>!X,]Q +MIP"(20#:!?0#V`ZA`O!.H>!^X'CAQ>'&SW6``.@LH(T`WH#EP*,2]('@S"$A +M@`[RH.)$]L"C"O#`X@;8!?9"(@`(0[@"X`"CP<;@?\'%X'BX<$#<`"$`@_'` +M#@`D`)AQC"`"@(OV"B'`#^MRSW```,D4#0?O^(HC!PW/<(``.$ST(``!SW&` +M`#A-!"A^`2]P]2$!`4(H`P3!NU*X!"E^`2]Q0BD"!,&Z4KF!X\`@:0"!XL`A +M:0"((#X`B2#!#X@A/@")(<$/@.#6("L(@.'6(2L(@@D``-'`X'[@>/'`]@C/ +M]Z'!.G$`WX#@RB'!#\HBP0?*(($/``#*%,HC@0\``(@"RB3!`'@&X?C*)<$# +MSW&``.PL0+'/<8``[BS@L4PA`*#*)#N__J7%"(5$@3"$`H+`'[?]`(E(@R7`J"2``J7&5 +M`._WH<#@>/'`/@C/]SIPSW"``.@L`(B`X!IQ3O1,(0"@&_+/<8``B`>EB02) +M'64R=SXRB5,`P#=SW>` +M`.DL`-X(\`#9(*_F#N__BMD!YL]^`""!+X``B`+5X +MU'C/)(08X#@4FWL\P+@$'C4>H#ASW.``&R24F/=]0'9W/$!Y:]]@^6J +M!\O_[0>/]_'`D@^/]\]S@`#N+$"34R)-@"'R@N4D],]U@`"(!PFM**TBA<]V +M@`#L+`"6*=T2O<]W@`#I+!4E#!`@I."/@.<&\E8@#PCP?_5](*4!X`"V!O#/ +M=8``B`<+K2JM`>*=!Z_W0+/QP"H/C_<(=AIQSW6``.XLX)4+\,Q_U@CO]T`I +M0'%%N'X-[_\*<2"5C"$0@+3V80>/]^!X\<#J#H_W"'7/<(``Z"P`B'IQ@."A +MP1IR;?1,(P"@&_+/<8``B`?%B02)'F9R=LHAS`_*(LP'RB",#P``S!3*(XP/ +M```\`\HDS`1,!.SXRB6,`THA`"``W@KP`1U2$`:/@.`$]""M`>4!YL]^SW>` +M`(@'`"<`%`:(`>`0=F8`*@`"=T`K@"`4>!4@0`34>,]Q@`!XDC0A$@!,(@"@ +M`-G?\XMQ2G`"VJH/K_\`VX#@#?(!%(`P`1T2$`:/@.#6]0$4@#``K='Q"B'` +M#^MRSW```,T4BB--!`HD@`2]`^_X2B6``$`A42`O(4CP4HA`""+<2IP +M2B``(0IR^@ZO_RIS@.`.]`HAP`_K)(`WP3P`>?O?T$H`0+#N3!W<@`* +M``#>$_!`*8$@-'D*%(`P%2%!`0'FSWX4>;EA`!D$!(`@`B,O(`@D`,!!*`$& +MP[D!X3!VP`?*_X+!"G`"VH8-K_\`VPL4A#`O*`$!3B"%!R\E1P%,)<"`L`?+ +M_PHAP`_K!\G_02@!!<.Y@.$*=;0`+`!*(``@2B(`(`;P0")2("\BAR1!*`$#P[E2 +M<8``#`!*(0`@%/`"OM1^"A2`,!4F3A%`(5$@+R%')!1^`":`'X``>)*@L(`E +M`A.P?0#`02@!!P'A,G&X!\S_,+C#N``@#@2"P:EP`MK"#*__`-L+%(0P+R@! +M`4X@A0/'`S@J/]Z+!0,!!PD`H%`5`*1<% +M`-U`*A,%0"L2!0'>2B6`(:EW!/`*="^_WZ7!,(P"@F'``V!/RA><)\HOG!?((\$HF +M`"`S\`'8`O`"V,]Q@`"$%22!"R$`@`/R`-H"\`':`"!`(\X-[_@J<0HF`*`? +M\DPD`((2\L]P@`#L'18@``%`@`:($'<@](#B'O*I<&!ZJG$*(@"@"_(B=<]P +MH`#0#Q`86(-,(`"@K?7/<:#^B`7/`'AH*#2 +MX80K`@H`)$`.M/>D$`,!SW&D`*`_?:&F$``!'J$(&D`!Z0"/]^!X\58D7#C1P.!^\<"^#B``1]@`VL]QJP"@_UFA!]@:H5BAT<#@?N!^X'C@?N!X +M\<#/<8``O"XX@8#A(`X"`-'`X'[QP,]Q@`"\+CV!@.&4#P(`T<#@?JT&``FI +M!@`)I08`"0#9SW"``,B;(:!Q`F`"(J#QP.'%SW6``,B;H@M@`JEPN'``A8#@ +M$_)*)(!SSW.``-`J`-FH((`"0(-$*;X#,B)"#K!R'O(!X1#P`-E*)(!YSW*` +M`,A2J""``D0IO@,R(D,.L',.\@'A"B'`#^MRSW```(89BB-$`?D$K_A*)``` +MS0=O]RAPX'C/<(``R)M`@(#B(X`+\L]P@`#0*@"`1"F^`PW@,B!`#@CPSW"` +M`-521"F^`S(@0`[@?N!X`-O/<:H`\$-EH<]R```_/T:ASW```#X_!Z&*(!`` +M"*$)V(RX":'/<```%AP*H<]P```?'PNASW```!P6#*&1V`2X#:$$V`ZASW`` +M`#\^#Z%0H7&AX'[@>/'`O@Y/]YX(H`(`W48-(``'V$H.[_\:<,]VI`"X/:P6 +M`!;/=Z4`V,LYV:*XK!X8$`'8K*?V'A@0SW`5`"LKFAX8$(H@Q`"?'A@0&MCS +M'A@0]!X8$&38R!X8$*K8R1X8$&G8S!X8$,#8S1X8$,]PI0`(##Z@-@_/_]H) +M(``*/'`&@@``#X/S__2#0``N@B/^M'` +MX'[@>/'`W@U/]\]P@`!TJ$`@$@8(<<]P@`"0!B"@`-[$J`3?1"X^%PHA0"X` +M(8!_@`!TJ#H++_D;%!4_WX'CQP&X-;_=$ +M*#X'`"&.?X``=*@0K@'?\:XAKD"N8JX#'@(1"2``$QX"$:D%3_?QP#8- +M;_=$*#X'&G`Z<<]Q@`!TJ"]P&F%1BH'B&V$/\@HAP`_KZ:!`9"X>*>!8*:D>*&&0"$/!(#BI7@!IASR +M`8$"',0P,+L$',0P`!P$,""!BW5@>:EP`8:EP`-@`I@&F701O]Z+`X'CQP.X+;_>(X$H +M@5$A`(``W@OR3)4KE5MZ17E3&UB`+954&UB`!?!3&YB#5!N8@RZ55AM8@"^5 +M6!M8@#"551M8@#&55QM8@#*56AM8@#.57!M8@#2561M8@#656QM8@'H-S_]5 +M`D_WX'CQP(0H`@?/<8``N#DHD5,A08#/:EPY[@GN%(@``#*)2(0RB%"`\HAX0'`N!-XPKC/ +MI<.!X\__>:WR">__`Z4$I0;PS@BO +M_HH@B0S/<*``>$4`@`0@@`]P````02@^A0#9\O7/<*``M`\\H,]RJP"@_SFB +M!]@:HCBB]@U``H#F`=C`>`S@<@_O^0'960%/]_'`V@A/]PAWSW6``,B;&(U( +M=A!R.G$:/'`"',`V0+: +MA"L*`@`A@'^``.2;A"D$#P3@V@H@!B=P8;J`X@'A,O?1P.!^\<#/<(``F,NB +M#>_XBB$)#,]P@``8(I8-[_@4V<]P@``\)8H-[_@4V='`X'[QP.H/#_>BP3IP +M&G$`W8(.[_\'V)IP`MFI<%IP>G$`VS1H`G$H=10A`"!HR#EMO8MP0B-!((#AO@?M_T`B +M0"`J"^__BG#1!R_WHL#@>/'`?@\/]SIP6G'/=X``$#H,C\]V@`#(FZ6&AB#_ +M`4.X#B4-D,]P@`"X*B"`RB5B$&!Y!-B`X"/R&HZ`X,PE(9`=\@#8$-T:<`*X +M%7C'<(``0"X@@(#A!_(B@(#A%/)@>2IP8;V`Y4`@0"`O]P#8&JX,CX8@_P%# +MN`6FL@BO]TIP602G:$KH5(L,`(*,`D/`B``#1P.!^\<#AQ<]Q@`#DJ`"!@.``W13RSW*` +M`!"I`*((@:"AI*$(HM()[_D)V,H)[_D#V,]P@`",0EH,@`?/<(``9#&F#:`` +M`X#/<(``)#7]!B_WKZB5!8_Y\<`:""`#X<7/<(``5$(N#*`'`-W/<(``<$(B +M#(`'SW"``(Q"%@R`!\]P@`!D,:*@;@GO^0/8SW"``.PTHZ"U!B_WH:#QP.'% +MSW&@`*PO'(&]@01]SW"``#0>`(B!X`GTSW#`WP$`'*$HV1BY"?#\O3P)`@/V +MO90,POD`V9NYSW"@`-`;,:!M!@_WX'CQP"((``#^"$``T<#@?N!XSW"``&0Q +M`("!X`'8X'_`>/'`T@T/]\]P@`!4J<>`P+Z!Y@'>SW&``-`V`('`?N&X+/2! +MN`"ASW6@`,`O$X7ZN`3R$X6ZN!.E`M@1I<]P@`"X*B"`8'D`V(C@#/32#V`) +M"M@,\,]PH`"H(`V`Y."0]Q"%42``@/CSX@CO_\EP%14`EH"X%1T8D,4%#_=< +M%0000!4%$`HAP`_K!X\<`J#0_W"'7/=J``P"\:ACFX +M4B```%,@$0`4AE$@P(`']#(+[_XDV/*X`-\"\@'?418`EH#@"_2C%@"6!""` +M#P````^,(!"``_0`V`+P`=@:<`0ADD\`!```SW````@<\@K/_C^X4B`#``0@ +M@$\"````UW`"`````=K`>@QPAB`]`(#@`=G`>5$@@,$(\L]P@`#(!P"`@>`` +MV`/T`=@!WN6]RB&!(TPA`*`H\N:]RB=A$(#G(O+COGP/'`(@_/_<]RH`#`+P#9B!I``!."B[@3HL]P +M@`!T%`&0$+A%(``/P!H``,]P@`!X-+X*K_D@H-'`X'[@>/'`G@LO]P#9F[G/ +M<*``T!LQH.(,H```WH#@)_+/<(``3!,Q@(#ASW6``#@=#/+/<)\`N/\]H"2% +M`>&SN;6YN+DDI3:@SW"``,@'((#/<(``I#3P($``0'@`A?&X!?+/<)\`N/_= +MH*4##_?@>/'`*@L/]QIPSW"``%2I!X#/<8``^!Y3(`T`@>4!W2"!Q!$!!L!] +M42%`@1SRSW&``'0489'/<8``S`?`@3SC>69DX1)QCO<*(<`/ZW)^9HH@S`A: +MVPHD``1Q`&_X529%%H/@`-\.],]P@`!T%"&0SW"``,P'`(`\X3A@9.`"(!`@ +MSW"``(06`(A1(("`!_+/<:``P!T`@:"X`*$+R`0@@`_^__\#"QH8,`O(A[@+ +M&A@PK@VO_ZEPSW&?`+C_78'/<(``;`9`H/VA'-D7\,]PH`#(.S:`5H"&(?\( +MAB+_"$5Y5H"&(O\(17G/&^;@A],]P@`"D&`"`42!`@!OT"B'`#^MR"B0`"%$6 +M!9:*($P(D00+8@N`-\D`6!!`*(<`/ZW**((P( +MC=LY!R_XN'.*(!`!$:80AE$@`(#^]12&J[@4IL]P@`"D&`"`@N`2V,`H(@;* +M("$`SR!A!AFFSW&@`,@?&!$`AJ&X&!D8@(H@$``1H0G8"+@/H1.&J;@3IL]P +M@`!4J0>`@^#,(.*!!O1`*(`@G[B('@`0S@F`!\]P@`#0-H4!+_?@H('A\<`$ +M]'(,```$\$X,``#1P.!^\/'` +M/@@O]P#9SW6?`+C_787/=H``;`9`ICVE'-D5\,]SH`#(.S:#1"$"!S:#AB'_ +M""5Z-H.&(?\(17G/`X>OUO@K/_X#@$/0`V"?P.!,$`%@3 +M!0`*(<`/ZW+/<```F2%M!2_X+]L`AAVE/@[@!P#8SW&``,@'((&*($P&:@U@ +M``/:P@Y@``/8%@[/^PC8A@P@`(HA_P\!V`D`#_?@>/'`SW"``,@'`("#X`?T +MM@E/_)8)S_]>#P``T<#@?N!X\SW6``.PT0"4#'*@@0`3/ +M<8``?#35>>.!@>`5(XP#X*0#]`'?XZ$!YL]^`=BA!^_V1!T"$,]P@`#DJ`'9 +M)*@EJ,]Q@`!\-`"1AB`8`*BX`+':V`.ISW```%##`:'/<`$`H(8A!6```J'Q +MP,]R@``D-0Z*@>"AP2GT#XJ$X$WV@.!T#^'_RB!A``^*`>`/>`^JH<#1P.!^ +M`-@-J@ZJ+@@@``^JE@_/_P#8C@KO_XRXSW"M"[ZZ0,"+<`39?=H]V_8*H`47 +MN^;QYO'AQ<]R@`#L-$02@`"!X$`B`PP3]$HD0'$`V:@@``/P(TT`SW"``(@T +M-7@!X:"@+WD`V$0:`@#@?\'%X'@`VL]Q@``D-4^I`=@-J4ZI3*E0J5&I4JE3 +MJ>!_5*G@>/'`0@[O]@':SW&``/@>8X%XBX3C&O0`@<]Q@`!D,<00``8EN%(@ +M```A@<"X`=J`X<]Q@`!XQR:!P'J`X`#])AR`_!*)```SW"@`"P@<(#/=H``[#1%AJ:&`B.`@`#:RB)O``(C +M3X,`W`Y0;R`B.` +M#TX``2`&I@&&@.`6\L]W@`!\-`"&X82&B`-G/<(`` +M5*DPH"6`X'\QH/'`K@S/]L]U@`!D,2&%)7@!I<]Q@`!4J1"!@."AP0;T`=@0 +MH06!$:%2#2_]BW``P<]P`0"P)3!P"_+/<`$`U#$0<0?RSW`!`&Q'$'$$]/8) +M;_P!V`#>SW"``.PTP:!R#V_Y!]AJ#V_Y"-CB#8`"SW"``%1"]@E`!\]P@`!P +M0NX)0`?/<(``^!X`@,00``91($"!!O(.#B``R7`%\"(+8``#A7D$[_:AP/'` +M!@SO]KAPN@WO_RAP@.``V5:`AB+_"$5YSW*@`*@@38+DXHKW@.'K]78. +MK_^H<""%/:8-!,_V.!`$`%@0!0`*(<`/ZW+/<```F2$A`2_X+]O@>/'`SW"` +M``R1"(!1(,"!(?3/<8``?#1"@2&!SW"``-`T0*#/<(``[#0GH,]Q@`#(!R"! +MBB!&`/X(8``"VE8*8``"V#H,(`8"V.H/#_D'\,]P@`!D,3X*8``#@-'`X'[@ +M>/'`'@O/]H/@*'4>],]Q@`!T%"&1SW.``,P'0(,\X3IB(8-DXEEA,'4.]PHA +MP`_K_V +MH-@'\,]Q@`#0-@"!@K@`H=X)S_^:"D``T(;V"._[`=@>#0``Q@P@"3+8SW`` +M@@$`'*5""6_Y`B;`$P#8,@B@`,EQO0+/]O'`X<7/<8``/*D`$80`3"0`@*7! +M#O8#$84`3"5`@`KR"B'`#^MRBB`-`<4'[_?QVTPD@(`;]`.)@.`9]`#8`*G/ +M<8``R`<@@?G8!]VZ#R``J7(6"6``J7#/<(``9#%^"Z__HZ!R"X__8/#/<8`` +MY*@$B8'@#/0%B8'@"O3/<```___2#>__`-F`X%#RSW&``/@>`('$$``&42!` +M@07R`X$8B(3@#/3/<8``R`<@@8H@Q`16#R```MH"V#;P^@M``(P@$(5D``4` +MSW"``&0Q`("!X`;TE@X/^8#@*/(`V<]PH``L(+"`SW`!`+`E0,`!V$'`0L!# +MP43!!MD(<@#;F'.X">_V`-G/` +M`'C'`B8-$,]P@``\J>6'8X`%+_X0-W4!VZ"(PB/.`('EI<$%\@.(@>`/],]P +M@`!D,2.@SW&``,@'(('/V'H.(```V@#82?#/=8``^!X`A<00``91($"!0_*! +MXT'T`X48B(/@/?3/<(``9#$!@(#@-_3/<(``#!T`D('@`=C`>`RXUW`````0 +M*_*P@L]P`0#4,4#``=A!P$+!$=A#P`#8C+A$P"AP#-D!V@ASF'"X<``EAQ\` +M``!]%@\O_-APSW"``.PTSW&``,@'(('(H-C8Z@T@``C:"-A"#P``D0#O]J7` +M\<#AQ0#9SW"@`-`;F[DQH,]P@`!$)02`42"`@![R,@A/_\]U@`#X'DV%/I53 +M(@``Z@X@!`';`(7$$``&42!`@0SR`X48B(3@"/+/<(``5*D'@%$@P(`'\L]P +M@`!D,0.`"_#/<8``R`<@@8H@20=J#2```MH"V,(.```A`,_VX'CQP*(/C_;/ +M\@6%@.`: +M\C""`GG7<0``4,,!V,(@#@"`X";R,@C/_P2%Y:7CIJ"X!*6R#"_Y`-@<\%X+ +M;_D'V!CP@.,&\@:&`GF`X=+V4R"`P02F#O3/<8``R`<@@8H@2P*B#"```MK^ +M#2```MA)!X_V\<#AQ:'!BB#_#U8,(`!`P('@,?3/<(``^!X`@,00``91($"! +M$/+/<(``#!T`D,]Q@`#(!X'@`=C`>`RXUW`````0#_+/<8``R`<@@8H@10=" +M#"```-J>#2```-@Y\""!BB!%""X,(``%VHH-(``%V"_PSW"``'@T0("!X@#9 +M!/0@H"?PSW'`WP$`SW"@`*PO/*#/<`"`__]&"N__`=F`X!?R>@_@!8MP"B4` +MD!'RSW&``,@'((&*(,8!V@L@``/:,@T@``/8J7#Z"N__`,&)!J_VH<#QP+AP +MSW"``,@'`!`$`,]Q@`"H,$`L@``4>!4@0`$`88K@"O0*(<`/ZW**(,T`>0/O +M]Z#;Z@P``-'`X'[@?N!X\<#AQ<]P@`#X'@.`&(B$X`WT"B'`#^MRBB!-`8HC +MA`U*)```00/O][AS!@A```AUSW```+_?E@GO_P#9@>`%](PE$)40]QX++_P! +MV,]Q@`#(!R"!BB!%`B8+(```VH(,(```V-T%C_;@?N!X\ +M(('$$0$&42%`@0/R`>`)HJH-C_>>"X_]@>`+\@"%@N`)\L]P@`#(!P"`@^"L +M#,'_.06/]N!X\<#&#(_VSW"``&0Q`("!X!3RSW6``,@'((6`XX<_(-3_]>#N__`MC/<`$`U#$&"R_\`=G1P.!^X'CQP%(,K_8&V&H/ +M#_G/<(``F!5*)````!@``<]P@`#X'@.`&(B$X`OT"B'`#^MRBB#,#>K;M0'O +M][ASSW"``/`^S@D`!\]PH``L(-"`SW6``.PT((4")D`0UW```"!.!O8V"&_Y +M!]C`I<]P@`!XQP:`42``@"`(8OG*("("S@S@!0#8/02/]N!X\<#AQ>8.+_D( +MV,]U@`#(!P"%A^#,("*"//+/<8``[#1!@8#B!O+/`>\@.!@.`:\@#9SW"` +M`.2H*:`JH`#8*@]O_XRX$/"`X,P@HH$,]`.!@.``V@?RSW"@`"P@$(`"H4.A +M((6'X`!V,!X#+C7 +M<````!`8]/H,[_\!V,]P@`!D,0"`@>`V]`.%@>"`""'YRB!A``.%@.!T""'Y +MRB"A`"KP@.$.],]R@`#DJ`J"":(`V`JBSW"@`"P@$(`&HL]P@``,'0"0@>`! +MV,!X#+C7<````!`0],]P@`!D,0&`@.`*](H@A09&""```MJB"2```MC]`H_V +MSW*``.PT`8*`X`#9!/0#@H#@`_(!V5,@@,$$H@':PB*!``#8@.',(B&``_(! +MV.!_#WC@>.'%X<;/=8``=#'`%0,6O^-']])KU'Z^9@"F(:9"I@%KQ;C`'1@0 +MP<;@?\'%SW&``"0U#8F!X!GTSW"``'PT`)#IN-$@HH((]`#8#JD-J64#K_\/ +MJ0'8#JD/B4(@`("!`J__RB!B`.!^\<#F"8_VV@B/_\]S@`!\-`"3SW*``#RI +M02B!`,"Y(:K/<8``#!T@D8'A`=G`>0RYUW$````0#?2B@\]Q@`#0-*"AH8// +M<8``[#2GH3;PSW&``&0QH(&!Y1+TSW:``.2H)(Z!X07T)8Z!X0'9`O(`V8#A +MRB&"#P``$"<#]"*#@>7/=H``T#0@IA+TSW6``.2H)(V!X07T)8V!X0'9`O(` +MV8#ARB&"#P``$"<#]"&#SW6``.PT)Z6I<<]UH``L(-"%Y8$")LT3_[T"],6A +MYH$")LT3_[T"],:A*(.`X07TSW&``'C'*)$CHB6XP+@:"R_Y`]E=`8_VX'CQ +MP,]Q@`#(!P`1!`"X<,]R@``\-4`L@``6>!4@0`$`8H7@"O0*(<`/ZW**((T` +M70:O]W;;@N``&4`!8_*$X![TSW&``.2H`(&`X%ORSW*``!"I`*((@0BB`-@` +MH02AM@LO^0G8K@LO^0/8SW"``(Q"/@[`!M'`X'Z!X!/T`-G/<*``M`\\H`O( +M!""`#_[__P,+&A@P"\B'N`L:&##L\<]P@`!$)02`42"`@!#RSW"``%0X`("` +MX`KTK@FO_9#8@>`$],X,P`,/\`#:GKH`V<]PH`#\1$&@X'@AH,]PH`"T#SR@ +MSW"``/@>`X`8B(3@!O2*"@_]@.`%](X*0`*\\;SQ\<#R#T_VSW6@`,`O.H7/ +M(3],]S@`#DJ`2+@>`&]`6+@>`!V`/R`-B`X,H@@@\``!`G!?3/ +M<(``?#0"@('BSW.``-`T`*,2],]R@`#DJ`2*@>`%]`6*@>`!V`+R`-B`X,H@ +M@@\``!`G!O3/<(``?#0!@,]R@`#L-`>B^07O_P.!X'CQP,]P```('/(,+_ZA +MP?^X#?+/<*``+"`0@`39?-H]VT#`BW`&"R`%%[NAP-'`X'[@?N!XX'\!V.!^ +MX'CQP.'%SW$#`$`-SW"@`*@@+:#/<:``P"\4@?"X%($,\@0@@`\(````UW`( +M`````=C`>`?PAB!_#X+@`=C`>(#@7_05$0"&H+@5&1B`$?#/<*``J"`-@.3@ +MSW6@`*POC_</'`6@@@`.'% +M'@@@``AUY@@@``AS<'7*(T4#$'/Y!6_VRB#%`/'`X<6AP0#=0,56#J_\BW"" +MX(H@_P\,\L]P@`!`1`.`((``P")X@.#*($P#Q05O]J'`X'CQP*'!`-A`P,]P +M@``\J2&(@>&+(#@RB`L`//QX'CAQ<]Q +M@`!8'22!(('/$@@```)($``:KA( +M(```X'_!Q<]Q@`!@1`N!0(`.@8#@RB"!#_____\*\@*`0GA((```F2`&`$@@ +M``#@?N!X\`&],]R@``0OQGPC@Y/_8#@-_+/ +M<(``0"((B(?@,?3/<(``4*N8$(``SW*``!"_`K@6>`!B[;@C\L]P@`!0JY@0 +M@0#LOQ)I%G@:8A/R`(*(N`"B`=@/JL]P@`#X'@&`P!``!A$@0(#,(:*#2`O" +M"`?P#@S@"*^J@."4"\((SW"``,@'(("'X`4],]P[?Z^ND#`BW`$V7W:/=NB#^`$%[M& +M'D(311Y"$ZH,;_]''D(3_@Z/^`>&)H9"<`(@0@#_N@+T!J9,%H`0@>`&]$P> +M0A,"\`"F:0-O]J'`X'CQP!(+3_8(=L]PH``L(/"`A^8`W8SV"B'`#^MRBB`- +M`HHC"0.8=8D`K_>X!^X'CQP/X);_8`V<]SH`"T#QR#/*///'`=@E/]L]V@`!0'0"&`>"!X`"F +M`-T*]`'9SW"@`,@<,:"B"Z`(*'#/<0$`0FG/<*``["$2(2:D%B.!_ +M"JGQP(8+3_V`X#/RSW"``$`B+)#/<(``^!X>D!!Q*?+/<(``U"MHB$J(1"L^ +M"P`A@'^``&0K57@&B,]Q@`#8*H'@%_0`@8JX$@QO_0"A@.#`#F(&RB`B`,]R +M@`"D0P:"`X`@@,=Q````*,X.H`9(<-'`X'[@>/'`C@A/]L]QIP`42`#=J*$' +M@<]V@`#F$('/ASW#S#__\$*&@V+:AFKCU&A@`SW&D`+@] +MFQ$`!L]W@`!0'0.FIA$`!@2FDA$`!@6FHQ$`!@:F_]B;&5@#IAD8`)(9&`"C +M&1@`SW&D`.S_SW```/__IZ$&H0"'`>"!X`"G"O0!V<]PH`#('#&@3@J@""AP +M!-C^#"_W0"8!$@W8\@PO]T`F@1+/<"@``@'/<:``["<&H8H@C0`&H0"'0B!` +M@`"G!?3/<*``R!RQH"4`3_;@>/'`M@\/]E$@P($-$@\VSW.```"J`Q(--L]Q +M@``0J_1[$8L0$X0`$O(!X`AR,A6%$&>1`AD"`<]V00"#`&:QSW.``#@]`ZD1 +M\$`D0@`Q%8400JG`$P,!`ZG/=B$`@@!FL<]S@``\/;!RQ_?$H0"#`>``HP2! +M5/#/!(`$\AN0`_`:D`H-``>`X#KTSW"@`!0$`]DCH"#8#!H< +M,,]Q@`#`.Q:!`>`6H0/(`-J8$`$`I!`#`)080`">$`$!K+N2&$0`OA`!`:V[ +M@!`-`:08P`"0&$0`?A`!`8`8A``]9;`0`0&B>3!YL!A$`((0`0%^&(0`AB/E +MC[(81`#@"@+]=08/]O'`^@TO]@AS$(DS$8T``=I`JPT2#S;/=H``**KN9L]R +M@`!8JDC.'%SW```.@MSW.``!`'`://<(``#)$`H\]Q@``(R,]R@``,10""H(&@ +MH`""'-V@J`1I`:)5(4`$`Z(8V`*B52'`!06B`8$$HFAP`Z$"@8VX`J'@?\'% +MX'CQP,(,+_8*(%`(@,'`"5`'D\A3P/HH@*(42!`@&&F!?*#N8VY +M**+/<(``?`@"HQC8`J;/<(``_`<`@`P>`!'/<8``Y`="#>_Y(($`V<]RH``L +M(%""@.!AADJCQ/988`JCSW"``-X'`!"'`(0O'P`G=4*-42)`@"]P6/1,(`"@ +M"_3/<8``A+`;8<]P@`#P!V"@2O#/H#B`!"%`$``+``")8,`A"\?`"]P`"%/#@`G +MAA^``&BM1"B^*$`FCP4R)T\>.V/'$O>5!QX*L")8,`IO:$ +M+Q\``"%`+AMCSW"``/`'8*`.E0(@``$.M0Z56&`.M66F!_#/<8``8+`X8`6F +M#I6Y`R_V!*;@>/'`2@L/]@AV`(B,X,]U@`#4+&@5@9"D](#A!_(#CFD5@I`0 +M*#7/1K'0*0(XZ`X&D=0I`DCFH= +M0I#-]@5NJ7&"(4(#I@B@!0W:$A,F``X``B?0%%T2$28*<#8,+_8J<04I/B!<$@(F`B!!+E!QP"!F +M`,]R@`!'+#1N*F*,(L./$O0J<04I/@``(<%T"?"@`F@!^``$`L0*@` +MV`3P`=@`V8#@"_1PW`(E`!-$+CX7)W!&"*`&^6%K%8"0`>80=D('Q?\)\(#A +M!_(`V+X(K_EH'0*0.0(/]N!X\<`2""```MCZ"0``T<#@?O'`U@DO]DHD`'(( +M=\]P@`#X'A4@T`,`$`T@`-[)<-JEJ"!`#<]Q@``L4/0A`@#/<8``^)$4>4"Q +MSW&``#Q2]"$"`,]Q@``HDA1Y0+'/<8``/%#T(0(`SW&```B2%'E`L<]Q@`!, +M4O0A`@#/<8``4)(4>4"QSW&``"12]"$"`,]Q@``8DA1Y`>!`L0B%Y;@%\@39 +M-*4"\-2EY+@'\@G91AU$$"[:!?`4V48=1!`RVENU68U1(`"`66$P>48=1!`: +MX3JU"O(*V%0=!!`&V%8=!!`'V`CP$-A4'0005AV$$P78#Z6F"*`#Z7`\C2AP +M1!U"$(8@`P#FN5@=`A#*(D$`#/)0(<,!;WI$'<(04"##`6]X6!W"$.6Y"/)( +MD0=PA#DN07RI;A8'0(042'`@`7RI+I$'8(0@N<5\N8-[_CI<``0 +M`""Y$``&42!`@/'8P"@B`!:'000`M@:I0/P6AV$$P#8%Z48I:8.+_WI +M<"B%`=I(&)#/=X``^!XCA\]UK`#4`1J!3!X8D(+@`MC*(.(`T!T`D(H@!``/ +MID81``&P'@`01A$``;0>`!`?V`BX#J8(@5$@`(``V(NX"O(0IFH,C_C/<*`` +MI#`!@(2X"O`1IJX,C_C/<*``I#`!@*2XSW&@`*0P`:'/<(``=$4`@."X"O*& +M(/\.(K@4N,]Q@`#D!0NAM@Y/^&H,``'*#T`#3@B``\]P``!555H>&)`!V5D> +M6)#/<*8`*``OH`.'6A`!`<]PI@#H!R:@8@C/_`.'D@[@!`V0`-B,'1B0!]B- +M'1B0`-B+'1B0SW"``+@J((!@>038@.`.\L]Q@`"D%AJ!.X$D>/&X!O(>"Z`` +M`M@%\)8*X`8!V,]RH`#$)P\2`(9CAT0@`0(;@P\:&(`/$@"&H[@/&AB`#Q(` +MA@5Y#QI8@#R#SW"@`#`0)*#/<(``4*L0>(\:&(#/<(``H(;/<8``H)80>!"Y +M)7B0&AB`BB`$`)(:&(`=@T`:`(#/<(``.!Y3&AB`#Q(`AI^X#QH8@`#8$!H` +M@!Z#'!H8@*T&S_7@>.!^X'CQP,]P@`#`?Q@0!``*(<`/ZW+/<```Y0[>V[T# +M+_=*)0``X'C/<8``P'\%@>!_!J&`X/'`--@']!X,C_U0($$$!?`6#(_]3R!! +M!%(,K_TTV-'`X'Z`X/'`]-@(]/H+C_U0(`$`]-@'\.X+C_T(BBZ!!R$ +M,.P1`@`('(0P\!$"`/01`0`*'(0P#!Q$,`#:2':H(``$`]D1(8"#"?(4)($S +MX)$((L(#))$)(T,``>8`)8$1!2F!#P,``"`O<04M/@$W<47WC!`!``'A`O`` +MV;"`C!A``%4@S@53)<$0%"9!$$"Q7!`"`4HD`'0`WZ@@0`+T)L$3,'+*(DL` +M`>=5(,$'D!B$`,*]%"%-`V"U?!`#`4HD`'(`VJ@@P`'T(8T`""-#`P'B!N-P +M>Y(8Q`#M!._UI,#QP'X,[_68<&]_`[^D;\]P@```.;9@HF^U8(GC!N?P8`;T +MZI&,)P*8RB!K`('B"O1T>4&1'66Q/'`(@S/]72`7('8$0T`<'+"(L8`]X$S@#!WPB=&$`+; +MSW&``+@Y]"'$``81!0$`V2Z@^F(%*H(/`P``("]Q!2Q^`S=QC!`.`,+W;J`% +M+7X#-W%-]Y`0`0&4$`(!`N%0<8/VY.;#]P'9+J`A!,_UX'CAQ>'&=(!<@=@1 +M#@`"(L0`4X`W@3P0!P",$`@``B&%`,]Q@`"X.0+:]"&*``81"0'/=8```#E" +ME6&5"A$&`9(0`0&CE0@C0P``)0$!!2F!#P,``"`*)$`.!2J^$PPD0([6]RJ0 +MC"$"B-+V`MDOH)`0`0%^99081``AD#!V;``*`*)Y(;`!V9@80@`P\`4IOA,, +M)$".5O=,)X"`%/*0$`$!E!`.`0+A,'8$]HP@`9G*]\&0`=G=9;%R+Z"8]J&P +MY?%,)X"`#/2AD``FP0`P=)/>F"TX'[@>/'`B@K/]7*`R('8$0(`<';")L80<8`C@7!QPB'& +M``#?SW.``+@Y]"/$`P'=V6$%*8$/`P``("]Q!2R^`#=Q]"-#`^V@Q/<"V2V@ +M!_`%*[X`-W%#]ZV@G0+/]>!X2(%2H$.!4:!<@52@-X'@?S.@X'CQP!X*[_4" +MVPAU*'8!V`"QJ7".#>__385-A:EPR7&"#>__`]M-A:EPR7%V#>__!=M-A:EP +MR7%J#>__!MM.A:EPR7%>#>__"=M.A:EPR7%2#>__!-LU`L_USW"@`"P@$(#/ +M#,("*`$_(0\(#A$?*!X`;T!8J!X`'8`_(`V(#@I`4"^('A!?("V.!_`*+@ +M?O'`8@G/]<]V@`!0'0"&`>"!X`"F`-T*]`'9SW"@`,@<,:"."R`(*'#/<(`` +M8`<@D(:Y$+D%(8(/``#"$L]QH`#L)T:A`9`0N`4@@`\```(3!J$`AD(@0(`` +MI@;TSW"@`,@``I@#="O0! +MV<]PH`#('#&@F@H@""APSW*``(`O`(K/<:``["<0N`4@@`\``,)I!J$!BA"X +M!2"`#P```FH&H0"&0B!`@`"F!?3/<*``R!RQH'D`S_7@>/'`X<4*)0"`SW&` +M`"1'`!$$`##R3"0`@,]PI`"X/0#;&?2;$`T&SW*``"A'H**F$`T&SW*``"Q' +MH**2$`T&SW*``!Q'H**C$`T&SW*``"!'H**;&-@`_]JF&)@`DAB8`*,8F``! +MVL]PH`"T#UR@-_!,)`"`RB+!!\H@@0\``'X9RB.!#P``_`(4!>'VRB'!#\]P +M@``H1T"`SW"D`+@]FQB8`,]R@``L1T""IAB8`,]R@``<1T""DAB8`,]R@``@ +M1T""HQB8`,]P@`!$)02`SW*@`+0/(KC`N!RBH0>O]0`90`'@>/'`%@^O]0#8 +MSW6``+@J((5`>8S@%/3/=H``P"H@AF!Y`MB`X`ST((9@>0/8@.`(]`H-;_U0 +MV%$@@($$\@#8`_`!V"\A!R#/<(``[#;/=X``T"IV"F_Y`*?/<8``-#T4@0'@ +M%*'/<8``4!T`@0'@@>``H0GT`=C/<:``R!P1H?((``C/<8``O"X$@8'@%?0F +M@<]VH`#L)V!Y`-C/<(``R)L8B(#@%_3/<`$`!@$&IL]P$@`&!!;P"B'`#^MR +MSW```(<9BB/%"4HD``#Q`^_V"B4``<]P`0`'`0:FSW`2``<$!J;/<(``R)L@ +M@(#A`X`L\N"'1"B^`\;8DK@&IB"%)W=@>0#8C.`X\B"%8'D`V)#@,O(@A6!Y +M`-B1X"[R((5@>0#8DN`H\L]P.0`",P:FSW`Y`(),!J;/<#D``F8&IL?8E;@9 +M\$0HO@,`(8]_@`#(4L?8DK@&IL]P```",P:FSW```(),!J;/<````F8&IL;8 +ME;@&IO8+3_[/<(``R)L8B,]Q@`#(F^H/8`0@@4PA`*`5\L]P```";@:FSW#! +M`$)N!J;/<`,`PFX&IL]P-@!"EP:FSW`"`$)K!J;//$+@%((`/``#"<0:F +M!H\0N`4@@`\```)R!J8!CQ"X!2"`#P``0G(&I@N/$+@%((`/``""0#8D.`,\B"%8'D`V)'@!O(@A6!Y +M`-B2X`GT#(\0N`4@@`\``,)_!J;/<`$`1FH&IL]WH`#('Z07$!!,(0"@"?+/ +M<%``QG,&IL]P(`#'0#8C.`9\B"%8'D`V)#@$_(@A6!Y`-B1X`_R +M((5@>0#8DN`)\L]P@``&=`:FSW"```=T!J;/<(``QG,&IL]P0`!"=`:FSW"` +M`,=S!J;/<`(`1FH&IL]P$`#&:@:FSW"``,B;6(C/<8``R)L`B"2)@.(!VL!Z +MSW.``,B;L@U@!WF+)-@8V4X.X``',]J!X!7RSW"` +M`#0]4!`$`,]P@`#(FPP0!0`*(<`/ZW+/<```JBCE`._VBB/'#*07`!#/<8`` +M-#T"(``$$Z'/<`(`1VH&IB"%8'D`V(S@%?(@A6!Y`-B0X`_R((5@>0#8D>`+ +M\B"%8'D`V)+@!?+/<&4`PFX&IL]P@`!0'0"`SW&``%`=0B!`@`"A!?0`V%$? +M&)`U`X_UX'CQP,8*C_7/<(``0"X4@(#@B_)6"6_^!]AZ<,]P@``0.@R(AB#_ +M`4.X8;B&X/0`#0#/=H``R)LDAL]R@`#XF3,F`'"``%1(0"(1"P2Y-'E`(A`* +M0"(2!D`B#PA`(@T$.F)`)P%R%'D`><]Q@`#H+TAP5?#/<8``"#`$:E'PSW&` +M`"@P0"(``DOP0"(``\]Q@`#H+^8-+_X`V@2&SW&```@P!+@4>+A@._!`(@`' +MSW&``.@OQ@TO_@#:!(;/<8``*#`$N!1X^&`K\$`B``7/<8``"#"F#2_^`-H$ +MAL]Q@``H,`2X%'A"`)P +M<@TO_@#:!(;/<8``*#`$N!1X(G!>#2_^`=I_^:G#Y`8_UX'CQP,]P@`!` +M+@^`@.`0\L]P@`#(FP2`SW&``$B;`K@4>#A@SW&``$@PA@E/_M'`X'[QP'X) +MK_5$VL]P@`"$4L]Q@`"LJ0X/X`0`W@+=%@@@`,EP8;V`Y0'F.O?!`8_UX'CQ +MP$8)K_4`VL]Q@`#X'A5Y8($$N``@D`^```Q/N1N8``"!!!`/(,]V@`"$4KX8 +MV`.@@4*&BB`'#V&&'67P'8`0[!W`$""!1H;/=8``K*EEACA@^!B``!8FP1/T +M&,``%B7`$P3@!.&J":_U"-H,$``@%GX6?01M)&Z6":_U"-HM`8_UX'CQP,(( +MK_42V:G!"'9R"&`'BW!*)`!Q`-JH((`"%B2`,"B(@>'#]F&Y**@!X@+"````Q/.&#L&,``\!B```"%!L(%PSA@^!B``(/! +M]!C```07$!#/<(``K*D6(``$!.`:":_U"-KCA\]P@`"LJ8?!]G@$X`8)K_4( +MV@#`((6Y&1@`((6Y$0`&42``@`GROAG8`R"%OQ$`!H"X!_"^&1@$((6_$0`& +MH+@V"Z_\OQD8`(#@!?2F"H_\@.`#\@#8`O`!V!!VZ`ZA!\H@@0,`A;D0`091 +M(4"`\=G`*2(!RB&!#P``DP#`*2$!I@UO^H080``E`*_UJ<#QP,(/3_7/=H`` +M$$>`X<]U@``<"!+R((:`X0WT`*7J"R_X#MCR"J_^BB`0``'8`*8.\""%)7@+ +M\*H*+_@.V+X*K_Z*(!```-@`I@"EX0=/]?'`8@]/]<]Q@`"<*@"!H+@`H38/ +M+_L!V,]P@`#`D``0!`!,),"`RB'-#\HBS0?*((T/``"!#,HCC0\``-H`S`2M +M]LHE[0!,)`"`J@`N``#=%&T`(($/@`#`D`>1QI'DD1"X!7X%D4.1$+@%?P*1 +M$+I%>!IP#@EO]\EQ6G#/<(``Q%KP($$#1"T^%PHA0"X`(8!_@``L02"@8@VO +M^@IP"'$`(8`O@``@0?H,P`7Q=H3W@.<9],]P@`"X6O`@00-$+3X7+W8`(8!_ +M@`#402"@+@VO^DIP"'$`)H`?@`#(0<8,P`7/<(``P)``@`'E$'5D!\7_S09/ +M]>!XX'[@>,]Q@`#P+,]P@`"\+N!_(J#\'`BT\<`:<,]P@`#`*B"`8'D!V('@ +MRB'"#\HBP@?*(((/``">&@+90(4'V&!Z`MD"CAT&;_4`KN!^X'C@?N!X\<#/<(`` +MX$0`@(7@?``%`,]PH`"L+QJ`4B```%$@`(`T],]Q@`#`+H<]P@`"L +M*@"`0'@*"$``SW"``*@J`(!`>'X)P`""#<_]G@X/_`;PY@RO_(H@B0S/<*`` +M>$4`@`0@@`]P````02@^A?+USW"``/@>(X!(@3214R(``!8,X`(!VSH(+_@2 +MV-'`X'[@>/'`X<6TP0;PG@RO_(H@B0S/=:``M$=Q%0"6!""`#W````!!*#Z% +M\O6*(/\/;QT8D&L=&)":"*_XBW"&"T_\@.`.\F\5!)9K%066"B'`#^MRSW`` +M`+$340*O]C3;V@M/^`8.``0=!6_UM,#@>$"(`=@`H6BZ`KI5>L=R@`!`+F." +M8Z%A@F&A8H)BH62"9*'@?P"BX'CQP'8,3_7/=X``Y$0&AP.`SW6``-Q_((!) +MA0`B@`\M`,#&`GF`X8(`+`"AP<]V@`!0'0"&`>"!X`"F"O0!V<]PH`#('#&@ +M@@Z@!RAPBW$R"6_V0M@`AD(@0(``I@;T`-G/<*``R!PQH``4!#$$)+Z/```7 +M_\HAP@_*(L('RB""#P``IA/*(R(,B`&B]LHE(@``A8*XA@X@``"E)@@@``'8 +M`(6BN`"E*87'<2T`P,;^">`%Z7`I!&_UH<#@>/'`D@MO]0#:SW&``(A*`(&[ +MP5?`!(E*)`!R>,#/<(``^!X#@`B`P+A`P%\4@##/<8``I!9!P#C`0L!>%(`P +M0\`:@3N!!'DQN<"YJ""``@#;`"2`,&08P@`!XD]ZSW"``-Q_8I#/<(``O`9` +MD%!S,?3/+MKSW.``-Q_*A.#``XC0X/* +M(V(``KME>`+P!]B`X(8%(0!%P,]PH`"T1T<0`(:`X,PA(H!N!0$`SW"``-Q_ +M`!`$`%$D0(#*(<$/RB+!!\H@@0\``*H3RB.!#P``=@!$`*'VRB4A`,]Q@``0 +M.@Z)SW.``-Q_AB#_`4.X*!L"``^)AB#_`4.X*1L"`!")SW&``-Q_0K&&(/\! +M0[@J&0(``-F>N<]PH`"T1U,86(#@>`#94QA8@,8)#__/=H``4!T`A@'@@>`` +MI@GTSW&@`,@<`=@1H88,@`\FZ97K/`ZC +M;@K/_4;``,"`X`ORBB'_#\]PH`"T1V\86(!K&%B``-@#V43`4<%(P,]Q@``$ +M@`AAB."V`"H`1\`(P07`$2!`@)`#`0`'P``D`3!D$8$`@>&``R$`@W`!V608 +M0@`'P<]PH`"T1V`86(#/<(``^!X#@!"YF[DR((`/``#8`I^Y@.`!V,!X#[@E +M>,]QH`"T1U\9&(`&\,((K_R*((D,SW"@`+1'<1``A@0@@`]P````02@^A?+U +M`MD`V!IP!\`1(`"$`@,A`%#!SW"G`!1(7!@`!$P@`*`C\DP@0*`4](H@Q#:* +M(80X'_`<%`0P"B'`#^MRSW```*L3I-M=!F_V2B4```HAP`_K!"X@;B'N(RX +M!J<@AD(A08`&],]RH`#('`#8$:)*)``ABG5`((`Q$'A+P$`A@#$0>$S`0"A` +M(4W`"B:`)`'A@>%AO2"F"/3/<:``R!P!V!&A6@J`!P/`-6T`)1<6+R?()25X +M$'@0N(4@B@`&IT`O@"&!N)>X`"53%@:G+R/()$`K@"&!N)>X!J<+P`:X@;@& +MIPS`!KB!N`:G`(9"($"``*8&],]QH`#('`#8$:&2P)/!E,*5PV(+(`56),0R +M-L"`X`GT`""!+X``A#T0B0'@#W@0J0#`@.`,\C8.#_R!X`CT`-AVP`3`@+@/ +M>$3``,#/,=P@`#TDD*P8[``A@'@@>`` +MI@GTSW&@`,@<`=@1H0()@`<*P0;`0"^"(8&Z!+D&N#A@M7C'<(``])(BD#QY +M$+DE>D:G(I#`N;AY!2&!!"\B2"`CD$`K@B&!NCQY$+DE>D:G`Y#`N+AX!2"` +M!2\F""``AD(@08`)],]RH`#('$HD``!$&@`!0B14($PD`*`D!LW_@>``I@GT +MSW&@`,@<`=@1H7X(@`<+P4`J`"0&N8&Y)7@&IPS`0"X!)`:X@;@E>`:G`(9" +M($"``*8'],]QH`#('`#8$:$0P6&Y@.'P!.W_0"!`(!'!8;F`X0C`3`3M_P'@ +M`(8!X('@`*8)],]QH`#('`'8$:$:"(`'SW`(`(80!J<`AD(@0(``I@?TSW&@ +M`,@<`-@1H:X+S_X&\#X-;_R*((D,SW"@`+1'<1``A@0@@`]P````02@^A?+U +MK@P/^,]P@`#`'9SW"@`,@<,*!+V<]PI``<0"2@X'[@>,]Q`0"X4L]P@`"P*N!_(*#/<8`` +MW'\`@8"XX'\`H>!X\<#&#"_UN'!3(($`SW"``#1:*&"!X,HAP@_*(L('RB"" +M#P``E1G*)((/``#^`#`"8O;*(^('SW:``,`J((9@>0'8@>`1\B"&ZW5@>0'8 +MN'#/<```EAD*(<`/J7(BVP$";_:*)(,/S00O]0'8"=G@?R"@X'CQP,]P@`#` +M*B"`8'D(V!!YSW"``(0*Q@D/]LX-X`,'V'H*S_W^#P``*@@``-'`X'X(<5B) +M`8"`X@*A"?19B8#BPB"B`,`@H0`"H>!^X'[@>/'`_@L/]<]V@`!0'0"&`>"! +MX`"F`-\*]`'9SW"@`,@<,:`N#F`'*'#'V)2XSW6@`.PG!J7/<`,`@BL&I<]P +M`P#"1`:ESW`#``(L!J7/<`,`0D4&I<]Q``#"=,]P`P#"=`:ESW`#`()O!J7/ +M<`,`@FP&I<;8D+@&I2:EU@U@!PK8SW```()L!J7`'"MC/<````BP&I;H- +M8`<*V,]P``!"10:EJ@U@!PK8SW```()O!J6>#6`'"MC/<```@BL&I8X-8`<* +MV,]P``#"1`:E@@U@!PK8SW`3`,8`!J5R#6`',M@`AD(@0(``I@;TSW"@`,@< +M\:!Q`P_U\<`&"P_USW*@`*PO-8)1(0"``-\&\@';SW&@`.PG9J&`X`CTSW"` +M`*PJ`(!`>$KP%8)1(`"`RB'!#\HBP0?*(($/``!_&"! +MX`"F"?0!V<]PH`#('#&@Q@Q@!RAPSW```$(M!J7/<```@D8&I<]P``!"8`:E +M`(9"($"``*8&],]PH`#('/&@K0(/]?'`1@H/]<]P@`"X*B"`H<%@>038@.`S +M\L]V@`!0'0"&`-T!X('@`!Q$,P"F"?0!V<]PH`#('#&@6@Q@!RAPBW$*#^_U +M`-@`AD(@0(``I@;TSW"@`,@@+8`!0! +M,4"%`]A@>L&Y.0(O]:'`\<"^"2_U`]C/=H``N"H@AL]U@`"X-V!YHL&`X`;R +M((9@>038@.`&]$L#(`!(%000`]@:<,]WIP`42,]VH`#L)Q(([_T%V`ZESW"` +M`%`=`(`!X('@SW&``%`=`*$)]`'9SW"@`,@<,:"J"V`'*'`#V%H.[_6I<038 +M4@[O]2)M!=A*#N_U)&T+V$(.[_4F;0_8.@[O]4`E`1(VV#(.[_5`)8$2-]@F +M#N_U0"4!$SC8'@[O]4`E@1,(AP2E#8<%I0Z'!J7/<*<`F$<<@`>E%X<(I1:' +M":7/<*L`H/\8@`NESW"K`*#_&8`,I<]PJP"@_QJ`#:7/<`4`Q@,&IL;8D+@& +MIL]P+``"`0:FSW!:`$(!!J:*((L`!J;/<$``APT&IL]PT0#"#0:FSW#```<. +M!J;/<(``4!T@@('A!O3/%`GFZ"2_U+W`!PD_@SW&``(B!%*57H1BASW!``(<- +M!J;/"&_[$J4RA56%+'@WA2\@0`Y">3EA4@DO]35YX+@<>,`@ +M8@""(,0"SW&``(B!$J43I1:ASW"``%`=`(`!P@'@5:&!X,]Q@`!0'0"A"/3/ +M<:``R!P!V!&AH@E`!P&5$+B%((0`!J8"E1"XA2"%``:F`Y40N(4@BP`&I@25 +M$+B%((\`!J8%E1"X!2"`#P``@@T&I@:5$+@%((`/``#"#0:F!Y40N`4@@`\` +M``(.!J;/<(``4!T`@,]Q@`!0'4(@0(``H0?TSW&@`,@<`-@1H02%*X4(IP6% +M#:<&A0ZG"(47IPF%%J?/<*L`H/\XH"R%.:`MA3J@1@JO_0Z%2!4$$(PD@H!% +M]HPD/X$-]O((8`<*V'H-@`1"($`@@.`"!A4`B(#@!_+/<(``&#@`$`0`B'"M +M!N_THL#@>,]P@`"X-^!_%(#@>,]Q`0#D;,]R`0`(8Y4$[_D`V.!XX'[@>/'` +MSW"``*PJ`(!`>,]P@`"H*@"`0'C1P.!^X'A1(`"`SW*``'@'"_*`X5'8P"@B +M!,H@803`*"$$`O``V.!_`*+QP-X-[_0$V,]U@`"X*B"%8'FAP8'@O?1"#^_[ +MBB"$"('@M_0`V````IPGT`=G/<*``R!PQH.X/(`"N_UR7$@A6!Y`-@`%`4QD."H<88A_`\1](PA`X`>\@HAP`_K@`4`3$`%`4Q3"4`@,PE8H#,):*`RB'"#\HBP@?*(((/``":&_UR7%`A0`4#C$%V,&^8'K) +M<8/F$/(`%`4Q"B'`#^MRSW```)P9BB-%!0T"+_9*)$```(="($"``*<6],]Q +MH`#('`#8$:$0\,]U@`#$*D"%`=A@>@AQ0(4$V&!Z`]E`A0788'H#V94$[_2A +MP.!X\<`R#,_TSW6``%`=`(4!X('@`*4`W@KT`=G/<*``R!PQH%X.(`"!X`"F`-T*]`'9SW"@`,@<,:#6#2`'*'#/ +M<```PBS/<:``["<&H<]P```"1@:ASW```,)?!J$`AD(@0(``I@;TSW"@`,@< +ML:"]`\_T\`('$$``&42!`@0?T`8'$$``&42!` +M@03R1@SO]Q/8SW"``*`J((!@>0O8T<#@?O'`@@SO^XH@B`6`X`WR&@DO_0#8 +MSW"``+@J((!@>038@.#,"P+_T<#@?N!XSW"``/@>`X`(@,]Q@`#PJ5$@`(`# +M\@&)`O`"B>!_`*F`X/'`N'$.]`HAP`_K!XZ"!M`_`@10`$)8(/`0`` +MP"ZZ97I0'1P.!^"B'`#^MRSW```*@9BB,$#NT'[_5*)$``X'CQP.'% +M`-W/<(``A`:F""``H*#/<*<`%$BHH*4"S_3@>/'`H<&X<`#80,!3)8``@>`1 +M\H+@(/*$X"7R"B'`#^MRSW```*L9BB.*"ID'[_6*)(,/SW"``,`J((!@>0'8 +MA.`!V/'`D@G/]`/( +ME!```,]V@`!0'00@D`\!``#``(9!*)`C`>"!X`"F`-T)]`'9SW"@`,@<,:"R +M"R`'*'#/<20`!P'/<*``["(X$H@5$A`(#*(8(/@`#' +M(,HA@0^``((X$H@5$A`(#*(8(/@`"'4\HA@0^``$=7)J#/<00`QS$F +MH`"&0B!`@`"F!O3/<*``R!RQH/D`S_3QP*'!SW&``/@>(X$O*`$`*('`N0`A +M@P\``"+23B"!!RG8$KCP(,``SW*``!/.-'E984#`BW`*#B`$`]JAP-'`X'[@ +M>/'`X<6X<,]P+``&`<]QH`#L)P:ASW*K`*#_&H)3)8T`@>4`VQ'R@N4V\H3E +M5/(*(<`/ZW+/<```@1F*(X4#L07O]8HD@P_/HX6HA5$E +M`)`*\L]U@`"&4Z:ASW4#`((""?#/=8``1E>FH<]U`@""`J:ASW4$`,8QIJ'/ +M=4P`0@&FH<]QIP`42'>A@+@:HJD'C_3QP"X/C_0#R`'=SW:G`!1(E!```*BF +M!""`#P$``,#"#N__+KC_V)NXSW*G`)A''*+/<8``A`8`@0#?@.#*(<(/RB+" +M!\H@@@\``*P9RB."#P``OP#*),(#<`3B]O]*"AX'CQP+X. +MC_3/<*8`G#\9@%$@`(!7\L]V@`"0!@"&1H"@$@`&+R@!`$X@@0=!*=``3""` +MH`GW2'"`(`H`,B``!(#@#_0*(<`/ZW+/<```K1F*(TL"BB2##PD$[_4*)0`$ +MSW6```C.0"7`$MH+;_8)V0#8D@FO_P\@``2`X`#8#R``!`3T'@S/_P3PI@W/ +M_P/(N1"``!MX@+A`A@JM)H*6(4$#`"$`!!B(C"##CP)Q!/)AN`]X&*DF@J`1 +M``:?&1@`P@O/_V$&C_3@>/'`SW"``,`J((!@>0'8@>#*(<(/RB+"!\H@@@\` +M`)T9RB."#P``CP'*)&(`:`/B]/'` +MJ@V/]#IP&WW/<*8`G#]D$!``42``H!7T9@CO]`/88;V,)?^?\_4*(<`/ZW+/ +M<```I"A1VPHD0`05`^_U"B4`!,D%C_3@>/'`:@V/],]QH`"L+SJ!4B$!`%$A +M`("AP0#=@/2`X,]V@`!0'5CR2@F/_\]W@`"X*B"'8'FI<(S@!_(@AV!YJ7"0 +MX""&*_0`A@'@@>``I@KT`=G/<*``R!PQH&(/X`8H<(MQ$@JO]8H@!P4`AD(@ +M0(``I@;TSW"@`,@"!X`"F"?0!V<]PH`#('#&@S@[@!BAPSW"` +M`/@>`X#/<:``["<.@("X!J$`AD(@0(``I@;TSW"@`,@/'` +M;@@``,]P@`"X*B"`8'D#V(#@(`Y"!,]P@`!$)02`42"`@`ORSW&``/@>38$^ +MD5,B```."R`"`=O1P.!^X'CQP.'%SW6``+`W`(51(`"`#/1B"8`#%@M/^^() +M3_CV#<__`(6`N`"E602/]/'`5@VO^XH@!`*`X!'RL@V/_\8/S__/<(``N"H@ +M@&!Y!-B`X`7R;@]/_PX(``#1P.!^X'CQP*X+C_3/=8``L#<`A5$@0(`=],]P +M@`"X*B"`8'D$V(#@%?+^#*_[XMB`X`_R#@IO_0?85@I@!`AV`@T/__X.+_W) +M<`"%@;@`I=$#C_3@>.!^X'CQP%8+C_2D$`$`^+D(\K80`0'/<*``F`,^H)_P +M`!8-0;RP`!8"05VP`!8.0,^@`!8"04`8A```%@)`4:``%@)!2!B$`$0E`A.$ +MXAKR&-MR&,0``!8#0(CBVW(8Q```%@]`]J``%@]!7!C$ +M`ZEWAB?]'(PG`I()]`+C<'MR&,0``!8/00+P`-_ANV`8Q`,$\@`6#T$H=(0D +M#)`$]`#:(_"`XAKT428`D-$A(H(4\M"(J+G/M=R26L`T@#?PO?BH*KFRB8F$.!X%0*O],"EX'CQ +MP,]P@``T#P[9`=H&"R```-O/<(``;`\)V0':]@H@`$ASSW"``&`.*MD`VN8* +M(```V\]P@``(#PO9`-K6"B```=O1P.!^X'CQP`38X@\O^P'9SW"``$DX`(C/ +M<8``2CC&"R``((G1P.!^X'C/<(``-$?U!L`$X'B`X/'`!?3Z"````-DBH-'` +MX'[@>/'`'@F/]$H-C_O/=H``@`=FV")N`=I:"J_\2'.`X`ST"B'`#^MRSW`` +M`+84V=N*)($)//`"%@413"4`@,PE@H\``/__#/0*(<`/ZW+/<```MQ3`0>.8)K_Q("9N`=JV":_\2'.`X`ST"B'`#^MRH9;/<```NA3EVT`EA!#J\;$`C_3/ +M<:``8!T2L121X'Z!X/'`N'$<]$PE`(#$]DPE@(/,]@HAP`_K!0R($`!C"##C\HAP0_* +M(L$'RB"!#P``X13*(X$/``"B`&P%H?7*)"$``K@4>``@@0^``*@0*'#1P.!^ +MZ+@(\@0@OH\````8`=@#]`#8X'\`J>!X\<"B#V_TV'#/=H``O@8`CL]U@`"\ +M!EH/[_\@C2&(X[D&\@'9SW"``%`X;/`"@(#@7/11(0"!,/+/<8``)"(`E4>) +M4'`H]`"608E0<"3TSW"``,`&`(@FB1!Q'O3/<(``^!X.@%$@0($6\L]P@`!, +M.$"`@.+/<(``4#@,\L]QH``L(#"!0GG7<3$!`"UR``4``-DX\(PF`H`J],]P +M@``PD3>(@.$)\L]P@`#(FP&`$'$!VY5'H8\L=R@`#(#Q?PSW"``(`'P9"AC0HAP`_K +M`"KX'_!Q>!X@.#QP`OT3@W/_\]QH``L(#"!QW%) +M:P#2(J#1P.!^X'CQP&8-;_38<0HF@)"(=SW>@ +M`+0/A!R#O+/<(``51W`J,]P@`!,.,"@SW"``%`XP*@O(`<"'*<5 +M!&_TH<#QP*X)S__/<(``O`8`D(#@^`OB_\H@(@#1P.!^X'[@>/'`;@K/_]X) +MS__V#P`&P@V/_&8/0`'1P.!^X'C@?N!X\,]Q@``D(L]P@`"\!@"01XD0""``#W@" +MC2&%P@SO_P':I0%/].!X\<#/<(``^!X\$(8`#8#`N.8([__(<0&(42``@/'`W@\O]/AQH<$(=AER`-W/ +M<*``M`]P$`D`SW"@`+0/O*`%AN..BW%`)(,P_@GO_T`D0C`*AO8/K_]`)$$P +M@.?2]Q0@3!/P)TX#((P@P`$4@C`"%(,P*@KO_U,F!!`!Y?%UL?/'`P@O/_R8+``;1P.!^X'CQP%(/#_0*)`"`SW"```C( +MI&A5($$$!!`&`07R#"2`@8SW"B'`#^MRSW```-L;JMO!!&_U"B6``3(A``$` +M(0,!`"$.`0`A!0%BB\..`"$"`005A0",($.'S".!CP``4`#,)H&?``!O`,PE +M@8\``)H`!_3EBHGG!?3FBHSG$O(*(<`/ZW(8N!"[!2,-``B^Q7W/<```WANU +MVUD$;_4%)44#`8H")@(!0""0``(B`H3*(<0/RB+$!\H@A`\``-\;RB.$#P`` +MO``L!&3URB4$!(#BQO:`<2H,H`,`(0`$`)4`V0(@``0`M<]P@`#0*\D&+_0V +MH.!X\<#AQ<]P@`!4J0#9):#/<(``9#$BH,]Q@`#X'@"!Q!``!E$@0($\\@.! +M&(B$X#CRSW6``.!"`(5"(`"`RB!B`('@$_3>"Z`$J7#/<8``J$(`@4(@`(#* +M(&(`@.`%]"AP-@R@!"*%SW6``/Q"`(5"(`"`RB!B`('@$O2J"Z`$J7#/<8`` +MQ$(`@4(@`(#*(&(`@.`&]"AP`@R@!"*%/08/].'%`-O/6"AX!W$$/`=Q!#@?\'%X'CQP#8*+_P1V(#@/?3/<8``)"+/ +M<(``O`8`D$>)$'(L],]P@`"^!@"008D0`#9G+G/<*``K"\]H.!^ +MX'B%`P_[\<#N#Z_V%=C/<(``7#@DB(#A#O*#X0GT`XB`X`CT_@X``]'`X'[V +M"$`#_?'[\>!XSW&``%PX`XF`X,3V8;@#J>!^X'BAP?'`X<6LP0#92L&0V1BY +M2,'/%$@`((2 +M\@K`"\&$*`0.`"&`?X``F,L"N0C@-'DA8,]PIP"(22^@H@[@!*EP"-P_!"_T +MK,#@>*'!\<"^"R_T"'*MP0C82L"0V!BX2<#/<(``\*F@@`0AC@\!``#`AB7^ +M$R2]#KT+)D"34,&0PQ;RUW8```!`S":"GP```(#,)H*?`0````3T`8`#\`*` +MKKFON;"Y)7@`HQ##"<4$(X$/`0``P"ZY0"D`!N:Z!7U)Q0WR"L`$([Z/```` +M&$4@P`!*P`7RA2`0`4K`Y+H0\IN]SW"@`"P@!8``VP*X;KB`X,H@S`#)N*5X +M2<`&\.BZ!/*=O4G%$,"!Q4+`J7!&""```ML#R`S"SW&``*06N1B"`!J!.X$D +M>%$@`((,\@*ZSW"``*#+5'I!8,]PIP"(22^@F@W@!*EP"-PO`R_TK<#@>/'` +MM@H/]*/!88`(=4##Z;L`V`JE-/($(X`/`0``P"ZXSW*``-!-"F))(H(`8;I+ +MI1)J%'C'<(``D,S*@,]W@`"(J\:E"X#/=H``^!X%I<.&(,#4AO6/!'[D?@F^ +M0"D/`N5^Q7@$(X,/````$&5X!Z4(A1CBGK@(I4NED/!1(H""&O+/<(``6$4` +M@$'`Z+A"P`_RAB#_"2.X`>"!X,GW@N`$]`;88<`E\`?88<`A\"+`8<`?\$'# +MSW*``)`&0()&@IX2`@:&XA3T!".^CP```!@.],]R@`#X'D2"2(($(KZ/``8` +M``3R`=@*I03P"J4`V`'&Z+X?\D+&(L*@XLHB(0`$)H\?`0``P$$OA!-$)@\6 +M([\!YP0FCA\&````,;X`)L43SW:``-!-,B8.$0(F3A$2\%,FPA#/=X``'%)= +M>DIG!":.'P$``,`NOL]W@`#03 +MA"@$#@`A@'^``)C+`KI4>D=@8;Y88.:E`8`$(X,/+P``W2:[Q7M2(\,#!:5G +MI<]P@`#PJ0.`$2!`@`#?#O+/<(``=$4`@."X"O*&('\/'7A`*,\#!/``WX^_ +M@.<<],]V@`"X*B"&8'D`V(S@$?(@AF!Y`-B0X`OR((9@>0#8D>`'\B"&8'D` +MV)+@!/3>#B_\`-@(A05_Z*4=`2_TH\#QP+((#_3/=8``U"H`A<20R7#J"J`` +MAB#\`P"%R7%Z"F``AB'\`YK@SW.``,RK!?0A@X"Y(:-*@P'B2J//1 +M$P&&P[E0<0WTBB4($!,;6(.1$P&&P[E0<0/R$AM8@[T`#_3@>/'`0@@O]`#8 +MSW&@`*@@*('/<:``U`LX@<]QH`#$)U(1`H85$0*&0A$#AOZ[!O(!V,]Q@`!0 +MJV&Q42+`@!IPRB5B%!+T42#`QLHEHA0,],]PH`#0#R`0`88?$`"&$'$`W22RBB"(!<]R`/\`_RX-3_T`EO8*K_TTEI07`!"`X,]R@`#`.P'9&O)M +M@@'C;:)K@GA@"Z+/<(``5!T@J!^&[K@&\L]P@`!P!B"@"/#ON`;RSW"``'0& +M(*#/<*``_"43@&R">&`,HL]R`*`(`.QP0*!O(D,`['!`H`X?6)!""D`&SW`` +M`/]_SW&@``PD`:$;V`2A50?O\ZEPX'\!V/'`X<6AP>8,+_N+<(#@-_0`%`4P +M424`@`SRA@@``,]Q@`!0JT.!SW&``.R10:$G\%$E@(`$\G(.S_\A\%$E0(($ +M\OX-S_\;\%$EP(`<\@C8SW6@`,0G$QT8D!((``&4X`WR`M@\'0"0SW"``%"K +M(X#/<(``[)$AH!G8F>#%\^4&[_.AP`HAP`_K/'`X<7/<(``4*L_@`0A@0___X\X!"6`7P``<,,]Q@`!0JQ^A1"(`4XC@ +MSW6``%"K)/11)4#1(/(J#\__426`TYP=`!`)\L]P@`!,'P6(F!T"$!7P427` +MTPGRSW"``%PB&8B8'0(0"_`#A98*+_4DA9@=`A`%\`#8G!T`$)P5`!"`X,P@ +MXH!X\L]P@`!,$PN`@.`/\L]RGP"X_QVBSW&``#@=!($!X+.XM;BXN`2A%J+/ +M<*``J"`(@!^%42``@0?T427`T@7T@-B8'0(0F!6`$%$@P(%`*`$&"?11(H#3 +M@KD.\H8*0`(:\!^%42*`T[.X'Z7%(8(/````!T4A``;/<8``W*LLB88A_0]2 +M(<$!1;DE>,]QH`"()!"ABB#6`,]QH`#$)WX9&(#/<*``U`L!VE*@!-@0&1B` +MSW6``%"K'X51(("!)?(4E5$@0($A],]PH``L(`^`@.`;]*UQ$@WO^%8E0!6` +M%0`0E+B`'0`0'X60N!^E#?#/<8``1#L/@0'@#Z$0V<]PH`"0(SV@.07O\QG8 +M\<"V#._S`-D(=@&`P;B#X,H@02`%\KH.(`#)L]SH`#$)T$;F(``VHRZ`B9/ +M`/IBR[K7<@````A`+0\#D+]2]P4GCQ%B&]B#C"("@,?WSW&``+@\$H$!X!*A +M`-F=N4;PY7EB&UB`UW(``,`/4@`.``XB@P\````0SW*``,"%%GJ@XR""!!(% +M`$_W`-@/(,``8;A.(P\(`2G"`WAY!7D`+<``!7H6\$(C`P@`V`\@P`!AN'AY +M!2$"`(HA_P\*\,]S@`"X/!.#BB'_#RAR`>`3HP'8SW.``&"2`*L"&P0!(:-" +MH[WQ`-F(`=`!!`)@`2H!T`$`+9SW"@`/0F(Z`EAL]P@`#LD2&@ +M60/O\PIP\<#>"L_S"'95($T$#VXT2!B@`?R!,A6#J__F!```,]P@`#, +MJPR`SW&@`,@?9.`>H1#8#J$!V!49&(`!AH#@!?11(P#`^_,!AL&X@^`F`@(` +M`(5!P`04`#%!*!`#$(91(("!!A01,3WR#XF!\"$$HB0"`'\CZ' +M2B(`()6Y/J<$N,]R@`"HDB:2!2``!#!P"O+/$MHEIP`9:T$I#2&E>T<8V(`-S.NX#O(0VZNX#!K<,`T:'##/\]UH`#`+T<=V)"4X<`AA@\``),`SW.@`&@L\"-!`"NPCQ4!EN>Y(?3/ +MY]J;A@O:FXH+VIN." +M]J;L\P?P2B0``@SPY[G*(2$`0,$!%((PQKG&NEBH.:B(<*$`[_.BP/'`X<7/ +M<8``^!XC@4B!42(`@"ORAB#_`<]R@`#034.X"F(`VX#BRB'!#\HBP0?*(.$' +MSR`A`\HC@0\``&\`RB3!`+0%X?3*)2$`@>+/<*H`#%"Y@<;W@+VYH0'9):`% +M\*"]N:%EH&T`S_/@>/'`\@^/\PAU#A`\@-$@$VA!`" +M`<]P@`!\JC5X*8!982F@&M[!\<]Q@`!$.PV!`>`-H0'9SW"``%0=`=I`J,]P +M@`#,JTZ`!H(!X`:B`O`!V0+:SW"@`/0F0Z!#A<]P@`#LD8#A0:!(#$(#80>O +M\\EPSW.``%"K6!.!`(#A`-H/]#R38KD0N44A0P'/<:``]"9CH<]Q@`#LD4&A +MU0```.!X\<"^#H_S"'8!@,&X@^``W07RO@@@`,EP"'6`Y3+T$(91(("!(O(, +MS,]Q@`#(.E$@0($7\D#8#!H<,%41``8`V@'@51D8``W(SW&```"J%'D#R$"I +MX@FO_Y@0```&\*P1```!X*P9``#/<(``5!T!V2"HSW"``,RK+H`&@0'@!J$" +MV<]PH`#T)B.@(X;/<(``[)$AH)4&K_.I<.!XX'\(V/'`)+E3(<(`B.+/<8`` +MW%16>0GR09!A@03B<'+*("("`_0@@4!YT<#@?N!X\<#AQ0AUSW"``,RK#(#/ +M<:``R!]DX!ZA$-@.H0'8%1D8@`7PI@WO^GG8`86`X`7T42,`P/CS`87!N(/@ +M#_3/<(``5!T!V2"HSW"``,RK+H`&@0'@!J$`V`SP`851(`"``-C*(.$%(851 +M(4"`RB"A!/4%C_/QP'X-C_//=H``S*L!A@0@OH\`<```.O(O*0$`SW"``)@X +M]"!-`"N&3R6`$&8-(`))AH#@$_2,)0.0SW&``,@Z!_2Z$0`&`>"Z&1@`'O"Y +M$0`&`>"Y&1@`&/`!AOZX#_+/<8``$#H,B4^)$'(,\A&)42#`@"@,@0(&\`#9 +MSW"``&"2(*@N"``&806/\_'`[@R/\PAV`8#!N(/@`-\2](#GSW6``%"K2?00 +MAE$@@($[\A"&[K@+\L]P@`!,'P6($_#.#N__R7`(=^OQ$(;ON`?RSW"``%PB +M&8@%\`6&)H92"<_T42#`@9@=`A`(\AZ%E;@>I1^%E[@?I8`5`!`$(+Z/$'`` +M``_TG+B`'0`0,(:.#*_X5B5`%4`F`!*@'0`0`-@%M@'9SW"``%0=(*BT%0$0 +M!H$!X`:A6!6`$(#@&?3"#H_Z@.`%\A"&[;@!V`+T`-C/<8``LJOT(0``/)4X +M8&*X$+B`N,]QH`#T)@.A!O`"V<]PH`#T)B.@)8;/<(``[)$AH&$$K_/I__R7`:<,]PH``L(`:`$'A,(`"@SW6` +M`%"KRBN(`=`!!1(4""0"8`$J`=`!#J\P'9SW"``%0=(*BT%0$0!H$! +MX`:A6!6!$(#ASW"@`/0F$_3/<8``LJM`+H<;Q`MDCH"6&SW"``.R1(:`M`Z_S"G#@>/'`R@J/\\]P@`!,$PN`@.`/ +M\L]RGP"X_QVBSW&``#@=!($!X+.XM;BXN`2A%J+/<*``J"`(@%$E@-//=H`` +M4*L&\L]P@`!,'P6(#O!1)<#3!O+/<(``7"(9B`;P`X8>#Z_T)(:8'@(0'X91 +M(`"!"_11)4#3!_11)4#2!?2`V)@>`A"8%H`0Y[@+\E^&/H:SNI6YE[H^IE^F +M`-D!W1;PG!8!$('A$/0_AE$A0(+/<8``^!XC@2F!!?)$(0T$!?!$(0T"`_`! +MW039&+@E>,]QH`"()!"A'X91(("!'/(4EE$@0($8]$X.0`*`X!3TSW"@`"P@ +M#X"`X`;R#">_ZBB#>#%$@@,0$]%$A`,;X\\]R@`!0JY@2@`#GN`#9 +M"O0"N,]Q@``0OQ9X`6$MN<"YBA(``4\2@@#/`/H0/9SW"@`-0+,:#@>.!X +MX'C@>.!XX'C@>.!XX'C@>.!XX'C@>.!XX'C@>#&@SW6@`,0G%14`EL]QGP"X +M_Q:A,14`EA:A$-@0'1B0B@ZO^HMP@.`7]``4!3!1)8"`#?0*(<`/ZW(-V(RX +MBB-?!P4&K_2*)(,/!-D3'5B0&]D6'5B0S0"O\Z'`\<#/<(``.)+_U&-G/ +M<(``<(*Z#2_U&-G1P.!^X'CQP"H(C_,:<,]UH`#4"Q"%`-^!X*'!0,<.\@HA +MP`_K<@_8C+B*(Y8(BB2##YT%K_0*)0`$SW"@_G@"SW:?`+C_%J98'@`4SW&@ +M`/Q$&8$$(+Z/```((`/T'8%,(,"D"/+.#:_ZBW"`X,H@`B!"(,$@E.%N`0T` +M,B9!<(```$A`)X!R-'@`>,]Q@`#,JTZ!"((!X`BB#H$(@!:F5@S```#9*'!* +M\,]R@`#,JRZ"!X$!X`>A#H('@!:F\_'/`,H0Z"#(`6INGQ +MSW*``,RK+H("@0'@`J$.@@*`)_#/<8``P#L%@0'@!:$?\,]R@`#,JRZ"`X$! +MX`.A#H(#@!:F`=D`V!;PSW&``+@\&H$!X!JA%J;>"F`$`=B]\<]Q@`"X/!2! +M`>`4H1:F`=@(<8#AO`N"`,]P@`!0JQ^`\[@)\L]P@`"TA>NHSW"``.B"[+`# +MV!&EX'C@>.!XX'C@>.!XX'C@>.!XX'C@>.!XX'C@>.!XX'@1I1$';_.AP!B& +MSW*``,RKD+@8IAB&L+@8IBZ"!8$!X`6A#H(%@!:F4@@``,;QSW*``,RK+H($ +M@0'@!*$.@@2`]/'/`1H0Z"$8"Q\<]Q@`#`.PZ!`>`.H93Q +M"B'`#^MR0=B,N(HCV`<>\>!X\<`^#D_SSW"``,RK#(#/=J``R!\0W0'?9.`> +MIJZF%1[8D\]QH/X@`L]PGP"X_S:@*@C@!0G8`]@>IJZF%1[8DP7PF@VO^HH@ +M50[/<*``#"0'@(#@!/)1(P#`]?-)!D_S\<#/<(``S!.^#B_[`=EOV`:X=@XO +M^PC9!]@*N&H.+_L%V='`X'[@>/'`X<7/<8``^!XC@2F!42%`@,H@H@`K]$2X +MSW&``(@XP[@)8>"Y!?)1)8#1'/11(4"`'/+/=8``^!X#A1B(@>`/\OH/3_J` +MX`?RSW"``$`B"(B'X`7R`X48B(+@!O11)8#1!/(!V`/P`-C%!4_SX'CQP$(- +M3_-$(A!337:&)OP337!-<`0E@%\````@02A^@P7RJ@]/^H#@`_0`WP+P`=_/ +M=8``4*L?A?&X!/(`W:3P3"``H/SUA@]/^H#@'O+/<(``0"((B(?@S"!B@A;T +M`86,(/^/$O0DE<]P``#__Q!Q#/0%A8P@_X\(]`R5UW```/__RB5A$(#RSW"` +M`/@>\"#!`PF!42!`@07RSW"``)Q.!/#/<(``J$XXB2I@02X`$<]Q@`"T3@AA +M%GK/<(``7%5(8."X!?(_A88A]H\7\N&X!?(_A5$A@((1\N*X!/)1)0#2!/(! +MW0SPX[@)\L]QH``,)#&!C"'_C_;S`-U1(("!RB4B$,8.3_J`X`CR!"6^WP`` +M`"+*)6(0@.4H\L]Q@`!0JQ^!Z+@/\HPF`I#,)H*?``!0`,PF@I\``-```_23 +MN!^ASW"``/@>`H#"$``&@.`:\HPF`I#,)H*?``!0`!3T3X%%>`^A#_#/<(`` +M^!X#@`F`X;@'](PF`I`&]%$@@($"\@+=(01O\ZEPX'CQP*X+;_,`V<]P@`!, +M$P"`@."BP0_RSW.?`+C_'://+`;``#/<(``6'\YH,]P@``(K""@!-[/<(``6`?`H)D3@`"@N)D; +M`@#/<*``Q"=D&%B`SW8``/]_$QB8@QO>%AB8@QH86("*)_\?SW:@`/Q$_:;Y +MIHHGF!W/=J``4`SBIE&E4*4\&$"`BB(8"$ZE@!4"$*0;0`!1(D"`SW*``.R1 +M6!M"``WR0A``A@0@OH\`P```!?(!@H#@`_("HB&B@!U`$%$A@,//"(X+CW"(+KN"`,`@8?ARNX4R`0`%$@ +M@,6K\L]Q@`!$)0:!`>`/>`:A02F`0\]Q@`!$)4:!SW&@`+0/-X'`N#!R`-V? +M],]QH`"H(":!C"&#CC(!#0`2<)#TSW*``"BL!8+/1!`)\%0>1!,$(8$/__\``"BB#8,&H@0@ +M@`\```#^*;A6'@00'X;KN"/RSW"J```$!(`)HL]P@`!P@B"(@.%D:#;R@.%B +M`"X``A"$`)]Q`-BH((`#]",/`!7=$[WP)<\3SW6``"BK%7T!X."E'?#/<(`` +M.)(@B(#A9&@:\@(0A`"`X#@I2&J`AH"`;06`1`"W0&!`>`!H0SP!""^SV`````$]`3=!O!1(P#` +M`O(#W3H/;_KQV('E1?."Y1;T`MT$(+[/@`$``,HEHA$%]%$C`,#*)>(0@N7T +M\\]PH``P$`.`@.#*)6(1AN5"!`(`SW:``%"K')9"((0`'X;KN"\D"`%\\L]Q +MJ@``!**!SW"E``@,0(#/HFX!7I2 +MIJRC3:,`@4@6CQ"4YPJC&O(%]HKG&/0CN`WPM^<-\N[G$O1%*/X"02G`<%$E +MP)'"(&(`!]T*\$4H_@)!*0!Q^_$BN/GQ`-@(W2&!%Z:4X"NC'+,.],]W@`#X +M'N.'Z(<$)[Z?``8```3RC+I2IN2YRB4B$N&YRB4A$H8A_@]!*0(!31Z"$"B3 +MA^5%>2BS$_2.X`?=D/?/<8``^!XC@801`0`P<`CWSW&@`#`0*($P<`/R"-V' +MY8(++_N(<`7PH@LO^XAP!""`3X`!``#7<``!```` +MV1;T`=A.'@(0SW*``"BLFA:`$$(>1!!-'D(0-Z8IH@2X*)*)N"5X"+)V\$T> +M0A#/<*8`C`,]@%$@P,<$(8`/.````$$HP@2:'H(0!"&"#P```/`LNB6X17C/ +M2BS'+.'Y3*"+:,_],]QI@",`[V!!"6!'P$````PN4X:0@!. +M$H$`@.&IHQSRC>8L]%$@`,8H]!38SW&@`,@?'J$0V`ZA`=@5&1B`"MU1(`#& +MRB7B$5$C`,#*)2(2BN7W\Q7PCN"2]\]Q@`#X'B.!A!$!`#!P"O?/<:``,!`H +M@3!P!/0'W0/P"-V'Y>?TSW:``%"K3A:`$(#@X?+/"5X1">!$!2Y)7B(N$0G`1)!*<&` +M4B!`!1*F6!Y"$,HA@@\``/__RB&!#P``$!\Z<3>&0!Y$$`0B@2__`P#_*+DW +MIG8);_@`VO*_K!X`$#KR2!:"$#*&H.+1(>&"-/($(8./`````0?R1"$-!B.] +M`>6!Y0GW!"&-#P```"37=0```"0@\@0AC0\&````,;V"Y30`#0""Y0GT@.,4 +M\D0A#08CO0'E@N4.](#C`_+,X@KV5X8R(3"_#/<*``,!`(@#>&$'$'W&,!D`!!RQ$H;KH0VAK!8`$"@9@`0=L8?E!/1J"```"'6`Y0* +MHO_*($(#`-C/<:``U`L0H<]P@`#*0!O0`V(^X#!H<,#+9SW"@`,@<*J`1`R_SHL#QP+H* +M#_//<*``U`L8@$(@``A((```SW&``$0E)8&!X8HAF0X(],]Q@`#X'B.!/H&` +M(9D.$'$`W`KPCN`!V,!X!O!1(4"!$O0`V,]Q@`#X'B.!*8$] +M>5(A`0#`N21X@.`(\A^&D;@?I@OP%X;H\5H,``!8%H`0@.`L"P$`@.5X`@(` +MSW6``%"K6!6`$(#@$_("V<]PH`#T)B.@SW&``.R1`-@!H<#9F16`$("XF1T" +M$"AP`O!"V,]QH`#$)[\9&(``V`P9`(`!V!`9&(`?A?&X%`("`!*%-X7:#B_X +M`-JL'0`0'X7KN%GRSW"``/@>0X!(%8$0%((D>$0@`P%$(0`,0B@$`8!SSW"` +M`*P?P;MH8(FX'*5P%8X0]()-%8`0Q'^&)O\3!']$OM]GSW:``$Q.]";/$V(= +MQ!//=X``T")K9XF[?:5T@G05CQ#D>X8G_Q,$>T2_^V/T)L,09!W$$'*%>J54 +M@GNE1'DD>,]Q@`!<3H!P]"$"`,]Q@`"$3O0A``".'800D!V$$)(=!!"4'000 +M`-A.'0(0F/!.%8$0SW"``!PX@.$`@,"X4_*`X`#;RB`B``KT\*X`"".#X``H!\PCMB.QW"``,PBSW>``/B12(AE?MREA"(8AV$$P5[?:5T%8`0P[@<>/0G`!`ZI<]Q@``(DF0=!!!H +M%8`0P[@<>/0A`P!;I8X=Q!#/,]Q@`"<'PEASW*``/B1/*5P%8$0P[D\ +M>?0B00!B'400SW&``,`B"&$=I705@!##N!QX]"(``,]Q@``(DE*%9!T$$$@5 +M@!##N!QX]"$#`%JESW&``%"2]"$``(X=Q!!;I9`=Q!"2'000E!T$$#X/0`'/ +M<(``^!X#@`B`Z[@&\DX5@!"`X-P(P@58%8`0@.`%\K((#_\#\"H-```(=?4' +M[_*I<.!XSW&@`,0G%1$#A@38$QD8@!O8%AD8@`/:SW"@`-0+4:#@>.!XX'C@ +M>.!XX'C@>.!XX'C@>.!XX'C@>.!XX'C@>%&@Y+OA(,('%MA2$0"&X+CA(,$' +MRB#A!5$@0(`$\E$CP(`$\N!_$M@!V<]P@`!4'<]R@`!0J[02`P`@J`:#`>`& +MHQ^"[K@'\L]P@`!P!B"@!_#ON`7RSW"``'0&(*`5V.!^X'C!V!0:`C#/<8`` +M^!X#@1B(`=O/"PB/!``S@&"#``&8:!`!F$@`!`^`$((`/``#\ +M_YVXG[CL"AN`!V,(@`0`8(0$`['`@H.!^X'CQP((. +MS_+/<(``4*LR@%$A0((2\L]Q@`#X'B.!2!""`#2!1'E1(8"`2-K*(H$/``"0 +M``/P#MH`W\]QH`"H(">!K!`-`%EAL7'")440RB7F$K!XB@OO^@K9SW"``'PT +M`)#/=J``Q"=1(`"!!?*,)0.2`_<`W1OPSW"@`+0/_*#/<*L`H/_ZH+8([_T` +MV!D6`):`X`7R`M@0'AB0SW&``+@\&X%JO;A@%-T;H1D6`):`X`?T42$`QN@/ +M(07*(&$`+0;O\JEPX<7/<8``**Q!B<#;%!K",,]S@`#X'F.#$FI'X`0@@`\` +M`/S_:8,JN\"[%[O'.QS`*,%R.QS`*-*),!S`-NH(,`!\"'-`.QP +MH*`!XX#B`-G,]\]P@``HJ_`@0P#L<&"@`>%0<;CW`-G/<*``U`LMH`O,`>`0 +M>`0@@`\``/^_C[@+&APPSW&@`(@D'J'@?\'%X'CQP"8-S_+/=X``4*M`EPAV +M2'&&(?P#0BD%`40B"`.,%P$10BB($$#8SW6@`-`/"B-`@!`=&)#*(V(`K!<` +M$$`KA@7/6$P>68?1!`%]`Z74R#`@!#RSW"``/@>`X`)@%$@`(`]V,`HX@7* +M(*$'P"@A!@KP0"@`$:!PSW*``#@>"&(7N`/A!"&!#P``_/\%((`!)7B=N)^X +M#!T8D`O,`>`0>`0@@`\``/^_C[@+&APP#AV8DR`5`);/<(``^!X#@`B`Z[@/ +M\N2^#?2R#:`%R7#/<(``9*R@V<3:/=MJ"*`!%[N!!,_RX'CQP.'%SW"``(`' +M`)#/<8``^)FHV@'=@"!$"Q!X2@WO^JES@.#*(<$/RB+!!\H@@0\``+44RB.! +M#P``S`#*)"$`;`$A],HE`0'V"$``SW"``$`N.03O\K2@\<``V<]PH`"T#SR@ +M5@^/]@8(#_[2"T_[6@ZO_0#8_]G/<*L`H/\YH#B@T<#@?N!X\<#AQ<]Q@`#X +M'O`A`@!*)$``PQ(!!@]X,B*"#P``'P,$(8,/``8``(#C`=O`>P0AC0]````` +MUW5`````PB0"`=X-;_O`N;T#S_+@>/'`0@O/\HX(H`((=<]Q@`!0JQ^!L+@? +MH<]PGP"X_U@8P`C/`':`-G/<*``M`]3^@@.70`P(``,#IN-KRSW6` +M`-0J`(6*(0@`Y)#/=J``Q"<3'EB0SW&``%"K/X$Z=_&YAB'\(T/R02D!(<.Y +MSW*``-Q4-GH@@D!Y"'49%@"6@.`%\@+8$!X8D,]P``#_?Q,>&)`;V!8>&)`# +MV<]PH`#4"S&@X'C@>.!XX'C@>.!XX'C@>.!XX'C@>.!XX'C@>.!XX'@QH,]P +M@`!8?QF`@.`H#T(!FN4P`P(`SW"```C.%@G@`P#=(0,``+X+;_\J(Y\PGXI7*)<$3+?),(`"@"_+^"B``@<`*)0"0'/0:#.__ +M`<`8\`/9SW"@`-0+,:#@>.!XX'C@>.!XX'C@>.!XX'C@>.!XX'C@>.!XX'C@ +M>#&@`-V:YP?TSW"```C.F@C``X#EH`("`/X-P`'/<(``4*L?@.ZX"/(!V<]P +M@`!P!B"@"?#ON`?R`=G/<(``=`8@H!$6`)8`W5$@@(!!P!CTN@[O^8'`"B4` +MD!+T!!0%,%$E@(`,]`HAP`_K<@K8C+B*(\<*,0;O\XHD@P^`Y3@"`@`$V!,> +M&)`;V!8>&)#/<(``6'\9@(#@$`Y"`1D"``#@N,;RSW:``%"K$H:&(#H`C"`$ +M@F`-Q0'/<:``#"0\@1>&(GADN!!XBAX$$$0B`%.(X`OT'X;QN`7T425`T0'8 +M!?0`V`/PX@I/_X#@G!X`$##R5@T/_PHE`)#>]`W,42#`@1'R'X91(("!#?(O +M((<*C"`"A@?TSW&``%"K'X&8N!^AG@D@`('`"B4`D,3TSW:``%"K'X;PN!7R +MJ!8!$-38X@\@``>H0'?SW"` +M`%0=M!8!$."H!H$!X`:A'X;SN!0)0OH/AH#@(`A"^A^&[K@(\@'9SW"``'`& +M(*`)\.^X!_(!V<]P@`!T!B"@`-C/<:``R!P'H3#8"J$R"N__`<`?AOBX$_(0 +MV`P:'##/<(``",[:#H`##<@`(($/@`"(JA^&X*FXN!^F`):&(/P`C"`"@!OT +MC@K/^8#@%_0#V<]PH`#4"S&@X'C@>.!XX'C@>.!XX'C@>.!XX'C@>.!XX'C@ +M>.!XX'@QH`"69@IO_#261O!1(,"`0<`5WP/TZ74@\`C8SW:@`,0G$QX8D$8( +MS_^4X`AU%O("V#P>`)`A%@&6SW"``.R1(:`1%@"642"`@.?UG@SO^8'`"B4` +MD.'SE>4>],]PH`"0(QZ`!!0$,%$@@(#*(<$/RB+!!\H@80+/("$#RB.!#P`` +MY`0`!.'SRB4A`-H.K_^(<`AUJ7"M!J_RHL#QP%(.C_*AP0AV`-A`P`"F/@SO +M^8MP"B4`D)7TSW"@``0E(H``A@0A@0__`%__!2$"`$"F4R&"`%,@@P!E>H?B +M8/11(H#3SW"``%"K'X!"\OJX:?0$(+Z/`!X```CT`(8/\,]P``"C"<(-S_E1 +M(H#`^?51(@#``(8#\H6X`*;/%$@@,0`IC`/X@3*("((J7!Y!:_RH<#@>/'` +MX<4`W078"[A2"J_ZJ7&R#8_SSW"``%"K'X#KN`WTA.7:``8`42!`Q6GT42`` +MQ57T`>54\`#9G+G/<*``T!LPH`'9SW"D`)A`/*`%V`CP4@D``/X.X`0%V`'8 +M]@[`!(3E;@`&``0@OL\P`````>7*)2(042,`P!KT42!`Q07R42&`PR7R42`` +MQQOT42#`Q>7S42&`P^/SSW"J```$`8"&(#\+@^``W=3U$_#V"```SW&``#P\ +M#H$!X`ZA"?``V9RYSW"@`-`;,*#:"````-G/<*0`F$`\H!'P`-U1(P#`!?1R +M#N`$`=B<\;H(``#/<8``/#P.@0'@#J&!!(_RX'CAQ0S,1"`^BDKRX[@J\H#8 +M#!H<,`W,SW&``,`[SW*@_I0!SW.?`+C_Z[@`W0;R'8$!X!VA2'`'\!6!`>`5 +MH4`B``T6HPW,42#`@`;TSW"@`"P@KZ`-S(8@@@(-&APP(/!1($"!'/**(`0` +M#!H<,,]Q@`#`.Q2!`>`4H0W,`-E&((`"#1H<,,]PH``L("^@SW&@_O0!SW"? +M`+C_-J#@?\'%\/'`+@N/\L]QH`#\1`6!`-Z\N`6ASW6@`-`/U:4# +MW]X-K_+I`SW"``$0E!H`0<03T +MN@_/_P3PG@_/_]'`X'[AQ>'&`-D'V`#:M&FT?<=U@`#XF55]P)6,)@*=`-N% +M]HPFA9+#]O_>P+7!G;SF!?:,)C^10O9AM0'B3WJ,XJ?V8;B`X`'A+WD@]\'& +MX'_!Q?'`B@J/\@#=SW"``#"1_@RO^;2H@.`3\@C>@.7,):*0S"4BD`#92B0`*HT>`'A8+#/=8``^![/=H``=)%`)0`7)&ZN +M#^`!!MI`)0`50":!$IX/X`$&VD`E`!=`)@$4D@_@`0;:+!6`$(3@"?0\%8`0 +M]@AO_BV%/@P/^0Z%42!`@5@(0@/J#(_Y@.#$#L+_SW$``/__SW"``$S++*#Z +M#J_Y*Z`5`H_RX'CQP*()K_(4V<]U@`#P(MC<#@\O]`(E`!//<(``/"4"#R_T +M%-G/=H``^!Y`)@`5`*;`W`(E`!,!I@#=J7"I<5(/[_(&V@'8J7%&#^_R!MH` +MADHD@'"I<0*F`Z:H(,`%%29"$&""BB#�VS`((!X:F@`(*IH`""P!A8`P"" +MP1A8`P""PAA8`QO9SW"``!`Z@0&O\BRH\<#Z"(_R&G`Z<5IRU@FO^7IS"'<$ +M(H,O``8``(#C`=O`>P0BC2]`````UW5`````2B1``,(D`@$`WL]UH`"T#]RE +M@.?/<(``D`8$B`GT+R$'!"\B1P1*"R_[`]@#V'(++_MJ<8#G!?3*"$`#`_#^ +M"$`#SW"``$0E!(!1(("`$?+/<(``5#@`@(#@"_0YV/8)K_F+N('@!?02#<__ +M"O``V9ZYSW"@`/Q$(:#@>,&@W*6A`(_R\M=Y.& +M(_X'1;MHK8#B$_+/@*MX8OCK>*+Y*UCBV6M(XHZ#^`` +M)JV+<*EQ>@BO\@S:`,`!P3H+K_,"PHMPJ7%F"*_R#-H`P`'!C@JO\P+"SW&` +M`#0'`*&B"Z`$R7#I!V_RH\#@>/'`?@]O\E?8SW6``/@>(X7/"[ +M`*(#\E_8`*+BNP/RA;@`HE$C0(`$\H>X`***)@L6RV'980#:@./*(($`SW*E +M`.@/!J(`B<]QH`"D,(#@`8'/(.(`T"#A``&AP@I/]@.%SW&@`,@<3X`"X$BA +M4@V@``(V@N`"M@!6`$*"X@!T"$$`5@!"@N$`=`A`0C:"X$*V0%8`0 +MH+B0'0(04!6`$*"X4!T"$`'BWO&0XD0`!@``(HT/@``X'@"-@+@`K8`5@!"` +MN(`=`A!`%8`0@+A`'0(0$(V`N!"MD!6`$("XD!T"$%`5@!"`N%`=`A`!XM_Q +MYKD1\L]R@``X'@B*@+@(JH@2@`"`N(@:`@!($H``@+@1\(#C$?3/(!."`*"Z(!N"`*`3@@"@NJ`;@@!@$X(` +MH+I@&X(``>#@?\'%\<".#4_R@>#/=H``^!X:<`/T`(8"\`&&Q!``!A4F#11, +M(`"@`=\EN%,@!0`@AE!$_R\<`>#&_R`-D*)`"@ +MH<'*(6$`%?+/<(``1"40$`4`426`@`WT"B'`#^MR?MB-N(HC"`VM`:_S"B0` +M!<]U@`#X'A4E#A4`AA4E4A`D$!4``!(!("`0%@`H$!#,(.*![O7/<(``F$H`@`39O=H>VT#`BW#Z#N``&+L!V'IP`(8( +M@%$@`(`'\BP5@!"$X`38`O0&V#IP`(8H@!20!"&/CP`&```%?P?R]KG")Z(0 +MP">A$"IPBG'R""_UZ7*`X,H@0@3@""+URB'"`TPC`*`%\BH(3_>E\-(,3_D$ +MV<]P@`!$)?8,+_TDH""&R!$`!H8@?XY$]%8(;_R*<-H/8`2*<``2`"#($``& +MAB!_CD'TSW"``$`B+)`>E1!Q!O+2"8_XDG`W](IP"G%*"J_T`=I_V1&YSW"@ +M`+`?-*">#D_Y/@T``8#@"/0+R`4@@`\````\"QH8,'(-``&`X`GT"\@%((`/ +M````U`L:&#`+R)"X"QH8,/H-3_(-\"090`4@AB`9@`4@AB@9Q`4@AAH9!`8: +M#$_Y@.`*\DPD`*``V`3TV@K`!`3PL@C`!`'=T@P@`JEPSW"``"`=(@P@`J"H +M3"1`H!3TSW"``$`B"(B)X,P@XH$$]$P@`*`(](C@"/1N#$_Y@.`$\@H*#_1J +M"0_Y#@\/]P3*D.#,((*/``"S``[R"B'`#^MR`1($-I+8C;B*(XT&K09O\PHE +M``7Z"^`!`-@E`6_RH<#QP/X(3_(`W<]V@`#X'@C?`(;$$`$&02E``5$@`(`. +M\H#ES"6BD,PE(I',)6*1!O2`Y``"0BSW6``)210"<`%21M-@Z@`0;:0"<` +M$T`E@1(J#J`!!MI`)P`50"4!%!H.H`$&VLX6`!;/<:``Q"?/"('1@0 +M!/($V`P:'##6"N_^`-[/<*``_$0E@+RY):!K%0`6C"`"C5`'QO_/<(``.!T` +M@.NX!?+/<)\`N/_=H$H@0"`,S.2X(?3FN$/TAB#_A`=I4H@`"">\`W,4R!` +M@!7R!,@#$@$V`QH8,!H/(`,$&E@PSW"@`/Q$)8#/=:``R!^\N26@`-X&\,]U +MH`#('P#>&_#/<(``A`8`@(#@%?)R\.8.(`,`WL]PH`#\1"6`O+DEH,]P@`"$ +M!@"`@.#/=:``R!]@]`;(!""^CP.`Z%-N]5$@0,5L]2H/#_I,(0"@&_+_A:`5 +M`!`))P`0Y.#-]L]P@`"LJ0"`42!`@`7RWJ6"#Z``$-CDY\?W0!4!%C!Y1@LO +M^A#8BB`(`*`=@!,.I1^%@.`$](H@!``.I28*0`$OV)6X$AT8D,]P`0#`_!4= +M&)#N"P`#SW&``.0V`(&'X-GRSW"@`#@N!8`$((`/P````-=PP````,CR]=@% +MN,]RGP"X_QJB!]@;HFG8&+@9H@'8O?"F"D_]G_$`WHCP%84!X!6ESW"``!`Z +M$8A1(`"`E`@B`\H@8@"`YP7R'(4!X!RE#,SGN`#>6O(-S`0@A`\````8#"2` +MCP````@S\FH,#_<-S%$@P(`_\L]QH``L(`6!)H$*X#!P5@`-``,2`38"V`P: +M'#!0V+H(+_Z8$0$`A@T``\]PH`#\1"6`SW6@`,@?O+DEH$#QBB`$``P:'#`4 +MA8#G`>`4I<7S&X4!X!NEP?$&#F_X"G!1(`"`"/3/=:``R!\$V`8:&#`_\0C8 +MF[@&&A@P)/`#R*`0``#PN,EP,_).#X_W`-B6N"_PZ+@<].FXSW&@`,@?+?0$ +M(+Z/````4`OR42,`P`?RBB`$``ZA!-@&&A@P#P#*""+"`(C@3Z$+]`/9SW"@`$`M,*!)!.__`!J",P'@/03O_P`:`C#QP`8+ +M#_+/<(``K#@`W^B@SW6``-P'`86&('F/ZZ4(]`/89@HO^0NX@.`.]`;9SW"` +M`!0((*#/<(``&`C@H"D"(```V,]P@``8"`"`@.`<`@(``A6%$`,5AA#/=H`` +M3*U`)I(0J'"$*!\`+W,`(X$/@`!4M!4A@0$;D3J10"81&D`F$!4`)L00!!0$ +M`7IF0X*`X-MC2B-`("#R,'!.]PHAP`_K0<4_W"B'`#^MRSW`` +M`'TGBB-&`THD0`#!!R_SN'.`X@_R4''*(<8/RB"&#P``?B?*(X8/``"/`%@.`.],]PH`"P'V08P`3/<(``6!T(@`"`'!W`%`FESW:` +M`/@>`X8E@RB@J'"$*!\``"&`?X``&+0PX/0@@0$#AC2P`X8F@RF@`X8EDRVP +M`M@(<98/;_(`VHX*0``"C80H'P`O<3`@0B`#C<=Q@``8M##A]"$!`%,B``!V +M"*__`-L"C80H'P`O<3`A0"``(8(/@`!(LB.-`KDT>=X+X`!984H,H`$!V.X) +MP``BC4.-A"D?```A@'^``)BS57AL@%$C`(`,\L]SH`"P'V0;P`3/%5X`"""#X``5+0TXC0B +M00X*N3(B0"XHI<]Q@``4"%$@0(`)\L]P@``HK2`0@`"!X`78`O($V`"ASW"` +M`!@(X*`,A8#@RB`A`&0.H?3*(0$``=CY``_R"B'`#^MRSW```'\GBB,'"$HD +M@``U!B_SN'/@>/'`-@G/]P#8T<#@?O'`8@HO^>'%@.`T\L]Q@``PD12)@>`N +M\C>)@.$)\L]P@`#(FP&`$'$!V,!X%/#/\L]Q@`#(FV&! +MI(JQ@J*EW8?!,)`"`+_0"VL]Q@``4"$"ASW>``!E'((^@IH#A +M`-X)],]QH``L(#"!QW$```!]+:`T$`<`SW```%QX0,`$V$'``=U"Q4/&1,:I +M@J+H+;_<$V,]P@``4"*"@`-@`I@#=`=\9\$PD@(`<],]Q@``91P#?X*E/ +M)8$`(:#/<8``K#@&@>"F`>`&H<]P@``4"*"@Z76`Y1P/`0#I<#T&[_&EP`HA +MP`_K$JH@'9P/$`$`0`"B'` +M#^MRSW```(0G?0(O\XHC3@CQP-8,S_'/=H``W`"Z_S3B+`!Q?P*'2$)`:0%O+*"Z_S3B+`!P&&SW6``+0JAB`&``&F`(6` +MX`7R0'@`V`"EZ03O\0'8SW"``!@(`("`X)GT42$`@,]U@`#X'G[T`HY#CH0H +M'P`O<``@@0^``)BS,.'P(8,``=D"NF9Y-'K'2?]VTHD@`#1`"_SN'/@>/'`+@O/ +M\<]P@``8"`"`@.";](()K_,!W0+8:@HO]ZEQSW"``)`&`(#/=X``^!XF@)X1 +M``:FN)X9&``CATB!-)%3(@``X@EO_ZES*@P@``#>`MC)<N<]PH`#\1"&@X'C!H,]PH`"T#]R@Y@R/_4.' +MSW&``!0("8)1($"!#/+/<(``W`<,@(#@!O08BH/@!]@9\L]P@`#AE0'/\0HAP`_K`0#T'S#(:`X,PG89`'],]Q@`"L.`"!`>``H0;9SW"``!0((*``V`"E +MHO`"CB..A"@?```A@'^``)BS,.#P($``42``@`#?+O((AE8)K_4BAHP@$(!, +M`"D`((6!X;0.0?,#AL]S@`"L.(#@Z*,1\B2&`-H`WP\G3Q`&(,"#+R\!$`.F +M3B>!%P'B]?4DIDBC!=G/<(``%`@@H`38`*4`V&CP((6%X=P`#0`S)D%P@`!L +M2$`G`'(T>`!X`HZ$*!\`,B!`+E$@0(!P#D$#`HXCCH0H'P``(8!_@`"8LS#@ +M\"!``,]R@``4"."X!=D(\H"X`Z8`V`2F(*4\\,]SH`"P'P'8&://+@U@ +M`,"ER7`"\`'8H0>/\<]Q@`"L.`>!`>`'H078C_$*(<`/ZW)/V`>XBB-)"DHD +M@`"Y!._RN'//<*``3"X+@-.XO01@`P;9\<#AQ<]UH``X+D>%SW"``*@X`-E` +MH">E<@E@!"#8!X6*N`>E"\@$((`/____`PL:&#`+R(^X"QH8,`O(D+@+&A@P +M/0>/\>!X\<"J#\__SW"``*@X((#/<*``."XGH$()C_W1P.!^X'CQP)H.C_&^ +M#Z_X`=V`X,]V@`#`5]`*.(XZ$*!\``"&`?X``F+,U>"R`@+DLH,]P@`"L +M.(#9*:``V`NF!MG/<(``%`@@H,]Q@``8"`#8`*''\&*.0XZ$*Q\`+W``((T/ +M@`"8LU5]+(53(02`(O3KAH'G'O1/)`,`4B,#`'M[57N`N<=S@`!4M"RE-.,0 +M8\]Q@`"L.(#=J:$*N`#9"*;/<*``+"`#@"NF`J8$V*(TL.2B2``.$"[_*X!X\@H,]P@`"L +M.*F@`MBI<0H*+_((.!^X'C@?N!X\<#AQ12X)7C/=:``."X&I07P +MD@OO^(H@7`@&A?^X^_-=!(_QSW&@`+PM%7E9@3&!SW.@`,`O,'(*]*`3`881 +M(0"``=C"(`$`!+@&\$`B``0B>,.XX'[@>/'`K@N/\<]QH/[$!@#?SW"?`+C_ +M-J#/=J``P"^E'MB3#]T(O07P)@OO^(H@G`*C%@"6I'B,(!"`]_,4'MB3!O`* +M"^_XBB#B:*(,N)VXG[@E>`:BX'[QP"H+C_$(=G(/[_\H=!XX<4PVP#=SW"@`,@<::`#VL]QH`#,%R$9F(!.H:>@:J#@?\'% +M\<#F"J_Q`-G/<*``#"18@,]U@`!0JZUP02J&!X8@]P^8%8,0*;AV>!SW*@`/`7!J(&@0:B!8$&H@2!!J(`V`JBBA4` +M$6BX$'B*'000`)6&(/^,*?0!V!VB)_!.%8`0@.`C](H5`!%,I62X$'B*'000 +M@^8$V4\=0A`,]"L7`99DN!!XBAT$$`S8+:5/'0(0B@UO^(AP"?`%(T,!01_8 +MD!^%L[@?I0T"C_'@>!#:SW&@`,@<2:$!V\]QH`#P%VJAI!`"`.NZ)O("VEVA +MSW.``(B]1(-&H4.#1J%"@T:A08-&H7`0``$`1J%&@$:A +M!8`&H>!^X<4O@,]SH`#P%\]RH`#\%RBC0!`!`2JR,8`HHT@0`0$JLC.`**-0 +M$`$!*K(\D(8A\P^,(0R`!_0V@"BC7!`!`2JRCNH"GH[N`IZ-R$``!.&`0>`BRSW"@`/0')Z`"V<]PH`#( +M'">@X'_!Q?'`F@\/^/H*3_C1P.!^X'C@?N!X\<"""(_QSW6``%"K%X68X,]V +M@`"8QP;R6!6`$(#@!?(:A5N%`_`L]Q_O__/R1X`:8`W^*F1'DMI@[8K@JO^`ZF@.`(\L]Q@``P(F(/ +MH``!V,]Q@``,'U8/H```V!>%E.`&]`'8`:XQ'@(0!?#AKC$>PA-9`(_QX'BA +MP?'`V@]/\3IQ"'9(=P(*;_H`W8'@RB!"(POTSW"```P=`)"!X`'8P'A`*!`# +MR7"&(/P`C"`"A2/TSW"``%"KF!"``.>XRB`B``KT`K@6>,]Q@``0OP!A+;C` +MN,]Q@`!8!R"!42&`@,]Q@`#(JQ1Y!/(@VJV1"O"8VJN1!O#/<(``C*NSD`[: +M`9=`)0$5$'%&]J)X2"```!!X`_``V%IP`-@J<:ESW@L@!)AP"B$`H`3TX@K` +M`SIP3"$`H`#82/0%((`C#7$`L0UQ`!F$!".'#7`@H"B7#7`@L(PF`I45\HPF +M`Y$=\HPF`Y4D\@HAP`_K`/H=8)(`#I`.H0KPSW"``%"KM!`!``V! +M`>`-H<]QH`#T!P#8!*$!V!IPSW&@`,@?^!$"`$)U`B6`$$@@``!?@1!X4'`\ +M``4`0X?/<(``[)%,(0"@0J"@V`^A`-@?H<]P@`!0JQR08KA"#` +M?0R]SW"``(3*!(#//0B`P#/<*`` +MR!^@$`(`<+M0#O=^$`*&H[I^&)B`\'AP>R8.(`04VOBX!/+)<#CP`]C/ +M<:``]`<%H84E`QD-<*"P#7#`L(HB_P\-<$"@SW(``/__#7!`L`/(SW.``!"_ +MSW*``/@>$(@"N!9X`&,MN,"X\"(``*"`#7"@H`/($(@"N!9X`&,MN,"X\"(` +M`$*0#7!`L,2A2@C``P'8<05O\:'`X'CQP/H,3_$:<,]Q@`!@D@")@.`6\L&! +MHH'/<(``6`<"$1$!X(#/<8``N#P+@32_`>`+H3;P:@RO^(H@"PC/<:``Q"<1 +M$0"&42"`@0#?]/-D$0*&9!G8@P+8$QD8@(#B+RB!`$X@@0<3\L]P@`#`A39X +MP("A@,]P@`!`AO0@40#/<(``8(;P($\`"O#/<8``N#P*@>EUZ78Z=P'@"J$$ +M$`$@#7`@H`@0`2$-<""PSW&```BL`(&`X`?R0H$-<$"@`-@`H<]P@`#X'@.` +M"(#KN,H@@@/*(4(#RB+"`Q@/(@3*(T($4R'`(,]Q@`!8!R"!%+]1(8"`#+CE +M>`GR@K@-<0"A#7#`H`UPH*`?\`UQ`*%*)`!TX'BH(,`"1":!$`^Y4R8`$"5X +M#7$`H2*^2B0`=.!XJ"#``D0E@1`/N5,E`!`E>`UQ`*$BO14$3_'@>/'`N@M/ +M\0AV*'4H<$AQ8@@@`&AR@>#*(($#$`@A`,HA00,%!$_QX'@BN0;P[')@H@3@ +M8;F!X6"`.O?/<*``U`MMH`/9SW"@`$0=-:#@?N!X02F!@`KR+R1)<.!XJ""` +M`000`@3L<4"AX'[@>/'`3@M/\:'!"'5(=L]PH`"L+QF`!""`#W````#7<"`` +M```!V,!X+R8'\`#:RB"!`"WR"\P`'$0P3R#!`P(<1#`!X!!X!""`#P``_[^/ +MN`L:'##/<*``U`LX@$(A`0B`XQPH*``P>QP(*`!V#$#;_&AP/'`X<7:#"_Z`-V!X,H@0@,)],]P@``, +M'0"0@>`!V,!X#+B%(`,!`]K/<:``]`=%H0UR`+(#R`#;79`-<$"P`\A1@`UP +M0*`#R$@0`@$-<$"P9*'A`D_QX'CQP&8*3_'/=8``W'[@%0`0`-Z`X-#W1"X^ +M%P`A0',80=K/W`-B=`F_QX!T`$.!X\<`F"D_Q +M(8`*)@"0$(G#N,HAP0_*(L$'RB"A!LHC@0\``*H`SR`A`SSR@.'*(<$/RB+! +M!\H@X0;*(X$/``"K`,\@(0,N\@*XSW&``!"_%G@`8<]Q@``8!RVXP+@,J2.& +M()&&(?P`C"$"@`STSW&``/@>\"$!`+\1``:!N+\9&``!AJ*`@.4(\@&%@.`$ +M\@"%@.`-]`HAP`_K#0``@.`(\@"% +M@-DHH`&%0'@?\`&&()`4R!!QRB'-#\HBS0<=V,HCC0\``,8`SR`M`\`'S?\2 +M#R_XR7#6"R```87/<(``&`=."*`##(B5`4_QX'C/<(``#$7Y!L__X'CQP!() +M;_$`V@AWSW"``-\'((BCP<]P@`#>!P"(`1S",X0H'P``(8!_@``8M"[@]"!` +M`&#!`QP",`'8`AR",,]VH`#('Q.FSW&``%@=#($`@(3:0L`(@0"`#-D>VT'` +MBW#^#.__&+O/T`;_&CP.!XSW"```R1*(#/<)\`N/\`VC:@"-GL<""@`]G/<*``%`0EH`'( +M['$`H<]PH`#4"TV@X'[@>/'`X<6EP0AR`-O/<*``+""P@$#!!MA!P$+#0\-$ +MPP'8'MF8<[AS`"6''P```'T"">``V'.-`&_QI<#@>/'`X<6DP<]P@`#>!R"( +MSW"``-\'8(B$*1\``"&!?X``&+0PX?0AP0``VL]U@`"L.&#!SW&``&"M,"%` +M#L"X`1P",`F%2:4"'`(P"(5(I0'9`QP",,]PH`"P'SF@SW&``%@=#($`@(/: +M0L`(@0"`'MM!P,]P@`#(.CV`"8`X8$/`BW`0V=8+[_\8NP`4A#`+$@$WSW.` +M`!0(7I6#V.X/8`-@@^4'+_&DP/'`I<'/<(``W@<@B,]P@`#?!V"(A"D?```A +M@7^``!BT,.'T(<$``-K/DH+85@]@`V"#I<#1P.!^",B'N`@:&#`) +MR)NX"1H8,`K("AH8,`O(A[@+&A@P#,@,&A@PX'[@>,]Q@`#,]Q@`#,]Q@`"8/N!_`*'@>,]R@`#P.15ZX'\@HO'`F'`*(<`/ +MZW(*)<`'SW```*(9S0-O\E;;X'C/N!_(*+QP)AP"B'`#^MR"B7` +M!\]P``"C&:4#;_)>V^!XSW*```0Z%7K@?R"B\<"8<`HAP`_K<@HEP`?/<``` +MI!E]`V_R9MO@>/'`I!`!`/FY!/2R"@_X!_`@V<]PH`#('"F@`]G/<*``$!0E +MH-'`X'[AQ0.X-7C/<8``4$<"84HD`'0`V:@@P`(6(D``H8!@@"G8$K@!X75X +MH*#@?\'%X'C/<(``D`8`@*'!)H">$0`&AKB>&1@`X'^AP.!XX'[@>,]Q@``8 +M!^!_!*'@>/'`X<7/D!!Q#O3/<(``V"H`@`0@OH\``#@0!O36"P_X +M@.`$]`'8`_``V-'`X'[@>/'`%@P/\<]U@`!@!\R-#8W"OL*X%G[/?J((K_P- +MV`:X@;@0OL5XSW&@`.PG!J$$A<]QI0#H#P:A!84'H44$#_'QP-(+#_'/=J4` +MZ`\FAJ>&SW"``&`'`-\DH*6@7@BO_`W8!KB!N,]QH`#L)P:AYJ9%))$'(5],]P@`"^!@"008D0<@WTSW"``,`& +M`(@FB1!Q!_3/<(``G"H`@`+P`-C@?N!XX<6?X>'&`-T8\I[A`_:`X4/V`-@4 +M\)_A']Y*]DXA_`?@>*@@@`$/)8T38;X1($"``_*E>`+PIG@`H@'8P<;@?\'% +MX'CQP!(++_$HV"8)S_C/=8``O"I`A0AV!""$#P``\/\`V&!Z02P!`4"%`=A$ +M)@038'I!+($`0(4"V&!Z4R9!$/(([_@`V$"%"'=!*`$"`]A@>L"Y0(5!+T$2 +M!-A@>L"YSW$``"S)SW"``+@J(*#/<`$`Z-H)`R_Q`*7QP+H,#_Q6"`_\SW$` +M`%3)SW"``,`J:@RO^R"@SW$!`!#;SW"``,0J(*#1P.!^X'CQP&X*+_%0V'X( +MS_C/=8``S"I`A0AV`-A@>E,F01!`A0'8R7%@>H8A_0_/<0``>,G/<(``R"H@ +MH,]P`0`XVYT"+_$`I?'`X<6R"._Y!]@F#>_["'6N#\__8@X/_)X-K_FI<($" +M#_'@>/'``@H/\<]P@`!<.`2(SW:``!`Z@.#/=X``^!X&]`.'#9`_EA!Q(?+^ +M#*_S%=C/<(``7#@`W:2H`X>QKE8*(``-D,]P@`"X*B"`8'D$V(#@"O+/<(`` +M5#@`@(#@[`VA^\H@H0"_M@4"#_'@>/'`E@DO\9APSW&``!`Z;8D`W4`A`@I* +M),!PX'BH($`#$2-`@P?TSW#_`/__%2),`P"D`>6O?6N!JH%P=0R!U?80=<_V +M$',"V\H@*0#*)6D0RB-L`,H@+`#*):P0%/`!VP+8`-T0\!!SR_80=0#=RB.I +M`,H@:0`(]@'8`MT#\`+8`=T`V_`BSP#P(D4#\"(```(ES@/-H0(@0`$.H0#8 +M#R#``#P9`@`/($`#/1D"`%$!+_$`',(`X'CQP.'%SW&``!`Z#XG/=8``$#H, +MJ9(,X`$"V%$@`,,=]!(.@``,C88@_P%#N!"X3R#!!L]P@`#X'@.`,B"`#P`` +MV`*?N8#@`=C`>`^X)7C/<:``_$0-H1&-A+C]`"_Q$:WQP'H(#_$(=\]P@`#X +M'@.`SW:``%PX"8`EN%,@$``'CH'@T2#L__4?`2 +MC8#@`-DS]`'8!*ZL'4`0,:T5K1:M"M@7K078&*U0VEFM`-J.NDBE2:5'I0/: +M0!V"$`3:01V"$$(=@A!#'0(01!T"$$4=`A`&V$8=`A!''0(02!T"$$D=`A`( +MV$H=`A`,V$L=`A`RV+0=`!#^"2``L!U"$`2.@.`5\@3*D.`1]$P@`*`/\L]R +M@``0.@V*,V@E>`^J#JJT$@``.@T@``.NV0?/\.!X\`AS#:T$((XO````#$J^N';3K00@CR\````P3+_TK00@CB\` +M``!`3KZQ'8(34R"^@,HAP0_*(($/``"1&LHC@0\``((!RB+!!R'R3"0`@"[R +M!"-``!!QRB'"#\H@@@\``)(:RB."#P``C`'*(L('#?0$(8``$'(/\@HAP`_K +M`ZM@.',)B*1!?(3:25X +M#ZT3:V5X$*T.C0RMB@G@`0#8'0;O\#X=!!3@>/'`X<7/<(``6!T$@`+:`=L@ +M@,]P@`!<.$2H`-C/"$'%,]X&[?@L@`'&JSW&``#P\&8$!X!FA!/`V"P``P07/ +M\/'`1@W/\*'!`-Z."R``8,:B"^__BW#/<8``$#JP$8,`@.-`(0`*"_(@PKF) +M\""/`$&!!2I^`S=WP_:SB0;P`=VSJ;`9@@/)@^I +M$(D%(D(!17B&(/\!#1&$`$.X"R0`@,HBP0?*(($/``":&LHC@0\``&8#?`$A +M\LHAP0\&(#Z!RB+"!\H@@@\``)L:RB."#P``9P-<`2+RRB'"#R$$[_"AP.!X +M\<#AQ<]U@``0.A6-(840<4;W%HTBA1!Q1@`%`"V%SW"``%`Z*6#/<(``7#A" +M#N__(ZC/<(``N"H@@&!Y!-B`X+@/8OO*(*(``-@-I0ZE`*4!I0*E&@D@`*P= +M`!`9\#>-0(41C3!RH;@1K87W8@D```_PSW&``%@=)($@@4>%U;E0<47W@;@1 +MK?+Q$@D``)T#S_#@>/'`X<7/=8``$#H1C5$@`($)\@Z-#*W2#J`!`=@1C:2X +M$:UU`\_PX'CQP/8*S_#/=H``$#H1CE$@`(!-\L]R@`!0JS^"YKD+]`"2AB#\ +M`(P@`H!!]%$A`((]\@"&`>``IA".AB#_`9H2C0!#N+%P,_0`V:P6!1!*),!P +M5A($`:@@P`7/<(``H*LT>&"($25`D$`D#PM`+8``%'@U>-A@!?+@X\(GQ1#S +MH`'A0"5``,*XK!X`$`&&`>`!I@"2AB#\`(P@`H`$]`*&`>`"IIH.;_,5V+4" +MS_#@>/'``]G/<(``7#@DJ`#8SW&``!`Z$:D.B4R)$'(%\@RI[@V@`0'8T<#@ +M?N!X\<#/<(``7#@"V22HSW&``!`Z$8F`N*.X#WBAN!&I#HE,B1!R!?(,J;H- +MH`$!V-'`X'[@>/'``MG/<(``7#@DJ,]Q@``0.A&)12!``A&I$(E,B1!R!?(, +MJ8H-H`$!V-'`X'[@>.!^X'CQP+()S_#/8M2&6&`5@-MC+WF0<1UEK**Q]VNBZJ*I`<_P +MX'CQP.'%SW&``/@>(X$MD0';SW6``%PX4R$"`('B1XW`>P>MSW6``!`Z#B(" +M@,HB8@"`X\PB88`&\K(5@A"!XA[T@>``VK(=@A`5]/(.C_\,C2Z-$'$$]$`E +M@A,$\$`EPA,4C0G9@N`NK4+V+ZT`B@RM!/!.".__*'`Y`<_PX'[@>/'`N@CO +M\`#;H<$$N,]R@`"8QQ1X'6*0WAIB`8(8OLBEBB8$$L]R_O__/\FE!'J`X4#" +MRB7!``OR"($$((`/````,$(@!8#*)6(`3"4`@";RSW"``/"I`!`$`,B!!"2& +M#P`'```$)HX?````,"R^8;Y!+@8&0":`$Q$F@(,/(@(`0,)"]`HAP`_K@/P`8,%>D#"SW"``/"I(("+O\*'`X'@*)0"`\<`3\DPE@(`3\DPEP(`4 +M\@HAP`_K!X +M\<#"#H_P"'7/<(``[#0)@(#@"O3/<(``?#0I@`S@\"!.``/P`=Z*(/\/`*7/ +M<8``^!X`@<00``91($"!,_3>"4_Z`*4"#:_XJ7"`X,HEXA&D])X)S_>`X`7T +M`(6,(/^/#/3/<(``P"H@@&!Y`=B!X`7=RB4B$9+PSW&``'04(9'/ +M#T__C06O\*EPX'CQP"(-K_"X<8#@RB'!#\HBP0?*(($/``!+`\HCX0[*)"$` +MH`+A\'2O?/<(``!(,A@`4IO@`G=0`@4"!,)0"` +M$?),)4"`,O),)8"`3O(*(<`/ZW**((T.D-LM`N_QBB2##PX/C_>`X!'RSW"` +M`$`B+)#/<(``^!X>D!!Q!_(")8$?````#.EP!/#I<$(E`16."@`!SW"``,1" +M`"6!'P``B!-^"@`!3?#&#H_W@.#/<8``&$,1\L]P@`!`(DR0SW"``/@>'I`0 +M<@?R*'`")8$?````#`3P*'!")8$70@H``<]P@``T0]OQA@Z/]X#@SW&``.!" +M$?+/<(``0"),D,]P@`#X'AZ0$'('\BAP`B6!'P````P$\"AP0B4!%0(*``'/ +M<(``_$(`)8$?``"($_()``$*<,FXSW&```2#`Z$&AH&X"02O\`:F\<#AQ<]U +M@`",0EH)(`&I<,]P@`#(-""`SW"``,PT`(`)(0$`SW"``.2H"(`)(0(`SW"@ +M`"P@,("I<)X)(`%98=D#C_#QP,]P@``$@P&`@>`%],]P@`!\-`>`SW&``.!" +M((%"(0&`RB%B`(#A$/3/<8``5*DL@8#A"O3/<8``/*F6"N_V(X'Z#>__`MG1 +MP.!^\<`6"X_PSW"``%@=!("@@,]P@`!40@"`0B``@,H@8@"`X#:]!O3/<(`` +M<$*F"``!SW"``#RI(8C/<(``J$*`X<]V@``$@PST((!"(0&`RB%B`(#A!/(@ +MAH#A+O1V"``!SW"``,1";@@``0&&&@KO]@AQ(88/>#!PS/<*(<`/ZW**(`T# +MU]M*)```)0#O\;ASSW&``.!"((%"(0&`RB%B`(#A!_(=92.&R;TP=03R0@WO +M_P#9S0*/\(#BX<7AQE;R0"+#`R2[P[H"\`#:C^*6``T`,R:"<(``D$A`)PUS +M5'T@?<"(`1F2`P'@`1""!`$9D@`!$(($`1F2``$0@@0!&9(``1""!`$9D@`! +M$(($`1F2``$0@@0!&9(``1""!`$9D@`!$(($`1F2``$0@@0!&9(``1""!`$9 +MD@`!$(($`1F2``$0@@0!&9(``1""!`$9D@`!$(($`1F2``$0@@0!&9(`0B-# +M@+/UP<;@?\'%X'B`XEA@66$+\B\DB7#@>*@@P`'_$(*"_QF*@.!^@.+AQ>'& +M)/)C:B*[P;H#\`#:@^(9]S,F@G"``(Q(0"<-!X\<`V":_P4R%"`$XB +M#0'/4.\H+E +M"/*#Y0[TSW"@`#@$:*C/<*``.`1HJ,]PH``X!&BH'0&/\/'`B@B/\+IP>G'Z +M(?PM@\/_X7PSW:``$!%`88`V:H-+_(XV@"& +M'-D@H`&&&-D@L,]Q@`#X'A4A5@,`%@$@4X$-P?"HSW>``&P(*!A`!$5YI+DA +MH`#9,QA"`.EQ(J`*(4"#,1C"!#(8P@0T&,0%RB%B`.(.K_8,X(#E!O3/<8`` +M=)$%\,]Q@`"4D2.FSW```$@1`+%,)4"@&-@"I@3RBB`%`@"Q#,"`X`3TSW`! +M`+#8`:<`%@`@N1``!E$@`(`7\D&&&M@`L@*F`)&'N`"Q`-@+L0&"3"0`H*VX +M`:('\L]P@`"`-@2`,QH"`$PB`*`4\B&&`8&8N`&A`X&?N`.ASW&``$@'`!8` +M(``9!`1`@`&`0:$"H0H-+__)<$T'3_#@>/'`_@Y/\+IP>G'Z<@HB`"$*(4`A +MR'4*),`A"B!`@\]R@``>O\H@8@`(<0*X%G@(8DPC`*`$N(8@_@,%(%``RB', +M#\HBS`?*((P/``"_(&9".N(^X`;%,(@"@#,`!IQ/R(88!@9BX`:$#@9^X`Z'/<8``2`<` +M%@`@`!E$!$"``8!!H0*AH@LO_\EPX05/\/'`X<7/=8``V#T`C8P@PX\/],]R +M@``X0@:"`X`@@,=Q#P``H,8+X`!(/'`SW"``/@>`X`8B(7@#_3/<`$`H(;* +M#T`"*@N/]0AQSW"``(A#C@K``-'`X'[@>/'`1@Q/\`AW&G$Z/'` +MSW*``,0&`H(EB(#A`=@&\@C9S@[O]RNB"/#/<8``<"52#2_Q`*'1P.!^\<`" +M"V_PV'`^""```-W):(#FEO;X<*EW,B:``[#@BO:YX`CV@@_/\S)O.'@%?0'G +M0B='`$PG`(!AOC#W+0-O\*EPX'@(<@/P`>`@B(#A_O7@?T)X\<"R"D_PSW6@ +M`/Q$'84YA68-H`(`W@#8GK@!I>!XP:7%I?T"3_#@>,]QJJJ[N\]PGP"X_S:@ +M-J`VH#:@SW&@`,@[#H&(N`ZA:2!``/[QX'CQP,]P@`#X'@.`&(B$X`[T"B'` +M#^MRBB`,#HHCA0I*)```V0=O\;AS6@WO\@/8SW"``.2H`!`$`$PD`(`+]`HA +MP`_K2"2__`=C1P.!^X'CQP,H)3_#/=8`` +M5*DOA4H@`""`XD5[;AG8`"*%`*$*\&X1``9$>&X9&``< +MV!BX%1D8@*4!3_"`X`'9P'G/<(``U#W@?R"@\<`B"4_PSW"``+@J(("BP6!Y +M!-B`X*H"`0#/<8``4!T`@0'@@>``H0GT`=G/<*``R!PQH#X+H`(H<'H/[_@% +MV,]V@`"X-PZFSW&``%`=`($!X('@`*$*]`'9SW"@`,@<,:`2"Z`"*'`#V,(- +M+_')<038N@TO\2)N!=BR#2_Q)&X+V*H-+_$F;@_8H@TO\4`F`1(VV)8-+_%` +M)H$2-]B.#2_Q0"8!$SC8@@TO\4`F@1//=Z<`%$@(A\]QIP"81P2F#8?/F%X<(IA:'":88@@NF&8(,IAJ"#:;/<`4`Q@,& +MI<;8D+@&I<]P+``"`0:ESW!:`$(!!J6*((L`!J7/<$``APT&I<]PT0#"#0:E +MSW#```<.!J7/<(``4!T`@,]R@`!0'4(@0(``H@;TSW*@`,@<`-@1H@'8"*<` +MV`VG#J?/<%``_P`#/<8``4!T`H0CTSW&@`,@<`=@1H78)@`(! +MEA"XA2"$``:E`I80N(4@A0`&I0.6$+B%((L`!J4$EA"XA2"/``:E!980N`4@ +M@`\``((-!J4&EA"X!2"`#P``P@T&I0>6$+@%((`/```"#@:ESW"``%`=`(#/ +M<8``4!U"($"``*$'],]QH`#('`#8$:$$ABN&"*<%A@VG!H8.IPB&%Z<)AA:G +MSW"K`*#_.*`LACF@+88ZH!H*[_@.AL]P@`!0'<]Q@`!0'0"`0B!`@`"A!_3/ +M<:``R!P`V!&AS08O\*+`\VT#`BW!*"N_^&+NAP-'`X'[@>/'`$@GO\A;8 +M`-C1P.!^X'CQP-H-+_`'V&8,S_C/=J``M`_\AAIP`-@_R%MC1P.!^SW&` +M`/@>`('$$``&42!`@0CT`8'$$``&42!`@0GR#@XO\Q/8"@XO\Q'8Z?'I\?'` +MSW"``)P8`(#/<8``E`<;>$X.[_,@@8#@"?(!V<]P@``@'6H/[_\@J-'`X'[@ +M>/'`J@P/\`AW?=@-N,]Q@`!XQ\6!,@XO\,EQC"`"@,]Q@`"@&`#=A_<=>(P@ +M`H`!Y7SW`"A"`P4JO@//V,]RIP`42!V"OH)L$A``]Q26"'P#_``#3)>$5J@VO]8HA$``(=JEPG@VO]8HA +M$``(=4`H`"*.#:_UBB$(``AW0"H`(H(-K_6*(0@`T7D9X2QY+W&Q>AGB3'HO +MXQ2""#P#_``#3(.$% +MA0,O\`"EX'CQP`H+#_#/,<&@48@0`$&H<]P@`#(!R"`@N$*](H@1@^B#V_Y!-K^"*_Y!-C1P.!^\<#/ +M<8``[#0)@0'@":'/<8``>,<&@8*X!J'/<(``7#@#B(#@K`UA_LH@H0#Z#:_R +M!MC1P.!^X'C_V<]P@``@C""H;R!#`-D`;_$!V;4"+_,1V.!X\<#/<(``^!X# +M@!@0A`!,)`"!#?0*(<`/ZW**($P/BB-'"@4'+_%*)0``SW"@`"P@,(#/<(`` +M\#Z&#V``EB%!#\]P@`!4J0R`@.`2\L]P@``,'0"0@>`!V,!X#+C7<````!`& +M]#H*(```V"'PA@S/^\]P@`"8%0"`@.`7],]Q@`!XQP:!1B!``0:ASW"``,@' +M((""X0GTBB"(!*8.;_D$VOX/;_D$V.8/C_G1P.!^X'CQP,]P@``80P"`0B`` +M@,H@8@"`X`?TSW&``.PT"8$!X`FASW&``'C'!H&"N`:A]@RO\@;8SW"``%2I +M#("`X!GRSW"``&0Q`H"`X!/RSW"```P=`)"!X`'8P'@,N-=P````$`?T"@H@ +M``#8T<#@?L]P@`!<.`.(@.`%]%8,;_X"V/7Q]?'@?N!X\0"?(! +MV'X.K_$&I?8,K_8"V'()C_:"X`WR"B'`#^MRBB#?!8HC!P"*),,/C04O\;AS +M+@F/]L]P@`!XQP60@.#4]@J%"-E!P`N%E-H>VT#`BW`:#*_^&+N`Y@;T)@Z/ +M\0'8!Z4`V`NE)0`O\*+`X'CQP.'%"'7/<(``>,<%D(#@Q/;V"X_V!/"N"X_V +M#@@@`*EP!0`/\.!X\<"*#\_O6@@O]P#>@.#/=8``J!T']`'8`:4>#^__R7`Y +M\`O(!""`#_[__P,+&A@P"\B'N`L:&#`+R)"X"QH8,(X+#_`B#`_WA@NO\@O8 +MSW"``%@=$(`DA0"`QW$````4(GC7<`"```!"]\.E?@BO]L*E@.#T"Z'VRB!A +M`,]P@`!XQP60@.#*((D/``!``%`*"?EE!\_OX'CQP,]P@`#X'@.`&(B$X`[T +M"B'`#^MRBB#,#XHCR@5*)```:00O\;ASSW"@`"P@,(#/<(``\#[N#&``EB%! +M#\]P@`#(!R"`@N$)](H@"@A6#&_Y!-JN#6_Y!-@!V<]P@`!D,2*@SW&``'C' +M!H%&($`!Y@GO^P:A>@V/^='`X'[@>/'`SW"``/@>`X`8B(3@#O0*(<`/ZW** +M(`T/BB.*#THD``#I`R_QN'//<8``>,<$V`:ASW"``&0Q`=DBH,]P@`!<.`.( +M@.`<"F'^RB"A`&H*K_(&V&(*K_((V-'`X'[@>,]P@`#P/LD#0`#@>/'`2@JO +M\A/8SW"``%@=!(`@@,]P@`!($R"@T<#@?N!X"'(`V`D#;_80V>!X"'(!V/T" +M;_8@V>!X"'("V/$";_9`V>!X"'$%`V_V`-@(`X`8$(0`3"0`@0WT"B'`#^MRBB",#XHC"0,=`R_Q2B4``,]P +MH``L(#"`SW"``/`^G@M@`)8A00_/<8``>,<&@48@0`$&H<]P@`#(!R"`@N$) +M](H@B0?Z"F_Y!-I2#&_Y!-@Z#(_YT<#@?N!X\<#/<8``>,<&@8*X!J'/<(`` +M7#@#B(#@"`EA_LH@H0!6":_R!MC1P.!^X'CQP$H)K_(4V`H)#_'1P.!^\<`Z +M":_R%-A"""_Y!-C1P.!^X'@E`:_R&-C@>/'`_]G/<(``V#T:#V__(*CN#X__ +MT<#@?O'`X<7/=8``;"4`%040C"7#CR#T@.`1],]P@``HD&@0!``*(<`/ZW+/ +M<```APPM`B_QBB/'`PAQ@B$(`,]P@`!(C`X@0``V#N_OBB$(";APSW"``$24 +M$Q`"AHPBPX__V07R'!A8@2"E"_`3&%B!(*4`V<]P@`#$!B2@*@]/][D$S^_@ +M>`'9SW"``,0&)*`5!T_WX'CQP"X,[^\"V,]U@`#8*LX.;_$!I8+@#?+"#D_Q +M@.`)\KH.3_$5)0$0%($!X!2ASW"``$`B+)#/=H``^!X>EA!Q&/(+R`0@@`__ +M___#"QH8,`O(A[@+&A@P&@@/\`"&Q!``!E$@0($&\@"%Z[@#V`/R!=C."(_Q +M`-@*#^_XC+@=!,_OX'CQP.'%SW"``$`B"(B'X"WTSW"``/@>`8`>V<`0`P:` +MXR\HP0`"(0(`$O*.X@GR$FH6>,]U@``?OPAE@.`*\A`C@X`O*,$``B$"`/'U +M`=@#\`#8@.`9\J8.S_:`X%0)(@#*("(`$?#R"L_VSW*``-@J((*AN8#@KKD@ +MH@?RV@K/]H#@B`P!]ID#S^_@>/'`X<7/<(``U"M(B"J(1"H^"P`A@'^``&0K +M-7@&B,]U@`#8*H'@%/3/<8``^!X`@<@0``:&('^."O0!@<@0``:&('^.K`[A +M_\H@(0``A:JX00/O[P"EX'CQP+H*S^\(=<]W@`#4+'#<`B-&X`(9$/@`#8*EP1`29"A40N/A`.\IH,3_&`X`KRD@Q/\<]Q@`#8*A5Y"X$!X`NASW"``-@J((#/ +M<(``0"),D,]P@`#X'O"Y'I`0\A!R"_0$(;Z/```X$`7T.@S/]H#@NO(Z#*_P +M`=C/<(``0"(LD,]P@`#X'AZ0$'$+],]P@`#8*@"`!""^CP``.!`#\@'?`O`` +MW\]P@`#X0Q(-;_/I<<]P@`!`(@B(B.`1]`#=`MY$+3X7`"&`?X``G"SN#&_S +MZ7%AOH#F`>4T]^8(S_8:<,]P@`!`(BR0SW"``/@>'I`0<0[TSW"``-@J`(`$ +M(+Z/```X$`3T3"``H#_T"\@$((`/____*PL:&#`+R(>X"QH8,#8-S^_/<(`` +M0"((B(?@*_3/<(``^!X!@,`0#P:`YR\HP0-.((X''_*.YA;RLFZV?<=U@``0 +MOP65@.#.]@:542!`@`CRJ@WO\,]XU@A``@78$JT`V`6U$">/DR\HP0-.((X' +MX_7/<(``0"((B(C@)_3/<(``0"(LD,]P@`#X'AZ0$'$*],]P@`#8*@"`!""^ +MCP``.!`5\L]P@`#X'@&`Q!``!E$@0(',("*@"?+/<(``V"H`@%$@`(`'V`+R +M!MA"#4_QB0#/[_'`SW&``-@J_!$"`(/B1``-`#,F@G"``&!(0">``?H0;8"/"`$0```>"`&0```=CN#$_Q +MT<#@?O'`S@^/[PAUSW"``$P3#X"`X!#RSW*?`+C_':+/<8``.!T$@0'@L[BU +MN+BX!*$6HL]P@`!`(@B(A^#/=H``V"K4](#E2O+X%@`0@.#BA13T]A:`$(#@ +M$/8*(<`/ZW+/<```OQN*(P@&2B0```$%[_`*)0`!8;@.">_P^!X`$)H([_#I +M<,]P@`#`!P"`^!8!$"*H^!8`$(#@(/26"<_V@.`*\M(.``+/<(``P`<@@`2) +M@+@$J<]P@`#4*TB(*HA$*CX+`"&`?X``9"LU>`:(@>!(""'RRB#!`P"&\+B0 +M":+PRB!B`)X)3_&!X`WRE@E/\14F`1`"@0'@`J$`AH>X`*;@\(#E!/(`AJ>X +M`*8+R)"X"QH8,`8+[^]*(``@SW.@`)`C'(-1(,"`#_1P$P0`"B'`#^MRSW`` +M`.4;BB-)"3$$[_`*)0`$;@F/^6X-3_[/<:``T!L3@9"X'*'/<(``.!T`@.^X +M!O+/<)\`N/]T&``$SW>``,`'((<"B8#@HO0$B5$@`(`M\L]PH`!(+@N`SW6@ +M`#@NT[@GA01Y11Y8$">%!GDGI;((8`(0V`"&SW$``,0=D+@`I@>%B+@'I88* +MK_<-V`O(!""`#_[__P,+&A@P"\B.N)"X"QH8,$(*S^\@AP2)H+@$J6[P^!8` +M$(#@XH4-]/86@!"`X`GV"B'`#^MR;]@&N(HCB@@P\6&X=@^O\/@>`!#/<(`` +M^!X!@,00``91($"!"/+X%@`0@.#$#>'TRB#!`\]U@`#4+&@5@)"`X"+R:A6` +MD%$@P($>\C8-C_9J%8&0&G``AL:Y"KF!N`"FSW"``,!#H@L@`/EA3"``H`KT +M$@V/]H#@!O(`AHZXS@[O]0"F_@R/]H#@!_)5%@`6`>!5'A@0^!8`$(#@$O0( +M%8"0"A6!D$0H/@L`(8!_@`!D*S5X!HB!X&`.X?'*(,$#<06/[_'`#@V/[PAW +MSW"``$P3#X"`X`#>#_+/"SN+6XN+@$H1:B(H?/ +M<(``V"K/=8``U"M`&%@`"(W/<8``9RM$*#X+,B%$#DJ-0B1!`%!QR/9`(D4` +M+R5'`0H=0A$%\,JM2B4``$0H/@L`(8!_@`!0*Q4@0@%,DH#B#O2P<EP!/2"#0`"!/!: +M"P`"SW"``#@=`(#ON`;RSW"?`+C_W:!U!(_O\<#AQ0#==@XO]ZEPU@EO\:EP +M2@A/\B(*3_'/<(``@!5A!*_OH*#@>/'`_@Y/]HH/3_9N#D_VT<#@?N!XX<7/ +M`ZA$(()`J_T#:'QP'X+K^]*)$``P(&@ +M@`'?T77")`(!T76A@6&`PB?.$P'>L7/`?K%S`=O"(\X`3"0`@,PF(I#*(V(` +M"_2`XP;T@.;,)R*0!/("VP/P`-N`XQ3R@>,.\H+C&O2@@,"!`8`A@0(EC9.@ +MH@,@0``!HA#P`-@`H@&B#/"@@<"`(8$!@`(EC9.@H@,A`0`AHED#K^]H<.!X +M\<#AQ2:`0(!"(@*`RB)B`(#BRB'"#\HBP@?*(((/```V$$"`$'((\F2""R-`@`7T0((0`!`!V!.F*(4,A4"!`(`` +M(L*#0*$LA0$@P```H0+8$Z8IA4V%`(%`@@`@P(,`H0V%`2+"`$"@!-@3IBJ% +M#H5`@0"``"+"@T"A+H4!(,```*$(V!.F*X4/A4"!`(``(L*#0*$OA0$@P``` +MH02%`(`B""_RZ7$DA0"A!84`@!8(+_+I<26%`*$&A0"`!@@O\NEQ)H4`H0>% +M`(#Z#^_QZ7$GA0"A#]B:N`ZF#]@,N!"FSW"``$!$B@X/_\]U@`!@1'X.+_^I +M<'H.+_]`)0`8<@XO_U8E`!)J#B__5B4`$ST`C^_@>/'`S@]/[PAW(/``AB&& +M(:``H0#8`*;/<*W>`@`!IJ:&!H40=@;TJ7#Z#>__`MD&I::&!X40=@?TJ7#J +M#>__"-D'I2.&8'G)<*X-[__I<`HF`)`)\@.'((`"AB)X@."N!\S_^@TO_^EP +MS0=/[P_8FKC/<:``L!\5H0_8#+@7H>!^\(X%($H,`P-TT@61YAB'_#B*Y.GT$\!3=`MB*$@$!`GD2@@3A +M.@RO]`#:9@E@``(@3@,#V,]QH`#('Q.ASW6``%@="(4`@$+`#(4`@$/`"84` +M@$3`#84`@$7`!(7@@`6%`!`2`$`1``8>9OP1``#/<(``,)%`@"&``-@`(H*# +M`2!``$#"3"%`H$'`BW8.]$H)#_&$P1IPR7`>"^__AL((=@@0`2$,\(+!R7`* +M"^__AL((=L]P@`!XQR20SW*``'C'98(&P@2[4'-`*8`"B/=0<$OW`GI0<+[W +M!O"."V``AL`(_Q`]D(=DIPJ@WO\0/9"'<`P`'!0"#`@$$A`0!`P`3`0<$%P4`@P(!!(0$` +M1,`""&``1<%,(0"@"O0$A<"@"(4`P2"@#(4!P2"@3"&`H!'T!(7`H`B%`,$@ +MH`R%`<$@H`6%X*`)A03!(*`-A07!(*!,(4"@"O0%A>"@"84`P2"@#84!P2"@ +M3"``H`'9P'G/<(``J!TTJ(D%;^^JP.!X\<`R#4_OI<$(=@*+*'68<&3``(L` +M$@8!$1P",'EP`A('`002"`$0%``QY)(&$@4!`"#)`P"1+R%($@<@0`(*"2`` +M$'@`((H!`94O(H@2!R"``O8((``0>``@Q@$"E2\FB`$'((`!X@@@`!!X`"`' +M`@.5+R?(`0<@P`'.""``$'@`)04`!)4O)4@!!R!``;H((``0>!]G!97P?^=X +MJ@@@`!!X)I4A@^Y)7I0>@`B@0(P>0`<1#!'E2=Z7'D/ND5Y,'D` +M(8(!4'I<>0(0008< +MA#`/ND5Y,'D_9_!__'D(',0S#[_E>3!Y.&!I<<:YA;D(N04AP0(@MA!X()4* +M'`0P)W@<>`BX!2```0&V`,`!I@'``J8"P`.F701O[Z7`#WM(N`]XSW*```!8 +M]"(``$`H`0)(N`5Y]"+``#!YX'\G>.!X\<#/`AS*'+/<(``6!T$@`"``B"`#P`"``!)`"``:''A +MQ5,@0@4$((T/P/\``,]P@`!XQP6``B"#``0A@@_`_P``U;DB>*5[17@0<\H@ +MK0`%]Q!S`-C*(&8`X'_!Q>!X\<#AQ=APN'&Z#^__F'((=#:X-KDP<-8@A0\``(``X'\B>.!X\/'``-G/<(``&"H:""``(*#/<(``U#YN#X__T<#@?N!X`-G/B(J+/<(``/$0@H,]P@`#@1""@,;(PLL]P@`#8-N!_(*#@>/'` +MSW&``!@J`(&`X!/T`=@`H0#9SW"``$@3M@_O_R"@SW"``!0?$(B#X#@)(0#* +M(&$!T<#@?N!X\<`V"4_O\@IO]J3!@."("0(`SW"``$@3`(#/<8``V#:B#N__ +M(('/=H``F`$`W0/TH*,@@\]W +M@`#@1$#!((=#PD+`BW!!P1#9HMH>V^X,[_T8NP#8F@WO]XMQSW"``$@3((#/ +M<(``V#:BIK&V(*"PML]P@`!P!J"@F@OO\1/8`(>%X([W7@@@``'8R@K/^<]Q +M@``\/!V!`>`=H07P1@@@``78M0!O[Z3`%=@`VL]QH`#('V\9&`#@V)"X$*$) +MV+`9``"T&0``=-A"&1@``-B:N`^AI!F``,]P``P`&0ZAX'[/%@.`4],]P@``8*@"`@>`.]&H,#_>:X`KRSW"``%@=!(``@`6E +M`=@'I1T`3^_QP.'%SW6``)@'!X6`X!KRSW"``!@J`("!X!3T,@P/]YK@$/+/ +M<(``6!T$@`"`!J7^#.__)84PE3A@$+4`V`>EV0`+],]P +M@`!8'02`((#/<(``F`,]P@`!@1">`@.$'\@.`0(`"@4)X!/#/)($H +M@00AOH\`!@``H<$%]%$A`(`(]`CP!""^CP```!@$\@#8`_`!V,]QI@"D`!>A +MX'^AP.!X\<`R#0_OSW6@`+1'"'8&\+H,;_:*((D,<14`E@0@@`]P````02@^ +MA?3UBB#_#V\=&)!K'1B0`]@/N,]QH`#('Q,9&(`%AED=&)`&AEH=&)`'AEL= +M&)`)AE@=&)`(AE<=&)!`$0$&SW&``'1%(($$((`/````@%$A`(`'](#@!MC* +M(.$!`O``V,]Q@`#X'B.!*('//'`,@P/[SIPSW>``!`Z#(^&(/\! +M0BC0`,]VH`"T1PIU!?"V"V_VBB")#'$6`)8$((`/<````$$H/H7U]4,6`)9& +M(``-0QX8D%<6`):\N+^X5QX8D%\6`):_N%\>&)``V)ZX4QX8D.!X`-A3'AB0 +M#(]@'AB0;@M/^\]P@`"X*B"`8'D$V(#@%O),(4"@"`ZA^%\>&)`&\,(*;_:*((D,<18`E@0@ +M@`]P````02@^A?3UBB#_#V\>&)!K'AB0$_#/<(``^!X#@!"],B"`#P``V`*? +MO8#@`=C`>`^XI7A?'AB0!LB$X"`/H?'*(*$$*0,/[^!X\<#."@_O"'4H=EX, +M+^\!@*"%$+E!+0`4.&!.#"_OR7$0N;!X.&!"#"_O0"Z!$@T#+^\H$D= +M&)!Y`@_OX'B%X/'`F'"+]PHAP`_K""042$`@@WR$!,$```0!0$*(<`/ZW**(-H*70">F*`Y<]S@`!XJM1['O0`)HT?@`!PJOB- +M@N<(]."3^W\CD8"_)'_@LP;P@><$]"*1(+,`V3BMSW6@`,@<^H4@D^1Y++,% +M\"R3,'7#]UEA`_"LL[EBB2'/#P084``!Y@#9SW"``'C'%0$O[R>@X'CQP``6 +M!$`'&A@Q`!8%0`$:6#$$$H$PG.'*(L('RB""#P``W`[*(X(/``#T"A0&(O#* +M(<(/>@^@``[9T<#@?N!X\@!N<`_`;A`/@&X0#%"5# +M$,"S`8+NN!_TR+/0&X0#$(K/<8``$+\"N!9X&V%EDX#C.]F&[9;`0BG)H +M=GMZ8462@.)Y80?T)I%1(4"`W`P"\`W(`""!#X``'*K$J11](I'`'803%7]X'400`Q(!-L"G`8$$((`/````8-=P````(!/T$(G/<8`` +M$+\"N!9X`&'MN,HF8A#/<(``,![4>""0$.$@L`/9SW"@`!0$,*#B"J`!"G`^ +M\'`2#0'@$P$!`B%.`[%V!_?"?:)X$'B`&QP`SW"@`-0'#Q`.A@#=\!N$`W`2 +M`@'`&T0#0GDP>>`;1`#0$P$!`>$P>?`3!0'0&T0`4R5^@,HAP@_*(L('RB#B +M#"SN+6XN+@$ +MH1:B#,S/=J``U`=1(`"``]@@'AB04O(4'AB0`Q(!-@`6!$`'&A@Q`!8%0`$: +M6#$$RIS@RB+"!\H@@@\``-P.RB."#P``]`H(!"+PRB'"#RAP:@V@``[9`Q(! +M-E")4R+``(8B_@,0J42Z`KC$&8(`%GC/-`9!``%\-`1``&\ +M&@0``=B@&@``W@FO^O"*@.#6`R$``Q(--@;(42"`@%0`1I[B>'000!O"%V)"XH!T`$,]P@`#X'@.`&(B$X-X"`@#/ +M<8``J!T,@0\@``$,H<]Q@`#D'0"!`>##`B```*%BD#,5@!`1(P"`)O('R`0@ +M@`\`P```UW``P```%?0(C8#@%/:D%0`0M+BD'0`0DA4`$:>XDAT$$)X5`!&G +MN)X=!!`*\%$A@($(\HW8D+AO`B``H!T`$`;(42``@!@"`0`Z#H__`Q(--@AS +MJ!T`$,]P@`!8'02`L!4'$2"`52=`!M6Y$''/,=#]P78!Z(%@B)XC"`) +MALHA)0"D%0`0"2'!`/*XK!U`$.?RF!6!$,.Y!\@\>00@B`\!``#P#1($-L]P +M@`"LJ18@``%%D*P5`!!!*`@3"2""`(`5#Q%^%0`1^&#/=X``^![DAT87#Q$? +M9P@BP@/B>I@5#Q#HOP#8A_)$)P`6!">!'P8````CN#&Y`>`X8,]Q@```6C(A +M!@`$)X4?P````$$MA04R(4`!02^!$E(A`0#`N0.YP'`8X87@RB&-#P$`B0W5 +M(0X`I!4`$/2X)/(B>H05`1$"(D``2"```$*X02]"$\"Z!+I4>NEQQKE)(<$% +M-'KKO\]Q@`!\4%%A!O)!*0(!%")!``4I/@!!*0!R`-E9\$$JA0!!+T(3P+H$ +MNE1ZZ7#&N$D@P`44>NN_SW"``'Q04&`&\D$H`@$4(@``!2A^`4$I`'*$%0(1 +MZ[]984$O0A/`NA#A!+I!*84`5'KI<<:Y22'!!31ZSW&``'Q046$&\D$I`@$4 +M(D$`!2E^`4$I`7(?\%$G0)+*(0(`&_0#XL]P@`!44/`@00`BN@4IO@`O<%,@ +M`@!88(05`A$=>"?B(KH%*;X`+W%3(0(`66$]><]RH`#$+"^B+J)`*`$6GKE` +M+`\%Y7DE>,`=`!`*HL]Q@`#\.`'8`*$%\&^"L!4'$?!S1?<%V!BXH!T`$,]P +M@`!`!T&`()4)(8$``(B!X`;T&18`EA!Q`-@#]P'8@.`+]`/8&+B@'0`0SW&` +M`,`[$X$!X!.AH!4`$`0@OH\!`0``%?22%0`1E!4!$)`5`A&R%0,1W@U@`4HD +M0``#$@TVH!4!$"5XH!T`$`0@OH\!`0``!O)R#@_U>0-```/,SW&?`+C_&*$& +MR%$@`(#*(D$#RB`A('SRI!4`$/*X-/+/<8``_#@`@8#@`-@T\@#8`*&`%0`1 +M?A4.$1YFSW"``/@>!(!&$``!'F8%\*X(+_:*(,<)42&`Q?OSSW"@`,0L"X!3 +M(($$_KC,(2*`"O*8%0`0U@WO\P#:=+C88`/P`-@#$@(V"?`-R,]Q@`"LJ19Y +M!9&I,=P@``0OV60@./1]@:042!`@`WR@>,( +M]&`2``&$N&`:!``%\!R2C;@%0:!`"`$0$' +M@.$%]#R2BKD\LJ02#0#HO0GR:!(#`5,@P0!Y83!Y:!I$`%$E0)()\FH2@0## +MN#A@#WAJ&@(`!\C/<8``$*L$((`/`,```-=P`,````3T#AD$!`7P`-B+N`>Q +M`9*`X!3R#=`1``%3(,"`"O+P$0$!SW"@`)@#/J"V&D0`I!(` +M``0@OH\````P!_2&(.6/7`QB`,H@@@"*"@`!@.`$\M(+#_VI\`/(I!````0@ +MOH\````PL_+TN"P(P?,#$@$VI!$``.RX3?)R"J_Q`=@#$@$V';'/<(``^![$ +M@,((K_<`W8'@#/3/<(``#!T`D('@`-W/)2$3RB4"%`/8SW&@`/0'!:&%)0(= +M#7"@L`/(79`-<$"P`\A/@."Z!O)"A@UP0*!&E@?P#7!`H`/(0!`"`0UP0+`# +MR%&`#7!`H`/(2!`"`0UP0+`0&0`$`\B4$```42!`@J`/@?:.#T_X=0!```&! +M^+@/\L]P@`!(!P"0';'/<(``3`=`@`&`4:$2H0?PN@FO\0+8`Q(!-AVQ-@M/ +M_0/(=@RO_W@0``&`X#0`0@`#$@,V`8.8$P$`^+B4&T``%?+/=8``",ZI<,8/ +M;_AH<1#8#!H<,`W,H[@-&APP1@VO_ZEP_0<``)X3``&^$P(!DAL$`)`;A``R +M#F`!@A,#`?BX#/(#V<]PH``4!".@BB`0`,T'(``&&A@P`\BD$`$`AB'EC]@* +M0@`#$@XVI!8`$/2XD`(!`!".SW*``/"]%GK/[`6#1%DY;%S_@`N``DC00`"N!9XSW.``!"_`&,$((T/@`,``#>]9;V`Y`!WTHA`"`/(1$@`Q*2`/8++_68%@`0"2!!!)@6`!#M +MN,H@PB-`*`,A='L(`(H0?PSW```/`1/@S/]5$A@,7Z\\]P +MH`#$+`N`!""-#_`'``#^N#2]4R"!!`/R@>4-]P,2#C9*(``@SW&``#P\`8$! +MX`&A`-DP\`"6$.`0XU2%"`\]W@`#XQR"GHJ>8%@`0+@GO\P#: +M`:?/<8``/#P"@0'@`J$`@;A@`*'/<(``^!X#@`F`42!`@`?R#BDL""0,'/(FXCK@9H\]PH`"H(`B`-@]/\*4%``"D%@$0 +MI[B2'@00M+FD'D`0E!8`$)`6`Q'/<:4`K/]`P+`6`A%XH<]S@`#X'F2#5A,# +M`13C8GH#XB*Z6V)Z8D@B0@`%ND4B0@-6H2#"!""`#P```"`EN`4B`@1%>(FX +MCK@9H<]PH`"H(`B``]G/<*``]`P+@?,#R`&`^;@(]#8.;_$$V`,2`38=L8X,;_<` +MWH'@"_3/<(``#!T`D('@`-[/)B$3RB8"%,]UH`#T!QF%@.#*(<(/RB+"!\H@ +MX@S/("(#RB."#P``;@K*)`($P`>B[\HE`@0#R!R0Q7@-<0"Q`\@]D`UP(+`# +MR"^`#7`@H`/(0!`!`0UP(+`#R#&`#7`@H`/(2!`!`0UP(+`#$@$V')&&(/\, +MA.`?\C.!#7`@H`/(4!`!`0UP(+`#R%00`0$-<""P`Q(!-AR1AB#S#XP@#(`) +M]#:!#7`@H`/(7!`!`0UP(+`#$@$V')&&(/T,C"`"@AOT8!$!`0UP(+`#$@$V +MI!$``/>X$?(Y@0UP(*`#$@$VI!$``&09``2X&0($NAD$!+>XI!D``*01```$ +M(+Z/``!`"`?R`8'PN%P,0O$.\#J!#7`@H`,2`3:D$0``AB#SCP3R.X$-<""@ +M`=DKI0/:2*7/`#]*"CSW"@`/Q$/8`9@.NX +M-?0$(;Z/``8``"_TX'C@>.!X42!`PRGR`\C/<:``R!^P$``!EB!!#QZA$-@. +MH0'8%1D8@+H*X`!!V%$@0,,5\L]P@``(10'9(*`#R*00`0":N:080`!"#&__ +M`=C/<8``-#T-@0'@#:'&#```!""^CP8`R@"8<"7RSW"``"@=`X"`X,HAP@_* +M(L('RB#B"L\@(@/*(X(/```_!-`%HN_*)0(!SW&``#0]$($!X!"ASW&@_N@` +MSW"?`+C_-J!M`@```]G/<*``%`0EH`,2`38!@5$@P(`I\J01``#/```C.Z7"2"6_X`Q(!-@/(!A(0-L]V@``L'J`0$0`!V`"F_@UO_ZEP +M@.``V2"F"?*&('Z/VO(#R*`80`0&&A@T`Q(!-I(1``'JN`;RJKB""._YDAD$ +M``,2`C9^$@$!@A(``8`2`P$X8!MC#0F!<'L;8VFA`8)1(,"` +M;O):#R_X@-@&$@$V!"&!#P(``0#7<0(````-$@(W"?3]N`?R3R+!``T:7#`% +M\*.Z4'D-&IPP`Q("-@&"42"`@2SR3R'``HRX$'D-&APP$(HS$H(`!+A%>,]U +M@`"HDL]RH``X+D2"!K40\"\N@1!.)H,7`-X/)LX0QGK/=H``8+WT)LX0T7`) +M\H#B\?7/<```__\$M0+P9+4(V`P:'##/<(``$#H1B%$@0($*\L]P@`!<.-H- +M[_P`B`T2`3<#R`&`_;C/(>(!T"'A`0T:7##/<8``P#L6@0'@%J$O\!#8#!H< +M,`W,H[@-&APPP@UO_^EP`Q("-@&2@.`)\@W(SW&```"K]"$``(#@"_(!@NZX +M"?0-R`':`""!#X``B*I`J0W,4R!`@`GR!!(!-HH@!``F"F_ZF!$!``/(&I#& +M"B_X#1(!-@W,42#`@`[RSW"``!"K`Q(!-@*`F!D```;(:@UO]`T2`C;Y!6_N +MH\#@>/'`X<6IP8MUJ7#/<8``K$A6#F_N)-JI<)(/+_@#$@$V]@@@`:EP]05O +M[JG`\<#AQ0,2`3:B@2"%$@OO_23:@.4-]`HAP`_K%,ES@#@N,HA@@/*(2$`5B3,.2"LX;C*(8(#RB$A`(MT@"1$'B"L42"`@,H@ +M@@/*("$`BW2`)(0>`*P%\%,E_H`L]`#>A"L*(@`A@'^``.2;"B1`+@3@A"D$ +M+P`@4@Y$*SXG`"&`?X``=*B@B.&(D'7,(\&#'_(*(<`/ZW)`+`X$0"L%!(?8 +MC;B(VP4F1!/1`:_O!27%`PHAP`_KSW6@`-0'TJ4.",_\SW"``"P>`("`X,PG(I`$ +M]!,=F),#R*`0```S\%$DP(0#$@$V"O)O($,`H!D``(H@"``&&A@PT_%1)("$ +M`-C/(.(%]?6D$0``^K@%\@78$+CM\8#B"/+/OQ\O`@@`#"N`\E#1``V`\@ +M@``&(0&`[_6`Y27R+RA!`TX@C@<-&I@S]=@%N&8(+_;)<0W(SW&@`!0$"J'/ +M<:``9"[P(0$`T[D*<(H.[_/D>2(,+_K)<`#8#R"``P8E#9#>]<]R@`#D-@"" +M!]F'X`T:6#`>\L]PH``X+@6`!""`#\````#7<,`````.\O78!;C/`"]""BSW"@`!0$*J#=`4_N\<#AQ<]P@``$K<]U +M@`#>!V"-08B$*Q\``"&!?X``A+`"#Z_]`N(@C<]P@`#8-\H+F!_*#Y@ST +M`!:`0`$=$A``%H!``1T2$``6@$``K6T!3^Z`X5;R0"'"`R2ZP[D"\`#9C^&6 +M``T`,R9!<(``?$A`)X-R-'L`>P`6`4`$&%```!8!0`084```%@%`!!A0```6 +M`4`$&%```!8!0`084```%@%`!!A0```6`4`$&%```!8!0`084```%@%`!!A0 +M```6`4`$&%```!8!0`084```%@%`!!A0```6`4`$&%```!8!0`084```%@%` +M!!A0```6`4`$&%``0B)"@+/UX'[@>(#ARB1-<.!XZ""M`0`6`4$"&%0`X'[@ +M>/'`+@A/[@#=SW<```0=2B``(JEV%2*`,PX0`08`V,]RH``4!,JBJ*(GH@2B +M/66(X6BYRB$.`%X.[_7I<$(@4"!,(`"@(.%$@`((.\L]P@`"8!@N( +MBW/)<8(-;_:I<@#``GVM?0`F@!^``)@&'!#!`,]R@`#\]R@`"@@GEB(8F`X7IB"O(0<0_R +M$'$2]H7E5O8!Y:]]"_!")9$0+R%')&&]KWT0\`,2SP``V6IU#?"`Y4HA`"#* +M)6$0!O)")5$0+R%')`'9@.$L\O-N]'\5)T$3SW.``*"".F,`(T4`%2=/%/EC +M(8E!BC!R^V/CB]GV`B)$``,5@@`$O_!_(G@$NB\D"`$")X,0;'@O($8.,@AO +M[HAQ#G@"?PCG[G]$O^U_3"``IH/V"N?M?\EP"G&6#"``Z7(!YL]P@`"8!A*( +MSWX0=K@&S/^%!B_NH<#QP#8.#^XH=8#BS",B@`GR+&TO><]V@`"8!C.N!O#/ +M<8``F`:SJ:EQSW:``)@&M*X`KE6N`@H@`':N`!")`.&(R7`2B-&.$':@`0D` +M1"D^%R]QA"X#$0HF0`X`(4,."B6`#X``&(.@H0N`14*)T`.`")- +M#@H@@!^``"S.`"!$$P`FC1^``)@&3"$`D,PA8I`G]!H3P```V1BM&Q/``$HD +M@'$$"K0"*+WD#K7OP`1/``(#@&/0`VEBM7*T@'8(02B2`<0#9J"#``Q-N +M%'@U>,=P@`"@@D"H0:A"J$.H`>$O>6'P?+D`(8H!.HMLN@`B0!$:B.!R`"(& +M`O8((`#I;5YQW&``*""`*GX +M<0`2@!``$X$0D@@@`.ER`1\"`!4E2P,5(TH#`1.`$`$2@1!Z""``Z7("'P(` +M`!.`$``2@1!F""``Z7(#'P(`0B!($$P@`)`!Y9('[?^O?0'FSW"``)@&$HC/ +M?A!V:`;,_P#9SW"``&1%K00O[B"HX'CQP,]P@`#4QY5B$!Y.&#@?P]XX'B!X/'`N'$< +M]$PE`(#$]DPE@(/.]@HAP`_K!0R($$!C"'#C\HAP0_*(L$'RB"!#P``V!3*(X$/```0 +M`CP!8>_*)"$`SW"``*`2-7C1P.!^X'CQP,]R@`"J!@IJL@D@`"EJ[@T``-8( +M``#/<8``F%H@@<]P@``T#R(((``!VL]Q@`"46B"!SW"``&`.#@@@``#:T<#@ +M?O'`/@L/[AIP@.%(=Y0`+```W3IQ%2!`(X#G0(@"B`SRSW:``'`0%7X"N!1X +MQW"``,@/"_#/=H``H!(5?@*X%'C'<(``J!`AB%$A`(`D\@40P0`BK@80P``# +MKNEPF@P@`$AQ`*Z`X,P@8H#*("$`$_)$*#X'`"&`?X``%(/%$(,`X1"!``(B +MP``0>`>X=@PO[F)Y`:Y"(4$@@.%Z!^W_`>7]`@_N\<"B"@_NSW"``)@&$1"( +M`,]P@`"8!A*($7"T``L`2B8``$HAP!%$+CX'+W"$*`,1)W``(($/@``4@Q\1 +MRP``(($/@``4@QX1R@#X<`#>!M\`)XT/@``4@]5]!XUI<07:F'`F"B``!17# +M$$`N@@!4>H0H`14`(D$.U'G'<8``+,ZX<0"IB'!)<0?:_@D@``85PQ`!'0(` +M8;^`YP'FM`?M_\]^0B%)$$PA`)!`)D8`>@?M_R\FAP%`($@0SW"``)@&$H@O +M(`<2$7!6!\K_/0(/[@+;8*@`V`"I`=C@?P"JX'BAP?'`O@D/[J'!9<((=BAU +MSW"``+X&A<&+@`J +M`""H4R6`$(7@3``*`$8ES1&O?1OP`12`,``F@1^``)C+4FU4>EEA(,(`J40N +MOA8`)4`>1*D4%,$P^&`@J,EP7@@@`*EQ`>6O?5,E@!"%X*/V(?`!%((P$FT4 +M>``F@1^``)C+.&!`J"#"1*C)<#(((`"I<0_P0B4`%@]X`12!,,=V@`"PS`*X +M%'@>9B#`**X,K@C<6P$O[J'`X'C@?N!X\<#B"`_N`-[/<*``M`]P$!``SW"@ +M`+0/W*#/<8``F`92B7&)4',1]L]U@`#$BG_8%"//`+]G`*_!KP'C;WM0"@_N`*4`W0[>SW>``(!'E@CO_ZAG +M8;Z`Y@'EKWTY]\]P@`#"QX*6H`V`\@0``G<%IXX'\.(,``X'CQP/8/S^W/=X``F`8`C_H+[_\SC\]P +M@`!<10`0T`#/<8``)"(4CT>)$'(/]`"/(8DP<`OTSW"``&5%`!#```D@``0O +M(`4@L8\#\`'EKWT2CQ!U`@$)``#>2B*`(\]P@`!=10"(@.`1\D0MOA,`)D`> +MSW&``%Q%`!'"```@@0^``"C/0*E>\,]P@`"X*B"`8'D`V(S@"_3/<(``<$?) +M8`(@0"`->$@@0``#\$@@0"`O(04@SW"``(!'RV`3CZEQ]@QO]56/"2!`!"\A +M!2#/<(``N"H@@&!Y`-@`)9,?@`"T!I#@#/+/<(``N"H@@&!Y`-B,X`03@2`, +M],]Q@`!4",EA!!.`(")X"2!!!`GPSW"``&!'R&`">0DA001$+;X3`"9`'L=P +M@``HSR"H.G`3CZEQ]@ZO_\ER`!'!(`)Y`!E"($(B4B!,(@"@`>84!^W_SWY] +M\?T&S^W@>/'`K@[/[0AUSW"``.LL`(@0=2AW!_3/<(``ZBP`B!!W'/+/=H`` +MF`:I<$`F@1*""&_V0";"$BJ.!&Y^#R_V2XX*CBH.+_8KCL]P@`#K+*"HSW"` +M`.HLX*B]!L_M@.#AQ1KTC"'"C3@`*@`!V$HD@''/.!_P<7AQ>'&`!'-`(#E1/8` +MW:"I@.`<\H#E1?8`V`"I`-W/<(``7$8`D!!UA/:I:*U]H*G/<(``M$44($X# +MH(Z@J@`1P0`T>`&(&O"`Y43V`-V@J<]P@``(1P"0$'6%]JEHK7V@J<]P@`!@ +M1A0@3@.@CJ"J`!'!`#1X`8@`J\'&X'_!Q?'`D@WO[0#8H<$`'`0PSW6``(`' +M`)7/=H``%(/)<8HB!`K"#N_U`=N`X!#T"B'`#^MR`!4$$<]P``#;%(?;B[OI +M`B_OBB4$"@`6A!!,)`"!RB'+#\HBRP?*((L/``#<%,HCBP\``(P`SR/K`KP" +M*^_*)2L`V@I/]8#@RB'"#\HBP@?*(((/``#=%,HC@@\``)(`SR/B`LHD(@"0 +M`B+ORB4B`(MQ1=@!VCH.[_4!VX#@#_0*(<`/ZW+/<```WA25VXN[BB1!`64" +M+^]*)0```!0`,0'9AB#^#\#@P'G/<(``>!4@J!D%[^VAP.!X\<"F#,_M"'77 +M=24``(``V$KWSW&``'C')8$P==#W(GT!X/GQSW"``'C'Q8"IC"`0@,HAQ@_*(L8'RB"&#P``S2+*(^8,RB0F`.0!)N_*)08!%KBQ +M!._MI7B!X,]Q@`!`!P3T`=@`J0&I`(F!X,H@@0\``,0)RB""#P``@`#@?P&A +MSW*``(`O((H982"J(8HX8.!_`:I!B0*X%GC'<(``$+](J"*)X'\IJ,]Q@`#X +M'O`A`@``V0V21+C@N"ZB!/**(0@`+J+AN`3RB[DNHE$@@(`#\HVY+J+@?@T2 +M`C8$(+Z/8````,]S@```JE1[QW*``'"J"'$&\@/(')!1(("""O($(8$/80`` +M`-=Q`0````;T`-@`LP'8'O`,S%$@P($#$@$V#?(R$8$``8LP<`3T`-@!J_+Q +M`>`!JPOP,1&!``"+,'`%]`#8`*OF\0'@`*L"V.!_&*KAQ>'&SW*``(@&@.#` +M(B(!_]T2:19X`""##X``%[^@JP#=2B0`<<]S@`#XQJ@@@`*N8GAE-GC$J*YB +M`>6O?<"HP<;@?\'%X'CQP/H*S^W/<(``I!@`@(+@H<%&\L]V@`!4.,]U@`!8 +M.`"%((80<2KRSW"``$0E!(!1(("`0,$%\D\A``%`P(#A"/3R"F_S`-CF"D_S +M/@V/]8MP!-FAVCW;Z@YO_!>[((:`X0OR`(6`X`?TR@IO\P'8Y@N/]2"&(*6` +MX1+RW@M/\W_8"KC/<:``T!L3H7_8$*$`V)6X$*&J#F_P`=C)`N_MH<#@>/'` +M4@K/[<]Q@`#X'A5Y0($(@@0@@P^`````1"`/`B^[!K]E?P0@@P\``0``02M. +M`^5^++O%>\$2#@;1<\`2#08P\@0@OH^``0``'O+/=H``0"+(CH?F&/2^N`BB +M0($(@@0@@P^`````1"`!`B^[!KEE>00@@`\``0``02A#`R5[++@%>X#EP1K8 +M``SR+RE!`TXA@`<2""``$"4-$(#E^/49`L_M\<"N">_MF'"0X(WW"B'`#^MR +M<=B-N(HCC0LQ!^_N2B4`!$HD`'0`VZ@@P`Y`+(T!=7U`+((`QW6``-#"`(7/ +M<8``$+]6>MVX06$`I?&YT2`B@@CR1"`"!B.Z`>*!X@OWSW*``%#!%B("`4"* +M42(`@`/RGK@3\"VYP+G/=X``^![P)T\04B!.`L$7`18+(8"#!O(HA_ZY[_.? +MN`"E`>-Y`<_MX'C/<8``^![P(0``SW&``*RINQ`"!KH0`P9"H6&AO!`"!KT0 +M``9%H>!_!J'@>,]Q@`#X'O`A``#/<8``K*F^$``&%B$"``*2&K$#DANQ"(HX +M&0(``-C@?QVQ\<#AQ<]SH`"L+QF#\+@9@P#=#/($((`/"````-=P"`````'8 +MP'@'\(8@?P^"X`'8P'B`X!GR&8,$((`/#@```$(@`(#*(&(`@>`/\@HAP`_K +MK:#AB7_&*5ZSW6@`*@@K87DY9+W@.+L +M]0+;SW*``*088*)1($"`SW*``%0X`((1\H&X$/`X$P0`6!,%``HAP`_KA4`B('@(*() +M],]Q@``8.`"!@.##]FJX`*$!V<]P@``@';H)K_T@J`4`S^W@?N!XX'[@>/'` +M>@^/[4H@`"#/<8``U"O/<(``V"K/2R1`>4`(%`@1"H^"R]Q/V/CC_%U/F.O]\]R@`!`(DB*A^)&]$:.QW&``%`K +M@>(!VL(B@0!5>1@1!`%,)(""3O8*(<`/ZW+/<```Q1N*(T0&G03O[DHE@`+L +M$`$!]A"``"QX+W42=97WJ7"&"._M"G&`X7OR"B'`#^MRSW```,8;BB-$"`HD +M``1E!._NN'4*<%X([^VI<8#A9_(*(<`/ZW+/<```QQN*(X0)[O&(XDGT0(!1 +M(@""1?)&CL=Q@`!0*X'B`=K"(H$`57D8$00!3"2`@LHARP_*((L/``#,&\HC +MBP\``"X!;`?K_\HBRP?L$!$!#"!`I!;W*G#V#Z_M"G&`X3/R"B'`#^MRSW`` +M`,\;BB-$#0HD``35`^_N"B5`!`IPS@^O[2IQ@.$?\@HAP`_K!XX'C@>.!X +MX'C@>.!XX'@`@N!^\<#AQ2#=SW.@`,@?L*-#&Q@``-BZ#^__C;BQHPT&C^WQ +MP(H-C^VAP3IP*'8:H1#8#J$!V!49&("Z#N__@=A1(`##%?+/<(``"$4!V2"@`\BD +M$`$`FKFD&$``0@BO_@'8SW&``#0]#8$!X`VA`]G/<*``]`HA#8#J(!V!4:&(`H<`?P`=D$((`/(````%$@`,/,(2&`S"`A +M@!CT42,`P!3TSW```)\7M@O/],]RH`#\1!V"68+KN@#9YO4$(+Z/``8``.#U +MX?%1(P#`%?+/<(``"$4!V2"@`\BD$`$`FKFD&$``I@]O_@'8SW&``#0]#8$! +MX`VA42``PP#8"?3/<8``N#P0@0'@$*$`V)BXT<#@?N!X\<"J"Z_M`-D(=\]P +MH``L($`0$`#/=9\`N/\=A<]V@`!L!CVE`*8,\$8,C^[/<`\`0$("#6_Q"G&! +MX!#RSW"@`-0+&(!"(``(2"```!!W+O<`AAVEN0./[0"&"B'`#^MR7ML=I<]P +M``#.(HHDPP_=`._NN'/QP"8+K^T!V:7!&G#/=8``B`9:=2X*;_^+<$P@0*`` +M%(4P`121,`3T0"42$4PE`(#$]DPE`('-]@HAP`_KG"%]HPDPZ\H]``6`$$`%H]``!:` +M0``6`$%,)`"DA@`*`(#G)_+/<(``;!0`@$`LS2"U?1#@N&"F"6__!-G/<(`` +M;!0`@$PA0*`=93)@.&`%(D($ +M0+`$W0;P@<`$W3H);_^I<0`BC",`'`(5SW"``/@>\"``!![?P!`"!H#B+RF! +M``(G0!`E\C)HSW.``!>_-GDK8Q$C@(,(\@`F@1^``/C&%GD`&0(%`"V!$PLA +MP(`(\@`F@1^``/C&%GD$&0(%$"("@"\I@0`")T`0WO5"(T`@@.#8!LW_C@]/ +M\O$!K^VEP.!XX'[@>/'`G@F/[<]Q@``(R$`A$0%5(4X$SW"``-`K%H``WX#@ +M5B%-`RP*0OD`$00A`"2!`S!U.``&`$"-C")#AP&-$O1"C=#B#O1#C>_B#/1$ +MC8PB@H8(]$6-B>($]+]@`N?"?P+@'64P=:GW@.?*)P$1SW.``/`K!8M`()`` +M521`!`)PC"`(@,HAS0_*(LT'RB"-#P``PAO*(XT/``"0`*0&K>[*)0T$D'?8 +M9T?W`"`!!%H/[_P"),(#!&O99Y8.[_P*<@`1`2'/<(``T"L"<0`91"`M`:_M +M]J#@>/'`X<4(=<]P@`!`(@B(A^#,(&*"!?)J"(_T@.`>\L]P@`#8*@"`42"` +M@ACTV@N/](#@%/+/<8``^!X`@<@0``:&('^."O0!@<@0``:&('^.5`RA_/'`8@B/[>(*C_2`X"3RSW"``$`B"(B' +MX![TSW:``-@J`(;QN!CRSW"@`$@N"X#/=:``."[3N.>%!'\'A>9X!Z6F"B`` +M$-@'A:B_Y7@'I0"&L;@`IG4`C^WQP(X*C_2`X!?RSW"``$`B"(B'X!'TSW.` +M`-@J`(/PN-$@880)],]RH``X+B>"D;B(N2>B`*/1P.!^X'CQP-(/3^W/<*`` +M2"X+@,]UH``X+M.XQX4$?@>%QG@'I3(*(``0V`>%I;[%>`>E$0"/[>!XX<7/ +M<(``^!X!@![9P!`#!H#C+RC!``(A`@`3\H[B"O(2:A9XSW6``!^_"&6!X`OR +M$".#@"\HP0`"(0(`\/4!V`+P`-C@?\'%SW"``-`K%H!"(`"`X'_*(&(`X'CQ +MP.'%SW6``-@J`(7ON`?RK[A""2```*4Y\)H)C_2`X`3R@@]/\C/P`(7RN`7R +MLKCR#V_R`*6."8_T@.`G\L]P@`#4*TB(*HA$*CX+`"&`?X``9"LU>`:(@>`9 +M]!H*C_2`X!7RSW"``$`B"(B'X,P@8H(&\GH.3_2`X`GR`(51((""E`JA_4&3^T`V<]P@`#0*^!_-J#/<8``V"H`@8*X +M`*$`V+$!K_:,N/'`3@Y/[<]P@`#X0P8,[_T`W\]P@``41/H+[_T"W<]P@`#` +M0^X+S_W/<(``I$/F"\_]SW:``-0L<-P")@$31"\^%PH@0"[."^_]`"%`#D(F +M`![""^_]`G!AO8#E`>"R_U`=F`Y?#U@.;=]<]PH`#`+Z48&(44&-B$ +MV01/[>!^X'C@?N!X\<".#$_M"'8&\,]P``!K#AH,C_3/=:``P"^C%0"642`` +M@?7S!\A`'1B0#`J/@.`)\L]Q@``@/1>1`>`0>!>Q`-@*K]D#3^W@>*'!\[A4'@&]-(*[_"!P1KP +MM^$(]!MX$'C""N_P@<$1\)3A!/0<>`KPBN$%]`0`#8SW*I`*3_N:($%`$Q@K@WHAJB"?#HN3?R3"(`H-$@XJ$%\L]UH`#('TSP +MSW*E`*S_SW"``/@>N*($@%80``$4X`(A`R`#XR*[>&-X8$@@0``%N$4@0`,6 +MHD$HP"'`N'=H+,`$(8$/````("6YSW6@`,@?97@E>(FXCK@9HD`5`!8@\"S` +M@.#*(<$/RB+!!\H@(0[/("$#RB,A!<\C(0/*)"$`_`=A[LHEP0`%O:5XSW&E +M`*S_%J'/=:``R!]`%0`6SW:@`+1'5Q8!EDHC`"!*)$`@!"&^CP`H``#/<8`` +M6!TP@2"!PB0")07PLMBR":_TC+AO%@"63"0`H`0@A`^`````!"""#R`````$ +M((,/``8```;R0!4!%H/AA/<`V0/P`=D3%0^6!""^CP`X```$)X\?````@,PA +M(8#`(V$@!2(!`>5Y!2'^@`7T3"-`HIX'SO^`YP7R@.+,(R&`CO)K%A.63",` +MH$_R:G2$)-"1"_+/<8``P#L0@0#=`>`0H9R]8_!1(\"@"O+/<8``P#L1@0'@ +M$:%"W5GP:G2$)`*8"?+/<8``-#T1@0'@$:$.\%$C@*$)\L]Q@``T/02!`>`$ +MH03P4R,^HP3R`-T_\%$C0*,*\H8.S_S/<8``L#T%@0'@!:'T\9X+S_(*(<`/ +MZW)O%@661-B,N.G;C+NE!F_N"B3`!(#B"/+/<8``N#P0@0'@$*'<\8#C*?+Z +MN`ORSW*``#0]+X(`W0'A+Z*1O0KP^;@-\L]R@``T/3*"0MT!X3*BB@NO_VIQ +MF+U(\'$6!)9O%@66"B'`#^MR.=C/\`,2`3;/<(``",H0<0?RSW"``-#*$'$+])(1``&J +MN)(9!`">$0`!JKB>&00`SW&@_F0`SW"?`+C_-J#/<(``R`8`@(#@"/+/<8`` +M="4%@2)P!:'/<8``P#L/@0'@#Z'/<(``R)LA@,]P@`#X'@.`%)`0<0WTSW&` +M`*06&H$[@21X42``@I`-8O7*(&(`J7`(W*L'+^VCP*'!\!XSW"D`)!!38#/<8``9*Q"L1J`42!`Q@.Q!""`#_\````PN`2QSW"``&2L +M`-H(\L]Q@`!0JS*!42&`@@7R0K!#L$2PX']9L.!X\<""#B_MF'#/<(``4*L. +MD,]V@`!DK`"VSW"``%"K2!`%``0ECX\````"`-L$\E$E`((*\L]QI@#H_PN! +M`Z8,@02F!/!DIF.FSW6D`+1%#!4"E@T5`);_V2\@AQ"`YQ"Y!"))`"SR,A4! +MEE,ACP#_9R&V_]GT?PBY[W]$>4`O!A(`)D<``"#($P4G!P)`+P$6!"*"#P#_ +M``!`+P@4.F(`($@2_]D%)P<""+D%(L(!!"!'`/A@`">!`25XY;9/>00B@@__ +M````*+I%>2.V#W@$M@05`)91)0""`K80\D0E``8CN`'@@>#*]\]QI@#H_PV! +M!:8.@0:F!/!FIF6F`-I*)(!P!MF-N:@@0`,IV!*X\"!#`$`F`!]5>`'A8*`! +MXL]P@`!0JP"0.!X`$54F010:ML]P@``\K,H+K_P(VAL5`);/<:4`V,L9IAP5 +M`)8:IAT5`)8;I@Z!'*8/@1VF)A4`EAZFSW"D`)!_'("!!2_M'Z;AQ<]U@`!D +MK`FE*J5XM4NE`=@9M>!_P<5*)`!Z`-FH((`"`-K/<(``9*PU>$"@`>'@?@`` +M`@````!``0``````J!)#=0$&``$``````````.@]@`!\/H```'Z``#@=@`!` +M"(``<`6`@0\:&R(``!LE`@`;0```&W$4!H"!``#`%@\;"R(8!H"!``#`%@\; +M"B(!,B``#&$0,` +M`20```$E```3)<(L$R0$*,`1`D83)`0HP!'"7Q,D!"C`$0]%`"(`7``Y+``` +M9```$R0!`!,E.!S`$0]W$R+@',`1`@`!8@\!$R($",`1_`7`$@0HP!$/$P(B +M``;`$@0HP!$/$PX!("!#QH;(@``&R4"`!M````;<0`````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M``````````"P`H``/`"`````````````*`"````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M````````````#)&```P&```,D8``G-$```D-$10*#1,7&1D9&0D)```DC(`` +M*'D```R1@```````#)&````````HK8``2'D``#"`````````,8```(B(,S,R +M@````*JJBC.`````````-(`````````U@````````#:`````````-X`````` +M```X@````````#F`````````.H`````````[@````````#R`````````/8`` +M`*JJ"@`^@```4X6(B#^````````P,(`````````Q@```FIE853*`````JJJJ +M,X`````````T@````````#6`````````-H`````````W@````````#B````` +M````.8`````````Z@````````#N`````````/(`````````]@```JJH*`#Z` +M``!5F)FJ/X```````%`P@````````#&`````````,H`````````S@``````` +M`#2`````````-8`````````V@````````#>`````````.(`````````Y@``` +M`````#J`````````.X`````````\@````````#V`````````/H`````````_ +M@````````#"`````````,8`````````R@````````#.`````````-(```)IY +M```U@```JJJJJC:`````````-X`````````X@````````#F`````````.H`` +M`*JJJ@H[@````'"9JCR`````````/8`````````^@````````#^````````` +M__\``*4!`0"Y`=\`L0`;`!8!&P"O`!L`%`$;`&P`H`#1`*``;P"#`'$`@P!V +M`(,``!)`-T` +M20!_`%H`Y`!:`*H`/P"K``$`#P$_`!`!`0!Y`&H`W@!J`*@````-`0``I@`W +M`*<``0`+`3<`#`$!``0`"`"<``0``O0```+X```"_````P````"(!```C`0``)`$``"4!```2 +MT@``$](``````0`"``,`+`!D`'0`@`",`*$`!P```````0`"``,``````+<3 +M(@"X%",`N14D`+L6)0"\%R8`O1@G`,`9*`#$&BD`!QL```@<`0`+'0(`#!X# +M`!`?!``B(04`)"(&`"8C!P`H)`@`*B4)`"PF"@`N)PL`,"@,`#0I#0`X*@X` +M/"L/`$`L$`!D+A$`:"\2`&PP$P!P,10`=#(5`'@S%@!\-!<`@#48`(0V&0"( +M-QH`C#@;`)$Z'`"5.QT`F3P>`)T]'P"A/B``I3\A`"1)!@(L2@H"-$L-`3Q, +M#P%D31$!;$X3`71/%0%\4!"2@`'PD +MH`"`)*``A"2@`%`0H`!4$*``2":@`&`0H`!,)J``9!"@`&@0H`!<$*``6!"@ +M`#`0H``\$*``-!"@`"P,H```@:0``8&D``.!I`"()*``C"2@`)`DH`"4)*`` +MF"2@`)PDH`"@)*``I"2@`(!;@```````L@S(`/________\``?__`@/___\$ +M______________________\%_P;_!_\(_PG_"O\+_PS___\-____#O___P__ +M__\0______________________________________________\1____$O__ +M_Q/___\4____%?___Q;___\7____&/___QG___\:____&_____\<____'?__ +M_Q[___\?____(/___R'______________________R(C)/\E)B?__RC___\I +M____________________________________________________________ +M__________________\``0`!`0```````````0```````````````````P`` +M```````!`````````/1"`0``````6'````$```!@]P$``@```-#V`0`#```` +MF%0"``0```#T0@$`!0```.`L`0`&````+/X```<```!H+0$`"``````[```) +M````6&,```H```#4TP``"P```$@T```,````G%0"``T```#4\P``#@```(CT +M```/````8/,``!````!D]```$0```)!3`0`2````^!<"`!,````\)0``%``` +M`&1\`0`5````Y&4!`!8```#$=0$`%P```/CU`0`8````I*8!`!D```"4'0$` +M&@```/!"`0`;`````!0%``````````````````````#_`/\````````'```` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M````````````__________\````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M``````````````````````````````````````B9```(F0``")D``/"````( +MF0``")D``%2!```(F0``")D```B9```(F0``")D```B9```(F0``")D```B9 +M```4B0``6(@``$B(``!PAP``@(@``"R'```(F0``")D``/R.``"PD@``8)0` +M``B9```(F0``")D``'"8```4C@``3(X``+B-```(F0``")D```B9```LF``` +M")D``)B-```(F0``")D```B9```(F0``")D```B9```(F0``")D```B9```( +MF0``")D```B9```(F0``")D```B9```(F0``")D```B9```(F0``")D```B9 +M```(F0``")D```B9```(F0``")D```B9```(F0``")D```B9```(F0``")D` +M`!B"```(F0``")D```B9```(F0``")D``'R5```(F0``")D```B9```(F0`` +M")D```"%```(F0``$(4```R%```$A0``"(4``*!^```(F0``='X```B9```( +MF0``")D```B9```(F0``")D```B9```(F0```'X```B9```(F0``")D```B9 +M```(F0``")D```B9```(F0``")D```B9```(F0``4(,``.R"```(F0``;(,` +M``B9```X@@``\(8```B9```(F0``-(D```B9```(F0``")D```B9```(F0`` +MB(H``&2)```(F0``")D```B9```(F0``")D```B9```(F0``")D```B9```( +MF0``")D``!2%```(F0``")D```B9```@E0``")D```B9```(F0``P)<```B9 +M```DF```O)0```B9```(F0``X'P``'R4```(F0``")D``'B'``"0AP``")D` +M``B9``"\A```Y'X```B9```(F0``")D``%2.```4AP``")D```B9```(F0`` +M")D```B9```(F0``3(8```B9``#LF0``+)P``&";```TG```6)L``%";```\ +MG```+)D```B9```(F0``")D```B9```(F0``")D```B9```(F0``Q(0```B9 +M```(F0``")D```B9```(F0``")D```B9``#0G```Y)T``"A]``!T?0``")D` +M``B9```(F0``")D```B9```8?P``")D```B9```(F0``")D```B9```(F0`` +M")D```B9```(F0``")D```B9```(F0``")D```B9```(F0``")D```B9```( +MF0``")D```B9```(F0``")D```B9```(F0``")D```B9```(F0``")D``!Q_ +M```\@```K'\``$2<``",?0``R(```&"!```(F0``")D```B9```(F0``3($` +M`%"!```(F0``")D``/2````````````````````````````````````````` +M``````````````````````````````````````````````````````````"( +MJP``T*D``/2L``#\JP``"*X``````0#_____`````/__________`0```$`. +M`````````````````````````````````````````0``````T/X````````` +M```````8(*``(""@`$`AH`!((:``'""@`"0@H`!$(:``3"&@`"@@H``P(*`` +M:"&@`'`AH``L(*``-""@`&PAH`!T(:``.""@`#P@H`!X(:``?"&@```````` +M``````````````````````````$````!```````````````````````````` +M````````````````````````]`P```#_`P"4#0```/\%`#@-````_RT`%`T` +M``#_/0"P#````/\$`-@,````_R4`;-<```#_W0!<#0``$!!,```````````` +M``$!`#P\/#P\/#P\/#P\/#P\/#P\/#P\/#P\/#P\/#P\/#P\%145%3P\/#P5 +M%145/#P\/``````````````````````\/#P\/#P\/#P\/#P\/#P\/#P\/#P\ +M/#P\/#P\/#P\/!45%14\/#P\%145%3P\/#P`````````````````````/#P\ +M/#P\/#P\/#P\/#P\/#P\/#P\/#P\/#P\/#P\/#P5%145/#P\/!45%14\/#P\ +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M````_P````$````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M````````````````````````````B`\``#06``#D$@``9!```/07``#<$P`` +M,!4````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M`!@'@`"TD8``&````'21@```````````````````````^!H!`-"Y``#$(0`` +MT+D``-"Y``#0N0``$`<````0`@#4Y```T+D``-"Y```D*0``)"D``"0I```D +M*0``)"D``"0I```D*0``T+D``-"Y``#0N0``T+D``.Q4``#0N0``T+D``-"Y +M``#0N0``T+D``+CD``#0N0``T+D``*C6````````/`(!`$`"`0"T`@``H`(` +M`+A2`0``````.,D``-S:`0!@R0``!-L!`(3)```LVP$`[#:``*"&@``````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````!``(``@`#``0`!``%``8`!@`'``@`"``)``H`"@`+``P`#``- +M``X`#@`/`"8`)P`H`"D`*@!&`$8`1P!(`$@`20!*`$H`2P!,`&@`:0!J`&H` +M:P!L`&P`;0!N`&X`;P!P`'``<0!R`'(```+21@``8````=)&``````````````````'\``````````'\` +M`````````````````````````````/\``/__```!``````````<````````` +M`````````````0(#!`0$!`0%!@<("`@("`D*"PP-``!N.V@[8CM<.VXZ:#IB +M.EPZ;CEH.6(Y7#EN.&@X8CA<.&XW:#=B-UPW;BEH*6(I7"EN*&@H8BA<*&XG +M:"=B)UPG;AEH&6(97!EN&&@88AA<&&X7:!=B%UP7;A9H%F(67!9N%6@58A5< +M%6X4:!1B%%P4;A-H$V(37!-N$F@28A)<$FX1:!%B$5P1;A!H$&(07!!N`F@" +M8@)<`FX!:`%B`5P!;@!H`&(`7`!4````;CMH.V([7#MN.F@Z8CI<.FXY:#EB +M.5PY;BMH*V(K7"MN*F@J8BI<*FXI:"EB*5PI;BAH*&(H7"AN)V@G8B=<)VXF +M:"9B)EPF;B5H)6(E7"5N)&@D8B1<)&XC:"-B(UPC;B)H(F(B7")N(6@A8B%< +M(6X@:"!B(%P@;A)H$F(27!)N$6@18A%<$6X0:!!B$%P05Q!2$$T021!N`6@! +M8@%<`6X`:`!B`%P`5```````````````'0`````````````````````````` +M``````````````````````````````#0;P$`"`````,```!`1(``A`B```0) +M@`"$"8``!`J```H-$10*#1$4&1D9&0H*````````!@8&!@D)"0D`!@````4& +M!P@-#@\0%187&!D````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M````````````````````````````````````````````````#QJLK"4OK*RL +MK*REK'Z13D,,FS8````R````CP",`(H`0P`/`"!H-P```!$`/CH@$0```B4` +M``PO```"+SDY``HE/+I'<8T`!Q`````X` +M```G````#P```"`````0`````@```/,`````````]`````````#U```````` +M```````````````````````````````````````````````````````````` +M!`````4```#A`PX>X0```.$##A[A```````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M`````````````````````````````````````````````.$##A[A````%?9C +M]K#V_/9&]Y#WV/[Y*_IG^J+ZW/H4^TO[@?NV^^K[ +M'/Q-_'W\J_S9_`7],/U9_8+]J?W/_?3]%_XY_EK^>OZ8_K;^TO[M_@;_'O\U +M_TO_8/]S_X7_EO^F_[3_P?_-_]C_X?_I__#_]O_Z__W__________?_Z__;_ +M\/_I_^'_V/_-_\'_M/^F_Y;_A?]S_V#_2_\U_Q[_!O_M_M+^MOZ8_GK^6OXY +M_A?^]/W/_:G]@OU9_3#]!?W9_*O\??Q-_!S\ZONV^X'[2_L4^]SZHOIG^BOZ +M[OFP^7#Y+_GM^*GX9?@?^-CWD/=&]_SVL/9C]G"Y@[J6NZJ\OKW2ON>__,`1 +MPB?#/<13Q6K&@,>7R*_)QLK>R_;,#\XGST#06=%RTHS3IM2_U=K6]-<.V2G: +M1-M?W'K=EMZQW\W@Z>$%XR'D/N5:YG?GD^BPZ+^```>`3P"6@-X!)8%M`;1!^\(#0HK"T@, +M9@V##J$/OA#<$?D2%A0S%5`6;1>)&*89PAK?&_L<%QXS'T\@:B&&(J$CO"37 +M)?(F#"@F*4$J6BMT+(XMIR[`+]DP\3$*,R(T.C51-FDW@#B6.:TZPSO9/.\] +M!#\90"Y!0D)60VI$?47___\`____`?\"`____P0%_PG_!PH&"`L``0$"`0(" +M`P$!`0$!`0$!`@("`@("`@(#`P,#`P,#`P0$!`0$!`0$`0("`@("`@,#`P,# +M`P,#`P,#`P,#!`0$!`0$!`0$!`0$!`0$!`0$!`0$!`0$````H#,``.`U```@ +M,```("\``*`O``"T-```Z"X``-PT``!<-0``.@$"`=4`WP#:`*(`=0!_`(H% +M*@,Y`:@!B@7*`MD`2`$!`P\'"A0W;FH!&@'9`.@`"@&Z`'D`B`#*`4H!X@#Y +M`,H!Z@""`)D`=-%%%^BBBRX`!0(`'!FF0!@_\P`\- +MTB`-"[1`"PW2(`T+M$`+B9W8"0B,P`B)G=@)"(S`"`=^X`<'?N`'P2PI!PJH +M@`H(C,`(!WB`!PB,P`@'>(`'!FF0!K"RU04&:9`&L++5!0540`4%5$`%UAW& +M!`$'#Q\_?___9N8```4&`0(#!```5`!4`&P`8`!<`%0`C`!X``T/!0<)"P$# +M*``H`#0`,``L`"P`1``\`"P`+``\`#0`,``L`%0`1`!5554!2V@O`555507C +M.(X#JJJJ`G$```` +M```$```!>``#A!X``0````,```%Z``.&'@`"````!````7H`!(@>``(````# +M```!>P`$C!\```````0```%\``21'P`!0````P```7X`!)4?``,````$```! +M?P`%EQ\``L````,```&```69(```0````P```8$`!9T@``%````#```!@@`% +MGR```<````,```&#``6A(``#````!````8,`!:4A``!````#```!A0`%$(L! +M```````0BP$``````!"+`0``````$(L!```````0BP$``````!"+`0`````` +M$(L!```````0BP$``````'2$`0`8````/(8!`"````!`C`$`%````#R-`0`4 +M````=(H!``X```!`B0$`#@```$2*`0`4````1(H!`!0```!`(T`E(2$A(4!` +M0$!`!00$`0%`0$!`!05`0`P,0`T,#`$!`05`0`4%``0`!$!```1`0$`%0$!` +M0$`%0$!`!04%`0$!`4`%!04!!0$!0`4%!4`%0`4%!04%``````````!D```` +M`)`!``H```"=BM>9-FNQ%CYT?0(F'^A7OZ[+)C@O[[$%GL_U? +MZD6_(_=3EN1;F\)U'.&N/6I,6FQ!?@+U3X-<:/11--$(^9/B5/%NW%AM>:56:4$<^*$.D&!('^\*!$>+HEXTOSHOY=P("*!:T_ +MO"%(<`3QWV/!=W6O8T(P(!KE#OUMOTR!%!@U)B_#X;ZB-JS(Y[HK,I7FH,"8&=&>?Z-F1'Y4JSN#"\J,*:?BO!T6=JT[VU9D +M3G0>%-N2"@QL2.2X79]NO>]#IL2H.:0Q-].+\C+50XM9;K?:C`%DL=* +MRWO\J-9M.BP``0($!`````0,#`@$#`0$0````(```````0````(``$`````` +M!```0````$``````\&$```$!`@$"`@,````````````````````````````` +M`````````0`````````````````````````````````````````````````` +M``````````````````````!D````*@````X```````$!`````````````0$` +M`````@`!``("`P,#3`,"`%@#`@!D`P(`<`,"`'@#`@"``P(``0$``0(!`0$` +M`````0````````````````````$``````````0````$``````````0`````` +M```!``````````$```````````````$``````````````/____\````````` +M`````````````````````````````````````````````````(`-````(``` +M@`T``(`-````(```@`T````&````!``````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M`````````````````````````````````````!````#T+P(`(""`#P``0`!I +M(```:2!``&D@``!I($``(""`#P``Z`!I(```:2!``&D@``!I($``(""`#P`` +M..AI(```:2!``&D@``!*(```2B$``$HB``!*(P``2B0``$HE``!*)@``2B<` +M`$H@`!!*(0`02B(`$$HC`!!*)``02B4`$$HF`!!*)P`02B``($HA`"!*(@`@ +M2B,`($HD`"!*)0`@2B8`($HG`"!*(``P2B$`,`HD@#^!``!`02R<,$`LG#!" +M)!PT"B*`/X``(%4*(P`W<@O`!DHF`'!I($``2B8`<$HF`'!*)@!P2B8`<``6 +M`'"``)`;0'@@($"'`````````````/P`3<.-TU\.!X +M!-PTW3/PX'@$W##=,?#@>`3<+-TO\.!X!-PHW2WPX'@$W"3=*_#@>`3<(-TI +M\.!X!-P`3<%-TC\.!X!-P0W2'PX'@$W`S='_#@>`3< +M"-T<\.!X!-P$W1GP-!0:,#`4&3`L%!@P*!07,"04%C`@%!4P'!04,!@4$S`4 +M%!(P$!01,`P4$#`"QP'&L"1-,[`D'S/@?N!XX'C@>.!XX'C@>`HD@/`%($0` +MX"#!!T0D_H!!*L0`A``"`"\D`O%"(0$!0B`#`>@@H@0$$00"!!$%`@01!@($ +M$0<"!!L(`00;2`$$&X@!!!O(`2P`)0!$(CZ!/``B`$0B_(!`(<$`X"#!!T`C +MPP"H((`!`1&$`@$;"@$@(,`'!!$$`@01!0($&P@!U`?A_P0;2`%$(OR`!!$$ +M`LD'[_\$&P@!0B%!`$(@0P"H((`!`1&$`@$;"@$@(,`'SW&@`*PO&(&:N!BA +M706@$`78X'C/<:``K"\8@;.XNK@8H4D%H!!DV`HB0(``V>X``0`O)@#P2B9` +M`$X`!@!/`"``BB7_#^!X"B)`@`#9S@`!`&P`)``O)@#P7``%`"L(-0A*)D`` +M"'$`V`(AOH#@(,4'0GD!X`(AOH#@(,4'0GGK!^__`>`O+0$`0"5%``(F?/$` +M`"```"A``>@@8@,O(`"`+R%+``(AOH#`((8!PB&&`.!^$0`@`$H@`!!*($`0 +M#B)"`"\@"Q+.($6`BB7_#P@`!0`O+0$`0"5%``(F?/$``"```"A``4HF0`#H +M("(#+R``@"\A2P`"(;Z`P""&`<(AA@!*)@``0B#^D,X@@@%$('Z0SB&"`>!^ +M"0```.!X"B8`\(H@OP_*(&0`X'\O(`,`X'^*(/\/_!R(L?P<2+'\'`BQX@*)"+A'>$.) +M!WH`AN*&!R"'`$6)!(D0NABX1WA&B0BZ1WA'B0=Z`8:CA@<@B``(B4F)&+@0 +MN@=Z"HD(N`=Z"XE'>$V)!W\,B1"Z&+A'>$Z)"+HOB4=X)W@'?4HC0"%>\/8+ +MH`4`V0AWZG#N"Z`%`=D'?^APX@N@!0+9!W\)<-H+H`4#V2"&YW@'(0<`02L` +M!L8+H`4`V0AS*7"^"Z`%`=D'>TEPL@N@!0+9!WMI<*H+H`4#V2&&9W@'(0@` +M02X`)I8+H`4`V0AS"G"."Z`%`=D'>RIP@@N@!0+9!WN(<'H+H`4#V>*&9W@' +M?T$M`!9J"Z`%`-D(T$O`!(/>!X+H`4"V0=[+R`' +M`A(+H`4#V22&9W@'(04`02@`%@(+H`4`V0AS02\`!`]X\@J@!0'9!WM!+0`2 +M#WCF"J`%`MD'>^]XV@J@!0/99WAEA@=[02\`%LH*H`4`V9AP02@`%`]XO@J@ +M!0'9!R0$`$$O``(/>*X*H`4"V0*(*H`4#V2:&!R```0'(*H`4"V0=]+R#'`68*H`4# +MV:=XIX9"(U.@02T)!$$K$`0'?2#F02T7%$$N`"(O(,<002T*$B\CAQ5!+0$" +M+R1'`T$N%"1!*P("+R5'(2\GQR4O)P<`+R%'$B\BAQ(O(``=_Z'#>":`% +M!-G_V0BY)'@'?PEPS@F@!039#W@@AN=X)WA!*`$&`!I"($$H`00!&D(@02@! +M`@,:`B`"&D(@02L`!IX)H`4$V00@@P\`_P``*7".":`%!-G_V1"Y)'@'>TEP +M?@F@!039_]D(N21X!WMI<&X)H`4$V0]X(89G>"=X02@!!@0:0B!!*`$$!1I" +M($$H`0('&@(@!AI"($$N`"9"":`%!-D$((,/`/\```IP,@F@!039_]D0N21X +M!WLJ<"()H`4$V?_9"+DD>`=[B'`2":`%!-D/>"*&9W@G>$$H`08(&D(@02@! +M!`D:0B!!*`$""QH"(`H:0B!!+0`6X@B@!039!""##P#_``"*<-((H`4$V?_9 +M$+DD>`=[R'#""*`%!-G_V0BY)'@'>ZIPL@B@!039#W@CAF=X)WA!*`$&#!I" +M($$H`00-&D(@02@!`@X:0B"!`>__#QH"(.!X\&((H`4$V0]X'@B@!0'9 +M!WX`A2BX#WA*"*`%!-D/>`8(H`4"V0=^`(TZ"*`%!-D/>/8/8`4#V<=X`*4! +MA3BX(@B@!039#WC>#V`%`-D(=@&%,+@/>`X(H`4$V0]XR@]@!0'9!WX!A2BX +M#WCV#V`%!-D/>+(/8`4"V0=^!(WF#V`%!-D/>*(/8`4#V<=X`:4"A3BXS@]@ +M!039#WB*#V`%`-D(=@*%,+@/>+H/8`4$V0]X=@]@!0'9!WX"A2BX#WBB#V`% +M!-D/>%X/8`4"V0=^"(V2#V`%!-D/>$X/8`4#V<=X`J4#A3BX>@]@!039#W@V +M#V`%`-D(=@.%,+@/>&8/8`4$V0]X(@]@!0'9!WX#A2BX#WA.#V`%!-D/>`H/ +M8`4"V0=^#(T^#V`%!-D/>/H.8`4#V6&[@./'>*X&[?\#I1D`S__QP*X/C_]A +MB4")$+L8NF=Z8HD(NV=Z8XD`WV=Z0*!$B66)&+H0NT=[1HD(ND=[1XEG>D&@ +M2(EIB1BZ$+M'>TJ)"+I'>TN)9WI"H&V)3(D0NQBZ9WINB0B[+XEG>@K=)WI# +MH,.`"'-!+@`4#WBR#F`%!-D@@P0@@`\`_P``!R$$`$$N`!(/>)H.8`4$V00@ +M@`__````!R0$`,]XA@Y@!039!""`#P```/\')`0`02X`%FX.8`4$V0]XSW&` +M`-A4\"'!`P<@``$G>"&#!*,G>"*#!:,G>".#!J-AO2=X!Z-`(P`$@PUUD`'G +M+0>/_^'%"'41\.!XX'C@>.!XX'C@>.!XX'C@>.!XX'C@>.!XX'C@>.!X8;V, +M)?^?[?7@?\'%X'CQP.'%SW"``$P=38#/=8``](X@A;>ZN+H$(8$/`P````>Y +M17DMH/8*8!``V`"%SW&``-#)42"`@DR)SW"!``@.,FHV><=Q@0#("F"!5GA! +M@`7RE;M@H:NZ!/"UNV"AB[I!H`N-H[BA!J__"ZVBP?'`'@Z/_T7!SW6``$P= +M)X45"$$`,)44%`XQ"0Y!$%D=@A#0%0$6'0A!`,]Q@`!8(#R1%!0-,0T-01#/ +M<8``L"!9J8OJSW6``-0&P8V`Y@#9RB!!`"3R(:T+"I$#`=@>\$$H#0('?4$H +M`02G><]W@`#4!J"/4R5%$1\-,@3&N0HAP`_KM`!E"`0`;0@''\?'`2@V/_PC(SW*@`,@?#AH8@`G(#QH8@`K($!H8 +M@`L2`38"R"1X$1H8@`S(SW&``+0Q+1H8@`"!`>``H<.X5PA1`PO(?]D*N21X +M+R@!`$X@@@<`V`\@@``$(0&`0B*-`AGR"R-`P!?TSW"@`(@@\"!0`\]V@`", +M+@"&SW>``)`N#0T!$`"'$G`@#0$&H*8`'P`4'06/_^!XSW&``)Q4X'\(8>!X +M\<#&"4`%SW&``!P7\"$``$!X@-G/<*``T!LPH-'`X'Z`X>":`.)!T`%,]P@`"@-Y()@`[/ +M<(``2#B&"8`.SW"``&0X?@F`#L]W@``DEFV/@..>\DR/4',X`0P`SW"``,`; +M!(`@@,]P@`!$C$"@8:`BH,]P@0`P$PB0*0L"`,]P@0`P$\]Q@``\QVBP`=[/ +M<(``),?,H2.`#0GE``$8`@1CH!"/@.#*(&(``Z41CQ/HD^K/<(``3!T#@`F` +M&PB>`+X(8`('V`'8`:7/<*``+"`0@`"ESW"``!0I`(`="%$`SW"!`#`3SW$` +M`!`G"@VO_P6`$'@#\`#8SW&``,S&![$#A0P9!`1C"%$``(&"X,P@XH`#]`'8 +M`*%,%8`03PA1`,]PH``L(-"`SW`!`%`60,`!V$'`"!P`-!'80\``V(RX1,`` +MV!#9!-H(X*H`38<$L5@!`!X`]X2QT"$`R/A>@!A8#@ +MP`O!!,]P@`#T-T8(@`X!V<]P@``D%""@]@\@`@;8*0.O_Z7`X'BBP?'`O@JO +M_YAR1<%!*`$"!WE!*`($)WK&NL]U@0!("DEE764G"=\!%!0.,<]S@0"(#6AR +M-GK@@@L(P0/BDA,/@!,GBJ=J[PG>@0#8)_#&BH;N@-_/<(``U`;AJ,]W@`"@ +M'06/"PX!$(#8!:\)\,]W@`"P(!F/"PX!$(#8&:_&BC9[`!R``P>*A[D`K<]P +M@`#4!D"((*@!V$>K#-R/`H__H<'QP`,2`C?7<@```$`!VL(BB@`7NL=R``X` +M`(.Z['-`H^QR`*(^#"`$*'#1P.!_H<#@>*7@+?(1]F,(D`B$X"7R!_8OZ&<( +MT0#@?P#8-PA0`5L($0C@?PO8C"!#AQORS.`;\JW@$?(']C<(D`D_"!$*X'\* +MV#,($`PS"%$/X'\#V.!_`=C@?P+8X'\$V.!_!=C@?P;8X'\'V.!_"-C@?PG8 +MX'\,V.!_#=@.V.!^X'CQP.'%SW6``#0GJ7!`)8$;R@G@#2[:`=C1`:__81T" +M$/'`2@F/_Q\(M``(=0HAP`_KV5Z4QF8@`V05!D8 +M@`;P4QF8@U09F(-$+;X;)W<.EU89&(`/EU@9&(`0EU49&(`1EU<9&(`2EUH9 +M&(`3EUP9&(`4EUD9&(`5EUL9&(!""V`'*G!5`(__AN#QP`#8#_3/<(``0)9F +M"^__!MG/<8``M)D`@8*X`*$!V-'`X'[@>/'`@^#AQ0#8&/3/=8``))9`)0`5 +M-@OO_P/9SW"``$P=`X`4D#6-$PD``,]Q@`!$R1^!C;@?H0'8$0"/__'`@>#A +MQ0#8"?3/<(``.Y8!W?X*[_^I<:EP]0=/_^!X\<"6X.'%`-B,]\]U@`#TCJEP +MW@KO_P39"XV#N`NM`=C-!T__\<":X.'%`-B,]\]U@`#TC@1MN@KO_P39"XV" +MN`NM`=BI!T__\<`R#T__H\'/=H``7*TFCD`F#1("N31YCN`]90#8)?2+<(8* +M[_\,V0(4@#"!4QP`=BDP-'`X'Z!X`#9RB!" +M`!3TSW"@`%`,)8#/#Y"\F!_#/<9\`N/\0KAB!SR#B!]`@X0<8H1B! +MGK@8H1B!OK@8H0'8G05/_^!XX<3\',B^_!Q(ON'`X<'APN'#_!P(L?P<2+'\ +M'(BQ_!S(L?P<"++\'$BR_!R(LOP(P@7((!V.!_PB`+`/'`7@QO_THD0`#/=8``3!T5)0,0`(-`)0X5T7#")`(! +M\"4-$<@5!19$);Z!"?(*(<`/ZW*.V(VX<07@`'3;R!`-!J5YR!A8`*"#!ME& +M><@5`!8D>,@=&!``@\@0``:&('^.I`X!$&4$3__@>/'`[@MO_X#:"',]Q16`<@C@$`@<=W("3;<`(@@`^5*(A; +MV&`;8Q=K.;ME>`F)$?@=^`B>/'T(^$C'^9MUE +M0"V.%2J]Y('%?0`PB!Q7T' +M(XX`766D?F=^QW>`:=B8_F;88-=HZ8$YN,5X!R).`[A@!'Y'?@(GCQ^[=%$( +M_F;;8T`K#@,TN^J!Q7L')0X0&V-D?J=^`B>/'P``3Z3^9MIB0"I.!"^ZZX'% +M>@`/'^$)GMK^ +M9MA@U6@[N`5^YH%>9@$=X`B>/'[\_P$SX8'A@0"A#`C>X!7OK@=MC +M!R.``T1XQWC'=UXF45KX8+A@0"B-`S*X!7W@@7UE!R-``\1X9W@")X\?2196 +M./A@6&!`*`(%++A%>.6!N&`')0(09'JG>@(GCQ_0*:/O^F+:8M5J.[K%>NJ! +M&F('((X`I'X'?L=W1`)3%/YFVV-`*TX"-[ME?EYF!R:#$`1[!R./``(E@P]> +M)W\9^V-]94`M@Q,RO65]Y('=90\=[`B>/'RP8.`3[8WA@0"@#!2RX +M!7OI@;MC!R7`$,1XIWC'=^$AYLWX8%A@56@[N`5Z>F('(X``I'@X$0<`!R#/ +M``(G@`_(/"KX^X$`H3@(WN,5XXX%88`<@C@!D?D=^`B>/'RH+>?+^9MUE +M0"V.$^B!,KVE?AYF!R"-`T1]!WW'=UI%[13]9;MC0"L-!2R[97WM@=UE!R9# +M$P1[QWL")X\?'%;[%OMC>F)U:N*!.[IE>KIB!R6#$,1[IWL")X\?$`,(7/MC +M>&!`*$,"-[@%>^>!6V,'(X``I'A'>,=W;V?9`OA@V&!`*(X#,K@%?NR!?F8' +M(X`#1'AG>`(GCQ_5!-;I%?=UE!R6"$P=Z`B>/'T0)H+1?9_MC0"L"!.J! +M,+M%>[MC!R7"$,=Z`B>/'T!!D$-?9_A@0"C"!2FX[8$%>GIB!R.``*=XQW>; +M*,9^^X-1H/+C@@05^7F8'(H`#9W@")X\?7A4&V/A@N&!`*,T"-;CC@05] +MW64')8`31W@")X\?$"M[S_A@>&!`*`,$YH$PN&5XN&`')0,0QWO'=X@$!1W[ +M8WIB0"K#!>F!*;I%>QMC!R#"`*=Z`B>/'RLFQR_Z8MIBU&KL@3RZQ7IZ8@&!`*,,%X($IN&5XLGO88`5[ +MQWL")X\?U@N\W?MC>F)V:N>!.KI%>QMCTGIE>@=ZQWGUEI7IG>@(GCQ]K5%G<^F+:8D`JS@/E@3&ZQ7IR?KIB17ZG?@(G +MCQ]L`\=?_F;88$`H3@4KN.R!Q7A88+)^!7Y'?L=W6V7#6?YFVV/6:SJ[XX%E +M?E)['F;%>P=[`B>/'_-P;C/[8[MC0"N-`NJ!-KME?1)[W66E>\=[`B>/'Q`` +M@PO[8WIB0"K#`^&!,;IE>M)[NF)%>Z=[`B>/'WMZ+Z)_9_A@0"A#!2NXZ($% +M>[)X6V-E>$=XQW>H;T]^^X-9H.K@%?E)X?F;%>&=X`B6/#],!(!D?9_UE +M0"V`$N:!-KVE>-A@/'_Y<[+R_9_IB0"K-`^V!,;JE>M)]&F)% +M?0=]QW<(3J$1OV?[8^2!0"M-!2N[97T2>UUEI7M'>P(GCQ^L"'Z!^V-^9NN! +M=FXZOL5[4GZ[8V5^IWX")X\?Q4++#=]G^&!`*(X"-KCB@05^LG@I@7YFQ7AG +M>,=WURJ[TA]G^F)`*L`#,;H%>G)XVF)%>`(A@0]Y%&\LQW@98;EA0"E`!2NY +M!7D`%`T`66&[8P`6$$'$``"!0!`%EA"!Q````F@0,-!"__#!Q` +M`.!X\<"2"P__"'9(=42`*'<6(D$#)*`+":4`!8`!X`6F02U!%SA@02K!`%,A +M08$%ID`F$!8?\D#<#B$1`^EP/0UD%`)QP@M@#2IRR7#Z#Z__"G$B=P(E310- +M\*H+8`U`VLEPX@^O_T`F`19`YX(E`1#`Y>EP0"8!%G+WB@M@#:ER=0,/_^!X +M\<#/<(``M",`@!OHSW"``$0D`("9Z$(*``V)Z`O(!2"`#P```#P+&A@P,@H` +M#8GH"\@%((`/````U`L:&#`+R)"X"QH8,"X+@`71P.!^X'CQP,8*+_]G>L]U +M@``4!Z"%%25-$<"%1'EG>4,HP`9#+@\2!">/'P#_`/]#+@X6!":.'_\`_P#E +M?L"EV6''<8):F7GM`B__.&#QP'X*+_^8<$`E30//=H``%`?`AL.]\"9/$T`E +M#0+#O?`F31/G?4`ECP##O_`FSQ-3)<``%7X`AN=]9WI$>:=X0RC`!P"F9WD9 +M8<=Q@EJ9>4,LP`:1`B__.&#@>/'`(@HO_YAP0"5-`\]V@``4!\"&P[U`)0\" +MP[_P)L\3\"9-$^=]0"6/`,._\";/$U,EP``5?@"&YWU'>6=Y!WU#+<`7`*89 +M8<=QV6ZAZT,LP`8Y`B__.&#QP,H)+_^8<$`E30//=H``%`?`AL.]0"4/`L._ +M\"9-$_`FSQ-3)<``IW]`)8T`P[WP)DT3%7X`AN=]IW@%(DT`1'FD>T,HP`=E +M>1EA`*8"(8$/Y'`D0T,LP`;9`2__.&#QP&H)+_^8<$`E30//=H``%`?`AL.] +M0"4/`L._\";/$_`F31/G?4`ECP##O_`FSQ-3)<``%7X`AN=]1WEG>0=]0RW` +M%P"F&6$"(8$/G34J/D,LP`9]`2__.&#@>.'%`=O/.!X +MX'C@>.!XX'C@>.!XX'C@>.!XX'C@>.!XX'C@>.!XX'C@>.!XX'C@>.!XX'C@ +M>.!XX'C@>.!XX'@&N$4@S0#/<*``[">FH`J``-L`L7ZRX'_!Q>!X\<"2"X`" +MQ@Z`#WH,@`^`V<]PH`#0&S"@T<#@?O'`?@@/_QIP`=\`$!(!$_!:=1'P%2#` +M(Z"0`A`1`0'GUW4``/O_\']T]AD*@"\``/__SW```/O_W0B!A)T`#__/=H`` +MN!L`A@'@`*85"%$``=G/<*``R!PQH`X/H`\H<`:]@;U`*0`DI7C/<:``["<& +MH0"&0B!`@`"FW?7/<:``R!P`V!&AU_'QP,]P@`#P)0"`@>#*(<(/RB+"!\H@ +M@@\``*\3RB."#P``\P'*)"(`*`&B`,HE`@$:"```T<#@?O'`@@T`#[X,P`S1 +MP.!^X'CQP)X/S_[/<(``3!T#@,]S#P``_"B`SW"``*"PP+DV>$2`((`*NF1Z +MR;DE>L]QIP`42$VA18`!@`JZR;AD>D5X#J'/<8``<.8.B88@_P%;:,]P@``X +MB4RH3XDPB4`@$P.&(O\!0[J&(?\!3:A#N:(/H`8NJ-IPSW"``+@;`(`!X,]Q +M@`"X&P"A%0A1``'9SW"@`,@<,:`"#J`/*'#/<0@`AQ#/<*``["XC+C/<:``["<&H4T,$"`M#%$@ +MBB7$!HHFA`@B\`HAP`_K0`C30''<8``W+!"D;!]!KV!O5QZ$+I%?<]RH`#L)Z:B0I'` +MNGAZY7I0?T.1`".-`;!]!KU<>H&]$+JE>L]VH`#L)T:F(Y'`N7AY)7@0>&SQ +M0B)`((#@O@;M_T`A02#/<0@`AA#/<*``["`;PAB!_#X+@`=C`>,]R@`"`!R""86F,Z,]P +M@`#,!0&(ANA@HA8-X`5(V`/P8*+/L]6$$ +M[_ZCP/'`X<4(=03P:@G`#B'!#KE%>25X(,$5"5$`!Q2!,"+"!KD(ND5Y)7C@?Z/` +MH\'AQ4+!"12!,$/"0<`9"3,!`-@1"5(`"A2!,`D)4@`'"1(!`=@'%((P!A2# +M,!$+@``BP3!SS")"@`/T`=@AQ2$-41`*%($P(\,9"<,`"Q2",%!QS".J@(3V +M@.+*(&D`&PA1`(HAR0_/<(``5`8@H('E_]G*(2(`(:#!Q>!_H\#QP,X*S_Z^ +MP3IQ6G*(=@H@0"'H=9?'Z7!B":``T-D#V&'`!1R"-`L*$2`)W3[P)0I0(%,* +MD"`*(<`/ZW+/<```L0W+VPHD@`3A`V``2B4``)04`#$&'((S0"B!(0L<0C,' +M'`(P2+ABP$`I@""!N"5X$'D)'`(P2+D*'$(P2+UCQ=7QG!2`,$`I`2,+N`5Y +M,'A(N`<<`C"8%``Q!AQ",`;=8L!(N`D<`C!V%($PB,;)<+EA,'EJ":```-I$ +MQT7&@/'`X<6CP0#=0,5!PD+#`MJI@`D0#!`J`'ASW:``'P\J7#)<03PJ7`B;FX) +M8``!V@6%AB#X`8P@!X#V]8MVJ7#)<58)8``(VDHD`'4`V*@@P`-!*($`\"5! +M$%,@0P!.(\(``[I9>1QG(*P!X*EP`-DR"Z``!-K)<`#9)@N@``C:K0'O_J+` +MSW%%9P$C(*#/<_^&7#/<(``%`<@H``0#A`$$`\0"!`'$`P0!A#)AR +MR',>#J__2B4````@#0%#+XD0J7#)<2ERZ',*)(`!`@ZO_THE0`#`<-AP0RZ* +M$*EQ27(IAR27,*)$`"S@VO_THE +MP```($\"0RZ)`.EPR7$I#:__2B7``4%PV'!#+8H0R7%)AR27,*)$`"U@RO_THE``,`($T"0RZ)`*EPR7$IAR27,*)$`"A@RO_THE0`4` +M($T"0RZ)`*EPZ7$IAR27,*)$`"C@NO_THE@`<`($T"0RZ)`*EPR7$IAR27,*)$`"F@JO_THEP`D`($T" +M0RZ)`*EPZ7$IAR27,*)$`"^@FO_THE``P`($T"0RZ)`*EPR7$IAR27,*)$`"!@FO_THE0`X`($T"0RZ) +M`*EPZ7$IAR +M27,*)$`";@BO_XHE@0``($T"0RZ)`*EPR7$I8/;_^*)<$!`"#.`4,MAQ#)<,AQZ'))@]O_XHEP0(`($T"0RZ&`*EP +MZ7'(`&ID$O01``,A0`_``%040`85`+0$")7@@A@<(0```IO`G0!%`>'/HU0=/_@HA +MP`_K'%0L%!*`("!WI!*`$$1WG/.!^X'@(R)6X"!H8,`G(F[@)&A@P"\B*N(VXD+@+&A@PX'[@ +M>/'`X<4(=3Z(SW"``/@20(!`)0`4`[DU>5EAX@^@#`K:O@_O_ZEP,0=/_N!X +M\<"EP4'`0L$,'``Q$!Q`,<]Q@`"PAS09P`\P&0`/+!G`#B@9@`XD&4`.SW"` +M`+"'(!A`"\]P@`"PAQP8``O/<(``L(<8&,`*SW"``+"'%!B`"L]P@`"PAQ`8 +MP`C/<(``L(<,&(`(SW"``+"'"!A`",]Q@``TAX`9``A\&<`'>!F`!W090`=P +M&0`';!D`!V@9@`9D&4`&8!D`!EP9P`58&8`%5!E`!5`9``5,&<`$2!F`!$09 +M0`1`&0`$[Z'.H:VAC*$L&<`"*!F``B090`(@&0`"'!G``1@9@`$4&4`!$!D` +M`6.A:B```]@9``!J(,`"U!D``&H@@`+0&0``:B!``<@9``!J(``!Q!D``&H@ +MP`#`&0``:B"``+P9``!J($``N!D``&H@``"T&0``:B"``B:P,+DGL!F%\+@9A07RCK@9I0?P +MBK@9I07P0@J@!@#8*07`#.!X`-M@J"&H0JC@?P/@\<#6#$_^SW.``+@CSW6` +M`'P[0(4,Z0"CE.K>"2`!#]@Z#:`&"-@!V`"E#/``WL"C".JB""`!#]@&#:`& +M"-C`I0$%3_[QP(X,3_[/<(``R`4`@,]V@`#P#J"&SW>``.P.!"""#P\``.`$ +M(H$/`0```')I1'MG?:"F!"".#P```$"8=:"'`[Y$?@0@@`\```"`QWV@IP0C +M`P$&(L\`Q'VF?SUY)7@"N`0B@@\"````!'H&)X`0+R@!`$X@0@0-&I@P#PJ0 +M`<]P@`#HQPZ0)^C/<(``1`8`B,]Q@`!,'?`A`0"_$0$&4R%!@!GTSW&``$A+ +M!+@!82,*D0'/<(``^,?T($``#>C/`T1HZ[F*(,,O!/0>%I`0#8Y1(`"`HO)Y"=\` +M*PO>`O_8!ZU*)`!Q`-FH((`#*&(`(8,/@0"P$O9[!*LH8@'A+WD`JUOP(PD2 +M(0HAP`_K-,B)"!``A@2^!`+`2 +M]GD(\D2I!-D`*4$$)7@'K3WP0*D/($`$8_`M"!(DC"##K\HAP@_*(L('RB"" +M#P``+B7*(X(/``#D`LHD8@#D`^+_RB4"!+(+[__)<`B6"PB>`P*.":T#\`&. +M"*T`A3$(W@(`VD>M2B0`<<]Q@0"P$J@@P`(X8O9X!!@"!``8`@0!XD]Z`8X( +MK0*.":TL\$PA`*'*(0GR!!D"!`39`"E!!"9X!ZW=\0`9`@0`V0\A000F>`>M`8X( +MK4D"3_[QP.X)3_[/\R["PL%`.T+ +M'L!1(P#`RB`B`!_T.0I1`,]UH`#0#Q`5`Y8I"%0`SW*``!`4GW`C@J@@P`(" +MBB45#Y;!N--HV'\!X`*JYWDCHA`=V)`!V.D!3_[@>,]P@``$K^!_!H#@>,]P +M@`#PKN!^X'[@>/'`8@EO_@;:"'4H=R"PSW:``$P=`X8(X+()H`PD;0.&0"6! +M$J8)H`P&V@.&0"4!%`C@F@F@#`;:`-@!M0RU1"1!0(V`*F0"8`%,(,X`.I<<]P@0#("@Z(-QY"$S4>0A.\M@2X +MAB#^`Q2NBB#_#PNF#]@V'@(04R*`H+VVS"`BH!#R&0C1``O8%:X*<$`G`16: +M"*`,$-H'\`L(D0`*V/;QL:]Q`$_^X'CQP!((;_X&VH(D`STZ$P>4`C`@8LLXGI +M#9,!X!!X#;.%Z`Z3`>`.LU@<1#``V%H<`C`@V%L<`C`!DG3=7!P$,`*27AP$ +M,);'$?`*(<`/ZW+/<```O"'NVYAS70#O_THE```(=0#>F,>/P$P@`*"*(08" +MRB&!#P``"`&.#<__5A2!,(MPN6$P>28.[__)XP+@3>%,@3@`"V%X/ +MX`;)<0T:F#//<:``%`3*H<]R@``8!P""%+X!X`]X`*+%>`NA`866"*`,/-D` +MA8X(H`PZB`*%A@B@#":5O@Z`!,T&#_[@>"AR,0`@``#9X<7AQ@#=@.+*)(EP +MZ"`I`JYAJF#">D]Z@^H!Y0#:2'#!QN!_P<7@>.'%X<9`*0T")7U`+0,4I7LE +M"C0""'53)7Z0!O(!'5(08;K[\4$JC@#!ND(F3I`$'=`0_?4)ZB\DB7#@>*@@ +M0`$!'5(0X'C!QN!_P<7QP-8-+_X&V`T2#C8!$A`V#1H8,,]UH``4!`JE"84' +M$@\V)^@#V!"E!*7/<($`P!,:#:`-`QH8,)+9`\B0N:`80`#N"6`#`-@)A0_H +M*!4$$"05!1`>V`HAP`_K">``%-C/<(``3!T`@,00``8EN'X*X`#`N*X-8`8$V)(/X`NI<-X, +M@`N>"H`+I04/_O'`X<6AP;D(]``(=3(+[_\`VJT($`#/WQ$]@Y\/T)E(//<(``3!T#@!B(\0A0@,]R@``H +MC$APR@]O_@;90"(``KX/;_X&V0R2@;@,LL'QS0D4@<]R@``HC$`B``6B#V_^ +M!-D,DH"X#+*S\;$)T8'/<(``"`>*#V_^!]G/<(``A"2N#R`/`("E\1S8=00O +M_J'`\<#AQ<]P@``HC`R0"P@>`.H-``,%\%$@0(!4#P(#SW"``/2."X@/"%`` +M@N!`"@$$`_!6"P`$SW6``!`'`)4Q")X`SW*``$3)3!*``,]Q@`"4R<&X]"$` +M`,]Q@`"4K0&I2!*```*IV@Q@!0'8`)51(`"!K`\"#P#8]0,O_@"UX'C@?N!X +M\<#AQ0#8SW6``,S&2B1`<22%J""``@#;#R,#``LAP(`#]`'@!?!F>38/8``D +MI02%@.!`#Z$`RB!A`K$##_[@>`AS.N]6Y#0GE`#:X`B-"``KPSW*!`#`3 +M18(!X,FX(GIZ8A:XX']%>.!X\<`&"R_^F'((=<]V@`#`C_0F0!#/=X``0(]1 +M($""RB!!`,HD(G3*("(`Z"`B`O0F`A`)"EX"`>!'"!4$+;O`N\]R@0`H"K1Z +M0"N%`F"2!+V&)?@3B;T/(T,`8+(`VA9_0*=!I\.YI7D%(4,!%'Y@ML]Q@`#@ +MCQ5Y`!D``0+P@-CM`@_^X'[@>/'`9@H/_H(D`S((=EIQ2'4Z`\<`C"I<(3!>@I@#`C: +MAL#)<=(,+_[)P!\+'D`9$@"&"B'`#^MR0]B,N,]S``!$%GT"K_^X +M=0\:6(.=`0_^X'CQP/8(#_ZKP<]P@0"P$P`0$P`'R`0@@`_Q``#P0,`-S`#> +MSW6@`,@?42!`@,]P@0"P$R&``\@/\J`5`A#X%0,08GD"(E<`=A`!`2\GR"59 +M803PA!`7`>)Q.AC$!1^%#PA%`#!X+@T@!0+9`=G/<*``U`,]S@0"P$V.#""#$`,]SH`#4!S6C`!I` +M!0(B`24OHP(D`0`[H_"CSW&``)0N#1("-@"!/0B``,]PH``X+@6`!""`#\`` +M```A"(`/P````/78!;C/I1#8#J4!V!4=&)`'R`0@@`\!``#P +M++@#$@,V!+$/@\ZI`*%`$P`!`K$0BV`3`P%`*`0!P[L%(P,!9K$/J2\C2`'/ +M<(``9,A`(`0)57A)@,]Q@`#HQUMC::"D%0`0^!4"$*!P0GA%P`'8SW*@`-0+ +M$*(#P#6XP+@7N``@@@\`#@``SW"!`+`3`H`"N"O@!""`#P``_/]%>.QR`*(! +M$@(V['!`H,]P@0"P$T*`['!`J`W(%"$"`%"*['!`J.QPP+`#R)00`@#L<$"@ +M#`2X17@"\(#8['(`J@/( +M.W90B#,0@``$N@5Z['!`J`/(&G9``-[5"-Z'+?``WOJXRB:"'P```0+YN,HF@A\```("_+C* +M)H(?```!`@KJSW.``*PS4(.*)@@2`>)0HX8,@`X1\`'9SW"``)`Y(*">"6`- +M*'#/<8``*#0-@8HF"!(!X`VA!2>/DPOR80(@``#>A!(``"$(E`R3"Q]`SW*@ +M`-0'#X(0>!D2`898X.<)!(`*\,]S@``P,R2#BB$0(0'A)*.)"9\@'AI8@QT2 +M`(8'&A@P'1(`ADK`'1(!A@3((*`=$@&&(:`=$@&&(J`=$@&&(Z`=$@&&)*!6 +M)0`2'AH8@!T2`89`+P(D,'@%()4`!!("-H8A\P\`$A(!C"$,@`&"0\`6\AK8 +M%?#/<($`L!,($`0``!`%``HAP`_K"P@1(`/(<_`#P!$(G@7/<:``2`A`(@`C!_!`(@`ASW&@`$P( +M1\$#<$C`!,$"P"5X!245(`C`!^#/<8$`L!,C@00@@`\``/S_""!6``PFP*0N +M`2T`2<`6#0``!2`,2`C<0ND5X['(`H@K`0"%9,`$: +M&#`$R`,2`C8H=D'%`QH8,`0:F#`A@`"0`<4TN<"Y-'@#X$#E!""`#P``_/\= +M90T2`38&\!4B0#`.$``&`GT5(D`P#A``!N\-!9`#S,]QGP"X_QBASW"@`/Q$ +M78`$(KZ/``8``%WT&0@0(`3(4(A3(L$`AB+^`T2ZQ!B"`#"HSW"@`!0$Q*`' +MR,]QH`!(+!VASW"!`+`3`H!`(%`@$G`@!$`/(*8@!X2FHSW&``#`S!H$! +MX`:A0/`*(<`/ZW(H%`4P/-B,N,]S```;%+$#;_]*)$```!0$,$?8"B'`#^MR +MC+C/.QQ*G2$)`*1`*%`(4TP%/+/<:``U`>`&<`$`\PJ +M.QR`*+,H0'8%!D8@&H)H`X!Y0,2`C:2$@`!!!(!-@T(GP*2$0,! +M;0N>`JJXDAH$`)(1``&JN%X.8`B2&00`$-G/<*``T`\0&%B`)!`!AL]R@0#` +M%T62,'D"ND5Y#!A8@!39$!A8@,]Q@0#`%V>11I$8V1"[97H,&)B`$!A8@,]Q +M@0#`%VF12)$0NV5Z#!B8@`;PSW"!`,`7RJC/$AX/K_\!P`?P`]@3'1B0%!V8 +MDRD($"#/<*``+"`P@`7`,'`!V`#4('D// +M<8``3!TC@<]P@`!PYA"($+@R(8$/``#8`I^X@.$!V,]QH`#\1`VA +M&PL0(,]PH`#T!V`8P`3/<8``,#,#@0'@`Z'/<(``2+`DD)3AP"&&#P``DP#/ +M<*``:"SP($``SW&``*PY(($`VL]VH`#4!R5XSW&@`-0+#:%,IHH.(`T&P!D6 +M`);`X*``#@`-S)D(7@`#W2`>6),!V!0>&)`$$@$V`!8$0`<:&#$`%@5``1I8 +M,03*G.#*(L('RB""#P``W`[*(X(/``#T"N``8O_*(<(/*'"6#>`-#MD/%@"6 +M!!(!-K09!``3'EB3$(E3(,(`AB#^`T2XQ!D"`%"ISW`2(```0@T@`PT2`C8$ +MR,]QH``L(+`0``$O@63@,'#*((4/$B@(`(7WSW``*`@`!AH8,`#>#<]P@0"P +M$\"Q(H`&DAEA,'DFLJW8SW(`NP"['@E@!@6X`\@:D)H/8`8-$@$VZ0:O_:O` +M\<#AQ4\(7D//<($`L!,!@,]QH`#('Y8@00\>H1#8#J$!V!49&(!:#6`.0=@G +M"%Y#`=G/<(``D#D@H`X+(`T!V,]Q@``H-`V!`>`-H8HE"!(M\,]QH`#\1!V! +M.8$$(8*/````"`#=!_0$(+Z/``8``!CR`-WZN,HE@A\```$"^;C*)8(?```" +M`@GJSW.``*PS4(.*)0@2`>)0HX8-0`X'\`/9SW"@`!0$):"9!J_]J7#QP!H. +MC_T(=<]V@`"P#`".J,'/"!$`BW?I<,]Q@`!(/KX.K_T@V@'8`*X`V(^X"QH< +M,`#8%1H",,]V@````*"V#PV!'P``_LH'P("X1\#/<*``K"\:@%(@```3"!X` +M`9:`N`&V!\"!N$?`SW"``$!+H(A:">`#JJ[/<4-UJ!)`P8HA&@I!P2N.!*8- +M'$(S1L!CP<]Q@`!P-43!SW&``-PTSW6``-0%`(5%P8#@RB#!`\HA(0C*(F$` +MRB-A#R@-80O`*^$%`(6`X,H@(0)0#:$$RB%A``+9SW"``)@C)*"E!:_]J,#Q +MP#8-K_TO)`<`SW.``$P=*'>$Z"&#`_`@@\01`08EN?`C#0#`N8#GHJ.CH\PA +M(8`!VN<]PH`#\1"&@X'C! +MH-RE@.>P"6(`RB!B`&H*``2%Z&X-P`P#\*(-P`S/=8``A`T`C8;HZ@T`#`'8 +M`*VY!(_]\ES@VO_0X@@``( +MI<2U$(?%M0:EA02O_0B%X'CQP,]Q@`#,Q@`1!`"X<,]R@`#$*$`L@``5>!4@ +M0`$P(@8`&0X1`0HAP`_K_Q\<#/!T)(P`&L@2*#0A1 +M``6*@>`!V`/R`-B`X'@(`@#1P.!^SW"``$3)()!$(0`#8P@1`@#;SW*``,S& +M9:($@J"X!*(]"9\!!)+/<8``),/'`>@J/_>X/S__/<(``^,9@ +M@,]R@`#,QJB`8*+/=H``%"D$@JBB`-G`AJ"X):($HB,.41""X\PCXH`2],]P +M@`"<+".@!.V6#\__"O!*"@``!O`!VV"B***@N`2BB0*/_?'`&@JO_0#8SW&@ +M`"P@4('/=H``S,8DCL]U@`#XQ@BE"PE1`"6."0E0``'8HN@JAASI!H9^"Z_] +M#B"``,]Q```0)R4)!0#/<8$`,!,E@9DAS0H9"$4`!?#/<```$"<(I0+8!_`` +MV`?P"8;XZ`'8`*4!V!4"C_W@>,]R@`#,QB2"#R$!`"2BM08@``G8\<"*"8_] +MSW:@`"P@$(;/=8``S,8'I<]P@`"\-]8.8`P`WP`5!1`G#5``3"6`@,PEXH`\ +M\@HAP`_K`! +MV`/R`-@%Z`H.S_]!\,]P``"($PBE3@[/_SOP!)6:Z"65"(6!X<`@@0\``(@3 +M`_(;>,]Q@0`P$R6!"*69(@$C0T(40`%C8'@`=@#\@#8!>B:#<__!_#/<```B!,(I=X- +MS_\$E06UY+40AM4`K_T&I?'`SW&``$2,08'/<8$`,!,E@04IO@`P<,H@3@`, +M(0#PSW$``!`GW@FO_/'`X<4`V,]S@`#,Q@"C +MSW6@`"P@$(4!V<]R@`#XQ@:C$(4@H@:BSW"``"PL`X@DJXP@@X8DJ@3R):HE +MJ^X+(``#V&$`C_W@>`'9SW"``,S&X'\@H,]P@`!,'0.`SW&D`!Q`"(#`N!-X +MP;@2H>!^X'CAQ0#:2B0`=,]U@`!`C\]S@`"XCTAPJ"```T`C`0(4>4"Q%B4! +M$$"A0:$!X$HDP',`V:@@0`+/<($`*`HT>$"P`>'/<(``F`9!H,]P@``HC$RP +MX'_!Q>!X!?!"><=P0````,]R@0`P$T6"\PI$@%,@0P5P<<`@C0]`````P""- +M`.!_(G@&\&)Y`B"`#T````#/`5A+;W` +MO0/P_]WJ#<`#">C/<(``E"`(B(?@`M@"\@#8SW&``"26=XG/`1H07P$($!X!"AV@G/_<]V@``0!R"6$PE>`)H*@`X`EJ&X$'D` +MMA\)G@&N"H`.SW&``)BM"Y$!X!!X"[$`EJ:X`+8>#<`##^C/<(``E"`(B(C@ +MS"5AD`?T4@Y@#`'8(@S`!(PEPY]&\B,($"#/<8``#!0`@0OH`-@`H<]Q@`!$ +M)`"!HKCV":`(`*&:"P`,SW&!`#`3!H%%($`!!J'/=H``](X+CE$@P(`<#X+] +M"XY1(("`L`M"`SX)``.V#,`#@."T"B(`RB`B!@;M`(?$$``&(0A?`<]Q@``0 +MR02)"N@#B8#@?`WA"LH@X0"."B``%=C!!4_]X<7/<8``$!0`B0';8:DDZ,]P +MH`"P'WF@SW"``,`;"("C@6"``H$`VC$-`1#/<(``*!0`B(/H`=@*\`&!`B,- +M`/<-A9],`$!+0:E(<`<(40!AH4*IX'_!Q:*A[_&`X`'8PB`,`,]R@``0%`"J +M`=@!J@#8`JH!H@*B`Z+@?R2BX'CQP.'%"'43"#0$F'(.V.8*+_\`VH/H$]TL +M\,]R@``DEDAP-@CO_0S9SW&``!`4`(D.Z,]P@`!$R0"0AB#\`(P@`H`&]`62 +M9))G>`.A0B4`$QX*(`6(<0HE`)`,],]P@`!$R0"0AB#\`(P@`H`4#\'_Y01O +M_:EPX'CQP)APN'&;^RB'&#P#:SW&``/2(GKH5(4$!`($! +M*@(!1G@*#J`&`*'1P.!^G0?O_P79X'CQP.'%`-W/<(``](B:#2__'-D;V*8( +M(``%V4HD`'?/<8``,!2H(,`"%B%``P00!0"8=0\-01%`)$T`.01/_0HAP`_K +M_^4]O@>/'`SW"``/2(&!`%`"\L00%,)`"'RB+&!\H@A@\``.(. +MRB.&#P``JP#0!.;^RB'&#\]P@``P%!8@``$`@$!XT<#@?N!X\<#AQ<]P`P!` +M#<]UH`#('T4=&!"J#\__@-@5'1B0P0-/_>!X\<"8<+AQG.#*(L8'RB"&#P`` +MXP[*(X8/``!C`'0$YO[*(<8/3"6`@!X +MX'[@>.!^X'C@?N!XX'\!V.!^X'C@?N!XX'\`V.!^X'C@?N!XX'[@>.!^X'C@ +M?N!XSW&``+0R$H$!X!*A#/'`9@I/_9@0`@`$(H$/````"#MY!"*# +M#P```!`E>\]Q@`!,':2!5B5.%%8E#Q68$($`%0I>`H8A_P-$N2]GB;_I<1GP +M42(`@KP5`A$,\L*Y@"4"&3]EZ(\]93"-97_P?T5Y"?##N3QY/V8^9C".Z(]% +M>8@8P`-E>54";_V,&$``\<#AQ0/(I!`!`)@0`@!1(0"`&N""`"`-JL:/(,@`S/B!<]P@0"P$Z&`!/""$0T!#6"1!/""$`,!SW&``$P=)(&`$`T!5!$!`3UE +MNV.$$`T!NV.`$`T!N6%^$`T!0GTG\$,(D0`#$@TV#EQ#.!X\<"R#P_] +MSW&``$P=\"$"`%8B100(@E8B!`51(,"`BB`(`,H@(0"\&@0`2B0`<@#9J"!` +M#\]U@`",2_R*+F7D?B\H@0-.((,'SW"``'1-;V``)4,`X*M$$H\`Y'XO+H$3 +M3B:/%^Y@R*O(@B$.WA`=BH;ATR"F`"\H`0!.((T'SW"``.Q*J&`1\,]V@`"T +M2RYFSF6\BL1]6!*.`,1]+RU!$TXECA?(8!"K`>%*)`!R`-NH(,`/W(K/<8`` +M4$UO8<]U@`!T3>1^+RB!`TX@CP?O90`EP`#\J$02CP#D?B\N@1-.)H\7[F4D +M&((#R((?#MX0/8J`X],AH0`O*4$`3B&-!\]Q@`#L2JEA$/`$Z\EK`_!H=LYA +M/(K$>5@2C@#$>2\I00!.(8X'R64L&$(``>-*)`!Q`-BH(``%SW&``.A*?8H) +M80`D#``!X&1Y+RE!`$XA@P?/<8``[$II82"LO08/_>'%X<;/'&F'#/\5[>:)X@J1[!"%!@V5X +M&*+?]<'&X'_!Q>!X\<`B#0_].G`%@:"!RK@0N,J]!24-D`&!)H'*N,JY$+D% +M(1```=X9\@0E@),3\B\H`0!.((('\"&!(`#?#R>/$`CI!"<`%$(@`(!@>`1_!R>/DT''%O2Q#A$0^@\@!`K8I0@0``HAP`_K0S8!A0!,1CI'A0`,0L@0(`-\L]P@`!,)&"`SW$``&!< +M#=A@>P3:"/"(Z,]P@`!()""`8'D-V`LG@),%\C()[_\*V`?PA^X"".__"MA6 +M"```W*4(W%\$+_VCP/'`\@LO_0#:SW.``!`5.X.Z@P#>#R8.$*1Y!"9`$$(@ +M`(#*(&(`+R8'\`'=RB"!``?R'(,D>/(.[__%>*EP(00/_>!X\<#AQ:'!`=A` +MP,]U@``0%0J%&P@>`(MP!-EGVCW;6@O@"A>["H6@N`JE^0,O_:'`\/'`0@L/_:;!*'4:0#850,O_:;`X'CQ +MP.8*+_T!V:+!SW6``!`5&H5;A01Z'(4$()"`+?(#\#MY!"!`H/[S+R@!`$X@ +MD0=<'4`4%25-%!V%@.#*(<$/RB+!!\H@@0\``(\3RB.!#P``'`+*)`$$Y`.A +M_LHE000F",__'85`>-X/C_\`V`\@0`0&(!`@L@WO_PIPR0(O_:+`X'C@?N!X +M`=G/<(``;";@?SB@X'[@>/'`H@J``\]P`0"00`GHSW&``!`5N!D``!N!D;@; +MH<]P`0`(0`CHSW&``!`5'J$;@8&X&Z'/<```Q%X*Z,]Q@``0%909```;@8BX +M&Z'/<```R%X*Z,]Q@``0%9@9```;@8FX&Z'/<```U%X*Z,]Q@``0%9P9```; +M@8JX&Z'/<`$`X$@*Z,]Q@``0%=@9```;@9FX&Z'1P.!^\<#AQ:'!SW*``*"P +MSW6``!`5%X4`V0\A`0`8A21X0B``@,H@8@`!VP#9(PA1``C88,`!'$(P`AS" +M,`,4!YM,.%)$!Y[L/U)$J<,]RK=[OOIH)X`$0V3(/(``J[[Z&">`!*G`*#^__*G"#X,H@(@`1`0_]\<"R""_]`]JFP1IPQ@E@"X/! +M`\'/<(``O`X4%`[[Y"P`3""G"`VR()X`&8<\X)(``*<'T(T``#P\]P@`#<#D*%\"#!`,"E +M#!40$,&E".G/=X``Y`[P)\`0ANC`I<&E`-D9\(0J#`.2"&``+W`.(($/```` +M`2"E`\"$*`PC\"8PA!XW#]\&E`-A!`"_]IL#@>/'`V@_O_`3:IL'N"&`+BW'/<```&](` +MW:EQ^@M@`*ER`,'/<```'-+J"V``J7(`P<]P@``4#0'"%2!!``"1`L$%NK8, +M8`!%>0/`@.#:``4`SW:``&`'TM@(N!G9M@M@``#:SW```"+20"8!$O8)8``$ +MVL]P```CTD`F`1/F"6```-K/<```(-*$P=H)8```VH7'SW```"'2Z7'*"6`` +M`-H"AA?9.@_@"D`F`A(#AA?9+@_@"D`F`A,$P!?9(@_@"H3"!<`7V1H/X`KI +M<@*&`-ER#R``B[D"I@.&`-EF#R``B[D#I@3``-D(N%H/(`"+N0AW!<``V0BX +M2@\@`(NY(H8Q>1GA!2E^`".&+W)0=S%Y&>$%*7X`+W',($6`A?<#P`'E-PA% +M@P/`#PA%`P'9SW"``&`')*``V`D'[_RFP.!X\<">#N_\"=JIP0AVJ@\@"XMQ +MU@EO_2'`"'%"V)H+8``%N0P4!#``P6X*8`"I<@X- +MH`T%V"`4!#``P#>_\`MJGP9IP#@\@"X/!SW"``#@_`(`'V47`SW```!'2$@I@``#: +MSW```!+2`-D&"F```-K/<```$](`V?8)8```VL]P```4T@#9Z@E@``#:SW`` +M``%$!]G:"6```-K/<*``M`]P$!<``=G/<*``M`\\H&8,H`T%V+S8G@I@``#9 +MP]B6"F```-F*($0(B@I@``#9BB`$"H(*8```V27%M=AV"F``J7&*((0&;@I@ +M`*EQ`]A`P`3>0<;/=ZW>[[Y"QXIP!,$#PA[;F'-*)0``2B8``.(-H`%*)P`` +MB@[O_XIP@^#9\L]U@`!@!P@5%A`,%1,0#MA`P$'&0L>*<`3!`\(>VYAS2B4` +M`$HF``"F#:`!2B<``$X.[_^*<(/@N_((%1`0#!45$`[80,!!QD+'BG`$P0/" +MX=N8P"C`B%`)<]S@`#D#E5[`*/# +MVU5^8*;/=H``U`Y5?F"F(?0R"4`#"B'`#^MR$.C/<*``_$1T$`0`9!`%`,]P +M``"Q$UT%;_Z*(TD*SW```*T3BB.)"DHD``!)!6_^"B4``9SH\@A``PHAP`_K +MA(,(``O<`3"`B`!(,]P@`"\#E5X(*`")<`DN6`" +M(4&$$/(")4(D#'KJ"R``+W`$P@(E`2#/<(``Q`Y5>""@`-B1`^_\I\#QP(H- +M(```V,]P```-T@#9B@\@``#:SW````S2`-E^#R```-K/<```%=+/#R```-H"V(ZX`-D2 +M#R```-H!V(ZXSW$``/__`@\@``#:SW````O2`-GR#B```-K/<```#=(!V>8. +M(```VL]P```2T@#9U@X@``#:SW```!/2`-G*#B```-K/<```%-(`V;H.(``` +MV@#8T<#@?O'`>@K/_*/!BW$!W88+(`NI83@S"!B@=H.(``/V`#8)0+O_*/`\<#AQ:'!BW'""B`+`=K/=8`` +M+(L`%`0PSW"``+@,0"4!'Q+:;@T@``#;`!0$,,]P@`"T#*EQ`=I:#2```MO/ +M<(``W`PD;1S:7@T@``##`-C5`>_\H<#@>/'`/@GO_`/:H\&Z<&8*(`N+<0'! +MSW"``&0-`-_T($X``L'/<(``?`V`YO0@5`#/<(``8`?@H.&@S":BD,PF8I', +M)J*1RB7"$P+T`-V!YLPFXI#,)N*1S"8BD@/T`=V$YLPF8I+,)J*2S";BD@+T +M`MV&#<__JG#/[[Z2":`!R7%B#N__JG#M"-```,"`X,P@HH%0](#FS"9B +MD,PF(I%*]`+`D0@1`,]P@`"\#K5X6G#@H,]P@`#$#K5X>G#@H,]P@`#<#K5X +M&G#@H,]P@`#D#K5X.G#@H,]P@`#,#K5XX*#/<(``U`ZU>."@JG#)<<]SK=[O +MOAH)H`&I`RAJG"I<[[[."*`!BG-:#Z__JG`I +M"-```,#/<8``8`=`@02^!KC88!4@``7'<(``W+`A@4*P([``V!D`[_RCP.!X +M\<"DP8MQ\@@@"P3:`,`!P02X-7C/<8``'`T08=H,(``"P0#``<$$N#5XSW&` +M`#P-$&'&#"```\$`V*3`T<#@?O'`H<&&"N`!BW(`P*'`T<#@?N!XH<'AQ>'& +MN'#/<(``L+D0$`8`SW"``/`E!8"8<:'!AB3W#^<($`#/<(``_#D`@!\(@0'/ +M<(``!#H`@!,(00'/<(```#H`@,,(``$`'$`Q(,(!%($P\-Y3(L``Q'I3(<<` +M)'Y4>D`NC0&T?;IB%7K/<8``H+)(8=1^"'.&(_T/>WLZ8D&*97A(]UE%27-$;YAPHYE>LESAB/]#WM[N6$CB65^*'.&(_T/>WME>2<,$`#/=:H` +MX`=SA1$+'@!(I0FE*J7+I1#P"*5)I!_H<#QP,]Q +M`((!`,]PH`"L+SR@SW"```@P`("+Z,]P@``0%P"`#PB0`%H+``/1P.!^-@E` +M`)X+X`1OV(?H&@U@#0K8(@E``//Q\_'/.!_`*+@>`0H@`\``"^Z0BG"=%!Z1"K^`@(@0`X0>`/H`>)0>@L(,P%` +ML8/H`-@"\(#8X'[@>(D'3_[QP-(-C_PZ<,]U@`"X&P"%`>``I14(40`!V<]P +MH`#('#&@E@Q@#2APX@N@!`?8&G#/=J``["?KAC8+H`8J<`NF`(5"($"``*4& +M],]QH`#('`#8$:'""*`$"G#9!:_\Z7#QP&X-C_PZ<"AU&G*>"Z`$!]A:<`\( +MGB`^"V`'R-A0()`@3""`H!GR"/8C"!`@10A1(!78$[@-\"4($"0U"!$H=@R@ +M`RIP`*4/\"G8$KCP($`$`*4)\"O8$KC[\<]PH`#L)QF``*52"*`$2G!=!8_\ +M"B'`#^MRSW```(H3>]L*)$`$208O_@HE``3@>/'`Y@R/_`AW.G$:`$Q4A@2-:#^__"G)AO?$-=9`!YAT%C_S@>/'`N@R/_*'!"'<:<2,* +M=```WDAU]">`$QX((`"+<0#`%"",(V&]`+3M#760`>;Q!*_\H<#QP(8,C_RA +MP1IPSW:``+@;`(8!X"AU`*85"%$``=G/<*``R!PQH$(+8`TH<(X*H`0'V`AW +ME@H@`[/8%NB+<4(+K_T*<``4`#$`I0"&0B!`@`"F!_0`V<]PH`#('#&@:@]@ +M!.EPA02O_*'`X'CQP`T,W@`N#\__!/#2"```T<#@?O'`#0O>`$H/S_\$\.X( +M``#1P.!^\<#Z"Z_\"'&.X`'8PB`-``#:SW:@`+0/O(9YH*8.8`1*<+$#C_P*(<`/ZW+/<```B1-*VPHD0`2=!"_^ +M"B4`!.!X\<`Z"X_\"'2'7T)X`3\"&!(UX/[_\*`$QH((`#T(($C8;WS#760 +M`>9-`X_\X'CQP-X*C_P:<,]V@`"X&P"&`>`H=0"F%PA1``'9SW"@`,@<,:"> +M"6`-*'#N"*`$!]@Z.!X\`7*)SW:!`,@*\FWV?^9F-,H($84`22#``!$.GA7/=H$`"`VV?L&.`_`` +MWL=P@0`(#;9X!(@((P,`"".#`P`C0`%)(,,#%FUU>,]S@0"(#@-CSW"!``@. +MMGC/=8``3!VDA;B%`8"E>`0@@`\````(!GL"\&.!Z+N8&<```-T)\J01```` +MW9>]D;B4N*09```[#!X`SW"``$P=Q(#`NLB&!":.'P!````^OA[FV'I%>Y@9 +MP``="YX'I!$``(4E`12,N)&XI!D``)P90`,>\"<+W@>D$0(`A24!%):]F+V- +MNI&ZI!F``)P90`,D@!"!GK@0H0KPE+V6O9P90`,D@!"!GKB?N!"AF0&/_/'` +M*@FO_`/8SW:``&`D((9`>8#@;?(@AF!Y!-C3"!``((9@>0#89[@5"!4#,R8` +M<(``-#Q`)P%R%'D`>0#80O#/<(``:"0@@&!Y`=B`X`'8P'@X\,]U@`!H)""% +M8'D!V",(4``@A6!Y`=@;"-``((5@>0'8#PB0`""%8'D!V,$(48`!V![PSW"` +M`&@D((!@>0'8A>`!V,!X%/#/<(``:"0@@&!Y`=B!X`'8P'@*\,]P@`!H)""` +M8'D!V(/@`=C`>"\(4``@ANMU8'D`V!IPSW"``&@D((!@>0'8N'`WV`HAP`^I +M`,]P@```EB`0 +M@``)"%$``]@A\`+8`*(?\,]P@```EB`0@``M"%$`SW"``"('8(C/<(``4,N$ +M*Q\`,"!`#@KHSW"``"0'`("&(#F/"-@#\@'8`*(`V`"AT<#@?O'`SW"``%@' +M`!`$`,]Q@`!F]Y"+G_V`BX +M9'@HN`5Y17D(W?0D@`,G>$3`9@N@"Q`4`#$2%`(Q8;U`*`$$!7E'>43!$!0" +M,10D@#-`L-<-=9`!YE,EP@5`IP`4#0$'V0?P$'T4)TP0`+1AN10D0#"[>T^] +M`)"E>W![Z0FU@'A@!""`#P```/\0N`5Z0*?U!6_\I<#@>/'`0@A@!@#8E@BO +M_0#8SW"``.@X:@F/_<]P@`#(.&()C_VN#<_^V@P`"`#8_@G@`H#99@E`"U8- +M0`(N#(`+(@F``<(,@`(`V'8*+_\(<8X.0`HR#8`"\@M@`?_8B@A``8H@A0\( +M<5X/X`0(.!^X'CQP.'%`-W/<(``G`:@H,]P@``H +MC*RP*@N@"ZEP6@]/_;8*(`NI<%()@`/:#@_^#@A``8H@!@H(<=X.X`0(``IP\(40`!V%$>&)#^"@`-`=G/<*``["/'`X<6DP8MP_@F@#`39SW6``+@; +M`(4!X`"E%PA1``'9SW"@`,@<,:"N"B`-*'``A4(@0(``I0?T`-G/<*``R!PQ +MH+8-P``M!&_\I,#QP*'!BW"V":`,`=F>#<``H<#1P.!^X'CQP*'!BW!*":`, +M!-D`P%$@0("@"V(&RB"B``#`42"`@`0-P@H`P%$@P(!<#8(&`,!1(`"!1`J" +M!MH/H`L!V,]Q@*[@`>QP(*`!R.QQ`*'/`0<2!38*(<`/ZW**(,4`J0/O_2?;@@Y@ +M`4#82@S``(8)``>AP-'`X'[@>/'`0@I/_,]U@`!\&P*%(X4!WA!QP'ZI<#(( +MH`P#V1X,P``$[@*%`_``A84";_P#I?'`X<7/=8``V`6I<`X(H`P"V?H+P``@ +MA0OISW"@`"P@4(#/<(``X#6^#R`+66%9`D_\X'CQP.'%SW6``)0;J7"&#V`, +M$-D`%000(0Q0`$$,T``I#!`!"B'`#^MRC]B-N)C;_0+O_;AS`84,N`0@@`\! +M``#P`:4,\"&%SW"``,@O(*`CA<]P@``'%""H`\S7<````$`!V,(@"@`7N,=P +M``X``(.XG;B?N.QQ`*$!$@$V['`@H)H+X``!V,T!3_S@>/'``-C/<8``I!L` +MH0&A`J'/<-#^```$H0`6`$``%@!``!8`0``6`$`#S-=P````0`'8PB`*`!>X +MQW``#@``@[B=N)^X['$`H0$2`3;L<""@/@O@``+87@U``M'`X'[@>/'``!8" +M0*'!0,(!%(`P#P@>`,]Q@`"PBP3PSW&``#2M0*%@B0':"/``%@!`%2&,``"D +M`>)]>/4(A8`7"QX``!8`00/P`-@5(8P``*0!XOD*5($#S-=P````0`'8PB`* +M`!>XQW``#@``@[B=N)^X['(`H@$2`C;L<$"@T@K@``*)H<#1P.!^X'CQP.'% +MSW6``'0'J7`2#F`,"-D`A<]QH`"X'@*A`84#H4(*P`"Y`$_\.0+``/'`I,&+ +M<.X-8`P0V0/,UW````!``=C"(`H`%[C'<``.``"#N)VXG[CL<0"A`1(!-NQP +M(*``P%$@`(`#P`;T`L'F#N```-H%\+8.(`(!P2X*P`"DP-'`X'X)````!0`` +M`/'`U@G``!D`P`K@>/'`X<6TP8MUJ7#2#6`,%-D`P(;@S"#B@0;T4@O@`JEP +M"'$C\`\(D0#>"^`"J7`(<1OP$0A1`%X-X`*I<`AQ%?"#X,P@(H('](X*X`*I +M<`AQ"_`1"!$!>@O@`JEP"'$%\#L(40(!V0/,UW````!``=C"(`H`%[C'<``. +M``"#N)VXG[CL<@"B`1("-NQP0*""">``*'"Q!R_\M,`*(<`/ZW)\V(VX=]N+ +MNTHD``!Q`._]"B4``?'`X<6BP8MUJ7`>#6`,`MD>#>`"J7`""<``>0`"4!Z7]RB4E``.&&0C>`#X)@`4(<<]P@`!,-P(,``L"V`/P`=B1!B_\!*7Q +MP!H.#_P`%@1`SW6``#2;=!T`$8PD`8G*("V`,"MD!YAV%SW[Q#@20O@^` +M`"4&#_S/<(``3*NI!J``BB$?!.!X\<"N#0_\`!8$0,]U@`#`F50=`!%,)("" +MRB'-#\HBS0?*((T/``#]*LHCC0\``*H`R`:M_T.!)!2#X``P04/_.!XSW"!`&0:(8#/<(``3*O,&$`` +M+0:@`-39\<`N#2_\!-FCP0#>0L;>"V`,BW`#S-=P````0`'8PB`*`!>X`""! +M#P`.```&%``Q&W@3X`0@@`\``/S_)7B=N)^X['$`H0$2`3;L<""@`,'L<""@ +M!!0!,>QP(+`&%`$Q['`@L`84!#$;#!X``1(%-@HAP`_K$+`SW"@`"P@0!`0`,"]`>4#\`'F!A0`,8$.`Q`$%``Q +M@L.EQ"@F@`ZER['$`J004`#'I<1MX`>`0>/((H`.I/'`SW"``-0OM@A@#"C9]@R``-'`X'[@ +M>/'`X<4`%@!`SW6``!`7`*4/")$``-G/<)\`N/\]H,X,@``@A4$)50$S)D%P +M@`!4/$`G`'(T>`!X>@H@`U38*0A>`,]Q@``(,`"!@;A^#J`,`*$*\+X/K_X! +MV.(*``,$\#(/``0!`P_\\<#:"T`(?@R``-'`X'[@>/'`Y@K@"`#8SW"``$P= +MR!`!!L"Y@>$!V@J@##P0@`#1P.!^X'CQP.'%SW6``$P=`(7$$``&'0A> +M`0HAP`_K/'`HL&+<%(/(`P(V0#`SW&``$@P`*$( +MZ`84`#$#L004`#$"L78+@`"BP-'`X'[@>/'`9@DO_('8H<%@P`/,`-_/=H`` +M6`<"'`0PSW"@`"P@0!`1``"&`1S",Q#HSW&``"0'`(&!N`"ASW&``%0P`X$! +MX`.A`=@#\`+8&G``P`(/;_P*<<]U@`!4,`,2`3=>E8'88(:Z#Z`,"B0`!,]P +MH``L(!"`0!U`%!&E2!T`%)<(D"``AB<($0+/<(``G#A*#L`*W@RO_A380@E@ +M!`38X*;/<(``7`?@H`#8"PA0`0#8"/#/<(``7`<`@/<($8$!V"\F!_`-\@(, +M(`,4V(GHSW"``(`X)H`C@2"!;@[`"@"&`^@`V`?PSW"``%P'`(#[Z`'8+R8' +M\`7TL@Y``@OHSW"``"0'`(`O*`$`!@OO_4X@P`>U`"_\H<#QP$X(+_R`V*'! +M`Q(!-V#`SW.``%@'8(//=8``5#`"'$0P+Z4H``"(' +M(*^I<(0I'P``(8%_@`!`RZ8(8`J_VF"/"B&`+X``7,N$*Q\``"&"?X``0,L% +MDH8@?PP<>%,@@(`(],]Q@``D!P"!AK@`H0**SPA?`(0K'P``(8!_@``\SOH, +M(`P8V0"/A"@?`"]P-"$-($(E!!:,)`>!S/<*(<`/ZW+/<```@2>8VV4`K_V* +M)0`"9`'@"A&PT0``HA +MP`_K`$I<]PH`#4`QR0G@D``0#`2@MO_`+9`Q(!-\]P@`!8!V"`@-@H0#80"2/,",($`,@A6!Y`-@;"!`$((5@>0#8#PA0!""%8'D` +MV`\(D03I<,EQ&-H$\.EPR7$NVI8,``H!V&`>`A`7AH#@T`IA_,H@(0``%``Q +M*0A1`$`D@##/=8``-"=`)8$;:@P@"B[:`=@WA6$=`A"!X:`*0?SJ#4``403O +M^ZS`X'CQP-8+[_L7V;?!Y@D@#(MP(\!*(D`@4R#0`(8@_@-"*!$!)0@R)`P< +M`C0*(<`/ZW)RV(VXBB,/`PHD@`3U!&_]"B4`!$@4!3`@P$`HCB#/=8$`R`K6 +M?E$@`(#`94$M3P/`O[YFAB#W#USTC>@*(<`/ZW)SV(VXBB//!+D$;_T*)``$ +MBB!/!0IQE@U@!*AR`<`"P0IR+@TO_&9N?P@0`.EPU@Y@#`IQ#12`,(4@P0`- +M'`(PBB#_#U/``(:IN`"F$L"&(/L/*+@/KDHD`'0`V*@@``/_VKM@0"B!(#9Y +M$N,[8T"K`>`*<,(-8`R+<<]P@`!,'?`@P0/`$0`&#R``!,`9&``/C@\(40"` +MY\P@HJ-P#8(,`=\"\`+?I@[@`0IP!_"`X,HG@13*)R(2@>>X]""&SW"``$P= +M`X`8B"AUAB7['R$(4`"J"4`"((89Z,]P@`"4(`B()PC1`4$I0`,?"!X`$\`2 +MPA<('@*&(OL/02H$`D^."PH``:BX4\`3P!+"!GE$>"5X`*:&(/L/"^V`X,H@ +M`03*(2$`P`TA`\HBX0,.'D(4`-C/<8$`"`X6(0$$0(8`H0&A"PI?!0#8B[@! +MH0\*G@4!@44@``8!H:8.;_V+<`T4@#`_"%X!6!0`,06V6A0`,0:V!987Z`X) +M0`(.Z`:6$PA>`.H*;_T*<(X,@`P%V!*N`-@%M@?P"G``V4H-(`,/V@T4@#`U +M"%X`4!0`,0*V%.@`W1#8.G`"EA$@0(/*(`($RB%"`R`-(@/*(D(#0B%`(.<( +M=8`!Y0T4@#`/"!X!"G#."^``512!,`T4@#`["-X`-<%6%`(Q"G!V#J_]$L., +M(`*`N'`-]`HAP`_K"V``Z7!I +M`>_[M\#QP/X([_N*(%,)I,$`W:EQ/@M@!*ERSW:!`(@2`(Y*)$`@H:X"'@(5 +M`>``KJ.NH::BIJ2FI::XKKFN`<"ZK@+!!Z8#P"BF"::!P-8.X`L!V0'`!Z9Z +M=8GP@L#&#N`+`MD!C@/!`=_CK@'@`:X"P"FF"*;B#2_\BW($(``%+R0'H`+9 +M(ZX"K@#!(:9M\A)I%GC/0`A@@^!`,@*BB$(`**R(*(&V2.N`-E^ +M"R`##]H`PH#9$FH6>,=P@0#("BBH*:@'V`.NSW"``$P=\"``!,]S@0`(#E9[ +MP!`!!@0A@03`&%@``-D@H\]P@0`H"B&C5'AJ"J`,H+`'Z%H*@`P(V`.N^JY` +M(U,@(/'`BB!5"P#9F@E@!"AR$@T` +M"E()0`#1P.!^X'CQP.'%`!8-0`/,`=K7<````$`!R,(BB@`7NL=R``X``-X- +M(`I3)0$0425`D,]Q@`"\,0'8RB`A`(D'K_L`H>!X\<"AP8MP$@W@"P'9`!0% +M,!D-$0`*(<`/ZW*)V(VX1=LY`&_]2B1``,]Q@`#8QP,90@%`+8`#`J%*),!P +M`-JH((`"`-@/((``"R!`@0/T`>(#\`ZX`:&R"$``H<#1P.!^\<"2#H_[`!82 +M00`6`$'/<8$`R`I`*H`@%G@P(04`HL%!+4`#(PHT)%,@$P`*(<`/ZW)UV(VX +MBB,8`DHD0`"]!R_]2B4``!T-7@(*(<`/ZW)VV(VXBB-8`J$'+_T*)(`$SW"! +M``@-%B"`!!IP2@S@"P+9SW"!`*@)%B"`!#H,X`L"V4`JE2$`)8`O@0"(#BH, +MX`L0V8MP(@S@"P'9`"6`+X$`B`[F#B`&$-D!$(`@(0@2!`HAP`_K0;8 +M+PB$`\]P@`!,'?`@P`3#$``&`=D$(+Z/``8```0D@"\````(PB%!`"NX"PD% +M``#8`O`!V`]X!/`!W^EP!"2!+P$``,`NN<]R@``T5"EB,'4"$(`@SW&``'!*"&$Y"%``"B'`#^MR>=B-N(HC&0`Y +M\0HAP`_/<(``3!WP(,`$ZW**(U@/PQ`$!GC8C;@1!B_]"B4`!0,0@"`(81<( +MD``*(<`/ZW)ZV(VXBB.9`AGQS@E@#$IPSW"!`*@)%B"`!""0SW(``!@5"2&! +M`((.(``@L+4$K_NBP.!X\<``%H%`SW"``,PY(*@`%H1``!:!0,]P@`#5.2"H +M`!:`0%`DOH'*(<(/RB+"!\H@@@\``-H4RB."#P``@0>(!2+]RB4B`,]P@``P +M!@"0!NCR"@`,$@H`#!H.``#1P.!^(0:@"P#8X'CQP!(,K_L`V4HD`'*H($`" +M`!8"0!4B0#`.&)@``>$`%@U``!8.0+H*P`O/<*``%`2LH,]PH`#4"]R@T@T` +M`$$$C_O@>/'`Q@NO^PC9HL$!$@XVSW6@`#@N'!40$&H)X`N+<``4!#``WP0D +MOH_P_P``RB'"#\HBP@?*(((/``"F*,HC@@\``.$&U`0B_E +MY@^@"X*Y'!T`%#X-(``!&I@SF0.O^Z+`\<``V)H,(``$$H$P!!*%,`HAP`_K +M/'`X<6AP1_=BW`.">`+!-EAO?D-59#R#``` +M:0.O^Z'`\<"IP8MPG@G@"Q+9V@P``*G`T<#@?N!X`!8`0``6`$``%H!``!:` +M0``6`$$`%H!``!:`0``6@$``%H!``!8`00`6`$$`%@!`G00``/'`X<6AP0O= +MBW"B".`+!-EAO?D-59"&#```_0*O^Z'`\<"MP8MPA@C@"PW9;@P``*W`T<#@ +M?N!X<02@"P'8X'C@?N!X\<#/<($`#!E>".`+!ME*#```T<#@?N!X\$U`H_[\<#AQ<]U@```>*EPR@^@"Q/9M@L` +M`&X(H`FI<`'9SW"``.`%'0*O^R&HX'CQP*()K_L!V:'!H@^@"XMP`,#/=8`` +MH'L`%040H'`0>)3@RB')#\HBR0?*((D/``"X(;0?@#`ZPX$D#H+```2#R`!`!0`,0"% +M`,$X8`"EF0&O^Z'`\<#/<(``X`4.#Z`+`=D2"P``T<#@?N!X\<#/<(``5'GV +M#J`+2MGZ"@``3@N`"M'`X'[@>/'`SW"``$QX\@Z@"T+9W@H``-'`X'[@>/'` +MSW"``*!YP@Z@"]#9Q@H``-'`X'[@>/'`H<$`V4#!`!8"0``6`$`U"E```\S7 +M<````$`!V,(@"@`7N,=P``X``$4@``.=N)^X['(`H@$2`C;L<$"@['`@H!_P +MV@^@!8MP`\P!V==P````0`'8PB`*`!>XQW``#@``A+B=N)^X['(`H@$2`C;L +M<$"@['`@H`#"['!`H((*(``H<*'`T<#@?N!X\<`R"*_[`MG/=X``Z#G>#J`+ +MZ7!`A\]VH`#L)\]U@`!H))<*'@`KAD0B@`"&(O\.(KJAN12ZM+D%((,`97DK +MI@0@@`\0``(`!"*"#Q```@#/<8``1`5%>`NA((4$WF!YR7`;"-`!((5@>H18.@`4@ +MA6!Y`=AI"%$!`(=A"-X`SW"@`$0=Q:##H,2@*/#/<*``R!P!V3Z@"X:!N`NF +MY@[`!2"%8'D!V"4(40'/<(``3!T#@`B`&0@>``#9E+G/<(``1`4KH`N&E+@) +M\,]P@`!$!0#9*Z`+AK2X"Z8J"0``D0=/^_'`SW"````3U@V@"P+9%@D``-'` +MX'[@>/'`$@]O^P#:"'4H=L]PH`#4"SB`0B$!"(#ARB&,`$`F`!(0<:0/!0P# +MS-=P````0`'8PB`*`!>X`""!#P`.```';@0@@`\``/S_)7B=N)^X['$`H0$2 +M`3;L<""@(KX&\.QQ`*$$Y6&^^0ZUD`"%X@@```T'3_O@>/'`X<7/`/, +MUW````!``=C"(`H`%[C'<``.``!/(($`G;F?N>QP(*#/<*``%`0#V26@`1(" +M-L]PH`#4"TV@SW"@`$0=-:#@?N!X`]K/<:``%`1%H<]QH`#4"PVASW"@`$0= +M5:#@?@/:SW&@`!0$1:'/<:``_`L,J<]PH`!$'56@X'[@?N!XX'[@>.!^X'C@ +M?N!XX'[@>.!^X'C@?N!XX<7AQL]PH``4!`/9(Z`-R,]R@`#XR&&2SW&``.C' +MQ(H4(0T`:+4`((,/@``(R#CAP*MB@A5Y!I)@H0,2`S;`'000!(*@$P$`AB'# +M#R5XH!L``,'&X'_!Q?'`&@U/^PAV&@S@`2AU@.#1)6*3`=@#]`#8!+C/=8$` +M4!,4>`EE'645"5$`B@C@"JEP(@HO_@&-`-@`K0&%205O^P"F\<#*#$_[HL$- +M$@(VSW.@`+PMSW"``$P=3J,D@`#=1A$1`0T2$#=6(08%1B#`(`,2#C8-&APP +MI!8`$(2XI!X`$`&65B&(!%8A1P2&'D03".C/<(``Z,CT((``">@!A@\(GP-0 +M(``@+R`((%,@?J!(`P$`SW"``+PQ:1``!L]Q@`"\,0'@:1D8``02`3:D&4`# +M`9:/"!``SW"``.C'5'B`$`\'?P\1$-`0`P%3(\.`%?1R%@,1X)9B?[@6@Q!B +M?_!_X!C$`Z06`Q"&(_./!?)HO_!_X!C$`W`6#Q'@$``!89;B>/%PPB<.$,(C +MS@-T%@`1&V.X%H`0=!E$`Z"Q>&`0>)`9!`"^&00`$(X`VQ"I`88!H0B."*D2 +MCA*IEKLP\`^#_PC>A6^#4R/``E4+G@4A")4#SW"``+PQIQ``!K:[SW:``+PQ +M`>"G'A@0&O!DN!!XD!D$``0C@`\```#P++@0J7091`.@L:&QOAE$`P&&J*F& +M(/\-A+@!H1*.$JGVNS@"`0``V):XI!D``"D+7@4"">_^`-@$$@$VI!$```0@ +M@P\"````+;L%(P($+R"((#[P`8&E"!X!<(E/>DDBP`#/D&*`_``VL=P@0`(#79X!(@()@X0"":"$$DBP@,6:U5X +MSW*!`(@.`&+/L]S@`!,'62#>(-!@F5Z!"*"#P````A&>)@9```` +MV):X08&&(O\-0P@>!:$*$`"8$8(`0"<`"4A@SW.``$RM0,`@PL.Z7'KT(X(` +M4O`*(<`/ZW(TV(RX7]L%NXHD@P^]`^_\2B4``)@1`P"<&4`#20M>`H"XI!D` +M`"CJF!&``,]R@`!,'4."AB#_`T2X,B8``(FX0,`@PU2"9'J&(_\#AB+_#D2[ +M>F)/>L]S@`#(2O0C@@`>\!,+'@((ZI@1@@!`)P`)2&`+\(7J`-I("X&1@`$/!983!YAAU$$,]Q@`"\,6H1``8-&APT`>!J&1@`=0%O^Z+` +MX'BAP?'`"@E/^PAU1L#HO2APS@`A`$AV`[A`()`%1"4"%B.Z!"6/'P8````! +MXD$O0!0$)8$?P````%A@-KG/$\]R@`!834"2!2H^```A@'\``/\_+KA?`"``&6%7`"``%7E1 +M)4"25@`A`";%M^4B``L`,VA3)0(0SW"``!!+\""```4I/@`*(,`.`>`&\(KE +MP"CA`,`HH@#/<8``3!TC@<#:-(&D>88A_PXBN3IZVGH98C!X"-P_`$_[,VA3 +M)<`0''C/@6`R;H%*+X`)W'/<(``Z#@#@`"`X'\X +M8,]Q@`#`&R2!((%!*(,%U;A!*8(%U;D9"24`6V//F*@HDF!0:!E*H5)%3J"B!P+DMJ!KP.0U1 +M`&)B2*%!@$FA2(A`+8`*4!V<]P@`!X&R"P!@Z@""AP +M`H7/#"!,$``\@0``"I<]P@``<&S5X0*`8$P4!#!,&`,]P@`#4 +M+`#9-*C/<```%*!`P`6#$!,'`$'`&HL[BT"#3@B@"6/#/<(``>AL!V2"H +MSW"``#P;)X#/<(``/,``'@;@@V@"`"WSW:` +M`#P;`H5(AF>&#R"!`,]P@``<&U5X8*`BI>S8B@^@`T"7"!8$$,]P```4H!@6 +M!1$,%@800,`%AA`6!Q!!P!J..XY`ALX/8`EAAB06@!!(A@#942``@02%#R&! +M``KR`=O//'`X<4!W<]P@`#(BZ"@`-C/<8``>!O& +M#*`(`+%>#"``J7`A!0_[X'CQP.'%`-C/=8``R(M:""```*62"._]`M@BA<]R +M@`!X&W?8O@Z@`T"2\00/^_'`=@P/^P#>SW>``'@;P+=V#*`(R7#/=8``R(O" +MI<.EQ*6*(,D`R7&*#J`#0)!X\<#/<8``R(L`$04`&PU4`0HA +MP`_K-'`X'[QP`8,#_O/ +M=8``R(L$%040HL%)#5``(PV0`-$-4`$(%000"B'`#^MRSW```$0?*06O_(HC +M1P;/<(``>AL!V2"HSW"``#P;)X#/<(``/,8`#0`S)D%P@``D +M/$`G@'(T>`!X`H;/<8``/!M(@2>!#R"```*FSW"``!P;57@@H%GPSW"``'H; +M@-D@J,]P@``\&R>`SW"``#S'+Z#6#Z_]`MA'\`J6C"`"@!'T`-C/=8``>!NJ +M"J`(`+4BAHH@!03*#*`#0)4!V`"F,_`#V`"F,?`#AHP@PX\!WQ+T`-C/=8`` +M>!MZ"J`(`+4BAHH@10K@II8,H`-`E7X,S_P;\`#9#R$!``*&!B!`@!+T`-C/ +M=8``>!M*"J`(`+4BAHH@A0QJ#*`#0)5.#._\X*8#\`*FA0(/^P@6!!`*(<`/ +MZW+/<```0Q]9`Z_\BB-&`.!X\<#AQ<]U@`#(BP05!1!")4$`DPF5`3,F07"` +M`"P\0">`AN`V2"HSW"``#P;)X#/<(``/,!#R"```*ESW"``!P;57@@H!WP`X6,(,./`=H)\@#9#R$! +M``*%!B!`@`WTSW"``'@;0+"*":`(`=@#V)H+[_P`I07P`J4#\`'8`*79`0_[ +M"!4$$`HAP`_KX +MP+@3>,:X`>`*M0C8.G``W@*%#R;.$PL@@(,M\@2%"R"`@QGRQG@$I<]P@``` +ME@B`#WDC"5``SW*``-0L,(J&(,,/@+@!X2]Y,*K/<8```)8(H<]P@``<&Q4@ +MT`,`$``@@.#B(`(``H4`V0`80"#&>`*E0B%`(`'GE0AU@.]_2I7/<(``>!L@ +MD('BS"$A@`?T`-K/<(``U"Q4J`&%$0A0`8'A`]C*("(!'@S/_ZT`#_O/A)L#(H209`@`'P&&A!:%3V*EQ$@J@`\ER)L`3"!X`5]BI +M<0(*H`/)<@;8!?"!Y0+8RB!B`'H+S_\A``_[X'CQP*8/S_H:<,]V@`"X&P"& +M`>#/=Z``R!\`IA$(40`!V%$?&)!B#H`+I!<`$,]P@`!L)B:`SW6``+"Y8'D` +MV`&%*>@DV!C98@Z@"S/:'PA0``05!1`*(<`/ZW+/<```=!F:VYD`K_P*)``$ +M)-@!V3H.H`LSVA\(4``$%040"B'`#^MRSW```*LHG]MQ`*_\"B0`!`"&0B!` +M@`"F!?0`V%$?&)!I!\_ZX'CQP`8/S_K/<(``;"8$@"7HSW6``&@O,H7DX/'`SW"``-@%`8!1(`"`(`@"`-'`X'[@>('@ +MA]C*("(`SW&``-PTX'\!H>!XSW"``.`U`(!"(`"`RB!B``?H`=G/<(``!10@ +MJ`SPSW&@`*PO&8'PN!F!SR"B`\\@H0(9H>!^X'C/<(``A"Y`B!$*'@#/<:`` +MK"\9@8JX&:$1"EX`SW&@`*PO&8&.N!FAX'[@>,]QH`#(.QV!!^B"V!2ASW`` +M@!$4#J'@?L]P@`#0R6R(SW&``"B,C","@`J102@"`PSR&0C?`@*[=GO'!X\<#&#>_Z5&B&(O@#3R)#`E,AP@`%(L0`SW*! +M`"@*%'J/X8HC#PS*("D`"?8`D@#=#R5-$(HCSP^F>`"R`-E*)`!TSW:``$"/ +MSW*``+B/SW6``+R/J"#`!!0B0`#DD&1_&0\!$0#?Y+`6)D`0X*#AH$`E`!DU +M>."@`>&U!<_ZX'@`VIZZ`-G/<*``_$1!H.!X(:#/<*``M`\\H`O(!""`#_[_ +M_P,+&A@P"\B'N`L:&##@?N!X\<`6#<_Z2':`X`'=1/:*)?\?$W@)"1,`LWTS +M>10A``"N#N_Z.WFL>``>0!Y5!>_Z`=C@>/'`X<4(<@'=@.'*(<$/RB+!!\H@ +M@0\``)L3RB.!#P``7`#*)"$`"`9A_,HE`0&`XD3V4WJ*)?\?"0D3`#-YLWT4 +M(8``5@[O^CMYK'@)!>_Z+W#@>/'`X<7/=8``*(S/<(``3!TC@$"%`(%#"@$` +M`I%"E3L*`0`"A18-;_PCA8P@`H`5\L]R@`"8!B&"`-L/(P,``KAF>19X(:(` +M(($/@0#("@"!JKB(N`"A`-BI!._Z#+7@>/'`X<7/<```___/=8``1(P#I<]P +M@`"$-WH)P`G/<(``H#=R"<`)SW"``$@X9@G`"<]P@`!D.%X)P`D`V2"E!=@! +MI2*EY@]O_0;8X@]O_0G8403/^@?9SW*@`-0'&AI8@`WH&1(!A@D@0P`/$@&& +M`B#`@'EA#QI8@/7UX'[QP*X+S_H#$@,V"'<-$@XVSW&``.C'$(O/0*X%G@%8C&)+;U88,"]#.DA@Q4)7@//<8``A!RT>:"1$.6@L260(PE2`&&Y +M);`0BS)H-GD[8F63.F*'ZR:242%`@$@,0OSR#,`*%@_@!0W(`\@!V:`80`#/ +M<0\`__^^""``Z7"1`\_Z\<`F"^_Z`]G/+`HO8D+B@ +M'``P`=U"\(C8D+C[\4Z(4''*(((/``"1`,\@(@3Q]0'!$0F>!@'=D-B0N*`< +M`#`N\"*0,Q2`,"T)#@`'R`0@@`\`P```'0B!#P#````BP(#@RB")#P``C0"F +M!^G_SR`I!`K!C"'_CQ+RSW"@`"P@$(`B>-=P`(```,H@A0\``(<`?@?E_\\@ +M)01,(`"@S"4AD&;USW"@`!0$XZ!,(`"@J79B]5,F?I`(\L]PH``4!`F`@.!: +M]64.7A`!VE<)$"`J<2\H00!.((,'E./*)<40A?=H=8`EPA3/<*``:"SP($`# +ME.,/>,HGQ1"$]VAW@"?"$<]UH``8+/`ES1.Q<,HB(@`)Z@#8#R#```8A`8#: +M]0'8`O``V(#@*/.Q`._Z@"0#,O'`3@C/^AIP#@C@`3#8F'`IN%$@`(#*(<(/ +MRB+"!\H@@@\``.D4RB."#P``QP!T`6+\RB4B`"S8'@C@`4`H@2`!WHHE#QK2 +M#Z`!,-B8<"FX&0@>`(PF#YHF\M8.8`L!V&&]YPUUD`'FK@^@`3383R`!!96Y +MX@^@`338G@^@`2S8"'66#Z`!--BX<#,(7@4*(<`/ZW+/<```ZQ3CVP4!;_Q* +M)```"B'`#^MRSW```.H4U-OQ`&_\2B4``/D'K_I!+0`4X'CQP(H/C_H(=P#> +MR7#&"Z`$R7$#V,EU&G`)[T0M/A<`(8!_@`!0-M(,@`D*[T0M/A<`(8!_@`#X +M-KX,@`E"($`@UPAU@`'ESW"``+25R72=L#"\GK#/<(``'`=>"^`%P*"-!X_Z +MX'[@>/'`U@BO_>'%SW.``+PQSW&``(PY0('T$PT`&0VD$`#8^!,!``T)A`#\ +M$P$`,'+#]P'8;0>/^N!X\<#/<(``R`4`$`0``1(%-@HAP`_K/'`N@Z/^L]PH`!4+BN`!]W3N2\H00!.((\'SW"@`,`OI1`2 +MAA00$8;/=J``%`2JIAH/(`B`V//8!;B`V8X.H`&?N0T2$#;UV`6X@@Z@`:EQ +MJJ8-&E@S!/`#V`6FJ88;[7SM02V`D`KR+R0)<.!XJ""``0`6`$#@>%,E39`) +M\B\D27/@>*@@0`$`%H!`X'BIANCQ\]CV#:`!!;C)"-^']=@%N"H.H`$*<2@> +M`!24YPT:*(<4#A??I<8`AP@'/<*``&"SP($(`E.?*(<4#A??I<8`AP@3/ +M<*``:"PU>`2_0*#'=X``3.05AS:'!7D7A[B')7@%)0V0RB'"#\HBP@?*(((/ +M``#"(`D0A``8CN$%H!"&`#P8````QN%A@!"&" +M#P8```'7<@(```'*(*$``_`!V",(4``5")``@^``V,H@X0'`**$#"_#/<(`` +MV,<"@`7PSW"``-C'`8`%>9@=0!">%0`1E!U`$)(=!!""%0`1D!41$;(=!!`` +MV(`=!!!^'000`\C/=J``U`=!D!`5DA`(Z@W(SW&``.C(]"$``!/H&18`EA\( +M%0X-S,]Q@`"T,D8@@`(-&APP&H$!X'\"(``:H0\6%)8)Z@W(SW&``.C(]"$` +M``/H`=@%\`/8$QX8D`#8!Q(/-@$2$#8`%@1`>G`'&A@Q`!8%0`$:6#$$RIS@ +MRB+"!\H@@@\``-P.RB."#P``]`J4!2+\RB'"#ZEP2@K@"@[9'PM1(`3(`9`@ +MZ,]Q@``H-!J!`>`:H1R!`>``U01``%3(,"`"/3/<8``*#09 +M@0'@&:$"%041*0T0``&%[KC*(<(/RB+"!\H@H@O/("(#RB."#P``M0?X!"+\ +MRB1B``"5L'#*(

+X=!!!983]G#O"^ +M%0`1"?`@E;@5@!!983A@$'B^'000"'>0'000#Q8`EK0=!!"F"V`%J7`0C3)W +MS""!A!/R"B'`#^MR0"D-)$`H#@0PV(RX`-N+NP4EQ!/)`R_\!2:%%*05`!`( +M=(0D&I`A\CT(7@(#R`&0&N@-R,]Q@`#HQQ1Y@!$`!Y+HT!$``6H5CQ`!X,.X +M^&`/>&H=`A!6"^``J7!J'<(3!?!*"^``J7`/'AB580*/^N!X\<`."H_Z&G`` +MWZ09P`//<(``3!T$@-")\*`'R`0@@`\`P```*'4S"($/`,````W(SW&``.C' +M%'D1B8_HSW"!`*@)UG@BB`B-#PA#``IPB@_O_:EQW/!1(`"@AO($%000@0P> +M`0W(SW*``.C'%'H1$H4`#WA)(,(`V!@,HT3")X%SW"!``@- +MUG@!B`+P`-C'D2*""&!``@A`0``(4`!22#!`Q9N-7C/<8$`B`X` +M8<]R@`!,'42"SW&!``@.UGE8@B&!17D$(8$/````""9X`_`#A<]Q@`!,'9@= +M`!`D@2B!!"&!#P!````^N5,D`@`>X3AZ17B8'0`0%PB>!Z05`!",N*0=`!!0 +MV)P=`!!X\"L(W@>D%0`0C;BD'0`0SW!``5``G!T`$,]P@`!,'22`$(&>N!"A +M9/`%V!2XG!T`$,]P@`!,':0=P!,D@!"!GKB?N!"A5O"/"%XG`85S"!X!$HTT +M$H$P22'!`')NSW*!`,@*=GMB8A$*G@7/D&*`_``VL=Q@0`(#=9Y +M)(D(($``"""``$D@P0,6;C5XSW*``$P=1(+/<8$`B`X!8<]P@0`(#M9X6((! +M@$5X!""`#P````@&>0+P(X68'4`0#B"BG!W`$P7P!=@4N)P= +M`!`1"!XE`-B1N*0=`!`$\*0=P!-T'<03T@\@`*EPSW&```A4=!4"$0EA66$P +M>70=1!#/<8``$%3P(0``I!4!$"5XF!4!$*0=`!`9"5X""MEV'400>!U$$("X +MI!T`$!7P$-G/H8A_P-$N88B_PXZ8L]Q +M@`#P2O0AD0#/<8``R$KT(9(`#O##N<]R@``DK3QY]")1`,]R@`#TK/0B4@"8 +M%0404R`$@,H@@@05](@5@1!1)0""P[D\>=$@(H4(\L]P@`!,K?0@0``'\,]P +M@`#TK/0@0``AA0L)W@"$'000`_"$'<03'0T>`D0E`@8CN@'B!"6`#P8````Q +MN!IB`_`!V@/(`9`LZ`W(SW&``.C(]"$``(+H`96X%8,0=!4!$00EOH\!``#` +M>6$X8!!XOAT$$`;T3R6%`Y@=0!$$);Z/`0``P`[T"B'`#^MR+-B,N(HC&@G= +M!^_[BB2##P"5WO$["E``@N+,(N*`RB'"#\HBP@?*(&(+SR`B`\HC@@\``+4& +MRB0B`*P'XOO*)0(!SW"!``@-UG@#B`;PSW"!``@-UG@"B(P5`1`.N"5XC!T` +M$,]P@``$!D"`!H*@$``&B.C/<(``U#D`B+4($``-$@,VK0N0`0"5SW&``"@T +MG0@2#,]P@`#HQW1X$8B-"!$`A0P1`'T('B">%0`1SW.``!0TBKB>'000%I,! +MX!!X%K,!R.>A!:&8%0$0KKFON;"YF!U`$`:"H!``!B\H`0!.((('([H.X@\A +M@`"D%0$0F!T`$+2YI!U`$)X5`1&GN9X=1!#/<8``R#D`H00@@`___]/VF!T` +M$`W8F!T"$`KP$-@'\`C8!?`"V`/P`=@'H9@5`!"^%0$12@PO_P#:I!4!$`0A +MOH\````P@AT$$%'RC!4"$)P5`!&4'8`0DAT$$(`=A!0#$@,V%PD>`Q38D!T$ +M$"IP?AT$$'@3#@$)\`[8D!T$$'X=Q!-X$PX!2G#">!!XLAT$$,]P@`"4QP"` +MAB!_CPSTF!4.$!$.7Q)ADX;KD;F2N:0=0!`0N"5XI!T`$`0B@@\````0SW&` +M`$P=9(%2(@(#$(,%>E"C1($0@@0@@0\````0/7DE>!"B$_"8%0$0@!W$$Y0= +M0!">%0$1?AW$$Y(=1!"^%0$1LAT$$)`=1!"`%0`1?A4"$8(5`1$:8H05`!%9 +M83A@$'B]!&_ZL!T$$.!X\`!A +M+;A3(`"`!?3/=8``O#$-\,]Q@`!,'2"!Q!$!!L]U@`"\,5$A0($$]`'9W!U` +M$,]Q@`!,'?`A``#/`!X +MX@F@"0/8?@F@"4#8`-C@'0`0#O#/`\]R@``,(07PSW*``.@= +M0"(#!D`B`0=1)$""RB+"!\H@@@\``,LBRB."#P``K`/`!.+[RB'"#\]V@0"( +M#D`MC0&F9D#&(,4-#AX2PKVJ80[P$0Y>$D0E`1Q$N2ICB;H&\%,EP1`\>2IB +MSW&!``@-%B%!`2*)#KE%>2"@E0-O^J+`\<"8<+AQ%'@X8,]Q@`!82@AAC"## +MC\HBP0?*(($/``"L$\HC@0\``(L!1`3A^\HAP0_1P.!^X'CQP`?8SW&@`-0' +M&AD8@`X1`H8-&A@PSW"@`$@L7J`?$0"&!QJ8,`$:&#`$RIS@S"""CP``D0`& +M\@`6`$``%@!``\S/<9\`N/\8H7W8U@S@`@$2`38$RM'`X'[@>/'`N'$"N<]R +M@0#("C9Y,")$`%$D0(+*(L('RB""#P``RR+*(X(/``"3`[`#XOO*(<(/0"V! +M`<]R@0"(#B%B42%`@HHB"`7*(F$#SW&!``@-%B%!`2*)#KE%>2"@T<#@?O'` +M*@I/^L]R@`"@>_AR0"(&`6,(=```VL]Q@0`D&9AR`!<"`+AP`-V`@JJ&+ +M&GW`O;-]J*H!X$(E0`"W"'6`0"1"`"$"3_K@>/'`M@E/^L]R@`#`&T2"SW6` +M`$2,8H5`@C:[-KI0<]8BC0\``(``P(4]8GYF'0V%$PHAP`_K9O\-A9-88-4!;_H.((`#X'C@?P#8%'@X8,]Q@`"@5.!_"&'@ +M>.!_`=C/<8``F#'@?_`A``#QP)AP"B'`#^MR"B7`!\]P``"?&7$"[_L[V^!X +MSW&``'0QX'_P(0``\<"8<`HAP`_K<@HEP`?-V`6X30+O^T3;SW&``*PQX'_P +M(0``\<"8<`HAP`_K<@HEP`?/<```H1DE`N_[3=O@>!\)$0'/<8``6$4(84`H +M`@(%>D`H`01%>1BX)7@.\,]R@`!80?`B```#\$,H``)AN2]YC"'#C_OUX'[@ +M>,]R@`!81O`B```7"1`!8;DO>8PAPX\4\D,H``+Y\00@@`\```#_02@!`H8@ +MPP\%($(`0"D`!$5X&+DE>.!^X'C/<(``U#3@?P"8X'C/<8``.`8D@>!_(*`1 +MB.!_PKC@>,]Q@`#(.$:!BB'_#R"@!NHB@B"@`=@#\`+8X'[/<8``Z#A&@8HA +M_P\@H`;J(H(@H`'8`_`"V.!^BB'_#R"@SW.``.@X1H,2ZB2"&PE>`,]Q@`#8 +M-P\*0`#/<8``]#<1"D$`0(+E"X&``M@%\"*"(*`!V.!^\<"R#P_ZSP@0`,]V +M@`!(L"^.SW"!``@-SW6``$P=-G@BB`.%`-_/#:`)*G`!W1#P`-W/=H``2+`Z#Z`'!)8`V,]Q@`"T,@ZN'H$! +MX!ZA'0ECH6!1$*(<`/ZW(0N`4E +M!0#/<```@PR*(X4/Z0>O^PHD``1`*`TAW64EE025$+DE>#CHSW"``,Q4\"`! +M!$0H/B<`(8!_@`"P-B]W(*`CE0*5$+FJ#N_^)7@(<0`G@!^``*0V*@P`"<]P +M@`#`5/`@`00`)X`?@``(-D>5(*`CE0*5$+H0N25X)I7Z"B_\17EN#L_^"'$` +M)X`?@`#\->X+``E>EAV6`-D/(0$$$+I%>`8@0(`!W1VV,+@>MA3TSW&``$0D +M`(&@N!8*8`4`H<]P@`#`&P2`((#/<(``])6BH"&@$-K/<8``'`<`@0`J`@1& +M>"$&+_H`H>!X\<"^#2_Z`-H/(@(`SW.``+25'I,]DQ"X!7D&(;Z`-O3/=8`` +M1"0`A8"X`*7/<(``,`;/=8``>"``D,>--PX!$,]P@``R!@"0P8TK#@$0SW"` +M`#0&`(BFC1L-`1`+R`0@@`_^__\#"QH8,`O(A[@+&A@PSW"``,`;!(#/=8`` +M])4`@`"E`-@"I45Y/;,PN9T%+_H^L_'`X<6."J``*'6`X,H@00-8"2$$RB%A +M`(4%#_I1!\__\<`�_Z'@V@"`#=SW"@`-`;$8`7"-X#:@F@"0'8SW&``"@T +M"8$!X`FA!L@#$@(V.P@>`*02```S")X$SW&``*0P`($3Z*"A`0F>1<]PH`#$ +M+`N`4R"!!/ZXS"$B@`?RF!(``(X+[_X`V@,2`3:@$0``%P@>!(H@"``,&APP +MC@W@!"AP+O!)"!X%!\C0B0#:,Q&/``0@@`\!``#P02@-`\]QH``X+@>!#R)" +M`P'<1G@'H0W(#@_@"@`L`!#'=X$`R`H"OM9^$N??9Z"OBB`0``8:&#`#R*`0 +M@`#$X-0*P0H#V<]PH``4!".@A00/^N!XHL'QP$'"8L,*)0`!@<-`)`0R$@@@ +M``':T<#@?Z+`X'CQP.(+#_J")`,\"'_[0=I` +M(P`H`-E^#>_[0=KI<);!`@Q@"*ERZ7!`(P$H]@M@"*ERBB0!<`#9J""`!0`D +M0C!8$H(``"1`,$<&((`U@^O^LEPR7"6 +MP;8/[_I`VA\(#^_ZR7!AO_$/=9`!Y4IP.@^O^LEQ +MH@^O^LEPR7!`(P$H?@_O^D#:R7!*<7(/[_H0VDIP%@^O^LEQ/0,O^H`D`SS@ +M>*+!\:'(0$'H!A"`(MV&@EO^\EPR7"I<1X*K_M`VA\/=!`` +MW?`@02/P(T(C"@JO^\EP8;_Q#W60`>4J<$8(;_O)<>8(;_O)<,EP0"(!*>H) +MK_M`VLEP*G'>":_[%-HJ<"((;_O)<34"+_J`)`,YX'CQP/()#_K/=8``](X! +MA<]S@0`(#D0@!(//<(``T,D,B-)HUG['=H$`R`I`AA9[(8,2\E`BCP7@ID8A +M`08AHPT,$0&1O^"F!?"QNK:Z0*;2#H`*!_"6ND"F12$!!B&C"XVBN/D!+_H+ +MK>'%X<;/<(``T,E,B(PB`H#/<=Q@0#("E9X +M0(&A@`7NE;I`H:N]!/"UND"AB[VAH`#8"ZO!QN!_P<6AP?'`42``@N'%J``A +M``AU1"4#%@0E@A\&````([LQN@'C>F($)8`?P````#:XSW.```!42F,(8UA@ +M02V"$E(B`@#`N@.Z&.*%X,HBC0\!`(D-U2(.`%!Q4@`E``#8[;T8`"$``B&` +M`,]Q'$?'<04H?@`*(,`.`_`BN$$M01/`N02Y-'FI/'`I@I/^N!XX'C@>.!X:2"``6\A/P!I(```]_'QP-(/S_D:<,]VH`#0 +M#P#=!_`0%@"6_6'X8!`>&)`C;6\(1"`E%@.6)18"EB\DQP`E%@"63W\/?0B] +MI7_7#!&#@N?,)^*3S"&)"9!^_Y`=C@>/'`,@_/^8P@!("``"8`"'=Y#Q05SW:``"265B9` +M$XX*;_H"V6@6`!%A"%$`5B:`$WH*;_H$V50F0!UR"F_Z`MEJ%@`110AS`6B_ +M`K@]#P00`-T+\%4F0!>U>%(*;_H$V62_`>6O?6H6`!'M#0*052;`%CH*;_H" +MV6P6`!$-"',!8K\"N`D/!1``V!CP`-T+\%4F0!BU>!8*;_H$V62_`>6O?6P6 +M`!'M#0*0SW&@`-0+#X$?9P'8[Z'A!L_Y\<"@X`AQ`-@)]\]P@``DEC&@W@EO +M^DC@`=C1P.!^X'CQP$(.[_D`VCIPC"`$@$H@0"#"(`TDSW"@`-`/)1``AL]U +M@``DEL]W@`"PED`G4A.4'0(0SW"@`-`/)1``AD`G$Q,"WI4=`A"6%8$0E16` +M$#A@D.#*((D@?PA1(`#8$_!N"6_Z$."6%8$0`>$O>1-I%7@R(@`@EAU"$!YF +M0"1`(`]XFG"5%8`03PP#(!-I%7AB<#X);_H!V985@!#/<8``X)8#N!5X.&`F +M"6_Z!-F6%8`0`[@5>$)P%@EO^@'9EA6`$`.X%7@99RV)!N:5"7*(^&!*(``@ +MSW*@`-`/$!(`A@(A@2,X8!`:&("M!>_Y"G#QP&H-S_FAP0AU*'99#301`-B+ +M<,H(;_H$V0#`-0B`#_(!`%`W"(`/\@4`4!L(@`^:"5!OSW&@`-0+#X%DO;A@ +M#Z$!V!#PJ7!*#>__R7$+\*EPY@WO_\EQ!?`,;;8.[__)<0]X<07O^:'`X'C/ +M.'%,F@V><]R@0#("B%B +MSW*``$P=+;G`N?`B0P`H@U$A`(#/<8``V,=!@0GR/(N`X<4B@0\```H"`_)% +M(D(#2B0`=`#;J""``C9H=7D`(8T/@0"(#D"E`>,`W<]S@0`(#18C`@"@JJ&J +M`=DBJ@/9(ZI*)`!QJ7*H(,`!>6(6>:2I`>+@?\'%X'CAQ4HD`'@`V*@@``@` +MV\]U@`"D&T"%#R,#``LBP(`/\D&%"R+`@$#:SR+B!\HB@0\``-``SR+A!P+P +M`-K/!(5>T"C`>#@?\'%SW"``&PY!H`#@""`SW"``!R)*:!1!Z_\$=C@ +M>/'`.@O/^<]P@`!`R\*($PZ>$`#9SW"``"('(*@=\,]P@`#HTKJ(X[W*(6(` +M]/7/<(``P)D,$`0`'PQ?``HAP`_K<@B^SW```/HJBB.-!SD$;_L`)D4340// +M^>!X\<#:"N_Y`=G/<(``F",DH(H@Q0_/=J``R!\9'AB0*'`HA`!AHFYB[EZ&%B`SW&```#>$!A8 +M@%8-P`'>#8_^O@K`!T#9SW"?`+C_,J`^"0`*@-G/<*``%`0LH!T=6)#N"@`) +M:@C`"+8((`GI<`?82!X8D,(.0`;V"X`!O@E```X)``1F"\`'O@R``^8/``BR +M"D``1@U`!VH*3_RZ#L``3@Z`".X-S_Z^#L`$-@A``;8-0`9^"8`%Q@R`"FX/ +M``1Z#$_^A@K`!'X*P`0:#,`'SW```/[*S@L/_$D"S_G@>/'`U@G/^<]W@`!, +M'0.'"(#`N,H/(`DO(``@`-W/=J``M$?/<*``C$2XH`#8D[AW'AB0"-AW'AB0 +M`-B>N%,>&)#@>%,>6)//<(``/``0>$<>&)#/<(``X`(0>$@>&)!/((`C12`` +M#4\@Q@&)`&)!&'EB3SW"``'#F8@E@!0R(2B2`<,]Q@0!0$Z@@ +M0`//<(``V,=!@'1M='L[8P*`0Z,!Y02CSW6``(@Y`(4$Z&0>&)!#'IB1F@T@ +M"0'8`X<(@$"%'0@>`%,B00`2N40B``,.N"5XAB+_`PJZ17@2\$APAB#S#PJX +M!"*!#P````P&N25X!"*!#P```#`"N25XSW&``(@D,0'O^0*A\<#AQ1#=]@Z@ +M`:EP!]D+N<]RH`#P%S&BSW$``/#_.**RHN8+@`$=`<_YX'@`VH#ARB1-<.@@ +MK0'_V5Q@(*P!XN!^X'CQP.'%SW&``#S'SW"``#Q4W@@@"$C:SW"``+Q-SW&` +M`(P&S@@@"`C:`-W/<8``%"FAH:*ASW"``"PLJ:#B"V`"`X'/<*``+"#/<8`` +MG"Q0@!"`1:$&H0H/X`&IH:$`S_GQP`#9SW*``+25(*+/<(``1"0@H#VR,+D^ +MLM'`X'[@>.!^X'CQP`(([_D@VZ3!SW&@`,@<::'/<:``E!,`VENASW&``/@2 +MH($S:#5YBW`E>V"G)84AIR:%(JO^:3`X'CQP.(.C_FD$`$`HL';"5\& +M(-G/$0 +MWLHFXA$&%`\QC"?#GPGT!!0/,?%VS"?JD`'>0O8`WNONQ8!%?L>EL8B&)?P? +M&+VE>L]UH`#,%UJ@%O!%@,]QH``0%$>AI!`!`!4)G@(QB->ZAB'\#QBY17DZ +MH,]UH`#,%PW9`=H#X0T=F)`.'5B0)H`9'5B0)X`:'5B0*(`;'5B0`]D4'5B0 +MCI!`!`)FYI!A``&4&K_FBP/'`]@VO +M^039"'4-$@XV!M@-&A@PSW>@`!0$"J?/<(``,%3V#\`'`(7N#^`'!-D!A>8/ +MX`( +M<0&%0H4@D`6%K@_@!T)YRJ?M!:_Y#1J8,_'`=@V/^2AVSW>!`+`2.V=DBT`G +M$1&,(\./&G(_9Q+T`-D$\`'A+WG/1#B66&6#>`'$-I$CP"%0"X!$@.Z57I88""P)(\`A0.Y +M-7DR8#A@12*"`D"P2B3`<`'9J""``Q4.0!`R(4,@`(4`V@.[=7MX8$"P`>$O +M>0#8$-T(=L]Q@`#$>!9Y0"!`+PCA-@W@!P;:8;T!;ND-=9`/>!4%C_GQP*X, +MC_FBP0AW*'4:)/>O4*,X0`AG-J=7MP8/CH!!J"(""&`[I5>NEP$.%98`/>$`G`!3/<8``9'DJ#>`'`MJ+<4`@ +M0"]J#.`'`MJ!P0IP0>!>#.`'!-H`WQ#=BW#/<8``Q'CV>0CA1@S@!P;:8;T! +MY^T-=9#O?P02@2``AL]U@`!TL`'"`[DU>0+@.&!`J`02@2``A@.Y-7DX8`#9 +M*!U"$`'!!:6+=Q#@)Z4FA>ER4@Z@"&B%!!*"(""&"(4#NE5Z!.%98>X,X`<% +MV@'`)H7I<@'@0<`%A1#@*@Z@"&F%M0.O^:+`X'CQP%X+C_D(=L]U@`!8!P"% +M*'>&Z(#FXB""`R'PSW"``)PXI@B`",]P@`!<),]Q@``D!\"@`($%?^"ASW&` +M`%0P`H$!X`*A!/!N"P_[`(7^Z,]P@`!@*X,[_P!V`GPBK@:H1J!TP@? +M@*H,[_P!V`#9F[G/<*``T!LQH-H(0`J.#@`*SW"```@P`(!"(`"`RB!B`-'` +MX'[@>/'`X<7/<8``M)5^D5V1$+ME>@'=&PH/`,]Q@`!0-D0H/@?>#V`(`"%` +M#JEP`O``V-T"C_E&@0GJ(X%@@2*"8GDP<`#8`O8!V.!^X'CQP$X*C_D(=<]V +M@`#H.-X/[__)<0?HJ7#2#^__0"8!&(/H`-@)\,]Q@`#(.+X/[_^I<'GH`=B% +M`H_YX'CQP)((```&Z`()```/"%$`SW"``+0C`("#Z`#8$O`*"0``C^CZ"``` +MB^C/<(``E"`LD,]P@`!,'1Z0XPD!@`'8T<#@?N!_`=C@?P#8\<"X<,]QH`"L +M+QB!&0B>!@HAP`_K,]P@`!8!P"`A>`!V.!_P'C/ +M<(``6`<`@(;@`=C@?\!XX'\`V.!_`=C@?P#8X'\!V,]P@``H%`"(!NC/<(`` +M$!0!B`/P`=C@?@T)7D<)R+VX"1H8,`#9G;G/<*``T!LQH.!^X'CQP+8(C_G/ +M=:``R!\D%0Z6%0X>$H45`);B#0_\BB`$`"0=&)!1)H"0&`M""O4`C_GQP((( +MK_DTV#X(@`#/=X```'`O"!X$3@O@`@#8(@O@`@'8BB80$`#="@CO_JEP%"=, +M$V&^`+3U#G60`>4.\`#;BB(0`$8,8`1P>!0GS!!AN@"T]0IU@`'CD0"/^?'` +M)@BO^338H<$`W=X/8`!`Q<]W@```6#,('@22"^```=@#W@J^`-B,N+A@$'B+ +M<88,X``!VA0G3!-AO@"TZPYUD`'E6@O``!#P!=L*NP/:"KIX9=H+8`00>!0G +M3!-AN@"T\PIU@`'E)0"O^:'`X'C/<0$`2#7/<(``A!;@?R2@\<#AQ6_8E;C/ +M=:``R!\2'1B0SW`!`$`\%1T8D&(.P`B*(`0`#J7Y!T_YX'CAQ0#;SW*``$"/ +M2B0`=,]U@`"XCVAPJ"```D`E`1(4>6"Q`>!(<,]QH``$)0^A5B(`!!&A5B(` +M!1"AX'_!Q>!X\<`Z#T_YSW6``$P=!87/=J``Q"=U'AB0#)5V'AB0!X5Y'AB0 +M$)5Z'AB0*@[O_P#?&^AW'MB3>![8DX`>V).!'MB3!X6&'AB0$)6''AB0!X6* +M'AB0$)6+'AB0!86('AB0#)6)'AB0!86$'AB0#)6%'AB0P=A0'AB0*0=/^>'% +M"''#N,]R@`#`C_0B`P#)NW!QRB0B=,H@(@#H("("]"(-`,F]"0E``P'@X'_! +MQ?'`X<4(=<]QH`#$)QD1`(8!VH#@$1$`AL!Z@.(`I=$@X8<`V#/TSW"``,#) +M#(#/<:``R!]DX!ZA$-@.H0'8%1D8@`(-(`H+V%$A`,;*("(`&/0E"%Y'SW&@ +M`-0+%H$X@23@%0A%`-X,(`H#V`D+'T`)")Y$&-@#\`#8@.#*(.($SW&@`)`C +M/H$@I7T&3_G@>/'`_@U/^<]V@`!,'14F`1!`@6F"N(I!*\``P+@7N,=P``"` +M'.2[SR`B!N"[3M_/(*(`RB>"'P``3@&&Y<\G81(I"U\!SW6``)0@&!4$$;Z6 +M&PT!$:&&Q!4-%A$-7Q&@AL05#18'#5X1@;A1(P""SR"B!1NB_*)`@<]P.@1* +M8H@ +M!``>I1GPBB`0`!ZE%?``V(NX'J41\`#8C+@>I0WP`-B-N!ZE"?`#V`RX'J4% +M\`#8CK@>I8(@`0&%!6_Y'J4*(<`/ZW*,V(VXOMN+NTHD``!5!N_Z"B4``>!X +M(8``VE,A?H`+\@#:F;I1(4"`RB*"#P``@P#`*F(&SW&@`.Q&1:%FD$@C`P-' +MD!"[!".##P\```#(ND5[2)`,N@0B@@\```#P97I&H4"`00K1`&*`SW*?`+C_ +M?:)%@$FA1H!*H4>`4:%(@$NA28!,H4J`4J%+@$VA3(!.H4V`4Z%.@$^A3X!0 +MH1"`%*'/<```557@?O'`:@Q/^2&`SW6``*0;`H`@I0#>`:7"I<]PT/X``*X( +MK_\$I<]RGP"X_]VB;R%#`,]PH`#L1B6@&@Y/_B"%&0G>!\]PH`#(.QV`!NC/ +M<```554'\-VB?.G/<```K=YY!$_YX'@BD$@A00%`*0(#(Y!B@,NYC[E%><]R +MGP"X_WVBSW*@`.Q&)Z(C@#6B)(`VH@6`%Z+/<```557@?O'`Q@MO^0#;IL'/ +M<8``I!M@H6&A8J'/=J``M$6)!*)(!QSW*``*0;"!(%``L0D```W3EUV'6I-*)P``#R='`PL@P*$)\A4D03,@@4HC +M`!`/(TL0`_!*(P`0*H@+(<"!!2')$@GR%21!,R"!2B<```\G1P`#\$HG```% +M(L(!D.\5)$$S(($,$`4`/]\*)(`/``"MW@\@2!`$&L(3$0]1$A4D03,@@0#: +M#R)"`!,/$1(5)$$S((%*)@``#R9&``'ESW6``*0;"!U`$8#CRB2!#P``K=XU +M\B6(9(@&(H(!Q;H0N04C@P\````_97D%(8$/`#\``)L>6)!GB":("+ME>6B( +M$+ME>6F(&+ME>9P>6)`&(0$2Q;F9'EB0`-F5'IB0F;E,'D"0)(!8'D"0"I"4 +M'AB0;R!#`),>&)`Z#$_^B'"E`F_YIL#@>.'%X<;/4$&E``(H8$&U``,8#/8&EB`,H"S&EB`$X"T +M&AB`SW```%55P<;@?\'%X<7AQB2(SW*``!A5IHC"N2YB`-D/(8$#SW.``""4 +M0(.$[29Z0*,8\$5Y(*,EB!4CC0,CI2:(18A982:E((",(1"`1?>*(1``(*`C +MN2&C`(`JN`*C`-G/<*``\#8LH".#):`F@R:@)(,GH">#**`E@RF@*(,JH"&# +M*Z`B@RV@((,DH,'&X'_!Q?'`/@E/^:'!^G`;<3MR0,,*(P`A"B1`(0HE@"$* +M)L`ASW>``-0%((<`V(#ARB"!#P``R!OT"$$`6G``AXCHSW```,P;Y@A``#IP +M!/!*(0`@`(>)Z,]P```$'-((0``:<`/P2B``(`'8SW6@`,@?$Z4&V,]V@`#< +M-`"F!![`%0@>`!8`P`P>0!84'L`4&!X`%02F'!Y`%0_`(!Z`%0FFSW"``,`; +M)(`@@2JF*(`@@0R`*Z8`@`RFH!4`$`VFI!4`$`ZFJ!4`$`^FSW!#=:@2$*8` +MAX;H5@A@`"C8`_``V!&F`(>`X`#81`A!`!*F4R?`=1.F`F$Q4`EB\AAP08IA05`)8(N1FF%14`EAJF)!4`EANF%A4`EARFSW"` +M`*PS%X`=IG@>@!I\'L`:@!X`&X0>0!M(%0"6B!X`$,]P@`````2`C!X`$%,@ +M`",0N"5X+R%'!"5XD!X`$,EP1@A@`"79#0!O^:'`X'CQP.X/#_G//'`B@\O^6K8HL&+<0':O@]@`$ASC^@*(<`/ZW+/<```TA2*(\4$BB2!"JT` +M[_I*)0``0"2!,438`=J2#V``2'./Z`HAP`_K*),$*20#O^DHE```$%``Q0"2!,`':+@]@`$ASC^@$%`4Q"B'`#^MRSW`` +M`-04BB-%"!T`[_J*),$*`A0`,<]V@`"$.QMX02C%`$PE@(P`'D`1U/8*(<`/ +MZW+/<```U12*(X4)Z0>O^HHDP0H=V,]V@`"$.P"FN'``%``QSW6!`/P:0"V" +M`*EQN@Y@``';D.@`%`0Q`!8%$`HAP`_K@7P@.(!VL!Z-PI1`$PB`*8!VL(BB@"&(_T/)[L/ +M"8``@.+,(&&@"_0R=\PC(8`)\@/O!>L+"<(C#P["$\EW!/`!V0CP"'4!X&<( +M1($`V8H@_P^$Z8#ERB!*`XP@_X_*(($/_____Q7R,B2"-,]Q@0#\&@\*40!B +M<19Y`A'```GP%GD+"I$`!A'```/P!Q'``$$%+_FGP/'`/@W/_Q8*3_^Z"0`' +M]@P/_PHAP`_K!X\<#/<(``3!T"@,(0``91 +M($"`-`A"!M'`X'[@>/'`H@P/^1IP*'4Z<@#9SW"``%RM(*#/<(``))8Z#N_Z +MBB$,!L]RH`#4"WZ"`"6!'P```"#/<(``V`9B>6"@S+G/<(``P,DOH@R`SW*@ +M`,@?9.`>HA#8#J(!V!4:&(!-<(8@_`/0X,P@@H\``(``$O*,(`.$$_(*(<`/ +MZW(*)(`*SW```#(1BB,:#7$%K_JX/'`X@L/^<]P +MH`#$)U(0`89!$`"&AB#CCP#=!O+KN=$AHH$Z\L]P@`!,'0.`"8#/=H``))8M +M"%X!F@T`!(GH%(Z!X,H@(0&@#B'_RB%A`,]P@`"TF0"`#0B>`#((8``0EK2N +MSW"``+29H*!-<(8@_`.,(`*`#/3/<8``+!0`@0'@]@T@!`"A6@R/^P;PC"`# +MA%@/P?JY`P_YX'[@>/'`1@L/^<]V@``7Q#?20((:`X.!^X'C@?N!XX'[@>.'%X<9! +M+0!4P;@7"!4!,R8`<(``4#Q`)X%R%'D`>0#8%_#/<8``1,F8$8``0"@"!H8@ +M_0]2(,`!1;A%>,]RH`"()!"B'X&SN!^A2O`!V!#;SW&@`,@<::'/<]UH`"()#"E/X,"W40H +M/@T`(8!_@``(YI6Y/Z//<:``\!>]H:2`BA,#`::AHX`4XZ:AHH!3(\.`IJ&A +M@*:AP"`A",`@(@Q@@'.A;&A@@W.A^!`#@G.A_!``@!.A2J'!QN!_P<7@>/'` +M%@HO^0#;>0I2`)]RJ""`#L"0;V%3)DT0`[VX?Z.``>;`L%,F3I"E?^.@*/2A +M@.=]H:#B@$,MSA/G?MUE!"6/'P#_`/\$)8(?_P#_`"B_"+I%?\*@YWZAH-UE +M0RU/%Z&@QW_"H/UE0RV.$*&@YW[BH-UE`-K"H*&@0Z`!XW![_0$/^?'`E@DO +M^0':HL$(=5K88,``V$'`BW%J#^__J7"!QJEPR7%>#^__!-H`E!QLEPU@_O_T&'R7`I/'`X<6AP0AUSW#4NO[*0,`$\)(/H`D!V,]Q +MGP"X_[JA!-@;H8MP'J$`VIVZSW"@`-`;4:#/<`!M`!`9H0$)7D<`P-,(@(_4 +MNO[*]0`O^:'``-O/;$`#_G/<8``O"-@B<]RGP"X_P;KSW'0NO[*/J(: +MH@[KSW"@`#@N!8`$((`/P````/$(@(_`````:M@8N!FB'(+@?N!XX<7/PB>``(+ +M#__/=H``3!W)%@`6I;C)'A@0DQ4`EJ6XDQT8D-<6`!:EN-<>&!`.AJ6X#J8` +MAL@0``:&('^.RB`B`,HA`@`("Z+YRB*B`0&&R!``!H8@?X[*(&(`RB$B`.P* +MHOG*(J(!`(;/<8``/,?$$``&);C`N`JAZ@D/_G_8"KC/<:``T!L3H7_8$*$` +MV)6X$*'/<0``B"`."B``!MC/<:``\#8$@48@P`$$H<]P@`#4!0"`@.#*(($/ +M``"4`.@.X?_*(2$&SW"``!`7`(!1($"`*`PB``,]P@``(,`"`B^BV#&__BB#&"`L(40`Z"8`%#?`` +MV9ZYSW"@`/Q$(:#@>,&@SW"@`+0/W*`Z"8_\Y@L``4(,H`/'`,@[/^(AU`-\*Z!D(40`!WL]P@``)%,"H!O#/<(``"13@J`GI&PE1 +M``'9SW"```84(*@%\,]P@``&%."H"NH9"E$``=G/<(``"!0@J`;PSW"```@4 +MX*C/=J``R!_/<(``"108'MB3`(B*(1``$>C/<(``B1P`B`OHSW`#`$`-11X8 +M$#"F`M@8'AB0`O`QIL]P@``&%`"(&^C/<(``BAP`B!?HSW`"`/(O(!X8D,]P +M@``H`"$>&)#/<(``0`4B'AB0&!8`ED4@``,8'AB0SW"```@4`(@(Z!@6`):% +M(`$$&!X8D`\+40`8%@"6B+@8'AB0&!8`EH"X&!X8D!CM`-B4N,]U@`!\!P"E +M<=@&N"H-[__\V2"%SW```$P<&@WO_Y^Y&!8`EH6X&!X8D&T%S_CQP)AP`^DC +M#!((SW"``,@%`!`%``HAP`_K`#92B2`<<]S@`#TB"ARJ"#``?`C@``!X@5YX'\O*$$`X<4`VDHD@''/ +M=8``](A(,E>@#9GKD9>00A@`!"(`"`RB!B`.!_P<7@>/'` +MSW&@`,@?I!$"`,]P@`"<+`"`-8'/`! +MV`+R`(,-"%$`#0F$#P``B!,`V`/P`=B!XLP@8H`H"*'[RB#A`='`X'X"X3!Y +M06D-"@,`(G@0>`/P`MC/<:``R!\>H1#8#J$!V!49&(#@?N!X\<#AQ5#=`-K/ +M`#9 +MSW"``+29(:#/<(``1,D`@A`0`P>0+8%1H8 +M@#^BX'X"X3!Y06D-"@,`(G@0>`/P`MC/<:``R!\?H8H@&`@.H0+8%1D8@.!^ +M`-G/<(``M)D@H"&@X'\BH.'%X<;/<8$`,!-%@23HSW&@`,@?0!$.!L]S@`!$ +MR4`HC0)"$P`!?)/0?MA@NV-BNP@C`P`">PDBP@`"V!49&(#/<(``3!U?H0.` +M(H#/<(``M)DBH,'&X'_!Q>!X\<`"#@`""''/<(``3#?&",`'`MG/<($`9!HD +MH-'`X'[QP.'%SW"``,"9`X#/=8$`9!H1"%\`%P@>``B-#PA1``380@SO_02E +M#?!^#\_^M@T``@AQSW"``$PW>@C`!P+8!*41`\_XX'CQP.'%SW6``,"9%86` +MX%0/`@)Z%0`6"NB`X,HD#7#@>.@@+0'@>.!XX0+/^/'`SW"!`&0:!(`;"-$` +MSW"``$3)`)"&(/P`C"`"@+@/P?\5V-'`X'[QP,]Q@``B!P")C>C/<(``Z-(: +MB`\(W@`!V/H,(`8`J1?PSW"!`&0:SW&@`"P@,($%@")XSW&``""L`*$H<,]Q +M@`!,JV(*(`?4VAX/S__1P.!^X'CQP.'%"'7/<($`9!H$@`?H(@B``0"E`=@" +M\`#810+/^/'`R@G/^,]P@``DEI40@```W8#@4@`L`,HB3`,S;<]R@`#@EC5Y +M(F($(H(/!P#P_R2Z+RN!`$XC@00#;#R-#`,]Q@``(56YASW&!`'P:MGGA +M@08BPH#E?L&AZ_4!Y:]]O0T"D&T(<@``V9AP$VG/`-C!".##_@! +M``!!*\.$&?(O*($`3B".!\]^`-T/)8T3SW"```!5KF#/<($`?!HV>."`!B-# +M@^5^P*#L]0CPSW"!`'P:-GA@@("[8*!")$```>&E"'6`+WEA`<_XX<3\',B^ +M_!Q(ON'`X<'APN'#_!P(L?P<2+'\'(BQ_!S(L?P<"++\'$BR_!R(LOP'&X%!]@V".__"KA3 +M($$'!]AJ".__"KC/<*``U`L8@$(@``A((```SW.``+PQSW&``'P'((&\&Q@` +M"R%`A,H@(@,N]!\(D2`7"9XEB>A1(4"E9-C*(($/``!<`"#P--@>\(P@!*`9 +M\DP@`*(1\@CV&PA0("<($2&&V!+P%P@0)(P@`:`+]$S8"O!FV`CP/-@&\$;8 +M!/!4V`+PA-B3$P,&Z7')<@HD``0=`6_Z"B5`!.!^X'C@?P'8\<#AQ0AU*'`% +MZ\]Q@```<`3PSW&```!86WIJ"._XM'D5`._X`=C@>,]P@``R!@"0!N@`V<]P +MI``<0#*@X'[@>,]P@``R!@"0!N@#V<]PI``<0#*@X'[@>`#9SW"``%RMX'\@ +MH/'`2@^/^!IP`-[/<($`,!,4$!,`%A`!(<]P@`!X)`"`(*`Y\-UFM'U`(`\A +M`"=2$P`B0",!D$8)8`D*N,]Q@`!X)""!0"X1$0SA(G$`H;AG`I"I9PJX!2G^ +M!"()8`DG<`AQR@NO^@`0`"#/<8``>"0@@4)U!.$B<0"A`8T!Y@4H_@3/<(`` +M>"0`@`C@(G``&$`.%A``(9$.!)`)!X_XX'CQP+(.K_@#V8(D`CLZ<0CHJ0A0 +M`($(D``J=8SPSW6``%RMY8V+<#)O-'D`)5`0"!"0(``E1!`!V:8);_H*<@KP +M,F\T>3IE2(H`)40010H!!`AV)F@GV`"N#]@!K@#8!*X#K@>V"12```'G`JX* +M%(``P;\%KD`D``.J#N`&"-H0V3"N)HU`)D`40"%1)+4/09#EK<3QSW&``%RM +M5A&``%01C0"'N%89`@!4(<`*BW$#Y7(.X`:I"6_Z0(TC +MVD"H`MI!J,]R@`"@>6*2".)J8B)H0J@"C6&X"PC4`P#:(/`S)@!P@`!L/$`G +M@G(4>@!ZBB+^"1;PBB)^"Q+PBB(^"Q#PBB(^#`SPBB)^#`KPBB)^#0;PBB+^ +M#03PBB*^#@&-!]U">`&IG\=`)P`5)@QO^M#9JA2!,)7&R7"Y83!YM@QO^@#: +M0"<`%5'`4L:+<%/`4!Q$,ZH.;_J1P*T%K_B`)`([\',(:*`3_0AB)L)%0$S)D%P@`!`!XJ7"6"J`) +M!-D`B'L(40"I<(H*H`D(V0"(;PA1`(#9SW"``'!Z)K`PN2>P(-@K\,]V@`#A +M!0"..0A1`*EP7@J@"039`(@!V8+@P'D5"%``@N#,((*/``#^``CR`-@)\`X- +MH`&I<`7PJ@K@`:EP`*Z+Z!#8SW&``'!Z"'2&L3"\A[%2"\`)!06/^.!XX'[@ +M>,]Q`0#'`\]PH`#L)R:@X'[\'`BT\<`:<#8,K_\DV)AP42``@,HAP0_*(L$' +MRB"!#P``42;*(X$/```I`9P%(?K*)0$$SW&@`*PO&(&'"!$@#PB>!L]P@`!0 +M)`"`0'CTV`#9S@NO_P':--@`V9&YP@NO_P#:,-B*(08`M@NO_P#:--@`V0/: +MJ@NO_Q2ZP@NO_S#8PK@)"%$``-@#\`38SW(!`,8#SW&@`.PG1J'/"2__BB`%`X?H!-C1P.!_!!00-$39SW"@`,@<*:!: +M"F`)`=C6#4`!PO'QP&X+C_BBP2AV"B2`@`#?SW6@`"P@0!40$``1"Y!2&"#P``@OW/<:``["=& +MH00@@`\````?2+B&N!"X!2"`#P``0OT&H1"%`B``!(P@#XH+]XMQE@FO^8H@ +M#PT`%``QYP@>@`3P`(:`N`"F@<%^":_YBB!/#`04`#$-")X``(:!N`"FBW6* +M((\/8@FO^:EQ(,`(N`(!"X!2"`#P``0OT&IQ"%`B``!(P@#XH+ +M]XMQ!@FO^8H@3P\`%``QYP@>@03P`(:`N`"FSW`&``+_!J=`)($PX@BO^8H@ +MSPX"%``Q00*O^*+`X'C@?N!XX'[@>,]QIP"(20#:"PA1``/8#J$"\$ZAX'[@ +M>.'%X<;/=8``F"2@C0#>P*.1[8'@S"$A@`WR"PH3","C"?#`X@;8!O9"(@`( +M0[@"X`"CP<;@?\'%N'!`W``A`(/QP`X`)`"8<8P@`H"+]@HAP`_K/`(&D`@>+`(6D`B"`^`(D@P0^((3X`B2'!#X#@UB`K +M"(#AUB$K"'8)``#1P.!^X'CQP.8(C_BAP3IQ`-^`X,HAP0_*(L$'RB"!#P`` +MRA3*(X$/``"(`LHDP0`<`B'ZRB7!`\]Q@`"<)$"QSW&``)XDX+%,(0"@RB7. +M$V0`+@#*)LX3&G=:=P7PR7<:=6IP0"!3`(MQ`=K6".__`-L`%`TQ+R/()*EV +M*;W(OK_EV24I%$PB`*#*(,(#RB&"`\HB`@1,#2(`RB-"`\EPW@[O_ZEQ0B%1 +M(+4)=:!`(E(@R7`B"2``J7&)`*_XH<#@>/'`,@B/^#IPSW"``)@D`(@:<9D( +M$0`Y"1`@SW&``,0&I8D$B1UE,G7*(

"?``V2"OZ@[O_XK9`>;/?@`@@2^``,0& +M)HD!:3$.`Q!`*8`@%'BU>-1XSW.``!BP$&-2;6WH`N`0>-1ZSW.```RP4F/@ +MZ0'9W_$!Y:]]L0W2D.D'3_CQP(X/3_C/)."5 +M"_#,?]((K_A`*4!Q1;B*#>__"G$@E8PA$("T]ET'3_C@>/'`Y@Y/^`AUSW"` +M`)@D`(AZ<:'!&G+/"!$`.0L0(,]Q@`#$!L6)!(D>9G)VRB',#\HBS`?*((P/ +M``#,%,HCC`\``#P#RB3,!/P'[/G*)8P#2B$`(`#>"O`!'5(0!H^$Z""M`>4! +MYL]^SW>``,0&`"<`%`:(`>!C#B,0`G=`*X`@%'@5($`$U'C/<8``&+`T(1(` +M`-G%"A"@BW%*<`+:G@ZO_P#;"^@!%(`P`1T2$`:/VN@!%(`P`*W5\0HAP`_K +M0?O^4HE@`!`(5$@+R%')'D)TJ!=!F_XH<``VV"I +M$0AR`&"J#0C3`V"IX']@J@\(D@C`X`7V`=@`J1'PY."&]HP@`H/*(*P`R?:, +M($*$B?:,($*)!_8#V`"I`=C@?P"JX'[QP+H-3_BCP4HA`""+<2IP2B``(0IR +M]@VO_RISC>@*(<`/ZW)3V`:X^]L*)$`$Z0;O^0HE``0@PA,*$@``P$$H`0)3 +M(<0`%0P2`0'9SW"``)@D5P(@`""HSW&``,0&0*D"&0(!02@.`U,FQ1`#&4(! +M3"7`@,HBR0?*((D/``#"%,HCB0\```X!C`;I^$O!`*8$@-'D* +M%(`P%2%!`0'FSWX4>;EA`!D$!(`@`B,O(`@D`,!!*`$&P[D!X<4.0Y""P0IP +M`MJ*#*__`-L+%(0P+R@!`4X@A0&]"4.@,+C#N``@#@2"P:EP +M`MK6"Z__`-L+%(0P+R@!`4X@A06&^`>#E#G60#WAQ`V_XH\#QP/H*3_BBP4#`0<)` +M*!0%0"D7!0#=0"H3!4`K$@4!WDHE@"&I=P3P"G7*=P#`%;@3>!0@P`6B#&_X +M!]D"(%`#`B!`(Y(,;_@.V6)-I +M"A&@3",`H,P@(J`1\@#8$/`*(<`/ZW+/<```,1&*(Q<+2B0``-$"[_D*)0`! +M$]BE`4_X\<#AQ<]P@`!L)@@0!`!,)`"`RB'!#\HBP0?*(($/``!I&/'` +M621<.(MPSW&``"0]?@EO^(HB!`)*)$!X`-FH(``#%B1`,&&`0)`KV!*X`>%5 +M>&"@,'E6)%PXT<#@?O'`N@X@`$?8`-K/<:L`H/]9H0?8&J%8H='`X'[@?N!X +MX'[@>/'`SW&``&PF.(&`X1P.`@#1P.!^\<#/<8``;"8]@8#AA`\"`-'`X'[U +M!4`(\05`".T%0`@`V<]P@`"PN2&@@09@`B*@\<#AQ<]U@`"PN;(/8`*I<+AP +M`(42Z$HD@'//$*(<`/ZW+/<```AAF*(T0!#0'O^4HD +M```M`&_X*'#/<(``L+E`@".`#.K/<(``?"0`@$0IO@,-X#(@0`X)\,]P@``5 +M3D0IO@,R($`.X'X`V\]QJ@#P0V6ASW(``#\_1J'/<```/C\'H8H@$``(H0G8 +MC+@)H<]P```6'`JASW```!\?"Z'/<```'!8,H9'8!+@-H038#J'/<```/SX/ +MH5"A<:'@?N!X\<`B#P_XJ@R@`@#=2@T@``?84@[O_QIPSW:D`+@]K!8`%L]W +MI0#8RVG9H-JBN*P>&!"LIP'8]AX8$,]P%0`K*YH>&!#*'E@0P-C+'A@0Q!Z8 +M$,]R``!N;IL>F!"*(L0`GQZ8$!K:\QZ8$/0>F!!DVL@>F!"JVLD>F!#,'E@0 +MS1X8$#G9SW"E``@,/J`>#\__T@D@``IP&-B5'A@0SW&``'P;H:'(V`*A`*$# +MH<]Q`0"LTL]P@`",%=080`"4V`NG0=G/<*4`S'\MH,]PI``,@**@M08/^.!X +M\<`:"```)@_/_[(-``#N"P_[T<#@?N!X\<`J#@_XSW"``%S&0"`2!@AQSW"` +M``0&(*``WL2H!-]$+CX7"B%`+@`A@'^``%S&L@_O^1S9A"X*$@`AC7^``,RY +MJ7">#^_YBB$*`H0N`A<`(8!_@`#LPQIPB@_O^9S9`"&1)``90"-AOZ$=&!2U +M#W60`>81!@_X\<"^#2_X1"@^!P`ACG^``%S&$*X!W_&N(:Y`KF*N`QX"$7*N +M5@D@`!,>`A'Y!0_X\<"_X1"@^!QIP.G'/<8``7,8OVXHDPP^M!J_Y"B4`!!4(T"#/Z:!`9"X>*>!8*:D>*&& +M0"$/!*5X`:8=Z@&!`AS$,#"[!!S$,``#>_Y`"5`'IH-H`'X90()(`#)<(4$#_C@>/'`&@PO^`':A"@"!P`AC7^` +M`.S#SW"```0&`(`H=B"(`(:9'8(03(4PN`]XB0I1``/:F1V"$($)`0`$V)D= +M`A`0A8WH!=B9'0(0"(82I0.&$:45PAI8`"B5PQI8`"F5Q1I8`"J5HQI8`,]Q@`!, +M'2.!*($`WAD)'@!,E2N56WI%>5,;6(`ME50;6(`&\%,;F(-4&YB#+I56&UB` +M+Y58&UB`,)55&UB`,957&UB`,I5:&UB`,Y5<&UB`-)59&UB`-95;&UB`@@W/ +M_[$"#_CQP(0H`@?/<8``8#$HD5,A08#/:EPY[@GN%(@``#*)2(0RB%"`\HAX0'`N!-XPKC/ +M__>:WR">__`Z4$I<]PH`!X +M10"`!""`#W````!!*#Z%`-GU]<]PH`"T#SR@SW*K`*#_.:('V!JB.*+^"8`" +M@.8!V,!X#.":#J_Z`=G%`0_XX'CQP$()#_@(=\]U@`"PN1B-2'8Z<1IS$PH! +M`(7N&8T+"`$$`-@"\`'8+R('(.EPQ@Y@`LEQ((4`V`\/01`AA3)QS"(AH`+R +M`=@O)@?P&JT7\NEP*G')`S@#@ZO^@'9%0$/^/'`"',`V0+:A"L*`@`A@'^``,RY +MA"D$#P3@J@[@!2=P8;KI"G6``>'1P.!^\<#/<($`4!4Z"N_YBB$)#,]P@`!L +M("X*[_D4V<]P@`"0(R(*[_D4V='`X'[QP%8(#_BBP3IP&G$`W8X.[_\'V)IP +M`MFI<%IP>G$`VS1H`G$H=10A`"!H8MP0B-!(+\)=8!`(D`@0@OO_XIP00`O^*+` +M\<#N#\_W.G!:<<]W@`!PY@R/SW:``+"YI8:&(/\!0[@.)0V0SW"``&`D((#* +M)6(08'D$V"#H&HZ`X,PE(9`<\@#8$-T:<`*X%7C'<(``\"4@@`;I(H`5Z6!Y +M*G!AO>D-=9!`($`@`-@:K@R/AB#_`4.X!:8V#F_X2G#1!\_W"B'`#^MRSW`` +M`&49-]L*)``$N0"O^;ASX'CQP$*0(9!@D!"Z17DIVA*Z%2+#`""C`)#P(@`` +MT<#@?O'`X<7/<8``S,8`@0#=%>C/@S`!L]P@``4*9X*H``#@,]P@`#4+'$'[_>OJ.!XR09/^O'` +M(@H@`^'%SW"``(0W2@S@!@#=SW"``*`W/@S`!L]P@`"\-S(,P`;/<(``%"FB +MH+X*K_H#V,]P@`"<+*.@*0?O]Z&@\<#AQ<]QH`"L+QR!O8$$?<]P@`"('`"( +M$PA1`,]PP-\!`!RA*-D8N0GP_+TX"P(#]KW("X+Z`-F;N<]PH`#0&S&@X0;/ +M]^!X\<`B"```H@X``-'`X'[@>,]P@``4*0"`@>`!V.!_P'CQP$8.S_?/<(`` +M/,?'@,"^@>8!WL]Q@`"`+@"!P'Y1"%\`@;@`H<]UH`"L+QB%#0B>!AB%NK@8 +MI0+8%J7/<(``8"0@@&!Y`-@9"!$"T@R@"`K8"_#/<*``J"`-@.3@BO<5A?4( +M'H`&">__R7!%!L_W%,@8,'/ +M<*``K"\9@(8@/@,#\@#8`_!_Z`'8+R`'@`?R`=K/<8``S`5!J>!^\<#AQ<]Q +M@``4/4"!(8&GP4;!SW&``!0I(H%%PA4D0C#/<:``+""P@<]Q`0!0%D#!`=E! +MP4+!$-E#P43`18(`V`S9"'.8<+AP`"6''P```'TF#>_\V'"M!>_WI\#QP*H, +M#_[/`<]Q@```$Z&1SW&``(P&P($\Y;EF9.$?"00$"B'`#^MRW66*(,P( +M6ML*)``$P05O^54E118A"-$`SW"````309#/<8``C`8@@3SB66%DX0(@4"`+ +MR`0@@`_^__\#"QH8,`O(A[@+&A@P$@^O_^EPSW6@`,`O20]1$!"%-0@?`,]V +M@`!H)""&8'D!V`\(40$@AF!Y`M@=")``0!4$$`HAP`_K`@^#, +M(.*!!O1`*(`@G[B('0`0R@O`!L]Q@`"`+@#8V0/O]P"AX'CQP`T)40#V"P`` +M!/#2"P``T<#@?O'`:@F@`.'%>@BO^AK8SW"``"PL`)#/!X\<`""\_WSW6@`,`O%X4:A<]VH`#('X@5`!`'V!D>&)`! +MV`AQ"'((/'`]@S/_X#@`-G*($$`&?(N"6`'*'#/<8``=`8@@8H@3`;& +M#&```]H:#F```]CZ#$_\"-@B#"``BB'_#P'8T<#@?N!X\<#/<(``=`8`@!,( +MT0"Z#8_\]@O/_^H.``#1P.!^X'CQP$8*[_=*)$!Q`-[/=8``G"Q`)0,.'%SW*``)PL1!*``$`B`PPG"%$`2B1`<0#9 +MJ"```_`C30#/<(``."PU>`'AH*`O>0#81!H"`.!_P<7@>`#:SW&``-0L3ZD! +MV`VI3JE,J5"I4:E2J5.IX']4J>!X\<`*">_W`=K/<8``3!UC@7B+.0L1`0"! +MSW&``!0IQ!``!B6X4B```"&!P+@!VH#ASW&!`#`3)H'`>H#AS"`A@,PB(H!\ +M\H#P$0@>`,]P@``DQP"("0A1`)AR!/!*)```SW"@`"P@<(#/=H``G"Q%AJ:& +M`B.`@`#:RB)O``(C3X,`W2&B +M`-G/<(``/,`88-(`#)<`7P>@I@``.%30>O]Z'` +M\<#:#H_W"'8`W<8-[_\H<(#@RB!!`PP)XO_*(((#*0>/]^!X\<#/<(```)8( +M@$4(WP'/<8``+"Q"@2&!SW"``(`L0*#/<(``G"PGH,]Q@`!T!B"!BB!&`+H( +M8``"VA(*8``"V%(*X`4"V/H+#_H(\,]P@``4*?H)8``#@-'`X'[QP%8.C_A>@_`!GX(;_P! +MV#()[_^I<`'8D@]@`.EQSW6@`*PO'(43"%\&&(6(N!BE%@_O]Z#8!_#/<8`` +M@"X`@8*X`*'""L__4@I``-"&/@AO_`'8^@P``(H,8`@RV,]P`((!`!RE.@TO +M^@(FP!,`V#H/8`#)\,]Q@`#,Q@2)'0A1``6)%0A1`,]P +M``#__SX.[_\`V9T($`#/<8``3!T`@<00``8-"%X!`X$8B!L($0'/<8``=`8@ +M@8H@Q`0>#R```MH"V#3P_@I``&D(A`\``!0$SW"``!0I`(`-"%$`K@H/^BCH +M`-G/<*``+""P@,]P`0!\"D#``=A!P$+`0\%$P0;9"'(`VYASN',`)8EP.!XX'[@>/'` +M8@RO]P#9SW*@`"P@T(+/<(``G"P(@,]W@0`P$P(F#1#/<(``),?EAV.`!2_^ +M$#=U`=N@B,(CS@"EP0L-4!`#B",(40#/<(``%"DCH,]Q@`!T!B"!S]A&#B`` +M`-H`V$?PSW6``$P=`(7$$``&APA>`8,+40`#A1B(>PC1`,]P@``4*0&`M^C/ +M<(``>!L`D('@`=C`>`RX6PB`#P```!"P@L]P`0!0%D#``=A!P$+!$=A#P`#8 +MC+A$P"AP#-D!V@ASF'"X<``EAQ\```!]@@NO_-APSW"``)PLSW&``'0&(('( +MH-C8N@T@``C:"-@.#P``V0.O]Z7`\<#AQ0#9SW"@`-`;F[DQH,]P@`"8(P2` +M.PB>`!X+3__/=8``3!U-A3Z54R(``$X(8`0!VP"%Q!``!AL(7@$#A1B($P@0 +M`<]P@``\QP>`$0C>`,]P@``4*0.`#/#/<8``=`8@@8H@20<^#2```MH"V)(. +M``!M`X_W\<#R"H_WSW*@`"P@,(+/<(``P!L$@,]V@`"<+`"`H(8"(4,#UW,` +M`*`/`-_+]\]S@0`P$Z6#U;A!+8,08GT+"$0#`8:@Z&.&X:9O"U``SW6``,S& +M`(4BV",__!(7EI>.FH+@$I=H(+_H` +MV!OPD@\O^@?8%_`%ZP:&`GDG"5(`4R"`P02F#?3/<8``=`8@@8H@2P*&#"`` +M`MK:#2```MBE`H_WX'CQP.'%H<&*(/\/.@P@`$#`80A1`,]P@`!,'0"`Q!`` +M!B,(7@'/<(``>!L`D,]Q@`!T!H'@`=C`>`RX)0B`#P```!#/<8``=`8@@8H@ +M10/'`X<7/<(``3!T#@!B('P@1`0HAP`_KB#V_\ +M`=C/<8``=`8@@8H@10(*"R```-IB#"```-@Y`8_WX'[@>/'`O@B/]U(*P`;/ +M=8``$!<`A<]VH`"L+Q\(D``8AA<(G@8:AE(@```+"!X`'(8+"!X'*@J/_QR& +M-P@>`,]P@`"$-P"`0B``@,H@8@"1Z,]R@``L+`F"&P@5`<]Q@`!,'2"!Q!$! +M!@L)7@$!X`FBZ@J/^%(-S_T;"%```(43")``SW"``'0&`("#X,@,P?^=`(_W +MX'CQP"H(C_?/<(``%"D`@"T(4`#/=8``=`8@A8#AS"'B@/'`M@]O]P;8J@L/^L]P@``D +M%$HD````&``!SW"``$P=`X`8B!<($0$*(<`/ZW**(,P-ZMO-`"_YN'//<(`` +MQ#7:#$`&SW"@`"P@T(#/=8``G"P@A0(F0!`1#@)P```@3G8,+_H'V,"ESW"! +M`#`3!H!1(`"`8`PB^LH@(@(6"Z`%`-BA!T_WX'CQP.'%)@LO^@C8SW6``'0& +M`(6'X,P@(H(W\L]Q@`"<+$&!!^K/`!V,!X#+@W"($/````$!(-[_\!V,]P@``4*0"` +M:PA1``.%@>#`#.'YRB!A``.%@."T#.'YRB"A`"?PCNG/!L`D('@`=C`>`RX(PB!#P```!#/<(``%"D!@(GH +MBB"%!D8((``"VIH)(``"V'4&3_?@>,]R@`"<+`&"`-F%Z`."@.`"\@'94R"` +MP02B`=K"(H$``-B`X.'%X<;/=8``)"G`%0,6$PO5#])K +MU'Z^9@"F(:9"I@%KQ;C`'1@0P<;@?\'%SW&``-0L#8DS"%$`SW"``"PL`)#I +MN-$@HH((]`#8#JD-J14$K_\/J0'8#JD/B4(@`(`E`Z__RB!B`.!^\#4_W +M_@F/_\]S@``L+`"3SW*``"3'02B!`,"Y(:K/<8``>!L@D8'A`=G`>0RY'PF! +M#P```!"B@\]Q@`"`+*"AH8//<8``G"RGH3;PSW&``!0IH($I#5$0SW:``,S& +M)(X/"5$`)8Z!X0'9`O(`V8#ARB&"#P``$"<#]"*#SW:``(`L(*8I#5$0SW6` +M`,S&)(T/"5$`)8V!X0'9`O(`V8#ARB&"#P``$"<#]"&#SW6``)PL)Z6I<<]U +MH``L(-"%Y8$")LT3"0W?%\6AYH$")LT3"0W?%\:A*(.&Z<]Q@0`P$RB1(Z(E +MN,"X=@_O^0/9U01/]_'`SW&``'0&`!$$`+APSW*``.PL0"R``!9X%2!``0!B +M%0A1`0HAP`_K_X=ML`&4`!P0B0`#T($0'/<8``S,8`@;$($`#/ +MX"QH8,.SQSW"``)@C!(`A +M")X`SW"```@P`("*Z/8)[_V0V`T(40!^#L`##_``VIZZ`-G/<*``_$1!H.!X +M(:#/<*``M`\\H,]P@`!,'0.`&(@+"!$!A@M/_83HH@]``L#QOO'@>/'`<@M/ +M]\]UH`#`+SJ%SW*``(`N`()W"!\`@+@`HL]P@``\Q\>`P+Z!Y@'>P'X-"1X' +M$(4)"!\``-@#\`'8#W@?[C"%'PF?`D`5!!!,%040"B'`#^MRBB!,"64$[_B* +M(X4&$PA1`(H@$`$1I=X)(`@*V(H@$``2I=()(`@%V,]P@`!8)""`8'G)<%4# +M3_?@>,]Q@``4*4"!)PI1`,]S@`#,Q@2+#0A1``6+@>`!V`/R`-B`X,H@@@\` +M`!`G!?3/<(``+"P"@,]S@`"`+`"C*0I1`,]R@`#,Q@2*#PA1``6*@>`!V`+R +M`-B`X,H@@@\``!`G!O3/<(``+"P!@,]R@`"<+`>B`0;O_P.!X'C@?N!XSW*` +M`"PL()(#BH"YI[BBN8:X(++@?P.J\8((``((#@RB!,`P4";_>AP.!X\<"AP0#80,#/<(`` +M),0\.17!.```@7@D/_0/P/@D/ +M_1$(D0"*(/\/H<#1P.!^SW"``.@X`X`@@`#`(GB`X,H@+`#S\>!XX<7/<8`` +MP!LD@2"!SW.``.@X0X/5N:""1H.*(/\/@.(%\@*"HGA((```"2!``&JX2"`` +M`.!_P<7/<8``Z#@+@4"`#H&`X,H@@0______"O("@$)X2"```)D@!@!((``` +MX'[@>/'`M@A/]Z'!"'?/<*``+"!`$!(`SW"``$3)7X``W40G`1.(X4$J@`$: +M`!B2PA>`\]P@`!$R9@0@0`2:19X +M&F(K#QX3`(*(N`"B`=@/JL]P@`!,'0&`P!``!A$@0(#,(:*#B`H""`?PD@H@ +M"*^J@."`"@((SW"``'0&(("'XG/<8``S,8*@0'@ +M"J$F\$86@!!%"%$`SW#M_KZZ0,#/<(``U`4`@(#@RB`!!\HA(0'*(H$/``!] +M`,HC80]0#^$$P"OA!48>0A-%'D(3$@YO_T<>0A,2#(_Y!X8FAD)P`B!"``D* +MWP<&IDP6@!`-"%$`3!Y"$P+P`*:9!R_WH<#@>/'`0@\/]PAVSW"@`"P@\(`= +M#O(1`-T*(<`/ZW**(`T"BB,)`YAU;0#O^+AS-PF0`>X.C__/<(``G"SHH,]P +M@`!T!@"`@.#,(.*!"?+/<(``%"D!@(#@8`N!^<]P@``L+*F@SW"``(`LSW&` +M`!0I(('P((`#^&`/"5$`SW&``"PLJ:'/0"!%PB%`,]P@`!,'0"`Q!``!@<(7@%`H>!^\<`R#B_W`-G/"SW>G`!1( +MQZ?(@@]XT*?C@L]VI`"X/9L>V!/D@J8>V!/E@I(>V!-&@J,>F!#//'`5@T/]\]QIP`42`#=J*$'@<]V +M@``F$('/ASW#S#__\$*&@V+:AFKCU&A@`SW&D`+@]FQ$` +M!L]W@`"X&P.FIA$`!@2FDA$`!@6FHQ$`!@:F_]B;&5@#IAD8`)(9&`"C&1@` +MSW&D`.S_SW```/__IZ$&H0"'`>``IQ4(40`!V<]PH`#('#&@H@O@!RAP!-BR +M"R_X0"8!$@W8I@LO^$`F@1+/<"@``@'/<:``["<&H8H@C0`&H0"'0B!`@`"G +M!?3/<*``R!RQH.T$#_?@>/'`?@P/]U$@P($-$@\VSW.``.C'`Q(--L]Q@`#X +MR/1[$8L0$X0`$O(!X`AR,A6%$&>1`AD"`<]V00"#`&:QSW.``"PT`ZD1\$`D +M0@`Q%8400JG`$P,!`ZG/=B$`@@!FL<]S@``P-!,-A0#$H0"#`>``HP2!4_#/ +M!<]P@0`(#79X`8@#\$AP`"2/#X$`"`UV?^2/"";.$P@F`!"@<$D@S@,6 +M:]5XSW:!`(@.`&;/=H$`"`YV?F&&SW:``$P=Q(;8AL5[!".##P````AF>`+P +M`X4"H9@5@!!HB0T+``!$J6#8&+@$\`#8G;@$H/'`X<4#R*00``!1 +M(`"`SW"``$P=!(`$\AN0`_`:D.H)@`:[Z,]PH``4!`/9(Z`@V`P:'##/<8`` +MM#(6@0'@%J$#R`#:F!`!`*00`P"4&$``GA`!`:R[DAA$`+X0`0&MNX`0#0&D +M&,``D!A$`'X0`0&`&(0`/66P$`$!HGDP>;`81`""$`$!?AB$`(8CY8^R&$0` +M[`M"_4$##_?@>/'`P@HO]PAS$(DS$8T``=I`JPT2#S;/=H``$,CN9L]R@`!` +MR$C@2Z17[`"5"'BB!0"(#!P`E0!Y/(4\#Z*("B&&F#0A>`(.YC;DHHL]P@`"D +M!P*C&-@"IL]P@`!`!P"`#!X`$<]Q@``H!YX*K_H@@0#9SW*@`"P@4()AA@L( +M<@!*HUA@"J//<(``(@<`$(<`A"\?`"=U0HTO<+$*7P`9"!$@SW&``'C.&V'/ +M<(``-`=@H$KPSW*``#0'0()!BD0HOBC'<(``7,L7X$`BA``R($(.+R0'`<]P +M@``X!P+B3WH`$(4`00IR``(E@P"$+Q\`+W``(4\.`">&'X``7,M$*+XH0":/ +M!3(G3QX[8\=S@``TS@CC&V,!X2]YX*O1":*``B6#`(0O'P``(4`N&V//<(`` +M-`=@H`Z5`B```0ZU#I588`ZU9:8'\,]Q@`!4SCA@!:8.E>4`+_<$IN!X\<#A +MQ0AU!HTEC0BX!7DO>"BY+WD(N*>YK+BLN25X#WDHN`BY)7@%K2BX!JT`V2AP +M81T"$"BX8AT"$"AP!ZTHN`BM0"5`%!X*[_@@VD`E0!P`V1(*[_@0VJEP0>`` +MV08*[_@(VJEP4>``V?H)[_@0VL]P@`"`!F"`02L!!"]Y"+E!*P(&17EO>BB[ +M"+IO>V5Z$+I%>2FM*+DJK2BY*ZTHN0&`+*U!*`$$+WE!*`(&"+E%>0]Z*+@/ +M>`BZ17@0N"5X#:THN`ZM*+@/K2BX$*U?V`BX+0`O]P&U\<"J#^_V`-G/=X`` +M@`9EER,+<@"")`(XSW6``""/SW*``"!X*F(\90'A+WGS">*`0*Q$ER$*<@`` +MW<]S@```C\]Q@```>*EAO&,!Y:]]]0VBD""L;I"PXR(!)0``WHMRV@S@!\EQ +M@<64P4`E`!>J#R`%$-I`)0`7R7$2">_X$-H"%``QBW)6)`0S#WDHN`BY)7AD +M:&][SW"```"/_@KO_"B/2B0`=,EPJ""``P`D`C``)`$P8!&!`%`2@@"_"D$` +M`>`/>"*-`8T(N25X#WXHN`]X"+X%?@67G,%`(!`$0"7`$B\@!R0R#R`%$-I6 +M)`$TSW"``""/(@\@!467G,*,P,EQS@Z@`0IS^XW/<(``_`7/<8``^!)@@>A@ +M`[@5>!!CPKA?"%$!SW@C"!,$3B`\!*@@0`,"(($#`"0",(-Q,!&!``'@#W@P +M&D(``">-'X$`L!(DC078`[DU>3MC`+,DC<]P@`#X$D"`C,`#N35Y$.)98:8. +M(`40VB?P`-@]\``GC1^!`+`2)(T!V`.Y-7D[8P"S)(W/<(``^!)`@(S``[DU +M>1/B66%R#B`%R7+/<(``^!(@@$2-$PY1$P.Z57I085EAC+@`L42-SW"``/@2 +M((`(OP.Z57I085EAY7@`L42-SW"``/@2((`#NE5Z4&%988.X`+$!V!D&[_:` +M)`(XX'CQP!(((``"V/8)``#1P.!^\<":#>_V2B0`<@AWSW"``$P=%2#0`P`0 +M#2``WLEPVJ6H($`-SW&``&Q+]"$"`,]Q@`#TK!1Y0+'/<8``?$WT(0(`SW&` +M`"2M%'E`L<]Q@`!\2_0A`@#/<8``!*T4>4"QSW&``(Q-]"$"`,]Q@`!,K11Y +M0+'/<8``9$WT(0(`SW&``!2M%'D!X$"Q"(4+"%X!!-DTI0+PU*4/"!X!"=E& +M'400+MH%\!391AU$$#+:6[59C5EA,'E&'400&N$ZM1<('@`*V%0=!!`&V%8= +M!!`'V`?P$-A4'0005AV$$P78#Z5Z"J`#Z7`\C2AP1!U"$(8@`P#FN5@=`A#* +M(D$`"_)0(<,!;WI$'<(04"##`6]X6!W"$!,)7@%(D0=PA`-"1X! +MI;A8'0(0"PG>`*2Z1!V"$"\/D!"V#*_YZ7``$``@N1``!E$@0(#QV,`H(@'* +M(($/``"3`,`H(0&$'0`0&-B-N!.E"(51(,"`SW"``$P=!?*V$(``B;@#\)T0 +M@``2I<]PH`"L+QF`SW&``)@C,+C`N)(.8`<%H0B%!""^CP`&```+\C:XP+@; +M>`'@6AT$$`+8&J4#\%H=A!,`V!>E&*7>#6_]Z7`HA0':2'-!*0`%-;E2(``` +M4B$!`,"XP+F.#>_]F'(5!,_V\<"R"^_V!]C/=J``R!]('AB0SW>``$P=(X?/ +M=:P`U`$:@4P>&)""X`+8RB#B`-`=`)"*(`0`#Z9&$0`!L!X`$$81``&T'@`0 +M']@(N`ZF"(%1(`"``-B+N`KR$*:""(_YSW"@`*0P`8"$N`KP$:9V"(_YSW"@ +M`*0P`8"DN,]QH`"D,`&ASW"``.@Y`(`5"!X`AB#_#B*X%+C/<8``1`4+H3X+ +M3_G."T`!G@F``QX*@`//<```555:'AB0`=E9'EB0SW"F`"@`+Z`#AUH0`0'/ +M<*8`Z`!+X*X``"V`3P%@\@!@'8SW*@`,0G#Q(` +MAF.'1"`!`AN##QH8@`\2`(:CN`\:&(`/$@"&!7D/&EB`/(//<*``,!`DH,]P +M@`!$R1!XCQH8@,]P@``@D,]Q@``@H!!X$+DE>)`:&("*(`0`DAH8@!V#0!H` +M@,]P@`",'%,:&(`/$@"&G[@/&AB``-@0&@"`'H,<&AB`=0+/]O'`^@G/]FZ0 +M"'?CXY@D03,:<9/W`-F"#Z`'BW*!QB*.`8X(N25X#WHHN`BZ#WD%(9$`#0C> +M`*$#(```V`D($2#W")^!42'`H=$A(J+1(6*B\_,+"!`@XPD>HUX6@1!=%H`0 +M"+DE>`]Y*+@/>`BY!2!2`":.!8X(N25X)XX0N25X*(X8N25X02@!!"]Y02@" +M!@BY17D/>BBX#W@(ND5X$+A*C@5Y"8X(ND5X2XX0ND5X3(X8ND5X02@"!$]Z +M02@#!@BZ97H/>RBX#W@(NV5X$+@%>L]S@`"`!@PHD@%,AA"`-"@$``(-/ +M"02`(*-!H\]S@`!P>B"S,+DALT*S,+I,)$"`0[.?Q<#TR7!-X%8E@1)B"2`% +M$-K)<$W@`-G*"J_X$-J+<"&0BW(O>`BX*+DE>&1H;WO/<(```(\0V;H,K_Q6 +M)0022B0`=`#8J"```QIE&65`$8$`4!*"`#!R;?4!X`]XZ7``V8MR+@Z@!VZ7 +M0"9`&U8E@1/Z""`%$-I6)-,WSW"``""/0",!(>8((`40VI<($"!6)8`3(-F* +M(@0`BW-CXYX(H`$*)(`$BW!CX$`C`26^""`%2G(R)(`_```,`8P@0X`7]3(D@C\``!(!0",`)T`E`1A3(E``5@@@!4IR +M%_!6)8`3(-F*(@0`BW-CXPX(H`$*)(`$BW!CX$`E`1@N""`%2G)$(0`L0B@0 +M`4`E`!@*<#Z__BW#/<(```(\0V8MR8]M^"Z_\ +M5B4$$U8E`!.9\,EP3>!5)4$5X@_@!!#:R7!-X`#92@FO^!#:BW`AD(MR+W@( +MN"BY)7AD:&][SW"```"/$-E>#*_\5B4$$DHD`'0`V*@@@`,:91EE0!&!`%02 +M@@`P``@C0^``""L")53(!``SW"``"26UXB.[L]Q@`#TC@N)AB#_C`3R +MP(D&\,]P@`"PN<&`X@C@`\EPSW*``$3)5A(!`<]S@`"2R4`A!`O/<8``(@?@ +MB<]Q@`!*RX0O'Q`T(4$.AB%_##QY%"-!`(P@_X]@B0(DP0`-\@#;$0ZU$P\C +M`P`)E65X";4%\`6%97@%I0T($"`'E1$)!0`GM2."!((AI0*EQK6I!8_V@.#Q +MP#38!_0"#8_]4"!!!`7P^@R/_4\@000R#:_]--C1P.!^@.#QP/38"/3>#(_] +M4"`!`/38!_#2#(_]"''TV("Y"@V/_='`X'[@>,]P@0!D&B"`SW"``,"9!.D@ +M@`/P(H#/<*``+"`0@#A@SW&``/P&X'\"H>!X\<#.#(_VSW*``*@P8I+/=8`` +M'#U`A:3!0,)"E=@1!`#/=8``,#\$'(0P0(5"PD*5#!R$,+2`7($+"F0#V'(" +M(D8#4X"W@5!UPB6&$,]R@`!@,082!0'($0X`_]H(ND1^*+X`'(0SS!$.`$1^ +M*+X"'(0ST!$.`$HDP'#$>BBZ!!R$,.P1`@`('(0P\!$"`/01`0`*'(0P#!Q$ +M,`#:2':H(,`#`]D5"8X#%"2!,^"1""+"`R21"2-#``'F`"6!$04I@0\#```@ +M+W$%+3X!#0E%#HP0`0`!X0/P`-FP@(P80`!5(,X%4R7!$!0F01!`L5P0`@%* +M)`!T`-^H(``"]";!$S!RRB)+``'G52#!!Y`8A`#"O10A30-@M7P0`P%*)`!R +M`-JH(``"]"&-``@C0P,!X@;C<'N2&,0`_0.O]J3`X'CQP(X+K_:8<&]_`[^D +M;\]P@`"H,+9@HF^U8`;G\&`1"U$"ZI&,)P*8RB!K`!4*40!T>4&1'64C"D(# +M`GI!L0KP%PJ1`'1Y09%88`\.`A`!L0'9F!Q"`*$#C_;@>/'`,@NO]@#9SW"` +M`"2652!$!V@0``&`X,]P@``DEE;R:A`"`3T*4@#/L```/S">"G_(``%`,]#B^PKX!X2]YSF7?":*`Q7@!HP#9!/`! +MHP'9@0D1`,]P@``DEFP0#0%U#7(0`-K/F5(DGUE"P@!`8.Z2+7/@BUSW"``"('`(A^$8,`A"@?`,]P@`!" +MRS(@0`X+(,"`!?)/(H`!$'H(M?CB#/3/<(``;)9,]R@`"PEAEB+8D`(@4`$V\5>``@@@^``,"961*&```@@@^` +M`,"9$F\5>,]U@``@K!ME:),=90L)@0&#NVBUSW&!`'P:UGE\$H``!!$$``L@ +M`($&\D\C``$0>PBU?1*``""!"R!`@`;R3R-``1!["+7/<(``(@<`B'X2@0"$ +M*!\`SW"``$++,B!`#@L@0(`&\D\C@`$0>PBU^.,8]$`E``15(L$%/@IO^,AR +MD.@(E8*X"+6&"N__Z7`(E0'9@+@(M<]P@0!D&BBH`>;/?L]P@``DEI40@``I +M#@*0`>?/<(``P)D5@.]_%0\$D,]P@`#\!NH);_@(V<]P@0!\&MX);_B`V<]P +M@`#`F76`)PMT``#9SW*``""LLFFU?;AB")!=90'A+WF&(`$/[PGD@`BU:0"/ +M]N!X\<#Z#T_V=(!<@=@1#0!P.'%X<9T@%R!V!$.``(BQ`!3@#>! +M/!`'`(P0"``"(84`SW&``&`Q`MKT(8H`!A$)`<]U@`"H,$*5894*$08!DA`! +M`:.5""-#```E`0$%*8$/`P``(`HD0`X%*KX3+0D%<2J0C"$"B-+V`MDOH)`0 +M`0%^99081``AD&<.0Q"B>2&P`=F8&$(`+?`%*;X3+0Q%#BD/D`"0$`$!E!`. +M`0+A#0F"`XP@`9G*]\&0`=G=93,*8@,OH*&PY_$;#Y$`H9``)L$`$PE#`P(E +MC1&AL`'9F!A"`)`0`0&4&$0``-DOH`DBP@`AD`T*0@!!L`'9F!A"`,'&X'_! +MQ>!X(F@`VD"P2B3`=<]P@`"H,*@@@`(6((,`8),4(8P``>)/>F"TX'[@>/'` +M:@Y/]G*`R('8$0(`<';")L80<8`C@7!QPB'&``#?SW.``&`Q]"/$`P'=V6$% +M*8$/`P``("]Q!2R^`/0C0P,-"65P[:`"V2V@!_`%*[X`!PE%#JV@?09/]N!X +M2(%2H$.!4:!<@52@-X'@?S.@X'CQP/X-;_8"VPAU*'8!V`"QJ7!>"N__385- +MA:EPR7%2"N__`]M-A:EPR7%&"N__!=M-A:EPR7$Z"N__!MM.A:EPR7$N"N__ +M"=M.A:EPR7$B"N__!-L5!D_VSW"@`"P@$(#/#*(<(/RB+"!\H@@@\``*@3RB."#P``D`'*)$(#_`7B]\HE +M0@//=H``N!L`A@'@`*87"%$``=G/<*``R!PQH&8+(`!"XA2"-`,]QH`#L)P:A`(9"($"``*8&],]PH`#('+&@P01/]O'` +M4@Q/]L]V@`"X&P"&`>``I@#=%0A1``'9SW"@`,@<,:`*"R`'*'#/0+8 +MB^@@AF!Y`]B'Z)X*;_U0V`L(G@$`V`+P`=@O(0<@SW"``)PNSW>``'PD0@CO +M^0"GSW&``"@T%($!X!2ASW&``+@;`($!X`"A%0A1``'8SW&@`,@<$:%J"0`' +MSW&``&PF!($K"%$`)H'/=J``["=@>0#8SW"``+"Y&(B7Z,]P`0`&`0:FSW`2 +M``8$%O`*(<`/ZW+/<```AQF*(\4)2B0``)4#[_<*)0`!SW`!``"'1"B^`\;8DK@&IB"%)W=@>0#80#84PB0!,]P.0`",P:FSW`Y`(),!J;/<#D` +M`F8&IL?8E;@8\$0HO@,`(8]_@``(3L?8DK@&IL]P```",P:FSW```(),!J;/ +M<````F8&IL;8E;@&IHH/#_[/<(``L+D8B,]Q@`"PN3X/(`0@@2\)$"#/<``` +M`FX&IL]PP0!";@:FSW`#`,)N!J;/<#8`0I<&IL]P`@!":P:FSW`0`(=R!J8% +MCQ"X!2"`#P``0G`&I@2/$+@%((`/``""<`:F`X\0N`4@@`\``,)P!J8"CQ"X +M!2"`#P```G$&I@F/$+@%((`/``!"<0:F"(\0N`4@@`\``()Q!J8'CQ"X!2"` +M#P``PG$&I@:/$+@%((`/```"<@:F`8\0N`4@@`\``$)R!J8+CQ"X!2"`#P`` +M@G,&I@J/$+@%((`/``#"0#8)0@0`R"%8'D`V!D($`0@A6!Y`-@1 +M"%`$((5@>0#8$PB1!`R/$+@%((`/``#"?P:FSW`!`$9J!J;/=Z``R!^D%Q`0 +M%0D0(,]P4`#&0#8*0@0!""%8'D` +MV"$(4`0@A6!Y`-@5")`$SW"```9T!J;/<(``!W0&IL]P@`#&"*`&>8LDV!C9S@[@!C/:+PA0`,]P@``H-%`0!`#/<(`` +ML+D,$`4`"B'`#^MRSW```(H9^0#O]XHC!P0#8(0@0!""%8'D`V!D( +M4`0@A6!Y`-@-")`$SW!E`,)N!J;/<(``N!L`@,]Q@`"X&T(@0(``H03T`-A1 +M'QB0+04`B$`I` +M(A(&0"(/"$`B#00Z8D`G`7(4>0!YSW&``)@G2'!5\,]Q@`"X)P1J4?#/<8`` +MV"=`(@`"2_!`(@`#SW&``)@GC@DO_@#:!(;/<8``N"<$N!1XN&`[\$`B``?/ +M<8``F"=N"2_^`-H$AL]Q@`#8)P2X%'CX8"OP0"(`!<]Q@`"X)TX)+_X`V@2& +MSW&``-@G!+@4>$)P&_!`(@`)SW&``)@G+@DO_@#:!(;/<8``N"<$N!1X`G`: +M"2_^`-H$AL]Q@`#8)P2X%'@B<`8)+_X!VNX(+_YJ/'`SW"``/`E +M#X`1Z,]P@`"PN02`SW&``#"Y`K@4>#A@SW&``/@G&@T/_M'`X'[@>/'`>@TO +M]D3:SW"``,1-SW&``)3'R@U@!`#>`MT6""``R7!AO?D-=9`!YKT%#_;@>/'` +M0@TO]@#:SW&``$P=%7E@@02X`""0#X``1$NY&Y@``($$$`\@SW:``,1-OAC8 +M`Z"!0H:*(`/'`O@PO +M]A+9J<$(=FX+8`:+<$HD`'$`VJ@@@`(6)(`P*(@+"9(`8;DHJ`'B`L(!P\]U +M@`!,'=5]`(6*(0`3@`@TO]@C: +M`,`@A;D9&``@A;D1``85"!X`OAG8`R"%OQ$`!H"X"/"^&1@$((6_$0`&H+@B +M"Z_\OQD8`(3H$@N/_`3H`-@#\`'8$':\">$&RB"!`P"%N1`!!E$A0(#QV<`I +M(@'*(8$/``"3`,`I(0'*#:_ZA!A``"4$+_:IP.!X\<#""P_VSW:``(`[SW6` +M`!P'$ND@AHWI`*7*"._X#M@F#&_^BB`0``'8`*8.\""%)7@+\(H/K_@.V/(+ +M;_Z*(!```-@`I@"EY0,/]O'`9@L/]L]Q@`!$)`"!H+@`H=8+;_L!V,]P@`"T +ME0`0!`!,),"`RB'-#\HBS0?*((T/``"!#,HCC0\``-H`A`2M]\HE[0"G#'0` +M`-T4;0`@@0^``+25!Y'&D>21$+@%?@610Y$0N`5_`I$0ND5X&G#2#^_WR7%: +M<,]P@`#,5/`@00-$+3X7"B%`+@`A@'^``%PV(*`F"^_Z"G`(<0`A@"^``%`V +MI@@`!0<.Q!.8[\]P@`#`5/`@00-$+3X7+W8`(8!_@``$-R"@\@KO^DIP"'$` +M)H`?@`#X-G((``7/<(``M)4`@`'E:0T$D-D"#_;@>.!^X'C/<8``H"3/<(`` +M;";@?R*@_!P(M/'`&G#/<(``:"0@@&!Y`=B!X,HAP@_*(L('RB""#P``GAG* +M(X(/``"H`0'8@>#*(<(/RB+"!\H@@@\``)@9RB."#P``%0'*)&(`0`.B +M]\HE0@,`AIBXF;@`I@#8CK@!I@/8H:ZBK@ZX`J;/=8``;"1`A0;88'H"V4"% +M!]A@>@+9`HXI`B_V`*[@?N!XX'[@>/'`SW"``&@Y`(!S"%0!SW"@`*PO&H!2 +M(```8P@?`,]Q@```+H<]P@`!4)`"`0'C.#P``SW"``%`D`(!`>`X) +MP``V"<_]B@\/_,]PH`!X10"`!""`#W````!!*#Z%]_7/<(``3!TC@$B!-)%3 +M(@``1@Z@`@';,@VO^!+8T<#@?O'`X<6TP<]UH`"T1W$5`)8$((`/<````$$H +M/H7U]8H@_P]O'1B0:QT8D)X/[_B+$"(`=@`H6BZ`KI5>L=R@`#P)6." +M8Z%A@F&A8H)BH62"9*'@?P"BX'CQP)X(#_;/=X``;#D&AP.`SW6``!R)((!) +MA0`B@`\M`,#&`GF!"7(`H<'/=H``N!L`A@'@`*87"%$``=G/<*``R!PQH#8/ +MH`8H<(MQ1@_O]D+8`(9"($"``*8']`#9SW"@`,@<,:``%`0Q!"2^CP``%__* +M(<(/RB+"!\H@@@\``*83RB,B#&@!HO?*)2(``(6"N&8.(```I2(((``!V`"% +MHK@`I2F%QW$M`,#&R@W@!.EP50`O]J'`\<"^#^_U`-K/<8``-#X`@;O!5\`$ +MB4HD`')XP,]P@`!,'0.`"(#`N$#`7Q2`,,]Q@``0%4'`.,!"P%X4@#!#P!J! +M.X$$>3&YP+FH((`"`-L`)(`P9!C"``'B3WK/<(``'(EBD,]P@``P!D"08PN! +M`,]S@`!PY@Z+SW6``!R)AB#_`2@5C1!#N`(@0(.OBW"+RB!B`(8E_Q';;<]U +M@````IA,(40#/ +M<:``R!P!V!&A/@V`!C?`SW>@`.PG$+@%(($/``!"+2:G!2"!#P``@D8%((`/ +M``!"8":G!J?/<`@`AQ`&IP"&0B!`@`"F!_3/<:``R!P`V!&A`,#/<8``H+`6 +M>62!0('/<`\``/P*NP1[R;IE>L]SIP`42$VC18$A@0JZ1'C)N25X#J,Z#H_] +M1L``P`KHBB'_#\]PH`"T1V\86(!K&%B``-@#V43`4<%(P,]Q@`!$B0AAJ0@S +M`D?`",$%P!$@0(!^`P$`!\``)`$P9!&!`('A;@,A`(-P`=ED&$(`!\'/<*`` +MM$=@&%B`SW"``$P=`X`0N9NY,B"`#P``V`*?N8#@`=C`>`^X)7C/<:``M$=? +M&1B`SW"@`+1'<1``A@0@@`]P````02@^A?7U`MD`V!IP!\`1(`"$_`(A`%#! +MSW"G`!1(7!@`!$D($"`K"%$@BB#$-HHAA#@@\!P4!#`*(<`/ZW+/<```JQ.D +MVU$&;_=*)0``"B'`#^MRSW```*XHU]M*)```-09O]PHE``2*(((]BB%"/P'! +M`L`B>$G`!\!Z"Z_["G$Z<`?`I@FO^PIQ2L``A@'@2B(`(`"F%0A1`,]QH`#( +M'`'8$:%^"X`&0"E`(1!X$+B!N(>XC+@&IR"&0B%!@`?TSW*@`,@<`-@1HDHD +M`"&*=4`@@#$0>$O`0"&`,1!X3,!`*$`A3<`*)H`D`>%AO2"F$PE1`,]QH`#( +M'`'8$:$F"X`&`\`U;0`E%Q8O)\@E)7@0>!"XA2"*``:G0"^`(8&XE[@`)5,6 +M!J#00B0'@#W@0J0#` +M"^C*"$_\$PA1``#8=L`$P("X#WA$P`#`SW*``*"P`[@5(``$&6(:8@R"*($2 +MPD[`#<"V>``@E0^``%2)$\#P'8`@]!T`(`G`B")\`"\A`"`$*;X@E@[O^B]P +M#B"!#P````%/P1/`B"!\``0H?@0O<'H.[_H.P0X@@0\````!#\`)(8,/``#_ +M`0D@@@\``/\!2"("`$@C`P`VP%0=F"!5'=@@(0A1``K!&!0$,`2Y0"R``3A@ +MM7C'<(``W+!"L&.P`(8!X`"F%0A1`,]QH`#('`'8$:'."8`&"L$&P$`O@B&! +MN@2Y!K@X8+5XQW"``-RP(I`\>1"Y)7I&IR*0P+FX>04A@00O(D@@(Y!`*X(A +M@;H\>1"Y)7I&IP.0P+BX>`4@@`4O)@@@`(9"($&`"/3/``IA4(40#/<:``R!P!V!&AY@B`!L]P +M"`"&$`:G`(9"($"``*8&],]QH`#('`#8$:&Z"X_^SW"@`+1'<1``A@0@@`]P +M````02@^A?;U^@N/^,]P@```'9SW"@`,@<,*!+V<]PI``<0"2@X'[@>,]Q`0"L/L]P@`!8 +M).!_(*#/<8``'(D`@8"XX'\`H>!X\<`2">_UN'!3(($`SW"``#14*&"!X,HA +MP@_*(L('RB""#P``E1G*)((/``#^`#`"8O?*(^('SW:``&@D((9@>0'8)PA0 +M`""&ZW5@>0'8N'#/<```EAD*(<`/J7(BVP$";_>*)(,/&0'O]0'8"=G@?R"@ +MX'CQP,]P@`!H)""`8'D(V!!YSW"``*P)_@^/]HH-H`,'V&8.C_WN#P``*@@` +M`-'`X'X(<5B)`8`"H8CJ68F`XL(@H@#`(*$``J'@?N!XX'[@>/'`2@C/]<]V +M@`"X&P"&`>``I@#?%0A1``'9SW"@`,@<,:`&#V`&*'#'V)2XSW6@`.PG!J7/ +M<`,`@BL&I<]P`P#"1`:ESW`#``(L!J7/<`,`0D4&I<]Q``#"=,]P`P#"=`:E +MSW`#`()O!J7/<`,`@FP&I<;8D+@&I2:EK@Y@!@K8SW```()L!J6>#F`&"MC/ +M<````BP&I9(.8`8*V,]P``!"10:E@@Y@!@K8SW```()O!J5V#F`&"MC/<``` +M@BL&I68.8`8*V,]P``#"1`:E6@Y@!@K8SW`3`,8`!J5*#F`&,M@`AD(@0(`` +MI@;TSW"@`,@<\:"]!X_U\@+8 +M`!0!,4"%`]A@>L&YB0:O]:'`X'CQP`X.K_4#V,]V@`!@)""&SW6``&@O8'FB +MP0;H((9@>038ANA)`R``2!4$$`/8&G#/=Z<`%$C/=J``["<&#*_]!=@.I<]P +M@`"X&P"``>#/<8``N!L`H1<(40`!V<]PH`#('#&@B@Q@!BAP`]B:#*_VJ7$$ +MV)(,K_8B;078B@RO]B1M"]B"#*_V)FT/V'H,K_9`)0$2-MAR#*_V0"6!$C?8 +M9@RO]D`E`1,XV%X,K_9`)8$3"(<$I0V'!:4.AP:ESW"G`)A''(`'I1>'"*46 +MAPFESW"K`*#_&(`+I<]PJP"@_QF`#*7/<*L`H/\:@`VESW`%`,8#!J;&V)"X +M!J;/<"P``@$&IL]P6@!"`0:FBB"+``:FSW!``(<-!J;/<-$`P@T&IL]PP``' +M#@:FSW"``+@;((`1"5$`SW*@`,@<`-@1H@'8"*<`V`VG#J?/<*<`F$?/`0H@`\``'0)%84WA0)Y#@ZO]2]P`<)/X,]Q@`#(BA2E5Z$8H<]P0`"' +M#0:FSW`1``8.!J;/<(``N!L`@,]Q@`"X&T(@0(``H0?TSW&@`,@<`-@1H8MP +M,@M@!('!-H4`P")XK@MO^Q*E,H55A2QX-X4O($`.0GDY8:8-K_4U>>"X''C` +M(&(`@B#$`L]Q@`#(BA*E$Z46H<]P@`"X&P"``<(!X%6ASW&``+@;`*$5"%$` +MSW&@`,@<`=@1H8(*0`8!E1"XA2"$``:F`I40N(4@A0`&I@.5$+B%((L`!J8$ +ME1"XA2"/``:F!940N`4@@`\``((-!J8&E1"X!2"`#P``P@T&I@>5$+@%((`/ +M```"#@:FSW"``+@;`(#/<8``N!M"($"``*$'],]QH`#('`#8$:$$A2N%"*<% +MA0VG!H4.IPB%%Z<)A1:GSW"K`*#_.*`LA3F@+84ZH$8.;_T.A4@5!!",)(*` +M1?:,)#^!#?;2"6`&"MA^#``$0B!`((#@`@7-_T@5!!",)(*`1?:,)#^!#/8* +M(<`/ZW+/<```M!F*(T4,$00O][ASSW"```<4`(@&Z,]P@`#(+P`0!`"(<`4# +MK_6BP,]P@`!H+^!_%(#@>,]Q`0!H6,]R`0"P3OT$+_H`V.!XX'[@>/'`SW"` +M`%0D`(!`>,]P@`!0)`"`0'C1P.!^X'C/\@HAP`_K@`4`3$`%`4Q3"4`@,PE8H#,):*`RB'"#\HBP@?*(((/``":&@/90(4%V&!Z`]GQ`*_UH<#QP(X( +MC_7/=8``N!L`A0'@`*4`WA4(40`!V<]PH`#('#&@1@\@!BAPSW"```8ASW&@ +M`.PG!J'/<(``1CH&H<]P@`#&4P:ASW"``,8D!J'/<(``!CX&H<]P@`"&5P:A +M`(5"($"``*4&],]PH`#('-&@SW"G`(A)T*"!`(_U"-G/<(``V,?@?R.@\<`& +M"(_USW:``+@;`(8!X`"F`-T5"%$``=G/<*``R!PQH+X.(`8H<,]P``#"+,]Q +MH`#L)P:ASW````)&!J'/<```PE\&H0"&0B!`@`"F!O3/<*``R!RQH!D`C_7Q +MP*8++_@6V/(+``3/<8``3!T`@<00``8/"%\!`8'$$``�A>`98+;_@3V,]P +M@`!()""`8'D+V-'`X'[QP*H-[_N*((@%#NB.#>_\`-C/<(``8"0@@&!Y!-B` +MX$0,`O_1P.!^SW"``$P=`X`(@,]Q@`#8QPD('@`!B0/P`HG@?P"IX'CQP+AQ +MC>@*(<`/ZW+/<```IQF*(\0+70`O]XHD@P_/<8``V,<@@4PE`(`$(8$/``<` +M`$$I`P8`V@L+@0`!X='`X'X*(<`/ +MZW+/<```J!F*(P0."0`O]THD0`#@>/'`X<4`W<]P@`#X!:8((`"@H,]PIP`4 +M2*B@#0=/]>!X\<"AP;AP`-A`P%,E@``G"%``10B0`$\($`$*(<`/ZW+/<``` +MJQF*(XH*M0?O]HHD@P_/<(``:"0@@&!Y`=B$X`'9P'G/<```(M(T>,]Q@0#+ +M%P_PSW```"/2SW&!`,X7!_#/<```)-+/<8$`T15EA0,"+<#(-H`,#VJ'`T<#@?N!X\<#AQ;APSW`L``8!SW&@`.PG +M!J'/_VBB2##\]S@`!,'6.#:(,5"QX`SW.``,8@9J'/$,]U@``&.J:ASW4#`((""?#/=8``QCVFH<]U`@""`J:ASW4$`,8Q +MIJ'/=4H`0@$@\,]U@`!,':.%J(47#1X0SW6``(93IJ'/=0,`@@((\,]U@`!& +M5Z:ASW4"`(("IJ'/=00`QC&FH<]U3`!"`::ASW&G`!1(=Z&`N!JB&01/]>!X +M\<":"T_U`\@!W<]VIP`42)00``"HI@0@@`\!``#`Q@[O_RZX_]B;N,]RIP"8 +M1QRBSW&``/@%`($`WX#@RB'"#\HBP@?*(((/``"L&!X\<`J"T_USW"F`)P_&8"M"!X`SW:```0&`(9& +M@*`2``8O*`$`3B"!!T$IT``1"-4@2'"`(`H`,B``!)#H"B'`#^MRSW```*T9 +MBB-+`HHD@P\M!._V"B4`!,]U@0#`%T`EP!)Z#"_W"=D`V+H)K_\/(``$@.`` +MV`\@``0%]"8,S_\#\+(-S_\#R+D0@``;>("X0(8*K2:"EB%!`P`A``08B(P@ +MPX\"<07R8;@/>!BI)H*@$0`&GQD8`,H+S__1`D_U\<#/<(``:"0@@&!Y`=B! +MX,HAP@_*(L('RB""#P``G1G*(X(/``"/`"D_U.G`;?<]PI@":EP((99"!$$`(8!X`"F%0A1``'9SW"@`,@<,:!F""`&*'"+<78( +M;_:*(`<%`(9"($"``*8&],]PH`#('+&@`!0%,5<-T``*(<`/ZW+/<```B1GJ +MVZ$"[_:8`,]Q@`!, +M'4V!/I%3(@``I@W@`0';T<#@?O'`X<7/=8``8"\`A1L('P`V"4`#%@Q/^T8/ +MC_@"#L__`(6`N`"EW0!/]>!X\<">#J_[BB`$`A'HV@V/_\8/S__/<(``8"0@ +M@&!Y!-@%Z*8/3_\."```T<#@?N!X\<`R"$_USW6``&`O`(4Y"%\`SW"``&`D +M((!@>038%.A.#J_[XM@0Z#8.+_T'V)()X`,(=GX-#_\R"R_]R7``A8&X`*59 +M`$_UX'[@>/'`X<4(=9AQ2'!H<0#:#@@@`*ES10!/]?'`R@\O]?AP620<.-AQ +M&7*X,HA)@`R)`TP`"0.,`'CH*]`K@#9*PAT$"AR`>$O>3(D +M33!"($@0`"1#,+IB3WHR)(XP`"2`,,"KXPAUD*"H3"0`@,HD#7'H("T'`>$O +M>3(D0#``)$,P&F)/>C(DCC``)(TPP*L`K3(D0S!X8`]X,B0#,``5@`!G>`$= +M$@!M!R_U5B0<./'`_@X/]:00`0`5"1X&MA`!`<]PH`"8`SZ@GO``%@U!O+`` +M%@)!7;``%@Y`SZ``%@)!0!B$```6`D!1H``6`D%(&(0`1"4"$S4*$`$8VW(8 +MQ```%@-`S$-L,\![;&)_T< +MC"<"D@GT`N-P>W(8Q```%@]!`O``WV`8Q`,)"UX``!8/02ATA"0,D`3T`-HB +M\)GJ428`D-$A(H(5\M"(J+G/!XNYI!A```#: +M6J!;H.;Q`!8"0%J@`!8"0%N@"-IT$`X!OA`/`<)_8G]"?[@0@@"8N:080`#/ +M<:``F`-"?WIB4'IR&(0`NA`"`?!_3I@0H(!YCA@#>K/<:``+"`P +M@2)ZUW)):P#2`-_#]^*@JN;*)B80X'C%!2_UP*7QP,]P@`!<#@[9`=KJ"B`` +M`-O/<(``E`X)V0':V@H@`$ASSW"``(@-*MD`VLH*(```V\]P@``P#@O9`-JZ +M"B```=O1P.!^X'CQP`38$@AO^P'9SW"``/TO`(C/<8``_B^:"R``((G1P.!^ +MX'C/<(``H#M-`@`$X'CQP(;H[@@```#9(J#1P.!^\<#2#`_UI@R/^\]V@`"\ +M!F;8(FX!VOX,;_Q("``E4>)30B!``"608E%"($`SW"``#0&`(@FB3D)`0#/<(``3!T. +M@"T(7@'/<(```#!`@,]P@``$,`KJSW&@`"P@,(%">7,)A`\Q`0`M`-DV\(PF +M`H`I],]P@``DEC>("NG/<(``L+D!@!!Q`=G`>;[QSW&``/2."XF&(/^,$_+/ +M<(``L+E!@&2)#0K```")$'(`V`/T`=@/><]R@``$,`"J#?``V:+QSW"``+P; +M((C/<(``!#!,Z0'9(*C9`B_U*'#QP$H*#_6AP1IP.G)H=KT)<@``V)IQ%2`- +M(,]Q@`"\!@`5DQ`"%9(0NG#CC2&1`8T!VCA@$'B+<6X*;_Q(Y5'HS"1`@QW*``/0.&/#/<(``O`;!D*&-"B'`#^MRSW`` +M`+L4BB.$```F1!,U`Z_V"B5`!<=R@`#4#P`:P@0#[@*J`O`!JB4('@`,[@.* +M@+@#JA)O%'@;8F.+6&"!NV.HY*H#[B:J`O`EJD(D02!5"76`0"5`(-$!+_6A +MP.!XX<53(`T`H*D$(($/``8``$(A`8`$((`/0````,HA8@`@JM=P0`````'8 +MP'@`J^!_P<7@>/'`C.AN#<__SW&@`"P@,(''<4EK`-(BH-'`X'[QP$8)+_78 +M<0HF@)"(=4.( +M`^$"$(4`(PH?``HAP`_K!X +M\"`'B24(P0$! +MB5,E`A`9"@$`!"6-'P`&``"`Y0':!HG`>AT*``#/<(``O1O`J,]P@```,,"@ +MSW"```0PP*@O(`<"'*/'`I@K/_QH*S__*"D`%H@F/_%X(0`'1P.!^X'C@?N!X\"^__R7"(Z)#9`\B0N:`80`"I"#/=H``,`8`EF>*SW&``+P;+0L! +M`,]P@``R!@"088H="P$`SW"``#0&`(A&BA$*`0#/<(``O1L`B`/P`-@`J0#= +M/@OO_ZEPSW"``#0&0(C/<8``,@8`B2".@.(!VL!ZZ',Z#>__F'7/<(``7`8` +M@`&(SW&``!07"P@>`0'8`*D"\*"IQ0;/],]Q@`!X(,]P@``P!@"01XDQ"@$` +MSW"``#(&`)!!B24*`0#/<(``-`8`B":)%0D!`,]P@`"\&R"(SW"``+T;(*C@ +M?N!X\<`.#L_TSW:``"26%(XI"%$`!-@""2_[`=G/<(``,@8`B,]Q@``P!HH, +M[_\@B0#8%*XM\+:.*^W/=X``_"\`CV&X)0T`$.((S__/<($`,!,%@"%M!2A^ +M`,]P@`"@.XH+X`,O<<]P@``R!B"0SW"``/TOH*\@J,]P@``P!B"0SW"``/XO +M(*@`V!:N-8X)Z<]P@``R!IX([_\`B`#8%:[9!>_T`=C/<*``+"`P@,]P@``` +M,.!_(*#@>/'`5@WO]/AQH<$(=AER`-W/<*``M`]P$`D`SW"@`+0/O*`%AN.. +MBW%`)(,PF@OO_T`D0C`*AJX)[_]`)$$P*0]4$!0@3!/P)TX#((P@P`$4@C`" +M%(,PP@OO_U,F!!`!Y>,-Q),O($<"SW&@`+0/'*%-!>_TH<#@>/'`3@W/_XH/ +M``71P.!^X'CQP.'%SW"``#S'`-DEH,]P@``4*2*@SW&``$P=`('$$``&=PA> +M`0.!&(AO"!`!SW6``!`X`(5"(`"`RB!B`"<(40#V">`#J7#/<8``V#<`@4(@ +M`(#*(&(`A>@H<$H*X`,BA<]U@``L.`"%0B``@,H@8@`G"%$`P@G@`ZEPSW&` +M`/0W`(%"(`"`RB!B`(7H*'`6"N`#(H6Q!,_TX'CAQ0#;SW*``.C'%"(-`&"U +M:+4:8B`:P@#`'<00*!K"`,]Q@`"4QQ9Y(I$P&L(`T!W$$(`=W!!X'400`=F( +M&D(`SW&``(C(%7E@H>`=Q!#P'<00X'_!Q>!X\<`R#^_[$=BYZ,]Q@`!X(,]P +M@``P!@"01XE5"@$`SW"``#(&`)!!B44*`0#/<(``-`8`B":).0D!`,]P@`!$ +M)`"`FNC6"L`"B.@+R`4@@`\````\"QH8,,8*P`*(Z`O(!2"`#P```-0+&A@P +M"\B0N`L:&##""T_[`_!.",_UT<#@?N!X`-F$T`3_O@ +M?N!XX'[@>*'!\<#AQ:S!`-E*P9#9&+E(P<]S@`#8QR"#!""-#P$``,"&(?X# +M)+D.N0LE0)!.P(["%O+7=0```$#,)8*?````@,PE@I\!````!/0A@P/P(H.N +MN*^XL+@%>2"B#L,(P(MU!".!#P$``,`NN4`I`@9%>$C`BB`&!DG`0<.I<`#: +M2@D@``';SW&``!`5&H$[@21X)P@>`@K`"\&$*`0.`"&`?X$`4!4"N0C@-'DA +M8,]PIP"(22^@)@X@!*EP"-SW`N_TK,"AP?'`=@KO]`ARK<$(V$K`D-@8N$G` +MSW"``-C'H(`$(8X/`0``P(8E_A,DO0Z]"R9`DU#!D,,6\M=V````0,PF@I\` +M``"`S":"GP$````$]`&``_`"@*ZYK[FPN25X`*,0PPG%!".!#P$``,`NN4`I +M``8%?4G%'PJ>`0K`!".^CP```!A%(,``2L`%\H4@$`%*P"4*'@&;O<]PH``L +M(`6``-L"N&ZX@.#*(,P`R;BE>$G`!O`)"AX"G;U)Q1#`@<5"P*EP0@@@``+; +M`\@,PL]Q@``0%;D8@@`:@3N!)'@;"!X"`KK/<($`6!54>D%@SW"G`(A)+Z`B +M#2`$J7`(W.L![_2MP/'`<@G/]*/!88`(=4##`-@*I6T+7@($(X`/`0``P"ZX +MSW*``'!*"F))(H(`8;I+I1)J%'C'<($`2!;*@,]W@`!\R<:E"X#/=H``3!T% +MI<.&(,#4AO6/!'[D?@F^0"D/`N5^Q7@$(X,/````$&5X!Z4(A1CBGK@(I4NE +MC_`W"IX"SW"``,@Y`(!!P$+`(0@>`H8@_PDCN`'@%0B4``L(D0`&V&'`)/`' +MV&'`(O`BP&'`'O!!P\]R@``$!D""1H*>$@(&*PJ1`00COH\````8#_3/<$)HX?!@```#&^`";%$\]V@`!P2C(F#A$")DX1 +M$_!3)L(0SW>``%Q-77I*9P0FCA\!``#`+K[/=X``<$K.9V&^UGI+I1,+'@(@ +MQ\]V@`!X2NYF`O`!WH0H!`X`(8!_@0!0%0*Z5'I'8&&^6&#FI0&`!".##R\` +M`-TFN\5[4B/#`P6E9Z7/<(``V,<#@`#?'0A.`,]P@`#H.0"`%0@>`(8@?P\= +M>$`HSP,$\`#?C[^;[\]V@`!@)""&8'D`V"4($`,@AF!Y`-@9"!`$((9@>0#8 +M$0A0!""&8'D`V`L(D02R#2_\`-@(A05_Z*7=!Z_TH\#@>/'`<@^/],]U@`"` +M)`"%Q)#))*H\]SH`#$)Y$3`8;#N1L)@0"*)0@0$QM8@Y$3`8;#N0L)@``2&UB#?0>/ +M].!X\<`"#Z_T`-C/<:``Q"=2$0*&%1$"AD(1`X81"YX'`=C/<8``1,EAL5$B +MP(`:<,HE8A02]%$@P,;*):(4#/3/<*``T`\@$`&&'Q``AA!Q`-W*)6(5Z0T1 +M$,]V@`!$R1^&RP@>!*@6`1"4V&(*8`+)22RBB"( +M!<]R`/\`_XH(3_T`EHH-;_TTEI07`!#/`\]P@`#H!2"@SW"@`/PE$X!L +M@GA@#*+/<@"@"`#L<$"@;R)#`.QP0*`.'UB0E@U`!<]P``#_?\]QH``,)`&A +M&]@$H2D&K_2I<.!XX'\#V/'`X<6AP2X/+_N+<++H`!0%,!T-'@!^"```SW&` +M`$3)0X'/<8``M)E!H23P"PV>`(H.S_\>\`T-7@(:#L__&O`[#=X`"-C/=:`` +MQ"<3'1B0I@W``!T($`4"V#P=`)#/<(``1,DC@,]P@`"TF2&@&=B7"%"&P06O +M]*'`"B'`#^MR%]B,N(HCQP"%!B_VBB2##_'`X<7/<(``1,D_@`0A@0___X\X +M!"6`7P``<,,]Q@`!$R1^A1"(`4\]U@`!$R4,($0(_#5Y1-@_/_YP=`!`3 +M#9Y3SW"``*`=!8B8'0(0%/`5#=Y3SW"``+`@&8B8'0(0#/`#A7H-+_8DA9@= +M`A`$\`#8G!T`$)P5`!"`X,P@XH!>\L]PH`"H(`B`'X41"!\!#0W?4H#8F!T" +M$)@5@!!`*`$&$0C?`8*Y'PJ>4T8-``(;\!^%42*`T[.X'Z7%(8(/````!T4A +M``;/<8``T,DLB88A_0]2(<$!1;DE>,]QH`"()!"ABB#6`,]QH`#$)WX9&(#/ +M<*``U`L!VE*@!-@0&1B`SW6``$3)'X5'")X!%)5#"%\!SW"@`"P@#X";Z*UQ +M^@XO^58E0!6`%0`0E+B`'0`0'X60N!^E#?#/<8``.#(/@0'@#Z$0V<]PH`"0 +M(SV@502O]!G8\<#2"Z_T`-D(=@&`P;B#X,H@02`%\B(.(`#)`1Z%E;@>I7GP>@PO^T\D0`+K +M"!4$SW&``."/F!6#$/`A`0!`*P(&AB/]#U(CPP%%NV5ZSW.@`,0G01N8@`#: +MC+H")D\`^F++NM=R````"$`M#P.0OU+W!2>/$6(;V(.,(@*`Q_?/<8``K#,2 +M@0'@$J$`V9VY1?#E>6(;6(!9#H5P``#`#PXB@P\````0SW*``$"/%GH@@B4+ +M-0@$$@4``-@/(,``8;A.(P\(`2G"`WAY!7D`+<``!7H7\$(C`P@`V`\@P`!A +MN'AY!2$"`(HA_P\+\,]S@`"L,Q.#BB'_#RAR`>`3HP'8SW.```"N`*L"&P0! +M(:-"H[WQ`-F(`=`!!`)@`2H!T`$`+9SW"@`/0F(Z`EAL]P@`"T +MF2&@>0*O]`IPX'CQP`8*C_0(=54@4`0-S*+![;C1(&*`!_($R,(.K_^8$``` +MSW"``,#)#(#/<:``R!]DX!ZA$-@.H0'8%1D8@`&%@^C_"Q[``87!N(/@UO0` +M$``@0<`$%`\Q$(4LOP84$C%U")X!#`\]P@`"P(!F(!O`%A2:%+@H/]N>XF!X"$,HF81`&\CZ&E;D^ +MI@#>!+C/<8``2+!&D>5X$PB``,]R@`"L,PF"`-X!X`FB!)$;"($/``#__P#> +M"?#/<8``.#(-@0#>`>`-H0&5G."(]`00$2`($!`@SW"@`/0F`MDCH".%SW"` +M`+29(:!J"R``J7"`X(#T)N[/T$J`2%E>2:B#1(!-QT)W@(0VJNY#!J<,`T:7##/`QK8K+D-&EPPI0X0$,]V@``0B.`6`Q!%A40K/@<`)D$>0*%,E0'C +M0K'/P!#/FI"AF$!`P90`1$DA`9``00O0R_02H# +M(>5]97U*L<]SH`#`+T<;6(.4XL`BA@\``),`SW6@`&@L\"6"$$NQCQ,"A@GP +MHQ,"AE$B`(&/$P*&!O3U"MZ!!_`(V`SPY[K*(B$`0,(!%(,PQKK&NWBI6:E1 +M`*_THL#@>/'`X<7/<8``3!TC@4B!60H>`(8@_P'/,]S@`!$R5@3@0``VH[I/)-BN1"Y12%#`<]QH`#T)F.ASW&``+29 +M0:')````\`0S,SW&` +M`+PQ,0A>`4#8#!H<,%41``8`V@'@51D8``W(SW&``.C'%'D#R$"IX@JO_Y@0 +M```'\*P1```!X*P9``#/<(``O!L!V2"HSW"``,#)+H`&@0'@!J$"V<]PH`#T +M)B.@(X;/<(``M)DAH$D&;_2I<.!_"-CQP"2Y4R'"`,]Q@``<4%9Y$PH0`D&0 +M88$$XG!RRB`B`@/T((%`>='`X'[@>,]Q@`#`R2R!SW*@`,@?9.$^HA#9+J(! +MV14:6(`A@(3I_0L>P"&`P;DA"=$`SW"``+P;`=D@J,]P@`#`R2Z`!H$!X`:A +M`-D-\"&`42$`@`#9RB'A!0&`42!`@,HAH03@?RAPX'CQP$(-3_3/=H``P,D! +MA@0@OH\`<```.?(O*0$`SW"``$`P]"!-`"N&3R6`$-8((`))AI3HC"4#D,]Q +M@`"\,0CTNA$`!@'@NAD8`!_PN1$`!@'@N1D8`!GP`88A")X'SW&``'#F#(E/ +MB1L*```1B5$@P(`D#D$"!_``V<]P@```KB"H<@P`!2D%3_3@>/'`L@Q/]`AV +M`8#!N`#?)PC1`,]U@`!$R8\/$1`0AG<(G@$0AAD(G@//<(``H!T%B!+PX@[O +M_\EP"'?M\1"&$0C>`\]P@`"P(!F(!O`%AB:&$@W/]9@=`A`1"-X!'H65N!ZE +M'X67N!^E@!4`$`0@OH\0<```#_2@2"\_Z!>@0ANVX`=@"]`#8 +MSW&``*;)]"$``#R5.&!BN!"X@+C/<:``]"8#H0;P`MG/<*``]"8CH"6&SW"` +M`+29(:`M!&_TZ7#QP+H+;_0`V0AV`8#!N(/@RB!!(`7R"@[O_\EP&G#/<*`` +M+"`&@!!X3"``H,]U@`!$R`3R5$PD#`"6&SW"``+29`H"W +M"0$`$(8/")X#SW"``*`=!8@-\!"&#PC>`\]P@`"P(!F(!?`%AB:&^@O/]9@= +M`A"`%0`0!""^CQ!P```*]#X*S_H+Z!"&$PA>`P'?"/``WQCPB@N/^Q3P`-\P +MA@(.[_A6)4`5@!4`$*@5`1">N(`=`!!`)@`2H!T`$-D)7H(!V<]P@`"\&R"H +MM!4!$`:!`>`&H5@5@1#/<*``]":3Z<]Q@`"FR5R5]"'!`UEA8KD0N8"Y"/"T +M%0$0"X$!X`NAR/$"V2.@)8;/<(``M)DAH`$#;_0*<.!X\<">"D_TSW"@`*@@ +M"(#/=H``1,D/#9Y3SW"``*`=!8@,\!$-WE//<(``L"`9B`;P`X8:"^_U)(:8 +M'@(0'X85"!\!$0U?4PT-7U*`V)@>`A"8%H`0%PC>`3^&E[D_ICZ&E;D^I@#9 +M`=T6\)P6`1`E"5$`/X91(4""SW&``$P=(X$I@07R1"$-!`7P1"$-`@/P`=T$ +MV1BX)7C/<:``B"00H1^&,PB>`126*PA?`;()0`*1Z,]PH``L(`^`!>@-S!<( +MW@$?AI"X'Z:M<;X,[_A6)D`5F^T+"IY3A@K``17PAB+_W,]Q@`!$R0_T`8$; +M"!X`F!&``,]Q@0#("@*X%G@`8?ZXH`X"^\]PH`!0#""`SW"``-P&!-H@H,]P +MH`"0(UV@SW*``$3)'X(-"-\$#X*`X`#8*?(7@HH2`@$9803B"PB?1/\)'L;/ +M=8``1,F8%8`0Y[@`VPGT`KC/`-C+;O`NXH5`!%/%8T0SW:``$P= +M\";#$$)YHGAV#"_[3Y.)`4_TX'CQP.'%H<$`V$#`SW&``#@R#X$!X`^A`]G/ +M<*``U`LQH.!XX'C@>.!XX'C@>.!XX'C@>.!XX'C@>.!XX'C@>.!X,:`0V,]U +MH`#$)Q`=&)`Z"N_ZBW"6Z``4!3`=#9\`"B'`#^MR#=B,N(HC7P?U`>_UBB2# +M#P39$QU8D!O9%AU8D`D!;_2AP.!X\<#/<(``-*TN"B_V&-G/<(``L(LB"B_V +M&-G1P.!^X'CQP&8(3_0:<,]UH`#4"Q"%`-ZAP4#&(0A0``HAP`_K<@_8C+B* +M(Y8(BB2##XT![_4*)0`$SW&@`/Q$&8$$(+Z/```((`+T'8$1"-`DD@GO^HMP +M@.#*(`(@0B#!()3A2@$-`#(F07"````\0">``( +MH=H*P```V2AP//#/<(``P,DN@`>!`>`'H?;QSW"``,#)+H`,@0'@#*'N\<]P +M@`#`R2Z``H$!X`*A(O#/<8``M#(%@0'@!:$<\,]P@`#`R2Z``X$!X`.A`=D` +MV!3PSW&``*PS&H$!X!JA_@N@`P'8RO'/<8``K#,4@0'@%*$!V`AQ@.$X"8(` +MSW"``$3)'X`7"-X$SW"``/2.RZC/<(``*(S,L`/8$:7@>.!XX'C@>.!XX'C@ +M>.!XX'C@>.!XX'C@>.!XX'C@>!&E@0`Z""``!*&\\<]P@`#` +MR2Z`$8$!X!&AM/'/<8``M#(.@0'@#J&9\0HAP`_K/'` +ML@X/],]P@`#`R0R`SW6@`,@?$-X!WV3@'J7.I14=V)-&#>`$"=@#V!ZESJ45 +M'=B3SW"@``PD!X`$Z/<+'L#9!@_T\<#AQ<]Q@`!,'2.!*8%1(4"`RB"B`"?T +M1+C/<8``,###N`EA"0D>`#4-GU$U"5X`SW6``$P=`X48B"$(4``^#8_Z".C/ +M<(``E"`(B`T(T`$#A1B(#0B1``D-GE$!V`/P`-B%!@_TX'CQP/X-#_1$(A%3 +M37>&)_P337`$(I!?````0`0E@%\````@02A^@P7RZ@R/^H/H`-X"\`'>SW6` +M`$3)'X4)"%X$`-V\\/T)$:#*#(_Z'NC/<(``E"`(B(?@S"!B@A;T`86,(/^/ +M$O0DE<]P``#__QT)`0`%A8P@_X\(]`R5UW```/__RB5A$)KRSW"``$P=\""` +M`RF`#0E>`<]Q@``82P7PSW&``"1+&(@*84$O`!'/<8``,$L(811ZSW"``)Q0 +M3F`K#AX0'X4(=(0D"9`/].2^T2`B@A_R`86,(/^/&_0$E3,(@0\``/__"O`- +M#EX0'X4C")X""0Z>$`D-'E(!W0SP$P[>$,]PH``,)!&`C"#_C_;S`-WFOLHE +M(A#Z"X_Z".@$);[?````(LHE8A"-#1`0%0[>$1$($2#/<8``1,D?@9.X'Z$1 +M#EX1SW&``$3)'X&-N!^AC"<"D`[TSW&``$3)`8&,(/^/!_0$D0T(@`\``/__ +M`-V,)P*0S">"GP``4``'],]Q@`!$R1^!D[@?H<]P@`!,'0*`PA``!A#HC"<" +MD,PG@I\``%``"/3/+`9``#/<(``C(BYH,]P@`#\R:"@!-[/<(``F`;`H)D1 +M@`"@N)D9`@#/<*``Q"=D&%B#SW8``/]_$QB8@QO>%AB8@QH86(.*)_\?SW:@ +M`/Q$_:;YIHHGF!W/=J``4`SBIG&B<*(\&$"#BB,8"&ZB@!(#`*090`-1(T"` +MSW.``+296!E"`PSR0A``A@0@OH\`P```!/(!@P+H`J.AHX`:0`//$0SW>``$3)M!``H?K8 +MM@XO^P#9(-C/=H``',K^#6`#`*8!V,]RH`#('Q.BSW&``,`;"($`@&R!8(,D +M@4`F$!4`$00`^!("``#9`B"`@`&F`-@#(T,`4!\$$%(?!!!4'P00`B2!`,]P +M@`!,'6*F0X`CIA22SW&E``@,";8(@L"X"+8`$00`4R1%`5,D00!,'T(1@^'* +M(<$/RB+!!\H@807*(X$/``"<"X@#H?7/("$#!"2!#P```.`MN7^'FA]"$!0> +M`!$="]X"!+F!N25X"+8'V`?P`-D5(`P@(*0#\`38`>#S"!2""(+KN*`(@@4? +MARNX4R`%`%$@@,6F\L]Q@`"8(P:!`>`/>`:A02F`0\]Q@`"8(V:!SW&@`+0/ +M-X'`N#!S`-J;],]QH`"H(":!C"&#CB8!#0"P<(WTSW6``!S*!87/=J0`D$'U +MAC:&!""`#P```.`MN.>ESW.``$3)**4-"!X`4!O$`PGP4!N$``0GCQ___P`` +MYZ4/"%X`,+]2&\0#!?!2&X0`\'_GI0T(G@!4&T0`"?!4&X0`!"&!#___```H +MI0V&!J4$((`/````_BFX5AL$`!^#1PC>`L]PJ@``!`2`":7/<(``L(L@B$1H +M->EA"70``A"$`)]Q`-BH((`#]"(/`!7>$[[P)L\3SW:``!S)%7X!X."F'/#/ +M<(``-*T@B$1H&>D"$(0`@.'*)$UPRB`M`.@@K0/T(@\`*=X2OO`FSQ//=H`` +M',D5?@'@X*8AK0(=`A&T$P$``MT!@0'@`:$,\`0@OL]@`````_0$W03P"0L> +M0`/=@>5.\R\-D1`"W00@OL^``0``RB6B$0;T42,`P,HEXA#K#9"0SW"@`#`0 +M`X"`X,HE8A&&Y3($`@#/=H``1,DL]S@````$P=XX?HAP0G +MOI\`!@```_*,NE*FY+G*)2(2X;G*)2$2AB'^#T$I`@%-'H(0*)-%>2BS*0W1 +M$2,(M`,'W<]Q@`!,'2.!A!$!`!,)!`#/<:``,!`H@0D(0``(W8?ERB`!`5P+ +M(?O*(2$`*P,``,]PI@`(!`&`!""`#S`````TN$(>!!!"%@$1&0A?1L]PH`"H +M(`B`&6$P>?X*+_N(<`7P'@LO^XAP!""`3X`!````V3$(@0\``0```=A.'@(0 +MSW*``!S*FA:`$$(>1!!-'D(0-Z8IH@2X*)*)N"5X"+)S\$T>0A#/<*8`C`,] +M@`0A@@\X````02K`!)H>`A`$(8`/````\"6Z++A%>,]R@`!$R1*F#0C>1Q*" +MC+@2HE,APP)($HX`=Z+@OM$AXH<'W0/T"-W/<(``',HIH)H2@0#HD`2YY7DH +ML'RP,H(MH'T-T1'/<:8`C`.]@00E@1\!````,+E.&D(`J:!.$H``&^A;#E$3 +M4P@?1A38SW&@`,@?'J$0V`ZA`=@5&1B`"MU1(`#&RB7B$5$C`,#*)2(2\0V0 +MDA3P)PN4`\]P@`!,'0.`A!```!<(Q`#/<*``,!`(@`L+`0`'W0+P"-V'Y>7T +MSW:``$3)3A:`$(#@W?+/ERAB+] +M#P:Z17E*!$!2Y)7B(N$0G`1)!*<&`4B!`!1*F6!Y"$,HA@@\` +M`/__RB&!#P``$!\Z<3>&0!Y$$`0B@2__`P#_*+DWIBX,K_@`VJP>`!!Q#YX4 +M2!:#$#*&H./1(>&",/($(8*/`````0CR1"$-!B.]`>45#940!"&-#P```"1! +M#8`?````)`0AC0\&````,;TQ#=40%0V1$!3J1"$-!B.]`>4=#9$0`^K,XPKV +M5X8R(3"_#/<*``,!`(@#>&$'$' +MW&,!D`!!RQ$H;KH0VAK!8`$"@9 +M@`0=L0T-T1%Z"```"'6,[28.8``5W:H)+_L(=H#FS"`B@,HE(1"`Y90+HO_* +M($(#`-C/<:``U`L0H<]P@`#0R0V($0@>`,]PH`"()!Z`"QH<,#X,0``,S(8@ +M^8\*](3ES"7BD`;T`-B/N`P:'#`RV<]PH`#('"J@_0/O\Z+`\<"F"\_S`-W/ +M<*``U`L8@$(@``B`X,H@3`//<8``F",E@8'ABB&9#@CTSW&``$P=(X$^@8`A +MF0X0<0#8RB!M!'4($0#/``T2`3=3 +M(7Z`#?+KN3>"!?*@X0'9P'D(\([A`=G`>03P)PM?`0#9SW.``$P=8X-I@WU[ +M4B,#`,"[9'D'Z3^"D;D_H@KP-X+I\?8+``!8$H$`@.$H"P$`@.!^`@(`SW:` +M`$3)6!:`$!+H`MG/<*``]"8CH,]P@`"TF:&@P-F9%H`0@+B9'@(0*'`#\$+8 +MSW&@`,0GOQD8@`'8#!E`@Q`9&(`?AO&X(@("`!*&-X:*":_X`-JL'@`0'X;% +M"-X"SW&``$P=8X%(%H`0-(,$>40A`@%$(`$,0BD$`8!RSW&````>4R)&`#(A +M@0&)N3RF5(-P%H$0SW>``,A*!"&%`$T6@A"&(?\#1+D$)84`H''T)T$08AY$ +M$,]Q@``D(3(A@0&)N3VF=!:!$/2#)'^&(?\#1+E$?S]GSW&``,A*]"'!`V0> +M1!`RACJF=(,[IF1X!'K/<(``V$J`Q!"2 +M'@00E!X$$$X>0A.9\$X6@1#/<(``T"\`@,"XJ0D0`(#@`-O*(2(`"_1RAD@6 +M@1`$(X,/````"'M[PKD`(8T/@`#T'5"-N(W'<8``("'/=X``]*P(B65]O*9P +M%HT097K#O;Q]]"=-$V5X,(EE>3VF=!:!$,.Y/'GT)T$08AY$$UJF:!:#$&0> +M1!#/<8``!*W#NWQ[]"'"`!NFCAZ$$,]R@`!,K?0BPP!L%H`0P[@<>/0A`0"2 +M'L00D!Y$$/0B```]\(#@`-D%]$@6@1##N3QYSW"``/`=*&#/!!#/<(``%"$H8!VF=!:`$,.X''CT(@``SW&```2M4H9D +M'@002!:`$,.X''CT(0,`6J;/<8``3*WT(0``CA[$$%NFD![$$)(>!!"4'@00 +MU@M``<]P@`!,'0.`"(`/"-X"3A:`$(#@V`X"!5@6@!`%Z,(*#_\#\,8*``#= +M`,_SX'C/<:``Q"<5$0.&!-@3&1B`&]@6&1B``]K/<*``U`M1H.!XX'C@>.!X +MX'C@>.!XX'C@>.!XX'C@>.!XX'C@>.!X4:#DN^$@P@<6V%(1`(;@N.$@P0?* +M(.$%"0A>``D+W@#@?Q+8`=G/<(``O!O/`\]P@`#H!2"@%=C@?N!XX'[@>/'`R@^/\\]P@`!$ +MR3*`)PE>`L]Q@`!,'2.!2!""`#2!1'E1(8"`2-K*(H$/``"0``+P#MH`W\]Q +MH`"H(">!K!`-`%EAL7'")440RB7F$K!X7@OO^@K9SW"``"PL`)#/=J``Q"<+ +M"!X!C"4#D@/W`-T:\,]PH`"T#_R@SW"K`*#_^J"V#>_]`-@9%@"6!.@"V!`> +M&)#/<8``K#,;@6J]N&`4W1NA&18`EH?H42$`QM`-803*(&$`?0>O\ZEPX'[@ +M>.!^X'CQP.'%SW"``+P&`)#/<8``X+>HV@'=@"!$"Q!X+@_O^JES@.#*(<$/ +MRB+!!\H@@0\``+44RB.!#P``S`#*)"$`%`!A]O +M\[2@\<``V<]PH`"T#SR@%@I/]YX.#_YJ#D_[`@WO_0#8_]G/<*L`H/\YH#B@ +MT<#@?N!X\<#AQ<]Q@`!,'?`A`@!*)$``PQ(!!@]X,B*"#P``'P,$(8,/``8` +M`(#C`=O`>P0AC0]`````UW5`````PB0"`8((K_O`N;$&C_/@>/'`-@Z/\TH. +M(`((=<]Q@`!$R1^!SW:@`,0GL+@?H1D6`)8`V03H`M@0'AB0SW"@`-0+-Z"> +M#@`!=@H@`P'8!>WB"$``!?"^"$``*@K/^AD6`)8%Z`+8$!X8D$D&C_/@>`': +M`-G/<*``M`]!`+9C!A$``0E@E\``'#' +M/X!%>3^@@.6Z`P(``,#IN-;RSW6``(`D`(6*(0@`Y)#/=J``Q"<3'EB0SW&` +M`$3)/X$Z=X8A_".%"5X$02D!(<.YSW*``!Q0-GH@@D!Y"'49%@"6!.@"V!`> +M&)#/<```_W\3'AB0&]@6'AB0`]G/<*``U`LQH.!XX'C@>.!XX'C@>.!XX'C@ +M>.!XX'C@>.!XX'C@>.!X,:#/<(``C(@9@(#@\`U"`9KE&@,"`,]P@0#`%W8, +M(`,`W0L#``!.#F__*G`:<`"%Y@XO_RIQ;@\O_PAWB.?,)^*5RB7!$ROR&P@0 +M(.X*(`"!P`HE`)`<]((-[_\!P!CP`]G/<*``U`LQH.!XX'C@>.!XX'C@>.!X +MX'C@>.!XX'C@>.!XX'C@>.!X,:``W0\/D1;/<($`P!?Z"P`#@.6.`@(`8@Z` +M`<]P@`!$R1^`$0B>`P'9SW"``.0%(*`)\`\(W@,!V<]P@`#H!2"@$18`E@#= +M0<`Q")\`?@TO^H'`"B4`D!#T!!0%,!T-GP`*(<`/ZW(*V(RXBB/'"C4%+_6* +M)(,/@.4J`@(`!-@3'AB0&]@6'AB0SW"``(R(&8"`X-P,0@$+`@``X+C!\L]V +M@`!$R1*&AB`Z`(P@!(+(#84!SW&@``PD/($7AB)X9+@0>(H>!!!$(@!3%P@1 +M`A^&#PA?!%$E0-$!V`7T`-@#\'(-3_^<'@`0+NC2"$__"B4`D-?T#!*@6`1#4V.8.(`')<@?HS@I`!`OP`@I/_ZSPSW&``"@T'H$!X!ZA +M`=_/<(``O!NT%@$0X*@&@0'@!J$?AO.X#`]"^@^&@.`<#D+Z'X81")X#`=G/ +M<(``Y`4@H`GP#PC>`P'9SW"``.@%(*``V,]QH`#('`>A,-@*H:8+[_\!P!^& +M)P@>!A#8#!H<,,]P@0#`%T8*``,-R``@@0^``'#('X;@J;BX'Z8`EH8@_`", +M(`*`&O22"0_ZF.@#V<]PH`#4"S&@X'C@>.!XX'C@>.!XX'C@>.!XX'C@>.!X +MX'C@>.!XX'@QH`"6@@EO_#260_!!P!7?"PC?`.EU'_`(V,]VH`#$)Q,>&)!: +M"L__"'4K"!`%`M@\'@"0(18!EL]P@`"TF2&@$18`EM,(GX!R"R_Z@<`*)0"0 +MX?,_#5$5SW"@`)`C'H`$%`0P42"`@,HAP0_*(L$'RB!A`L\@(0/*(X$/``#D +M!!0#(?7*)2$`4@GO_XAP"'6I<`D"K_.BP.!X\<"N"8_SH<$(=@#80,``I@X+ +M+_J+<`HE`)"%],]PH``$)2*``(8$(8$/_P!?_P4A`@!`IE,A@@!3((,`97JE +M"M$!SW"``$3)'X!Y"IY3MPB?!@0@OH\`'@``!/0`A@GP`0J?0`"&"PH>0(6X +M`*;/!D\@`0*)N8VYB[F.N2"F'H($ +M((`/`@```%(@0`0JN"5X`*8M\/RYQ2""#P````7D]84@'```IB/P];@`AA_R +MAB`<`(4@&```I@$*'T$`AB\*WD"&N`"F$_!3(0,`4R`"``4COH#*)>$5"?*& +M(7\/AB!_#P4A/H#*):$4SW"``-#)#(C$N$`H`08`AB5X42"`Q`"F1`\B!,H@ +M(@BI7*)2(0+0L?0`D(7D5'"9Y#,P@? +M1]D(WL75"9[#SW"J```$`8"&(#\+`-V["-&`$_"V"@``SW&``#`S#H$!X`ZA +M"?``V9RYSW"@`-`;,*":"@```-G/<*0`F$`\H!#P`-T-"Q]`C@X@!`'8J/%Z +M"@``SW&``#`S#H$!X`ZA$0"/\PS,1"`^BCGR00C>``T2`C>`V,]Q@`"T,@P: +M'#`-"MX"'8$!X!VA!?`5@0'@%:$1"M\``-G/<*``+"`OH`W,AB""`N!_#1H< +M,"\(7@&*(`0`#!H<,,]Q@`"T,A2!`>`4H0W,`-E&((`"#1H<,,]PH``L("^@ +MX'[@?O'`X<4M"!$"*0J?4<]Q@`!$R7^!AB/W#[>!SW*@``PD'((4X`@E`!!< +M@HH1`0$1\)7HSW.``$3)-X///'`SW"``$3)%)!1(,"!SW"``(0D`(`%\@(-0`0#\$X-0`31 +MP.!^X'CQP)8.3_//=H``A"0`AH#@RB!A!2/RSW&``-P&(('/_S-X4`A@#?0(`WA5EAK@R@ +M!""@Z7"E!D_SX'CQP/(+@`3/`QX/8`0!V`C8SW&``'!Z +M"'2&L3"\$@R@!(>Q#O"<$@``%0C1`#22F!*``"7:P[D^"Z_T`-NI!6_S%=C@ +M>/'`-@UO\P/8`=[/=:``M`_O+@%H<]UH`#0#]6E`]\B#J_SZ7#Z +MI<]P@`"8(_JE.05O\\:@\<#/<*``M`\W@,]P@`"8(P:`#0D!`+H/S_\$\)X/ +MS__1P.!^X<7AQ@#9!]@`VK1IM'W'=8``X+=5?<"5C"8"G0#;A?:,)H62P_;_ +MWL"UP9T+#E,?C"8_D4+V8;4!XD]ZSPH2@V&X`>'%"'6`+WG!QN!_P<7QP%H, +M3_,`W<]P@``DEE8+[_FTJ!+H"-Z`Y4=\(HD`7'/<8``Z,>H((`!!!E0`^!X`-E*)`!RSW*``)3'J"#``A8B +M0`!BD,]P@`!@R#1X`>%@L,]U@`!,'<]V@``\F4`E`!&I +MH`""J:``@L`86`,`@L$86`,`@L(86`,;V<]P@`!PYFD#;_,LJ/'`X@I/\QIP +M.G%:^."X`"`_#""X`"SW"``)@C!(`C")X`SW"```@P`("+Z#G8K@CO^8NX"PA1 +M`#(-S_\*\`#9GKG/<*``_$0AH.!XP:#Y)'J&(/X#1+@?"8``"B'`#^MR@]B- +MN(HCT@U*)```40/O]`HE``%(@W]G.[I3(@*`0*]-D\"Z0:T+\O>3AB?_&4._ +MYZUWDX8C_@=%NVBM%.K/@*MX8OCK>*+Y*UCBV6M(XKB +M#.``)JV+<*EQ:@IO\PS:`,`!P48.;_0"PHMPJ7%6"F_S#-H`P`'!F@UO]`+" +MSW&``%0&`*%F#.`#R7#=`6_SH\#QP'();_-7V,]U@`!,'2.%SW*``%@&=Y$` +MH@L+'@!?V`"B"PN>`(6X`*(+"UX`A[@`HHHF"Q;+8=EA`-J`X\H@@0#/`$HD`'3@>*@@``8K")X``""##X``C!P@ +M$X$`@+D@&T(`H!.!`("YH!M"`&`3@0"`N6`;0@`!X!SP2B0`=.!XJ"``!BL( +MG@``((,/@`",'"`3@@"@NB`;@@"@$X(`H+J@&X(`8!."`*"Z8!N"``'@X'_! +MQ?'`C@\/\\]V@`!,'1IP"PA1``"&`O`!AL00``85)@T43"``H`'?);A3(`4` +M((7`?T`A``;$$0$&&PE?`0HAP`_KE@T)``!&#<_X2P@!!`"%Q!`!!@IP);G`N58)K_4` +MVL8-``&)Z`O(!2"`#P```#P+&A@PM@T``8GH"\@%((`/````U`L:&#`+R)"X +M"QH8,`H)C_-.",_WK08/\^!X\<`B#B_S`-D*)`"@H<'*(6$`%/+/<(``F",0 +M$`4`'0V?``HAP`_KO]`HD``7/=8``3!T5)0X5`(85)5(0 +M)!`5```2`2`@$!8`*!`7`4$M3R$I@1H0&`'`OR6Y4R$3`.H+8`,-V8H@B0"* +M<0X([_OI0@0(`WO +M"B'`#^MR?]B-N(HCB0=*)```X0:O]+AS/@R``1H([_?\&X+C_D$V<]P@`"8([X*;_TDH""&R!$`!H8@?XY!]*H.+_R*<,H( +MX`.*<``2`"#($``&AB!_CD#TSW"``)0@+)`>E0\)``#&"L_X;0@!!8IP"G'> +M#F_U`=I_V1&YSW"@`+`?-*!:#(_Y.@L``8CH"\@%((`/````/`L:&#`J"P`! +MB.@+R`4@@`\```#4"QH8,`O(D+@+&A@P?@Y/\PSP)!E`!2"&(!F`!2"&*!G$ +M!2"&&AD$!KH*C_D)Z`#8"PP1(%(.P`,#\#(.P`,!W9()X`&I<,]P@`",&^X( +MX`&@J"D,42#/<(``E"`(B(G@S"#B@0/T$0@1(!$($0*""H_Y!.CN#\_T#@A/ +M^58-C_<$RI#@S"""CP``LP`.\@HAP`_K<@$2!#:2V(VXBB.-!H4$K_0*)0`% +M5@F@`0#820,O\Z'`\<`>"P_S#,P`WGL('@#/<*``R!^P$`(`SW&``$P=(X$" +MXD81`0%AN0@B00`^H!#9+J`!V1486(#/<($`P!,#&A@PSW"!`(@4!@R@`@0: +M&##/<*``_$0E@$H@0""\N26@#,R&(/^!SW"``/@%`(#"(`$D@.``#(+]!""/ +M3S`````>\#D(7@-:#L_^SW"@`/Q$)8"\N26@SW"``#`S#H",(`*-B?>B#Z_U +M&=C/<*``M`_2?`$V,]QH`#('P8:&#`?@8#@BB`,`,H@@@\````" +M#J$#V!6X$AD8@+\"```,$@$W30A>10;(AB#QCR+TSW6``+PQB!4`%@0AOH\` +M``!0`>"('1@0!/($V`P:'#">#>_^`-[/<*``_$0E@+RY):!K%0`6@0B%CP`` +MM``!W@S,TP@?`>4(GP&&(/^%KO)1(P#`>O0&R`0@OH\#@.A3R/61"%_%(@IO +M^@#>SW6@`,@?.0@0(/^%H!4`$`DG`!#DX,SVSW"``)3'`(`-"%X`WJ4N#Z`` +M$-CDY\CW0!4!%C!YU@TO^A#8BB`(`*`=@!,.I1^%A.B*(`0`#J5F"P`!+]B5 +MN!(=&)#/<`$`P/P5'1B0"@B``L]Q@`"L,PR!38$((@``#:'/<(``P!L0@$^! +M8(`.@0)[`,H((L(`B.!/H=+T`]G/<*``0"TPH``:@C/-\`W,4R!`@)_S!,@# +M$@$V`QH8,`0:6#`&"H`"SW"@`/Q$)8"\N26@SW"``/@%`("`X!0*@OV)\0\( +M7\4,S,]U@`"T,C\(W@"`V`P:'#`-S`\(W@(=A0'@':4`W@3P%84!X!6ESW"` +M`'#F$8A1(`"`_`QB`LH@8@`0[QR%`>``;I0S,>PC>`0W,!""$#P```!@Y#(`/````"-8*C_<-S$D(W@#/<*``+"`E +M@`:`"N$Q"$0``Q(!-@+8#!H<,%#8J@TO_I@1`0">\58([_C)0(HA!`#/<*``L!\TH`38!AH8,`W,[[@%\\]QH`"H($B! +MSW&``+S)+Y$P"J_W`-T#R*`0``#P +MN*EP!?+."P_X`-B5N(X*@`.X\;X++_@!V`#8D+CY\0'@`!H",+D'S_+@>/'` +M0@_/\L]P@`!4,`#?Z*#/=8``(`8_KI0?T`]CJ#&_Y"[B-Z`;9SW"` +M`%@'(*#/<(``7`?@H!D"(```V,]P@`!R>*(\8!"B3`!/4';_2X* +M(T8#2B1``+D';_2X&R`&PL>`,]SH`"P'V0;P`3/%5X`"""#X``2-(TXC0B00X*N3(B0"XHI<]Q +M@`!8!Q<(7@#/<(``',L@$(``@>`%V`+R!-@`H<]P@`!'& +M"'4`V,]S@``@!\]Q@`!`R\]R@``,TF*+`_`!X`]XPVF$*Q\`,B9.'AD(@P.$ +M*Q\``").#C#F]"8.$.,-@9,$\(H@_P_!QN!_P<7@>/'`H@T/^`#8T<#@?O'` +M(@MO^>'%,NC/<8``))84B5T(4``WB0GISW"``+"Y`8`0<0'8P'@4\,]R@`#T +MC@N*AB#_C![RSW&``+"Y88&DB@#8"PM``R"*"0M!``'8SW&``$P'`*$5"%$` +MSW"``(`X)H`C@2"!\@G``8T$S_+N#`_X[O'@>,]Q@``D!P"!@+@`H<]Q@`!4 +M,`6!`>`%H0;9SW"``%@'(*``V<]P@`!/'`V@O/\L]P@``B!P"( +MSW.``",'P(L!W80H'P`O<0`A@@^``(S1F'`PXO`B@`/`N('@`"&`#X``#-(P +MX/0@A0/'<8``0,O`?0'FXXG/?CD.XQ/`J_0@CP,M#T$1%.WP(H\#)0\?$`'F +MXXG/?A<.PQ/T((\##P]!$07M\"*/`^L/'I#`JP.)5PX#$/`B@`.$+!\`P+A2 +M(```&WC5>``@@0^``$C2-.$T(4`.SW&``$`'"K@`H<=P````&-H(3_G/@H`#8`*8`W0'?%_`]#)$`SW&``(@[`-_@J4\E@0`AH,]Q +M@`!4,`:!X*8!X`:ASW"``%@'H*#I=8#E7`\!`.EP50+O\J7`"B'`#^MRSW`` +M`'HG*0-O](HCA0+QP.'%SW*``"`'(8*EP2ATA"0&D,]U@`!_RI<#/<(``6`<9 +M#9$`A;DAHL]R@`!4,"J"`>$JH@'9P_$`$`0`"B'`#^MRSW```(0G40)O](HC +M3@CQP/8(S_+/=H``(`31ZQW*``$C2-.(08@JX"*;'<````!@2#B_Y2B!`(,,5`18(=\]P +M@`#HQS1X$8B`X'8.+_G"(`(D@.?,("*@S"`B@$+R`H[/<8``/-*$*!\`+W`3 +M8<]Q@``P!B"10X4W"T$`QW"``$#+98`H@E,C#P!3(0T`'P]!$P.(@>#$(X$/ +M``8``,0A@0\`!@``S"-!@`/R`-D"\`'9"8+//'N":`` +M`=@`A<00``8EN+X,;_7`N(8+;_44V.H/[_H$V"(/```Z#<_Y9/$*(<`/ZW+/ +M<```>2?]VTHD@`"M`&_TN'/QP%8/C_+/<(``7`<`@(#@EO1N":_T`=T"V$H( +MK_>I<<]P@``$!@"`SW>``$P=)H">$0`&IKB>&1@`(X=(@3214R(``"(,;_^I +MN<]PH`#\1"&@ +MX'C!H,]PH`"T#]R@H@K/_4.'SW&``%@'"8(9"%X!SW"``"`'#("&Z!B*@^`' +MV!CRSW"``"`'0HC/<(``4,N$*A\`,"!`#@GHSW"``"`'`8"&(#F/`_*@H0/P +M"-@`H<]P@`!/'`X<7/<8``(`<$$00`SW6``%P'B'2$)`:0"O(!V<]P@`!8!R"@`-@`I43P +M`(6RZ(AT`HF$)(:0A"@?```A@'^``$#+#?(0$`4`"B'`#^MRSW```(TG^08O +M](HC#P`B#F_W!(`(<<]P@`"<.&H+@`'/<8``5#`,@0'@#*&""6_U%-CJ#>_Z +M!-@#V`"E`=@2\"4(T0#/AQ06/ +M\@HAP`_K_Z +M!-@!V<]P@`!8!R"@`-G/<(``7`<@H-'`X'[@>,]P@`!,'0.`#9"&('\.SW*` +M`+@."0B1`2"2)WB`N`"RSW&``$#+!;'/<8``_-+@?P>QX'CQP,H,C_+/=H`` +M(`&('F/"_0` +MV$X*+_F,N`?H#(:`X,PG89`8]`"%@>"H#D'T#(:`X,PG89`(],]Q@`!4,`"! +M`>``H0;9SW"``%@'(*``V`"EG_`"CB..A"@?```A@'^``(S1,.#P($```-]; +M"!X`"(9>#2_V(H:,(!"`2@`I`""%@>%,#D'T`X;/+V]22F2*,%V<]P@`!8!R"@!-@`I0#89?`@ +MA=L)E0$S)D%P@`"`/$`G`'(T>`!X`HZ$*!\`,B!`+E$@0(`T"<$"`HXCCH0H +M'P``(8!_@`",T3#@\"!``,]R@`!8!P79$0@>`("X`Z8`V`2F(*4[\,]SH`"P +M'P'8&://@`@;9\<#AQ<]UH``X +M+D>%SW"``%`P`-E`H">E@@U@`R#8!X6*N`>E"\@$((`/____`PL:&#`+R(^X +M"QH8,`O(D+@+&A@P%0./\N!X\<"J#\__SW"``%`P((#/<*``."XGH*H.C_W1 +MP.!^X'CQP'(*C_*J""_Y`=V`X,]V@``@!P&&P'V&('D/0B``@,H@8@`(N`5] +M`-@&""_YC+B`X`'8P'@0N`4@?H,B\@N&*PA1``*.(XZ$*!\``"&`?X``C-$U +M>"R`@+DLH,]P@`!4,(#9*:``V`NF!MG/<(``6`<@H,]Q@`!BK"!`!"B'`#^MRSW```((GBB-+#DHD@`!U`B_TN',(AJX*+_8BA@OH!=G/ +M<(``6`<@H`38`*<`V$7P`HZ$*!\`+W`992.1'64HZ2..QW"``(S1,.#P($`` +M00@>``*5"KAN"B_V*H8AZ,]R@`"\,1F".((">02"18)">#A@(HZ$*1\`-"!! +M+A4)!`#/<8``5#`!@0'@`:$3\`79SW"``%@'(*`$V`"G`=@-\`B&M@DO]B*& +M"B$`@!(`#P#F"T``J@R/_]T`C_+/#6`! +MH*;/<(``@#B2#4`!SW"``",'H*C/<(``)`>@H,]P@``\!Z"@SW"``%0PJ:`" +MV*EQJ@LO\PAR;0"/\N!XX'[@>.!^X'@4N"5XSW&@`#@N!J$&@0$(W@?@?@#; +MSW&@`,`OI1G8@`_:"+JC$0"&1'B,(!"`_/,4&=B`HQ$`A@L@@(#\]>!^E.#* +M(@4`A?<(#^__*'7)<((/[_^I<=D'3_+@>.'%,-L`W<]PH`#('&F@`]K/ +M<:``S!&(/T$?V)!C#I$0'X40VIJX'Z4(V$\=`A#/<*``R!Q)H`>!SW*@`/`7!J(&@0:B +M!8$&H@2!!J(`V`JBBA4`$6BX$'B*'000`)6&(/^,`_0!V!VBJ@E``ZWHSW"` +M`(0D((``V`*Q)?!.%8`0H^B*%0`13*5DN!!XBAT$$`393QU"$!D.T1`K%P&6 +M9+@0>(H=!!`,V"VE3QT"$`8.K_B(<`GP!2-#`4$?V)`?A;.X'Z59!D_RX'@0 +MVL]QH`#('$FA`=O/<:``\!=JH:00`@!-"MX"`MI=H<]S@``(YD2#1J%#@T:A +M0H-&H4J%P$``!'.!3(,"`!/1`(P`(!/!`(P`,0(!3H4QH0()3H?@0`H)3 +MH?P0`(`3H0_P7)"&(O^,`_1]H4B`1J%'@$:A1H!&H06`!J'@?N'%+X#/C@`MG/<*``R!PGH.!_P<7QP&8(C_@6 +M#(_XT<#@?N!XX'[@>/'`S@Q/\L]U@`!$R1>%SW:!`%`3"P@0!E@5@!`$Z!J% +M6X4$\!R%787/<8``T"\@@1,)'P`RA00A@0\````0)7@E>L]Q_O__/R1X`:8` +MW^*F1'DMI@[8A@OO^`ZF!^C/<8``A"":#F```=C/<8``8!V.#F```-@7A0\( +M$04!V`&N,1X"$`3PX:XQ'L(3J01/\J'!\<`N#$_R.G$(=DAWX@UO^@#=@>#* +M($(C"_3/<(``>!L`D('@`=C`>$`H$`/)<(8@_`",(`*%(_3/<(``1,F8$(`` +MY[C*("(`"O0"N!9XSW&!`,@*`&$MN,"XSW&``)@&((%1(8"`SW&``+S)%'D$ +M\B#:K9$*\)C:JY$&\,]P@`"`R;.0#MH!ET`E`141"0,`HGA((```$'@#\`#8 +M6G``V"IQJ7/"#:`#F'`*(0"@!/2N"P`#.G!,(0"@`-A(]`4@@",-<0"Q#7$` +M&80$(X<-<""@*)<-<""PC"8"E17RC"8#D1WRC"8#E23R"B'`#^MR$]B,N,]S +M``"4"HHD@P^-!._SN'//<(``1,FT$`$`#X$!X`^AQ@D@`.EP$O#/<(``1,FT +M$`$`#H$!X`ZA"O#/<(``1,FT$`$`#8$!X`VASW&@`/0'`-@$H0'8&G#/<:`` +MR!_X$0(`0G4")8`02"```%^!$'@]"(0`0X?/<(``M)E"H*#8#Z$`V!^ASW"` +M`$3)')!BN$)P'Z$"V!49&(`-"1`@42!`QB#8`_*`V`ZAC"8#E0;TSW"``$3) +M')`)\(PF`Y$(],]P@`"\R0^0C@YO^0#9Y@K/_@S,AB#YCQ+TC"8#D`!C+;C`N/`B``"@@`UPH*`#R!"(`K@6>`!C +M+;C`N/`B``!"D`UP0+#$H2H)``,!V-4!;_*AP.!X\$KHC#PSW*@`,0G$1(!A@#? +M]0F>@602`X9D&MB#`MD3&EB`+RG!`$XA@@<3Z\]Q@`!`CU9YP(&A@<]Q@`#` +MC_0AD`#/<8``X(_P(8\`"O#/`(*X#7$`H0UPP*`-<*"@ +M'?`-<0"A2B0`=*@@``-$)H$0#[E3)@`0)7@-<0"A(KY*)`!TJ"#``D0E@1`/ +MN5,E`!`E>`UQ`*$BO9T`3_+@>/'`.@A/\@AV*'4H<$AQ7@@@`&AR@>#*(($# +M$`@A`,HA00.%`$_RX'@BN0;P[')@H@3@8;GY";6`8(#/<*``U`MMH`/9SW"@ +M`$0=-:#@?N!X02F!@`GR+R1)<*@@P`$$$`($['%`H>!^\<#2#P_RH<$(=4AV +MSW"@`*PO&8`$((`/<````-=P(`````'8P'@O)@?P`-K*(($`+?(+S``<1#!/ +M(,$#`AQ$,`'@$'@$((`/``#_OX^X"QH<,,]PH`#4"SB`0B$!"(#ARB&,`$`E +M`!(0<2`(!0,'Y00EC1\``/S_Q7V=O9^]['"@H`#!['`@H`'8M0H(;_H`W8'@RB!"`PGTSW"``'@;`)"!X`'8P'@,N(4@`P$#VL]QH`#T!T6A +M#7(`L@/(`-M=D`UP0+`#R%&`#7!`H`/(2!`"`0UP0+!DH64'#_+@>/'`Z@X/ +M\L]U@``0B.`5`!``WH#@T/=$+CX7`"%`VXX.[_\8N^`5`!`!YN<. +M!)``V"$'+_+@'0`0X'CQP*H.#_(A@`HF`)`0B<.XRB'!#\HBP0?*(*$&RB.! +M#P``J@#/("$#.?*`X`!ASW&``#@&+;C`N`RI(X8@D88A_`",(0*`#/3/<8``3!WP(0$`OQ$` +M!H&XOQD8``&&HH`%[0&%`^@`A8SH"B'`#^MR'-B,N+G;2B1``%D'K_.X/'`K@TO\@#: +M"'?/<(``(P<@B*/!SW"``"('`(@!',(SA"@?```A@'^```S2+N#T($``8,$# +M'`(P`=@"'((PSW:@`,@?$Z;/<8``P!L,@0"`0L`(@0"`0<#/<(``U`4`@(#@ +MRB`!!\HA(0/*(H$/``"$`,HCH0<$#>'_P"LA!L]S@`!8!\]U@`!4,`L2`3=> +ME8388(/*"^`"F'>N#\`"I!8`$!.E<04O\J/`X'@(V>QP(*`#V0#:SW"@`!0$ +M):`!R.QQ`*'/<*``U`M-H.!^X'CQP.'%I<$(<@#;SW"@`"P@L(!`P0;80"N`"8(-A!"_R +MI,#QP*7!SW"``"('((C/<(``(P=@B(0I'P``(8%_@``,TC#A]"'!``#:SW.` +M`,`;8\'/<8``5,LP(4`.`=G`N`T<`C#/<*``L!\YH`R#`(!!P`B#`(!`P`2# +M`(`.'((P#QR",$+`SW"``-0%`(!$P8#@RB`!!\HA(07*(H$/``""`,HCH0X"QH8,`S(#!H8,.!^X'C/<8``'(D` +M@8&XX'\`H>!XSW&``!R)X'\#L>!XSW*``)@Q%7K@?R"B\<"8<`HAP`_K<@HE +MP`?/<```HAD=!*_S5MO@>,]R@`!T,15ZX'\@HO'`F'`*(<`/ZW(*)<`'SW`` +M`*,9]0.O\U[;X'C/N!_(*+QP)AP"B'`#^MR"B7`!\]P``"D&,]Q@`"\.P)A2B0`=`#9J"#``A8B0`"A@&"`*=@2N`'A=7B@H.!_ +MP<7@>,]P@``$!@"`H<$F@)X1``:&N)X9&`#@?Z'`X'C@?N!XSW&``#@&X'\$ +MH>!X\<#AQ<]R@`!,'2.".(D?"1$!"B'`#^MRBB",#HHC!@)*)```'0.O\[AS +M((+$$0$&`<]R@`"$-R""0B$!@,HA8@"OZ8_H"B'`#^MRBB#,#HHCA@-* +M)```Z0*O\PHE``$F@B.!8;B@@<]Q@`#`&R2!(('5N3UESW&!`#`3)8$%*3X` +M)W5(<$(/X`!")8$2SW"``*`W`"6!'P``B!,N#\``R0$/\N!X\;/?B"L(PIR``#=GW+/ +M<8``((^H($`"0"`""*IBO&$!Y:]]0*PQ@""C$H`Y`2_R`:/@>.!_`-C@?P#8 +M\<"^"`_RSW6``*`&S(T-C<*^PK@6?L]^O@CO_`W8!KB!N!"^Q7C/<:``["<& +MH02%SW&E`.@/!J$%A0>A[0`/\O'`>@@/\L]VI0#H#R:&IX;/<(``H`8`WR2@ +MI:!Z"._\#=@&N(&XSW&@`.PG!J'FID4ES1^GIJT`#_+@>,]Q@`!X(,]P@``P +M!@"01XDK"@$`SW"``#(&`)!!B1\*`0#/<(``-`8`B":)#PD!`,]P@`!$)`"` +M`O``V.!^X'CAQ>'&`-TS"=`'"PG3!PL)$P``V!/P&0GS!Q_>3B'\!^!XJ""` +M`0\EC1-AO@D(3@"E>`/PIG@`H@'8P<;@?\'%\<"^#^_Q*-AZ#\_XSW6``&0D +M0(4(=@0@A`\``/#_`-A@>D$L`0%`A0'81"8$$V!Z02R!`$"%`MA@>E,F01!& +M#^_X`-A`A0AW02@!`@/88'K`N4"%02]!$@388'K`N<]Q``#LM\]P@`!@)""@ +MSW`!`$R^M0?O\0"E\<`.#4_\L@A/_,]Q```4N,]P@`!H)`H-[_L@H,]Q`0!T +MOL]P@`!L)""@T<#@?N!X\<`:#^_Q4-C2#L_XSW6``'0D0(4(=@#88'I3)D$0 +M0(4!V,EQ8'J&(?T/SW$``#BXSW"``'`D(*#/<`$`G+Y)!^_Q`*7QP.'%_@SO +M^0?8A@TO_`AUK@_/_[(.3_SV">_YJ7`M!\_QX'C@?N!XX'[@>`4```#QP*(. +M[_$(&)_$?1[]$(($#/'G/=8``<.8MK00@A`\````,0BR` +M`A.M!":$'P```#!"+``#%*T$)H0?````0%,AOH!"+(`#L1T"$`[T"B'`#^MR +MU=@%N%K;BB3##XD';_-*)0``SW"``!#)!(B!X,P@(H#,("*!!O13:25Z3ZU. +MK8#CS"`B@07R4VME>DZM@.?,("*!!/(3;^5X#ZT3:25X$*T.C0RM;@I@`0#8 +M40;O\=^UX'C@?N!XX'[@>.!^X'C@?N!X\<#2#>_Q`-NAP02XSW*!`%`3%'@= +M8I#>&F(!@AB^R*6*)@02SW+^__\_R:4$>H#A0,+*)<$`"_((@00@@`\````P +M0B`%@,HE8@!-#1``SW"``-C'`!`$`,B!!"2&#P`'```$)HX?````,"R^8;Y! +M+@8&0":`$P\B`@!`PH4.CP,*(<`/ZW(^V(RXBB.*#I4&;_.X=L]P@``0R0>( +M@>#/(J$#+_+/#R;.$X8A_P,$)@Z0`-]$N01[#R=/$.1XRB8!$(#CRB.!`PZ[ +M97H#\`&#!7I`PL]P@`#8QR"`BW*&(?X#)+E`*8,#((($(8X/`0``P`LFP)`6 +M\M=V````0,PF@I\```"`S":"GP$````$]`&``_`"@*ZYK[FPN25X`*(`%`0P +M!"2!CP$``,`*]`HAP`_K!X\<#B"\_Q"'7/<(``G"P)@(GHSW"``"PL +M*8`,X/`@3@`"\`'>BB#_#P"ESW&``$P=`('$$``&:0A?`<(*C_H`I;X)+_FI +M<(#@RB7B$:+TL@I/^(;H`(6,(/^/#?3/<(``:"0@@&!Y`=B!X`7=RB4B$9#P +MSW&````3(9'/"H_Z`*7/<8```!,AD<]S@`",!D"#/.$Z8B .(4X5EAY?'/<(`` +MA#<`@,]W@`!$C$(@$(`R":_ZRB!B(`"E`8``-@W)HC/<(``E"`LD,]P@`!,'1Z0$PD```(E@1\````,Z7`$\.EP0B4!%6(/@`#/ +M<(``]#<`)8$?``"($U(/@`!+\'X(3_C/<8``2#@2Z,]P@`"4($R0SW"``$P= +M'I`1"@``*'`")8$?````#`7P*'!")8$7&@^``,]P@`!D.-OQ0@A/^,]Q@``0 +M.!'HSW"``)0@3)#/<(``3!T>D!,*```H<`(E@1\````,!/`H<$(E`17:#H`` +MSW"``"PX`"6!'P``B!/*#H``"G#)N,]Q@`!$C`.A!H:!N#T![_$&IO'`X<7/ +M=8``O#"P@@,]P@`!\+`"`"2$!`,]P@`#,Q@B`"2$"`,]P +MH``L(#"`J7!V#J``66$-`<_Q\<#/<(``1(P!@`\(40#/<(``+"P'@,]Q@``0 +M.""!0B$!@,HA8@"0Z<]Q@``\QRR!BNG/<8``),>J#F_W(X$*#N__`MG1P.!^ +M\__`-D1`,_QX'CAQ>'& +MJPH0`$`BPP,DN\.Z`O``VI4*%00S)H)P@`"T/$`GC7)4?2!]P(@!&9(#`>`! +M$(($`1F2``$0@@0!&9(``1""!`$9D@`!$(($`1F2``$0@@0!&9(``1""!`$9 +MD@`!$(($`1F2``$0@@0!&9(``1""!`$9D@`!$(($`1F2``$0@@0!&9(``1"" +M!`$9D@`!$(($`1F2``$0@@0!&9(``1""!`$9D@!"(T.`L_7!QN!_P<7AQ>'& +M(^IC:B*[P;H"\`#:-0H5`3,F@G"``*`\0">-'%X<:K"A``0"+#`R2[ +MP[H"\`#:E0H5!#,F@G"``*0\0">-+(J1T-4!`1#9`0 +M'0W1$,]PH``X!&BHSW"@`#@$:*C/<*``.`1HJ,T%C_'QP#H-C_&Z<'IQ^G*: +M('PY@]/_W_PSW:``+`Y`88`V;(.;_,XV@"&'-D@H`&& +M&-D@L,]Q@`!,'14A5@,`%@$@4X$-P?"HSW>``)0'*!A`!$5YI+DAH`#9,QA" +M`.EQ(J`*(4"#,1C"!#(8P@0T&,0%RB%B`/H);_<,X(7MSW&``#R9!/#/<8`` +M7)DCIL]P``!($0"Q&-@"I@T-4""*(`4"`+$,P(7HSW`!`"R\`:<`%@`@N1`` +M!BT('@!!AAK8`+("I@"1A[@`L0#8"[$!@JVX`:(1#!`@SW"``#`N!(`S&@(` +M*0H0("&&`8&8N`&A`X&?N`.ASW&``&@&`!8`(``9!`1`@`&`0:$"H48-;__) +M<`T$C_'@>/'`O@N/\;IP>G'Z<@HB`"$*(4`AR'4*),`A"B!`@\]R@0#6"LH@ +M8@`(<0*X%G@(8DPC`*`$N(8@_@,%(%``RB',#\HBS`?*((P/``"_(``)P'$!@"!*2YC;F9N2&@Z7$BH`HA +M0(,H&``%,1C"!#(8P@0T&,0%RB%B`&8(;_<,X(;MSW&``#R9!?#/<8``7)DC +MIJ38`+$0V`*F"PU1(*38C+@`L<]P@`!,'1F0CKB/N`&Q#,`!IRD*$"`AA@&! +MF+@!H0.!G[@#H<]Q@`!H!@`6`"``&40$0(`!@$&A`J'F"V__R7"M`H_QX'CQ +MP.'%SW6``,PT`(V,(,./#_3/!X\<=Q@0#("D"A`-I"L<:IP-A_'0(0SW6``-0&P*W/<($`2`J`V=8+ +M;_,H/'`V@F/\:'!"'?2#2_T&-C/ +M=H``O#$!AJOHSW"@`-0+&(``W4(@``B`X,H@3`.,(`B%2/?!%@`6`>#!'A@0 +M&_"=V``00>4!`!XXPC@H`@ +MAKCWSW*@`-0++:(D>`"F9QW8$V@=&!$`V*$`K_'<'0`0X'AE!N__`-C@>/'` +M-@BO\=AP.@@@``#=R6@K#A(0^'"I=S(F@`,5"!(,$0B3#IX-S_0R;SAX!7T! +MYT(G1P#E#W6`8;YE`*_QJ7`(<@/P`>`@B/[IX']">.!X\<#J#T_QSW6@`/Q$ +M'84YA2H/(`(`W@#8GK@!I>!XP:7%I34`C_'@>,]QH`#(.PZ!B+@.H6D@0`#^ +M\>!X\<#/<(``3!T#@!B('0@1`0HAP`_K!L` +MD('@`=C`>`RX*0B!#P```!#/<*``+"`0@,]Q@`"<+`*A`M@#H<]Q`0",,TX* +M;_\!V-'`X'[QP!H/3_'/=8``/,VX9V``B +MA0"A"_!N$0`&1'AN&1@`'-@8N!49&(#Y!D_QX'B`X`'9P'G/<(``R#3@?R"@ +M\F%X<(IA:'":88 +M@@NF&8(,IAJ"#:;/<`4`Q@,&I<;8D+@&I<]P+``"`0:ESW!:`$(!!J6*((L` +M!J7/<$``APT&I<]PT0#"#0:ESW#```<.!J7/<(``N!L`@,]R@`"X&T(@0(`` +MH@;TSW*@`,@<`-@1H@'8"*<`V`VG#J?/<%``_P`6$+@% +M((`/```"#@:ESW"``+@;`(#/<8``N!M"($"``*$'],]QH`#('`#8$:$$ABN& +M"*<%A@VG!H8.IPB&%Z<)AA:GSW"K`*#_.*`LACF@+88ZH!8/+_D.AL]P@`"X +M&\]Q@`"X&P"`0B!`@`"A!_3/<:``R!P`V!&A'01O\:+`\<"R"T_Q?@@``,]V +M@`#T.5H*+_<`A@AU`(89#0`0Y@EO^ZEP\@FO^Z"FF@\O]!'8?@Q/]L]PH``L +M(#"`SW"``-`&W0-O\2"@\<"Z#^__H<'/<(``T#0`@`398MH>VT#`BW`:"R__ +M&+NAP-'`X'[@>/'`/@_O\Q;8`-C1P.!^X'CQP"H+;_$'V%8)3_G/=J``M`_\ +MAAIP`-@KQX'CQP,]P@``(%P"`SW&``-`&&WC6"^_T(($(Z`'9SW"``(P;=@_O +M_R"HT<#@?O'`!@I/\0AW?=@-N,]Q@0`P$\6!C@MO\(P@`H`!Y7SW`"A"`P4JO@//V,]RIP`42!V"OH)L$A``]Q26"'P#_``#3)>$57@QO]HHA$``(=JEP +M4@QO]HHA$``(=4`H`")"#&_VBB$(``AW0"H`(C8,;_:*(0@`T7D9X2QY+W&Q +M>AGB3'HO<@`9@",/#V(0`!M`(P#8!/#_"(.``=@A`6_Q`!P"(/'`W@A/\0AU +M*':N#^`!"M@!V,]QIP"81QJAG@_@`0K8SW"F`)P_9!`$`%$D`(#*(<$/RB+! +M!\H@@0\``+\9RB.!#P``N`#D`>'RRB4A`,]PIP`42"R`'8``IO>XQ2""#P#_ +M``#3(.$%X0!O\0"EX'CQP&H,[_,&V"8-3_S/<(``3!T#@!B('P@1`0HAP`_K +M"^_Y!-C1P.!^\<#/<8``G"P) +M@0'@":'/<8$`,!,&@8*X!J'/<(``$,D#B(#@X`^A_LH@H0#R#._S!MC1P.!^ +MX'C/<(``!10`B!L(40#/<:``K"\9@?"X&8'/(*(#SR"A`AFAX'ZQ`R_T$=C@ +M>/'`SW"``$P=`X`8$(0`'0P1`0HAP`_K`!V,!X#+@1 +M"($/````$,H((```V"#P\@M/_,]P@``D%`"`ENC/<8$`,!,&@48@0`$&H<]P +M@`!T!B"`%0F1`(H@B`0^">_Y!-J6"N_Y!-B>"0_ZT<#@?O'`SW"``$@X`(!" +M(`"`RB!B`(CHSW&``)PL"8$!X`FASW&!`#`3!H&"N`:AY@OO\P;8SW"``#S' +M#(`8Z,]P@``4*0*`$NC/<(``>!L`D('@`=C`>`RX%0B!#P```!"F""```-C1 +MP.!^SW"``!#)`XB%Z(H.K_X"V/;Q]O'@?N!X\<#/<(``3!T#@!B('0@1`0HA +MP`_KO\KASSW&!`#`3!-@&H<]P@``4*0'9(J#/<(``$,D#B(#@ +MM`VA_LH@H0#&"N_S!MB^"N_S"-C1P.!^X'C/<(``Q#7Q`@``X'CQP*8*[_,3 +MV,]P@`#`&P2`((#/<(``=!(@H-'`X'[@>/'`X<7/<(``P)D#@,]U@0!D&A$( +M7@`$V-(.+_8$I1SP`]@$I0#8"*W/<(``(*SB#N_RU-D`A03H8;@`I28(C_X! +MA0'@`:7/<*``+"`0@.H/;_X%I84%#_$(<@#8X0?O]A#9X'@(<@'8U0?O]B#9 +MX'@(<@+8R0?O]D#9X'@(<=T'[_8`V`AQU0?O]@'8"''-!^_V`MAI!P_R\<#/ +M<(``3!T#@!@0A``=#!$!"B'`#^MRBB",#XHC"0/Y!:_R2B4``,]PH``L(#"` +MSW"``,0U;@H@`)8A00_/<8$`,!,&@48@0`$&H<]P@`!T!B"`%0F1`(H@B0>J +M#J_Y!-H""._Y!-@*#\_YT<#@?O'`SW&!`#`3!H&"N`:ASW"``!#)`XB`X$P, +MH?[*(*$`7@GO\P;8T<#@?N!X\_S%-A^#(_RT<#@?O'`0@GO\Q38G@QO +M^038T<#@?N!X\<#_V<]P@`#,-(8)K_\@J,H)S__1P.!^\<#AQ0#=SW"``$0D +MH*!:#&_VJ7"*(/\/"@^O\JEQ300/\>!XJ0-/]_'`R@LO\4HD0`#`@:"``=_1 +M=<(D`@'1=:&!88#")\X3`=ZQ<\!^L7,!V\(CS@!,)`"`S"8BD,HC8@`*](7K +M@.;,)R*0`_("VP+P`-L4ZR$+4``Y"Y$`H(#`@0&`(8$")8V3H*(#($```:(0 +M\`#8`*(!H@SPH('`@"&!`8`")8V3H*(#(0$`(:*I`R_Q:'#@>/'`X<4F@$"` +M0B("@,HB8@"`XLHAP@_*(L('RB""#P``-A'*(X(/``!W`,HD(@!`$>%!NJB@D*`0GT'#5(0`*-! +M@`L)@0#B"Z__!H`9`P_QX'CQP)H*#_$(=@"`0B`!@,HA8@``V";I)H9!A@'? +M,'(@AD&&0:$@H@"FSW"MW@(``::FAL!_!H41#@$0J7!6""```MD&I::&!X4/ +M#@$0J7!&""``"-D'I07O?@NO_P:&`=BA`@_Q((`0<0(O\>EPX'A`@!4*``!D@@LC0(`%]$""]PH! +M@`#:X'](<.!XSW*@`,@?]!(``+S;&+L$((`/__\`\/0:```+R&5X"QH8,!4: +MV(#//'`C@D/\<]UH`#0&].%$0Z>%L]P@`#H.$H) +M```/#MX6SW"```@Y/@D``!$.'A?/<(``*#DN"0``#PY>%\]P@`!(.2()```1 +M#MX7SW"``,@X$@D``+S8&+@3I:$!#_'@>/'`*@DO\0#;"'?/=J``R!^D%@`0 +MSW6``,`;^&"D'@`0`=@3IBB%#(5`@0"``"+"@T"A+(4!(,```*$"V!.F*85- +MA0"!0((`(,"#`*$-A0$BP@!`H`38$Z8JA0Z%0($`@``BPH-`H2Z%`2#```"A +M"-@3IBN%#X5`@0"``"+"@T"A+X4!(,```*$$A0"`/@FO\^EQ)(4`H06%`(`R +M":_SZ7$EA0"A!H4`@"()K_/I<2:%`*$'A0"`%@FO\^EQ)X4`H0_8FK@.I@_8 +M#+@0IL]P@`#(.)8)C__/=8``Z#B*":__J7"&":__0"4`&'X)K_]6)0`2=@FO +M_U8E`!.9``_QX'CQP"H(#_$(=R#P`(8AAB&@`*$`V`"FSW"MW@(``::FA@:% +M$0X!$*EP^@WO_P+9!J6FA@>%#PX!$*EPZ@WO_PC9!Z4CAF!YR7"N#>__Z7`* +M)@"0"/(#AR"``H8B>*\(4H`&":__Z7`M``_QX'@/V)JXSW&@`+`?%:$/V`RX +M%Z'@?O'`G@_/\,]R@`!$R3^".G"JP0#8(0G>`L]S@`!,'6.#=(-($H$`P-UD +M>88A_PXBN3I]!/`4W0+8BA(!`0)Y$H($X58.K_4`VIH*8``"($\#`]C/<:`` +MR!\3H<]U@`#`&PB%`(!"P`R%`(!#P`F%`(!$P`V%`(!%P`2%P(`%A0`0$@!` +M$0`&`-D?9\]P@``DED"``8``(L*#`2!``$#"0<"+=Q\)42":#8_RA,$:<.EP +M,@OO_X;""'<($`(A"_""P>EP(@OO_X;""'?/<($`,!-$D,]Q@0`P$V6!!L$$ +MNQ<+9`!`*H`"&PA%``)Y_PA$@`;PAL""#&``2'$(<4;!+0^1$$H/;_/)<`AV +M2G`^#V_S!L$&PEIP!,,'P07``"+"@`$@0`!$PA7PE.].#V_SR7`(=DIP0@]O +M\P;!!,-:<`;!!<`'P@(C0X!$PP,@@`!%P!D/4!#/<(``3!T#@!B(A.#,)R&0 +M`-@#]`'8+R8'\$3TR7#:#F_S`]D(=TIPS@YO\P/9"'8`P`'!0"#`@$$A`0!! +MP03!0,`%P$`AP8!!(```1,%""6``1<`5"1$@!(7@H`B%`,$@H`R%`<$@H"4) +MD2`$A>"@"(4`P2"@#(4!P2"@!87`H`F%!,$@H`V%!<$@H!4)42`%A<"@"84` +MP2"@#84!P2"@!0;O\*K`X'CAQ>'&SW"``$P=PX#/<8``6+#/=8```'A4%0`6 +MP:'/`"U!XI&B@BX1W@!M4&)`(E*(0`B"+I'>`*U0XD"B0BZ1W@#M02))8D( +MN2=X`-D$M5,A#P`4(,XC08Y:<2"."+I'>2=X9@H@`!!X()5"(5$@.&`B;Q!X +M%"!"(`"U((I!B@BZ1WDG>$(*(``0>"&51&\X8!!X%""!(`&U0(DAB0BY1WDG +M>"8*(``0>"*5!N`8*(``0>".5.&`0>`.U +M08X@C@BZ1WDG>.X)(``0>"25.&`0>$IQ.&`0>`2U`>%C"76@+WFM!,_P\``@Q@$" +ME2\FB`$'((`!:@D@`!!X`"`'`@.5+R?(`0<@P`%6"2``$'@`)04`!)4O)4@! +M!R!``4()(``0>!]G!97P?^=X,@D@`!!X)I4A@^Y)7I0>@`B@0(P +M>0`<1#!'E2=Z7'D/ND5Y,'D`(8(!4'I<>0(00< +MA#`/ND5Y,'D`(4(!4'I<>083!Y.&!I +M<<:YA;D(N04AP0(@MA!X()4*'`0P)W@<>`BX!2```0&V`,`!I@'``J8"P`.F +MA0/O\*7`\<`2"\_PHL%*(``@`=_/<(``?@8`B`L@P(,H\D`HS2#/=H``6+"T +M?;AFN68$@".!4@PO_P7:N&8"@+IF(6@BH@+@0<"Y9L]P@`!8L*!@(8&^9F2& +MBW)^#>__$.``V0`E@!^``&RP(*C[?^]_0"!0("\@!R2;#Q&2`-G/<(``?@8@ +MJ/4"[_"BP`][2+@/>,]R@```4O0B``!`*`$"2+@%>?0BP``P>>!_)WC@>/'` +MSW*``+@X((*`X`HB`(#QP!?RX@_/_X#@RB'!#\HBP0?*(($/```S$/'`G@G/\`HG`)#/=H$`,!//=8``Z#@/ +M],]P@`"T4,EQY@DO_Q3:<@WO\:EP0"4`&!'P&0^1$/X/3_+)<`#9 +MSW*``-@&(Z(DHB6B)J(GHB*BSW"``,0X(*#/<(``:#D@H#&R,++/<(``B"[@ +M?R"@X'CQP,]Q@`#`(P"!E.@!V`"A`-G/<(``=!*V#^__(*#/<(``:!T0B(/@ +M<`DA`,H@80'1P.!^\<":",_P&@\O]Z3!@.#$"0(`SW"``'02`(#/<8``B"[* +M#N__(('/=H``V`8PEE&666$P<,H@+@#"($T`0H;/<8``Q#C/=X``:#F/Z@WH +M8(<;8V"G8($;8V"ASW.``#@RLH,=9;*CSW.``.0%8(,`W0<+40"@H2"!0\)` +MP2"'0L#/<(``U`4`@$'!@.#*(`$'RB$A!,HB@0\``*(`RB.A!\0/8?[`*R$& +M`-B6#*_XBW'/<(``U`4`@(/HH*<1\`"''P@4"H8)X`$`V`38SW&``'!Z"'2& +ML3"\>@[@`8>QSW"``'02((#/<(``B"ZBIK&V(*"PML]P@`#D!:"@G@MO\Q/8 +M`(<="%0!7@@@``'8Y@V/^L]Q@``P,QV!`>`=H07P1@@@``78W0>O\*3`%=@` +MVL]QH`#('V\9&`#@V)"X$*$)V+`9``"T&0``=-A"&1@``-B:N`^AI!F``,]P +M``P`&0ZAX'[/%D^C/<(``P",`@!\(40`B"L_W +M%PB0!L]P@`#`&P2``(`%I0'8!Z5)!X_PX'CQP.'%SW6``-@&!X49Z,]P@`#` +M(P"`*PA1`.H)S_,]P@`#`(P"`%PA1`,]P@`#`&P2`((#/<(``V`8CH.!^\<#/<(``P",` +M@"4(40#/<(``P!L$@,]R@`#8!@"`!**6#.__(X(QDCA@$;+1P.!^\<`N#J_P +MBB$(``AUSW"@`,@?,*`!V4$86`!6"0``SW:!`#`3`X8EAM6X,'#*(1>!^SW"``.@X)X`&Z0.`0(`"@4)X!?#/`$\@`@*-NI>Z1J,%(((/@`!`.D>C!2"`#X``P%,/\`4@ +M@@^``,`D1J,%(((/@```/D>C!2"`#X``@%<(HX01`(8)HP:%$@SO\R&%^@[O +M_P&%`02/\/'`?@NO\`#:.G#/<(``<.8,B,]VH`"T1T0@`0Y"*=``"G5Q%@&6 +M!"&!#W````!!*3Z%^?5#%@&61B$!#4,>6)!7%@&6O+F_N5<>6)!?%@&6O[E? +M'EB0`-F>N5,>6)#@>%,>F)!@'AB0)@L/_,]P@`!@)""`8'D$V!7H3"%`H%@) +MH?K*($$#SW>``.PY`(\7#0`0SW"``/`E-H!@>0#8`!\"%&8(C_)#%@"612`` +M#9^X0QX8D(T)$"`C"5`@8PF0(`HAP`_K`^XI7A?'AB0<18`E@0@@`]P +M````02@^A?GUBB#_#V\>&)!K'AB0$O#/<(``3!T#@!"],B"`#P``V`*?O8#@ +M`=C`>`^XI7A?'AB0!LB$X&0/(?/*(*$$D0*/\/'`.@J/\`AU*';*"Z_P`8"@ +MA1"Y02T`%#A@N@NO\,EQ$+FP>#A@K@NO\$`N@1)Y`J_P*'#QP`8*K_`$V0#8 +MSW6@`+1'2QT8D`#:D+IW'9B0`=IW'9B0SW*@`(1$&*(`VI&Z=QV8D`+:=QV8 +MD,]RH`"(1!BB`-B2N'<=&)!W'5B0@-AW'1B0`-B>N%0=&)``V)RX5!T8D,]V +M@`"8!">F+/YWM`":-'X`` +M6,CXC1,/D1#@D_M_(Y&`OR1_X+,%\`L/41`BD2"S`-DXK<]UH`#('/J%()/D +M>2RS!/`LDPD)10-9803PK+.Y8HDASP\$&%```>8`V<]P@0`P$P$!K_`GH/'` +M`!8$0`<:&#$`%@5``1I8,002@3"``(C(\"WIA19)Y88;J)I%1(4"`Q`@"\@W(`""!#X``!,C$J11](I'`'803%7]X'400`Q(!-L"G`8$$((`/````8"T(@0\````@$(G/<8$` +MR`H"N!9X`&'MN,HF8A#/<(``A!S4>""0$.$@L`/9SW"@`!0$,*`J">`!"G`] +M\'`2#0'@$P$!`B%.`Q$-A!/"?:)X$'B`&QP`SW"@`-0'#Q`.A@#=\!N$`W`2 +M`@'`&T0#0GDP>>`;1`#0$P$!`>$P>?`3!0'0&T0`4R5^@,HAP@_*(L('RB#B +M#/'`S@YO\`#8 +MSW&``)`Y`*$,S,]VH`#4!U$@`(`#V"`>&)"CP5+R%!X8D`,2`38`%@1`!QH8 +M,0`6!4`!&E@Q!,J&)$`%@!`!QH8,``6!4`!&E@Q!,J&)"4$@``#0A>`D8. +MC_<#$@(V#1(--L]P@`#HQQ0@0P,HDZ'I\(K/<8$`R`JU>`*_]G_A89@2#P`M +MN>Z@]J#`N<]P@`"$'/0@00"\&D0`T!,``00A@0\``/#_P[@E>-`;!``%\-`3 +M``&\&@0``=B@&@``"@YO^_"*@."H`R$``Q(--@;(42"`@9P#`@`AA1$)G@:0 +MV)"XC0,@`*`=`!`"O\]P@0#("D`@@@/V?^MBQ!6"$!$*P`"1V)"X:0,@`*`= +M`!!JA<]RH``L(/""C"/_CPWR8G\7#X4?`(```(?8D+A%`R``H!T`$/"-`K_V +M?^-@!".^CP```!/X8#GR$PM>`HO8D+@A`R``H!T`$%<+'P,%D);H!\@$((`/ +M`,```!4(@0\`P```$=@4N/D"(`"@'0`0B-B0N.T"(`"@'0`0I!4`$+2XI!T` +M$)(5`!&GN)(=!!">%0`1I[C-`B``GAT$$(78D+C!`B``H!T`$&*0,Q6`$$T+ +M#@`'R`0@@`\`P```,0B!#P#````(C2D(4P"D%0`0M+BD'0`0DA4`$:>XDAT$ +M$)X5`!&GN)X=!!`*\!$)G@&-V)"X;0(@`*`=`!`&R%$@`(`8`@$`4@^/_P,2 +M#38(C!8,B +M>(P@"8;*(24`I!4`$`DA@0#RN*P=0!#G\I@5@1##N0?(/'D$((@/`0``\`T2 +M!#;/<(``E,<6(``!99"L%0`002@($PD@SP!^%0`1@!4#$1MCSW"``$P=!(!& +M$``!&V,()\\08G^8%0,0Z+L`V(?R1",`!@0C@0\&````([@QN0'@.&#/<8`` +M`%0R(08`!".%#\````!!+84%,B%``4$K@0)2(0$`P+D#N&"$%0,1'7@GXR*[!2G^`"]Q4R$#`'EA/7G/5Y)7C`'0`0"J//<8``I#`!V`"A!?!/@K`5!Q$/"L4!!=@8N*`= +M`!#/<(``8`9!@""5"2&!``"($0A1`!D6`)80<0#8`_!,]Q@`"D,`"!@.``WBWR`-@`H7X5 +M#A&`%0`1SW&``$P=)('88$81#@$>9@,)GD7/<*``Q"P+@%,@@03^N,PA(H`) +M\I@5`!#J#^_T`-ITN!YF`O``W@,2`38(\`W(SW&``)3'%GG%D:EQ2B``(,]P +MH`#('ZP5`Q"([J05`A"QNJ0=@!`$\`DC@P,#VABZ3Z#X$`(`H6H((T,#0GN@ +M&,```-J8NDZ@"^ZD$0``\;@-S,4@H@3/(&$`#1H<,`&1".@-R,]R@`#HR/0B +M```%Z`&!#PB>`PW,@+@-&APP;@DO^RAP`Q("-KR21"4`$]4($`$-R,]Q@`#H +MQQ1YP!$``6&"I7@,=P@0#("F60(PM2``:0&PA>`!$+40!@$@`!A+A@&@0`!?`%Z3R2BKD\LJ02#0`7#1X2:!(#`5,@ +MP0!Y83!Y:!I$`!4-7A)J$H$`P[@X8`]X:AH"``?(SW&``/C(!""`#P#````/ +M"($/`,````X9!`0$\`#8B[@'L0&2%.@-R,]Q@`#HQQ1YT!$``5,@P(`*\O`1 +M`0'/<*``F`,^H+8:1`"D$@``!""^CP```#`'](8@Y8^L"V(`RB""`&8/P``% +MZ-X/C_VH\`/(I!````0@OH\````PLO+TN%0*P?0#$@$VI!$``)T('@/>#>_R +M`=@#$@$V';'/<(``3!VD@+((;_@`WAL(40#/<(``>!L`D('@`-[/)B$3RB8" +M%`/8SW&@`/0'!:&%)@(=#7#`L`/(79`-<$"P`\A/@`\*'@!"A0UP0*!&E0;P +M#7!`H`/(0!`"`0UP0+`#R%&`#7!`H`/(2!`"`0UP0+`0&0`$`\B4$```42!` +M@O0.0?!L]P@`!H!@"0';'/<(``;`9`@`&`4:$2H0CP +M)@WO\@+8`Q(!-AVQ%@_/_0/(]@RO_W@0``&`X.('`@`#$@,V`8.8$P$`E!M` +M`"T('@;/=8$`P!>I<&8++_EH<1#8#!H<,`W,H[@-&APPP@VO_ZEPJP<``)X3 +M``&^$P(!DAL$`)`;A`"N"Z`!@A,#`1L('@8#V<]PH``4!".@BB`0`'\'(``& +M&A@P`\BD$`$`AB'ECR@*0@`#$@XVI!8`$/2X:`(!`+".SW"!`*@)MGC/K`6`Q%DX^L+I0`)(D$`4FU6>L]W@0#("D)G!"*- +M#X`#```WO66]@.7*)0P4!"*"#Q@````SN@WB2B$`(`\AD2#CB!X,+_:8%@`0 +MF!8"$`D@003MNLH@8B!`*`,A='M(<,:X22#`!11[SW"``+Q+<&`-"MX"02@" +M`10B```HN+AX`^`$((`/``#\_\]R@0"P$P.BSW*@`,0L#:+LH@?(#1(#-@0@ +M@`\!``#P++@8N)VX%+ME>`5Y*J+/<8``*#0(@0'@"*$!"9Y%SW"@`,0L"X`$ +M((T/\`<``#2]4R"!!`L(G@!`+`3(*>BIY@6`!!J"^_T`-H!I\]Q@``P +M,P*!`>`"H0"!'67/<(``3!T#@`F`H*$1"%X`#1!"TN*0>`!"4%@`0D!8# +M$<]QI0"L_T#`L!8"$7BASW.``$P=9(-6$P,!%.-B>@/B(KI;8GIB2")"``6Z +M12)"`U:A(,($((`/````("6X!2("!$5XB;B.N!FA`]G/<*``]`!L`D('@`-[/)B$3RB8"%,]U +MH`#T!QF%@.#*(<(/RB+"!\H@X@S/("(#RB."#P``;@K*)`($!`2B\XI!D``*01```$(+Z/``!`"`?R`8'PN/0/@O(.\#J!#7`@H`,2 +M`3:D$0``AB#SCP3R.X$-<""@`=DKI0/:2*7/.!X4PA>0P/(SW&@ +M`,@?L!```98@00\>H1#8#J$!V!49&(#B#Z``0=@O"%Y#SW"``)`Y`=D@H`/( +MI!`!`)JYI!A``(H-;_\!V,]Q@``H-`V!`>`-H5H,```$(+Z/!@#*`)AP'O+/ +M<(``E!L#@(#@RB'"#\HBP@?*(.(*SR`B`\HC@@\``#\$&`*B\0^Y)7C/<:``_$0-H03P=A$-`0W,4R!`@`GR!L@$$@$VQ@IO]0T2`C;/ +M=H$`P!?)<'(-[_@#$@$V`\@&$A`VSW>``(`^#F__J7``V2"G +M">B&('Z/T_(#R*`80`0&&A@T`Q(!-I(1``$1")X"JKB^#*_ZDAD$``,2`C9^ +M$@$!@A(``8`2`P$X8!MC#Q5Y"8%X8`FA`8+9"-X`.@OO^(#8 +M!A("-@0B@@\"``$`#1(!-QD*@0\"````$0A>!T\AP@`-&IPP!O"CN3!Z#1I< +M,`,2`S8!@UL(G@%/(L`"C+@-&APP,(L0>C,3@`#/=8``2+`$N25XSW&@`#@N +M)($&M1#P+RY!$$XF@Q<`W@\FSA#&><]V@`#@Y?0FSA`1"(`#\>G/<```__\$ +MM0/P9+4(V`P:'##/<(``<.81B!4(7@'/<(``$,D^"J_]`(@-$@(W`\@!@/VX +MSR+B`=`BX0'/<8``M#(6@0T:G#`!X!:A+?`0V`P:'#`-S*.X#1H<,(8.;__) +M<`,2`C8!D@GH#`,]P@`#X +MR`,2`38"@)@9```&R%(,;_4-$@(VN0;O[Z/`\<#AQ:G!BW6I<,]Q@`#0/`X/ +M[^\DVJEP@@OO^`,2`38&"&`!J7"M!N_OJ<#QP.'%`Q(!-J*!((6*#B_^)-J. +M[0HAP`_K`*P%\%,E_H`F]`#=A"H*(B]W +M`">"'X``S+DJ<80I!`\$X@`B3@[)<#(+(`!(V58F`!DF"R``!MG'=X``.+PC +MART)$2"!N2.G3A0!-B2G):<0\`HAP`_K +M!JH-3_,-\$\,7@:J"8_R`Q(!-J`9``"&('Z/`_0`WP+P`=^(<(H+H`!H<0/> +MSW6@`-0'TJ7&#$_]SW"``(`<`("`X,PG(I`#]!,=F),#R*`0```P\`,2`389 +M#-X$;R!#`*`9``"*(`@`!AH8,-GQ422`A`#8SR#B!?7UI!$```L(G@8%V!"X +M[?$)ZL]R@`"L,Q""`>`0HO;Q"B'`#^MR"B4`"#+8SW,``"4)_01O\8RX*'`- +M!,_O\<"D$`$`#PE>`B8)S_W1P.!^*'2$)!*0$?(-"5\&I@P/]@?P(-G/<*`` +MR!PIH`/9SW"@`!`4):#K\>OQ\O`@@`#"N`\E#1``V`\@@``&(0&`[_4D[2\H00-.((X'#1J8,_78 +M!;@B"^_VR7$-R,]QH``4!`JASW&@`&0N\"$!`-.Y"G"*""_UY'G*#N_ZR7`` +MV`\@@`,&)0V0W_7/,[6"B_^ +M`N(@C<]P@``@!T"0A"D?```A@'^``%S+R0+O[T"PX'CQP$X*S^\H=D8AS0`= +M94H((``BN<&^'PY0$!,.D!`=#M$0`!:`0`$=$A``%H!``1T2$``6@$``K8$" +MS^^`X.@@K0$`%H%``1A2`.!^X'BK"1``0"'"`R2ZP[D"\`#9E0D5 +M!#,F07"``)`\0"<#!^@.'*)$UPX'CH(*T!`!8!00(85`#@?N!X\<`N +M"<_O`-W/=P``!!U*(``BJ785(H`S#A`!!@#8SW*@`!0$RJ*HHB>B!*(]98CA +M:+G*(0X``@GO]NEP0B!0("#GU0AUH`'F20'/[\]S@``(.BN+"PI"`"^+"PF# +M``#8X'Z,(`*!X2#!!P38X'\"V/'`L@C/[QIPSW"```@Z,B`2!,]P@``,!M&( +M$HA*(P`@$':AP50!*0``',`T:G<*(<`D`_!Z=40NOA,`(D`NSW&!`.`8,R$- +M`+M],0@S)JU]SW&``!`5&H$[@21X'0@>`L]P@``,!@N(BW/)<9X.+_>I<@#` +M`GVM?0`F@!^```P&'!#!`,]R@`#0-`"*!=KJ#B``J7//<8``V#D@@0#=2B2` +M<2)XJ"``!7-N='NU>\]R@`#@BWEB(8EZ8@OI(0D``"<(0@`O#5,1`>6O?0KP +M0B61$"\A1R1AO:]]$?`#$L\``-EJ=0SP@.5*(0`@RB5A$`7R0B51$"\A1R0! +MV2SI\V[T?Q4G01//`2Z+R0(`0(G@Q!L>"\@1@X^">_OB'$.>`)_".?N?T2_[7\) +M"!(F"N?M?\EP"G%Z#"``Z7(!YL]P@``,!A*(SWX0=L`&S/^1!Z_OH<#@>/'` +M0@^/[RAU@.+,(R*`"?(L;2]YSW:```P&,ZX&\,]Q@``,!K.IJ7'/=H``#`:T +MK@"N5:[Z"2``=JX`$(D`X8C)0.M>/`! +M$\``F>@`VEBM7*T@'8(02B2`<0#9J""``Q-N%'@U>,=P@`#@BT"H0:A"J$.H +M`>$O>5[P?+D`(8H!.HMLN@`B0!$:B.!R`"(&`NX((`#I +M""``Z7(;5YQW&``."+`*GX<0`2@!``$X$0C@@@`.ER`1\" +M`!4E2P,5(TH#`1.`$`$2@1!R""``Z7("'P(``!.`$``2@1!B""``Z7(#'P(` +M0B!($`'EF0AUD*]]`>;/<(``#`82B,]^$'9P!LS_`-G/<(``U#G!!:_O(*CQ +MP,]P@`#0-`"`SW&``-`Y((%-:#!RP"!L`4QY +M5B$!Y.&#@?P]XX'CQP+AQ-0A1``D-4@`=#=(#"B'`#^MRSW```-<4BB.( +M`D4&+_%*)```0"V!`&2Y`"&`#X``G`\=\,]P@``$$S(@00&,(<./RB'!#\HB +MP0?*(($/``#8%,HC@0\``!`""`8A\-'`X'[@>/'`SW*` +M`!X&"FJB"2``*6K*#0``S@@``,]Q@`"85""!SW"``%P.(@@@``':SW&``)14 +M(('/<(``B`T.""```-K1P.!^\,=P@`#T#@OPSW:``,P1%7X"N!1XQW"``-0/(8A+"1X` +M!1#!`"*N!A#```.NZ7""#"``2'$`KH#@S"!B@,H@(0`2\D0H/@<`(8!_@`!4 +MC,40@P#A$($``B+``!!X![B6#:_O8GD!KD(A02"!"76``>4=!(_O\<#""X_O +MSW"```P&$1"(`,]P@``,!A*(JP@"`DHF``!*(<`11"X^!R]PA"@#$2=P`""! +M#X``5(P?$$-#X``5(S5?0>-:7$%VIAP +M%@H@``45PQ!`+H(`5'J$*`$5`")!#M1YQW&!`.07N'$`J8AP27$'VNX)(``& +M%<,0`1T"`&&_`>:W#W60SWY"(4D00"9&`($)=9`O)H``*2.^&!W#3,6 +M(*A3)8`030A3`48ES1&O?1OP`12`,``F@1^!`%`54FU4>EEA(,(`J40NOA8` +M)4`>1*D4%,$P^&`@J,EP7@@@`*EQ`>6O?5,E@!#+"%*!(?`!%((P$FT4>``F +M@1^!`%`5.&!`J"#"1*C)<#(((`"I<0_P0B4`%@]X`12!,,=V@0!H%@*X%'@> +M9B#`**X,K@C$") +M@.**(0\*P"CB``3T1"B^`R]P2@N/[P"E`-T.WL]W@`#L.[X([_^H9V&^`>7Y +M#G60KWW/<(``T#0@@,]P@`#0.2"@+R`'!,]QH`"T#QRAL0&/[PYX+'@I:@#8 +M#R!``"=P6GC@?PX@P`#@>/'`)@F/[\]W@``,!@"/%@SO_S./SW"``,PY`!#0 +M`,]Q@`!X(!2/1XD?"@$``(\AB1<(00#/<(``U3D`$,``"2``!"\@!2"QCP/P +M`>6O?1*/$'7\``D``-Y*(H`CSW"``,TY`(@0Z$0MOA,`)D`>SW&``,PY`!'" +M```@@0^!`.`80*E?\,]P@`!@)""`8'D`V!D($0//<(``W#O)8`(@0"`->$@@ +M0``$\$@@0"`O(04@SW"``.P[RV`3CZEQX@AO]E6/"2!`!"\A!2#/<(``8"0@ +M@&!Y`-@`)9,?@``H!AL($`3/<(``8"0@@&!Y`-@$$X$@&P@1`\]Q@`"$!\EA +M!!.`(")X"2!!!`CPSW"``,P[R&`">0DA001$+;X3`"9`'L=P@0#@&""H.G`3 +MCZEQ)@^O_\ER`!'!(`)Y`!E"($(B4B`!YAD*=:#/?H'Q,0"/[_'`Y@]/[PAU +MSW"``)LD`(@H=Q,-`1#/<(``FB0`B#T/`!#/=H``#`:I<$`F@1*N"2_W0";" +M$BJ.!&ZZ""_W2XX*CFX/[_8KCL]P@`";)*"HSW"``)HDX*CU!T_OX<69Z(PA +MPHT!V%GV2B2`<<]S@``TC:@@P`.A:T0H/@%PU#$`?M$PB0`0'@#W@` +MV`/P8;@/>.!_P<7@>.'%X<8`$`&(&?`+#1,0`-V@ +MJ<]P@`!X.P"0#0T"$*EHK7V@J<]P@`#0.A0@3@.@CJ"J`!'!`#1X`8@`J\'& +MX'_!Q>!X\<#.#F_O`-BAP0`/'`Y@U/[PAUUW4E``"``-A*]\]Q@0`P$R6!)0E%`R)] +M`>#Y\<]P@0`P$\6`J7!2#V_OR7$%+CX0`B5-'HP@$(#*(<8/RB+&!\H@A@\` +M`,TBRB/F#,HD)@#8!N;PRB4&`1:X\05O[Z5XSW&``&`�A1``'8`*D!J0") +M@>#*(($/``#$"!_*:C/<:``+"`P@3A@SW&``)PLX'\%H<]Q@`!,'?`A +M`0!-D42Z#0H>``Z!B;@.H0L*7@`.@8NX#J$-"IX`#H&-N`ZAX'[@>`T2`C8$ +M(+Z/8````,]S@`#HQU1[QW*``%C("'$%\@/(')`7")X"!"&!#V$````3"8$/ +M`0````#8`+,!V!SP#,P#$@$V&PC>`3(1@0`!BPT(00``V`&K\_$!X`&K"_`Q +M$8$``(L+"$$``-@`J^?Q`>``JP+8X'\8JN'%X<;/&4V>,2HKF(!Y:]]P*C! +MQN!_P<7@>/'`*@Q/[\]P@``0%P"`H<&;")``SW:```@PSW6```PP`(4@AF4) +M``#/<(``F",$@$#!#0B>`$\A``%`P(CI7@UO]`#8/@U/]!H-3_;/<(``U`4` +M@(#@RB`!!\HA(0'*(H$/``"A`,HC80^("R']P"OA!2"&">D`A8?H(@UO]`'8 +M\@M/]B"&(*41Z5H.3_1_V`JXSW&@`-`;$Z%_V!"A`-B5N!"AI@@O\@'8[0-O +M[Z'`\\L]V@`"4(,B.,0[1 +M$;ZX"*)`@0B"!""##X````!$(`$"+[L&N65Y!""`#P`!``!!*$,#)7LLN`5[ +MP1K8``KM+RE!`TXA@`<2""``$"4-$/GM00-/[_'`U@IO[YAP&P@4!`HAP`_K +MN!3P+;G`N<]W@`!,'?`G3Q!2($X"P1AY^X +M`*4!XZ$"3^_/<8``3!WP(0``SW&``)3'NQ`"!KH0`P9"H6&AO!`"!KT0``9% +MH>!_!J'@>,]Q@`!,'?`A``#/<8``E,>^$``&%B$"``*2&K$#DANQ"(HX&0(` +M`-C@?QVQ\<#/<(``U`4`@(;HE@EO]E38`_``V$0@`@(;"!X!`-O/<9\`N/]] +MH0+;SW&``!`78*'/<8``"#!1($"``('/(&(`T"!A``"A/0B>`,]Q@`#<.0"! +M,0H``,]P@``'%`"(0*$7"%$`SW&``,@O`($+"%(`:K@`H0'9SW"``(P;-@[O +M_2"HT<#@?N!^X'C@?N!XSW&``*PS7!G`!YVXGKC/<:``R!P-H>!XX'C@>.!X +MX'C@>.!XX'C@?O'`(-O/-L.`9`!V`+P`-@-`6_OH<#@>/'`SW*@ +M`"P@0!($`$`2!0`/"=\"!""^CP`&```@\D$)'P//<0``$"<#\$`2!0#/<*`` +M_$09@.RX`B4``0/T[PA"@!T(@@\``!`G"B'`#^MRBB":"FG;J0'O\(R[T<#@ +M?L]PH`#T!_'`5P@>0R>`&8`P>3A@`[B6($(%SW&@`,@?'J$0V`ZA`=@5&1B` +MU@[O_X'8+P@>0\]P@`"0.0'9(*`#R*00`0":N:080`!^#*_^`=C/<8``*#0- +M@0'@#:$#V<]PH`#T!RJ@T<#@?O'``-D*V,]RH`#('QZB$-@.H@'8%1H8@"AP +M!_`!V00@@`\@````42``P\PA(8#,("&`$O0A"Q]`SW*@`/Q$'8)9@@#9V0K? +M@@0@OH\`!@``YO7G\2T+'D#/<(``D#D!V2"@`\BD$`$`FKFD&$``\@NO_@'8 +MSW&``"@T#8$!X`VA42``PP#8"O3/<8``K#,0@0'@$*$`V)BXT<#@?O'`2@\/ +M[PAVSW"@`"P@L(`+\-()3_#/<`\`0$+>"._RJ7$?"%``SW"@`-0+&(!"(``( +M2"```-\(A(-]!P_O"B'`#^MRSW```,XB7MN*),,/10#O\+ASX'CQP-H.+^\! +MV:7!&G#/=8``_`5:=>8,;_^+<``4A3`!%)$P"PA1($`E$A$+#5(`'0U2`0HA +MP`_KO\$HD0`!,)0"`)@$.`*AP`!:.0``6E$`/##(D>G", +M),.O)?0`%@!!`!:/0``6@$``%@!!A0P3)"COSW"``/@2`(!`+,T@M7T0X+A@ +M;@QO_P39SW"``/@2`(!,(4"@'67,)V&3&O0`V(RX%_`*(<`/ZW+/<```*B6W +MVTHD0`!]!Z_P"B4`!0HAP`_K +MW\`0`@8O*8$``B=`$"3J,FC/2MC$PN.`P`F@1^!`+`2%GD`&0(% +M`"V!$PLAP(`)\@`F@1^!`+`2%GD$&0(%$"("@"\I@0`")T`0X/5"(T`@@.#F +M!LW_>@^/\[D%+^^EP.!^X'C@?N!XX'[@>.!^X'C@?N!XX'[@>.!_`=C@?N!X +MX'[@>.!^X'C@?N!XX'[@>.!^X'CQP$(-#^_/<(``N#'/<8``2.?/<@``8")6 +M#F```("^#4_V+@A``$8+``#/=8``8"0@A6!Y`]@&Z""%8'D$V(3H4@\/_L]P +M@```>`#>R7'.#N_PBB(%!<]P@`!X!L"@SW"``.$%P*C/<*``+"`P@,]P@`#, +M!<]U@`!P>B&@SW"``*![P*"I<,EQD@[O\(HBA`O_V!T%+^\0K5$BP-'/-4@X@S5(.$$-G@`HR*0>Y(E +M"<(`)0M"`"&0>I(9"<(`&0M"``"0.)(-"$(`#0D"``D*WE(!V`/P`-C@?O'` +M1@PO[P#9O@D@``AV"'<#D,]U@`!X!L]Q@`!(00*U<@D@`.EPDNC/<8``0$%F +M"2``Z7`1Z`*5UW```($WS"""CP``@/,)\@.6".`#M@Z6:+@.MA?PSW&``%!! +M$@D@`.EP+I8)Z`.69+DNM@3@`[8(V`*U!_`O>`BX*+DO>05Y(K7)<&8)(``` +MV<]Q@`!0E`>Q`I77<```B(Y@#F'VRB"!`PT$#^_@>/'`G@LO[P#;2X"RD$HD +M@'%H=EUE4R5`$$X@!`$`)$(#J"```@$5CQ3(80'FYW@%>['K2B2`<0#>J"`` +M`@$5CQ3(80'FYW@%>Z7K`-A,)`"`RB0-<>@@+0(!%8\4#F$!X.=^Q7N7ZT`B +M``,#\`S@5B*.`M%PX(#`@N=^Q7M-]^&`P8+G?L5[XH#"@N=^!2.#@^[S`-@6 +M\`&`08('>@4C@X!0Y?GU0"2.`!4.E1$!%8(4R&$!YD=X^0ZTD05[Z^L!V$4# +M#^]`B6"(#0N!`$&)88@)"X```-@'\$*(`HGY"@&``=C@?P]XX'A`D6"0#0N! +M`$&189`)"X```-@'\$*0`I'Y"@&``=C@?P]XX'@(<<]P@`"X,5$#8```@.!X +M2X`#D%A@X'\X8.!X\<#AQ:N``Y"X8#A@2''2"F_]:'+9`@_O2X`#D%A@X'\P +M8.!XSW"``+@Q`(`1`V```-G@>/'`1@H/[\]U@`"$)`"%`-Z$Z*H)````I0GH +M(I"'Z5H-``#Y\;H)````I0;HPI!3)@Z1^O//<8``>`8@@<2Y!29.D`WR+RZ! +M$\]Q@0",'$XF@A?P(8$`0'D(\,X)``"`X.`-HO'*(&(#20(/[\]P`@`L)<]Q +M@0",'`2ASW`"`*`F`Z'/<`(`V"4"H<]P`@!X)`&ASW`"`"@EX'\`H?'`H@D/ +M[ZZ0"'9##7(0XY`+A@S@\&`/>0BY*+@%>2ZV0">`$P[A`[8#:1\-9!!&(-`` +M(@@@`,EP`B4-E`)WU`?O__!_`_``V`*VO0$/[^!X\<#AQ0X-[_\(=3X+(`"I +M<&8*(`"I<+D!#^_@>.'%X<:@@6"`P8%G?6""I'NA@*=^H8+$?:5[HH'"@*=^ +MHH+$?:5[HX`#@0=]`X*D>`4@_H`!V,!XP<;@?\'%X'CQP/H(+^^X<@AU*'?/ +M=H``N#&8" +M&6'MZ0#8%O`R"F``2'`2Z##9(*#NL`#:(!B"`"@8``%`(`$,*Z`&&$0!4[!" +ML"$8@@#U``_O\<#/".P.!^ +M\!P($"(,\`&P^B$UUGPGW!@<)]&0T2$`.!&0T# +M$'AE"?!B@2.!8GT)#4,06&`#\`#8C0`/[^!X\<#/<(``N#%:"6```(`'Z,]P +M@`!X!@"``^@`V`+P`=C1P.!^X'\+@.!_"X#QP-X/S^XZ<$`@$P(H=AIR3",` +MHLHAP0_*(L$'RB"!#P``NR'*(X$/``"R`VTR),,@1"6.$0`0!`':>\&[SW:``&1+]";.$&"2 +M!R,#`0LC@(,/]`+EKGT"XM4-8I`"X`PE@(0!V,(@"P!Y!\_N`-@`(-`D`!@" +M("$1@"!AN`]X(1D"(+[QX'CAQ7*0!?``VE&P`>-P>T%I'PK"`$N`;6*,)<.? +M49#`(F$`]//C"I*!/'`X<4(=<]P@0`,&0"`@0@> +M``Z5YN!X``L`(Y4`VH(@P0=1M3*U&6$P>:(/[_^I<(#@`=@)]`*5XKC,)2*0 +M!/*BN`*U`-A)"%$`SW&``$P=J7#V"N__(X$BE40A`@$2Z`7J+@L@`*EP$N@! +MV,]Q@`!P>@ATAK$PO"(-(`"'L0CP@.+,)2*0!/*BN2*US0;/[O'`X<41"%$` +M,@B/^RH/;_L`V`SP!-@`V2AR0@WO_RASSW&``(0D`*'/=8``A"0`A9(+[_\` +M@%(,(```A8T&S^[QP`X.S^X(=1(.[_]`(!`""';/<(``1,D`D,]W@`!0E!<( +M'@)`)@`4Z7%6#R_]`]I`)H`2"/`$;NEQ1@\O_0/:0"8`%"9O.@\O_0/:SW"` +M`'@&`I`&MP#>#_`!V"\F!_``((PC`*P(\B$5@!`!X`]X(1T"$`'FSW"``.0Y +M((``@2T.!1"$+@(5)W$.E941@@`0X!D(A`#I<%1NQW*!`"09.@SO_Q3AV^@` +MV-KQ(16`$#3H`Y4`W@+@`[4.E6*X#K4"\`'FSW"``.0Y`(``@$$.!1`R((`C +M=^C/>JEP1@WO_W#9<>@"E0\(G@#6"2``J7`4Z`+:SW"``'!Z2'$FL#"Y)[#0 +MJ,8+(`!(<`CP`Y5BN`.U#I4"X`ZU607O[B$5@!#/#R9.$`LF +M@)`*]`'A+WG/<8``>`8`@:&X`*$*\,9Z0*V&(/X#)=IF"B_P`-O1!._N`=C@ +M>/'`X<6CP<]R@``(!V**(8K/<($`R`JCB@`2A``$(X,/``#`_PZ()=I"*X4! +M0,(`VP2XAB'_`$'#0KE"PX8@_@,!VMASL'T^"2_P^'4!V(4$[^ZCP.!_`-CQ +MP`7H(I"DN2*P;@X/](H*;_X`V,]QH`"L+QF!B;@9H0'8T<#@?N!X\<#>"\_N +MSW6``%RM!XT$Z`:-!:T`W@X-+_;)<`6-)HWU"0&`QJW%K<>MSW"``'@&&03O +M[@"`X'CQP,]P@`"@>2"009`'"8,`(;`&#```SW"``'@&`(#1P.!^X'CQP.'% +MSW6``%RM'@PO]D`E`!_^#X_X`-A2'000SW"``'@&U0/O[@"`\0"0!?)`(0\$`_!`(0\"1"`!`Q4)$0((<88A +M_`.,(0*``ME#]@#9&.'HN-$@8H(&V`+T`-@X8`]XV&!`(!`"J7!Z".__+I4" +M(`0$()8`V%$AP($O)`@!RB4!``7R#)93(,4`"B8`!T`D!S'I<"1N0"8"%)() +M[_4*D>1!I$0ND5XC>@*(<`/ +MZW)NV(VXLMM*)```L0-O\`HE``'B"\_S!>T"E:.X`K7%`N_N`=CAQ<]R@`#, +M+V""'.O/=8``1,D?A1D(W@8-A0&C,!6!$#(5@!`(N25X`+,*\!$(G@88E0"S +M&I4!LQN5`K,`V`"BX'_!Q<]Q@`!$R2Z14R'!@`ST)0J>4L]Q@`!$R3^!!"&^ +MCP`8```(\@#:0K#/<9\`N/]7H>!^X'CQP.'%SW6``%RM0(4'ZL]S@`!X!B"# +M17D@HP#9(*6'Z,]P@`!X!@"`!.C*#F_Q#=@5`L_NSW&``$3)/X$5"5X&(Y`$ +MX2.P+I!DN2ZPX'\$V!T)W@8CD`CA([`ND&RY+K`$Z"*0@KDBL`C8X'X3"9X& +M(Y`(X2.P+I!HN2ZP]O'@?P#8X'CQP$X)S^[/<8``<'IGD4:1$+ME>H[J"B'` +M#^MR;MB-N(HCAPE*)```<0)O\$HE``#/=8$`#!D@A<]V@`"$).BYQB*"C___ +M$_X/\@H.S_4`V`"E`(9F#J__`(#:"D_[[@QO\0W8#?`+(0"`((8%\@GI`I&# +MN`3P!>D"D82X`K$]`<_NX'CQP+X(S^X(=\]Q@`!TR5$BP-'/<(``1,D`V@3R +M5)##NF")HHD(NP4ET!`?@,&!SW.``,PO42!`@L]P@```>`+9U2#B#,HA80#5 +M(.$$.G$#N31Y5G@`(8T/@`!8L"&`$PYD$`"C#PY!$`"0"P@"!`#8,/`"A1\. +M`1`4C2OH`X4D;^"EL@DO_07:`(7"J`#8%*T?\`'@VPX!D!2-&PA0``2%)&_@ +MI8X)+_T%V@"%PJ@!V!2M'PX#=````(#@I<]R@`!^!@"*#R!`!.H,[_T`J@'8 +M30#/[O'`Z@^/[@AU.G$:0R1SW.``%BP:'`'H0"#(8,0X'H*[_UC@P&.*G$`K0&.QKB%N`&M`(X"K2#8`ZT!A@&E +M0X<*<$(+[_VI<^D'C^[AQ>'&SW&``*!YSW"``-`T`(!CF0)[#PMR`$`A`@(< +M:P3P&`.>$HDP'$`VZ@@@`%M8@D-`Q`!XP*1"0L``&*Q +MP<;@?\'%X'CAQ13I88"E@(;K0X"$ZJ"@(:`,\$"`>F(+#8$0>6$AH`3P0X`Z +M8D.@`-DEH":@X'_!Q>!X\<`C@`3I)@@```?P$@@@`&"`"",``-'`X'[@>$"` +M(8`$@%EAX'\B>$*`(X``@%EAX'\B>`#;8J!@H&.@8:!FH&6@)Z#@?T2@(8"$ +MZ0.`!.@`V`/P`=C@?D&`%PF$`"*`(*`C@"&@`-DCH"*@!O`B>D&@0(!982"@ +MX'[@>/'`X<4H=2.`"',/Z:(/S_\W"$0#&>U"@R.#IJ,Z8D6C!X-88!;P?@_/ +M_R"#$PA$``OH$'7*($4#!J-!@_#Q`-@#Z0L)10,`V`3P!:,'@Z:CL0:/[O'` +M.@ZO[@AQSW*@`,`OHQ(`A@#=]0@>@0?(0!H8@`W(#0B1`>8/K_XH[A4'@% +M]*(/[_&+<1GP$PG1#1MX$'B2#^_QBW$0\`L)$04<>`GP#0F1`@``#8SW*I`*3_N:(`%`$Q@K@WHAJB2/!="1X"3"$`H-$@XJ%" +M],]RI0"L_\]P@`!,';BB!(!6$``!%.`">P/C(KMX8WA@2"!```6X12!``Q:B +M02C`(<"X=V@HP`0A@0\````@);EE>"5XB;B.N!FB'/`HP(#@RB'!#\HBP0?* +M("$.SR`A`\HC(07/(R$#RB0A`#0&(?#*)<$`!;VE>,]QI0"L_Q:ASW*@`,@? +MSW6@`+1'5Q4`E@#?2B1```0@OH\`*```PB0"`6\5`)8$((4/@`````0@@P\@ +M````!"".#P`&```/#!``0!(!!@L)U```V0+P`=G8<1,2`88$(+Z/`#@```0A +MAP\```"`S"8A@,`G81`%(T$!!2'!`04AOH,$]*`,]Q@`"T,A&! +M`>`1H4+=4/"(=(0D`I@(\L]Q@``H-!&!`>`1H0[P$PR>`<]Q@``H-`2!`>`$ +MH03P4R0^@P3R`-TX\!<,7@/V"P_]SW&``*0T!8$!X`6A]/$*(<`/ZW)O%066 +M1-B,N.G;"04O\(R[".O/<8``K#,0@0'@$*'B\2CN&0B>!L]R@``H-"^"`-T! +MX2^BD;T+\!T(7@;/$RHLX*;_^(<9B]1?!Q%026;Q4%E@HA +MP`_K`1H;#Q$Q(`AO"XRB`A +M`.@)8?_/(*$#:Q4!EE@5`)8+($"`'/)O%0"6SW6@`/0'4R!`@`'8#/()I>!X +M`-@)I<]Q@`"D-`B!`>`(H78/[_T!V`/8"J4%W9B]`_``W9?M%PC>(1T)$2`! +MV<]PH`#T!RR@`]D%\`/9SW"@`/0'):!1(("B*`\"^1?P`Q(!-L]P@0#`$P\) +M``#/<($`B!0;"0$`DA$``:JXDAD$`)X1``&JN)X9!`#/<8``M#(/@0'@#Z'/ +M<(``L+DA@,]P@`!,'0.`%)`="0$`SW&``!`5&H$[@21X42``@HP((O;*(&(` +MJ7`(W*L"K^ZBP.!XH<'QP$(*C^XH=0AV&G($(;Z/`0``P&AW+?0O#1X21"4` +M%B.X(6@$)8`?!@```#&X.&`$)8$?!@```==Q`@```1L]Q@`!$R3*!"PF>`D*P0[!$ +ML.!_6;#@>.'%SW6``%C*":4JI7BU2Z4!V!FUX'_!Q4HD`'H`V:@@@`(`VL]P +M@`!8RC5X0*`!X>!^```1`````$`!``````"H$D-U`08`!0``````````W#2` +M`'`U@``TAX``I!N``(`'@`#0!("!#QH;(@``&R4"`!M````;<0]%`"(`7``Y +M!P``8@9@`&(``%@X8$7`$'!%P!!X1<`0D$7`$&D``&$(`%AN^`\`80``$R4` +M`!,D)!#`$0"`$R0X',`1#P`3(@$`$S`$*,`1#W,3(H(!$S`$*,`1#W03(@(" +M$S`$*,`1#W43(D("$S`$*,`1#Q05(@$`%28/!,B +M!`#*$0```20```$E!@``80]V$R(L2,<1#W@3(@``QA$#``$D```!)0``$R7" +M+!,D!"C`$0)&$R0$*,`1PE\3)`0HP!$/10`B`%P`.2P``&0``!,D`0`3)3@< +MP!$/=Q,BX!S`$0(``6(/`1,B!`C`$5P%P!($*,`1#Q,"(F`%P!($*,`1#Q,' +M(F0%P!($*,`1#Q,$(@(`<7`'``!A_P`3)0(0$R0$*,`1```3)0``$R3(2<<1 +M!@``80``$R4"$!,D!"C`$0``$R5)`!,DR$G'$0]P$R(!`!,P!"C`$0,`$R0` +M`!,E!`C`$0``$R0X1<`1:`7`$A@HP!$/$P,B!```80``6#@``!,D`0`3)3@< +MP!$``!4D````(4@%@($``,`6#QM0(DP%@($``,`6#QL:(E`%@($``,`6#QL9 +M(E0%@($``,`6````A40%@($``,`6#QL$(AP$&V8;`1MH%!S`$`H`&T`$`!MN +M"P``80\<'2(!`!TF^0\`85@%@($``,`6!0`;8M`$@($/&ALB```;)0(`&T`` +M`!MQ9`P`$`#`!A$!``0G_``$9```&R0"`!LE.!S`$=`$@($/&ALB```;)0(` +M&T```!MQ```;)4``&R0P',`1T`2`@0\:&R(``!LE`@`;0```&W'X!8"!``#` +M%@(!$V1"`1,D!"C`$9`Y@($``,`6!@$38@0(P!`$`!-D#UP`(@H``$``!@!P +M&0``80``$R0``!,E``#`%P`(6##((,`0<$7`$!`(P!```!,E`P`3)!P(P!$< +M",`1```3)`0(P!$/%!4B!``5)OO_,#(#`!,D&`C`$0\4%2("`!4F!``P,``` +M$R001<`1&`C`$0`06#`/?!,B"`#,$0``$R4``!,D-$C'$0][$R(!`!,P!"C` +M$0\4%2("`!4F_P`3)0(0$R0$*,`1#Q05(@(`%2:X!H"!``#`%L(L$R0$*,`1 +M`D83)`0HP!'"7Q,D!"C`$0]-$R($$,41`@`3)/`8!8"!`!H;*`\;#B*P!8"!``!`%@$`&R8``$`7 +M&`2`@0\:&R(``!LE`@`;0```&W$````````````````````````````````` +M```````````````````````````````````````````````````````````` +M````````````````````````````````````````````````````````$`*` +M`)0&@````````````"@`@``````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````)#1$4"@T3%QD9&1D)"0```):`````````EH```````!S+ +M@`"\<0``,(`````````Q@```B(@S,S*`````JJJ*,X`````````T@``````` +M`#6`````````-H`````````W@````````#B`````````.8`````````Z@``` +M`````#N`````````/(`````````]@```JJH*`#Z```!3A8B(/X```````#`P +M@````````#&```":F5A5,H````"JJJHS@````````#2`````````-8`````` +M```V@````````#>`````````.(`````````Y@````````#J`````````.X`` +M```````\@````````#V```"JJ@H`/H```%68F:H_@```````4#"````````` +M,8`````````R@````````#.`````````-(`````````U@````````#:````` +M````-X`````````X@````````#F`````````.H`````````[@````````#R` +M````````/8`````````^@````````#^`````````,(`````````Q@``````` +M`#*`````````,X`````````T@```FGD``#6```"JJJJJ-H`````````W@``` +M`````#B`````````.8`````````Z@```JJJJ"CN`````<)FJ/(`````````] +M@````````#Z`````````/X````````#__P``I0$!`+D!WP"Q`!L`%@$;`*\` +M&P`4`1L`;`"@`-$`H`!O`(,`<0"#`'8`@P!S`#,`;@`S`'``,P!R`#,`UP`S +M`-0!!@#0`0``?@`\`.,`/`!X`$D`W0!)`'\`6@#D`%H`J@`_`*L``0`/`3\` +M$`$!`'D`:@#>`&H`J`````T!``"F`#<`IP`!``L!-P`,`0$`!``(`)P!S`"= +M`#,6`'PT%P"`-1@`A#89`(@W&@",.!L`D3H<`)4['0"9/!X`G3T? +M`*$^(`"E/R$`)$D&`BQ*"@(T2PT!/$P/`61-$0%L3A,!=$\5`7Q0%P&$41D! +ME5(=`9U3'P$!!````@4!``,&`@`$!P,`!0@$``8)!0`'"@8`"`L'``D,"``* +M#0D`"PX*``P/"P`-$`P`#A$-``%```0"00$$`T("!`1#`P0%1`0$!D4%!`=& +M!@0(1P<$"4@(!`0````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M````````````````````````@%6```````"R#,@`_________P`!__\"`___ +M_P3______________________P7_!O\'_PC_"?\*_PO_#/___PW___\.____ +M#____Q#______________________________________________Q'___\2 +M____$____Q3___\5____%O___Q?___\8____&?___QK___\;_____QS___\= +M____'O___Q____\@____(?______________________(B,D_R4F)___*/__ +M_RG_________________________________________________________ +M_____________________P```0`!`0`````````!```````````````````# +M``````````$`````````!"L!``````"X:@```0```!#2`0`"````A-$!``,` +M``#L&P(`!`````0K`0`%````?!$!``8```"XY```!P````02`0`(````O#T` +M``D```!`7@``"@```#C!```+````R#<```P```#L'@(`#0```.S=```.```` +M+-X```\```#HW0``$````"C>```1````A#\!`!(```"4Z`$`$P```(0Q```4 +M````[&4!`!4```"(40$`%@```-AA`0`7````\-`!`!@```!P``='L``*QV +M``#\C0``@'8``/R-``#\C0``_(T``/R-``#\C0``_(T``/R-``#\C0``#'8` +M`/R-``#\C0``_(T``/R-``#\C0``_(T``/R-``#\C0``_(T``/R-``#\C0`` +M_(T``/R-``#\C0``_(T``/R-``#\C0``S'T``/R-``#\C0``D'\``/R-``#\ +MC0``_(T``/R-``#\C0``V(```,!_``#\C0``_(T``/R-``#\C0``_(T``/R- +M``#\C0``_(T``/R-``#\C0``_(T``/Q[``#\C0``_(T``/R-``#\C0``_(T` +M`/R-``#\C0``N(P``/R-```8C0``)(H``/R-``#\C0``K'0``.2)``#\C0`` +M_(T``"A^``!`?@``_(T``/R-```T>@``\'8``/R-``#\C0``_(T``*2$``#P +M?0``_(T``/R-``#\C0``_(T``/R-``#\C0``,'T``/R-```DC@``L(X``)". +M``#(C@``7(X``$2.``#0C@``((X``/R-``#\C0``_(T``/R-``#\C0``_(T` +M`/R-``#\C0``/'H``/R-``#\C0``_(T``/R-``#\C0``_(T``/R-``#\D``` +M")(``#1U``"`=0``_(T``/R-``#\C0``_(T``/R-```@=P``_(T``/R-``#\ +MC0``_(T``/R-``#\C0``_(T``/R-``#\C0``_(T``/R-``#\C0``D(\``-2. +M``!`D```))```&2/``#LC@``6)````R0``#\C0``_(T``/R-``#\C0``_(T` +M`/R-``#\C0``_(T``%1W``!`>```X'<``'"0``"8=0``R'@``&!Y``#\C0`` +M_(T``/R-``#\C0``3'D``%!Y``#\C0``_(T``/1X```````````````````` +M```````````````````````````````````````````````````````````` +M``````````````````"XG````)L``!R>```HG0``,)\``````0#_____```` +M`/__________`0```/@3```````````````````````````````````````` +M`@``````T/X``````````!@@H``@(*``0"&@`$@AH``<(*``)""@`$0AH`!, +M(:``*""@`#`@H`!H(:``<"&@`"P@H``T(*``;"&@`'0AH``X(*``/""@`'@A +MH`!\(:``F!$```#_`P!0$P```/\%`-P1````_RT`N!$```#_/0`T$0```/\$ +M`%P1````_R4`R,,```#_W0!L$@``$!!,`#@3````_R(``!(```#_)@#8$@`` +M`/\H`*02````_R``N,(````@``#\P0```/\P``````````````$!`#P\/#P\ +M/#P\/#P\/#P\/#P\/#P\/#P\/#P\/#P\/#P\%145%3P\/#P5%145/#P\/``` +M```````````````````\/#P\/#P\/#P\/#P\/#P\/#P\/#P\/#P\/#P\/#P\ +M/!45%14\/#P\%145%3P\/#P`````````````````````/#P\/#P\/#P\/#P\ +M/#P\/#P\/#P\/#P\/#P\/#P\/#P5%145/#P\/!45%14\/#P\```````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M`````````````````````````````````````0```````````````````(0" +M`0!`J@``H",``$"J``!`J@``0*H``(0,``"DWP$`?-```$"J``!`J@``H#0` +M`*`T``"@-```H#0``*`T``"@-```H#0``$"J``!`J@``0*H``$"J``#(50`` +M0*H``$"J``!`J@``0*H``$"J``!@T```0*H``$"J```\P0```````&CI``!L +MZ0``M`(``*`"``"L/@$``````/BW``!`O@$`(+@``&B^`0!$N```D+X!`+25 +M@`"<+H``()"````````````````````````````````````````````````` +M`````````````0`"``(``P`$``0`!0`&``8`!P`(``@`"0`*``H`"P`,``P` +M#0`.``X`#P`F`"<`*``I`"H`1@!&`$<`2`!(`$D`2@!*`$L`3`!H`&D`:@!J +M`&L`;`!L`&T`;@!N`&\`<`!P`'$`<@!R`',`8!`0`E`"4`)0`!`%4#50-5`P$`!``$``0``0#<`=P!W`$! +M`&,`8P!C``$````````````R``4`,@`%``(`"`!D`*```P`````````````` +M```````````````````````````````````````````````````````````` +M````````````````````*.>```$````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M``````````````````````````````````````````````````#_```````` +M`(P*```````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M``````````````````````````````$````!```````````````````````` +M``````````````````````!@W`$`%0````,```#(.(`````````````````` +MK-L!``4````#````R#B``````````````````*#;`0`*`````P```,@X@``` +M``````````````!LV0$`"@````,```#(.(``````````````````O-H!``H` +M````````Z#B``````````````````+S:`0`*`````````.@X@``````````` +M``````"\V@$`"@````````#H.(``````````````````O-H!``H````````` +MZ#B``````````````````+S:`0`*`````````.@X@`````````````````"\ +MV@$`"@````````#H.(``````````````````O-H!``H`````````Z#B````` +M`````````````+S:`0`*`````````.@X@`````````````````"\V@$`"@`` +M``````#H.(``````````````````O-H!``H`````````Z#B````````````` +M`````+S:`0`*`````````.@X@`````````````````"\V@$`"@````````#H +M.(``````````````````S-L!``H````#````R#B``````````````````"3= +M`0`%`````P```,@X@`````````````````#$V`$`"@````````#H.(`````` +M````````````--D!``H`````````Z#B``````````````````&3G`0`*```` +M`P```,@X@`````````````````"8V0$`"@````````#H.(`````````````` +M````0-H!``H`````````Z#B``````````````````,#:`0`*`````````.@X +M@`````````````````!`VP$`"@````````#H.(``````````````````9-P! +M``H`````````Z#B``````````````````-3<`0`*`````````.@X@``````` +M````````````W0$`!@````````#H.(``````````````````$-T!``8````` +M````Z#B```````````````````````#(.(``R#B``+@@H`!L(*```(`!`/]_ +M_/\``````````.@X@`#H.(``I""@`#@@H``!````_/___P``````````"#F` +M``@Y@`"H(*``/""@`!````#'____```````````H.8``*#F``*P@H`!X(:`` +M0`$``#_^__\``````````$@Y@`!(.8``L""@`'PAH```#```__'__P`````` +M`````````````````````````)#9`0`5`````P```,@X@```````4``````` +M```X!H``?)F``!@````\F8``````````````````.`:``'R9@``8````/)F` +M`````````````````'\``````````'\``````````````````*![@``````` +M`````/__```!``````````<``````````````````````0(#!`0$!`0%!@<( +M"`@("`D*"PP-``!N.V@[8CM<.VXZ:#IB.EPZ;CEH.6(Y7#EN.&@X8CA<.&XW +M:#=B-UPW;BEH*6(I7"EN*&@H8BA<*&XG:"=B)UPG;AEH&6(97!EN&&@88AA< +M&&X7:!=B%UP7;A9H%F(67!9N%6@58A5<%6X4:!1B%%P4;A-H$V(37!-N$F@2 +M8A)<$FX1:!%B$5P1;A!H$&(07!!N`F@"8@)<`FX!:`%B`5P!;@!H`&(`7`!4 +M````;CMH.V([7#MN.F@Z8CI<.FXY:#EB.5PY;BMH*V(K7"MN*F@J8BI<*FXI +M:"EB*5PI;BAH*&(H7"AN)V@G8B=<)VXF:"9B)EPF;B5H)6(E7"5N)&@D8B1< +M)&XC:"-B(UPC;B)H(F(B7")N(6@A8B%<(6X@:"!B(%P@;A)H$F(27!)N$6@1 +M8A%<$6X0:!!B$%P05Q!2$$T021!N`6@!8@%<`6X`:`!B`%P`5``````````` +M````'0```````````````````````````````````````````````````"!< +M`0`(`````P```,@X@`"L!X``+`B``*P(@``L"8``"@T1%`H-$109&1D9"@H` +M```````&!@8&"0D)"0`&````!08'"`T.#Q`5%A<8&0`````````,%)J:'"2: +MFIJ:FI.:;H$_-0F+*P```#(```"/`(P`B@!#``\`(&@W````$0`^.B`1```" +M)0``#"\```(O.3D`"B4\MT=OB@`'%"=B+@```@`7```6#A84`````$)"%P`% +M$`H@,$````8&"A+V%?8&]@GV#/8/]@#V`P"``````%A;8V,Q```,$!08(`@$ +M```\.#0P+"@D(!P8%!`,"`0`"P<#`#LW,R\K)R,?&Q<3#PL'`P`[-S,O*R_S7_2_]@_W/_A?^6_Z;_M/_!_\W_V/_A_^G_\/_V +M__K__?_________]__K_]O_P_^G_X?_8_\W_P?^T_Z;_EO^%_W/_8/]+_S7_ +M'O\&_^W^TOZV_IC^>OY:_CG^%_[T_<_]J?V"_5G],/T%_=G\J_Q]_$W\'/SJ +M^[;[@?M+^Q3[W/JB^F?Z*_KN^;#YWXJ?AE^!_XV/>0]T;W_/:P]F/V +M<+F#NI:[JKR^O=*^Y[_\P!'")\,]Q%/%:L:`QY?(K\G&RM[+]LP/SB?/0-!9 +MT7+2C-.FU+_5VM;TUP[9*=I$VU_<>MV6WK'?S>#IX07C(>0^Y5KF=^>3Z+#I +MS>KJZP?M).Y"[U_P??&:\KCSU?3S]1'W+_A,^6KZB/NF_,3]XOX``!X!/`): +M`W@$E@6T!M$'[P@-"BL+2`QF#8,.H0^^$-P1^1(6%#,54!9M%XD8IAG"&M\; +M^QP7'C,?3R!J(88BH2.\)-`.)8YK3K#.]D\[ST$/QE`+D%"0E9#:D1]1:JJ`P``````JJH#``#X +M````J@/___\``%"G]%%394%^PZ07&I9>)SK+:ZL[\46='ZM8^JR3`^-+5?HP +M(/9M=JV1=LR()4P"]?S7Y4_7RRK%@$0U)H^C8K5)6K'>9QNZ)9@.ZD7AP/Y= +M`G4OPQ+P3(&CET:-QOG3:^=?CP.5G)(5ZWIMO]I94I4M@[[4TR%T6"EIX$E$ +MR,F.:HG"=7AYCO1K/EB9W7&Y)[9/X;X7K8CP9JP@R;0ZSGT82M]C@C$:Y6`S +M49=%?U-BX'=DL82N:[LE+?X>U(CTW.KX@)+ +M/'^,JJU5F!RCKL@/"M2^:>\6&I0@WT_*'*#"RI;\CNFH#`ER"%NTK',^* +MDK1YI_#R!_.AXFE.S?3:9=6^!08?8C31BOZFQ)U3+C2@5?.B,N&*!77K]J0Y +M[(,+JN]@0`:?<5Y1$&Z]^8HA/CT&W9:N!3[=1KWF3;6-5)$%7<1Q;]0&!/\5 +M4&`D^Y@9E^F]ULQ#0(EWGMEGO4+HL(B+B04<*?*'I#T)\R1Z$ +M^`````"#AH`)2.TK,JQP$1Y.U:X])SDM-F39#PHAIEQH +MT51;FSHN-B2Q9PH,#^=7D]*6[K2>D9L;3\7`@*(@W&%I2W=:%AH2'`JZD^+E +M*J#`0^`B/!T7&Q(+#0D.K<>+\KFHMBW(J1X4A1GQ5TP'=:^[W9GN_6!_HY\F +M`?>\]7)2XRMPQUQ"%8T)`(I<3(!'& +MA'TD2H7X/;O2$3+YKFVA*<=++YX=\S"RW.Q2A@W0X\%W;!:S*YFY<*GZ2)01 +M(F3I1\2,_*@:/_"@V"Q]5N^0,R+'3DF'P=$XV?ZBRHPV"]28SX'UIBC>>J4F +MCK?:I+^M/^2=.BP-DGA0F\Q?:F)&?E3"$XWVZ+C8D%[W.2[UK\."OH!=GWR3 +MT&FI+=5OLQ(ESSN9K,BG?1@0;F.";-]!A9;@&WFNRHFD^#96Z5 +MYG[F_ZH(S[PAYN@5[]F;Y[K.-F]*U`F?ZM9\L"FOLJ0Q,2,_*C"4I<;`9J(U +M-[Q.=*;*@ORPT)#@%=BG,TJ8!/'WVNQ!#E#-?R_VD1>-UDUV3;#O0U1-JLS? +M!);DX[71GAN(:DRX'RS!?U%E1@3J7IU=-8P!S4M+;DC-6 +M$.D31]9MC&'7FGH,H3>.%/A9B3P3Z^XGJ4('SK\H^Q&BY+#0D.%]`H\)RPQT6 +M#"7BO(M)/"A!E0W_<0&H.=ZS#`B-G820V*L`C+S3"O?D6`6XLT4&T"P>C\H_#P+!K[T#`1.*:SJ1$4%/9]SJ +ME_+/SO"TYG.6K'0BYZTUA>+Y-^@<==]N1_$:<1TIQ8EOMV(.JAB^&_Q6/DO& +MTGD@FMO`_GC-6O0?W:@SB`?',;$2$%DG@.Q?8%%_J1FU2@TMY7J?D\F<[Z#@ +M.TVN*O6PR.N[/(-3F6$7*P1^NG?6)N%I%&-5(0Q]I6-CQH1\?/B9=W?NC7M[ +M]@WR\O^]:VO6L6]OWE3%Q9%0,#!@`P$!`JEG9\Y]*RM6&?[^YV+7U[7FJZM- +MFG9V[$7*RH^=@H(?0,G)B8=]??H5^OKOZUE9LLE'1XX+\/#[[*VM06?4U+/] +MHJ)?ZJ^O1;^@X,=="PL6"X:&C0M&QLVLFYNW.Y:6K3[ +MH*!;]E)2I$T[.W9AUM:WSK.S?7LI*5(^X^/=<2\O7I>$A!/U4U.F:-'1N0`` +M```L[>W!8"`@0!_\_./(L;%Y[5M;MKYJ:M1&R\N-V;Z^9TLY.7+>2DJ4U$Q, +MF.A86+!*S\^%:]#0NRKO[\7EJJI/%OO[[<5#0X;734V:53,S9I2%A1'/146* +M$/GYZ08"`@2!?W_^\%!0H$0\/'BZGY\EXZBH2_-14:+^HZ-=P$!`@(J/CP6M +MDI(_O)V=(4@X.'`$]?7QW[R\8\&VMG=UVMJO8R$A0C`0$"`:___E#O/S_6W2 +MTK],S?]S[NQ].XN&L\%!0H>=[>I^)>7KP="PL6=MO;K3O@X-M6 +M,C)D3CHZ=!X*"A3;24F2"@8&#&PD)$CD7%RX7<+"GV[3T[WOK*Q#IF)BQ*B1 +MD3FDE94Q-^3DTXMY>?(RY^?50\C(BUDW-VZW;6W:C(V-`635U;'23DZ/!O)25* +MNT+FY:9&&AA=8P<&9)QT=.KF>GB'9$_CXZ[.8F"LS$1$BNVEI +MTG#9V:F)CHX'IY24,[:;FRTB'AX\DH>'%2#IZR["P>_Q45*C6N[MM.A86+/___P#___\!_P(#____!`7_"?\'"@8("P`! +M`0(!`@(#`0$!`0$!`0$"`@("`@("`@,#`P,#`P,#!`0$!`0$!`0!`@("`@(" +M`P,#`P,#`P,#`P,#`P,$!`0$!`0$!`0$!`0$!`0$!`0$!`0$!`0````Z`0(! +MU0#?`-H`H@!U`'\`B@4J`SD!J`&*!0"(`,H!2@'B`/D`R@'J`((`F0!TT447Z**++@`%!P$#!``%`04````% +M!@`"!``%``4```$"`0(#!```!08'"`D*```%``````````$````"`````P`` +M```````$`````@````4``````/\``/___R@`*``P`"P`+``H`#P`-`!``#P` +MC`!L`%@`2`#T`+``?_\'#Q\_`0,P````-@````P````2````&````"0````& +M````"0````4`!P(#!`8&0`.`!L`)``V`$P`:0!V`((`&``V`$P`:`"<`-(`Z +M`$'`"8`30!T`)X`Z`$[`5X!AF0,S!]D*8<@"`9)#,'8<62O, +M.0!!,TC9"J85@"!9*P!!IE:`85ELG=B)G4[LQ$XT2(,T)W9B)QJD01H3.[$3 +M$1B!$0_\P`].[,1.)W9B)QJD01H3.[$3#=(@#8F=V`D(C,`(!W[@!S1(@S0: +MI$$:$1B!$0W2(`T(C,`(!FF0!K"RU04%5$`%)W9B)Q,[L1,-TB`-B9W8"09I +MD`;$3NP$!$9@!`,_\`.JJJJJ&J1!&A,[L1,/_,`/$1B!$0W2(`T*J(`*$SNQ +M$P_\P`\/_,`/#=(@#0NT0`L+M$`+B9W8"0W2(`T*J(`*"JB`"@B,P`@'>(`' +M!WB`!P9ID`8/_,`/#=(@#0NT0`L-TB`-"[1`"XF=V`D(C,`(B9W8"0B,P`@' +M?N`'!W[@!\$L*0<*J(`*"(S`"`=X@`<(C,`(!WB`!P9ID`:PLM4%!FF0!K"R +MU04%5$`%!51`!=8=Q@0!!P\?/W___V;F```%!@$"`P0``%0`5`!L`&``7`!4 +M`(P`>``-#P4'"0L!`R@`*``T`#``+``L`$0`/``L`"P`/``T`#``+`!4`$0` +M5555`4MH+P%5554%XSB.`ZJJJ@)Q',``$````#```!>@`#AAX``@````0` +M``%Z``2('@`"`````P```7L`!(P?```````$```!?``$D1\``4````,```%^ +M``25'P`#````!````7\`!9Z-]@W_O=:QWE214&`#`JG.?589YV*UYDV:[$6/G1]`B8?Z%>_KLLF. +M"_OL06>S_5_J1;\C]U.6Y%N;PG4G2@PH33\E6"_$=ZK,CGNBLRE>:@P)@9T9Y_HV9$?E2K.X,+RHPIQ]-K/"AYI^*\ +M'19VK3O;5F1.=!X4VY(*#&Q(Y+A=GVZ][T.FQ*@YI#$WTXOR,M5#BUENM]J, +M`62QTIS@2;38^JP'\R7/K\J.].E'&!#5;XCP;TIR7"0X\5?'G,[8M(CR2%2#)28?_JGA0>J6/`_A9@`D7&MIE,=?&A+C0 +MPX*P*7=:$1[+>_RHUFTZ+``!`@0$````!`P,"`0,!`1`````@``````!```` +M`@``0``````$``!`````0`````#P80```0$"`0("`P`````````````````` +M```````````````````!```````````````````````````````````````` +M`````````````````````````````````&0````J````#@```````0$````` +M```````!`0`````"``$``@(#`P,!`@3_"!#__R3<`0`PW`$`/-P!`$C<`0!0 +MW`$`6-P!``````$````"````!`````@````0````(````$````"`````&P`` +M`#8$`@0"``````$"$`0(`````A`$"``````!`0`!`@$!`0``````````____ +M_P`````````````````````````````````````````````````````````` +M@`T````@``"`#0``@`T````@``"`#0````8````$```````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M`````````````````````````````````````````````````````````P`` +M`&@(`@`@((`/``!``&D@``!I($``:2```&D@0``@((`/``#H`&D@``!I($`` +M:2```&D@0``@((`/```PZFD@``!I($``:2```$H@``!*(0``2B(``$HC``!* +M)```2B4``$HF``!*)P``2B``$$HA`!!*(@`02B,`$$HD`!!*)0`02B8`$$HG +M`!!*(``@2B$`($HB`"!*(P`@2B0`($HE`"!*)@`@2B<`($H@`#!*(0`P"B2` +M/X$``$!!+)PP0"R<,$(D'#0*(H`_@`#86`HC`#=B#<`&2B8`<&D@0`!*)@!P +M2B8`<$HF`'!*)@!P`!8`<(``K"!`>"`@0(<`````````````_!R(MOP<2+;\ +M'`BV_!S(M?P`3<,-TQ\.!X!-PLW2_PX'@$W"C=+?#@ +M>`3<)-TK\.!X!-P@W2GPX'@$W!S=)_#@>`3<&-TE\.!X!-P4W2/PX'@$W!#= +M(?#@>`3<#-T?\.!X!-P(W1SPX'@$W`3=&?`T%!HP,!09,"P4&#`H%!.!X +MX'C@>.!X"B2`\`4@1`#@(,$'1"3^@$$JQ`"$``(`+R0"\4(A`0%"(`,!Z""B +M!`01!`($$04"!!$&`@01!P($&P@!!!M(`00;B`$$&\@!+``E`$0B/H$\`"(` +M1"+\@$`AP0#@(,$'0"/#`*@@@`$!$80"`1L*`2`@P`<$$00"!!$%`@0;"`'4 +M!^'_!!M(`40B_(`$$00"R0?O_P0;"`%"(4$`0B!#`*@@@`$!$80"`1L*`2`@ +MP`?/<:``K"\8@9JX&*$5`N`/!=C@>,]QH`"L+QB!L[BZN!BA`0+@#V38"B)` +M@`#9[@`!`"\F`/!*)D``3@`&`$\`(`"*)?\/X'@*(D"``-G.``$`;``D`"\F +M`/!<``4`*P@U"$HF0``(<0#8`B&^@.`@Q0=">0'@`B&^@.`@Q0=">>L'[_\! +MX"\M`0!`)44``B9\\0``(```*$`!Z"!B`R\@`(`O(4L``B&^@,`@A@'"(88` +MX'X1`"``2B``$$H@0!`.(D(`+R`+$LX@18"*)?\/"``%`"\M`0!`)44``B9\ +M\0``(```*$`!2B9``.@@(@,O(`"`+R%+``(AOH#`((8!PB&&`$HF``!"(/Z0 +MSB""`40@?I#.(8(!X'X)````X'@*)@#PBB"_#\H@9`#@?R\@`P#@?XH@_P_\ +M'(BQ_!Q(L?P<"+'AP^'"X<'AP`?`'!S`,>'`X'\!P%,B0H'@?$XB`X@6``P` +M`2C,```I@0``*(``X'^%>4XC`P``*,$`X'\">.!X4R)"@>!\3B(#B!8`#``` +M*#,`(`!*)```!R'$`"\F0/!*)0``$``F`"\D!`$. +M($"!`R5!`(#C#@`#``XB0H$#)<,`!2.%@#`!`0!Y"RB.!X +MX'C@>.!XX'C@>.!XX'C@>.!XX'C@>.!X8;V,)?^?[?7@?\'%X'CQP.'%SW"` +M`#PB38#/=8``!(L@A;>ZN+H$(8$/`P````>Y17DMH&(+H`\`V`"%SW&``'"S +M42"`@DR)SW"``(C",FHV><=Q@`!(OV"!5GA!@`7RE;M@H:NZ!/"UNV"AB[I! +MH`N-H[CY`N__"ZWAQ>'&P(!A@*"!`8$`)8V3`2#``*"B`:+!QN!_P<7@>*+! +M\``+0'H(]3)441'PTR!,:Y"B'`#^MRSW`` +M`,T;G]OI!>``BB2##P\-GA$`V`S<)P+/_\]V@``(PA8F31&GC:"OR746)4T1 +M`*44%``Q1JW'<8``R+X"M0")!ZT`&4(!`!M"`4%I"PH1"(HA_P\&\`#9#R&!`&&Y&'G@?RAP\<"R +M".__BB`/"H(D`CJ:<7IR6G.(=:AW"B&`(0H@P"'R"&``GL&*<.H(8`"+<8+& +M:G#B"&``R7%*<-H(8`"$P:EPT@A@`(;!B,7I<,8(8`"I<2IPO@A@`(K!"G"V +M"&``C,&I<(K!8@S@`)#"BW#)<58,X`"2PLEPA,%.#.``E,*&P*EQ0@S@`);" +MF,:0P)+!\@R@`,ERFL>4P);!Y@R@`.ERR7#I<2(,X`"__0"4"$XMPA,%V#>__0"4"%93` +M0"4!$YH,H`#)@R@`,ERE@R@ +M`,EPR7".P88+(`!`)0(7DL!`)0$37@R@`,EREL!`)0$54@R@`.ERR7#I<8H+ +MX`"O_X`D`CK@>,]Q@`"<6.!_ +M"&'@>/'`*@^O_]AP*'9(<8AU,@[O_\EP"'>I<"8.[_^H<0AQ`"Z``P1_)G\` +M*T`#)'AE!Z__Y7CQP/H+@`7/<8``.!SP(0``0'C/<*``T!N`VE"@SW"``,`@ +M`(``V0\('@+/<)\`N/\]H-'`X'[@>&"`0($!@"&!4'/,($&`X2#!!\H@(0`P +M<(;V!/8)"L4`X'\!V(H@_P_@?N!X@.'*)$UPZ"!M`L]QH`!0#"6!`1A2`.!^ +M\SW"``,2PS*$C@`T) +MY0`!&`($8Z`0CX#@RB!B``.E$8\3Z)/JSW"``#PB`X`)@!L(G@#F"^`!!]@! +MV`&ESW"@`"P@$(``I<]P@`"D,@"`'0A1`,]P@`"PQ\]Q```0)Q(/K_\%@!!X +M`_``V,]Q@`!LL`>Q`X4,&00$8PA1``"!@N#,(.*``_0!V`"A3!6`$$\(40#/ +M<*``+"#0@,]P`0"\%D#``=A!P`@<`#01V$/``-B,N$3``-@0V03:"'.8<+AP +M`":''P```'TB#>`$V'!+%8`0`>`/>$L=`A`,CX7H`86`X)0.`07/<(``7$#" +M#H`-`=G/<(``0!D@H!X+X`$&V#$%K_^EP.!XHL'QP,8,K_^8P`<@`,'BH>Y`*W/<(``M`=`B""H`=A' +MJPS!XH<'QP`,2 +M`C?7<@```$`!VL(BB@`7NL=R``X``(.Z['-`H^QR`*(*#6`$*'#1P.!_H<#@ +M>*7@'_()]B\(T``S"!`!.PA1`>!_`=B]X`_R!O8K"%$+X'\"V,S@#_*,($.' +M#?3@?P;8X'\`V.!_`]C@?P38X'\%V.!_!]@(V.!^X'CQP.'%SW6``,0PJ7!` +M)8$;S@_@#"[:`=CI`Z__81T"$/'`8@N/_Q\(M``(=0HAP`_KV5Z4QF8@`V05!D8@`;P4QF8@U09F(-$+;X;)W<.EU89&(`/EU@9 +M&(`0EU49&(`1EU<9&(`2EUH9&(`3EUP9&(`4EUD9&(`5EUL9&(!V#6`'*G!M +M`H__AN#QP`#8#_3/<(``U)9V"^__!MG/<8``=)<`@8*X`*$!V-'`X'[@>(/@ +M\<``V`GTSW"``,R63@OO_P/9`=C1P.!^X'CQP('@X<4`V`GTSW"``,^6`=TN +M"^__J7&I<"T"C__@>/'`EN#AQ0#8C/?/=8``!(NI<`X+[_\$V0N-@[@+K0'8 +M!0*/__'`FN#AQ0#8C/?/=8``!(L$;>H*[_\$V0N-@K@+K0'8X0&/__'`I,&0 +MX`#9RB!"`!/TBW#&"N__$-D`%``QA.#,(&*!"/3/<(``Y+(?@`D(7@5,<`'8 +MI,#1P.!^\<`J"8__"'?/<(``/"(#@!B(&G&-"!`!A.<`W88`)0#*($4#SW:` +M`+B60"8`$W(*[_\$V2Z.L*Y3(0``$:Y!*,`@H+E="&0``B!"`&._50K%`P_J +MSW&@`-`/$!$`AF&Z6&`0&1B`)1$`A@]X`O`/C@#94R""(`\A@0`D>"\F!_#/ +M<9\`N/\0KAB!SR#B!]`@X0<8H1B!GK@8H1B!OK@8H0'8[0"/_^!XX<3\',B^ +M_!Q(ON'`X<'APN'#_!P(L?P<2+'\'(BQ_!S(L?P<"++\'$BR_!R(LOP`AR7[A`H>!_`:'@>.!_`(",(%R"`=C@ +M?\(@"P#QP)X/;_]*)$``SW6``#PB%24#$`"#0"4.%=%PPB0"`?`E#1'(%046 +M1"6^@0GR"B'`#^MRCMB-N'T#H`!TV\@0#0:E><@86`"@@P;91GG(%0`6)'C( +M'1@0`(/($``&AB!_CM`.00^E!T__X'CQP"X/3__/D='MB3#!`%``0E@0_` +M_P``02D$!L]Q@`"PQQ01!@`%+CX!`"&$?S\`__\$)$$!'AY8D""0C"&"A@'9 +MPB%.`"JCYZ,D@,]V@``(EL"Y*K;/=H``N"@HKD"N`HBDHP&N'O`$@SD(40!& +M"D`(`-@$HP*#)(B2Z2>#'.`V>"2(SW"``&@E!X@0<0'9P'G/<(``M"@@H`+8 +M`_`!V`.CQ09O_P'8X'CQP%8.3__/=8``Q`8$A:/!`-^HZ*X*0``!V`2E`H4$ +MB(#@2`(!`,]P@`"T*`"`@.`X`@(`SW"``.`@$(#/( +M!1Q",`>(BW$&'`(P#@]@#*@2``#/<(``X"`0@""`SW"``+@H(:"."Z``Y:4# +MV`2ERO`$A7,(T0!"A2>%0"(`!S9X!8@I"%X!SW&``.`@`Y(P@<]S@`"X*""! +M88,*N&)Y#0D$``G8"Z6+\`6%C>@$BH#@J/+/<(``+)9>#Z`-`H"`X*#R!84% +MZ`78"Z4!V`CPSW"``+0H`("`X)3T`-@&",`&D/`$A=T(40"J"D`#(H5'A4`A +M``=6>$6(.0H>`(.Z1:C/@*(<`/`H7K$2(SW"``+P&`)`! +MWB$*`0#/<(``O@9`D,]P@``(E@J0#0H!`,2E`-A(\`2)&.C/<(``M"@`@)+H +MSW"``"R6(X#/<(``J"@`@+8*X`4X8(;H<@Z@!@#8`=@P\,2E`=@L\`2%60A1 +M``*%SW*``#PB(X)D@&BA(X)E@!S@::$GA39X)(@#@@#>-+`"V`39J@OO_\ER +MSW.```B60H4'A4`B`0<6>0J3)(E$@G(+H`O)<\2E`]@#I0'8A0-/_PP5!!`0 +M%040"B'`#^MRSW```(D,&0=@`(HC#@'QP/8*3__/=8``Q`8$A:'!K^@!W\]P +M@`"T*."@`-@/I0"E`:4"WLEP!@Q@!.EQSW"``)`&`(`F@)X1``:FN)X9&`#) +M<`#9&@OO_P3:2@L@#\EPSW"``#PB(X!(@3214R(``.(*H`OI<\2EZ7!J\`2% +M.0B1``*%!(@6Z`F%E.C/VT#`BW#Z#B`,&+L:ANFEA"@("0`A +M@'^``,"3*X"AN2N@!M@$I0#8!O`$A0\(D`$!V!T";_^AP`;8`Z4`V-7QX'CQ +MP*8)3__/=8``Q`8$A:7!AN@"A02(F.@"V`2E!(6I"%$`!86+"!$`SW"``.`@ +M!(#/<8``J$8`@%X*H`T@@;3H`-@X\,]P@`#@(`2``-[%I<]Q@`"H*`"`K@^@ +M`""!SW&``*A&`=\$V@"ASW"@`"P@0!`'`,]P``!T?4#`!=A!P$+'0\9$QLEP +M!MG)```B6 +MZI?!B@D.P!,`W@;PQHK]"8&#`=[/<8``M"C`H97NSW&``+P&()$C"T$`SW&` +M`+X&()%ABA,+00#/<8``P`8@B4:*"PI```#9`O`!V<4)$``<$`0`SW"``"R6 +M#!@``<]P@`!4B`00!0#/<(``L,<%@`4H?@%`*8!RD'#*(LX'RB".#P``B`S* +M(XX/```!`_@#;@#*(EP +M'?``V,]Q@``T1@"I`MDCI17P!(4!WB$(40`%A9SHSW"``"R6(X#/<(``J"@` +M@%X.H`4X8`;H`=B-!R__I<#/<(``-$;`J`(.8`0%V`#8!*6E\078"Z4^#R`& +MR7``V<]P@``T1B"HZ/'@>/'`\@X/_\]V@`#$!@2&Z0@1``*&!(@3Z,]P@`"T +M*`"`C>C/<(``+);R"*`-`H`'Z+8)H`8`V`4#``#/<(``X"`0@$>&((#/<(`` +MN"@!@`)Y`H96>`>`#PD$``'8!*;=`@```(8+Z!<+7D`"V<]PH`#0&S.@P@Y@ +M#1[8SW:``.`@!(;/=8``Q`8`@%H/8`TFA8#@J`(!``2&SW&``*PH`("R#*`` +M(($&I0*%)X4& +M'.`V>`40A@``VD^F@0X>`,]S@`"X*,]P@``D.KB`)("Y8<]U@`#,F1<5#Y98 +MJUP0!``,$`4``"4%`1@5!);B>0(E!0$5%0^6)!`$``(DQ(,6%0V6!8"B>,HE +M@1`$\@'=N*L-Z4`LCP`-"<0#3R6`$`7P!>A/)4`0#WT8JT$IP``980D)10&" +MO;BK60Y>``"&#NC/<:``+"`F@0Z&(GC/<8``N"@%H4"F!?`!A@/H0:;2#L`# +MS@U`#2<(D`#K=<(-0`T,%@00N'#/<```C`P*(<`/J7(]`6``BB,3"\H-8`T` +MV`*&)X8<=90L)10,8BX*X&*L`W<]W@``LEJ6G&!0``4`E00`/"24`)Z:' +M%``&$0A>``'82@]@!@RF3/!.#"`&"X8+R`0@@`____\#"QH8,.X/X`>KI@+8 +M`Z8"AL]R@`"T*"2(CNDGAAS@-GC/<8``:"4GB02(,'`!V,!X`*(I\"""!>D! +MV`.F(_`GAC9X'!`$`,]P@`!4B`00!0#/<(``L,<%@`P?`!$%*'X!0"F`!X\<#/<(``M"@`@!OHSW"``.`M`("9Z+X- +M``R)Z`O(!2"`#P```#P+&A@PK@T`#(GH"\@%((`/````U`L:&#`+R)"X"QH8 +M,/8,@`71P.!^X'CQP$H+#_](=4"`88#!@0"!6@XO_\EQ`*6=`R__(:7@>$"` +M(8!.(@.``-H#(D(`8*#@?T&@X<4!V\]R@`#D!7ZRX'C@>.!XX'C@>.!XX'C@ +M>.!XX'C@>.!XX'C@>.!XX'C@>.!XX'C@>.!XX'C@>.!XX'C@>.!XX'C@>.!X +MX'C@>`:X12#-`,]PH`#L)Z:@"H``VP"Q?K+@?\'%X'CQP-X(``/>#,`.J@K` +M#H#9SW"@`-`;,*#1P.!^\<"""@__&G`!WP`0$@$3\%IU$?`5(,`CH)`"$!$! +M`>?7=0``^__P?W3V&0J`+P``___/<```^__="(&$H0(/_\]V@`#8(`"&`>`` +MIA4(40`!V<]PH`#('#&@R@W@#BAP!KV!O4`I`"2E>,]QH`#L)P:A`(9"($"` +M`*;=]<]QH`#('`#8$:'7\?'`SW"``(`O`("!X,HAP@_*(L('RB""#P``KQ/* +M(X(/``#S`"T`.1@H`#-'`X'[@>/'` +MH@D/_\]P@``\(@.`SW,/``#\*(#/<(``0)K`N39X1(`@@`JZ9'K)N25ZSW&G +M`!1(3:%%@`&`"KK)N&1Z17@.H<]Q@`!PO0Z)AB#_`5MHSW"```1^3*A/B3") +M0"`3`X8B_P%#NH8A_P%-J$.YV@G@!BZHVG#/<(``V"``@`'@SW&``-@@`*$5 +M"%$``=G/<*``R!PQH+X,X`XH<,]Q"`"'$,]PH`#L)R:@`]@`V3(C52`Z<8T- +M,R):<$HD`"`:\$`E@0$P>0:Y@;D0OR5_SW&@`.PGYJ%`)H$!,'D&N8&Y$+@E +M>,]QH`#L)P:A0"14(,]P@```+B"`8'D&V.\,!2#M#0ZEJG#^#N`$BG$:<*IP +M>@W@!(IQF'!`*$`A$'@0N(&XA[B,N,]QH`#L)P:A30P0("T,42"*)<0&BB:$ +M""+P"B'`#^MRSW```+`3BB/%#0HD0`6%!"``2B4```HAP`_K'KE>E!_ +M0Y$`(XT!L'T&O5QZ@;T0NJ5ZSW:@`.PG1J8CD<"Y>'DE>!!X;/%"(D`@@."^ +M!NW_0"%!(,]Q"`"&$,]PH`#L)R:@SW"``-@@`(#/<8``V"!"($"``*$'],]Q +MH`#('`#8$:')!\_^X'CQP`#8C;BJ#V`+!AH8,`S,AB#_B@CRSW"``!0X`(B` +MX``)0@31P.!^SW$#`$`-SW"@`*@@+:#/<8``Z`A`@0%J`*'/<*``."X%@`0@ +M@`_`````'0B`#\````!(V,]QGP"X_QJA6Z%IV!BX&:'/L]84&[_ZCP/'`X<4(=03P`@@`#6(((`VIB'!#KE%>25X(,$5"5$`!Q2!,"+"!KD(ND5Y +M)7C@?Z/`H\'AQ4+!"12!,$/"0<`9"3,!`-@1"5(`"A2!,`D)4@`'"1(!`=@' +M%((P!A2#,!$+@``BP3!SS")"@`/T`=@AQ2$-41`*%($P(\,9"<,`"Q2",%!Q +MS".J@(3V@.+*(&D`&PA1`(HAR0_/<(``+`<@H('E_]G*(2(`(:#!Q>!_H\#Q +MP`HAP`_K4"!"PB!`$*1$0K``$>)[PK> +M@8#8`_`&B<'%X'^BP.!^X'C@?N!X\<`(R)6X"!H8,`G(F[@)&A@P"\B*N(VX +MD+@+&A@PSW"``#PB`X`8B!D(40`+R,]Q``#X**RX"QH8,$X+(`8/V-'`X'[Q +MP.'%"'4^B,]P@``4&$"`0"4`%`.Y-7E983H)8`P*VIH/[_^I<)T$S_[@>/'` +M(@S/_DAUP8``@"ARS@@O_\EQ`*5Y!._^(:7AQ6"`H($!@"&!`B-#@V"B`R!` +M``&BX'_!Q>!X\<"EP4'`0L$,'``Q$!Q`,<]Q@`!\?#09P`\P&0`/+!G`#B@9 +M@`XD&4`.SW"``'Q\(!A`"\]P@`!\?!P8``O/<(``?'P8&,`*SW"``'Q\%!B` +M"L]P@`!\?!`8P`C/<(``?'P,&(`(SW"``'Q\"!A`",]Q@```?(`9``A\&<`' +M>!F`!W090`=P&0`';!D`!V@9@`9D&4`&8!D`!EP9P`58&8`%5!E`!5`9``5, +M&<`$2!F`!$090`1`&0`$[Z'.H:VAC*$L&<`"*!F``B090`(@&0`"'!G``1@9 +M@`$4&4`!$!D``6.A:B```]@9``!J(,`"U!D``&H@@`+0&0``:B!``<@9``!J +M(``!Q!D``&H@P`#`&0``:B"``+P9``!J($``N!D``&H@``"T&0``:B"``(*H`40%`M@V```C8`-DF#>`%F;GU!4`,\#!*%5(D`-`Z%`(@`'=G@%B!D($0C/<(``O`8`D$AT@"1$$P"L +M'ML#\!C;8J%5(D`->&`%H3T`X`LH<.!X\<"."<_^SW"``/06`X`0Z,]RGP"X +M_QVBSW&``,`@!($!X+.XM;BXN`2A%J+/<(``:`9`@,]V@`!L$Z"&!"*##P\` +M`.`$(X$/`0```!)I9'@'?:"FF'4$(HX/````0,]U@`!H$^"%`[YD?CUYQW_@ +MI00D#0`$(H(/````@`8C0`-%>0*YY'X$(X,/`@```,9X9'DF>"\H`0!.($$$ +M#1I8,`\)D`'/<(``B+$.D"?HSW"``!P'`(C/$CH@3P +M-PD``"APSW.@`!0$"J//.!_`-CQP.'%SW"``&@&`(`$((`/#P``X"\M`1#F#^__3B5`%`HE`(`- +M\@HAP`_K#HHCQ0H=!.__3B5$%'_8"KC/<:``T!L3H7_8$*%A`,_^ +MX'CQP.(/C_[/<(```"X@@`'>8'G)<,]Q@``X"&*!!]^`X,P@XH`-],]U@`"H +M$.&E`-J`X\HC8@!S>\*[8J4,\`#:@./*(V(`EI0>O_@#8@^@`V`3P +M_PA1@`'82-D/(0$`SW"``/P0X'\QL/'`$@^/_@AV[(@HEL]P@`"(!K)O*'.& +M(_,/MGU"*Q$"QW6``$B_8(4(<@D+7@-$:.NYBB##+P3T'A:0$`V.42``@*+R +M>0G?`"L+W@+_V`>M2B0`<0#9J""``RAB`"&##X``,,?V>P2K*&(!X2]Y`*M; +M\",)$B$*(<`/ZW+/<```+26*(PL$2B1``*T"[_\*)4`$[KD'C3(B0@0`(8$O +M@``PQ_9Y"/)$J039`"E!!"5X!ZT]\$"I#R!`!&/P+0@2)(P@PZ_*(<(/RB+" +M!\H@@@\``"XERB."#P``Y`+*)&(`5`+B_\HE`@3J">__R7`(E@L(G@,"C@FM +M`_`!C@BM`(4Q"-X"`-I'K4HD`''/<8``,,>H(,`".&+V>`08`@0`&`($`>)/ +M>@&."*T"C@FM+/!,(0"ARB'*#\H@B@\``"\ERB.*#P```0,^!^K_RB+*!PB6 +M`"&!+X``,,?NN`>-]GD)\@09`@0$V0`I000F>`>MW?$`&0($`-D/(4$$)G@' +MK0&."*WM!8_^\<"2#8_^SW.``)`'8(,`WL]UGP"X__V%>6'/\R["PL%`.T+'L#/<8``;`9@@<]QGP"X_WVA42,` +MP,H@(@`?]#D*40#/`"K>=[8Z40&EB``=AI!8_^X'C/<(``[)C@?P:`X'C/<(``V)C@ +M?N!^X'@H<@D`(```V>'%X<9`*0T")7U`+0,4I7LE"C0""'53)7Z0!O(!'5(0 +M8;K[\4$JC@#!ND(F3I`$'=`0_?4)ZB\DB7#@>*@@0`$!'5(0X'C!QN!_P<7Q +MP.'%SW6``*B1((V,(<./"?('Z,]P@`!(/E(.@`S_V`"MSW"``%"1`-DUH,]P +M@``H&2"@SW&``.`M`(&BN/X)8`D`H0#8"@KO_PAQN02/_O'`X<4`W<]P@`#, +M!Z"@SW"``.`MH*#/<(``2):I=)VP,+R>L%H-(`2I<*EPX@D@"*EQA02/_N!X +M\<`"#(_^SW"``/06`H`'$@\V#1(.-@$2$#80Z,]RGP"X_QVBSW&``,`@!($! +MX+.XM;BXN`2A%J(&V`T:&##/=:``%`0*I0F%)N@#V!"E!*7/<(``0,CN#B`- +M`QH8,)+9`\B0N:`80`!:""`$`-@)A0[H*!4$$"05!1`>V`HAP`_KO +M_XHC!`8'&M@S`1H8-,]P@`#`(,JE`(`-&I@S$0B>`,]QGP"X_P#8':&U`X_^ +M\<#AQ8+@H<$<`2X`"'6V#>__`-K/OQ +M<0E1``H,[_Z+<"#`SW&```2+4R`"`(8@?P](J1QX":GL\5$)E`//<(``/"(# +M@!B(00A0`,]R@``XB$APT@OO_@;90"(``LH+[_X&V0R2@;@,LL'Q'0D4`<]R +M@``XB$`B``6N"^_^!-D,DH"X#+*S\1/8`_`!^X'CQP.'%`-C/=8``;+!*)$!Q)(6H((`"`-L/(P,`"R'`@`/T`>`% +M\&9Y`@]@`"2E!(6`X-`.H0#*(&$"(0*/_N!X"',X8-6[U;D-">4`-K@"(T(` +M"O#/GIB%KC@?T5XX'CQP'8)K_Z8<@AUSW:``,B+]"9` +M$,]W@`!(BU$@0(+*($$`RB0B=,H@(@#H("("]"8"$`D*7@(!X$<(%00MN\"[ +MSW*``*B^M'I`*X4"8)($O88E^!.)O0\C0P!@L@#:%G]`IT&GP[FE>04A0P$4 +M?F"VSW&``.B+%7D`&0`!`O"`V%T!C_[@?N!X\<#AQ<]Q@`!8D$&)SW6``"@9 +MSW.``.`M((,'Z@'8`*6"N2"C"/``VD"EHKF`X""C:`X""0#8>@ZO_PAQA@E@ +M`@#8)0&/_N!X\<"J"*_^F'`#$@$V`)$A@4#@]+G`(*(``^`$((`/``#\_\]Q +MH`#4!P\1#88`(`4!$PTE$0#:#<@5(@,P#A,`!AUE&1$`A@(E0P-[",4`!=T, +MO<]PH`#('[Z@$-VNH`'=%1A8@\]VGP"X_[V&SW"``&P&H*!=IAD1`(8+",4` +M^PL>P,]P@`!L!D"`SW"?`+C_7:`S"QY`#<@5(@(P#A("!L]PGP"X_U:@=J`9 +M$0"&"B'`#^MR0]C/.(/ +MH`4"V0'9SW"@`-0'-*`SH`/?[:`1$`"&SW&@`-0'0`IH.``L$\$<=F)//<*``U`<-$`"&0"\!)!!X!2$5``/((8``$!(! +M0\&X$)@``!V,!X#+A" +MP`/P0L8#R,]QH`#4!UF`B!F``*00`0#9H+@8@@.Z&(0#M[FD&$```\`3")X% +MSW*@`$@(0"(`(P;P0"(`(<]RH`!,"`3!`L,#<&5Y!255("=HSW,``/S_9'G/ +M.QR`*(#$@,V`8,?"!X!4HMPB\]P@`"(P79X +M`(B&('\,''@$N$5X`O"`V.QR`*H#R#MV4(@S$(``!+I%>.QR`*H#R!IV7)#L +M<$"P`Q(#-IP3``%O@R:XP+C`NT`H`@,-N\]P@`#`065Z0*`-$@(V`"*`#X`` +ML+'`J,]P@``TL59X5'G`L0*0P!F$`Q4D@@!X&00`SW"``#PB!(`:D-`9A`-& +MP,]P@``PR`*`P**`X,HGCA,2`RX`RB&.(\EUR7``-[7"-Z'+O`` +MWOJXRB:"'P```0+YN,HF@A\```("_+C*)H(?```!`@GJSW.``!0\4(.*)@@2 +M`>)0H_H/``X2\`'9SW"``+Q!(*":"^`,*'#/<8``D#P-@8HF"!(!X`VA!2>/ +MDP_R;0(@``#>A!(``"D(E`R;"Q]`SW```)`3^@_`!,]RH`#4!P^"$'@9$@&& +M6.#="02`"O#/T?`@V)IP`W`0>'(9!```W@L( +M$2`#R'/P`\`1")X%SW&@`$@(0"(`(P?P0"(`(<]QH`!,"$?!`W!(P`3!`L`E +M>`4E%2`(P`?@SW&``##((X$$((`/``#\_P@@5@`,)L"D+@$M`$G`/@T```4G +M#Y"8]`'9SW"@`-0'%!A8@%4E010/&%B``PH?0@C`SW*@`-0'%:('PP(B`"4` +M&T`%#Z()PP(FP"`;H@/8$*(JP)S@`-F0]`<2#C8`P`0F@A_Q``#P4'"4]`/( +MZ7+(N@(CDR4(B`RX17@#$@(W$+I%>.QR`*(*P$`A63`!&A@P!,@#$@(V*'9! +MQ0,:&#`$&I@P(8``D`'%-+G`N31X`^!`Y00@@`\``/S_'64-$@$V!O`5(D`P +M#A``!@)]%2)`,`X0``;O#060`\S/<9\`N/\8H<]PH`#\1#V`!"&^CP`&``!= +M]!D($"`$R%"(4R+!`(8B_@-$NL08@@`PJ,]PH``4!,2@!\C/<:``2"P=H<]P +M@``PR`*`0"!0(!)P%@7-_POPSW*``)@[(X**(1(@`>$CH@/P.G>J#(`%4R%^ +MH`7T^@L```5_GP\0$(D/7A`#R"F(`>$IJ,]Q@`"8.P:!`>`&H4#P"B'`#^MR +M*!0%,#S8C+C/!@6"@+\!X`6B +MN/$&@H&_`>`&HK3Q$0\>$,]Q@`"8.P6!`>`%H3IW`\CI<7/<:#^A`#/<)\`N/\VH`,2`C:2$@`!!!(!-@\(GP*2 +M$0,!;PN>`JJXDAH$`)(1``&JN+8.8`B2&00`$-G/<*``T`\0&%B`)!`!AL]R +M@`!`S$62,'D"ND5Y#!A8@!39$!A8@,]Q@`!`S&>11I$8V1"[97H,&)B`$!A8 +M@,]Q@`!`S&F12)$0NV5Z#!B8@`7PSW"``$#,RJC/7/<(``,,@"@/,-!)#/<(``,)HDD)3AP"&& +M#P``DP#/<*``:"SP($``SW&``,!!(('/=:``U``VB`]@2I5H-P`H-#UX2 +MN@ZO_P'`!_`#V!,=&)`4'9B3,P@0(,]PH``L(#"`!<`P<`'=RB6&$P0@CT\@ +M````SW```#45>@O`!(#ES"`#,('D//<8``/"(C@<]P@`!PO1"($+@R +M(8$/``#8`I^X@.$!V,]QH`#\1`VA'0L0(,]PH`#T!V`8P`3/<8`` +MF#L#@0'@`Z'/<(``,)HDD)3AP"&&#P``DP#/<*``:"SP($``SW&``,!!(($` +MVL]VH`#4!R5XSW&@`-0+#:%,IMX(X`P&P!D6`);`X)X`#@`-S)L(7@`#W2`> +M6),!V!0>&)`$$@$V`!8$0`<:&#$`%@5``1I8,03*G.#*(L('RB""#P``W`[* +M(X(/``#T"C@"8O_*(<(/*'!>"*`-#MD/%@"6!!(!-K09!``3'EB3$(E3(,(` +MAB#^`T2XQ!D"`%"ISW`2(```4@S@`PT2`C8$R,]QH``L(+`0``$O@63@,'#* +M((4/$B@(`(3WSW``*`@`!AH8,`#>#<"Q)I+/<(``,,@"@!EA,'DFLJW8SW(` +MNP"[%@C@!@6X`\@:D"X/X`8-$@$V<04O_JO`X'CQP.'%3PA>0\]P@``PR`&` +MSW&@`,@?EB!!#QZA$-@.H0'8%1D8@(X((`Y!V"<(7D,!V<]P@`"\02"@U@R@ +M#`'8SW&``)`\#8$!X`VABB4($BWPSW&@`/Q$'8$Y@00A@H\````(`-T']`0@ +MOH\`!@``&/(`W?JXRB6"'P```0+YN,HE@A\```("">K/["-C*#B`%`=D"V<]P@`"(*"2@200O_JC` +M\<#:"R_^+R0'`,]S@``\(BAWA.@A@P/P((/$$0$&);GP(PT`P+F`YZ*CHZ/, +M(2&``=G*(2$`SW6``(0ER(TK#E$2P8/$%@X6'PY>$+*(2$``-[/=:``M`_ +M`,]P@`"4.0"`B^C/<```%@D*"X`$"PA1`#()0`H*\`#9GKG/<*``_$0AH.!X +MP:#@^#T`,`_!R#T`,SW6```02`(V&Z`X(P`L! +MV`"M70,/_O'`\@H/_L]U@`!LL"25$NG/=Z``+"!0AP#>!H5'I7(,+_X.((`` +M"*7$M1"'Q;4&I2D#+_X(A>!X\<#/<8``;+``$00`N'#/2$'@="2,`!K($B@T( +M40`%BH'@`=@#\@#8@.!X"`(`T<#@?L]P@`#DLB"01"$``V,($0(`V\]R@`!L +ML&6B!(*@N`2B/0F?`022SW&``,2P`>`D@1!X'PDE``2R!(H/"%$`!8J!X`'8 +M`O(`V(#@*``"`,]P@``L-@.`"N@#V`GPSW&``"PV`X$"Z&.A!-A9`0``X'[Q +MP,]S@`!LL`03A``-#%$`!8N!X`'8`_(`V(SH!1.%``HAP`_K*` +M//(*(<`/ZW**($P-BB,(!PD$+_^*)(,/!)7/"!``[@S/_\]P@`"PQP6`*(69 +M(,T*,'`!V,(@#@"S"!``SW"``+PUZ:#7<0``$"=O(`L`$>@$C0T(40`%C8'@ +M`=@#\@#8!>@*#L__0?#/<```B!,(I4X.S_\[\`25FN@EE0B%@>'`(($/``"( +M$P/R&WC/<8``L,-=P +M```0)PBE;R`+`!'H!(T-"%$`!8V!X`'8`_(`V`7HF@W/_P?PSW```(@3"*7> +M#<__!)4%M>2U$(9Y!^_]!J7QP,]Q@`!4B$&!SW&``+#')8$%*;X`,'#*($X` +M#"$`\,]Q```0)X((+_[*($4.SW&``,2P!*'1P.!^X'CQP.'%`-C/,&X$J'@?N!XX<4`VDHD`'3/=8``2(O/!*),!S`-FH($`"SW"``*B^-'A`L`'ASW"``%0'0:#/<(``.(A, +ML.!_P<7@>`7P0GG'<$````#/0(@@`]`````SW*``+#'98+O"T2`4R!"!3IB"PN$`#A@!_`" +M((`/0````&)X.&#@?O'`I@W/_<]P@`!PLPR(&0C?`0*XSW&``$B_%G@%82V] +MP+T#\/_=F@U`!`GHSW"``(0E"(B'X`+8`O(`V,]Q@`"XEG>)SW*``%"C(8() +M"T``((*$Z0'?`_``W\]V@``\(B"&Q!$!!E4)7@&H[2.&.(E)"1`!_@D`#,]Q +M@`"@.AGOSW*``)`'`H(!X`*B`-C/`0H7H.#_X`$#NC/<(``A"4(B(C@S"5AD`;TI@D@#`'8 +M#@M`!8PEPY]$\A'OSW&``"@9`($+Z`#8`*'/<8``X"T`@:*X6@J@"`"A^@V` +M"\]Q@`"PQP:!12!``0:ASW>```2+"X]1(,"`I`D"_@N/42"`@%0(`@2B",`# +MH@Q`!(#@M`HB`,H@(@8&[0"&Q!``!B$(7P'/<8``L+($B0KH`XF`X'0)H0K* +M(.$`C@H@`!78J03/_>'%SW&``"P9`(D!VV&I).C/<*``L!]YH,]P@`#@(`B` +MHX%@@`*!`-HQ#0$0SW"``$09`(B#Z`'8"O`!@0(C#0#W#86?3`!`2T&I2'`' +M"%$`8:%"J>!_P<6BH>_Q@.`!V,(@#`#/!X\<#AQ0#=SW"``,!][@TO_QS9&]BF""``!=E*)`!WSW&``$P9 +MJ"#``A8A0`,$$`4`F'4/#4$10"1-`!D#S_T*(<`/ZW)WV`6XK0;O_E/;X'CQ +MP,]P@`#`?1@0!0`O+$$!3"0`A\HBQ@?*((8/``#B#LHCA@\``*L`?`;F_LHA +MQ@_/<(``3!D6(``!`(!`>-'`X'[@>/'`X<7/<`,`0`W/=:``R!]%'1@0J@_/ +M_X#8%1T8D*$"S_W@>/'`F'"X<9S@RB+&!\H@A@\``.,.RB.&#P``8P`@!N;^ +MRB'&#TPE@('*(L8'RB"&#P``Y`[*(X8/``!D```&YO[*(<8/`-K/<(``P'V> +MNA4@0`$@@`$J`@%%>=H,X`8@H-'`X'Z=!^__!=G@>/'`L@GO_?_:SW"``,R9 +M$QB8@!P8F(``WL]Q@`#$!L.ASW"``+`H0*`!VL]P@`"T*$"@S*'0H=&ASZ'` +MH<&A`MW)=\]P@`#`DX0O"!D`($(.2X(G<``AD'^``,R31B+"`$N@,@O@"T`@ +M`"%AO208@B/3#760`><"V`#9I@EO_@3:;@G@!`'8D0'/_>!XX'[@>.!^X'C@ +M?N!XX'\!V.!^X'C@?N!XX'\`V.!^X'C@?N!XX'[@>.!^X'@`V<]PH`#`'2>@ +M)J`MH.!^SW&``!P[$H$!X!*A#\]Q@``\(J2!5B5. +M%%8E#Q68$($`%0I>`H8A_P-$N2]GB;_I<1GP42(`@KP5`A$,\L*Y@"4"&3]E +MZ(\]93"-97_P?T5Y"?##N3QY/V8^9C".Z(]%>8@8P`-E>84`[_V,&$``\<#A +MQ0/(I!`!`)@0`@!1(0"`$*#Z`"`-JL:$X. +M``S/B!<]P@``PR*&`!/""$0T! +M#6"1!/""$`,!SW&``#PB)(&`$`T!5!$!`3UENV.$$`T!NV.`$`T!N6%^$`T! +M0GTG\$,(D0`#$@TV#EQ#.!X\<#B#8_]SW&``#PB\"$"`%8B100(@E8B +M!`51(,"`BB`(`,H@(0"\&@0`2B0`<@#9J"!`#\]U@`",3_R*+F7D?B\H@0-. +M((,'SW"``'11;V``)4,`X*M$$H\`Y'XO+H$33B:/%^Y@R*O(@B$.WA`=BH;A +MTR"F`"\H`0!.((T'SW"``/1-J&`1\,]V@`"T3RYFSF6\BL1]6!*.`,1]+RU! +M$TXECA?(8!"K`>%*)`!R`-NH(,`/W(K/<8``4%%O8<]U@`!T4>1^+RB!`TX@ +MCP?O90`EP`#\J$02CP#D?B\N@1-.)H\7[F4D&((#R((?#MX0/8J`X],AH0`O +M*4$`3B&-!\]Q@`#T3:EA$/`$Z\EK`_!H=LYA/(K$>5@2C@#$>2\I00!.(8X' +MR64L&$(``>-*)`!Q`-BH(``%SW&``/!-?8H)80`D#``!X&1Y+RE!`$XA@P?/ +M<8``]$UI82"L[02/_>'%X<;/'&F'#/\5[>:)X@J1[!"%!@V5X&*+?]<'&X'_!Q>!X\/$`CI!"<`%$(@`(!@>`1_!R>/DT''%O2Q#A$0(@K@ +M!`K8I0@0``HAP`_K0S8!A0! +M,1CI'A0`,0L@0(`-\L]P@`#H+6"`SW$``#!.#=A@>P3:"/"(Z,]P@`#D+2"` +M8'D-V`LG@),%\H(([_\*V`?PA^Y2#Z__"MA6"```W*4(W(\"K_VCP/'`(@JO +M_0#:SW.``"P:.X.Z@P#>#R8.$*1Y!"9`$$(@`(#*(&(`+R8'\`'=RB"!``?R +M'(,D>/(.[__%>*EP40*/_>!X\<#AQ:'!`=A`P,]U@``L&@J%&P@>`(MP!-EG +MVCW;U@Y@"A>["H6@N`JE*0*O_:'`\<"F"8_]&G`H=4AW:'8X8V;9/=H6#V`* +M%[H7"%$`"G#R#F`*J7'I<,(.8`K)<=T!C_W@>/'`<@F/_:;!*'4:0#8A0&O_:;`X'CQP!8)K_T!V:+!SW6``"P:&H5; +MA01Z'(4$()"`+?(#\#MY!"!`H/[S+R@!`$X@D0=<'4`4%25-%!V%@.#*(<$/ +MRB+!!\H@@0\``(\3RB.!#P``'`+*)`$$X`2A_LHE0006",__'85`>+H/C_\` +MV`\@0`0&(!`@L@WO_PIP^0"O_:+`X'CQP+3!!@U@`(MPM,#1P.!^\<"TP38- +MH`"+<+3`T<#@?N!^X'@`V<]P@`#\+^!_.*#QP+3!H@C@`(MPM,#1P.!^X'[@ +M>/'`5@BO_0#9SW:``"P:%X;/=8``?(`/(0$`&88D>$(@`(#*(&(`H<$!WQ<( +M40#/<0$`@*T+V*X.[_]5)<(7-X8`V`\@0``XAB1X0B``@,H@8@``V2,(40`+ +MV&#``1Q",`(C/<8``+!JX&0``&X&1N!NASW`!`*`Q".C/<8``+!H>H1N!@;@; +MH<]P``"T4`KHSW&``"P:E!D``!N!B+@;H<]P``"X4`KHSW&``"P:F!D``!N! +MB;@;H<]P``#44`KHSW&``"P:G!D``!N!BK@;H<]P`0"4.@KHSW&``"P:V!D` +M`!N!F;@;H='`X'[QP.'%H<'/__BB,(``C8`-E^ +M#>__*'(`V&$';_VAP/'`V@YO_0C9SW*MWN^^N@]@`CIPH@\@`"IPD0C0`,]P +M@`#H?0.03B#/`5$/U1'/<(``'`\&#B`!]"#``P#>`-T$V!IP*G#I<[[YV#V`"J7.V#R``*G!-"-``0B!`(-\(=8`!Y0'FTPX4D0'GNP_4D2IP +MSW*MWN^^1@]@`A#9,@\@`"IP'0C0`,]QK=[OOC(/8`(J<`H/[_\J<(/@RB`B +M`)$&3_WQP#(.;_T#VJ;!&G`R"^`*@\$#P<]P@``X$Q04!S``WO`@10#/<(`` +M0!/P($8`SW6``-`'#MC$I4#`!-A!P,]PK=[OOD+`!,(*<(#;S@Y@`IASS@D@ +M``IP?0C0``/#SW"``%@30H7P(,$`P*4,%1`0P:4(Z<]W@`!@$_`GP!"&Z,"E +MP:4`V1GPA"H,`Y((8``O<`X@@0\````!(*4#P(0H#"/P)P$0>@A@`"]P#B"! +M#P````$AI02%&PA1``"%$7B,(`>-PO?`I3%YC"$'C$%*7X`(X8O4W"$6#`\`/"$4#`=G/<(``T``*BW'6#*_](<`(<4+89@P@`06Y#!0$,`#! +MR7`&P@HE@`^MWN^^R@Q@`@+#B@X@`,EP50C0``#!!<+/<(``#`\`W?`@0``$ +MP0JZ!"*"#P\``/S)N45Y.@L@`:ER1@\@#078(!0$,`#!R7`&P@HE@`^MWN^^ +M?@Q@`@?#.@[O_\EP@^#*($(#]0-O_:G`X'CQP%X+;_T"VJ?!FG!Z".`*@\'/ +M<(``6$L`@`?91<#/<```$=+>"B`!`-K/<```$M(`V=(*(`$`VL]P```3T@#9 +MP@H@`0#:SW```!32`-FV"B`!`-K/<````40'V:8*(`$`VL]PH`"T#W`0%P`! +MV<]PH`"T#SR@G@X@#078O-AJ"R`!`-G#V&(+(`$`V8H@1`A6"R`!`-F*(`0* +M3@L@`0#9)<6UV$(+(`&I<8H@A`8Z"R`!J7$#V$#`!-Y!QL]WK=[OOD+'BG`$ +MP0/"'MN8*<`3!`\+AVYAS2B4``$HF```>"V`"2B<``!H. +M[_^*<(/@H?((%1(0#!41$`/80,!!QD+'BG`$P0/"X=N8__BG"#X(?R(H6CA2\@QP7/NH+(``O<`3"`B4! +M(,]P@`!`$U5X(*``V!$!;_VGP/'`5@[@``#8SW````W2`-E6""`!`-K/<``` +M#-(`V4H((`$`VL]P```5TL]Q\P___#8((`$`VL]P```;T@#9*@@@`0#:SW`` +M``+2H-F:N1H((`$`V@G8C+@`V0X((`$`VA38C+C_V0(((`$`V@#8C+C_V?8/ +MX```VA'8C+C_V>H/X```V@+8CK@`V=X/X```V@'8CKC/<0``___.#^```-K/ +M<```"](`V;X/X```VL]P```-T@'9L@_@``#:SW```!+2`-FB#^```-K/<``` +M$](`V98/X```VL]P```4T@#9A@_@``#:`-C1P.!^\<#Z#P_]H\&+<0'=\@R@ +M"JERSW"```1*`(!!P`380@@@`2S9#M@Z""`!`-DAQK78+@@@`#,(**`S"#B@,P@HH',(.*!S"`B@LP@HH+,(.*"`_2"N2]Y +MA.#,(&*!S""B@0.YM7G'<8``0)H`H0`3`"`$H0`0`"`;>`BA`!$`(!MX#*&J<*EQ +MR7(*)(`/K=[OOGH.(`**"J`*!-H`P`'!!+@U>,]Q +M@`#,#A!AI@W@``+!`,`!P02X-7C/<8``[`X089(-X``#P0#8I,#1P.!^\<"A +MP?H/8`*+<@#`H<#1P.!^X'CQP.X,#_VNP;IPFG%Z[[Z.#"`"`<'*"R``R7"#X,H@0@,)!"_]HL#QP)8++_T(V<]RK=[OOFH, +M(`((=G8((`#)<&L(T```V<]U@`#$"""ESW*MWN^^2@P@`LEP.@D@`,EP2PC0 +M`""%0"%!@""E\O,HE!X\<#AQ:'!BW$*"*`*`=K/ +M=8``>($`%`0PSW"``/P/J7$7VAH*X```VP`4!##/<(``]`]5)<$5`]H""N`` +M`MO/<(``+!!6)4$3"]H&"N```,,`V#$#+_VAP/'`L@HO_0#9SW*``'B!SW6` +M`"P:%X5(=P\A`0`9A21X0B``@,H@8@"AP0'>%0A1`,]Q`0"`K1#8!@FO_X`B +M`@`WA0#8#R!``#B%)'A"(`"`RB!B``#9)0A1`!#88,`!'$(P`AR",P,<@C.+ +M<`395B<"%)H(K_\H[[X!IMX*(`*J[[[&"B`"JG"B"2``JG#-"-``JG`/V<]SK=[OOJX*(`("VN(- +M[_^J<`#?2B3`)ZT(T``(%A`0#!81$-IW`_"I=^EU._":=3GP`"3-([U]L'VJ +M<*EQSW.MWN^^;@H@`@K:I@WO_ZIP=0C0``@6$A#/<(```"X@@`P6$Q!@>0'8 +M@>`*<"IQ2G)J<\HD@0\``,T:RB6!#P``XQ?*)((/``!-&/'`!@DO_0S:J<'/=D`?_P#/=3P`/#S/<8``$$JJ"2_]BW#/ +M<(``_`_/<8``0`\7VMX(X```V\]P```+T@P".```-F*(,4/5@C@``#9`-A=`"_]H<#QP.'%H<&+<=X,8`H! +MV@#!SW````/2/7E*#Z```-K2V`BX$]D^#Z```-K/=8``S`C/<```(-*I<7H- +MH``$VL]P```ATB1M;@V@``3:`-@)`"_]H<#QP'8/S_R:<4AWSW"``#@(9!`2 +M`,]P@``X"%P0$``,V0#=*'!Z<,]P@``D"?`@40-*F*U>L=R@`!L@M2J%:H0\!T/41!4:51Z0"R#(71[ +M>F*U>L=R@`!L@M:J%ZHY"9$`C>]`+($A-'F`(0($M7G'<8``;(+4J16I#O`= +M#U$00"R!(31Y@"$"!+5YQW&``&R"UJD7J4(C0"!!"'6``>7A!N_\`-CQP'(. +MS_REP;IP`-C/=H``.`AT%A@01,``V`.FSW"````N((!`(-DP"B*`+P``"-(! +MW6!YJ7`Z<`+8A!X`$$PA@*#,(>*@!/2$'D`3`X:$%@$0,'#J`@8`@.#*(H$O +M```(TH'@RB*!+P``"=(`W4PA@*#,(2*@S"'BH$+T!MU`\!"&"*81A@FF`X:' +MZ`;8T@R@`%8FP1(#A@\(40`(V,(,H`!6)L$2%H;#N`T(=`,6I@O8%J;/<8`` +M)`GP(0``*89(A@QY9!Y`'@QZ!(9H'D`>@^@%A@GH!H:#Z`>&!>B`XLPA(8`& +M]`#8%Z88IAFF&J:J<*EQ'@[O_T.&`>6'Y3("!@#/=X``_$&U?P"'`=J.X,(B +MC@!"IH#B(]G*(0(&&G&`XB;9RB%"!LH,H`!Z<<]P@`!$$,]Q@`"<$`/::@V@ +M``+;`X:!X,H@H0#*(6$$T`RA`,HBH0!,(<"@RB"A`,HA80*\#*$`RB(!`,]Q +MK=[OO@X.X`&J<$8-+_ZJ<(/@XO+/<(``3!#/<8``J!`5VAH-H```V](-+_X# +MAL]P@`!X$,]Q@`#\$#(-H``2VL]QK=[OOLX-X`&J<#H(H`"J<(/@P/(`ASZ& +M`*9`*0`"0"D"!`5ZSW````O217E&#*```-J*)[\=0,=!QPK80L#/<*W>[[Y# +MP*IP(X8*"R``JG"#X)3R$(8^A@2F +M$897:06F0"G``P5Z/7G/<```"])%>>H+H```VD#'0<<*V$+`SW"MWN^^0\"J +M<".&:G)*F$WA3>C1Z6*:*(0\*[@X@`NER&(80%!0PBB$/"A-XV@X@ +M`NER%X80%!38+H```V@`<@#4$',`T"MA"P,]PK=[O +MOD/`JG`CA@IR2G-`)(0B0">%(@HF``%F#.`!"B?`!$X*(`"J<(/@6`7"_PKP +M`X:$%@$0`>`P<"`%Y?\#I@#8<0/O_*7`X'CQP%(+[_P(V<]RK=[OOBH,X`$( +M=SH)(`#I<'D(T```WJ8(H`#)<,]U@``X"-NE(-@>I=^ER7`$N$4@P``EP;@SO_^EP00C0`!N%`>#?"/2`&Z7/<:W>[[[2"^`!Z7`Z +M"2``Z7`A"-``Z7#/[[ZZ"^`!$-G*""``Z7"#X,H@(@`I`\_\\<"Z"N_\ +M!-JDP1IPN@\@"HMQ`L`#PP#=J7$(VDHD0`)V"R_]2B5`!`AQ`<`6"J``J7(* +M<,]RK=[OOFH+X`$`P1H*(``*<&T(T`#/=H``.`C/<```(-)6)D$2.@B@``3: +MSW```"'252;!%"H(H``$VC*&\X9!*<`%P+@8N!-X)7A!+\$5P+D8N3-Y)7\2 +MIL]Q``!H'_.FY@OO_`BX%*;/<0``:!_6"^_\0"\`$A6FJ7!M`N_\I,#@>/'` +MX<6AP8MQ!@\@"@':SW6``/R!`!0$,,]P@`!,$*EQ%=H6":```-L`%`0PSW"` +M`$00525!%0/:_@B@``+;SW"``'@05B4!$Q+:`@F@``##`-@M`N_\H<#QP*X) +M[_P`V<]U@``L&A>%SW:``/R!#R$!`!F%)'A"(`"`RB!B`*'!`=\7"%$`SW$! +M`("M"=@&"&__529"&#>%`-@/($``.(4D>$(@`(#*(&(``-DE"%$`"=A@P`$< +M0C`"',(S`QS",XMP!-E5)D(8E@\O_XHC%0``V)T![_RAP/'`,@GO_`K:JL$( +M=BH.(`J+<0;8@@F@``'!"-AZ":```<$0%`0PR7``P0+""B6`#ZW>[[[>">`! +M`\,Z#N__R7"#"-``SW6``#@(%(48%`0P"J45A0#!"B6`#ZW>[[X"P@VER7"N +M">`!!<,*#N__R7!3"-``%(4@%`0P"Z45A0#!"B6`#ZW>[[X"P@ZER7"&">`! +M!\/>#>__R7`K"-``=(54%080R7`T%000.!4%$&RE)!0',"J%/!V`$;(/[_Q+ +MA0#8W0#O_*K`\<"AP8MQ:@T@"@':TM@(N`'9V@]@``#:`-BAP-'`X'[QP$8( +MS_RIP4#`0<$`V$C`@L5J#^`,J7"$QF(/X`S)<(;'6@_@#.EP`,"+/'`P@^O_`C9&G#/ +M=H``Y`<"V`>F"M@)IL]RK=[OOHX(X`$*<-X,(``*<(/@>_(`WPH-8`#I<,]P +M@```+B"`X*9@>0'8"'6"X,PE(I#,)>*0`_0&V`"F`(:;"-4!SW&``'Q&\"$` +M``'9CN`"IL(A3@"N#F``(Z;AI@+8$::"Y*0!/0!V!&F`-@%\`&& +M`>`!IC&&40A%`,]QK=[OO@H(X`$*<)H-(``*<'<(T`#/<:W>[[[R#Z`!"G`V +M#B``"G!?"-``ZJ;_V`NF"G#/[[[6#Z`!((:6#B``"G"Q"-&`'_``A@'@ +M<0CT@0"FSW&MWN^^L@^@`0IP=@XO_PIP'PC0``IPSW*MWN^^F@^@`1#9[@L@ +M``IP@^#*("(`!0>/_.!X\<"6#H_\&G#/<(```"X@@,]V@`#D!V!Y`=@Z<`*& +M`=V.X,(E3A,!AE(E#1"`X`;8RB`B`M(.8`!`)0$4"G#/[[X^#Z`!0"4! +M%.8+(``*<(/@$@(!`,]P```'TL]Q`P#PP+X-8```VL]P```&T@#9L@U@``#: +M(88*<`3:"B2`#ZW>[[[^#J`!_]OR"&``"G"#X.GRSW```"#252;!%-(+8``$ +MVL]P```ATE8F@1+""V``!-H3AJ(-[_\TA@AWSW````?2SW$$``XY5@U@``#: +MSW````;2`-E*#6```-HAA@IP!-H*)(`/K=[OOI8.H`'_VXH(8``*<(/@M?+/ +M<```(-)5)L$4:@M@``3:SW```"'25B:!$EH+8``$VA.&.@WO_S2&`B#0`XP@ +M!*X!W\HGI1`A"!,@`H:.X`'8PB`.``CH`8:`X`'?RB6A$03R`-VI=V<(4B!C +M"(,O``!\DL]P``!0P_8.K_P*<8#@RB!L`,CVC"`"B,H@A@\``)\`SW&``!Q" +M\"$``!5XS@ZO_(HA#PH=94/8(PWT$@6F`8:+Z`*&CN`!V,(@#@"!X`C=RB>A +M$`/R"-T!WW4.`W0``"3TSW$``%##D@ZO_`IP@.#*(&P`Q_:,(`*(RB"&#P`` +MGP#/<8``'$+P(0``%7AN#J_\BB$/"B&&FNDBAH[A`=G"(4X`*0E1``T(U`!. +M((T#`-\.\$X@S0(`W\]P```+TL]Q(``@(/X+8`#I<@&&!+_]99#H!MC2#&`` +MJ7$"V`K9X@M@``AR'PG1(`+8"=D(\`C8M@Q@`*EQ`M@1V<8+8``"VJ2F`-B1 +M!(_\X'CQP"H,C_RAP3IP`MZ*(`$ISW6``.0'$-@0\.R%`-A`P`IQ^&<5>"8/ +MX`&++<`#``>8:=T`F#Q17#C43(853"-0#@.$&V,H@(@).#&``Z7'D +MI2IPSW*MWN^^N@R@`>EQ8@D@`"IP0PC0`"J%2X4J<`';"B6`#ZW>[[Z:#*`! +MBB3##RX((``J<(\(T8`-\$`FCQ.`X0;8RB`B`OX+8`!`)H$3Y*4`V.$#K_RA +MP/'`>@NO_`3:I,$:<'X((`J+<0#!SW:``.0'88;/<(``;$8$%!$P`-WP(,(` +MSW"``'1&\"#/`,]P```&TEAYR@I@`*ERSW````?2`"G!([H*8`"I<@IPSW*M +MWN^^"@R@`22&M@@@``IP3PC0`"&&`L(*<`HD@`^MWN^^[@N@`0/#X@T@``IP +M,PC0`,]P```@TE4FP13""&``!-K/<```(=)6)H$2L@A@``3:$X:2"N__-(8, +MIJEP)0.O_*3`\<#AQ:'!BW'&#^`)`=K/=8``?(``%`0PSW"``+@1J7$3VM8) +M8```VP`4!##/<(``L!%5)<$4`]J^"6```MO/<(``X!%6)<$2$MK""6```,,` +MV.T"K_RAP/'`8@JO_`':"';/<(``#$H`@*+!0,"!P5X/X`G)<`'"SW&``.0' +MBW/)<,6Z0<*J#:`%(($@P((-H`D'V1IP`12`,'8-H`D'V0AV"G``V0C:R7-* +M)$`"\@KO_$HE0`1:<`(4@#!2#:`)!]D(=0,4@#!&#:`)!]D(=ZEP`-D(VNES +M2B1``L(*[_Q*)4`$.G#/<```"-)*<5X)8```VD'8";@*<5()8``!VL]P```! +M@LEQ0@E@``':SW````G2*G$V"6```-K/<````H*I<28)8``!VL]P```#@NEQ +M&@E@``':`-C=`:_\HL#QP.'%`-@(<0()8``"V@'8`-GZ"&```MH"V`K9[@A@ +M``+:SW"````N((!@>0'8"'6#X,H@H0#*(6$"T`AA`,HBH0#//'`U@BO_`O:SW"``(`1SW&``)@1_@A@`*'!SW6``.0'084% +MV$C9&@E@``\A@0`#A<]V@`!,1HMW%28`$`"07@\@`.EQ`X4`P14F`!``D/(( +M8`#&N0.%SW:``%Q&%28`$`"0.@\@`.EQ`X4`P14F`!``D,X(8`#&N0#8Q0"O +M_*'`\`)R7+/=8``Y`<`A0*X%'@`((\/@`#X@`#` +MR*4`I<]P@```+B"`8'G)<`AV:PC1`,]P```+TD#9D@\@``#:!MAN"&``"'$* +MV&8(8``"V3/8`-EZ#R```MHTV`#9;@\@``+:-]@`V68/(``"VCC8`-E:#R`` +M`MH[V`#94@\@``+:/-@`V48/(``"V@+8"=D^#R```MH,\,]QK=[OOHX(H`$* +M<`H)[_\*<-D(T``"A<]QK=[OOH[@`=C"(`X`@>`*%FPAE``BESW&MWN^^1@B@`0IPG@@@``IPY0C1@$;P00[1 +M$"X(@`&*""``"G!]"-``SW&MWN^^&@B@`0IP0@D@``IP90C0`,]QK=[OO@8( +MH`$*<"H)(``*<$,(T0`F\/(/0`$:"2``"G!!"-``SW&MWN^^W@]@`0IPQ@KO +M_PIP*0C0`,]QK=[OOLH/8`$*<"(((``*66%+A8(/8`&8 +M=A8+[__I<)L(T``,A2J%`MM)A0VEZ7`*)8`/K=[OOEEA2X5:#V`!F';N"N__ +MZ7!S"-``#(4JA0';284.I>EP"B6`#ZW>[[Y">4N%,@]@`9AVQ@KO_^EP2PC0 +M``R%*H4"VTF%#Z7I<`HE@`^MWN^^0GE+A0H/8`&8=IX*[__I<",(T``P%040 +M/!4$$`J%*85`'4`1386J":`%;H4*I +M[[YIA2J%#:7)<'IB!-N.#F`!BB3##R(*[__)<'4(T``,A4N%"B6`#ZW>[[YI +MA2J%#J7)<&)Z`]MB#F`!BB3##_8)[__)<$T(T``,A4N%"B6`#ZW>[[YIA2J% +M#Z7)<&)Z!-LZ#F`!BB3##\X)[__)<"$(T``P%040/!4$$`N%*85`'4`1387: +M"*`%;H4+I0#8E05/_/'`H\&+<28*X`D#V@#!SW```!O2C^D!V8X,(```VL]P +M```$^#"```-H`V*/`T<#@?O'` +MJ@QO_`78H<'/=8``.`A#A4C9_@P@``\A@0`"A<]V@`!41HMW%28`$`"00@L@ +M`.EQ`H4`P14F`!``D-8,(`#&N0*%SW:``&1&%28`$`"0'@L@`.EQ`H4`P14F +M`!``D+(,(`#&N0#8J01O_*'`X'BAP>'%X<:X<,]P@`!0HQ`0!@#/<(``@"\% +M@)AQH<&&)/E,AQP`D?E1Z0"Z-`;1]NF(5>L]Q@`!` +MG$AAU'X(SIB08IE>$ASAB/]#WM[W645)WNY82.)97XHV5Y)PP0`,]UJ@#@!W.%$0L>`$BE":4JI,]RIP`42`.B";DE?L2BSW&``*A$`!F``<]P@`"P1``8 +M0`'/<(``K$0`&``!H<#!QL'%X'^AP/'`SW$`@@$`SW"@`*PO/*#/<(``E#D` +M@(OHSW"``"P<`(`/")``A@G``M'`X'XR"4``#@B@!&_8A^B&#B`,"M@>"4`` +M\_'S\<]R@`"4.2""!GG@?R"BX'C/(*8)H`;(V%`@D"!,(("@&?(( +M]B,($"!%"%$@%=@3N`WP)0@0)#4($2CV"V`#*G``I0_P*=@2N/`@0`0`I0GP +M*]@2N/OQSW"@`.PG&8``I2(-(`1*2'7T)X`3%2&!(UH/[_\*2'7T)X`3'@@@`(MQ`,`4 +M((PC8;T`M.T-=9`!YJ4!;_RAP/'`.@E/_*'!&G#/=H``V"``A@'@*'4`IA4( +M40`!V<]PH`#('#&@K@P@#"AP/@T@!`?8"'?2".`"L]@6Z(MQ\@TO_0IP`!0` +M,0"E`(9"($"``*8']`#9SW"@`,@<,:`Z#"`$Z7`Y`6_\H<#@>/'`#0S>`"X/ +MS_\$\-((``#1P.!^\<`-"]X`2@_/_P3P[@@``-'`X'[QP*X(;_P(<8[@`=C" +M(`T``-K/=J``M`^\AERF`=K6#"`$2'.O?;RF\0!/_/'`<@A/_#IP*'4:8E`$_\X'CQP,(/#_P(=QIQ +M'PIT``#>2'7T)X`3&@@@`/0@@2-AO?,-=9`!Y@$`3_S@>/'`D@\/_!IPSW:` +M`-@@`(8!X"AU`*87"%$``=G/<*``R!PQH`H+(`PH<)X+(`0'V#IP,@^@`I/8 +M&.BP?4`HCR&!OQ"]I7_/<*``["?FH`"&0B!`@`#9`*8%],]PH`#('#&@D@H@ +M!"IPB0/'`T@XO_(HB!`[/<(``D`8`@,]V@`!8D":`0"8`%`8+ +MH`D$X0&&SW6``#PB(H;('1@0SW*``&@ER1U8$"&6)ZH@C@0@@`\`!@``@.`! +MV,!X(:H&J@#>B@P@",EPSW"``-T@N@QO_L"H<@Z``@CH=@Z``H;H;@VO_H.X`L"V".%2($TD5,B``"&#F`(`=L`V9ZYSW"``#PI(*"9 +M!@_\X'CQP.'%"P@R#`AU'0V2'@HAP`_K%<]V@`"(P;9^P8X#\`#>QW"``(C!MG@$B`@C`P`((X,#`"-``4D@ +MPP,6;75XSW.```C#`V//<(``B,*V>,]U@``\(J2%N(4!@*5X!""`#P````@& +M>P+P8X'HNY@9P```W0GRI!$```#=E[V1N)2XI!D``#L,'@#/<(``/"+$@,"Z +MR(8$)HX?`$```#Z^'N;8>D5[F!G``!T+G@>D$0``A24!%(RXD;BD&0``G!E` +M`Q[P)PO>!Z01`@"%)0$4EKV8O8VZD;JD&8``G!E``R2`$(&>N!"A"O"4O9:] +MG!E``R2`$(&>N)^X$*%%!0_\\<#6#"_\`]C/=H``^"T@AD!Y@.!M\B"&8'D$ +MV-,($``@AF!Y`-AGN!4(%0,S)@!P@``T2$`G`7(4>0!Y`-A"\,]P@```+B"` +M8'D!V(#@`=C`>#CPSW6````N((5@>0'8(PA0`""%8'D!V!L(T``@A6!Y`=@/ +M")``((5@>0'8P0A1@`'8'O#/<(```"X@@&!Y`=B%X`'8P'@4\,]P@```+B"` +M8'D!V('@`=C`>`KPSW"````N((!@>0'8@^`!V,!X+PA0`""&ZW5@>0#8&G#/ +M<(```"X@@&!Y`=BX<#?8"B'`#ZERE-L1`&_]"B0`!$T$#_S@>,]P@`"4EB`0 +M@`#/<8``Q`87"%$``-K/<*``M`]!^SW"``)26(!"` +M`,]Q@`#$!@L(40`"V`2A`_`!V`6AX'[/<(``K)$@$(``SW&``,0&"PA1``38 +M!*$#\`'8!:'@?@W(QW"``*2Q-(@!X2]Y-*@="3(!`Q("-L]P`P"$`*`:``"* +M(`@`!AH8,`OPBB`0``8:&##/<`(!A`"@&@``X'[/F]Y"+G_V`BX9'@HN`5Y17D(W?0D@`,G>$3`_@I@"A`4`#$2%`(Q +M8;U`*`$$!7E'>43!$!0",10D@#-`L-<-=9`!YE,EP@5`IP`4#0$'V0?P$'T4 +M)TP0`+1AN10D0#"[>T^]`)"E>W![Z0FU@'A@!""`#P```/\0N`5Z0*?A`B_\ +MI<#@>/'`-@@``-H(``#:"```T<#@?N!XSW&``+@H0"$``U4AP@41"(4``-D$ +M&%``^PB$@.!^X'CQP(8.8`4`V"H++_T`V,]P@``80?X+#_W/<(``^$#V"P_] +MP@L/_L(+0`<`V(H(H`*`V2X*``K2#P`"4@Q`"D(.0`&"#4`"`-C:"&_^"'%B +M#@`)\@U``M8(8`'_V&X-``&*((4/"'&"#*`$"'+1P.!^\<#2"2_\BB#_#\]U +MH``X+L>%!Z7/<*``5"X+@-.X!B8`<`\`___B"F`+%MER"X`!QZ4-`@_\X'C@ +M?N!X\<#AQ0#=SW"``%@'H*#/<(``.(BLL"H+8`JI<`X*#_WN"N`)J7`""D`# +M9@U/_?(,``&*(`8*"'$"#*`$"'+N#6_\J7#_\P0$/_`#9SW"@`.PG*Z#@ +M?O'`SW"``-Q]?@M@"P/9#@K``-'`X'[@>/'`X<6DP8MP9@M@"P39SW6``-@@ +M`(4!X`"E%PA1``'9SW"@`,@<,:".#.`+*'``A4(@0(``I0?T`-G/<*``R!PQ +MH,()P`!5`2_\I,#QP*'!BW`>"V`+`=FJ"<``H<#1P.!^X'CQP*'!BW#*"F`+ +M!-D`P%$@0(!H"J(%RB"B``#`42"`@*P-@@D`P%$@P(`X#,(%`,!1(`"!(`G" +M!4((H`H!V,]Q@*[@`>QP(*`!R.QQ`*'/`0<2!38*(<`/ZW**(,4`G0,O_2?;$@Q@ +M`4#85@C``&((0`:AP-'`X'[@>/'`:@_/^\]U@`"8(`*%(X4!WA!QP'ZI<)H) +M8`L#V2H(P``$[@*%`_``A:T'[_L#I>!^X'CQP.'%SW6``+`@J7`V"6`+$-D` +M%000(0Q0`$$,T``I#!`!"B'`#^MRC]B-N)C;(0,O_;AS`84,N`0@@`\!``#P +M`:4,\"&%SW"``%@Y(*`CA<]P@``B&2"H`\S7<````$`!V,(@"@`7N,=P``X` +M`(.XG;B?N.QQ`*$!$@$V['`@H-8/H``!V"4'S_O@>/'`X<4`V<]R@`#`(""B +M(:(BHL]PT/X```2B`!8-0*"B`!8#0&&B`!8`0``6`$`E#=X7_[M`V,\@X@?* +M(($/``#0`,\@X0?/<9\`N/\=H0;PSW"?`+C_/:`#S-=P````0`'8PB`*`!>X +MQW``#@``@[B=N)^X['$`H0$2`3;L<""@2@^@``'80@X``I4&S_O@>/'``!8" +M0*'!0,(!%(`P#P@>`,]Q@`#`AP3PSW&``,"70*%@B0':"/``%@!`%2&,``"D +M`>)]>/4(A8`7"QX``!8`00/P`-@5(8P``*0!XOD*5($#S-=P````0`'8PB`* +M`!>XQW``#@``@[B=N)^X['(`H@$2`C;L<$"@\@Z@``*)H<#1P.!^X'CQP.'% +MSW6``-P(J7"2#R`+"-D`A<]QH`"X'@*A`84#H4X.@`#A!<_[10:``/'`I,&+ +M<&X/(`L0V0/,UW````!``=C"(`H`%[C'<``.``"#N)VXG[CL<0"A`1(!-NQP +M(*``P%$@`(`#P`;T`L$Z#.```-H%\*()(`(!P3H.@`"DP-'`X'X)````!0`` +M`/'`X@V``*T`@`G@>/'`X<6TP8MUJ7`Z#R`+%-D`P(;@S"#B@0;T0@J@`JEP +M"'$D\`\(D0#>"J`"J7`(<1SP$0A1`%X,H`*I<`AQ%O"#X,P@(H(']'X)H`*I +M<`AQ#/`1"!$!>@J@`JEP"'$&\#T(40**(40``\S7<````$`!V,(@"@`7N,=P +M``X``(.XG;B?N.QR`*(!$@(V['!`H(H-H``H<-D$[_NTP`HAP`_K`A`7\,=W@`#` +MDPN'@;@+I\]P@`#$!B^``=H$Z42@!-@'\`#9+*!)H"2@!=B6"\`"U0/O^Z'` +M\<#AQ<]P@``\*0"`!""^CP#````(],]P@`"HD0"(C"##CP7RO@XO_0'8SW6` +M`%B0J7"*#2`+4MFR#D`&$@R@`*.%H@O@`*EP"''/<(``2#YJ#<`)_MG/<(`` +MJ)&)`^_[(*C@>/'`SW"``$B63@T@"PW9W@N``!8)0`71P.!^X'CQP-H*[_L" +MV:+!,@T@"XMP`Q22,"$*DB`$%(4P"B'`#^MRSW```(0,BB.%"=T&[_P*)(`$ +M`A2`,,]V@`#$!H0J""DO=R`>`A#/<(``Y)/Y8"R)0"`0`P`4%#$`(-,#$>DB +M#R`!0B2`(0'8$;;_V"$>`A!`)@`84@J@``396/``V!&V(1Z"%,]U@`#0D4`E +M$1+]98MPJ7%B#R`)`MI`)0`27@P@"T(D@2$`)X`?@`#0D0@0!0#/<(``L,<% +M@%,E0040<_\ +MBB/&!>!X`!8`0($"@`#QP.'%SW6``+"SJ7#>"R`+`]D!A<]QH`"`)0RA`H4- +MH0"-42``@`#8CK@$\@^A`_`0H4X*@`#A`<_[X'C@?N!XX'[@>.!^X'C@?N!X +MX'[@>/'`2@GO^P39H\$`WD+&.@P@"XMP`\S7<````$`!V,(@"@`7N``@@0\` +M#@``!A0`,1MX$^`$((`/``#\_R5XG;B?N.QQ`*$!$@$V['`@H`#!['`@H`04 +M`3'L<""P!A0!,>QP(+`&%`0Q&PP>``$2!38*(<`/ZW+/<```3R;M!._\:=MF +M#"`#`=@"P0#%)7A"P,]PH``L($`0$`#`O0'E`_`!Y@84`#&!#@,0!!0`,8+' +M+0V1$!MX$'CI<38-(`.I`'@$'@>#2`#J7+L<0"I"/#I +M<1(-(`.I>"2`+!]DN"(``T<#@?N!X\<"EP8MPB@D@"P79 +M`,`M"!X`SW"``#PB`X`8B!T(40``V)JXSW&@`,@?#Z$!P*09``##V!JX#J'J +M#T``I<#1P.!^X'C)`R`%`-C@>/'`SW"``&`Y_@@@"RC9R@]``-'`X'[@>/'` +MX<4`%@!`SW6``"P<`*4]")$``-G/<)\`N/\]H!S9%?#/<*``R#LV@$0A`@C:`AB'_"$5YSW*@`*@@38+DXH_W[>ER#T``((5="54!,R9!<(`` +M8$A`)X!R-'@`>#@0!`!8$`4`"B'`#^MRSW```)DA@0+O_"_;]@C@`E38*0A> +M`,]Q@`"4.0"!@;A2#F`+`*$*\(8,[_T!V$(*P`($\'X*P`.I!H_[\<`.#(`' +M"@]``-'`X'[@>/'`R@D@"`#8SW"``#PBR!`!!L"Y@>$!V`0HAP`_K_\N',6#H_\]@Y@"0#8 +M/@Z``E(.0`#E!8_[X'CQP%X*(`@`V-((S_S/<8``;*,"B:X)8`L@B='`X'[@ +M>/'`HL&+<%(/X`H(V0#`SW&``-0Y`*$(Z`84`#$#L004`#$"L0(.0`"BP-'` +MX'[@>/'`H<&!V&#``\P"'`0P`,#^""_\`MFAP-'`X'[QP*'!@-A@P`/,`AP$ +M,,]PH`#4`QR0>@D``0#`U@@O_`+9>@R@"`+8H<#1P.!^X'CQP`HAP`_K*)(,/T0#O_$HE``#@>/'`X<4@V\]QH`#('&FA`!8`0,]RH``0 +M%`RB`!8%0`'=3"4`@,HAP0_*(L$'RB"!#P``+"7*(X$/```)`8@`X?S*)$$# +M&!I``6@90`$#V`^BN:%JH38-0`#)!(_[\<#AQ:W!BW6I<)(.X`H-V0#`'7A3 +M(`$`1"D^#:EP`"&!?X``"+TR"2`)#=H"#4``E02O^ZW`X'@9`J`+`-C@>/'` +M#@RO^PS9K,%2#N`*BW``%``QK^C/=8``^"T@A<]V@`#$,&!Y`-A`)(\P(P@0 +M`R"%8'D`V!L($`0@A6!Y`-@/"%`$((5@>0#8#PB1!.EPR7$8V@3PZ7#)<2[: +M!@@`"0'88!X"$!>&@.`\""'\RB`A```4`#$I"%$`0"2`,,]U@`#$,$`E@1O: +M#^`(+MH!V#>%81T"$('A#`@!_%(,0`#5`Z_[K,#@>/'`6@NO^Q?9M\&J#>`* +MBW`CP$HB0"!3(-``AB#^`T(H$0$E"#(D#!P"-`HAP`_KO_`HD``2*($\%"G&`$J'(!P`+!"G)Z +M"._[9FY_"!``Z7"&"F`+"G$-%(`PA2#!``T<`C"*(/\/4\``AJFX`*82P(8@ +M^P\HN`^N2B0`=`#8J"```__:NV!`*($@-GD2XSMC0*L!X`IPA@E@"XMQSW"` +M`#PB\"#!`\`1``8/(``$P!D8``^.#PA1`(#GS""BH^`)@@L!WP+P`M_F">`! +M"G`'\(#@RB>!%,HG(A*!Y[CT((;/<(``/"(#@!B(*'6&)?L?(0A0`#H*``(@ +MAAGHSW"``(0E"(@G"-$!02E``Q\('@`3P!+"%P@>`H8B^P]!*@0"3XX+"@`! +MJ+A3P!/`$L(&>41X)7@`IH8@^P\+[8#@RB`!!,HA(0!4#>$"RB+A`PX>0A0` +MV,]Q@`"(PA8A`01`A@"A`:$+"E\%`-B+N`&A#PJ>!0&!12``!@&AA@KO_(MP +M#12`,#\(7@%8%``Q!;9:%``Q!K8%EA?HG@D``@[H!I83"%X`W@RO_`IP_@B` +M"P78$JX`V`6V!_`*<`#9W@S@`@_:#12`,#4(7@!0%``Q`K84Z`#=$-@Z<`*6 +M$2!`@\H@`@3*(4(#M`SB`LHB0@-"(4`@YPAU@`'E#12`,`\('@$*<$8+X`!5 +M%($P#12`,#L(W@`UP584`C$*<(H/[_P2PXP@`H"X<`WT"B'`#^MR=-B-N(HC +MD@_]!*_\2B1``%$EP('*)R(1$@I@"PIP`\S7<````$`!V,(@"@`7N,=P``X` +M`(.XG;B?N.QQ`*$!$@$V['`@H,8)8`#I<.T`K_NWP/'`@@BO^XH@4PFDP0#= +MJ7$N"R`$J7+/=H``",<`CDHD0""AK@(>`A4!X`"NHZZAIJ*FI*:EIKBNN:X! +MP+JN`L$'I@/`**8)IH'`F@K@"@'9`<`'IGIUB?""P(H*X`H"V0&.`\$!W^.N +M`>`!K@+`*:8(IEX+[_N+<@0@``4O)`>@`MDCK@*N`,$AIFWR$FD6>,]R@`!( +MOP!B2B$`(`\A42`MN%,@$`"*(%0%E@H@!`IRSW&``%@'0($O(DHD^*X0'D`4 +M!"*`H!0>`!0`H0/9(ZY"I@.F!O0&ZCH(X`,@V/FN!=@#KB#`X@G@`!#9`,`R +M:#9Y`"&"#X``2+^*(0@`HK(@H@;9(ZX`V1(+X`(/V@#"@-D2:A9XQW"``$B_ +M**@IJ`?8`Z[/<(``/"+P(``$SW.``(C"5GO`$`$&!"&!!,`86```V2"CSW"` +M`*B^(:-4>-H.8`N@L`?HR@Y`"PC8`Z[ZKD`C4R`AP')P\`;-_PG8`ZX#S-=P +M````0`'8PB`*`!>XQW``#@``@[B=N)^X['$`H0$2`3;L<""@4@A@`(IP"M@# +MKC$';_NDP.!X\<"*(%4+`-F*"2`$*'+.#\`(N@\``-'`X'[@>/'`X<4`%@U` +M`\P!VM=P````0`'(PB**`!>ZQW(`#@``H@@@"5,E`1!1)4"0SW&``"0Z`=C* +M("$`#0=O^P"AX'CQP*'!BW#6".`*`=D`%`4P&0T1``HAP`_K0;8+PB$`\]P@``\(O`@P`3# +M$``&`=D$(+Z/``8```0D@"\````(PB%!`"NX"PD%``#8`O`!V`]X!/`!W^EP +M!"2!+P$``,`NN<]R@``T6"EB,'4" +M$(`@SW&``'A-"&$Y"%``"B'`#^MR>=B-N(HC&0`Y\0HAP`_/<(``/"+P(,`$ +MZW**(U@/PQ`$!GC8C;A=`*_\"B4`!0,0@"`(81<(D``*(<`/ZW)ZV(VXBB.9 +M`AGQ8@T@"TIPSW"``"B^%B"`!""0SW(``!@5"2&!`.8,(``@L#4$;_NBP.!X +M\<``%H%`SW"``.!!(*@`%H1``!:!0,]P@`#I02"H`!:`0%`DOH'*(<(/RB+" +M!\H@@@\``-H4RB."#P``@0?4!V+\RB4B`,]P@`"\!@"0!NBR#L`*T@W`"GX, +M``#1P.!^]0&@"@#8X'CQP)(+;_L`V4HD`'*H($`"`!8"0!4B0#`.&)@``>$` +M%@U``!8.0'H.@`K/<*``%`2LH,]PH`#4"]R@-@P``,$#3_O@>/'`1@MO^PC9 +MHL$!$@XVSW6@`#@N'!40$$(-H`J+<``4!#``WP0DOH_P_P``RB'"#\HBP@?* +M(((/``"F*,HC@@\``.$&(`=B_,HEP@!1)$""RB'"#\HBP@?*(((/``"G*,HC +M@@\``.0&_`9B_,HEP@#GI7X*8`L_V`#`!!0!,0>E`@R@"H*Y'!T`%*(+(``! +M&I@S&0-O^Z+`\<``V/X*(``$$H$P!!*%,`HAP`_K/'`X<6AP1_=BW#.#*`*!-EAO?D-59!6"P``Z0)O^Z'`\<"IP8MP7@V@ +M"A+9/@L``*G`T<#@?N!X`!8`0``6`$``%H!``!:`0``6`$$`%H!``!:`0``6 +M@$``%H!``!8`00`6`$$`%@!``0,``/'`X<6AP0O=BW!B#*`*!-EAO?D-59#J +M"@``?0)O^Z'`\<"MP8MP1@R@"@W9T@H``*W`T<#@?N!X10"@"@'8X'C@?N!X +M\<"AP0#90,$`%@)``!8`0#4*4``#S-=P````0`'8PB`*`!>XQW``#@``12`` +M`YVXG[CL<@"B`1("-NQP0*#L<""@'_"J"B`%BW`#S`'9UW````!``=C"(`H` +M%[C'<``.``"$N)VXG[CL<@"B`1("-NQP0*#L<""@`,+L<$"@@@H@`"APH<#1 +MP.!^X'CQP$X);_L"V<]W@`#X03H,H`KI<$"'SW:@`.PGSW6````NEPH>`"N& +M1"*``(8B_PXBNJ&Y%+JTN04@@P!E>2NF!""`#Q```@`$(H(/$``"`,]Q@`#D +M!45X"Z$@A03>8'G)0'8)0A1``"'SW&@`,@< +M$0A>``'8'J'R"4`%!O``V!ZAY@@`!2"%8'D!V&D(40$`AV$(W@#/<*``1!W% +MH,.@Q*`H\,]PH`#('`'9/J`+AH&X"Z:V"4`%((5@>0'8)0A1`<]P@``\(@.` +M"(`9"!X``-F4N<]P@`#D!2N@"X:4N`GPSW"``.0%`-DKH`N&M+@+IBH)``"M +M`$_[\<#/<(``'!@R"Z`*`MD6"0``T<#@?N!X\<`N"&_[`-H(=2AVSW"@`-0+ +M.(!"(0$(@.'*(8P`0"8`$A!QE`T%"P/,UW````!``=C"(`H`%[@`(($/``X` +M``=N!""`#P``_/\E>)VXG[CL<0"A`1(!-NQP(*`BO@;P['$`H03E8;[Y#K60 +M`(7B"```*0!/^^!X\<#AQ<]RH`#4"P/=L:(`VW"B`Q("-]=R````0`':PB** +M`!>ZQW(`#@``12("!IVZG[KL(+&IPP +M['(`H@$2`C;L<$"@['`@H,]PH`"P'P'9.:#/<8``X"`(@4"`['!`H`R!`(!> +M"```SW&@`,@[#H&(N`ZAH0.!^X'C@?N!XX'[@ +M>.!^X'C@?N!XX'[@>,]SH`"H(#&#SW*``+@H`X(X8`.B`=@2H^!^X'CQP%8. +M+_NX<<]P@`"PE6@0!`!*(``@3"2`@,HBQ@?*((8/``"1#,HCA@\``+<'2`)F +M_,HAQ@_/<(``Q`8'@(0L"`D`(8%_@`#0D19YQX%_#1$`SW"``$`I(@FO_(HA +M#P_/<(``U"@6":_\(-G/<*4`"`R@@%,E39`2\BD-4!`K#9`0"B'`#^MRSW`` +M`)(,BB.?!YAUX0%O_`HE``3_V`?P_]@(N`/P_]@0N,]Q@`#D!0RAK:'.H0#9 +MD;G/<*``T!LQH(8/H`D!V!WPSW.``.0%#H.;Z,]Q@`!L3L]R@`!`*<]U@`"X +M*(HDPW\*<*@@P`(/814EPQ/G@_`B#@`!X/YFQZ.Y!0_[.!,$``HAP`_K`EE'645"5$`?@N@":EPE@MO_0&-`-@`K0&%$04O^P"F\<"&#`_[ +M#1(!-L]WH`"\+<]P@``\(BZG!(``W480$0%6(%($5B"3!`T2$#=6(!0%1B#` +M(`,2`C8-&APPI!(``(2XI!H```&2HL&&&D0#".C/<(``B++T($``">@!@@\( +MGP-0(``@+R`((%,@?J`^`P$`SW:``"0Z:18`%@'@!!(#-FD>&!"D&T`#`9*5 +M"!``SW"``(BQ-'B`$`$'A0D1`-`0`0%3(<&`%/1R$@$!X)(B?[@2@0`B?_!_ +MX!C$`Z02`0"&(?./!O)HO_!_X!C$`W`2#P'@$``!(9+B>/%PPB<.$,(AS@-T +M$@`!.&"X$H$`=!M$`Z"S.&`0>)`;!`"^&P0`$(H0JP&"`:,(B@BK$HH`VA*K +MEKHR\-(/H`&*(`4!#X?Y"-Z%3X=3(L`"30J>!1,(E0.G%@`6MKH!X*<>&!`< +M\&2X!!(!-A!XD!D$``0B@`\```#P++AT&40#H+$0J:&Q`\B^&40#88"HJ88C +M_PV$NV&A$H@2J?:Z,@(!``#8EK@$$@$VI!D``"D*7@4>#>_^`-@$$@$VI!$` +M``0@@@\"````+;H%(@($+R"((#_P`8&G"!X!-,I0B4D@Q`#R:L]P@`!(O_9_ +MX&!RB1$(G@7/<(``B,%6>`&(`_``V``DCP^``(C!5G_DCP@CPP,((P``22## +M`Q9J=7C/O0C@@!1\`HAP`_KJ8$8``SW*``#PB0X*&(/\#1+@R)``@B;A`P"##5()D>H8C_P.& +M(O\.1+MZ8D]ZSW.``-!-]"."`!WP%0L>`@GJF!&"`$`B`"E(8`SPA.H`VDAP +M$?"8$8``P[@<>#(C`"!`P"#"SW.``("7P[I<>O0C@@"(&0``F!$``(09A`"0 +M$0$!K@@@``#:!!(!-@,2#3:$$0(!@AD$`,]SH`#('UA@$'BP&00`^!,"`+`5 +M#Q%"?\]R@``\(D2"`"'1(U02!`$`)$\$'V>@$P,`\'\Y"\0#4(*8%0,0"R+` +M@!;T,(E0C3!RT2,B@A;RAB/_"2.[`>,A"Y0``KK/<8``2+]6>D%A$0E>!+@6 +M`!8!X+@>&!`-\(!P$'B&'000:A8`%@T:'#0!X&H>&!`Q`2_[HL"AP?'`W@@/ +M^PAU1L#HO2APS@`A`$AV`[A`()`%1"4"%B.Z!"6/'P8````!XD$O0!0$)8$? +MP````%A@-KG/ +M$\]R@`!844"2!2H^```A@'\``/\_+KA?`"``&6%7`"``%7E1)4"25@`A`";% +MM^4B``L`,VA3)0(0SW"``!A.\""```4I/@`*(,`.`>`&\(KEP"CA`,`HH@#/ +M<8``/"(C@<#:-(&D>88A_PXBN3IZVGH98C!X"-P3``_[,VA3)<`0''C/@6`R;H%*+X`)W'/<(``&$$#@`"`X'\X8,]Q@`#@("2! +M((%!*(,%U;A!*8(%U;D9"24`6V//F*@HDF!0:!E*H5)%3J"B!P+DMJ!KP.0U1`&)B2*%!@$FA +M2(A`+8`*4!V<]P@`"4(""P\@J@!RAP`H7/#"!,$``\@0``"I<]P@``X(#5X0*`8$P4!#!,&`,]P@`!D-@#9-*C/<``` +M0*A`P`6#$!,'`$'`&HL[BT"#6@M@"&/#/<(``EB`!V2"HSW"``%@@)X#/ +M<(``W+`OH)X++_T"V$KP!-@`I0#8SW>``)0@;@J@!P"WSW:``%@@`H5(AF>& +M#R"!`,]P@``X(%5X8*`BI>S8R@]@`T"7"!8$$,]P``!`J!@6!1$,%@800,`% +MAA`6!Q!!P!J..XY`AMH*8`AAAB06@!!(A@#942``@02%#R&!``KR`=O/_ZHL`(%000"B'`#^MRSW`` +M`$(?Q0`O_(HC1`?@>/'`X<4!W<]P@`#8AZ"@`-C/<8``E""R":`'`+%>#"`` +MJ7#U!,_ZX'CQP.'%`-C/=8``V(=:""```*6&"2_]`M@BA<]R@`"4('?8_@Y@ +M`T"2Q03/^O'`2@S/^@#>SW>``)0@P+=B":`'R7#/=8``V(?"I<.EQ*6*(,D` +MR7'*#F`#0)!X\<#/<8``V(<`$04`&PU4`0HAP`_K-'`X'[QP-H+S_K/=8``V(<$%040 +MHL%)#5``(PV0`-$-4`$(%000"B'`#^MRSW```$0?R0?O^XHC1P;/<(``EB`! +MV2"HSW"``%@@)X#/<(``W+`OH,X)+_T"V$SP!-@`I0#9SW"``)0@(+">"*`' +M*'#/=H``6"`"A4B&9X8/(($`SW"``#@@57@BI6"@^@U@`XH@A@L(%@00&!8% +M$<]P``!`J`P6!A!`P`6&$!8'$$'`&HX[CD"&"@E@"&&&)!:`$`'?2(8`V5$@ +M`($$A0\A@0`(\L]R@`!D-O2J!7DDI03P)G@$I7X+8`,`V`3P[@X/_%4#[_JB +MP.!X\<#F"L_ZSW:``-B'!!8%$$(E00"$X>8`#0`S)D%P@``D2$`G@'(T>`!X +M`H;/<8``6"!(@2>!#R"```*FSW"``#@@57@@H%GPSW"``)8@@-D@J,]P@`!8 +M(">`SW"``-RP+Z#*""_]`MA'\`J6C"`"@!'T`-C/=8``E""6#V`'`+4BAHH@ +M!00*#6`#0)4!V`"F,_`#V`"F,?`#AHP@PX\!WQ+T`-C/=8``E"!F#V`'`+4B +MAHH@10K@IM8,8`-`E28.#_P;\`#9#R$!``*&!B!`@!+T`-C/=8``E"`V#V`' +M`+4BAHH@A0RJ#&`#0)7V#2_\X*8#\`*F60+/^@@6!!`*(<`/ZW+/<```0Q_Y +M!>_[BB-&`.!X\<#AQ<]U@`#8AP05!1!")4$`DPF5`3,F07"``"Q(0">`!#R"```*ESW"``#@@57@@H!WP`X6,(,./`=H)\@#9#R$!``*%!B!`@`WT +MSW"``)0@0+!V#F`'`=@#V$(-+_P`I07P`J4#\`'8`*6M`<_Z"!4$$`HAP`_K +MXP+@3>,:X`>`* +MM0C8.G``W@*%#R;.$PL@@(,M\@2%"R"`@QGRQG@$I<]P@`"4E@B`#WDC"5`` +MSW*``&0V,(J&(,,/@+@!X2]Y,*K/<8``E)8(H<]P@``X(!4@T`,`$``@@.#B +M(`(``H4`V0`80"#&>`*E0B%`(`'GE0AU@.]_2I7/<(``E"`@D('BS"$A@`?T +M`-K/<(``9#94J`&%$0A0`8'A`]C*("(!'@S/_X$`S_K/A)L#(H209`@`'P&&A!:%3V*EQ4@I@`\ER)L`3"!X`5]BI<4(*8`/)<@;8 +M!?"!Y0+8RB!B`'H+S__U!X_ZX'CQP'H/C_H:<,]V@`#8(`"&`>#/=Z``R!\` +MIA$(40`!V%$?&)#N"H`*I!<`$,]P@`#\+R:`SW6``%"C8'D`V`&%*>@DV!C9 +M[@J@"C/:'PA0``05!1`*(<`/ZW+/<```=!F:VSD#[_L*)``$)-@!V<8*H`HS +MVA\(4``$%040"B'`#^MRSW```*LHG]L1`^_["B0`!`"&0B!`@`"F!?0`V%$? +M&)`]!X_ZX'CQP-H.C_K/<(``_"\$@"7HSW6``/@X,H7DX.!^X'C@?N!XSW"``!0X0(@1"AX`SW&@`*PO&8&*N!FA$0I> +M`,]QH`"L+QF!CK@9H>!^X'C/<:``R#L=@0?H@M@4H<]P`(`1%`ZAX'[QP.'% +MM,&+=:EPSW&``"!*X@ZO^E#:?@M@`:EPB0:O^K3`X'C/<(``<+-LB,]Q@``X +MB(PC`H`*D4$H`@,,\AD(WP("NW9[QW.``$B_`I,/((```K,`V.!_#+'@>/'` +MT@VO^E1HAB+X`T\B0P)3(<(`!2+$`,]R@`"HOA1ZC^&*(P\,RB`I``GV`)(` +MW0\E31"*(\\/IG@`L@#92B0`=,]V@`!(B\]R@`#`B\]U@`#$BZ@@P`04(D`` +MY)!D?QD/`1$`W^2P%B9`$."@X:!`)0`9-7C@H`'AP06/^N!X`-J>N@#9SW"@ +M`/Q$0:#@>"&@SW"@`+0//*`+R`0@@`_^__\#"QH8,`O(A[@+&A@PX'[@>/'` +M(@V/^DAV@.`!W43VBB7_'Q-X"0D3`+-],WD4(0``N@ZO^CMYK'@`'D`>806O +M^@'8X'BAP?'`X<5"P)AQ2'6`X`#:1/8!VA-X0L`6#Z`'@L`"P`+J$WB"#J_Z +MB'$`I0C<-P6/^N!X\<#AQ0AR`=V`XHHE_Q\)"1,`,WFS?10A@``N#J_Z.WFL>.$$ +MK_HO<.!X\<#AQ<]U@``XB,]P@``\(B.`0(4`@4,*`0`"D4*5.PH!``*%7@^O +M^R.%C"`"@!7RSW*``%0'(8(`VP\C`P`"N&9Y%G@AH@`@@0^``$B_`(&JN(BX +M`*$`V($$K_H,M>!XSW"?`+C_SW&@_O`$-J#/<*``R!\\@$`0``;/<)\`N/]8 +M&``(2B3`<<]Q```(@:@@``(IV!*X\"!```'AX'[@>/'`X<7/<```___/=8`` +M5(@#I<]P@`#L/X8-@`C/<(``"$!^#8`(SW"``+!`<@V`",]P@`#,0&H-@`@` +MV2"E!=@!I2*EG@CO_`;8F@CO_`G8Z0./^@?9SW*@`-0'&AI8@`WH&1(!A@D@ +M0P`/$@&&`B#`@'EA#QI8@/7UX'[QP$8+C_H#$@,V"'<-$@XVSW&``(BQ$(O/ +M0*X%G@%8C&)+;U88,"]#.DA@Q4)7@//<8``="&T>:"1$.6@L260 +M(PE2`&&Y);`0BS)H-GD[8F63.F*'ZR:242%`@%`.@OO>",`),@A@!0W(`\@! +MV:`80`#/<0\`___B""``Z7`I`X_Z\<"V"H_Z`]_/=J``U`<3'MB3#Q80E@`6 +M`T``%@%`HL'/<+#^``!`P=.[!7O/&)`@'MB3&18` +MEB<(%`(?%@"60<`AP)S@RB'"#\HBP@!:ARB`B`,`.P?\:<`W(SW&@`&0NSW*@ +M`#@N\"$!`-.Y!X(D>`0@D0.G\)X.S__/=H``0,P:<,EPN@]@`XMQ]@B@"LEP +MF?`#W\]PH``4!/"@Y*``%@1`!QH8,0`6!4`!&E@Q!,H]"!$'BW!F"^`)#MDD +MP%,@P0"&(/X#1+C$'`(P9,%$)HT4,PY>$([8D+B@'``PU0X>$8;8D+B@'``P +M9/#KH`V`\@P``&(0&`VO4!V`+P`-B`X"OSY0=O^H`D`S+QP((/3_H:<- +M=9`!YK8)H`$TV$\@`065N>H)H`$TV*8)H`$LV`AUG@F@`338N'`S"%X%"B'` +M#^MRSW```.L4X]L%`Z_[2B0```HAP`_K]$+3X7`"&`?X``N#YZ +M"(`("N]$+3X7`"&`?X``8#]F"(`(0B!`(-<(=8`!Y<]P@`!(ELETG;`PO)ZP +MSW"``,P'%@P@!<"@P09/^N!^X'CQP,]P@`!H!@`0!``!$@4V"B'`#^MRSW`` +M`-L.60*O^X_;X'C@?N!X\<`F#D_ZSW"@`%0N*X`'W=.Y+RA!`$X@CP?/<*`` +MP"^E$!*&%!`1AL]VH``4!*JFV@L@!X#8\]@%N(#9S@B@`9^Y#1(0-O78!;C" +M"*`!J7&JI@T:6#,$\`/8!::IAAOM?.U!+8"0"O(O)`EPX'BH((`!`!8`0.!X +M4R5-D`GR+R1)<^!XJ"!``0`6@$#@>*F&Z/'SV#8(H`$%N,D(WX?UV`6X:@B@ +M`0IQ*!X`%)3G#1H8-,HAQ0.%]^EQ@"'"`<]PH``8+/`@0@"4Y\HAQ0.%]^EQ +M@"'"!,]PH`!H+#5X!+]`H,=W@`!,NQ6'-H<%>1>'N(`4E#9#*(<(/RB+" +M!\H@@@\``,(ARB."#P``C0?*)$(#.`&B^\HE(@"`V<]PH`#0&S"@SW"@`,`O +MI1B8A!086(1-!4_Z\<#F#$_ZI!$``"AU42``@`K8RB`A!)@5`1`$(;Z/`0`` +MP'8=!!`P]"T)'@)$(0`&([A!:`0A@`\&````,;A88`0A@@\&```!UW("```! +MRB"A``/P`=@C"%``%0B0`(/@`-C*(.$!P"BA`POPSW"``'BQ`H`%\,]P@`!X +ML0&`!7F8'4`0GA4`$90=0!"2'000@A4`$9`5$1&R'000`-B`'000?AT$$`/( +MSW:@`-0'09`0%9(0".H-R,]Q@`"(LO0A```3Z!D6`)8?"!4.#!_`B``&J$/%A26">H-R,]Q@`"(LO0A```#Z`'8!?`#V!,> +M&)``V`<2#S8!$A`V`!8$0'IP!QH8,0`6!4`!&E@Q!,J`: +MH1N!`>`;H0,2`38!@1T(G@-4$0`!4R#`@`CTSW&``)`\&8$!X!FA`A4%$2D- +M$``!A>ZXRB'"#\HBP@?*(*(+SR`B`\HC@@\``+4'0`=B^\HD8@``E;!PRB', +M#\HBS`?*(.P+SR`L`\HCC`\``+@''`=L^\HD;``0C5,@P0"&(/X#1+C$'0(0 +MI!4`$#"M1PB?!0<2`C8"(L$#`-@/"5```B>!$(PAPX\"]`'8D^@-S,]Q@``< +M.T8@@`(-&APP&8$!X!FA#QX8E0<:V#,!&A@T@_`'&M@S`1H8-`#8=!T$$'8) +M8`"I<,]Q@``(6`MA=!4"$<]Q@``06/`A``!Z8E!ZI!4!$'0=A!`E>*0=`!`$ +MR`&0$^@="U$@`96X%8\06&`@E?A@$'B^'00066$_9P[POA4`$0GP()6X%8`0 +M66$X8!!XOAT$$`AWD!T$$`\6`):T'000\@N@!*EP$(TR=\P@@803\@HAP`_K +M8`1``>2Z-`1``%J%8\0`>##N/A@#WAJ'0(0Q@Z@ +M`*EP:AW"$P7PN@Z@`*EP#QX8E=T!3_K@>/'`B@E/^AIP`-^D&<`#SW"``#PB +M!(#0B?"@!\@$((`/`,```"AU,PB!#P#````-R,]Q@`"(L11Y$8F/Z,]P@``H +MOM9X(H@(C0\(0P`*<%H++_ZI<=SP42``H(;R!!4$$($,'@$-R,]R@`"(L11Z +M$1*%``]X22#"`')NSW"``$B_=GM@8#*-$PB>!<]P@`"(P=9X`8@"\`#8QW*` +M`(C!UGI$B@@A@0`((0$``"%``4D@P0,6;C5XSW&```C#`&'/`/P`X7/<8``/"*8'0`0)($H@00A@0\` +M0```/KE3)`(`'N$X>D5XF!T`$!<(G@>D%0`0C+BD'0`04-B<'0`0>/`K"-X' +MI!4`$(VXI!T`$,]P0`%0`)P=`!#/<(``/"(D@!"!GK@0H63P!=@4N)P=`!#/ +M<(``/"*D'<`3)(`0@9ZXG[@0H5;PCPA>)P&%`1*--!*!,$DAP0!R;L]R +M@`!(OW9[8F(1"IX%SW*``(C!UGI!B@/P`-K'<8``B,'6>22)""!```@@@`!) +M(,$#%FXU>,]R@``\(D2"SW&```C#`6'/<(``B,+6>%B"`8!%>`0@@`\````( +M!GD"\".%F!U`$`W(SW*``,"Q%7H@HIP=P!,%\`78%+B<'0`0$0@>)0#8D;BD +M'0`0!/"D'<`3=!W$$VH.(`"I<,]Q@``(6'05`A$)85EA,'ET'400SW&``!!8 +M\"$``*05`1`E>)@5`1"D'0`0&0E>`@K9=AU$$'@=1!"`N*0=`!`5\!#9SW*` +M`#PB=AU$$$."2((3"MX`"MEX'400@[BD'0`0`_!X'400A@YO_*EPI!4`$$0@ +M?H*,%8$0&/+/?0B40#/+X=!!`/ +M]`HAP`_K;Q/0I0`(+BS"+B@,HAP@_*(L(' +MRB!B"\\@(@/*(X(/``"U!LHD(@`$`F+[RB4"`<]P@`"(P=9X`X@'\,]P@`"( +MP=9X`HB,%0$0#K@E>(P=`!#/<(``D`9`@`:"H!``!H?HSW"``.A!`(BW"!`` +M#1(#-J\+D`$`E<]Q@`"0/)\($@S/<(``B+%T>!&(BP@1`(,,$0!["!X@GA4` +M$<]S@`!\/(JXGAT$$!:3`>`0>!:S`%0$1I[F>'400SW&` +M`-Q!`*$$((`/___3]I@=`!`-V)@=`A`)\!#8!O`(V`3P`M@"\`'8!Z&8%0`0 +MOA4!$08,+_\`VJ05`1`$(;Z/````,((=!!!0\HP5`A"<%0`1E!V`$)(=!!"` +M'804`Q(#-AD)'@,4V)`=!!`J<'X=!!!X$PX!"O`.V)`=!!!^'<03>!,.`4IP +MPG@0>+(=!!#/<(``-+$`@(8@?X\+])@5#A`3#E\289.%ZY&YDKFD'4`0$+@E +M>*0=`!`$(H(/````$,]Q@``\(F2!4B("`Q"#!7I0HT2!$(($(($/````$#UY +M)7@0HA3PF!4!$(`=Q!.4'4`0GA4!$7X=Q!.2'400OA4!$;(=!!"0'400@!4` +M$7X5`A&"%0$1&F*$%0`166$X8!!XL!T$$*05`!#/<9\`N/\6H9P5`!`6H34$ +M#_K@>/'`X@L/^EH/3_S/<(``<+,,B,]Q@`!(OP*X%G@`82VX4R``@`7TSW6` +M`"0Z#?#/<8``/"(@@<01`0;/=8``)#I1(4"!!/0!V=P=0!#/<8``/"+P(0`` +MSW*``.A`(((8B$4)-0%!'1@0,R9!<(``7$A`)P!R-'@`>!8,8`@#V*8+8`A` +MV`#8X!T`$`[PSW.@`*@@,8,"@@#>PJ(X8.`=`!`!V!*CJ0,/^O'`F'"X<11X +M.&#/<8``8$T(88P@PX_*(L$'RB"!#P``K!/*(X$/``"+`2@'(?O*(<$/T<#@ +M?N!X\<#AQ0?9#1I8,,]PH`#4!QH86(`.$`*&SW&``/06*($'&I@P#^G/=9\` +MN/\]I<]S@`#`("2#`>&SN;6YN+DDHS:ESW&@`$@L7J$?$`"&`1H8,`3*G.#, +M((*/``"1``;R`!8`0``6`$`#S,]QGP"X_QBA?=@R#:`"`1(!-OD"+_H$RN!X +M\<"X<0*YSW*``$B_-GDP(D0`421`@LHBP@?*(((/``#+(LHC@@\``),#;`8B +M^\HAP@]`+8$!SW*```C#(6)1(4""BB((!"@_ZSW*``.`@1(+/=8``5(ABA4""-KLVNE!SUB*-#P``@`#` +MA3UB?F8=#843"B'`#^MRBB"-`HHC$`28=O4%+_NX=1YF_PV%DUA@/0(O^@X@ +M@`/@>.!_`-@4>#A@SW&``*!8X'\(8>!XX'\!V,]Q@``$.N!_\"$``/'`F'`* +M(<`/ZW(*)<`'SW```)\9I04O^SO;X'C/<8``X#G@?_`A``#QP)AP"B'`#^MR +M"B7`!\W8!;B!!2_[1-O/<8``&#K@?_`A``#QP)AP"B'`#^MR"B7`!\]P``"A +M&5D%+_M-V^!XSW"``#P]X'\`F.!XSW&``!`')('@?R"@$8C@?\*XX'C/<8`` +M^$!&@8HA_P\@H`;J(H(@H`'8`_`"V.!^SW&``!A!1H&*(?\/(*`&ZB*"(*`! +MV`/P`MC@?HHA_P\@H,]S@``804:#$NHD@AL)7@#/<8``0$`/"D``SW&``%Q` +M$0I!`$""Y0N!@`+8!?`B@B"@`=C@?O'`E@@/^L\($`#/=H``,)HOCL]P@`"( +MP<]U@``\(C9X(H@#A0#?SW*@`"P@-!`1`3P2$@`.CH#@G``I`,HEJ1",(@&D +MD``E`,HE)1%DEI3CP".&#P``DP#/<*``:"SP(-``Y:)0V$4A00(8VNX,(`H@ +MV_BXRB4B$B[T`]C/<:``]`<%H83:#7!`L$(B`"@-<@"R0(8-<$"@0I8-<$"P +M`X5`@`UP0*`#A4*0#7!`L`:60"@")<.X#+B"N`5Z#7!`H.2A#HX!X`ZN3@J@ +M""IP`=T0\`#=SW:``#":<@V@!@26`-C/<8``'#L.KAZ!`>`>H0$`+_JI<.!X +M\<"F#\_Y&G"$*`@)`"&!?X``T)&'$0T&SW"``,0&`H"@O8<96`,$B`_H`X&- +MZ`HAP`_KECH6!1$*(<`/ +MZW(0N`4E!0#/<```@PR*(X4/`0,O^PHD``1`*`TAW64EE025$+DE>#CHSW"` +M`,18\"`!!$0H/B<`(8!_@``8/R]W(*`CE0*5$+DB#^_^)7@(<0`G@!^```P_ +MZ@@`",]P@`"X6/`@`00`)X`?@`!P/D>5(*`CE0*5$+H0N25X)I76#&_[17GF +M#L_^"'$`)X`?@`!D/JX(``A>EAV6`-D/(0$$$+I%>`8@0(`!W1VV,+@>MAKT +MSW&``.`M`(&@N.8+H`0`H<]P@`#@(`2`EMH>VR"`SW"``(B6HJ`AH`S9,@O@ +M!AB[$-K/<8``S`<`@0`J`@1&>&$&[_D`H>!X\<#^#<_Y`-W/=H``2)8^E@\E +M#1`=EA"Y)7@&('Z#0?3/<8``X"T`@8"X`*'/<(``O`;/<8``:"4`D$>)-PH! +M`,]P@`"^!@"008DK"@$`SW"``,`&`(@FB1L)`0`+R`0@@`_^__\#"QH8,`O( +MA[@+&A@PSW"``.`@!(#/<8``B):6VA[;`(``H0#8`J$H<`S9@@K@!AB[`-C: +M"V``@-D^EAV6$+DE>*5X';8PN+T%[_D>MN!X\<#AQ28,8``H=8#@RB!!`P0+ +M80/*(6$`I07/^3$'S__QP"8-S_FJ"*`'`-[/<*``T!L1@!<(W@/*#&`(`=C/ +M<8``D#P)@0'@":$&R`,2#39%"!X`I!4`$#T(G@3/<8``W#D`@1CHP*$%\&X) +MH`"*(,<)^PF>Q<]PH`#$+`N`4R"!!/ZXS"$B@`;RF!4`$-(+[_X`V@,2`3:@ +M$0``%0@>!(H@"``,&APP<@X@!"AP+_!'"!X%!\C0B0#:,Q&/``0@@`\!``#P +M02@-`\]QH``X+@>!#R)"`P'<1G@'H0W(#@S@"0`L`!#'=X``2+\"OM9^$N?? +M9Z"OBB`0``8:&#`#R*`0@`#$X)P/@0D#V<]PH``4!".@F03/^?'`+@S/^<]U +M@``$BP&%SW.``(C"1"`$@\]P@`!PLPR(TFC6?L=V@`!(OT"&%GLA@Q+R4"*/ +M!>"F1B$!!B&C#0P1`9&_X*8%\+&ZMKI`IB(-@`D'\):Z0*9%(0$&(:,+C:*X +M-03O^0NMX<7AQL]P@`!PLTR(C"("@,]S@``$BQ?RRHO/<(``B,(R:C9YQW&` +M`$B_5GA`@:&`!>Z5ND"AJ[T$\+6Z0*&+O:&@`-@+J\'&X'_!Q:'!\-Z8@0E@!_`````-KC/:ERQKI)(L(%5'G/ +M<=Q@`!( +MOZ"!SW.``(C"SW>```2+Y)<6>T"6.%88GNQ_`H8PG1)!&(@(&0:,%])&^ +MP*$+\+&]MKV@H0\/41"6O:"A12("!D&CC@N`"0#9SW"```2+J0+O^2NHX'[@ +M>.!^X'C@?N!XX'\`V.!^X'CQP*((#_K@>.!XX'C@>&D@@`%O(3\`:2```/?Q +M\<`&"L_Y&G#/=J``T`\`W0?P$!8`EOUA^&`0'AB0(VUO"$0@)18#EB46`I8O +M),<`)18`ED]_#WT(O:5_UPP1@X+GS"?BD\PG(I?*)4(0(?3/=8``U)9)K246 +M`I8*K4NM)18"EFBM3*VB:14/T1//<(``X98&"R_Z#=D-Y1,/$1?/<(``[I;V +M"B_Z#=D-Y1`6`)8"($$C.&`0'AB0S0'O^0'8X'CQP&H)S_FAP0AU*':$Y0#8 +MF/>+<,(*+_H$V0#`&PB`#YH)4&_/<:``U`L/@62]N&`/H0'8!O"I<"8/[__) +M<0]XE0'O^:'`X'C//'`SW.``(@&:'`F#"``!-D$:QX,(``$V='`X'X`V,]Q +M@``\!P&I90=@"0"I\<#AQ7(*K_TPV+1H:@JO_3;8!7T8O9&]SW"``)1(L@M@ +M!Y*]*+CI`._YI7CAQ3)H-GG/!XX<5*)`!X`-BH(``(`-O/=8``P"!`A0\C`P`+(L"`#_)! +MA0LBP(!`VL\BX@?*(H$/``#0`,\BX0<"\`#:SW.``/06%7M`HP'@X'_!Q<]P +M@`"<00:``X`@@,]P@`#H?2F@904O_!'8X'CQP'H/K_D!V<]P@`"(*$()+_\D +MH(H@Q0_/=J``R!\9'AB0`=@!V2AR*',:#>``F'%R#._[`-\^#L_[SW6@`-`/ +M]:7/<*``P"]Z$`&&B;F+N7H86(#/<8```+40&%B`P@C``:8*S_YV"L`&0-G/ +M<)\`N/\RH!8*``F`V<]PH``4!"R@'1U8D+8*``AZ"<`'F@@@".EP!]A('AB0 +MF@[`!:X(@`%F#P``7@]``QX+P`;F"@`#N@\`!Y8(0``B#T`&8@C/^TX/P`#^ +M#H`'\@H/_]X,0`3B#``!>@W`!4H(``7"#8`#$@B/_O8/``3N#P`$U@O`!L]P +M``#^RN()C_OE!H_Y\``#PB`X<(@,"XP@\@""\@`"``W<]VH`"T +M1\]PH`",1+B@`-B3N'<>&)`(V'<>&)``V)ZX4QX8D.!XSW"``-P`$'A3'EB3 +M1QX8D,]P@`"``Q!X2!X8D$\@@"-%(``-3R#&!S381!X8D!S811X8D,]P@`#< +M?0&(1AX8D,]P@`!PO=H/H`0,B$HD@'#/<8``T,>H($`#SW"``'BQ08!T;71[ +M.V,"@$.C`>4$H\]U@`"X00"%!.AD'AB00QZ8D7(-(`@!V`.'"(!`A1T('@!3 +M(D$`$KE$(@`##K@E>(8B_P,*ND5X$O!(<(8@\P\*N`0B@0\````,!KDE>`0B +M@0\````P`KDE>,]Q@``8+LD%K_D"H?'`X<40W8H)H`&I<`?9"[G/!X\<`V#:_Y`-K/<(``Q`9#H/_;SW"``,R9 +M$QC8@$HD@'!(<:@@0`>$*0@)`"&.?X``S)//=X``&$%!I@;=I:;/=0$`O,"D +MID:FYZ8D'H(0`"&-?X``[)-`I0'ASW"``,R9'!C8@,]Q@`!`+0"!'-I`H!C8 +M#@@@``*A)06/^3G9SW"E``@,/J#@?O_9SW"``*B1(*@`V<]P@`!0D>!_-:#@ +M>`#:@.'*)$UPZ""M`?_97&`@K`'BX'[@>/'`X<7/<8``W+#/<(``/%C"""`' +M2-K/<(``O%'/<8``O`>R""`'"-H`W<]Q@`"D,J&AHJ'/<(``O#6IH$8(8`(# +M@<]PH``L(,]Q@``L-E"`$(!%H0:A$@O@`:FAF02/^?'``-G//'`^@NO^2#9`-K/=:``R!PII<]QH`"4 +M$UNASW.``!088(/S:,]V@`#DL@R&]7]3(,0%\&/[8U,@CP"DP8MQ.P_1$!^& +MF[@?IC06@!#BBQL(P0,H<$`C`01$:WX*X`=`)@,<#=HJ\!Z&D;B2N!ZFSW"@ +M`,P7*_`A#U$102H"4D`C``3!NF8(K_V((%)P`1`*$%@P&A!H,"H0>#`Z$#XL]PH`#,%\]QH`"4$URA`=J( +MZA^&E[@?IB#8"J49\`#!`]H8&%B``<$9&%B``L$:&%B``\$;&%B`%!B8@(H6 +M`1$0&%B`!-DGI188F(!=`Z_YI,#QP.X*C_FD$`$`HL';"5\&(-G/$0WLHFXA$&%`\Q +MC"?#GPGT!!0/,?%VS"?JD`'>0O8`WNONQ8!%?L>EL8B&)?P?&+VE>L]UH`#, +M%UJ@%O!%@,]QH``0%$>AI!`!`!4)G@(QB->ZAB'\#QBY17DZH,]UH`#,%PW9 +M`=H#X0T=F)`.'5B0)H`9'5B0)X`:'5B0*(`;'5B0`]D4'5B0CI!`!`)FYI!A``'$"K_FBP/'``@JO^039"'4-$@XV +M!M@-&A@PSW>@`!0$"J?/<(``,%@Z#\`&`(4R#^`&!-D!A2H/X`8XV0@5!!`! +MA0`0!0$)#!``%0T%`0HAP`_K@((.+_T!V`GPBK@:H1J!TP@?@'X.+_T!V`#9F[G/<*`` +MT!LQH'8+0`E""4`)SW"``)0Y`(!"(`"`RB!B`-'`X'[@>/'`X<7/<8``2)9^ +MD5V1$+ME>@'=&PH/`,]Q@`"X/D0H/@?:"J`'`"%`#JEP`O``V&4!C_E&@0GJ +M(X%@@2*"8GDP<`#8`O8!V.!^X'CQP-8(C_D(=<]V@``80=X/[__)<0?HJ7#2 +M#^__0"8!&(/H`-@)\,]Q@`#X0+X/[_^I<'GH`=@-`8_YX'C/<(``Y`4.@(#@ +M`=C@?\!X\<"2"```!NB."```#PA1`,]P@`"T*`"`@^@`V!+PC@@``(_H?@@` +M`(OHSW"``(0E+)#/<(``/"(>D.,)`8`!V-'`X'Z1!Z``$=C@>/'`N'#/<:`` +MK"\8@1D(G@8*(<`/ZW**((P)9]LY!*_Z2B0``!6!&P@?``HAP`_K$H45`):"#H_[BB`$`"0=&)!1)H"0 +M_`Y"">D'3_GQP'8/;_DTV`8*@`#/=X```'0O"!X$H@J@`@#8=@J@`@'8BB80 +M$`#=R@_O_JEP%"=,$V&^`+3U#G60`>4.\`#;BB(0``H,X`-P>!0GS!!AN@"T +M]0IU@`'CA0=/^?'`&@]O^338H<$`W:8)H`!`Q<]W@```7#,('@26"N```=@# +MW@J^`-B,N+A@$'B+<8H+X``!VA0G3!-AO@"TZPYUD`'E7@K``!#P!=L*NP/: +M"KIX99X+X`,0>!0G3!-AN@"T\PIU@`'E&0=O^:'`X'C/<`$`U";/<8``+!IA +M&1@`SW```,1052%"!T`A`P,&Z!VC&X&#N!NASW```*10!N@"HAN!@K@;H<]P +M``"44`;H`*(;@8"X&Z'@?N!X\<#AQ6_8E;C/=:``R!\2'1B0SW`!`$`\%1T8 +MD.8(``B*(`0`#J6Q!D_YX'CAQ0#;SW*``$B+2B0`=,]U@`#`BVAPJ"```D`E +M`1(4>6"Q`>!(<,]QH``$)0^A5B(`!!&A5B(`!1"AX'_!Q>!X\<#R#4_YSW6` +M`#PB!87/=J``Q"=U'AB0#)5V'AB0!X5Y'AB0$)5Z'AB0[@WO_P#?&^AW'MB3 +M>![8DX`>V).!'MB3!X6&'AB0$)6''AB0!X6*'AB0$)6+'AB0!86('AB0#)6) +M'AB0!86$'AB0#)6%'AB0P=A0'AB0X05/^>'%"''#N,]R@`#(B_0B`P#)NW!Q +MRB0B=,H@(@#H("("]"(-`,F]"0E``P'@X'_!Q?'`X<4(=<]QH`#$)QD1`(8! +MVH#@$1$`AL!Z@.(`I=$@X8<`V#/TSW"``&"S#(#/<:``R!]DX!ZA$-@.H0'8 +M%1D8@&8(8`D+V%$A`,;*("(`&/0E"%Y'SW&@`-0+%H$X@23@%0A%`$((8`D# +MV`D+'T`)")Y$&-@#\`#8@.#*(.($SW&@`)`C/H$@I34%3_G@>/'`M@Q/^<]V +M@``\(A4F`1!`@6F"N(I!*\``P+@7N,=P``"`'.2[SR`B!N"[3M_/(*(`RB>" +M'P``3@&&Y<\G81(I"U\!SW6``(0E&!4$$;Z6&PT!$:&&Q!4-%A$-7Q&@AL05 +M#18'#5X1@;A1(P""SR"B!1NB_*)`@<]P.@1*8H@!``>I1GPBB`0`!ZE%?``V(NX +M'J41\`#8C+@>I0WP`-B-N!ZE"?`#V`RX'J4%\`#8CK@>I8(@`0$]!&_Y'J4* +M(<`/ZW*,V(VXOMN+NTHD``#9!V_Z"B4``>!X\<#/<8``%#P7H>!XX'C@>.!X +MX'C@>.!XX'C@>.!XX'C@>.!XX'C@>.!XX'C@>.!XX'C@>.!XX'C@>-'`X'[@ +M>/'`SW&``!0\%Z'@>.!XX'C@>.!XX'C@>.!XX'C@>.!XX'C@>.!XX'C@>.!X +MX'C@>.!XX'C@>.!XX'C1P.!^X'@A@`#:4R%^@`OR`-J9NE$A0(#*(H(/``"# +M`,`J8@;/<:``[$9%H6:02",#`T>0$+L$(X,/#P```,BZ17M(D`RZ!"*"#P`` +M`/!E>D:A0(!!"M$`8H#/!^\<"B"D_Y(8#/=8`` +MP"`"@""E`-X!I<*ESW#0_@``H@JO_P2ESW*?`+C_W:)O(4,`SW"@`.Q&):`B +M#(_^`(4M"-X'SW"@`,@['8`0Z,]P@`#T%A^`"^@=H@2%`>"SN+6XN+@6H@2E +M`_#=HL]P``!559T"3_DBD$@A00%`*0(#(Y!B@,NYC[E%><]RGP"X_WVBSW*@ +M`.Q&)Z(C@#6B)(`VH@6`%Z+/<```557@?O'`[@EO^0#;IL'/<8``P"!@H6&A +M8J'/=J``M$6)!*)(!QSW*``,`@"!(%``L0D```W3EUV'6I-*)P``#R='`PL@P*$)\A4D03,@@4HC`!`/(TL0`_!* +M(P`0*H@+(<"!!2')$@GR%21!,R"!2B<```\G1P`#\$HG```%(L(!D.\5)$$S +M(($,$`4`/]\*)(`/``"MW@\@2!`$&L(3$0]1$A4D03,@@0#:#R)"`!,/$1(5 +M)$$S((%*)@``#R9&``'ESW6``,`@"!U`$8#CRB2!#P``K=XU\B6(9(@&(H(! +MQ;H0N04C@P\````_97D%(8$/`#\``)L>6)!GB":("+ME>6B($+ME>6F(&+ME +M>9P>6)`&(0$2Q;F9'EB0`-F5'IB0F;E,'D"0)(!8'D"0"I"4'AB0;R!#`),> +M&)`R"H_^B'#-`&_YIL#@>.'%X<;/4$&E``(H8$&U``,8#/8&EB`,H"S&EB`$X"T&AB`SW```%55 +MP<;@?\'%X<7AQB2(SW*``-!8IHC"N2YB`-D/(8$#SW.``"B00(.$[29Z0*,8 +M\$5Y(*,EB!4CC0,CI2:(18A982:E((",(1"`1?>*(1``(*`CN2&C`(`JN`*C +M`-G/<*``\#8LH".#):`F@R:@)(,GH">#**`E@RF@*(,JH"&#*Z`B@RV@((,D +MH,'&X'_!Q?'`9@\/^0AWFG&Z`':7/ +M<(``1#UX&(`*SW"``$0]?!C`"L]P@`#`/008``O/<(``1#V$&$`+*(&(&$`` +MSW&`````)(&,&$``+R''!0BY)7HO(0<&17F0&$``6@E@`"79308/^?'`,@X/ +M^<]S@`#8/4.#`-W/=J``+"#0AO)J]']_9\6G!*=`(D*`)J=#HP;R`H.CHP'@ +M`J-E!@_YSW&``.`@"($`VD"@#($!V4"@SW"@`+`?-*#@?O'`X@T/^0O(`-X$ +M((`/____`PL:&##F#2``R7#/=8``Q`81A8#@M`PB`,H@8@`A!B_YT*7QP'H) +M``",(/^/RB`A`-'`X'[@>/'`F@TO^6K8HL&+<0':M@B@`$ASC^@*(<`/ZW+/ +M<```TA2*(\4$BB2!"HD!;_I*)0``0"2!,438`=J*"*``2'./Z`HAP`_K*),$*)0%O^DHE```$%``Q0"2!,`':)@B@`$AS +MC^@$%`4Q"B'`#^MRSW```-04BB-%"/D`;_J*),$*`A0`,<]V@``P1AMX02C% +M`$PE@(P`'D`1U/8*(<`/ZW+/<```U12*(X4)Q0!O^HHDP0H=V,]V@``P1@"F +MN'``%``QSW6``(S-0"V"`*EQL@]@``';D.@`%`0Q`!8%$`HAP`_K@7P@.(!VL!Z-PI1`$PB +M`*8!VL(BB@"&(_T/)[L/"8``@.+,(&&@"_0R=\PC(8`)\@/O!>L+"<(C#P[" +M$\EW!/`!V0CP"'4!X&<(1($`V8H@_P^$Z8#ERB!*`XP@_X_*(($/_____Q7R +M,B2"-,]Q@`",S0\*40!B<19Y`A'```GP%GD+"I$`!A'```/P!Q'``%$#+_FG +MP/'`"@W/_X8+3_]."P`&T@A/_PHAP`_K!X +M\<#/<(``/"("@,(0``91($"`Y`^"!='`X'[@>/'`L@H/^1IP*'4Z6"@S+G/<(``8+,OH@R` +MSW*@`,@?9.`>HA#8#J(!V!4:&(!-<(8@_`/0X,P@@H\``(``$O*,(`.$$_(* +M(<`/ZW(*)(`*SW```#(1BB,:#5D&+_JX@XO^RIR!/#>#F_Z"G`;Z,]Q +MGP"X_\]SH/YL`W:A%J'/`78,@`.)Z!2.@>#*("$!B`AA_\HA80#/ +M<(``=)<`@`T(G@`V"F``$):TKL]P@`!TEZ"@37"&(/P#C"`"@!3TSW&``$@9 +M`($!X`"ASW"``#PB`X`8B(3@*`\!_\(,@`.R"P_[!O",(`.$*`]!^K4!#_GQ +MP$()#_D`W@+=SW>``,"30"<`&X0N"!DP($`.42``@'@)(O_*(((#8;WI#760 +M`>8.""```-AU`0_YX'B`X/'`$-@*\D(/#_N2"6`!BB`$`-'`X'X.#@_[9@E@ +M`8H@!`!N"4`'#0B1`(H)8`<`V/+Q\/'@>/'`T@@/^<]V@`#,!P#="_`0V+AX +M"R$`@*0)(O_*($(#`>7Q#?20((:`X/'`X<7/ +M=8``Q`80A9_H$@E`!X+@+`EA!\H@(0`!V!"ED@TO^Q'8O@\@`!#8$:4)Z((- +M+_L0V-H(8`&*(`0`SW```%#;Q@YO_X#9P0`/^>!X\"X/@/(0?*("$`L0?O^*'`\<#/<```($Z2#8`(SW&``#@I`*'/ +M<0``N`O/<(``I"@@H,]P``"($W(-@`C/<8``J"@`H<]P#P!`0F(-@`C/<8`` +MK"@`H0784@V@"`NXSW&``#PM`*'1P.!^X'CAQ>'&02T`5,&X%P@5`3,F`'"` +M`%!(0">!46]I7G/=:``B"0PI3^#`MU$*#X-`"&`?X``"+V5N3^C +MSW&@`/`7O:&D@(H3`P&FH:.`%..FH:*`4R/#@*:AH8"FH<`@(0C`("(,8(!S +MH6QH8(-SH?@0`X)SH?P0`(`3H4JAP<;@?\'%X'CQP.'%H<$(=<]PU+K^RD#` +M!/!^">`(`=C/<9\`N/^ZH038&Z&+_XF'`H=AH((`!(=08@@0.(<$X((`"E>=T%S_C/<8``6"U@ +MB<]RGP"X_P;KSW'0NO[*/J(:H@[KSW"@`#@N!8`$((`/P````/$(@(_````` +M:M@8N!FB'(+@?N!XX<7/.!XX'C@>.!X`>+@?\'%\<"R +M#,_X"'?/.!XX'C@>.!X +MX'AAONL.=9`!Y>$$S_C@>/'`<@S/^`AWSW*@_H`$SW"?`+C_5J`G"70``-TH +M=NX.[__P)T`3X'C@>.!XX'C@>.!X8;[K#G60`>6A!,_XX'CAQ<]UH/X@!`#: +MSW.?`+C_MJ.`X.!XX'C@>.!X`>+@?\'%\<#^ +M"\_XZW#/=X``+!P`AS4(7P#/=8``U"``A5(@@```I0GPSW"@`*@@#8#DX.P` +M!0!F#N__5-@`%000AB#_#N4(`8'/=H``."J8%@"6?PB>`.H*#__/=8``/"+) +M%0`6I;C)'1@0DQ8`EJ6XDQX8D-<5`!:EN-<=&!`.A:6X#J4`A<@0``:&('^. +MRB`B`,HA`@#4"V+YRB*B`0&%R!``!H8@?X[*(&(`RB$B`+@+8OG*(J(!`(7/ +M<8``W+#$$``&);C`N,H([_P*H>8-#_Y_V`JXSW&@`-`;$Z%_V!"A`-B5N!"A +MSW$``(0>#@H@``;8SW&@`/`V!(%&(,`!!*&4V-8-[_\8V0"'42!`@`0((@'* +M("(`40//^`HAP`_K/'`T@KO^`'8)@U@!P#> +MSW"E``@,SW6``(@HPJ`$A5$@@(`@#L+YSW$``#@*F@D@``;8"\@%((`/`0`` +M_`L:&#`$A2,(G@#/<(``E#D`@(OH1@IO_XH@Q@@+"%$`:@@`!0WP`-F>N<]P +MH`#\1"&@X'C!H,]PH`"T#]R@B@C/_`H(``'*"Z`&`=AF#^_Z`=BQ`L_XX'CQ +MP#H*S_B(=0#?"N@9"%$``=[/<(``)!G`J`;PSW"``"09X*@)Z1L)40`!V<]P +M@``A&2"H!?#/<(``(1G@J`KJ&0I1``'9SW"``",9(*@&\,]P@``C&>"HSW:@ +M`,@?SW"``"09&![8DP"(BB$0`!'HSW"``'DA`(@+Z,]P`P!`#44>&!`PI@+8 +M&!X8D`+P,:;/<(``(1D`B!OHSW"``'HA`(@7Z,]P`@!H""`>&)#/<(``*``A +M'AB0SW"``.`%(AX8D!@6`)9%(``#&!X8D,]P@``C&0"(".@8%@"6A2`!!!@> +M&)`/"U$`&!8`EHBX&!X8D!@6`):`N!@>&)`8[0#8E+C/=8``Y`@`I7'8!K@& +M#.___-D@A<]P``!,'/8+[_^?N1@6`):%N!@>&)!U`<_X\<"8<`/I(PP2",]P +M@`!H!@`0!0`*(<`/ZW+/<```V@X%!>_Y>=O/<(``8"T5(``!(*#1P.!^X'@` +MV4HD@''/(%>>!_+RA!`.'%`-I*)(!QSW6``,!] +M2'.H((`!\"7!$`'C)7H`V9ZY&7D$(8``0B``@,H@8@#@?\'%X'CQP,]QH`#( +M'Z01`@#/<(``+#8`@#6!SW.``%2(EB!!#Q!R`-K*(F\``8/5N8'@`=@"\@"# +M#0A1``T)A`\``(@3`-@#\`'8@>+,(&*`4`WA^LH@X0'1P.!^`N$P>4%I#0H# +M`")X$'@#\`+8SW&@`,@?'J$0V`ZA`=@5&1B`X'[@>/'`X<50W0#:SW.@`,@? +MKZ->HP(@0@!>HP':%1N8@$#:3J,$(+[/``(`$+`/P?])`,_XX'@`V<]P@`!T +MER&@SW"``.2R')!BN$@@0``0><]RH`#('Q^"$'@((0$`,'D"V!4:&(`_HN!^ +M`N$P>4%I#0H#`")X$'@#\`+8SW&@`,@?'Z&*(!@(#J$"V!49&(#@?@#9SW"` +M`'27(*`AH.!_(J#AQ>'&SW&``+#'18$DZ,]QH`#('T`1#@;/.!_`-CAQ/P'"X]AJ"*__BB$$`\]VGP"X__V& +M"B'`)T#9G[D]IL]QH/X<`#:F4R;`-`4@@`^P_@``%J;/<```1!SZ".__"B#` +M+WIP&14`EB,('@)8'H`7(14`EB(5`);/<8``W`@`@1:F`8$6IOVF!]C*".__ +M"KA3($$'!]C^".__"KC/<*``U`L8@$(@``A((```SW6``"0ZO!T8$,]P@`#D +M"`"`"R#`A,HF(A/*(&(`7_2#"I$@;]B"".__!KC/<```T!MV",__SW```-0; +M;@C/_\]P``#8&V((S_\'V%X([_\*N,]P```$'%((S__/<```"!Q&",__SW`` +M``P);P5`!:)Z$$K3B7`O@.^ +M7.8!V"'P--X>\(PB!*`9\DPB`*(1\@CV&PI0("<*$2&&WA+P%PH0)(PB`:`+ +M]$S>"O!FW@CP/-X&\$;>!/!4W@+PA-X`V('@$`R!!),5`Q;)<`IQ*G(*)(`$ +M/0'O^0HEP`3@>.!^X'C@?P'8SW*``,0&(H(EB1+ISW&``+"5>H'/<8``[).$ +M*P@),"%!#@T)7P`(V`NB`=@)H@#8!*(%V`.BX'[QP.'%"'4H<`7KSW&```!T +M!/#/<8```%Q;>H(-K_BT>2T%K_@!V.!XSW"``+X&`)`&Z`#9SW"D`!Q`,J#@ +M?N!XSW"``+X&`)`&Z`/9SW"D`!Q`,J#@?N!XX'[@>,]Q`0#'`\]PH`#L)R:@ +MX'[\'`BT\<`:!L]P@`#L+0"`0'CTV`#9D@ZO_P': +M--@`V9&YA@ZO_P#:,-B*(08`>@ZO_P#:--@`V0/:;@ZO_Q2ZA@ZO_S#8PK@) +M"%$``-@#\`38SW(!`,8#SW&@`.PG1J'/O^0HE``0R:`0A@0\``/S_[@VO_RS8$(4"(``$C"`/B@CW +MG@VO_RS8"'?O"!Z`!?``AH"X`*:*#:__--@7"%X%`(8`V0#:@;@`IC384@VO +M_Y6Z,+\"',0S?/`/>1"Y!2&"#P``@OW/<:``["=&H00@@`\````?2+B&N!"X +M!2"`#P``0OT&H1"%`B``!(P@#XH+]XMQ@@]O^8H@#PT`%``QYP@>@`3P`(:` +MN`"F@<%J#V_YBB!/#`04`#$-")X``(:!N`"FBW6*((\/3@]O^:EQ(,`(N`(< +M!#"*(,\/.@]O^:EQ(,$"%``Q)7@"'`0P,O#/<0,`0O[/=Z``["!"X!2"`#P``0OT&IQ"%`B``!(P@#XH+]XMQ\@YO^8H@3P\`%``Q +MYP@>@03P`(:`N`"FSW`&``+_!J=`)($PS@YO^8H@SPX"%``Q,0*O^*+`X'C@ +M?N!XX'[@>,]QIP"(20#:"PA1``/8#J$"\$ZAX'[@>.'%X<;/=8``*"Z@C0#> +MP*.1[8'@S"$A@`WR"PH3","C"?#`X@;8!O9"(@`(0[@"X`"CP<;@?\'%N'!` +MW``A`(/QP`X`)`"8<8P@`H"+]@HAP`_K/` +M(&D`@>+`(6D`B"`^`(D@P0^((3X`B2'!#X#@UB`K"(#AUB$K"'8)``#1P.!^ +MX'CQP-8(C_BAP3IQ`-^`X,HAP0_*(L$'RB"!#P``RA3*(X$/``"(`LHDP0#8 +M!*'YRB7!`\]Q@``L+D"QSW&``"XNX+%,(0"@RB7.$V0`+@#*)LX3&G=:=P7P +MR7<:=6IP0"!3`(MQ`=JN"^__`-L`%`TQ+R/()*EV*;W(OK_EV24I%$PB`*#* +M(,(#RB&"`\HB`@1,#2(`RB-"`\EPW@[O_ZEQ0B%1(+4)=:!`(E(@R7`B"2`` +MJ7%Y`*_XH<#@>/'`(@B/^#IPSW"``"@N`(@:<9D($0`Y"1`@SW&``(`'I8D$ +MB1UE,G7*(

"?``V2"OZ@[O_XK9`>;/?@`@@2^``(`')HD!:3$.`Q!`*8`@%'BU +M>-1XSW.```":$&-2;6WH`N`0>-1ZSW.``/294F/@Z0'9W_$!Y:]]L0W2D-D' +M3_CQP'X/3_C/__"G$@E8PA$("T]DT'3_C@>/'`U@Y/^`AUSW"``"@N`(AZ<:'!&G+/"!$` +M.0L0(,]Q@`"`!\6)!(D>9G)VRB',#\HBS`?*((P/``#,%,HCC`\``#P#RB3, +M!+@"K/G*)8P#2B$`(`#>"O`!'5(0!H^$Z""M`>4!YL]^SW>``(`'`"<`%`:( +M`>!C#B,0`G=`*X`@%'@5($`$U'C/<8```)HT(1(``-G%"A"@BW%*<`+:=@GO +M_P#;"^@!%(`P`1T2$`:/VN@!%(`P`*W5\0HAP`_K@*(<`/ZW)3 +MV`:X^]L*)$`$I0&O^0HE``0@PA,*$@``P$$H`0)3(<0`%0P2`0'9SW"``"@N +M5P(@`""HSW&``(`'0*D"&0(!02@.`U,FQ1`#&4(!3"7`@,HBR0?*((D/``#" +M%,HCB0\```X!2`&I^$O!`*8$@-'D*%(`P%2%!`0'FSWX4>;EA +M`!D$!(`@`B,O(`@D`,!!*`$&P[D!X<4.0Y""P0IP`MIB#Z__`-L+%(0P+R@! +M`4X@A0&]"4.@,+C#N``@#@2"P:EP`MJN#J__`-L+%(0P+R@! +M`4X@A06&^`>#E#G60#WAA`V_XH\#QP.H*3_BBP4#`0<)`*!0%0"D7!0#=0"H3!4`K +M$@4!WDHE@"&I=P3P"G7*=P#`%;@3>!0@P`62#&_X!]D"(%`#`B!`(X(,;_@. +MV/'`621<.(MPSW&``.1(2@EO^(HB!`)*)$!X +M`-FH(``#%B1`,&&`0)`KV!*X`>%5>&"@,'E6)%PXT<#@?O'`@@P@`$?8`-K/ +M<:L`H/]9H0?8&J%8H='`X'[@?N!XX'[@>/'`SW&``/PO.(&`X0@,`@#1P.!^ +M\<#/<8``_"\]@8#A5`T"`-'`X'X9`H`'%0*`!Q$"@`<`V<]P@`!0HR&@`0`@ +M`B*@\<#AQ<]U@`!0HS()(`*I<+AP`(42Z$HD@'//$* +M(<`/ZW+/<```AAF*(T0!I0-O^4HD``#Y!R_X*'#/<(``4*-`@".`#.K/<(`` +M$"X`@$0IO@,-X#(@0`X)\,]P@``54D0IO@,R($`.X'X`V\]QJ@#P0V6ASW(` +M`#\_1J'/<```/C\'H8H@$``(H0G8C+@)H<]P```6'`JASW```!\?"Z'/<``` +M'!8,H9'8!+@-H038#J'/<```/SX/H5"A<:'@?N!X\<#N#@_X*@X@`@#=$@L@ +M``?84@[O_QIPSW:D`+@]K!8`%L]WI0#8RSG9HKBL'A@0`=BLI_8>&!#/&!`:V/,>&!#T'A@09-C('A@0JMC)'A@0:=C,'A@0P-C- +M'A@0SW"E``@,/J`V#\__T@D@``IP&-B5'A@0SW&``)@@H:'(V`*A`*$#H<]Q +M`0`LML]P@`"H&M080`"4V`NG0=G/<*4`S'\MH,]PI``,@**@F08/^.!X\<`: +M"```/@_/_YH+``!2#H_ZT<#@?N!X\<`.#@_XSW"``/RO0"`2!@AQSW"``)`& +M(*``WL2H!-]$+CX7"B%`+@`A@'^``/RO"@FO^1S9A"X*$@`AC7^``&RCJ7#V +M"*_YBB$*`H0N`A<`(8!_@`",K1IPX@BO^9S9`"&1)``90"-AOZ$=&!2U#W60 +M`>;U!0_X\<"B#2_X1"@^!P`ACG^``/RO$*X!W_&N(:Y`KF*N`QX"$7*N5@D@ +M`!,>`A'=!0_X\VXHDPP]=`6_Y"B4`!!4(T"#/Z:!`9"X>*>!8*:D>*&&0"$/ +M!*5X`:8=Z@&!`AS$,#"[!!S$,``.!^X'CQP.'%SW"` +M``@N((`!W6!YJ7#GN">X4B```,HE(A#*(4(#RB'A`<"X$WC"N,]RIP`42`NB +M+*+/<*H`X`>SH%4$#_C@>/'`X<7/<:``R!RH@0BAL@AO^`;8.00O^*EPX'CQ +MP+X+#_@(=L]U@`!0HP"E(:58K;H+[_]YK2H,[_\#I02E!O#F#Z_^BB")#,]P +MH`!X10"`!""`#W````!!*#Z%`-GR]<]PH`"T#SR@SW*K`*#_.:('V!JB.**N +M#0`"@.8!V,!X#.!""F_Z`=F]`P_X\<`^"P_X"'?/=8``4*,8C4AV.G$:#6_Y%-G/<(``@"B2#6_Y%-G1P.!^\+$?^5[\0KT@"#E +M`8$"',0P,+L`'`0P(($$',0P8'F+<$(C02"_"76`0")`(%H-[_^*<#T"+_BB +MP/'`Z@D/^#IP6G'/=X``<+T,C\]V@`!0HZ6&AB#_`4.X#B4-D,]P@`#X+2"` +MRB5B$&!Y!-@@Z!J.@.#,)2&0'/(`V!#=&G`"N!5XQW"``(`O((`&Z2*`%>E@ +M>2IP8;WI#7600"!`(`#8&JX,CX8@_P%#N`6F&@YO^$IPS0$/^`HAP`_K"$"#_KQ +MP*(-H`+AQ<]P@`#L/[H*(`8`W<]P@``(0*X*``;/<(``)$"B"@`&SW"``*0R +MHJ#:#2_Z`]C/<(``+#:CH"4!+_BAH/'`X<7/<:``K"\<@;V!!'W/<(``>"$` +MB!,(40#/<,#?`0`/'`(@@``*X(0`#1P.!^X'C/<(``I#(`@('@`=C@?\!X\0#8&0@1`H8+X`<*V`OPSW"@`*@@#8#DX(_W$(7U +M"!Z`'@OO_\EP%14`EH"X%1T8D#D`#_A<%0000!4%$`HAP`_K@QPAB`]`(#@`=G` +M>1,(GD'/<(``N`<`@('@`-@"]`'8`=[EO_CO/'`X<7/<8``]$E`@2&!I\%&P<]Q@`"D,B*! +M1<(5)$(PSW&@`"P@L('/<0$`O!9`P0'90<%"P1#90\%$P$6"`-@,V0ASF'"X +M<``EAQ\```!]@@XO_=APW0;O]Z?`\<#B"4_^SW*@`,`O`-F(&D``$X*+N!.B +MSW"``!P8`9`0N$4@``_`&@``SW"``+@U8@_O^2"@T<#@?N!X\<`F#N_W`-F; +MN<]PH`#0&S&@4@R@``#>)>C/<(``]!8Q@,]U@`#`(`SISW"?`+C_/:`DA0'A +ML[FUN;BY)*4VH,]P@`"X!R"`SW"``.0U\"!``$!X`(4/"%X$SW"?`+C_W:`Q +M!L_WX'CQP+8-S_<:<,]P@`#`SW&``#PB4R`-`('E`=T@@<01`0;`?3L) +M7@'/<8``'!AAD<]Q@`"\!\"!/.-Y9F3A'PD$!`HAP`_K0'8$0A1`2"%8'D"V!L(D`!`%@00"B'` +M#^MRBB","(W;:0`O^;ASBB`0`1&F$(8!"!\`%(:KN!2FSW"``"P<`(""X!+8 +MP"@B!LH@(0#/(&$&&:;/<:``R!\8$0"&H;@8&1B`BB`0`!&A"=@(N`^A$X:I +MN!.FSW"``-RP!X"#X,P@XH$']$`H@""?N(@>`!":"``&SW"``!`X-03O]^"@ +MX'CQP`T)40!B#```!/`^#```T<#@?O'`_@F@`.'%]@DO^AK8SW"``+PU`)#/ +M!X\"\_WSW6@`,`O%X4:A<]VH`#( +M'X@5`!`'V!D>&)`!V`AQ"'((<_X(+_^8<,H-[_Y4V!4('P'/<(``;`8@@,]P +MGP"X_SV@@!4/$"*_7@[@!>EPSW&``)@[$H'X8!*A`-B('0`0"=@(N`ZF60// +M]_'`\@KO]P#9SW6?`+C_787/=H``;`9`ICVE'-D5\,]SH`#(.S:#1"$"!S:# +MAB'_""5Z-H.&(?\(17G/#&`&`-C/<8``N`<@@8H@3`86#6`` +M`]IJ#F```]C6"X_\"-AR#"``BB'_#P'8P0+/]^!X\<#/<(``N`<`@!,(T0#* +M#<_\W@G/_SH/``#1P.!^X'CQP"H*[_=*)$!Q`-[/=8``+#9`)0,@Z@!!>[ +MYO'F\>'%SW*``"PV1!*``$`B`PPG"%$`2B1`<0#9J"```_`C30#/<(``R#4U +M>`'AH*`O>0#81!H"`.!_P<7@>`#:SW&``&0V3ZD!V`VI3JE,J5"I4:E2J5.I +MX']4J>!X\<#Z"._W`=K/<8``/")C@7B+.0L1`0"!SW&``*0RQ!``!B6X4B`` +M`"&!P+@!VH#ASW&``+#')H'`>H#AS"`A@,PB(H!\\H#P$0@>`,]P@`#$L`"( +M"0A1`)AR!/!*)```SW"@`"P@<(#/=H``+#9%AJ:&`B.`@`#:RB)O``(C3X,` +MW2&B`-G/<(``W+`PH"6`X'\Q +MH/'`;@^/]\]U@`"D,B&%)7@!I<]Q@`#`>(-(`#)<`7PU@I@``.%/0>O]Z'`\<#*#J_WN'#&#>__*'"` +MX`#9RB!!`"?RSW:?`+C_'8;/=8``;`8`I3VF'-D5\,]PH`#(.S:`5H"&(?\( +MAB+_"$5Y5H"&(O\(17G/@SW&``+@'((&*($8`N@A@``+:$@I@``+8`@W@ +M!`+8Z@R/^0CPSW"``*0R^@E@``.`T<#@?O'`Z@V/]RAU/0C1`,]Q@``<&"&1 +MSW.``+P'0(,\X3IB(8-DXEEA(0E$`PHAP`_K@`V`"ISW&``+@'(('YV`?=@@\@`*ERV@A@`*EPSW"``*0R +MX@NO_Z.@U@N/_U[PSW&``&RP!(D="%$`!8D5"%$`SW```/__X@WO_P#9G0@0 +M`,]Q@``\(@"!Q!``!@T(7@$#@1B(&P@1`<]Q@`"X!R"!BB#$!!X/(``"V@+8 +M-/"V"T``:0B$#P``%`3/<(``I#(`@`T(40">"X_Y*.@`V<]PH``L(+"`SW`! +M`.@*0,`!V$'`0L!#P43!!MD(<@#;F'.X02O]Z7`X'C@?N!X\<#V"Z_W`-G/``+#'`B8-$,]P@`#$L.6'8X`%+_X0-W4!VZ"(PB/.`*7! +M"PU0$`.((PA1`,]P@`"D,B.@SW&``+@'(('/V$8.(```V@#81_#/=8``/"(` +MA<00``:'"%X!@PM1``.%&(A["-$`SW"``*0R`8"WZ,]P@`"4(`"0@>`!V,!X +M#+A;"(`/````$+""SW`!`+P60,`!V$'`0L$1V$/``-B,N$3`*'`,V0':"'.8 +M<+AP`"6''P```'U""^_\V'#/<(``+#;/<8``N`<@@`0.%&(@3"!`!SW"``-RP!X`1"-X` +MSW"``*0R`X`,\,]Q@`"X!R"!BB!)!SX-(``"V@+8D@X```$#C_?QP(8*C_?/ +M6EXZ:@N`2ER@FO^0#8&_!&"._Y!]@7\`7K +M!H8">2<)4@!3((#!!*8-],]Q@`"X!R"!BB!+`H8,(``"VMH-(``"V#D"C_?@ +M>/'`X<6AP8H@_P\Z#"``0,!A"%$`SW"``#PB`(#$$``&(PA>`<]P@`"4(`"0 +MSW&``+@'@>`!V,!X#+@E"(`/````$,]Q@`"X!R"!BB!%!R8,(```VGH-(``` +MV#GP((&*($4($@P@``7:9@T@``78+_#/<(``N#5`@`#9"PI1`""@)?#/<<#? +M`0#/<*``K"\\H,]P`(#__W(*[_\!V1?H9@C@!(MP"B4`D!'RSW&``+@'((&* +M(,8!O@L@``/:$@T@``/8J7`>"^__`,%Y`:_WH<#QP+APSW"``+@'`!`$`,]Q +M@`#H,4`L@``4>!4@0`$`814(D0(*(<`/ZW**(,T`Z02O^*#;R@P``-'`X'[@ +M?N!X\<#AQ<]P@``\(@.`&(@?"!$!"B'`#^MRBB!-`8HCA`U*)```L02O^+AS +MV@\```AUSW```+_?P@GO_P#9"PA1`(PE$)40]V(/K_P!V,]Q@`"X!R"!BB!% +M`@H+(```VF(,(```V,T`C_?@?N!X\!AJ&4B````L('@``0'@":)Z"(_X +M7@[/_1L(4```A1,(D`#/<(``N`<`@(/@R`S!_S$`C_?@>/'`O@]/]\]P@`"D +M,@"`+0A0`,]U@`"X!R"%@.',(>*!S"$B@@CRBB#1``#>-@H@`,ERP*7J"H_\ +M\0=/]PHD`(#QP`SR"B'`#^MRBB!-`HHCC@V%`Z_XN'-Z#D__9@[O_P+8SW`! +M`+P63@^O_`'9T<#@?N!X\#(_YSW"``$`92B0````8``'/<(`` +M/"(#@!B(%P@1`0HAP`_K/'`X<7:"Z_Y"-C/=8``N`<`A8?@S"`B@C?RSW&` +M`"PV08$'ZL]RH``L(%""0*'/`!V,!X#+@C"($/````$,]P@`"D,@&`B>B*((4&1@@@``+:F@D@ +M``+8"09/]^!XSW*``"PV`8(`V87H`X*`X`+R`=E3((#!!*(!VL(B@0``V(#A +MS"(A@`+R`=C@?P]XX<7AQL]U@`"T,L`5`Q83"]4/TFO4?KYF`*8AID*F`6O% +MN,`=&!#!QN!_P<7/<8``9#8-B3,(40#/<(``O#4`D.FXT2"B@@CT`-@.J0VI +MN0.O_P^I`=@.J0^)0B``@-4"K__*(&(`X'[QP/(,3_$!VA-O#/<8``I#*@@2D-41#/=H``;+`DC@\)40`ECH'A`=D" +M\@#9@.'*(8(/```0)P/T(H//=H``$#8@IBD-41#/=8``;+`DC0\)40`EC8'A +M`=D"\@#9@.'*(8(/```0)P/T(8//=8``+#8GI:EQSW6@`"P@T(7E@0(FS1,) +M#=\7Q:'F@0(FS1,)#=\7QJ$H@X;ISW&``+#'*)$CHB6XP+@J"*_Y`]EI!$_W +M\<#/<8``N`<`$00`N'#/!X\<`&"T_WSW6@`,`O.H7/!_N,(4?"9\"0!4$$$P5!1`*(<`/ZW**($P)Q09O^(HCA083"%$`BB`0`1&E +M*@X@!PK8BB`0`!*E'@X@!P78SW"``/0M((!@>/'`SW````@!\]P +MH``L(!"`!-E\VCW;0,"+<.H.(`07NZ'`T<#@?N!^X'C@?P'8X'[@>/'`X<7/ +M<0,`0`W/<*``J"`MH,]QH`#`+Q2!\+@4@0SR!""`#P@```#7<`@````!V,!X +M!_"&('\/@N`!V,!XP0@1`!41`(:@N!49&(`1\,]PH`"H(`V`Y.#/=:``K"^. +M]QR%D0A?!@QTA"3"GT+TI@EO_UK8;>A$\(H@B0,."R_^BB')"<]QH`#4"SN! +M_@HO_HH@B0,L8((``( +M@\O_8MP@N"*(/\/#/+/<(`` +M^$`#@""``,`B>(#@RB!,`^$`;_>AP.!X\<"AP0#80,#/<(``Q+`AB(MP)PE1 +M`,]QH``L(#"!SW*``"PV2()">0\.17!.```@5@\/_0/P-@\/_1$(D0"*(/\/ +MH<#1P.!^SW"``!A!`X`@@`#`(GB`X,H@+`#S\>!XX<7/<8``X"`D@2"!SW.` +M`!A!0X/5N:""1H.*(/\/@.(%\@*"HGA((```"2!``&JX2"```.!_P<7/<8`` +M&$$+@4"`#H&`X,H@@0______"O("@$)X2"```)D@!@!((```X'[@>/'`D@\/ +M]Z'!"'?/<*``+"!`$!(`SW"``.2R7X``W40G`1.(X4$J@`$:`!B2PA>`\]P@`#DLI@0@0`2:19X&F(K#QX3`(*( +MN`"B`=@/JL]P@``\(@&`P!``!A$@0(#,(:*#4`X"!P?P6@X@!Z^J@.!(#@(' +MSW"``+@'(("'XG/<8``;+`*@0'@"J$:\$86@!`M +M"%$`SW#M_KZZ0,"+<`39?=H]VY(+(`07NT8>0A-%'D(3%@UO_T<>0A-B#`_Y +M!X8FAD)P`B!"``D*WP<&IDP6@!`-"%$`3!Y"$P+P`*:-!B_WH<#@>/'`-@X/ +M]PAVSW"@`"P@\(`=#O(1`-T*(<`/ZW**(`T"BB,)`YAU+0)O^+AS-PF0`4X. +MC__/<(``+#;HH,]P@`"X!P"`@.#,(.*!"?+/<(``I#(!@(#@L`L!^<]P@`"\ +M-:F@SW"``!`VSW&``*0R(('P((`#^&`/"5$`SW&``+PUJ:'/0"!%PB%`,]P@``\(@"`Q!``!@<(7@%`H>!^\<`F +M#2_W`-G/"SW>G`!1(QZ?(@@]XT*?C@L]VI`"X/9L>V!/D@J8>V!/E@I(>V!-& +M@J,>F!#/``H!"J1S@5GA$B$FI!8C@?PJIX'[@>/'`)@P/]\]QIP`42`#=J*$' +M@<]V@`#H?0>F$('/ASW#S#__\$*&@V+:AFKCU&A@`SW&D`+@] +MFQ$`!L]W@`#8(`.FIA$`!@2FDA$`!@6FHQ$`!@:F_]B;&5@#IAD8`)(9&`"C +M&1@`SW&D`.S_SW```/__IZ$&H0"'`>``IQ4(40`!V<]PH`#('#&@*@_@!BAP +M!-A^""_X0"8!$@W8<@@O^$`F@1+/<"@``@'/<:``["<&H8H@C0`&H0"'0B!` +M@`"G!?3/<*``R!RQH+T##_?@>/'`3@L/]U$@P($-$@\VSW.``(BQ`Q(--L]Q +M@`"8LO1[$8L0$X0`$O(!X`AR,A6%$&>1`AD"`<]V00"#`&:QSW.``)0\`ZD1 +M\$`D0@`Q%8400JG`$P,!`ZG/=B$`@@!FL<]S@`"8/!,-A0#$H0"#`>``HP2! +M4_#/!<]P@`"(P79X`8@#\$AP`"2/#X``B,%V?^2/"";.$P@F`!"@<$D@ +MS@,6:]5XSW:```C#`&;/=H``B,)V?F&&SW:``#PBQ(;8AL5[!".##P````AF +M>`+P`X4"H9@5@!!HB0T+``!$J6#8&+@$\`#8G;@$H9T"#_?@>/'`X<4#R*00 +M``!1(`"`SW"``#PB!(`$\AN0`_`:D(8,@`6[Z,]PH``4!`/9(Z`@V`P:'##/ +M<8``'#L6@0'@%J$#R`#:F!`!`*00`P"4&$``GA`!`:R[DAA$`+X0`0&MNX`0 +M#0&D&,``D!A$`'X0`0&`&(0`/66P$`$!HGDP>;`81`""$`$!?AB$`(8CY8^R +M&$0`L`Y"_1$"#_?@>/'`D@DO]PAS$(DS$8T``=I`JPT2#S;/=H``L+'N9L]R +M@`#@L4C@2Z17[4"QSW&``'Q1]"$"`,]Q@`"PEQ1Y +M0+'/<8``?$_T(0(`SW&``)"7%'E`L<]Q@`",4?0A`@#/<8``V)<4>4"QSW&` +M`&11]"$"`,]Q@`"@EQ1Y`>!`L0B%"PA>`039-*4"\-2E#P@>`0G91AU$$"[: +M!?`4V48=1!`RVENU68U983!Y1AU$$!KA.K47"!X`"MA4'000!MA6'000!]@' +M\!#85!T$$%8=A!,%V`^EQ@A@`^EP/(TH<$0=0A"&(`,`YKE8'0(0RB)!``OR +M4"'#`6]Z1!W"$%`@PP%O>%@=PA`3"5X!2'.&(P,`;WI$'<(0#0D>`:6X6!T" +M$`L)W@"DND0=@A`O#Y`0E@EO^>EP`!``(+D0``91($"`\=C`*"(!RB"!#P`` +MDP#`*"$!A!T`$!C8C;@3I0B%42#`@,]P@``\(@7RMA"``(FX`_"=$(``$J7/ +M<*``K"\9@,]Q@`"(*#"XP+CB#:`&!:$(A00@OH\`!@``"_(VN,"X&W@!X%H= +M!!`"V!JE`_!:'803`-@7I1BE-@JO_>EP*(4!VDAS02D`!36Y4B```%(A`0#` +MN,"YE@PO_IAR)0?/]O'`P@[O]@?8SW:@`,@?2!X8D,]W@``\(B.'SW6L`-0! +M&H%,'AB0@N`"V,H@X@#0'0"0BB`$``^F1A$``;`>`!!&$0`!M!X`$!_8"+@. +MI@B!42``@`#8B[@*\A"F/@T/^<]PH`"D,`&`A+@*\!&F,@T/^<]PH`"D,`&` +MI+C/<:``I#`!H<]P@`#X00"`%0@>`(8@_PXBN!2XSW&``.0%"Z&J#\_XD@@` +M`>H/``-J"$`#SW```%556AX8D`'961Y8D,]PI@`H`"^@`X=:$`$!SW"F`.@' +M)J!V#P_]`X=>"B`$#9``V(P=&)`'V(T=&)``V(L=&)#/<(``^"T@@&!Y!-@- +MZ,]Q@``L&AJ!.X$D>`\(7@1N#V```M@$\&8-8`4!V,]RH`#$)P\2`(9CAT0@ +M`0(;@P\:&(`/$@"&H[@/&AB`#Q(`A@5Y#QI8@#R#SW"@`#`0)*#/<(``Y+(0 +M>(\:&(#/<(``*(S/<8``*)P0>!"Y)7B0&AB`BB`$`)(:&(`=@T`:`(#/<(`` +M?"%3&AB`#Q(`AI^X#QH8@`#8$!H`@!Z#'!H8@(4%S_;@?N!X\<#/<(``P'T8 +M$`0`"B'`#^MRSW```.4.WML9`2_X2B4``.!XSW&``,!]!8'@?P:A@.#QP#38 +M!_1V#\_]4"!!!`7P;@_/_4\@002F#^_]--C1P.!^@.#QP/38"/12#\_]4"`! +M`/38!_!&#\_]"''TV("Y?@_/_='`X'[@>/'`X<6AP6AU1"+``H8B_P-&(L(# +M5'H&N31Y66$5><=Q@`!X@HMP".$V#>_V!-JI<(MQ+@WO]@3:`-C9!._VH<#Q +MP%H,S_:AP0AU`"2.`&)^`B9.$:!R8GH"(@*!`-A`P`WR+'Z+=B]P2'%*#R_\ +MR7)V"J`!R7``P`)]J7"%!._VH<#@>,]PH``L(!"`SW*``&RP!:+/<(``E)8( +M@`#;&0C>`02*"PA1`"6*"0E0``';((((ZP""@>#,("*`$O(/\!#I#PA1``6* +M@>`!V`+R`-B`X#@"PO@-"5```MC@?P"BX'[@>/'`N@O/]L]V@`#8(`"&`>`` +MI@#=%0A1``'9SW"@`,@<,:`J#Z`&*'#/<(``7`<@D(:Y$+D%(8(/``#"$L]Q +MH`#L)T:A`9`0N`4@@`\```(3!J$`AD(@0(``I@;TSW"@`,@/'`X<4*)0"`SW&``$!&`!$$`"_RSW"D`+@]`-LU +M#!$`FQ`-!L]R@`!$1J"BIA`-!L]R@`!(1J"BDA`-!L]R@``X1J"BHQ`-!L]R +M@``\1J"BFQC8`/_:IAB8`)(8F`"C&)@``=K/<*``M`]_V`-C/=8``^"T@A4!Y)P@1`\]V@```+B"& +M8'D"V(OH((9@>0/8A^CF"^_]4-@+")X!`-@"\`'8+R$'(,]P@``L.,]W@``0 +M+EX*K_H`I\]Q@`"0/!2!`>`4H<]Q@`#8(`"!`>``H14(40`!V,]QH`#('!&A +ME@R`!L]Q@`#\+P2!*PA1`":!SW:@`.PG8'D`V,]P@`!0HQB(E^C/<`$`!@$& +MIL]P$@`&!!;P"B'`#^MRSW```(<9BB/%"4HD``#5!._W"B4``<]P`0`'`0:F +MSW`2``<$!J;/<(``4*,@@`.`*^G@AT0HO@/&V)*X!J8@A2=W8'D`V',($`,@ +MA6!Y`-AG"!`$((5@>0#87PA0!""%8'D`V%,(D`3/<#D``C,&IL]P.0""3`:F +MSW`Y``)F!J;'V)6X&/!$*+X#`"&/?X``"%+'V)*X!J;/<````C,&IL]P``"" +M3`:FSW````)F!J;&V)6X!J;Z"X_^SW"``%"C&(C/<8``4*,V">`#(($O"1`@ +MSW````)N!J;/<,$`0FX&IL]P`P#";@:FSW`V`$*7!J;/<`(`0FL&IL]P$`"' +M<@:F!8\0N`4@@`\``$)P!J8$CQ"X!2"`#P``@G`&I@./$+@%((`/``#"<`:F +M`H\0N`4@@`\```)Q!J8)CQ"X!2"`#P``0G$&I@B/$+@%((`/``""<0:F!X\0 +MN`4@@`\``,)Q!J8&CQ"X!2"`#P```G(&I@&/$+@%((`/``!"<@:F"X\0N`4@ +M@`\``()S!J8*CQ"X!2"`#P``PG,&IB"%8'D`V"4($`,@A6!Y`-@9"!`$((5@ +M>0#8$0A0!""%8'D`V!,(D00,CQ"X!2"`#P``PG\&IL]P`0!&:@:FSW>@`,@? +MI!<0$!4)$"#/<%``QG,&IL]P(`#'0#8-0@0`R"%8'D`V"D($`0@ +MA6!Y`-@A"%`$((5@>0#8%0B0!,]P@``&=`:FSW"```=T!J;/<(``QG,&IL]P +M0`!"=`:FSW"``,=S!J;/<`(`1FH&IL]P$`#&:@:FSW"``%"C6(C/<8``4*,` +MB"2)@.(!VL!ZSW.``%"C4@L@!GF+)-@8V?H)H`8SVB\(4`#/<(``D#Q0$`0` +MSW"``%"C#!`%``HAP`_K*(P<'#PD0(,]P!@!":P:FSW`0 +M`,=J!J;/*(\<,I!<` +M$,]Q@`"0/`(@``03H<]P`@!':@:F((5@>0#8+0@0`R"%8'D`V"$($`0@A6!Y +M`-@9"%`$((5@>0#8#0B0!,]P90#";@:FSW"``-@@`(#/<8``V"!"($"``*$$ +M]`#841\8D*$%C_;QP#8-C_;/<(``@"\4@(#@B_)B":_^!]AZ<,]P@`!PO0R( +MAB#_`4.X8;B&X/0`#0#/=H``4*,DAL]R@`"`H3,F`'"``%1(0"(1"P2Y-'E` +M(A`*0"(2!D`B#PA`(@T$.F)`)P%R%'D`><]Q@``H,4AP5?#/<8``2#$$:E'P +MSW&``&@Q0"(``DOP0"(``\]Q@``H,1X(K_X`V@2&SW&``$@Q!+@4>+A@._!` +M(@`'SW&``"@Q_@]O_@#:!(;/<8``:#$$N!1X^&`K\$`B``7/<8``2#'>#V_^ +M`-H$AL]Q@`!H,02X%'A"`)PJ@]O_@#:!(;/<8``:#$$N!1X(G"6#V_^`=I^#V_^:G!I!(_VX'CQP,]P +M@`"`+P^`$>C/<(``4*,$@,]Q@`#0H@*X%'@X8,]Q@`"(,9()C_[1P.!^X'CQ +MP.X+K_9$VL]P@`#$4<]Q@``TL2H((`0`W@+=%@@@`,EP8;WY#760`>8Q!(_V +MX'CQP+8+K_8`VL]Q@``\(A5Y8($$N``@D`^``$Q.N1N8``"!!!`/(,]V@`#$ +M4;X8V`.@@4*&BB`'#V&&'67P'8`0[!W`$""!1H;/=8``-+%EACA@^!B``!8F +MP1/T&,``%B7`$P3@!.$:#*_V"-H,$``@%GX6?01M)&X&#*_V"-J=`X_VX'CQ +MP#(+K_82V:G!"'8B#N`%BW!*)`!Q`-JH((`"%B2`,"B("PF2`&&Y**@!X@+" +M```$Q..&#L&,``\!B```"%!L(%PSA@^!B` +M`(/!]!C```07$!#/<(``-+$6(``$!."*"Z_V"-KCA\]P@``TL8?!]G@$X'8+ +MK_8(V@#`((6Y&1@`((6Y$0`&%0@>`+X9V`,@A;\1``:`N`CPOAD8!""%OQ$` +M!J"XH@HO_;\9&`"$Z)(*#_T$Z`#8`_`!V!!V1`QA!LH@@0,`A;D0`091(4"` +M\=G`*2(!RB&!#P``DP#`*2$!(@MO^X080`"9`J_VJ<#@>/'`-@J/]L]V@``L +M1L]U@`#,!Q+I((:-Z0"E7@CO^`[8J@KO_HH@$``!V`"F#O`@A25X"_`>#Z_X +M#MAV"N_^BB`0``#8`*8`I5D"C_;QP-H)C_;/<8``X"T`@:"X`*$6"R_\`=C/ +M<(``2)8`$`0`3"3`@,HAS0_*(LT'RB"-#P``@0S*(XT/``#:`,0%K??*)>T` +MIPQT``#=%&T`(($/@`!(E@>1QI'DD1"X!7X%D4.1$+@%?P*1$+I%>!IPU@_O +M]\EQ6G#/<(``Q%CP($$#1"T^%PHA0"X`(8!_@`#$/B"@Q@FO^PIP"'$`(8`O +M@`"X/HX+@`0'#L03F._/<(``N%CP($$#1"T^%R]V`"&`?X``;#\@H)()K_M* +M<`AQ`":`'X``8#]:"X`$SW"``$B6`(`!Y6D-!)!-`8_VX'C@?N!XSW&``#`N +MSW"``/POX'\BH/P<"+3QP!IPSW"````N((!@>0'8@>#*(<(/RB+"!\H@@@\` +M`)X9RB."#P``J`'*)&(`S`2B]\HE(@`*<-'`X'\$%!`T\<"B"(_V`-W/=H`` +M>+'/<(```"X@@*"F8'D!V('@RB'"#\HBP@?*(((/``"8&.!^X'CQP,]P@`"800"`?0A4`<]PH`"L +M+QJ`4B```&T('P#/<8``Z'T+@0'@"Z'/<(``\"T`@$!X]@\``,]P@`#L+0"` +M0'@V"<``W@\/_EX)S_P&\!X,+_V*((D,SW"@`'A%`(`$((`/<````$$H/H7R +M]<]P@``\(B.`2($TD5,B``#N#Z`"`=NZ#*_X$MC1P.!^X'CQP.'%M,$&\-8+ +M+_V*((D,SW6@`+1'<14`E@0@@`]P````02@^A?+UBB#_#V\=&)!K'1B0>@@O +M^8MPT@W/_`_H;Q4$EFL5!98*(<`/ZW+/<```L1-5`Z_W--LR"\_XK@]``Z4' +M;_:TP$"(`=@`H6BZ`KI5>L=R@`"`+V."8Z%A@F&A8H)BH62"9*'@?P"BX'CQ +MP/X.3_;/=X``G$$&AP.`SW6``.A]((!)A0`B@`\M`,#&`GF!"7(`H<'/=H`` +MV"``A@'@`*87"%$``=G/<*``R!PQH$X*8`8H<(MQH@MO]T+8`(9"($"``*8' +M]`#9SW"@`,@<,:``%`0Q!"2^CP``%__*(<(/RB+"!\H@@@\``*83RB,B#)0" +MHO?*)2(``(6"N'H.(```I2(((``!V`"%HK@`I2F%QW$M`,#&G@B@!.EPM09O +M]J'`\<`>#F_V`-K/<8``_$D`@;O!5\`$B4HD`')XP,]P@``\(@.`"(#`N$#` +M7Q2`,,]Q@``L&D'`.,!"P%X4@#!#P!J!.X$$>3&YP+FH((`"`-L`)(`P9!C" +M``'B3WK/<(``Z'UBD,]P@`"\!D"08PN!`,]S@`!PO0Z+SW6``.A]AB#_`2@5 +MC1!#N`(@0(.OBW"+RB!B`(8E_Q';;<]U@`#H?2D5C1"&(_\!#B6-D\HE8A"[ +M?:5XNVO/@4A`$7`SW"@`+1' +M1Q``AH#@S"$B@&(%`0#/<(``Z'T`$`0`421`@,HAP0_*(L$'RB"!#P``JA/* +M(X$/``!V`%`!H??*)2$`SW&``'"]#HG/``IA,(40#/<:``R!P!V!&A5@A`!C?`SW>@`.PG +M$+@%(($/``!"+2:G!2"!#P``@D8%((`/``!"8":G!J?/<`@`AQ`&IP"&0B!` +M@`"F!_3/<:``R!P`V!&A`,#/<8``0)H6>62!0('/<`\``/P*NP1[R;IE>L]S +MIP`42$VC18$A@0JZ1'C)N25X#J/.#`_^1L``P`KHBB'_#\]PH`"T1V\86(!K +M&%B``-@#V43`4<%(P,]Q@``0?@AALP@S`D?`",$%P!$@0("(`P$`!\``)`$P +M9!&!`('A>`,A`(-P`=ED&$(`!\'/<*``M$=@&%B`SW"``#PB`X`0N9NY,B"` +M#P``V`*?N8#@`=C`>`^X)7C/<:``M$=?&1B`!O`&""_]BB")#,]PH`"T1W$0 +M`(8$((`/<````$$H/H7R]0+9`-@:<`?`$2``A/H"(0!0P<]PIP`42%P8``1' +M"!`@*0A1((H@Q#:*(80X'_`<%`0P"B'`#^MRSW```*L3I-MQ!V_W2B4```HA +MP`_K!"X@;B'N(RX!J<@AD(A08`&],]RH`#('`#8$:)*)``ABG5`((`Q$'A+ +MP$`A@#$0>$S`0"A`(4W`"B:`)`'A8;T@IA4)40#/<:``R!P!V!&A,@X`!@/` +M-6T`)1<6+R?()25X$'@0N(4@B@`&IT`O@"&!N)>X`"53%@:G+R/()$`K@"&! +MN)>X!J<+P`:X@;@&IPS`!KB!N`:G`(9"($"``*8&],]QH`#('`#8$:&2P)/! +ME,*5PQH,(`16),0R-L"*Z``@@2^``.`\$(D!X`]X$*D`P`SHE@C/_!$(40`` +MV';`!,"`N`]X1,``P,]R@`!`F@.X%2``!!EB&F(,@BB!$L).P`W`MG@`()4/ +M@``@?A/`\!V`(/0=`"`)P(@B?``O(0`@!"F^(!8-K_LO<`X@@0\````!3\$3 +MP(@@?``$*'X$+W#Z#*_[#L$.(($/`````0_`"2&##P``_P$)(((/``#_`4@B +M`@!((P,`-L!4'9@@51W8(!\(40`*P1@4!#`$N4`L@`$X8+5XQW"``'R:0K!C +ML`"&`>``IA,(40#/<:``R!P!V!&AW@P`!@K!!L!`+X(A@;H$N0:X.&"U>,=P +M@`!\FB*0/'D0N25Z1J`:G#,!`+@$D!KB! +MN"5X!J<`AD(@0(``I@?TSW&@`,@<`-@1H1#!8;F`X?@$[?]`($`@$<%AN8#A +M",!6!.W_`>``A@'@`*83"%$`SW&@`,@<`=@1H?8+``;/<`@`AA`&IP"&0B!` +M@`"F!_3/<:``R!P`V!&A'@L/_P;PB@SO_(H@B0S/<*``M$=Q$`"&!""`#W`` +M``!!*#Z%\O46#(_XSW"``.A]!,$,@#A@SW&``.A]#*$-@0'@#:$5`&_VN\`` +MV<]P@``$?BRH+:C@?RZHX'[@>("XSW&@`.PG!J'@?L]P@``'(<]QH`#L)P:A +MSW"``$!_(*#@ +M>/'`SW"````N((!@>0C8$'G/<(``7`M&#`_W6@\@`P?8K@H/_NX/```J"``` +MT<#@?@AQ6(D!@`*AB.I9B8#BPB"B`,`@H0`"H>!^X'C@?N!X\<"6#@_VSW:` +M`-@@`(8!X`"F`-\5"%$``=G/<*``R!PQH`H*(`8H<,?8E+C/=:``["<&I<]P +M`P""*P:ESW`#`,)$!J7/<`,``BP&I<]P`P!"10:ESW$``,)TSW`#`,)T!J7/ +M<`,`@F\&I<]P`P"";`:EQMB0N`:E)J6R"2`&"MC/<```@FP&I:()(`8*V,]P +M```"+`:EE@D@!@K8SW```$)%!J6&"2`&"MC/<```@F\&I7H)(`8*V,]P``"" +M*P:E:@D@!@K8SW```,)$!J5>"2`&"MC/``';SW&@`.PG9J&( +MZ,]P@`#P+0"`0'A*\!6"42``@,HAP0_*(L$'RB"!#P``?QG*(X$/``"J`,HD +MP0!L`6'WRB7!`,]P$P#'`,]UH`#L)P:ESW`0``9I!J7'V)6X!J7/=H``V"`` +MA@'@`*87"%$``=G/<*``R!PQH*8((`8H<,]P``!"+0:ESW```()&!J7/<``` +M0F`&I0"&0B!`@`"F!O3/<*``R!SQH$D%#_;QP.(,#_;/<(``^"T@@*'!8'D$ +MV#+HSW:``-@@`(8`W0'@`!Q$,P"F%0A1``'9SW"@`,@<,:`^""`&*'"+<9() +M+_<`V`"&0B!`@`"F!?3/<*``R!RQH``4`3'/=8``!"Z&(?\,0(5"N6!Z`M@` +M%`$Q0(4#V&!ZP;G5!"_VH<#@>/'`6@PO]@/8SW:``/@M((;/=8``^#A@>:+! +M!N@@AF!Y!-B&Z$D#(`!(%000`]@:<,]WIP`42,]VH`#L)TX(+_X%V`ZESW"` +M`-@@`(`!X,]Q@`#8(`"A%PA1``'9SW"@`,@<,:".#^`%*'`#V.((+_>I<038 +MV@@O]R)M!=C2""_W)&T+V,H(+_E%X<(I1:' +M":7/<*L`H/\8@`NESW"K`*#_&8`,I<]PJP"@_QJ`#:7/<`4`Q@,&IL;8D+@& +MIL]P+``"`0:FSW!:`$(!!J:*((L`!J;/<$``APT&IL]PT0#"#0:FSW#```<. +M!J;/<(``V"`@@!$)40#/%`GE:#"_V+W`!PD_@SW&``)1_%*57H1BASW!``(<- +M!J;/3EA\@LO]C5YX+@<>,`@ +M8@""(,0"SW&``)1_$J43I1:ASW"``-@@`(`!P@'@5:'/<8``V"``H14(40#/ +M<:``R!P!V!&AA@W`!0&5$+B%((0`!J8"E1"XA2"%``:F`Y40N(4@BP`&I@25 +M$+B%((\`!J8%E1"X!2"`#P``@@T&I@:5$+@%((`/``#"#0:F!Y40N`4@@`\` +M``(.!J;/<(``V"``@,]Q@`#8($(@0(``H0?TSW&@`,@<`-@1H02%*X4(IP6% +M#:<&A0ZG"(47IPF%%J?/<*L`H/\XH"R%.:`MA3J@K@SO_0Z%2!4$$(PD@H!% +M]HPD/X$-]M8,X`4*V$H.@`-"($`@@.`"!-'`X'[@>,]R@`!T!QD('@"`X5'8P"@B!,H@803` +M*"$$`_``V.!_`*+@>/'`@@@O]@38SW6``/@M((5@>:'!@>"\]#8(K_R*((0( +M@>"V]`#8`!P$,,]W@`#8(`"'`>``IQ<(40`!V<]PH`#('#&@U@O@!2APBW8` +MV"H-[_;)<2"%8'D`V``4!3&H<88A_`\C"!$$C"$#@![R"B'`#^MRSW```+X9 +MBB,$#"4$+_>*)(,/P.'*(<(/RB+"!\H@@@\``)D9RB."#P``-@$`!"+WRB1B +M`,]U@``$+D"%`-A@>D:Y`!0`,4"%1"`!#`'88'I$N0'8K@SO]LEQ0(4(V&!Z +M`!0!,0`4!3%,)0"`S"5B@,PEHH#*(<(/RB+"!\H@@@\``)H9RB."#P``10&@ +M`R+WRB1B``+8;@SO]LEQ`!0`,4"%4R!0``388'H*<2$(T"``%`4Q"B'`#^MR +MSW```)L9BB.%`VD#+_=*)$``$M@V#._VR7%`A0`4#C$%V,&^8'K)<2,.T!`` +M%`4Q"B'`#^MRSW```)P9BB-%!34#+_=*)$```(="($"``*<7],]QH`#('`#8 +M$:$1\,]U@``$+D"%`=A@>@AQ0(4$V&!Z`]E`A0788'H#V3T'[_6AP/'`V@[/ +M]<]U@`#8(`"%`>``I0#>%0A1``'9SW"@`,@<,:!*"N`%*'#/<(``!B'/<:`` +M["<&H<]P@`!&.@:ASW"``,93!J'/<(``QB0&H<]P@``&/@:ASW"``(97!J$` +MA4(@0(``I0;TSW"@`,@!_(Z#QP%(. +MS_7/=H``V"``A@'@`*8`W14(40`!V<]PH`#('#&@P@G@!2APSW```,(LSW&@ +M`.PG!J'/<````D8&H<]P``#"7P:A`(9"($"``*8&],]PH`#('+&@90;/]?'` +M$@LO^!;8O@V``\]Q@``\(@"!Q!``!@\(7P$!@<00``8-"%X!L@MO^!/8SW"` +M`.0M((!@>0O8T<#@?O'`?@UO_(H@B`4.Z.H+;_T`V,]P@`#X+2"`8'D$V(#@ +M'`P"_]'`X'[/<(``/"(#@`B`SW&``'BQ"0@>``&)`_`"B>!_`*G@>/'`N'&- +MZ`HAP`_K@@;0/P($4`!"6"#P$``,`NNF5Z"PN!``'AT<#@?@HAP`_K +M<]P```BTC1XSW&``$O, +M#_#/<```(]+/<8``3LP'\,]P```DTL]Q@`!1S"G:$KKP(@``#B""#P`!``!` +MPHMPK@A@`P/:H<#1P.!^X'CQP$8,S_4#R)00``#/=H``V"`$()`/`0``P`"& +M02B0(P'@`*8`W1<(40`!V<]PH`#('#&@J@^@!2APSW$D``A@;A$\,]U@``\(J.% +MJ(45#1X0SW6```8ZIJ'/=0,`@@()\,]U@`#&/::ASW4"`(("IJ'/=00`QC&F +MH<]U2@!"`2#PSW6``#PBHX6HA1<-'A#/=8``AE.FH<]U`P""`@CPSW6``$97 +MIJ'/=0(`@@*FH<]U!`#&,::ASW5,`$(!IJ'/<:<`%$AWH8"X&J)E`L_UX'CQ +MP.8)S_4#R`'=SW:G`!1(E!```*BF!""`#P$``,#&#N__+KC_V)NXSW*G`)A' +M'*+/<8``A`8`@0#?@.#*(<(/RB+"!\H@@@\``*P9RB."#P``OP#*),(#J`7B +M]LHEP@/VIKJBZ0'O]:"AX'CQP'8)S_7/<*8`G#\9@*T('@#/=H``D`8`AD:` +MH!(`!B\H`0!.(($'02G0`!$(U2!(<(`@"@`R(``$D.@*(<`/ZW+/<```K1F* +M(TL"BB2##T4%[_8*)0`$SW6``$#,0"7`$CH,+_<)V0#8N@FO_P\@``2`X`#8 +M#R``!`7T)@S/_P/PL@W/_P/(N1"``!MX@+A`A@JM)H*6(4$#`"$`!!B(C"## +MCP)Q!?)AN`]X&*DF@J`1``:?&1@`R@O/_QT!S_7QP,]P@```+B"`8'D!V('@ +MRB'"#\HBP@?*(((/``"=&_U`]AAO8PE +M_Y_S]0HAP`_KK; +MN0/O]IAS`>$@IA4)40`!V<]PH`#('#&@&@N@!2APSW$&``)USW"@`.PG)J`` +MAD(@0(``I@GTSW"@`,@O +M]:'`\0/8@.`8#T(#SW"``(@H!(`9")X`SW&``#PB +M38$^D5,B```N#^`!`=O1P.!^\<#AQ<]U@`#P.`"%&P@?`)X*P`(>#L_[Y@F/ +M^0(.S_\`A8"X`*4I!X_UX'CQP'(.+_R*(`0"$>C:#8__Q@_/_\]P@`#X+2"` +M8'D$V`7HI@]/_PX(``#1P.!^X'CQP'X.C_7/=8``\#@`A3D(7P#/<(``^"T@ +M@&!Y!-@4Z"(.+_SBV!#H?@JO_0?87@M@`PAV5@T/_YH)K_W)<`"%@;@`I:4& +MC_7@?N!X\<`N#H_UI!`!`!4)'@:V$`$!SW"@`)@#/J">\``6#4&\L``6`D%= +ML``6#D#/H``6`D%`&(0``!8"0%&@`!8"04@8A`!$)0(3-0H0`1C;I1 +M)@"0T2$B@A7RT(BHN<]R@`!(OZ080``"OM9^PF(+"IX'B[FD&$```-I:H%N@ +MYO$`%@)`6J``%@)`6Z`(VG00#@&^$`\!PG]B?T)_N!""`)BYI!A``,]QH`"8 +M`T)_>F)0>G(8A`"Z$`(!\']P&,0#I7I/4$K_7`I?'`SW"``-P2#MD!VNH*(```V\]P +M@``4$PG9`=K:"B``2'//<(``"!(JV0#:R@H@``#;SW"``+`2"]D`VKH*(``! +MV]'`X'[@>/'`!-@>"^_[`=G/<(``B3D`B,]Q@`"*.9H+(``@B='`X'[@>,]P +M@`"L1O$%@`/@>/'`ANCN"````-DBH-'`X'[QP`(,C_7B#`_\SW:``'@'9M@B +M;@':%@_O_$ASB^@*(<`/ZW+/<```MA39VXHD@0DY\`(6!1%,)0"`S"6"CP`` +M__\-]`HAP`_KO]HHD@0EGV,EQ`=K*#N_\2'.,Z`HAP`_K +M`0>*8.[_Q(@[O_$ASC.@*(<`/ZW*AEL]P``"Z +M%.7;0"6$$.OQG0./]<]QH`!@'1*Q%)'@?O'`N'$U"%$`"0U2`!D-T@,*(<`/ +MZW*GV`6XG-L=!Z_V2B0``$`M@``4>$(@`0//<(``/'`D@JO]=APSW:` +M`+X&`([/=8``O`9>#^__((TAB!$)W@`!V<]P@`"0.6;P`H"S"!$`70D>`<]Q +M@`!H)0"51XE-"($``)9!B44(@0#/<(``P`8`B":).0D!`,]P@``\(@Z`+0A> +M`<]P@`",.4"`SW"``)`Y"NK/<:``+"`P@4)Y@F/]:'!&G`Z(MQA@SO_$AS$N@`%``Q0"J"(`0@ +M@0\```#_1[E4>C,)$"#'``SN`XJ`N`.J +M$F\4>!MB8XM88(&[8ZCDJ@/N)JH"\"6J0B1!(%4)=8!`)4`@`0&O]:'`X'CA +MQ5,@#0"@J00@@0\`!@``0B$!@`0@@`]`````RB%B`""JUW!``````=C`>`"K +MX'_!Q>!X\<",Z&X-S__/<:``+"`P@<=Q26L`TB*@T<#@?O'`=@BO]=AQ"B:` +MD(AUS",B@`;R0B8&`2\FAP$Z#>__R''/<8``-`<`H27N)(@"N31Y0X@#X0(0 +MA0`C"A\`"B'`#^MRSW```.(4BB.("4HD```]!*_V"B6``0AA&PA?``HAP`_K +MSW>@`+0/$<]Q@`!H)0>))0C!`0&)4R4" +M$!D*`0`$)8T?``8``(#E`=H&B.!^X'CQP(8.3_7/<*`` +M``3,B,]P@`"0.0"(`-V,Z-X+[__)<(CHD-D#R)"YH!A``*EP$O#/<(``,!P` +MB`OHC"8"D`GTD=D#R)"YH!A```#8`O`!V*$&3_7/<8``/"+P(0$`*!&``"B! +ME0;O_P#:X'CQP!H.;_7X<,]R@`!H)<]V@`"\!@"69XK/<8``W"`M"P$`SW"` +M`+X&`)!ABAT+`0#/<(``P`8`B$:*$0H!`,]P@`#=(`"(`_``V`"I`-T^"^__ +MJ7#/<(``P`9`B,]Q@`"^!@")((Z`X@':P'KH/'`X<6AP=APBW5`)$(P0"2#,"AP?@OO_ZEQ`12`,`GH`A2` +M,`7H0B8&`2\FAP$@P`8)[__(<0$4@3`$Z0*(`_`!B.&XT2#B@`/R(P@>`0HA +MP`_K/'`X<7/<(``W+``V26@SW"``*0R(J#/<8``/"(`@<00``9W"%X!`X$8 +MB&\($`'/=8``>$``A4(@`(#*(&(`)PA1`.8,8`.I<,]Q@`!`0`"!0B``@,H@ +M8@"%Z"AP.@U@`R*%SW6``)1``(5"(`"`RB!B`"<(40"R#&`#J7#/<8``7$`` +M@4(@`(#*(&(`A>@H<`8-8`,BA2T#3_7@>.'%`-O/6"AX!W$$/`=Q!#@?\'%X'CQP*8)K_P1V+GHSW&``&@ESW"``+P& +M`)!'B54*`0#/<(``O@8`D$&)10H!`,]P@`#`!@"()HDY"0$`SW"``.`M`(": +MZ$X,0`*(Z`O(!2"`#P```#P+&A@P/@Q``HCH"\@%((`/````U`L:&#`+R)"X +M"QH8,(8+S_L#\$H.#_;1P.!^X'@`V9RYSW"@`*PO/:#@?N!X00#/^^!^X'C@ +M?N!X((``VH#A1?8!VC-Y(*"`(0&`?]S`(00#1[D@H`/J,WD@H.!^H<'QP.'% +MK,$`V4K!D-D8N4C!SW.``'BQ((,$((T/`0``P(8A_@,DN0ZY"R5`D$[`CL(6 +M\M=U````0,PE@I\```"`S"6"GP$````$]"&#`_`B@ZZXK[BPN`5Y(*(.PPC` +MBW4$(X$/`0``P"ZY0"D"!D5X2,"*(`8&22%@SW"G`(A)+Z"J#Z`# +MJ7`(W$\!;_6LP*'!\<#."&_U"'*MP0C82L"0V!BX2<#/<(``>+&@@`0AC@\! +M``#`AB7^$R2]#KT+)D"34,&0PQ;RUW8```!`S":"GP```(#,)H*?`0````3T +M`8`#\`*`KKFON;"Y)7@`HQ##"<4$(X$/`0``P"ZY0"D`!@5]2<4?"IX!"L`$ +M([Z/````&$4@P`!*P`7RA2`0`4K`)0H>`9N]SW"@`"P@!8``VP*X;KB`X,H@ +MS`#)N*5X2<`&\`D*'@*=O4G%$,"!Q4+`J7!"""```ML#R`S"SW&``"P:N1B" +M`!J!.X$D>!L('@("NL]P@`#8R51Z06#/<*<`B$DOH*8.H`.I<`C<0P!O]:W` +M\<#*#P_UH\%A@`AU0,,`V`JE;0M>`@0C@`\!``#`+KC/$T*8DDB@@!A +MNDNE$FH4>,=P@`#(RLJ`SW>``!RSQJ4+@,]V@``\(@6EPX8@P-2&]8\$?N1^ +M";Y`*0\"Y7[%>`0C@P\````097@'I0B%&.*>N`BE2Z6/\#<*G@+/<(``W$$` +M@$'`0L`A"!X"AB#_"2.X`>`5")0`"PB1``;88<`D\`?88<`B\"+`8<`>\$'# +MSW*``)`&0()&@IX2`@8K"I$!!".^CP```!@/],]R@``\(D2"2(($(KZ/``8` +M``7R`=@*I0/P"J4`V`'&00X>$D+&(L*@XLHB(0`$)H\?`0``P$$OA!-$)@\6 +M([\!YP0FCA\&````,;X`)L43SW:``'A-,B8.$0(F3A$3\%,FPA#/=X``7%%= +M>DIG!":.'P$``,`NOL]W@`!X3DNE$PL>`B#'SW:``(!-[F8"\`'> +MA"@$#@`A@'^``-#)`KI4>D=@8;Y88.:E`8`$(X,/+P``W2:[Q7M2(\,#!:5G +MI<]P@`!XL0.``-\="$X`SW"``/A!`(`5"!X`AB!_#QUX0"C/`P3P`-^/OYOO +MSW:``/@M((9@>0#8)0@0`R"&8'D`V!D($`0@AF!Y`-@1"%`$((9@>0#8"PB1 +M!!H,K_P`V`B%!7_HI34&+_6CP.!X\<#*#0_USW6``!0N`(7$D,EP[@F@`(8@ +M_`,`A!*@6`1"4V.H-X`')22R +MBB"(!<]R`/\`_SX/C_T`EO8,[_TTEI07`!#/`\]P@`!T!B"@SW"@`/PE +M$X!L@GA@#*+/<@"@"`#L<$"@;R)#`.QP0*`.'UB0I@@`!<]P``#_?\]QH``, +M)`&A&]@$H7$$+_6I<.!XX'\!V/'`X<6AP;X.K_N+<++H`!0%,!T-'@!^"``` +MSW&``.2R0X'/<8``=)=!H23P"PV>`'H.S_\>\`T-7@(*#L__&O`[#=X`"-C/ +M=:``Q"<3'1B0V@[``!T($`4"V#P=`)#/<(``Y+(C@,]P@`!TER&@&=B7"%"& +M"00O]:'`"B'`#^MR%]B,N(HCQP"9!R_VBB2##_'`X<7/<(``Y+(_@`0A@0__ +M_X\X!"6`7P``<,,]Q@`#DLA^A1"(`4\]U@`#DLD,($0(_#5Y1-@_/_YP= +M`!`3#9Y3SW"``)`B!8B8'0(0%/`5#=Y3SW"``*`E&8B8'0(0#/`#A3(.+_8D +MA9@=`A`$\`#8G!T`$)P5`!"`X,P@XH!Q\L]P@`#T%@N`#^C/"SN+6XN+@$H1:BSW"@`*@@"(`?A0\('P$+#=]2@-B8'0(0F!6` +M$$`H`083"-\!@KD="IY3N@C``1KP'X51(H#3L[@?I<4A@@\````'12$`!L]Q +M@`!PLRR)AB']#U(AP0%%N25XSW&@`(@D$*&*(-8`SW&@`,0G?AD8@,]PH`#4 +M"P':4J`$V!`9&(#/=8``Y+(?A4D(G@$4E4$(7P'/<*``+"`/@)KHK7%2#>_Y +M5B5`%8`5`!"4N(`=`!`?A9"X'Z4,\,]Q@`"@.@^!`>`/H1#9SW"@`)`C/:!U +M`B_U&=C@>/'`\@DO]0#9"'8!@,&X@^#*($$@!?(B#B``R7`:<$P@`*#$]!"& +M42"`@<#R$(;/=8``Y+(/")X#SW"``)`B!8@-\!"&#PC>`\]P@`"@)1F(!?`% +MAB:&P@P/]I@=`A"`%0`0!""^CQ!P```']*UQK@SO^58E0!41AL]Q@`!4!P"A +M02@!`U,AQ0"8%8$002@&!11I!2!$`0\)W@$>A96X'J5Y\.(+K_M/)$`"ZP@5 +M!,]Q@`#HBY@5@Q#P(0$`0"L"!H8C_0]2(\,!1;ME>L]SH`#$)T$;F(``VHRZ +M`B9/`/IBR[K7<@````A`+0\#D+]2]P4GCQ%B&]B#C"("@,?WSW&``!0\$H$! +MX!*A`-F=N47PY7EB&UB`60Z%<```P`\.(H,/````$,]R@`!(BQ9Z(((E"S4( +M!!(%``#8#R#``&&X3B,/"`$IP@-X>05Y`"W```5Z%_!"(P,(`-@/(,``8;AX +M>04A`@"*(?\/"_#/VXT2!B@`?R!,B*#J__F!```,]P +M@`!@LPR`SW&@`,@?9.`>H1#8#J$!V!49&(`!A8/H_PL>P`&%P;B#X-;T`!`` +M($'`!!0/,1"%++\&%!(Q=0B>`0W,=0C>`A"%SW:``.2R$0B>`\]P@`"0(@6( +M#O`0A1$(W@//<(``H"49B`;P!84FA;X*#_;GN)@>`A#*)F$0!O(^AI6Y/J8` +MW@2XSW&``#":1I'E>!,(@`#/`>`)H@21&PB!#P``__\`W@GP +MSW&``*`Z#8$`W@'@#:$!E9S@B/0$$!$@"!`0(,]PH`#T)@+9(Z`CA<]P@`!T +MER&@:@L@`*EP@."`]";NSW*@`,0L'!I`!,]Q@`!PLR`:``0LB4`O`Q,0N9^Y +M)7M!*@$A97DFH@T2`3<="=X"$-JKN0P:G#`-&EPPSW*``)@[)X(!X2>B#1(! +M-PT)'@,:V*RY#1I<,*4.$!#/=H``W'S@%@,0185$*SX'`"9!'D"A3)4!XT*Q +MSW*``'"SK(K@'L`0SW*``#":J*GIJ0H9A`0,&4`$1)(0&0`$$+T,OT$J`R'E +M?65]2K'/@0?P"-@,\.>ZRB(A`$#"`12#,,:ZQKMXJ5FI<0;O +M]*+`X'CQP.'%SW&``#PB(X%(@5D*'@"&(/\!SW*``'A-0[@*8@#;@.+*(<$/ +MRB+!!\H@X0?/("$#RB.!#P``;P#*),$`]`$A]LHE(0#/<*H`#%`3"K0`N8&` +MO;FA`=DEH`3PH+VYH66@+0;/]/'`M@W/]`AU#0C>``W,4R!` +M@`T2`C8=]``B@`^``!"R`=G/=H``<+T@J!&.42``@`@-8@/*($(`$8X7"%X! +MSW"``+"R`XB`X%@*H0'*("$!$-@,&APPSW&``!P[%X$!X!>A`\@-$@$VA!`" +M`<]P@``$LC5X*8!982F@&M[$\<]Q@`"@.@V!`>`-H0'9SW"``-P@`=I`J,]P +M@`!@LTZ`!H(!X`:B`_`!V0+:SW"@`/0F0Z!#A<]P@`!TEX#A0:!$"(("*07O +M],EPX'C/T0AD<(G@$,S,]Q@``D +M.C$(7@%`V`P:'#!5$0`&`-H!X%49&``-R,]Q@`"(L11Y`\A`J:H*K_^8$``` +M!_"L$0```>"L&0``SW"``-P@`=D@J,]P@`!@LRZ`!H$!X`:A`MG/<*``]"8C +MH".&SW"``'27(:!I!._TJ7#@?PC8\<`DN5,AP@#/<8``'%16>1,*$`)!D&&! +M!.)P=@!A8/H^0L>P`&%P;@C"-$`SW"``-P@`=D@J,]P +M@`!@LRZ`!H$!X`:A`-@,\`&%42``@`#8RB#A!2&%42%`@,H@H03-`\_T\ +M!\]Q@`!PO0R)3XD;"@``$8E1(,"`5`_!`0?P`-G/<(``Z)<@J$X/@`0]`\_T +MX'CQP,8*S_0(=@&`P;@`WR<(T0#/=8``Y+*/#Q$0$(9W")X!$(89")X#SW"` +M`)`B!8@2\-8.[__)<`AW[?$0AA$(W@//<(``H"49B`;P!88FAI8-S_68'0(0 +M$0C>`1Z%E;@>I1^%E[@?I8`5`!`$(+Z/$'````_TG+B`'0`0,(9N#:_Y5B5` +M%4`F`!*@'0`0`-@%M@'9SW"``-P@(*BT%0$0!H$!X`:A6!6`$)GH,@I/^P7H +M$(;MN`'8`O0`V,]Q@`!&L_0A```\E3A@8K@0N("XSW&@`/0F`Z$&\`+9SW"@ +M`/0F(Z`EAL]P@`!TER&@00+O].EP\<#.">_T`-D(=@&`P;B#X,H@02`%\OX- +M[__)$P@`*#/=8``Y++*)R(05O0PAF4)G@$\E1,)`P`E +MAL]P@`!TEP*`MPD!`!"&#PB>`\]P@`"0(@6(#?`0A@\(W@//<(``H"49B`7P +M!88FAGX,S_68'0(0@!4`$`0@OH\0<```"O1>"4_["^@0AA,(7@,!WPCP`-\8 +M\(8,#_P4\`#?,(9.#*_Y5B5`%8`5`!"H%0$0GKB`'0`00"8`$J`=`!#9"5Z" +M`=G/<(``W"`@J+05`1`&@0'@!J%8%8$0SW"@`/0FD^G/<8``1K-`+H_T"G#@>/'` +ML@C/],]P@`#T%@N`$.C/"SN+6XN+@$H1:BSW"@ +M`*@@"(#/=H``Y+(1#9Y3SW"``)`B!8@-\`\-WE//<(``H"49B`7P`X9V"^_U +M)(:8'@(0'X83"!\!#PU?4PL-7U*`V)@>`A"8%H`0&PC>`5^&/H:SNI6YE[H^ +MIE^F`-D!W1;PG!8!$"4)40`_AE$A0(+/<8``/"(C@2F!!?)$(0T$!?!$(0T" +M`_`!W039&+@E>,]QH`"()!"A'X8S")X!%)8K"%\!H@K``9'HSW"@`"P@#X`% +MZ`W,%PC>`1^&D+@?IJUQX@JO^58F0!6;[0L*GE/`!%?"&(O_`!A_KBH",+[SW"@`%`,H(#/<(``E`<$ +MV:"@SW"@`)`C/:#/<8``Y+(?@0T(WP0/@8#@`-@O\A>!BA$.`1UE!.8&\-8+ +M;_N*(-X,"0B?1/4)'L;/`%A+;G` +MN8H2``%/$H(`SW.``#PB\"-#``(E@1-">$8,K_M/DVD'C_3@>/'`X<6AP0#8 +M0,#/<8``H#H/@0'@#Z$#V<]PH`#4"S&@X'C@>.!XX'C@>.!XX'C@>.!XX'C@ +M>.!XX'C@>.!XX'@QH,]UH`#$)Q45`);/<9\`N/\6H3$5`)86H1#8$!T8D$X) +M;_N+<)?H`!0%,!L-GP`*(<`/ZW(-V(RXBB-?!Y$"[_6*)(,/!-D3'5B0&]D6 +M'5B0V0:O]*'`\<#/<(``P)=R"2_V&-G/<(``P(=F"2_V&-G1P.!^X'CQP#8. +MC_0:<,]UH`#4"Q"%`-^AP4#'(0A0``HAP`_K<@_8C+B*(Y8(BB2##RD"[_4* +M)0`$SW"@_CP"SW:?`+C_%J98'@`4SW&@`/Q$&8$$(+Z/```((`/T'8$3"-`D +MF@AO^XMP@.#*(`(@0B#!()3A;`$-`#(F07"```!(0"<``(H@Z!"(`6IKH+P```V2AP2?#/!`>`'H0Z"!X`6IO3Q +MSW*``&"S+H(,@0'@#*$.@@R`%J;H\<]R@`!@LRZ"`H$!X`*A#H("@";PSW&` +M`!P[!8$!X`6A(/#/`#H0Z"`X`6I@'9`-@5\,]Q@``4/!J! +M`>`:H1:FW@P@`P'8OO'/<8``%#P4@0'@%*$6I@'8"'&`X6`+@@#/<(``Y+(? +M@!4(W@3/<(``!(OKJ,]P@``XB.RP`]@1I>!XX'C@>.!XX'C@>.!XX'C@>.!X +MX'C@>.!XX'C@>.!X$:4A!:_TH<`8AL]R@`!@LY"X&*88AK"X&*8N@@6!`>`% +MH0Z"!8`6IDX(``#%\<]R@`!@LRZ"!($!X`2A#H($@//QSW*``&"S+H(1@0'@ +M$:$.@A&`L/'/<8``'#L.@0'@#J&5\0HAP`_K"&_[BB!5#L]PH``,)`>`!.CO"Q[`702/]/'`SW"` +M`'07>@^O^P'9;]@&N#(/K_L(V0?8"K@F#Z_[!=G1P.!^X'CQP.'%SW&``#PB +M(X$I@5$A0(#*(*(`)_1$N,]Q@`"\.<.X"6$)"1X`-0V?434)7@#/=8``/"(# +MA1B((0A0`*8+#_L(Z,]P@`"$)0B(#0C0`0.%&(@-")$`"0V>40'8`_``V.$# +MC_3@>/'`7@N/]$0B$%--=H8F_!--<$UP!"6`7P```"!!*'Z#!/):"P_[A.@` +MWP/P`=_/=8``Y+(?A0L(7@0`W9KP_P@1H#H+#_L?Z,]P@`"$)0B(A^#,(&*" +M%_0!A8P@_X\3]"25SW```/__&PD!``6%C"#_CPGT#)77<```___*)6$0>/+/ +M<(``/"+P(,$#"8$/"%X!SW"``"!.!/#/<(``+$XXB2I@02X`$<]Q@``X3@AA +M%GK/<(``G%1(8`\('@`_A88A]H\5\@T(7@`_A2,)G@()")X`"0T>4@'=#/`3 +M"-X`SW&@``PD,8&,(?^/]O,`W5$@@('*)2(0@@H/^P?H!"6^WP```"+*)6(0 +M)^W/<8``Y+(?@1\('@*,)@*0S":"GP``4`#,)H*?``#0``/TD[@?H<]P@``\ +M(@*`PA``!AKHC"8"D,PF@I\``%``$O1/@45X#Z$-\,]P@``\(@.`"8`/"%\` +MC"8"D`3T"0B>`0+=40*O]*EPX'CQP-X)K_0`V<]P@`#T%@"`HL$0Z,]SGP"X +M_QVCSW*``,`@!((!X+.XM;BXN`2B%J//SW"``%0'P*"9$X``H+B9&P(`SW"@`,0G9!A8@,]V +M``#_?Q,8F(,;WA88F(,:&%B`BB?_'\]VH`#\1/VF^::*)Y@=SW:@`%`,XJ91 +MI5"E/!A`@(HB&`A.I8`5`A"D&T``42)`@,]R@`!TEU@;0@`+\D(0`(8$(+Z/ +M`,````7R`8(#Z`*B(:*`'4`0SW*``"0ZSW"``#PB(X`K"9Y#'X.+N!^C52+` +M!;0;```*V!RS&Y$&V98;!`#/<*``R!PIH`KP0"(``[0;```0V!RS&I&6&P0` +MSW&@`-0+$($="%$`"B'`#^MR"]B,N(HCU0"*)(,/E02O];AS`=VPH14(7T;/ +M<*``J"`F@`>`C0(@``/=SW>``.2RM!``H?K8)@CO^P#9(-C/=H`` +MO+.>#N`"`*8!V,]QH`#('Q.ASW.``.`@"(-`)A`50(`,@P`0!``$@\]SGP"X +M_P`0!0#X$0``SW&@`#`0(8$VH\]QH``,)">!`B("@#:C`-D#)$,``B4!``#8 +M4!\$$%(?!!!4'P000:;/<(``/")BID.`(Z84DL]QI0`(#`FV"(+`N`BV`!$$ +M`%,D10%3)$$`3!]"$8/ARB'!#\HBP0?*(&$%RB.!#P``G`NL`Z'USR`A`P0D +M@0\```#@+;E_AYH?0A`4'@`1&PO>`@2Y@;DE>`BV!]@(\`#9%2`,(""D`O`$ +MV`'@]0@4@@B"Z[AH#H($'X!P+@P<@#=FO3/<:``J"`F@8PA@XXH`0T`$G", +M],]R@`"\LP6"SW.D`)!!]8,V@P0@@`\```#@+;CGHL]V@`#DLBBB"P@>`%`> +MQ!,(\%`>1!,$)X\?__\``.>B#0A>`#"_4A[$$P;P4AY$$_!_YZ(+")X`5!Y$ +M$`CP5!Y$$P0A@0___P``**(-@P:B!""`#P```/XIN%8>!!`?AD4(W@+/<*H` +M``0$@`FBSW"``,"'((AD:#3I8PET``(0A`"?<0#8J"#``_0C#P`5W1.]\"7/ +M$\]U@`"\LA5]`>#@I1WPSW"``,"7((AD:!KI`A"$`(#ARB1-<,H@+0#H(.T# +M]",/`"G=$KWP)<\3SW6``+RR%7T!X."E(:H"&@(!M!8!$`+=`8$!X`&A"_`$ +M(+[/8`````3T!-T%\`<+'D`#W3(*+_OQV('E2_,O#9$0`MT$(+[/@`$``,HE +MHA$&]%$C`,#*)>(0ZPV0D,]PH``P$`.`@.#*)6(1AN4R!`(`SW:``.2R')9" +M((0`'X;KN"\D"`%[\L]QJ@``!**!SW"E``@,0(#/HFX!7I2IJRC3:,`@4@6CQ"4YPJC&?(&]C,/D1(CN`[P'0_0 +M'>[G$_1%*/X"02G`<%$EP)'"(&(`!]T+\$4H_@)!*0!Q^O$BN/CQ`-@(W2&! +M%Z8KHQRS'P@1!<]W@``\(N.'Z(<$)[Z?``8```/RC+I2IN2YRB4B$N&YRB4A +M$H8A_@]!*0(!31Z"$"B317DHLRD-T1$C"+0#!]W/<8``/"(C@801`0`3"00` +MSW&@`#`0*($)"$``"-V'YF*:($ +MN"B2B;@E>`BR<_!-'D(0SW"F`(P#/8`$(8`/.````$$HP@2:'H(0!"&"#P`` +M`/`LNB6X17C/BX+[1(>*'!]T# +M]`C=SW.``+RS*:.:$H$`Z),$N>5Y*+,`0A@@\"```` +M)[I%>$0G`AP-ND5XZ7*&(O,/!"&!#S@````.NB6Y17@E>$0G@1`4N25XB+A$ +M)P$202G!@%(@0`42IE@>0A#*(8(/``#__\HA@0\``!`?.G$WAD`>1!`$(H$O +M_P,`_RBY-Z:N"6_Y`-JL'@`0<0^>%$@6@A`RAJ#BT2'A@C#R!"&#CP````$( +M\D0A#08CO0'E%0V5$`0AC0\````D00V`'P```"0$(8T/!@```#&],0W5$!4- +MD1`4ZT0A#08CO0'E'0V1$`/KS.(*]E>&,G+*(HX/`0"(#@`L:'#"6#D`` +M#,R&(/F/"?2$Y&*(9D."/3/<8``/"(C@3Z! +M@"&9#A!Q`-W*)6T4&!O*@X`'8P'@)\([@`=C`>`7P)0E?`0#8SW&``#PB(X$I@3UY +M4B$!`,"Y)'@(Z!^&D;@?I@OP%X;J\4H,``!8%H`0@.`D"P$`@.5T`@(`SW6` +M`.2R6!6`$!3H`MG/<*``]"8CH,]Q@`!TEP#8`:'`V9D5@!"`N)D=`A`H<`/P +M0MC/<:``Q">_&1B``-@,&0"``=@0&1B`'X7QN!("`@`2A3>%&@\O^0#:K!T` +M$!^%M0C>`L]P@``\(D.`2!6!$!2")'A$(`,!1"$`#$(H!`&`<\]P@`#P(L&[ +M:&")N!RE` +M`!0F:V>)NWVE=()T%8\0Y'N&)_\3!'M$O_MC]";#$&0=Q!!RA7JE5()[I41Y +M)'C/<8``X$V`WO"N``@C@^``.0B,([8CL=P@``0)L]W@`"`ETB(97[<.^W'[T +M)XX397H0B&(=A!,%>WVE=!6`$,.X''CT)P`0.J7/<8``D)=D'000:!6`$,.X +M''CT(0,`6Z6.'<00SW.``-B7]",``)(=!!!L%8`0P[@<>/0A`0"0'400]",` +M`#WP@.``V`7T2!6`$,.X''C/<8``X"()8<]R@`"`ESRE/0B``#/<8``D)=2A60=!!!(%8`0P[@< +M>/0A`P!:I<]Q@`#8E_0A``".'<006Z60'<00DAT$$)0=!!"*#L``SW"``#PB +M`X`(@`\(W@).%8`0@.!,"T($6!6`$`7H[@D/_P/P%@T```AU308O]*EPX'C/ +M<:``Q"<5$0.&!-@3&1B`&]@6&1B``]K/<*``U`M1H.!XX'C@>.!XX'C@>.!X +MX'C@>.!XX'C@>.!XX'C@>.!X4:#DN^$@P@<6V%(1`(;@N.$@P0?*(.$%"0A> +M``D+W@#@?Q+8`=G/<(``W"#/`\]P@`!T!B"@%=C@?N!XP=@4&@(PSW&``#PB`X$8B`';SW*` +M`.2RAN`7@L(CP0`,X!@@P`!F&@0`9A(``0/@!""`#P``_/^=N)^X[',`HP7( +M[',`HP.!&(@W@H;@`=C"(`$`&"$!`.QP(*#@?N!X\<#>#`_TSW"``.2R,H`G +M"5X"SW&``#PB(X%($((`-(%$>5$A@(!(VLHB@0\``)```O`.V@#?SW&@`*@@ +M)X&L$`T`66&Q<<(E11#*)>82L'AJ#&_["MG/<(``O#4`D,]VH`#$)PL('@&, +M)0.2`_<`W1KPSW"@`+0/_*#/<*L`H/_ZH'X,[_T`V!D6`)8$Z`+8$!X8D,]Q +M@``4/!N!:KVX8!3=&Z$9%@"6A^A1(0#&D`_A`\H@80"1!"_TJ7#AQ<]Q@`"\ +MLT&)P-L4&L(PSW.``#PB8X,2:D?@!""`#P``_/]I@RJ[P+L7N\=S``X``&5X +M[',`HP7([',`HTHDP',`VZ@@P`'P(H>!_P<7@>/'`B@L/],]W@`#DLD"7"'9(<88A_`-"*04!1"((`XP7`1%" +M*(@00-C/=:``T`\*(T"`$!T8D,HC8@"L%P`00"N&!<]S@`"\LR\D"``=LS@3 +M!P%`+`0$!2<``0P=&)!ABP*[2.,0'=B09A<#$7EA,'EF'T00#0J?`@Z74R#` +M@!'RSW"``#PB`X`)@%$@`(`]V,`HX@7*(*$'P"@A!@GP0"@`$:!PSW*``'PA +M"&(7N`/A!"&!#P``_/\%((`!)7B=N)^X#!T8D`O,`>`0>`0@@`\``/^_C[@+ +M&APP#AV8DR`5`);/<(``/"(#@`B`(0C>`AT.'Q$J"&`$R7#/<(``^+.@V<3: +M/=N>#^``%[OE`@_T\<#AQ<]P@`!X!P"0SW&``("AJ-H!W8`@1`L0>(H-;_NI +M/'`X<7/<8``/"+P(0(`2B1``,,2`08/>#(B@@\``!\# +M!"&##P`&``"`XP';P'L$(8T/0````-=U0````,(D`@$2#._[P+DE`@_TX'CQ +MP*H)#_0J#:`!"'7/<8``Y+(?@;"X'Z'/<)\`N/]8&,`(SW.?`+C_6!L`",]R +MH`#0#Q""SW:@`,0G%J//<(``B"@&@!:C&18`E@#9!.@"V!`>&)`8&EB`0@^` +M``8)H`(!V`7M6@\```7P-@\``'()3_L9%@"6!>@"V!`>&)"9`0_TX'CQP,]P +MGP"X_U@8``C/<)\`N/]8&$`(SW"``(@7U@PO^R/9$@U/_]'`X'[@>`':`-G/ +M<*``M`]!`+9C!A$``0E@E\``'#'/X!% +M>3^@@.6Z`P(``,#IN-;RSW6``!0N`(6*(0@`Y)#/=J``Q"<3'EB0SW&``.2R +M/X$Z=X8A_".%"5X$02D!(<.YSW*``!Q4-GH@@D!Y"'49%@"6!.@"V!`>&)#/ +M<```_W\3'AB0&]@6'AB0`]G/<*``U`LQH.!XX'C@>.!XX'C@>.!XX'C@>.!X +MX'C@>.!XX'C@>.!X,:#/<(``6'T9@(#@8`["`)KE&@,"`,]P@`!`S&8+H`(` +MW0L#```6#&__*G`:<`"%Z@LO_RIQ?@PO_PAWB.?,)^*5RB7!$ROR&P@0(.X* +M(`"!P`HE`)`<]"8,[_\!P!CP`]G/<*``U`LQH.!XX'C@>.!XX'C@>.!XX'C@ +M>.!XX'C@>.!XX'C@>.!X,:``W0\/D1;/<(``0,SJ"H`"@.6.`@(`H@P``<]P +M@`#DLA^`$0B>`P'9SW"``'`&(*`)\`\(W@,!V<]P@`!T!B"@$18`E@#=0<`Q +M")\`Z@FO^H'`"B4`D!#T!!0%,!T-GP`*(<`/ZW(*V(RXBB/'"B4#+_6*)(,/ +M@.4J`@(`!-@3'AB0&]@6'AB0SW"``%A]&8"`X$P-P@`+`@``X+C!\L]V@`#D +MLA*&AB`Z`(P@!(((#`4!SW&@``PD/($7AB)X9+@0>(H>!!!$(@!3%P@1`A^& +M#PA?!%$E0-$!V`7T`-@#\#H+3_^<'@`0+NCB#0__"B4`D-?T#!*@6`1#4V$H/H`#)<@?HN@K``POP1@\/_ZSPSW&``)`\'H$!X!ZA`=_/ +M<(``W""T%@$0X*@&@0'@!J$?AO.X)`S"^@^&@.`P"\+Z'X81")X#`=G/<(`` +M<`8@H`GP#PC>`P'9SW"``'0&(*``V,]QH`#('`>A,-@*H4H*[_\!P!^&)P@> +M!A#8#!H<,,]P@`!`S#8)@`(-R``@@0^``!"R'X;@J;BX'Z8`EH8@_`",(`*` +M&O3"#4_ZF.@#V<]PH`#4"S&@X'C@>.!XX'C@>.!XX'C@>.!XX'C@>.!XX'C@ +M>.!XX'@QH`"6R@VO_#260_!!P!7?"PC?`.EU'_`(V,]VH`#$)Q,>&)!J",__ +M"'4K"!`%`M@\'@"0(18!EL]P@`!TER&@$18`EM,(GX#>#V_Z@<`*)0"0X?,_ +M#5$5SW"@`)`C'H`$%`0P42"`@,HAP0_*(L$'RB!A`L\@(0/*(X$/``#D!`0! +M(?7*)2$`!@^O_XAP"'6I<"T%[_.BP.!X\<#2#,_SH<$(=@#80,``IGH/;_J+ +M<`HE`)"0],]PH``$)2*``(8$(8$/_P!?_P4A`@!`IE,A@@!3((,`97J["M$! +MSW"``.2R'X"#"IY3S0B?!@0@OH\`'@``"?0`A@[PSW```*,)^@B/^OD*G\`` +MA@D*'D"%N`"FSW*``.2R/X(/"5X&B+B+N(ZX`*9&\"L)W@9/(`$"B;F-N8NY +MCKD@IAZ"!""`#P(```!2($`$*K@E>`"F,O#\N<4@@@\````%X_6%(!P``*8H +M\/6X`(8F\H8@'`"%(!@``*8'\,]P``#6"7X(C_KY"A_!`(8M"MY`AK@`IA+P +M4R$#`%,@`@`%([Z`RB7A%0KRAB%_#X8@?P\%(3Z`RB6A%,]P@`!PLPR(Q+A` +M*`$&`(8E>%$@@,0`IOP.H@/*("((J7`%!._SH<#QP.'%`-T%V`NX8@LO^ZEQ +M%@S/],]P@`#DLA^`%0C?`L4-%1'!"%]%GP@?10'E3O``V9RYSW"@`-`;,*`! +MV<]PI`"80#R@!=@'\#X)``#6#J`#!=@!V,X.@`-C#141!""^SS`````!Y14<)GD,S"!]'V0C>Q=4)GL//<*H```0!@(8@/PL`W;L(T8`3 +M\.X(``#/<8``F#L.@0'@#J$)\`#9G+G/<*``T!LPH-((````V<]PI`"80#R@ +M$/``W0T+'T!6#J`#`=BH\;((``#/<8``F#L.@0'@#J$A`\_SX<4,S$0@/HI( +M\E<(W@"`V`P:'#`-S,]Q@``<.\]RH/Y8`<]SGP"X_P#=$0C>`AV!`>`=H4AP +M!_`5@0'@%:%`(@`-%J,-S`\(WP#/<*``+""OH`W,AB""`@T:'#`>\#T(7@&* +M(`0`#!H<,,]Q@``<.Q2!`>`4H0W,`-E&((`"#1H<,,]PH``L("^@SW&@_K@! +MSW"?`+C_-J#@?\'%\<`."N_S`]@!WL]UH`"T#]REX@[/\R((``#/'`U@G/\\]QH`#\1`6!`-Z\N`6ASW6@`-`/ +MU:4#WZ(.[_/I`SW"``(@H!H`- +M"0$`N@_/_P3PG@_/_]'`X'[AQ>'&`-D'V`#:M&FT?<=U@`"`H55]P)6,)@*= +M`-N%]HPFA9+#]O_>P+7!G0L.4Q^,)C^10O9AM0'B3WK/"A*#8;@!X<4(=8`O +M><'&X'_!Q?'`,@G/\P#=SW"``+B6.@EO^K2H$N@(WH#ES"6BD,PE(I',)6*1 +M7`YB_LH@0@-AOND.=9`!Y1WPBB0!<<]Q@`"(L:@@@`$$&5`#X'@`V4HD`'+/ +MG,(=P0B +M@R\`!@``@.,!V\![!"*-+T````#7=4````!*)$``PB0"`0#>SW6@`+0/W*7/ +M<(``D`8$B(KO+R$'!"\B1P3"":_[`]@#V.H)K_MJ<87O?@L``@/PL@L``L]P +M@`"(*`2`(PB>`,]P@`"4.0"`B^@YV/H.+_J+N`L(40`>#<__"O``V9ZYSW"@ +M`/Q$(:#@>,&@W*55!X_S\<`*#X_SH\$(=L]P@``\(O`@@P.*)PL6+9/]8SQZ +M*'"&(?$/PKI'N21ZAB#^`T2X'PF```HAP`_KM=Y.&(_X'1;MHK13J +MSW*``)PY%2(#``"+-7H"K>&+XZWBB^2M8XMEK2.*Y@Y@`":MBW"I<2X/K_,, +MV@#``<'F"._T`L*+<*EQ&@^O\PS:`,`!P3H([_0"PL]Q@``L!P"A5@U@`\EP +MH0:O\Z/`\<`V#J_S5]C/=8``/"(CA<]R@``P!W>1`*(+"QX`7]@`H@L+G@"% +MN`"B"PM>`(>X`***)@L6RV'980#:@./*(($`SW*E`.@/!J(`B<]QH`"D,(#@ +M`8'/(.(`T"#A``&AC@R/]P.%SW&@`,@<3X`"X$BA!@O@`,A@`X7:#6_]#H`A +M!H_SX<7/<(``/"(#@"F`1"&#@`#:)/2)"A4$`"*-#X``?"$`C:"X`*V`%8`0 +MH+B`'0(00!6`$*"X0!T"$!"-H+@0K9`5@!"@N)`=`A!0%8`0H+A0'0(0`>+@ +M\44*%00`(HT/@`!\(0"-@+@`K8`5@!"`N(`=`A!`%8`0@+A`'0(0$(V`N!"M +MD!6`$("XD!T"$%`5@!"`N%`=`A`!XN#Q(PF>`<]R@`!\(0B*@+@(JH@2@`"` +MN(@:`@!($H``@+@0\)+KSW*``'PA"(J@N`BJB!*``*"XB!H"`$@2@`"@N$@: +M`@``V#\)'@!*)`!TX'BH(``&*PB>```@@P^``'PA(!.!`("Y(!M"`*`3@0"` +MN:`;0@!@$X$`@+E@&T(``>`<\$HD`'3@>*@@``8K")X``""##X``?"$@$X(` +MH+H@&X(`H!."`*"ZH!N"`&`3@@"@NF`;@@`!X.!_P<7QP%(,C_//=H``/"(: +M<`L(40``A@+P`8;$$``&%28-%$P@`*`!WR6X4R`%`""%P']`(0`&Q!$!!AL) +M7P$*(<`/ZW*!V(VXBB.-#RD`[_0*)``$BB(+#5E@`!8#0%A@8*``%@!``:$` +M%H!`"*D`%H!`":G/<(``B"@%@!$(40!`A0`6`$$/L@3P`!8`00`6@$`*J0`6 +M@$`+J0`6@$`,J0`6@$``%@!!![$`%@!!"+$`%@!`P@M/_0"%R!``!H8@?XY! +M]/`FP!/($``&AB!_CCOT"G"^#*_X`=G2"J_\+R`'!!8,8`,*<,]P@`"$)2R0 +M'I8-"0``H@F/^4L(`00`A<00`08*<"6YP+EV#V_U`-J``B>@+R`4@@`\` +M```\"QH8,'8-@`")Z`O(!2"`#P```-0+&A@P"\B0N`L:&#"6"<_S]@M/^'$# +MC_/@>/'`Y@JO\P#9"B0`H*'!RB%A`!3RSW"``(@H$!`%`!T-GP`*(<`/ZW)^ +MV(VXBB,(#?4&K_0*)``%SW6``#PB%24.%0"&%252$"00%0``$@$@(!`6`"@0 +M%P%!+4\A*8$:$!@!P+\EN5,A$P#N#.`"#=F*((D`BG$^#2_\Z7)B#"_^BG`` +MA@F`);A3(!``BG"V"Z_X`-E,)`"@1`G!_WD($"`-[PHAP`_K`"P5@!"$X`/8`O0%V#IP(0L1 +M($8)C_ON"(_[SW"``+"R`XB`X#P/(0#*("$`+!6`$(3@RB"!#P``@``X#J'[ +MRB%A`$HC`"!+\`8(K_^*<'H+;_@`V"P5@!"$X,H@@0\``(``$`ZA^\HA(0`A +M#!$@Z@B/^^((C_N.#T_WZ@D/^H#@+`B"]P#8'_`B"(_WU@D/^H3H3@F``Q;P +MSW"``(0E"(B)X,P@XH'P]<]P@``<2@"`!-F]VA[;0,"+<)X.8``8NP'8>G`` +MA@B`#P@>`"P5@!"$X`38`O0&V#IP`(8H@!20!"&/CP`&```%?P?R]KG")Z(0 +MP">A$"IPBG$V"._UZ7*`X,H@0@0D".+URB'"`PT+$"`."D_XG_`^"0_Z!-G/ +M<(``B"@V"6_])*`@AL@1``:&('^.0?1>"*_\BG"B"6`#BG``$@`@R!``!H8@ +M?XY`],]P@`"$)2R0'I4/"0``(@]/^6T(`06*<`IQ_@QO]0':?]D1N<]PH`"P +M'S2@9@H/^OH*@`"(Z`O(!2"`#P```#P+&A@PZ@J``(CH"\@%((`/````U`L: +M&#`+R)"X"QH8,`H/C_,,\"090`4@AB`9@`4@AB@9Q`4@AAH9!`:*"`_Z">@` +MV`L,$2`""(`#`_#F#T`#`=W6"6`!J7#/<(``J"`R"6`!H*@I#%$@SW"``(0E +M"(B)X,P@XH$#]!$($2`1"!$"4@@/^@3H0@[/](X(S_G^"$_X!,J0X,P@@H\` +M`+,`#O(*(<`/ZW(!$@0VDMB-N(HCC085!*_T"B4`!5()(`$`V`T`K_.AP/'` +MW@]/\\]U@`#$!@"%2B``((#@8`B"`<]P@`#T%@.``-X0Z,]RGP"X_QVBSW&` +M`,`@!($!X+.XM;BXN`2A%J(,S'L('@#/A0P2`3=C"%Y%!LB&(/&/+?3/=8``)#J(%0`6!"&^CP```%`! +MX(@=&!`$\@38#!H<,%H,[_X`WL]PH`#\1"6`O+DEH&L5`!97"(6/``"T`,]P +M@`#`(`"`#PC>`L]PGP"X_]V@2B!`(`S,00@?`8,(GP&&(/^%P?)1(P#`K/)1 +M($#%J/0,S,]U@``<.U$@P(#E\H#8#!H<,`W,Z[B>\AV%`>`=I4H@`"";\`W, +M4R!`@!;R!,@#$@$V`QH8,+(*(`($&E@PSW"@`/Q$)8#/=:``R!^\N26@`-X' +M\,]UH`#('P#>&?#/<(``A`8`@!7H;_!^"B`"`-[/<*``_$0E@+RY):#/<(`` +MA`8`@,]UH`#('[\($0`&R`0@OH\#@.A3B*(`0`#J4^"H``+]B5N!(=&)#/<`$`P/P5'1B0C@_` +M`<]Q@``D.`"!A^#2\L]PH``X+@6`!""`#\````#7<,````#!\O78!;C/`5I<]P@`!PO1&(42`` +M@$@,X@'*(&(`!>\KPC>`0W,!""$#P```!AG#(`/````""X/ +M#_@-S'T(W@#/<:``+"`%@2:!"N!5"00``Q(!-@+8#!H<,%#8<@LO_I@1`0`N +M"0`"SW"@`/Q$)8#/=:``R!^\N26@1_&*(`0`#!H<,!2%`>`4I4GO&X4!X!NE +MQ?%F"V_Y"G`3"!\`SW6@`,@?!-@&&A@P1_$(V)NX!AH8,"3P`\B@$```\+C) +M<#/RD@C/^`#8EK@M\#D('P+/<:``R!];"%\"!""^CP```%`)\A,+'D"*(`0` +M#J$$V`8:&#`-S$<(WP//=:``R!\=\4H(;_R*(`0`6@XO^,EU`\B@$```\+BI +M<`7R.@C/^`#8E;B^"@`#!-@&&A@PYO$F"._X`=@`V)"X]?%`$0(&SW&``%RS +M+Y&U"D2`K[@-&APPU/$`V`D(40`'V`"ASW"``,0&`("`X(P+0@'/<8``%#P, +M@4V!""(```VASW"``.`@$(!/@6"`#H$">P#*""+"`$^A&0@1`@/9SW"@`$`M +M,*!C!.__`!J",P'@6P3O_P`:`C#@>.!^X'C@?N!XX'[@>/'`X<44N"5XSW6@ +M`#@N!J4%\!8/[_F*(%P(!H7W"-Z'+0-/\_'`L@I/\\]QH/Z<`P#?SW"?`+C_ +M-J#/=J``P"^E'MB3#]T(O07PW@[O^8H@G`*C%@"6I'B,(!"`]_,4'MB3!O#" +M#N_YBB#B:*(,N)VXG[@E>`:BX'[QP"X*3_,(=G(/[_\H=@:J#@?\'% +M\<#J"6_S`-G/<*``#"18@,]U@`#DLJUP02J&!X8@]P^8%8,0*;AV>(H=!!``E88@_XPH]`'8':(F\$X5@!"BZ(H5`!%,I62X$'B*'000!-E/'4(0 +M&P[1$"L7`99DN!!XBAT$$`S8+:5/'0(0S@RO^8AP"/`%(T,!01_8D!^%L[@? +MI14!3_,0VL]QH`#('$FA`=O/<:``\!=JH:00`@!-"MX"`MI=H<]S@``(O42# +M1J%#@T:A0H-&H4J%P$``!'.!3(,"`!/1`(P`(!/!`(P`,0(!3H4QH0()3 +MH?@0`H)3H?P0`(`3H0_P7)"&(O^,`_1]H4B`1J%'@$:A1H!&H06`!J'@?N'% +M+X#/C@`MG/<*``R!PGH.!_P<7Q +MP.(.3_D^"H_YT<#@?N!XX'[@>/'`C@\/\\]U@`#DLA>%SW:``-#'"P@0!E@5 +M@!`$Z!J%6X4$\!R%787/<8``7#D@@1,)'P`RA00A@0\````0)7@E>L]Q_O__ +M/R1X`:8`W^*F1'DMI@[84@^O^0ZF!^C/<8``="5V#&```=C/<8``4")J#&`` +M`-@7A0\($04!V`&N,1X"$`3PX:XQ'L(3:0#*($(C"_3/<(``E"``D('@`=C`>$`H$`/)<(8@_`",(`*%(_3/<(`` +MY+*8$(``Y[C*("(`"O0"N!9XSW&``$B_`&$MN,"XSW&``%0'((%1(8"`SW&` +M`%RS%'D$\B#:K9$*\)C:JY$&\,]P@``@L[.0#MH!ET`E`141"0,`HGA((``` +M$'@#\`#86G``V"IQJ7,F#R`#F'`*(0"@!/0V"P`#.G!,(0"@`-A(]`4@@",- +M<0"Q#7$`&80$(X<-<""@*)<-<""PC"8"E17RC"8#D1WRC"8#E23R"B'`#^MR +M$]B,N,]S``"4"HHD@P\9`F_TN'//<(``Y+*T$`$`#X$!X`^AQ@D@`.EP$O#/ +M<(``Y+*T$`$`#H$!X`ZA"O#/<(``Y+*T$`$`#8$!X`VASW&@`/0'`-@$H0'8 +M&G#/<:``R!_X$0(`0G4")8`02"```%^!$'@]"(0`0X?/<(``=)="H*#8#Z$` +MV!^ASW"``.2R')!BN$)P'Z$"V!49&(`-"1`@42!`QB#8`_*`V`ZAC"8#E0;T +MSW"``.2R')`)\(PF`Y$(],]P@`!R#"_[`-T5"%$`SW"``)0@`)`!W8'@P'T,O<]P@`"\R`2`SW*``+"7!""` +M#P```!!%($$#0,$@P,.X''CT(@,`SW"@`"P@#X!PNQ4(Y```WO!X<'MB"6`# +M%-H)"!X&R7`X\`/8SW&@`/0'!:&%)0,9#7"@L`UPP+"*(O\/#7!`H,]R``#_ +M_PUP0+`#R,]S@`!(O\]R@``\(A"(`K@6>`!C+;C`N/`B``"@@`UPH*`#R!"( +M`K@6>`!C+;C`N/`B``!"D`UP0+#$H;((``,!V)4$+_.AP.!X\<`>#`_S&G#/ +M<8``Z)<`B17HP8&B@<]P@`!4!P(1$0'@@,]Q@``4/`N!-+\!X`NA,_!&"._Y +MBB`+",]QH`#$)Q$1`(8`W^\(GH%D$0*&9!G8@P+8$QD8@"\H@0!.(($'$NK/ +M<(``2(LV>,"`H8#/<(``R(OT(%$`SW"``.B+\"!/``OPSW&``!0\"H'I=>EV +M.G\`UQ`*%*)`!TJ"#``D0F@1`/N5,F`!`E +M>`UQ`*$BODHD`'2H(``#1"6!$`^Y4R4`$"5X#7$`H2*]10,/\_'`[@H/\PAV +M*'4H<$AQ7@@@`&AR@>#*(($#$`@A`,HA00,Y`P_SX'@BN0;P[')@H@3@8;GY +M";6`8(#/<*``U`MMH`/9SW"@`$0=-:#@?N!X02F!@`GR+R1)<*@@P`$$$`($ +M['%`H>!^\<"&"@_SH<$(=4AVSW"@`*PO&8`$((`/<````-=P(`````'8P'@O +M)@?P`-K*(($`+?(+S``<1#!/(,$#`AQ$,`'@$'@$((`/``#_OX^X"QH<,,]P +MH`#4"SB`0B$!"(#ARB&,`$`E`!(0<:@/Q0('Y00EC1\``/S_Q7V=O9^]['"@ +MH`#!['`@H`'8:0(O\Z'`\<#AQ:()+_L`W8'@RB!"`PGTSW"``)0@`)"!X`'8 +MP'@,N(4@`P$#VL]QH`#T!T6A#7(`L@/(`-M=D`UP0+`#R%&`#7!`H`/(2!`" +M`0UP0+!DH1D"#_/@>/'`G@D/\\]U@`#VXX.[_\8N^`5`!`!YN<.!)``V-4!+_/@'0`0X'CQP%X)#_,A@`HF`)`0 +MB<.XRB'!#\HBP0?*(*$&RB.!#P``J@#/("$#.?*`X`!ASW&``!`'+;C`N`RI(X8@D88A_`", +M(0*`#/3/<8``/"+P(0$`OQ$`!H&XOQD8``&&HH`%[0&%`^@`A8SH"B'`#^MR +M'-B,N+G;2B1``-D$+_2X/'`H\$`V6#!`1P",`,<0C`"'$(P`=G/<*``L!\YH,]Q@`#@ +M(`R!`("$VD+`"($`@`S9'MM!P(MP0@WO_QB[H\#1P.!^SW"``)26*(#/<)\` +MN/\`VC:@"-GL<""@`]G/<*``%`0EH`'(['$`H<]PH`#4"TV@X'[@>/'`X<6E +MP0AR`-O/<*``+""P@$#!!MA!P$+#0\-$PP'8'MF8<[AS`"6''P```'T:#V`` +MV',M`"_SI<#@>`C(A[@(&A@P"X"QH8,`S(#!H8 +M,.!^X'C/<8``Z'T`@8&XX'\`H>!XSW&``.A]X'\#L>!XSW*```0Z%7K@?R"B +M\<"8<`HAP`_K<@HEP`?/<```HAEE`R_T5MO@>,]R@`#@.15ZX'\@HO'`F'`* +M(<`/ZW(*)<`'SW```*,9/0,O]%[;X'C/N!_(*+QP)AP"B'`#^MR +M"B7`!\]P``"D&14#+_1FV^!X\<"D$`$`#0E?!OX+3_D'\"#9SW"@`,@<*:`# +MV<]PH``0%"6@T<#@?N'%`[@U>,]Q@`#(1@)A2B0`=`#9J"#``A8B0`"A@&"` +M*=@2N`'A=7B@H.!_P<7@>,]P@`"0!@"`H<$F@)X1``:&N)X9&`#@?Z'`X'C@ +M?N!XSW&``!`'X'\$H>!X\<#AQ<]R@``\(B.".(D?"1$!"B'`#^MRBB",#HHC +M!@)*)```90(O]+AS((+$$0$&`<]R@`#L/R""0B$!@,HA8@"OZ8_H"B'` +M#^MRBB#,#HHCA@-*)```,0(O]`HE``$F@B.!8;B@@<]Q@`#@("2!(('5N3UE +MSW&``+#')8$%*3X`)W5(<#(((`%")8$2SW"```A``"6!'P``B!,>"``!10;/ +M\N!XX'\`V.!_`-@@@`>YX'\@H/'`N@W/\L]U@`!D%S_+QP'8-S_+/ +M=J4`Z`\FAJ>&SW"``%P'`-\DH*6@*@\O_0W8!KB!N,]QH`#L)P:AYJ9%))*PH!`,]P@`"^!@"008D?"@$`SW"` +M`,`&`(@FB0\)`0#/<(``X"T`@`+P`-C@?N!XX<7AQ@#=,PG0!PL)TP<+"1,` +M`-@3\!D)\P*@@@`$/)8T38;X)"$X`I7@#\*9X`*(!V,'&X'_! +MQ?'`H<$`VD#"N@_O_XMR`,"AP-'`X'[@>/'`H@SO\BC8,@_/^<]U@`#\+4"% +M"'8$((0/``#P_P#88'I!+`$!0(4!V$0F!!-@>D$L@0!`A0+88'I3)D$0_@[O +M^0#80(4(=T$H`0(#V&!ZP+E`A4$O01($V&!ZP+G/<0``A+_/<(``^"T@H,]P +M`0#0H9D$[_(`I?'`I@O/_$H/C_S/<0``K+_/<(```"YZ"V_\(*#/<0$`^*'/ +M<(``!"X@H-'`X'[@>/'`_@OO\E#8B@[/^<]U@``,+D"%"'8`V&!Z4R9!$$"% +M`=C)<6!ZAB']#\]Q``#0O\]P@``(+B"@SW`!`""B+03O\@"E\<#AQ=X/K_H' +MV!X,K_P(=:X/S_]*#<_\]@ZO^JEP$03/\N!XX'[@>.!^X'@%````\<"&"^_R +M"',(=H8C_@-$NPAWAB?Q'T>_1""!`SQYSW6``'"]+:T$((0/````#$(L@`(3 +MK00FA!\````P0BP``Q2M!":$'P```$!3(;Z`0BR``[$=`A`.]`HAP`_K#,("*`S"`B@0;T4VDE>D^M3JV` +MX\P@(H$%\E-K97I.K8#GS"`B@03R$V_E>`^M$VDE>!"M#HT,K9(*8`$`V#4# +M[_+?M>!XX'[@>.!^X'C@?N!XX'[@>/'`M@KO\@#;H<$$N,]R@`#0QQ1X'6*0 +MWAIB`8(8OLBEBB8$$L]R_O__/\FE!'J`X4#"RB7!``OR"($$((`/````,$(@ +M!8#*)6(`30T0`,]P@`!XL0`0!`#(@00DA@\`!P``!":.'P```#`LOF&^02X& +M!D`F@!,/(@(`0,*%#H\#"B'`#^MR/MB,N(HCB@Y%!N_SN';/<(``L+('B('@ +MSR*A`R_RSW.``'BQSW:``.2RFA:!$`.+"R$`@"'R3!:!$`#;4R%.`$0A#P,/ +M(X,#0K\`W@\FSA.&(?\#!"8.D`#?1+D$>P\G3Q#D>,HF`1"`X\HC@0,.NV5Z +M`_`!@P5Z0,+/<(``>+$@@(MRAB'^`R2Y0"F#`R""!"&.#P$``,`+)L"0%O+7 +M=@```$#,)H*?````@,PF@I\!````!/0!@`/P`H"NN:^YL+DE>`"B`!0$,`0D +M@8\!``#`"O0*(<`/ZW)&V(RX;07O\XHCBPD(A2ZY0"D"!@0=`!%%>`BEBB`% +M!@FESW"``#BS!(B`X(H@!0[*(($/``#8`0FEJ7``V@'>3@FO__R +MH<#@>`HE`(#QP!'R)PV0`"D-T``*(<`/ZW**(,T.BB,$!04%[_.*)(,/M@L` +M`-'`X'YF"P``_?$:"P``^?'@>/'`Q@C/\@AUSW"``"PV"8")Z,]P@`"\-2F` +M#.#P($X``O`!WHH@_P\`I<]Q@``\(@"!Q!``!FD(7P'*"(_[`*6*"2_ZJ7"` +MX,HEXA&B]*((3_F&Z`"%C"#_CPWTSW"````N((!@>0'8@>`%W7QSW"``.P_ +M`(#/=X``5(A"(!"`.@]O^\H@8B``I0&'#PB%`\H-K_C)<0AVSW"``.`@!(#/ +M<8``L,<`@"6!26[5N`4IO@`G<&JX((5((```,'#*($8`1/<`I4H@0"#/<8`` +M'!AAD<]Q@`"\!T"!(8$\XP'=>F)DXA3A.F)0<,(E3A.S?5,E39`B\D$(42#/ +M<(``[#]."<``SW"```A`0@G``,]P@`"P0#H)P`#/<(``S$`N"<``(8<1":4# +MR7`6""```=D$\-((S_^9!Z_RJ7#@>/'`+@^O\KAQ@.#*(<$/RB+!!\H@@0\` +M`$L#RB/A#LHD(0`L`^'SRB4!`<]W@`!`0":'(X$@@<]R@`#@($2"0(+/=H`` +ML,=3(DT%-KH`(A``/65%AF&X!2H^`"=U`B5`$(P@%X=*]\]P@`!4B"&`!2F^ +M`"=U`"!0("$-$`!A#5``FPV0``HAP`_KD!,)```")8$?````#.EP!/#I<$(E`16Z",``SW"` +M`%Q``"6!'P``B!.J",``2_!N#@_YSW&``+!`$NC/<(``A"5,D,]P@``\(AZ0 +M$0H``"AP`B6!'P````P%\"AP0B6!%W((P`#/<(``S$#;\3(.#_G/<8``>$`1 +MZ,]P@`"$)4R0SW"``#PB'I`3"@``*'`")8$?````#`3P*'!")0$5,@C``,]P +M@`"40``E@1\``(@3(@C```IPR;C/<8``5(@#H0:&@;@A!J_R!J;QP.'%SW6` +M`"1`C@^@`*EPSW"```@V((#/<(``##8`@`DA`0#/<(``;+`(@`DA`@#/<*`` +M+"`P@*EPS@^@`%EA\06/\O'`SW"``%2(`8`/"%$`SW"``+PU!X#/<8``>$`@ +M@4(A`8#*(6(`D.G/<8``W+`L@8KISW&``,2P)@NO^".!"@[O_P+9T<#@?O'` +M,@V/\L]P@`#@(`2`H(#/<(``[#\`@$(@`(#*(&(`-KV'Z,]P@``(0.(.@`#/ +M<(``Q+`AB,]P@`!`0,]V@`!4B(KI((!"(0&`RB%B``3I((:MZ;8.@`#/<(`` +M7$"N#H```8:R"J_X"'$AA@]X'0D%``HAP`_K$`@@4(A`8#*(6(`!ND=92.&R;T+#4`06@WO_P#9]02/\N!X\-'%X<8CZF-J(KO!N@+P`-HU"A4!,R:" +M<(``@$A`)XUR5'T@?<"`!!F0`P3@!!`"!`09D``$$`($!!F0``00`@0$&9`` +M0B-#@./UP<;@?\'%\<"N"J_R4R%"`$XB#0'/G'ZW/<8``_)8$\,]Q@``C/<`$`$*$!IP`6`""Y$``&+0@>`$&&&M@`L@*F`)&'N`"Q +M`-@+L0&"K;@!HA$,$"#/<(``P#<$@#,:`@`I"A`@(88!@9BX`:$#@9^X`Z'/ +M<8``1`<`%@`@`!D$!$"``8!!H0*A6@]O_\EPU0"/\N!X\<"&"(_RNG!Z`AB3",`H`2XAB#^ +M`P4@4`#*(

&?PSW:` +M`,1!`88`W^EQ,@OO\SC:`(8/'`X<7/=8``-#T`C8P@PX\/],]R +M@`#0/P:"`X`@@,=Q#P``H&X)H`!(/'`!@Y/\@AW&G$ZQP +MX*`#A@B`#0@>``*%@;@"I<]P@`#`!@"(A.@"A8.X`J7/<*``+"`0@,]S@`"L +M.W(=&!!*),!P`-BH(,`%SW&``)@&((F`X0S:RB(A`$0HO@//<8``8,TG.,(X*`((:X]\]RH`#4"RVB +M)'@`IF<=V!-H'1@1`-AA!6_RW!T`$.!X70;O_P#8X'CQP,]R@`#$!@*")8@! +MV`?I"-D>#6_Y*Z('\,]Q@`"T*$();_,`H='`X'[@>/'`R@QO\MAP.@@@``#= +MR6@K#A(0^'"I=S(F@`,5"!(,$0B3#H8.#_8R;SAX!7T!YT(G1P#E#W6`8;[Y +M!&_RJ7`(<@/P`>`@B/[IX']">.!X\!XP:7%I,]QJJJ[N\]PGP"X_S:@-J`VH#:@SW&@`,@[#H&( +MN`ZA:2!``/[QX'CQP,]P@``\(@.`&(@="!$!"B'`#^MRBB`,#HHCA0I*)``` +M)0"O\[AS(@FO]`/8SW"``&RP`!`$`!D,$0`*(<`/ZW**($P.BB,%#/T';_.X +M<\]P@`"4(`"0@>`!V,!X#+@I"($/````$,]PH``L(!"`SW&``"PV`J$"V`.A +MSW$!`!@EP@MO_P'8T<#@?O'`F@M/\L]U@`#4B@ +M/!T`%'((K_0"V*D#3_+QP$H+3_*"#6``"'7/<:``R!]%A0SH;A$.!@*`9(7$ +M>D5[;AG8`"*%`*$+\&X1``9$>&X9&``(#@`=G`><]P +M@``P/>!_(*#QP/(*3_+/<(``^"T@@*+!8'D$V(#@J@(!`,]Q@`#8(`"!`>`` +MH1<(40`!V<]PH`#('#&@4@X@`BAPY@XO^@78SW:``/@X#J;/<8``V"``@0'@ +M`*$5"%$``=G/<*``R!PQH"8.(`(H<`/8>@\O\\EQ!-AR#R_S(FX%V&H/+_,D +M;@O88@\O\R9N#]A:#R_S0"8!$C;83@\O\T`F@1(WV$8/+_-`)@$3.-@Z#R_S +M0":!$\]WIP`42`B'SW&G`)A'!*8-A\]RJP"@_P6F#H?/=:``["<&IAR!!Z87 +MAPBF%H<)IAB""Z89@@RF&H(-IL]P!0#&`P:EQMB0N`:ESW`L``(!!J7/<%H` +M0@$&I8H@BP`&I<]P0`"'#0:ESW#1`,(-!J7/<,``!PX&I<]P@`#8(`"`SW*` +M`-@@0B!`@`"B!O3/F`<$QH,]P@`#8(`"``>#/ +M<8``V"``H14(40#/<:``R!P!V!&AB@P``@&6$+B%((0`!J4"EA"XA2"%``:E +M`Y80N(4@BP`&I026$+B%((\`!J4%EA"X!2"`#P``@@T&I0:6$+@%((`/``#" +M#0:E!Y80N`4@@`\```(.!J7/<(``V"``@,]Q@`#8($(@0(``H0?TSW&@`,@< +M`-@1H02&*X8(IP6&#:<&A@ZG"(87IPF&%J?/<*L`H/\XH"R&.:`MACJ@L@LO +M^@Z&SW"``-@@SW&``-@@`(!"($"``*$'],]QH`#('`#8$:&=`&_RHL#QP#(( +M3_)^"```SW:``*!$<@XO^`"&"'4`AAD-`!#R#Z_[J7`F""_\H*;J#:_T$=@J +M"8_WSW"@`"P@,(#/<(``C`==`&_R(*#QP+H/[_^AP<]P@``X/0"`!-EBVA[; +M0,"+<.8,+_\8NZ'`T<#@?N!X\<#>#&_T%M@`V-'`X'[@>/'`J@\O\@?8T@L/ +M^L]VH`"T#_R&&G``V!RF.@LO_.]_SW6``#@]H@XO_`"E0(7/<8``]$$`H<]Q +M@`"8.TJA&@SO_`NA_*;""B_Z"G#/<(``J"``B#<(40!`A8H@1`3/=8``F"`C +MA1IB.&`0<@'9PB%%``38!>EJ#,_U`(4$\%(,S_4"A3(/X`$#I84'#_+@>/'` +MSW"``#`]`("038$.C/<(``Z"U@@,]Q`0!P +MN0O88'L$VBX-;_06V-'`X'[/<8``/"(`@<00``8/"%\!`8'$$``&%0A>`:H, +MK_03V*(,K_01V.SQZO'@>/'`SW"``"0<`(#/<8``C`<;>&H++_8@@0CH`=G/ +M<(``J"!V#^__(*C1P.!^\<"&#@_R"'=]V`VXSW&``+#'Q8$."&_RR7&,(`*` +MSW&``"@<`-V']QUXC"`"@`'E?/<`*$(#!2J^`\]R@``D'!:X`*'/<8``-#T` +M&D`.A.__V`"I`(F,(,./Z`Z!_Y$&#_+@>/'`#@X/\CIP>G%(=VAV"B0`(0#: +MSW&K`*#_6:$'V!JA6*&6"2`"`=@9V<]PIP"81SJ@@@UO_![8SW*G`!1('8*^ +M@FP2$`!P$A(``*>@IO>XQ2""#P#_``#3(.$%][W%)8(?`/\``-,EX14&":_W +MBB$0``AVJ7#Z"*_WBB$0``AU0"@`(NH(K_>*(0@`"'=`*@`BW@BO]XHA"`#1 +M>1GA+'DO<;%Z&>),>B]R`!F`(P\/8A``&T`C`-@$\/\(@X`!V*$%+_(`'`(@ +M\#0_R"'4H=N8((`(*V`'8SW&G`)A'&J'6""`""MC/<*8`G#]D$`0`420` +M@,HAP0_*(L$'RB"!#P``OQG*(X$/``"X`#`!8?/*)2$`SW"G`!1(+(`=@`"F +M][C%(((/`/\``-,@X05A!2_R`*7@>/'`Y@P/\L]R@``LEL2"C";#GSOR_]DD +MHL"@A"X(&0`AC7^``-"1!(T*($`N`=^2Z`*%SW&``#@I$@NO\R"!"''/<(`` +MX"`$@`"`D@U``(3H`=@<\,]P@`"X*"*-P*@AJ,]QH`"P'_FASW&``.`@,($@ +M@0#=(:!V#"_YZ7``((`O@`#PDZ"H`-C!!`_R\"L_\SW"``#PB +M`X`8B!\($0$*(<`/ZW**(`P/BB,&"THD``!-`&_SN'//<8``O#4)@0L(%0$! +MX`FASW&``+#'!H%&($`!!J'/<(``N`<@@!4)D0"*($8/I@ZO^@3:_@^O^@38 +MT<#@?O'`SW&``"PV"8$!X`FASW&``+#'!H&"N`:ASW"``+"R`XB`X.P(X?[* +M(*$`!@IO]`;8T<#@?N!X_]G/<(``J)$@J&\@0P!I`6_S`=F%`:_T$=C@>/'` +MSW"``#PB`X`8$(0`'0P1`0HAP`_K`!V,!X#+@1"($/ +M````$,H((```V"#P>@G/_,]P@`!`&0"`ENC/<8``L,<&@48@0`$&H<]P@`"X +M!R"`%0F1`(H@B`2N#:_Z!-H&#Z_Z!-C2#L_ZT<#@?O'`SW"``+!``(!"(`"` +MRB!B`(CHSW&``"PV"8$!X`FASW&``+#'!H&"N`:A"@EO]`;8SW"``-RP#(`8 +MZ,]P@`"D,@*`$NC/<(``E"``D('@`=C`>`RX%0B!#P```!"F""```-C1P.!^ +MSW"``+"R`XB%Z*8/K_X"V/;Q]O'@?N!X\<#/<(``/"(#@!B('0@1`0HAP`_K +M`AR`-@=!"_X$-G@>`AR`=@1!"_X(-G@ +M>`AR`M@%!"_X0-G@>`AQ&00O^`#8"'$1!"_X`=@(<0D$+_@"V,$!#_/QP,]P +M@``\(@.`&!"$`!T,$0$*(<`/ZW**((P/BB,)`R$%+_-*)0``SW"@`"P@,(#/ +M<(``+#X^"R``EB%!#\]Q@`"PQP:!1B!``0:ASW"``+@'((`5"9$`BB")!W(+ +MK_H$VLH,K_H$V)8,S_K1P.!^\<#/<8``L,<&@8*X!J'/<(``L+(#B(#@P`VA +M_LH@H0#:#B_T!MC1P.!^X'CQP/_9SW"``#0]/@FO_R"HR@G/_]'`X'[QP.'% +MSW6``+`H`!4%$(PEPX\?])#HSW"``+"5:!`$``HAP`_K#@_TD@M/\\]P@``H&3D`+_*@H.!X +M\<`."T_XF@M/^'X*3_C1P.!^X'CAQ<]RH`#('Z02`P#/<8``Q`8-@1!SPB,& +M`$3W8G@3>[^"#H&[8WA@#J$!V$H:&`#@?\'%SW*@`"P@9H+/<8``Q`8.@6)X +M#J$0@ND`[_8-H?'`5@_O\4HD0`#`@:"``=_1=<(D`@'1=:&!88#")\X3`=ZQ +M<\!^L7,!V\(CS@!,)`"`S"8BD,HC8@`*](7K@.;,)R*0`_("VP+P`-L4ZR$+ +M4``Y"Y$`H(#`@0&`(8$")8V3H*(#($```:(0\`#8`*(!H@SPH('`@"&!`8`" +M)8V3H*(#(0$`(:(U!^_Q:'#@>/'`X<4F@$"`0B("@,HB8@"`XLHAP@_*(L(' +MRB""#P``-A'*(X(/``!W`,HD(@"T`B+SRB4"`6"!%0M``$*`HH-"?0T-4Q!@ +M@_4+08!!@P&C8*!!H`"B1("F@$`E`Q87"EX`1H4&ZJ*"0H!"?0<-4A``HT2` +MIH!`)0,7%PK>`$>%!NJB@D*`0GT'#5(0`*-!@`L)@0#N"J__!H"E!L_QX'CQ +MP"8.S_$(=@"`0B`!@,HA8@``V";I)H9!A@'?,'(@AD&&0:$@H@"FSW"MW@(` +M`::FAL!_!H41#@$0J7!6""```MD&I::&!X4/#@$0J7!&""``"-D'I07OB@JO +M_P:&`=@M!L_Q((`0<EPX'A`@!4*``!D@@LC0(`%]$""]PH!@`#:X'](<.!XSW*@`,@?]!(` +M`+S;&+L$((`/__\`\/0:```+R&5X"QH8,!4:V(#//'`&@W/\<]UH`#0&].%$0Z>%L]P@``804H)```/#MX6SW"``#A!/@D``!$. +M'A?/<(``6$$N"0``#PY>%\]P@`!X02()```1#MX7SW"``/A`$@D``+S8&+@3 +MI2T%S_'@>/'`M@SO\0#;"'?/=J``R!^D%@`0SW6``.`@^&"D'@`0`=@3IBB% +M#(5`@0"``"+"@T"A+(4!(,```*$"V!.F*85-A0"!0((`(,"#`*$-A0$BP@!` +MH`38$Z8JA0Z%0($`@``BPH-`H2Z%`2#```"A"-@3IBN%#X5`@0"``"+"@T"A +M+X4!(,```*$$A0"`)@[O\^EQ)(4`H06%`(`:#N_SZ7$EA0"A!H4`@`H.[_/I +M<2:%`*$'A0"`_@WO\^EQ)X4`H0_8FK@.I@_8#+@0IL]P@`#X0*((C__/=8`` +M&$&6"*__J7"2"*__0"4`&(H(K_]6)0`2@@BO_U8E`!,E!,_QX'CQP+8+S_$( +M=R#P`(8AAB&@`*$`V`"FSW"MW@(``::FA@:%$0X!$*EP^@WO_P+9!J6FA@>% +M#PX!$*EPZ@WO_PC9!Z4CAF!YR7"N#>__Z7`*)@"0"/(#AR"``H8B>*\(4H`2 +M"*__Z7"Y`\_QX'@/V)JXSW&@`+`?%:$/V`RX%Z'@?O'`*@O/\<]R@`#DLC^" +M.G"JP0#8(0G>`L]Q@``\(B.!2!*#`,#=-(%D>88A_PXBN3I]!/`4W0+8BA(! +M`0)Y$H($X0X*[_8`VE()8``"($X#`]C/<:``R!\3H<]U@`#@(`B%`(!"P`R% +M`(!#P`F%`(!$P`V%`(!%P`2%X(`%A0`0$@!`$0`&'F;\$0``SW"``+B60(`A +M@`#8`"*"@P$@0`!`PD'`BW8?"5$@H@T/\X3!&G#)<"X+[_^&P@AV"!`!(0OP +M@L')O\(A(`%\&8+8`"&P`AR1L(M#I$0Z7`N#._S2'$(=TIP(@SO\P;!!L):<`3# +M!\`%P0`BPH`!($``1,(6\)7NZ7`N#._S2'$(=TIP)@SO\P;!!,-:<`;!!<`' +MP@(C0X!$PP,@@`!%P!L.4!#/<(``/"(#@!B(A.#,)B&0`-@"]`'8+R`'H$/T +MZ7"Z"^_S`]D(=DIPL@OO\P/9"'<`P`'!0"#`@$$A`0!`P`3`0<$%P4`@P(!! +M(0$`1,#R#R``1<$7"1$@!(7`H`B%`,$@H`R%`<$@H"<)D2`$A<"@"(4`P2"@ +M#(4!P2"@!87@H`F%!,$@H`V%!<$@H!<)42`%A>"@"84`P2"@#84!P2"@3"`` +MH`'9P'G/<(``$(LTJ'T![_&JP/'`)@G/\:7!"'8"BRAUF'!DP`"+`!(&`1$< +M`C!Y<`(2!P$$$@@!$!0`,>22!A(%`0`@R0,`D2\A2!('($`""@D@`!!X`""* +M`0&5+R*($@<@@`+V""``$'@`(,8!`I4O)H@!!R"``>(((``0>``@!P(#E2\G +MR`$'(,`!S@@@`!!X`"4%``25+R5(`0<@0`&Z""``$'@?9P65\'_G>*H((``0 +M>":5(7`0>`=Y/'H/N25Z4'H`(H$",'D`'$0P1Y4G>EQY#[I%>3!Y`"&"`5!Z +M7'D"'(0P#[I%>3!Y`"'"`5!Z7'D$'(0P#[I%>3!Y`"%"`5!Z7'D&'(0P#[I% +M>3!Y/V?P?_QY"!S$,P^_Y7DP>3A@:7'&N86Y"+D%(<$"(+80>""5"AP$,"=X +M''@(N`4@``$!M@#``:8!P`*F`L`#IE$`[_&EP`][2+@/>,]R@```5O0B``!` +M*`$"2+@%>?0BP``P>>!_)WC@>/'`SW*``.A`((*`X,]Q@`#H0""!`-B#X`HB`(#QP!?R +MX@_/_X#@RB'!#\HBP0?*(($/```S$T5X$'/*(*T`!?<0 +M/'`X<78<+AQN@_O_YAR"'7(<+(/[_^(<1!URB"M``KW +M$'4`V,H@1@&8#^;_RB$&`?T&C_$`V,]Q@`!LL`6A!(&@N`2AM03O\P/8X'@V +MN#:Y,'#6((4/``"``.!_(GC@>/'`5@Z/\0HG`)#/=H``L,?/=8``&$$/],]P +M@`#T5,EQB@HO_Q3:!@CO\JEP0"4`&!'P&0^1$#8)#_/)<6X*+_\4VD`E`!@, +M\,EP6@@@`079J7#:#X_RSW"``/A`S@^/\@26"K@%I@:&AB###P:FX@H@`.EP +M>@Z/\D4&C_'QP*'!"'/>#*_WBW""X`#8!O(`P!!S`=C"(`X`H<#1P.!^X'CQ +MP`#9SW"``%PM&@@@`""@SW"``!`^?@^/_]'`X'[@>`#9SW*``)`'(Z(DHB6B +M)J(GHB*BSW"``/1`(*#/<(``F$$@H#&R,++/<(``&#C@?R"@X'CQP,]Q@`!< +M+0"!E.@!V`"A`-G/<(``\!:V#^__(*#/<(``6"(0B(/@,`DA`,H@80'1P.!^ +M\<`N#8_Q-@TO^*3!@."$"0(`SW"``/`6`(#/<8``&#BF#N__(('/=H``D`

``)A!0,$@AT/"0L"+ +M<$'!$-FBVA[;M@FO_AB[`-AV"*_YBW'/<(``\!8@@,]P@``8.**FL;8@H+"V +MSW"``'`&H*"2">_S$]@`AQT(5`%>""```=A&#`_[SW&``)@['8$!X!VA!?!& +M""``!=BQ!*_QI,`5V`#:SW&@`,@?;QD8`.#8D+@0H0G8L!D``+09``!TV$(9 +M&```V)JX#Z&D&8``SW``#``9#J'@?L]R@``0/B:"(X%AN&"!SW&``/`6(('5 +MN7EASW.``+#'98,%*SX`)W''<0```!`I!J__2'#QP.'%SW6``)`'!X63Z,]P +M@`!<+0"`'PA1`.X*S_@7")`&SW"``.`@!(``@`6E`=@'I1T$C_'@>/'`X<7/ +M=8``D`<'A1GHSW"``%PM`(`K"%$`M@K/^",(D`;/<(``X"`$@`"`!J4*#>__ +M)84PE3A@$+4`V`>EV0./\>!XSW"``%PM`(`7"%$`SW"``.`@!(`@@,]P@`"0 +M!R.@X'[QP,]P@`!<+0"`)0A1`,]P@`#@(`2`SW*``)`'`(`$HK(,[_\C@C&2 +M.&`1LM'`X'[QP`(+K_&*(0@`"'7/<*``R!\PH`'901A8`%8)``#/=H``L,<# +MAB6&U;@P<,HAS0_*(LT'RB"-#P``-1'*(XT/``";`,HD+0#_R"'<:<(#ES"5BD$KTSW6``.`@"(4@AB"@#(4AAB"@`(4EAB"@!(4C +MAB"@H@H/^.4($`#/<(``A"4(B-D(T0$%A<"``(`$)HX?P/\``%,@404$A0"` +MA@RO\PIQU;A%A05^`MO`HL]RH`#('W.BR84"($&$8(9-A4"""@`$`$(IP`<' +M\"27"KD"(4$$&6$`V`(C0X`#(@$`8*8-A3OP=0V1$`27SW6``.`@(84*N`"A +MSW"``#PB`(#$$``&42!`@0F%(/+/<8``A"4HB3D)T0'/<:``R!\!VE.A*(4` +MVR"!3(4"(0&$0((@H`V%`R+"`$"@!(4`@.8+K_,*<26%`*$*\""'(*`-A2&' +M(*`CA@6%(*#Y`8_Q`-F6N<]PH`#0&S.@X'@#"YY%X'[/<(``&$$G@`;I`X!` +M@`*!0G@%\,]P_P___^!^SW&``#PB)($H@00AOH\`!@``H<$$]!,)'P`)\`0@ +MOH\````8`_(`V`+P`=C/<:8`I``7H>!_H<#QP#H)C_'/=:``M$<(=@;P=@TO +M^(H@B0QQ%0"6!""`#W````!!*#Z%]/6*(/\/;QT8D&L=&)`#V`^XSW&@`,@? +M$QD8@`6&61T8D`:&6AT8D`>&6QT8D`F&6!T8D`B&5QT8D$`1`0;/<8``^$$@ +M@00@@`\```"`$0D?`(#@!MC*(.$!`_``V,]Q@``\(B.!*('/&)!7%@"6O+B_N%<>&)!?%@"6O[A?'AB0 +M`-B>N%,>&)#@>`#84QX8D`R/8!X8D+H)C_S/<(``^"T@@&!Y!-@5Z$PA0*"8 +M#^'ZRB!!`\]W@`"<1`"/%PT`$,]P@`"`+S:`8'D`V``?`A2J#L_R0Q8`ED4@ +M``V?N$,>&)"7"1`@(PE0(&T)D"`*(<`/ZW**(%H*BB.-`DHD``"%`Z_R"B5` +M!,]P@``\(@.`$+V;O3(@@`\``-@"G[V`X`'8P'@/N*5X7QX8D`;PB@LO^(H@ +MB0QQ%@"6!""`#W````!!*#Z%]/6*(/\/;QX8D&L>&)`3\,]P@``\(@.`$+TR +M((`/``#8`I^]@.`!V,!X#[BE>%\>&)`&R(3@,`VA\\H@H00]!T_QX'CQP.(. +M3_$(=2AV<@BO\0&`H(40N4$M`!0X8&((K_')<1"YL'@X8%8(K_%`+H$2(0=O +M\2AP\<"N#F_Q!-D`V,]UH`"T1TL=&)``VI"Z=QV8D`':=QV8D,]RH`"$1!BB +M`-J1NG<=F)`"VG<=F)#//'`&PAT`9AP"B'`#^MRBB#:"W?;'0*O\KASSW.` +M`#@&)(-H<#1X()`="1X"$!,$```0!0$*(<`/ZW**(-H*]0&O\G[;)(,!X22C +M`-H'"1$"1*,O(0">F+/YWM`":-'X``^+'XC1,/ +MD1#@D_M_(Y&`OR1_X+,%\`L/41`BD2"S`-DXK<]UH`#('/J%()/D>2RS!/`L +MDPD)10-9803PK+.Y8HDASP\$&%```>8`V<]P@`"PQS4%;_$GH/'``!8$0`<: +M&#$`%@5``1I8,002@3" +M#J``#MG1P.!^X'CQP((,3_$:<`W(SW>``"BR\"WIA +M19)Y88;J)I%1(4"`:`]"\@W(`""!#X``I+'$J11](I'` +M'803%7]X'400`Q(!-L"G`8$$((`/````8"T(@0\````@$(G/<8``2+\"N!9X +M`&'MN,HF8A#/<(``="'4>""0$.$@L`/9SW"@`!0$,*!""V`!"G`]\'`2#0'@ +M$P$!`B%.`Q$-A!/"?:)X$'B`&QP`SW"@`-0'#Q`.A@#=\!N$`W`2`@'`&T0# +M0GDP>>`;1`#0$P$!`>$P>?`3!0'0&T0`4R5^@,HAP@_*(L('RB#B#/'`_@IO\0#8SW&``+Q! +M`*'/<(``]!8!@*/!$.C/"SN+6XN+@$H1:B#,S/ +M=J``U`=1(`"``]@@'AB04_(4'AB0`Q(!-@`6!$`'&A@Q`!8%0`$:6#$$RIS@ +MRB+"!\H@@@\``-P.RB."#P``]`JH!F+RRB'"#RAPT@R@``[9`Q(!-E")4R+` +M`(8B_@,0J42Z`KC$&8(`%GC/` +M`(BQ%"=!$PB1HNAPBL]P@`!(O[5_`KMV>V!@F!(#`"VX;J=VI\"XSW.``'0A +M]",#`+P:Q`#0$0`!!".##P``\/_#N&5XT!D$``;PT!$``;P:!``!V*`:``#J +M"N_[\(J`X-`#(0`#$@TV!LA1(("!P`,"`"&%$PF>!I#8D+BU`R``H!T`$`*_ +MSW"``$B_0"""`_9_ZV+$%8(0$PK``)'8D+B1`R``H!T`$&J%SW*@`"P@\(*, +M(_^/#/)B?Q4/A1\`@```A]B0N&D#(`"@'0`0$!6$$$`LCP#V?^-@!".^CP`` +M`!/X8$OR$0M>`HO8D+A!`R``H!T`$$T+'P,%D)3H!\@$((`/`,```-=P`,`` +M`!'8P"@A!%0`1 +MI[B>'000!O"%V)"XH!T`$,]P@``\(@.`&(B$X-@"`@#/<8``$(L,@0\@``$, +MH<]Q@``X!P"!`>"]`B```*%BD#,5@!!-"PX`!\@$((`/`,```#$(@0\`P``` +M"(TI"%,`I!4`$+2XI!T`$)(5`!&GN)(=!!">%0`1I[B>'000"O`1"9X!C=B0 +MN&T"(`"@'0`0!LA1(`"`&`(!`&8.C_\#$@TV"'.H'0`0SW"``.`@!("P%0<1 +M((!5)T`&U;G/#*(8T/`0")#=4A#@"D%0`020@>!2)ZA!4! +M$0(B0`!((```0KA!+T(3P+H$NE1ZZ7'&N4DAP04T>L]Q@`"\3U%A#0_>$D$I +M`@$4(D$`!2D^`$$I`'(`V5GP02J%`$$O0A/`N@2Z5'KI<,:X22#`!11ZSW"` +M`+Q/4&`-#]X202@"`10B```%*'X!02D`N4`L#P7E>25XP!T`$`JBSW&` +M`-PY`=@`H07P;X*P%0<1#PO%`078&+B@'0`0SW"``#P'08`@E0DA@0``B!$( +M40`9%@"6$'$`V`/W`=B,Z`/8&+B@'0`0SW&``!P[$X$!X!.AH!4`$`0@OH\! +M`0``%/22%0`1E!4!$)`5`A&R%0,10@X@`4HD0``#$@TVH!4!$"5XH!T`$`0@ +MOH\!`0``!?(R"$_W1P-```/,SW&?`+C_&*$&R%$@`(#*(D$#RB`A('CRI!4` +M$&D(G@3/<8``W#D`@8#@`-@R\@#8`*&`%0`1?A4.$1YFSW"``#PB!(!&$``! +M'F8&\(X)[_>*(,<)^0F>Q<]PH`#$+`N`4R"!!/ZXS"$B@`KRF!4`$/(++_8` +MVG2XV&`#\`#8`Q("-@GP#061J7)*(``@SW&@`,@?K!4.$(?H +MI!4#$+&[I!W`$`/P"28.$`/;&+MOH?@1`P"A:P@F31-B?:`90`,`VYB[;J$, +MZ*02``#QN`W,Q2"B!,\@80`-&APP`9()Z`W(SW&``(BR]"$```3H`8(-")X# +M#`U02`P&\$@T!P[NE>U0:Q`"&(/T,C"`"@ACT$(H"N!9XQW"``$B_ +M99`A"U(`!I`="%X`$PM1`&`2``&$N&`:!``$\!R2C;@C0$0,!5!(` +M`<.[97A4&@0`@!$!!X3I/)**N3RRI!(-`!4-'A)H$@,!4R#!`'EA,'EH&D0` +M$PU>$FH2@0##N#A@#WAJ&@(`!\C/<8``F+($((`/`,```!$(@0\`P```#AD$ +M!`7P`-B+N`>Q`9(3Z`W(SW&``(BQ%'G0$0`!4R#`@`GR\!$!`<]PH`"8`SZ@ +MMAI$`*02```$(+Z/````,`CTAB#ECR0,8@#*(((`"@@``03H$@G/_:GP`\BD +M$```!""^CP```#"S\O2X;`[!]0,2`3:D$0``GP@>`XX+;_,!V`,2`38=L<]P +M@``\(L2`E@HO^0#=&0A1`,]P@`"4(`"0@>``W<\E(1/*)0(4`]C/<:``]`<% +MH84E`AT-<*"P`\A=D`UP0+`#R$^`$0H>`$*!`H$:6!_`-<$"@`\A`$`(! +M#7!`L`/(48`-<$"@`\A($`(!#7!`L!`9``0#R)00``!1($""O`U!^#H(#_I7 +M`$```8$?"!X&SW"``$0'`)`=L<]P@`!(!T"``8!1H1*A!_#6"F_S`M@#$@$V +M';%""`_^`\B>#*__>!```8#@%@!"``,2`S8!@Y@3`0"4&T``*P@>!L]U@`!` +MS*EP<@@O^FAQ$-@,&APP#!@/9SW"@`!0$(Z"*(!``KP<@``8:&#`#R*00`0"& +M(>6/I`I"``,2#C:D%@`0]+B*`@$`$([/L]SH``L(&^#A!8-$2"2 +M""-#`Z)[L!8-$63EL7/\`"X`"2-!``*X%GC/PARQKI)(L(%5'O/2JBSW&``)`\"($!X`BA!_#/<```\!$V#8_W^0F> +MQ<]PH`#$+`N`!""-#_`'```TO5,@@00)")X''0V5$`,2#C9*(``@SW&``)@[ +M`8$!X`&A`-DP\`"6$.#E"$6`I!8`$/>XU2%"`\]W@``PR""GHJ>8%@`07@_O +M]0#:`:?/<8``F#L"@0'@`J$`@;A@`*'/<(``/"(#@`F`#PA>``W,1B"``@T: +M'#`#$@XV`=E*(``@DA8`$3GIE!8!$,]R@``PR**2P()`P<]SI0"L_\]R@``\ +M(MBC1()6$@(!%.)"?0/E(KVZ9;IB2")"``6Z12)"`U:C42#`@!!"TN:0>0!"4%@`0D!8#$<]QI0"L_T#`L!8"$7BASW.``#PB9(-6$P,! +M%.-B>@/B(KI;8GIB2")"``6Z12)"`U:A(,($((`/````("6X!2("!$5XB;B. +MN!FASW"@`*@@"(`#V<]PH`#T!R6@# +M&0A1`,]P@`"4(`"0@>``WL\F(1/*)@(4SW6@`/0'&86`X,HAP@_*(L('RB#B +M#,\@(@/*(X(/``!N"LHD`@2,`B+RRB4"!`/(')#%>`UQ`+$#R#V0#7`@L`/( +M+X`-<""@`\A`$`$!#7`@L`/(,8`-<""@`\A($`$!#7`@L`,2`38!3F!#7`@H`,2`3:D$0``9!D`!+@9`@2Z&00$M[BD&0``I!$```0@ +MOH\``$`(!O(!@?"X@`T"\P_P.H$-<""@`Q(!-J01``"&(/./!?([@0UP(*`! +MV2NE`]I(I<]S@``D.`T2#39`@P#8.0V`$,]RH``X+D6"!"*"#\`````?"H`/ +MP````/78!;C/.!XX'A5"%Y#`\C/<:``R!^P$``!EB!!#QZA$-@.H0'8 +M%1D8@$H(X`!!V"T(7D//<(``O$$!V2"@`\BD$`$`FKFD&$``A@QO_P'8SW&` +M`)`\#8$!X`VAH@P```0@OH\&`,H`F'`F\L]P@`"P(`.`@.#*(<(/RB+"!\H@ +MX@K/("(#RB."#P``/P2@`"+RRB4"`<]Q@`"0/!"!`>`0H<]QH/[H`,]PGP"X +M_S:@5P(```/9SW"@`!0$):`#$@$V`8%1"-X`I!$``,]R@``\(E$@`(`$@@/R +MNY`$\(H/+_BZD,]Q@`!PO1&)+0@>``.",(D0N3(@@`\``-@"G[F`X`'8P'@/ +MN"5XSW&@`/Q$#:$$\'81#0$-S%,@0(`)\@;(!!(!-O8.;_8-$@(VSW>``$#, +MZ7!."N_Y`Q(!-@/(!A(0-L]V@`!P(:`0$0`!V`"F-@YO_ZEP`-D@I@GHAB!^ +MC]/R`\B@&$`$!AH8-`,2`3:2$0`!$0B>`JJX'@HO^Y(9!``#$@(V?A(!`8(2 +M``&`$@,!.&`;8PW(SW&```2R%7D)@7![&V-IH0&"V0C>`!8([_F`V`82`38$ +M(8$/`@`!``T2`C<9"8$/`@```!$(7@=/(L$`#1I<,`;PH[I0>0T:G#`#$@(V +M`8);")X!3R'``HRX$'D-&APP$(HS$H(`!+A%>,]U@``PFL]RH``X+D2"!K40 +M\"\N@1!.)H,7`-X/)LX0QGK/=H``X+ST)LX0$0B``_'JSW```/__!+4#\&2U +M"-@,&APPSW"``'"]$8@5"%X!SW"``+"R*@NO_0"(#1(!-P/(`8#]N,\AX@'0 +M(>$!#1I<,,]Q@``<.Q:!`>`6H2WP$-@,&APP#+$# +MB*AQAB'\!T6Y)GA3)!L]P@``\(@2`SW&@`,@?1A`` +M`1^A(-@.H0T,G@92#(_T#?!9#%X&T@[/\@,2`3:@&0``AB!^CP/T`-\"\`'? +MSW"?`+C_6!@`"(API@N@`&AQ`][/=:``U`?2I7X-3_W/<(``<"$`@(#@S"!@78$+CN\0CJSW*``!0\$((!X!"B]_$*(<`/ZW(* +M)0`(,MC//'`I!`!``\)7@(.",_]T<#@?BAT +MA"02D!'R#0E?!OX+#_<'\"#9SW"@`,@<*:`#V<]PH``0%"6@Z_'K\?'`Q@Z/ +M\`HG`)`:<0#=%O+I<2\H00!.((('SW"@``PM3WKP((``PK@/)0T0`-@/((`` +M!B$!@._U).TO*$$#3B".!PT:F#/UV`6X6@GO]\EQ#@PO]N1YL@MO^\EP`-@/((`#!B4-D-_USW*``"0X`(('V0T: +M6#`_"-`!SW"@`#@N!8`$((`/P````",(@`_`````]=@%N,]SGP"X_QJC.Z-I +MV!BX&:,!V`+P`-@'"%$`(*+/<*``%`0JH%D&C_#@>/'`]@V/\"AV1B'-`!UE +M,@@@`"*YP;X?#E`0$PZ0$!T.T1``%H!``1T2$``6@$`!'1(0`!:`0`"M*0:/ +M\*L)$`!`(<(#)+K#N0+P`-F5"14$,R9!<(``<$A`)P-R-'L`>P`6`4`$&%`` +M`!8!0`084```%@%`!!A0```6`4`$&%```!8!0`084```%@%`!!A0```6`4`$ +M&%```!8!0`084```%@%`!!A0```6`4`$&%```!8!0`084```%@%`!!A0```6 +M`4`$&%```!8!0`084```%@%`!!A0```6`4`$&%``0B)"@+/UX'Z`X.@@K0$`%@%!`AA4`.!^X'CQP.X,C_``W<]W```$'4H@`"*I=A4B@#,.$`$& +M`-C/8) +M!8_PSW.``+1$*XL+"D(`+XL+"8,``-C@?HP@`H'A(,$'!-C@?P+8\[5[SW*``/"'>6(AB7IB +M"^DA"0``)PA"`"\-4Q$!Y:]]"O!")9$0+R%')&&]KWT1\`,2SP``V6IU#/"` +MY4HA`"#*)6$0!?)")5$0+R%')`'9+.GS;O1_%2=!$\]S@`#PASIC`"-%`!4G +M3Q3Y8R&)08K[8S<)HP#CBP(B1``#%8(`!+_P?R)X!+HO)`@!`B>#$&QX+R!& +M#OX,K_"(<0YX`G\(Y^Y_1+_M?PD($B8*Y^U_R7`*<7H,(`#I<@'FSW"``)@& +M$HC/?A!VP`;,_U$#K_"AP.!X\<`""X_P*'6`XLPC(H`)\BQM+WG/=H``F`8S +MK@;PSW&``)@&LZFI<<]V@`"8!K2N`*Y5KOH)(`!VK@`0B0#AB,EP$HC1CA!V +MF@$)`$0I/A#5XQW"``/"'0*A!J$*H0Z@!X2]Y7O!\N0`AB@$ZBVRZ`")`$1J(X'(` +M(@8"[@@@`.ER&*T`(D`1&X@[B]X((`#I69"'60KWT!YL]P@`"8!A*(SWX0 +M=G`&S/\`V<]P@`#H08$!K_`@J/'`SW"``#@]`(#/<8``Y$$@@4UH,'+`(&P! +MS"$,@"0+"0#1P.!^X'@">2UY3'E6(0%R1[DX8.!_#WC@>/'`N'$U"%$`"0U2 +M`!T-T@,*(<`/ZW+/<```UQ2*(X@"T02O\4HD``!`+8$`9+D`(8`/@``8%!WP +MSW"``"`8,B!!`8PAPX_*(<$/RB+!!\H@@0\``-@4RB.!#P``$`*4!*'QRB0A +M`,]P@`!(%C5XT<#@?N!X\<#/0&N +M0B%!(($)=8`!Y=T'3_#QP((/3_#/<(``F`81$(@`SW"``)@&$HBK"`("2B8` +M`$HAP!%$+CX'+W"$*`,1)W``(($/@`!DB!\1RP``(($/@`!DB!X1R@#X<`#> +M!M\`)XT/@`!DB-5]!XUI<07:F'`6"B``!17#$$`N@@!4>H0H`14`(D$.U'G' +M<8``9,RX<0"IB'!)<0?:[@D@``85PQ`!'0(`8;\!YK`+;8*@`V`"I`=C@ +M?P"JX'BAP?'`I@Y/\*'!9<((=BAUSW"``+X&A<&+""``J7$!Y:]] +M4R6`$,L(4H$A\`$4@C`2;11X`":!'X``T,DX8$"H(,)$J,EP,@@@`*EQ#_!" +M)0`6#W@!%($PQW:``.C*`K@4>!YF(,`HK@RN"-Q'!F_PH<#@>.!^X'CQP,X- +M3_``WL]PH`"T#W`0$`#/<*``M`_-O>P78[PKC@`*OSW"``&2(09#/=8``[$'`I1OJSW"``/1! +M`(",(!^$T_8G"(,/``"@#T)X0(F`XHHA#PK`*.(`!/1$*+X#+W`*#T_P`*4` +MW0[>SW>``/A&O@CO_ZAG8;X!Y?D.=9"O?<]P@``X/2"`SW"``.1!(*`O(`<$ +MSW&@`+0/'*%Q!4_P#G@L>"EJ`-@/($``)W!:>.!_#B#``.!X\<#F#$_PSW>` +M`)@&`(\6#.__,X_/<(``X$$`$-``SW&``&@E%(]'B1\*`0``CR&)%PA!`,]P +M@`#I00`0P``)(``$+R`%(+&/`_`!Y:]]$H\0=?P`"0``WDHB@"//<(``X4$` +MB!#H1"V^$P`F0![/<8``X$$`$<(``""!#X``8,U`J5_PSW"``/@M((!@>0#8 +M&0@1`\]P@`#H1LE@`B!`(`UX2"!```3P2"!`("\A!2#/<(``^$;+8!./J7&2 +M#B_W58\)($`$+R$%(,]P@`#X+2"`8'D`V``EDQ^``+0&&P@0!,]P@`#X+2"` +M8'D`V`03@2`;"!$#SW&``/P(R6$$$X`@(G@)($$$"/#/<(``V$;(8`)Y"2%! +M!$0MOA,`)D`>QW"``&#-(*@Z`/>`#8`_!AN`]XX'_!Q>!XX<7AQ@`1S0`)#1,0 +M`-V@J1OH#0T3$`#8`*D`W<]P@`!X10"0"PT"$*EHK7V@J<]P@`#01!0@3@.@ +MCJ"J`!'!`#1X`8@9\`L-$Q``W:"ISW"``"1&`)`-#0(0J6BM?:"ISW"``'Q% +M%"!.`Z".H*H`$<$`-'@!B`"KP<;@?\'%X'CQP(X*;_``V*'!`!P$,,]U@`!X +M!P"5SW:``&2(R7&*(@0*E@VO]P';C^@*(<`/ZW(`%001SW```-L4A]N+NVD& +M;_&*)00*`!:$$$PD`('*(4%;_%* +M)0```!0`,0'9AB#^#\#@P'G/<(``(!D@J!D";_"AP.!X\<"F"4_P"'77=24` +M`(``V$KWSW&``+#')8$E"44#(GT!X/GQSW"``+#'Q8"IC"`0@,HAQ@_*(L8'RB"&#P``S2+*(^8,RB0F`&0%9O'*)08!%KBQ`6_P +MI7C/<8``/`<-"%$``=@`J0&I`(F!X,H@@0\``,0)RB""#P``@`#@?P&ASW*` +M`,`P((H982"J(8HX8.!_`:I!B0*X%GC'<(``2+](J"*)X'\IJ,]Q@``\(O`A +M`0!-D42Z#0H>``Z!B;@.H0L*7@`.@8NX#J$-"IX`#H&-N`ZAX'[@>`T2`C8$ +M(+Z/8````,]S@`"(L51[QW*``/BQ"'$%\@/(')`7")X"!"&!#V$````3"8$/ +M`0````#8`+,!V!SP#,P#$@$V&PC>`3(1@0`!BPT(00``V`&K\_$!X`&K"_`Q +M$8$``(L+"$$``-@`J^?Q`>``JP+8X'\8JN'%X<;/&4V>,2HKF(!Y:]]P*C! +MQN!_P<7@>/'`_@\/\,]P@``L'`"`H<&#")``SW:``)0YSW6``)@Y`(4@ADT) +M``#/<(``B"@$@$#!#0B>`$\A``%`P(CI2@FO]0#8/@F/]>8,3_>+<`39H=H] +MV[X,+_T7NR"&">D`A8?H)@FO]0'8E@M/]R"&(*41Z3H*C_5_V`JXSW&@`-`; +M$Z%_V!"A`-B5N!"AL@UO\@'8V0\L]V@`"$)N!3P+;G`N<]W@``\(O`G3Q!2 +M($X"P1AY^X`*4!XXT&#_#/<8``/"+P(0``SW&``#2Q +MNQ`"!KH0`P9"H6&AO!`"!KT0``9%H>!_!J'@>,]Q@``\(O`A``#/<8``-+&^ +M$``&%B$"``*2&K$#DANQ"(HX&0(``-C@?QVQ\<#AQ<]SH`"L+QF#\+@9@P#= +M#/($((`/"````-=P"`````'8P'@'\(8@?P^"X`'8P'@:Z!F#!""`#PX```!" +M(`"`RB!B`"$(4``*(<`/ZW)D$P0`SW```*X-F=N)`6_Q2B4``/X/+_=4V$0@ +M`0)'"!X!SW*?`+C_O:(L]UH`"H(*V%Y.60]^WJ`MO/.!^X'C/<8``%#Q<&<`'SW&``.`@4(&=N)ZX((+/<:``R!P-H>!XX'C@ +M>.!XX'C@>.!XX'@`@N!^\<#AQ2#=SW.@`,@?L*-#&Q@``-BZ#^__C;BQH\D$ +M#_#QP$8,#_"AP3IP*'8:0R>`&8`P>3A@`[B6 +M($(%SW&@`,@?'J$0V`ZA`=@5&1B`N@[O_X'8+P@>0\]P@`"\00'9(*`#R*00 +M`0":N:080`#V"J_^`=C/<8``D#P-@0'@#:$#V<]PH`#T!RJ@T<#@?O'``-D* +MV,]RH`#('QZB$-@.H@'8%1H8@"AP!_`!V00@@`\@````42``P\PA(8#,("&` +M%_0K"Q]`SW```)\7+@^/]L]RH`#\1!V"68(`V=$*WX($(+Z/``8``.+UX?$O +M"QY`SW"``+Q!`=D@H`/(I!`!`)JYI!A``%X*K_X!V,]Q@`"0/`V!`>`-H5$@ +M`,,`V`GTSW&``!0\$($!X!"A`-B8N-'`X'[@>/'`;@HO\`#9"'?/<*``+"!` +M$!``SW6?`+C_'87/=H``;`8]I0"F#/#B"@_QSW`/`$!""@_O\PIQ(0A0`,]P +MH`#4"QB`0B``"$@@``#=",2#`(8=I7T"#_``A@HAP`_K.!^X'C@?N!X +MX'[@>.!_`=C@?N!XX'[@>.!^X'C@?N!XX'[@>.!^X'CQP%((#_`(=@;PSW`` +M`&L.D@R/]L]UH`#`+Z,5`);O"!Z!!\A`'1B0#<@/")$!3@XO_\EP`$D`F@1)>#&_]"MK/<*``U`L8@$(@``A((```L."`#>7_RB`E +M#`/(`Y`EN,"X%[C'<``.``!%(`$+['`@H`$2`3;L<""@((;L<""@(8;L<""@ +M(H;L<""@(X;L<""@)(;L<""@)8;L<""@)H;L<""@)X;L<""@*(;L<""@!_#/ +M<```3@[>"X_VHQ4`EO4('H$'R`0@@`\!``#P++B4X,`@A@\``),`SW&@`&@L +M\"$!`,]P@`#`00"`F@CO]"5X"H\)Z,]Q@`!\/!>1`>`0>!>Q`-@*KZ4'S^_@ +M>*'!\<`>#\_OH\%,P1IP2'4Z[A +M4'@&]$8,K_.!P1KP$0G1#1MX$'@V#*_S@<$1\`D)$04<>`KP"PF1`@0`#8SW*I`*3_N:($%`$Q@K@WHAJB"?!O"1X"3"(`H-$@ +MXJ$%\L]UH`#('TSPSW*E`*S_SW"``#PBN*($@%80``$4X`(A`R`#XR*[>&-X +M8$@@0``%N$4@0`,6HD$HP"'`N'=H+,`$(8$/````("6YSW6@`,@?97@E>(FX +MCK@9HD`5`!8@\"S`@.#*(<$/RB+!!\H@(0[/("$#RB,A!<\C(0/*)"$`2`(A +M\/'P```(#,(2&`P"-A(`4B`0'E>04A_H`$]*,+E*(%[X#BS",A@(CR:Q83 +MEIL+$"!J=(0DT)$*\L]Q@``<.Q"!`-T!X!"AG+U?\!4+WB#/<8``'#L1@0'@ +M$:%"W57P:G2$)`*8"?+/<8``D#P1@0'@$:$-\!4+GB'/<8``D#P$@0'@!*$% +M\%,C/J,#\@#=._`9"UXCE@B/_<]Q@``,/06!`>`%H?7Q^@A/]0HAP`_KK/<8``%#P0@0'@$*'=\2GK%PB>!L]R@`"0 +M/"^"`-T!X2^BD;T*\!\(7@;/$RHJX([_]J<9B]2/!Q%@26 +M;Q8%E@HAP`_K`1H:WQ$Q4` +MEO"XRB`A`,`/H?_/(*$#:Q8!EE@6`)8+($"`(?)O%@"6SW6@`/0'4R!`@`'8 +M#_()I>!X`-@)I<]Q@``,/0B!`>`(H<]QGP"X_Q:AV@MO_@'8`]@*I07=F+T" +M\`#=ENT9"-XA'PH1(`'9SW"@`/0'+*`#V0;P`]G/<*``]``/H<]P@`!0HR&`SW"``#PB`X`4D!\)`0#/<8``+!H:@3N!)'A1(`""G`EB +M]\H@8@"I<`C+$!@`5]R7`*"J_Z +MJ7')<*EQ"G+I1L]Q@`#DLC*!"PF>`D*P +M0[!$L.!_6;#@>/'`;@KO[YAPSW"``.2R#I#/=H``^+,`ML]P@`#DLD@0!0`$ +M)8^/`````@#;`_(7#1X"SW&F`.C_"X$#I@R!!*8#\&2F8Z;/=:0`M$4,%0*6 +M#14`EO_9+R"'$!"Y!"))`"SO,A4!EE,ACP#_9R&V_]GT?PBY[W]$>4`O!A(` +M)D<``"#($P4G!P)`+P$6!"*"#P#_``!`+P@4.F(`($@2_]D%)P<""+D%(L(! +M!"!'`/A@`">!`25XY;9/>00B@@__````*+I%>2.V#W@$M@05`)8"MB,-'@)$ +M)0`&([@!X!<(E`#/<:8`Z/\-@06F#H$&I@/P9J9EI@#:2B2`<`;9C;FH(``# +M*=@2N/`@0P!`)@`?57@!X6"@`>+/<(``Y+(`D#@>`!%5)D$4&K;/<(``T+-& +M#B_]"-H;%0"6SW&E`-C+&:8<%0"6&J8=%0"6&Z8.@1RF#X$=IB85`)8>IL]P +MI`"0?QR`<0'O[Q^FX'CAQ<]U@`#XLPFE*J5XM4NE`=@9M>!_P<5*)`!Z`-FH +M((`"`-K/<(``^+,U>$"@`>'@?N!X`-D@H.!_(:`$`````$`!``````"H$D-U +M`08``0``````````1#V``-@]@```?(``P""``.@(@`!P!8"!#QH;(@``&R4" +M`!M````;<10&@($``,`6#QL+(A@&@($``,`6#QL*(AP&@($``,`6#QL((D`I +M@($"`%QN$0``8?A!Q!`/&PDB``L).0(`"F(#`0IB!`(*8@``"4`$``!A"0`) +M0`(``&$*``E`````80(`"4$`"1HH``#`%@$`&R8``,`7!``=)@$`""?I``AD +M#R`;(AP&@($``,`7<`6`@0\:&R(``!LE`@`;0```&W$/10`B`%P`.0<``&(& +M8`!B``!8.&!%P!!P1<`0>$7`$)!%P!!I``!A"`!8;O@/`&$``!,E```3)"00 +MP!$`@!,D.!S`$0\`$R(!`!,P!"C`$0]S$R*"`1,P!"C`$0]T$R("`A,P!"C` +M$0]U$R)"`A,P!"C`$0\4%2(!`!4F#W(3(@@`S!$/1``B"@``0`!``'`.``!A +M```3)0(`$R3L',`1#W83(A@(RA$)`!-`'`C*$0D`$T`@",H1#W@3(@0`RA$` +M``$D```!)08``&$/=A,B+$C'$0]X$R(``,81`P`!)````24``!,EPBP3)`0H +MP!$"1A,D!"C`$<)?$R0$*,`1#T4`(@!<`#DL``!D```3)`$`$R4X',`1#W<3 +M(N`Q,B`0`3,`0HP!$/%!4B +M`@`5)O\`$R4"$!,D!"C`$0\4%2("`!4F=`>`@0``P!;"+!,D!"C`$0)&$R0$ +M*,`1PE\3)`0HP!$/31,B!!#%$0(`$R3P',`1`0`3).P`````````.(`````````Y@````````#J`````````.X`````` +M```\@````````#V```"JJ@H`/H```%68F:H_@```````4#"`````````,8`` +M```````R@````````#.`````````-(`````````U@````````#:````````` +M-X`````````X@````````#F`````````.H`````````[@````````#R````` +M````/8`````````^@````````#^`````````,(`````````Q@````````#*` +M````````,X`````````T@```FGD``#6```"JJJJJ-H`````````W@``````` +M`#B`````````.8`````````Z@```JJJJ"CN`````<)FJ/(`````````]@``` +M`````#Z`````````/X````````#__P``I0$!`+D!WP"Q`!L`%@$;`*\`&P`4 +M`1L`;`"@`-$`H`!O`(,`<0"#`'8`@P!S`#,`;@`S`'``,P!R`#,`UP`S`-0! +M!@#0`0``?@`\`.,`/`!X`$D`W0!)`'\`6@#D`%H`J@`_`*L``0`/`3\`$`$! +M`'D`:@#>`&H`J`````T!``"F`#<`IP`!``L!-P`,`0$`!``(`)P!S`"=`#,6`'PT%P"`-1@`A#89`(@W&@",.!L`D3H<`)4['0"9/!X`G3T?`*$^ +M(`"E/R$`)$D&`BQ*"@(T2PT!/$P/`61-$0%L3A,!=$\5`7Q0%P&$41D!E5(= +M`9U3'P$!!````@4!``,&`@`$!P,`!0@$``8)!0`'"@8`"`L'``D,"``*#0D` +M"PX*``P/"P`-$`P`#A$-``%```0"00$$`T("!`1#`P0%1`0$!D4%!`=&!@0( +M1P<$"4@(!``````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M``````````````!`'```1!P``$P<``#(&P``S!L``#0,H`!`#*``'!R@`$`@ +MH``H)*``;!"@`!@DH`!X)*``?"2@`(`DH`"$)*``4!"@`%00H`!()J``8!"@ +M`$PFH`!D$*``:!"@`%P0H`!8$*``,!"@`#P0H``T$*``+`R@``"!I``!@:0` +M`X&D`(@DH`",)*``D"2@`)0DH`"8)*``G"2@`*`DH`"D)*``@%F```````"R +M#,@`_________P`!__\"`____P3______________________P7_!O\'_PC_ +M"?\*_PO_#/___PW___\.____#____Q#_____________________________ +M_________________Q'___\2____$____Q3___\5____%O___Q?___\8____ +M&?___QK___\;_____QS___\=____'O___Q____\@____(?______________ +M________(B,D_R4F)___*/___RG_________________________________ +M_____________________________________________P`!``$!```````` +M```!```````````````````#``````````$`````````&"0!```````$=@`` +M`0```)"U`0`"````!+4!``,```#4``(`!````!@D`0`%````Z!$!``8```"P +MZ```!P```'`2`0`(````3"\```D````04```"@````3'```+````2"P```P` +M``#8``(`#0```&#@```.````#.$```\```#LWP``$````.S@```1````$#$! +M`!(`````S`$`$P```#`D```4````<%``!@```#\EH```````````````````````(@` +M`0#4L@``G"$``-2R``#4L@``U+(``#@*```8Q`$`B-$``-2R``#4L@``H"<` +M`*`G``"@)P``H"<``*`G``"@)P``H"<``-2R``#4L@``U+(``-2R``#H1@`` +MU+(``-2R``#4L@``U+(``-2R``!LT0``U+(``-2R```(QP```````!CL```< +M[```M`(``*`"```X,`$`D+\``,2A`0"XOP``[*$!`-R_```4H@$`+#B``"B, +M@````````````````````````````````````````````````````````0`" +M``(``P`$``0`!0`&``8`!P`(``@`"0`*``H`"P`,``P`#0`.``X`#P`F`"<` +M*``I`"H`1@!&`$<`2`!(`$D`2@!*`$L`3`!H`&D`:@!J`&L`;`!L`&T`;@!N +M`&\`<`!P`'$`<@!R`',````@````!````)/D``/#W```` +M````4`$````````D^0``;"8!`,`P@``"`````````"3Y``!X)0$`7`>```0` +M````````+/D``/3X```DHX``*@`````````D^0``]/@``#P]@``$```````` +M``````#\^``````````````!`````````!#Y```````````````````````` +M^/@``````````````````"3Y``"XHP$`````````````````)/D``'BC`0!H +M!X``!`````````!N`&X`:0#``*``4`"``+X`4`%]`#X`````````G`($`.8! +M)0!5`P0`W`%C````;@!N`&D`P`"@`%``@`"^`%`!?0`^`````````)P"!`#F +M`24`50,$`-P!8P`````````!`0``T/\``!72````````_P,``-#_```,T@`` +M`````/\!``#0_P``%=(```H`````_`\`T/\```S2```)`````/X#`-#_```5 +MT@``%```````\#_0_P``#-(``!(``````/P'T/\```;2````````_P$``-#_ +M```'T@```````/\#``#0_P``!M(```D`````_@,`T/\```?2```*`````/P/ +M`-#_```&T@``$@``````_`?0_P``!](``!0``````/`_```````````````` +M`0````$````*````!0````4````&````"@````H````&````!`````4````& +M````!0````4````&````!@````8````&````!P````<````'````"`````@` +M```(````!``````````````````````````!`````@````$````"`````0`` +M``$````#`````P````(````!````!````````````````@````$````!```` +M```````````'```````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M``````````````````````````$`````````+`$``%X!```!`````0````$` +M```!`````P``````````````$`P!`'00`0!D#P$`X!`!`'`0`0"H#@$`W!`! +M`#@-`0`T#0$`8.,6`"#6$P``````$`````"``````*``$"<``.@#``#H`P`` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````"`````@````$` +M```!`````@````4````"`````@````4````"`````@````$````!````!0`` +M``4````"````!0````4`````````!0````(````"```````````````````` +M!0````4`````````!0````(````"````!0````4````%``````````4````" +M````!0````$````!`````@````(````"````!0````4````"````!0````$` +M```!`````@````(````"````!0````4````"`````@````4````!`````@`` +M``4````"````!0````4````$````!0````4````!````!0````4````%```` +M`@````(````%````!0````4````!````!0````4````%`````@````(````` +M```````````````````````````````````````````````````````````` +M``````````````````````````````````````````````,````````````` +M````````````"``````````!$``!`````H```4(`!@(0``(@```#P``!0P`& +M`Q```L````/```%#``8$$``"0````H```40`!@41``!````#P``!10`&!A$` +M`.````/```%%``8'$0`!`````H```48`!@@1``(@```#P``!1P`&"1$``L`` +M``/```%'``8*$0`"0````H```4@`!@L2``!````#P``!20`&#!(``.````/` +M``%)``8-$@`!`````H```4H`!@X2``(````"@``!3``&```````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M`````````0```````````````````````````````0(!`0`"`0`!`@("``$! +M``(!`@$"``(``0(#@("`@("`@(`!@`*`@("`@,``D`#0```````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````!```````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M_P````````","@`````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````!`````0`````````````` +M````````````````````````````````!,`!`!4````#````^$"````````` +M`````````*B_`0`%`````P```/A`@`````````````````"+T!``H`````````&$&````````````````` +M`+B^`0`*`````````!A!@`````````````````"XO@$`"@`````````808`` +M````````````````N+X!``H`````````&$&``````````````````+B^`0`* +M`````````!A!@`````````````````"XO@$`"@`````````808`````````` +M````````N+X!``H`````````&$&``````````````````+B^`0`*```````` +M`!A!@`````````````````"XO@$`"@`````````808`````````````````` +MN+X!``H`````````&$&``````````````````+B^`0`*`````````!A!@``` +M``````````````"XO@$`"@`````````808``````````````````N+X!``H` +M````````&$&``````````````````#S!`0`&`````````!A!@``````````` +M``````"DP`$`!0````,```#X0(``````````````````T+P!``H````````` +M&$&``````````````````$"]`0`*`````````!A!@`````````````````"L +MR@$`"@````,```#X0(``````````````````E+T!``H`````````&$&````` +M`````````````#R^`0`*`````````!A!@`````````````````"\O@$`"@`` +M```````808``````````````````/+\!``H`````````&$&````````````` +M``````C``0`*`````````!A!@`````````````````!XP`$`"@`````````8 +M08```````````````````````/A`@`#X0(``N""@`&P@H```@`$`_W_\_P`` +M````````&$&``!A!@`"D(*``.""@``$```#\____```````````X08``.$&` +M`*@@H``\(*``$````,?___\``````````%A!@`!808``K""@`'@AH`!``0`` +M/_[__P``````````>$&``'A!@`"P(*``?"&@```,``#_\?__```````````` +M````````````````````C+T!`!4````#````^$"``````````````````!`' +M@``\EX``&````/R6@`````````````````!_``````````!_```````````` +M````````````.````&@```!T````@````(P```"=````!P````````#_____ +M`````"T!``#=`0``6@(``+H"```*`P``30,``(<#``"Z`P``Z`,``!$$```W +M!```600``'H$``"8!```M`0``,X$``#G!```_@0``!4%```J!0``/@4``%$% +M``!D!0``=04``(8%``"7!0``IP4``+8%``#%!0``TP4``.$%``#N!0``^P4` +M``@&```4!@``(`8``"L&```W!@``0@8``$P&``!7!@``808``&L&``!U!@`` +M?@8``(@&``"1!@``F@8``*(&``"K!@``M`8``+P&``#$!@``S`8``-0&``#; +M!@``XP8``.H&``#R!@``^08````'```'!P``#@<``!0'```;!P``(@<``"@' +M```N!P``-0<``#L'``!!!P``1P<``$T'``!3!P``6`<``%X'``!D!P``:0<` +M`&\'``!T!P``>0<``'\'``"$!P``B0<``(X'``"3!P``F`<``)T'``"B!P`` +MIP<``*L'``"P!P``M0<``+D'``"^!P``P@<``,<'``#+!P``T`<``-0'``#8 +M!P``W`<``.$'``#E!P``Z0<``.T'``#Q!P``]0<``/D'``#]!P```0@```4( +M```("```#`@``!`(```4"```%P@``!L(```?"```(@@``"8(```I"```+0@` +M`#`(```T"```-P@``#L(```^"```00@``$4(``!("```2P@``$\(``!2"``` +M50@``%@(``!;"```7P@``&((``!E"```:`@``&L(``!N"```<0@``'0(``!W +M"```>@@``'T(``"`"```@@@``(4(``"("```BP@``(X(``"1"```DP@``)8( +M``"9"`````````$`````````!P`````````````````````!`@,$!`0$!`4& +M!P@("`@("0H+#`T``&X[:#MB.UP[;CIH.F(Z7#IN.6@Y8CE<.6XX:#AB.%PX +M;C=H-V(W7#=N*6@I8BE<*6XH:"AB*%PH;B=H)V(G7"=N&6@98AE<&6X8:!AB +M&%P8;A=H%V(77!=N%F@68A9<%FX5:!5B%5P5;A1H%&(47!1N$V@38A-<$VX2 +M:!)B$EP2;A%H$6(17!%N$&@08A!<$&X":`)B`EP";@%H`6(!7`%N`&@`8@!< +M`%0```!N.V@[8CM<.VXZ:#IB.EPZ;CEH.6(Y7#EN*V@K8BM<*VXJ:"IB*EPJ +M;BEH*6(I7"EN*&@H8BA<*&XG:"=B)UPG;B9H)F(F7"9N)6@E8B5<)6XD:"1B +M)%PD;B-H(V(C7"-N(F@B8B)<(FXA:"%B(5PA;B!H(&(@7"!N$F@28A)<$FX1 +M:!%B$5P1;A!H$&(07!!7$%(031!)$&X!:`%B`5P!;@!H`&(`7`!4```````` +M```````=````````````````````````````````````@P```)(```"#```` +MD@```.@```#W````Z````/<`````````"0`````````*````.````&@```!T +M````@````(P```"=````!P`````````'````!P````<````````````````` +M````````\$P!``@````#````^$"``%P)@`#<"8``7`J``-P*@``*#1$4"@T1 +M%!D9&1D*"@````````8&!@8)"0D)``8````%!@<(#0X/$!46%Q@9```````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````/&JRL)2^LK*RLK*6L?I%.0PR;-@```#(` +M``"/`(P`B@!#``\`(&@W````$0`^.B`1```")0``#"\```(O.3D`"B4\MT=O +MB@`'%"=B+@```@`7```%$`H@,$````8&"@TC&R,A````#!`4&"`(!```/#@T +M,"PH)"`<&!00#`@$``L'`P`[-S,O*R`````X````G````#P```"`````0`````@```/,````````` +M]`````````#U````````````````````!`````4```#A`PX>X0```.$##A[A +M``````````H````1```````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M``````````````````````````#A`PX>X0```!7V8_:P]OSV1O>0]]CW'_AE +M^*GX[?@O^7#YL/GN^2OZ9_JB^MSZ%/M+^X'[MOOJ^QS\3?Q]_*O\V?P%_3#] +M6?V"_:G]S_WT_1?^.?Y:_GK^F/ZV_M+^[?X&_Q[_-?]+_V#_<_^%_Y;_IO^T +M_\'_S?_8_^'_Z?_P__;_^O_]__________W_^O_V__#_Z?_A_]C_S?_!_[3_ +MIO^6_X7_<_]@_TO_-?\>_P;_[?[2_K;^F/YZ_EK^.?X7_O3]S_VI_8+]6?TP +M_07]V?RK_'W\3?P<_.K[MON!^TO[%/O<^J+Z9_HK^N[YL/EP^2_Y[?BI^&7X +M'_C8]Y#W1O?\]K#V8_9PN8.ZEKNJO+Z]TK[GO_S`$<(GPSW$4\5JQH#'E\BO +MR<;*WLOVS`_.)\]`T%G1L=_-X.GA +M!>,AY#[E6N9WYY/HL.G-ZNKK!^TD[D+O7_!]\9KRN//5]//U$?`26!;0&T0?O"`T**PM(#&8-@PZA#[X0W!'Y$A84 +M,Q50%FT7B1BF&<(:WQO['!<>,Q]/(&HAAB*A([PDUR7R)@PH)BE!*EHK="R. +M+:(`^0#*`>H` +M@@"9`'3111?HHHLN``4'`0,$``4!!0````4&``($``4`!0```0(!`@,$```% +M!@<("0H```D``````````0````(````#``````````0````"````!0````<' +M!P<'!P<'!P<'!P<'!P<'!P<'!P<'!P<'!P<'!P<'!P<'!P<'!P<'!P<'!P<' +M!P<'!P<'!P<'!P8&!@8&!04%!04$!`0$!`,#`P,#`@("`@(!`0$!`0`````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````H`"@`,``L`"P`*``\`#0` +M0``\`(P`;`!8`$@`]`"P`'__!P\?/P$#,````#8````,````$@```!@````D +M````!@````D````%``<"`P0&!D`#@`;`"0`-@!,`&D`=@""`!@`-@!,`&@`G +M`#2`.@!!P`F`$T`=`">`.@!.P%>`89D#,P?9"G,.IA7F'(`@&20S!W,.IA7F +M'%DKS#D`03-(V0JF%8`@62L`0:96@&%9;)W8B9U.[,1.-$B#-"=V8B<:I$$: +M$SNQ$Q$8@1$/_,`/3NS$3B=V8B<:I$$:$SNQ$PW2(`V)G=@)"(S`"`=^X`(`'"(S`"`=X@`<&:9`&L++5!09I +MD`:PLM4%!51`!0540`76'<8$`0,XC@.JJJH"<1S'`:JJJ@K'<1P'``0``&0````` +M````#P`_``$````/`#\``0````\`/P`!````#P`_``$````/`#\``0````\` +M/P`!````#P`_``(````/`#\``0```"(6``"````#```!60``)!8``0````,` +M``%:```F%@`"````!````5H``"@6``(````#```!6P``*A8``H````,```%< +M```L%P``````!````5P``"X7``"````#```!70``,!<``0````,```%>```T +M%P`"`````P```5\``#87``*````#```!8```.!@```````0```%@```\&``! +M`````P```6(``#X8``(````$```!8@``0!@``@````,```%C``!D&P`"```` +M`P```6\``68;``*````#```!<``!:!P```````0```%P``%L'``!`````P`` +M`7(``6X<``(````$```!<@`!!T``0````,```%V``)\'0`"`````P```7<``WX= +M``*````#```!>``#@!X```````0```%X``.$'@`!`````P```7H``X8>``(` +M```$```!>@`$B!X``@````,```%[``2,'P``````!````7P`!)$?``%````# +M```!?@`$E1\``P````0```%_``67'P`"P````P```8``!9D@``!````#```! +M@0`%G2```4````,```&"``6?(``!P````P```8,`!:$@``,````$```!@P`% +MI2$``$````,```&%``4\90$``````#QE`0``````/&4!```````\90$````` +M`#QE`0``````/&4!```````\90$``````#QE`0``````.%\!`!@```#\8`$` +M(````&AF`0`4````7&=BM>9-FNQ%CYT?0(F'^A7O +MZ[+)C@O[[$%GL_U?ZD6_(_=3EN1;F\)U'.&N/6I,6FQ!?@+U3X-<:/11--$( +M^9/B5/%NW%AM>:56:4$<^*$.D&!('^\*!$>+HE +MXTOSHOY=P("*!:T_O"%(<`3QWV/!=W6O8T(P(!KE#OUMOTR!%!@U)B_#X;ZB +M-JS(Y[HK,I7FH,"8&=&>?Z-F1'Y4JSN#"\J,*:?BO!T6=JT[VU9D3G0>%-N2"@QL2.2X79]NO>]#IL2H.:0Q-].+\C+50XM9 +M;K?:C`%DL=*RWO\J-9M.BP``0($!`````0,#`@$#`0$0````(`````` +M`0````(``$``````!```0````$``````\&$```$!`@$"`@,````````````` +M`````````````````````````0`````````````````````````````````` +M``````````````````````````````````````!D````*@````X```````$! +M`````````````0$``````@`!``("`P,#R+\!`-2_`0#@OP$`[+\!`/2_`0#\ +MOP$``0$``0(!`0$``````````/____\````````````````````````````` +M`````````````````````````````(`-````(```@`T``(`-````(```@`T` +M```&````!``````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M````````````````!@````0````L`0``!P`````````2````!`````L````( +M````!````'P^@``)````!````!P````*````!````.@]@``+````!````-@] +M@``,````!````!P````-````!````$0]@``/````!````!8````.```````` +!```` +` +end diff --git a/sys/contrib/dev/iwn/iwnwifi-2030-18.168.6.1.fw.uu b/sys/contrib/dev/iwn/iwnwifi-2030-18.168.6.1.fw.uu new file mode 100644 index 00000000000..9109f65eff5 --- /dev/null +++ b/sys/contrib/dev/iwn/iwnwifi-2030-18.168.6.1.fw.uu @@ -0,0 +1,24904 @@ +Copyright (c) 2006-2012, Intel Corporation. +All rights reserved. + +Redistribution. Redistribution and use in binary form, without +modification, are permitted provided that the following conditions are +met: + +* Redistributions must reproduce the above copyright notice and the + following disclaimer in the documentation and/or other materials + provided with the distribution. +* Neither the name of Intel Corporation nor the names of its suppliers + may be used to endorse or promote products derived from this software + without specific prior written permission. +* No reverse engineering, decompilation, or disassembly of this software + is permitted. + +Limited patent license. Intel Corporation grants a world-wide, +royalty-free, non-exclusive license under patents it now or hereafter +owns or controls to make, have made, use, import, offer to sell and +sell ("Utilize") this software, but solely to the extent that any +such patent is necessary to Utilize the software alone, or in +combination with an operating system licensed under an approved Open +Source license as listed by the Open Source Initiative at +http://opensource.org/licenses. The patent license shall not apply to +any other combinations which include this software. No hardware per +se is licensed hereunder. + +DISCLAIMER. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 +COPYRIGHT OWNER 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. +begin-base64 644 iwlwifi-2030-18.168.6.1.fw +AAAAAElXTAoyMDMwIGZ3IHYxOC4xNjguNi4xIGJ1aWxkIDAKAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAQaoEgAAAAABAAAAAAAAAAEAAABErAIAICCADwAAQABpIAAAaSBAAGkg +AABpIEAAICCADwAA6ABpIAAAaSBAAGkgAABpIEAAICCADwEANB1pIAAAaSBAAGkgAABKIAAASiEA +AEoiAABKIwAASiQAAEolAABKJgAASicAAEogABBKIQAQSiIAEEojABBKJAAQSiUAEEomABBKJwAQ +SiAAIEohACBKIgAgSiMAIEokACBKJQAgSiYAIEonACBKIAAwSiEAMAokgD+BAABAQSycMEAsnDBC +JBw0CiKAP4AAIIcKIwA3ng8ACEomAHBpIEAASiYAcEomAHBKJgBwSiYAcAAWAHCAAIwvQHggIECH +AAAAAAAAAAAAAPwciLb8HEi2/BwItvwcyLX8HIi1/BxItfwcCLX8HMi0/ByItPwcSLT8HAi0/BzI +s/wciLP8HEiz4H7geATcON018OB4BNw03TPw4HgE3DDdMfDgeATcLN0v8OB4BNwo3S3w4HgE3CTd +K/DgeATcIN0p8OB4BNwc3Sfw4HgE3BjdJfDgeATcFN0j8OB4BNwQ3SHw4HgE3AzdH/DgeATcCN0c +8OB4BNwE3RnwNBQaMDAUGTAsFBgwKBQXMCQUFjAgFBUwHBQUMBgUEzAUFBIwEBQRMAwUEDACxwHG +sCRNM7AkHzPgfuB44HjgeOB44HjgeAokgPAFIEQA4CDBB0Qk/oBBKsQAhAACAC8kAvFCIQEBQiAD +AeggogQEEQQCBBEFAgQRBgIEEQcCBBsIAQQbSAEEG4gBBBvIASwAJQBEIj6BPAAiAEQi/IBAIcEA +4CDBB0AjwwCoIIABARGEAgEbCgEgIMAHBBEEAgQRBQIEGwgB1Afh/wQbSAFEIvyABBEEAskH7/8E +GwgBQiFBAEIgQwCoIIABARGEAgEbCgEgIMAHz3GgAKwvGIGauBihcQDgFAXY4HjPcaAArC8YgbO4 +urgYoV0A4BRk2AoiQIAA2e4AAQAvJgDwSiZAAE4ABgBPACAAiiX/D+B4CiJAgADZzgABAGwAJAAv +JgDwXAAFACsINQhKJkAACHEA2AIhvoDgIMUHQnkB4AIhvoDgIMUHQnnrB+//AeAvLQEAQCVFAAIm +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 +0AEAAHgASQB5AGoA3gBqAKgAAAANAQAApgA/AKcAAQALAT8ADAEBAAQACACcAcwAnQHMANUBzADW +AcwAtAAgABkBIACPAIgA9ACIAJAAIgD1ACIAkQAEAPYABACFAAAAhgAAAIcAVQCIAAAAiQCqAIoA +AACLAN0AjAAAAIUAAQCGAAEAhwBVAIgAAACJAKoAigAAAIsA3QCMAAAAhQACAIYAAwCHAFUAiAAA +AIkAqgCKAAAAiwDdAIwAAACFAAMAhgAHAIcAVQCIAAAAiQCqAIoAAACLAN0AjAAAAPv/AAD//wAA +uQHfADsCLQCxABsAFgEbAK8AGwAUARsAbACgANEAoABvAIMAcQCDAHMAMwDUAQYA0AEAAHgASQB5 +AGoA3gBqAKgAAAANAQAApgA/AKcAAQALAT8ADAEBAAQACACcAcwAnQHMANUBzADWAcwAtAAgABkB +IACPAIgA9ACIAJAAIgD1ACIAkQAEAPYABACoAAwADQEMAIUAAACGAAAAhwCZAIgAAACJAKoAigAA +AIsA3QCMAAAAhQABAIYAAQCHAJkAiAAAAIkAqgCKAAAAiwDdAIwAAACFAAIAhgADAIcAmQCIAAAA +iQCqAIoAAACLAN0AjAAAAIUAAwCGAAcAhwCZAIgAAACJAKoAigAAAIsA3QCMAAAA+/8AAP//AAC5 +Ad8AsQAbABYBGwCvABsAFAEbAGwAoADRAKAAbwCDAHEAgwB2AIMAcwAzAG4AMwBwADMAcgAzANcA +MwDUAQYA0AEAAH4APADjADwAeABJAN0ASQB/AFoA5ABaAKoAPwCrAAEADwE/ABABAQB5AGoA3gBq +AKgAAAANAQAApgA3AKcAAQALATcADAEBAAQACACcAcwAnQHMANUBzADWAcwAtAAgABkBIAAxAgwA +MgIMADMCvQA2AgwANwIMADgCvQCgAIgABQGIAKEA1QAGAdUAogAEAAcBBACPAIgA9ACIAJAAIgD1 +ACIAkQAEAPYABACfAAwA+wAMAJQAAACVAAAAnACXAJ0A0ACaAI0AmAARAJYAMwCXAHcAlAABAJUA +AQCcAJcAnQDQAJoAjQCYABEAlgAzAJcAdwCUAAIAlQADAJwAlwCdANAAmgCNAJgAEQCWADMAlwB3 +AJQAAwCVAAcAnACXAJ0A0ACaAI0AmAARAJYAMwCXAHcA+gAAAPkAAAACAZcAAwHQAAABjQD+ABEA +/AAzAP0AdwD6AAEA+QABAAIBlwADAdAAAAGNAP4AEQD8ADMA/QB3APoAAgD5AAMAAgGXAAMB0AAA +AY0A/gARAPwAMwD9AHcA+gADAPkABwACAZcAAwHQAAABjQD+ABEA/AAzAP0AdwCFAAAAhgAAAIcA +VQCIAAAAiQCnAIoAAACLAN4AjAAAAIUAAQCGAAEAhwBVAIgAAACJAKcAigAAAIsA3gCMAAAAhQAC +AIYAAwCHAFUAiAAAAIkApwCKAAAAiwDeAIwAAACFAAMAhgAHAIcAVQCIAAAAiQCnAIoAAACLAN4A +jAAAAOsAAADqAAAA7ABVAO0AAADuAKcA7wAAAPAA3gDxAAAA6wABAOoAAQDsAFUA7QAAAO4ApwDv +AAAA8ADeAPEAAADrAAIA6gADAOwAVQDtAAAA7gCnAO8AAADwAN4A8QAAAOsAAwDqAAcA7ABVAO0A +AADuAKcA7wAAAPAA3gDxAAAApAGAAKEBQAD7/wAA//8AAKUBAQC5Ad8AsQAbABYBGwCvABsAFAEb +AGwAoADRAKAAbwCDAHEAgwB2AIMAcwAzAG4AMwBwADMAcgAzANcAMwDUAQYA0AEAAH4APADjADwA +eABJAN0ASQB/AFoA5ABaAKoAPwCrAAEADwE/ABABAQB5AGoA3gBqAKgAAAANAQAApgA3AKcAAQAL +ATcADAEBAAQACACcAcwAnQHMANUBzADWAcwAtAAgABkBIAAxAgwAMgIMADMCvQA2AgwANwIMADgC +vQCgAIgABQGIAKEA1QAGAdUAogAEAAcBBACPAIgA9ACIAJAAIgD1ACIAkQAEAPYABACfAAwA+wAM +AJQAAACVAAAAnACXAJ0A0ACaAI0AmAARAJYAMwCXAHcAlAABAJUAAQCcAJcAnQDQAJoAjQCYABEA +lgAzAJcAdwCUAAIAlQADAJwAlwCdANAAmgCNAJgAEQCWADMAlwB3AJQAAwCVAAcAnACXAJ0A0ACa +AI0AmAARAJYAMwCXAHcA+gAAAPkAAAACAZcAAwHQAAABjQD+ABEA/AAzAP0AdwD6AAEA+QABAAIB +lwADAdAAAAGNAP4AEQD8ADMA/QB3APoAAgD5AAMAAgGXAAMB0AAAAY0A/gARAPwAMwD9AHcA+gAD +APkABwACAZcAAwHQAAABjQD+ABEA/AAzAP0AdwCFAAAAhgAAAIcAVQCIAAAAiQCnAIoAAACLAN4A +jAAAAIUAAQCGAAEAhwBVAIgAAACJAKcAigAAAIsA3gCMAAAAhQACAIYAAwCHAFUAiAAAAIkApwCK +AAAAiwDeAIwAAACFAAMAhgAHAIcAVQCIAAAAiQCnAIoAAACLAN4AjAAAAOsAAADqAAAA7ABVAO0A +AADuAKcA7wAAAPAA3gDxAAAA6wABAOoAAQDsAFUA7QAAAO4ApwDvAAAA8ADeAPEAAADrAAIA6gAD +AOwAVQDtAAAA7gCnAO8AAADwAN4A8QAAAOsAAwDqAAcA7ABVAO0AAADuAKcA7wAAAPAA3gDxAAAA +pAGAAKEBQAD7/wAA//8AALkBwQDUAQMA0AEEAHgAPADdADwAeQBqAN4AagCoAAEADQEBAAQACACc +AcwAnQHMANUBzADWAcwAtAAgABkBIACPAIgA9ACIAJAAAAD1AAAAkQAGAPYABgCFAAQA6wAEAKQB +gABdAjMASgIOAEwCDgBNAgEArQEHALMBBAC4AQAAuwFWAFACCwBRAgMAUgIBAFMCAABUAgsAVQID +AFYCAQBXAgAAZgIGAGgCBwBqAgcAbAIHAG4CBQBwAgwAfQIGAH8CBwCBAgcAgwIHAIUCBQCHAgwA +tQAhABoBIQBLAgEAoQFAALMAAAAYAQAAlAILAJUCAwCWAgEAlwIAAJgCCwCZAgMAmgIBAJsCAACy +ADAAFwEwAJwCDwChAg8AoAKIAJ8CiACeAogAnQKIAKUCiACkAogAowKIAKICiAD7/wAA//8AALkB +wQDUAQMA0AEAAHgAPADdADwAeQBqAN4AagCoAAEADQEBAAQACACcAcwAnQHMANUBzADWAcwAtAAg +ABkBIACPAIgA9ACIAJAAAAD1AAAAkQAGAPYABgCFAAQA6wAEAKQBgABdAjYASgINAEwCDwBNAgEA +rQEGALMBBAC4AQAAuwFWAFACCwBRAgMAUgIBAFMCAABUAgsAVQIDAFYCAQBXAgAAZgIGAGgCBwBq +AgcAbAIHAG4CBQBwAgwAfQIGAH8CBwCBAgcAgwIHAIUCBQCHAgwAtQAhABoBIQChAUAA+/8AAP// +AAC5AcEA1AEDANABBAB4ADwA3QA8AHkAagDeAGoAqAABAA0BAQAEAAgAnAHMAJ0BzADVAcwA1gHM +ALQAIAAZASAAjwCIAPQAiACQAAAA9QAAAJEABgD2AAYAhQAEAOsABACkAYAAXQIzAEoCDgBMAg4A +TQIBAK0BBwCzAQQAuAEAALsBVgBQAgsAUQIDAFICAQBTAgAAVAILAFUCAwBWAgEAVwIAAJQCCwCV +AgMAlgIBAJcCAACYAgsAmQIDAJoCAQCbAgAAZgIGAGgCBwBqAgYAbAIHAG4CBQBwAgwAfQIGAH8C +BwCBAgYAgwIHAIUCBQCHAgwAsgAwABcBMACzAAAAGAEAAJwCDwChAg8AoAKIAJ8CiACeAogAnQKI +AKUCiACkAogAowKIAKICiAC1ACEAGgEhAKEBQABLAgEA+/8AAP//AAC5AcEA1AEDANABAAB4ADwA +3QA8AHkAagDeAGoAqAABAA0BAQAEAAgAnAHMAJ0BzADVAcwA1gHMALQAIAAZASAAjwCIAPQAiACQ +AAAA9QAAAJEABgD2AAYAhQAEAOsABACkAYAAXQI2AEoCDQBMAg8ATQIBAK0BBgCzAQQAuAEAALsB +VgBQAgsAUQIDAFICAQBTAgAAVAILAFUCAwBWAgEAVwIAAGYCBgBoAgcAagIGAGwCBwBuAgUAcAIM +AH0CBgB/AgcAgQIGAIMCBwCFAgUAhwIMALUAIQAaASEAoQFAAPv/AAAAAAAAAAAAAAEAAAACAAAA +AwAAAAQAAAAFAAAABgAAAAcAAAAAAAAAAAEAAAEBAAAAAAEBAAAAAAAAAAAAAAAAAQEBAQIAAAAN +0hLSE9IU0gzSFdIL0gLSEdIEQwAQFBAJEBEQAUAb0hzSANIKAAsABAAOALUAGgEPAEIAvADDACEB +KAG2ALcAuAC5AL0AvgC/AMAAGwEcAR0BHgEiASMBJAElAQoAAAALAAAAtgAAALcAAAC4AAAAuQAA +ABsBAAAcAQAAHQEAAB4BAAC9AAAAvgAAAL8AAADAAAAAIgEAACMBAAAkAQAAJQEAABLSAAAT0gAA +AAABAAIAAwAsAGQAdACAAIwAoQAHAAAAAAABAAIAAwAAAAAAtxMiALgUIwC5FSQAuxYlALwXJgC9 +GCcAwBkoAMQaKQAHGwAACBwBAAsdAgAMHgMAEB8EACIhBQAkIgYAJiMHACgkCAAqJQkALCYKAC4n +CwAwKAwANCkNADgqDgA8Kw8AQCwQAGQuEQBoLxIAbDATAHAxFAB0MhUAeDMWAHw0FwCANRgAhDYZ +AIg3GgCMOBsAkTocAJU7HQCZPB4AnT0fAKE+IAClPyEAJEkGAixKCgI0Sw0BPEwPAWRNEQFsThMB +dE8VAXxQFwGEURkBlVIdAZ1THwEBBAAAAgUBAAMGAgAEBwMABQgEAAYJBQAHCgYACAsHAAkMCAAK +DQkACw4KAAwPCwANEAwADhENAAFAAAQCQQEEA0ICBARDAwQFRAQEBkUFBAdGBgQIRwcECUgIBAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAhgCGAIYAhgCGAIYAhgCG +AIYAhgCGAIYAhgCGAIcAiAAEAAUABgAGAAcACAAIAAkACQAKAAsADAAMACQAJQAlACYAJwAoACgA +RABFAEYARgBHAEgASABJAEoASgBLAEwAaABoAGkAagBrAGwAbQBtAG4AbwBvAHAAcQBxAHIAcgBy +AHIAcgByAHIAcgByAHIAcgByAHIAcgByAHIAcgByAHIAcgAKAD8AhgCGAIYAhgCGAIYAhgCGAIYA +hgCGAIYAhgCGAIcAiAAEAAUABgAGAAcACAAIAAkACQAKAAsADAAMACQAJQAlACYAJwAoACgARABF +AEYARgBHAEgASABJAEoASgBLAEwAaABoAGkAagBrAGwAbQBtAG4AbwBvAHAAcQBxAHIAcgByAHIA +cgByAHIAcgByAHIAcgByAHIAcgByAHIAcgByAHIAcgAKAD8AAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAHAAARBwA +AEwcAADIGwAAzBsAADQMoABADKAAHBygAEAgoAAoJKAAbBCgABgkoAB4JKAAfCSgAIAkoACEJKAA +UBCgAFQQoABIJqAAYBCgAEwmoABkEKAAaBCgAFwQoABYEKAAMBCgADwQoAA0EKAALAygAACBpAAB +gaQAA4GkAIgkoACMJKAAkCSgAJQkoACYJKAAnCSgAKAkoACkJKAAAAAAAAAAAAAAAAAAAAAAAAEA +AgACAAMABAAEAAUABgAGAAcACAAIAAkACgAKAAsADAAMAA0ADgAOAA8AJgAnACgAKQAqAEYARgBH +AEgASABJAEoASgBLAEwAaABpAGoAagBrAGwAbABtAG4AbgBvAHAAcABxAHIAcgBzAHMAdAB0AHQA +dAB0AHQAdAB0AHQAdAB0AHQAdAB0AHQAdAB0AHQAdAAKAD8AAAAAAAAAAAAAAAAAAAAAAAEAAQAC +AAMAAwAEAAUABQAGAAcABwAIAAkACQAKAAsACwAMAAwADQAOACMAJAAlACYAJwAoACkARABFAEYA +RgBHAEcASABIAEkASQBKAEsAaABoAGkAagBrAGwAbQBtAG4AbwBvAHAAcQBxAHIAcgBzAHMAcwBz +AHMAcwBzAHMAcwBzAHMAcwBzAHMAcwBzAHMAcwAKAD8AAAAAAAAAAAAAAAAAAAAAAAEAAgACAAMA +BAAEAAUABgAGAAcACAAIAAkACgAKAAsADAAMAA0ADgAOAA8AJgAnACgAKQAqAEYARgBHAEgASABJ +AEoASgBLAEwAaABpAGoAagBrAGwAbABtAG4AbgBvAHAAcABxAHIAcgBzAHMAdAB0AHQAdAB0AHQA +dAB0AHQAdAB0AHQAdAB0AHQAdAB0AHQAdAAKAD8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAB +AAIAAwADAAQABQAFAAYABwAHAAgACQAJAAoACgALACQAJQAlACYAJwAoACgARABFAEYARgBHAEgA +SABJAEoASgBLAEwAaABoAGkAagBrAGwAbQBtAG4AbwBvAHAAcQBxAHIAcgByAHIAcgByAHIAcgBy +AHIAcgByAHIAcgByAHIAcgByAHIAcgAKAD8AgIeAAAAAAACyDCwB/////////wAB//8CA////wT/ +/////////////////////wX/Bv8H/wj/Cf8K/wv/DP///w3///8O////D////xD///////////// +/////////////////////////////////xH///8S////E////xT///8V////Fv///xf///8Y//// +Gf///xr///8b/////xz///8d////Hv///x////8g////If//////////////////////IiMk/yUm +J///KP///yn///////////////////////////////////////////////////////////////// +/////////////wABAAEBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAwAA +AAAAAAABAAAAAAAAAKxnAQAAAAAAeIEAAAEAAACIOgIAAgAAAOA5AgADAAAAbKQCAAQAAACsZwEA +BQAAADBPAQAGAAAApBsBAAcAAADATwEACAAAAHw+AAAJAAAA6HMAAAoAAABs6QAACwAAAGw3AAAM +AAAAcKQCAA0AAAC4CgEADgAAAGwLAQAPAAAARAoBABAAAABICwEAEQAAAPCCAQASAAAAoF4CABMA +AAAoKAAAFAAAAFixAQAVAAAArJkBABYAAADgqQEAFwAAAAg5AgAYAAAAkN8BABkAAADkPAEAGgAA +AKhnAQAbAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAP//////////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEaAAABIAAABSABLgkiATIPIAcwEyILMhkgETAdIhUy +IyAbMCciHzEtIAAAMR8AADcZAAA7FQAAAAAAAAAAAAAIrQAACK0AAAitAAA0kwAACK0AAAitAACY +kwAACK0AAAitAAAIrQAACK0AAAitAAAIrQAACK0AAAitAAAIrQAANJwAAHibAABomwAAkJoAAKCb +AABMmgAACK0AAAitAACQogAARKYAAGCoAAAIrQAACK0AAAitAABwrAAAlKEAAMyhAAA4oQAACK0A +AAitAAAIrQAALKwAAAitAAAYoQAACK0AAAitAAAIrQAACK0AAAitAAAIrQAACK0AAAitAAAIrQAA +CK0AAAitAAAIrQAACK0AAAitAAAIrQAACK0AAAitAAAIrQAACK0AAAitAAAIrQAACK0AAAitAAAI +rQAACK0AAAitAAAIrQAACK0AAAitAAAIrQAACK0AAAitAABclAAACK0AAAitAAAIrQAACK0AAAit +AAB8qQAACK0AAAitAAAIrQAACK0AAAitAAC0lwAACK0AAMSXAADAlwAAuJcAALyXAADYkAAACK0A +AJCQAAAIrQAACK0AAAitAAAIrQAACK0AAAitAAAIrQAACK0AABSQAAAIrQAACK0AAAitAAAIrQAA +CK0AAAitAAAIrQAACK0AAAitAAAIrQAACK0AAMSVAAA8lQAACK0AAOCVAAAIrQAAfJQAABCaAAAI +rQAACK0AAFScAAAIrQAACK0AAAitAAAIrQAACK0AANidAACEnAAACK0AAAitAAAIrQAACK0AAAit +AAAIrQAACK0AAAitAAAIrQAACK0AAAitAADIlwAACK0AAAitAAAIrQAAIKkAAAitAAAIrQAACK0A +AMCrAAAIrQAAJKwAALyoAAAIrQAACK0AACCPAAB8qAAACK0AAAitAACYmgAAsJoAAAitAAAIrQAA +cJcAACiRAAAIrQAACK0AAAitAADUoQAANJoAAAitAAAIrQAACK0AAAitAAAIrQAACK0AAGyZAAAI +rQAA7K0AACywAABgrwAANLAAAFivAABQrwAAPLAAACytAAAIrQAACK0AAAitAAAIrQAACK0AAAit +AAAIrQAACK0AAHiXAAAIrQAACK0AAAitAAAIrQAACK0AAAitAAAIrQAA0LAAAOSxAABAjwAAYI8A +AAitAAAIrQAACK0AAAitAAAIrQAAXJEAAAitAAAIrQAACK0AAAitAAAIrQAACK0AAAitAAAIrQAA +CK0AAAitAAAIrQAACK0AAAitAAAIrQAACK0AAAitAAAIrQAACK0AAAitAAAIrQAACK0AAAitAAAI +rQAACK0AAAitAAAIrQAACK0AAAitAABgkQAAgJIAAPCRAABEsAAAoI8AAAyTAACkkwAACK0AAAit +AAAIrQAACK0AAJCTAACUkwAACK0AAAitAAA4kwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAtL8AAOC9AAAswQAA +KMAAAEDCAAAAAAEA/////wAAAAD//////////wEAAAAQDwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAEAAAAAAND+AAAAAAAAAAAAAAAAGCCgACAgoABAIaAASCGgABwgoAAkIKAARCGgAEwh +oAAoIKAAMCCgAGghoABwIaAALCCgADQgoABsIaAAdCGgADggoAA8IKAAeCGgAHwhoAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAABAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAMQNAAAA/wMAZA4AAAD/BQAIDgAAAP8tAOQNAAAA/z0AgA0AAAD/BACoDQAAAP8lACjtAAAA +/90ALA4AABAQTAAAAAAAAAAAAAABAQA8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PBUV +FRU8PDw8FRUVFTw8PDwAAAAAAAAAAAAAAAAAAAAAPDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8 +PDw8PDwVFRUVPDw8PBUVFRU8PDw8AAAAAAAAAAAAAAAAAAAAADw8PDw8PDw8PDw8PDw8PDw8PDw8 +PDw8PDw8PDw8PDw8FRUVFTw8PDwVFRUVPDw8PAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/wAAAAEA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWBAAALAXAADgEwAA +PBEAAHwZAADwFAAAmBYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAPAJgAC0vYAAGAAAAHS9gAAAAAAAAAAAAAAAAAAAAAAAFDoB +ANTOAACQJAAA1M4AANTOAADUzgAAEAcAAJRWAgDs+gAA1M4AANTOAAAkLAAAJCwAACQsAAAkLAAA +JCwAACQsAAAkLAAA1M4AANTOAADUzgAA1M4AAAhbAADUzgAA1M4AANTOAADUzgAA1M4AAND6AADU +zgAA1M4AAGTsAAAAAAAAwB8BAMQfAQC0AgAAoAIAAMSBAQAAAAAAQN4AAGAbAgBo3gAAkBsCAIze +AADAGwIADEiAAKCygAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABIIgAAECIAALi+gAAAAgAA +AAAAAIgvAQBYLwEAuMCAAEAFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACMLwEAdH4BAEjHgABU +AAAAAAAAAIgvAQBIfQEA+MWAAFABAAAAAAAAiC8BADB3AQBACoAACAAAAAEAAACILwEAVC4BAAAA +AABQAQAAAAAAAIgvAQC8dwEAoECAAAIAAAAAAAAAiC8BAMh2AQA8CoAABAAAAAAAAACQLwEAWC8B +AJzHgAAqAAAAAAAAAIgvAQBYLwEAxE+AAAgAAAAAAAAAAAAAAGAvAQAAAAAAAAAAAAEAAAAAAAAA +dC8BAAAAAAAAAAAAAAAAAAAAAABcLwEAAAAAAAAAAAAAAAAAiC8BABQeAgAAAAAAAAAAAAAAAACI +LwEA1B0CAEgKgAAEAAAAAAAAAG4AbgBpAMAAoABQAIAAvgBQAX0APgABAAEAAQBYAigA5gEtAFUD +PADcAWMAAABuAG4AaQDAAKAAUACAAL4AUAF9AD4AAQABAAEAWAIoAOYBLQBVAzwA3AFjAAAAAAAA +AAEBAAAIOQEAFdIAAAAAAAD/AwAACDkBAAzSAAAAAAAA/wEAAAg5AQAV0gAACgAAAAD8DwAIOQEA +DNIAAAkAAAAA/gMACDkBABXSAAAUAAAAAADwPwg5AQAM0gAAEgAAAAAA/AcIOQEABtIAAAAAAAD/ +AQAACDkBAAfSAAAAAAAA/wMAAAg5AQAG0gAACQAAAAD+AwAIOQEAB9IAAAoAAAAA/A8ACDkBAAbS +AAASAAAAAAD8Bwg5AQAH0gAAFAAAAAAA8D8AAAAAAAAAAAAAAAABAAAAAQAAAAoAAAAFAAAABQAA +AAYAAAAKAAAACgAAAAYAAAAEAAAABQAAAAYAAAAFAAAABQAAAAYAAAAGAAAABgAAAAYAAAAHAAAA +BwAAAAcAAAAIAAAACAAAAAgAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAACAAAAAQAAAAIAAAAB +AAAAAQAAAAMAAAADAAAAAgAAAAEAAAAEAAAAAAAAAAAAAAACAAAAAQAAAAEAAAAAAAAAAAAAAAcA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAsAQAAXgEAAAEAAAABAAAA +AQAAAAEAAAADAAAAAAAAAAAAAADQRwEAfE0BAChMAQAATgEAeE0BAEhLAQD8TQEAPEkBADhJAQBg +4xYAINYTAAAAAAAQAAAAAIAAAAAAoAAQJwAA6AMAAOgDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAIAAAACAAAAAQAAAAEAAAACAAAABQAAAAIAAAACAAAABQAAAAIAAAACAAAAAQAAAAEAAAAFAAAA +BQAAAAIAAAAFAAAABQAAAAAAAAAFAAAAAgAAAAIAAAAAAAAAAAAAAAAAAAAFAAAABQAAAAAAAAAF +AAAAAgAAAAIAAAAFAAAABQAAAAUAAAAAAAAABQAAAAIAAAAFAAAAAQAAAAEAAAACAAAAAgAAAAIA +AAAFAAAABQAAAAIAAAAFAAAAAQAAAAEAAAACAAAAAgAAAAIAAAAFAAAABQAAAAIAAAACAAAABQAA +AAEAAAACAAAABQAAAAIAAAAFAAAABQAAAAQAAAAFAAAABQAAAAEAAAAFAAAABQAAAAUAAAACAAAA +AgAAAAUAAAAFAAAABQAAAAEAAAAFAAAABQAAAAUAAAACAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAwAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAAAEQAAEAAAACgAABQgAGAhAAAiAAAAPA +AAFDAAYDEAACwAAAA8AAAUMABgQQAAJAAAACgAABRAAGBREAAEAAAAPAAAFFAAYGEQAA4AAAA8AA +AUUABgcRAAEAAAACgAABRgAGCBEAAiAAAAPAAAFHAAYJEQACwAAAA8AAAUcABgoRAAJAAAACgAAB +SAAGCxIAAEAAAAPAAAFJAAYMEgAA4AAAA8AAAUkABg0SAAEAAAACgAABSgAGDhIAAgAAAAKAAAFM +AAYBeAAwAAAAUAAABLY8BgJ4AEQAAABQAAAEuTwGA3kACAAAAFAAAAS7PAYEeQAcAAAAUAAABL48 +BgV5ADAAAABQAAAEwDwGBnkARAAAAFAAAATDPAYHegAIAAAAUAAABMU8Bgh6ABwAAABQAAAEyDwG +CXoAMAAAAFAAAATKPAYKegBEAAAAUAAABM08Bgt7AAgAAABQAAAEzzwGDHsAHAAAAFAAAATSPAYN +ewAwAAAAUAAABNQ8Bg58ABAAAABQAAAE2j4GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQIBAQACAQABAgIC +AAEBAAIBAgECAAIAAQIDgICAgICAgIABgAKAgICAgMAAkADQAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAbgAAAG4AAgBuAG4AbgACAGkAaQBuAAEAwADAAOgAAQCgAKAA +NgEDAFAAUAD1AAEAgACAAOgAAQC+AL4AvgABAFABUAFQAQEAfQB9AK8AAwA+AD4APgABAAEAAQAB +AAEAAQABAAEAAQABAAEAAQABAFgCWAJYAgEAKAAoACgAAQDmAeYB5gEBAC0ALQAtAAEAVQNVA1UD +AQA8ADwAPAABANwB3AHcAQEAYwBjAGMAAQAAAAAAAAAAADIABQAyAAUAAgAIAGQAoAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAP8AAAAAAAAAjAqMCgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAACAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA4EgCABUAAAADAAAAOG6AAAAAAAAAAAAAAAAAAPhBAgAG +AAAAAAAAAFhugAAAAAAAAAAAAAAAAACESAIABQAAAAMAAAA4boAAAAAAAAAAAAAAAAAAdF4AABUA +AAADAAAAOG6AAAAAAAAAAAAAAAAAAGxIAgAKAAAAAwAAADhugAAAAAAAAAAAAAAAAAAsRQIABAAA +AAAAAABYboAAAAAAAAAAAAAAAAAALEUCAAQAAAAAAAAAWG6AAAAAAAAAAAAAAAAAACxFAgAEAAAA +AAAAAFhugAAAAAAAAAAAAAAAAAAsRQIABAAAAAAAAABYboAAAAAAAAAAAAAAAAAALEUCAAQAAAAA +AAAAWG6AAAAAAAAAAAAAAAAAABxGAgAGAAAAAAAAAFhugAAAAAAAAAAAAAAAAABURgIABAAAAAAA +AABYboAAAAAAAAAAAAAAAAAAVEYCAAQAAAAAAAAAWG6AAAAAAAAAAAAAAAAAAFRGAgAEAAAAAAAA +AFhugAAAAAAAAAAAAAAAAABURgIABAAAAAAAAABYboAAAAAAAAAAAAAAAAAAHEYCAAYAAAAAAAAA +WG6AAAAAAAAAAAAAAAAAABxGAgAGAAAAAAAAAFhugAAAAAAAAAAAAAAAAAAcRgIABgAAAAAAAABY +boAAAAAAAAAAAAAAAAAAVEYCAAQAAAAAAAAAWG6AAAAAAAAAAAAAAAAAAFRGAgAEAAAAAAAAAFhu +gAAAAAAAAAAAAAAAAAA4QwIACgAAAAAAAABYboAAAAAAAAAAAAAAAAAAKEUCAAoAAAAAAAAAWG6A +AAAAAAAAAAAAAAAAAChFAgAKAAAAAAAAAFhugAAAAAAAAAAAAAAAAAAoRQIACgAAAAAAAABYboAA +AAAAAAAAAAAAAAAAKEUCAAoAAAAAAAAAWG6AAAAAAAAAAAAAAAAAAChFAgAKAAAAAAAAAFhugAAA +AAAAAAAAAAAAAAAoRQIACgAAAAAAAABYboAAAAAAAAAAAAAAAAAAKEUCAAoAAAAAAAAAWG6AAAAA +AAAAAAAAAAAAAChFAgAKAAAAAAAAAFhugAAAAAAAAAAAAAAAAAAoRQIACgAAAAAAAABYboAAAAAA +AAAAAAAAAAAAKEUCAAoAAAAAAAAAWG6AAAAAAAAAAAAAAAAAAChFAgAKAAAAAAAAAFhugAAAAAAA +AAAAAAAAAAAoRQIACgAAAAAAAABYboAAAAAAAAAAAAAAAAAA1EoCAAYAAAAAAAAAWG6AAAAAAAAA +AAAAAAAAACRKAgAFAAAAAwAAADhugAAAAAAAAAAAAAAAAAAoQgIACgAAAAAAAABYboAAAAAAAAAA +AAAAAAAAyEICAAoAAAAAAAAAWG6AAAAAAAAAAAAAAAAAADxdAgAKAAAAAwAAADhugAAAAAAAAAAA +AAAAAACEQwIACgAAAAAAAABYboAAAAAAAAAAAAAAAAAAeEQCAAoAAAAAAAAAWG6AAAAAAAAAAAAA +AAAAABBHAgAKAAAAAAAAAFhugAAAAAAAAAAAAAAAAADURwIACgAAAAAAAABYboAAAAAAAAAAAAAA +AAAA5EgCAAoAAAAAAAAAWG6AAAAAAAAAAAAAAAAAAJBJAgAKAAAAAAAAAFhugAAAAAAAAAAAAAAA +AADoSQIABgAAAAAAAABYboAAAAAAAAAAAAAAAAAA+EkCAAYAAAAAAAAAWG6AAAAAAAAAAAAAAAAA +ABxKAgABAAAAAAAAAFhugAAAAAAAAAAAAAAAAAAUTAIACgAAAAEAAAB4boAAAAAAAAAAAAAAAAAA +iEsCAAUAAAABAAAAeG6AAAAAAAAAAAAAAAAAACxPAgAGAAAAAQAAAHhugAAAAAAAAAAAAAAAAACA +TwIABgAAAAEAAAB4boAAAAAAAAAAAAAAAAAAoFICAAoAAAABAAAAeG6AAAAAAAAAAAAAAAAAAAAA +AAA4boAAOG6AALggoABsIKAAAIABAP9//P8AAAAAAAAAAFhugABYboAApCCgADggoAABAAAA/P// +/wAAAAAAAAAAeG6AAHhugACoIKAAPCCgABAAAADH////AAAAAAAAAACYboAAmG6AAKwgoAB4IaAA +QAEAAD/+//8AAAAAAAAAALhugAC4boAAsCCgAHwhoAAADAAA//H//wAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAHxDAgAVAAAAAwAAADhugAAAAAAAUAAAAAAAAADwCYAAtL2AABgAAAB0vYAAAAAAAAAA +AADwCYAAtL2AABgAAAB0vYAAAAAAAAAAAAAAAAAA8AmAALS9gAAYAAAAdL2AAAAAAAAAAAAAAAAA +AH8AAAAAAAAAAH8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP8AAP//AAABAAAAAAAAAAcAAAAAAAAA +AAAAAAAAAAB4bAEARGsBAAAAAAAAAAAAAAAAAAAAAADcbgEAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADcbgEARG0BAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAB4bAEARGsBAAAAAAD8agEAAAAAABRrAQDcbgEARG0BAAAAAAAAAAAA +AAAAABBtAQD8agEA3GoBAAAAAAB4bAEARGsBANhrAQB4awEAAAAAABRrAQDcbgEARG0BAPxqAQDc +agEAjGwBAAAAAAD8agEA3GoBAAcAAAAAAAAABgAAAAIAAAACAAAAAgAAAAIAAAAGAAAAAgAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAcAAAABAAAABgAAAAcAAAAHAAAABwAAAAcAAAABAAAABgAA +AAEAAAABAAAAAAAAAAEAAAABAAAAAQAAAAcAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAA +AgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAcAAAADAAAAAwAAAAcAAAAEAAAAAwAAAAMAAAAD +AAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAcAAAAEAAAABAAAAAQAAAAEAAAABwAAAAMA +AAAEAAAABAAAAAQAAAAEAAAAAwAAAAQAAAAEAAAABAAAAAAAAAAAAQIDBAQEBAQFBgcICAgICAkK +CwwNAABuO2g7YjtcO246aDpiOlw6bjloOWI5XDluK2grYitcK24qaCpiKlwqbiloKWIpXCluKGgo +YihcKG4naCdiJ1wnbiZoJmImXCZuJWglYiVcJW4kaCRiJFwkbiNoI2IjXCNuImgiYiJcIm4haCFi +IVwhbiBoIGIgXCBiE1wTbhJoEmISXBJuEWgRYhFcEW4QaBBiEFwQbgJoAmICXAJuAWgBYgFcAW4A +aABUAAAAbjtoO2I7XDtuOmg6YjpcOm45aDliOVw5bitoK2IrXCtuKmgqYipcKm4paCliKVwpbiho +KGIoXChuJ2gnYidcJ24maCZiJlwmbiVoJWIlXCVuJGgkYiRcJG4jaCNiI1wjbiJoImIiXCJuIWgh +YiFcIW4gaCBiIFwgbhJoEmISXBJuEWgRYhFcEW4QaBBiEFwQVxBSEE0QSRBuAWgBYgFcAW4AaABi +AFwAVAAAAAAAAAAAAAAAHQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAxKMBAAgAAAADAAAAOG6AAFwLgADcC4AAXAyAANwMgAAKDREUCg0RFBkZGRkKCgAAAAAA +AAYGBgYJCQkJAAYAAAAFBgcIDQ4PEBUWFxgZAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AA8arKwlL6ysrKyspax+kU5DDJs2AAAANwCako9IAAAPACBoNwAAABEAPjogEQAAAiUAAAwvAAAC +Lzk5AAolPLpHcY0ABxctaTQAAAIAFwAABRAKIDBAAAAGBgoABQoPDSQcJCIAAAAAR5ivr0kAAAwQ +FBggCAQAADw4NDAsKCQgHBgUEAwIBAALBwMAOzczLysnIx8bFxMPCwcDMTA6NTQ6MzkAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIgAAABAAAAAdAAAANwAAAG8AAACgAAAA +cAAAAAUAAAD2AAAAWgAAAGkAAAAAAAAAaQAAAAIAAAAfAAAASAAAACAAAABIAAAACAAAAAEAAAAz +AAAAfwAAADQAAAB/AAAANwAAAAEAAAA4AAAATwAAADsAAAB/AAAAPAAAAH8AAAAxAAAAAAAAADIA +AAAAAAAANQAAAAAAAAA2AAAAAAAAADkAAAAAAAAAOgAAAAAAAAANAAAAKgAAAA4AAAB6AAAADwAA +ABAAAADzAAAASAAAAPQAAABIAAAAAAAAAAEBAQEBAQEBAgICAgICAgIDAwMDAwMDAwECAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAABAAAAAUAAAChAw4e4QAAAKEDDh7hAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKEDDh7hAAAAFfZj9rD2/PZG95D3 +2Pcf+GX4qfjt+C/5cPmw+e75K/pn+qL63PoU+0v7gfu2++r7HPxN/H38q/zZ/AX9MP1Z/YL9qf3P +/fT9F/45/lr+ev6Y/rb+0v7t/gb/Hv81/0v/YP9z/4X/lv+m/7T/wf/N/9j/4f/p//D/9v/6//3/ +/////////f/6//b/8P/p/+H/2P/N/8H/tP+m/5b/hf9z/2D/S/81/x7/Bv/t/tL+tv6Y/nr+Wv45 +/hf+9P3P/an9gv1Z/TD9Bf3Z/Kv8ffxN/Bz86vu2+4H7S/sU+9z6ovpn+iv67vmw+XD5L/nt+Kn4 +Zfgf+Nj3kPdG9/z2sPZj9nC5g7qWu6q8vr3Svue//MARwifDPcRTxWrGgMeXyK/Jxsrey/bMD84n +z0DQWdFy0ozTptS/1drW9NcO2SnaRNtf3Hrdlt6x383g6eEF4yHkPuVa5nfnk+iw6c3q6usH7STu +Qu9f8H3xmvK489X08/UR9y/4TPlq+oj7pvzE/eL+AAAeATwCWgN4BJYFtAbRB+8IDQorC0gMZg2D +DqEPvhDcEfkSFhQzFVAWbReJGKYZwhrfG/scFx4zH08gaiGGIqEjvCTXJfImDCgmKUEqWit0LI4t +py7AL9kw8TEKMyI0OjVRNmk3gDiWOa06wzvZPO89BD8ZQC5BQkJWQ2pEfUUAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACqqqqqqqqqqqqqqq6qqqqqKP8AzKqq +AACqqgDMqqoAAABAAMAAAAAAAFAA8ABAAPCqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq +qgAAAAAAAAAAAAAAAAAAAAD///8A////Af8CA////wQF/wn/BwoGCAsAAQECAQICAwEBAQEBAQEB +AgICAgICAgIDAwMDAwMDAwQEBAQEBAQEAQICAgICAgMDAwMDAwMDAwMDAwMDBAQEBAQEBAQEBAQE +BAQEBAQEBAQEBAQEAAAAxDYAABA5AABEMwAARDIAAMQyAADkNwAADDIAAAw4AACMOAAAOgECAdUA +3wDaAKIAdQB/AIoFKgM5AagBigXKAtkASAEBAw8HChQ3bmoBGgHZAOgACgG6AHkAiADKAUoB4gD5 +AMoB6gCCAJkAdNFFF+iiiy4ABQcBAwQABQEFAAAABQYAAgQABQAFAAABAgECAwQAAAUGBwgJCgAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAc +6QAA4OsAAPDmAAABAAAAAAAAAAEAAAACAAAAAwAAAAAAAAAEAAAAAgAAAAUAAAAHBwcHBwcHBwcH +BwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcGBgYGBgUFBQUF +BAQEBAQDAwMDAwICAgICAQEBAQEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +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 +AgMDA6RIAgCwSAIAvEgCAMhIAgDQSAIA2EgCAAEBAAECAQEBAAAAAAEAAAAAAAAAAAAAAAAAAAAB +AAAAAAAAAAEAAAABAAAAAAAAAAEAAAAAAAAAAQAAAAAAAAABAAAAAAAAAAAAAAABAAAAAAAAAAAA +AAD/////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACADQAAACAA +AIANAACADQAAACAAAIANAAAABgAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAEAAAABA3AgAgIIAPAABAAGkgAABpIEAAaSAAAGkgQAAgIIAPAADoAGkgAABp +IEAAaSAAAGkgQAAgIIAPAABg6mkgAABpIEAAaSAAAEogAABKIQAASiIAAEojAABKJAAASiUAAEom +AABKJwAASiAAEEohABBKIgAQSiMAEEokABBKJQAQSiYAEEonABBKIAAgSiEAIEoiACBKIwAgSiQA +IEolACBKJgAgSicAIEogADBKIQAwCiSAP4EAAEBBLJwwQCycMEIkHDQKIoA/gAAgZQojADeeDcAG +SiYAcGkgQABKJgBwSiYAcEomAHBKJgBwABYAcIAABCxAeCAgQIcAAAAAAAAAAAAA/ByItvwcSLb8 +HAi2/BzItfwciLX8HEi1/BwItfwcyLT8HIi0/BxItPwcCLT8HMiz/ByIs/wcSLPgfuB4BNw43TXw +4HgE3DTdM/DgeATcMN0x8OB4BNws3S/w4HgE3CjdLfDgeATcJN0r8OB4BNwg3Snw4HgE3BzdJ/Dg +eATcGN0l8OB4BNwU3SPw4HgE3BDdIfDgeATcDN0f8OB4BNwI3Rzw4HgE3ATdGfA0FBowMBQZMCwU +GDAoFBcwJBQWMCAUFTAcFBQwGBQTMBQUEjAQFBEwDBQQMALHAcawJE0zsCQfM+B+4HjgeOB44Hjg +eOB4CiSA8AUgRADgIMEHRCT+gEEqxACEAAIALyQC8UIhAQFCIAMB6CCiBAQRBAIEEQUCBBEGAgQR +BwIEGwgBBBtIAQQbiAEEG8gBLAAlAEQiPoE8ACIARCL8gEAhwQDgIMEHQCPDAKgggAEBEYQCARsK +ASAgwAcEEQQCBBEFAgQbCAHUB+H/BBtIAUQi/IAEEQQCyQfv/wQbCAFCIUEAQiBDAKgggAEBEYQC +ARsKASAgwAfPcaAArC8YgZq4GKGVBOAQBdjgeM9xoACsLxiBs7i6uBihgQTgEGTYCiJAgADZ7gAB +AC8mAPBKJkAATgAGAE8AIACKJf8P4HgKIkCAANnOAAEAbAAkAC8mAPBcAAUAKwg1CEomQAAIcQDY +AiG+gOAgxQdCeQHgAiG+gOAgxQdCeesH7/8B4C8tAQBAJUUAAiZ88QAAIAAAKEAB6CBiAy8gAIAv +IUsAAiG+gMAghgHCIYYA4H4RACAASiAAEEogQBAOIkIALyALEs4gRYCKJf8PCAAFAC8tAQBAJUUA +AiZ88QAAIAAAKEABSiZAAOggIgMvIACALyFLAAIhvoDAIIYBwiGGAEomAABCIP6QziCCAUQgfpDO +IYIB4H4JAAAA4HgKJgDwiiC/D8ogZADgfy8gAwDgf4og/w/8HIix/BxIsfwcCLHhw+HC4cHhwAfA +HBzAMeHA4H8BwPHAFg3P/wh2WnIBiUCJELgYugd6AokIuEd4Q4kHegCG4oYHIIcARYkEiRC6GLhH +eEaJCLpHeEeJB3oBhqOGByCIAAiJSYkYuBC6B3oKiQi4B3oLiUd4TYkHfwyJELoYuEd4TokIui+J +R3gneAd9SiNAIV7wzgygBQDZCHfqcMYMoAUB2Qd/6HC6DKAFAtkHfwlwsgygBQPZIIbneAchBwBB +KwAGngygBQDZCHMpcJYMoAUB2Qd7SXCKDKAFAtkHe2lwggygBQPZIYZneAchCABBLgAmbgygBQDZ +CHMKcGYMoAUB2Qd7KnBaDKAFAtkHe4hwUgygBQPZ4oZneAd/QS0AFkIMoAUA2QhzinA2DKAFAdkH +e8hwLgygBQLZB3uqcCIMoAUD2aOGZ3gHfUEvAAYSDKAFANkIc0EtABQPeAYMoAUB2Qd7QS8AEg94 +9gugBQLZB3svIAcC6gugBQPZJIZneAchBQBBKAAW2gugBQDZCHNBLwAED3jKC6AFAdkHe0EtABIP +eL4LoAUC2Qd773iyC6AFA9lneGWGB3tBLwAWogugBQDZmHBBKAAUD3iWC6AFAdkHJAQAQS8AAg94 +hgugBQLZByQEAK94egugBQPZJoYHIAABByEWAEEtABZmC6AFANkIdUEvABQPeFYLoAUB2Qd9QSgA +Eg94SgugBQLZB30vIMcBPgugBQPZp3inhkIjU6BBLQkEQSsQBAd9IOZBLRcUQS4AIi8gxxBBLQoS +LyOHFUEtAQIvJEcDQS4UJEErAgIvJUchLyfHJS8nBwAvIUcSLyKHEi8gByQvIUcgLyQHJS8mhwBB +LQAGBPXWCqAFBNkEII8PAP8AAOpwxgqgBQTZ/9kQuSR4B3/ocLYKoAUE2f/ZCLkkeAd/CXCmCqAF +BNkPeCCG53gneEEoAQYAGkIgQSgBBAEaQiBBKAECAxoCIAIaQiBBKwAGdgqgBQTZBCCDDwD/AAAp +cGYKoAUE2f/ZELkkeAd7SXBWCqAFBNn/2Qi5JHgHe2lwRgqgBQTZD3ghhmd4J3hBKAEGBBpCIEEo +AQQFGkIgQSgBAgcaAiAGGkIgQS4AJhoKoAUE2QQggw8A/wAACnAKCqAFBNn/2RC5JHgHeypw+gmg +BQTZ/9kIuSR4B3uIcOoJoAUE2Q94IoZneCd4QSgBBggaQiBBKAEECRpCIEEoAQILGgIgChpCIEEt +ABa6CaAFBNkEIIMPAP8AAIpwqgmgBQTZ/9kQuSR4B3vIcJoJoAUE2f/ZCLkkeAd7qnCKCaAFBNkP +eCOGZ3gneEEoAQYMGkIgQSgBBA0aQiBBKAECDhpCIIEB7/8PGgIg4HjxwE4Jz/+eCSAACHUA2SjY +FSVCEBUlAxDAg+CCwKLgo8GD4YLBouGjwoPigsKi4qPDg+OCBOFkuMOi2QkigOOjCdsEhRDlOLhO +CaAFBNkPeAoJoAUA2Qh2AIUwuA94OgmgBQTZD3j2CKAFAdkHfgCFKLgPeCIJoAUE2Q943gigBQLZ +B34AjRIJoAUE2Q94zgigBQPZx3gApQGFOLj6CKAFBNkPeLYIoAUA2Qh2AYUwuA945gigBQTZD3ii +CKAFAdkHfgGFKLgPeM4IoAUE2Q94igigBQLZB34Ejb4IoAUE2Q94egigBQPZx3gBpQKFOLimCKAF +BNkPeGIIoAUA2Qh2AoUwuA94kgigBQTZD3hOCKAFAdkHfgKFKLgPeHoIoAUE2Q94NgigBQLZB34I +jWoIoAUE2Q94JgigBQPZx3gCpQOFOLhSCKAFBNkPeA4IoAUA2Qh2A4UwuA94PgigBQTZD3j6D2AF +AdkHfgOFKLgPeCYIoAUE2Q944g9gBQLZB34MjRYIoAUE2Q940g9gBQPZYbuA48d4rgbt/wOlGQDP +//HArg+P/2GJQIkQuxi6Z3piiQi7Z3pjiQDfZ3pAoESJZYkYuhC7R3tGiQi6R3tHiWd6QaBIiWmJ +GLoQu0d7SokIukd7S4lnekKgbYlMiRC7GLpnem6JCLsviWd6Ct0nekOgw4AIc0EuABQPeIoPYAUE +2SCDBCCADwD/AAAHIQQAQS4AEg94cg9gBQTZBCCAD/8AAAAHJAQAz3heD2AFBNkEIIAPAAAA/wck +BABBLgAWRg9gBQTZD3jPcYAA2GTwIcEDByAAASd4IYMEoyd4IoMFoyd4I4MGo2G9J3gHo0AjAASD +DXWQAectB4//4cUIdRHw4HjgeOB44HjgeOB44HjgeOB44HjgeOB44HjgeOB44HhhvYwl/5/t9eB/ +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 +ACCBIDIO4Aypcslw1g6v/wpxCPDJcM4Or/8AJUEUQOVAJcAf8QjEgwDaA/AA3UAmARcAJUAUWWH+ +DeAMAidCE3kHT/7xwB4PT/7PdYAAIAoAFQUQMw1VAs92gADYSc93gACYYAPwABUFEAGFQC0BAiV4 +IIYHCEAAAKbwJ0ARQHhz6EkHT/4KIcAP63LPcAAAhidj26kAIABKJIAAosHhxULBQSgCAgd6QSgB +BEd5z3KBAEgaxrkqYiUK3wEIFAMxz3WBAIgdqXFWeUCBCwiBAEKREQrAAEeJ7wregYDYA/AGicHF +4H+iwOB+4HjgfuB4CMiVuAgaGDAJyJu4CRoYMAvIiriNuJC4CxoYMOB+4HjxwOHFCHU+iM9wgABM +I0CAQCUAFAO5NXlZYcIN4AwK2r4P7/+pcKUGT/7gePHApcFBwELBDBwAMRAcQDHPcYAAsJc0GcAP +MBkADywZwA4oGYAOJBlADs9wgACwlyAYQAvPcIAAsJccGAALz3CAALCXGBjACs9wgACwlxQYgArP +cIAAsJcQGMAIz3CAALCXDBiACM9wgACwlwgYQAjPcYAANJeAGQAIfBnAB3gZgAd0GUAHcBkAB2wZ +AAdoGYAGZBlABmAZAAZcGcAFWBmABVQZQAVQGQAFTBnABEgZgAREGUAEQBkABO+hzqGtoYyhLBnA +AigZgAIkGUACIBkAAhwZwAEYGYABFBlAARAZAAFjoWogAAPYGQAAaiDAAtQZAABqIIAC0BkAAGog +QAHIGQAAaiAAAcQZAABqIMAAwBkAAGoggAC8GQAAaiBAALgZAABqIAAAtBkAAGoggAHMGQAAQMMB +wALBUyfNNQwUBjBTJcQ1UybFNde6qXOiDSAFEBQHMHoPYAwA2C4IAAPPcAAArd4yD4AAlgogDgDY +z3WgAKwvGYWJuBmlz3CAAJgIAICA4MogIQLKISEAyA5hBc8hYQbPcIAAnAgBgCkIXgBA2c9wgABw +iiawMLknsBmF8LgZhQXyjrgZpQfwirgZpQXw3gugBgDYDQMADeB4ANtgqCGoQqjgfwPg8cBODE/+ +z3OAACw0z3WAAGRLQIUM6QCjlOriCSABD9jWDqAGCNgB2AClDPAA3sCjCOqmCCABD9iiDqAGCNjA +pXkET/7xwAYMT/7PcIAAjAgAgM92gABUG6CGz3eAAFAbBCCCDw8AAOAEIoEPAQAAAHJpRHtnfaCm +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 +/gDMByL3yiPiCAHY0cDgfgnZ4H8goOB48cDaDa/1ANjPdYAA3DQghUB5IIUpCJAA5QjQAOt2YHkB +2Lhwz3AAALoZCiHAD8lyiiPPB4EHL/eKJIMPYHkB2CCFEQhQAGB5AdgghX8I0QBgeQLYCugghWB5 +AtgghSUIUQBgeQPYjuh+CQAAIIVgeQjYEHnPcIAAsBHaDI/2FfAghWB5AtgghQ8IUABgeQLYIIUb +CJEAYHkI2BB5z3CAAKgOsgyP9gfYhvDrdmB5Ati4cM9wAAC7GQohwA/JcoojTgX1Bi/3iiSDD2B5 +AdjtCJEAIIVgeQLYgOAghQjYCPRAeRB5z3CAAHQMB/BAeRB5z3CAAIwNXgyP9l3wYHkC2JHoIIXr +d2B5Adi4cGfYBrgKIcAP6XKKI44MnQYv94okgw/PcIAA1DQggGB5ANgghU0IEQNgeQjYIIUPCJAA +YHkI2CCFkehgeQLYIIUbCFEAYHkI2BB5z3WAAAAW9guv9qlwqXAl8GB5CNgQec91gAC8FOILr/ap +cKlwG/BgeQLYgeAghQjYDPRAec91gAAsGBB5wguv9qlwqXAL8EB5z3WAAOgWEHmuC6/2qXCpcC4M +j/YB2MIOgAM2DI/9oglAAM4IAAB9BI/1CHFYiQGAAqGI6lmJgOLCIKIAwCChAAKh4H7gePHA4cXP +cIAA1DQggKHBYHkE2I8IUQCCCy/8iiDMDoMIUQDPdYAALCwAhQHgAKUXCFEAAdnPcKAAyBwxoM4J +oAYocEokwHCoIIACz3EBAEJpz3CgAOwnJqCLcZIKr/aKIEYJAIVCIECAAKUH9ADZz3CgAMgcMaAA +FAUxTCVAgMohwg/KIsIHyiCCDwAArCjKI4IPAABHAzwFIvfKJCIA0QOv9aHA4H7gePHASguP9c92 +gAAsLACGAeAAphUIUQAB2M9xoADIHBGhQgmABs91gADcNCCFYHkA2NcIkQDH2JS4z3WgAOwnBqXP +dwAAgivPcAMAgisGpc9wAwDCRAalz3ADAAIsBqXPcAMAQkUGpc9xAADCdM9wAwDCdAalz3ADAIJv +BqXPcAMAgmwGpcbYkLgGpSal1gigBgrYz3AAAIJsBqXKCKAGCtjPcAAAAiwGpboIoAYK2M9wAABC +RQalrgigBgrYz3AAAIJvBqWeCKAGCtjmpZYIoAYK2M9wAADCRAaligigBgrYz3ATAMYABqV6CKAG +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 +gACEnAGADwhRAM9wgABQOweAz3GAAPhHIIFCIQGAyiFiAJDpz3GAAHzXLIGK6c9xgABk1xYJb/cj +gQoO7/8C2dHA4H7xwOIJj/HPcIAANCwEgKCAz3CAAGxHAIBCIACAyiBiADa9h+jPcIAAiEeODYAA +z3CAAGTXIYjPcIAAwEfPdoAAhJyK6SCAQiEBgMohYgAE6SCGreliDYAAz3CAANxHWg2AAAGGoghv +9whxIYYPeB0JBQAKIcAP63KKIA0D19tKJAAAOQMv87hzz3GAAPhHIIFCIQGAyiFiAAbpHWUjhsm9 +Cw1AEFoN7/8A2aUBj/HgeOHF4carChAAQCLDAyS7w7oC8ADalQoVBDMmgnCAALRMQCeNclR9IH3A +iAEZkgMB4AEQggQBGZIAARCCBAEZkgABEIIEARmSAAEQggQBGZIAARCCBAEZkgABEIIEARmSAAEQ +ggQBGZIAARCCBAEZkgABEIIEARmSAAEQggQBGZIAARCCBAEZkgABEIIEARmSAAEQggQBGZIAARCC +BAEZkgABEIIEARmSAEIjQ4Cz9cHG4H/BxeHF4cYj6mNqIrvBugLwANo1ChUBMyaCcIAAoExAJ41y +VH0gfcCABBmQAwTgBBACBAQZkAAEEAIEBBmQAAQQAgQEGZAAQiNDgOP1wcbgf8HF4cXhxqsKEABA +IsMDJLvDugLwANqVChUEMyaCcIAApExAJ41yVH0gfcCQAhmUAwLgAhACBQIZlAACEAIFAhmUAAIQ +AgUCGZQAAhACBQIZlAACEAIFAhmUAAIQAgUCGZQAAhACBQIZlAACEAIFAhmUAAIQAgUCGZQAAhAC +BQIZlAACEAIFAhmUAAIQAgUCGZQAAhACBQIZlAACEAIFAhmUAAIQAgUCGZQAQiNDgLP1wcbgf8HF +8cB6D2/xUyFCAE4iDQHPcqAAFATJggDbDiaCHwAAAAZQccohxg/KIsYHyiCGDwAAxiLKI4YPAACb +AsokZgAUASbzyiXGAIDhyiRNcMoizQDoIC0CTmDPcaAAOAQB4sipHQ1QEBENkBAdDdEQz3CgADgE +aKjPcKAAOARoqM9woAA4BGioYQdP8fHAzg5P8bpwenH6cppzCiIAIQogQCHIdQohwCEKJ0CTz3GB +ANYayidiEBJvFngIYUwjAKAEuIYg/gMFf8ohzA/KIswHyiCMDwAAwSHKI4wPAADuAMokbABwACzz +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 +DxQVIgIAFSaECYCBAADAFsIsEyQEKMARAkYTJAQowBHCXxMkBCjAEQ9NEyIEEMURAgATJPAcwBEB +ABMk7BzAEQAAEyRwABMlEBzAEQAAEyUAABMk4BzAEYAAEyVGaBMkBCjAEQAAEyUBABMkJBDAEQAA +FSQAAAAhDw4aIgAAQBYAARtwDQAAYYAAYyT//hsyAABAFwAAGyUPGw8ieAiAgf8AGzICABtBABsa +KAAAwBYAABslAgAbQAAAG3EBAGRwBwAAYQEAYyQAABskWgiAgQAAQBdQCICBAABAFu0PAGECAGRw +EAAAYQIAYyQBABskWgiAgQAAQBdSCICBAABAFuQPAGEEAGRwBwAAYQIAYyQCABskWgiAgQAAQBdU +CICBAABAFtsPAGEAAB0kAAAAIQACD24JAABhWgiAgQAAQBYAABslVgiAgQAbGigAAAAWAQAbJgAA +ABcNAABhcAiAgQAAQBYCABsmARAbaAAAGyQAAEAXXAiAgQAaGygPGw4idAiAgQAAQBYBABsmAABA +F9wGgIEPGhsiAAAbJQIAG0AAABtxAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAMwDgABgCYAAAAAAAAAAAAAoAIAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAkNERQKDRMXGRkZGQkJAABApoAAAAAAAECm +gAAAAAAAXNuAAEhyAAAwgAAAAAAAADGAAACZuViFMoAAAADKmrgzgAAAAFAAUDSAAAAAUAAANYAA +AABQAFA2gAAAAFAAUDeAAAAAAABQOIAAAAAAAAA5gAAAAFAAUDqAAAAAUABQO4AAAABQAFA8gAAA +AFAAAD2AAACZyQpQPoAAAFW4iMk/gAAAAAAAgjCAAAAAAAAAMYAAAJm5WIUygAAAAMqauDOAAAAA +UABQNIAAAABQAAA1gAAAAFAAUDaAAAAAUABQN4AAAAAAAFA4gAAAAAAAADmAAAAAUABQOoAAAABQ +AFA7gAAAAFAAUDyAAAAAUAAAPYAAAJnJClA+gAAAVbiIyT+AAAAAAACCMIAAAAAAAAAxgAAAAAAA +ADKAAAAAAAAAM4AAAAAAAAA0gAAAmkUAADWAAACqyqrKNoAAAAAgACA3gAAAACAAIDiAAAAAIAAA +OYAAAAAgACA6gAAAqsqqKjuAAAAAEJbKPIAAAAAAAAA9gAAAAAAAAD6AAAAAAAAAP4AAAAAAAAAw +gAAAAAAAADGAAAAAAAAAMoAAAAAAAAAzgAAAAAAAADSAAACaRQAANYAAAKrKqso2gAAAACAAIDeA +AAAAIAAgOIAAAAAgAAA5gAAAACAAIDqAAACqyqoqO4AAAAAQlso8gAAAAAAAAD2AAAAAAAAAPoAA +AAAAAAA/gAAAAAAAAP//AAClAQEAuQHfADsCLQCxABsAFgEbAK8AGwAUARsAbACgANEAoABvAIMA +cQCDAHMAMwDUAQYA0AEAAHgASQB5AGoA3gBqAKgAAAANAQAApgA/AKcAAQALAT8ADAEBAAQACACc +AcwAnQHMANUBzADWAcwAtAAgABkBIACPAIgA9ACIAJAAIgD1ACIAkQAEAPYABACFAAAAhgAAAIcA +VQCIAAAAiQCqAIoAAACLAN0AjAAAAIUAAQCGAAEAhwBVAIgAAACJAKoAigAAAIsA3QCMAAAAhQAC +AIYAAwCHAFUAiAAAAIkAqgCKAAAAiwDdAIwAAACFAAMAhgAHAIcAVQCIAAAAiQCqAIoAAACLAN0A +jAAAAPv/AAD//wAAuQHfADsCLQCxABsAFgEbAK8AGwAUARsAbACgANEAoABvAIMAcQCDAHMAMwDU +AQYA0AEAAHgASQB5AGoA3gBqAKgAAAANAQAApgA/AKcAAQALAT8ADAEBAAQACACcAcwAnQHMANUB +zADWAcwAtAAgABkBIACPAIgA9ACIAJAAIgD1ACIAkQAEAPYABACoAAwADQEMAIUAAACGAAAAhwCZ +AIgAAACJAKoAigAAAIsA3QCMAAAAhQABAIYAAQCHAJkAiAAAAIkAqgCKAAAAiwDdAIwAAACFAAIA +hgADAIcAmQCIAAAAiQCqAIoAAACLAN0AjAAAAIUAAwCGAAcAhwCZAIgAAACJAKoAigAAAIsA3QCM +AAAA+/8AAP//AAC5Ad8AsQAbABYBGwCvABsAFAEbAGwAoADRAKAAbwCDAHEAgwB2AIMAcwAzAG4A +MwBwADMAcgAzANcAMwDUAQYA0AEAAH4APADjADwAeABJAN0ASQB/AFoA5ABaAKoAPwCrAAEADwE/ +ABABAQB5AGoA3gBqAKgAAAANAQAApgA3AKcAAQALATcADAEBAAQACACcAcwAnQHMANUBzADWAcwA +tAAgABkBIAAxAgwAMgIMADMCvQA2AgwANwIMADgCvQCgAIgABQGIAKEA1QAGAdUAogAEAAcBBACP +AIgA9ACIAJAAIgD1ACIAkQAEAPYABACfAAwA+wAMAJQAAACVAAAAnACXAJ0A0ACaAI0AmAARAJYA +MwCXAHcAlAABAJUAAQCcAJcAnQDQAJoAjQCYABEAlgAzAJcAdwCUAAIAlQADAJwAlwCdANAAmgCN +AJgAEQCWADMAlwB3AJQAAwCVAAcAnACXAJ0A0ACaAI0AmAARAJYAMwCXAHcA+gAAAPkAAAACAZcA +AwHQAAABjQD+ABEA/AAzAP0AdwD6AAEA+QABAAIBlwADAdAAAAGNAP4AEQD8ADMA/QB3APoAAgD5 +AAMAAgGXAAMB0AAAAY0A/gARAPwAMwD9AHcA+gADAPkABwACAZcAAwHQAAABjQD+ABEA/AAzAP0A +dwCFAAAAhgAAAIcAVQCIAAAAiQCnAIoAAACLAN4AjAAAAIUAAQCGAAEAhwBVAIgAAACJAKcAigAA +AIsA3gCMAAAAhQACAIYAAwCHAFUAiAAAAIkApwCKAAAAiwDeAIwAAACFAAMAhgAHAIcAVQCIAAAA +iQCnAIoAAACLAN4AjAAAAOsAAADqAAAA7ABVAO0AAADuAKcA7wAAAPAA3gDxAAAA6wABAOoAAQDs +AFUA7QAAAO4ApwDvAAAA8ADeAPEAAADrAAIA6gADAOwAVQDtAAAA7gCnAO8AAADwAN4A8QAAAOsA +AwDqAAcA7ABVAO0AAADuAKcA7wAAAPAA3gDxAAAApAGAAKEBQAD7/wAA//8AAKUBAQC5Ad8AsQAb +ABYBGwCvABsAFAEbAGwAoADRAKAAbwCDAHEAgwB2AIMAcwAzAG4AMwBwADMAcgAzANcAMwDUAQYA +0AEAAH4APADjADwAeABJAN0ASQB/AFoA5ABaAKoAPwCrAAEADwE/ABABAQB5AGoA3gBqAKgAAAAN +AQAApgA3AKcAAQALATcADAEBAAQACACcAcwAnQHMANUBzADWAcwAtAAgABkBIAAxAgwAMgIMADMC +vQA2AgwANwIMADgCvQCgAIgABQGIAKEA1QAGAdUAogAEAAcBBACPAIgA9ACIAJAAIgD1ACIAkQAE +APYABACfAAwA+wAMAJQAAACVAAAAnACXAJ0A0ACaAI0AmAARAJYAMwCXAHcAlAABAJUAAQCcAJcA +nQDQAJoAjQCYABEAlgAzAJcAdwCUAAIAlQADAJwAlwCdANAAmgCNAJgAEQCWADMAlwB3AJQAAwCV +AAcAnACXAJ0A0ACaAI0AmAARAJYAMwCXAHcA+gAAAPkAAAACAZcAAwHQAAABjQD+ABEA/AAzAP0A +dwD6AAEA+QABAAIBlwADAdAAAAGNAP4AEQD8ADMA/QB3APoAAgD5AAMAAgGXAAMB0AAAAY0A/gAR +APwAMwD9AHcA+gADAPkABwACAZcAAwHQAAABjQD+ABEA/AAzAP0AdwCFAAAAhgAAAIcAVQCIAAAA +iQCnAIoAAACLAN4AjAAAAIUAAQCGAAEAhwBVAIgAAACJAKcAigAAAIsA3gCMAAAAhQACAIYAAwCH +AFUAiAAAAIkApwCKAAAAiwDeAIwAAACFAAMAhgAHAIcAVQCIAAAAiQCnAIoAAACLAN4AjAAAAOsA +AADqAAAA7ABVAO0AAADuAKcA7wAAAPAA3gDxAAAA6wABAOoAAQDsAFUA7QAAAO4ApwDvAAAA8ADe +APEAAADrAAIA6gADAOwAVQDtAAAA7gCnAO8AAADwAN4A8QAAAOsAAwDqAAcA7ABVAO0AAADuAKcA +7wAAAPAA3gDxAAAApAGAAKEBQAD7/wAA//8AALkBwQDUAQMA0AEEAHgAPADdADwAeQBqAN4AagCo +AAEADQEBAAQACACcAcwAnQHMANUBzADWAcwAtAAgABkBIACPAIgA9ACIAJAAAAD1AAAAkQAGAPYA +BgCFAAQA6wAEAKQBgABdAjMASgIOAEwCDgBNAgEArQEHALMBBAC4AQAAuwFWAFACCwBRAgMAUgIB +AFMCAABUAgsAVQIDAFYCAQBXAgAAZgIGAGgCBwBqAgcAbAIHAG4CBQBwAgwAfQIGAH8CBwCBAgcA +gwIHAIUCBQCHAgwAtQAhABoBIQBLAgEAoQFAALMAAAAYAQAAlAILAJUCAwCWAgEAlwIAAJgCCwCZ +AgMAmgIBAJsCAACyADAAFwEwAJwCDwChAg8AoAKIAJ8CiACeAogAnQKIAKUCiACkAogAowKIAKIC +iAD7/wAA//8AALkBwQDUAQMA0AEAAHgAPADdADwAeQBqAN4AagCoAAEADQEBAAQACACcAcwAnQHM +ANUBzADWAcwAtAAgABkBIACPAIgA9ACIAJAAAAD1AAAAkQAGAPYABgCFAAQA6wAEAKQBgABdAjYA +SgINAEwCDwBNAgEArQEGALMBBAC4AQAAuwFWAFACCwBRAgMAUgIBAFMCAABUAgsAVQIDAFYCAQBX +AgAAZgIGAGgCBwBqAgcAbAIHAG4CBQBwAgwAfQIGAH8CBwCBAgcAgwIHAIUCBQCHAgwAtQAhABoB +IQChAUAA+/8AAP//AAC5AcEA1AEDANABBAB4ADwA3QA8AHkAagDeAGoAqAABAA0BAQAEAAgAnAHM +AJ0BzADVAcwA1gHMALQAIAAZASAAjwCIAPQAiACQAAAA9QAAAJEABgD2AAYAhQAEAOsABACkAYAA +XQIzAEoCDgBMAg4ATQIBAK0BBwCzAQQAuAEAALsBVgBQAgsAUQIDAFICAQBTAgAAVAILAFUCAwBW +AgEAVwIAAJQCCwCVAgMAlgIBAJcCAACYAgsAmQIDAJoCAQCbAgAAZgIGAGgCBwBqAgYAbAIHAG4C +BQBwAgwAfQIGAH8CBwCBAgYAgwIHAIUCBQCHAgwAsgAwABcBMACzAAAAGAEAAJwCDwChAg8AoAKI +AJ8CiACeAogAnQKIAKUCiACkAogAowKIAKICiAC1ACEAGgEhAKEBQABLAgEA+/8AAP//AAC5AcEA +1AEDANABAAB4ADwA3QA8AHkAagDeAGoAqAABAA0BAQAEAAgAnAHMAJ0BzADVAcwA1gHMALQAIAAZ +ASAAjwCIAPQAiACQAAAA9QAAAJEABgD2AAYAhQAEAOsABACkAYAAXQI2AEoCDQBMAg8ATQIBAK0B +BgCzAQQAuAEAALsBVgBQAgsAUQIDAFICAQBTAgAAVAILAFUCAwBWAgEAVwIAAGYCBgBoAgcAagIG +AGwCBwBuAgUAcAIMAH0CBgB/AgcAgQIGAIMCBwCFAgUAhwIMALUAIQAaASEAoQFAAPv/AAAAAAAA +AgAAAA3SEtIT0hTSDNIV0gvSAtIR0gRDABAUEAkQERABQBvSHNIA0goACwAEAA4AtQAaAQ8AQgC8 +AMMAIQEoAbYAtwC4ALkAvQC+AL8AwAAbARwBHQEeASIBIwEkASUBCgAAAAsAAAC2AAAAtwAAALgA +AAC5AAAAGwEAABwBAAAdAQAAHgEAAL0AAAC+AAAAvwAAAMAAAAAiAQAAIwEAACQBAAAlAQAAEtIA +ABPSAAAAAAEAAgADACwAZAB0AIAAjAChAAcAAAAAAAEAAgADAAAAAAC3EyIAuBQjALkVJAC7FiUA +vBcmAL0YJwDAGSgAxBopAAcbAAAIHAEACx0CAAweAwAQHwQAIiEFACQiBgAmIwcAKCQIAColCQAs +JgoALicLADAoDAA0KQ0AOCoOADwrDwBALBAAZC4RAGgvEgBsMBMAcDEUAHQyFQB4MxYAfDQXAIA1 +GACENhkAiDcaAIw4GwCROhwAlTsdAJk8HgCdPR8AoT4gAKU/IQAkSQYCLEoKAjRLDQE8TA8BZE0R +AWxOEwF0TxUBfFAXAYRRGQGVUh0BnVMfAQEEAAACBQEAAwYCAAQHAwAFCAQABgkFAAcKBgAICwcA +CQwIAAoNCQALDgoADA8LAA0QDAAOEQ0AAUAABAJBAQQDQgIEBEMDBAVEBAQGRQUEB0YGBAhHBwQJ +SAgEBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAhgCGAIYA +hgCGAIYAhgCGAIYAhgCGAIYAhgCGAIcAiAAEAAUABgAGAAcACAAIAAkACQAKAAsADAAMACQAJQAl +ACYAJwAoACgARABFAEYARgBHAEgASABJAEoASgBLAEwAaABoAGkAagBrAGwAbQBtAG4AbwBvAHAA +cQBxAHIAcgByAHIAcgByAHIAcgByAHIAcgByAHIAcgByAHIAcgByAHIAcgAKAD8AhgCGAIYAhgCG +AIYAhgCGAIYAhgCGAIYAhgCGAIcAiAAEAAUABgAGAAcACAAIAAkACQAKAAsADAAMACQAJQAlACYA +JwAoACgARABFAEYARgBHAEgASABJAEoASgBLAEwAaABoAGkAagBrAGwAbQBtAG4AbwBvAHAAcQBx +AHIAcgByAHIAcgByAHIAcgByAHIAcgByAHIAcgByAHIAcgByAHIAcgAKAD8AAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAQACAAIAAwAEAAQABQAGAAYABwAIAAgACQAKAAoACwAMAAwADQAO +AA4ADwAmACcAKAApACoARgBGAEcASABIAEkASgBKAEsATABoAGkAagBqAGsAbABsAG0AbgBuAG8A +cABwAHEAcgByAHMAcwB0AHQAdAB0AHQAdAB0AHQAdAB0AHQAdAB0AHQAdAB0AHQAdAB0AAoAPwAA +AAAAAAAAAAAAAAAAAAAAAQABAAIAAwADAAQABQAFAAYABwAHAAgACQAJAAoACwALAAwADAANAA4A +IwAkACUAJgAnACgAKQBEAEUARgBGAEcARwBIAEgASQBJAEoASwBoAGgAaQBqAGsAbABtAG0AbgBv +AG8AcABxAHEAcgByAHMAcwBzAHMAcwBzAHMAcwBzAHMAcwBzAHMAcwBzAHMAcwBzAAoAPwAAAAAA +AAAAAAAAAAAAAAAAAQACAAIAAwAEAAQABQAGAAYABwAIAAgACQAKAAoACwAMAAwADQAOAA4ADwAm +ACcAKAApACoARgBGAEcASABIAEkASgBKAEsATABoAGkAagBqAGsAbABsAG0AbgBuAG8AcABwAHEA +cgByAHMAcwB0AHQAdAB0AHQAdAB0AHQAdAB0AHQAdAB0AHQAdAB0AHQAdAB0AAoAPwAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAABAAEAAgADAAMABAAFAAUABgAHAAcACAAJAAkACgAKAAsAJAAlACUA +JgAnACgAKABEAEUARgBGAEcASABIAEkASgBKAEsATABoAGgAaQBqAGsAbABtAG0AbgBvAG8AcABx +AHEAcgByAHIAcgByAHIAcgByAHIAcgByAHIAcgByAHIAcgByAHIAcgByAAoAPwCAZYAAAAAAALIM +LAH/////////AAH//wID////BP//////////////////////Bf8G/wf/CP8J/wr/C/8M////Df// +/w7///8P////EP//////////////////////////////////////////////Ef///xL///8T//// +FP///xX///8W////F////xj///8Z////Gv///xv/////HP///x3///8e////H////yD///8h//// +//////////////////8iIyT/JSYn//8o////Kf////////////////////////////////////// +////////////////////////////////////////AAABAAEBAAAAAAAAAAEAAAAAAAAAAAAAAAAA +AAMAAAAAAAAAAQAAAAAAAAB0LQEAAAAAADxrAAABAAAAfNgBAAIAAADw1wEAAwAAAAgjAgAEAAAA +dC0BAAUAAACgEwEABgAAAODmAAAHAAAAKBQBAAgAAABEPgAACQAAAMxeAAAKAAAAUMIAAAsAAABQ +OAAADAAAAAgmAgANAAAAGOAAAA4AAABY4AAADwAAABTgAAAQAAAAVOAAABEAAABEQgEAEgAAAATv +AQATAAAAEDIAABQAAACAawEAFQAAAJRXAQAWAAAAfGcBABcAAABc1wEAGAAAAOyQAQAZAAAAYAYB +ABoAAABwLQEAGwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP//////////AAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADU +jgAA1I4AANSOAADIeQAA1I4AANSOAAAsegAA1I4AANSOAADUjgAA1I4AANSOAADUjgAA1I4AANSO +AADUjgAASIAAAJB/AACAfwAA+H4AALh/AADgfgAA1I4AANSOAAAkhgAA9IgAAKCKAADUjgAA1I4A +ANSOAAA8jgAAPIUAAHSFAADghAAA1I4AANSOAADUjgAA+I0AANSOAADAhAAA1I4AANSOAADUjgAA +1I4AANSOAADUjgAA1I4AANSOAADUjgAA1I4AANSOAADUjgAA1I4AANSOAADUjgAA1I4AANSOAADU +jgAA1I4AANSOAADUjgAA1I4AANSOAADUjgAA1I4AANSOAADUjgAA1I4AANSOAADUjgAA1I4AANSO +AADsegAA1I4AANSOAADUjgAA1I4AANSOAABciwAA1I4AANSOAADUjgAA1I4AANSOAABQewAA1I4A +ALx8AABcfAAA7HsAAEx8AACEdwAA1I4AAFh3AADUjgAA1I4AANSOAADUjgAA1I4AANSOAADUjgAA +1I4AAOR2AADUjgAA1I4AANSOAADUjgAA1I4AANSOAADUjgAA1I4AANSOAADUjgAA1I4AANSOAADU +jgAA1I4AANSOAADUjgAA1I4AAKR+AADUjgAA1I4AAGiAAADUjgAA1I4AANSOAADUjgAA1I4AALCB +AACYgAAA1I4AANSOAADUjgAA1I4AANSOAADUjgAA1I4AANSOAADUjgAA1I4AANSOAADUfAAA1I4A +ANSOAADUjgAA1I4AANSOAADUjgAA1I4AAJCNAADUjgAA8I0AAPyKAADUjgAA1I4AADh1AAC8igAA +1I4AANSOAAAAfwAAGH8AANSOAADUjgAADHsAAMh3AADUjgAA1I4AANSOAAB8hQAAyH4AANSOAADU +jgAA1I4AANSOAADUjgAA1I4AAAh+AADUjgAA/I4AAIiPAABojwAAoI8AADSPAAAcjwAAqI8AAPiO +AADUjgAA1I4AANSOAADUjgAA1I4AANSOAADUjgAA1I4AABR7AADUjgAA1I4AANSOAADUjgAA1I4A +ANSOAADUjgAA1JEAAOCSAADkdQAAWHYAANSOAADUjgAA1I4AANSOAADUjgAA+HcAANSOAADUjgAA +1I4AANSOAADUjgAA1I4AANSOAADUjgAA1I4AANSOAADUjgAA1I4AAGiQAACsjwAAGJEAAPyQAAA8 +kAAAxI8AADCRAADkkAAA1I4AANSOAADUjgAA1I4AANSOAADUjgAA1I4AANSOAAAseAAAGHkAALh4 +AABIkQAAcHYAAKB5AAA4egAA1I4AANSOAADUjgAA1I4AACR6AAAoegAA1I4AANSOAADMeQAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAkJ0AANibAAD0ngAAAJ4AAAigAAAAAAEA/////wAAAAD//////////wEAAAD4 +EwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAND+AAAAAAAAAAAYIKAAICCgAEAh +oABIIaAAHCCgACQgoABEIaAATCGgACggoAAwIKAAaCGgAHAhoAAsIKAANCCgAGwhoAB0IaAAOCCg +ADwgoAB4IaAAfCGgAJgRAAAA/wMAUBMAAAD/BQDcEQAAAP8tALgRAAAA/z0ANBEAAAD/BABcEQAA +AP8lAODEAAAA/90AbBIAABAQTAA4EwAAAP8iAAASAAAA/yYA2BIAAAD/KACkEgAAAP8gANDDAAAA +IAAAFMMAAAD/MAAAAAAAAAAAAAABAQA8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PBUV +FRU8PDw8FRUVFTw8PDwAAAAAAAAAAAAAAAAAAAAAPDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8 +PDw8PDwVFRUVPDw8PBUVFRU8PDw8AAAAAAAAAAAAAAAAAAAAADw8PDw8PDw8PDw8PDw8PDw8PDw8 +PDw8PDw8PDw8PDw8FRUVFTw8PDwVFRUVPDw8PAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAACoBAEAGKsAACwk +AAAYqwAAGKsAABirAACEDAAAFOYBANzRAAAYqwAAGKsAACg1AAAoNQAAKDUAACg1AAAoNQAAKDUA +ACg1AAAYqwAAGKsAABirAAAYqwAAVFYAABirAAAYqwAAGKsAABirAAAYqwAAwNEAABirAAAYqwAA +VMIAAAAAAACQ6wAAlOsAALQCAACgAgAAGEEBAAAAAADQuAAArMQBAPi4AADUxAEAHLkAAPzEAQD0 +pYAAvD2AAGCggAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPAhAAC4IQAA4MCAAAAC +AAAAAAAAFPsAAOT6AADgwoAAQAUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABj7AAD0PQEAcMmA +AFQAAAAAAAAAFPsAAMg8AQAgyIAAUAEAAAAAAAAU+wAAxDYBAHAJgAAIAAAAAQAAABT7AADg+QAA +AAAAAFABAAAAAAAAFPsAAFA3AQBUNoAAAgAAAAAAAAAU+wAAXDYBAGwJgAAEAAAAAAAAABz7AADk ++gAAxMmAACoAAAAAAAAAFPsAAOT6AAC4RIAACAAAAAAAAAAAAAAA7PoAAAAAAAAAAAAAAQAAAAAA +AAAA+wAAAAAAAAAAAAAAAAAAAAAAAOj6AAAAAAAAAAAAAAAAAAAU+wAAIMcBAAAAAAAAAAAAAAAA +ABT7AADgxgEAeAmAAAQAAAAAAAAAbgBuAGkAwACgAFAAgAC+AFABfQA+AAEAAQABAFgCKADmAS0A +VQM8ANwBYwAAAG4AbgBpAMAAoABQAIAAvgBQAX0APgABAAEAAQBYAigA5gEtAFUDPADcAWMAAAAA +AAAAAQEAAPADAQAV0gAAAAAAAP8DAADwAwEADNIAAAAAAAD/AQAA8AMBABXSAAAKAAAAAPwPAPAD +AQAM0gAACQAAAAD+AwDwAwEAFdIAABQAAAAAAPA/8AMBAAzSAAASAAAAAAD8B/ADAQAG0gAAAAAA +AP8BAADwAwEAB9IAAAAAAAD/AwAA8AMBAAbSAAAJAAAAAP4DAPADAQAH0gAACgAAAAD8DwDwAwEA +BtIAABIAAAAAAPwH8AMBAAfSAAAUAAAAAADwPwAAAAAAAAAAAAAAAAEAAAABAAAACgAAAAUAAAAF +AAAABgAAAAoAAAAKAAAABgAAAAQAAAAFAAAABgAAAAUAAAAFAAAABgAAAAYAAAAGAAAABgAAAAcA +AAAHAAAABwAAAAgAAAAIAAAACAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAIAAAABAAAAAgAA +AAEAAAABAAAAAwAAAAMAAAACAAAAAQAAAAQAAAAAAAAAAAAAAAIAAAABAAAAAQAAAAAAAAAAAAAA +BwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAACwBAABeAQAAAQAAAAEA +AAABAAAAAQAAAAMAAAAAAAAAAAAAAMgNAQAsEgEAHBEBAJgSAQAoEgEAYBABAJQSAQDwDgEA7A4B +AGDjFgAg1hMAAAAAABAAAAAAgAAAAACgABAnAADoAwAA6AMAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAgAAAAIAAAABAAAAAQAAAAIAAAAFAAAAAgAAAAIAAAAFAAAAAgAAAAIAAAABAAAAAQAAAAUA +AAAFAAAAAgAAAAUAAAAFAAAAAAAAAAUAAAACAAAAAgAAAAAAAAAAAAAAAAAAAAUAAAAFAAAAAAAA +AAUAAAACAAAAAgAAAAUAAAAFAAAABQAAAAAAAAAFAAAAAgAAAAUAAAABAAAAAQAAAAIAAAACAAAA +AgAAAAUAAAAFAAAAAgAAAAUAAAABAAAAAQAAAAIAAAACAAAAAgAAAAUAAAAFAAAAAgAAAAIAAAAF +AAAAAQAAAAIAAAAFAAAAAgAAAAUAAAAFAAAABAAAAAUAAAAFAAAAAQAAAAUAAAAFAAAABQAAAAIA +AAACAAAABQAAAAUAAAAFAAAAAQAAAAUAAAAFAAAABQAAAAIAAAACAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAABEAABAAAAAoAAAUIABgIQAAIgAAADwAAB +QwAGAxAAAsAAAAPAAAFDAAYEEAACQAAAAoAAAUQABgURAABAAAADwAABRQAGBhEAAOAAAAPAAAFF +AAYHEQABAAAAAoAAAUYABggRAAIgAAADwAABRwAGCREAAsAAAAPAAAFHAAYKEQACQAAAAoAAAUgA +BgsSAABAAAADwAABSQAGDBIAAOAAAAPAAAFJAAYNEgABAAAAAoAAAUoABg4SAAIAAAACgAABTAAG +AXgAMAAAAFAAAAS2PAYCeABEAAAAUAAABLk8BgN5AAgAAABQAAAEuzwGBHkAHAAAAFAAAAS+PAYF +eQAwAAAAUAAABMA8BgZ5AEQAAABQAAAEwzwGB3oACAAAAFAAAATFPAYIegAcAAAAUAAABMg8Bgl6 +ADAAAABQAAAEyjwGCnoARAAAAFAAAATNPAYLewAIAAAAUAAABM88Bgx7ABwAAABQAAAE0jwGDXsA +MAAAAFAAAATUPAYOfAAQAAAAUAAABNo+BgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAgEBAAIBAAECAgIAAQEAAgECAQIA +AgABAgOAgICAgICAgAGAAoCAgICAwACQANAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAABuAAAAbgACAG4AbgBuAAIAaQBpAG4AAQDAAMAA6AABAKAAoAA2AQMAUABQAPUA +AQCAAIAA6AABAL4AvgC+AAEAUAFQAVABAQB9AH0ArwADAD4APgA+AAEAAQABAAEAAQABAAEAAQAB +AAEAAQABAAEAWAJYAlgCAQAoACgAKAABAOYB5gHmAQEALQAtAC0AAQBVA1UDVQMBADwAPAA8AAEA +3AHcAdwBAQBjAGMAYwABAAAAAAAAAAAAMgAFADIABQACAAgAZACgAAMAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACj3 +gAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/wAAAAAAAACMCowK +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAEAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAANDiAQAVAAAAAwAAALBIgAAAAAAAAAAAAAAAAAAc4gEA +BQAAAAMAAACwSIAAAAAAAAAAAAAAAAAAEOIBAAoAAAADAAAAsEiAAAAAAAAAAAAAAAAAANzfAQAK +AAAAAwAAALBIgAAAAAAAAAAAAAAAAAAs4QEACgAAAAAAAADQSIAAAAAAAAAAAAAAAAAALOEBAAoA +AAAAAAAA0EiAAAAAAAAAAAAAAAAAACzhAQAKAAAAAAAAANBIgAAAAAAAAAAAAAAAAAAs4QEACgAA +AAAAAADQSIAAAAAAAAAAAAAAAAAALOEBAAoAAAAAAAAA0EiAAAAAAAAAAAAAAAAAACzhAQAKAAAA +AAAAANBIgAAAAAAAAAAAAAAAAAAs4QEACgAAAAAAAADQSIAAAAAAAAAAAAAAAAAALOEBAAoAAAAA +AAAA0EiAAAAAAAAAAAAAAAAAACzhAQAKAAAAAAAAANBIgAAAAAAAAAAAAAAAAAAs4QEACgAAAAAA +AADQSIAAAAAAAAAAAAAAAAAALOEBAAoAAAAAAAAA0EiAAAAAAAAAAAAAAAAAACzhAQAKAAAAAAAA +ANBIgAAAAAAAAAAAAAAAAAA84gEACgAAAAMAAACwSIAAAAAAAAAAAAAAAAAAlOMBAAUAAAADAAAA +sEiAAAAAAAAAAAAAAAAAADTfAQAKAAAAAAAAANBIgAAAAAAAAAAAAAAAAACk3wEACgAAAAAAAADQ +SIAAAAAAAAAAAAAAAAAA1O0BAAoAAAADAAAAsEiAAAAAAAAAAAAAAAAAAAjgAQAKAAAAAAAAANBI +gAAAAAAAAAAAAAAAAACw4AEACgAAAAAAAADQSIAAAAAAAAAAAAAAAAAAMOEBAAoAAAAAAAAA0EiA +AAAAAAAAAAAAAAAAALDhAQAKAAAAAAAAANBIgAAAAAAAAAAAAAAAAADU4gEACgAAAAAAAADQSIAA +AAAAAAAAAAAAAAAAROMBAAoAAAAAAAAA0EiAAAAAAAAAAAAAAAAAAHDjAQAGAAAAAAAAANBIgAAA +AAAAAAAAAAAAAACA4wEABgAAAAAAAADQSIAAAAAAAAAAAAAAAAAAAAAAALBIgACwSIAAuCCgAGwg +oAAAgAEA/3/8/wAAAAAAAAAA0EiAANBIgACkIKAAOCCgAAEAAAD8////AAAAAAAAAADwSIAA8EiA +AKggoAA8IKAAEAAAAMf///8AAAAAAAAAABBJgAAQSYAArCCgAHghoABAAQAAP/7//wAAAAAAAAAA +MEmAADBJgACwIKAAfCGgAAAMAAD/8f//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOABABUAAAAD +AAAAsEiAAAAAAABQAAAAAAAAAPwIgAC8qYAAGAAAAHypgAAAAAAAAAAAAAAAAAD8CIAAvKmAABgA +AAB8qYAAAAAAAAAAAAAAAAAAfwAAAAAAAAAAfwAAAAAAAAAAAAAAAAAAoIuAAAAAAAAAAAAA//8A +AAEAAAAAAAAABwAAAAAAAAAAAAAAAAAAAAABAgMEBAQEBAUGBwgICAgICQoLDA0AAG47aDtiO1w7 +bjpoOmI6XDpuOWg5YjlcOW4raCtiK1wrbipoKmIqXCpuKWgpYilcKW4oaChiKFwobidoJ2InXCdu +JmgmYiZcJm4laCViJVwlbiRoJGIkXCRuI2gjYiNcI24iaCJiIlwibiFoIWIhXCFuIGggYiBcIGIT +XBNuEmgSYhJcEm4RaBFiEVwRbhBoEGIQXBBuAmgCYgJcAm4BaAFiAVwBbgBoAFQAAABuO2g7Yjtc +O246aDpiOlw6bjloOWI5XDluK2grYitcK24qaCpiKlwqbiloKWIpXCluKGgoYihcKG4naCdiJ1wn +biZoJmImXCZuJWglYiVcJW4kaCRiJFwkbiNoI2IjXCNuImgiYiJcIm4haCFiIVwhbiBoIGIgXCBu +EmgSYhJcEm4RaBFiEVwRbhBoEGIQXBBXEFIQTRBJEG4BaAFiAVwBbgBoAGIAXABUAAAAAAAAAAAA +AAAdAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKGIBAAgAAAADAAAAsEiA +AHQKgAD0CoAAdAuAAPQLgAAKDREUCg0RFBkZGRkKCgAAAAAAAAYGBgYJCQkJAAYAAAAFBgcIDQ4P +EBUWFxgZAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwUmpocJJqampqak5pugT81CYsr +AAAAMgAAAI8AjACKAEMADwAgaDcAAAARAD46IBEAAAIlAAAMLwAAAi85OQAKJTy3R2+KAAcUJ2Iu +AAACABcAABYOFhQAAAAAQkIXAAUQCiAwQAAABgYKEvYV9gb2CfYM9g/2APYDAIAAAAAAWFtjYzEA +AAwQFBggCAQAADw4NDAsKCQgHBgUEAwIBAALBwMAOzczLysnIx8bFxMPCwcDADs3My8rJyMfGxcT +DwsHAzEwOjU3OjIwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACIAAAAQ +AAAAHQAAADcAAABvAAAAoAAAAHAAAAAFAAAA9gAAAFoAAABpAAAAAAAAAGkAAAACAAAAHwAAAEgA +AAAgAAAASAAAAAgAAAABAAAAMwAAAH8AAAA0AAAAfwAAADcAAAABAAAAOAAAAE8AAAA7AAAAfwAA +ADwAAAB/AAAAMQAAAAAAAAAyAAAAAAAAADUAAAAAAAAANgAAAAAAAAA5AAAAAAAAADoAAAAAAAAA +DQAAACoAAAAOAAAAegAAAA8AAAAQAAAA8wAAAEgAAAD0AAAASAAAAAAAAAABAQEBAQEBAQICAgIC +AgICAwMDAwMDAwMBAgAABAAAAAUAAAAAAAAAAAAAAAAAAAAAAAAAoQMOHuEAAAChAw4e4QAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAChAw4e4QAAABX2Y/aw9vz2RveQ99j3H/hl+Kn47fgv+XD5sPnu+Sv6Z/qi ++tz6FPtL+4H7tvvq+xz8Tfx9/Kv82fwF/TD9Wf2C/an9z/30/Rf+Of5a/nr+mP62/tL+7f4G/x7/ +Nf9L/2D/c/+F/5b/pv+0/8H/zf/Y/+H/6f/w//b/+v/9//////////3/+v/2//D/6f/h/9j/zf/B +/7T/pv+W/4X/c/9g/0v/Nf8e/wb/7f7S/rb+mP56/lr+Of4X/vT9z/2p/YL9Wf0w/QX92fyr/H38 +Tfwc/Or7tvuB+0v7FPvc+qL6Z/or+u75sPlw+S/57fip+GX4H/jY95D3Rvf89rD2Y/ZwuYO6lruq +vL690r7nv/zAEcInwz3EU8VqxoDHl8ivycbK3sv2zA/OJ89A0FnRctKM06bUv9Xa1vTXDtkp2kTb +X9x63Zbesd/N4OnhBeMh5D7lWuZ355PosOnN6urrB+0k7kLvX/B98ZryuPPV9PP1Efcv+Ez5avqI ++6b8xP3i/gAAHgE8AloDeASWBbQG0QfvCA0KKwtIDGYNgw6hD74Q3BH5EhYUMxVQFm0XiRimGcIa +3xv7HBceMx9PIGohhiKhI7wk1yXyJgwoJilBKlordCyOLacuwC/ZMPExCjMiNDo1UTZpN4A4ljmt +OsM72TzvPQQ/GUAuQUJCVkNqRH1FqqoDAAAAAACqqgMAAPgAAACqA////wAAUKf0UVNlQX7DpBca +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 +NSYvw+G+ojXMiDkuV5PyVYL8R3qsyOe6KzKV5qDAmBnRnn+jZkR+VKs7gwvKjCnH02s8KHmn4rwd +FnatO9tWZE50HhTbkgoMbEjkuF2fbr3vQ6bEqDmkMTfTi/Iy1UOLWW632owBZLHSnOBJtNj6rAfz +Jc+vyo706UcYENVviPBvSnJcJDjxV8dzUZcjy3yhnOghPt2W3GGGDYUPkOBCfMRxqszYkAUGAfcS +HKPCX2r5rtBpkRdYmSc6uSc42RPrsyszIrvScKmJB6czti0iPJIVIMlJh/+qeFB6pY8D+FmACRca +2mUx18aEuNDDgrApd1oRHst7/KjWbTosAAECBAQAAAAEDAwIBAwEBEAAAACAAAAAAAEAAAACAABA +AAAAAAQAAEAAAABAAAAAAPBhAAABAQIBAgIDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAZAAA +ACoAAAAOAAAAAAABAQAAAAAAAAAAAAEBAAAAAAIAAQACAgMDAwECBP8IEP//lOIBAKDiAQCs4gEA +uOIBAMDiAQDI4gEAAAAAAQAAAAIAAAAEAAAACAAAABAAAAAgAAAAQAAAAIAAAAAbAAAANgQCBAIA +AAAAAQIQBAgAAAACEAQIAAAAAAEBAAECAQEBAAAAAAAAAAD/////AAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACADQAAACAAAIANAACADQAAACAAAIANAAAABgAAAAQA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +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 +HQhBAM9xgAC4NTyRFBQNMQ0NQRDPcYAAEDZZqYvqz3WAAHgKwY2A5gDZyiBBACTyIa0LCpEDAdge +8EEoDQIHfUEoAQSnec93gAB4CqCPUyVFER8NMgTGuQohwA/rcs9wAADNG5/bVQAgAYokgw8PDZ4R +ANgM3CcCz//PdoAACOoWJk0Rp42gr8l1FiVNEQClFBQAMUatx3GAAMjmArUAiQetABlCAQAbQgHH +8fHAggnP/wjIz3KgAMgfDhoYgAnIDxoYgArIEBoYgAsSATYCyCR4ERoYgAzIz3GAAABKLRoYgACB +AeAAocO4VwhRAwvIf9kKuSR4LygBAE4gggcA2A8ggAAEIQGAQiKNAhnyCyNAwBf0z3CgAIgg8CBQ +A892gAA8RwCGz3eAAEBHDQ0BEACHEnBQC4EGoKYAHwAUVQHP/+B4n+HMIO6HzCBOgAb3AnlBaQsK +EQiKIf8PBvAA2Q8hgQBhuRh54H8ocPHAsgjv/4ogDwqCJAI6mnF6clpziHWodwohgCEKIMAhwglg +AJ7BinC6CWAAi3GCxmpwsglgAMlxSnCqCWAAhMGpcKIJYACGwYjF6XCWCWAAqXEqcI4JYACKwQpw +hglgAIzBqXCKwc4O4ACQwotwyXHCDuAAksLJcITBug7gAJTChsCpca4O4ACWwpjGkMCSwcYOoADJ +cprHlMCWwboOoADpcslw6XGODuAAnMKcwJ7BpgwgAI7Cn8XCDeAQBG2OwEoJIAAkbbcIEACGwIrB +gg3v/0AlAhOLcITBdg3v/0AlAhWUwEAlARNuDqAAyXKQwEAlARViDqAA6XLJcOlxNg7gAJzCjMCc +wU4OoADJcmoOoADJcMlwjsE+DCAAQCUCF5LAQCUBEzIOoADJcpbAQCUBFSYOoADpcslw6XH2DeAA +nMKMwJzBEg6gAMlyyXCOwQYMIABAJQIZlghgAEAlABfPcYAA/AoQoYYIYABAJQAZz3GAAPwKEaEH +8ADZz3CAAPwKMKAxoIEHr/+AJAI64HjPcYAAnIDgfwhh4HjxwCoPr//YcCh2SHGIdTIO7//JcAh3 +qXAmDu//qHEIcQAugAMEfyZ/ACtAAyR4ZQev/+V48cDiC8AFz3GAAKgs8CEAAEB4z3CgANAbgNpQ +oM9wgAAwMQCAANkPCB4Cz3CfALj/PaDRwOB+4HhggECBAYAhgVBzzCBBgOEgwQfKICEAMHCG9gT2 +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 +4H7geAHZz3CgAMgcMKBL2c9wpAAcQCSg4H7geM9xAQDkQM9wgABkPuB/IKDPcYAAKKYAgYC44H8A +oeB48cC4cFMggQDPcIAANIAoYIHgyiHCD8oiwgfKIIIPAACVGcokgg8AAP4AJATi9soj4ggB2NHA +4H4J2eB/IKDgePHAhg2v9QDYz3WAAHA+IIVAeSCFKQiQAOUI0ADrdmB5Adi4cM9wAAC6GQohwA/J +coojzwfZA+/2iiSDD2B5AdgghREIUABgeQHYIIV/CNEAYHkC2AroIIVgeQLYIIUlCFEAYHkD2I7o +fgkAACCFYHkI2BB5z3CAAFwTYgyP9hXwIIVgeQLYIIUPCFAAYHkC2CCFGwiRAGB5CNgQec9wgABU +EDoMj/YH2Ibw63ZgeQLYuHDPcAAAuxkKIcAPyXKKI04FTQPv9ookgw9geQHY7QiRACCFYHkC2IDg +IIUI2Aj0QHkQec9wgAAgDgfwQHkQec9wgAA4D+YLj/Zd8GB5AtiR6CCF63dgeQHYuHBn2Aa4CiHA +D+lyiiOODPUC7/aKJIMPz3CAAGg+IIBgeQDYIIVNCBEDYHkI2CCFDwiQAGB5CNgghZHoYHkC2CCF +GwhRAGB5CNgQec91gACsF34Lr/apcKlwJfBgeQjYEHnPdYAAaBZqC6/2qXCpcBvwYHkC2IHgIIUI +2Az0QHnPdYAA2BkQeUoLr/apcKlwC/BAec91gACUGBB5Nguv9qlwqXC2C4/2AdiWC0AD0grP/cIJ +QADOCAAAKQSP9QhxWIkBgAKhiOpZiYDiwiCiAMAgoQACoeB+4HjxwOHFz3CAAGg+IIChwWB5BNiP +CFEA+gtv/IogzA6DCFEAz3WAAEgxAIUB4AClFwhRAAHZz3CgAMgcMaBKDWAGKHBKJMBwqCCAAs9x +AQBCac9woADsJyagi3EOCq/2iiBGCQCFQiBAgAClB/QA2c9woADIHDGgABQFMUwlQIDKIcIPyiLC +B8oggg8AAKwoyiOCDwAARwOUAeL2yiQiAH0Dr/WhwOB+4HjxwPYKj/XPdoAASDEAhgHgAKYVCFEA +AdjPcaAAyBwRob4MQAbPdYAAcD4ghWB5ANjXCJEAx9iUuM91oADsJwalz3cAAIIrz3ADAIIrBqXP +cAMAwkQGpc9wAwACLAalz3ADAEJFBqXPcQAAwnTPcAMAwnQGpc9wAwCCbwalz3ADAIJsBqXG2JC4 +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 +1L5geUigPB0AFJIL7/MC2IECj/HxwCIKj/GGD2AACHXPcaAAyB9FhQzobhEOBgKAZIXEekV7bhnY +ACKFAKEL8G4RAAZEeG4ZGAAc2Bi4FRkYgFECj/HgeIDgAdnAec9wgAAQTeB/IKDxwMoJj/HPcIAA +aD4ggKLBYHkE2IDgqgIBAM9xgABIMQCBAeAAoRcIUQAB2c9woADIHDGgegtgAihw2gjv+QXYz3aA +ANhIDqbPcYAASDEAgQHgAKEVCFEAAdnPcKAAyBwxoE4LYAIocAPYJgiv8slxBNgeCK/yIm4F2BYI +r/IkbgvYDgiv8iZuD9gGCK/yQCYBEjbY+g9v8kAmgRI32PIPb/JAJgETONjmD2/yQCaBE893pwAU +SAiHz3GnAJhHBKYNh89yqwCg/wWmDofPdaAA7CcGphyBB6YXhwimFocJphiCC6YZggymGoINps9w +BQDGAwalxtiQuAalz3AsAAIBBqXPcFoAQgEGpYogiwAGpc9wQACHDQalz3DRAMINBqXPcMAABw4G +pc9wgABIMQCAz3KAAEgxQiBAgACiBvTPcqAAyBwA2BGiAdgIpwDYDacOp89wUAD/AByhAdgXpwDY +Fqf82c9wqwCg/zigc9k5oBqAz3GrAKD/gbgaoc9wKgACDgali3A2DCAAgcEAwc9wgADUpzWmMqAB +wS+gz3AaAAIOBqWLcBYMIACBwQDBz3CAANSnNqYzoAHBMKDPcCYAAg4GpYtw9gsgAIHBAMHPcIAA +1Kc0oDemAcExoM9wgABIMQCAAeDPcYAASDEAoRUIUQDPcaAAyBwB2BGhsglAAgGWELiFIIQABqUC +lhC4hSCFAAalA5YQuIUgiwAGpQSWELiFII8ABqUFlhC4BSCADwAAgg0GpQaWELgFIIAPAADCDQal +B5YQuAUggA8AAAIOBqXPcIAASDEAgM9xgABIMUIgQIAAoQf0z3GgAMgcANgRoQSGK4YIpwWGDacG +hg6nCIYXpwmGFqfPcKsAoP84oCyGOaAthjqgpg2v+Q6Gz3CAAEgxz3GAAEgxAIBCIECAAKEH9M9x +oADIHADYEaF1B2/xosDxwAoPT/F+CAAAz3aAAHhsQg2v9wCGCHUAhhkNABDKD2/7qXBeCO/7oKZK +CS/0Edh2D8/2z3CgACwgMIDPcIAAFAo1B2/xIKDxwLoP7/+hwc9wgAAYTQCABNli2h7bQMCLcLoK +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 ++giAgQAAQBYAABsl9giAgQAbGigAAAAWAQAbJgAAABcNAABhEAmAgQAAQBYCABsmARAbaAAAGyQA +AEAX/AiAgQAaGygPGw4iFAmAgQAAQBYBABsmAABAF3wHgIEPGhsiAAAbJQIAG0AAABtxAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGwEgAC4AYAA +AAAAAAAAAAAoAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHAAAABwAAAAAAAAAHAAAACQ0RFAoNExcZGRkZ +CQkAAOy5gADYggAA1L6AAAAAAADUvoAAAAAAAN8AAAAZAQAAYgEAAL4BAAAyAgAAwwIAAHsDAABi +BAAAhAUAAPIGAAC+CAAAAgsAAAEAAAAAAAAAMIAAAAAAAAAxgAAAmblYhTKAAAAAypq4M4AAAABQ +AFA0gAAAAFAAADWAAAAAUABQNoAAAABQAFA3gAAAAAAAUDiAAAAAAAAAOYAAAABQAFA6gAAAAFAA +UDuAAAAAUABQPIAAAABQAAA9gAAAmckKUD6AAABVuIjJP4AAAAAAAIIwgAAAAAAAADGAAACZuViF +MoAAAADKmrgzgAAAAFAAUDSAAAAAUAAANYAAAABQAFA2gAAAAFAAUDeAAAAAAABQOIAAAAAAAAA5 +gAAAAFAAUDqAAAAAUABQO4AAAABQAFA8gAAAAFAAAD2AAACZyQpQPoAAAFW4iMk/gAAAAAAAgjCA +AAAAAAAAMYAAAAAAAAAygAAAAAAAADOAAAAAAAAANIAAAJpFAAA1gAAAqsqqyjaAAAAAIAAgN4AA +AAAgACA4gAAAACAAADmAAAAAIAAgOoAAAKrKqio7gAAAABCWyjyAAAAAAAAAPYAAAAAAAAA+gAAA +AAAAAD+AAAAAAAAAMIAAAAAAAAAxgAAAAAAAADKAAAAAAAAAM4AAAAAAAAA0gAAAmkUAADWAAACq +yqrKNoAAAAAgACA3gAAAACAAIDiAAAAAIAAAOYAAAAAgACA6gAAAqsqqKjuAAAAAEJbKPIAAAAAA +AAA9gAAAAAAAAD6AAAAAAAAAP4AAAAAAAAD//wAApQEBALkB3wA7Ai0AsQAbABYBGwCvABsAFAEb +AGwAoADRAKAAbwCDAHEAgwBzADMA1AEGANABAAB4AEkAeQBqAN4AagCoAAAADQEAAKYAPwCnAAEA +CwE/AAwBAQAEAAgAnAHMAJ0BzADVAcwA1gHMALQAIAAZASAAjwCIAPQAiACQACIA9QAiAJEABAD2 +AAQAhQAAAIYAAACHAFUAiAAAAIkAqgCKAAAAiwDdAIwAAACFAAEAhgABAIcAVQCIAAAAiQCqAIoA +AACLAN0AjAAAAIUAAgCGAAMAhwBVAIgAAACJAKoAigAAAIsA3QCMAAAAhQADAIYABwCHAFUAiAAA +AIkAqgCKAAAAiwDdAIwAAAD7/wAA//8AALkB3wA7Ai0AsQAbABYBGwCvABsAFAEbAGwAoADRAKAA +bwCDAHEAgwBzADMA1AEGANABAAB4AEkAeQBqAN4AagCoAAAADQEAAKYAPwCnAAEACwE/AAwBAQAE +AAgAnAHMAJ0BzADVAcwA1gHMALQAIAAZASAAjwCIAPQAiACQACIA9QAiAJEABAD2AAQAqAAMAA0B +DACFAAAAhgAAAIcAmQCIAAAAiQCqAIoAAACLAN0AjAAAAIUAAQCGAAEAhwCZAIgAAACJAKoAigAA +AIsA3QCMAAAAhQACAIYAAwCHAJkAiAAAAIkAqgCKAAAAiwDdAIwAAACFAAMAhgAHAIcAmQCIAAAA +iQCqAIoAAACLAN0AjAAAAPv/AAD//wAAuQHfALEAGwAWARsArwAbABQBGwBsAKAA0QCgAG8AgwBx +AIMAdgCDAHMAMwBuADMAcAAzAHIAMwDXADMA1AEGANABAAB+ADwA4wA8AHgASQDdAEkAfwBaAOQA +WgCqAD8AqwABAA8BPwAQAQEAeQBqAN4AagCoAAAADQEAAKYANwCnAAEACwE3AAwBAQAEAAgAnAHM +AJ0BzADVAcwA1gHMALQAIAAZASAAMQIMADICDAAzAr0ANgIMADcCDAA4Ar0AoACIAAUBiAChANUA +BgHVAKIABAAHAQQAjwCIAPQAiACQACIA9QAiAJEABAD2AAQAnwAMAPsADACUAAAAlQAAAJwAlwCd +ANAAmgCNAJgAEQCWADMAlwB3AJQAAQCVAAEAnACXAJ0A0ACaAI0AmAARAJYAMwCXAHcAlAACAJUA +AwCcAJcAnQDQAJoAjQCYABEAlgAzAJcAdwCUAAMAlQAHAJwAlwCdANAAmgCNAJgAEQCWADMAlwB3 +APoAAAD5AAAAAgGXAAMB0AAAAY0A/gARAPwAMwD9AHcA+gABAPkAAQACAZcAAwHQAAABjQD+ABEA +/AAzAP0AdwD6AAIA+QADAAIBlwADAdAAAAGNAP4AEQD8ADMA/QB3APoAAwD5AAcAAgGXAAMB0AAA +AY0A/gARAPwAMwD9AHcAhQAAAIYAAACHAFUAiAAAAIkApwCKAAAAiwDeAIwAAACFAAEAhgABAIcA +VQCIAAAAiQCnAIoAAACLAN4AjAAAAIUAAgCGAAMAhwBVAIgAAACJAKcAigAAAIsA3gCMAAAAhQAD +AIYABwCHAFUAiAAAAIkApwCKAAAAiwDeAIwAAADrAAAA6gAAAOwAVQDtAAAA7gCnAO8AAADwAN4A +8QAAAOsAAQDqAAEA7ABVAO0AAADuAKcA7wAAAPAA3gDxAAAA6wACAOoAAwDsAFUA7QAAAO4ApwDv +AAAA8ADeAPEAAADrAAMA6gAHAOwAVQDtAAAA7gCnAO8AAADwAN4A8QAAAKQBgAChAUAA+/8AAP// +AAClAQEAuQHfALEAGwAWARsArwAbABQBGwBsAKAA0QCgAG8AgwBxAIMAdgCDAHMAMwBuADMAcAAz +AHIAMwDXADMA1AEGANABAAB+ADwA4wA8AHgASQDdAEkAfwBaAOQAWgCqAD8AqwABAA8BPwAQAQEA +eQBqAN4AagCoAAAADQEAAKYANwCnAAEACwE3AAwBAQAEAAgAnAHMAJ0BzADVAcwA1gHMALQAIAAZ +ASAAMQIMADICDAAzAr0ANgIMADcCDAA4Ar0AoACIAAUBiAChANUABgHVAKIABAAHAQQAjwCIAPQA +iACQACIA9QAiAJEABAD2AAQAnwAMAPsADACUAAAAlQAAAJwAlwCdANAAmgCNAJgAEQCWADMAlwB3 +AJQAAQCVAAEAnACXAJ0A0ACaAI0AmAARAJYAMwCXAHcAlAACAJUAAwCcAJcAnQDQAJoAjQCYABEA +lgAzAJcAdwCUAAMAlQAHAJwAlwCdANAAmgCNAJgAEQCWADMAlwB3APoAAAD5AAAAAgGXAAMB0AAA +AY0A/gARAPwAMwD9AHcA+gABAPkAAQACAZcAAwHQAAABjQD+ABEA/AAzAP0AdwD6AAIA+QADAAIB +lwADAdAAAAGNAP4AEQD8ADMA/QB3APoAAwD5AAcAAgGXAAMB0AAAAY0A/gARAPwAMwD9AHcAhQAA +AIYAAACHAFUAiAAAAIkApwCKAAAAiwDeAIwAAACFAAEAhgABAIcAVQCIAAAAiQCnAIoAAACLAN4A +jAAAAIUAAgCGAAMAhwBVAIgAAACJAKcAigAAAIsA3gCMAAAAhQADAIYABwCHAFUAiAAAAIkApwCK +AAAAiwDeAIwAAADrAAAA6gAAAOwAVQDtAAAA7gCnAO8AAADwAN4A8QAAAOsAAQDqAAEA7ABVAO0A +AADuAKcA7wAAAPAA3gDxAAAA6wACAOoAAwDsAFUA7QAAAO4ApwDvAAAA8ADeAPEAAADrAAMA6gAH +AOwAVQDtAAAA7gCnAO8AAADwAN4A8QAAAKQBgAChAUAA+/8AAP//AAC5AcEA1AEDANABBAB4ADwA +3QA8AHkAagDeAGoAqAABAA0BAQAEAAgAnAHMAJ0BzADVAcwA1gHMALQAIAAZASAAjwCIAPQAiACQ +AAAA9QAAAJEABgD2AAYAhQAEAOsABACkAYAAXQIzAEoCDgBMAg4ATQIBAK0BBwCzAQQAuAEAALsB +VgBQAgsAUQIDAFICAQBTAgAAVAILAFUCAwBWAgEAVwIAAGYCBgBoAgcAagIHAGwCBwBuAgUAcAIM +AH0CBgB/AgcAgQIHAIMCBwCFAgUAhwIMALUAIQAaASEASwIBAKEBQACzAAAAGAEAAJQCCwCVAgMA +lgIBAJcCAACYAgsAmQIDAJoCAQCbAgAAsgAwABcBMACcAg8AoQIPAKACiACfAogAngKIAJ0CiACl +AogApAKIAKMCiACiAogA+/8AAP//AAC5AcEA1AEDANABAAB4ADwA3QA8AHkAagDeAGoAqAABAA0B +AQAEAAgAnAHMAJ0BzADVAcwA1gHMALQAIAAZASAAjwCIAPQAiACQAAAA9QAAAJEABgD2AAYAhQAE +AOsABACkAYAAXQI2AEoCDQBMAg8ATQIBAK0BBgCzAQQAuAEAALsBVgBQAgsAUQIDAFICAQBTAgAA +VAILAFUCAwBWAgEAVwIAAGYCBgBoAgcAagIHAGwCBwBuAgUAcAIMAH0CBgB/AgcAgQIHAIMCBwCF +AgUAhwIMALUAIQAaASEAoQFAAPv/AAD//wAAuQHBANQBAwDQAQQAeAA8AN0APAB5AGoA3gBqAKgA +AQANAQEABAAIAJwBzACdAcwA1QHMANYBzAC0ACAAGQEgAI8AiAD0AIgAkAAAAPUAAACRAAYA9gAG +AIUABADrAAQApAGAAF0CMwBKAg4ATAIOAE0CAQCtAQcAswEEALgBAAC7AVYAUAILAFECAwBSAgEA +UwIAAFQCCwBVAgMAVgIBAFcCAACUAgsAlQIDAJYCAQCXAgAAmAILAJkCAwCaAgEAmwIAAGYCBgBo +AgcAagIGAGwCBwBuAgUAcAIMAH0CBgB/AgcAgQIGAIMCBwCFAgUAhwIMALIAMAAXATAAswAAABgB +AACcAg8AoQIPAKACiACfAogAngKIAJ0CiAClAogApAKIAKMCiACiAogAtQAhABoBIQChAUAASwIB +APv/AAD//wAAuQHBANQBAwDQAQAAeAA8AN0APAB5AGoA3gBqAKgAAQANAQEABAAIAJwBzACdAcwA +1QHMANYBzAC0ACAAGQEgAI8AiAD0AIgAkAAAAPUAAACRAAYA9gAGAIUABADrAAQApAGAAF0CNgBK +Ag0ATAIPAE0CAQCtAQYAswEEALgBAAC7AVYAUAILAFECAwBSAgEAUwIAAFQCCwBVAgMAVgIBAFcC +AABmAgYAaAIHAGoCBgBsAgcAbgIFAHACDAB9AgYAfwIHAIECBgCDAgcAhQIFAIcCDAC1ACEAGgEh +AKEBQAD7/wAAAAAAAAIAAAAN0hLSE9IU0gzSFdIL0gLSEdIEQwAQFBAJEBEQAUAb0hzSANIKAAsA +BAAOALUAGgEPAEIAvADDACEBKAG2ALcAuAC5AL0AvgC/AMAAGwEcAR0BHgEiASMBJAElAQoAAAAL +AAAAtgAAALcAAAC4AAAAuQAAABsBAAAcAQAAHQEAAB4BAAC9AAAAvgAAAL8AAADAAAAAIgEAACMB +AAAkAQAAJQEAABLSAAAT0gAAAAABAAIAAwAsAGQAdACAAIwAoQAHAAAAAAABAAIAAwAAAAAAAAAA +AAAAAAAAAAAAAQAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHAAAABwAAAAAAAAADAAAA +BAAAAAMAAAAAAAAA/wMAAAMAAAAAAAAAAAAAAAAAAAABAAAAAQAAAAAAAAAAAAAALAABAAAAAQAB +AAEAAAAAAAAAAAABAAEAAgACAAIAAwADAAQABAAFAAUABgAGAAcABwAIAAgACQAJAAoACgALAAsA +DAAMAA0ADQAOAA4ADwAAAAEAAgAAAAvSDtIN0gjSCdIK0hLSE9IU0hHSENIC0gHSA9IAgAXSBEMb +0hzSBNIARTDSMdIAALUAGgGBAQUABAAPABAACgALAAwATgAAAAAAAQACAAAADdIR0hDSAtIB0gPS +G9IL0gCABdIS0hPSFNIEQwjSCdIK0hzSBtIH0nDSAAC1ABoBgQEEAA8AgwDoAE4BkgD3AF0BBgAI +AAkACgALAAwABQAAAAAAAAAAAAoAAAABAAAAAAAAAAAAAAAAAAAAAwAAAAQAAAADAAAAAAAAAAMA +AAAAAAAAAAAAAAAAAAAAAAAA/wMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAs +AAEAAAAAAAAAAAAAAAAAAAAAAAAAAQABAAEAAAAE0g3SEdIQ0gLSAdID0hvSAIAF0gvSEtIT0hTS +BENw0gAAAAABAAAAAQAAAAEAAAABAAAAAwAAAAIAAAADAAAAAwAAAAAAAAABAAAAAAAAAAAAAAAA +AAAA/wMAAAAAAAC1ABoBgQEEAA8ABgAIAAkACgALAAwAAAAAAAAAAAAsAAEAFQAVABUAAQABAAEA +AAAAAAEAAgAAABzSDdIR0hDSAtIB0gPSG9IL0gCABdIS0hPSFNIEQwbSB9IE0nDSAAC1ABoBgQEF +AAQABgAIAAkACgALAAwAgwCSAOgA9wBOAV0BDwAAAAAAtxMiALgUIwC5FSQAuxYlALwXJgC9GCcA +wBkoAMQaKQAHGwAACBwBAAsdAgAMHgMAEB8EACIhBQAkIgYAJiMHACgkCAAqJQkALCYKAC4nCwAw +KAwANCkNADgqDgA8Kw8AQCwQAGQuEQBoLxIAbDATAHAxFAB0MhUAeDMWAHw0FwCANRgAhDYZAIg3 +GgCMOBsAkTocAJU7HQCZPB4AnT0fAKE+IAClPyEAJEkGAixKCgI0Sw0BPEwPAWRNEQFsThMBdE8V +AXxQFwGEURkBlVIdAZ1THwEBBAAAAgUBAAMGAgAEBwMABQgEAAYJBQAHCgYACAsHAAkMCAAKDQkA +Cw4KAAwPCwANEAwADhENAAFAAAQCQQEEA0ICBARDAwQFRAQEBkUFBAdGBgQIRwcECUgIBAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAhgCGAIYAhgCGAIYAhgCGAIYA +hgCGAIYAhgCGAIcAiAAEAAUABgAGAAcACAAIAAkACQAKAAsADAAMACQAJQAlACYAJwAoACgARABF +AEYARgBHAEgASABJAEoASgBLAEwAaABoAGkAagBrAGwAbQBtAG4AbwBvAHAAcQBxAHIAcgByAHIA +cgByAHIAcgByAHIAcgByAHIAcgByAHIAcgByAHIAcgAKAD8AhgCGAIYAhgCGAIYAhgCGAIYAhgCG +AIYAhgCGAIcAiAAEAAUABgAGAAcACAAIAAkACQAKAAsADAAMACQAJQAlACYAJwAoACgARABFAEYA +RgBHAEgASABJAEoASgBLAEwAaABoAGkAagBrAGwAbQBtAG4AbwBvAHAAcQBxAHIAcgByAHIAcgBy +AHIAcgByAHIAcgByAHIAcgByAHIAcgByAHIAcgAKAD8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAHAAARBwAAEwc +AADIGwAAzBsAADQMoABADKAAHBygAEAgoAAoJKAAbBCgABgkoAB4JKAAfCSgAIAkoACEJKAAUBCg +AFQQoABIJqAAYBCgAEwmoABkEKAAaBCgAFwQoABYEKAAMBCgADwQoAA0EKAALAygAACBpAABgaQA +A4GkAIgkoACMJKAAkCSgAJQkoACYJKAAnCSgAKAkoACkJKAAAAAAAAAAAAAAAAAAAAAAAAEAAgAC +AAMABAAEAAUABgAGAAcACAAIAAkACgAKAAsADAAMAA0ADgAOAA8AJgAnACgAKQAqAEYARgBHAEgA +SABJAEoASgBLAEwAaABpAGoAagBrAGwAbABtAG4AbgBvAHAAcABxAHIAcgBzAHMAdAB0AHQAdAB0 +AHQAdAB0AHQAdAB0AHQAdAB0AHQAdAB0AHQAdAAKAD8AAAAAAAAAAAAAAAAAAAAAAAEAAQACAAMA +AwAEAAUABQAGAAcABwAIAAkACQAKAAsACwAMAAwADQAOACMAJAAlACYAJwAoACkARABFAEYARgBH +AEcASABIAEkASQBKAEsAaABoAGkAagBrAGwAbQBtAG4AbwBvAHAAcQBxAHIAcgBzAHMAcwBzAHMA +cwBzAHMAcwBzAHMAcwBzAHMAcwBzAHMAcwAKAD8AAAAAAAAAAAAAAAAAAAAAAAEAAgACAAMABAAE +AAUABgAGAAcACAAIAAkACgAKAAsADAAMAA0ADgAOAA8AJgAnACgAKQAqAEYARgBHAEgASABJAEoA +SgBLAEwAaABpAGoAagBrAGwAbABtAG4AbgBvAHAAcABxAHIAcgBzAHMAdAB0AHQAdAB0AHQAdAB0 +AHQAdAB0AHQAdAB0AHQAdAB0AHQAdAAKAD8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQABAAIA +AwADAAQABQAFAAYABwAHAAgACQAJAAoACgALACQAJQAlACYAJwAoACgARABFAEYARgBHAEgASABJ +AEoASgBLAEwAaABoAGkAagBrAGwAbQBtAG4AbwBvAHAAcQBxAHIAcgByAHIAcgByAHIAcgByAHIA +cgByAHIAcgByAHIAcgByAHIAcgAKAD8AgIGAAAAAAACyDCwB/////////wAB//8CA////wT///// +/////////////////wX/Bv8H/wj/Cf8K/wv/DP///w3///8O////D////xD///////////////// +/////////////////////////////xH///8S////E////xT///8V////Fv///xf///8Y////Gf// +/xr///8b/////xz///8d////Hv///x////8g////If//////////////////////IiMk/yUmJ/// +KP///yn///////////////////////////////////////////////////////////////////// +/////////wABAAEBAAAAAAAAAAABAAAAAAAAAAAAAAAAAAADAAAAAAAAAAEAAAAAAAAAkDQBAAAA +AAD0egAAAQAAALjOAQACAAAAEM4BAAMAAAA0HwIABAAAAJA0AQAFAAAAwCABAAYAAACc8gAABwAA +AEghAQAIAAAA8DEAAAkAAACkVAAACgAAAFzPAAALAAAAyC4AAAwAAAA4HwIADQAAAMzpAAAOAAAA +eOoAAA8AAABY6QAAEAAAAFjqAAARAAAAEEIBABIAAABE5wEAEwAAAJwmAAAUAAAAUG0BABUAAAAI +WAEAFgAAANBnAQAXAAAAPM0BABgAAACklgEAGQAAANQOAQAaAAAAjDQBABsAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAD//////////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAdJ0AAHSdAAB0nQAAdIkAAHSdAAB0nQAA +2IkAAHSdAAB0nQAAdJ0AAHSdAAB0nQAAdJ0AAHSdAAB0nQAAdJ0AAECSAACIkQAAeJEAAKiQAACw +kQAAZJAAAHSdAAB0nQAAVJQAACSXAAA8mQAAdJ0AAHSdAAB0nQAA3JwAAFiTAACQkwAA/JIAAHSd +AAB0nQAAdJ0AAJicAAB0nQAA3JIAAHSdAAB0nQAAdJ0AAHSdAAB0nQAAdJ0AAHSdAAB0nQAAdJ0A +AHSdAAB0nQAAdJ0AAHSdAAB0nQAAdJ0AAHSdAAB0nQAAdJ0AAHSdAAB0nQAAdJ0AAHSdAAB0nQAA +dJ0AAHSdAAB0nQAAdJ0AAHSdAAB0nQAAdJ0AAHSdAAB0nQAAnIoAAHSdAAB0nQAAdJ0AAHSdAAB0 +nQAA/JkAAHSdAAB0nQAAdJ0AAHSdAAB0nQAA6I0AAHSdAAD4jQAA9I0AAOyNAADwjQAAJIcAAHSd +AADchgAAdJ0AAHSdAAB0nQAAdJ0AAHSdAAB0nQAAdJ0AAHSdAABghgAAdJ0AAHSdAAB0nQAAdJ0A +AHSdAAB0nQAAdJ0AAHSdAAB0nQAAdJ0AAHSdAAAAjAAAeIsAAHSdAAAcjAAAdJ0AALyKAAAokAAA +dJ0AAHSdAABgkgAAdJ0AAHSdAAB0nQAAdJ0AAHSdAACskgAAkJIAAHSdAAB0nQAAdJ0AAHSdAAB0 +nQAAdJ0AAHSdAAB0nQAAdJ0AAHSdAAB0nQAA/I0AAHSdAAB0nQAAdJ0AAPiZAAB0nQAAdJ0AAHSd +AAAwnAAAdJ0AAJCcAACYmQAAdJ0AAHSdAABIhQAAWJkAAHSdAAB0nQAAsJAAAMiQAAB0nQAAdJ0A +AKSNAAB0hwAAdJ0AAHSdAAB0nQAAmJMAAEyQAAB0nQAAdJ0AAHSdAAB0nQAAdJ0AAHSdAACMjwAA +dJ0AAJydAAAongAACJ4AAECeAADUnQAAvJ0AAEieAACYnQAAdJ0AAHSdAAB0nQAAdJ0AAHSdAAB0 +nQAAdJ0AAHSdAACsjQAAdJ0AAHSdAAB0nQAAdJ0AAHSdAAB0nQAAdJ0AANieAADknwAAYIUAANSF +AAB0nQAAdJ0AAHSdAAB0nQAAdJ0AAKSHAAB0nQAAdJ0AAHSdAAB0nQAAdJ0AAHSdAAB0nQAAdJ0A +AHSdAAB0nQAAdJ0AAHSdAAB0nQAAdJ0AAHSdAAB0nQAAdJ0AAHSdAAB0nQAAdJ0AAHSdAAB0nQAA +dJ0AAHSdAAB0nQAAdJ0AAHSdAAB0nQAAqIcAAMSIAAA0iAAATJ4AAOyFAABMiQAA5IkAAHSdAAB0 +nQAAdJ0AAHSdAADQiQAA1IkAAHSdAAB0nQAAeIkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPirAAAkqgAAZK0A +AGisAAB4rgAAAAABAP////8AAAAA//////////8BAAAAeBEAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAABAAAAAADQ/gAAAAAAAAAAAAAAABggoAAgIKAAQCGgAEghoAAcIKAAJCCgAEQhoABM +IaAAKCCgADAgoABoIaAAcCGgACwgoAA0IKAAbCGgAHQhoAA4IKAAPCCgAHghoAB8IaAAMBAAAAD/ +AwDQEAAAAP8FAHQQAAAA/y0AUBAAAAD/PQDsDwAAAP8EABQQAAAA/yUAINAAAAD/3QCYEAAAEBBM +AAAAAAAAAAAAAAEBADw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8FRUVFTw8PDwVFRUV +PDw8PAAAAAAAAAAAAAAAAAAAAAA8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PBUVFRU8 +PDw8FRUVFTw8PDwAAAAAAAAAAAAAAAAAAAAAPDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8 +PDwVFRUVPDw8PBUVFRU8PDw8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/wAAAAEAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA0BIAAAQaAABIFgAAsBMAAMQbAABQFwAA9BgAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAANQJgAB8v4AAGAAAADy/gAAAAAAAAAAAAAAAAAAAAAAAHAwBALS6AAAIJAAAtLoAALS6AAC0 +ugAAOAoAAETfAQAs2gAAtLoAALS6AAAYKgAAGCoAABgqAAAYKgAAGCoAABgqAAAYKgAAtLoAALS6 +AAC0ugAAtLoAADBLAAC0ugAAtLoAALS6AAC0ugAAtLoAABDaAAC0ugAAtLoAAGDPAAAAAAAAEPYA +ABT2AAC0AgAAoAIAAORAAQCIxwAA1LkBALDHAAAEugEA1McAADS6AQBIR4AAaLSAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAADMIQAAlCEAAIDCgAAAAgAAAAAAAOwDAQC8AwEAgMSAAEAFAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAADwAwEApD0BABDLgABUAAAAAAAAAOwDAQB4PAEAwMmAAFAB +AAAAAAAA7AMBAKA2AQAoCoAACAAAAAEAAADsAwEAuAIBAAAAAABQAQAAAAAAAOwDAQAsNwEA4D+A +AAIAAAAAAAAA7AMBADg2AQAkCoAABAAAAAAAAAD0AwEAvAMBAGTLgAAqAAAAAAAAAOwDAQC8AwEA +HE2AAAgAAAAAAAAAAAAAAMQDAQAAAAAAAAAAAAEAAAAAAAAA2AMBAAAAAAAAAAAAAAAAAAAAAADA +AwEAAAAAAAAAAAAAAAAA7AMBAPi7AQAAAAAAAAAAAAAAAADsAwEAuLsBADAKgAAEAAAAAAAAAG4A +bgBpAMAAoABQAIAAvgBQAX0APgABAAEAAQBYAigA5gEtAFUDPADcAWMAAABuAG4AaQDAAKAAUACA +AL4AUAF9AD4AAQABAAEAWAIoAOYBLQBVAzwA3AFjAAAAAAAAAAEBAAAUCwEAFdIAAAAAAAD/AwAA +FAsBAAzSAAAAAAAA/wEAABQLAQAV0gAACgAAAAD8DwAUCwEADNIAAAkAAAAA/gMAFAsBABXSAAAU +AAAAAADwPxQLAQAM0gAAEgAAAAAA/AcUCwEABtIAAAAAAAD/AQAAFAsBAAfSAAAAAAAA/wMAABQL +AQAG0gAACQAAAAD+AwAUCwEAB9IAAAoAAAAA/A8AFAsBAAbSAAASAAAAAAD8BxQLAQAH0gAAFAAA +AAAA8D8AAAAAAAAAAAAAAAABAAAAAQAAAAoAAAAFAAAABQAAAAYAAAAKAAAACgAAAAYAAAAEAAAA +BQAAAAYAAAAFAAAABQAAAAYAAAAGAAAABgAAAAYAAAAHAAAABwAAAAcAAAAIAAAACAAAAAgAAAAE +AAAAAAAAAAAAAAAAAAAAAAAAAAEAAAACAAAAAQAAAAIAAAABAAAAAQAAAAMAAAADAAAAAgAAAAEA +AAAEAAAAAAAAAAAAAAACAAAAAQAAAAEAAAAAAAAAAAAAAAcAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAQAAAAAAAAAsAQAAXgEAAAEAAAABAAAAAQAAAAEAAAADAAAAAAAAAAAAAACI +GQEAFB8BAMQdAQCYHwEAEB8BAPAcAQCUHwEA6BoBAOQaAQBg4xYAINYTAAAAAAAQAAAAAIAAAAAA +oAAQJwAA6AMAAOgDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAACAAAAAQAAAAEAAAACAAAA +BQAAAAIAAAACAAAABQAAAAIAAAACAAAAAQAAAAEAAAAFAAAABQAAAAIAAAAFAAAABQAAAAAAAAAF +AAAAAgAAAAIAAAAAAAAAAAAAAAAAAAAFAAAABQAAAAAAAAAFAAAAAgAAAAIAAAAFAAAABQAAAAUA +AAAAAAAABQAAAAIAAAAFAAAAAQAAAAEAAAACAAAAAgAAAAIAAAAFAAAABQAAAAIAAAAFAAAAAQAA +AAEAAAACAAAAAgAAAAIAAAAFAAAABQAAAAIAAAACAAAABQAAAAEAAAACAAAABQAAAAIAAAAFAAAA +BQAAAAQAAAAFAAAABQAAAAEAAAAFAAAABQAAAAUAAAACAAAAAgAAAAUAAAAFAAAABQAAAAEAAAAF +AAAABQAAAAUAAAACAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAAAAAAAAAAAAAAAA +AAAAAAAIAAAAARAAAQAAAAKAAAFCAAYCEAACIAAAA8AAAUMABgMQAALAAAADwAABQwAGBBAAAkAA +AAKAAAFEAAYFEQAAQAAAA8AAAUUABgYRAADgAAADwAABRQAGBxEAAQAAAAKAAAFGAAYIEQACIAAA +A8AAAUcABgkRAALAAAADwAABRwAGChEAAkAAAAKAAAFIAAYLEgAAQAAAA8AAAUkABgwSAADgAAAD +wAABSQAGDRIAAQAAAAKAAAFKAAYOEgACAAAAAoAAAUwABgF4ADAAAABQAAAEtjwGAngARAAAAFAA +AAS5PAYDeQAIAAAAUAAABLs8BgR5ABwAAABQAAAEvjwGBXkAMAAAAFAAAATAPAYGeQBEAAAAUAAA +BMM8Bgd6AAgAAABQAAAExTwGCHoAHAAAAFAAAATIPAYJegAwAAAAUAAABMo8Bgp6AEQAAABQAAAE +zTwGC3sACAAAAFAAAATPPAYMewAcAAAAUAAABNI8Bg17ADAAAABQAAAE1DwGDnwAEAAAAFAAAATa +PgYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAABAgEBAAIBAAECAgIAAQEAAgECAQIAAgABAgOAgICAgICAgAGAAoCAgICAwACQANAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAD/AAAAAAAAAIwKjAoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAgAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwNoBABUAAAADAAAA0GiAAAAAAAAAAAAAAAAAAGTaAQAF +AAAAAwAAANBogAAAAAAAAAAAAAAAAABY2gEACgAAAAMAAADQaIAAAAAAAAAAAAAAAAAAKNcBAAoA +AAAAAAAA8GiAAAAAAAAAAAAAAAAAAADZAQAKAAAAAAAAAPBogAAAAAAAAAAAAAAAAAAA2QEACgAA +AAAAAADwaIAAAAAAAAAAAAAAAAAAANkBAAoAAAAAAAAA8GiAAAAAAAAAAAAAAAAAAADZAQAKAAAA +AAAAAPBogAAAAAAAAAAAAAAAAAAA2QEACgAAAAAAAADwaIAAAAAAAAAAAAAAAAAAANkBAAoAAAAA +AAAA8GiAAAAAAAAAAAAAAAAAAADZAQAKAAAAAAAAAPBogAAAAAAAAAAAAAAAAAAA2QEACgAAAAAA +AADwaIAAAAAAAAAAAAAAAAAAANkBAAoAAAAAAAAA8GiAAAAAAAAAAAAAAAAAAADZAQAKAAAAAAAA +APBogAAAAAAAAAAAAAAAAAAA2QEACgAAAAAAAADwaIAAAAAAAAAAAAAAAAAAANkBAAoAAAAAAAAA +8GiAAAAAAAAAAAAAAAAAAGjcAQAGAAAAAAAAAPBogAAAAAAAAAAAAAAAAAC82wEABQAAAAMAAADQ +aIAAAAAAAAAAAAAAAAAAGNYBAAoAAAAAAAAA8GiAAAAAAAAAAAAAAAAAALjWAQAKAAAAAAAAAPBo +gAAAAAAAAAAAAAAAAADk5QEACgAAAAMAAADQaIAAAAAAAAAAAAAAAAAAdNcBAAoAAAAAAAAA8GiA +AAAAAAAAAAAAAAAAAFjYAQAKAAAAAAAAAPBogAAAAAAAAAAAAAAAAAAE2QEACgAAAAAAAADwaIAA +AAAAAAAAAAAAAAAAwNkBAAoAAAAAAAAA8GiAAAAAAAAAAAAAAAAAAMTaAQAKAAAAAAAAAPBogAAA +AAAAAAAAAAAAAABk2wEACgAAAAAAAADwaIAAAAAAAAAAAAAAAAAAAAAAANBogADQaIAAuCCgAGwg +oAAAgAEA/3/8/wAAAAAAAAAA8GiAAPBogACkIKAAOCCgAAEAAAD8////AAAAAAAAAAAQaYAAEGmA +AKggoAA8IKAAEAAAAMf///8AAAAAAAAAADBpgAAwaYAArCCgAHghoABAAQAAP/7//wAAAAAAAAAA +UGmAAFBpgACwIKAAfCGgAAAMAAD/8f//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAbNcBABUAAAAD +AAAA0GiAAAAAAAAAAAAAAAAAANQJgAB8v4AAGAAAADy/gAAAAAAAAAAAAAAAAAB/AAAAAAAAAAB/ +AAAAAAAAAAAAAAAAAAAAAAAAOAAAAGgAAAB0AAAAgAAAAIwAAACdAAAABwAAAAAAAAD/////AAAA +AC0BAADdAQAAWgIAALoCAAAKAwAATQMAAIcDAAC6AwAA6AMAABEEAAA3BAAAWQQAAHoEAACYBAAA +tAQAAM4EAADnBAAA/gQAABUFAAAqBQAAPgUAAFEFAABkBQAAdQUAAIYFAACXBQAApwUAALYFAADF +BQAA0wUAAOEFAADuBQAA+wUAAAgGAAAUBgAAIAYAACsGAAA3BgAAQgYAAEwGAABXBgAAYQYAAGsG +AAB1BgAAfgYAAIgGAACRBgAAmgYAAKIGAACrBgAAtAYAALwGAADEBgAAzAYAANQGAADbBgAA4wYA +AOoGAADyBgAA+QYAAAAHAAAHBwAADgcAABQHAAAbBwAAIgcAACgHAAAuBwAANQcAADsHAABBBwAA +RwcAAE0HAABTBwAAWAcAAF4HAABkBwAAaQcAAG8HAAB0BwAAeQcAAH8HAACEBwAAiQcAAI4HAACT +BwAAmAcAAJ0HAACiBwAApwcAAKsHAACwBwAAtQcAALkHAAC+BwAAwgcAAMcHAADLBwAA0AcAANQH +AADYBwAA3AcAAOEHAADlBwAA6QcAAO0HAADxBwAA9QcAAPkHAAD9BwAAAQgAAAUIAAAICAAADAgA +ABAIAAAUCAAAFwgAABsIAAAfCAAAIggAACYIAAApCAAALQgAADAIAAA0CAAANwgAADsIAAA+CAAA +QQgAAEUIAABICAAASwgAAE8IAABSCAAAVQgAAFgIAABbCAAAXwgAAGIIAABlCAAAaAgAAGsIAABu +CAAAcQgAAHQIAAB3CAAAeggAAH0IAACACAAAgggAAIUIAACICAAAiwgAAI4IAACRCAAAkwgAAJYI +AACZCAAAAAAAAAEAAAAAAAAABwAAAAAAAAAAAAAAAAAAAAABAgMEBAQEBAUGBwgICAgICQoLDA0A +AG47aDtiO1w7bjpoOmI6XDpuOWg5YjlcOW4raCtiK1wrbipoKmIqXCpuKWgpYilcKW4oaChiKFwo +bidoJ2InXCduJmgmYiZcJm4laCViJVwlbiRoJGIkXCRuI2gjYiNcI24iaCJiIlwibiFoIWIhXCFu +IGggYiBcIGITXBNuEmgSYhJcEm4RaBFiEVwRbhBoEGIQXBBuAmgCYgJcAm4BaAFiAVwBbgBoAFQA +AABuO2g7YjtcO246aDpiOlw6bjloOWI5XDluK2grYitcK24qaCpiKlwqbiloKWIpXCluKGgoYihc +KG4naCdiJ1wnbiZoJmImXCZuJWglYiVcJW4kaCRiJFwkbiNoI2IjXCNuImgiYiJcIm4haCFiIVwh +biBoIGIgXCBuEmgSYhJcEm4RaBFiEVwRbhBoEGIQXBBXEFIQTRBJEG4BaAFiAVwBbgBoAGIAXABU +AAAAAAAAAAAAAAAdAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgwAAAJIAAACDAAAAkgAAAOgA +AAD3AAAA6AAAAPcAAAAAAAAACQAAAAAAAAAKAAAAOAAAAGgAAAB0AAAAgAAAAIwAAACdAAAABwAA +AAAAAAAHAAAABwAAAAcAAAAAAAAAAAAAAAAAAAAAAAAA7GEBAAgAAAADAAAA0GiAACAMgACgDIAA +IA2AAKANgAAKDREUCg0RFBkZGRkKCgAAAAAAAAYGBgYJCQkJAAYAAAAFBgcIDQ4PEBUWFxgZAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAADxqsrCUvrKysrKylrH6RTkMMmzYAAAA3AJqSj0gAAA8AIGg3AAAAEQA+OiARAAACJQAADC8A +AAIvOTkACiU8t0dvigAHFCdiLgAAAgAXAAAFEAogMEAAAAYGCg0jGyMhAAAADBAUGCAIBAAAPDg0 +MCwoJCAcGBQQDAgEAAsHAwA7NzMvKycjHxsXEw8LBwMxMDo1MTo0NgAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAiAAAAEAAAAB0AAAA3AAAAbwAAAKAAAABwAAAABQAAAPYA +AABaAAAAaQAAAAAAAABpAAAAAgAAAB8AAABIAAAAIAAAAEgAAAAIAAAAAQAAADMAAAB/AAAANAAA +AH8AAAA3AAAAAQAAADgAAABPAAAAOwAAAH8AAAA8AAAAfwAAADEAAAAAAAAAMgAAAAAAAAA1AAAA +AAAAADYAAAAAAAAAOQAAAAAAAAA6AAAAAAAAAA0AAAAqAAAADgAAAHoAAAAPAAAAEAAAAPMAAABI +AAAA9AAAAEgAAAAAAAAAAQEBAQEBAQECAgICAgICAgMDAwMDAwMDAQIAAAAAAAAAAAAABAAAAAUA +AAChAw4e4QAAAKEDDh7hAAAAAAAAAAoAAAARAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAChAw4e4QAAABX2Y/aw9vz2RveQ99j3H/hl+Kn47fgv+XD5sPnu+Sv6Z/qi+tz6FPtL ++4H7tvvq+xz8Tfx9/Kv82fwF/TD9Wf2C/an9z/30/Rf+Of5a/nr+mP62/tL+7f4G/x7/Nf9L/2D/ +c/+F/5b/pv+0/8H/zf/Y/+H/6f/w//b/+v/9//////////3/+v/2//D/6f/h/9j/zf/B/7T/pv+W +/4X/c/9g/0v/Nf8e/wb/7f7S/rb+mP56/lr+Of4X/vT9z/2p/YL9Wf0w/QX92fyr/H38Tfwc/Or7 +tvuB+0v7FPvc+qL6Z/or+u75sPlw+S/57fip+GX4H/jY95D3Rvf89rD2Y/ZwuYO6lruqvL690r7n +v/zAEcInwz3EU8VqxoDHl8ivycbK3sv2zA/OJ89A0FnRctKM06bUv9Xa1vTXDtkp2kTbX9x63Zbe +sd/N4OnhBeMh5D7lWuZ355PosOnN6urrB+0k7kLvX/B98ZryuPPV9PP1Efcv+Ez5avqI+6b8xP3i +/gAAHgE8AloDeASWBbQG0QfvCA0KKwtIDGYNgw6hD74Q3BH5EhYUMxVQFm0XiRimGcIa3xv7HBce +Mx9PIGohhiKhI7wk1yXyJgwoJilBKlordCyOLacuwC/ZMPExCjMiNDo1UTZpN4A4ljmtOsM72Tzv +PQQ/GUAuQUJCVkNqRH1F////AP///wH/AgP///8EBf8J/wcKBggLAAEBAgECAgMBAQEBAQEBAQIC +AgICAgICAwMDAwMDAwMEBAQEBAQEBAECAgICAgIDAwMDAwMDAwMDAwMDAwQEBAQEBAQEBAQEBAQE +BAQEBAQEBAQEBAAAADoBAgHVAN8A2gCiAHUAfwCKBSoDOQGoAYoFygLZAEgBAQMPBwoUN25qARoB +2QDoAAoBugB5AIgAygFKAeIA+QDKAeoAggCZAHTRRRfooosuAAUHAQMEAAUBBQAAAAUGAAIEAAUA +BQAAAQIBAgMEAAAFBgcICQoAAAkAAAAAAAAAAQAAAAIAAAADAAAAAAAAAAQAAAACAAAABQAAAAcH +BwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwYG +BgYGBQUFBQUEBAQEBAMDAwMDAgICAgIBAQEBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +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 +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAABgAAAAQAAAAsAQAABwAAAAAAAAASAAAABAAAAAsAAAAIAAAABAAAAGRQgAAJ +AAAABAAAABAYAAAKAAAABAAAANBPgAALAAAABAAAALxNgAAMAAAABAAAABAYAAANAAAABAAAAChN +gAAOAAAAAAAAAA8AAAAEAAAAFgAAAA== +==== +Copyright (c) 2006-2012, Intel Corporation. +All rights reserved. + +Redistribution. Redistribution and use in binary form, without +modification, are permitted provided that the following conditions are +met: + +* Redistributions must reproduce the above copyright notice and the + following disclaimer in the documentation and/or other materials + provided with the distribution. +* Neither the name of Intel Corporation nor the names of its suppliers + may be used to endorse or promote products derived from this software + without specific prior written permission. +* No reverse engineering, decompilation, or disassembly of this software + is permitted. + +Limited patent license. Intel Corporation grants a world-wide, +royalty-free, non-exclusive license under patents it now or hereafter +owns or controls to make, have made, use, import, offer to sell and +sell ("Utilize") this software, but solely to the extent that any +such patent is necessary to Utilize the software alone, or in +combination with an operating system licensed under an approved Open +Source license as listed by the Open Source Initiative at +http://opensource.org/licenses. The patent license shall not apply to +any other combinations which include this software. No hardware per +se is licensed hereunder. + +DISCLAIMER. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 +COPYRIGHT OWNER 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. +begin-base64 644 iwlwifi-2030-18.168.6.1.fw +AAAAAElXTAoyMDMwIGZ3IHYxOC4xNjguNi4xIGJ1aWxkIDAKAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAQaoEgAAAAABAAAAAAAAAAEAAABErAIAICCADwAAQABpIAAAaSBAAGkg +AABpIEAAICCADwAA6ABpIAAAaSBAAGkgAABpIEAAICCADwEANB1pIAAAaSBAAGkgAABKIAAASiEA +AEoiAABKIwAASiQAAEolAABKJgAASicAAEogABBKIQAQSiIAEEojABBKJAAQSiUAEEomABBKJwAQ +SiAAIEohACBKIgAgSiMAIEokACBKJQAgSiYAIEonACBKIAAwSiEAMAokgD+BAABAQSycMEAsnDBC +JBw0CiKAP4AAIIcKIwA3ng8ACEomAHBpIEAASiYAcEomAHBKJgBwSiYAcAAWAHCAAIwvQHggIECH +AAAAAAAAAAAAAPwciLb8HEi2/BwItvwcyLX8HIi1/BxItfwcCLX8HMi0/ByItPwcSLT8HAi0/BzI +s/wciLP8HEiz4H7geATcON018OB4BNw03TPw4HgE3DDdMfDgeATcLN0v8OB4BNwo3S3w4HgE3CTd +K/DgeATcIN0p8OB4BNwc3Sfw4HgE3BjdJfDgeATcFN0j8OB4BNwQ3SHw4HgE3AzdH/DgeATcCN0c +8OB4BNwE3RnwNBQaMDAUGTAsFBgwKBQXMCQUFjAgFBUwHBQUMBgUEzAUFBIwEBQRMAwUEDACxwHG +sCRNM7AkHzPgfuB44HjgeOB44HjgeAokgPAFIEQA4CDBB0Qk/oBBKsQAhAACAC8kAvFCIQEBQiAD +AeggogQEEQQCBBEFAgQRBgIEEQcCBBsIAQQbSAEEG4gBBBvIASwAJQBEIj6BPAAiAEQi/IBAIcEA +4CDBB0AjwwCoIIABARGEAgEbCgEgIMAHBBEEAgQRBQIEGwgB1Afh/wQbSAFEIvyABBEEAskH7/8E +GwgBQiFBAEIgQwCoIIABARGEAgEbCgEgIMAHz3GgAKwvGIGauBihcQDgFAXY4HjPcaAArC8YgbO4 +urgYoV0A4BRk2AoiQIAA2e4AAQAvJgDwSiZAAE4ABgBPACAAiiX/D+B4CiJAgADZzgABAGwAJAAv +JgDwXAAFACsINQhKJkAACHEA2AIhvoDgIMUHQnkB4AIhvoDgIMUHQnnrB+//AeAvLQEAQCVFAAIm +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 +0AEAAHgASQB5AGoA3gBqAKgAAAANAQAApgA/AKcAAQALAT8ADAEBAAQACACcAcwAnQHMANUBzADW +AcwAtAAgABkBIACPAIgA9ACIAJAAIgD1ACIAkQAEAPYABACFAAAAhgAAAIcAVQCIAAAAiQCqAIoA +AACLAN0AjAAAAIUAAQCGAAEAhwBVAIgAAACJAKoAigAAAIsA3QCMAAAAhQACAIYAAwCHAFUAiAAA +AIkAqgCKAAAAiwDdAIwAAACFAAMAhgAHAIcAVQCIAAAAiQCqAIoAAACLAN0AjAAAAPv/AAD//wAA +uQHfADsCLQCxABsAFgEbAK8AGwAUARsAbACgANEAoABvAIMAcQCDAHMAMwDUAQYA0AEAAHgASQB5 +AGoA3gBqAKgAAAANAQAApgA/AKcAAQALAT8ADAEBAAQACACcAcwAnQHMANUBzADWAcwAtAAgABkB +IACPAIgA9ACIAJAAIgD1ACIAkQAEAPYABACoAAwADQEMAIUAAACGAAAAhwCZAIgAAACJAKoAigAA +AIsA3QCMAAAAhQABAIYAAQCHAJkAiAAAAIkAqgCKAAAAiwDdAIwAAACFAAIAhgADAIcAmQCIAAAA +iQCqAIoAAACLAN0AjAAAAIUAAwCGAAcAhwCZAIgAAACJAKoAigAAAIsA3QCMAAAA+/8AAP//AAC5 +Ad8AsQAbABYBGwCvABsAFAEbAGwAoADRAKAAbwCDAHEAgwB2AIMAcwAzAG4AMwBwADMAcgAzANcA +MwDUAQYA0AEAAH4APADjADwAeABJAN0ASQB/AFoA5ABaAKoAPwCrAAEADwE/ABABAQB5AGoA3gBq +AKgAAAANAQAApgA3AKcAAQALATcADAEBAAQACACcAcwAnQHMANUBzADWAcwAtAAgABkBIAAxAgwA +MgIMADMCvQA2AgwANwIMADgCvQCgAIgABQGIAKEA1QAGAdUAogAEAAcBBACPAIgA9ACIAJAAIgD1 +ACIAkQAEAPYABACfAAwA+wAMAJQAAACVAAAAnACXAJ0A0ACaAI0AmAARAJYAMwCXAHcAlAABAJUA +AQCcAJcAnQDQAJoAjQCYABEAlgAzAJcAdwCUAAIAlQADAJwAlwCdANAAmgCNAJgAEQCWADMAlwB3 +AJQAAwCVAAcAnACXAJ0A0ACaAI0AmAARAJYAMwCXAHcA+gAAAPkAAAACAZcAAwHQAAABjQD+ABEA +/AAzAP0AdwD6AAEA+QABAAIBlwADAdAAAAGNAP4AEQD8ADMA/QB3APoAAgD5AAMAAgGXAAMB0AAA +AY0A/gARAPwAMwD9AHcA+gADAPkABwACAZcAAwHQAAABjQD+ABEA/AAzAP0AdwCFAAAAhgAAAIcA +VQCIAAAAiQCnAIoAAACLAN4AjAAAAIUAAQCGAAEAhwBVAIgAAACJAKcAigAAAIsA3gCMAAAAhQAC +AIYAAwCHAFUAiAAAAIkApwCKAAAAiwDeAIwAAACFAAMAhgAHAIcAVQCIAAAAiQCnAIoAAACLAN4A +jAAAAOsAAADqAAAA7ABVAO0AAADuAKcA7wAAAPAA3gDxAAAA6wABAOoAAQDsAFUA7QAAAO4ApwDv +AAAA8ADeAPEAAADrAAIA6gADAOwAVQDtAAAA7gCnAO8AAADwAN4A8QAAAOsAAwDqAAcA7ABVAO0A +AADuAKcA7wAAAPAA3gDxAAAApAGAAKEBQAD7/wAA//8AAKUBAQC5Ad8AsQAbABYBGwCvABsAFAEb +AGwAoADRAKAAbwCDAHEAgwB2AIMAcwAzAG4AMwBwADMAcgAzANcAMwDUAQYA0AEAAH4APADjADwA +eABJAN0ASQB/AFoA5ABaAKoAPwCrAAEADwE/ABABAQB5AGoA3gBqAKgAAAANAQAApgA3AKcAAQAL +ATcADAEBAAQACACcAcwAnQHMANUBzADWAcwAtAAgABkBIAAxAgwAMgIMADMCvQA2AgwANwIMADgC +vQCgAIgABQGIAKEA1QAGAdUAogAEAAcBBACPAIgA9ACIAJAAIgD1ACIAkQAEAPYABACfAAwA+wAM +AJQAAACVAAAAnACXAJ0A0ACaAI0AmAARAJYAMwCXAHcAlAABAJUAAQCcAJcAnQDQAJoAjQCYABEA +lgAzAJcAdwCUAAIAlQADAJwAlwCdANAAmgCNAJgAEQCWADMAlwB3AJQAAwCVAAcAnACXAJ0A0ACa +AI0AmAARAJYAMwCXAHcA+gAAAPkAAAACAZcAAwHQAAABjQD+ABEA/AAzAP0AdwD6AAEA+QABAAIB +lwADAdAAAAGNAP4AEQD8ADMA/QB3APoAAgD5AAMAAgGXAAMB0AAAAY0A/gARAPwAMwD9AHcA+gAD +APkABwACAZcAAwHQAAABjQD+ABEA/AAzAP0AdwCFAAAAhgAAAIcAVQCIAAAAiQCnAIoAAACLAN4A +jAAAAIUAAQCGAAEAhwBVAIgAAACJAKcAigAAAIsA3gCMAAAAhQACAIYAAwCHAFUAiAAAAIkApwCK +AAAAiwDeAIwAAACFAAMAhgAHAIcAVQCIAAAAiQCnAIoAAACLAN4AjAAAAOsAAADqAAAA7ABVAO0A +AADuAKcA7wAAAPAA3gDxAAAA6wABAOoAAQDsAFUA7QAAAO4ApwDvAAAA8ADeAPEAAADrAAIA6gAD +AOwAVQDtAAAA7gCnAO8AAADwAN4A8QAAAOsAAwDqAAcA7ABVAO0AAADuAKcA7wAAAPAA3gDxAAAA +pAGAAKEBQAD7/wAA//8AALkBwQDUAQMA0AEEAHgAPADdADwAeQBqAN4AagCoAAEADQEBAAQACACc +AcwAnQHMANUBzADWAcwAtAAgABkBIACPAIgA9ACIAJAAAAD1AAAAkQAGAPYABgCFAAQA6wAEAKQB +gABdAjMASgIOAEwCDgBNAgEArQEHALMBBAC4AQAAuwFWAFACCwBRAgMAUgIBAFMCAABUAgsAVQID +AFYCAQBXAgAAZgIGAGgCBwBqAgcAbAIHAG4CBQBwAgwAfQIGAH8CBwCBAgcAgwIHAIUCBQCHAgwA +tQAhABoBIQBLAgEAoQFAALMAAAAYAQAAlAILAJUCAwCWAgEAlwIAAJgCCwCZAgMAmgIBAJsCAACy +ADAAFwEwAJwCDwChAg8AoAKIAJ8CiACeAogAnQKIAKUCiACkAogAowKIAKICiAD7/wAA//8AALkB +wQDUAQMA0AEAAHgAPADdADwAeQBqAN4AagCoAAEADQEBAAQACACcAcwAnQHMANUBzADWAcwAtAAg +ABkBIACPAIgA9ACIAJAAAAD1AAAAkQAGAPYABgCFAAQA6wAEAKQBgABdAjYASgINAEwCDwBNAgEA +rQEGALMBBAC4AQAAuwFWAFACCwBRAgMAUgIBAFMCAABUAgsAVQIDAFYCAQBXAgAAZgIGAGgCBwBq +AgcAbAIHAG4CBQBwAgwAfQIGAH8CBwCBAgcAgwIHAIUCBQCHAgwAtQAhABoBIQChAUAA+/8AAP// +AAC5AcEA1AEDANABBAB4ADwA3QA8AHkAagDeAGoAqAABAA0BAQAEAAgAnAHMAJ0BzADVAcwA1gHM +ALQAIAAZASAAjwCIAPQAiACQAAAA9QAAAJEABgD2AAYAhQAEAOsABACkAYAAXQIzAEoCDgBMAg4A +TQIBAK0BBwCzAQQAuAEAALsBVgBQAgsAUQIDAFICAQBTAgAAVAILAFUCAwBWAgEAVwIAAJQCCwCV +AgMAlgIBAJcCAACYAgsAmQIDAJoCAQCbAgAAZgIGAGgCBwBqAgYAbAIHAG4CBQBwAgwAfQIGAH8C +BwCBAgYAgwIHAIUCBQCHAgwAsgAwABcBMACzAAAAGAEAAJwCDwChAg8AoAKIAJ8CiACeAogAnQKI +AKUCiACkAogAowKIAKICiAC1ACEAGgEhAKEBQABLAgEA+/8AAP//AAC5AcEA1AEDANABAAB4ADwA +3QA8AHkAagDeAGoAqAABAA0BAQAEAAgAnAHMAJ0BzADVAcwA1gHMALQAIAAZASAAjwCIAPQAiACQ +AAAA9QAAAJEABgD2AAYAhQAEAOsABACkAYAAXQI2AEoCDQBMAg8ATQIBAK0BBgCzAQQAuAEAALsB +VgBQAgsAUQIDAFICAQBTAgAAVAILAFUCAwBWAgEAVwIAAGYCBgBoAgcAagIGAGwCBwBuAgUAcAIM +AH0CBgB/AgcAgQIGAIMCBwCFAgUAhwIMALUAIQAaASEAoQFAAPv/AAAAAAAAAAAAAAEAAAACAAAA +AwAAAAQAAAAFAAAABgAAAAcAAAAAAAAAAAEAAAEBAAAAAAEBAAAAAAAAAAAAAAAAAQEBAQIAAAAN +0hLSE9IU0gzSFdIL0gLSEdIEQwAQFBAJEBEQAUAb0hzSANIKAAsABAAOALUAGgEPAEIAvADDACEB +KAG2ALcAuAC5AL0AvgC/AMAAGwEcAR0BHgEiASMBJAElAQoAAAALAAAAtgAAALcAAAC4AAAAuQAA +ABsBAAAcAQAAHQEAAB4BAAC9AAAAvgAAAL8AAADAAAAAIgEAACMBAAAkAQAAJQEAABLSAAAT0gAA +AAABAAIAAwAsAGQAdACAAIwAoQAHAAAAAAABAAIAAwAAAAAAtxMiALgUIwC5FSQAuxYlALwXJgC9 +GCcAwBkoAMQaKQAHGwAACBwBAAsdAgAMHgMAEB8EACIhBQAkIgYAJiMHACgkCAAqJQkALCYKAC4n +CwAwKAwANCkNADgqDgA8Kw8AQCwQAGQuEQBoLxIAbDATAHAxFAB0MhUAeDMWAHw0FwCANRgAhDYZ +AIg3GgCMOBsAkTocAJU7HQCZPB4AnT0fAKE+IAClPyEAJEkGAixKCgI0Sw0BPEwPAWRNEQFsThMB +dE8VAXxQFwGEURkBlVIdAZ1THwEBBAAAAgUBAAMGAgAEBwMABQgEAAYJBQAHCgYACAsHAAkMCAAK +DQkACw4KAAwPCwANEAwADhENAAFAAAQCQQEEA0ICBARDAwQFRAQEBkUFBAdGBgQIRwcECUgIBAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAhgCGAIYAhgCGAIYAhgCG +AIYAhgCGAIYAhgCGAIcAiAAEAAUABgAGAAcACAAIAAkACQAKAAsADAAMACQAJQAlACYAJwAoACgA +RABFAEYARgBHAEgASABJAEoASgBLAEwAaABoAGkAagBrAGwAbQBtAG4AbwBvAHAAcQBxAHIAcgBy +AHIAcgByAHIAcgByAHIAcgByAHIAcgByAHIAcgByAHIAcgAKAD8AhgCGAIYAhgCGAIYAhgCGAIYA +hgCGAIYAhgCGAIcAiAAEAAUABgAGAAcACAAIAAkACQAKAAsADAAMACQAJQAlACYAJwAoACgARABF +AEYARgBHAEgASABJAEoASgBLAEwAaABoAGkAagBrAGwAbQBtAG4AbwBvAHAAcQBxAHIAcgByAHIA +cgByAHIAcgByAHIAcgByAHIAcgByAHIAcgByAHIAcgAKAD8AAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAHAAARBwA +AEwcAADIGwAAzBsAADQMoABADKAAHBygAEAgoAAoJKAAbBCgABgkoAB4JKAAfCSgAIAkoACEJKAA +UBCgAFQQoABIJqAAYBCgAEwmoABkEKAAaBCgAFwQoABYEKAAMBCgADwQoAA0EKAALAygAACBpAAB +gaQAA4GkAIgkoACMJKAAkCSgAJQkoACYJKAAnCSgAKAkoACkJKAAAAAAAAAAAAAAAAAAAAAAAAEA +AgACAAMABAAEAAUABgAGAAcACAAIAAkACgAKAAsADAAMAA0ADgAOAA8AJgAnACgAKQAqAEYARgBH +AEgASABJAEoASgBLAEwAaABpAGoAagBrAGwAbABtAG4AbgBvAHAAcABxAHIAcgBzAHMAdAB0AHQA +dAB0AHQAdAB0AHQAdAB0AHQAdAB0AHQAdAB0AHQAdAAKAD8AAAAAAAAAAAAAAAAAAAAAAAEAAQAC +AAMAAwAEAAUABQAGAAcABwAIAAkACQAKAAsACwAMAAwADQAOACMAJAAlACYAJwAoACkARABFAEYA +RgBHAEcASABIAEkASQBKAEsAaABoAGkAagBrAGwAbQBtAG4AbwBvAHAAcQBxAHIAcgBzAHMAcwBz +AHMAcwBzAHMAcwBzAHMAcwBzAHMAcwBzAHMAcwAKAD8AAAAAAAAAAAAAAAAAAAAAAAEAAgACAAMA +BAAEAAUABgAGAAcACAAIAAkACgAKAAsADAAMAA0ADgAOAA8AJgAnACgAKQAqAEYARgBHAEgASABJ +AEoASgBLAEwAaABpAGoAagBrAGwAbABtAG4AbgBvAHAAcABxAHIAcgBzAHMAdAB0AHQAdAB0AHQA +dAB0AHQAdAB0AHQAdAB0AHQAdAB0AHQAdAAKAD8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAB +AAIAAwADAAQABQAFAAYABwAHAAgACQAJAAoACgALACQAJQAlACYAJwAoACgARABFAEYARgBHAEgA +SABJAEoASgBLAEwAaABoAGkAagBrAGwAbQBtAG4AbwBvAHAAcQBxAHIAcgByAHIAcgByAHIAcgBy +AHIAcgByAHIAcgByAHIAcgByAHIAcgAKAD8AgIeAAAAAAACyDCwB/////////wAB//8CA////wT/ +/////////////////////wX/Bv8H/wj/Cf8K/wv/DP///w3///8O////D////xD///////////// +/////////////////////////////////xH///8S////E////xT///8V////Fv///xf///8Y//// +Gf///xr///8b/////xz///8d////Hv///x////8g////If//////////////////////IiMk/yUm +J///KP///yn///////////////////////////////////////////////////////////////// +/////////////wABAAEBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAwAA +AAAAAAABAAAAAAAAAKxnAQAAAAAAeIEAAAEAAACIOgIAAgAAAOA5AgADAAAAbKQCAAQAAACsZwEA +BQAAADBPAQAGAAAApBsBAAcAAADATwEACAAAAHw+AAAJAAAA6HMAAAoAAABs6QAACwAAAGw3AAAM +AAAAcKQCAA0AAAC4CgEADgAAAGwLAQAPAAAARAoBABAAAABICwEAEQAAAPCCAQASAAAAoF4CABMA +AAAoKAAAFAAAAFixAQAVAAAArJkBABYAAADgqQEAFwAAAAg5AgAYAAAAkN8BABkAAADkPAEAGgAA +AKhnAQAbAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAP//////////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEaAAABIAAABSABLgkiATIPIAcwEyILMhkgETAdIhUy +IyAbMCciHzEtIAAAMR8AADcZAAA7FQAAAAAAAAAAAAAIrQAACK0AAAitAAA0kwAACK0AAAitAACY +kwAACK0AAAitAAAIrQAACK0AAAitAAAIrQAACK0AAAitAAAIrQAANJwAAHibAABomwAAkJoAAKCb +AABMmgAACK0AAAitAACQogAARKYAAGCoAAAIrQAACK0AAAitAABwrAAAlKEAAMyhAAA4oQAACK0A +AAitAAAIrQAALKwAAAitAAAYoQAACK0AAAitAAAIrQAACK0AAAitAAAIrQAACK0AAAitAAAIrQAA +CK0AAAitAAAIrQAACK0AAAitAAAIrQAACK0AAAitAAAIrQAACK0AAAitAAAIrQAACK0AAAitAAAI +rQAACK0AAAitAAAIrQAACK0AAAitAAAIrQAACK0AAAitAABclAAACK0AAAitAAAIrQAACK0AAAit +AAB8qQAACK0AAAitAAAIrQAACK0AAAitAAC0lwAACK0AAMSXAADAlwAAuJcAALyXAADYkAAACK0A +AJCQAAAIrQAACK0AAAitAAAIrQAACK0AAAitAAAIrQAACK0AABSQAAAIrQAACK0AAAitAAAIrQAA +CK0AAAitAAAIrQAACK0AAAitAAAIrQAACK0AAMSVAAA8lQAACK0AAOCVAAAIrQAAfJQAABCaAAAI +rQAACK0AAFScAAAIrQAACK0AAAitAAAIrQAACK0AANidAACEnAAACK0AAAitAAAIrQAACK0AAAit +AAAIrQAACK0AAAitAAAIrQAACK0AAAitAADIlwAACK0AAAitAAAIrQAAIKkAAAitAAAIrQAACK0A +AMCrAAAIrQAAJKwAALyoAAAIrQAACK0AACCPAAB8qAAACK0AAAitAACYmgAAsJoAAAitAAAIrQAA +cJcAACiRAAAIrQAACK0AAAitAADUoQAANJoAAAitAAAIrQAACK0AAAitAAAIrQAACK0AAGyZAAAI +rQAA7K0AACywAABgrwAANLAAAFivAABQrwAAPLAAACytAAAIrQAACK0AAAitAAAIrQAACK0AAAit +AAAIrQAACK0AAHiXAAAIrQAACK0AAAitAAAIrQAACK0AAAitAAAIrQAA0LAAAOSxAABAjwAAYI8A +AAitAAAIrQAACK0AAAitAAAIrQAAXJEAAAitAAAIrQAACK0AAAitAAAIrQAACK0AAAitAAAIrQAA +CK0AAAitAAAIrQAACK0AAAitAAAIrQAACK0AAAitAAAIrQAACK0AAAitAAAIrQAACK0AAAitAAAI +rQAACK0AAAitAAAIrQAACK0AAAitAABgkQAAgJIAAPCRAABEsAAAoI8AAAyTAACkkwAACK0AAAit +AAAIrQAACK0AAJCTAACUkwAACK0AAAitAAA4kwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAtL8AAOC9AAAswQAA +KMAAAEDCAAAAAAEA/////wAAAAD//////////wEAAAAQDwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAEAAAAAAND+AAAAAAAAAAAAAAAAGCCgACAgoABAIaAASCGgABwgoAAkIKAARCGgAEwh +oAAoIKAAMCCgAGghoABwIaAALCCgADQgoABsIaAAdCGgADggoAA8IKAAeCGgAHwhoAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAABAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAMQNAAAA/wMAZA4AAAD/BQAIDgAAAP8tAOQNAAAA/z0AgA0AAAD/BACoDQAAAP8lACjtAAAA +/90ALA4AABAQTAAAAAAAAAAAAAABAQA8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PBUV +FRU8PDw8FRUVFTw8PDwAAAAAAAAAAAAAAAAAAAAAPDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8 +PDw8PDwVFRUVPDw8PBUVFRU8PDw8AAAAAAAAAAAAAAAAAAAAADw8PDw8PDw8PDw8PDw8PDw8PDw8 +PDw8PDw8PDw8PDw8FRUVFTw8PDwVFRUVPDw8PAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/wAAAAEA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWBAAALAXAADgEwAA +PBEAAHwZAADwFAAAmBYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAPAJgAC0vYAAGAAAAHS9gAAAAAAAAAAAAAAAAAAAAAAAFDoB +ANTOAACQJAAA1M4AANTOAADUzgAAEAcAAJRWAgDs+gAA1M4AANTOAAAkLAAAJCwAACQsAAAkLAAA +JCwAACQsAAAkLAAA1M4AANTOAADUzgAA1M4AAAhbAADUzgAA1M4AANTOAADUzgAA1M4AAND6AADU +zgAA1M4AAGTsAAAAAAAAwB8BAMQfAQC0AgAAoAIAAMSBAQAAAAAAQN4AAGAbAgBo3gAAkBsCAIze +AADAGwIADEiAAKCygAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABIIgAAECIAALi+gAAAAgAA +AAAAAIgvAQBYLwEAuMCAAEAFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACMLwEAdH4BAEjHgABU +AAAAAAAAAIgvAQBIfQEA+MWAAFABAAAAAAAAiC8BADB3AQBACoAACAAAAAEAAACILwEAVC4BAAAA +AABQAQAAAAAAAIgvAQC8dwEAoECAAAIAAAAAAAAAiC8BAMh2AQA8CoAABAAAAAAAAACQLwEAWC8B +AJzHgAAqAAAAAAAAAIgvAQBYLwEAxE+AAAgAAAAAAAAAAAAAAGAvAQAAAAAAAAAAAAEAAAAAAAAA +dC8BAAAAAAAAAAAAAAAAAAAAAABcLwEAAAAAAAAAAAAAAAAAiC8BABQeAgAAAAAAAAAAAAAAAACI +LwEA1B0CAEgKgAAEAAAAAAAAAG4AbgBpAMAAoABQAIAAvgBQAX0APgABAAEAAQBYAigA5gEtAFUD +PADcAWMAAABuAG4AaQDAAKAAUACAAL4AUAF9AD4AAQABAAEAWAIoAOYBLQBVAzwA3AFjAAAAAAAA +AAEBAAAIOQEAFdIAAAAAAAD/AwAACDkBAAzSAAAAAAAA/wEAAAg5AQAV0gAACgAAAAD8DwAIOQEA +DNIAAAkAAAAA/gMACDkBABXSAAAUAAAAAADwPwg5AQAM0gAAEgAAAAAA/AcIOQEABtIAAAAAAAD/ +AQAACDkBAAfSAAAAAAAA/wMAAAg5AQAG0gAACQAAAAD+AwAIOQEAB9IAAAoAAAAA/A8ACDkBAAbS +AAASAAAAAAD8Bwg5AQAH0gAAFAAAAAAA8D8AAAAAAAAAAAAAAAABAAAAAQAAAAoAAAAFAAAABQAA +AAYAAAAKAAAACgAAAAYAAAAEAAAABQAAAAYAAAAFAAAABQAAAAYAAAAGAAAABgAAAAYAAAAHAAAA +BwAAAAcAAAAIAAAACAAAAAgAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAACAAAAAQAAAAIAAAAB +AAAAAQAAAAMAAAADAAAAAgAAAAEAAAAEAAAAAAAAAAAAAAACAAAAAQAAAAEAAAAAAAAAAAAAAAcA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAsAQAAXgEAAAEAAAABAAAA +AQAAAAEAAAADAAAAAAAAAAAAAADQRwEAfE0BAChMAQAATgEAeE0BAEhLAQD8TQEAPEkBADhJAQBg +4xYAINYTAAAAAAAQAAAAAIAAAAAAoAAQJwAA6AMAAOgDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAIAAAACAAAAAQAAAAEAAAACAAAABQAAAAIAAAACAAAABQAAAAIAAAACAAAAAQAAAAEAAAAFAAAA +BQAAAAIAAAAFAAAABQAAAAAAAAAFAAAAAgAAAAIAAAAAAAAAAAAAAAAAAAAFAAAABQAAAAAAAAAF +AAAAAgAAAAIAAAAFAAAABQAAAAUAAAAAAAAABQAAAAIAAAAFAAAAAQAAAAEAAAACAAAAAgAAAAIA +AAAFAAAABQAAAAIAAAAFAAAAAQAAAAEAAAACAAAAAgAAAAIAAAAFAAAABQAAAAIAAAACAAAABQAA +AAEAAAACAAAABQAAAAIAAAAFAAAABQAAAAQAAAAFAAAABQAAAAEAAAAFAAAABQAAAAUAAAACAAAA +AgAAAAUAAAAFAAAABQAAAAEAAAAFAAAABQAAAAUAAAACAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAwAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAAAEQAAEAAAACgAABQgAGAhAAAiAAAAPA +AAFDAAYDEAACwAAAA8AAAUMABgQQAAJAAAACgAABRAAGBREAAEAAAAPAAAFFAAYGEQAA4AAAA8AA +AUUABgcRAAEAAAACgAABRgAGCBEAAiAAAAPAAAFHAAYJEQACwAAAA8AAAUcABgoRAAJAAAACgAAB +SAAGCxIAAEAAAAPAAAFJAAYMEgAA4AAAA8AAAUkABg0SAAEAAAACgAABSgAGDhIAAgAAAAKAAAFM +AAYBeAAwAAAAUAAABLY8BgJ4AEQAAABQAAAEuTwGA3kACAAAAFAAAAS7PAYEeQAcAAAAUAAABL48 +BgV5ADAAAABQAAAEwDwGBnkARAAAAFAAAATDPAYHegAIAAAAUAAABMU8Bgh6ABwAAABQAAAEyDwG +CXoAMAAAAFAAAATKPAYKegBEAAAAUAAABM08Bgt7AAgAAABQAAAEzzwGDHsAHAAAAFAAAATSPAYN +ewAwAAAAUAAABNQ8Bg58ABAAAABQAAAE2j4GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQIBAQACAQABAgIC +AAEBAAIBAgECAAIAAQIDgICAgICAgIABgAKAgICAgMAAkADQAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAbgAAAG4AAgBuAG4AbgACAGkAaQBuAAEAwADAAOgAAQCgAKAA +NgEDAFAAUAD1AAEAgACAAOgAAQC+AL4AvgABAFABUAFQAQEAfQB9AK8AAwA+AD4APgABAAEAAQAB +AAEAAQABAAEAAQABAAEAAQABAFgCWAJYAgEAKAAoACgAAQDmAeYB5gEBAC0ALQAtAAEAVQNVA1UD +AQA8ADwAPAABANwB3AHcAQEAYwBjAGMAAQAAAAAAAAAAADIABQAyAAUAAgAIAGQAoAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAP8AAAAAAAAAjAqMCgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAACAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA4EgCABUAAAADAAAAOG6AAAAAAAAAAAAAAAAAAPhBAgAG +AAAAAAAAAFhugAAAAAAAAAAAAAAAAACESAIABQAAAAMAAAA4boAAAAAAAAAAAAAAAAAAdF4AABUA +AAADAAAAOG6AAAAAAAAAAAAAAAAAAGxIAgAKAAAAAwAAADhugAAAAAAAAAAAAAAAAAAsRQIABAAA +AAAAAABYboAAAAAAAAAAAAAAAAAALEUCAAQAAAAAAAAAWG6AAAAAAAAAAAAAAAAAACxFAgAEAAAA +AAAAAFhugAAAAAAAAAAAAAAAAAAsRQIABAAAAAAAAABYboAAAAAAAAAAAAAAAAAALEUCAAQAAAAA +AAAAWG6AAAAAAAAAAAAAAAAAABxGAgAGAAAAAAAAAFhugAAAAAAAAAAAAAAAAABURgIABAAAAAAA +AABYboAAAAAAAAAAAAAAAAAAVEYCAAQAAAAAAAAAWG6AAAAAAAAAAAAAAAAAAFRGAgAEAAAAAAAA +AFhugAAAAAAAAAAAAAAAAABURgIABAAAAAAAAABYboAAAAAAAAAAAAAAAAAAHEYCAAYAAAAAAAAA +WG6AAAAAAAAAAAAAAAAAABxGAgAGAAAAAAAAAFhugAAAAAAAAAAAAAAAAAAcRgIABgAAAAAAAABY +boAAAAAAAAAAAAAAAAAAVEYCAAQAAAAAAAAAWG6AAAAAAAAAAAAAAAAAAFRGAgAEAAAAAAAAAFhu +gAAAAAAAAAAAAAAAAAA4QwIACgAAAAAAAABYboAAAAAAAAAAAAAAAAAAKEUCAAoAAAAAAAAAWG6A +AAAAAAAAAAAAAAAAAChFAgAKAAAAAAAAAFhugAAAAAAAAAAAAAAAAAAoRQIACgAAAAAAAABYboAA +AAAAAAAAAAAAAAAAKEUCAAoAAAAAAAAAWG6AAAAAAAAAAAAAAAAAAChFAgAKAAAAAAAAAFhugAAA +AAAAAAAAAAAAAAAoRQIACgAAAAAAAABYboAAAAAAAAAAAAAAAAAAKEUCAAoAAAAAAAAAWG6AAAAA +AAAAAAAAAAAAAChFAgAKAAAAAAAAAFhugAAAAAAAAAAAAAAAAAAoRQIACgAAAAAAAABYboAAAAAA +AAAAAAAAAAAAKEUCAAoAAAAAAAAAWG6AAAAAAAAAAAAAAAAAAChFAgAKAAAAAAAAAFhugAAAAAAA +AAAAAAAAAAAoRQIACgAAAAAAAABYboAAAAAAAAAAAAAAAAAA1EoCAAYAAAAAAAAAWG6AAAAAAAAA +AAAAAAAAACRKAgAFAAAAAwAAADhugAAAAAAAAAAAAAAAAAAoQgIACgAAAAAAAABYboAAAAAAAAAA +AAAAAAAAyEICAAoAAAAAAAAAWG6AAAAAAAAAAAAAAAAAADxdAgAKAAAAAwAAADhugAAAAAAAAAAA +AAAAAACEQwIACgAAAAAAAABYboAAAAAAAAAAAAAAAAAAeEQCAAoAAAAAAAAAWG6AAAAAAAAAAAAA +AAAAABBHAgAKAAAAAAAAAFhugAAAAAAAAAAAAAAAAADURwIACgAAAAAAAABYboAAAAAAAAAAAAAA +AAAA5EgCAAoAAAAAAAAAWG6AAAAAAAAAAAAAAAAAAJBJAgAKAAAAAAAAAFhugAAAAAAAAAAAAAAA +AADoSQIABgAAAAAAAABYboAAAAAAAAAAAAAAAAAA+EkCAAYAAAAAAAAAWG6AAAAAAAAAAAAAAAAA +ABxKAgABAAAAAAAAAFhugAAAAAAAAAAAAAAAAAAUTAIACgAAAAEAAAB4boAAAAAAAAAAAAAAAAAA +iEsCAAUAAAABAAAAeG6AAAAAAAAAAAAAAAAAACxPAgAGAAAAAQAAAHhugAAAAAAAAAAAAAAAAACA +TwIABgAAAAEAAAB4boAAAAAAAAAAAAAAAAAAoFICAAoAAAABAAAAeG6AAAAAAAAAAAAAAAAAAAAA +AAA4boAAOG6AALggoABsIKAAAIABAP9//P8AAAAAAAAAAFhugABYboAApCCgADggoAABAAAA/P// +/wAAAAAAAAAAeG6AAHhugACoIKAAPCCgABAAAADH////AAAAAAAAAACYboAAmG6AAKwgoAB4IaAA +QAEAAD/+//8AAAAAAAAAALhugAC4boAAsCCgAHwhoAAADAAA//H//wAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAHxDAgAVAAAAAwAAADhugAAAAAAAUAAAAAAAAADwCYAAtL2AABgAAAB0vYAAAAAAAAAA +AADwCYAAtL2AABgAAAB0vYAAAAAAAAAAAAAAAAAA8AmAALS9gAAYAAAAdL2AAAAAAAAAAAAAAAAA +AH8AAAAAAAAAAH8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP8AAP//AAABAAAAAAAAAAcAAAAAAAAA +AAAAAAAAAAB4bAEARGsBAAAAAAAAAAAAAAAAAAAAAADcbgEAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADcbgEARG0BAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAB4bAEARGsBAAAAAAD8agEAAAAAABRrAQDcbgEARG0BAAAAAAAAAAAA +AAAAABBtAQD8agEA3GoBAAAAAAB4bAEARGsBANhrAQB4awEAAAAAABRrAQDcbgEARG0BAPxqAQDc +agEAjGwBAAAAAAD8agEA3GoBAAcAAAAAAAAABgAAAAIAAAACAAAAAgAAAAIAAAAGAAAAAgAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAcAAAABAAAABgAAAAcAAAAHAAAABwAAAAcAAAABAAAABgAA +AAEAAAABAAAAAAAAAAEAAAABAAAAAQAAAAcAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAA +AgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAcAAAADAAAAAwAAAAcAAAAEAAAAAwAAAAMAAAAD +AAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAcAAAAEAAAABAAAAAQAAAAEAAAABwAAAAMA +AAAEAAAABAAAAAQAAAAEAAAAAwAAAAQAAAAEAAAABAAAAAAAAAAAAQIDBAQEBAQFBgcICAgICAkK +CwwNAABuO2g7YjtcO246aDpiOlw6bjloOWI5XDluK2grYitcK24qaCpiKlwqbiloKWIpXCluKGgo +YihcKG4naCdiJ1wnbiZoJmImXCZuJWglYiVcJW4kaCRiJFwkbiNoI2IjXCNuImgiYiJcIm4haCFi +IVwhbiBoIGIgXCBiE1wTbhJoEmISXBJuEWgRYhFcEW4QaBBiEFwQbgJoAmICXAJuAWgBYgFcAW4A +aABUAAAAbjtoO2I7XDtuOmg6YjpcOm45aDliOVw5bitoK2IrXCtuKmgqYipcKm4paCliKVwpbiho +KGIoXChuJ2gnYidcJ24maCZiJlwmbiVoJWIlXCVuJGgkYiRcJG4jaCNiI1wjbiJoImIiXCJuIWgh +YiFcIW4gaCBiIFwgbhJoEmISXBJuEWgRYhFcEW4QaBBiEFwQVxBSEE0QSRBuAWgBYgFcAW4AaABi +AFwAVAAAAAAAAAAAAAAAHQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAxKMBAAgAAAADAAAAOG6AAFwLgADcC4AAXAyAANwMgAAKDREUCg0RFBkZGRkKCgAAAAAA +AAYGBgYJCQkJAAYAAAAFBgcIDQ4PEBUWFxgZAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AA8arKwlL6ysrKyspax+kU5DDJs2AAAANwCako9IAAAPACBoNwAAABEAPjogEQAAAiUAAAwvAAAC +Lzk5AAolPLpHcY0ABxctaTQAAAIAFwAABRAKIDBAAAAGBgoABQoPDSQcJCIAAAAAR5ivr0kAAAwQ +FBggCAQAADw4NDAsKCQgHBgUEAwIBAALBwMAOzczLysnIx8bFxMPCwcDMTA6NTQ6MzkAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIgAAABAAAAAdAAAANwAAAG8AAACgAAAA +cAAAAAUAAAD2AAAAWgAAAGkAAAAAAAAAaQAAAAIAAAAfAAAASAAAACAAAABIAAAACAAAAAEAAAAz +AAAAfwAAADQAAAB/AAAANwAAAAEAAAA4AAAATwAAADsAAAB/AAAAPAAAAH8AAAAxAAAAAAAAADIA +AAAAAAAANQAAAAAAAAA2AAAAAAAAADkAAAAAAAAAOgAAAAAAAAANAAAAKgAAAA4AAAB6AAAADwAA +ABAAAADzAAAASAAAAPQAAABIAAAAAAAAAAEBAQEBAQEBAgICAgICAgIDAwMDAwMDAwECAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAABAAAAAUAAAChAw4e4QAAAKEDDh7hAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKEDDh7hAAAAFfZj9rD2/PZG95D3 +2Pcf+GX4qfjt+C/5cPmw+e75K/pn+qL63PoU+0v7gfu2++r7HPxN/H38q/zZ/AX9MP1Z/YL9qf3P +/fT9F/45/lr+ev6Y/rb+0v7t/gb/Hv81/0v/YP9z/4X/lv+m/7T/wf/N/9j/4f/p//D/9v/6//3/ +/////////f/6//b/8P/p/+H/2P/N/8H/tP+m/5b/hf9z/2D/S/81/x7/Bv/t/tL+tv6Y/nr+Wv45 +/hf+9P3P/an9gv1Z/TD9Bf3Z/Kv8ffxN/Bz86vu2+4H7S/sU+9z6ovpn+iv67vmw+XD5L/nt+Kn4 +Zfgf+Nj3kPdG9/z2sPZj9nC5g7qWu6q8vr3Svue//MARwifDPcRTxWrGgMeXyK/Jxsrey/bMD84n +z0DQWdFy0ozTptS/1drW9NcO2SnaRNtf3Hrdlt6x383g6eEF4yHkPuVa5nfnk+iw6c3q6usH7STu +Qu9f8H3xmvK489X08/UR9y/4TPlq+oj7pvzE/eL+AAAeATwCWgN4BJYFtAbRB+8IDQorC0gMZg2D +DqEPvhDcEfkSFhQzFVAWbReJGKYZwhrfG/scFx4zH08gaiGGIqEjvCTXJfImDCgmKUEqWit0LI4t +py7AL9kw8TEKMyI0OjVRNmk3gDiWOa06wzvZPO89BD8ZQC5BQkJWQ2pEfUUAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACqqqqqqqqqqqqqqq6qqqqqKP8AzKqq +AACqqgDMqqoAAABAAMAAAAAAAFAA8ABAAPCqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq +qgAAAAAAAAAAAAAAAAAAAAD///8A////Af8CA////wQF/wn/BwoGCAsAAQECAQICAwEBAQEBAQEB +AgICAgICAgIDAwMDAwMDAwQEBAQEBAQEAQICAgICAgMDAwMDAwMDAwMDAwMDBAQEBAQEBAQEBAQE +BAQEBAQEBAQEBAQEAAAAxDYAABA5AABEMwAARDIAAMQyAADkNwAADDIAAAw4AACMOAAAOgECAdUA +3wDaAKIAdQB/AIoFKgM5AagBigXKAtkASAEBAw8HChQ3bmoBGgHZAOgACgG6AHkAiADKAUoB4gD5 +AMoB6gCCAJkAdNFFF+iiiy4ABQcBAwQABQEFAAAABQYAAgQABQAFAAABAgECAwQAAAUGBwgJCgAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAc +6QAA4OsAAPDmAAABAAAAAAAAAAEAAAACAAAAAwAAAAAAAAAEAAAAAgAAAAUAAAAHBwcHBwcHBwcH +BwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcGBgYGBgUFBQUF +BAQEBAQDAwMDAwICAgICAQEBAQEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +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 +AgMDA6RIAgCwSAIAvEgCAMhIAgDQSAIA2EgCAAEBAAECAQEBAAAAAAEAAAAAAAAAAAAAAAAAAAAB +AAAAAAAAAAEAAAABAAAAAAAAAAEAAAAAAAAAAQAAAAAAAAABAAAAAAAAAAAAAAABAAAAAAAAAAAA +AAD/////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACADQAAACAA +AIANAACADQAAACAAAIANAAAABgAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAEAAAABA3AgAgIIAPAABAAGkgAABpIEAAaSAAAGkgQAAgIIAPAADoAGkgAABp +IEAAaSAAAGkgQAAgIIAPAABg6mkgAABpIEAAaSAAAEogAABKIQAASiIAAEojAABKJAAASiUAAEom +AABKJwAASiAAEEohABBKIgAQSiMAEEokABBKJQAQSiYAEEonABBKIAAgSiEAIEoiACBKIwAgSiQA +IEolACBKJgAgSicAIEogADBKIQAwCiSAP4EAAEBBLJwwQCycMEIkHDQKIoA/gAAgZQojADeeDcAG +SiYAcGkgQABKJgBwSiYAcEomAHBKJgBwABYAcIAABCxAeCAgQIcAAAAAAAAAAAAA/ByItvwcSLb8 +HAi2/BzItfwciLX8HEi1/BwItfwcyLT8HIi0/BxItPwcCLT8HMiz/ByIs/wcSLPgfuB4BNw43TXw +4HgE3DTdM/DgeATcMN0x8OB4BNws3S/w4HgE3CjdLfDgeATcJN0r8OB4BNwg3Snw4HgE3BzdJ/Dg +eATcGN0l8OB4BNwU3SPw4HgE3BDdIfDgeATcDN0f8OB4BNwI3Rzw4HgE3ATdGfA0FBowMBQZMCwU +GDAoFBcwJBQWMCAUFTAcFBQwGBQTMBQUEjAQFBEwDBQQMALHAcawJE0zsCQfM+B+4HjgeOB44Hjg +eOB4CiSA8AUgRADgIMEHRCT+gEEqxACEAAIALyQC8UIhAQFCIAMB6CCiBAQRBAIEEQUCBBEGAgQR +BwIEGwgBBBtIAQQbiAEEG8gBLAAlAEQiPoE8ACIARCL8gEAhwQDgIMEHQCPDAKgggAEBEYQCARsK +ASAgwAcEEQQCBBEFAgQbCAHUB+H/BBtIAUQi/IAEEQQCyQfv/wQbCAFCIUEAQiBDAKgggAEBEYQC +ARsKASAgwAfPcaAArC8YgZq4GKGVBOAQBdjgeM9xoACsLxiBs7i6uBihgQTgEGTYCiJAgADZ7gAB +AC8mAPBKJkAATgAGAE8AIACKJf8P4HgKIkCAANnOAAEAbAAkAC8mAPBcAAUAKwg1CEomQAAIcQDY +AiG+gOAgxQdCeQHgAiG+gOAgxQdCeesH7/8B4C8tAQBAJUUAAiZ88QAAIAAAKEAB6CBiAy8gAIAv +IUsAAiG+gMAghgHCIYYA4H4RACAASiAAEEogQBAOIkIALyALEs4gRYCKJf8PCAAFAC8tAQBAJUUA +AiZ88QAAIAAAKEABSiZAAOggIgMvIACALyFLAAIhvoDAIIYBwiGGAEomAABCIP6QziCCAUQgfpDO +IYIB4H4JAAAA4HgKJgDwiiC/D8ogZADgfy8gAwDgf4og/w/8HIix/BxIsfwcCLHhw+HC4cHhwAfA +HBzAMeHA4H8BwPHAFg3P/wh2WnIBiUCJELgYugd6AokIuEd4Q4kHegCG4oYHIIcARYkEiRC6GLhH +eEaJCLpHeEeJB3oBhqOGByCIAAiJSYkYuBC6B3oKiQi4B3oLiUd4TYkHfwyJELoYuEd4TokIui+J +R3gneAd9SiNAIV7wzgygBQDZCHfqcMYMoAUB2Qd/6HC6DKAFAtkHfwlwsgygBQPZIIbneAchBwBB +KwAGngygBQDZCHMpcJYMoAUB2Qd7SXCKDKAFAtkHe2lwggygBQPZIYZneAchCABBLgAmbgygBQDZ +CHMKcGYMoAUB2Qd7KnBaDKAFAtkHe4hwUgygBQPZ4oZneAd/QS0AFkIMoAUA2QhzinA2DKAFAdkH +e8hwLgygBQLZB3uqcCIMoAUD2aOGZ3gHfUEvAAYSDKAFANkIc0EtABQPeAYMoAUB2Qd7QS8AEg94 +9gugBQLZB3svIAcC6gugBQPZJIZneAchBQBBKAAW2gugBQDZCHNBLwAED3jKC6AFAdkHe0EtABIP +eL4LoAUC2Qd773iyC6AFA9lneGWGB3tBLwAWogugBQDZmHBBKAAUD3iWC6AFAdkHJAQAQS8AAg94 +hgugBQLZByQEAK94egugBQPZJoYHIAABByEWAEEtABZmC6AFANkIdUEvABQPeFYLoAUB2Qd9QSgA +Eg94SgugBQLZB30vIMcBPgugBQPZp3inhkIjU6BBLQkEQSsQBAd9IOZBLRcUQS4AIi8gxxBBLQoS +LyOHFUEtAQIvJEcDQS4UJEErAgIvJUchLyfHJS8nBwAvIUcSLyKHEi8gByQvIUcgLyQHJS8mhwBB +LQAGBPXWCqAFBNkEII8PAP8AAOpwxgqgBQTZ/9kQuSR4B3/ocLYKoAUE2f/ZCLkkeAd/CXCmCqAF +BNkPeCCG53gneEEoAQYAGkIgQSgBBAEaQiBBKAECAxoCIAIaQiBBKwAGdgqgBQTZBCCDDwD/AAAp +cGYKoAUE2f/ZELkkeAd7SXBWCqAFBNn/2Qi5JHgHe2lwRgqgBQTZD3ghhmd4J3hBKAEGBBpCIEEo +AQQFGkIgQSgBAgcaAiAGGkIgQS4AJhoKoAUE2QQggw8A/wAACnAKCqAFBNn/2RC5JHgHeypw+gmg +BQTZ/9kIuSR4B3uIcOoJoAUE2Q94IoZneCd4QSgBBggaQiBBKAEECRpCIEEoAQILGgIgChpCIEEt +ABa6CaAFBNkEIIMPAP8AAIpwqgmgBQTZ/9kQuSR4B3vIcJoJoAUE2f/ZCLkkeAd7qnCKCaAFBNkP +eCOGZ3gneEEoAQYMGkIgQSgBBA0aQiBBKAECDhpCIIEB7/8PGgIg4HjxwE4Jz/+eCSAACHUA2SjY +FSVCEBUlAxDAg+CCwKLgo8GD4YLBouGjwoPigsKi4qPDg+OCBOFkuMOi2QkigOOjCdsEhRDlOLhO +CaAFBNkPeAoJoAUA2Qh2AIUwuA94OgmgBQTZD3j2CKAFAdkHfgCFKLgPeCIJoAUE2Q943gigBQLZ +B34AjRIJoAUE2Q94zgigBQPZx3gApQGFOLj6CKAFBNkPeLYIoAUA2Qh2AYUwuA945gigBQTZD3ii +CKAFAdkHfgGFKLgPeM4IoAUE2Q94igigBQLZB34Ejb4IoAUE2Q94egigBQPZx3gBpQKFOLimCKAF +BNkPeGIIoAUA2Qh2AoUwuA94kgigBQTZD3hOCKAFAdkHfgKFKLgPeHoIoAUE2Q94NgigBQLZB34I +jWoIoAUE2Q94JgigBQPZx3gCpQOFOLhSCKAFBNkPeA4IoAUA2Qh2A4UwuA94PgigBQTZD3j6D2AF +AdkHfgOFKLgPeCYIoAUE2Q944g9gBQLZB34MjRYIoAUE2Q940g9gBQPZYbuA48d4rgbt/wOlGQDP +//HArg+P/2GJQIkQuxi6Z3piiQi7Z3pjiQDfZ3pAoESJZYkYuhC7R3tGiQi6R3tHiWd6QaBIiWmJ +GLoQu0d7SokIukd7S4lnekKgbYlMiRC7GLpnem6JCLsviWd6Ct0nekOgw4AIc0EuABQPeIoPYAUE +2SCDBCCADwD/AAAHIQQAQS4AEg94cg9gBQTZBCCAD/8AAAAHJAQAz3heD2AFBNkEIIAPAAAA/wck +BABBLgAWRg9gBQTZD3jPcYAA2GTwIcEDByAAASd4IYMEoyd4IoMFoyd4I4MGo2G9J3gHo0AjAASD +DXWQAectB4//4cUIdRHw4HjgeOB44HjgeOB44HjgeOB44HjgeOB44HjgeOB44HhhvYwl/5/t9eB/ +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 +ACCBIDIO4Aypcslw1g6v/wpxCPDJcM4Or/8AJUEUQOVAJcAf8QjEgwDaA/AA3UAmARcAJUAUWWH+ +DeAMAidCE3kHT/7xwB4PT/7PdYAAIAoAFQUQMw1VAs92gADYSc93gACYYAPwABUFEAGFQC0BAiV4 +IIYHCEAAAKbwJ0ARQHhz6EkHT/4KIcAP63LPcAAAhidj26kAIABKJIAAosHhxULBQSgCAgd6QSgB +BEd5z3KBAEgaxrkqYiUK3wEIFAMxz3WBAIgdqXFWeUCBCwiBAEKREQrAAEeJ7wregYDYA/AGicHF +4H+iwOB+4HjgfuB4CMiVuAgaGDAJyJu4CRoYMAvIiriNuJC4CxoYMOB+4HjxwOHFCHU+iM9wgABM +I0CAQCUAFAO5NXlZYcIN4AwK2r4P7/+pcKUGT/7gePHApcFBwELBDBwAMRAcQDHPcYAAsJc0GcAP +MBkADywZwA4oGYAOJBlADs9wgACwlyAYQAvPcIAAsJccGAALz3CAALCXGBjACs9wgACwlxQYgArP +cIAAsJcQGMAIz3CAALCXDBiACM9wgACwlwgYQAjPcYAANJeAGQAIfBnAB3gZgAd0GUAHcBkAB2wZ +AAdoGYAGZBlABmAZAAZcGcAFWBmABVQZQAVQGQAFTBnABEgZgAREGUAEQBkABO+hzqGtoYyhLBnA +AigZgAIkGUACIBkAAhwZwAEYGYABFBlAARAZAAFjoWogAAPYGQAAaiDAAtQZAABqIIAC0BkAAGog +QAHIGQAAaiAAAcQZAABqIMAAwBkAAGoggAC8GQAAaiBAALgZAABqIAAAtBkAAGoggAHMGQAAQMMB +wALBUyfNNQwUBjBTJcQ1UybFNde6qXOiDSAFEBQHMHoPYAwA2C4IAAPPcAAArd4yD4AAlgogDgDY +z3WgAKwvGYWJuBmlz3CAAJgIAICA4MogIQLKISEAyA5hBc8hYQbPcIAAnAgBgCkIXgBA2c9wgABw +iiawMLknsBmF8LgZhQXyjrgZpQfwirgZpQXw3gugBgDYDQMADeB4ANtgqCGoQqjgfwPg8cBODE/+ +z3OAACw0z3WAAGRLQIUM6QCjlOriCSABD9jWDqAGCNgB2AClDPAA3sCjCOqmCCABD9iiDqAGCNjA +pXkET/7xwAYMT/7PcIAAjAgAgM92gABUG6CGz3eAAFAbBCCCDw8AAOAEIoEPAQAAAHJpRHtnfaCm +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 +/gDMByL3yiPiCAHY0cDgfgnZ4H8goOB48cDaDa/1ANjPdYAA3DQghUB5IIUpCJAA5QjQAOt2YHkB +2Lhwz3AAALoZCiHAD8lyiiPPB4EHL/eKJIMPYHkB2CCFEQhQAGB5AdgghX8I0QBgeQLYCugghWB5 +AtgghSUIUQBgeQPYjuh+CQAAIIVgeQjYEHnPcIAAsBHaDI/2FfAghWB5AtgghQ8IUABgeQLYIIUb +CJEAYHkI2BB5z3CAAKgOsgyP9gfYhvDrdmB5Ati4cM9wAAC7GQohwA/JcoojTgX1Bi/3iiSDD2B5 +AdjtCJEAIIVgeQLYgOAghQjYCPRAeRB5z3CAAHQMB/BAeRB5z3CAAIwNXgyP9l3wYHkC2JHoIIXr +d2B5Adi4cGfYBrgKIcAP6XKKI44MnQYv94okgw/PcIAA1DQggGB5ANgghU0IEQNgeQjYIIUPCJAA +YHkI2CCFkehgeQLYIIUbCFEAYHkI2BB5z3WAAAAW9guv9qlwqXAl8GB5CNgQec91gAC8FOILr/ap +cKlwG/BgeQLYgeAghQjYDPRAec91gAAsGBB5wguv9qlwqXAL8EB5z3WAAOgWEHmuC6/2qXCpcC4M +j/YB2MIOgAM2DI/9oglAAM4IAAB9BI/1CHFYiQGAAqGI6lmJgOLCIKIAwCChAAKh4H7gePHA4cXP +cIAA1DQggKHBYHkE2I8IUQCCCy/8iiDMDoMIUQDPdYAALCwAhQHgAKUXCFEAAdnPcKAAyBwxoM4J +oAYocEokwHCoIIACz3EBAEJpz3CgAOwnJqCLcZIKr/aKIEYJAIVCIECAAKUH9ADZz3CgAMgcMaAA +FAUxTCVAgMohwg/KIsIHyiCCDwAArCjKI4IPAABHAzwFIvfKJCIA0QOv9aHA4H7gePHASguP9c92 +gAAsLACGAeAAphUIUQAB2M9xoADIHBGhQgmABs91gADcNCCFYHkA2NcIkQDH2JS4z3WgAOwnBqXP +dwAAgivPcAMAgisGpc9wAwDCRAalz3ADAAIsBqXPcAMAQkUGpc9xAADCdM9wAwDCdAalz3ADAIJv +BqXPcAMAgmwGpcbYkLgGpSal1gigBgrYz3AAAIJsBqXKCKAGCtjPcAAAAiwGpboIoAYK2M9wAABC +RQalrgigBgrYz3AAAIJvBqWeCKAGCtjmpZYIoAYK2M9wAADCRAaligigBgrYz3ATAMYABqV6CKAG +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 +gACEnAGADwhRAM9wgABQOweAz3GAAPhHIIFCIQGAyiFiAJDpz3GAAHzXLIGK6c9xgABk1xYJb/cj +gQoO7/8C2dHA4H7xwOIJj/HPcIAANCwEgKCAz3CAAGxHAIBCIACAyiBiADa9h+jPcIAAiEeODYAA +z3CAAGTXIYjPcIAAwEfPdoAAhJyK6SCAQiEBgMohYgAE6SCGreliDYAAz3CAANxHWg2AAAGGoghv +9whxIYYPeB0JBQAKIcAP63KKIA0D19tKJAAAOQMv87hzz3GAAPhHIIFCIQGAyiFiAAbpHWUjhsm9 +Cw1AEFoN7/8A2aUBj/HgeOHF4carChAAQCLDAyS7w7oC8ADalQoVBDMmgnCAALRMQCeNclR9IH3A +iAEZkgMB4AEQggQBGZIAARCCBAEZkgABEIIEARmSAAEQggQBGZIAARCCBAEZkgABEIIEARmSAAEQ +ggQBGZIAARCCBAEZkgABEIIEARmSAAEQggQBGZIAARCCBAEZkgABEIIEARmSAAEQggQBGZIAARCC +BAEZkgABEIIEARmSAEIjQ4Cz9cHG4H/BxeHF4cYj6mNqIrvBugLwANo1ChUBMyaCcIAAoExAJ41y +VH0gfcCABBmQAwTgBBACBAQZkAAEEAIEBBmQAAQQAgQEGZAAQiNDgOP1wcbgf8HF4cXhxqsKEABA +IsMDJLvDugLwANqVChUEMyaCcIAApExAJ41yVH0gfcCQAhmUAwLgAhACBQIZlAACEAIFAhmUAAIQ +AgUCGZQAAhACBQIZlAACEAIFAhmUAAIQAgUCGZQAAhACBQIZlAACEAIFAhmUAAIQAgUCGZQAAhAC +BQIZlAACEAIFAhmUAAIQAgUCGZQAAhACBQIZlAACEAIFAhmUAAIQAgUCGZQAQiNDgLP1wcbgf8HF +8cB6D2/xUyFCAE4iDQHPcqAAFATJggDbDiaCHwAAAAZQccohxg/KIsYHyiCGDwAAxiLKI4YPAACb +AsokZgAUASbzyiXGAIDhyiRNcMoizQDoIC0CTmDPcaAAOAQB4sipHQ1QEBENkBAdDdEQz3CgADgE +aKjPcKAAOARoqM9woAA4BGioYQdP8fHAzg5P8bpwenH6cppzCiIAIQogQCHIdQohwCEKJ0CTz3GB +ANYayidiEBJvFngIYUwjAKAEuIYg/gMFf8ohzA/KIswHyiCMDwAAwSHKI4wPAADuAMokbABwACzz +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 +DxQVIgIAFSaECYCBAADAFsIsEyQEKMARAkYTJAQowBHCXxMkBCjAEQ9NEyIEEMURAgATJPAcwBEB +ABMk7BzAEQAAEyRwABMlEBzAEQAAEyUAABMk4BzAEYAAEyVGaBMkBCjAEQAAEyUBABMkJBDAEQAA +FSQAAAAhDw4aIgAAQBYAARtwDQAAYYAAYyT//hsyAABAFwAAGyUPGw8ieAiAgf8AGzICABtBABsa +KAAAwBYAABslAgAbQAAAG3EBAGRwBwAAYQEAYyQAABskWgiAgQAAQBdQCICBAABAFu0PAGECAGRw +EAAAYQIAYyQBABskWgiAgQAAQBdSCICBAABAFuQPAGEEAGRwBwAAYQIAYyQCABskWgiAgQAAQBdU +CICBAABAFtsPAGEAAB0kAAAAIQACD24JAABhWgiAgQAAQBYAABslVgiAgQAbGigAAAAWAQAbJgAA +ABcNAABhcAiAgQAAQBYCABsmARAbaAAAGyQAAEAXXAiAgQAaGygPGw4idAiAgQAAQBYBABsmAABA +F9wGgIEPGhsiAAAbJQIAG0AAABtxAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAMwDgABgCYAAAAAAAAAAAAAoAIAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAkNERQKDRMXGRkZGQkJAABApoAAAAAAAECm +gAAAAAAAXNuAAEhyAAAwgAAAAAAAADGAAACZuViFMoAAAADKmrgzgAAAAFAAUDSAAAAAUAAANYAA +AABQAFA2gAAAAFAAUDeAAAAAAABQOIAAAAAAAAA5gAAAAFAAUDqAAAAAUABQO4AAAABQAFA8gAAA +AFAAAD2AAACZyQpQPoAAAFW4iMk/gAAAAAAAgjCAAAAAAAAAMYAAAJm5WIUygAAAAMqauDOAAAAA +UABQNIAAAABQAAA1gAAAAFAAUDaAAAAAUABQN4AAAAAAAFA4gAAAAAAAADmAAAAAUABQOoAAAABQ +AFA7gAAAAFAAUDyAAAAAUAAAPYAAAJnJClA+gAAAVbiIyT+AAAAAAACCMIAAAAAAAAAxgAAAAAAA +ADKAAAAAAAAAM4AAAAAAAAA0gAAAmkUAADWAAACqyqrKNoAAAAAgACA3gAAAACAAIDiAAAAAIAAA +OYAAAAAgACA6gAAAqsqqKjuAAAAAEJbKPIAAAAAAAAA9gAAAAAAAAD6AAAAAAAAAP4AAAAAAAAAw +gAAAAAAAADGAAAAAAAAAMoAAAAAAAAAzgAAAAAAAADSAAACaRQAANYAAAKrKqso2gAAAACAAIDeA +AAAAIAAgOIAAAAAgAAA5gAAAACAAIDqAAACqyqoqO4AAAAAQlso8gAAAAAAAAD2AAAAAAAAAPoAA +AAAAAAA/gAAAAAAAAP//AAClAQEAuQHfADsCLQCxABsAFgEbAK8AGwAUARsAbACgANEAoABvAIMA +cQCDAHMAMwDUAQYA0AEAAHgASQB5AGoA3gBqAKgAAAANAQAApgA/AKcAAQALAT8ADAEBAAQACACc +AcwAnQHMANUBzADWAcwAtAAgABkBIACPAIgA9ACIAJAAIgD1ACIAkQAEAPYABACFAAAAhgAAAIcA +VQCIAAAAiQCqAIoAAACLAN0AjAAAAIUAAQCGAAEAhwBVAIgAAACJAKoAigAAAIsA3QCMAAAAhQAC +AIYAAwCHAFUAiAAAAIkAqgCKAAAAiwDdAIwAAACFAAMAhgAHAIcAVQCIAAAAiQCqAIoAAACLAN0A +jAAAAPv/AAD//wAAuQHfADsCLQCxABsAFgEbAK8AGwAUARsAbACgANEAoABvAIMAcQCDAHMAMwDU +AQYA0AEAAHgASQB5AGoA3gBqAKgAAAANAQAApgA/AKcAAQALAT8ADAEBAAQACACcAcwAnQHMANUB +zADWAcwAtAAgABkBIACPAIgA9ACIAJAAIgD1ACIAkQAEAPYABACoAAwADQEMAIUAAACGAAAAhwCZ +AIgAAACJAKoAigAAAIsA3QCMAAAAhQABAIYAAQCHAJkAiAAAAIkAqgCKAAAAiwDdAIwAAACFAAIA +hgADAIcAmQCIAAAAiQCqAIoAAACLAN0AjAAAAIUAAwCGAAcAhwCZAIgAAACJAKoAigAAAIsA3QCM +AAAA+/8AAP//AAC5Ad8AsQAbABYBGwCvABsAFAEbAGwAoADRAKAAbwCDAHEAgwB2AIMAcwAzAG4A +MwBwADMAcgAzANcAMwDUAQYA0AEAAH4APADjADwAeABJAN0ASQB/AFoA5ABaAKoAPwCrAAEADwE/ +ABABAQB5AGoA3gBqAKgAAAANAQAApgA3AKcAAQALATcADAEBAAQACACcAcwAnQHMANUBzADWAcwA +tAAgABkBIAAxAgwAMgIMADMCvQA2AgwANwIMADgCvQCgAIgABQGIAKEA1QAGAdUAogAEAAcBBACP +AIgA9ACIAJAAIgD1ACIAkQAEAPYABACfAAwA+wAMAJQAAACVAAAAnACXAJ0A0ACaAI0AmAARAJYA +MwCXAHcAlAABAJUAAQCcAJcAnQDQAJoAjQCYABEAlgAzAJcAdwCUAAIAlQADAJwAlwCdANAAmgCN +AJgAEQCWADMAlwB3AJQAAwCVAAcAnACXAJ0A0ACaAI0AmAARAJYAMwCXAHcA+gAAAPkAAAACAZcA +AwHQAAABjQD+ABEA/AAzAP0AdwD6AAEA+QABAAIBlwADAdAAAAGNAP4AEQD8ADMA/QB3APoAAgD5 +AAMAAgGXAAMB0AAAAY0A/gARAPwAMwD9AHcA+gADAPkABwACAZcAAwHQAAABjQD+ABEA/AAzAP0A +dwCFAAAAhgAAAIcAVQCIAAAAiQCnAIoAAACLAN4AjAAAAIUAAQCGAAEAhwBVAIgAAACJAKcAigAA +AIsA3gCMAAAAhQACAIYAAwCHAFUAiAAAAIkApwCKAAAAiwDeAIwAAACFAAMAhgAHAIcAVQCIAAAA +iQCnAIoAAACLAN4AjAAAAOsAAADqAAAA7ABVAO0AAADuAKcA7wAAAPAA3gDxAAAA6wABAOoAAQDs +AFUA7QAAAO4ApwDvAAAA8ADeAPEAAADrAAIA6gADAOwAVQDtAAAA7gCnAO8AAADwAN4A8QAAAOsA +AwDqAAcA7ABVAO0AAADuAKcA7wAAAPAA3gDxAAAApAGAAKEBQAD7/wAA//8AAKUBAQC5Ad8AsQAb +ABYBGwCvABsAFAEbAGwAoADRAKAAbwCDAHEAgwB2AIMAcwAzAG4AMwBwADMAcgAzANcAMwDUAQYA +0AEAAH4APADjADwAeABJAN0ASQB/AFoA5ABaAKoAPwCrAAEADwE/ABABAQB5AGoA3gBqAKgAAAAN +AQAApgA3AKcAAQALATcADAEBAAQACACcAcwAnQHMANUBzADWAcwAtAAgABkBIAAxAgwAMgIMADMC +vQA2AgwANwIMADgCvQCgAIgABQGIAKEA1QAGAdUAogAEAAcBBACPAIgA9ACIAJAAIgD1ACIAkQAE +APYABACfAAwA+wAMAJQAAACVAAAAnACXAJ0A0ACaAI0AmAARAJYAMwCXAHcAlAABAJUAAQCcAJcA +nQDQAJoAjQCYABEAlgAzAJcAdwCUAAIAlQADAJwAlwCdANAAmgCNAJgAEQCWADMAlwB3AJQAAwCV +AAcAnACXAJ0A0ACaAI0AmAARAJYAMwCXAHcA+gAAAPkAAAACAZcAAwHQAAABjQD+ABEA/AAzAP0A +dwD6AAEA+QABAAIBlwADAdAAAAGNAP4AEQD8ADMA/QB3APoAAgD5AAMAAgGXAAMB0AAAAY0A/gAR +APwAMwD9AHcA+gADAPkABwACAZcAAwHQAAABjQD+ABEA/AAzAP0AdwCFAAAAhgAAAIcAVQCIAAAA +iQCnAIoAAACLAN4AjAAAAIUAAQCGAAEAhwBVAIgAAACJAKcAigAAAIsA3gCMAAAAhQACAIYAAwCH +AFUAiAAAAIkApwCKAAAAiwDeAIwAAACFAAMAhgAHAIcAVQCIAAAAiQCnAIoAAACLAN4AjAAAAOsA +AADqAAAA7ABVAO0AAADuAKcA7wAAAPAA3gDxAAAA6wABAOoAAQDsAFUA7QAAAO4ApwDvAAAA8ADe +APEAAADrAAIA6gADAOwAVQDtAAAA7gCnAO8AAADwAN4A8QAAAOsAAwDqAAcA7ABVAO0AAADuAKcA +7wAAAPAA3gDxAAAApAGAAKEBQAD7/wAA//8AALkBwQDUAQMA0AEEAHgAPADdADwAeQBqAN4AagCo +AAEADQEBAAQACACcAcwAnQHMANUBzADWAcwAtAAgABkBIACPAIgA9ACIAJAAAAD1AAAAkQAGAPYA +BgCFAAQA6wAEAKQBgABdAjMASgIOAEwCDgBNAgEArQEHALMBBAC4AQAAuwFWAFACCwBRAgMAUgIB +AFMCAABUAgsAVQIDAFYCAQBXAgAAZgIGAGgCBwBqAgcAbAIHAG4CBQBwAgwAfQIGAH8CBwCBAgcA +gwIHAIUCBQCHAgwAtQAhABoBIQBLAgEAoQFAALMAAAAYAQAAlAILAJUCAwCWAgEAlwIAAJgCCwCZ +AgMAmgIBAJsCAACyADAAFwEwAJwCDwChAg8AoAKIAJ8CiACeAogAnQKIAKUCiACkAogAowKIAKIC +iAD7/wAA//8AALkBwQDUAQMA0AEAAHgAPADdADwAeQBqAN4AagCoAAEADQEBAAQACACcAcwAnQHM +ANUBzADWAcwAtAAgABkBIACPAIgA9ACIAJAAAAD1AAAAkQAGAPYABgCFAAQA6wAEAKQBgABdAjYA +SgINAEwCDwBNAgEArQEGALMBBAC4AQAAuwFWAFACCwBRAgMAUgIBAFMCAABUAgsAVQIDAFYCAQBX +AgAAZgIGAGgCBwBqAgcAbAIHAG4CBQBwAgwAfQIGAH8CBwCBAgcAgwIHAIUCBQCHAgwAtQAhABoB +IQChAUAA+/8AAP//AAC5AcEA1AEDANABBAB4ADwA3QA8AHkAagDeAGoAqAABAA0BAQAEAAgAnAHM +AJ0BzADVAcwA1gHMALQAIAAZASAAjwCIAPQAiACQAAAA9QAAAJEABgD2AAYAhQAEAOsABACkAYAA +XQIzAEoCDgBMAg4ATQIBAK0BBwCzAQQAuAEAALsBVgBQAgsAUQIDAFICAQBTAgAAVAILAFUCAwBW +AgEAVwIAAJQCCwCVAgMAlgIBAJcCAACYAgsAmQIDAJoCAQCbAgAAZgIGAGgCBwBqAgYAbAIHAG4C +BQBwAgwAfQIGAH8CBwCBAgYAgwIHAIUCBQCHAgwAsgAwABcBMACzAAAAGAEAAJwCDwChAg8AoAKI +AJ8CiACeAogAnQKIAKUCiACkAogAowKIAKICiAC1ACEAGgEhAKEBQABLAgEA+/8AAP//AAC5AcEA +1AEDANABAAB4ADwA3QA8AHkAagDeAGoAqAABAA0BAQAEAAgAnAHMAJ0BzADVAcwA1gHMALQAIAAZ +ASAAjwCIAPQAiACQAAAA9QAAAJEABgD2AAYAhQAEAOsABACkAYAAXQI2AEoCDQBMAg8ATQIBAK0B +BgCzAQQAuAEAALsBVgBQAgsAUQIDAFICAQBTAgAAVAILAFUCAwBWAgEAVwIAAGYCBgBoAgcAagIG +AGwCBwBuAgUAcAIMAH0CBgB/AgcAgQIGAIMCBwCFAgUAhwIMALUAIQAaASEAoQFAAPv/AAAAAAAA +AgAAAA3SEtIT0hTSDNIV0gvSAtIR0gRDABAUEAkQERABQBvSHNIA0goACwAEAA4AtQAaAQ8AQgC8 +AMMAIQEoAbYAtwC4ALkAvQC+AL8AwAAbARwBHQEeASIBIwEkASUBCgAAAAsAAAC2AAAAtwAAALgA +AAC5AAAAGwEAABwBAAAdAQAAHgEAAL0AAAC+AAAAvwAAAMAAAAAiAQAAIwEAACQBAAAlAQAAEtIA +ABPSAAAAAAEAAgADACwAZAB0AIAAjAChAAcAAAAAAAEAAgADAAAAAAC3EyIAuBQjALkVJAC7FiUA +vBcmAL0YJwDAGSgAxBopAAcbAAAIHAEACx0CAAweAwAQHwQAIiEFACQiBgAmIwcAKCQIAColCQAs +JgoALicLADAoDAA0KQ0AOCoOADwrDwBALBAAZC4RAGgvEgBsMBMAcDEUAHQyFQB4MxYAfDQXAIA1 +GACENhkAiDcaAIw4GwCROhwAlTsdAJk8HgCdPR8AoT4gAKU/IQAkSQYCLEoKAjRLDQE8TA8BZE0R +AWxOEwF0TxUBfFAXAYRRGQGVUh0BnVMfAQEEAAACBQEAAwYCAAQHAwAFCAQABgkFAAcKBgAICwcA +CQwIAAoNCQALDgoADA8LAA0QDAAOEQ0AAUAABAJBAQQDQgIEBEMDBAVEBAQGRQUEB0YGBAhHBwQJ +SAgEBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAhgCGAIYA +hgCGAIYAhgCGAIYAhgCGAIYAhgCGAIcAiAAEAAUABgAGAAcACAAIAAkACQAKAAsADAAMACQAJQAl +ACYAJwAoACgARABFAEYARgBHAEgASABJAEoASgBLAEwAaABoAGkAagBrAGwAbQBtAG4AbwBvAHAA +cQBxAHIAcgByAHIAcgByAHIAcgByAHIAcgByAHIAcgByAHIAcgByAHIAcgAKAD8AhgCGAIYAhgCG +AIYAhgCGAIYAhgCGAIYAhgCGAIcAiAAEAAUABgAGAAcACAAIAAkACQAKAAsADAAMACQAJQAlACYA +JwAoACgARABFAEYARgBHAEgASABJAEoASgBLAEwAaABoAGkAagBrAGwAbQBtAG4AbwBvAHAAcQBx +AHIAcgByAHIAcgByAHIAcgByAHIAcgByAHIAcgByAHIAcgByAHIAcgAKAD8AAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAQACAAIAAwAEAAQABQAGAAYABwAIAAgACQAKAAoACwAMAAwADQAO +AA4ADwAmACcAKAApACoARgBGAEcASABIAEkASgBKAEsATABoAGkAagBqAGsAbABsAG0AbgBuAG8A +cABwAHEAcgByAHMAcwB0AHQAdAB0AHQAdAB0AHQAdAB0AHQAdAB0AHQAdAB0AHQAdAB0AAoAPwAA +AAAAAAAAAAAAAAAAAAAAAQABAAIAAwADAAQABQAFAAYABwAHAAgACQAJAAoACwALAAwADAANAA4A +IwAkACUAJgAnACgAKQBEAEUARgBGAEcARwBIAEgASQBJAEoASwBoAGgAaQBqAGsAbABtAG0AbgBv +AG8AcABxAHEAcgByAHMAcwBzAHMAcwBzAHMAcwBzAHMAcwBzAHMAcwBzAHMAcwBzAAoAPwAAAAAA +AAAAAAAAAAAAAAAAAQACAAIAAwAEAAQABQAGAAYABwAIAAgACQAKAAoACwAMAAwADQAOAA4ADwAm +ACcAKAApACoARgBGAEcASABIAEkASgBKAEsATABoAGkAagBqAGsAbABsAG0AbgBuAG8AcABwAHEA +cgByAHMAcwB0AHQAdAB0AHQAdAB0AHQAdAB0AHQAdAB0AHQAdAB0AHQAdAB0AAoAPwAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAABAAEAAgADAAMABAAFAAUABgAHAAcACAAJAAkACgAKAAsAJAAlACUA +JgAnACgAKABEAEUARgBGAEcASABIAEkASgBKAEsATABoAGgAaQBqAGsAbABtAG0AbgBvAG8AcABx +AHEAcgByAHIAcgByAHIAcgByAHIAcgByAHIAcgByAHIAcgByAHIAcgByAAoAPwCAZYAAAAAAALIM +LAH/////////AAH//wID////BP//////////////////////Bf8G/wf/CP8J/wr/C/8M////Df// +/w7///8P////EP//////////////////////////////////////////////Ef///xL///8T//// +FP///xX///8W////F////xj///8Z////Gv///xv/////HP///x3///8e////H////yD///8h//// +//////////////////8iIyT/JSYn//8o////Kf////////////////////////////////////// +////////////////////////////////////////AAABAAEBAAAAAAAAAAEAAAAAAAAAAAAAAAAA +AAMAAAAAAAAAAQAAAAAAAAB0LQEAAAAAADxrAAABAAAAfNgBAAIAAADw1wEAAwAAAAgjAgAEAAAA +dC0BAAUAAACgEwEABgAAAODmAAAHAAAAKBQBAAgAAABEPgAACQAAAMxeAAAKAAAAUMIAAAsAAABQ +OAAADAAAAAgmAgANAAAAGOAAAA4AAABY4AAADwAAABTgAAAQAAAAVOAAABEAAABEQgEAEgAAAATv +AQATAAAAEDIAABQAAACAawEAFQAAAJRXAQAWAAAAfGcBABcAAABc1wEAGAAAAOyQAQAZAAAAYAYB +ABoAAABwLQEAGwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP//////////AAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADU +jgAA1I4AANSOAADIeQAA1I4AANSOAAAsegAA1I4AANSOAADUjgAA1I4AANSOAADUjgAA1I4AANSO +AADUjgAASIAAAJB/AACAfwAA+H4AALh/AADgfgAA1I4AANSOAAAkhgAA9IgAAKCKAADUjgAA1I4A +ANSOAAA8jgAAPIUAAHSFAADghAAA1I4AANSOAADUjgAA+I0AANSOAADAhAAA1I4AANSOAADUjgAA +1I4AANSOAADUjgAA1I4AANSOAADUjgAA1I4AANSOAADUjgAA1I4AANSOAADUjgAA1I4AANSOAADU +jgAA1I4AANSOAADUjgAA1I4AANSOAADUjgAA1I4AANSOAADUjgAA1I4AANSOAADUjgAA1I4AANSO +AADsegAA1I4AANSOAADUjgAA1I4AANSOAABciwAA1I4AANSOAADUjgAA1I4AANSOAABQewAA1I4A +ALx8AABcfAAA7HsAAEx8AACEdwAA1I4AAFh3AADUjgAA1I4AANSOAADUjgAA1I4AANSOAADUjgAA +1I4AAOR2AADUjgAA1I4AANSOAADUjgAA1I4AANSOAADUjgAA1I4AANSOAADUjgAA1I4AANSOAADU +jgAA1I4AANSOAADUjgAA1I4AAKR+AADUjgAA1I4AAGiAAADUjgAA1I4AANSOAADUjgAA1I4AALCB +AACYgAAA1I4AANSOAADUjgAA1I4AANSOAADUjgAA1I4AANSOAADUjgAA1I4AANSOAADUfAAA1I4A +ANSOAADUjgAA1I4AANSOAADUjgAA1I4AAJCNAADUjgAA8I0AAPyKAADUjgAA1I4AADh1AAC8igAA +1I4AANSOAAAAfwAAGH8AANSOAADUjgAADHsAAMh3AADUjgAA1I4AANSOAAB8hQAAyH4AANSOAADU +jgAA1I4AANSOAADUjgAA1I4AAAh+AADUjgAA/I4AAIiPAABojwAAoI8AADSPAAAcjwAAqI8AAPiO +AADUjgAA1I4AANSOAADUjgAA1I4AANSOAADUjgAA1I4AABR7AADUjgAA1I4AANSOAADUjgAA1I4A +ANSOAADUjgAA1JEAAOCSAADkdQAAWHYAANSOAADUjgAA1I4AANSOAADUjgAA+HcAANSOAADUjgAA +1I4AANSOAADUjgAA1I4AANSOAADUjgAA1I4AANSOAADUjgAA1I4AAGiQAACsjwAAGJEAAPyQAAA8 +kAAAxI8AADCRAADkkAAA1I4AANSOAADUjgAA1I4AANSOAADUjgAA1I4AANSOAAAseAAAGHkAALh4 +AABIkQAAcHYAAKB5AAA4egAA1I4AANSOAADUjgAA1I4AACR6AAAoegAA1I4AANSOAADMeQAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAkJ0AANibAAD0ngAAAJ4AAAigAAAAAAEA/////wAAAAD//////////wEAAAD4 +EwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAND+AAAAAAAAAAAYIKAAICCgAEAh +oABIIaAAHCCgACQgoABEIaAATCGgACggoAAwIKAAaCGgAHAhoAAsIKAANCCgAGwhoAB0IaAAOCCg +ADwgoAB4IaAAfCGgAJgRAAAA/wMAUBMAAAD/BQDcEQAAAP8tALgRAAAA/z0ANBEAAAD/BABcEQAA +AP8lAODEAAAA/90AbBIAABAQTAA4EwAAAP8iAAASAAAA/yYA2BIAAAD/KACkEgAAAP8gANDDAAAA +IAAAFMMAAAD/MAAAAAAAAAAAAAABAQA8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PBUV +FRU8PDw8FRUVFTw8PDwAAAAAAAAAAAAAAAAAAAAAPDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8 +PDw8PDwVFRUVPDw8PBUVFRU8PDw8AAAAAAAAAAAAAAAAAAAAADw8PDw8PDw8PDw8PDw8PDw8PDw8 +PDw8PDw8PDw8PDw8FRUVFTw8PDwVFRUVPDw8PAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAACoBAEAGKsAACwk +AAAYqwAAGKsAABirAACEDAAAFOYBANzRAAAYqwAAGKsAACg1AAAoNQAAKDUAACg1AAAoNQAAKDUA +ACg1AAAYqwAAGKsAABirAAAYqwAAVFYAABirAAAYqwAAGKsAABirAAAYqwAAwNEAABirAAAYqwAA +VMIAAAAAAACQ6wAAlOsAALQCAACgAgAAGEEBAAAAAADQuAAArMQBAPi4AADUxAEAHLkAAPzEAQD0 +pYAAvD2AAGCggAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPAhAAC4IQAA4MCAAAAC +AAAAAAAAFPsAAOT6AADgwoAAQAUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABj7AAD0PQEAcMmA +AFQAAAAAAAAAFPsAAMg8AQAgyIAAUAEAAAAAAAAU+wAAxDYBAHAJgAAIAAAAAQAAABT7AADg+QAA +AAAAAFABAAAAAAAAFPsAAFA3AQBUNoAAAgAAAAAAAAAU+wAAXDYBAGwJgAAEAAAAAAAAABz7AADk ++gAAxMmAACoAAAAAAAAAFPsAAOT6AAC4RIAACAAAAAAAAAAAAAAA7PoAAAAAAAAAAAAAAQAAAAAA +AAAA+wAAAAAAAAAAAAAAAAAAAAAAAOj6AAAAAAAAAAAAAAAAAAAU+wAAIMcBAAAAAAAAAAAAAAAA +ABT7AADgxgEAeAmAAAQAAAAAAAAAbgBuAGkAwACgAFAAgAC+AFABfQA+AAEAAQABAFgCKADmAS0A +VQM8ANwBYwAAAG4AbgBpAMAAoABQAIAAvgBQAX0APgABAAEAAQBYAigA5gEtAFUDPADcAWMAAAAA +AAAAAQEAAPADAQAV0gAAAAAAAP8DAADwAwEADNIAAAAAAAD/AQAA8AMBABXSAAAKAAAAAPwPAPAD +AQAM0gAACQAAAAD+AwDwAwEAFdIAABQAAAAAAPA/8AMBAAzSAAASAAAAAAD8B/ADAQAG0gAAAAAA +AP8BAADwAwEAB9IAAAAAAAD/AwAA8AMBAAbSAAAJAAAAAP4DAPADAQAH0gAACgAAAAD8DwDwAwEA +BtIAABIAAAAAAPwH8AMBAAfSAAAUAAAAAADwPwAAAAAAAAAAAAAAAAEAAAABAAAACgAAAAUAAAAF +AAAABgAAAAoAAAAKAAAABgAAAAQAAAAFAAAABgAAAAUAAAAFAAAABgAAAAYAAAAGAAAABgAAAAcA +AAAHAAAABwAAAAgAAAAIAAAACAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAIAAAABAAAAAgAA +AAEAAAABAAAAAwAAAAMAAAACAAAAAQAAAAQAAAAAAAAAAAAAAAIAAAABAAAAAQAAAAAAAAAAAAAA +BwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAACwBAABeAQAAAQAAAAEA +AAABAAAAAQAAAAMAAAAAAAAAAAAAAMgNAQAsEgEAHBEBAJgSAQAoEgEAYBABAJQSAQDwDgEA7A4B +AGDjFgAg1hMAAAAAABAAAAAAgAAAAACgABAnAADoAwAA6AMAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAgAAAAIAAAABAAAAAQAAAAIAAAAFAAAAAgAAAAIAAAAFAAAAAgAAAAIAAAABAAAAAQAAAAUA +AAAFAAAAAgAAAAUAAAAFAAAAAAAAAAUAAAACAAAAAgAAAAAAAAAAAAAAAAAAAAUAAAAFAAAAAAAA +AAUAAAACAAAAAgAAAAUAAAAFAAAABQAAAAAAAAAFAAAAAgAAAAUAAAABAAAAAQAAAAIAAAACAAAA +AgAAAAUAAAAFAAAAAgAAAAUAAAABAAAAAQAAAAIAAAACAAAAAgAAAAUAAAAFAAAAAgAAAAIAAAAF +AAAAAQAAAAIAAAAFAAAAAgAAAAUAAAAFAAAABAAAAAUAAAAFAAAAAQAAAAUAAAAFAAAABQAAAAIA +AAACAAAABQAAAAUAAAAFAAAAAQAAAAUAAAAFAAAABQAAAAIAAAACAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAABEAABAAAAAoAAAUIABgIQAAIgAAADwAAB +QwAGAxAAAsAAAAPAAAFDAAYEEAACQAAAAoAAAUQABgURAABAAAADwAABRQAGBhEAAOAAAAPAAAFF +AAYHEQABAAAAAoAAAUYABggRAAIgAAADwAABRwAGCREAAsAAAAPAAAFHAAYKEQACQAAAAoAAAUgA +BgsSAABAAAADwAABSQAGDBIAAOAAAAPAAAFJAAYNEgABAAAAAoAAAUoABg4SAAIAAAACgAABTAAG +AXgAMAAAAFAAAAS2PAYCeABEAAAAUAAABLk8BgN5AAgAAABQAAAEuzwGBHkAHAAAAFAAAAS+PAYF +eQAwAAAAUAAABMA8BgZ5AEQAAABQAAAEwzwGB3oACAAAAFAAAATFPAYIegAcAAAAUAAABMg8Bgl6 +ADAAAABQAAAEyjwGCnoARAAAAFAAAATNPAYLewAIAAAAUAAABM88Bgx7ABwAAABQAAAE0jwGDXsA +MAAAAFAAAATUPAYOfAAQAAAAUAAABNo+BgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAgEBAAIBAAECAgIAAQEAAgECAQIA +AgABAgOAgICAgICAgAGAAoCAgICAwACQANAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAABuAAAAbgACAG4AbgBuAAIAaQBpAG4AAQDAAMAA6AABAKAAoAA2AQMAUABQAPUA +AQCAAIAA6AABAL4AvgC+AAEAUAFQAVABAQB9AH0ArwADAD4APgA+AAEAAQABAAEAAQABAAEAAQAB +AAEAAQABAAEAWAJYAlgCAQAoACgAKAABAOYB5gHmAQEALQAtAC0AAQBVA1UDVQMBADwAPAA8AAEA +3AHcAdwBAQBjAGMAYwABAAAAAAAAAAAAMgAFADIABQACAAgAZACgAAMAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACj3 +gAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/wAAAAAAAACMCowK +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAEAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAANDiAQAVAAAAAwAAALBIgAAAAAAAAAAAAAAAAAAc4gEA +BQAAAAMAAACwSIAAAAAAAAAAAAAAAAAAEOIBAAoAAAADAAAAsEiAAAAAAAAAAAAAAAAAANzfAQAK +AAAAAwAAALBIgAAAAAAAAAAAAAAAAAAs4QEACgAAAAAAAADQSIAAAAAAAAAAAAAAAAAALOEBAAoA +AAAAAAAA0EiAAAAAAAAAAAAAAAAAACzhAQAKAAAAAAAAANBIgAAAAAAAAAAAAAAAAAAs4QEACgAA +AAAAAADQSIAAAAAAAAAAAAAAAAAALOEBAAoAAAAAAAAA0EiAAAAAAAAAAAAAAAAAACzhAQAKAAAA +AAAAANBIgAAAAAAAAAAAAAAAAAAs4QEACgAAAAAAAADQSIAAAAAAAAAAAAAAAAAALOEBAAoAAAAA +AAAA0EiAAAAAAAAAAAAAAAAAACzhAQAKAAAAAAAAANBIgAAAAAAAAAAAAAAAAAAs4QEACgAAAAAA +AADQSIAAAAAAAAAAAAAAAAAALOEBAAoAAAAAAAAA0EiAAAAAAAAAAAAAAAAAACzhAQAKAAAAAAAA +ANBIgAAAAAAAAAAAAAAAAAA84gEACgAAAAMAAACwSIAAAAAAAAAAAAAAAAAAlOMBAAUAAAADAAAA +sEiAAAAAAAAAAAAAAAAAADTfAQAKAAAAAAAAANBIgAAAAAAAAAAAAAAAAACk3wEACgAAAAAAAADQ +SIAAAAAAAAAAAAAAAAAA1O0BAAoAAAADAAAAsEiAAAAAAAAAAAAAAAAAAAjgAQAKAAAAAAAAANBI +gAAAAAAAAAAAAAAAAACw4AEACgAAAAAAAADQSIAAAAAAAAAAAAAAAAAAMOEBAAoAAAAAAAAA0EiA +AAAAAAAAAAAAAAAAALDhAQAKAAAAAAAAANBIgAAAAAAAAAAAAAAAAADU4gEACgAAAAAAAADQSIAA +AAAAAAAAAAAAAAAAROMBAAoAAAAAAAAA0EiAAAAAAAAAAAAAAAAAAHDjAQAGAAAAAAAAANBIgAAA +AAAAAAAAAAAAAACA4wEABgAAAAAAAADQSIAAAAAAAAAAAAAAAAAAAAAAALBIgACwSIAAuCCgAGwg +oAAAgAEA/3/8/wAAAAAAAAAA0EiAANBIgACkIKAAOCCgAAEAAAD8////AAAAAAAAAADwSIAA8EiA +AKggoAA8IKAAEAAAAMf///8AAAAAAAAAABBJgAAQSYAArCCgAHghoABAAQAAP/7//wAAAAAAAAAA +MEmAADBJgACwIKAAfCGgAAAMAAD/8f//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOABABUAAAAD +AAAAsEiAAAAAAABQAAAAAAAAAPwIgAC8qYAAGAAAAHypgAAAAAAAAAAAAAAAAAD8CIAAvKmAABgA +AAB8qYAAAAAAAAAAAAAAAAAAfwAAAAAAAAAAfwAAAAAAAAAAAAAAAAAAoIuAAAAAAAAAAAAA//8A +AAEAAAAAAAAABwAAAAAAAAAAAAAAAAAAAAABAgMEBAQEBAUGBwgICAgICQoLDA0AAG47aDtiO1w7 +bjpoOmI6XDpuOWg5YjlcOW4raCtiK1wrbipoKmIqXCpuKWgpYilcKW4oaChiKFwobidoJ2InXCdu +JmgmYiZcJm4laCViJVwlbiRoJGIkXCRuI2gjYiNcI24iaCJiIlwibiFoIWIhXCFuIGggYiBcIGIT +XBNuEmgSYhJcEm4RaBFiEVwRbhBoEGIQXBBuAmgCYgJcAm4BaAFiAVwBbgBoAFQAAABuO2g7Yjtc +O246aDpiOlw6bjloOWI5XDluK2grYitcK24qaCpiKlwqbiloKWIpXCluKGgoYihcKG4naCdiJ1wn +biZoJmImXCZuJWglYiVcJW4kaCRiJFwkbiNoI2IjXCNuImgiYiJcIm4haCFiIVwhbiBoIGIgXCBu +EmgSYhJcEm4RaBFiEVwRbhBoEGIQXBBXEFIQTRBJEG4BaAFiAVwBbgBoAGIAXABUAAAAAAAAAAAA +AAAdAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKGIBAAgAAAADAAAAsEiA +AHQKgAD0CoAAdAuAAPQLgAAKDREUCg0RFBkZGRkKCgAAAAAAAAYGBgYJCQkJAAYAAAAFBgcIDQ4P +EBUWFxgZAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwUmpocJJqampqak5pugT81CYsr +AAAAMgAAAI8AjACKAEMADwAgaDcAAAARAD46IBEAAAIlAAAMLwAAAi85OQAKJTy3R2+KAAcUJ2Iu +AAACABcAABYOFhQAAAAAQkIXAAUQCiAwQAAABgYKEvYV9gb2CfYM9g/2APYDAIAAAAAAWFtjYzEA +AAwQFBggCAQAADw4NDAsKCQgHBgUEAwIBAALBwMAOzczLysnIx8bFxMPCwcDADs3My8rJyMfGxcT +DwsHAzEwOjU3OjIwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACIAAAAQ +AAAAHQAAADcAAABvAAAAoAAAAHAAAAAFAAAA9gAAAFoAAABpAAAAAAAAAGkAAAACAAAAHwAAAEgA +AAAgAAAASAAAAAgAAAABAAAAMwAAAH8AAAA0AAAAfwAAADcAAAABAAAAOAAAAE8AAAA7AAAAfwAA +ADwAAAB/AAAAMQAAAAAAAAAyAAAAAAAAADUAAAAAAAAANgAAAAAAAAA5AAAAAAAAADoAAAAAAAAA +DQAAACoAAAAOAAAAegAAAA8AAAAQAAAA8wAAAEgAAAD0AAAASAAAAAAAAAABAQEBAQEBAQICAgIC +AgICAwMDAwMDAwMBAgAABAAAAAUAAAAAAAAAAAAAAAAAAAAAAAAAoQMOHuEAAAChAw4e4QAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAChAw4e4QAAABX2Y/aw9vz2RveQ99j3H/hl+Kn47fgv+XD5sPnu+Sv6Z/qi ++tz6FPtL+4H7tvvq+xz8Tfx9/Kv82fwF/TD9Wf2C/an9z/30/Rf+Of5a/nr+mP62/tL+7f4G/x7/ +Nf9L/2D/c/+F/5b/pv+0/8H/zf/Y/+H/6f/w//b/+v/9//////////3/+v/2//D/6f/h/9j/zf/B +/7T/pv+W/4X/c/9g/0v/Nf8e/wb/7f7S/rb+mP56/lr+Of4X/vT9z/2p/YL9Wf0w/QX92fyr/H38 +Tfwc/Or7tvuB+0v7FPvc+qL6Z/or+u75sPlw+S/57fip+GX4H/jY95D3Rvf89rD2Y/ZwuYO6lruq +vL690r7nv/zAEcInwz3EU8VqxoDHl8ivycbK3sv2zA/OJ89A0FnRctKM06bUv9Xa1vTXDtkp2kTb +X9x63Zbesd/N4OnhBeMh5D7lWuZ355PosOnN6urrB+0k7kLvX/B98ZryuPPV9PP1Efcv+Ez5avqI ++6b8xP3i/gAAHgE8AloDeASWBbQG0QfvCA0KKwtIDGYNgw6hD74Q3BH5EhYUMxVQFm0XiRimGcIa +3xv7HBceMx9PIGohhiKhI7wk1yXyJgwoJilBKlordCyOLacuwC/ZMPExCjMiNDo1UTZpN4A4ljmt +OsM72TzvPQQ/GUAuQUJCVkNqRH1FqqoDAAAAAACqqgMAAPgAAACqA////wAAUKf0UVNlQX7DpBca +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 +NSYvw+G+ojXMiDkuV5PyVYL8R3qsyOe6KzKV5qDAmBnRnn+jZkR+VKs7gwvKjCnH02s8KHmn4rwd +FnatO9tWZE50HhTbkgoMbEjkuF2fbr3vQ6bEqDmkMTfTi/Iy1UOLWW632owBZLHSnOBJtNj6rAfz +Jc+vyo706UcYENVviPBvSnJcJDjxV8dzUZcjy3yhnOghPt2W3GGGDYUPkOBCfMRxqszYkAUGAfcS +HKPCX2r5rtBpkRdYmSc6uSc42RPrsyszIrvScKmJB6czti0iPJIVIMlJh/+qeFB6pY8D+FmACRca +2mUx18aEuNDDgrApd1oRHst7/KjWbTosAAECBAQAAAAEDAwIBAwEBEAAAACAAAAAAAEAAAACAABA +AAAAAAQAAEAAAABAAAAAAPBhAAABAQIBAgIDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAZAAA +ACoAAAAOAAAAAAABAQAAAAAAAAAAAAEBAAAAAAIAAQACAgMDAwECBP8IEP//lOIBAKDiAQCs4gEA +uOIBAMDiAQDI4gEAAAAAAQAAAAIAAAAEAAAACAAAABAAAAAgAAAAQAAAAIAAAAAbAAAANgQCBAIA +AAAAAQIQBAgAAAACEAQIAAAAAAEBAAECAQEBAAAAAAAAAAD/////AAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACADQAAACAAAIANAACADQAAACAAAIANAAAABgAAAAQA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +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 +HQhBAM9xgAC4NTyRFBQNMQ0NQRDPcYAAEDZZqYvqz3WAAHgKwY2A5gDZyiBBACTyIa0LCpEDAdge +8EEoDQIHfUEoAQSnec93gAB4CqCPUyVFER8NMgTGuQohwA/rcs9wAADNG5/bVQAgAYokgw8PDZ4R +ANgM3CcCz//PdoAACOoWJk0Rp42gr8l1FiVNEQClFBQAMUatx3GAAMjmArUAiQetABlCAQAbQgHH +8fHAggnP/wjIz3KgAMgfDhoYgAnIDxoYgArIEBoYgAsSATYCyCR4ERoYgAzIz3GAAABKLRoYgACB +AeAAocO4VwhRAwvIf9kKuSR4LygBAE4gggcA2A8ggAAEIQGAQiKNAhnyCyNAwBf0z3CgAIgg8CBQ +A892gAA8RwCGz3eAAEBHDQ0BEACHEnBQC4EGoKYAHwAUVQHP/+B4n+HMIO6HzCBOgAb3AnlBaQsK +EQiKIf8PBvAA2Q8hgQBhuRh54H8ocPHAsgjv/4ogDwqCJAI6mnF6clpziHWodwohgCEKIMAhwglg +AJ7BinC6CWAAi3GCxmpwsglgAMlxSnCqCWAAhMGpcKIJYACGwYjF6XCWCWAAqXEqcI4JYACKwQpw +hglgAIzBqXCKwc4O4ACQwotwyXHCDuAAksLJcITBug7gAJTChsCpca4O4ACWwpjGkMCSwcYOoADJ +cprHlMCWwboOoADpcslw6XGODuAAnMKcwJ7BpgwgAI7Cn8XCDeAQBG2OwEoJIAAkbbcIEACGwIrB +gg3v/0AlAhOLcITBdg3v/0AlAhWUwEAlARNuDqAAyXKQwEAlARViDqAA6XLJcOlxNg7gAJzCjMCc +wU4OoADJcmoOoADJcMlwjsE+DCAAQCUCF5LAQCUBEzIOoADJcpbAQCUBFSYOoADpcslw6XH2DeAA +nMKMwJzBEg6gAMlyyXCOwQYMIABAJQIZlghgAEAlABfPcYAA/AoQoYYIYABAJQAZz3GAAPwKEaEH +8ADZz3CAAPwKMKAxoIEHr/+AJAI64HjPcYAAnIDgfwhh4HjxwCoPr//YcCh2SHGIdTIO7//JcAh3 +qXAmDu//qHEIcQAugAMEfyZ/ACtAAyR4ZQev/+V48cDiC8AFz3GAAKgs8CEAAEB4z3CgANAbgNpQ +oM9wgAAwMQCAANkPCB4Cz3CfALj/PaDRwOB+4HhggECBAYAhgVBzzCBBgOEgwQfKICEAMHCG9gT2 +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 +4H7geAHZz3CgAMgcMKBL2c9wpAAcQCSg4H7geM9xAQDkQM9wgABkPuB/IKDPcYAAKKYAgYC44H8A +oeB48cC4cFMggQDPcIAANIAoYIHgyiHCD8oiwgfKIIIPAACVGcokgg8AAP4AJATi9soj4ggB2NHA +4H4J2eB/IKDgePHAhg2v9QDYz3WAAHA+IIVAeSCFKQiQAOUI0ADrdmB5Adi4cM9wAAC6GQohwA/J +coojzwfZA+/2iiSDD2B5AdgghREIUABgeQHYIIV/CNEAYHkC2AroIIVgeQLYIIUlCFEAYHkD2I7o +fgkAACCFYHkI2BB5z3CAAFwTYgyP9hXwIIVgeQLYIIUPCFAAYHkC2CCFGwiRAGB5CNgQec9wgABU +EDoMj/YH2Ibw63ZgeQLYuHDPcAAAuxkKIcAPyXKKI04FTQPv9ookgw9geQHY7QiRACCFYHkC2IDg +IIUI2Aj0QHkQec9wgAAgDgfwQHkQec9wgAA4D+YLj/Zd8GB5AtiR6CCF63dgeQHYuHBn2Aa4CiHA +D+lyiiOODPUC7/aKJIMPz3CAAGg+IIBgeQDYIIVNCBEDYHkI2CCFDwiQAGB5CNgghZHoYHkC2CCF +GwhRAGB5CNgQec91gACsF34Lr/apcKlwJfBgeQjYEHnPdYAAaBZqC6/2qXCpcBvwYHkC2IHgIIUI +2Az0QHnPdYAA2BkQeUoLr/apcKlwC/BAec91gACUGBB5Nguv9qlwqXC2C4/2AdiWC0AD0grP/cIJ +QADOCAAAKQSP9QhxWIkBgAKhiOpZiYDiwiCiAMAgoQACoeB+4HjxwOHFz3CAAGg+IIChwWB5BNiP +CFEA+gtv/IogzA6DCFEAz3WAAEgxAIUB4AClFwhRAAHZz3CgAMgcMaBKDWAGKHBKJMBwqCCAAs9x +AQBCac9woADsJyagi3EOCq/2iiBGCQCFQiBAgAClB/QA2c9woADIHDGgABQFMUwlQIDKIcIPyiLC +B8oggg8AAKwoyiOCDwAARwOUAeL2yiQiAH0Dr/WhwOB+4HjxwPYKj/XPdoAASDEAhgHgAKYVCFEA +AdjPcaAAyBwRob4MQAbPdYAAcD4ghWB5ANjXCJEAx9iUuM91oADsJwalz3cAAIIrz3ADAIIrBqXP +cAMAwkQGpc9wAwACLAalz3ADAEJFBqXPcQAAwnTPcAMAwnQGpc9wAwCCbwalz3ADAIJsBqXG2JC4 +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 +1L5geUigPB0AFJIL7/MC2IECj/HxwCIKj/GGD2AACHXPcaAAyB9FhQzobhEOBgKAZIXEekV7bhnY +ACKFAKEL8G4RAAZEeG4ZGAAc2Bi4FRkYgFECj/HgeIDgAdnAec9wgAAQTeB/IKDxwMoJj/HPcIAA +aD4ggKLBYHkE2IDgqgIBAM9xgABIMQCBAeAAoRcIUQAB2c9woADIHDGgegtgAihw2gjv+QXYz3aA +ANhIDqbPcYAASDEAgQHgAKEVCFEAAdnPcKAAyBwxoE4LYAIocAPYJgiv8slxBNgeCK/yIm4F2BYI +r/IkbgvYDgiv8iZuD9gGCK/yQCYBEjbY+g9v8kAmgRI32PIPb/JAJgETONjmD2/yQCaBE893pwAU +SAiHz3GnAJhHBKYNh89yqwCg/wWmDofPdaAA7CcGphyBB6YXhwimFocJphiCC6YZggymGoINps9w +BQDGAwalxtiQuAalz3AsAAIBBqXPcFoAQgEGpYogiwAGpc9wQACHDQalz3DRAMINBqXPcMAABw4G +pc9wgABIMQCAz3KAAEgxQiBAgACiBvTPcqAAyBwA2BGiAdgIpwDYDacOp89wUAD/AByhAdgXpwDY +Fqf82c9wqwCg/zigc9k5oBqAz3GrAKD/gbgaoc9wKgACDgali3A2DCAAgcEAwc9wgADUpzWmMqAB +wS+gz3AaAAIOBqWLcBYMIACBwQDBz3CAANSnNqYzoAHBMKDPcCYAAg4GpYtw9gsgAIHBAMHPcIAA +1Kc0oDemAcExoM9wgABIMQCAAeDPcYAASDEAoRUIUQDPcaAAyBwB2BGhsglAAgGWELiFIIQABqUC +lhC4hSCFAAalA5YQuIUgiwAGpQSWELiFII8ABqUFlhC4BSCADwAAgg0GpQaWELgFIIAPAADCDQal +B5YQuAUggA8AAAIOBqXPcIAASDEAgM9xgABIMUIgQIAAoQf0z3GgAMgcANgRoQSGK4YIpwWGDacG +hg6nCIYXpwmGFqfPcKsAoP84oCyGOaAthjqgpg2v+Q6Gz3CAAEgxz3GAAEgxAIBCIECAAKEH9M9x +oADIHADYEaF1B2/xosDxwAoPT/F+CAAAz3aAAHhsQg2v9wCGCHUAhhkNABDKD2/7qXBeCO/7oKZK +CS/0Edh2D8/2z3CgACwgMIDPcIAAFAo1B2/xIKDxwLoP7/+hwc9wgAAYTQCABNli2h7bQMCLcLoK +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 ++giAgQAAQBYAABsl9giAgQAbGigAAAAWAQAbJgAAABcNAABhEAmAgQAAQBYCABsmARAbaAAAGyQA +AEAX/AiAgQAaGygPGw4iFAmAgQAAQBYBABsmAABAF3wHgIEPGhsiAAAbJQIAG0AAABtxAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGwEgAC4AYAA +AAAAAAAAAAAoAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHAAAABwAAAAAAAAAHAAAACQ0RFAoNExcZGRkZ +CQkAAOy5gADYggAA1L6AAAAAAADUvoAAAAAAAN8AAAAZAQAAYgEAAL4BAAAyAgAAwwIAAHsDAABi +BAAAhAUAAPIGAAC+CAAAAgsAAAEAAAAAAAAAMIAAAAAAAAAxgAAAmblYhTKAAAAAypq4M4AAAABQ +AFA0gAAAAFAAADWAAAAAUABQNoAAAABQAFA3gAAAAAAAUDiAAAAAAAAAOYAAAABQAFA6gAAAAFAA +UDuAAAAAUABQPIAAAABQAAA9gAAAmckKUD6AAABVuIjJP4AAAAAAAIIwgAAAAAAAADGAAACZuViF +MoAAAADKmrgzgAAAAFAAUDSAAAAAUAAANYAAAABQAFA2gAAAAFAAUDeAAAAAAABQOIAAAAAAAAA5 +gAAAAFAAUDqAAAAAUABQO4AAAABQAFA8gAAAAFAAAD2AAACZyQpQPoAAAFW4iMk/gAAAAAAAgjCA +AAAAAAAAMYAAAAAAAAAygAAAAAAAADOAAAAAAAAANIAAAJpFAAA1gAAAqsqqyjaAAAAAIAAgN4AA +AAAgACA4gAAAACAAADmAAAAAIAAgOoAAAKrKqio7gAAAABCWyjyAAAAAAAAAPYAAAAAAAAA+gAAA +AAAAAD+AAAAAAAAAMIAAAAAAAAAxgAAAAAAAADKAAAAAAAAAM4AAAAAAAAA0gAAAmkUAADWAAACq +yqrKNoAAAAAgACA3gAAAACAAIDiAAAAAIAAAOYAAAAAgACA6gAAAqsqqKjuAAAAAEJbKPIAAAAAA +AAA9gAAAAAAAAD6AAAAAAAAAP4AAAAAAAAD//wAApQEBALkB3wA7Ai0AsQAbABYBGwCvABsAFAEb +AGwAoADRAKAAbwCDAHEAgwBzADMA1AEGANABAAB4AEkAeQBqAN4AagCoAAAADQEAAKYAPwCnAAEA +CwE/AAwBAQAEAAgAnAHMAJ0BzADVAcwA1gHMALQAIAAZASAAjwCIAPQAiACQACIA9QAiAJEABAD2 +AAQAhQAAAIYAAACHAFUAiAAAAIkAqgCKAAAAiwDdAIwAAACFAAEAhgABAIcAVQCIAAAAiQCqAIoA +AACLAN0AjAAAAIUAAgCGAAMAhwBVAIgAAACJAKoAigAAAIsA3QCMAAAAhQADAIYABwCHAFUAiAAA +AIkAqgCKAAAAiwDdAIwAAAD7/wAA//8AALkB3wA7Ai0AsQAbABYBGwCvABsAFAEbAGwAoADRAKAA +bwCDAHEAgwBzADMA1AEGANABAAB4AEkAeQBqAN4AagCoAAAADQEAAKYAPwCnAAEACwE/AAwBAQAE +AAgAnAHMAJ0BzADVAcwA1gHMALQAIAAZASAAjwCIAPQAiACQACIA9QAiAJEABAD2AAQAqAAMAA0B +DACFAAAAhgAAAIcAmQCIAAAAiQCqAIoAAACLAN0AjAAAAIUAAQCGAAEAhwCZAIgAAACJAKoAigAA +AIsA3QCMAAAAhQACAIYAAwCHAJkAiAAAAIkAqgCKAAAAiwDdAIwAAACFAAMAhgAHAIcAmQCIAAAA +iQCqAIoAAACLAN0AjAAAAPv/AAD//wAAuQHfALEAGwAWARsArwAbABQBGwBsAKAA0QCgAG8AgwBx +AIMAdgCDAHMAMwBuADMAcAAzAHIAMwDXADMA1AEGANABAAB+ADwA4wA8AHgASQDdAEkAfwBaAOQA +WgCqAD8AqwABAA8BPwAQAQEAeQBqAN4AagCoAAAADQEAAKYANwCnAAEACwE3AAwBAQAEAAgAnAHM +AJ0BzADVAcwA1gHMALQAIAAZASAAMQIMADICDAAzAr0ANgIMADcCDAA4Ar0AoACIAAUBiAChANUA +BgHVAKIABAAHAQQAjwCIAPQAiACQACIA9QAiAJEABAD2AAQAnwAMAPsADACUAAAAlQAAAJwAlwCd +ANAAmgCNAJgAEQCWADMAlwB3AJQAAQCVAAEAnACXAJ0A0ACaAI0AmAARAJYAMwCXAHcAlAACAJUA +AwCcAJcAnQDQAJoAjQCYABEAlgAzAJcAdwCUAAMAlQAHAJwAlwCdANAAmgCNAJgAEQCWADMAlwB3 +APoAAAD5AAAAAgGXAAMB0AAAAY0A/gARAPwAMwD9AHcA+gABAPkAAQACAZcAAwHQAAABjQD+ABEA +/AAzAP0AdwD6AAIA+QADAAIBlwADAdAAAAGNAP4AEQD8ADMA/QB3APoAAwD5AAcAAgGXAAMB0AAA +AY0A/gARAPwAMwD9AHcAhQAAAIYAAACHAFUAiAAAAIkApwCKAAAAiwDeAIwAAACFAAEAhgABAIcA +VQCIAAAAiQCnAIoAAACLAN4AjAAAAIUAAgCGAAMAhwBVAIgAAACJAKcAigAAAIsA3gCMAAAAhQAD +AIYABwCHAFUAiAAAAIkApwCKAAAAiwDeAIwAAADrAAAA6gAAAOwAVQDtAAAA7gCnAO8AAADwAN4A +8QAAAOsAAQDqAAEA7ABVAO0AAADuAKcA7wAAAPAA3gDxAAAA6wACAOoAAwDsAFUA7QAAAO4ApwDv +AAAA8ADeAPEAAADrAAMA6gAHAOwAVQDtAAAA7gCnAO8AAADwAN4A8QAAAKQBgAChAUAA+/8AAP// +AAClAQEAuQHfALEAGwAWARsArwAbABQBGwBsAKAA0QCgAG8AgwBxAIMAdgCDAHMAMwBuADMAcAAz +AHIAMwDXADMA1AEGANABAAB+ADwA4wA8AHgASQDdAEkAfwBaAOQAWgCqAD8AqwABAA8BPwAQAQEA +eQBqAN4AagCoAAAADQEAAKYANwCnAAEACwE3AAwBAQAEAAgAnAHMAJ0BzADVAcwA1gHMALQAIAAZ +ASAAMQIMADICDAAzAr0ANgIMADcCDAA4Ar0AoACIAAUBiAChANUABgHVAKIABAAHAQQAjwCIAPQA +iACQACIA9QAiAJEABAD2AAQAnwAMAPsADACUAAAAlQAAAJwAlwCdANAAmgCNAJgAEQCWADMAlwB3 +AJQAAQCVAAEAnACXAJ0A0ACaAI0AmAARAJYAMwCXAHcAlAACAJUAAwCcAJcAnQDQAJoAjQCYABEA +lgAzAJcAdwCUAAMAlQAHAJwAlwCdANAAmgCNAJgAEQCWADMAlwB3APoAAAD5AAAAAgGXAAMB0AAA +AY0A/gARAPwAMwD9AHcA+gABAPkAAQACAZcAAwHQAAABjQD+ABEA/AAzAP0AdwD6AAIA+QADAAIB +lwADAdAAAAGNAP4AEQD8ADMA/QB3APoAAwD5AAcAAgGXAAMB0AAAAY0A/gARAPwAMwD9AHcAhQAA +AIYAAACHAFUAiAAAAIkApwCKAAAAiwDeAIwAAACFAAEAhgABAIcAVQCIAAAAiQCnAIoAAACLAN4A +jAAAAIUAAgCGAAMAhwBVAIgAAACJAKcAigAAAIsA3gCMAAAAhQADAIYABwCHAFUAiAAAAIkApwCK +AAAAiwDeAIwAAADrAAAA6gAAAOwAVQDtAAAA7gCnAO8AAADwAN4A8QAAAOsAAQDqAAEA7ABVAO0A +AADuAKcA7wAAAPAA3gDxAAAA6wACAOoAAwDsAFUA7QAAAO4ApwDvAAAA8ADeAPEAAADrAAMA6gAH +AOwAVQDtAAAA7gCnAO8AAADwAN4A8QAAAKQBgAChAUAA+/8AAP//AAC5AcEA1AEDANABBAB4ADwA +3QA8AHkAagDeAGoAqAABAA0BAQAEAAgAnAHMAJ0BzADVAcwA1gHMALQAIAAZASAAjwCIAPQAiACQ +AAAA9QAAAJEABgD2AAYAhQAEAOsABACkAYAAXQIzAEoCDgBMAg4ATQIBAK0BBwCzAQQAuAEAALsB +VgBQAgsAUQIDAFICAQBTAgAAVAILAFUCAwBWAgEAVwIAAGYCBgBoAgcAagIHAGwCBwBuAgUAcAIM +AH0CBgB/AgcAgQIHAIMCBwCFAgUAhwIMALUAIQAaASEASwIBAKEBQACzAAAAGAEAAJQCCwCVAgMA +lgIBAJcCAACYAgsAmQIDAJoCAQCbAgAAsgAwABcBMACcAg8AoQIPAKACiACfAogAngKIAJ0CiACl +AogApAKIAKMCiACiAogA+/8AAP//AAC5AcEA1AEDANABAAB4ADwA3QA8AHkAagDeAGoAqAABAA0B +AQAEAAgAnAHMAJ0BzADVAcwA1gHMALQAIAAZASAAjwCIAPQAiACQAAAA9QAAAJEABgD2AAYAhQAE +AOsABACkAYAAXQI2AEoCDQBMAg8ATQIBAK0BBgCzAQQAuAEAALsBVgBQAgsAUQIDAFICAQBTAgAA +VAILAFUCAwBWAgEAVwIAAGYCBgBoAgcAagIHAGwCBwBuAgUAcAIMAH0CBgB/AgcAgQIHAIMCBwCF +AgUAhwIMALUAIQAaASEAoQFAAPv/AAD//wAAuQHBANQBAwDQAQQAeAA8AN0APAB5AGoA3gBqAKgA +AQANAQEABAAIAJwBzACdAcwA1QHMANYBzAC0ACAAGQEgAI8AiAD0AIgAkAAAAPUAAACRAAYA9gAG +AIUABADrAAQApAGAAF0CMwBKAg4ATAIOAE0CAQCtAQcAswEEALgBAAC7AVYAUAILAFECAwBSAgEA +UwIAAFQCCwBVAgMAVgIBAFcCAACUAgsAlQIDAJYCAQCXAgAAmAILAJkCAwCaAgEAmwIAAGYCBgBo +AgcAagIGAGwCBwBuAgUAcAIMAH0CBgB/AgcAgQIGAIMCBwCFAgUAhwIMALIAMAAXATAAswAAABgB +AACcAg8AoQIPAKACiACfAogAngKIAJ0CiAClAogApAKIAKMCiACiAogAtQAhABoBIQChAUAASwIB +APv/AAD//wAAuQHBANQBAwDQAQAAeAA8AN0APAB5AGoA3gBqAKgAAQANAQEABAAIAJwBzACdAcwA +1QHMANYBzAC0ACAAGQEgAI8AiAD0AIgAkAAAAPUAAACRAAYA9gAGAIUABADrAAQApAGAAF0CNgBK +Ag0ATAIPAE0CAQCtAQYAswEEALgBAAC7AVYAUAILAFECAwBSAgEAUwIAAFQCCwBVAgMAVgIBAFcC +AABmAgYAaAIHAGoCBgBsAgcAbgIFAHACDAB9AgYAfwIHAIECBgCDAgcAhQIFAIcCDAC1ACEAGgEh +AKEBQAD7/wAAAAAAAAIAAAAN0hLSE9IU0gzSFdIL0gLSEdIEQwAQFBAJEBEQAUAb0hzSANIKAAsA +BAAOALUAGgEPAEIAvADDACEBKAG2ALcAuAC5AL0AvgC/AMAAGwEcAR0BHgEiASMBJAElAQoAAAAL +AAAAtgAAALcAAAC4AAAAuQAAABsBAAAcAQAAHQEAAB4BAAC9AAAAvgAAAL8AAADAAAAAIgEAACMB +AAAkAQAAJQEAABLSAAAT0gAAAAABAAIAAwAsAGQAdACAAIwAoQAHAAAAAAABAAIAAwAAAAAAAAAA +AAAAAAAAAAAAAQAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHAAAABwAAAAAAAAADAAAA +BAAAAAMAAAAAAAAA/wMAAAMAAAAAAAAAAAAAAAAAAAABAAAAAQAAAAAAAAAAAAAALAABAAAAAQAB +AAEAAAAAAAAAAAABAAEAAgACAAIAAwADAAQABAAFAAUABgAGAAcABwAIAAgACQAJAAoACgALAAsA +DAAMAA0ADQAOAA4ADwAAAAEAAgAAAAvSDtIN0gjSCdIK0hLSE9IU0hHSENIC0gHSA9IAgAXSBEMb +0hzSBNIARTDSMdIAALUAGgGBAQUABAAPABAACgALAAwATgAAAAAAAQACAAAADdIR0hDSAtIB0gPS +G9IL0gCABdIS0hPSFNIEQwjSCdIK0hzSBtIH0nDSAAC1ABoBgQEEAA8AgwDoAE4BkgD3AF0BBgAI +AAkACgALAAwABQAAAAAAAAAAAAoAAAABAAAAAAAAAAAAAAAAAAAAAwAAAAQAAAADAAAAAAAAAAMA +AAAAAAAAAAAAAAAAAAAAAAAA/wMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAs +AAEAAAAAAAAAAAAAAAAAAAAAAAAAAQABAAEAAAAE0g3SEdIQ0gLSAdID0hvSAIAF0gvSEtIT0hTS +BENw0gAAAAABAAAAAQAAAAEAAAABAAAAAwAAAAIAAAADAAAAAwAAAAAAAAABAAAAAAAAAAAAAAAA +AAAA/wMAAAAAAAC1ABoBgQEEAA8ABgAIAAkACgALAAwAAAAAAAAAAAAsAAEAFQAVABUAAQABAAEA +AAAAAAEAAgAAABzSDdIR0hDSAtIB0gPSG9IL0gCABdIS0hPSFNIEQwbSB9IE0nDSAAC1ABoBgQEF +AAQABgAIAAkACgALAAwAgwCSAOgA9wBOAV0BDwAAAAAAtxMiALgUIwC5FSQAuxYlALwXJgC9GCcA +wBkoAMQaKQAHGwAACBwBAAsdAgAMHgMAEB8EACIhBQAkIgYAJiMHACgkCAAqJQkALCYKAC4nCwAw +KAwANCkNADgqDgA8Kw8AQCwQAGQuEQBoLxIAbDATAHAxFAB0MhUAeDMWAHw0FwCANRgAhDYZAIg3 +GgCMOBsAkTocAJU7HQCZPB4AnT0fAKE+IAClPyEAJEkGAixKCgI0Sw0BPEwPAWRNEQFsThMBdE8V +AXxQFwGEURkBlVIdAZ1THwEBBAAAAgUBAAMGAgAEBwMABQgEAAYJBQAHCgYACAsHAAkMCAAKDQkA +Cw4KAAwPCwANEAwADhENAAFAAAQCQQEEA0ICBARDAwQFRAQEBkUFBAdGBgQIRwcECUgIBAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAhgCGAIYAhgCGAIYAhgCGAIYA +hgCGAIYAhgCGAIcAiAAEAAUABgAGAAcACAAIAAkACQAKAAsADAAMACQAJQAlACYAJwAoACgARABF +AEYARgBHAEgASABJAEoASgBLAEwAaABoAGkAagBrAGwAbQBtAG4AbwBvAHAAcQBxAHIAcgByAHIA +cgByAHIAcgByAHIAcgByAHIAcgByAHIAcgByAHIAcgAKAD8AhgCGAIYAhgCGAIYAhgCGAIYAhgCG +AIYAhgCGAIcAiAAEAAUABgAGAAcACAAIAAkACQAKAAsADAAMACQAJQAlACYAJwAoACgARABFAEYA +RgBHAEgASABJAEoASgBLAEwAaABoAGkAagBrAGwAbQBtAG4AbwBvAHAAcQBxAHIAcgByAHIAcgBy +AHIAcgByAHIAcgByAHIAcgByAHIAcgByAHIAcgAKAD8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAHAAARBwAAEwc +AADIGwAAzBsAADQMoABADKAAHBygAEAgoAAoJKAAbBCgABgkoAB4JKAAfCSgAIAkoACEJKAAUBCg +AFQQoABIJqAAYBCgAEwmoABkEKAAaBCgAFwQoABYEKAAMBCgADwQoAA0EKAALAygAACBpAABgaQA +A4GkAIgkoACMJKAAkCSgAJQkoACYJKAAnCSgAKAkoACkJKAAAAAAAAAAAAAAAAAAAAAAAAEAAgAC +AAMABAAEAAUABgAGAAcACAAIAAkACgAKAAsADAAMAA0ADgAOAA8AJgAnACgAKQAqAEYARgBHAEgA +SABJAEoASgBLAEwAaABpAGoAagBrAGwAbABtAG4AbgBvAHAAcABxAHIAcgBzAHMAdAB0AHQAdAB0 +AHQAdAB0AHQAdAB0AHQAdAB0AHQAdAB0AHQAdAAKAD8AAAAAAAAAAAAAAAAAAAAAAAEAAQACAAMA +AwAEAAUABQAGAAcABwAIAAkACQAKAAsACwAMAAwADQAOACMAJAAlACYAJwAoACkARABFAEYARgBH +AEcASABIAEkASQBKAEsAaABoAGkAagBrAGwAbQBtAG4AbwBvAHAAcQBxAHIAcgBzAHMAcwBzAHMA +cwBzAHMAcwBzAHMAcwBzAHMAcwBzAHMAcwAKAD8AAAAAAAAAAAAAAAAAAAAAAAEAAgACAAMABAAE +AAUABgAGAAcACAAIAAkACgAKAAsADAAMAA0ADgAOAA8AJgAnACgAKQAqAEYARgBHAEgASABJAEoA +SgBLAEwAaABpAGoAagBrAGwAbABtAG4AbgBvAHAAcABxAHIAcgBzAHMAdAB0AHQAdAB0AHQAdAB0 +AHQAdAB0AHQAdAB0AHQAdAB0AHQAdAAKAD8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQABAAIA +AwADAAQABQAFAAYABwAHAAgACQAJAAoACgALACQAJQAlACYAJwAoACgARABFAEYARgBHAEgASABJ +AEoASgBLAEwAaABoAGkAagBrAGwAbQBtAG4AbwBvAHAAcQBxAHIAcgByAHIAcgByAHIAcgByAHIA +cgByAHIAcgByAHIAcgByAHIAcgAKAD8AgIGAAAAAAACyDCwB/////////wAB//8CA////wT///// +/////////////////wX/Bv8H/wj/Cf8K/wv/DP///w3///8O////D////xD///////////////// +/////////////////////////////xH///8S////E////xT///8V////Fv///xf///8Y////Gf// +/xr///8b/////xz///8d////Hv///x////8g////If//////////////////////IiMk/yUmJ/// +KP///yn///////////////////////////////////////////////////////////////////// +/////////wABAAEBAAAAAAAAAAABAAAAAAAAAAAAAAAAAAADAAAAAAAAAAEAAAAAAAAAkDQBAAAA +AAD0egAAAQAAALjOAQACAAAAEM4BAAMAAAA0HwIABAAAAJA0AQAFAAAAwCABAAYAAACc8gAABwAA +AEghAQAIAAAA8DEAAAkAAACkVAAACgAAAFzPAAALAAAAyC4AAAwAAAA4HwIADQAAAMzpAAAOAAAA +eOoAAA8AAABY6QAAEAAAAFjqAAARAAAAEEIBABIAAABE5wEAEwAAAJwmAAAUAAAAUG0BABUAAAAI +WAEAFgAAANBnAQAXAAAAPM0BABgAAACklgEAGQAAANQOAQAaAAAAjDQBABsAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAD//////////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAdJ0AAHSdAAB0nQAAdIkAAHSdAAB0nQAA +2IkAAHSdAAB0nQAAdJ0AAHSdAAB0nQAAdJ0AAHSdAAB0nQAAdJ0AAECSAACIkQAAeJEAAKiQAACw +kQAAZJAAAHSdAAB0nQAAVJQAACSXAAA8mQAAdJ0AAHSdAAB0nQAA3JwAAFiTAACQkwAA/JIAAHSd +AAB0nQAAdJ0AAJicAAB0nQAA3JIAAHSdAAB0nQAAdJ0AAHSdAAB0nQAAdJ0AAHSdAAB0nQAAdJ0A +AHSdAAB0nQAAdJ0AAHSdAAB0nQAAdJ0AAHSdAAB0nQAAdJ0AAHSdAAB0nQAAdJ0AAHSdAAB0nQAA +dJ0AAHSdAAB0nQAAdJ0AAHSdAAB0nQAAdJ0AAHSdAAB0nQAAnIoAAHSdAAB0nQAAdJ0AAHSdAAB0 +nQAA/JkAAHSdAAB0nQAAdJ0AAHSdAAB0nQAA6I0AAHSdAAD4jQAA9I0AAOyNAADwjQAAJIcAAHSd +AADchgAAdJ0AAHSdAAB0nQAAdJ0AAHSdAAB0nQAAdJ0AAHSdAABghgAAdJ0AAHSdAAB0nQAAdJ0A +AHSdAAB0nQAAdJ0AAHSdAAB0nQAAdJ0AAHSdAAAAjAAAeIsAAHSdAAAcjAAAdJ0AALyKAAAokAAA +dJ0AAHSdAABgkgAAdJ0AAHSdAAB0nQAAdJ0AAHSdAACskgAAkJIAAHSdAAB0nQAAdJ0AAHSdAAB0 +nQAAdJ0AAHSdAAB0nQAAdJ0AAHSdAAB0nQAA/I0AAHSdAAB0nQAAdJ0AAPiZAAB0nQAAdJ0AAHSd +AAAwnAAAdJ0AAJCcAACYmQAAdJ0AAHSdAABIhQAAWJkAAHSdAAB0nQAAsJAAAMiQAAB0nQAAdJ0A +AKSNAAB0hwAAdJ0AAHSdAAB0nQAAmJMAAEyQAAB0nQAAdJ0AAHSdAAB0nQAAdJ0AAHSdAACMjwAA +dJ0AAJydAAAongAACJ4AAECeAADUnQAAvJ0AAEieAACYnQAAdJ0AAHSdAAB0nQAAdJ0AAHSdAAB0 +nQAAdJ0AAHSdAACsjQAAdJ0AAHSdAAB0nQAAdJ0AAHSdAAB0nQAAdJ0AANieAADknwAAYIUAANSF +AAB0nQAAdJ0AAHSdAAB0nQAAdJ0AAKSHAAB0nQAAdJ0AAHSdAAB0nQAAdJ0AAHSdAAB0nQAAdJ0A +AHSdAAB0nQAAdJ0AAHSdAAB0nQAAdJ0AAHSdAAB0nQAAdJ0AAHSdAAB0nQAAdJ0AAHSdAAB0nQAA +dJ0AAHSdAAB0nQAAdJ0AAHSdAAB0nQAAqIcAAMSIAAA0iAAATJ4AAOyFAABMiQAA5IkAAHSdAAB0 +nQAAdJ0AAHSdAADQiQAA1IkAAHSdAAB0nQAAeIkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPirAAAkqgAAZK0A +AGisAAB4rgAAAAABAP////8AAAAA//////////8BAAAAeBEAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAABAAAAAADQ/gAAAAAAAAAAAAAAABggoAAgIKAAQCGgAEghoAAcIKAAJCCgAEQhoABM +IaAAKCCgADAgoABoIaAAcCGgACwgoAA0IKAAbCGgAHQhoAA4IKAAPCCgAHghoAB8IaAAMBAAAAD/ +AwDQEAAAAP8FAHQQAAAA/y0AUBAAAAD/PQDsDwAAAP8EABQQAAAA/yUAINAAAAD/3QCYEAAAEBBM +AAAAAAAAAAAAAAEBADw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8FRUVFTw8PDwVFRUV +PDw8PAAAAAAAAAAAAAAAAAAAAAA8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PBUVFRU8 +PDw8FRUVFTw8PDwAAAAAAAAAAAAAAAAAAAAAPDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8 +PDwVFRUVPDw8PBUVFRU8PDw8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/wAAAAEAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA0BIAAAQaAABIFgAAsBMAAMQbAABQFwAA9BgAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAANQJgAB8v4AAGAAAADy/gAAAAAAAAAAAAAAAAAAAAAAAHAwBALS6AAAIJAAAtLoAALS6AAC0 +ugAAOAoAAETfAQAs2gAAtLoAALS6AAAYKgAAGCoAABgqAAAYKgAAGCoAABgqAAAYKgAAtLoAALS6 +AAC0ugAAtLoAADBLAAC0ugAAtLoAALS6AAC0ugAAtLoAABDaAAC0ugAAtLoAAGDPAAAAAAAAEPYA +ABT2AAC0AgAAoAIAAORAAQCIxwAA1LkBALDHAAAEugEA1McAADS6AQBIR4AAaLSAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAADMIQAAlCEAAIDCgAAAAgAAAAAAAOwDAQC8AwEAgMSAAEAFAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAADwAwEApD0BABDLgABUAAAAAAAAAOwDAQB4PAEAwMmAAFAB +AAAAAAAA7AMBAKA2AQAoCoAACAAAAAEAAADsAwEAuAIBAAAAAABQAQAAAAAAAOwDAQAsNwEA4D+A +AAIAAAAAAAAA7AMBADg2AQAkCoAABAAAAAAAAAD0AwEAvAMBAGTLgAAqAAAAAAAAAOwDAQC8AwEA +HE2AAAgAAAAAAAAAAAAAAMQDAQAAAAAAAAAAAAEAAAAAAAAA2AMBAAAAAAAAAAAAAAAAAAAAAADA +AwEAAAAAAAAAAAAAAAAA7AMBAPi7AQAAAAAAAAAAAAAAAADsAwEAuLsBADAKgAAEAAAAAAAAAG4A +bgBpAMAAoABQAIAAvgBQAX0APgABAAEAAQBYAigA5gEtAFUDPADcAWMAAABuAG4AaQDAAKAAUACA +AL4AUAF9AD4AAQABAAEAWAIoAOYBLQBVAzwA3AFjAAAAAAAAAAEBAAAUCwEAFdIAAAAAAAD/AwAA +FAsBAAzSAAAAAAAA/wEAABQLAQAV0gAACgAAAAD8DwAUCwEADNIAAAkAAAAA/gMAFAsBABXSAAAU +AAAAAADwPxQLAQAM0gAAEgAAAAAA/AcUCwEABtIAAAAAAAD/AQAAFAsBAAfSAAAAAAAA/wMAABQL +AQAG0gAACQAAAAD+AwAUCwEAB9IAAAoAAAAA/A8AFAsBAAbSAAASAAAAAAD8BxQLAQAH0gAAFAAA +AAAA8D8AAAAAAAAAAAAAAAABAAAAAQAAAAoAAAAFAAAABQAAAAYAAAAKAAAACgAAAAYAAAAEAAAA +BQAAAAYAAAAFAAAABQAAAAYAAAAGAAAABgAAAAYAAAAHAAAABwAAAAcAAAAIAAAACAAAAAgAAAAE +AAAAAAAAAAAAAAAAAAAAAAAAAAEAAAACAAAAAQAAAAIAAAABAAAAAQAAAAMAAAADAAAAAgAAAAEA +AAAEAAAAAAAAAAAAAAACAAAAAQAAAAEAAAAAAAAAAAAAAAcAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAQAAAAAAAAAsAQAAXgEAAAEAAAABAAAAAQAAAAEAAAADAAAAAAAAAAAAAACI +GQEAFB8BAMQdAQCYHwEAEB8BAPAcAQCUHwEA6BoBAOQaAQBg4xYAINYTAAAAAAAQAAAAAIAAAAAA +oAAQJwAA6AMAAOgDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAACAAAAAQAAAAEAAAACAAAA +BQAAAAIAAAACAAAABQAAAAIAAAACAAAAAQAAAAEAAAAFAAAABQAAAAIAAAAFAAAABQAAAAAAAAAF +AAAAAgAAAAIAAAAAAAAAAAAAAAAAAAAFAAAABQAAAAAAAAAFAAAAAgAAAAIAAAAFAAAABQAAAAUA +AAAAAAAABQAAAAIAAAAFAAAAAQAAAAEAAAACAAAAAgAAAAIAAAAFAAAABQAAAAIAAAAFAAAAAQAA +AAEAAAACAAAAAgAAAAIAAAAFAAAABQAAAAIAAAACAAAABQAAAAEAAAACAAAABQAAAAIAAAAFAAAA +BQAAAAQAAAAFAAAABQAAAAEAAAAFAAAABQAAAAUAAAACAAAAAgAAAAUAAAAFAAAABQAAAAEAAAAF +AAAABQAAAAUAAAACAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAAAAAAAAAAAAAAAA +AAAAAAAIAAAAARAAAQAAAAKAAAFCAAYCEAACIAAAA8AAAUMABgMQAALAAAADwAABQwAGBBAAAkAA +AAKAAAFEAAYFEQAAQAAAA8AAAUUABgYRAADgAAADwAABRQAGBxEAAQAAAAKAAAFGAAYIEQACIAAA +A8AAAUcABgkRAALAAAADwAABRwAGChEAAkAAAAKAAAFIAAYLEgAAQAAAA8AAAUkABgwSAADgAAAD +wAABSQAGDRIAAQAAAAKAAAFKAAYOEgACAAAAAoAAAUwABgF4ADAAAABQAAAEtjwGAngARAAAAFAA +AAS5PAYDeQAIAAAAUAAABLs8BgR5ABwAAABQAAAEvjwGBXkAMAAAAFAAAATAPAYGeQBEAAAAUAAA +BMM8Bgd6AAgAAABQAAAExTwGCHoAHAAAAFAAAATIPAYJegAwAAAAUAAABMo8Bgp6AEQAAABQAAAE +zTwGC3sACAAAAFAAAATPPAYMewAcAAAAUAAABNI8Bg17ADAAAABQAAAE1DwGDnwAEAAAAFAAAATa +PgYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAABAgEBAAIBAAECAgIAAQEAAgECAQIAAgABAgOAgICAgICAgAGAAoCAgICAwACQANAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAD/AAAAAAAAAIwKjAoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAgAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwNoBABUAAAADAAAA0GiAAAAAAAAAAAAAAAAAAGTaAQAF +AAAAAwAAANBogAAAAAAAAAAAAAAAAABY2gEACgAAAAMAAADQaIAAAAAAAAAAAAAAAAAAKNcBAAoA +AAAAAAAA8GiAAAAAAAAAAAAAAAAAAADZAQAKAAAAAAAAAPBogAAAAAAAAAAAAAAAAAAA2QEACgAA +AAAAAADwaIAAAAAAAAAAAAAAAAAAANkBAAoAAAAAAAAA8GiAAAAAAAAAAAAAAAAAAADZAQAKAAAA +AAAAAPBogAAAAAAAAAAAAAAAAAAA2QEACgAAAAAAAADwaIAAAAAAAAAAAAAAAAAAANkBAAoAAAAA +AAAA8GiAAAAAAAAAAAAAAAAAAADZAQAKAAAAAAAAAPBogAAAAAAAAAAAAAAAAAAA2QEACgAAAAAA +AADwaIAAAAAAAAAAAAAAAAAAANkBAAoAAAAAAAAA8GiAAAAAAAAAAAAAAAAAAADZAQAKAAAAAAAA +APBogAAAAAAAAAAAAAAAAAAA2QEACgAAAAAAAADwaIAAAAAAAAAAAAAAAAAAANkBAAoAAAAAAAAA +8GiAAAAAAAAAAAAAAAAAAGjcAQAGAAAAAAAAAPBogAAAAAAAAAAAAAAAAAC82wEABQAAAAMAAADQ +aIAAAAAAAAAAAAAAAAAAGNYBAAoAAAAAAAAA8GiAAAAAAAAAAAAAAAAAALjWAQAKAAAAAAAAAPBo +gAAAAAAAAAAAAAAAAADk5QEACgAAAAMAAADQaIAAAAAAAAAAAAAAAAAAdNcBAAoAAAAAAAAA8GiA +AAAAAAAAAAAAAAAAAFjYAQAKAAAAAAAAAPBogAAAAAAAAAAAAAAAAAAE2QEACgAAAAAAAADwaIAA +AAAAAAAAAAAAAAAAwNkBAAoAAAAAAAAA8GiAAAAAAAAAAAAAAAAAAMTaAQAKAAAAAAAAAPBogAAA +AAAAAAAAAAAAAABk2wEACgAAAAAAAADwaIAAAAAAAAAAAAAAAAAAAAAAANBogADQaIAAuCCgAGwg +oAAAgAEA/3/8/wAAAAAAAAAA8GiAAPBogACkIKAAOCCgAAEAAAD8////AAAAAAAAAAAQaYAAEGmA +AKggoAA8IKAAEAAAAMf///8AAAAAAAAAADBpgAAwaYAArCCgAHghoABAAQAAP/7//wAAAAAAAAAA +UGmAAFBpgACwIKAAfCGgAAAMAAD/8f//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAbNcBABUAAAAD +AAAA0GiAAAAAAAAAAAAAAAAAANQJgAB8v4AAGAAAADy/gAAAAAAAAAAAAAAAAAB/AAAAAAAAAAB/ +AAAAAAAAAAAAAAAAAAAAAAAAOAAAAGgAAAB0AAAAgAAAAIwAAACdAAAABwAAAAAAAAD/////AAAA +AC0BAADdAQAAWgIAALoCAAAKAwAATQMAAIcDAAC6AwAA6AMAABEEAAA3BAAAWQQAAHoEAACYBAAA +tAQAAM4EAADnBAAA/gQAABUFAAAqBQAAPgUAAFEFAABkBQAAdQUAAIYFAACXBQAApwUAALYFAADF +BQAA0wUAAOEFAADuBQAA+wUAAAgGAAAUBgAAIAYAACsGAAA3BgAAQgYAAEwGAABXBgAAYQYAAGsG +AAB1BgAAfgYAAIgGAACRBgAAmgYAAKIGAACrBgAAtAYAALwGAADEBgAAzAYAANQGAADbBgAA4wYA +AOoGAADyBgAA+QYAAAAHAAAHBwAADgcAABQHAAAbBwAAIgcAACgHAAAuBwAANQcAADsHAABBBwAA +RwcAAE0HAABTBwAAWAcAAF4HAABkBwAAaQcAAG8HAAB0BwAAeQcAAH8HAACEBwAAiQcAAI4HAACT +BwAAmAcAAJ0HAACiBwAApwcAAKsHAACwBwAAtQcAALkHAAC+BwAAwgcAAMcHAADLBwAA0AcAANQH +AADYBwAA3AcAAOEHAADlBwAA6QcAAO0HAADxBwAA9QcAAPkHAAD9BwAAAQgAAAUIAAAICAAADAgA +ABAIAAAUCAAAFwgAABsIAAAfCAAAIggAACYIAAApCAAALQgAADAIAAA0CAAANwgAADsIAAA+CAAA +QQgAAEUIAABICAAASwgAAE8IAABSCAAAVQgAAFgIAABbCAAAXwgAAGIIAABlCAAAaAgAAGsIAABu +CAAAcQgAAHQIAAB3CAAAeggAAH0IAACACAAAgggAAIUIAACICAAAiwgAAI4IAACRCAAAkwgAAJYI +AACZCAAAAAAAAAEAAAAAAAAABwAAAAAAAAAAAAAAAAAAAAABAgMEBAQEBAUGBwgICAgICQoLDA0A +AG47aDtiO1w7bjpoOmI6XDpuOWg5YjlcOW4raCtiK1wrbipoKmIqXCpuKWgpYilcKW4oaChiKFwo +bidoJ2InXCduJmgmYiZcJm4laCViJVwlbiRoJGIkXCRuI2gjYiNcI24iaCJiIlwibiFoIWIhXCFu +IGggYiBcIGITXBNuEmgSYhJcEm4RaBFiEVwRbhBoEGIQXBBuAmgCYgJcAm4BaAFiAVwBbgBoAFQA +AABuO2g7YjtcO246aDpiOlw6bjloOWI5XDluK2grYitcK24qaCpiKlwqbiloKWIpXCluKGgoYihc +KG4naCdiJ1wnbiZoJmImXCZuJWglYiVcJW4kaCRiJFwkbiNoI2IjXCNuImgiYiJcIm4haCFiIVwh +biBoIGIgXCBuEmgSYhJcEm4RaBFiEVwRbhBoEGIQXBBXEFIQTRBJEG4BaAFiAVwBbgBoAGIAXABU +AAAAAAAAAAAAAAAdAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgwAAAJIAAACDAAAAkgAAAOgA +AAD3AAAA6AAAAPcAAAAAAAAACQAAAAAAAAAKAAAAOAAAAGgAAAB0AAAAgAAAAIwAAACdAAAABwAA +AAAAAAAHAAAABwAAAAcAAAAAAAAAAAAAAAAAAAAAAAAA7GEBAAgAAAADAAAA0GiAACAMgACgDIAA +IA2AAKANgAAKDREUCg0RFBkZGRkKCgAAAAAAAAYGBgYJCQkJAAYAAAAFBgcIDQ4PEBUWFxgZAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAADxqsrCUvrKysrKylrH6RTkMMmzYAAAA3AJqSj0gAAA8AIGg3AAAAEQA+OiARAAACJQAADC8A +AAIvOTkACiU8t0dvigAHFCdiLgAAAgAXAAAFEAogMEAAAAYGCg0jGyMhAAAADBAUGCAIBAAAPDg0 +MCwoJCAcGBQQDAgEAAsHAwA7NzMvKycjHxsXEw8LBwMxMDo1MTo0NgAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAiAAAAEAAAAB0AAAA3AAAAbwAAAKAAAABwAAAABQAAAPYA +AABaAAAAaQAAAAAAAABpAAAAAgAAAB8AAABIAAAAIAAAAEgAAAAIAAAAAQAAADMAAAB/AAAANAAA +AH8AAAA3AAAAAQAAADgAAABPAAAAOwAAAH8AAAA8AAAAfwAAADEAAAAAAAAAMgAAAAAAAAA1AAAA +AAAAADYAAAAAAAAAOQAAAAAAAAA6AAAAAAAAAA0AAAAqAAAADgAAAHoAAAAPAAAAEAAAAPMAAABI +AAAA9AAAAEgAAAAAAAAAAQEBAQEBAQECAgICAgICAgMDAwMDAwMDAQIAAAAAAAAAAAAABAAAAAUA +AAChAw4e4QAAAKEDDh7hAAAAAAAAAAoAAAARAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAChAw4e4QAAABX2Y/aw9vz2RveQ99j3H/hl+Kn47fgv+XD5sPnu+Sv6Z/qi+tz6FPtL ++4H7tvvq+xz8Tfx9/Kv82fwF/TD9Wf2C/an9z/30/Rf+Of5a/nr+mP62/tL+7f4G/x7/Nf9L/2D/ +c/+F/5b/pv+0/8H/zf/Y/+H/6f/w//b/+v/9//////////3/+v/2//D/6f/h/9j/zf/B/7T/pv+W +/4X/c/9g/0v/Nf8e/wb/7f7S/rb+mP56/lr+Of4X/vT9z/2p/YL9Wf0w/QX92fyr/H38Tfwc/Or7 +tvuB+0v7FPvc+qL6Z/or+u75sPlw+S/57fip+GX4H/jY95D3Rvf89rD2Y/ZwuYO6lruqvL690r7n +v/zAEcInwz3EU8VqxoDHl8ivycbK3sv2zA/OJ89A0FnRctKM06bUv9Xa1vTXDtkp2kTbX9x63Zbe +sd/N4OnhBeMh5D7lWuZ355PosOnN6urrB+0k7kLvX/B98ZryuPPV9PP1Efcv+Ez5avqI+6b8xP3i +/gAAHgE8AloDeASWBbQG0QfvCA0KKwtIDGYNgw6hD74Q3BH5EhYUMxVQFm0XiRimGcIa3xv7HBce +Mx9PIGohhiKhI7wk1yXyJgwoJilBKlordCyOLacuwC/ZMPExCjMiNDo1UTZpN4A4ljmtOsM72Tzv +PQQ/GUAuQUJCVkNqRH1F////AP///wH/AgP///8EBf8J/wcKBggLAAEBAgECAgMBAQEBAQEBAQIC +AgICAgICAwMDAwMDAwMEBAQEBAQEBAECAgICAgIDAwMDAwMDAwMDAwMDAwQEBAQEBAQEBAQEBAQE +BAQEBAQEBAQEBAAAADoBAgHVAN8A2gCiAHUAfwCKBSoDOQGoAYoFygLZAEgBAQMPBwoUN25qARoB +2QDoAAoBugB5AIgAygFKAeIA+QDKAeoAggCZAHTRRRfooosuAAUHAQMEAAUBBQAAAAUGAAIEAAUA +BQAAAQIBAgMEAAAFBgcICQoAAAkAAAAAAAAAAQAAAAIAAAADAAAAAAAAAAQAAAACAAAABQAAAAcH +BwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwYG +BgYGBQUFBQUEBAQEBAMDAwMDAgICAgIBAQEBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +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 +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAABgAAAAQAAAAsAQAABwAAAAAAAAASAAAABAAAAAsAAAAIAAAABAAAAGRQgAAJ +AAAABAAAABAYAAAKAAAABAAAANBPgAALAAAABAAAALxNgAAMAAAABAAAABAYAAANAAAABAAAAChN +gAAOAAAAAAAAAA8AAAAEAAAAFgAAAA== +==== diff --git a/sys/contrib/ipfilter/netinet/QNX_OCL.txt b/sys/contrib/ipfilter/netinet/QNX_OCL.txt deleted file mode 100644 index b6237767967..00000000000 --- a/sys/contrib/ipfilter/netinet/QNX_OCL.txt +++ /dev/null @@ -1,277 +0,0 @@ -$FreeBSD$ - - End User License Certificate (EULA) End User License Certificate - (EULA) - Support Support - QNX Source Licenses QNX Source Licenses - License of the month - Confidential Source License - Version 1.0 - -QNX Open Community License Version 1.0 - - THIS QNX OPEN COMMUNITY LICENSE ( "THE OCL", OR "THIS AGREEMENT") - APPLIES TO PROGRAMS THAT QNX SOFTWARE SYSTEMS LTD. ("QSS") EXPRESSLY - ELECTS TO LICENSE UNDER THE OCL TERMS. IT ALSO APPLIES TO DERIVATIVE - WORKS CREATED UNDER THIS AGREEMENT THAT CREATORS ELECT TO LICENSE TO - OTHERS IN SOURCE CODE FORM. ANY USE, REPRODUCTION, MODIFICATION OR - DISTRIBUTION OF SUCH PROGRAMS CONSTITUTES RECIPIENT'S ACCEPTANCE OF - THE OCL. THE LICENSE RIGHTS GRANTED BELOW ARE CONDITIONAL UPON - RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT AND THE FORMATION OF A - BINDING CONTRACT. NOTHING ELSE GRANTS PERMISSION TO USE, REPRODUCE, - MODIFY OR DISTRIBUTE SUCH PROGRAMS OR THEIR DERIVATIVE WORKS. THESE - ACTIONS ARE OTHERWISE PROHIBITED. CONTACT QSS IF OTHER STEPS ARE - REQUIRED LOCALLY TO CREATE A BINDING CONTRACT. - - The OCL is intended to promote the development, use and distribution - of derivative works created from QSS source code. This includes - commercial distribution of object code versions under the terms of - Recipient's own license agreement and, at Recipient's option, sharing - of source code modifications within the QNX developer's community. The - license granted under the OCL is royalty free. Recipient is entitled - to charge royalties for object code versions of derivative works that - originate with Recipient. If Recipient elects to license source code - for its derivative works to others, then it must be licensed under the - OCL. The terms of the OCL are as follows: - -1. DEFINITIONS - - "Contribution" means: - - a. in the case of QSS: (i) the Original Program, where the Original - Program originates from QSS, (ii) changes and/or additions to - Unrestricted Open Source, where the Original Program originates - from Unrestricted Open Source and where such changes and/or - additions originate from QSS, and (iii) changes and/or additions - to the Program where such changes and/or additions originate from - QSS. - b. in the case of each Contributor, changes and/or additions to the - Program, where such changes and/or additions originate from and - are distributed by that particular Contributor. - - A Contribution 'originates' from a Contributor if it was added to the - Program by such Contributor itself or anyone acting on such - Contributor's behalf. Contributions do not include additions to the - Program which: (i) are separate modules of software distributed in - conjunction with the Program under their own license agreement, and - (ii) are not derivative works of the Program. - - "Contributor" means QSS and any other entity that distributes the - Program. - - "Licensed Patents " mean patent claims licensable by Contributor to - others, which are necessarily infringed by the use or sale of its - Contribution alone or when combined with the Program. - - "Unrestricted Open Source" means published source code that is - licensed for free use and distribution under an unrestricted licensing - and distribution model, such as the Berkley Software Design ("BSD") - and "BSD-like" licenses. It specifically excludes any source code - licensed under any version of the GNU General Public License (GPL) or - the GNU Lesser/Library GPL. All "Unrestricted Open Source" license - terms appear or are clearly identified in the header of any affected - source code for the Original Program. - - "Original Program" means the original version of the software - accompanying this Agreement as released by QSS, including source code, - object code and documentation, if any. - - "Program" means the Original Program and Contributions. - - "Recipient" means anyone who receives the Program under this - Agreement, including all Contributors. - -2. GRANT OF RIGHTS - - a. Subject to the terms of this Agreement, each Contributor hereby - grants Recipient a non-exclusive, worldwide, royalty-free - copyright license to reproduce, prepare derivative works of, - publicly display, publicly perform, and directly and indirectly - sublicense and distribute the Contribution of such Contributor, if - any, and such derivative works, in source code and object code - form. - b. Subject to the terms of this Agreement, each Contributor hereby - grants Recipient a non-exclusive, worldwide, royalty-free patent - license under Licensed Patents to make, use, sell, offer to sell, - import and otherwise transfer the Contribution of such - Contributor, if any, in source code and object code form. This - patent license shall apply to the combination of the Contribution - and the Program if, at the time the Contribution is added by the - Contributor, such addition of the Contribution causes such - combination to be covered by the Licensed Patents. The patent - license shall not apply to any other combinations which include - the Contribution. - c. Recipient understands that although each Contributor grants the - licenses to its Contributions set forth herein, no assurances are - provided by any Contributor that the Program does not infringe the - patent or other intellectual property rights of any other entity. - Each Contributor disclaims any liability to Recipient for claims - brought by any other entity based on infringement of intellectual - property rights or otherwise. As a condition to exercising the - rights and licenses granted hereunder, each Recipient hereby - assumes sole responsibility to secure any other intellectual - property rights needed, if any. For example, if a third party - patent license is required to allow Recipient to distribute the - Program, it is Recipient's responsibility to acquire that license - before distributing the Program. - d. Each Contributor represents that to its knowledge it has - sufficient copyright rights in its Contribution, if any, to grant - the copyright license set forth in this Agreement. - - 3. REQUIREMENTS - - A Contributor may choose to distribute the Program in object code form - under its own license agreement, provided that: - - a. it complies with the terms and conditions of this Agreement; and - b. its license agreement: - i. effectively disclaims on behalf of all Contributors all - warranties and conditions, express and implied, including - warranties or conditions of title and non-infringement, and - implied warranties or conditions of merchantability and - fitness for a particular purpose; - ii. effectively excludes on behalf of all Contributors all - liability for damages, including direct, indirect, special, - incidental and consequential damages, such as lost profits; - and - iii. states that any provisions which differ from this Agreement - are offered by that Contributor alone and not by any other - party. - - If the Program is made available in source code form: - - a. it must be made available under this Agreement; and - b. a copy of this Agreement must be included with each copy of the - Program. Each Contributor must include the following in a - conspicuous location in the Program along with any other copyright - or attribution statements required by the terms of any applicable - Unrestricted Open Source license: - Copyright {date here}, QNX Software Systems Ltd. and others. All - Rights Reserved. - - In addition, each Contributor must identify itself as the originator - of its Contribution, if any, in a manner that reasonably allows - subsequent Recipients to identify the originator of the Contribution. - - 4. COMMERCIAL DISTRIBUTION - - Commercial distributors of software may accept certain - responsibilities with respect to end users, business partners and the - like. While this license is intended to facilitate the commercial use - of the Program, the Contributor who includes the Program in a - commercial product offering should do so in a manner which does not - create potential liability for other Contributors. Therefore, if a - Contributor includes the Program in a commercial product offering, - such Contributor ("Commercial Contributor") hereby agrees to defend - and indemnify every other Contributor ("Indemnified Contributor") - against any losses, damages and costs (collectively "Losses") arising - from claims, lawsuits and other legal actions brought by a third party - against the Indemnified Contributor to the extent caused by the acts - or omissions of such Commercial Contributor in connection with its - distribution of the Program in a commercial product offering. The - obligations in this section do not apply to any claims or Losses - relating to any actual or alleged intellectual property infringement. - In order to qualify, an Indemnified Contributor must: a) promptly - notify the Commercial Contributor in writing of such claim, and b) - allow the Commercial Contributor to control, and cooperate with the - Commercial Contributor in, the defense and any related settlement - negotiations. The Indemnified Contributor may participate in any such - claim at its own expense. - - For example, a Contributor might include the Program in a commercial - product offering, Product X. That Contributor is then a Commercial - Contributor. If that Commercial Contributor then makes performance - claims, or offers warranties related to Product X, those performance - claims and warranties are such Commercial Contributor's responsibility - alone. Under this section, the Commercial Contributor would have to - defend claims against the other Contributors related to those - performance claims and warranties, and if a court requires any other - Contributor to pay any damages as a result, the Commercial Contributor - must pay those damages. - - 5. NO WARRANTY - - Recipient acknowledges that there may be errors or bugs in the Program - and that it is imperative that Recipient conduct thorough testing to - identify and correct any problems prior to the productive use or - commercial release of any products that use the Program, and prior to - the release of any modifications, updates or enhancements thereto. - - EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS - PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY - WARRANTIES OR CONDITIONS OF TITLE, NON- INFRINGEMENT, MERCHANTABILITY - OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is solely - responsible for determining the appropriateness of using and - distributing the Program and assumes all risks associated with its - exercise of rights under this Agreement, including but not limited to - the risks and costs of program errors, compliance with applicable - laws, damage to or loss of data, programs or equipment, and - unavailability or interruption of operations. - - 6. DISCLAIMER OF LIABILITY - - EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR - ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, - INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING - WITHOUT LIMITATION LOST PROFITS), 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 OR - DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED - HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - - 7. GENERAL - - If any provision of this Agreement is invalid or unenforceable under - applicable law, it shall not affect the validity or enforceability of - the remainder of the terms of this Agreement, and without further - action by the parties hereto, such provision shall be reformed to the - minimum extent necessary to make such provision valid and enforceable. - - If Recipient institutes patent litigation against a Contributor with - respect to a patent applicable to software (including a cross-claim or - counterclaim in a lawsuit), then any patent licenses granted by that - Contributor to such recipient under this Agreement shall terminate as - of the date such litigation is filed. In addition, If Recipient - institutes patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Program - itself (excluding combinations of the Program with other software or - hardware) infringes such Recipient's patent(s), then such Recipient's - rights granted under Section 2(b) shall terminate as of the date such - litigation is filed. - - All Recipient's rights under this Agreement shall terminate if it - fails to comply with any of the material terms or conditions of this - Agreement and does not cure such failure in a reasonable period of - time after becoming aware of such noncompliance. If all Recipient's - rights under this Agreement terminate, Recipient agrees to cease use - and distribution of the Program as soon as reasonably practicable. - However, Recipient's obligations under this Agreement and any licenses - granted by Recipient relating to the Program shall continue and - survive. - - QSS may publish new versions (including revisions) of this Agreement - from time to time. Each new version of the Agreement will be given a - distinguishing version number. The Program (including Contributions) - may always be distributed subject to the version of the Agreement - under which it was received. In addition, after a new version of the - Agreement is published, Contributor may elect to distribute the - Program (including its Contributions) under the new version. No one - other than QSS has the right to modify this Agreement. Except as - expressly stated in Sections 2(a) and 2(b) above, Recipient receives - no rights or licenses to the intellectual property of any Contributor - under this Agreement, whether expressly, by implication, estoppel or - otherwise. All rights in the Program not expressly granted under this - Agreement are reserved. - - This Agreement is governed by the laws in force in the Province of - Ontario, Canada without regard to the conflict of law provisions - therein. The parties expressly disclaim the provisions of the United - Nations Convention on Contracts for the International Sale of Goods. - No party to this Agreement will bring a legal action under this - Agreement more than one year after the cause of action arose. Each - party waives its rights to a jury trial in any resulting litigation. - - * QNX is a registered trademark of QNX Software Systems Ltd. - - Document Version: ocl1_00 diff --git a/sys/contrib/ipfilter/netinet/fil.c b/sys/contrib/ipfilter/netinet/fil.c index e8e543a1083..2adfe26bdd2 100644 --- a/sys/contrib/ipfilter/netinet/fil.c +++ b/sys/contrib/ipfilter/netinet/fil.c @@ -1,9 +1,14 @@ /* $FreeBSD$ */ /* - * Copyright (C) 1993-2003 by Darren Reed. + * Copyright (C) 2012 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. + * + * Copyright 2008 Sun Microsystems. + * + * $Id$ + * */ #if defined(KERNEL) || defined(_KERNEL) # undef KERNEL @@ -15,15 +20,6 @@ #include #include #include -#if defined(__NetBSD__) -# if (NetBSD >= 199905) && !defined(IPFILTER_LKM) && defined(_KERNEL) -# if (__NetBSD_Version__ < 301000000) -# include "opt_ipfilter_log.h" -# else -# include "opt_ipfilter.h" -# endif -# endif -#endif #if defined(_KERNEL) && defined(__FreeBSD_version) && \ (__FreeBSD_version >= 220000) # if (__FreeBSD_version >= 400000) @@ -82,23 +78,9 @@ struct file; #ifdef sun # include #endif -#if !defined(_KERNEL) && (defined(__FreeBSD__) || defined(SOLARIS2)) -# if (__FreeBSD_version >= 504000) -# undef _RADIX_H_ -# endif -# include "radix_ipf.h" -#endif -#ifdef __osf__ -# include "radix_ipf.h" -#else -# include -#endif #include #include #include -#if !defined(linux) -# include -#endif #if defined(__sgi) && defined(IFF_DRVRLOCK) /* IRIX 6 */ # include # include @@ -121,7 +103,6 @@ struct file; # include # endif #endif -#include #include "netinet/ip_fil.h" #include "netinet/ip_nat.h" #include "netinet/ip_frag.h" @@ -131,9 +112,8 @@ struct file; #ifdef IPFILTER_SCAN # include "netinet/ip_scan.h" #endif -#ifdef IPFILTER_SYNC -# include "netinet/ip_sync.h" -#endif +#include "netinet/ip_sync.h" +#include "netinet/ip_lookup.h" #include "netinet/ip_pool.h" #include "netinet/ip_htable.h" #ifdef IPFILTER_COMPILED @@ -144,14 +124,18 @@ struct file; #endif #if defined(__FreeBSD_version) && (__FreeBSD_version >= 300000) # include -# if defined(_KERNEL) && !defined(IPFILTER_LKM) -# include "opt_ipfilter.h" -# endif #endif #include "netinet/ipl.h" -/* END OF INCLUDES */ -#include +#if defined(__NetBSD__) && (__NetBSD_Version__ >= 104230000) +# include +extern struct callout ipf_slowtimer_ch; +#endif +#if defined(__OpenBSD__) +# include +extern struct timeout ipf_slowtimer_ch; +#endif +/* END OF INCLUDES */ #if !defined(lint) static const char sccsid[] = "@(#)fil.c 1.36 6/5/96 (C) 1993-2000 Darren Reed"; @@ -162,101 +146,80 @@ static const char rcsid[] = "@(#)$FreeBSD$"; #ifndef _KERNEL # include "ipf.h" # include "ipt.h" -# include "bpf-ipf.h" extern int opts; +extern int blockreason; #endif /* _KERNEL */ +#define LBUMP(x) softc->x++ +#define LBUMPD(x, y) do { softc->x.y++; DT(y); } while (0) -fr_info_t frcache[2][8]; -struct filterstats frstats[2]; -struct frentry *ipfilter[2][2] = { { NULL, NULL }, { NULL, NULL } }, - *ipfilter6[2][2] = { { NULL, NULL }, { NULL, NULL } }, - *ipacct6[2][2] = { { NULL, NULL }, { NULL, NULL } }, - *ipacct[2][2] = { { NULL, NULL }, { NULL, NULL } }, - *ipnatrules[2][2] = { { NULL, NULL }, { NULL, NULL } }; -struct frgroup *ipfgroups[IPL_LOGSIZE][2]; -char ipfilter_version[] = IPL_VERSION; -int fr_refcnt = 0; -/* - * For fr_running: - * 0 == loading, 1 = running, -1 = disabled, -2 = unloading - */ -int fr_running = 0; -int fr_flags = IPF_LOGGING; -int fr_active = 0; -int fr_control_forwarding = 0; -int fr_update_ipid = 0; -u_short fr_ip_id = 0; -int fr_chksrc = 0; /* causes a system crash if enabled */ -int fr_minttl = 4; -int fr_icmpminfragmtu = 68; -u_long fr_frouteok[2] = {0, 0}; -u_long fr_userifqs = 0; -u_long fr_badcoalesces[2] = {0, 0}; -u_char ipf_iss_secret[32]; -#if defined(IPFILTER_DEFAULT_BLOCK) -int fr_pass = FR_BLOCK|FR_NOMATCH; -#else -int fr_pass = (IPF_DEFAULT_PASS)|FR_NOMATCH; +static INLINE int ipf_check_ipf __P((fr_info_t *, frentry_t *, int)); +static u_32_t ipf_checkcipso __P((fr_info_t *, u_char *, int)); +static u_32_t ipf_checkripso __P((u_char *)); +static u_32_t ipf_decaps __P((fr_info_t *, u_32_t, int)); +#ifdef IPFILTER_LOG +static frentry_t *ipf_dolog __P((fr_info_t *, u_32_t *)); #endif -int fr_features = 0 -#ifdef IPFILTER_LKM - | IPF_FEAT_LKM +static int ipf_flushlist __P((ipf_main_softc_t *, int *, + frentry_t **)); +static int ipf_flush_groups __P((ipf_main_softc_t *, frgroup_t **, + int)); +static ipfunc_t ipf_findfunc __P((ipfunc_t)); +static void *ipf_findlookup __P((ipf_main_softc_t *, int, + frentry_t *, + i6addr_t *, i6addr_t *)); +static frentry_t *ipf_firewall __P((fr_info_t *, u_32_t *)); +static int ipf_fr_matcharray __P((fr_info_t *, int *)); +static int ipf_frruleiter __P((ipf_main_softc_t *, void *, int, + void *)); +static void ipf_funcfini __P((ipf_main_softc_t *, frentry_t *)); +static int ipf_funcinit __P((ipf_main_softc_t *, frentry_t *)); +static int ipf_geniter __P((ipf_main_softc_t *, ipftoken_t *, + ipfgeniter_t *)); +static void ipf_getstat __P((ipf_main_softc_t *, + struct friostat *, int)); +static int ipf_group_flush __P((ipf_main_softc_t *, frgroup_t *)); +static void ipf_group_free __P((frgroup_t *)); +static int ipf_grpmapfini __P((struct ipf_main_softc_s *, + frentry_t *)); +static int ipf_grpmapinit __P((struct ipf_main_softc_s *, + frentry_t *)); +static frentry_t *ipf_nextrule __P((ipf_main_softc_t *, int, int, + frentry_t *, int)); +static int ipf_portcheck __P((frpcmp_t *, u_32_t)); +static INLINE int ipf_pr_ah __P((fr_info_t *)); +static INLINE void ipf_pr_esp __P((fr_info_t *)); +static INLINE void ipf_pr_gre __P((fr_info_t *)); +static INLINE void ipf_pr_udp __P((fr_info_t *)); +static INLINE void ipf_pr_tcp __P((fr_info_t *)); +static INLINE void ipf_pr_icmp __P((fr_info_t *)); +static INLINE void ipf_pr_ipv4hdr __P((fr_info_t *)); +static INLINE void ipf_pr_short __P((fr_info_t *, int)); +static INLINE int ipf_pr_tcpcommon __P((fr_info_t *)); +static INLINE int ipf_pr_udpcommon __P((fr_info_t *)); +static void ipf_rule_delete __P((ipf_main_softc_t *, frentry_t *f, + int, int)); +static void ipf_rule_expire_insert __P((ipf_main_softc_t *, + frentry_t *, int)); +static int ipf_synclist __P((ipf_main_softc_t *, frentry_t *, + void *)); +static void ipf_token_flush __P((ipf_main_softc_t *)); +static void ipf_token_unlink __P((ipf_main_softc_t *, + ipftoken_t *)); +static ipftuneable_t *ipf_tune_findbyname __P((ipftuneable_t *, + const char *)); +static ipftuneable_t *ipf_tune_findbycookie __P((ipftuneable_t **, void *, + void **)); +static int ipf_updateipid __P((fr_info_t *)); +static int ipf_settimeout __P((struct ipf_main_softc_s *, + struct ipftuneable *, + ipftuneval_t *)); +#if !defined(_KERNEL) || (!defined(__NetBSD__) && !defined(__OpenBSD__) && \ + !defined(__FreeBSD__)) || \ + FREEBSD_LT_REV(501000) || NETBSD_LT_REV(105000000) || \ + OPENBSD_LT_REV(200006) +static int ppsratecheck(struct timeval *, int *, int); #endif -#ifdef IPFILTER_LOG - | IPF_FEAT_LOG -#endif -#ifdef IPFILTER_LOOKUP - | IPF_FEAT_LOOKUP -#endif -#ifdef IPFILTER_BPF - | IPF_FEAT_BPF -#endif -#ifdef IPFILTER_COMPILED - | IPF_FEAT_COMPILED -#endif -#ifdef IPFILTER_CKSUM - | IPF_FEAT_CKSUM -#endif -#ifdef IPFILTER_SYNC - | IPF_FEAT_SYNC -#endif -#ifdef IPFILTER_SCAN - | IPF_FEAT_SCAN -#endif -#ifdef USE_INET6 - | IPF_FEAT_IPV6 -#endif - ; - -static INLINE int fr_ipfcheck __P((fr_info_t *, frentry_t *, int)); -static int fr_portcheck __P((frpcmp_t *, u_short *)); -static int frflushlist __P((int, minor_t, int *, frentry_t **)); -static ipfunc_t fr_findfunc __P((ipfunc_t)); -static frentry_t *fr_firewall __P((fr_info_t *, u_32_t *)); -static int fr_funcinit __P((frentry_t *fr)); -static INLINE void frpr_ah __P((fr_info_t *)); -static INLINE void frpr_esp __P((fr_info_t *)); -static INLINE void frpr_gre __P((fr_info_t *)); -static INLINE void frpr_udp __P((fr_info_t *)); -static INLINE void frpr_tcp __P((fr_info_t *)); -static INLINE void frpr_icmp __P((fr_info_t *)); -static INLINE void frpr_ipv4hdr __P((fr_info_t *)); -static INLINE int frpr_pullup __P((fr_info_t *, int)); -static INLINE void frpr_short __P((fr_info_t *, int)); -static INLINE int frpr_tcpcommon __P((fr_info_t *)); -static INLINE int frpr_udpcommon __P((fr_info_t *)); -static int fr_updateipid __P((fr_info_t *)); -#ifdef IPFILTER_LOOKUP -static int fr_grpmapinit __P((frentry_t *fr)); -static INLINE void *fr_resolvelookup __P((u_int, u_int, i6addr_t *, lookupfunc_t *)); -#endif -static void frsynclist __P((frentry_t *, void *)); -static ipftuneable_t *fr_findtunebyname __P((const char *)); -static ipftuneable_t *fr_findtunebycookie __P((void *, void **)); -static int ipf_geniter __P((ipftoken_t *, ipfgeniter_t *)); -static int ipf_frruleiter __P((void *, int, void *)); -static void ipf_unlinktoken __P((ipftoken_t *)); /* @@ -265,7 +228,7 @@ static void ipf_unlinktoken __P((ipftoken_t *)); * hand side to allow for binary searching of the array and include a trailer * with a 0 for the bitmask for linear searches to easily find the end with. */ -const struct optlist ipopts[20] = { +static const struct optlist ipopts[20] = { { IPOPT_NOP, 0x000001 }, { IPOPT_RR, 0x000002 }, { IPOPT_ZSU, 0x000004 }, @@ -289,7 +252,7 @@ const struct optlist ipopts[20] = { }; #ifdef USE_INET6 -struct optlist ip6exthdr[] = { +static struct optlist ip6exthdr[] = { { IPPROTO_HOPOPTS, 0x000001 }, { IPPROTO_IPV6, 0x000002 }, { IPPROTO_ROUTING, 0x000004 }, @@ -303,20 +266,10 @@ struct optlist ip6exthdr[] = { }; #endif -struct optlist tcpopts[] = { - { TCPOPT_NOP, 0x000001 }, - { TCPOPT_MAXSEG, 0x000002 }, - { TCPOPT_WINDOW, 0x000004 }, - { TCPOPT_SACK_PERMITTED, 0x000008 }, - { TCPOPT_SACK, 0x000010 }, - { TCPOPT_TIMESTAMP, 0x000020 }, - { 0, 0x000000 } -}; - /* * bit values for identifying presence of individual IP security options */ -const struct optlist secopt[8] = { +static const struct optlist secopt[8] = { { IPSO_CLASS_RES4, 0x01 }, { IPSO_CLASS_TOPS, 0x02 }, { IPSO_CLASS_SECR, 0x04 }, @@ -327,16 +280,143 @@ const struct optlist secopt[8] = { { IPSO_CLASS_RES1, 0x80 } }; +char ipfilter_version[] = IPL_VERSION; + +int ipf_features = 0 +#ifdef IPFILTER_LKM + | IPF_FEAT_LKM +#endif +#ifdef IPFILTER_LOG + | IPF_FEAT_LOG +#endif + | IPF_FEAT_LOOKUP +#ifdef IPFILTER_BPF + | IPF_FEAT_BPF +#endif +#ifdef IPFILTER_COMPILED + | IPF_FEAT_COMPILED +#endif +#ifdef IPFILTER_CKSUM + | IPF_FEAT_CKSUM +#endif + | IPF_FEAT_SYNC +#ifdef IPFILTER_SCAN + | IPF_FEAT_SCAN +#endif +#ifdef USE_INET6 + | IPF_FEAT_IPV6 +#endif + ; + /* * Table of functions available for use with call rules. */ -static ipfunc_resolve_t fr_availfuncs[] = { -#ifdef IPFILTER_LOOKUP - { "fr_srcgrpmap", fr_srcgrpmap, fr_grpmapinit }, - { "fr_dstgrpmap", fr_dstgrpmap, fr_grpmapinit }, +static ipfunc_resolve_t ipf_availfuncs[] = { + { "srcgrpmap", ipf_srcgrpmap, ipf_grpmapinit, ipf_grpmapfini }, + { "dstgrpmap", ipf_dstgrpmap, ipf_grpmapinit, ipf_grpmapfini }, + { "", NULL, NULL, NULL } +}; + +static ipftuneable_t ipf_main_tuneables[] = { + { { (void *)offsetof(struct ipf_main_softc_s, ipf_flags) }, + "ipf_flags", 0, 0xffffffff, + stsizeof(ipf_main_softc_t, ipf_flags), + 0, NULL, NULL }, + { { (void *)offsetof(struct ipf_main_softc_s, ipf_active) }, + "active", 0, 0, + stsizeof(ipf_main_softc_t, ipf_active), + IPFT_RDONLY, NULL, NULL }, + { { (void *)offsetof(ipf_main_softc_t, ipf_control_forwarding) }, + "control_forwarding", 0, 1, + stsizeof(ipf_main_softc_t, ipf_control_forwarding), + 0, NULL, NULL }, + { { (void *)offsetof(ipf_main_softc_t, ipf_update_ipid) }, + "update_ipid", 0, 1, + stsizeof(ipf_main_softc_t, ipf_update_ipid), + 0, NULL, NULL }, + { { (void *)offsetof(ipf_main_softc_t, ipf_chksrc) }, + "chksrc", 0, 1, + stsizeof(ipf_main_softc_t, ipf_chksrc), + 0, NULL, NULL }, + { { (void *)offsetof(ipf_main_softc_t, ipf_minttl) }, + "min_ttl", 0, 1, + stsizeof(ipf_main_softc_t, ipf_minttl), + 0, NULL, NULL }, + { { (void *)offsetof(ipf_main_softc_t, ipf_icmpminfragmtu) }, + "icmp_minfragmtu", 0, 1, + stsizeof(ipf_main_softc_t, ipf_icmpminfragmtu), + 0, NULL, NULL }, + { { (void *)offsetof(ipf_main_softc_t, ipf_pass) }, + "default_pass", 0, 0xffffffff, + stsizeof(ipf_main_softc_t, ipf_pass), + 0, NULL, NULL }, + { { (void *)offsetof(ipf_main_softc_t, ipf_tcpidletimeout) }, + "tcp_idle_timeout", 1, 0x7fffffff, + stsizeof(ipf_main_softc_t, ipf_tcpidletimeout), + 0, NULL, ipf_settimeout }, + { { (void *)offsetof(ipf_main_softc_t, ipf_tcpclosewait) }, + "tcp_close_wait", 1, 0x7fffffff, + stsizeof(ipf_main_softc_t, ipf_tcpclosewait), + 0, NULL, ipf_settimeout }, + { { (void *)offsetof(ipf_main_softc_t, ipf_tcplastack) }, + "tcp_last_ack", 1, 0x7fffffff, + stsizeof(ipf_main_softc_t, ipf_tcplastack), + 0, NULL, ipf_settimeout }, + { { (void *)offsetof(ipf_main_softc_t, ipf_tcptimeout) }, + "tcp_timeout", 1, 0x7fffffff, + stsizeof(ipf_main_softc_t, ipf_tcptimeout), + 0, NULL, ipf_settimeout }, + { { (void *)offsetof(ipf_main_softc_t, ipf_tcpsynsent) }, + "tcp_syn_sent", 1, 0x7fffffff, + stsizeof(ipf_main_softc_t, ipf_tcpsynsent), + 0, NULL, ipf_settimeout }, + { { (void *)offsetof(ipf_main_softc_t, ipf_tcpsynrecv) }, + "tcp_syn_received", 1, 0x7fffffff, + stsizeof(ipf_main_softc_t, ipf_tcpsynrecv), + 0, NULL, ipf_settimeout }, + { { (void *)offsetof(ipf_main_softc_t, ipf_tcpclosed) }, + "tcp_closed", 1, 0x7fffffff, + stsizeof(ipf_main_softc_t, ipf_tcpclosed), + 0, NULL, ipf_settimeout }, + { { (void *)offsetof(ipf_main_softc_t, ipf_tcphalfclosed) }, + "tcp_half_closed", 1, 0x7fffffff, + stsizeof(ipf_main_softc_t, ipf_tcphalfclosed), + 0, NULL, ipf_settimeout }, + { { (void *)offsetof(ipf_main_softc_t, ipf_tcptimewait) }, + "tcp_time_wait", 1, 0x7fffffff, + stsizeof(ipf_main_softc_t, ipf_tcptimewait), + 0, NULL, ipf_settimeout }, + { { (void *)offsetof(ipf_main_softc_t, ipf_udptimeout) }, + "udp_timeout", 1, 0x7fffffff, + stsizeof(ipf_main_softc_t, ipf_udptimeout), + 0, NULL, ipf_settimeout }, + { { (void *)offsetof(ipf_main_softc_t, ipf_udpacktimeout) }, + "udp_ack_timeout", 1, 0x7fffffff, + stsizeof(ipf_main_softc_t, ipf_udpacktimeout), + 0, NULL, ipf_settimeout }, + { { (void *)offsetof(ipf_main_softc_t, ipf_icmptimeout) }, + "icmp_timeout", 1, 0x7fffffff, + stsizeof(ipf_main_softc_t, ipf_icmptimeout), + 0, NULL, ipf_settimeout }, + { { (void *)offsetof(ipf_main_softc_t, ipf_icmpacktimeout) }, + "icmp_ack_timeout", 1, 0x7fffffff, + stsizeof(ipf_main_softc_t, ipf_icmpacktimeout), + 0, NULL, ipf_settimeout }, + { { (void *)offsetof(ipf_main_softc_t, ipf_iptimeout) }, + "ip_timeout", 1, 0x7fffffff, + stsizeof(ipf_main_softc_t, ipf_iptimeout), + 0, NULL, ipf_settimeout }, +#if defined(INSTANCES) && defined(_KERNEL) + { { (void *)offsetof(ipf_main_softc_t, ipf_get_loopback) }, + "intercept_loopback", 0, 1, + stsizeof(ipf_main_softc_t, ipf_get_loopback), + 0, NULL, ipf_set_loopback }, #endif - { "", NULL, NULL } + { { 0 }, + NULL, 0, 0, + 0, + 0, NULL, NULL } }; @@ -346,39 +426,41 @@ static ipfunc_resolve_t fr_availfuncs[] = { * current packet. There are different routines for the same protocol * for each of IPv4 and IPv6. Adding a new protocol, for which there * will "special" inspection for setup, is now more easily done by adding - * a new routine and expanding the frpr_ipinit*() function rather than by + * a new routine and expanding the ipf_pr_ipinit*() function rather than by * adding more code to a growing switch statement. */ #ifdef USE_INET6 -static INLINE int frpr_ah6 __P((fr_info_t *)); -static INLINE void frpr_esp6 __P((fr_info_t *)); -static INLINE void frpr_gre6 __P((fr_info_t *)); -static INLINE void frpr_udp6 __P((fr_info_t *)); -static INLINE void frpr_tcp6 __P((fr_info_t *)); -static INLINE void frpr_icmp6 __P((fr_info_t *)); -static INLINE int frpr_ipv6hdr __P((fr_info_t *)); -static INLINE void frpr_short6 __P((fr_info_t *, int)); -static INLINE int frpr_hopopts6 __P((fr_info_t *)); -static INLINE int frpr_mobility6 __P((fr_info_t *)); -static INLINE int frpr_routing6 __P((fr_info_t *)); -static INLINE int frpr_dstopts6 __P((fr_info_t *)); -static INLINE int frpr_fragment6 __P((fr_info_t *)); -static INLINE int frpr_ipv6exthdr __P((fr_info_t *, int, int)); +static INLINE int ipf_pr_ah6 __P((fr_info_t *)); +static INLINE void ipf_pr_esp6 __P((fr_info_t *)); +static INLINE void ipf_pr_gre6 __P((fr_info_t *)); +static INLINE void ipf_pr_udp6 __P((fr_info_t *)); +static INLINE void ipf_pr_tcp6 __P((fr_info_t *)); +static INLINE void ipf_pr_icmp6 __P((fr_info_t *)); +static INLINE void ipf_pr_ipv6hdr __P((fr_info_t *)); +static INLINE void ipf_pr_short6 __P((fr_info_t *, int)); +static INLINE int ipf_pr_hopopts6 __P((fr_info_t *)); +static INLINE int ipf_pr_mobility6 __P((fr_info_t *)); +static INLINE int ipf_pr_routing6 __P((fr_info_t *)); +static INLINE int ipf_pr_dstopts6 __P((fr_info_t *)); +static INLINE int ipf_pr_fragment6 __P((fr_info_t *)); +static INLINE struct ip6_ext *ipf_pr_ipv6exthdr __P((fr_info_t *, int, int)); /* ------------------------------------------------------------------------ */ -/* Function: frpr_short6 */ +/* Function: ipf_pr_short6 */ /* Returns: void */ -/* Parameters: fin(I) - pointer to packet information */ +/* Parameters: fin(I) - pointer to packet information */ +/* xmin(I) - minimum header size */ /* */ /* IPv6 Only */ /* This is function enforces the 'is a packet too short to be legit' rule */ /* for IPv6 and marks the packet with FI_SHORT if so. See function comment */ -/* for frpr_short() for more details. */ +/* for ipf_pr_short() for more details. */ /* ------------------------------------------------------------------------ */ -static INLINE void frpr_short6(fin, xmin) -fr_info_t *fin; -int xmin; +static INLINE void +ipf_pr_short6(fin, xmin) + fr_info_t *fin; + int xmin; { if (fin->fin_dlen < xmin) @@ -387,8 +469,8 @@ int xmin; /* ------------------------------------------------------------------------ */ -/* Function: frpr_ipv6hdr */ -/* Returns: int - 0 = IPv6 packet intact, -1 = packet lost */ +/* Function: ipf_pr_ipv6hdr */ +/* Returns: void */ /* Parameters: fin(I) - pointer to packet information */ /* */ /* IPv6 Only */ @@ -397,8 +479,9 @@ int xmin; /* analyzer may pullup or free the packet itself so we need to be vigiliant */ /* of that possibility arising. */ /* ------------------------------------------------------------------------ */ -static INLINE int frpr_ipv6hdr(fin) -fr_info_t *fin; +static INLINE void +ipf_pr_ipv6hdr(fin) + fr_info_t *fin; { ip6_t *ip6 = (ip6_t *)fin->fin_ip; int p, go = 1, i, hdrcount; @@ -412,57 +495,68 @@ fr_info_t *fin; fi->fi_auth = 0; p = ip6->ip6_nxt; + fin->fin_crc = p; fi->fi_ttl = ip6->ip6_hlim; fi->fi_src.in6 = ip6->ip6_src; + fin->fin_crc += fi->fi_src.i6[0]; + fin->fin_crc += fi->fi_src.i6[1]; + fin->fin_crc += fi->fi_src.i6[2]; + fin->fin_crc += fi->fi_src.i6[3]; fi->fi_dst.in6 = ip6->ip6_dst; - fin->fin_id = (u_short)(ip6->ip6_flow & 0xffff); + fin->fin_crc += fi->fi_dst.i6[0]; + fin->fin_crc += fi->fi_dst.i6[1]; + fin->fin_crc += fi->fi_dst.i6[2]; + fin->fin_crc += fi->fi_dst.i6[3]; + fin->fin_id = 0; + if (IN6_IS_ADDR_MULTICAST(&fi->fi_dst.in6)) + fin->fin_flx |= FI_MULTICAST|FI_MBCAST; hdrcount = 0; - while (go && !(fin->fin_flx & (FI_BAD|FI_SHORT))) { + while (go && !(fin->fin_flx & FI_SHORT)) { switch (p) { case IPPROTO_UDP : - frpr_udp6(fin); + ipf_pr_udp6(fin); go = 0; break; case IPPROTO_TCP : - frpr_tcp6(fin); + ipf_pr_tcp6(fin); go = 0; break; case IPPROTO_ICMPV6 : - frpr_icmp6(fin); + ipf_pr_icmp6(fin); go = 0; break; case IPPROTO_GRE : - frpr_gre6(fin); + ipf_pr_gre6(fin); go = 0; break; case IPPROTO_HOPOPTS : - p = frpr_hopopts6(fin); + p = ipf_pr_hopopts6(fin); break; case IPPROTO_MOBILITY : - p = frpr_mobility6(fin); + p = ipf_pr_mobility6(fin); break; case IPPROTO_DSTOPTS : - p = frpr_dstopts6(fin); + p = ipf_pr_dstopts6(fin); break; case IPPROTO_ROUTING : - p = frpr_routing6(fin); + p = ipf_pr_routing6(fin); break; case IPPROTO_AH : - p = frpr_ah6(fin); + p = ipf_pr_ah6(fin); break; case IPPROTO_ESP : - frpr_esp6(fin); + ipf_pr_esp6(fin); go = 0; break; @@ -480,7 +574,13 @@ fr_info_t *fin; break; case IPPROTO_FRAGMENT : - p = frpr_fragment6(fin); + p = ipf_pr_fragment6(fin); + /* + * Given that the only fragments we want to let through + * (where fin_off != 0) are those where the non-first + * fragments only have data, we can safely stop looking + * at headers if this is a non-leading fragment. + */ if (fin->fin_off != 0) go = 0; break; @@ -501,39 +601,61 @@ fr_info_t *fin; * header. */ if ((go != 0) && (p != IPPROTO_NONE) && - (frpr_pullup(fin, 0) == -1)) { + (ipf_pr_pullup(fin, 0) == -1)) { p = IPPROTO_NONE; - go = 0; + break; } } + + /* + * Some of the above functions, like ipf_pr_esp6(), can call ipf_pullup + * and destroy whatever packet was here. The caller of this function + * expects us to return if there is a problem with ipf_pullup. + */ + if (fin->fin_m == NULL) { + ipf_main_softc_t *softc = fin->fin_main_soft; + + LBUMPD(ipf_stats[fin->fin_out], fr_v6_bad); + return; + } + fi->fi_p = p; /* - * Some of the above functions, like frpr_esp6(), can call fr_pullup - * and destroy whatever packet was here. The caller of this function - * expects us to return -1 if there is a problem with fr_pullup. + * IPv6 fragment case 1 - see comment for ipf_pr_fragment6(). + * "go != 0" imples the above loop hasn't arrived at a layer 4 header. */ - if (fin->fin_m == NULL) - return -1; + if ((go != 0) && (fin->fin_flx & FI_FRAG) && (fin->fin_off == 0)) { + ipf_main_softc_t *softc = fin->fin_main_soft; - return 0; + fin->fin_flx |= FI_BAD; + LBUMPD(ipf_stats[fin->fin_out], fr_v6_badfrag); + LBUMP(ipf_stats[fin->fin_out].fr_v6_bad); + } } /* ------------------------------------------------------------------------ */ -/* Function: frpr_ipv6exthdr */ -/* Returns: int - value of the next header or IPPROTO_NONE if error */ +/* Function: ipf_pr_ipv6exthdr */ +/* Returns: struct ip6_ext * - pointer to the start of the next header */ +/* or NULL if there is a prolblem. */ /* Parameters: fin(I) - pointer to packet information */ /* multiple(I) - flag indicating yes/no if multiple occurances */ /* of this extension header are allowed. */ /* proto(I) - protocol number for this extension header */ /* */ /* IPv6 Only */ +/* This function embodies a number of common checks that all IPv6 extension */ +/* headers must be subjected to. For example, making sure the packet is */ +/* big enough for it to be in, checking if it is repeated and setting a */ +/* flag to indicate its presence. */ /* ------------------------------------------------------------------------ */ -static INLINE int frpr_ipv6exthdr(fin, multiple, proto) -fr_info_t *fin; -int multiple, proto; +static INLINE struct ip6_ext * +ipf_pr_ipv6exthdr(fin, multiple, proto) + fr_info_t *fin; + int multiple, proto; { + ipf_main_softc_t *softc = fin->fin_main_soft; struct ip6_ext *hdr; u_short shift; int i; @@ -543,11 +665,14 @@ int multiple, proto; /* 8 is default length of extension hdr */ if ((fin->fin_dlen - 8) < 0) { fin->fin_flx |= FI_SHORT; - return IPPROTO_NONE; + LBUMPD(ipf_stats[fin->fin_out], fr_v6_ext_short); + return NULL; } - if (frpr_pullup(fin, 8) == -1) - return IPPROTO_NONE; + if (ipf_pr_pullup(fin, 8) == -1) { + LBUMPD(ipf_stats[fin->fin_out], fr_v6_ext_pullup); + return NULL; + } hdr = fin->fin_dp; switch (proto) @@ -562,9 +687,21 @@ int multiple, proto; if (shift > fin->fin_dlen) { /* Nasty extension header length? */ fin->fin_flx |= FI_BAD; - return IPPROTO_NONE; + LBUMPD(ipf_stats[fin->fin_out], fr_v6_ext_hlen); + return NULL; } + fin->fin_dp = (char *)fin->fin_dp + shift; + fin->fin_dlen -= shift; + + /* + * If we have seen a fragment header, do not set any flags to indicate + * the presence of this extension header as it has no impact on the + * end result until after it has been defragmented. + */ + if (fin->fin_flx & FI_FRAG) + return hdr; + for (i = 0; ip6exthdr[i].ol_bit != 0; i++) if (ip6exthdr[i].ol_val == proto) { /* @@ -578,149 +715,196 @@ int multiple, proto; break; } - fin->fin_exthdr = fin->fin_dp; - fin->fin_dp = (char *)fin->fin_dp + shift; - fin->fin_dlen -= shift; - - return hdr->ip6e_nxt; + return hdr; } /* ------------------------------------------------------------------------ */ -/* Function: frpr_hopopts6 */ +/* Function: ipf_pr_hopopts6 */ /* Returns: int - value of the next header or IPPROTO_NONE if error */ /* Parameters: fin(I) - pointer to packet information */ /* */ /* IPv6 Only */ /* This is function checks pending hop by hop options extension header */ /* ------------------------------------------------------------------------ */ -static INLINE int frpr_hopopts6(fin) -fr_info_t *fin; +static INLINE int +ipf_pr_hopopts6(fin) + fr_info_t *fin; { - return frpr_ipv6exthdr(fin, 0, IPPROTO_HOPOPTS); + struct ip6_ext *hdr; + + hdr = ipf_pr_ipv6exthdr(fin, 0, IPPROTO_HOPOPTS); + if (hdr == NULL) + return IPPROTO_NONE; + return hdr->ip6e_nxt; } /* ------------------------------------------------------------------------ */ -/* Function: frpr_mobility6 */ +/* Function: ipf_pr_mobility6 */ /* Returns: int - value of the next header or IPPROTO_NONE if error */ /* Parameters: fin(I) - pointer to packet information */ /* */ /* IPv6 Only */ /* This is function checks the IPv6 mobility extension header */ /* ------------------------------------------------------------------------ */ -static INLINE int frpr_mobility6(fin) -fr_info_t *fin; +static INLINE int +ipf_pr_mobility6(fin) + fr_info_t *fin; { - return frpr_ipv6exthdr(fin, 0, IPPROTO_MOBILITY); + struct ip6_ext *hdr; + + hdr = ipf_pr_ipv6exthdr(fin, 0, IPPROTO_MOBILITY); + if (hdr == NULL) + return IPPROTO_NONE; + return hdr->ip6e_nxt; } /* ------------------------------------------------------------------------ */ -/* Function: frpr_routing6 */ +/* Function: ipf_pr_routing6 */ /* Returns: int - value of the next header or IPPROTO_NONE if error */ /* Parameters: fin(I) - pointer to packet information */ /* */ /* IPv6 Only */ /* This is function checks pending routing extension header */ /* ------------------------------------------------------------------------ */ -static INLINE int frpr_routing6(fin) -fr_info_t *fin; +static INLINE int +ipf_pr_routing6(fin) + fr_info_t *fin; { - struct ip6_ext *hdr; + struct ip6_routing *hdr; - if (frpr_ipv6exthdr(fin, 0, IPPROTO_ROUTING) == IPPROTO_NONE) + hdr = (struct ip6_routing *)ipf_pr_ipv6exthdr(fin, 0, IPPROTO_ROUTING); + if (hdr == NULL) return IPPROTO_NONE; - hdr = fin->fin_exthdr; - if ((hdr->ip6e_len & 1) != 0) { + switch (hdr->ip6r_type) + { + case 0 : /* - * The routing header data is made up of 128 bit IPv6 addresses - * which means it must be a multiple of 2 lots of 8 in length. + * Nasty extension header length? */ - fin->fin_flx |= FI_BAD; - /* - * Compensate for the changes made in frpr_ipv6exthdr() - */ - fin->fin_dlen += 8 + (hdr->ip6e_len << 3); - fin->fin_dp = hdr; - return IPPROTO_NONE; + if (((hdr->ip6r_len >> 1) < hdr->ip6r_segleft) || + (hdr->ip6r_segleft && (hdr->ip6r_len & 1))) { + ipf_main_softc_t *softc = fin->fin_main_soft; + + fin->fin_flx |= FI_BAD; + LBUMPD(ipf_stats[fin->fin_out], fr_v6_rh_bad); + return IPPROTO_NONE; + } + break; + + default : + break; } - return hdr->ip6e_nxt; + return hdr->ip6r_nxt; } /* ------------------------------------------------------------------------ */ -/* Function: frpr_fragment6 */ +/* Function: ipf_pr_fragment6 */ /* Returns: int - value of the next header or IPPROTO_NONE if error */ /* Parameters: fin(I) - pointer to packet information */ /* */ /* IPv6 Only */ /* Examine the IPv6 fragment header and extract fragment offset information.*/ /* */ -/* We don't know where the transport layer header (or whatever is next is), */ -/* as it could be behind destination options (amongst others). Because */ -/* there is no fragment cache, there is no knowledge about whether or not an*/ -/* upper layer header has been seen (or where it ends) and thus we are not */ -/* able to continue processing beyond this header with any confidence. */ +/* Fragments in IPv6 are extraordinarily difficult to deal with - much more */ +/* so than in IPv4. There are 5 cases of fragments with IPv6 that all */ +/* packets with a fragment header can fit into. They are as follows: */ +/* */ +/* 1. [IPv6][0-n EH][FH][0-n EH] (no L4HDR present) */ +/* 2. [IPV6][0-n EH][FH][0-n EH][L4HDR part] (short) */ +/* 3. [IPV6][0-n EH][FH][L4HDR part][0-n data] (short) */ +/* 4. [IPV6][0-n EH][FH][0-n EH][L4HDR][0-n data] */ +/* 5. [IPV6][0-n EH][FH][data] */ +/* */ +/* IPV6 = IPv6 header, FH = Fragment Header, */ +/* 0-n EH = 0 or more extension headers, 0-n data = 0 or more bytes of data */ +/* */ +/* Packets that match 1, 2, 3 will be dropped as the only reasonable */ +/* scenario in which they happen is in extreme circumstances that are most */ +/* likely to be an indication of an attack rather than normal traffic. */ +/* A type 3 packet may be sent by an attacked after a type 4 packet. There */ +/* are two rules that can be used to guard against type 3 packets: L4 */ +/* headers must always be in a packet that has the offset field set to 0 */ +/* and no packet is allowed to overlay that where offset = 0. */ /* ------------------------------------------------------------------------ */ -static INLINE int frpr_fragment6(fin) -fr_info_t *fin; +static INLINE int +ipf_pr_fragment6(fin) + fr_info_t *fin; { + ipf_main_softc_t *softc = fin->fin_main_soft; struct ip6_frag *frag; - int extoff; fin->fin_flx |= FI_FRAG; - if (frpr_ipv6exthdr(fin, 0, IPPROTO_FRAGMENT) == IPPROTO_NONE) - return IPPROTO_NONE; - - extoff = (char *)fin->fin_exthdr - (char *)fin->fin_dp; - - if (frpr_pullup(fin, sizeof(*frag)) == -1) - return IPPROTO_NONE; - - fin->fin_exthdr = (char *)fin->fin_dp + extoff; - frag = fin->fin_exthdr; - /* - * Fragment but no fragmentation info set? Bad packet... - */ - if (frag->ip6f_offlg == 0) { - fin->fin_flx |= FI_BAD; + frag = (struct ip6_frag *)ipf_pr_ipv6exthdr(fin, 0, IPPROTO_FRAGMENT); + if (frag == NULL) { + LBUMPD(ipf_stats[fin->fin_out], fr_v6_frag_bad); return IPPROTO_NONE; } + if ((frag->ip6f_offlg & IP6F_MORE_FRAG) != 0) { + /* + * Any fragment that isn't the last fragment must have its + * length as a multiple of 8. + */ + if ((fin->fin_plen & 7) != 0) + fin->fin_flx |= FI_BAD; + } + + fin->fin_fraghdr = frag; + fin->fin_id = frag->ip6f_ident; fin->fin_off = ntohs(frag->ip6f_offlg & IP6F_OFF_MASK); - fin->fin_off <<= 3; if (fin->fin_off != 0) fin->fin_flx |= FI_FRAGBODY; - fin->fin_dp = (char *)fin->fin_dp + sizeof(*frag); - fin->fin_dlen -= sizeof(*frag); + /* + * Jumbograms aren't handled, so the max. length is 64k + */ + if ((fin->fin_off << 3) + fin->fin_dlen > 65535) + fin->fin_flx |= FI_BAD; + /* + * We don't know where the transport layer header (or whatever is next + * is), as it could be behind destination options (amongst others) so + * return the fragment header as the type of packet this is. Note that + * this effectively disables the fragment cache for > 1 protocol at a + * time. + */ return frag->ip6f_nxt; } /* ------------------------------------------------------------------------ */ -/* Function: frpr_dstopts6 */ +/* Function: ipf_pr_dstopts6 */ /* Returns: int - value of the next header or IPPROTO_NONE if error */ /* Parameters: fin(I) - pointer to packet information */ -/* nextheader(I) - stores next header value */ /* */ /* IPv6 Only */ /* This is function checks pending destination options extension header */ /* ------------------------------------------------------------------------ */ -static INLINE int frpr_dstopts6(fin) -fr_info_t *fin; +static INLINE int +ipf_pr_dstopts6(fin) + fr_info_t *fin; { - return frpr_ipv6exthdr(fin, 1, IPPROTO_DSTOPTS); + ipf_main_softc_t *softc = fin->fin_main_soft; + struct ip6_ext *hdr; + + hdr = ipf_pr_ipv6exthdr(fin, 0, IPPROTO_DSTOPTS); + if (hdr == NULL) { + LBUMPD(ipf_stats[fin->fin_out], fr_v6_dst_bad); + return IPPROTO_NONE; + } + return hdr->ip6e_nxt; } /* ------------------------------------------------------------------------ */ -/* Function: frpr_icmp6 */ +/* Function: ipf_pr_icmp6 */ /* Returns: void */ /* Parameters: fin(I) - pointer to packet information */ /* */ @@ -728,14 +912,19 @@ fr_info_t *fin; /* This routine is mainly concerned with determining the minimum valid size */ /* for an ICMPv6 packet. */ /* ------------------------------------------------------------------------ */ -static INLINE void frpr_icmp6(fin) -fr_info_t *fin; +static INLINE void +ipf_pr_icmp6(fin) + fr_info_t *fin; { int minicmpsz = sizeof(struct icmp6_hdr); struct icmp6_hdr *icmp6; - if (frpr_pullup(fin, ICMP6ERR_MINPKTLEN - sizeof(ip6_t)) == -1) + if (ipf_pr_pullup(fin, ICMP6ERR_MINPKTLEN - sizeof(ip6_t)) == -1) { + ipf_main_softc_t *softc = fin->fin_main_soft; + + LBUMPD(ipf_stats[fin->fin_out], fr_v6_icmp6_pullup); return; + } if (fin->fin_dlen > 1) { ip6_t *ip6; @@ -744,12 +933,18 @@ fr_info_t *fin; fin->fin_data[0] = *(u_short *)icmp6; + if ((icmp6->icmp6_type & ICMP6_INFOMSG_MASK) != 0) + fin->fin_flx |= FI_ICMPQUERY; + switch (icmp6->icmp6_type) { case ICMP6_ECHO_REPLY : case ICMP6_ECHO_REQUEST : + if (fin->fin_dlen >= 6) + fin->fin_data[1] = icmp6->icmp6_id; minicmpsz = ICMP6ERR_MINPKTLEN - sizeof(ip6_t); break; + case ICMP6_DST_UNREACH : case ICMP6_PACKET_TOO_BIG : case ICMP6_TIME_EXCEEDED : @@ -760,10 +955,13 @@ fr_info_t *fin; break; if (M_LEN(fin->fin_m) < fin->fin_plen) { - if (fr_coalesce(fin) != 1) + if (ipf_coalesce(fin) != 1) return; } + if (ipf_pr_pullup(fin, ICMP6ERR_MINPKTLEN) == -1) + return; + /* * If the destination of this packet doesn't match the * source of the original packet then this packet is @@ -774,19 +972,25 @@ fr_info_t *fin; if (IP6_NEQ(&fin->fin_fi.fi_dst, (i6addr_t *)&ip6->ip6_src)) fin->fin_flx |= FI_BAD; - break; default : break; } } - frpr_short6(fin, minicmpsz); + ipf_pr_short6(fin, minicmpsz); + if ((fin->fin_flx & (FI_SHORT|FI_BAD)) == 0) { + u_char p = fin->fin_p; + + fin->fin_p = IPPROTO_ICMPV6; + ipf_checkv6sum(fin); + fin->fin_p = p; + } } /* ------------------------------------------------------------------------ */ -/* Function: frpr_udp6 */ +/* Function: ipf_pr_udp6 */ /* Returns: void */ /* Parameters: fin(I) - pointer to packet information */ /* */ @@ -794,24 +998,23 @@ fr_info_t *fin; /* Analyse the packet for IPv6/UDP properties. */ /* Is not expected to be called for fragmented packets. */ /* ------------------------------------------------------------------------ */ -static INLINE void frpr_udp6(fin) -fr_info_t *fin; +static INLINE void +ipf_pr_udp6(fin) + fr_info_t *fin; { - frpr_short6(fin, sizeof(struct udphdr)); - - if (frpr_udpcommon(fin) == 0) { + if (ipf_pr_udpcommon(fin) == 0) { u_char p = fin->fin_p; fin->fin_p = IPPROTO_UDP; - fr_checkv6sum(fin); + ipf_checkv6sum(fin); fin->fin_p = p; } } /* ------------------------------------------------------------------------ */ -/* Function: frpr_tcp6 */ +/* Function: ipf_pr_tcp6 */ /* Returns: void */ /* Parameters: fin(I) - pointer to packet information */ /* */ @@ -819,24 +1022,23 @@ fr_info_t *fin; /* Analyse the packet for IPv6/TCP properties. */ /* Is not expected to be called for fragmented packets. */ /* ------------------------------------------------------------------------ */ -static INLINE void frpr_tcp6(fin) -fr_info_t *fin; +static INLINE void +ipf_pr_tcp6(fin) + fr_info_t *fin; { - frpr_short6(fin, sizeof(struct tcphdr)); - - if (frpr_tcpcommon(fin) == 0) { + if (ipf_pr_tcpcommon(fin) == 0) { u_char p = fin->fin_p; fin->fin_p = IPPROTO_TCP; - fr_checkv6sum(fin); + ipf_checkv6sum(fin); fin->fin_p = p; } } /* ------------------------------------------------------------------------ */ -/* Function: frpr_esp6 */ +/* Function: ipf_pr_esp6 */ /* Returns: void */ /* Parameters: fin(I) - pointer to packet information */ /* */ @@ -847,19 +1049,23 @@ fr_info_t *fin; /* is 32bits as well, it is not possible(?) to determine the version from a */ /* simple packet header. */ /* ------------------------------------------------------------------------ */ -static INLINE void frpr_esp6(fin) -fr_info_t *fin; +static INLINE void +ipf_pr_esp6(fin) + fr_info_t *fin; { - frpr_short6(fin, sizeof(grehdr_t)); + if ((fin->fin_off == 0) && (ipf_pr_pullup(fin, 8) == -1)) { + ipf_main_softc_t *softc = fin->fin_main_soft; - (void) frpr_pullup(fin, 8); + LBUMPD(ipf_stats[fin->fin_out], fr_v6_esp_pullup); + return; + } } /* ------------------------------------------------------------------------ */ -/* Function: frpr_ah6 */ -/* Returns: void */ +/* Function: ipf_pr_ah6 */ +/* Returns: int - value of the next header or IPPROTO_NONE if error */ /* Parameters: fin(I) - pointer to packet information */ /* */ /* IPv6 Only */ @@ -867,37 +1073,51 @@ fr_info_t *fin; /* The minimum length is taken to be the combination of all fields in the */ /* header being present and no authentication data (null algorithm used.) */ /* ------------------------------------------------------------------------ */ -static INLINE int frpr_ah6(fin) -fr_info_t *fin; +static INLINE int +ipf_pr_ah6(fin) + fr_info_t *fin; { authhdr_t *ah; - frpr_short6(fin, 12); + fin->fin_flx |= FI_AH; - if (frpr_pullup(fin, sizeof(*ah)) == -1) + ah = (authhdr_t *)ipf_pr_ipv6exthdr(fin, 0, IPPROTO_HOPOPTS); + if (ah == NULL) { + ipf_main_softc_t *softc = fin->fin_main_soft; + + LBUMPD(ipf_stats[fin->fin_out], fr_v6_ah_bad); return IPPROTO_NONE; + } - ah = (authhdr_t *)fin->fin_dp; + ipf_pr_short6(fin, sizeof(*ah)); + + /* + * No need for another pullup, ipf_pr_ipv6exthdr() will pullup + * enough data to satisfy ah_next (the very first one.) + */ return ah->ah_next; } /* ------------------------------------------------------------------------ */ -/* Function: frpr_gre6 */ +/* Function: ipf_pr_gre6 */ /* Returns: void */ /* Parameters: fin(I) - pointer to packet information */ /* */ /* Analyse the packet for GRE properties. */ /* ------------------------------------------------------------------------ */ -static INLINE void frpr_gre6(fin) -fr_info_t *fin; +static INLINE void +ipf_pr_gre6(fin) + fr_info_t *fin; { grehdr_t *gre; - frpr_short6(fin, sizeof(grehdr_t)); + if (ipf_pr_pullup(fin, sizeof(grehdr_t)) == -1) { + ipf_main_softc_t *softc = fin->fin_main_soft; - if (frpr_pullup(fin, sizeof(grehdr_t)) == -1) + LBUMPD(ipf_stats[fin->fin_out], fr_v6_gre_pullup); return; + } gre = fin->fin_dp; if (GRE_REV(gre->gr_flags) == 1) @@ -907,13 +1127,13 @@ fr_info_t *fin; /* ------------------------------------------------------------------------ */ -/* Function: frpr_pullup */ +/* Function: ipf_pr_pullup */ /* Returns: int - 0 == pullup succeeded, -1 == failure */ /* Parameters: fin(I) - pointer to packet information */ /* plen(I) - length (excluding L3 header) to pullup */ /* */ /* Short inline function to cut down on code duplication to perform a call */ -/* to fr_pullup to ensure there is the required amount of data, */ +/* to ipf_pullup to ensure there is the required amount of data, */ /* consecutively in the packet buffer. */ /* */ /* This function pulls up 'extra' data at the location of fin_dp. fin_dp */ @@ -924,23 +1144,32 @@ fr_info_t *fin; /* is necessary to add those we can already assume to be pulled up (fin_dp */ /* - fin_ip) to what is passed through. */ /* ------------------------------------------------------------------------ */ -static INLINE int frpr_pullup(fin, plen) -fr_info_t *fin; -int plen; +int +ipf_pr_pullup(fin, plen) + fr_info_t *fin; + int plen; { + ipf_main_softc_t *softc = fin->fin_main_soft; + if (fin->fin_m != NULL) { if (fin->fin_dp != NULL) plen += (char *)fin->fin_dp - ((char *)fin->fin_ip + fin->fin_hlen); plen += fin->fin_hlen; - if (M_LEN(fin->fin_m) < plen) { + if (M_LEN(fin->fin_m) < plen + fin->fin_ipoff) { #if defined(_KERNEL) - if (fr_pullup(fin->fin_m, fin, plen) == NULL) + if (ipf_pullup(fin->fin_m, fin, plen) == NULL) { + DT(ipf_pullup_fail); + LBUMP(ipf_stats[fin->fin_out].fr_pull[1]); return -1; + } + LBUMP(ipf_stats[fin->fin_out].fr_pull[0]); #else + LBUMP(ipf_stats[fin->fin_out].fr_pull[1]); /* - * Fake fr_pullup failing + * Fake ipf_pullup failing */ + fin->fin_reason = FRB_PULLUP; *fin->fin_mp = NULL; fin->fin_m = NULL; fin->fin_ip = NULL; @@ -953,7 +1182,7 @@ int plen; /* ------------------------------------------------------------------------ */ -/* Function: frpr_short */ +/* Function: ipf_pr_short */ /* Returns: void */ /* Parameters: fin(I) - pointer to packet information */ /* xmin(I) - minimum header size */ @@ -964,9 +1193,10 @@ int plen; /* start within the layer 4 header (hdrmin) or if it is at offset 0, the */ /* entire layer 4 header must be present (min). */ /* ------------------------------------------------------------------------ */ -static INLINE void frpr_short(fin, xmin) -fr_info_t *fin; -int xmin; +static INLINE void +ipf_pr_short(fin, xmin) + fr_info_t *fin; + int xmin; { if (fin->fin_off == 0) { @@ -979,7 +1209,7 @@ int xmin; /* ------------------------------------------------------------------------ */ -/* Function: frpr_icmp */ +/* Function: ipf_pr_icmp */ /* Returns: void */ /* Parameters: fin(I) - pointer to packet information */ /* */ @@ -991,110 +1221,111 @@ int xmin; /* */ /* XXX - other ICMP sanity checks? */ /* ------------------------------------------------------------------------ */ -static INLINE void frpr_icmp(fin) -fr_info_t *fin; +static INLINE void +ipf_pr_icmp(fin) + fr_info_t *fin; { + ipf_main_softc_t *softc = fin->fin_main_soft; int minicmpsz = sizeof(struct icmp); icmphdr_t *icmp; ip_t *oip; + ipf_pr_short(fin, ICMPERR_ICMPHLEN); + if (fin->fin_off != 0) { - frpr_short(fin, ICMPERR_ICMPHLEN); + LBUMPD(ipf_stats[fin->fin_out], fr_v4_icmp_frag); return; } - if (frpr_pullup(fin, ICMPERR_ICMPHLEN) == -1) + if (ipf_pr_pullup(fin, ICMPERR_ICMPHLEN) == -1) { + LBUMPD(ipf_stats[fin->fin_out], fr_v4_icmp_pullup); return; + } - if (fin->fin_dlen > 1) { - icmp = fin->fin_dp; + icmp = fin->fin_dp; - fin->fin_data[0] = *(u_short *)icmp; + fin->fin_data[0] = *(u_short *)icmp; + fin->fin_data[1] = icmp->icmp_id; - if (fin->fin_dlen >= 6) /* ID field */ - fin->fin_data[1] = icmp->icmp_id; - - switch (icmp->icmp_type) - { - case ICMP_ECHOREPLY : - case ICMP_ECHO : - /* Router discovery messaes - RFC 1256 */ - case ICMP_ROUTERADVERT : - case ICMP_ROUTERSOLICIT : - minicmpsz = ICMP_MINLEN; - break; - /* - * type(1) + code(1) + cksum(2) + id(2) seq(2) + - * 3 * timestamp(3 * 4) - */ - case ICMP_TSTAMP : - case ICMP_TSTAMPREPLY : - minicmpsz = 20; - break; - /* - * type(1) + code(1) + cksum(2) + id(2) seq(2) + - * mask(4) - */ - case ICMP_MASKREQ : - case ICMP_MASKREPLY : - minicmpsz = 12; - break; - /* - * type(1) + code(1) + cksum(2) + id(2) seq(2) + ip(20+) - */ - case ICMP_UNREACH : + switch (icmp->icmp_type) + { + case ICMP_ECHOREPLY : + case ICMP_ECHO : + /* Router discovery messaes - RFC 1256 */ + case ICMP_ROUTERADVERT : + case ICMP_ROUTERSOLICIT : + fin->fin_flx |= FI_ICMPQUERY; + minicmpsz = ICMP_MINLEN; + break; + /* + * type(1) + code(1) + cksum(2) + id(2) seq(2) + + * 3 * timestamp(3 * 4) + */ + case ICMP_TSTAMP : + case ICMP_TSTAMPREPLY : + fin->fin_flx |= FI_ICMPQUERY; + minicmpsz = 20; + break; + /* + * type(1) + code(1) + cksum(2) + id(2) seq(2) + + * mask(4) + */ + case ICMP_IREQ : + case ICMP_IREQREPLY : + case ICMP_MASKREQ : + case ICMP_MASKREPLY : + fin->fin_flx |= FI_ICMPQUERY; + minicmpsz = 12; + break; + /* + * type(1) + code(1) + cksum(2) + id(2) seq(2) + ip(20+) + */ + case ICMP_UNREACH : #ifdef icmp_nextmtu - if (icmp->icmp_code == ICMP_UNREACH_NEEDFRAG) { - if (icmp->icmp_nextmtu < fr_icmpminfragmtu) - fin->fin_flx |= FI_BAD; - } -#endif - case ICMP_SOURCEQUENCH : - case ICMP_REDIRECT : - case ICMP_TIMXCEED : - case ICMP_PARAMPROB : - fin->fin_flx |= FI_ICMPERR; - if (fr_coalesce(fin) != 1) - return; - /* - * ICMP error packets should not be generated for IP - * packets that are a fragment that isn't the first - * fragment. - */ - oip = (ip_t *)((char *)fin->fin_dp + ICMPERR_ICMPHLEN); - if ((ntohs(oip->ip_off) & IP_OFFMASK) != 0) + if (icmp->icmp_code == ICMP_UNREACH_NEEDFRAG) { + if (icmp->icmp_nextmtu < softc->ipf_icmpminfragmtu) fin->fin_flx |= FI_BAD; - - /* - * If the destination of this packet doesn't match the - * source of the original packet then this packet is - * not correct. - */ - if (oip->ip_src.s_addr != fin->fin_daddr) - fin->fin_flx |= FI_BAD; - - /* - * If the destination of this packet doesn't match the - * source of the original packet then this packet is - * not correct. - */ - if (oip->ip_src.s_addr != fin->fin_daddr) - fin->fin_flx |= FI_BAD; - break; - default : - break; } +#endif + case ICMP_SOURCEQUENCH : + case ICMP_REDIRECT : + case ICMP_TIMXCEED : + case ICMP_PARAMPROB : + fin->fin_flx |= FI_ICMPERR; + if (ipf_coalesce(fin) != 1) { + LBUMPD(ipf_stats[fin->fin_out], fr_icmp_coalesce); + return; + } + + /* + * ICMP error packets should not be generated for IP + * packets that are a fragment that isn't the first + * fragment. + */ + oip = (ip_t *)((char *)fin->fin_dp + ICMPERR_ICMPHLEN); + if ((ntohs(oip->ip_off) & IP_OFFMASK) != 0) + fin->fin_flx |= FI_BAD; + + /* + * If the destination of this packet doesn't match the + * source of the original packet then this packet is + * not correct. + */ + if (oip->ip_src.s_addr != fin->fin_daddr) + fin->fin_flx |= FI_BAD; + break; + default : + break; } - frpr_short(fin, minicmpsz); + ipf_pr_short(fin, minicmpsz); - if ((fin->fin_flx & FI_FRAG) == 0) - fr_checkv4sum(fin); + ipf_checkv4sum(fin); } /* ------------------------------------------------------------------------ */ -/* Function: frpr_tcpcommon */ +/* Function: ipf_pr_tcpcommon */ /* Returns: int - 0 = header ok, 1 = bad packet, -1 = buffer error */ /* Parameters: fin(I) - pointer to packet information */ /* */ @@ -1103,27 +1334,35 @@ fr_info_t *fin; /* If compiled with IPFILTER_CKSUM, check to see if the TCP checksum is */ /* valid and mark the packet as bad if not. */ /* ------------------------------------------------------------------------ */ -static INLINE int frpr_tcpcommon(fin) -fr_info_t *fin; +static INLINE int +ipf_pr_tcpcommon(fin) + fr_info_t *fin; { + ipf_main_softc_t *softc = fin->fin_main_soft; int flags, tlen; tcphdr_t *tcp; fin->fin_flx |= FI_TCPUDP; - if (fin->fin_off != 0) + if (fin->fin_off != 0) { + LBUMPD(ipf_stats[fin->fin_out], fr_tcp_frag); return 0; + } - if (frpr_pullup(fin, sizeof(*tcp)) == -1) + if (ipf_pr_pullup(fin, sizeof(*tcp)) == -1) { + LBUMPD(ipf_stats[fin->fin_out], fr_tcp_pullup); return -1; - tcp = fin->fin_dp; + } + tcp = fin->fin_dp; if (fin->fin_dlen > 3) { fin->fin_sport = ntohs(tcp->th_sport); fin->fin_dport = ntohs(tcp->th_dport); } - if ((fin->fin_flx & FI_SHORT) != 0) + if ((fin->fin_flx & FI_SHORT) != 0) { + LBUMPD(ipf_stats[fin->fin_out], fr_tcp_short); return 1; + } /* * Use of the TCP data offset *must* result in a value that is at @@ -1131,6 +1370,7 @@ fr_info_t *fin; */ tlen = TCP_OFF(tcp) << 2; if (tlen < sizeof(tcphdr_t)) { + LBUMPD(ipf_stats[fin->fin_out], fr_tcp_small); fin->fin_flx |= FI_BAD; return 1; } @@ -1189,6 +1429,10 @@ fr_info_t *fin; fin->fin_flx |= FI_BAD; } } + if (fin->fin_flx & FI_BAD) { + LBUMPD(ipf_stats[fin->fin_out], fr_tcp_bad_flags); + return 1; + } /* * At this point, it's not exactly clear what is to be gained by @@ -1198,11 +1442,14 @@ fr_info_t *fin; * Now if we were to analyse the header for passive fingerprinting, * then that might add some weight to adding this... */ - if (tlen == sizeof(tcphdr_t)) + if (tlen == sizeof(tcphdr_t)) { return 0; + } - if (frpr_pullup(fin, tlen) == -1) + if (ipf_pr_pullup(fin, tlen) == -1) { + LBUMPD(ipf_stats[fin->fin_out], fr_tcp_pullup); return -1; + } #if 0 tcp = fin->fin_dp; @@ -1249,23 +1496,27 @@ fr_info_t *fin; /* ------------------------------------------------------------------------ */ -/* Function: frpr_udpcommon */ +/* Function: ipf_pr_udpcommon */ /* Returns: int - 0 = header ok, 1 = bad packet */ /* Parameters: fin(I) - pointer to packet information */ /* */ /* Extract the UDP source and destination ports, if present. If compiled */ /* with IPFILTER_CKSUM, check to see if the UDP checksum is valid. */ /* ------------------------------------------------------------------------ */ -static INLINE int frpr_udpcommon(fin) -fr_info_t *fin; +static INLINE int +ipf_pr_udpcommon(fin) + fr_info_t *fin; { udphdr_t *udp; fin->fin_flx |= FI_TCPUDP; if (!fin->fin_off && (fin->fin_dlen > 3)) { - if (frpr_pullup(fin, sizeof(*udp)) == -1) { + if (ipf_pr_pullup(fin, sizeof(*udp)) == -1) { + ipf_main_softc_t *softc = fin->fin_main_soft; + fin->fin_flx |= FI_SHORT; + LBUMPD(ipf_stats[fin->fin_out], fr_udp_pullup); return 1; } @@ -1280,49 +1531,47 @@ fr_info_t *fin; /* ------------------------------------------------------------------------ */ -/* Function: frpr_tcp */ +/* Function: ipf_pr_tcp */ /* Returns: void */ /* Parameters: fin(I) - pointer to packet information */ /* */ /* IPv4 Only */ /* Analyse the packet for IPv4/TCP properties. */ /* ------------------------------------------------------------------------ */ -static INLINE void frpr_tcp(fin) -fr_info_t *fin; +static INLINE void +ipf_pr_tcp(fin) + fr_info_t *fin; { - frpr_short(fin, sizeof(tcphdr_t)); + ipf_pr_short(fin, sizeof(tcphdr_t)); - if (frpr_tcpcommon(fin) == 0) { - if ((fin->fin_flx & FI_FRAG) == 0) - fr_checkv4sum(fin); - } + if (ipf_pr_tcpcommon(fin) == 0) + ipf_checkv4sum(fin); } /* ------------------------------------------------------------------------ */ -/* Function: frpr_udp */ +/* Function: ipf_pr_udp */ /* Returns: void */ /* Parameters: fin(I) - pointer to packet information */ /* */ /* IPv4 Only */ /* Analyse the packet for IPv4/UDP properties. */ /* ------------------------------------------------------------------------ */ -static INLINE void frpr_udp(fin) -fr_info_t *fin; +static INLINE void +ipf_pr_udp(fin) + fr_info_t *fin; { - frpr_short(fin, sizeof(udphdr_t)); + ipf_pr_short(fin, sizeof(udphdr_t)); - if (frpr_udpcommon(fin) == 0) { - if ((fin->fin_flx & FI_FRAG) == 0) - fr_checkv4sum(fin); - } + if (ipf_pr_udpcommon(fin) == 0) + ipf_checkv4sum(fin); } /* ------------------------------------------------------------------------ */ -/* Function: frpr_esp */ +/* Function: ipf_pr_esp */ /* Returns: void */ /* Parameters: fin(I) - pointer to packet information */ /* */ @@ -1332,78 +1581,107 @@ fr_info_t *fin; /* is 32bits as well, it is not possible(?) to determine the version from a */ /* simple packet header. */ /* ------------------------------------------------------------------------ */ -static INLINE void frpr_esp(fin) -fr_info_t *fin; +static INLINE void +ipf_pr_esp(fin) + fr_info_t *fin; { if (fin->fin_off == 0) { - frpr_short(fin, 8); - (void) frpr_pullup(fin, 8); - } + ipf_pr_short(fin, 8); + if (ipf_pr_pullup(fin, 8) == -1) { + ipf_main_softc_t *softc = fin->fin_main_soft; + LBUMPD(ipf_stats[fin->fin_out], fr_v4_esp_pullup); + } + } } /* ------------------------------------------------------------------------ */ -/* Function: frpr_ah */ -/* Returns: void */ +/* Function: ipf_pr_ah */ +/* Returns: int - value of the next header or IPPROTO_NONE if error */ /* Parameters: fin(I) - pointer to packet information */ /* */ /* Analyse the packet for AH properties. */ /* The minimum length is taken to be the combination of all fields in the */ /* header being present and no authentication data (null algorithm used.) */ /* ------------------------------------------------------------------------ */ -static INLINE void frpr_ah(fin) -fr_info_t *fin; +static INLINE int +ipf_pr_ah(fin) + fr_info_t *fin; { + ipf_main_softc_t *softc = fin->fin_main_soft; authhdr_t *ah; int len; - frpr_short(fin, sizeof(*ah)); + fin->fin_flx |= FI_AH; + ipf_pr_short(fin, sizeof(*ah)); - if (((fin->fin_flx & FI_SHORT) != 0) || (fin->fin_off != 0)) - return; + if (((fin->fin_flx & FI_SHORT) != 0) || (fin->fin_off != 0)) { + LBUMPD(ipf_stats[fin->fin_out], fr_v4_ah_bad); + return IPPROTO_NONE; + } - if (frpr_pullup(fin, sizeof(*ah)) == -1) - return; + if (ipf_pr_pullup(fin, sizeof(*ah)) == -1) { + DT(fr_v4_ah_pullup_1); + LBUMP(ipf_stats[fin->fin_out].fr_v4_ah_pullup); + return IPPROTO_NONE; + } ah = (authhdr_t *)fin->fin_dp; len = (ah->ah_plen + 2) << 2; - frpr_short(fin, len); + ipf_pr_short(fin, len); + if (ipf_pr_pullup(fin, len) == -1) { + DT(fr_v4_ah_pullup_2); + LBUMP(ipf_stats[fin->fin_out].fr_v4_ah_pullup); + return IPPROTO_NONE; + } + + /* + * Adjust fin_dp and fin_dlen for skipping over the authentication + * header. + */ + fin->fin_dp = (char *)fin->fin_dp + len; + fin->fin_dlen -= len; + return ah->ah_next; } /* ------------------------------------------------------------------------ */ -/* Function: frpr_gre */ +/* Function: ipf_pr_gre */ /* Returns: void */ /* Parameters: fin(I) - pointer to packet information */ /* */ /* Analyse the packet for GRE properties. */ /* ------------------------------------------------------------------------ */ -static INLINE void frpr_gre(fin) -fr_info_t *fin; +static INLINE void +ipf_pr_gre(fin) + fr_info_t *fin; { + ipf_main_softc_t *softc = fin->fin_main_soft; grehdr_t *gre; - frpr_short(fin, sizeof(*gre)); + ipf_pr_short(fin, sizeof(grehdr_t)); - if (fin->fin_off != 0) + if (fin->fin_off != 0) { + LBUMPD(ipf_stats[fin->fin_out], fr_v4_gre_frag); return; - - if (frpr_pullup(fin, sizeof(*gre)) == -1) - return; - - if (fin->fin_off == 0) { - gre = fin->fin_dp; - if (GRE_REV(gre->gr_flags) == 1) - fin->fin_data[0] = gre->gr_call; } + + if (ipf_pr_pullup(fin, sizeof(grehdr_t)) == -1) { + LBUMPD(ipf_stats[fin->fin_out], fr_v4_gre_pullup); + return; + } + + gre = fin->fin_dp; + if (GRE_REV(gre->gr_flags) == 1) + fin->fin_data[0] = gre->gr_call; } /* ------------------------------------------------------------------------ */ -/* Function: frpr_ipv4hdr */ +/* Function: ipf_pr_ipv4hdr */ /* Returns: void */ /* Parameters: fin(I) - pointer to packet information */ /* */ @@ -1411,8 +1689,9 @@ fr_info_t *fin; /* Analyze the IPv4 header and set fields in the fr_info_t structure. */ /* Check all options present and flag their presence if any exist. */ /* ------------------------------------------------------------------------ */ -static INLINE void frpr_ipv4hdr(fin) -fr_info_t *fin; +static INLINE void +ipf_pr_ipv4hdr(fin) + fr_info_t *fin; { u_short optmsk = 0, secmsk = 0, auth = 0; int hlen, ol, mv, p, i; @@ -1428,16 +1707,14 @@ fr_info_t *fin; ip = fin->fin_ip; p = ip->ip_p; fi->fi_p = p; + fin->fin_crc = p; fi->fi_tos = ip->ip_tos; fin->fin_id = ip->ip_id; - off = ip->ip_off; + off = ntohs(ip->ip_off); /* Get both TTL and protocol */ fi->fi_p = ip->ip_p; fi->fi_ttl = ip->ip_ttl; -#if 0 - (*(((u_short *)fi) + 1)) = (*(((u_short *)ip) + 4)); -#endif /* Zero out bits not used in IPv6 address */ fi->fi_src.i6[1] = 0; @@ -1448,7 +1725,11 @@ fr_info_t *fin; fi->fi_dst.i6[3] = 0; fi->fi_saddr = ip->ip_src.s_addr; + fin->fin_crc += fi->fi_saddr; fi->fi_daddr = ip->ip_dst.s_addr; + fin->fin_crc += fi->fi_daddr; + if (IN_CLASSD(ntohl(fi->fi_daddr))) + fin->fin_flx |= FI_MULTICAST|FI_MBCAST; /* * set packet attribute flags based on the offset and @@ -1463,12 +1744,12 @@ fr_info_t *fin; if (off != 0) { fin->fin_flx |= FI_FRAGBODY; off <<= 3; - if ((off + fin->fin_dlen > 65535) || + if ((off + fin->fin_dlen > 65535) || (fin->fin_dlen == 0) || ((morefrag != 0) && ((fin->fin_dlen & 7) != 0))) { - /* + /* * The length of the packet, starting at its - * offset cannot exceed 65535 (0xffff) as the + * offset cannot exceed 65535 (0xffff) as the * length of an IP packet is only 16 bits. * * Any fragment that isn't the last fragment @@ -1484,25 +1765,30 @@ fr_info_t *fin; /* * Call per-protocol setup and checking */ + if (p == IPPROTO_AH) { + /* + * Treat AH differently because we expect there to be another + * layer 4 header after it. + */ + p = ipf_pr_ah(fin); + } + switch (p) { case IPPROTO_UDP : - frpr_udp(fin); + ipf_pr_udp(fin); break; case IPPROTO_TCP : - frpr_tcp(fin); + ipf_pr_tcp(fin); break; case IPPROTO_ICMP : - frpr_icmp(fin); - break; - case IPPROTO_AH : - frpr_ah(fin); + ipf_pr_icmp(fin); break; case IPPROTO_ESP : - frpr_esp(fin); + ipf_pr_esp(fin); break; case IPPROTO_GRE : - frpr_gre(fin); + ipf_pr_gre(fin); break; } @@ -1545,32 +1831,37 @@ fr_info_t *fin; } for (i = 9, mv = 4; mv >= 0; ) { op = ipopts + i; - if ((opt == (u_char)op->ol_val) && (ol > 4)) { - optmsk |= op->ol_bit; - if (opt == IPOPT_SECURITY) { - const struct optlist *sp; - u_char sec; - int j, m; - sec = *(s + 2); /* classification */ - for (j = 3, m = 2; m >= 0; ) { - sp = secopt + j; - if (sec == sp->ol_val) { - secmsk |= sp->ol_bit; - auth = *(s + 3); - auth *= 256; - auth += *(s + 4); - break; - } - if (sec < sp->ol_val) - j -= m; - else - j += m; - m--; + if ((opt == (u_char)op->ol_val) && (ol > 4)) { + u_32_t doi; + + switch (opt) + { + case IPOPT_SECURITY : + if (optmsk & op->ol_bit) { + fin->fin_flx |= FI_BAD; + } else { + doi = ipf_checkripso(s); + secmsk = doi >> 16; + auth = doi & 0xffff; } + break; + + case IPOPT_CIPSO : + + if (optmsk & op->ol_bit) { + fin->fin_flx |= FI_BAD; + } else { + doi = ipf_checkcipso(fin, + s, ol); + secmsk = doi >> 16; + auth = doi & 0xffff; + } + break; } - break; + optmsk |= op->ol_bit; } + if (opt < op->ol_val) i -= mv; else @@ -1593,8 +1884,142 @@ fr_info_t *fin; /* ------------------------------------------------------------------------ */ -/* Function: fr_makefrip */ +/* Function: ipf_checkripso */ /* Returns: void */ +/* Parameters: s(I) - pointer to start of RIPSO option */ +/* */ +/* ------------------------------------------------------------------------ */ +static u_32_t +ipf_checkripso(s) + u_char *s; +{ + const struct optlist *sp; + u_short secmsk = 0, auth = 0; + u_char sec; + int j, m; + + sec = *(s + 2); /* classification */ + for (j = 3, m = 2; m >= 0; ) { + sp = secopt + j; + if (sec == sp->ol_val) { + secmsk |= sp->ol_bit; + auth = *(s + 3); + auth *= 256; + auth += *(s + 4); + break; + } + if (sec < sp->ol_val) + j -= m; + else + j += m; + m--; + } + + return (secmsk << 16) | auth; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_checkcipso */ +/* Returns: u_32_t - 0 = failure, else the doi from the header */ +/* Parameters: fin(IO) - pointer to packet information */ +/* s(I) - pointer to start of CIPSO option */ +/* ol(I) - length of CIPSO option field */ +/* */ +/* This function returns the domain of integrity (DOI) field from the CIPSO */ +/* header and returns that whilst also storing the highest sensitivity */ +/* value found in the fr_info_t structure. */ +/* */ +/* No attempt is made to extract the category bitmaps as these are defined */ +/* by the user (rather than the protocol) and can be rather numerous on the */ +/* end nodes. */ +/* ------------------------------------------------------------------------ */ +static u_32_t +ipf_checkcipso(fin, s, ol) + fr_info_t *fin; + u_char *s; + int ol; +{ + ipf_main_softc_t *softc = fin->fin_main_soft; + fr_ip_t *fi; + u_32_t doi; + u_char *t, tag, tlen, sensitivity; + int len; + + if (ol < 6 || ol > 40) { + LBUMPD(ipf_stats[fin->fin_out], fr_v4_cipso_bad); + fin->fin_flx |= FI_BAD; + return 0; + } + + fi = &fin->fin_fi; + fi->fi_sensitivity = 0; + /* + * The DOI field MUST be there. + */ + bcopy(s + 2, &doi, sizeof(doi)); + + t = (u_char *)s + 6; + for (len = ol - 6; len >= 2; len -= tlen, t+= tlen) { + tag = *t; + tlen = *(t + 1); + if (tlen > len || tlen < 4 || tlen > 34) { + LBUMPD(ipf_stats[fin->fin_out], fr_v4_cipso_tlen); + fin->fin_flx |= FI_BAD; + return 0; + } + + sensitivity = 0; + /* + * Tag numbers 0, 1, 2, 5 are laid out in the CIPSO Internet + * draft (16 July 1992) that has expired. + */ + if (tag == 0) { + fin->fin_flx |= FI_BAD; + continue; + } else if (tag == 1) { + if (*(t + 2) != 0) { + fin->fin_flx |= FI_BAD; + continue; + } + sensitivity = *(t + 3); + /* Category bitmap for categories 0-239 */ + + } else if (tag == 4) { + if (*(t + 2) != 0) { + fin->fin_flx |= FI_BAD; + continue; + } + sensitivity = *(t + 3); + /* Enumerated categories, 16bits each, upto 15 */ + + } else if (tag == 5) { + if (*(t + 2) != 0) { + fin->fin_flx |= FI_BAD; + continue; + } + sensitivity = *(t + 3); + /* Range of categories (2*16bits), up to 7 pairs */ + + } else if (tag > 127) { + /* Custom defined DOI */ + ; + } else { + fin->fin_flx |= FI_BAD; + continue; + } + + if (sensitivity > fi->fi_sensitivity) + fi->fi_sensitivity = sensitivity; + } + + return doi; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_makefrip */ +/* Returns: int - 0 == packet ok, -1 == packet freed */ /* Parameters: hlen(I) - length of IP packet header */ /* ip(I) - pointer to the IP header */ /* fin(IO) - pointer to packet information */ @@ -1604,15 +2029,15 @@ fr_info_t *fin; /* in the fr_info_t structure pointer to by fin. At present, it is assumed */ /* this function will be called with either an IPv4 or IPv6 packet. */ /* ------------------------------------------------------------------------ */ -int fr_makefrip(hlen, ip, fin) -int hlen; -ip_t *ip; -fr_info_t *fin; +int +ipf_makefrip(hlen, ip, fin) + int hlen; + ip_t *ip; + fr_info_t *fin; { + ipf_main_softc_t *softc = fin->fin_main_soft; int v; - fin->fin_nat = NULL; - fin->fin_state = NULL; fin->fin_depth = 0; fin->fin_hlen = (u_short)hlen; fin->fin_ip = ip; @@ -1623,43 +2048,43 @@ fr_info_t *fin; v = fin->fin_v; if (v == 4) { - fin->fin_plen = ip->ip_len; + fin->fin_plen = ntohs(ip->ip_len); fin->fin_dlen = fin->fin_plen - hlen; - - frpr_ipv4hdr(fin); + ipf_pr_ipv4hdr(fin); #ifdef USE_INET6 } else if (v == 6) { fin->fin_plen = ntohs(((ip6_t *)ip)->ip6_plen); fin->fin_dlen = fin->fin_plen; fin->fin_plen += hlen; - if (frpr_ipv6hdr(fin) == -1) - return -1; + ipf_pr_ipv6hdr(fin); #endif } - if (fin->fin_ip == NULL) + if (fin->fin_ip == NULL) { + LBUMP(ipf_stats[fin->fin_out].fr_ip_freed); return -1; + } return 0; } /* ------------------------------------------------------------------------ */ -/* Function: fr_portcheck */ +/* Function: ipf_portcheck */ /* Returns: int - 1 == port matched, 0 == port match failed */ /* Parameters: frp(I) - pointer to port check `expression' */ -/* pop(I) - pointer to port number to evaluate */ +/* pop(I) - port number to evaluate */ /* */ /* Perform a comparison of a port number against some other(s), using a */ /* structure with compare information stored in it. */ /* ------------------------------------------------------------------------ */ -static INLINE int fr_portcheck(frp, pop) -frpcmp_t *frp; -u_short *pop; +static INLINE int +ipf_portcheck(frp, pop) + frpcmp_t *frp; + u_32_t pop; { - u_short tup, po; int err = 1; + u_32_t po; - tup = *pop; po = frp->frp_port; /* @@ -1668,39 +2093,39 @@ u_short *pop; switch (frp->frp_cmp) { case FR_EQUAL : - if (tup != po) /* EQUAL */ + if (pop != po) /* EQUAL */ err = 0; break; case FR_NEQUAL : - if (tup == po) /* NOTEQUAL */ + if (pop == po) /* NOTEQUAL */ err = 0; break; case FR_LESST : - if (tup >= po) /* LESSTHAN */ + if (pop >= po) /* LESSTHAN */ err = 0; break; case FR_GREATERT : - if (tup <= po) /* GREATERTHAN */ + if (pop <= po) /* GREATERTHAN */ err = 0; break; case FR_LESSTE : - if (tup > po) /* LT or EQ */ + if (pop > po) /* LT or EQ */ err = 0; break; case FR_GREATERTE : - if (tup < po) /* GT or EQ */ + if (pop < po) /* GT or EQ */ err = 0; break; case FR_OUTRANGE : - if (tup >= po && tup <= frp->frp_top) /* Out of range */ + if (pop >= po && pop <= frp->frp_top) /* Out of range */ err = 0; break; case FR_INRANGE : - if (tup <= po || tup >= frp->frp_top) /* In range */ + if (pop <= po || pop >= frp->frp_top) /* In range */ err = 0; break; case FR_INCRANGE : - if (tup < po || tup > frp->frp_top) /* Inclusive range */ + if (pop < po || pop > frp->frp_top) /* Inclusive range */ err = 0; break; default : @@ -1711,17 +2136,18 @@ u_short *pop; /* ------------------------------------------------------------------------ */ -/* Function: fr_tcpudpchk */ +/* Function: ipf_tcpudpchk */ /* Returns: int - 1 == protocol matched, 0 == check failed */ -/* Parameters: fin(I) - pointer to packet information */ +/* Parameters: fda(I) - pointer to packet information */ /* ft(I) - pointer to structure with comparison data */ /* */ /* Compares the current pcket (assuming it is TCP/UDP) information with a */ /* structure containing information that we want to match against. */ /* ------------------------------------------------------------------------ */ -int fr_tcpudpchk(fin, ft) -fr_info_t *fin; -frtuc_t *ft; +int +ipf_tcpudpchk(fi, ft) + fr_ip_t *fi; + frtuc_t *ft; { int err = 1; @@ -1732,13 +2158,13 @@ frtuc_t *ft; * compare destination ports */ if (ft->ftu_dcmp) - err = fr_portcheck(&ft->ftu_dst, &fin->fin_dport); + err = ipf_portcheck(&ft->ftu_dst, fi->fi_ports[1]); /* * compare source ports */ if (err && ft->ftu_scmp) - err = fr_portcheck(&ft->ftu_src, &fin->fin_sport); + err = ipf_portcheck(&ft->ftu_src, fi->fi_ports[0]); /* * If we don't have all the TCP/UDP header, then how can we @@ -1746,15 +2172,15 @@ frtuc_t *ft; * TCP flags, then NO match. If not, then match (which should * satisfy the "short" class too). */ - if (err && (fin->fin_p == IPPROTO_TCP)) { - if (fin->fin_flx & FI_SHORT) + if (err && (fi->fi_p == IPPROTO_TCP)) { + if (fi->fi_flx & FI_SHORT) return !(ft->ftu_tcpf | ft->ftu_tcpfm); /* * Match the flags ? If not, abort this match. */ if (ft->ftu_tcpfm && - ft->ftu_tcpf != (fin->fin_tcpf & ft->ftu_tcpfm)) { - FR_DEBUG(("f. %#x & %#x != %#x\n", fin->fin_tcpf, + ft->ftu_tcpf != (fi->fi_tcpf & ft->ftu_tcpfm)) { + FR_DEBUG(("f. %#x & %#x != %#x\n", fi->fi_tcpf, ft->ftu_tcpfm, ft->ftu_tcpf)); err = 0; } @@ -1763,10 +2189,9 @@ frtuc_t *ft; } - /* ------------------------------------------------------------------------ */ -/* Function: fr_ipfcheck */ -/* Returns: int - 0 == match, 1 == no match */ +/* Function: ipf_check_ipf */ +/* Returns: int - 0 == match, else no match */ /* Parameters: fin(I) - pointer to packet information */ /* fr(I) - pointer to filter rule */ /* portcmp(I) - flag indicating whether to attempt matching on */ @@ -1776,10 +2201,11 @@ frtuc_t *ft; /* port numbers, etc, for "standard" IPFilter rules are all orchestrated in */ /* this function. */ /* ------------------------------------------------------------------------ */ -static INLINE int fr_ipfcheck(fin, fr, portcmp) -fr_info_t *fin; -frentry_t *fr; -int portcmp; +static INLINE int +ipf_check_ipf(fin, fr, portcmp) + fr_info_t *fin; + frentry_t *fr; + int portcmp; { u_32_t *ld, *lm, *lip; fripf_t *fri; @@ -1807,10 +2233,10 @@ int portcmp; * are present (if any) in this packet. */ lip++, lm++, ld++; - i |= ((*lip & *lm) != *ld); + i = ((*lip & *lm) != *ld); FR_DEBUG(("1. %#08x & %#08x != %#08x\n", ntohl(*lip), ntohl(*lm), ntohl(*ld))); - if (i) + if (i != 0) return 1; lip++, lm++, ld++; @@ -1820,16 +2246,15 @@ int portcmp; /* * Check the source address. */ -#ifdef IPFILTER_LOOKUP if (fr->fr_satype == FRI_LOOKUP) { - i = (*fr->fr_srcfunc)(fr->fr_srcptr, fi->fi_v, lip); + i = (*fr->fr_srcfunc)(fin->fin_main_soft, fr->fr_srcptr, + fi->fi_v, lip, fin->fin_plen); if (i == -1) return 1; lip += 3; lm += 3; ld += 3; } else { -#endif i = ((*lip & *lm) != *ld); FR_DEBUG(("2a. %#08x & %#08x != %#08x\n", ntohl(*lip), ntohl(*lm), ntohl(*ld))); @@ -1851,27 +2276,24 @@ int portcmp; lm += 3; ld += 3; } -#ifdef IPFILTER_LOOKUP } -#endif i ^= (fr->fr_flags & FR_NOTSRCIP) >> 6; - if (i) + if (i != 0) return 1; /* * Check the destination address. */ lip++, lm++, ld++; -#ifdef IPFILTER_LOOKUP if (fr->fr_datype == FRI_LOOKUP) { - i = (*fr->fr_dstfunc)(fr->fr_dstptr, fi->fi_v, lip); + i = (*fr->fr_dstfunc)(fin->fin_main_soft, fr->fr_dstptr, + fi->fi_v, lip, fin->fin_plen); if (i == -1) return 1; lip += 3; lm += 3; ld += 3; } else { -#endif i = ((*lip & *lm) != *ld); FR_DEBUG(("3a. %#08x & %#08x != %#08x\n", ntohl(*lip), ntohl(*lm), ntohl(*ld))); @@ -1893,28 +2315,24 @@ int portcmp; lm += 3; ld += 3; } -#ifdef IPFILTER_LOOKUP } -#endif i ^= (fr->fr_flags & FR_NOTDSTIP) >> 7; - if (i) + if (i != 0) return 1; /* * IP addresses matched. The next 32bits contains: * mast of old IP header security & authentication bits. */ lip++, lm++, ld++; - i |= ((*lip & *lm) != *ld); - FR_DEBUG(("4. %#08x & %#08x != %#08x\n", - *lip, *lm, *ld)); + i = (*ld - (*lip & *lm)); + FR_DEBUG(("4. %#08x & %#08x != %#08x\n", *lip, *lm, *ld)); /* * Next we have 32 bits of packet flags. */ lip++, lm++, ld++; - i |= ((*lip & *lm) != *ld); - FR_DEBUG(("5. %#08x & %#08x != %#08x\n", - *lip, *lm, *ld)); + i |= (*ld - (*lip & *lm)); + FR_DEBUG(("5. %#08x & %#08x != %#08x\n", *lip, *lm, *ld)); if (i == 0) { /* @@ -1922,7 +2340,7 @@ int portcmp; * looking for here... */ if (portcmp) { - if (!fr_tcpudpchk(fin, &fr->fr_tuc)) + if (!ipf_tcpudpchk(&fin->fin_fi, &fr->fr_tuc)) i = 1; } else { if (fr->fr_dcmp || fr->fr_scmp || @@ -1948,7 +2366,7 @@ int portcmp; /* ------------------------------------------------------------------------ */ -/* Function: fr_scanlist */ +/* Function: ipf_scanlist */ /* Returns: int - result flags of scanning filter list */ /* Parameters: fin(I) - pointer to packet information */ /* pass(I) - default result to return for filtering */ @@ -1963,10 +2381,12 @@ int portcmp; /* Could be per interface, but this gets real nasty when you don't have, */ /* or can't easily change, the kernel source code to . */ /* ------------------------------------------------------------------------ */ -int fr_scanlist(fin, pass) -fr_info_t *fin; -u_32_t pass; +int +ipf_scanlist(fin, pass) + fr_info_t *fin; + u_32_t pass; { + ipf_main_softc_t *softc = fin->fin_main_soft; int rulen, portcmp, off, skip; struct frentry *fr, *fnext; u_32_t passt, passo; @@ -1997,7 +2417,7 @@ u_32_t pass; for (rulen = 0; fr; fr = fnext, rulen++) { fnext = fr->fr_next; if (skip != 0) { - FR_VERBOSE(("%d (%#x)\n", skip, fr->fr_flags)); + FR_VERBOSE(("SKIP %d (%#x)\n", skip, fr->fr_flags)); skip--; continue; } @@ -2027,27 +2447,29 @@ u_32_t pass; switch (fr->fr_type) { case FR_T_IPF : - case FR_T_IPF|FR_T_BUILTIN : - if (fr_ipfcheck(fin, fr, portcmp)) + case FR_T_IPF_BUILTIN : + if (ipf_check_ipf(fin, fr, portcmp)) continue; break; #if defined(IPFILTER_BPF) case FR_T_BPFOPC : - case FR_T_BPFOPC|FR_T_BUILTIN : + case FR_T_BPFOPC_BUILTIN : { u_char *mc; + int wlen; if (*fin->fin_mp == NULL) continue; - if (fin->fin_v != fr->fr_v) + if (fin->fin_family != fr->fr_family) continue; mc = (u_char *)fin->fin_m; - if (!bpf_filter(fr->fr_data, mc, fin->fin_plen, 0)) + wlen = fin->fin_dlen + fin->fin_hlen; + if (!bpf_filter(fr->fr_data, mc, wlen, 0)) continue; break; } #endif - case FR_T_CALLFUNC|FR_T_BUILTIN : + case FR_T_CALLFUNC_BUILTIN : { frentry_t *f; @@ -2058,6 +2480,15 @@ u_32_t pass; continue; break; } + + case FR_T_IPFEXPR : + case FR_T_IPFEXPR_BUILTIN : + if (fin->fin_family != fr->fr_family) + continue; + if (ipf_fr_matcharray(fin, fr->fr_data) == 0) + continue; + break; + default : break; } @@ -2065,22 +2496,13 @@ u_32_t pass; if ((fin->fin_out == 0) && (fr->fr_nattag.ipt_num[0] != 0)) { if (fin->fin_nattag == NULL) continue; - if (fr_matchtag(&fr->fr_nattag, fin->fin_nattag) == 0) + if (ipf_matchtag(&fr->fr_nattag, fin->fin_nattag) == 0) continue; } - FR_VERBOSE(("=%s.%d *", fr->fr_group, rulen)); + FR_VERBOSE(("=%d/%d.%d *", fr->fr_grhead, fr->fr_group, rulen)); passt = fr->fr_flags; - /* - * Allowing a rule with the "keep state" flag set to match - * packets that have been tagged "out of window" by the TCP - * state tracking is foolish as the attempt to add a new - * state entry to the table will fail. - */ - if ((passt & FR_KEEPSTATE) && (fin->fin_flx & FI_OOW)) - continue; - /* * If the rule is a "call now" rule, then call the function * in the rule, if it exists and use the results from that. @@ -2091,7 +2513,7 @@ u_32_t pass; frentry_t *frs; ATOMIC_INC64(fr->fr_hits); - if ((fr->fr_func != NULL) && + if ((fr->fr_func == NULL) || (fr->fr_func == (ipfunc_t)-1)) continue; @@ -2111,43 +2533,69 @@ u_32_t pass; * Just log this packet... */ if ((passt & FR_LOGMASK) == FR_LOG) { - if (ipflog(fin, passt) == -1) { + if (ipf_log_pkt(fin, passt) == -1) { if (passt & FR_LOGORBLOCK) { + DT(frb_logfail); passt &= ~FR_CMDMASK; passt |= FR_BLOCK|FR_QUICK; + fin->fin_reason = FRB_LOGFAIL; } - ATOMIC_INCL(frstats[fin->fin_out].fr_skip); } - ATOMIC_INCL(frstats[fin->fin_out].fr_pkl); - fin->fin_flx |= FI_DONTCACHE; } #endif /* IPFILTER_LOG */ + + MUTEX_ENTER(&fr->fr_lock); fr->fr_bytes += (U_QUAD_T)fin->fin_plen; + fr->fr_hits++; + MUTEX_EXIT(&fr->fr_lock); + fin->fin_rule = rulen; + passo = pass; - if (FR_ISSKIP(passt)) + if (FR_ISSKIP(passt)) { skip = fr->fr_arg; - else if ((passt & FR_LOGMASK) != FR_LOG) + continue; + } else if (((passt & FR_LOGMASK) != FR_LOG) && + ((passt & FR_LOGMASK) != FR_DECAPSULATE)) { pass = passt; + } + if (passt & (FR_RETICMP|FR_FAKEICMP)) fin->fin_icode = fr->fr_icode; - FR_DEBUG(("pass %#x\n", pass)); - ATOMIC_INC64(fr->fr_hits); - fin->fin_rule = rulen; - (void) strncpy(fin->fin_group, fr->fr_group, FR_GROUPLEN); - if (fr->fr_grp != NULL) { - fin->fin_fr = *fr->fr_grp; - passt = fr_scanlist(fin, pass); + + if (fr->fr_group != -1) { + (void) strncpy(fin->fin_group, + FR_NAME(fr, fr_group), + strlen(FR_NAME(fr, fr_group))); + } else { + fin->fin_group[0] = '\0'; + } + + FR_DEBUG(("pass %#x/%#x/%x\n", passo, pass, passt)); + + if (fr->fr_grphead != NULL) { + fin->fin_fr = fr->fr_grphead->fg_start; + FR_VERBOSE(("group %s\n", FR_NAME(fr, fr_grhead))); + + if (FR_ISDECAPS(passt)) + passt = ipf_decaps(fin, pass, fr->fr_icode); + else + passt = ipf_scanlist(fin, pass); + if (fin->fin_fr == NULL) { fin->fin_rule = rulen; - (void) strncpy(fin->fin_group, fr->fr_group, - FR_GROUPLEN); + if (fr->fr_group != -1) + (void) strncpy(fin->fin_group, + fr->fr_names + + fr->fr_group, + strlen(fr->fr_names + + fr->fr_group)); fin->fin_fr = fr; passt = pass; } pass = passt; } - if (passt & FR_QUICK) { + if (pass & FR_QUICK) { /* * Finally, if we've asked to track state for this * packet, set it up. Add state for "quick" rules @@ -2155,15 +2603,15 @@ u_32_t pass; * the rule to "not match" and keep on processing * filter rules. */ - if ((pass & FR_KEEPSTATE) && + if ((pass & FR_KEEPSTATE) && !FR_ISAUTH(pass) && !(fin->fin_flx & FI_STATE)) { int out = fin->fin_out; fin->fin_fr = fr; - if (fr_addstate(fin, NULL, 0) != NULL) { - ATOMIC_INCL(frstats[out].fr_ads); + if (ipf_state_add(softc, fin, NULL, 0) == 0) { + LBUMPD(ipf_stats[out], fr_ads); } else { - ATOMIC_INCL(frstats[out].fr_bads); + LBUMPD(ipf_stats[out], fr_bads); pass = passo; continue; } @@ -2177,7 +2625,7 @@ u_32_t pass; /* ------------------------------------------------------------------------ */ -/* Function: fr_acctpkt */ +/* Function: ipf_acctpkt */ /* Returns: frentry_t* - always returns NULL */ /* Parameters: fin(I) - pointer to packet information */ /* passp(IO) - pointer to current/new filter decision (unused) */ @@ -2186,32 +2634,29 @@ u_32_t pass; /* IP protocol version. */ /* */ /* N.B.: this function returns NULL to match the prototype used by other */ -/* functions called from the IPFilter "mainline" in fr_check(). */ +/* functions called from the IPFilter "mainline" in ipf_check(). */ /* ------------------------------------------------------------------------ */ -frentry_t *fr_acctpkt(fin, passp) -fr_info_t *fin; -u_32_t *passp; +frentry_t * +ipf_acctpkt(fin, passp) + fr_info_t *fin; + u_32_t *passp; { + ipf_main_softc_t *softc = fin->fin_main_soft; char group[FR_GROUPLEN]; frentry_t *fr, *frsave; u_32_t pass, rulen; passp = passp; -#ifdef USE_INET6 - if (fin->fin_v == 6) - fr = ipacct6[fin->fin_out][fr_active]; - else -#endif - fr = ipacct[fin->fin_out][fr_active]; + fr = softc->ipf_acct[fin->fin_out][softc->ipf_active]; if (fr != NULL) { frsave = fin->fin_fr; bcopy(fin->fin_group, group, FR_GROUPLEN); rulen = fin->fin_rule; fin->fin_fr = fr; - pass = fr_scanlist(fin, FR_NOMATCH); + pass = ipf_scanlist(fin, FR_NOMATCH); if (FR_ISACCOUNT(pass)) { - ATOMIC_INCL(frstats[0].fr_acct); + LBUMPD(ipf_stats[0], fr_acct); } fin->fin_fr = frsave; bcopy(group, fin->fin_group, FR_GROUPLEN); @@ -2222,7 +2667,7 @@ u_32_t *passp; /* ------------------------------------------------------------------------ */ -/* Function: fr_firewall */ +/* Function: ipf_firewall */ /* Returns: frentry_t* - returns pointer to matched rule, if no matches */ /* were found, returns NULL. */ /* Parameters: fin(I) - pointer to packet information */ @@ -2234,12 +2679,13 @@ u_32_t *passp; /* matching rule is found, take any appropriate actions as defined by the */ /* rule - except logging. */ /* ------------------------------------------------------------------------ */ -static frentry_t *fr_firewall(fin, passp) -fr_info_t *fin; -u_32_t *passp; +static frentry_t * +ipf_firewall(fin, passp) + fr_info_t *fin; + u_32_t *passp; { + ipf_main_softc_t *softc = fin->fin_main_soft; frentry_t *fr; - fr_info_t *fc; u_32_t pass; int out; @@ -2247,56 +2693,28 @@ u_32_t *passp; pass = *passp; /* - * If a packet is found in the auth table, then skip checking - * the access lists for permission but we do need to consider - * the result as if it were from the ACL's. + * This rule cache will only affect packets that are not being + * statefully filtered. */ - fc = &frcache[out][CACHE_HASH(fin)]; - READ_ENTER(&ipf_frcache); - if (!bcmp((char *)fin, (char *)fc, FI_CSIZE)) { - /* - * copy cached data so we can unlock the mutexes earlier. - */ - bcopy((char *)fc, (char *)fin, FI_COPYSIZE); - RWLOCK_EXIT(&ipf_frcache); - ATOMIC_INCL(frstats[out].fr_chit); + fin->fin_fr = softc->ipf_rules[out][softc->ipf_active]; + if (fin->fin_fr != NULL) + pass = ipf_scanlist(fin, softc->ipf_pass); - if ((fr = fin->fin_fr) != NULL) { - ATOMIC_INC64(fr->fr_hits); - pass = fr->fr_flags; - } - } else { - RWLOCK_EXIT(&ipf_frcache); - -#ifdef USE_INET6 - if (fin->fin_v == 6) - fin->fin_fr = ipfilter6[out][fr_active]; - else -#endif - fin->fin_fr = ipfilter[out][fr_active]; - if (fin->fin_fr != NULL) - pass = fr_scanlist(fin, fr_pass); - - if (((pass & FR_KEEPSTATE) == 0) && - ((fin->fin_flx & FI_DONTCACHE) == 0)) { - WRITE_ENTER(&ipf_frcache); - bcopy((char *)fin, (char *)fc, FI_COPYSIZE); - RWLOCK_EXIT(&ipf_frcache); - } - if ((pass & FR_NOMATCH)) { - ATOMIC_INCL(frstats[out].fr_nom); - } - fr = fin->fin_fr; + if ((pass & FR_NOMATCH)) { + LBUMPD(ipf_stats[out], fr_nom); } + fr = fin->fin_fr; /* * Apply packets per second rate-limiting to a rule as required. */ if ((fr != NULL) && (fr->fr_pps != 0) && !ppsratecheck(&fr->fr_lastpkt, &fr->fr_curpps, fr->fr_pps)) { - pass &= ~(FR_CMDMASK|FR_DUP|FR_RETICMP|FR_RETRST); + DT2(frb_ppsrate, fr_info_t *, fin, frentry_t *, fr); + pass &= ~(FR_CMDMASK|FR_RETICMP|FR_RETRST); pass |= FR_BLOCK; - ATOMIC_INCL(frstats[out].fr_ppshit); + LBUMPD(ipf_stats[out], fr_ppshit); + fin->fin_reason = FRB_PPSRATE; } /* @@ -2305,15 +2723,15 @@ u_32_t *passp; * we've dropped it already. */ if (FR_ISAUTH(pass)) { - if (fr_newauth(fin->fin_m, fin) != 0) { -#ifdef _KERNEL + if (ipf_auth_new(fin->fin_m, fin) != 0) { + DT1(frb_authnew, fr_info_t *, fin); fin->fin_m = *fin->fin_mp = NULL; -#else - ; -#endif + fin->fin_reason = FRB_AUTHNEW; fin->fin_error = 0; - } else + } else { + IPFERROR(1); fin->fin_error = ENOSPC; + } } if ((fr != NULL) && (fr->fr_func != NULL) && @@ -2327,8 +2745,7 @@ u_32_t *passp; * is treated as "not a pass", hence the packet is blocked. */ if (FR_ISPREAUTH(pass)) { - if ((fin->fin_fr = ipauth) != NULL) - pass = fr_scanlist(fin, fr_pass); + pass = ipf_auth_pre_scanlist(softc, fin, pass); } /* @@ -2337,27 +2754,25 @@ u_32_t *passp; */ if ((pass & (FR_KEEPFRAG|FR_KEEPSTATE)) == FR_KEEPFRAG) { if (fin->fin_flx & FI_FRAG) { - if (fr_newfrag(fin, pass) == -1) { - ATOMIC_INCL(frstats[out].fr_bnfr); + if (ipf_frag_new(softc, fin, pass) == -1) { + LBUMP(ipf_stats[out].fr_bnfr); } else { - ATOMIC_INCL(frstats[out].fr_nfr); + LBUMP(ipf_stats[out].fr_nfr); } } else { - ATOMIC_INCL(frstats[out].fr_cfr); + LBUMP(ipf_stats[out].fr_cfr); } } fr = fin->fin_fr; - - if (passp != NULL) - *passp = pass; + *passp = pass; return fr; } /* ------------------------------------------------------------------------ */ -/* Function: fr_check */ +/* Function: ipf_check */ /* Returns: int - 0 == packet allowed through, */ /* User space: */ /* -1 == packet blocked */ @@ -2375,7 +2790,7 @@ u_32_t *passp; /* qpi(I) - pointer to STREAMS queue information for this */ /* interface & direction. */ /* */ -/* fr_check() is the master function for all IPFilter packet processing. */ +/* ipf_check() is the master function for all IPFilter packet processing. */ /* It orchestrates: Network Address Translation (NAT), checking for packet */ /* authorisation (or pre-authorisation), presence of related state info., */ /* generating log entries, IP packet accounting, routing of packets as */ @@ -2386,31 +2801,34 @@ u_32_t *passp; /* freed. Packets passed may be returned with the pointer pointed to by */ /* by "mp" changed to a new buffer. */ /* ------------------------------------------------------------------------ */ -int fr_check(ip, hlen, ifp, out +int +ipf_check(ctx, ip, hlen, ifp, out #if defined(_KERNEL) && defined(MENTAT) -, qif, mp) -void *qif; + , qif, mp) + void *qif; #else -, mp) + , mp) #endif -mb_t **mp; -ip_t *ip; -int hlen; -void *ifp; -int out; + mb_t **mp; + ip_t *ip; + int hlen; + void *ifp; + int out; + void *ctx; { /* * The above really sucks, but short of writing a diff */ + ipf_main_softc_t *softc = ctx; fr_info_t frinfo; fr_info_t *fin = &frinfo; - u_32_t pass = fr_pass; + u_32_t pass = softc->ipf_pass; frentry_t *fr = NULL; int v = IP_V(ip); mb_t *mc = NULL; mb_t *m; /* - * The first part of fr_check() deals with making sure that what goes + * The first part of ipf_check() deals with making sure that what goes * into the filtering engine makes some sense. Information about the * the packet is distilled, collected into a fr_info_t structure and * the an attempt to ensure the buffer the packet is in is big enough @@ -2420,7 +2838,7 @@ int out; # ifdef MENTAT qpktinfo_t *qpi = qif; -# if !defined(_INET_IP_STACK_H) +# ifdef __sparc if ((u_int)ip & 0x3) return 2; # endif @@ -2428,18 +2846,17 @@ int out; SPL_INT(s); # endif - READ_ENTER(&ipf_global); - - if (fr_running <= 0) { - RWLOCK_EXIT(&ipf_global); + if (softc->ipf_running <= 0) { return 0; } bzero((char *)fin, sizeof(*fin)); # ifdef MENTAT - if (qpi->qpi_flags & QF_GROUP) - fin->fin_flx |= FI_MBCAST; + if (qpi->qpi_flags & QF_BROADCAST) + fin->fin_flx |= FI_MBCAST|FI_BROADCAST; + if (qpi->qpi_flags & QF_MULTICAST) + fin->fin_flx |= FI_MBCAST|FI_MULTICAST; m = qpi->qpi_m; fin->fin_qfm = m; fin->fin_qpi = qpi; @@ -2467,7 +2884,8 @@ int out; */ m->m_flags &= ~M_CANFASTFWD; # endif /* M_CANFASTFWD */ -# ifdef CSUM_DELAY_DATA +# if defined(CSUM_DELAY_DATA) && (!defined(__FreeBSD_version) || \ + (__FreeBSD_version < 501108)) /* * disable delayed checksums. */ @@ -2478,10 +2896,20 @@ int out; # endif /* CSUM_DELAY_DATA */ # endif /* MENTAT */ #else - READ_ENTER(&ipf_global); - bzero((char *)fin, sizeof(*fin)); m = *mp; +# if defined(M_MCAST) + if ((m->m_flags & M_MCAST) != 0) + fin->fin_flx |= FI_MBCAST|FI_MULTICAST; +# endif +# if defined(M_MLOOP) + if ((m->m_flags & M_MLOOP) != 0) + fin->fin_flx |= FI_MBCAST|FI_MULTICAST; +# endif +# if defined(M_BCAST) + if ((m->m_flags & M_BCAST) != 0) + fin->fin_flx |= FI_MBCAST|FI_BROADCAST; +# endif #endif /* _KERNEL */ fin->fin_v = v; @@ -2493,6 +2921,7 @@ int out; fin->fin_error = ENETUNREACH; fin->fin_hlen = (u_short)hlen; fin->fin_dp = (char *)ip + hlen; + fin->fin_main_soft = softc; fin->fin_ipoff = (char *)ip - MTOD(m, char *); @@ -2500,27 +2929,29 @@ int out; #ifdef USE_INET6 if (v == 6) { - ATOMIC_INCL(frstats[out].fr_ipv6); + LBUMP(ipf_stats[out].fr_ipv6); /* * Jumbo grams are quite likely too big for internal buffer * structures to handle comfortably, for now, so just drop * them. */ if (((ip6_t *)ip)->ip6_plen == 0) { + DT1(frb_jumbo, ip6_t *, (ip6_t *)ip); pass = FR_BLOCK|FR_NOMATCH; + fin->fin_reason = FRB_JUMBO; goto finished; } + fin->fin_family = AF_INET6; } else #endif { -#if ((defined(OpenBSD) && (OpenBSD >= 200311)) || (__FreeBSD_version >= 1000019)) && defined(_KERNEL) - ip->ip_len = ntohs(ip->ip_len); - ip->ip_off = ntohs(ip->ip_off); -#endif + fin->fin_family = AF_INET; } - if (fr_makefrip(hlen, ip, fin) == -1) { + if (ipf_makefrip(hlen, ip, fin) == -1) { + DT1(frb_makefrip, fr_info_t *, fin); pass = FR_BLOCK|FR_NOMATCH; + fin->fin_reason = FRB_MAKEFRIP; goto finished; } @@ -2533,21 +2964,19 @@ int out; if (!out) { if (v == 4) { -#ifdef _KERNEL - if (fr_chksrc && !fr_verifysrc(fin)) { - ATOMIC_INCL(frstats[0].fr_badsrc); + if (softc->ipf_chksrc && !ipf_verifysrc(fin)) { + LBUMPD(ipf_stats[0], fr_v4_badsrc); fin->fin_flx |= FI_BADSRC; } -#endif - if (fin->fin_ip->ip_ttl < fr_minttl) { - ATOMIC_INCL(frstats[0].fr_badttl); + if (fin->fin_ip->ip_ttl < softc->ipf_minttl) { + LBUMPD(ipf_stats[0], fr_v4_badttl); fin->fin_flx |= FI_LOWTTL; } } #ifdef USE_INET6 else if (v == 6) { - if (((ip6_t *)ip)->ip6_hlim < fr_minttl) { - ATOMIC_INCL(frstats[0].fr_badttl); + if (((ip6_t *)ip)->ip6_hlim < softc->ipf_minttl) { + LBUMPD(ipf_stats[0], fr_v6_badttl); fin->fin_flx |= FI_LOWTTL; } } @@ -2555,120 +2984,135 @@ int out; } if (fin->fin_flx & FI_SHORT) { - ATOMIC_INCL(frstats[out].fr_short); + LBUMPD(ipf_stats[out], fr_short); } - READ_ENTER(&ipf_mutex); + READ_ENTER(&softc->ipf_mutex); - /* - * Check auth now. This, combined with the check below to see if apass - * is 0 is to ensure that we don't count the packet twice, which can - * otherwise occur when we reprocess it. As it is, we only count it - * after it has no auth. table matchup. This also stops NAT from - * occuring until after the packet has been auth'd. - */ - fr = fr_checkauth(fin, &pass); if (!out) { - if (fr_checknatin(fin, &pass) == -1) { - goto filterdone; + switch (fin->fin_v) + { + case 4 : + if (ipf_nat_checkin(fin, &pass) == -1) { + goto filterdone; + } + break; +#ifdef USE_INET6 + case 6 : + if (ipf_nat6_checkin(fin, &pass) == -1) { + goto filterdone; + } + break; +#endif + default : + break; } } - if (!out) - (void) fr_acctpkt(fin, NULL); + /* + * Check auth now. + * If a packet is found in the auth table, then skip checking + * the access lists for permission but we do need to consider + * the result as if it were from the ACL's. In addition, being + * found in the auth table means it has been seen before, so do + * not pass it through accounting (again), lest it be counted twice. + */ + fr = ipf_auth_check(fin, &pass); + if (!out && (fr == NULL)) + (void) ipf_acctpkt(fin, NULL); if (fr == NULL) { - if ((fin->fin_flx & (FI_FRAG|FI_BAD)) == FI_FRAG) { - fr = fr_knownfrag(fin, &pass); - /* - * Reset the keep state flag here so that we don't - * try and add a new state entry because of it, leading - * to a blocked packet because the add will fail. - */ - if (fr != NULL) - pass &= ~FR_KEEPSTATE; - } + if ((fin->fin_flx & FI_FRAG) != 0) + fr = ipf_frag_known(fin, &pass); + if (fr == NULL) - fr = fr_checkstate(fin, &pass); + fr = ipf_state_check(fin, &pass); } if ((pass & FR_NOMATCH) || (fr == NULL)) - fr = fr_firewall(fin, &pass); + fr = ipf_firewall(fin, &pass); /* * If we've asked to track state for this packet, set it up. - * Here rather than fr_firewall because fr_checkauth may decide + * Here rather than ipf_firewall because ipf_checkauth may decide * to return a packet for "keep state" */ if ((pass & FR_KEEPSTATE) && (fin->fin_m != NULL) && !(fin->fin_flx & FI_STATE)) { - if (fr_addstate(fin, NULL, 0) != NULL) { - ATOMIC_INCL(frstats[out].fr_ads); + if (ipf_state_add(softc, fin, NULL, 0) == 0) { + LBUMP(ipf_stats[out].fr_ads); } else { - ATOMIC_INCL(frstats[out].fr_bads); + LBUMP(ipf_stats[out].fr_bads); if (FR_ISPASS(pass)) { + DT(frb_stateadd); pass &= ~FR_CMDMASK; pass |= FR_BLOCK; + fin->fin_reason = FRB_STATEADD; } } } fin->fin_fr = fr; + if ((fr != NULL) && !(fin->fin_flx & FI_STATE)) { + fin->fin_dif = &fr->fr_dif; + fin->fin_tif = &fr->fr_tifs[fin->fin_rev]; + } /* * Only count/translate packets which will be passed on, out the * interface. */ if (out && FR_ISPASS(pass)) { - (void) fr_acctpkt(fin, NULL); + (void) ipf_acctpkt(fin, NULL); - if (fr_checknatout(fin, &pass) == -1) { - ; - } else if ((fr_update_ipid != 0) && (v == 4)) { - if (fr_updateipid(fin) == -1) { - ATOMIC_INCL(frstats[1].fr_ipud); - pass &= ~FR_CMDMASK; - pass |= FR_BLOCK; - } else { - ATOMIC_INCL(frstats[0].fr_ipud); + switch (fin->fin_v) + { + case 4 : + if (ipf_nat_checkout(fin, &pass) == -1) { + ; + } else if ((softc->ipf_update_ipid != 0) && (v == 4)) { + if (ipf_updateipid(fin) == -1) { + DT(frb_updateipid); + LBUMP(ipf_stats[1].fr_ipud); + pass &= ~FR_CMDMASK; + pass |= FR_BLOCK; + fin->fin_reason = FRB_UPDATEIPID; + } else { + LBUMP(ipf_stats[0].fr_ipud); + } } + break; +#ifdef USE_INET6 + case 6 : + (void) ipf_nat6_checkout(fin, &pass); + break; +#endif + default : + break; } } filterdone: #ifdef IPFILTER_LOG - if ((fr_flags & FF_LOGGING) || (pass & FR_LOGMASK)) { - (void) fr_dolog(fin, &pass); + if ((softc->ipf_flags & FF_LOGGING) || (pass & FR_LOGMASK)) { + (void) ipf_dolog(fin, &pass); } #endif /* - * The FI_STATE flag is cleared here so that calling fr_checkstate + * The FI_STATE flag is cleared here so that calling ipf_state_check * will work when called from inside of fr_fastroute. Although * there is a similar flag, FI_NATED, for NAT, it does have the same * impact on code execution. */ - if (fin->fin_state != NULL) { - fr_statederef((ipstate_t **)&fin->fin_state); - fin->fin_flx ^= FI_STATE; - } - - if (fin->fin_nat != NULL) { - if (FR_ISBLOCK(pass) && (fin->fin_flx & FI_NEWNAT)) { - WRITE_ENTER(&ipf_nat); - nat_delete((nat_t *)fin->fin_nat, NL_DESTROY); - RWLOCK_EXIT(&ipf_nat); - fin->fin_nat = NULL; - } else { - fr_natderef((nat_t **)&fin->fin_nat); - } - } + fin->fin_flx &= ~FI_STATE; +#if defined(FASTROUTE_RECURSION) /* - * Up the reference on fr_lock and exit ipf_mutex. fr_fastroute - * only frees up the lock on ipf_global and the generation of a - * packet below could cause a recursive call into IPFilter. - * Hang onto the filter rule just in case someone decides to remove - * or flush it in the meantime. + * Up the reference on fr_lock and exit ipf_mutex. The generation of + * a packet below can sometimes cause a recursive call into IPFilter. + * On those platforms where that does happen, we need to hang onto + * the filter rule just in case someone decides to remove or flush it + * in the meantime. */ if (fr != NULL) { MUTEX_ENTER(&fr->fr_lock); @@ -2676,16 +3120,17 @@ filterdone: MUTEX_EXIT(&fr->fr_lock); } - RWLOCK_EXIT(&ipf_mutex); + RWLOCK_EXIT(&softc->ipf_mutex); +#endif if ((pass & FR_RETMASK) != 0) { /* * Should we return an ICMP packet to indicate error * status passing through the packet filter ? * WARNING: ICMP error packets AND TCP RST packets should - * ONLY be sent in repsonse to incoming packets. Sending them - * in response to outbound packets can result in a panic on - * some operating systems. + * ONLY be sent in repsonse to incoming packets. Sending + * them in response to outbound packets can result in a + * panic on some operating systems. */ if (!out) { if (pass & FR_RETICMP) { @@ -2695,13 +3140,14 @@ filterdone: dst = 1; else dst = 0; - (void) fr_send_icmp_err(ICMP_UNREACH, fin, dst); - ATOMIC_INCL(frstats[0].fr_ret); + (void) ipf_send_icmp_err(ICMP_UNREACH, fin, + dst); + LBUMP(ipf_stats[0].fr_ret); } else if (((pass & FR_RETMASK) == FR_RETRST) && !(fin->fin_flx & FI_SHORT)) { if (((fin->fin_flx & FI_OOW) != 0) || - (fr_send_reset(fin) == 0)) { - ATOMIC_INCL(frstats[1].fr_ret); + (ipf_send_reset(fin) == 0)) { + LBUMP(ipf_stats[1].fr_ret); } } @@ -2710,61 +3156,81 @@ filterdone: * takes over disposing of this packet. */ if (FR_ISAUTH(pass) && (fin->fin_m != NULL)) { + DT1(frb_authcapture, fr_info_t *, fin); fin->fin_m = *fin->fin_mp = NULL; + fin->fin_reason = FRB_AUTHCAPTURE; + m = NULL; } } else { - if (pass & FR_RETRST) + if (pass & FR_RETRST) { fin->fin_error = ECONNRESET; + } } } + /* + * After the above so that ICMP unreachables and TCP RSTs get + * created properly. + */ + if (FR_ISBLOCK(pass) && (fin->fin_flx & FI_NEWNAT)) + ipf_nat_uncreate(fin); + /* * If we didn't drop off the bottom of the list of rules (and thus * the 'current' rule fr is not NULL), then we may have some extra * instructions about what to do with a packet. * Once we're finished return to our caller, freeing the packet if - * we are dropping it (* BSD ONLY *). + * we are dropping it. */ if (fr != NULL) { frdest_t *fdp; - fdp = &fr->fr_tifs[fin->fin_rev]; + /* + * Generate a duplicated packet first because ipf_fastroute + * can lead to fin_m being free'd... not good. + */ + fdp = fin->fin_dif; + if ((fdp != NULL) && (fdp->fd_ptr != NULL) && + (fdp->fd_ptr != (void *)-1)) { + mc = M_COPY(fin->fin_m); + if (mc != NULL) + ipf_fastroute(mc, &mc, fin, fdp); + } + fdp = fin->fin_tif; if (!out && (pass & FR_FASTROUTE)) { /* - * For fastroute rule, no destioation interface defined + * For fastroute rule, no destination interface defined * so pass NULL as the frdest_t parameter */ - (void) fr_fastroute(fin->fin_m, mp, fin, NULL); + (void) ipf_fastroute(fin->fin_m, mp, fin, NULL); m = *mp = NULL; - } else if ((fdp->fd_ifp != NULL) && - (fdp->fd_ifp != (struct ifnet *)-1)) { + } else if ((fdp != NULL) && (fdp->fd_ptr != NULL) && + (fdp->fd_ptr != (struct ifnet *)-1)) { /* this is for to rules: */ - (void) fr_fastroute(fin->fin_m, mp, fin, fdp); + ipf_fastroute(fin->fin_m, mp, fin, fdp); m = *mp = NULL; } - /* - * Generate a duplicated packet. - */ - if ((pass & FR_DUP) != 0) { - mc = M_DUPLICATE(fin->fin_m); - if (mc != NULL) - (void) fr_fastroute(mc, &mc, fin, &fr->fr_dif); - } - - (void) fr_derefrule(&fr); +#if defined(FASTROUTE_RECURSION) + (void) ipf_derefrule(softc, &fr); +#endif } +#if !defined(FASTROUTE_RECURSION) + RWLOCK_EXIT(&softc->ipf_mutex); +#endif finished: if (!FR_ISPASS(pass)) { - ATOMIC_INCL(frstats[out].fr_block); + LBUMP(ipf_stats[out].fr_block); if (*mp != NULL) { +#ifdef _KERNEL FREE_MB_T(*mp); +#endif m = *mp = NULL; } } else { - ATOMIC_INCL(frstats[out].fr_pass); + LBUMP(ipf_stats[out].fr_pass); #if defined(_KERNEL) && defined(__sgi) if ((fin->fin_hbuf != NULL) && (mtod(fin->fin_m, struct ip *) != fin->fin_ip)) { @@ -2774,21 +3240,20 @@ finished: } SPL_X(s); - RWLOCK_EXIT(&ipf_global); #ifdef _KERNEL -# if (defined(OpenBSD) && (OpenBSD >= 200311)) || (__FreeBSD_version >= 1000019) - if (FR_ISPASS(pass) && (v == 4)) { - ip = fin->fin_ip; - ip->ip_len = ntohs(ip->ip_len); - ip->ip_off = ntohs(ip->ip_off); - } -# endif - return (FR_ISPASS(pass)) ? 0 : fin->fin_error; + if (FR_ISPASS(pass)) + return 0; + LBUMP(ipf_stats[out].fr_blocked[fin->fin_reason]); + return fin->fin_error; #else /* _KERNEL */ + if (*mp != NULL) + (*mp)->mb_ifp = fin->fin_ifp; + blockreason = fin->fin_reason; FR_VERBOSE(("fin_flx %#x pass %#x ", fin->fin_flx, pass)); - if ((pass & FR_NOMATCH) != 0) - return 1; + /*if ((pass & FR_CMDMASK) == (softc->ipf_pass & FR_CMDMASK))*/ + if ((pass & FR_NOMATCH) != 0) + return 1; if ((pass & FR_RETMASK) != 0) switch (pass & FR_RETMASK) @@ -2821,7 +3286,7 @@ finished: #ifdef IPFILTER_LOG /* ------------------------------------------------------------------------ */ -/* Function: fr_dolog */ +/* Function: ipf_dolog */ /* Returns: frentry_t* - returns contents of fin_fr (no change made) */ /* Parameters: fin(I) - pointer to packet information */ /* passp(IO) - pointer to current/new filter decision (unused) */ @@ -2829,43 +3294,47 @@ finished: /* Checks flags set to see how a packet should be logged, if it is to be */ /* logged. Adjust statistics based on its success or not. */ /* ------------------------------------------------------------------------ */ -frentry_t *fr_dolog(fin, passp) -fr_info_t *fin; -u_32_t *passp; +frentry_t * +ipf_dolog(fin, passp) + fr_info_t *fin; + u_32_t *passp; { + ipf_main_softc_t *softc = fin->fin_main_soft; u_32_t pass; int out; out = fin->fin_out; pass = *passp; - if ((fr_flags & FF_LOGNOMATCH) && (pass & FR_NOMATCH)) { + if ((softc->ipf_flags & FF_LOGNOMATCH) && (pass & FR_NOMATCH)) { pass |= FF_LOGNOMATCH; - ATOMIC_INCL(frstats[out].fr_npkl); + LBUMPD(ipf_stats[out], fr_npkl); goto logit; + } else if (((pass & FR_LOGMASK) == FR_LOGP) || - (FR_ISPASS(pass) && (fr_flags & FF_LOGPASS))) { + (FR_ISPASS(pass) && (softc->ipf_flags & FF_LOGPASS))) { if ((pass & FR_LOGMASK) != FR_LOGP) pass |= FF_LOGPASS; - ATOMIC_INCL(frstats[out].fr_ppkl); + LBUMPD(ipf_stats[out], fr_ppkl); goto logit; + } else if (((pass & FR_LOGMASK) == FR_LOGB) || - (FR_ISBLOCK(pass) && (fr_flags & FF_LOGBLOCK))) { + (FR_ISBLOCK(pass) && (softc->ipf_flags & FF_LOGBLOCK))) { if ((pass & FR_LOGMASK) != FR_LOGB) pass |= FF_LOGBLOCK; - ATOMIC_INCL(frstats[out].fr_bpkl); -logit: - if (ipflog(fin, pass) == -1) { - ATOMIC_INCL(frstats[out].fr_skip); + LBUMPD(ipf_stats[out], fr_bpkl); +logit: + if (ipf_log_pkt(fin, pass) == -1) { /* * If the "or-block" option has been used then * block the packet if we failed to log it. */ - if ((pass & FR_LOGORBLOCK) && - FR_ISPASS(pass)) { + if ((pass & FR_LOGORBLOCK) && FR_ISPASS(pass)) { + DT1(frb_logfail2, u_int, pass); pass &= ~FR_CMDMASK; pass |= FR_BLOCK; + fin->fin_reason = FRB_LOGFAIL2; } } *passp = pass; @@ -2886,9 +3355,10 @@ logit: /* */ /* N.B.: addr should be 16bit aligned. */ /* ------------------------------------------------------------------------ */ -u_short ipf_cksum(addr, len) -u_short *addr; -int len; +u_short +ipf_cksum(addr, len) + u_short *addr; + int len; { u_32_t sum = 0; @@ -2911,11 +3381,10 @@ int len; /* ------------------------------------------------------------------------ */ /* Function: fr_cksum */ /* Returns: u_short - layer 4 checksum */ -/* Parameters: m(I ) - pointer to buffer holding packet */ +/* Parameters: fin(I) - pointer to packet information */ /* ip(I) - pointer to IP header */ /* l4proto(I) - protocol to caclulate checksum for */ /* l4hdr(I) - pointer to layer 4 header */ -/* l3len(I) - length of layer 4 data plus layer 3 header */ /* */ /* Calculates the TCP checksum for the packet held in "m", using the data */ /* in the IP header "ip" to seed it. */ @@ -2924,31 +3393,31 @@ int len; /* and the TCP header. We also assume that data blocks aren't allocated in */ /* odd sizes. */ /* */ -/* For IPv6, l3len excludes extension header size. */ -/* */ -/* Expects ip_len to be in host byte order when called. */ +/* Expects ip_len and ip_off to be in network byte order when called. */ /* ------------------------------------------------------------------------ */ -u_short fr_cksum(m, ip, l4proto, l4hdr, l3len) -mb_t *m; -ip_t *ip; -int l4proto, l3len; -void *l4hdr; +u_short +fr_cksum(fin, ip, l4proto, l4hdr) + fr_info_t *fin; + ip_t *ip; + int l4proto; + void *l4hdr; { - u_short *sp, slen, sumsave, l4hlen, *csump; + u_short *sp, slen, sumsave, *csump; u_int sum, sum2; int hlen; + int off; #ifdef USE_INET6 ip6_t *ip6; #endif csump = NULL; sumsave = 0; - l4hlen = 0; sp = NULL; slen = 0; hlen = 0; sum = 0; + sum = htons((u_short)l4proto); /* * Add up IP Header portion */ @@ -2956,9 +3425,7 @@ void *l4hdr; if (IP_V(ip) == 4) { #endif hlen = IP_HL(ip) << 2; - slen = l3len - hlen; - sum = htons((u_short)l4proto); - sum += htons(slen); + off = hlen; sp = (u_short *)&ip->ip_src; sum += *sp++; /* ip_src */ sum += *sp++; @@ -2968,9 +3435,7 @@ void *l4hdr; } else if (IP_V(ip) == 6) { ip6 = (ip6_t *)ip; hlen = sizeof(*ip6); - slen = l3len - hlen; - sum = htons((u_short)l4proto); - sum += htons(slen); + off = ((char *)fin->fin_dp - (char *)fin->fin_ip); sp = (u_short *)&ip6->ip6_src; sum += *sp++; /* ip6_src */ sum += *sp++; @@ -2980,6 +3445,7 @@ void *l4hdr; sum += *sp++; sum += *sp++; sum += *sp++; + /* This needs to be routing header aware. */ sum += *sp++; /* ip6_dst */ sum += *sp++; sum += *sp++; @@ -2988,25 +3454,31 @@ void *l4hdr; sum += *sp++; sum += *sp++; sum += *sp++; + } else { + return 0xffff; } #endif + slen = fin->fin_plen - off; + sum += htons(slen); switch (l4proto) { case IPPROTO_UDP : csump = &((udphdr_t *)l4hdr)->uh_sum; - l4hlen = sizeof(udphdr_t); break; case IPPROTO_TCP : csump = &((tcphdr_t *)l4hdr)->th_sum; - l4hlen = sizeof(tcphdr_t); break; case IPPROTO_ICMP : csump = &((icmphdr_t *)l4hdr)->icmp_cksum; - l4hlen = 4; - sum = 0; + sum = 0; /* Pseudo-checksum is not included */ break; +#ifdef USE_INET6 + case IPPROTO_ICMPV6 : + csump = &((struct icmp6_hdr *)l4hdr)->icmp6_cksum; + break; +#endif default : break; } @@ -3016,298 +3488,18 @@ void *l4hdr; *csump = 0; } - l4hlen = l4hlen; /* LINT */ - -#ifdef _KERNEL -# ifdef MENTAT - { - void *rp = m->b_rptr; - - if ((unsigned char *)ip > m->b_rptr && (unsigned char *)ip < m->b_wptr) - m->b_rptr = (u_char *)ip; - sum2 = ip_cksum(m, hlen, sum); /* hlen == offset */ - m->b_rptr = rp; - sum2 = (u_short)(~sum2 & 0xffff); - } -# else /* MENTAT */ -# if defined(BSD) || defined(sun) -# if BSD >= 199103 - m->m_data += hlen; -# else - m->m_off += hlen; -# endif - m->m_len -= hlen; - sum2 = in_cksum(m, slen); - m->m_len += hlen; -# if BSD >= 199103 - m->m_data -= hlen; -# else - m->m_off -= hlen; -# endif - /* - * Both sum and sum2 are partial sums, so combine them together. - */ - sum += ~sum2 & 0xffff; - while (sum > 0xffff) - sum = (sum & 0xffff) + (sum >> 16); - sum2 = ~sum & 0xffff; -# else /* defined(BSD) || defined(sun) */ -{ - union { - u_char c[2]; - u_short s; - } bytes; - u_short len = ip->ip_len; -# if defined(__sgi) - int add; -# endif - - /* - * Add up IP Header portion - */ - if (sp != (u_short *)l4hdr) - sp = (u_short *)l4hdr; - - switch (l4proto) - { - case IPPROTO_UDP : - sum += *sp++; /* sport */ - sum += *sp++; /* dport */ - sum += *sp++; /* udp length */ - sum += *sp++; /* checksum */ - break; - - case IPPROTO_TCP : - sum += *sp++; /* sport */ - sum += *sp++; /* dport */ - sum += *sp++; /* seq */ - sum += *sp++; - sum += *sp++; /* ack */ - sum += *sp++; - sum += *sp++; /* off */ - sum += *sp++; /* win */ - sum += *sp++; /* checksum */ - sum += *sp++; /* urp */ - break; - case IPPROTO_ICMP : - sum = *sp++; /* type/code */ - sum += *sp++; /* checksum */ - break; - } - -# ifdef __sgi - /* - * In case we had to copy the IP & TCP header out of mbufs, - * skip over the mbuf bits which are the header - */ - if ((char *)ip != mtod(m, char *)) { - hlen = (char *)sp - (char *)ip; - while (hlen) { - add = MIN(hlen, m->m_len); - sp = (u_short *)(mtod(m, caddr_t) + add); - hlen -= add; - if (add == m->m_len) { - m = m->m_next; - if (!hlen) { - if (!m) - break; - sp = mtod(m, u_short *); - } - PANIC((!m),("fr_cksum(1): not enough data")); - } - } - } -# endif - - len -= (l4hlen + hlen); - if (len <= 0) - goto nodata; - - while (len > 1) { - if (((char *)sp - mtod(m, char *)) >= m->m_len) { - m = m->m_next; - PANIC((!m),("fr_cksum(2): not enough data")); - sp = mtod(m, u_short *); - } - if (((char *)(sp + 1) - mtod(m, char *)) > m->m_len) { - bytes.c[0] = *(u_char *)sp; - m = m->m_next; - PANIC((!m),("fr_cksum(3): not enough data")); - sp = mtod(m, u_short *); - bytes.c[1] = *(u_char *)sp; - sum += bytes.s; - sp = (u_short *)((u_char *)sp + 1); - } - if ((u_long)sp & 1) { - bcopy((char *)sp++, (char *)&bytes.s, sizeof(bytes.s)); - sum += bytes.s; - } else - sum += *sp++; - len -= 2; - } - - if (len != 0) - sum += ntohs(*(u_char *)sp << 8); -nodata: - while (sum > 0xffff) - sum = (sum & 0xffff) + (sum >> 16); - sum2 = (u_short)(~sum & 0xffff); -} -# endif /* defined(BSD) || defined(sun) */ -# endif /* MENTAT */ -#else /* _KERNEL */ - /* - * Add up IP Header portion - */ - if (sp != (u_short *)l4hdr) - sp = (u_short *)l4hdr; - - for (; slen > 1; slen -= 2) - sum += *sp++; - if (slen) - sum += ntohs(*(u_char *)sp << 8); - while (sum > 0xffff) - sum = (sum & 0xffff) + (sum >> 16); - sum2 = (u_short)(~sum & 0xffff); -#endif /* _KERNEL */ + sum2 = ipf_pcksum(fin, off, sum); if (csump != NULL) *csump = sumsave; return sum2; } -#if defined(_KERNEL) && ( ((BSD < 199103) && !defined(MENTAT)) || \ - defined(__sgi) ) && !defined(linux) && !defined(_AIX51) -/* - * Copyright (c) 1982, 1986, 1988, 1991, 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. - * - * @(#)uipc_mbuf.c 8.2 (Berkeley) 1/4/94 - * $Id: fil.c,v 2.243.2.125 2007/10/10 09:27:20 darrenr Exp $ - */ -/* - * Copy data from an mbuf chain starting "off" bytes from the beginning, - * continuing for "len" bytes, into the indicated buffer. - */ -void -m_copydata(m, off, len, cp) - mb_t *m; - int off; - int len; - caddr_t cp; -{ - unsigned count; - - if (off < 0 || len < 0) - panic("m_copydata"); - while (off > 0) { - if (m == 0) - panic("m_copydata"); - if (off < m->m_len) - break; - off -= m->m_len; - m = m->m_next; - } - while (len > 0) { - if (m == 0) - panic("m_copydata"); - count = MIN(m->m_len - off, len); - bcopy(mtod(m, caddr_t) + off, cp, count); - len -= count; - cp += count; - off = 0; - m = m->m_next; - } -} - - -/* - * Copy data from a buffer back into the indicated mbuf chain, - * starting "off" bytes from the beginning, extending the mbuf - * chain if necessary. - */ -void -m_copyback(m0, off, len, cp) - struct mbuf *m0; - int off; - int len; - caddr_t cp; -{ - int mlen; - struct mbuf *m = m0, *n; - int totlen = 0; - - if (m0 == 0) - return; - while (off > (mlen = m->m_len)) { - off -= mlen; - totlen += mlen; - if (m->m_next == 0) { - n = m_getclr(M_DONTWAIT, m->m_type); - if (n == 0) - goto out; - n->m_len = min(MLEN, len + off); - m->m_next = n; - } - m = m->m_next; - } - while (len > 0) { - mlen = min(m->m_len - off, len); - bcopy(cp, off + mtod(m, caddr_t), (unsigned)mlen); - cp += mlen; - len -= mlen; - mlen += off; - off = 0; - totlen += mlen; - if (len == 0) - break; - if (m->m_next == 0) { - n = m_get(M_DONTWAIT, m->m_type); - if (n == 0) - break; - n->m_len = min(MLEN, len); - m->m_next = n; - } - m = m->m_next; - } -out: -#if 0 - if (((m = m0)->m_flags & M_PKTHDR) && (m->m_pkthdr.len < totlen)) - m->m_pkthdr.len = totlen; -#endif - return; -} -#endif /* (_KERNEL) && ( ((BSD < 199103) && !MENTAT) || __sgi) */ - - /* ------------------------------------------------------------------------ */ -/* Function: fr_findgroup */ +/* Function: ipf_findgroup */ /* Returns: frgroup_t * - NULL = group not found, else pointer to group */ -/* Parameters: group(I) - group name to search for */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* group(I) - group name to search for */ /* unit(I) - device to which this group belongs */ /* set(I) - which set of rules (inactive/inactive) this is */ /* fgpp(O) - pointer to place to store pointer to the pointer */ @@ -3316,11 +3508,13 @@ out: /* */ /* Search amongst the defined groups for a particular group number. */ /* ------------------------------------------------------------------------ */ -frgroup_t *fr_findgroup(group, unit, set, fgpp) -char *group; -minor_t unit; -int set; -frgroup_t ***fgpp; +frgroup_t * +ipf_findgroup(softc, group, unit, set, fgpp) + ipf_main_softc_t *softc; + char *group; + minor_t unit; + int set; + frgroup_t ***fgpp; { frgroup_t *fg, **fgp; @@ -3328,7 +3522,7 @@ frgroup_t ***fgpp; * Which list of groups to search in is dependent on which list of * rules are being operated on. */ - fgp = &ipfgroups[unit][set]; + fgp = &softc->ipf_groups[unit][set]; while ((fg = *fgp) != NULL) { if (strncmp(group, fg->fg_name, FR_GROUPLEN) == 0) @@ -3343,10 +3537,11 @@ frgroup_t ***fgpp; /* ------------------------------------------------------------------------ */ -/* Function: fr_addgroup */ +/* Function: ipf_group_add */ /* Returns: frgroup_t * - NULL == did not create group, */ /* != NULL == pointer to the group */ -/* Parameters: num(I) - group number to add */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* num(I) - group number to add */ /* head(I) - rule pointer that is using this as the head */ /* flags(I) - rule flags which describe the type of rule it is */ /* unit(I) - device to which this group will belong to */ @@ -3356,12 +3551,14 @@ frgroup_t ***fgpp; /* Add a new group head, or if it already exists, increase the reference */ /* count to it. */ /* ------------------------------------------------------------------------ */ -frgroup_t *fr_addgroup(group, head, flags, unit, set) -char *group; -void *head; -u_32_t flags; -minor_t unit; -int set; +frgroup_t * +ipf_group_add(softc, group, head, flags, unit, set) + ipf_main_softc_t *softc; + char *group; + void *head; + u_32_t flags; + minor_t unit; + int set; { frgroup_t *fg, **fgp; u_32_t gflags; @@ -3375,8 +3572,10 @@ int set; fgp = NULL; gflags = flags & FR_INOUT; - fg = fr_findgroup(group, unit, set, &fgp); + fg = ipf_findgroup(softc, group, unit, set, &fgp); if (fg != NULL) { + if (fg->fg_head == NULL && head != NULL) + fg->fg_head = head; if (fg->fg_flags == 0) fg->fg_flags = gflags; else if (gflags != fg->fg_flags) @@ -3384,14 +3583,16 @@ int set; fg->fg_ref++; return fg; } + KMALLOC(fg, frgroup_t *); if (fg != NULL) { fg->fg_head = head; fg->fg_start = NULL; fg->fg_next = *fgp; - bcopy(group, fg->fg_name, FR_GROUPLEN); + bcopy(group, fg->fg_name, strlen(group) + 1); fg->fg_flags = gflags; fg->fg_ref = 1; + fg->fg_set = &softc->ipf_groups[unit][set]; *fgp = fg; } return fg; @@ -3399,38 +3600,82 @@ int set; /* ------------------------------------------------------------------------ */ -/* Function: fr_delgroup */ -/* Returns: Nil */ -/* Parameters: group(I) - group name to delete */ -/* unit(I) - device to which this group belongs */ -/* set(I) - which set of rules (inactive/inactive) this is */ +/* Function: ipf_group_del */ +/* Returns: int - number of rules deleted */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* group(I) - group name to delete */ +/* fr(I) - filter rule from which group is referenced */ /* Write Locks: ipf_mutex */ /* */ -/* Attempt to delete a group head. */ -/* Only do this when its reference count reaches 0. */ +/* This function is called whenever a reference to a group is to be dropped */ +/* and thus its reference count needs to be lowered and the group free'd if */ +/* the reference count reaches zero. Passing in fr is really for the sole */ +/* purpose of knowing when the head rule is being deleted. */ /* ------------------------------------------------------------------------ */ -void fr_delgroup(group, unit, set) -char *group; -minor_t unit; -int set; +void +ipf_group_del(softc, group, fr) + ipf_main_softc_t *softc; + frgroup_t *group; + frentry_t *fr; { - frgroup_t *fg, **fgp; - fg = fr_findgroup(group, unit, set, &fgp); - if (fg == NULL) - return; + if (group->fg_head == fr) + group->fg_head = NULL; - fg->fg_ref--; - if (fg->fg_ref == 0) { - *fgp = fg->fg_next; - KFREE(fg); - } + group->fg_ref--; + if ((group->fg_ref == 0) && (group->fg_start == NULL)) + ipf_group_free(group); } /* ------------------------------------------------------------------------ */ -/* Function: fr_getrulen */ +/* Function: ipf_group_free */ +/* Returns: Nil */ +/* Parameters: group(I) - pointer to filter rule group */ +/* */ +/* Remove the group from the list of groups and free it. */ +/* ------------------------------------------------------------------------ */ +static void +ipf_group_free(group) + frgroup_t *group; +{ + frgroup_t **gp; + + for (gp = group->fg_set; *gp != NULL; gp = &(*gp)->fg_next) { + if (*gp == group) { + *gp = group->fg_next; + break; + } + } + KFREE(group); +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_group_flush */ +/* Returns: int - number of rules flush from group */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* Parameters: group(I) - pointer to filter rule group */ +/* */ +/* Remove all of the rules that currently are listed under the given group. */ +/* ------------------------------------------------------------------------ */ +static int +ipf_group_flush(softc, group) + ipf_main_softc_t *softc; + frgroup_t *group; +{ + int gone = 0; + + (void) ipf_flushlist(softc, &gone, &group->fg_start); + + return gone; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_getrulen */ /* Returns: frentry_t * - NULL == not found, else pointer to rule n */ +/* Parameters: softc(I) - pointer to soft context main structure */ /* Parameters: unit(I) - device for which to count the rule's number */ /* flags(I) - which set of rules to find the rule in */ /* group(I) - group name */ @@ -3439,18 +3684,20 @@ int set; /* Find rule # n in group # g and return a pointer to it. Return NULl if */ /* group # g doesn't exist or there are less than n rules in the group. */ /* ------------------------------------------------------------------------ */ -frentry_t *fr_getrulen(unit, group, n) -int unit; -char *group; -u_32_t n; +frentry_t * +ipf_getrulen(softc, unit, group, n) + ipf_main_softc_t *softc; + int unit; + char *group; + u_32_t n; { frentry_t *fr; frgroup_t *fg; - fg = fr_findgroup(group, unit, fr_active, NULL); + fg = ipf_findgroup(softc, group, unit, softc->ipf_active, NULL); if (fg == NULL) return NULL; - for (fr = fg->fg_head; fr && n; fr = fr->fr_next, n--) + for (fr = fg->fg_start; fr && n; fr = fr->fr_next, n--) ; if (n != 0) return NULL; @@ -3459,41 +3706,9 @@ u_32_t n; /* ------------------------------------------------------------------------ */ -/* Function: fr_rulen */ -/* Returns: int - >= 0 - rule number, -1 == search failed */ -/* Parameters: unit(I) - device for which to count the rule's number */ -/* fr(I) - pointer to rule to match */ -/* */ -/* Return the number for a rule on a specific filtering device. */ -/* ------------------------------------------------------------------------ */ -int fr_rulen(unit, fr) -int unit; -frentry_t *fr; -{ - frentry_t *fh; - frgroup_t *fg; - u_32_t n = 0; - - if (fr == NULL) - return -1; - fg = fr_findgroup(fr->fr_group, unit, fr_active, NULL); - if (fg == NULL) - return -1; - for (fh = fg->fg_head; fh; n++, fh = fh->fr_next) - if (fh == fr) - break; - if (fh == NULL) - return -1; - return n; -} - - -/* ------------------------------------------------------------------------ */ -/* Function: frflushlist */ +/* Function: ipf_flushlist */ /* Returns: int - >= 0 - number of flushed rules */ -/* Parameters: set(I) - which set of rules (inactive/inactive) this is */ -/* unit(I) - device for which to flush rules */ -/* flags(I) - which set of rules to flush */ +/* Parameters: softc(I) - pointer to soft context main structure */ /* nfreedp(O) - pointer to int where flush count is stored */ /* listp(I) - pointer to list to flush pointer */ /* Write Locks: ipf_mutex */ @@ -3507,11 +3722,11 @@ frentry_t *fr; /* */ /* NOTE: Rules not loaded from user space cannot be flushed. */ /* ------------------------------------------------------------------------ */ -static int frflushlist(set, unit, nfreedp, listp) -int set; -minor_t unit; -int *nfreedp; -frentry_t **listp; +static int +ipf_flushlist(softc, nfreedp, listp) + ipf_main_softc_t *softc; + int *nfreedp; + frentry_t **listp; { int freed = 0; frentry_t *fp; @@ -3523,18 +3738,27 @@ frentry_t **listp; continue; } *listp = fp->fr_next; - if (fp->fr_grp != NULL) { - (void) frflushlist(set, unit, nfreedp, fp->fr_grp); + if (fp->fr_next != NULL) + fp->fr_next->fr_pnext = fp->fr_pnext; + fp->fr_pnext = NULL; + + if (fp->fr_grphead != NULL) { + freed += ipf_group_flush(softc, fp->fr_grphead); + fp->fr_names[fp->fr_grhead] = '\0'; } - if (fp->fr_grhead != NULL) { - fr_delgroup(fp->fr_grhead, unit, set); - *fp->fr_grhead = '\0'; + if (fp->fr_icmpgrp != NULL) { + freed += ipf_group_flush(softc, fp->fr_icmpgrp); + fp->fr_names[fp->fr_icmphead] = '\0'; } + if (fp->fr_srctrack.ht_max_nodes) + ipf_rb_ht_flush(&fp->fr_srctrack); + + fp->fr_next = NULL; + ASSERT(fp->fr_ref > 0); - fp->fr_next = NULL; - if (fr_derefrule(&fp) == 0) + if (ipf_derefrule(softc, &fp) == 0) freed++; } *nfreedp += freed; @@ -3543,61 +3767,47 @@ frentry_t **listp; /* ------------------------------------------------------------------------ */ -/* Function: frflush */ +/* Function: ipf_flush */ /* Returns: int - >= 0 - number of flushed rules */ -/* Parameters: unit(I) - device for which to flush rules */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* unit(I) - device for which to flush rules */ /* flags(I) - which set of rules to flush */ /* */ /* Calls flushlist() for all filter rules (accounting, firewall - both IPv4 */ /* and IPv6) as defined by the value of flags. */ /* ------------------------------------------------------------------------ */ -int frflush(unit, proto, flags) -minor_t unit; -int proto, flags; +int +ipf_flush(softc, unit, flags) + ipf_main_softc_t *softc; + minor_t unit; + int flags; { int flushed = 0, set; - WRITE_ENTER(&ipf_mutex); - bzero((char *)frcache, sizeof(frcache)); + WRITE_ENTER(&softc->ipf_mutex); - set = fr_active; + set = softc->ipf_active; if ((flags & FR_INACTIVE) == FR_INACTIVE) set = 1 - set; if (flags & FR_OUTQUE) { - if (proto == 0 || proto == 6) { - (void) frflushlist(set, unit, - &flushed, &ipfilter6[1][set]); - (void) frflushlist(set, unit, - &flushed, &ipacct6[1][set]); - } - if (proto == 0 || proto == 4) { - (void) frflushlist(set, unit, - &flushed, &ipfilter[1][set]); - (void) frflushlist(set, unit, - &flushed, &ipacct[1][set]); - } + ipf_flushlist(softc, &flushed, &softc->ipf_rules[1][set]); + ipf_flushlist(softc, &flushed, &softc->ipf_acct[1][set]); } if (flags & FR_INQUE) { - if (proto == 0 || proto == 6) { - (void) frflushlist(set, unit, - &flushed, &ipfilter6[0][set]); - (void) frflushlist(set, unit, - &flushed, &ipacct6[0][set]); - } - if (proto == 0 || proto == 4) { - (void) frflushlist(set, unit, - &flushed, &ipfilter[0][set]); - (void) frflushlist(set, unit, - &flushed, &ipacct[0][set]); - } + ipf_flushlist(softc, &flushed, &softc->ipf_rules[0][set]); + ipf_flushlist(softc, &flushed, &softc->ipf_acct[0][set]); } - RWLOCK_EXIT(&ipf_mutex); + + flushed += ipf_flush_groups(softc, &softc->ipf_groups[unit][set], + flags & (FR_INQUE|FR_OUTQUE)); + + RWLOCK_EXIT(&softc->ipf_mutex); if (unit == IPL_LOGIPF) { int tmp; - tmp = frflush(IPL_LOGCOUNT, proto, flags); + tmp = ipf_flush(softc, IPL_LOGCOUNT, flags); if (tmp >= 0) flushed += tmp; } @@ -3605,6 +3815,59 @@ int proto, flags; } +/* ------------------------------------------------------------------------ */ +/* Function: ipf_flush_groups */ +/* Returns: int - >= 0 - number of flushed rules */ +/* Parameters: softc(I) - soft context pointerto work with */ +/* grhead(I) - pointer to the start of the group list to flush */ +/* flags(I) - which set of rules to flush */ +/* */ +/* Walk through all of the groups under the given group head and remove all */ +/* of those that match the flags passed in. The for loop here is bit more */ +/* complicated than usual because the removal of a rule with ipf_derefrule */ +/* may end up removing not only the structure pointed to by "fg" but also */ +/* what is fg_next and fg_next after that. So if a filter rule is actually */ +/* removed from the group then it is necessary to start again. */ +/* ------------------------------------------------------------------------ */ +static int +ipf_flush_groups(softc, grhead, flags) + ipf_main_softc_t *softc; + frgroup_t **grhead; + int flags; +{ + frentry_t *fr, **frp; + frgroup_t *fg, **fgp; + int flushed = 0; + int removed = 0; + + for (fgp = grhead; (fg = *fgp) != NULL; ) { + while ((fg != NULL) && ((fg->fg_flags & flags) == 0)) + fg = fg->fg_next; + if (fg == NULL) + break; + removed = 0; + frp = &fg->fg_start; + while ((removed == 0) && ((fr = *frp) != NULL)) { + if ((fr->fr_flags & flags) == 0) { + frp = &fr->fr_next; + } else { + if (fr->fr_next != NULL) + fr->fr_next->fr_pnext = fr->fr_pnext; + *frp = fr->fr_next; + fr->fr_pnext = NULL; + fr->fr_next = NULL; + (void) ipf_derefrule(softc, &fr); + flushed++; + removed++; + } + } + if (removed == 0) + fgp = &fg->fg_next; + } + return flushed; +} + + /* ------------------------------------------------------------------------ */ /* Function: memstr */ /* Returns: char * - NULL if failed, != NULL pointer to matching bytes */ @@ -3616,10 +3879,11 @@ int proto, flags; /* Search dst for a sequence of bytes matching those at src and extend for */ /* slen bytes. */ /* ------------------------------------------------------------------------ */ -char *memstr(src, dst, slen, dlen) -const char *src; -char *dst; -size_t slen, dlen; +char * +memstr(src, dst, slen, dlen) + const char *src; + char *dst; + size_t slen, dlen; { char *s = NULL; @@ -3634,7 +3898,7 @@ size_t slen, dlen; return s; } /* ------------------------------------------------------------------------ */ -/* Function: fr_fixskip */ +/* Function: ipf_fixskip */ /* Returns: Nil */ /* Parameters: listp(IO) - pointer to start of list with skip rule */ /* rp(I) - rule added/removed with skip in it. */ @@ -3645,9 +3909,10 @@ size_t slen, dlen; /* Adjust all the rules in a list which would have skip'd past the position */ /* where we are inserting to skip to the right place given the change. */ /* ------------------------------------------------------------------------ */ -void fr_fixskip(listp, rp, addremove) -frentry_t **listp, *rp; -int addremove; +void +ipf_fixskip(listp, rp, addremove) + frentry_t **listp, *rp; + int addremove; { int rules, rn; frentry_t *fp; @@ -3676,8 +3941,9 @@ int addremove; /* consecutive 1's is different to that passed, return -1, else return # */ /* of bits. */ /* ------------------------------------------------------------------------ */ -int count4bits(ip) -u_32_t ip; +int +count4bits(ip) + u_32_t ip; { u_32_t ipn; int cnt = 0, i, j; @@ -3700,7 +3966,6 @@ u_32_t ip; } -# if 0 /* ------------------------------------------------------------------------ */ /* Function: count6bits */ /* Returns: int - >= 0 - number of consecutive bits in input */ @@ -3709,8 +3974,10 @@ u_32_t ip; /* IPv6 ONLY */ /* count consecutive 1's in bit mask. */ /* ------------------------------------------------------------------------ */ -int count6bits(msk) -u_32_t *msk; +# ifdef USE_INET6 +int +count6bits(msk) + u_32_t *msk; { int i = 0, k; u_32_t j; @@ -3730,8 +3997,8 @@ u_32_t *msk; /* ------------------------------------------------------------------------ */ -/* Function: frsynclist */ -/* Returns: void */ +/* Function: ipf_synclist */ +/* Returns: int - 0 = no failures, else indication of first failure */ /* Parameters: fr(I) - start of filter list to sync interface names for */ /* ifp(I) - interface pointer for limiting sync lookups */ /* Write Locks: ipf_mutex */ @@ -3740,16 +4007,33 @@ u_32_t *msk; /* pointers. Where dynamic addresses are used, also update the IP address */ /* used in the rule. The interface pointer is used to limit the lookups to */ /* a specific set of matching names if it is non-NULL. */ +/* Errors can occur when resolving the destination name of to/dup-to fields */ +/* when the name points to a pool and that pool doest not exist. If this */ +/* does happen then it is necessary to check if there are any lookup refs */ +/* that need to be dropped before returning with an error. */ /* ------------------------------------------------------------------------ */ -static void frsynclist(fr, ifp) -frentry_t *fr; -void *ifp; +static int +ipf_synclist(softc, fr, ifp) + ipf_main_softc_t *softc; + frentry_t *fr; + void *ifp; { + frentry_t *frt, *start = fr; frdest_t *fdp; + char *name; + int error; + void *ifa; int v, i; + error = 0; + for (; fr; fr = fr->fr_next) { - v = fr->fr_v; + if (fr->fr_family == AF_INET) + v = 4; + else if (fr->fr_family == AF_INET6) + v = 6; + else + v = 0; /* * Lookup all the interface names that are part of the rule. @@ -3757,102 +4041,124 @@ void *ifp; for (i = 0; i < 4; i++) { if ((ifp != NULL) && (fr->fr_ifas[i] != ifp)) continue; - fr->fr_ifas[i] = fr_resolvenic(fr->fr_ifnames[i], v); + if (fr->fr_ifnames[i] == -1) + continue; + name = FR_NAME(fr, fr_ifnames[i]); + fr->fr_ifas[i] = ipf_resolvenic(softc, name, v); } - if (fr->fr_type == FR_T_IPF) { + if ((fr->fr_type & ~FR_T_BUILTIN) == FR_T_IPF) { if (fr->fr_satype != FRI_NORMAL && fr->fr_satype != FRI_LOOKUP) { - (void)fr_ifpaddr(v, fr->fr_satype, - fr->fr_ifas[fr->fr_sifpidx], - &fr->fr_src, &fr->fr_smsk); + ifa = ipf_resolvenic(softc, fr->fr_names + + fr->fr_sifpidx, v); + ipf_ifpaddr(softc, v, fr->fr_satype, ifa, + &fr->fr_src6, &fr->fr_smsk6); } if (fr->fr_datype != FRI_NORMAL && fr->fr_datype != FRI_LOOKUP) { - (void)fr_ifpaddr(v, fr->fr_datype, - fr->fr_ifas[fr->fr_difpidx], - &fr->fr_dst, &fr->fr_dmsk); + ifa = ipf_resolvenic(softc, fr->fr_names + + fr->fr_sifpidx, v); + ipf_ifpaddr(softc, v, fr->fr_datype, ifa, + &fr->fr_dst6, &fr->fr_dmsk6); } } fdp = &fr->fr_tifs[0]; - if ((ifp == NULL) || (fdp->fd_ifp == ifp)) - fr_resolvedest(fdp, v); + if ((ifp == NULL) || (fdp->fd_ptr == ifp)) { + error = ipf_resolvedest(softc, fr->fr_names, fdp, v); + if (error != 0) + goto unwind; + } fdp = &fr->fr_tifs[1]; - if ((ifp == NULL) || (fdp->fd_ifp == ifp)) - fr_resolvedest(fdp, v); + if ((ifp == NULL) || (fdp->fd_ptr == ifp)) { + error = ipf_resolvedest(softc, fr->fr_names, fdp, v); + if (error != 0) + goto unwind; + } fdp = &fr->fr_dif; - if ((ifp == NULL) || (fdp->fd_ifp == ifp)) { - fr_resolvedest(fdp, v); - - fr->fr_flags &= ~FR_DUP; - if ((fdp->fd_ifp != (void *)-1) && - (fdp->fd_ifp != NULL)) - fr->fr_flags |= FR_DUP; + if ((ifp == NULL) || (fdp->fd_ptr == ifp)) { + error = ipf_resolvedest(softc, fr->fr_names, fdp, v); + if (error != 0) + goto unwind; } -#ifdef IPFILTER_LOOKUP - if (fr->fr_type == FR_T_IPF && fr->fr_satype == FRI_LOOKUP && - fr->fr_srcptr == NULL) { - fr->fr_srcptr = fr_resolvelookup(fr->fr_srctype, - fr->fr_srcsubtype, - &fr->fr_slookup, - &fr->fr_srcfunc); + if (((fr->fr_type & ~FR_T_BUILTIN) == FR_T_IPF) && + (fr->fr_satype == FRI_LOOKUP) && (fr->fr_srcptr == NULL)) { + fr->fr_srcptr = ipf_lookup_res_num(softc, + fr->fr_srctype, + IPL_LOGIPF, + fr->fr_srcnum, + &fr->fr_srcfunc); } - if (fr->fr_type == FR_T_IPF && fr->fr_datype == FRI_LOOKUP && - fr->fr_dstptr == NULL) { - fr->fr_dstptr = fr_resolvelookup(fr->fr_dsttype, - fr->fr_dstsubtype, - &fr->fr_dlookup, - &fr->fr_dstfunc); + if (((fr->fr_type & ~FR_T_BUILTIN) == FR_T_IPF) && + (fr->fr_datype == FRI_LOOKUP) && (fr->fr_dstptr == NULL)) { + fr->fr_dstptr = ipf_lookup_res_num(softc, + fr->fr_dsttype, + IPL_LOGIPF, + fr->fr_dstnum, + &fr->fr_dstfunc); } -#endif } + return 0; + +unwind: + for (frt = start; frt != fr; fr = fr->fr_next) { + if (((frt->fr_type & ~FR_T_BUILTIN) == FR_T_IPF) && + (frt->fr_satype == FRI_LOOKUP) && (frt->fr_srcptr != NULL)) + ipf_lookup_deref(softc, frt->fr_srctype, + frt->fr_srcptr); + if (((frt->fr_type & ~FR_T_BUILTIN) == FR_T_IPF) && + (frt->fr_datype == FRI_LOOKUP) && (frt->fr_dstptr != NULL)) + ipf_lookup_deref(softc, frt->fr_dsttype, + frt->fr_dstptr); + } + return error; } -#ifdef _KERNEL /* ------------------------------------------------------------------------ */ -/* Function: frsync */ +/* Function: ipf_sync */ /* Returns: void */ /* Parameters: Nil */ /* */ -/* frsync() is called when we suspect that the interface list or */ +/* ipf_sync() is called when we suspect that the interface list or */ /* information about interfaces (like IP#) has changed. Go through all */ /* filter rules, NAT entries and the state table and check if anything */ /* needs to be changed/updated. */ /* ------------------------------------------------------------------------ */ -void frsync(ifp) -void *ifp; +int +ipf_sync(softc, ifp) + ipf_main_softc_t *softc; + void *ifp; { int i; # if !SOLARIS - fr_natsync(ifp); - fr_statesync(ifp); + ipf_nat_sync(softc, ifp); + ipf_state_sync(softc, ifp); + ipf_lookup_sync(softc, ifp); # endif - WRITE_ENTER(&ipf_mutex); - frsynclist(ipacct[0][fr_active], ifp); - frsynclist(ipacct[1][fr_active], ifp); - frsynclist(ipfilter[0][fr_active], ifp); - frsynclist(ipfilter[1][fr_active], ifp); - frsynclist(ipacct6[0][fr_active], ifp); - frsynclist(ipacct6[1][fr_active], ifp); - frsynclist(ipfilter6[0][fr_active], ifp); - frsynclist(ipfilter6[1][fr_active], ifp); + WRITE_ENTER(&softc->ipf_mutex); + (void) ipf_synclist(softc, softc->ipf_acct[0][softc->ipf_active], ifp); + (void) ipf_synclist(softc, softc->ipf_acct[1][softc->ipf_active], ifp); + (void) ipf_synclist(softc, softc->ipf_rules[0][softc->ipf_active], ifp); + (void) ipf_synclist(softc, softc->ipf_rules[1][softc->ipf_active], ifp); for (i = 0; i < IPL_LOGSIZE; i++) { frgroup_t *g; - for (g = ipfgroups[i][0]; g != NULL; g = g->fg_next) - frsynclist(g->fg_start, ifp); - for (g = ipfgroups[i][1]; g != NULL; g = g->fg_next) - frsynclist(g->fg_start, ifp); + for (g = softc->ipf_groups[i][0]; g != NULL; g = g->fg_next) + (void) ipf_synclist(softc, g->fg_start, ifp); + for (g = softc->ipf_groups[i][1]; g != NULL; g = g->fg_next) + (void) ipf_synclist(softc, g->fg_start, ifp); } - RWLOCK_EXIT(&ipf_mutex); + RWLOCK_EXIT(&softc->ipf_mutex); + + return 0; } @@ -3872,9 +4178,11 @@ void *ifp; /* to start copying from (src) and a pointer to where to store it (dst). */ /* NB: src - pointer to user space pointer, dst - kernel space pointer */ /* ------------------------------------------------------------------------ */ -int copyinptr(src, dst, size) -void *src, *dst; -size_t size; +int +copyinptr(softc, src, dst, size) + ipf_main_softc_t *softc; + void *src, *dst; + size_t size; { caddr_t ca; int error; @@ -3887,8 +4195,10 @@ size_t size; bcopy(src, (caddr_t)&ca, sizeof(ca)); # endif error = COPYIN(ca, dst, size); - if (error != 0) + if (error != 0) { + IPFERROR(3); error = EFAULT; + } return error; } @@ -3904,24 +4214,29 @@ size_t size; /* to start copying from (src) and a pointer to where to store it (dst). */ /* NB: src - kernel space pointer, dst - pointer to user space pointer. */ /* ------------------------------------------------------------------------ */ -int copyoutptr(src, dst, size) -void *src, *dst; -size_t size; +int +copyoutptr(softc, src, dst, size) + ipf_main_softc_t *softc; + void *src, *dst; + size_t size; { caddr_t ca; int error; bcopy(dst, (caddr_t)&ca, sizeof(ca)); error = COPYOUT(src, ca, size); - if (error != 0) + if (error != 0) { + IPFERROR(4); error = EFAULT; + } return error; } +#ifdef _KERNEL #endif /* ------------------------------------------------------------------------ */ -/* Function: fr_lock */ +/* Function: ipf_lock */ /* Returns: int - 0 = success, else error */ /* Parameters: data(I) - pointer to lock value to set */ /* lockp(O) - pointer to location to store old lock value */ @@ -3929,9 +4244,10 @@ size_t size; /* Get the new value for the lock integer, set it and return the old value */ /* in *lockp. */ /* ------------------------------------------------------------------------ */ -int fr_lock(data, lockp) -caddr_t data; -int *lockp; +int +ipf_lock(data, lockp) + caddr_t data; + int *lockp; { int arg, err; @@ -3947,51 +4263,78 @@ int *lockp; /* ------------------------------------------------------------------------ */ -/* Function: fr_getstat */ +/* Function: ipf_getstat */ /* Returns: Nil */ -/* Parameters: fiop(I) - pointer to ipfilter stats structure */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* fiop(I) - pointer to ipfilter stats structure */ +/* rev(I) - version claim by program doing ioctl */ /* */ /* Stores a copy of current pointers, counters, etc, in the friostat */ /* structure. */ +/* If IPFILTER_COMPAT is compiled, we pretend to be whatever version the */ +/* program is looking for. This ensure that validation of the version it */ +/* expects will always succeed. Thus kernels with IPFILTER_COMPAT will */ +/* allow older binaries to work but kernels without it will not. */ /* ------------------------------------------------------------------------ */ -void fr_getstat(fiop) -friostat_t *fiop; +/*ARGSUSED*/ +static void +ipf_getstat(softc, fiop, rev) + ipf_main_softc_t *softc; + friostat_t *fiop; + int rev; { - int i, j; + int i; - bcopy((char *)frstats, (char *)fiop->f_st, sizeof(filterstats_t) * 2); - fiop->f_locks[IPL_LOGSTATE] = fr_state_lock; - fiop->f_locks[IPL_LOGNAT] = fr_nat_lock; - fiop->f_locks[IPL_LOGIPF] = fr_frag_lock; - fiop->f_locks[IPL_LOGAUTH] = fr_auth_lock; + bcopy((char *)softc->ipf_stats, (char *)fiop->f_st, + sizeof(ipf_statistics_t) * 2); + fiop->f_locks[IPL_LOGSTATE] = -1; + fiop->f_locks[IPL_LOGNAT] = -1; + fiop->f_locks[IPL_LOGIPF] = -1; + fiop->f_locks[IPL_LOGAUTH] = -1; - for (i = 0; i < 2; i++) - for (j = 0; j < 2; j++) { - fiop->f_ipf[i][j] = ipfilter[i][j]; - fiop->f_acct[i][j] = ipacct[i][j]; - fiop->f_ipf6[i][j] = ipfilter6[i][j]; - fiop->f_acct6[i][j] = ipacct6[i][j]; - } + fiop->f_ipf[0][0] = softc->ipf_rules[0][0]; + fiop->f_acct[0][0] = softc->ipf_acct[0][0]; + fiop->f_ipf[0][1] = softc->ipf_rules[0][1]; + fiop->f_acct[0][1] = softc->ipf_acct[0][1]; + fiop->f_ipf[1][0] = softc->ipf_rules[1][0]; + fiop->f_acct[1][0] = softc->ipf_acct[1][0]; + fiop->f_ipf[1][1] = softc->ipf_rules[1][1]; + fiop->f_acct[1][1] = softc->ipf_acct[1][1]; - fiop->f_ticks = fr_ticks; - fiop->f_active = fr_active; - fiop->f_froute[0] = fr_frouteok[0]; - fiop->f_froute[1] = fr_frouteok[1]; + fiop->f_ticks = softc->ipf_ticks; + fiop->f_active = softc->ipf_active; + fiop->f_froute[0] = softc->ipf_frouteok[0]; + fiop->f_froute[1] = softc->ipf_frouteok[1]; + fiop->f_rb_no_mem = softc->ipf_rb_no_mem; + fiop->f_rb_node_max = softc->ipf_rb_node_max; - fiop->f_running = fr_running; + fiop->f_running = softc->ipf_running; for (i = 0; i < IPL_LOGSIZE; i++) { - fiop->f_groups[i][0] = ipfgroups[i][0]; - fiop->f_groups[i][1] = ipfgroups[i][1]; + fiop->f_groups[i][0] = softc->ipf_groups[i][0]; + fiop->f_groups[i][1] = softc->ipf_groups[i][1]; } #ifdef IPFILTER_LOG + fiop->f_log_ok = ipf_log_logok(softc, IPL_LOGIPF); + fiop->f_log_fail = ipf_log_failures(softc, IPL_LOGIPF); fiop->f_logging = 1; #else + fiop->f_log_ok = 0; + fiop->f_log_fail = 0; fiop->f_logging = 0; #endif - fiop->f_defpass = fr_pass; - fiop->f_features = fr_features; + fiop->f_defpass = softc->ipf_pass; + fiop->f_features = ipf_features; + +#ifdef IPFILTER_COMPAT + sprintf(fiop->f_version, "IP Filter: v%d.%d.%d", + (rev / 1000000) % 100, + (rev / 10000) % 100, + (rev / 100) % 100); +#else + rev = rev; (void) strncpy(fiop->f_version, ipfilter_version, sizeof(fiop->f_version)); +#endif } @@ -4042,7 +4385,7 @@ int icmpreplytype4[ICMP_MAXTYPE + 1]; /* ------------------------------------------------------------------------ */ -/* Function: fr_matchicmpqueryreply */ +/* Function: ipf_matchicmpqueryreply */ /* Returns: int - 1 if "icmp" is a valid reply to "ic" else 0. */ /* Parameters: v(I) - IP protocol version (4 or 6) */ /* ic(I) - ICMP information */ @@ -4053,11 +4396,12 @@ int icmpreplytype4[ICMP_MAXTYPE + 1]; /* reply to one as described by what's in ic. If it is a match, return 1, */ /* else return 0 for no match. */ /* ------------------------------------------------------------------------ */ -int fr_matchicmpqueryreply(v, ic, icmp, rev) -int v; -icmpinfo_t *ic; -icmphdr_t *icmp; -int rev; +int +ipf_matchicmpqueryreply(v, ic, icmp, rev) + int v; + icmpinfo_t *ic; + icmphdr_t *icmp; + int rev; { int ictype; @@ -4091,88 +4435,6 @@ int rev; } -#ifdef IPFILTER_LOOKUP -/* ------------------------------------------------------------------------ */ -/* Function: fr_resolvelookup */ -/* Returns: void * - NULL = failure, else success. */ -/* Parameters: type(I) - type of lookup these parameters are for. */ -/* subtype(I) - whether the info below contains number/name */ -/* info(I) - pointer to name/number of the lookup data */ -/* funcptr(IO) - pointer to pointer for storing IP address */ -/* searching function. */ -/* */ -/* Search for the "table" number passed in amongst those configured for */ -/* that particular type. If the type is recognised then the function to */ -/* call to do the IP address search will be change, regardless of whether */ -/* or not the "table" number exists. */ -/* ------------------------------------------------------------------------ */ -static void *fr_resolvelookup(type, subtype, info, funcptr) -u_int type, subtype; -i6addr_t *info; -lookupfunc_t *funcptr; -{ - char label[FR_GROUPLEN], *name; - iphtable_t *iph; - ip_pool_t *ipo; - void *ptr; - - if (subtype == 0) { -#if defined(SNPRINTF) && defined(_KERNEL) - SNPRINTF(label, sizeof(label), "%u", info->iplookupnum); -#else - (void) sprintf(label, "%u", info->iplookupnum); -#endif - name = label; - } else if (subtype == 1) { - /* - * Because iplookupname is currently only a 12 character - * string and FR_GROUPLEN is 16, copy all of it into the - * label buffer and add on a NULL at the end. - */ - strncpy(label, info->iplookupname, sizeof(info->iplookupname)); - label[sizeof(info->iplookupname)] = '\0'; - name = label; - } else { - return NULL; - } - - READ_ENTER(&ip_poolrw); - - switch (type) - { - case IPLT_POOL : -# if (defined(__osf__) && defined(_KERNEL)) - ptr = NULL; - *funcptr = NULL; -# else - ipo = ip_pool_find(IPL_LOGIPF, name); - ptr = ipo; - if (ipo != NULL) { - ATOMIC_INC32(ipo->ipo_ref); - } - *funcptr = ip_pool_search; -# endif - break; - case IPLT_HASH : - iph = fr_findhtable(IPL_LOGIPF, name); - ptr = iph; - if (iph != NULL) { - ATOMIC_INC32(iph->iph_ref); - } - *funcptr = fr_iphmfindip; - break; - default: - ptr = NULL; - *funcptr = NULL; - break; - } - RWLOCK_EXIT(&ip_poolrw); - - return ptr; -} -#endif - - /* ------------------------------------------------------------------------ */ /* Function: frrequest */ /* Returns: int - 0 == success, > 0 == errno value */ @@ -4190,54 +4452,98 @@ lookupfunc_t *funcptr; /* of the rule structure being loaded. If a rule has user defined timeouts */ /* then make sure they are created and initialised before exiting. */ /* ------------------------------------------------------------------------ */ -int frrequest(unit, req, data, set, makecopy) -int unit; -ioctlcmd_t req; -int set, makecopy; -caddr_t data; +int +frrequest(softc, unit, req, data, set, makecopy) + ipf_main_softc_t *softc; + int unit; + ioctlcmd_t req; + int set, makecopy; + caddr_t data; { + int error = 0, in, family, addrem, need_free = 0; frentry_t frd, *fp, *f, **fprev, **ftail; - int error = 0, in, v; - void *ptr, *uptr; + void *ptr, *uptr, *cptr; u_int *p, *pp; frgroup_t *fg; char *group; + ptr = NULL; + cptr = NULL; fg = NULL; fp = &frd; if (makecopy != 0) { - error = fr_inobj(data, fp, IPFOBJ_FRENTRY); - if (error) - return EFAULT; - if ((fp->fr_flags & FR_T_BUILTIN) != 0) + bzero(fp, sizeof(frd)); + error = ipf_inobj(softc, data, NULL, fp, IPFOBJ_FRENTRY); + if (error) { + return error; + } + if ((fp->fr_type & FR_T_BUILTIN) != 0) { + IPFERROR(6); return EINVAL; + } + KMALLOCS(f, frentry_t *, fp->fr_size); + if (f == NULL) { + IPFERROR(131); + return ENOMEM; + } + bzero(f, fp->fr_size); + error = ipf_inobjsz(softc, data, f, IPFOBJ_FRENTRY, + fp->fr_size); + if (error) { + KFREES(f, fp->fr_size); + return error; + } + + fp = f; + f = NULL; + fp->fr_dnext = NULL; fp->fr_ref = 0; fp->fr_flags |= FR_COPIED; } else { fp = (frentry_t *)data; - if ((fp->fr_type & FR_T_BUILTIN) == 0) + if ((fp->fr_type & FR_T_BUILTIN) == 0) { + IPFERROR(7); return EINVAL; + } fp->fr_flags &= ~FR_COPIED; } if (((fp->fr_dsize == 0) && (fp->fr_data != NULL)) || - ((fp->fr_dsize != 0) && (fp->fr_data == NULL))) - return EINVAL; + ((fp->fr_dsize != 0) && (fp->fr_data == NULL))) { + IPFERROR(8); + error = EINVAL; + goto donenolock; + } - v = fp->fr_v; + family = fp->fr_family; uptr = fp->fr_data; + if (req == (ioctlcmd_t)SIOCINAFR || req == (ioctlcmd_t)SIOCINIFR || + req == (ioctlcmd_t)SIOCADAFR || req == (ioctlcmd_t)SIOCADIFR) + addrem = 0; + else if (req == (ioctlcmd_t)SIOCRMAFR || req == (ioctlcmd_t)SIOCRMIFR) + addrem = 1; + else if (req == (ioctlcmd_t)SIOCZRLST) + addrem = 2; + else { + IPFERROR(9); + error = EINVAL; + goto donenolock; + } + /* * Only filter rules for IPv4 or IPv6 are accepted. */ - if (v == 4) + if (family == AF_INET) { /*EMPTY*/; #ifdef USE_INET6 - else if (v == 6) + } else if (family == AF_INET6) { /*EMPTY*/; #endif - else { - return EINVAL; + } else if (family != 0) { + IPFERROR(10); + error = EINVAL; + goto donenolock; } /* @@ -4246,37 +4552,110 @@ caddr_t data; * rule. */ if ((makecopy == 1) && (fp->fr_func != NULL)) { - if (fr_findfunc(fp->fr_func) == NULL) - return ESRCH; - error = fr_funcinit(fp); - if (error != 0) - return error; + if (ipf_findfunc(fp->fr_func) == NULL) { + IPFERROR(11); + error = ESRCH; + goto donenolock; + } + + if (addrem == 0) { + error = ipf_funcinit(softc, fp); + if (error != 0) + goto donenolock; + } + } + if ((fp->fr_flags & FR_CALLNOW) && + ((fp->fr_func == NULL) || (fp->fr_func == (ipfunc_t)-1))) { + IPFERROR(142); + error = ESRCH; + goto donenolock; + } + if (((fp->fr_flags & FR_CMDMASK) == FR_CALL) && + ((fp->fr_func == NULL) || (fp->fr_func == (ipfunc_t)-1))) { + IPFERROR(143); + error = ESRCH; + goto donenolock; } ptr = NULL; - /* - * Check that the group number does exist and that its use (in/out) - * matches what the rule is. - */ - if (!strncmp(fp->fr_grhead, "0", FR_GROUPLEN)) - *fp->fr_grhead = '\0'; - group = fp->fr_group; - if (!strncmp(group, "0", FR_GROUPLEN)) - *group = '\0'; + cptr = NULL; if (FR_ISACCOUNT(fp->fr_flags)) unit = IPL_LOGCOUNT; - if ((req != (int)SIOCZRLST) && (*group != '\0')) { - fg = fr_findgroup(group, unit, set, NULL); - if (fg == NULL) - return ESRCH; - if (fg->fg_flags == 0) - fg->fg_flags = fp->fr_flags & FR_INOUT; - else if (fg->fg_flags != (fp->fr_flags & FR_INOUT)) - return ESRCH; + /* + * Check that each group name in the rule has a start index that + * is valid. + */ + if (fp->fr_icmphead != -1) { + if ((fp->fr_icmphead < 0) || + (fp->fr_icmphead >= fp->fr_namelen)) { + IPFERROR(136); + error = EINVAL; + goto donenolock; + } + if (!strcmp(FR_NAME(fp, fr_icmphead), "0")) + fp->fr_names[fp->fr_icmphead] = '\0'; } + if (fp->fr_grhead != -1) { + if ((fp->fr_grhead < 0) || + (fp->fr_grhead >= fp->fr_namelen)) { + IPFERROR(137); + error = EINVAL; + goto donenolock; + } + if (!strcmp(FR_NAME(fp, fr_grhead), "0")) + fp->fr_names[fp->fr_grhead] = '\0'; + } + + if (fp->fr_group != -1) { + if ((fp->fr_group < 0) || + (fp->fr_group >= fp->fr_namelen)) { + IPFERROR(138); + error = EINVAL; + goto donenolock; + } + if ((req != (int)SIOCZRLST) && (fp->fr_group != -1)) { + /* + * Allow loading rules that are in groups to cause + * them to be created if they don't already exit. + */ + group = FR_NAME(fp, fr_group); + if (addrem == 0) { + fg = ipf_group_add(softc, group, NULL, + fp->fr_flags, unit, set); + fp->fr_grp = fg; + } else { + fg = ipf_findgroup(softc, group, unit, + set, NULL); + if (fg == NULL) { + IPFERROR(12); + error = ESRCH; + goto donenolock; + } + } + + if (fg->fg_flags == 0) { + fg->fg_flags = fp->fr_flags & FR_INOUT; + } else if (fg->fg_flags != (fp->fr_flags & FR_INOUT)) { + IPFERROR(13); + error = ESRCH; + goto donenolock; + } + } + } else { + /* + * If a rule is going to be part of a group then it does + * not matter whether it is an in or out rule, but if it + * isn't in a group, then it does... + */ + if ((fp->fr_flags & (FR_INQUE|FR_OUTQUE)) == 0) { + IPFERROR(14); + error = EINVAL; + goto donenolock; + } + } in = (fp->fr_flags & FR_INQUE) ? 0 : 1; /* @@ -4284,27 +4663,30 @@ caddr_t data; */ ftail = NULL; fprev = NULL; - if (unit == IPL_LOGAUTH) - fprev = &ipauth; - else if (v == 4) { + if (unit == IPL_LOGAUTH) { + if ((fp->fr_tifs[0].fd_ptr != NULL) || + (fp->fr_tifs[1].fd_ptr != NULL) || + (fp->fr_dif.fd_ptr != NULL) || + (fp->fr_flags & FR_FASTROUTE)) { + softc->ipf_interror = 145; + error = EINVAL; + goto donenolock; + } + fprev = ipf_auth_rulehead(softc); + } else { if (FR_ISACCOUNT(fp->fr_flags)) - fprev = &ipacct[in][set]; + fprev = &softc->ipf_acct[in][set]; else if ((fp->fr_flags & (FR_OUTQUE|FR_INQUE)) != 0) - fprev = &ipfilter[in][set]; - } else if (v == 6) { - if (FR_ISACCOUNT(fp->fr_flags)) - fprev = &ipacct6[in][set]; - else if ((fp->fr_flags & (FR_OUTQUE|FR_INQUE)) != 0) - fprev = &ipfilter6[in][set]; + fprev = &softc->ipf_rules[in][set]; + } + if (fprev == NULL) { + IPFERROR(15); + error = ESRCH; + goto donenolock; } - if (fprev == NULL) - return ESRCH; - if (*group != '\0') { - if (!fg && !(fg = fr_findgroup(group, unit, set, NULL))) - return ESRCH; + if (fg != NULL) fprev = &fg->fg_start; - } /* * Copy in extra data for the rule. @@ -4312,51 +4694,82 @@ caddr_t data; if (fp->fr_dsize != 0) { if (makecopy != 0) { KMALLOCS(ptr, void *, fp->fr_dsize); - if (!ptr) - return ENOMEM; - error = COPYIN(uptr, ptr, fp->fr_dsize); - if (error != 0) - error = EFAULT; + if (ptr == NULL) { + IPFERROR(16); + error = ENOMEM; + goto donenolock; + } + + /* + * The bcopy case is for when the data is appended + * to the rule by ipf_in_compat(). + */ + if (uptr >= (void *)fp && + uptr < (void *)((char *)fp + fp->fr_size)) { + bcopy(uptr, ptr, fp->fr_dsize); + error = 0; + } else { + error = COPYIN(uptr, ptr, fp->fr_dsize); + if (error != 0) { + IPFERROR(17); + error = EFAULT; + goto donenolock; + } + } } else { ptr = uptr; - error = 0; - } - if (error != 0) { - KFREES(ptr, fp->fr_dsize); - return ENOMEM; } fp->fr_data = ptr; - } else + } else { fp->fr_data = NULL; + } /* * Perform per-rule type sanity checks of their members. + * All code after this needs to be aware that allocated memory + * may need to be free'd before exiting. */ switch (fp->fr_type & ~FR_T_BUILTIN) { #if defined(IPFILTER_BPF) case FR_T_BPFOPC : - if (fp->fr_dsize == 0) - return EINVAL; + if (fp->fr_dsize == 0) { + IPFERROR(19); + error = EINVAL; + break; + } if (!bpf_validate(ptr, fp->fr_dsize/sizeof(struct bpf_insn))) { - if (makecopy && fp->fr_data != NULL) { - KFREES(fp->fr_data, fp->fr_dsize); - } - return EINVAL; + IPFERROR(20); + error = EINVAL; + break; } break; #endif case FR_T_IPF : - if (fp->fr_dsize != sizeof(fripf_t)) - return EINVAL; + /* + * Preparation for error case at the bottom of this function. + */ + if (fp->fr_datype == FRI_LOOKUP) + fp->fr_dstptr = NULL; + if (fp->fr_satype == FRI_LOOKUP) + fp->fr_srcptr = NULL; + + if (fp->fr_dsize != sizeof(fripf_t)) { + IPFERROR(21); + error = EINVAL; + break; + } /* * Allowing a rule with both "keep state" and "with oow" is * pointless because adding a state entry to the table will * fail with the out of window (oow) flag set. */ - if ((fp->fr_flags & FR_KEEPSTATE) && (fp->fr_flx & FI_OOW)) - return EINVAL; + if ((fp->fr_flags & FR_KEEPSTATE) && (fp->fr_flx & FI_OOW)) { + IPFERROR(22); + error = EINVAL; + break; + } switch (fp->fr_satype) { @@ -4365,26 +4778,30 @@ caddr_t data; case FRI_NETWORK : case FRI_NETMASKED : case FRI_PEERADDR : - if (fp->fr_sifpidx < 0 || fp->fr_sifpidx > 3) { - if (makecopy && fp->fr_data != NULL) { - KFREES(fp->fr_data, fp->fr_dsize); - } - return EINVAL; + if (fp->fr_sifpidx < 0) { + IPFERROR(23); + error = EINVAL; } break; -#ifdef IPFILTER_LOOKUP case FRI_LOOKUP : - fp->fr_srcptr = fr_resolvelookup(fp->fr_srctype, - fp->fr_srcsubtype, - &fp->fr_slookup, - &fp->fr_srcfunc); - if (fp->fr_srcptr == NULL) - return ESRCH; + fp->fr_srcptr = ipf_findlookup(softc, unit, fp, + &fp->fr_src6, + &fp->fr_smsk6); + if (fp->fr_srcfunc == NULL) { + IPFERROR(132); + error = ESRCH; + break; + } + break; + case FRI_NORMAL : break; -#endif default : + IPFERROR(133); + error = EINVAL; break; } + if (error != 0) + break; switch (fp->fr_datype) { @@ -4393,45 +4810,84 @@ caddr_t data; case FRI_NETWORK : case FRI_NETMASKED : case FRI_PEERADDR : - if (fp->fr_difpidx < 0 || fp->fr_difpidx > 3) { - if (makecopy && fp->fr_data != NULL) { - KFREES(fp->fr_data, fp->fr_dsize); - } - return EINVAL; + if (fp->fr_difpidx < 0) { + IPFERROR(24); + error = EINVAL; } break; -#ifdef IPFILTER_LOOKUP case FRI_LOOKUP : - fp->fr_dstptr = fr_resolvelookup(fp->fr_dsttype, - fp->fr_dstsubtype, - &fp->fr_dlookup, - &fp->fr_dstfunc); - if (fp->fr_dstptr == NULL) - return ESRCH; + fp->fr_dstptr = ipf_findlookup(softc, unit, fp, + &fp->fr_dst6, + &fp->fr_dmsk6); + if (fp->fr_dstfunc == NULL) { + IPFERROR(134); + error = ESRCH; + } + break; + case FRI_NORMAL : break; -#endif default : - break; + IPFERROR(135); + error = EINVAL; } break; + case FR_T_NONE : - break; case FR_T_CALLFUNC : - break; case FR_T_COMPIPF : break; - default : - if (makecopy && fp->fr_data != NULL) { - KFREES(fp->fr_data, fp->fr_dsize); + + case FR_T_IPFEXPR : + if (ipf_matcharray_verify(fp->fr_data, fp->fr_dsize) == -1) { + IPFERROR(25); + error = EINVAL; + } + break; + + default : + IPFERROR(26); + error = EINVAL; + break; + } + if (error != 0) + goto donenolock; + + if (fp->fr_tif.fd_name != -1) { + if ((fp->fr_tif.fd_name < 0) || + (fp->fr_tif.fd_name >= fp->fr_namelen)) { + IPFERROR(139); + error = EINVAL; + goto donenolock; + } + } + + if (fp->fr_dif.fd_name != -1) { + if ((fp->fr_dif.fd_name < 0) || + (fp->fr_dif.fd_name >= fp->fr_namelen)) { + IPFERROR(140); + error = EINVAL; + goto donenolock; + } + } + + if (fp->fr_rif.fd_name != -1) { + if ((fp->fr_rif.fd_name < 0) || + (fp->fr_rif.fd_name >= fp->fr_namelen)) { + IPFERROR(141); + error = EINVAL; + goto donenolock; } - return EINVAL; } /* * Lookup all the interface names that are part of the rule. */ - frsynclist(fp, NULL); + error = ipf_synclist(softc, fp, NULL); + if (error != 0) + goto donenolock; fp->fr_statecnt = 0; + if (fp->fr_srctrack.ht_max_nodes != 0) + ipf_rb_ht_init(&fp->fr_srctrack); /* * Look for an existing matching filter rule, but don't include the @@ -4447,7 +4903,7 @@ caddr_t data; for (p = (u_int *)fp->fr_data; p < pp; p++) fp->fr_cksum += *p; - WRITE_ENTER(&ipf_mutex); + WRITE_ENTER(&softc->ipf_mutex); /* * Now that the filter rule lists are locked, we can walk the @@ -4462,13 +4918,15 @@ caddr_t data; } fprev = ftail; } - bzero((char *)frcache, sizeof(frcache)); for (; (f = *ftail) != NULL; ftail = &f->fr_next) { + DT2(rule_cmp, frentry_t *, fp, frentry_t *, f); if ((fp->fr_cksum != f->fr_cksum) || + (fp->fr_size != f->fr_size) || (f->fr_dsize != fp->fr_dsize)) continue; - if (bcmp((char *)&f->fr_func, (char *)&fp->fr_func, FR_CMPSIZ)) + if (bcmp((char *)&f->fr_func, (char *)&fp->fr_func, + fp->fr_size - offsetof(struct frentry, fr_func)) != 0) continue; if ((!ptr && !f->fr_data) || (ptr && f->fr_data && @@ -4479,10 +4937,11 @@ caddr_t data; /* * If zero'ing statistics, copy current to caller and zero. */ - if (req == (ioctlcmd_t)SIOCZRLST) { - if (f == NULL) + if (addrem == 2) { + if (f == NULL) { + IPFERROR(27); error = ESRCH; - else { + } else { /* * Copy and reduce lock because of impending copyout. * Well we should, but if we do then the atomicity of @@ -4491,22 +4950,24 @@ caddr_t data; * only resets them to 0 if they are successfully * copied out into user space. */ - bcopy((char *)f, (char *)fp, sizeof(*f)); - /* MUTEX_DOWNGRADE(&ipf_mutex); */ + bcopy((char *)f, (char *)fp, f->fr_size); + /* MUTEX_DOWNGRADE(&softc->ipf_mutex); */ /* * When we copy this rule back out, set the data * pointer to be what it was in user space. */ fp->fr_data = uptr; - error = fr_outobj(data, fp, IPFOBJ_FRENTRY); + error = ipf_outobj(softc, data, fp, IPFOBJ_FRENTRY); if (error == 0) { if ((f->fr_dsize != 0) && (uptr != NULL)) error = COPYOUT(f->fr_data, uptr, f->fr_dsize); - if (error != 0) + if (error != 0) { + IPFERROR(28); error = EFAULT; + } if (error == 0) { f->fr_hits = 0; f->fr_bytes = 0; @@ -4514,14 +4975,17 @@ caddr_t data; } } - if ((ptr != NULL) && (makecopy != 0)) { - KFREES(ptr, fp->fr_dsize); + if (makecopy != 0) { + if (ptr != NULL) { + KFREES(ptr, fp->fr_dsize); + } + KFREES(fp, fp->fr_size); } - RWLOCK_EXIT(&ipf_mutex); + RWLOCK_EXIT(&softc->ipf_mutex); return error; } - if (!f) { + if (!f) { /* * At the end of this, ftail must point to the place where the * new rule is to be saved/inserted/added. @@ -4539,7 +5003,6 @@ caddr_t data; } f = NULL; ptr = NULL; - error = 0; } else if (req == (ioctlcmd_t)SIOCINAFR || req == (ioctlcmd_t)SIOCINIFR) { while ((f = *fprev) != NULL) { @@ -4547,34 +5010,35 @@ caddr_t data; break; fprev = &f->fr_next; } - ftail = fprev; - if (fp->fr_hits != 0) { + ftail = fprev; + if (fp->fr_hits != 0) { while (fp->fr_hits && (f = *ftail)) { if (f->fr_collect != fp->fr_collect) break; fprev = ftail; - ftail = &f->fr_next; + ftail = &f->fr_next; fp->fr_hits--; } - } - f = NULL; - ptr = NULL; - error = 0; + } + f = NULL; + ptr = NULL; } } /* * Request to remove a rule. */ - if (req == (ioctlcmd_t)SIOCRMAFR || req == (ioctlcmd_t)SIOCRMIFR) { - if (!f) + if (addrem == 1) { + if (!f) { + IPFERROR(29); error = ESRCH; - else { + } else { /* * Do not allow activity from user space to interfere * with rules not loaded that way. */ if ((makecopy == 1) && !(f->fr_flags & FR_COPIED)) { + IPFERROR(30); error = EPERM; goto done; } @@ -4584,101 +5048,265 @@ caddr_t data; * something else (eg state information.) */ if (f->fr_ref > 1) { + IPFERROR(31); error = EBUSY; goto done; } #ifdef IPFILTER_SCAN - if (f->fr_isctag[0] != '\0' && + if (f->fr_isctag != -1 && (f->fr_isc != (struct ipscan *)-1)) - ipsc_detachfr(f); + ipf_scan_detachfr(f); #endif + if (unit == IPL_LOGAUTH) { - error = fr_preauthcmd(req, f, ftail); + error = ipf_auth_precmd(softc, req, f, ftail); goto done; } - if (*f->fr_grhead != '\0') - fr_delgroup(f->fr_grhead, unit, set); - fr_fixskip(ftail, f, -1); - *ftail = f->fr_next; - f->fr_next = NULL; - (void) fr_derefrule(&f); + + ipf_rule_delete(softc, f, unit, set); + + need_free = makecopy; } } else { /* * Not removing, so we must be adding/inserting a rule. */ - if (f) + if (f != NULL) { + IPFERROR(32); error = EEXIST; - else { - if (unit == IPL_LOGAUTH) { - error = fr_preauthcmd(req, fp, ftail); - goto done; - } - if (makecopy) { - KMALLOC(f, frentry_t *); - } else - f = fp; - if (f != NULL) { - if (fp != f) - bcopy((char *)fp, (char *)f, - sizeof(*f)); - MUTEX_NUKE(&f->fr_lock); - MUTEX_INIT(&f->fr_lock, "filter rule lock"); -#ifdef IPFILTER_SCAN - if (f->fr_isctag[0] != '\0' && - ipsc_attachfr(f)) - f->fr_isc = (struct ipscan *)-1; -#endif - f->fr_hits = 0; - if (makecopy != 0) - f->fr_ref = 1; - f->fr_next = *ftail; - *ftail = f; - if (req == (ioctlcmd_t)SIOCINIFR || - req == (ioctlcmd_t)SIOCINAFR) - fr_fixskip(ftail, f, 1); - f->fr_grp = NULL; - group = f->fr_grhead; - if (*group != '\0') { - fg = fr_addgroup(group, f, f->fr_flags, - unit, set); - if (fg != NULL) - f->fr_grp = &fg->fg_start; - } - } else - error = ENOMEM; + goto done; + } + if (unit == IPL_LOGAUTH) { + error = ipf_auth_precmd(softc, req, fp, ftail); + goto done; + } + + MUTEX_NUKE(&fp->fr_lock); + MUTEX_INIT(&fp->fr_lock, "filter rule lock"); + if (fp->fr_die != 0) + ipf_rule_expire_insert(softc, fp, set); + + fp->fr_hits = 0; + if (makecopy != 0) + fp->fr_ref = 1; + fp->fr_pnext = ftail; + fp->fr_next = *ftail; + *ftail = fp; + if (addrem == 0) + ipf_fixskip(ftail, fp, 1); + + fp->fr_icmpgrp = NULL; + if (fp->fr_icmphead != -1) { + group = FR_NAME(fp, fr_icmphead); + fg = ipf_group_add(softc, group, fp, 0, unit, set); + fp->fr_icmpgrp = fg; + } + + fp->fr_grphead = NULL; + if (fp->fr_grhead != -1) { + group = FR_NAME(fp, fr_grhead); + fg = ipf_group_add(softc, group, fp, fp->fr_flags, + unit, set); + fp->fr_grphead = fg; } } done: - RWLOCK_EXIT(&ipf_mutex); - if ((ptr != NULL) && (error != 0) && (makecopy != 0)) { - KFREES(ptr, fp->fr_dsize); + RWLOCK_EXIT(&softc->ipf_mutex); +donenolock: + if (need_free || (error != 0)) { + if ((fp->fr_type & ~FR_T_BUILTIN) == FR_T_IPF) { + if ((fp->fr_satype == FRI_LOOKUP) && + (fp->fr_srcptr != NULL)) + ipf_lookup_deref(softc, fp->fr_srctype, + fp->fr_srcptr); + if ((fp->fr_datype == FRI_LOOKUP) && + (fp->fr_dstptr != NULL)) + ipf_lookup_deref(softc, fp->fr_dsttype, + fp->fr_dstptr); + } + if (fp->fr_grp != NULL) { + WRITE_ENTER(&softc->ipf_mutex); + ipf_group_del(softc, fp->fr_grp, fp); + RWLOCK_EXIT(&softc->ipf_mutex); + } + if ((ptr != NULL) && (makecopy != 0)) { + KFREES(ptr, fp->fr_dsize); + } + KFREES(fp, fp->fr_size); } return (error); } /* ------------------------------------------------------------------------ */ -/* Function: fr_funcinit */ +/* Function: ipf_rule_delete */ +/* Returns: Nil */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* f(I) - pointer to the rule being deleted */ +/* ftail(I) - pointer to the pointer to f */ +/* unit(I) - device for which this is for */ +/* set(I) - 1 or 0 (filter set) */ +/* */ +/* This function attempts to do what it can to delete a filter rule: remove */ +/* it from any linked lists and remove any groups it is responsible for. */ +/* But in the end, removing a rule can only drop the reference count - we */ +/* must use that as the guide for whether or not it can be freed. */ +/* ------------------------------------------------------------------------ */ +static void +ipf_rule_delete(softc, f, unit, set) + ipf_main_softc_t *softc; + frentry_t *f; + int unit, set; +{ + + /* + * If fr_pdnext is set, then the rule is on the expire list, so + * remove it from there. + */ + if (f->fr_pdnext != NULL) { + *f->fr_pdnext = f->fr_dnext; + if (f->fr_dnext != NULL) + f->fr_dnext->fr_pdnext = f->fr_pdnext; + f->fr_pdnext = NULL; + f->fr_dnext = NULL; + } + + ipf_fixskip(f->fr_pnext, f, -1); + if (f->fr_pnext != NULL) + *f->fr_pnext = f->fr_next; + if (f->fr_next != NULL) + f->fr_next->fr_pnext = f->fr_pnext; + f->fr_pnext = NULL; + f->fr_next = NULL; + + (void) ipf_derefrule(softc, &f); +} + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_rule_expire_insert */ +/* Returns: Nil */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* f(I) - pointer to rule to be added to expire list */ +/* set(I) - 1 or 0 (filter set) */ +/* */ +/* If the new rule has a given expiration time, insert it into the list of */ +/* expiring rules with the ones to be removed first added to the front of */ +/* the list. The insertion is O(n) but it is kept sorted for quick scans at */ +/* expiration interval checks. */ +/* ------------------------------------------------------------------------ */ +static void +ipf_rule_expire_insert(softc, f, set) + ipf_main_softc_t *softc; + frentry_t *f; + int set; +{ + frentry_t *fr; + + /* + */ + + f->fr_die = softc->ipf_ticks + IPF_TTLVAL(f->fr_die); + for (fr = softc->ipf_rule_explist[set]; fr != NULL; + fr = fr->fr_dnext) { + if (f->fr_die < fr->fr_die) + break; + if (fr->fr_dnext == NULL) { + /* + * We've got to the last rule and everything + * wanted to be expired before this new node, + * so we have to tack it on the end... + */ + fr->fr_dnext = f; + f->fr_pdnext = &fr->fr_dnext; + fr = NULL; + break; + } + } + + if (softc->ipf_rule_explist[set] == NULL) { + softc->ipf_rule_explist[set] = f; + f->fr_pdnext = &softc->ipf_rule_explist[set]; + } else if (fr != NULL) { + f->fr_dnext = fr; + f->fr_pdnext = fr->fr_pdnext; + fr->fr_pdnext = &f->fr_dnext; + } +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_findlookup */ +/* Returns: NULL = failure, else success */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* unit(I) - ipf device we want to find match for */ +/* fp(I) - rule for which lookup is for */ +/* addrp(I) - pointer to lookup information in address struct */ +/* maskp(O) - pointer to lookup information for storage */ +/* */ +/* When using pools and hash tables to store addresses for matching in */ +/* rules, it is necessary to resolve both the object referred to by the */ +/* name or address (and return that pointer) and also provide the means by */ +/* which to determine if an address belongs to that object to make the */ +/* packet matching quicker. */ +/* ------------------------------------------------------------------------ */ +static void * +ipf_findlookup(softc, unit, fr, addrp, maskp) + ipf_main_softc_t *softc; + int unit; + frentry_t *fr; + i6addr_t *addrp, *maskp; +{ + void *ptr = NULL; + + switch (addrp->iplookupsubtype) + { + case 0 : + ptr = ipf_lookup_res_num(softc, unit, addrp->iplookuptype, + addrp->iplookupnum, + &maskp->iplookupfunc); + break; + case 1 : + if (addrp->iplookupname < 0) + break; + if (addrp->iplookupname >= fr->fr_namelen) + break; + ptr = ipf_lookup_res_name(softc, unit, addrp->iplookuptype, + fr->fr_names + addrp->iplookupname, + &maskp->iplookupfunc); + break; + default : + break; + } + + return ptr; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_funcinit */ /* Returns: int - 0 == success, else ESRCH: cannot resolve rule details */ -/* Parameters: fr(I) - pointer to filter rule */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* fr(I) - pointer to filter rule */ /* */ /* If a rule is a call rule, then check if the function it points to needs */ /* an init function to be called now the rule has been loaded. */ /* ------------------------------------------------------------------------ */ -static int fr_funcinit(fr) -frentry_t *fr; +static int +ipf_funcinit(softc, fr) + ipf_main_softc_t *softc; + frentry_t *fr; { ipfunc_resolve_t *ft; int err; + IPFERROR(34); err = ESRCH; - for (ft = fr_availfuncs; ft->ipfu_addr != NULL; ft++) + for (ft = ipf_availfuncs; ft->ipfu_addr != NULL; ft++) if (ft->ipfu_addr == fr->fr_func) { err = 0; if (ft->ipfu_init != NULL) - err = (*ft->ipfu_init)(fr); + err = (*ft->ipfu_init)(softc, fr); break; } return err; @@ -4686,18 +5314,45 @@ frentry_t *fr; /* ------------------------------------------------------------------------ */ -/* Function: fr_findfunc */ +/* Function: ipf_funcfini */ +/* Returns: Nil */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* fr(I) - pointer to filter rule */ +/* */ +/* For a given filter rule, call the matching "fini" function if the rule */ +/* is using a known function that would have resulted in the "init" being */ +/* called for ealier. */ +/* ------------------------------------------------------------------------ */ +static void +ipf_funcfini(softc, fr) + ipf_main_softc_t *softc; + frentry_t *fr; +{ + ipfunc_resolve_t *ft; + + for (ft = ipf_availfuncs; ft->ipfu_addr != NULL; ft++) + if (ft->ipfu_addr == fr->fr_func) { + if (ft->ipfu_fini != NULL) + (void) (*ft->ipfu_fini)(softc, fr); + break; + } +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_findfunc */ /* Returns: ipfunc_t - pointer to function if found, else NULL */ /* Parameters: funcptr(I) - function pointer to lookup */ /* */ /* Look for a function in the table of known functions. */ /* ------------------------------------------------------------------------ */ -static ipfunc_t fr_findfunc(funcptr) -ipfunc_t funcptr; +static ipfunc_t +ipf_findfunc(funcptr) + ipfunc_t funcptr; { ipfunc_resolve_t *ft; - for (ft = fr_availfuncs; ft->ipfu_addr != NULL; ft++) + for (ft = ipf_availfuncs; ft->ipfu_addr != NULL; ft++) if (ft->ipfu_addr == funcptr) return funcptr; return NULL; @@ -4705,7 +5360,7 @@ ipfunc_t funcptr; /* ------------------------------------------------------------------------ */ -/* Function: fr_resolvefunc */ +/* Function: ipf_resolvefunc */ /* Returns: int - 0 == success, else error */ /* Parameters: data(IO) - ioctl data pointer to ipfunc_resolve_t struct */ /* */ @@ -4714,46 +5369,55 @@ ipfunc_t funcptr; /* function pointer if the name is set. When found, fill in the other one */ /* so that the entire, complete, structure can be copied back to user space.*/ /* ------------------------------------------------------------------------ */ -int fr_resolvefunc(data) -void *data; +int +ipf_resolvefunc(softc, data) + ipf_main_softc_t *softc; + void *data; { ipfunc_resolve_t res, *ft; - int err; + int error; - err = BCOPYIN(data, &res, sizeof(res)); - if (err != 0) + error = BCOPYIN(data, &res, sizeof(res)); + if (error != 0) { + IPFERROR(123); return EFAULT; + } if (res.ipfu_addr == NULL && res.ipfu_name[0] != '\0') { - for (ft = fr_availfuncs; ft->ipfu_addr != NULL; ft++) + for (ft = ipf_availfuncs; ft->ipfu_addr != NULL; ft++) if (strncmp(res.ipfu_name, ft->ipfu_name, sizeof(res.ipfu_name)) == 0) { res.ipfu_addr = ft->ipfu_addr; res.ipfu_init = ft->ipfu_init; - if (COPYOUT(&res, data, sizeof(res)) != 0) + if (COPYOUT(&res, data, sizeof(res)) != 0) { + IPFERROR(35); return EFAULT; + } return 0; } } if (res.ipfu_addr != NULL && res.ipfu_name[0] == '\0') { - for (ft = fr_availfuncs; ft->ipfu_addr != NULL; ft++) + for (ft = ipf_availfuncs; ft->ipfu_addr != NULL; ft++) if (ft->ipfu_addr == res.ipfu_addr) { (void) strncpy(res.ipfu_name, ft->ipfu_name, sizeof(res.ipfu_name)); res.ipfu_init = ft->ipfu_init; - if (COPYOUT(&res, data, sizeof(res)) != 0) + if (COPYOUT(&res, data, sizeof(res)) != 0) { + IPFERROR(36); return EFAULT; + } return 0; } } + IPFERROR(37); return ESRCH; } -#if !defined(_KERNEL) || (!defined(__NetBSD__) && !defined(__OpenBSD__) && !defined(__FreeBSD__)) || \ - (defined(__FreeBSD__) && (__FreeBSD_version < 501000)) || \ - (defined(__NetBSD__) && (__NetBSD_Version__ < 105000000)) || \ - (defined(__OpenBSD__) && (OpenBSD < 200006)) +#if !defined(_KERNEL) || (!defined(__NetBSD__) && !defined(__OpenBSD__) && \ + !defined(__FreeBSD__)) || \ + FREEBSD_LT_REV(501000) || NETBSD_LT_REV(105000000) || \ + OPENBSD_LT_REV(200006) /* * From: NetBSD * ppsratecheck(): packets (or events) per second limitation. @@ -4803,17 +5467,20 @@ ppsratecheck(lasttime, curpps, maxpps) /* ------------------------------------------------------------------------ */ -/* Function: fr_derefrule */ +/* Function: ipf_derefrule */ /* Returns: int - 0 == rule freed up, else rule not freed */ /* Parameters: fr(I) - pointer to filter rule */ /* */ /* Decrement the reference counter to a rule by one. If it reaches zero, */ /* free it and any associated storage space being used by it. */ /* ------------------------------------------------------------------------ */ -int fr_derefrule(frp) -frentry_t **frp; +int +ipf_derefrule(softc, frp) + ipf_main_softc_t *softc; + frentry_t **frp; { frentry_t *fr; + frdest_t *fdp; fr = *frp; *frp = NULL; @@ -4824,18 +5491,41 @@ frentry_t **frp; MUTEX_EXIT(&fr->fr_lock); MUTEX_DESTROY(&fr->fr_lock); -#ifdef IPFILTER_LOOKUP - if (fr->fr_type == FR_T_IPF && fr->fr_satype == FRI_LOOKUP) - ip_lookup_deref(fr->fr_srctype, fr->fr_srcptr); - if (fr->fr_type == FR_T_IPF && fr->fr_datype == FRI_LOOKUP) - ip_lookup_deref(fr->fr_dsttype, fr->fr_dstptr); -#endif + ipf_funcfini(softc, fr); + + fdp = &fr->fr_tif; + if (fdp->fd_type == FRD_DSTLIST) + ipf_lookup_deref(softc, IPLT_DSTLIST, fdp->fd_ptr); + + fdp = &fr->fr_rif; + if (fdp->fd_type == FRD_DSTLIST) + ipf_lookup_deref(softc, IPLT_DSTLIST, fdp->fd_ptr); + + fdp = &fr->fr_dif; + if (fdp->fd_type == FRD_DSTLIST) + ipf_lookup_deref(softc, IPLT_DSTLIST, fdp->fd_ptr); + + if ((fr->fr_type & ~FR_T_BUILTIN) == FR_T_IPF && + fr->fr_satype == FRI_LOOKUP) + ipf_lookup_deref(softc, fr->fr_srctype, fr->fr_srcptr); + if ((fr->fr_type & ~FR_T_BUILTIN) == FR_T_IPF && + fr->fr_datype == FRI_LOOKUP) + ipf_lookup_deref(softc, fr->fr_dsttype, fr->fr_dstptr); + + if (fr->fr_grp != NULL) + ipf_group_del(softc, fr->fr_grp, fr); + + if (fr->fr_grphead != NULL) + ipf_group_del(softc, fr->fr_grphead, fr); + + if (fr->fr_icmpgrp != NULL) + ipf_group_del(softc, fr->fr_icmpgrp, fr); - if (fr->fr_dsize) { - KFREES(fr->fr_data, fr->fr_dsize); - } if ((fr->fr_flags & FR_COPIED) != 0) { - KFREE(fr); + if (fr->fr_dsize) { + KFREES(fr->fr_data, fr->fr_dsize); + } + KFREES(fr, fr->fr_size); return 0; } return 1; @@ -4846,17 +5536,18 @@ frentry_t **frp; } -#ifdef IPFILTER_LOOKUP /* ------------------------------------------------------------------------ */ -/* Function: fr_grpmapinit */ +/* Function: ipf_grpmapinit */ /* Returns: int - 0 == success, else ESRCH because table entry not found*/ /* Parameters: fr(I) - pointer to rule to find hash table for */ /* */ /* Looks for group hash table fr_arg and stores a pointer to it in fr_ptr. */ -/* fr_ptr is later used by fr_srcgrpmap and fr_dstgrpmap. */ +/* fr_ptr is later used by ipf_srcgrpmap and ipf_dstgrpmap. */ /* ------------------------------------------------------------------------ */ -static int fr_grpmapinit(fr) -frentry_t *fr; +static int +ipf_grpmapinit(softc, fr) + ipf_main_softc_t *softc; + frentry_t *fr; { char name[FR_GROUPLEN]; iphtable_t *iph; @@ -4866,18 +5557,45 @@ frentry_t *fr; #else (void) sprintf(name, "%d", fr->fr_arg); #endif - iph = fr_findhtable(IPL_LOGIPF, name); - if (iph == NULL) + iph = ipf_lookup_find_htable(softc, IPL_LOGIPF, name); + if (iph == NULL) { + IPFERROR(38); return ESRCH; - if ((iph->iph_flags & FR_INOUT) != (fr->fr_flags & FR_INOUT)) + } + if ((iph->iph_flags & FR_INOUT) != (fr->fr_flags & FR_INOUT)) { + IPFERROR(39); return ESRCH; + } + iph->iph_ref++; fr->fr_ptr = iph; return 0; } /* ------------------------------------------------------------------------ */ -/* Function: fr_srcgrpmap */ +/* Function: ipf_grpmapfini */ +/* Returns: int - 0 == success, else ESRCH because table entry not found*/ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* fr(I) - pointer to rule to release hash table for */ +/* */ +/* For rules that have had ipf_grpmapinit called, ipf_lookup_deref needs to */ +/* be called to undo what ipf_grpmapinit caused to be done. */ +/* ------------------------------------------------------------------------ */ +static int +ipf_grpmapfini(softc, fr) + ipf_main_softc_t *softc; + frentry_t *fr; +{ + iphtable_t *iph; + iph = fr->fr_ptr; + if (iph != NULL) + ipf_lookup_deref(softc, IPLT_HASH, iph); + return 0; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_srcgrpmap */ /* Returns: frentry_t * - pointer to "new last matching" rule or NULL */ /* Parameters: fin(I) - pointer to packet information */ /* passp(IO) - pointer to current/new filter decision (unused) */ @@ -4886,26 +5604,28 @@ frentry_t *fr; /* the key, and descend into that group and continue matching rules against */ /* the packet. */ /* ------------------------------------------------------------------------ */ -frentry_t *fr_srcgrpmap(fin, passp) -fr_info_t *fin; -u_32_t *passp; +frentry_t * +ipf_srcgrpmap(fin, passp) + fr_info_t *fin; + u_32_t *passp; { frgroup_t *fg; void *rval; - rval = fr_iphmfindgroup(fin->fin_fr->fr_ptr, &fin->fin_src); + rval = ipf_iphmfindgroup(fin->fin_main_soft, fin->fin_fr->fr_ptr, + &fin->fin_src); if (rval == NULL) return NULL; fg = rval; fin->fin_fr = fg->fg_start; - (void) fr_scanlist(fin, *passp); + (void) ipf_scanlist(fin, *passp); return fin->fin_fr; } /* ------------------------------------------------------------------------ */ -/* Function: fr_dstgrpmap */ +/* Function: ipf_dstgrpmap */ /* Returns: frentry_t * - pointer to "new last matching" rule or NULL */ /* Parameters: fin(I) - pointer to packet information */ /* passp(IO) - pointer to current/new filter decision (unused) */ @@ -4914,37 +5634,39 @@ u_32_t *passp; /* address as the key, and descend into that group and continue matching */ /* rules against the packet. */ /* ------------------------------------------------------------------------ */ -frentry_t *fr_dstgrpmap(fin, passp) -fr_info_t *fin; -u_32_t *passp; +frentry_t * +ipf_dstgrpmap(fin, passp) + fr_info_t *fin; + u_32_t *passp; { frgroup_t *fg; void *rval; - rval = fr_iphmfindgroup(fin->fin_fr->fr_ptr, &fin->fin_dst); + rval = ipf_iphmfindgroup(fin->fin_main_soft, fin->fin_fr->fr_ptr, + &fin->fin_dst); if (rval == NULL) return NULL; fg = rval; fin->fin_fr = fg->fg_start; - (void) fr_scanlist(fin, *passp); + (void) ipf_scanlist(fin, *passp); return fin->fin_fr; } -#endif /* IPFILTER_LOOKUP */ /* * Queue functions * =============== - * These functions manage objects on queues for efficient timeouts. There are - * a number of system defined queues as well as user defined timeouts. It is - * expected that a lock is held in the domain in which the queue belongs - * (i.e. either state or NAT) when calling any of these functions that prevents - * fr_freetimeoutqueue() from being called at the same time as any other. + * These functions manage objects on queues for efficient timeouts. There + * are a number of system defined queues as well as user defined timeouts. + * It is expected that a lock is held in the domain in which the queue + * belongs (i.e. either state or NAT) when calling any of these functions + * that prevents ipf_freetimeoutqueue() from being called at the same time + * as any other. */ /* ------------------------------------------------------------------------ */ -/* Function: fr_addtimeoutqueue */ +/* Function: ipf_addtimeoutqueue */ /* Returns: struct ifqtq * - NULL if malloc fails, else pointer to */ /* timeout queue with given interval. */ /* Parameters: parent(I) - pointer to pointer to parent node of this list */ @@ -4960,16 +5682,18 @@ u_32_t *passp; /* It is assumed that the caller of this function has an appropriate lock */ /* held (exclusively) in the domain that encompases 'parent'. */ /* ------------------------------------------------------------------------ */ -ipftq_t *fr_addtimeoutqueue(parent, seconds) -ipftq_t **parent; -u_int seconds; +ipftq_t * +ipf_addtimeoutqueue(softc, parent, seconds) + ipf_main_softc_t *softc; + ipftq_t **parent; + u_int seconds; { ipftq_t *ifq; u_int period; period = seconds * IPF_HZ_DIVIDE; - MUTEX_ENTER(&ipf_timeoutlock); + MUTEX_ENTER(&softc->ipf_timeoutlock); for (ifq = *parent; ifq != NULL; ifq = ifq->ifq_next) { if (ifq->ifq_ttl == period) { /* @@ -4980,7 +5704,7 @@ u_int seconds; ifq->ifq_flags &= ~IFQF_DELETE; ifq->ifq_ref++; MUTEX_EXIT(&ifq->ifq_lock); - MUTEX_EXIT(&ipf_timeoutlock); + MUTEX_EXIT(&softc->ipf_timeoutlock); return ifq; } @@ -4988,25 +5712,22 @@ u_int seconds; KMALLOC(ifq, ipftq_t *); if (ifq != NULL) { - ifq->ifq_ttl = period; - ifq->ifq_head = NULL; - ifq->ifq_tail = &ifq->ifq_head; + MUTEX_NUKE(&ifq->ifq_lock); + IPFTQ_INIT(ifq, period, "ipftq mutex"); ifq->ifq_next = *parent; ifq->ifq_pnext = parent; - ifq->ifq_ref = 1; ifq->ifq_flags = IFQF_USER; + ifq->ifq_ref++; *parent = ifq; - fr_userifqs++; - MUTEX_NUKE(&ifq->ifq_lock); - MUTEX_INIT(&ifq->ifq_lock, "ipftq mutex"); + softc->ipf_userifqs++; } - MUTEX_EXIT(&ipf_timeoutlock); + MUTEX_EXIT(&softc->ipf_timeoutlock); return ifq; } /* ------------------------------------------------------------------------ */ -/* Function: fr_deletetimeoutqueue */ +/* Function: ipf_deletetimeoutqueue */ /* Returns: int - new reference count value of the timeout queue */ /* Parameters: ifq(I) - timeout queue which is losing a reference. */ /* Locks: ifq->ifq_lock */ @@ -5020,8 +5741,9 @@ u_int seconds; /* way because the locking may not be sufficient to safely do a free when */ /* this function is called. */ /* ------------------------------------------------------------------------ */ -int fr_deletetimeoutqueue(ifq) -ipftq_t *ifq; +int +ipf_deletetimeoutqueue(ifq) + ipftq_t *ifq; { ifq->ifq_ref--; @@ -5034,7 +5756,7 @@ ipftq_t *ifq; /* ------------------------------------------------------------------------ */ -/* Function: fr_freetimeoutqueue */ +/* Function: ipf_freetimeoutqueue */ /* Parameters: ifq(I) - timeout queue which is losing a reference. */ /* Returns: Nil */ /* */ @@ -5043,17 +5765,18 @@ ipftq_t *ifq; /* held (exclusively) in the domain that encompases the callers "domain". */ /* The ifq_lock for this structure should not be held. */ /* */ -/* Remove a user definde timeout queue from the list of queues it is in and */ +/* Remove a user defined timeout queue from the list of queues it is in and */ /* tidy up after this is done. */ /* ------------------------------------------------------------------------ */ -void fr_freetimeoutqueue(ifq) -ipftq_t *ifq; +void +ipf_freetimeoutqueue(softc, ifq) + ipf_main_softc_t *softc; + ipftq_t *ifq; { - if (((ifq->ifq_flags & IFQF_DELETE) == 0) || (ifq->ifq_ref != 0) || ((ifq->ifq_flags & IFQF_USER) == 0)) { - printf("fr_freetimeoutqueue(%lx) flags 0x%x ttl %d ref %d\n", + printf("ipf_freetimeoutqueue(%lx) flags 0x%x ttl %d ref %d\n", (u_long)ifq, ifq->ifq_flags, ifq->ifq_ttl, ifq->ifq_ref); return; @@ -5065,26 +5788,28 @@ ipftq_t *ifq; *ifq->ifq_pnext = ifq->ifq_next; if (ifq->ifq_next != NULL) ifq->ifq_next->ifq_pnext = ifq->ifq_pnext; + ifq->ifq_next = NULL; + ifq->ifq_pnext = NULL; MUTEX_DESTROY(&ifq->ifq_lock); - ATOMIC_DEC(fr_userifqs); + ATOMIC_DEC(softc->ipf_userifqs); KFREE(ifq); } /* ------------------------------------------------------------------------ */ -/* Function: fr_deletequeueentry */ +/* Function: ipf_deletequeueentry */ /* Returns: Nil */ /* Parameters: tqe(I) - timeout queue entry to delete */ -/* ifq(I) - timeout queue to remove entry from */ /* */ /* Remove a tail queue entry from its queue and make it an orphan. */ -/* fr_deletetimeoutqueue is called to make sure the reference count on the */ -/* queue is correct. We can't, however, call fr_freetimeoutqueue because */ +/* ipf_deletetimeoutqueue is called to make sure the reference count on the */ +/* queue is correct. We can't, however, call ipf_freetimeoutqueue because */ /* the correct lock(s) may not be held that would make it safe to do so. */ /* ------------------------------------------------------------------------ */ -void fr_deletequeueentry(tqe) -ipftqent_t *tqe; +void +ipf_deletequeueentry(tqe) + ipftqent_t *tqe; { ipftq_t *ifq; @@ -5103,21 +5828,23 @@ ipftqent_t *tqe; tqe->tqe_ifq = NULL; } - (void) fr_deletetimeoutqueue(ifq); + (void) ipf_deletetimeoutqueue(ifq); + ASSERT(ifq->ifq_ref > 0); MUTEX_EXIT(&ifq->ifq_lock); } /* ------------------------------------------------------------------------ */ -/* Function: fr_queuefront */ +/* Function: ipf_queuefront */ /* Returns: Nil */ /* Parameters: tqe(I) - pointer to timeout queue entry */ /* */ /* Move a queue entry to the front of the queue, if it isn't already there. */ /* ------------------------------------------------------------------------ */ -void fr_queuefront(tqe) -ipftqent_t *tqe; +void +ipf_queuefront(tqe) + ipftqent_t *tqe; { ipftq_t *ifq; @@ -5143,21 +5870,27 @@ ipftqent_t *tqe; /* ------------------------------------------------------------------------ */ -/* Function: fr_queueback */ +/* Function: ipf_queueback */ /* Returns: Nil */ -/* Parameters: tqe(I) - pointer to timeout queue entry */ +/* Parameters: ticks(I) - ipf tick time to use with this call */ +/* tqe(I) - pointer to timeout queue entry */ /* */ /* Move a queue entry to the back of the queue, if it isn't already there. */ +/* We use use ticks to calculate the expiration and mark for when we last */ +/* touched the structure. */ /* ------------------------------------------------------------------------ */ -void fr_queueback(tqe) -ipftqent_t *tqe; +void +ipf_queueback(ticks, tqe) + u_long ticks; + ipftqent_t *tqe; { ipftq_t *ifq; ifq = tqe->tqe_ifq; if (ifq == NULL) return; - tqe->tqe_die = fr_ticks + ifq->ifq_ttl; + tqe->tqe_die = ticks + ifq->ifq_ttl; + tqe->tqe_touched = ticks; MUTEX_ENTER(&ifq->ifq_lock); if (tqe->tqe_next != NULL) { /* at the end already ? */ @@ -5180,18 +5913,23 @@ ipftqent_t *tqe; /* ------------------------------------------------------------------------ */ -/* Function: fr_queueappend */ +/* Function: ipf_queueappend */ /* Returns: Nil */ -/* Parameters: tqe(I) - pointer to timeout queue entry */ +/* Parameters: ticks(I) - ipf tick time to use with this call */ +/* tqe(I) - pointer to timeout queue entry */ /* ifq(I) - pointer to timeout queue */ /* parent(I) - owing object pointer */ /* */ /* Add a new item to this queue and put it on the very end. */ +/* We use use ticks to calculate the expiration and mark for when we last */ +/* touched the structure. */ /* ------------------------------------------------------------------------ */ -void fr_queueappend(tqe, ifq, parent) -ipftqent_t *tqe; -ipftq_t *ifq; -void *parent; +void +ipf_queueappend(ticks, tqe, ifq, parent) + u_long ticks; + ipftqent_t *tqe; + ipftq_t *ifq; + void *parent; { MUTEX_ENTER(&ifq->ifq_lock); @@ -5201,14 +5939,15 @@ void *parent; ifq->ifq_tail = &tqe->tqe_next; tqe->tqe_next = NULL; tqe->tqe_ifq = ifq; - tqe->tqe_die = fr_ticks + ifq->ifq_ttl; + tqe->tqe_die = ticks + ifq->ifq_ttl; + tqe->tqe_touched = ticks; ifq->ifq_ref++; MUTEX_EXIT(&ifq->ifq_lock); } /* ------------------------------------------------------------------------ */ -/* Function: fr_movequeue */ +/* Function: ipf_movequeue */ /* Returns: Nil */ /* Parameters: tq(I) - pointer to timeout queue information */ /* oifp(I) - old timeout queue entry was on */ @@ -5218,58 +5957,82 @@ void *parent; /* If it notices that the current entry is already last and does not need */ /* to move queue, the return. */ /* ------------------------------------------------------------------------ */ -void fr_movequeue(tqe, oifq, nifq) -ipftqent_t *tqe; -ipftq_t *oifq, *nifq; +void +ipf_movequeue(ticks, tqe, oifq, nifq) + u_long ticks; + ipftqent_t *tqe; + ipftq_t *oifq, *nifq; { + + /* + * If the queue hasn't changed and we last touched this entry at the + * same ipf time, then we're not going to achieve anything by either + * changing the ttl or moving it on the queue. + */ + if (oifq == nifq && tqe->tqe_touched == ticks) + return; + + /* + * For any of this to be outside the lock, there is a risk that two + * packets entering simultaneously, with one changing to a different + * queue and one not, could end up with things in a bizarre state. + */ + MUTEX_ENTER(&oifq->ifq_lock); + + tqe->tqe_touched = ticks; + tqe->tqe_die = ticks + nifq->ifq_ttl; /* * Is the operation here going to be a no-op ? */ - MUTEX_ENTER(&oifq->ifq_lock); - if ((oifq != nifq) || (*oifq->ifq_tail != tqe)) { - /* - * Remove from the old queue - */ - *tqe->tqe_pnext = tqe->tqe_next; - if (tqe->tqe_next) - tqe->tqe_next->tqe_pnext = tqe->tqe_pnext; - else - oifq->ifq_tail = tqe->tqe_pnext; - tqe->tqe_next = NULL; - - /* - * If we're moving from one queue to another, release the - * lock on the old queue and get a lock on the new queue. - * For user defined queues, if we're moving off it, call - * delete in case it can now be freed. - */ - if (oifq != nifq) { - tqe->tqe_ifq = NULL; - - (void) fr_deletetimeoutqueue(oifq); - + if (oifq == nifq) { + if ((tqe->tqe_next == NULL) || + (tqe->tqe_next->tqe_die == tqe->tqe_die)) { MUTEX_EXIT(&oifq->ifq_lock); - - MUTEX_ENTER(&nifq->ifq_lock); - - tqe->tqe_ifq = nifq; - nifq->ifq_ref++; + return; } - - /* - * Add to the bottom of the new queue - */ - tqe->tqe_die = fr_ticks + nifq->ifq_ttl; - tqe->tqe_pnext = nifq->ifq_tail; - *nifq->ifq_tail = tqe; - nifq->ifq_tail = &tqe->tqe_next; } + + /* + * Remove from the old queue + */ + *tqe->tqe_pnext = tqe->tqe_next; + if (tqe->tqe_next) + tqe->tqe_next->tqe_pnext = tqe->tqe_pnext; + else + oifq->ifq_tail = tqe->tqe_pnext; + tqe->tqe_next = NULL; + + /* + * If we're moving from one queue to another, release the + * lock on the old queue and get a lock on the new queue. + * For user defined queues, if we're moving off it, call + * delete in case it can now be freed. + */ + if (oifq != nifq) { + tqe->tqe_ifq = NULL; + + (void) ipf_deletetimeoutqueue(oifq); + + MUTEX_EXIT(&oifq->ifq_lock); + + MUTEX_ENTER(&nifq->ifq_lock); + + tqe->tqe_ifq = nifq; + nifq->ifq_ref++; + } + + /* + * Add to the bottom of the new queue + */ + tqe->tqe_pnext = nifq->ifq_tail; + *nifq->ifq_tail = tqe; + nifq->ifq_tail = &tqe->tqe_next; MUTEX_EXIT(&nifq->ifq_lock); } /* ------------------------------------------------------------------------ */ -/* Function: fr_updateipid */ +/* Function: ipf_updateipid */ /* Returns: int - 0 == success, -1 == error (packet should be droppped) */ /* Parameters: fin(I) - pointer to packet information */ /* */ @@ -5280,23 +6043,24 @@ ipftq_t *oifq, *nifq; /* the fragment cache for non-leading fragments. If a non-leading fragment */ /* has no match in the cache, return an error. */ /* ------------------------------------------------------------------------ */ -static int fr_updateipid(fin) -fr_info_t *fin; +static int +ipf_updateipid(fin) + fr_info_t *fin; { u_short id, ido, sums; u_32_t sumd, sum; ip_t *ip; if (fin->fin_off != 0) { - sum = fr_ipid_knownfrag(fin); + sum = ipf_frag_ipidknown(fin); if (sum == 0xffffffff) return -1; sum &= 0xffff; id = (u_short)sum; } else { - id = fr_nextipid(fin); + id = ipf_nextipid(fin); if (fin->fin_off == 0 && (fin->fin_flx & FI_FRAG) != 0) - (void) fr_ipid_newfrag(fin, (u_32_t)id); + (void) ipf_frag_ipidnew(fin, (u_32_t)id); } ip = fin->fin_ip; @@ -5317,7 +6081,7 @@ fr_info_t *fin; #ifdef NEED_FRGETIFNAME /* ------------------------------------------------------------------------ */ -/* Function: fr_getifname */ +/* Function: ipf_getifname */ /* Returns: char * - pointer to interface name */ /* Parameters: ifp(I) - pointer to network interface */ /* buffer(O) - pointer to where to store interface name */ @@ -5326,9 +6090,10 @@ fr_info_t *fin; /* expected to be at least LIFNAMSIZ in bytes big. If buffer is passed in */ /* as a NULL pointer then return a pointer to a static array. */ /* ------------------------------------------------------------------------ */ -char *fr_getifname(ifp, buffer) -struct ifnet *ifp; -char *buffer; +char * +ipf_getifname(ifp, buffer) + struct ifnet *ifp; + char *buffer; { static char namebuf[LIFNAMSIZ]; # if defined(MENTAT) || defined(__FreeBSD__) || defined(__osf__) || \ @@ -5350,7 +6115,7 @@ char *buffer; ; unit = ifp->if_unit; space = LIFNAMSIZ - (s - buffer); - if (space > 0) { + if ((space > 0) && (unit >= 0)) { # if defined(SNPRINTF) && defined(_KERNEL) SNPRINTF(temp, sizeof(temp), "%d", unit); # else @@ -5365,7 +6130,7 @@ char *buffer; /* ------------------------------------------------------------------------ */ -/* Function: fr_ioctlswitch */ +/* Function: ipf_ioctlswitch */ /* Returns: int - -1 continue processing, else ioctl return value */ /* Parameters: unit(I) - device unit opened */ /* data(I) - pointer to ioctl data */ @@ -5376,63 +6141,99 @@ char *buffer; /* */ /* Based on the value of unit, call the appropriate ioctl handler or return */ /* EIO if ipfilter is not running. Also checks if write perms are req'd */ -/* for the device in order to execute the ioctl. */ +/* for the device in order to execute the ioctl. A special case is made */ +/* SIOCIPFINTERROR so that the same code isn't required in every handler. */ +/* The context data pointer is passed through as this is used as the key */ +/* for locating a matching token for continued access for walking lists, */ +/* etc. */ /* ------------------------------------------------------------------------ */ -int fr_ioctlswitch(unit, data, cmd, mode, uid, ctx) -int unit, mode, uid; -ioctlcmd_t cmd; -void *data, *ctx; +int +ipf_ioctlswitch(softc, unit, data, cmd, mode, uid, ctx) + ipf_main_softc_t *softc; + int unit, mode, uid; + ioctlcmd_t cmd; + void *data, *ctx; { int error = 0; + switch (cmd) + { + case SIOCIPFINTERROR : + error = BCOPYOUT(&softc->ipf_interror, data, + sizeof(softc->ipf_interror)); + if (error != 0) { + IPFERROR(40); + error = EFAULT; + } + return error; + default : + break; + } + switch (unit) { case IPL_LOGIPF : - error = fr_ipf_ioctl(data, cmd, mode, uid, ctx); + error = ipf_ipf_ioctl(softc, data, cmd, mode, uid, ctx); break; case IPL_LOGNAT : - if (fr_running > 0) - error = fr_nat_ioctl(data, cmd, mode, uid, ctx); - else + if (softc->ipf_running > 0) { + error = ipf_nat_ioctl(softc, data, cmd, mode, + uid, ctx); + } else { + IPFERROR(42); error = EIO; + } break; case IPL_LOGSTATE : - if (fr_running > 0) - error = fr_state_ioctl(data, cmd, mode, uid, ctx); - else + if (softc->ipf_running > 0) { + error = ipf_state_ioctl(softc, data, cmd, mode, + uid, ctx); + } else { + IPFERROR(43); error = EIO; + } break; case IPL_LOGAUTH : - if (fr_running > 0) - error = fr_auth_ioctl(data, cmd, mode, uid, ctx); - else + if (softc->ipf_running > 0) { + error = ipf_auth_ioctl(softc, data, cmd, mode, + uid, ctx); + } else { + IPFERROR(44); error = EIO; + } break; case IPL_LOGSYNC : -#ifdef IPFILTER_SYNC - if (fr_running > 0) - error = fr_sync_ioctl(data, cmd, mode, uid, ctx); - else -#endif + if (softc->ipf_running > 0) { + error = ipf_sync_ioctl(softc, data, cmd, mode, + uid, ctx); + } else { error = EIO; + IPFERROR(45); + } break; case IPL_LOGSCAN : #ifdef IPFILTER_SCAN - if (fr_running > 0) - error = fr_scan_ioctl(data, cmd, mode, uid, ctx); + if (softc->ipf_running > 0) + error = ipf_scan_ioctl(softc, data, cmd, mode, + uid, ctx); else #endif + { error = EIO; + IPFERROR(46); + } break; case IPL_LOGLOOKUP : -#ifdef IPFILTER_LOOKUP - if (fr_running > 0) - error = ip_lookup_ioctl(data, cmd, mode, uid, ctx); - else -#endif + if (softc->ipf_running > 0) { + error = ipf_lookup_ioctl(softc, data, cmd, mode, + uid, ctx); + } else { error = EIO; + IPFERROR(47); + } break; default : + IPFERROR(48); error = EIO; break; } @@ -5443,200 +6244,244 @@ void *data, *ctx; /* * This array defines the expected size of objects coming into the kernel - * for the various recognised object types. + * for the various recognised object types. The first column is flags (see + * below), 2nd column is current size, 3rd column is the version number of + * when the current size became current. + * Flags: + * 1 = minimum size, not absolute size */ -static int fr_objbytes[IPFOBJ_COUNT][2] = { - { 1, sizeof(struct frentry) }, /* frentry */ - { 0, sizeof(struct friostat) }, - { 0, sizeof(struct fr_info) }, - { 0, sizeof(struct fr_authstat) }, - { 0, sizeof(struct ipfrstat) }, - { 0, sizeof(struct ipnat) }, - { 0, sizeof(struct natstat) }, - { 0, sizeof(struct ipstate_save) }, - { 1, sizeof(struct nat_save) }, /* nat_save */ - { 0, sizeof(struct natlookup) }, - { 1, sizeof(struct ipstate) }, /* ipstate */ - { 0, sizeof(struct ips_stat) }, - { 0, sizeof(struct frauth) }, - { 0, sizeof(struct ipftune) }, - { 0, sizeof(struct nat) }, /* nat_t */ - { 0, sizeof(struct ipfruleiter) }, - { 0, sizeof(struct ipfgeniter) }, - { 0, sizeof(struct ipftable) }, - { 0, sizeof(struct ipflookupiter) }, +static int ipf_objbytes[IPFOBJ_COUNT][3] = { + { 1, sizeof(struct frentry), 5010000 }, /* 0 */ + { 1, sizeof(struct friostat), 5010000 }, + { 0, sizeof(struct fr_info), 5010000 }, + { 0, sizeof(struct ipf_authstat), 4010100 }, + { 0, sizeof(struct ipfrstat), 5010000 }, + { 1, sizeof(struct ipnat), 5010000 }, /* 5 */ + { 0, sizeof(struct natstat), 5010000 }, + { 0, sizeof(struct ipstate_save), 5010000 }, + { 1, sizeof(struct nat_save), 5010000 }, + { 0, sizeof(struct natlookup), 5010000 }, + { 1, sizeof(struct ipstate), 5010000 }, /* 10 */ + { 0, sizeof(struct ips_stat), 5010000 }, + { 0, sizeof(struct frauth), 5010000 }, + { 0, sizeof(struct ipftune), 4010100 }, + { 0, sizeof(struct nat), 5010000 }, + { 0, sizeof(struct ipfruleiter), 4011400 }, /* 15 */ + { 0, sizeof(struct ipfgeniter), 4011400 }, + { 0, sizeof(struct ipftable), 4011400 }, + { 0, sizeof(struct ipflookupiter), 4011400 }, { 0, sizeof(struct ipftq) * IPF_TCP_NSTATES }, + { 1, 0, 0 }, /* IPFEXPR */ + { 0, 0, 0 }, /* PROXYCTL */ + { 0, sizeof (struct fripf), 5010000 } }; /* ------------------------------------------------------------------------ */ -/* Function: fr_inobj */ +/* Function: ipf_inobj */ /* Returns: int - 0 = success, else failure */ -/* Parameters: data(I) - pointer to ioctl data */ -/* ptr(I) - pointer to store real data in */ -/* type(I) - type of structure being moved */ +/* Parameters: softc(I) - soft context pointerto work with */ +/* data(I) - pointer to ioctl data */ +/* objp(O) - where to store ipfobj structure */ +/* ptr(I) - pointer to data to copy out */ +/* type(I) - type of structure being moved */ /* */ /* Copy in the contents of what the ipfobj_t points to. In future, we */ /* add things to check for version numbers, sizes, etc, to make it backward */ /* compatible at the ABI for user land. */ +/* If objp is not NULL then we assume that the caller wants to see what is */ +/* in the ipfobj_t structure being copied in. As an example, this can tell */ +/* the caller what version of ipfilter the ioctl program was written to. */ /* ------------------------------------------------------------------------ */ -int fr_inobj(data, ptr, type) -void *data; -void *ptr; -int type; +int +ipf_inobj(softc, data, objp, ptr, type) + ipf_main_softc_t *softc; + void *data; + ipfobj_t *objp; + void *ptr; + int type; { ipfobj_t obj; - int error = 0; + int error; + int size; - if ((type < 0) || (type >= IPFOBJ_COUNT)) - return EINVAL; - - error = BCOPYIN(data, &obj, sizeof(obj)); - if (error != 0) - return EFAULT; - - if (obj.ipfo_type != type) - return EINVAL; - -#ifndef IPFILTER_COMPAT - if ((fr_objbytes[type][0] & 1) != 0) { - if (obj.ipfo_size < fr_objbytes[type][1]) - return EINVAL; - } else if (obj.ipfo_size != fr_objbytes[type][1]) { + if ((type < 0) || (type >= IPFOBJ_COUNT)) { + IPFERROR(49); return EINVAL; } -#else - if (obj.ipfo_rev != IPFILTER_VERSION) - /* XXX compatibility hook here */ - ; - if ((fr_objbytes[type][0] & 1) != 0) { - if (obj.ipfo_size < fr_objbytes[type][1]) - /* XXX compatibility hook here */ - return EINVAL; - } else if (obj.ipfo_size != fr_objbytes[type][1]) - /* XXX compatibility hook here */ - return EINVAL; -#endif - if ((fr_objbytes[type][0] & 1) != 0) { - error = COPYIN(obj.ipfo_ptr, ptr, fr_objbytes[type][1]); + if (objp == NULL) + objp = &obj; + error = BCOPYIN(data, objp, sizeof(*objp)); + if (error != 0) { + IPFERROR(124); + return EFAULT; + } + + if (objp->ipfo_type != type) { + IPFERROR(50); + return EINVAL; + } + + if (objp->ipfo_rev >= ipf_objbytes[type][2]) { + if ((ipf_objbytes[type][0] & 1) != 0) { + if (objp->ipfo_size < ipf_objbytes[type][1]) { + IPFERROR(51); + return EINVAL; + } + size = ipf_objbytes[type][1]; + } else if (objp->ipfo_size == ipf_objbytes[type][1]) { + size = objp->ipfo_size; + } else { + IPFERROR(52); + return EINVAL; + } + error = COPYIN(objp->ipfo_ptr, ptr, size); + if (error != 0) { + IPFERROR(55); + error = EFAULT; + } } else { - error = COPYIN(obj.ipfo_ptr, ptr, obj.ipfo_size); +#ifdef IPFILTER_COMPAT + error = ipf_in_compat(softc, objp, ptr, 0); +#else + IPFERROR(54); + error = EINVAL; +#endif } - if (error != 0) - error = EFAULT; return error; } /* ------------------------------------------------------------------------ */ -/* Function: fr_inobjsz */ +/* Function: ipf_inobjsz */ +/* Returns: int - 0 = success, else failure */ +/* Parameters: softc(I) - soft context pointerto work with */ +/* data(I) - pointer to ioctl data */ +/* ptr(I) - pointer to store real data in */ +/* type(I) - type of structure being moved */ +/* sz(I) - size of data to copy */ +/* */ +/* As per ipf_inobj, except the size of the object to copy in is passed in */ +/* but it must not be smaller than the size defined for the type and the */ +/* type must allow for varied sized objects. The extra requirement here is */ +/* that sz must match the size of the object being passed in - this is not */ +/* not possible nor required in ipf_inobj(). */ +/* ------------------------------------------------------------------------ */ +int +ipf_inobjsz(softc, data, ptr, type, sz) + ipf_main_softc_t *softc; + void *data; + void *ptr; + int type, sz; +{ + ipfobj_t obj; + int error; + + if ((type < 0) || (type >= IPFOBJ_COUNT)) { + IPFERROR(56); + return EINVAL; + } + + error = BCOPYIN(data, &obj, sizeof(obj)); + if (error != 0) { + IPFERROR(125); + return EFAULT; + } + + if (obj.ipfo_type != type) { + IPFERROR(58); + return EINVAL; + } + + if (obj.ipfo_rev >= ipf_objbytes[type][2]) { + if (((ipf_objbytes[type][0] & 1) == 0) || + (sz < ipf_objbytes[type][1])) { + IPFERROR(57); + return EINVAL; + } + error = COPYIN(obj.ipfo_ptr, ptr, sz); + if (error != 0) { + IPFERROR(61); + error = EFAULT; + } + } else { +#ifdef IPFILTER_COMPAT + error = ipf_in_compat(softc, &obj, ptr, sz); +#else + IPFERROR(60); + error = EINVAL; +#endif + } + return error; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_outobjsz */ /* Returns: int - 0 = success, else failure */ /* Parameters: data(I) - pointer to ioctl data */ /* ptr(I) - pointer to store real data in */ /* type(I) - type of structure being moved */ /* sz(I) - size of data to copy */ /* */ -/* As per fr_inobj, except the size of the object to copy in is passed in */ +/* As per ipf_outobj, except the size of the object to copy out is passed in*/ /* but it must not be smaller than the size defined for the type and the */ /* type must allow for varied sized objects. The extra requirement here is */ /* that sz must match the size of the object being passed in - this is not */ -/* not possible nor required in fr_inobj(). */ +/* not possible nor required in ipf_outobj(). */ /* ------------------------------------------------------------------------ */ -int fr_inobjsz(data, ptr, type, sz) -void *data; -void *ptr; -int type, sz; +int +ipf_outobjsz(softc, data, ptr, type, sz) + ipf_main_softc_t *softc; + void *data; + void *ptr; + int type, sz; { ipfobj_t obj; int error; - if ((type < 0) || (type >= IPFOBJ_COUNT)) - return EINVAL; - if (((fr_objbytes[type][0] & 1) == 0) || (sz < fr_objbytes[type][1])) + if ((type < 0) || (type >= IPFOBJ_COUNT)) { + IPFERROR(62); return EINVAL; + } error = BCOPYIN(data, &obj, sizeof(obj)); - if (error != 0) + if (error != 0) { + IPFERROR(127); return EFAULT; + } - if (obj.ipfo_type != type) + if (obj.ipfo_type != type) { + IPFERROR(63); return EINVAL; + } -#ifndef IPFILTER_COMPAT - if (obj.ipfo_size != sz) - return EINVAL; + if (obj.ipfo_rev >= ipf_objbytes[type][2]) { + if (((ipf_objbytes[type][0] & 1) == 0) || + (sz < ipf_objbytes[type][1])) { + IPFERROR(146); + return EINVAL; + } + error = COPYOUT(ptr, obj.ipfo_ptr, sz); + if (error != 0) { + IPFERROR(66); + error = EFAULT; + } + } else { +#ifdef IPFILTER_COMPAT + error = ipf_out_compat(softc, &obj, ptr); #else - if (obj.ipfo_rev != IPFILTER_VERSION) - /* XXX compatibility hook here */ - ; - if (obj.ipfo_size != sz) - /* XXX compatibility hook here */ - return EINVAL; + IPFERROR(65); + error = EINVAL; #endif - - error = COPYIN(obj.ipfo_ptr, ptr, sz); - if (error != 0) - error = EFAULT; + } return error; } /* ------------------------------------------------------------------------ */ -/* Function: fr_outobjsz */ -/* Returns: int - 0 = success, else failure */ -/* Parameters: data(I) - pointer to ioctl data */ -/* ptr(I) - pointer to store real data in */ -/* type(I) - type of structure being moved */ -/* sz(I) - size of data to copy */ -/* */ -/* As per fr_outobj, except the size of the object to copy out is passed in */ -/* but it must not be smaller than the size defined for the type and the */ -/* type must allow for varied sized objects. The extra requirement here is */ -/* that sz must match the size of the object being passed in - this is not */ -/* not possible nor required in fr_outobj(). */ -/* ------------------------------------------------------------------------ */ -int fr_outobjsz(data, ptr, type, sz) -void *data; -void *ptr; -int type, sz; -{ - ipfobj_t obj; - int error; - - if ((type < 0) || (type >= IPFOBJ_COUNT) || - ((fr_objbytes[type][0] & 1) == 0) || - (sz < fr_objbytes[type][1])) - return EINVAL; - - error = BCOPYIN(data, &obj, sizeof(obj)); - if (error != 0) - return EFAULT; - - if (obj.ipfo_type != type) - return EINVAL; - -#ifndef IPFILTER_COMPAT - if (obj.ipfo_size != sz) - return EINVAL; -#else - if (obj.ipfo_rev != IPFILTER_VERSION) - /* XXX compatibility hook here */ - ; - if (obj.ipfo_size != sz) - /* XXX compatibility hook here */ - return EINVAL; -#endif - - error = COPYOUT(ptr, obj.ipfo_ptr, sz); - if (error != 0) - error = EFAULT; - return error; -} - - -/* ------------------------------------------------------------------------ */ -/* Function: fr_outobj */ +/* Function: ipf_outobj */ /* Returns: int - 0 = success, else failure */ /* Parameters: data(I) - pointer to ioctl data */ /* ptr(I) - pointer to store real data in */ @@ -5646,75 +6491,134 @@ int type, sz; /* future, we add things to check for version numbers, sizes, etc, to make */ /* it backward compatible at the ABI for user land. */ /* ------------------------------------------------------------------------ */ -int fr_outobj(data, ptr, type) -void *data; -void *ptr; -int type; +int +ipf_outobj(softc, data, ptr, type) + ipf_main_softc_t *softc; + void *data; + void *ptr; + int type; { ipfobj_t obj; int error; - if ((type < 0) || (type >= IPFOBJ_COUNT)) + if ((type < 0) || (type >= IPFOBJ_COUNT)) { + IPFERROR(67); return EINVAL; + } error = BCOPYIN(data, &obj, sizeof(obj)); - if (error != 0) + if (error != 0) { + IPFERROR(126); return EFAULT; + } - if (obj.ipfo_type != type) + if (obj.ipfo_type != type) { + IPFERROR(68); return EINVAL; + } -#ifndef IPFILTER_COMPAT - if ((fr_objbytes[type][0] & 1) != 0) { - if (obj.ipfo_size < fr_objbytes[type][1]) + if (obj.ipfo_rev >= ipf_objbytes[type][2]) { + if ((ipf_objbytes[type][0] & 1) != 0) { + if (obj.ipfo_size < ipf_objbytes[type][1]) { + IPFERROR(69); + return EINVAL; + } + } else if (obj.ipfo_size != ipf_objbytes[type][1]) { + IPFERROR(70); return EINVAL; - } else if (obj.ipfo_size != fr_objbytes[type][1]) - return EINVAL; + } + + error = COPYOUT(ptr, obj.ipfo_ptr, obj.ipfo_size); + if (error != 0) { + IPFERROR(73); + error = EFAULT; + } + } else { +#ifdef IPFILTER_COMPAT + error = ipf_out_compat(softc, &obj, ptr); #else - if (obj.ipfo_rev != IPFILTER_VERSION) - /* XXX compatibility hook here */ - ; - if ((fr_objbytes[type][0] & 1) != 0) { - if (obj.ipfo_size < fr_objbytes[type][1]) - /* XXX compatibility hook here */ - return EINVAL; - } else if (obj.ipfo_size != fr_objbytes[type][1]) - /* XXX compatibility hook here */ - return EINVAL; + IPFERROR(72); + error = EINVAL; #endif - - error = COPYOUT(ptr, obj.ipfo_ptr, obj.ipfo_size); - if (error != 0) - error = EFAULT; + } return error; } /* ------------------------------------------------------------------------ */ -/* Function: fr_checkl4sum */ +/* Function: ipf_outobjk */ +/* Returns: int - 0 = success, else failure */ +/* Parameters: obj(I) - pointer to data description structure */ +/* ptr(I) - pointer to kernel data to copy out */ +/* */ +/* In the above functions, the ipfobj_t structure is copied into the kernel,*/ +/* telling ipfilter how to copy out data. In this instance, the ipfobj_t is */ +/* already populated with information and now we just need to use it. */ +/* There is no need for this function to have a "type" parameter as there */ +/* is no point in validating information that comes from the kernel with */ +/* itself. */ +/* ------------------------------------------------------------------------ */ +int +ipf_outobjk(softc, obj, ptr) + ipf_main_softc_t *softc; + ipfobj_t *obj; + void *ptr; +{ + int type = obj->ipfo_type; + int error; + + if ((type < 0) || (type >= IPFOBJ_COUNT)) { + IPFERROR(147); + return EINVAL; + } + + if (obj->ipfo_rev >= ipf_objbytes[type][2]) { + if ((ipf_objbytes[type][0] & 1) != 0) { + if (obj->ipfo_size < ipf_objbytes[type][1]) { + IPFERROR(148); + return EINVAL; + } + + } else if (obj->ipfo_size != ipf_objbytes[type][1]) { + IPFERROR(149); + return EINVAL; + } + + error = COPYOUT(ptr, obj->ipfo_ptr, obj->ipfo_size); + if (error != 0) { + IPFERROR(150); + error = EFAULT; + } + } else { +#ifdef IPFILTER_COMPAT + error = ipf_out_compat(softc, obj, ptr); +#else + IPFERROR(151); + error = EINVAL; +#endif + } + return error; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_checkl4sum */ /* Returns: int - 0 = good, -1 = bad, 1 = cannot check */ /* Parameters: fin(I) - pointer to packet information */ /* */ /* If possible, calculate the layer 4 checksum for the packet. If this is */ /* not possible, return without indicating a failure or success but in a */ -/* way that is ditinguishable. */ +/* way that is ditinguishable. This function should only be called by the */ +/* ipf_checkv6sum() for each platform. */ /* ------------------------------------------------------------------------ */ -int fr_checkl4sum(fin) -fr_info_t *fin; +INLINE int +ipf_checkl4sum(fin) + fr_info_t *fin; { u_short sum, hdrsum, *csump; udphdr_t *udp; int dosum; - if ((fin->fin_flx & FI_NOCKSUM) != 0) - return 0; - - if (fin->fin_cksum == 1) - return 0; - - if (fin->fin_cksum == -1) - return -1; - /* * If the TCP packet isn't a fragment, isn't too short and otherwise * isn't already considered "bad", then validate the checksum. If @@ -5728,48 +6632,44 @@ fr_info_t *fin; dosum = 0; sum = 0; -#if SOLARIS && defined(_KERNEL) && (SOLARIS2 >= 6) && defined(ICK_VALID) - if (dohwcksum && ((*fin->fin_mp)->b_ick_flag == ICK_VALID)) { - hdrsum = 0; - sum = 0; - } else { + switch (fin->fin_p) + { + case IPPROTO_TCP : + csump = &((tcphdr_t *)fin->fin_dp)->th_sum; + dosum = 1; + break; + + case IPPROTO_UDP : + udp = fin->fin_dp; + if (udp->uh_sum != 0) { + csump = &udp->uh_sum; + dosum = 1; + } + break; + +#ifdef USE_INET6 + case IPPROTO_ICMPV6 : + csump = &((struct icmp6_hdr *)fin->fin_dp)->icmp6_cksum; + dosum = 1; + break; #endif - switch (fin->fin_p) - { - case IPPROTO_TCP : - csump = &((tcphdr_t *)fin->fin_dp)->th_sum; - dosum = 1; - break; - case IPPROTO_UDP : - udp = fin->fin_dp; - if (udp->uh_sum != 0) { - csump = &udp->uh_sum; - dosum = 1; - } - break; + case IPPROTO_ICMP : + csump = &((struct icmp *)fin->fin_dp)->icmp_cksum; + dosum = 1; + break; - case IPPROTO_ICMP : - csump = &((struct icmp *)fin->fin_dp)->icmp_cksum; - dosum = 1; - break; - - default : - return 1; - /*NOTREACHED*/ - } - - if (csump != NULL) - hdrsum = *csump; - - if (dosum) { - sum = fr_cksum(fin->fin_m, fin->fin_ip, - fin->fin_p, fin->fin_dp, - fin->fin_dlen + fin->fin_hlen); - } -#if SOLARIS && defined(_KERNEL) && (SOLARIS2 >= 6) && defined(ICK_VALID) + default : + return 1; + /*NOTREACHED*/ + } + + if (csump != NULL) + hdrsum = *csump; + + if (dosum) { + sum = fr_cksum(fin, fin->fin_ip, fin->fin_p, fin->fin_dp); } -#endif #if !defined(_KERNEL) if (sum == hdrsum) { FR_DEBUG(("checkl4sum: %hx == %hx\n", sum, hdrsum)); @@ -5777,17 +6677,18 @@ fr_info_t *fin; FR_DEBUG(("checkl4sum: %hx != %hx\n", sum, hdrsum)); } #endif + DT2(l4sums, u_short, hdrsum, u_short, sum); if (hdrsum == sum) { - fin->fin_cksum = 1; + fin->fin_cksum = FI_CK_SUMOK; return 0; } - fin->fin_cksum = -1; + fin->fin_cksum = FI_CK_BAD; return -1; } /* ------------------------------------------------------------------------ */ -/* Function: fr_ifpfillv4addr */ +/* Function: ipf_ifpfillv4addr */ /* Returns: int - 0 = address update, -1 = address not updated */ /* Parameters: atype(I) - type of network address update to perform */ /* sin(I) - pointer to source of address information */ @@ -5802,10 +6703,11 @@ fr_info_t *fin; /* FRI_NETMASKED, if inpmask is non-NULL then the mask is set to an all 1s */ /* value. */ /* ------------------------------------------------------------------------ */ -int fr_ifpfillv4addr(atype, sin, mask, inp, inpmask) -int atype; -struct sockaddr_in *sin, *mask; -struct in_addr *inp, *inpmask; +int +ipf_ifpfillv4addr(atype, sin, mask, inp, inpmask) + int atype; + struct sockaddr_in *sin, *mask; + struct in_addr *inp, *inpmask; { if (inpmask != NULL && atype != FRI_NETMASKED) inpmask->s_addr = 0xffffffff; @@ -5826,7 +6728,7 @@ struct in_addr *inp, *inpmask; #ifdef USE_INET6 /* ------------------------------------------------------------------------ */ -/* Function: fr_ifpfillv6addr */ +/* Function: ipf_ifpfillv6addr */ /* Returns: int - 0 = address update, -1 = address not updated */ /* Parameters: atype(I) - type of network address update to perform */ /* sin(I) - pointer to source of address information */ @@ -5841,44 +6743,43 @@ struct in_addr *inp, *inpmask; /* FRI_NETMASKED, if inpmask is non-NULL then the mask is set to an all 1s */ /* value. */ /* ------------------------------------------------------------------------ */ -int fr_ifpfillv6addr(atype, sin, mask, inp, inpmask) -int atype; -struct sockaddr_in6 *sin, *mask; -struct in_addr *inp, *inpmask; +int +ipf_ifpfillv6addr(atype, sin, mask, inp, inpmask) + int atype; + struct sockaddr_in6 *sin, *mask; + i6addr_t *inp, *inpmask; { - i6addr_t *src, *dst, *and, *dmask; + i6addr_t *src, *and; src = (i6addr_t *)&sin->sin6_addr; and = (i6addr_t *)&mask->sin6_addr; - dst = (i6addr_t *)inp; - dmask = (i6addr_t *)inpmask; if (inpmask != NULL && atype != FRI_NETMASKED) { - dmask->i6[0] = 0xffffffff; - dmask->i6[1] = 0xffffffff; - dmask->i6[2] = 0xffffffff; - dmask->i6[3] = 0xffffffff; + inpmask->i6[0] = 0xffffffff; + inpmask->i6[1] = 0xffffffff; + inpmask->i6[2] = 0xffffffff; + inpmask->i6[3] = 0xffffffff; } if (atype == FRI_NETWORK || atype == FRI_NETMASKED) { if (atype == FRI_NETMASKED) { if (inpmask == NULL) return -1; - dmask->i6[0] = and->i6[0]; - dmask->i6[1] = and->i6[1]; - dmask->i6[2] = and->i6[2]; - dmask->i6[3] = and->i6[3]; + inpmask->i6[0] = and->i6[0]; + inpmask->i6[1] = and->i6[1]; + inpmask->i6[2] = and->i6[2]; + inpmask->i6[3] = and->i6[3]; } - dst->i6[0] = src->i6[0] & and->i6[0]; - dst->i6[1] = src->i6[1] & and->i6[1]; - dst->i6[2] = src->i6[2] & and->i6[2]; - dst->i6[3] = src->i6[3] & and->i6[3]; + inp->i6[0] = src->i6[0] & and->i6[0]; + inp->i6[1] = src->i6[1] & and->i6[1]; + inp->i6[2] = src->i6[2] & and->i6[2]; + inp->i6[3] = src->i6[3] & and->i6[3]; } else { - dst->i6[0] = src->i6[0]; - dst->i6[1] = src->i6[1]; - dst->i6[2] = src->i6[2]; - dst->i6[3] = src->i6[3]; + inp->i6[0] = src->i6[0]; + inp->i6[1] = src->i6[1]; + inp->i6[2] = src->i6[2]; + inp->i6[3] = src->i6[3]; } return 0; } @@ -5886,7 +6787,7 @@ struct in_addr *inp, *inpmask; /* ------------------------------------------------------------------------ */ -/* Function: fr_matchtag */ +/* Function: ipf_matchtag */ /* Returns: 0 == mismatch, 1 == match. */ /* Parameters: tag1(I) - pointer to first tag to compare */ /* tag2(I) - pointer to second tag to compare */ @@ -5898,8 +6799,9 @@ struct in_addr *inp, *inpmask; /* comparison. This function should only be called with both tag1 and tag2 */ /* as non-NULL pointers. */ /* ------------------------------------------------------------------------ */ -int fr_matchtag(tag1, tag2) -ipftag_t *tag1, *tag2; +int +ipf_matchtag(tag1, tag2) + ipftag_t *tag1, *tag2; { if (tag1 == tag2) return 1; @@ -5917,16 +6819,18 @@ ipftag_t *tag1, *tag2; /* ------------------------------------------------------------------------ */ -/* Function: fr_coalesce */ +/* Function: ipf_coalesce */ /* Returns: 1 == success, -1 == failure, 0 == no change */ /* Parameters: fin(I) - pointer to packet information */ /* */ /* Attempt to get all of the packet data into a single, contiguous buffer. */ /* If this call returns a failure then the buffers have also been freed. */ /* ------------------------------------------------------------------------ */ -int fr_coalesce(fin) -fr_info_t *fin; +int +ipf_coalesce(fin) + fr_info_t *fin; { + if ((fin->fin_flx & FI_COALESCE) != 0) return 1; @@ -5938,11 +6842,15 @@ fr_info_t *fin; return 0; #if defined(_KERNEL) - if (fr_pullup(fin->fin_m, fin, fin->fin_plen) == NULL) { - ATOMIC_INCL(fr_badcoalesces[fin->fin_out]); + if (ipf_pullup(fin->fin_m, fin, fin->fin_plen) == NULL) { + ipf_main_softc_t *softc = fin->fin_main_soft; + + DT1(frb_coalesce, fr_info_t *, fin); + LBUMP(ipf_stats[fin->fin_out].fr_badcoalesces); # ifdef MENTAT FREE_MB_T(*fin->fin_mp); # endif + fin->fin_reason = FRB_COALESCE; *fin->fin_mp = NULL; fin->fin_m = NULL; return -1; @@ -5967,114 +6875,10 @@ fr_info_t *fin; * The obvious implication is if neither of these are set then the value can be * changed at any time without harm. */ -ipftuneable_t ipf_tuneables[] = { - /* filtering */ - { { &fr_flags }, "fr_flags", 0, 0xffffffff, - sizeof(fr_flags), 0, NULL }, - { { &fr_active }, "fr_active", 0, 0, - sizeof(fr_active), IPFT_RDONLY, NULL }, - { { &fr_control_forwarding }, "fr_control_forwarding", 0, 1, - sizeof(fr_control_forwarding), 0, NULL }, - { { &fr_update_ipid }, "fr_update_ipid", 0, 1, - sizeof(fr_update_ipid), 0, NULL }, - { { &fr_chksrc }, "fr_chksrc", 0, 1, - sizeof(fr_chksrc), 0, NULL }, - { { &fr_minttl }, "fr_minttl", 0, 1, - sizeof(fr_minttl), 0, NULL }, - { { &fr_icmpminfragmtu }, "fr_icmpminfragmtu", 0, 1, - sizeof(fr_icmpminfragmtu), 0, NULL }, - { { &fr_pass }, "fr_pass", 0, 0xffffffff, - sizeof(fr_pass), 0, NULL }, - /* state */ - { { &fr_tcpidletimeout }, "fr_tcpidletimeout", 1, 0x7fffffff, - sizeof(fr_tcpidletimeout), IPFT_WRDISABLED, NULL }, - { { &fr_tcpclosewait }, "fr_tcpclosewait", 1, 0x7fffffff, - sizeof(fr_tcpclosewait), IPFT_WRDISABLED, NULL }, - { { &fr_tcplastack }, "fr_tcplastack", 1, 0x7fffffff, - sizeof(fr_tcplastack), IPFT_WRDISABLED, NULL }, - { { &fr_tcptimeout }, "fr_tcptimeout", 1, 0x7fffffff, - sizeof(fr_tcptimeout), IPFT_WRDISABLED, NULL }, - { { &fr_tcpclosed }, "fr_tcpclosed", 1, 0x7fffffff, - sizeof(fr_tcpclosed), IPFT_WRDISABLED, NULL }, - { { &fr_tcphalfclosed }, "fr_tcphalfclosed", 1, 0x7fffffff, - sizeof(fr_tcphalfclosed), IPFT_WRDISABLED, NULL }, - { { &fr_udptimeout }, "fr_udptimeout", 1, 0x7fffffff, - sizeof(fr_udptimeout), IPFT_WRDISABLED, NULL }, - { { &fr_udpacktimeout }, "fr_udpacktimeout", 1, 0x7fffffff, - sizeof(fr_udpacktimeout), IPFT_WRDISABLED, NULL }, - { { &fr_icmptimeout }, "fr_icmptimeout", 1, 0x7fffffff, - sizeof(fr_icmptimeout), IPFT_WRDISABLED, NULL }, - { { &fr_icmpacktimeout }, "fr_icmpacktimeout", 1, 0x7fffffff, - sizeof(fr_icmpacktimeout), IPFT_WRDISABLED, NULL }, - { { &fr_iptimeout }, "fr_iptimeout", 1, 0x7fffffff, - sizeof(fr_iptimeout), IPFT_WRDISABLED, NULL }, - { { &fr_statemax }, "fr_statemax", 1, 0x7fffffff, - sizeof(fr_statemax), 0, NULL }, - { { &fr_statesize }, "fr_statesize", 1, 0x7fffffff, - sizeof(fr_statesize), IPFT_WRDISABLED, NULL }, - { { &fr_state_lock }, "fr_state_lock", 0, 1, - sizeof(fr_state_lock), IPFT_RDONLY, NULL }, - { { &fr_state_maxbucket }, "fr_state_maxbucket", 1, 0x7fffffff, - sizeof(fr_state_maxbucket), IPFT_WRDISABLED, NULL }, - { { &fr_state_maxbucket_reset }, "fr_state_maxbucket_reset", 0, 1, - sizeof(fr_state_maxbucket_reset), IPFT_WRDISABLED, NULL }, - { { &ipstate_logging }, "ipstate_logging", 0, 1, - sizeof(ipstate_logging), 0, NULL }, - /* nat */ - { { &fr_nat_lock }, "fr_nat_lock", 0, 1, - sizeof(fr_nat_lock), IPFT_RDONLY, NULL }, - { { &ipf_nattable_sz }, "ipf_nattable_sz", 1, 0x7fffffff, - sizeof(ipf_nattable_sz), IPFT_WRDISABLED, NULL }, - { { &ipf_nattable_max }, "ipf_nattable_max", 1, 0x7fffffff, - sizeof(ipf_nattable_max), 0, NULL }, - { { &ipf_natrules_sz }, "ipf_natrules_sz", 1, 0x7fffffff, - sizeof(ipf_natrules_sz), IPFT_WRDISABLED, NULL }, - { { &ipf_rdrrules_sz }, "ipf_rdrrules_sz", 1, 0x7fffffff, - sizeof(ipf_rdrrules_sz), IPFT_WRDISABLED, NULL }, - { { &ipf_hostmap_sz }, "ipf_hostmap_sz", 1, 0x7fffffff, - sizeof(ipf_hostmap_sz), IPFT_WRDISABLED, NULL }, - { { &fr_nat_maxbucket }, "fr_nat_maxbucket", 1, 0x7fffffff, - sizeof(fr_nat_maxbucket), 0, NULL }, - { { &fr_nat_maxbucket_reset }, "fr_nat_maxbucket_reset", 0, 1, - sizeof(fr_nat_maxbucket_reset), IPFT_WRDISABLED, NULL }, - { { &nat_logging }, "nat_logging", 0, 1, - sizeof(nat_logging), 0, NULL }, - { { &fr_defnatage }, "fr_defnatage", 1, 0x7fffffff, - sizeof(fr_defnatage), IPFT_WRDISABLED, NULL }, - { { &fr_defnatipage }, "fr_defnatipage", 1, 0x7fffffff, - sizeof(fr_defnatipage), IPFT_WRDISABLED, NULL }, - { { &fr_defnaticmpage }, "fr_defnaticmpage", 1, 0x7fffffff, - sizeof(fr_defnaticmpage), IPFT_WRDISABLED, NULL }, - { { &fr_nat_doflush }, "fr_nat_doflush", 0, 1, - sizeof(fr_nat_doflush), 0, NULL }, - /* proxy */ - { { &ipf_proxy_debug }, "ipf_proxy_debug", 0, 10, - sizeof(ipf_proxy_debug), 0, 0 }, - /* frag */ - { { &ipfr_size }, "ipfr_size", 1, 0x7fffffff, - sizeof(ipfr_size), IPFT_WRDISABLED, NULL }, - { { &fr_ipfrttl }, "fr_ipfrttl", 1, 0x7fffffff, - sizeof(fr_ipfrttl), IPFT_WRDISABLED, NULL }, -#ifdef IPFILTER_LOG - /* log */ - { { &ipl_suppress }, "ipl_suppress", 0, 1, - sizeof(ipl_suppress), 0, NULL }, - { { &ipl_logmax }, "ipl_logmax", 0, 0x7fffffff, - sizeof(ipl_logmax), IPFT_WRDISABLED, NULL }, - { { &ipl_logall }, "ipl_logall", 0, 1, - sizeof(ipl_logall), 0, NULL }, - { { &ipl_logsize }, "ipl_logsize", 0, 0x80000, - sizeof(ipl_logsize), 0, NULL }, -#endif - { { NULL }, NULL, 0, 0, - 0, 0, NULL } -}; - -static ipftuneable_t *ipf_tunelist = NULL; /* ------------------------------------------------------------------------ */ -/* Function: fr_findtunebycookie */ +/* Function: ipf_tune_findbycookie */ /* Returns: NULL = search failed, else pointer to tune struct */ /* Parameters: cookie(I) - cookie value to search for amongst tuneables */ /* next(O) - pointer to place to store the cookie for the */ @@ -6085,12 +6889,14 @@ static ipftuneable_t *ipf_tunelist = NULL; /* a matching value for "cookie" - ie its address. When returning a match, */ /* the next one to be found may be returned inside next. */ /* ------------------------------------------------------------------------ */ -static ipftuneable_t *fr_findtunebycookie(cookie, next) -void *cookie, **next; +static ipftuneable_t * +ipf_tune_findbycookie(ptop, cookie, next) + ipftuneable_t **ptop; + void *cookie, **next; { ipftuneable_t *ta, **tap; - for (ta = ipf_tuneables; ta->ipft_name != NULL; ta++) + for (ta = *ptop; ta->ipft_name != NULL; ta++) if (ta == cookie) { if (next != NULL) { /* @@ -6104,12 +6910,12 @@ void *cookie, **next; if ((ta + 1)->ipft_name != NULL) *next = ta + 1; else - *next = &ipf_tunelist; + *next = ptop; } return ta; } - for (tap = &ipf_tunelist; (ta = *tap) != NULL; tap = &ta->ipft_next) + for (tap = ptop; (ta = *tap) != NULL; tap = &ta->ipft_next) if (tap == cookie) { if (next != NULL) *next = &ta->ipft_next; @@ -6123,7 +6929,7 @@ void *cookie, **next; /* ------------------------------------------------------------------------ */ -/* Function: fr_findtunebyname */ +/* Function: ipf_tune_findbyname */ /* Returns: NULL = search failed, else pointer to tune struct */ /* Parameters: name(I) - name of the tuneable entry to find. */ /* */ @@ -6131,17 +6937,14 @@ void *cookie, **next; /* for an entry with a matching name. If we can find one, return a pointer */ /* to the matching structure. */ /* ------------------------------------------------------------------------ */ -static ipftuneable_t *fr_findtunebyname(name) -const char *name; +static ipftuneable_t * +ipf_tune_findbyname(top, name) + ipftuneable_t *top; + const char *name; { ipftuneable_t *ta; - for (ta = ipf_tuneables; ta->ipft_name != NULL; ta++) - if (!strcmp(ta->ipft_name, name)) { - return ta; - } - - for (ta = ipf_tunelist; ta != NULL; ta = ta->ipft_next) + for (ta = top; ta != NULL; ta = ta->ipft_next) if (!strcmp(ta->ipft_name, name)) { return ta; } @@ -6151,24 +6954,175 @@ const char *name; /* ------------------------------------------------------------------------ */ -/* Function: fr_addipftune */ +/* Function: ipf_tune_add_array */ /* Returns: int - 0 == success, else failure */ -/* Parameters: newtune - pointer to new tune struct to add to tuneables */ +/* Parameters: newtune - pointer to new tune array to add to tuneables */ /* */ -/* Appends the tune structure pointer to by "newtune" to the end of the */ -/* current list of "dynamic" tuneable parameters. Once added, the owner */ -/* of the object is not expected to ever change "ipft_next". */ +/* Appends tune structures from the array passed in (newtune) to the end of */ +/* the current list of "dynamic" tuneable parameters. */ +/* If any entry to be added is already present (by name) then the operation */ +/* is aborted - entries that have been added are removed before returning. */ +/* An entry with no name (NULL) is used as the indication that the end of */ +/* the array has been reached. */ /* ------------------------------------------------------------------------ */ -int fr_addipftune(newtune) -ipftuneable_t *newtune; +int +ipf_tune_add_array(softc, newtune) + ipf_main_softc_t *softc; + ipftuneable_t *newtune; +{ + ipftuneable_t *nt, *dt; + int error = 0; + + for (nt = newtune; nt->ipft_name != NULL; nt++) { + error = ipf_tune_add(softc, nt); + if (error != 0) { + for (dt = newtune; dt != nt; dt++) { + (void) ipf_tune_del(softc, dt); + } + } + } + + return error; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_tune_array_link */ +/* Returns: 0 == success, -1 == failure */ +/* Parameters: softc(I) - soft context pointerto work with */ +/* array(I) - pointer to an array of tuneables */ +/* */ +/* Given an array of tunables (array), append them to the current list of */ +/* tuneables for this context (softc->ipf_tuners.) To properly prepare the */ +/* the array for being appended to the list, initialise all of the next */ +/* pointers so we don't need to walk parts of it with ++ and others with */ +/* next. The array is expected to have an entry with a NULL name as the */ +/* terminator. Trying to add an array with no non-NULL names will return as */ +/* a failure. */ +/* ------------------------------------------------------------------------ */ +int +ipf_tune_array_link(softc, array) + ipf_main_softc_t *softc; + ipftuneable_t *array; +{ + ipftuneable_t *t, **p; + + t = array; + if (t->ipft_name == NULL) + return -1; + + for (; t[1].ipft_name != NULL; t++) + t[0].ipft_next = &t[1]; + t->ipft_next = NULL; + + /* + * Since a pointer to the last entry isn't kept, we need to find it + * each time we want to add new variables to the list. + */ + for (p = &softc->ipf_tuners; (t = *p) != NULL; p = &t->ipft_next) + if (t->ipft_name == NULL) + break; + *p = array; + + return 0; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_tune_array_unlink */ +/* Returns: 0 == success, -1 == failure */ +/* Parameters: softc(I) - soft context pointerto work with */ +/* array(I) - pointer to an array of tuneables */ +/* */ +/* ------------------------------------------------------------------------ */ +int +ipf_tune_array_unlink(softc, array) + ipf_main_softc_t *softc; + ipftuneable_t *array; +{ + ipftuneable_t *t, **p; + + for (p = &softc->ipf_tuners; (t = *p) != NULL; p = &t->ipft_next) + if (t == array) + break; + if (t == NULL) + return -1; + + for (; t[1].ipft_name != NULL; t++) + ; + + *p = t->ipft_next; + + return 0; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_tune_array_copy */ +/* Returns: NULL = failure, else pointer to new array */ +/* Parameters: base(I) - pointer to structure base */ +/* size(I) - size of the array at template */ +/* template(I) - original array to copy */ +/* */ +/* Allocate memory for a new set of tuneable values and copy everything */ +/* from template into the new region of memory. The new region is full of */ +/* uninitialised pointers (ipft_next) so set them up. Now, ipftp_offset... */ +/* */ +/* NOTE: the following assumes that sizeof(long) == sizeof(void *) */ +/* In the array template, ipftp_offset is the offset (in bytes) of the */ +/* location of the tuneable value inside the structure pointed to by base. */ +/* As ipftp_offset is a union over the pointers to the tuneable values, if */ +/* we add base to the copy's ipftp_offset, copy ends up with a pointer in */ +/* ipftp_void that points to the stored value. */ +/* ------------------------------------------------------------------------ */ +ipftuneable_t * +ipf_tune_array_copy(base, size, template) + void *base; + size_t size; + ipftuneable_t *template; +{ + ipftuneable_t *copy; + int i; + + + KMALLOCS(copy, ipftuneable_t *, size); + if (copy == NULL) { + return NULL; + } + bcopy(template, copy, size); + + for (i = 0; copy[i].ipft_name; i++) { + copy[i].ipft_una.ipftp_offset += (u_long)base; + copy[i].ipft_next = copy + i + 1; + } + + return copy; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_tune_add */ +/* Returns: int - 0 == success, else failure */ +/* Parameters: newtune - pointer to new tune entry to add to tuneables */ +/* */ +/* Appends tune structures from the array passed in (newtune) to the end of */ +/* the current list of "dynamic" tuneable parameters. Once added, the */ +/* owner of the object is not expected to ever change "ipft_next". */ +/* ------------------------------------------------------------------------ */ +int +ipf_tune_add(softc, newtune) + ipf_main_softc_t *softc; + ipftuneable_t *newtune; { ipftuneable_t *ta, **tap; - ta = fr_findtunebyname(newtune->ipft_name); - if (ta != NULL) + ta = ipf_tune_findbyname(softc->ipf_tuners, newtune->ipft_name); + if (ta != NULL) { + IPFERROR(74); return EEXIST; + } - for (tap = &ipf_tunelist; *tap != NULL; tap = &(*tap)->ipft_next) + for (tap = &softc->ipf_tuners; *tap != NULL; tap = &(*tap)->ipft_next) ; newtune->ipft_next = NULL; @@ -6178,33 +7132,72 @@ ipftuneable_t *newtune; /* ------------------------------------------------------------------------ */ -/* Function: fr_delipftune */ +/* Function: ipf_tune_del */ /* Returns: int - 0 == success, else failure */ -/* Parameters: oldtune - pointer to tune struct to remove from the list of */ +/* Parameters: oldtune - pointer to tune entry to remove from the list of */ /* current dynamic tuneables */ /* */ /* Search for the tune structure, by pointer, in the list of those that are */ /* dynamically added at run time. If found, adjust the list so that this */ /* structure is no longer part of it. */ /* ------------------------------------------------------------------------ */ -int fr_delipftune(oldtune) -ipftuneable_t *oldtune; +int +ipf_tune_del(softc, oldtune) + ipf_main_softc_t *softc; + ipftuneable_t *oldtune; { ipftuneable_t *ta, **tap; + int error = 0; - for (tap = &ipf_tunelist; (ta = *tap) != NULL; tap = &ta->ipft_next) + for (tap = &softc->ipf_tuners; (ta = *tap) != NULL; + tap = &ta->ipft_next) { if (ta == oldtune) { *tap = oldtune->ipft_next; oldtune->ipft_next = NULL; - return 0; + break; } + } - return ESRCH; + if (ta == NULL) { + error = ESRCH; + IPFERROR(75); + } + return error; } /* ------------------------------------------------------------------------ */ -/* Function: fr_ipftune */ +/* Function: ipf_tune_del_array */ +/* Returns: int - 0 == success, else failure */ +/* Parameters: oldtune - pointer to tuneables array */ +/* */ +/* Remove each tuneable entry in the array from the list of "dynamic" */ +/* tunables. If one entry should fail to be found, an error will be */ +/* returned and no further ones removed. */ +/* An entry with a NULL name is used as the indicator of the last entry in */ +/* the array. */ +/* ------------------------------------------------------------------------ */ +int +ipf_tune_del_array(softc, oldtune) + ipf_main_softc_t *softc; + ipftuneable_t *oldtune; +{ + ipftuneable_t *ot; + int error = 0; + + for (ot = oldtune; ot->ipft_name != NULL; ot++) { + error = ipf_tune_del(softc, ot); + if (error != 0) + break; + } + + return error; + +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_tune */ /* Returns: int - 0 == success, else failure */ /* Parameters: cmd(I) - ioctl command number */ /* data(I) - pointer to ioctl data structure */ @@ -6216,16 +7209,18 @@ ipftuneable_t *oldtune; /* and 'destruction' routines of the various components of ipfilter are all */ /* each responsible for handling their own values being too big. */ /* ------------------------------------------------------------------------ */ -int fr_ipftune(cmd, data) -ioctlcmd_t cmd; -void *data; +int +ipf_ipftune(softc, cmd, data) + ipf_main_softc_t *softc; + ioctlcmd_t cmd; + void *data; { ipftuneable_t *ta; ipftune_t tu; void *cookie; int error; - error = fr_inobj(data, &tu, IPFOBJ_TUNEABLE); + error = ipf_inobj(softc, data, NULL, &tu, IPFOBJ_TUNEABLE); if (error != 0) return error; @@ -6246,9 +7241,10 @@ void *data; * at the front of the list. */ if (cookie != NULL) { - ta = fr_findtunebycookie(cookie, &tu.ipft_cookie); + ta = ipf_tune_findbycookie(&softc->ipf_tuners, + cookie, &tu.ipft_cookie); } else { - ta = ipf_tuneables; + ta = softc->ipf_tuners; tu.ipft_cookie = ta + 1; } if (ta != NULL) { @@ -6256,8 +7252,10 @@ void *data; * Entry found, but does the data pointed to by that * row fit in what we can return? */ - if (ta->ipft_sz > sizeof(tu.ipft_un)) + if (ta->ipft_sz > sizeof(tu.ipft_un)) { + IPFERROR(76); return EINVAL; + } tu.ipft_vlong = 0; if (ta->ipft_sz == sizeof(u_long)) @@ -6277,7 +7275,7 @@ void *data; MIN(sizeof(tu.ipft_name), strlen(ta->ipft_name) + 1)); } - error = fr_outobj(data, &tu, IPFOBJ_TUNEABLE); + error = ipf_outobj(softc, data, &tu, IPFOBJ_TUNEABLE); break; case SIOCIPFGET : @@ -6286,13 +7284,16 @@ void *data; * Search by name or by cookie value for a particular entry * in the tuning paramter table. */ + IPFERROR(77); error = ESRCH; if (cookie != NULL) { - ta = fr_findtunebycookie(cookie, NULL); + ta = ipf_tune_findbycookie(&softc->ipf_tuners, + cookie, NULL); if (ta != NULL) error = 0; } else if (tu.ipft_name[0] != '\0') { - ta = fr_findtunebyname(tu.ipft_name); + ta = ipf_tune_findbyname(softc->ipf_tuners, + tu.ipft_name); if (ta != NULL) error = 0; } @@ -6317,7 +7318,7 @@ void *data; tu.ipft_min = ta->ipft_min; tu.ipft_max = ta->ipft_max; tu.ipft_flags = ta->ipft_flags; - error = fr_outobj(data, &tu, IPFOBJ_TUNEABLE); + error = ipf_outobj(softc, data, &tu, IPFOBJ_TUNEABLE); } else if (cmd == (ioctlcmd_t)SIOCIPFSET) { /* @@ -6328,35 +7329,49 @@ void *data; u_long in; if (((ta->ipft_flags & IPFT_WRDISABLED) != 0) && - (fr_running > 0)) { + (softc->ipf_running > 0)) { + IPFERROR(78); error = EBUSY; break; } in = tu.ipft_vlong; if (in < ta->ipft_min || in > ta->ipft_max) { + IPFERROR(79); error = EINVAL; break; } - if (ta->ipft_sz == sizeof(u_long)) { + if (ta->ipft_func != NULL) { + SPL_INT(s); + + SPL_NET(s); + error = (*ta->ipft_func)(softc, ta, + &tu.ipft_un); + SPL_X(s); + + } else if (ta->ipft_sz == sizeof(u_long)) { tu.ipft_vlong = *ta->ipft_plong; *ta->ipft_plong = in; + } else if (ta->ipft_sz == sizeof(u_int)) { tu.ipft_vint = *ta->ipft_pint; *ta->ipft_pint = (u_int)(in & 0xffffffff); + } else if (ta->ipft_sz == sizeof(u_short)) { tu.ipft_vshort = *ta->ipft_pshort; *ta->ipft_pshort = (u_short)(in & 0xffff); + } else if (ta->ipft_sz == sizeof(u_char)) { tu.ipft_vchar = *ta->ipft_pchar; *ta->ipft_pchar = (u_char)(in & 0xff); } - error = fr_outobj(data, &tu, IPFOBJ_TUNEABLE); + error = ipf_outobj(softc, data, &tu, IPFOBJ_TUNEABLE); } break; default : + IPFERROR(80); error = EINVAL; break; } @@ -6366,109 +7381,7 @@ void *data; /* ------------------------------------------------------------------------ */ -/* Function: fr_initialise */ -/* Returns: int - 0 == success, < 0 == failure */ -/* Parameters: None. */ -/* */ -/* Call of the initialise functions for all the various subsystems inside */ -/* of IPFilter. If any of them should fail, return immeadiately a failure */ -/* BUT do not try to recover from the error here. */ -/* ------------------------------------------------------------------------ */ -int fr_initialise() -{ - int i; - - bzero(&frstats, sizeof(frstats)); - -#ifdef IPFILTER_LOG - i = fr_loginit(); - if (i < 0) - return -10 + i; -#endif - i = fr_natinit(); - if (i < 0) - return -20 + i; - - i = fr_stateinit(); - if (i < 0) - return -30 + i; - - i = fr_authinit(); - if (i < 0) - return -40 + i; - - i = fr_fraginit(); - if (i < 0) - return -50 + i; - - i = appr_init(); - if (i < 0) - return -60 + i; - -#ifdef IPFILTER_SYNC - i = ipfsync_init(); - if (i < 0) - return -70 + i; -#endif -#ifdef IPFILTER_SCAN - i = ipsc_init(); - if (i < 0) - return -80 + i; -#endif -#ifdef IPFILTER_LOOKUP - i = ip_lookup_init(); - if (i < 0) - return -90 + i; -#endif -#ifdef IPFILTER_COMPILED - ipfrule_add(); -#endif - return 0; -} - - -/* ------------------------------------------------------------------------ */ -/* Function: fr_deinitialise */ -/* Returns: None. */ -/* Parameters: None. */ -/* */ -/* Call all the various subsystem cleanup routines to deallocate memory or */ -/* destroy locks or whatever they've done that they need to now undo. */ -/* The order here IS important as there are some cross references of */ -/* internal data structures. */ -/* ------------------------------------------------------------------------ */ -void fr_deinitialise() -{ - fr_fragunload(); - fr_authunload(); - fr_natunload(); - fr_stateunload(); -#ifdef IPFILTER_SCAN - fr_scanunload(); -#endif - appr_unload(); - -#ifdef IPFILTER_COMPILED - ipfrule_remove(); -#endif - - (void) frflush(IPL_LOGIPF, 0, FR_INQUE|FR_OUTQUE|FR_INACTIVE); - (void) frflush(IPL_LOGIPF, 0, FR_INQUE|FR_OUTQUE); - (void) frflush(IPL_LOGCOUNT, 0, FR_INQUE|FR_OUTQUE|FR_INACTIVE); - (void) frflush(IPL_LOGCOUNT, 0, FR_INQUE|FR_OUTQUE); - -#ifdef IPFILTER_LOOKUP - ip_lookup_unload(); -#endif - -#ifdef IPFILTER_LOG - fr_logunload(); -#endif -} - - -/* ------------------------------------------------------------------------ */ -/* Function: fr_zerostats */ +/* Function: ipf_zerostats */ /* Returns: int - 0 = success, else failure */ /* Parameters: data(O) - pointer to pointer for copying data back to */ /* */ @@ -6476,30 +7389,38 @@ void fr_deinitialise() /* current ones in the kernel. The lock is only held across the bzero() as */ /* the copyout may result in paging (ie network activity.) */ /* ------------------------------------------------------------------------ */ -int fr_zerostats(data) -void *data; +int +ipf_zerostats(softc, data) + ipf_main_softc_t *softc; + caddr_t data; { friostat_t fio; + ipfobj_t obj; int error; - fr_getstat(&fio); - error = fr_outobj(data, &fio, IPFOBJ_IPFSTAT); - if (error) - return EFAULT; + error = ipf_inobj(softc, data, &obj, &fio, IPFOBJ_IPFSTAT); + if (error != 0) + return error; + ipf_getstat(softc, &fio, obj.ipfo_rev); + error = ipf_outobj(softc, data, &fio, IPFOBJ_IPFSTAT); + if (error != 0) + return error; - WRITE_ENTER(&ipf_mutex); - bzero(&frstats, sizeof(frstats)); - RWLOCK_EXIT(&ipf_mutex); + WRITE_ENTER(&softc->ipf_mutex); + bzero(&softc->ipf_stats, sizeof(softc->ipf_stats)); + RWLOCK_EXIT(&softc->ipf_mutex); return 0; } /* ------------------------------------------------------------------------ */ -/* Function: fr_resolvedest */ +/* Function: ipf_resolvedest */ /* Returns: Nil */ -/* Parameters: fdp(IO) - pointer to destination information to resolve */ -/* v(I) - IP protocol version to match */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* base(I) - where strings are stored */ +/* fdp(IO) - pointer to destination information to resolve */ +/* v(I) - IP protocol version to match */ /* */ /* Looks up an interface name in the frdest structure pointed to by fdp and */ /* if a matching name can be found for the particular IP protocol version */ @@ -6507,52 +7428,66 @@ void *data; /* found, then set the interface pointer to be -1 as NULL is considered to */ /* indicate there is no information at all in the structure. */ /* ------------------------------------------------------------------------ */ -void fr_resolvedest(fdp, v) -frdest_t *fdp; -int v; +int +ipf_resolvedest(softc, base, fdp, v) + ipf_main_softc_t *softc; + char *base; + frdest_t *fdp; + int v; { + int errval = 0; void *ifp; ifp = NULL; - v = v; /* LINT */ - if (*fdp->fd_ifname != '\0') { - ifp = GETIFP(fdp->fd_ifname, v); - if (ifp == NULL) - ifp = (void *)-1; + if (fdp->fd_name != -1) { + if (fdp->fd_type == FRD_DSTLIST) { + ifp = ipf_lookup_res_name(softc, IPL_LOGIPF, + IPLT_DSTLIST, + base + fdp->fd_name, + NULL); + if (ifp == NULL) { + IPFERROR(144); + errval = ESRCH; + } + } else { + ifp = GETIFP(base + fdp->fd_name, v); + if (ifp == NULL) + ifp = (void *)-1; + } } - fdp->fd_ifp = ifp; + fdp->fd_ptr = ifp; + + if ((ifp != NULL) && (ifp != (void *)-1)) { + fdp->fd_local = ipf_deliverlocal(softc, v, ifp, &fdp->fd_ip6); + } + + return errval; } /* ------------------------------------------------------------------------ */ -/* Function: fr_resolvenic */ +/* Function: ipf_resolvenic */ /* Returns: void* - NULL = wildcard name, -1 = failed to find NIC, else */ /* pointer to interface structure for NIC */ -/* Parameters: name(I) - complete interface name */ +/* Parameters: softc(I)- pointer to soft context main structure */ +/* name(I) - complete interface name */ /* v(I) - IP protocol version */ /* */ /* Look for a network interface structure that firstly has a matching name */ /* to that passed in and that is also being used for that IP protocol */ /* version (necessary on some platforms where there are separate listings */ /* for both IPv4 and IPv6 on the same physical NIC. */ -/* */ -/* One might wonder why name gets terminated with a \0 byte in here. The */ -/* reason is an interface name could get into the kernel structures of ipf */ -/* in any number of ways and so long as they all use the same sized array */ -/* to put the name in, it makes sense to ensure it gets null terminated */ -/* before it is used for its intended purpose - finding its match in the */ -/* kernel's list of configured interfaces. */ -/* */ -/* NOTE: This SHOULD ONLY be used with IPFilter structures that have an */ -/* array for the name that is LIFNAMSIZ bytes (at least) in length. */ /* ------------------------------------------------------------------------ */ -void *fr_resolvenic(name, v) -char *name; -int v; +void * +ipf_resolvenic(softc, name, v) + ipf_main_softc_t *softc; + char *name; + int v; { void *nic; + softc = softc; /* gcc -Wextra */ if (name[0] == '\0') return NULL; @@ -6560,8 +7495,6 @@ int v; return NULL; } - name[LIFNAMSIZ - 1] = '\0'; - nic = GETIFP(name, v); if (nic == NULL) nic = (void *)-1; @@ -6569,68 +7502,121 @@ int v; } -ipftoken_t *ipftokenhead = NULL, **ipftokentail = &ipftokenhead; - - /* ------------------------------------------------------------------------ */ -/* Function: ipf_expiretokens */ +/* Function: ipf_token_expire */ /* Returns: None. */ -/* Parameters: None. */ +/* Parameters: softc(I) - pointer to soft context main structure */ /* */ /* This function is run every ipf tick to see if there are any tokens that */ /* have been held for too long and need to be freed up. */ /* ------------------------------------------------------------------------ */ -void ipf_expiretokens() +void +ipf_token_expire(softc) + ipf_main_softc_t *softc; { ipftoken_t *it; - WRITE_ENTER(&ipf_tokens); - while ((it = ipftokenhead) != NULL) { - if (it->ipt_die > fr_ticks) + WRITE_ENTER(&softc->ipf_tokens); + while ((it = softc->ipf_token_head) != NULL) { + if (it->ipt_die > softc->ipf_ticks) break; - ipf_freetoken(it); + ipf_token_deref(softc, it); } - RWLOCK_EXIT(&ipf_tokens); + RWLOCK_EXIT(&softc->ipf_tokens); } /* ------------------------------------------------------------------------ */ -/* Function: ipf_deltoken */ +/* Function: ipf_token_flush */ +/* Returns: None. */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* */ +/* Loop through all of the existing tokens and call deref to see if they */ +/* can be freed. Normally a function like this might just loop on */ +/* ipf_token_head but there is a chance that a token might have a ref count */ +/* of greater than one and in that case the the reference would drop twice */ +/* by code that is only entitled to drop it once. */ +/* ------------------------------------------------------------------------ */ +static void +ipf_token_flush(softc) + ipf_main_softc_t *softc; +{ + ipftoken_t *it, *next; + + WRITE_ENTER(&softc->ipf_tokens); + for (it = softc->ipf_token_head; it != NULL; it = next) { + next = it->ipt_next; + (void) ipf_token_deref(softc, it); + } + RWLOCK_EXIT(&softc->ipf_tokens); +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_token_del */ /* Returns: int - 0 = success, else error */ -/* Parameters: type(I) - the token type to match */ +/* Parameters: softc(I)- pointer to soft context main structure */ +/* type(I) - the token type to match */ /* uid(I) - uid owning the token */ /* ptr(I) - context pointer for the token */ /* */ /* This function looks for a a token in the current list that matches up */ /* the fields (type, uid, ptr). If none is found, ESRCH is returned, else */ -/* call ipf_freetoken() to remove it from the list. */ +/* call ipf_token_dewref() to remove it from the list. In the event that */ +/* the token has a reference held elsewhere, setting ipt_complete to 2 */ +/* enables debugging to distinguish between the two paths that ultimately */ +/* lead to a token to be deleted. */ /* ------------------------------------------------------------------------ */ -int ipf_deltoken(type, uid, ptr) -int type, uid; -void *ptr; +int +ipf_token_del(softc, type, uid, ptr) + ipf_main_softc_t *softc; + int type, uid; + void *ptr; { ipftoken_t *it; - int error = ESRCH; + int error; - WRITE_ENTER(&ipf_tokens); - for (it = ipftokenhead; it != NULL; it = it->ipt_next) + IPFERROR(82); + error = ESRCH; + + WRITE_ENTER(&softc->ipf_tokens); + for (it = softc->ipf_token_head; it != NULL; it = it->ipt_next) { if (ptr == it->ipt_ctx && type == it->ipt_type && uid == it->ipt_uid) { - ipf_freetoken(it); + it->ipt_complete = 2; + ipf_token_deref(softc, it); error = 0; break; + } } - RWLOCK_EXIT(&ipf_tokens); + RWLOCK_EXIT(&softc->ipf_tokens); return error; } /* ------------------------------------------------------------------------ */ -/* Function: ipf_findtoken */ +/* Function: ipf_token_mark_complete */ +/* Returns: None. */ +/* Parameters: token(I) - pointer to token structure */ +/* */ +/* Mark a token as being ineligable for being found with ipf_token_find. */ +/* ------------------------------------------------------------------------ */ +void +ipf_token_mark_complete(token) + ipftoken_t *token; +{ + if (token->ipt_complete == 0) + token->ipt_complete = 1; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_token_find */ /* Returns: ipftoken_t * - NULL if no memory, else pointer to token */ -/* Parameters: type(I) - the token type to match */ +/* Parameters: softc(I)- pointer to soft context main structure */ +/* type(I) - the token type to match */ /* uid(I) - uid owning the token */ /* ptr(I) - context pointer for the token */ /* */ @@ -6638,97 +7624,115 @@ void *ptr; /* matches the tuple (type, uid, ptr). If one cannot be found then one is */ /* allocated. If one is found then it is moved to the top of the list of */ /* currently active tokens. */ -/* */ -/* NOTE: It is by design that this function returns holding a read lock on */ -/* ipf_tokens. Callers must make sure they release it! */ /* ------------------------------------------------------------------------ */ -ipftoken_t *ipf_findtoken(type, uid, ptr) -int type, uid; -void *ptr; +ipftoken_t * +ipf_token_find(softc, type, uid, ptr) + ipf_main_softc_t *softc; + int type, uid; + void *ptr; { ipftoken_t *it, *new; KMALLOC(new, ipftoken_t *); + if (new != NULL) + bzero((char *)new, sizeof(*new)); - WRITE_ENTER(&ipf_tokens); - for (it = ipftokenhead; it != NULL; it = it->ipt_next) { - if (it->ipt_alive == 0) - continue; - if (ptr == it->ipt_ctx && type == it->ipt_type && - uid == it->ipt_uid) + WRITE_ENTER(&softc->ipf_tokens); + for (it = softc->ipf_token_head; it != NULL; it = it->ipt_next) { + if ((ptr == it->ipt_ctx) && (type == it->ipt_type) && + (uid == it->ipt_uid) && (it->ipt_complete < 2)) break; } if (it == NULL) { it = new; new = NULL; - if (it == NULL) + if (it == NULL) { + RWLOCK_EXIT(&softc->ipf_tokens); return NULL; - it->ipt_data = NULL; + } it->ipt_ctx = ptr; it->ipt_uid = uid; it->ipt_type = type; - it->ipt_next = NULL; - it->ipt_alive = 1; + it->ipt_ref = 1; } else { if (new != NULL) { KFREE(new); new = NULL; } - ipf_unlinktoken(it); + if (it->ipt_complete > 0) + it = NULL; + else + ipf_token_unlink(softc, it); } - it->ipt_pnext = ipftokentail; - *ipftokentail = it; - ipftokentail = &it->ipt_next; - it->ipt_next = NULL; - it->ipt_die = fr_ticks + 2; + if (it != NULL) { + it->ipt_pnext = softc->ipf_token_tail; + *softc->ipf_token_tail = it; + softc->ipf_token_tail = &it->ipt_next; + it->ipt_next = NULL; + it->ipt_ref++; - MUTEX_DOWNGRADE(&ipf_tokens); + it->ipt_die = softc->ipf_ticks + 20; + } + + RWLOCK_EXIT(&softc->ipf_tokens); return it; } /* ------------------------------------------------------------------------ */ -/* Function: ipf_unlinktoken */ +/* Function: ipf_token_unlink */ /* Returns: None. */ -/* Parameters: token(I) - pointer to token structure */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* token(I) - pointer to token structure */ +/* Write Locks: ipf_tokens */ /* */ /* This function unlinks a token structure from the linked list of tokens */ /* that "own" it. The head pointer never needs to be explicitly adjusted */ /* but the tail does due to the linked list implementation. */ /* ------------------------------------------------------------------------ */ -static void ipf_unlinktoken(token) -ipftoken_t *token; +static void +ipf_token_unlink(softc, token) + ipf_main_softc_t *softc; + ipftoken_t *token; { - if (ipftokentail == &token->ipt_next) - ipftokentail = token->ipt_pnext; + if (softc->ipf_token_tail == &token->ipt_next) + softc->ipf_token_tail = token->ipt_pnext; *token->ipt_pnext = token->ipt_next; if (token->ipt_next != NULL) token->ipt_next->ipt_pnext = token->ipt_pnext; + token->ipt_next = NULL; + token->ipt_pnext = NULL; } /* ------------------------------------------------------------------------ */ -/* Function: ipf_freetoken */ -/* Returns: None. */ -/* Parameters: token(I) - pointer to token structure */ +/* Function: ipf_token_deref */ +/* Returns: int - 0 == token freed, else reference count */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* token(I) - pointer to token structure */ +/* Write Locks: ipf_tokens */ /* */ -/* This function unlinks a token from the linked list and on the path to */ -/* free'ing the data, it calls the dereference function that is associated */ -/* with the type of data pointed to by the token as it is considered to */ -/* hold a reference to it. */ +/* Drop the reference count on the token structure and if it drops to zero, */ +/* call the dereference function for the token type because it is then */ +/* possible to free the token data structure. */ /* ------------------------------------------------------------------------ */ -void ipf_freetoken(token) -ipftoken_t *token; +int +ipf_token_deref(softc, token) + ipf_main_softc_t *softc; + ipftoken_t *token; { void *data, **datap; - ipf_unlinktoken(token); + ASSERT(token->ipt_ref > 0); + token->ipt_ref--; + if (token->ipt_ref > 0) + return token->ipt_ref; data = token->ipt_data; datap = &data; @@ -6737,54 +7741,96 @@ ipftoken_t *token; switch (token->ipt_type) { case IPFGENITER_IPF : - (void) fr_derefrule((frentry_t **)datap); + (void) ipf_derefrule(softc, (frentry_t **)datap); break; case IPFGENITER_IPNAT : - WRITE_ENTER(&ipf_nat); - fr_ipnatderef((ipnat_t **)datap); - RWLOCK_EXIT(&ipf_nat); + WRITE_ENTER(&softc->ipf_nat); + ipf_nat_rule_deref(softc, (ipnat_t **)datap); + RWLOCK_EXIT(&softc->ipf_nat); break; case IPFGENITER_NAT : - fr_natderef((nat_t **)datap); + ipf_nat_deref(softc, (nat_t **)datap); break; case IPFGENITER_STATE : - fr_statederef((ipstate_t **)datap); + ipf_state_deref(softc, (ipstate_t **)datap); break; case IPFGENITER_FRAG : -#ifdef USE_MUTEXES - fr_fragderef((ipfr_t **)datap, &ipf_frag); -#else - fr_fragderef((ipfr_t **)datap); -#endif + ipf_frag_pkt_deref(softc, (ipfr_t **)datap); break; case IPFGENITER_NATFRAG : -#ifdef USE_MUTEXES - fr_fragderef((ipfr_t **)datap, &ipf_natfrag); -#else - fr_fragderef((ipfr_t **)datap); -#endif + ipf_frag_nat_deref(softc, (ipfr_t **)datap); break; case IPFGENITER_HOSTMAP : - WRITE_ENTER(&ipf_nat); - fr_hostmapdel((hostmap_t **)datap); - RWLOCK_EXIT(&ipf_nat); + WRITE_ENTER(&softc->ipf_nat); + ipf_nat_hostmapdel(softc, (hostmap_t **)datap); + RWLOCK_EXIT(&softc->ipf_nat); break; default : -#ifdef IPFILTER_LOOKUP - ip_lookup_iterderef(token->ipt_type, data); -#endif + ipf_lookup_iterderef(softc, token->ipt_type, data); break; } } + ipf_token_unlink(softc, token); KFREE(token); + return 0; } +/* ------------------------------------------------------------------------ */ +/* Function: ipf_nextrule */ +/* Returns: frentry_t * - NULL == no more rules, else pointer to next */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* fr(I) - pointer to filter rule */ +/* out(I) - 1 == out rules, 0 == input rules */ +/* */ +/* Starting with "fr", find the next rule to visit. This includes visiting */ +/* the list of rule groups if either fr is NULL (empty list) or it is the */ +/* last rule in the list. When walking rule lists, it is either input or */ +/* output rules that are returned, never both. */ +/* ------------------------------------------------------------------------ */ +static frentry_t * +ipf_nextrule(softc, active, unit, fr, out) + ipf_main_softc_t *softc; + int active, unit; + frentry_t *fr; + int out; +{ + frentry_t *next; + frgroup_t *fg; + + if (fr != NULL && fr->fr_group != -1) { + fg = ipf_findgroup(softc, fr->fr_names + fr->fr_group, + unit, active, NULL); + if (fg != NULL) + fg = fg->fg_next; + } else { + fg = softc->ipf_groups[unit][active]; + } + + while (fg != NULL) { + next = fg->fg_start; + while (next != NULL) { + if (out) { + if (next->fr_flags & FR_OUTQUE) + return next; + } else if (next->fr_flags & FR_INQUE) { + return next; + } + next = next->fr_next; + } + if (next == NULL) + fg = fg->fg_next; + } + + return NULL; +} + /* ------------------------------------------------------------------------ */ /* Function: ipf_getnextrule */ /* Returns: int - 0 = success, else error */ -/* Parameters: t(I) - pointer to destination information to resolve */ +/* Parameters: softc(I)- pointer to soft context main structure */ +/* t(I) - pointer to destination information to resolve */ /* ptr(I) - pointer to ipfobj_t to copyin from user space */ /* */ /* This function's first job is to bring in the ipfruleiter_t structure via */ @@ -6795,47 +7841,72 @@ ipftoken_t *token; /* When we have found the rule to return, increase its reference count and */ /* if we used an existing rule to get here, decrease its reference count. */ /* ------------------------------------------------------------------------ */ -int ipf_getnextrule(ipftoken_t *t, void *ptr) +int +ipf_getnextrule(softc, t, ptr) + ipf_main_softc_t *softc; + ipftoken_t *t; + void *ptr; { frentry_t *fr, *next, zero; - int error, count, out; ipfruleiter_t it; + int error, out; frgroup_t *fg; + ipfobj_t obj; + int predict; char *dst; + int unit; - if (t == NULL || ptr == NULL) + if (t == NULL || ptr == NULL) { + IPFERROR(84); return EFAULT; - error = fr_inobj(ptr, &it, IPFOBJ_IPFITER); + } + + error = ipf_inobj(softc, ptr, &obj, &it, IPFOBJ_IPFITER); if (error != 0) return error; - if ((it.iri_inout < 0) || (it.iri_inout > 3)) - return EINVAL; - if ((it.iri_active != 0) && (it.iri_active != 1)) - return EINVAL; - if (it.iri_nrules == 0) - return ENOSPC; - if (it.iri_rule == NULL) - return EFAULT; - out = it.iri_inout & F_OUT; + if ((it.iri_inout < 0) || (it.iri_inout > 3)) { + IPFERROR(85); + return EINVAL; + } + if ((it.iri_active != 0) && (it.iri_active != 1)) { + IPFERROR(86); + return EINVAL; + } + if (it.iri_nrules == 0) { + IPFERROR(87); + return ENOSPC; + } + if (it.iri_rule == NULL) { + IPFERROR(88); + return EFAULT; + } + + fg = NULL; fr = t->ipt_data; - READ_ENTER(&ipf_mutex); + if ((it.iri_inout & F_OUT) != 0) + out = 1; + else + out = 0; + if ((it.iri_inout & F_ACIN) != 0) + unit = IPL_LOGCOUNT; + else + unit = IPL_LOGIPF; + + READ_ENTER(&softc->ipf_mutex); if (fr == NULL) { if (*it.iri_group == '\0') { - if ((it.iri_inout & F_ACIN) != 0) { - if (it.iri_v == 4) - next = ipacct[out][it.iri_active]; - else - next = ipacct6[out][it.iri_active]; + if (unit == IPL_LOGCOUNT) { + next = softc->ipf_acct[out][it.iri_active]; } else { - if (it.iri_v == 4) - next = ipfilter[out][it.iri_active]; - else - next = ipfilter6[out][it.iri_active]; + next = softc->ipf_rules[out][it.iri_active]; } + if (next == NULL) + next = ipf_nextrule(softc, it.iri_active, + unit, NULL, out); } else { - fg = fr_findgroup(it.iri_group, IPL_LOGIPF, - it.iri_active, NULL); + fg = ipf_findgroup(softc, it.iri_group, unit, + it.iri_active, NULL); if (fg != NULL) next = fg->fg_start; else @@ -6843,113 +7914,133 @@ int ipf_getnextrule(ipftoken_t *t, void *ptr) } } else { next = fr->fr_next; + if (next == NULL) + next = ipf_nextrule(softc, it.iri_active, unit, + fr, out); } + if (next != NULL && next->fr_next != NULL) + predict = 1; + else if (ipf_nextrule(softc, it.iri_active, unit, next, out) != NULL) + predict = 1; + else + predict = 0; + + if (fr != NULL) + (void) ipf_derefrule(softc, &fr); + + obj.ipfo_type = IPFOBJ_FRENTRY; dst = (char *)it.iri_rule; - count = it.iri_nrules; - /* - * The ipfruleiter may ask for more than 1 rule at a time to be - * copied out, so long as that many exist in the list to start with! - */ - for (;;) { - if (next != NULL) { - if (count == 1) { - MUTEX_ENTER(&next->fr_lock); - next->fr_ref++; - MUTEX_EXIT(&next->fr_lock); - t->ipt_data = next; - } - } else { - bzero(&zero, sizeof(zero)); - next = &zero; - count = 1; - t->ipt_data = NULL; - } - RWLOCK_EXIT(&ipf_mutex); - error = COPYOUT(next, dst, sizeof(*next)); - if (error != 0) - return EFAULT; + if (next != NULL) { + obj.ipfo_size = next->fr_size; + MUTEX_ENTER(&next->fr_lock); + next->fr_ref++; + MUTEX_EXIT(&next->fr_lock); + t->ipt_data = next; + } else { + obj.ipfo_size = sizeof(frentry_t); + bzero(&zero, sizeof(zero)); + next = &zero; + t->ipt_data = NULL; + } + it.iri_rule = predict ? next : NULL; + if (predict == 0) + ipf_token_mark_complete(t); + RWLOCK_EXIT(&softc->ipf_mutex); + + obj.ipfo_ptr = dst; + error = ipf_outobjk(softc, &obj, next); + if (error == 0 && t->ipt_data != NULL) { + dst += obj.ipfo_size; if (next->fr_data != NULL) { - dst += sizeof(*next); - error = COPYOUT(next->fr_data, dst, next->fr_dsize); - if (error != 0) - error = EFAULT; + ipfobj_t dobj; + + if (next->fr_type == FR_T_IPFEXPR) + dobj.ipfo_type = IPFOBJ_IPFEXPR; else - dst += next->fr_dsize; + dobj.ipfo_type = IPFOBJ_FRIPF; + dobj.ipfo_size = next->fr_dsize; + dobj.ipfo_rev = obj.ipfo_rev; + dobj.ipfo_ptr = dst; + error = ipf_outobjk(softc, &dobj, next->fr_data); } - - if ((count == 1) || (error != 0)) - break; - - count--; - - READ_ENTER(&ipf_mutex); - next = next->fr_next; } - if (fr != NULL) { - (void) fr_derefrule(&fr); - } + if ((fr != NULL) && (next == &zero)) + (void) ipf_derefrule(softc, &fr); return error; } /* ------------------------------------------------------------------------ */ -/* Function: fr_frruleiter */ +/* Function: ipf_frruleiter */ /* Returns: int - 0 = success, else error */ -/* Parameters: data(I) - the token type to match */ +/* Parameters: softc(I)- pointer to soft context main structure */ +/* data(I) - the token type to match */ /* uid(I) - uid owning the token */ /* ptr(I) - context pointer for the token */ /* */ -/* This function serves as a stepping stone between fr_ipf_ioctl and */ +/* This function serves as a stepping stone between ipf_ipf_ioctl and */ /* ipf_getnextrule. It's role is to find the right token in the kernel for */ /* the process doing the ioctl and use that to ask for the next rule. */ /* ------------------------------------------------------------------------ */ -static int ipf_frruleiter(data, uid, ctx) -void *data, *ctx; -int uid; +static int +ipf_frruleiter(softc, data, uid, ctx) + ipf_main_softc_t *softc; + void *data, *ctx; + int uid; { ipftoken_t *token; + ipfruleiter_t it; + ipfobj_t obj; int error; - token = ipf_findtoken(IPFGENITER_IPF, uid, ctx); - if (token != NULL) - error = ipf_getnextrule(token, data); - else - error = EFAULT; - RWLOCK_EXIT(&ipf_tokens); + token = ipf_token_find(softc, IPFGENITER_IPF, uid, ctx); + if (token != NULL) { + error = ipf_getnextrule(softc, token, data); + WRITE_ENTER(&softc->ipf_tokens); + ipf_token_deref(softc, token); + RWLOCK_EXIT(&softc->ipf_tokens); + } else { + error = ipf_inobj(softc, data, &obj, &it, IPFOBJ_IPFITER); + if (error != 0) + return error; + it.iri_rule = NULL; + error = ipf_outobj(softc, data, &it, IPFOBJ_IPFITER); + } return error; } /* ------------------------------------------------------------------------ */ -/* Function: fr_geniter */ +/* Function: ipf_geniter */ /* Returns: int - 0 = success, else error */ -/* Parameters: token(I) - pointer to ipftoken_t structure */ -/* itp(I) - */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* token(I) - pointer to ipftoken_t structure */ +/* itp(I) - pointer to iterator data */ /* */ +/* Decide which iterator function to call using information passed through */ +/* the ipfgeniter_t structure at itp. */ /* ------------------------------------------------------------------------ */ -static int ipf_geniter(token, itp) -ipftoken_t *token; -ipfgeniter_t *itp; +static int +ipf_geniter(softc, token, itp) + ipf_main_softc_t *softc; + ipftoken_t *token; + ipfgeniter_t *itp; { int error; switch (itp->igi_type) { case IPFGENITER_FRAG : -#ifdef USE_MUTEXES - error = fr_nextfrag(token, itp, - &ipfr_list, &ipfr_tail, &ipf_frag); -#else - error = fr_nextfrag(token, itp, &ipfr_list, &ipfr_tail); -#endif + error = ipf_frag_pkt_next(softc, token, itp); break; default : + IPFERROR(92); error = EINVAL; break; } @@ -6959,41 +8050,50 @@ ipfgeniter_t *itp; /* ------------------------------------------------------------------------ */ -/* Function: fr_genericiter */ +/* Function: ipf_genericiter */ /* Returns: int - 0 = success, else error */ -/* Parameters: data(I) - the token type to match */ +/* Parameters: softc(I)- pointer to soft context main structure */ +/* data(I) - the token type to match */ /* uid(I) - uid owning the token */ /* ptr(I) - context pointer for the token */ /* */ +/* Handle the SIOCGENITER ioctl for the ipfilter device. The primary role */ /* ------------------------------------------------------------------------ */ -int ipf_genericiter(data, uid, ctx) -void *data, *ctx; -int uid; +int +ipf_genericiter(softc, data, uid, ctx) + ipf_main_softc_t *softc; + void *data, *ctx; + int uid; { ipftoken_t *token; ipfgeniter_t iter; int error; - error = fr_inobj(data, &iter, IPFOBJ_GENITER); + error = ipf_inobj(softc, data, NULL, &iter, IPFOBJ_GENITER); if (error != 0) return error; - token = ipf_findtoken(iter.igi_type, uid, ctx); + token = ipf_token_find(softc, iter.igi_type, uid, ctx); if (token != NULL) { token->ipt_subtype = iter.igi_type; - error = ipf_geniter(token, &iter); - } else - error = EFAULT; - RWLOCK_EXIT(&ipf_tokens); + error = ipf_geniter(softc, token, &iter); + WRITE_ENTER(&softc->ipf_tokens); + ipf_token_deref(softc, token); + RWLOCK_EXIT(&softc->ipf_tokens); + } else { + IPFERROR(93); + error = 0; + } return error; } /* ------------------------------------------------------------------------ */ -/* Function: fr_ipf_ioctl */ +/* Function: ipf_ipf_ioctl */ /* Returns: int - 0 = success, else error */ -/* Parameters: data(I) - the token type to match */ +/* Parameters: softc(I)- pointer to soft context main structure */ +/* data(I) - the token type to match */ /* cmd(I) - the ioctl command number */ /* mode(I) - mode flags for the ioctl */ /* uid(I) - uid owning the token */ @@ -7002,231 +8102,283 @@ int uid; /* This function handles all of the ioctl command that are actually isssued */ /* to the /dev/ipl device. */ /* ------------------------------------------------------------------------ */ -int fr_ipf_ioctl(data, cmd, mode, uid, ctx) -caddr_t data; -ioctlcmd_t cmd; -int mode, uid; -void *ctx; +int +ipf_ipf_ioctl(softc, data, cmd, mode, uid, ctx) + ipf_main_softc_t *softc; + caddr_t data; + ioctlcmd_t cmd; + int mode, uid; + void *ctx; { friostat_t fio; int error, tmp; + ipfobj_t obj; SPL_INT(s); switch (cmd) { case SIOCFRENB : - if (!(mode & FWRITE)) + if (!(mode & FWRITE)) { + IPFERROR(94); error = EPERM; - else { + } else { error = BCOPYIN(data, &tmp, sizeof(tmp)); if (error != 0) { + IPFERROR(95); error = EFAULT; break; } - WRITE_ENTER(&ipf_global); + WRITE_ENTER(&softc->ipf_global); if (tmp) { - if (fr_running > 0) + if (softc->ipf_running > 0) error = 0; else - error = ipfattach(); + error = ipfattach(softc); if (error == 0) - fr_running = 1; + softc->ipf_running = 1; else - (void) ipfdetach(); + (void) ipfdetach(softc); } else { - error = ipfdetach(); + if (softc->ipf_running == 1) + error = ipfdetach(softc); + else + error = 0; if (error == 0) - fr_running = -1; + softc->ipf_running = -1; } - RWLOCK_EXIT(&ipf_global); + RWLOCK_EXIT(&softc->ipf_global); } break; case SIOCIPFSET : if (!(mode & FWRITE)) { + IPFERROR(96); error = EPERM; break; } /* FALLTHRU */ case SIOCIPFGETNEXT : case SIOCIPFGET : - error = fr_ipftune(cmd, (void *)data); + error = ipf_ipftune(softc, cmd, (void *)data); break; case SIOCSETFF : - if (!(mode & FWRITE)) + if (!(mode & FWRITE)) { + IPFERROR(97); error = EPERM; - else { - error = BCOPYIN(data, &fr_flags, sizeof(fr_flags)); - if (error != 0) + } else { + error = BCOPYIN(data, &softc->ipf_flags, + sizeof(softc->ipf_flags)); + if (error != 0) { + IPFERROR(98); error = EFAULT; + } } break; case SIOCGETFF : - error = BCOPYOUT(&fr_flags, data, sizeof(fr_flags)); - if (error != 0) + error = BCOPYOUT(&softc->ipf_flags, data, + sizeof(softc->ipf_flags)); + if (error != 0) { + IPFERROR(99); error = EFAULT; + } break; case SIOCFUNCL : - error = fr_resolvefunc((void *)data); + error = ipf_resolvefunc(softc, (void *)data); break; case SIOCINAFR : case SIOCRMAFR : case SIOCADAFR : case SIOCZRLST : - if (!(mode & FWRITE)) + if (!(mode & FWRITE)) { + IPFERROR(100); error = EPERM; - else - error = frrequest(IPL_LOGIPF, cmd, data, fr_active, 1); + } else { + error = frrequest(softc, IPL_LOGIPF, cmd, (caddr_t)data, + softc->ipf_active, 1); + } break; case SIOCINIFR : case SIOCRMIFR : case SIOCADIFR : - if (!(mode & FWRITE)) + if (!(mode & FWRITE)) { + IPFERROR(101); error = EPERM; - else - error = frrequest(IPL_LOGIPF, cmd, data, - 1 - fr_active, 1); + } else { + error = frrequest(softc, IPL_LOGIPF, cmd, (caddr_t)data, + 1 - softc->ipf_active, 1); + } break; case SIOCSWAPA : - if (!(mode & FWRITE)) + if (!(mode & FWRITE)) { + IPFERROR(102); error = EPERM; - else { - WRITE_ENTER(&ipf_mutex); - bzero((char *)frcache, sizeof(frcache[0]) * 2); - error = BCOPYOUT(&fr_active, data, sizeof(fr_active)); - if (error != 0) + } else { + WRITE_ENTER(&softc->ipf_mutex); + error = BCOPYOUT(&softc->ipf_active, data, + sizeof(softc->ipf_active)); + if (error != 0) { + IPFERROR(103); error = EFAULT; - else - fr_active = 1 - fr_active; - RWLOCK_EXIT(&ipf_mutex); + } else { + softc->ipf_active = 1 - softc->ipf_active; + } + RWLOCK_EXIT(&softc->ipf_mutex); } break; case SIOCGETFS : - fr_getstat(&fio); - error = fr_outobj((void *)data, &fio, IPFOBJ_IPFSTAT); + error = ipf_inobj(softc, (void *)data, &obj, &fio, + IPFOBJ_IPFSTAT); + if (error != 0) + break; + ipf_getstat(softc, &fio, obj.ipfo_rev); + error = ipf_outobj(softc, (void *)data, &fio, IPFOBJ_IPFSTAT); break; case SIOCFRZST : - if (!(mode & FWRITE)) + if (!(mode & FWRITE)) { + IPFERROR(104); error = EPERM; - else - error = fr_zerostats(data); + } else + error = ipf_zerostats(softc, (caddr_t)data); break; case SIOCIPFFL : - if (!(mode & FWRITE)) + if (!(mode & FWRITE)) { + IPFERROR(105); error = EPERM; - else { + } else { error = BCOPYIN(data, &tmp, sizeof(tmp)); if (!error) { - tmp = frflush(IPL_LOGIPF, 4, tmp); + tmp = ipf_flush(softc, IPL_LOGIPF, tmp); error = BCOPYOUT(&tmp, data, sizeof(tmp)); - if (error != 0) + if (error != 0) { + IPFERROR(106); error = EFAULT; - } else + } + } else { + IPFERROR(107); error = EFAULT; + } } break; #ifdef USE_INET6 case SIOCIPFL6 : - if (!(mode & FWRITE)) + if (!(mode & FWRITE)) { + IPFERROR(108); error = EPERM; - else { + } else { error = BCOPYIN(data, &tmp, sizeof(tmp)); if (!error) { - tmp = frflush(IPL_LOGIPF, 6, tmp); + tmp = ipf_flush(softc, IPL_LOGIPF, tmp); error = BCOPYOUT(&tmp, data, sizeof(tmp)); - if (error != 0) + if (error != 0) { + IPFERROR(109); error = EFAULT; - } else + } + } else { + IPFERROR(110); error = EFAULT; + } } break; #endif case SIOCSTLCK : - error = BCOPYIN(data, &tmp, sizeof(tmp)); - if (error == 0) { - fr_state_lock = tmp; - fr_nat_lock = tmp; - fr_frag_lock = tmp; - fr_auth_lock = tmp; - } else - error = EFAULT; + if (!(mode & FWRITE)) { + IPFERROR(122); + error = EPERM; + } else { + error = BCOPYIN(data, &tmp, sizeof(tmp)); + if (error == 0) { + ipf_state_setlock(softc->ipf_state_soft, tmp); + ipf_nat_setlock(softc->ipf_nat_soft, tmp); + ipf_frag_setlock(softc->ipf_frag_soft, tmp); + ipf_auth_setlock(softc->ipf_auth_soft, tmp); + } else { + IPFERROR(111); + error = EFAULT; + } + } break; #ifdef IPFILTER_LOG case SIOCIPFFB : - if (!(mode & FWRITE)) + if (!(mode & FWRITE)) { + IPFERROR(112); error = EPERM; - else { - tmp = ipflog_clear(IPL_LOGIPF); + } else { + tmp = ipf_log_clear(softc, IPL_LOGIPF); error = BCOPYOUT(&tmp, data, sizeof(tmp)); - if (error) + if (error) { + IPFERROR(113); error = EFAULT; + } } break; #endif /* IPFILTER_LOG */ case SIOCFRSYN : - if (!(mode & FWRITE)) + if (!(mode & FWRITE)) { + IPFERROR(114); error = EPERM; - else { - WRITE_ENTER(&ipf_global); -#ifdef MENTAT + } else { + WRITE_ENTER(&softc->ipf_global); +#if (defined(MENTAT) && defined(_KERNEL)) && !defined(INSTANCES) error = ipfsync(); #else - frsync(NULL); + ipf_sync(softc, NULL); error = 0; #endif - RWLOCK_EXIT(&ipf_global); + RWLOCK_EXIT(&softc->ipf_global); } break; case SIOCGFRST : - error = fr_outobj((void *)data, fr_fragstats(), - IPFOBJ_FRAGSTAT); + error = ipf_outobj(softc, (void *)data, + ipf_frag_stats(softc->ipf_frag_soft), + IPFOBJ_FRAGSTAT); break; #ifdef IPFILTER_LOG case FIONREAD : - tmp = (int)iplused[IPL_LOGIPF]; - + tmp = ipf_log_bytesused(softc, IPL_LOGIPF); error = BCOPYOUT(&tmp, data, sizeof(tmp)); break; #endif case SIOCIPFITER : SPL_SCHED(s); - error = ipf_frruleiter(data, uid, ctx); + error = ipf_frruleiter(softc, data, uid, ctx); SPL_X(s); break; case SIOCGENITER : SPL_SCHED(s); - error = ipf_genericiter(data, uid, ctx); + error = ipf_genericiter(softc, data, uid, ctx); SPL_X(s); break; case SIOCIPFDELTOK : - SPL_SCHED(s); error = BCOPYIN(data, &tmp, sizeof(tmp)); - if (error == 0) - error = ipf_deltoken(tmp, uid, ctx); - SPL_X(s); + if (error == 0) { + SPL_SCHED(s); + error = ipf_token_del(softc, tmp, uid, ctx); + SPL_X(s); + } break; default : + IPFERROR(115); error = EINVAL; break; } @@ -7235,91 +8387,611 @@ void *ctx; } +/* ------------------------------------------------------------------------ */ +/* Function: ipf_decaps */ +/* Returns: int - -1 == decapsulation failed, else bit mask of */ +/* flags indicating packet filtering decision. */ +/* Parameters: fin(I) - pointer to packet information */ +/* pass(I) - IP protocol version to match */ +/* l5proto(I) - layer 5 protocol to decode UDP data as. */ +/* */ +/* This function is called for packets that are wrapt up in other packets, */ +/* for example, an IP packet that is the entire data segment for another IP */ +/* packet. If the basic constraints for this are satisfied, change the */ +/* buffer to point to the start of the inner packet and start processing */ +/* rules belonging to the head group this rule specifies. */ +/* ------------------------------------------------------------------------ */ +u_32_t +ipf_decaps(fin, pass, l5proto) + fr_info_t *fin; + u_32_t pass; + int l5proto; +{ + fr_info_t fin2, *fino = NULL; + int elen, hlen, nh; + grehdr_t gre; + ip_t *ip; + mb_t *m; + + if ((fin->fin_flx & FI_COALESCE) == 0) + if (ipf_coalesce(fin) == -1) + goto cantdecaps; + + m = fin->fin_m; + hlen = fin->fin_hlen; + + switch (fin->fin_p) + { + case IPPROTO_UDP : + /* + * In this case, the specific protocol being decapsulated + * inside UDP frames comes from the rule. + */ + nh = fin->fin_fr->fr_icode; + break; + + case IPPROTO_GRE : /* 47 */ + bcopy(fin->fin_dp, (char *)&gre, sizeof(gre)); + hlen += sizeof(grehdr_t); + if (gre.gr_R|gre.gr_s) + goto cantdecaps; + if (gre.gr_C) + hlen += 4; + if (gre.gr_K) + hlen += 4; + if (gre.gr_S) + hlen += 4; + + nh = IPPROTO_IP; + + /* + * If the routing options flag is set, validate that it is + * there and bounce over it. + */ +#if 0 + /* This is really heavy weight and lots of room for error, */ + /* so for now, put it off and get the simple stuff right. */ + if (gre.gr_R) { + u_char off, len, *s; + u_short af; + int end; + + end = 0; + s = fin->fin_dp; + s += hlen; + aplen = fin->fin_plen - hlen; + while (aplen > 3) { + af = (s[0] << 8) | s[1]; + off = s[2]; + len = s[3]; + aplen -= 4; + s += 4; + if (af == 0 && len == 0) { + end = 1; + break; + } + if (aplen < len) + break; + s += len; + aplen -= len; + } + if (end != 1) + goto cantdecaps; + hlen = s - (u_char *)fin->fin_dp; + } +#endif + break; + +#ifdef IPPROTO_IPIP + case IPPROTO_IPIP : /* 4 */ +#endif + nh = IPPROTO_IP; + break; + + default : /* Includes ESP, AH is special for IPv4 */ + goto cantdecaps; + } + + switch (nh) + { + case IPPROTO_IP : + case IPPROTO_IPV6 : + break; + default : + goto cantdecaps; + } + + bcopy((char *)fin, (char *)&fin2, sizeof(fin2)); + fino = fin; + fin = &fin2; + elen = hlen; +#if defined(MENTAT) && defined(_KERNEL) + m->b_rptr += elen; +#else + m->m_data += elen; + m->m_len -= elen; +#endif + fin->fin_plen -= elen; + + ip = (ip_t *)((char *)fin->fin_ip + elen); + + /* + * Make sure we have at least enough data for the network layer + * header. + */ + if (IP_V(ip) == 4) + hlen = IP_HL(ip) << 2; +#ifdef USE_INET6 + else if (IP_V(ip) == 6) + hlen = sizeof(ip6_t); +#endif + else + goto cantdecaps2; + + if (fin->fin_plen < hlen) + goto cantdecaps2; + + fin->fin_dp = (char *)ip + hlen; + + if (IP_V(ip) == 4) { + /* + * Perform IPv4 header checksum validation. + */ + if (ipf_cksum((u_short *)ip, hlen)) + goto cantdecaps2; + } + + if (ipf_makefrip(hlen, ip, fin) == -1) { +cantdecaps2: + if (m != NULL) { +#if defined(MENTAT) && defined(_KERNEL) + m->b_rptr -= elen; +#else + m->m_data -= elen; + m->m_len += elen; +#endif + } +cantdecaps: + DT1(frb_decapfrip, fr_info_t *, fin); + pass &= ~FR_CMDMASK; + pass |= FR_BLOCK|FR_QUICK; + fin->fin_reason = FRB_DECAPFRIP; + return -1; + } + + pass = ipf_scanlist(fin, pass); + + /* + * Copy the packet filter "result" fields out of the fr_info_t struct + * that is local to the decapsulation processing and back into the + * one we were called with. + */ + fino->fin_flx = fin->fin_flx; + fino->fin_rev = fin->fin_rev; + fino->fin_icode = fin->fin_icode; + fino->fin_rule = fin->fin_rule; + (void) strncpy(fino->fin_group, fin->fin_group, FR_GROUPLEN); + fino->fin_fr = fin->fin_fr; + fino->fin_error = fin->fin_error; + fino->fin_mp = fin->fin_mp; + fino->fin_m = fin->fin_m; + m = fin->fin_m; + if (m != NULL) { +#if defined(MENTAT) && defined(_KERNEL) + m->b_rptr -= elen; +#else + m->m_data -= elen; + m->m_len += elen; +#endif + } + return pass; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_matcharray_load */ +/* Returns: int - 0 = success, else error */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* data(I) - pointer to ioctl data */ +/* objp(I) - ipfobj_t structure to load data into */ +/* arrayptr(I) - pointer to location to store array pointer */ +/* */ +/* This function loads in a mathing array through the ipfobj_t struct that */ +/* describes it. Sanity checking and array size limitations are enforced */ +/* in this function to prevent userspace from trying to load in something */ +/* that is insanely big. Once the size of the array is known, the memory */ +/* required is malloc'd and returned through changing *arrayptr. The */ +/* contents of the array are verified before returning. Only in the event */ +/* of a successful call is the caller required to free up the malloc area. */ +/* ------------------------------------------------------------------------ */ +int +ipf_matcharray_load(softc, data, objp, arrayptr) + ipf_main_softc_t *softc; + caddr_t data; + ipfobj_t *objp; + int **arrayptr; +{ + int arraysize, *array, error; + + *arrayptr = NULL; + + error = BCOPYIN(data, objp, sizeof(*objp)); + if (error != 0) { + IPFERROR(116); + return EFAULT; + } + + if (objp->ipfo_type != IPFOBJ_IPFEXPR) { + IPFERROR(117); + return EINVAL; + } + + if (((objp->ipfo_size & 3) != 0) || (objp->ipfo_size == 0) || + (objp->ipfo_size > 1024)) { + IPFERROR(118); + return EINVAL; + } + + arraysize = objp->ipfo_size * sizeof(*array); + KMALLOCS(array, int *, arraysize); + if (array == NULL) { + IPFERROR(119); + return ENOMEM; + } + + error = COPYIN(objp->ipfo_ptr, array, arraysize); + if (error != 0) { + KFREES(array, arraysize); + IPFERROR(120); + return EFAULT; + } + + if (ipf_matcharray_verify(array, arraysize) != 0) { + KFREES(array, arraysize); + IPFERROR(121); + return EINVAL; + } + + *arrayptr = array; + return 0; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_matcharray_verify */ +/* Returns: Nil */ +/* Parameters: array(I) - pointer to matching array */ +/* arraysize(I) - number of elements in the array */ +/* */ +/* Verify the contents of a matching array by stepping through each element */ +/* in it. The actual commands in the array are not verified for */ +/* correctness, only that all of the sizes are correctly within limits. */ +/* ------------------------------------------------------------------------ */ +int +ipf_matcharray_verify(array, arraysize) + int *array, arraysize; +{ + int i, nelem, maxidx; + ipfexp_t *e; + + nelem = arraysize / sizeof(*array); + + /* + * Currently, it makes no sense to have an array less than 6 + * elements long - the initial size at the from, a single operation + * (minimum 4 in length) and a trailer, for a total of 6. + */ + if ((array[0] < 6) || (arraysize < 24) || (arraysize > 4096)) { + return -1; + } + + /* + * Verify the size of data pointed to by array with how long + * the array claims to be itself. + */ + if (array[0] * sizeof(*array) != arraysize) { + return -1; + } + + maxidx = nelem - 1; + /* + * The last opcode in this array should be an IPF_EXP_END. + */ + if (array[maxidx] != IPF_EXP_END) { + return -1; + } + + for (i = 1; i < maxidx; ) { + e = (ipfexp_t *)(array + i); + + /* + * The length of the bits to check must be at least 1 + * (or else there is nothing to comapre with!) and it + * cannot exceed the length of the data present. + */ + if ((e->ipfe_size < 1 ) || + (e->ipfe_size + i > maxidx)) { + return -1; + } + i += e->ipfe_size; + } + return 0; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_fr_matcharray */ +/* Returns: int - 0 = match failed, else positive match */ +/* Parameters: fin(I) - pointer to packet information */ +/* array(I) - pointer to matching array */ +/* */ +/* This function is used to apply a matching array against a packet and */ +/* return an indication of whether or not the packet successfully matches */ +/* all of the commands in it. */ +/* ------------------------------------------------------------------------ */ +static int +ipf_fr_matcharray(fin, array) + fr_info_t *fin; + int *array; +{ + int i, n, *x, rv, p; + ipfexp_t *e; + + rv = 0; + n = array[0]; + x = array + 1; + + for (; n > 0; x += 3 + x[3], rv = 0) { + e = (ipfexp_t *)x; + if (e->ipfe_cmd == IPF_EXP_END) + break; + n -= e->ipfe_size; + + /* + * The upper 16 bits currently store the protocol value. + * This is currently used with TCP and UDP port compares and + * allows "tcp.port = 80" without requiring an explicit + " "ip.pr = tcp" first. + */ + p = e->ipfe_cmd >> 16; + if ((p != 0) && (p != fin->fin_p)) + break; + + switch (e->ipfe_cmd) + { + case IPF_EXP_IP_PR : + for (i = 0; !rv && i < e->ipfe_narg; i++) { + rv |= (fin->fin_p == e->ipfe_arg0[i]); + } + break; + + case IPF_EXP_IP_SRCADDR : + if (fin->fin_v != 4) + break; + for (i = 0; !rv && i < e->ipfe_narg; i++) { + rv |= ((fin->fin_saddr & + e->ipfe_arg0[i * 2 + 1]) == + e->ipfe_arg0[i * 2]); + } + break; + + case IPF_EXP_IP_DSTADDR : + if (fin->fin_v != 4) + break; + for (i = 0; !rv && i < e->ipfe_narg; i++) { + rv |= ((fin->fin_daddr & + e->ipfe_arg0[i * 2 + 1]) == + e->ipfe_arg0[i * 2]); + } + break; + + case IPF_EXP_IP_ADDR : + if (fin->fin_v != 4) + break; + for (i = 0; !rv && i < e->ipfe_narg; i++) { + rv |= ((fin->fin_saddr & + e->ipfe_arg0[i * 2 + 1]) == + e->ipfe_arg0[i * 2]) || + ((fin->fin_daddr & + e->ipfe_arg0[i * 2 + 1]) == + e->ipfe_arg0[i * 2]); + } + break; + +#ifdef USE_INET6 + case IPF_EXP_IP6_SRCADDR : + if (fin->fin_v != 6) + break; + for (i = 0; !rv && i < e->ipfe_narg; i++) { + rv |= IP6_MASKEQ(&fin->fin_src6, + &e->ipfe_arg0[i * 8 + 4], + &e->ipfe_arg0[i * 8]); + } + break; + + case IPF_EXP_IP6_DSTADDR : + if (fin->fin_v != 6) + break; + for (i = 0; !rv && i < e->ipfe_narg; i++) { + rv |= IP6_MASKEQ(&fin->fin_dst6, + &e->ipfe_arg0[i * 8 + 4], + &e->ipfe_arg0[i * 8]); + } + break; + + case IPF_EXP_IP6_ADDR : + if (fin->fin_v != 6) + break; + for (i = 0; !rv && i < e->ipfe_narg; i++) { + rv |= IP6_MASKEQ(&fin->fin_src6, + &e->ipfe_arg0[i * 8 + 4], + &e->ipfe_arg0[i * 8]) || + IP6_MASKEQ(&fin->fin_dst6, + &e->ipfe_arg0[i * 8 + 4], + &e->ipfe_arg0[i * 8]); + } + break; +#endif + + case IPF_EXP_UDP_PORT : + case IPF_EXP_TCP_PORT : + for (i = 0; !rv && i < e->ipfe_narg; i++) { + rv |= (fin->fin_sport == e->ipfe_arg0[i]) || + (fin->fin_dport == e->ipfe_arg0[i]); + } + break; + + case IPF_EXP_UDP_SPORT : + case IPF_EXP_TCP_SPORT : + for (i = 0; !rv && i < e->ipfe_narg; i++) { + rv |= (fin->fin_sport == e->ipfe_arg0[i]); + } + break; + + case IPF_EXP_UDP_DPORT : + case IPF_EXP_TCP_DPORT : + for (i = 0; !rv && i < e->ipfe_narg; i++) { + rv |= (fin->fin_dport == e->ipfe_arg0[i]); + } + break; + + case IPF_EXP_TCP_FLAGS : + for (i = 0; !rv && i < e->ipfe_narg; i++) { + rv |= ((fin->fin_tcpf & + e->ipfe_arg0[i * 2 + 1]) == + e->ipfe_arg0[i * 2]); + } + break; + } + rv ^= e->ipfe_not; + + if (rv == 0) + break; + } + + return rv; +} + + /* ------------------------------------------------------------------------ */ /* Function: ipf_queueflush */ /* Returns: int - number of entries flushed (0 = none) */ -/* Parameters: deletefn(I) - function to call to delete entry */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* deletefn(I) - function to call to delete entry */ /* ipfqs(I) - top of the list of ipf internal queues */ /* userqs(I) - top of the list of user defined timeouts */ /* */ /* This fucntion gets called when the state/NAT hash tables fill up and we */ -/* need to try a bit harder to free up some space. The algorithm used is */ -/* to look for the oldest entries on each timeout queue and free them if */ -/* they are within the given window we are considering. Where the window */ -/* starts and the steps taken to increase its size depend upon how long ipf */ -/* has been running (fr_ticks.) Anything modified in the last 30 seconds */ -/* is not touched. */ +/* need to try a bit harder to free up some space. The algorithm used here */ +/* split into two parts but both halves have the same goal: to reduce the */ +/* number of connections considered to be "active" to the low watermark. */ +/* There are two steps in doing this: */ +/* 1) Remove any TCP connections that are already considered to be "closed" */ +/* but have not yet been removed from the state table. The two states */ +/* TCPS_TIME_WAIT and TCPS_CLOSED are considered to be the perfect */ +/* candidates for this style of removal. If freeing up entries in */ +/* CLOSED or both CLOSED and TIME_WAIT brings us to the low watermark, */ +/* we do not go on to step 2. */ +/* */ +/* 2) Look for the oldest entries on each timeout queue and free them if */ +/* they are within the given window we are considering. Where the */ +/* window starts and the steps taken to increase its size depend upon */ +/* how long ipf has been running (ipf_ticks.) Anything modified in the */ +/* last 30 seconds is not touched. */ /* touched */ -/* die fr_ticks 30*1.5 1800*1.5 | 43200*1.5 */ +/* die ipf_ticks 30*1.5 1800*1.5 | 43200*1.5 */ /* | | | | | | */ /* future <--+----------+--------+-----------+-----+-----+-----------> past */ /* now \_int=30s_/ \_int=1hr_/ \_int=12hr */ /* */ /* Points to note: */ /* - tqe_die is the time, in the future, when entries die. */ -/* - tqe_die - fr_ticks is how long left the connection has to live in ipf */ +/* - tqe_die - ipf_ticks is how long left the connection has to live in ipf */ /* ticks. */ /* - tqe_touched is when the entry was last used by NAT/state */ -/* - the closer tqe_touched is to fr_ticks, the further tqe_die will be for */ -/* any given timeout queue and vice versa. */ +/* - the closer tqe_touched is to ipf_ticks, the further tqe_die will be */ +/* ipf_ticks any given timeout queue and vice versa. */ /* - both tqe_die and tqe_touched increase over time */ /* - timeout queues are sorted with the highest value of tqe_die at the */ /* bottom and therefore the smallest values of each are at the top */ +/* - the pointer passed in as ipfqs should point to an array of timeout */ +/* queues representing each of the TCP states */ /* */ /* We start by setting up a maximum range to scan for things to move of */ /* iend (newest) to istart (oldest) in chunks of "interval". If nothing is */ /* found in that range, "interval" is adjusted (so long as it isn't 30) and */ -/* we start again with a new value for "iend" and "istart". The downside */ -/* of the current implementation is that it may return removing just 1 entry*/ -/* every time (pathological case) where it could remove more. */ +/* we start again with a new value for "iend" and "istart". This is */ +/* continued until we either finish the scan of 30 second intervals or the */ +/* low water mark is reached. */ /* ------------------------------------------------------------------------ */ -int ipf_queueflush(deletefn, ipfqs, userqs) -ipftq_delete_fn_t deletefn; -ipftq_t *ipfqs, *userqs; +int +ipf_queueflush(softc, deletefn, ipfqs, userqs, activep, size, low) + ipf_main_softc_t *softc; + ipftq_delete_fn_t deletefn; + ipftq_t *ipfqs, *userqs; + u_int *activep; + int size, low; { u_long interval, istart, iend; ipftq_t *ifq, *ifqnext; ipftqent_t *tqe, *tqn; - int removed; + int removed = 0; + + for (tqn = ipfqs[IPF_TCPS_CLOSED].ifq_head; ((tqe = tqn) != NULL); ) { + tqn = tqe->tqe_next; + if ((*deletefn)(softc, tqe->tqe_parent) == 0) + removed++; + } + if ((*activep * 100 / size) > low) { + for (tqn = ipfqs[IPF_TCPS_TIME_WAIT].ifq_head; + ((tqe = tqn) != NULL); ) { + tqn = tqe->tqe_next; + if ((*deletefn)(softc, tqe->tqe_parent) == 0) + removed++; + } + } + + if ((*activep * 100 / size) <= low) { + return removed; + } /* * NOTE: Use of "* 15 / 10" is required here because if "* 1.5" is * used then the operations are upgraded to floating point * and kernels don't like floating point... */ - if (fr_ticks > IPF_TTLVAL(43200 * 15 / 10)) { + if (softc->ipf_ticks > IPF_TTLVAL(43200 * 15 / 10)) { istart = IPF_TTLVAL(86400 * 4); interval = IPF_TTLVAL(43200); - } else if (fr_ticks > IPF_TTLVAL(1800 * 15 / 10)) { + } else if (softc->ipf_ticks > IPF_TTLVAL(1800 * 15 / 10)) { istart = IPF_TTLVAL(43200); interval = IPF_TTLVAL(1800); - } else if (fr_ticks > IPF_TTLVAL(30 * 15 / 10)) { + } else if (softc->ipf_ticks > IPF_TTLVAL(30 * 15 / 10)) { istart = IPF_TTLVAL(1800); interval = IPF_TTLVAL(30); } else { return 0; } - if (istart > fr_ticks) { - if (fr_ticks - interval < interval) + if (istart > softc->ipf_ticks) { + if (softc->ipf_ticks - interval < interval) istart = interval; else - istart = (fr_ticks / interval) * interval; + istart = (softc->ipf_ticks / interval) * interval; } - iend = fr_ticks - interval; - removed = 0; + iend = softc->ipf_ticks - interval; - for (;;) { + while ((*activep * 100 / size) > low) { u_long try; - try = fr_ticks - istart; + try = softc->ipf_ticks - istart; for (ifq = ipfqs; ifq != NULL; ifq = ifq->ifq_next) { for (tqn = ifq->ifq_head; ((tqe = tqn) != NULL); ) { if (try < tqe->tqe_touched) break; tqn = tqe->tqe_next; - if ((*deletefn)(tqe->tqe_parent) == 0) + if ((*deletefn)(softc, tqe->tqe_parent) == 0) removed++; } } @@ -7331,14 +9003,12 @@ ipftq_t *ipfqs, *userqs; if (try < tqe->tqe_touched) break; tqn = tqe->tqe_next; - if ((*deletefn)(tqe->tqe_parent) == 0) + if ((*deletefn)(softc, tqe->tqe_parent) == 0) removed++; } } if (try >= iend) { - if (removed > 0) - break; if (interval == IPF_TTLVAL(43200)) { interval = IPF_TTLVAL(1800); } else if (interval == IPF_TTLVAL(1800)) { @@ -7346,13 +9016,1200 @@ ipftq_t *ipfqs, *userqs; } else { break; } - if (interval >= fr_ticks) + if (interval >= softc->ipf_ticks) break; - iend = fr_ticks - interval; + iend = softc->ipf_ticks - interval; } istart -= interval; } return removed; } + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_deliverlocal */ +/* Returns: int - 1 = local address, 0 = non-local address */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* ipversion(I) - IP protocol version (4 or 6) */ +/* ifp(I) - network interface pointer */ +/* ipaddr(I) - IPv4/6 destination address */ +/* */ +/* This fucntion is used to determine in the address "ipaddr" belongs to */ +/* the network interface represented by ifp. */ +/* ------------------------------------------------------------------------ */ +int +ipf_deliverlocal(softc, ipversion, ifp, ipaddr) + ipf_main_softc_t *softc; + int ipversion; + void *ifp; + i6addr_t *ipaddr; +{ + i6addr_t addr; + int islocal = 0; + + if (ipversion == 4) { + if (ipf_ifpaddr(softc, 4, FRI_NORMAL, ifp, &addr, NULL) == 0) { + if (addr.in4.s_addr == ipaddr->in4.s_addr) + islocal = 1; + } + +#ifdef USE_INET6 + } else if (ipversion == 6) { + if (ipf_ifpaddr(softc, 6, FRI_NORMAL, ifp, &addr, NULL) == 0) { + if (IP6_EQ(&addr, ipaddr)) + islocal = 1; + } +#endif + } + + return islocal; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_settimeout */ +/* Returns: int - 0 = success, -1 = failure */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* t(I) - pointer to tuneable array entry */ +/* p(I) - pointer to values passed in to apply */ +/* */ +/* This function is called to set the timeout values for each distinct */ +/* queue timeout that is available. When called, it calls into both the */ +/* state and NAT code, telling them to update their timeout queues. */ +/* ------------------------------------------------------------------------ */ +static int +ipf_settimeout(softc, t, p) + struct ipf_main_softc_s *softc; + ipftuneable_t *t; + ipftuneval_t *p; +{ + + /* + * ipf_interror should be set by the functions called here, not + * by this function - it's just a middle man. + */ + if (ipf_state_settimeout(softc, t, p) == -1) + return -1; + if (ipf_nat_settimeout(softc, t, p) == -1) + return -1; + return 0; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_apply_timeout */ +/* Returns: int - 0 = success, -1 = failure */ +/* Parameters: head(I) - pointer to tuneable array entry */ +/* seconds(I) - pointer to values passed in to apply */ +/* */ +/* This function applies a timeout of "seconds" to the timeout queue that */ +/* is pointed to by "head". All entries on this list have an expiration */ +/* set to be the current tick value of ipf plus the ttl. Given that this */ +/* function should only be called when the delta is non-zero, the task is */ +/* to walk the entire list and apply the change. The sort order will not */ +/* change. The only catch is that this is O(n) across the list, so if the */ +/* queue has lots of entries (10s of thousands or 100s of thousands), it */ +/* could take a relatively long time to work through them all. */ +/* ------------------------------------------------------------------------ */ +void +ipf_apply_timeout(head, seconds) + ipftq_t *head; + u_int seconds; +{ + u_int oldtimeout, newtimeout; + ipftqent_t *tqe; + int delta; + + MUTEX_ENTER(&head->ifq_lock); + oldtimeout = head->ifq_ttl; + newtimeout = IPF_TTLVAL(seconds); + delta = oldtimeout - newtimeout; + + head->ifq_ttl = newtimeout; + + for (tqe = head->ifq_head; tqe != NULL; tqe = tqe->tqe_next) { + tqe->tqe_die += delta; + } + MUTEX_EXIT(&head->ifq_lock); +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_settimeout_tcp */ +/* Returns: int - 0 = successfully applied, -1 = failed */ +/* Parameters: t(I) - pointer to tuneable to change */ +/* p(I) - pointer to new timeout information */ +/* tab(I) - pointer to table of TCP queues */ +/* */ +/* This function applies the new timeout (p) to the TCP tunable (t) and */ +/* updates all of the entries on the relevant timeout queue by calling */ +/* ipf_apply_timeout(). */ +/* ------------------------------------------------------------------------ */ +int +ipf_settimeout_tcp(t, p, tab) + ipftuneable_t *t; + ipftuneval_t *p; + ipftq_t *tab; +{ + if (!strcmp(t->ipft_name, "tcp_idle_timeout") || + !strcmp(t->ipft_name, "tcp_established")) { + ipf_apply_timeout(&tab[IPF_TCPS_ESTABLISHED], p->ipftu_int); + } else if (!strcmp(t->ipft_name, "tcp_close_wait")) { + ipf_apply_timeout(&tab[IPF_TCPS_CLOSE_WAIT], p->ipftu_int); + } else if (!strcmp(t->ipft_name, "tcp_last_ack")) { + ipf_apply_timeout(&tab[IPF_TCPS_LAST_ACK], p->ipftu_int); + } else if (!strcmp(t->ipft_name, "tcp_timeout")) { + ipf_apply_timeout(&tab[IPF_TCPS_LISTEN], p->ipftu_int); + ipf_apply_timeout(&tab[IPF_TCPS_HALF_ESTAB], p->ipftu_int); + ipf_apply_timeout(&tab[IPF_TCPS_CLOSING], p->ipftu_int); + } else if (!strcmp(t->ipft_name, "tcp_listen")) { + ipf_apply_timeout(&tab[IPF_TCPS_LISTEN], p->ipftu_int); + } else if (!strcmp(t->ipft_name, "tcp_half_established")) { + ipf_apply_timeout(&tab[IPF_TCPS_HALF_ESTAB], p->ipftu_int); + } else if (!strcmp(t->ipft_name, "tcp_closing")) { + ipf_apply_timeout(&tab[IPF_TCPS_CLOSING], p->ipftu_int); + } else if (!strcmp(t->ipft_name, "tcp_syn_received")) { + ipf_apply_timeout(&tab[IPF_TCPS_SYN_RECEIVED], p->ipftu_int); + } else if (!strcmp(t->ipft_name, "tcp_syn_sent")) { + ipf_apply_timeout(&tab[IPF_TCPS_SYN_SENT], p->ipftu_int); + } else if (!strcmp(t->ipft_name, "tcp_closed")) { + ipf_apply_timeout(&tab[IPF_TCPS_CLOSED], p->ipftu_int); + } else if (!strcmp(t->ipft_name, "tcp_half_closed")) { + ipf_apply_timeout(&tab[IPF_TCPS_CLOSED], p->ipftu_int); + } else if (!strcmp(t->ipft_name, "tcp_time_wait")) { + ipf_apply_timeout(&tab[IPF_TCPS_TIME_WAIT], p->ipftu_int); + } else { + /* + * ipf_interror isn't set here because it should be set + * by whatever called this function. + */ + return -1; + } + return 0; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_main_soft_create */ +/* Returns: NULL = failure, else success */ +/* Parameters: arg(I) - pointer to soft context structure if already allocd */ +/* */ +/* Create the foundation soft context structure. In circumstances where it */ +/* is not required to dynamically allocate the context, a pointer can be */ +/* passed in (rather than NULL) to a structure to be initialised. */ +/* The main thing of interest is that a number of locks are initialised */ +/* here instead of in the where might be expected - in the relevant create */ +/* function elsewhere. This is done because the current locking design has */ +/* some areas where these locks are used outside of their module. */ +/* Possibly the most important exercise that is done here is setting of all */ +/* the timeout values, allowing them to be changed before init(). */ +/* ------------------------------------------------------------------------ */ +void * +ipf_main_soft_create(arg) + void *arg; +{ + ipf_main_softc_t *softc; + + if (arg == NULL) { + KMALLOC(softc, ipf_main_softc_t *); + if (softc == NULL) + return NULL; + } else { + softc = arg; + } + + bzero((char *)softc, sizeof(*softc)); + + /* + * This serves as a flag as to whether or not the softc should be + * free'd when _destroy is called. + */ + softc->ipf_dynamic_softc = (arg == NULL) ? 1 : 0; + + softc->ipf_tuners = ipf_tune_array_copy(softc, + sizeof(ipf_main_tuneables), + ipf_main_tuneables); + if (softc->ipf_tuners == NULL) { + ipf_main_soft_destroy(softc); + return NULL; + } + + MUTEX_INIT(&softc->ipf_rw, "ipf rw mutex"); + MUTEX_INIT(&softc->ipf_timeoutlock, "ipf timeout lock"); + RWLOCK_INIT(&softc->ipf_global, "ipf filter load/unload mutex"); + RWLOCK_INIT(&softc->ipf_mutex, "ipf filter rwlock"); + RWLOCK_INIT(&softc->ipf_tokens, "ipf token rwlock"); + RWLOCK_INIT(&softc->ipf_state, "ipf state rwlock"); + RWLOCK_INIT(&softc->ipf_nat, "ipf IP NAT rwlock"); + RWLOCK_INIT(&softc->ipf_poolrw, "ipf pool rwlock"); + RWLOCK_INIT(&softc->ipf_frag, "ipf frag rwlock"); + + softc->ipf_token_head = NULL; + softc->ipf_token_tail = &softc->ipf_token_head; + + softc->ipf_tcpidletimeout = FIVE_DAYS; + softc->ipf_tcpclosewait = IPF_TTLVAL(2 * TCP_MSL); + softc->ipf_tcplastack = IPF_TTLVAL(30); + softc->ipf_tcptimewait = IPF_TTLVAL(2 * TCP_MSL); + softc->ipf_tcptimeout = IPF_TTLVAL(2 * TCP_MSL); + softc->ipf_tcpsynsent = IPF_TTLVAL(2 * TCP_MSL); + softc->ipf_tcpsynrecv = IPF_TTLVAL(2 * TCP_MSL); + softc->ipf_tcpclosed = IPF_TTLVAL(30); + softc->ipf_tcphalfclosed = IPF_TTLVAL(2 * 3600); + softc->ipf_udptimeout = IPF_TTLVAL(120); + softc->ipf_udpacktimeout = IPF_TTLVAL(12); + softc->ipf_icmptimeout = IPF_TTLVAL(60); + softc->ipf_icmpacktimeout = IPF_TTLVAL(6); + softc->ipf_iptimeout = IPF_TTLVAL(60); + +#if defined(IPFILTER_DEFAULT_BLOCK) + softc->ipf_pass = FR_BLOCK|FR_NOMATCH; +#else + softc->ipf_pass = (IPF_DEFAULT_PASS)|FR_NOMATCH; +#endif + softc->ipf_minttl = 4; + softc->ipf_icmpminfragmtu = 68; + softc->ipf_flags = IPF_LOGGING; + + return softc; +} + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_main_soft_init */ +/* Returns: 0 = success, -1 = failure */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* */ +/* A null-op function that exists as a placeholder so that the flow in */ +/* other functions is obvious. */ +/* ------------------------------------------------------------------------ */ +/*ARGSUSED*/ +int +ipf_main_soft_init(softc) + ipf_main_softc_t *softc; +{ + return 0; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_main_soft_destroy */ +/* Returns: void */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* */ +/* Undo everything that we did in ipf_main_soft_create. */ +/* */ +/* The most important check that needs to be made here is whether or not */ +/* the structure was allocated by ipf_main_soft_create() by checking what */ +/* value is stored in ipf_dynamic_main. */ +/* ------------------------------------------------------------------------ */ +/*ARGSUSED*/ +void +ipf_main_soft_destroy(softc) + ipf_main_softc_t *softc; +{ + + RW_DESTROY(&softc->ipf_frag); + RW_DESTROY(&softc->ipf_poolrw); + RW_DESTROY(&softc->ipf_nat); + RW_DESTROY(&softc->ipf_state); + RW_DESTROY(&softc->ipf_tokens); + RW_DESTROY(&softc->ipf_mutex); + RW_DESTROY(&softc->ipf_global); + MUTEX_DESTROY(&softc->ipf_timeoutlock); + MUTEX_DESTROY(&softc->ipf_rw); + + if (softc->ipf_tuners != NULL) { + KFREES(softc->ipf_tuners, sizeof(ipf_main_tuneables)); + } + if (softc->ipf_dynamic_softc == 1) { + KFREE(softc); + } +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_main_soft_fini */ +/* Returns: 0 = success, -1 = failure */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* */ +/* Clean out the rules which have been added since _init was last called, */ +/* the only dynamic part of the mainline. */ +/* ------------------------------------------------------------------------ */ +int +ipf_main_soft_fini(softc) + ipf_main_softc_t *softc; +{ + (void) ipf_flush(softc, IPL_LOGIPF, FR_INQUE|FR_OUTQUE|FR_INACTIVE); + (void) ipf_flush(softc, IPL_LOGIPF, FR_INQUE|FR_OUTQUE); + (void) ipf_flush(softc, IPL_LOGCOUNT, FR_INQUE|FR_OUTQUE|FR_INACTIVE); + (void) ipf_flush(softc, IPL_LOGCOUNT, FR_INQUE|FR_OUTQUE); + + return 0; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_main_load */ +/* Returns: 0 = success, -1 = failure */ +/* Parameters: none */ +/* */ +/* Handle global initialisation that needs to be done for the base part of */ +/* IPFilter. At present this just amounts to initialising some ICMP lookup */ +/* arrays that get used by the state/NAT code. */ +/* ------------------------------------------------------------------------ */ +int +ipf_main_load() +{ + int i; + + /* fill icmp reply type table */ + for (i = 0; i <= ICMP_MAXTYPE; i++) + icmpreplytype4[i] = -1; + icmpreplytype4[ICMP_ECHO] = ICMP_ECHOREPLY; + icmpreplytype4[ICMP_TSTAMP] = ICMP_TSTAMPREPLY; + icmpreplytype4[ICMP_IREQ] = ICMP_IREQREPLY; + icmpreplytype4[ICMP_MASKREQ] = ICMP_MASKREPLY; + +#ifdef USE_INET6 + /* fill icmp reply type table */ + for (i = 0; i <= ICMP6_MAXTYPE; i++) + icmpreplytype6[i] = -1; + icmpreplytype6[ICMP6_ECHO_REQUEST] = ICMP6_ECHO_REPLY; + icmpreplytype6[ICMP6_MEMBERSHIP_QUERY] = ICMP6_MEMBERSHIP_REPORT; + icmpreplytype6[ICMP6_NI_QUERY] = ICMP6_NI_REPLY; + icmpreplytype6[ND_ROUTER_SOLICIT] = ND_ROUTER_ADVERT; + icmpreplytype6[ND_NEIGHBOR_SOLICIT] = ND_NEIGHBOR_ADVERT; +#endif + + return 0; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_main_unload */ +/* Returns: 0 = success, -1 = failure */ +/* Parameters: none */ +/* */ +/* A null-op function that exists as a placeholder so that the flow in */ +/* other functions is obvious. */ +/* ------------------------------------------------------------------------ */ +int +ipf_main_unload() +{ + return 0; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_load_all */ +/* Returns: 0 = success, -1 = failure */ +/* Parameters: none */ +/* */ +/* Work through all of the subsystems inside IPFilter and call the load */ +/* function for each in an order that won't lead to a crash :) */ +/* ------------------------------------------------------------------------ */ +int +ipf_load_all() +{ + if (ipf_main_load() == -1) + return -1; + + if (ipf_state_main_load() == -1) + return -1; + + if (ipf_nat_main_load() == -1) + return -1; + + if (ipf_frag_main_load() == -1) + return -1; + + if (ipf_auth_main_load() == -1) + return -1; + + if (ipf_proxy_main_load() == -1) + return -1; + + return 0; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_unload_all */ +/* Returns: 0 = success, -1 = failure */ +/* Parameters: none */ +/* */ +/* Work through all of the subsystems inside IPFilter and call the unload */ +/* function for each in an order that won't lead to a crash :) */ +/* ------------------------------------------------------------------------ */ +int +ipf_unload_all() +{ + if (ipf_proxy_main_unload() == -1) + return -1; + + if (ipf_auth_main_unload() == -1) + return -1; + + if (ipf_frag_main_unload() == -1) + return -1; + + if (ipf_nat_main_unload() == -1) + return -1; + + if (ipf_state_main_unload() == -1) + return -1; + + if (ipf_main_unload() == -1) + return -1; + + return 0; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_create_all */ +/* Returns: NULL = failure, else success */ +/* Parameters: arg(I) - pointer to soft context main structure */ +/* */ +/* Work through all of the subsystems inside IPFilter and call the create */ +/* function for each in an order that won't lead to a crash :) */ +/* ------------------------------------------------------------------------ */ +ipf_main_softc_t * +ipf_create_all(arg) + void *arg; +{ + ipf_main_softc_t *softc; + + softc = ipf_main_soft_create(arg); + if (softc == NULL) + return NULL; + +#ifdef IPFILTER_LOG + softc->ipf_log_soft = ipf_log_soft_create(softc); + if (softc->ipf_log_soft == NULL) { + ipf_destroy_all(softc); + return NULL; + } +#endif + + softc->ipf_lookup_soft = ipf_lookup_soft_create(softc); + if (softc->ipf_lookup_soft == NULL) { + ipf_destroy_all(softc); + return NULL; + } + + softc->ipf_sync_soft = ipf_sync_soft_create(softc); + if (softc->ipf_sync_soft == NULL) { + ipf_destroy_all(softc); + return NULL; + } + + softc->ipf_state_soft = ipf_state_soft_create(softc); + if (softc->ipf_state_soft == NULL) { + ipf_destroy_all(softc); + return NULL; + } + + softc->ipf_nat_soft = ipf_nat_soft_create(softc); + if (softc->ipf_nat_soft == NULL) { + ipf_destroy_all(softc); + return NULL; + } + + softc->ipf_frag_soft = ipf_frag_soft_create(softc); + if (softc->ipf_frag_soft == NULL) { + ipf_destroy_all(softc); + return NULL; + } + + softc->ipf_auth_soft = ipf_auth_soft_create(softc); + if (softc->ipf_auth_soft == NULL) { + ipf_destroy_all(softc); + return NULL; + } + + softc->ipf_proxy_soft = ipf_proxy_soft_create(softc); + if (softc->ipf_proxy_soft == NULL) { + ipf_destroy_all(softc); + return NULL; + } + + return softc; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_destroy_all */ +/* Returns: void */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* */ +/* Work through all of the subsystems inside IPFilter and call the destroy */ +/* function for each in an order that won't lead to a crash :) */ +/* */ +/* Every one of these functions is expected to succeed, so there is no */ +/* checking of return values. */ +/* ------------------------------------------------------------------------ */ +void +ipf_destroy_all(softc) + ipf_main_softc_t *softc; +{ + + if (softc->ipf_state_soft != NULL) { + ipf_state_soft_destroy(softc, softc->ipf_state_soft); + softc->ipf_state_soft = NULL; + } + + if (softc->ipf_nat_soft != NULL) { + ipf_nat_soft_destroy(softc, softc->ipf_nat_soft); + softc->ipf_nat_soft = NULL; + } + + if (softc->ipf_frag_soft != NULL) { + ipf_frag_soft_destroy(softc, softc->ipf_frag_soft); + softc->ipf_frag_soft = NULL; + } + + if (softc->ipf_auth_soft != NULL) { + ipf_auth_soft_destroy(softc, softc->ipf_auth_soft); + softc->ipf_auth_soft = NULL; + } + + if (softc->ipf_proxy_soft != NULL) { + ipf_proxy_soft_destroy(softc, softc->ipf_proxy_soft); + softc->ipf_proxy_soft = NULL; + } + + if (softc->ipf_sync_soft != NULL) { + ipf_sync_soft_destroy(softc, softc->ipf_sync_soft); + softc->ipf_sync_soft = NULL; + } + + if (softc->ipf_lookup_soft != NULL) { + ipf_lookup_soft_destroy(softc, softc->ipf_lookup_soft); + softc->ipf_lookup_soft = NULL; + } + +#ifdef IPFILTER_LOG + if (softc->ipf_log_soft != NULL) { + ipf_log_soft_destroy(softc, softc->ipf_log_soft); + softc->ipf_log_soft = NULL; + } +#endif + + ipf_main_soft_destroy(softc); +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_init_all */ +/* Returns: 0 = success, -1 = failure */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* */ +/* Work through all of the subsystems inside IPFilter and call the init */ +/* function for each in an order that won't lead to a crash :) */ +/* ------------------------------------------------------------------------ */ +int +ipf_init_all(softc) + ipf_main_softc_t *softc; +{ + + if (ipf_main_soft_init(softc) == -1) + return -1; + +#ifdef IPFILTER_LOG + if (ipf_log_soft_init(softc, softc->ipf_log_soft) == -1) + return -1; +#endif + + if (ipf_lookup_soft_init(softc, softc->ipf_lookup_soft) == -1) + return -1; + + if (ipf_sync_soft_init(softc, softc->ipf_sync_soft) == -1) + return -1; + + if (ipf_state_soft_init(softc, softc->ipf_state_soft) == -1) + return -1; + + if (ipf_nat_soft_init(softc, softc->ipf_nat_soft) == -1) + return -1; + + if (ipf_frag_soft_init(softc, softc->ipf_frag_soft) == -1) + return -1; + + if (ipf_auth_soft_init(softc, softc->ipf_auth_soft) == -1) + return -1; + + if (ipf_proxy_soft_init(softc, softc->ipf_proxy_soft) == -1) + return -1; + + return 0; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_fini_all */ +/* Returns: 0 = success, -1 = failure */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* */ +/* Work through all of the subsystems inside IPFilter and call the fini */ +/* function for each in an order that won't lead to a crash :) */ +/* ------------------------------------------------------------------------ */ +int +ipf_fini_all(softc) + ipf_main_softc_t *softc; +{ + + ipf_token_flush(softc); + + if (ipf_proxy_soft_fini(softc, softc->ipf_proxy_soft) == -1) + return -1; + + if (ipf_auth_soft_fini(softc, softc->ipf_auth_soft) == -1) + return -1; + + if (ipf_frag_soft_fini(softc, softc->ipf_frag_soft) == -1) + return -1; + + if (ipf_nat_soft_fini(softc, softc->ipf_nat_soft) == -1) + return -1; + + if (ipf_state_soft_fini(softc, softc->ipf_state_soft) == -1) + return -1; + + if (ipf_sync_soft_fini(softc, softc->ipf_sync_soft) == -1) + return -1; + + if (ipf_lookup_soft_fini(softc, softc->ipf_lookup_soft) == -1) + return -1; + +#ifdef IPFILTER_LOG + if (ipf_log_soft_fini(softc, softc->ipf_log_soft) == -1) + return -1; +#endif + + if (ipf_main_soft_fini(softc) == -1) + return -1; + + return 0; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_rule_expire */ +/* Returns: Nil */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* */ +/* At present this function exists just to support temporary addition of */ +/* firewall rules. Both inactive and active lists are scanned for items to */ +/* purge, as by rights, the expiration is computed as soon as the rule is */ +/* loaded in. */ +/* ------------------------------------------------------------------------ */ +void +ipf_rule_expire(softc) + ipf_main_softc_t *softc; +{ + frentry_t *fr; + + if ((softc->ipf_rule_explist[0] == NULL) && + (softc->ipf_rule_explist[1] == NULL)) + return; + + WRITE_ENTER(&softc->ipf_mutex); + + while ((fr = softc->ipf_rule_explist[0]) != NULL) { + /* + * Because the list is kept sorted on insertion, the fist + * one that dies in the future means no more work to do. + */ + if (fr->fr_die > softc->ipf_ticks) + break; + ipf_rule_delete(softc, fr, IPL_LOGIPF, 0); + } + + while ((fr = softc->ipf_rule_explist[1]) != NULL) { + /* + * Because the list is kept sorted on insertion, the fist + * one that dies in the future means no more work to do. + */ + if (fr->fr_die > softc->ipf_ticks) + break; + ipf_rule_delete(softc, fr, IPL_LOGIPF, 1); + } + + RWLOCK_EXIT(&softc->ipf_mutex); +} + + +static int ipf_ht_node_cmp __P((struct host_node_s *, struct host_node_s *)); +static void ipf_ht_node_make_key __P((host_track_t *, host_node_t *, int, + i6addr_t *)); + +host_node_t RBI_ZERO(ipf_rb); +RBI_CODE(ipf_rb, host_node_t, hn_entry, ipf_ht_node_cmp) + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_ht_node_cmp */ +/* Returns: int - 0 == nodes are the same, .. */ +/* Parameters: k1(I) - pointer to first key to compare */ +/* k2(I) - pointer to second key to compare */ +/* */ +/* The "key" for the node is a combination of two fields: the address */ +/* family and the address itself. */ +/* */ +/* Because we're not actually interpreting the address data, it isn't */ +/* necessary to convert them to/from network/host byte order. The mask is */ +/* just used to remove bits that aren't significant - it doesn't matter */ +/* where they are, as long as they're always in the same place. */ +/* */ +/* As with IP6_EQ, comparing IPv6 addresses starts at the bottom because */ +/* this is where individual ones will differ the most - but not true for */ +/* for /48's, etc. */ +/* ------------------------------------------------------------------------ */ +static int +ipf_ht_node_cmp(k1, k2) + struct host_node_s *k1, *k2; +{ + int i; + + i = (k2->hn_addr.adf_family - k1->hn_addr.adf_family); + if (i != 0) + return i; + + if (k1->hn_addr.adf_family == AF_INET) + return (k2->hn_addr.adf_addr.in4.s_addr - + k1->hn_addr.adf_addr.in4.s_addr); + + i = k2->hn_addr.adf_addr.i6[3] - k1->hn_addr.adf_addr.i6[3]; + if (i != 0) + return i; + i = k2->hn_addr.adf_addr.i6[2] - k1->hn_addr.adf_addr.i6[2]; + if (i != 0) + return i; + i = k2->hn_addr.adf_addr.i6[1] - k1->hn_addr.adf_addr.i6[1]; + if (i != 0) + return i; + i = k2->hn_addr.adf_addr.i6[0] - k1->hn_addr.adf_addr.i6[0]; + return i; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_ht_node_make_key */ +/* Returns: Nil */ +/* parameters: htp(I) - pointer to address tracking structure */ +/* key(I) - where to store masked address for lookup */ +/* family(I) - protocol family of address */ +/* addr(I) - pointer to network address */ +/* */ +/* Using the "netmask" (number of bits) stored parent host tracking struct, */ +/* copy the address passed in into the key structure whilst masking out the */ +/* bits that we don't want. */ +/* */ +/* Because the parser will set ht_netmask to 128 if there is no protocol */ +/* specified (the parser doesn't know if it should be a v4 or v6 rule), we */ +/* have to be wary of that and not allow 32-128 to happen. */ +/* ------------------------------------------------------------------------ */ +static void +ipf_ht_node_make_key(htp, key, family, addr) + host_track_t *htp; + host_node_t *key; + int family; + i6addr_t *addr; +{ + key->hn_addr.adf_family = family; + if (family == AF_INET) { + u_32_t mask; + int bits; + + key->hn_addr.adf_len = sizeof(key->hn_addr.adf_addr.in4); + bits = htp->ht_netmask; + if (bits >= 32) { + mask = 0xffffffff; + } else { + mask = htonl(0xffffffff << (32 - bits)); + } + key->hn_addr.adf_addr.in4.s_addr = addr->in4.s_addr & mask; +#ifdef USE_INET6 + } else { + int bits = htp->ht_netmask; + + key->hn_addr.adf_len = sizeof(key->hn_addr.adf_addr.in6); + if (bits > 96) { + key->hn_addr.adf_addr.i6[3] = addr->i6[3] & + htonl(0xffffffff << (128 - bits)); + key->hn_addr.adf_addr.i6[2] = addr->i6[2]; + key->hn_addr.adf_addr.i6[1] = addr->i6[2]; + key->hn_addr.adf_addr.i6[0] = addr->i6[2]; + } else if (bits > 64) { + key->hn_addr.adf_addr.i6[3] = 0; + key->hn_addr.adf_addr.i6[2] = addr->i6[2] & + htonl(0xffffffff << (96 - bits)); + key->hn_addr.adf_addr.i6[1] = addr->i6[1]; + key->hn_addr.adf_addr.i6[0] = addr->i6[0]; + } else if (bits > 32) { + key->hn_addr.adf_addr.i6[3] = 0; + key->hn_addr.adf_addr.i6[2] = 0; + key->hn_addr.adf_addr.i6[1] = addr->i6[1] & + htonl(0xffffffff << (64 - bits)); + key->hn_addr.adf_addr.i6[0] = addr->i6[0]; + } else { + key->hn_addr.adf_addr.i6[3] = 0; + key->hn_addr.adf_addr.i6[2] = 0; + key->hn_addr.adf_addr.i6[1] = 0; + key->hn_addr.adf_addr.i6[0] = addr->i6[0] & + htonl(0xffffffff << (32 - bits)); + } +#endif + } +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_ht_node_add */ +/* Returns: int - 0 == success, -1 == failure */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* htp(I) - pointer to address tracking structure */ +/* family(I) - protocol family of address */ +/* addr(I) - pointer to network address */ +/* */ +/* NOTE: THIS FUNCTION MUST BE CALLED WITH AN EXCLUSIVE LOCK THAT PREVENTS */ +/* ipf_ht_node_del FROM RUNNING CONCURRENTLY ON THE SAME htp. */ +/* */ +/* After preparing the key with the address information to find, look in */ +/* the red-black tree to see if the address is known. A successful call to */ +/* this function can mean one of two things: a new node was added to the */ +/* tree or a matching node exists and we're able to bump up its activity. */ +/* ------------------------------------------------------------------------ */ +int +ipf_ht_node_add(softc, htp, family, addr) + ipf_main_softc_t *softc; + host_track_t *htp; + int family; + i6addr_t *addr; +{ + host_node_t *h; + host_node_t k; + + ipf_ht_node_make_key(htp, &k, family, addr); + + h = RBI_SEARCH(ipf_rb, &htp->ht_root, &k); + if (h == NULL) { + if (htp->ht_cur_nodes >= htp->ht_max_nodes) + return -1; + KMALLOC(h, host_node_t *); + if (h == NULL) { + DT(ipf_rb_no_mem); + LBUMP(ipf_rb_no_mem); + return -1; + } + + /* + * If there was a macro to initialise the RB node then that + * would get used here, but there isn't... + */ + bzero((char *)h, sizeof(*h)); + h->hn_addr = k.hn_addr; + h->hn_addr.adf_family = k.hn_addr.adf_family; + RBI_INSERT(ipf_rb, &htp->ht_root, h); + htp->ht_cur_nodes++; + } else { + if ((htp->ht_max_per_node != 0) && + (h->hn_active >= htp->ht_max_per_node)) { + DT(ipf_rb_node_max); + LBUMP(ipf_rb_node_max); + return -1; + } + } + + h->hn_active++; + + return 0; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_ht_node_del */ +/* Returns: int - 0 == success, -1 == failure */ +/* parameters: htp(I) - pointer to address tracking structure */ +/* family(I) - protocol family of address */ +/* addr(I) - pointer to network address */ +/* */ +/* NOTE: THIS FUNCTION MUST BE CALLED WITH AN EXCLUSIVE LOCK THAT PREVENTS */ +/* ipf_ht_node_add FROM RUNNING CONCURRENTLY ON THE SAME htp. */ +/* */ +/* Try and find the address passed in amongst the leavese on this tree to */ +/* be friend. If found then drop the active account for that node drops by */ +/* one. If that count reaches 0, it is time to free it all up. */ +/* ------------------------------------------------------------------------ */ +int +ipf_ht_node_del(htp, family, addr) + host_track_t *htp; + int family; + i6addr_t *addr; +{ + host_node_t *h; + host_node_t k; + + ipf_ht_node_make_key(htp, &k, family, addr); + + h = RBI_SEARCH(ipf_rb, &htp->ht_root, &k); + if (h == NULL) { + return -1; + } else { + h->hn_active--; + if (h->hn_active == 0) { + (void) RBI_DELETE(ipf_rb, &htp->ht_root, h); + htp->ht_cur_nodes--; + KFREE(h); + } + } + + return 0; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_rb_ht_init */ +/* Returns: Nil */ +/* Parameters: head(I) - pointer to host tracking structure */ +/* */ +/* Initialise the host tracking structure to be ready for use above. */ +/* ------------------------------------------------------------------------ */ +void +ipf_rb_ht_init(head) + host_track_t *head; +{ + RBI_INIT(ipf_rb, &head->ht_root); +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_rb_ht_freenode */ +/* Returns: Nil */ +/* Parameters: head(I) - pointer to host tracking structure */ +/* arg(I) - additional argument from walk caller */ +/* */ +/* Free an actual host_node_t structure. */ +/* ------------------------------------------------------------------------ */ +void +ipf_rb_ht_freenode(node, arg) + host_node_t *node; + void *arg; +{ + KFREE(node); +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_rb_ht_flush */ +/* Returns: Nil */ +/* Parameters: head(I) - pointer to host tracking structure */ +/* */ +/* Remove all of the nodes in the tree tracking hosts by calling a walker */ +/* and free'ing each one. */ +/* ------------------------------------------------------------------------ */ +void +ipf_rb_ht_flush(head) + host_track_t *head; +{ + RBI_WALK(ipf_rb, &head->ht_root, ipf_rb_ht_freenode, NULL); +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_slowtimer */ +/* Returns: Nil */ +/* Parameters: ptr(I) - pointer to main ipf soft context structure */ +/* */ +/* Slowly expire held state for fragments. Timeouts are set * in */ +/* expectation of this being called twice per second. */ +/* ------------------------------------------------------------------------ */ +void +ipf_slowtimer(softc) + ipf_main_softc_t *softc; +{ + + ipf_token_expire(softc); + ipf_frag_expire(softc); + ipf_state_expire(softc); + ipf_nat_expire(softc); + ipf_auth_expire(softc); + ipf_lookup_expire(softc); + ipf_rule_expire(softc); + ipf_sync_expire(softc); + softc->ipf_ticks++; +# if defined(__OpenBSD__) + timeout_add(&ipf_slowtimer_ch, hz/2); +# endif +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_inet_mask_add */ +/* Returns: Nil */ +/* Parameters: bits(I) - pointer to nat context information */ +/* mtab(I) - pointer to mask hash table structure */ +/* */ +/* When called, bits represents the mask of a new NAT rule that has just */ +/* been added. This function inserts a bitmask into the array of masks to */ +/* search when searching for a matching NAT rule for a packet. */ +/* Prevention of duplicate masks is achieved by checking the use count for */ +/* a given netmask. */ +/* ------------------------------------------------------------------------ */ +void +ipf_inet_mask_add(bits, mtab) + int bits; + ipf_v4_masktab_t *mtab; +{ + u_32_t mask; + int i, j; + + mtab->imt4_masks[bits]++; + if (mtab->imt4_masks[bits] > 1) + return; + + if (bits == 0) + mask = 0; + else + mask = 0xffffffff << (32 - bits); + + for (i = 0; i < 33; i++) { + if (ntohl(mtab->imt4_active[i]) < mask) { + for (j = 32; j > i; j--) + mtab->imt4_active[j] = mtab->imt4_active[j - 1]; + mtab->imt4_active[i] = htonl(mask); + break; + } + } + mtab->imt4_max++; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_inet_mask_del */ +/* Returns: Nil */ +/* Parameters: bits(I) - number of bits set in the netmask */ +/* mtab(I) - pointer to mask hash table structure */ +/* */ +/* Remove the 32bit bitmask represented by "bits" from the collection of */ +/* netmasks stored inside of mtab. */ +/* ------------------------------------------------------------------------ */ +void +ipf_inet_mask_del(bits, mtab) + int bits; + ipf_v4_masktab_t *mtab; +{ + u_32_t mask; + int i, j; + + mtab->imt4_masks[bits]--; + if (mtab->imt4_masks[bits] > 0) + return; + + mask = htonl(0xffffffff << (32 - bits)); + for (i = 0; i < 33; i++) { + if (mtab->imt4_active[i] == mask) { + for (j = i + 1; j < 33; j++) + mtab->imt4_active[j - 1] = mtab->imt4_active[j]; + break; + } + } + mtab->imt4_max--; + ASSERT(mtab->imt4_max >= 0); +} + + +#ifdef USE_INET6 +/* ------------------------------------------------------------------------ */ +/* Function: ipf_inet6_mask_add */ +/* Returns: Nil */ +/* Parameters: bits(I) - number of bits set in mask */ +/* mask(I) - pointer to mask to add */ +/* mtab(I) - pointer to mask hash table structure */ +/* */ +/* When called, bitcount represents the mask of a IPv6 NAT map rule that */ +/* has just been added. This function inserts a bitmask into the array of */ +/* masks to search when searching for a matching NAT rule for a packet. */ +/* Prevention of duplicate masks is achieved by checking the use count for */ +/* a given netmask. */ +/* ------------------------------------------------------------------------ */ +void +ipf_inet6_mask_add(bits, mask, mtab) + int bits; + i6addr_t *mask; + ipf_v6_masktab_t *mtab; +{ + i6addr_t zero; + int i, j; + + mtab->imt6_masks[bits]++; + if (mtab->imt6_masks[bits] > 1) + return; + + if (bits == 0) { + mask = &zero; + zero.i6[0] = 0; + zero.i6[1] = 0; + zero.i6[2] = 0; + zero.i6[3] = 0; + } + + for (i = 0; i < 129; i++) { + if (IP6_LT(&mtab->imt6_active[i], mask)) { + for (j = 128; j > i; j--) + mtab->imt6_active[j] = mtab->imt6_active[j - 1]; + mtab->imt6_active[i] = *mask; + break; + } + } + mtab->imt6_max++; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_inet6_mask_del */ +/* Returns: Nil */ +/* Parameters: bits(I) - number of bits set in mask */ +/* mask(I) - pointer to mask to remove */ +/* mtab(I) - pointer to mask hash table structure */ +/* */ +/* Remove the 128bit bitmask represented by "bits" from the collection of */ +/* netmasks stored inside of mtab. */ +/* ------------------------------------------------------------------------ */ +void +ipf_inet6_mask_del(bits, mask, mtab) + int bits; + i6addr_t *mask; + ipf_v6_masktab_t *mtab; +{ + i6addr_t zero; + int i, j; + + mtab->imt6_masks[bits]--; + if (mtab->imt6_masks[bits] > 0) + return; + + if (bits == 0) + mask = &zero; + zero.i6[0] = 0; + zero.i6[1] = 0; + zero.i6[2] = 0; + zero.i6[3] = 0; + + for (i = 0; i < 129; i++) { + if (IP6_EQ(&mtab->imt6_active[i], mask)) { + for (j = i + 1; j < 129; j++) { + mtab->imt6_active[j - 1] = mtab->imt6_active[j]; + if (IP6_EQ(&mtab->imt6_active[j - 1], &zero)) + break; + } + break; + } + } + mtab->imt6_max--; + ASSERT(mtab->imt6_max >= 0); +} +#endif diff --git a/sys/contrib/ipfilter/netinet/ip_auth.c b/sys/contrib/ipfilter/netinet/ip_auth.c index fcd891f7f94..5a2ebeca5da 100644 --- a/sys/contrib/ipfilter/netinet/ip_auth.c +++ b/sys/contrib/ipfilter/netinet/ip_auth.c @@ -1,7 +1,7 @@ /* $FreeBSD$ */ /* - * Copyright (C) 1998-2003 by Darren Reed & Guido van Rooij. + * Copyright (C) 2012 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. */ @@ -19,6 +19,9 @@ #if !defined(_KERNEL) # include # include +# ifdef _STDC_C99 +# include +# endif # include # define _KERNEL # ifdef __OpenBSD__ @@ -52,7 +55,7 @@ struct file; # include # include #endif -#if (defined(_BSDI_VERSION) && _BSDI_VERSION >= 199802) || \ +#if (defined(_BSDI_VERSION) && (_BSDI_VERSION >= 199802)) || \ (defined(__FreeBSD_version) &&(__FreeBSD_version >= 400000)) # include #endif @@ -62,11 +65,14 @@ struct file; #if defined(_KERNEL) && defined(__NetBSD__) && (__NetBSD_Version__ >= 104000000) # include #endif +#if defined(__NetBSD_Version__) && (__NetBSD_Version__ >= 400000) && \ + !defined(_KERNEL) +# include +#endif #include #ifdef sun # include #endif -#include #include #include #include @@ -125,71 +131,249 @@ static const char rcsid[] = "@(#)$FreeBSD$"; #endif + +typedef struct ipf_auth_softc_s { #if SOLARIS && defined(_KERNEL) -extern kcondvar_t ipfauthwait; -extern struct pollhead iplpollhead[IPL_LOGSIZE]; + kcondvar_t ipf_auth_wait; #endif /* SOLARIS */ #if defined(linux) && defined(_KERNEL) -wait_queue_head_t fr_authnext_linux; + wait_queue_head_t ipf_auth_next_linux; #endif + ipfrwlock_t ipf_authlk; + ipfmutex_t ipf_auth_mx; + int ipf_auth_size; + int ipf_auth_used; + int ipf_auth_replies; + int ipf_auth_defaultage; + int ipf_auth_lock; + ipf_authstat_t ipf_auth_stats; + frauth_t *ipf_auth; + mb_t **ipf_auth_pkts; + int ipf_auth_start; + int ipf_auth_end; + int ipf_auth_next; + frauthent_t *ipf_auth_entries; + frentry_t *ipf_auth_ip; + frentry_t *ipf_auth_rules; +} ipf_auth_softc_t; -int fr_authsize = FR_NUMAUTH; -int fr_authused = 0; -int fr_defaultauthage = 600; -int fr_auth_lock = 0; -int fr_auth_init = 0; -fr_authstat_t fr_authstats; -static frauth_t *fr_auth = NULL; -mb_t **fr_authpkts = NULL; -int fr_authstart = 0, fr_authend = 0, fr_authnext = 0; -frauthent_t *fae_list = NULL; -frentry_t *ipauth = NULL, - *fr_authlist = NULL; -void fr_authderef __P((frauthent_t **)); -int fr_authgeniter __P((ipftoken_t *, ipfgeniter_t *)); -int fr_authreply __P((char *)); -int fr_authwait __P((char *)); +static void ipf_auth_deref __P((frauthent_t **)); +static void ipf_auth_deref_unlocked __P((ipf_auth_softc_t *, frauthent_t **)); +static int ipf_auth_geniter __P((ipf_main_softc_t *, ipftoken_t *, + ipfgeniter_t *, ipfobj_t *)); +static int ipf_auth_reply __P((ipf_main_softc_t *, ipf_auth_softc_t *, char *)); +static int ipf_auth_wait __P((ipf_main_softc_t *, ipf_auth_softc_t *, char *)); +static int ipf_auth_flush __P((void *)); + /* ------------------------------------------------------------------------ */ -/* Function: fr_authinit */ +/* Function: ipf_auth_main_load */ /* Returns: int - 0 == success, else error */ /* Parameters: None */ /* */ +/* A null-op function that exists as a placeholder so that the flow in */ +/* other functions is obvious. */ +/* ------------------------------------------------------------------------ */ +int +ipf_auth_main_load() +{ + return 0; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_auth_main_unload */ +/* Returns: int - 0 == success, else error */ +/* Parameters: None */ +/* */ +/* A null-op function that exists as a placeholder so that the flow in */ +/* other functions is obvious. */ +/* ------------------------------------------------------------------------ */ +int +ipf_auth_main_unload() +{ + return 0; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_auth_soft_create */ +/* Returns: int - NULL = failure, else success */ +/* Parameters: softc(I) - pointer to soft context data */ +/* */ +/* Create a structre to store all of the run-time data for packet auth in */ +/* and initialise some fields to their defaults. */ +/* ------------------------------------------------------------------------ */ +void * +ipf_auth_soft_create(softc) + ipf_main_softc_t *softc; +{ + ipf_auth_softc_t *softa; + + KMALLOC(softa, ipf_auth_softc_t *); + if (softa == NULL) + return NULL; + + bzero((char *)softa, sizeof(*softa)); + + softa->ipf_auth_size = FR_NUMAUTH; + softa->ipf_auth_defaultage = 600; + + RWLOCK_INIT(&softa->ipf_authlk, "ipf IP User-Auth rwlock"); + MUTEX_INIT(&softa->ipf_auth_mx, "ipf auth log mutex"); +#if SOLARIS && defined(_KERNEL) + cv_init(&softa->ipf_auth_wait, "ipf auth condvar", CV_DRIVER, NULL); +#endif + + return softa; +} + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_auth_soft_init */ +/* Returns: int - 0 == success, else error */ +/* Parameters: softc(I) - pointer to soft context data */ +/* arg(I) - opaque pointer to auth context data */ +/* */ /* Allocate memory and initialise data structures used in handling auth */ /* rules. */ /* ------------------------------------------------------------------------ */ -int fr_authinit() +int +ipf_auth_soft_init(softc, arg) + ipf_main_softc_t *softc; + void *arg; { - KMALLOCS(fr_auth, frauth_t *, fr_authsize * sizeof(*fr_auth)); - if (fr_auth != NULL) - bzero((char *)fr_auth, fr_authsize * sizeof(*fr_auth)); - else + ipf_auth_softc_t *softa = arg; + + KMALLOCS(softa->ipf_auth, frauth_t *, + softa->ipf_auth_size * sizeof(*softa->ipf_auth)); + if (softa->ipf_auth == NULL) return -1; + bzero((char *)softa->ipf_auth, + softa->ipf_auth_size * sizeof(*softa->ipf_auth)); - KMALLOCS(fr_authpkts, mb_t **, fr_authsize * sizeof(*fr_authpkts)); - if (fr_authpkts != NULL) - bzero((char *)fr_authpkts, fr_authsize * sizeof(*fr_authpkts)); - else + KMALLOCS(softa->ipf_auth_pkts, mb_t **, + softa->ipf_auth_size * sizeof(*softa->ipf_auth_pkts)); + if (softa->ipf_auth_pkts == NULL) return -2; + bzero((char *)softa->ipf_auth_pkts, + softa->ipf_auth_size * sizeof(*softa->ipf_auth_pkts)); - MUTEX_INIT(&ipf_authmx, "ipf auth log mutex"); - RWLOCK_INIT(&ipf_auth, "ipf IP User-Auth rwlock"); -#if SOLARIS && defined(_KERNEL) - cv_init(&ipfauthwait, "ipf auth condvar", CV_DRIVER, NULL); -#endif #if defined(linux) && defined(_KERNEL) - init_waitqueue_head(&fr_authnext_linux); + init_waitqueue_head(&softa->ipf_auth_next_linux); #endif - fr_auth_init = 1; - return 0; } /* ------------------------------------------------------------------------ */ -/* Function: fr_checkauth */ +/* Function: ipf_auth_soft_fini */ +/* Returns: int - 0 == success, else error */ +/* Parameters: softc(I) - pointer to soft context data */ +/* arg(I) - opaque pointer to auth context data */ +/* */ +/* Free all network buffer memory used to keep saved packets that have been */ +/* connectedd to the soft soft context structure *but* do not free that: it */ +/* is free'd by _destroy(). */ +/* ------------------------------------------------------------------------ */ +int +ipf_auth_soft_fini(softc, arg) + ipf_main_softc_t *softc; + void *arg; +{ + ipf_auth_softc_t *softa = arg; + frauthent_t *fae, **faep; + frentry_t *fr, **frp; + mb_t *m; + int i; + + if (softa->ipf_auth != NULL) { + KFREES(softa->ipf_auth, + softa->ipf_auth_size * sizeof(*softa->ipf_auth)); + softa->ipf_auth = NULL; + } + + if (softa->ipf_auth_pkts != NULL) { + for (i = 0; i < softa->ipf_auth_size; i++) { + m = softa->ipf_auth_pkts[i]; + if (m != NULL) { + FREE_MB_T(m); + softa->ipf_auth_pkts[i] = NULL; + } + } + KFREES(softa->ipf_auth_pkts, + softa->ipf_auth_size * sizeof(*softa->ipf_auth_pkts)); + softa->ipf_auth_pkts = NULL; + } + + faep = &softa->ipf_auth_entries; + while ((fae = *faep) != NULL) { + *faep = fae->fae_next; + KFREE(fae); + } + softa->ipf_auth_ip = NULL; + + if (softa->ipf_auth_rules != NULL) { + for (frp = &softa->ipf_auth_rules; ((fr = *frp) != NULL); ) { + if (fr->fr_ref == 1) { + *frp = fr->fr_next; + MUTEX_DESTROY(&fr->fr_lock); + KFREE(fr); + } else + frp = &fr->fr_next; + } + } + + return 0; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_auth_soft_destroy */ +/* Returns: void */ +/* Parameters: softc(I) - pointer to soft context data */ +/* arg(I) - opaque pointer to auth context data */ +/* */ +/* Undo what was done in _create() - i.e. free the soft context data. */ +/* ------------------------------------------------------------------------ */ +void +ipf_auth_soft_destroy(softc, arg) + ipf_main_softc_t *softc; + void *arg; +{ + ipf_auth_softc_t *softa = arg; + +# if SOLARIS && defined(_KERNEL) + cv_destroy(&softa->ipf_auth_wait); +# endif + MUTEX_DESTROY(&softa->ipf_auth_mx); + RW_DESTROY(&softa->ipf_authlk); + + KFREE(softa); +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_auth_setlock */ +/* Returns: void */ +/* Paramters: arg(I) - pointer to soft context data */ +/* tmp(I) - value to assign to auth lock */ +/* */ +/* ------------------------------------------------------------------------ */ +void +ipf_auth_setlock(arg, tmp) + void *arg; + int tmp; +{ + ipf_auth_softc_t *softa = arg; + + softa->ipf_auth_lock = tmp; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_auth_check */ /* Returns: frentry_t* - pointer to ipf rule if match found, else NULL */ /* Parameters: fin(I) - pointer to ipftoken structure */ /* passp(I) - pointer to ipfgeniter structure */ @@ -198,10 +382,13 @@ int fr_authinit() /* authorization result and that would result in a feedback loop (i.e. it */ /* will end up returning FR_AUTH) then return FR_BLOCK instead. */ /* ------------------------------------------------------------------------ */ -frentry_t *fr_checkauth(fin, passp) -fr_info_t *fin; -u_32_t *passp; +frentry_t * +ipf_auth_check(fin, passp) + fr_info_t *fin; + u_32_t *passp; { + ipf_main_softc_t *softc = fin->fin_main_soft; + ipf_auth_softc_t *softa = softc->ipf_auth_soft; frentry_t *fr; frauth_t *fra; u_32_t pass; @@ -209,27 +396,29 @@ u_32_t *passp; ip_t *ip; int i; - if (fr_auth_lock || !fr_authused) + if (softa->ipf_auth_lock || !softa->ipf_auth_used) return NULL; ip = fin->fin_ip; id = ip->ip_id; - READ_ENTER(&ipf_auth); - for (i = fr_authstart; i != fr_authend; ) { + READ_ENTER(&softa->ipf_authlk); + for (i = softa->ipf_auth_start; i != softa->ipf_auth_end; ) { /* * index becomes -2 only after an SIOCAUTHW. Check this in * case the same packet gets sent again and it hasn't yet been * auth'd. */ - fra = fr_auth + i; + fra = softa->ipf_auth + i; if ((fra->fra_index == -2) && (id == fra->fra_info.fin_id) && !bcmp((char *)fin, (char *)&fra->fra_info, FI_CSIZE)) { /* * Avoid feedback loop. */ - if (!(pass = fra->fra_pass) || (FR_ISAUTH(pass))) + if (!(pass = fra->fra_pass) || (FR_ISAUTH(pass))) { pass = FR_BLOCK; + fin->fin_reason = FRB_AUTHFEEDBACK; + } /* * Create a dummy rule for the stateful checking to * use and return. Zero out any values we don't @@ -249,60 +438,65 @@ u_32_t *passp; fr->fr_ifas[1] = NULL; fr->fr_ifas[2] = NULL; fr->fr_ifas[3] = NULL; + MUTEX_INIT(&fr->fr_lock, + "ipf auth rule"); } } else fr = fra->fra_info.fin_fr; fin->fin_fr = fr; - RWLOCK_EXIT(&ipf_auth); + fin->fin_flx |= fra->fra_flx; + RWLOCK_EXIT(&softa->ipf_authlk); - WRITE_ENTER(&ipf_auth); + WRITE_ENTER(&softa->ipf_authlk); /* - * fr_authlist is populated with the rules malloc'd + * ipf_auth_rules is populated with the rules malloc'd * above and only those. */ if ((fr != NULL) && (fr != fra->fra_info.fin_fr)) { - fr->fr_next = fr_authlist; - fr_authlist = fr; + fr->fr_next = softa->ipf_auth_rules; + softa->ipf_auth_rules = fr; } - fr_authstats.fas_hits++; + softa->ipf_auth_stats.fas_hits++; fra->fra_index = -1; - fr_authused--; - if (i == fr_authstart) { + softa->ipf_auth_used--; + softa->ipf_auth_replies--; + if (i == softa->ipf_auth_start) { while (fra->fra_index == -1) { i++; fra++; - if (i == fr_authsize) { + if (i == softa->ipf_auth_size) { i = 0; - fra = fr_auth; + fra = softa->ipf_auth; } - fr_authstart = i; - if (i == fr_authend) + softa->ipf_auth_start = i; + if (i == softa->ipf_auth_end) break; } - if (fr_authstart == fr_authend) { - fr_authnext = 0; - fr_authstart = fr_authend = 0; + if (softa->ipf_auth_start == + softa->ipf_auth_end) { + softa->ipf_auth_next = 0; + softa->ipf_auth_start = 0; + softa->ipf_auth_end = 0; } } - RWLOCK_EXIT(&ipf_auth); + RWLOCK_EXIT(&softa->ipf_authlk); if (passp != NULL) *passp = pass; - ATOMIC_INC64(fr_authstats.fas_hits); + softa->ipf_auth_stats.fas_hits++; return fr; } i++; - if (i == fr_authsize) + if (i == softa->ipf_auth_size) i = 0; } - fr_authstats.fas_miss++; - RWLOCK_EXIT(&ipf_auth); - ATOMIC_INC64(fr_authstats.fas_miss); + RWLOCK_EXIT(&softa->ipf_authlk); + softa->ipf_auth_stats.fas_miss++; return NULL; } /* ------------------------------------------------------------------------ */ -/* Function: fr_newauth */ +/* Function: ipf_auth_new */ /* Returns: int - 1 == success, 0 = did not put packet on auth queue */ /* Parameters: m(I) - pointer to mb_t with packet in it */ /* fin(I) - pointer to packet information */ @@ -311,10 +505,13 @@ u_32_t *passp; /* packet. If we do, store it and wake up any user programs which are */ /* waiting to hear about these events. */ /* ------------------------------------------------------------------------ */ -int fr_newauth(m, fin) -mb_t *m; -fr_info_t *fin; +int +ipf_auth_new(m, fin) + mb_t *m; + fr_info_t *fin; { + ipf_main_softc_t *softc = fin->fin_main_soft; + ipf_auth_softc_t *softa = softc->ipf_auth_soft; #if defined(_KERNEL) && defined(MENTAT) qpktinfo_t *qpi = fin->fin_qpi; #endif @@ -324,31 +521,33 @@ fr_info_t *fin; #endif int i; - if (fr_auth_lock) + if (softa->ipf_auth_lock) return 0; - WRITE_ENTER(&ipf_auth); - if (((fr_authend + 1) % fr_authsize) == fr_authstart) { - fr_authstats.fas_nospace++; - RWLOCK_EXIT(&ipf_auth); + WRITE_ENTER(&softa->ipf_authlk); + if (((softa->ipf_auth_end + 1) % softa->ipf_auth_size) == + softa->ipf_auth_start) { + softa->ipf_auth_stats.fas_nospace++; + RWLOCK_EXIT(&softa->ipf_authlk); return 0; } - fr_authstats.fas_added++; - fr_authused++; - i = fr_authend++; - if (fr_authend == fr_authsize) - fr_authend = 0; - fra = fr_auth + i; - fra->fra_index = i; - RWLOCK_EXIT(&ipf_auth); + softa->ipf_auth_stats.fas_added++; + softa->ipf_auth_used++; + i = softa->ipf_auth_end++; + if (softa->ipf_auth_end == softa->ipf_auth_size) + softa->ipf_auth_end = 0; + fra = softa->ipf_auth + i; + fra->fra_index = i; if (fin->fin_fr != NULL) fra->fra_pass = fin->fin_fr->fr_flags; else fra->fra_pass = 0; - fra->fra_age = fr_defaultauthage; + fra->fra_age = softa->ipf_auth_defaultage; bcopy((char *)fin, (char *)&fra->fra_info, sizeof(*fin)); + fra->fra_flx = fra->fra_info.fin_flx & (FI_STATE|FI_NATED); + fra->fra_info.fin_flx &= ~(FI_STATE|FI_NATED); #if !defined(sparc) && !defined(m68k) /* * No need to copyback here as we want to undo the changes, not keep @@ -370,24 +569,24 @@ fr_info_t *fin; #if SOLARIS && defined(_KERNEL) COPYIFNAME(fin->fin_v, fin->fin_ifp, fra->fra_info.fin_ifname); m->b_rptr -= qpi->qpi_off; - fr_authpkts[i] = *(mblk_t **)fin->fin_mp; -# if !defined(_INET_IP_STACK_H) fra->fra_q = qpi->qpi_q; /* The queue can disappear! */ -# endif fra->fra_m = *fin->fin_mp; fra->fra_info.fin_mp = &fra->fra_m; - cv_signal(&ipfauthwait); - pollwakeup(&iplpollhead[IPL_LOGAUTH], POLLIN|POLLRDNORM); + softa->ipf_auth_pkts[i] = *(mblk_t **)fin->fin_mp; + RWLOCK_EXIT(&softa->ipf_authlk); + cv_signal(&softa->ipf_auth_wait); + pollwakeup(&softc->ipf_poll_head[IPL_LOGAUTH], POLLIN|POLLRDNORM); #else - fr_authpkts[i] = m; - WAKEUP(&fr_authnext,0); + softa->ipf_auth_pkts[i] = m; + RWLOCK_EXIT(&softa->ipf_authlk); + WAKEUP(&softa->ipf_auth_next, 0); #endif return 1; } /* ------------------------------------------------------------------------ */ -/* Function: fr_auth_ioctl */ +/* Function: ipf_auth_ioctl */ /* Returns: int - 0 == success, else error */ /* Parameters: data(IO) - pointer to ioctl data */ /* cmd(I) - ioctl command */ @@ -396,14 +595,17 @@ fr_info_t *fin; /* ctx(I) - pointer for context */ /* */ /* This function handles all of the ioctls recognised by the auth component */ -/* in IPFilter - ie ioctls called on an open fd for /dev/ipauth */ +/* in IPFilter - ie ioctls called on an open fd for /dev/ipf_auth */ /* ------------------------------------------------------------------------ */ -int fr_auth_ioctl(data, cmd, mode, uid, ctx) -caddr_t data; -ioctlcmd_t cmd; -int mode, uid; -void *ctx; +int +ipf_auth_ioctl(softc, data, cmd, mode, uid, ctx) + ipf_main_softc_t *softc; + caddr_t data; + ioctlcmd_t cmd; + int mode, uid; + void *ctx; { + ipf_auth_softc_t *softa = softc->ipf_auth_soft; int error = 0, i; SPL_INT(s); @@ -413,18 +615,23 @@ void *ctx; { ipftoken_t *token; ipfgeniter_t iter; + ipfobj_t obj; - error = fr_inobj(data, &iter, IPFOBJ_GENITER); + error = ipf_inobj(softc, data, &obj, &iter, IPFOBJ_GENITER); if (error != 0) break; SPL_SCHED(s); - token = ipf_findtoken(IPFGENITER_AUTH, uid, ctx); + token = ipf_token_find(softc, IPFGENITER_AUTH, uid, ctx); if (token != NULL) - error = fr_authgeniter(token, &iter); - else + error = ipf_auth_geniter(softc, token, &iter, &obj); + else { + WRITE_ENTER(&softc->ipf_tokens); + ipf_token_deref(softc, token); + RWLOCK_EXIT(&softc->ipf_tokens); + IPFERROR(10001); error = ESRCH; - RWLOCK_EXIT(&ipf_tokens); + } SPL_X(s); break; @@ -432,46 +639,52 @@ void *ctx; case SIOCADAFR : case SIOCRMAFR : - if (!(mode & FWRITE)) + if (!(mode & FWRITE)) { + IPFERROR(10002); error = EPERM; - else - error = frrequest(IPL_LOGAUTH, cmd, data, - fr_active, 1); + } else + error = frrequest(softc, IPL_LOGAUTH, cmd, data, + softc->ipf_active, 1); break; case SIOCSTLCK : if (!(mode & FWRITE)) { + IPFERROR(10003); error = EPERM; - break; + } else { + error = ipf_lock(data, &softa->ipf_auth_lock); } - error = fr_lock(data, &fr_auth_lock); break; case SIOCATHST: - fr_authstats.fas_faelist = fae_list; - error = fr_outobj(data, &fr_authstats, IPFOBJ_AUTHSTAT); + softa->ipf_auth_stats.fas_faelist = softa->ipf_auth_entries; + error = ipf_outobj(softc, data, &softa->ipf_auth_stats, + IPFOBJ_AUTHSTAT); break; case SIOCIPFFL: SPL_NET(s); - WRITE_ENTER(&ipf_auth); - i = fr_authflush(); - RWLOCK_EXIT(&ipf_auth); + WRITE_ENTER(&softa->ipf_authlk); + i = ipf_auth_flush(softa); + RWLOCK_EXIT(&softa->ipf_authlk); SPL_X(s); - error = BCOPYOUT((char *)&i, data, sizeof(i)); - if (error != 0) + error = BCOPYOUT(&i, data, sizeof(i)); + if (error != 0) { + IPFERROR(10004); error = EFAULT; + } break; case SIOCAUTHW: - error = fr_authwait(data); + error = ipf_auth_wait(softc, softa, data); break; case SIOCAUTHR: - error = fr_authreply(data); + error = ipf_auth_reply(softc, softa, data); break; default : + IPFERROR(10005); error = EINVAL; break; } @@ -480,75 +693,18 @@ void *ctx; /* ------------------------------------------------------------------------ */ -/* Function: fr_authunload */ -/* Returns: None */ -/* Parameters: None */ -/* */ -/* Free all network buffer memory used to keep saved packets. */ -/* ------------------------------------------------------------------------ */ -void fr_authunload() -{ - register int i; - register frauthent_t *fae, **faep; - frentry_t *fr, **frp; - mb_t *m; - - if (fr_auth != NULL) { - KFREES(fr_auth, fr_authsize * sizeof(*fr_auth)); - fr_auth = NULL; - } - - if (fr_authpkts != NULL) { - for (i = 0; i < fr_authsize; i++) { - m = fr_authpkts[i]; - if (m != NULL) { - FREE_MB_T(m); - fr_authpkts[i] = NULL; - } - } - KFREES(fr_authpkts, fr_authsize * sizeof(*fr_authpkts)); - fr_authpkts = NULL; - } - - faep = &fae_list; - while ((fae = *faep) != NULL) { - *faep = fae->fae_next; - KFREE(fae); - } - ipauth = NULL; - - if (fr_authlist != NULL) { - for (frp = &fr_authlist; ((fr = *frp) != NULL); ) { - if (fr->fr_ref == 1) { - *frp = fr->fr_next; - KFREE(fr); - } else - frp = &fr->fr_next; - } - } - - if (fr_auth_init == 1) { -# if SOLARIS && defined(_KERNEL) - cv_destroy(&ipfauthwait); -# endif - MUTEX_DESTROY(&ipf_authmx); - RW_DESTROY(&ipf_auth); - - fr_auth_init = 0; - } -} - - -/* ------------------------------------------------------------------------ */ -/* Function: fr_authexpire */ +/* Function: ipf_auth_expire */ /* Returns: None */ /* Parameters: None */ /* */ /* Slowly expire held auth records. Timeouts are set in expectation of */ /* this being called twice per second. */ /* ------------------------------------------------------------------------ */ -void fr_authexpire() +void +ipf_auth_expire(softc) + ipf_main_softc_t *softc; { + ipf_auth_softc_t *softa = softc->ipf_auth_soft; frauthent_t *fae, **faep; frentry_t *fr, **frp; frauth_t *fra; @@ -556,70 +712,81 @@ void fr_authexpire() int i; SPL_INT(s); - if (fr_auth_lock) + if (softa->ipf_auth_lock) return; - SPL_NET(s); - WRITE_ENTER(&ipf_auth); - for (i = 0, fra = fr_auth; i < fr_authsize; i++, fra++) { + WRITE_ENTER(&softa->ipf_authlk); + for (i = 0, fra = softa->ipf_auth; i < softa->ipf_auth_size; + i++, fra++) { fra->fra_age--; - if ((fra->fra_age == 0) && (m = fr_authpkts[i])) { - FREE_MB_T(m); - fr_authpkts[i] = NULL; - fr_auth[i].fra_index = -1; - fr_authstats.fas_expire++; - fr_authused--; + if ((fra->fra_age == 0) && + (softa->ipf_auth[i].fra_index != -1)) { + if ((m = softa->ipf_auth_pkts[i]) != NULL) { + FREE_MB_T(m); + softa->ipf_auth_pkts[i] = NULL; + } else if (softa->ipf_auth[i].fra_index == -2) { + softa->ipf_auth_replies--; + } + softa->ipf_auth[i].fra_index = -1; + softa->ipf_auth_stats.fas_expire++; + softa->ipf_auth_used--; } } /* * Expire pre-auth rules */ - for (faep = &fae_list; ((fae = *faep) != NULL); ) { + for (faep = &softa->ipf_auth_entries; ((fae = *faep) != NULL); ) { fae->fae_age--; if (fae->fae_age == 0) { - fr_authderef(&fae); - fr_authstats.fas_expire++; + ipf_auth_deref(&fae); + softa->ipf_auth_stats.fas_expire++; } else faep = &fae->fae_next; } - if (fae_list != NULL) - ipauth = &fae_list->fae_fr; + if (softa->ipf_auth_entries != NULL) + softa->ipf_auth_ip = &softa->ipf_auth_entries->fae_fr; else - ipauth = NULL; + softa->ipf_auth_ip = NULL; - for (frp = &fr_authlist; ((fr = *frp) != NULL); ) { + for (frp = &softa->ipf_auth_rules; ((fr = *frp) != NULL); ) { if (fr->fr_ref == 1) { *frp = fr->fr_next; + MUTEX_DESTROY(&fr->fr_lock); KFREE(fr); } else frp = &fr->fr_next; } - RWLOCK_EXIT(&ipf_auth); + RWLOCK_EXIT(&softa->ipf_authlk); SPL_X(s); } /* ------------------------------------------------------------------------ */ -/* Function: fr_preauthcmd */ +/* Function: ipf_auth_precmd */ /* Returns: int - 0 == success, else error */ /* Parameters: cmd(I) - ioctl command for rule */ /* fr(I) - pointer to ipf rule */ /* fptr(I) - pointer to caller's 'fr' */ /* */ /* ------------------------------------------------------------------------ */ -int fr_preauthcmd(cmd, fr, frptr) -ioctlcmd_t cmd; -frentry_t *fr, **frptr; +int +ipf_auth_precmd(softc, cmd, fr, frptr) + ipf_main_softc_t *softc; + ioctlcmd_t cmd; + frentry_t *fr, **frptr; { + ipf_auth_softc_t *softa = softc->ipf_auth_soft; frauthent_t *fae, **faep; int error = 0; SPL_INT(s); - if ((cmd != SIOCADAFR) && (cmd != SIOCRMAFR)) + if ((cmd != SIOCADAFR) && (cmd != SIOCRMAFR)) { + IPFERROR(10006); return EIO; + } - for (faep = &fae_list; ((fae = *faep) != NULL); ) { + for (faep = &softa->ipf_auth_entries; ((fae = *faep) != NULL); ) { if (&fae->fae_fr == fr) break; else @@ -627,17 +794,22 @@ frentry_t *fr, **frptr; } if (cmd == (ioctlcmd_t)SIOCRMAFR) { - if (fr == NULL || frptr == NULL) + if (fr == NULL || frptr == NULL) { + IPFERROR(10007); error = EINVAL; - else if (fae == NULL) + + } else if (fae == NULL) { + IPFERROR(10008); error = ESRCH; - else { + + } else { SPL_NET(s); - WRITE_ENTER(&ipf_auth); + WRITE_ENTER(&softa->ipf_authlk); *faep = fae->fae_next; - if (ipauth == &fae->fae_fr) - ipauth = fae_list ? &fae_list->fae_fr : NULL; - RWLOCK_EXIT(&ipf_auth); + if (softa->ipf_auth_ip == &fae->fae_fr) + softa->ipf_auth_ip = softa->ipf_auth_entries ? + &softa->ipf_auth_entries->fae_fr : NULL; + RWLOCK_EXIT(&softa->ipf_authlk); SPL_X(s); KFREE(fae); @@ -648,161 +820,199 @@ frentry_t *fr, **frptr; bcopy((char *)fr, (char *)&fae->fae_fr, sizeof(*fr)); SPL_NET(s); - WRITE_ENTER(&ipf_auth); - fae->fae_age = fr_defaultauthage; + WRITE_ENTER(&softa->ipf_authlk); + fae->fae_age = softa->ipf_auth_defaultage; fae->fae_fr.fr_hits = 0; fae->fae_fr.fr_next = *frptr; fae->fae_ref = 1; *frptr = &fae->fae_fr; fae->fae_next = *faep; *faep = fae; - ipauth = &fae_list->fae_fr; - RWLOCK_EXIT(&ipf_auth); + softa->ipf_auth_ip = &softa->ipf_auth_entries->fae_fr; + RWLOCK_EXIT(&softa->ipf_authlk); SPL_X(s); - } else + } else { + IPFERROR(10009); error = ENOMEM; - } else + } + } else { + IPFERROR(10010); error = EINVAL; + } return error; } /* ------------------------------------------------------------------------ */ -/* Function: fr_authflush */ +/* Function: ipf_auth_flush */ /* Returns: int - number of auth entries flushed */ /* Parameters: None */ -/* Locks: WRITE(ipf_auth) */ +/* Locks: WRITE(ipf_authlk) */ /* */ -/* This function flushs the fr_authpkts array of any packet data with */ +/* This function flushs the ipf_auth_pkts array of any packet data with */ /* references still there. */ /* It is expected that the caller has already acquired the correct locks or */ /* set the priority level correctly for this to block out other code paths */ /* into these data structures. */ /* ------------------------------------------------------------------------ */ -int fr_authflush() +static int +ipf_auth_flush(arg) + void *arg; { - register int i, num_flushed; + ipf_auth_softc_t *softa = arg; + int i, num_flushed; mb_t *m; - if (fr_auth_lock) + if (softa->ipf_auth_lock) return -1; num_flushed = 0; - for (i = 0 ; i < fr_authsize; i++) { - m = fr_authpkts[i]; - if (m != NULL) { - FREE_MB_T(m); - fr_authpkts[i] = NULL; - fr_auth[i].fra_index = -1; + for (i = 0 ; i < softa->ipf_auth_size; i++) { + if (softa->ipf_auth[i].fra_index != -1) { + m = softa->ipf_auth_pkts[i]; + if (m != NULL) { + FREE_MB_T(m); + softa->ipf_auth_pkts[i] = NULL; + } + + softa->ipf_auth[i].fra_index = -1; /* perhaps add & use a flush counter inst.*/ - fr_authstats.fas_expire++; - fr_authused--; + softa->ipf_auth_stats.fas_expire++; num_flushed++; } } - fr_authstart = 0; - fr_authend = 0; - fr_authnext = 0; + softa->ipf_auth_start = 0; + softa->ipf_auth_end = 0; + softa->ipf_auth_next = 0; + softa->ipf_auth_used = 0; + softa->ipf_auth_replies = 0; return num_flushed; } /* ------------------------------------------------------------------------ */ -/* Function: fr_auth_waiting */ -/* Returns: int - 0 = no packets waiting, 1 = packets waiting. */ +/* Function: ipf_auth_waiting */ +/* Returns: int - number of packets in the auth queue */ /* Parameters: None */ /* */ /* Simple truth check to see if there are any packets waiting in the auth */ /* queue. */ /* ------------------------------------------------------------------------ */ -int fr_auth_waiting() +int +ipf_auth_waiting(softc) + ipf_main_softc_t *softc; { - return (fr_authused != 0); + ipf_auth_softc_t *softa = softc->ipf_auth_soft; + + return (softa->ipf_auth_used != 0); } /* ------------------------------------------------------------------------ */ -/* Function: fr_authgeniter */ +/* Function: ipf_auth_geniter */ /* Returns: int - 0 == success, else error */ /* Parameters: token(I) - pointer to ipftoken structure */ /* itp(I) - pointer to ipfgeniter structure */ +/* objp(I) - pointer to ipf object destription */ /* */ +/* Iterate through the list of entries in the auth queue list. */ +/* objp is used here to get the location of where to do the copy out to. */ +/* Stomping over various fields with new information will not harm anything */ /* ------------------------------------------------------------------------ */ -int fr_authgeniter(token, itp) -ipftoken_t *token; -ipfgeniter_t *itp; +static int +ipf_auth_geniter(softc, token, itp, objp) + ipf_main_softc_t *softc; + ipftoken_t *token; + ipfgeniter_t *itp; + ipfobj_t *objp; { + ipf_auth_softc_t *softa = softc->ipf_auth_soft; frauthent_t *fae, *next, zero; int error; - if (itp->igi_data == NULL) + if (itp->igi_data == NULL) { + IPFERROR(10011); return EFAULT; + } - if (itp->igi_type != IPFGENITER_AUTH) + if (itp->igi_type != IPFGENITER_AUTH) { + IPFERROR(10012); return EINVAL; + } + + objp->ipfo_type = IPFOBJ_FRAUTH; + objp->ipfo_ptr = itp->igi_data; + objp->ipfo_size = sizeof(frauth_t); + + READ_ENTER(&softa->ipf_authlk); fae = token->ipt_data; - READ_ENTER(&ipf_auth); if (fae == NULL) { - next = fae_list; + next = softa->ipf_auth_entries; } else { next = fae->fae_next; } + /* + * If we found an auth entry to use, bump its reference count + * so that it can be used for is_next when we come back. + */ if (next != NULL) { - /* - * If we find an auth entry to use, bump its reference count - * so that it can be used for is_next when we come back. - */ ATOMIC_INC(next->fae_ref); - if (next->fae_next == NULL) { - ipf_freetoken(token); - token = NULL; - } else { - token->ipt_data = next; - } + token->ipt_data = next; } else { bzero(&zero, sizeof(zero)); next = &zero; - } - RWLOCK_EXIT(&ipf_auth); - - /* - * If we had a prior pointer to an auth entry, release it. - */ - if (fae != NULL) { - WRITE_ENTER(&ipf_auth); - fr_authderef(&fae); - RWLOCK_EXIT(&ipf_auth); + token->ipt_data = NULL; } - /* - * This should arguably be via fr_outobj() so that the auth - * structure can (if required) be massaged going out. - */ - error = COPYOUT(next, itp->igi_data, sizeof(*next)); - if (error != 0) - error = EFAULT; + RWLOCK_EXIT(&softa->ipf_authlk); + error = ipf_outobjk(softc, objp, next); + if (fae != NULL) + ipf_auth_deref_unlocked(softa, &fae); + + if (next->fae_next == NULL) + ipf_token_mark_complete(token); return error; } /* ------------------------------------------------------------------------ */ -/* Function: fr_authderef */ +/* Function: ipf_auth_deref_unlocked */ /* Returns: None */ /* Parameters: faep(IO) - pointer to caller's frauthent_t pointer */ -/* Locks: WRITE(ipf_auth) */ +/* */ +/* Wrapper for ipf_auth_deref for when a write lock on ipf_authlk is not */ +/* held. */ +/* ------------------------------------------------------------------------ */ +static void +ipf_auth_deref_unlocked(softa, faep) + ipf_auth_softc_t *softa; + frauthent_t **faep; +{ + WRITE_ENTER(&softa->ipf_authlk); + ipf_auth_deref(faep); + RWLOCK_EXIT(&softa->ipf_authlk); +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_auth_deref */ +/* Returns: None */ +/* Parameters: faep(IO) - pointer to caller's frauthent_t pointer */ +/* Locks: WRITE(ipf_authlk) */ /* */ /* This function unconditionally sets the pointer in the caller to NULL, */ /* to make it clear that it should no longer use that pointer, and drops */ /* the reference count on the structure by 1. If it reaches 0, free it up. */ /* ------------------------------------------------------------------------ */ -void fr_authderef(faep) -frauthent_t **faep; +static void +ipf_auth_deref(faep) + frauthent_t **faep; { frauthent_t *fae; @@ -817,30 +1027,30 @@ frauthent_t **faep; /* ------------------------------------------------------------------------ */ -/* Function: fr_authwait */ +/* Function: ipf_auth_wait_pkt */ /* Returns: int - 0 == success, else error */ /* Parameters: data(I) - pointer to data from ioctl call */ /* */ /* This function is called when an application is waiting for a packet to */ /* match an "auth" rule by issuing an SIOCAUTHW ioctl. If there is already */ /* a packet waiting on the queue then we will return that _one_ immediately.*/ -/* If there are no packets present in the queue (fr_authpkts) then we go to */ -/* sleep. */ +/* If there are no packets present in the queue (ipf_auth_pkts) then we go */ +/* to sleep. */ /* ------------------------------------------------------------------------ */ -int fr_authwait(data) -char *data; +static int +ipf_auth_wait(softc, softa, data) + ipf_main_softc_t *softc; + ipf_auth_softc_t *softa; + char *data; { frauth_t auth, *au = &auth; int error, len, i; mb_t *m; char *t; -#if defined(_KERNEL) && !defined(MENTAT) && !defined(linux) && \ - (!defined(__FreeBSD_version) || (__FreeBSD_version < 501000)) SPL_INT(s); -#endif -fr_authioctlloop: - error = fr_inobj(data, au, IPFOBJ_FRAUTH); +ipf_auth_ioctlloop: + error = ipf_inobj(softc, data, NULL, au, IPFOBJ_FRAUTH); if (error != 0) return error; @@ -850,30 +1060,36 @@ fr_authioctlloop: * we are trying to guard against here is an error in the copyout * steps should not cause the packet to "disappear" from the queue. */ - READ_ENTER(&ipf_auth); + SPL_NET(s); + READ_ENTER(&softa->ipf_authlk); /* - * If fr_authnext is not equal to fr_authend it will be because there - * is a packet waiting to be delt with in the fr_authpkts array. We - * copy as much of that out to user space as requested. + * If ipf_auth_next is not equal to ipf_auth_end it will be because + * there is a packet waiting to be delt with in the ipf_auth_pkts + * array. We copy as much of that out to user space as requested. */ - if (fr_authused > 0) { - while (fr_authpkts[fr_authnext] == NULL) { - fr_authnext++; - if (fr_authnext == fr_authsize) - fr_authnext = 0; + if (softa->ipf_auth_used > 0) { + while (softa->ipf_auth_pkts[softa->ipf_auth_next] == NULL) { + softa->ipf_auth_next++; + if (softa->ipf_auth_next == softa->ipf_auth_size) + softa->ipf_auth_next = 0; } - error = fr_outobj(data, &fr_auth[fr_authnext], IPFOBJ_FRAUTH); - if (error != 0) + error = ipf_outobj(softc, data, + &softa->ipf_auth[softa->ipf_auth_next], + IPFOBJ_FRAUTH); + if (error != 0) { + RWLOCK_EXIT(&softa->ipf_authlk); + SPL_X(s); return error; + } if (auth.fra_len != 0 && auth.fra_buf != NULL) { /* * Copy packet contents out to user space if * requested. Bail on an error. */ - m = fr_authpkts[fr_authnext]; + m = softa->ipf_auth_pkts[softa->ipf_auth_next]; len = MSGDSIZE(m); if (len > auth.fra_len) len = auth.fra_len; @@ -881,62 +1097,69 @@ fr_authioctlloop: for (t = auth.fra_buf; m && (len > 0); ) { i = MIN(M_LEN(m), len); - error = copyoutptr(MTOD(m, char *), &t, i); + error = copyoutptr(softc, MTOD(m, char *), + &t, i); len -= i; t += i; - if (error != 0) + if (error != 0) { + RWLOCK_EXIT(&softa->ipf_authlk); + SPL_X(s); return error; + } m = m->m_next; } } - RWLOCK_EXIT(&ipf_auth); + RWLOCK_EXIT(&softa->ipf_authlk); SPL_NET(s); - WRITE_ENTER(&ipf_auth); - fr_authnext++; - if (fr_authnext == fr_authsize) - fr_authnext = 0; - RWLOCK_EXIT(&ipf_auth); + WRITE_ENTER(&softa->ipf_authlk); + softa->ipf_auth_next++; + if (softa->ipf_auth_next == softa->ipf_auth_size) + softa->ipf_auth_next = 0; + RWLOCK_EXIT(&softa->ipf_authlk); SPL_X(s); return 0; } - RWLOCK_EXIT(&ipf_auth); + RWLOCK_EXIT(&softa->ipf_authlk); + SPL_X(s); - MUTEX_ENTER(&ipf_authmx); + MUTEX_ENTER(&softa->ipf_auth_mx); #ifdef _KERNEL # if SOLARIS error = 0; - if (!cv_wait_sig(&ipfauthwait, &ipf_authmx.ipf_lk)) + if (!cv_wait_sig(&softa->ipf_auth_wait, &softa->ipf_auth_mx.ipf_lk)) { + IPFERROR(10014); error = EINTR; + } # else /* SOLARIS */ # ifdef __hpux { lock_t *l; - l = get_sleep_lock(&fr_authnext); - error = sleep(&fr_authnext, PZERO+1); + l = get_sleep_lock(&softa->ipf_auth_next); + error = sleep(&softa->ipf_auth_next, PZERO+1); spinunlock(l); } # else # ifdef __osf__ - error = mpsleep(&fr_authnext, PSUSP|PCATCH, "fr_authnext", 0, - &ipf_authmx, MS_LOCK_SIMPLE); + error = mpsleep(&softa->ipf_auth_next, PSUSP|PCATCH, "ipf_auth_next", + 0, &softa->ipf_auth_mx, MS_LOCK_SIMPLE); # else - error = SLEEP(&fr_authnext, "fr_authnext"); + error = SLEEP(&softa->ipf_auth_next, "ipf_auth_next"); # endif /* __osf__ */ # endif /* __hpux */ # endif /* SOLARIS */ #endif - MUTEX_EXIT(&ipf_authmx); + MUTEX_EXIT(&softa->ipf_auth_mx); if (error == 0) - goto fr_authioctlloop; + goto ipf_auth_ioctlloop; return error; } /* ------------------------------------------------------------------------ */ -/* Function: fr_authreply */ +/* Function: ipf_auth_reply */ /* Returns: int - 0 == success, else error */ /* Parameters: data(I) - pointer to data from ioctl call */ /* */ @@ -945,23 +1168,27 @@ fr_authioctlloop: /* received information using an SIOCAUTHW. The decision returned in the */ /* form of flags, the same as those used in each rule. */ /* ------------------------------------------------------------------------ */ -int fr_authreply(data) -char *data; +static int +ipf_auth_reply(softc, softa, data) + ipf_main_softc_t *softc; + ipf_auth_softc_t *softa; + char *data; { frauth_t auth, *au = &auth, *fra; + fr_info_t fin; int error, i; mb_t *m; SPL_INT(s); - error = fr_inobj(data, &auth, IPFOBJ_FRAUTH); + error = ipf_inobj(softc, data, NULL, &auth, IPFOBJ_FRAUTH); if (error != 0) return error; SPL_NET(s); - WRITE_ENTER(&ipf_auth); + WRITE_ENTER(&softa->ipf_authlk); i = au->fra_index; - fra = fr_auth + i; + fra = softa->ipf_auth + i; error = 0; /* @@ -969,19 +1196,27 @@ char *data; * checks. First, the auth index value should be within the size of * the array and second the packet id being returned should also match. */ - if ((i < 0) || (i >= fr_authsize) || - (fra->fra_info.fin_id != au->fra_info.fin_id)) { - RWLOCK_EXIT(&ipf_auth); + if ((i < 0) || (i >= softa->ipf_auth_size)) { + RWLOCK_EXIT(&softa->ipf_authlk); SPL_X(s); + IPFERROR(10015); + return ESRCH; + } + if (fra->fra_info.fin_id != au->fra_info.fin_id) { + RWLOCK_EXIT(&softa->ipf_authlk); + SPL_X(s); + IPFERROR(10019); return ESRCH; } - m = fr_authpkts[i]; + m = softa->ipf_auth_pkts[i]; fra->fra_index = -2; fra->fra_pass = au->fra_pass; - fr_authpkts[i] = NULL; + softa->ipf_auth_pkts[i] = NULL; + softa->ipf_auth_replies++; + bcopy(&fra->fra_info, &fin, sizeof(fin)); - RWLOCK_EXIT(&ipf_auth); + RWLOCK_EXIT(&softa->ipf_authlk); /* * Re-insert the packet back into the packet stream flowing through @@ -992,22 +1227,25 @@ char *data; */ #ifdef _KERNEL if ((m != NULL) && (au->fra_info.fin_out != 0)) { - error = ipf_inject(&fra->fra_info, m); + error = ipf_inject(&fin, m); if (error != 0) { + IPFERROR(10016); error = ENOBUFS; - fr_authstats.fas_sendfail++; + softa->ipf_auth_stats.fas_sendfail++; } else { - fr_authstats.fas_sendok++; + softa->ipf_auth_stats.fas_sendok++; } } else if (m) { - error = ipf_inject(&fra->fra_info, m); + error = ipf_inject(&fin, m); if (error != 0) { + IPFERROR(10017); error = ENOBUFS; - fr_authstats.fas_quefail++; + softa->ipf_auth_stats.fas_quefail++; } else { - fr_authstats.fas_queok++; + softa->ipf_auth_stats.fas_queok++; } } else { + IPFERROR(10018); error = EINVAL; } @@ -1016,28 +1254,54 @@ char *data; * not being processed, make sure we advance to the next one. */ if (error == ENOBUFS) { - WRITE_ENTER(&ipf_auth); - fr_authused--; + WRITE_ENTER(&softa->ipf_authlk); + softa->ipf_auth_used--; fra->fra_index = -1; fra->fra_pass = 0; - if (i == fr_authstart) { + if (i == softa->ipf_auth_start) { while (fra->fra_index == -1) { i++; - if (i == fr_authsize) + if (i == softa->ipf_auth_size) i = 0; - fr_authstart = i; - if (i == fr_authend) + softa->ipf_auth_start = i; + if (i == softa->ipf_auth_end) break; } - if (fr_authstart == fr_authend) { - fr_authnext = 0; - fr_authstart = fr_authend = 0; + if (softa->ipf_auth_start == softa->ipf_auth_end) { + softa->ipf_auth_next = 0; + softa->ipf_auth_start = 0; + softa->ipf_auth_end = 0; } } - RWLOCK_EXIT(&ipf_auth); + RWLOCK_EXIT(&softa->ipf_authlk); } #endif /* _KERNEL */ SPL_X(s); return 0; } + + +u_32_t +ipf_auth_pre_scanlist(softc, fin, pass) + ipf_main_softc_t *softc; + fr_info_t *fin; + u_32_t pass; +{ + ipf_auth_softc_t *softa = softc->ipf_auth_soft; + + if (softa->ipf_auth_ip != NULL) + return ipf_scanlist(fin, softc->ipf_pass); + + return pass; +} + + +frentry_t ** +ipf_auth_rulehead(softc) + ipf_main_softc_t *softc; +{ + ipf_auth_softc_t *softa = softc->ipf_auth_soft; + + return &softa->ipf_auth_ip; +} diff --git a/sys/contrib/ipfilter/netinet/ip_auth.h b/sys/contrib/ipfilter/netinet/ip_auth.h index 36c4bacf8f9..914f9996ef0 100644 --- a/sys/contrib/ipfilter/netinet/ip_auth.h +++ b/sys/contrib/ipfilter/netinet/ip_auth.h @@ -1,7 +1,7 @@ /* $FreeBSD$ */ /* - * Copyright (C) 1997-2001 by Darren Reed & Guido Van Rooij. + * Copyright (C) 2012 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. * @@ -21,6 +21,7 @@ typedef struct frauth { u_32_t fra_pass; fr_info_t fra_info; char *fra_buf; + u_32_t fra_flx; #ifdef MENTAT queue_t *fra_q; mb_t *fra_m; @@ -35,7 +36,7 @@ typedef struct frauthent { int fae_ref; } frauthent_t; -typedef struct fr_authstat { +typedef struct ipf_authstat { U_QUAD_T fas_hits; U_QUAD_T fas_miss; u_long fas_nospace; @@ -46,26 +47,28 @@ typedef struct fr_authstat { u_long fas_quefail; u_long fas_expire; frauthent_t *fas_faelist; -} fr_authstat_t; +} ipf_authstat_t; -extern frentry_t *ipauth; -extern struct fr_authstat fr_authstats; -extern int fr_defaultauthage; -extern int fr_authstart; -extern int fr_authend; -extern int fr_authsize; -extern int fr_authused; -extern int fr_auth_lock; -extern frentry_t *fr_checkauth __P((fr_info_t *, u_32_t *)); -extern void fr_authexpire __P((void)); -extern int fr_authinit __P((void)); -extern void fr_authunload __P((void)); -extern int fr_authflush __P((void)); -extern mb_t **fr_authpkts; -extern int fr_newauth __P((mb_t *, fr_info_t *)); -extern int fr_preauthcmd __P((ioctlcmd_t, frentry_t *, frentry_t **)); -extern int fr_auth_ioctl __P((caddr_t, ioctlcmd_t, int, int, void *)); -extern int fr_auth_waiting __P((void)); +extern frentry_t *ipf_auth_check __P((fr_info_t *, u_32_t *)); +extern void ipf_auth_expire __P((ipf_main_softc_t *)); +extern int ipf_auth_ioctl __P((ipf_main_softc_t *, caddr_t, ioctlcmd_t, + int, int, void *)); +extern int ipf_auth_init __P((void)); +extern int ipf_auth_main_load __P((void)); +extern int ipf_auth_main_unload __P((void)); +extern void ipf_auth_soft_destroy __P((ipf_main_softc_t *, void *)); +extern void *ipf_auth_soft_create __P((ipf_main_softc_t *)); +extern int ipf_auth_new __P((mb_t *, fr_info_t *)); +extern int ipf_auth_precmd __P((ipf_main_softc_t *, ioctlcmd_t, + frentry_t *, frentry_t **)); +extern void ipf_auth_unload __P((ipf_main_softc_t *)); +extern int ipf_auth_waiting __P((ipf_main_softc_t *)); +extern void ipf_auth_setlock __P((void *, int)); +extern int ipf_auth_soft_init __P((ipf_main_softc_t *, void *)); +extern int ipf_auth_soft_fini __P((ipf_main_softc_t *, void *)); +extern u_32_t ipf_auth_pre_scanlist __P((ipf_main_softc_t *, fr_info_t *, + u_32_t)); +extern frentry_t **ipf_auth_rulehead __P((ipf_main_softc_t *)); #endif /* __IP_AUTH_H__ */ diff --git a/sys/contrib/ipfilter/netinet/ip_compat.h b/sys/contrib/ipfilter/netinet/ip_compat.h index 4305c48e54a..6cce5912f2c 100644 --- a/sys/contrib/ipfilter/netinet/ip_compat.h +++ b/sys/contrib/ipfilter/netinet/ip_compat.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 1993-2001, 2003 by Darren Reed. + * Copyright (C) 2012 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. * @@ -32,14 +32,7 @@ # define __KERNEL__ #endif -#ifndef SOLARIS #define SOLARIS (defined(sun) && (defined(__svr4__) || defined(__SVR4))) -#endif -#if (defined(SOLARIS2) && (SOLARIS2 >= 8)) -# ifndef USE_INET6 -# define USE_INET6 -# endif -#endif #if defined(__FreeBSD_version) && (__FreeBSD_version >= 400000) && \ !defined(_KERNEL) && !defined(USE_INET6) && !defined(NOINET6) # define USE_INET6 @@ -47,26 +40,22 @@ #if defined(__NetBSD_Version__) && (__NetBSD_Version__ >= 105000000) && \ !defined(_KERNEL) && !defined(USE_INET6) # define USE_INET6 +#endif +#if defined(__NetBSD_Version__) && (__NetBSD_Version__ >= 106140000) && \ + defined(_KERNEL) && \ + (!defined(IPFILTER_LKM) || (__NetBSD_Version__ >= 399000100)) # define IPFILTER_M_IPFILTER #endif -#if defined(OpenBSD) && (OpenBSD >= 200206) && \ +#if !defined(USE_INET6) +# if defined(OpenBSD) && (OpenBSD >= 200206) && \ !defined(_KERNEL) && !defined(USE_INET6) -# define USE_INET6 -#endif -#if defined(__osf__) -# define USE_INET6 -#endif -#if defined(linux) && (!defined(_KERNEL) || defined(CONFIG_IPV6)) -# define USE_INET6 -#endif -#if defined(HPUXREV) && (HPUXREV >= 1111) -# define USE_INET6 +# define USE_INET6 +# endif +# if defined(HPUXREV) && (HPUXREV >= 1111) +# define USE_INET6 +# endif #endif -#if defined(BSD) && (BSD < 199103) && defined(__osf__) -# undef BSD -# define BSD 199103 -#endif #if defined(__SVR4) || defined(__svr4__) || defined(__sgi) # define index strchr @@ -95,862 +84,120 @@ struct ether_addr { }; #endif -#if defined(__sgi) && !defined(IPFILTER_LKM) -# ifdef __STDC__ -# define IPL_EXTERN(ep) ipfilter##ep -# else -# define IPL_EXTERN(ep) ipfilter/**/ep -# endif -#else # ifdef __STDC__ # define IPL_EXTERN(ep) ipl##ep # else # define IPL_EXTERN(ep) ipl/**/ep # endif -#endif /* * This is a workaround for troubles on FreeBSD and OpenBSD. */ -#ifndef linux # ifndef _KERNEL # define ADD_KERNEL # define _KERNEL # define KERNEL # endif -# ifdef __OpenBSD__ -struct file; -# endif # include # ifdef ADD_KERNEL # undef _KERNEL # undef KERNEL # endif -#endif - -/* ----------------------------------------------------------------------- */ -/* S O L A R I S */ -/* ----------------------------------------------------------------------- */ -#if SOLARIS -# define MENTAT 1 -# include -# include -# include -# include -# include -# include -# if defined(SOLARIS2) && SOLARIS2 >= 10 -# include -# include -# include -# include -# endif -/* - * because Solaris 2 defines these in two places :-/ - */ -# ifndef KERNEL -# define _KERNEL -# undef RES_INIT -# endif /* _KERNEL */ - -# if defined(SOLARIS2) && SOLARIS2 >= 8 -# include -# include -# endif - -# include -/* These 5 are defined in and */ -# undef IPOPT_EOL -# undef IPOPT_NOP -# undef IPOPT_LSRR -# undef IPOPT_RR -# undef IPOPT_SSRR -# ifdef i386 -# define _SYS_PROMIF_H -# endif -# ifndef _KERNEL -# include "radix_ipf.h" -# else -# include "radix_ipf_local.h" -# endif -# include -# undef COPYOUT -# include -# ifndef KERNEL -# undef _KERNEL -# endif -# if defined(SOLARIS2) && SOLARIS2 >= 8 -# define SNPRINTF snprintf - -# include -# define ipif_local_addr ipif_lcl_addr -/* Only defined in private include file */ -# ifndef V4_PART_OF_V6 -# define V4_PART_OF_V6(v6) v6.s6_addr32[3] -# endif -struct ip6_ext { - u_char ip6e_nxt; - u_char ip6e_len; -}; -# endif /* SOLARIS2 >= 8 */ - -# if defined(SOLARIS2) && SOLARIS2 >= 6 -# include -typedef uint32_t u_32_t; -# else -typedef unsigned int u_32_t; -# endif -# define U_32_T 1 - -# ifdef _KERNEL -# define NEED_LOCAL_RAND 1 -# define ipf_random arc4random -# define KRWLOCK_T krwlock_t -# define KMUTEX_T kmutex_t - -# if !defined(FW_HOOKS) -# include "qif.h" -# include "pfil.h" -# else -# include - -extern net_data_t ipfipv4; -extern net_data_t ipfipv6; - -typedef struct qpktinfo { - void *qpi_data; - mblk_t **qpi_mp; - mblk_t *qpi_m; - uintptr_t qpi_real; - int qpi_flags; - int qpi_num; - int qpi_off; -} qpktinfo_t; -# define QF_GROUP 0x01 -# endif - -# if SOLARIS2 >= 6 -# if SOLARIS2 == 6 -# define ATOMIC_INCL(x) atomic_add_long((uint32_t*)&(x), 1) -# define ATOMIC_DECL(x) atomic_add_long((uint32_t*)&(x), -1) -# else -# define ATOMIC_INCL(x) atomic_add_long(&(x), 1) -# define ATOMIC_DECL(x) atomic_add_long(&(x), -1) -# endif /* SOLARIS2 == 6 */ -# define ATOMIC_INC64(x) atomic_add_64((uint64_t*)&(x), 1) -# define ATOMIC_INC32(x) atomic_add_32((uint32_t*)&(x), 1) -# define ATOMIC_INC16(x) atomic_add_16((uint16_t*)&(x), 1) -# define ATOMIC_DEC64(x) atomic_add_64((uint64_t*)&(x), -1) -# define ATOMIC_DEC32(x) atomic_add_32((uint32_t*)&(x), -1) -# define ATOMIC_DEC16(x) atomic_add_16((uint16_t*)&(x), -1) -# else -# define ATOMIC_INC(x) { mutex_enter(&ipf_rw); (x)++; \ - mutex_exit(&ipf_rw); } -# define ATOMIC_DEC(x) { mutex_enter(&ipf_rw); (x)--; \ - mutex_exit(&ipf_rw); } -# endif /* SOLARIS2 >= 6 */ -# define USE_MUTEXES -# define MUTEX_ENTER(x) mutex_enter(&(x)->ipf_lk) -# define READ_ENTER(x) rw_enter(&(x)->ipf_lk, RW_READER) -# define WRITE_ENTER(x) rw_enter(&(x)->ipf_lk, RW_WRITER) -# define MUTEX_DOWNGRADE(x) rw_downgrade(&(x)->ipf_lk) -# define RWLOCK_INIT(x, y) rw_init(&(x)->ipf_lk, (y), \ - RW_DRIVER, NULL) -# define RWLOCK_EXIT(x) rw_exit(&(x)->ipf_lk) -# define RW_DESTROY(x) rw_destroy(&(x)->ipf_lk) -# define MUTEX_INIT(x, y) mutex_init(&(x)->ipf_lk, (y), \ - MUTEX_DRIVER, NULL) -# define MUTEX_DESTROY(x) mutex_destroy(&(x)->ipf_lk) -# define MUTEX_NUKE(x) bzero((x), sizeof(*(x))) -# define MUTEX_EXIT(x) mutex_exit(&(x)->ipf_lk) -# define COPYIN(a,b,c) copyin((caddr_t)(a), (caddr_t)(b), (c)) -# define COPYOUT(a,b,c) copyout((caddr_t)(a), (caddr_t)(b), (c)) -# define BCOPYIN(a,b,c) copyin((caddr_t)(a), (caddr_t)(b), (c)) -# define BCOPYOUT(a,b,c) copyout((caddr_t)(a), (caddr_t)(b), (c)) -# define UIOMOVE(a,b,c,d) uiomove((caddr_t)a,b,c,d) -# define KFREE(x) kmem_free((char *)(x), sizeof(*(x))) -# define KFREES(x,s) kmem_free((char *)(x), (s)) -# define SPL_SCHED(x) ; -# define SPL_NET(x) ; -# define SPL_IMP(x) ; -# undef SPL_X -# define SPL_X(x) ; -# ifdef sparc -# define ntohs(x) (x) -# define ntohl(x) (x) -# define htons(x) (x) -# define htonl(x) (x) -# endif /* sparc */ -# define KMALLOC(a,b) (a) = (b)kmem_alloc(sizeof(*(a)), KM_NOSLEEP) -# define KMALLOCS(a,b,c) (a) = (b)kmem_alloc((c), KM_NOSLEEP) -# define GET_MINOR(x) getminor(x) -extern void *get_unit __P((char *, int)); -# define GETIFP(n, v) get_unit(n, v) -# if defined(_INET_IP_STACK_H) -# define COPYIFNAME(v, x, b) \ - do { \ - if ((v) == 4) { \ - (void) net_getifname(ipfipv4,\ - (uintptr_t)x, b, \ - LIFNAMSIZ); \ - } else { \ - (void) net_getifname(ipfipv6,\ - (uintptr_t)x, b, \ - LIFNAMSIZ); \ - } \ - } while (0) -# else -# define COPYIFNAME(v, x, b) \ - (void) strncpy(b, ((qif_t *)x)->qf_name, \ - LIFNAMSIZ) -# endif -# define GETKTIME(x) uniqtime((struct timeval *)x) -# define MSGDSIZE(x) msgdsize(x) -# define M_LEN(x) ((x)->b_wptr - (x)->b_rptr) -# define M_DUPLICATE(x) dupmsg((x)) -# define MTOD(m,t) ((t)((m)->b_rptr)) -# define MTYPE(m) ((m)->b_datap->db_type) -# define FREE_MB_T(m) freemsg(m) -# define m_next b_cont -# if !defined(_INET_IP_STACK_H) -# define CACHE_HASH(x) (((qpktinfo_t *)(x)->fin_qpi)->qpi_num & 7) -# else -# define CACHE_HASH(x) ((uintptr_t)(x)->fin_ifp & 7) -# endif -# define IPF_PANIC(x,y) if (x) { printf y; cmn_err(CE_PANIC, "ipf_panic"); } -typedef mblk_t mb_t; -# endif /* _KERNEL */ - -# if defined(SOLARIS2) && (SOLARIS2 >= 7) -# ifdef lint -# define ALIGN32(ptr) (ptr ? 0L : 0L) -# define ALIGN16(ptr) (ptr ? 0L : 0L) -# else -# define ALIGN32(ptr) (ptr) -# define ALIGN16(ptr) (ptr) -# endif -# endif - -# if defined(SOLARIS2) && SOLARIS2 < 6 -typedef struct uio uio_t; -# endif -typedef int ioctlcmd_t; -typedef uint8_t u_int8_t; - -# define OS_RECOGNISED 1 - -#endif /* SOLARIS */ - -/* ----------------------------------------------------------------------- */ -/* H P U X */ -/* ----------------------------------------------------------------------- */ -#ifdef __hpux -# define MENTAT 1 -# include -# include -# include -# include -# ifdef USE_INET6 -# include -# include -# include -typedef struct ip6_hdr ip6_t; -# endif - -# ifdef _KERNEL -# define SNPRINTF sprintf -# if (HPUXREV >= 1111) -# define IPL_SELECT -# ifdef IPL_SELECT -# include -# include -# define READ_COLLISION 0x01 - -typedef struct iplog_select_s { - kthread_t *read_waiter; - int state; -} iplog_select_t; -# endif -# endif - -# define GETKTIME(x) uniqtime((struct timeval *)x) - -# if HPUXREV == 1111 -# include "kern_svcs.h" -# else -# include -# endif -# undef ti_flags -# undef TCP_NODELAY -# undef TCP_MAXSEG -# include -# include "../netinet/ip_info.h" -/* - * According to /usr/include/sys/spinlock.h on HP-UX 11.00, these functions - * are available. Attempting to use them actually results in unresolved - * symbols when it comes time to load the module. - * This has been fixed! Yipee! - */ -# if 1 -# ifdef __LP64__ -# define ATOMIC_INCL(x) lock_and_incr_int64(&ipf_rw.ipf_lk, &(x), 1) -# define ATOMIC_DECL(x) lock_and_incr_int64(&ipf_rw.ipf_lk, &(x), -1) -# else -# define ATOMIC_INCL(x) lock_and_incr_int32(&ipf_rw.ipf_lk, &(x), 1) -# define ATOMIC_DECL(x) lock_and_incr_int32(&ipf_rw.ipf_lk, &(x), -1) -# endif -# define ATOMIC_INC64(x) lock_and_incr_int64(&ipf_rw.ipf_lk, &(x), 1) -# define ATOMIC_INC32(x) lock_and_incr_int32(&ipf_rw.ipf_lk, &(x), 1) -# define ATOMIC_INC16(x) lock_and_incr_int16(&ipf_rw.ipf_lk, &(x), 1) -# define ATOMIC_DEC64(x) lock_and_incr_int64(&ipf_rw.ipf_lk, &(x), -1) -# define ATOMIC_DEC32(x) lock_and_incr_int32(&ipf_rw.ipf_lk, &(x), -1) -# define ATOMIC_DEC16(x) lock_and_incr_int16(&ipf_rw.ipf_lk, &(x), -1) -# else /* 0 */ -# define ATOMIC_INC64(x) { MUTEX_ENTER(&ipf_rw); (x)++; \ - MUTEX_EXIT(&ipf_rw); } -# define ATOMIC_DEC64(x) { MUTEX_ENTER(&ipf_rw); (x)--; \ - MUTEX_EXIT(&ipf_rw); } -# define ATOMIC_INC32(x) { MUTEX_ENTER(&ipf_rw); (x)++; \ - MUTEX_EXIT(&ipf_rw); } -# define ATOMIC_DEC32(x) { MUTEX_ENTER(&ipf_rw); (x)--; \ - MUTEX_EXIT(&ipf_rw); } -# define ATOMIC_INCL(x) { MUTEX_ENTER(&ipf_rw); (x)++; \ - MUTEX_EXIT(&ipf_rw); } -# define ATOMIC_DECL(x) { MUTEX_ENTER(&ipf_rw); (x)--; \ - MUTEX_EXIT(&ipf_rw); } -# define ATOMIC_INC(x) { MUTEX_ENTER(&ipf_rw); (x)++; \ - MUTEX_EXIT(&ipf_rw); } -# define ATOMIC_DEC(x) { MUTEX_ENTER(&ipf_rw); (x)--; \ - MUTEX_EXIT(&ipf_rw); } -# endif -# define ip_cksum ip_csuma -# define memcpy(a,b,c) bcopy((caddr_t)b, (caddr_t)a, c) -# define USE_MUTEXES -# define MUTEX_INIT(x, y) initlock(&(x)->ipf_lk, 0, 0, (y)) -# define MUTEX_ENTER(x) spinlock(&(x)->ipf_lk) -# define MUTEX_EXIT(x) spinunlock(&(x)->ipf_lk); -# define MUTEX_DESTROY(x) -# define MUTEX_NUKE(x) bzero((char *)(x), sizeof(*(x))) -# define KMUTEX_T lock_t -# define kmutex_t lock_t /* for pfil.h */ -# define krwlock_t lock_t /* for pfil.h */ -/* - * The read-write lock implementation in HP-UX 11.0 is crippled - it can - * only be used by threads working in a user context! - * This has been fixed! Yipee! (Or at least it does in 11.00, not 11.11..) - */ -# if HPUXREV < 1111 -# define MUTEX_DOWNGRADE(x) lock_write_to_read(x) -# define KRWLOCK_T struct rw_lock -# define READ_ENTER(x) lock_read(&(x)->ipf_lk) -# define WRITE_ENTER(x) lock_write(&(x)->ipf_lk) -# if HPUXREV >= 1111 -# define RWLOCK_INIT(x, y) rwlock_init4(&(x)->ipf_lk, 0, RWLCK_CANSLEEP, 0, y) -# else -# define RWLOCK_INIT(x, y) lock_init3(&(x)->ipf_lk, 0, 1, 0, 0, y) -# endif -# define RWLOCK_EXIT(x) lock_done(&(x)->ipf_lk) -# else -# define KRWLOCK_T lock_t -# define KMUTEX_T lock_t -# define READ_ENTER(x) MUTEX_ENTER(x) -# define WRITE_ENTER(x) MUTEX_ENTER(x) -# define MUTEX_DOWNGRADE(x) -# define RWLOCK_INIT(x, y) initlock(&(x)->ipf_lk, 0, 0, y) -# define RWLOCK_EXIT(x) MUTEX_EXIT(x) -# endif -# define RW_DESTROY(x) -# define COPYIN(a,b,c) copyin((caddr_t)(a), (caddr_t)(b), (c)) -# define COPYOUT(a,b,c) copyout((caddr_t)(a), (caddr_t)(b), (c)) -# define SPL_SCHED(x) ; -# define SPL_NET(x) ; -# define SPL_IMP(x) ; -# undef SPL_X -# define SPL_X(x) ; -extern void *get_unit __P((char *, int)); -# define GETIFP(n, v) get_unit(n, v) -# define COPYIFNAME(v, x, b) \ - (void) strncpy(b, ((qif_t *)x)->qf_name, \ - LIFNAMSIZ) -# define UIOMOVE(a,b,c,d) uiomove((caddr_t)a,b,c,d) -# define SLEEP(id, n) { lock_t *_l = get_sleep_lock((caddr_t)id); \ - sleep(id, PZERO+1); \ - spinunlock(_l); \ - } -# define WAKEUP(id,x) { lock_t *_l = get_sleep_lock((caddr_t)id); \ - wakeup(id + x); \ - spinunlock(_l); \ - } -# define POLLWAKEUP(x) ; -# define KMALLOC(a, b) MALLOC((a), b, sizeof(*(a)), M_IOSYS, M_NOWAIT) -# define KMALLOCS(a, b, c) MALLOC((a), b, (c), M_IOSYS, M_NOWAIT) -# define KFREE(x) kmem_free((char *)(x), sizeof(*(x))) -# define KFREES(x,s) kmem_free((char *)(x), (s)) -# define MSGDSIZE(x) msgdsize(x) -# define M_LEN(x) ((x)->b_wptr - (x)->b_rptr) -# define M_DUPLICATE(x) dupmsg((x)) -# define MTOD(m,t) ((t)((m)->b_rptr)) -# define MTYPE(m) ((m)->b_datap->db_type) -# define FREE_MB_T(m) freemsg(m) -# define m_next b_cont -# define IPF_PANIC(x,y) if (x) { printf y; panic("ipf_panic"); } -typedef mblk_t mb_t; - -# define CACHE_HASH(x) (((qpktinfo_t *)(x)->fin_qpi)->qpi_num & 7) - -# include "qif.h" -# include "pfil.h" - -# else /* _KERNEL */ - -typedef unsigned char uchar_t; - -# ifndef _SYS_STREAM_INCLUDED -typedef char * mblk_t; -typedef void * queue_t; -typedef u_long ulong; -# endif -# include - -# endif /* _KERNEL */ - -# ifdef lint -# define ALIGN32(ptr) (ptr ? 0L : 0L) -# define ALIGN16(ptr) (ptr ? 0L : 0L) -# else -# define ALIGN32(ptr) (ptr) -# define ALIGN16(ptr) (ptr) -# endif - -typedef struct uio uio_t; -typedef int ioctlcmd_t; -typedef int minor_t; -typedef unsigned int u_32_t; -# define U_32_T 1 - -# define OS_RECOGNISED 1 - -#endif /* __hpux */ - -/* ----------------------------------------------------------------------- */ -/* I R I X */ -/* ----------------------------------------------------------------------- */ -#ifdef __sgi -# undef MENTAT -# if IRIX < 60500 -typedef struct uio uio_t; -# endif -typedef int ioctlcmd_t; -typedef u_int32_t u_32_t; -# define U_32_T 1 - -# ifdef INET6 -# define USE_INET6 -# endif - -# define hz HZ -# include -# define IPF_LOCK_PL plhi -# include -# undef kmutex_t -typedef struct { - lock_t *l; - int pl; -} kmutex_t; - -# ifdef MUTEX_INIT -# define KMUTEX_T mutex_t -# else -# define KMUTEX_T kmutex_t -# define KRWLOCK_T kmutex_t -# endif - -# ifdef _KERNEL -# define NEED_LOCAL_RAND 1 -# define ipf_random arc4random -# define ATOMIC_INC(x) { MUTEX_ENTER(&ipf_rw); \ - (x)++; MUTEX_EXIT(&ipf_rw); } -# define ATOMIC_DEC(x) { MUTEX_ENTER(&ipf_rw); \ - (x)--; MUTEX_EXIT(&ipf_rw); } -# define USE_MUTEXES -# ifdef MUTEX_INIT -# include -# define ATOMIC_INCL(x) atomicAddUlong(&(x), 1) -# define ATOMIC_INC64(x) atomicAddUint64(&(x), 1) -# define ATOMIC_INC32(x) atomicAddUint(&(x), 1) -# define ATOMIC_INC16 ATOMIC_INC -# define ATOMIC_DECL(x) atomicAddUlong(&(x), -1) -# define ATOMIC_DEC64(x) atomicAddUint64(&(x), -1) -# define ATOMIC_DEC32(x) atomicAddUint(&(x), -1) -# define ATOMIC_DEC16 ATOMIC_DEC -# undef MUTEX_INIT -# define MUTEX_INIT(x, y) mutex_init(&(x)->ipf_lk, \ - MUTEX_DEFAULT, y) -# undef MUTEX_ENTER -# define MUTEX_ENTER(x) mutex_lock(&(x)->ipf_lk, 0) -# undef MUTEX_EXIT -# define MUTEX_EXIT(x) mutex_unlock(&(x)->ipf_lk) -# undef MUTEX_DESTROY -# define MUTEX_DESTROY(x) mutex_destroy(&(x)->ipf_lk) -# define MUTEX_DOWNGRADE(x) mrdemote(&(x)->ipf_lk) -# define KRWLOCK_T mrlock_t -# define RWLOCK_INIT(x, y) mrinit(&(x)->ipf_lk, y) -# undef RW_DESTROY -# define RW_DESTROY(x) mrfree(&(x)->ipf_lk) -# define READ_ENTER(x) RW_RDLOCK(&(x)->ipf_lk) -# define WRITE_ENTER(x) RW_WRLOCK(&(x)->ipf_lk) -# define RWLOCK_EXIT(x) RW_UNLOCK(&(x)->ipf_lk) -# else -# define READ_ENTER(x) MUTEX_ENTER(&(x)->ipf_lk) -# define WRITE_ENTER(x) MUTEX_ENTER(&(x)->ipf_lk) -# define MUTEX_DOWNGRADE(x) ; -# define RWLOCK_EXIT(x) MUTEX_EXIT(&(x)->ipf_lk) -# define MUTEX_EXIT(x) UNLOCK((x)->ipf_lk.l, (x)->ipf_lk.pl); -# define MUTEX_INIT(x,y) (x)->ipf_lk.l = LOCK_ALLOC((uchar_t)-1, IPF_LOCK_PL, (lkinfo_t *)-1, KM_NOSLEEP) -# define MUTEX_DESTROY(x) LOCK_DEALLOC((x)->ipf_lk.l) -# define MUTEX_ENTER(x) (x)->ipf_lk.pl = LOCK((x)->ipf_lk.l, \ - IPF_LOCK_PL); -# endif -# define MUTEX_NUKE(x) bzero((x), sizeof(*(x))) -# define FREE_MB_T(m) m_freem(m) -# define MTOD(m,t) mtod(m,t) -# define COPYIN(a,b,c) (bcopy((caddr_t)(a), (caddr_t)(b), (c)), 0) -# define COPYOUT(a,b,c) (bcopy((caddr_t)(a), (caddr_t)(b), (c)), 0) -# define UIOMOVE(a,b,c,d) uiomove((caddr_t)a,b,c,d) -# define SLEEP(id, n) sleep((id), PZERO+1) -# define WAKEUP(id,x) wakeup(id+x) -# define POLLWAKEUP(x) ; -# define KFREE(x) kmem_free((char *)(x), sizeof(*(x))) -# define KFREES(x,s) kmem_free((char *)(x), (s)) -# define GETIFP(n,v) ifunit(n) -# include -# include -# define KMALLOC(a,b) (a) = (b)kmem_alloc(sizeof(*(a)), KM_NOSLEEP) -# define KMALLOCS(a,b,c) (a) = (b)kmem_alloc((c), KM_NOSLEEP) -# define GET_MINOR(x) getminor(x) -# define USE_SPL 1 -# define SPL_IMP(x) (x) = splimp() -# define SPL_NET(x) (x) = splnet() -# define SPL_SCHED(x) (x) = splsched() -# define SPL_X(x) (void) splx(x) -extern void m_copydata __P((struct mbuf *, int, int, caddr_t)); -extern void m_copyback __P((struct mbuf *, int, int, caddr_t)); -# define MSGDSIZE(x) mbufchainlen(x) -# define M_LEN(x) (x)->m_len -# define M_DUPLICATE(x) m_copy((x), 0, M_COPYALL) -# define GETKTIME(x) microtime((struct timeval *)x) -# define IFNAME(x) ((struct ifnet *)x)->if_name -# define CACHE_HASH(x) ((IFNAME(fin->fin_ifp)[0] + \ - ((struct ifnet *)fin->fin_ifp)->if_unit) & 7) -# define IPF_PANIC(x,y) if (x) { printf y; panic("ipf_panic"); } -typedef struct mbuf mb_t; -# else -# undef RW_DESTROY -# undef MUTEX_INIT -# undef MUTEX_DESTROY -# endif /* _KERNEL */ - -# define OS_RECOGNISED 1 - -#endif /* __sgi */ - -/* ----------------------------------------------------------------------- */ -/* T R U 6 4 */ -/* ----------------------------------------------------------------------- */ -#ifdef __osf__ -# undef MENTAT - -# include -# include - -# ifdef _KERNEL -# define NEED_LOCAL_RAND 1 -# define ipf_random arc4random -# define KMUTEX_T simple_lock_data_t -# define KRWLOCK_T lock_data_t -# include -# define USE_MUTEXES -# define READ_ENTER(x) lock_read(&(x)->ipf_lk) -# define WRITE_ENTER(x) lock_write(&(x)->ipf_lk) -# define MUTEX_DOWNGRADE(x) lock_write_to_read(&(x)->ipf_lk) -# define RWLOCK_INIT(x, y) lock_init(&(x)->ipf_lk, TRUE) -# define RWLOCK_EXIT(x) lock_done(&(x)->ipf_lk) -# define RW_DESTROY(x) lock_terminate(&(x)->ipf_lk) -# define MUTEX_ENTER(x) simple_lock(&(x)->ipf_lk) -# define MUTEX_INIT(x, y) simple_lock_init(&(x)->ipf_lk) -# define MUTEX_DESTROY(x) simple_lock_terminate(&(x)->ipf_lk) -# define MUTEX_EXIT(x) simple_unlock(&(x)->ipf_lk) -# define MUTEX_NUKE(x) bzero(x, sizeof(*(x))) -# define ATOMIC_INC64(x) atomic_incq((uint64_t*)&(x)) -# define ATOMIC_DEC64(x) atomic_decq((uint64_t*)&(x)) -# define ATOMIC_INC32(x) atomic_incl((uint32_t*)&(x)) -# define ATOMIC_DEC32(x) atomic_decl((uint32_t*)&(x)) -# define ATOMIC_INC16(x) { simple_lock(&ipf_rw); (x)++; \ - simple_unlock(&ipf_rw); } -# define ATOMIC_DEC16(x) { simple_lock(&ipf_rw); (x)--; \ - simple_unlock(&ipf_rw); } -# define ATOMIC_INCL(x) atomic_incl((uint32_t*)&(x)) -# define ATOMIC_DECL(x) atomic_decl((uint32_t*)&(x)) -# define ATOMIC_INC(x) { simple_lock(&ipf_rw); (x)++; \ - simple_unlock(&ipf_rw); } -# define ATOMIC_DEC(x) { simple_lock(&ipf_rw); (x)--; \ - simple_unlock(&ipf_rw); } -# define SPL_SCHED(x) ; -# define SPL_NET(x) ; -# define SPL_IMP(x) ; -# undef SPL_X -# define SPL_X(x) ; -# define UIOMOVE(a,b,c,d) uiomove((caddr_t)a, b, d) -# define FREE_MB_T(m) m_freem(m) -# define MTOD(m,t) mtod(m,t) -# define GETIFP(n, v) ifunit(n) -# define GET_MINOR getminor -# define WAKEUP(id,x) wakeup(id + x) -# define POLLWAKEUP(x) ; -# define COPYIN(a,b,c) copyin((caddr_t)(a), (caddr_t)(b), (c)) -# define COPYOUT(a,b,c) copyout((caddr_t)(a), (caddr_t)(b), (c)) -# define KMALLOC(a, b) MALLOC((a), b, sizeof(*(a)), M_PFILT, M_NOWAIT) -# define KMALLOCS(a, b, c) MALLOC((a), b, (c), M_PFILT, \ - ((c) > 4096) ? M_WAITOK : M_NOWAIT) -# define KFREE(x) FREE((x), M_PFILT) -# define KFREES(x,s) FREE((x), M_PFILT) -# define MSGDSIZE(x) mbufchainlen(x) -# define M_LEN(x) (x)->m_len -# define M_DUPLICATE(x) m_copy((x), 0, M_COPYALL) -# define GETKTIME(x) microtime((struct timeval *)x) -# define IFNAME(x) ((struct ifnet *)x)->if_name -# define CACHE_HASH(x) ((IFNAME(fin->fin_ifp)[0] + \ - ((struct ifnet *)fin->fin_ifp)->if_unit) & 7) -# define IPF_PANIC(x,y) if (x) { printf y; panic("ipf_panic"); } -typedef struct mbuf mb_t; -# endif /* _KERNEL */ - -# if (defined(_KERNEL) || defined(_NO_BITFIELDS) || (__STDC__ == 1)) -# define IP_V(x) ((x)->ip_vhl >> 4) -# define IP_HL(x) ((x)->ip_vhl & 0xf) -# define IP_V_A(x,y) (x)->ip_vhl |= (((y) << 4) & 0xf0) -# define IP_HL_A(x,y) (x)->ip_vhl |= ((y) & 0xf) -# define TCP_X2(x) ((x)->th_xoff & 0xf) -# define TCP_X2_A(x,y) (x)->th_xoff |= ((y) & 0xf) -# define TCP_OFF(x) ((x)->th_xoff >> 4) -# define TCP_OFF_A(x,y) (x)->th_xoff |= (((y) << 4) & 0xf0) -# endif - -/* - * These are from's Solaris' #defines for little endian. - */ -#define IP6F_MORE_FRAG 0x0100 -#define IP6F_RESERVED_MASK 0x0600 -#define IP6F_OFF_MASK 0xf8ff - -struct ip6_ext { - u_char ip6e_nxt; - u_char ip6e_len; -}; - -typedef int ioctlcmd_t; -/* - * Really, any arch where sizeof(long) != sizeof(int). - */ -typedef unsigned int u_32_t; -# define U_32_T 1 - -# define OS_RECOGNISED 1 -#endif /* __osf__ */ - -/* ----------------------------------------------------------------------- */ -/* N E T B S D */ -/* ----------------------------------------------------------------------- */ -#ifdef __NetBSD__ -# if (NetBSD >= 199905) && !defined(IPFILTER_LKM) && defined(_KERNEL) -# include "opt_ipfilter.h" -# endif -# if defined(_KERNEL) -# include -# else -# include -# endif -# if defined(_KERNEL) && !defined(IPFILTER_LKM) -# include "bpfilter.h" -# if defined(__NetBSD_Version__) && (__NetBSD_Version__ >= 104110000) -# include "opt_inet.h" -# endif -# ifdef INET6 -# define USE_INET6 -# endif -# if (__NetBSD_Version__ >= 105000000) -# define HAVE_M_PULLDOWN 1 -# endif -# endif - -# if (__NetBSD_Version__ >= 499000000) -typedef char * caddr_t; -# endif - -# define ipf_random arc4random - -# ifdef _KERNEL -# if (__NetBSD_Version__ >= 399001400) -# define KMALLOCS(a, b, c) (a) = (b)malloc((c), _M_IPF, M_NOWAIT) -# endif -# define MSGDSIZE(x) mbufchainlen(x) -# define M_LEN(x) (x)->m_len -# define M_DUPLICATE(x) m_copy((x), 0, M_COPYALL) -# define GETKTIME(x) microtime((struct timeval *)x) -# define IPF_PANIC(x,y) if (x) { printf y; panic("ipf_panic"); } -# define COPYIN(a,b,c) copyin((caddr_t)(a), (caddr_t)(b), (c)) -# define COPYOUT(a,b,c) copyout((caddr_t)(a), (caddr_t)(b), (c)) -typedef struct mbuf mb_t; -# endif /* _KERNEL */ -# if (NetBSD <= 1991011) && (NetBSD >= 199606) -# define IFNAME(x) ((struct ifnet *)x)->if_xname -# define COPYIFNAME(v, x, b) \ - (void) strncpy(b, \ - ((struct ifnet *)x)->if_xname, \ - LIFNAMSIZ) -# define CACHE_HASH(x) ((((struct ifnet *)fin->fin_ifp)->if_index)&7) -# else -# define IFNAME(x) ((struct ifnet *)x)->if_name -# define CACHE_HASH(x) ((IFNAME(fin->fin_ifp)[0] + \ - ((struct ifnet *)fin->fin_ifp)->if_unit) & 7) -# endif -typedef struct uio uio_t; -typedef u_long ioctlcmd_t; -typedef int minor_t; -typedef u_int32_t u_32_t; -# define U_32_T 1 - -# define OS_RECOGNISED 1 -#endif /* __NetBSD__ */ +#define NETBSD_GE_REV(x) (defined(__NetBSD_Version__) && \ + (__NetBSD_Version__ >= (x))) +#define NETBSD_GT_REV(x) (defined(__NetBSD_Version__) && \ + (__NetBSD_Version__ > (x))) +#define NETBSD_LT_REV(x) (defined(__NetBSD_Version__) && \ + (__NetBSD_Version__ < (x))) +#define FREEBSD_GE_REV(x) (defined(__FreeBSD_version) && \ + (__FreeBSD_version >= (x))) +#define FREEBSD_GT_REV(x) (defined(__FreeBSD_version) && \ + (__FreeBSD_version > (x))) +#define FREEBSD_LT_REV(x) (defined(__FreeBSD_version) && \ + (__FreeBSD_version < (x))) +#define BSDOS_GE_REV(x) (defined(_BSDI_VERSION) && \ + (_BSDI_VERSION >= (x))) +#define BSDOS_GT_REV(x) (defined(_BSDI_VERSION) && \ + (_BSDI_VERSION > (x))) +#define BSDOS_LT_REV(x) (defined(_BSDI_VERSION) && \ + (_BSDI_VERSION < (x))) +#define OPENBSD_GE_REV(x) (defined(OpenBSD) && (OpenBSD >= (x))) +#define OPENBSD_GT_REV(x) (defined(OpenBSD) && (OpenBSD > (x))) +#define OPENBSD_LT_REV(x) (defined(OpenBSD) && (OpenBSD < (x))) +#define BSD_GE_YEAR(x) (defined(BSD) && (BSD >= (x))) +#define BSD_GT_YEAR(x) (defined(BSD) && (BSD > (x))) +#define BSD_LT_YEAR(x) (defined(BSD) && (BSD < (x))) /* ----------------------------------------------------------------------- */ /* F R E E B S D */ /* ----------------------------------------------------------------------- */ -#ifdef __FreeBSD__ -# if (__FreeBSD_version < 400000) -# define NEED_LOCAL_RAND 1 -# else -# define ipf_random arc4random -# endif +# define HAS_SYS_MD5_H 1 # if defined(_KERNEL) -# if (__FreeBSD_version >= 500000) # include "opt_bpf.h" -# else -# include "bpf.h" -# endif -# if defined(__FreeBSD_version) && (__FreeBSD_version >= 400000) # include "opt_inet6.h" -# endif # if defined(INET6) && !defined(USE_INET6) # define USE_INET6 # endif # endif # if defined(_KERNEL) -# if (__FreeBSD_version >= 400000) +# include +# define p_cred td_ucred +# define p_uid td_ucred->cr_ruid + /* * When #define'd, the 5.2.1 kernel panics when used with the ftp proxy. * There may be other, safe, kernels but this is not extensively tested yet. */ # define HAVE_M_PULLDOWN -# endif # if !defined(IPFILTER_LKM) && (__FreeBSD_version >= 300000) # include "opt_ipfilter.h" # endif # define COPYIN(a,b,c) copyin((caddr_t)(a), (caddr_t)(b), (c)) # define COPYOUT(a,b,c) copyout((caddr_t)(a), (caddr_t)(b), (c)) -# if (__FreeBSD_version >= 500043) # define NETBSD_PF -# endif +# else +# include # endif /* _KERNEL */ -# if (__FreeBSD_version >= 500043) +# include # include -# if (__FreeBSD_version > 700014) +# define KRWLOCK_FILL_SZ 56 +# define KMUTEX_FILL_SZ 56 # include -# define KRWLOCK_T struct rwlock -# ifdef _KERNEL -# define READ_ENTER(x) rw_rlock(&(x)->ipf_lk) -# define WRITE_ENTER(x) rw_wlock(&(x)->ipf_lk) -# define MUTEX_DOWNGRADE(x) rw_downgrade(&(x)->ipf_lk) -# define RWLOCK_INIT(x, y) rw_init(&(x)->ipf_lk, (y)) -# define RW_DESTROY(x) rw_destroy(&(x)->ipf_lk) -# define RWLOCK_EXIT(x) do { \ +# define KMUTEX_T struct mtx +# define KRWLOCK_T struct rwlock +# ifdef _KERNEL +# define READ_ENTER(x) rw_rlock(&(x)->ipf_lk) +# define WRITE_ENTER(x) rw_wlock(&(x)->ipf_lk) +# define MUTEX_DOWNGRADE(x) rw_downgrade(&(x)->ipf_lk) +# define RWLOCK_INIT(x,y) rw_init(&(x)->ipf_lk, (y)) +# define RW_DESTROY(x) rw_destroy(&(x)->ipf_lk) +# define RWLOCK_EXIT(x) do { \ if (rw_wowned(&(x)->ipf_lk)) \ - rw_wunlock(&(x)->ipf_lk); \ - else \ + rw_wunlock(&(x)->ipf_lk); \ + else \ rw_runlock(&(x)->ipf_lk); \ } while (0) # endif -# else -# include -/* - * Whilst the sx(9) locks on FreeBSD have the right semantics and interface - * for what we want to use them for, despite testing showing they work - - * with a WITNESS kernel, it generates LOR messages. - */ -# ifdef _KERNEL -# if (__FreeBSD_version < 700000) -# define KRWLOCK_T struct mtx -# define READ_ENTER(x) mtx_lock(&(x)->ipf_lk) -# define WRITE_ENTER(x) mtx_lock(&(x)->ipf_lk) -# define RWLOCK_EXIT(x) mtx_unlock(&(x)->ipf_lk) -# define MUTEX_DOWNGRADE(x) ; -# define RWLOCK_INIT(x,y) mtx_init(&(x)->ipf_lk, (y), NULL,\ - MTX_DEF) -# define RW_DESTROY(x) mtx_destroy(&(x)->ipf_lk) -# else -# define KRWLOCK_T struct sx -# define READ_ENTER(x) sx_slock(&(x)->ipf_lk) -# define WRITE_ENTER(x) sx_xlock(&(x)->ipf_lk) -# define MUTEX_DOWNGRADE(x) sx_downgrade(&(x)->ipf_lk) -# define RWLOCK_INIT(x, y) sx_init(&(x)->ipf_lk, (y)) -# define RW_DESTROY(x) sx_destroy(&(x)->ipf_lk) -# ifdef sx_unlock -# define RWLOCK_EXIT(x) sx_unlock(&(x)->ipf_lk) -# else -# define RWLOCK_EXIT(x) do { \ - if ((x)->ipf_lk.sx_cnt < 0) \ - sx_xunlock(&(x)->ipf_lk); \ - else \ - sx_sunlock(&(x)->ipf_lk); \ - } while (0) -# endif -# endif -# endif -# endif -# define KMUTEX_T struct mtx -# endif -# if (__FreeBSD_version >= 501113) # include # define IFNAME(x) ((struct ifnet *)x)->if_xname # define COPYIFNAME(v, x, b) \ (void) strncpy(b, \ ((struct ifnet *)x)->if_xname, \ LIFNAMSIZ) -# endif -# if (__FreeBSD_version >= 500043) -# define CACHE_HASH(x) ((((struct ifnet *)fin->fin_ifp)->if_index) & 7) -# else -# define IFNAME(x) ((struct ifnet *)x)->if_name -# define CACHE_HASH(x) ((IFNAME(fin->fin_ifp)[0] + \ - ((struct ifnet *)fin->fin_ifp)->if_unit) & 7) -# endif # ifdef _KERNEL # define GETKTIME(x) microtime((struct timeval *)x) -# if (__FreeBSD_version >= 500002) # include # include # include -# endif -# if (__FreeBSD_version >= 500043) # define USE_MUTEXES # define MUTEX_ENTER(x) mtx_lock(&(x)->ipf_lk) # define MUTEX_EXIT(x) mtx_unlock(&(x)->ipf_lk) @@ -958,460 +205,47 @@ typedef u_int32_t u_32_t; MTX_DEF) # define MUTEX_DESTROY(x) mtx_destroy(&(x)->ipf_lk) # define MUTEX_NUKE(x) bzero((x), sizeof(*(x))) +/* + * Whilst the sx(9) locks on FreeBSD have the right semantics and interface + * for what we want to use them for, despite testing showing they work - + * with a WITNESS kernel, it generates LOR messages. + */ # include -# define ATOMIC_INC(x) { mtx_lock(&ipf_rw.ipf_lk); (x)++; \ - mtx_unlock(&ipf_rw.ipf_lk); } -# define ATOMIC_DEC(x) { mtx_lock(&ipf_rw.ipf_lk); (x)--; \ - mtx_unlock(&ipf_rw.ipf_lk); } +# define ATOMIC_INC(x) { mtx_lock(&softc->ipf_rw.ipf_lk); (x)++; \ + mtx_unlock(&softc->ipf_rw.ipf_lk); } +# define ATOMIC_DEC(x) { mtx_lock(&softc->ipf_rw.ipf_lk); (x)--; \ + mtx_unlock(&softc->ipf_rw.ipf_lk); } # define ATOMIC_INCL(x) atomic_add_long(&(x), 1) # define ATOMIC_INC64(x) ATOMIC_INC(x) # define ATOMIC_INC32(x) atomic_add_32((u_int *)&(x), 1) -# define ATOMIC_INC16(x) atomic_add_16(&(x), 1) # define ATOMIC_DECL(x) atomic_add_long(&(x), -1) # define ATOMIC_DEC64(x) ATOMIC_DEC(x) # define ATOMIC_DEC32(x) atomic_add_32((u_int *)&(x), -1) -# define ATOMIC_DEC16(x) atomic_add_16(&(x), -1) # define SPL_X(x) ; # define SPL_NET(x) ; # define SPL_IMP(x) ; # define SPL_SCHED(x) ; -# else -# define SPL_SCHED(x) x = splhigh() -# endif /* __FreeBSD_version >= 500043 */ -# define MSGDSIZE(x) mbufchainlen(x) -# define M_LEN(x) (x)->m_len -# define M_DUPLICATE(x) m_copy((x), 0, M_COPYALL) +# define GET_MINOR dev2unit +# define MSGDSIZE(m) mbufchainlen(m) +# define M_LEN(m) (m)->m_len +# define M_ADJ(m,x) m_adj(m, x) +# define M_COPY(x) m_copy((x), 0, M_COPYALL) +# define M_DUP(m) m_dup(m, M_NOWAIT) # define IPF_PANIC(x,y) if (x) { printf y; panic("ipf_panic"); } typedef struct mbuf mb_t; # endif /* _KERNEL */ -# if __FreeBSD_version < 300000 -# include -# else -# if __FreeBSD_version < 400000 -# if defined(IPFILTER_LKM) && !defined(ACTUALLY_LKM_NOT_KERNEL) -# define ACTUALLY_LKM_NOT_KERNEL -# endif -# endif -# endif -# if (__FreeBSD_version >= 300000) typedef u_long ioctlcmd_t; -# else -typedef int ioctlcmd_t; -# endif typedef struct uio uio_t; typedef int minor_t; typedef u_int32_t u_32_t; # define U_32_T 1 -# define OS_RECOGNISED 1 -#endif /* __FreeBSD__ */ - - -/* ----------------------------------------------------------------------- */ -/* O P E N B S D */ -/* ----------------------------------------------------------------------- */ -#ifdef __OpenBSD__ -# ifdef INET6 -# define USE_INET6 -# endif - -# ifdef _KERNEL -# if !defined(IPFILTER_LKM) -# include "bpfilter.h" -# endif -# if (OpenBSD >= 200311) -# define SNPRINTF snprintf -# if defined(USE_INET6) -# include "netinet6/in6_var.h" -# include "netinet6/nd6.h" -# endif -# endif -# if (OpenBSD >= 200012) -# define HAVE_M_PULLDOWN 1 -# endif -# define COPYIN(a,b,c) copyin((caddr_t)(a), (caddr_t)(b), (c)) -# define COPYOUT(a,b,c) copyout((caddr_t)(a), (caddr_t)(b), (c)) -# define GETKTIME(x) microtime((struct timeval *)x) -# define MSGDSIZE(x) mbufchainlen(x) -# define M_LEN(x) (x)->m_len -# define M_DUPLICATE(x) m_copy((x), 0, M_COPYALL) -# define IPF_PANIC(x,y) if (x) { printf y; panic("ipf_panic"); } -typedef struct mbuf mb_t; -# endif /* _KERNEL */ -# if (OpenBSD >= 199603) -# define IFNAME(x, b) ((struct ifnet *)x)->if_xname -# define COPYIFNAME(v, x, b) \ - (void) strncpy(b, \ - ((struct ifnet *)x)->if_xname, \ - LIFNAMSIZ) -# define CACHE_HASH(x) ((((struct ifnet *)fin->fin_ifp)->if_index)&7) -# else -# define IFNAME(x, b) ((struct ifnet *)x)->if_name -# define CACHE_HASH(x) ((IFNAME(fin->fin_ifp)[0] + \ - ((struct ifnet *)fin->fin_ifp)->if_unit) & 7) -# endif - -typedef struct uio uio_t; -typedef u_long ioctlcmd_t; -typedef int minor_t; -typedef u_int32_t u_32_t; -# define U_32_T 1 - -# define OS_RECOGNISED 1 -#endif /* __OpenBSD__ */ - - -/* ----------------------------------------------------------------------- */ -/* B S D O S */ -/* ----------------------------------------------------------------------- */ -#ifdef _BSDI_VERSION -# ifdef INET6 -# define USE_INET6 -# endif - -# ifdef _KERNEL -# define GETKTIME(x) microtime((struct timeval *)x) -# define MSGDSIZE(x) mbufchainlen(x) -# define M_LEN(x) (x)->m_len -# define M_DUPLICATE(x) m_copy((x), 0, M_COPYALL) -# define IFNAME(x, b) ((struct ifnet *)x)->if_name -# define CACHE_HASH(x) ((IFNAME(fin->fin_ifp)[0] + \ - ((struct ifnet *)fin->fin_ifp)->if_unit) & 7) -typedef struct mbuf mb_t; -# endif /* _KERNEL */ - -# if (_BSDI_VERSION >= 199701) -typedef u_long ioctlcmd_t; -# else -typedef int ioctlcmd_t; -# endif -typedef u_int32_t u_32_t; -# define U_32_T 1 - -#endif /* _BSDI_VERSION */ - - -/* ----------------------------------------------------------------------- */ -/* S U N O S 4 */ -/* ----------------------------------------------------------------------- */ -#if defined(sun) && !defined(OS_RECOGNISED) /* SunOS4 */ -# ifdef _KERNEL -# include -# define GETKTIME(x) uniqtime((struct timeval *)x) -# define MSGDSIZE(x) mbufchainlen(x) -# define M_LEN(x) (x)->m_len -# define M_DUPLICATE(x) m_copy((x), 0, M_COPYALL) -# define IFNAME(x, b) ((struct ifnet *)x)->if_name -# define CACHE_HASH(x) ((IFNAME(fin->fin_ifp)[0] + \ - ((struct ifnet *)fin->fin_ifp)->if_unit) & 7) -# define GETIFP(n, v) ifunit(n, IFNAMSIZ) -# define KFREE(x) kmem_free((char *)(x), sizeof(*(x))) -# define KFREES(x,s) kmem_free((char *)(x), (s)) -# define SLEEP(id, n) sleep((id), PZERO+1) -# define WAKEUP(id,x) wakeup(id + x) -# define POLLWAKEUP(x) ; -# define UIOMOVE(a,b,c,d) uiomove((caddr_t)a,b,c,d) -# define IPF_PANIC(x,y) if (x) { printf y; panic("ipf_panic"); } - -extern void m_copydata __P((struct mbuf *, int, int, caddr_t)); -extern void m_copyback __P((struct mbuf *, int, int, caddr_t)); - -typedef struct mbuf mb_t; -# endif - -typedef struct uio uio_t; -typedef int ioctlcmd_t; -typedef int minor_t; -typedef unsigned int u_32_t; -# define U_32_T 1 - -# define OS_RECOGNISED 1 - -#endif /* SunOS 4 */ - -/* ----------------------------------------------------------------------- */ -/* L I N U X */ -/* ----------------------------------------------------------------------- */ -#if defined(linux) && !defined(OS_RECOGNISED) -#include -#include -# if (LINUX >= 20600) && defined(_KERNEL) -# define HDR_T_PRIVATE 1 -# endif -# undef USE_INET6 -# ifdef USE_INET6 -struct ip6_ext { - u_char ip6e_nxt; - u_char ip6e_len; -}; -# endif - -# ifdef _KERNEL -# define IPF_PANIC(x,y) if (x) { printf y; panic("ipf_panic"); } -# define COPYIN(a,b,c) copy_from_user((caddr_t)(b), (caddr_t)(a), (c)) -# define COPYOUT(a,b,c) copy_to_user((caddr_t)(b), (caddr_t)(a), (c)) -# define FREE_MB_T(m) kfree_skb(m) -# define GETKTIME(x) do_gettimeofday((struct timeval *)x) -# define POLLWAKEUP(x) ; -# ifdef wait_event_interruptible -# define SLEEP(x,s) wait_event_interruptible((*(x##_linux)), 0) -# else -# define SLEEP(x,s) 0, interruptible_sleep_on(x##_linux) -# endif -# define WAKEUP(x,y) wake_up(x##_linux + y) -# define UIOMOVE(a,b,c,d) uiomove((caddr_t)a,b,c,d) -# define USE_MUTEXES -# define KRWLOCK_T rwlock_t -# define KMUTEX_T spinlock_t -# define MUTEX_INIT(x,y) spin_lock_init(&(x)->ipf_lk) -# define MUTEX_ENTER(x) spin_lock(&(x)->ipf_lk) -# define MUTEX_EXIT(x) spin_unlock(&(x)->ipf_lk) -# define MUTEX_DESTROY(x) do { } while (0) -# define MUTEX_NUKE(x) bzero(&(x)->ipf_lk, sizeof((x)->ipf_lk)) -# define READ_ENTER(x) ipf_read_enter(x) -# define WRITE_ENTER(x) ipf_write_enter(x) -# define RWLOCK_INIT(x,y) ipf_rw_init(x, y) -# define RW_DESTROY(x) do { } while (0) -# define RWLOCK_EXIT(x) ipf_rw_exit(x) -# define MUTEX_DOWNGRADE(x) ipf_rw_downgrade(x) -# define ATOMIC_INCL(x) MUTEX_ENTER(&ipf_rw); (x)++; \ - MUTEX_EXIT(&ipf_rw) -# define ATOMIC_DECL(x) MUTEX_ENTER(&ipf_rw); (x)--; \ - MUTEX_EXIT(&ipf_rw) -# define ATOMIC_INC64(x) MUTEX_ENTER(&ipf_rw); (x)++; \ - MUTEX_EXIT(&ipf_rw) -# define ATOMIC_INC32(x) MUTEX_ENTER(&ipf_rw); (x)++; \ - MUTEX_EXIT(&ipf_rw) -# define ATOMIC_INC16(x) MUTEX_ENTER(&ipf_rw); (x)++; \ - MUTEX_EXIT(&ipf_rw) -# define ATOMIC_DEC64(x) MUTEX_ENTER(&ipf_rw); (x)--; \ - MUTEX_EXIT(&ipf_rw) -# define ATOMIC_DEC32(x) MUTEX_ENTER(&ipf_rw); (x)--; \ - MUTEX_EXIT(&ipf_rw) -# define ATOMIC_DEC16(x) MUTEX_ENTER(&ipf_rw); (x)--; \ - MUTEX_EXIT(&ipf_rw) -# define SPL_SCHED(x) do { } while (0) -# define SPL_IMP(x) do { } while (0) -# define SPL_NET(x) do { } while (0) -# define SPL_X(x) do { } while (0) -# define IFNAME(x) ((struct net_device*)x)->name -# define CACHE_HASH(x) ((IFNAME(fin->fin_ifp)[0] + \ - ((struct net_device *)fin->fin_ifp)->ifindex) & 7) -typedef struct sk_buff mb_t; -extern void m_copydata __P((mb_t *, int, int, caddr_t)); -extern void m_copyback __P((mb_t *, int, int, caddr_t)); -extern void m_adj __P((mb_t *, int)); -extern mb_t *m_pullup __P((mb_t *, int)); -# define mbuf sk_buff - -# define mtod(m, t) ((t)(m)->data) -# define m_data data -# define m_len len -# define m_next next -# define M_DUPLICATE(m) skb_clone((m), in_interrupt() ? GFP_ATOMIC : \ - GFP_KERNEL) -# define MSGDSIZE(m) (m)->len -# define M_LEN(m) (m)->len - -# define splnet(x) ; -# define printf printk -# define bcopy(s,d,z) memmove(d, s, z) -# define bzero(s,z) memset(s, 0, z) -# define bcmp(a,b,z) memcmp(a, b, z) - -# define ifnet net_device -# define if_xname name -# define if_unit ifindex - -# define KMALLOC(x,t) (x) = (t)kmalloc(sizeof(*(x)), \ - in_interrupt() ? GFP_ATOMIC : GFP_KERNEL) -# define KFREE(x) kfree(x) -# define KMALLOCS(x,t,s) (x) = (t)kmalloc((s), \ - in_interrupt() ? GFP_ATOMIC : GFP_KERNEL) -# define KFREES(x,s) kfree(x) - -# define GETIFP(n,v) dev_get_by_name(n) - -# else -# include - -struct mbuf { -}; - -# ifndef _NET_ROUTE_H -struct rtentry { -}; -# endif - -struct ifnet { - char if_xname[IFNAMSIZ]; - int if_unit; - int (* if_output) __P((struct ifnet *, struct mbuf *, struct sockaddr *, struct rtentry *)); - struct ifaddr *if_addrlist; -}; -# define IFNAME(x) ((struct ifnet *)x)->if_xname - -# endif /* _KERNEL */ - -# define COPYIFNAME(v, x, b) \ - (void) strncpy(b, \ - ((struct ifnet *)x)->if_xname, \ - LIFNAMSIZ) - -# include -# define FWRITE FMODE_WRITE -# define FREAD FMODE_READ - -# define __USE_MISC 1 -# define __FAVOR_BSD 1 - -typedef struct uio { - struct iovec *uio_iov; - void *uio_file; - char *uio_buf; - int uio_iovcnt; - int uio_offset; - size_t uio_resid; - int uio_rw; -} uio_t; - -extern int uiomove __P((caddr_t, size_t, int, struct uio *)); - -# define UIO_READ 1 -# define UIO_WRITE 2 - -typedef u_long ioctlcmd_t; -typedef int minor_t; -typedef u_int32_t u_32_t; -# define U_32_T 1 - -# define OS_RECOGNISED 1 - -#endif - - -/* ----------------------------------------------------------------------- */ -/* A I X */ -/* ----------------------------------------------------------------------- */ -#if defined(_AIX51) -# undef MENTAT - -# include -# include - -# ifdef _KERNEL -# define rw_read_locked(x) 0 -# include -# include -# define KMUTEX_T simple_lock_t -# define KRWLOCK_T complex_lock_t -# define USE_MUTEXES 1 -# define USE_SPL 1 -# define READ_ENTER(x) lock_read((x)->ipf_lk) -# define WRITE_ENTER(x) lock_write((x)->ipf_lk) -# define MUTEX_DOWNGRADE(x) lock_write_to_read((x)->ipf_lk) -# define RWLOCK_INIT(x, y) lock_alloc(&(x)->ipf_lk, \ - LOCK_ALLOC_PIN, \ - (u_short)y, 0); \ - lock_init((x)->ipf_lk, TRUE) -# define RWLOCK_EXIT(x) lock_done((x)->ipf_lk) -# define RW_DESTROY(x) lock_free(&(x)->ipf_lk) -# define MUTEX_ENTER(x) simple_lock((x)->ipf_lk) -# define MUTEX_INIT(x, y) lock_alloc(&(x)->ipf_lk, \ - LOCK_ALLOC_PIN, \ - (u_short)y, 0); \ - simple_lock_init((x)->ipf_lk) -# define MUTEX_DESTROY(x) lock_free(&(x)->ipf_lk) -# define MUTEX_EXIT(x) simple_unlock((x)->ipf_lk) -# define MUTEX_NUKE(x) bzero(&(x)->ipf_lk, sizeof((x)->ipf_lk)) -# define ATOMIC_INC64(x) { MUTEX_ENTER(&ipf_rw); (x)++; \ - MUTEX_EXIT(&ipf_rw); } -# define ATOMIC_DEC64(x) { MUTEX_ENTER(&ipf_rw); (x)--; \ - MUTEX_EXIT(&ipf_rw); } -# define ATOMIC_INC32(x) { MUTEX_ENTER(&ipf_rw); (x)++; \ - MUTEX_EXIT(&ipf_rw); } -# define ATOMIC_DEC32(x) { MUTEX_ENTER(&ipf_rw); (x)--; \ - MUTEX_EXIT(&ipf_rw); } -# define ATOMIC_INCL(x) { MUTEX_ENTER(&ipf_rw); (x)++; \ - MUTEX_EXIT(&ipf_rw); } -# define ATOMIC_DECL(x) { MUTEX_ENTER(&ipf_rw); (x)--; \ - MUTEX_EXIT(&ipf_rw); } -# define ATOMIC_INC(x) { MUTEX_ENTER(&ipf_rw); (x)++; \ - MUTEX_EXIT(&ipf_rw); } -# define ATOMIC_DEC(x) { MUTEX_ENTER(&ipf_rw); (x)--; \ - MUTEX_EXIT(&ipf_rw); } -# define SPL_SCHED(x) x = splsched() -# define SPL_NET(x) x = splnet() -# define SPL_IMP(x) x = splimp() -# undef SPL_X -# define SPL_X(x) splx(x) -# define UIOMOVE(a,b,c,d) uiomove((caddr_t)a,b,c,d) -extern void* getifp __P((char *, int)); -# define GETIFP(n, v) getifp(n, v) -# define GET_MINOR minor -# define SLEEP(id, n) sleepx((id), PZERO+1, 0) -# define WAKEUP(id,x) wakeup(id) -# define POLLWAKEUP(x) ; -# define COPYIN(a,b,c) copyin((caddr_t)(a), (caddr_t)(b), (c)) -# define COPYOUT(a,b,c) copyout((caddr_t)(a), (caddr_t)(b), (c)) -# define KMALLOC(a, b) MALLOC((a), b, sizeof(*(a)), M_TEMP, M_NOWAIT) -# define KMALLOCS(a, b, c) MALLOC((a), b, (c), M_TEMP, \ - ((c) > 4096) ? M_WAITOK : M_NOWAIT) -# define KFREE(x) FREE((x), M_TEMP) -# define KFREES(x,s) FREE((x), M_TEMP) -# define MSGDSIZE(x) mbufchainlen(x) -# define M_LEN(x) (x)->m_len -# define M_DUPLICATE(x) m_copy((x), 0, M_COPYALL) -# define GETKTIME(x) -# define IFNAME(x, b) ((struct ifnet *)x)->if_name -# define CACHE_HASH(x) ((IFNAME(fin->fin_ifp)[0] + \ - ((struct ifnet *)fin->fin_ifp)->if_unit) & 7) -# define IPF_PANIC(x,y) -typedef struct mbuf mb_t; -# endif /* _KERNEL */ - -/* - * These are from's Solaris' #defines for little endian. - */ -#if !defined(IP6F_MORE_FRAG) -# define IP6F_MORE_FRAG 0x0100 -#endif -#if !defined(IP6F_RESERVED_MASK) -# define IP6F_RESERVED_MASK 0x0600 -#endif -#if !defined(IP6F_OFF_MASK) -# define IP6F_OFF_MASK 0xf8ff -#endif - -struct ip6_ext { - u_char ip6e_nxt; - u_char ip6e_len; -}; - -typedef int ioctlcmd_t; -typedef int minor_t; -/* - * Really, any arch where sizeof(long) != sizeof(int). - */ -typedef unsigned int u_32_t; -# define U_32_T 1 - -# define OS_RECOGNISED 1 -#endif /* _AIX51 */ - - -#ifndef OS_RECOGNISED -#error ip_compat.h does not recognise this platform/OS. -#endif - /* ----------------------------------------------------------------------- */ /* G E N E R I C */ /* ----------------------------------------------------------------------- */ -#ifndef OS_RECOGNISED -#endif /* * For BSD kernels, if bpf is in the kernel, enable ipfilter to use bpf in @@ -1427,15 +261,21 @@ typedef unsigned int u_32_t; /* * Userland locking primitives */ +#ifndef _KERNEL +#if !defined(KMUTEX_FILL_SZ) +# define KMUTEX_FILL_SZ 1 +#endif +#if !defined(KRWLOCK_FILL_SZ) +# define KRWLOCK_FILL_SZ 1 +#endif +#endif + typedef struct { char *eMm_owner; char *eMm_heldin; u_int eMm_magic; int eMm_held; int eMm_heldat; -#if defined(__hpux) || defined(__linux) - char eMm_fill[8]; -#endif } eMmutex_t; typedef struct { @@ -1445,26 +285,25 @@ typedef struct { short eMrw_read; short eMrw_write; int eMrw_heldat; -#ifdef __hpux - char eMm_fill[24]; -#endif } eMrwlock_t; typedef union { + char _fill[KMUTEX_FILL_SZ]; #ifdef KMUTEX_T struct { KMUTEX_T ipf_slk; - char *ipf_lname; + const char *ipf_lname; } ipf_lkun_s; #endif eMmutex_t ipf_emu; } ipfmutex_t; typedef union { + char _fill[KRWLOCK_FILL_SZ]; #ifdef KRWLOCK_T struct { KRWLOCK_T ipf_slk; - char *ipf_lname; + const char *ipf_lname; int ipf_sr; int ipf_sw; u_int ipf_magic; @@ -1488,14 +327,12 @@ typedef union { # define INLINE __inline__ #endif -#if defined(linux) && defined(_KERNEL) -extern void ipf_read_enter __P((ipfrwlock_t *)); -extern void ipf_write_enter __P((ipfrwlock_t *)); -extern void ipf_rw_exit __P((ipfrwlock_t *)); -extern void ipf_rw_init __P((ipfrwlock_t *, char *)); -extern void ipf_rw_downgrade __P((ipfrwlock_t *)); +#if defined(__FreeBSD_version) && defined(_KERNEL) + CTASSERT(sizeof(ipfrwlock_t) == KRWLOCK_FILL_SZ); + CTASSERT(sizeof(ipfmutex_t) == KMUTEX_FILL_SZ); #endif + /* * In a non-kernel environment, there are a lot of macros that need to be * filled in to be null-ops or to point to some compatibility function, @@ -1504,18 +341,40 @@ extern void ipf_rw_downgrade __P((ipfrwlock_t *)); #ifndef _KERNEL typedef struct mb_s { struct mb_s *mb_next; + char *mb_data; + void *mb_ifp; int mb_len; + int mb_flags; u_long mb_buf[2048]; } mb_t; # undef m_next # define m_next mb_next -# define MSGDSIZE(x) (x)->mb_len /* XXX - from ipt.c */ -# define M_LEN(x) (x)->mb_len -# define M_DUPLICATE(x) (x) +# undef m_len +# define m_len mb_len +# undef m_flags +# define m_flags mb_flags +# undef m_data +# define m_data mb_data +# undef M_MCAST +# define M_MCAST 0x01 +# undef M_BCAST +# define M_BCAST 0x02 +# undef M_MBCAST +# define M_MBCAST 0x04 +# define MSGDSIZE(m) msgdsize(m) +# define M_LEN(m) (m)->mb_len +# define M_ADJ(m,x) (m)->mb_len += x +# define M_COPY(m) dupmbt(m) +# define M_DUP(m) dupmbt(m) # define GETKTIME(x) gettimeofday((struct timeval *)(x), NULL) -# undef MTOD -# define MTOD(m, t) ((t)(m)->mb_buf) -# define FREE_MB_T(x) +# define MTOD(m, t) ((t)(m)->mb_data) +# define FREE_MB_T(m) freembt(m) +# define ALLOC_MB_T(m,l) (m) = allocmbt(l) +# define PREP_MB_T(f, m) do { \ + (m)->mb_next = *(f)->fin_mp; \ + *(fin)->fin_mp = (m); \ + (f)->fin_m = (m); \ + } while (0) # define SLEEP(x,y) 1; # define WAKEUP(x,y) ; # define POLLWAKEUP(y) ; @@ -1530,6 +389,8 @@ typedef struct mb_s { # define KFREE(x) free(x) # define KFREES(x,s) free(x) # define GETIFP(x, v) get_unit(x,v) +# define GETIFMTU_4(x) 2048 +# define GETIFMTU_6(x) 2048 # define COPYIN(a,b,c) bcopywrap((a), (b), (c)) # define COPYOUT(a,b,c) bcopywrap((a), (b), (c)) # define COPYDATA(m, o, l, b) bcopy(MTOD((mb_t *)m, char *) + (o), \ @@ -1541,16 +402,18 @@ typedef struct mb_s { extern void m_copydata __P((mb_t *, int, int, caddr_t)); extern int ipfuiomove __P((caddr_t, int, int, struct uio *)); extern int bcopywrap __P((void *, void *, size_t)); -# ifndef CACHE_HASH -# define CACHE_HASH(x) ((IFNAME(fin->fin_ifp)[0] + \ - ((struct ifnet *)fin->fin_ifp)->if_unit) & 7) -# endif +extern mb_t *allocmbt __P((size_t)); +extern mb_t *dupmbt __P((mb_t *)); +extern void freembt __P((mb_t *)); -# define MUTEX_DESTROY(x) eMmutex_destroy(&(x)->ipf_emu) +# define MUTEX_DESTROY(x) eMmutex_destroy(&(x)->ipf_emu, \ + __FILE__, __LINE__) # define MUTEX_ENTER(x) eMmutex_enter(&(x)->ipf_emu, \ __FILE__, __LINE__) -# define MUTEX_EXIT(x) eMmutex_exit(&(x)->ipf_emu) -# define MUTEX_INIT(x,y) eMmutex_init(&(x)->ipf_emu, y) +# define MUTEX_EXIT(x) eMmutex_exit(&(x)->ipf_emu, \ + __FILE__, __LINE__) +# define MUTEX_INIT(x,y) eMmutex_init(&(x)->ipf_emu, y, \ + __FILE__, __LINE__) # define MUTEX_NUKE(x) bzero((x), sizeof(*(x))) # define MUTEX_DOWNGRADE(x) eMrwlock_downgrade(&(x)->ipf_emu, \ @@ -1566,10 +429,10 @@ extern int bcopywrap __P((void *, void *, size_t)); # define USE_MUTEXES 1 -extern void eMmutex_destroy __P((eMmutex_t *)); +extern void eMmutex_destroy __P((eMmutex_t *, char *, int)); extern void eMmutex_enter __P((eMmutex_t *, char *, int)); -extern void eMmutex_exit __P((eMmutex_t *)); -extern void eMmutex_init __P((eMmutex_t *, char *)); +extern void eMmutex_exit __P((eMmutex_t *, char *, int)); +extern void eMmutex_init __P((eMmutex_t *, char *, char *, int)); extern void eMrwlock_destroy __P((eMrwlock_t *)); extern void eMrwlock_exit __P((eMrwlock_t *)); extern void eMrwlock_init __P((eMrwlock_t *, char *)); @@ -1579,6 +442,8 @@ extern void eMrwlock_downgrade __P((eMrwlock_t *, char *, int)); #endif +extern mb_t *allocmbt(size_t); + #define MAX_IPV4HDR ((0xf << 2) + sizeof(struct icmp) + sizeof(ip_t) + 8) #ifndef IP_OFFMASK @@ -1590,13 +455,15 @@ extern void eMrwlock_downgrade __P((eMrwlock_t *, char *, int)); * On BSD's use quad_t as a guarantee for getting at least a 64bit sized * object. */ -#if (BSD > 199306) +#if !defined(__amd64__) && BSD_GT_YEAR(199306) # define USE_QUAD_T # define U_QUAD_T unsigned long long # define QUAD_T long long #else /* BSD > 199306 */ -# define U_QUAD_T u_long -# define QUAD_T long +# if !defined(U_QUAD_T) +# define U_QUAD_T u_long +# define QUAD_T long +# endif #endif /* BSD > 199306 */ @@ -1605,11 +472,9 @@ extern void eMrwlock_downgrade __P((eMrwlock_t *, char *, int)); defined(__osf__) || defined(linux) # include # include -# if !defined(linux) # if defined(_KERNEL) && !defined(__osf__) # include # endif -# endif typedef struct ip6_hdr ip6_t; # endif #endif @@ -1619,23 +484,20 @@ typedef struct ip6_hdr ip6_t; #endif #if defined(_KERNEL) -# ifdef MENTAT +# if defined(MENTAT) && !defined(INSTANCES) # define COPYDATA mb_copydata # define COPYBACK mb_copyback # else # define COPYDATA m_copydata # define COPYBACK m_copyback # endif -# if (BSD >= 199306) || defined(__FreeBSD__) # if (defined(__NetBSD_Version__) && (__NetBSD_Version__ < 105180000)) || \ defined(__FreeBSD__) || (defined(OpenBSD) && (OpenBSD < 200206)) || \ defined(_BSDI_VERSION) # include # endif -# if !defined(__FreeBSD__) || (defined (__FreeBSD_version) && \ - (__FreeBSD_version >= 300000)) -# if (defined(__NetBSD_Version__) && (__NetBSD_Version__ >= 105180000)) || \ - (defined(OpenBSD) && (OpenBSD >= 200111)) +# if !defined(__FreeBSD__) || FREEBSD_GE_REV(300000) +# if NETBSD_GE_REV(105180000) || OPENBSD_GE_REV(200111) # include # else # include @@ -1661,33 +523,31 @@ MALLOC_DECLARE(M_IPFILTER); # endif /* M_IPFILTER */ # endif /* M_PFIL */ # endif /* IPFILTER_M_IPFILTER */ -# if defined(__FreeBSD__) && __FreeBSD_version >= 800051 -# define KMALLOC(a, b) do { \ - a = (b)malloc(sizeof(*(a)), _M_IPF, M_NOWAIT); \ - } while (0) -# define KMALLOCS(a, b, c) do { \ - a = (b)malloc((c), _M_IPF, ((c) > 4096) ? M_WAITOK : M_NOWAIT); \ - } while (0) -# define KFREE(x) free((x), _M_IPF) -# define KFREES(x,s) free((x), _M_IPF) -# else +# if !defined(KMALLOC) # define KMALLOC(a, b) MALLOC((a), b, sizeof(*(a)), _M_IPF, M_NOWAIT) -# if !defined(KMALLOCS) -# define KMALLOCS(a, b, c) MALLOC((a), b, (c), _M_IPF, M_NOWAIT) -# endif +# endif +# if !defined(KMALLOCS) +# define KMALLOCS(a, b, c) MALLOC((a), b, (c), _M_IPF, M_NOWAIT) +# endif +# if !defined(KFREE) # define KFREE(x) FREE((x), _M_IPF) -# define KFREES(x,s) FREE((x), _M_IPF) +# endif +# if !defined(KFREES) +# define KFREES(x,s) FREE((x), _M_IPF) # endif # define UIOMOVE(a,b,c,d) uiomove((caddr_t)a,b,d) # define SLEEP(id, n) tsleep((id), PPAUSE|PCATCH, n, 0) # define WAKEUP(id,x) wakeup(id+x) -# define POLLWAKEUP(x) selwakeup(ipfselwait+x) +# if !defined(POLLWAKEUP) +# define POLLWAKEUP(x) selwakeup(softc->ipf_selwait+x) +# endif # define GETIFP(n, v) ifunit(n) -# endif /* (Free)BSD */ +# define GETIFMTU_4(x) ((struct ifnet *)x)->if_mtu +# define GETIFMTU_6(x) ((struct ifnet *)x)->if_mtu # if !defined(USE_MUTEXES) && !defined(SPL_NET) # if (defined(NetBSD) && (NetBSD <= 1991011) && (NetBSD >= 199407)) || \ - (defined(OpenBSD) && (OpenBSD >= 200006)) + OPENBSD_GE_REV(200006) # define SPL_NET(x) x = splsoftnet() # else # define SPL_IMP(x) x = splimp() @@ -1702,6 +562,45 @@ MALLOC_DECLARE(M_IPFILTER); # ifndef FREE_MB_T # define FREE_MB_T(m) m_freem(m) # endif +# ifndef ALLOC_MB_T +# ifdef MGETHDR +# define ALLOC_MB_T(m,l) do { \ + MGETHDR((m), M_DONTWAIT, MT_HEADER); \ + if ((m) != NULL) { \ + (m)->m_len = (l); \ + (m)->m_pkthdr.len = (l); \ + } \ + } while (0) +# else +# define ALLOC_MB_T(m,l) do { \ + MGET((m), M_DONTWAIT, MT_HEADER); \ + if ((m) != NULL) { \ + (m)->m_len = (l); \ + (m)->m_pkthdr.len = (l); \ + } \ + } while (0) +# endif +# endif +# ifndef PREP_MB_T +# define PREP_MB_T(f, m) do { \ + mb_t *_o = *(f)->fin_mp; \ + (m)->m_next = _o; \ + *(fin)->fin_mp = (m); \ + if (_o->m_flags & M_PKTHDR) { \ + (m)->m_pkthdr.len += \ + _o->m_pkthdr.len; \ + (m)->m_pkthdr.rcvif = \ + _o->m_pkthdr.rcvif; \ + } \ + } while (0) +# endif +# ifndef M_DUP +# ifdef M_COPYALL +# define M_DUP(m) m_dup(m, 0, M_COPYALL, 0) +# else +# define M_DUP(m) m_dup(m) +# endif +# endif # ifndef MTOD # define MTOD(m,t) mtod(m,t) @@ -1725,13 +624,13 @@ MALLOC_DECLARE(M_IPFILTER); #endif /* _KERNEL */ #if !defined(IFNAME) && !defined(_KERNEL) -# define IFNAME(x) ((struct ifnet *)x)->if_name +# define IFNAME(x) get_ifname((struct ifnet *)x) #endif #ifndef COPYIFNAME # define NEED_FRGETIFNAME -extern char *fr_getifname __P((struct ifnet *, char *)); +extern char *ipf_getifname __P((struct ifnet *, char *)); # define COPYIFNAME(v, x, b) \ - fr_getifname((struct ifnet *)x, b) + ipf_getifname((struct ifnet *)x, b) #endif #ifndef ASSERT @@ -1753,9 +652,7 @@ extern char *fr_getifname __P((struct ifnet *, char *)); */ #define ISALNUM(x) isalnum((u_char)(x)) #define ISALPHA(x) isalpha((u_char)(x)) -#define ISASCII(x) isascii((u_char)(x)) #define ISDIGIT(x) isdigit((u_char)(x)) -#define ISPRINT(x) isprint((u_char)(x)) #define ISSPACE(x) isspace((u_char)(x)) #define ISUPPER(x) isupper((u_char)(x)) #define ISXDIGIT(x) isxdigit((u_char)(x)) @@ -1803,11 +700,9 @@ extern char *fr_getifname __P((struct ifnet *, char *)); # define ATOMIC_INCL ATOMIC_INC # define ATOMIC_INC64 ATOMIC_INC # define ATOMIC_INC32 ATOMIC_INC -# define ATOMIC_INC16 ATOMIC_INC # define ATOMIC_DECL ATOMIC_DEC # define ATOMIC_DEC64 ATOMIC_DEC # define ATOMIC_DEC32 ATOMIC_DEC -# define ATOMIC_DEC16 ATOMIC_DEC #endif #ifndef HDR_T_PRIVATE @@ -1824,7 +719,10 @@ typedef struct tcpiphdr tcpiphdr_t; #endif #ifndef offsetof -# define offsetof(t,m) (int)((&((t *)0L)->m)) +# define offsetof(t,m) (size_t)((&((t *)0L)->m)) +#endif +#ifndef stsizeof +# define stsizeof(t,m) sizeof(((t *)0L)->m) #endif /* @@ -1871,9 +769,9 @@ typedef struct tcpiphdr tcpiphdr_t; #define TCPF_ALL (TH_FIN|TH_SYN|TH_RST|TH_PUSH|TH_ACK|TH_URG|\ TH_ECN|TH_CWR) -#if (BSD >= 199306) && !defined(m_act) +#if BSD_GE_YEAR(199306) && !defined(m_act) # define m_act m_nextpkt -#endif +#endif /* * Security Options for Intenet Protocol (IPSO) as defined in RFC 1108. @@ -1910,7 +808,7 @@ typedef struct tcpiphdr tcpiphdr_t; * IP option #defines */ #undef IPOPT_RR -#define IPOPT_RR 7 +#define IPOPT_RR 7 #undef IPOPT_ZSU #define IPOPT_ZSU 10 /* ZSU */ #undef IPOPT_MTUP @@ -1958,6 +856,8 @@ typedef struct tcpiphdr tcpiphdr_t; #define IPOPT_UMP 152 #undef IPOPT_FINN #define IPOPT_FINN 205 /* FINN */ +#undef IPOPT_AH +#define IPOPT_AH 256+IPPROTO_AH #ifndef TCPOPT_EOL # define TCPOPT_EOL 0 @@ -2236,8 +1136,11 @@ typedef struct tcpiphdr tcpiphdr_t; #ifndef IPPROTO_HOPOPTS # define IPPROTO_HOPOPTS 0 #endif +#ifndef IPPROTO_IPIP +# define IPPROTO_IPIP 4 +#endif #ifndef IPPROTO_ENCAP -# define IPPROTO_ENCAP 4 +# define IPPROTO_ENCAP 98 #endif #ifndef IPPROTO_IPV6 # define IPPROTO_IPV6 41 @@ -2436,6 +1339,38 @@ typedef struct tcpiphdr tcpiphdr_t; # define ICMP6_NI_SUBJ_IPV4 2 #endif +#ifndef MLD_MTRACE_RESP +# define MLD_MTRACE_RESP 200 +#endif +#ifndef MLD_MTRACE +# define MLD_MTRACE 201 +#endif +#ifndef MLD6_MTRACE_RESP +# define MLD6_MTRACE_RESP MLD_MTRACE_RESP +#endif +#ifndef MLD6_MTRACE +# define MLD6_MTRACE MLD_MTRACE +#endif + +#if !defined(IPV6_FLOWINFO_MASK) +# if (BYTE_ORDER == BIG_ENDIAN) || defined(_BIG_ENDIAN) +# define IPV6_FLOWINFO_MASK 0x0fffffff /* flow info (28 bits) */ +# else +# if(BYTE_ORDER == LITTLE_ENDIAN) || !defined(_BIG_ENDIAN) +# define IPV6_FLOWINFO_MASK 0xffffff0f /* flow info (28 bits) */ +# endif /* LITTLE_ENDIAN */ +# endif +#endif +#if !defined(IPV6_FLOWLABEL_MASK) +# if (BYTE_ORDER == BIG_ENDIAN) || defined(_BIG_ENDIAN) +# define IPV6_FLOWLABEL_MASK 0x000fffff /* flow label (20 bits) */ +# else +# if (BYTE_ORDER == LITTLE_ENDIAN) || !defined(_BIG_ENDIAN) +# define IPV6_FLOWLABEL_MASK 0xffff0f00 /* flow label (20 bits) */ +# endif /* LITTLE_ENDIAN */ +# endif +#endif + /* * ECN is a new addition to TCP - RFC 2481 */ @@ -2516,14 +1451,50 @@ typedef struct tcpiphdr tcpiphdr_t; # define MIN(a,b) (((a)<(b))?(a):(b)) #endif +#ifdef RESCUE +# undef IPFILTER_BPF +#endif + #ifdef IPF_DEBUG # define DPRINT(x) printf x #else # define DPRINT(x) #endif -#ifdef RESCUE -# undef IPFILTER_BPF +#ifndef AF_INET6 +# define AF_INET6 26 #endif +#ifdef DTRACE_PROBE +# ifdef _KERNEL +# define DT(_n) DTRACE_PROBE(_n) +# define DT1(_n,_a,_b) DTRACE_PROBE1(_n,_a,_b) +# define DT2(_n,_a,_b,_c,_d) DTRACE_PROBE2(_n,_a,_b,_c,_d) +# define DT3(_n,_a,_b,_c,_d,_e,_f) \ + DTRACE_PROBE3(_n,_a,_b,_c,_d,_e,_f) +# define DT4(_n,_a,_b,_c,_d,_e,_f,_g,_h) \ + DTRACE_PROBE4(_n,_a,_b,_c,_d,_e,_f,_g,_h) +# else +# define DT(_n) +# define DT1(_n,_a,_b) +# define DT2(_n,_a,_b,_c,_d) +# define DT3(_n,_a,_b,_c,_d,_e,_f) +# define DT4(_n,_a,_b,_c,_d,_e,_f,_g,_h) +# endif +#else +# define DT(_n) +# define DT1(_n,_a,_b) +# define DT2(_n,_a,_b,_c,_d) +# define DT3(_n,_a,_b,_c,_d,_e,_f) +# define DT4(_n,_a,_b,_c,_d,_e,_f,_g,_h) +#endif + +struct ip6_routing { + u_char ip6r_nxt; /* next header */ + u_char ip6r_len; /* length in units of 8 octets */ + u_char ip6r_type; /* always zero */ + u_char ip6r_segleft; /* segments left */ + u_32_t ip6r_reserved; /* reserved field */ +}; + #endif /* __IP_COMPAT_H__ */ diff --git a/sys/contrib/ipfilter/netinet/ip_dns_pxy.c b/sys/contrib/ipfilter/netinet/ip_dns_pxy.c new file mode 100644 index 00000000000..df863b8f6fb --- /dev/null +++ b/sys/contrib/ipfilter/netinet/ip_dns_pxy.c @@ -0,0 +1,402 @@ +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + * + * $Id: ip_dns_pxy.c,v 1.1.2.10 2012/07/22 08:04:23 darren_r Exp $ + */ + +#define IPF_DNS_PROXY + +/* + * map ... proxy port dns/udp 53 { block .cnn.com; } + */ +typedef struct ipf_dns_filter { + struct ipf_dns_filter *idns_next; + char *idns_name; + int idns_namelen; + int idns_pass; +} ipf_dns_filter_t; + + +typedef struct ipf_dns_softc_s { + ipf_dns_filter_t *ipf_p_dns_list; + ipfrwlock_t ipf_p_dns_rwlock; + u_long ipf_p_dns_compress; + u_long ipf_p_dns_toolong; + u_long ipf_p_dns_nospace; +} ipf_dns_softc_t; + +int ipf_p_dns_allow_query __P((ipf_dns_softc_t *, dnsinfo_t *)); +int ipf_p_dns_ctl __P((ipf_main_softc_t *, void *, ap_ctl_t *)); +int ipf_p_dns_del __P((ipf_main_softc_t *, ap_session_t *)); +int ipf_p_dns_get_name __P((ipf_dns_softc_t *, char *, int, char *, int)); +int ipf_p_dns_inout __P((void *, fr_info_t *, ap_session_t *, nat_t *)); +int ipf_p_dns_match __P((fr_info_t *, ap_session_t *, nat_t *)); +int ipf_p_dns_match_names __P((ipf_dns_filter_t *, char *, int)); +int ipf_p_dns_new __P((void *, fr_info_t *, ap_session_t *, nat_t *)); +void *ipf_p_dns_soft_create __P((ipf_main_softc_t *)); +void ipf_p_dns_soft_destroy __P((ipf_main_softc_t *, void *)); + +typedef struct { + u_char dns_id[2]; + u_short dns_ctlword; + u_short dns_qdcount; + u_short dns_ancount; + u_short dns_nscount; + u_short dns_arcount; +} ipf_dns_hdr_t; + +#define DNS_QR(x) ((ntohs(x) & 0x8000) >> 15) +#define DNS_OPCODE(x) ((ntohs(x) & 0x7800) >> 11) +#define DNS_AA(x) ((ntohs(x) & 0x0400) >> 10) +#define DNS_TC(x) ((ntohs(x) & 0x0200) >> 9) +#define DNS_RD(x) ((ntohs(x) & 0x0100) >> 8) +#define DNS_RA(x) ((ntohs(x) & 0x0080) >> 7) +#define DNS_Z(x) ((ntohs(x) & 0x0070) >> 4) +#define DNS_RCODE(x) ((ntohs(x) & 0x000f) >> 0) + + +void * +ipf_p_dns_soft_create(softc) + ipf_main_softc_t *softc; +{ + ipf_dns_softc_t *softd; + + KMALLOC(softd, ipf_dns_softc_t *); + if (softd == NULL) + return NULL; + + bzero((char *)softd, sizeof(*softd)); + RWLOCK_INIT(&softd->ipf_p_dns_rwlock, "ipf dns rwlock"); + + return softd; +} + + +void +ipf_p_dns_soft_destroy(softc, arg) + ipf_main_softc_t *softc; + void *arg; +{ + ipf_dns_softc_t *softd = arg; + ipf_dns_filter_t *idns; + + while ((idns = softd->ipf_p_dns_list) != NULL) { + KFREES(idns->idns_name, idns->idns_namelen); + idns->idns_name = NULL; + idns->idns_namelen = 0; + softd->ipf_p_dns_list = idns->idns_next; + KFREE(idns); + } + RW_DESTROY(&softd->ipf_p_dns_rwlock); + + KFREE(softd); +} + + +int +ipf_p_dns_ctl(softc, arg, ctl) + ipf_main_softc_t *softc; + void *arg; + ap_ctl_t *ctl; +{ + ipf_dns_softc_t *softd = arg; + ipf_dns_filter_t *tmp, *idns, **idnsp; + int error = 0; + + /* + * To make locking easier. + */ + KMALLOC(tmp, ipf_dns_filter_t *); + + WRITE_ENTER(&softd->ipf_p_dns_rwlock); + for (idnsp = &softd->ipf_p_dns_list; (idns = *idnsp) != NULL; + idnsp = &idns->idns_next) { + if (idns->idns_namelen != ctl->apc_dsize) + continue; + if (!strncmp(ctl->apc_data, idns->idns_name, + idns->idns_namelen)) + break; + } + + switch (ctl->apc_cmd) + { + case APC_CMD_DEL : + if (idns == NULL) { + IPFERROR(80006); + error = ESRCH; + break; + } + *idnsp = idns->idns_next; + idns->idns_next = NULL; + KFREES(idns->idns_name, idns->idns_namelen); + idns->idns_name = NULL; + idns->idns_namelen = 0; + KFREE(idns); + break; + case APC_CMD_ADD : + if (idns != NULL) { + IPFERROR(80007); + error = EEXIST; + break; + } + if (tmp == NULL) { + IPFERROR(80008); + error = ENOMEM; + break; + } + idns = tmp; + tmp = NULL; + idns->idns_namelen = ctl->apc_dsize; + idns->idns_name = ctl->apc_data; + idns->idns_pass = ctl->apc_arg; + idns->idns_next = NULL; + *idnsp = idns; + ctl->apc_data = NULL; + ctl->apc_dsize = 0; + break; + default : + IPFERROR(80009); + error = EINVAL; + break; + } + RWLOCK_EXIT(&softd->ipf_p_dns_rwlock); + + if (tmp != NULL) { + KFREE(tmp); + tmp = NULL; + } + + return error; +} + + +/* ARGSUSED */ +int +ipf_p_dns_new(arg, fin, aps, nat) + void *arg; + fr_info_t *fin; + ap_session_t *aps; + nat_t *nat; +{ + dnsinfo_t *di; + int dlen; + + if (fin->fin_v != 4) + return -1; + + dlen = fin->fin_dlen - sizeof(udphdr_t); + if (dlen < sizeof(ipf_dns_hdr_t)) { + /* + * No real DNS packet is smaller than that. + */ + return -1; + } + + aps->aps_psiz = sizeof(dnsinfo_t); + KMALLOCS(di, dnsinfo_t *, sizeof(dnsinfo_t)); + if (di == NULL) { + printf("ipf_dns_new:KMALLOCS(%d) failed\n", sizeof(*di)); + return -1; + } + + MUTEX_INIT(&di->dnsi_lock, "dns lock"); + + aps->aps_data = di; + + dlen = fin->fin_dlen - sizeof(udphdr_t); + COPYDATA(fin->fin_m, fin->fin_hlen + sizeof(udphdr_t), + MIN(dlen, sizeof(di->dnsi_buffer)), di->dnsi_buffer); + di->dnsi_id = (di->dnsi_buffer[0] << 8) | di->dnsi_buffer[1]; + return 0; +} + + +/* ARGSUSED */ +int +ipf_p_dns_del(softc, aps) + ipf_main_softc_t *softc; + ap_session_t *aps; +{ +#ifdef USE_MUTEXES + dnsinfo_t *di = aps->aps_data; + + MUTEX_DESTROY(&di->dnsi_lock); +#endif + KFREES(aps->aps_data, aps->aps_psiz); + aps->aps_data = NULL; + aps->aps_psiz = 0; + return 0; +} + + +/* + * Tries to match the base string (in our ACL) with the query from a packet. + */ +int +ipf_p_dns_match_names(idns, query, qlen) + ipf_dns_filter_t *idns; + char *query; + int qlen; +{ + int blen; + char *base; + + blen = idns->idns_namelen; + base = idns->idns_name; + + if (blen > qlen) + return 1; + + if (blen == qlen) + return strncasecmp(base, query, qlen); + + /* + * If the base string string is shorter than the query, allow the + * tail of the base to match the same length tail of the query *if*: + * - the base string starts with a '*' (*cnn.com) + * - the base string represents a domain (.cnn.com) + * as otherwise it would not be possible to block just "cnn.com" + * without also impacting "foocnn.com", etc. + */ + if (*base == '*') { + base++; + blen--; + } else if (*base != '.') + return 1; + + return strncasecmp(base, query + qlen - blen, blen); +} + + +int +ipf_p_dns_get_name(softd, start, len, buffer, buflen) + ipf_dns_softc_t *softd; + char *start; + int len; + char *buffer; + int buflen; +{ + char *s, *t, clen; + int slen, blen; + + s = start; + t = buffer; + slen = len; + blen = buflen - 1; /* Always make room for trailing \0 */ + + while (*s != '\0') { + clen = *s; + if ((clen & 0xc0) == 0xc0) { /* Doesn't do compression */ + softd->ipf_p_dns_compress++; + return 0; + } + if (clen > slen) { + softd->ipf_p_dns_toolong++; + return 0; /* Does the name run off the end? */ + } + if ((clen + 1) > blen) { + softd->ipf_p_dns_nospace++; + return 0; /* Enough room for name+.? */ + } + s++; + bcopy(s, t, clen); + t += clen; + s += clen; + *t++ = '.'; + slen -= clen; + blen -= (clen + 1); + } + + *(t - 1) = '\0'; + return s - start; +} + + +int +ipf_p_dns_allow_query(softd, dnsi) + ipf_dns_softc_t *softd; + dnsinfo_t *dnsi; +{ + ipf_dns_filter_t *idns; + int len; + + len = strlen(dnsi->dnsi_buffer); + + for (idns = softd->ipf_p_dns_list; idns != NULL; idns = idns->idns_next) + if (ipf_p_dns_match_names(idns, dnsi->dnsi_buffer, len) == 0) + return idns->idns_pass; + return 0; +} + + +/* ARGSUSED */ +int +ipf_p_dns_inout(arg, fin, aps, nat) + void *arg; + fr_info_t *fin; + ap_session_t *aps; + nat_t *nat; +{ + ipf_dns_softc_t *softd = arg; + ipf_dns_hdr_t *dns; + dnsinfo_t *di; + char *data; + int dlen, q, rc = 0; + + if (fin->fin_dlen < sizeof(*dns)) + return APR_ERR(1); + + dns = (ipf_dns_hdr_t *)((char *)fin->fin_dp + sizeof(udphdr_t)); + + q = dns->dns_qdcount; + + data = (char *)(dns + 1); + dlen = fin->fin_dlen - sizeof(*dns) - sizeof(udphdr_t); + + di = aps->aps_data; + + READ_ENTER(&softd->ipf_p_dns_rwlock); + MUTEX_ENTER(&di->dnsi_lock); + + for (; (dlen > 0) && (q > 0); q--) { + int len; + + len = ipf_p_dns_get_name(softd, data, dlen, di->dnsi_buffer, + sizeof(di->dnsi_buffer)); + if (len == 0) { + rc = 1; + break; + } + rc = ipf_p_dns_allow_query(softd, di); + if (rc != 0) + break; + data += len; + dlen -= len; + } + MUTEX_EXIT(&di->dnsi_lock); + RWLOCK_EXIT(&softd->ipf_p_dns_rwlock); + + return APR_ERR(rc); +} + + +/* ARGSUSED */ +int +ipf_p_dns_match(fin, aps, nat) + fr_info_t *fin; + ap_session_t *aps; + nat_t *nat; +{ + dnsinfo_t *di = aps->aps_data; + ipf_dns_hdr_t *dnh; + + if ((fin->fin_dlen < sizeof(u_short)) || (fin->fin_flx & FI_FRAG)) + return -1; + + dnh = (ipf_dns_hdr_t *)((char *)fin->fin_dp + sizeof(udphdr_t)); + if (((dnh->dns_id[0] << 8) | dnh->dns_id[1]) != di->dnsi_id) + return -1; + return 0; +} diff --git a/sys/contrib/ipfilter/netinet/ip_dstlist.c b/sys/contrib/ipfilter/netinet/ip_dstlist.c new file mode 100644 index 00000000000..ce2e72e8130 --- /dev/null +++ b/sys/contrib/ipfilter/netinet/ip_dstlist.c @@ -0,0 +1,1351 @@ +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + */ +#if defined(KERNEL) || defined(_KERNEL) +# undef KERNEL +# undef _KERNEL +# define KERNEL 1 +# define _KERNEL 1 +#endif +#if defined(__osf__) +# define _PROTO_NET_H_ +#endif +#include +#include +#include +#include +#if !defined(_KERNEL) && !defined(__KERNEL__) +# include +# include +# include +# define _KERNEL +# ifdef __OpenBSD__ +struct file; +# endif +# include +# undef _KERNEL +#else +# include +# if defined(NetBSD) && (__NetBSD_Version__ >= 104000000) +# include +# endif +#endif +#include +#if !defined(linux) +# include +#endif +#include +#if defined(_KERNEL) && (!defined(__SVR4) && !defined(__svr4__)) +# include +#endif +#if defined(__SVR4) || defined(__svr4__) +# include +# include +# ifdef _KERNEL +# include +# endif +# include +# include +#endif +#if defined(__FreeBSD_version) && (__FreeBSD_version >= 300000) +# include +#endif + +#include +#include + +#include "netinet/ip_compat.h" +#include "netinet/ip_fil.h" +#include "netinet/ip_nat.h" +#include "netinet/ip_lookup.h" +#include "netinet/ip_dstlist.h" + +/* END OF INCLUDES */ + +#ifdef HAS_SYS_MD5_H +# include +#else +# include "md5.h" +#endif + +#if !defined(lint) +static const char rcsid[] = "@(#)$Id: ip_dstlist.c,v 1.13.2.12 2012/07/20 08:40:19 darren_r Exp $"; +#endif + +typedef struct ipf_dstl_softc_s { + ippool_dst_t *dstlist[LOOKUP_POOL_SZ]; + ippool_dst_t **tails[LOOKUP_POOL_SZ]; + ipf_dstl_stat_t stats; +} ipf_dstl_softc_t; + + +static void *ipf_dstlist_soft_create __P((ipf_main_softc_t *)); +static void ipf_dstlist_soft_destroy __P((ipf_main_softc_t *, void *)); +static int ipf_dstlist_soft_init __P((ipf_main_softc_t *, void *)); +static void ipf_dstlist_soft_fini __P((ipf_main_softc_t *, void *)); +static int ipf_dstlist_addr_find __P((ipf_main_softc_t *, void *, int, + void *, u_int)); +static size_t ipf_dstlist_flush __P((ipf_main_softc_t *, void *, + iplookupflush_t *)); +static int ipf_dstlist_iter_deref __P((ipf_main_softc_t *, void *, int, int, + void *)); +static int ipf_dstlist_iter_next __P((ipf_main_softc_t *, void *, ipftoken_t *, + ipflookupiter_t *)); +static int ipf_dstlist_node_add __P((ipf_main_softc_t *, void *, + iplookupop_t *, int)); +static int ipf_dstlist_node_del __P((ipf_main_softc_t *, void *, + iplookupop_t *, int)); +static int ipf_dstlist_stats_get __P((ipf_main_softc_t *, void *, + iplookupop_t *)); +static int ipf_dstlist_table_add __P((ipf_main_softc_t *, void *, + iplookupop_t *)); +static int ipf_dstlist_table_del __P((ipf_main_softc_t *, void *, + iplookupop_t *)); +static int ipf_dstlist_table_deref __P((ipf_main_softc_t *, void *, void *)); +static void *ipf_dstlist_table_find __P((void *, int, char *)); +static void ipf_dstlist_table_free __P((ipf_dstl_softc_t *, ippool_dst_t *)); +static void ipf_dstlist_table_remove __P((ipf_main_softc_t *, + ipf_dstl_softc_t *, ippool_dst_t *)); +static void ipf_dstlist_table_clearnodes __P((ipf_dstl_softc_t *, + ippool_dst_t *)); +static ipf_dstnode_t *ipf_dstlist_select __P((fr_info_t *, ippool_dst_t *)); +static void *ipf_dstlist_select_ref __P((void *, int, char *)); +static void ipf_dstlist_node_free __P((ipf_dstl_softc_t *, ippool_dst_t *, ipf_dstnode_t *)); +static int ipf_dstlist_node_deref __P((void *, ipf_dstnode_t *)); +static void ipf_dstlist_expire __P((ipf_main_softc_t *, void *)); +static void ipf_dstlist_sync __P((ipf_main_softc_t *, void *)); + +ipf_lookup_t ipf_dstlist_backend = { + IPLT_DSTLIST, + ipf_dstlist_soft_create, + ipf_dstlist_soft_destroy, + ipf_dstlist_soft_init, + ipf_dstlist_soft_fini, + ipf_dstlist_addr_find, + ipf_dstlist_flush, + ipf_dstlist_iter_deref, + ipf_dstlist_iter_next, + ipf_dstlist_node_add, + ipf_dstlist_node_del, + ipf_dstlist_stats_get, + ipf_dstlist_table_add, + ipf_dstlist_table_del, + ipf_dstlist_table_deref, + ipf_dstlist_table_find, + ipf_dstlist_select_ref, + ipf_dstlist_select_node, + ipf_dstlist_expire, + ipf_dstlist_sync +}; + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_dstlist_soft_create */ +/* Returns: int - 0 = success, else error */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* */ +/* Allocating a chunk of memory filled with 0's is enough for the current */ +/* soft context used with destination lists. */ +/* ------------------------------------------------------------------------ */ +static void * +ipf_dstlist_soft_create(softc) + ipf_main_softc_t *softc; +{ + ipf_dstl_softc_t *softd; + int i; + + KMALLOC(softd, ipf_dstl_softc_t *); + if (softd == NULL) { + IPFERROR(120028); + return NULL; + } + + bzero((char *)softd, sizeof(*softd)); + for (i = 0; i <= IPL_LOGMAX; i++) + softd->tails[i] = &softd->dstlist[i]; + + return softd; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_dstlist_soft_destroy */ +/* Returns: Nil */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* arg(I) - pointer to local context to use */ +/* */ +/* For destination lists, the only thing we have to do when destroying the */ +/* soft context is free it! */ +/* ------------------------------------------------------------------------ */ +static void +ipf_dstlist_soft_destroy(softc, arg) + ipf_main_softc_t *softc; + void *arg; +{ + ipf_dstl_softc_t *softd = arg; + + KFREE(softd); +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_dstlist_soft_init */ +/* Returns: int - 0 = success, else error */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* arg(I) - pointer to local context to use */ +/* */ +/* There is currently no soft context for destination list management. */ +/* ------------------------------------------------------------------------ */ +static int +ipf_dstlist_soft_init(softc, arg) + ipf_main_softc_t *softc; + void *arg; +{ + return 0; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_dstlist_soft_fini */ +/* Returns: Nil */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* arg(I) - pointer to local context to use */ +/* */ +/* There is currently no soft context for destination list management. */ +/* ------------------------------------------------------------------------ */ +static void +ipf_dstlist_soft_fini(softc, arg) + ipf_main_softc_t *softc; + void *arg; +{ + ipf_dstl_softc_t *softd = arg; + int i; + + for (i = -1; i <= IPL_LOGMAX; i++) { + while (softd->dstlist[i + 1] != NULL) { + ipf_dstlist_table_remove(softc, softd, + softd->dstlist[i + 1]); + } + } + + ASSERT(softd->stats.ipls_numderefnodes == 0); +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_dstlist_addr_find */ +/* Returns: int - 0 = success, else error */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* arg1(I) - pointer to local context to use */ +/* arg2(I) - pointer to local context to use */ +/* arg3(I) - pointer to local context to use */ +/* arg4(I) - pointer to local context to use */ +/* */ +/* There is currently no such thing as searching a destination list for an */ +/* address so this function becomes a no-op. Its presence is required as */ +/* ipf_lookup_res_name() stores the "addr_find" function pointer in the */ +/* pointer passed in to it as funcptr, although it could be a generic null- */ +/* op function rather than a specific one. */ +/* ------------------------------------------------------------------------ */ +/*ARGSUSED*/ +static int +ipf_dstlist_addr_find(softc, arg1, arg2, arg3, arg4) + ipf_main_softc_t *softc; + void *arg1, *arg3; + int arg2; + u_int arg4; +{ + return -1; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_dstlist_flush */ +/* Returns: int - number of objects deleted */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* arg(I) - pointer to local context to use */ +/* fop(I) - pointer to lookup flush operation data */ +/* */ +/* Flush all of the destination tables that match the data passed in with */ +/* the iplookupflush_t. There are two ways to match objects: the device for */ +/* which they are to be used with and their name. */ +/* ------------------------------------------------------------------------ */ +static size_t +ipf_dstlist_flush(softc, arg, fop) + ipf_main_softc_t *softc; + void *arg; + iplookupflush_t *fop; +{ + ipf_dstl_softc_t *softd = arg; + ippool_dst_t *node, *next; + int n, i; + + for (n = 0, i = -1; i <= IPL_LOGMAX; i++) { + if (fop->iplf_unit != IPLT_ALL && fop->iplf_unit != i) + continue; + for (node = softd->dstlist[i + 1]; node != NULL; node = next) { + next = node->ipld_next; + + if ((*fop->iplf_name != '\0') && + strncmp(fop->iplf_name, node->ipld_name, + FR_GROUPLEN)) + continue; + + ipf_dstlist_table_remove(softc, softd, node); + n++; + } + } + return n; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_dstlist_iter_deref */ +/* Returns: int - 0 = success, else error */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* arg(I) - pointer to local context to use */ +/* otype(I) - type of data structure to iterate through */ +/* unit(I) - device we are working with */ +/* data(I) - address of object in kernel space */ +/* */ +/* This function is called when the iteration token is being free'd and is */ +/* responsible for dropping the reference count of the structure it points */ +/* to. */ +/* ------------------------------------------------------------------------ */ +static int +ipf_dstlist_iter_deref(softc, arg, otype, unit, data) + ipf_main_softc_t *softc; + void *arg; + int otype, unit; + void *data; +{ + if (data == NULL) { + IPFERROR(120001); + return EINVAL; + } + + if (unit < -1 || unit > IPL_LOGMAX) { + IPFERROR(120002); + return EINVAL; + } + + switch (otype) + { + case IPFLOOKUPITER_LIST : + ipf_dstlist_table_deref(softc, arg, (ippool_dst_t *)data); + break; + + case IPFLOOKUPITER_NODE : + ipf_dstlist_node_deref(arg, (ipf_dstnode_t *)data); + break; + } + + return 0; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_dstlist_iter_next */ +/* Returns: int - 0 = success, else error */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* arg(I) - pointer to local context to use */ +/* op(I) - pointer to lookup operation data */ +/* uid(I) - uid of process doing the ioctl */ +/* */ +/* This function is responsible for either selecting the next destination */ +/* list or node on a destination list to be returned as a user process */ +/* iterates through the list of destination lists or nodes. */ +/* ------------------------------------------------------------------------ */ +static int +ipf_dstlist_iter_next(softc, arg, token, iter) + ipf_main_softc_t *softc; + void *arg; + ipftoken_t *token; + ipflookupiter_t *iter; +{ + ipf_dstnode_t zn, *nextnode = NULL, *node = NULL; + ippool_dst_t zero, *next = NULL, *dsttab = NULL; + ipf_dstl_softc_t *softd = arg; + int err = 0; + void *hint; + + switch (iter->ili_otype) + { + case IPFLOOKUPITER_LIST : + dsttab = token->ipt_data; + if (dsttab == NULL) { + next = softd->dstlist[(int)iter->ili_unit + 1]; + } else { + next = dsttab->ipld_next; + } + + if (next != NULL) { + ATOMIC_INC32(next->ipld_ref); + token->ipt_data = next; + hint = next->ipld_next; + } else { + bzero((char *)&zero, sizeof(zero)); + next = &zero; + token->ipt_data = NULL; + hint = NULL; + } + break; + + case IPFLOOKUPITER_NODE : + node = token->ipt_data; + if (node == NULL) { + dsttab = ipf_dstlist_table_find(arg, iter->ili_unit, + iter->ili_name); + if (dsttab == NULL) { + IPFERROR(120004); + err = ESRCH; + nextnode = NULL; + } else { + if (dsttab->ipld_dests == NULL) + nextnode = NULL; + else + nextnode = *dsttab->ipld_dests; + dsttab = NULL; + } + } else { + nextnode = node->ipfd_next; + } + + if (nextnode != NULL) { + MUTEX_ENTER(&nextnode->ipfd_lock); + nextnode->ipfd_ref++; + MUTEX_EXIT(&nextnode->ipfd_lock); + token->ipt_data = nextnode; + hint = nextnode->ipfd_next; + } else { + bzero((char *)&zn, sizeof(zn)); + nextnode = &zn; + token->ipt_data = NULL; + hint = NULL; + } + break; + default : + IPFERROR(120003); + err = EINVAL; + break; + } + + if (err != 0) + return err; + + switch (iter->ili_otype) + { + case IPFLOOKUPITER_LIST : + if (dsttab != NULL) + ipf_dstlist_table_deref(softc, arg, dsttab); + err = COPYOUT(next, iter->ili_data, sizeof(*next)); + if (err != 0) { + IPFERROR(120005); + err = EFAULT; + } + break; + + case IPFLOOKUPITER_NODE : + if (node != NULL) + ipf_dstlist_node_deref(arg, node); + err = COPYOUT(nextnode, iter->ili_data, sizeof(*nextnode)); + if (err != 0) { + IPFERROR(120006); + err = EFAULT; + } + break; + } + + if (hint == NULL) + ipf_token_mark_complete(token); + + return err; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_dstlist_node_add */ +/* Returns: int - 0 = success, else error */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* arg(I) - pointer to local context to use */ +/* op(I) - pointer to lookup operation data */ +/* uid(I) - uid of process doing the ioctl */ +/* Locks: WRITE(ipf_poolrw) */ +/* */ +/* Add a new node to a destination list. To do this, we only copy in the */ +/* frdest_t structure because that contains the only data required from the */ +/* application to create a new node. The frdest_t doesn't contain the name */ +/* itself. When loading filter rules, fd_name is a 'pointer' to the name. */ +/* In this case, the 'pointer' does not work, instead it is the length of */ +/* the name and the name is immediately following the frdest_t structure. */ +/* fd_name must include the trailing \0, so it should be strlen(str) + 1. */ +/* For simple sanity checking, an upper bound on the size of fd_name is */ +/* imposed - 128. */ +/* ------------------------------------------------------------------------ */ +static int +ipf_dstlist_node_add(softc, arg, op, uid) + ipf_main_softc_t *softc; + void *arg; + iplookupop_t *op; + int uid; +{ + ipf_dstl_softc_t *softd = arg; + ipf_dstnode_t *node, **nodes; + ippool_dst_t *d; + frdest_t dest; + int err; + + if (op->iplo_size < sizeof(frdest_t)) { + IPFERROR(120007); + return EINVAL; + } + + err = COPYIN(op->iplo_struct, &dest, sizeof(dest)); + if (err != 0) { + IPFERROR(120009); + return EFAULT; + } + + d = ipf_dstlist_table_find(arg, op->iplo_unit, op->iplo_name); + if (d == NULL) { + IPFERROR(120010); + return ESRCH; + } + + switch (dest.fd_addr.adf_family) + { + case AF_INET : + case AF_INET6 : + break; + default : + IPFERROR(120019); + return EINVAL; + } + + if (dest.fd_name < -1 || dest.fd_name > 128) { + IPFERROR(120018); + return EINVAL; + } + + KMALLOCS(node, ipf_dstnode_t *, sizeof(*node) + dest.fd_name); + if (node == NULL) { + softd->stats.ipls_nomem++; + IPFERROR(120008); + return ENOMEM; + } + bzero((char *)node, sizeof(*node) + dest.fd_name); + + bcopy(&dest, &node->ipfd_dest, sizeof(dest)); + node->ipfd_size = sizeof(*node) + dest.fd_name; + + if (dest.fd_name > 0) { + /* + * fd_name starts out as the length of the string to copy + * in (including \0) and ends up being the offset from + * fd_names (0). + */ + err = COPYIN((char *)op->iplo_struct + sizeof(dest), + node->ipfd_names, dest.fd_name); + if (err != 0) { + IPFERROR(120017); + KFREES(node, node->ipfd_size); + return EFAULT; + } + node->ipfd_dest.fd_name = 0; + } else { + node->ipfd_dest.fd_name = -1; + } + + if (d->ipld_nodes == d->ipld_maxnodes) { + KMALLOCS(nodes, ipf_dstnode_t **, + sizeof(*nodes) * (d->ipld_maxnodes + 1)); + if (nodes == NULL) { + softd->stats.ipls_nomem++; + IPFERROR(120022); + KFREES(node, node->ipfd_size); + return ENOMEM; + } + if (d->ipld_dests != NULL) { + bcopy(d->ipld_dests, nodes, + sizeof(*nodes) * d->ipld_maxnodes); + KFREES(d->ipld_dests, sizeof(*nodes) * d->ipld_nodes); + nodes[0]->ipfd_pnext = nodes; + } + d->ipld_dests = nodes; + d->ipld_maxnodes++; + } + d->ipld_dests[d->ipld_nodes] = node; + d->ipld_nodes++; + + if (d->ipld_nodes == 1) { + node->ipfd_pnext = d->ipld_dests; + } else if (d->ipld_nodes > 1) { + node->ipfd_pnext = &d->ipld_dests[d->ipld_nodes - 2]->ipfd_next; + } + *node->ipfd_pnext = node; + + MUTEX_INIT(&node->ipfd_lock, "ipf dst node lock"); + node->ipfd_uid = uid; + node->ipfd_ref = 1; + if (node->ipfd_dest.fd_name == 0) + (void) ipf_resolvedest(softc, node->ipfd_names, + &node->ipfd_dest, AF_INET); +#ifdef USE_INET6 + if (node->ipfd_dest.fd_name == 0 && + node->ipfd_dest.fd_ptr == (void *)-1) + (void) ipf_resolvedest(softc, node->ipfd_names, + &node->ipfd_dest, AF_INET6); +#endif + + softd->stats.ipls_numnodes++; + + return 0; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_dstlist_node_deref */ +/* Returns: int - 0 = success, else error */ +/* Parameters: arg(I) - pointer to local context to use */ +/* node(I) - pointer to destionation node to free */ +/* */ +/* Dereference the use count by one. If it drops to zero then we can assume */ +/* that it has been removed from any lists/tables and is ripe for freeing. */ +/* The pointer to context is required for the purpose of maintaining */ +/* statistics. */ +/* ------------------------------------------------------------------------ */ +static int +ipf_dstlist_node_deref(arg, node) + void *arg; + ipf_dstnode_t *node; +{ + ipf_dstl_softc_t *softd = arg; + int ref; + + MUTEX_ENTER(&node->ipfd_lock); + ref = --node->ipfd_ref; + MUTEX_EXIT(&node->ipfd_lock); + + if (ref > 0) + return 0; + + if ((node->ipfd_flags & IPDST_DELETE) != 0) + softd->stats.ipls_numderefnodes--; + MUTEX_DESTROY(&node->ipfd_lock); + KFREES(node, node->ipfd_size); + softd->stats.ipls_numnodes--; + + return 0; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_dstlist_node_del */ +/* Returns: int - 0 = success, else error */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* arg(I) - pointer to local context to use */ +/* op(I) - pointer to lookup operation data */ +/* uid(I) - uid of process doing the ioctl */ +/* */ +/* Look for a matching destination node on the named table and free it if */ +/* found. Because the name embedded in the frdest_t is variable in length, */ +/* it is necessary to allocate some memory locally, to complete this op. */ +/* ------------------------------------------------------------------------ */ +static int +ipf_dstlist_node_del(softc, arg, op, uid) + ipf_main_softc_t *softc; + void *arg; + iplookupop_t *op; + int uid; +{ + ipf_dstl_softc_t *softd = arg; + ipf_dstnode_t *node; + frdest_t frd, *temp; + ippool_dst_t *d; + size_t size; + int err; + + d = ipf_dstlist_table_find(arg, op->iplo_unit, op->iplo_name); + if (d == NULL) { + IPFERROR(120012); + return ESRCH; + } + + err = COPYIN(op->iplo_struct, &frd, sizeof(frd)); + if (err != 0) { + IPFERROR(120011); + return EFAULT; + } + + size = sizeof(*temp) + frd.fd_name; + KMALLOCS(temp, frdest_t *, size); + if (temp == NULL) { + softd->stats.ipls_nomem++; + IPFERROR(120026); + return ENOMEM; + } + + err = COPYIN(op->iplo_struct, temp, size); + if (err != 0) { + IPFERROR(120027); + return EFAULT; + } + + MUTEX_ENTER(&d->ipld_lock); + for (node = *d->ipld_dests; node != NULL; node = node->ipfd_next) { + if ((uid != 0) && (node->ipfd_uid != uid)) + continue; + if (node->ipfd_size != size) + continue; + if (!bcmp(&node->ipfd_dest.fd_ip6, &frd.fd_ip6, + size - offsetof(frdest_t, fd_ip6))) { + ipf_dstlist_node_free(softd, d, node); + MUTEX_EXIT(&d->ipld_lock); + KFREES(temp, size); + return 0; + } + } + MUTEX_EXIT(&d->ipld_lock); + KFREES(temp, size); + + return ESRCH; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_dstlist_node_free */ +/* Returns: Nil */ +/* Parameters: softd(I) - pointer to the destination list context */ +/* d(I) - pointer to destination list */ +/* node(I) - pointer to node to free */ +/* Locks: MUTEX(ipld_lock) or WRITE(ipf_poolrw) */ +/* */ +/* Free the destination node by first removing it from any lists and then */ +/* checking if this was the last reference held to the object. While the */ +/* array of pointers to nodes is compacted, its size isn't reduced (by way */ +/* of allocating a new smaller one and copying) because the belief is that */ +/* it is likely the array will again reach that size. */ +/* ------------------------------------------------------------------------ */ +static void +ipf_dstlist_node_free(softd, d, node) + ipf_dstl_softc_t *softd; + ippool_dst_t *d; + ipf_dstnode_t *node; +{ + int i; + + /* + * Compact the array of pointers to nodes. + */ + for (i = 0; i < d->ipld_nodes; i++) + if (d->ipld_dests[i] == node) + break; + if (d->ipld_nodes - i > 1) { + bcopy(&d->ipld_dests[i + 1], &d->ipld_dests[i], + sizeof(*d->ipld_dests) * (d->ipld_nodes - i - 1)); + } + d->ipld_nodes--; + + if (node->ipfd_pnext != NULL) + *node->ipfd_pnext = node->ipfd_next; + if (node->ipfd_next != NULL) + node->ipfd_next->ipfd_pnext = node->ipfd_pnext; + node->ipfd_pnext = NULL; + node->ipfd_next = NULL; + + if ((node->ipfd_flags & IPDST_DELETE) == 0) { + softd->stats.ipls_numderefnodes++; + node->ipfd_flags |= IPDST_DELETE; + } + + ipf_dstlist_node_deref(softd, node); +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_dstlist_stats_get */ +/* Returns: int - 0 = success, else error */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* arg(I) - pointer to local context to use */ +/* op(I) - pointer to lookup operation data */ +/* */ +/* Return the current statistics for destination lists. This may be for all */ +/* of them or just information pertaining to a particular table. */ +/* ------------------------------------------------------------------------ */ +/*ARGSUSED*/ +static int +ipf_dstlist_stats_get(softc, arg, op) + ipf_main_softc_t *softc; + void *arg; + iplookupop_t *op; +{ + ipf_dstl_softc_t *softd = arg; + ipf_dstl_stat_t stats; + int unit, i, err = 0; + + if (op->iplo_size != sizeof(ipf_dstl_stat_t)) { + IPFERROR(120023); + return EINVAL; + } + + stats = softd->stats; + unit = op->iplo_unit; + if (unit == IPL_LOGALL) { + for (i = 0; i <= IPL_LOGMAX; i++) + stats.ipls_list[i] = softd->dstlist[i]; + } else if (unit >= 0 && unit <= IPL_LOGMAX) { + void *ptr; + + if (op->iplo_name[0] != '\0') + ptr = ipf_dstlist_table_find(softd, unit, + op->iplo_name); + else + ptr = softd->dstlist[unit + 1]; + stats.ipls_list[unit] = ptr; + } else { + IPFERROR(120024); + err = EINVAL; + } + + if (err == 0) { + err = COPYOUT(&stats, op->iplo_struct, sizeof(stats)); + if (err != 0) { + IPFERROR(120025); + return EFAULT; + } + } + return 0; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_dstlist_table_add */ +/* Returns: int - 0 = success, else error */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* arg(I) - pointer to local context to use */ +/* op(I) - pointer to lookup operation data */ +/* */ +/* Add a new destination table to the list of those available for the given */ +/* device. Because we seldom operate on these objects (find/add/delete), */ +/* they are just kept in a simple linked list. */ +/* ------------------------------------------------------------------------ */ +static int +ipf_dstlist_table_add(softc, arg, op) + ipf_main_softc_t *softc; + void *arg; + iplookupop_t *op; +{ + ipf_dstl_softc_t *softd = arg; + ippool_dst_t user, *d, *new; + int unit, err; + + d = ipf_dstlist_table_find(arg, op->iplo_unit, op->iplo_name); + if (d != NULL) { + IPFERROR(120013); + return EEXIST; + } + + err = COPYIN(op->iplo_struct, &user, sizeof(user)); + if (err != 0) { + IPFERROR(120021); + return EFAULT; + } + + KMALLOC(new, ippool_dst_t *); + if (new == NULL) { + softd->stats.ipls_nomem++; + IPFERROR(120014); + return ENOMEM; + } + bzero((char *)new, sizeof(*new)); + + MUTEX_INIT(&new->ipld_lock, "ipf dst table lock"); + + strncpy(new->ipld_name, op->iplo_name, FR_GROUPLEN); + unit = op->iplo_unit; + new->ipld_unit = unit; + new->ipld_policy = user.ipld_policy; + new->ipld_seed = ipf_random(); + new->ipld_ref = 1; + + new->ipld_pnext = softd->tails[unit + 1]; + *softd->tails[unit + 1] = new; + softd->tails[unit + 1] = &new->ipld_next; + softd->stats.ipls_numlists++; + + return 0; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_dstlist_table_del */ +/* Returns: int - 0 = success, else error */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* arg(I) - pointer to local context to use */ +/* op(I) - pointer to lookup operation data */ +/* */ +/* Find a named destinstion list table and delete it. If there are other */ +/* references to it, the caller isn't told. */ +/* ------------------------------------------------------------------------ */ +static int +ipf_dstlist_table_del(softc, arg, op) + ipf_main_softc_t *softc; + void *arg; + iplookupop_t *op; +{ + ippool_dst_t *d; + + d = ipf_dstlist_table_find(arg, op->iplo_unit, op->iplo_name); + if (d == NULL) { + IPFERROR(120015); + return ESRCH; + } + + if (d->ipld_dests != NULL) { + IPFERROR(120016); + return EBUSY; + } + + ipf_dstlist_table_remove(softc, arg, d); + + return 0; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_dstlist_table_remove */ +/* Returns: Nil */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* softd(I) - pointer to the destination list context */ +/* d(I) - pointer to destination list */ +/* */ +/* Remove a given destination list from existance. While the IPDST_DELETE */ +/* flag is set every time we call this function and the reference count is */ +/* non-zero, the "numdereflists" counter is always incremented because the */ +/* decision about whether it will be freed or not is not made here. This */ +/* means that the only action the code can take here is to treat it as if */ +/* it will become a detached. */ +/* ------------------------------------------------------------------------ */ +static void +ipf_dstlist_table_remove(softc, softd, d) + ipf_main_softc_t *softc; + ipf_dstl_softc_t *softd; + ippool_dst_t *d; +{ + + if (softd->tails[d->ipld_unit + 1] == &d->ipld_next) + softd->tails[d->ipld_unit + 1] = d->ipld_pnext; + + if (d->ipld_pnext != NULL) + *d->ipld_pnext = d->ipld_next; + if (d->ipld_next != NULL) + d->ipld_next->ipld_pnext = d->ipld_pnext; + d->ipld_pnext = NULL; + d->ipld_next = NULL; + + ipf_dstlist_table_clearnodes(softd, d); + + softd->stats.ipls_numdereflists++; + d->ipld_flags |= IPDST_DELETE; + + ipf_dstlist_table_deref(softc, softd, d); +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_dstlist_table_free */ +/* Returns: Nil */ +/* Parameters: softd(I) - pointer to the destination list context */ +/* d(I) - pointer to destination list */ +/* */ +/* Free up a destination list data structure and any other memory that was */ +/* directly allocated as part of creating it. Individual destination list */ +/* nodes are not freed. It is assumed the caller will have already emptied */ +/* the destination list. */ +/* ------------------------------------------------------------------------ */ +static void +ipf_dstlist_table_free(softd, d) + ipf_dstl_softc_t *softd; + ippool_dst_t *d; +{ + MUTEX_DESTROY(&d->ipld_lock); + + if ((d->ipld_flags & IPDST_DELETE) != 0) + softd->stats.ipls_numdereflists--; + softd->stats.ipls_numlists--; + + if (d->ipld_dests != NULL) { + KFREES(d->ipld_dests, + d->ipld_maxnodes * sizeof(*d->ipld_dests)); + } + + KFREE(d); +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_dstlist_table_deref */ +/* Returns: int - 0 = success, else error */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* arg(I) - pointer to local context to use */ +/* op(I) - pointer to lookup operation data */ +/* */ +/* Drops the reference count on a destination list table object and free's */ +/* it if 0 has been reached. */ +/* ------------------------------------------------------------------------ */ +static int +ipf_dstlist_table_deref(softc, arg, table) + ipf_main_softc_t *softc; + void *arg; + void *table; +{ + ippool_dst_t *d = table; + + d->ipld_ref--; + if (d->ipld_ref > 0) + return d->ipld_ref; + + ipf_dstlist_table_free(arg, d); + + return 0; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_dstlist_table_clearnodes */ +/* Returns: Nil */ +/* Parameters: softd(I) - pointer to the destination list context */ +/* dst(I) - pointer to destination list */ +/* */ +/* Free all of the destination nodes attached to the given table. */ +/* ------------------------------------------------------------------------ */ +static void +ipf_dstlist_table_clearnodes(softd, dst) + ipf_dstl_softc_t *softd; + ippool_dst_t *dst; +{ + ipf_dstnode_t *node; + + if (dst->ipld_dests == NULL) + return; + + while ((node = *dst->ipld_dests) != NULL) { + ipf_dstlist_node_free(softd, dst, node); + } +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_dstlist_table_find */ +/* Returns: int - 0 = success, else error */ +/* Parameters: arg(I) - pointer to local context to use */ +/* unit(I) - device we are working with */ +/* name(I) - destination table name to find */ +/* */ +/* Return a pointer to a destination table that matches the unit+name that */ +/* is passed in. */ +/* ------------------------------------------------------------------------ */ +static void * +ipf_dstlist_table_find(arg, unit, name) + void *arg; + int unit; + char *name; +{ + ipf_dstl_softc_t *softd = arg; + ippool_dst_t *d; + + for (d = softd->dstlist[unit + 1]; d != NULL; d = d->ipld_next) { + if ((d->ipld_unit == unit) && + !strncmp(d->ipld_name, name, FR_GROUPLEN)) { + return d; + } + } + + return NULL; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_dstlist_select_ref */ +/* Returns: void * - NULL = failure, else pointer to table */ +/* Parameters: arg(I) - pointer to local context to use */ +/* unit(I) - device we are working with */ +/* name(I) - destination table name to find */ +/* */ +/* Attempt to find a destination table that matches the name passed in and */ +/* if successful, bump up the reference count on it because we intend to */ +/* store the pointer to it somewhere else. */ +/* ------------------------------------------------------------------------ */ +static void * +ipf_dstlist_select_ref(arg, unit, name) + void *arg; + int unit; + char *name; +{ + ippool_dst_t *d; + + d = ipf_dstlist_table_find(arg, unit, name); + if (d != NULL) { + MUTEX_ENTER(&d->ipld_lock); + d->ipld_ref++; + MUTEX_EXIT(&d->ipld_lock); + } + return d; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_dstlist_select */ +/* Returns: void * - NULL = failure, else pointer to table */ +/* Parameters: fin(I) - pointer to packet information */ +/* d(I) - pointer to destination list */ +/* */ +/* Find the next node in the destination list to be used according to the */ +/* defined policy. Of these, "connection" is the most expensive policy to */ +/* implement as it always looks for the node with the least number of */ +/* connections associated with it. */ +/* */ +/* The hashes exclude the port numbers so that all protocols map to the */ +/* same destination. Otherwise, someone doing a ping would target a */ +/* different server than their TCP connection, etc. MD-5 is used to */ +/* transform the addressese into something random that the other end could */ +/* not easily guess and use in an attack. ipld_seed introduces an unknown */ +/* into the hash calculation to increase the difficult of an attacker */ +/* guessing the bucket. */ +/* */ +/* One final comment: mixing different address families in a single pool */ +/* will currently result in failures as the address family of the node is */ +/* only matched up with that in the packet as the last step. While this can */ +/* be coded around for the weighted connection and round-robin models, it */ +/* cannot be supported for the hash/random models as they do not search and */ +/* nor is the algorithm conducive to searching. */ +/* ------------------------------------------------------------------------ */ +static ipf_dstnode_t * +ipf_dstlist_select(fin, d) + fr_info_t *fin; + ippool_dst_t *d; +{ + ipf_dstnode_t *node, *sel; + int connects; + u_32_t hash[4]; + MD5_CTX ctx; + int family; + int x; + + if (d->ipld_dests == NULL || *d->ipld_dests == NULL) + return NULL; + + family = fin->fin_family; + + MUTEX_ENTER(&d->ipld_lock); + + switch (d->ipld_policy) + { + case IPLDP_ROUNDROBIN: + sel = d->ipld_selected; + if (sel == NULL) { + sel = *d->ipld_dests; + } else { + sel = sel->ipfd_next; + if (sel == NULL) + sel = *d->ipld_dests; + } + break; + + case IPLDP_CONNECTION: + if (d->ipld_selected == NULL) { + sel = *d->ipld_dests; + break; + } + + sel = d->ipld_selected; + connects = 0x7fffffff; + node = sel->ipfd_next; + if (node == NULL) + node = *d->ipld_dests; + while (node != d->ipld_selected) { + if (node->ipfd_states == 0) { + sel = node; + break; + } + if (node->ipfd_states < connects) { + sel = node; + connects = node->ipfd_states; + } + node = node->ipfd_next; + if (node == NULL) + node = *d->ipld_dests; + } + break; + + case IPLDP_RANDOM : + x = ipf_random() % d->ipld_nodes; + sel = d->ipld_dests[x]; + break; + + case IPLDP_HASHED : + MD5Init(&ctx); + MD5Update(&ctx, (u_char *)&d->ipld_seed, sizeof(d->ipld_seed)); + MD5Update(&ctx, (u_char *)&fin->fin_src6, + sizeof(fin->fin_src6)); + MD5Update(&ctx, (u_char *)&fin->fin_dst6, + sizeof(fin->fin_dst6)); + MD5Final((u_char *)hash, &ctx); + x = hash[0] % d->ipld_nodes; + sel = d->ipld_dests[x]; + break; + + case IPLDP_SRCHASH : + MD5Init(&ctx); + MD5Update(&ctx, (u_char *)&d->ipld_seed, sizeof(d->ipld_seed)); + MD5Update(&ctx, (u_char *)&fin->fin_src6, + sizeof(fin->fin_src6)); + MD5Final((u_char *)hash, &ctx); + x = hash[0] % d->ipld_nodes; + sel = d->ipld_dests[x]; + break; + + case IPLDP_DSTHASH : + MD5Init(&ctx); + MD5Update(&ctx, (u_char *)&d->ipld_seed, sizeof(d->ipld_seed)); + MD5Update(&ctx, (u_char *)&fin->fin_dst6, + sizeof(fin->fin_dst6)); + MD5Final((u_char *)hash, &ctx); + x = hash[0] % d->ipld_nodes; + sel = d->ipld_dests[x]; + break; + + default : + sel = NULL; + break; + } + + if (sel->ipfd_dest.fd_addr.adf_family != family) + sel = NULL; + d->ipld_selected = sel; + + MUTEX_EXIT(&d->ipld_lock); + + return sel; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_dstlist_select_node */ +/* Returns: int - -1 == failure, 0 == success */ +/* Parameters: fin(I) - pointer to packet information */ +/* group(I) - destination pool to search */ +/* addr(I) - pointer to store selected address */ +/* pfdp(O) - pointer to storage for selected destination node */ +/* */ +/* This function is only responsible for obtaining the next IP address for */ +/* use and storing it in the caller's address space (addr). "addr" is only */ +/* used for storage if pfdp is NULL. No permanent reference is currently */ +/* kept on the node. */ +/* ------------------------------------------------------------------------ */ +int +ipf_dstlist_select_node(fin, group, addr, pfdp) + fr_info_t *fin; + void *group; + u_32_t *addr; + frdest_t *pfdp; +{ +#ifdef USE_MUTEXES + ipf_main_softc_t *softc = fin->fin_main_soft; +#endif + ippool_dst_t *d = group; + ipf_dstnode_t *node; + frdest_t *fdp; + + READ_ENTER(&softc->ipf_poolrw); + + node = ipf_dstlist_select(fin, d); + if (node == NULL) { + RWLOCK_EXIT(&softc->ipf_poolrw); + return -1; + } + + if (pfdp != NULL) { + bcopy(&node->ipfd_dest, pfdp, sizeof(*pfdp)); + } else { + if (fin->fin_family == AF_INET) { + addr[0] = node->ipfd_dest.fd_addr.adf_addr.i6[0]; + } else if (fin->fin_family == AF_INET6) { + addr[0] = node->ipfd_dest.fd_addr.adf_addr.i6[0]; + addr[1] = node->ipfd_dest.fd_addr.adf_addr.i6[1]; + addr[2] = node->ipfd_dest.fd_addr.adf_addr.i6[2]; + addr[3] = node->ipfd_dest.fd_addr.adf_addr.i6[3]; + } + } + + fdp = &node->ipfd_dest; + if (fdp->fd_ptr == NULL) + fdp->fd_ptr = fin->fin_ifp; + + MUTEX_ENTER(&node->ipfd_lock); + node->ipfd_states++; + MUTEX_EXIT(&node->ipfd_lock); + + RWLOCK_EXIT(&softc->ipf_poolrw); + + return 0; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_dstlist_expire */ +/* Returns: Nil */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* arg(I) - pointer to local context to use */ +/* */ +/* There are currently no objects to expire in destination lists. */ +/* ------------------------------------------------------------------------ */ +static void +ipf_dstlist_expire(softc, arg) + ipf_main_softc_t *softc; + void *arg; +{ + return; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_dstlist_sync */ +/* Returns: Nil */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* arg(I) - pointer to local context to use */ +/* */ +/* When a network interface appears or disappears, we need to revalidate */ +/* all of the network interface names that have been configured as a target */ +/* in a destination list. */ +/* ------------------------------------------------------------------------ */ +void +ipf_dstlist_sync(softc, arg) + ipf_main_softc_t *softc; + void *arg; +{ + ipf_dstl_softc_t *softd = arg; + ipf_dstnode_t *node; + ippool_dst_t *list; + int i; + int j; + + for (i = 0; i < IPL_LOGMAX; i++) { + for (list = softd->dstlist[i]; list != NULL; + list = list->ipld_next) { + for (j = 0; j < list->ipld_maxnodes; j++) { + node = list->ipld_dests[j]; + if (node == NULL) + continue; + if (node->ipfd_dest.fd_name == -1) + continue; + (void) ipf_resolvedest(softc, + node->ipfd_names, + &node->ipfd_dest, + AF_INET); + } + } + } +} diff --git a/sys/contrib/ipfilter/netinet/ip_dstlist.h b/sys/contrib/ipfilter/netinet/ip_dstlist.h new file mode 100644 index 00000000000..e2885e5c47a --- /dev/null +++ b/sys/contrib/ipfilter/netinet/ip_dstlist.h @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + * + * $Id: ip_dstlist.h,v 1.5.2.6 2012/07/22 08:04:23 darren_r Exp $ + */ + +#ifndef __IP_DSTLIST_H__ +#define __IP_DSTLIST_H__ + +typedef struct ipf_dstnode { + struct ipf_dstnode *ipfd_next; + struct ipf_dstnode **ipfd_pnext; + ipfmutex_t ipfd_lock; + frdest_t ipfd_dest; + u_long ipfd_syncat; + int ipfd_flags; + int ipfd_size; + int ipfd_states; + int ipfd_ref; + int ipfd_uid; + char ipfd_names[1]; +} ipf_dstnode_t; + +typedef enum ippool_policy_e { + IPLDP_NONE = 0, + IPLDP_ROUNDROBIN, + IPLDP_CONNECTION, + IPLDP_RANDOM, + IPLDP_HASHED, + IPLDP_SRCHASH, + IPLDP_DSTHASH +} ippool_policy_t; + +typedef struct ippool_dst { + struct ippool_dst *ipld_next; + struct ippool_dst **ipld_pnext; + ipfmutex_t ipld_lock; + int ipld_seed; + int ipld_unit; + int ipld_ref; + int ipld_flags; + int ipld_nodes; + int ipld_maxnodes; + ippool_policy_t ipld_policy; + ipf_dstnode_t **ipld_dests; + ipf_dstnode_t *ipld_selected; + char ipld_name[FR_GROUPLEN]; +} ippool_dst_t; + +#define IPDST_DELETE 0x01 + +typedef struct dstlist_stat_s { + void *ipls_list[LOOKUP_POOL_SZ]; + int ipls_numlists; + u_long ipls_nomem; + int ipls_numnodes; + int ipls_numdereflists; + int ipls_numderefnodes; +} ipf_dstl_stat_t; + +extern ipf_lookup_t ipf_dstlist_backend; + +extern int ipf_dstlist_select_node __P((fr_info_t *, void *, u_32_t *, + frdest_t *)); + +#endif /* __IP_DSTLIST_H__ */ diff --git a/sys/contrib/ipfilter/netinet/ip_fil.h b/sys/contrib/ipfilter/netinet/ip_fil.h index 0cd84b995a8..22a11c341c2 100644 --- a/sys/contrib/ipfilter/netinet/ip_fil.h +++ b/sys/contrib/ipfilter/netinet/ip_fil.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 1993-2001, 2003 by Darren Reed. + * Copyright (C) 2012 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. * @@ -12,6 +12,21 @@ #define __IP_FIL_H__ #include "netinet/ip_compat.h" +#include "netinet/ipf_rb.h" +#if NETBSD_GE_REV(104040000) +# include +#endif +#if defined(BSD) && defined(_KERNEL) +# if NETBSD_LT_REV(399000000) || defined(__osf__) || FREEBSD_LT_REV(500043) +# include +# else +# include +# endif +#endif + +#if !defined(linux) || !defined(_KERNEL) +# include +#endif #ifndef SOLARIS # define SOLARIS (defined(sun) && (defined(__svr4__) || defined(__SVR4))) @@ -63,6 +78,8 @@ # define SIOCIPFDELTOK _IOWR('r', 94, int) # define SIOCLOOKUPITER _IOWR('r', 95, struct ipfobj) # define SIOCGTQTAB _IOWR('r', 96, struct ipfobj) +# define SIOCMATCHFLUSH _IOWR('r', 97, struct ipfobj) +# define SIOCIPFINTERROR _IOR('r', 98, int) #else # define SIOCADAFR _IOW(r, 60, struct ipfobj) # define SIOCRMAFR _IOW(r, 61, struct ipfobj) @@ -101,6 +118,8 @@ # define SIOCIPFDELTOK _IOWR(r, 94, int) # define SIOCLOOKUPITER _IOWR(r, 95, struct ipfobj) # define SIOCGTQTAB _IOWR(r, 96, struct ipfobj) +# define SIOCMATCHFLUSH _IOWR(r, 97, struct ipfobj) +# define SIOCIPFINTERROR _IOR(r, 98, int) #endif #define SIOCADDFR SIOCADAFR #define SIOCDELFR SIOCRMAFR @@ -111,9 +130,10 @@ struct ipscan; struct ifnet; +struct ipf_main_softc_s; - -typedef int (* lookupfunc_t) __P((void *, int, void *)); +typedef int (* lookupfunc_t) __P((struct ipf_main_softc_s *, void *, + int, void *, u_int)); /* * i6addr is used as a container for both IPv4 and IPv6 addresses, as well @@ -129,7 +149,7 @@ typedef union i6addr { struct { u_short type; u_short subtype; - char label[12]; + int name; } i6un; } i6addr_t; #else @@ -141,14 +161,14 @@ typedef union i6addr { struct { u_short type; u_short subtype; - char label[12]; + int name; } i6un; } i6addr_t; #endif #define in4_addr in4.s_addr #define iplookupnum i6[1] -#define iplookupname i6un.label +#define iplookupname i6un.name #define iplookuptype i6un.type #define iplookupsubtype i6un.subtype /* @@ -172,17 +192,25 @@ typedef union i6addr { (I61(a) != I61(b)) || (I60(a) != I60(b))) #define IP6_ISZERO(a) ((I60(a) | I61(a) | I62(a) | I63(a)) == 0) #define IP6_NOTZERO(a) ((I60(a) | I61(a) | I62(a) | I63(a)) != 0) -#define IP6_GT(a,b) (HI60(a) > HI60(b) || (HI60(a) == HI60(b) && \ - (HI61(a) > HI61(b) || (HI61(a) == HI61(b) && \ - (HI62(a) > HI62(b) || (HI62(a) == HI62(b) && \ - HI63(a) > HI63(b))))))) -#define IP6_LT(a,b) (HI60(a) < HI60(b) || (HI60(a) == HI60(b) && \ - (HI61(a) < HI61(b) || (HI61(a) == HI61(b) && \ - (HI62(a) < HI62(b) || (HI62(a) == HI62(b) && \ - HI63(a) < HI63(b))))))) +#define IP6_ISONES(a) ((I63(a) == 0xffffffff) && (I62(a) == 0xffffffff) && \ + (I61(a) == 0xffffffff) && (I60(a) == 0xffffffff)) +#define IP6_GT(a,b) (ntohl(HI60(a)) > ntohl(HI60(b)) || \ + (HI60(a) == HI60(b) && \ + (ntohl(HI61(a)) > ntohl(HI61(b)) || \ + (HI61(a) == HI61(b) && \ + (ntohl(HI62(a)) > ntohl(HI62(b)) || \ + (HI62(a) == HI62(b) && \ + ntohl(HI63(a)) > ntohl(HI63(b)))))))) +#define IP6_LT(a,b) (ntohl(HI60(a)) < ntohl(HI60(b)) || \ + (HI60(a) == HI60(b) && \ + (ntohl(HI61(a)) < ntohl(HI61(b)) || \ + (HI61(a) == HI61(b) && \ + (ntohl(HI62(a)) < ntohl(HI62(b)) || \ + (HI62(a) == HI62(b) && \ + ntohl(HI63(a)) < ntohl(HI63(b)))))))) #define NLADD(n,x) htonl(ntohl(n) + (x)) #define IP6_INC(a) \ - { u_32_t *_i6 = (u_32_t *)(a); \ + do { u_32_t *_i6 = (u_32_t *)(a); \ _i6[3] = NLADD(_i6[3], 1); \ if (_i6[3] == 0) { \ _i6[2] = NLADD(_i6[2], 1); \ @@ -193,9 +221,9 @@ typedef union i6addr { } \ } \ } \ - } + } while (0) #define IP6_ADD(a,x,d) \ - { i6addr_t *_s = (i6addr_t *)(a); \ + do { i6addr_t *_s = (i6addr_t *)(a); \ i6addr_t *_d = (i6addr_t *)(d); \ _d->i6[0] = NLADD(_s->i6[0], x); \ if (ntohl(_d->i6[0]) < ntohl(_s->i6[0])) { \ @@ -207,26 +235,65 @@ typedef union i6addr { } \ } \ } \ - } -#define IP6_AND(a,b,d) { i6addr_t *_s1 = (i6addr_t *)(a); \ - i6addr_t *_s2 = (i6addr_t *)(d); \ + } while (0) +#define IP6_AND(a,b,d) do { i6addr_t *_s1 = (i6addr_t *)(a); \ + i6addr_t *_s2 = (i6addr_t *)(b); \ i6addr_t *_d = (i6addr_t *)(d); \ _d->i6[0] = _s1->i6[0] & _s2->i6[0]; \ _d->i6[1] = _s1->i6[1] & _s2->i6[1]; \ _d->i6[2] = _s1->i6[2] & _s2->i6[2]; \ _d->i6[3] = _s1->i6[3] & _s2->i6[3]; \ - } + } while (0) +#define IP6_ANDASSIGN(a,m) \ + do { i6addr_t *_d = (i6addr_t *)(a); \ + i6addr_t *_m = (i6addr_t *)(m); \ + _d->i6[0] &= _m->i6[0]; \ + _d->i6[1] &= _m->i6[1]; \ + _d->i6[2] &= _m->i6[2]; \ + _d->i6[3] &= _m->i6[3]; \ + } while (0) +#define IP6_MASKEQ(a,m,b) \ + (((I60(a) & I60(m)) == I60(b)) && \ + ((I61(a) & I61(m)) == I61(b)) && \ + ((I62(a) & I62(m)) == I62(b)) && \ + ((I63(a) & I63(m)) == I63(b))) +#define IP6_MASKNEQ(a,m,b) \ + (((I60(a) & I60(m)) != I60(b)) || \ + ((I61(a) & I61(m)) != I61(b)) || \ + ((I62(a) & I62(m)) != I62(b)) || \ + ((I63(a) & I63(m)) != I63(b))) #define IP6_MERGE(a,b,c) \ - { i6addr_t *_d, *_s1, *_s2; \ + do { i6addr_t *_d, *_s1, *_s2; \ _d = (i6addr_t *)(a); \ _s1 = (i6addr_t *)(b); \ _s2 = (i6addr_t *)(c); \ _d->i6[0] |= _s1->i6[0] & ~_s2->i6[0]; \ _d->i6[1] |= _s1->i6[1] & ~_s2->i6[1]; \ _d->i6[2] |= _s1->i6[2] & ~_s2->i6[2]; \ - _d->i6[2] |= _s1->i6[3] & ~_s2->i6[3]; \ - } + _d->i6[3] |= _s1->i6[3] & ~_s2->i6[3]; \ + } while (0) +#define IP6_MASK(a,b,c) \ + do { i6addr_t *_d, *_s1, *_s2; \ + _d = (i6addr_t *)(a); \ + _s1 = (i6addr_t *)(b); \ + _s2 = (i6addr_t *)(c); \ + _d->i6[0] = _s1->i6[0] & ~_s2->i6[0]; \ + _d->i6[1] = _s1->i6[1] & ~_s2->i6[1]; \ + _d->i6[2] = _s1->i6[2] & ~_s2->i6[2]; \ + _d->i6[3] = _s1->i6[3] & ~_s2->i6[3]; \ + } while (0) +#define IP6_SETONES(a) \ + do { i6addr_t *_d = (i6addr_t *)(a); \ + _d->i6[0] = 0xffffffff; \ + _d->i6[1] = 0xffffffff; \ + _d->i6[2] = 0xffffffff; \ + _d->i6[3] = 0xffffffff; \ + } while (0) +typedef union ipso_u { + u_short ipso_ripso[2]; + u_32_t ipso_doi; +} ipso_t; typedef struct fr_ip { u_32_t fi_v:4; /* IP version */ @@ -237,11 +304,13 @@ typedef struct fr_ip { u_32_t fi_optmsk; /* bitmask composed from IP options */ i6addr_t fi_src; /* source address from packet */ i6addr_t fi_dst; /* destination address from packet */ - u_short fi_secmsk; /* bitmask composed from IP security options */ - u_short fi_auth; /* authentication code from IP sec. options */ + ipso_t fi_ipso; /* IP security options */ u_32_t fi_flx; /* packet flags */ u_32_t fi_tcpmsk; /* TCP options set/reset */ - u_32_t fi_res1; /* RESERVED */ + u_32_t fi_ports[2]; /* TCP ports */ + u_char fi_tcpf; /* TCP flags */ + u_char fi_sensitivity; + u_char fi_xxx[2]; /* pad */ } fr_ip_t; /* @@ -263,16 +332,23 @@ typedef struct fr_ip { #define FI_FRAGBODY 0x2000 #define FI_BADSRC 0x4000 #define FI_LOWTTL 0x8000 -#define FI_CMP 0xcf03 /* Not FI_FRAG,FI_NATED,FI_FRAGTAIL,broadcast */ +#define FI_CMP 0x5cfe3 /* Not FI_FRAG,FI_NATED,FI_FRAGTAIL */ #define FI_ICMPCMP 0x0003 /* Flags we can check for ICMP error packets */ -#define FI_WITH 0xeffe /* Not FI_TCPUDP */ +#define FI_WITH 0x5effe /* Not FI_TCPUDP */ #define FI_V6EXTHDR 0x10000 #define FI_COALESCE 0x20000 #define FI_NEWNAT 0x40000 +#define FI_ICMPQUERY 0x80000 +#define FI_ENCAP 0x100000 /* encap/decap with NAT */ +#define FI_AH 0x200000 /* AH header present */ +#define FI_DOCKSUM 0x10000000 /* Proxy wants L4 recalculation */ #define FI_NOCKSUM 0x20000000 /* don't do a L4 checksum validation */ -#define FI_DONTCACHE 0x40000000 /* don't cache the result */ +#define FI_NOWILD 0x40000000 /* Do not do wildcard searches */ #define FI_IGNORE 0x80000000 +#define fi_secmsk fi_ipso.ipso_ripso[0] +#define fi_auth fi_ipso.ipso_ripso[1] +#define fi_doi fi_ipso.ipso_doi #define fi_saddr fi_src.in4.s_addr #define fi_daddr fi_dst.in4.s_addr #define fi_srcnum fi_src.iplookupnum @@ -303,37 +379,87 @@ typedef struct fr_ip { #define SI_NEWFR 0x00001000 #define SI_CLONE 0x00002000 #define SI_CLONED 0x00004000 +#define SI_NEWCLONE 0x00008000 +typedef struct { + u_short fda_ports[2]; + u_char fda_tcpf; /* TCP header flags (SYN, ACK, etc) */ +} frdat_t; + +typedef enum fr_breasons_e { + FRB_BLOCKED = 0, + FRB_LOGFAIL = 1, + FRB_PPSRATE = 2, + FRB_JUMBO = 3, + FRB_MAKEFRIP = 4, + FRB_STATEADD = 5, + FRB_UPDATEIPID = 6, + FRB_LOGFAIL2 = 7, + FRB_DECAPFRIP = 8, + FRB_AUTHNEW = 9, + FRB_AUTHCAPTURE = 10, + FRB_COALESCE = 11, + FRB_PULLUP = 12, + FRB_AUTHFEEDBACK = 13, + FRB_BADFRAG = 14, + FRB_NATV4 = 15, + FRB_NATV6 = 16, +} fr_breason_t; + +#define FRB_MAX_VALUE 16 + +typedef enum ipf_cksum_e { + FI_CK_BAD = -1, + FI_CK_NEEDED = 0, + FI_CK_SUMOK = 1, + FI_CK_L4PART = 2, + FI_CK_L4FULL = 4 +} ipf_cksum_t; typedef struct fr_info { + void *fin_main_soft; void *fin_ifp; /* interface packet is `on' */ - fr_ip_t fin_fi; /* IP Packet summary */ - union { - u_short fid_16[2]; /* TCP/UDP ports, ICMP code/type */ - u_32_t fid_32; - } fin_dat; - int fin_out; /* in or out ? 1 == out, 0 == in */ - int fin_rev; /* state only: 1 = reverse */ - u_short fin_hlen; /* length of IP header in bytes */ - u_char fin_tcpf; /* TCP header flags (SYN, ACK, etc) */ - u_char fin_icode; /* ICMP error to return */ - u_32_t fin_rule; /* rule # last matched */ - char fin_group[FR_GROUPLEN]; /* group number, -1 for none */ struct frentry *fin_fr; /* last matching rule */ - void *fin_dp; /* start of data past IP header */ + int fin_out; /* in or out ? 1 == out, 0 == in */ + fr_ip_t fin_fi; /* IP Packet summary */ + frdat_t fin_dat; /* TCP/UDP ports, ICMP code/type */ int fin_dlen; /* length of data portion of packet */ int fin_plen; + u_32_t fin_rule; /* rule # last matched */ + u_short fin_hlen; /* length of IP header in bytes */ + char fin_group[FR_GROUPLEN]; /* group number, -1 for none */ + void *fin_dp; /* start of data past IP header */ + /* + * Fields after fin_dp aren't used for compression of log records. + * fin_fi contains the IP version (fin_family) + * fin_rule isn't included because adding a new rule can change it but + * not change fin_fr. fin_rule is the rule number reported. + * It isn't necessary to include fin_crc because that is checked + * for explicitly, before calling bcmp. + */ + u_32_t fin_crc; /* Simple calculation for logging */ + int fin_family; /* AF_INET, etc. */ + int fin_icode; /* ICMP error to return */ + int fin_mtu; /* MTU input for ICMP need-frag */ + int fin_rev; /* state only: 1 = reverse */ int fin_ipoff; /* # bytes from buffer start to hdr */ - u_short fin_id; /* IP packet id field */ + u_32_t fin_id; /* IP packet id field */ + u_short fin_l4hlen; /* length of L4 header, if known */ u_short fin_off; int fin_depth; /* Group nesting depth */ int fin_error; /* Error code to return */ - int fin_cksum; /* -1 bad, 1 good, 0 not done */ - void *fin_nat; - void *fin_state; + ipf_cksum_t fin_cksum; /* -1 = bad, 1 = good, 0 = not done */ + fr_breason_t fin_reason; /* why auto blocked */ + u_int fin_pktnum; void *fin_nattag; - void *fin_exthdr; - ip_t *fin_ip; + struct frdest *fin_dif; + struct frdest *fin_tif; + union { + ip_t *fip_ip; +#ifdef USE_INET6 + ip6_t *fip_ip6; +#endif + } fin_ipu; mb_t **fin_mp; /* pointer to pointer to mbuf */ mb_t *fin_m; /* pointer to mbuf */ #ifdef MENTAT @@ -344,35 +470,42 @@ typedef struct fr_info { #ifdef __sgi void *fin_hbuf; #endif + void *fin_fraghdr; /* pointer to start of ipv6 frag hdr */ } fr_info_t; +#define fin_ip fin_ipu.fip_ip +#define fin_ip6 fin_ipu.fip_ip6 #define fin_v fin_fi.fi_v #define fin_p fin_fi.fi_p #define fin_flx fin_fi.fi_flx #define fin_optmsk fin_fi.fi_optmsk #define fin_secmsk fin_fi.fi_secmsk +#define fin_doi fin_fi.fi_doi #define fin_auth fin_fi.fi_auth #define fin_src fin_fi.fi_src.in4 -#define fin_src6 fin_fi.fi_src.in6 #define fin_saddr fin_fi.fi_saddr #define fin_dst fin_fi.fi_dst.in4 -#define fin_dst6 fin_fi.fi_dst.in6 #define fin_daddr fin_fi.fi_daddr -#define fin_data fin_dat.fid_16 -#define fin_sport fin_dat.fid_16[0] -#define fin_dport fin_dat.fid_16[1] -#define fin_ports fin_dat.fid_32 +#define fin_data fin_fi.fi_ports +#define fin_sport fin_fi.fi_ports[0] +#define fin_dport fin_fi.fi_ports[1] +#define fin_tcpf fin_fi.fi_tcpf +#define fin_src6 fin_fi.fi_src +#define fin_dst6 fin_fi.fi_dst +#define fin_srcip6 fin_fi.fi_src.in6 +#define fin_dstip6 fin_fi.fi_dst.in6 #define IPF_IN 0 #define IPF_OUT 1 typedef struct frentry *(*ipfunc_t) __P((fr_info_t *, u_32_t *)); -typedef int (*ipfuncinit_t) __P((struct frentry *)); +typedef int (*ipfuncinit_t) __P((struct ipf_main_softc_s *, struct frentry *)); typedef struct ipfunc_resolve { char ipfu_name[32]; ipfunc_t ipfu_addr; ipfuncinit_t ipfu_init; + ipfuncinit_t ipfu_fini; } ipfunc_resolve_t; /* @@ -401,39 +534,78 @@ typedef struct { #define ipt_tag ipt_un.iptu_tag #define ipt_num ipt_un.iptu_num +/* + * Structure to define address for pool lookups. + */ +typedef struct { + u_char adf_len; + sa_family_t adf_family; + u_char adf_xxx[2]; + i6addr_t adf_addr; +} addrfamily_t; + +RBI_LINK(ipf_rb, host_node_s); + +typedef struct host_node_s { + RBI_FIELD(ipf_rb) hn_entry; + addrfamily_t hn_addr; + int hn_active; +} host_node_t; + +typedef RBI_HEAD(ipf_rb, host_node_s) ipf_rb_head_t; + +typedef struct host_track_s { + ipf_rb_head_t ht_root; + int ht_max_nodes; + int ht_max_per_node; + int ht_netmask; + int ht_cur_nodes; +} host_track_t; + + +typedef enum fr_dtypes_e { + FRD_NORMAL = 0, + FRD_DSTLIST +} fr_dtypes_t; /* * This structure is used to hold information about the next hop for where * to forward a packet. */ typedef struct frdest { - void *fd_ifp; - i6addr_t fd_ip6; - char fd_ifname[LIFNAMSIZ]; + void *fd_ptr; + addrfamily_t fd_addr; + fr_dtypes_t fd_type; + int fd_name; + int fd_local; } frdest_t; +#define fd_ip6 fd_addr.adf_addr #define fd_ip fd_ip6.in4 +typedef enum fr_ctypes_e { + FR_NONE = 0, + FR_EQUAL, + FR_NEQUAL, + FR_LESST, + FR_GREATERT, + FR_LESSTE, + FR_GREATERTE, + FR_OUTRANGE, + FR_INRANGE, + FR_INCRANGE +} fr_ctypes_t; + /* * This structure holds information about a port comparison. */ typedef struct frpcmp { - int frp_cmp; /* data for port comparisons */ - u_short frp_port; /* top port for <> and >< */ - u_short frp_top; /* top port for <> and >< */ + fr_ctypes_t frp_cmp; /* data for port comparisons */ + u_32_t frp_port; /* top port for <> and >< */ + u_32_t frp_top; /* top port for <> and >< */ } frpcmp_t; -#define FR_NONE 0 -#define FR_EQUAL 1 -#define FR_NEQUAL 2 -#define FR_LESST 3 -#define FR_GREATERT 4 -#define FR_LESSTE 5 -#define FR_GREATERTE 6 -#define FR_OUTRANGE 7 -#define FR_INRANGE 8 -#define FR_INCRANGE 9 /* * Structure containing all the relevant TCP things that can be checked in @@ -455,23 +627,37 @@ typedef struct frtuc { #define FR_TCPFMAX 0x3f +typedef enum fr_atypes_e { + FRI_NONE = -1, /* For LHS of NAT */ + FRI_NORMAL = 0, /* Normal address */ + FRI_DYNAMIC, /* dynamic address */ + FRI_LOOKUP, /* address is a pool # */ + FRI_RANGE, /* address/mask is a range */ + FRI_NETWORK, /* network address from if */ + FRI_BROADCAST, /* broadcast address from if */ + FRI_PEERADDR, /* Peer address for P-to-P */ + FRI_NETMASKED, /* network address with netmask from if */ + FRI_SPLIT, /* For NAT compatibility */ + FRI_INTERFACE /* address is based on interface name */ +} fr_atypes_t; + /* * This structure makes up what is considered to be the IPFilter specific * matching components of a filter rule, as opposed to the data structures * used to define the result which are in frentry_t and not here. */ typedef struct fripf { - fr_ip_t fri_ip; - fr_ip_t fri_mip; /* mask structure */ + fr_ip_t fri_ip; + fr_ip_t fri_mip; /* mask structure */ - u_short fri_icmpm; /* data for ICMP packets (mask) */ - u_short fri_icmp; + u_short fri_icmpm; /* data for ICMP packets (mask) */ + u_short fri_icmp; - frtuc_t fri_tuc; - int fri_satype; /* addres type */ - int fri_datype; /* addres type */ - int fri_sifpidx; /* doing dynamic addressing */ - int fri_difpidx; /* index into fr_ifps[] to use when */ + frtuc_t fri_tuc; + fr_atypes_t fri_satype; /* addres type */ + fr_atypes_t fri_datype; /* addres type */ + int fri_sifpidx; /* doing dynamic addressing */ + int fri_difpidx; /* index into fr_ifps[] to use when */ } fripf_t; #define fri_dlookup fri_mip.fi_dst @@ -483,28 +669,42 @@ typedef struct fripf { #define fri_dstptr fri_mip.fi_dstptr #define fri_srcptr fri_mip.fi_srcptr -#define FRI_NORMAL 0 /* Normal address */ -#define FRI_DYNAMIC 1 /* dynamic address */ -#define FRI_LOOKUP 2 /* address is a pool # */ -#define FRI_RANGE 3 /* address/mask is a range */ -#define FRI_NETWORK 4 /* network address from if */ -#define FRI_BROADCAST 5 /* broadcast address from if */ -#define FRI_PEERADDR 6 /* Peer address for P-to-P */ -#define FRI_NETMASKED 7 /* network address with netmask from if */ +typedef enum fr_rtypes_e { + FR_T_NONE = 0, + FR_T_IPF, /* IPF structures */ + FR_T_BPFOPC, /* BPF opcode */ + FR_T_CALLFUNC, /* callout to function in fr_func only */ + FR_T_COMPIPF, /* compiled C code */ + FR_T_IPFEXPR, /* IPF expression */ + FR_T_BUILTIN = 0x40000000, /* rule is in kernel space */ + FR_T_IPF_BUILTIN, + FR_T_BPFOPC_BUILTIN, + FR_T_CALLFUNC_BUILTIN, + FR_T_COMPIPF_BUILTIN, + FR_T_IPFEXPR_BUILTIN +} fr_rtypes_t; typedef struct frentry * (* frentfunc_t) __P((fr_info_t *)); typedef struct frentry { ipfmutex_t fr_lock; struct frentry *fr_next; - struct frentry **fr_grp; + struct frentry **fr_pnext; + struct frgroup *fr_grp; + struct frgroup *fr_grphead; + struct frgroup *fr_icmpgrp; struct ipscan *fr_isc; + struct frentry *fr_dnext; /* 2 fr_die linked list pointers */ + struct frentry **fr_pdnext; void *fr_ifas[4]; void *fr_ptr; /* for use with fr_arg */ - char *fr_comment; /* text comment for rule */ - int fr_ref; /* reference count - for grouping */ + int fr_comment; /* text comment for rule */ + int fr_size; /* size of this structure */ + int fr_ref; /* reference count */ int fr_statecnt; /* state count - for limit rules */ + u_32_t fr_die; /* only used on loading the rule */ + u_int fr_cksum; /* checksum on filter rules for performance */ /* * The line number from a file is here because we need to be able to * match the rule generated with ``grep rule ipf.conf | ipf -rf -'' @@ -521,13 +721,18 @@ typedef struct frentry { /* * For PPS rate limiting + * fr_lpu is used to always have the same size for this field, + * allocating 64bits for seconds and 32bits for milliseconds. */ - struct timeval fr_lastpkt; + union { + struct timeval frp_lastpkt; + char frp_bytes[12]; + } fr_lpu; int fr_curpps; union { void *fru_data; - caddr_t fru_caddr; + char *fru_caddr; fripf_t *fru_ipf; frentfunc_t fru_func; } fr_dun; @@ -538,29 +743,38 @@ typedef struct frentry { ipfunc_t fr_func; /* call this function */ int fr_dsize; int fr_pps; - int fr_statemax; /* max reference count */ - u_32_t fr_type; + fr_rtypes_t fr_type; u_32_t fr_flags; /* per-rule flags && options (see below) */ u_32_t fr_logtag; /* user defined log tag # */ u_32_t fr_collect; /* collection number */ - u_int fr_arg; /* misc. numeric arg for rule */ + u_int fr_arg; /* misc. numeric arg for rule */ u_int fr_loglevel; /* syslog log facility + priority */ - u_int fr_age[2]; /* non-TCP timeouts */ - u_char fr_v; + u_char fr_family; u_char fr_icode; /* return ICMP code */ - char fr_group[FR_GROUPLEN]; /* group to which this rule belongs */ - char fr_grhead[FR_GROUPLEN]; /* group # which this rule starts */ + int fr_group; /* group to which this rule belongs */ + int fr_grhead; /* group # which this rule starts */ + int fr_ifnames[4]; + int fr_isctag; + int fr_rpc; /* XID Filtering */ ipftag_t fr_nattag; - char fr_ifnames[4][LIFNAMSIZ]; - char fr_isctag[16]; frdest_t fr_tifs[2]; /* "to"/"reply-to" interface */ frdest_t fr_dif; /* duplicate packet interface */ /* - * This must be last and will change after loaded into the kernel. + * These are all options related to stateful filtering */ - u_int fr_cksum; /* checksum on filter rules for performance */ + host_track_t fr_srctrack; + int fr_nostatelog; + int fr_statemax; /* max reference count */ + int fr_icmphead; /* ICMP group for state options */ + u_int fr_age[2]; /* non-TCP state timeouts */ + /* + * How big is the name buffer at the end? + */ + int fr_namelen; + char fr_names[1]; } frentry_t; +#define fr_lastpkt fr_lpu.frp_lastpkt #define fr_caddr fr_dun.fru_caddr #define fr_data fr_dun.fru_data #define fr_dfunc fr_dun.fru_func @@ -589,12 +803,16 @@ typedef struct frentry { #define fr_stop fr_tuc.ftu_stop #define fr_dtop fr_tuc.ftu_dtop #define fr_dst fr_ip.fi_dst.in4 +#define fr_dst6 fr_ip.fi_dst #define fr_daddr fr_ip.fi_dst.in4.s_addr #define fr_src fr_ip.fi_src.in4 +#define fr_src6 fr_ip.fi_src #define fr_saddr fr_ip.fi_src.in4.s_addr #define fr_dmsk fr_mip.fi_dst.in4 +#define fr_dmsk6 fr_mip.fi_dst #define fr_dmask fr_mip.fi_dst.in4.s_addr #define fr_smsk fr_mip.fi_src.in4 +#define fr_smsk6 fr_mip.fi_src #define fr_smask fr_mip.fi_src.in4.s_addr #define fr_dstnum fr_ip.fi_dstnum #define fr_srcnum fr_ip.fi_srcnum @@ -616,10 +834,10 @@ typedef struct frentry { #define fr_secmask fr_mip.fi_secmsk #define fr_authbits fr_ip.fi_auth #define fr_authmask fr_mip.fi_auth +#define fr_doi fr_ip.fi_doi +#define fr_doimask fr_mip.fi_doi #define fr_flx fr_ip.fi_flx #define fr_mflx fr_mip.fi_flx -#define fr_ifname fr_ifnames[0] -#define fr_oifname fr_ifnames[2] #define fr_ifa fr_ifas[0] #define fr_oifa fr_ifas[2] #define fr_tif fr_tifs[0] @@ -627,33 +845,22 @@ typedef struct frentry { #define FR_NOLOGTAG 0 -#ifndef offsetof -#define offsetof(t,m) (int)((&((t *)0L)->m)) -#endif #define FR_CMPSIZ (sizeof(struct frentry) - \ offsetof(struct frentry, fr_func)) +#define FR_NAME(_f, _n) (_f)->fr_names + (_f)->_n -/* - * fr_type - */ -#define FR_T_NONE 0 -#define FR_T_IPF 1 /* IPF structures */ -#define FR_T_BPFOPC 2 /* BPF opcode */ -#define FR_T_CALLFUNC 3 /* callout to function in fr_func only */ -#define FR_T_COMPIPF 4 /* compiled C code */ -#define FR_T_BUILTIN 0x80000000 /* rule is in kernel space */ /* * fr_flags */ -#define FR_CALL 0x00000 /* call rule */ #define FR_BLOCK 0x00001 /* do not allow packet to pass */ #define FR_PASS 0x00002 /* allow packet to pass */ #define FR_AUTH 0x00003 /* use authentication */ #define FR_PREAUTH 0x00004 /* require preauthentication */ #define FR_ACCOUNT 0x00005 /* Accounting rule */ #define FR_SKIP 0x00006 /* skip rule */ -#define FR_DIVERT 0x00007 /* divert rule */ +#define FR_DECAPSULATE 0x00008 /* decapsulate rule */ +#define FR_CALL 0x00009 /* call rule */ #define FR_CMDMASK 0x0000f #define FR_LOG 0x00010 /* Log */ #define FR_LOGB 0x00011 /* Log-fail */ @@ -674,19 +881,19 @@ typedef struct frentry { #define FR_LOGBODY 0x10000 /* Log the body */ #define FR_LOGFIRST 0x20000 /* Log the first byte if state held */ #define FR_LOGORBLOCK 0x40000 /* block the packet if it can't be logged */ -#define FR_DUP 0x80000 /* duplicate packet */ +#define FR_STLOOSE 0x80000 /* loose state checking */ #define FR_FRSTRICT 0x100000 /* strict frag. cache */ #define FR_STSTRICT 0x200000 /* strict keep state */ #define FR_NEWISN 0x400000 /* new ISN for outgoing TCP */ #define FR_NOICMPERR 0x800000 /* do not match ICMP errors in state */ #define FR_STATESYNC 0x1000000 /* synchronize state to slave */ +#define FR_COPIED 0x2000000 /* copied from user space */ +#define FR_INACTIVE 0x4000000 /* only used when flush'ing rules */ #define FR_NOMATCH 0x8000000 /* no match occured */ /* 0x10000000 FF_LOGPASS */ /* 0x20000000 FF_LOGBLOCK */ /* 0x40000000 FF_LOGNOMATCH */ /* 0x80000000 FF_BLOCKNONIP */ -#define FR_COPIED 0x40000000 /* copied from user space */ -#define FR_INACTIVE 0x80000000 /* only used when flush'ing rules */ #define FR_RETMASK (FR_RETICMP|FR_RETRST|FR_FAKEICMP) #define FR_ISBLOCK(x) (((x) & FR_CMDMASK) == FR_BLOCK) @@ -695,6 +902,7 @@ typedef struct frentry { #define FR_ISPREAUTH(x) (((x) & FR_CMDMASK) == FR_PREAUTH) #define FR_ISACCOUNT(x) (((x) & FR_CMDMASK) == FR_ACCOUNT) #define FR_ISSKIP(x) (((x) & FR_CMDMASK) == FR_SKIP) +#define FR_ISDECAPS(x) (((x) & FR_CMDMASK) == FR_DECAPSULATE) #define FR_ISNOMATCH(x) ((x) & FR_NOMATCH) #define FR_INOUT (FR_INQUE|FR_OUTQUE) @@ -712,8 +920,8 @@ typedef struct frentry { * Structure that passes information on what/how to flush to the kernel. */ typedef struct ipfflush { - int ipflu_how; - int ipflu_arg; + int ipflu_how; + int ipflu_arg; } ipfflush_t; @@ -721,12 +929,12 @@ typedef struct ipfflush { * */ typedef struct ipfgetctl { - u_int ipfg_min; /* min value */ - u_int ipfg_current; /* current value */ - u_int ipfg_max; /* max value */ - u_int ipfg_default; /* default value */ - u_int ipfg_steps; /* value increments */ - char ipfg_name[40]; /* tag name for this control */ + u_int ipfg_min; /* min value */ + u_int ipfg_current; /* current value */ + u_int ipfg_max; /* max value */ + u_int ipfg_default; /* default value */ + u_int ipfg_steps; /* value increments */ + char ipfg_name[40]; /* tag name for this control */ } ipfgetctl_t; typedef struct ipfsetctl { @@ -741,7 +949,43 @@ typedef struct ipfsetctl { * in this single structure so that they can all easily be collected and * copied back as required. */ -typedef struct filterstats { +typedef struct ipf_statistics { + u_long fr_icmp_coalesce; + u_long fr_tcp_frag; + u_long fr_tcp_pullup; + u_long fr_tcp_short; + u_long fr_tcp_small; + u_long fr_tcp_bad_flags; + u_long fr_udp_pullup; + u_long fr_ip_freed; + u_long fr_v6_ah_bad; + u_long fr_v6_bad; + u_long fr_v6_badfrag; + u_long fr_v6_dst_bad; + u_long fr_v6_esp_pullup; + u_long fr_v6_ext_short; + u_long fr_v6_ext_pullup; + u_long fr_v6_ext_hlen; + u_long fr_v6_frag_bad; + u_long fr_v6_frag_pullup; + u_long fr_v6_frag_size; + u_long fr_v6_gre_pullup; + u_long fr_v6_icmp6_pullup; + u_long fr_v6_rh_bad; + u_long fr_v6_badttl; /* TTL in packet doesn't reach minimum */ + u_long fr_v4_ah_bad; + u_long fr_v4_ah_pullup; + u_long fr_v4_esp_pullup; + u_long fr_v4_cipso_bad; + u_long fr_v4_cipso_tlen; + u_long fr_v4_gre_frag; + u_long fr_v4_gre_pullup; + u_long fr_v4_icmp_frag; + u_long fr_v4_icmp_pullup; + u_long fr_v4_badttl; /* TTL in packet doesn't reach minimum */ + u_long fr_v4_badsrc; /* source received doesn't match route */ + u_long fr_l4_badcksum; /* layer 4 header checksum failure */ + u_long fr_badcoalesces; u_long fr_pass; /* packets allowed */ u_long fr_block; /* packets denied */ u_long fr_nom; /* packets which don't match any rule */ @@ -749,8 +993,6 @@ typedef struct filterstats { u_long fr_ppkl; /* packets allowed and logged */ u_long fr_bpkl; /* packets denied and logged */ u_long fr_npkl; /* packets unmatched and logged */ - u_long fr_pkl; /* packets logged */ - u_long fr_skip; /* packets to be logged but buffer full */ u_long fr_ret; /* packets for which a return is sent */ u_long fr_acct; /* packets for which counting was performed */ u_long fr_bnfr; /* bad attempts to allocate fragment state */ @@ -759,15 +1001,15 @@ typedef struct filterstats { u_long fr_bads; /* bad attempts to allocate packet state */ u_long fr_ads; /* new packet state kept */ u_long fr_chit; /* cached hit */ + u_long fr_cmiss; /* cached miss */ u_long fr_tcpbad; /* TCP checksum check failures */ u_long fr_pull[2]; /* good and bad pullup attempts */ - u_long fr_badsrc; /* source received doesn't match route */ - u_long fr_badttl; /* TTL in packet doesn't reach minimum */ u_long fr_bad; /* bad IP packets to the filter */ u_long fr_ipv6; /* IPv6 packets in/out */ u_long fr_ppshit; /* dropped because of pps ceiling */ u_long fr_ipud; /* IP id update failures */ -} filterstats_t; + u_long fr_blocked[FRB_MAX_VALUE + 1]; +} ipf_statistics_t; /* * Log structure. Each packet header logged is prepended by one of these. @@ -777,6 +1019,7 @@ typedef struct filterstats { typedef struct iplog { u_32_t ipl_magic; u_int ipl_count; + u_32_t ipl_seqnum; struct timeval ipl_time; size_t ipl_dsize; struct iplog *ipl_next; @@ -796,18 +1039,19 @@ typedef struct ipflog { #else u_int fl_unit; #endif - u_32_t fl_rule; - u_32_t fl_flags; - u_32_t fl_lflags; - u_32_t fl_logtag; + u_32_t fl_rule; + u_32_t fl_flags; + u_32_t fl_lflags; + u_32_t fl_logtag; ipftag_t fl_nattag; - u_short fl_plen; /* extra data after hlen */ - u_short fl_loglevel; /* syslog log level */ - char fl_group[FR_GROUPLEN]; - u_char fl_hlen; /* length of IP headers saved */ - u_char fl_dir; - u_char fl_xxx[2]; /* pad */ - char fl_ifname[LIFNAMSIZ]; + u_short fl_plen; /* extra data after hlen */ + u_short fl_loglevel; /* syslog log level */ + char fl_group[FR_GROUPLEN]; + u_char fl_hlen; /* length of IP headers saved */ + u_char fl_dir; + u_char fl_breason; /* from fin_reason */ + u_char fl_family; /* address family of packet logged */ + char fl_ifname[LIFNAMSIZ]; } ipflog_t; #ifndef IPF_LOGGING @@ -817,12 +1061,12 @@ typedef struct ipflog { # define IPF_DEFAULT_PASS FR_PASS #endif -#define DEFAULT_IPFLOGSIZE 8192 +#define DEFAULT_IPFLOGSIZE 32768 #ifndef IPFILTER_LOGSIZE # define IPFILTER_LOGSIZE DEFAULT_IPFLOGSIZE #else -# if IPFILTER_LOGSIZE < DEFAULT_IPFLOGSIZE -# error IPFILTER_LOGSIZE too small. Must be >= DEFAULT_IPFLOGSIZE +# if IPFILTER_LOGSIZE < 8192 +# error IPFILTER_LOGSIZE too small. Must be >= 8192 # endif #endif @@ -867,34 +1111,30 @@ typedef struct ipflog { * For SIOCGETFS */ typedef struct friostat { - struct filterstats f_st[2]; - struct frentry *f_ipf[2][2]; - struct frentry *f_acct[2][2]; - struct frentry *f_ipf6[2][2]; - struct frentry *f_acct6[2][2]; - struct frentry *f_auth; - struct frgroup *f_groups[IPL_LOGSIZE][2]; - u_long f_froute[2]; - u_long f_ticks; - int f_locks[IPL_LOGMAX]; - size_t f_kmutex_sz; - size_t f_krwlock_sz; - int f_defpass; /* default pass - from fr_pass */ - int f_active; /* 1 or 0 - active rule set */ - int f_running; /* 1 if running, else 0 */ - int f_logging; /* 1 if enabled, else 0 */ - int f_features; - char f_version[32]; /* version string */ + ipf_statistics_t f_st[2]; + frentry_t *f_ipf[2][2]; + frentry_t *f_acct[2][2]; + frentry_t *f_auth; + struct frgroup *f_groups[IPL_LOGSIZE][2]; + u_long f_froute[2]; + u_long f_log_ok; + u_long f_log_fail; + u_long f_rb_no_mem; + u_long f_rb_node_max; + u_32_t f_ticks; + int f_locks[IPL_LOGSIZE]; + int f_defpass; /* default pass - from fr_pass */ + int f_active; /* 1 or 0 - active rule set */ + int f_running; /* 1 if running, else 0 */ + int f_logging; /* 1 if enabled, else 0 */ + int f_features; + char f_version[32]; /* version string */ } friostat_t; #define f_fin f_ipf[0] -#define f_fin6 f_ipf6[0] #define f_fout f_ipf[1] -#define f_fout6 f_ipf6[1] #define f_acctin f_acct[0] -#define f_acctin6 f_acct6[0] #define f_acctout f_acct[1] -#define f_acctout6 f_acct6[1] #define IPF_FEAT_LKM 0x001 #define IPF_FEAT_LOG 0x002 @@ -916,12 +1156,13 @@ typedef struct optlist { * Group list structure. */ typedef struct frgroup { - struct frgroup *fg_next; - struct frentry *fg_head; - struct frentry *fg_start; - u_32_t fg_flags; - int fg_ref; - char fg_name[FR_GROUPLEN]; + struct frgroup *fg_next; + struct frentry *fg_head; + struct frentry *fg_start; + struct frgroup **fg_set; + u_32_t fg_flags; + int fg_ref; + char fg_name[FR_GROUPLEN]; } frgroup_t; #define FG_NAME(g) (*(g)->fg_name == '\0' ? "" : (g)->fg_name) @@ -931,24 +1172,24 @@ typedef struct frgroup { * Used by state and NAT tables */ typedef struct icmpinfo { - u_short ici_id; - u_short ici_seq; - u_char ici_type; + u_short ici_id; + u_short ici_seq; + u_char ici_type; } icmpinfo_t; typedef struct udpinfo { - u_short us_sport; - u_short us_dport; + u_short us_sport; + u_short us_dport; } udpinfo_t; typedef struct tcpdata { - u_32_t td_end; - u_32_t td_maxend; - u_32_t td_maxwin; - u_32_t td_winscale; - u_32_t td_maxseg; - int td_winflags; + u_32_t td_end; + u_32_t td_maxend; + u_32_t td_maxwin; + u_32_t td_winscale; + u_32_t td_maxseg; + int td_winflags; } tcpdata_t; #define TCP_WSCALE_MAX 14 @@ -959,9 +1200,9 @@ typedef struct tcpdata { typedef struct tcpinfo { - u_short ts_sport; - u_short ts_dport; - tcpdata_t ts_data[2]; + u_32_t ts_sport; + u_32_t ts_dport; + tcpdata_t ts_data[2]; } tcpinfo_t; @@ -969,16 +1210,28 @@ typedef struct tcpinfo { * Structures to define a GRE header as seen in a packet. */ struct grebits { - u_32_t grb_C:1; - u_32_t grb_R:1; - u_32_t grb_K:1; - u_32_t grb_S:1; - u_32_t grb_s:1; - u_32_t grb_recur:1; - u_32_t grb_A:1; - u_32_t grb_flags:3; - u_32_t grb_ver:3; - u_short grb_ptype; +#if defined(sparc) + u_32_t grb_ver:3; + u_32_t grb_flags:3; + u_32_t grb_A:1; + u_32_t grb_recur:1; + u_32_t grb_s:1; + u_32_t grb_S:1; + u_32_t grb_K:1; + u_32_t grb_R:1; + u_32_t grb_C:1; +#else + u_32_t grb_C:1; + u_32_t grb_R:1; + u_32_t grb_K:1; + u_32_t grb_S:1; + u_32_t grb_s:1; + u_32_t grb_recur:1; + u_32_t grb_A:1; + u_32_t grb_flags:3; + u_32_t grb_ver:3; +#endif + u_short grb_ptype; }; typedef struct grehdr { @@ -986,8 +1239,8 @@ typedef struct grehdr { struct grebits gru_bits; u_short gru_flags; } gr_un; - u_short gr_len; - u_short gr_call; + u_short gr_len; + u_short gr_call; } grehdr_t; #define gr_flags gr_un.gru_flags @@ -1006,9 +1259,9 @@ typedef struct grehdr { * GRE information tracked by "keep state" */ typedef struct greinfo { - u_short gs_call[2]; - u_short gs_flags; - u_short gs_ptype; + u_short gs_call[2]; + u_short gs_flags; + u_short gs_ptype; } greinfo_t; #define GRE_REV(x) ((ntohs(x) >> 13) & 7) @@ -1018,11 +1271,11 @@ typedef struct greinfo { * Format of an Authentication header */ typedef struct authhdr { - u_char ah_next; - u_char ah_plen; - u_short ah_reserved; - u_32_t ah_spi; - u_32_t ah_seq; + u_char ah_next; + u_char ah_plen; + u_short ah_reserved; + u_32_t ah_spi; + u_32_t ah_seq; /* Following the sequence number field is 0 or more bytes of */ /* authentication data, as specified by ah_plen - RFC 2402. */ } authhdr_t; @@ -1035,14 +1288,15 @@ typedef struct ipftqent { struct ipftqent **tqe_pnext; struct ipftqent *tqe_next; struct ipftq *tqe_ifq; - void *tqe_parent; /* pointer back to NAT/state struct */ - u_long tqe_die; /* when this entriy is to die */ - u_long tqe_touched; - int tqe_flags; - int tqe_state[2]; /* current state of this entry */ + void *tqe_parent; /* pointer back to NAT/state struct */ + u_32_t tqe_die; /* when this entriy is to die */ + u_32_t tqe_touched; + int tqe_flags; + int tqe_state[2]; /* current state of this entry */ } ipftqent_t; #define TQE_RULEBASED 0x00000001 +#define TQE_DELETE 0x00000002 /* @@ -1050,45 +1304,46 @@ typedef struct ipftqent { */ typedef struct ipftq { ipfmutex_t ifq_lock; - u_int ifq_ttl; + u_int ifq_ttl; ipftqent_t *ifq_head; ipftqent_t **ifq_tail; - struct ipftq *ifq_next; - struct ipftq **ifq_pnext; - int ifq_ref; - u_int ifq_flags; + struct ipftq *ifq_next; + struct ipftq **ifq_pnext; + int ifq_ref; + u_int ifq_flags; } ipftq_t; #define IFQF_USER 0x01 /* User defined aging */ #define IFQF_DELETE 0x02 /* Marked for deletion */ #define IFQF_PROXY 0x04 /* Timeout queue in use by a proxy */ +#define IPFTQ_INIT(x,y,z) do { \ + (x)->ifq_ttl = (y); \ + (x)->ifq_head = NULL; \ + (x)->ifq_ref = 1; \ + (x)->ifq_tail = &(x)->ifq_head; \ + MUTEX_INIT(&(x)->ifq_lock, (z)); \ + } while (0) + #define IPF_HZ_MULT 1 #define IPF_HZ_DIVIDE 2 /* How many times a second ipfilter */ /* checks its timeout queues. */ #define IPF_TTLVAL(x) (((x) / IPF_HZ_MULT) * IPF_HZ_DIVIDE) -typedef int (*ipftq_delete_fn_t)(void *); - -/* - * Structure to define address for pool lookups. - */ -typedef struct { - u_char adf_len; - i6addr_t adf_addr; -} addrfamily_t; +typedef int (*ipftq_delete_fn_t)(struct ipf_main_softc_s *, void *); /* * Object structure description. For passing through in ioctls. */ typedef struct ipfobj { - u_32_t ipfo_rev; /* IPFilter version number */ - u_32_t ipfo_size; /* size of object at ipfo_ptr */ - void *ipfo_ptr; /* pointer to object */ - int ipfo_type; /* type of object being pointed to */ - int ipfo_offset; /* bytes from ipfo_ptr where to start */ - u_char ipfo_xxxpad[32]; /* reserved for future use */ + u_32_t ipfo_rev; /* IPFilter version number */ + u_32_t ipfo_size; /* size of object at ipfo_ptr */ + void *ipfo_ptr; /* pointer to object */ + int ipfo_type; /* type of object being pointed to */ + int ipfo_offset; /* bytes from ipfo_ptr where to start */ + int ipfo_retval; /* return value */ + u_char ipfo_xxxpad[28]; /* reserved for future use */ } ipfobj_t; #define IPFOBJ_FRENTRY 0 /* struct frentry */ @@ -1110,18 +1365,32 @@ typedef struct ipfobj { #define IPFOBJ_GENITER 16 /* struct ipfgeniter */ #define IPFOBJ_GTABLE 17 /* struct ipftable */ #define IPFOBJ_LOOKUPITER 18 /* struct ipflookupiter */ -#define IPFOBJ_STATETQTAB 19 /* struct ipftq [NSTATES] */ -#define IPFOBJ_COUNT 20 /* How many #defines are above this? */ +#define IPFOBJ_STATETQTAB 19 /* struct ipftq * NSTATES */ +#define IPFOBJ_IPFEXPR 20 +#define IPFOBJ_PROXYCTL 21 /* strct ap_ctl */ +#define IPFOBJ_FRIPF 22 /* structfripf */ +#define IPFOBJ_COUNT 23 /* How many #defines are above this? */ typedef union ipftunevalptr { - void *ipftp_void; - u_long *ipftp_long; - u_int *ipftp_int; - u_short *ipftp_short; - u_char *ipftp_char; + void *ipftp_void; + u_long *ipftp_long; + u_int *ipftp_int; + u_short *ipftp_short; + u_char *ipftp_char; + u_long ipftp_offset; } ipftunevalptr_t; +typedef union ipftuneval { + u_long ipftu_long; + u_int ipftu_int; + u_short ipftu_short; + u_char ipftu_char; +} ipftuneval_t; + +struct ipftuneable; +typedef int (* ipftunefunc_t) __P((struct ipf_main_softc_s *, struct ipftuneable *, ipftuneval_t *)); + typedef struct ipftuneable { ipftunevalptr_t ipft_una; const char *ipft_name; @@ -1130,6 +1399,7 @@ typedef struct ipftuneable { int ipft_sz; int ipft_flags; struct ipftuneable *ipft_next; + ipftunefunc_t ipft_func; } ipftuneable_t; #define ipft_addr ipft_una.ipftp_void @@ -1141,13 +1411,6 @@ typedef struct ipftuneable { #define IPFT_RDONLY 1 /* read-only */ #define IPFT_WRDISABLED 2 /* write when disabled only */ -typedef union ipftuneval { - u_long ipftu_long; - u_int ipftu_int; - u_short ipftu_short; - u_char ipftu_char; -} ipftuneval_t; - typedef struct ipftune { void *ipft_cookie; ipftuneval_t ipft_un; @@ -1163,6 +1426,53 @@ typedef struct ipftune { #define ipft_vshort ipft_un.ipftu_short #define ipft_vchar ipft_un.ipftu_char +/* + * Hash table header + */ +#define IPFHASH(x,y) typedef struct { \ + ipfrwlock_t ipfh_lock; \ + struct x *ipfh_head; \ + } y + +/* +** HPUX Port +*/ +#ifdef __hpux +/* HP-UX locking sequence deadlock detection module lock MAJOR ID */ +# define IPF_SMAJ 0 /* temp assignment XXX, not critical */ +#endif + +#if !defined(CDEV_MAJOR) && defined (__FreeBSD_version) && \ + (__FreeBSD_version >= 220000) +# define CDEV_MAJOR 79 +#endif + +/* + * Post NetBSD 1.2 has the PFIL interface for packet filters. This turns + * on those hooks. We don't need any special mods in non-IP Filter code + * with this! + */ +#if (defined(NetBSD) && (NetBSD > 199609) && (NetBSD <= 1991011)) || \ + (defined(NetBSD1_2) && NetBSD1_2 > 1) || \ + (defined(__FreeBSD__) && (__FreeBSD_version >= 500043)) +# if (defined(NetBSD) && NetBSD >= 199905) +# define PFIL_HOOKS +# endif +# ifdef PFIL_HOOKS +# define NETBSD_PF +# endif +#endif + +#ifdef _KERNEL +# define FR_VERBOSE(verb_pr) +# define FR_DEBUG(verb_pr) +#else +extern void ipfkdebug __P((char *, ...)); +extern void ipfkverbose __P((char *, ...)); +# define FR_VERBOSE(verb_pr) ipfkverbose verb_pr +# define FR_DEBUG(verb_pr) ipfkdebug verb_pr +#endif + /* * */ @@ -1171,7 +1481,7 @@ typedef struct ipfruleiter { char iri_group[FR_GROUPLEN]; int iri_active; int iri_nrules; - int iri_v; + int iri_v; /* No longer used (compatibility) */ frentry_t *iri_rule; } ipfruleiter_t; @@ -1210,6 +1520,19 @@ typedef struct ipftable { #define IPFTABLE_BUCKETS_NATOUT 3 +typedef struct ipf_v4_masktab_s { + u_32_t imt4_active[33]; + int imt4_masks[33]; + int imt4_max; +} ipf_v4_masktab_t; + +typedef struct ipf_v6_masktab_s { + i6addr_t imt6_active[129]; + int imt6_masks[129]; + int imt6_max; +} ipf_v6_masktab_t; + + /* * */ @@ -1222,190 +1545,246 @@ typedef struct ipftoken { int ipt_type; int ipt_uid; int ipt_subtype; - int ipt_alive; + int ipt_ref; + int ipt_complete; } ipftoken_t; /* -** HPUX Port -*/ -#ifdef __hpux -/* HP-UX locking sequence deadlock detection module lock MAJOR ID */ -# define IPF_SMAJ 0 /* temp assignment XXX, not critical */ -#endif - -#if !defined(CDEV_MAJOR) && defined (__FreeBSD_version) && \ - (__FreeBSD_version >= 220000) -# define CDEV_MAJOR 79 -#endif + * + */ +typedef struct ipfexp { + int ipfe_cmd; + int ipfe_not; + int ipfe_narg; + int ipfe_size; + int ipfe_arg0[1]; +} ipfexp_t; /* - * Post NetBSD 1.2 has the PFIL interface for packet filters. This turns - * on those hooks. We don't need any special mods in non-IP Filter code - * with this! + * Currently support commands (ipfe_cmd) + * 32bits is split up follows: + * aabbcccc + * aa = 0 = packet matching, 1 = meta data matching + * bb = IP protocol number + * cccc = command */ -#if (defined(NetBSD) && (NetBSD > 199609) && (NetBSD <= 1991011)) || \ - (defined(NetBSD1_2) && NetBSD1_2 > 1) || \ - (defined(__FreeBSD__) && (__FreeBSD_version >= 500043)) -# if defined(NetBSD) && (NetBSD >= 199905) -# define PFIL_HOOKS -# endif -# ifdef PFIL_HOOKS -# define NETBSD_PF +#define IPF_EXP_IP_PR 0x00000001 +#define IPF_EXP_IP_ADDR 0x00000002 +#define IPF_EXP_IP_SRCADDR 0x00000003 +#define IPF_EXP_IP_DSTADDR 0x00000004 +#define IPF_EXP_IP6_ADDR 0x00000005 +#define IPF_EXP_IP6_SRCADDR 0x00000006 +#define IPF_EXP_IP6_DSTADDR 0x00000007 +#define IPF_EXP_TCP_FLAGS 0x00060001 +#define IPF_EXP_TCP_PORT 0x00060002 +#define IPF_EXP_TCP_SPORT 0x00060003 +#define IPF_EXP_TCP_DPORT 0x00060004 +#define IPF_EXP_UDP_PORT 0x00110002 +#define IPF_EXP_UDP_SPORT 0x00110003 +#define IPF_EXP_UDP_DPORT 0x00110004 +#define IPF_EXP_IDLE_GT 0x01000001 +#define IPF_EXP_TCP_STATE 0x01060002 +#define IPF_EXP_END 0xffffffff + +#define ONE_DAY IPF_TTLVAL(1 * 86400) /* 1 day */ +#define FIVE_DAYS (5 * ONE_DAY) + +typedef struct ipf_main_softc_s { + struct ipf_main_softc_s *ipf_next; + ipfmutex_t ipf_rw; + ipfmutex_t ipf_timeoutlock; + ipfrwlock_t ipf_mutex; + ipfrwlock_t ipf_frag; + ipfrwlock_t ipf_global; + ipfrwlock_t ipf_tokens; + ipfrwlock_t ipf_state; + ipfrwlock_t ipf_nat; + ipfrwlock_t ipf_natfrag; + ipfrwlock_t ipf_poolrw; + int ipf_dynamic_softc; + int ipf_refcnt; + int ipf_running; + int ipf_flags; + int ipf_active; + int ipf_control_forwarding; + int ipf_update_ipid; + int ipf_chksrc; /* causes a system crash if enabled */ + int ipf_pass; + int ipf_minttl; + int ipf_icmpminfragmtu; + int ipf_interror; /* Should be in a struct that is per */ + /* thread or process. Does not belong */ + /* here but there's a lot more work */ + /* in doing that properly. For now, */ + /* it is squatting. */ + u_int ipf_tcpidletimeout; + u_int ipf_tcpclosewait; + u_int ipf_tcplastack; + u_int ipf_tcptimewait; + u_int ipf_tcptimeout; + u_int ipf_tcpsynsent; + u_int ipf_tcpsynrecv; + u_int ipf_tcpclosed; + u_int ipf_tcphalfclosed; + u_int ipf_udptimeout; + u_int ipf_udpacktimeout; + u_int ipf_icmptimeout; + u_int ipf_icmpacktimeout; + u_int ipf_iptimeout; + u_long ipf_ticks; + u_long ipf_userifqs; + u_long ipf_rb_no_mem; + u_long ipf_rb_node_max; + u_long ipf_frouteok[2]; + ipftuneable_t *ipf_tuners; + void *ipf_frag_soft; + void *ipf_nat_soft; + void *ipf_state_soft; + void *ipf_auth_soft; + void *ipf_proxy_soft; + void *ipf_sync_soft; + void *ipf_lookup_soft; + void *ipf_log_soft; + struct frgroup *ipf_groups[IPL_LOGSIZE][2]; + frentry_t *ipf_rules[2][2]; + frentry_t *ipf_acct[2][2]; + frentry_t *ipf_rule_explist[2]; + ipftoken_t *ipf_token_head; + ipftoken_t **ipf_token_tail; +#if defined(__FreeBSD_version) && (__FreeBSD_version >= 300000) && \ + defined(_KERNEL) + struct callout_handle ipf_slow_ch; +#endif +#if defined(linux) && defined(_KERNEL) + struct timer_list ipf_timer; +#endif +#if NETBSD_GE_REV(104040000) + struct callout ipf_slow_ch; +#endif +#if SOLARIS +# if SOLARIS2 >= 7 + timeout_id_t ipf_slow_ch; +# else + int ipf_slow_ch; # endif #endif - -#ifdef _KERNEL -# define FR_VERBOSE(verb_pr) -# define FR_DEBUG(verb_pr) -#else -extern void debug __P((char *, ...)); -extern void verbose __P((char *, ...)); -# define FR_VERBOSE(verb_pr) verbose verb_pr -# define FR_DEBUG(verb_pr) debug verb_pr +#if defined(_KERNEL) +# if SOLARIS + struct pollhead ipf_poll_head[IPL_LOGSIZE]; + void *ipf_dip; +# if defined(INSTANCES) + int ipf_get_loopback; + u_long ipf_idnum; + net_handle_t ipf_nd_v4; + net_handle_t ipf_nd_v6; + hook_t *ipf_hk_v4_in; + hook_t *ipf_hk_v4_out; + hook_t *ipf_hk_v4_nic; + hook_t *ipf_hk_v6_in; + hook_t *ipf_hk_v6_out; + hook_t *ipf_hk_v6_nic; + hook_t *ipf_hk_loop_v4_in; + hook_t *ipf_hk_loop_v4_out; + hook_t *ipf_hk_loop_v6_in; + hook_t *ipf_hk_loop_v6_out; +# endif +# else +# if defined(linux) && defined(_KERNEL) + struct poll_table_struct ipf_selwait[IPL_LOGSIZE]; + wait_queue_head_t iplh_linux[IPL_LOGSIZE]; +# else + struct selinfo ipf_selwait[IPL_LOGSIZE]; +# endif +# endif #endif + void *ipf_slow; + ipf_statistics_t ipf_stats[2]; + u_char ipf_iss_secret[32]; + u_short ipf_ip_id; +} ipf_main_softc_t; +#define IPFERROR(_e) do { softc->ipf_interror = (_e); \ + DT1(user_error, int, _e); \ + } while (0) #ifndef _KERNEL -extern int fr_check __P((struct ip *, int, void *, int, mb_t **)); -extern int (*fr_checkp) __P((ip_t *, int, void *, int, mb_t **)); -extern int ipf_log __P((void)); +extern int ipf_check __P((void *, struct ip *, int, void *, int, mb_t **)); +extern int (*ipf_checkp) __P((ip_t *, int, void *, int, mb_t **)); extern struct ifnet *get_unit __P((char *, int)); extern char *get_ifname __P((struct ifnet *)); -# if defined(__NetBSD__) || defined(__OpenBSD__) || \ - (_BSDI_VERSION >= 199701) || (__FreeBSD_version >= 300000) -extern int iplioctl __P((int, ioctlcmd_t, caddr_t, int)); -# else -extern int iplioctl __P((int, ioctlcmd_t, caddr_t, int)); -# endif -extern int iplopen __P((dev_t, int)); -extern int iplclose __P((dev_t, int)); +extern int ipfioctl __P((ipf_main_softc_t *, int, ioctlcmd_t, + caddr_t, int)); extern void m_freem __P((mb_t *)); +extern size_t msgdsize __P((mb_t *)); extern int bcopywrap __P((void *, void *, size_t)); #else /* #ifndef _KERNEL */ -# ifdef BSD -# if (defined(__NetBSD__) && (__NetBSD_Version__ < 399000000)) || \ - defined(__osf__) || \ - (defined(__FreeBSD_version) && (__FreeBSD_version < 500043)) -# include -# else -# include -# endif -extern struct selinfo ipfselwait[IPL_LOGSIZE]; -# endif # if defined(__NetBSD__) && defined(PFIL_HOOKS) extern void ipfilterattach __P((int)); # endif extern int ipl_enable __P((void)); extern int ipl_disable __P((void)); -extern int ipf_inject __P((fr_info_t *, mb_t *)); # ifdef MENTAT -extern int fr_check __P((struct ip *, int, void *, int, void *, - mblk_t **)); +extern int ipf_check __P((void *, struct ip *, int, void *, int, void *, + mblk_t **)); # if SOLARIS +extern void ipf_prependmbt(fr_info_t *, mblk_t *); # if SOLARIS2 >= 7 -extern int iplioctl __P((dev_t, int, intptr_t, int, cred_t *, int *)); +extern int ipfioctl __P((dev_t, int, intptr_t, int, cred_t *, int *)); # else -extern int iplioctl __P((dev_t, int, int *, int, cred_t *, int *)); +extern int ipfioctl __P((dev_t, int, int *, int, cred_t *, int *)); # endif -extern int iplopen __P((dev_t *, int, int, cred_t *)); -extern int iplclose __P((dev_t, int, int, cred_t *)); -extern int iplread __P((dev_t, uio_t *, cred_t *)); -extern int iplwrite __P((dev_t, uio_t *, cred_t *)); # endif # ifdef __hpux -extern int iplopen __P((dev_t, int, intptr_t, int)); -extern int iplclose __P((dev_t, int, int)); -extern int iplioctl __P((dev_t, int, caddr_t, int)); -extern int iplread __P((dev_t, uio_t *)); -extern int iplwrite __P((dev_t, uio_t *)); -extern int iplselect __P((dev_t, int)); +extern int ipfioctl __P((dev_t, int, caddr_t, int)); +extern int ipf_select __P((dev_t, int)); # endif -extern int fr_qout __P((queue_t *, mblk_t *)); +extern int ipf_qout __P((queue_t *, mblk_t *)); # else /* MENTAT */ -extern int fr_check __P((struct ip *, int, void *, int, mb_t **)); +extern int ipf_check __P((void *, struct ip *, int, void *, int, mb_t **)); extern int (*fr_checkp) __P((ip_t *, int, void *, int, mb_t **)); extern size_t mbufchainlen __P((mb_t *)); # ifdef __sgi # include -extern int iplioctl __P((dev_t, int, caddr_t, int, cred_t *, int *)); -extern int iplopen __P((dev_t *, int, int, cred_t *)); -extern int iplclose __P((dev_t, int, int, cred_t *)); -extern int iplread __P((dev_t, uio_t *, cred_t *)); -extern int iplwrite __P((dev_t, uio_t *, cred_t *)); +extern int ipfioctl __P((dev_t, int, caddr_t, int, cred_t *, int *)); extern int ipfilter_sgi_attach __P((void)); extern void ipfilter_sgi_detach __P((void)); extern void ipfilter_sgi_intfsync __P((void)); # else # ifdef IPFILTER_LKM -extern int iplidentify __P((char *)); +extern int ipf_identify __P((char *)); # endif -# if (defined(_BSDI_VERSION) && _BSDI_VERSION >= 199510) || \ - (__FreeBSD_version >= 220000) || \ - (NetBSD >= 199511) || defined(__OpenBSD__) -# if defined(__NetBSD__) || \ - (defined(_BSDI_VERSION) && _BSDI_VERSION >= 199701) || \ - defined(__OpenBSD__) || (__FreeBSD_version >= 300000) +# if BSDOS_GE_REV(199510) || FREEBSD_GE_REV(220000) || \ + (defined(NetBSD) && (NetBSD >= 199511)) || defined(__OpenBSD__) +# if defined(__NetBSD__) || BSDOS_GE_REV(199701) || \ + defined(__OpenBSD__) || FREEBSD_GE_REV(300000) # if (__FreeBSD_version >= 500024) # if (__FreeBSD_version >= 502116) -extern int iplioctl __P((struct cdev*, u_long, caddr_t, int, struct thread *)); +extern int ipfioctl __P((struct cdev*, u_long, caddr_t, int, struct thread *)); # else -extern int iplioctl __P((dev_t, u_long, caddr_t, int, struct thread *)); +extern int ipfioctl __P((dev_t, u_long, caddr_t, int, struct thread *)); # endif /* __FreeBSD_version >= 502116 */ # else -# if (__NetBSD_Version__ >= 499001000) -extern int iplioctl __P((dev_t, u_long, void *, int, struct lwp *)); +# if NETBSD_GE_REV(499001000) +extern int ipfioctl __P((dev_t, u_long, void *, int, struct lwp *)); # else -# if (__NetBSD_Version__ >= 399001400) -extern int iplioctl __P((dev_t, u_long, caddr_t, int, struct lwp *)); +# if NETBSD_GE_REV(399001400) +extern int ipfioctl __P((dev_t, u_long, caddr_t, int, struct lwp *)); # else -extern int iplioctl __P((dev_t, u_long, caddr_t, int, struct proc *)); +extern int ipfioctl __P((dev_t, u_long, caddr_t, int, struct proc *)); # endif # endif # endif /* __FreeBSD_version >= 500024 */ # else -extern int iplioctl __P((dev_t, int, caddr_t, int, struct thread *)); +extern int ipfioctl __P((dev_t, int, caddr_t, int, struct proc *)); # endif -# if (__FreeBSD_version >= 500024) -# if (__FreeBSD_version >= 502116) -extern int iplopen __P((struct cdev*, int, int, struct thread *)); -extern int iplclose __P((struct cdev*, int, int, struct thread *)); -# else -extern int iplopen __P((dev_t, int, int, struct thread *)); -extern int iplclose __P((dev_t, int, int, struct thread *)); -# endif /* __FreeBSD_version >= 502116 */ -# else -# if (__NetBSD_Version__ >= 399001400) -extern int iplopen __P((dev_t, int, int, struct lwp *)); -extern int iplclose __P((dev_t, int, int, struct lwp *)); -# else -extern int iplopen __P((dev_t, int, int, struct proc *)); -extern int iplclose __P((dev_t, int, int, struct proc *)); -# endif /* __NetBSD_Version__ >= 399001400 */ -# endif /* __FreeBSD_version >= 500024 */ # else # ifdef linux -extern int iplioctl __P((struct inode *, struct file *, u_int, u_long)); +extern int ipfioctl __P((struct inode *, struct file *, u_int, u_long)); # else -extern int iplopen __P((dev_t, int)); -extern int iplclose __P((dev_t, int)); -extern int iplioctl __P((dev_t, int, caddr_t, int)); +extern int ipfioctl __P((dev_t, int, caddr_t, int)); # endif # endif /* (_BSDI_VERSION >= 199510) */ -# if BSD >= 199306 -# if (__FreeBSD_version >= 502116) -extern int iplread __P((struct cdev*, struct uio *, int)); -extern int iplwrite __P((struct cdev*, struct uio *, int)); -# else -extern int iplread __P((dev_t, struct uio *, int)); -extern int iplwrite __P((dev_t, struct uio *, int)); -# endif /* __FreeBSD_version >= 502116 */ -# else -# ifndef linux -extern int iplread __P((dev_t, struct uio *)); -extern int iplwrite __P((dev_t, struct uio *)); -# endif -# endif /* BSD >= 199306 */ # endif /* __ sgi */ # endif /* MENTAT */ @@ -1416,153 +1795,206 @@ extern void ipf_event_reg __P((void)); extern void ipf_event_dereg __P((void)); # endif -#endif /* #ifndef _KERNEL */ +# if defined(INSTANCES) +extern ipf_main_softc_t *ipf_find_softc __P((u_long)); +extern int ipf_set_loopback __P((ipf_main_softc_t *, ipftuneable_t *, + ipftuneval_t *)); +# endif -extern ipfmutex_t ipl_mutex, ipf_authmx, ipf_rw, ipf_hostmap; -extern ipfmutex_t ipf_timeoutlock, ipf_stinsert, ipf_natio, ipf_nat_new; -extern ipfrwlock_t ipf_mutex, ipf_global, ip_poolrw, ipf_ipidfrag; -extern ipfrwlock_t ipf_frag, ipf_state, ipf_nat, ipf_natfrag, ipf_auth; -extern ipfrwlock_t ipf_frcache, ipf_tokens; +#endif /* #ifndef _KERNEL */ extern char *memstr __P((const char *, char *, size_t, size_t)); extern int count4bits __P((u_32_t)); -extern int frrequest __P((int, ioctlcmd_t, caddr_t, int, int)); -extern char *getifname __P((struct ifnet *)); -extern int ipfattach __P((void)); -extern int ipfdetach __P((void)); -extern u_short ipf_cksum __P((u_short *, int)); -extern int copyinptr __P((void *, void *, size_t)); -extern int copyoutptr __P((void *, void *, size_t)); -extern int fr_fastroute __P((mb_t *, mb_t **, fr_info_t *, frdest_t *)); -extern int fr_inobj __P((void *, void *, int)); -extern int fr_inobjsz __P((void *, void *, int, int)); -extern int fr_ioctlswitch __P((int, void *, ioctlcmd_t, int, int, void *)); -extern int fr_ipf_ioctl __P((caddr_t, ioctlcmd_t, int, int, void *)); -extern int fr_ipftune __P((ioctlcmd_t, void *)); -extern int fr_outobj __P((void *, void *, int)); -extern int fr_outobjsz __P((void *, void *, int, int)); -extern void *fr_pullup __P((mb_t *, fr_info_t *, int)); -extern void fr_resolvedest __P((struct frdest *, int)); -extern int fr_resolvefunc __P((void *)); -extern void *fr_resolvenic __P((char *, int)); -extern int fr_send_icmp_err __P((int, fr_info_t *, int)); -extern int fr_send_reset __P((fr_info_t *)); -#if (__FreeBSD_version < 501000) || !defined(_KERNEL) -extern int ppsratecheck __P((struct timeval *, int *, int)); +#ifdef USE_INET6 +extern int count6bits __P((u_32_t *)); #endif -extern ipftq_t *fr_addtimeoutqueue __P((ipftq_t **, u_int)); -extern void fr_deletequeueentry __P((ipftqent_t *)); -extern int fr_deletetimeoutqueue __P((ipftq_t *)); -extern void fr_freetimeoutqueue __P((ipftq_t *)); -extern void fr_movequeue __P((ipftqent_t *, ipftq_t *, ipftq_t *)); -extern void fr_queueappend __P((ipftqent_t *, ipftq_t *, void *)); -extern void fr_queueback __P((ipftqent_t *)); -extern void fr_queuefront __P((ipftqent_t *)); -extern void fr_checkv4sum __P((fr_info_t *)); -extern int fr_checkl4sum __P((fr_info_t *)); -extern int fr_ifpfillv4addr __P((int, struct sockaddr_in *, +extern int frrequest __P((ipf_main_softc_t *, int, ioctlcmd_t, caddr_t, + int, int)); +extern char *getifname __P((struct ifnet *)); +extern int ipfattach __P((ipf_main_softc_t *)); +extern int ipfdetach __P((ipf_main_softc_t *)); +extern u_short ipf_cksum __P((u_short *, int)); +extern int copyinptr __P((ipf_main_softc_t *, void *, void *, size_t)); +extern int copyoutptr __P((ipf_main_softc_t *, void *, void *, size_t)); +extern int ipf_fastroute __P((mb_t *, mb_t **, fr_info_t *, frdest_t *)); +extern int ipf_inject __P((fr_info_t *, mb_t *)); +extern int ipf_inobj __P((ipf_main_softc_t *, void *, ipfobj_t *, + void *, int)); +extern int ipf_inobjsz __P((ipf_main_softc_t *, void *, void *, + int , int)); +extern int ipf_ioctlswitch __P((ipf_main_softc_t *, int, void *, + ioctlcmd_t, int, int, void *)); +extern int ipf_ipf_ioctl __P((ipf_main_softc_t *, caddr_t, ioctlcmd_t, + int, int, void *)); +extern int ipf_ipftune __P((ipf_main_softc_t *, ioctlcmd_t, void *)); +extern int ipf_matcharray_load __P((ipf_main_softc_t *, caddr_t, + ipfobj_t *, int **)); +extern int ipf_matcharray_verify __P((int *, int)); +extern int ipf_outobj __P((ipf_main_softc_t *, void *, void *, int)); +extern int ipf_outobjk __P((ipf_main_softc_t *, ipfobj_t *, void *)); +extern int ipf_outobjsz __P((ipf_main_softc_t *, void *, void *, + int, int)); +extern void *ipf_pullup __P((mb_t *, fr_info_t *, int)); +extern int ipf_resolvedest __P((ipf_main_softc_t *, char *, + struct frdest *, int)); +extern int ipf_resolvefunc __P((ipf_main_softc_t *, void *)); +extern void *ipf_resolvenic __P((ipf_main_softc_t *, char *, int)); +extern int ipf_send_icmp_err __P((int, fr_info_t *, int)); +extern int ipf_send_reset __P((fr_info_t *)); +#if (defined(__FreeBSD_version) && (__FreeBSD_version < 501000)) || \ + !defined(_KERNEL) || defined(linux) +#endif +extern void ipf_apply_timeout __P((ipftq_t *, u_int)); +extern ipftq_t *ipf_addtimeoutqueue __P((ipf_main_softc_t *, ipftq_t **, + u_int)); +extern void ipf_deletequeueentry __P((ipftqent_t *)); +extern int ipf_deletetimeoutqueue __P((ipftq_t *)); +extern void ipf_freetimeoutqueue __P((ipf_main_softc_t *, ipftq_t *)); +extern void ipf_movequeue __P((u_long, ipftqent_t *, ipftq_t *, + ipftq_t *)); +extern void ipf_queueappend __P((u_long, ipftqent_t *, ipftq_t *, void *)); +extern void ipf_queueback __P((u_long, ipftqent_t *)); +extern int ipf_queueflush __P((ipf_main_softc_t *, ipftq_delete_fn_t, + ipftq_t *, ipftq_t *, u_int *, int, int)); +extern void ipf_queuefront __P((ipftqent_t *)); +extern int ipf_settimeout_tcp __P((ipftuneable_t *, ipftuneval_t *, + ipftq_t *)); +extern int ipf_checkv4sum __P((fr_info_t *)); +extern int ipf_checkl4sum __P((fr_info_t *)); +extern int ipf_ifpfillv4addr __P((int, struct sockaddr_in *, struct sockaddr_in *, struct in_addr *, struct in_addr *)); -extern int fr_coalesce __P((fr_info_t *)); +extern int ipf_coalesce __P((fr_info_t *)); #ifdef USE_INET6 -extern void fr_checkv6sum __P((fr_info_t *)); -extern int fr_ifpfillv6addr __P((int, struct sockaddr_in6 *, - struct sockaddr_in6 *, struct in_addr *, - struct in_addr *)); +extern int ipf_checkv6sum __P((fr_info_t *)); +extern int ipf_ifpfillv6addr __P((int, struct sockaddr_in6 *, + struct sockaddr_in6 *, i6addr_t *, + i6addr_t *)); #endif -extern int fr_addipftune __P((ipftuneable_t *)); -extern int fr_delipftune __P((ipftuneable_t *)); +extern int ipf_tune_add __P((ipf_main_softc_t *, ipftuneable_t *)); +extern int ipf_tune_add_array __P((ipf_main_softc_t *, ipftuneable_t *)); +extern int ipf_tune_del __P((ipf_main_softc_t *, ipftuneable_t *)); +extern int ipf_tune_del_array __P((ipf_main_softc_t *, ipftuneable_t *)); +extern int ipf_tune_array_link __P((ipf_main_softc_t *, ipftuneable_t *)); +extern int ipf_tune_array_unlink __P((ipf_main_softc_t *, + ipftuneable_t *)); +extern ipftuneable_t *ipf_tune_array_copy __P((void *, size_t, + ipftuneable_t *)); -extern int frflush __P((minor_t, int, int)); -extern void frsync __P((void *)); -extern frgroup_t *fr_addgroup __P((char *, void *, u_32_t, minor_t, int)); -extern int fr_derefrule __P((frentry_t **)); -extern void fr_delgroup __P((char *, minor_t, int)); -extern frgroup_t *fr_findgroup __P((char *, minor_t, int, frgroup_t ***)); +extern int ipf_pr_pullup __P((fr_info_t *, int)); -extern int fr_loginit __P((void)); -extern int ipflog_canread __P((int)); -extern int ipflog_clear __P((minor_t)); -extern int ipflog_read __P((minor_t, uio_t *)); -extern int ipflog __P((fr_info_t *, u_int)); -extern int ipllog __P((int, fr_info_t *, void **, size_t *, int *, int)); -extern void fr_logunload __P((void)); +extern int ipf_flush __P((ipf_main_softc_t *, minor_t, int)); +extern frgroup_t *ipf_group_add __P((ipf_main_softc_t *, char *, void *, + u_32_t, minor_t, int)); +extern void ipf_group_del __P((ipf_main_softc_t *, frgroup_t *, + frentry_t *)); +extern int ipf_derefrule __P((ipf_main_softc_t *, frentry_t **)); +extern frgroup_t *ipf_findgroup __P((ipf_main_softc_t *, char *, minor_t, + int, frgroup_t ***)); -extern frentry_t *fr_acctpkt __P((fr_info_t *, u_32_t *)); -extern int fr_copytolog __P((int, char *, int)); -extern u_short fr_cksum __P((mb_t *, ip_t *, int, void *, int)); -extern void fr_deinitialise __P((void)); -extern frentry_t *fr_dolog __P((fr_info_t *, u_32_t *)); -extern frentry_t *fr_dstgrpmap __P((fr_info_t *, u_32_t *)); -extern void fr_fixskip __P((frentry_t **, frentry_t *, int)); -extern void fr_forgetifp __P((void *)); -extern frentry_t *fr_getrulen __P((int, char *, u_32_t)); -extern void fr_getstat __P((struct friostat *)); -extern int fr_ifpaddr __P((int, int, void *, - struct in_addr *, struct in_addr *)); -extern int fr_initialise __P((void)); -extern int fr_lock __P((caddr_t, int *)); -extern int fr_makefrip __P((int, ip_t *, fr_info_t *)); -extern int fr_matchtag __P((ipftag_t *, ipftag_t *)); -extern int fr_matchicmpqueryreply __P((int, icmpinfo_t *, - struct icmp *, int)); -extern u_32_t fr_newisn __P((fr_info_t *)); -extern u_short fr_nextipid __P((fr_info_t *)); -extern int ipf_queueflush __P((ipftq_delete_fn_t, ipftq_t *, ipftq_t *)); -extern int fr_rulen __P((int, frentry_t *)); -extern int fr_scanlist __P((fr_info_t *, u_32_t)); -extern frentry_t *fr_srcgrpmap __P((fr_info_t *, u_32_t *)); -extern int fr_tcpudpchk __P((fr_info_t *, frtuc_t *)); -extern int fr_verifysrc __P((fr_info_t *fin)); -extern int fr_zerostats __P((void *)); -extern ipftoken_t *ipf_findtoken __P((int, int, void *)); -extern int ipf_getnextrule __P((ipftoken_t *, void *)); -extern void ipf_expiretokens __P((void)); -extern void ipf_freetoken __P((ipftoken_t *)); -extern int ipf_deltoken __P((int,int, void *)); -extern int ipfsync __P((void)); -extern int ipf_genericiter __P((void *, int, void *)); -#ifndef ipf_random +extern int ipf_log_init __P((void)); +extern int ipf_log_bytesused __P((ipf_main_softc_t *, int)); +extern int ipf_log_canread __P((ipf_main_softc_t *, int)); +extern int ipf_log_clear __P((ipf_main_softc_t *, minor_t)); +extern u_long ipf_log_failures __P((ipf_main_softc_t *, int)); +extern int ipf_log_read __P((ipf_main_softc_t *, minor_t, uio_t *)); +extern int ipf_log_items __P((ipf_main_softc_t *, int, fr_info_t *, + void **, size_t *, int *, int)); +extern u_long ipf_log_logok __P((ipf_main_softc_t *, int)); +extern void ipf_log_unload __P((ipf_main_softc_t *)); +extern int ipf_log_pkt __P((fr_info_t *, u_int)); + +extern frentry_t *ipf_acctpkt __P((fr_info_t *, u_32_t *)); +extern u_short fr_cksum __P((fr_info_t *, ip_t *, int, void *)); +extern void ipf_deinitialise __P((ipf_main_softc_t *)); +extern int ipf_deliverlocal __P((ipf_main_softc_t *, int, void *, + i6addr_t *)); +extern frentry_t *ipf_dstgrpmap __P((fr_info_t *, u_32_t *)); +extern void ipf_fixskip __P((frentry_t **, frentry_t *, int)); +extern void ipf_forgetifp __P((ipf_main_softc_t *, void *)); +extern frentry_t *ipf_getrulen __P((ipf_main_softc_t *, int, char *, + u_32_t)); +extern int ipf_ifpaddr __P((ipf_main_softc_t *, int, int, void *, + i6addr_t *, i6addr_t *)); +extern void ipf_inet_mask_add __P((int, ipf_v4_masktab_t *)); +extern void ipf_inet_mask_del __P((int, ipf_v4_masktab_t *)); +#ifdef USE_INET6 +extern void ipf_inet6_mask_add __P((int, i6addr_t *, + ipf_v6_masktab_t *)); +extern void ipf_inet6_mask_del __P((int, i6addr_t *, + ipf_v6_masktab_t *)); +#endif +extern int ipf_initialise __P((void)); +extern int ipf_lock __P((caddr_t, int *)); +extern int ipf_makefrip __P((int, ip_t *, fr_info_t *)); +extern int ipf_matchtag __P((ipftag_t *, ipftag_t *)); +extern int ipf_matchicmpqueryreply __P((int, icmpinfo_t *, + struct icmp *, int)); +extern u_32_t ipf_newisn __P((fr_info_t *)); +extern u_short ipf_nextipid __P((fr_info_t *)); +extern u_int ipf_pcksum __P((fr_info_t *, int, u_int)); +extern void ipf_rule_expire __P((ipf_main_softc_t *)); +extern int ipf_scanlist __P((fr_info_t *, u_32_t)); +extern frentry_t *ipf_srcgrpmap __P((fr_info_t *, u_32_t *)); +extern int ipf_tcpudpchk __P((fr_ip_t *, frtuc_t *)); +extern int ipf_verifysrc __P((fr_info_t *fin)); +extern int ipf_zerostats __P((ipf_main_softc_t *, char *)); +extern int ipf_getnextrule __P((ipf_main_softc_t *, ipftoken_t *, + void *)); +extern int ipf_sync __P((ipf_main_softc_t *, void *)); +extern int ipf_token_deref __P((ipf_main_softc_t *, ipftoken_t *)); +extern void ipf_token_expire __P((ipf_main_softc_t *)); +extern ipftoken_t *ipf_token_find __P((ipf_main_softc_t *, int, int, + void *)); +extern int ipf_token_del __P((ipf_main_softc_t *, int, int, + void *)); +extern void ipf_token_mark_complete __P((ipftoken_t *)); +extern int ipf_genericiter __P((ipf_main_softc_t *, void *, + int, void *)); +#ifdef IPFILTER_LOOKUP +extern void *ipf_resolvelookup __P((int, u_int, u_int, + lookupfunc_t *)); +#endif extern u_32_t ipf_random __P((void)); -#endif -#ifdef NEED_LOCAL_RAND -extern void ipf_rand_push __P((void *, int)); -#endif -extern int fr_running; -extern u_long fr_frouteok[2]; -extern int fr_pass; -extern int fr_flags; -extern int fr_active; -extern int fr_chksrc; -extern int fr_minttl; -extern int fr_refcnt; -extern int fr_control_forwarding; -extern int fr_update_ipid; -extern int nat_logging; -extern int ipstate_logging; -extern int ipl_suppress; -extern int ipl_logmax; -extern int ipl_logall; -extern int ipl_logsize; -extern u_long fr_ticks; -extern fr_info_t frcache[2][8]; +extern int ipf_main_load __P((void)); +extern void *ipf_main_soft_create __P((void *)); +extern void ipf_main_soft_destroy __P((ipf_main_softc_t *)); +extern int ipf_main_soft_init __P((ipf_main_softc_t *)); +extern int ipf_main_soft_fini __P((ipf_main_softc_t *)); +extern int ipf_main_unload __P((void)); +extern int ipf_load_all __P((void)); +extern int ipf_unload_all __P((void)); +extern void ipf_destroy_all __P((ipf_main_softc_t *)); +extern ipf_main_softc_t *ipf_create_all __P((void *)); +extern int ipf_init_all __P((ipf_main_softc_t *)); +extern int ipf_fini_all __P((ipf_main_softc_t *)); +extern void ipf_log_soft_destroy __P((ipf_main_softc_t *, void *)); +extern void *ipf_log_soft_create __P((ipf_main_softc_t *)); +extern int ipf_log_soft_init __P((ipf_main_softc_t *, void *)); +extern int ipf_log_soft_fini __P((ipf_main_softc_t *, void *)); +extern int ipf_log_main_load __P((void)); +extern int ipf_log_main_unload __P((void)); + + extern char ipfilter_version[]; -extern iplog_t **iplh[IPL_LOGMAX+1], *iplt[IPL_LOGMAX+1]; -extern int iplused[IPL_LOGMAX + 1]; -extern struct frentry *ipfilter[2][2], *ipacct[2][2]; #ifdef USE_INET6 -extern struct frentry *ipfilter6[2][2], *ipacct6[2][2]; extern int icmptoicmp6types[ICMP_MAXTYPE+1]; extern int icmptoicmp6unreach[ICMP_MAX_UNREACH]; extern int icmpreplytype6[ICMP6_MAXTYPE + 1]; #endif +#ifdef IPFILTER_COMPAT +extern int ipf_in_compat __P((ipf_main_softc_t *, ipfobj_t *, void *,int)); +extern int ipf_out_compat __P((ipf_main_softc_t *, ipfobj_t *, void *)); +#endif extern int icmpreplytype4[ICMP_MAXTYPE + 1]; -extern struct frgroup *ipfgroups[IPL_LOGSIZE][2]; -extern struct filterstats frstats[]; -extern frentry_t *ipfrule_match __P((fr_info_t *)); -extern u_char ipf_iss_secret[32]; -extern ipftuneable_t ipf_tuneables[]; + +extern int ipf_ht_node_add __P((ipf_main_softc_t *, host_track_t *, + int, i6addr_t *)); +extern int ipf_ht_node_del __P((host_track_t *, int, i6addr_t *)); +extern void ipf_rb_ht_flush __P((host_track_t *)); +extern void ipf_rb_ht_freenode __P((host_node_t *, void *)); +extern void ipf_rb_ht_init __P((host_track_t *)); #endif /* __IP_FIL_H__ */ diff --git a/sys/contrib/ipfilter/netinet/ip_fil_freebsd.c b/sys/contrib/ipfilter/netinet/ip_fil_freebsd.c index 338b69a50c6..9d2619309fc 100644 --- a/sys/contrib/ipfilter/netinet/ip_fil_freebsd.c +++ b/sys/contrib/ipfilter/netinet/ip_fil_freebsd.c @@ -1,13 +1,13 @@ /* $FreeBSD$ */ /* - * Copyright (C) 1993-2003 by Darren Reed. + * Copyright (C) 2012 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. */ #if !defined(lint) static const char sccsid[] = "@(#)ip_fil.c 2.41 6/5/96 (C) 1993-2000 Darren Reed"; -static const char rcsid[] = "@(#)$Id: ip_fil_freebsd.c,v 2.53.2.50 2007/09/20 12:51:50 darrenr Exp $"; +static const char rcsid[] = "@(#)$Id$"; #endif #if defined(KERNEL) || defined(_KERNEL) @@ -25,60 +25,26 @@ static const char rcsid[] = "@(#)$Id: ip_fil_freebsd.c,v 2.53.2.50 2007/09/20 12 # include "opt_random_ip_id.h" #endif #include -#if defined(__FreeBSD__) && !defined(__FreeBSD_version) -# if defined(IPFILTER_LKM) -# ifndef __FreeBSD_cc_version -# include -# else -# if __FreeBSD_cc_version < 430000 -# include -# endif -# endif -# endif -#endif #include #include #include -#if __FreeBSD_version >= 220000 # include # include -#else -# include -#endif #include #include -#if (__FreeBSD_version >= 300000) # include -#else -# include -#endif +# include +# include #if !defined(__hpux) # include #endif -#include #include -#if __FreeBSD_version >= 500043 # include -#else -# include -#endif -#if __FreeBSD_version >= 800044 # include -#else -#define V_path_mtu_discovery path_mtu_discovery -#define V_ipforwarding ipforwarding -#endif #include -#if __FreeBSD_version >= 300000 # include -# if __FreeBSD_version >= 500043 # include -# endif -# if !defined(IPFILTER_LKM) -# include "opt_ipfilter.h" -# endif -#endif #include #include #include @@ -92,9 +58,6 @@ static const char rcsid[] = "@(#)$Id: ip_fil_freebsd.c,v 2.53.2.50 2007/09/20 12 #include #include #include -#ifndef _KERNEL -# include "netinet/ipf.h" -#endif #include "netinet/ip_compat.h" #ifdef USE_INET6 # include @@ -105,101 +68,94 @@ static const char rcsid[] = "@(#)$Id: ip_fil_freebsd.c,v 2.53.2.50 2007/09/20 12 #include "netinet/ip_state.h" #include "netinet/ip_proxy.h" #include "netinet/ip_auth.h" -#ifdef IPFILTER_SYNC #include "netinet/ip_sync.h" -#endif +#include "netinet/ip_lookup.h" +#include "netinet/ip_dstlist.h" #ifdef IPFILTER_SCAN #include "netinet/ip_scan.h" #endif #include "netinet/ip_pool.h" -#if defined(__FreeBSD_version) && (__FreeBSD_version >= 300000) # include -#endif #include #ifdef CSUM_DATA_VALID #include #endif extern int ip_optcopy __P((struct ip *, struct ip *)); -#if (__FreeBSD_version > 460000) && (__FreeBSD_version < 800055) -extern int path_mtu_discovery; -#endif # ifdef IPFILTER_M_IPFILTER MALLOC_DEFINE(M_IPFILTER, "ipfilter", "IP Filter packet filter data structures"); # endif -#if !defined(__osf__) -extern struct protosw inetsw[]; -#endif - -static int (*fr_savep) __P((ip_t *, int, void *, int, struct mbuf **)); -static int fr_send_ip __P((fr_info_t *, mb_t *, mb_t **)); -# ifdef USE_MUTEXES -ipfmutex_t ipl_mutex, ipf_authmx, ipf_rw, ipf_stinsert; -ipfmutex_t ipf_nat_new, ipf_natio, ipf_timeoutlock; -ipfrwlock_t ipf_mutex, ipf_global, ipf_ipidfrag, ipf_frcache, ipf_tokens; -ipfrwlock_t ipf_frag, ipf_state, ipf_nat, ipf_natfrag, ipf_auth; -# endif +static u_short ipid = 0; +static int (*ipf_savep) __P((void *, ip_t *, int, void *, int, struct mbuf **)); +static int ipf_send_ip __P((fr_info_t *, mb_t *)); +static void ipf_timer_func __P((void *arg)); int ipf_locks_done = 0; -#if (__FreeBSD_version >= 300000) -struct callout_handle fr_slowtimer_ch; -#endif -struct selinfo ipfselwait[IPL_LOGSIZE]; +ipf_main_softc_t ipfmain; -#if (__FreeBSD_version >= 500011) # include # if defined(NETBSD_PF) # include -# if (__FreeBSD_version < 501108) -# include -# endif -/* - * We provide the fr_checkp name just to minimize changes later. - */ -int (*fr_checkp) __P((ip_t *ip, int hlen, void *ifp, int out, mb_t **mp)); # endif /* NETBSD_PF */ -#endif /* __FreeBSD_version >= 500011 */ +/* + * We provide the ipf_checkp name just to minimize changes later. + */ +int (*ipf_checkp) __P((void *, ip_t *ip, int hlen, void *ifp, int out, mb_t **mp)); -#if (__FreeBSD_version >= 502103) static eventhandler_tag ipf_arrivetag, ipf_departtag, ipf_clonetag; static void ipf_ifevent(void *arg); static void ipf_ifevent(arg) -void *arg; + void *arg; { - frsync(NULL); + ipf_sync(arg, NULL); } -#endif -#if (__FreeBSD_version >= 501108) && defined(_KERNEL) static int -fr_check_wrapper(void *arg, struct mbuf **mp, struct ifnet *ifp, int dir) +ipf_check_wrapper(void *arg, struct mbuf **mp, struct ifnet *ifp, int dir) { struct ip *ip = mtod(*mp, struct ip *); - return fr_check(ip, ip->ip_hl << 2, ifp, (dir == PFIL_OUT), mp); + int rv; + + /* + * IPFilter expects evreything in network byte order + */ +#if (__FreeBSD_version < 1000019) + ip->ip_len = htons(ip->ip_len); + ip->ip_off = htons(ip->ip_off); +#endif + rv = ipf_check(&ipfmain, ip, ip->ip_hl << 2, ifp, (dir == PFIL_OUT), + mp); +#if (__FreeBSD_version < 1000019) + if ((rv == 0) && (*mp != NULL)) { + ip = mtod(*mp, struct ip *); + ip->ip_len = ntohs(ip->ip_len); + ip->ip_off = ntohs(ip->ip_off); + } +#endif + return rv; } # ifdef USE_INET6 # include static int -fr_check_wrapper6(void *arg, struct mbuf **mp, struct ifnet *ifp, int dir) +ipf_check_wrapper6(void *arg, struct mbuf **mp, struct ifnet *ifp, int dir) { - return (fr_check(mtod(*mp, struct ip *), sizeof(struct ip6_hdr), - ifp, (dir == PFIL_OUT), mp)); + return (ipf_check(&ipfmain, mtod(*mp, struct ip *), + sizeof(struct ip6_hdr), ifp, (dir == PFIL_OUT), mp)); } # endif -#endif /* __FreeBSD_version >= 501108 */ #if defined(IPFILTER_LKM) -int iplidentify(s) -char *s; +int ipf_identify(s) + char *s; { if (strcmp(s, "ipl") == 0) return 1; @@ -208,49 +164,67 @@ char *s; #endif /* IPFILTER_LKM */ -int ipfattach() +static void +ipf_timer_func(arg) + void *arg; +{ + ipf_main_softc_t *softc = arg; + SPL_INT(s); + + SPL_NET(s); + READ_ENTER(&softc->ipf_global); + + if (softc->ipf_running > 0) + ipf_slowtimer(softc); + + if (softc->ipf_running == -1 || softc->ipf_running == 1) { +#if FREEBSD_GE_REV(300000) + softc->ipf_slow_ch = timeout(ipf_timer_func, softc, hz/2); +#else + timeout(ipf_timer_func, softc, hz/2); +#endif + } + RWLOCK_EXIT(&softc->ipf_global); + SPL_X(s); +} + + +int +ipfattach(softc) + ipf_main_softc_t *softc; { #ifdef USE_SPL int s; #endif SPL_NET(s); - if (fr_running > 0) { + if (softc->ipf_running > 0) { SPL_X(s); return EBUSY; } - MUTEX_INIT(&ipf_rw, "ipf rw mutex"); - MUTEX_INIT(&ipf_timeoutlock, "ipf timeout queue mutex"); - RWLOCK_INIT(&ipf_ipidfrag, "ipf IP NAT-Frag rwlock"); - RWLOCK_INIT(&ipf_tokens, "ipf token rwlock"); - ipf_locks_done = 1; - - if (fr_initialise() < 0) { + if (ipf_init_all(softc) < 0) { SPL_X(s); return EIO; } - if (fr_checkp != fr_check) { - fr_savep = fr_checkp; - fr_checkp = fr_check; + if (ipf_checkp != ipf_check) { + ipf_savep = ipf_checkp; + ipf_checkp = ipf_check; } - bzero((char *)ipfselwait, sizeof(ipfselwait)); - bzero((char *)frcache, sizeof(frcache)); - fr_running = 1; + bzero((char *)ipfmain.ipf_selwait, sizeof(ipfmain.ipf_selwait)); + softc->ipf_running = 1; - if (fr_control_forwarding & 1) + if (softc->ipf_control_forwarding & 1) V_ipforwarding = 1; + ipid = 0; + SPL_X(s); -#if (__FreeBSD_version >= 300000) - fr_slowtimer_ch = timeout(fr_slowtimer, NULL, - (hz / IPF_HZ_DIVIDE) * IPF_HZ_MULT); -#else - timeout(fr_slowtimer, NULL, (hz / IPF_HZ_DIVIDE) * IPF_HZ_MULT); -#endif + softc->ipf_slow_ch = timeout(ipf_timer_func, softc, + (hz / IPF_HZ_DIVIDE) * IPF_HZ_MULT); return 0; } @@ -259,44 +233,32 @@ int ipfattach() * Disable the filter by removing the hooks from the IP input/output * stream. */ -int ipfdetach() +int +ipfdetach(softc) + ipf_main_softc_t *softc; { #ifdef USE_SPL int s; #endif - if (fr_control_forwarding & 2) + + if (softc->ipf_control_forwarding & 2) V_ipforwarding = 0; SPL_NET(s); -#if (__FreeBSD_version >= 300000) - if (fr_slowtimer_ch.callout != NULL) - untimeout(fr_slowtimer, NULL, fr_slowtimer_ch); - bzero(&fr_slowtimer_ch, sizeof(fr_slowtimer_ch)); -#else - untimeout(fr_slowtimer, NULL); -#endif /* FreeBSD */ + if (softc->ipf_slow_ch.callout != NULL) + untimeout(ipf_timer_func, softc, softc->ipf_slow_ch); + bzero(&softc->ipf_slow, sizeof(softc->ipf_slow)); #ifndef NETBSD_PF - if (fr_checkp != NULL) - fr_checkp = fr_savep; - fr_savep = NULL; + if (ipf_checkp != NULL) + ipf_checkp = ipf_savep; + ipf_savep = NULL; #endif - fr_deinitialise(); + ipf_fini_all(softc); - fr_running = -2; - - (void) frflush(IPL_LOGIPF, 0, FR_INQUE|FR_OUTQUE|FR_INACTIVE); - (void) frflush(IPL_LOGIPF, 0, FR_INQUE|FR_OUTQUE); - - if (ipf_locks_done == 1) { - MUTEX_DESTROY(&ipf_timeoutlock); - MUTEX_DESTROY(&ipf_rw); - RW_DESTROY(&ipf_ipidfrag); - RW_DESTROY(&ipf_tokens); - ipf_locks_done = 0; - } + softc->ipf_running = -2; SPL_X(s); @@ -307,62 +269,51 @@ int ipfdetach() /* * Filter ioctl interface. */ -int iplioctl(dev, cmd, data, mode -# if defined(_KERNEL) && ((BSD >= 199506) || (__FreeBSD_version >= 220000)) +int +ipfioctl(dev, cmd, data, mode , p) -# if (__FreeBSD_version >= 500024) -struct thread *p; -# if (__FreeBSD_version >= 500043) + struct thread *p; # define p_cred td_ucred # define p_uid td_ucred->cr_ruid -# else -# define p_cred t_proc->p_cred -# define p_uid t_proc->p_cred->p_ruid -# endif -# else -struct proc *p; -# define p_uid p_cred->p_ruid -# endif /* __FreeBSD_version >= 500024 */ -# else -) -# endif -#if defined(_KERNEL) && (__FreeBSD_version >= 502116) -struct cdev *dev; -#else -dev_t dev; -#endif -ioctlcmd_t cmd; -caddr_t data; -int mode; + struct cdev *dev; + ioctlcmd_t cmd; + caddr_t data; + int mode; { int error = 0, unit = 0; SPL_INT(s); -#if (BSD >= 199306) && defined(_KERNEL) -# if (__FreeBSD_version >= 500034) - if (securelevel_ge(p->p_cred, 3) && (mode & FWRITE)) -# else - if ((securelevel >= 3) && (mode & FWRITE)) -# endif +#if (BSD >= 199306) + if (securelevel_ge(p->p_cred, 3) && (mode & FWRITE)) + { + ipfmain.ipf_interror = 130001; return EPERM; + } #endif unit = GET_MINOR(dev); - if ((IPL_LOGMAX < unit) || (unit < 0)) + if ((IPL_LOGMAX < unit) || (unit < 0)) { + ipfmain.ipf_interror = 130002; return ENXIO; + } - if (fr_running <= 0) { - if (unit != IPL_LOGIPF) + if (ipfmain.ipf_running <= 0) { + if (unit != IPL_LOGIPF && cmd != SIOCIPFINTERROR) { + ipfmain.ipf_interror = 130003; return EIO; + } if (cmd != SIOCIPFGETNEXT && cmd != SIOCIPFGET && cmd != SIOCIPFSET && cmd != SIOCFRENB && - cmd != SIOCGETFS && cmd != SIOCGETFF) + cmd != SIOCGETFS && cmd != SIOCGETFF && + cmd != SIOCIPFINTERROR) { + ipfmain.ipf_interror = 130004; return EIO; + } } SPL_NET(s); - error = fr_ioctlswitch(unit, data, cmd, mode, p->p_uid, p); + error = ipf_ioctlswitch(&ipfmain, unit, data, cmd, mode, p->p_uid, p); if (error != -1) { SPL_X(s); return error; @@ -374,182 +325,13 @@ int mode; } -#if 0 -void fr_forgetifp(ifp) -void *ifp; -{ - register frentry_t *f; - - WRITE_ENTER(&ipf_mutex); - for (f = ipacct[0][fr_active]; (f != NULL); f = f->fr_next) - if (f->fr_ifa == ifp) - f->fr_ifa = (void *)-1; - for (f = ipacct[1][fr_active]; (f != NULL); f = f->fr_next) - if (f->fr_ifa == ifp) - f->fr_ifa = (void *)-1; - for (f = ipfilter[0][fr_active]; (f != NULL); f = f->fr_next) - if (f->fr_ifa == ifp) - f->fr_ifa = (void *)-1; - for (f = ipfilter[1][fr_active]; (f != NULL); f = f->fr_next) - if (f->fr_ifa == ifp) - f->fr_ifa = (void *)-1; -#ifdef USE_INET6 - for (f = ipacct6[0][fr_active]; (f != NULL); f = f->fr_next) - if (f->fr_ifa == ifp) - f->fr_ifa = (void *)-1; - for (f = ipacct6[1][fr_active]; (f != NULL); f = f->fr_next) - if (f->fr_ifa == ifp) - f->fr_ifa = (void *)-1; - for (f = ipfilter6[0][fr_active]; (f != NULL); f = f->fr_next) - if (f->fr_ifa == ifp) - f->fr_ifa = (void *)-1; - for (f = ipfilter6[1][fr_active]; (f != NULL); f = f->fr_next) - if (f->fr_ifa == ifp) - f->fr_ifa = (void *)-1; -#endif - RWLOCK_EXIT(&ipf_mutex); - fr_natsync(ifp); -} -#endif - - /* - * routines below for saving IP headers to buffer - */ -int iplopen(dev, flags -#if ((BSD >= 199506) || (__FreeBSD_version >= 220000)) && defined(_KERNEL) -, devtype, p) -int devtype; -# if (__FreeBSD_version >= 500024) -struct thread *p; -# else -struct proc *p; -# endif /* __FreeBSD_version >= 500024 */ -#else -) -#endif -#if defined(_KERNEL) && (__FreeBSD_version >= 502116) -struct cdev *dev; -#else -dev_t dev; -#endif -int flags; -{ - u_int min = GET_MINOR(dev); - - if (IPL_LOGMAX < min) - min = ENXIO; - else - min = 0; - return min; -} - - -int iplclose(dev, flags -#if ((BSD >= 199506) || (__FreeBSD_version >= 220000)) && defined(_KERNEL) -, devtype, p) -int devtype; -# if (__FreeBSD_version >= 500024) -struct thread *p; -# else -struct proc *p; -# endif /* __FreeBSD_version >= 500024 */ -#else -) -#endif -#if defined(_KERNEL) && (__FreeBSD_version >= 502116) -struct cdev *dev; -#else -dev_t dev; -#endif -int flags; -{ - u_int min = GET_MINOR(dev); - - if (IPL_LOGMAX < min) - min = ENXIO; - else - min = 0; - return min; -} - -/* - * iplread/ipllog - * both of these must operate with at least splnet() lest they be - * called during packet processing and cause an inconsistancy to appear in - * the filter lists. - */ -#if (BSD >= 199306) -int iplread(dev, uio, ioflag) -int ioflag; -#else -int iplread(dev, uio) -#endif -#if defined(_KERNEL) && (__FreeBSD_version >= 502116) -struct cdev *dev; -#else -dev_t dev; -#endif -register struct uio *uio; -{ - u_int xmin = GET_MINOR(dev); - - if (fr_running < 1) - return EIO; - - if (xmin < 0) - return ENXIO; - -# ifdef IPFILTER_SYNC - if (xmin == IPL_LOGSYNC) - return ipfsync_read(uio); -# endif - -#ifdef IPFILTER_LOG - return ipflog_read(xmin, uio); -#else - return ENXIO; -#endif -} - - -/* - * iplwrite - * both of these must operate with at least splnet() lest they be - * called during packet processing and cause an inconsistancy to appear in - * the filter lists. - */ -#if (BSD >= 199306) -int iplwrite(dev, uio, ioflag) -int ioflag; -#else -int iplwrite(dev, uio) -#endif -#if defined(_KERNEL) && (__FreeBSD_version >= 502116) -struct cdev *dev; -#else -dev_t dev; -#endif -register struct uio *uio; -{ - - if (fr_running < 1) - return EIO; - -#ifdef IPFILTER_SYNC - if (GET_MINOR(dev) == IPL_LOGSYNC) - return ipfsync_write(uio); -#endif - return ENXIO; -} - - -/* - * fr_send_reset - this could conceivably be a call to tcp_respond(), but that + * ipf_send_reset - this could conceivably be a call to tcp_respond(), but that * requires a large amount of setting up and isn't any more efficient. */ -int fr_send_reset(fin) -fr_info_t *fin; +int +ipf_send_reset(fin) + fr_info_t *fin; { struct tcphdr *tcp, *tcp2; int tlen = 0, hlen; @@ -563,7 +345,7 @@ fr_info_t *fin; if (tcp->th_flags & TH_RST) return -1; /* feedback loop */ - if (fr_checkl4sum(fin) == -1) + if (ipf_checkl4sum(fin) == -1) return -1; tlen = fin->fin_dlen - (TCP_OFF(tcp) << 2) + @@ -628,11 +410,11 @@ fr_info_t *fin; ip6->ip6_plen = htons(sizeof(struct tcphdr)); ip6->ip6_nxt = IPPROTO_TCP; ip6->ip6_hlim = 0; - ip6->ip6_src = fin->fin_dst6; - ip6->ip6_dst = fin->fin_src6; + ip6->ip6_src = fin->fin_dst6.in6; + ip6->ip6_dst = fin->fin_src6.in6; tcp2->th_sum = in6_cksum(m, IPPROTO_TCP, sizeof(*ip6), sizeof(*tcp2)); - return fr_send_ip(fin, m, &m); + return ipf_send_ip(fin, m); } #endif ip->ip_p = IPPROTO_TCP; @@ -640,14 +422,18 @@ fr_info_t *fin; ip->ip_src.s_addr = fin->fin_daddr; ip->ip_dst.s_addr = fin->fin_saddr; tcp2->th_sum = in_cksum(m, hlen + sizeof(*tcp2)); - ip->ip_len = hlen + sizeof(*tcp2); - return fr_send_ip(fin, m, &m); + ip->ip_len = htons(hlen + sizeof(*tcp2)); + return ipf_send_ip(fin, m); } -static int fr_send_ip(fin, m, mpp) -fr_info_t *fin; -mb_t *m, **mpp; +/* + * ip_len must be in network byte order when called. + */ +static int +ipf_send_ip(fin, m) + fr_info_t *fin; + mb_t *m; { fr_info_t fnew; ip_t *ip, *oip; @@ -655,24 +441,27 @@ mb_t *m, **mpp; ip = mtod(m, ip_t *); bzero((char *)&fnew, sizeof(fnew)); + fnew.fin_main_soft = fin->fin_main_soft; IP_V_A(ip, fin->fin_v); switch (fin->fin_v) { case 4 : - fnew.fin_v = 4; oip = fin->fin_ip; + hlen = sizeof(*oip); + fnew.fin_v = 4; + fnew.fin_p = ip->ip_p; + fnew.fin_plen = ntohs(ip->ip_len); IP_HL_A(ip, sizeof(*oip) >> 2); ip->ip_tos = oip->ip_tos; ip->ip_id = fin->fin_ip->ip_id; -#if (__FreeBSD_version > 460000) - ip->ip_off = V_path_mtu_discovery ? IP_DF : 0; +#if defined(FreeBSD) && (__FreeBSD_version > 460000) + ip->ip_off = htons(path_mtu_discovery ? IP_DF : 0); #else ip->ip_off = 0; #endif ip->ip_ttl = V_ip_defttl; ip->ip_sum = 0; - hlen = sizeof(*oip); break; #ifdef USE_INET6 case 6 : @@ -682,8 +471,10 @@ mb_t *m, **mpp; ip6->ip6_vfc = 0x60; ip6->ip6_hlim = IPDEFTTL; - fnew.fin_v = 6; hlen = sizeof(*ip6); + fnew.fin_p = ip6->ip6_nxt; + fnew.fin_v = 6; + fnew.fin_plen = ntohs(ip6->ip6_plen) + hlen; break; } #endif @@ -698,28 +489,29 @@ mb_t *m, **mpp; fnew.fin_flx = FI_NOCKSUM; fnew.fin_m = m; fnew.fin_ip = ip; - fnew.fin_mp = mpp; + fnew.fin_mp = &m; fnew.fin_hlen = hlen; fnew.fin_dp = (char *)ip + hlen; - (void) fr_makefrip(hlen, ip, &fnew); + (void) ipf_makefrip(hlen, ip, &fnew); - return fr_fastroute(m, mpp, &fnew, NULL); + return ipf_fastroute(m, &m, &fnew, NULL); } -int fr_send_icmp_err(type, fin, dst) -int type; -fr_info_t *fin; -int dst; +int +ipf_send_icmp_err(type, fin, dst) + int type; + fr_info_t *fin; + int dst; { int err, hlen, xtra, iclen, ohlen, avail, code; struct in_addr dst4; struct icmp *icmp; struct mbuf *m; + i6addr_t dst6; void *ifp; #ifdef USE_INET6 ip6_t *ip6; - struct in6_addr dst6; #endif ip_t *ip, *ip2; @@ -728,11 +520,17 @@ int dst; code = fin->fin_icode; #ifdef USE_INET6 - if ((code < 0) || (code > sizeof(icmptoicmp6unreach)/sizeof(int))) +#if 0 + /* XXX Fix an off by one error: s/>/>=/ + was: + if ((code < 0) || (code > sizeof(icmptoicmp6unreach)/sizeof(int))) + Fix obtained from NetBSD ip_fil_netbsd.c r1.4: */ +#endif + if ((code < 0) || (code >= sizeof(icmptoicmp6unreach)/sizeof(int))) return -1; #endif - if (fr_checkl4sum(fin) == -1) + if (ipf_checkl4sum(fin) == -1) return -1; #ifdef MGETHDR MGETHDR(m, M_DONTWAIT, MT_HEADER); @@ -746,10 +544,10 @@ int dst; xtra = 0; hlen = 0; ohlen = 0; + dst4.s_addr = 0; ifp = fin->fin_ifp; if (fin->fin_v == 4) { - if ((fin->fin_p == IPPROTO_ICMP) && - !(fin->fin_flx & FI_SHORT)) + if ((fin->fin_p == IPPROTO_ICMP) && !(fin->fin_flx & FI_SHORT)) switch (ntohs(fin->fin_data[0]) >> 8) { case ICMP_ECHO : @@ -763,16 +561,18 @@ int dst; } if (dst == 0) { - if (fr_ifpaddr(4, FRI_NORMAL, ifp, - &dst4, NULL) == -1) { + if (ipf_ifpaddr(&ipfmain, 4, FRI_NORMAL, ifp, + &dst6, NULL) == -1) { FREE_MB_T(m); return -1; } + dst4 = dst6.in4; } else dst4.s_addr = fin->fin_daddr; hlen = sizeof(ip_t); ohlen = fin->fin_hlen; + iclen = hlen + offsetof(struct icmp, icmp_ip) + ohlen; if (fin->fin_hlen < fin->fin_plen) xtra = MIN(fin->fin_dlen, 8); else @@ -783,12 +583,12 @@ int dst; else if (fin->fin_v == 6) { hlen = sizeof(ip6_t); ohlen = sizeof(ip6_t); + iclen = hlen + offsetof(struct icmp, icmp_ip) + ohlen; type = icmptoicmp6types[type]; if (type == ICMP6_DST_UNREACH) code = icmptoicmp6unreach[code]; - if (hlen + sizeof(*icmp) + max_linkhdr + - fin->fin_plen > avail) { + if (iclen + max_linkhdr + fin->fin_plen > avail) { MCLGET(m, M_DONTWAIT); if ((m->m_flags & M_EXT) == 0) { FREE_MB_T(m); @@ -796,11 +596,11 @@ int dst; } avail = MCLBYTES; } - xtra = MIN(fin->fin_plen, - avail - hlen - sizeof(*icmp) - max_linkhdr); + xtra = MIN(fin->fin_plen, avail - iclen - max_linkhdr); + xtra = MIN(xtra, IPV6_MMTU - iclen); if (dst == 0) { - if (fr_ifpaddr(6, FRI_NORMAL, ifp, - (struct in_addr *)&dst6, NULL) == -1) { + if (ipf_ifpaddr(&ipfmain, 6, FRI_NORMAL, ifp, + &dst6, NULL) == -1) { FREE_MB_T(m); return -1; } @@ -813,7 +613,6 @@ int dst; return -1; } - iclen = hlen + sizeof(*icmp); avail -= (max_linkhdr + iclen); if (avail < 0) { FREE_MB_T(m); @@ -834,9 +633,17 @@ int dst; icmp->icmp_code = fin->fin_icode; icmp->icmp_cksum = 0; #ifdef icmp_nextmtu - if (type == ICMP_UNREACH && - fin->fin_icode == ICMP_UNREACH_NEEDFRAG && ifp) - icmp->icmp_nextmtu = htons(((struct ifnet *)ifp)->if_mtu); + if (type == ICMP_UNREACH && fin->fin_icode == ICMP_UNREACH_NEEDFRAG) { + if (fin->fin_mtu != 0) { + icmp->icmp_nextmtu = htons(fin->fin_mtu); + + } else if (ifp != NULL) { + icmp->icmp_nextmtu = htons(GETIFMTU_4(ifp)); + + } else { /* make up a number... */ + icmp->icmp_nextmtu = htons(fin->fin_plen - 20); + } + } #endif bcopy((char *)fin->fin_ip, (char *)ip2, ohlen); @@ -848,8 +655,8 @@ int dst; ip6->ip6_plen = htons(iclen - hlen); ip6->ip6_nxt = IPPROTO_ICMPV6; ip6->ip6_hlim = 0; - ip6->ip6_src = dst6; - ip6->ip6_dst = fin->fin_src6; + ip6->ip6_src = dst6.in6; + ip6->ip6_dst = fin->fin_src6.in6; if (xtra > 0) bcopy((char *)fin->fin_ip + ohlen, (char *)&icmp->icmp_ip + ohlen, xtra); @@ -858,8 +665,6 @@ int dst; } else #endif { - ip2->ip_len = htons(ip2->ip_len); - ip2->ip_off = htons(ip2->ip_off); ip->ip_p = IPPROTO_ICMP; ip->ip_src.s_addr = dst4.s_addr; ip->ip_dst.s_addr = fin->fin_saddr; @@ -869,41 +674,25 @@ int dst; (char *)&icmp->icmp_ip + ohlen, xtra); icmp->icmp_cksum = ipf_cksum((u_short *)icmp, sizeof(*icmp) + 8); - ip->ip_len = iclen; + ip->ip_len = htons(iclen); ip->ip_p = IPPROTO_ICMP; } - err = fr_send_ip(fin, m, &m); + err = ipf_send_ip(fin, m); return err; } -#if !defined(IPFILTER_LKM) && (__FreeBSD_version < 300000) -# if (BSD < 199306) -int iplinit __P((void)); - -int -# else -void iplinit __P((void)); - -void -# endif -iplinit() -{ - if (ipfattach() != 0) - printf("IP Filter failed to attach\n"); - ip_init(); -} -#endif /* __FreeBSD_version < 300000 */ /* * m0 - pointer to mbuf where the IP packet starts * mpp - pointer to the mbuf pointer that is the start of the mbuf chain */ -int fr_fastroute(m0, mpp, fin, fdp) -mb_t *m0, **mpp; -fr_info_t *fin; -frdest_t *fdp; +int +ipf_fastroute(m0, mpp, fin, fdp) + mb_t *m0, **mpp; + fr_info_t *fin; + frdest_t *fdp; { register struct ip *ip, *mhip; register struct mbuf *m = *mpp; @@ -913,6 +702,7 @@ frdest_t *fdp; struct sockaddr_in *dst; struct route iproute; u_short ip_off; + frdest_t node; frentry_t *fr; ro = NULL; @@ -949,33 +739,36 @@ frdest_t *fdp; * currently "to " and "to :ip#" are not supported * for IPv6 */ -#if (__FreeBSD_version >= 490000) - return ip6_output(m0, NULL, NULL, 0, NULL, NULL, NULL); -#else - return ip6_output(m0, NULL, NULL, 0, NULL, NULL); -#endif + return ip6_output(m, NULL, NULL, 0, NULL, NULL, NULL); } #endif hlen = fin->fin_hlen; ip = mtod(m0, struct ip *); + ifp = NULL; /* * Route packet. */ ro = &iproute; - bzero((caddr_t)ro, sizeof (*ro)); + bzero(ro, sizeof (*ro)); dst = (struct sockaddr_in *)&ro->ro_dst; dst->sin_family = AF_INET; dst->sin_addr = ip->ip_dst; fr = fin->fin_fr; + if ((fr != NULL) && !(fr->fr_flags & FR_KEEPSTATE) && (fdp != NULL) && + (fdp->fd_type == FRD_DSTLIST)) { + if (ipf_dstlist_select_node(fin, fdp->fd_ptr, NULL, &node) == 0) + fdp = &node; + } + if (fdp != NULL) - ifp = fdp->fd_ifp; + ifp = fdp->fd_ptr; else ifp = fin->fin_ifp; - if ((ifp == NULL) && (!fr || !(fr->fr_flags & FR_FASTROUTE))) { + if ((ifp == NULL) && ((fr == NULL) || !(fr->fr_flags & FR_FASTROUTE))) { error = -2; goto bad; } @@ -1012,21 +805,19 @@ frdest_t *fdp; sifp = fin->fin_ifp; fin->fin_ifp = ifp; fin->fin_out = 1; - (void) fr_acctpkt(fin, NULL); + (void) ipf_acctpkt(fin, NULL); fin->fin_fr = NULL; if (!fr || !(fr->fr_flags & FR_RETMASK)) { u_32_t pass; - if (fr_checkstate(fin, &pass) != NULL) - fr_statederef((ipstate_t **)&fin->fin_state); + (void) ipf_state_check(fin, &pass); } - switch (fr_checknatout(fin, NULL)) + switch (ipf_nat_checkout(fin, NULL)) { case 0 : break; case 1 : - fr_natderef((nat_t **)&fin->fin_nat); ip->ip_sum = 0; break; case -1 : @@ -1042,14 +833,12 @@ frdest_t *fdp; /* * If small enough for interface, can just send directly. */ - if (ip->ip_len <= ifp->if_mtu) { - ip->ip_len = htons(ip->ip_len); - ip->ip_off = htons(ip->ip_off); - + if (ntohs(ip->ip_len) <= ifp->if_mtu) { if (!ip->ip_sum) ip->ip_sum = in_cksum(m, hlen); error = (*ifp->if_output)(ifp, m, (struct sockaddr *)dst, - ro); + ro + ); goto done; } /* @@ -1077,7 +866,7 @@ frdest_t *fdp; */ m0 = m; mhlen = sizeof (struct ip); - for (off = hlen + len; off < ip->ip_len; off += len) { + for (off = hlen + len; off < ntohs(ip->ip_len); off += len) { #ifdef MGETHDR MGETHDR(m, M_DONTWAIT, MT_HEADER); #else @@ -1097,8 +886,8 @@ frdest_t *fdp; } m->m_len = mhlen; mhip->ip_off = ((off - hlen) >> 3) + ip_off; - if (off + len >= ip->ip_len) - len = ip->ip_len - off; + if (off + len >= ntohs(ip->ip_len)) + len = ntohs(ip->ip_len) - off; else mhip->ip_off |= IP_MF; mhip->ip_len = htons((u_short)(len + mhlen)); @@ -1130,21 +919,22 @@ sendorfree: m->m_act = 0; if (error == 0) error = (*ifp->if_output)(ifp, m, - (struct sockaddr *)dst, ro); + (struct sockaddr *)dst, + ro + ); else FREE_MB_T(m); } - } + } done: if (!error) - fr_frouteok[0]++; + ipfmain.ipf_frouteok[0]++; else - fr_frouteok[1]++; + ipfmain.ipf_frouteok[1]++; if ((ro != NULL) && (ro->ro_rt != NULL)) { RTFREE(ro->ro_rt); } - *mpp = NULL; return 0; bad: if (error == EMSGSIZE) { @@ -1152,7 +942,7 @@ bad: code = fin->fin_icode; fin->fin_icode = ICMP_UNREACH_NEEDFRAG; fin->fin_ifp = ifp; - (void) fr_send_icmp_err(ICMP_UNREACH, fin, 1); + (void) ipf_send_icmp_err(ICMP_UNREACH, fin, 1); fin->fin_ifp = sifp; fin->fin_icode = code; } @@ -1161,8 +951,9 @@ bad: } -int fr_verifysrc(fin) -fr_info_t *fin; +int +ipf_verifysrc(fin) + fr_info_t *fin; { struct sockaddr_in *dst; struct route iproute; @@ -1182,10 +973,12 @@ fr_info_t *fin; /* * return the first IP Address associated with an interface */ -int fr_ifpaddr(v, atype, ifptr, inp, inpmask) -int v, atype; -void *ifptr; -struct in_addr *inp, *inpmask; +int +ipf_ifpaddr(softc, v, atype, ifptr, inp, inpmask) + ipf_main_softc_t *softc; + int v, atype; + void *ifptr; + i6addr_t *inp, *inpmask; { #ifdef USE_INET6 struct in6_addr *inp6 = NULL; @@ -1202,16 +995,12 @@ struct in_addr *inp, *inpmask; ifp = ifptr; if (v == 4) - inp->s_addr = 0; + inp->in4.s_addr = 0; #ifdef USE_INET6 else if (v == 6) - bzero((char *)inp, sizeof(struct in6_addr)); + bzero((char *)inp, sizeof(*inp)); #endif -#if (__FreeBSD_version >= 300000) ifa = TAILQ_FIRST(&ifp->if_addrhead); -#else - ifa = ifp->if_addrlist; -#endif /* __FreeBSD_version >= 300000 */ sock = ifa->ifa_addr; while (sock != NULL && ifa != NULL) { @@ -1226,11 +1015,7 @@ struct in_addr *inp, *inpmask; break; } #endif -#if (__FreeBSD_version >= 300000) ifa = TAILQ_NEXT(ifa, ifa_link); -#else - ifa = ifa->ifa_next; -#endif /* __FreeBSD_version >= 300000 */ if (ifa != NULL) sock = ifa->ifa_addr; } @@ -1249,79 +1034,45 @@ struct in_addr *inp, *inpmask; #ifdef USE_INET6 if (v == 6) { - return fr_ifpfillv6addr(atype, (struct sockaddr_in6 *)sock, - (struct sockaddr_in6 *)mask, - inp, inpmask); + return ipf_ifpfillv6addr(atype, (struct sockaddr_in6 *)sock, + (struct sockaddr_in6 *)mask, + inp, inpmask); } #endif - return fr_ifpfillv4addr(atype, (struct sockaddr_in *)sock, - (struct sockaddr_in *)mask, inp, inpmask); + return ipf_ifpfillv4addr(atype, (struct sockaddr_in *)sock, + (struct sockaddr_in *)mask, + &inp->in4, &inpmask->in4); } -u_32_t fr_newisn(fin) -fr_info_t *fin; +u_32_t +ipf_newisn(fin) + fr_info_t *fin; { u_32_t newiss; -#if (__FreeBSD_version >= 400000) newiss = arc4random(); -#else - static iss_seq_off = 0; - u_char hash[16]; - MD5_CTX ctx; - - /* - * Compute the base value of the ISS. It is a hash - * of (saddr, sport, daddr, dport, secret). - */ - MD5Init(&ctx); - - MD5Update(&ctx, (u_char *) &fin->fin_fi.fi_src, - sizeof(fin->fin_fi.fi_src)); - MD5Update(&ctx, (u_char *) &fin->fin_fi.fi_dst, - sizeof(fin->fin_fi.fi_dst)); - MD5Update(&ctx, (u_char *) &fin->fin_dat, sizeof(fin->fin_dat)); - - MD5Update(&ctx, ipf_iss_secret, sizeof(ipf_iss_secret)); - - MD5Final(hash, &ctx); - - memcpy(&newiss, hash, sizeof(newiss)); - - /* - * Now increment our "timer", and add it in to - * the computed value. - * - * XXX Use `addin'? - * XXX TCP_ISSINCR too large to use? - */ - iss_seq_off += 0x00010000; - newiss += iss_seq_off; -#endif return newiss; } /* ------------------------------------------------------------------------ */ -/* Function: fr_nextipid */ +/* Function: ipf_nextipid */ /* Returns: int - 0 == success, -1 == error (packet should be droppped) */ /* Parameters: fin(I) - pointer to packet information */ /* */ /* Returns the next IPv4 ID to use for this packet. */ /* ------------------------------------------------------------------------ */ -u_short fr_nextipid(fin) -fr_info_t *fin; +u_short +ipf_nextipid(fin) + fr_info_t *fin; { + u_short id; + #ifndef RANDOM_IP_ID - static u_short ipid = 0; - u_short id; - - MUTEX_ENTER(&ipf_rw); + MUTEX_ENTER(&ipfmain.ipf_rw); id = ipid++; - MUTEX_EXIT(&ipf_rw); + MUTEX_EXIT(&ipfmain.ipf_rw); #else - u_short id; - id = ip_randomid(); #endif @@ -1329,8 +1080,9 @@ fr_info_t *fin; } -INLINE void fr_checkv4sum(fin) -fr_info_t *fin; +INLINE int +ipf_checkv4sum(fin) + fr_info_t *fin; { #ifdef CSUM_DATA_VALID int manual = 0; @@ -1339,10 +1091,13 @@ fr_info_t *fin; mb_t *m; if ((fin->fin_flx & FI_NOCKSUM) != 0) - return; + return 0; - if (fin->fin_cksum != 0) - return; + if ((fin->fin_flx & FI_SHORT) != 0) + return 1; + + if (fin->fin_cksum != FI_CK_NEEDED) + return (fin->fin_cksum > FI_CK_NEEDED) ? 0 : -1; m = fin->fin_m; if (m == NULL) { @@ -1351,56 +1106,86 @@ fr_info_t *fin; } ip = fin->fin_ip; + if ((m->m_pkthdr.csum_flags & (CSUM_IP_CHECKED|CSUM_IP_VALID)) == + CSUM_IP_CHECKED) { + fin->fin_cksum = FI_CK_BAD; + fin->fin_flx |= FI_BAD; + return -1; + } if (m->m_pkthdr.csum_flags & CSUM_DATA_VALID) { if (m->m_pkthdr.csum_flags & CSUM_PSEUDO_HDR) sum = m->m_pkthdr.csum_data; else sum = in_pseudo(ip->ip_src.s_addr, ip->ip_dst.s_addr, htonl(m->m_pkthdr.csum_data + - fin->fin_ip->ip_len - - (fin->fin_ip->ip_hl << 2) + - fin->fin_p)); + fin->fin_dlen + fin->fin_p)); sum ^= 0xffff; if (sum != 0) { + fin->fin_cksum = FI_CK_BAD; fin->fin_flx |= FI_BAD; - fin->fin_cksum = -1; } else { - fin->fin_cksum = 1; + fin->fin_cksum = FI_CK_SUMOK; + return 0; } - } else - manual = 1; + } else { + if (m->m_pkthdr.csum_flags == CSUM_DELAY_DATA) { + fin->fin_cksum = FI_CK_L4FULL; + return 0; + } else if (m->m_pkthdr.csum_flags == CSUM_TCP || + m->m_pkthdr.csum_flags == CSUM_UDP) { + fin->fin_cksum = FI_CK_L4PART; + return 0; + } else if (m->m_pkthdr.csum_flags == CSUM_IP) { + fin->fin_cksum = FI_CK_L4PART; + return 0; + } else { + manual = 1; + } + } skipauto: -# ifdef IPFILTER_CKSUM - if (manual != 0) - if (fr_checkl4sum(fin) == -1) + if (manual != 0) { + if (ipf_checkl4sum(fin) == -1) { fin->fin_flx |= FI_BAD; -# else - ; -# endif + return -1; + } + } #else -# ifdef IPFILTER_CKSUM - if (fr_checkl4sum(fin) == -1) + if (ipf_checkl4sum(fin) == -1) { fin->fin_flx |= FI_BAD; -# endif + return -1; + } #endif + return 0; } #ifdef USE_INET6 -INLINE void fr_checkv6sum(fin) -fr_info_t *fin; +INLINE int +ipf_checkv6sum(fin) + fr_info_t *fin; { -# ifdef IPFILTER_CKSUM - if (fr_checkl4sum(fin) == -1) + if ((fin->fin_flx & FI_NOCKSUM) != 0) + return 0; + + if ((fin->fin_flx & FI_SHORT) != 0) + return 1; + + if (fin->fin_cksum != FI_CK_NEEDED) + return (fin->fin_cksum > FI_CK_NEEDED) ? 0 : -1; + + if (ipf_checkl4sum(fin) == -1) { fin->fin_flx |= FI_BAD; -# endif + return -1; + } + return 0; } #endif /* USE_INET6 */ -size_t mbufchainlen(m0) -struct mbuf *m0; -{ +size_t +mbufchainlen(m0) + struct mbuf *m0; + { size_t len; if ((m0->m_flags & M_PKTHDR) != 0) { @@ -1416,29 +1201,30 @@ struct mbuf *m0; /* ------------------------------------------------------------------------ */ -/* Function: fr_pullup */ +/* Function: ipf_pullup */ /* Returns: NULL == pullup failed, else pointer to protocol header */ -/* Parameters: m(I) - pointer to buffer where data packet starts */ +/* Parameters: xmin(I)- pointer to buffer where data packet starts */ /* fin(I) - pointer to packet information */ /* len(I) - number of bytes to pullup */ /* */ /* Attempt to move at least len bytes (from the start of the buffer) into a */ /* single buffer for ease of access. Operating system native functions are */ /* used to manage buffers - if necessary. If the entire packet ends up in */ -/* a single buffer, set the FI_COALESCE flag even though fr_coalesce() has */ +/* a single buffer, set the FI_COALESCE flag even though ipf_coalesce() has */ /* not been called. Both fin_ip and fin_dp are updated before exiting _IF_ */ /* and ONLY if the pullup succeeds. */ /* */ -/* We assume that 'min' is a pointer to a buffer that is part of the chain */ +/* We assume that 'xmin' is a pointer to a buffer that is part of the chain */ /* of buffers that starts at *fin->fin_mp. */ /* ------------------------------------------------------------------------ */ -void *fr_pullup(min, fin, len) -mb_t *min; -fr_info_t *fin; -int len; +void * +ipf_pullup(xmin, fin, len) + mb_t *xmin; + fr_info_t *fin; + int len; { - int out = fin->fin_out, dpoff, ipoff; - mb_t *m = min; + int dpoff, ipoff; + mb_t *m = xmin; char *ip; if (m == NULL) @@ -1455,12 +1241,25 @@ int len; dpoff = 0; if (M_LEN(m) < len) { -#ifdef MHLEN + mb_t *n = *fin->fin_mp; /* * Assume that M_PKTHDR is set and just work with what is left * rather than check.. * Should not make any real difference, anyway. */ + if (m != n) { + /* + * Record the mbuf that points to the mbuf that we're + * about to go to work on so that we can update the + * m_next appropriately later. + */ + for (; n->m_next != m; n = n->m_next) + ; + } else { + n = NULL; + } + +#ifdef MHLEN if (len > MHLEN) #else if (len > MLEN) @@ -1472,29 +1271,46 @@ int len; #else FREE_MB_T(*fin->fin_mp); m = NULL; + n = NULL; #endif } else { m = m_pullup(m, len); } - *fin->fin_mp = m; + if (n != NULL) + n->m_next = m; if (m == NULL) { + /* + * When n is non-NULL, it indicates that m pointed to + * a sub-chain (tail) of the mbuf and that the head + * of this chain has not yet been free'd. + */ + if (n != NULL) { + FREE_MB_T(*fin->fin_mp); + } + + *fin->fin_mp = NULL; fin->fin_m = NULL; - ATOMIC_INCL(frstats[out].fr_pull[1]); return NULL; } + if (n == NULL) + *fin->fin_mp = m; + while (M_LEN(m) == 0) { m = m->m_next; } fin->fin_m = m; ip = MTOD(m, char *) + ipoff; - } - ATOMIC_INCL(frstats[out].fr_pull[0]); - fin->fin_ip = (ip_t *)ip; - if (fin->fin_dp != NULL) - fin->fin_dp = (char *)fin->fin_ip + dpoff; + fin->fin_ip = (ip_t *)ip; + if (fin->fin_dp != NULL) + fin->fin_dp = (char *)fin->fin_ip + dpoff; + if (fin->fin_fraghdr != NULL) + fin->fin_fraghdr = (char *)ip + + ((char *)fin->fin_fraghdr - + (char *)fin->fin_ip); + } if (len == fin->fin_plen) fin->fin_flx |= FI_COALESCE; @@ -1502,45 +1318,19 @@ int len; } -int ipf_inject(fin, m) -fr_info_t *fin; -mb_t *m; +int +ipf_inject(fin, m) + fr_info_t *fin; + mb_t *m; { int error = 0; if (fin->fin_out == 0) { -#if (__FreeBSD_version >= 501000) netisr_dispatch(NETISR_IP, m); -#else - struct ifqueue *ifq; - - ifq = &ipintrq; - -# ifdef _IF_QFULL - if (_IF_QFULL(ifq)) -# else - if (IF_QFULL(ifq)) -# endif - { -# ifdef _IF_DROP - _IF_DROP(ifq); -# else - IF_DROP(ifq); -# endif - FREE_MB_T(m); - error = ENOBUFS; - } else { - IF_ENQUEUE(ifq, m); - } -#endif } else { fin->fin_ip->ip_len = ntohs(fin->fin_ip->ip_len); fin->fin_ip->ip_off = ntohs(fin->fin_ip->ip_off); -#if (__FreeBSD_version >= 470102) error = ip_output(m, NULL, NULL, IP_FORWARDING, NULL, NULL); -#else - error = ip_output(m, NULL, NULL, IP_FORWARDING, NULL); -#endif } return error; @@ -1548,38 +1338,22 @@ mb_t *m; int ipf_pfil_unhook(void) { #if defined(NETBSD_PF) && (__FreeBSD_version >= 500011) -# if __FreeBSD_version >= 501108 struct pfil_head *ph_inet; # ifdef USE_INET6 struct pfil_head *ph_inet6; # endif -# endif #endif #ifdef NETBSD_PF -# if (__FreeBSD_version >= 500011) -# if (__FreeBSD_version >= 501108) ph_inet = pfil_head_get(PFIL_TYPE_AF, AF_INET); if (ph_inet != NULL) - pfil_remove_hook((void *)fr_check_wrapper, NULL, + pfil_remove_hook((void *)ipf_check_wrapper, NULL, PFIL_IN|PFIL_OUT|PFIL_WAITOK, ph_inet); -# else - pfil_remove_hook((void *)fr_check, PFIL_IN|PFIL_OUT|PFIL_WAITOK, - &inetsw[ip_protox[IPPROTO_IP]].pr_pfh); -# endif -# else - pfil_remove_hook((void *)fr_check, PFIL_IN|PFIL_OUT|PFIL_WAITOK); -# endif # ifdef USE_INET6 -# if (__FreeBSD_version >= 501108) ph_inet6 = pfil_head_get(PFIL_TYPE_AF, AF_INET6); if (ph_inet6 != NULL) - pfil_remove_hook((void *)fr_check_wrapper6, NULL, + pfil_remove_hook((void *)ipf_check_wrapper6, NULL, PFIL_IN|PFIL_OUT|PFIL_WAITOK, ph_inet6); -# else - pfil_remove_hook((void *)fr_check, PFIL_IN|PFIL_OUT|PFIL_WAITOK, - &inet6sw[ip6_protox[IPPROTO_IPV6]].pr_pfh); -# endif # endif #endif @@ -1588,17 +1362,13 @@ int ipf_pfil_unhook(void) { int ipf_pfil_hook(void) { #if defined(NETBSD_PF) && (__FreeBSD_version >= 500011) -# if __FreeBSD_version >= 501108 struct pfil_head *ph_inet; # ifdef USE_INET6 struct pfil_head *ph_inet6; # endif -# endif #endif # ifdef NETBSD_PF -# if __FreeBSD_version >= 500011 -# if __FreeBSD_version >= 501108 ph_inet = pfil_head_get(PFIL_TYPE_AF, AF_INET); # ifdef USE_INET6 ph_inet6 = pfil_head_get(PFIL_TYPE_AF, AF_INET6); @@ -1607,28 +1377,17 @@ int ipf_pfil_hook(void) { # ifdef USE_INET6 && ph_inet6 == NULL # endif - ) + ) { return ENODEV; + } if (ph_inet != NULL) - pfil_add_hook((void *)fr_check_wrapper, NULL, + pfil_add_hook((void *)ipf_check_wrapper, NULL, PFIL_IN|PFIL_OUT|PFIL_WAITOK, ph_inet); -# else - pfil_add_hook((void *)fr_check, PFIL_IN|PFIL_OUT|PFIL_WAITOK, - &inetsw[ip_protox[IPPROTO_IP]].pr_pfh); -# endif -# else - pfil_add_hook((void *)fr_check, PFIL_IN|PFIL_OUT|PFIL_WAITOK); -# endif # ifdef USE_INET6 -# if __FreeBSD_version >= 501108 if (ph_inet6 != NULL) - pfil_add_hook((void *)fr_check_wrapper6, NULL, + pfil_add_hook((void *)ipf_check_wrapper6, NULL, PFIL_IN|PFIL_OUT|PFIL_WAITOK, ph_inet6); -# else - pfil_add_hook((void *)fr_check, PFIL_IN|PFIL_OUT|PFIL_WAITOK, - &inet6sw[ip6_protox[IPPROTO_IPV6]].pr_pfh); -# endif # endif # endif return (0); @@ -1637,22 +1396,19 @@ int ipf_pfil_hook(void) { void ipf_event_reg(void) { -#if (__FreeBSD_version >= 502103) - ipf_arrivetag = EVENTHANDLER_REGISTER(ifnet_arrival_event, \ - ipf_ifevent, NULL, \ + ipf_arrivetag = EVENTHANDLER_REGISTER(ifnet_arrival_event, \ + ipf_ifevent, &ipfmain, \ EVENTHANDLER_PRI_ANY); - ipf_departtag = EVENTHANDLER_REGISTER(ifnet_departure_event, \ - ipf_ifevent, NULL, \ + ipf_departtag = EVENTHANDLER_REGISTER(ifnet_departure_event, \ + ipf_ifevent, &ipfmain, \ EVENTHANDLER_PRI_ANY); - ipf_clonetag = EVENTHANDLER_REGISTER(if_clone_event, ipf_ifevent, \ - NULL, EVENTHANDLER_PRI_ANY); -#endif + ipf_clonetag = EVENTHANDLER_REGISTER(if_clone_event, ipf_ifevent, \ + &ipfmain, EVENTHANDLER_PRI_ANY); } void ipf_event_dereg(void) { -#if (__FreeBSD_version >= 502103) if (ipf_arrivetag != NULL) { EVENTHANDLER_DEREGISTER(ifnet_arrival_event, ipf_arrivetag); } @@ -1662,5 +1418,40 @@ ipf_event_dereg(void) if (ipf_clonetag != NULL) { EVENTHANDLER_DEREGISTER(if_clone_event, ipf_clonetag); } -#endif +} + + +u_32_t +ipf_random() +{ + return arc4random(); +} + + +u_int +ipf_pcksum(fin, hlen, sum) + fr_info_t *fin; + int hlen; + u_int sum; +{ + struct mbuf *m; + u_int sum2; + int off; + + m = fin->fin_m; + off = (char *)fin->fin_dp - (char *)fin->fin_ip; + m->m_data += hlen; + m->m_len -= hlen; + sum2 = in_cksum(fin->fin_m, fin->fin_plen - off); + m->m_len += hlen; + m->m_data -= hlen; + + /* + * Both sum and sum2 are partial sums, so combine them together. + */ + sum += ~sum2 & 0xffff; + while (sum > 0xffff) + sum = (sum & 0xffff) + (sum >> 16); + sum2 = ~sum & 0xffff; + return sum2; } diff --git a/sys/contrib/ipfilter/netinet/ip_frag.c b/sys/contrib/ipfilter/netinet/ip_frag.c index fb21bd1d484..87e5b7bff4f 100644 --- a/sys/contrib/ipfilter/netinet/ip_frag.c +++ b/sys/contrib/ipfilter/netinet/ip_frag.c @@ -1,7 +1,7 @@ /* $FreeBSD$ */ /* - * Copyright (C) 1993-2003 by Darren Reed. + * Copyright (C) 2012 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. */ @@ -30,7 +30,8 @@ struct file; # include # undef _KERNEL #endif -#if defined(_KERNEL) && (__FreeBSD_version >= 220000) +#if defined(_KERNEL) && \ + defined(__FreeBSD_version) && (__FreeBSD_version >= 220000) # include # include #else @@ -62,7 +63,6 @@ struct file; #ifdef sun # include #endif -#include #include #include #include @@ -79,25 +79,9 @@ struct file; #include "netinet/ip_frag.h" #include "netinet/ip_state.h" #include "netinet/ip_auth.h" +#include "netinet/ip_lookup.h" #include "netinet/ip_proxy.h" -#if (__FreeBSD_version >= 300000) -# include -# if defined(_KERNEL) -# ifndef IPFILTER_LKM -# include -# include -# endif -extern struct callout_handle fr_slowtimer_ch; -# endif -#endif -#if defined(__NetBSD__) && (__NetBSD_Version__ >= 104230000) -# include -extern struct callout fr_slowtimer_ch; -#endif -#if defined(__OpenBSD__) -# include -extern struct timeout fr_slowtimer_ch; -#endif +#include "netinet/ip_sync.h" /* END OF INCLUDES */ #if !defined(lint) @@ -107,180 +91,398 @@ static const char rcsid[] = "@(#)$FreeBSD$"; #endif -ipfr_t *ipfr_list = NULL; -ipfr_t **ipfr_tail = &ipfr_list; - -ipfr_t *ipfr_natlist = NULL; -ipfr_t **ipfr_nattail = &ipfr_natlist; - -ipfr_t *ipfr_ipidlist = NULL; -ipfr_t **ipfr_ipidtail = &ipfr_ipidlist; - -static ipfr_t **ipfr_heads; -static ipfr_t **ipfr_nattab; -static ipfr_t **ipfr_ipidtab; - -static ipfrstat_t ipfr_stats; -static int ipfr_inuse = 0; -int ipfr_size = IPFT_SIZE; - -int fr_ipfrttl = 120; /* 60 seconds */ -int fr_frag_lock = 0; -int fr_frag_init = 0; -u_long fr_ticks = 0; +typedef struct ipf_frag_softc_s { + ipfrwlock_t ipfr_ipidfrag; + ipfrwlock_t ipfr_frag; + ipfrwlock_t ipfr_natfrag; + int ipfr_size; + int ipfr_ttl; + int ipfr_lock; + int ipfr_inited; + ipfr_t *ipfr_list; + ipfr_t **ipfr_tail; + ipfr_t *ipfr_natlist; + ipfr_t **ipfr_nattail; + ipfr_t *ipfr_ipidlist; + ipfr_t **ipfr_ipidtail; + ipfr_t **ipfr_heads; + ipfr_t **ipfr_nattab; + ipfr_t **ipfr_ipidtab; + ipfrstat_t ipfr_stats; +} ipf_frag_softc_t; -static ipfr_t *ipfr_newfrag __P((fr_info_t *, u_32_t, ipfr_t **)); -static ipfr_t *fr_fraglookup __P((fr_info_t *, ipfr_t **)); -static void fr_fragdelete __P((ipfr_t *, ipfr_t ***)); -static void fr_fragfree __P((ipfr_t *)); +#ifdef USE_MUTEXES +static ipfr_t *ipfr_frag_new __P((ipf_main_softc_t *, ipf_frag_softc_t *, + fr_info_t *, u_32_t, ipfr_t **, + ipfrwlock_t *)); +static ipfr_t *ipf_frag_lookup __P((ipf_main_softc_t *, ipf_frag_softc_t *, fr_info_t *, ipfr_t **, ipfrwlock_t *)); +static void ipf_frag_deref __P((void *, ipfr_t **, ipfrwlock_t *)); +static int ipf_frag_next __P((ipf_main_softc_t *, ipftoken_t *, ipfgeniter_t *, + ipfr_t **, ipfrwlock_t *)); +#else +static ipfr_t *ipfr_frag_new __P((ipf_main_softc_t *, ipf_frag_softc_t *, + fr_info_t *, u_32_t, ipfr_t **)); +static ipfr_t *ipf_frag_lookup __P((ipf_main_softc_t *, ipf_frag_softc_t *, fr_info_t *, ipfr_t **)); +static void ipf_frag_deref __P((void *, ipfr_t **)); +static int ipf_frag_next __P((ipf_main_softc_t *, ipftoken_t *, ipfgeniter_t *, + ipfr_t **)); +#endif +static void ipf_frag_delete __P((ipf_main_softc_t *, ipfr_t *, ipfr_t ***)); +static void ipf_frag_free __P((ipf_frag_softc_t *, ipfr_t *)); + +static frentry_t ipfr_block; + +ipftuneable_t ipf_tuneables[] = { + { { (void *)offsetof(ipf_frag_softc_t, ipfr_size) }, + "frag_size", 1, 0x7fffffff, + stsizeof(ipf_frag_softc_t, ipfr_size), + IPFT_WRDISABLED, NULL, NULL }, + { { (void *)offsetof(ipf_frag_softc_t, ipfr_ttl) }, + "frag_ttl", 1, 0x7fffffff, + stsizeof(ipf_frag_softc_t, ipfr_ttl), + 0, NULL, NULL }, + { { NULL }, + NULL, 0, 0, + 0, + 0, NULL, NULL } +}; + +#define FBUMP(x) softf->ipfr_stats.x++ +#define FBUMPD(x) do { softf->ipfr_stats.x++; DT(x); } while (0) /* ------------------------------------------------------------------------ */ -/* Function: fr_fraginit */ +/* Function: ipf_frag_main_load */ /* Returns: int - 0 == success, -1 == error */ /* Parameters: Nil */ /* */ -/* Initialise the hash tables for the fragment cache lookups. */ +/* Initialise the filter rule associted with blocked packets - everyone can */ +/* use it. */ /* ------------------------------------------------------------------------ */ -int fr_fraginit() +int +ipf_frag_main_load() { - KMALLOCS(ipfr_heads, ipfr_t **, ipfr_size * sizeof(ipfr_t *)); - if (ipfr_heads == NULL) - return -1; - bzero((char *)ipfr_heads, ipfr_size * sizeof(ipfr_t *)); - - KMALLOCS(ipfr_nattab, ipfr_t **, ipfr_size * sizeof(ipfr_t *)); - if (ipfr_nattab == NULL) - return -1; - bzero((char *)ipfr_nattab, ipfr_size * sizeof(ipfr_t *)); - - KMALLOCS(ipfr_ipidtab, ipfr_t **, ipfr_size * sizeof(ipfr_t *)); - if (ipfr_ipidtab == NULL) - return -1; - bzero((char *)ipfr_ipidtab, ipfr_size * sizeof(ipfr_t *)); - - RWLOCK_INIT(&ipf_frag, "ipf fragment rwlock"); - fr_frag_init = 1; + bzero((char *)&ipfr_block, sizeof(ipfr_block)); + ipfr_block.fr_flags = FR_BLOCK|FR_QUICK; + ipfr_block.fr_ref = 1; return 0; } /* ------------------------------------------------------------------------ */ -/* Function: fr_fragunload */ -/* Returns: Nil */ +/* Function: ipf_frag_main_unload */ +/* Returns: int - 0 == success, -1 == error */ /* Parameters: Nil */ /* */ +/* A null-op function that exists as a placeholder so that the flow in */ +/* other functions is obvious. */ +/* ------------------------------------------------------------------------ */ +int +ipf_frag_main_unload() +{ + return 0; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_frag_soft_create */ +/* Returns: void * - NULL = failure, else pointer to local context */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* */ +/* Allocate a new soft context structure to track fragment related info. */ +/* ------------------------------------------------------------------------ */ +/*ARGSUSED*/ +void * +ipf_frag_soft_create(softc) + ipf_main_softc_t *softc; +{ + ipf_frag_softc_t *softf; + + KMALLOC(softf, ipf_frag_softc_t *); + if (softf == NULL) + return NULL; + + bzero((char *)softf, sizeof(*softf)); + + RWLOCK_INIT(&softf->ipfr_ipidfrag, "frag ipid lock"); + RWLOCK_INIT(&softf->ipfr_frag, "ipf fragment rwlock"); + RWLOCK_INIT(&softf->ipfr_natfrag, "ipf NAT fragment rwlock"); + + softf->ipfr_size = IPFT_SIZE; + softf->ipfr_ttl = IPF_TTLVAL(60); + softf->ipfr_lock = 1; + softf->ipfr_tail = &softf->ipfr_list; + softf->ipfr_nattail = &softf->ipfr_natlist; + softf->ipfr_ipidtail = &softf->ipfr_ipidlist; + + return softf; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_frag_soft_destroy */ +/* Returns: Nil */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* arg(I) - pointer to local context to use */ +/* */ +/* Initialise the hash tables for the fragment cache lookups. */ +/* ------------------------------------------------------------------------ */ +void +ipf_frag_soft_destroy(softc, arg) + ipf_main_softc_t *softc; + void *arg; +{ + ipf_frag_softc_t *softf = arg; + + RW_DESTROY(&softf->ipfr_ipidfrag); + RW_DESTROY(&softf->ipfr_frag); + RW_DESTROY(&softf->ipfr_natfrag); + + KFREE(softf); +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_frag_soft_init */ +/* Returns: int - 0 == success, -1 == error */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* arg(I) - pointer to local context to use */ +/* */ +/* Initialise the hash tables for the fragment cache lookups. */ +/* ------------------------------------------------------------------------ */ +/*ARGSUSED*/ +int +ipf_frag_soft_init(softc, arg) + ipf_main_softc_t *softc; + void *arg; +{ + ipf_frag_softc_t *softf = arg; + + KMALLOCS(softf->ipfr_heads, ipfr_t **, + softf->ipfr_size * sizeof(ipfr_t *)); + if (softf->ipfr_heads == NULL) + return -1; + + bzero((char *)softf->ipfr_heads, softf->ipfr_size * sizeof(ipfr_t *)); + + KMALLOCS(softf->ipfr_nattab, ipfr_t **, + softf->ipfr_size * sizeof(ipfr_t *)); + if (softf->ipfr_nattab == NULL) + return -2; + + bzero((char *)softf->ipfr_nattab, softf->ipfr_size * sizeof(ipfr_t *)); + + KMALLOCS(softf->ipfr_ipidtab, ipfr_t **, + softf->ipfr_size * sizeof(ipfr_t *)); + if (softf->ipfr_ipidtab == NULL) + return -3; + + bzero((char *)softf->ipfr_ipidtab, + softf->ipfr_size * sizeof(ipfr_t *)); + + softf->ipfr_lock = 0; + softf->ipfr_inited = 1; + + return 0; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_frag_soft_fini */ +/* Returns: int - 0 == success, -1 == error */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* arg(I) - pointer to local context to use */ +/* */ /* Free all memory allocated whilst running and from initialisation. */ /* ------------------------------------------------------------------------ */ -void fr_fragunload() +int +ipf_frag_soft_fini(softc, arg) + ipf_main_softc_t *softc; + void *arg; { - if (fr_frag_init == 1) { - fr_fragclear(); + ipf_frag_softc_t *softf = arg; - RW_DESTROY(&ipf_frag); - fr_frag_init = 0; + softf->ipfr_lock = 1; + + if (softf->ipfr_inited == 1) { + ipf_frag_clear(softc); + + softf->ipfr_inited = 0; } - if (ipfr_heads != NULL) - KFREES(ipfr_heads, ipfr_size * sizeof(ipfr_t *)); - ipfr_heads = NULL; + if (softf->ipfr_heads != NULL) + KFREES(softf->ipfr_heads, + softf->ipfr_size * sizeof(ipfr_t *)); + softf->ipfr_heads = NULL; - if (ipfr_nattab != NULL) - KFREES(ipfr_nattab, ipfr_size * sizeof(ipfr_t *)); - ipfr_nattab = NULL; + if (softf->ipfr_nattab != NULL) + KFREES(softf->ipfr_nattab, + softf->ipfr_size * sizeof(ipfr_t *)); + softf->ipfr_nattab = NULL; - if (ipfr_ipidtab != NULL) - KFREES(ipfr_ipidtab, ipfr_size * sizeof(ipfr_t *)); - ipfr_ipidtab = NULL; + if (softf->ipfr_ipidtab != NULL) + KFREES(softf->ipfr_ipidtab, + softf->ipfr_size * sizeof(ipfr_t *)); + softf->ipfr_ipidtab = NULL; + + return 0; } /* ------------------------------------------------------------------------ */ -/* Function: fr_fragstats */ +/* Function: ipf_frag_set_lock */ +/* Returns: Nil */ +/* Parameters: arg(I) - pointer to local context to use */ +/* tmp(I) - new value for lock */ +/* */ +/* Stub function that allows for external manipulation of ipfr_lock */ +/* ------------------------------------------------------------------------ */ +void +ipf_frag_setlock(arg, tmp) + void *arg; + int tmp; +{ + ipf_frag_softc_t *softf = arg; + + softf->ipfr_lock = tmp; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_frag_stats */ /* Returns: ipfrstat_t* - pointer to struct with current frag stats */ -/* Parameters: Nil */ +/* Parameters: arg(I) - pointer to local context to use */ /* */ /* Updates ipfr_stats with current information and returns a pointer to it */ /* ------------------------------------------------------------------------ */ -ipfrstat_t *fr_fragstats() +ipfrstat_t * +ipf_frag_stats(arg) + void *arg; { - ipfr_stats.ifs_table = ipfr_heads; - ipfr_stats.ifs_nattab = ipfr_nattab; - ipfr_stats.ifs_inuse = ipfr_inuse; - return &ipfr_stats; + ipf_frag_softc_t *softf = arg; + + softf->ipfr_stats.ifs_table = softf->ipfr_heads; + softf->ipfr_stats.ifs_nattab = softf->ipfr_nattab; + return &softf->ipfr_stats; } /* ------------------------------------------------------------------------ */ -/* Function: ipfr_newfrag */ +/* Function: ipfr_frag_new */ /* Returns: ipfr_t * - pointer to fragment cache state info or NULL */ /* Parameters: fin(I) - pointer to packet information */ /* table(I) - pointer to frag table to add to */ +/* lock(I) - pointer to lock to get a write hold of */ /* */ /* Add a new entry to the fragment cache, registering it as having come */ /* through this box, with the result of the filter operation. */ +/* */ +/* If this function succeeds, it returns with a write lock held on "lock". */ +/* If it fails, no lock is held on return. */ /* ------------------------------------------------------------------------ */ -static ipfr_t *ipfr_newfrag(fin, pass, table) -fr_info_t *fin; -u_32_t pass; -ipfr_t *table[]; +static ipfr_t * +ipfr_frag_new(softc, softf, fin, pass, table +#ifdef USE_MUTEXES +, lock +#endif +) + ipf_main_softc_t *softc; + ipf_frag_softc_t *softf; + fr_info_t *fin; + u_32_t pass; + ipfr_t *table[]; +#ifdef USE_MUTEXES + ipfrwlock_t *lock; +#endif { - ipfr_t *fra, frag; + ipfr_t *fra, frag, *fran; u_int idx, off; frentry_t *fr; - ip_t *ip; - if (ipfr_inuse >= IPFT_SIZE) + if (softf->ipfr_stats.ifs_inuse >= softf->ipfr_size) { + FBUMPD(ifs_maximum); return NULL; + } - if ((fin->fin_flx & (FI_FRAG|FI_BAD)) != FI_FRAG) + if ((fin->fin_flx & (FI_FRAG|FI_BAD)) != FI_FRAG) { + FBUMPD(ifs_newbad); return NULL; + } - ip = fin->fin_ip; - - if (pass & FR_FRSTRICT) - if (fin->fin_off != 0) + if (pass & FR_FRSTRICT) { + if (fin->fin_off != 0) { + FBUMPD(ifs_newrestrictnot0); return NULL; + } + } - frag.ipfr_p = ip->ip_p; - idx = ip->ip_p; - frag.ipfr_id = ip->ip_id; - idx += ip->ip_id; - frag.ipfr_tos = ip->ip_tos; - frag.ipfr_src.s_addr = ip->ip_src.s_addr; - idx += ip->ip_src.s_addr; - frag.ipfr_dst.s_addr = ip->ip_dst.s_addr; - idx += ip->ip_dst.s_addr; + frag.ipfr_v = fin->fin_v; + idx = fin->fin_v; + frag.ipfr_p = fin->fin_p; + idx += fin->fin_p; + frag.ipfr_id = fin->fin_id; + idx += fin->fin_id; + frag.ipfr_source = fin->fin_fi.fi_src; + idx += frag.ipfr_src.s_addr; + frag.ipfr_dest = fin->fin_fi.fi_dst; + idx += frag.ipfr_dst.s_addr; frag.ipfr_ifp = fin->fin_ifp; idx *= 127; - idx %= IPFT_SIZE; + idx %= softf->ipfr_size; frag.ipfr_optmsk = fin->fin_fi.fi_optmsk & IPF_OPTCOPY; frag.ipfr_secmsk = fin->fin_fi.fi_secmsk; frag.ipfr_auth = fin->fin_fi.fi_auth; + off = fin->fin_off >> 3; + if (off == 0) { + char *ptr; + int end; + +#ifdef USE_INET6 + if (fin->fin_v == 6) { + + ptr = (char *)fin->fin_fraghdr + + sizeof(struct ip6_frag); + } else +#endif + { + ptr = fin->fin_dp; + } + end = fin->fin_plen - (ptr - (char *)fin->fin_ip); + frag.ipfr_firstend = end >> 3; + } else { + frag.ipfr_firstend = 0; + } + + /* + * allocate some memory, if possible, if not, just record that we + * failed to do so. + */ + KMALLOC(fran, ipfr_t *); + if (fran == NULL) { + FBUMPD(ifs_nomem); + return NULL; + } + + WRITE_ENTER(lock); + /* * first, make sure it isn't already there... */ for (fra = table[idx]; (fra != NULL); fra = fra->ipfr_hnext) if (!bcmp((char *)&frag.ipfr_ifp, (char *)&fra->ipfr_ifp, IPFR_CMPSZ)) { - ipfr_stats.ifs_exists++; + RWLOCK_EXIT(lock); + FBUMPD(ifs_exists); + KFREE(fra); return NULL; } - /* - * allocate some memory, if possible, if not, just record that we - * failed to do so. - */ - KMALLOC(fra, ipfr_t *); - if (fra == NULL) { - ipfr_stats.ifs_nomem++; - return NULL; - } - + fra = fran; + fran = NULL; fr = fin->fin_fr; fra->ipfr_rule = fr; if (fr != NULL) { @@ -300,56 +502,63 @@ ipfr_t *table[]; fra->ipfr_data = NULL; table[idx] = fra; bcopy((char *)&frag.ipfr_ifp, (char *)&fra->ipfr_ifp, IPFR_CMPSZ); - fra->ipfr_ttl = fr_ticks + fr_ipfrttl; + fra->ipfr_v = fin->fin_v; + fra->ipfr_ttl = softc->ipf_ticks + softf->ipfr_ttl; + fra->ipfr_firstend = frag.ipfr_firstend; /* * Compute the offset of the expected start of the next packet. */ - off = ip->ip_off & IP_OFFMASK; if (off == 0) fra->ipfr_seen0 = 1; fra->ipfr_off = off + (fin->fin_dlen >> 3); fra->ipfr_pass = pass; fra->ipfr_ref = 1; - ipfr_stats.ifs_new++; - ipfr_inuse++; + fra->ipfr_pkts = 1; + fra->ipfr_bytes = fin->fin_plen; + FBUMP(ifs_inuse); + FBUMP(ifs_new); return fra; } /* ------------------------------------------------------------------------ */ -/* Function: fr_newfrag */ +/* Function: ipf_frag_new */ /* Returns: int - 0 == success, -1 == error */ /* Parameters: fin(I) - pointer to packet information */ /* */ /* Add a new entry to the fragment cache table based on the current packet */ /* ------------------------------------------------------------------------ */ -int fr_newfrag(fin, pass) -u_32_t pass; -fr_info_t *fin; +int +ipf_frag_new(softc, fin, pass) + ipf_main_softc_t *softc; + u_32_t pass; + fr_info_t *fin; { + ipf_frag_softc_t *softf = softc->ipf_frag_soft; ipfr_t *fra; - if ((fin->fin_v != 4) || (fr_frag_lock != 0)) + if (softf->ipfr_lock != 0) return -1; - WRITE_ENTER(&ipf_frag); - fra = ipfr_newfrag(fin, pass, ipfr_heads); +#ifdef USE_MUTEXES + fra = ipfr_frag_new(softc, softf, fin, pass, softf->ipfr_heads, &softc->ipf_frag); +#else + fra = ipfr_frag_new(softc, softf, fin, pass, softf->ipfr_heads); +#endif if (fra != NULL) { - *ipfr_tail = fra; - fra->ipfr_prev = ipfr_tail; - ipfr_tail = &fra->ipfr_next; - if (ipfr_list == NULL) - ipfr_list = fra; + *softf->ipfr_tail = fra; + fra->ipfr_prev = softf->ipfr_tail; + softf->ipfr_tail = &fra->ipfr_next; fra->ipfr_next = NULL; + RWLOCK_EXIT(&softc->ipf_frag); } - RWLOCK_EXIT(&ipf_frag); return fra ? 0 : -1; } /* ------------------------------------------------------------------------ */ -/* Function: fr_nat_newfrag */ +/* Function: ipf_frag_natnew */ /* Returns: int - 0 == success, -1 == error */ /* Parameters: fin(I) - pointer to packet information */ /* nat(I) - pointer to NAT structure */ @@ -357,33 +566,41 @@ fr_info_t *fin; /* Create a new NAT fragment cache entry based on the current packet and */ /* the NAT structure for this "session". */ /* ------------------------------------------------------------------------ */ -int fr_nat_newfrag(fin, pass, nat) -fr_info_t *fin; -u_32_t pass; -nat_t *nat; +int +ipf_frag_natnew(softc, fin, pass, nat) + ipf_main_softc_t *softc; + fr_info_t *fin; + u_32_t pass; + nat_t *nat; { + ipf_frag_softc_t *softf = softc->ipf_frag_soft; ipfr_t *fra; - if ((fin->fin_v != 4) || (fr_frag_lock != 0)) + if (softf->ipfr_lock != 0) return 0; - WRITE_ENTER(&ipf_natfrag); - fra = ipfr_newfrag(fin, pass, ipfr_nattab); +#ifdef USE_MUTEXES + fra = ipfr_frag_new(softc, softf, fin, pass, softf->ipfr_nattab, + &softf->ipfr_natfrag); +#else + fra = ipfr_frag_new(softc, softf, fin, pass, softf->ipfr_nattab); +#endif if (fra != NULL) { fra->ipfr_data = nat; nat->nat_data = fra; - *ipfr_nattail = fra; - fra->ipfr_prev = ipfr_nattail; - ipfr_nattail = &fra->ipfr_next; + *softf->ipfr_nattail = fra; + fra->ipfr_prev = softf->ipfr_nattail; + softf->ipfr_nattail = &fra->ipfr_next; fra->ipfr_next = NULL; + RWLOCK_EXIT(&softf->ipfr_natfrag); + return 0; } - RWLOCK_EXIT(&ipf_natfrag); - return fra ? 0 : -1; + return -1; } /* ------------------------------------------------------------------------ */ -/* Function: fr_ipid_newfrag */ +/* Function: ipf_frag_ipidnew */ /* Returns: int - 0 == success, -1 == error */ /* Parameters: fin(I) - pointer to packet information */ /* ipid(I) - new IP ID for this fragmented packet */ @@ -391,31 +608,37 @@ nat_t *nat; /* Create a new fragment cache entry for this packet and store, as a data */ /* pointer, the new IP ID value. */ /* ------------------------------------------------------------------------ */ -int fr_ipid_newfrag(fin, ipid) -fr_info_t *fin; -u_32_t ipid; +int +ipf_frag_ipidnew(fin, ipid) + fr_info_t *fin; + u_32_t ipid; { + ipf_main_softc_t *softc = fin->fin_main_soft; + ipf_frag_softc_t *softf = softc->ipf_frag_soft; ipfr_t *fra; - if ((fin->fin_v != 4) || (fr_frag_lock)) + if (softf->ipfr_lock) return 0; - WRITE_ENTER(&ipf_ipidfrag); - fra = ipfr_newfrag(fin, 0, ipfr_ipidtab); +#ifdef USE_MUTEXES + fra = ipfr_frag_new(softc, softf, fin, 0, softf->ipfr_ipidtab, &softf->ipfr_ipidfrag); +#else + fra = ipfr_frag_new(softc, softf, fin, 0, softf->ipfr_ipidtab); +#endif if (fra != NULL) { - fra->ipfr_data = (void *)(uintptr_t)ipid; - *ipfr_ipidtail = fra; - fra->ipfr_prev = ipfr_ipidtail; - ipfr_ipidtail = &fra->ipfr_next; + fra->ipfr_data = (void *)(intptr_t)ipid; + *softf->ipfr_ipidtail = fra; + fra->ipfr_prev = softf->ipfr_ipidtail; + softf->ipfr_ipidtail = &fra->ipfr_next; fra->ipfr_next = NULL; + RWLOCK_EXIT(&softf->ipfr_ipidfrag); } - RWLOCK_EXIT(&ipf_ipidfrag); return fra ? 0 : -1; } /* ------------------------------------------------------------------------ */ -/* Function: fr_fraglookup */ +/* Function: ipf_frag_lookup */ /* Returns: ipfr_t * - pointer to ipfr_t structure if there's a */ /* matching entry in the frag table, else NULL */ /* Parameters: fin(I) - pointer to packet information */ @@ -423,17 +646,44 @@ u_32_t ipid; /* */ /* Check the fragment cache to see if there is already a record of this */ /* packet with its filter result known. */ +/* */ +/* If this function succeeds, it returns with a write lock held on "lock". */ +/* If it fails, no lock is held on return. */ /* ------------------------------------------------------------------------ */ -static ipfr_t *fr_fraglookup(fin, table) -fr_info_t *fin; -ipfr_t *table[]; +static ipfr_t * +ipf_frag_lookup(softc, softf, fin, table +#ifdef USE_MUTEXES +, lock +#endif +) + ipf_main_softc_t *softc; + ipf_frag_softc_t *softf; + fr_info_t *fin; + ipfr_t *table[]; +#ifdef USE_MUTEXES + ipfrwlock_t *lock; +#endif { ipfr_t *f, frag; u_int idx; - ip_t *ip; - if ((fin->fin_flx & (FI_FRAG|FI_BAD)) != FI_FRAG) + /* + * We don't want to let short packets match because they could be + * compromising the security of other rules that want to match on + * layer 4 fields (and can't because they have been fragmented off.) + * Why do this check here? The counter acts as an indicator of this + * kind of attack, whereas if it was elsewhere, it wouldn't know if + * other matching packets had been seen. + */ + if (fin->fin_flx & FI_SHORT) { + FBUMPD(ifs_short); return NULL; + } + + if ((fin->fin_flx & FI_BAD) != 0) { + FBUMPD(ifs_bad); + return NULL; + } /* * For fragments, we record protocol, packet id, TOS and both IP#'s @@ -441,59 +691,58 @@ ipfr_t *table[]; * * build up a hash value to index the table with. */ - ip = fin->fin_ip; - frag.ipfr_p = ip->ip_p; - idx = ip->ip_p; - frag.ipfr_id = ip->ip_id; - idx += ip->ip_id; - frag.ipfr_tos = ip->ip_tos; - frag.ipfr_src.s_addr = ip->ip_src.s_addr; - idx += ip->ip_src.s_addr; - frag.ipfr_dst.s_addr = ip->ip_dst.s_addr; - idx += ip->ip_dst.s_addr; + frag.ipfr_v = fin->fin_v; + idx = fin->fin_v; + frag.ipfr_p = fin->fin_p; + idx += fin->fin_p; + frag.ipfr_id = fin->fin_id; + idx += fin->fin_id; + frag.ipfr_source = fin->fin_fi.fi_src; + idx += frag.ipfr_src.s_addr; + frag.ipfr_dest = fin->fin_fi.fi_dst; + idx += frag.ipfr_dst.s_addr; frag.ipfr_ifp = fin->fin_ifp; idx *= 127; - idx %= IPFT_SIZE; + idx %= softf->ipfr_size; frag.ipfr_optmsk = fin->fin_fi.fi_optmsk & IPF_OPTCOPY; frag.ipfr_secmsk = fin->fin_fi.fi_secmsk; frag.ipfr_auth = fin->fin_fi.fi_auth; + READ_ENTER(lock); + /* * check the table, careful to only compare the right amount of data */ - for (f = table[idx]; f; f = f->ipfr_hnext) + for (f = table[idx]; f; f = f->ipfr_hnext) { if (!bcmp((char *)&frag.ipfr_ifp, (char *)&f->ipfr_ifp, IPFR_CMPSZ)) { u_short off; - /* - * We don't want to let short packets match because - * they could be compromising the security of other - * rules that want to match on layer 4 fields (and - * can't because they have been fragmented off.) - * Why do this check here? The counter acts as an - * indicator of this kind of attack, whereas if it was - * elsewhere, it wouldn't know if other matching - * packets had been seen. - */ - if (fin->fin_flx & FI_SHORT) { - ATOMIC_INCL(ipfr_stats.ifs_short); - continue; - } - /* * XXX - We really need to be guarding against the * retransmission of (src,dst,id,offset-range) here * because a fragmented packet is never resent with * the same IP ID# (or shouldn't). */ - off = ip->ip_off & IP_OFFMASK; + off = fin->fin_off >> 3; if (f->ipfr_seen0) { if (off == 0) { - ATOMIC_INCL(ipfr_stats.ifs_retrans0); + FBUMPD(ifs_retrans0); continue; } + + /* + * Case 3. See comment for frpr_fragment6. + */ + if ((f->ipfr_firstend != 0) && + (off < f->ipfr_firstend)) { + FBUMP(ifs_overlap); + DT2(ifs_overlap, u_short, off, + ipfr_t *, f); + fin->fin_flx |= FI_BAD; + break; + } } else if (off == 0) f->ipfr_seen0 = 1; @@ -522,82 +771,124 @@ ipfr_t *table[]; * last (in order), shrink expiration time. */ if (off == f->ipfr_off) { - if (!(ip->ip_off & IP_MF)) - f->ipfr_ttl = fr_ticks + 1; f->ipfr_off = (fin->fin_dlen >> 3) + off; - } else if (f->ipfr_pass & FR_FRSTRICT) - continue; - ATOMIC_INCL(ipfr_stats.ifs_hits); + + /* + * Well, we could shrink the expiration time + * but only if every fragment has been seen + * in order upto this, the last. ipfr_badorder + * is used here to count those out of order + * and if it equals 0 when we get to the last + * fragment then we can assume all of the + * fragments have been seen and in order. + */ +#if 0 + /* + * Doing this properly requires moving it to + * the head of the list which is infesible. + */ + if ((more == 0) && (f->ipfr_badorder == 0)) + f->ipfr_ttl = softc->ipf_ticks + 1; +#endif + } else { + f->ipfr_badorder++; + FBUMPD(ifs_unordered); + if (f->ipfr_pass & FR_FRSTRICT) { + FBUMPD(ifs_strict); + continue; + } + } + f->ipfr_pkts++; + f->ipfr_bytes += fin->fin_plen; + FBUMP(ifs_hits); return f; } + } + + RWLOCK_EXIT(lock); + FBUMP(ifs_miss); return NULL; } /* ------------------------------------------------------------------------ */ -/* Function: fr_nat_knownfrag */ +/* Function: ipf_frag_natknown */ /* Returns: nat_t* - pointer to 'parent' NAT structure if frag table */ /* match found, else NULL */ /* Parameters: fin(I) - pointer to packet information */ /* */ /* Functional interface for NAT lookups of the NAT fragment cache */ /* ------------------------------------------------------------------------ */ -nat_t *fr_nat_knownfrag(fin) -fr_info_t *fin; +nat_t * +ipf_frag_natknown(fin) + fr_info_t *fin; { + ipf_main_softc_t *softc = fin->fin_main_soft; + ipf_frag_softc_t *softf = softc->ipf_frag_soft; nat_t *nat; ipfr_t *ipf; - if ((fin->fin_v != 4) || (fr_frag_lock) || !ipfr_natlist) + if ((softf->ipfr_lock) || !softf->ipfr_natlist) return NULL; - READ_ENTER(&ipf_natfrag); - ipf = fr_fraglookup(fin, ipfr_nattab); +#ifdef USE_MUTEXES + ipf = ipf_frag_lookup(softc, softf, fin, softf->ipfr_nattab, + &softf->ipfr_natfrag); +#else + ipf = ipf_frag_lookup(softc, softf, fin, softf->ipfr_nattab); +#endif if (ipf != NULL) { nat = ipf->ipfr_data; /* * This is the last fragment for this packet. */ - if ((ipf->ipfr_ttl == fr_ticks + 1) && (nat != NULL)) { + if ((ipf->ipfr_ttl == softc->ipf_ticks + 1) && (nat != NULL)) { nat->nat_data = NULL; ipf->ipfr_data = NULL; } + RWLOCK_EXIT(&softf->ipfr_natfrag); } else nat = NULL; - RWLOCK_EXIT(&ipf_natfrag); return nat; } /* ------------------------------------------------------------------------ */ -/* Function: fr_ipid_knownfrag */ +/* Function: ipf_frag_ipidknown */ /* Returns: u_32_t - IPv4 ID for this packet if match found, else */ /* return 0xfffffff to indicate no match. */ /* Parameters: fin(I) - pointer to packet information */ /* */ /* Functional interface for IP ID lookups of the IP ID fragment cache */ /* ------------------------------------------------------------------------ */ -u_32_t fr_ipid_knownfrag(fin) -fr_info_t *fin; +u_32_t +ipf_frag_ipidknown(fin) + fr_info_t *fin; { + ipf_main_softc_t *softc = fin->fin_main_soft; + ipf_frag_softc_t *softf = softc->ipf_frag_soft; ipfr_t *ipf; u_32_t id; - if ((fin->fin_v != 4) || (fr_frag_lock) || !ipfr_ipidlist) + if (softf->ipfr_lock || !softf->ipfr_ipidlist) return 0xffffffff; - READ_ENTER(&ipf_ipidfrag); - ipf = fr_fraglookup(fin, ipfr_ipidtab); - if (ipf != NULL) - id = (u_32_t)(uintptr_t)ipf->ipfr_data; - else +#ifdef USE_MUTEXES + ipf = ipf_frag_lookup(softc, softf, fin, softf->ipfr_ipidtab, + &softf->ipfr_ipidfrag); +#else + ipf = ipf_frag_lookup(softc, softf, fin, softf->ipfr_ipidtab); +#endif + if (ipf != NULL) { + id = (u_32_t)(intptr_t)ipf->ipfr_data; + RWLOCK_EXIT(&softf->ipfr_ipidfrag); + } else id = 0xffffffff; - RWLOCK_EXIT(&ipf_ipidfrag); return id; } /* ------------------------------------------------------------------------ */ -/* Function: fr_knownfrag */ +/* Function: ipf_frag_known */ /* Returns: frentry_t* - pointer to filter rule if a match is found in */ /* the frag cache table, else NULL. */ /* Parameters: fin(I) - pointer to packet information */ @@ -607,78 +898,82 @@ fr_info_t *fin; /* match is found, return the rule pointer and flags from the rule, except */ /* that if FR_LOGFIRST is set, reset FR_LOG. */ /* ------------------------------------------------------------------------ */ -frentry_t *fr_knownfrag(fin, passp) -fr_info_t *fin; -u_32_t *passp; +frentry_t * +ipf_frag_known(fin, passp) + fr_info_t *fin; + u_32_t *passp; { + ipf_main_softc_t *softc = fin->fin_main_soft; + ipf_frag_softc_t *softf = softc->ipf_frag_soft; frentry_t *fr = NULL; ipfr_t *fra; u_32_t pass; - if ((fin->fin_v != 4) || (fr_frag_lock) || (ipfr_list == NULL)) + if ((softf->ipfr_lock) || (softf->ipfr_list == NULL)) return NULL; - READ_ENTER(&ipf_frag); - fra = fr_fraglookup(fin, ipfr_heads); +#ifdef USE_MUTEXES + fra = ipf_frag_lookup(softc, softf, fin, softf->ipfr_heads, + &softc->ipf_frag); +#else + fra = ipf_frag_lookup(softc, softf, fin, softf->ipfr_heads); +#endif if (fra != NULL) { - fr = fra->ipfr_rule; + if (fin->fin_flx & FI_BAD) { + fr = &ipfr_block; + fin->fin_reason = FRB_BADFRAG; + } else { + fr = fra->ipfr_rule; + } fin->fin_fr = fr; if (fr != NULL) { pass = fr->fr_flags; + if ((pass & FR_KEEPSTATE) != 0) { + fin->fin_flx |= FI_STATE; + /* + * Reset the keep state flag here so that we + * don't try and add a new state entry because + * of a match here. That leads to blocking of + * the packet later because the add fails. + */ + pass &= ~FR_KEEPSTATE; + } if ((pass & FR_LOGFIRST) != 0) pass &= ~(FR_LOGFIRST|FR_LOG); *passp = pass; } + RWLOCK_EXIT(&softc->ipf_frag); } - RWLOCK_EXIT(&ipf_frag); return fr; } /* ------------------------------------------------------------------------ */ -/* Function: fr_forget */ -/* Returns: Nil */ -/* Parameters: ptr(I) - pointer to data structure */ -/* */ -/* Search through all of the fragment cache entries and wherever a pointer */ -/* is found to match ptr, reset it to NULL. */ -/* ------------------------------------------------------------------------ */ -void fr_forget(ptr) -void *ptr; -{ - ipfr_t *fr; - - WRITE_ENTER(&ipf_frag); - for (fr = ipfr_list; fr; fr = fr->ipfr_next) - if (fr->ipfr_data == ptr) - fr->ipfr_data = NULL; - RWLOCK_EXIT(&ipf_frag); -} - - -/* ------------------------------------------------------------------------ */ -/* Function: fr_forgetnat */ +/* Function: ipf_frag_natforget */ /* Returns: Nil */ /* Parameters: ptr(I) - pointer to data structure */ /* */ /* Search through all of the fragment cache entries for NAT and wherever a */ /* pointer is found to match ptr, reset it to NULL. */ /* ------------------------------------------------------------------------ */ -void fr_forgetnat(ptr) -void *ptr; +void +ipf_frag_natforget(softc, ptr) + ipf_main_softc_t *softc; + void *ptr; { + ipf_frag_softc_t *softf = softc->ipf_frag_soft; ipfr_t *fr; - WRITE_ENTER(&ipf_natfrag); - for (fr = ipfr_natlist; fr; fr = fr->ipfr_next) + WRITE_ENTER(&softf->ipfr_natfrag); + for (fr = softf->ipfr_natlist; fr; fr = fr->ipfr_next) if (fr->ipfr_data == ptr) fr->ipfr_data = NULL; - RWLOCK_EXIT(&ipf_natfrag); + RWLOCK_EXIT(&softf->ipfr_natfrag); } /* ------------------------------------------------------------------------ */ -/* Function: fr_fragdelete */ +/* Function: ipf_frag_delete */ /* Returns: Nil */ /* Parameters: fra(I) - pointer to fragment structure to delete */ /* tail(IO) - pointer to the pointer to the tail of the frag */ @@ -688,9 +983,12 @@ void *ptr; /* the filter rule it is associated with it if it is no longer used as a */ /* result of decreasing the reference count. */ /* ------------------------------------------------------------------------ */ -static void fr_fragdelete(fra, tail) -ipfr_t *fra, ***tail; +static void +ipf_frag_delete(softc, fra, tail) + ipf_main_softc_t *softc; + ipfr_t *fra, ***tail; { + ipf_frag_softc_t *softf = softc->ipf_frag_soft; if (fra->ipfr_next) fra->ipfr_next->ipfr_prev = fra->ipfr_prev; @@ -703,107 +1001,112 @@ ipfr_t *fra, ***tail; *fra->ipfr_hprev = fra->ipfr_hnext; if (fra->ipfr_rule != NULL) { - (void) fr_derefrule(&fra->ipfr_rule); + (void) ipf_derefrule(softc, &fra->ipfr_rule); } if (fra->ipfr_ref <= 0) - fr_fragfree(fra); + ipf_frag_free(softf, fra); } /* ------------------------------------------------------------------------ */ -/* Function: fr_fragfree */ +/* Function: ipf_frag_free */ /* Returns: Nil */ -/* Parameters: fra - pointer to frag structure to free */ /* */ -/* Take care of the details associated with deleting an entry from the frag */ -/* cache. Currently this just means bumping stats correctly after freeing */ /* ------------------------------------------------------------------------ */ -static void fr_fragfree(fra) -ipfr_t *fra; +static void +ipf_frag_free(softf, fra) + ipf_frag_softc_t *softf; + ipfr_t *fra; { KFREE(fra); - ipfr_stats.ifs_expire++; - ipfr_inuse--; + FBUMP(ifs_expire); + softf->ipfr_stats.ifs_inuse--; } /* ------------------------------------------------------------------------ */ -/* Function: fr_fragclear */ +/* Function: ipf_frag_clear */ /* Returns: Nil */ /* Parameters: Nil */ /* */ /* Free memory in use by fragment state information kept. Do the normal */ /* fragment state stuff first and then the NAT-fragment table. */ /* ------------------------------------------------------------------------ */ -void fr_fragclear() +void +ipf_frag_clear(softc) + ipf_main_softc_t *softc; { + ipf_frag_softc_t *softf = softc->ipf_frag_soft; ipfr_t *fra; nat_t *nat; - WRITE_ENTER(&ipf_frag); - while ((fra = ipfr_list) != NULL) { + WRITE_ENTER(&softc->ipf_frag); + while ((fra = softf->ipfr_list) != NULL) { fra->ipfr_ref--; - fr_fragdelete(fra, &ipfr_tail); + ipf_frag_delete(softc, fra, &softf->ipfr_tail); } - ipfr_tail = &ipfr_list; - RWLOCK_EXIT(&ipf_frag); + softf->ipfr_tail = &softf->ipfr_list; + RWLOCK_EXIT(&softc->ipf_frag); - WRITE_ENTER(&ipf_nat); - WRITE_ENTER(&ipf_natfrag); - while ((fra = ipfr_natlist) != NULL) { + WRITE_ENTER(&softc->ipf_nat); + WRITE_ENTER(&softf->ipfr_natfrag); + while ((fra = softf->ipfr_natlist) != NULL) { nat = fra->ipfr_data; if (nat != NULL) { if (nat->nat_data == fra) nat->nat_data = NULL; } fra->ipfr_ref--; - fr_fragdelete(fra, &ipfr_nattail); + ipf_frag_delete(softc, fra, &softf->ipfr_nattail); } - ipfr_nattail = &ipfr_natlist; - RWLOCK_EXIT(&ipf_natfrag); - RWLOCK_EXIT(&ipf_nat); + softf->ipfr_nattail = &softf->ipfr_natlist; + RWLOCK_EXIT(&softf->ipfr_natfrag); + RWLOCK_EXIT(&softc->ipf_nat); } /* ------------------------------------------------------------------------ */ -/* Function: fr_fragexpire */ +/* Function: ipf_frag_expire */ /* Returns: Nil */ /* Parameters: Nil */ /* */ /* Expire entries in the fragment cache table that have been there too long */ /* ------------------------------------------------------------------------ */ -void fr_fragexpire() +void +ipf_frag_expire(softc) + ipf_main_softc_t *softc; { + ipf_frag_softc_t *softf = softc->ipf_frag_soft; ipfr_t **fp, *fra; nat_t *nat; SPL_INT(s); - if (fr_frag_lock) + if (softf->ipfr_lock) return; SPL_NET(s); - WRITE_ENTER(&ipf_frag); + WRITE_ENTER(&softc->ipf_frag); /* * Go through the entire table, looking for entries to expire, - * which is indicated by the ttl being less than or equal to fr_ticks. + * which is indicated by the ttl being less than or equal to ipf_ticks. */ - for (fp = &ipfr_list; ((fra = *fp) != NULL); ) { - if (fra->ipfr_ttl > fr_ticks) + for (fp = &softf->ipfr_list; ((fra = *fp) != NULL); ) { + if (fra->ipfr_ttl > softc->ipf_ticks) break; fra->ipfr_ref--; - fr_fragdelete(fra, &ipfr_tail); + ipf_frag_delete(softc, fra, &softf->ipfr_tail); } - RWLOCK_EXIT(&ipf_frag); + RWLOCK_EXIT(&softc->ipf_frag); - WRITE_ENTER(&ipf_ipidfrag); - for (fp = &ipfr_ipidlist; ((fra = *fp) != NULL); ) { - if (fra->ipfr_ttl > fr_ticks) + WRITE_ENTER(&softf->ipfr_ipidfrag); + for (fp = &softf->ipfr_ipidlist; ((fra = *fp) != NULL); ) { + if (fra->ipfr_ttl > softc->ipf_ticks) break; fra->ipfr_ref--; - fr_fragdelete(fra, &ipfr_ipidtail); + ipf_frag_delete(softc, fra, &softf->ipfr_ipidtail); } - RWLOCK_EXIT(&ipf_ipidfrag); + RWLOCK_EXIT(&softf->ipfr_ipidfrag); /* * Same again for the NAT table, except that if the structure also @@ -815,11 +1118,11 @@ void fr_fragexpire() * operations - no need to do that if there are no entries in this * list, right? */ - if (ipfr_natlist != NULL) { - WRITE_ENTER(&ipf_nat); - WRITE_ENTER(&ipf_natfrag); - for (fp = &ipfr_natlist; ((fra = *fp) != NULL); ) { - if (fra->ipfr_ttl > fr_ticks) + if (softf->ipfr_natlist != NULL) { + WRITE_ENTER(&softc->ipf_nat); + WRITE_ENTER(&softf->ipfr_natfrag); + for (fp = &softf->ipfr_natlist; ((fra = *fp) != NULL); ) { + if (fra->ipfr_ttl > softc->ipf_ticks) break; nat = fra->ipfr_data; if (nat != NULL) { @@ -827,76 +1130,60 @@ void fr_fragexpire() nat->nat_data = NULL; } fra->ipfr_ref--; - fr_fragdelete(fra, &ipfr_nattail); + ipf_frag_delete(softc, fra, &softf->ipfr_nattail); } - RWLOCK_EXIT(&ipf_natfrag); - RWLOCK_EXIT(&ipf_nat); + RWLOCK_EXIT(&softf->ipfr_natfrag); + RWLOCK_EXIT(&softc->ipf_nat); } SPL_X(s); } /* ------------------------------------------------------------------------ */ -/* Function: fr_slowtimer */ -/* Returns: Nil */ -/* Parameters: Nil */ -/* */ -/* Slowly expire held state for fragments. Timeouts are set * in */ -/* expectation of this being called twice per second. */ +/* Function: ipf_frag_pkt_next */ /* ------------------------------------------------------------------------ */ -#if !defined(_KERNEL) || (!SOLARIS && !defined(__hpux) && !defined(__sgi) && \ - !defined(__osf__) && !defined(linux)) -# if defined(_KERNEL) && ((BSD >= 199103) || defined(__sgi)) -void fr_slowtimer __P((void *ptr)) -# else -int fr_slowtimer() -# endif +int +ipf_frag_pkt_next(softc, token, itp) + ipf_main_softc_t *softc; + ipftoken_t *token; + ipfgeniter_t *itp; { - READ_ENTER(&ipf_global); + ipf_frag_softc_t *softf = softc->ipf_frag_soft; - ipf_expiretokens(); - fr_fragexpire(); - fr_timeoutstate(); - fr_natexpire(); - fr_authexpire(); - fr_ticks++; - if (fr_running <= 0) - goto done; -# ifdef _KERNEL -# if defined(__NetBSD__) && (__NetBSD_Version__ >= 104240000) - callout_reset(&fr_slowtimer_ch, hz / 2, fr_slowtimer, NULL); -# else -# if defined(__OpenBSD__) - timeout_add(&fr_slowtimer_ch, hz/2); -# else -# if (__FreeBSD_version >= 300000) - fr_slowtimer_ch = timeout(fr_slowtimer, NULL, hz/2); -# else -# ifdef linux - ; -# else - timeout(fr_slowtimer, NULL, hz/2); -# endif -# endif /* FreeBSD */ -# endif /* OpenBSD */ -# endif /* NetBSD */ -# endif -done: - RWLOCK_EXIT(&ipf_global); -# if (BSD < 199103) || !defined(_KERNEL) - return 0; -# endif +#ifdef USE_MUTEXES + return ipf_frag_next(softc, token, itp, &softf->ipfr_list, + &softf->ipfr_frag); +#else + return ipf_frag_next(softc, token, itp, &softf->ipfr_list); +#endif } -#endif /* !SOLARIS && !defined(__hpux) && !defined(__sgi) */ /* ------------------------------------------------------------------------ */ -/* Function: fr_nextfrag */ +/* Function: ipf_frag_nat_next */ +/* ------------------------------------------------------------------------ */ +int +ipf_frag_nat_next(softc, token, itp) + ipf_main_softc_t *softc; + ipftoken_t *token; + ipfgeniter_t *itp; +{ + ipf_frag_softc_t *softf = softc->ipf_frag_soft;; + +#ifdef USE_MUTEXES + return ipf_frag_next(softc, token, itp, &softf->ipfr_natlist, + &softf->ipfr_natfrag); +#else + return ipf_frag_next(softc, token, itp, &softf->ipfr_natlist); +#endif +} + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_frag_next */ /* Returns: int - 0 == success, else error */ /* Parameters: token(I) - pointer to token information for this caller */ /* itp(I) - pointer to generic iterator from caller */ /* top(I) - top of the fragment list */ -/* tail(I) - tail of the fragment list */ /* lock(I) - fragment cache lock */ /* */ /* This function is used to interate through the list of entries in the */ @@ -904,30 +1191,39 @@ done: /* being returned so that the caller can come back and resume from it later.*/ /* */ /* This function is used for both the NAT fragment cache as well as the ipf */ -/* fragment cache - hence the reason for passing in top, tail and lock. */ +/* fragment cache - hence the reason for passing in top and lock. */ /* ------------------------------------------------------------------------ */ -int fr_nextfrag(token, itp, top, tail +static int +ipf_frag_next(softc, token, itp, top #ifdef USE_MUTEXES , lock #endif ) -ipftoken_t *token; -ipfgeniter_t *itp; -ipfr_t **top, ***tail; + ipf_main_softc_t *softc; + ipftoken_t *token; + ipfgeniter_t *itp; + ipfr_t **top; #ifdef USE_MUTEXES -ipfrwlock_t *lock; + ipfrwlock_t *lock; #endif { ipfr_t *frag, *next, zero; int error = 0; - frag = token->ipt_data; - if (frag == (ipfr_t *)-1) { - ipf_freetoken(token); - return ESRCH; + if (itp->igi_data == NULL) { + IPFERROR(20001); + return EFAULT; } + if (itp->igi_nitems != 1) { + IPFERROR(20003); + return EFAULT; + } + + frag = token->ipt_data; + READ_ENTER(lock); + if (frag == NULL) next = *top; else @@ -941,26 +1237,72 @@ ipfrwlock_t *lock; next = &zero; token->ipt_data = NULL; } - RWLOCK_EXIT(lock); + if (next->ipfr_next == NULL) + ipf_token_mark_complete(token); - if (frag != NULL) { -#ifdef USE_MUTEXES - fr_fragderef(&frag, lock); -#else - fr_fragderef(&frag); -#endif - } + RWLOCK_EXIT(lock); error = COPYOUT(next, itp->igi_data, sizeof(*next)); if (error != 0) - error = EFAULT; + IPFERROR(20002); - return error; + if (frag != NULL) { +#ifdef USE_MUTEXES + ipf_frag_deref(softc, &frag, lock); +#else + ipf_frag_deref(softc, &frag); +#endif + } + return error; } /* ------------------------------------------------------------------------ */ -/* Function: fr_fragderef */ +/* Function: ipf_frag_pkt_deref */ +/* Returns: Nil */ +/* */ +/* ------------------------------------------------------------------------ */ +void +ipf_frag_pkt_deref(softc, data) + ipf_main_softc_t *softc; + void *data; +{ + ipfr_t **frp = data; + +#ifdef USE_MUTEXES + ipf_frag_softc_t *softf = softc->ipf_frag_soft; + + ipf_frag_deref(softc->ipf_frag_soft, frp, &softf->ipfr_frag); +#else + ipf_frag_deref(softc->ipf_frag_soft, frp); +#endif +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_frag_nat_deref */ +/* Returns: Nil */ +/* */ +/* ------------------------------------------------------------------------ */ +void +ipf_frag_nat_deref(softc, data) + ipf_main_softc_t *softc; + void *data; +{ + ipfr_t **frp = data; + +#ifdef USE_MUTEXES + ipf_frag_softc_t *softf = softc->ipf_frag_soft; + + ipf_frag_deref(softc->ipf_frag_soft, frp, &softf->ipfr_natfrag); +#else + ipf_frag_deref(softc->ipf_frag_soft, frp); +#endif +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_frag_deref */ /* Returns: Nil */ /* Parameters: frp(IO) - pointer to fragment structure to deference */ /* lock(I) - lock associated with the fragment */ @@ -970,16 +1312,19 @@ ipfrwlock_t *lock; /* not freed, to enforce the notion that the caller is no longer entitled */ /* to use the pointer it is dropping the reference to. */ /* ------------------------------------------------------------------------ */ -void fr_fragderef(frp +static void +ipf_frag_deref(arg, frp #ifdef USE_MUTEXES , lock #endif ) -ipfr_t **frp; + void *arg; + ipfr_t **frp; #ifdef USE_MUTEXES -ipfrwlock_t *lock; + ipfrwlock_t *lock; #endif { + ipf_frag_softc_t *softf = arg; ipfr_t *fra; fra = *frp; @@ -988,6 +1333,6 @@ ipfrwlock_t *lock; WRITE_ENTER(lock); fra->ipfr_ref--; if (fra->ipfr_ref <= 0) - fr_fragfree(fra); + ipf_frag_free(softf, fra); RWLOCK_EXIT(lock); } diff --git a/sys/contrib/ipfilter/netinet/ip_frag.h b/sys/contrib/ipfilter/netinet/ip_frag.h index 227dbcd5553..6b0c1be221e 100644 --- a/sys/contrib/ipfilter/netinet/ip_frag.h +++ b/sys/contrib/ipfilter/netinet/ip_frag.h @@ -1,7 +1,7 @@ /* $FreeBSD$ */ /* - * Copyright (C) 1993-2001 by Darren Reed. + * Copyright (C) 2012 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. * @@ -21,26 +21,33 @@ typedef struct ipfr { void *ipfr_data; frentry_t *ipfr_rule; u_long ipfr_ttl; + u_int ipfr_pkts; + u_int ipfr_bytes; + u_int ipfr_badorder; int ipfr_ref; u_short ipfr_off; - u_short ipfr_seen0; + u_short ipfr_firstend; + u_char ipfr_p; + u_char ipfr_seen0; /* * All of the fields, from ipfr_ifp to ipfr_pass, are compared * using bcmp to see if an identical entry is present. It is * therefore important for this set to remain together. */ void *ipfr_ifp; - struct in_addr ipfr_src; - struct in_addr ipfr_dst; + i6addr_t ipfr_source; + i6addr_t ipfr_dest; u_32_t ipfr_optmsk; u_short ipfr_secmsk; u_short ipfr_auth; - u_short ipfr_id; - u_char ipfr_p; - u_char ipfr_tos; + u_32_t ipfr_id; u_32_t ipfr_pass; + int ipfr_v; } ipfr_t; +#define ipfr_src ipfr_source.in4 +#define ipfr_dst ipfr_dest.in4 + typedef struct ipfrstat { u_long ifs_exists; /* add & already exists */ @@ -51,6 +58,14 @@ typedef struct ipfrstat { u_long ifs_inuse; u_long ifs_retrans0; u_long ifs_short; + u_long ifs_bad; + u_long ifs_overlap; + u_long ifs_unordered; + u_long ifs_strict; + u_long ifs_miss; + u_long ifs_maximum; + u_long ifs_newbad; + u_long ifs_newrestrictnot0; struct ipfr **ifs_table; struct ipfr **ifs_nattab; } ipfrstat_t; @@ -58,51 +73,32 @@ typedef struct ipfrstat { #define IPFR_CMPSZ (offsetof(ipfr_t, ipfr_pass) - \ offsetof(ipfr_t, ipfr_ifp)) -extern ipfr_t *ipfr_list, **ipfr_tail; -extern ipfr_t *ipfr_natlist, **ipfr_nattail; -extern int ipfr_size; -extern int fr_ipfrttl; -extern int fr_frag_lock; -extern int fr_fraginit __P((void)); -extern void fr_fragunload __P((void)); -extern ipfrstat_t *fr_fragstats __P((void)); - -extern int fr_newfrag __P((fr_info_t *, u_32_t)); -extern frentry_t *fr_knownfrag __P((fr_info_t *, u_32_t *)); - -extern int fr_nat_newfrag __P((fr_info_t *, u_32_t, struct nat *)); -extern nat_t *fr_nat_knownfrag __P((fr_info_t *)); - -extern int fr_ipid_newfrag __P((fr_info_t *, u_32_t)); -extern u_32_t fr_ipid_knownfrag __P((fr_info_t *)); -#ifdef USE_MUTEXES -extern void fr_fragderef __P((ipfr_t **, ipfrwlock_t *)); -extern int fr_nextfrag __P((ipftoken_t *, ipfgeniter_t *, ipfr_t **, \ - ipfr_t ***, ipfrwlock_t *)); -#else -extern void fr_fragderef __P((ipfr_t **)); -extern int fr_nextfrag __P((ipftoken_t *, ipfgeniter_t *, ipfr_t **, \ - ipfr_t ***)); -#endif - -extern void fr_forget __P((void *)); -extern void fr_forgetnat __P((void *)); -extern void fr_fragclear __P((void)); -extern void fr_fragexpire __P((void)); - -#if defined(_KERNEL) && ((BSD >= 199306) || SOLARIS || defined(__sgi) \ - || defined(__osf__) || (defined(__sgi) && (IRIX >= 60500))) -# if defined(SOLARIS2) && (SOLARIS2 < 7) -extern void fr_slowtimer __P((void)); -# else -extern void fr_slowtimer __P((void *)); -# endif -#else -# if defined(linux) && defined(_KERNEL) -extern void fr_slowtimer __P((long)); -# else -extern int fr_slowtimer __P((void)); -# endif -#endif +extern void *ipf_frag_soft_create __P((ipf_main_softc_t *)); +extern int ipf_frag_soft_init __P((ipf_main_softc_t *, void *)); +extern int ipf_frag_soft_fini __P((ipf_main_softc_t *, void *)); +extern void ipf_frag_soft_destroy __P((ipf_main_softc_t *, void *)); +extern int ipf_frag_main_load __P((void)); +extern int ipf_frag_main_unload __P((void)); +extern int ipf_frag_load __P((void)); +extern void ipf_frag_clear __P((ipf_main_softc_t *)); +extern void ipf_frag_expire __P((ipf_main_softc_t *)); +extern void ipf_frag_forget __P((void *)); +extern int ipf_frag_init __P((void)); +extern u_32_t ipf_frag_ipidknown __P((fr_info_t *)); +extern int ipf_frag_ipidnew __P((fr_info_t *, u_32_t)); +extern frentry_t *ipf_frag_known __P((fr_info_t *, u_32_t *)); +extern void ipf_frag_natforget __P((ipf_main_softc_t *, void *)); +extern int ipf_frag_natnew __P((ipf_main_softc_t *, fr_info_t *, u_32_t, struct nat *)); +extern nat_t *ipf_frag_natknown __P((fr_info_t *)); +extern int ipf_frag_new __P((ipf_main_softc_t *, fr_info_t *, u_32_t)); +extern ipfrstat_t *ipf_frag_stats __P((void *)); +extern void ipf_frag_setlock __P((void *, int)); +extern void ipf_frag_pkt_deref __P((ipf_main_softc_t *, void *)); +extern int ipf_frag_pkt_next __P((ipf_main_softc_t *, ipftoken_t *, + ipfgeniter_t *)); +extern void ipf_frag_nat_deref __P((ipf_main_softc_t *, void *)); +extern int ipf_frag_nat_next __P((ipf_main_softc_t *, ipftoken_t *, + ipfgeniter_t *)); +extern void ipf_slowtimer __P((ipf_main_softc_t *)); #endif /* __IP_FRAG_H__ */ diff --git a/sys/contrib/ipfilter/netinet/ip_ftp_pxy.c b/sys/contrib/ipfilter/netinet/ip_ftp_pxy.c index f56a6904433..ff8397678de 100644 --- a/sys/contrib/ipfilter/netinet/ip_ftp_pxy.c +++ b/sys/contrib/ipfilter/netinet/ip_ftp_pxy.c @@ -1,7 +1,7 @@ /* $FreeBSD$ */ /* - * Copyright (C) 1997-2003 by Darren Reed + * Copyright (C) 2012 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. * @@ -15,6 +15,7 @@ #define IPF_FTP_PROXY #define IPF_MINPORTLEN 18 +#define IPF_MINEPRTLEN 20 #define IPF_MAXPORTLEN 30 #define IPF_MIN227LEN 39 #define IPF_MAX227LEN 51 @@ -38,93 +39,197 @@ #define FTPXY_PASS_2 14 #define FTPXY_PAOK_2 15 +#define FTPXY_JUNK_OK 0 +#define FTPXY_JUNK_BAD 1 /* Ignore all commands for this connection */ +#define FTPXY_JUNK_EOL 2 /* consume the rest of this line only */ +#define FTPXY_JUNK_CONT 3 /* Saerching for next numeric */ + /* * Values for FTP commands. Numerics cover 0-999 */ #define FTPXY_C_PASV 1000 - -int ippr_ftp_client __P((fr_info_t *, ip_t *, nat_t *, ftpinfo_t *, int)); -int ippr_ftp_complete __P((char *, size_t)); -int ippr_ftp_in __P((fr_info_t *, ap_session_t *, nat_t *)); -int ippr_ftp_init __P((void)); -void ippr_ftp_fini __P((void)); -int ippr_ftp_new __P((fr_info_t *, ap_session_t *, nat_t *)); -int ippr_ftp_out __P((fr_info_t *, ap_session_t *, nat_t *)); -int ippr_ftp_pasv __P((fr_info_t *, ip_t *, nat_t *, ftpinfo_t *, int)); -int ippr_ftp_epsv __P((fr_info_t *, ip_t *, nat_t *, ftpside_t *, int)); -int ippr_ftp_port __P((fr_info_t *, ip_t *, nat_t *, ftpside_t *, int)); -int ippr_ftp_process __P((fr_info_t *, nat_t *, ftpinfo_t *, int)); -int ippr_ftp_server __P((fr_info_t *, ip_t *, nat_t *, ftpinfo_t *, int)); -int ippr_ftp_valid __P((ftpinfo_t *, int, char *, size_t)); -int ippr_ftp_server_valid __P((ftpside_t *, char *, size_t)); -int ippr_ftp_client_valid __P((ftpside_t *, char *, size_t)); -u_short ippr_ftp_atoi __P((char **)); -int ippr_ftp_pasvreply __P((fr_info_t *, ip_t *, nat_t *, ftpside_t *, - u_int, char *, char *, u_int)); +#define FTPXY_C_PORT 1001 +#define FTPXY_C_EPSV 1002 +#define FTPXY_C_EPRT 1003 -int ftp_proxy_init = 0; -int ippr_ftp_pasvonly = 0; -int ippr_ftp_insecure = 0; /* Do not require logins before transfers */ -int ippr_ftp_pasvrdr = 0; -int ippr_ftp_forcepasv = 0; /* PASV must be last command prior to 227 */ -#if defined(_KERNEL) -int ippr_ftp_debug = 0; -#else -int ippr_ftp_debug = 2; -#endif +typedef struct ipf_ftp_softc_s { + int ipf_p_ftp_pasvonly; + /* Do not require logins before transfers */ + int ipf_p_ftp_insecure; + int ipf_p_ftp_pasvrdr; + /* PASV must be last command prior to 227 */ + int ipf_p_ftp_forcepasv; + int ipf_p_ftp_debug; + int ipf_p_ftp_single_xfer; + void *ipf_p_ftp_tune; +} ipf_ftp_softc_t; + + +void ipf_p_ftp_main_load __P((void)); +void ipf_p_ftp_main_unload __P((void)); +void *ipf_p_ftp_soft_create __P((ipf_main_softc_t *)); +void ipf_p_ftp_soft_destroy __P((ipf_main_softc_t *, void *)); + +int ipf_p_ftp_client __P((ipf_ftp_softc_t *, fr_info_t *, ip_t *, nat_t *, + ftpinfo_t *, int)); +int ipf_p_ftp_complete __P((char *, size_t)); +int ipf_p_ftp_in __P((void *, fr_info_t *, ap_session_t *, nat_t *)); +int ipf_p_ftp_new __P((void *, fr_info_t *, ap_session_t *, nat_t *)); +void ipf_p_ftp_del __P((ipf_main_softc_t *, ap_session_t *)); +int ipf_p_ftp_out __P((void *, fr_info_t *, ap_session_t *, nat_t *)); +int ipf_p_ftp_pasv __P((ipf_ftp_softc_t *, fr_info_t *, ip_t *, nat_t *, + ftpinfo_t *, int)); +int ipf_p_ftp_epsv __P((ipf_ftp_softc_t *, fr_info_t *, ip_t *, nat_t *, + ftpinfo_t *, int)); +int ipf_p_ftp_port __P((ipf_ftp_softc_t *, fr_info_t *, ip_t *, nat_t *, + ftpinfo_t *, int)); +int ipf_p_ftp_process __P((ipf_ftp_softc_t *, fr_info_t *, nat_t *, + ftpinfo_t *, int)); +int ipf_p_ftp_server __P((ipf_ftp_softc_t *, fr_info_t *, ip_t *, nat_t *, + ftpinfo_t *, int)); +int ipf_p_ftp_valid __P((ipf_ftp_softc_t *, ftpinfo_t *, int, char *, size_t)); +int ipf_p_ftp_server_valid __P((ipf_ftp_softc_t *, ftpside_t *, char *, + size_t)); +int ipf_p_ftp_client_valid __P((ipf_ftp_softc_t *, ftpside_t *, char *, + size_t)); +u_short ipf_p_ftp_atoi __P((char **)); +int ipf_p_ftp_pasvreply __P((ipf_ftp_softc_t *, fr_info_t *, ip_t *, nat_t *, + ftpinfo_t *, u_int, char *, char *)); +int ipf_p_ftp_eprt __P((ipf_ftp_softc_t *, fr_info_t *, ip_t *, nat_t *, + ftpinfo_t *, int)); +int ipf_p_ftp_eprt4 __P((ipf_ftp_softc_t *, fr_info_t *, ip_t *, nat_t *, + ftpinfo_t *, int)); +int ipf_p_ftp_eprt6 __P((ipf_ftp_softc_t *, fr_info_t *, ip_t *, nat_t *, + ftpinfo_t *, int)); +int ipf_p_ftp_addport __P((ipf_ftp_softc_t *, fr_info_t *, ip_t *, nat_t *, + ftpinfo_t *, int, int, int)); +void ipf_p_ftp_setpending __P((ipf_main_softc_t *, ftpinfo_t *)); + /* - * 1 - security - * 2 - errors - * 3 - error debugging - * 4 - parsing errors - * 5 - parsing info - * 6 - parsing debug + * Debug levels */ +#define DEBUG_SECURITY 0x01 +#define DEBUG_ERROR 0x02 +#define DEBUG_INFO 0x04 +#define DEBUG_PARSE_ERR 0x08 +#define DEBUG_PARSE_INFO 0x10 +#define DEBUG_PARSE 0x20 +static int ipf_p_ftp_proxy_init = 0; static frentry_t ftppxyfr; -static ipftuneable_t ftptune = { - { &ippr_ftp_debug }, - "ippr_ftp_debug", - 0, - 10, - sizeof(ippr_ftp_debug), - 0, - NULL +static ipftuneable_t ipf_ftp_tuneables[] = { + { { (void *)offsetof(ipf_ftp_softc_t, ipf_p_ftp_debug) }, + "ftp_debug", 0, 0x7f, + stsizeof(ipf_ftp_softc_t, ipf_p_ftp_debug), + 0, NULL, NULL }, + { { (void *)offsetof(ipf_ftp_softc_t, ipf_p_ftp_pasvonly) }, + "ftp_pasvonly", 0, 1, + stsizeof(ipf_ftp_softc_t, ipf_p_ftp_pasvonly), + 0, NULL, NULL }, + { { (void *)offsetof(ipf_ftp_softc_t, ipf_p_ftp_insecure) }, + "ftp_insecure", 0, 1, + stsizeof(ipf_ftp_softc_t, ipf_p_ftp_insecure), + 0, NULL, NULL }, + { { (void *)offsetof(ipf_ftp_softc_t, ipf_p_ftp_pasvrdr) }, + "ftp_pasvrdr", 0, 1, + stsizeof(ipf_ftp_softc_t, ipf_p_ftp_pasvrdr), + 0, NULL, NULL }, + { { (void *)offsetof(ipf_ftp_softc_t, ipf_p_ftp_forcepasv) }, + "ftp_forcepasv", 0, 1, + stsizeof(ipf_ftp_softc_t, ipf_p_ftp_forcepasv), + 0, NULL, NULL }, + { { (void *)offsetof(ipf_ftp_softc_t, ipf_p_ftp_single_xfer) }, + "ftp_single_xfer", 0, 1, + stsizeof(ipf_ftp_softc_t, ipf_p_ftp_single_xfer), + 0, NULL, NULL }, + { { NULL }, NULL, 0, 0, 0, 0, NULL, NULL } }; +void +ipf_p_ftp_main_load() +{ + bzero((char *)&ftppxyfr, sizeof(ftppxyfr)); + ftppxyfr.fr_ref = 1; + ftppxyfr.fr_flags = FR_INQUE|FR_PASS|FR_QUICK|FR_KEEPSTATE; + + MUTEX_INIT(&ftppxyfr.fr_lock, "FTP Proxy Mutex"); + ipf_p_ftp_proxy_init = 1; +} + + +void +ipf_p_ftp_main_unload() +{ + + if (ipf_p_ftp_proxy_init == 1) { + MUTEX_DESTROY(&ftppxyfr.fr_lock); + ipf_p_ftp_proxy_init = 0; + } +} + + /* * Initialize local structures. */ -int ippr_ftp_init() +void * +ipf_p_ftp_soft_create(softc) + ipf_main_softc_t *softc; { - bzero((char *)&ftppxyfr, sizeof(ftppxyfr)); - ftppxyfr.fr_ref = 1; - ftppxyfr.fr_flags = FR_INQUE|FR_PASS|FR_QUICK|FR_KEEPSTATE; - MUTEX_INIT(&ftppxyfr.fr_lock, "FTP Proxy Mutex"); - ftp_proxy_init = 1; - (void) fr_addipftune(&ftptune); + ipf_ftp_softc_t *softf; - return 0; -} + KMALLOC(softf, ipf_ftp_softc_t *); + if (softf == NULL) + return NULL; + bzero((char *)softf, sizeof(*softf)); +#if defined(_KERNEL) + softf->ipf_p_ftp_debug = 0; +#else + softf->ipf_p_ftp_debug = DEBUG_PARSE_ERR; +#endif + softf->ipf_p_ftp_forcepasv = 1; -void ippr_ftp_fini() -{ - (void) fr_delipftune(&ftptune); - - if (ftp_proxy_init == 1) { - MUTEX_DESTROY(&ftppxyfr.fr_lock); - ftp_proxy_init = 0; + softf->ipf_p_ftp_tune = ipf_tune_array_copy(softf, + sizeof(ipf_ftp_tuneables), + ipf_ftp_tuneables); + if (softf->ipf_p_ftp_tune == NULL) { + ipf_p_ftp_soft_destroy(softc, softf); + return NULL; } + if (ipf_tune_array_link(softc, softf->ipf_p_ftp_tune) == -1) { + ipf_p_ftp_soft_destroy(softc, softf); + return NULL; + } + + return softf; } -int ippr_ftp_new(fin, aps, nat) -fr_info_t *fin; -ap_session_t *aps; -nat_t *nat; +void +ipf_p_ftp_soft_destroy(softc, arg) + ipf_main_softc_t *softc; + void *arg; +{ + ipf_ftp_softc_t *softf = arg; + + if (softf->ipf_p_ftp_tune != NULL) { + ipf_tune_array_unlink(softc, softf->ipf_p_ftp_tune); + KFREES(softf->ipf_p_ftp_tune, sizeof(ipf_ftp_tuneables)); + softf->ipf_p_ftp_tune = NULL; + } + + KFREE(softf); +} + + +int +ipf_p_ftp_new(arg, fin, aps, nat) + void *arg; + fr_info_t *fin; + ap_session_t *aps; + nat_t *nat; { ftpinfo_t *ftp; ftpside_t *f; @@ -133,11 +238,12 @@ nat_t *nat; if (ftp == NULL) return -1; - fin = fin; /* LINT */ nat = nat; /* LINT */ aps->aps_data = ftp; aps->aps_psiz = sizeof(ftpinfo_t); + aps->aps_sport = htons(fin->fin_sport); + aps->aps_dport = htons(fin->fin_dport); bzero((char *)ftp, sizeof(*ftp)); f = &ftp->ftp_side[0]; @@ -152,25 +258,53 @@ nat_t *nat; } -int ippr_ftp_port(fin, ip, nat, f, dlen) -fr_info_t *fin; -ip_t *ip; -nat_t *nat; -ftpside_t *f; -int dlen; +void +ipf_p_ftp_setpending(ipf_main_softc_t *softc, ftpinfo_t *ftp) +{ + if (ftp->ftp_pendnat != NULL) + ipf_nat_setpending(softc, ftp->ftp_pendnat); + + if (ftp->ftp_pendstate != NULL) { + READ_ENTER(&softc->ipf_state); + ipf_state_setpending(softc, ftp->ftp_pendstate); + RWLOCK_EXIT(&softc->ipf_state); + } +} + + +void +ipf_p_ftp_del(softc, aps) + ipf_main_softc_t *softc; + ap_session_t *aps; +{ + ftpinfo_t *ftp; + + ftp = aps->aps_data; + if (ftp != NULL) + ipf_p_ftp_setpending(softc, ftp); +} + + +int +ipf_p_ftp_port(softf, fin, ip, nat, ftp, dlen) + ipf_ftp_softc_t *softf; + fr_info_t *fin; + ip_t *ip; + nat_t *nat; + ftpinfo_t *ftp; + int dlen; { - tcphdr_t *tcp, tcph, *tcp2 = &tcph; char newbuf[IPF_FTPBUFSZ], *s; - struct in_addr swip, swip2; u_int a1, a2, a3, a4; - int inc, off, flags; u_short a5, a6, sp; size_t nlen, olen; - fr_info_t fi; - nat_t *nat2; + tcphdr_t *tcp; + int inc, off; + ftpside_t *f; mb_t *m; m = fin->fin_m; + f = &ftp->ftp_side[0]; tcp = (tcphdr_t *)fin->fin_dp; off = (char *)tcp - (char *)ip + (TCP_OFF(tcp) << 2) + fin->fin_ipoff; @@ -178,8 +312,10 @@ int dlen; * Check for client sending out PORT message. */ if (dlen < IPF_MINPORTLEN) { - if (ippr_ftp_debug > 1) - printf("ippr_ftp_port:dlen(%d) < IPF_MINPORTLEN\n", + DT3(ftp_PORT_error_dlen, nat_t *, nat, ftpside_t *, f, + u_int, dlen); + if (softf->ipf_p_ftp_debug & DEBUG_PARSE) + printf("ipf_p_ftp_port:dlen(%d) < IPF_MINPORTLEN\n", dlen); return 0; } @@ -190,16 +326,18 @@ int dlen; /* * Pick out the address components, two at a time. */ - a1 = ippr_ftp_atoi(&s); + a1 = ipf_p_ftp_atoi(&s); if (s == NULL) { - if (ippr_ftp_debug > 1) - printf("ippr_ftp_port:ippr_ftp_atoi(%d) failed\n", 1); + DT2(ftp_PORT_error_atoi_1, nat_t *, nat, ftpside_t *, f); + if (softf->ipf_p_ftp_debug & DEBUG_PARSE_ERR) + printf("ipf_p_ftp_port:ipf_p_ftp_atoi(%d) failed\n", 1); return 0; } - a2 = ippr_ftp_atoi(&s); + a2 = ipf_p_ftp_atoi(&s); if (s == NULL) { - if (ippr_ftp_debug > 1) - printf("ippr_ftp_port:ippr_ftp_atoi(%d) failed\n", 2); + DT2(ftp_PORT_error_atoi_2, nat_t *, nat, ftpside_t *, f); + if (softf->ipf_p_ftp_debug & DEBUG_PARSE_ERR) + printf("ipf_p_ftp_port:ipf_p_ftp_atoi(%d) failed\n", 2); return 0; } @@ -210,18 +348,21 @@ int dlen; a1 <<= 16; a1 |= a2; if (((nat->nat_dir == NAT_OUTBOUND) && - (a1 != ntohl(nat->nat_inip.s_addr))) || + (a1 != ntohl(nat->nat_osrcaddr))) || ((nat->nat_dir == NAT_INBOUND) && - (a1 != ntohl(nat->nat_oip.s_addr)))) { - if (ippr_ftp_debug > 0) - printf("ippr_ftp_port:%s != nat->nat_inip\n", "a1"); + (a1 != ntohl(nat->nat_nsrcaddr)))) { + DT3(ftp_PORT_error_address, nat_t *, nat, ftpside_t *, f, + u_int, a1); + if (softf->ipf_p_ftp_debug & DEBUG_ERROR) + printf("ipf_p_ftp_port:%s != nat->nat_inip\n", "a1"); return APR_ERR(1); } - a5 = ippr_ftp_atoi(&s); + a5 = ipf_p_ftp_atoi(&s); if (s == NULL) { - if (ippr_ftp_debug > 1) - printf("ippr_ftp_port:ippr_ftp_atoi(%d) failed\n", 3); + DT2(ftp_PORT_error_atoi_3, nat_t *, nat, ftpside_t *, f); + if (softf->ipf_p_ftp_debug & DEBUG_PARSE_ERR) + printf("ipf_p_ftp_port:ipf_p_ftp_atoi(%d) failed\n", 3); return 0; } if (*s == ')') @@ -232,34 +373,31 @@ int dlen; */ if (*s == '\n') s--; - if ((*s == '\r') && (*(s + 1) == '\n')) { - s += 2; - a6 = a5 & 0xff; - } else { - if (ippr_ftp_debug > 1) - printf("ippr_ftp_port:missing %s\n", "cr-lf"); + if ((*s != '\r') || (*(s + 1) != '\n')) { + DT2(ftp_PORT_error_no_crlf, nat_t *, nat, ftpside_t *, f); + if (softf->ipf_p_ftp_debug & DEBUG_PARSE_ERR) + printf("ipf_p_ftp_port:missing %s\n", "cr-lf"); return 0; } + s += 2; + a6 = a5 & 0xff; + /* + * Calculate the source port. Verification of > 1024 is in + * ipf_p_ftp_addport. + */ a5 >>= 8; a5 &= 0xff; sp = a5 << 8 | a6; - /* - * Don't allow the PORT command to specify a port < 1024 due to - * security crap. - */ - if (sp < 1024) { - if (ippr_ftp_debug > 0) - printf("ippr_ftp_port:sp(%d) < 1024\n", sp); - return 0; - } + /* * Calculate new address parts for PORT command */ if (nat->nat_dir == NAT_INBOUND) - a1 = ntohl(nat->nat_oip.s_addr); + a1 = ntohl(nat->nat_ndstaddr); else a1 = ntohl(ip->ip_src.s_addr); + a1 = ntohl(ip->ip_src.s_addr); a2 = (a1 >> 16) & 0xff; a3 = (a1 >> 8) & 0xff; a4 = a1 & 0xff; @@ -276,117 +414,213 @@ int dlen; nlen = strlen(newbuf); inc = nlen - olen; - if ((inc + ip->ip_len) > 65535) { - if (ippr_ftp_debug > 0) - printf("ippr_ftp_port:inc(%d) + ip->ip_len > 65535\n", + if ((inc + fin->fin_plen) > 65535) { + DT3(ftp_PORT_error_inc, nat_t *, nat, ftpside_t *, f, + int, inc); + if (softf->ipf_p_ftp_debug & DEBUG_ERROR) + printf("ipf_p_ftp_port:inc(%d) + ip->ip_len > 65535\n", inc); return 0; } #if !defined(_KERNEL) - bcopy(newbuf, MTOD(m, char *) + off, nlen); + M_ADJ(m, inc); #else -# if defined(MENTAT) - if (inc < 0) - (void)adjmsg(m, inc); -# else /* defined(MENTAT) */ /* * m_adj takes care of pkthdr.len, if required and treats inc<0 to * mean remove -len bytes from the end of the packet. * The mbuf chain will be extended if necessary by m_copyback(). */ if (inc < 0) - m_adj(m, inc); -# endif /* defined(MENTAT) */ + M_ADJ(m, inc); #endif /* !defined(_KERNEL) */ COPYBACK(m, off, nlen, newbuf); + fin->fin_flx |= FI_DOCKSUM; if (inc != 0) { - ip->ip_len += inc; - fin->fin_dlen += inc; fin->fin_plen += inc; + ip->ip_len = htons(fin->fin_plen); + fin->fin_dlen += inc; } + f->ftps_cmd = FTPXY_C_PORT; + return ipf_p_ftp_addport(softf, fin, ip, nat, ftp, dlen, sp, inc); +} + + +int +ipf_p_ftp_addport(softf, fin, ip, nat, ftp, dlen, nport, inc) + ipf_ftp_softc_t *softf; + fr_info_t *fin; + ip_t *ip; + nat_t *nat; + ftpinfo_t *ftp; + int dlen, nport, inc; +{ + tcphdr_t tcph, *tcp2 = &tcph; + ipf_main_softc_t *softc; + ipf_nat_softc_t *softn; + int direction; + fr_info_t fi; + ipnat_t *ipn; + nat_t *nat2; + u_short sp; + int flags; + + softc = fin->fin_main_soft; + softn = softc->ipf_nat_soft; + + if ((ftp->ftp_pendnat != NULL) || (ftp->ftp_pendstate != NULL)) { + if (softf->ipf_p_ftp_single_xfer != 0) { + DT2(ftp_PORT_error_add_active, nat_t *, nat, + ftpinfo_t *, ftp); + if (softf->ipf_p_ftp_debug & DEBUG_ERROR) + printf("ipf_p_ftp_addport:xfer active %p/%p\n", + ftp->ftp_pendnat, ftp->ftp_pendstate); + return 0; + } + ipf_p_ftp_setpending(softc, ftp); + } + + /* + * Add skeleton NAT entry for connection which will come back the + * other way. + */ + sp = nport; + /* + * Don't allow the PORT command to specify a port < 1024 due to + * security risks. + */ + if (sp < 1024) { + DT3(ftp_PORT_error_port, nat_t *, nat, ftpinfo_t *, ftp, + u_int, sp); + if (softf->ipf_p_ftp_debug & DEBUG_SECURITY) + printf("ipf_p_ftp_addport:sp(%d) < 1024\n", sp); + return 0; + } /* * The server may not make the connection back from port 20, but * it is the most likely so use it here to check for a conflicting * mapping. */ bcopy((char *)fin, (char *)&fi, sizeof(fi)); - fi.fin_state = NULL; - fi.fin_nat = NULL; fi.fin_flx |= FI_IGNORE; fi.fin_data[0] = sp; fi.fin_data[1] = fin->fin_data[1] - 1; + fi.fin_src6 = nat->nat_ndst6; + fi.fin_dst6 = nat->nat_nsrc6; + + if (nat->nat_v[0] == 6) { +#ifndef USE_INET6 + return APR_INC(inc); +#endif + } + /* * Add skeleton NAT entry for connection which will come back the * other way. */ - if (nat->nat_dir == NAT_OUTBOUND) - nat2 = nat_outlookup(&fi, NAT_SEARCH|IPN_TCP, nat->nat_p, - nat->nat_inip, nat->nat_oip); - else - nat2 = nat_inlookup(&fi, NAT_SEARCH|IPN_TCP, nat->nat_p, - nat->nat_inip, nat->nat_oip); - if (nat2 == NULL) { - int slen; - - slen = ip->ip_len; - ip->ip_len = fin->fin_hlen + sizeof(*tcp2); - bzero((char *)tcp2, sizeof(*tcp2)); - tcp2->th_win = htons(8192); - tcp2->th_sport = htons(sp); - TCP_OFF_A(tcp2, 5); - tcp2->th_flags = TH_SYN; - tcp2->th_dport = 0; /* XXX - don't specify remote port */ - fi.fin_data[1] = 0; - fi.fin_dlen = sizeof(*tcp2); - fi.fin_plen = fi.fin_hlen + sizeof(*tcp2); - fi.fin_dp = (char *)tcp2; - fi.fin_fr = &ftppxyfr; - fi.fin_out = nat->nat_dir; - fi.fin_flx &= FI_LOWTTL|FI_FRAG|FI_TCPUDP|FI_OPTIONS|FI_IGNORE; - swip = ip->ip_src; - swip2 = ip->ip_dst; +#ifdef USE_INET6 + if (nat->nat_v[0] == 6) { if (nat->nat_dir == NAT_OUTBOUND) { - fi.fin_fi.fi_saddr = nat->nat_inip.s_addr; - ip->ip_src = nat->nat_inip; - } else if (nat->nat_dir == NAT_INBOUND) { - fi.fin_fi.fi_saddr = nat->nat_oip.s_addr; - ip->ip_src = nat->nat_oip; + nat2 = ipf_nat6_outlookup(&fi, IPN_TCP|NAT_SEARCH, + nat->nat_pr[1], + &nat->nat_osrc6.in6, + &nat->nat_odst6.in6); + } else { + nat2 = ipf_nat6_inlookup(&fi, IPN_TCP|NAT_SEARCH, + nat->nat_pr[0], + &nat->nat_odst6.in6, + &nat->nat_osrc6.in6); } - - flags = NAT_SLAVE|IPN_TCP|SI_W_DPORT; - if (nat->nat_dir == NAT_INBOUND) - flags |= NAT_NOTRULEPORT; - nat2 = nat_new(&fi, nat->nat_ptr, NULL, flags, nat->nat_dir); - - if (nat2 != NULL) { - (void) nat_proto(&fi, nat2, IPN_TCP); - nat_update(&fi, nat2, nat->nat_ptr); - fi.fin_ifp = NULL; - if (nat->nat_dir == NAT_INBOUND) { - fi.fin_fi.fi_daddr = nat->nat_inip.s_addr; - ip->ip_dst = nat->nat_inip; - } - (void) fr_addstate(&fi, NULL, SI_W_DPORT); - if (fi.fin_state != NULL) - fr_statederef((ipstate_t **)&fi.fin_state); + } else +#endif + { + if (nat->nat_dir == NAT_OUTBOUND) { + nat2 = ipf_nat_outlookup(&fi, IPN_TCP|NAT_SEARCH, + nat->nat_pr[1], + nat->nat_osrcip, + nat->nat_odstip); + } else { + nat2 = ipf_nat_inlookup(&fi, IPN_TCP|NAT_SEARCH, + nat->nat_pr[0], + nat->nat_odstip, + nat->nat_osrcip); } - ip->ip_len = slen; - ip->ip_src = swip; - ip->ip_dst = swip2; } + if (nat2 != NULL) + return APR_INC(inc); + + ipn = ipf_proxy_rule_rev(nat); + if (ipn == NULL) + return APR_ERR(1); + ipn->in_use = 0; + + fi.fin_fr = &ftppxyfr; + fi.fin_dp = (char *)tcp2; + fi.fin_dlen = sizeof(*tcp2); + fi.fin_plen = fi.fin_hlen + sizeof(*tcp2); + fi.fin_flx &= FI_LOWTTL|FI_FRAG|FI_TCPUDP|FI_OPTIONS|FI_IGNORE; + fi.fin_data[1] = sp; + fi.fin_data[0] = 0; + + bzero((char *)tcp2, sizeof(*tcp2)); + tcp2->th_sport = 0; + tcp2->th_dport = htons(sp); + + tcp2->th_win = htons(8192); + TCP_OFF_A(tcp2, 5); + tcp2->th_flags = TH_SYN; + + if (nat->nat_dir == NAT_INBOUND) { + fi.fin_out = 1; + direction = NAT_OUTBOUND; + } else { + fi.fin_out = 0; + direction = NAT_INBOUND; + } + flags = SI_W_SPORT|NAT_SLAVE|IPN_TCP; + + MUTEX_ENTER(&softn->ipf_nat_new); + if (nat->nat_v[0] == 6) { +#ifdef USE_INET6 + nat2 = ipf_nat6_add(&fi, ipn, &ftp->ftp_pendnat, flags, + direction); +#endif + } else { + nat2 = ipf_nat_add(&fi, ipn, &ftp->ftp_pendnat, flags, + direction); + } + MUTEX_EXIT(&softn->ipf_nat_new); + + if (nat2 == NULL) { + KFREES(ipn, ipn->in_size); + return APR_ERR(1); + } + + (void) ipf_nat_proto(&fi, nat2, IPN_TCP); + MUTEX_ENTER(&nat2->nat_lock); + ipf_nat_update(&fi, nat2); + MUTEX_EXIT(&nat2->nat_lock); + fi.fin_ifp = NULL; + if (nat2->nat_dir == NAT_INBOUND) + fi.fin_dst6 = nat->nat_osrc6; + if (ipf_state_add(softc, &fi, (ipstate_t **)&ftp->ftp_pendstate, + SI_W_SPORT) != 0) + ipf_nat_setpending(softc, nat2); + return APR_INC(inc); } -int ippr_ftp_client(fin, ip, nat, ftp, dlen) -fr_info_t *fin; -nat_t *nat; -ftpinfo_t *ftp; -ip_t *ip; -int dlen; +int +ipf_p_ftp_client(softf, fin, ip, nat, ftp, dlen) + ipf_ftp_softc_t *softf; + fr_info_t *fin; + nat_t *nat; + ftpinfo_t *ftp; + ip_t *ip; + int dlen; { char *rptr, *wptr, cmd[6], c; ftpside_t *f; @@ -408,6 +642,7 @@ int dlen; cmd[i] = '\0'; ftp->ftp_incok = 0; + DT2(ftp_client_command, char [], cmd, int, ftp->ftp_passok); if (!strncmp(cmd, "USER ", 5) || !strncmp(cmd, "XAUT ", 5)) { if (ftp->ftp_passok == FTPXY_ADOK_1 || ftp->ftp_passok == FTPXY_AUOK_1) { @@ -437,14 +672,24 @@ int dlen; !strncmp(cmd, "ACCT ", 5)) { ftp->ftp_passok = FTPXY_ACCT_1; ftp->ftp_incok = 1; - } else if ((ftp->ftp_passok == FTPXY_GO) && !ippr_ftp_pasvonly && + } else if ((ftp->ftp_passok == FTPXY_GO) && + !softf->ipf_p_ftp_pasvonly && !strncmp(cmd, "PORT ", 5)) { - inc = ippr_ftp_port(fin, ip, nat, f, dlen); - } else if (ippr_ftp_insecure && !ippr_ftp_pasvonly && + inc = ipf_p_ftp_port(softf, fin, ip, nat, ftp, dlen); + } else if ((ftp->ftp_passok == FTPXY_GO) && + !softf->ipf_p_ftp_pasvonly && + !strncmp(cmd, "EPRT ", 5)) { + inc = ipf_p_ftp_eprt(softf, fin, ip, nat, ftp, dlen); + } else if (softf->ipf_p_ftp_insecure && + !softf->ipf_p_ftp_pasvonly && !strncmp(cmd, "PORT ", 5)) { - inc = ippr_ftp_port(fin, ip, nat, f, dlen); + inc = ipf_p_ftp_port(softf, fin, ip, nat, ftp, dlen); } + if (softf->ipf_p_ftp_debug & DEBUG_PARSE) + printf("ipf_p_ftp_client: cmd[%s] passok %d incok %d inc %d\n", + cmd, ftp->ftp_passok, ftp->ftp_incok, inc); + DT2(ftp_client_passok, char *, cmd, int, ftp->ftp_passok); while ((*rptr++ != '\n') && (rptr < wptr)) ; f->ftps_rptr = rptr; @@ -452,12 +697,14 @@ int dlen; } -int ippr_ftp_pasv(fin, ip, nat, ftp, dlen) -fr_info_t *fin; -ip_t *ip; -nat_t *nat; -ftpinfo_t *ftp; -int dlen; +int +ipf_p_ftp_pasv(softf, fin, ip, nat, ftp, dlen) + ipf_ftp_softc_t *softf; + fr_info_t *fin; + ip_t *ip; + nat_t *nat; + ftpinfo_t *ftp; + int dlen; { u_int a1, a2, a3, a4, data_ip; char newbuf[IPF_FTPBUFSZ]; @@ -466,11 +713,12 @@ int dlen; ftpside_t *f; char *s; - if (ippr_ftp_forcepasv != 0 && - ftp->ftp_side[0].ftps_cmds != FTPXY_C_PASV) { - if (ippr_ftp_debug > 0) - printf("ippr_ftp_pasv:ftps_cmds(%d) != FTPXY_C_PASV\n", - ftp->ftp_side[0].ftps_cmds); + if ((softf->ipf_p_ftp_forcepasv != 0) && + (ftp->ftp_side[0].ftps_cmd != FTPXY_C_PASV)) { + DT2(ftp_PASV_error_state, nat_t *, nat, ftpinfo_t *, ftp); + if (softf->ipf_p_ftp_debug & DEBUG_ERROR) + printf("ipf_p_ftp_pasv:ftps_cmd(%d) != FTPXY_C_PASV\n", + ftp->ftp_side[0].ftps_cmd); return 0; } @@ -481,14 +729,17 @@ int dlen; * Check for PASV reply message. */ if (dlen < IPF_MIN227LEN) { - if (ippr_ftp_debug > 1) - printf("ippr_ftp_pasv:dlen(%d) < IPF_MIN227LEN\n", + DT3(ftp_PASV_error_short, nat_t *, nat, ftpinfo_t *, ftp, + int, dlen); + if (softf->ipf_p_ftp_debug & DEBUG_ERROR) + printf("ipf_p_ftp_pasv:dlen(%d) < IPF_MIN227LEN\n", dlen); return 0; } else if (strncmp(f->ftps_rptr, "227 Entering Passive Mod", PASV_REPLEN)) { - if (ippr_ftp_debug > 0) - printf("ippr_ftp_pasv:%d reply wrong\n", 227); + DT2(ftp_PASV_error_string, nat_t *, nat, ftpinfo_t *, ftp); + if (softf->ipf_p_ftp_debug & DEBUG_PARSE_ERR) + printf("ipf_p_ftp_pasv:%d reply wrong\n", 227); return 0; } @@ -509,16 +760,18 @@ int dlen; /* * Pick out the address components, two at a time. */ - a1 = ippr_ftp_atoi(&s); + a1 = ipf_p_ftp_atoi(&s); if (s == NULL) { - if (ippr_ftp_debug > 1) - printf("ippr_ftp_pasv:ippr_ftp_atoi(%d) failed\n", 1); + DT2(ftp_PASV_error_atoi_1, nat_t *, nat, ftpside_t *, f); + if (softf->ipf_p_ftp_debug & DEBUG_PARSE_ERR) + printf("ipf_p_ftp_pasv:ipf_p_ftp_atoi(%d) failed\n", 1); return 0; } - a2 = ippr_ftp_atoi(&s); + a2 = ipf_p_ftp_atoi(&s); if (s == NULL) { - if (ippr_ftp_debug > 1) - printf("ippr_ftp_pasv:ippr_ftp_atoi(%d) failed\n", 2); + DT2(ftp_PASV_error_atoi_2, nat_t *, nat, ftpside_t *, f); + if (softf->ipf_p_ftp_debug & DEBUG_PARSE_ERR) + printf("ipf_p_ftp_pasv:ipf_p_ftp_atoi(%d) failed\n", 2); return 0; } @@ -530,18 +783,21 @@ int dlen; a1 |= a2; if (((nat->nat_dir == NAT_INBOUND) && - (a1 != ntohl(nat->nat_inip.s_addr))) || + (a1 != ntohl(nat->nat_ndstaddr))) || ((nat->nat_dir == NAT_OUTBOUND) && - (a1 != ntohl(nat->nat_oip.s_addr)))) { - if (ippr_ftp_debug > 0) - printf("ippr_ftp_pasv:%s != nat->nat_oip\n", "a1"); + (a1 != ntohl(nat->nat_odstaddr)))) { + DT3(ftp_PASV_error_address, nat_t *, nat, ftpside_t *, f, + u_int, a1); + if (softf->ipf_p_ftp_debug & DEBUG_ERROR) + printf("ipf_p_ftp_pasv:%s != nat->nat_oip\n", "a1"); return 0; } - a5 = ippr_ftp_atoi(&s); + a5 = ipf_p_ftp_atoi(&s); if (s == NULL) { - if (ippr_ftp_debug > 1) - printf("ippr_ftp_pasv:ippr_ftp_atoi(%d) failed\n", 3); + DT2(ftp_PASV_error_atoi_3, nat_t *, nat, ftpside_t *, f); + if (softf->ipf_p_ftp_debug & DEBUG_PARSE_ERR) + printf("ipf_p_ftp_pasv:ipf_p_ftp_atoi(%d) failed\n", 3); return 0; } @@ -554,13 +810,13 @@ int dlen; /* * check for CR-LF at the end. */ - if ((*s == '\r') && (*(s + 1) == '\n')) { - s += 2; - } else { - if (ippr_ftp_debug > 1) - printf("ippr_ftp_pasv:missing %s", "cr-lf\n"); + if ((*s != '\r') || (*(s + 1) != '\n')) { + DT(pasv_missing_crlf); + if (softf->ipf_p_ftp_debug & DEBUG_PARSE_ERR) + printf("ipf_p_ftp_pasv:missing %s", "cr-lf\n"); return 0; } + s += 2; a6 = a5 & 0xff; a5 >>= 8; @@ -568,7 +824,7 @@ int dlen; * Calculate new address parts for 227 reply */ if (nat->nat_dir == NAT_INBOUND) { - data_ip = nat->nat_outip.s_addr; + data_ip = nat->nat_odstaddr; a1 = ntohl(data_ip); } else data_ip = htonl(a1); @@ -587,156 +843,165 @@ int dlen; "227 Entering Passive Mode", brackets[0], a1, a2, a3, a4, a5, a6, brackets[1]); #endif - return ippr_ftp_pasvreply(fin, ip, nat, f, (a5 << 8 | a6), - newbuf, s, data_ip); + return ipf_p_ftp_pasvreply(softf, fin, ip, nat, ftp, (a5 << 8 | a6), + newbuf, s); } -int ippr_ftp_pasvreply(fin, ip, nat, f, port, newmsg, s, data_ip) -fr_info_t *fin; -ip_t *ip; -nat_t *nat; -ftpside_t *f; -u_int port; -char *newmsg; -char *s; -u_int data_ip; +int +ipf_p_ftp_pasvreply(softf, fin, ip, nat, ftp, port, newmsg, s) + ipf_ftp_softc_t *softf; + fr_info_t *fin; + ip_t *ip; + nat_t *nat; + ftpinfo_t *ftp; + u_int port; + char *newmsg; + char *s; { - int inc, off, nflags, sflags; + int inc, off, nflags; tcphdr_t *tcp, tcph, *tcp2; - struct in_addr swip, swip2; - struct in_addr data_addr; + ipf_main_softc_t *softc; + ipf_nat_softc_t *softn; size_t nlen, olen; +#ifdef USE_INET6 + ip6_t *ip6; +#endif + ipnat_t *ipn; fr_info_t fi; + ftpside_t *f; nat_t *nat2; mb_t *m; + softc = fin->fin_main_soft; + softn = softc->ipf_nat_soft; + + if ((ftp->ftp_pendnat != NULL) || (ftp->ftp_pendstate != NULL)) + ipf_p_ftp_setpending(softc, ftp); + m = fin->fin_m; tcp = (tcphdr_t *)fin->fin_dp; off = (char *)tcp - (char *)ip + (TCP_OFF(tcp) << 2) + fin->fin_ipoff; - data_addr.s_addr = data_ip; tcp2 = &tcph; inc = 0; - + f = &ftp->ftp_side[1]; olen = s - f->ftps_rptr; nlen = strlen(newmsg); inc = nlen - olen; - if ((inc + ip->ip_len) > 65535) { - if (ippr_ftp_debug > 0) - printf("ippr_ftp_pasv:inc(%d) + ip->ip_len > 65535\n", + if ((inc + fin->fin_plen) > 65535) { + DT3(ftp_PASV_error_inc, nat_t *, nat, ftpside_t *, f, + int, inc); + if (softf->ipf_p_ftp_debug & DEBUG_ERROR) + printf("ipf_p_ftp_pasv:inc(%d) + ip->ip_len > 65535\n", inc); return 0; } + ipn = ipf_proxy_rule_fwd(nat); + if (ipn == NULL) + return APR_ERR(1); + ipn->in_use = 0; + + /* + * Add skeleton NAT entry for connection which will come back the + * other way. + */ + bzero((char *)tcp2, sizeof(*tcp2)); + bcopy((char *)fin, (char *)&fi, sizeof(fi)); + fi.fin_flx |= FI_IGNORE; + fi.fin_data[0] = 0; + fi.fin_data[1] = port; + nflags = IPN_TCP|SI_W_SPORT; + + fi.fin_fr = &ftppxyfr; + fi.fin_dp = (char *)tcp2; + fi.fin_out = 1 - fin->fin_out; + fi.fin_dlen = sizeof(*tcp2); + fi.fin_src6 = nat->nat_osrc6; + fi.fin_dst6 = nat->nat_odst6; + fi.fin_plen = fi.fin_hlen + sizeof(*tcp); + fi.fin_flx &= FI_LOWTTL|FI_FRAG|FI_TCPUDP|FI_OPTIONS|FI_IGNORE; + + TCP_OFF_A(tcp2, 5); + tcp2->th_flags = TH_SYN; + tcp2->th_win = htons(8192); + tcp2->th_dport = htons(port); + + MUTEX_ENTER(&softn->ipf_nat_new); +#ifdef USE_INET6 + if (nat->nat_v[0] == 6) + nat2 = ipf_nat6_add(&fi, ipn, &ftp->ftp_pendnat, + nflags, nat->nat_dir); + else +#endif + nat2 = ipf_nat_add(&fi, ipn, &ftp->ftp_pendnat, + nflags, nat->nat_dir); + MUTEX_EXIT(&softn->ipf_nat_new); + + if (nat2 == NULL) { + KFREES(ipn, ipn->in_size); + return APR_ERR(1); + } + + (void) ipf_nat_proto(&fi, nat2, IPN_TCP); + MUTEX_ENTER(&nat2->nat_lock); + ipf_nat_update(&fi, nat2); + MUTEX_EXIT(&nat2->nat_lock); + fi.fin_ifp = NULL; + if (nat->nat_dir == NAT_INBOUND) { + if (nat->nat_v[0] == 6) { +#ifdef USE_INET6 + fi.fin_dst6 = nat->nat_ndst6; +#endif + } else { + fi.fin_daddr = nat->nat_ndstaddr; + } + } + if (ipf_state_add(softc, &fi, (ipstate_t **)&ftp->ftp_pendstate, + SI_W_SPORT) != 0) + ipf_nat_setpending(softc, nat2); + #if !defined(_KERNEL) - bcopy(newmsg, MTOD(m, char *) + off, nlen); + M_ADJ(m, inc); #else -# if defined(MENTAT) - if (inc < 0) - (void)adjmsg(m, inc); -# else /* defined(MENTAT) */ /* * m_adj takes care of pkthdr.len, if required and treats inc<0 to * mean remove -len bytes from the end of the packet. * The mbuf chain will be extended if necessary by m_copyback(). */ if (inc < 0) - m_adj(m, inc); -# endif /* defined(MENTAT) */ + M_ADJ(m, inc); #endif /* !defined(_KERNEL) */ COPYBACK(m, off, nlen, newmsg); + fin->fin_flx |= FI_DOCKSUM; if (inc != 0) { - ip->ip_len += inc; - fin->fin_dlen += inc; fin->fin_plen += inc; + fin->fin_dlen += inc; + if (nat->nat_v[0] == 6) { +#ifdef USE_INET6 + ip6 = (ip6_t *)fin->fin_ip; + u_short len = ntohs(ip6->ip6_plen) + inc; + ip6->ip6_plen = htons(len); +#endif + } else { + ip->ip_len = htons(fin->fin_plen); + } } - /* - * Add skeleton NAT entry for connection which will come back the - * other way. - */ - bcopy((char *)fin, (char *)&fi, sizeof(fi)); - fi.fin_state = NULL; - fi.fin_nat = NULL; - fi.fin_flx |= FI_IGNORE; - fi.fin_data[0] = 0; - fi.fin_data[1] = port; - nflags = IPN_TCP|SI_W_SPORT; - if (ippr_ftp_pasvrdr && f->ftps_ifp) - nflags |= SI_W_DPORT; - if (nat->nat_dir == NAT_OUTBOUND) - nat2 = nat_outlookup(&fi, nflags|NAT_SEARCH, - nat->nat_p, nat->nat_inip, nat->nat_oip); - else - nat2 = nat_inlookup(&fi, nflags|NAT_SEARCH, - nat->nat_p, nat->nat_inip, nat->nat_oip); - if (nat2 == NULL) { - int slen; - - slen = ip->ip_len; - ip->ip_len = fin->fin_hlen + sizeof(*tcp2); - bzero((char *)tcp2, sizeof(*tcp2)); - tcp2->th_win = htons(8192); - tcp2->th_sport = 0; /* XXX - fake it for nat_new */ - TCP_OFF_A(tcp2, 5); - tcp2->th_flags = TH_SYN; - fi.fin_data[1] = port; - fi.fin_dlen = sizeof(*tcp2); - tcp2->th_dport = htons(port); - fi.fin_data[0] = 0; - fi.fin_dp = (char *)tcp2; - fi.fin_plen = fi.fin_hlen + sizeof(*tcp); - fi.fin_fr = &ftppxyfr; - fi.fin_out = nat->nat_dir; - fi.fin_flx &= FI_LOWTTL|FI_FRAG|FI_TCPUDP|FI_OPTIONS|FI_IGNORE; - swip = ip->ip_src; - swip2 = ip->ip_dst; - if (nat->nat_dir == NAT_OUTBOUND) { - fi.fin_fi.fi_daddr = data_addr.s_addr; - fi.fin_fi.fi_saddr = nat->nat_inip.s_addr; - ip->ip_dst = data_addr; - ip->ip_src = nat->nat_inip; - } else if (nat->nat_dir == NAT_INBOUND) { - fi.fin_fi.fi_saddr = nat->nat_oip.s_addr; - fi.fin_fi.fi_daddr = nat->nat_outip.s_addr; - ip->ip_src = nat->nat_oip; - ip->ip_dst = nat->nat_outip; - } - - sflags = nflags; - nflags |= NAT_SLAVE; - if (nat->nat_dir == NAT_INBOUND) - nflags |= NAT_NOTRULEPORT; - nat2 = nat_new(&fi, nat->nat_ptr, NULL, nflags, nat->nat_dir); - if (nat2 != NULL) { - (void) nat_proto(&fi, nat2, IPN_TCP); - nat_update(&fi, nat2, nat->nat_ptr); - fi.fin_ifp = NULL; - if (nat->nat_dir == NAT_INBOUND) { - fi.fin_fi.fi_daddr = nat->nat_inip.s_addr; - ip->ip_dst = nat->nat_inip; - } - (void) fr_addstate(&fi, NULL, sflags); - if (fi.fin_state != NULL) - fr_statederef((ipstate_t **)&fi.fin_state); - } - - ip->ip_len = slen; - ip->ip_src = swip; - ip->ip_dst = swip2; - } - return inc; + return APR_INC(inc); } -int ippr_ftp_server(fin, ip, nat, ftp, dlen) -fr_info_t *fin; -ip_t *ip; -nat_t *nat; -ftpinfo_t *ftp; -int dlen; +int +ipf_p_ftp_server(softf, fin, ip, nat, ftp, dlen) + ipf_ftp_softc_t *softf; + fr_info_t *fin; + ip_t *ip; + nat_t *nat; + ftpinfo_t *ftp; + int dlen; { char *rptr, *wptr; ftpside_t *f; @@ -747,19 +1012,29 @@ int dlen; rptr = f->ftps_rptr; wptr = f->ftps_wptr; + DT2(ftp_server_response, char *, rptr, int, ftp->ftp_passok); if (*rptr == ' ') goto server_cmd_ok; if (!ISDIGIT(*rptr) || !ISDIGIT(*(rptr + 1)) || !ISDIGIT(*(rptr + 2))) return 0; + if (softf->ipf_p_ftp_debug & DEBUG_PARSE) + printf("ipf_p_ftp_server_1: cmd[%4.4s] passok %d\n", + rptr, ftp->ftp_passok); if (ftp->ftp_passok == FTPXY_GO) { if (!strncmp(rptr, "227 ", 4)) - inc = ippr_ftp_pasv(fin, ip, nat, ftp, dlen); + inc = ipf_p_ftp_pasv(softf, fin, ip, nat, ftp, dlen); else if (!strncmp(rptr, "229 ", 4)) - inc = ippr_ftp_epsv(fin, ip, nat, f, dlen); - } else if (ippr_ftp_insecure && !strncmp(rptr, "227 ", 4)) { - inc = ippr_ftp_pasv(fin, ip, nat, ftp, dlen); - } else if (ippr_ftp_insecure && !strncmp(rptr, "229 ", 4)) { - inc = ippr_ftp_epsv(fin, ip, nat, f, dlen); + inc = ipf_p_ftp_epsv(softf, fin, ip, nat, ftp, dlen); + else if (strncmp(rptr, "200", 3)) { + /* + * 200 is returned for a successful command. + */ + ; + } + } else if (softf->ipf_p_ftp_insecure && !strncmp(rptr, "227 ", 4)) { + inc = ipf_p_ftp_pasv(softf, fin, ip, nat, ftp, dlen); + } else if (softf->ipf_p_ftp_insecure && !strncmp(rptr, "229 ", 4)) { + inc = ipf_p_ftp_epsv(softf, fin, ip, nat, ftp, dlen); } else if (*rptr == '5' || *rptr == '4') ftp->ftp_passok = FTPXY_INIT; else if (ftp->ftp_incok) { @@ -784,8 +1059,13 @@ int dlen; } } } -server_cmd_ok: ftp->ftp_incok = 0; +server_cmd_ok: + if (softf->ipf_p_ftp_debug & DEBUG_PARSE) + printf("ipf_p_ftp_server_2: cmd[%4.4s] passok %d\n", + rptr, ftp->ftp_passok); + DT3(ftp_server_passok, char *,rptr, int, ftp->ftp_incok, + int, ftp->ftp_passok); while ((*rptr++ != '\n') && (rptr < wptr)) ; @@ -795,13 +1075,20 @@ server_cmd_ok: /* + * 0 FTPXY_JUNK_OK + * 1 FTPXY_JUNK_BAD + * 2 FTPXY_JUNK_EOL + * 3 FTPXY_JUNK_CONT + * * Look to see if the buffer starts with something which we recognise as * being the correct syntax for the FTP protocol. */ -int ippr_ftp_client_valid(ftps, buf, len) -ftpside_t *ftps; -char *buf; -size_t len; +int +ipf_p_ftp_client_valid(softf, ftps, buf, len) + ipf_ftp_softc_t *softf; + ftpside_t *ftps; + char *buf; + size_t len; { register char *s, c, pc; register size_t i = len; @@ -809,12 +1096,13 @@ size_t len; s = buf; - if (ftps->ftps_junk == 1) - return 1; + if (ftps->ftps_junk == FTPXY_JUNK_BAD) + return FTPXY_JUNK_BAD; if (i < 5) { - if (ippr_ftp_debug > 3) - printf("ippr_ftp_client_valid:i(%d) < 5\n", (int)i); + DT1(client_valid, int, i); + if (softf->ipf_p_ftp_debug & DEBUG_ERROR) + printf("ipf_p_ftp_client_valid:i(%d) < 5\n", (int)i); return 2; } @@ -847,12 +1135,13 @@ size_t len; goto bad_client_command; } else { bad_client_command: - if (ippr_ftp_debug > 3) + DT4(client_junk, int, len, int, i, int, c, char *, buf); + if (softf->ipf_p_ftp_debug & DEBUG_PARSE_ERR) printf("%s:bad:junk %d len %d/%d c 0x%x buf [%*.*s]\n", - "ippr_ftp_client_valid", + "ipf_p_ftp_client_valid", ftps->ftps_junk, (int)len, (int)i, c, (int)len, (int)len, buf); - return 1; + return FTPXY_JUNK_BAD; } for (; i; i--) { @@ -860,25 +1149,30 @@ bad_client_command: c = *s++; if ((pc == '\r') && (c == '\n')) { cmd[4] = '\0'; - if (!strcmp(cmd, "PASV")) - ftps->ftps_cmds = FTPXY_C_PASV; - else - ftps->ftps_cmds = 0; + if (!strcmp(cmd, "PASV")) { + ftps->ftps_cmd = FTPXY_C_PASV; + } else if (!strcmp(cmd, "EPSV")) { + ftps->ftps_cmd = FTPXY_C_EPSV; + } else { + ftps->ftps_cmd = 0; + } return 0; } } #if !defined(_KERNEL) - printf("ippr_ftp_client_valid:junk after cmd[%*.*s]\n", + printf("ipf_p_ftp_client_valid:junk after cmd[%*.*s]\n", (int)len, (int)len, buf); #endif - return 2; + return FTPXY_JUNK_EOL; } -int ippr_ftp_server_valid(ftps, buf, len) -ftpside_t *ftps; -char *buf; -size_t len; +int +ipf_p_ftp_server_valid(softf, ftps, buf, len) + ipf_ftp_softc_t *softf; + ftpside_t *ftps; + char *buf; + size_t len; { register char *s, c, pc; register size_t i = len; @@ -887,19 +1181,22 @@ size_t len; s = buf; cmd = 0; - if (ftps->ftps_junk == 1) - return 1; + if (ftps->ftps_junk == FTPXY_JUNK_BAD) + return FTPXY_JUNK_BAD; if (i < 5) { - if (ippr_ftp_debug > 3) - printf("ippr_ftp_servert_valid:i(%d) < 5\n", (int)i); + DT1(server_valid, int, i); + if (softf->ipf_p_ftp_debug & DEBUG_PARSE_ERR) + printf("ipf_p_ftp_servert_valid:i(%d) < 5\n", (int)i); return 2; } c = *s++; i--; - if (c == ' ') + if (c == ' ') { + cmd = -1; goto search_eol; + } if (ISDIGIT(c)) { cmd = (c - '0') * 100; @@ -915,40 +1212,54 @@ size_t len; i--; if ((c != '-') && (c != ' ')) goto bad_server_command; + if (c == '-') + return FTPXY_JUNK_CONT; } else goto bad_server_command; } else goto bad_server_command; } else { bad_server_command: - if (ippr_ftp_debug > 3) + DT4(server_junk, int len, buf, int, i, int, c, char *, buf); + if (softf->ipf_p_ftp_debug & DEBUG_PARSE_INFO) printf("%s:bad:junk %d len %d/%d c 0x%x buf [%*.*s]\n", - "ippr_ftp_server_valid", + "ipf_p_ftp_server_valid", ftps->ftps_junk, (int)len, (int)i, c, (int)len, (int)len, buf); - return 1; + if (ftps->ftps_junk == FTPXY_JUNK_CONT) + return FTPXY_JUNK_CONT; + return FTPXY_JUNK_BAD; } search_eol: for (; i; i--) { pc = c; c = *s++; if ((pc == '\r') && (c == '\n')) { - ftps->ftps_cmds = cmd; - return 0; + if (cmd == -1) { + if (ftps->ftps_junk == FTPXY_JUNK_CONT) + return FTPXY_JUNK_CONT; + } else { + ftps->ftps_cmd = cmd; + } + return FTPXY_JUNK_OK; } } - if (ippr_ftp_debug > 3) - printf("ippr_ftp_server_valid:junk after cmd[%*.*s]\n", + + DT2(junk_eol, int, len, char *, buf); + if (softf->ipf_p_ftp_debug & DEBUG_PARSE_INFO) + printf("ipf_p_ftp_server_valid:junk after cmd[%*.*s]\n", (int)len, (int)len, buf); - return 2; + return FTPXY_JUNK_EOL; } -int ippr_ftp_valid(ftp, side, buf, len) -ftpinfo_t *ftp; -int side; -char *buf; -size_t len; +int +ipf_p_ftp_valid(softf, ftp, side, buf, len) + ipf_ftp_softc_t *softf; + ftpinfo_t *ftp; + int side; + char *buf; + size_t len; { ftpside_t *ftps; int ret; @@ -956,9 +1267,9 @@ size_t len; ftps = &ftp->ftp_side[side]; if (side == 0) - ret = ippr_ftp_client_valid(ftps, buf, len); + ret = ipf_p_ftp_client_valid(softf, ftps, buf, len); else - ret = ippr_ftp_server_valid(ftps, buf, len); + ret = ipf_p_ftp_server_valid(softf, ftps, buf, len); return ret; } @@ -971,13 +1282,15 @@ size_t len; * rv == 0 for inbound processing, * rv == 1 for outbound processing. */ -int ippr_ftp_process(fin, nat, ftp, rv) -fr_info_t *fin; -nat_t *nat; -ftpinfo_t *ftp; -int rv; +int +ipf_p_ftp_process(softf, fin, nat, ftp, rv) + ipf_ftp_softc_t *softf; + fr_info_t *fin; + nat_t *nat; + ftpinfo_t *ftp; + int rv; { - int mlen, len, off, inc, i, sel, sel2, ok, ackoff, seqoff; + int mlen, len, off, inc, i, sel, sel2, ok, ackoff, seqoff, retry; char *rptr, *wptr, *s; u_32_t thseq, thack; ap_session_t *aps; @@ -995,14 +1308,17 @@ int rv; t = &ftp->ftp_side[1 - rv]; thseq = ntohl(tcp->th_seq); thack = ntohl(tcp->th_ack); - #ifdef __sgi mlen = fin->fin_plen - off; #else mlen = MSGDSIZE(m) - off; #endif - if (ippr_ftp_debug > 4) - printf("ippr_ftp_process: mlen %d\n", mlen); + + DT3(process_debug, tcphdr_t *, tcp, int, off, int, mlen); + if (softf->ipf_p_ftp_debug & DEBUG_INFO) + printf("ipf_p_ftp_process: %d:%d,%d, mlen %d flags %x\n", + fin->fin_out, fin->fin_sport, fin->fin_dport, + mlen, tcp->th_flags); if ((mlen == 0) && ((tcp->th_flags & TH_OPENING) == TH_OPENING)) { f->ftps_seq[0] = thseq + 1; @@ -1016,7 +1332,7 @@ int rv; sel = aps->aps_sel[1 - rv]; sel2 = aps->aps_sel[rv]; - if (rv == 0) { + if (rv == 1) { seqoff = aps->aps_seqoff[sel]; if (aps->aps_seqmin[sel] > seqoff + thseq) seqoff = aps->aps_seqoff[!sel]; @@ -1025,14 +1341,14 @@ int rv; ackoff = aps->aps_ackoff[!sel2]; } else { seqoff = aps->aps_ackoff[sel]; - if (ippr_ftp_debug > 2) + if (softf->ipf_p_ftp_debug & DEBUG_INFO) printf("seqoff %d thseq %x ackmin %x\n", seqoff, thseq, aps->aps_ackmin[sel]); if (aps->aps_ackmin[sel] > seqoff + thseq) seqoff = aps->aps_ackoff[!sel]; ackoff = aps->aps_seqoff[sel2]; - if (ippr_ftp_debug > 2) + if (softf->ipf_p_ftp_debug & DEBUG_INFO) printf("ackoff %d thack %x seqmin %x\n", ackoff, thack, aps->aps_seqmin[sel2]); if (ackoff > 0) { @@ -1043,7 +1359,7 @@ int rv; ackoff = aps->aps_seqoff[!sel2]; } } - if (ippr_ftp_debug > 2) { + if (softf->ipf_p_ftp_debug & DEBUG_INFO) { printf("%s: %x seq %x/%d ack %x/%d len %d/%d off %d\n", rv ? "IN" : "OUT", tcp->th_flags, thseq, seqoff, thack, ackoff, mlen, fin->fin_plen, off); @@ -1060,7 +1376,7 @@ int rv; * that it is out of order (and there is no real danger in doing so * apart from causing packets to go through here ordered). */ - if (ippr_ftp_debug > 2) { + if (softf->ipf_p_ftp_debug & DEBUG_INFO) { printf("rv %d t:seq[0] %x seq[1] %x %d/%d\n", rv, t->ftps_seq[0], t->ftps_seq[1], seqoff, ackoff); } @@ -1078,37 +1394,44 @@ int rv; ok = 1; } } else { - if (t->ftps_seq[0] + ackoff == thack) + if (t->ftps_seq[0] + ackoff == thack) { + t->ftps_seq[0] = thack; ok = 1; - else if (t->ftps_seq[0] == thack + ackoff) + } else if (t->ftps_seq[0] == thack + ackoff) { + t->ftps_seq[0] = thack + ackoff; ok = 1; - else if (t->ftps_seq[1] + ackoff == thack) { - t->ftps_seq[0] = thack - ackoff; + } else if (t->ftps_seq[1] + ackoff == thack) { + t->ftps_seq[0] = thack; ok = 1; } else if (t->ftps_seq[1] == thack + ackoff) { - t->ftps_seq[0] = thack - ackoff; + t->ftps_seq[0] = thack + ackoff; ok = 1; } } } - if (ippr_ftp_debug > 2) { + if (softf->ipf_p_ftp_debug & DEBUG_INFO) { if (!ok) printf("%s ok\n", "not"); } if (!mlen) { - if (t->ftps_seq[0] + ackoff != thack) { - if (ippr_ftp_debug > 1) { - printf("%s:seq[0](%x) + (%x) != (%x)\n", - "ippr_ftp_process", t->ftps_seq[0], + if (t->ftps_seq[0] + ackoff != thack && + t->ftps_seq[1] + ackoff != thack) { + DT3(thack, ftpside_t *t, t, int, ackoff, u_32_t, thack); + if (softf->ipf_p_ftp_debug & DEBUG_ERROR) { + printf("%s:seq[0](%u) + (%d) != (%u)\n", + "ipf_p_ftp_process", t->ftps_seq[0], + ackoff, thack); + printf("%s:seq[0](%u) + (%d) != (%u)\n", + "ipf_p_ftp_process", t->ftps_seq[1], ackoff, thack); } return APR_ERR(1); } - if (ippr_ftp_debug > 2) { - printf("ippr_ftp_process:f:seq[0] %x seq[1] %x\n", + if (softf->ipf_p_ftp_debug & DEBUG_PARSE) { + printf("ipf_p_ftp_process:f:seq[0] %x seq[1] %x\n", f->ftps_seq[0], f->ftps_seq[1]); } @@ -1117,9 +1440,10 @@ int rv; f->ftps_seq[0] = f->ftps_seq[1] - seqoff; f->ftps_seq[1] = thseq + 1 - seqoff; } else { - if (ippr_ftp_debug > 1) { - printf("FIN: thseq %x seqoff %d ftps_seq %x %x\n", - thseq, seqoff, f->ftps_seq[0], f->ftps_seq[1]); + DT2(thseq, ftpside_t *t, t, u_32_t, thseq); + if (softf->ipf_p_ftp_debug & DEBUG_ERROR) { + printf("FIN: thseq %x seqoff %d ftps_seq %x\n", + thseq, seqoff, f->ftps_seq[0]); } return APR_ERR(1); } @@ -1140,8 +1464,9 @@ int rv; } if (ok == 0) { + DT3(ok_0, ftpside_t *, f, u_32_t, thseq, int, mlen); inc = thseq - f->ftps_seq[0]; - if (ippr_ftp_debug > 1) { + if (softf->ipf_p_ftp_debug & DEBUG_ERROR) { printf("inc %d sel %d rv %d\n", inc, sel, rv); printf("th_seq %x ftps_seq %x/%x\n", thseq, f->ftps_seq[0], f->ftps_seq[1]); @@ -1163,68 +1488,69 @@ int rv; while (mlen > 0) { len = MIN(mlen, sizeof(f->ftps_buf) - (wptr - rptr)); + if (len == 0) + break; COPYDATA(m, off, len, wptr); mlen -= len; off += len; wptr += len; - if (ippr_ftp_debug > 3) +whilemore: + if (softf->ipf_p_ftp_debug & DEBUG_PARSE) printf("%s:len %d/%d off %d wptr %lx junk %d [%*.*s]\n", - "ippr_ftp_process", + "ipf_p_ftp_process", len, mlen, off, (u_long)wptr, f->ftps_junk, len, len, rptr); f->ftps_wptr = wptr; - if (f->ftps_junk != 0) { + if (f->ftps_junk != FTPXY_JUNK_OK) { i = f->ftps_junk; - f->ftps_junk = ippr_ftp_valid(ftp, rv, rptr, + f->ftps_junk = ipf_p_ftp_valid(softf, ftp, rv, rptr, wptr - rptr); + DT2(junk_transit, int, i, int, f->ftps_junk); - if (ippr_ftp_debug > 5) + if (softf->ipf_p_ftp_debug & DEBUG_PARSE) printf("%s:junk %d -> %d\n", - "ippr_ftp_process", i, f->ftps_junk); + "ipf_p_ftp_process", i, f->ftps_junk); - if (f->ftps_junk != 0) { + if (f->ftps_junk == FTPXY_JUNK_BAD) { + DT(buffer_full); if (wptr - rptr == sizeof(f->ftps_buf)) { - if (ippr_ftp_debug > 4) + if (softf->ipf_p_ftp_debug & + DEBUG_PARSE_INFO) printf("%s:full buffer\n", - "ippr_ftp_process"); + "ipf_p_ftp_process"); f->ftps_rptr = f->ftps_buf; f->ftps_wptr = f->ftps_buf; rptr = f->ftps_rptr; wptr = f->ftps_wptr; - /* - * Because we throw away data here that - * we would otherwise parse, set the - * junk flag to indicate just ignore - * any data upto the next CRLF. - */ - f->ftps_junk = 1; continue; } } } - while ((f->ftps_junk == 0) && (wptr > rptr)) { + while ((f->ftps_junk == FTPXY_JUNK_OK) && (wptr > rptr)) { len = wptr - rptr; - f->ftps_junk = ippr_ftp_valid(ftp, rv, rptr, len); + f->ftps_junk = ipf_p_ftp_valid(softf, ftp, rv, + rptr, len); - if (ippr_ftp_debug > 3) { + if (softf->ipf_p_ftp_debug & DEBUG_PARSE) { printf("%s=%d len %d rv %d ptr %lx/%lx ", - "ippr_ftp_valid", + "ipf_p_ftp_valid", f->ftps_junk, len, rv, (u_long)rptr, (u_long)wptr); printf("buf [%*.*s]\n", len, len, rptr); } - if (f->ftps_junk == 0) { + if (f->ftps_junk == FTPXY_JUNK_OK) { + f->ftps_cmds++; f->ftps_rptr = rptr; if (rv) - inc += ippr_ftp_server(fin, ip, nat, - ftp, len); + inc += ipf_p_ftp_server(softf, fin, ip, + nat, ftp, len); else - inc += ippr_ftp_client(fin, ip, nat, - ftp, len); + inc += ipf_p_ftp_client(softf, fin, ip, + nat, ftp, len); rptr = f->ftps_rptr; wptr = f->ftps_wptr; } @@ -1234,21 +1560,25 @@ int rv; * Off to a bad start so lets just forget about using the * ftp proxy for this connection. */ - if ((f->ftps_cmds == 0) && (f->ftps_junk == 1)) { + if ((f->ftps_cmds == 0) && (f->ftps_junk == FTPXY_JUNK_BAD)) { /* f->ftps_seq[1] += inc; */ - if (ippr_ftp_debug > 1) + DT(ftp_junk_cmd); + if (softf->ipf_p_ftp_debug & DEBUG_ERROR) printf("%s:cmds == 0 junk == 1\n", - "ippr_ftp_process"); + "ipf_p_ftp_process"); return APR_ERR(2); } - if ((f->ftps_junk != 0) && (rptr < wptr)) { + retry = 0; + if ((f->ftps_junk != FTPXY_JUNK_OK) && (rptr < wptr)) { for (s = rptr; s < wptr; s++) { if ((*s == '\r') && (s + 1 < wptr) && (*(s + 1) == '\n')) { rptr = s + 2; - f->ftps_junk = 0; + retry = 1; + if (f->ftps_junk != FTPXY_JUNK_CONT) + f->ftps_junk = FTPXY_JUNK_OK; break; } } @@ -1264,19 +1594,21 @@ int rv; * current state. */ if (rptr > f->ftps_buf) { - bcopy(rptr, f->ftps_buf, len); + bcopy(rptr, f->ftps_buf, wptr - rptr); wptr -= rptr - f->ftps_buf; rptr = f->ftps_buf; } } f->ftps_rptr = rptr; f->ftps_wptr = wptr; + if (retry) + goto whilemore; } /* f->ftps_seq[1] += inc; */ if (tcp->th_flags & TH_FIN) f->ftps_seq[1]++; - if (ippr_ftp_debug > 3) { + if (softf->ipf_p_ftp_debug & DEBUG_PARSE_INFO) { #ifdef __sgi mlen = fin->fin_plen; #else @@ -1293,11 +1625,14 @@ int rv; } -int ippr_ftp_out(fin, aps, nat) -fr_info_t *fin; -ap_session_t *aps; -nat_t *nat; +int +ipf_p_ftp_out(arg, fin, aps, nat) + void *arg; + fr_info_t *fin; + ap_session_t *aps; + nat_t *nat; { + ipf_ftp_softc_t *softf = arg; ftpinfo_t *ftp; int rev; @@ -1309,15 +1644,18 @@ nat_t *nat; if (ftp->ftp_side[1 - rev].ftps_ifp == NULL) ftp->ftp_side[1 - rev].ftps_ifp = fin->fin_ifp; - return ippr_ftp_process(fin, nat, ftp, rev); + return ipf_p_ftp_process(softf, fin, nat, ftp, rev); } -int ippr_ftp_in(fin, aps, nat) -fr_info_t *fin; -ap_session_t *aps; -nat_t *nat; +int +ipf_p_ftp_in(arg, fin, aps, nat) + void *arg; + fr_info_t *fin; + ap_session_t *aps; + nat_t *nat; { + ipf_ftp_softc_t *softf = arg; ftpinfo_t *ftp; int rev; @@ -1329,18 +1667,19 @@ nat_t *nat; if (ftp->ftp_side[rev].ftps_ifp == NULL) ftp->ftp_side[rev].ftps_ifp = fin->fin_ifp; - return ippr_ftp_process(fin, nat, ftp, 1 - rev); + return ipf_p_ftp_process(softf, fin, nat, ftp, 1 - rev); } /* - * ippr_ftp_atoi - implement a version of atoi which processes numbers in + * ipf_p_ftp_atoi - implement a version of atoi which processes numbers in * pairs separated by commas (which are expected to be in the range 0 - 255), * returning a 16 bit number combining either side of the , as the MSB and * LSB. */ -u_short ippr_ftp_atoi(ptr) -char **ptr; +u_short +ipf_p_ftp_atoi(ptr) + char **ptr; { register char *s = *ptr, c; register u_char i = 0, j = 0; @@ -1364,26 +1703,237 @@ char **ptr; } -int ippr_ftp_epsv(fin, ip, nat, f, dlen) -fr_info_t *fin; -ip_t *ip; -nat_t *nat; -ftpside_t *f; -int dlen; +int +ipf_p_ftp_eprt(softf, fin, ip, nat, ftp, dlen) + ipf_ftp_softc_t *softf; + fr_info_t *fin; + ip_t *ip; + nat_t *nat; + ftpinfo_t *ftp; + int dlen; +{ + ftpside_t *f; + + /* + * Check for client sending out EPRT message. + */ + if (dlen < IPF_MINEPRTLEN) { + DT1(epert_dlen, int, dlen); + if (softf->ipf_p_ftp_debug & DEBUG_PARSE_ERR) + printf("ipf_p_ftp_eprt:dlen(%d) < IPF_MINEPRTLEN\n", + dlen); + return 0; + } + + /* + * Parse the EPRT command. Format is: + * "EPRT |1|1.2.3.4|2000|" for IPv4 and + * "EPRT |2|ef00::1:2|2000|" for IPv6 + */ + f = &ftp->ftp_side[0]; + if (f->ftps_rptr[5] != '|') + return 0; + if (f->ftps_rptr[5] == f->ftps_rptr[7]) { + if (f->ftps_rptr[6] == '1' && nat->nat_v[0] == 4) + return ipf_p_ftp_eprt4(softf, fin, ip, nat, ftp, dlen); +#ifdef USE_INET6 + if (f->ftps_rptr[6] == '2' && nat->nat_v[0] == 6) + return ipf_p_ftp_eprt6(softf, fin, ip, nat, ftp, dlen); +#endif + } + return 0; +} + + +int +ipf_p_ftp_eprt4(softf, fin, ip, nat, ftp, dlen) + ipf_ftp_softc_t *softf; + fr_info_t *fin; + ip_t *ip; + nat_t *nat; + ftpinfo_t *ftp; + int dlen; +{ + int a1, a2, a3, a4, port, olen, nlen, inc, off; + char newbuf[IPF_FTPBUFSZ]; + char *s, c, delim; + u_32_t addr, i; + tcphdr_t *tcp; + ftpside_t *f; + mb_t *m; + + m = fin->fin_m; + tcp = (tcphdr_t *)fin->fin_dp; + off = (char *)tcp - (char *)ip + (TCP_OFF(tcp) << 2) + fin->fin_ipoff; + f = &ftp->ftp_side[0]; + delim = f->ftps_rptr[5]; + s = f->ftps_rptr + 8; + + /* + * get the IP address. + */ + i = 0; + while (((c = *s++) != '\0') && ISDIGIT(c)) { + i *= 10; + i += c - '0'; + } + if (i > 255) + return 0; + if (c != '.') + return 0; + addr = (i << 24); + + i = 0; + while (((c = *s++) != '\0') && ISDIGIT(c)) { + i *= 10; + i += c - '0'; + } + if (i > 255) + return 0; + if (c != '.') + return 0; + addr |= (addr << 16); + + i = 0; + while (((c = *s++) != '\0') && ISDIGIT(c)) { + i *= 10; + i += c - '0'; + } + if (i > 255) + return 0; + if (c != '.') + return 0; + addr |= (addr << 8); + + i = 0; + while (((c = *s++) != '\0') && ISDIGIT(c)) { + i *= 10; + i += c - '0'; + } + if (i > 255) + return 0; + if (c != delim) + return 0; + addr |= addr; + + /* + * Get the port number + */ + i = 0; + while (((c = *s++) != '\0') && ISDIGIT(c)) { + i *= 10; + i += c - '0'; + } + if (i > 65535) + return 0; + if (c != delim) + return 0; + port = i; + + /* + * Check for CR-LF at the end of the command string. + */ + if ((*s != '\r') || (*(s + 1) != '\n')) { + DT(eprt4_no_crlf); + if (softf->ipf_p_ftp_debug & DEBUG_PARSE_ERR) + printf("ipf_p_ftp_eprt4:missing %s\n", "cr-lf"); + return 0; + } + s += 2; + + /* + * Calculate new address parts for PORT command + */ + if (nat->nat_dir == NAT_INBOUND) + a1 = ntohl(nat->nat_odstaddr); + else + a1 = ntohl(ip->ip_src.s_addr); + a2 = (a1 >> 16) & 0xff; + a3 = (a1 >> 8) & 0xff; + a4 = a1 & 0xff; + a1 >>= 24; + olen = s - f->ftps_rptr; + /* DO NOT change this to snprintf! */ + /* + * While we could force the use of | as a delimiter here, it makes + * sense to preserve whatever character is being used by the systems + * involved in the communication. + */ +#if defined(SNPRINTF) && defined(_KERNEL) + SNPRINTF(newbuf, sizeof(newbuf), "%s %c1%c%u.%u.%u.%u%c%u%c\r\n", + "EPRT", delim, delim, a1, a2, a3, a4, delim, port, delim); +#else + (void) sprintf(newbuf, "%s %c1%c%u.%u.%u.%u%c%u%c\r\n", + "EPRT", delim, delim, a1, a2, a3, a4, delim, port, + delim); +#endif + + nlen = strlen(newbuf); + inc = nlen - olen; + if ((inc + fin->fin_plen) > 65535) { + DT2(eprt4_len, int, inc, int, fin->fin_plen); + if (softf->ipf_p_ftp_debug & DEBUG_ERROR) + printf("ipf_p_ftp_eprt4:inc(%d) + ip->ip_len > 65535\n", + inc); + return 0; + } + + off = (char *)tcp - (char *)ip + (TCP_OFF(tcp) << 2) + fin->fin_ipoff; +#if !defined(_KERNEL) + M_ADJ(m, inc); +#else + if (inc < 0) + M_ADJ(m, inc); +#endif + /* the mbuf chain will be extended if necessary by m_copyback() */ + COPYBACK(m, off, nlen, newbuf); + fin->fin_flx |= FI_DOCKSUM; + + if (inc != 0) { + fin->fin_plen += inc; + ip->ip_len = htons(fin->fin_plen); + fin->fin_dlen += inc; + } + + f->ftps_cmd = FTPXY_C_EPRT; + return ipf_p_ftp_addport(softf, fin, ip, nat, ftp, dlen, port, inc); +} + + +int +ipf_p_ftp_epsv(softf, fin, ip, nat, ftp, dlen) + ipf_ftp_softc_t *softf; + fr_info_t *fin; + ip_t *ip; + nat_t *nat; + ftpinfo_t *ftp; + int dlen; { char newbuf[IPF_FTPBUFSZ]; - char *s; u_short ap = 0; + ftpside_t *f; + char *s; + + if ((softf->ipf_p_ftp_forcepasv != 0) && + (ftp->ftp_side[0].ftps_cmd != FTPXY_C_EPSV)) { + DT1(epsv_cmd, int, ftp->ftp_side[0].ftps_cmd); + if (softf->ipf_p_ftp_debug & DEBUG_PARSE_ERR) + printf("ipf_p_ftp_epsv:ftps_cmd(%d) != FTPXY_C_EPSV\n", + ftp->ftp_side[0].ftps_cmd); + return 0; + } + f = &ftp->ftp_side[1]; #define EPSV_REPLEN 33 /* * Check for EPSV reply message. */ - if (dlen < IPF_MIN229LEN) + if (dlen < IPF_MIN229LEN) { return (0); - else if (strncmp(f->ftps_rptr, - "229 Entering Extended Passive Mode", EPSV_REPLEN)) + } else if (strncmp(f->ftps_rptr, + "229 Entering Extended Passive Mode", EPSV_REPLEN)) { return (0); +} /* * Skip the EPSV command + space @@ -1401,8 +1951,9 @@ int dlen; ap += *s++ - '0'; } - if (!*s) + if (!s) { return 0; +} if (*s == '|') s++; @@ -1413,10 +1964,10 @@ int dlen; /* * check for CR-LF at the end. */ - if ((*s == '\r') && (*(s + 1) == '\n')) { - s += 2; - } else + if ((*s != '\r') || (*(s + 1) != '\n')) { return 0; + } + s += 2; #if defined(SNPRINTF) && defined(_KERNEL) SNPRINTF(newbuf, sizeof(newbuf), "%s (|||%u|)\r\n", @@ -1426,6 +1977,218 @@ int dlen; "229 Entering Extended Passive Mode", ap); #endif - return ippr_ftp_pasvreply(fin, ip, nat, f, (u_int)ap, newbuf, s, - ip->ip_src.s_addr); + return ipf_p_ftp_pasvreply(softf, fin, ip, nat, ftp, (u_int)ap, + newbuf, s); } + +#ifdef USE_INET6 +int +ipf_p_ftp_eprt6(softf, fin, ip, nat, ftp, dlen) + ipf_ftp_softc_t *softf; + fr_info_t *fin; + ip_t *ip; + nat_t *nat; + ftpinfo_t *ftp; + int dlen; +{ + int port, olen, nlen, inc, off, left, i; + char newbuf[IPF_FTPBUFSZ]; + char *s, c; + i6addr_t addr, *a6; + tcphdr_t *tcp; + ip6_t *ip6; + char delim; + u_short whole; + u_short part; + ftpside_t *f; + u_short *t; + int fwd; + mb_t *m; + u_32_t a; + + m = fin->fin_m; + ip6 = (ip6_t *)ip; + f = &ftp->ftp_side[0]; + s = f->ftps_rptr + 8; + f = &ftp->ftp_side[0]; + delim = f->ftps_rptr[5]; + tcp = (tcphdr_t *)fin->fin_dp; + off = (char *)tcp - (char *)ip + (TCP_OFF(tcp) << 2) + fin->fin_ipoff; + + addr.i6[0] = 0; + addr.i6[1] = 0; + addr.i6[2] = 0; + addr.i6[3] = 0; + /* + * Parse an IPv6 address. + * Go forward until either :: or | is found. If :: is found, + * reverse direction. Direction change is performed to ease + * parsing an unknown number of 0s in the middle. + */ + whole = 0; + t = (u_short *)&addr; + fwd = 1; + for (part = 0; (c = *s) != '\0'; ) { + if (c == delim) { + *t = htons((u_short)whole); + break; + } + if (c == ':') { + *t = part; + if (fwd) { + *t = htons((u_short)whole); + t++; + } else { + *t = htons((u_short)(whole >> 16)); + t--; + } + whole = 0; + if (fwd == 1 && s[1] == ':') { + while (*s && *s != '|') + s++; + if ((c = *s) != delim) + break; + t = (u_short *)&addr.i6[3]; + t++; + fwd = 0; + } else if (fwd == 0 && s[-1] == ':') { + break; + } + } else { + if (c >= '0' && c <= '9') { + c -= '0'; + } else if (c >= 'a' && c <= 'f') { + c -= 'a' + 10; + } else if (c >= 'A' && c <= 'F') { + c -= 'A' + 10; + } + if (fwd) { + whole <<= 8; + whole |= c; + } else { + whole >>= 8; + whole |= ((u_32_t)c) << 24; + } + } + if (fwd) + s++; + else + s--; + } + if (c != ':' && c != delim) + return 0; + + while (*s != '|') + s++; + s++; + + /* + * Get the port number + */ + i = 0; + while (((c = *s++) != '\0') && ISDIGIT(c)) { + i *= 10; + i += c - '0'; + } + if (i > 65535) + return 0; + if (c != delim) + return 0; + port = (u_short)(i & 0xffff); + + /* + * Check for CR-LF at the end of the command string. + */ + if ((*s != '\r') || (*(s + 1) != '\n')) { + DT(eprt6_no_crlf); + if (softf->ipf_p_ftp_debug & DEBUG_PARSE_ERR) + printf("ipf_p_ftp_eprt6:missing %s\n", "cr-lf"); + return 0; + } + s += 2; + + /* + * Calculate new address parts for PORT command + */ + a6 = (i6addr_t *)&ip6->ip6_src; + olen = s - f->ftps_rptr; + /* DO NOT change this to snprintf! */ + /* + * While we could force the use of | as a delimiter here, it makes + * sense to preserve whatever character is being used by the systems + * involved in the communication. + */ + s = newbuf; + left = sizeof(newbuf); +#if defined(SNPRINTF) && defined(_KERNEL) + SNPRINTF(newbuf, left, "EPRT %c2%c", delim, delim); + left -= strlen(s) + 1; + s += strlen(s); + a = ntohl(a6->i6[0]); + SNPRINTF(s, left, "%x:%x:", a >> 16, a & 0xffff); + left -= strlen(s); + s += strlen(s); + a = ntohl(a6->i6[1]); + SNPRINTF(s, left, "%x:%x:", a >> 16, a & 0xffff); + left -= strlen(s); + s += strlen(s); + a = ntohl(a6->i6[2]); + SNPRINTF(s, left, "%x:%x:", a >> 16, a & 0xffff); + left -= strlen(s); + s += strlen(s); + a = ntohl(a6->i6[3]); + SNPRINTF(s, left, "%x:%x", a >> 16, a & 0xffff); + left -= strlen(s); + s += strlen(s); + sprintf(s, "|%d|\r\n", port); +#else + (void) sprintf(s, "EPRT %c2%c", delim, delim); + s += strlen(s); + a = ntohl(a6->i6[0]); + sprintf(s, "%x:%x:", a >> 16, a & 0xffff); + s += strlen(s); + a = ntohl(a6->i6[1]); + sprintf(s, "%x:%x:", a >> 16, a & 0xffff); + left -= strlen(s); + s += strlen(s); + a = ntohl(a6->i6[2]); + sprintf(s, "%x:%x:", a >> 16, a & 0xffff); + left -= strlen(s); + s += strlen(s); + a = ntohl(a6->i6[3]); + sprintf(s, "%x:%x", a >> 16, a & 0xffff); + left -= strlen(s); + s += strlen(s); + sprintf(s, "|%d|\r\n", port); +#endif + nlen = strlen(newbuf); + inc = nlen - olen; + if ((inc + fin->fin_plen) > 65535) { + DT2(eprt6_len, int, inc, int, fin->fin_plen); + if (softf->ipf_p_ftp_debug & DEBUG_ERROR) + printf("ipf_p_ftp_eprt6:inc(%d) + ip->ip_len > 65535\n", + inc); + return 0; + } + + off = (char *)tcp - (char *)ip + (TCP_OFF(tcp) << 2) + fin->fin_ipoff; +#if !defined(_KERNEL) + M_ADJ(m, inc); +#else + if (inc < 0) + M_ADJ(m, inc); +#endif + /* the mbuf chain will be extended if necessary by m_copyback() */ + COPYBACK(m, off, nlen, newbuf); + fin->fin_flx |= FI_DOCKSUM; + + if (inc != 0) { + fin->fin_plen += inc; + ip6->ip6_plen = htons(fin->fin_plen - fin->fin_hlen); + fin->fin_dlen += inc; + } + + f->ftps_cmd = FTPXY_C_EPRT; + return ipf_p_ftp_addport(softf, fin, ip, nat, ftp, dlen, port, inc); +} +#endif diff --git a/sys/contrib/ipfilter/netinet/ip_htable.c b/sys/contrib/ipfilter/netinet/ip_htable.c index fc521d851a3..62707f40edd 100644 --- a/sys/contrib/ipfilter/netinet/ip_htable.c +++ b/sys/contrib/ipfilter/netinet/ip_htable.c @@ -1,7 +1,7 @@ /* $FreeBSD$ */ /* - * Copyright (C) 1993-2001, 2003 by Darren Reed. + * Copyright (C) 2012 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. */ @@ -41,7 +41,7 @@ struct file; #if defined(_KERNEL) # include #else -# include +# include "ipf.h" #endif #include #include @@ -53,82 +53,277 @@ struct file; /* END OF INCLUDES */ #if !defined(lint) -static const char rcsid[] = "@(#)$Id: ip_htable.c,v 2.34.2.11 2007/09/20 12:51:51 darrenr Exp $"; +static const char rcsid[] = "@(#)$Id$"; #endif -#ifdef IPFILTER_LOOKUP -static iphtent_t *fr_iphmfind __P((iphtable_t *, struct in_addr *)); -static u_long ipht_nomem[IPL_LOGSIZE] = { 0, 0, 0, 0, 0, 0, 0, 0 }; -static u_long ipf_nhtables[IPL_LOGSIZE] = { 0, 0, 0, 0, 0, 0, 0, 0 }; -static u_long ipf_nhtnodes[IPL_LOGSIZE] = { 0, 0, 0, 0, 0, 0, 0, 0 }; - -iphtable_t *ipf_htables[IPL_LOGSIZE] = { NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL }; +# ifdef USE_INET6 +static iphtent_t *ipf_iphmfind6 __P((iphtable_t *, i6addr_t *)); +# endif +static iphtent_t *ipf_iphmfind __P((iphtable_t *, struct in_addr *)); +static int ipf_iphmfindip __P((ipf_main_softc_t *, void *, int, void *, u_int)); +static int ipf_htable_clear __P((ipf_main_softc_t *, void *, iphtable_t *)); +static int ipf_htable_create __P((ipf_main_softc_t *, void *, iplookupop_t *)); +static int ipf_htable_deref __P((ipf_main_softc_t *, void *, void *)); +static int ipf_htable_destroy __P((ipf_main_softc_t *, void *, int, char *)); +static void *ipf_htable_exists __P((void *, int, char *)); +static size_t ipf_htable_flush __P((ipf_main_softc_t *, void *, + iplookupflush_t *)); +static void ipf_htable_free __P((void *, iphtable_t *)); +static int ipf_htable_iter_deref __P((ipf_main_softc_t *, void *, int, + int, void *)); +static int ipf_htable_iter_next __P((ipf_main_softc_t *, void *, ipftoken_t *, + ipflookupiter_t *)); +static int ipf_htable_node_add __P((ipf_main_softc_t *, void *, + iplookupop_t *, int)); +static int ipf_htable_node_del __P((ipf_main_softc_t *, void *, + iplookupop_t *, int)); +static int ipf_htable_remove __P((ipf_main_softc_t *, void *, iphtable_t *)); +static void *ipf_htable_soft_create __P((ipf_main_softc_t *)); +static void ipf_htable_soft_destroy __P((ipf_main_softc_t *, void *)); +static int ipf_htable_soft_init __P((ipf_main_softc_t *, void *)); +static void ipf_htable_soft_fini __P((ipf_main_softc_t *, void *)); +static int ipf_htable_stats_get __P((ipf_main_softc_t *, void *, + iplookupop_t *)); +static int ipf_htable_table_add __P((ipf_main_softc_t *, void *, + iplookupop_t *)); +static int ipf_htable_table_del __P((ipf_main_softc_t *, void *, + iplookupop_t *)); +static int ipf_htent_deref __P((void *, iphtent_t *)); +static iphtent_t *ipf_htent_find __P((iphtable_t *, iphtent_t *)); +static int ipf_htent_insert __P((ipf_main_softc_t *, void *, iphtable_t *, + iphtent_t *)); +static int ipf_htent_remove __P((ipf_main_softc_t *, void *, iphtable_t *, + iphtent_t *)); +static void *ipf_htable_select_add_ref __P((void *, int, char *)); +static void ipf_htable_expire __P((ipf_main_softc_t *, void *)); -void fr_htable_unload() +typedef struct ipf_htable_softc_s { + u_long ipht_nomem[LOOKUP_POOL_SZ]; + u_long ipf_nhtables[LOOKUP_POOL_SZ]; + u_long ipf_nhtnodes[LOOKUP_POOL_SZ]; + iphtable_t *ipf_htables[LOOKUP_POOL_SZ]; + iphtent_t *ipf_node_explist; +} ipf_htable_softc_t; + +ipf_lookup_t ipf_htable_backend = { + IPLT_HASH, + ipf_htable_soft_create, + ipf_htable_soft_destroy, + ipf_htable_soft_init, + ipf_htable_soft_fini, + ipf_iphmfindip, + ipf_htable_flush, + ipf_htable_iter_deref, + ipf_htable_iter_next, + ipf_htable_node_add, + ipf_htable_node_del, + ipf_htable_stats_get, + ipf_htable_table_add, + ipf_htable_table_del, + ipf_htable_deref, + ipf_htable_exists, + ipf_htable_select_add_ref, + NULL, + ipf_htable_expire, + NULL +}; + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_htable_soft_create */ +/* Returns: void * - NULL = failure, else pointer to local context */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* */ +/* Initialise the routing table data structures where required. */ +/* ------------------------------------------------------------------------ */ +static void * +ipf_htable_soft_create(softc) + ipf_main_softc_t *softc; +{ + ipf_htable_softc_t *softh; + + KMALLOC(softh, ipf_htable_softc_t *); + if (softh == NULL) { + IPFERROR(30026); + return NULL; + } + + bzero((char *)softh, sizeof(*softh)); + + return softh; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_htable_soft_destroy */ +/* Returns: Nil */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* arg(I) - pointer to local context to use */ +/* */ +/* Clean up the pool by free'ing the radix tree associated with it and free */ +/* up the pool context too. */ +/* ------------------------------------------------------------------------ */ +static void +ipf_htable_soft_destroy(softc, arg) + ipf_main_softc_t *softc; + void *arg; +{ + ipf_htable_softc_t *softh = arg; + + KFREE(softh); +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_htable_soft_init */ +/* Returns: int - 0 = success, else error */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* arg(I) - pointer to local context to use */ +/* */ +/* Initialise the hash table ready for use. */ +/* ------------------------------------------------------------------------ */ +static int +ipf_htable_soft_init(softc, arg) + ipf_main_softc_t *softc; + void *arg; +{ + ipf_htable_softc_t *softh = arg; + + bzero((char *)softh, sizeof(*softh)); + + return 0; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_htable_soft_fini */ +/* Returns: Nil */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* arg(I) - pointer to local context to use */ +/* Locks: WRITE(ipf_global) */ +/* */ +/* Clean up all the pool data structures allocated and call the cleanup */ +/* function for the radix tree that supports the pools. ipf_pool_destroy is */ +/* used to delete the pools one by one to ensure they're properly freed up. */ +/* ------------------------------------------------------------------------ */ +static void +ipf_htable_soft_fini(softc, arg) + ipf_main_softc_t *softc; + void *arg; { iplookupflush_t fop; + fop.iplf_type = IPLT_HASH; fop.iplf_unit = IPL_LOGALL; - (void)fr_flushhtable(&fop); + fop.iplf_arg = 0; + fop.iplf_count = 0; + *fop.iplf_name = '\0'; + ipf_htable_flush(softc, arg, &fop); } -int fr_gethtablestat(op) -iplookupop_t *op; +/* ------------------------------------------------------------------------ */ +/* Function: ipf_htable_stats_get */ +/* Returns: int - 0 = success, else error */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* arg(I) - pointer to local context to use */ +/* op(I) - pointer to lookup operation data */ +/* */ +/* Copy the relevant statistics out of internal structures and into the */ +/* structure used to export statistics. */ +/* ------------------------------------------------------------------------ */ +static int +ipf_htable_stats_get(softc, arg, op) + ipf_main_softc_t *softc; + void *arg; + iplookupop_t *op; { + ipf_htable_softc_t *softh = arg; iphtstat_t stats; + int err; - if (op->iplo_size != sizeof(stats)) + if (op->iplo_size != sizeof(stats)) { + IPFERROR(30001); return EINVAL; + } - stats.iphs_tables = ipf_htables[op->iplo_unit]; - stats.iphs_numtables = ipf_nhtables[op->iplo_unit]; - stats.iphs_numnodes = ipf_nhtnodes[op->iplo_unit]; - stats.iphs_nomem = ipht_nomem[op->iplo_unit]; + stats.iphs_tables = softh->ipf_htables[op->iplo_unit + 1]; + stats.iphs_numtables = softh->ipf_nhtables[op->iplo_unit + 1]; + stats.iphs_numnodes = softh->ipf_nhtnodes[op->iplo_unit + 1]; + stats.iphs_nomem = softh->ipht_nomem[op->iplo_unit + 1]; - return COPYOUT(&stats, op->iplo_struct, sizeof(stats)); + err = COPYOUT(&stats, op->iplo_struct, sizeof(stats)); + if (err != 0) { + IPFERROR(30013); + return EFAULT; + } + return 0; } -/* - * Create a new hash table using the template passed. - */ -int fr_newhtable(op) -iplookupop_t *op; +/* ------------------------------------------------------------------------ */ +/* Function: ipf_htable_create */ +/* Returns: int - 0 = success, else error */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* arg(I) - pointer to local context to use */ +/* op(I) - pointer to lookup operation data */ +/* */ +/* Create a new hash table using the template passed. */ +/* ------------------------------------------------------------------------ */ +static int +ipf_htable_create(softc, arg, op) + ipf_main_softc_t *softc; + void *arg; + iplookupop_t *op; { - iphtable_t *iph, *oiph; + ipf_htable_softc_t *softh = arg; + iphtable_t htab, *iph, *oiph; char name[FR_GROUPLEN]; int err, i, unit; + if (op->iplo_size != sizeof(htab)) { + IPFERROR(30024); + return EINVAL; + } + err = COPYIN(op->iplo_struct, &htab, sizeof(htab)); + if (err != 0) { + IPFERROR(30003); + return EFAULT; + } + unit = op->iplo_unit; + if (htab.iph_unit != unit) { + IPFERROR(30005); + return EINVAL; + } + if (htab.iph_size < 1) { + IPFERROR(30025); + return EINVAL; + } + + if ((op->iplo_arg & IPHASH_ANON) == 0) { - iph = fr_existshtable(unit, op->iplo_name); + iph = ipf_htable_exists(softh, unit, op->iplo_name); if (iph != NULL) { - if ((iph->iph_flags & IPHASH_DELETE) == 0) + if ((iph->iph_flags & IPHASH_DELETE) == 0) { + IPFERROR(30004); return EEXIST; + } iph->iph_flags &= ~IPHASH_DELETE; + iph->iph_ref++; return 0; } } KMALLOC(iph, iphtable_t *); if (iph == NULL) { - ipht_nomem[op->iplo_unit]++; + softh->ipht_nomem[op->iplo_unit + 1]++; + IPFERROR(30002); return ENOMEM; } - err = COPYIN(op->iplo_struct, iph, sizeof(*iph)); - if (err != 0) { - KFREE(iph); - return EFAULT; - } - - if (iph->iph_unit != unit) { - KFREE(iph); - return EINVAL; - } + *iph = htab; if ((op->iplo_arg & IPHASH_ANON) != 0) { i = IPHASH_ANON; @@ -139,7 +334,7 @@ iplookupop_t *op; #else (void)sprintf(name, "%u", i); #endif - for (oiph = ipf_htables[unit]; oiph != NULL; + for (oiph = softh->ipf_htables[unit + 1]; oiph != NULL; oiph = oiph->iph_next) if (strncmp(oiph->iph_name, name, sizeof(oiph->iph_name)) == 0) @@ -149,113 +344,316 @@ iplookupop_t *op; (void)strncpy(iph->iph_name, name, sizeof(iph->iph_name)); (void)strncpy(op->iplo_name, name, sizeof(op->iplo_name)); iph->iph_type |= IPHASH_ANON; + } else { + (void)strncpy(iph->iph_name, op->iplo_name, + sizeof(iph->iph_name)); + iph->iph_name[sizeof(iph->iph_name) - 1] = '\0'; } KMALLOCS(iph->iph_table, iphtent_t **, iph->iph_size * sizeof(*iph->iph_table)); if (iph->iph_table == NULL) { KFREE(iph); - ipht_nomem[unit]++; + softh->ipht_nomem[unit + 1]++; + IPFERROR(30006); return ENOMEM; } bzero((char *)iph->iph_table, iph->iph_size * sizeof(*iph->iph_table)); - iph->iph_masks = 0; - iph->iph_list = NULL; + iph->iph_maskset[0] = 0; + iph->iph_maskset[1] = 0; + iph->iph_maskset[2] = 0; + iph->iph_maskset[3] = 0; iph->iph_ref = 1; - iph->iph_next = ipf_htables[unit]; - iph->iph_pnext = &ipf_htables[unit]; - if (ipf_htables[unit] != NULL) - ipf_htables[unit]->iph_pnext = &iph->iph_next; - ipf_htables[unit] = iph; - ipf_nhtables[unit]++; + iph->iph_list = NULL; + iph->iph_tail = &iph->iph_list; + iph->iph_next = softh->ipf_htables[unit + 1]; + iph->iph_pnext = &softh->ipf_htables[unit + 1]; + if (softh->ipf_htables[unit + 1] != NULL) + softh->ipf_htables[unit + 1]->iph_pnext = &iph->iph_next; + softh->ipf_htables[unit + 1] = iph; + + softh->ipf_nhtables[unit + 1]++; return 0; } -/* - */ -int fr_removehtable(unit, name) -int unit; -char *name; +/* ------------------------------------------------------------------------ */ +/* Function: ipf_htable_table_del */ +/* Returns: int - 0 = success, else error */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* arg(I) - pointer to local context to use */ +/* op(I) - pointer to lookup operation data */ +/* */ +/* ------------------------------------------------------------------------ */ +static int +ipf_htable_table_del(softc, arg, op) + ipf_main_softc_t *softc; + void *arg; + iplookupop_t *op; +{ + return ipf_htable_destroy(softc, arg, op->iplo_unit, op->iplo_name); +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_htable_destroy */ +/* Returns: int - 0 = success, else error */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* arg(I) - pointer to local context to use */ +/* op(I) - pointer to lookup operation data */ +/* */ +/* Find the hash table that belongs to the relevant part of ipfilter with a */ +/* matching name and attempt to destroy it. If it is in use, empty it out */ +/* and mark it for deletion so that when all the references disappear, it */ +/* can be removed. */ +/* ------------------------------------------------------------------------ */ +static int +ipf_htable_destroy(softc, arg, unit, name) + ipf_main_softc_t *softc; + void *arg; + int unit; + char *name; { iphtable_t *iph; - iph = fr_findhtable(unit, name); - if (iph == NULL) + iph = ipf_htable_find(arg, unit, name); + if (iph == NULL) { + IPFERROR(30007); return ESRCH; + } if (iph->iph_unit != unit) { + IPFERROR(30008); return EINVAL; } if (iph->iph_ref != 0) { - (void) fr_clearhtable(iph); + ipf_htable_clear(softc, arg, iph); iph->iph_flags |= IPHASH_DELETE; return 0; } - fr_delhtable(iph); + ipf_htable_remove(softc, arg, iph); return 0; } -int fr_clearhtable(iph) -iphtable_t *iph; +/* ------------------------------------------------------------------------ */ +/* Function: ipf_htable_clear */ +/* Returns: int - 0 = success, else error */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* arg(I) - pointer to local context to use */ +/* iph(I) - pointer to hash table to destroy */ +/* */ +/* Clean out the hash table by walking the list of entries and removing */ +/* each one, one by one. */ +/* ------------------------------------------------------------------------ */ +static int +ipf_htable_clear(softc, arg, iph) + ipf_main_softc_t *softc; + void *arg; + iphtable_t *iph; { iphtent_t *ipe; while ((ipe = iph->iph_list) != NULL) - if (fr_delhtent(iph, ipe) != 0) + if (ipf_htent_remove(softc, arg, iph, ipe) != 0) return 1; return 0; } -int fr_delhtable(iph) -iphtable_t *iph; +/* ------------------------------------------------------------------------ */ +/* Function: ipf_htable_free */ +/* Returns: Nil */ +/* Parameters: arg(I) - pointer to local context to use */ +/* iph(I) - pointer to hash table to destroy */ +/* */ +/* ------------------------------------------------------------------------ */ +static void +ipf_htable_free(arg, iph) + void *arg; + iphtable_t *iph; +{ + ipf_htable_softc_t *softh = arg; + + if (iph->iph_next != NULL) + iph->iph_next->iph_pnext = iph->iph_pnext; + if (iph->iph_pnext != NULL) + *iph->iph_pnext = iph->iph_next; + iph->iph_pnext = NULL; + iph->iph_next = NULL; + + softh->ipf_nhtables[iph->iph_unit + 1]--; + + KFREES(iph->iph_table, iph->iph_size * sizeof(*iph->iph_table)); + KFREE(iph); +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_htable_remove */ +/* Returns: int - 0 = success, else error */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* arg(I) - pointer to local context to use */ +/* iph(I) - pointer to hash table to destroy */ +/* */ +/* It is necessary to unlink here as well as free (called by deref) so that */ +/* the while loop in ipf_htable_flush() functions properly. */ +/* ------------------------------------------------------------------------ */ +static int +ipf_htable_remove(softc, arg, iph) + ipf_main_softc_t *softc; + void *arg; + iphtable_t *iph; { - if (fr_clearhtable(iph) != 0) + if (ipf_htable_clear(softc, arg, iph) != 0) return 1; if (iph->iph_pnext != NULL) *iph->iph_pnext = iph->iph_next; if (iph->iph_next != NULL) iph->iph_next->iph_pnext = iph->iph_pnext; + iph->iph_pnext = NULL; + iph->iph_next = NULL; - ipf_nhtables[iph->iph_unit]--; - - return fr_derefhtable(iph); + return ipf_htable_deref(softc, arg, iph); } -/* - * Delete an entry from a hash table. - */ -int fr_delhtent(iph, ipe) -iphtable_t *iph; -iphtent_t *ipe; +/* ------------------------------------------------------------------------ */ +/* Function: ipf_htable_node_del */ +/* Returns: int - 0 = success, else error */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* arg(I) - pointer to local context to use */ +/* op(I) - pointer to lookup operation data */ +/* uid(I) - real uid of process doing operation */ +/* */ +/* ------------------------------------------------------------------------ */ +static int +ipf_htable_node_del(softc, arg, op, uid) + ipf_main_softc_t *softc; + void *arg; + iplookupop_t *op; + int uid; +{ + iphtable_t *iph; + iphtent_t hte, *ent; + int err; + + if (op->iplo_size != sizeof(hte)) { + IPFERROR(30014); + return EINVAL; + } + + err = COPYIN(op->iplo_struct, &hte, sizeof(hte)); + if (err != 0) { + IPFERROR(30015); + return EFAULT; + } + + iph = ipf_htable_find(arg, op->iplo_unit, op->iplo_name); + if (iph == NULL) { + IPFERROR(30016); + return ESRCH; + } + + ent = ipf_htent_find(iph, &hte); + if (ent == NULL) { + IPFERROR(30022); + return ESRCH; + } + + if ((uid != 0) && (ent->ipe_uid != uid)) { + IPFERROR(30023); + return EACCES; + } + + err = ipf_htent_remove(softc, arg, iph, ent); + + return err; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_htable_node_del */ +/* Returns: int - 0 = success, else error */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* arg(I) - pointer to local context to use */ +/* op(I) - pointer to lookup operation data */ +/* */ +/* ------------------------------------------------------------------------ */ +static int +ipf_htable_table_add(softc, arg, op) + ipf_main_softc_t *softc; + void *arg; + iplookupop_t *op; +{ + int err; + + if (ipf_htable_find(arg, op->iplo_unit, op->iplo_name) != NULL) { + IPFERROR(30017); + err = EEXIST; + } else { + err = ipf_htable_create(softc, arg, op); + } + + return err; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_htent_remove */ +/* Returns: int - 0 = success, else error */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* arg(I) - pointer to local context to use */ +/* iph(I) - pointer to hash table */ +/* ipe(I) - pointer to hash table entry to remove */ +/* */ +/* Delete an entry from a hash table. */ +/* ------------------------------------------------------------------------ */ +static int +ipf_htent_remove(softc, arg, iph, ipe) + ipf_main_softc_t *softc; + void *arg; + iphtable_t *iph; + iphtent_t *ipe; { - if (ipe->ipe_phnext != NULL) - *ipe->ipe_phnext = ipe->ipe_hnext; + if (iph->iph_tail == &ipe->ipe_next) + iph->iph_tail = ipe->ipe_pnext; + if (ipe->ipe_hnext != NULL) ipe->ipe_hnext->ipe_phnext = ipe->ipe_phnext; + if (ipe->ipe_phnext != NULL) + *ipe->ipe_phnext = ipe->ipe_hnext; + ipe->ipe_phnext = NULL; + ipe->ipe_hnext = NULL; + + if (ipe->ipe_dnext != NULL) + ipe->ipe_dnext->ipe_pdnext = ipe->ipe_pdnext; + if (ipe->ipe_pdnext != NULL) + *ipe->ipe_pdnext = ipe->ipe_dnext; + ipe->ipe_pdnext = NULL; + ipe->ipe_dnext = NULL; - if (ipe->ipe_pnext != NULL) - *ipe->ipe_pnext = ipe->ipe_next; if (ipe->ipe_next != NULL) ipe->ipe_next->ipe_pnext = ipe->ipe_pnext; + if (ipe->ipe_pnext != NULL) + *ipe->ipe_pnext = ipe->ipe_next; + ipe->ipe_pnext = NULL; + ipe->ipe_next = NULL; switch (iph->iph_type & ~IPHASH_ANON) { case IPHASH_GROUPMAP : if (ipe->ipe_group != NULL) - fr_delgroup(ipe->ipe_group, IPL_LOGIPF, fr_active); + ipf_group_del(softc, ipe->ipe_ptr, NULL); break; default : @@ -264,35 +662,54 @@ iphtent_t *ipe; break; } - return fr_derefhtent(ipe); + return ipf_htent_deref(arg, ipe); } -int fr_derefhtable(iph) -iphtable_t *iph; +/* ------------------------------------------------------------------------ */ +/* Function: ipf_htable_deref */ +/* Returns: int - 0 = success, else error */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* arg(I) - pointer to local context to use */ +/* object(I) - pointer to hash table */ +/* */ +/* ------------------------------------------------------------------------ */ +static int +ipf_htable_deref(softc, arg, object) + ipf_main_softc_t *softc; + void *arg, *object; { + ipf_htable_softc_t *softh = arg; + iphtable_t *iph = object; int refs; iph->iph_ref--; refs = iph->iph_ref; if (iph->iph_ref == 0) { - KFREES(iph->iph_table, iph->iph_size * sizeof(*iph->iph_table)); - KFREE(iph); + ipf_htable_free(softh, iph); } return refs; } -int fr_derefhtent(ipe) -iphtent_t *ipe; +/* ------------------------------------------------------------------------ */ +/* Function: ipf_htent_deref */ +/* Parameters: arg(I) - pointer to local context to use */ +/* ipe(I) - */ +/* */ +/* ------------------------------------------------------------------------ */ +static int +ipf_htent_deref(arg, ipe) + void *arg; + iphtent_t *ipe; { + ipf_htable_softc_t *softh = arg; ipe->ipe_ref--; if (ipe->ipe_ref == 0) { - ipf_nhtnodes[ipe->ipe_unit]--; - + softh->ipf_nhtnodes[ipe->ipe_unit + 1]--; KFREE(ipe); return 0; @@ -302,26 +719,87 @@ iphtent_t *ipe; } -iphtable_t *fr_existshtable(unit, name) -int unit; -char *name; +/* ------------------------------------------------------------------------ */ +/* Function: ipf_htable_exists */ +/* Parameters: arg(I) - pointer to local context to use */ +/* */ +/* ------------------------------------------------------------------------ */ +static void * +ipf_htable_exists(arg, unit, name) + void *arg; + int unit; + char *name; { + ipf_htable_softc_t *softh = arg; iphtable_t *iph; - for (iph = ipf_htables[unit]; iph != NULL; iph = iph->iph_next) - if (strncmp(iph->iph_name, name, sizeof(iph->iph_name)) == 0) - break; + if (unit == IPL_LOGALL) { + int i; + + for (i = 0; i <= LOOKUP_POOL_MAX; i++) { + for (iph = softh->ipf_htables[i]; iph != NULL; + iph = iph->iph_next) { + if (strncmp(iph->iph_name, name, + sizeof(iph->iph_name)) == 0) + break; + } + if (iph != NULL) + break; + } + } else { + for (iph = softh->ipf_htables[unit + 1]; iph != NULL; + iph = iph->iph_next) { + if (strncmp(iph->iph_name, name, + sizeof(iph->iph_name)) == 0) + break; + } + } return iph; } -iphtable_t *fr_findhtable(unit, name) -int unit; -char *name; +/* ------------------------------------------------------------------------ */ +/* Function: ipf_htable_select_add_ref */ +/* Returns: void * - NULL = failure, else pointer to the hash table */ +/* Parameters: arg(I) - pointer to local context to use */ +/* unit(I) - ipfilter device to which we are working on */ +/* name(I) - name of the hash table */ +/* */ +/* ------------------------------------------------------------------------ */ +static void * +ipf_htable_select_add_ref(arg, unit, name) + void *arg; + int unit; + char *name; { iphtable_t *iph; - iph = fr_existshtable(unit, name); + iph = ipf_htable_exists(arg, unit, name); + if (iph != NULL) { + ATOMIC_INC32(iph->iph_ref); + } + return iph; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_htable_find */ +/* Returns: void * - NULL = failure, else pointer to the hash table */ +/* Parameters: arg(I) - pointer to local context to use */ +/* unit(I) - ipfilter device to which we are working on */ +/* name(I) - name of the hash table */ +/* */ +/* This function is exposed becaues it is used in the group-map feature. */ +/* ------------------------------------------------------------------------ */ +iphtable_t * +ipf_htable_find(arg, unit, name) + void *arg; + int unit; + char *name; +{ + iphtable_t *iph; + + iph = ipf_htable_exists(arg, unit, name); if ((iph != NULL) && (iph->iph_flags & IPHASH_DELETE) == 0) return iph; @@ -329,19 +807,31 @@ char *name; } -size_t fr_flushhtable(op) -iplookupflush_t *op; +/* ------------------------------------------------------------------------ */ +/* Function: ipf_htable_flush */ +/* Returns: size_t - number of entries flushed */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* arg(I) - pointer to local context to use */ +/* op(I) - pointer to lookup operation data */ +/* */ +/* ------------------------------------------------------------------------ */ +static size_t +ipf_htable_flush(softc, arg, op) + ipf_main_softc_t *softc; + void *arg; + iplookupflush_t *op; { + ipf_htable_softc_t *softh = arg; iphtable_t *iph; size_t freed; int i; freed = 0; - for (i = 0; i <= IPL_LOGMAX; i++) { + for (i = -1; i <= IPL_LOGMAX; i++) { if (op->iplf_unit == i || op->iplf_unit == IPL_LOGALL) { - while ((iph = ipf_htables[i]) != NULL) { - if (fr_delhtable(iph) == 0) { + while ((iph = softh->ipf_htables[i + 1]) != NULL) { + if (ipf_htable_remove(softc, arg, iph) == 0) { freed++; } else { iph->iph_flags |= IPHASH_DELETE; @@ -354,13 +844,73 @@ iplookupflush_t *op; } -/* - * Add an entry to a hash table. - */ -int fr_addhtent(iph, ipeo) -iphtable_t *iph; -iphtent_t *ipeo; +/* ------------------------------------------------------------------------ */ +/* Function: ipf_htable_node_add */ +/* Returns: int - 0 = success, else error */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* arg(I) - pointer to local context to use */ +/* op(I) - pointer to lookup operation data */ +/* uid(I) - real uid of process doing operation */ +/* */ +/* ------------------------------------------------------------------------ */ +static int +ipf_htable_node_add(softc, arg, op, uid) + ipf_main_softc_t *softc; + void *arg; + iplookupop_t *op; + int uid; { + iphtable_t *iph; + iphtent_t hte; + int err; + + if (op->iplo_size != sizeof(hte)) { + IPFERROR(30018); + return EINVAL; + } + + err = COPYIN(op->iplo_struct, &hte, sizeof(hte)); + if (err != 0) { + IPFERROR(30019); + return EFAULT; + } + hte.ipe_uid = uid; + + iph = ipf_htable_find(arg, op->iplo_unit, op->iplo_name); + if (iph == NULL) { + IPFERROR(30020); + return ESRCH; + } + + if (ipf_htent_find(iph, &hte) != NULL) { + IPFERROR(30021); + return EEXIST; + } + + err = ipf_htent_insert(softc, arg, iph, &hte); + + return err; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_htent_insert */ +/* Returns: int - 0 = success, -1 = error */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* arg(I) - pointer to local context to use */ +/* op(I) - pointer to lookup operation data */ +/* ipeo(I) - */ +/* */ +/* Add an entry to a hash table. */ +/* ------------------------------------------------------------------------ */ +static int +ipf_htent_insert(softc, arg, iph, ipeo) + ipf_main_softc_t *softc; + void *arg; + iphtable_t *iph; + iphtent_t *ipeo; +{ + ipf_htable_softc_t *softh = arg; iphtent_t *ipe; u_int hv; int bits; @@ -370,13 +920,35 @@ iphtent_t *ipeo; return -1; bcopy((char *)ipeo, (char *)ipe, sizeof(*ipe)); - ipe->ipe_addr.in4_addr &= ipe->ipe_mask.in4_addr; - ipe->ipe_addr.in4_addr = ntohl(ipe->ipe_addr.in4_addr); - bits = count4bits(ipe->ipe_mask.in4_addr); - ipe->ipe_mask.in4_addr = ntohl(ipe->ipe_mask.in4_addr); + ipe->ipe_addr.i6[0] &= ipe->ipe_mask.i6[0]; + if (ipe->ipe_family == AF_INET) { + bits = count4bits(ipe->ipe_mask.in4_addr); + ipe->ipe_addr.i6[1] = 0; + ipe->ipe_addr.i6[2] = 0; + ipe->ipe_addr.i6[3] = 0; + ipe->ipe_mask.i6[1] = 0; + ipe->ipe_mask.i6[2] = 0; + ipe->ipe_mask.i6[3] = 0; + hv = IPE_V4_HASH_FN(ipe->ipe_addr.in4_addr, + ipe->ipe_mask.in4_addr, iph->iph_size); + } else +#ifdef USE_INET6 + if (ipe->ipe_family == AF_INET6) { + ipe->ipe_addr.i6[1] &= ipe->ipe_mask.i6[1]; + ipe->ipe_addr.i6[2] &= ipe->ipe_mask.i6[2]; + ipe->ipe_addr.i6[3] &= ipe->ipe_mask.i6[3]; - hv = IPE_HASH_FN(ipe->ipe_addr.in4_addr, ipe->ipe_mask.in4_addr, - iph->iph_size); + bits = count6bits(ipe->ipe_mask.i6); + hv = IPE_V6_HASH_FN(ipe->ipe_addr.i6, + ipe->ipe_mask.i6, iph->iph_size); + } else +#endif + { + KFREE(ipe); + return -1; + } + + ipe->ipe_owner = iph; ipe->ipe_ref = 1; ipe->ipe_hnext = iph->iph_table[hv]; ipe->ipe_phnext = iph->iph_table + hv; @@ -385,21 +957,63 @@ iphtent_t *ipeo; iph->iph_table[hv]->ipe_phnext = &ipe->ipe_hnext; iph->iph_table[hv] = ipe; - ipe->ipe_next = iph->iph_list; - ipe->ipe_pnext = &iph->iph_list; - if (ipe->ipe_next != NULL) - ipe->ipe_next->ipe_pnext = &ipe->ipe_next; - iph->iph_list = ipe; + ipe->ipe_pnext = iph->iph_tail; + *iph->iph_tail = ipe; + iph->iph_tail = &ipe->ipe_next; + ipe->ipe_next = NULL; - if ((bits >= 0) && (bits != 32)) - iph->iph_masks |= 1 << bits; + if (ipe->ipe_die != 0) { + /* + * If the new node has a given expiration time, insert it + * into the list of expiring nodes with the ones to be + * removed first added to the front of the list. The + * insertion is O(n) but it is kept sorted for quick scans + * at expiration interval checks. + */ + iphtent_t *n; + + ipe->ipe_die = softc->ipf_ticks + IPF_TTLVAL(ipe->ipe_die); + for (n = softh->ipf_node_explist; n != NULL; n = n->ipe_dnext) { + if (ipe->ipe_die < n->ipe_die) + break; + if (n->ipe_dnext == NULL) { + /* + * We've got to the last node and everything + * wanted to be expired before this new node, + * so we have to tack it on the end... + */ + n->ipe_dnext = ipe; + ipe->ipe_pdnext = &n->ipe_dnext; + n = NULL; + break; + } + } + + if (softh->ipf_node_explist == NULL) { + softh->ipf_node_explist = ipe; + ipe->ipe_pdnext = &softh->ipf_node_explist; + } else if (n != NULL) { + ipe->ipe_dnext = n; + ipe->ipe_pdnext = n->ipe_pdnext; + n->ipe_pdnext = &ipe->ipe_dnext; + } + } + + if (ipe->ipe_family == AF_INET) { + ipf_inet_mask_add(bits, &iph->iph_v4_masks); + } +#ifdef USE_INET6 + else if (ipe->ipe_family == AF_INET6) { + ipf_inet6_mask_add(bits, &ipe->ipe_mask, &iph->iph_v6_masks); + } +#endif switch (iph->iph_type & ~IPHASH_ANON) { case IPHASH_GROUPMAP : - ipe->ipe_ptr = fr_addgroup(ipe->ipe_group, NULL, + ipe->ipe_ptr = ipf_group_add(softc, ipe->ipe_group, NULL, iph->iph_flags, IPL_LOGIPF, - fr_active); + softc->ipf_active); break; default : @@ -409,116 +1023,218 @@ iphtent_t *ipeo; } ipe->ipe_unit = iph->iph_unit; - ipf_nhtnodes[ipe->ipe_unit]++; + softh->ipf_nhtnodes[ipe->ipe_unit + 1]++; return 0; } -void *fr_iphmfindgroup(tptr, aptr) -void *tptr, *aptr; +/* ------------------------------------------------------------------------ */ +/* Function: ipf_htent_find */ +/* Returns: int - 0 = success, else error */ +/* Parameters: iph(I) - pointer to table to search */ +/* ipeo(I) - pointer to entry to find */ +/* */ +/* While it isn't absolutely necessary to for the address and mask to be */ +/* passed in through an iphtent_t structure, one is always present when it */ +/* is time to call this function, so it is just more convenient. */ +/* ------------------------------------------------------------------------ */ +static iphtent_t * +ipf_htent_find(iph, ipeo) + iphtable_t *iph; + iphtent_t *ipeo; +{ + iphtent_t ipe, *ent; + u_int hv; + int bits; + + bcopy((char *)ipeo, (char *)&ipe, sizeof(ipe)); + ipe.ipe_addr.i6[0] &= ipe.ipe_mask.i6[0]; + ipe.ipe_addr.i6[1] &= ipe.ipe_mask.i6[1]; + ipe.ipe_addr.i6[2] &= ipe.ipe_mask.i6[2]; + ipe.ipe_addr.i6[3] &= ipe.ipe_mask.i6[3]; + if (ipe.ipe_family == AF_INET) { + bits = count4bits(ipe.ipe_mask.in4_addr); + ipe.ipe_addr.i6[1] = 0; + ipe.ipe_addr.i6[2] = 0; + ipe.ipe_addr.i6[3] = 0; + ipe.ipe_mask.i6[1] = 0; + ipe.ipe_mask.i6[2] = 0; + ipe.ipe_mask.i6[3] = 0; + hv = IPE_V4_HASH_FN(ipe.ipe_addr.in4_addr, + ipe.ipe_mask.in4_addr, iph->iph_size); + } else +#ifdef USE_INET6 + if (ipe.ipe_family == AF_INET6) { + bits = count6bits(ipe.ipe_mask.i6); + hv = IPE_V6_HASH_FN(ipe.ipe_addr.i6, + ipe.ipe_mask.i6, iph->iph_size); + } else +#endif + return NULL; + + for (ent = iph->iph_table[hv]; ent != NULL; ent = ent->ipe_hnext) { + if (ent->ipe_family != ipe.ipe_family) + continue; + if (IP6_NEQ(&ipe.ipe_addr, &ent->ipe_addr)) + continue; + if (IP6_NEQ(&ipe.ipe_mask, &ent->ipe_mask)) + continue; + break; + } + + return ent; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_iphmfindgroup */ +/* Returns: int - 0 = success, else error */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* tptr(I) - */ +/* aptr(I) - */ +/* */ +/* Search a hash table for a matching entry and return the pointer stored */ +/* in it for use as the next group of rules to search. */ +/* */ +/* This function is exposed becaues it is used in the group-map feature. */ +/* ------------------------------------------------------------------------ */ +void * +ipf_iphmfindgroup(softc, tptr, aptr) + ipf_main_softc_t *softc; + void *tptr, *aptr; { struct in_addr *addr; iphtable_t *iph; iphtent_t *ipe; void *rval; - READ_ENTER(&ip_poolrw); + READ_ENTER(&softc->ipf_poolrw); iph = tptr; addr = aptr; - ipe = fr_iphmfind(iph, addr); + ipe = ipf_iphmfind(iph, addr); if (ipe != NULL) rval = ipe->ipe_ptr; else rval = NULL; - RWLOCK_EXIT(&ip_poolrw); + RWLOCK_EXIT(&softc->ipf_poolrw); return rval; } /* ------------------------------------------------------------------------ */ -/* Function: fr_iphmfindip */ +/* Function: ipf_iphmfindip */ /* Returns: int - 0 == +ve match, -1 == error, 1 == -ve/no match */ -/* Parameters: tptr(I) - pointer to the pool to search */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* tptr(I) - pointer to the pool to search */ /* ipversion(I) - IP protocol version (4 or 6) */ /* aptr(I) - pointer to address information */ +/* bytes(I) - packet length */ /* */ /* Search the hash table for a given address and return a search result. */ /* ------------------------------------------------------------------------ */ -int fr_iphmfindip(tptr, ipversion, aptr) -void *tptr, *aptr; -int ipversion; +static int +ipf_iphmfindip(softc, tptr, ipversion, aptr, bytes) + ipf_main_softc_t *softc; + void *tptr, *aptr; + int ipversion; + u_int bytes; { struct in_addr *addr; iphtable_t *iph; iphtent_t *ipe; int rval; - if (ipversion != 4) - return -1; - if (tptr == NULL || aptr == NULL) return -1; iph = tptr; addr = aptr; - READ_ENTER(&ip_poolrw); - ipe = fr_iphmfind(iph, addr); - if (ipe != NULL) + READ_ENTER(&softc->ipf_poolrw); + if (ipversion == 4) { + ipe = ipf_iphmfind(iph, addr); +#ifdef USE_INET6 + } else if (ipversion == 6) { + ipe = ipf_iphmfind6(iph, (i6addr_t *)addr); +#endif + } else { + ipe = NULL; + } + + if (ipe != NULL) { rval = 0; - else + ipe->ipe_hits++; + ipe->ipe_bytes += bytes; + } else { rval = 1; - RWLOCK_EXIT(&ip_poolrw); + } + RWLOCK_EXIT(&softc->ipf_poolrw); return rval; } -/* Locks: ip_poolrw */ -static iphtent_t *fr_iphmfind(iph, addr) -iphtable_t *iph; -struct in_addr *addr; +/* ------------------------------------------------------------------------ */ +/* Function: ipf_iphmfindip */ +/* Parameters: iph(I) - pointer to hash table */ +/* addr(I) - pointer to IPv4 address */ +/* Locks: ipf_poolrw */ +/* */ +/* ------------------------------------------------------------------------ */ +static iphtent_t * +ipf_iphmfind(iph, addr) + iphtable_t *iph; + struct in_addr *addr; { - u_32_t hmsk, msk, ips; + u_32_t msk, ips; iphtent_t *ipe; u_int hv; + int i; - hmsk = iph->iph_masks; - msk = 0xffffffff; + i = 0; maskloop: - ips = ntohl(addr->s_addr) & msk; - hv = IPE_HASH_FN(ips, msk, iph->iph_size); + msk = iph->iph_v4_masks.imt4_active[i]; + ips = addr->s_addr & msk; + hv = IPE_V4_HASH_FN(ips, msk, iph->iph_size); for (ipe = iph->iph_table[hv]; (ipe != NULL); ipe = ipe->ipe_hnext) { - if (ipe->ipe_mask.in4_addr != msk || - ipe->ipe_addr.in4_addr != ips) { + if ((ipe->ipe_family != AF_INET) || + (ipe->ipe_mask.in4_addr != msk) || + (ipe->ipe_addr.in4_addr != ips)) { continue; } break; } - if ((ipe == NULL) && (hmsk != 0)) { - while (hmsk != 0) { - msk <<= 1; - if (hmsk & 0x80000000) - break; - hmsk <<= 1; - } - if (hmsk != 0) { - hmsk <<= 1; + if (ipe == NULL) { + i++; + if (i < iph->iph_v4_masks.imt4_max) goto maskloop; - } } return ipe; } -int fr_htable_getnext(token, ilp) -ipftoken_t *token; -ipflookupiter_t *ilp; +/* ------------------------------------------------------------------------ */ +/* Function: ipf_htable_iter_next */ +/* Returns: int - 0 = success, else error */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* arg(I) - pointer to local context to use */ +/* token(I) - */ +/* ilp(I) - */ +/* */ +/* ------------------------------------------------------------------------ */ +static int +ipf_htable_iter_next(softc, arg, token, ilp) + ipf_main_softc_t *softc; + void *arg; + ipftoken_t *token; + ipflookupiter_t *ilp; { + ipf_htable_softc_t *softh = arg; iphtent_t *node, zn, *nextnode; iphtable_t *iph, zp, *nextiph; + void *hnext; int err; err = 0; @@ -527,14 +1243,14 @@ ipflookupiter_t *ilp; nextiph = NULL; nextnode = NULL; - READ_ENTER(&ip_poolrw); + READ_ENTER(&softc->ipf_poolrw); switch (ilp->ili_otype) { case IPFLOOKUPITER_LIST : iph = token->ipt_data; if (iph == NULL) { - nextiph = ipf_htables[(int)ilp->ili_unit]; + nextiph = softh->ipf_htables[(int)ilp->ili_unit + 1]; } else { nextiph = iph->iph_next; } @@ -547,15 +1263,18 @@ ipflookupiter_t *ilp; nextiph = &zp; token->ipt_data = NULL; } + hnext = nextiph->iph_next; break; case IPFLOOKUPITER_NODE : node = token->ipt_data; if (node == NULL) { - iph = fr_findhtable(ilp->ili_unit, ilp->ili_name); - if (iph == NULL) + iph = ipf_htable_find(arg, ilp->ili_unit, + ilp->ili_name); + if (iph == NULL) { + IPFERROR(30009); err = ESRCH; - else { + } else { nextnode = iph->iph_list; } } else { @@ -570,73 +1289,179 @@ ipflookupiter_t *ilp; nextnode = &zn; token->ipt_data = NULL; } + hnext = nextnode->ipe_next; break; + default : + IPFERROR(30010); err = EINVAL; + hnext = NULL; break; } - RWLOCK_EXIT(&ip_poolrw); + RWLOCK_EXIT(&softc->ipf_poolrw); if (err != 0) return err; switch (ilp->ili_otype) { case IPFLOOKUPITER_LIST : - if (iph != NULL) { - WRITE_ENTER(&ip_poolrw); - fr_derefhtable(iph); - RWLOCK_EXIT(&ip_poolrw); - } err = COPYOUT(nextiph, ilp->ili_data, sizeof(*nextiph)); - if (err != 0) + if (err != 0) { + IPFERROR(30011); err = EFAULT; + } + if (iph != NULL) { + WRITE_ENTER(&softc->ipf_poolrw); + ipf_htable_deref(softc, softh, iph); + RWLOCK_EXIT(&softc->ipf_poolrw); + } break; case IPFLOOKUPITER_NODE : - if (node != NULL) { - WRITE_ENTER(&ip_poolrw); - fr_derefhtent(node); - RWLOCK_EXIT(&ip_poolrw); - } err = COPYOUT(nextnode, ilp->ili_data, sizeof(*nextnode)); - if (err != 0) + if (err != 0) { + IPFERROR(30012); err = EFAULT; + } + if (node != NULL) { + WRITE_ENTER(&softc->ipf_poolrw); + ipf_htent_deref(softc, node); + RWLOCK_EXIT(&softc->ipf_poolrw); + } break; } + if (hnext == NULL) + ipf_token_mark_complete(token); + return err; } -void fr_htable_iterderef(otype, unit, data) -u_int otype; -int unit; -void *data; +/* ------------------------------------------------------------------------ */ +/* Function: ipf_htable_iter_deref */ +/* Returns: int - 0 = success, else error */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* arg(I) - pointer to local context to use */ +/* otype(I) - which data structure type is being walked */ +/* unit(I) - ipfilter device to which we are working on */ +/* data(I) - pointer to old data structure */ +/* */ +/* ------------------------------------------------------------------------ */ +static int +ipf_htable_iter_deref(softc, arg, otype, unit, data) + ipf_main_softc_t *softc; + void *arg; + int otype; + int unit; + void *data; { if (data == NULL) - return; + return EFAULT; - if (unit < 0 || unit > IPL_LOGMAX) - return; + if (unit < -1 || unit > IPL_LOGMAX) + return EINVAL; switch (otype) { case IPFLOOKUPITER_LIST : - WRITE_ENTER(&ip_poolrw); - fr_derefhtable((iphtable_t *)data); - RWLOCK_EXIT(&ip_poolrw); + ipf_htable_deref(softc, arg, (iphtable_t *)data); break; case IPFLOOKUPITER_NODE : - WRITE_ENTER(&ip_poolrw); - fr_derefhtent((iphtent_t *)data); - RWLOCK_EXIT(&ip_poolrw); + ipf_htent_deref(arg, (iphtent_t *)data); break; default : break; } + + return 0; } -#endif /* IPFILTER_LOOKUP */ + +#ifdef USE_INET6 +/* ------------------------------------------------------------------------ */ +/* Function: ipf_iphmfind6 */ +/* Parameters: iph(I) - pointer to hash table */ +/* addr(I) - pointer to IPv6 address */ +/* Locks: ipf_poolrw */ +/* */ +/* ------------------------------------------------------------------------ */ +static iphtent_t * +ipf_iphmfind6(iph, addr) + iphtable_t *iph; + i6addr_t *addr; +{ + i6addr_t *msk, ips; + iphtent_t *ipe; + u_int hv; + int i; + + i = 0; +maskloop: + msk = iph->iph_v6_masks.imt6_active + i; + ips.i6[0] = addr->i6[0] & msk->i6[0]; + ips.i6[1] = addr->i6[1] & msk->i6[1]; + ips.i6[2] = addr->i6[2] & msk->i6[2]; + ips.i6[3] = addr->i6[3] & msk->i6[3]; + hv = IPE_V6_HASH_FN(ips.i6, msk->i6, iph->iph_size); + for (ipe = iph->iph_table[hv]; (ipe != NULL); ipe = ipe->ipe_next) { + if ((ipe->ipe_family != AF_INET6) || + IP6_NEQ(&ipe->ipe_mask, msk) || + IP6_NEQ(&ipe->ipe_addr, &ips)) { + continue; + } + break; + } + + if (ipe == NULL) { + i++; + if (i < iph->iph_v6_masks.imt6_max) + goto maskloop; + } + return ipe; +} +#endif + + +static void +ipf_htable_expire(softc, arg) + ipf_main_softc_t *softc; + void *arg; +{ + ipf_htable_softc_t *softh = arg; + iphtent_t *n; + + while ((n = softh->ipf_node_explist) != NULL) { + if (n->ipe_die > softc->ipf_ticks) + break; + + ipf_htent_remove(softc, softh, n->ipe_owner, n); + } +} + + +#ifndef _KERNEL + +/* ------------------------------------------------------------------------ */ +/* */ +/* ------------------------------------------------------------------------ */ +void +ipf_htable_dump(softc, arg) + ipf_main_softc_t *softc; + void *arg; +{ + ipf_htable_softc_t *softh = arg; + iphtable_t *iph; + int i; + + printf("List of configured hash tables\n"); + for (i = 0; i < IPL_LOGSIZE; i++) + for (iph = softh->ipf_htables[i]; iph != NULL; + iph = iph->iph_next) + printhash(iph, bcopywrap, NULL, opts, NULL); + +} +#endif diff --git a/sys/contrib/ipfilter/netinet/ip_htable.h b/sys/contrib/ipfilter/netinet/ip_htable.h index 2c088123011..8038bbcc104 100644 --- a/sys/contrib/ipfilter/netinet/ip_htable.h +++ b/sys/contrib/ipfilter/netinet/ip_htable.h @@ -6,36 +6,52 @@ typedef struct iphtent_s { struct iphtent_s *ipe_next, **ipe_pnext; struct iphtent_s *ipe_hnext, **ipe_phnext; + struct iphtent_s *ipe_dnext, **ipe_pdnext; + struct iphtable_s *ipe_owner; void *ipe_ptr; i6addr_t ipe_addr; i6addr_t ipe_mask; + U_QUAD_T ipe_hits; + U_QUAD_T ipe_bytes; + u_long ipe_die; + int ipe_uid; int ipe_ref; int ipe_unit; + char ipe_family; + char ipe_xxx[3]; union { char ipeu_char[16]; u_long ipeu_long; u_int ipeu_int; - }ipe_un; + } ipe_un; } iphtent_t; #define ipe_value ipe_un.ipeu_int #define ipe_group ipe_un.ipeu_char -#define IPE_HASH_FN(a, m, s) (((a) * (m)) % (s)) - +#define IPE_V4_HASH_FN(a, m, s) ((((m) ^ (a)) - 1 - ((a) >> 8)) % (s)) +#define IPE_V6_HASH_FN(a, m, s) (((((m)[0] ^ (a)[0]) - ((a)[0] >> 8)) + \ + (((m)[1] & (a)[1]) - ((a)[1] >> 8)) + \ + (((m)[2] & (a)[2]) - ((a)[2] >> 8)) + \ + (((m)[3] & (a)[3]) - ((a)[3] >> 8))) % (s)) typedef struct iphtable_s { ipfrwlock_t iph_rwlock; struct iphtable_s *iph_next, **iph_pnext; struct iphtent_s **iph_table; struct iphtent_s *iph_list; + struct iphtent_s **iph_tail; +#ifdef USE_INET6 + ipf_v6_masktab_t iph_v6_masks; +#endif + ipf_v4_masktab_t iph_v4_masks; size_t iph_size; /* size of hash table */ u_long iph_seed; /* hashing seed */ u_32_t iph_flags; u_int iph_unit; /* IPL_LOG* */ u_int iph_ref; u_int iph_type; /* lookup or group map - IPHASH_* */ - u_int iph_masks; /* IPv4 netmasks in use */ + u_int iph_maskset[4]; /* netmasks in use */ char iph_name[FR_GROUPLEN]; /* hash table number */ } iphtable_t; @@ -55,24 +71,11 @@ typedef struct iphtstat_s { } iphtstat_t; -extern iphtable_t *ipf_htables[IPL_LOGSIZE]; - -extern iphtable_t *fr_existshtable __P((int, char *)); -extern int fr_clearhtable __P((iphtable_t *)); -extern void fr_htable_unload __P((void)); -extern int fr_newhtable __P((iplookupop_t *)); -extern iphtable_t *fr_findhtable __P((int, char *)); -extern int fr_removehtable __P((int, char *)); -extern size_t fr_flushhtable __P((iplookupflush_t *)); -extern int fr_addhtent __P((iphtable_t *, iphtent_t *)); -extern int fr_delhtent __P((iphtable_t *, iphtent_t *)); -extern int fr_derefhtable __P((iphtable_t *)); -extern int fr_derefhtent __P((iphtent_t *)); -extern int fr_delhtable __P((iphtable_t *)); -extern void *fr_iphmfindgroup __P((void *, void *)); -extern int fr_iphmfindip __P((void *, int, void *)); -extern int fr_gethtablestat __P((iplookupop_t *)); -extern int fr_htable_getnext __P((ipftoken_t *, ipflookupiter_t *)); -extern void fr_htable_iterderef __P((u_int, int, void *)); +extern void *ipf_iphmfindgroup __P((ipf_main_softc_t *, void *, void *)); +extern iphtable_t *ipf_htable_find __P((void *, int, char *)); +extern ipf_lookup_t ipf_htable_backend; +#ifndef _KERNEL +extern void ipf_htable_dump __P((ipf_main_softc_t *, void *)); +#endif #endif /* __IP_HTABLE_H__ */ diff --git a/sys/contrib/ipfilter/netinet/ip_ipsec_pxy.c b/sys/contrib/ipfilter/netinet/ip_ipsec_pxy.c index e88a6b98b51..980476ad8bf 100644 --- a/sys/contrib/ipfilter/netinet/ip_ipsec_pxy.c +++ b/sys/contrib/ipfilter/netinet/ip_ipsec_pxy.c @@ -1,152 +1,228 @@ /* - * Copyright (C) 2001-2003 by Darren Reed + * Copyright (C) 2012 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. * * Simple ISAKMP transparent proxy for in-kernel use. For use with the NAT * code. * - * $Id: ip_ipsec_pxy.c,v 2.20.2.8 2006/07/14 06:12:14 darrenr Exp $ + * $Id$ * */ #define IPF_IPSEC_PROXY -int ippr_ipsec_init __P((void)); -void ippr_ipsec_fini __P((void)); -int ippr_ipsec_new __P((fr_info_t *, ap_session_t *, nat_t *)); -void ippr_ipsec_del __P((ap_session_t *)); -int ippr_ipsec_inout __P((fr_info_t *, ap_session_t *, nat_t *)); -int ippr_ipsec_match __P((fr_info_t *, ap_session_t *, nat_t *)); +/* + * IPSec proxy + */ +typedef struct ipf_ipsec_softc_s { + frentry_t ipsec_fr; + int ipsec_proxy_init; + int ipsec_proxy_ttl; + ipftq_t *ipsec_nat_tqe; + ipftq_t *ipsec_state_tqe; + char ipsec_buffer[1500]; +} ipf_ipsec_softc_t; -static frentry_t ipsecfr; -static ipftq_t *ipsecnattqe; -static ipftq_t *ipsecstatetqe; -static char ipsec_buffer[1500]; -int ipsec_proxy_init = 0; -int ipsec_proxy_ttl = 60; +void *ipf_p_ipsec_soft_create __P((ipf_main_softc_t *)); +void ipf_p_ipsec_soft_destroy __P((ipf_main_softc_t *, void *)); +int ipf_p_ipsec_soft_init __P((ipf_main_softc_t *, void *)); +void ipf_p_ipsec_soft_fini __P((ipf_main_softc_t *, void *)); +int ipf_p_ipsec_init __P((void)); +void ipf_p_ipsec_fini __P((void)); +int ipf_p_ipsec_new __P((void *, fr_info_t *, ap_session_t *, nat_t *)); +void ipf_p_ipsec_del __P((ipf_main_softc_t *, ap_session_t *)); +int ipf_p_ipsec_inout __P((void *, fr_info_t *, ap_session_t *, nat_t *)); +int ipf_p_ipsec_match __P((fr_info_t *, ap_session_t *, nat_t *)); + /* * IPSec application proxy initialization. */ -int ippr_ipsec_init() +void * +ipf_p_ipsec_soft_create(softc) + ipf_main_softc_t *softc; { - bzero((char *)&ipsecfr, sizeof(ipsecfr)); - ipsecfr.fr_ref = 1; - ipsecfr.fr_flags = FR_OUTQUE|FR_PASS|FR_QUICK|FR_KEEPSTATE; - MUTEX_INIT(&ipsecfr.fr_lock, "IPsec proxy rule lock"); - ipsec_proxy_init = 1; + ipf_ipsec_softc_t *softi; - ipsecnattqe = fr_addtimeoutqueue(&nat_utqe, ipsec_proxy_ttl); - if (ipsecnattqe == NULL) + KMALLOC(softi, ipf_ipsec_softc_t *); + if (softi == NULL) + return NULL; + + bzero((char *)softi, sizeof(*softi)); + softi->ipsec_fr.fr_ref = 1; + softi->ipsec_fr.fr_flags = FR_OUTQUE|FR_PASS|FR_QUICK|FR_KEEPSTATE; + MUTEX_INIT(&softi->ipsec_fr.fr_lock, "IPsec proxy rule lock"); + softi->ipsec_proxy_init = 1; + softi->ipsec_proxy_ttl = 60; + + return softi; +} + + +int +ipf_p_ipsec_soft_init(softc, arg) + ipf_main_softc_t *softc; + void *arg; +{ + ipf_ipsec_softc_t *softi = arg; + + softi->ipsec_nat_tqe = ipf_state_add_tq(softc, softi->ipsec_proxy_ttl); + if (softi->ipsec_nat_tqe == NULL) return -1; - ipsecstatetqe = fr_addtimeoutqueue(&ips_utqe, ipsec_proxy_ttl); - if (ipsecstatetqe == NULL) { - if (fr_deletetimeoutqueue(ipsecnattqe) == 0) - fr_freetimeoutqueue(ipsecnattqe); - ipsecnattqe = NULL; + softi->ipsec_state_tqe = ipf_nat_add_tq(softc, softi->ipsec_proxy_ttl); + if (softi->ipsec_state_tqe == NULL) { + if (ipf_deletetimeoutqueue(softi->ipsec_nat_tqe) == 0) + ipf_freetimeoutqueue(softc, softi->ipsec_nat_tqe); + softi->ipsec_nat_tqe = NULL; return -1; } - ipsecnattqe->ifq_flags |= IFQF_PROXY; - ipsecstatetqe->ifq_flags |= IFQF_PROXY; - - ipsecfr.fr_age[0] = ipsec_proxy_ttl; - ipsecfr.fr_age[1] = ipsec_proxy_ttl; + softi->ipsec_nat_tqe->ifq_flags |= IFQF_PROXY; + softi->ipsec_state_tqe->ifq_flags |= IFQF_PROXY; + softi->ipsec_fr.fr_age[0] = softi->ipsec_proxy_ttl; + softi->ipsec_fr.fr_age[1] = softi->ipsec_proxy_ttl; return 0; } -void ippr_ipsec_fini() +void +ipf_p_ipsec_soft_fini(softc, arg) + ipf_main_softc_t *softc; + void *arg; { - if (ipsecnattqe != NULL) { - if (fr_deletetimeoutqueue(ipsecnattqe) == 0) - fr_freetimeoutqueue(ipsecnattqe); - } - ipsecnattqe = NULL; - if (ipsecstatetqe != NULL) { - if (fr_deletetimeoutqueue(ipsecstatetqe) == 0) - fr_freetimeoutqueue(ipsecstatetqe); - } - ipsecstatetqe = NULL; + ipf_ipsec_softc_t *softi = arg; - if (ipsec_proxy_init == 1) { - MUTEX_DESTROY(&ipsecfr.fr_lock); - ipsec_proxy_init = 0; + if (arg == NULL) + return; + + if (softi->ipsec_nat_tqe != NULL) { + if (ipf_deletetimeoutqueue(softi->ipsec_nat_tqe) == 0) + ipf_freetimeoutqueue(softc, softi->ipsec_nat_tqe); } + softi->ipsec_nat_tqe = NULL; + if (softi->ipsec_state_tqe != NULL) { + if (ipf_deletetimeoutqueue(softi->ipsec_state_tqe) == 0) + ipf_freetimeoutqueue(softc, softi->ipsec_state_tqe); + } + softi->ipsec_state_tqe = NULL; +} + + +void +ipf_p_ipsec_soft_destroy(softc, arg) + ipf_main_softc_t *softc; + void *arg; +{ + ipf_ipsec_softc_t *softi = arg; + + if (softi->ipsec_proxy_init == 1) { + MUTEX_DESTROY(&softi->ipsec_fr.fr_lock); + softi->ipsec_proxy_init = 0; + } + + KFREE(softi); } /* * Setup for a new IPSEC proxy. */ -int ippr_ipsec_new(fin, aps, nat) -fr_info_t *fin; -ap_session_t *aps; -nat_t *nat; +int +ipf_p_ipsec_new(arg, fin, aps, nat) + void *arg; + fr_info_t *fin; + ap_session_t *aps; + nat_t *nat; { - ipsec_pxy_t *ipsec; - fr_info_t fi; - ipnat_t *ipn; - char *ptr; + ipf_ipsec_softc_t *softi = arg; + ipf_main_softc_t *softc = fin->fin_main_soft; +#ifdef USE_MUTEXES + ipf_nat_softc_t *softn = softc->ipf_nat_soft; +#endif int p, off, dlen, ttl; - mb_t *m; + ipsec_pxy_t *ipsec; + ipnat_t *ipn, *np; + fr_info_t fi; + char *ptr; + int size; ip_t *ip; + mb_t *m; + + if (fin->fin_v != 4) + return -1; off = fin->fin_plen - fin->fin_dlen + fin->fin_ipoff; - bzero(ipsec_buffer, sizeof(ipsec_buffer)); + bzero(softi->ipsec_buffer, sizeof(softi->ipsec_buffer)); ip = fin->fin_ip; m = fin->fin_m; dlen = M_LEN(m) - off; if (dlen < 16) return -1; - COPYDATA(m, off, MIN(sizeof(ipsec_buffer), dlen), ipsec_buffer); + COPYDATA(m, off, MIN(sizeof(softi->ipsec_buffer), dlen), + softi->ipsec_buffer); - if (nat_outlookup(fin, 0, IPPROTO_ESP, nat->nat_inip, + if (ipf_nat_outlookup(fin, 0, IPPROTO_ESP, nat->nat_nsrcip, ip->ip_dst) != NULL) return -1; - aps->aps_psiz = sizeof(*ipsec); - KMALLOCS(aps->aps_data, ipsec_pxy_t *, sizeof(*ipsec)); - if (aps->aps_data == NULL) + np = nat->nat_ptr; + size = np->in_size; + KMALLOC(ipsec, ipsec_pxy_t *); + if (ipsec == NULL) return -1; - ipsec = aps->aps_data; + KMALLOCS(ipn, ipnat_t *, size); + if (ipn == NULL) { + KFREE(ipsec); + return -1; + } + + aps->aps_data = ipsec; + aps->aps_psiz = sizeof(*ipsec); bzero((char *)ipsec, sizeof(*ipsec)); + bzero((char *)ipn, size); + ipsec->ipsc_rule = ipn; /* * Create NAT rule against which the tunnel/transport mapping is * created. This is required because the current NAT rule does not * describe ESP but UDP instead. */ - ipn = &ipsec->ipsc_rule; - ttl = IPF_TTLVAL(ipsecnattqe->ifq_ttl); - ipn->in_tqehead[0] = fr_addtimeoutqueue(&nat_utqe, ttl); - ipn->in_tqehead[1] = fr_addtimeoutqueue(&nat_utqe, ttl); + ipn->in_size = size; + ttl = IPF_TTLVAL(softi->ipsec_nat_tqe->ifq_ttl); + ipn->in_tqehead[0] = ipf_nat_add_tq(softc, ttl); + ipn->in_tqehead[1] = ipf_nat_add_tq(softc, ttl); ipn->in_ifps[0] = fin->fin_ifp; ipn->in_apr = NULL; ipn->in_use = 1; ipn->in_hits = 1; - ipn->in_nip = ntohl(nat->nat_outip.s_addr); + ipn->in_snip = ntohl(nat->nat_nsrcaddr); ipn->in_ippip = 1; - ipn->in_inip = nat->nat_inip.s_addr; - ipn->in_inmsk = 0xffffffff; - ipn->in_outip = fin->fin_saddr; - ipn->in_outmsk = nat->nat_outip.s_addr; - ipn->in_srcip = fin->fin_saddr; - ipn->in_srcmsk = 0xffffffff; + ipn->in_osrcip = nat->nat_osrcip; + ipn->in_osrcmsk = 0xffffffff; + ipn->in_nsrcip = nat->nat_nsrcip; + ipn->in_nsrcmsk = 0xffffffff; + ipn->in_odstip = nat->nat_odstip; + ipn->in_odstmsk = 0xffffffff; + ipn->in_ndstip = nat->nat_ndstip; + ipn->in_ndstmsk = 0xffffffff; ipn->in_redir = NAT_MAP; - bcopy(nat->nat_ptr->in_ifnames[0], ipn->in_ifnames[0], - sizeof(ipn->in_ifnames[0])); - ipn->in_p = IPPROTO_ESP; + ipn->in_pr[0] = IPPROTO_ESP; + ipn->in_pr[1] = IPPROTO_ESP; + ipn->in_flags = (np->in_flags | IPN_PROXYRULE); + MUTEX_INIT(&ipn->in_lock, "IPSec proxy NAT rule"); + + ipn->in_namelen = np->in_namelen; + bcopy(np->in_names, ipn->in_ifnames, ipn->in_namelen); + ipn->in_ifnames[0] = np->in_ifnames[0]; + ipn->in_ifnames[1] = np->in_ifnames[1]; bcopy((char *)fin, (char *)&fi, sizeof(fi)); - fi.fin_state = NULL; - fi.fin_nat = NULL; fi.fin_fi.fi_p = IPPROTO_ESP; - fi.fin_fr = &ipsecfr; + fi.fin_fr = &softi->ipsec_fr; fi.fin_data[0] = 0; fi.fin_data[1] = 0; p = ip->ip_p; @@ -154,7 +230,7 @@ nat_t *nat; fi.fin_flx &= ~(FI_TCPUDP|FI_STATE|FI_FRAG); fi.fin_flx |= FI_IGNORE; - ptr = ipsec_buffer; + ptr = softi->ipsec_buffer; bcopy(ptr, (char *)ipsec->ipsc_icookie, sizeof(ipsec_cookie_t)); ptr += sizeof(ipsec_cookie_t); bcopy(ptr, (char *)ipsec->ipsc_rcookie, sizeof(ipsec_cookie_t)); @@ -166,18 +242,19 @@ nat_t *nat; if ((ipsec->ipsc_rcookie[0]|ipsec->ipsc_rcookie[1]) != 0) ipsec->ipsc_rckset = 1; - ipsec->ipsc_nat = nat_new(&fi, ipn, &ipsec->ipsc_nat, - NAT_SLAVE|SI_WILDP, NAT_OUTBOUND); + MUTEX_ENTER(&softn->ipf_nat_new); + ipsec->ipsc_nat = ipf_nat_add(&fi, ipn, &ipsec->ipsc_nat, + NAT_SLAVE|SI_WILDP, NAT_OUTBOUND); + MUTEX_EXIT(&softn->ipf_nat_new); if (ipsec->ipsc_nat != NULL) { - (void) nat_proto(&fi, ipsec->ipsc_nat, 0); - nat_update(&fi, ipsec->ipsc_nat, ipn); + (void) ipf_nat_proto(&fi, ipsec->ipsc_nat, 0); + MUTEX_ENTER(&ipsec->ipsc_nat->nat_lock); + ipf_nat_update(&fi, ipsec->ipsc_nat); + MUTEX_EXIT(&ipsec->ipsc_nat->nat_lock); fi.fin_data[0] = 0; fi.fin_data[1] = 0; - ipsec->ipsc_state = fr_addstate(&fi, &ipsec->ipsc_state, - SI_WILDP); - if (fi.fin_state != NULL) - fr_statederef((ipstate_t **)&fi.fin_state); + (void) ipf_state_add(softc, &fi, &ipsec->ipsc_state, SI_WILDP); } ip->ip_p = p & 0xff; return 0; @@ -188,11 +265,15 @@ nat_t *nat; * For outgoing IKE packets. refresh timeouts for NAT & state entries, if * we can. If they have disappeared, recreate them. */ -int ippr_ipsec_inout(fin, aps, nat) -fr_info_t *fin; -ap_session_t *aps; -nat_t *nat; +int +ipf_p_ipsec_inout(arg, fin, aps, nat) + void *arg; + fr_info_t *fin; + ap_session_t *aps; + nat_t *nat; { + ipf_ipsec_softc_t *softi = arg; + ipf_main_softc_t *softc = fin->fin_main_soft; ipsec_pxy_t *ipsec; fr_info_t fi; ip_t *ip; @@ -212,10 +293,8 @@ nat_t *nat; if ((ipsec->ipsc_nat == NULL) || (ipsec->ipsc_state == NULL)) { bcopy((char *)fin, (char *)&fi, sizeof(fi)); - fi.fin_state = NULL; - fi.fin_nat = NULL; fi.fin_fi.fi_p = IPPROTO_ESP; - fi.fin_fr = &ipsecfr; + fi.fin_fr = &softi->ipsec_fr; fi.fin_data[0] = 0; fi.fin_data[1] = 0; ip->ip_p = IPPROTO_ESP; @@ -227,36 +306,42 @@ nat_t *nat; * Update NAT timeout/create NAT if missing. */ if (ipsec->ipsc_nat != NULL) - fr_queueback(&ipsec->ipsc_nat->nat_tqe); + ipf_queueback(softc->ipf_ticks, + &ipsec->ipsc_nat->nat_tqe); else { - ipsec->ipsc_nat = nat_new(&fi, &ipsec->ipsc_rule, - &ipsec->ipsc_nat, - NAT_SLAVE|SI_WILDP, - nat->nat_dir); +#ifdef USE_MUTEXES + ipf_nat_softc_t *softn = softc->ipf_nat_soft; +#endif + + MUTEX_ENTER(&softn->ipf_nat_new); + ipsec->ipsc_nat = ipf_nat_add(&fi, ipsec->ipsc_rule, + &ipsec->ipsc_nat, + NAT_SLAVE|SI_WILDP, + nat->nat_dir); + MUTEX_EXIT(&softn->ipf_nat_new); if (ipsec->ipsc_nat != NULL) { - (void) nat_proto(&fi, ipsec->ipsc_nat, 0); - nat_update(&fi, ipsec->ipsc_nat, - &ipsec->ipsc_rule); + (void) ipf_nat_proto(&fi, ipsec->ipsc_nat, 0); + MUTEX_ENTER(&ipsec->ipsc_nat->nat_lock); + ipf_nat_update(&fi, ipsec->ipsc_nat); + MUTEX_EXIT(&ipsec->ipsc_nat->nat_lock); } } /* * Update state timeout/create state if missing. */ - READ_ENTER(&ipf_state); + READ_ENTER(&softc->ipf_state); if (ipsec->ipsc_state != NULL) { - fr_queueback(&ipsec->ipsc_state->is_sti); + ipf_queueback(softc->ipf_ticks, + &ipsec->ipsc_state->is_sti); ipsec->ipsc_state->is_die = nat->nat_age; - RWLOCK_EXIT(&ipf_state); + RWLOCK_EXIT(&softc->ipf_state); } else { - RWLOCK_EXIT(&ipf_state); + RWLOCK_EXIT(&softc->ipf_state); fi.fin_data[0] = 0; fi.fin_data[1] = 0; - ipsec->ipsc_state = fr_addstate(&fi, - &ipsec->ipsc_state, - SI_WILDP); - if (fi.fin_state != NULL) - fr_statederef((ipstate_t **)&fi.fin_state); + (void) ipf_state_add(softc, &fi, &ipsec->ipsc_state, + SI_WILDP); } ip->ip_p = p; } @@ -270,10 +355,11 @@ nat_t *nat; * in the same order (not reversed depending on packet flow direction as with * UDP/TCP port numbers). */ -int ippr_ipsec_match(fin, aps, nat) -fr_info_t *fin; -ap_session_t *aps; -nat_t *nat; +int +ipf_p_ipsec_match(fin, aps, nat) + fr_info_t *fin; + ap_session_t *aps; + nat_t *nat; { ipsec_pxy_t *ipsec; u_32_t cookies[4]; @@ -314,8 +400,10 @@ nat_t *nat; /* * clean up after ourselves. */ -void ippr_ipsec_del(aps) -ap_session_t *aps; +void +ipf_p_ipsec_del(softc, aps) + ipf_main_softc_t *softc; + ap_session_t *aps; { ipsec_pxy_t *ipsec; @@ -327,15 +415,17 @@ ap_session_t *aps; * *_del() is on a callback from aps_free(), from nat_delete() */ - READ_ENTER(&ipf_state); + READ_ENTER(&softc->ipf_state); if (ipsec->ipsc_state != NULL) { - ipsec->ipsc_state->is_die = fr_ticks + 1; + ipsec->ipsc_state->is_die = softc->ipf_ticks + 1; ipsec->ipsc_state->is_me = NULL; - fr_queuefront(&ipsec->ipsc_state->is_sti); + ipf_queuefront(&ipsec->ipsc_state->is_sti); } - RWLOCK_EXIT(&ipf_state); + RWLOCK_EXIT(&softc->ipf_state); ipsec->ipsc_state = NULL; ipsec->ipsc_nat = NULL; + ipsec->ipsc_rule->in_flags |= IPN_DELETE; + ipf_nat_rule_deref(softc, &ipsec->ipsc_rule); } } diff --git a/sys/contrib/ipfilter/netinet/ip_irc_pxy.c b/sys/contrib/ipfilter/netinet/ip_irc_pxy.c index 5bb252a25da..b9954b4c067 100644 --- a/sys/contrib/ipfilter/netinet/ip_irc_pxy.c +++ b/sys/contrib/ipfilter/netinet/ip_irc_pxy.c @@ -1,9 +1,9 @@ /* - * Copyright (C) 2000-2003 Darren Reed + * Copyright (C) 2012 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. * - * $Id: ip_irc_pxy.c,v 2.39.2.6 2006/07/14 06:12:14 darrenr Exp $ + * $Id$ */ #define IPF_IRC_PROXY @@ -11,12 +11,12 @@ #define IPF_IRCBUFSZ 96 /* This *MUST* be >= 64! */ -int ippr_irc_init __P((void)); -void ippr_irc_fini __P((void)); -int ippr_irc_new __P((fr_info_t *, ap_session_t *, nat_t *)); -int ippr_irc_out __P((fr_info_t *, ap_session_t *, nat_t *)); -int ippr_irc_send __P((fr_info_t *, nat_t *)); -int ippr_irc_complete __P((ircinfo_t *, char *, size_t)); +void ipf_p_irc_main_load __P((void)); +void ipf_p_irc_main_unload __P((void)); +int ipf_p_irc_new __P((void *, fr_info_t *, ap_session_t *, nat_t *)); +int ipf_p_irc_out __P((void *, fr_info_t *, ap_session_t *, nat_t *)); +int ipf_p_irc_send __P((fr_info_t *, nat_t *)); +int ipf_p_irc_complete __P((ircinfo_t *, char *, size_t)); u_short ipf_irc_atoi __P((char **)); static frentry_t ircnatfr; @@ -27,19 +27,19 @@ int irc_proxy_init = 0; /* * Initialize local structures. */ -int ippr_irc_init() +void +ipf_p_irc_main_load() { bzero((char *)&ircnatfr, sizeof(ircnatfr)); ircnatfr.fr_ref = 1; ircnatfr.fr_flags = FR_INQUE|FR_PASS|FR_QUICK|FR_KEEPSTATE; MUTEX_INIT(&ircnatfr.fr_lock, "IRC proxy rule lock"); irc_proxy_init = 1; - - return 0; } -void ippr_irc_fini() +void +ipf_p_irc_main_unload() { if (irc_proxy_init == 1) { MUTEX_DESTROY(&ircnatfr.fr_lock); @@ -48,7 +48,7 @@ void ippr_irc_fini() } -const char *ippr_irc_dcctypes[] = { +const char *ipf_p_irc_dcctypes[] = { "CHAT ", /* CHAT chat ipnumber portnumber */ "SEND ", /* SEND filename ipnumber portnumber */ "MOVE ", @@ -64,10 +64,11 @@ const char *ippr_irc_dcctypes[] = { */ -int ippr_irc_complete(ircp, buf, len) -ircinfo_t *ircp; -char *buf; -size_t len; +int +ipf_p_irc_complete(ircp, buf, len) + ircinfo_t *ircp; + char *buf; + size_t len; { register char *s, c; register size_t i; @@ -145,12 +146,12 @@ size_t len; /* * Check for a recognised DCC command */ - for (j = 0, k = 0; ippr_irc_dcctypes[j]; j++) { - k = MIN(strlen(ippr_irc_dcctypes[j]), i); - if (!strncmp(ippr_irc_dcctypes[j], s, k)) + for (j = 0, k = 0; ipf_p_irc_dcctypes[j]; j++) { + k = MIN(strlen(ipf_p_irc_dcctypes[j]), i); + if (!strncmp(ipf_p_irc_dcctypes[j], s, k)) break; } - if (!ippr_irc_dcctypes[j]) + if (!ipf_p_irc_dcctypes[j]) return 0; ircp->irc_type = s; @@ -222,18 +223,22 @@ size_t len; } -int ippr_irc_new(fin, aps, nat) -fr_info_t *fin; -ap_session_t *aps; -nat_t *nat; +int +ipf_p_irc_new(arg, fin, aps, nat) + void *arg; + fr_info_t *fin; + ap_session_t *aps; + nat_t *nat; { ircinfo_t *irc; + if (fin->fin_v != 4) + return -1; + KMALLOC(irc, ircinfo_t *); if (irc == NULL) return -1; - fin = fin; /* LINT */ nat = nat; /* LINT */ aps->aps_data = irc; @@ -244,13 +249,15 @@ nat_t *nat; } -int ippr_irc_send(fin, nat) -fr_info_t *fin; -nat_t *nat; +int +ipf_p_irc_send(fin, nat) + fr_info_t *fin; + nat_t *nat; { char ctcpbuf[IPF_IRCBUFSZ], newbuf[IPF_IRCBUFSZ]; tcphdr_t *tcp, tcph, *tcp2 = &tcph; int off, inc = 0, i, dlen; + ipf_main_softc_t *softc; size_t nlen = 0, olen; struct in_addr swip; u_short a5, sp; @@ -263,6 +270,7 @@ nat_t *nat; #ifdef MENTAT mb_t *m1; #endif + softc = fin->fin_main_soft; m = fin->fin_m; ip = fin->fin_ip; @@ -285,14 +293,14 @@ nat_t *nat; *newbuf = '\0'; irc = nat->nat_aps->aps_data; - if (ippr_irc_complete(irc, ctcpbuf, dlen) == 0) + if (ipf_p_irc_complete(irc, ctcpbuf, dlen) == 0) return 0; /* - * check that IP address in the PORT/PASV reply is the same as the - * sender of the command - prevents using PORT for port scanning. + * check that IP address in the DCC reply is the same as the + * sender of the command - prevents use for port scanning. */ - if (irc->irc_ipnum != ntohl(nat->nat_inip.s_addr)) + if (irc->irc_ipnum != ntohl(nat->nat_osrcaddr)) return 0; a5 = irc->irc_port; @@ -315,7 +323,7 @@ nat_t *nat; nlen = strlen(newbuf); inc = nlen - olen; - if ((inc + ip->ip_len) > 65535) + if ((inc + fin->fin_plen) > 65535) return 0; #ifdef MENTAT @@ -326,14 +334,14 @@ nat_t *nat; /* alloc enough to keep same trailer space for lower driver */ nm = allocb(nlen, BPRI_MED); - PANIC((!nm),("ippr_irc_out: allocb failed")); + PANIC((!nm),("ipf_p_irc_out: allocb failed")); nm->b_band = m1->b_band; nm->b_wptr += nlen; m1->b_wptr -= olen; PANIC((m1->b_wptr < m1->b_rptr), - ("ippr_irc_out: cannot handle fragmented data block")); + ("ipf_p_irc_out: cannot handle fragmented data block")); linkb(m1, nm); } else { @@ -350,13 +358,14 @@ nat_t *nat; /* the mbuf chain will be extended if necessary by m_copyback() */ #endif COPYBACK(m, off, nlen, newbuf); + fin->fin_flx |= FI_DOCKSUM; if (inc != 0) { #if defined(MENTAT) || defined(__sgi) register u_32_t sum1, sum2; - sum1 = ip->ip_len; - sum2 = ip->ip_len + inc; + sum1 = fin->fin_plen; + sum2 = fin->fin_plen + inc; /* Because ~1 == -2, We really need ~1 == -1 */ if (sum1 > sum2) @@ -364,9 +373,11 @@ nat_t *nat; sum2 -= sum1; sum2 = (sum2 & 0xffff) + (sum2 >> 16); - fix_outcksum(fin, &ip->ip_sum, sum2); + ipf_fix_outcksum(0, &ip->ip_sum, sum2, 0); #endif - ip->ip_len += inc; + fin->fin_plen += inc; + ip->ip_len = htons(fin->fin_plen); + fin->fin_dlen += inc; } /* @@ -389,16 +400,18 @@ nat_t *nat; bcopy((caddr_t)fin, (caddr_t)&fi, sizeof(fi)); fi.fin_data[0] = sp; fi.fin_data[1] = fin->fin_data[1]; - nat2 = nat_outlookup(fin, IPN_TCP, nat->nat_p, nat->nat_inip, + nat2 = ipf_nat_outlookup(fin, IPN_TCP, nat->nat_pr[1], nat->nat_nsrcip, ip->ip_dst); if (nat2 == NULL) { +#ifdef USE_MUTEXES + ipf_nat_softc_t *softn = softc->ipf_nat_soft; +#endif + bcopy((caddr_t)fin, (caddr_t)&fi, sizeof(fi)); bzero((char *)tcp2, sizeof(*tcp2)); tcp2->th_win = htons(8192); tcp2->th_sport = sp; tcp2->th_dport = 0; /* XXX - don't specify remote port */ - fi.fin_state = NULL; - fi.fin_nat = NULL; fi.fin_data[0] = ntohs(sp); fi.fin_data[1] = 0; fi.fin_dp = (char *)tcp2; @@ -406,16 +419,18 @@ nat_t *nat; fi.fin_dlen = sizeof(*tcp2); fi.fin_plen = fi.fin_hlen + sizeof(*tcp2); swip = ip->ip_src; - ip->ip_src = nat->nat_inip; - nat2 = nat_new(&fi, nat->nat_ptr, NULL, + ip->ip_src = nat->nat_nsrcip; + MUTEX_ENTER(&softn->ipf_nat_new); + nat2 = ipf_nat_add(&fi, nat->nat_ptr, NULL, NAT_SLAVE|IPN_TCP|SI_W_DPORT, NAT_OUTBOUND); + MUTEX_EXIT(&softn->ipf_nat_new); if (nat2 != NULL) { - (void) nat_proto(&fi, nat2, 0); - nat_update(&fi, nat2, nat2->nat_ptr); + (void) ipf_nat_proto(&fi, nat2, 0); + MUTEX_ENTER(&nat2->nat_lock); + ipf_nat_update(&fi, nat2); + MUTEX_EXIT(&nat2->nat_lock); - (void) fr_addstate(&fi, NULL, SI_W_DPORT); - if (fi.fin_state != NULL) - fr_statederef((ipstate_t **)&fi.fin_state); + (void) ipf_state_add(softc, &fi, NULL, SI_W_DPORT); } ip->ip_src = swip; } @@ -423,11 +438,13 @@ nat_t *nat; } -int ippr_irc_out(fin, aps, nat) -fr_info_t *fin; -ap_session_t *aps; -nat_t *nat; +int +ipf_p_irc_out(arg, fin, aps, nat) + void *arg; + fr_info_t *fin; + ap_session_t *aps; + nat_t *nat; { aps = aps; /* LINT */ - return ippr_irc_send(fin, nat); + return ipf_p_irc_send(fin, nat); } diff --git a/sys/contrib/ipfilter/netinet/ip_log.c b/sys/contrib/ipfilter/netinet/ip_log.c index 863cc9c058e..d41384d5a8f 100644 --- a/sys/contrib/ipfilter/netinet/ip_log.c +++ b/sys/contrib/ipfilter/netinet/ip_log.c @@ -1,7 +1,7 @@ /* $FreeBSD$ */ /* - * Copyright (C) 1997-2003 by Darren Reed. + * Copyright (C) 2012 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. * @@ -15,22 +15,8 @@ # define KERNEL 1 # define _KERNEL 1 #endif -#if defined(__NetBSD__) && (NetBSD >= 199905) && !defined(IPFILTER_LKM) && \ - defined(_KERNEL) -# if (__NetBSD_Version__ < 399001400) -# include "opt_ipfilter_log.h" -# else -# include "opt_ipfilter.h" -# endif -#endif -#if defined(__FreeBSD__) && !defined(IPFILTER_LKM) -# if defined(_KERNEL) -# if defined(__FreeBSD_version) && (__FreeBSD_version >= 300000) -# include "opt_ipfilter.h" -# endif -# else -# include -# endif +#if defined(__FreeBSD__) && !defined(_KERNEL) +# include #endif #ifndef SOLARIS # define SOLARIS (defined(sun) && (defined(__svr4__) || defined(__SVR4))) @@ -104,7 +90,6 @@ struct file; #if __FreeBSD_version >= 300000 # include #endif -#include #include #ifdef __sgi # include @@ -148,100 +133,240 @@ struct file; # include # include # define READ_COLLISION 0x001 - -iplog_select_t iplog_ss[IPL_LOGMAX+1]; - extern int selwait; # endif /* IPL_SELECT */ -# if defined(linux) && defined(_KERNEL) -wait_queue_head_t iplh_linux[IPL_LOGSIZE]; -# endif +typedef struct ipf_log_softc_s { + ipfmutex_t ipl_mutex[IPL_LOGSIZE]; # if SOLARIS && defined(_KERNEL) -extern kcondvar_t iplwait; -extern struct pollhead iplpollhead[IPL_LOGSIZE]; + kcondvar_t ipl_wait[IPL_LOGSIZE]; # endif +# if defined(linux) && defined(_KERNEL) + wait_queue_head_t iplh_linux[IPL_LOGSIZE]; +# endif +# if defined(__hpux) && defined(_KERNEL) + iplog_select_t ipl_ss[IPL_LOGSIZE]; +# endif + iplog_t **iplh[IPL_LOGSIZE]; + iplog_t *iplt[IPL_LOGSIZE]; + iplog_t *ipll[IPL_LOGSIZE]; + u_long ipl_logfail[IPL_LOGSIZE]; + u_long ipl_logok[IPL_LOGSIZE]; + fr_info_t ipl_crc[IPL_LOGSIZE]; + u_32_t ipl_counter[IPL_LOGSIZE]; + int ipl_suppress; + int ipl_logall; + int ipl_log_init; + int ipl_logsize; + int ipl_used[IPL_LOGSIZE]; + int ipl_magic[IPL_LOGSIZE]; + ipftuneable_t *ipf_log_tune; + int ipl_readers[IPL_LOGSIZE]; +} ipf_log_softc_t; -iplog_t **iplh[IPL_LOGSIZE], *iplt[IPL_LOGSIZE], *ipll[IPL_LOGSIZE]; -int iplused[IPL_LOGSIZE]; -static fr_info_t iplcrc[IPL_LOGSIZE]; -int ipl_suppress = 1; -int ipl_logmax = IPL_LOGMAX; -int ipl_logall = 0; -int ipl_log_init = 0; -int ipl_logsize = IPFILTER_LOGSIZE; -int ipl_magic[IPL_LOGSIZE] = { IPL_MAGIC, IPL_MAGIC_NAT, IPL_MAGIC_STATE, - IPL_MAGIC, IPL_MAGIC, IPL_MAGIC, - IPL_MAGIC, IPL_MAGIC }; +static int magic[IPL_LOGSIZE] = { IPL_MAGIC, IPL_MAGIC_NAT, IPL_MAGIC_STATE, + IPL_MAGIC, IPL_MAGIC, IPL_MAGIC, + IPL_MAGIC, IPL_MAGIC }; +static ipftuneable_t ipf_log_tuneables[] = { + /* log */ + { { (void *)offsetof(ipf_log_softc_t, ipl_suppress) }, + "log_suppress", 0, 1, + stsizeof(ipf_log_softc_t, ipl_suppress), + 0, NULL, NULL }, + { { (void *)offsetof(ipf_log_softc_t, ipl_logall) }, + "log_all", 0, 1, + stsizeof(ipf_log_softc_t, ipl_logall), + 0, NULL, NULL }, + { { (void *)offsetof(ipf_log_softc_t, ipl_logsize) }, + "log_size", 0, 0x80000, + stsizeof(ipf_log_softc_t, ipl_logsize), + 0, NULL, NULL }, + { { NULL }, NULL, 0, 0, + 0, + 0, NULL, NULL } +}; + + +int +ipf_log_main_load() +{ + return 0; +} + + +int +ipf_log_main_unload() +{ + return 0; +} /* ------------------------------------------------------------------------ */ -/* Function: fr_loginit */ -/* Returns: int - 0 == success (always returned) */ -/* Parameters: Nil */ +/* Function: ipf_log_soft_create */ +/* Returns: void * - NULL = failure, else pointer to log context data */ +/* Parameters: softc(I) - pointer to soft context main structure */ /* */ /* Initialise log buffers & pointers. Also iniialised the CRC to a local */ /* secret for use in calculating the "last log checksum". */ /* ------------------------------------------------------------------------ */ -int fr_loginit() +void * +ipf_log_soft_create(softc) + ipf_main_softc_t *softc; { - int i; + ipf_log_softc_t *softl; - for (i = IPL_LOGMAX; i >= 0; i--) { - iplt[i] = NULL; - ipll[i] = NULL; - iplh[i] = &iplt[i]; - iplused[i] = 0; - bzero((char *)&iplcrc[i], sizeof(iplcrc[i])); -# ifdef IPL_SELECT - iplog_ss[i].read_waiter = 0; - iplog_ss[i].state = 0; -# endif -# if defined(linux) && defined(_KERNEL) - init_waitqueue_head(iplh_linux + i); -# endif + KMALLOC(softl, ipf_log_softc_t *); + if (softl == NULL) + return NULL; + + bzero((char *)softl, sizeof(*softl)); + bcopy((char *)magic, (char *)softl->ipl_magic, sizeof(magic)); + + softl->ipf_log_tune = ipf_tune_array_copy(softl, + sizeof(ipf_log_tuneables), + ipf_log_tuneables); + if (softl->ipf_log_tune == NULL) { + ipf_log_soft_destroy(softc, softl); + return NULL; + } + if (ipf_tune_array_link(softc, softl->ipf_log_tune) == -1) { + ipf_log_soft_destroy(softc, softl); + return NULL; } -# if SOLARIS && defined(_KERNEL) - cv_init(&iplwait, "ipl condvar", CV_DRIVER, NULL); -# endif - MUTEX_INIT(&ipl_mutex, "ipf log mutex"); + softl->ipl_suppress = 1; + softl->ipl_logall = 0; + softl->ipl_log_init = 0; + softl->ipl_logsize = IPFILTER_LOGSIZE; - ipl_log_init = 1; + return softl; +} + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_log_soft_init */ +/* Returns: int - 0 == success (always returned) */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* */ +/* Initialise log buffers & pointers. Also iniialised the CRC to a local */ +/* secret for use in calculating the "last log checksum". */ +/* ------------------------------------------------------------------------ */ +int +ipf_log_soft_init(softc, arg) + ipf_main_softc_t *softc; + void *arg; +{ + ipf_log_softc_t *softl = arg; + int i; + + for (i = IPL_LOGMAX; i >= 0; i--) { + softl->iplt[i] = NULL; + softl->ipll[i] = NULL; + softl->iplh[i] = &softl->iplt[i]; + bzero((char *)&softl->ipl_crc[i], sizeof(softl->ipl_crc[i])); +# ifdef IPL_SELECT + softl->iplog_ss[i].read_waiter = 0; + softl->iplog_ss[i].state = 0; +# endif +# if defined(linux) && defined(_KERNEL) + init_waitqueue_head(softl->iplh_linux + i); +# endif +# if SOLARIS && defined(_KERNEL) + cv_init(&softl->ipl_wait[i], NULL, CV_DRIVER, NULL); +# endif + MUTEX_INIT(&softl->ipl_mutex[i], "ipf log mutex"); + } + + + softl->ipl_log_init = 1; return 0; } /* ------------------------------------------------------------------------ */ -/* Function: fr_logunload */ -/* Returns: Nil */ -/* Parameters: Nil */ +/* Function: ipf_log_soft_fini */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* arg(I) - pointer to log context structure */ /* */ /* Clean up any log data that has accumulated without being read. */ /* ------------------------------------------------------------------------ */ -void fr_logunload() +int +ipf_log_soft_fini(softc, arg) + ipf_main_softc_t *softc; + void *arg; { + ipf_log_softc_t *softl = arg; int i; - if (ipl_log_init == 0) - return; + if (softl->ipl_log_init == 0) + return 0; - for (i = IPL_LOGMAX; i >= 0; i--) - (void) ipflog_clear(i); + softl->ipl_log_init = 0; + for (i = IPL_LOGMAX; i >= 0; i--) { + (void) ipf_log_clear(softc, i); + + /* + * This is a busy-wait loop so as to avoid yet another lock + * to wait on. + */ + MUTEX_ENTER(&softl->ipl_mutex[i]); + while (softl->ipl_readers[i] > 0) { # if SOLARIS && defined(_KERNEL) - cv_destroy(&iplwait); + cv_broadcast(&softl->ipl_wait[i]); + MUTEX_EXIT(&softl->ipl_mutex[i]); + delay(100); + pollwakeup(&softc->ipf_poll_head[i], POLLRDNORM); +# else + MUTEX_EXIT(&softl->ipl_mutex[i]); + WAKEUP(softl->iplh, i); + POLLWAKEUP(i); # endif - MUTEX_DESTROY(&ipl_mutex); + MUTEX_ENTER(&softl->ipl_mutex[i]); + } + MUTEX_EXIT(&softl->ipl_mutex[i]); + } - ipl_log_init = 0; + return 0; } /* ------------------------------------------------------------------------ */ -/* Function: ipflog */ -/* Returns: int - 0 == success, -1 == failure */ +/* Function: ipf_log_soft_destroy */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* arg(I) - pointer to log context structure */ +/* */ +/* When this function is called, it is expected that there are no longer */ +/* any threads active in the reading code path or the logging code path. */ +/* ------------------------------------------------------------------------ */ +void +ipf_log_soft_destroy(softc, arg) + ipf_main_softc_t *softc; + void *arg; +{ + ipf_log_softc_t *softl = arg; + int i; + + for (i = IPL_LOGMAX; i >= 0; i--) { +# if SOLARIS && defined(_KERNEL) + cv_destroy(&softl->ipl_wait[i]); +# endif + MUTEX_DESTROY(&softl->ipl_mutex[i]); + } + + if (softl->ipf_log_tune != NULL) { + ipf_tune_array_unlink(softc, softl->ipf_log_tune); + KFREES(softl->ipf_log_tune, sizeof(ipf_log_tuneables)); + softl->ipf_log_tune = NULL; + } + + KFREE(softl); +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_log_pkt */ +/* Returns: int - 0 == success, -1 == failure */ /* Parameters: fin(I) - pointer to packet information */ /* flags(I) - flags from filter rules */ /* */ @@ -251,10 +376,13 @@ void fr_logunload() /* how much data to copy into the log, including part of the data body if */ /* requested. */ /* ------------------------------------------------------------------------ */ -int ipflog(fin, flags) -fr_info_t *fin; -u_int flags; +int +ipf_log_pkt(fin, flags) + fr_info_t *fin; + u_int flags; { + ipf_main_softc_t *softc = fin->fin_main_soft; + ipf_log_softc_t *softl = softc->ipf_log_soft; register size_t hlen; int types[2], mlen; size_t sizes[2]; @@ -262,8 +390,7 @@ u_int flags; ipflog_t ipfl; u_char p; mb_t *m; -# if (SOLARIS || defined(__hpux)) && defined(_KERNEL) && \ - !defined(_INET_IP_STACK_H) +# if (SOLARIS || defined(__hpux)) && defined(_KERNEL) && !defined(FW_HOOKS) qif_t *ifp; # else struct ifnet *ifp; @@ -275,10 +402,8 @@ u_int flags; ipfl.fl_nattag.ipt_num[0] = 0; ifp = fin->fin_ifp; - if (fin->fin_exthdr != NULL) - hlen = (char *)fin->fin_dp - (char *)fin->fin_ip; - else - hlen = fin->fin_hlen; + hlen = (char *)fin->fin_dp - (char *)fin->fin_ip; + /* * calculate header size. */ @@ -292,7 +417,7 @@ u_int flags; struct icmp *icmp; icmp = (struct icmp *)fin->fin_dp; - + /* * For ICMP, if the packet is an error packet, also * include the information about the packet which @@ -339,15 +464,14 @@ u_int flags; * Get the interface number and name to which this packet is * currently associated. */ -# if (SOLARIS || defined(__hpux)) && defined(_KERNEL) && \ - !defined(_INET_IP_STACK_H) +# if (SOLARIS || defined(__hpux)) && defined(_KERNEL) +# if !defined(FW_HOOKS) ipfl.fl_unit = (u_int)ifp->qf_ppa; +# endif COPYIFNAME(fin->fin_v, ifp, ipfl.fl_ifname); # else -# if (defined(NetBSD) && (NetBSD <= 1991011) && (NetBSD >= 199603)) || \ - (defined(OpenBSD) && (OpenBSD >= 199603)) || defined(linux) || \ - (defined(__FreeBSD__) && (__FreeBSD_version >= 501113)) || \ - (SOLARIS && defined(_INET_IP_STACK_H)) +# if (defined(NetBSD) && (NetBSD <= 1991011) && (NetBSD >= 199603)) || \ + OPENBSD_GE_REV(199603) || defined(linux) || FREEBSD_GE_REV(501113) COPYIFNAME(fin->fin_v, ifp, ipfl.fl_ifname); # else ipfl.fl_unit = (u_int)ifp->if_unit; @@ -357,13 +481,13 @@ u_int flags; if ((ipfl.fl_ifname[2] = ifp->if_name[2])) ipfl.fl_ifname[3] = ifp->if_name[3]; # else - COPYIFNAME(fin->fin_v, ifp, ipfl.fl_ifname); + (void) strncpy(ipfl.fl_ifname, IFNAME(ifp), sizeof(ipfl.fl_ifname)); ipfl.fl_ifname[sizeof(ipfl.fl_ifname) - 1] = '\0'; # endif # endif # endif /* __hpux || SOLARIS */ mlen = fin->fin_plen - hlen; - if (!ipl_logall) { + if (!softl->ipl_logall) { mlen = (flags & FR_LOGBODY) ? MIN(mlen, 128) : 0; } else if ((flags & FR_LOGBODY) == 0) { mlen = 0; @@ -385,8 +509,10 @@ u_int flags; bcopy(fin->fin_nattag, (void *)&ipfl.fl_nattag, sizeof(ipfl.fl_nattag)); ipfl.fl_flags = flags; + ipfl.fl_breason = (fin->fin_reason & 0xff); ipfl.fl_dir = fin->fin_out; ipfl.fl_lflags = fin->fin_flx; + ipfl.fl_family = fin->fin_family; ptrs[0] = (void *)&ipfl; sizes[0] = sizeof(ipfl); types[0] = 0; @@ -408,14 +534,15 @@ u_int flags; sizes[1] = hlen + mlen; types[1] = 1; # endif /* MENTAT */ - return ipllog(IPL_LOGIPF, fin, ptrs, sizes, types, 2); + return ipf_log_items(softc, IPL_LOGIPF, fin, ptrs, sizes, types, 2); } /* ------------------------------------------------------------------------ */ -/* Function: ipllog */ -/* Returns: int - 0 == success, -1 == failure */ -/* Parameters: dev(I) - device that owns this log record */ +/* Function: ipf_log_items */ +/* Returns: int - 0 == success, -1 == failure */ +/* Parameters: softc(I) - pointer to main soft context */ +/* unit(I) - device we are reading from */ /* fin(I) - pointer to packet information */ /* items(I) - array of pointers to log data */ /* itemsz(I) - array of size of valid memory pointed to */ @@ -426,72 +553,51 @@ u_int flags; /* miscellaneous packet information, as well as packet data, for reading */ /* from the log device. */ /* ------------------------------------------------------------------------ */ -int ipllog(dev, fin, items, itemsz, types, cnt) -int dev; -fr_info_t *fin; -void **items; -size_t *itemsz; -int *types, cnt; +int +ipf_log_items(softc, unit, fin, items, itemsz, types, cnt) + ipf_main_softc_t *softc; + int unit; + fr_info_t *fin; + void **items; + size_t *itemsz; + int *types, cnt; { - u_char *buf, *ptr; + ipf_log_softc_t *softl = softc->ipf_log_soft; + caddr_t buf, ptr; iplog_t *ipl; size_t len; int i; SPL_INT(s); - /* - * Check to see if this log record has a CRC which matches the last - * record logged. If it does, just up the count on the previous one - * rather than create a new one. - */ - if (ipl_suppress) { - MUTEX_ENTER(&ipl_mutex); - if ((fin != NULL) && (fin->fin_off == 0)) { - if ((ipll[dev] != NULL) && - bcmp((char *)fin, (char *)&iplcrc[dev], - FI_LCSIZE) == 0) { - ipll[dev]->ipl_count++; - MUTEX_EXIT(&ipl_mutex); - return 0; - } - bcopy((char *)fin, (char *)&iplcrc[dev], FI_LCSIZE); - } else - bzero((char *)&iplcrc[dev], FI_CSIZE); - MUTEX_EXIT(&ipl_mutex); - } - /* * Get the total amount of data to be logged. */ for (i = 0, len = sizeof(iplog_t); i < cnt; i++) len += itemsz[i]; + SPL_NET(s); + MUTEX_ENTER(&softl->ipl_mutex[unit]); + softl->ipl_counter[unit]++; /* * check that we have space to record this information and can * allocate that much. */ - KMALLOCS(buf, u_char *, len); - if (buf == NULL) - return -1; - SPL_NET(s); - MUTEX_ENTER(&ipl_mutex); - if ((iplused[dev] + len) > ipl_logsize) { - MUTEX_EXIT(&ipl_mutex); - SPL_X(s); - KFREES(buf, len); + if ((softl->ipl_used[unit] + len) > softl->ipl_logsize) { + softl->ipl_logfail[unit]++; + MUTEX_EXIT(&softl->ipl_mutex[unit]); return -1; } - iplused[dev] += len; - MUTEX_EXIT(&ipl_mutex); - SPL_X(s); - /* - * advance the log pointer to the next empty record and deduct the - * amount of space we're going to use. - */ + KMALLOCS(buf, caddr_t, len); + if (buf == NULL) { + softl->ipl_logfail[unit]++; + MUTEX_EXIT(&softl->ipl_mutex[unit]); + return -1; + } ipl = (iplog_t *)buf; - ipl->ipl_magic = ipl_magic[dev]; + ipl->ipl_magic = softl->ipl_magic[unit]; ipl->ipl_count = 1; + ipl->ipl_seqnum = softl->ipl_counter[unit]; ipl->ipl_next = NULL; ipl->ipl_dsize = len; #ifdef _KERNEL @@ -509,42 +615,71 @@ int *types, cnt; if (types[i] == 0) { bcopy(items[i], ptr, itemsz[i]); } else if (types[i] == 1) { - COPYDATA(items[i], 0, itemsz[i], (char *)ptr); + COPYDATA(items[i], 0, itemsz[i], ptr); } ptr += itemsz[i]; } - SPL_NET(s); - MUTEX_ENTER(&ipl_mutex); - ipll[dev] = ipl; - *iplh[dev] = ipl; - iplh[dev] = &ipl->ipl_next; + /* + * Check to see if this log record has a CRC which matches the last + * record logged. If it does, just up the count on the previous one + * rather than create a new one. + */ + if (softl->ipl_suppress) { + if ((fin != NULL) && (fin->fin_off == 0)) { + if ((softl->ipll[unit] != NULL) && + (fin->fin_crc == softl->ipl_crc[unit].fin_crc) && + bcmp((char *)fin, (char *)&softl->ipl_crc[unit], + FI_LCSIZE) == 0) { + softl->ipll[unit]->ipl_count++; + MUTEX_EXIT(&softl->ipl_mutex[unit]); + SPL_X(s); + KFREES(buf, len); + return 0; + } + bcopy((char *)fin, (char *)&softl->ipl_crc[unit], + FI_LCSIZE); + softl->ipl_crc[unit].fin_crc = fin->fin_crc; + } else + bzero((char *)&softl->ipl_crc[unit], FI_CSIZE); + } + + /* + * advance the log pointer to the next empty record and deduct the + * amount of space we're going to use. + */ + softl->ipl_logok[unit]++; + softl->ipll[unit] = ipl; + *softl->iplh[unit] = ipl; + softl->iplh[unit] = &ipl->ipl_next; + softl->ipl_used[unit] += len; /* * Now that the log record has been completed and added to the queue, * wake up any listeners who may want to read it. */ # if SOLARIS && defined(_KERNEL) - cv_signal(&iplwait); - MUTEX_EXIT(&ipl_mutex); - pollwakeup(&iplpollhead[dev], POLLRDNORM); + cv_signal(&softl->ipl_wait[unit]); + MUTEX_EXIT(&softl->ipl_mutex[unit]); + pollwakeup(&softc->ipf_poll_head[unit], POLLRDNORM); # else - MUTEX_EXIT(&ipl_mutex); - WAKEUP(iplh, dev); - POLLWAKEUP(dev); + MUTEX_EXIT(&softl->ipl_mutex[unit]); + WAKEUP(softl->iplh, unit); + POLLWAKEUP(unit); # endif SPL_X(s); # ifdef IPL_SELECT - iplog_input_ready(dev); + iplog_input_ready(unit); # endif return 0; } /* ------------------------------------------------------------------------ */ -/* Function: ipflog_read */ -/* Returns: int - 0 == success, else error value. */ -/* Parameters: unit(I) - device we are reading from */ -/* uio(O) - pointer to information about where to store data */ +/* Function: ipf_log_read */ +/* Returns: int - 0 == success, else error value. */ +/* Parameters: softc(I) - pointer to main soft context */ +/* unit(I) - device we are reading from */ +/* uio(O) - pointer to information about where to store data */ /* */ /* Called to handle a read on an IPFilter device. Returns only complete */ /* log messages - will not partially copy a log record out to userland. */ @@ -552,38 +687,58 @@ int *types, cnt; /* NOTE: This function will block and wait for a signal to return data if */ /* there is none present. Asynchronous I/O is not implemented. */ /* ------------------------------------------------------------------------ */ -int ipflog_read(unit, uio) -minor_t unit; -struct uio *uio; +int +ipf_log_read(softc, unit, uio) + ipf_main_softc_t *softc; + minor_t unit; + struct uio *uio; { + ipf_log_softc_t *softl = softc->ipf_log_soft; size_t dlen, copied; int error = 0; iplog_t *ipl; SPL_INT(s); + if (softl->ipl_log_init == 0) { + IPFERROR(40007); + return 0; + } + /* * Sanity checks. Make sure the minor # is valid and we're copying * a valid chunk of data. */ - if (IPL_LOGMAX < unit) + if (IPL_LOGMAX < unit) { + IPFERROR(40001); return ENXIO; + } if (uio->uio_resid == 0) return 0; - if ((uio->uio_resid < sizeof(iplog_t)) || - (uio->uio_resid > ipl_logsize)) + + if (uio->uio_resid < sizeof(iplog_t)) { + IPFERROR(40002); return EINVAL; + } + if (uio->uio_resid > softl->ipl_logsize) { + IPFERROR(40005); + return EINVAL; + } /* * Lock the log so we can snapshot the variables. Wait for a signal * if the log is empty. */ SPL_NET(s); - MUTEX_ENTER(&ipl_mutex); + MUTEX_ENTER(&softl->ipl_mutex[unit]); + softl->ipl_readers[unit]++; - while (iplt[unit] == NULL) { + while (softl->ipl_log_init == 1 && softl->iplt[unit] == NULL) { # if SOLARIS && defined(_KERNEL) - if (!cv_wait_sig(&iplwait, &ipl_mutex.ipf_lk)) { - MUTEX_EXIT(&ipl_mutex); + if (!cv_wait_sig(&softl->ipl_wait[unit], + &softl->ipl_mutex[unit].ipf_lk)) { + softl->ipl_readers[unit]--; + MUTEX_EXIT(&softl->ipl_mutex[unit]); + IPFERROR(40003); return EINTR; } # else @@ -593,114 +748,206 @@ struct uio *uio; # ifdef IPL_SELECT if (uio->uio_fpflags & (FNBLOCK|FNDELAY)) { /* this is no blocking system call */ - MUTEX_EXIT(&ipl_mutex); + softl->ipl_readers[unit]--; + MUTEX_EXIT(&softl->ipl_mutex[unit]); return 0; } # endif - MUTEX_EXIT(&ipl_mutex); - l = get_sleep_lock(&iplh[unit]); - error = sleep(&iplh[unit], PZERO+1); + MUTEX_EXIT(&softl->ipl_mutex[unit]); + l = get_sleep_lock(&softl->iplh[unit]); + error = sleep(&softl->iplh[unit], PZERO+1); spinunlock(l); # else # if defined(__osf__) && defined(_KERNEL) - error = mpsleep(&iplh[unit], PSUSP|PCATCH, "iplread", 0, - &ipl_mutex, MS_LOCK_SIMPLE); + error = mpsleep(&softl->iplh[unit], PSUSP|PCATCH, "ipfread", 0, + &softl->ipl_mutex, MS_LOCK_SIMPLE); # else - MUTEX_EXIT(&ipl_mutex); + MUTEX_EXIT(&softl->ipl_mutex[unit]); SPL_X(s); - error = SLEEP(unit + iplh, "ipl sleep"); + error = SLEEP(unit + softl->iplh, "ipl sleep"); # endif /* __osf__ */ # endif /* __hpux */ - if (error) - return error; SPL_NET(s); - MUTEX_ENTER(&ipl_mutex); + MUTEX_ENTER(&softl->ipl_mutex[unit]); + if (error) { + softl->ipl_readers[unit]--; + MUTEX_EXIT(&softl->ipl_mutex[unit]); + IPFERROR(40004); + return error; + } # endif /* SOLARIS */ } + if (softl->ipl_log_init != 1) { + softl->ipl_readers[unit]--; + MUTEX_EXIT(&softl->ipl_mutex[unit]); + IPFERROR(40008); + return EIO; + } -# if (BSD >= 199101) || defined(__FreeBSD__) || defined(__osf__) +# if (defined(BSD) && (BSD >= 199101)) || defined(__FreeBSD__) || \ + defined(__osf__) uio->uio_rw = UIO_READ; # endif - for (copied = 0; (ipl = iplt[unit]) != NULL; copied += dlen) { + for (copied = 0; (ipl = softl->iplt[unit]) != NULL; copied += dlen) { dlen = ipl->ipl_dsize; if (dlen > uio->uio_resid) break; /* * Don't hold the mutex over the uiomove call. */ - iplt[unit] = ipl->ipl_next; - iplused[unit] -= dlen; - MUTEX_EXIT(&ipl_mutex); + softl->iplt[unit] = ipl->ipl_next; + softl->ipl_used[unit] -= dlen; + MUTEX_EXIT(&softl->ipl_mutex[unit]); SPL_X(s); error = UIOMOVE(ipl, dlen, UIO_READ, uio); if (error) { SPL_NET(s); - MUTEX_ENTER(&ipl_mutex); - ipl->ipl_next = iplt[unit]; - iplt[unit] = ipl; - iplused[unit] += dlen; + MUTEX_ENTER(&softl->ipl_mutex[unit]); + IPFERROR(40006); + ipl->ipl_next = softl->iplt[unit]; + softl->iplt[unit] = ipl; + softl->ipl_used[unit] += dlen; break; } - MUTEX_ENTER(&ipl_mutex); - KFREES(ipl, dlen); + MUTEX_ENTER(&softl->ipl_mutex[unit]); + KFREES((caddr_t)ipl, dlen); SPL_NET(s); } - if (!iplt[unit]) { - iplused[unit] = 0; - iplh[unit] = &iplt[unit]; - ipll[unit] = NULL; + if (!softl->iplt[unit]) { + softl->ipl_used[unit] = 0; + softl->iplh[unit] = &softl->iplt[unit]; + softl->ipll[unit] = NULL; } - MUTEX_EXIT(&ipl_mutex); + softl->ipl_readers[unit]--; + MUTEX_EXIT(&softl->ipl_mutex[unit]); SPL_X(s); return error; } /* ------------------------------------------------------------------------ */ -/* Function: ipflog_clear */ -/* Returns: int - number of log bytes cleared. */ -/* Parameters: unit(I) - device we are reading from */ +/* Function: ipf_log_clear */ +/* Returns: int - number of log bytes cleared. */ +/* Parameters: softc(I) - pointer to main soft context */ +/* unit(I) - device we are reading from */ /* */ /* Deletes all queued up log records for a given output device. */ /* ------------------------------------------------------------------------ */ -int ipflog_clear(unit) -minor_t unit; +int +ipf_log_clear(softc, unit) + ipf_main_softc_t *softc; + minor_t unit; { + ipf_log_softc_t *softl = softc->ipf_log_soft; iplog_t *ipl; int used; SPL_INT(s); SPL_NET(s); - MUTEX_ENTER(&ipl_mutex); - while ((ipl = iplt[unit]) != NULL) { - iplt[unit] = ipl->ipl_next; - KFREES(ipl, ipl->ipl_dsize); + MUTEX_ENTER(&softl->ipl_mutex[unit]); + while ((ipl = softl->iplt[unit]) != NULL) { + softl->iplt[unit] = ipl->ipl_next; + KFREES((caddr_t)ipl, ipl->ipl_dsize); } - iplh[unit] = &iplt[unit]; - ipll[unit] = NULL; - used = iplused[unit]; - iplused[unit] = 0; - bzero((char *)&iplcrc[unit], FI_CSIZE); - MUTEX_EXIT(&ipl_mutex); + softl->iplh[unit] = &softl->iplt[unit]; + softl->ipll[unit] = NULL; + used = softl->ipl_used[unit]; + softl->ipl_used[unit] = 0; + bzero((char *)&softl->ipl_crc[unit], FI_CSIZE); + MUTEX_EXIT(&softl->ipl_mutex[unit]); SPL_X(s); return used; } /* ------------------------------------------------------------------------ */ -/* Function: ipflog_canread */ -/* Returns: int - 0 == no data to read, 1 = data present */ -/* Parameters: unit(I) - device we are reading from */ +/* Function: ipf_log_canread */ +/* Returns: int - 0 == no data to read, 1 = data present */ +/* Parameters: softc(I) - pointer to main soft context */ +/* unit(I) - device we are reading from */ /* */ /* Returns an indication of whether or not there is data present in the */ /* current buffer for the selected ipf device. */ /* ------------------------------------------------------------------------ */ -int ipflog_canread(unit) -int unit; +int +ipf_log_canread(softc, unit) + ipf_main_softc_t *softc; + int unit; { - return iplt[unit] != NULL; + ipf_log_softc_t *softl = softc->ipf_log_soft; + + return softl->iplt[unit] != NULL; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_log_canread */ +/* Returns: int - 0 == no data to read, 1 = data present */ +/* Parameters: softc(I) - pointer to main soft context */ +/* unit(I) - device we are reading from */ +/* */ +/* Returns how many bytes are currently held in log buffers for the */ +/* selected ipf device. */ +/* ------------------------------------------------------------------------ */ +int +ipf_log_bytesused(softc, unit) + ipf_main_softc_t *softc; + int unit; +{ + ipf_log_softc_t *softl = softc->ipf_log_soft; + + if (softl == NULL) + return 0; + + return softl->ipl_used[unit]; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_log_failures */ +/* Returns: U_QUAD_T - number of log failures */ +/* Parameters: softc(I) - pointer to main soft context */ +/* unit(I) - device we are reading from */ +/* */ +/* Returns how many times we've tried to log a packet but failed to do so */ +/* for the selected ipf device. */ +/* ------------------------------------------------------------------------ */ +u_long +ipf_log_failures(softc, unit) + ipf_main_softc_t *softc; + int unit; +{ + ipf_log_softc_t *softl = softc->ipf_log_soft; + + if (softl == NULL) + return 0; + + return softl->ipl_logfail[unit]; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_log_logok */ +/* Returns: U_QUAD_T - number of packets logged */ +/* Parameters: softc(I) - pointer to main soft context */ +/* unit(I) - device we are reading from */ +/* */ +/* Returns how many times we've successfully logged a packet for the */ +/* selected ipf device. */ +/* ------------------------------------------------------------------------ */ +u_long +ipf_log_logok(softc, unit) + ipf_main_softc_t *softc; + int unit; +{ + ipf_log_softc_t *softl = softc->ipf_log_soft; + + if (softl == NULL) + return 0; + + return softl->ipl_logok[unit]; } #endif /* IPFILTER_LOG */ diff --git a/sys/contrib/ipfilter/netinet/ip_lookup.c b/sys/contrib/ipfilter/netinet/ip_lookup.c index e33a6fe5cbe..45999e0447f 100644 --- a/sys/contrib/ipfilter/netinet/ip_lookup.c +++ b/sys/contrib/ipfilter/netinet/ip_lookup.c @@ -1,5 +1,6 @@ +/* $FreeBSD$ */ /* - * Copyright (C) 2002-2003 by Darren Reed. + * Copyright (C) 2012 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. */ @@ -24,7 +25,9 @@ # include #endif #if !defined(_KERNEL) +# include # include +# include # define _KERNEL # ifdef __OpenBSD__ struct file; @@ -33,106 +36,223 @@ struct file; # undef _KERNEL #endif #include -#if (defined(__osf__) || defined(AIX) || defined(__hpux) || defined(__sgi)) && defined(_KERNEL) -# include "radix_ipf_local.h" -# define _RADIX_H_ -#endif #include #if defined(__FreeBSD__) -# include -# include +# include +# include #endif #if defined(_KERNEL) # include # if !defined(__SVR4) && !defined(__svr4__) # include # endif +#else +# include "ipf.h" #endif #include #include "netinet/ip_compat.h" #include "netinet/ip_fil.h" +#include "netinet/ip_lookup.h" #include "netinet/ip_pool.h" #include "netinet/ip_htable.h" -#include "netinet/ip_lookup.h" +#include "netinet/ip_dstlist.h" /* END OF INCLUDES */ #if !defined(lint) -static const char rcsid[] = "@(#)$Id: ip_lookup.c,v 2.35.2.19 2007/10/11 09:05:51 darrenr Exp $"; +static const char rcsid[] = "@(#)$Id$"; #endif -#ifdef IPFILTER_LOOKUP -int ip_lookup_inited = 0; +/* + * In this file, ip_pool.c, ip_htable.c and ip_dstlist.c, you will find the + * range for unit is [-1,IPL_LOGMAX]. The -1 is considered to be a valid number + * and represents a "wildcard" or "all" units (IPL_LOGALL). The reason for not + * starting the numbering at 0 is because the numbers [0,IPL_LOGMAX] correspond + * to the minor device number for their respective device. Thus where there is + * array indexing on the unit, +1 is used to map [-1.IPL_LOGMAX] to + * [0.POOL_LOOKUP_MAX]. + */ +static int ipf_lookup_addnode __P((ipf_main_softc_t *, caddr_t, int)); +static int ipf_lookup_delnode __P((ipf_main_softc_t *, caddr_t, int)); +static int ipf_lookup_addtable __P((ipf_main_softc_t *, caddr_t)); +static int ipf_lookup_deltable __P((ipf_main_softc_t *, caddr_t)); +static int ipf_lookup_stats __P((ipf_main_softc_t *, caddr_t)); +static int ipf_lookup_flush __P((ipf_main_softc_t *, caddr_t)); +static int ipf_lookup_iterate __P((ipf_main_softc_t *, void *, int, void *)); +static int ipf_lookup_deltok __P((ipf_main_softc_t *, void *, int, void *)); -static int iplookup_addnode __P((caddr_t)); -static int iplookup_delnode __P((caddr_t data)); -static int iplookup_addtable __P((caddr_t)); -static int iplookup_deltable __P((caddr_t)); -static int iplookup_stats __P((caddr_t)); -static int iplookup_flush __P((caddr_t)); -static int iplookup_iterate __P((void *, int, void *)); -static int iplookup_deltok __P((void *, int, void *)); +#define MAX_BACKENDS 3 +static ipf_lookup_t *backends[MAX_BACKENDS] = { + &ipf_pool_backend, + &ipf_htable_backend, + &ipf_dstlist_backend +}; + + +typedef struct ipf_lookup_softc_s { + void *ipf_back[MAX_BACKENDS]; +} ipf_lookup_softc_t; /* ------------------------------------------------------------------------ */ -/* Function: iplookup_init */ -/* Returns: int - 0 = success, else error */ -/* Parameters: Nil */ +/* Function: ipf_lookup_init */ +/* Returns: int - 0 = success, else error */ +/* Parameters: softc(I) - pointer to soft context main structure */ /* */ /* Initialise all of the subcomponents of the lookup infrstructure. */ /* ------------------------------------------------------------------------ */ -int ip_lookup_init() +void * +ipf_lookup_soft_create(softc) + ipf_main_softc_t *softc; { + ipf_lookup_softc_t *softl; + ipf_lookup_t **l; + int i; - if (ip_pool_init() == -1) - return -1; + KMALLOC(softl, ipf_lookup_softc_t *); + if (softl == NULL) + return NULL; - RWLOCK_INIT(&ip_poolrw, "ip pool rwlock"); + bzero((char *)softl, sizeof(*softl)); - ip_lookup_inited = 1; + for (i = 0, l = backends; i < MAX_BACKENDS; i++, l++) { + softl->ipf_back[i] = (*(*l)->ipfl_create)(softc); + if (softl->ipf_back[i] == NULL) { + ipf_lookup_soft_destroy(softc, softl); + return NULL; + } + } + + return softl; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_lookup_soft_init */ +/* Returns: int - 0 = success, else error */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* arg(I) - pointer to local context to use */ +/* */ +/* Initialise all of the subcomponents of the lookup infrstructure. */ +/* ------------------------------------------------------------------------ */ +int +ipf_lookup_soft_init(softc, arg) + ipf_main_softc_t *softc; + void *arg; +{ + ipf_lookup_softc_t *softl = (ipf_lookup_softc_t *)arg; + int err = 0; + int i; + + for (i = 0; i < MAX_BACKENDS; i++) { + err = (*backends[i]->ipfl_init)(softc, softl->ipf_back[i]); + if (err != 0) + break; + } + + return err; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_lookup_soft_fini */ +/* Returns: int - 0 = success, else error */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* arg(I) - pointer to local context to use */ +/* */ +/* Call the fini function in each backend to cleanup all allocated data. */ +/* ------------------------------------------------------------------------ */ +int +ipf_lookup_soft_fini(softc, arg) + ipf_main_softc_t *softc; + void *arg; +{ + ipf_lookup_softc_t *softl = (ipf_lookup_softc_t *)arg; + int i; + + for (i = 0; i < MAX_BACKENDS; i++) { + if (softl->ipf_back[i] != NULL) + (*backends[i]->ipfl_fini)(softc, + softl->ipf_back[i]); + } return 0; } /* ------------------------------------------------------------------------ */ -/* Function: iplookup_unload */ -/* Returns: int - 0 = success, else error */ -/* Parameters: Nil */ +/* Function: ipf_lookup_expire */ +/* Returns: Nil */ +/* Parameters: softc(I) - pointer to soft context main structure */ /* */ -/* Free up all pool related memory that has been allocated whilst IPFilter */ -/* has been running. Also, do any other deinitialisation required such */ -/* ip_lookup_init() can be called again, safely. */ +/* Step through each of the backends and call their expire functions, */ +/* allowing them to delete any lifetime limited data. */ /* ------------------------------------------------------------------------ */ -void ip_lookup_unload() +void +ipf_lookup_expire(softc) + ipf_main_softc_t *softc; { - ip_pool_fini(); - fr_htable_unload(); + ipf_lookup_softc_t *softl = softc->ipf_lookup_soft; + int i; - if (ip_lookup_inited == 1) { - RW_DESTROY(&ip_poolrw); - ip_lookup_inited = 0; - } + WRITE_ENTER(&softc->ipf_poolrw); + for (i = 0; i < MAX_BACKENDS; i++) + (*backends[i]->ipfl_expire)(softc, softl->ipf_back[i]); + RWLOCK_EXIT(&softc->ipf_poolrw); } /* ------------------------------------------------------------------------ */ -/* Function: iplookup_ioctl */ +/* Function: ipf_lookup_softc_destroy */ +/* Returns: int - 0 = success, else error */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* arg(I) - pointer to local context to use */ +/* */ +/* Free up all pool related memory that has been allocated whilst IPFilter */ +/* has been running. Also, do any other deinitialisation required such */ +/* ipf_lookup_init() can be called again, safely. */ +/* ------------------------------------------------------------------------ */ +void +ipf_lookup_soft_destroy(softc, arg) + ipf_main_softc_t *softc; + void *arg; +{ + ipf_lookup_softc_t *softl = (ipf_lookup_softc_t *)arg; + int i; + + for (i = 0; i < MAX_BACKENDS; i++) { + if (softl->ipf_back[i] != NULL) + (*backends[i]->ipfl_destroy)(softc, + softl->ipf_back[i]); + } + + KFREE(softl); +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_lookup_ioctl */ /* Returns: int - 0 = success, else error */ -/* Parameters: data(IO) - pointer to ioctl data to be copied to/from user */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* arg(I) - pointer to local context to use */ +/* data(IO) - pointer to ioctl data to be copied to/from user */ /* space. */ /* cmd(I) - ioctl command number */ /* mode(I) - file mode bits used with open */ +/* uid(I) - uid of process doing ioctl */ +/* ctx(I) - pointer that represents context for uid */ /* */ /* Handle ioctl commands sent to the ioctl device. For the most part, this */ /* involves just calling another function to handle the specifics of each */ /* command. */ /* ------------------------------------------------------------------------ */ -int ip_lookup_ioctl(data, cmd, mode, uid, ctx) -caddr_t data; -ioctlcmd_t cmd; -int mode, uid; -void *ctx; +int +ipf_lookup_ioctl(softc, data, cmd, mode, uid, ctx) + ipf_main_softc_t *softc; + caddr_t data; + ioctlcmd_t cmd; + int mode, uid; + void *ctx; { int err; SPL_INT(s); @@ -145,52 +265,53 @@ void *ctx; { case SIOCLOOKUPADDNODE : case SIOCLOOKUPADDNODEW : - WRITE_ENTER(&ip_poolrw); - err = iplookup_addnode(data); - RWLOCK_EXIT(&ip_poolrw); + WRITE_ENTER(&softc->ipf_poolrw); + err = ipf_lookup_addnode(softc, data, uid); + RWLOCK_EXIT(&softc->ipf_poolrw); break; case SIOCLOOKUPDELNODE : case SIOCLOOKUPDELNODEW : - WRITE_ENTER(&ip_poolrw); - err = iplookup_delnode(data); - RWLOCK_EXIT(&ip_poolrw); + WRITE_ENTER(&softc->ipf_poolrw); + err = ipf_lookup_delnode(softc, data, uid); + RWLOCK_EXIT(&softc->ipf_poolrw); break; case SIOCLOOKUPADDTABLE : - WRITE_ENTER(&ip_poolrw); - err = iplookup_addtable(data); - RWLOCK_EXIT(&ip_poolrw); + WRITE_ENTER(&softc->ipf_poolrw); + err = ipf_lookup_addtable(softc, data); + RWLOCK_EXIT(&softc->ipf_poolrw); break; case SIOCLOOKUPDELTABLE : - WRITE_ENTER(&ip_poolrw); - err = iplookup_deltable(data); - RWLOCK_EXIT(&ip_poolrw); + WRITE_ENTER(&softc->ipf_poolrw); + err = ipf_lookup_deltable(softc, data); + RWLOCK_EXIT(&softc->ipf_poolrw); break; case SIOCLOOKUPSTAT : case SIOCLOOKUPSTATW : - WRITE_ENTER(&ip_poolrw); - err = iplookup_stats(data); - RWLOCK_EXIT(&ip_poolrw); + WRITE_ENTER(&softc->ipf_poolrw); + err = ipf_lookup_stats(softc, data); + RWLOCK_EXIT(&softc->ipf_poolrw); break; case SIOCLOOKUPFLUSH : - WRITE_ENTER(&ip_poolrw); - err = iplookup_flush(data); - RWLOCK_EXIT(&ip_poolrw); + WRITE_ENTER(&softc->ipf_poolrw); + err = ipf_lookup_flush(softc, data); + RWLOCK_EXIT(&softc->ipf_poolrw); break; case SIOCLOOKUPITER : - err = iplookup_iterate(data, uid, ctx); + err = ipf_lookup_iterate(softc, data, uid, ctx); break; case SIOCIPFDELTOK : - err = iplookup_deltok(data, uid, ctx); + err = ipf_lookup_deltok(softc, data, uid, ctx); break; default : + IPFERROR(50001); err = EINVAL; break; } @@ -200,192 +321,155 @@ void *ctx; /* ------------------------------------------------------------------------ */ -/* Function: iplookup_addnode */ +/* Function: ipf_lookup_addnode */ /* Returns: int - 0 = success, else error */ -/* Parameters: data(I) - pointer to data from ioctl call */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* data(I) - pointer to data from ioctl call */ /* */ /* Add a new data node to a lookup structure. First, check to see if the */ /* parent structure refered to by name exists and if it does, then go on to */ /* add a node to it. */ /* ------------------------------------------------------------------------ */ -static int iplookup_addnode(data) -caddr_t data; +static int +ipf_lookup_addnode(softc, data, uid) + ipf_main_softc_t *softc; + caddr_t data; + int uid; { - ip_pool_node_t node, *m; + ipf_lookup_softc_t *softl = softc->ipf_lookup_soft; iplookupop_t op; - iphtable_t *iph; - iphtent_t hte; - ip_pool_t *p; + ipf_lookup_t **l; int err; + int i; err = BCOPYIN(data, &op, sizeof(op)); - if (err != 0) + if (err != 0) { + IPFERROR(50002); return EFAULT; + } - if (op.iplo_unit < 0 || op.iplo_unit > IPL_LOGMAX) + if ((op.iplo_unit < 0 || op.iplo_unit > IPL_LOGMAX) && + (op.iplo_unit != IPLT_ALL)) { + IPFERROR(50003); return EINVAL; + } op.iplo_name[sizeof(op.iplo_name) - 1] = '\0'; - switch (op.iplo_type) - { - case IPLT_POOL : - if (op.iplo_size != sizeof(node)) - return EINVAL; - - err = COPYIN(op.iplo_struct, &node, sizeof(node)); - if (err != 0) - return EFAULT; - - p = ip_pool_find(op.iplo_unit, op.iplo_name); - if (p == NULL) - return ESRCH; - - /* - * add an entry to a pool - return an error if it already - * exists remove an entry from a pool - if it exists - * - in both cases, the pool *must* exist! - */ - m = ip_pool_findeq(p, &node.ipn_addr, &node.ipn_mask); - if (m) - return EEXIST; - err = ip_pool_insert(p, &node.ipn_addr.adf_addr, - &node.ipn_mask.adf_addr, node.ipn_info); - break; - - case IPLT_HASH : - if (op.iplo_size != sizeof(hte)) - return EINVAL; - - err = COPYIN(op.iplo_struct, &hte, sizeof(hte)); - if (err != 0) - return EFAULT; - - iph = fr_findhtable(op.iplo_unit, op.iplo_name); - if (iph == NULL) - return ESRCH; - err = fr_addhtent(iph, &hte); - break; - - default : - err = EINVAL; - break; + for (i = 0, l = backends; i < MAX_BACKENDS; i++, l++) { + if (op.iplo_type == (*l)->ipfl_type) { + err = (*(*l)->ipfl_node_add)(softc, + softl->ipf_back[i], + &op, uid); + break; + } } + + if (i == MAX_BACKENDS) { + IPFERROR(50012); + err = EINVAL; + } + return err; } /* ------------------------------------------------------------------------ */ -/* Function: iplookup_delnode */ +/* Function: ipf_lookup_delnode */ /* Returns: int - 0 = success, else error */ -/* Parameters: data(I) - pointer to data from ioctl call */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* data(I) - pointer to data from ioctl call */ /* */ /* Delete a node from a lookup table by first looking for the table it is */ /* in and then deleting the entry that gets found. */ /* ------------------------------------------------------------------------ */ -static int iplookup_delnode(data) -caddr_t data; +static int +ipf_lookup_delnode(softc, data, uid) + ipf_main_softc_t *softc; + caddr_t data; + int uid; { - ip_pool_node_t node, *m; + ipf_lookup_softc_t *softl = softc->ipf_lookup_soft; iplookupop_t op; - iphtable_t *iph; - iphtent_t hte; - ip_pool_t *p; + ipf_lookup_t **l; int err; + int i; err = BCOPYIN(data, &op, sizeof(op)); - if (err != 0) + if (err != 0) { + IPFERROR(50042); return EFAULT; + } - if (op.iplo_unit < 0 || op.iplo_unit > IPL_LOGMAX) + if ((op.iplo_unit < 0 || op.iplo_unit > IPL_LOGMAX) && + (op.iplo_unit != IPLT_ALL)) { + IPFERROR(50013); return EINVAL; + } op.iplo_name[sizeof(op.iplo_name) - 1] = '\0'; - switch (op.iplo_type) - { - case IPLT_POOL : - if (op.iplo_size != sizeof(node)) - return EINVAL; + for (i = 0, l = backends; i < MAX_BACKENDS; i++, l++) { + if (op.iplo_type == (*l)->ipfl_type) { + err = (*(*l)->ipfl_node_del)(softc, softl->ipf_back[i], + &op, uid); + break; + } + } - err = COPYIN(op.iplo_struct, &node, sizeof(node)); - if (err != 0) - return EFAULT; - - p = ip_pool_find(op.iplo_unit, op.iplo_name); - if (!p) - return ESRCH; - - m = ip_pool_findeq(p, &node.ipn_addr, &node.ipn_mask); - if (m == NULL) - return ENOENT; - err = ip_pool_remove(p, m); - break; - - case IPLT_HASH : - if (op.iplo_size != sizeof(hte)) - return EINVAL; - - err = COPYIN(op.iplo_struct, &hte, sizeof(hte)); - if (err != 0) - return EFAULT; - - iph = fr_findhtable(op.iplo_unit, op.iplo_name); - if (iph == NULL) - return ESRCH; - err = fr_delhtent(iph, &hte); - break; - - default : + if (i == MAX_BACKENDS) { + IPFERROR(50021); err = EINVAL; - break; } return err; } /* ------------------------------------------------------------------------ */ -/* Function: iplookup_addtable */ +/* Function: ipf_lookup_addtable */ /* Returns: int - 0 = success, else error */ -/* Parameters: data(I) - pointer to data from ioctl call */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* data(I) - pointer to data from ioctl call */ /* */ /* Create a new lookup table, if one doesn't already exist using the name */ /* for this one. */ /* ------------------------------------------------------------------------ */ -static int iplookup_addtable(data) -caddr_t data; +static int +ipf_lookup_addtable(softc, data) + ipf_main_softc_t *softc; + caddr_t data; { + ipf_lookup_softc_t *softl = softc->ipf_lookup_soft; iplookupop_t op; - int err; + ipf_lookup_t **l; + int err, i; err = BCOPYIN(data, &op, sizeof(op)); - if (err != 0) + if (err != 0) { + IPFERROR(50022); return EFAULT; + } - if (op.iplo_unit < 0 || op.iplo_unit > IPL_LOGMAX) + if ((op.iplo_unit < 0 || op.iplo_unit > IPL_LOGMAX) && + (op.iplo_unit != IPLT_ALL)) { + IPFERROR(50023); return EINVAL; + } op.iplo_name[sizeof(op.iplo_name) - 1] = '\0'; - switch (op.iplo_type) - { - case IPLT_POOL : - if (ip_pool_find(op.iplo_unit, op.iplo_name) != NULL) - err = EEXIST; - else - err = ip_pool_create(&op); - break; + for (i = 0, l = backends; i < MAX_BACKENDS; i++, l++) { + if (op.iplo_type == (*l)->ipfl_type) { + err = (*(*l)->ipfl_table_add)(softc, + softl->ipf_back[i], + &op); + break; + } + } - case IPLT_HASH : - if (fr_findhtable(op.iplo_unit, op.iplo_name) != NULL) - err = EEXIST; - else - err = fr_newhtable(&op); - break; - - default : + if (i == MAX_BACKENDS) { + IPFERROR(50026); err = EINVAL; - break; } /* @@ -394,8 +478,10 @@ caddr_t data; */ if ((err == 0) && ((op.iplo_arg & LOOKUP_ANON) != 0)) { err = BCOPYOUT(&op, data, sizeof(op)); - if (err != 0) + if (err != 0) { + IPFERROR(50027); err = EFAULT; + } } return err; @@ -403,260 +489,317 @@ caddr_t data; /* ------------------------------------------------------------------------ */ -/* Function: iplookup_deltable */ +/* Function: ipf_lookup_deltable */ /* Returns: int - 0 = success, else error */ -/* Parameters: data(I) - pointer to data from ioctl call */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* data(I) - pointer to data from ioctl call */ /* */ /* Decodes ioctl request to remove a particular hash table or pool and */ /* calls the relevant function to do the cleanup. */ /* ------------------------------------------------------------------------ */ -static int iplookup_deltable(data) -caddr_t data; +static int +ipf_lookup_deltable(softc, data) + ipf_main_softc_t *softc; + caddr_t data; { + ipf_lookup_softc_t *softl = softc->ipf_lookup_soft; iplookupop_t op; - int err; + ipf_lookup_t **l; + int err, i; err = BCOPYIN(data, &op, sizeof(op)); - if (err != 0) + if (err != 0) { + IPFERROR(50028); return EFAULT; + } - if (op.iplo_unit < 0 || op.iplo_unit > IPL_LOGMAX) + if ((op.iplo_unit < 0 || op.iplo_unit > IPL_LOGMAX) && + (op.iplo_unit != IPLT_ALL)) { + IPFERROR(50029); return EINVAL; + } op.iplo_name[sizeof(op.iplo_name) - 1] = '\0'; - /* - * create a new pool - fail if one already exists with - * the same # - */ - switch (op.iplo_type) - { - case IPLT_POOL : - err = ip_pool_destroy(op.iplo_unit, op.iplo_name); - break; + for (i = 0, l = backends; i < MAX_BACKENDS; i++, l++) { + if (op.iplo_type == (*l)->ipfl_type) { + err = (*(*l)->ipfl_table_del)(softc, + softl->ipf_back[i], + &op); + break; + } + } - case IPLT_HASH : - err = fr_removehtable(op.iplo_unit, op.iplo_name); - break; - - default : + if (i == MAX_BACKENDS) { + IPFERROR(50030); err = EINVAL; - break; } return err; } /* ------------------------------------------------------------------------ */ -/* Function: iplookup_stats */ +/* Function: ipf_lookup_stats */ /* Returns: int - 0 = success, else error */ -/* Parameters: data(I) - pointer to data from ioctl call */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* data(I) - pointer to data from ioctl call */ /* */ /* Copy statistical information from inside the kernel back to user space. */ /* ------------------------------------------------------------------------ */ -static int iplookup_stats(data) -caddr_t data; +static int +ipf_lookup_stats(softc, data) + ipf_main_softc_t *softc; + caddr_t data; { + ipf_lookup_softc_t *softl = softc->ipf_lookup_soft; iplookupop_t op; + ipf_lookup_t **l; int err; + int i; err = BCOPYIN(data, &op, sizeof(op)); - if (err != 0) + if (err != 0) { + IPFERROR(50031); return EFAULT; - - if (op.iplo_unit < 0 || op.iplo_unit > IPL_LOGMAX) - return EINVAL; - - switch (op.iplo_type) - { - case IPLT_POOL : - err = ip_pool_statistics(&op); - break; - - case IPLT_HASH : - err = fr_gethtablestat(&op); - break; - - default : - err = EINVAL; - break; } + + if ((op.iplo_unit < 0 || op.iplo_unit > IPL_LOGMAX) && + (op.iplo_unit != IPLT_ALL)) { + IPFERROR(50032); + return EINVAL; + } + + for (i = 0, l = backends; i < MAX_BACKENDS; i++, l++) { + if (op.iplo_type == (*l)->ipfl_type) { + err = (*(*l)->ipfl_stats_get)(softc, + softl->ipf_back[i], + &op); + break; + } + } + + if (i == MAX_BACKENDS) { + IPFERROR(50033); + err = EINVAL; + } + return err; } /* ------------------------------------------------------------------------ */ -/* Function: iplookup_flush */ +/* Function: ipf_lookup_flush */ /* Returns: int - 0 = success, else error */ -/* Parameters: data(I) - pointer to data from ioctl call */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* data(I) - pointer to data from ioctl call */ /* */ /* A flush is called when we want to flush all the nodes from a particular */ /* entry in the hash table/pool or want to remove all groups from those. */ /* ------------------------------------------------------------------------ */ -static int iplookup_flush(data) -caddr_t data; +static int +ipf_lookup_flush(softc, data) + ipf_main_softc_t *softc; + caddr_t data; { - int err, unit, num, type; + ipf_lookup_softc_t *softl = softc->ipf_lookup_soft; + int err, unit, num, type, i; iplookupflush_t flush; + ipf_lookup_t **l; err = BCOPYIN(data, &flush, sizeof(flush)); - if (err != 0) + if (err != 0) { + IPFERROR(50034); return EFAULT; + } unit = flush.iplf_unit; - if ((unit < 0 || unit > IPL_LOGMAX) && (unit != IPLT_ALL)) + if ((unit < 0 || unit > IPL_LOGMAX) && (unit != IPLT_ALL)) { + IPFERROR(50035); return EINVAL; + } flush.iplf_name[sizeof(flush.iplf_name) - 1] = '\0'; type = flush.iplf_type; + IPFERROR(50036); err = EINVAL; num = 0; - if (type == IPLT_POOL || type == IPLT_ALL) { - err = 0; - num = ip_pool_flush(&flush); - } - - if (type == IPLT_HASH || type == IPLT_ALL) { - err = 0; - num += fr_flushhtable(&flush); + for (i = 0, l = backends; i < MAX_BACKENDS; i++, l++) { + if (type == (*l)->ipfl_type || type == IPLT_ALL) { + err = 0; + num += (*(*l)->ipfl_flush)(softc, + softl->ipf_back[i], + &flush); + } } if (err == 0) { flush.iplf_count = num; err = BCOPYOUT(&flush, data, sizeof(flush)); - if (err != 0) + if (err != 0) { + IPFERROR(50037); err = EFAULT; + } } return err; } /* ------------------------------------------------------------------------ */ -/* Function: ip_lookup_delref */ +/* Function: ipf_lookup_delref */ /* Returns: void */ -/* Parameters: type(I) - table type to operate on */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* type(I) - table type to operate on */ /* ptr(I) - pointer to object to remove reference for */ /* */ /* This function organises calling the correct deref function for a given */ /* type of object being passed into it. */ /* ------------------------------------------------------------------------ */ -void ip_lookup_deref(type, ptr) -int type; -void *ptr; +void +ipf_lookup_deref(softc, type, ptr) + ipf_main_softc_t *softc; + int type; + void *ptr; { + ipf_lookup_softc_t *softl = softc->ipf_lookup_soft; + int i; + if (ptr == NULL) return; - WRITE_ENTER(&ip_poolrw); - switch (type) - { - case IPLT_POOL : - ip_pool_deref(ptr); - break; - - case IPLT_HASH : - fr_derefhtable(ptr); - break; + for (i = 0; i < MAX_BACKENDS; i++) { + if (type == backends[i]->ipfl_type) { + WRITE_ENTER(&softc->ipf_poolrw); + (*backends[i]->ipfl_table_deref)(softc, + softl->ipf_back[i], + ptr); + RWLOCK_EXIT(&softc->ipf_poolrw); + break; + } } - RWLOCK_EXIT(&ip_poolrw); } /* ------------------------------------------------------------------------ */ -/* Function: iplookup_iterate */ +/* Function: ipf_lookup_iterate */ /* Returns: int - 0 = success, else error */ -/* Parameters: data(I) - pointer to data from ioctl call */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* data(I) - pointer to data from ioctl call */ /* uid(I) - uid of caller */ /* ctx(I) - pointer to give the uid context */ /* */ /* Decodes ioctl request to step through either hash tables or pools. */ /* ------------------------------------------------------------------------ */ -static int iplookup_iterate(data, uid, ctx) -void *data; -int uid; -void *ctx; +static int +ipf_lookup_iterate(softc, data, uid, ctx) + ipf_main_softc_t *softc; + void *data; + int uid; + void *ctx; { + ipf_lookup_softc_t *softl = softc->ipf_lookup_soft; ipflookupiter_t iter; ipftoken_t *token; - int err; + int err, i; SPL_INT(s); - err = fr_inobj(data, &iter, IPFOBJ_LOOKUPITER); + err = ipf_inobj(softc, data, NULL, &iter, IPFOBJ_LOOKUPITER); if (err != 0) return err; - if (iter.ili_unit > IPL_LOGMAX) + if (iter.ili_unit < IPL_LOGALL && iter.ili_unit > IPL_LOGMAX) { + IPFERROR(50038); return EINVAL; + } - if (iter.ili_ival != IPFGENITER_LOOKUP) + if (iter.ili_ival != IPFGENITER_LOOKUP) { + IPFERROR(50039); return EINVAL; + } SPL_SCHED(s); - token = ipf_findtoken(iter.ili_key, uid, ctx); + token = ipf_token_find(softc, iter.ili_key, uid, ctx); if (token == NULL) { - RWLOCK_EXIT(&ipf_tokens); SPL_X(s); + IPFERROR(50040); return ESRCH; } - switch (iter.ili_type) - { - case IPLT_POOL : - err = ip_pool_getnext(token, &iter); - break; - case IPLT_HASH : - err = fr_htable_getnext(token, &iter); - break; - default : - err = EINVAL; - break; + for (i = 0; i < MAX_BACKENDS; i++) { + if (iter.ili_type == backends[i]->ipfl_type) { + err = (*backends[i]->ipfl_iter_next)(softc, + softl->ipf_back[i], + token, &iter); + break; + } } - RWLOCK_EXIT(&ipf_tokens); SPL_X(s); + if (i == MAX_BACKENDS) { + IPFERROR(50041); + err = EINVAL; + } + + WRITE_ENTER(&softc->ipf_tokens); + ipf_token_deref(softc, token); + RWLOCK_EXIT(&softc->ipf_tokens); + return err; } /* ------------------------------------------------------------------------ */ -/* Function: iplookup_iterderef */ -/* Returns: int - 0 = success, else error */ -/* Parameters: data(I) - pointer to data from ioctl call */ +/* Function: ipf_lookup_iterderef */ +/* Returns: void */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* type(I) - backend type to iterate through */ +/* data(I) - pointer to data from ioctl call */ /* */ /* Decodes ioctl request to remove a particular hash table or pool and */ /* calls the relevant function to do the cleanup. */ +/* Because each of the backend types has a different data structure, */ +/* iteration is limited to one type at a time (i.e. it is not permitted to */ +/* go on from pool types to hash types as part of the "get next".) */ /* ------------------------------------------------------------------------ */ -void ip_lookup_iterderef(type, data) -u_32_t type; -void *data; +void +ipf_lookup_iterderef(softc, type, data) + ipf_main_softc_t *softc; + u_32_t type; + void *data; { - iplookupiterkey_t key; + ipf_lookup_softc_t *softl = softc->ipf_lookup_soft; + struct iplookupiterkey *lkey; + iplookupiterkey_t key; + int i; key.ilik_key = type; + lkey = &key.ilik_unstr; - if (key.ilik_unstr.ilik_ival != IPFGENITER_LOOKUP) + if (lkey->ilik_ival != IPFGENITER_LOOKUP) return; - switch (key.ilik_unstr.ilik_type) - { - case IPLT_HASH : - fr_htable_iterderef((u_int)key.ilik_unstr.ilik_otype, - (int)key.ilik_unstr.ilik_unit, data); - break; - case IPLT_POOL : - ip_pool_iterderef((u_int)key.ilik_unstr.ilik_otype, - (int)key.ilik_unstr.ilik_unit, data); - break; + WRITE_ENTER(&softc->ipf_poolrw); + + for (i = 0; i < MAX_BACKENDS; i++) { + if (lkey->ilik_type == backends[i]->ipfl_type) { + (*backends[i]->ipfl_iter_deref)(softc, + softl->ipf_back[i], + lkey->ilik_otype, + lkey->ilik_unit, + data); + break; + } } + RWLOCK_EXIT(&softc->ipf_poolrw); } /* ------------------------------------------------------------------------ */ -/* Function: iplookup_deltok */ +/* Function: ipf_lookup_deltok */ /* Returns: int - 0 = success, else error */ -/* Parameters: data(I) - pointer to data from ioctl call */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* data(I) - pointer to data from ioctl call */ /* uid(I) - uid of caller */ /* ctx(I) - pointer to give the uid context */ /* */ @@ -664,32 +807,198 @@ void *data; /* "key" is a combination of the table type, iterator type and the unit for */ /* which the token was being used. */ /* ------------------------------------------------------------------------ */ -static int iplookup_deltok(data, uid, ctx) -void *data; -int uid; -void *ctx; +int +ipf_lookup_deltok(softc, data, uid, ctx) + ipf_main_softc_t *softc; + void *data; + int uid; + void *ctx; { int error, key; SPL_INT(s); SPL_SCHED(s); - error = BCOPYIN(data, &key, sizeof(key)); + error = BCOPYIN(data, &key, sizeof(key)); if (error == 0) - error = ipf_deltoken(key, uid, ctx); + error = ipf_token_del(softc, key, uid, ctx); SPL_X(s); return error; } -#else /* IPFILTER_LOOKUP */ - -/*ARGSUSED*/ -int ip_lookup_ioctl(data, cmd, mode, uid, ctx) -caddr_t data; -ioctlcmd_t cmd; -int mode, uid; -void *ctx; +/* ------------------------------------------------------------------------ */ +/* Function: ipf_lookup_res_num */ +/* Returns: void * - NULL = failure, else success. */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* unit(I) - device for which this is for */ +/* type(I) - type of lookup these parameters are for. */ +/* number(I) - table number to use when searching */ +/* funcptr(IO) - pointer to pointer for storing IP address */ +/* searching function. */ +/* */ +/* Search for the "table" number passed in amongst those configured for */ +/* that particular type. If the type is recognised then the function to */ +/* call to do the IP address search will be change, regardless of whether */ +/* or not the "table" number exists. */ +/* ------------------------------------------------------------------------ */ +void * +ipf_lookup_res_num(softc, unit, type, number, funcptr) + ipf_main_softc_t *softc; + int unit; + u_int type; + u_int number; + lookupfunc_t *funcptr; { - return EIO; + char name[FR_GROUPLEN]; + +#if defined(SNPRINTF) && defined(_KERNEL) + SNPRINTF(name, sizeof(name), "%u", number); +#else + (void) sprintf(name, "%u", number); +#endif + + return ipf_lookup_res_name(softc, unit, type, name, funcptr); } -#endif /* IPFILTER_LOOKUP */ + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_lookup_res_name */ +/* Returns: void * - NULL = failure, else success. */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* unit(I) - device for which this is for */ +/* type(I) - type of lookup these parameters are for. */ +/* name(I) - table name to use when searching */ +/* funcptr(IO) - pointer to pointer for storing IP address */ +/* searching function. */ +/* */ +/* Search for the "table" number passed in amongst those configured for */ +/* that particular type. If the type is recognised then the function to */ +/* call to do the IP address search will be changed, regardless of whether */ +/* or not the "table" number exists. */ +/* ------------------------------------------------------------------------ */ +void * +ipf_lookup_res_name(softc, unit, type, name, funcptr) + ipf_main_softc_t *softc; + int unit; + u_int type; + char *name; + lookupfunc_t *funcptr; +{ + ipf_lookup_softc_t *softl = softc->ipf_lookup_soft; + ipf_lookup_t **l; + void *ptr = NULL; + int i; + + READ_ENTER(&softc->ipf_poolrw); + + for (i = 0, l = backends; i < MAX_BACKENDS; i++, l++) { + if (type == (*l)->ipfl_type) { + ptr = (*(*l)->ipfl_select_add_ref)(softl->ipf_back[i], + unit, name); + if (ptr != NULL && funcptr != NULL) { + *funcptr = (*l)->ipfl_addr_find; + } + break; + } + } + + if (i == MAX_BACKENDS) { + ptr = NULL; + if (funcptr != NULL) + *funcptr = NULL; + } + + RWLOCK_EXIT(&softc->ipf_poolrw); + + return ptr; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_lookup_find_htable */ +/* Returns: void * - NULL = failure, else success. */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* unit(I) - device for which this is for */ +/* name(I) - table name to use when searching */ +/* */ +/* To support the group-map feature, where a hash table maps address */ +/* networks to rule group numbers, we need to expose a function that uses */ +/* only the hash table backend. */ +/* ------------------------------------------------------------------------ */ +void * +ipf_lookup_find_htable(softc, unit, name) + ipf_main_softc_t *softc; + int unit; + char *name; +{ + ipf_lookup_softc_t *softl = softc->ipf_lookup_soft; + ipf_lookup_t **l; + void *tab = NULL; + int i; + + READ_ENTER(&softc->ipf_poolrw); + + for (i = 0, l = backends; i < MAX_BACKENDS; i++, l++) + if (IPLT_HASH == (*l)->ipfl_type) { + tab = ipf_htable_find(softl->ipf_back[i], unit, name); + break; + } + + RWLOCK_EXIT(&softc->ipf_poolrw); + + return tab; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_lookup_sync */ +/* Returns: void */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* */ +/* This function is the interface that the machine dependent sync functions */ +/* call when a network interface name change occurs. It then calls the sync */ +/* functions of the lookup implementations - if they have one. */ +/* ------------------------------------------------------------------------ */ +/*ARGSUSED*/ +void +ipf_lookup_sync(softc, ifp) + ipf_main_softc_t *softc; + void *ifp; +{ + ipf_lookup_softc_t *softl = softc->ipf_lookup_soft; + ipf_lookup_t **l; + int i; + + READ_ENTER(&softc->ipf_poolrw); + + for (i = 0, l = backends; i < MAX_BACKENDS; i++, l++) + if ((*l)->ipfl_sync != NULL) + (*(*l)->ipfl_sync)(softc, softl->ipf_back[i]); + + RWLOCK_EXIT(&softc->ipf_poolrw); +} + + +#ifndef _KERNEL +void +ipf_lookup_dump(softc, arg) + ipf_main_softc_t *softc; + void *arg; +{ + ipf_lookup_softc_t *softl = softc->ipf_lookup_soft; + ipf_lookup_t **l; + int i; + + for (i = 0, l = backends; i < MAX_BACKENDS; i++, l++) + if (IPLT_POOL == (*l)->ipfl_type) { + ipf_pool_dump(softc, softl->ipf_back[i]); + break; + } + + for (i = 0, l = backends; i < MAX_BACKENDS; i++, l++) + if (IPLT_HASH == (*l)->ipfl_type) { + ipf_htable_dump(softc, softl->ipf_back[i]); + break; + } +} +#endif diff --git a/sys/contrib/ipfilter/netinet/ip_lookup.h b/sys/contrib/ipfilter/netinet/ip_lookup.h index 3886df16303..181e1bc5d17 100644 --- a/sys/contrib/ipfilter/netinet/ip_lookup.h +++ b/sys/contrib/ipfilter/netinet/ip_lookup.h @@ -1,4 +1,10 @@ - +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + * + * $Id$ + */ #ifndef __IP_LOOKUP_H__ #define __IP_LOOKUP_H__ @@ -24,6 +30,9 @@ # define SIOCLOOKUPDELNODEW _IOW(r, 68, struct iplookupop) #endif +#define LOOKUP_POOL_MAX (IPL_LOGSIZE) +#define LOOKUP_POOL_SZ (IPL_LOGSIZE + 1) + typedef struct iplookupop { int iplo_type; /* IPLT_* */ int iplo_unit; /* IPL_LOG* */ @@ -40,7 +49,7 @@ typedef struct iplookupflush { int iplf_type; /* IPLT_* */ int iplf_unit; /* IPL_LOG* */ u_int iplf_arg; - size_t iplf_count; + u_int iplf_count; char iplf_name[FR_GROUPLEN]; } iplookupflush_t; @@ -55,16 +64,18 @@ typedef struct iplookuplink { #define IPLT_NONE 0 #define IPLT_POOL 1 #define IPLT_HASH 2 +#define IPLT_DSTLIST 3 + #define IPLT_ANON 0x80000000 typedef union { struct iplookupiterkey { - char ilik_ival; + u_char ilik_ival; u_char ilik_type; /* IPLT_* */ u_char ilik_otype; - u_char ilik_unit; /* IPL_LOG* */ + signed char ilik_unit; /* IPL_LOG* */ } ilik_unstr; u_32_t ilik_key; } iplookupiterkey_t; @@ -86,10 +97,56 @@ typedef struct ipflookupiter { #define IPFLOOKUPITER_NODE 1 -extern int ip_lookup_init __P((void)); -extern int ip_lookup_ioctl __P((caddr_t, ioctlcmd_t, int, int, void *)); -extern void ip_lookup_unload __P((void)); -extern void ip_lookup_deref __P((int, void *)); -extern void ip_lookup_iterderef __P((u_32_t, void *)); +typedef struct ipf_lookup { + int ipfl_type; + void *(*ipfl_create) __P((ipf_main_softc_t *)); + void (*ipfl_destroy) __P((ipf_main_softc_t *, void *)); + int (*ipfl_init) __P((ipf_main_softc_t *, void *)); + void (*ipfl_fini) __P((ipf_main_softc_t *, void *)); + int (*ipfl_addr_find) __P((ipf_main_softc_t *, void *, + int, void *, u_int)); + size_t (*ipfl_flush) __P((ipf_main_softc_t *, void *, + iplookupflush_t *)); + int (*ipfl_iter_deref) __P((ipf_main_softc_t *, void *, + int, int, void *)); + int (*ipfl_iter_next) __P((ipf_main_softc_t *, void *, + ipftoken_t *, ipflookupiter_t *)); + int (*ipfl_node_add) __P((ipf_main_softc_t *, void *, + iplookupop_t *, int)); + int (*ipfl_node_del) __P((ipf_main_softc_t *, void *, + iplookupop_t *, int)); + int (*ipfl_stats_get) __P((ipf_main_softc_t *, void *, + iplookupop_t *)); + int (*ipfl_table_add) __P((ipf_main_softc_t *, void *, + iplookupop_t *)); + int (*ipfl_table_del) __P((ipf_main_softc_t *, void *, + iplookupop_t *)); + int (*ipfl_table_deref) __P((ipf_main_softc_t *, void *, void *)); + void *(*ipfl_table_find) __P((void *, int, char *)); + void *(*ipfl_select_add_ref) __P((void *, int, char *)); + int (*ipfl_select_node) __P((fr_info_t *, void *, u_32_t *, + frdest_t *)); + void (*ipfl_expire) __P((ipf_main_softc_t *, void *)); + void (*ipfl_sync) __P((ipf_main_softc_t *, void *)); +} ipf_lookup_t; +extern int ipf_lookup_init __P((void)); +extern int ipf_lookup_ioctl __P((ipf_main_softc_t *, caddr_t, ioctlcmd_t, int, int, void *)); +extern void ipf_lookup_main_unload __P((void)); +extern void ipf_lookup_deref __P((ipf_main_softc_t *, int, void *)); +extern void ipf_lookup_iterderef __P((ipf_main_softc_t *, u_32_t, void *)); +extern void *ipf_lookup_res_name __P((ipf_main_softc_t *, int, u_int, char *, + lookupfunc_t *)); +extern void *ipf_lookup_res_num __P((ipf_main_softc_t *, int, u_int, u_int, + lookupfunc_t *)); +extern void ipf_lookup_soft_destroy __P((ipf_main_softc_t *, void *)); +extern void *ipf_lookup_soft_create __P((ipf_main_softc_t *)); +extern int ipf_lookup_soft_init __P((ipf_main_softc_t *, void *)); +extern int ipf_lookup_soft_fini __P((ipf_main_softc_t *, void *)); +extern void *ipf_lookup_find_htable __P((ipf_main_softc_t *, int, char *)); +extern void ipf_lookup_expire __P((ipf_main_softc_t *)); +extern void ipf_lookup_sync __P((ipf_main_softc_t *, void *)); +#ifndef _KERNEL +extern void ipf_lookup_dump __P((ipf_main_softc_t *, void *)); +#endif #endif /* __IP_LOOKUP_H__ */ diff --git a/sys/contrib/ipfilter/netinet/ip_nat.c b/sys/contrib/ipfilter/netinet/ip_nat.c index f790c7d1ec3..d6647085a7e 100644 --- a/sys/contrib/ipfilter/netinet/ip_nat.c +++ b/sys/contrib/ipfilter/netinet/ip_nat.c @@ -1,7 +1,7 @@ /* $FreeBSD$ */ /* - * Copyright (C) 1995-2003 by Darren Reed. + * Copyright (C) 2012 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. */ @@ -16,30 +16,23 @@ #include #include #include -#if defined(_KERNEL) && defined(__NetBSD_Version__) && \ - (__NetBSD_Version__ >= 399002000) +#if defined(_KERNEL) && \ + (defined(__NetBSD_Version) && (__NetBSD_Version >= 399002000)) # include #endif -#if defined(__NetBSD__) && (NetBSD >= 199905) && !defined(IPFILTER_LKM) && \ - defined(_KERNEL) -#if defined(__NetBSD_Version__) && (__NetBSD_Version__ < 399001400) -# include "opt_ipfilter_log.h" -# else -# include "opt_ipfilter.h" -# endif -#endif #if !defined(_KERNEL) # include # include # include -# define _KERNEL -# ifdef __OpenBSD__ +# define KERNEL +# ifdef _OpenBSD__ struct file; # endif # include -# undef _KERNEL +# undef KERNEL #endif -#if defined(_KERNEL) && (__FreeBSD_version >= 220000) +#if defined(_KERNEL) && \ + defined(__FreeBSD_version) && (__FreeBSD_version >= 220000) # include # include #else @@ -61,7 +54,7 @@ struct file; #if defined(__SVR4) || defined(__svr4__) # include # include -# ifdef _KERNEL +# ifdef KERNEL # include # endif # include @@ -73,14 +66,10 @@ struct file; #include #if __FreeBSD_version >= 300000 # include -# if defined(_KERNEL) && !defined(IPFILTER_LKM) -# include "opt_ipfilter.h" -# endif #endif #ifdef sun # include #endif -#include #include #include #include @@ -99,17 +88,23 @@ extern struct ifnet vpnif; #include #include "netinet/ip_compat.h" #include +#include "netinet/ipl.h" #include "netinet/ip_fil.h" #include "netinet/ip_nat.h" #include "netinet/ip_frag.h" #include "netinet/ip_state.h" #include "netinet/ip_proxy.h" -#ifdef IPFILTER_SYNC +#include "netinet/ip_lookup.h" +#include "netinet/ip_dstlist.h" #include "netinet/ip_sync.h" -#endif -#if (__FreeBSD_version >= 300000) +#if FREEBSD_GE_REV(300000) # include #endif +#ifdef HAS_SYS_MD5_H +# include +#else +# include "md5.h" +#endif /* END OF INCLUDES */ #undef SOCKADDR_IN @@ -122,195 +117,396 @@ static const char rcsid[] = "@(#)$FreeBSD$"; #endif +#define NATFSUM(n,v,f) ((v) == 4 ? (n)->f.in4.s_addr : (n)->f.i6[0] + \ + (n)->f.i6[1] + (n)->f.i6[2] + (n)->f.i6[3]) +#define NBUMP(x) softn->(x)++ +#define NBUMPD(x, y) do { \ + softn->x.y++; \ + DT(y); \ + } while (0) +#define NBUMPSIDE(y,x) softn->ipf_nat_stats.ns_side[y].x++ +#define NBUMPSIDED(y,x) do { softn->ipf_nat_stats.ns_side[y].x++; \ + DT(x); } while (0) +#define NBUMPSIDEX(y,x,z) \ + do { softn->ipf_nat_stats.ns_side[y].x++; \ + DT(z); } while (0) +#define NBUMPSIDEDF(y,x)do { softn->ipf_nat_stats.ns_side[y].x++; \ + DT1(x, fr_info_t *, fin); } while (0) + +frentry_t ipfnatblock; + +static ipftuneable_t ipf_nat_tuneables[] = { + /* nat */ + { { (void *)offsetof(ipf_nat_softc_t, ipf_nat_lock) }, + "nat_lock", 0, 1, + stsizeof(ipf_nat_softc_t, ipf_nat_lock), + IPFT_RDONLY, NULL, NULL }, + { { (void *)offsetof(ipf_nat_softc_t, ipf_nat_table_sz) }, + "nat_table_size", 1, 0x7fffffff, + stsizeof(ipf_nat_softc_t, ipf_nat_table_sz), + 0, NULL, ipf_nat_rehash }, + { { (void *)offsetof(ipf_nat_softc_t, ipf_nat_table_max) }, + "nat_table_max", 1, 0x7fffffff, + stsizeof(ipf_nat_softc_t, ipf_nat_table_max), + 0, NULL, NULL }, + { { (void *)offsetof(ipf_nat_softc_t, ipf_nat_maprules_sz) }, + "nat_rules_size", 1, 0x7fffffff, + stsizeof(ipf_nat_softc_t, ipf_nat_maprules_sz), + 0, NULL, ipf_nat_rehash_rules }, + { { (void *)offsetof(ipf_nat_softc_t, ipf_nat_rdrrules_sz) }, + "rdr_rules_size", 1, 0x7fffffff, + stsizeof(ipf_nat_softc_t, ipf_nat_rdrrules_sz), + 0, NULL, ipf_nat_rehash_rules }, + { { (void *)offsetof(ipf_nat_softc_t, ipf_nat_hostmap_sz) }, + "hostmap_size", 1, 0x7fffffff, + stsizeof(ipf_nat_softc_t, ipf_nat_hostmap_sz), + 0, NULL, ipf_nat_hostmap_rehash }, + { { (void *)offsetof(ipf_nat_softc_t, ipf_nat_maxbucket) }, + "nat_maxbucket",1, 0x7fffffff, + stsizeof(ipf_nat_softc_t, ipf_nat_maxbucket), + 0, NULL, NULL }, + { { (void *)offsetof(ipf_nat_softc_t, ipf_nat_logging) }, + "nat_logging", 0, 1, + stsizeof(ipf_nat_softc_t, ipf_nat_logging), + 0, NULL, NULL }, + { { (void *)offsetof(ipf_nat_softc_t, ipf_nat_doflush) }, + "nat_doflush", 0, 1, + stsizeof(ipf_nat_softc_t, ipf_nat_doflush), + 0, NULL, NULL }, + { { (void *)offsetof(ipf_nat_softc_t, ipf_nat_table_wm_low) }, + "nat_table_wm_low", 1, 99, + stsizeof(ipf_nat_softc_t, ipf_nat_table_wm_low), + 0, NULL, NULL }, + { { (void *)offsetof(ipf_nat_softc_t, ipf_nat_table_wm_high) }, + "nat_table_wm_high", 2, 100, + stsizeof(ipf_nat_softc_t, ipf_nat_table_wm_high), + 0, NULL, NULL }, + { { 0 }, + NULL, 0, 0, + 0, + 0, NULL, NULL } +}; + /* ======================================================================== */ /* How the NAT is organised and works. */ /* */ /* Inside (interface y) NAT Outside (interface x) */ /* -------------------- -+- ------------------------------------- */ -/* Packet going | out, processsed by fr_checknatout() for x */ +/* Packet going | out, processsed by ipf_nat_checkout() for x */ /* ------------> | ------------> */ /* src=10.1.1.1 | src=192.1.1.1 */ /* | */ -/* | in, processed by fr_checknatin() for x */ +/* | in, processed by ipf_nat_checkin() for x */ /* <------------ | <------------ */ /* dst=10.1.1.1 | dst=192.1.1.1 */ /* -------------------- -+- ------------------------------------- */ -/* fr_checknatout() - changes ip_src and if required, sport */ +/* ipf_nat_checkout() - changes ip_src and if required, sport */ /* - creates a new mapping, if required. */ -/* fr_checknatin() - changes ip_dst and if required, dport */ +/* ipf_nat_checkin() - changes ip_dst and if required, dport */ /* */ /* In the NAT table, internal source is recorded as "in" and externally */ /* seen as "out". */ /* ======================================================================== */ -nat_t **nat_table[2] = { NULL, NULL }, - *nat_instances = NULL; -ipnat_t *nat_list = NULL; -u_int ipf_nattable_max = NAT_TABLE_MAX; -u_int ipf_nattable_sz = NAT_TABLE_SZ; -u_int ipf_natrules_sz = NAT_SIZE; -u_int ipf_rdrrules_sz = RDR_SIZE; -u_int ipf_hostmap_sz = HOSTMAP_SIZE; -u_int fr_nat_maxbucket = 0, - fr_nat_maxbucket_reset = 1; -u_32_t nat_masks = 0; -u_32_t rdr_masks = 0; -u_long nat_last_force_flush = 0; -ipnat_t **nat_rules = NULL; -ipnat_t **rdr_rules = NULL; -hostmap_t **ipf_hm_maptable = NULL; -hostmap_t *ipf_hm_maplist = NULL; -ipftq_t nat_tqb[IPF_TCP_NSTATES]; -ipftq_t nat_udptq; -ipftq_t nat_icmptq; -ipftq_t nat_iptq; -ipftq_t *nat_utqe = NULL; -int fr_nat_doflush = 0; -#ifdef IPFILTER_LOG -int nat_logging = 1; -#else -int nat_logging = 0; -#endif - -u_long fr_defnatage = DEF_NAT_AGE, - fr_defnatipage = 120, /* 60 seconds */ - fr_defnaticmpage = 6; /* 3 seconds */ -natstat_t nat_stats; -int fr_nat_lock = 0; -int fr_nat_init = 0; -#if SOLARIS && !defined(_INET_IP_STACK_H) +#if SOLARIS && !defined(INSTANCES) extern int pfil_delayed_copy; #endif -static int nat_flush_entry __P((void *)); -static int nat_flushtable __P((void)); -static int nat_clearlist __P((void)); -static void nat_addnat __P((struct ipnat *)); -static void nat_addrdr __P((struct ipnat *)); -static void nat_delrdr __P((struct ipnat *)); -static void nat_delnat __P((struct ipnat *)); -static int fr_natgetent __P((caddr_t, int)); -static int fr_natgetsz __P((caddr_t, int)); -static int fr_natputent __P((caddr_t, int)); -static int nat_extraflush __P((int)); -static int nat_gettable __P((char *)); -static void nat_tabmove __P((nat_t *)); -static int nat_match __P((fr_info_t *, ipnat_t *)); -static INLINE int nat_newmap __P((fr_info_t *, nat_t *, natinfo_t *)); -static INLINE int nat_newrdr __P((fr_info_t *, nat_t *, natinfo_t *)); -static hostmap_t *nat_hostmap __P((ipnat_t *, struct in_addr, - struct in_addr, struct in_addr, u_32_t)); -static int nat_icmpquerytype4 __P((int)); -static int nat_siocaddnat __P((ipnat_t *, ipnat_t **, int)); -static void nat_siocdelnat __P((ipnat_t *, ipnat_t **, int)); -static int nat_finalise __P((fr_info_t *, nat_t *, natinfo_t *, - tcphdr_t *, nat_t **, int)); -static int nat_resolverule __P((ipnat_t *)); -static nat_t *fr_natclone __P((fr_info_t *, nat_t *)); -static void nat_mssclamp __P((tcphdr_t *, u_32_t, fr_info_t *, u_short *)); -static int nat_wildok __P((nat_t *, int, int, int, int)); -static int nat_getnext __P((ipftoken_t *, ipfgeniter_t *)); -static int nat_iterator __P((ipftoken_t *, ipfgeniter_t *)); - +static int ipf_nat_flush_entry __P((ipf_main_softc_t *, void *)); +static int ipf_nat_getent __P((ipf_main_softc_t *, caddr_t, int)); +static int ipf_nat_getsz __P((ipf_main_softc_t *, caddr_t, int)); +static int ipf_nat_putent __P((ipf_main_softc_t *, caddr_t, int)); +static void ipf_nat_addmap __P((ipf_nat_softc_t *, ipnat_t *)); +static void ipf_nat_addrdr __P((ipf_nat_softc_t *, ipnat_t *)); +static int ipf_nat_builddivertmp __P((ipf_nat_softc_t *, ipnat_t *)); +static int ipf_nat_clearlist __P((ipf_main_softc_t *, ipf_nat_softc_t *)); +static int ipf_nat_cmp_rules __P((ipnat_t *, ipnat_t *)); +static int ipf_nat_decap __P((fr_info_t *, nat_t *)); +static void ipf_nat_delrule __P((ipf_main_softc_t *, ipf_nat_softc_t *, + ipnat_t *, int)); +static int ipf_nat_extraflush __P((ipf_main_softc_t *, ipf_nat_softc_t *, int)); +static int ipf_nat_finalise __P((fr_info_t *, nat_t *)); +static int ipf_nat_flushtable __P((ipf_main_softc_t *, ipf_nat_softc_t *)); +static int ipf_nat_getnext __P((ipf_main_softc_t *, ipftoken_t *, + ipfgeniter_t *, ipfobj_t *)); +static int ipf_nat_gettable __P((ipf_main_softc_t *, ipf_nat_softc_t *, + char *)); +static hostmap_t *ipf_nat_hostmap __P((ipf_nat_softc_t *, ipnat_t *, + struct in_addr, struct in_addr, + struct in_addr, u_32_t)); +static int ipf_nat_icmpquerytype __P((int)); +static int ipf_nat_iterator __P((ipf_main_softc_t *, ipftoken_t *, + ipfgeniter_t *, ipfobj_t *)); +static int ipf_nat_match __P((fr_info_t *, ipnat_t *)); +static int ipf_nat_matcharray __P((nat_t *, int *, u_long)); +static int ipf_nat_matchflush __P((ipf_main_softc_t *, ipf_nat_softc_t *, + caddr_t)); +static void ipf_nat_mssclamp __P((tcphdr_t *, u_32_t, fr_info_t *, + u_short *)); +static int ipf_nat_newmap __P((fr_info_t *, nat_t *, natinfo_t *)); +static int ipf_nat_newdivert __P((fr_info_t *, nat_t *, natinfo_t *)); +static int ipf_nat_newrdr __P((fr_info_t *, nat_t *, natinfo_t *)); +static int ipf_nat_newrewrite __P((fr_info_t *, nat_t *, natinfo_t *)); +static int ipf_nat_nextaddr __P((fr_info_t *, nat_addr_t *, u_32_t *, + u_32_t *)); +static int ipf_nat_nextaddrinit __P((ipf_main_softc_t *, char *, + nat_addr_t *, int, void *)); +static int ipf_nat_resolverule __P((ipf_main_softc_t *, ipnat_t *)); +static int ipf_nat_ruleaddrinit __P((ipf_main_softc_t *, + ipf_nat_softc_t *, ipnat_t *)); +static void ipf_nat_rule_fini __P((ipf_main_softc_t *, ipnat_t *)); +static int ipf_nat_rule_init __P((ipf_main_softc_t *, ipf_nat_softc_t *, + ipnat_t *)); +static int ipf_nat_siocaddnat __P((ipf_main_softc_t *, ipf_nat_softc_t *, + ipnat_t *, int)); +static void ipf_nat_siocdelnat __P((ipf_main_softc_t *, ipf_nat_softc_t *, + ipnat_t *, int)); +static void ipf_nat_tabmove __P((ipf_nat_softc_t *, nat_t *)); /* ------------------------------------------------------------------------ */ -/* Function: fr_natinit */ +/* Function: ipf_nat_main_load */ /* Returns: int - 0 == success, -1 == failure */ /* Parameters: Nil */ /* */ -/* Initialise all of the NAT locks, tables and other structures. */ +/* The only global NAT structure that needs to be initialised is the filter */ +/* rule that is used with blocking packets. */ /* ------------------------------------------------------------------------ */ -int fr_natinit() +int +ipf_nat_main_load() { - int i; + bzero((char *)&ipfnatblock, sizeof(ipfnatblock)); + ipfnatblock.fr_flags = FR_BLOCK|FR_QUICK; + ipfnatblock.fr_ref = 1; - KMALLOCS(nat_table[0], nat_t **, sizeof(nat_t *) * ipf_nattable_sz); - if (nat_table[0] != NULL) - bzero((char *)nat_table[0], ipf_nattable_sz * sizeof(nat_t *)); - else - return -1; + return 0; +} - KMALLOCS(nat_table[1], nat_t **, sizeof(nat_t *) * ipf_nattable_sz); - if (nat_table[1] != NULL) - bzero((char *)nat_table[1], ipf_nattable_sz * sizeof(nat_t *)); - else - return -2; - KMALLOCS(nat_rules, ipnat_t **, sizeof(ipnat_t *) * ipf_natrules_sz); - if (nat_rules != NULL) - bzero((char *)nat_rules, ipf_natrules_sz * sizeof(ipnat_t *)); - else - return -3; +/* ------------------------------------------------------------------------ */ +/* Function: ipf_nat_main_unload */ +/* Returns: int - 0 == success, -1 == failure */ +/* Parameters: Nil */ +/* */ +/* A null-op function that exists as a placeholder so that the flow in */ +/* other functions is obvious. */ +/* ------------------------------------------------------------------------ */ +int +ipf_nat_main_unload() +{ + return 0; +} - KMALLOCS(rdr_rules, ipnat_t **, sizeof(ipnat_t *) * ipf_rdrrules_sz); - if (rdr_rules != NULL) - bzero((char *)rdr_rules, ipf_rdrrules_sz * sizeof(ipnat_t *)); - else - return -4; - KMALLOCS(ipf_hm_maptable, hostmap_t **, \ - sizeof(hostmap_t *) * ipf_hostmap_sz); - if (ipf_hm_maptable != NULL) - bzero((char *)ipf_hm_maptable, - sizeof(hostmap_t *) * ipf_hostmap_sz); - else - return -5; - ipf_hm_maplist = NULL; +/* ------------------------------------------------------------------------ */ +/* Function: ipf_nat_soft_create */ +/* Returns: void * - NULL = failure, else pointer to NAT context */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* */ +/* Allocate the initial soft context structure for NAT and populate it with */ +/* some default values. Creating the tables is left until we call _init so */ +/* that sizes can be changed before we get under way. */ +/* ------------------------------------------------------------------------ */ +void * +ipf_nat_soft_create(softc) + ipf_main_softc_t *softc; +{ + ipf_nat_softc_t *softn; - KMALLOCS(nat_stats.ns_bucketlen[0], u_long *, - ipf_nattable_sz * sizeof(u_long)); - if (nat_stats.ns_bucketlen[0] == NULL) - return -6; - bzero((char *)nat_stats.ns_bucketlen[0], - ipf_nattable_sz * sizeof(u_long)); + KMALLOC(softn, ipf_nat_softc_t *); + if (softn == NULL) + return NULL; - KMALLOCS(nat_stats.ns_bucketlen[1], u_long *, - ipf_nattable_sz * sizeof(u_long)); - if (nat_stats.ns_bucketlen[1] == NULL) - return -7; + bzero((char *)softn, sizeof(*softn)); - bzero((char *)nat_stats.ns_bucketlen[1], - ipf_nattable_sz * sizeof(u_long)); - - if (fr_nat_maxbucket == 0) { - for (i = ipf_nattable_sz; i > 0; i >>= 1) - fr_nat_maxbucket++; - fr_nat_maxbucket *= 2; + softn->ipf_nat_tune = ipf_tune_array_copy(softn, + sizeof(ipf_nat_tuneables), + ipf_nat_tuneables); + if (softn->ipf_nat_tune == NULL) { + ipf_nat_soft_destroy(softc, softn); + return NULL; + } + if (ipf_tune_array_link(softc, softn->ipf_nat_tune) == -1) { + ipf_nat_soft_destroy(softc, softn); + return NULL; } - fr_sttab_init(nat_tqb); + softn->ipf_nat_list_tail = &softn->ipf_nat_list; + + softn->ipf_nat_table_max = NAT_TABLE_MAX; + softn->ipf_nat_table_sz = NAT_TABLE_SZ; + softn->ipf_nat_maprules_sz = NAT_SIZE; + softn->ipf_nat_rdrrules_sz = RDR_SIZE; + softn->ipf_nat_hostmap_sz = HOSTMAP_SIZE; + softn->ipf_nat_doflush = 0; +#ifdef IPFILTER_LOG + softn->ipf_nat_logging = 1; +#else + softn->ipf_nat_logging = 0; +#endif + + softn->ipf_nat_defage = DEF_NAT_AGE; + softn->ipf_nat_defipage = IPF_TTLVAL(60); + softn->ipf_nat_deficmpage = IPF_TTLVAL(3); + softn->ipf_nat_table_wm_high = 99; + softn->ipf_nat_table_wm_low = 90; + + return softn; +} + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_nat_soft_destroy */ +/* Returns: Nil */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* */ +/* ------------------------------------------------------------------------ */ +void +ipf_nat_soft_destroy(softc, arg) + ipf_main_softc_t *softc; + void *arg; +{ + ipf_nat_softc_t *softn = arg; + + if (softn->ipf_nat_tune != NULL) { + ipf_tune_array_unlink(softc, softn->ipf_nat_tune); + KFREES(softn->ipf_nat_tune, sizeof(ipf_nat_tuneables)); + softn->ipf_nat_tune = NULL; + } + + KFREE(softn); +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_nat_init */ +/* Returns: int - 0 == success, -1 == failure */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* */ +/* Initialise all of the NAT locks, tables and other structures. */ +/* ------------------------------------------------------------------------ */ +int +ipf_nat_soft_init(softc, arg) + ipf_main_softc_t *softc; + void *arg; +{ + ipf_nat_softc_t *softn = arg; + ipftq_t *tq; + int i; + + KMALLOCS(softn->ipf_nat_table[0], nat_t **, \ + sizeof(nat_t *) * softn->ipf_nat_table_sz); + + if (softn->ipf_nat_table[0] != NULL) { + bzero((char *)softn->ipf_nat_table[0], + softn->ipf_nat_table_sz * sizeof(nat_t *)); + } else { + return -1; + } + + KMALLOCS(softn->ipf_nat_table[1], nat_t **, \ + sizeof(nat_t *) * softn->ipf_nat_table_sz); + + if (softn->ipf_nat_table[1] != NULL) { + bzero((char *)softn->ipf_nat_table[1], + softn->ipf_nat_table_sz * sizeof(nat_t *)); + } else { + return -2; + } + + KMALLOCS(softn->ipf_nat_map_rules, ipnat_t **, \ + sizeof(ipnat_t *) * softn->ipf_nat_maprules_sz); + + if (softn->ipf_nat_map_rules != NULL) { + bzero((char *)softn->ipf_nat_map_rules, + softn->ipf_nat_maprules_sz * sizeof(ipnat_t *)); + } else { + return -3; + } + + KMALLOCS(softn->ipf_nat_rdr_rules, ipnat_t **, \ + sizeof(ipnat_t *) * softn->ipf_nat_rdrrules_sz); + + if (softn->ipf_nat_rdr_rules != NULL) { + bzero((char *)softn->ipf_nat_rdr_rules, + softn->ipf_nat_rdrrules_sz * sizeof(ipnat_t *)); + } else { + return -4; + } + + KMALLOCS(softn->ipf_hm_maptable, hostmap_t **, \ + sizeof(hostmap_t *) * softn->ipf_nat_hostmap_sz); + + if (softn->ipf_hm_maptable != NULL) { + bzero((char *)softn->ipf_hm_maptable, + sizeof(hostmap_t *) * softn->ipf_nat_hostmap_sz); + } else { + return -5; + } + softn->ipf_hm_maplist = NULL; + + KMALLOCS(softn->ipf_nat_stats.ns_side[0].ns_bucketlen, u_int *, + softn->ipf_nat_table_sz * sizeof(u_int)); + + if (softn->ipf_nat_stats.ns_side[0].ns_bucketlen == NULL) { + return -6; + } + bzero((char *)softn->ipf_nat_stats.ns_side[0].ns_bucketlen, + softn->ipf_nat_table_sz * sizeof(u_int)); + + KMALLOCS(softn->ipf_nat_stats.ns_side[1].ns_bucketlen, u_int *, + softn->ipf_nat_table_sz * sizeof(u_int)); + + if (softn->ipf_nat_stats.ns_side[1].ns_bucketlen == NULL) { + return -7; + } + + bzero((char *)softn->ipf_nat_stats.ns_side[1].ns_bucketlen, + softn->ipf_nat_table_sz * sizeof(u_int)); + + if (softn->ipf_nat_maxbucket == 0) { + for (i = softn->ipf_nat_table_sz; i > 0; i >>= 1) + softn->ipf_nat_maxbucket++; + softn->ipf_nat_maxbucket *= 2; + } + + ipf_sttab_init(softc, softn->ipf_nat_tcptq); /* * Increase this because we may have "keep state" following this too * and packet storms can occur if this is removed too quickly. */ - nat_tqb[IPF_TCPS_CLOSED].ifq_ttl = fr_tcplastack; - nat_tqb[IPF_TCP_NSTATES - 1].ifq_next = &nat_udptq; - nat_udptq.ifq_ttl = fr_defnatage; - nat_udptq.ifq_ref = 1; - nat_udptq.ifq_head = NULL; - nat_udptq.ifq_tail = &nat_udptq.ifq_head; - MUTEX_INIT(&nat_udptq.ifq_lock, "nat ipftq udp tab"); - nat_udptq.ifq_next = &nat_icmptq; - nat_icmptq.ifq_ttl = fr_defnaticmpage; - nat_icmptq.ifq_ref = 1; - nat_icmptq.ifq_head = NULL; - nat_icmptq.ifq_tail = &nat_icmptq.ifq_head; - MUTEX_INIT(&nat_icmptq.ifq_lock, "nat icmp ipftq tab"); - nat_icmptq.ifq_next = &nat_iptq; - nat_iptq.ifq_ttl = fr_defnatipage; - nat_iptq.ifq_ref = 1; - nat_iptq.ifq_head = NULL; - nat_iptq.ifq_tail = &nat_iptq.ifq_head; - MUTEX_INIT(&nat_iptq.ifq_lock, "nat ip ipftq tab"); - nat_iptq.ifq_next = NULL; + softn->ipf_nat_tcptq[IPF_TCPS_CLOSED].ifq_ttl = softc->ipf_tcplastack; + softn->ipf_nat_tcptq[IPF_TCP_NSTATES - 1].ifq_next = + &softn->ipf_nat_udptq; - for (i = 0; i < IPF_TCP_NSTATES; i++) { - if (nat_tqb[i].ifq_ttl < fr_defnaticmpage) - nat_tqb[i].ifq_ttl = fr_defnaticmpage; + IPFTQ_INIT(&softn->ipf_nat_udptq, softn->ipf_nat_defage, + "nat ipftq udp tab"); + softn->ipf_nat_udptq.ifq_next = &softn->ipf_nat_udpacktq; + + IPFTQ_INIT(&softn->ipf_nat_udpacktq, softn->ipf_nat_defage, + "nat ipftq udpack tab"); + softn->ipf_nat_udpacktq.ifq_next = &softn->ipf_nat_icmptq; + + IPFTQ_INIT(&softn->ipf_nat_icmptq, softn->ipf_nat_deficmpage, + "nat icmp ipftq tab"); + softn->ipf_nat_icmptq.ifq_next = &softn->ipf_nat_icmpacktq; + + IPFTQ_INIT(&softn->ipf_nat_icmpacktq, softn->ipf_nat_defage, + "nat icmpack ipftq tab"); + softn->ipf_nat_icmpacktq.ifq_next = &softn->ipf_nat_iptq; + + IPFTQ_INIT(&softn->ipf_nat_iptq, softn->ipf_nat_defipage, + "nat ip ipftq tab"); + softn->ipf_nat_iptq.ifq_next = &softn->ipf_nat_pending; + + IPFTQ_INIT(&softn->ipf_nat_pending, 1, "nat pending ipftq tab"); + softn->ipf_nat_pending.ifq_next = NULL; + + for (i = 0, tq = softn->ipf_nat_tcptq; i < IPF_TCP_NSTATES; i++, tq++) { + if (tq->ifq_ttl < softn->ipf_nat_deficmpage) + tq->ifq_ttl = softn->ipf_nat_deficmpage; #ifdef LARGE_NAT - else if (nat_tqb[i].ifq_ttl > fr_defnatage) - nat_tqb[i].ifq_ttl = fr_defnatage; + else if (tq->ifq_ttl > softn->ipf_nat_defage) + tq->ifq_ttl = softn->ipf_nat_defage; #endif } @@ -319,21 +515,124 @@ int fr_natinit() * this too and packet storms can occur if this is removed * too quickly. */ - nat_tqb[IPF_TCPS_CLOSED].ifq_ttl = nat_tqb[IPF_TCPS_LAST_ACK].ifq_ttl; + softn->ipf_nat_tcptq[IPF_TCPS_CLOSED].ifq_ttl = softc->ipf_tcplastack; - RWLOCK_INIT(&ipf_nat, "ipf IP NAT rwlock"); - RWLOCK_INIT(&ipf_natfrag, "ipf IP NAT-Frag rwlock"); - MUTEX_INIT(&ipf_nat_new, "ipf nat new mutex"); - MUTEX_INIT(&ipf_natio, "ipf nat io mutex"); + MUTEX_INIT(&softn->ipf_nat_new, "ipf nat new mutex"); + MUTEX_INIT(&softn->ipf_nat_io, "ipf nat io mutex"); - fr_nat_init = 1; + softn->ipf_nat_inited = 1; return 0; } /* ------------------------------------------------------------------------ */ -/* Function: nat_addrdr */ +/* Function: ipf_nat_soft_fini */ +/* Returns: Nil */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* */ +/* Free all memory used by NAT structures allocated at runtime. */ +/* ------------------------------------------------------------------------ */ +int +ipf_nat_soft_fini(softc, arg) + ipf_main_softc_t *softc; + void *arg; +{ + ipf_nat_softc_t *softn = arg; + ipftq_t *ifq, *ifqnext; + + (void) ipf_nat_clearlist(softc, softn); + (void) ipf_nat_flushtable(softc, softn); + + /* + * Proxy timeout queues are not cleaned here because although they + * exist on the NAT list, ipf_proxy_unload is called after unload + * and the proxies actually are responsible for them being created. + * Should the proxy timeouts have their own list? There's no real + * justification as this is the only complication. + */ + for (ifq = softn->ipf_nat_utqe; ifq != NULL; ifq = ifqnext) { + ifqnext = ifq->ifq_next; + if (ipf_deletetimeoutqueue(ifq) == 0) + ipf_freetimeoutqueue(softc, ifq); + } + + if (softn->ipf_nat_table[0] != NULL) { + KFREES(softn->ipf_nat_table[0], + sizeof(nat_t *) * softn->ipf_nat_table_sz); + softn->ipf_nat_table[0] = NULL; + } + if (softn->ipf_nat_table[1] != NULL) { + KFREES(softn->ipf_nat_table[1], + sizeof(nat_t *) * softn->ipf_nat_table_sz); + softn->ipf_nat_table[1] = NULL; + } + if (softn->ipf_nat_map_rules != NULL) { + KFREES(softn->ipf_nat_map_rules, + sizeof(ipnat_t *) * softn->ipf_nat_maprules_sz); + softn->ipf_nat_map_rules = NULL; + } + if (softn->ipf_nat_rdr_rules != NULL) { + KFREES(softn->ipf_nat_rdr_rules, + sizeof(ipnat_t *) * softn->ipf_nat_rdrrules_sz); + softn->ipf_nat_rdr_rules = NULL; + } + if (softn->ipf_hm_maptable != NULL) { + KFREES(softn->ipf_hm_maptable, + sizeof(hostmap_t *) * softn->ipf_nat_hostmap_sz); + softn->ipf_hm_maptable = NULL; + } + if (softn->ipf_nat_stats.ns_side[0].ns_bucketlen != NULL) { + KFREES(softn->ipf_nat_stats.ns_side[0].ns_bucketlen, + sizeof(u_int) * softn->ipf_nat_table_sz); + softn->ipf_nat_stats.ns_side[0].ns_bucketlen = NULL; + } + if (softn->ipf_nat_stats.ns_side[1].ns_bucketlen != NULL) { + KFREES(softn->ipf_nat_stats.ns_side[1].ns_bucketlen, + sizeof(u_int) * softn->ipf_nat_table_sz); + softn->ipf_nat_stats.ns_side[1].ns_bucketlen = NULL; + } + + if (softn->ipf_nat_inited == 1) { + softn->ipf_nat_inited = 0; + ipf_sttab_destroy(softn->ipf_nat_tcptq); + + MUTEX_DESTROY(&softn->ipf_nat_new); + MUTEX_DESTROY(&softn->ipf_nat_io); + + MUTEX_DESTROY(&softn->ipf_nat_udptq.ifq_lock); + MUTEX_DESTROY(&softn->ipf_nat_udpacktq.ifq_lock); + MUTEX_DESTROY(&softn->ipf_nat_icmptq.ifq_lock); + MUTEX_DESTROY(&softn->ipf_nat_icmpacktq.ifq_lock); + MUTEX_DESTROY(&softn->ipf_nat_iptq.ifq_lock); + MUTEX_DESTROY(&softn->ipf_nat_pending.ifq_lock); + } + + return 0; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_nat_setlock */ +/* Returns: Nil */ +/* Parameters: arg(I) - pointer to soft state information */ +/* tmp(I) - new lock value */ +/* */ +/* Set the "lock status" of NAT to the value in tmp. */ +/* ------------------------------------------------------------------------ */ +void +ipf_nat_setlock(arg, tmp) + void *arg; + int tmp; +{ + ipf_nat_softc_t *softn = arg; + + softn->ipf_nat_lock = tmp; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_nat_addrdr */ /* Returns: Nil */ /* Parameters: n(I) - pointer to NAT rule to add */ /* */ @@ -341,31 +640,41 @@ int fr_natinit() /* loaded NAT rules. Updates the bitmask indicating which netmasks are in */ /* use by redirect rules. */ /* ------------------------------------------------------------------------ */ -static void nat_addrdr(n) -ipnat_t *n; +static void +ipf_nat_addrdr(softn, n) + ipf_nat_softc_t *softn; + ipnat_t *n; { ipnat_t **np; u_32_t j; u_int hv; + u_int rhv; int k; - k = count4bits(n->in_outmsk); - if ((k >= 0) && (k != 32)) - rdr_masks |= 1 << k; - j = (n->in_outip & n->in_outmsk); - hv = NAT_HASH_FN(j, 0, ipf_rdrrules_sz); - np = rdr_rules + hv; + if (n->in_odstatype == FRI_NORMAL) { + k = count4bits(n->in_odstmsk); + ipf_inet_mask_add(k, &softn->ipf_nat_rdr_mask); + j = (n->in_odstaddr & n->in_odstmsk); + rhv = NAT_HASH_FN(j, 0, 0xffffffff); + } else { + ipf_inet_mask_add(0, &softn->ipf_nat_rdr_mask); + j = 0; + rhv = 0; + } + hv = rhv % softn->ipf_nat_rdrrules_sz; + np = softn->ipf_nat_rdr_rules + hv; while (*np != NULL) np = &(*np)->in_rnext; n->in_rnext = NULL; n->in_prnext = np; - n->in_hv = hv; + n->in_hv[0] = hv; + n->in_use++; *np = n; } /* ------------------------------------------------------------------------ */ -/* Function: nat_addnat */ +/* Function: ipf_nat_addmap */ /* Returns: Nil */ /* Parameters: n(I) - pointer to NAT rule to add */ /* */ @@ -373,63 +682,91 @@ ipnat_t *n; /* NAT rules. Updates the bitmask indicating which netmasks are in use by */ /* redirect rules. */ /* ------------------------------------------------------------------------ */ -static void nat_addnat(n) -ipnat_t *n; +static void +ipf_nat_addmap(softn, n) + ipf_nat_softc_t *softn; + ipnat_t *n; { ipnat_t **np; u_32_t j; u_int hv; + u_int rhv; int k; - k = count4bits(n->in_inmsk); - if ((k >= 0) && (k != 32)) - nat_masks |= 1 << k; - j = (n->in_inip & n->in_inmsk); - hv = NAT_HASH_FN(j, 0, ipf_natrules_sz); - np = nat_rules + hv; + if (n->in_osrcatype == FRI_NORMAL) { + k = count4bits(n->in_osrcmsk); + ipf_inet_mask_add(k, &softn->ipf_nat_map_mask); + j = (n->in_osrcaddr & n->in_osrcmsk); + rhv = NAT_HASH_FN(j, 0, 0xffffffff); + } else { + ipf_inet_mask_add(0, &softn->ipf_nat_map_mask); + j = 0; + rhv = 0; + } + hv = rhv % softn->ipf_nat_maprules_sz; + np = softn->ipf_nat_map_rules + hv; while (*np != NULL) np = &(*np)->in_mnext; n->in_mnext = NULL; n->in_pmnext = np; - n->in_hv = hv; + n->in_hv[1] = rhv; + n->in_use++; *np = n; } /* ------------------------------------------------------------------------ */ -/* Function: nat_delrdr */ +/* Function: ipf_nat_delrdr */ /* Returns: Nil */ /* Parameters: n(I) - pointer to NAT rule to delete */ /* */ /* Removes a redirect rule from the hash table of redirect rules. */ /* ------------------------------------------------------------------------ */ -static void nat_delrdr(n) -ipnat_t *n; +void +ipf_nat_delrdr(softn, n) + ipf_nat_softc_t *softn; + ipnat_t *n; { + if (n->in_odstatype == FRI_NORMAL) { + int k = count4bits(n->in_odstmsk); + ipf_inet_mask_del(k, &softn->ipf_nat_rdr_mask); + } else { + ipf_inet_mask_del(0, &softn->ipf_nat_rdr_mask); + } if (n->in_rnext) n->in_rnext->in_prnext = n->in_prnext; *n->in_prnext = n->in_rnext; + n->in_use--; } /* ------------------------------------------------------------------------ */ -/* Function: nat_delnat */ +/* Function: ipf_nat_delmap */ /* Returns: Nil */ /* Parameters: n(I) - pointer to NAT rule to delete */ /* */ /* Removes a NAT map rule from the hash table of NAT map rules. */ /* ------------------------------------------------------------------------ */ -static void nat_delnat(n) -ipnat_t *n; +void +ipf_nat_delmap(softn, n) + ipf_nat_softc_t *softn; + ipnat_t *n; { + if (n->in_osrcatype == FRI_NORMAL) { + int k = count4bits(n->in_osrcmsk); + ipf_inet_mask_del(k, &softn->ipf_nat_map_mask); + } else { + ipf_inet_mask_del(0, &softn->ipf_nat_map_mask); + } if (n->in_mnext != NULL) n->in_mnext->in_pmnext = n->in_pmnext; *n->in_pmnext = n->in_mnext; + n->in_use--; } /* ------------------------------------------------------------------------ */ -/* Function: nat_hostmap */ +/* Function: ipf_nat_hostmap */ /* Returns: struct hostmap* - NULL if no hostmap could be created, */ /* else a pointer to the hostmapping to use */ /* Parameters: np(I) - pointer to NAT rule */ @@ -442,57 +779,70 @@ ipnat_t *n; /* that is not doing port based translation. If is not yet allocated, then */ /* create a new entry if a non-NULL NAT rule pointer has been supplied. */ /* ------------------------------------------------------------------------ */ -static struct hostmap *nat_hostmap(np, src, dst, map, port) -ipnat_t *np; -struct in_addr src; -struct in_addr dst; -struct in_addr map; -u_32_t port; +static struct hostmap * +ipf_nat_hostmap(softn, np, src, dst, map, port) + ipf_nat_softc_t *softn; + ipnat_t *np; + struct in_addr src; + struct in_addr dst; + struct in_addr map; + u_32_t port; { hostmap_t *hm; - u_int hv; + u_int hv, rhv; hv = (src.s_addr ^ dst.s_addr); hv += src.s_addr; hv += dst.s_addr; - hv %= HOSTMAP_SIZE; - for (hm = ipf_hm_maptable[hv]; hm; hm = hm->hm_next) - if ((hm->hm_srcip.s_addr == src.s_addr) && - (hm->hm_dstip.s_addr == dst.s_addr) && + rhv = hv; + hv %= softn->ipf_nat_hostmap_sz; + for (hm = softn->ipf_hm_maptable[hv]; hm; hm = hm->hm_hnext) + if ((hm->hm_osrcip.s_addr == src.s_addr) && + (hm->hm_odstip.s_addr == dst.s_addr) && ((np == NULL) || (np == hm->hm_ipnat)) && ((port == 0) || (port == hm->hm_port))) { + softn->ipf_nat_stats.ns_hm_addref++; hm->hm_ref++; return hm; } - if (np == NULL) + if (np == NULL) { + softn->ipf_nat_stats.ns_hm_nullnp++; return NULL; + } KMALLOC(hm, hostmap_t *); if (hm) { - hm->hm_next = ipf_hm_maplist; - hm->hm_pnext = &ipf_hm_maplist; - if (ipf_hm_maplist != NULL) - ipf_hm_maplist->hm_pnext = &hm->hm_next; - ipf_hm_maplist = hm; - hm->hm_hnext = ipf_hm_maptable[hv]; - hm->hm_phnext = ipf_hm_maptable + hv; - if (ipf_hm_maptable[hv] != NULL) - ipf_hm_maptable[hv]->hm_phnext = &hm->hm_hnext; - ipf_hm_maptable[hv] = hm; + hm->hm_next = softn->ipf_hm_maplist; + hm->hm_pnext = &softn->ipf_hm_maplist; + if (softn->ipf_hm_maplist != NULL) + softn->ipf_hm_maplist->hm_pnext = &hm->hm_next; + softn->ipf_hm_maplist = hm; + hm->hm_hnext = softn->ipf_hm_maptable[hv]; + hm->hm_phnext = softn->ipf_hm_maptable + hv; + if (softn->ipf_hm_maptable[hv] != NULL) + softn->ipf_hm_maptable[hv]->hm_phnext = &hm->hm_hnext; + softn->ipf_hm_maptable[hv] = hm; hm->hm_ipnat = np; - hm->hm_srcip = src; - hm->hm_dstip = dst; - hm->hm_mapip = map; + np->in_use++; + hm->hm_osrcip = src; + hm->hm_odstip = dst; + hm->hm_nsrcip = map; + hm->hm_ndstip.s_addr = 0; hm->hm_ref = 1; hm->hm_port = port; + hm->hm_hv = rhv; + hm->hm_v = 4; + softn->ipf_nat_stats.ns_hm_new++; + } else { + softn->ipf_nat_stats.ns_hm_newfail++; } return hm; } /* ------------------------------------------------------------------------ */ -/* Function: fr_hostmapdel */ +/* Function: ipf_nat_hostmapdel */ /* Returns: Nil */ /* Parameters: hmp(I) - pointer to hostmap structure pointer */ /* Write Locks: ipf_nat */ @@ -500,8 +850,10 @@ u_32_t port; /* Decrement the references to this hostmap structure by one. If this */ /* reaches zero then remove it and free it. */ /* ------------------------------------------------------------------------ */ -void fr_hostmapdel(hmp) -struct hostmap **hmp; +void +ipf_nat_hostmapdel(softc, hmp) + ipf_main_softc_t *softc; + struct hostmap **hmp; { struct hostmap *hm; @@ -510,6 +862,7 @@ struct hostmap **hmp; hm->hm_ref--; if (hm->hm_ref == 0) { + ipf_nat_rule_deref(softc, &hm->hm_ipnat); if (hm->hm_hnext) hm->hm_hnext->hm_phnext = hm->hm_phnext; *hm->hm_phnext = hm->hm_hnext; @@ -522,7 +875,7 @@ struct hostmap **hmp; /* ------------------------------------------------------------------------ */ -/* Function: fix_outcksum */ +/* Function: ipf_fix_outcksum */ /* Returns: Nil */ /* Parameters: fin(I) - pointer to packet information */ /* sp(I) - location of 16bit checksum to update */ @@ -530,10 +883,11 @@ struct hostmap **hmp; /* */ /* Adjusts the 16bit checksum by "n" for packets going out. */ /* ------------------------------------------------------------------------ */ -void fix_outcksum(fin, sp, n) -fr_info_t *fin; -u_short *sp; -u_32_t n; +void +ipf_fix_outcksum(cksum, sp, n, partial) + int cksum; + u_short *sp; + u_32_t n, partial; { u_short sumshort; u_32_t sum1; @@ -541,11 +895,14 @@ u_32_t n; if (n == 0) return; - if (n & NAT_HW_CKSUM) { - n &= 0xffff; - n += fin->fin_dlen; - n = (n & 0xffff) + (n >> 16); - *sp = n & 0xffff; + if (cksum == 4) { + *sp = 0; + return; + } + if (cksum == 2) { + sum1 = partial; + sum1 = (sum1 & 0xffff) + (sum1 >> 16); + *sp = htons(sum1); return; } sum1 = (~ntohs(*sp)) & 0xffff; @@ -559,7 +916,7 @@ u_32_t n; /* ------------------------------------------------------------------------ */ -/* Function: fix_incksum */ +/* Function: ipf_fix_incksum */ /* Returns: Nil */ /* Parameters: fin(I) - pointer to packet information */ /* sp(I) - location of 16bit checksum to update */ @@ -567,10 +924,11 @@ u_32_t n; /* */ /* Adjusts the 16bit checksum by "n" for packets going in. */ /* ------------------------------------------------------------------------ */ -void fix_incksum(fin, sp, n) -fr_info_t *fin; -u_short *sp; -u_32_t n; +void +ipf_fix_incksum(cksum, sp, n, partial) + int cksum; + u_short *sp; + u_32_t n, partial; { u_short sumshort; u_32_t sum1; @@ -578,13 +936,17 @@ u_32_t n; if (n == 0) return; - if (n & NAT_HW_CKSUM) { - n &= 0xffff; - n += fin->fin_dlen; - n = (n & 0xffff) + (n >> 16); - *sp = n & 0xffff; + if (cksum == 4) { + *sp = 0; return; } + if (cksum == 2) { + sum1 = partial; + sum1 = (sum1 & 0xffff) + (sum1 >> 16); + *sp = htons(sum1); + return; + } + sum1 = (~ntohs(*sp)) & 0xffff; sum1 += ~(n) & 0xffff; sum1 = (sum1 >> 16) + (sum1 & 0xffff); @@ -596,7 +958,7 @@ u_32_t n; /* ------------------------------------------------------------------------ */ -/* Function: fix_datacksum */ +/* Function: ipf_fix_datacksum */ /* Returns: Nil */ /* Parameters: sp(I) - location of 16bit checksum to update */ /* n((I) - amount to adjust checksum by */ @@ -613,9 +975,10 @@ u_32_t n; /* processing like hardware cksum or ntohs processing have been done by the */ /* kernel on the data section. */ /* ------------------------------------------------------------------------ */ -void fix_datacksum(sp, n) -u_short *sp; -u_32_t n; +void +ipf_fix_datacksum(sp, n) + u_short *sp; + u_32_t n; { u_short sumshort; u_32_t sum1; @@ -634,42 +997,48 @@ u_32_t n; /* ------------------------------------------------------------------------ */ -/* Function: fr_nat_ioctl */ +/* Function: ipf_nat_ioctl */ /* Returns: int - 0 == success, != 0 == failure */ -/* Parameters: data(I) - pointer to ioctl data */ -/* cmd(I) - ioctl command integer */ -/* mode(I) - file mode bits used with open */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* data(I) - pointer to ioctl data */ +/* cmd(I) - ioctl command integer */ +/* mode(I) - file mode bits used with open */ +/* uid(I) - uid of calling process */ +/* ctx(I) - pointer used as key for finding context */ /* */ /* Processes an ioctl call made to operate on the IP Filter NAT device. */ /* ------------------------------------------------------------------------ */ -int fr_nat_ioctl(data, cmd, mode, uid, ctx) -ioctlcmd_t cmd; -caddr_t data; -int mode, uid; -void *ctx; +int +ipf_nat_ioctl(softc, data, cmd, mode, uid, ctx) + ipf_main_softc_t *softc; + ioctlcmd_t cmd; + caddr_t data; + int mode, uid; + void *ctx; { - ipnat_t *nat, *nt, *n = NULL, **np = NULL; + ipf_nat_softc_t *softn = softc->ipf_nat_soft; int error = 0, ret, arg, getlock; + ipnat_t *nat, *nt, *n; ipnat_t natd; SPL_INT(s); -#if (BSD >= 199306) && defined(_KERNEL) -# if defined(__NetBSD_Version__) && (__NetBSD_Version__ >= 399002000) +#if BSD_GE_YEAR(199306) && defined(_KERNEL) +# if NETBSD_GE_REV(399002000) if ((mode & FWRITE) && kauth_authorize_network(curlwp->l_cred, KAUTH_NETWORK_FIREWALL, KAUTH_REQ_NETWORK_FIREWALL_FW, - NULL, NULL, NULL)) { - return EPERM; - } + NULL, NULL, NULL)) # else # if defined(__FreeBSD_version) && (__FreeBSD_version >= 500034) - if (securelevel_ge(curthread->td_ucred, 3) && (mode & FWRITE)) { + if (securelevel_ge(curthread->td_ucred, 3) && (mode & FWRITE)) # else - if ((securelevel >= 3) && (mode & FWRITE)) { + if ((securelevel >= 3) && (mode & FWRITE)) # endif +# endif + { + IPFERROR(60001); return EPERM; } -# endif #endif #if defined(__osf__) && defined(_KERNEL) @@ -678,48 +1047,69 @@ void *ctx; getlock = (mode & NAT_LOCKHELD) ? 0 : 1; #endif - nat = NULL; /* XXX gcc -Wuninitialized */ - if (cmd == (ioctlcmd_t)SIOCADNAT) { - KMALLOC(nt, ipnat_t *); - } else { - nt = NULL; - } + n = NULL; + nt = NULL; + nat = NULL; - if ((cmd == (ioctlcmd_t)SIOCADNAT) || (cmd == (ioctlcmd_t)SIOCRMNAT)) { + if ((cmd == (ioctlcmd_t)SIOCADNAT) || (cmd == (ioctlcmd_t)SIOCRMNAT) || + (cmd == (ioctlcmd_t)SIOCPURGENAT)) { if (mode & NAT_SYSSPACE) { bcopy(data, (char *)&natd, sizeof(natd)); + nat = &natd; error = 0; } else { - error = fr_inobj(data, &natd, IPFOBJ_IPNAT); + bzero(&natd, sizeof(natd)); + error = ipf_inobj(softc, data, NULL, &natd, + IPFOBJ_IPNAT); + if (error != 0) + goto done; + + if (natd.in_size < sizeof(ipnat_t)) { + error = EINVAL; + goto done; + } + KMALLOCS(nt, ipnat_t *, natd.in_size); + if (nt == NULL) { + IPFERROR(60070); + error = ENOMEM; + goto done; + } + bzero(nt, natd.in_size); + error = ipf_inobjsz(softc, data, nt, IPFOBJ_IPNAT, + natd.in_size); + if (error) + goto done; + nat = nt; } - } - if (error != 0) - goto done; - - /* - * For add/delete, look to see if the NAT entry is already present - */ - if ((cmd == (ioctlcmd_t)SIOCADNAT) || (cmd == (ioctlcmd_t)SIOCRMNAT)) { - nat = &natd; - if (nat->in_v == 0) /* For backward compat. */ - nat->in_v = 4; + /* + * For add/delete, look to see if the NAT entry is + * already present + */ nat->in_flags &= IPN_USERFLAGS; if ((nat->in_redir & NAT_MAPBLK) == 0) { - if ((nat->in_flags & IPN_SPLIT) == 0) - nat->in_inip &= nat->in_inmsk; - if ((nat->in_flags & IPN_IPRANGE) == 0) - nat->in_outip &= nat->in_outmsk; - } - MUTEX_ENTER(&ipf_natio); - for (np = &nat_list; ((n = *np) != NULL); np = &n->in_next) - if (bcmp((char *)&nat->in_flags, (char *)&n->in_flags, - IPN_CMPSIZ) == 0) { - if (nat->in_redir == NAT_REDIRECT && - nat->in_pnext != n->in_pnext) - continue; - break; + if (nat->in_osrcatype == FRI_NORMAL || + nat->in_osrcatype == FRI_NONE) + nat->in_osrcaddr &= nat->in_osrcmsk; + if (nat->in_odstatype == FRI_NORMAL || + nat->in_odstatype == FRI_NONE) + nat->in_odstaddr &= nat->in_odstmsk; + if ((nat->in_flags & (IPN_SPLIT|IPN_SIPRANGE)) == 0) { + if (nat->in_nsrcatype == FRI_NORMAL) + nat->in_nsrcaddr &= nat->in_nsrcmsk; + if (nat->in_ndstatype == FRI_NORMAL) + nat->in_ndstaddr &= nat->in_ndstmsk; } + } + + error = ipf_nat_rule_init(softc, softn, nat); + if (error != 0) + goto done; + + MUTEX_ENTER(&softn->ipf_nat_io); + for (n = softn->ipf_nat_list; n != NULL; n = n->in_next) + if (ipf_nat_cmp_rules(nat, n) == 0) + break; } switch (cmd) @@ -729,115 +1119,169 @@ void *ctx; { int tmp; - if (!(mode & FWRITE)) + if (!(mode & FWRITE)) { + IPFERROR(60002); error = EPERM; - else { - tmp = ipflog_clear(IPL_LOGNAT); - error = BCOPYOUT((char *)&tmp, (char *)data, - sizeof(tmp)); - if (error != 0) + } else { + tmp = ipf_log_clear(softc, IPL_LOGNAT); + error = BCOPYOUT(&tmp, data, sizeof(tmp)); + if (error != 0) { + IPFERROR(60057); error = EFAULT; + } } break; } case SIOCSETLG : - if (!(mode & FWRITE)) + if (!(mode & FWRITE)) { + IPFERROR(60003); error = EPERM; - else { - error = BCOPYIN((char *)data, (char *)&nat_logging, - sizeof(nat_logging)); + } else { + error = BCOPYIN(data, &softn->ipf_nat_logging, + sizeof(softn->ipf_nat_logging)); if (error != 0) error = EFAULT; } break; case SIOCGETLG : - error = BCOPYOUT((char *)&nat_logging, (char *)data, - sizeof(nat_logging)); - if (error != 0) + error = BCOPYOUT(&softn->ipf_nat_logging, data, + sizeof(softn->ipf_nat_logging)); + if (error != 0) { + IPFERROR(60004); error = EFAULT; + } break; case FIONREAD : - arg = iplused[IPL_LOGNAT]; + arg = ipf_log_bytesused(softc, IPL_LOGNAT); error = BCOPYOUT(&arg, data, sizeof(arg)); - if (error != 0) + if (error != 0) { + IPFERROR(60005); error = EFAULT; + } break; #endif case SIOCADNAT : if (!(mode & FWRITE)) { + IPFERROR(60006); error = EPERM; } else if (n != NULL) { + natd.in_flineno = n->in_flineno; + (void) ipf_outobj(softc, data, &natd, IPFOBJ_IPNAT); + IPFERROR(60007); error = EEXIST; } else if (nt == NULL) { + IPFERROR(60008); error = ENOMEM; } if (error != 0) { - MUTEX_EXIT(&ipf_natio); + MUTEX_EXIT(&softn->ipf_nat_io); break; } - bcopy((char *)nat, (char *)nt, sizeof(*n)); - error = nat_siocaddnat(nt, np, getlock); - MUTEX_EXIT(&ipf_natio); - if (error == 0) + if (nat != nt) + bcopy((char *)nat, (char *)nt, sizeof(*n)); + error = ipf_nat_siocaddnat(softc, softn, nt, getlock); + MUTEX_EXIT(&softn->ipf_nat_io); + if (error == 0) { + nat = NULL; nt = NULL; + } break; case SIOCRMNAT : + case SIOCPURGENAT : if (!(mode & FWRITE)) { + IPFERROR(60009); error = EPERM; n = NULL; } else if (n == NULL) { + IPFERROR(60010); error = ESRCH; } if (error != 0) { - MUTEX_EXIT(&ipf_natio); + MUTEX_EXIT(&softn->ipf_nat_io); break; } - nat_siocdelnat(n, np, getlock); + if (cmd == (ioctlcmd_t)SIOCPURGENAT) { + error = ipf_outobjsz(softc, data, n, IPFOBJ_IPNAT, + n->in_size); + if (error) { + MUTEX_EXIT(&softn->ipf_nat_io); + goto done; + } + n->in_flags |= IPN_PURGE; + } + ipf_nat_siocdelnat(softc, softn, n, getlock); - MUTEX_EXIT(&ipf_natio); + MUTEX_EXIT(&softn->ipf_nat_io); n = NULL; break; case SIOCGNATS : - nat_stats.ns_table[0] = nat_table[0]; - nat_stats.ns_table[1] = nat_table[1]; - nat_stats.ns_list = nat_list; - nat_stats.ns_maptable = ipf_hm_maptable; - nat_stats.ns_maplist = ipf_hm_maplist; - nat_stats.ns_nattab_sz = ipf_nattable_sz; - nat_stats.ns_nattab_max = ipf_nattable_max; - nat_stats.ns_rultab_sz = ipf_natrules_sz; - nat_stats.ns_rdrtab_sz = ipf_rdrrules_sz; - nat_stats.ns_hostmap_sz = ipf_hostmap_sz; - nat_stats.ns_instances = nat_instances; - nat_stats.ns_apslist = ap_sess_list; - nat_stats.ns_ticks = fr_ticks; - error = fr_outobj(data, &nat_stats, IPFOBJ_NATSTAT); + { + natstat_t *nsp = &softn->ipf_nat_stats; + + nsp->ns_side[0].ns_table = softn->ipf_nat_table[0]; + nsp->ns_side[1].ns_table = softn->ipf_nat_table[1]; + nsp->ns_list = softn->ipf_nat_list; + nsp->ns_maptable = softn->ipf_hm_maptable; + nsp->ns_maplist = softn->ipf_hm_maplist; + nsp->ns_nattab_sz = softn->ipf_nat_table_sz; + nsp->ns_nattab_max = softn->ipf_nat_table_max; + nsp->ns_rultab_sz = softn->ipf_nat_maprules_sz; + nsp->ns_rdrtab_sz = softn->ipf_nat_rdrrules_sz; + nsp->ns_hostmap_sz = softn->ipf_nat_hostmap_sz; + nsp->ns_instances = softn->ipf_nat_instances; + nsp->ns_ticks = softc->ipf_ticks; +#ifdef IPFILTER_LOGGING + nsp->ns_log_ok = ipf_log_logok(softc, IPF_LOGNAT); + nsp->ns_log_fail = ipf_log_failures(softc, IPF_LOGNAT); +#else + nsp->ns_log_ok = 0; + nsp->ns_log_fail = 0; +#endif + error = ipf_outobj(softc, data, nsp, IPFOBJ_NATSTAT); break; + } case SIOCGNATL : { natlookup_t nl; - error = fr_inobj(data, &nl, IPFOBJ_NATLOOKUP); + error = ipf_inobj(softc, data, NULL, &nl, IPFOBJ_NATLOOKUP); if (error == 0) { void *ptr; if (getlock) { - READ_ENTER(&ipf_nat); + READ_ENTER(&softc->ipf_nat); } - ptr = nat_lookupredir(&nl); + + switch (nl.nl_v) + { + case 4 : + ptr = ipf_nat_lookupredir(&nl); + break; +#ifdef USE_INET6 + case 6 : + ptr = ipf_nat6_lookupredir(&nl); + break; +#endif + default: + ptr = NULL; + break; + } + if (getlock) { - RWLOCK_EXIT(&ipf_nat); + RWLOCK_EXIT(&softc->ipf_nat); } if (ptr != NULL) { - error = fr_outobj(data, &nl, IPFOBJ_NATLOOKUP); + error = ipf_outobj(softc, data, &nl, + IPFOBJ_NATLOOKUP); } else { + IPFERROR(60011); error = ESRCH; } } @@ -846,246 +1290,257 @@ void *ctx; case SIOCIPFFL : /* old SIOCFLNAT & SIOCCNATL */ if (!(mode & FWRITE)) { + IPFERROR(60012); error = EPERM; break; } if (getlock) { - WRITE_ENTER(&ipf_nat); + WRITE_ENTER(&softc->ipf_nat); } error = BCOPYIN(data, &arg, sizeof(arg)); - if (error != 0) + if (error != 0) { + IPFERROR(60013); error = EFAULT; - else { + } else { if (arg == 0) - ret = nat_flushtable(); + ret = ipf_nat_flushtable(softc, softn); else if (arg == 1) - ret = nat_clearlist(); + ret = ipf_nat_clearlist(softc, softn); else - ret = nat_extraflush(arg); + ret = ipf_nat_extraflush(softc, softn, arg); + ipf_proxy_flush(softc->ipf_proxy_soft, arg); } if (getlock) { - RWLOCK_EXIT(&ipf_nat); + RWLOCK_EXIT(&softc->ipf_nat); } if (error == 0) { error = BCOPYOUT(&ret, data, sizeof(ret)); } break; + case SIOCMATCHFLUSH : + if (!(mode & FWRITE)) { + IPFERROR(60014); + error = EPERM; + break; + } + if (getlock) { + WRITE_ENTER(&softc->ipf_nat); + } + + error = ipf_nat_matchflush(softc, softn, data); + + if (getlock) { + RWLOCK_EXIT(&softc->ipf_nat); + } + break; + case SIOCPROXY : - error = appr_ioctl(data, cmd, mode, ctx); + error = ipf_proxy_ioctl(softc, data, cmd, mode, ctx); break; case SIOCSTLCK : if (!(mode & FWRITE)) { + IPFERROR(60015); error = EPERM; } else { - error = fr_lock(data, &fr_nat_lock); + error = ipf_lock(data, &softn->ipf_nat_lock); } break; case SIOCSTPUT : if ((mode & FWRITE) != 0) { - error = fr_natputent(data, getlock); + error = ipf_nat_putent(softc, data, getlock); } else { + IPFERROR(60016); error = EACCES; } break; case SIOCSTGSZ : - if (fr_nat_lock) { - error = fr_natgetsz(data, getlock); - } else + if (softn->ipf_nat_lock) { + error = ipf_nat_getsz(softc, data, getlock); + } else { + IPFERROR(60017); error = EACCES; + } break; case SIOCSTGET : - if (fr_nat_lock) { - error = fr_natgetent(data, getlock); - } else + if (softn->ipf_nat_lock) { + error = ipf_nat_getent(softc, data, getlock); + } else { + IPFERROR(60018); error = EACCES; + } break; case SIOCGENITER : { ipfgeniter_t iter; ipftoken_t *token; + ipfobj_t obj; + + error = ipf_inobj(softc, data, &obj, &iter, IPFOBJ_GENITER); + if (error != 0) + break; SPL_SCHED(s); - error = fr_inobj(data, &iter, IPFOBJ_GENITER); - if (error == 0) { - token = ipf_findtoken(iter.igi_type, uid, ctx); - if (token != NULL) { - error = nat_iterator(token, &iter); - } - RWLOCK_EXIT(&ipf_tokens); + token = ipf_token_find(softc, iter.igi_type, uid, ctx); + if (token != NULL) { + error = ipf_nat_iterator(softc, token, &iter, &obj); + WRITE_ENTER(&softc->ipf_tokens); + ipf_token_deref(softc, token); + RWLOCK_EXIT(&softc->ipf_tokens); } SPL_X(s); break; } case SIOCIPFDELTOK : - error = BCOPYIN((caddr_t)data, (caddr_t)&arg, sizeof(arg)); + error = BCOPYIN(data, &arg, sizeof(arg)); if (error == 0) { SPL_SCHED(s); - error = ipf_deltoken(arg, uid, ctx); + error = ipf_token_del(softc, arg, uid, ctx); SPL_X(s); } else { + IPFERROR(60019); error = EFAULT; } break; case SIOCGTQTAB : - error = fr_outobj(data, nat_tqb, IPFOBJ_STATETQTAB); + error = ipf_outobj(softc, data, softn->ipf_nat_tcptq, + IPFOBJ_STATETQTAB); break; case SIOCGTABL : - error = nat_gettable(data); + error = ipf_nat_gettable(softc, softn, data); break; default : + IPFERROR(60020); error = EINVAL; break; } done: + if (nat != NULL) + ipf_nat_rule_fini(softc, nat); if (nt != NULL) - KFREE(nt); + KFREES(nt, nt->in_size); return error; } /* ------------------------------------------------------------------------ */ -/* Function: nat_siocaddnat */ +/* Function: ipf_nat_siocaddnat */ /* Returns: int - 0 == success, != 0 == failure */ -/* Parameters: n(I) - pointer to new NAT rule */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* softn(I) - pointer to NAT context structure */ +/* n(I) - pointer to new NAT rule */ /* np(I) - pointer to where to insert new NAT rule */ -/* getlock(I) - flag indicating if lock on ipf_nat is held */ -/* Mutex Locks: ipf_natio */ +/* getlock(I) - flag indicating if lock on is held */ +/* Mutex Locks: ipf_nat_io */ /* */ /* Handle SIOCADNAT. Resolve and calculate details inside the NAT rule */ /* from information passed to the kernel, then add it to the appropriate */ /* NAT rule table(s). */ /* ------------------------------------------------------------------------ */ -static int nat_siocaddnat(n, np, getlock) -ipnat_t *n, **np; -int getlock; +static int +ipf_nat_siocaddnat(softc, softn, n, getlock) + ipf_main_softc_t *softc; + ipf_nat_softc_t *softn; + ipnat_t *n; + int getlock; { - int error = 0, i, j; + int error = 0; - if (nat_resolverule(n) != 0) + if (ipf_nat_resolverule(softc, n) != 0) { + IPFERROR(60022); return ENOENT; - - if ((n->in_age[0] == 0) && (n->in_age[1] != 0)) - return EINVAL; - - n->in_use = 0; - if (n->in_redir & NAT_MAPBLK) - n->in_space = USABLE_PORTS * ~ntohl(n->in_outmsk); - else if (n->in_flags & IPN_AUTOPORTMAP) - n->in_space = USABLE_PORTS * ~ntohl(n->in_inmsk); - else if (n->in_flags & IPN_IPRANGE) - n->in_space = ntohl(n->in_outmsk) - ntohl(n->in_outip); - else if (n->in_flags & IPN_SPLIT) - n->in_space = 2; - else if (n->in_outmsk != 0) - n->in_space = ~ntohl(n->in_outmsk); - else - n->in_space = 1; - - /* - * Calculate the number of valid IP addresses in the output - * mapping range. In all cases, the range is inclusive of - * the start and ending IP addresses. - * If to a CIDR address, lose 2: broadcast + network address - * (so subtract 1) - * If to a range, add one. - * If to a single IP address, set to 1. - */ - if (n->in_space) { - if ((n->in_flags & IPN_IPRANGE) != 0) - n->in_space += 1; - else - n->in_space -= 1; - } else - n->in_space = 1; - - if ((n->in_outmsk != 0xffffffff) && (n->in_outmsk != 0) && - ((n->in_flags & (IPN_IPRANGE|IPN_SPLIT)) == 0)) - n->in_nip = ntohl(n->in_outip) + 1; - else if ((n->in_flags & IPN_SPLIT) && - (n->in_redir & NAT_REDIRECT)) - n->in_nip = ntohl(n->in_inip); - else - n->in_nip = ntohl(n->in_outip); - if (n->in_redir & NAT_MAP) { - n->in_pnext = ntohs(n->in_pmin); - /* - * Multiply by the number of ports made available. - */ - if (ntohs(n->in_pmax) >= ntohs(n->in_pmin)) { - n->in_space *= (ntohs(n->in_pmax) - - ntohs(n->in_pmin) + 1); - /* - * Because two different sources can map to - * different destinations but use the same - * local IP#/port #. - * If the result is smaller than in_space, then - * we may have wrapped around 32bits. - */ - i = n->in_inmsk; - if ((i != 0) && (i != 0xffffffff)) { - j = n->in_space * (~ntohl(i) + 1); - if (j >= n->in_space) - n->in_space = j; - else - n->in_space = 0xffffffff; - } - } - /* - * If no protocol is specified, multiple by 256 to allow for - * at least one IP:IP mapping per protocol. - */ - if ((n->in_flags & IPN_TCPUDPICMP) == 0) { - j = n->in_space * 256; - if (j >= n->in_space) - n->in_space = j; - else - n->in_space = 0xffffffff; - } } - /* Otherwise, these fields are preset */ + if ((n->in_age[0] == 0) && (n->in_age[1] != 0)) { + IPFERROR(60023); + return EINVAL; + } + + if (n->in_redir == (NAT_DIVERTUDP|NAT_MAP)) { + /* + * Prerecord whether or not the destination of the divert + * is local or not to the interface the packet is going + * to be sent out. + */ + n->in_dlocal = ipf_deliverlocal(softc, n->in_v[1], + n->in_ifps[1], &n->in_ndstip6); + } if (getlock) { - WRITE_ENTER(&ipf_nat); + WRITE_ENTER(&softc->ipf_nat); } n->in_next = NULL; - *np = n; - - if (n->in_age[0] != 0) - n->in_tqehead[0] = fr_addtimeoutqueue(&nat_utqe, n->in_age[0]); - - if (n->in_age[1] != 0) - n->in_tqehead[1] = fr_addtimeoutqueue(&nat_utqe, n->in_age[1]); + n->in_pnext = softn->ipf_nat_list_tail; + *n->in_pnext = n; + softn->ipf_nat_list_tail = &n->in_next; + n->in_use++; if (n->in_redir & NAT_REDIRECT) { n->in_flags &= ~IPN_NOTDST; - nat_addrdr(n); + switch (n->in_v[0]) + { + case 4 : + ipf_nat_addrdr(softn, n); + break; +#ifdef USE_INET6 + case 6 : + ipf_nat6_addrdr(softn, n); + break; +#endif + default : + break; + } + ATOMIC_INC32(softn->ipf_nat_stats.ns_rules_rdr); } + if (n->in_redir & (NAT_MAP|NAT_MAPBLK)) { n->in_flags &= ~IPN_NOTSRC; - nat_addnat(n); + switch (n->in_v[0]) + { + case 4 : + ipf_nat_addmap(softn, n); + break; +#ifdef USE_INET6 + case 6 : + ipf_nat6_addmap(softn, n); + break; +#endif + default : + break; + } + ATOMIC_INC32(softn->ipf_nat_stats.ns_rules_map); } + + if (n->in_age[0] != 0) + n->in_tqehead[0] = ipf_addtimeoutqueue(softc, + &softn->ipf_nat_utqe, + n->in_age[0]); + + if (n->in_age[1] != 0) + n->in_tqehead[1] = ipf_addtimeoutqueue(softc, + &softn->ipf_nat_utqe, + n->in_age[1]); + MUTEX_INIT(&n->in_lock, "ipnat rule lock"); n = NULL; - nat_stats.ns_rules++; -#if SOLARIS && !defined(_INET_IP_STACK_H) + ATOMIC_INC32(softn->ipf_nat_stats.ns_rules); +#if SOLARIS && !defined(INSTANCES) pfil_delayed_copy = 0; #endif if (getlock) { - RWLOCK_EXIT(&ipf_nat); /* WRITE */ + RWLOCK_EXIT(&softc->ipf_nat); /* WRITE */ } return error; @@ -1093,30 +1548,113 @@ int getlock; /* ------------------------------------------------------------------------ */ -/* Function: nat_resolvrule */ +/* Function: ipf_nat_ruleaddrinit */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* softn(I) - pointer to NAT context structure */ +/* n(I) - pointer to NAT rule */ +/* */ +/* Initialise all of the NAT address structures in a NAT rule. */ +/* ------------------------------------------------------------------------ */ +static int +ipf_nat_ruleaddrinit(softc, softn, n) + ipf_main_softc_t *softc; + ipf_nat_softc_t *softn; + ipnat_t *n; +{ + int idx, error; + + if ((n->in_ndst.na_atype == FRI_LOOKUP) && + (n->in_ndst.na_type != IPLT_DSTLIST)) { + IPFERROR(60071); + return EINVAL; + } + if ((n->in_nsrc.na_atype == FRI_LOOKUP) && + (n->in_nsrc.na_type != IPLT_DSTLIST)) { + IPFERROR(60069); + return EINVAL; + } + + if (n->in_redir == NAT_BIMAP) { + n->in_ndstaddr = n->in_osrcaddr; + n->in_ndstmsk = n->in_osrcmsk; + n->in_odstaddr = n->in_nsrcaddr; + n->in_odstmsk = n->in_nsrcmsk; + + } + + if (n->in_redir & NAT_REDIRECT) + idx = 1; + else + idx = 0; + /* + * Initialise all of the address fields. + */ + error = ipf_nat_nextaddrinit(softc, n->in_names, &n->in_osrc, 1, + n->in_ifps[idx]); + if (error != 0) + return error; + + error = ipf_nat_nextaddrinit(softc, n->in_names, &n->in_odst, 1, + n->in_ifps[idx]); + if (error != 0) + return error; + + error = ipf_nat_nextaddrinit(softc, n->in_names, &n->in_nsrc, 1, + n->in_ifps[idx]); + if (error != 0) + return error; + + error = ipf_nat_nextaddrinit(softc, n->in_names, &n->in_ndst, 1, + n->in_ifps[idx]); + if (error != 0) + return error; + + if (n->in_redir & NAT_DIVERTUDP) + ipf_nat_builddivertmp(softn, n); + + return 0; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_nat_resolvrule */ /* Returns: Nil */ -/* Parameters: n(I) - pointer to NAT rule */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* n(I) - pointer to NAT rule */ /* */ /* Handle SIOCADNAT. Resolve and calculate details inside the NAT rule */ /* from information passed to the kernel, then add it to the appropriate */ /* NAT rule table(s). */ /* ------------------------------------------------------------------------ */ -static int nat_resolverule(n) -ipnat_t *n; +static int +ipf_nat_resolverule(softc, n) + ipf_main_softc_t *softc; + ipnat_t *n; { - n->in_ifnames[0][LIFNAMSIZ - 1] = '\0'; - n->in_ifps[0] = fr_resolvenic(n->in_ifnames[0], 4); + char *base; - n->in_ifnames[1][LIFNAMSIZ - 1] = '\0'; - if (n->in_ifnames[1][0] == '\0') { - (void) strncpy(n->in_ifnames[1], n->in_ifnames[0], LIFNAMSIZ); + base = n->in_names; + + n->in_ifps[0] = ipf_resolvenic(softc, base + n->in_ifnames[0], + n->in_v[0]); + + if (n->in_ifnames[1] == -1) { + n->in_ifnames[1] = n->in_ifnames[0]; n->in_ifps[1] = n->in_ifps[0]; } else { - n->in_ifps[1] = fr_resolvenic(n->in_ifnames[1], 4); + n->in_ifps[1] = ipf_resolvenic(softc, base + n->in_ifnames[1], + n->in_v[1]); } - if (n->in_plabel[0] != '\0') { - n->in_apr = appr_lookup(n->in_p, n->in_plabel); + if (n->in_plabel != -1) { + if (n->in_redir & NAT_REDIRECT) + n->in_apr = ipf_proxy_lookup(softc->ipf_proxy_soft, + n->in_pr[0], + base + n->in_plabel); + else + n->in_apr = ipf_proxy_lookup(softc->ipf_proxy_soft, + n->in_pr[1], + base + n->in_plabel); if (n->in_apr == NULL) return -1; } @@ -1125,106 +1663,93 @@ ipnat_t *n; /* ------------------------------------------------------------------------ */ -/* Function: nat_siocdelnat */ +/* Function: ipf_nat_siocdelnat */ /* Returns: int - 0 == success, != 0 == failure */ -/* Parameters: n(I) - pointer to new NAT rule */ -/* np(I) - pointer to where to insert new NAT rule */ -/* getlock(I) - flag indicating if lock on ipf_nat is held */ -/* Mutex Locks: ipf_natio */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* softn(I) - pointer to NAT context structure */ +/* n(I) - pointer to new NAT rule */ +/* getlock(I) - flag indicating if lock on is held */ +/* Mutex Locks: ipf_nat_io */ /* */ /* Handle SIOCADNAT. Resolve and calculate details inside the NAT rule */ /* from information passed to the kernel, then add it to the appropriate */ /* NAT rule table(s). */ /* ------------------------------------------------------------------------ */ -static void nat_siocdelnat(n, np, getlock) -ipnat_t *n, **np; -int getlock; +static void +ipf_nat_siocdelnat(softc, softn, n, getlock) + ipf_main_softc_t *softc; + ipf_nat_softc_t *softn; + ipnat_t *n; + int getlock; { - if (getlock) { - WRITE_ENTER(&ipf_nat); - } - if (n->in_redir & NAT_REDIRECT) - nat_delrdr(n); - if (n->in_redir & (NAT_MAPBLK|NAT_MAP)) - nat_delnat(n); - if (nat_list == NULL) { - nat_masks = 0; - rdr_masks = 0; - } - - if (n->in_tqehead[0] != NULL) { - if (fr_deletetimeoutqueue(n->in_tqehead[0]) == 0) { - fr_freetimeoutqueue(n->in_tqehead[1]); - } - } - - if (n->in_tqehead[1] != NULL) { - if (fr_deletetimeoutqueue(n->in_tqehead[1]) == 0) { - fr_freetimeoutqueue(n->in_tqehead[1]); - } - } - - *np = n->in_next; - - if (n->in_use == 0) { - if (n->in_apr) - appr_free(n->in_apr); - MUTEX_DESTROY(&n->in_lock); - KFREE(n); - nat_stats.ns_rules--; -#if SOLARIS && !defined(_INET_IP_STACK_H) - if (nat_stats.ns_rules == 0) - pfil_delayed_copy = 1; +#ifdef IPF_NAT6 + int i; #endif - } else { - n->in_flags |= IPN_DELETE; - n->in_next = NULL; - } + if (getlock) { - RWLOCK_EXIT(&ipf_nat); /* READ/WRITE */ + WRITE_ENTER(&softc->ipf_nat); + } + + ipf_nat_delrule(softc, softn, n, 1); + + if (getlock) { + RWLOCK_EXIT(&softc->ipf_nat); /* READ/WRITE */ } } /* ------------------------------------------------------------------------ */ -/* Function: fr_natgetsz */ +/* Function: ipf_nat_getsz */ /* Returns: int - 0 == success, != 0 is the error value. */ -/* Parameters: data(I) - pointer to natget structure with kernel pointer */ -/* get the size of. */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* data(I) - pointer to natget structure with kernel */ +/* pointer get the size of. */ +/* getlock(I) - flag indicating whether or not the caller */ +/* holds a lock on ipf_nat */ /* */ /* Handle SIOCSTGSZ. */ /* Return the size of the nat list entry to be copied back to user space. */ /* The size of the entry is stored in the ng_sz field and the enture natget */ /* structure is copied back to the user. */ /* ------------------------------------------------------------------------ */ -static int fr_natgetsz(data, getlock) -caddr_t data; -int getlock; +static int +ipf_nat_getsz(softc, data, getlock) + ipf_main_softc_t *softc; + caddr_t data; + int getlock; { + ipf_nat_softc_t *softn = softc->ipf_nat_soft; ap_session_t *aps; nat_t *nat, *n; natget_t ng; + int error; - if (BCOPYIN(data, &ng, sizeof(ng)) != 0) + error = BCOPYIN(data, &ng, sizeof(ng)); + if (error != 0) { + IPFERROR(60024); return EFAULT; + } if (getlock) { - READ_ENTER(&ipf_nat); + READ_ENTER(&softc->ipf_nat); } nat = ng.ng_ptr; if (!nat) { - nat = nat_instances; + nat = softn->ipf_nat_instances; ng.ng_sz = 0; /* * Empty list so the size returned is 0. Simple. */ if (nat == NULL) { if (getlock) { - RWLOCK_EXIT(&ipf_nat); + RWLOCK_EXIT(&softc->ipf_nat); } - if (BCOPYOUT(&ng, data, sizeof(ng)) != 0) + error = BCOPYOUT(&ng, data, sizeof(ng)); + if (error != 0) { + IPFERROR(60025); return EFAULT; + } return 0; } } else { @@ -1233,13 +1758,14 @@ int getlock; * current list of entries. Security precaution to prevent * copying of random kernel data. */ - for (n = nat_instances; n; n = n->nat_next) + for (n = softn->ipf_nat_instances; n; n = n->nat_next) if (n == nat) break; if (n == NULL) { if (getlock) { - RWLOCK_EXIT(&ipf_nat); + RWLOCK_EXIT(&softc->ipf_nat); } + IPFERROR(60026); return ESRCH; } } @@ -1255,56 +1781,71 @@ int getlock; ng.ng_sz += aps->aps_psiz; } if (getlock) { - RWLOCK_EXIT(&ipf_nat); + RWLOCK_EXIT(&softc->ipf_nat); } - if (BCOPYOUT(&ng, data, sizeof(ng)) != 0) + error = BCOPYOUT(&ng, data, sizeof(ng)); + if (error != 0) { + IPFERROR(60027); return EFAULT; + } return 0; } /* ------------------------------------------------------------------------ */ -/* Function: fr_natgetent */ +/* Function: ipf_nat_getent */ /* Returns: int - 0 == success, != 0 is the error value. */ -/* Parameters: data(I) - pointer to natget structure with kernel pointer */ -/* to NAT structure to copy out. */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* data(I) - pointer to natget structure with kernel pointer*/ +/* to NAT structure to copy out. */ +/* getlock(I) - flag indicating whether or not the caller */ +/* holds a lock on ipf_nat */ /* */ /* Handle SIOCSTGET. */ /* Copies out NAT entry to user space. Any additional data held for a */ /* proxy is also copied, as to is the NAT rule which was responsible for it */ /* ------------------------------------------------------------------------ */ -static int fr_natgetent(data, getlock) -caddr_t data; -int getlock; +static int +ipf_nat_getent(softc, data, getlock) + ipf_main_softc_t *softc; + caddr_t data; + int getlock; { + ipf_nat_softc_t *softn = softc->ipf_nat_soft; int error, outsize; ap_session_t *aps; nat_save_t *ipn, ipns; nat_t *n, *nat; - error = fr_inobj(data, &ipns, IPFOBJ_NATSAVE); + error = ipf_inobj(softc, data, NULL, &ipns, IPFOBJ_NATSAVE); if (error != 0) return error; - if ((ipns.ipn_dsize < sizeof(ipns)) || (ipns.ipn_dsize > 81920)) + if ((ipns.ipn_dsize < sizeof(ipns)) || (ipns.ipn_dsize > 81920)) { + IPFERROR(60028); return EINVAL; + } KMALLOCS(ipn, nat_save_t *, ipns.ipn_dsize); - if (ipn == NULL) + if (ipn == NULL) { + IPFERROR(60029); return ENOMEM; + } if (getlock) { - READ_ENTER(&ipf_nat); + READ_ENTER(&softc->ipf_nat); } ipn->ipn_dsize = ipns.ipn_dsize; nat = ipns.ipn_next; if (nat == NULL) { - nat = nat_instances; + nat = softn->ipf_nat_instances; if (nat == NULL) { - if (nat_instances == NULL) + if (softn->ipf_nat_instances == NULL) { + IPFERROR(60030); error = ENOENT; + } goto finished; } } else { @@ -1313,10 +1854,11 @@ int getlock; * current list of entries. Security precaution to prevent * copying of random kernel data. */ - for (n = nat_instances; n; n = n->nat_next) + for (n = softn->ipf_nat_instances; n; n = n->nat_next) if (n == nat) break; if (n == NULL) { + IPFERROR(60031); error = ESRCH; goto finished; } @@ -1333,7 +1875,7 @@ int getlock; */ if (nat->nat_ptr != NULL) bcopy((char *)nat->nat_ptr, (char *)&ipn->ipn_ipnat, - sizeof(ipn->ipn_ipnat)); + ipn->ipn_ipnat.in_size); /* * If we also know the NAT entry has an associated filter rule, @@ -1354,6 +1896,7 @@ int getlock; char *s; if (outsize < sizeof(*aps)) { + IPFERROR(60032); error = ENOBUFS; goto finished; } @@ -1364,20 +1907,23 @@ int getlock; outsize -= sizeof(*aps); if ((aps->aps_data != NULL) && (outsize >= aps->aps_psiz)) bcopy(aps->aps_data, s, aps->aps_psiz); - else + else { + IPFERROR(60033); error = ENOBUFS; + } } if (error == 0) { if (getlock) { - RWLOCK_EXIT(&ipf_nat); + READ_ENTER(&softc->ipf_nat); getlock = 0; } - error = fr_outobjsz(data, ipn, IPFOBJ_NATSAVE, ipns.ipn_dsize); + error = ipf_outobjsz(softc, data, ipn, IPFOBJ_NATSAVE, + ipns.ipn_dsize); } finished: if (getlock) { - RWLOCK_EXIT(&ipf_nat); + READ_ENTER(&softc->ipf_nat); } if (ipn != NULL) { KFREES(ipn, ipns.ipn_dsize); @@ -1387,21 +1933,25 @@ finished: /* ------------------------------------------------------------------------ */ -/* Function: fr_natputent */ +/* Function: ipf_nat_putent */ /* Returns: int - 0 == success, != 0 is the error value. */ -/* Parameters: data(I) - pointer to natget structure with NAT */ -/* structure information to load into the kernel */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* data(I) - pointer to natget structure with NAT */ +/* structure information to load into the kernel */ /* getlock(I) - flag indicating whether or not a write lock */ -/* on ipf_nat is already held. */ +/* on is already held. */ /* */ /* Handle SIOCSTPUT. */ /* Loads a NAT table entry from user space, including a NAT rule, proxy and */ /* firewall rule data structures, if pointers to them indicate so. */ /* ------------------------------------------------------------------------ */ -static int fr_natputent(data, getlock) -caddr_t data; -int getlock; +static int +ipf_nat_putent(softc, data, getlock) + ipf_main_softc_t *softc; + caddr_t data; + int getlock; { + ipf_nat_softc_t *softn = softc->ipf_nat_soft; nat_save_t ipn, *ipnn; ap_session_t *aps; nat_t *n, *nat; @@ -1410,13 +1960,14 @@ int getlock; ipnat_t *in; int error; - error = fr_inobj(data, &ipn, IPFOBJ_NATSAVE); + error = ipf_inobj(softc, data, NULL, &ipn, IPFOBJ_NATSAVE); if (error != 0) return error; /* * Initialise early because of code at junkput label. */ + n = NULL; in = NULL; aps = NULL; nat = NULL; @@ -1429,17 +1980,21 @@ int getlock; */ if (ipn.ipn_dsize > sizeof(ipn)) { if (ipn.ipn_dsize > 81920) { + IPFERROR(60034); error = ENOMEM; goto junkput; } KMALLOCS(ipnn, nat_save_t *, ipn.ipn_dsize); - if (ipnn == NULL) + if (ipnn == NULL) { + IPFERROR(60035); return ENOMEM; + } - error = fr_inobjsz(data, ipnn, IPFOBJ_NATSAVE, ipn.ipn_dsize); + bzero(ipnn, ipn.ipn_dsize); + error = ipf_inobjsz(softc, data, ipnn, IPFOBJ_NATSAVE, + ipn.ipn_dsize); if (error != 0) { - error = EFAULT; goto junkput; } } else @@ -1447,13 +2002,29 @@ int getlock; KMALLOC(nat, nat_t *); if (nat == NULL) { + IPFERROR(60037); error = ENOMEM; goto junkput; } bcopy((char *)&ipnn->ipn_nat, (char *)nat, sizeof(*nat)); + + switch (nat->nat_v[0]) + { + case 4: +#ifdef USE_INET6 + case 6 : +#endif + break; + default : + IPFERROR(60061); + error = EPROTONOSUPPORT; + goto junkput; + /*NOTREACHED*/ + } + /* - * Initialize all these so that nat_delete() doesn't cause a crash. + * Initialize all these so that ipf_nat_delete() doesn't cause a crash. */ bzero((char *)nat, offsetof(struct nat, nat_tqe)); nat->nat_tqe.tqe_pnext = NULL; @@ -1466,20 +2037,22 @@ int getlock; */ in = ipnn->ipn_nat.nat_ptr; if (in != NULL) { - KMALLOC(in, ipnat_t *); + KMALLOCS(in, ipnat_t *, ipnn->ipn_ipnat.in_size); nat->nat_ptr = in; if (in == NULL) { + IPFERROR(60038); error = ENOMEM; goto junkput; } - bzero((char *)in, offsetof(struct ipnat, in_next6)); - bcopy((char *)&ipnn->ipn_ipnat, (char *)in, sizeof(*in)); + bcopy((char *)&ipnn->ipn_ipnat, (char *)in, + ipnn->ipn_ipnat.in_size); in->in_use = 1; in->in_flags |= IPN_DELETE; - ATOMIC_INC(nat_stats.ns_rules); + ATOMIC_INC32(softn->ipf_nat_stats.ns_rules); - if (nat_resolverule(in) != 0) { + if (ipf_nat_resolverule(softc, in) != 0) { + IPFERROR(60039); error = ESRCH; goto junkput; } @@ -1491,43 +2064,76 @@ int getlock; * For NAT_OUTBOUND, we're lookup for a duplicate MAP entry. To do * this, we check to see if the inbound combination of addresses and * ports is already known. Similar logic is applied for NAT_INBOUND. - * + * */ bzero((char *)&fin, sizeof(fin)); - fin.fin_p = nat->nat_p; - if (nat->nat_dir == NAT_OUTBOUND) { - fin.fin_ifp = nat->nat_ifps[0]; - fin.fin_data[0] = ntohs(nat->nat_oport); - fin.fin_data[1] = ntohs(nat->nat_outport); + fin.fin_v = nat->nat_v[0]; + fin.fin_p = nat->nat_pr[0]; + fin.fin_rev = nat->nat_rev; + fin.fin_ifp = nat->nat_ifps[0]; + fin.fin_data[0] = ntohs(nat->nat_ndport); + fin.fin_data[1] = ntohs(nat->nat_nsport); + + switch (nat->nat_dir) + { + case NAT_OUTBOUND : + case NAT_DIVERTOUT : if (getlock) { - READ_ENTER(&ipf_nat); + READ_ENTER(&softc->ipf_nat); } - n = nat_inlookup(&fin, nat->nat_flags, fin.fin_p, - nat->nat_oip, nat->nat_inip); + + fin.fin_v = nat->nat_v[1]; + if (nat->nat_v[1] == 4) { + n = ipf_nat_inlookup(&fin, nat->nat_flags, fin.fin_p, + nat->nat_ndstip, nat->nat_nsrcip); +#ifdef USE_INET6 + } else if (nat->nat_v[1] == 6) { + n = ipf_nat6_inlookup(&fin, nat->nat_flags, fin.fin_p, + &nat->nat_ndst6.in6, + &nat->nat_nsrc6.in6); +#endif + } + if (getlock) { - RWLOCK_EXIT(&ipf_nat); + RWLOCK_EXIT(&softc->ipf_nat); } if (n != NULL) { + IPFERROR(60040); error = EEXIST; goto junkput; } - } else if (nat->nat_dir == NAT_INBOUND) { - fin.fin_ifp = nat->nat_ifps[0]; - fin.fin_data[0] = ntohs(nat->nat_outport); - fin.fin_data[1] = ntohs(nat->nat_oport); + break; + + case NAT_INBOUND : + case NAT_DIVERTIN : if (getlock) { - READ_ENTER(&ipf_nat); + READ_ENTER(&softc->ipf_nat); } - n = nat_outlookup(&fin, nat->nat_flags, fin.fin_p, - nat->nat_outip, nat->nat_oip); + + if (fin.fin_v == 4) { + n = ipf_nat_outlookup(&fin, nat->nat_flags, fin.fin_p, + nat->nat_ndstip, + nat->nat_nsrcip); +#ifdef USE_INET6 + } else if (fin.fin_v == 6) { + n = ipf_nat6_outlookup(&fin, nat->nat_flags, fin.fin_p, + &nat->nat_ndst6.in6, + &nat->nat_nsrc6.in6); +#endif + } + if (getlock) { - RWLOCK_EXIT(&ipf_nat); + RWLOCK_EXIT(&softc->ipf_nat); } if (n != NULL) { + IPFERROR(60041); error = EEXIST; goto junkput; } - } else { + break; + + default : + IPFERROR(60042); error = EINVAL; goto junkput; } @@ -1541,6 +2147,7 @@ int getlock; KMALLOC(aps, ap_session_t *); nat->nat_aps = aps; if (aps == NULL) { + IPFERROR(60043); error = ENOMEM; goto junkput; } @@ -1551,11 +2158,13 @@ int getlock; aps->aps_apr = NULL; if (aps->aps_psiz != 0) { if (aps->aps_psiz > 81920) { + IPFERROR(60044); error = ENOMEM; goto junkput; } KMALLOCS(aps->aps_data, void *, aps->aps_psiz); if (aps->aps_data == NULL) { + IPFERROR(60045); error = ENOMEM; goto junkput; } @@ -1577,12 +2186,13 @@ int getlock; KMALLOC(fr, frentry_t *); nat->nat_fr = fr; if (fr == NULL) { + IPFERROR(60046); error = ENOMEM; goto junkput; } ipnn->ipn_nat.nat_fr = fr; fr->fr_ref = 1; - (void) fr_outobj(data, ipnn, IPFOBJ_NATSAVE); + (void) ipf_outobj(softc, data, ipnn, IPFOBJ_NATSAVE); bcopy((char *)&ipnn->ipn_fr, (char *)fr, sizeof(*fr)); fr->fr_ref = 1; @@ -1594,9 +2204,9 @@ int getlock; MUTEX_INIT(&fr->fr_lock, "nat-filter rule lock"); } else { if (getlock) { - READ_ENTER(&ipf_nat); + READ_ENTER(&softc->ipf_nat); } - for (n = nat_instances; n; n = n->nat_next) + for (n = softn->ipf_nat_instances; n; n = n->nat_next) if (n->nat_fr == fr) break; @@ -1606,10 +2216,11 @@ int getlock; MUTEX_EXIT(&fr->fr_lock); } if (getlock) { - RWLOCK_EXIT(&ipf_nat); + RWLOCK_EXIT(&softc->ipf_nat); } - if (!n) { + if (n == NULL) { + IPFERROR(60047); error = ESRCH; goto junkput; } @@ -1622,25 +2233,30 @@ int getlock; } if (getlock) { - WRITE_ENTER(&ipf_nat); - } - error = nat_insert(nat, nat->nat_rev); - if ((error == 0) && (aps != NULL)) { - aps->aps_next = ap_sess_list; - ap_sess_list = aps; + WRITE_ENTER(&softc->ipf_nat); } + + if (fin.fin_v == 4) + error = ipf_nat_finalise(&fin, nat); +#ifdef USE_INET6 + else + error = ipf_nat6_finalise(&fin, nat); +#endif + if (getlock) { - RWLOCK_EXIT(&ipf_nat); + RWLOCK_EXIT(&softc->ipf_nat); } if (error == 0) return 0; + IPFERROR(60048); error = ENOMEM; junkput: - if (fr != NULL) - (void) fr_derefrule(&fr); + if (fr != NULL) { + (void) ipf_derefrule(softc, &fr); + } if ((ipnn != NULL) && (ipnn != &ipn)) { KFREES(ipnn, ipn.ipn_dsize); @@ -1654,8 +2270,8 @@ junkput: } if (in != NULL) { if (in->in_apr) - appr_free(in->in_apr); - KFREE(in); + ipf_proxy_deref(in->in_apr); + KFREES(in, in->in_size); } KFREE(nat); } @@ -1664,27 +2280,29 @@ junkput: /* ------------------------------------------------------------------------ */ -/* Function: nat_delete */ +/* Function: ipf_nat_delete */ /* Returns: Nil */ -/* Parameters: natd(I) - pointer to NAT structure to delete */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* nat(I) - pointer to NAT structure to delete */ /* logtype(I) - type of LOG record to create before deleting */ /* Write Lock: ipf_nat */ /* */ /* Delete a nat entry from the various lists and table. If NAT logging is */ /* enabled then generate a NAT log record for this event. */ /* ------------------------------------------------------------------------ */ -void nat_delete(nat, logtype) -struct nat *nat; -int logtype; +void +ipf_nat_delete(softc, nat, logtype) + ipf_main_softc_t *softc; + struct nat *nat; + int logtype; { + ipf_nat_softc_t *softn = softc->ipf_nat_soft; + int madeorphan = 0, bkt, removed = 0; + nat_stat_side_t *nss; struct ipnat *ipn; - int removed = 0; - if (logtype != 0 && nat_logging != 0) - nat_log(nat, logtype); -#if defined(NEED_LOCAL_RAND) && defined(_KERNEL) - ipf_rand_push(nat, sizeof(*nat)); -#endif + if (logtype != 0 && softn->ipf_nat_logging != 0) + ipf_nat_log(softc, softn, nat, logtype); /* * Take it as a general indication that all the pointers are set if @@ -1693,8 +2311,19 @@ int logtype; if (nat->nat_pnext != NULL) { removed = 1; - nat_stats.ns_bucketlen[0][nat->nat_hv[0]]--; - nat_stats.ns_bucketlen[1][nat->nat_hv[1]]--; + bkt = nat->nat_hv[0] % softn->ipf_nat_table_sz; + nss = &softn->ipf_nat_stats.ns_side[0]; + nss->ns_bucketlen[bkt]--; + if (nss->ns_bucketlen[bkt] == 0) { + nss->ns_inuse--; + } + + bkt = nat->nat_hv[1] % softn->ipf_nat_table_sz; + nss = &softn->ipf_nat_stats.ns_side[1]; + nss->ns_bucketlen[bkt]--; + if (nss->ns_bucketlen[bkt] == 0) { + nss->ns_inuse--; + } *nat->nat_pnext = nat->nat_next; if (nat->nat_next != NULL) { @@ -1717,20 +2346,34 @@ int logtype; } nat->nat_phnext[1] = NULL; - if ((nat->nat_flags & SI_WILDP) != 0) - nat_stats.ns_wilds--; + if ((nat->nat_flags & SI_WILDP) != 0) { + ATOMIC_DEC32(softn->ipf_nat_stats.ns_wilds); + } + madeorphan = 1; } if (nat->nat_me != NULL) { *nat->nat_me = NULL; nat->nat_me = NULL; + nat->nat_ref--; + ASSERT(nat->nat_ref >= 0); } - if (nat->nat_tqe.tqe_ifq != NULL) - fr_deletequeueentry(&nat->nat_tqe); + if (nat->nat_tqe.tqe_ifq != NULL) { + /* + * No call to ipf_freetimeoutqueue() is made here, they are + * garbage collected in ipf_nat_expire(). + */ + (void) ipf_deletequeueentry(&nat->nat_tqe); + } + + if (nat->nat_sync) { + ipf_sync_del_nat(softc->ipf_sync_soft, nat->nat_sync); + nat->nat_sync = NULL; + } if (logtype == NL_EXPIRE) - nat_stats.ns_expire++; + softn->ipf_nat_stats.ns_expire++; MUTEX_ENTER(&nat->nat_lock); /* @@ -1743,35 +2386,36 @@ int logtype; nat->nat_ref -= 2; MUTEX_EXIT(&nat->nat_lock); if (removed) - nat_stats.ns_orphans++; + softn->ipf_nat_stats.ns_orphans++; return; } } else if (nat->nat_ref > 1) { nat->nat_ref--; MUTEX_EXIT(&nat->nat_lock); - if (removed) - nat_stats.ns_orphans++; + if (madeorphan == 1) + softn->ipf_nat_stats.ns_orphans++; return; } + ASSERT(nat->nat_ref >= 0); MUTEX_EXIT(&nat->nat_lock); - /* - * At this point, nat_ref is 1, doing "--" would make it 0.. - */ nat->nat_ref = 0; - if (!removed) - nat_stats.ns_orphans--; -#ifdef IPFILTER_SYNC - if (nat->nat_sync) - ipfsync_del(nat->nat_sync); -#endif + if (madeorphan == 0) + softn->ipf_nat_stats.ns_orphans--; - if (nat->nat_fr != NULL) - (void) fr_derefrule(&nat->nat_fr); + /* + * At this point, nat_ref can be either 0 or -1 + */ + softn->ipf_nat_stats.ns_proto[nat->nat_pr[0]]--; - if (nat->nat_hm != NULL) - fr_hostmapdel(&nat->nat_hm); + if (nat->nat_fr != NULL) { + (void) ipf_derefrule(softc, &nat->nat_fr); + } + + if (nat->nat_hm != NULL) { + ipf_nat_hostmapdel(softc, &nat->nat_hm); + } /* * If there is an active reference from the nat entry to its parent @@ -1779,38 +2423,51 @@ int logtype; * longer being used. */ ipn = nat->nat_ptr; + nat->nat_ptr = NULL; + if (ipn != NULL) { - fr_ipnatderef(&ipn); + ipn->in_space++; + ipf_nat_rule_deref(softc, &ipn); + } + + if (nat->nat_aps != NULL) { + ipf_proxy_free(softc, nat->nat_aps); + nat->nat_aps = NULL; } MUTEX_DESTROY(&nat->nat_lock); - aps_free(nat->nat_aps); - nat_stats.ns_inuse--; + softn->ipf_nat_stats.ns_active--; /* * If there's a fragment table entry too for this nat entry, then * dereference that as well. This is after nat_lock is released * because of Tru64. */ - fr_forgetnat((void *)nat); + ipf_frag_natforget(softc, (void *)nat); KFREE(nat); } /* ------------------------------------------------------------------------ */ -/* Function: nat_flushtable */ +/* Function: ipf_nat_flushtable */ /* Returns: int - number of NAT rules deleted */ -/* Parameters: Nil */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* softn(I) - pointer to NAT context structure */ +/* Write Lock: ipf_nat */ /* */ /* Deletes all currently active NAT sessions. In deleting each NAT entry a */ -/* log record should be emitted in nat_delete() if NAT logging is enabled. */ +/* log record should be emitted in ipf_nat_delete() if NAT logging is */ +/* enabled. */ /* ------------------------------------------------------------------------ */ /* * nat_flushtable - clear the NAT table of all mapping entries. */ -static int nat_flushtable() +static int +ipf_nat_flushtable(softc, softn) + ipf_main_softc_t *softc; + ipf_nat_softc_t *softn; { nat_t *nat; int j = 0; @@ -1819,67 +2476,141 @@ static int nat_flushtable() * ALL NAT mappings deleted, so lets just make the deletions * quicker. */ - if (nat_table[0] != NULL) - bzero((char *)nat_table[0], - sizeof(nat_table[0]) * ipf_nattable_sz); - if (nat_table[1] != NULL) - bzero((char *)nat_table[1], - sizeof(nat_table[1]) * ipf_nattable_sz); + if (softn->ipf_nat_table[0] != NULL) + bzero((char *)softn->ipf_nat_table[0], + sizeof(softn->ipf_nat_table[0]) * + softn->ipf_nat_table_sz); + if (softn->ipf_nat_table[1] != NULL) + bzero((char *)softn->ipf_nat_table[1], + sizeof(softn->ipf_nat_table[1]) * + softn->ipf_nat_table_sz); - while ((nat = nat_instances) != NULL) { - nat_delete(nat, NL_FLUSH); + while ((nat = softn->ipf_nat_instances) != NULL) { + ipf_nat_delete(softc, nat, NL_FLUSH); j++; } - nat_stats.ns_inuse = 0; return j; } /* ------------------------------------------------------------------------ */ -/* Function: nat_clearlist */ +/* Function: ipf_nat_clearlist */ /* Returns: int - number of NAT/RDR rules deleted */ -/* Parameters: Nil */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* softn(I) - pointer to NAT context structure */ /* */ /* Delete all rules in the current list of rules. There is nothing elegant */ /* about this cleanup: simply free all entries on the list of rules and */ /* clear out the tables used for hashed NAT rule lookups. */ /* ------------------------------------------------------------------------ */ -static int nat_clearlist() +static int +ipf_nat_clearlist(softc, softn) + ipf_main_softc_t *softc; + ipf_nat_softc_t *softn; { - ipnat_t *n, **np = &nat_list; + ipnat_t *n; int i = 0; - if (nat_rules != NULL) - bzero((char *)nat_rules, sizeof(*nat_rules) * ipf_natrules_sz); - if (rdr_rules != NULL) - bzero((char *)rdr_rules, sizeof(*rdr_rules) * ipf_rdrrules_sz); + if (softn->ipf_nat_map_rules != NULL) { + bzero((char *)softn->ipf_nat_map_rules, + sizeof(*softn->ipf_nat_map_rules) * + softn->ipf_nat_maprules_sz); + } + if (softn->ipf_nat_rdr_rules != NULL) { + bzero((char *)softn->ipf_nat_rdr_rules, + sizeof(*softn->ipf_nat_rdr_rules) * + softn->ipf_nat_rdrrules_sz); + } - while ((n = *np) != NULL) { - *np = n->in_next; - if (n->in_use == 0) { - if (n->in_apr != NULL) - appr_free(n->in_apr); - MUTEX_DESTROY(&n->in_lock); - KFREE(n); - nat_stats.ns_rules--; - } else { - n->in_flags |= IPN_DELETE; - n->in_next = NULL; - } + while ((n = softn->ipf_nat_list) != NULL) { + ipf_nat_delrule(softc, softn, n, 0); i++; } -#if SOLARIS && !defined(_INET_IP_STACK_H) +#if SOLARIS && !defined(INSTANCES) pfil_delayed_copy = 1; #endif - nat_masks = 0; - rdr_masks = 0; return i; } /* ------------------------------------------------------------------------ */ -/* Function: nat_newmap */ +/* Function: ipf_nat_delrule */ +/* Returns: Nil */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* softn(I) - pointer to NAT context structure */ +/* np(I) - pointer to NAT rule to delete */ +/* purge(I) - 1 == allow purge, 0 == prevent purge */ +/* Locks: WRITE(ipf_nat) */ +/* */ +/* Preventing "purge" from occuring is allowed because when all of the NAT */ +/* rules are being removed, allowing the "purge" to walk through the list */ +/* of NAT sessions, possibly multiple times, would be a large performance */ +/* hit, on the order of O(N^2). */ +/* ------------------------------------------------------------------------ */ +static void +ipf_nat_delrule(softc, softn, np, purge) + ipf_main_softc_t *softc; + ipf_nat_softc_t *softn; + ipnat_t *np; + int purge; +{ + + if (np->in_pnext != NULL) { + *np->in_pnext = np->in_next; + if (np->in_next != NULL) + np->in_next->in_pnext = np->in_pnext; + if (softn->ipf_nat_list_tail == &np->in_next) + softn->ipf_nat_list_tail = np->in_pnext; + } + + if ((purge == 1) && ((np->in_flags & IPN_PURGE) != 0)) { + nat_t *next; + nat_t *nat; + + for (next = softn->ipf_nat_instances; (nat = next) != NULL;) { + next = nat->nat_next; + if (nat->nat_ptr == np) + ipf_nat_delete(softc, nat, NL_PURGE); + } + } + + if ((np->in_flags & IPN_DELETE) == 0) { + if (np->in_redir & NAT_REDIRECT) { + switch (np->in_v[0]) + { + case 4 : + ipf_nat_delrdr(softn, np); + break; +#ifdef USE_INET6 + case 6 : + ipf_nat6_delrdr(softn, np); + break; +#endif + } + } + if (np->in_redir & (NAT_MAPBLK|NAT_MAP)) { + switch (np->in_v[0]) + { + case 4 : + ipf_nat_delmap(softn, np); + break; +#ifdef USE_INET6 + case 6 : + ipf_nat6_delmap(softn, np); + break; +#endif + } + } + } + + np->in_flags |= IPN_DELETE; + ipf_nat_rule_deref(softc, &np); +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_nat_newmap */ /* Returns: int - -1 == error, 0 == success */ /* Parameters: fin(I) - pointer to packet information */ /* nat(I) - pointer to NAT entry */ @@ -1891,11 +2622,14 @@ static int nat_clearlist() /* ni.nai_ip is passed in uninitialised and must be set, in host byte order,*/ /* to the new IP address for the translation. */ /* ------------------------------------------------------------------------ */ -static INLINE int nat_newmap(fin, nat, ni) -fr_info_t *fin; -nat_t *nat; -natinfo_t *ni; +static int +ipf_nat_newmap(fin, nat, ni) + fr_info_t *fin; + nat_t *nat; + natinfo_t *ni; { + ipf_main_softc_t *softc = fin->fin_main_soft; + ipf_nat_softc_t *softn = softc->ipf_nat_soft; u_short st_port, dport, sport, port, sp, dp; struct in_addr in, inb; hostmap_t *hm; @@ -1912,11 +2646,17 @@ natinfo_t *ni; l = 0; hm = NULL; np = ni->nai_np; - st_ip = np->in_nip; - st_port = np->in_pnext; - flags = ni->nai_flags; - sport = ni->nai_sport; - dport = ni->nai_dport; + st_ip = np->in_snip; + st_port = np->in_spnext; + flags = nat->nat_flags; + + if (flags & IPN_ICMPQUERY) { + sport = fin->fin_data[1]; + dport = 0; + } else { + sport = htons(fin->fin_data[0]); + dport = htons(fin->fin_data[1]); + } /* * Do a loop until we either run out of entries to try or we find @@ -1925,50 +2665,54 @@ natinfo_t *ni; */ do { port = 0; - in.s_addr = htonl(np->in_nip); + in.s_addr = htonl(np->in_snip); if (l == 0) { /* * Check to see if there is an existing NAT * setup for this IP address pair. */ - hm = nat_hostmap(np, fin->fin_src, fin->fin_dst, - in, 0); + hm = ipf_nat_hostmap(softn, np, fin->fin_src, + fin->fin_dst, in, 0); if (hm != NULL) - in.s_addr = hm->hm_mapip.s_addr; + in.s_addr = hm->hm_nsrcip.s_addr; } else if ((l == 1) && (hm != NULL)) { - fr_hostmapdel(&hm); + ipf_nat_hostmapdel(softc, &hm); } in.s_addr = ntohl(in.s_addr); nat->nat_hm = hm; - if ((np->in_outmsk == 0xffffffff) && (np->in_pnext == 0)) { - if (l > 0) + if ((np->in_nsrcmsk == 0xffffffff) && (np->in_spnext == 0)) { + if (l > 0) { + NBUMPSIDEX(1, ns_exhausted, ns_exhausted_1); return -1; + } } if (np->in_redir == NAT_BIMAP && - np->in_inmsk == np->in_outmsk) { + np->in_osrcmsk == np->in_nsrcmsk) { /* * map the address block in a 1:1 fashion */ - in.s_addr = np->in_outip; - in.s_addr |= fin->fin_saddr & ~np->in_inmsk; + in.s_addr = np->in_nsrcaddr; + in.s_addr |= fin->fin_saddr & ~np->in_osrcmsk; in.s_addr = ntohl(in.s_addr); } else if (np->in_redir & NAT_MAPBLK) { if ((l >= np->in_ppip) || ((l > 0) && - !(flags & IPN_TCPUDP))) + !(flags & IPN_TCPUDP))) { + NBUMPSIDEX(1, ns_exhausted, ns_exhausted_2); return -1; + } /* * map-block - Calculate destination address. */ in.s_addr = ntohl(fin->fin_saddr); - in.s_addr &= ntohl(~np->in_inmsk); + in.s_addr &= ntohl(~np->in_osrcmsk); inb.s_addr = in.s_addr; in.s_addr /= np->in_ippip; - in.s_addr &= ntohl(~np->in_outmsk); - in.s_addr += ntohl(np->in_outip); + in.s_addr &= ntohl(~np->in_nsrcmsk); + in.s_addr += ntohl(np->in_nsrcaddr); /* * Calculate destination port. */ @@ -1982,28 +2726,34 @@ natinfo_t *ni; port = htons(port); } - } else if ((np->in_outip == 0) && - (np->in_outmsk == 0xffffffff)) { + } else if ((np->in_nsrcaddr == 0) && + (np->in_nsrcmsk == 0xffffffff)) { + i6addr_t in6; + /* * 0/32 - use the interface's IP address. */ if ((l > 0) || - fr_ifpaddr(4, FRI_NORMAL, fin->fin_ifp, - &in, NULL) == -1) + ipf_ifpaddr(softc, 4, FRI_NORMAL, fin->fin_ifp, + &in6, NULL) == -1) { + NBUMPSIDEX(1, ns_new_ifpaddr, ns_new_ifpaddr_1); return -1; - in.s_addr = ntohl(in.s_addr); + } + in.s_addr = ntohl(in6.in4.s_addr); - } else if ((np->in_outip == 0) && (np->in_outmsk == 0)) { + } else if ((np->in_nsrcaddr == 0) && (np->in_nsrcmsk == 0)) { /* * 0/0 - use the original source address/port. */ - if (l > 0) + if (l > 0) { + NBUMPSIDEX(1, ns_exhausted, ns_exhausted_3); return -1; + } in.s_addr = ntohl(fin->fin_saddr); - } else if ((np->in_outmsk != 0xffffffff) && - (np->in_pnext == 0) && ((l > 0) || (hm == NULL))) - np->in_nip++; + } else if ((np->in_nsrcmsk != 0xffffffff) && + (np->in_spnext == 0) && ((l > 0) || (hm == NULL))) + np->in_snip++; natl = NULL; @@ -2014,11 +2764,9 @@ natinfo_t *ni; * "ports auto" (without map-block) */ if ((l > 0) && (l % np->in_ppip == 0)) { - if (l > np->in_space) { - return -1; - } else if ((l > np->in_ppip) && - np->in_outmsk != 0xffffffff) - np->in_nip++; + if ((l > np->in_ppip) && + np->in_nsrcmsk != 0xffffffff) + np->in_snip++; } if (np->in_ppip != 0) { port = ntohs(sport); @@ -2032,35 +2780,35 @@ natinfo_t *ni; } } else if (((np->in_redir & NAT_MAPBLK) == 0) && - (flags & IPN_TCPUDPICMP) && (np->in_pnext != 0)) { + (flags & IPN_TCPUDPICMP) && (np->in_spnext != 0)) { /* * Standard port translation. Select next port. */ if (np->in_flags & IPN_SEQUENTIAL) { - port = np->in_pnext; + port = np->in_spnext; } else { - port = ipf_random() % (ntohs(np->in_pmax) - - ntohs(np->in_pmin)); - port += ntohs(np->in_pmin); + port = ipf_random() % (np->in_spmax - + np->in_spmin + 1); + port += np->in_spmin; } port = htons(port); - np->in_pnext++; + np->in_spnext++; - if (np->in_pnext > ntohs(np->in_pmax)) { - np->in_pnext = ntohs(np->in_pmin); - if (np->in_outmsk != 0xffffffff) - np->in_nip++; + if (np->in_spnext > np->in_spmax) { + np->in_spnext = np->in_spmin; + if (np->in_nsrcmsk != 0xffffffff) + np->in_snip++; } } - if (np->in_flags & IPN_IPRANGE) { - if (np->in_nip > ntohl(np->in_outmsk)) - np->in_nip = ntohl(np->in_outip); + if (np->in_flags & IPN_SIPRANGE) { + if (np->in_snip > ntohl(np->in_nsrcmsk)) + np->in_snip = ntohl(np->in_nsrcaddr); } else { - if ((np->in_outmsk != 0xffffffff) && - ((np->in_nip + 1) & ntohl(np->in_outmsk)) > - ntohl(np->in_outip)) - np->in_nip = ntohl(np->in_outip) + 1; + if ((np->in_nsrcmsk != 0xffffffff) && + ((np->in_snip + 1) & ntohl(np->in_nsrcmsk)) > + ntohl(np->in_nsrcaddr)) + np->in_snip = ntohl(np->in_nsrcaddr) + 1; } if ((port == 0) && (flags & (IPN_TCPUDPICMP|IPN_ICMPQUERY))) @@ -2080,9 +2828,9 @@ natinfo_t *ni; sp = fin->fin_data[0]; dp = fin->fin_data[1]; fin->fin_data[0] = fin->fin_data[1]; - fin->fin_data[1] = htons(port); - natl = nat_inlookup(fin, flags & ~(SI_WILDP|NAT_SEARCH), - (u_int)fin->fin_p, fin->fin_dst, inb); + fin->fin_data[1] = ntohs(port); + natl = ipf_nat_inlookup(fin, flags & ~(SI_WILDP|NAT_SEARCH), + (u_int)fin->fin_p, fin->fin_dst, inb); fin->fin_data[0] = sp; fin->fin_data[1] = dp; @@ -2091,64 +2839,41 @@ natinfo_t *ni; * start ? */ if ((natl != NULL) && - (np->in_pnext != 0) && (st_port == np->in_pnext) && - (np->in_nip != 0) && (st_ip == np->in_nip)) + (np->in_spnext != 0) && (st_port == np->in_spnext) && + (np->in_snip != 0) && (st_ip == np->in_snip)) { + NBUMPSIDED(1, ns_wrap); return -1; + } l++; } while (natl != NULL); - if (np->in_space > 0) - np->in_space--; - /* Setup the NAT table */ - nat->nat_inip = fin->fin_src; - nat->nat_outip.s_addr = htonl(in.s_addr); - nat->nat_oip = fin->fin_dst; + nat->nat_osrcip = fin->fin_src; + nat->nat_nsrcaddr = htonl(in.s_addr); + nat->nat_odstip = fin->fin_dst; + nat->nat_ndstip = fin->fin_dst; if (nat->nat_hm == NULL) - nat->nat_hm = nat_hostmap(np, fin->fin_src, fin->fin_dst, - nat->nat_outip, 0); - - /* - * The ICMP checksum does not have a pseudo header containing - * the IP addresses - */ - ni->nai_sum1 = LONG_SUM(ntohl(fin->fin_saddr)); - ni->nai_sum2 = LONG_SUM(in.s_addr); - if ((flags & IPN_TCPUDP)) { - ni->nai_sum1 += ntohs(sport); - ni->nai_sum2 += ntohs(port); - } + nat->nat_hm = ipf_nat_hostmap(softn, np, fin->fin_src, + fin->fin_dst, nat->nat_nsrcip, + 0); if (flags & IPN_TCPUDP) { - nat->nat_inport = sport; - nat->nat_outport = port; /* sport */ - nat->nat_oport = dport; + nat->nat_osport = sport; + nat->nat_nsport = port; /* sport */ + nat->nat_odport = dport; + nat->nat_ndport = dport; ((tcphdr_t *)fin->fin_dp)->th_sport = port; } else if (flags & IPN_ICMPQUERY) { + nat->nat_oicmpid = fin->fin_data[1]; ((icmphdr_t *)fin->fin_dp)->icmp_id = port; - nat->nat_inport = port; - nat->nat_outport = port; - } else if (fin->fin_p == IPPROTO_GRE) { -#if 0 - nat->nat_gre.gs_flags = ((grehdr_t *)fin->fin_dp)->gr_flags; - if (GRE_REV(nat->nat_gre.gs_flags) == 1) { - nat->nat_oport = 0;/*fin->fin_data[1];*/ - nat->nat_inport = 0;/*fin->fin_data[0];*/ - nat->nat_outport = 0;/*fin->fin_data[0];*/ - nat->nat_call[0] = fin->fin_data[0]; - nat->nat_call[1] = fin->fin_data[0]; - } -#endif + nat->nat_nicmpid = port; } - ni->nai_ip.s_addr = in.s_addr; - ni->nai_port = port; - ni->nai_nport = dport; return 0; } /* ------------------------------------------------------------------------ */ -/* Function: nat_newrdr */ +/* Function: ipf_nat_newrdr */ /* Returns: int - -1 == error, 0 == success (no move), 1 == success and */ /* allow rule to be moved if IPN_ROUNDR is set. */ /* Parameters: fin(I) - pointer to packet information */ @@ -2159,11 +2884,14 @@ natinfo_t *ni; /* ni.nai_ip is passed in uninitialised and must be set, in host byte order,*/ /* to the new IP address for the translation. */ /* ------------------------------------------------------------------------ */ -static INLINE int nat_newrdr(fin, nat, ni) -fr_info_t *fin; -nat_t *nat; -natinfo_t *ni; +static int +ipf_nat_newrdr(fin, nat, ni) + fr_info_t *fin; + nat_t *nat; + natinfo_t *ni; { + ipf_main_softc_t *softc = fin->fin_main_soft; + ipf_nat_softc_t *softn = softc->ipf_nat_soft; u_short nport, dport, sport; struct in_addr in, inb; u_short sp, dp; @@ -2177,9 +2905,18 @@ natinfo_t *ni; hm = NULL; in.s_addr = 0; np = ni->nai_np; - flags = ni->nai_flags; - sport = ni->nai_sport; - dport = ni->nai_dport; + flags = nat->nat_flags; + + if (flags & IPN_ICMPQUERY) { + dport = fin->fin_data[1]; + sport = 0; + } else { + sport = htons(fin->fin_data[0]); + dport = htons(fin->fin_data[1]); + } + + /* TRACE sport, dport */ + /* * If the matching rule has IPN_STICKY set, then we want to have the @@ -2190,13 +2927,14 @@ natinfo_t *ni; */ if (((np->in_flags & (IPN_ROUNDR|IPN_SPLIT)) != 0) && ((np->in_flags & IPN_STICKY) != 0)) { - hm = nat_hostmap(NULL, fin->fin_src, fin->fin_dst, in, - (u_32_t)dport); + hm = ipf_nat_hostmap(softn, NULL, fin->fin_src, fin->fin_dst, + in, (u_32_t)dport); if (hm != NULL) { - in.s_addr = ntohl(hm->hm_mapip.s_addr); + in.s_addr = ntohl(hm->hm_ndstip.s_addr); np = hm->hm_ipnat; ni->nai_np = np; move = 0; + ipf_nat_hostmapdel(softc, &hm); } } @@ -2207,53 +2945,60 @@ natinfo_t *ni; * internal port. */ if (np->in_flags & IPN_SPLIT) { - in.s_addr = np->in_nip; + in.s_addr = np->in_dnip; if ((np->in_flags & (IPN_ROUNDR|IPN_STICKY)) == IPN_STICKY) { - hm = nat_hostmap(NULL, fin->fin_src, fin->fin_dst, - in, (u_32_t)dport); + hm = ipf_nat_hostmap(softn, NULL, fin->fin_src, + fin->fin_dst, in, (u_32_t)dport); if (hm != NULL) { - in.s_addr = hm->hm_mapip.s_addr; + in.s_addr = hm->hm_ndstip.s_addr; move = 0; } } if (hm == NULL || hm->hm_ref == 1) { - if (np->in_inip == htonl(in.s_addr)) { - np->in_nip = ntohl(np->in_inmsk); + if (np->in_ndstaddr == htonl(in.s_addr)) { + np->in_dnip = ntohl(np->in_ndstmsk); move = 0; } else { - np->in_nip = ntohl(np->in_inip); + np->in_dnip = ntohl(np->in_ndstaddr); } } + if (hm != NULL) + ipf_nat_hostmapdel(softc, &hm); + + } else if ((np->in_ndstaddr == 0) && (np->in_ndstmsk == 0xffffffff)) { + i6addr_t in6; - } else if ((np->in_inip == 0) && (np->in_inmsk == 0xffffffff)) { /* * 0/32 - use the interface's IP address. */ - if (fr_ifpaddr(4, FRI_NORMAL, fin->fin_ifp, &in, NULL) == -1) + if (ipf_ifpaddr(softc, 4, FRI_NORMAL, fin->fin_ifp, + &in6, NULL) == -1) { + NBUMPSIDEX(0, ns_new_ifpaddr, ns_new_ifpaddr_2); return -1; - in.s_addr = ntohl(in.s_addr); + } + in.s_addr = ntohl(in6.in4.s_addr); - } else if ((np->in_inip == 0) && (np->in_inmsk== 0)) { + } else if ((np->in_ndstaddr == 0) && (np->in_ndstmsk== 0)) { /* * 0/0 - use the original destination address/port. */ in.s_addr = ntohl(fin->fin_daddr); } else if (np->in_redir == NAT_BIMAP && - np->in_inmsk == np->in_outmsk) { + np->in_ndstmsk == np->in_odstmsk) { /* * map the address block in a 1:1 fashion */ - in.s_addr = np->in_inip; - in.s_addr |= fin->fin_daddr & ~np->in_inmsk; + in.s_addr = np->in_ndstaddr; + in.s_addr |= fin->fin_daddr & ~np->in_ndstmsk; in.s_addr = ntohl(in.s_addr); } else { - in.s_addr = ntohl(np->in_inip); + in.s_addr = ntohl(np->in_ndstaddr); } - if ((np->in_pnext == 0) || ((flags & NAT_NOTRULEPORT) != 0)) + if ((np->in_dpnext == 0) || ((flags & NAT_NOTRULEPORT) != 0)) nport = dport; else { /* @@ -2261,12 +3006,15 @@ natinfo_t *ni; * pmin == pmax, the gain is not significant. */ if (((np->in_flags & IPN_FIXEDDPORT) == 0) && - (np->in_pmin != np->in_pmax)) { - nport = ntohs(dport) - ntohs(np->in_pmin) + - ntohs(np->in_pnext); + (np->in_odport != np->in_dtop)) { + nport = ntohs(dport) - np->in_odport + np->in_dpmax; nport = htons(nport); - } else - nport = np->in_pnext; + } else { + nport = htons(np->in_dpnext); + np->in_dpnext++; + if (np->in_dpnext > np->in_dpmax) + np->in_dpnext = np->in_dpmin; + } } /* @@ -2275,8 +3023,10 @@ natinfo_t *ni; * setup any translation for this either. */ if (in.s_addr == 0) { - if (nport == dport) + if (nport == dport) { + NBUMPSIDED(0, ns_xlate_null); return -1; + } in.s_addr = ntohl(fin->fin_daddr); } @@ -2290,54 +3040,41 @@ natinfo_t *ni; dp = fin->fin_data[1]; fin->fin_data[1] = fin->fin_data[0]; fin->fin_data[0] = ntohs(nport); - natl = nat_outlookup(fin, flags & ~(SI_WILDP|NAT_SEARCH), + natl = ipf_nat_outlookup(fin, flags & ~(SI_WILDP|NAT_SEARCH), (u_int)fin->fin_p, inb, fin->fin_src); fin->fin_data[0] = sp; fin->fin_data[1] = dp; - if (natl != NULL) + if (natl != NULL) { + DT2(ns_new_xlate_exists, fr_info_t *, fin, nat_t *, natl); + NBUMPSIDE(0, ns_xlate_exists); return -1; + } - nat->nat_inip.s_addr = htonl(in.s_addr); - nat->nat_outip = fin->fin_dst; - nat->nat_oip = fin->fin_src; + nat->nat_ndstaddr = htonl(in.s_addr); + nat->nat_odstip = fin->fin_dst; + nat->nat_nsrcip = fin->fin_src; + nat->nat_osrcip = fin->fin_src; if ((nat->nat_hm == NULL) && ((np->in_flags & IPN_STICKY) != 0)) - nat->nat_hm = nat_hostmap(np, fin->fin_src, fin->fin_dst, in, - (u_32_t)dport); - - ni->nai_sum1 = LONG_SUM(ntohl(fin->fin_daddr)) + ntohs(dport); - ni->nai_sum2 = LONG_SUM(in.s_addr) + ntohs(nport); - - ni->nai_ip.s_addr = in.s_addr; - ni->nai_nport = nport; - ni->nai_port = sport; + nat->nat_hm = ipf_nat_hostmap(softn, np, fin->fin_src, + fin->fin_dst, in, (u_32_t)dport); if (flags & IPN_TCPUDP) { - nat->nat_inport = nport; - nat->nat_outport = dport; - nat->nat_oport = sport; + nat->nat_odport = dport; + nat->nat_ndport = nport; + nat->nat_osport = sport; + nat->nat_nsport = sport; ((tcphdr_t *)fin->fin_dp)->th_dport = nport; } else if (flags & IPN_ICMPQUERY) { + nat->nat_oicmpid = fin->fin_data[1]; ((icmphdr_t *)fin->fin_dp)->icmp_id = nport; - nat->nat_inport = nport; - nat->nat_outport = nport; - } else if (fin->fin_p == IPPROTO_GRE) { -#if 0 - nat->nat_gre.gs_flags = ((grehdr_t *)fin->fin_dp)->gr_flags; - if (GRE_REV(nat->nat_gre.gs_flags) == 1) { - nat->nat_call[0] = fin->fin_data[0]; - nat->nat_call[1] = fin->fin_data[1]; - nat->nat_oport = 0; /*fin->fin_data[0];*/ - nat->nat_inport = 0; /*fin->fin_data[1];*/ - nat->nat_outport = 0; /*fin->fin_data[1];*/ - } -#endif + nat->nat_nicmpid = nport; } return move; } /* ------------------------------------------------------------------------ */ -/* Function: nat_new */ +/* Function: ipf_nat_add */ /* Returns: nat_t* - NULL == failure to create new NAT structure, */ /* else pointer to new NAT structure */ /* Parameters: fin(I) - pointer to packet information */ @@ -2358,29 +3095,32 @@ natinfo_t *ni; /* NOTE: natsave should NOT be used top point back to an ipstate_t struct */ /* as it can result in memory being corrupted. */ /* ------------------------------------------------------------------------ */ -nat_t *nat_new(fin, np, natsave, flags, direction) -fr_info_t *fin; -ipnat_t *np; -nat_t **natsave; -u_int flags; -int direction; +nat_t * +ipf_nat_add(fin, np, natsave, flags, direction) + fr_info_t *fin; + ipnat_t *np; + nat_t **natsave; + u_int flags; + int direction; { - u_short port = 0, sport = 0, dport = 0, nport = 0; - tcphdr_t *tcp = NULL; + ipf_main_softc_t *softc = fin->fin_main_soft; + ipf_nat_softc_t *softn = softc->ipf_nat_soft; hostmap_t *hm = NULL; - struct in_addr in; nat_t *nat, *natl; + natstat_t *nsp; u_int nflags; natinfo_t ni; - u_32_t sumd; int move; -#if SOLARIS && defined(_KERNEL) && (SOLARIS2 >= 6) && defined(ICK_M_CTL_MAGIC) - qpktinfo_t *qpi = fin->fin_qpi; -#endif - if (nat_stats.ns_inuse >= ipf_nattable_max) { - nat_stats.ns_memfail++; - fr_nat_doflush = 1; + nsp = &softn->ipf_nat_stats; + + if ((nsp->ns_active * 100 / softn->ipf_nat_table_max) > + softn->ipf_nat_table_wm_high) { + softn->ipf_nat_doflush = 1; + } + + if (nsp->ns_active >= softn->ipf_nat_table_max) { + NBUMPSIDED(fin->fin_out, ns_table_max); return NULL; } @@ -2389,34 +3129,29 @@ int direction; nflags &= NAT_FROMRULE; ni.nai_np = np; - ni.nai_nflags = nflags; - ni.nai_flags = flags; ni.nai_dport = 0; ni.nai_sport = 0; /* Give me a new nat */ KMALLOC(nat, nat_t *); if (nat == NULL) { - nat_stats.ns_memfail++; + NBUMPSIDED(fin->fin_out, ns_memfail); /* * Try to automatically tune the max # of entries in the * table allowed to be less than what will cause kmem_alloc() * to fail and try to eliminate panics due to out of memory * conditions arising. */ - if (ipf_nattable_max > ipf_nattable_sz) { - ipf_nattable_max = nat_stats.ns_inuse - 100; - printf("ipf_nattable_max reduced to %d\n", - ipf_nattable_max); + if ((softn->ipf_nat_table_max > softn->ipf_nat_table_sz) && + (nsp->ns_active > 100)) { + softn->ipf_nat_table_max = nsp->ns_active - 100; + printf("table_max reduced to %d\n", + softn->ipf_nat_table_max); } return NULL; } - if (flags & IPN_TCPUDP) { - tcp = fin->fin_dp; - ni.nai_sport = htons(fin->fin_sport); - ni.nai_dport = htons(fin->fin_dport); - } else if (flags & IPN_ICMPQUERY) { + if (flags & IPN_ICMPQUERY) { /* * In the ICMP query NAT code, we translate the ICMP id fields * to make them unique. This is indepedent of the ICMP type @@ -2430,28 +3165,34 @@ int direction; * the concept of source port. We overlay sport, so we can * maximally reuse the existing code. */ - ni.nai_sport = ((icmphdr_t *)fin->fin_dp)->icmp_id; - ni.nai_dport = ni.nai_sport; + ni.nai_sport = fin->fin_data[1]; + ni.nai_dport = 0; } bzero((char *)nat, sizeof(*nat)); nat->nat_flags = flags; nat->nat_redir = np->in_redir; - - if ((flags & NAT_SLAVE) == 0) { - MUTEX_ENTER(&ipf_nat_new); - } + nat->nat_dir = direction; + nat->nat_pr[0] = fin->fin_p; + nat->nat_pr[1] = fin->fin_p; /* - * Search the current table for a match. + * Search the current table for a match and create a new mapping + * if there is none found. */ - if (direction == NAT_OUTBOUND) { + if (np->in_redir & NAT_DIVERTUDP) { + move = ipf_nat_newdivert(fin, nat, &ni); + + } else if (np->in_redir & NAT_REWRITE) { + move = ipf_nat_newrewrite(fin, nat, &ni); + + } else if (direction == NAT_OUTBOUND) { /* * We can now arrange to call this for the same connection * because ipf_nat_new doesn't protect the code path into * this function. */ - natl = nat_outlookup(fin, nflags, (u_int)fin->fin_p, + natl = ipf_nat_outlookup(fin, nflags, (u_int)fin->fin_p, fin->fin_src, fin->fin_dst); if (natl != NULL) { KFREE(nat); @@ -2459,166 +3200,184 @@ int direction; goto done; } - move = nat_newmap(fin, nat, &ni); - if (move == -1) - goto badnat; - - np = ni.nai_np; - in = ni.nai_ip; + move = ipf_nat_newmap(fin, nat, &ni); } else { /* - * NAT_INBOUND is used only for redirects rules + * NAT_INBOUND is used for redirects rules */ - natl = nat_inlookup(fin, nflags, (u_int)fin->fin_p, - fin->fin_src, fin->fin_dst); + natl = ipf_nat_inlookup(fin, nflags, (u_int)fin->fin_p, + fin->fin_src, fin->fin_dst); if (natl != NULL) { KFREE(nat); nat = natl; goto done; } - move = nat_newrdr(fin, nat, &ni); - if (move == -1) + move = ipf_nat_newrdr(fin, nat, &ni); + } + if (move == -1) + goto badnat; + + np = ni.nai_np; + + nat->nat_mssclamp = np->in_mssclamp; + nat->nat_me = natsave; + nat->nat_fr = fin->fin_fr; + nat->nat_rev = fin->fin_rev; + nat->nat_ptr = np; + nat->nat_dlocal = np->in_dlocal; + + if ((np->in_apr != NULL) && ((nat->nat_flags & NAT_SLAVE) == 0)) { + if (ipf_proxy_new(fin, nat) == -1) { + NBUMPSIDED(fin->fin_out, ns_appr_fail); goto badnat; - - np = ni.nai_np; - in = ni.nai_ip; - } - port = ni.nai_port; - nport = ni.nai_nport; - - if ((move == 1) && (np->in_flags & IPN_ROUNDR)) { - if (np->in_redir == NAT_REDIRECT) { - nat_delrdr(np); - nat_addrdr(np); - } else if (np->in_redir == NAT_MAP) { - nat_delnat(np); - nat_addnat(np); } } - if (flags & IPN_TCPUDP) { - sport = ni.nai_sport; - dport = ni.nai_dport; - } else if (flags & IPN_ICMPQUERY) { - sport = ni.nai_sport; - dport = 0; + nat->nat_ifps[0] = np->in_ifps[0]; + if (np->in_ifps[0] != NULL) { + COPYIFNAME(np->in_v[0], np->in_ifps[0], nat->nat_ifnames[0]); } - CALC_SUMD(ni.nai_sum1, ni.nai_sum2, sumd); - nat->nat_sumd[0] = (sumd & 0xffff) + (sumd >> 16); -#if SOLARIS && defined(_KERNEL) && (SOLARIS2 >= 6) && defined(ICK_M_CTL_MAGIC) - if ((flags & IPN_TCP) && dohwcksum && - (((ill_t *)qpi->qpi_ill)->ill_ick.ick_magic == ICK_M_CTL_MAGIC)) { - if (direction == NAT_OUTBOUND) - ni.nai_sum1 = LONG_SUM(in.s_addr); - else - ni.nai_sum1 = LONG_SUM(ntohl(fin->fin_saddr)); - ni.nai_sum1 += LONG_SUM(ntohl(fin->fin_daddr)); - ni.nai_sum1 += 30; - ni.nai_sum1 = (ni.nai_sum1 & 0xffff) + (ni.nai_sum1 >> 16); - nat->nat_sumd[1] = NAT_HW_CKSUM|(ni.nai_sum1 & 0xffff); - } else -#endif - nat->nat_sumd[1] = nat->nat_sumd[0]; - - if ((flags & IPN_TCPUDPICMP) && ((sport != port) || (dport != nport))) { - if (direction == NAT_OUTBOUND) - ni.nai_sum1 = LONG_SUM(ntohl(fin->fin_saddr)); - else - ni.nai_sum1 = LONG_SUM(ntohl(fin->fin_daddr)); - - ni.nai_sum2 = LONG_SUM(in.s_addr); - - CALC_SUMD(ni.nai_sum1, ni.nai_sum2, sumd); - nat->nat_ipsumd = (sumd & 0xffff) + (sumd >> 16); - } else { - nat->nat_ipsumd = nat->nat_sumd[0]; - if (!(flags & IPN_TCPUDPICMP)) { - nat->nat_sumd[0] = 0; - nat->nat_sumd[1] = 0; - } + nat->nat_ifps[1] = np->in_ifps[1]; + if (np->in_ifps[1] != NULL) { + COPYIFNAME(np->in_v[1], np->in_ifps[1], nat->nat_ifnames[1]); } - if (nat_finalise(fin, nat, &ni, tcp, natsave, direction) == -1) { - fr_nat_doflush = 1; + if (ipf_nat_finalise(fin, nat) == -1) { goto badnat; } + + np->in_use++; + + if ((move == 1) && (np->in_flags & IPN_ROUNDR)) { + if ((np->in_redir & (NAT_REDIRECT|NAT_MAP)) == NAT_REDIRECT) { + ipf_nat_delrdr(softn, np); + ipf_nat_addrdr(softn, np); + } else if ((np->in_redir & (NAT_REDIRECT|NAT_MAP)) == NAT_MAP) { + ipf_nat_delmap(softn, np); + ipf_nat_addmap(softn, np); + } + } + if (flags & SI_WILDP) - nat_stats.ns_wilds++; - fin->fin_flx |= FI_NEWNAT; + nsp->ns_wilds++; + nsp->ns_proto[nat->nat_pr[0]]++; + goto done; badnat: - nat_stats.ns_badnat++; + DT2(ns_badnatnew, fr_info_t *, fin, nat_t *, nat); + NBUMPSIDE(fin->fin_out, ns_badnatnew); if ((hm = nat->nat_hm) != NULL) - fr_hostmapdel(&hm); + ipf_nat_hostmapdel(softc, &hm); KFREE(nat); nat = NULL; done: - if ((flags & NAT_SLAVE) == 0) { - MUTEX_EXIT(&ipf_nat_new); - } + if (nat != NULL && np != NULL) + np->in_hits++; + if (natsave != NULL) + *natsave = nat; return nat; } /* ------------------------------------------------------------------------ */ -/* Function: nat_finalise */ +/* Function: ipf_nat_finalise */ /* Returns: int - 0 == sucess, -1 == failure */ /* Parameters: fin(I) - pointer to packet information */ /* nat(I) - pointer to NAT entry */ -/* ni(I) - pointer to structure with misc. information needed */ -/* to create new NAT entry. */ /* Write Lock: ipf_nat */ /* */ /* This is the tail end of constructing a new NAT entry and is the same */ /* for both IPv4 and IPv6. */ /* ------------------------------------------------------------------------ */ /*ARGSUSED*/ -static int nat_finalise(fin, nat, ni, tcp, natsave, direction) -fr_info_t *fin; -nat_t *nat; -natinfo_t *ni; -tcphdr_t *tcp; -nat_t **natsave; -int direction; +static int +ipf_nat_finalise(fin, nat) + fr_info_t *fin; + nat_t *nat; { + ipf_main_softc_t *softc = fin->fin_main_soft; + ipf_nat_softc_t *softn = softc->ipf_nat_soft; + u_32_t sum1, sum2, sumd; frentry_t *fr; - ipnat_t *np; - - np = ni->nai_np; - - if (np->in_ifps[0] != NULL) { - COPYIFNAME(4, np->in_ifps[0], nat->nat_ifnames[0]); - } - if (np->in_ifps[1] != NULL) { - COPYIFNAME(4, np->in_ifps[1], nat->nat_ifnames[1]); - } -#ifdef IPFILTER_SYNC - if ((nat->nat_flags & SI_CLONE) == 0) - nat->nat_sync = ipfsync_new(SMC_NAT, fin, nat); + u_32_t flags; +#if SOLARIS && defined(_KERNEL) && (SOLARIS2 >= 6) && defined(ICK_M_CTL_MAGIC) + qpktinfo_t *qpi = fin->fin_qpi; #endif - nat->nat_me = natsave; - nat->nat_dir = direction; - nat->nat_ifps[0] = np->in_ifps[0]; - nat->nat_ifps[1] = np->in_ifps[1]; - nat->nat_ptr = np; - nat->nat_p = fin->fin_p; - nat->nat_mssclamp = np->in_mssclamp; - if (nat->nat_p == IPPROTO_TCP) - nat->nat_seqnext[0] = ntohl(tcp->th_seq); + flags = nat->nat_flags; - if ((np->in_apr != NULL) && ((ni->nai_flags & NAT_SLAVE) == 0)) - if (appr_new(fin, nat) == -1) - return -1; + switch (nat->nat_pr[0]) + { + case IPPROTO_ICMP : + sum1 = LONG_SUM(ntohs(nat->nat_oicmpid)); + sum2 = LONG_SUM(ntohs(nat->nat_nicmpid)); + CALC_SUMD(sum1, sum2, sumd); + nat->nat_sumd[0] = (sumd & 0xffff) + (sumd >> 16); - if (nat_insert(nat, fin->fin_rev) == 0) { - if (nat_logging) - nat_log(nat, (u_int)np->in_redir); - np->in_use++; - fr = fin->fin_fr; - nat->nat_fr = fr; + break; + + default : + sum1 = LONG_SUM(ntohl(nat->nat_osrcaddr) + \ + ntohs(nat->nat_osport)); + sum2 = LONG_SUM(ntohl(nat->nat_nsrcaddr) + \ + ntohs(nat->nat_nsport)); + CALC_SUMD(sum1, sum2, sumd); + nat->nat_sumd[0] = (sumd & 0xffff) + (sumd >> 16); + + sum1 = LONG_SUM(ntohl(nat->nat_odstaddr) + \ + ntohs(nat->nat_odport)); + sum2 = LONG_SUM(ntohl(nat->nat_ndstaddr) + \ + ntohs(nat->nat_ndport)); + CALC_SUMD(sum1, sum2, sumd); + nat->nat_sumd[0] += (sumd & 0xffff) + (sumd >> 16); + break; + } + + /* + * Compute the partial checksum, just in case. + * This is only ever placed into outbound packets so care needs + * to be taken over which pair of addresses are used. + */ + if (nat->nat_dir == NAT_OUTBOUND) { + sum1 = LONG_SUM(ntohl(nat->nat_nsrcaddr)); + sum1 += LONG_SUM(ntohl(nat->nat_ndstaddr)); + } else { + sum1 = LONG_SUM(ntohl(nat->nat_osrcaddr)); + sum1 += LONG_SUM(ntohl(nat->nat_odstaddr)); + } + sum1 += nat->nat_pr[1]; + nat->nat_sumd[1] = (sum1 & 0xffff) + (sum1 >> 16); + + sum1 = LONG_SUM(ntohl(nat->nat_osrcaddr)); + sum2 = LONG_SUM(ntohl(nat->nat_nsrcaddr)); + CALC_SUMD(sum1, sum2, sumd); + nat->nat_ipsumd = (sumd & 0xffff) + (sumd >> 16); + + sum1 = LONG_SUM(ntohl(nat->nat_odstaddr)); + sum2 = LONG_SUM(ntohl(nat->nat_ndstaddr)); + CALC_SUMD(sum1, sum2, sumd); + nat->nat_ipsumd += (sumd & 0xffff) + (sumd >> 16); + + nat->nat_v[0] = 4; + nat->nat_v[1] = 4; + + if ((nat->nat_ifps[0] != NULL) && (nat->nat_ifps[0] != (void *)-1)) { + nat->nat_mtu[0] = GETIFMTU_4(nat->nat_ifps[0]); + } + + if ((nat->nat_ifps[1] != NULL) && (nat->nat_ifps[1] != (void *)-1)) { + nat->nat_mtu[1] = GETIFMTU_4(nat->nat_ifps[1]); + } + + if ((nat->nat_flags & SI_CLONE) == 0) + nat->nat_sync = ipf_sync_new(softc, SMC_NAT, fin, nat); + + if (ipf_nat_insert(softc, softn, nat) == 0) { + if (softn->ipf_nat_logging) + ipf_nat_log(softc, softn, nat, NL_NEW); + fr = nat->nat_fr; if (fr != NULL) { MUTEX_ENTER(&fr->fr_lock); fr->fr_ref++; @@ -2627,112 +3386,224 @@ int direction; return 0; } + NBUMPSIDED(fin->fin_out, ns_unfinalised); /* * nat_insert failed, so cleanup time... */ + if (nat->nat_sync != NULL) + ipf_sync_del_nat(softc->ipf_sync_soft, nat->nat_sync); return -1; } /* ------------------------------------------------------------------------ */ -/* Function: nat_insert */ -/* Returns: int - 0 == sucess, -1 == failure */ -/* Parameters: nat(I) - pointer to NAT structure */ -/* rev(I) - flag indicating forward/reverse direction of packet */ -/* Write Lock: ipf_nat */ +/* Function: ipf_nat_insert */ +/* Returns: int - 0 == sucess, -1 == failure */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* softn(I) - pointer to NAT context structure */ +/* nat(I) - pointer to NAT structure */ +/* Write Lock: ipf_nat */ /* */ /* Insert a NAT entry into the hash tables for searching and add it to the */ /* list of active NAT entries. Adjust global counters when complete. */ /* ------------------------------------------------------------------------ */ -int nat_insert(nat, rev) -nat_t *nat; -int rev; +int +ipf_nat_insert(softc, softn, nat) + ipf_main_softc_t *softc; + ipf_nat_softc_t *softn; + nat_t *nat; { - u_int hv1, hv2; - nat_t **natp; + u_int hv0, hv1; + u_int sp, dp; + ipnat_t *in; /* * Try and return an error as early as possible, so calculate the hash * entry numbers first and then proceed. */ if ((nat->nat_flags & (SI_W_SPORT|SI_W_DPORT)) == 0) { - hv1 = NAT_HASH_FN(nat->nat_inip.s_addr, nat->nat_inport, - 0xffffffff); - hv1 = NAT_HASH_FN(nat->nat_oip.s_addr, hv1 + nat->nat_oport, - ipf_nattable_sz); - hv2 = NAT_HASH_FN(nat->nat_outip.s_addr, nat->nat_outport, - 0xffffffff); - hv2 = NAT_HASH_FN(nat->nat_oip.s_addr, hv2 + nat->nat_oport, - ipf_nattable_sz); + if ((nat->nat_flags & IPN_TCPUDP) != 0) { + sp = nat->nat_osport; + dp = nat->nat_odport; + } else if ((nat->nat_flags & IPN_ICMPQUERY) != 0) { + sp = 0; + dp = nat->nat_oicmpid; + } else { + sp = 0; + dp = 0; + } + hv0 = NAT_HASH_FN(nat->nat_osrcaddr, sp, 0xffffffff); + hv0 = NAT_HASH_FN(nat->nat_odstaddr, hv0 + dp, 0xffffffff); + /* + * TRACE nat_osrcaddr, nat_osport, nat_odstaddr, + * nat_odport, hv0 + */ + + if ((nat->nat_flags & IPN_TCPUDP) != 0) { + sp = nat->nat_nsport; + dp = nat->nat_ndport; + } else if ((nat->nat_flags & IPN_ICMPQUERY) != 0) { + sp = 0; + dp = nat->nat_nicmpid; + } else { + sp = 0; + dp = 0; + } + hv1 = NAT_HASH_FN(nat->nat_nsrcaddr, sp, 0xffffffff); + hv1 = NAT_HASH_FN(nat->nat_ndstaddr, hv1 + dp, 0xffffffff); + /* + * TRACE nat_nsrcaddr, nat_nsport, nat_ndstaddr, + * nat_ndport, hv1 + */ } else { - hv1 = NAT_HASH_FN(nat->nat_inip.s_addr, 0, 0xffffffff); - hv1 = NAT_HASH_FN(nat->nat_oip.s_addr, hv1, ipf_nattable_sz); - hv2 = NAT_HASH_FN(nat->nat_outip.s_addr, 0, 0xffffffff); - hv2 = NAT_HASH_FN(nat->nat_oip.s_addr, hv2, ipf_nattable_sz); + hv0 = NAT_HASH_FN(nat->nat_osrcaddr, 0, 0xffffffff); + hv0 = NAT_HASH_FN(nat->nat_odstaddr, hv0, 0xffffffff); + /* TRACE nat_osrcaddr, nat_odstaddr, hv0 */ + + hv1 = NAT_HASH_FN(nat->nat_nsrcaddr, 0, 0xffffffff); + hv1 = NAT_HASH_FN(nat->nat_ndstaddr, hv1, 0xffffffff); + /* TRACE nat_nsrcaddr, nat_ndstaddr, hv1 */ } - if (nat_stats.ns_bucketlen[0][hv1] >= fr_nat_maxbucket || - nat_stats.ns_bucketlen[1][hv2] >= fr_nat_maxbucket) { - return -1; - } - - nat->nat_hv[0] = hv1; - nat->nat_hv[1] = hv2; + nat->nat_hv[0] = hv0; + nat->nat_hv[1] = hv1; MUTEX_INIT(&nat->nat_lock, "nat entry lock"); - nat->nat_rev = rev; - nat->nat_ref = 1; - nat->nat_bytes[0] = 0; - nat->nat_pkts[0] = 0; - nat->nat_bytes[1] = 0; - nat->nat_pkts[1] = 0; + in = nat->nat_ptr; + nat->nat_ref = nat->nat_me ? 2 : 1; nat->nat_ifnames[0][LIFNAMSIZ - 1] = '\0'; - nat->nat_ifps[0] = fr_resolvenic(nat->nat_ifnames[0], 4); + nat->nat_ifps[0] = ipf_resolvenic(softc, nat->nat_ifnames[0], 4); if (nat->nat_ifnames[1][0] != '\0') { nat->nat_ifnames[1][LIFNAMSIZ - 1] = '\0'; - nat->nat_ifps[1] = fr_resolvenic(nat->nat_ifnames[1], 4); - } else { - (void) strncpy(nat->nat_ifnames[1], nat->nat_ifnames[0], - LIFNAMSIZ); - nat->nat_ifnames[1][LIFNAMSIZ - 1] = '\0'; - nat->nat_ifps[1] = nat->nat_ifps[0]; + nat->nat_ifps[1] = ipf_resolvenic(softc, + nat->nat_ifnames[1], 4); + } else if (in->in_ifnames[1] != -1) { + char *name; + + name = in->in_names + in->in_ifnames[1]; + if (name[1] != '\0' && name[0] != '-' && name[0] != '*') { + (void) strncpy(nat->nat_ifnames[1], + nat->nat_ifnames[0], LIFNAMSIZ); + nat->nat_ifnames[1][LIFNAMSIZ - 1] = '\0'; + nat->nat_ifps[1] = nat->nat_ifps[0]; + } + } + if ((nat->nat_ifps[0] != NULL) && (nat->nat_ifps[0] != (void *)-1)) { + nat->nat_mtu[0] = GETIFMTU_4(nat->nat_ifps[0]); + } + if ((nat->nat_ifps[1] != NULL) && (nat->nat_ifps[1] != (void *)-1)) { + nat->nat_mtu[1] = GETIFMTU_4(nat->nat_ifps[1]); } - nat->nat_next = nat_instances; - nat->nat_pnext = &nat_instances; - if (nat_instances) - nat_instances->nat_pnext = &nat->nat_next; - nat_instances = nat; + return ipf_nat_hashtab_add(softc, softn, nat); +} - natp = &nat_table[0][hv1]; - if (*natp) - (*natp)->nat_phnext[0] = &nat->nat_hnext[0]; + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_nat_hashtab_add */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* softn(I) - pointer to NAT context structure */ +/* nat(I) - pointer to NAT structure */ +/* */ +/* Handle the insertion of a NAT entry into the table/list. */ +/* ------------------------------------------------------------------------ */ +int +ipf_nat_hashtab_add(softc, softn, nat) + ipf_main_softc_t *softc; + ipf_nat_softc_t *softn; + nat_t *nat; +{ + nat_t **natp; + u_int hv0; + u_int hv1; + + hv0 = nat->nat_hv[0] % softn->ipf_nat_table_sz; + hv1 = nat->nat_hv[1] % softn->ipf_nat_table_sz; + + if (nat->nat_dir == NAT_INBOUND || nat->nat_dir == NAT_DIVERTIN) { + u_int swap; + + swap = hv0; + hv0 = hv1; + hv1 = swap; + } + + if (softn->ipf_nat_stats.ns_side[0].ns_bucketlen[hv0] >= + softn->ipf_nat_maxbucket) { + DT1(ns_bucket_max_0, int, + softn->ipf_nat_stats.ns_side[0].ns_bucketlen[hv0]); + NBUMPSIDE(0, ns_bucket_max); + return -1; + } + + if (softn->ipf_nat_stats.ns_side[1].ns_bucketlen[hv1] >= + softn->ipf_nat_maxbucket) { + DT1(ns_bucket_max_1, int, + softn->ipf_nat_stats.ns_side[1].ns_bucketlen[hv1]); + NBUMPSIDE(1, ns_bucket_max); + return -1; + } + + /* + * The ordering of operations in the list and hash table insertion + * is very important. The last operation for each task should be + * to update the top of the list, after all the "nexts" have been + * done so that walking the list while it is being done does not + * find strange pointers. + * + * Global list of NAT instances + */ + nat->nat_next = softn->ipf_nat_instances; + nat->nat_pnext = &softn->ipf_nat_instances; + if (softn->ipf_nat_instances) + softn->ipf_nat_instances->nat_pnext = &nat->nat_next; + softn->ipf_nat_instances = nat; + + /* + * Inbound hash table. + */ + natp = &softn->ipf_nat_table[0][hv0]; nat->nat_phnext[0] = natp; nat->nat_hnext[0] = *natp; + if (*natp) { + (*natp)->nat_phnext[0] = &nat->nat_hnext[0]; + } else { + NBUMPSIDE(0, ns_inuse); + } *natp = nat; - nat_stats.ns_bucketlen[0][hv1]++; + NBUMPSIDE(0, ns_bucketlen[hv0]); - natp = &nat_table[1][hv2]; - if (*natp) - (*natp)->nat_phnext[1] = &nat->nat_hnext[1]; + /* + * Outbound hash table. + */ + natp = &softn->ipf_nat_table[1][hv1]; nat->nat_phnext[1] = natp; nat->nat_hnext[1] = *natp; + if (*natp) + (*natp)->nat_phnext[1] = &nat->nat_hnext[1]; + else { + NBUMPSIDE(1, ns_inuse); + } *natp = nat; - nat_stats.ns_bucketlen[1][hv2]++; + NBUMPSIDE(1, ns_bucketlen[hv1]); - fr_setnatqueue(nat, rev); + ipf_nat_setqueue(softc, softn, nat); - nat_stats.ns_added++; - nat_stats.ns_inuse++; + if (nat->nat_dir & NAT_OUTBOUND) { + NBUMPSIDE(1, ns_added); + } else { + NBUMPSIDE(0, ns_added); + } + softn->ipf_nat_stats.ns_active++; return 0; } /* ------------------------------------------------------------------------ */ -/* Function: nat_icmperrorlookup */ +/* Function: ipf_nat_icmperrorlookup */ /* Returns: nat_t* - point to matching NAT structure */ /* Parameters: fin(I) - pointer to packet information */ /* dir(I) - direction of packet (in/out) */ @@ -2741,12 +3612,16 @@ int rev; /* ICMP query nat entry. It is assumed that the packet is already of the */ /* the required length. */ /* ------------------------------------------------------------------------ */ -nat_t *nat_icmperrorlookup(fin, dir) -fr_info_t *fin; -int dir; +nat_t * +ipf_nat_icmperrorlookup(fin, dir) + fr_info_t *fin; + int dir; { + ipf_main_softc_t *softc = fin->fin_main_soft; + ipf_nat_softc_t *softn = softc->ipf_nat_soft; int flags = 0, type, minlen; icmphdr_t *icmp, *orgicmp; + nat_stat_side_t *nside; tcphdr_t *tcp = NULL; u_short data[2]; nat_t *nat; @@ -2755,13 +3630,16 @@ int dir; icmp = fin->fin_dp; type = icmp->icmp_type; + nside = &softn->ipf_nat_stats.ns_side[fin->fin_out]; /* * Does it at least have the return (basic) IP header ? * Only a basic IP header (no options) should be with an ICMP error * header. Also, if it's not an error type, then return. */ - if ((fin->fin_hlen != sizeof(ip_t)) || !(fin->fin_flx & FI_ICMPERR)) + if ((fin->fin_hlen != sizeof(ip_t)) || !(fin->fin_flx & FI_ICMPERR)) { + ATOMIC_INCL(nside->ns_icmp_basic); return NULL; + } /* * Check packet size @@ -2769,35 +3647,45 @@ int dir; oip = (ip_t *)((char *)fin->fin_dp + 8); minlen = IP_HL(oip) << 2; if ((minlen < sizeof(ip_t)) || - (fin->fin_plen < ICMPERR_IPICMPHLEN + minlen)) + (fin->fin_plen < ICMPERR_IPICMPHLEN + minlen)) { + ATOMIC_INCL(nside->ns_icmp_size); return NULL; + } + /* * Is the buffer big enough for all of it ? It's the size of the IP * header claimed in the encapsulated part which is of concern. It * may be too big to be in this buffer but not so big that it's * outside the ICMP packet, leading to TCP deref's causing problems. * This is possible because we don't know how big oip_hl is when we - * do the pullup early in fr_check() and thus can't gaurantee it is + * do the pullup early in ipf_check() and thus can't gaurantee it is * all here now. */ -#ifdef _KERNEL +#ifdef ipf_nat_KERNEL { mb_t *m; m = fin->fin_m; # if defined(MENTAT) - if ((char *)oip + fin->fin_dlen - ICMPERR_ICMPHLEN > (char *)m->b_wptr) + if ((char *)oip + fin->fin_dlen - ICMPERR_ICMPHLEN > + (char *)m->b_wptr) { + ATOMIC_INCL(nside->ns_icmp_mbuf); return NULL; + } # else if ((char *)oip + fin->fin_dlen - ICMPERR_ICMPHLEN > - (char *)fin->fin_ip + M_LEN(m)) + (char *)fin->fin_ip + M_LEN(m)) { + ATOMIC_INCL(nside->ns_icmp_mbuf); return NULL; + } # endif } #endif - if (fin->fin_daddr != oip->ip_src.s_addr) + if (fin->fin_daddr != oip->ip_src.s_addr) { + ATOMIC_INCL(nside->ns_icmp_address); return NULL; + } p = oip->ip_p; if (p == IPPROTO_TCP) @@ -2808,7 +3696,7 @@ int dir; orgicmp = (icmphdr_t *)((char *)oip + (IP_HL(oip) << 2)); /* see if this is related to an ICMP query */ - if (nat_icmpquerytype4(orgicmp->icmp_type)) { + if (ipf_nat_icmpquerytype(orgicmp->icmp_type)) { data[0] = fin->fin_data[0]; data[1] = fin->fin_data[1]; fin->fin_data[0] = 0; @@ -2821,21 +3709,26 @@ int dir; * message flows in the opposite direction. */ if (dir == NAT_INBOUND) - nat = nat_inlookup(fin, flags, p, oip->ip_dst, - oip->ip_src); + nat = ipf_nat_inlookup(fin, flags, p, + oip->ip_dst, + oip->ip_src); else - nat = nat_outlookup(fin, flags, p, oip->ip_dst, - oip->ip_src); + nat = ipf_nat_outlookup(fin, flags, p, + oip->ip_dst, + oip->ip_src); fin->fin_data[0] = data[0]; fin->fin_data[1] = data[1]; return nat; } } - + if (flags & IPN_TCPUDP) { minlen += 8; /* + 64bits of data to get ports */ - if (fin->fin_plen < ICMPERR_IPICMPHLEN + minlen) + /* TRACE (fin,minlen) */ + if (fin->fin_plen < ICMPERR_IPICMPHLEN + minlen) { + ATOMIC_INCL(nside->ns_icmp_short); return NULL; + } data[0] = fin->fin_data[0]; data[1] = fin->fin_data[1]; @@ -2844,10 +3737,10 @@ int dir; fin->fin_data[1] = ntohs(tcp->th_sport); if (dir == NAT_INBOUND) { - nat = nat_inlookup(fin, flags, p, oip->ip_dst, - oip->ip_src); + nat = ipf_nat_inlookup(fin, flags, p, oip->ip_dst, + oip->ip_src); } else { - nat = nat_outlookup(fin, flags, p, oip->ip_dst, + nat = ipf_nat_outlookup(fin, flags, p, oip->ip_dst, oip->ip_src); } fin->fin_data[0] = data[0]; @@ -2855,14 +3748,16 @@ int dir; return nat; } if (dir == NAT_INBOUND) - return nat_inlookup(fin, 0, p, oip->ip_dst, oip->ip_src); + nat = ipf_nat_inlookup(fin, 0, p, oip->ip_dst, oip->ip_src); else - return nat_outlookup(fin, 0, p, oip->ip_dst, oip->ip_src); + nat = ipf_nat_outlookup(fin, 0, p, oip->ip_dst, oip->ip_src); + + return nat; } /* ------------------------------------------------------------------------ */ -/* Function: nat_icmperror */ +/* Function: ipf_nat_icmperror */ /* Returns: nat_t* - point to matching NAT structure */ /* Parameters: fin(I) - pointer to packet information */ /* nflags(I) - NAT flags for this packet */ @@ -2874,13 +3769,16 @@ int dir; /* This should *ONLY* be used for incoming ICMP error packets to make sure */ /* a NAT'd ICMP packet gets correctly recognised. */ /* ------------------------------------------------------------------------ */ -nat_t *nat_icmperror(fin, nflags, dir) -fr_info_t *fin; -u_int *nflags; -int dir; +nat_t * +ipf_nat_icmperror(fin, nflags, dir) + fr_info_t *fin; + u_int *nflags; + int dir; { + ipf_main_softc_t *softc = fin->fin_main_soft; + ipf_nat_softc_t *softn = softc->ipf_nat_soft; u_32_t sum1, sum2, sumd, sumd2; - struct in_addr a1, a2; + struct in_addr a1, a2, a3, a4; int flags, dlen, odst; icmphdr_t *icmp; u_short *csump; @@ -2889,13 +3787,18 @@ int dir; ip_t *oip; void *dp; - if ((fin->fin_flx & (FI_SHORT|FI_FRAGBODY))) + if ((fin->fin_flx & (FI_SHORT|FI_FRAGBODY))) { + NBUMPSIDED(fin->fin_out, ns_icmp_short); return NULL; + } + /* - * nat_icmperrorlookup() will return NULL for `defective' packets. + * ipf_nat_icmperrorlookup() will return NULL for `defective' packets. */ - if ((fin->fin_v != 4) || !(nat = nat_icmperrorlookup(fin, dir))) + if ((fin->fin_v != 4) || !(nat = ipf_nat_icmperrorlookup(fin, dir))) { + NBUMPSIDED(fin->fin_out, ns_icmp_notfound); return NULL; + } tcp = NULL; csump = NULL; @@ -2923,7 +3826,7 @@ int dir; /* * Need to adjust ICMP header to include the real IP#'s and * port #'s. Only apply a checksum change relative to the - * IP address change as it will be modified again in fr_checknatout + * IP address change as it will be modified again in ipf_nat_checkout * for both address and port. Two checksum changes are * necessary for the two header address changes. Be careful * to only modify the checksum once for the port # and twice @@ -2949,42 +3852,73 @@ int dir; * ------------ * MAP rule, SRC=a,DST=b -> SRC=c,DST=b * - response to outgoing packet (a,b)=>(c,b) (OIP_SRC=c,OIP_DST=b) - * - OIP_SRC(c)=nat_outip, OIP_DST(b)=nat_oip + * - OIP_SRC(c)=nat_newsrcip, OIP_DST(b)=nat_newdstip + *=> OIP_SRC(c)=nat_oldsrcip, OIP_DST(b)=nat_olddstip * * RDR rule, SRC=a,DST=b -> SRC=a,DST=c * - response to outgoing packet (c,a)=>(b,a) (OIP_SRC=b,OIP_DST=a) - * - OIP_SRC(b)=nat_outip, OIP_DST(a)=nat_oip + * - OIP_SRC(b)=nat_olddstip, OIP_DST(a)=nat_oldsrcip + *=> OIP_SRC(b)=nat_newdstip, OIP_DST(a)=nat_newsrcip + * + * REWRITE out rule, SRC=a,DST=b -> SRC=c,DST=d + * - response to outgoing packet (a,b)=>(c,d) (OIP_SRC=c,OIP_DST=d) + * - OIP_SRC(c)=nat_newsrcip, OIP_DST(d)=nat_newdstip + *=> OIP_SRC(c)=nat_oldsrcip, OIP_DST(d)=nat_olddstip + * + * REWRITE in rule, SRC=a,DST=b -> SRC=c,DST=d + * - response to outgoing packet (d,c)=>(b,a) (OIP_SRC=b,OIP_DST=a) + * - OIP_SRC(b)=nat_olddstip, OIP_DST(a)=nat_oldsrcip + *=> OIP_SRC(b)=nat_newdstip, OIP_DST(a)=nat_newsrcip * * Outbound ICMP * ------------- * MAP rule, SRC=a,DST=b -> SRC=c,DST=b * - response to incoming packet (b,c)=>(b,a) (OIP_SRC=b,OIP_DST=a) - * - OIP_SRC(a)=nat_oip, OIP_DST(c)=nat_inip + * - OIP_SRC(b)=nat_olddstip, OIP_DST(a)=nat_oldsrcip + *=> OIP_SRC(b)=nat_newdstip, OIP_DST(a)=nat_newsrcip * * RDR rule, SRC=a,DST=b -> SRC=a,DST=c * - response to incoming packet (a,b)=>(a,c) (OIP_SRC=a,OIP_DST=c) - * - OIP_SRC(a)=nat_oip, OIP_DST(c)=nat_inip + * - OIP_SRC(a)=nat_newsrcip, OIP_DST(c)=nat_newdstip + *=> OIP_SRC(a)=nat_oldsrcip, OIP_DST(c)=nat_olddstip * + * REWRITE out rule, SRC=a,DST=b -> SRC=c,DST=d + * - response to incoming packet (d,c)=>(b,a) (OIP_SRC=c,OIP_DST=d) + * - OIP_SRC(c)=nat_olddstip, OIP_DST(d)=nat_oldsrcip + *=> OIP_SRC(b)=nat_newdstip, OIP_DST(a)=nat_newsrcip + * + * REWRITE in rule, SRC=a,DST=b -> SRC=c,DST=d + * - response to incoming packet (a,b)=>(c,d) (OIP_SRC=b,OIP_DST=a) + * - OIP_SRC(b)=nat_newsrcip, OIP_DST(a)=nat_newdstip + *=> OIP_SRC(a)=nat_oldsrcip, OIP_DST(c)=nat_olddstip */ - odst = (oip->ip_dst.s_addr == nat->nat_oip.s_addr) ? 1 : 0; - if (odst == 1) { - a1.s_addr = ntohl(nat->nat_inip.s_addr); - a2.s_addr = ntohl(oip->ip_src.s_addr); - oip->ip_src.s_addr = htonl(a1.s_addr); - } else { - a1.s_addr = ntohl(nat->nat_outip.s_addr); + + if (((fin->fin_out == 0) && ((nat->nat_redir & NAT_MAP) != 0)) || + ((fin->fin_out == 1) && ((nat->nat_redir & NAT_REDIRECT) != 0))) { + a1.s_addr = ntohl(nat->nat_osrcaddr); + a4.s_addr = ntohl(oip->ip_src.s_addr); + a3.s_addr = ntohl(nat->nat_odstaddr); a2.s_addr = ntohl(oip->ip_dst.s_addr); - oip->ip_dst.s_addr = htonl(a1.s_addr); - } - - sumd = a2.s_addr - a1.s_addr; - if (sumd != 0) { - if (a1.s_addr > a2.s_addr) - sumd--; - sumd = ~sumd; - - fix_datacksum(&oip->ip_sum, sumd); + oip->ip_src.s_addr = htonl(a1.s_addr); + oip->ip_dst.s_addr = htonl(a3.s_addr); + odst = 1; + } else { + a1.s_addr = ntohl(nat->nat_ndstaddr); + a2.s_addr = ntohl(oip->ip_dst.s_addr); + a3.s_addr = ntohl(nat->nat_nsrcaddr); + a4.s_addr = ntohl(oip->ip_src.s_addr); + oip->ip_dst.s_addr = htonl(a3.s_addr); + oip->ip_src.s_addr = htonl(a1.s_addr); + odst = 0; } + sum1 = 0; + sum2 = 0; + sumd = 0; + CALC_SUMD(a2.s_addr, a3.s_addr, sum1); + CALC_SUMD(a4.s_addr, a1.s_addr, sum2); + sumd = sum2 + sum1; + if (sumd != 0) + ipf_fix_datacksum(&oip->ip_sum, sumd); sumd2 = sumd; sum1 = 0; @@ -2995,6 +3929,8 @@ int dir; * IP address change. */ if (((flags & IPN_TCPUDP) != 0) && (dlen >= 4)) { + u_32_t sum3, sum4, sumt; + /* * Step 2 : * For offending TCP/UDP IP packets, translate the ports as @@ -3003,24 +3939,33 @@ int dir; * * Since the port fields are part of the TCP/UDP checksum * of the offending IP packet, you need to adjust that checksum - * as well... except that the change in the port numbers should + * as well... except that the change in the port numbers should * be offset by the checksum change. However, the TCP/UDP * checksum will also need to change if there has been an * IP address change. */ if (odst == 1) { - sum1 = ntohs(nat->nat_inport); - sum2 = ntohs(tcp->th_sport); - - tcp->th_sport = htons(sum1); - } else { - sum1 = ntohs(nat->nat_outport); + sum1 = ntohs(nat->nat_osport); + sum4 = ntohs(tcp->th_sport); + sum3 = ntohs(nat->nat_odport); sum2 = ntohs(tcp->th_dport); - tcp->th_dport = htons(sum1); - } + tcp->th_sport = htons(sum1); + tcp->th_dport = htons(sum3); + } else { + sum1 = ntohs(nat->nat_ndport); + sum2 = ntohs(tcp->th_dport); + sum3 = ntohs(nat->nat_nsport); + sum4 = ntohs(tcp->th_sport); + + tcp->th_dport = htons(sum3); + tcp->th_sport = htons(sum1); + } + CALC_SUMD(sum4, sum1, sumt); + sumd += sumt; + CALC_SUMD(sum2, sum3, sumt); + sumd += sumt; - sumd += sum1 - sum2; if (sumd != 0 || sumd2 != 0) { /* * At this point, sumd is the delta to apply to the @@ -3038,39 +3983,26 @@ int dir; */ if (oip->ip_p == IPPROTO_UDP) { if ((dlen >= 8) && (*csump != 0)) { - fix_datacksum(csump, sumd); + ipf_fix_datacksum(csump, sumd); } else { - sumd2 = sum1 - sum2; - if (sum2 > sum1) - sumd2--; + CALC_SUMD(sum1, sum4, sumd2); + CALC_SUMD(sum3, sum2, sumt); + sumd2 += sumt; } } else if (oip->ip_p == IPPROTO_TCP) { if (dlen >= 18) { - fix_datacksum(csump, sumd); + ipf_fix_datacksum(csump, sumd); } else { - sumd2 = sum2 - sum1; - if (sum1 > sum2) - sumd2--; + CALC_SUMD(sum1, sum4, sumd2); + CALC_SUMD(sum3, sum2, sumt); + sumd2 += sumt; } } - if (sumd2 != 0) { - ipnat_t *np; - - np = nat->nat_ptr; sumd2 = (sumd2 & 0xffff) + (sumd2 >> 16); sumd2 = (sumd2 & 0xffff) + (sumd2 >> 16); sumd2 = (sumd2 & 0xffff) + (sumd2 >> 16); - - if ((odst == 0) && (dir == NAT_OUTBOUND) && - (fin->fin_rev == 0) && (np != NULL) && - (np->in_redir & NAT_REDIRECT)) { - fix_outcksum(fin, &icmp->icmp_cksum, - sumd2); - } else { - fix_incksum(fin, &icmp->icmp_cksum, - sumd2); - } + ipf_fix_incksum(0, &icmp->icmp_cksum, sumd2, 0); } } } else if (((flags & IPN_ICMPQUERY) != 0) && (dlen >= 8)) { @@ -3078,12 +4010,13 @@ int dir; /* * XXX - what if this is bogus hl and we go off the end ? - * In this case, nat_icmperrorlookup() will have returned NULL. + * In this case, ipf_nat_icmperrorlookup() will have + * returned NULL. */ orgicmp = (icmphdr_t *)dp; if (odst == 1) { - if (orgicmp->icmp_id != nat->nat_inport) { + if (orgicmp->icmp_id != nat->nat_osport) { /* * Fix ICMP checksum (of the offening ICMP @@ -3098,10 +4031,10 @@ int dir; * overall icmp->icmp_cksum */ sum1 = ntohs(orgicmp->icmp_id); - sum2 = ntohs(nat->nat_inport); + sum2 = ntohs(nat->nat_oicmpid); CALC_SUMD(sum1, sum2, sumd); - orgicmp->icmp_id = nat->nat_inport; - fix_datacksum(&orgicmp->icmp_cksum, sumd); + orgicmp->icmp_id = nat->nat_oicmpid; + ipf_fix_datacksum(&orgicmp->icmp_cksum, sumd); } } /* nat_dir == NAT_INBOUND is impossible for icmp queries */ } @@ -3109,13 +4042,20 @@ int dir; } +/* + * MAP-IN MAP-OUT RDR-IN RDR-OUT + * osrc X == src == src X + * odst X == dst == dst X + * nsrc == dst X X == dst + * ndst == src X X == src + * MAP = NAT_OUTBOUND, RDR = NAT_INBOUND + */ /* * NB: these lookups don't lock access to the list, it assumed that it has * already been done! */ - /* ------------------------------------------------------------------------ */ -/* Function: nat_inlookup */ +/* Function: ipf_nat_inlookup */ /* Returns: nat_t* - NULL == no match, */ /* else pointer to matching NAT entry */ /* Parameters: fin(I) - pointer to packet information */ @@ -3130,17 +4070,20 @@ int dir; /* */ /* NOTE: THE PACKET BEING CHECKED (IF FOUND) HAS A MAPPING ALREADY. */ /* */ -/* NOTE: IT IS ASSUMED THAT ipf_nat IS ONLY HELD WITH A READ LOCK WHEN */ +/* NOTE: IT IS ASSUMED THAT IS ONLY HELD WITH A READ LOCK WHEN */ /* THIS FUNCTION IS CALLED WITH NAT_SEARCH SET IN nflags. */ /* */ /* flags -> relevant are IPN_UDP/IPN_TCP/IPN_ICMPQUERY that indicate if */ /* the packet is of said protocol */ /* ------------------------------------------------------------------------ */ -nat_t *nat_inlookup(fin, flags, p, src, mapdst) -fr_info_t *fin; -u_int flags, p; -struct in_addr src , mapdst; +nat_t * +ipf_nat_inlookup(fin, flags, p, src, mapdst) + fr_info_t *fin; + u_int flags, p; + struct in_addr src , mapdst; { + ipf_main_softc_t *softc = fin->fin_main_soft; + ipf_nat_softc_t *softn = softc->ipf_nat_soft; u_short sport, dport; grehdr_t *gre; ipnat_t *ipn; @@ -3149,11 +4092,9 @@ struct in_addr src , mapdst; int nflags; u_32_t dst; void *ifp; - u_int hv; + u_int hv, rhv; ifp = fin->fin_ifp; - sport = 0; - dport = 0; gre = NULL; dst = mapdst.s_addr; sflags = flags & NAT_TCPUDPICMP; @@ -3166,12 +4107,17 @@ struct in_addr src , mapdst; dport = htons(fin->fin_data[1]); break; case IPPROTO_ICMP : - if (flags & IPN_ICMPERR) + if (flags & IPN_ICMPERR) { sport = fin->fin_data[1]; - else + dport = 0; + } else { dport = fin->fin_data[1]; + sport = 0; + } break; default : + sport = 0; + dport = 0; break; } @@ -3179,57 +4125,81 @@ struct in_addr src , mapdst; if ((flags & SI_WILDP) != 0) goto find_in_wild_ports; - hv = NAT_HASH_FN(dst, dport, 0xffffffff); - hv = NAT_HASH_FN(src.s_addr, hv + sport, ipf_nattable_sz); - nat = nat_table[1][hv]; + rhv = NAT_HASH_FN(dst, dport, 0xffffffff); + rhv = NAT_HASH_FN(src.s_addr, rhv + sport, 0xffffffff); + hv = rhv % softn->ipf_nat_table_sz; + nat = softn->ipf_nat_table[1][hv]; + /* TRACE dst, dport, src, sport, hv, nat */ + for (; nat; nat = nat->nat_hnext[1]) { if (nat->nat_ifps[0] != NULL) { if ((ifp != NULL) && (ifp != nat->nat_ifps[0])) continue; - } else if (ifp != NULL) - nat->nat_ifps[0] = ifp; + } - nflags = nat->nat_flags; + if (nat->nat_pr[0] != p) + continue; - if (nat->nat_oip.s_addr == src.s_addr && - nat->nat_outip.s_addr == dst && - (((p == 0) && - (sflags == (nat->nat_flags & IPN_TCPUDPICMP))) - || (p == nat->nat_p))) { - switch (p) - { -#if 0 - case IPPROTO_GRE : - if (nat->nat_call[1] != fin->fin_data[0]) + switch (nat->nat_dir) + { + case NAT_INBOUND : + case NAT_DIVERTIN : + if (nat->nat_v[0] != 4) + continue; + if (nat->nat_osrcaddr != src.s_addr || + nat->nat_odstaddr != dst) + continue; + if ((nat->nat_flags & IPN_TCPUDP) != 0) { + if (nat->nat_osport != sport) + continue; + if (nat->nat_odport != dport) + continue; + + } else if (p == IPPROTO_ICMP) { + if (nat->nat_osport != dport) { continue; - break; -#endif - case IPPROTO_ICMP : - if ((flags & IPN_ICMPERR) != 0) { - if (nat->nat_outport != sport) - continue; - } else { - if (nat->nat_outport != dport) - continue; } - break; - case IPPROTO_TCP : - case IPPROTO_UDP : - if (nat->nat_oport != sport) - continue; - if (nat->nat_outport != dport) - continue; - break; - default : - break; } + break; + case NAT_DIVERTOUT : + if (nat->nat_dlocal) + continue; + case NAT_OUTBOUND : + if (nat->nat_v[1] != 4) + continue; + if (nat->nat_dlocal) + continue; + if (nat->nat_dlocal) + continue; + if (nat->nat_ndstaddr != src.s_addr || + nat->nat_nsrcaddr != dst) + continue; + if ((nat->nat_flags & IPN_TCPUDP) != 0) { + if (nat->nat_ndport != sport) + continue; + if (nat->nat_nsport != dport) + continue; + } else if (p == IPPROTO_ICMP) { + if (nat->nat_osport != dport) { + continue; + } + } + break; + } + + + if ((nat->nat_flags & IPN_TCPUDP) != 0) { ipn = nat->nat_ptr; if ((ipn != NULL) && (nat->nat_aps != NULL)) - if (appr_match(fin, nat) != 0) + if (ipf_proxy_match(fin, nat) != 0) continue; - return nat; } + if ((nat->nat_ifps[0] == NULL) && (ifp != NULL)) { + nat->nat_ifps[0] = ifp; + nat->nat_mtu[0] = GETIFMTU_4(ifp); + } + return nat; } /* @@ -3240,126 +4210,191 @@ struct in_addr src , mapdst; * for "dummy" (FI_IGNORE) lookups. */ find_in_wild_ports: - if (!(flags & NAT_TCPUDP) || !(flags & NAT_SEARCH)) + if (!(flags & NAT_TCPUDP) || !(flags & NAT_SEARCH)) { + NBUMPSIDEX(0, ns_lookup_miss, ns_lookup_miss_0); return NULL; - if (nat_stats.ns_wilds == 0) + } + if (softn->ipf_nat_stats.ns_wilds == 0 || (fin->fin_flx & FI_NOWILD)) { + NBUMPSIDEX(0, ns_lookup_nowild, ns_lookup_nowild_0); return NULL; + } - RWLOCK_EXIT(&ipf_nat); + RWLOCK_EXIT(&softc->ipf_nat); hv = NAT_HASH_FN(dst, 0, 0xffffffff); - hv = NAT_HASH_FN(src.s_addr, hv, ipf_nattable_sz); + hv = NAT_HASH_FN(src.s_addr, hv, softn->ipf_nat_table_sz); + WRITE_ENTER(&softc->ipf_nat); - WRITE_ENTER(&ipf_nat); - - nat = nat_table[1][hv]; + nat = softn->ipf_nat_table[1][hv]; + /* TRACE dst, src, hv, nat */ for (; nat; nat = nat->nat_hnext[1]) { if (nat->nat_ifps[0] != NULL) { if ((ifp != NULL) && (ifp != nat->nat_ifps[0])) continue; - } else if (ifp != NULL) - nat->nat_ifps[0] = ifp; + } - if (nat->nat_p != fin->fin_p) - continue; - if (nat->nat_oip.s_addr != src.s_addr || - nat->nat_outip.s_addr != dst) + if (nat->nat_pr[0] != fin->fin_p) continue; + switch (nat->nat_dir & (NAT_INBOUND|NAT_OUTBOUND)) + { + case NAT_INBOUND : + if (nat->nat_v[0] != 4) + continue; + if (nat->nat_osrcaddr != src.s_addr || + nat->nat_odstaddr != dst) + continue; + break; + case NAT_OUTBOUND : + if (nat->nat_v[1] != 4) + continue; + if (nat->nat_ndstaddr != src.s_addr || + nat->nat_nsrcaddr != dst) + continue; + break; + } + nflags = nat->nat_flags; if (!(nflags & (NAT_TCPUDP|SI_WILDP))) continue; - if (nat_wildok(nat, (int)sport, (int)dport, nflags, - NAT_INBOUND) == 1) { + if (ipf_nat_wildok(nat, (int)sport, (int)dport, nflags, + NAT_INBOUND) == 1) { if ((fin->fin_flx & FI_IGNORE) != 0) break; if ((nflags & SI_CLONE) != 0) { - nat = fr_natclone(fin, nat); + nat = ipf_nat_clone(fin, nat); if (nat == NULL) break; } else { - MUTEX_ENTER(&ipf_nat_new); - nat_stats.ns_wilds--; - MUTEX_EXIT(&ipf_nat_new); + MUTEX_ENTER(&softn->ipf_nat_new); + softn->ipf_nat_stats.ns_wilds--; + MUTEX_EXIT(&softn->ipf_nat_new); + } + + if (nat->nat_dir == NAT_INBOUND) { + if (nat->nat_osport == 0) { + nat->nat_osport = sport; + nat->nat_nsport = sport; + } + if (nat->nat_odport == 0) { + nat->nat_odport = dport; + nat->nat_ndport = dport; + } + } else if (nat->nat_dir == NAT_OUTBOUND) { + if (nat->nat_osport == 0) { + nat->nat_osport = dport; + nat->nat_nsport = dport; + } + if (nat->nat_odport == 0) { + nat->nat_odport = sport; + nat->nat_ndport = sport; + } + } + if ((nat->nat_ifps[0] == NULL) && (ifp != NULL)) { + nat->nat_ifps[0] = ifp; + nat->nat_mtu[0] = GETIFMTU_4(ifp); } - nat->nat_oport = sport; - nat->nat_outport = dport; nat->nat_flags &= ~(SI_W_DPORT|SI_W_SPORT); - nat_tabmove(nat); + ipf_nat_tabmove(softn, nat); break; } } - MUTEX_DOWNGRADE(&ipf_nat); + MUTEX_DOWNGRADE(&softc->ipf_nat); + if (nat == NULL) { + NBUMPSIDE(0, ns_lookup_miss); + } return nat; } /* ------------------------------------------------------------------------ */ -/* Function: nat_tabmove */ +/* Function: ipf_nat_tabmove */ /* Returns: Nil */ -/* Parameters: nat(I) - pointer to NAT structure */ +/* Parameters: softn(I) - pointer to NAT context structure */ +/* nat(I) - pointer to NAT structure */ /* Write Lock: ipf_nat */ /* */ /* This function is only called for TCP/UDP NAT table entries where the */ /* original was placed in the table without hashing on the ports and we now */ /* want to include hashing on port numbers. */ /* ------------------------------------------------------------------------ */ -static void nat_tabmove(nat) -nat_t *nat; +static void +ipf_nat_tabmove(softn, nat) + ipf_nat_softc_t *softn; + nat_t *nat; { + u_int hv0, hv1, rhv0, rhv1; + natstat_t *nsp; nat_t **natp; - u_int hv; if (nat->nat_flags & SI_CLONE) return; + nsp = &softn->ipf_nat_stats; /* * Remove the NAT entry from the old location */ if (nat->nat_hnext[0]) nat->nat_hnext[0]->nat_phnext[0] = nat->nat_phnext[0]; *nat->nat_phnext[0] = nat->nat_hnext[0]; - nat_stats.ns_bucketlen[0][nat->nat_hv[0]]--; + nsp->ns_side[0].ns_bucketlen[nat->nat_hv[0] % + softn->ipf_nat_table_sz]--; if (nat->nat_hnext[1]) nat->nat_hnext[1]->nat_phnext[1] = nat->nat_phnext[1]; *nat->nat_phnext[1] = nat->nat_hnext[1]; - nat_stats.ns_bucketlen[1][nat->nat_hv[1]]--; + nsp->ns_side[1].ns_bucketlen[nat->nat_hv[1] % + softn->ipf_nat_table_sz]--; /* * Add into the NAT table in the new position */ - hv = NAT_HASH_FN(nat->nat_inip.s_addr, nat->nat_inport, 0xffffffff); - hv = NAT_HASH_FN(nat->nat_oip.s_addr, hv + nat->nat_oport, - ipf_nattable_sz); - nat->nat_hv[0] = hv; - natp = &nat_table[0][hv]; + rhv0 = NAT_HASH_FN(nat->nat_osrcaddr, nat->nat_osport, 0xffffffff); + rhv0 = NAT_HASH_FN(nat->nat_odstaddr, rhv0 + nat->nat_odport, + 0xffffffff); + rhv1 = NAT_HASH_FN(nat->nat_nsrcaddr, nat->nat_nsport, 0xffffffff); + rhv1 = NAT_HASH_FN(nat->nat_ndstaddr, rhv1 + nat->nat_ndport, + 0xffffffff); + + hv0 = rhv0 % softn->ipf_nat_table_sz; + hv1 = rhv1 % softn->ipf_nat_table_sz; + + if (nat->nat_dir == NAT_INBOUND || nat->nat_dir == NAT_DIVERTIN) { + u_int swap; + + swap = hv0; + hv0 = hv1; + hv1 = swap; + } + + /* TRACE nat_osrcaddr, nat_osport, nat_odstaddr, nat_odport, hv0 */ + /* TRACE nat_nsrcaddr, nat_nsport, nat_ndstaddr, nat_ndport, hv1 */ + + nat->nat_hv[0] = rhv0; + natp = &softn->ipf_nat_table[0][hv0]; if (*natp) (*natp)->nat_phnext[0] = &nat->nat_hnext[0]; nat->nat_phnext[0] = natp; nat->nat_hnext[0] = *natp; *natp = nat; - nat_stats.ns_bucketlen[0][hv]++; + nsp->ns_side[0].ns_bucketlen[hv0]++; - hv = NAT_HASH_FN(nat->nat_outip.s_addr, nat->nat_outport, 0xffffffff); - hv = NAT_HASH_FN(nat->nat_oip.s_addr, hv + nat->nat_oport, - ipf_nattable_sz); - nat->nat_hv[1] = hv; - natp = &nat_table[1][hv]; + nat->nat_hv[1] = rhv1; + natp = &softn->ipf_nat_table[1][hv1]; if (*natp) (*natp)->nat_phnext[1] = &nat->nat_hnext[1]; nat->nat_phnext[1] = natp; nat->nat_hnext[1] = *natp; *natp = nat; - nat_stats.ns_bucketlen[1][hv]++; + nsp->ns_side[1].ns_bucketlen[hv1]++; } /* ------------------------------------------------------------------------ */ -/* Function: nat_outlookup */ +/* Function: ipf_nat_outlookup */ /* Returns: nat_t* - NULL == no match, */ /* else pointer to matching NAT entry */ /* Parameters: fin(I) - pointer to packet information */ @@ -3367,7 +4402,7 @@ nat_t *nat; /* p(I) - protocol for this packet */ /* src(I) - source IP address */ /* dst(I) - destination IP address */ -/* rw(I) - 1 == write lock on ipf_nat held, 0 == read lock. */ +/* rw(I) - 1 == write lock on held, 0 == read lock. */ /* */ /* Lookup a nat entry based on the source 'real' ip address/port and */ /* destination address/port. We use this lookup when sending a packet out, */ @@ -3375,28 +4410,28 @@ nat_t *nat; /* */ /* NOTE: THE PACKET BEING CHECKED (IF FOUND) HAS A MAPPING ALREADY. */ /* */ -/* NOTE: IT IS ASSUMED THAT ipf_nat IS ONLY HELD WITH A READ LOCK WHEN */ +/* NOTE: IT IS ASSUMED THAT IS ONLY HELD WITH A READ LOCK WHEN */ /* THIS FUNCTION IS CALLED WITH NAT_SEARCH SET IN nflags. */ /* */ /* flags -> relevant are IPN_UDP/IPN_TCP/IPN_ICMPQUERY that indicate if */ /* the packet is of said protocol */ /* ------------------------------------------------------------------------ */ -nat_t *nat_outlookup(fin, flags, p, src, dst) -fr_info_t *fin; -u_int flags, p; -struct in_addr src , dst; +nat_t * +ipf_nat_outlookup(fin, flags, p, src, dst) + fr_info_t *fin; + u_int flags, p; + struct in_addr src , dst; { + ipf_main_softc_t *softc = fin->fin_main_soft; + ipf_nat_softc_t *softn = softc->ipf_nat_soft; u_short sport, dport; u_int sflags; ipnat_t *ipn; - u_32_t srcip; nat_t *nat; - int nflags; void *ifp; u_int hv; ifp = fin->fin_ifp; - srcip = src.s_addr; sflags = flags & IPN_TCPUDPICMP; sport = 0; dport = 0; @@ -3421,47 +4456,75 @@ struct in_addr src , dst; if ((flags & SI_WILDP) != 0) goto find_out_wild_ports; - hv = NAT_HASH_FN(srcip, sport, 0xffffffff); - hv = NAT_HASH_FN(dst.s_addr, hv + dport, ipf_nattable_sz); - nat = nat_table[0][hv]; + hv = NAT_HASH_FN(src.s_addr, sport, 0xffffffff); + hv = NAT_HASH_FN(dst.s_addr, hv + dport, softn->ipf_nat_table_sz); + nat = softn->ipf_nat_table[0][hv]; + + /* TRACE src, sport, dst, dport, hv, nat */ + for (; nat; nat = nat->nat_hnext[0]) { if (nat->nat_ifps[1] != NULL) { if ((ifp != NULL) && (ifp != nat->nat_ifps[1])) continue; - } else if (ifp != NULL) - nat->nat_ifps[1] = ifp; - - nflags = nat->nat_flags; - - if (nat->nat_inip.s_addr == srcip && - nat->nat_oip.s_addr == dst.s_addr && - (((p == 0) && (sflags == (nflags & NAT_TCPUDPICMP))) - || (p == nat->nat_p))) { - switch (p) - { -#if 0 - case IPPROTO_GRE : - if (nat->nat_call[1] != fin->fin_data[0]) - continue; - break; -#endif - case IPPROTO_TCP : - case IPPROTO_UDP : - if (nat->nat_oport != dport) - continue; - if (nat->nat_inport != sport) - continue; - break; - default : - break; - } - - ipn = nat->nat_ptr; - if ((ipn != NULL) && (nat->nat_aps != NULL)) - if (appr_match(fin, nat) != 0) - continue; - return nat; } + + if (nat->nat_pr[1] != p) + continue; + + switch (nat->nat_dir) + { + case NAT_INBOUND : + case NAT_DIVERTIN : + if (nat->nat_v[1] != 4) + continue; + if (nat->nat_ndstaddr != src.s_addr || + nat->nat_nsrcaddr != dst.s_addr) + continue; + + if ((nat->nat_flags & IPN_TCPUDP) != 0) { + if (nat->nat_ndport != sport) + continue; + if (nat->nat_nsport != dport) + continue; + + } else if (p == IPPROTO_ICMP) { + if (nat->nat_osport != dport) { + continue; + } + } + break; + case NAT_OUTBOUND : + case NAT_DIVERTOUT : + if (nat->nat_v[0] != 4) + continue; + if (nat->nat_osrcaddr != src.s_addr || + nat->nat_odstaddr != dst.s_addr) + continue; + + if ((nat->nat_flags & IPN_TCPUDP) != 0) { + if (nat->nat_odport != dport) + continue; + if (nat->nat_osport != sport) + continue; + + } else if (p == IPPROTO_ICMP) { + if (nat->nat_osport != dport) { + continue; + } + } + break; + } + + ipn = nat->nat_ptr; + if ((ipn != NULL) && (nat->nat_aps != NULL)) + if (ipf_proxy_match(fin, nat) != 0) + continue; + + if ((nat->nat_ifps[1] == NULL) && (ifp != NULL)) { + nat->nat_ifps[1] = ifp; + nat->nat_mtu[1] = GETIFMTU_4(ifp); + } + return nat; } /* @@ -3472,67 +4535,107 @@ struct in_addr src , dst; * for "dummy" (FI_IGNORE) lookups. */ find_out_wild_ports: - if (!(flags & NAT_TCPUDP) || !(flags & NAT_SEARCH)) + if (!(flags & NAT_TCPUDP) || !(flags & NAT_SEARCH)) { + NBUMPSIDEX(1, ns_lookup_miss, ns_lookup_miss_1); return NULL; - if (nat_stats.ns_wilds == 0) + } + if (softn->ipf_nat_stats.ns_wilds == 0 || (fin->fin_flx & FI_NOWILD)) { + NBUMPSIDEX(1, ns_lookup_nowild, ns_lookup_nowild_1); return NULL; + } - RWLOCK_EXIT(&ipf_nat); + RWLOCK_EXIT(&softc->ipf_nat); - hv = NAT_HASH_FN(srcip, 0, 0xffffffff); - hv = NAT_HASH_FN(dst.s_addr, hv, ipf_nattable_sz); + hv = NAT_HASH_FN(src.s_addr, 0, 0xffffffff); + hv = NAT_HASH_FN(dst.s_addr, hv, softn->ipf_nat_table_sz); - WRITE_ENTER(&ipf_nat); + WRITE_ENTER(&softc->ipf_nat); - nat = nat_table[0][hv]; + nat = softn->ipf_nat_table[0][hv]; for (; nat; nat = nat->nat_hnext[0]) { if (nat->nat_ifps[1] != NULL) { if ((ifp != NULL) && (ifp != nat->nat_ifps[1])) continue; - } else if (ifp != NULL) - nat->nat_ifps[1] = ifp; + } - if (nat->nat_p != fin->fin_p) - continue; - if ((nat->nat_inip.s_addr != srcip) || - (nat->nat_oip.s_addr != dst.s_addr)) + if (nat->nat_pr[1] != fin->fin_p) continue; - nflags = nat->nat_flags; - if (!(nflags & (NAT_TCPUDP|SI_WILDP))) + switch (nat->nat_dir & (NAT_INBOUND|NAT_OUTBOUND)) + { + case NAT_INBOUND : + if (nat->nat_v[1] != 4) + continue; + if (nat->nat_ndstaddr != src.s_addr || + nat->nat_nsrcaddr != dst.s_addr) + continue; + break; + case NAT_OUTBOUND : + if (nat->nat_v[0] != 4) + continue; + if (nat->nat_osrcaddr != src.s_addr || + nat->nat_odstaddr != dst.s_addr) + continue; + break; + } + + if (!(nat->nat_flags & (NAT_TCPUDP|SI_WILDP))) continue; - if (nat_wildok(nat, (int)sport, (int)dport, nflags, - NAT_OUTBOUND) == 1) { + if (ipf_nat_wildok(nat, (int)sport, (int)dport, nat->nat_flags, + NAT_OUTBOUND) == 1) { if ((fin->fin_flx & FI_IGNORE) != 0) break; - if ((nflags & SI_CLONE) != 0) { - nat = fr_natclone(fin, nat); + if ((nat->nat_flags & SI_CLONE) != 0) { + nat = ipf_nat_clone(fin, nat); if (nat == NULL) break; } else { - MUTEX_ENTER(&ipf_nat_new); - nat_stats.ns_wilds--; - MUTEX_EXIT(&ipf_nat_new); + MUTEX_ENTER(&softn->ipf_nat_new); + softn->ipf_nat_stats.ns_wilds--; + MUTEX_EXIT(&softn->ipf_nat_new); + } + + if (nat->nat_dir == NAT_OUTBOUND) { + if (nat->nat_osport == 0) { + nat->nat_osport = sport; + nat->nat_nsport = sport; + } + if (nat->nat_odport == 0) { + nat->nat_odport = dport; + nat->nat_ndport = dport; + } + } else if (nat->nat_dir == NAT_INBOUND) { + if (nat->nat_osport == 0) { + nat->nat_osport = dport; + nat->nat_nsport = dport; + } + if (nat->nat_odport == 0) { + nat->nat_odport = sport; + nat->nat_ndport = sport; + } + } + if ((nat->nat_ifps[1] == NULL) && (ifp != NULL)) { + nat->nat_ifps[1] = ifp; + nat->nat_mtu[1] = GETIFMTU_4(ifp); } - nat->nat_inport = sport; - nat->nat_oport = dport; - if (nat->nat_outport == 0) - nat->nat_outport = sport; nat->nat_flags &= ~(SI_W_DPORT|SI_W_SPORT); - nat_tabmove(nat); + ipf_nat_tabmove(softn, nat); break; } } - MUTEX_DOWNGRADE(&ipf_nat); + MUTEX_DOWNGRADE(&softc->ipf_nat); + if (nat == NULL) { + NBUMPSIDE(1, ns_lookup_miss); + } return nat; } /* ------------------------------------------------------------------------ */ -/* Function: nat_lookupredir */ +/* Function: ipf_nat_lookupredir */ /* Returns: nat_t* - NULL == no match, */ /* else pointer to matching NAT entry */ /* Parameters: np(I) - pointer to description of packet to find NAT table */ @@ -3550,8 +4653,9 @@ find_out_wild_ports: /* nl_in* = source information (untranslated) */ /* nl_out* = destination information (translated) */ /* ------------------------------------------------------------------------ */ -nat_t *nat_lookupredir(np) -natlookup_t *np; +nat_t * +ipf_nat_lookupredir(np) + natlookup_t *np; { fr_info_t fi; nat_t *nat; @@ -3577,34 +4681,34 @@ natlookup_t *np; * - default: we have the `in' and `out' address, look for `real'. */ if (np->nl_flags & IPN_IN) { - if ((nat = nat_inlookup(&fi, np->nl_flags, fi.fin_p, - np->nl_realip, np->nl_outip))) { - np->nl_inip = nat->nat_inip; - np->nl_inport = nat->nat_inport; + if ((nat = ipf_nat_inlookup(&fi, np->nl_flags, fi.fin_p, + np->nl_realip, np->nl_outip))) { + np->nl_inip = nat->nat_odstip; + np->nl_inport = nat->nat_odport; } } else { /* * If nl_inip is non null, this is a lookup based on the real * ip address. Else, we use the fake. */ - if ((nat = nat_outlookup(&fi, np->nl_flags, fi.fin_p, + if ((nat = ipf_nat_outlookup(&fi, np->nl_flags, fi.fin_p, np->nl_inip, np->nl_outip))) { if ((np->nl_flags & IPN_FINDFORWARD) != 0) { fr_info_t fin; bzero((char *)&fin, sizeof(fin)); - fin.fin_p = nat->nat_p; - fin.fin_data[0] = ntohs(nat->nat_outport); - fin.fin_data[1] = ntohs(nat->nat_oport); - if (nat_inlookup(&fin, np->nl_flags, fin.fin_p, - nat->nat_outip, - nat->nat_oip) != NULL) { + fin.fin_p = nat->nat_pr[0]; + fin.fin_data[0] = ntohs(nat->nat_ndport); + fin.fin_data[1] = ntohs(nat->nat_nsport); + if (ipf_nat_inlookup(&fin, np->nl_flags, + fin.fin_p, nat->nat_ndstip, + nat->nat_nsrcip) != NULL) { np->nl_flags &= ~IPN_FINDFORWARD; } } - np->nl_realip = nat->nat_outip; - np->nl_realport = nat->nat_outport; + np->nl_realip = nat->nat_ndstip; + np->nl_realport = nat->nat_ndport; } } @@ -3613,45 +4717,53 @@ natlookup_t *np; /* ------------------------------------------------------------------------ */ -/* Function: nat_match */ +/* Function: ipf_nat_match */ /* Returns: int - 0 == no match, 1 == match */ /* Parameters: fin(I) - pointer to packet information */ /* np(I) - pointer to NAT rule */ /* */ /* Pull the matching of a packet against a NAT rule out of that complex */ -/* loop inside fr_checknatin() and lay it out properly in its own function. */ +/* loop inside ipf_nat_checkin() and lay it out properly in its own function. */ /* ------------------------------------------------------------------------ */ -static int nat_match(fin, np) -fr_info_t *fin; -ipnat_t *np; +static int +ipf_nat_match(fin, np) + fr_info_t *fin; + ipnat_t *np; { + ipf_main_softc_t *softc = fin->fin_main_soft; frtuc_t *ft; + int match; - if (fin->fin_v != 4) - return 0; - - if (np->in_p && fin->fin_p != np->in_p) - return 0; - - if (fin->fin_out) { - if (!(np->in_redir & (NAT_MAP|NAT_MAPBLK))) - return 0; - if (((fin->fin_fi.fi_saddr & np->in_inmsk) != np->in_inip) - ^ ((np->in_flags & IPN_NOTSRC) != 0)) - return 0; - if (((fin->fin_fi.fi_daddr & np->in_srcmsk) != np->in_srcip) - ^ ((np->in_flags & IPN_NOTDST) != 0)) - return 0; - } else { - if (!(np->in_redir & NAT_REDIRECT)) - return 0; - if (((fin->fin_fi.fi_saddr & np->in_srcmsk) != np->in_srcip) - ^ ((np->in_flags & IPN_NOTSRC) != 0)) - return 0; - if (((fin->fin_fi.fi_daddr & np->in_outmsk) != np->in_outip) - ^ ((np->in_flags & IPN_NOTDST) != 0)) - return 0; + match = 0; + switch (np->in_osrcatype) + { + case FRI_NORMAL : + match = ((fin->fin_saddr & np->in_osrcmsk) != np->in_osrcaddr); + break; + case FRI_LOOKUP : + match = (*np->in_osrcfunc)(softc, np->in_osrcptr, + 4, &fin->fin_saddr, fin->fin_plen); + break; } + match ^= ((np->in_flags & IPN_NOTSRC) != 0); + if (match) + return 0; + + match = 0; + switch (np->in_odstatype) + { + case FRI_NORMAL : + match = ((fin->fin_daddr & np->in_odstmsk) != np->in_odstaddr); + break; + case FRI_LOOKUP : + match = (*np->in_odstfunc)(softc, np->in_odstptr, + 4, &fin->fin_daddr, fin->fin_plen); + break; + } + + match ^= ((np->in_flags & IPN_NOTDST) != 0); + if (match) + return 0; ft = &np->in_tuc; if (!(fin->fin_flx & FI_TCPUDP) || @@ -3661,28 +4773,33 @@ ipnat_t *np; return 1; } - return fr_tcpudpchk(fin, ft); + return ipf_tcpudpchk(&fin->fin_fi, ft); } /* ------------------------------------------------------------------------ */ -/* Function: nat_update */ +/* Function: ipf_nat_update */ /* Returns: Nil */ -/* Parameters: nat(I) - pointer to NAT structure */ -/* np(I) - pointer to NAT rule */ +/* Parameters: fin(I) - pointer to packet information */ +/* nat(I) - pointer to NAT structure */ /* */ /* Updates the lifetime of a NAT table entry for non-TCP packets. Must be */ -/* called with fin_rev updated - i.e. after calling nat_proto(). */ +/* called with fin_rev updated - i.e. after calling ipf_nat_proto(). */ +/* */ +/* This *MUST* be called after ipf_nat_proto() as it expects fin_rev to */ +/* already be set. */ /* ------------------------------------------------------------------------ */ -void nat_update(fin, nat, np) -fr_info_t *fin; -nat_t *nat; -ipnat_t *np; +void +ipf_nat_update(fin, nat) + fr_info_t *fin; + nat_t *nat; { + ipf_main_softc_t *softc = fin->fin_main_soft; + ipf_nat_softc_t *softn = softc->ipf_nat_soft; ipftq_t *ifq, *ifq2; ipftqent_t *tqe; + ipnat_t *np = nat->nat_ptr; - MUTEX_ENTER(&nat->nat_lock); tqe = &nat->nat_tqe; ifq = tqe->tqe_ifq; @@ -3691,51 +4808,36 @@ ipnat_t *np; * TCP, however, if it is TCP and there is no rule timeout set, * then do not update the timeout here. */ - if (np != NULL) + if (np != NULL) { + np->in_bytes[fin->fin_rev] += fin->fin_plen; ifq2 = np->in_tqehead[fin->fin_rev]; - else + } else { ifq2 = NULL; + } - if (nat->nat_p == IPPROTO_TCP && ifq2 == NULL) { - u_32_t end, ack; - u_char tcpflags; - tcphdr_t *tcp; - int dsize; - - tcp = fin->fin_dp; - tcpflags = tcp->th_flags; - dsize = fin->fin_dlen - (TCP_OFF(tcp) << 2) + - ((tcpflags & TH_SYN) ? 1 : 0) + - ((tcpflags & TH_FIN) ? 1 : 0); - - ack = ntohl(tcp->th_ack); - end = ntohl(tcp->th_seq) + dsize; - - if (SEQ_GT(ack, nat->nat_seqnext[1 - fin->fin_rev])) - nat->nat_seqnext[1 - fin->fin_rev] = ack; - - if (nat->nat_seqnext[fin->fin_rev] == 0) - nat->nat_seqnext[fin->fin_rev] = end; - - (void) fr_tcp_age(&nat->nat_tqe, fin, nat_tqb, 0); + if (nat->nat_pr[0] == IPPROTO_TCP && ifq2 == NULL) { + (void) ipf_tcp_age(&nat->nat_tqe, fin, softn->ipf_nat_tcptq, + 0, 2); } else { if (ifq2 == NULL) { - if (nat->nat_p == IPPROTO_UDP) - ifq2 = &nat_udptq; - else if (nat->nat_p == IPPROTO_ICMP) - ifq2 = &nat_icmptq; + if (nat->nat_pr[0] == IPPROTO_UDP) + ifq2 = fin->fin_rev ? &softn->ipf_nat_udpacktq : + &softn->ipf_nat_udptq; + else if (nat->nat_pr[0] == IPPROTO_ICMP || + nat->nat_pr[0] == IPPROTO_ICMPV6) + ifq2 = fin->fin_rev ? &softn->ipf_nat_icmpacktq: + &softn->ipf_nat_icmptq; else - ifq2 = &nat_iptq; + ifq2 = &softn->ipf_nat_iptq; } - fr_movequeue(tqe, ifq, ifq2); + ipf_movequeue(softc->ipf_ticks, tqe, ifq, ifq2); } - MUTEX_EXIT(&nat->nat_lock); } /* ------------------------------------------------------------------------ */ -/* Function: fr_checknatout */ +/* Function: ipf_nat_checkout */ /* Returns: int - -1 == packet failed NAT checks so block it, */ /* 0 == no packet translation occurred, */ /* 1 == packet was successfully translated. */ @@ -3749,29 +4851,46 @@ ipnat_t *np; /* NAT entry if a we matched a NAT rule. Lastly, actually change the */ /* packet header(s) as required. */ /* ------------------------------------------------------------------------ */ -int fr_checknatout(fin, passp) -fr_info_t *fin; -u_32_t *passp; +int +ipf_nat_checkout(fin, passp) + fr_info_t *fin; + u_32_t *passp; { + ipnat_t *np = NULL, *npnext; struct ifnet *ifp, *sifp; + ipf_main_softc_t *softc; + ipf_nat_softc_t *softn; icmphdr_t *icmp = NULL; tcphdr_t *tcp = NULL; int rval, natfailed; - ipnat_t *np = NULL; u_int nflags = 0; u_32_t ipa, iph; int natadd = 1; frentry_t *fr; nat_t *nat; - if (nat_stats.ns_rules == 0 || fr_nat_lock != 0) + if (fin->fin_v == 6) { +#ifdef USE_INET6 + return ipf_nat6_checkout(fin, passp); +#else + return 0; +#endif + } + + softc = fin->fin_main_soft; + softn = softc->ipf_nat_soft; + + if (softn->ipf_nat_lock != 0) + return 0; + if (softn->ipf_nat_stats.ns_rules == 0 && + softn->ipf_nat_instances == NULL) return 0; natfailed = 0; fr = fin->fin_fr; sifp = fin->fin_ifp; if (fr != NULL) { - ifp = fr->fr_tifs[fin->fin_rev].fd_ifp; + ifp = fr->fr_tifs[fin->fin_rev].fd_ptr; if ((ifp != NULL) && (ifp != (void *)-1)) fin->fin_ifp = ifp; } @@ -3793,117 +4912,152 @@ u_32_t *passp; * This is an incoming packet, so the destination is * the icmp_id and the source port equals 0 */ - if (nat_icmpquerytype4(icmp->icmp_type)) + if ((fin->fin_flx & FI_ICMPQUERY) != 0) nflags = IPN_ICMPQUERY; break; default : break; } - + if ((nflags & IPN_TCPUDP)) tcp = fin->fin_dp; } ipa = fin->fin_saddr; - READ_ENTER(&ipf_nat); + READ_ENTER(&softc->ipf_nat); - if (((fin->fin_flx & FI_ICMPERR) != 0) && - (nat = nat_icmperror(fin, &nflags, NAT_OUTBOUND))) + if ((fin->fin_p == IPPROTO_ICMP) && !(nflags & IPN_ICMPQUERY) && + (nat = ipf_nat_icmperror(fin, &nflags, NAT_OUTBOUND))) /*EMPTY*/; - else if ((fin->fin_flx & FI_FRAG) && (nat = fr_nat_knownfrag(fin))) + else if ((fin->fin_flx & FI_FRAG) && (nat = ipf_frag_natknown(fin))) natadd = 0; - else if ((nat = nat_outlookup(fin, nflags|NAT_SEARCH, (u_int)fin->fin_p, - fin->fin_src, fin->fin_dst))) { + else if ((nat = ipf_nat_outlookup(fin, nflags|NAT_SEARCH, + (u_int)fin->fin_p, fin->fin_src, + fin->fin_dst))) { nflags = nat->nat_flags; - } else { - u_32_t hv, msk, nmsk; + } else if (fin->fin_off == 0) { + u_32_t hv, msk, nmsk = 0; /* * If there is no current entry in the nat table for this IP#, * create one for it (if there is a matching rule). */ - RWLOCK_EXIT(&ipf_nat); - msk = 0xffffffff; - nmsk = nat_masks; - WRITE_ENTER(&ipf_nat); maskloop: - iph = ipa & htonl(msk); - hv = NAT_HASH_FN(iph, 0, ipf_natrules_sz); - for (np = nat_rules[hv]; np; np = np->in_mnext) - { + msk = softn->ipf_nat_map_active_masks[nmsk]; + iph = ipa & msk; + hv = NAT_HASH_FN(iph, 0, softn->ipf_nat_maprules_sz); +retry_roundrobin: + for (np = softn->ipf_nat_map_rules[hv]; np; np = npnext) { + npnext = np->in_mnext; if ((np->in_ifps[1] && (np->in_ifps[1] != ifp))) continue; - if (np->in_v != fin->fin_v) + if (np->in_v[0] != 4) continue; - if (np->in_p && (np->in_p != fin->fin_p)) + if (np->in_pr[1] && (np->in_pr[1] != fin->fin_p)) continue; - if ((np->in_flags & IPN_RF) && !(np->in_flags & nflags)) + if ((np->in_flags & IPN_RF) && + !(np->in_flags & nflags)) continue; if (np->in_flags & IPN_FILTER) { - if (!nat_match(fin, np)) + switch (ipf_nat_match(fin, np)) + { + case 0 : continue; - } else if ((ipa & np->in_inmsk) != np->in_inip) + case -1 : + rval = -1; + goto outmatchfail; + case 1 : + default : + break; + } + } else if ((ipa & np->in_osrcmsk) != np->in_osrcaddr) continue; if ((fr != NULL) && - !fr_matchtag(&np->in_tag, &fr->fr_nattag)) + !ipf_matchtag(&np->in_tag, &fr->fr_nattag)) continue; - if (*np->in_plabel != '\0') { + if (np->in_plabel != -1) { if (((np->in_flags & IPN_FILTER) == 0) && - (np->in_dport != tcp->th_dport)) + (np->in_odport != fin->fin_data[1])) continue; - if (appr_ok(fin, tcp, np) == 0) + if (ipf_proxy_ok(fin, tcp, np) == 0) continue; } - if ((nat = nat_new(fin, np, NULL, nflags, - NAT_OUTBOUND))) { + if (np->in_flags & IPN_NO) { np->in_hits++; break; - } else - natfailed = -1; - } - if ((np == NULL) && (nmsk != 0)) { - while (nmsk) { - msk <<= 1; - if (nmsk & 0x80000000) - break; - nmsk <<= 1; } - if (nmsk != 0) { - nmsk <<= 1; - goto maskloop; + MUTEX_ENTER(&softn->ipf_nat_new); + /* + * If we've matched a round-robin rule but it has + * moved in the list since we got it, start over as + * this is now no longer correct. + */ + if (npnext != np->in_mnext) { + if ((np->in_flags & IPN_ROUNDR) != 0) { + MUTEX_EXIT(&softn->ipf_nat_new); + goto retry_roundrobin; + } + npnext = np->in_mnext; } + + nat = ipf_nat_add(fin, np, NULL, nflags, NAT_OUTBOUND); + MUTEX_EXIT(&softn->ipf_nat_new); + if (nat != NULL) { + natfailed = 0; + break; + } + natfailed = -1; + } + if ((np == NULL) && (nmsk < softn->ipf_nat_map_max)) { + nmsk++; + goto maskloop; } - MUTEX_DOWNGRADE(&ipf_nat); } if (nat != NULL) { - rval = fr_natout(fin, nat, natadd, nflags); + rval = ipf_nat_out(fin, nat, natadd, nflags); if (rval == 1) { MUTEX_ENTER(&nat->nat_lock); - nat->nat_ref++; + ipf_nat_update(fin, nat); + nat->nat_bytes[1] += fin->fin_plen; + nat->nat_pkts[1]++; + fin->fin_pktnum = nat->nat_pkts[1]; MUTEX_EXIT(&nat->nat_lock); - nat->nat_touched = fr_ticks; - fin->fin_nat = nat; } } else rval = natfailed; - RWLOCK_EXIT(&ipf_nat); +outmatchfail: + RWLOCK_EXIT(&softc->ipf_nat); - if (rval == -1) { - if (passp != NULL) + switch (rval) + { + case -1 : + if (passp != NULL) { + DT1(frb_natv4out, fr_info_t *, fin); + NBUMPSIDED(1, ns_drop); *passp = FR_BLOCK; + fin->fin_reason = FRB_NATV4; + } fin->fin_flx |= FI_BADNAT; + NBUMPSIDED(1, ns_badnat); + break; + case 0 : + NBUMPSIDE(1, ns_ignored); + break; + case 1 : + NBUMPSIDE(1, ns_translated); + break; } fin->fin_ifp = sifp; return rval; } /* ------------------------------------------------------------------------ */ -/* Function: fr_natout */ +/* Function: ipf_nat_out */ /* Returns: int - -1 == packet failed NAT checks so block it, */ /* 1 == packet was successfully translated. */ /* Parameters: fin(I) - pointer to packet information */ @@ -3913,30 +5067,27 @@ maskloop: /* */ /* Translate a packet coming "out" on an interface. */ /* ------------------------------------------------------------------------ */ -int fr_natout(fin, nat, natadd, nflags) -fr_info_t *fin; -nat_t *nat; -int natadd; -u_32_t nflags; +int +ipf_nat_out(fin, nat, natadd, nflags) + fr_info_t *fin; + nat_t *nat; + int natadd; + u_32_t nflags; { + ipf_main_softc_t *softc = fin->fin_main_soft; + ipf_nat_softc_t *softn = softc->ipf_nat_soft; icmphdr_t *icmp; - u_short *csump; tcphdr_t *tcp; ipnat_t *np; + int skip; int i; tcp = NULL; icmp = NULL; - csump = NULL; np = nat->nat_ptr; if ((natadd != 0) && (fin->fin_flx & FI_FRAG) && (np != NULL)) - (void) fr_nat_newfrag(fin, 0, nat); - - MUTEX_ENTER(&nat->nat_lock); - nat->nat_bytes[1] += fin->fin_plen; - nat->nat_pkts[1]++; - MUTEX_EXIT(&nat->nat_lock); + (void) ipf_frag_natnew(softc, fin, 0, nat); /* * Fix up checksums, not by recalculating them, but @@ -3946,90 +5097,240 @@ u_32_t nflags; * IPFilter is called before the checksum needs calculating so there * is no call to modify whatever is in the header now. */ - if (fin->fin_v == 4) { - if (nflags == IPN_ICMPERR) { - u_32_t s1, s2, sumd; + if (nflags == IPN_ICMPERR) { + u_32_t s1, s2, sumd, msumd; - s1 = LONG_SUM(ntohl(fin->fin_saddr)); - s2 = LONG_SUM(ntohl(nat->nat_outip.s_addr)); - CALC_SUMD(s1, s2, sumd); - fix_outcksum(fin, &fin->fin_ip->ip_sum, sumd); + s1 = LONG_SUM(ntohl(fin->fin_saddr)); + if (nat->nat_dir == NAT_OUTBOUND) { + s2 = LONG_SUM(ntohl(nat->nat_nsrcaddr)); + } else { + s2 = LONG_SUM(ntohl(nat->nat_odstaddr)); } + CALC_SUMD(s1, s2, sumd); + msumd = sumd; + + s1 = LONG_SUM(ntohl(fin->fin_daddr)); + if (nat->nat_dir == NAT_OUTBOUND) { + s2 = LONG_SUM(ntohl(nat->nat_ndstaddr)); + } else { + s2 = LONG_SUM(ntohl(nat->nat_osrcaddr)); + } + CALC_SUMD(s1, s2, sumd); + msumd += sumd; + + ipf_fix_outcksum(0, &fin->fin_ip->ip_sum, msumd, 0); + } #if !defined(_KERNEL) || defined(MENTAT) || defined(__sgi) || \ defined(linux) || defined(BRIDGE_IPF) - else { - /* - * Strictly speaking, this isn't necessary on BSD - * kernels because they do checksum calculation after - * this code has run BUT if ipfilter is being used - * to do NAT as a bridge, that code doesn't exist. - */ - if (nat->nat_dir == NAT_OUTBOUND) - fix_outcksum(fin, &fin->fin_ip->ip_sum, - nat->nat_ipsumd); - else - fix_incksum(fin, &fin->fin_ip->ip_sum, - nat->nat_ipsumd); + else { + /* + * Strictly speaking, this isn't necessary on BSD + * kernels because they do checksum calculation after + * this code has run BUT if ipfilter is being used + * to do NAT as a bridge, that code doesn't exist. + */ + switch (nat->nat_dir) + { + case NAT_OUTBOUND : + ipf_fix_outcksum(fin->fin_cksum & FI_CK_L4PART, + &fin->fin_ip->ip_sum, + nat->nat_ipsumd, 0); + break; + + case NAT_INBOUND : + ipf_fix_incksum(fin->fin_cksum & FI_CK_L4PART, + &fin->fin_ip->ip_sum, + nat->nat_ipsumd, 0); + break; + + default : + break; } + } #endif + + /* + * Address assignment is after the checksum modification because + * we are using the address in the packet for determining the + * correct checksum offset (the ICMP error could be coming from + * anyone...) + */ + switch (nat->nat_dir) + { + case NAT_OUTBOUND : + fin->fin_ip->ip_src = nat->nat_nsrcip; + fin->fin_saddr = nat->nat_nsrcaddr; + fin->fin_ip->ip_dst = nat->nat_ndstip; + fin->fin_daddr = nat->nat_ndstaddr; + break; + + case NAT_INBOUND : + fin->fin_ip->ip_src = nat->nat_odstip; + fin->fin_saddr = nat->nat_ndstaddr; + fin->fin_ip->ip_dst = nat->nat_osrcip; + fin->fin_daddr = nat->nat_nsrcaddr; + break; + + case NAT_DIVERTIN : + { + mb_t *m; + + skip = ipf_nat_decap(fin, nat); + if (skip <= 0) { + NBUMPSIDED(1, ns_decap_fail); + return -1; + } + + m = fin->fin_m; + +#if defined(MENTAT) && defined(_KERNEL) + m->b_rptr += skip; +#else + m->m_data += skip; + m->m_len -= skip; + +# ifdef M_PKTHDR + if (m->m_flags & M_PKTHDR) + m->m_pkthdr.len -= skip; +# endif +#endif + + MUTEX_ENTER(&nat->nat_lock); + ipf_nat_update(fin, nat); + MUTEX_EXIT(&nat->nat_lock); + fin->fin_flx |= FI_NATED; + if (np != NULL && np->in_tag.ipt_num[0] != 0) + fin->fin_nattag = &np->in_tag; + return 1; + /* NOTREACHED */ + } + + case NAT_DIVERTOUT : + { + u_32_t s1, s2, sumd; + udphdr_t *uh; + ip_t *ip; + mb_t *m; + + m = M_DUP(np->in_divmp); + if (m == NULL) { + NBUMPSIDED(1, ns_divert_dup); + return -1; + } + + ip = MTOD(m, ip_t *); + ip->ip_id = htons(ipf_nextipid(fin)); + s2 = ntohs(ip->ip_id); + + s1 = ip->ip_len; + ip->ip_len = ntohs(ip->ip_len); + ip->ip_len += fin->fin_plen; + ip->ip_len = htons(ip->ip_len); + s2 += ntohs(ip->ip_len); + CALC_SUMD(s1, s2, sumd); + + uh = (udphdr_t *)(ip + 1); + uh->uh_ulen += fin->fin_plen; + uh->uh_ulen = htons(uh->uh_ulen); +#if !defined(_KERNEL) || defined(MENTAT) || defined(__sgi) || \ + defined(linux) || defined(BRIDGE_IPF) + ipf_fix_outcksum(0, &ip->ip_sum, sumd, 0); +#endif + + PREP_MB_T(fin, m); + + fin->fin_src = ip->ip_src; + fin->fin_dst = ip->ip_dst; + fin->fin_ip = ip; + fin->fin_plen += sizeof(ip_t) + 8; /* UDP + IPv4 hdr */ + fin->fin_dlen += sizeof(ip_t) + 8; /* UDP + IPv4 hdr */ + + nflags &= ~IPN_TCPUDPICMP; + + break; + } + + default : + break; } if (!(fin->fin_flx & FI_SHORT) && (fin->fin_off == 0)) { - if ((nat->nat_outport != 0) && (nflags & IPN_TCPUDP)) { + u_short *csump; + + if ((nat->nat_nsport != 0) && (nflags & IPN_TCPUDP)) { tcp = fin->fin_dp; - tcp->th_sport = nat->nat_outport; - fin->fin_data[0] = ntohs(nat->nat_outport); + switch (nat->nat_dir) + { + case NAT_OUTBOUND : + tcp->th_sport = nat->nat_nsport; + fin->fin_data[0] = ntohs(nat->nat_nsport); + tcp->th_dport = nat->nat_ndport; + fin->fin_data[1] = ntohs(nat->nat_ndport); + break; + + case NAT_INBOUND : + tcp->th_sport = nat->nat_odport; + fin->fin_data[0] = ntohs(nat->nat_odport); + tcp->th_dport = nat->nat_osport; + fin->fin_data[1] = ntohs(nat->nat_osport); + break; + } } - if ((nat->nat_outport != 0) && (nflags & IPN_ICMPQUERY)) { + if ((nat->nat_nsport != 0) && (nflags & IPN_ICMPQUERY)) { icmp = fin->fin_dp; - icmp->icmp_id = nat->nat_outport; + icmp->icmp_id = nat->nat_nicmpid; } - csump = nat_proto(fin, nat, nflags); + csump = ipf_nat_proto(fin, nat, nflags); + + /* + * The above comments do not hold for layer 4 (or higher) + * checksums... + */ + if (csump != NULL) { + if (nat->nat_dir == NAT_OUTBOUND) + ipf_fix_outcksum(fin->fin_cksum, csump, + nat->nat_sumd[0], + nat->nat_sumd[1] + + fin->fin_dlen); + else + ipf_fix_incksum(fin->fin_cksum, csump, + nat->nat_sumd[0], + nat->nat_sumd[1] + + fin->fin_dlen); + } } - fin->fin_ip->ip_src = nat->nat_outip; - - nat_update(fin, nat, np); - - /* - * The above comments do not hold for layer 4 (or higher) checksums... - */ - if (csump != NULL) { - if (nat->nat_dir == NAT_OUTBOUND) - fix_outcksum(fin, csump, nat->nat_sumd[1]); - else - fix_incksum(fin, csump, nat->nat_sumd[1]); - } -#ifdef IPFILTER_SYNC - ipfsync_update(SMC_NAT, fin, nat->nat_sync); -#endif + ipf_sync_update(softc, SMC_NAT, fin, nat->nat_sync); /* ------------------------------------------------------------- */ - /* A few quick notes: */ - /* Following are test conditions prior to calling the */ - /* appr_check routine. */ - /* */ - /* A NULL tcp indicates a non TCP/UDP packet. When dealing */ - /* with a redirect rule, we attempt to match the packet's */ - /* source port against in_dport, otherwise we'd compare the */ - /* packet's destination. */ + /* A few quick notes: */ + /* Following are test conditions prior to calling the */ + /* ipf_proxy_check routine. */ + /* */ + /* A NULL tcp indicates a non TCP/UDP packet. When dealing */ + /* with a redirect rule, we attempt to match the packet's */ + /* source port against in_dport, otherwise we'd compare the */ + /* packet's destination. */ /* ------------------------------------------------------------- */ if ((np != NULL) && (np->in_apr != NULL)) { - i = appr_check(fin, nat); - if (i == 0) + i = ipf_proxy_check(fin, nat); + if (i == 0) { i = 1; - } else + } else if (i == -1) { + NBUMPSIDED(1, ns_ipf_proxy_fail); + } + } else { i = 1; - ATOMIC_INCL(nat_stats.ns_mapped[1]); + } fin->fin_flx |= FI_NATED; return i; } /* ------------------------------------------------------------------------ */ -/* Function: fr_checknatin */ +/* Function: ipf_nat_checkin */ /* Returns: int - -1 == packet failed NAT checks so block it, */ /* 0 == no packet translation occurred, */ /* 1 == packet was successfully translated. */ @@ -4043,22 +5344,31 @@ u_32_t nflags; /* NAT entry if a we matched a NAT rule. Lastly, actually change the */ /* packet header(s) as required. */ /* ------------------------------------------------------------------------ */ -int fr_checknatin(fin, passp) -fr_info_t *fin; -u_32_t *passp; +int +ipf_nat_checkin(fin, passp) + fr_info_t *fin; + u_32_t *passp; { + ipf_main_softc_t *softc; + ipf_nat_softc_t *softn; u_int nflags, natadd; + ipnat_t *np, *npnext; int rval, natfailed; struct ifnet *ifp; struct in_addr in; icmphdr_t *icmp; tcphdr_t *tcp; u_short dport; - ipnat_t *np; nat_t *nat; u_32_t iph; - if (nat_stats.ns_rules == 0 || fr_nat_lock != 0) + softc = fin->fin_main_soft; + softn = softc->ipf_nat_soft; + + if (softn->ipf_nat_lock != 0) + return 0; + if (softn->ipf_nat_stats.ns_rules == 0 && + softn->ipf_nat_instances == NULL) return 0; tcp = NULL; @@ -4085,182 +5395,213 @@ u_32_t *passp; * This is an incoming packet, so the destination is * the icmp_id and the source port equals 0 */ - if (nat_icmpquerytype4(icmp->icmp_type)) { + if ((fin->fin_flx & FI_ICMPQUERY) != 0) { nflags = IPN_ICMPQUERY; - dport = icmp->icmp_id; + dport = icmp->icmp_id; } break; default : break; } - + if ((nflags & IPN_TCPUDP)) { tcp = fin->fin_dp; - dport = tcp->th_dport; + dport = fin->fin_data[1]; } } in = fin->fin_dst; - READ_ENTER(&ipf_nat); + READ_ENTER(&softc->ipf_nat); - if (((fin->fin_flx & FI_ICMPERR) != 0) && - (nat = nat_icmperror(fin, &nflags, NAT_INBOUND))) + if ((fin->fin_p == IPPROTO_ICMP) && !(nflags & IPN_ICMPQUERY) && + (nat = ipf_nat_icmperror(fin, &nflags, NAT_INBOUND))) /*EMPTY*/; - else if ((fin->fin_flx & FI_FRAG) && (nat = fr_nat_knownfrag(fin))) + else if ((fin->fin_flx & FI_FRAG) && (nat = ipf_frag_natknown(fin))) natadd = 0; - else if ((nat = nat_inlookup(fin, nflags|NAT_SEARCH, (u_int)fin->fin_p, - fin->fin_src, in))) { + else if ((nat = ipf_nat_inlookup(fin, nflags|NAT_SEARCH, + (u_int)fin->fin_p, + fin->fin_src, in))) { nflags = nat->nat_flags; - } else { - u_32_t hv, msk, rmsk; + } else if (fin->fin_off == 0) { + u_32_t hv, msk, rmsk = 0; - RWLOCK_EXIT(&ipf_nat); - rmsk = rdr_masks; - msk = 0xffffffff; - WRITE_ENTER(&ipf_nat); /* * If there is no current entry in the nat table for this IP#, * create one for it (if there is a matching rule). */ maskloop: - iph = in.s_addr & htonl(msk); - hv = NAT_HASH_FN(iph, 0, ipf_rdrrules_sz); - for (np = rdr_rules[hv]; np; np = np->in_rnext) { + msk = softn->ipf_nat_rdr_active_masks[rmsk]; + iph = in.s_addr & msk; + hv = NAT_HASH_FN(iph, 0, softn->ipf_nat_rdrrules_sz); +retry_roundrobin: + /* TRACE (iph,msk,rmsk,hv,softn->ipf_nat_rdrrules_sz) */ + for (np = softn->ipf_nat_rdr_rules[hv]; np; np = npnext) { + npnext = np->in_rnext; if (np->in_ifps[0] && (np->in_ifps[0] != ifp)) continue; - if (np->in_v != fin->fin_v) + if (np->in_v[0] != 4) continue; - if (np->in_p && (np->in_p != fin->fin_p)) + if (np->in_pr[0] && (np->in_pr[0] != fin->fin_p)) continue; if ((np->in_flags & IPN_RF) && !(np->in_flags & nflags)) continue; if (np->in_flags & IPN_FILTER) { - if (!nat_match(fin, np)) + switch (ipf_nat_match(fin, np)) + { + case 0 : continue; + case -1 : + rval = -1; + goto inmatchfail; + case 1 : + default : + break; + } } else { - if ((in.s_addr & np->in_outmsk) != np->in_outip) + if ((in.s_addr & np->in_odstmsk) != + np->in_odstaddr) continue; - if (np->in_pmin && - ((ntohs(np->in_pmax) < ntohs(dport)) || - (ntohs(dport) < ntohs(np->in_pmin)))) + if (np->in_odport && + ((np->in_dtop < dport) || + (dport < np->in_odport))) continue; } - if (*np->in_plabel != '\0') { - if (!appr_ok(fin, tcp, np)) { + if (np->in_plabel != -1) { + if (!ipf_proxy_ok(fin, tcp, np)) { continue; } } - nat = nat_new(fin, np, NULL, nflags, NAT_INBOUND); - if (nat != NULL) { + if (np->in_flags & IPN_NO) { np->in_hits++; break; - } else - natfailed = -1; - } + } - if ((np == NULL) && (rmsk != 0)) { - while (rmsk) { - msk <<= 1; - if (rmsk & 0x80000000) - break; - rmsk <<= 1; + MUTEX_ENTER(&softn->ipf_nat_new); + /* + * If we've matched a round-robin rule but it has + * moved in the list since we got it, start over as + * this is now no longer correct. + */ + if (npnext != np->in_rnext) { + if ((np->in_flags & IPN_ROUNDR) != 0) { + MUTEX_EXIT(&softn->ipf_nat_new); + goto retry_roundrobin; + } + npnext = np->in_rnext; } - if (rmsk != 0) { - rmsk <<= 1; - goto maskloop; + + nat = ipf_nat_add(fin, np, NULL, nflags, NAT_INBOUND); + MUTEX_EXIT(&softn->ipf_nat_new); + if (nat != NULL) { + natfailed = 0; + break; } + natfailed = -1; + } + if ((np == NULL) && (rmsk < softn->ipf_nat_rdr_max)) { + rmsk++; + goto maskloop; } - MUTEX_DOWNGRADE(&ipf_nat); } + if (nat != NULL) { - rval = fr_natin(fin, nat, natadd, nflags); + rval = ipf_nat_in(fin, nat, natadd, nflags); if (rval == 1) { MUTEX_ENTER(&nat->nat_lock); - nat->nat_ref++; + ipf_nat_update(fin, nat); + nat->nat_bytes[0] += fin->fin_plen; + nat->nat_pkts[0]++; + fin->fin_pktnum = nat->nat_pkts[0]; MUTEX_EXIT(&nat->nat_lock); - nat->nat_touched = fr_ticks; - fin->fin_nat = nat; } } else rval = natfailed; - RWLOCK_EXIT(&ipf_nat); +inmatchfail: + RWLOCK_EXIT(&softc->ipf_nat); - if (rval == -1) { - if (passp != NULL) + switch (rval) + { + case -1 : + if (passp != NULL) { + DT1(frb_natv4in, fr_info_t *, fin); + NBUMPSIDED(0, ns_drop); *passp = FR_BLOCK; + fin->fin_reason = FRB_NATV4; + } fin->fin_flx |= FI_BADNAT; + NBUMPSIDED(0, ns_badnat); + break; + case 0 : + NBUMPSIDE(0, ns_ignored); + break; + case 1 : + NBUMPSIDE(0, ns_translated); + break; } return rval; } /* ------------------------------------------------------------------------ */ -/* Function: fr_natin */ +/* Function: ipf_nat_in */ /* Returns: int - -1 == packet failed NAT checks so block it, */ /* 1 == packet was successfully translated. */ /* Parameters: fin(I) - pointer to packet information */ /* nat(I) - pointer to NAT structure */ /* natadd(I) - flag indicating if it is safe to add frag cache */ /* nflags(I) - NAT flags set for this packet */ -/* Locks Held: ipf_nat (READ) */ +/* Locks Held: ipf_nat(READ) */ /* */ /* Translate a packet coming "in" on an interface. */ /* ------------------------------------------------------------------------ */ -int fr_natin(fin, nat, natadd, nflags) -fr_info_t *fin; -nat_t *nat; -int natadd; -u_32_t nflags; +int +ipf_nat_in(fin, nat, natadd, nflags) + fr_info_t *fin; + nat_t *nat; + int natadd; + u_32_t nflags; { + ipf_main_softc_t *softc = fin->fin_main_soft; + ipf_nat_softc_t *softn = softc->ipf_nat_soft; + u_32_t sumd, ipsumd, sum1, sum2; icmphdr_t *icmp; - u_short *csump; tcphdr_t *tcp; ipnat_t *np; + int skip; int i; tcp = NULL; - csump = NULL; np = nat->nat_ptr; fin->fin_fr = nat->nat_fr; if (np != NULL) { if ((natadd != 0) && (fin->fin_flx & FI_FRAG)) - (void) fr_nat_newfrag(fin, 0, nat); + (void) ipf_frag_natnew(softc, fin, 0, nat); /* ------------------------------------------------------------- */ - /* A few quick notes: */ - /* Following are test conditions prior to calling the */ - /* appr_check routine. */ - /* */ - /* A NULL tcp indicates a non TCP/UDP packet. When dealing */ - /* with a map rule, we attempt to match the packet's */ - /* source port against in_dport, otherwise we'd compare the */ - /* packet's destination. */ + /* A few quick notes: */ + /* Following are test conditions prior to calling the */ + /* ipf_proxy_check routine. */ + /* */ + /* A NULL tcp indicates a non TCP/UDP packet. When dealing */ + /* with a map rule, we attempt to match the packet's */ + /* source port against in_dport, otherwise we'd compare the */ + /* packet's destination. */ /* ------------------------------------------------------------- */ if (np->in_apr != NULL) { - i = appr_check(fin, nat); + i = ipf_proxy_check(fin, nat); if (i == -1) { + NBUMPSIDED(0, ns_ipf_proxy_fail); return -1; } } } -#ifdef IPFILTER_SYNC - ipfsync_update(SMC_NAT, fin, nat->nat_sync); -#endif - - MUTEX_ENTER(&nat->nat_lock); - nat->nat_bytes[0] += fin->fin_plen; - nat->nat_pkts[0]++; - MUTEX_EXIT(&nat->nat_lock); - - fin->fin_ip->ip_dst = nat->nat_inip; - fin->fin_fi.fi_daddr = nat->nat_inip.s_addr; - if (nflags & IPN_TCPUDP) - tcp = fin->fin_dp; + ipf_sync_update(softc, SMC_NAT, fin, nat->nat_sync); + ipsumd = nat->nat_ipsumd; /* * Fix up checksums, not by recalculating them, but * simply computing adjustments. @@ -4271,42 +5612,166 @@ u_32_t nflags; * fast forwarding (so that it doesn't need to be recomputed) but with * header checksum offloading, perhaps it is a moot point. */ + + switch (nat->nat_dir) + { + case NAT_INBOUND : + if ((fin->fin_flx & FI_ICMPERR) == 0) { + fin->fin_ip->ip_src = nat->nat_nsrcip; + fin->fin_saddr = nat->nat_nsrcaddr; + } else { + sum1 = nat->nat_osrcaddr; + sum2 = nat->nat_nsrcaddr; + CALC_SUMD(sum1, sum2, sumd); + ipsumd -= sumd; + } + fin->fin_ip->ip_dst = nat->nat_ndstip; + fin->fin_daddr = nat->nat_ndstaddr; #if !defined(_KERNEL) || defined(MENTAT) || defined(__sgi) || \ defined(__osf__) || defined(linux) - if (nat->nat_dir == NAT_OUTBOUND) - fix_incksum(fin, &fin->fin_ip->ip_sum, nat->nat_ipsumd); - else - fix_outcksum(fin, &fin->fin_ip->ip_sum, nat->nat_ipsumd); + ipf_fix_outcksum(0, &fin->fin_ip->ip_sum, ipsumd, 0); +#endif + break; + + case NAT_OUTBOUND : + if ((fin->fin_flx & FI_ICMPERR) == 0) { + fin->fin_ip->ip_src = nat->nat_odstip; + fin->fin_saddr = nat->nat_odstaddr; + } else { + sum1 = nat->nat_odstaddr; + sum2 = nat->nat_ndstaddr; + CALC_SUMD(sum1, sum2, sumd); + ipsumd -= sumd; + } + fin->fin_ip->ip_dst = nat->nat_osrcip; + fin->fin_daddr = nat->nat_osrcaddr; +#if !defined(_KERNEL) || defined(MENTAT) || defined(__sgi) || \ + defined(__osf__) || defined(linux) + ipf_fix_incksum(0, &fin->fin_ip->ip_sum, ipsumd, 0); +#endif + break; + + case NAT_DIVERTIN : + { + udphdr_t *uh; + ip_t *ip; + mb_t *m; + + m = M_DUP(np->in_divmp); + if (m == NULL) { + NBUMPSIDED(0, ns_divert_dup); + return -1; + } + + ip = MTOD(m, ip_t *); + ip->ip_id = htons(ipf_nextipid(fin)); + sum1 = ntohs(ip->ip_len); + ip->ip_len = ntohs(ip->ip_len); + ip->ip_len += fin->fin_plen; + ip->ip_len = htons(ip->ip_len); + + uh = (udphdr_t *)(ip + 1); + uh->uh_ulen += fin->fin_plen; + uh->uh_ulen = htons(uh->uh_ulen); + + sum2 = ntohs(ip->ip_id) + ntohs(ip->ip_len); + sum2 += ntohs(ip->ip_off) & IP_DF; + CALC_SUMD(sum1, sum2, sumd); + +#if !defined(_KERNEL) || defined(MENTAT) || defined(__sgi) || \ + defined(__osf__) || defined(linux) + ipf_fix_outcksum(0, &ip->ip_sum, sumd, 0); +#endif + PREP_MB_T(fin, m); + + fin->fin_ip = ip; + fin->fin_plen += sizeof(ip_t) + 8; /* UDP + new IPv4 hdr */ + fin->fin_dlen += sizeof(ip_t) + 8; /* UDP + old IPv4 hdr */ + + nflags &= ~IPN_TCPUDPICMP; + + break; + } + + case NAT_DIVERTOUT : + { + mb_t *m; + + skip = ipf_nat_decap(fin, nat); + if (skip <= 0) { + NBUMPSIDED(0, ns_decap_fail); + return -1; + } + + m = fin->fin_m; + +#if defined(MENTAT) && defined(_KERNEL) + m->b_rptr += skip; +#else + m->m_data += skip; + m->m_len -= skip; + +# ifdef M_PKTHDR + if (m->m_flags & M_PKTHDR) + m->m_pkthdr.len -= skip; +# endif #endif + ipf_nat_update(fin, nat); + nflags &= ~IPN_TCPUDPICMP; + fin->fin_flx |= FI_NATED; + if (np != NULL && np->in_tag.ipt_num[0] != 0) + fin->fin_nattag = &np->in_tag; + return 1; + /* NOTREACHED */ + } + } + if (nflags & IPN_TCPUDP) + tcp = fin->fin_dp; + if (!(fin->fin_flx & FI_SHORT) && (fin->fin_off == 0)) { - if ((nat->nat_inport != 0) && (nflags & IPN_TCPUDP)) { - tcp->th_dport = nat->nat_inport; - fin->fin_data[1] = ntohs(nat->nat_inport); + u_short *csump; + + if ((nat->nat_odport != 0) && (nflags & IPN_TCPUDP)) { + switch (nat->nat_dir) + { + case NAT_INBOUND : + tcp->th_sport = nat->nat_nsport; + fin->fin_data[0] = ntohs(nat->nat_nsport); + tcp->th_dport = nat->nat_ndport; + fin->fin_data[1] = ntohs(nat->nat_ndport); + break; + + case NAT_OUTBOUND : + tcp->th_sport = nat->nat_odport; + fin->fin_data[0] = ntohs(nat->nat_odport); + tcp->th_dport = nat->nat_osport; + fin->fin_data[1] = ntohs(nat->nat_osport); + break; + } } - if ((nat->nat_inport != 0) && (nflags & IPN_ICMPQUERY)) { + if ((nat->nat_odport != 0) && (nflags & IPN_ICMPQUERY)) { icmp = fin->fin_dp; - icmp->icmp_id = nat->nat_inport; + icmp->icmp_id = nat->nat_nicmpid; } - csump = nat_proto(fin, nat, nflags); + csump = ipf_nat_proto(fin, nat, nflags); + + /* + * The above comments do not hold for layer 4 (or higher) + * checksums... + */ + if (csump != NULL) { + if (nat->nat_dir == NAT_OUTBOUND) + ipf_fix_incksum(0, csump, nat->nat_sumd[0], 0); + else + ipf_fix_outcksum(0, csump, nat->nat_sumd[0], 0); + } } - nat_update(fin, nat, np); - - /* - * The above comments do not hold for layer 4 (or higher) checksums... - */ - if (csump != NULL) { - if (nat->nat_dir == NAT_OUTBOUND) - fix_incksum(fin, csump, nat->nat_sumd[0]); - else - fix_outcksum(fin, csump, nat->nat_sumd[0]); - } - ATOMIC_INCL(nat_stats.ns_mapped[0]); fin->fin_flx |= FI_NATED; if (np != NULL && np->in_tag.ipt_num[0] != 0) fin->fin_nattag = &np->in_tag; @@ -4315,7 +5780,7 @@ u_32_t nflags; /* ------------------------------------------------------------------------ */ -/* Function: nat_proto */ +/* Function: ipf_nat_proto */ /* Returns: u_short* - pointer to transport header checksum to update, */ /* NULL if the transport protocol is not recognised */ /* as needing a checksum update. */ @@ -4328,10 +5793,11 @@ u_32_t nflags; /* that is not strictly 'address' translation, such as clamping the MSS in */ /* TCP down to a specific value, then do it from here. */ /* ------------------------------------------------------------------------ */ -u_short *nat_proto(fin, nat, nflags) -fr_info_t *fin; -nat_t *nat; -u_int nflags; +u_short * +ipf_nat_proto(fin, nat, nflags) + fr_info_t *fin; + nat_t *nat; + u_int nflags; { icmphdr_t *icmp; u_short *csump; @@ -4340,9 +5806,9 @@ u_int nflags; csump = NULL; if (fin->fin_out == 0) { - fin->fin_rev = (nat->nat_dir == NAT_OUTBOUND); + fin->fin_rev = (nat->nat_dir & NAT_OUTBOUND); } else { - fin->fin_rev = (nat->nat_dir == NAT_INBOUND); + fin->fin_rev = ((nat->nat_dir & NAT_OUTBOUND) == 0); } switch (fin->fin_p) @@ -4350,22 +5816,25 @@ u_int nflags; case IPPROTO_TCP : tcp = fin->fin_dp; - csump = &tcp->th_sum; + if ((nflags & IPN_TCP) != 0) + csump = &tcp->th_sum; /* * Do a MSS CLAMPING on a SYN packet, * only deal IPv4 for now. */ if ((nat->nat_mssclamp != 0) && (tcp->th_flags & TH_SYN) != 0) - nat_mssclamp(tcp, nat->nat_mssclamp, fin, csump); + ipf_nat_mssclamp(tcp, nat->nat_mssclamp, fin, csump); break; case IPPROTO_UDP : udp = fin->fin_dp; - if (udp->uh_sum) - csump = &udp->uh_sum; + if ((nflags & IPN_UDP) != 0) { + if (udp->uh_sum != 0) + csump = &udp->uh_sum; + } break; case IPPROTO_ICMP : @@ -4376,165 +5845,108 @@ u_int nflags; csump = &icmp->icmp_cksum; } break; + +#ifdef USE_INET6 + case IPPROTO_ICMPV6 : + { + struct icmp6_hdr *icmp6 = (struct icmp6_hdr *)fin->fin_dp; + + icmp6 = fin->fin_dp; + + if ((nflags & IPN_ICMPQUERY) != 0) { + if (icmp6->icmp6_cksum != 0) + csump = &icmp6->icmp6_cksum; + } + break; + } +#endif } return csump; } /* ------------------------------------------------------------------------ */ -/* Function: fr_natunload */ +/* Function: ipf_nat_expire */ /* Returns: Nil */ -/* Parameters: Nil */ -/* */ -/* Free all memory used by NAT structures allocated at runtime. */ -/* ------------------------------------------------------------------------ */ -void fr_natunload() -{ - ipftq_t *ifq, *ifqnext; - - (void) nat_clearlist(); - (void) nat_flushtable(); - - /* - * Proxy timeout queues are not cleaned here because although they - * exist on the NAT list, appr_unload is called after fr_natunload - * and the proxies actually are responsible for them being created. - * Should the proxy timeouts have their own list? There's no real - * justification as this is the only complication. - */ - for (ifq = nat_utqe; ifq != NULL; ifq = ifqnext) { - ifqnext = ifq->ifq_next; - if (((ifq->ifq_flags & IFQF_PROXY) == 0) && - (fr_deletetimeoutqueue(ifq) == 0)) - fr_freetimeoutqueue(ifq); - } - - if (nat_table[0] != NULL) { - KFREES(nat_table[0], sizeof(nat_t *) * ipf_nattable_sz); - nat_table[0] = NULL; - } - if (nat_table[1] != NULL) { - KFREES(nat_table[1], sizeof(nat_t *) * ipf_nattable_sz); - nat_table[1] = NULL; - } - if (nat_rules != NULL) { - KFREES(nat_rules, sizeof(ipnat_t *) * ipf_natrules_sz); - nat_rules = NULL; - } - if (rdr_rules != NULL) { - KFREES(rdr_rules, sizeof(ipnat_t *) * ipf_rdrrules_sz); - rdr_rules = NULL; - } - if (ipf_hm_maptable != NULL) { - KFREES(ipf_hm_maptable, sizeof(hostmap_t *) * ipf_hostmap_sz); - ipf_hm_maptable = NULL; - } - if (nat_stats.ns_bucketlen[0] != NULL) { - KFREES(nat_stats.ns_bucketlen[0], - sizeof(u_long *) * ipf_nattable_sz); - nat_stats.ns_bucketlen[0] = NULL; - } - if (nat_stats.ns_bucketlen[1] != NULL) { - KFREES(nat_stats.ns_bucketlen[1], - sizeof(u_long *) * ipf_nattable_sz); - nat_stats.ns_bucketlen[1] = NULL; - } - - if (fr_nat_maxbucket_reset == 1) - fr_nat_maxbucket = 0; - - if (fr_nat_init == 1) { - fr_nat_init = 0; - fr_sttab_destroy(nat_tqb); - - RW_DESTROY(&ipf_natfrag); - RW_DESTROY(&ipf_nat); - - MUTEX_DESTROY(&ipf_nat_new); - MUTEX_DESTROY(&ipf_natio); - - MUTEX_DESTROY(&nat_udptq.ifq_lock); - MUTEX_DESTROY(&nat_icmptq.ifq_lock); - MUTEX_DESTROY(&nat_iptq.ifq_lock); - } -} - - -/* ------------------------------------------------------------------------ */ -/* Function: fr_natexpire */ -/* Returns: Nil */ -/* Parameters: Nil */ +/* Parameters: softc(I) - pointer to soft context main structure */ /* */ /* Check all of the timeout queues for entries at the top which need to be */ /* expired. */ /* ------------------------------------------------------------------------ */ -void fr_natexpire() +void +ipf_nat_expire(softc) + ipf_main_softc_t *softc; { + ipf_nat_softc_t *softn = softc->ipf_nat_soft; ipftq_t *ifq, *ifqnext; ipftqent_t *tqe, *tqn; int i; SPL_INT(s); SPL_NET(s); - WRITE_ENTER(&ipf_nat); - for (ifq = nat_tqb, i = 0; ifq != NULL; ifq = ifq->ifq_next) { + WRITE_ENTER(&softc->ipf_nat); + for (ifq = softn->ipf_nat_tcptq, i = 0; ifq != NULL; + ifq = ifq->ifq_next) { for (tqn = ifq->ifq_head; ((tqe = tqn) != NULL); i++) { - if (tqe->tqe_die > fr_ticks) + if (tqe->tqe_die > softc->ipf_ticks) break; tqn = tqe->tqe_next; - nat_delete(tqe->tqe_parent, NL_EXPIRE); + ipf_nat_delete(softc, tqe->tqe_parent, NL_EXPIRE); } } - for (ifq = nat_utqe; ifq != NULL; ifq = ifqnext) { - ifqnext = ifq->ifq_next; - + for (ifq = softn->ipf_nat_utqe; ifq != NULL; ifq = ifq->ifq_next) { for (tqn = ifq->ifq_head; ((tqe = tqn) != NULL); i++) { - if (tqe->tqe_die > fr_ticks) + if (tqe->tqe_die > softc->ipf_ticks) break; tqn = tqe->tqe_next; - nat_delete(tqe->tqe_parent, NL_EXPIRE); + ipf_nat_delete(softc, tqe->tqe_parent, NL_EXPIRE); } } - for (ifq = nat_utqe; ifq != NULL; ifq = ifqnext) { + for (ifq = softn->ipf_nat_utqe; ifq != NULL; ifq = ifqnext) { ifqnext = ifq->ifq_next; if (((ifq->ifq_flags & IFQF_DELETE) != 0) && (ifq->ifq_ref == 0)) { - fr_freetimeoutqueue(ifq); + ipf_freetimeoutqueue(softc, ifq); } } - if (fr_nat_doflush != 0) { - nat_extraflush(2); - fr_nat_doflush = 0; + if (softn->ipf_nat_doflush != 0) { + ipf_nat_extraflush(softc, softn, 2); + softn->ipf_nat_doflush = 0; } - RWLOCK_EXIT(&ipf_nat); + RWLOCK_EXIT(&softc->ipf_nat); SPL_X(s); } /* ------------------------------------------------------------------------ */ -/* Function: fr_natsync */ +/* Function: ipf_nat_sync */ /* Returns: Nil */ -/* Parameters: ifp(I) - pointer to network interface */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* ifp(I) - pointer to network interface */ /* */ /* Walk through all of the currently active NAT sessions, looking for those */ /* which need to have their translated address updated. */ /* ------------------------------------------------------------------------ */ -void fr_natsync(ifp) -void *ifp; +void +ipf_nat_sync(softc, ifp) + ipf_main_softc_t *softc; + void *ifp; { + ipf_nat_softc_t *softn = softc->ipf_nat_soft; u_32_t sum1, sum2, sumd; - struct in_addr in; + i6addr_t in; ipnat_t *n; nat_t *nat; void *ifp2; + int idx; SPL_INT(s); - if (fr_running <= 0) + if (softc->ipf_running <= 0) return; /* @@ -4544,28 +5956,63 @@ void *ifp; * where the rule specifies the address is taken from the interface. */ SPL_NET(s); - WRITE_ENTER(&ipf_nat); + WRITE_ENTER(&softc->ipf_nat); - if (fr_running <= 0) { - RWLOCK_EXIT(&ipf_nat); + if (softc->ipf_running <= 0) { + RWLOCK_EXIT(&softc->ipf_nat); return; } - for (nat = nat_instances; nat; nat = nat->nat_next) { + for (nat = softn->ipf_nat_instances; nat; nat = nat->nat_next) { if ((nat->nat_flags & IPN_TCP) != 0) continue; + n = nat->nat_ptr; - if ((n == NULL) || - (n->in_outip != 0) || (n->in_outmsk != 0xffffffff)) - continue; + if (n != NULL) { + if (n->in_v[1] == 4) { + if (n->in_redir & NAT_MAP) { + if ((n->in_nsrcaddr != 0) || + (n->in_nsrcmsk != 0xffffffff)) + continue; + } else if (n->in_redir & NAT_REDIRECT) { + if ((n->in_ndstaddr != 0) || + (n->in_ndstmsk != 0xffffffff)) + continue; + } + } +#ifdef USE_INET6 + if (n->in_v[1] == 4) { + if (n->in_redir & NAT_MAP) { + if (!IP6_ISZERO(&n->in_nsrcaddr) || + !IP6_ISONES(&n->in_nsrcmsk)) + continue; + } else if (n->in_redir & NAT_REDIRECT) { + if (!IP6_ISZERO(&n->in_ndstaddr) || + !IP6_ISONES(&n->in_ndstmsk)) + continue; + } + } +#endif + } + if (((ifp == NULL) || (ifp == nat->nat_ifps[0]) || (ifp == nat->nat_ifps[1]))) { - nat->nat_ifps[0] = GETIFP(nat->nat_ifnames[0], 4); + nat->nat_ifps[0] = GETIFP(nat->nat_ifnames[0], + nat->nat_v[0]); + if ((nat->nat_ifps[0] != NULL) && + (nat->nat_ifps[0] != (void *)-1)) { + nat->nat_mtu[0] = GETIFMTU_4(nat->nat_ifps[0]); + } if (nat->nat_ifnames[1][0] != '\0') { nat->nat_ifps[1] = GETIFP(nat->nat_ifnames[1], - 4); - } else + nat->nat_v[1]); + } else { nat->nat_ifps[1] = nat->nat_ifps[0]; + } + if ((nat->nat_ifps[1] != NULL) && + (nat->nat_ifps[1] != (void *)-1)) { + nat->nat_mtu[1] = GETIFMTU_4(nat->nat_ifps[1]); + } ifp2 = nat->nat_ifps[0]; if (ifp2 == NULL) continue; @@ -4574,10 +6021,13 @@ void *ifp; * Change the map-to address to be the same as the * new one. */ - sum1 = nat->nat_outip.s_addr; - if (fr_ifpaddr(4, FRI_NORMAL, ifp2, &in, NULL) != -1) - nat->nat_outip = in; - sum2 = nat->nat_outip.s_addr; + sum1 = NATFSUM(nat, nat->nat_v[1], nat_nsrc6); + if (ipf_ifpaddr(softc, nat->nat_v[0], FRI_NORMAL, ifp2, + &in, NULL) != -1) { + if (nat->nat_v[0] == 4) + nat->nat_nsrcip = in.in4; + } + sum2 = NATFSUM(nat, nat->nat_v[1], nat_nsrc6); if (sum1 == sum2) continue; @@ -4595,27 +6045,53 @@ void *ifp; } } - for (n = nat_list; (n != NULL); n = n->in_next) { + for (n = softn->ipf_nat_list; (n != NULL); n = n->in_next) { + char *base = n->in_names; + if ((ifp == NULL) || (n->in_ifps[0] == ifp)) - n->in_ifps[0] = fr_resolvenic(n->in_ifnames[0], 4); + n->in_ifps[0] = ipf_resolvenic(softc, + base + n->in_ifnames[0], + n->in_v[0]); if ((ifp == NULL) || (n->in_ifps[1] == ifp)) - n->in_ifps[1] = fr_resolvenic(n->in_ifnames[1], 4); + n->in_ifps[1] = ipf_resolvenic(softc, + base + n->in_ifnames[1], + n->in_v[1]); + + if (n->in_redir & NAT_REDIRECT) + idx = 1; + else + idx = 0; + + if (((ifp == NULL) || (n->in_ifps[idx] == ifp)) && + (n->in_ifps[idx] != NULL && + n->in_ifps[idx] != (void *)-1)) { + + ipf_nat_nextaddrinit(softc, n->in_names, &n->in_osrc, + 0, n->in_ifps[idx]); + ipf_nat_nextaddrinit(softc, n->in_names, &n->in_odst, + 0, n->in_ifps[idx]); + ipf_nat_nextaddrinit(softc, n->in_names, &n->in_nsrc, + 0, n->in_ifps[idx]); + ipf_nat_nextaddrinit(softc, n->in_names, &n->in_ndst, + 0, n->in_ifps[idx]); + } } - RWLOCK_EXIT(&ipf_nat); + RWLOCK_EXIT(&softc->ipf_nat); SPL_X(s); } /* ------------------------------------------------------------------------ */ -/* Function: nat_icmpquerytype4 */ +/* Function: ipf_nat_icmpquerytype */ /* Returns: int - 1 == success, 0 == failure */ /* Parameters: icmptype(I) - ICMP type number */ /* */ /* Tests to see if the ICMP type number passed is a query/response type or */ /* not. */ /* ------------------------------------------------------------------------ */ -static int nat_icmpquerytype4(icmptype) -int icmptype; +static int +ipf_nat_icmpquerytype(icmptype) + int icmptype; { /* @@ -4627,10 +6103,8 @@ int icmptype; * altough it seems silly to call a reply a query, this is exactly * as it is defined in the IPv4 specification */ - switch (icmptype) { - case ICMP_ECHOREPLY: case ICMP_ECHO: /* route aedvertisement/solliciation is currently unsupported: */ @@ -4651,14 +6125,19 @@ int icmptype; /* ------------------------------------------------------------------------ */ /* Function: nat_log */ /* Returns: Nil */ -/* Parameters: nat(I) - pointer to NAT structure */ -/* type(I) - type of log entry to create */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* softn(I) - pointer to NAT context structure */ +/* nat(I) - pointer to NAT structure */ +/* action(I) - action related to NAT structure being performed */ /* */ /* Creates a NAT log entry. */ /* ------------------------------------------------------------------------ */ -void nat_log(nat, type) -struct nat *nat; -u_int type; +void +ipf_nat_log(softc, softn, nat, action) + ipf_main_softc_t *softc; + ipf_nat_softc_t *softn; + struct nat *nat; + u_int action; { #ifdef IPFILTER_LOG # ifndef LARGE_NAT @@ -4670,22 +6149,40 @@ u_int type; size_t sizes[1]; int types[1]; - natl.nl_inip = nat->nat_inip; - natl.nl_outip = nat->nat_outip; - natl.nl_origip = nat->nat_oip; + bcopy((char *)&nat->nat_osrc6, (char *)&natl.nl_osrcip, + sizeof(natl.nl_osrcip)); + bcopy((char *)&nat->nat_nsrc6, (char *)&natl.nl_nsrcip, + sizeof(natl.nl_nsrcip)); + bcopy((char *)&nat->nat_odst6, (char *)&natl.nl_odstip, + sizeof(natl.nl_odstip)); + bcopy((char *)&nat->nat_ndst6, (char *)&natl.nl_ndstip, + sizeof(natl.nl_ndstip)); + natl.nl_bytes[0] = nat->nat_bytes[0]; natl.nl_bytes[1] = nat->nat_bytes[1]; natl.nl_pkts[0] = nat->nat_pkts[0]; natl.nl_pkts[1] = nat->nat_pkts[1]; - natl.nl_origport = nat->nat_oport; - natl.nl_inport = nat->nat_inport; - natl.nl_outport = nat->nat_outport; - natl.nl_p = nat->nat_p; - natl.nl_type = type; + natl.nl_odstport = nat->nat_odport; + natl.nl_osrcport = nat->nat_osport; + natl.nl_nsrcport = nat->nat_nsport; + natl.nl_ndstport = nat->nat_ndport; + natl.nl_p[0] = nat->nat_pr[0]; + natl.nl_p[1] = nat->nat_pr[1]; + natl.nl_v[0] = nat->nat_v[0]; + natl.nl_v[1] = nat->nat_v[1]; + natl.nl_type = nat->nat_redir; + natl.nl_action = action; natl.nl_rule = -1; + + bcopy(nat->nat_ifnames[0], natl.nl_ifnames[0], + sizeof(nat->nat_ifnames[0])); + bcopy(nat->nat_ifnames[1], natl.nl_ifnames[1], + sizeof(nat->nat_ifnames[1])); + # ifndef LARGE_NAT if (nat->nat_ptr != NULL) { - for (rulen = 0, np = nat_list; np; np = np->in_next, rulen++) + for (rulen = 0, np = softn->ipf_nat_list; np != NULL; + np = np->in_next, rulen++) if (np == nat->nat_ptr) { natl.nl_rule = rulen; break; @@ -4696,63 +6193,108 @@ u_int type; sizes[0] = sizeof(natl); types[0] = 0; - (void) ipllog(IPL_LOGNAT, NULL, items, sizes, types, 1); + (void) ipf_log_items(softc, IPL_LOGNAT, NULL, items, sizes, types, 1); #endif } #if defined(__OpenBSD__) /* ------------------------------------------------------------------------ */ -/* Function: nat_ifdetach */ +/* Function: ipf_nat_ifdetach */ /* Returns: Nil */ /* Parameters: ifp(I) - pointer to network interface */ /* */ /* Compatibility interface for OpenBSD to trigger the correct updating of */ /* interface references within IPFilter. */ /* ------------------------------------------------------------------------ */ -void nat_ifdetach(ifp) -void *ifp; +void +ipf_nat_ifdetach(ifp) + void *ifp; { - frsync(ifp); + ipf_main_softc_t *softc; + + softc = ipf_get_softc(0); + + ipf_sync(ifp); return; } #endif /* ------------------------------------------------------------------------ */ -/* Function: fr_ipnatderef */ +/* Function: ipf_nat_rule_deref */ /* Returns: Nil */ -/* Parameters: isp(I) - pointer to pointer to NAT rule */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* inp(I) - pointer to pointer to NAT rule */ /* Write Locks: ipf_nat */ /* */ +/* Dropping the refernce count for a rule means that whatever held the */ +/* pointer to this rule (*inp) is no longer interested in it and when the */ +/* reference count drops to zero, any resources allocated for the rule can */ +/* be released and the rule itself free'd. */ /* ------------------------------------------------------------------------ */ -void fr_ipnatderef(inp) -ipnat_t **inp; +void +ipf_nat_rule_deref(softc, inp) + ipf_main_softc_t *softc; + ipnat_t **inp; { - ipnat_t *in; + ipf_nat_softc_t *softn = softc->ipf_nat_soft; + ipnat_t *n; - in = *inp; + n = *inp; *inp = NULL; - in->in_space++; - in->in_use--; - if (in->in_use == 0 && (in->in_flags & IPN_DELETE)) { - if (in->in_apr) - appr_free(in->in_apr); - MUTEX_DESTROY(&in->in_lock); - KFREE(in); - nat_stats.ns_rules--; -#if SOLARIS && !defined(_INET_IP_STACK_H) - if (nat_stats.ns_rules == 0) - pfil_delayed_copy = 1; -#endif + n->in_use--; + if (n->in_use > 0) + return; + + if (n->in_apr != NULL) + ipf_proxy_deref(n->in_apr); + + ipf_nat_rule_fini(softc, n); + + if (n->in_redir & NAT_REDIRECT) { + if ((n->in_flags & IPN_PROXYRULE) == 0) { + ATOMIC_DEC32(softn->ipf_nat_stats.ns_rules_rdr); + } } + if (n->in_redir & (NAT_MAP|NAT_MAPBLK)) { + if ((n->in_flags & IPN_PROXYRULE) == 0) { + ATOMIC_DEC32(softn->ipf_nat_stats.ns_rules_map); + } + } + + if (n->in_tqehead[0] != NULL) { + if (ipf_deletetimeoutqueue(n->in_tqehead[0]) == 0) { + ipf_freetimeoutqueue(softc, n->in_tqehead[1]); + } + } + + if (n->in_tqehead[1] != NULL) { + if (ipf_deletetimeoutqueue(n->in_tqehead[1]) == 0) { + ipf_freetimeoutqueue(softc, n->in_tqehead[1]); + } + } + + if ((n->in_flags & IPN_PROXYRULE) == 0) { + ATOMIC_DEC32(softn->ipf_nat_stats.ns_rules); + } + + MUTEX_DESTROY(&n->in_lock); + + KFREES(n, n->in_size); + +#if SOLARIS && !defined(INSTANCES) + if (softn->ipf_nat_stats.ns_rules == 0) + pfil_delayed_copy = 1; +#endif } /* ------------------------------------------------------------------------ */ -/* Function: fr_natderef */ +/* Function: ipf_nat_deref */ /* Returns: Nil */ -/* Parameters: isp(I) - pointer to pointer to NAT table entry */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* natp(I) - pointer to pointer to NAT table entry */ /* */ /* Decrement the reference counter for this NAT table entry and free it if */ /* there are no more things using it. */ @@ -4765,8 +6307,10 @@ ipnat_t **inp; /* Holding the lock on nat_lock is required to serialise nat_delete() being */ /* called from a NAT flush ioctl with a deref happening because of a packet.*/ /* ------------------------------------------------------------------------ */ -void fr_natderef(natp) -nat_t **natp; +void +ipf_nat_deref(softc, natp) + ipf_main_softc_t *softc; + nat_t **natp; { nat_t *nat; @@ -4776,19 +6320,20 @@ nat_t **natp; MUTEX_ENTER(&nat->nat_lock); if (nat->nat_ref > 1) { nat->nat_ref--; + ASSERT(nat->nat_ref >= 0); MUTEX_EXIT(&nat->nat_lock); return; } MUTEX_EXIT(&nat->nat_lock); - WRITE_ENTER(&ipf_nat); - nat_delete(nat, NL_EXPIRE); - RWLOCK_EXIT(&ipf_nat); + WRITE_ENTER(&softc->ipf_nat); + ipf_nat_delete(softc, nat, NL_EXPIRE); + RWLOCK_EXIT(&softc->ipf_nat); } /* ------------------------------------------------------------------------ */ -/* Function: fr_natclone */ +/* Function: ipf_nat_clone */ /* Returns: ipstate_t* - NULL == cloning failed, */ /* else pointer to new state structure */ /* Parameters: fin(I) - pointer to packet information */ @@ -4797,24 +6342,30 @@ nat_t **natp; /* */ /* Create a "duplcate" state table entry from the master. */ /* ------------------------------------------------------------------------ */ -static nat_t *fr_natclone(fin, nat) -fr_info_t *fin; -nat_t *nat; +nat_t * +ipf_nat_clone(fin, nat) + fr_info_t *fin; + nat_t *nat; { + ipf_main_softc_t *softc = fin->fin_main_soft; + ipf_nat_softc_t *softn = softc->ipf_nat_soft; frentry_t *fr; nat_t *clone; ipnat_t *np; KMALLOC(clone, nat_t *); - if (clone == NULL) + if (clone == NULL) { + NBUMPSIDED(fin->fin_out, ns_clone_nomem); return NULL; + } bcopy((char *)nat, (char *)clone, sizeof(*clone)); MUTEX_NUKE(&clone->nat_lock); + clone->nat_rev = fin->fin_rev; clone->nat_aps = NULL; /* - * Initialize all these so that nat_delete() doesn't cause a crash. + * Initialize all these so that ipf_nat_delete() doesn't cause a crash. */ clone->nat_tqe.tqe_pnext = NULL; clone->nat_tqe.tqe_next = NULL; @@ -4827,14 +6378,16 @@ nat_t *nat; if (clone->nat_hm) clone->nat_hm->hm_ref++; - if (nat_insert(clone, fin->fin_rev) == -1) { + if (ipf_nat_insert(softc, softn, clone) == -1) { KFREE(clone); + NBUMPSIDED(fin->fin_out, ns_insert_fail); return NULL; } + np = clone->nat_ptr; if (np != NULL) { - if (nat_logging) - nat_log(clone, (u_int)np->in_redir); + if (softn->ipf_nat_logging) + ipf_nat_log(softc, softn, clone, NL_CLONE); np->in_use++; } fr = clone->nat_fr; @@ -4844,26 +6397,25 @@ nat_t *nat; MUTEX_EXIT(&fr->fr_lock); } + /* * Because the clone is created outside the normal loop of things and * TCP has special needs in terms of state, initialise the timeout * state of the new NAT from here. */ - if (clone->nat_p == IPPROTO_TCP) { - (void) fr_tcp_age(&clone->nat_tqe, fin, nat_tqb, - clone->nat_flags); + if (clone->nat_pr[0] == IPPROTO_TCP) { + (void) ipf_tcp_age(&clone->nat_tqe, fin, softn->ipf_nat_tcptq, + clone->nat_flags, 2); } -#ifdef IPFILTER_SYNC - clone->nat_sync = ipfsync_new(SMC_NAT, fin, clone); -#endif - if (nat_logging) - nat_log(clone, NL_CLONE); + clone->nat_sync = ipf_sync_new(softc, SMC_NAT, fin, clone); + if (softn->ipf_nat_logging) + ipf_nat_log(softc, softn, clone, NL_CLONE); return clone; } /* ------------------------------------------------------------------------ */ -/* Function: nat_wildok */ +/* Function: ipf_nat_wildok */ /* Returns: int - 1 == packet's ports match wildcards */ /* 0 == packet's ports don't match wildcards */ /* Parameters: nat(I) - NAT entry */ @@ -4875,12 +6427,10 @@ nat_t *nat; /* Use NAT entry and packet direction to determine which combination of */ /* wildcard flags should be used. */ /* ------------------------------------------------------------------------ */ -static int nat_wildok(nat, sport, dport, flags, dir) -nat_t *nat; -int sport; -int dport; -int flags; -int dir; +int +ipf_nat_wildok(nat, sport, dport, flags, dir) + nat_t *nat; + int sport, dport, flags, dir; { /* * When called by dir is set to @@ -4891,34 +6441,33 @@ int dir; * "intended" direction of that NAT entry in nat->nat_dir to decide * which combination of wildcard flags to allow. */ - - switch ((dir << 1) | nat->nat_dir) + switch ((dir << 1) | (nat->nat_dir & (NAT_INBOUND|NAT_OUTBOUND))) { case 3: /* outbound packet / outbound entry */ - if (((nat->nat_inport == sport) || + if (((nat->nat_osport == sport) || (flags & SI_W_SPORT)) && - ((nat->nat_oport == dport) || + ((nat->nat_odport == dport) || (flags & SI_W_DPORT))) return 1; break; case 2: /* outbound packet / inbound entry */ - if (((nat->nat_outport == sport) || - (flags & SI_W_DPORT)) && - ((nat->nat_oport == dport) || - (flags & SI_W_SPORT))) + if (((nat->nat_osport == dport) || + (flags & SI_W_SPORT)) && + ((nat->nat_odport == sport) || + (flags & SI_W_DPORT))) return 1; break; case 1: /* inbound packet / outbound entry */ - if (((nat->nat_oport == sport) || - (flags & SI_W_DPORT)) && - ((nat->nat_outport == dport) || - (flags & SI_W_SPORT))) + if (((nat->nat_osport == dport) || + (flags & SI_W_SPORT)) && + ((nat->nat_odport == sport) || + (flags & SI_W_DPORT))) return 1; break; case 0: /* inbound packet / inbound entry */ - if (((nat->nat_oport == sport) || + if (((nat->nat_osport == sport) || (flags & SI_W_SPORT)) && - ((nat->nat_outport == dport) || + ((nat->nat_odport == dport) || (flags & SI_W_DPORT))) return 1; break; @@ -4942,11 +6491,12 @@ int dir; /* then the TCP header checksum will be updated to reflect the change in */ /* the MSS. */ /* ------------------------------------------------------------------------ */ -static void nat_mssclamp(tcp, maxmss, fin, csump) -tcphdr_t *tcp; -u_32_t maxmss; -fr_info_t *fin; -u_short *csump; +static void +ipf_nat_mssclamp(tcp, maxmss, fin, csump) + tcphdr_t *tcp; + u_32_t maxmss; + fr_info_t *fin; + u_short *csump; { u_char *cp, *ep, opt; int hlen, advance; @@ -4981,7 +6531,7 @@ u_short *csump; cp[2] = maxmss / 256; cp[3] = maxmss & 0xff; CALC_SUMD(mss, maxmss, sumd); - fix_outcksum(fin, csump, sumd); + ipf_fix_outcksum(0, csump, sumd, 0); } break; default: @@ -4996,20 +6546,24 @@ u_short *csump; /* ------------------------------------------------------------------------ */ -/* Function: fr_setnatqueue */ +/* Function: ipf_nat_setqueue */ /* Returns: Nil */ -/* Parameters: nat(I)- pointer to NAT structure */ -/* rev(I) - forward(0) or reverse(1) direction */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* softn(I) - pointer to NAT context structure */ +/* nat(I)- pointer to NAT structure */ /* Locks: ipf_nat (read or write) */ /* */ /* Put the NAT entry on its default queue entry, using rev as a helped in */ /* determining which queue it should be placed on. */ /* ------------------------------------------------------------------------ */ -void fr_setnatqueue(nat, rev) -nat_t *nat; -int rev; +void +ipf_nat_setqueue(softc, softn, nat) + ipf_main_softc_t *softc; + ipf_nat_softc_t *softn; + nat_t *nat; { ipftq_t *oifq, *nifq; + int rev = nat->nat_rev; if (nat->nat_ptr != NULL) nifq = nat->nat_ptr->in_tqehead[rev]; @@ -5017,19 +6571,20 @@ int rev; nifq = NULL; if (nifq == NULL) { - switch (nat->nat_p) + switch (nat->nat_pr[0]) { case IPPROTO_UDP : - nifq = &nat_udptq; + nifq = &softn->ipf_nat_udptq; break; case IPPROTO_ICMP : - nifq = &nat_icmptq; + nifq = &softn->ipf_nat_icmptq; break; case IPPROTO_TCP : - nifq = nat_tqb + nat->nat_tqe.tqe_state[rev]; + nifq = softn->ipf_nat_tcptq + + nat->nat_tqe.tqe_state[rev]; break; default : - nifq = &nat_iptq; + nifq = &softn->ipf_nat_iptq; break; } } @@ -5040,9 +6595,9 @@ int rev; * another, else put it on the end of the newly determined queue. */ if (oifq != NULL) - fr_movequeue(&nat->nat_tqe, oifq, nifq); + ipf_movequeue(softc->ipf_ticks, &nat->nat_tqe, oifq, nifq); else - fr_queueappend(&nat->nat_tqe, nifq, nat); + ipf_queueappend(softc->ipf_ticks, &nat->nat_tqe, nifq, nat); return; } @@ -5050,244 +6605,141 @@ int rev; /* ------------------------------------------------------------------------ */ /* Function: nat_getnext */ /* Returns: int - 0 == ok, else error */ -/* Parameters: t(I) - pointer to ipftoken structure */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* t(I) - pointer to ipftoken structure */ /* itp(I) - pointer to ipfgeniter_t structure */ /* */ /* Fetch the next nat/ipnat structure pointer from the linked list and */ /* copy it out to the storage space pointed to by itp_data. The next item */ /* in the list to look at is put back in the ipftoken struture. */ -/* If we call ipf_freetoken, the accompanying pointer is set to NULL because*/ -/* ipf_freetoken will call a deref function for us and we dont want to call */ -/* that twice (second time would be in the second switch statement below. */ /* ------------------------------------------------------------------------ */ -static int nat_getnext(t, itp) -ipftoken_t *t; -ipfgeniter_t *itp; +static int +ipf_nat_getnext(softc, t, itp, objp) + ipf_main_softc_t *softc; + ipftoken_t *t; + ipfgeniter_t *itp; + ipfobj_t *objp; { + ipf_nat_softc_t *softn = softc->ipf_nat_soft; hostmap_t *hm, *nexthm = NULL, zerohm; ipnat_t *ipn, *nextipnat = NULL, zeroipn; nat_t *nat, *nextnat = NULL, zeronat; - int error = 0, count; - char *dst; + int error = 0; + void *nnext; - count = itp->igi_nitems; - if (count < 1) + if (itp->igi_nitems != 1) { + IPFERROR(60075); return ENOSPC; + } - READ_ENTER(&ipf_nat); + READ_ENTER(&softc->ipf_nat); switch (itp->igi_type) { case IPFGENITER_HOSTMAP : hm = t->ipt_data; if (hm == NULL) { - nexthm = ipf_hm_maplist; + nexthm = softn->ipf_hm_maplist; } else { nexthm = hm->hm_next; } + if (nexthm != NULL) { + ATOMIC_INC32(nexthm->hm_ref); + t->ipt_data = nexthm; + } else { + bzero(&zerohm, sizeof(zerohm)); + nexthm = &zerohm; + t->ipt_data = NULL; + } + nnext = nexthm->hm_next; break; case IPFGENITER_IPNAT : ipn = t->ipt_data; if (ipn == NULL) { - nextipnat = nat_list; + nextipnat = softn->ipf_nat_list; } else { nextipnat = ipn->in_next; } + if (nextipnat != NULL) { + ATOMIC_INC32(nextipnat->in_use); + t->ipt_data = nextipnat; + } else { + bzero(&zeroipn, sizeof(zeroipn)); + nextipnat = &zeroipn; + t->ipt_data = NULL; + } + nnext = nextipnat->in_next; break; case IPFGENITER_NAT : nat = t->ipt_data; if (nat == NULL) { - nextnat = nat_instances; + nextnat = softn->ipf_nat_instances; } else { nextnat = nat->nat_next; } + if (nextnat != NULL) { + MUTEX_ENTER(&nextnat->nat_lock); + nextnat->nat_ref++; + MUTEX_EXIT(&nextnat->nat_lock); + t->ipt_data = nextnat; + } else { + bzero(&zeronat, sizeof(zeronat)); + nextnat = &zeronat; + t->ipt_data = NULL; + } + nnext = nextnat->nat_next; break; + default : - RWLOCK_EXIT(&ipf_nat); + RWLOCK_EXIT(&softc->ipf_nat); + IPFERROR(60055); return EINVAL; } - dst = itp->igi_data; - for (;;) { - switch (itp->igi_type) - { - case IPFGENITER_HOSTMAP : - if (nexthm != NULL) { - if (count == 1) { - ATOMIC_INC32(nexthm->hm_ref); - t->ipt_data = nexthm; - } - } else { - bzero(&zerohm, sizeof(zerohm)); - nexthm = &zerohm; - count = 1; - t->ipt_data = NULL; - } - break; - - case IPFGENITER_IPNAT : - if (nextipnat != NULL) { - if (count == 1) { - MUTEX_ENTER(&nextipnat->in_lock); - nextipnat->in_use++; - MUTEX_EXIT(&nextipnat->in_lock); - t->ipt_data = nextipnat; - } - } else { - bzero(&zeroipn, sizeof(zeroipn)); - nextipnat = &zeroipn; - count = 1; - t->ipt_data = NULL; - } - break; - - case IPFGENITER_NAT : - if (nextnat != NULL) { - if (count == 1) { - MUTEX_ENTER(&nextnat->nat_lock); - nextnat->nat_ref++; - MUTEX_EXIT(&nextnat->nat_lock); - t->ipt_data = nextnat; - } - } else { - bzero(&zeronat, sizeof(zeronat)); - nextnat = &zeronat; - count = 1; - t->ipt_data = NULL; - } - break; - default : - break; - } - RWLOCK_EXIT(&ipf_nat); - - /* - * Copying out to user space needs to be done without the lock. - */ - switch (itp->igi_type) - { - case IPFGENITER_HOSTMAP : - error = COPYOUT(nexthm, dst, sizeof(*nexthm)); - if (error != 0) - error = EFAULT; - else - dst += sizeof(*nexthm); - break; - - case IPFGENITER_IPNAT : - error = COPYOUT(nextipnat, dst, sizeof(*nextipnat)); - if (error != 0) - error = EFAULT; - else - dst += sizeof(*nextipnat); - break; - - case IPFGENITER_NAT : - error = COPYOUT(nextnat, dst, sizeof(*nextnat)); - if (error != 0) - error = EFAULT; - else - dst += sizeof(*nextnat); - break; - } - - if ((count == 1) || (error != 0)) - break; - - count--; - - READ_ENTER(&ipf_nat); - - /* - * We need to have the lock again here to make sure that - * using _next is consistent. - */ - switch (itp->igi_type) - { - case IPFGENITER_HOSTMAP : - nexthm = nexthm->hm_next; - break; - case IPFGENITER_IPNAT : - nextipnat = nextipnat->in_next; - break; - case IPFGENITER_NAT : - nextnat = nextnat->nat_next; - break; - } - } + RWLOCK_EXIT(&softc->ipf_nat); + objp->ipfo_ptr = itp->igi_data; switch (itp->igi_type) { case IPFGENITER_HOSTMAP : + error = COPYOUT(nexthm, objp->ipfo_ptr, sizeof(*nexthm)); + if (error != 0) { + IPFERROR(60049); + error = EFAULT; + } if (hm != NULL) { - WRITE_ENTER(&ipf_nat); - fr_hostmapdel(&hm); - RWLOCK_EXIT(&ipf_nat); + WRITE_ENTER(&softc->ipf_nat); + ipf_nat_hostmapdel(softc, &hm); + RWLOCK_EXIT(&softc->ipf_nat); } break; + case IPFGENITER_IPNAT : + objp->ipfo_size = nextipnat->in_size; + objp->ipfo_type = IPFOBJ_IPNAT; + error = ipf_outobjk(softc, objp, nextipnat); if (ipn != NULL) { - fr_ipnatderef(&ipn); + WRITE_ENTER(&softc->ipf_nat); + ipf_nat_rule_deref(softc, &ipn); + RWLOCK_EXIT(&softc->ipf_nat); } break; + case IPFGENITER_NAT : - if (nat != NULL) { - fr_natderef(&nat); - } - break; - default : + objp->ipfo_size = sizeof(nat_t); + objp->ipfo_type = IPFOBJ_NAT; + error = ipf_outobjk(softc, objp, nextnat); + if (nat != NULL) + ipf_nat_deref(softc, &nat); + break; } - return error; -} - - -/* ------------------------------------------------------------------------ */ -/* Function: nat_iterator */ -/* Returns: int - 0 == ok, else error */ -/* Parameters: token(I) - pointer to ipftoken structure */ -/* itp(I) - pointer to ipfgeniter_t structure */ -/* */ -/* This function acts as a handler for the SIOCGENITER ioctls that use a */ -/* generic structure to iterate through a list. There are three different */ -/* linked lists of NAT related information to go through: NAT rules, active */ -/* NAT mappings and the NAT fragment cache. */ -/* ------------------------------------------------------------------------ */ -static int nat_iterator(token, itp) -ipftoken_t *token; -ipfgeniter_t *itp; -{ - int error; - - if (itp->igi_data == NULL) - return EFAULT; - - token->ipt_subtype = itp->igi_type; - - switch (itp->igi_type) - { - case IPFGENITER_HOSTMAP : - case IPFGENITER_IPNAT : - case IPFGENITER_NAT : - error = nat_getnext(token, itp); - break; - - case IPFGENITER_NATFRAG : -#ifdef USE_MUTEXES - error = fr_nextfrag(token, itp, &ipfr_natlist, - &ipfr_nattail, &ipf_natfrag); -#else - error = fr_nextfrag(token, itp, &ipfr_natlist, &ipfr_nattail); -#endif - break; - default : - error = EINVAL; - break; - } + if (nnext == NULL) + ipf_token_mark_complete(t); return error; } @@ -5296,7 +6748,9 @@ ipfgeniter_t *itp; /* ------------------------------------------------------------------------ */ /* Function: nat_extraflush */ /* Returns: int - 0 == success, -1 == failure */ -/* Parameters: which(I) - how to flush the active NAT table */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* softn(I) - pointer to NAT context structure */ +/* which(I) - how to flush the active NAT table */ /* Write Locks: ipf_nat */ /* */ /* Flush nat tables. Three actions currently defined: */ @@ -5310,45 +6764,51 @@ ipfgeniter_t *itp; /* If that too fails, then work backwards in 30 second intervals */ /* for the last 30 minutes to at worst 30 seconds idle. */ /* ------------------------------------------------------------------------ */ -static int nat_extraflush(which) -int which; +static int +ipf_nat_extraflush(softc, softn, which) + ipf_main_softc_t *softc; + ipf_nat_softc_t *softn; + int which; { - ipftq_t *ifq, *ifqnext; nat_t *nat, **natp; ipftqent_t *tqn; + ipftq_t *ifq; int removed; SPL_INT(s); removed = 0; SPL_NET(s); - switch (which) { case 0 : + softn->ipf_nat_stats.ns_flush_all++; /* * Style 0 flush removes everything... */ - for (natp = &nat_instances; ((nat = *natp) != NULL); ) { - nat_delete(nat, NL_FLUSH); + for (natp = &softn->ipf_nat_instances; + ((nat = *natp) != NULL); ) { + ipf_nat_delete(softc, nat, NL_FLUSH); removed++; } break; case 1 : + softn->ipf_nat_stats.ns_flush_closing++; /* * Since we're only interested in things that are closing, * we can start with the appropriate timeout queue. */ - for (ifq = nat_tqb + IPF_TCPS_CLOSE_WAIT; ifq != NULL; - ifq = ifq->ifq_next) { + for (ifq = softn->ipf_nat_tcptq + IPF_TCPS_CLOSE_WAIT; + ifq != NULL; ifq = ifq->ifq_next) { for (tqn = ifq->ifq_head; tqn != NULL; ) { nat = tqn->tqe_parent; tqn = tqn->tqe_next; - if (nat->nat_p != IPPROTO_TCP) + if (nat->nat_pr[0] != IPPROTO_TCP || + nat->nat_pr[1] != IPPROTO_TCP) break; - nat_delete(nat, NL_EXPIRE); + ipf_nat_delete(softc, nat, NL_EXPIRE); removed++; } } @@ -5356,19 +6816,20 @@ int which; /* * Also need to look through the user defined queues. */ - for (ifq = nat_utqe; ifq != NULL; ifq = ifqnext) { - ifqnext = ifq->ifq_next; + for (ifq = softn->ipf_nat_utqe; ifq != NULL; + ifq = ifq->ifq_next) { for (tqn = ifq->ifq_head; tqn != NULL; ) { nat = tqn->tqe_parent; tqn = tqn->tqe_next; - if (nat->nat_p != IPPROTO_TCP) + if (nat->nat_pr[0] != IPPROTO_TCP || + nat->nat_pr[1] != IPPROTO_TCP) continue; if ((nat->nat_tcpstate[0] > IPF_TCPS_ESTABLISHED) && (nat->nat_tcpstate[1] > IPF_TCPS_ESTABLISHED)) { - nat_delete(nat, NL_EXPIRE); + ipf_nat_delete(softc, nat, NL_EXPIRE); removed++; } } @@ -5386,28 +6847,31 @@ int which; case IPF_TCPS_FIN_WAIT_2 : case IPF_TCPS_TIME_WAIT : case IPF_TCPS_CLOSED : - tqn = nat_tqb[which].ifq_head; + softn->ipf_nat_stats.ns_flush_state++; + tqn = softn->ipf_nat_tcptq[which].ifq_head; while (tqn != NULL) { nat = tqn->tqe_parent; tqn = tqn->tqe_next; - nat_delete(nat, NL_FLUSH); + ipf_nat_delete(softc, nat, NL_FLUSH); removed++; } break; - + default : if (which < 30) break; - + + softn->ipf_nat_stats.ns_flush_timeout++; /* * Take a large arbitrary number to mean the number of seconds * for which which consider to be the maximum value we'll allow * the expiration to be. */ which = IPF_TTLVAL(which); - for (natp = &nat_instances; ((nat = *natp) != NULL); ) { - if (fr_ticks - nat->nat_touched > which) { - nat_delete(nat, NL_FLUSH); + for (natp = &softn->ipf_nat_instances; + ((nat = *natp) != NULL); ) { + if (softc->ipf_ticks - nat->nat_touched > which) { + ipf_nat_delete(softc, nat, NL_FLUSH); removed++; } else natp = &nat->nat_next; @@ -5420,12 +6884,25 @@ int which; return removed; } + softn->ipf_nat_stats.ns_flush_queue++; + /* - * Asked to remove inactive entries because the table is full. + * Asked to remove inactive entries because the table is full, try + * again, 3 times, if first attempt failed with a different criteria + * each time. The order tried in must be in decreasing age. + * Another alternative is to implement random drop and drop N entries + * at random until N have been freed up. */ - if (fr_ticks - nat_last_force_flush > IPF_TTLVAL(5)) { - nat_last_force_flush = fr_ticks; - removed = ipf_queueflush(nat_flush_entry, nat_tqb, nat_utqe); + if (softc->ipf_ticks - softn->ipf_nat_last_force_flush > + IPF_TTLVAL(5)) { + softn->ipf_nat_last_force_flush = softc->ipf_ticks; + + removed = ipf_queueflush(softc, ipf_nat_flush_entry, + softn->ipf_nat_tcptq, + softn->ipf_nat_utqe, + &softn->ipf_nat_stats.ns_active, + softn->ipf_nat_table_sz, + softn->ipf_nat_table_wm_low); } SPL_X(s); @@ -5434,9 +6911,10 @@ int which; /* ------------------------------------------------------------------------ */ -/* Function: nat_flush_entry */ +/* Function: ipf_nat_flush_entry */ /* Returns: 0 - always succeeds */ -/* Parameters: entry(I) - pointer to NAT entry */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* entry(I) - pointer to NAT entry */ /* Write Locks: ipf_nat */ /* */ /* This function is a stepping stone between ipf_queueflush() and */ @@ -5444,50 +6922,1673 @@ int which; /* ipf_queueflush() function. Since the nat_delete() function returns void */ /* we translate that to mean it always succeeds in deleting something. */ /* ------------------------------------------------------------------------ */ -static int nat_flush_entry(entry) -void *entry; +static int +ipf_nat_flush_entry(softc, entry) + ipf_main_softc_t *softc; + void *entry; { - nat_delete(entry, NL_FLUSH); + ipf_nat_delete(softc, entry, NL_FLUSH); return 0; } /* ------------------------------------------------------------------------ */ -/* Function: nat_gettable */ +/* Function: ipf_nat_iterator */ +/* Returns: int - 0 == ok, else error */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* token(I) - pointer to ipftoken structure */ +/* itp(I) - pointer to ipfgeniter_t structure */ +/* obj(I) - pointer to data description structure */ +/* */ +/* This function acts as a handler for the SIOCGENITER ioctls that use a */ +/* generic structure to iterate through a list. There are three different */ +/* linked lists of NAT related information to go through: NAT rules, active */ +/* NAT mappings and the NAT fragment cache. */ +/* ------------------------------------------------------------------------ */ +static int +ipf_nat_iterator(softc, token, itp, obj) + ipf_main_softc_t *softc; + ipftoken_t *token; + ipfgeniter_t *itp; + ipfobj_t *obj; +{ + int error; + + if (itp->igi_data == NULL) { + IPFERROR(60052); + return EFAULT; + } + + switch (itp->igi_type) + { + case IPFGENITER_HOSTMAP : + case IPFGENITER_IPNAT : + case IPFGENITER_NAT : + error = ipf_nat_getnext(softc, token, itp, obj); + break; + + case IPFGENITER_NATFRAG : + error = ipf_frag_nat_next(softc, token, itp); + break; + default : + IPFERROR(60053); + error = EINVAL; + break; + } + + return error; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_nat_setpending */ +/* Returns: Nil */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* nat(I) - pointer to NAT structure */ +/* Locks: ipf_nat (read or write) */ +/* */ +/* Put the NAT entry on to the pending queue - this queue has a very short */ +/* lifetime where items are put that can't be deleted straight away because */ +/* of locking issues but we want to delete them ASAP, anyway. In calling */ +/* this function, it is assumed that the owner (if there is one, as shown */ +/* by nat_me) is no longer interested in it. */ +/* ------------------------------------------------------------------------ */ +void +ipf_nat_setpending(softc, nat) + ipf_main_softc_t *softc; + nat_t *nat; +{ + ipf_nat_softc_t *softn = softc->ipf_nat_soft; + ipftq_t *oifq; + + oifq = nat->nat_tqe.tqe_ifq; + if (oifq != NULL) + ipf_movequeue(softc->ipf_ticks, &nat->nat_tqe, oifq, + &softn->ipf_nat_pending); + else + ipf_queueappend(softc->ipf_ticks, &nat->nat_tqe, + &softn->ipf_nat_pending, nat); + + if (nat->nat_me != NULL) { + *nat->nat_me = NULL; + nat->nat_me = NULL; + nat->nat_ref--; + ASSERT(nat->nat_ref >= 0); + } +} + + +/* ------------------------------------------------------------------------ */ +/* Function: nat_newrewrite */ +/* Returns: int - -1 == error, 0 == success (no move), 1 == success and */ +/* allow rule to be moved if IPN_ROUNDR is set. */ +/* Parameters: fin(I) - pointer to packet information */ +/* nat(I) - pointer to NAT entry */ +/* ni(I) - pointer to structure with misc. information needed */ +/* to create new NAT entry. */ +/* Write Lock: ipf_nat */ +/* */ +/* This function is responsible for setting up an active NAT session where */ +/* we are changing both the source and destination parameters at the same */ +/* time. The loop in here works differently to elsewhere - each iteration */ +/* is responsible for changing a single parameter that can be incremented. */ +/* So one pass may increase the source IP#, next source port, next dest. IP#*/ +/* and the last destination port for a total of 4 iterations to try each. */ +/* This is done to try and exhaustively use the translation space available.*/ +/* ------------------------------------------------------------------------ */ +static int +ipf_nat_newrewrite(fin, nat, nai) + fr_info_t *fin; + nat_t *nat; + natinfo_t *nai; +{ + int src_search = 1; + int dst_search = 1; + fr_info_t frnat; + u_32_t flags; + u_short swap; + ipnat_t *np; + nat_t *natl; + int l = 0; + int changed; + + natl = NULL; + changed = -1; + np = nai->nai_np; + flags = nat->nat_flags; + bcopy((char *)fin, (char *)&frnat, sizeof(*fin)); + + nat->nat_hm = NULL; + + do { + changed = -1; + /* TRACE (l, src_search, dst_search, np) */ + + if ((src_search == 0) && (np->in_spnext == 0) && + (dst_search == 0) && (np->in_dpnext == 0)) { + if (l > 0) + return -1; + } + + /* + * Find a new source address + */ + if (ipf_nat_nextaddr(fin, &np->in_nsrc, &frnat.fin_saddr, + &frnat.fin_saddr) == -1) { + return -1; + } + + if ((np->in_nsrcaddr == 0) && (np->in_nsrcmsk == 0xffffffff)) { + src_search = 0; + if (np->in_stepnext == 0) + np->in_stepnext = 1; + + } else if ((np->in_nsrcaddr == 0) && (np->in_nsrcmsk == 0)) { + src_search = 0; + if (np->in_stepnext == 0) + np->in_stepnext = 1; + + } else if (np->in_nsrcmsk == 0xffffffff) { + src_search = 0; + if (np->in_stepnext == 0) + np->in_stepnext = 1; + + } else if (np->in_nsrcmsk != 0xffffffff) { + if (np->in_stepnext == 0 && changed == -1) { + np->in_snip++; + np->in_stepnext++; + changed = 0; + } + } + + if ((flags & IPN_TCPUDPICMP) != 0) { + if (np->in_spnext != 0) + frnat.fin_data[0] = np->in_spnext; + + /* + * Standard port translation. Select next port. + */ + if ((flags & IPN_FIXEDSPORT) != 0) { + np->in_stepnext = 2; + } else if ((np->in_stepnext == 1) && + (changed == -1) && (natl != NULL)) { + np->in_spnext++; + np->in_stepnext++; + changed = 1; + if (np->in_spnext > np->in_spmax) + np->in_spnext = np->in_spmin; + } + } else { + np->in_stepnext = 2; + } + np->in_stepnext &= 0x3; + + /* + * Find a new destination address + */ + /* TRACE (fin, np, l, frnat) */ + + if (ipf_nat_nextaddr(fin, &np->in_ndst, &frnat.fin_daddr, + &frnat.fin_daddr) == -1) + return -1; + if ((np->in_ndstaddr == 0) && (np->in_ndstmsk == 0xffffffff)) { + dst_search = 0; + if (np->in_stepnext == 2) + np->in_stepnext = 3; + + } else if ((np->in_ndstaddr == 0) && (np->in_ndstmsk == 0)) { + dst_search = 0; + if (np->in_stepnext == 2) + np->in_stepnext = 3; + + } else if (np->in_ndstmsk == 0xffffffff) { + dst_search = 0; + if (np->in_stepnext == 2) + np->in_stepnext = 3; + + } else if (np->in_ndstmsk != 0xffffffff) { + if ((np->in_stepnext == 2) && (changed == -1) && + (natl != NULL)) { + changed = 2; + np->in_stepnext++; + np->in_dnip++; + } + } + + if ((flags & IPN_TCPUDPICMP) != 0) { + if (np->in_dpnext != 0) + frnat.fin_data[1] = np->in_dpnext; + + /* + * Standard port translation. Select next port. + */ + if ((flags & IPN_FIXEDDPORT) != 0) { + np->in_stepnext = 0; + } else if (np->in_stepnext == 3 && changed == -1) { + np->in_dpnext++; + np->in_stepnext++; + changed = 3; + if (np->in_dpnext > np->in_dpmax) + np->in_dpnext = np->in_dpmin; + } + } else { + if (np->in_stepnext == 3) + np->in_stepnext = 0; + } + + /* TRACE (frnat) */ + + /* + * Here we do a lookup of the connection as seen from + * the outside. If an IP# pair already exists, try + * again. So if you have A->B becomes C->B, you can + * also have D->E become C->E but not D->B causing + * another C->B. Also take protocol and ports into + * account when determining whether a pre-existing + * NAT setup will cause an external conflict where + * this is appropriate. + * + * fin_data[] is swapped around because we are doing a + * lookup of the packet is if it were moving in the opposite + * direction of the one we are working with now. + */ + if (flags & IPN_TCPUDP) { + swap = frnat.fin_data[0]; + frnat.fin_data[0] = frnat.fin_data[1]; + frnat.fin_data[1] = swap; + } + if (fin->fin_out == 1) { + natl = ipf_nat_inlookup(&frnat, + flags & ~(SI_WILDP|NAT_SEARCH), + (u_int)frnat.fin_p, + frnat.fin_dst, frnat.fin_src); + + } else { + natl = ipf_nat_outlookup(&frnat, + flags & ~(SI_WILDP|NAT_SEARCH), + (u_int)frnat.fin_p, + frnat.fin_dst, frnat.fin_src); + } + if (flags & IPN_TCPUDP) { + swap = frnat.fin_data[0]; + frnat.fin_data[0] = frnat.fin_data[1]; + frnat.fin_data[1] = swap; + } + + /* TRACE natl, in_stepnext, l */ + + if ((natl != NULL) && (l > 8)) /* XXX 8 is arbitrary */ + return -1; + + np->in_stepnext &= 0x3; + + l++; + changed = -1; + } while (natl != NULL); + + nat->nat_osrcip = fin->fin_src; + nat->nat_odstip = fin->fin_dst; + nat->nat_nsrcip = frnat.fin_src; + nat->nat_ndstip = frnat.fin_dst; + + if ((flags & IPN_TCPUDP) != 0) { + nat->nat_osport = htons(fin->fin_data[0]); + nat->nat_odport = htons(fin->fin_data[1]); + nat->nat_nsport = htons(frnat.fin_data[0]); + nat->nat_ndport = htons(frnat.fin_data[1]); + } else if ((flags & IPN_ICMPQUERY) != 0) { + nat->nat_oicmpid = fin->fin_data[1]; + nat->nat_nicmpid = frnat.fin_data[1]; + } + + return 0; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: nat_newdivert */ +/* Returns: int - -1 == error, 0 == success */ +/* Parameters: fin(I) - pointer to packet information */ +/* nat(I) - pointer to NAT entry */ +/* ni(I) - pointer to structure with misc. information needed */ +/* to create new NAT entry. */ +/* Write Lock: ipf_nat */ +/* */ +/* Create a new NAT divert session as defined by the NAT rule. This is */ +/* somewhat different to other NAT session creation routines because we */ +/* do not iterate through either port numbers or IP addresses, searching */ +/* for a unique mapping, however, a complimentary duplicate check is made. */ +/* ------------------------------------------------------------------------ */ +static int +ipf_nat_newdivert(fin, nat, nai) + fr_info_t *fin; + nat_t *nat; + natinfo_t *nai; +{ + ipf_main_softc_t *softc = fin->fin_main_soft; + ipf_nat_softc_t *softn = softc->ipf_nat_soft; + fr_info_t frnat; + ipnat_t *np; + nat_t *natl; + int p; + + np = nai->nai_np; + bcopy((char *)fin, (char *)&frnat, sizeof(*fin)); + + nat->nat_pr[0] = 0; + nat->nat_osrcaddr = fin->fin_saddr; + nat->nat_odstaddr = fin->fin_daddr; + frnat.fin_saddr = htonl(np->in_snip); + frnat.fin_daddr = htonl(np->in_dnip); + if ((nat->nat_flags & IPN_TCPUDP) != 0) { + nat->nat_osport = htons(fin->fin_data[0]); + nat->nat_odport = htons(fin->fin_data[1]); + } else if ((nat->nat_flags & IPN_ICMPQUERY) != 0) { + nat->nat_oicmpid = fin->fin_data[1]; + } + + if (np->in_redir & NAT_DIVERTUDP) { + frnat.fin_data[0] = np->in_spnext; + frnat.fin_data[1] = np->in_dpnext; + frnat.fin_flx |= FI_TCPUDP; + p = IPPROTO_UDP; + } else { + frnat.fin_flx &= ~FI_TCPUDP; + p = IPPROTO_IPIP; + } + + if (fin->fin_out == 1) { + natl = ipf_nat_inlookup(&frnat, 0, p, + frnat.fin_dst, frnat.fin_src); + + } else { + natl = ipf_nat_outlookup(&frnat, 0, p, + frnat.fin_dst, frnat.fin_src); + } + + if (natl != NULL) { + NBUMPSIDED(fin->fin_out, ns_divert_exist); + return -1; + } + + nat->nat_nsrcaddr = frnat.fin_saddr; + nat->nat_ndstaddr = frnat.fin_daddr; + if ((nat->nat_flags & IPN_TCPUDP) != 0) { + nat->nat_nsport = htons(frnat.fin_data[0]); + nat->nat_ndport = htons(frnat.fin_data[1]); + } else if ((nat->nat_flags & IPN_ICMPQUERY) != 0) { + nat->nat_nicmpid = frnat.fin_data[1]; + } + + nat->nat_pr[fin->fin_out] = fin->fin_p; + nat->nat_pr[1 - fin->fin_out] = p; + + if (np->in_redir & NAT_REDIRECT) + nat->nat_dir = NAT_DIVERTIN; + else + nat->nat_dir = NAT_DIVERTOUT; + + return 0; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: nat_builddivertmp */ +/* Returns: int - -1 == error, 0 == success */ +/* Parameters: softn(I) - pointer to NAT context structure */ +/* np(I) - pointer to a NAT rule */ +/* */ +/* For divert rules, a skeleton packet representing what will be prepended */ +/* to the real packet is created. Even though we don't have the full */ +/* packet here, a checksum is calculated that we update later when we */ +/* fill in the final details. At present a 0 checksum for UDP is being set */ +/* here because it is expected that divert will be used for localhost. */ +/* ------------------------------------------------------------------------ */ +static int +ipf_nat_builddivertmp(softn, np) + ipf_nat_softc_t *softn; + ipnat_t *np; +{ + udphdr_t *uh; + size_t len; + ip_t *ip; + + if ((np->in_redir & NAT_DIVERTUDP) != 0) + len = sizeof(ip_t) + sizeof(udphdr_t); + else + len = sizeof(ip_t); + + ALLOC_MB_T(np->in_divmp, len); + if (np->in_divmp == NULL) { + NBUMPD(ipf_nat_stats, ns_divert_build); + return -1; + } + + /* + * First, the header to get the packet diverted to the new destination + */ + ip = MTOD(np->in_divmp, ip_t *); + IP_V_A(ip, 4); + IP_HL_A(ip, 5); + ip->ip_tos = 0; + if ((np->in_redir & NAT_DIVERTUDP) != 0) + ip->ip_p = IPPROTO_UDP; + else + ip->ip_p = IPPROTO_IPIP; + ip->ip_ttl = 255; + ip->ip_off = 0; + ip->ip_sum = 0; + ip->ip_len = htons(len); + ip->ip_id = 0; + ip->ip_src.s_addr = htonl(np->in_snip); + ip->ip_dst.s_addr = htonl(np->in_dnip); + ip->ip_sum = ipf_cksum((u_short *)ip, sizeof(*ip)); + + if (np->in_redir & NAT_DIVERTUDP) { + uh = (udphdr_t *)(ip + 1); + uh->uh_sum = 0; + uh->uh_ulen = 8; + uh->uh_sport = htons(np->in_spnext); + uh->uh_dport = htons(np->in_dpnext); + } + + return 0; +} + + +#define MINDECAP (sizeof(ip_t) + sizeof(udphdr_t) + sizeof(ip_t)) + +/* ------------------------------------------------------------------------ */ +/* Function: nat_decap */ +/* Returns: int - -1 == error, 0 == success */ +/* Parameters: fin(I) - pointer to packet information */ +/* nat(I) - pointer to current NAT session */ +/* */ +/* This function is responsible for undoing a packet's encapsulation in the */ +/* reverse of an encap/divert rule. After removing the outer encapsulation */ +/* it is necessary to call ipf_makefrip() again so that the contents of 'fin'*/ +/* match the "new" packet as it may still be used by IPFilter elsewhere. */ +/* We use "dir" here as the basis for some of the expectations about the */ +/* outer header. If we return an error, the goal is to leave the original */ +/* packet information undisturbed - this falls short at the end where we'd */ +/* need to back a backup copy of "fin" - expensive. */ +/* ------------------------------------------------------------------------ */ +static int +ipf_nat_decap(fin, nat) + fr_info_t *fin; + nat_t *nat; +{ + ipf_main_softc_t *softc = fin->fin_main_soft; + ipf_nat_softc_t *softn = softc->ipf_nat_soft; + char *hdr; + int hlen; + int skip; + mb_t *m; + + if ((fin->fin_flx & FI_ICMPERR) != 0) { + /* + * ICMP packets don't get decapsulated, instead what we need + * to do is change the ICMP reply from including (in the data + * portion for errors) the encapsulated packet that we sent + * out to something that resembles the original packet prior + * to encapsulation. This isn't done here - all we're doing + * here is changing the outer address to ensure that it gets + * targetted back to the correct system. + */ + + if (nat->nat_dir & NAT_OUTBOUND) { + u_32_t sum1, sum2, sumd; + + sum1 = ntohl(fin->fin_daddr); + sum2 = ntohl(nat->nat_osrcaddr); + CALC_SUMD(sum1, sum2, sumd); + fin->fin_ip->ip_dst = nat->nat_osrcip; + fin->fin_daddr = nat->nat_osrcaddr; +#if !defined(_KERNEL) || defined(MENTAT) || defined(__sgi) || \ + defined(__osf__) || defined(linux) + ipf_fix_outcksum(0, &fin->fin_ip->ip_sum, sumd, 0); +#endif + } + return 0; + } + + m = fin->fin_m; + skip = fin->fin_hlen; + + switch (nat->nat_dir) + { + case NAT_DIVERTIN : + case NAT_DIVERTOUT : + if (fin->fin_plen < MINDECAP) + return -1; + skip += sizeof(udphdr_t); + break; + + case NAT_ENCAPIN : + case NAT_ENCAPOUT : + if (fin->fin_plen < (skip + sizeof(ip_t))) + return -1; + break; + default : + return -1; + /* NOTREACHED */ + } + + /* + * The aim here is to keep the original packet details in "fin" for + * as long as possible so that returning with an error is for the + * original packet and there is little undoing work to do. + */ + if (M_LEN(m) < skip + sizeof(ip_t)) { + if (ipf_pr_pullup(fin, skip + sizeof(ip_t)) == -1) + return -1; + } + + hdr = MTOD(fin->fin_m, char *); + fin->fin_ip = (ip_t *)(hdr + skip); + hlen = IP_HL(fin->fin_ip) << 2; + + if (ipf_pr_pullup(fin, skip + hlen) == -1) { + NBUMPSIDED(fin->fin_out, ns_decap_pullup); + return -1; + } + + fin->fin_hlen = hlen; + fin->fin_dlen -= skip; + fin->fin_plen -= skip; + fin->fin_ipoff += skip; + + if (ipf_makefrip(hlen, (ip_t *)hdr, fin) == -1) { + NBUMPSIDED(fin->fin_out, ns_decap_bad); + return -1; + } + + return skip; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: nat_nextaddr */ +/* Returns: int - -1 == bad input (no new address), */ +/* 0 == success and dst has new address */ +/* Parameters: fin(I) - pointer to packet information */ +/* na(I) - how to generate new address */ +/* old(I) - original address being replaced */ +/* dst(O) - where to put the new address */ +/* Write Lock: ipf_nat */ +/* */ +/* This function uses the contents of the "na" structure, in combination */ +/* with "old" to produce a new address to store in "dst". Not all of the */ +/* possible uses of "na" will result in a new address. */ +/* ------------------------------------------------------------------------ */ +static int +ipf_nat_nextaddr(fin, na, old, dst) + fr_info_t *fin; + nat_addr_t *na; + u_32_t *old, *dst; +{ + ipf_main_softc_t *softc = fin->fin_main_soft; + ipf_nat_softc_t *softn = softc->ipf_nat_soft; + u_32_t amin, amax, new; + i6addr_t newip; + int error; + + new = 0; + amin = na->na_addr[0].in4.s_addr; + + switch (na->na_atype) + { + case FRI_RANGE : + amax = na->na_addr[1].in4.s_addr; + break; + + case FRI_NETMASKED : + case FRI_DYNAMIC : + case FRI_NORMAL : + /* + * Compute the maximum address by adding the inverse of the + * netmask to the minimum address. + */ + amax = ~na->na_addr[1].in4.s_addr; + amax |= amin; + break; + + case FRI_LOOKUP : + break; + + case FRI_BROADCAST : + case FRI_PEERADDR : + case FRI_NETWORK : + default : + return -1; + } + + error = -1; + + if (na->na_atype == FRI_LOOKUP) { + if (na->na_type == IPLT_DSTLIST) { + error = ipf_dstlist_select_node(fin, na->na_ptr, dst, + NULL); + } else { + NBUMPSIDE(fin->fin_out, ns_badnextaddr); + } + + } else if (na->na_atype == IPLT_NONE) { + /* + * 0/0 as the new address means leave it alone. + */ + if (na->na_addr[0].in4.s_addr == 0 && + na->na_addr[1].in4.s_addr == 0) { + new = *old; + + /* + * 0/32 means get the interface's address + */ + } else if (na->na_addr[0].in4.s_addr == 0 && + na->na_addr[1].in4.s_addr == 0xffffffff) { + if (ipf_ifpaddr(softc, 4, na->na_atype, + fin->fin_ifp, &newip, NULL) == -1) { + NBUMPSIDED(fin->fin_out, ns_ifpaddrfail); + return -1; + } + new = newip.in4.s_addr; + } else { + new = htonl(na->na_nextip); + } + *dst = new; + error = 0; + + } else { + NBUMPSIDE(fin->fin_out, ns_badnextaddr); + } + + return error; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: nat_nextaddrinit */ +/* Returns: int - 0 == success, else error number */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* na(I) - NAT address information for generating new addr*/ +/* initial(I) - flag indicating if it is the first call for */ +/* this "na" structure. */ +/* ifp(I) - network interface to derive address */ +/* information from. */ +/* */ +/* This function is expected to be called in two scenarious: when a new NAT */ +/* rule is loaded into the kernel and when the list of NAT rules is sync'd */ +/* up with the valid network interfaces (possibly due to them changing.) */ +/* To distinguish between these, the "initial" parameter is used. If it is */ +/* 1 then this indicates the rule has just been reloaded and 0 for when we */ +/* are updating information. This difference is important because in */ +/* instances where we are not updating address information associated with */ +/* a network interface, we don't want to disturb what the "next" address to */ +/* come out of ipf_nat_nextaddr() will be. */ +/* ------------------------------------------------------------------------ */ +static int +ipf_nat_nextaddrinit(softc, base, na, initial, ifp) + ipf_main_softc_t *softc; + char *base; + nat_addr_t *na; + int initial; + void *ifp; +{ + + switch (na->na_atype) + { + case FRI_LOOKUP : + if (na->na_subtype == 0) { + na->na_ptr = ipf_lookup_res_num(softc, IPL_LOGNAT, + na->na_type, + na->na_num, + &na->na_func); + } else if (na->na_subtype == 1) { + na->na_ptr = ipf_lookup_res_name(softc, IPL_LOGNAT, + na->na_type, + base + na->na_num, + &na->na_func); + } + if (na->na_func == NULL) { + IPFERROR(60060); + return ESRCH; + } + if (na->na_ptr == NULL) { + IPFERROR(60056); + return ESRCH; + } + break; + + case FRI_DYNAMIC : + case FRI_BROADCAST : + case FRI_NETWORK : + case FRI_NETMASKED : + case FRI_PEERADDR : + if (ifp != NULL) + (void )ipf_ifpaddr(softc, 4, na->na_atype, ifp, + &na->na_addr[0], &na->na_addr[1]); + break; + + case FRI_SPLIT : + case FRI_RANGE : + if (initial) + na->na_nextip = ntohl(na->na_addr[0].in4.s_addr); + break; + + case FRI_NONE : + na->na_addr[0].in4.s_addr &= na->na_addr[1].in4.s_addr; + return 0; + + case FRI_NORMAL : + na->na_addr[0].in4.s_addr &= na->na_addr[1].in4.s_addr; + break; + + default : + IPFERROR(60054); + return EINVAL; + } + + if (initial && (na->na_atype == FRI_NORMAL)) { + if (na->na_addr[0].in4.s_addr == 0) { + if ((na->na_addr[1].in4.s_addr == 0xffffffff) || + (na->na_addr[1].in4.s_addr == 0)) { + return 0; + } + } + + if (na->na_addr[1].in4.s_addr == 0xffffffff) { + na->na_nextip = ntohl(na->na_addr[0].in4.s_addr); + } else { + na->na_nextip = ntohl(na->na_addr[0].in4.s_addr) + 1; + } + } + + return 0; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_nat_matchflush */ +/* Returns: int - -1 == error, 0 == success */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* softn(I) - pointer to NAT context structure */ +/* nat(I) - pointer to current NAT session */ +/* */ +/* ------------------------------------------------------------------------ */ +static int +ipf_nat_matchflush(softc, softn, data) + ipf_main_softc_t *softc; + ipf_nat_softc_t *softn; + caddr_t data; +{ + int *array, flushed, error; + nat_t *nat, *natnext; + ipfobj_t obj; + + error = ipf_matcharray_load(softc, data, &obj, &array); + if (error != 0) + return error; + + flushed = 0; + + for (nat = softn->ipf_nat_instances; nat != NULL; nat = natnext) { + natnext = nat->nat_next; + if (ipf_nat_matcharray(nat, array, softc->ipf_ticks) == 0) { + ipf_nat_delete(softc, nat, NL_FLUSH); + flushed++; + } + } + + obj.ipfo_retval = flushed; + error = BCOPYOUT(&obj, data, sizeof(obj)); + + KFREES(array, array[0] * sizeof(*array)); + + return error; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_nat_matcharray */ +/* Returns: int - -1 == error, 0 == success */ +/* Parameters: fin(I) - pointer to packet information */ +/* nat(I) - pointer to current NAT session */ +/* */ +/* ------------------------------------------------------------------------ */ +static int +ipf_nat_matcharray(nat, array, ticks) + nat_t *nat; + int *array; + u_long ticks; +{ + int i, n, *x, e, p; + + e = 0; + n = array[0]; + x = array + 1; + + for (; n > 0; x += 3 + x[2]) { + if (x[0] == IPF_EXP_END) + break; + e = 0; + + n -= x[2] + 3; + if (n < 0) + break; + + p = x[0] >> 16; + if (p != 0 && p != nat->nat_pr[1]) + break; + + switch (x[0]) + { + case IPF_EXP_IP_PR : + for (i = 0; !e && i < x[2]; i++) { + e |= (nat->nat_pr[1] == x[i + 3]); + } + break; + + case IPF_EXP_IP_SRCADDR : + if (nat->nat_v[0] == 4) { + for (i = 0; !e && i < x[2]; i++) { + e |= ((nat->nat_osrcaddr & x[i + 4]) == + x[i + 3]); + } + } + if (nat->nat_v[1] == 4) { + for (i = 0; !e && i < x[2]; i++) { + e |= ((nat->nat_nsrcaddr & x[i + 4]) == + x[i + 3]); + } + } + break; + + case IPF_EXP_IP_DSTADDR : + if (nat->nat_v[0] == 4) { + for (i = 0; !e && i < x[2]; i++) { + e |= ((nat->nat_odstaddr & x[i + 4]) == + x[i + 3]); + } + } + if (nat->nat_v[1] == 4) { + for (i = 0; !e && i < x[2]; i++) { + e |= ((nat->nat_ndstaddr & x[i + 4]) == + x[i + 3]); + } + } + break; + + case IPF_EXP_IP_ADDR : + for (i = 0; !e && i < x[2]; i++) { + if (nat->nat_v[0] == 4) { + e |= ((nat->nat_osrcaddr & x[i + 4]) == + x[i + 3]); + } + if (nat->nat_v[1] == 4) { + e |= ((nat->nat_nsrcaddr & x[i + 4]) == + x[i + 3]); + } + if (nat->nat_v[0] == 4) { + e |= ((nat->nat_odstaddr & x[i + 4]) == + x[i + 3]); + } + if (nat->nat_v[1] == 4) { + e |= ((nat->nat_ndstaddr & x[i + 4]) == + x[i + 3]); + } + } + break; + +#ifdef USE_INET6 + case IPF_EXP_IP6_SRCADDR : + if (nat->nat_v[0] == 6) { + for (i = 0; !e && i < x[3]; i++) { + e |= IP6_MASKEQ(&nat->nat_osrc6, + x + i + 7, x + i + 3); + } + } + if (nat->nat_v[1] == 6) { + for (i = 0; !e && i < x[3]; i++) { + e |= IP6_MASKEQ(&nat->nat_nsrc6, + x + i + 7, x + i + 3); + } + } + break; + + case IPF_EXP_IP6_DSTADDR : + if (nat->nat_v[0] == 6) { + for (i = 0; !e && i < x[3]; i++) { + e |= IP6_MASKEQ(&nat->nat_odst6, + x + i + 7, + x + i + 3); + } + } + if (nat->nat_v[1] == 6) { + for (i = 0; !e && i < x[3]; i++) { + e |= IP6_MASKEQ(&nat->nat_ndst6, + x + i + 7, + x + i + 3); + } + } + break; + + case IPF_EXP_IP6_ADDR : + for (i = 0; !e && i < x[3]; i++) { + if (nat->nat_v[0] == 6) { + e |= IP6_MASKEQ(&nat->nat_osrc6, + x + i + 7, + x + i + 3); + } + if (nat->nat_v[0] == 6) { + e |= IP6_MASKEQ(&nat->nat_odst6, + x + i + 7, + x + i + 3); + } + if (nat->nat_v[1] == 6) { + e |= IP6_MASKEQ(&nat->nat_nsrc6, + x + i + 7, + x + i + 3); + } + if (nat->nat_v[1] == 6) { + e |= IP6_MASKEQ(&nat->nat_ndst6, + x + i + 7, + x + i + 3); + } + } + break; +#endif + + case IPF_EXP_UDP_PORT : + case IPF_EXP_TCP_PORT : + for (i = 0; !e && i < x[2]; i++) { + e |= (nat->nat_nsport == x[i + 3]) || + (nat->nat_ndport == x[i + 3]); + } + break; + + case IPF_EXP_UDP_SPORT : + case IPF_EXP_TCP_SPORT : + for (i = 0; !e && i < x[2]; i++) { + e |= (nat->nat_nsport == x[i + 3]); + } + break; + + case IPF_EXP_UDP_DPORT : + case IPF_EXP_TCP_DPORT : + for (i = 0; !e && i < x[2]; i++) { + e |= (nat->nat_ndport == x[i + 3]); + } + break; + + case IPF_EXP_TCP_STATE : + for (i = 0; !e && i < x[2]; i++) { + e |= (nat->nat_tcpstate[0] == x[i + 3]) || + (nat->nat_tcpstate[1] == x[i + 3]); + } + break; + + case IPF_EXP_IDLE_GT : + e |= (ticks - nat->nat_touched > x[3]); + break; + } + e ^= x[1]; + + if (!e) + break; + } + + return e; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_nat_gettable */ /* Returns: int - 0 = success, else error */ -/* Parameters: data(I) - pointer to ioctl data */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* softn(I) - pointer to NAT context structure */ +/* data(I) - pointer to ioctl data */ /* */ /* This function handles ioctl requests for tables of nat information. */ /* At present the only table it deals with is the hash bucket statistics. */ /* ------------------------------------------------------------------------ */ -static int nat_gettable(data) -char *data; +static int +ipf_nat_gettable(softc, softn, data) + ipf_main_softc_t *softc; + ipf_nat_softc_t *softn; + char *data; { ipftable_t table; int error; - error = fr_inobj(data, &table, IPFOBJ_GTABLE); + error = ipf_inobj(softc, data, NULL, &table, IPFOBJ_GTABLE); if (error != 0) return error; switch (table.ita_type) { case IPFTABLE_BUCKETS_NATIN : - error = COPYOUT(nat_stats.ns_bucketlen[0], table.ita_table, - ipf_nattable_sz * sizeof(u_long)); + error = COPYOUT(softn->ipf_nat_stats.ns_side[0].ns_bucketlen, + table.ita_table, + softn->ipf_nat_table_sz * sizeof(u_int)); break; case IPFTABLE_BUCKETS_NATOUT : - error = COPYOUT(nat_stats.ns_bucketlen[1], table.ita_table, - ipf_nattable_sz * sizeof(u_long)); + error = COPYOUT(softn->ipf_nat_stats.ns_side[1].ns_bucketlen, + table.ita_table, + softn->ipf_nat_table_sz * sizeof(u_int)); break; default : + IPFERROR(60058); return EINVAL; } if (error != 0) { + IPFERROR(60059); error = EFAULT; } return error; } + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_nat_settimeout */ +/* Returns: int - 0 = success, else failure */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* t(I) - pointer to tunable */ +/* p(I) - pointer to new tuning data */ +/* */ +/* Apply the timeout change to the NAT timeout queues. */ +/* ------------------------------------------------------------------------ */ +int +ipf_nat_settimeout(softc, t, p) + struct ipf_main_softc_s *softc; + ipftuneable_t *t; + ipftuneval_t *p; +{ + ipf_nat_softc_t *softn = softc->ipf_nat_soft; + + if (!strncmp(t->ipft_name, "tcp_", 4)) + return ipf_settimeout_tcp(t, p, softn->ipf_nat_tcptq); + + if (!strcmp(t->ipft_name, "udp_timeout")) { + ipf_apply_timeout(&softn->ipf_nat_udptq, p->ipftu_int); + } else if (!strcmp(t->ipft_name, "udp_ack_timeout")) { + ipf_apply_timeout(&softn->ipf_nat_udpacktq, p->ipftu_int); + } else if (!strcmp(t->ipft_name, "icmp_timeout")) { + ipf_apply_timeout(&softn->ipf_nat_icmptq, p->ipftu_int); + } else if (!strcmp(t->ipft_name, "icmp_ack_timeout")) { + ipf_apply_timeout(&softn->ipf_nat_icmpacktq, p->ipftu_int); + } else if (!strcmp(t->ipft_name, "ip_timeout")) { + ipf_apply_timeout(&softn->ipf_nat_iptq, p->ipftu_int); + } else { + IPFERROR(60062); + return ESRCH; + } + return 0; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_nat_rehash */ +/* Returns: int - 0 = success, else failure */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* t(I) - pointer to tunable */ +/* p(I) - pointer to new tuning data */ +/* */ +/* To change the size of the basic NAT table, we need to first allocate the */ +/* new tables (lest it fails and we've got nowhere to store all of the NAT */ +/* sessions currently active) and then walk through the entire list and */ +/* insert them into the table. There are two tables here: an inbound one */ +/* and an outbound one. Each NAT entry goes into each table once. */ +/* ------------------------------------------------------------------------ */ +int +ipf_nat_rehash(softc, t, p) + ipf_main_softc_t *softc; + ipftuneable_t *t; + ipftuneval_t *p; +{ + ipf_nat_softc_t *softn = softc->ipf_nat_soft; + nat_t **newtab[2], *nat, **natp; + u_int *bucketlens[2]; + u_int maxbucket; + u_int newsize; + int error; + u_int hv; + int i; + + newsize = p->ipftu_int; + /* + * In case there is nothing to do... + */ + if (newsize == softn->ipf_nat_table_sz) + return 0; + + newtab[0] = NULL; + newtab[1] = NULL; + bucketlens[0] = NULL; + bucketlens[1] = NULL; + /* + * 4 tables depend on the NAT table size: the inbound looking table, + * the outbound lookup table and the hash chain length for each. + */ + KMALLOCS(newtab[0], nat_t **, newsize * sizeof(nat_t *)); + if (newtab == NULL) { + error = 60063; + goto badrehash; + } + + KMALLOCS(newtab[1], nat_t **, newsize * sizeof(nat_t *)); + if (newtab == NULL) { + error = 60064; + goto badrehash; + } + + KMALLOCS(bucketlens[0], u_int *, newsize * sizeof(u_int)); + if (bucketlens[0] == NULL) { + error = 60065; + goto badrehash; + } + + KMALLOCS(bucketlens[1], u_int *, newsize * sizeof(u_int)); + if (bucketlens[1] == NULL) { + error = 60066; + goto badrehash; + } + + /* + * Recalculate the maximum length based on the new size. + */ + for (maxbucket = 0, i = newsize; i > 0; i >>= 1) + maxbucket++; + maxbucket *= 2; + + bzero((char *)newtab[0], newsize * sizeof(nat_t *)); + bzero((char *)newtab[1], newsize * sizeof(nat_t *)); + bzero((char *)bucketlens[0], newsize * sizeof(u_int)); + bzero((char *)bucketlens[1], newsize * sizeof(u_int)); + + WRITE_ENTER(&softc->ipf_nat); + + if (softn->ipf_nat_table[0] != NULL) { + KFREES(softn->ipf_nat_table[0], + softn->ipf_nat_table_sz * + sizeof(*softn->ipf_nat_table[0])); + } + softn->ipf_nat_table[0] = newtab[0]; + + if (softn->ipf_nat_table[1] != NULL) { + KFREES(softn->ipf_nat_table[1], + softn->ipf_nat_table_sz * + sizeof(*softn->ipf_nat_table[1])); + } + softn->ipf_nat_table[1] = newtab[1]; + + if (softn->ipf_nat_stats.ns_side[0].ns_bucketlen != NULL) { + KFREES(softn->ipf_nat_stats.ns_side[0].ns_bucketlen, + softn->ipf_nat_table_sz * sizeof(u_int)); + } + softn->ipf_nat_stats.ns_side[0].ns_bucketlen = bucketlens[0]; + + if (softn->ipf_nat_stats.ns_side[1].ns_bucketlen != NULL) { + KFREES(softn->ipf_nat_stats.ns_side[1].ns_bucketlen, + softn->ipf_nat_table_sz * sizeof(u_int)); + } + softn->ipf_nat_stats.ns_side[1].ns_bucketlen = bucketlens[1]; + +#ifdef USE_INET6 + if (softn->ipf_nat_stats.ns_side6[0].ns_bucketlen != NULL) { + KFREES(softn->ipf_nat_stats.ns_side6[0].ns_bucketlen, + softn->ipf_nat_table_sz * sizeof(u_int)); + } + softn->ipf_nat_stats.ns_side6[0].ns_bucketlen = bucketlens[0]; + + if (softn->ipf_nat_stats.ns_side6[1].ns_bucketlen != NULL) { + KFREES(softn->ipf_nat_stats.ns_side6[1].ns_bucketlen, + softn->ipf_nat_table_sz * sizeof(u_int)); + } + softn->ipf_nat_stats.ns_side6[1].ns_bucketlen = bucketlens[1]; +#endif + + softn->ipf_nat_maxbucket = maxbucket; + softn->ipf_nat_table_sz = newsize; + /* + * Walk through the entire list of NAT table entries and put them + * in the new NAT table, somewhere. Because we have a new table, + * we need to restart the counter of how many chains are in use. + */ + softn->ipf_nat_stats.ns_side[0].ns_inuse = 0; + softn->ipf_nat_stats.ns_side[1].ns_inuse = 0; +#ifdef USE_INET6 + softn->ipf_nat_stats.ns_side6[0].ns_inuse = 0; + softn->ipf_nat_stats.ns_side6[1].ns_inuse = 0; +#endif + + for (nat = softn->ipf_nat_instances; nat != NULL; nat = nat->nat_next) { + nat->nat_hnext[0] = NULL; + nat->nat_phnext[0] = NULL; + hv = nat->nat_hv[0] % softn->ipf_nat_table_sz; + + natp = &softn->ipf_nat_table[0][hv]; + if (*natp) { + (*natp)->nat_phnext[0] = &nat->nat_hnext[0]; + } else { + NBUMPSIDE(0, ns_inuse); + } + nat->nat_phnext[0] = natp; + nat->nat_hnext[0] = *natp; + *natp = nat; + NBUMPSIDE(0, ns_bucketlen[hv]); + + nat->nat_hnext[1] = NULL; + nat->nat_phnext[1] = NULL; + hv = nat->nat_hv[1] % softn->ipf_nat_table_sz; + + natp = &softn->ipf_nat_table[1][hv]; + if (*natp) { + (*natp)->nat_phnext[1] = &nat->nat_hnext[1]; + } else { + NBUMPSIDE(1, ns_inuse); + } + nat->nat_phnext[1] = natp; + nat->nat_hnext[1] = *natp; + *natp = nat; + NBUMPSIDE(1, ns_bucketlen[hv]); + } + RWLOCK_EXIT(&softc->ipf_nat); + + return 0; + +badrehash: + if (bucketlens[1] != NULL) { + KFREES(bucketlens[0], newsize * sizeof(u_int)); + } + if (bucketlens[0] != NULL) { + KFREES(bucketlens[0], newsize * sizeof(u_int)); + } + if (newtab[0] != NULL) { + KFREES(newtab[0], newsize * sizeof(nat_t *)); + } + if (newtab[1] != NULL) { + KFREES(newtab[1], newsize * sizeof(nat_t *)); + } + IPFERROR(error); + return ENOMEM; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_nat_rehash_rules */ +/* Returns: int - 0 = success, else failure */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* t(I) - pointer to tunable */ +/* p(I) - pointer to new tuning data */ +/* */ +/* All of the NAT rules hang off of a hash table that is searched with a */ +/* hash on address after the netmask is applied. There is a different table*/ +/* for both inbound rules (rdr) and outbound (map.) The resizing will only */ +/* affect one of these two tables. */ +/* ------------------------------------------------------------------------ */ +int +ipf_nat_rehash_rules(softc, t, p) + ipf_main_softc_t *softc; + ipftuneable_t *t; + ipftuneval_t *p; +{ + ipf_nat_softc_t *softn = softc->ipf_nat_soft; + ipnat_t **newtab, *np, ***old, **npp; + u_int newsize; + u_int mask; + u_int hv; + + newsize = p->ipftu_int; + /* + * In case there is nothing to do... + */ + if (newsize == *t->ipft_pint) + return 0; + + /* + * All inbound rules have the NAT_REDIRECT bit set in in_redir and + * all outbound rules have either NAT_MAP or MAT_MAPBLK set. + * This if statement allows for some more generic code to be below, + * rather than two huge gobs of code that almost do the same thing. + */ + if (t->ipft_pint == &softn->ipf_nat_rdrrules_sz) { + old = &softn->ipf_nat_rdr_rules; + mask = NAT_REDIRECT; + } else { + old = &softn->ipf_nat_map_rules; + mask = NAT_MAP|NAT_MAPBLK; + } + + KMALLOCS(newtab, ipnat_t **, newsize * sizeof(ipnat_t *)); + if (newtab == NULL) { + IPFERROR(60067); + return ENOMEM; + } + + bzero((char *)newtab, newsize * sizeof(ipnat_t *)); + + WRITE_ENTER(&softc->ipf_nat); + + if (*old != NULL) { + KFREES(*old, *t->ipft_pint * sizeof(ipnat_t **)); + } + *old = newtab; + *t->ipft_pint = newsize; + + for (np = softn->ipf_nat_list; np != NULL; np = np->in_next) { + if ((np->in_redir & mask) == 0) + continue; + + if (np->in_redir & NAT_REDIRECT) { + np->in_rnext = NULL; + hv = np->in_hv[0] % newsize; + for (npp = newtab + hv; *npp != NULL; ) + npp = &(*npp)->in_rnext; + np->in_prnext = npp; + *npp = np; + } + if (np->in_redir & NAT_MAP) { + np->in_mnext = NULL; + hv = np->in_hv[1] % newsize; + for (npp = newtab + hv; *npp != NULL; ) + npp = &(*npp)->in_mnext; + np->in_pmnext = npp; + *npp = np; + } + + } + RWLOCK_EXIT(&softc->ipf_nat); + + return 0; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_nat_hostmap_rehash */ +/* Returns: int - 0 = success, else failure */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* t(I) - pointer to tunable */ +/* p(I) - pointer to new tuning data */ +/* */ +/* Allocate and populate a new hash table that will contain a reference to */ +/* all of the active IP# translations currently in place. */ +/* ------------------------------------------------------------------------ */ +int +ipf_nat_hostmap_rehash(softc, t, p) + ipf_main_softc_t *softc; + ipftuneable_t *t; + ipftuneval_t *p; +{ + ipf_nat_softc_t *softn = softc->ipf_nat_soft; + hostmap_t *hm, **newtab; + u_int newsize; + u_int hv; + + newsize = p->ipftu_int; + /* + * In case there is nothing to do... + */ + if (newsize == *t->ipft_pint) + return 0; + + KMALLOCS(newtab, hostmap_t **, newsize * sizeof(hostmap_t *)); + if (newtab == NULL) { + IPFERROR(60068); + return ENOMEM; + } + + bzero((char *)newtab, newsize * sizeof(hostmap_t *)); + + WRITE_ENTER(&softc->ipf_nat); + if (softn->ipf_hm_maptable != NULL) { + KFREES(softn->ipf_hm_maptable, + softn->ipf_nat_hostmap_sz * sizeof(hostmap_t *)); + } + softn->ipf_hm_maptable = newtab; + softn->ipf_nat_hostmap_sz = newsize; + + for (hm = softn->ipf_hm_maplist; hm != NULL; hm = hm->hm_next) { + hv = hm->hm_hv % softn->ipf_nat_hostmap_sz; + hm->hm_hnext = softn->ipf_hm_maptable[hv]; + hm->hm_phnext = softn->ipf_hm_maptable + hv; + if (softn->ipf_hm_maptable[hv] != NULL) + softn->ipf_hm_maptable[hv]->hm_phnext = &hm->hm_hnext; + softn->ipf_hm_maptable[hv] = hm; + } + RWLOCK_EXIT(&softc->ipf_nat); + + return 0; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_nat_add_tq */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* */ +/* ------------------------------------------------------------------------ */ +ipftq_t * +ipf_nat_add_tq(softc, ttl) + ipf_main_softc_t *softc; + int ttl; +{ + ipf_nat_softc_t *softs = softc->ipf_nat_soft; + + return ipf_addtimeoutqueue(softc, &softs->ipf_nat_utqe, ttl); +} + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_nat_uncreate */ +/* Returns: Nil */ +/* Parameters: fin(I) - pointer to packet information */ +/* */ +/* This function is used to remove a NAT entry from the NAT table when we */ +/* decide that the create was actually in error. It is thus assumed that */ +/* fin_flx will have both FI_NATED and FI_NATNEW set. Because we're dealing */ +/* with the translated packet (not the original), we have to reverse the */ +/* lookup. Although doing the lookup is expensive (relatively speaking), it */ +/* is not anticipated that this will be a frequent occurance for normal */ +/* traffic patterns. */ +/* ------------------------------------------------------------------------ */ +void +ipf_nat_uncreate(fin) + fr_info_t *fin; +{ + ipf_main_softc_t *softc = fin->fin_main_soft; + ipf_nat_softc_t *softn = softc->ipf_nat_soft; + int nflags; + nat_t *nat; + + switch (fin->fin_p) + { + case IPPROTO_TCP : + nflags = IPN_TCP; + break; + case IPPROTO_UDP : + nflags = IPN_UDP; + break; + default : + nflags = 0; + break; + } + + WRITE_ENTER(&softc->ipf_nat); + + if (fin->fin_out == 0) { + nat = ipf_nat_outlookup(fin, nflags, (u_int)fin->fin_p, + fin->fin_dst, fin->fin_src); + } else { + nat = ipf_nat_inlookup(fin, nflags, (u_int)fin->fin_p, + fin->fin_src, fin->fin_dst); + } + + if (nat != NULL) { + NBUMPSIDE(fin->fin_out, ns_uncreate[0]); + ipf_nat_delete(softc, nat, NL_DESTROY); + } else { + NBUMPSIDE(fin->fin_out, ns_uncreate[1]); + } + + RWLOCK_EXIT(&softc->ipf_nat); +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_nat_cmp_rules */ +/* Returns: int - 0 == success, else rules do not match. */ +/* Parameters: n1(I) - first rule to compare */ +/* n2(I) - first rule to compare */ +/* */ +/* Compare two rules using pointers to each rule. A straight bcmp will not */ +/* work as some fields (such as in_dst, in_pkts) actually do change once */ +/* the rule has been loaded into the kernel. Whilst this function returns */ +/* various non-zero returns, they're strictly to aid in debugging. Use of */ +/* this function should simply care if the result is zero or not. */ +/* ------------------------------------------------------------------------ */ +static int +ipf_nat_cmp_rules(n1, n2) + ipnat_t *n1, *n2; +{ + if (n1->in_size != n2->in_size) + return 1; + + if (bcmp((char *)&n1->in_v, (char *)&n2->in_v, + offsetof(ipnat_t, in_ndst) - offsetof(ipnat_t, in_v)) != 0) + return 2; + + if (bcmp((char *)&n1->in_tuc, (char *)&n2->in_tuc, + n1->in_size - offsetof(ipnat_t, in_tuc)) != 0) + return 3; + if (n1->in_ndst.na_atype != n2->in_ndst.na_atype) + return 5; + if (n1->in_ndst.na_function != n2->in_ndst.na_function) + return 6; + if (bcmp((char *)&n1->in_ndst.na_addr, (char *)&n2->in_ndst.na_addr, + sizeof(n1->in_ndst.na_addr))) + return 7; + if (n1->in_nsrc.na_atype != n2->in_nsrc.na_atype) + return 8; + if (n1->in_nsrc.na_function != n2->in_nsrc.na_function) + return 9; + if (bcmp((char *)&n1->in_nsrc.na_addr, (char *)&n2->in_nsrc.na_addr, + sizeof(n1->in_nsrc.na_addr))) + return 10; + if (n1->in_odst.na_atype != n2->in_odst.na_atype) + return 11; + if (n1->in_odst.na_function != n2->in_odst.na_function) + return 12; + if (bcmp((char *)&n1->in_odst.na_addr, (char *)&n2->in_odst.na_addr, + sizeof(n1->in_odst.na_addr))) + return 13; + if (n1->in_osrc.na_atype != n2->in_osrc.na_atype) + return 14; + if (n1->in_osrc.na_function != n2->in_osrc.na_function) + return 15; + if (bcmp((char *)&n1->in_osrc.na_addr, (char *)&n2->in_osrc.na_addr, + sizeof(n1->in_osrc.na_addr))) + return 16; + return 0; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_nat_rule_init */ +/* Returns: int - 0 == success, else rules do not match. */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* softn(I) - pointer to NAT context structure */ +/* n(I) - first rule to compare */ +/* */ +/* ------------------------------------------------------------------------ */ +static int +ipf_nat_rule_init(softc, softn, n) + ipf_main_softc_t *softc; + ipf_nat_softc_t *softn; + ipnat_t *n; +{ + int error = 0; + + if ((n->in_flags & IPN_SIPRANGE) != 0) + n->in_nsrcatype = FRI_RANGE; + + if ((n->in_flags & IPN_DIPRANGE) != 0) + n->in_ndstatype = FRI_RANGE; + + if ((n->in_flags & IPN_SPLIT) != 0) + n->in_ndstatype = FRI_SPLIT; + + if ((n->in_redir & (NAT_MAP|NAT_REWRITE|NAT_DIVERTUDP)) != 0) + n->in_spnext = n->in_spmin; + + if ((n->in_redir & (NAT_REWRITE|NAT_DIVERTUDP)) != 0) { + n->in_dpnext = n->in_dpmin; + } else if (n->in_redir == NAT_REDIRECT) { + n->in_dpnext = n->in_dpmin; + } + + n->in_stepnext = 0; + + switch (n->in_v[0]) + { + case 4 : + error = ipf_nat_ruleaddrinit(softc, softn, n); + if (error != 0) + return error; + break; +#ifdef USE_INET6 + case 6 : + error = ipf_nat6_ruleaddrinit(softc, softn, n); + if (error != 0) + return error; + break; +#endif + default : + break; + } + + if (n->in_redir == (NAT_DIVERTUDP|NAT_MAP)) { + /* + * Prerecord whether or not the destination of the divert + * is local or not to the interface the packet is going + * to be sent out. + */ + n->in_dlocal = ipf_deliverlocal(softc, n->in_v[1], + n->in_ifps[1], &n->in_ndstip6); + } + + return error; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_nat_rule_fini */ +/* Returns: int - 0 == success, else rules do not match. */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* n(I) - rule to work on */ +/* */ +/* This function is used to release any objects that were referenced during */ +/* the rule initialisation. This is useful both when free'ing the rule and */ +/* when handling ioctls that need to initialise these fields but not */ +/* actually use them after the ioctl processing has finished. */ +/* ------------------------------------------------------------------------ */ +static void +ipf_nat_rule_fini(softc, n) + ipf_main_softc_t *softc; + ipnat_t *n; +{ + if (n->in_odst.na_atype == FRI_LOOKUP && n->in_odst.na_ptr != NULL) + ipf_lookup_deref(softc, n->in_odst.na_type, n->in_odst.na_ptr); + + if (n->in_osrc.na_atype == FRI_LOOKUP && n->in_osrc.na_ptr != NULL) + ipf_lookup_deref(softc, n->in_osrc.na_type, n->in_osrc.na_ptr); + + if (n->in_ndst.na_atype == FRI_LOOKUP && n->in_ndst.na_ptr != NULL) + ipf_lookup_deref(softc, n->in_ndst.na_type, n->in_ndst.na_ptr); + + if (n->in_nsrc.na_atype == FRI_LOOKUP && n->in_nsrc.na_ptr != NULL) + ipf_lookup_deref(softc, n->in_nsrc.na_type, n->in_nsrc.na_ptr); + + if (n->in_divmp != NULL) + FREE_MB_T(n->in_divmp); +} diff --git a/sys/contrib/ipfilter/netinet/ip_nat.h b/sys/contrib/ipfilter/netinet/ip_nat.h index c8581ef3d7c..a57bf0ce3ce 100644 --- a/sys/contrib/ipfilter/netinet/ip_nat.h +++ b/sys/contrib/ipfilter/netinet/ip_nat.h @@ -1,7 +1,7 @@ /* $FreeBSD$ */ /* - * Copyright (C) 1995-2001, 2003 by Darren Reed. + * Copyright (C) 2012 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. * @@ -22,11 +22,13 @@ #define SIOCRMNAT _IOW('r', 61, struct ipfobj) #define SIOCGNATS _IOWR('r', 62, struct ipfobj) #define SIOCGNATL _IOWR('r', 63, struct ipfobj) +#define SIOCPURGENAT _IOWR('r', 100, struct ipfobj) #else #define SIOCADNAT _IOW(r, 60, struct ipfobj) #define SIOCRMNAT _IOW(r, 61, struct ipfobj) #define SIOCGNATS _IOWR(r, 62, struct ipfobj) #define SIOCGNATL _IOWR(r, 63, struct ipfobj) +#define SIOCPURGENAT _IOWR(r, 100, struct ipfobj) #endif #undef LARGE_NAT /* define this if you're setting up a system to NAT @@ -78,13 +80,18 @@ #ifndef APR_LABELLEN #define APR_LABELLEN 16 #endif -#define NAT_HW_CKSUM 0x80000000 +#define NAT_HW_CKSUM 0x80000000 +#define NAT_HW_CKSUM_PART 0x40000000 #define DEF_NAT_AGE 1200 /* 10 minutes (600 seconds) */ struct ipstate; struct ap_session; +/* + * This structure is used in the active NAT table and represents an + * active NAT session. + */ typedef struct nat { ipfmutex_t nat_lock; struct nat *nat_next; @@ -101,13 +108,15 @@ typedef struct nat { void *nat_ifps[2]; void *nat_sync; ipftqent_t nat_tqe; + int nat_mtu[2]; u_32_t nat_flags; u_32_t nat_sumd[2]; /* ip checksum delta for data segment*/ u_32_t nat_ipsumd; /* ip checksum delta for ip header */ u_32_t nat_mssclamp; /* if != zero clamp MSS to this */ - i6addr_t nat_inip6; - i6addr_t nat_outip6; - i6addr_t nat_oip6; /* other ip */ + i6addr_t nat_odst6; + i6addr_t nat_osrc6; + i6addr_t nat_ndst6; + i6addr_t nat_nsrc6; U_QUAD_T nat_pkts[2]; U_QUAD_T nat_bytes[2]; union { @@ -115,28 +124,37 @@ typedef struct nat { tcpinfo_t nat_unt; icmpinfo_t nat_uni; greinfo_t nat_ugre; - } nat_un; - u_short nat_oport; /* other port */ - u_short nat_use; - u_char nat_p; /* protocol for NAT */ + } nat_unold, nat_unnew; + int nat_use; + int nat_pr[2]; /* protocol for NAT */ int nat_dir; int nat_ref; /* reference count */ - int nat_hv[2]; + u_int nat_hv[2]; char nat_ifnames[2][LIFNAMSIZ]; int nat_rev; /* 0 = forward, 1 = reverse */ - int nat_redir; /* copy of in_redir */ - u_32_t nat_seqnext[2]; + int nat_dlocal; + int nat_v[2]; /* 0 = old, 1 = new */ + u_int nat_redir; /* copy of in_redir */ } nat_t; -#define nat_inip nat_inip6.in4 -#define nat_outip nat_outip6.in4 -#define nat_oip nat_oip6.in4 +#define nat_osrcip nat_osrc6.in4 +#define nat_odstip nat_odst6.in4 +#define nat_nsrcip nat_nsrc6.in4 +#define nat_ndstip nat_ndst6.in4 +#define nat_osrcaddr nat_osrc6.in4.s_addr +#define nat_odstaddr nat_odst6.in4.s_addr +#define nat_nsrcaddr nat_nsrc6.in4.s_addr +#define nat_ndstaddr nat_ndst6.in4.s_addr #define nat_age nat_tqe.tqe_die -#define nat_inport nat_un.nat_unt.ts_sport -#define nat_outport nat_un.nat_unt.ts_dport -#define nat_type nat_un.nat_uni.ici_type -#define nat_seq nat_un.nat_uni.ici_seq -#define nat_id nat_un.nat_uni.ici_id +#define nat_osport nat_unold.nat_unt.ts_sport +#define nat_odport nat_unold.nat_unt.ts_dport +#define nat_nsport nat_unnew.nat_unt.ts_sport +#define nat_ndport nat_unnew.nat_unt.ts_dport +#define nat_oicmpid nat_unold.nat_uni.ici_id +#define nat_nicmpid nat_unnew.nat_uni.ici_id +#define nat_type nat_unold.nat_uni.ici_type +#define nat_oseq nat_unold.nat_uni.ici_seq +#define nat_nseq nat_unnew.nat_uni.ici_seq #define nat_tcpstate nat_tqe.tqe_state #define nat_die nat_tqe.tqe_die #define nat_touched nat_tqe.tqe_touched @@ -146,6 +164,10 @@ typedef struct nat { */ #define NAT_INBOUND 0 #define NAT_OUTBOUND 1 +#define NAT_ENCAPIN 2 +#define NAT_ENCAPOUT 3 +#define NAT_DIVERTIN 4 +#define NAT_DIVERTOUT 5 /* * Definitions for nat_flags @@ -174,9 +196,29 @@ typedef struct nat { #define NAT_DEBUG 0x800000 +typedef struct nat_addr_s { + i6addr_t na_addr[2]; + i6addr_t na_nextaddr; + int na_atype; + int na_function; +} nat_addr_t; + +#define na_nextip na_nextaddr.in4.s_addr +#define na_nextip6 na_nextaddr.in6 +#define na_num na_addr[0].iplookupnum +#define na_type na_addr[0].iplookuptype +#define na_subtype na_addr[0].iplookupsubtype +#define na_ptr na_addr[1].iplookupptr +#define na_func na_addr[1].iplookupfunc + + +/* + * This structure represents an actual NAT rule, loaded by ipnat. + */ typedef struct ipnat { ipfmutex_t in_lock; struct ipnat *in_next; /* NAT rule list next */ + struct ipnat **in_pnext; /* prior rdr next ptr */ struct ipnat *in_rnext; /* rdr rule hash next */ struct ipnat **in_prnext; /* prior rdr next ptr */ struct ipnat *in_mnext; /* map rule hash next */ @@ -185,49 +227,114 @@ typedef struct ipnat { void *in_ifps[2]; void *in_apr; char *in_comment; - i6addr_t in_next6; + mb_t *in_divmp; + void *in_pconf; + U_QUAD_T in_pkts[2]; + U_QUAD_T in_bytes[2]; u_long in_space; u_long in_hits; - u_int in_use; - u_int in_hv; + int in_size; + int in_use; + u_int in_hv[2]; int in_flineno; /* conf. file line number */ - u_short in_pnext; - u_char in_v; - u_char in_xxx; + int in_stepnext; + int in_dlocal; + u_short in_dpnext; + u_short in_spnext; /* From here to the end is covered by IPN_CMPSIZ */ + u_char in_v[2]; /* 0 = old, 1 = new */ u_32_t in_flags; u_32_t in_mssclamp; /* if != 0 clamp MSS to this */ u_int in_age[2]; int in_redir; /* see below for values */ - int in_p; /* protocol. */ - i6addr_t in_in[2]; - i6addr_t in_out[2]; - i6addr_t in_src[2]; + int in_pr[2]; /* protocol. */ + nat_addr_t in_ndst; + nat_addr_t in_nsrc; + nat_addr_t in_osrc; + nat_addr_t in_odst; frtuc_t in_tuc; - u_short in_port[2]; u_short in_ppip; /* ports per IP. */ u_short in_ippip; /* IP #'s per IP# */ - char in_ifnames[2][LIFNAMSIZ]; - char in_plabel[APR_LABELLEN]; /* proxy label. */ + u_short in_ndports[2]; + u_short in_nsports[2]; + int in_ifnames[2]; + int in_plabel; /* proxy label. */ + int in_pconfig; /* proxy label. */ ipftag_t in_tag; + int in_namelen; + char in_names[1]; } ipnat_t; -#define in_pmin in_port[0] /* Also holds static redir port */ -#define in_pmax in_port[1] -#define in_nextip in_next6.in4 -#define in_nip in_next6.in4.s_addr -#define in_inip in_in[0].in4.s_addr -#define in_inmsk in_in[1].in4.s_addr -#define in_outip in_out[0].in4.s_addr -#define in_outmsk in_out[1].in4.s_addr -#define in_srcip in_src[0].in4.s_addr -#define in_srcmsk in_src[1].in4.s_addr +/* + * MAP-IN MAP-OUT RDR-IN RDR-OUT + * osrc X == src == src X + * odst X == dst == dst X + * nsrc == dst X X == dst + * ndst == src X X == src + */ +#define in_dpmin in_ndports[0] /* Also holds static redir port */ +#define in_dpmax in_ndports[1] +#define in_spmin in_nsports[0] /* Also holds static redir port */ +#define in_spmax in_nsports[1] +#define in_ndport in_ndports[0] +#define in_nsport in_nsports[0] +#define in_dipnext in_ndst.na_nextaddr.in4 +#define in_dipnext6 in_ndst.na_nextaddr +#define in_dnip in_ndst.na_nextaddr.in4.s_addr +#define in_dnip6 in_ndst.na_nextaddr +#define in_sipnext in_nsrc.na_nextaddr.in4 +#define in_snip in_nsrc.na_nextaddr.in4.s_addr +#define in_snip6 in_nsrc.na_nextaddr +#define in_odstip in_odst.na_addr[0].in4 +#define in_odstip6 in_odst.na_addr[0] +#define in_odstaddr in_odst.na_addr[0].in4.s_addr +#define in_odstmsk in_odst.na_addr[1].in4.s_addr +#define in_odstmsk6 in_odst.na_addr[1] +#define in_odstatype in_odst.na_atype +#define in_osrcip in_osrc.na_addr[0].in4 +#define in_osrcip6 in_osrc.na_addr[0] +#define in_osrcaddr in_osrc.na_addr[0].in4.s_addr +#define in_osrcmsk in_osrc.na_addr[1].in4.s_addr +#define in_osrcmsk6 in_osrc.na_addr[1] +#define in_osrcatype in_osrc.na_atype +#define in_ndstip in_ndst.na_addr[0].in4 +#define in_ndstip6 in_ndst.na_addr[0] +#define in_ndstaddr in_ndst.na_addr[0].in4.s_addr +#define in_ndstmsk in_ndst.na_addr[1].in4.s_addr +#define in_ndstmsk6 in_ndst.na_addr[1] +#define in_ndstatype in_ndst.na_atype +#define in_ndstafunc in_ndst.na_function +#define in_nsrcip in_nsrc.na_addr[0].in4 +#define in_nsrcip6 in_nsrc.na_addr[0] +#define in_nsrcaddr in_nsrc.na_addr[0].in4.s_addr +#define in_nsrcmsk in_nsrc.na_addr[1].in4.s_addr +#define in_nsrcmsk6 in_nsrc.na_addr[1] +#define in_nsrcatype in_nsrc.na_atype +#define in_nsrcafunc in_nsrc.na_function #define in_scmp in_tuc.ftu_scmp #define in_dcmp in_tuc.ftu_dcmp #define in_stop in_tuc.ftu_stop #define in_dtop in_tuc.ftu_dtop -#define in_sport in_tuc.ftu_sport -#define in_dport in_tuc.ftu_dport +#define in_osport in_tuc.ftu_sport +#define in_odport in_tuc.ftu_dport +#define in_ndstnum in_ndst.na_addr[0].iplookupnum +#define in_ndsttype in_ndst.na_addr[0].iplookuptype +#define in_ndstptr in_ndst.na_addr[1].iplookupptr +#define in_ndstfunc in_ndst.na_addr[1].iplookupfunc +#define in_nsrcnum in_nsrc.na_addr[0].iplookupnum +#define in_nsrctype in_nsrc.na_addr[0].iplookuptype +#define in_nsrcptr in_nsrc.na_addr[1].iplookupptr +#define in_nsrcfunc in_nsrc.na_addr[1].iplookupfunc +#define in_odstnum in_odst.na_addr[0].iplookupnum +#define in_odsttype in_odst.na_addr[0].iplookuptype +#define in_odstptr in_odst.na_addr[1].iplookupptr +#define in_odstfunc in_odst.na_addr[1].iplookupfunc +#define in_osrcnum in_osrc.na_addr[0].iplookupnum +#define in_osrctype in_osrc.na_addr[0].iplookuptype +#define in_osrcptr in_osrc.na_addr[1].iplookupptr +#define in_osrcfunc in_osrc.na_addr[1].iplookupfunc +#define in_icmpidmin in_nsports[0] +#define in_icmpidmax in_nsports[1] /* * Bit definitions for in_flags @@ -242,25 +349,30 @@ typedef struct ipnat { #define IPN_TCPUDPICMPQ (IPN_TCP|IPN_UDP|IPN_ICMPQUERY) #define IPN_RF (IPN_TCPUDP|IPN_DELETE|IPN_ICMPERR) #define IPN_AUTOPORTMAP 0x00010 -#define IPN_IPRANGE 0x00020 -#define IPN_FILTER 0x00040 -#define IPN_SPLIT 0x00080 -#define IPN_ROUNDR 0x00100 -#define IPN_NOTSRC 0x04000 -#define IPN_NOTDST 0x08000 -#define IPN_DYNSRCIP 0x10000 /* dynamic src IP# */ -#define IPN_DYNDSTIP 0x20000 /* dynamic dst IP# */ -#define IPN_DELETE 0x40000 -#define IPN_STICKY 0x80000 -#define IPN_FRAG 0x100000 -#define IPN_FIXEDDPORT 0x200000 -#define IPN_FINDFORWARD 0x400000 -#define IPN_IN 0x800000 -#define IPN_SEQUENTIAL 0x1000000 -#define IPN_USERFLAGS (IPN_TCPUDP|IPN_AUTOPORTMAP|IPN_IPRANGE|IPN_SPLIT|\ - IPN_ROUNDR|IPN_FILTER|IPN_NOTSRC|IPN_NOTDST|\ +#define IPN_FILTER 0x00020 +#define IPN_SPLIT 0x00040 +#define IPN_ROUNDR 0x00080 +#define IPN_SIPRANGE 0x00100 +#define IPN_DIPRANGE 0x00200 +#define IPN_NOTSRC 0x00400 +#define IPN_NOTDST 0x00800 +#define IPN_NO 0x01000 +#define IPN_DYNSRCIP 0x02000 /* dynamic src IP# */ +#define IPN_DYNDSTIP 0x04000 /* dynamic dst IP# */ +#define IPN_DELETE 0x08000 +#define IPN_STICKY 0x10000 +#define IPN_FRAG 0x20000 +#define IPN_FIXEDSPORT 0x40000 +#define IPN_FIXEDDPORT 0x80000 +#define IPN_FINDFORWARD 0x100000 +#define IPN_IN 0x200000 +#define IPN_SEQUENTIAL 0x400000 +#define IPN_PURGE 0x800000 +#define IPN_PROXYRULE 0x1000000 +#define IPN_USERFLAGS (IPN_TCPUDP|IPN_AUTOPORTMAP|IPN_SIPRANGE|IPN_SPLIT|\ + IPN_ROUNDR|IPN_FILTER|IPN_NOTSRC|IPN_NOTDST|IPN_NO|\ IPN_FRAG|IPN_STICKY|IPN_FIXEDDPORT|IPN_ICMPQUERY|\ - IPN_SEQUENTIAL) + IPN_DIPRANGE|IPN_SEQUENTIAL|IPN_PURGE) /* * Values for in_redir @@ -269,22 +381,33 @@ typedef struct ipnat { #define NAT_REDIRECT 0x02 #define NAT_BIMAP (NAT_MAP|NAT_REDIRECT) #define NAT_MAPBLK 0x04 +#define NAT_REWRITE 0x08 +#define NAT_ENCAP 0x10 +#define NAT_DIVERTUDP 0x20 #define MAPBLK_MINPORT 1024 /* don't use reserved ports for src port */ #define USABLE_PORTS (65536 - MAPBLK_MINPORT) -#define IPN_CMPSIZ (sizeof(ipnat_t) - offsetof(ipnat_t, in_flags)) +#define IPN_CMPSIZ (sizeof(ipnat_t) - offsetof(ipnat_t, in_v)) typedef struct natlookup { - struct in_addr nl_inip; - struct in_addr nl_outip; - struct in_addr nl_realip; - int nl_flags; - u_short nl_inport; - u_short nl_outport; - u_short nl_realport; + i6addr_t nl_inipaddr; + i6addr_t nl_outipaddr; + i6addr_t nl_realipaddr; + int nl_v; + int nl_flags; + u_short nl_inport; + u_short nl_outport; + u_short nl_realport; } natlookup_t; +#define nl_inip nl_inipaddr.in4 +#define nl_outip nl_outipaddr.in4 +#define nl_realip nl_realipaddr.in4 +#define nl_inip6 nl_inipaddr.in6 +#define nl_outip6 nl_outipaddr.in6 +#define nl_realip6 nl_realipaddr.in6 + typedef struct nat_save { void *ipn_next; @@ -315,13 +438,25 @@ typedef struct hostmap { struct hostmap *hm_next; struct hostmap **hm_pnext; struct ipnat *hm_ipnat; - struct in_addr hm_srcip; - struct in_addr hm_dstip; - struct in_addr hm_mapip; + i6addr_t hm_osrcip6; + i6addr_t hm_odstip6; + i6addr_t hm_nsrcip6; + i6addr_t hm_ndstip6; u_32_t hm_port; int hm_ref; + int hm_hv; + int hm_v; } hostmap_t; +#define hm_osrcip hm_osrcip6.in4 +#define hm_odstip hm_odstip6.in4 +#define hm_nsrcip hm_nsrcip6.in4 +#define hm_ndstip hm_ndstip6.in4 +#define hm_osrc6 hm_osrcip6.in6 +#define hm_odst6 hm_odstip6.in6 +#define hm_nsrc6 hm_nsrcip6.in6 +#define hm_ndst6 hm_ndstip6.in6 + /* * Structure used to pass information in to nat_newmap and nat_newrdr. @@ -330,9 +465,7 @@ typedef struct natinfo { ipnat_t *nai_np; u_32_t nai_sum1; u_32_t nai_sum2; - u_32_t nai_nflags; - u_32_t nai_flags; - struct in_addr nai_ip; + struct in_addr nai_ip; /* In host byte order */ u_short nai_port; u_short nai_nport; u_short nai_sport; @@ -340,62 +473,134 @@ typedef struct natinfo { } natinfo_t; -typedef struct natstat { - u_long ns_mapped[2]; - u_long ns_rules; +typedef struct nat_stat_side { + u_int *ns_bucketlen; + nat_t **ns_table; u_long ns_added; - u_long ns_expire; - u_long ns_inuse; - u_long ns_logged; - u_long ns_logfail; - u_long ns_memfail; + u_long ns_appr_fail; u_long ns_badnat; - u_long ns_addtrpnt; - nat_t **ns_table[2]; - hostmap_t **ns_maptable; - ipnat_t *ns_list; - void *ns_apslist; - u_int ns_wilds; - u_int ns_nattab_sz; - u_int ns_nattab_max; - u_int ns_rultab_sz; - u_int ns_rdrtab_sz; - u_int ns_trpntab_sz; - u_int ns_hostmap_sz; - nat_t *ns_instances; - hostmap_t *ns_maplist; - u_long *ns_bucketlen[2]; - u_long ns_ticks; - u_int ns_orphans; + u_long ns_badnatnew; + u_long ns_badnextaddr; + u_long ns_bucket_max; + u_long ns_clone_nomem; + u_long ns_decap_bad; + u_long ns_decap_fail; + u_long ns_decap_pullup; + u_long ns_divert_dup; + u_long ns_divert_exist; + u_long ns_drop; + u_long ns_encap_dup; + u_long ns_encap_pullup; + u_long ns_exhausted; + u_long ns_icmp_address; + u_long ns_icmp_basic; + u_long ns_icmp_mbuf; + u_long ns_icmp_notfound; + u_long ns_icmp_rebuild; + u_long ns_icmp_short; + u_long ns_icmp_size; + u_long ns_ifpaddrfail; + u_long ns_ignored; + u_long ns_insert_fail; + u_long ns_inuse; + u_long ns_log; + u_long ns_lookup_miss; + u_long ns_lookup_nowild; + u_long ns_new_ifpaddr; + u_long ns_memfail; + u_long ns_table_max; + u_long ns_translated; + u_long ns_unfinalised; + u_long ns_wrap; + u_long ns_xlate_null; + u_long ns_xlate_exists; + u_long ns_ipf_proxy_fail; + u_long ns_uncreate[2]; +} nat_stat_side_t; + + +typedef struct natstat { + nat_t *ns_instances; + ipnat_t *ns_list; + hostmap_t *ns_maplist; + hostmap_t **ns_maptable; + u_int ns_active; + u_long ns_addtrpnt; + u_long ns_divert_build; + u_long ns_expire; + u_long ns_flush_all; + u_long ns_flush_closing; + u_long ns_flush_queue; + u_long ns_flush_state; + u_long ns_flush_timeout; + u_long ns_hm_new; + u_long ns_hm_newfail; + u_long ns_hm_addref; + u_long ns_hm_nullnp; + u_long ns_log_ok; + u_long ns_log_fail; + u_int ns_hostmap_sz; + u_int ns_nattab_sz; + u_int ns_nattab_max; + u_int ns_orphans; + u_int ns_rules; + u_int ns_rules_map; + u_int ns_rules_rdr; + u_int ns_rultab_sz; + u_int ns_rdrtab_sz; + u_32_t ns_ticks; + u_int ns_trpntab_sz; + u_int ns_wilds; + u_long ns_proto[256]; + nat_stat_side_t ns_side[2]; +#ifdef USE_INET6 + nat_stat_side_t ns_side6[2]; +#endif } natstat_t; typedef struct natlog { - struct in_addr nl_origip; - struct in_addr nl_outip; - struct in_addr nl_inip; - u_short nl_origport; - u_short nl_outport; - u_short nl_inport; - u_short nl_type; - int nl_rule; + i6addr_t nl_osrcip; + i6addr_t nl_odstip; + i6addr_t nl_nsrcip; + i6addr_t nl_ndstip; + u_short nl_osrcport; + u_short nl_odstport; + u_short nl_nsrcport; + u_short nl_ndstport; + int nl_action; + int nl_type; + int nl_rule; U_QUAD_T nl_pkts[2]; U_QUAD_T nl_bytes[2]; - u_char nl_p; + u_char nl_p[2]; + u_char nl_v[2]; + u_char nl_ifnames[2][LIFNAMSIZ]; } natlog_t; -#define NL_NEWMAP NAT_MAP -#define NL_NEWRDR NAT_REDIRECT -#define NL_NEWBIMAP NAT_BIMAP -#define NL_NEWBLOCK NAT_MAPBLK -#define NL_DESTROY 0xfffc -#define NL_CLONE 0xfffd +#define NL_NEW 0 +#define NL_CLONE 1 +#define NL_PURGE 0xfffc +#define NL_DESTROY 0xfffd #define NL_FLUSH 0xfffe #define NL_EXPIRE 0xffff -#define NAT_HASH_FN(k,l,m) (((k) + ((k) >> 12) + l) % (m)) +#define NAT_HASH_FN(_k,_l,_m) (((_k) + ((_k) >> 12) + _l) % (_m)) +#define NAT_HASH_FN6(_k,_l,_m) ((((u_32_t *)(_k))[3] \ + + (((u_32_t *)(_k))[3] >> 12) \ + + (((u_32_t *)(_k))[2]) \ + + (((u_32_t *)(_k))[2] >> 12) \ + + (((u_32_t *)(_k))[1]) \ + + (((u_32_t *)(_k))[1] >> 12) \ + + (((u_32_t *)(_k))[0]) \ + + (((u_32_t *)(_k))[0] >> 12) \ + + _l) % (_m)) -#define LONG_SUM(in) (((in) & 0xffff) + ((in) >> 16)) +#define LONG_SUM(_i) (((_i) & 0xffff) + ((_i) >> 16)) +#define LONG_SUM6(_i) (LONG_SUM(ntohl(((u_32_t *)(_i))[0])) + \ + LONG_SUM(ntohl(((u_32_t *)(_i))[1])) + \ + LONG_SUM(ntohl(((u_32_t *)(_i))[2])) + \ + LONG_SUM(ntohl(((u_32_t *)(_i))[3]))) #define CALC_SUMD(s1, s2, sd) { \ (s1) = ((s1) & 0xffff) + ((s1) >> 16); \ @@ -411,64 +616,159 @@ typedef struct natlog { #define NAT_SYSSPACE 0x80000000 #define NAT_LOCKHELD 0x40000000 +/* + * This is present in ip_nat.h because it needs to be shared between + * ip_nat.c and ip_nat6.c + */ +typedef struct ipf_nat_softc_s { + ipfmutex_t ipf_nat_new; + ipfmutex_t ipf_nat_io; + int ipf_nat_doflush; + int ipf_nat_logging; + int ipf_nat_lock; + int ipf_nat_inited; + int ipf_nat_table_wm_high; + int ipf_nat_table_wm_low; + u_int ipf_nat_table_max; + u_int ipf_nat_table_sz; + u_int ipf_nat_maprules_sz; + u_int ipf_nat_rdrrules_sz; + u_int ipf_nat_hostmap_sz; + u_int ipf_nat_maxbucket; + u_int ipf_nat_last_force_flush; + u_int ipf_nat_defage; + u_int ipf_nat_defipage; + u_int ipf_nat_deficmpage; + ipf_v4_masktab_t ipf_nat_map_mask; + ipf_v6_masktab_t ipf_nat6_map_mask; + ipf_v4_masktab_t ipf_nat_rdr_mask; + ipf_v6_masktab_t ipf_nat6_rdr_mask; + nat_t **ipf_nat_table[2]; + nat_t *ipf_nat_instances; + ipnat_t *ipf_nat_list; + ipnat_t **ipf_nat_list_tail; + ipnat_t **ipf_nat_map_rules; + ipnat_t **ipf_nat_rdr_rules; + ipftq_t *ipf_nat_utqe; + hostmap_t **ipf_hm_maptable ; + hostmap_t *ipf_hm_maplist ; + ipftuneable_t *ipf_nat_tune; + ipftq_t ipf_nat_udptq; + ipftq_t ipf_nat_udpacktq; + ipftq_t ipf_nat_icmptq; + ipftq_t ipf_nat_icmpacktq; + ipftq_t ipf_nat_iptq; + ipftq_t ipf_nat_pending; + ipftq_t ipf_nat_tcptq[IPF_TCP_NSTATES]; + natstat_t ipf_nat_stats; +} ipf_nat_softc_t ; -extern u_int ipf_nattable_sz; -extern u_int ipf_nattable_max; -extern u_int ipf_natrules_sz; -extern u_int ipf_rdrrules_sz; -extern u_int ipf_hostmap_sz; -extern u_int fr_nat_maxbucket; -extern u_int fr_nat_maxbucket_reset; -extern int fr_nat_lock; -extern int fr_nat_doflush; -extern void fr_natsync __P((void *)); -extern u_long fr_defnatage; -extern u_long fr_defnaticmpage; -extern u_long fr_defnatipage; - /* nat_table[0] -> hashed list sorted by inside (ip, port) */ - /* nat_table[1] -> hashed list sorted by outside (ip, port) */ -extern nat_t **nat_table[2]; -extern nat_t *nat_instances; -extern ipnat_t *nat_list; -extern ipnat_t **nat_rules; -extern ipnat_t **rdr_rules; -extern ipftq_t *nat_utqe; -extern natstat_t nat_stats; +#define ipf_nat_map_max ipf_nat_map_mask.imt4_max +#define ipf_nat_rdr_max ipf_nat_rdr_mask.imt4_max +#define ipf_nat6_map_max ipf_nat6_map_mask.imt6_max +#define ipf_nat6_rdr_max ipf_nat6_rdr_mask.imt6_max +#define ipf_nat_map_active_masks ipf_nat_map_mask.imt4_active +#define ipf_nat_rdr_active_masks ipf_nat_rdr_mask.imt4_active +#define ipf_nat6_map_active_masks ipf_nat6_map_mask.imt6_active +#define ipf_nat6_rdr_active_masks ipf_nat6_rdr_mask.imt6_active +extern frentry_t ipfnatblock; + +extern void ipf_fix_datacksum __P((u_short *, u_32_t)); +extern void ipf_fix_incksum __P((int, u_short *, u_32_t, u_32_t)); +extern void ipf_fix_outcksum __P((int, u_short *, u_32_t, u_32_t)); + +extern int ipf_nat_checkin __P((fr_info_t *, u_32_t *)); +extern int ipf_nat_checkout __P((fr_info_t *, u_32_t *)); +extern void ipf_nat_delete __P((ipf_main_softc_t *, struct nat *, int)); +extern void ipf_nat_deref __P((ipf_main_softc_t *, nat_t **)); +extern void ipf_nat_expire __P((ipf_main_softc_t *)); +extern int ipf_nat_hashtab_add __P((ipf_main_softc_t *, + ipf_nat_softc_t *, nat_t *)); +extern void ipf_nat_hostmapdel __P((ipf_main_softc_t *, hostmap_t **)); +extern int ipf_nat_hostmap_rehash __P((ipf_main_softc_t *, + ipftuneable_t *, ipftuneval_t *)); +extern nat_t *ipf_nat_icmperrorlookup __P((fr_info_t *, int)); +extern nat_t *ipf_nat_icmperror __P((fr_info_t *, u_int *, int)); #if defined(__OpenBSD__) -extern void nat_ifdetach __P((void *)); +extern void ipf_nat_ifdetach __P((void *)); #endif -extern int fr_nat_ioctl __P((caddr_t, ioctlcmd_t, int, int, void *)); -extern int fr_natinit __P((void)); -extern nat_t *nat_new __P((fr_info_t *, ipnat_t *, nat_t **, u_int, int)); -extern nat_t *nat_outlookup __P((fr_info_t *, u_int, u_int, struct in_addr, - struct in_addr)); -extern void fix_datacksum __P((u_short *, u_32_t)); -extern nat_t *nat_inlookup __P((fr_info_t *, u_int, u_int, struct in_addr, +extern int ipf_nat_init __P((void)); +extern nat_t *ipf_nat_inlookup __P((fr_info_t *, u_int, u_int, + struct in_addr, struct in_addr)); +extern int ipf_nat_in __P((fr_info_t *, nat_t *, int, u_32_t)); +extern int ipf_nat_insert __P((ipf_main_softc_t *, ipf_nat_softc_t *, + nat_t *)); +extern int ipf_nat_ioctl __P((ipf_main_softc_t *, caddr_t, ioctlcmd_t, + int, int, void *)); +extern void ipf_nat_log __P((ipf_main_softc_t *, ipf_nat_softc_t *, + struct nat *, u_int)); +extern nat_t *ipf_nat_lookupredir __P((natlookup_t *)); +extern nat_t *ipf_nat_maplookup __P((void *, u_int, struct in_addr, struct in_addr)); -extern nat_t *nat_tnlookup __P((fr_info_t *, int)); -extern nat_t *nat_maplookup __P((void *, u_int, struct in_addr, - struct in_addr)); -extern nat_t *nat_lookupredir __P((natlookup_t *)); -extern nat_t *nat_icmperrorlookup __P((fr_info_t *, int)); -extern nat_t *nat_icmperror __P((fr_info_t *, u_int *, int)); -extern void nat_delete __P((struct nat *, int)); -extern int nat_insert __P((nat_t *, int)); +extern nat_t *ipf_nat_add __P((fr_info_t *, ipnat_t *, nat_t **, + u_int, int)); +extern int ipf_nat_out __P((fr_info_t *, nat_t *, int, u_32_t)); +extern nat_t *ipf_nat_outlookup __P((fr_info_t *, u_int, u_int, + struct in_addr, struct in_addr)); +extern u_short *ipf_nat_proto __P((fr_info_t *, nat_t *, u_int)); +extern void ipf_nat_rule_deref __P((ipf_main_softc_t *, ipnat_t **)); +extern void ipf_nat_setqueue __P((ipf_main_softc_t *, ipf_nat_softc_t *, + nat_t *)); +extern void ipf_nat_setpending __P((ipf_main_softc_t *, nat_t *)); +extern nat_t *ipf_nat_tnlookup __P((fr_info_t *, int)); +extern void ipf_nat_update __P((fr_info_t *, nat_t *)); +extern int ipf_nat_rehash __P((ipf_main_softc_t *, ipftuneable_t *, + ipftuneval_t *)); +extern int ipf_nat_rehash_rules __P((ipf_main_softc_t *, ipftuneable_t *, + ipftuneval_t *)); +extern int ipf_nat_settimeout __P((struct ipf_main_softc_s *, + ipftuneable_t *, ipftuneval_t *)); +extern void ipf_nat_sync __P((ipf_main_softc_t *, void *)); + +extern nat_t *ipf_nat_clone __P((fr_info_t *, nat_t *)); +extern void ipf_nat_delmap __P((ipf_nat_softc_t *, ipnat_t *)); +extern void ipf_nat_delrdr __P((ipf_nat_softc_t *, ipnat_t *)); +extern int ipf_nat_wildok __P((nat_t *, int, int, int, int)); +extern void ipf_nat_setlock __P((void *, int)); +extern void ipf_nat_load __P((void)); +extern void *ipf_nat_soft_create __P((ipf_main_softc_t *)); +extern int ipf_nat_soft_init __P((ipf_main_softc_t *, void *)); +extern void ipf_nat_soft_destroy __P((ipf_main_softc_t *, void *)); +extern int ipf_nat_soft_fini __P((ipf_main_softc_t *, void *)); +extern int ipf_nat_main_load __P((void)); +extern int ipf_nat_main_unload __P((void)); +extern ipftq_t *ipf_nat_add_tq __P((ipf_main_softc_t *, int)); +extern void ipf_nat_uncreate __P((fr_info_t *)); + +#ifdef USE_INET6 +extern nat_t *ipf_nat6_add __P((fr_info_t *, ipnat_t *, nat_t **, + u_int, int)); +extern void ipf_nat6_addrdr __P((ipf_nat_softc_t *, ipnat_t *)); +extern void ipf_nat6_addmap __P((ipf_nat_softc_t *, ipnat_t *)); +extern void ipf_nat6_addencap __P((ipf_nat_softc_t *, ipnat_t *)); +extern int ipf_nat6_checkout __P((fr_info_t *, u_32_t *)); +extern int ipf_nat6_checkin __P((fr_info_t *, u_32_t *)); +extern void ipf_nat6_delmap __P((ipf_nat_softc_t *, ipnat_t *)); +extern void ipf_nat6_delrdr __P((ipf_nat_softc_t *, ipnat_t *)); +extern int ipf_nat6_finalise __P((fr_info_t *, nat_t *)); +extern nat_t *ipf_nat6_icmperror __P((fr_info_t *, u_int *, int)); +extern nat_t *ipf_nat6_icmperrorlookup __P((fr_info_t *, int)); +extern nat_t *ipf_nat6_inlookup __P((fr_info_t *, u_int, u_int, + struct in6_addr *, struct in6_addr *)); +extern u_32_t ipf_nat6_ip6subtract __P((i6addr_t *, i6addr_t *)); +extern frentry_t *ipf_nat6_ipfin __P((fr_info_t *, u_32_t *)); +extern frentry_t *ipf_nat6_ipfout __P((fr_info_t *, u_32_t *)); +extern nat_t *ipf_nat6_lookupredir __P((natlookup_t *)); +extern int ipf_nat6_newmap __P((fr_info_t *, nat_t *, natinfo_t *)); +extern int ipf_nat6_newrdr __P((fr_info_t *, nat_t *, natinfo_t *)); +extern nat_t *ipf_nat6_outlookup __P((fr_info_t *, u_int, u_int, + struct in6_addr *, struct in6_addr *)); +extern int ipf_nat6_newrewrite __P((fr_info_t *, nat_t *, natinfo_t *)); +extern int ipf_nat6_newdivert __P((fr_info_t *, nat_t *, natinfo_t *)); +extern int ipf_nat6_ruleaddrinit __P((ipf_main_softc_t *, ipf_nat_softc_t *, ipnat_t *)); + +#endif -extern int fr_checknatout __P((fr_info_t *, u_32_t *)); -extern int fr_natout __P((fr_info_t *, nat_t *, int, u_32_t)); -extern int fr_checknatin __P((fr_info_t *, u_32_t *)); -extern int fr_natin __P((fr_info_t *, nat_t *, int, u_32_t)); -extern void fr_natunload __P((void)); -extern void fr_natexpire __P((void)); -extern void nat_log __P((struct nat *, u_int)); -extern void fix_incksum __P((fr_info_t *, u_short *, u_32_t)); -extern void fix_outcksum __P((fr_info_t *, u_short *, u_32_t)); -extern void fr_ipnatderef __P((ipnat_t **)); -extern void fr_natderef __P((nat_t **)); -extern u_short *nat_proto __P((fr_info_t *, nat_t *, u_int)); -extern void nat_update __P((fr_info_t *, nat_t *, ipnat_t *)); -extern void fr_setnatqueue __P((nat_t *, int)); -extern void fr_hostmapdel __P((hostmap_t **)); #endif /* __IP_NAT_H__ */ diff --git a/sys/contrib/ipfilter/netinet/ip_nat6.c b/sys/contrib/ipfilter/netinet/ip_nat6.c new file mode 100644 index 00000000000..72931c9fca2 --- /dev/null +++ b/sys/contrib/ipfilter/netinet/ip_nat6.c @@ -0,0 +1,4098 @@ +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + */ +#if defined(KERNEL) || defined(_KERNEL) +# undef KERNEL +# undef _KERNEL +# define KERNEL 1 +# define _KERNEL 1 +#endif +#include +#include +#include +#include +#include +#if defined(_KERNEL) && defined(__NetBSD_Version__) && \ + (__NetBSD_Version__ >= 399002000) +# include +#endif +#if !defined(_KERNEL) +# include +# include +# include +# define _KERNEL +# ifdef ipf_nat6__OpenBSD__ +struct file; +# endif +# include +# undef _KERNEL +#endif +#if defined(_KERNEL) && (__FreeBSD_version >= 220000) +# include +# include +#else +# include +#endif +#if !defined(AIX) +# include +#endif +#if !defined(linux) +# include +#endif +#include +#if defined(_KERNEL) +# include +# if !defined(__SVR4) && !defined(__svr4__) +# include +# endif +#endif +#if defined(__SVR4) || defined(__svr4__) +# include +# include +# ifdef _KERNEL +# include +# endif +# include +# include +#endif +#if __FreeBSD_version >= 300000 +# include +#endif +#include +#if __FreeBSD_version >= 300000 +# include +#endif +#ifdef sun +# include +#endif +#include +#include +#include +#include + +#ifdef RFC1825 +# include +# include +extern struct ifnet vpnif; +#endif + +#if !defined(linux) +# include +#endif +#include +#include +#include +#include "netinet/ip_compat.h" +#include +#include "netinet/ip_fil.h" +#include "netinet/ip_nat.h" +#include "netinet/ip_frag.h" +#include "netinet/ip_state.h" +#include "netinet/ip_proxy.h" +#include "netinet/ip_lookup.h" +#include "netinet/ip_dstlist.h" +#include "netinet/ip_sync.h" +#if (__FreeBSD_version >= 300000) +# include +#endif +#ifdef HAS_SYS_MD5_H +# include +#else +# include "md5.h" +#endif +/* END OF INCLUDES */ + +#undef SOCKADDR_IN +#define SOCKADDR_IN struct sockaddr_in + +#if !defined(lint) +static const char rcsid[] = "@(#)$Id: ip_nat6.c,v 1.22.2.20 2012/07/22 08:04:23 darren_r Exp $"; +#endif + +#ifdef USE_INET6 +static struct hostmap *ipf_nat6_hostmap __P((ipf_nat_softc_t *, ipnat_t *, + i6addr_t *, i6addr_t *, + i6addr_t *, u_32_t)); +static int ipf_nat6_match __P((fr_info_t *, ipnat_t *)); +static void ipf_nat6_tabmove __P((ipf_nat_softc_t *, nat_t *)); +static int ipf_nat6_decap __P((fr_info_t *, nat_t *)); +static int ipf_nat6_nextaddr __P((fr_info_t *, nat_addr_t *, i6addr_t *, + i6addr_t *)); +static int ipf_nat6_icmpquerytype __P((int)); +static int ipf_nat6_out __P((fr_info_t *, nat_t *, int, u_32_t)); +static int ipf_nat6_in __P((fr_info_t *, nat_t *, int, u_32_t)); +static int ipf_nat6_builddivertmp __P((ipf_nat_softc_t *, ipnat_t *)); +static int ipf_nat6_nextaddrinit __P((ipf_main_softc_t *, char *, + nat_addr_t *, int, void *)); +static int ipf_nat6_insert __P((ipf_main_softc_t *, ipf_nat_softc_t *, + nat_t *)); + + +#define NINCLSIDE6(y,x) ATOMIC_INCL(softn->ipf_nat_stats.ns_side6[y].x) +#define NBUMPSIDE(y,x) softn->ipf_nat_stats.ns_side[y].x++ +#define NBUMPSIDE6(y,x) softn->ipf_nat_stats.ns_side6[y].x++ +#define NBUMPSIDE6D(y,x) \ + do { \ + softn->ipf_nat_stats.ns_side6[y].x++; \ + DT(x); \ + } while (0) +#define NBUMPSIDE6DX(y,x,z) \ + do { \ + softn->ipf_nat_stats.ns_side6[y].x++; \ + DT(z); \ + } while (0) + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_nat6_ruleaddrinit */ +/* Returns: int - 0 == success, else failure */ +/* Parameters: in(I) - NAT rule that requires address fields to be init'd */ +/* */ +/* For each of the source/destination address fields in a NAT rule, call */ +/* ipf_nat6_nextaddrinit() to prepare the structure for active duty. Other */ +/* IPv6 specific actions can also be taken care of here. */ +/* ------------------------------------------------------------------------ */ +int +ipf_nat6_ruleaddrinit(softc, softn, n) + ipf_main_softc_t *softc; + ipf_nat_softc_t *softn; + ipnat_t *n; +{ + int idx, error; + + if (n->in_redir == NAT_BIMAP) { + n->in_ndstip6 = n->in_osrcip6; + n->in_ndstmsk6 = n->in_osrcmsk6; + n->in_odstip6 = n->in_nsrcip6; + n->in_odstmsk6 = n->in_nsrcmsk6; + + } + + if (n->in_redir & NAT_REDIRECT) + idx = 1; + else + idx = 0; + /* + * Initialise all of the address fields. + */ + error = ipf_nat6_nextaddrinit(softc, n->in_names, &n->in_osrc, 1, + n->in_ifps[idx]); + if (error != 0) + return error; + + error = ipf_nat6_nextaddrinit(softc, n->in_names, &n->in_odst, 1, + n->in_ifps[idx]); + if (error != 0) + return error; + + error = ipf_nat6_nextaddrinit(softc, n->in_names, &n->in_nsrc, 1, + n->in_ifps[idx]); + if (error != 0) + return error; + + error = ipf_nat6_nextaddrinit(softc, n->in_names, &n->in_ndst, 1, + n->in_ifps[idx]); + if (error != 0) + return error; + + if (n->in_redir & NAT_DIVERTUDP) + ipf_nat6_builddivertmp(softn, n); + return 0; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_nat6_addrdr */ +/* Returns: Nil */ +/* Parameters: n(I) - pointer to NAT rule to add */ +/* */ +/* Adds a redirect rule to the hash table of redirect rules and the list of */ +/* loaded NAT rules. Updates the bitmask indicating which netmasks are in */ +/* use by redirect rules. */ +/* ------------------------------------------------------------------------ */ +void +ipf_nat6_addrdr(softn, n) + ipf_nat_softc_t *softn; + ipnat_t *n; +{ + i6addr_t *mask; + ipnat_t **np; + i6addr_t j; + u_int hv; + int k; + + if ((n->in_redir & NAT_BIMAP) == NAT_BIMAP) { + k = count6bits(n->in_nsrcmsk6.i6); + mask = &n->in_nsrcmsk6; + IP6_AND(&n->in_odstip6, &n->in_odstmsk6, &j); + hv = NAT_HASH_FN6(&j, 0, softn->ipf_nat_rdrrules_sz); + + } else if (n->in_odstatype == FRI_NORMAL) { + k = count6bits(n->in_odstmsk6.i6); + mask = &n->in_odstmsk6; + IP6_AND(&n->in_odstip6, &n->in_odstmsk6, &j); + hv = NAT_HASH_FN6(&j, 0, softn->ipf_nat_rdrrules_sz); + } else { + k = 0; + hv = 0; + mask = NULL; + } + ipf_inet6_mask_add(k, mask, &softn->ipf_nat6_rdr_mask); + + np = softn->ipf_nat_rdr_rules + hv; + while (*np != NULL) + np = &(*np)->in_rnext; + n->in_rnext = NULL; + n->in_prnext = np; + n->in_hv[0] = hv; + n->in_use++; + *np = n; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_nat6_addmap */ +/* Returns: Nil */ +/* Parameters: n(I) - pointer to NAT rule to add */ +/* */ +/* Adds a NAT map rule to the hash table of rules and the list of loaded */ +/* NAT rules. Updates the bitmask indicating which netmasks are in use by */ +/* redirect rules. */ +/* ------------------------------------------------------------------------ */ +void +ipf_nat6_addmap(softn, n) + ipf_nat_softc_t *softn; + ipnat_t *n; +{ + i6addr_t *mask; + ipnat_t **np; + i6addr_t j; + u_int hv; + int k; + + if (n->in_osrcatype == FRI_NORMAL) { + k = count6bits(n->in_osrcmsk6.i6); + mask = &n->in_osrcmsk6; + IP6_AND(&n->in_osrcip6, &n->in_osrcmsk6, &j); + hv = NAT_HASH_FN6(&j, 0, softn->ipf_nat_maprules_sz); + } else { + k = 0; + hv = 0; + mask = NULL; + } + ipf_inet6_mask_add(k, mask, &softn->ipf_nat6_map_mask); + + np = softn->ipf_nat_map_rules + hv; + while (*np != NULL) + np = &(*np)->in_mnext; + n->in_mnext = NULL; + n->in_pmnext = np; + n->in_hv[1] = hv; + n->in_use++; + *np = n; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_nat6_del_rdr */ +/* Returns: Nil */ +/* Parameters: n(I) - pointer to NAT rule to delete */ +/* */ +/* Removes a NAT rdr rule from the hash table of NAT rdr rules. */ +/* ------------------------------------------------------------------------ */ +void +ipf_nat6_delrdr(softn, n) + ipf_nat_softc_t *softn; + ipnat_t *n; +{ + i6addr_t *mask; + int k; + + if ((n->in_redir & NAT_BIMAP) == NAT_BIMAP) { + k = count6bits(n->in_nsrcmsk6.i6); + mask = &n->in_nsrcmsk6; + } else if (n->in_odstatype == FRI_NORMAL) { + k = count6bits(n->in_odstmsk6.i6); + mask = &n->in_odstmsk6; + } else { + k = 0; + mask = NULL; + } + ipf_inet6_mask_del(k, mask, &softn->ipf_nat6_rdr_mask); + + if (n->in_rnext != NULL) + n->in_rnext->in_prnext = n->in_prnext; + *n->in_prnext = n->in_rnext; + n->in_use--; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_nat6_delmap */ +/* Returns: Nil */ +/* Parameters: n(I) - pointer to NAT rule to delete */ +/* */ +/* Removes a NAT map rule from the hash table of NAT map rules. */ +/* ------------------------------------------------------------------------ */ +void +ipf_nat6_delmap(softn, n) + ipf_nat_softc_t *softn; + ipnat_t *n; +{ + i6addr_t *mask; + int k; + + if (n->in_osrcatype == FRI_NORMAL) { + k = count6bits(n->in_osrcmsk6.i6); + mask = &n->in_osrcmsk6; + } else { + k = 0; + mask = NULL; + } + ipf_inet6_mask_del(k, mask, &softn->ipf_nat6_map_mask); + + if (n->in_mnext != NULL) + n->in_mnext->in_pmnext = n->in_pmnext; + *n->in_pmnext = n->in_mnext; + n->in_use--; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_nat6_hostmap */ +/* Returns: struct hostmap* - NULL if no hostmap could be created, */ +/* else a pointer to the hostmapping to use */ +/* Parameters: np(I) - pointer to NAT rule */ +/* real(I) - real IP address */ +/* map(I) - mapped IP address */ +/* port(I) - destination port number */ +/* Write Locks: ipf_nat */ +/* */ +/* Check if an ip address has already been allocated for a given mapping */ +/* that is not doing port based translation. If is not yet allocated, then */ +/* create a new entry if a non-NULL NAT rule pointer has been supplied. */ +/* ------------------------------------------------------------------------ */ +static struct hostmap * +ipf_nat6_hostmap(softn, np, src, dst, map, port) + ipf_nat_softc_t *softn; + ipnat_t *np; + i6addr_t *src, *dst, *map; + u_32_t port; +{ + hostmap_t *hm; + u_int hv; + + hv = (src->i6[3] ^ dst->i6[3]); + hv += (src->i6[2] ^ dst->i6[2]); + hv += (src->i6[1] ^ dst->i6[1]); + hv += (src->i6[0] ^ dst->i6[0]); + hv += src->i6[3]; + hv += src->i6[2]; + hv += src->i6[1]; + hv += src->i6[0]; + hv += dst->i6[3]; + hv += dst->i6[2]; + hv += dst->i6[1]; + hv += dst->i6[0]; + hv %= HOSTMAP_SIZE; + for (hm = softn->ipf_hm_maptable[hv]; hm; hm = hm->hm_next) + if (IP6_EQ(&hm->hm_osrc6, src) && + IP6_EQ(&hm->hm_odst6, dst) && + ((np == NULL) || (np == hm->hm_ipnat)) && + ((port == 0) || (port == hm->hm_port))) { + softn->ipf_nat_stats.ns_hm_addref++; + hm->hm_ref++; + return hm; + } + + if (np == NULL) { + softn->ipf_nat_stats.ns_hm_nullnp++; + return NULL; + } + + KMALLOC(hm, hostmap_t *); + if (hm) { + hm->hm_next = softn->ipf_hm_maplist; + hm->hm_pnext = &softn->ipf_hm_maplist; + if (softn->ipf_hm_maplist != NULL) + softn->ipf_hm_maplist->hm_pnext = &hm->hm_next; + softn->ipf_hm_maplist = hm; + hm->hm_hnext = softn->ipf_hm_maptable[hv]; + hm->hm_phnext = softn->ipf_hm_maptable + hv; + if (softn->ipf_hm_maptable[hv] != NULL) + softn->ipf_hm_maptable[hv]->hm_phnext = &hm->hm_hnext; + softn->ipf_hm_maptable[hv] = hm; + hm->hm_ipnat = np; + np->in_use++; + hm->hm_osrcip6 = *src; + hm->hm_odstip6 = *dst; + hm->hm_nsrcip6 = *map; + hm->hm_ndstip6.i6[0] = 0; + hm->hm_ndstip6.i6[1] = 0; + hm->hm_ndstip6.i6[2] = 0; + hm->hm_ndstip6.i6[3] = 0; + hm->hm_ref = 1; + hm->hm_port = port; + hm->hm_hv = hv; + hm->hm_v = 6; + softn->ipf_nat_stats.ns_hm_new++; + } else { + softn->ipf_nat_stats.ns_hm_newfail++; + } + return hm; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_nat6_newmap */ +/* Returns: int - -1 == error, 0 == success */ +/* Parameters: fin(I) - pointer to packet information */ +/* nat(I) - pointer to NAT entry */ +/* ni(I) - pointer to structure with misc. information needed */ +/* to create new NAT entry. */ +/* */ +/* Given an empty NAT structure, populate it with new information about a */ +/* new NAT session, as defined by the matching NAT rule. */ +/* ni.nai_ip is passed in uninitialised and must be set, in host byte order,*/ +/* to the new IP address for the translation. */ +/* ------------------------------------------------------------------------ */ +int +ipf_nat6_newmap(fin, nat, ni) + fr_info_t *fin; + nat_t *nat; + natinfo_t *ni; +{ + ipf_main_softc_t *softc = fin->fin_main_soft; + ipf_nat_softc_t *softn = softc->ipf_nat_soft; + u_short st_port, dport, sport, port, sp, dp; + i6addr_t in, st_ip; + hostmap_t *hm; + u_32_t flags; + ipnat_t *np; + nat_t *natl; + int l; + + /* + * If it's an outbound packet which doesn't match any existing + * record, then create a new port + */ + l = 0; + hm = NULL; + np = ni->nai_np; + st_ip = np->in_snip6; + st_port = np->in_spnext; + flags = nat->nat_flags; + + if (flags & IPN_ICMPQUERY) { + sport = fin->fin_data[1]; + dport = 0; + } else { + sport = htons(fin->fin_data[0]); + dport = htons(fin->fin_data[1]); + } + + /* + * Do a loop until we either run out of entries to try or we find + * a NAT mapping that isn't currently being used. This is done + * because the change to the source is not (usually) being fixed. + */ + do { + port = 0; + in = np->in_nsrc.na_nextaddr; + if (l == 0) { + /* + * Check to see if there is an existing NAT + * setup for this IP address pair. + */ + hm = ipf_nat6_hostmap(softn, np, &fin->fin_src6, + &fin->fin_dst6, &in, 0); + if (hm != NULL) + in = hm->hm_nsrcip6; + } else if ((l == 1) && (hm != NULL)) { + ipf_nat_hostmapdel(softc, &hm); + } + + nat->nat_hm = hm; + + if (IP6_ISONES(&np->in_nsrcmsk6) && (np->in_spnext == 0)) { + if (l > 0) { + NBUMPSIDE6DX(1, ns_exhausted, ns_exhausted_1); + return -1; + } + } + + if ((np->in_redir == NAT_BIMAP) && + IP6_EQ(&np->in_osrcmsk6, &np->in_nsrcmsk6)) { + i6addr_t temp; + /* + * map the address block in a 1:1 fashion + */ + temp.i6[0] = fin->fin_src6.i6[0] & + ~np->in_osrcmsk6.i6[0]; + temp.i6[1] = fin->fin_src6.i6[1] & + ~np->in_osrcmsk6.i6[1]; + temp.i6[2] = fin->fin_src6.i6[2] & + ~np->in_osrcmsk6.i6[0]; + temp.i6[3] = fin->fin_src6.i6[3] & + ~np->in_osrcmsk6.i6[3]; + in = np->in_nsrcip6; + IP6_MERGE(&in, &temp, &np->in_osrc); + +#ifdef NEED_128BIT_MATH + } else if (np->in_redir & NAT_MAPBLK) { + if ((l >= np->in_ppip) || ((l > 0) && + !(flags & IPN_TCPUDP))) { + NBUMPSIDE6DX(1, ns_exhausted, ns_exhausted_2); + return -1; + } + /* + * map-block - Calculate destination address. + */ + IP6_MASK(&in, &fin->fin_src6, &np->in_osrcmsk6); + in = ntohl(in); + inb = in; + in.s_addr /= np->in_ippip; + in.s_addr &= ntohl(~np->in_nsrcmsk6); + in.s_addr += ntohl(np->in_nsrcaddr6); + /* + * Calculate destination port. + */ + if ((flags & IPN_TCPUDP) && + (np->in_ppip != 0)) { + port = ntohs(sport) + l; + port %= np->in_ppip; + port += np->in_ppip * + (inb.s_addr % np->in_ippip); + port += MAPBLK_MINPORT; + port = htons(port); + } +#endif + + } else if (IP6_ISZERO(&np->in_nsrcaddr) && + IP6_ISONES(&np->in_nsrcmsk)) { + /* + * 0/32 - use the interface's IP address. + */ + if ((l > 0) || + ipf_ifpaddr(softc, 6, FRI_NORMAL, fin->fin_ifp, + &in, NULL) == -1) { + NBUMPSIDE6DX(1, ns_new_ifpaddr, + ns_new_ifpaddr_1); + return -1; + } + + } else if (IP6_ISZERO(&np->in_nsrcip6) && + IP6_ISZERO(&np->in_nsrcmsk6)) { + /* + * 0/0 - use the original source address/port. + */ + if (l > 0) { + NBUMPSIDE6DX(1, ns_exhausted, ns_exhausted_3); + return -1; + } + in = fin->fin_src6; + + } else if (!IP6_ISONES(&np->in_nsrcmsk6) && + (np->in_spnext == 0) && ((l > 0) || (hm == NULL))) { + IP6_INC(&np->in_snip6); + } + + natl = NULL; + + if ((flags & IPN_TCPUDP) && + ((np->in_redir & NAT_MAPBLK) == 0) && + (np->in_flags & IPN_AUTOPORTMAP)) { +#ifdef NEED_128BIT_MATH + /* + * "ports auto" (without map-block) + */ + if ((l > 0) && (l % np->in_ppip == 0)) { + if ((l > np->in_ppip) && + !IP6_ISONES(&np->in_nsrcmsk)) { + IP6_INC(&np->in_snip6) + } + } + if (np->in_ppip != 0) { + port = ntohs(sport); + port += (l % np->in_ppip); + port %= np->in_ppip; + port += np->in_ppip * + (ntohl(fin->fin_src6) % + np->in_ippip); + port += MAPBLK_MINPORT; + port = htons(port); + } +#endif + + } else if (((np->in_redir & NAT_MAPBLK) == 0) && + (flags & IPN_TCPUDPICMP) && (np->in_spnext != 0)) { + /* + * Standard port translation. Select next port. + */ + if (np->in_flags & IPN_SEQUENTIAL) { + port = np->in_spnext; + } else { + port = ipf_random() % (np->in_spmax - + np->in_spmin + 1); + port += np->in_spmin; + } + port = htons(port); + np->in_spnext++; + + if (np->in_spnext > np->in_spmax) { + np->in_spnext = np->in_spmin; + if (!IP6_ISONES(&np->in_nsrcmsk6)) { + IP6_INC(&np->in_snip6); + } + } + } + + if (np->in_flags & IPN_SIPRANGE) { + if (IP6_GT(&np->in_snip, &np->in_nsrcmsk)) + np->in_snip6 = np->in_nsrcip6; + } else { + i6addr_t a1, a2; + + a1 = np->in_snip6; + IP6_INC(&a1); + IP6_AND(&a1, &np->in_nsrcmsk6, &a2); + + if (!IP6_ISONES(&np->in_nsrcmsk6) && + IP6_GT(&a2, &np->in_nsrcip6)) { + IP6_ADD(&np->in_nsrcip6, 1, &np->in_snip6); + } + } + + if ((port == 0) && (flags & (IPN_TCPUDPICMP|IPN_ICMPQUERY))) + port = sport; + + /* + * Here we do a lookup of the connection as seen from + * the outside. If an IP# pair already exists, try + * again. So if you have A->B becomes C->B, you can + * also have D->E become C->E but not D->B causing + * another C->B. Also take protocol and ports into + * account when determining whether a pre-existing + * NAT setup will cause an external conflict where + * this is appropriate. + */ + sp = fin->fin_data[0]; + dp = fin->fin_data[1]; + fin->fin_data[0] = fin->fin_data[1]; + fin->fin_data[1] = ntohs(port); + natl = ipf_nat6_inlookup(fin, flags & ~(SI_WILDP|NAT_SEARCH), + (u_int)fin->fin_p, &fin->fin_dst6.in6, + &in.in6); + fin->fin_data[0] = sp; + fin->fin_data[1] = dp; + + /* + * Has the search wrapped around and come back to the + * start ? + */ + if ((natl != NULL) && + (np->in_spnext != 0) && (st_port == np->in_spnext) && + (!IP6_ISZERO(&np->in_snip6) && + IP6_EQ(&st_ip, &np->in_snip6))) { + NBUMPSIDE6D(1, ns_wrap); + return -1; + } + l++; + } while (natl != NULL); + + /* Setup the NAT table */ + nat->nat_osrc6 = fin->fin_src6; + nat->nat_nsrc6 = in; + nat->nat_odst6 = fin->fin_dst6; + nat->nat_ndst6 = fin->fin_dst6; + if (nat->nat_hm == NULL) + nat->nat_hm = ipf_nat6_hostmap(softn, np, &fin->fin_src6, + &fin->fin_dst6, + &nat->nat_nsrc6, 0); + + if (flags & IPN_TCPUDP) { + nat->nat_osport = sport; + nat->nat_nsport = port; /* sport */ + nat->nat_odport = dport; + nat->nat_ndport = dport; + ((tcphdr_t *)fin->fin_dp)->th_sport = port; + } else if (flags & IPN_ICMPQUERY) { + nat->nat_oicmpid = fin->fin_data[1]; + ((struct icmp6_hdr *)fin->fin_dp)->icmp6_id = port; + nat->nat_nicmpid = port; + } + return 0; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_nat6_newrdr */ +/* Returns: int - -1 == error, 0 == success (no move), 1 == success and */ +/* allow rule to be moved if IPN_ROUNDR is set. */ +/* Parameters: fin(I) - pointer to packet information */ +/* nat(I) - pointer to NAT entry */ +/* ni(I) - pointer to structure with misc. information needed */ +/* to create new NAT entry. */ +/* */ +/* ni.nai_ip is passed in uninitialised and must be set, in host byte order,*/ +/* to the new IP address for the translation. */ +/* ------------------------------------------------------------------------ */ +int +ipf_nat6_newrdr(fin, nat, ni) + fr_info_t *fin; + nat_t *nat; + natinfo_t *ni; +{ + ipf_main_softc_t *softc = fin->fin_main_soft; + ipf_nat_softc_t *softn = softc->ipf_nat_soft; + u_short nport, dport, sport; + u_short sp, dp; + hostmap_t *hm; + u_32_t flags; + i6addr_t in; + ipnat_t *np; + nat_t *natl; + int move; + + move = 1; + hm = NULL; + in.i6[0] = 0; + in.i6[1] = 0; + in.i6[2] = 0; + in.i6[3] = 0; + np = ni->nai_np; + flags = nat->nat_flags; + + if (flags & IPN_ICMPQUERY) { + dport = fin->fin_data[1]; + sport = 0; + } else { + sport = htons(fin->fin_data[0]); + dport = htons(fin->fin_data[1]); + } + + /* TRACE sport, dport */ + + + /* + * If the matching rule has IPN_STICKY set, then we want to have the + * same rule kick in as before. Why would this happen? If you have + * a collection of rdr rules with "round-robin sticky", the current + * packet might match a different one to the previous connection but + * we want the same destination to be used. + */ + if (((np->in_flags & (IPN_ROUNDR|IPN_SPLIT)) != 0) && + ((np->in_flags & IPN_STICKY) != 0)) { + hm = ipf_nat6_hostmap(softn, NULL, &fin->fin_src6, + &fin->fin_dst6, &in, (u_32_t)dport); + if (hm != NULL) { + in = hm->hm_ndstip6; + np = hm->hm_ipnat; + ni->nai_np = np; + move = 0; + } + } + + /* + * Otherwise, it's an inbound packet. Most likely, we don't + * want to rewrite source ports and source addresses. Instead, + * we want to rewrite to a fixed internal address and fixed + * internal port. + */ + if (np->in_flags & IPN_SPLIT) { + in = np->in_dnip6; + + if ((np->in_flags & (IPN_ROUNDR|IPN_STICKY)) == IPN_STICKY) { + hm = ipf_nat6_hostmap(softn, NULL, &fin->fin_src6, + &fin->fin_dst6, &in, + (u_32_t)dport); + if (hm != NULL) { + in = hm->hm_ndstip6; + move = 0; + } + } + + if (hm == NULL || hm->hm_ref == 1) { + if (IP6_EQ(&np->in_ndstip6, &in)) { + np->in_dnip6 = np->in_ndstmsk6; + move = 0; + } else { + np->in_dnip6 = np->in_ndstip6; + } + } + + } else if (IP6_ISZERO(&np->in_ndstaddr) && + IP6_ISONES(&np->in_ndstmsk)) { + /* + * 0/32 - use the interface's IP address. + */ + if (ipf_ifpaddr(softc, 6, FRI_NORMAL, fin->fin_ifp, + &in, NULL) == -1) { + NBUMPSIDE6DX(0, ns_new_ifpaddr, ns_new_ifpaddr_2); + return -1; + } + + } else if (IP6_ISZERO(&np->in_ndstip6) && + IP6_ISZERO(&np->in_ndstmsk6)) { + /* + * 0/0 - use the original destination address/port. + */ + in = fin->fin_dst6; + + } else if (np->in_redir == NAT_BIMAP && + IP6_EQ(&np->in_ndstmsk6, &np->in_odstmsk6)) { + i6addr_t temp; + /* + * map the address block in a 1:1 fashion + */ + temp.i6[0] = fin->fin_dst6.i6[0] & ~np->in_osrcmsk6.i6[0]; + temp.i6[1] = fin->fin_dst6.i6[1] & ~np->in_osrcmsk6.i6[1]; + temp.i6[2] = fin->fin_dst6.i6[2] & ~np->in_osrcmsk6.i6[0]; + temp.i6[3] = fin->fin_dst6.i6[3] & ~np->in_osrcmsk6.i6[3]; + in = np->in_ndstip6; + IP6_MERGE(&in, &temp, &np->in_ndstmsk6); + } else { + in = np->in_ndstip6; + } + + if ((np->in_dpnext == 0) || ((flags & NAT_NOTRULEPORT) != 0)) + nport = dport; + else { + /* + * Whilst not optimized for the case where + * pmin == pmax, the gain is not significant. + */ + if (((np->in_flags & IPN_FIXEDDPORT) == 0) && + (np->in_odport != np->in_dtop)) { + nport = ntohs(dport) - np->in_odport + np->in_dpmax; + nport = htons(nport); + } else { + nport = htons(np->in_dpnext); + np->in_dpnext++; + if (np->in_dpnext > np->in_dpmax) + np->in_dpnext = np->in_dpmin; + } + } + + /* + * When the redirect-to address is set to 0.0.0.0, just + * assume a blank `forwarding' of the packet. We don't + * setup any translation for this either. + */ + if (IP6_ISZERO(&in)) { + if (nport == dport) { + NBUMPSIDE6D(0, ns_xlate_null); + return -1; + } + in = fin->fin_dst6; + } + + /* + * Check to see if this redirect mapping already exists and if + * it does, return "failure" (allowing it to be created will just + * cause one or both of these "connections" to stop working.) + */ + sp = fin->fin_data[0]; + dp = fin->fin_data[1]; + fin->fin_data[1] = fin->fin_data[0]; + fin->fin_data[0] = ntohs(nport); + natl = ipf_nat6_outlookup(fin, flags & ~(SI_WILDP|NAT_SEARCH), + (u_int)fin->fin_p, &in.in6, + &fin->fin_src6.in6); + fin->fin_data[0] = sp; + fin->fin_data[1] = dp; + if (natl != NULL) { + NBUMPSIDE6D(0, ns_xlate_exists); + return -1; + } + + nat->nat_ndst6 = in; + nat->nat_odst6 = fin->fin_dst6; + nat->nat_nsrc6 = fin->fin_src6; + nat->nat_osrc6 = fin->fin_src6; + if ((nat->nat_hm == NULL) && ((np->in_flags & IPN_STICKY) != 0)) + nat->nat_hm = ipf_nat6_hostmap(softn, np, &fin->fin_src6, + &fin->fin_dst6, &in, + (u_32_t)dport); + + if (flags & IPN_TCPUDP) { + nat->nat_odport = dport; + nat->nat_ndport = nport; + nat->nat_osport = sport; + nat->nat_nsport = sport; + ((tcphdr_t *)fin->fin_dp)->th_dport = nport; + } else if (flags & IPN_ICMPQUERY) { + nat->nat_oicmpid = fin->fin_data[1]; + ((struct icmp6_hdr *)fin->fin_dp)->icmp6_id = nport; + nat->nat_nicmpid = nport; + } + + return move; +} + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_nat6_add */ +/* Returns: nat6_t* - NULL == failure to create new NAT structure, */ +/* else pointer to new NAT structure */ +/* Parameters: fin(I) - pointer to packet information */ +/* np(I) - pointer to NAT rule */ +/* natsave(I) - pointer to where to store NAT struct pointer */ +/* flags(I) - flags describing the current packet */ +/* direction(I) - direction of packet (in/out) */ +/* Write Lock: ipf_nat */ +/* */ +/* Attempts to create a new NAT entry. Does not actually change the packet */ +/* in any way. */ +/* */ +/* This fucntion is in three main parts: (1) deal with creating a new NAT */ +/* structure for a "MAP" rule (outgoing NAT translation); (2) deal with */ +/* creating a new NAT structure for a "RDR" rule (incoming NAT translation) */ +/* and (3) building that structure and putting it into the NAT table(s). */ +/* */ +/* NOTE: natsave should NOT be used top point back to an ipstate_t struct */ +/* as it can result in memory being corrupted. */ +/* ------------------------------------------------------------------------ */ +nat_t * +ipf_nat6_add(fin, np, natsave, flags, direction) + fr_info_t *fin; + ipnat_t *np; + nat_t **natsave; + u_int flags; + int direction; +{ + ipf_main_softc_t *softc = fin->fin_main_soft; + ipf_nat_softc_t *softn = softc->ipf_nat_soft; + hostmap_t *hm = NULL; + nat_t *nat, *natl; + natstat_t *nsp; + u_int nflags; + natinfo_t ni; + int move; +#if SOLARIS && defined(_KERNEL) && (SOLARIS2 >= 6) && defined(ICK_M_CTL_MAGIC) + qpktinfo_t *qpi = fin->fin_qpi; +#endif + + nsp = &softn->ipf_nat_stats; + + if ((nsp->ns_active * 100 / softn->ipf_nat_table_max) > + softn->ipf_nat_table_wm_high) { + softn->ipf_nat_doflush = 1; + } + + if (nsp->ns_active >= softn->ipf_nat_table_max) { + NBUMPSIDE6(fin->fin_out, ns_table_max); + return NULL; + } + + move = 1; + nflags = np->in_flags & flags; + nflags &= NAT_FROMRULE; + + ni.nai_np = np; + ni.nai_dport = 0; + ni.nai_sport = 0; + + /* Give me a new nat */ + KMALLOC(nat, nat_t *); + if (nat == NULL) { + NBUMPSIDE6(fin->fin_out, ns_memfail); + /* + * Try to automatically tune the max # of entries in the + * table allowed to be less than what will cause kmem_alloc() + * to fail and try to eliminate panics due to out of memory + * conditions arising. + */ + if ((softn->ipf_nat_table_max > softn->ipf_nat_table_sz) && + (nsp->ns_active > 100)) { + softn->ipf_nat_table_max = nsp->ns_active - 100; + printf("table_max reduced to %d\n", + softn->ipf_nat_table_max); + } + return NULL; + } + + if (flags & IPN_ICMPQUERY) { + /* + * In the ICMP query NAT code, we translate the ICMP id fields + * to make them unique. This is indepedent of the ICMP type + * (e.g. in the unlikely event that a host sends an echo and + * an tstamp request with the same id, both packets will have + * their ip address/id field changed in the same way). + */ + /* The icmp6_id field is used by the sender to identify the + * process making the icmp request. (the receiver justs + * copies it back in its response). So, it closely matches + * the concept of source port. We overlay sport, so we can + * maximally reuse the existing code. + */ + ni.nai_sport = fin->fin_data[1]; + ni.nai_dport = 0; + } + + bzero((char *)nat, sizeof(*nat)); + nat->nat_flags = flags; + nat->nat_redir = np->in_redir; + nat->nat_dir = direction; + nat->nat_pr[0] = fin->fin_p; + nat->nat_pr[1] = fin->fin_p; + + /* + * Search the current table for a match and create a new mapping + * if there is none found. + */ + if (np->in_redir & NAT_DIVERTUDP) { + move = ipf_nat6_newdivert(fin, nat, &ni); + + } else if (np->in_redir & NAT_REWRITE) { + move = ipf_nat6_newrewrite(fin, nat, &ni); + + } else if (direction == NAT_OUTBOUND) { + /* + * We can now arrange to call this for the same connection + * because ipf_nat6_new doesn't protect the code path into + * this function. + */ + natl = ipf_nat6_outlookup(fin, nflags, (u_int)fin->fin_p, + &fin->fin_src6.in6, + &fin->fin_dst6.in6); + if (natl != NULL) { + KFREE(nat); + nat = natl; + goto done; + } + + move = ipf_nat6_newmap(fin, nat, &ni); + } else { + /* + * NAT_INBOUND is used for redirects rules + */ + natl = ipf_nat6_inlookup(fin, nflags, (u_int)fin->fin_p, + &fin->fin_src6.in6, + &fin->fin_dst6.in6); + if (natl != NULL) { + KFREE(nat); + nat = natl; + goto done; + } + + move = ipf_nat6_newrdr(fin, nat, &ni); + } + if (move == -1) + goto badnat; + + np = ni.nai_np; + + nat->nat_mssclamp = np->in_mssclamp; + nat->nat_me = natsave; + nat->nat_fr = fin->fin_fr; + nat->nat_rev = fin->fin_rev; + nat->nat_ptr = np; + nat->nat_dlocal = np->in_dlocal; + + if ((np->in_apr != NULL) && ((nat->nat_flags & NAT_SLAVE) == 0)) { + if (ipf_proxy_new(fin, nat) == -1) { + NBUMPSIDE6D(fin->fin_out, ns_appr_fail); + goto badnat; + } + } + + nat->nat_ifps[0] = np->in_ifps[0]; + if (np->in_ifps[0] != NULL) { + COPYIFNAME(np->in_v[0], np->in_ifps[0], nat->nat_ifnames[0]); + } + + nat->nat_ifps[1] = np->in_ifps[1]; + if (np->in_ifps[1] != NULL) { + COPYIFNAME(np->in_v[1], np->in_ifps[1], nat->nat_ifnames[1]); + } + + if (ipf_nat6_finalise(fin, nat) == -1) { + goto badnat; + } + + np->in_use++; + + if ((move == 1) && (np->in_flags & IPN_ROUNDR)) { + if ((np->in_redir & (NAT_REDIRECT|NAT_MAP)) == NAT_REDIRECT) { + ipf_nat6_delrdr(softn, np); + ipf_nat6_addrdr(softn, np); + } else if ((np->in_redir & (NAT_REDIRECT|NAT_MAP)) == NAT_MAP) { + ipf_nat6_delmap(softn, np); + ipf_nat6_addmap(softn, np); + } + } + + if (flags & SI_WILDP) + nsp->ns_wilds++; + softn->ipf_nat_stats.ns_proto[nat->nat_pr[0]]++; + + goto done; +badnat: + NBUMPSIDE6(fin->fin_out, ns_badnatnew); + if ((hm = nat->nat_hm) != NULL) + ipf_nat_hostmapdel(softc, &hm); + KFREE(nat); + nat = NULL; +done: + if (nat != NULL && np != NULL) + np->in_hits++; + if (natsave != NULL) + *natsave = nat; + return nat; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_nat6_finalise */ +/* Returns: int - 0 == sucess, -1 == failure */ +/* Parameters: fin(I) - pointer to packet information */ +/* nat(I) - pointer to NAT entry */ +/* Write Lock: ipf_nat */ +/* */ +/* This is the tail end of constructing a new NAT entry and is the same */ +/* for both IPv4 and IPv6. */ +/* ------------------------------------------------------------------------ */ +/*ARGSUSED*/ +int +ipf_nat6_finalise(fin, nat) + fr_info_t *fin; + nat_t *nat; +{ + ipf_main_softc_t *softc = fin->fin_main_soft; + ipf_nat_softc_t *softn = softc->ipf_nat_soft; + u_32_t sum1, sum2, sumd; + frentry_t *fr; + u_32_t flags; + + flags = nat->nat_flags; + + switch (fin->fin_p) + { + case IPPROTO_ICMPV6 : + sum1 = LONG_SUM6(&nat->nat_osrc6); + sum1 += ntohs(nat->nat_oicmpid); + sum2 = LONG_SUM6(&nat->nat_nsrc6); + sum2 += ntohs(nat->nat_nicmpid); + CALC_SUMD(sum1, sum2, sumd); + nat->nat_sumd[0] = (sumd & 0xffff) + (sumd >> 16); + + sum1 = LONG_SUM6(&nat->nat_odst6); + sum2 = LONG_SUM6(&nat->nat_ndst6); + CALC_SUMD(sum1, sum2, sumd); + nat->nat_sumd[0] += (sumd & 0xffff) + (sumd >> 16); + break; + + case IPPROTO_TCP : + case IPPROTO_UDP : + sum1 = LONG_SUM6(&nat->nat_osrc6); + sum1 += ntohs(nat->nat_osport); + sum2 = LONG_SUM6(&nat->nat_nsrc6); + sum2 += ntohs(nat->nat_nsport); + CALC_SUMD(sum1, sum2, sumd); + nat->nat_sumd[0] = (sumd & 0xffff) + (sumd >> 16); + + sum1 = LONG_SUM6(&nat->nat_odst6); + sum1 += ntohs(nat->nat_odport); + sum2 = LONG_SUM6(&nat->nat_ndst6); + sum2 += ntohs(nat->nat_ndport); + CALC_SUMD(sum1, sum2, sumd); + nat->nat_sumd[0] += (sumd & 0xffff) + (sumd >> 16); + break; + + default : + sum1 = LONG_SUM6(&nat->nat_osrc6); + sum2 = LONG_SUM6(&nat->nat_nsrc6); + CALC_SUMD(sum1, sum2, sumd); + nat->nat_sumd[0] = (sumd & 0xffff) + (sumd >> 16); + + sum1 = LONG_SUM6(&nat->nat_odst6); + sum2 = LONG_SUM6(&nat->nat_ndst6); + CALC_SUMD(sum1, sum2, sumd); + nat->nat_sumd[0] += (sumd & 0xffff) + (sumd >> 16); + break; + } + + /* + * Compute the partial checksum, just in case. + * This is only ever placed into outbound packets so care needs + * to be taken over which pair of addresses are used. + */ + if (nat->nat_dir == NAT_OUTBOUND) { + sum1 = LONG_SUM6(&nat->nat_nsrc6); + sum1 += LONG_SUM6(&nat->nat_ndst6); + } else { + sum1 = LONG_SUM6(&nat->nat_osrc6); + sum1 += LONG_SUM6(&nat->nat_odst6); + } + sum1 += nat->nat_pr[1]; + nat->nat_sumd[1] = (sum1 & 0xffff) + (sum1 >> 16); + + if ((nat->nat_flags & SI_CLONE) == 0) + nat->nat_sync = ipf_sync_new(softc, SMC_NAT, fin, nat); + + if ((nat->nat_ifps[0] != NULL) && (nat->nat_ifps[0] != (void *)-1)) { + nat->nat_mtu[0] = GETIFMTU_6(nat->nat_ifps[0]); + } + + if ((nat->nat_ifps[1] != NULL) && (nat->nat_ifps[1] != (void *)-1)) { + nat->nat_mtu[1] = GETIFMTU_6(nat->nat_ifps[1]); + } + + nat->nat_v[0] = 6; + nat->nat_v[1] = 6; + + if (ipf_nat6_insert(softc, softn, nat) == 0) { + if (softn->ipf_nat_logging) + ipf_nat_log(softc, softn, nat, NL_NEW); + fr = nat->nat_fr; + if (fr != NULL) { + MUTEX_ENTER(&fr->fr_lock); + fr->fr_ref++; + MUTEX_EXIT(&fr->fr_lock); + } + return 0; + } + + NBUMPSIDE6D(fin->fin_out, ns_unfinalised); + /* + * nat6_insert failed, so cleanup time... + */ + if (nat->nat_sync != NULL) + ipf_sync_del_nat(softc->ipf_sync_soft, nat->nat_sync); + return -1; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_nat6_insert */ +/* Returns: int - 0 == sucess, -1 == failure */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* softn(I) - pointer to NAT context structure */ +/* nat(I) - pointer to NAT structure */ +/* Write Lock: ipf_nat */ +/* */ +/* Insert a NAT entry into the hash tables for searching and add it to the */ +/* list of active NAT entries. Adjust global counters when complete. */ +/* ------------------------------------------------------------------------ */ +static int +ipf_nat6_insert(softc, softn, nat) + ipf_main_softc_t *softc; + ipf_nat_softc_t *softn; + nat_t *nat; +{ + u_int hv1, hv2; + u_32_t sp, dp; + ipnat_t *in; + + /* + * Try and return an error as early as possible, so calculate the hash + * entry numbers first and then proceed. + */ + if ((nat->nat_flags & (SI_W_SPORT|SI_W_DPORT)) == 0) { + if ((nat->nat_flags & IPN_TCPUDP) != 0) { + sp = nat->nat_osport; + dp = nat->nat_odport; + } else if ((nat->nat_flags & IPN_ICMPQUERY) != 0) { + sp = 0; + dp = nat->nat_oicmpid; + } else { + sp = 0; + dp = 0; + } + hv1 = NAT_HASH_FN6(&nat->nat_osrc6, sp, 0xffffffff); + hv1 = NAT_HASH_FN6(&nat->nat_odst6, hv1 + dp, + softn->ipf_nat_table_sz); + + /* + * TRACE nat6_osrc6, nat6_osport, nat6_odst6, + * nat6_odport, hv1 + */ + + if ((nat->nat_flags & IPN_TCPUDP) != 0) { + sp = nat->nat_nsport; + dp = nat->nat_ndport; + } else if ((nat->nat_flags & IPN_ICMPQUERY) != 0) { + sp = 0; + dp = nat->nat_nicmpid; + } else { + sp = 0; + dp = 0; + } + hv2 = NAT_HASH_FN6(&nat->nat_nsrc6, sp, 0xffffffff); + hv2 = NAT_HASH_FN6(&nat->nat_ndst6, hv2 + dp, + softn->ipf_nat_table_sz); + /* + * TRACE nat6_nsrcaddr, nat6_nsport, nat6_ndstaddr, + * nat6_ndport, hv1 + */ + } else { + hv1 = NAT_HASH_FN6(&nat->nat_osrc6, 0, 0xffffffff); + hv1 = NAT_HASH_FN6(&nat->nat_odst6, hv1, + softn->ipf_nat_table_sz); + /* TRACE nat6_osrcip6, nat6_odstip6, hv1 */ + + hv2 = NAT_HASH_FN6(&nat->nat_nsrc6, 0, 0xffffffff); + hv2 = NAT_HASH_FN6(&nat->nat_ndst6, hv2, + softn->ipf_nat_table_sz); + /* TRACE nat6_nsrcip6, nat6_ndstip6, hv2 */ + } + + nat->nat_hv[0] = hv1; + nat->nat_hv[1] = hv2; + + MUTEX_INIT(&nat->nat_lock, "nat entry lock"); + + in = nat->nat_ptr; + nat->nat_ref = nat->nat_me ? 2 : 1; + + nat->nat_ifnames[0][LIFNAMSIZ - 1] = '\0'; + nat->nat_ifps[0] = ipf_resolvenic(softc, nat->nat_ifnames[0], + nat->nat_v[0]); + + if (nat->nat_ifnames[1][0] != '\0') { + nat->nat_ifnames[1][LIFNAMSIZ - 1] = '\0'; + nat->nat_ifps[1] = ipf_resolvenic(softc, nat->nat_ifnames[1], + nat->nat_v[1]); + } else if (in->in_ifnames[1] != -1) { + char *name; + + name = in->in_names + in->in_ifnames[1]; + if (name[1] != '\0' && name[0] != '-' && name[0] != '*') { + (void) strncpy(nat->nat_ifnames[1], + nat->nat_ifnames[0], LIFNAMSIZ); + nat->nat_ifnames[1][LIFNAMSIZ - 1] = '\0'; + nat->nat_ifps[1] = nat->nat_ifps[0]; + } + } + if ((nat->nat_ifps[0] != NULL) && (nat->nat_ifps[0] != (void *)-1)) { + nat->nat_mtu[0] = GETIFMTU_6(nat->nat_ifps[0]); + } + if ((nat->nat_ifps[1] != NULL) && (nat->nat_ifps[1] != (void *)-1)) { + nat->nat_mtu[1] = GETIFMTU_6(nat->nat_ifps[1]); + } + + return ipf_nat_hashtab_add(softc, softn, nat); +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_nat6_icmperrorlookup */ +/* Returns: nat6_t* - point to matching NAT structure */ +/* Parameters: fin(I) - pointer to packet information */ +/* dir(I) - direction of packet (in/out) */ +/* */ +/* Check if the ICMP error message is related to an existing TCP, UDP or */ +/* ICMP query nat entry. It is assumed that the packet is already of the */ +/* the required length. */ +/* ------------------------------------------------------------------------ */ +nat_t * +ipf_nat6_icmperrorlookup(fin, dir) + fr_info_t *fin; + int dir; +{ + ipf_main_softc_t *softc = fin->fin_main_soft; + ipf_nat_softc_t *softn = softc->ipf_nat_soft; + struct icmp6_hdr *icmp6, *orgicmp; + int flags = 0, type, minlen; + nat_stat_side_t *nside; + tcphdr_t *tcp = NULL; + u_short data[2]; + ip6_t *oip6; + nat_t *nat; + u_int p; + + minlen = 40; + icmp6 = fin->fin_dp; + type = icmp6->icmp6_type; + nside = &softn->ipf_nat_stats.ns_side6[fin->fin_out]; + /* + * Does it at least have the return (basic) IP header ? + * Only a basic IP header (no options) should be with an ICMP error + * header. Also, if it's not an error type, then return. + */ + if (!(fin->fin_flx & FI_ICMPERR)) { + ATOMIC_INCL(nside->ns_icmp_basic); + return NULL; + } + + /* + * Check packet size + */ + if (fin->fin_plen < ICMP6ERR_IPICMPHLEN) { + ATOMIC_INCL(nside->ns_icmp_size); + return NULL; + } + oip6 = (ip6_t *)((char *)fin->fin_dp + 8); + + /* + * Is the buffer big enough for all of it ? It's the size of the IP + * header claimed in the encapsulated part which is of concern. It + * may be too big to be in this buffer but not so big that it's + * outside the ICMP packet, leading to TCP deref's causing problems. + * This is possible because we don't know how big oip_hl is when we + * do the pullup early in ipf_check() and thus can't gaurantee it is + * all here now. + */ +#ifdef _KERNEL + { + mb_t *m; + + m = fin->fin_m; +# if defined(MENTAT) + if ((char *)oip6 + fin->fin_dlen - ICMPERR_ICMPHLEN > + (char *)m->b_wptr) { + ATOMIC_INCL(nside->ns_icmp_mbuf); + return NULL; + } +# else + if ((char *)oip6 + fin->fin_dlen - ICMPERR_ICMPHLEN > + (char *)fin->fin_ip + M_LEN(m)) { + ATOMIC_INCL(nside->ns_icmp_mbuf); + return NULL; + } +# endif + } +#endif + + if (IP6_NEQ(&fin->fin_dst6, &oip6->ip6_src)) { + ATOMIC_INCL(nside->ns_icmp_address); + return NULL; + } + + p = oip6->ip6_nxt; + if (p == IPPROTO_TCP) + flags = IPN_TCP; + else if (p == IPPROTO_UDP) + flags = IPN_UDP; + else if (p == IPPROTO_ICMPV6) { + orgicmp = (struct icmp6_hdr *)(oip6 + 1); + + /* see if this is related to an ICMP query */ + if (ipf_nat6_icmpquerytype(orgicmp->icmp6_type)) { + data[0] = fin->fin_data[0]; + data[1] = fin->fin_data[1]; + fin->fin_data[0] = 0; + fin->fin_data[1] = orgicmp->icmp6_id; + + flags = IPN_ICMPERR|IPN_ICMPQUERY; + /* + * NOTE : dir refers to the direction of the original + * ip packet. By definition the icmp error + * message flows in the opposite direction. + */ + if (dir == NAT_INBOUND) + nat = ipf_nat6_inlookup(fin, flags, p, + &oip6->ip6_dst, + &oip6->ip6_src); + else + nat = ipf_nat6_outlookup(fin, flags, p, + &oip6->ip6_dst, + &oip6->ip6_src); + fin->fin_data[0] = data[0]; + fin->fin_data[1] = data[1]; + return nat; + } + } + + if (flags & IPN_TCPUDP) { + minlen += 8; /* + 64bits of data to get ports */ + /* TRACE (fin,minlen) */ + if (fin->fin_plen < ICMPERR_IPICMPHLEN + minlen) { + ATOMIC_INCL(nside->ns_icmp_short); + return NULL; + } + + data[0] = fin->fin_data[0]; + data[1] = fin->fin_data[1]; + tcp = (tcphdr_t *)(oip6 + 1); + fin->fin_data[0] = ntohs(tcp->th_dport); + fin->fin_data[1] = ntohs(tcp->th_sport); + + if (dir == NAT_INBOUND) { + nat = ipf_nat6_inlookup(fin, flags, p, &oip6->ip6_dst, + &oip6->ip6_src); + } else { + nat = ipf_nat6_outlookup(fin, flags, p, &oip6->ip6_dst, + &oip6->ip6_src); + } + fin->fin_data[0] = data[0]; + fin->fin_data[1] = data[1]; + return nat; + } + if (dir == NAT_INBOUND) + nat = ipf_nat6_inlookup(fin, 0, p, &oip6->ip6_dst, + &oip6->ip6_src); + else + nat = ipf_nat6_outlookup(fin, 0, p, &oip6->ip6_dst, + &oip6->ip6_src); + + return nat; +} + + +/* result = ip1 - ip2 */ +u_32_t +ipf_nat6_ip6subtract(ip1, ip2) + i6addr_t *ip1, *ip2; +{ + i6addr_t l1, l2, d; + u_short *s1, *s2, *ds; + u_32_t r; + int i, neg; + + neg = 0; + l1 = *ip1; + l2 = *ip2; + s1 = (u_short *)&l1; + s2 = (u_short *)&l2; + ds = (u_short *)&d; + + for (i = 7; i > 0; i--) { + if (s1[i] > s2[i]) { + ds[i] = s2[i] + 0x10000 - s1[i]; + s2[i - 1] += 0x10000; + } else { + ds[i] = s2[i] - s1[i]; + } + } + if (s2[0] > s1[0]) { + ds[0] = s2[0] + 0x10000 - s1[0]; + neg = 1; + } else { + ds[0] = s2[0] - s1[0]; + } + + for (i = 0, r = 0; i < 8; i++) { + r += ds[i]; + } + + return r; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_nat6_icmperror */ +/* Returns: nat6_t* - point to matching NAT structure */ +/* Parameters: fin(I) - pointer to packet information */ +/* nflags(I) - NAT flags for this packet */ +/* dir(I) - direction of packet (in/out) */ +/* */ +/* Fix up an ICMP packet which is an error message for an existing NAT */ +/* session. This will correct both packet header data and checksums. */ +/* */ +/* This should *ONLY* be used for incoming ICMP error packets to make sure */ +/* a NAT'd ICMP packet gets correctly recognised. */ +/* ------------------------------------------------------------------------ */ +nat_t * +ipf_nat6_icmperror(fin, nflags, dir) + fr_info_t *fin; + u_int *nflags; + int dir; +{ + ipf_main_softc_t *softc = fin->fin_main_soft; + ipf_nat_softc_t *softn = softc->ipf_nat_soft; + u_32_t sum1, sum2, sumd, sumd2; + i6addr_t a1, a2, a3, a4; + struct icmp6_hdr *icmp6; + int flags, dlen, odst; + u_short *csump; + tcphdr_t *tcp; + ip6_t *oip6; + nat_t *nat; + void *dp; + + if ((fin->fin_flx & (FI_SHORT|FI_FRAGBODY))) { + NBUMPSIDE6D(fin->fin_out, ns_icmp_short); + return NULL; + } + + /* + * ipf_nat6_icmperrorlookup() will return NULL for `defective' packets. + */ + if ((fin->fin_v != 6) || !(nat = ipf_nat6_icmperrorlookup(fin, dir))) { + NBUMPSIDE6D(fin->fin_out, ns_icmp_notfound); + return NULL; + } + + tcp = NULL; + csump = NULL; + flags = 0; + sumd2 = 0; + *nflags = IPN_ICMPERR; + icmp6 = fin->fin_dp; + oip6 = (ip6_t *)((u_char *)icmp6 + sizeof(*icmp6)); + dp = (u_char *)oip6 + sizeof(*oip6); + if (oip6->ip6_nxt == IPPROTO_TCP) { + tcp = (tcphdr_t *)dp; + csump = (u_short *)&tcp->th_sum; + flags = IPN_TCP; + } else if (oip6->ip6_nxt == IPPROTO_UDP) { + udphdr_t *udp; + + udp = (udphdr_t *)dp; + tcp = (tcphdr_t *)dp; + csump = (u_short *)&udp->uh_sum; + flags = IPN_UDP; + } else if (oip6->ip6_nxt == IPPROTO_ICMPV6) + flags = IPN_ICMPQUERY; + dlen = fin->fin_plen - ((char *)dp - (char *)fin->fin_ip); + + /* + * Need to adjust ICMP header to include the real IP#'s and + * port #'s. Only apply a checksum change relative to the + * IP address change as it will be modified again in ipf_nat6_checkout + * for both address and port. Two checksum changes are + * necessary for the two header address changes. Be careful + * to only modify the checksum once for the port # and twice + * for the IP#. + */ + + /* + * Step 1 + * Fix the IP addresses in the offending IP packet. You also need + * to adjust the IP header checksum of that offending IP packet. + * + * Normally, you would expect that the ICMP checksum of the + * ICMP error message needs to be adjusted as well for the + * IP address change in oip. + * However, this is a NOP, because the ICMP checksum is + * calculated over the complete ICMP packet, which includes the + * changed oip IP addresses and oip6->ip6_sum. However, these + * two changes cancel each other out (if the delta for + * the IP address is x, then the delta for ip_sum is minus x), + * so no change in the icmp_cksum is necessary. + * + * Inbound ICMP + * ------------ + * MAP rule, SRC=a,DST=b -> SRC=c,DST=b + * - response to outgoing packet (a,b)=>(c,b) (OIP_SRC=c,OIP_DST=b) + * - OIP_SRC(c)=nat6_newsrcip, OIP_DST(b)=nat6_newdstip + *=> OIP_SRC(c)=nat6_oldsrcip, OIP_DST(b)=nat6_olddstip + * + * RDR rule, SRC=a,DST=b -> SRC=a,DST=c + * - response to outgoing packet (c,a)=>(b,a) (OIP_SRC=b,OIP_DST=a) + * - OIP_SRC(b)=nat6_olddstip, OIP_DST(a)=nat6_oldsrcip + *=> OIP_SRC(b)=nat6_newdstip, OIP_DST(a)=nat6_newsrcip + * + * REWRITE out rule, SRC=a,DST=b -> SRC=c,DST=d + * - response to outgoing packet (a,b)=>(c,d) (OIP_SRC=c,OIP_DST=d) + * - OIP_SRC(c)=nat6_newsrcip, OIP_DST(d)=nat6_newdstip + *=> OIP_SRC(c)=nat6_oldsrcip, OIP_DST(d)=nat6_olddstip + * + * REWRITE in rule, SRC=a,DST=b -> SRC=c,DST=d + * - response to outgoing packet (d,c)=>(b,a) (OIP_SRC=b,OIP_DST=a) + * - OIP_SRC(b)=nat6_olddstip, OIP_DST(a)=nat6_oldsrcip + *=> OIP_SRC(b)=nat6_newdstip, OIP_DST(a)=nat6_newsrcip + * + * Outbound ICMP + * ------------- + * MAP rule, SRC=a,DST=b -> SRC=c,DST=b + * - response to incoming packet (b,c)=>(b,a) (OIP_SRC=b,OIP_DST=a) + * - OIP_SRC(b)=nat6_olddstip, OIP_DST(a)=nat6_oldsrcip + *=> OIP_SRC(b)=nat6_newdstip, OIP_DST(a)=nat6_newsrcip + * + * RDR rule, SRC=a,DST=b -> SRC=a,DST=c + * - response to incoming packet (a,b)=>(a,c) (OIP_SRC=a,OIP_DST=c) + * - OIP_SRC(a)=nat6_newsrcip, OIP_DST(c)=nat6_newdstip + *=> OIP_SRC(a)=nat6_oldsrcip, OIP_DST(c)=nat6_olddstip + * + * REWRITE out rule, SRC=a,DST=b -> SRC=c,DST=d + * - response to incoming packet (d,c)=>(b,a) (OIP_SRC=c,OIP_DST=d) + * - OIP_SRC(c)=nat6_olddstip, OIP_DST(d)=nat6_oldsrcip + *=> OIP_SRC(b)=nat6_newdstip, OIP_DST(a)=nat6_newsrcip + * + * REWRITE in rule, SRC=a,DST=b -> SRC=c,DST=d + * - response to incoming packet (a,b)=>(c,d) (OIP_SRC=b,OIP_DST=a) + * - OIP_SRC(b)=nat6_newsrcip, OIP_DST(a)=nat6_newdstip + *=> OIP_SRC(a)=nat6_oldsrcip, OIP_DST(c)=nat6_olddstip + */ + + if (((fin->fin_out == 0) && ((nat->nat_redir & NAT_MAP) != 0)) || + ((fin->fin_out == 1) && ((nat->nat_redir & NAT_REDIRECT) != 0))) { + a1 = nat->nat_osrc6; + a4.in6 = oip6->ip6_src; + a3 = nat->nat_odst6; + a2.in6 = oip6->ip6_dst; + oip6->ip6_src = a1.in6; + oip6->ip6_dst = a3.in6; + odst = 1; + } else { + a1 = nat->nat_ndst6; + a2.in6 = oip6->ip6_dst; + a3 = nat->nat_nsrc6; + a4.in6 = oip6->ip6_src; + oip6->ip6_dst = a3.in6; + oip6->ip6_src = a1.in6; + odst = 0; + } + + sumd = 0; + if (IP6_NEQ(&a3, &a2) || IP6_NEQ(&a1, &a4)) { + if (IP6_GT(&a3, &a2)) { + sumd = ipf_nat6_ip6subtract(&a2, &a3); + sumd--; + } else { + sumd = ipf_nat6_ip6subtract(&a2, &a3); + } + if (IP6_GT(&a1, &a4)) { + sumd += ipf_nat6_ip6subtract(&a4, &a1); + sumd--; + } else { + sumd += ipf_nat6_ip6subtract(&a4, &a1); + } + sumd = ~sumd; + } + + sumd2 = sumd; + sum1 = 0; + sum2 = 0; + + /* + * Fix UDP pseudo header checksum to compensate for the + * IP address change. + */ + if (((flags & IPN_TCPUDP) != 0) && (dlen >= 4)) { + u_32_t sum3, sum4; + /* + * Step 2 : + * For offending TCP/UDP IP packets, translate the ports as + * well, based on the NAT specification. Of course such + * a change may be reflected in the ICMP checksum as well. + * + * Since the port fields are part of the TCP/UDP checksum + * of the offending IP packet, you need to adjust that checksum + * as well... except that the change in the port numbers should + * be offset by the checksum change. However, the TCP/UDP + * checksum will also need to change if there has been an + * IP address change. + */ + if (odst == 1) { + sum1 = ntohs(nat->nat_osport); + sum4 = ntohs(tcp->th_sport); + sum3 = ntohs(nat->nat_odport); + sum2 = ntohs(tcp->th_dport); + + tcp->th_sport = htons(sum1); + tcp->th_dport = htons(sum3); + } else { + sum1 = ntohs(nat->nat_ndport); + sum2 = ntohs(tcp->th_dport); + sum3 = ntohs(nat->nat_nsport); + sum4 = ntohs(tcp->th_sport); + + tcp->th_dport = htons(sum3); + tcp->th_sport = htons(sum1); + } + sumd += sum1 - sum4; + sumd += sum3 - sum2; + + if (sumd != 0 || sumd2 != 0) { + /* + * At this point, sumd is the delta to apply to the + * TCP/UDP header, given the changes in both the IP + * address and the ports and sumd2 is the delta to + * apply to the ICMP header, given the IP address + * change delta that may need to be applied to the + * TCP/UDP checksum instead. + * + * If we will both the IP and TCP/UDP checksums + * then the ICMP checksum changes by the address + * delta applied to the TCP/UDP checksum. If we + * do not change the TCP/UDP checksum them we + * apply the delta in ports to the ICMP checksum. + */ + if (oip6->ip6_nxt == IPPROTO_UDP) { + if ((dlen >= 8) && (*csump != 0)) { + ipf_fix_datacksum(csump, sumd); + } else { + sumd2 = sum4 - sum1; + if (sum1 > sum4) + sumd2--; + sumd2 += sum2 - sum3; + if (sum3 > sum2) + sumd2--; + } + } else if (oip6->ip6_nxt == IPPROTO_TCP) { + if (dlen >= 18) { + ipf_fix_datacksum(csump, sumd); + } else { + sumd2 = sum4 - sum1; + if (sum1 > sum4) + sumd2--; + sumd2 += sum2 - sum3; + if (sum3 > sum2) + sumd2--; + } + } + if (sumd2 != 0) { + sumd2 = (sumd2 & 0xffff) + (sumd2 >> 16); + sumd2 = (sumd2 & 0xffff) + (sumd2 >> 16); + sumd2 = (sumd2 & 0xffff) + (sumd2 >> 16); + ipf_fix_incksum(0, &icmp6->icmp6_cksum, + sumd2, 0); + } + } + } else if (((flags & IPN_ICMPQUERY) != 0) && (dlen >= 8)) { + struct icmp6_hdr *orgicmp; + + /* + * XXX - what if this is bogus hl and we go off the end ? + * In this case, ipf_nat6_icmperrorlookup() will have + * returned NULL. + */ + orgicmp = (struct icmp6_hdr *)dp; + + if (odst == 1) { + if (orgicmp->icmp6_id != nat->nat_osport) { + + /* + * Fix ICMP checksum (of the offening ICMP + * query packet) to compensate the change + * in the ICMP id of the offending ICMP + * packet. + * + * Since you modify orgicmp->icmp6_id with + * a delta (say x) and you compensate that + * in origicmp->icmp6_cksum with a delta + * minus x, you don't have to adjust the + * overall icmp->icmp6_cksum + */ + sum1 = ntohs(orgicmp->icmp6_id); + sum2 = ntohs(nat->nat_osport); + CALC_SUMD(sum1, sum2, sumd); + orgicmp->icmp6_id = nat->nat_oicmpid; + ipf_fix_datacksum(&orgicmp->icmp6_cksum, sumd); + } + } /* nat6_dir == NAT_INBOUND is impossible for icmp queries */ + } + return nat; +} + + +/* + * MAP-IN MAP-OUT RDR-IN RDR-OUT + * osrc X == src == src X + * odst X == dst == dst X + * nsrc == dst X X == dst + * ndst == src X X == src + * MAP = NAT_OUTBOUND, RDR = NAT_INBOUND + */ +/* + * NB: these lookups don't lock access to the list, it assumed that it has + * already been done! + */ +/* ------------------------------------------------------------------------ */ +/* Function: ipf_nat6_inlookup */ +/* Returns: nat6_t* - NULL == no match, */ +/* else pointer to matching NAT entry */ +/* Parameters: fin(I) - pointer to packet information */ +/* flags(I) - NAT flags for this packet */ +/* p(I) - protocol for this packet */ +/* src(I) - source IP address */ +/* mapdst(I) - destination IP address */ +/* */ +/* Lookup a nat entry based on the mapped destination ip address/port and */ +/* real source address/port. We use this lookup when receiving a packet, */ +/* we're looking for a table entry, based on the destination address. */ +/* */ +/* NOTE: THE PACKET BEING CHECKED (IF FOUND) HAS A MAPPING ALREADY. */ +/* */ +/* NOTE: IT IS ASSUMED THAT IS ONLY HELD WITH A READ LOCK WHEN */ +/* THIS FUNCTION IS CALLED WITH NAT_SEARCH SET IN nflags. */ +/* */ +/* flags -> relevant are IPN_UDP/IPN_TCP/IPN_ICMPQUERY that indicate if */ +/* the packet is of said protocol */ +/* ------------------------------------------------------------------------ */ +nat_t * +ipf_nat6_inlookup(fin, flags, p, src, mapdst) + fr_info_t *fin; + u_int flags, p; + struct in6_addr *src , *mapdst; +{ + ipf_main_softc_t *softc = fin->fin_main_soft; + ipf_nat_softc_t *softn = softc->ipf_nat_soft; + u_short sport, dport; + grehdr_t *gre; + ipnat_t *ipn; + u_int sflags; + nat_t *nat; + int nflags; + i6addr_t dst; + void *ifp; + u_int hv; + + ifp = fin->fin_ifp; + sport = 0; + dport = 0; + gre = NULL; + dst.in6 = *mapdst; + sflags = flags & NAT_TCPUDPICMP; + + switch (p) + { + case IPPROTO_TCP : + case IPPROTO_UDP : + sport = htons(fin->fin_data[0]); + dport = htons(fin->fin_data[1]); + break; + case IPPROTO_ICMPV6 : + if (flags & IPN_ICMPERR) + sport = fin->fin_data[1]; + else + dport = fin->fin_data[1]; + break; + default : + break; + } + + + if ((flags & SI_WILDP) != 0) + goto find_in_wild_ports; + + hv = NAT_HASH_FN6(&dst, dport, 0xffffffff); + hv = NAT_HASH_FN6(src, hv + sport, softn->ipf_nat_table_sz); + nat = softn->ipf_nat_table[1][hv]; + /* TRACE dst, dport, src, sport, hv, nat */ + + for (; nat; nat = nat->nat_hnext[1]) { + if (nat->nat_ifps[0] != NULL) { + if ((ifp != NULL) && (ifp != nat->nat_ifps[0])) + continue; + } + + if (nat->nat_pr[0] != p) + continue; + + switch (nat->nat_dir) + { + case NAT_INBOUND : + if (nat->nat_v[0] != 6) + continue; + if (IP6_NEQ(&nat->nat_osrc6, src) || + IP6_NEQ(&nat->nat_odst6, &dst)) + continue; + if ((nat->nat_flags & IPN_TCPUDP) != 0) { + if (nat->nat_osport != sport) + continue; + if (nat->nat_odport != dport) + continue; + + } else if (p == IPPROTO_ICMPV6) { + if (nat->nat_osport != dport) { + continue; + } + } + break; + case NAT_OUTBOUND : + if (nat->nat_v[1] != 6) + continue; + if (IP6_NEQ(&nat->nat_ndst6, src) || + IP6_NEQ(&nat->nat_nsrc6, &dst)) + continue; + if ((nat->nat_flags & IPN_TCPUDP) != 0) { + if (nat->nat_ndport != sport) + continue; + if (nat->nat_nsport != dport) + continue; + + } else if (p == IPPROTO_ICMPV6) { + if (nat->nat_osport != dport) { + continue; + } + } + break; + } + + + if ((nat->nat_flags & IPN_TCPUDP) != 0) { + ipn = nat->nat_ptr; +#ifdef IPF_V6_PROXIES + if ((ipn != NULL) && (nat->nat_aps != NULL)) + if (appr_match(fin, nat) != 0) + continue; +#endif + } + if ((nat->nat_ifps[0] == NULL) && (ifp != NULL)) { + nat->nat_ifps[0] = ifp; + nat->nat_mtu[0] = GETIFMTU_6(ifp); + } + return nat; + } + + /* + * So if we didn't find it but there are wildcard members in the hash + * table, go back and look for them. We do this search and update here + * because it is modifying the NAT table and we want to do this only + * for the first packet that matches. The exception, of course, is + * for "dummy" (FI_IGNORE) lookups. + */ +find_in_wild_ports: + if (!(flags & NAT_TCPUDP) || !(flags & NAT_SEARCH)) { + NBUMPSIDE6DX(0, ns_lookup_miss, ns_lookup_miss_1); + return NULL; + } + if (softn->ipf_nat_stats.ns_wilds == 0 || (fin->fin_flx & FI_NOWILD)) { + NBUMPSIDE6D(0, ns_lookup_nowild); + return NULL; + } + + RWLOCK_EXIT(&softc->ipf_nat); + + hv = NAT_HASH_FN6(&dst, 0, 0xffffffff); + hv = NAT_HASH_FN6(src, hv, softn->ipf_nat_table_sz); + WRITE_ENTER(&softc->ipf_nat); + + nat = softn->ipf_nat_table[1][hv]; + /* TRACE dst, src, hv, nat */ + for (; nat; nat = nat->nat_hnext[1]) { + if (nat->nat_ifps[0] != NULL) { + if ((ifp != NULL) && (ifp != nat->nat_ifps[0])) + continue; + } + + if (nat->nat_pr[0] != fin->fin_p) + continue; + + switch (nat->nat_dir) + { + case NAT_INBOUND : + if (nat->nat_v[0] != 6) + continue; + if (IP6_NEQ(&nat->nat_osrc6, src) || + IP6_NEQ(&nat->nat_odst6, &dst)) + continue; + break; + case NAT_OUTBOUND : + if (nat->nat_v[1] != 6) + continue; + if (IP6_NEQ(&nat->nat_ndst6, src) || + IP6_NEQ(&nat->nat_nsrc6, &dst)) + continue; + break; + } + + nflags = nat->nat_flags; + if (!(nflags & (NAT_TCPUDP|SI_WILDP))) + continue; + + if (ipf_nat_wildok(nat, (int)sport, (int)dport, nflags, + NAT_INBOUND) == 1) { + if ((fin->fin_flx & FI_IGNORE) != 0) + break; + if ((nflags & SI_CLONE) != 0) { + nat = ipf_nat_clone(fin, nat); + if (nat == NULL) + break; + } else { + MUTEX_ENTER(&softn->ipf_nat_new); + softn->ipf_nat_stats.ns_wilds--; + MUTEX_EXIT(&softn->ipf_nat_new); + } + + if (nat->nat_dir == NAT_INBOUND) { + if (nat->nat_osport == 0) { + nat->nat_osport = sport; + nat->nat_nsport = sport; + } + if (nat->nat_odport == 0) { + nat->nat_odport = dport; + nat->nat_ndport = dport; + } + } else { + if (nat->nat_osport == 0) { + nat->nat_osport = dport; + nat->nat_nsport = dport; + } + if (nat->nat_odport == 0) { + nat->nat_odport = sport; + nat->nat_ndport = sport; + } + } + if ((nat->nat_ifps[0] == NULL) && (ifp != NULL)) { + nat->nat_ifps[0] = ifp; + nat->nat_mtu[0] = GETIFMTU_6(ifp); + } + nat->nat_flags &= ~(SI_W_DPORT|SI_W_SPORT); + ipf_nat6_tabmove(softn, nat); + break; + } + } + + MUTEX_DOWNGRADE(&softc->ipf_nat); + + if (nat == NULL) { + NBUMPSIDE6DX(0, ns_lookup_miss, ns_lookup_miss_2); + } + return nat; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_nat6_tabmove */ +/* Returns: Nil */ +/* Parameters: nat(I) - pointer to NAT structure */ +/* Write Lock: ipf_nat */ +/* */ +/* This function is only called for TCP/UDP NAT table entries where the */ +/* original was placed in the table without hashing on the ports and we now */ +/* want to include hashing on port numbers. */ +/* ------------------------------------------------------------------------ */ +static void +ipf_nat6_tabmove(softn, nat) + ipf_nat_softc_t *softn; + nat_t *nat; +{ + nat_t **natp; + u_int hv0, hv1; + + if (nat->nat_flags & SI_CLONE) + return; + + /* + * Remove the NAT entry from the old location + */ + if (nat->nat_hnext[0]) + nat->nat_hnext[0]->nat_phnext[0] = nat->nat_phnext[0]; + *nat->nat_phnext[0] = nat->nat_hnext[0]; + softn->ipf_nat_stats.ns_side[0].ns_bucketlen[nat->nat_hv[0]]--; + + if (nat->nat_hnext[1]) + nat->nat_hnext[1]->nat_phnext[1] = nat->nat_phnext[1]; + *nat->nat_phnext[1] = nat->nat_hnext[1]; + softn->ipf_nat_stats.ns_side[1].ns_bucketlen[nat->nat_hv[1]]--; + + /* + * Add into the NAT table in the new position + */ + hv0 = NAT_HASH_FN6(&nat->nat_osrc6, nat->nat_osport, 0xffffffff); + hv0 = NAT_HASH_FN6(&nat->nat_odst6, hv0 + nat->nat_odport, + softn->ipf_nat_table_sz); + hv1 = NAT_HASH_FN6(&nat->nat_nsrc6, nat->nat_nsport, 0xffffffff); + hv1 = NAT_HASH_FN6(&nat->nat_ndst6, hv1 + nat->nat_ndport, + softn->ipf_nat_table_sz); + + if (nat->nat_dir == NAT_INBOUND || nat->nat_dir == NAT_DIVERTIN) { + u_int swap; + + swap = hv0; + hv0 = hv1; + hv1 = swap; + } + + /* TRACE nat_osrc6, nat_osport, nat_odst6, nat_odport, hv0 */ + /* TRACE nat_nsrc6, nat_nsport, nat_ndst6, nat_ndport, hv1 */ + + nat->nat_hv[0] = hv0; + natp = &softn->ipf_nat_table[0][hv0]; + if (*natp) + (*natp)->nat_phnext[0] = &nat->nat_hnext[0]; + nat->nat_phnext[0] = natp; + nat->nat_hnext[0] = *natp; + *natp = nat; + softn->ipf_nat_stats.ns_side[0].ns_bucketlen[hv0]++; + + nat->nat_hv[1] = hv1; + natp = &softn->ipf_nat_table[1][hv1]; + if (*natp) + (*natp)->nat_phnext[1] = &nat->nat_hnext[1]; + nat->nat_phnext[1] = natp; + nat->nat_hnext[1] = *natp; + *natp = nat; + softn->ipf_nat_stats.ns_side[1].ns_bucketlen[hv1]++; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_nat6_outlookup */ +/* Returns: nat6_t* - NULL == no match, */ +/* else pointer to matching NAT entry */ +/* Parameters: fin(I) - pointer to packet information */ +/* flags(I) - NAT flags for this packet */ +/* p(I) - protocol for this packet */ +/* src(I) - source IP address */ +/* dst(I) - destination IP address */ +/* rw(I) - 1 == write lock on held, 0 == read lock. */ +/* */ +/* Lookup a nat entry based on the source 'real' ip address/port and */ +/* destination address/port. We use this lookup when sending a packet out, */ +/* we're looking for a table entry, based on the source address. */ +/* */ +/* NOTE: THE PACKET BEING CHECKED (IF FOUND) HAS A MAPPING ALREADY. */ +/* */ +/* NOTE: IT IS ASSUMED THAT IS ONLY HELD WITH A READ LOCK WHEN */ +/* THIS FUNCTION IS CALLED WITH NAT_SEARCH SET IN nflags. */ +/* */ +/* flags -> relevant are IPN_UDP/IPN_TCP/IPN_ICMPQUERY that indicate if */ +/* the packet is of said protocol */ +/* ------------------------------------------------------------------------ */ +nat_t * +ipf_nat6_outlookup(fin, flags, p, src, dst) + fr_info_t *fin; + u_int flags, p; + struct in6_addr *src , *dst; +{ + ipf_main_softc_t *softc = fin->fin_main_soft; + ipf_nat_softc_t *softn = softc->ipf_nat_soft; + u_short sport, dport; + u_int sflags; + ipnat_t *ipn; + nat_t *nat; + void *ifp; + u_int hv; + + ifp = fin->fin_ifp; + sflags = flags & IPN_TCPUDPICMP; + sport = 0; + dport = 0; + + switch (p) + { + case IPPROTO_TCP : + case IPPROTO_UDP : + sport = htons(fin->fin_data[0]); + dport = htons(fin->fin_data[1]); + break; + case IPPROTO_ICMPV6 : + if (flags & IPN_ICMPERR) + sport = fin->fin_data[1]; + else + dport = fin->fin_data[1]; + break; + default : + break; + } + + if ((flags & SI_WILDP) != 0) + goto find_out_wild_ports; + + hv = NAT_HASH_FN6(src, sport, 0xffffffff); + hv = NAT_HASH_FN6(dst, hv + dport, softn->ipf_nat_table_sz); + nat = softn->ipf_nat_table[0][hv]; + + /* TRACE src, sport, dst, dport, hv, nat */ + + for (; nat; nat = nat->nat_hnext[0]) { + if (nat->nat_ifps[1] != NULL) { + if ((ifp != NULL) && (ifp != nat->nat_ifps[1])) + continue; + } + + if (nat->nat_pr[1] != p) + continue; + + switch (nat->nat_dir) + { + case NAT_INBOUND : + if (nat->nat_v[1] != 6) + continue; + if (IP6_NEQ(&nat->nat_ndst6, src) || + IP6_NEQ(&nat->nat_nsrc6, dst)) + continue; + + if ((nat->nat_flags & IPN_TCPUDP) != 0) { + if (nat->nat_ndport != sport) + continue; + if (nat->nat_nsport != dport) + continue; + + } else if (p == IPPROTO_ICMPV6) { + if (nat->nat_osport != dport) { + continue; + } + } + break; + case NAT_OUTBOUND : + if (nat->nat_v[0] != 6) + continue; + if (IP6_NEQ(&nat->nat_osrc6, src) || + IP6_NEQ(&nat->nat_odst6, dst)) + continue; + + if ((nat->nat_flags & IPN_TCPUDP) != 0) { + if (nat->nat_odport != dport) + continue; + if (nat->nat_osport != sport) + continue; + + } else if (p == IPPROTO_ICMPV6) { + if (nat->nat_osport != dport) { + continue; + } + } + break; + } + + ipn = nat->nat_ptr; +#ifdef IPF_V6_PROXIES + if ((ipn != NULL) && (nat->nat_aps != NULL)) + if (appr_match(fin, nat) != 0) + continue; +#endif + + if ((nat->nat_ifps[1] == NULL) && (ifp != NULL)) { + nat->nat_ifps[1] = ifp; + nat->nat_mtu[1] = GETIFMTU_6(ifp); + } + return nat; + } + + /* + * So if we didn't find it but there are wildcard members in the hash + * table, go back and look for them. We do this search and update here + * because it is modifying the NAT table and we want to do this only + * for the first packet that matches. The exception, of course, is + * for "dummy" (FI_IGNORE) lookups. + */ +find_out_wild_ports: + if (!(flags & NAT_TCPUDP) || !(flags & NAT_SEARCH)) { + NBUMPSIDE6DX(1, ns_lookup_miss, ns_lookup_miss_3); + return NULL; + } + if (softn->ipf_nat_stats.ns_wilds == 0 || (fin->fin_flx & FI_NOWILD)) { + NBUMPSIDE6D(1, ns_lookup_nowild); + return NULL; + } + + RWLOCK_EXIT(&softc->ipf_nat); + + hv = NAT_HASH_FN6(src, 0, 0xffffffff); + hv = NAT_HASH_FN6(dst, hv, softn->ipf_nat_table_sz); + + WRITE_ENTER(&softc->ipf_nat); + + nat = softn->ipf_nat_table[0][hv]; + for (; nat; nat = nat->nat_hnext[0]) { + if (nat->nat_ifps[1] != NULL) { + if ((ifp != NULL) && (ifp != nat->nat_ifps[1])) + continue; + } + + if (nat->nat_pr[1] != fin->fin_p) + continue; + + switch (nat->nat_dir) + { + case NAT_INBOUND : + if (nat->nat_v[1] != 6) + continue; + if (IP6_NEQ(&nat->nat_ndst6, src) || + IP6_NEQ(&nat->nat_nsrc6, dst)) + continue; + break; + case NAT_OUTBOUND : + if (nat->nat_v[0] != 6) + continue; + if (IP6_NEQ(&nat->nat_osrc6, src) || + IP6_NEQ(&nat->nat_odst6, dst)) + continue; + break; + } + + if (!(nat->nat_flags & (NAT_TCPUDP|SI_WILDP))) + continue; + + if (ipf_nat_wildok(nat, (int)sport, (int)dport, nat->nat_flags, + NAT_OUTBOUND) == 1) { + if ((fin->fin_flx & FI_IGNORE) != 0) + break; + if ((nat->nat_flags & SI_CLONE) != 0) { + nat = ipf_nat_clone(fin, nat); + if (nat == NULL) + break; + } else { + MUTEX_ENTER(&softn->ipf_nat_new); + softn->ipf_nat_stats.ns_wilds--; + MUTEX_EXIT(&softn->ipf_nat_new); + } + + if (nat->nat_dir == NAT_OUTBOUND) { + if (nat->nat_osport == 0) { + nat->nat_osport = sport; + nat->nat_nsport = sport; + } + if (nat->nat_odport == 0) { + nat->nat_odport = dport; + nat->nat_ndport = dport; + } + } else { + if (nat->nat_osport == 0) { + nat->nat_osport = dport; + nat->nat_nsport = dport; + } + if (nat->nat_odport == 0) { + nat->nat_odport = sport; + nat->nat_ndport = sport; + } + } + if ((nat->nat_ifps[1] == NULL) && (ifp != NULL)) { + nat->nat_ifps[1] = ifp; + nat->nat_mtu[1] = GETIFMTU_6(ifp); + } + nat->nat_flags &= ~(SI_W_DPORT|SI_W_SPORT); + ipf_nat6_tabmove(softn, nat); + break; + } + } + + MUTEX_DOWNGRADE(&softc->ipf_nat); + + if (nat == NULL) { + NBUMPSIDE6DX(1, ns_lookup_miss, ns_lookup_miss_4); + } + return nat; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_nat6_lookupredir */ +/* Returns: nat6_t* - NULL == no match, */ +/* else pointer to matching NAT entry */ +/* Parameters: np(I) - pointer to description of packet to find NAT table */ +/* entry for. */ +/* */ +/* Lookup the NAT tables to search for a matching redirect */ +/* The contents of natlookup_t should imitate those found in a packet that */ +/* would be translated - ie a packet coming in for RDR or going out for MAP.*/ +/* We can do the lookup in one of two ways, imitating an inbound or */ +/* outbound packet. By default we assume outbound, unless IPN_IN is set. */ +/* For IN, the fields are set as follows: */ +/* nl_real* = source information */ +/* nl_out* = destination information (translated) */ +/* For an out packet, the fields are set like this: */ +/* nl_in* = source information (untranslated) */ +/* nl_out* = destination information (translated) */ +/* ------------------------------------------------------------------------ */ +nat_t * +ipf_nat6_lookupredir(np) + natlookup_t *np; +{ + fr_info_t fi; + nat_t *nat; + + bzero((char *)&fi, sizeof(fi)); + if (np->nl_flags & IPN_IN) { + fi.fin_data[0] = ntohs(np->nl_realport); + fi.fin_data[1] = ntohs(np->nl_outport); + } else { + fi.fin_data[0] = ntohs(np->nl_inport); + fi.fin_data[1] = ntohs(np->nl_outport); + } + if (np->nl_flags & IPN_TCP) + fi.fin_p = IPPROTO_TCP; + else if (np->nl_flags & IPN_UDP) + fi.fin_p = IPPROTO_UDP; + else if (np->nl_flags & (IPN_ICMPERR|IPN_ICMPQUERY)) + fi.fin_p = IPPROTO_ICMPV6; + + /* + * We can do two sorts of lookups: + * - IPN_IN: we have the `real' and `out' address, look for `in'. + * - default: we have the `in' and `out' address, look for `real'. + */ + if (np->nl_flags & IPN_IN) { + if ((nat = ipf_nat6_inlookup(&fi, np->nl_flags, fi.fin_p, + &np->nl_realip6, + &np->nl_outip6))) { + np->nl_inip6 = nat->nat_odst6.in6; + np->nl_inport = nat->nat_odport; + } + } else { + /* + * If nl_inip is non null, this is a lookup based on the real + * ip address. Else, we use the fake. + */ + if ((nat = ipf_nat6_outlookup(&fi, np->nl_flags, fi.fin_p, + &np->nl_inip6, &np->nl_outip6))) { + + if ((np->nl_flags & IPN_FINDFORWARD) != 0) { + fr_info_t fin; + bzero((char *)&fin, sizeof(fin)); + fin.fin_p = nat->nat_pr[0]; + fin.fin_data[0] = ntohs(nat->nat_ndport); + fin.fin_data[1] = ntohs(nat->nat_nsport); + if (ipf_nat6_inlookup(&fin, np->nl_flags, + fin.fin_p, + &nat->nat_ndst6.in6, + &nat->nat_nsrc6.in6) != + NULL) { + np->nl_flags &= ~IPN_FINDFORWARD; + } + } + + np->nl_realip6 = nat->nat_ndst6.in6; + np->nl_realport = nat->nat_ndport; + } + } + + return nat; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_nat6_match */ +/* Returns: int - 0 == no match, 1 == match */ +/* Parameters: fin(I) - pointer to packet information */ +/* np(I) - pointer to NAT rule */ +/* */ +/* Pull the matching of a packet against a NAT rule out of that complex */ +/* loop inside ipf_nat6_checkin() and lay it out properly in its own */ +/* function. */ +/* ------------------------------------------------------------------------ */ +static int +ipf_nat6_match(fin, np) + fr_info_t *fin; + ipnat_t *np; +{ + frtuc_t *ft; + int match; + + match = 0; + switch (np->in_osrcatype) + { + case FRI_NORMAL : + match = IP6_MASKNEQ(&fin->fin_src6, &np->in_osrcmsk6, + &np->in_osrcip6); + break; + case FRI_LOOKUP : + match = (*np->in_osrcfunc)(fin->fin_main_soft, np->in_osrcptr, + 6, &fin->fin_src6, fin->fin_plen); + break; + } + match ^= ((np->in_flags & IPN_NOTSRC) != 0); + if (match) + return 0; + + match = 0; + switch (np->in_odstatype) + { + case FRI_NORMAL : + match = IP6_MASKNEQ(&fin->fin_dst6, &np->in_odstmsk6, + &np->in_odstip6); + break; + case FRI_LOOKUP : + match = (*np->in_odstfunc)(fin->fin_main_soft, np->in_odstptr, + 6, &fin->fin_dst6, fin->fin_plen); + break; + } + + match ^= ((np->in_flags & IPN_NOTDST) != 0); + if (match) + return 0; + + ft = &np->in_tuc; + if (!(fin->fin_flx & FI_TCPUDP) || + (fin->fin_flx & (FI_SHORT|FI_FRAGBODY))) { + if (ft->ftu_scmp || ft->ftu_dcmp) + return 0; + return 1; + } + + return ipf_tcpudpchk(&fin->fin_fi, ft); +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_nat6_checkout */ +/* Returns: int - -1 == packet failed NAT checks so block it, */ +/* 0 == no packet translation occurred, */ +/* 1 == packet was successfully translated. */ +/* Parameters: fin(I) - pointer to packet information */ +/* passp(I) - pointer to filtering result flags */ +/* */ +/* Check to see if an outcoming packet should be changed. ICMP packets are */ +/* first checked to see if they match an existing entry (if an error), */ +/* otherwise a search of the current NAT table is made. If neither results */ +/* in a match then a search for a matching NAT rule is made. Create a new */ +/* NAT entry if a we matched a NAT rule. Lastly, actually change the */ +/* packet header(s) as required. */ +/* ------------------------------------------------------------------------ */ +int +ipf_nat6_checkout(fin, passp) + fr_info_t *fin; + u_32_t *passp; +{ + ipf_main_softc_t *softc = fin->fin_main_soft; + ipf_nat_softc_t *softn = softc->ipf_nat_soft; + struct icmp6_hdr *icmp6 = NULL; + struct ifnet *ifp, *sifp; + tcphdr_t *tcp = NULL; + int rval, natfailed; + ipnat_t *np = NULL; + u_int nflags = 0; + i6addr_t ipa, iph; + int natadd = 1; + frentry_t *fr; + nat_t *nat; + + if (softn->ipf_nat_stats.ns_rules == 0 || softn->ipf_nat_lock != 0) + return 0; + + icmp6 = NULL; + natfailed = 0; + fr = fin->fin_fr; + sifp = fin->fin_ifp; + if (fr != NULL) { + ifp = fr->fr_tifs[fin->fin_rev].fd_ptr; + if ((ifp != NULL) && (ifp != (void *)-1)) + fin->fin_ifp = ifp; + } + ifp = fin->fin_ifp; + + if (!(fin->fin_flx & FI_SHORT) && (fin->fin_off == 0)) { + switch (fin->fin_p) + { + case IPPROTO_TCP : + nflags = IPN_TCP; + break; + case IPPROTO_UDP : + nflags = IPN_UDP; + break; + case IPPROTO_ICMPV6 : + icmp6 = fin->fin_dp; + + /* + * Apart from ECHO request and reply, all other + * informational messages should not be translated + * so as to keep IPv6 working. + */ + if (icmp6->icmp6_type > ICMP6_ECHO_REPLY) + return 0; + + /* + * This is an incoming packet, so the destination is + * the icmp6_id and the source port equals 0 + */ + if ((fin->fin_flx & FI_ICMPQUERY) != 0) + nflags = IPN_ICMPQUERY; + break; + default : + break; + } + + if ((nflags & IPN_TCPUDP)) + tcp = fin->fin_dp; + } + + ipa = fin->fin_src6; + + READ_ENTER(&softc->ipf_nat); + + if ((fin->fin_p == IPPROTO_ICMPV6) && !(nflags & IPN_ICMPQUERY) && + (nat = ipf_nat6_icmperror(fin, &nflags, NAT_OUTBOUND))) + /*EMPTY*/; + else if ((fin->fin_flx & FI_FRAG) && (nat = ipf_frag_natknown(fin))) + natadd = 0; + else if ((nat = ipf_nat6_outlookup(fin, nflags|NAT_SEARCH, + (u_int)fin->fin_p, + &fin->fin_src6.in6, + &fin->fin_dst6.in6))) { + nflags = nat->nat_flags; + } else if (fin->fin_off == 0) { + u_32_t hv, nmsk = 0; + i6addr_t *msk; + + /* + * If there is no current entry in the nat table for this IP#, + * create one for it (if there is a matching rule). + */ +maskloop: + msk = &softn->ipf_nat6_map_active_masks[nmsk]; + IP6_AND(&ipa, msk, &iph); + hv = NAT_HASH_FN6(&iph, 0, softn->ipf_nat_maprules_sz); + for (np = softn->ipf_nat_map_rules[hv]; np; np = np->in_mnext) { + if ((np->in_ifps[1] && (np->in_ifps[1] != ifp))) + continue; + if (np->in_v[0] != 6) + continue; + if (np->in_pr[1] && (np->in_pr[1] != fin->fin_p)) + continue; + if ((np->in_flags & IPN_RF) && + !(np->in_flags & nflags)) + continue; + if (np->in_flags & IPN_FILTER) { + switch (ipf_nat6_match(fin, np)) + { + case 0 : + continue; + case -1 : + rval = -1; + goto outmatchfail; + case 1 : + default : + break; + } + } else if (!IP6_MASKEQ(&ipa, &np->in_osrcmsk, + &np->in_osrcip6)) + continue; + + if ((fr != NULL) && + !ipf_matchtag(&np->in_tag, &fr->fr_nattag)) + continue; + +#ifdef IPF_V6_PROXIES + if (np->in_plabel != -1) { + if (((np->in_flags & IPN_FILTER) == 0) && + (np->in_odport != fin->fin_data[1])) + continue; + if (appr_ok(fin, tcp, np) == 0) + continue; + } +#endif + + if (np->in_flags & IPN_NO) { + np->in_hits++; + break; + } + + MUTEX_ENTER(&softn->ipf_nat_new); + nat = ipf_nat6_add(fin, np, NULL, nflags, NAT_OUTBOUND); + MUTEX_EXIT(&softn->ipf_nat_new); + if (nat != NULL) { + np->in_hits++; + break; + } + natfailed = -1; + } + if ((np == NULL) && (nmsk < softn->ipf_nat6_map_max)) { + nmsk++; + goto maskloop; + } + } + + if (nat != NULL) { + rval = ipf_nat6_out(fin, nat, natadd, nflags); + if (rval == 1) { + MUTEX_ENTER(&nat->nat_lock); + ipf_nat_update(fin, nat); + nat->nat_bytes[1] += fin->fin_plen; + nat->nat_pkts[1]++; + MUTEX_EXIT(&nat->nat_lock); + } + } else + rval = natfailed; +outmatchfail: + RWLOCK_EXIT(&softc->ipf_nat); + + switch (rval) + { + case -1 : + if (passp != NULL) { + NBUMPSIDE6D(1, ns_drop); + *passp = FR_BLOCK; + fin->fin_reason = FRB_NATV6; + } + fin->fin_flx |= FI_BADNAT; + NBUMPSIDE6D(1, ns_badnat); + break; + case 0 : + NBUMPSIDE6D(1, ns_ignored); + break; + case 1 : + NBUMPSIDE6D(1, ns_translated); + break; + } + fin->fin_ifp = sifp; + return rval; +} + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_nat6_out */ +/* Returns: int - -1 == packet failed NAT checks so block it, */ +/* 1 == packet was successfully translated. */ +/* Parameters: fin(I) - pointer to packet information */ +/* nat(I) - pointer to NAT structure */ +/* natadd(I) - flag indicating if it is safe to add frag cache */ +/* nflags(I) - NAT flags set for this packet */ +/* */ +/* Translate a packet coming "out" on an interface. */ +/* ------------------------------------------------------------------------ */ +static int +ipf_nat6_out(fin, nat, natadd, nflags) + fr_info_t *fin; + nat_t *nat; + int natadd; + u_32_t nflags; +{ + ipf_main_softc_t *softc = fin->fin_main_soft; + ipf_nat_softc_t *softn = softc->ipf_nat_soft; + struct icmp6_hdr *icmp6; + tcphdr_t *tcp; + ipnat_t *np; + int skip; + int i; + + tcp = NULL; + icmp6 = NULL; + np = nat->nat_ptr; + + if ((natadd != 0) && (fin->fin_flx & FI_FRAG) && (np != NULL)) + (void) ipf_frag_natnew(softc, fin, 0, nat); + + /* + * Address assignment is after the checksum modification because + * we are using the address in the packet for determining the + * correct checksum offset (the ICMP error could be coming from + * anyone...) + */ + switch (nat->nat_dir) + { + case NAT_OUTBOUND : + fin->fin_ip6->ip6_src = nat->nat_nsrc6.in6; + fin->fin_src6 = nat->nat_nsrc6; + fin->fin_ip6->ip6_dst = nat->nat_ndst6.in6; + fin->fin_dst6 = nat->nat_ndst6; + break; + + case NAT_INBOUND : + fin->fin_ip6->ip6_src = nat->nat_odst6.in6; + fin->fin_src6 = nat->nat_ndst6; + fin->fin_ip6->ip6_dst = nat->nat_osrc6.in6; + fin->fin_dst6 = nat->nat_nsrc6; + break; + + case NAT_DIVERTIN : + { + mb_t *m; + + skip = ipf_nat6_decap(fin, nat); + if (skip <= 0) { + NBUMPSIDE6D(1, ns_decap_fail); + return -1; + } + + m = fin->fin_m; + +#if defined(MENTAT) && defined(_KERNEL) + m->b_rptr += skip; +#else + m->m_data += skip; + m->m_len -= skip; + +# ifdef M_PKTHDR + if (m->m_flags & M_PKTHDR) + m->m_pkthdr.len -= skip; +# endif +#endif + + MUTEX_ENTER(&nat->nat_lock); + ipf_nat_update(fin, nat); + MUTEX_EXIT(&nat->nat_lock); + fin->fin_flx |= FI_NATED; + if (np != NULL && np->in_tag.ipt_num[0] != 0) + fin->fin_nattag = &np->in_tag; + return 1; + /* NOTREACHED */ + } + + case NAT_DIVERTOUT : + { + udphdr_t *uh; + ip6_t *ip6; + mb_t *m; + + m = M_DUP(np->in_divmp); + if (m == NULL) { + NBUMPSIDE6D(1, ns_divert_dup); + return -1; + } + + ip6 = MTOD(m, ip6_t *); + + ip6->ip6_plen = htons(fin->fin_plen + 8); + + uh = (udphdr_t *)(ip6 + 1); + uh->uh_ulen = htons(fin->fin_plen); + + PREP_MB_T(fin, m); + + fin->fin_ip6 = ip6; + fin->fin_plen += sizeof(ip6_t) + 8; /* UDP + new IPv4 hdr */ + fin->fin_dlen += sizeof(ip6_t) + 8; /* UDP + old IPv4 hdr */ + + nflags &= ~IPN_TCPUDPICMP; + + break; + } + + default : + break; + } + + if (!(fin->fin_flx & FI_SHORT) && (fin->fin_off == 0)) { + u_short *csump; + + if ((nat->nat_nsport != 0) && (nflags & IPN_TCPUDP)) { + tcp = fin->fin_dp; + + switch (nat->nat_dir) + { + case NAT_OUTBOUND : + tcp->th_sport = nat->nat_nsport; + fin->fin_data[0] = ntohs(nat->nat_nsport); + tcp->th_dport = nat->nat_ndport; + fin->fin_data[1] = ntohs(nat->nat_ndport); + break; + + case NAT_INBOUND : + tcp->th_sport = nat->nat_odport; + fin->fin_data[0] = ntohs(nat->nat_odport); + tcp->th_dport = nat->nat_osport; + fin->fin_data[1] = ntohs(nat->nat_osport); + break; + } + } + + if ((nat->nat_nsport != 0) && (nflags & IPN_ICMPQUERY)) { + icmp6 = fin->fin_dp; + icmp6->icmp6_id = nat->nat_nicmpid; + } + + csump = ipf_nat_proto(fin, nat, nflags); + + /* + * The above comments do not hold for layer 4 (or higher) + * checksums... + */ + if (csump != NULL) { + if (nat->nat_dir == NAT_OUTBOUND) + ipf_fix_outcksum(fin->fin_cksum, csump, + nat->nat_sumd[0], + nat->nat_sumd[1] + + fin->fin_dlen); + else + ipf_fix_incksum(fin->fin_cksum, csump, + nat->nat_sumd[0], + nat->nat_sumd[1] + + fin->fin_dlen); + } + } + + ipf_sync_update(softc, SMC_NAT, fin, nat->nat_sync); + /* ------------------------------------------------------------- */ + /* A few quick notes: */ + /* Following are test conditions prior to calling the */ + /* ipf_proxy_check routine. */ + /* */ + /* A NULL tcp indicates a non TCP/UDP packet. When dealing */ + /* with a redirect rule, we attempt to match the packet's */ + /* source port against in_dport, otherwise we'd compare the */ + /* packet's destination. */ + /* ------------------------------------------------------------- */ + if ((np != NULL) && (np->in_apr != NULL)) { + i = ipf_proxy_check(fin, nat); + if (i == 0) { + i = 1; + } else if (i == -1) { + NBUMPSIDE6D(1, ns_ipf_proxy_fail); + } + } else { + i = 1; + } + fin->fin_flx |= FI_NATED; + return i; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_nat6_checkin */ +/* Returns: int - -1 == packet failed NAT checks so block it, */ +/* 0 == no packet translation occurred, */ +/* 1 == packet was successfully translated. */ +/* Parameters: fin(I) - pointer to packet information */ +/* passp(I) - pointer to filtering result flags */ +/* */ +/* Check to see if an incoming packet should be changed. ICMP packets are */ +/* first checked to see if they match an existing entry (if an error), */ +/* otherwise a search of the current NAT table is made. If neither results */ +/* in a match then a search for a matching NAT rule is made. Create a new */ +/* NAT entry if a we matched a NAT rule. Lastly, actually change the */ +/* packet header(s) as required. */ +/* ------------------------------------------------------------------------ */ +int +ipf_nat6_checkin(fin, passp) + fr_info_t *fin; + u_32_t *passp; +{ + ipf_main_softc_t *softc = fin->fin_main_soft; + ipf_nat_softc_t *softn = softc->ipf_nat_soft; + struct icmp6_hdr *icmp6; + u_int nflags, natadd; + int rval, natfailed; + struct ifnet *ifp; + i6addr_t ipa, iph; + tcphdr_t *tcp; + u_short dport; + ipnat_t *np; + nat_t *nat; + + if (softn->ipf_nat_stats.ns_rules == 0 || softn->ipf_nat_lock != 0) + return 0; + + tcp = NULL; + icmp6 = NULL; + dport = 0; + natadd = 1; + nflags = 0; + natfailed = 0; + ifp = fin->fin_ifp; + + if (!(fin->fin_flx & FI_SHORT) && (fin->fin_off == 0)) { + switch (fin->fin_p) + { + case IPPROTO_TCP : + nflags = IPN_TCP; + break; + case IPPROTO_UDP : + nflags = IPN_UDP; + break; + case IPPROTO_ICMPV6 : + icmp6 = fin->fin_dp; + + /* + * Apart from ECHO request and reply, all other + * informational messages should not be translated + * so as to keep IPv6 working. + */ + if (icmp6->icmp6_type > ICMP6_ECHO_REPLY) + return 0; + + /* + * This is an incoming packet, so the destination is + * the icmp6_id and the source port equals 0 + */ + if ((fin->fin_flx & FI_ICMPQUERY) != 0) { + nflags = IPN_ICMPQUERY; + dport = icmp6->icmp6_id; + } break; + default : + break; + } + + if ((nflags & IPN_TCPUDP)) { + tcp = fin->fin_dp; + dport = fin->fin_data[1]; + } + } + + ipa = fin->fin_dst6; + + READ_ENTER(&softc->ipf_nat); + + if ((fin->fin_p == IPPROTO_ICMPV6) && !(nflags & IPN_ICMPQUERY) && + (nat = ipf_nat6_icmperror(fin, &nflags, NAT_INBOUND))) + /*EMPTY*/; + else if ((fin->fin_flx & FI_FRAG) && (nat = ipf_frag_natknown(fin))) + natadd = 0; + else if ((nat = ipf_nat6_inlookup(fin, nflags|NAT_SEARCH, + (u_int)fin->fin_p, + &fin->fin_src6.in6, &ipa.in6))) { + nflags = nat->nat_flags; + } else if (fin->fin_off == 0) { + u_32_t hv, rmsk = 0; + i6addr_t *msk; + + /* + * If there is no current entry in the nat table for this IP#, + * create one for it (if there is a matching rule). + */ +maskloop: + msk = &softn->ipf_nat6_rdr_active_masks[rmsk]; + IP6_AND(&ipa, msk, &iph); + hv = NAT_HASH_FN6(&iph, 0, softn->ipf_nat_rdrrules_sz); + for (np = softn->ipf_nat_rdr_rules[hv]; np; np = np->in_rnext) { + if (np->in_ifps[0] && (np->in_ifps[0] != ifp)) + continue; + if (np->in_v[0] != 6) + continue; + if (np->in_pr[0] && (np->in_pr[0] != fin->fin_p)) + continue; + if ((np->in_flags & IPN_RF) && !(np->in_flags & nflags)) + continue; + if (np->in_flags & IPN_FILTER) { + switch (ipf_nat6_match(fin, np)) + { + case 0 : + continue; + case -1 : + rval = -1; + goto inmatchfail; + case 1 : + default : + break; + } + } else { + if (!IP6_MASKEQ(&ipa, &np->in_odstmsk6, + &np->in_odstip6)) { + continue; + } + if (np->in_odport && + ((np->in_dtop < dport) || + (dport < np->in_odport))) + continue; + } + +#ifdef IPF_V6_PROXIES + if (np->in_plabel != -1) { + if (!appr_ok(fin, tcp, np)) { + continue; + } + } +#endif + + if (np->in_flags & IPN_NO) { + np->in_hits++; + break; + } + + MUTEX_ENTER(&softn->ipf_nat_new); + nat = ipf_nat6_add(fin, np, NULL, nflags, NAT_INBOUND); + MUTEX_EXIT(&softn->ipf_nat_new); + if (nat != NULL) { + np->in_hits++; + break; + } + natfailed = -1; + } + + if ((np == NULL) && (rmsk < softn->ipf_nat6_rdr_max)) { + rmsk++; + goto maskloop; + } + } + if (nat != NULL) { + rval = ipf_nat6_in(fin, nat, natadd, nflags); + if (rval == 1) { + MUTEX_ENTER(&nat->nat_lock); + ipf_nat_update(fin, nat); + nat->nat_bytes[0] += fin->fin_plen; + nat->nat_pkts[0]++; + MUTEX_EXIT(&nat->nat_lock); + } + } else + rval = natfailed; +inmatchfail: + RWLOCK_EXIT(&softc->ipf_nat); + + switch (rval) + { + case -1 : + if (passp != NULL) { + NBUMPSIDE6D(0, ns_drop); + *passp = FR_BLOCK; + fin->fin_reason = FRB_NATV6; + } + fin->fin_flx |= FI_BADNAT; + NBUMPSIDE6D(0, ns_badnat); + break; + case 0 : + NBUMPSIDE6D(0, ns_ignored); + break; + case 1 : + NBUMPSIDE6D(0, ns_translated); + break; + } + return rval; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_nat6_in */ +/* Returns: int - -1 == packet failed NAT checks so block it, */ +/* 1 == packet was successfully translated. */ +/* Parameters: fin(I) - pointer to packet information */ +/* nat(I) - pointer to NAT structure */ +/* natadd(I) - flag indicating if it is safe to add frag cache */ +/* nflags(I) - NAT flags set for this packet */ +/* Locks Held: (READ) */ +/* */ +/* Translate a packet coming "in" on an interface. */ +/* ------------------------------------------------------------------------ */ +static int +ipf_nat6_in(fin, nat, natadd, nflags) + fr_info_t *fin; + nat_t *nat; + int natadd; + u_32_t nflags; +{ + ipf_main_softc_t *softc = fin->fin_main_soft; + ipf_nat_softc_t *softn = softc->ipf_nat_soft; + struct icmp6_hdr *icmp6; + u_short *csump; + tcphdr_t *tcp; + ipnat_t *np; + int skip; + int i; + + tcp = NULL; + csump = NULL; + np = nat->nat_ptr; + fin->fin_fr = nat->nat_fr; + + if (np != NULL) { + if ((natadd != 0) && (fin->fin_flx & FI_FRAG)) + (void) ipf_frag_natnew(softc, fin, 0, nat); + + /* ------------------------------------------------------------- */ + /* A few quick notes: */ + /* Following are test conditions prior to calling the */ + /* ipf_proxy_check routine. */ + /* */ + /* A NULL tcp indicates a non TCP/UDP packet. When dealing */ + /* with a map rule, we attempt to match the packet's */ + /* source port against in_dport, otherwise we'd compare the */ + /* packet's destination. */ + /* ------------------------------------------------------------- */ + if (np->in_apr != NULL) { + i = ipf_proxy_check(fin, nat); + if (i == -1) { + NBUMPSIDE6D(0, ns_ipf_proxy_fail); + return -1; + } + } + } + + ipf_sync_update(softc, SMC_NAT, fin, nat->nat_sync); + + /* + * Fix up checksums, not by recalculating them, but + * simply computing adjustments. + * Why only do this for some platforms on inbound packets ? + * Because for those that it is done, IP processing is yet to happen + * and so the IPv4 header checksum has not yet been evaluated. + * Perhaps it should always be done for the benefit of things like + * fast forwarding (so that it doesn't need to be recomputed) but with + * header checksum offloading, perhaps it is a moot point. + */ + + switch (nat->nat_dir) + { + case NAT_INBOUND : + if ((fin->fin_flx & FI_ICMPERR) == 0) { + fin->fin_ip6->ip6_src = nat->nat_nsrc6.in6; + fin->fin_src6 = nat->nat_nsrc6; + } + fin->fin_ip6->ip6_dst = nat->nat_ndst6.in6; + fin->fin_dst6 = nat->nat_ndst6; + break; + + case NAT_OUTBOUND : + if ((fin->fin_flx & FI_ICMPERR) == 0) { + fin->fin_ip6->ip6_src = nat->nat_odst6.in6; + fin->fin_src6 = nat->nat_odst6; + } + fin->fin_ip6->ip6_dst = nat->nat_osrc6.in6; + fin->fin_dst6 = nat->nat_osrc6; + break; + + case NAT_DIVERTIN : + { + udphdr_t *uh; + ip6_t *ip6; + mb_t *m; + + m = M_DUP(np->in_divmp); + if (m == NULL) { + NBUMPSIDE6D(0, ns_divert_dup); + return -1; + } + + ip6 = MTOD(m, ip6_t *); + ip6->ip6_plen = htons(fin->fin_plen + sizeof(udphdr_t)); + + uh = (udphdr_t *)(ip6 + 1); + uh->uh_ulen = ntohs(fin->fin_plen); + + PREP_MB_T(fin, m); + + fin->fin_ip6 = ip6; + fin->fin_plen += sizeof(ip6_t) + 8; /* UDP + new IPv6 hdr */ + fin->fin_dlen += sizeof(ip6_t) + 8; /* UDP + old IPv6 hdr */ + + nflags &= ~IPN_TCPUDPICMP; + + break; + } + + case NAT_DIVERTOUT : + { + mb_t *m; + + skip = ipf_nat6_decap(fin, nat); + if (skip <= 0) { + NBUMPSIDE6D(0, ns_decap_fail); + return -1; + } + + m = fin->fin_m; + +#if defined(MENTAT) && defined(_KERNEL) + m->b_rptr += skip; +#else + m->m_data += skip; + m->m_len -= skip; + +# ifdef M_PKTHDR + if (m->m_flags & M_PKTHDR) + m->m_pkthdr.len -= skip; +# endif +#endif + + ipf_nat_update(fin, nat); + fin->fin_flx |= FI_NATED; + if (np != NULL && np->in_tag.ipt_num[0] != 0) + fin->fin_nattag = &np->in_tag; + return 1; + /* NOTREACHED */ + } + } + if (nflags & IPN_TCPUDP) + tcp = fin->fin_dp; + + if (!(fin->fin_flx & FI_SHORT) && (fin->fin_off == 0)) { + if ((nat->nat_odport != 0) && (nflags & IPN_TCPUDP)) { + switch (nat->nat_dir) + { + case NAT_INBOUND : + tcp->th_sport = nat->nat_nsport; + fin->fin_data[0] = ntohs(nat->nat_nsport); + tcp->th_dport = nat->nat_ndport; + fin->fin_data[1] = ntohs(nat->nat_ndport); + break; + + case NAT_OUTBOUND : + tcp->th_sport = nat->nat_odport; + fin->fin_data[0] = ntohs(nat->nat_odport); + tcp->th_dport = nat->nat_osport; + fin->fin_data[1] = ntohs(nat->nat_osport); + break; + } + } + + + if ((nat->nat_odport != 0) && (nflags & IPN_ICMPQUERY)) { + icmp6 = fin->fin_dp; + + icmp6->icmp6_id = nat->nat_nicmpid; + } + + csump = ipf_nat_proto(fin, nat, nflags); + } + + /* + * The above comments do not hold for layer 4 (or higher) checksums... + */ + if (csump != NULL) { + if (nat->nat_dir == NAT_OUTBOUND) + ipf_fix_incksum(0, csump, nat->nat_sumd[0], 0); + else + ipf_fix_outcksum(0, csump, nat->nat_sumd[0], 0); + } + fin->fin_flx |= FI_NATED; + if (np != NULL && np->in_tag.ipt_num[0] != 0) + fin->fin_nattag = &np->in_tag; + return 1; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_nat6_newrewrite */ +/* Returns: int - -1 == error, 0 == success (no move), 1 == success and */ +/* allow rule to be moved if IPN_ROUNDR is set. */ +/* Parameters: fin(I) - pointer to packet information */ +/* nat(I) - pointer to NAT entry */ +/* ni(I) - pointer to structure with misc. information needed */ +/* to create new NAT entry. */ +/* Write Lock: ipf_nat */ +/* */ +/* This function is responsible for setting up an active NAT session where */ +/* we are changing both the source and destination parameters at the same */ +/* time. The loop in here works differently to elsewhere - each iteration */ +/* is responsible for changing a single parameter that can be incremented. */ +/* So one pass may increase the source IP#, next source port, next dest. IP#*/ +/* and the last destination port for a total of 4 iterations to try each. */ +/* This is done to try and exhaustively use the translation space available.*/ +/* ------------------------------------------------------------------------ */ +int +ipf_nat6_newrewrite(fin, nat, nai) + fr_info_t *fin; + nat_t *nat; + natinfo_t *nai; +{ + int src_search = 1; + int dst_search = 1; + fr_info_t frnat; + u_32_t flags; + u_short swap; + ipnat_t *np; + nat_t *natl; + int l = 0; + int changed; + + natl = NULL; + changed = -1; + np = nai->nai_np; + flags = nat->nat_flags; + bcopy((char *)fin, (char *)&frnat, sizeof(*fin)); + + nat->nat_hm = NULL; + + do { + changed = -1; + /* TRACE (l, src_search, dst_search, np) */ + + if ((src_search == 0) && (np->in_spnext == 0) && + (dst_search == 0) && (np->in_dpnext == 0)) { + if (l > 0) + return -1; + } + + /* + * Find a new source address + */ + if (ipf_nat6_nextaddr(fin, &np->in_nsrc, &frnat.fin_src6, + &frnat.fin_src6) == -1) { + return -1; + } + + if (IP6_ISZERO(&np->in_nsrcip6) && + IP6_ISONES(&np->in_nsrcmsk6)) { + src_search = 0; + if (np->in_stepnext == 0) + np->in_stepnext = 1; + + } else if (IP6_ISZERO(&np->in_nsrcip6) && + IP6_ISZERO(&np->in_nsrcmsk6)) { + src_search = 0; + if (np->in_stepnext == 0) + np->in_stepnext = 1; + + } else if (IP6_ISONES(&np->in_nsrcmsk)) { + src_search = 0; + if (np->in_stepnext == 0) + np->in_stepnext = 1; + + } else if (!IP6_ISONES(&np->in_nsrcmsk6)) { + if (np->in_stepnext == 0 && changed == -1) { + IP6_INC(&np->in_snip); + np->in_stepnext++; + changed = 0; + } + } + + if ((flags & IPN_TCPUDPICMP) != 0) { + if (np->in_spnext != 0) + frnat.fin_data[0] = np->in_spnext; + + /* + * Standard port translation. Select next port. + */ + if ((flags & IPN_FIXEDSPORT) != 0) { + np->in_stepnext = 2; + } else if ((np->in_stepnext == 1) && + (changed == -1) && (natl != NULL)) { + np->in_spnext++; + np->in_stepnext++; + changed = 1; + if (np->in_spnext > np->in_spmax) + np->in_spnext = np->in_spmin; + } + } else { + np->in_stepnext = 2; + } + np->in_stepnext &= 0x3; + + /* + * Find a new destination address + */ + /* TRACE (fin, np, l, frnat) */ + + if (ipf_nat6_nextaddr(fin, &np->in_ndst, &frnat.fin_dst6, + &frnat.fin_dst6) == -1) + return -1; + + if (IP6_ISZERO(&np->in_ndstip6) && + IP6_ISONES(&np->in_ndstmsk6)) { + dst_search = 0; + if (np->in_stepnext == 2) + np->in_stepnext = 3; + + } else if (IP6_ISZERO(&np->in_ndstip6) && + IP6_ISZERO(&np->in_ndstmsk6)) { + dst_search = 0; + if (np->in_stepnext == 2) + np->in_stepnext = 3; + + } else if (IP6_ISONES(&np->in_ndstmsk6)) { + dst_search = 0; + if (np->in_stepnext == 2) + np->in_stepnext = 3; + + } else if (!IP6_ISONES(&np->in_ndstmsk6)) { + if ((np->in_stepnext == 2) && (changed == -1) && + (natl != NULL)) { + changed = 2; + np->in_stepnext++; + IP6_INC(&np->in_dnip6); + } + } + + if ((flags & IPN_TCPUDPICMP) != 0) { + if (np->in_dpnext != 0) + frnat.fin_data[1] = np->in_dpnext; + + /* + * Standard port translation. Select next port. + */ + if ((flags & IPN_FIXEDDPORT) != 0) { + np->in_stepnext = 0; + } else if (np->in_stepnext == 3 && changed == -1) { + np->in_dpnext++; + np->in_stepnext++; + changed = 3; + if (np->in_dpnext > np->in_dpmax) + np->in_dpnext = np->in_dpmin; + } + } else { + if (np->in_stepnext == 3) + np->in_stepnext = 0; + } + + /* TRACE (frnat) */ + + /* + * Here we do a lookup of the connection as seen from + * the outside. If an IP# pair already exists, try + * again. So if you have A->B becomes C->B, you can + * also have D->E become C->E but not D->B causing + * another C->B. Also take protocol and ports into + * account when determining whether a pre-existing + * NAT setup will cause an external conflict where + * this is appropriate. + * + * fin_data[] is swapped around because we are doing a + * lookup of the packet is if it were moving in the opposite + * direction of the one we are working with now. + */ + if (flags & IPN_TCPUDP) { + swap = frnat.fin_data[0]; + frnat.fin_data[0] = frnat.fin_data[1]; + frnat.fin_data[1] = swap; + } + if (fin->fin_out == 1) { + natl = ipf_nat6_inlookup(&frnat, + flags & ~(SI_WILDP|NAT_SEARCH), + (u_int)frnat.fin_p, + &frnat.fin_dst6.in6, + &frnat.fin_src6.in6); + + } else { + natl = ipf_nat6_outlookup(&frnat, + flags & ~(SI_WILDP|NAT_SEARCH), + (u_int)frnat.fin_p, + &frnat.fin_dst6.in6, + &frnat.fin_src6.in6); + } + if (flags & IPN_TCPUDP) { + swap = frnat.fin_data[0]; + frnat.fin_data[0] = frnat.fin_data[1]; + frnat.fin_data[1] = swap; + } + + /* TRACE natl, in_stepnext, l */ + + if ((natl != NULL) && (l > 8)) /* XXX 8 is arbitrary */ + return -1; + + np->in_stepnext &= 0x3; + + l++; + changed = -1; + } while (natl != NULL); + nat->nat_osrc6 = fin->fin_src6; + nat->nat_odst6 = fin->fin_dst6; + nat->nat_nsrc6 = frnat.fin_src6; + nat->nat_ndst6 = frnat.fin_dst6; + + if ((flags & IPN_TCPUDP) != 0) { + nat->nat_osport = htons(fin->fin_data[0]); + nat->nat_odport = htons(fin->fin_data[1]); + nat->nat_nsport = htons(frnat.fin_data[0]); + nat->nat_ndport = htons(frnat.fin_data[1]); + } else if ((flags & IPN_ICMPQUERY) != 0) { + nat->nat_oicmpid = fin->fin_data[1]; + nat->nat_nicmpid = frnat.fin_data[1]; + } + + return 0; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_nat6_newdivert */ +/* Returns: int - -1 == error, 0 == success */ +/* Parameters: fin(I) - pointer to packet information */ +/* nat(I) - pointer to NAT entry */ +/* ni(I) - pointer to structure with misc. information needed */ +/* to create new NAT entry. */ +/* Write Lock: ipf_nat */ +/* */ +/* Create a new NAT divert session as defined by the NAT rule. This is */ +/* somewhat different to other NAT session creation routines because we */ +/* do not iterate through either port numbers or IP addresses, searching */ +/* for a unique mapping, however, a complimentary duplicate check is made. */ +/* ------------------------------------------------------------------------ */ +int +ipf_nat6_newdivert(fin, nat, nai) + fr_info_t *fin; + nat_t *nat; + natinfo_t *nai; +{ + ipf_main_softc_t *softc = fin->fin_main_soft; + ipf_nat_softc_t *softn = softc->ipf_nat_soft; + fr_info_t frnat; + ipnat_t *np; + nat_t *natl; + int p; + + np = nai->nai_np; + bcopy((char *)fin, (char *)&frnat, sizeof(*fin)); + + nat->nat_pr[0] = 0; + nat->nat_osrc6 = fin->fin_src6; + nat->nat_odst6 = fin->fin_dst6; + nat->nat_osport = htons(fin->fin_data[0]); + nat->nat_odport = htons(fin->fin_data[1]); + frnat.fin_src6 = np->in_snip6; + frnat.fin_dst6 = np->in_dnip6; + + if (np->in_redir & NAT_DIVERTUDP) { + frnat.fin_data[0] = np->in_spnext; + frnat.fin_data[1] = np->in_dpnext; + frnat.fin_flx |= FI_TCPUDP; + p = IPPROTO_UDP; + } else { + frnat.fin_flx &= ~FI_TCPUDP; + p = IPPROTO_IPIP; + } + + if (fin->fin_out == 1) { + natl = ipf_nat6_inlookup(&frnat, 0, p, &frnat.fin_dst6.in6, + &frnat.fin_src6.in6); + + } else { + natl = ipf_nat6_outlookup(&frnat, 0, p, &frnat.fin_dst6.in6, + &frnat.fin_src6.in6); + } + + if (natl != NULL) { + NBUMPSIDE6D(fin->fin_out, ns_divert_exist); + return -1; + } + + nat->nat_nsrc6 = frnat.fin_src6; + nat->nat_ndst6 = frnat.fin_dst6; + if (np->in_redir & NAT_DIVERTUDP) { + nat->nat_nsport = htons(frnat.fin_data[0]); + nat->nat_ndport = htons(frnat.fin_data[1]); + } + nat->nat_pr[fin->fin_out] = fin->fin_p; + nat->nat_pr[1 - fin->fin_out] = p; + + if (np->in_redir & NAT_REDIRECT) + nat->nat_dir = NAT_DIVERTIN; + else + nat->nat_dir = NAT_DIVERTOUT; + + return 0; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: nat6_builddivertmp */ +/* Returns: int - -1 == error, 0 == success */ +/* Parameters: np(I) - pointer to a NAT rule */ +/* */ +/* For divert rules, a skeleton packet representing what will be prepended */ +/* to the real packet is created. Even though we don't have the full */ +/* packet here, a checksum is calculated that we update later when we */ +/* fill in the final details. At present a 0 checksum for UDP is being set */ +/* here because it is expected that divert will be used for localhost. */ +/* ------------------------------------------------------------------------ */ +static int +ipf_nat6_builddivertmp(softn, np) + ipf_nat_softc_t *softn; + ipnat_t *np; +{ + udphdr_t *uh; + size_t len; + ip6_t *ip6; + + if ((np->in_redir & NAT_DIVERTUDP) != 0) + len = sizeof(ip6_t) + sizeof(udphdr_t); + else + len = sizeof(ip6_t); + + ALLOC_MB_T(np->in_divmp, len); + if (np->in_divmp == NULL) { + ATOMIC_INCL(softn->ipf_nat_stats.ns_divert_build); + return -1; + } + + /* + * First, the header to get the packet diverted to the new destination + */ + ip6 = MTOD(np->in_divmp, ip6_t *); + ip6->ip6_vfc = 0x60; + if ((np->in_redir & NAT_DIVERTUDP) != 0) + ip6->ip6_nxt = IPPROTO_UDP; + else + ip6->ip6_nxt = IPPROTO_IPIP; + ip6->ip6_hlim = 255; + ip6->ip6_plen = 0; + ip6->ip6_src = np->in_snip6.in6; + ip6->ip6_dst = np->in_dnip6.in6; + + if (np->in_redir & NAT_DIVERTUDP) { + uh = (udphdr_t *)((u_char *)ip6 + sizeof(*ip6)); + uh->uh_sum = 0; + uh->uh_ulen = 8; + uh->uh_sport = htons(np->in_spnext); + uh->uh_dport = htons(np->in_dpnext); + } + + return 0; +} + + +#define MINDECAP (sizeof(ip6_t) + sizeof(udphdr_t) + sizeof(ip6_t)) + +/* ------------------------------------------------------------------------ */ +/* Function: nat6_decap */ +/* Returns: int - -1 == error, 0 == success */ +/* Parameters: fin(I) - pointer to packet information */ +/* nat(I) - pointer to current NAT session */ +/* */ +/* This function is responsible for undoing a packet's encapsulation in the */ +/* reverse of an encap/divert rule. After removing the outer encapsulation */ +/* it is necessary to call ipf_makefrip() again so that the contents of 'fin'*/ +/* match the "new" packet as it may still be used by IPFilter elsewhere. */ +/* We use "dir" here as the basis for some of the expectations about the */ +/* outer header. If we return an error, the goal is to leave the original */ +/* packet information undisturbed - this falls short at the end where we'd */ +/* need to back a backup copy of "fin" - expensive. */ +/* ------------------------------------------------------------------------ */ +static int +ipf_nat6_decap(fin, nat) + fr_info_t *fin; + nat_t *nat; +{ + ipf_main_softc_t *softc = fin->fin_main_soft; + ipf_nat_softc_t *softn = softc->ipf_nat_soft; + char *hdr; + int skip; + mb_t *m; + + if ((fin->fin_flx & FI_ICMPERR) != 0) { + return 0; + } + + m = fin->fin_m; + skip = fin->fin_hlen; + + switch (nat->nat_dir) + { + case NAT_DIVERTIN : + case NAT_DIVERTOUT : + if (fin->fin_plen < MINDECAP) + return -1; + skip += sizeof(udphdr_t); + break; + + case NAT_ENCAPIN : + case NAT_ENCAPOUT : + if (fin->fin_plen < (skip + sizeof(ip6_t))) + return -1; + break; + default : + return -1; + /* NOTREACHED */ + } + + /* + * The aim here is to keep the original packet details in "fin" for + * as long as possible so that returning with an error is for the + * original packet and there is little undoing work to do. + */ + if (M_LEN(m) < skip + sizeof(ip6_t)) { + if (ipf_pr_pullup(fin, skip + sizeof(ip6_t)) == -1) + return -1; + } + + hdr = MTOD(fin->fin_m, char *); + fin->fin_ip6 = (ip6_t *)(hdr + skip); + + if (ipf_pr_pullup(fin, skip + sizeof(ip6_t)) == -1) { + NBUMPSIDE6D(fin->fin_out, ns_decap_pullup); + return -1; + } + + fin->fin_hlen = sizeof(ip6_t); + fin->fin_dlen -= skip; + fin->fin_plen -= skip; + fin->fin_ipoff += skip; + + if (ipf_makefrip(sizeof(ip6_t), (ip_t *)hdr, fin) == -1) { + NBUMPSIDE6D(fin->fin_out, ns_decap_bad); + return -1; + } + + return skip; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: nat6_nextaddr */ +/* Returns: int - -1 == bad input (no new address), */ +/* 0 == success and dst has new address */ +/* Parameters: fin(I) - pointer to packet information */ +/* na(I) - how to generate new address */ +/* old(I) - original address being replaced */ +/* dst(O) - where to put the new address */ +/* Write Lock: ipf_nat */ +/* */ +/* This function uses the contents of the "na" structure, in combination */ +/* with "old" to produce a new address to store in "dst". Not all of the */ +/* possible uses of "na" will result in a new address. */ +/* ------------------------------------------------------------------------ */ +static int +ipf_nat6_nextaddr(fin, na, old, dst) + fr_info_t *fin; + nat_addr_t *na; + i6addr_t *old, *dst; +{ + ipf_main_softc_t *softc = fin->fin_main_soft; + ipf_nat_softc_t *softn = softc->ipf_nat_soft; + i6addr_t newip, new; + u_32_t amin, amax; + int error; + + new.i6[0] = 0; + new.i6[1] = 0; + new.i6[2] = 0; + new.i6[3] = 0; + amin = na->na_addr[0].in4.s_addr; + + switch (na->na_atype) + { + case FRI_RANGE : + amax = na->na_addr[1].in4.s_addr; + break; + + case FRI_NETMASKED : + case FRI_DYNAMIC : + case FRI_NORMAL : + /* + * Compute the maximum address by adding the inverse of the + * netmask to the minimum address. + */ + amax = ~na->na_addr[1].in4.s_addr; + amax |= amin; + break; + + case FRI_LOOKUP : + break; + + case FRI_BROADCAST : + case FRI_PEERADDR : + case FRI_NETWORK : + default : + return -1; + } + + error = -1; + switch (na->na_function) + { + case IPLT_DSTLIST : + error = ipf_dstlist_select_node(fin, na->na_ptr, dst->i6, + NULL); + break; + + case IPLT_NONE : + /* + * 0/0 as the new address means leave it alone. + */ + if (na->na_addr[0].in4.s_addr == 0 && + na->na_addr[1].in4.s_addr == 0) { + new = *old; + + /* + * 0/32 means get the interface's address + */ + } else if (IP6_ISZERO(&na->na_addr[0].in6) && + IP6_ISONES(&na->na_addr[1].in6)) { + if (ipf_ifpaddr(softc, 6, na->na_atype, + fin->fin_ifp, &newip, NULL) == -1) { + NBUMPSIDE6(fin->fin_out, ns_ifpaddrfail); + return -1; + } + new = newip; + } else { + new.in6 = na->na_nextip6; + } + *dst = new; + error = 0; + break; + + default : + NBUMPSIDE6(fin->fin_out, ns_badnextaddr); + break; + } + + return error; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_nat6_nextaddrinit */ +/* Returns: int - 0 == success, else error number */ +/* Parameters: na(I) - NAT address information for generating new addr*/ +/* base(I) - start of where to find strings */ +/* initial(I) - flag indicating if it is the first call for */ +/* this "na" structure. */ +/* ifp(I) - network interface to derive address */ +/* information from. */ +/* */ +/* This function is expected to be called in two scenarious: when a new NAT */ +/* rule is loaded into the kernel and when the list of NAT rules is sync'd */ +/* up with the valid network interfaces (possibly due to them changing.) */ +/* To distinguish between these, the "initial" parameter is used. If it is */ +/* 1 then this indicates the rule has just been reloaded and 0 for when we */ +/* are updating information. This difference is important because in */ +/* instances where we are not updating address information associated with */ +/* a network interface, we don't want to disturb what the "next" address to */ +/* come out of ipf_nat6_nextaddr() will be. */ +/* ------------------------------------------------------------------------ */ +static int +ipf_nat6_nextaddrinit(softc, base, na, initial, ifp) + ipf_main_softc_t *softc; + char *base; + nat_addr_t *na; + int initial; + void *ifp; +{ + switch (na->na_atype) + { + case FRI_LOOKUP : + if (na->na_subtype == 0) { + na->na_ptr = ipf_lookup_res_num(softc, IPL_LOGNAT, + na->na_type, + na->na_num, + &na->na_func); + } else if (na->na_subtype == 1) { + na->na_ptr = ipf_lookup_res_name(softc, IPL_LOGNAT, + na->na_type, + base + na->na_num, + &na->na_func); + } + if (na->na_func == NULL) { + IPFERROR(60072); + return ESRCH; + } + if (na->na_ptr == NULL) { + IPFERROR(60073); + return ESRCH; + } + break; + case FRI_DYNAMIC : + case FRI_BROADCAST : + case FRI_NETWORK : + case FRI_NETMASKED : + case FRI_PEERADDR : + if (ifp != NULL) + (void )ipf_ifpaddr(softc, 6, na->na_atype, ifp, + &na->na_addr[0], + &na->na_addr[1]); + break; + + case FRI_SPLIT : + case FRI_RANGE : + if (initial) + na->na_nextip6 = na->na_addr[0].in6; + break; + + case FRI_NONE : + IP6_ANDASSIGN(&na->na_addr[0].in6, &na->na_addr[1].in6); + return 0; + + case FRI_NORMAL : + IP6_ANDASSIGN(&na->na_addr[0].in6, &na->na_addr[1].in6); + break; + + default : + IPFERROR(60074); + return EINVAL; + } + + if (initial && (na->na_atype == FRI_NORMAL)) { + if (IP6_ISZERO(&na->na_addr[0].in6)) { + if (IP6_ISONES(&na->na_addr[1].in6) || + IP6_ISZERO(&na->na_addr[1].in6)) { + return 0; + } + } + + na->na_nextip6 = na->na_addr[0].in6; + if (!IP6_ISONES(&na->na_addr[1].in6)) { + IP6_INC(&na->na_nextip6); + } + } + + return 0; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_nat6_icmpquerytype */ +/* Returns: int - 1 == success, 0 == failure */ +/* Parameters: icmptype(I) - ICMP type number */ +/* */ +/* Tests to see if the ICMP type number passed is a query/response type or */ +/* not. */ +/* ------------------------------------------------------------------------ */ +static int +ipf_nat6_icmpquerytype(icmptype) + int icmptype; +{ + + /* + * For the ICMP query NAT code, it is essential that both the query + * and the reply match on the NAT rule. Because the NAT structure + * does not keep track of the icmptype, and a single NAT structure + * is used for all icmp types with the same src, dest and id, we + * simply define the replies as queries as well. The funny thing is, + * altough it seems silly to call a reply a query, this is exactly + * as it is defined in the IPv4 specification + */ + + switch (icmptype) + { + + case ICMP6_ECHO_REPLY: + case ICMP6_ECHO_REQUEST: + /* route aedvertisement/solliciation is currently unsupported: */ + /* it would require rewriting the ICMP data section */ + case ICMP6_MEMBERSHIP_QUERY: + case ICMP6_MEMBERSHIP_REPORT: + case ICMP6_MEMBERSHIP_REDUCTION: + case ICMP6_WRUREQUEST: + case ICMP6_WRUREPLY: + case MLD6_MTRACE_RESP: + case MLD6_MTRACE: + return 1; + default: + return 0; + } +} +#endif /* USE_INET6 */ diff --git a/sys/contrib/ipfilter/netinet/ip_netbios_pxy.c b/sys/contrib/ipfilter/netinet/ip_netbios_pxy.c index 1a0b2a2e49e..4e8bdc66a2f 100644 --- a/sys/contrib/ipfilter/netinet/ip_netbios_pxy.c +++ b/sys/contrib/ipfilter/netinet/ip_netbios_pxy.c @@ -1,7 +1,7 @@ /* * Simple netbios-dgm transparent proxy for in-kernel use. * For use with the NAT code. - * $Id: ip_netbios_pxy.c,v 2.8.2.1 2005/08/20 13:48:23 darrenr Exp $ + * $Id$ */ /*- @@ -29,14 +29,14 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id: ip_netbios_pxy.c,v 2.8.2.1 2005/08/20 13:48:23 darrenr Exp $ + * $Id$ */ #define IPF_NETBIOS_PROXY -int ippr_netbios_init __P((void)); -void ippr_netbios_fini __P((void)); -int ippr_netbios_out __P((fr_info_t *, ap_session_t *, nat_t *)); +void ipf_p_netbios_main_load __P((void)); +void ipf_p_netbios_main_unload __P((void)); +int ipf_p_netbios_out __P((void *, fr_info_t *, ap_session_t *, nat_t *)); static frentry_t netbiosfr; @@ -45,19 +45,19 @@ int netbios_proxy_init = 0; /* * Initialize local structures. */ -int ippr_netbios_init() +void +ipf_p_netbios_main_load() { bzero((char *)&netbiosfr, sizeof(netbiosfr)); netbiosfr.fr_ref = 1; netbiosfr.fr_flags = FR_INQUE|FR_PASS|FR_QUICK|FR_KEEPSTATE; MUTEX_INIT(&netbiosfr.fr_lock, "NETBIOS proxy rule lock"); netbios_proxy_init = 1; - - return 0; } -void ippr_netbios_fini() +void +ipf_p_netbios_main_unload() { if (netbios_proxy_init == 1) { MUTEX_DESTROY(&netbiosfr.fr_lock); @@ -66,10 +66,12 @@ void ippr_netbios_fini() } -int ippr_netbios_out(fin, aps, nat) -fr_info_t *fin; -ap_session_t *aps; -nat_t *nat; +int +ipf_p_netbios_out(arg, fin, aps, nat) + void *arg; + fr_info_t *fin; + ap_session_t *aps; + nat_t *nat; { char dgmbuf[6]; int off, dlen; diff --git a/sys/contrib/ipfilter/netinet/ip_pool.c b/sys/contrib/ipfilter/netinet/ip_pool.c index eab33108a56..2a43cdb00bf 100644 --- a/sys/contrib/ipfilter/netinet/ip_pool.c +++ b/sys/contrib/ipfilter/netinet/ip_pool.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 1993-2001, 2003 by Darren Reed. + * Copyright (C) 2012 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. */ @@ -33,15 +33,10 @@ struct file; # endif #endif #include -#if !defined(linux) -# include -#endif -#include -#if defined(_KERNEL) && (!defined(__SVR4) && !defined(__svr4__)) +#if defined(_KERNEL) && !defined(SOLARIS2) # include #endif #if defined(__SVR4) || defined(__svr4__) -# include # include # ifdef _KERNEL # include @@ -53,69 +48,91 @@ struct file; # include #endif -#if defined(SOLARIS2) && !defined(_KERNEL) -# include "radix_ipf.h" -#endif -#if defined(_KERNEL) && (defined(__osf__) || defined(AIX) || \ - defined(__hpux) || defined(__sgi)) -# include "radix_ipf_local.h" -# define _RADIX_H_ -#endif +#include #include #include +#if !defined(_KERNEL) +# include "ipf.h" +#endif #include "netinet/ip_compat.h" #include "netinet/ip_fil.h" #include "netinet/ip_pool.h" - -#if defined(IPFILTER_LOOKUP) && defined(_KERNEL) && \ - ((BSD >= 198911) && !defined(__osf__) && \ - !defined(__hpux) && !defined(__sgi)) -static int rn_freenode __P((struct radix_node *, void *)); -#endif +#include "netinet/radix_ipf.h" /* END OF INCLUDES */ #if !defined(lint) static const char sccsid[] = "@(#)ip_fil.c 2.41 6/5/96 (C) 1993-2000 Darren Reed"; -static const char rcsid[] = "@(#)$Id: ip_pool.c,v 2.55.2.24 2007/10/10 09:45:37 darrenr Exp $"; +static const char rcsid[] = "@(#)$Id$"; #endif -#ifdef IPFILTER_LOOKUP +typedef struct ipf_pool_softc_s { + void *ipf_radix; + ip_pool_t *ipf_pool_list[LOOKUP_POOL_SZ]; + ipf_pool_stat_t ipf_pool_stats; + ip_pool_node_t *ipf_node_explist; +} ipf_pool_softc_t; -# if !defined(RADIX_NODE_HEAD_LOCK) || !defined(RADIX_NODE_HEAD_UNLOCK) || \ - !defined(_KERNEL) -# undef RADIX_NODE_HEAD_LOCK -# undef RADIX_NODE_HEAD_UNLOCK -# define RADIX_NODE_HEAD_LOCK(x) ; -# define RADIX_NODE_HEAD_UNLOCK(x) ; -# endif -static void ip_pool_clearnodes __P((ip_pool_t *)); -static void *ip_pool_exists __P((int, char *)); +static void ipf_pool_clearnodes __P((ipf_main_softc_t *, ipf_pool_softc_t *, + ip_pool_t *)); +static int ipf_pool_create __P((ipf_main_softc_t *, ipf_pool_softc_t *, iplookupop_t *)); +static int ipf_pool_deref __P((ipf_main_softc_t *, void *, void *)); +static int ipf_pool_destroy __P((ipf_main_softc_t *, ipf_pool_softc_t *, int, char *)); +static void *ipf_pool_exists __P((ipf_pool_softc_t *, int, char *)); +static void *ipf_pool_find __P((void *, int, char *)); +static ip_pool_node_t *ipf_pool_findeq __P((ipf_pool_softc_t *, ip_pool_t *, + addrfamily_t *, addrfamily_t *)); +static void ipf_pool_free __P((ipf_main_softc_t *, ipf_pool_softc_t *, + ip_pool_t *)); +static int ipf_pool_insert_node __P((ipf_main_softc_t *, ipf_pool_softc_t *, + ip_pool_t *, struct ip_pool_node *)); +static int ipf_pool_iter_deref __P((ipf_main_softc_t *, void *, int, int, void *)); +static int ipf_pool_iter_next __P((ipf_main_softc_t *, void *, ipftoken_t *, + ipflookupiter_t *)); +static size_t ipf_pool_flush __P((ipf_main_softc_t *, void *, iplookupflush_t *)); +static int ipf_pool_node_add __P((ipf_main_softc_t *, void *, iplookupop_t *, + int)); +static int ipf_pool_node_del __P((ipf_main_softc_t *, void *, iplookupop_t *, + int)); +static void ipf_pool_node_deref __P((ipf_pool_softc_t *, ip_pool_node_t *)); +static int ipf_pool_remove_node __P((ipf_main_softc_t *, ipf_pool_softc_t *, + ip_pool_t *, ip_pool_node_t *)); +static int ipf_pool_search __P((ipf_main_softc_t *, void *, int, + void *, u_int)); +static void *ipf_pool_soft_create __P((ipf_main_softc_t *)); +static void ipf_pool_soft_destroy __P((ipf_main_softc_t *, void *)); +static void ipf_pool_soft_fini __P((ipf_main_softc_t *, void *)); +static int ipf_pool_soft_init __P((ipf_main_softc_t *, void *)); +static int ipf_pool_stats_get __P((ipf_main_softc_t *, void *, iplookupop_t *)); +static int ipf_pool_table_add __P((ipf_main_softc_t *, void *, iplookupop_t *)); +static int ipf_pool_table_del __P((ipf_main_softc_t *, void *, iplookupop_t *)); +static void *ipf_pool_select_add_ref __P((void *, int, char *)); +static void ipf_pool_expire __P((ipf_main_softc_t *, void *)); -ip_pool_stat_t ipoolstat; -ipfrwlock_t ip_poolrw; - -/* - * Binary tree routines from Sedgewick and enhanced to do ranges of addresses. - * NOTE: Insertion *MUST* be from greatest range to least for it to work! - * These should be replaced, eventually, by something else - most notably a - * interval searching method. The important feature is to be able to find - * the best match. - * - * So why not use a radix tree for this? As the first line implies, it - * has been written to work with a _range_ of addresses. A range is not - * necessarily a match with any given netmask so what we end up dealing - * with is an interval tree. Implementations of these are hard to find - * and the one herein is far from bug free. - * - * Sigh, in the end I became convinced that the bugs the code contained did - * not make it worthwhile not using radix trees. For now the radix tree from - * 4.4 BSD is used, but this is not viewed as a long term solution. - */ -ip_pool_t *ip_pool_list[IPL_LOGSIZE] = { NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL }; +ipf_lookup_t ipf_pool_backend = { + IPLT_POOL, + ipf_pool_soft_create, + ipf_pool_soft_destroy, + ipf_pool_soft_init, + ipf_pool_soft_fini, + ipf_pool_search, + ipf_pool_flush, + ipf_pool_iter_deref, + ipf_pool_iter_next, + ipf_pool_node_add, + ipf_pool_node_del, + ipf_pool_stats_get, + ipf_pool_table_add, + ipf_pool_table_del, + ipf_pool_deref, + ipf_pool_find, + ipf_pool_select_add_ref, + NULL, + ipf_pool_expire, + NULL +}; #ifdef TEST_POOL @@ -126,96 +143,98 @@ main(argc, argv) int argc; char *argv[]; { + ip_pool_node_t node; addrfamily_t a, b; iplookupop_t op; ip_pool_t *ipo; i6addr_t ip; - RWLOCK_INIT(&ip_poolrw, "poolrw"); - ip_pool_init(); + RWLOCK_INIT(softc->ipf_poolrw, "poolrw"); + ipf_pool_init(); - bzero((char *)&a, sizeof(a)); - bzero((char *)&b, sizeof(b)); bzero((char *)&ip, sizeof(ip)); bzero((char *)&op, sizeof(op)); + bzero((char *)&node, sizeof(node)); strcpy(op.iplo_name, "0"); - if (ip_pool_create(&op) == 0) - ipo = ip_pool_exists(0, "0"); + if (ipf_pool_create(&op) == 0) + ipo = ipf_pool_exists(0, "0"); - a.adf_addr.in4.s_addr = 0x0a010203; - b.adf_addr.in4.s_addr = 0xffffffff; - ip_pool_insert(ipo, &a.adf_addr, &b.adf_addr, 1); - ip_pool_insert(ipo, &a.adf_addr, &b.adf_addr, 1); + node.ipn_addr.adf_family = AF_INET; - a.adf_addr.in4.s_addr = 0x0a000000; - b.adf_addr.in4.s_addr = 0xff000000; - ip_pool_insert(ipo, &a.adf_addr, &b.adf_addr, 0); - ip_pool_insert(ipo, &a.adf_addr, &b.adf_addr, 0); + node.ipn_addr.adf_addr.in4.s_addr = 0x0a010203; + node.ipn_mask.adf_addr.in4.s_addr = 0xffffffff; + node.ipn_info = 1; + ipf_pool_insert_node(ipo, &node); - a.adf_addr.in4.s_addr = 0x0a010100; - b.adf_addr.in4.s_addr = 0xffffff00; - ip_pool_insert(ipo, &a.adf_addr, &b.adf_addr, 1); - ip_pool_insert(ipo, &a.adf_addr, &b.adf_addr, 1); + node.ipn_addr.adf_addr.in4.s_addr = 0x0a000000; + node.ipn_mask.adf_addr.in4.s_addr = 0xff000000; + node.ipn_info = 0; + ipf_pool_insert_node(ipo, &node); - a.adf_addr.in4.s_addr = 0x0a010200; - b.adf_addr.in4.s_addr = 0xffffff00; - ip_pool_insert(ipo, &a.adf_addr, &b.adf_addr, 0); - ip_pool_insert(ipo, &a.adf_addr, &b.adf_addr, 0); + node.ipn_addr.adf_addr.in4.s_addr = 0x0a010100; + node.ipn_mask.adf_addr.in4.s_addr = 0xffffff00; + node.ipn_info = 1; + ipf_pool_insert_node(ipo, &node); - a.adf_addr.in4.s_addr = 0x0a010000; - b.adf_addr.in4.s_addr = 0xffff0000; - ip_pool_insert(ipo, &a.adf_addr, &b.adf_addr, 1); - ip_pool_insert(ipo, &a.adf_addr, &b.adf_addr, 1); + node.ipn_addr.adf_addr.in4.s_addr = 0x0a010200; + node.ipn_mask.adf_addr.in4.s_addr = 0xffffff00; + node.ipn_info = 0; + ipf_pool_insert_node(ipo, &node); - a.adf_addr.in4.s_addr = 0x0a01020f; - b.adf_addr.in4.s_addr = 0xffffffff; - ip_pool_insert(ipo, &a.adf_addr, &b.adf_addr, 1); - ip_pool_insert(ipo, &a.adf_addr, &b.adf_addr, 1); + node.ipn_addr.adf_addr.in4.s_addr = 0x0a010000; + node.ipn_mask.adf_addr.in4.s_addr = 0xffff0000; + node.ipn_info = 1; + ipf_pool_insert_node(ipo, &node); + + node.ipn_addr.adf_addr.in4.s_addr = 0x0a01020f; + node.ipn_mask.adf_addr.in4.s_addr = 0xffffffff; + node.ipn_info = 1; + ipf_pool_insert_node(ipo, &node); #ifdef DEBUG_POOL -treeprint(ipo); + treeprint(ipo); #endif ip.in4.s_addr = 0x0a00aabb; printf("search(%#x) = %d (0)\n", ip.in4.s_addr, - ip_pool_search(ipo, 4, &ip)); + ipf_pool_search(ipo, 4, &ip, 1)); ip.in4.s_addr = 0x0a000001; printf("search(%#x) = %d (0)\n", ip.in4.s_addr, - ip_pool_search(ipo, 4, &ip)); + ipf_pool_search(ipo, 4, &ip, 1)); ip.in4.s_addr = 0x0a000101; printf("search(%#x) = %d (0)\n", ip.in4.s_addr, - ip_pool_search(ipo, 4, &ip)); + ipf_pool_search(ipo, 4, &ip, 1)); ip.in4.s_addr = 0x0a010001; printf("search(%#x) = %d (1)\n", ip.in4.s_addr, - ip_pool_search(ipo, 4, &ip)); + ipf_pool_search(ipo, 4, &ip, 1)); ip.in4.s_addr = 0x0a010101; printf("search(%#x) = %d (1)\n", ip.in4.s_addr, - ip_pool_search(ipo, 4, &ip)); + ipf_pool_search(ipo, 4, &ip, 1)); ip.in4.s_addr = 0x0a010201; printf("search(%#x) = %d (0)\n", ip.in4.s_addr, - ip_pool_search(ipo, 4, &ip)); + ipf_pool_search(ipo, 4, &ip, 1)); ip.in4.s_addr = 0x0a010203; printf("search(%#x) = %d (1)\n", ip.in4.s_addr, - ip_pool_search(ipo, 4, &ip)); + ipf_pool_search(ipo, 4, &ip, 1)); ip.in4.s_addr = 0x0a01020f; printf("search(%#x) = %d (1)\n", ip.in4.s_addr, - ip_pool_search(ipo, 4, &ip)); + ipf_pool_search(ipo, 4, &ip, 1)); ip.in4.s_addr = 0x0b00aabb; printf("search(%#x) = %d (-1)\n", ip.in4.s_addr, - ip_pool_search(ipo, 4, &ip)); + ipf_pool_search(ipo, 4, &ip, 1)); #ifdef DEBUG_POOL -treeprint(ipo); + treeprint(ipo); #endif - ip_pool_fini(); + ipf_pool_fini(); return 0; } @@ -223,7 +242,7 @@ treeprint(ipo); void treeprint(ipo) -ip_pool_t *ipo; + ip_pool_t *ipo; { ip_pool_node_t *c; @@ -237,123 +256,445 @@ ip_pool_t *ipo; /* ------------------------------------------------------------------------ */ -/* Function: ip_pool_init */ -/* Returns: int - 0 = success, else error */ +/* Function: ipf_pool_soft_create */ +/* Returns: void * - NULL = failure, else pointer to local context */ +/* Parameters: softc(I) - pointer to soft context main structure */ /* */ /* Initialise the routing table data structures where required. */ /* ------------------------------------------------------------------------ */ -int ip_pool_init() +static void * +ipf_pool_soft_create(softc) + ipf_main_softc_t *softc; { + ipf_pool_softc_t *softp; - bzero((char *)&ipoolstat, sizeof(ipoolstat)); + KMALLOC(softp, ipf_pool_softc_t *); + if (softp == NULL) { + IPFERROR(70032); + return NULL; + } + + bzero((char *)softp, sizeof(*softp)); + + softp->ipf_radix = ipf_rx_create(); + if (softp->ipf_radix == NULL) { + IPFERROR(70033); + KFREE(softp); + return NULL; + } + + return softp; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_pool_soft_init */ +/* Returns: int - 0 = success, else error */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* arg(I) - pointer to local context to use */ +/* */ +/* Initialise the routing table data structures where required. */ +/* ------------------------------------------------------------------------ */ +static int +ipf_pool_soft_init(softc, arg) + ipf_main_softc_t *softc; + void *arg; +{ + ipf_pool_softc_t *softp = arg; + + ipf_rx_init(softp->ipf_radix); -#if (!defined(_KERNEL) || (BSD < 199306)) - rn_init(); -#endif return 0; } /* ------------------------------------------------------------------------ */ -/* Function: ip_pool_fini */ -/* Returns: int - 0 = success, else error */ +/* Function: ipf_pool_soft_fini */ +/* Returns: Nil */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* arg(I) - pointer to local context to use */ /* Locks: WRITE(ipf_global) */ /* */ /* Clean up all the pool data structures allocated and call the cleanup */ -/* function for the radix tree that supports the pools. ip_pool_destroy() is*/ +/* function for the radix tree that supports the pools. ipf_pool_destroy is */ /* used to delete the pools one by one to ensure they're properly freed up. */ /* ------------------------------------------------------------------------ */ -void ip_pool_fini() +static void +ipf_pool_soft_fini(softc, arg) + ipf_main_softc_t *softc; + void *arg; { + ipf_pool_softc_t *softp = arg; ip_pool_t *p, *q; int i; - for (i = 0; i <= IPL_LOGMAX; i++) { - for (q = ip_pool_list[i]; (p = q) != NULL; ) { + softc = arg; + + for (i = -1; i <= IPL_LOGMAX; i++) { + for (q = softp->ipf_pool_list[i + 1]; (p = q) != NULL; ) { q = p->ipo_next; - (void) ip_pool_destroy(i, p->ipo_name); + (void) ipf_pool_destroy(softc, arg, i, p->ipo_name); } } - -#if (!defined(_KERNEL) || (BSD < 199306)) - rn_fini(); -#endif } /* ------------------------------------------------------------------------ */ -/* Function: ip_pool_statistics */ -/* Returns: int - 0 = success, else error */ -/* Parameters: op(I) - pointer to lookup operation arguments */ +/* Function: ipf_pool_soft_destroy */ +/* Returns: Nil */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* arg(I) - pointer to local context to use */ /* */ -/* Copy the current statistics out into user space, collecting pool list */ -/* pointers as appropriate for later use. */ +/* Clean up the pool by free'ing the radix tree associated with it and free */ +/* up the pool context too. */ /* ------------------------------------------------------------------------ */ -int ip_pool_statistics(op) -iplookupop_t *op; +static void +ipf_pool_soft_destroy(softc, arg) + ipf_main_softc_t *softc; + void *arg; { - ip_pool_stat_t stats; - int unit, i, err = 0; + ipf_pool_softc_t *softp = arg; - if (op->iplo_size != sizeof(ipoolstat)) + ipf_rx_destroy(softp->ipf_radix); + + KFREE(softp); +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_pool_node_add */ +/* Returns: int - 0 = success, else error */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* arg(I) - pointer to local context to use */ +/* op(I) - pointer to lookup operatin data */ +/* */ +/* When adding a new node, a check is made to ensure that the address/mask */ +/* pair supplied has been appropriately prepared by applying the mask to */ +/* the address prior to calling for the pair to be added. */ +/* ------------------------------------------------------------------------ */ +static int +ipf_pool_node_add(softc, arg, op, uid) + ipf_main_softc_t *softc; + void *arg; + iplookupop_t *op; + int uid; +{ + ip_pool_node_t node, *m; + ip_pool_t *p; + int err; + + if (op->iplo_size != sizeof(node)) { + IPFERROR(70014); return EINVAL; + } + + err = COPYIN(op->iplo_struct, &node, sizeof(node)); + if (err != 0) { + IPFERROR(70015); + return EFAULT; + } + + p = ipf_pool_find(arg, op->iplo_unit, op->iplo_name); + if (p == NULL) { + IPFERROR(70017); + return ESRCH; + } + + if (node.ipn_addr.adf_family == AF_INET) { + if (node.ipn_addr.adf_len != offsetof(addrfamily_t, adf_addr) + + sizeof(struct in_addr)) { + IPFERROR(70028); + return EINVAL; + } + } +#ifdef USE_INET6 + else if (node.ipn_addr.adf_family == AF_INET6) { + if (node.ipn_addr.adf_len != offsetof(addrfamily_t, adf_addr) + + sizeof(struct in6_addr)) { + IPFERROR(70034); + return EINVAL; + } + } +#endif + if (node.ipn_mask.adf_len != node.ipn_addr.adf_len) { + IPFERROR(70029); + return EINVAL; + } + + /* + * Check that the address/mask pair works. + */ + if (node.ipn_addr.adf_family == AF_INET) { + if ((node.ipn_addr.adf_addr.in4.s_addr & + node.ipn_mask.adf_addr.in4.s_addr) != + node.ipn_addr.adf_addr.in4.s_addr) { + IPFERROR(70035); + return EINVAL; + } + } +#ifdef USE_INET6 + else if (node.ipn_addr.adf_family == AF_INET6) { + if (IP6_MASKNEQ(&node.ipn_addr.adf_addr.in6, + &node.ipn_mask.adf_addr.in6, + &node.ipn_addr.adf_addr.in6)) { + IPFERROR(70036); + return EINVAL; + } + } +#endif + + /* + * add an entry to a pool - return an error if it already + * exists remove an entry from a pool - if it exists + * - in both cases, the pool *must* exist! + */ + m = ipf_pool_findeq(arg, p, &node.ipn_addr, &node.ipn_mask); + if (m != NULL) { + IPFERROR(70018); + return EEXIST; + } + err = ipf_pool_insert_node(softc, arg, p, &node); - bcopy((char *)&ipoolstat, (char *)&stats, sizeof(stats)); - unit = op->iplo_unit; - if (unit == IPL_LOGALL) { - for (i = 0; i < IPL_LOGSIZE; i++) - stats.ipls_list[i] = ip_pool_list[i]; - } else if (unit >= 0 && unit < IPL_LOGSIZE) { - if (op->iplo_name[0] != '\0') - stats.ipls_list[unit] = ip_pool_exists(unit, - op->iplo_name); - else - stats.ipls_list[unit] = ip_pool_list[unit]; - } else - err = EINVAL; - if (err == 0) - err = COPYOUT(&stats, op->iplo_struct, sizeof(stats)); return err; } /* ------------------------------------------------------------------------ */ -/* Function: ip_pool_exists */ -/* Returns: int - 0 = success, else error */ -/* Parameters: ipo(I) - pointer to the pool getting the new node. */ +/* Function: ipf_pool_node_del */ +/* Returns: int - 0 = success, else error */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* arg(I) - pointer to local context to use */ +/* op(I) - pointer to lookup operatin data */ +/* */ +/* ------------------------------------------------------------------------ */ +static int +ipf_pool_node_del(softc, arg, op, uid) + ipf_main_softc_t *softc; + void *arg; + iplookupop_t *op; + int uid; +{ + ip_pool_node_t node, *m; + ip_pool_t *p; + int err; + + + if (op->iplo_size != sizeof(node)) { + IPFERROR(70019); + return EINVAL; + } + node.ipn_uid = uid; + + err = COPYIN(op->iplo_struct, &node, sizeof(node)); + if (err != 0) { + IPFERROR(70020); + return EFAULT; + } + + if (node.ipn_addr.adf_family == AF_INET) { + if (node.ipn_addr.adf_len != offsetof(addrfamily_t, adf_addr) + + sizeof(struct in_addr)) { + IPFERROR(70030); + return EINVAL; + } + } +#ifdef USE_INET6 + else if (node.ipn_addr.adf_family == AF_INET6) { + if (node.ipn_addr.adf_len != offsetof(addrfamily_t, adf_addr) + + sizeof(struct in6_addr)) { + IPFERROR(70037); + return EINVAL; + } + } +#endif + if (node.ipn_mask.adf_len != node.ipn_addr.adf_len) { + IPFERROR(70031); + return EINVAL; + } + + p = ipf_pool_find(arg, op->iplo_unit, op->iplo_name); + if (p == NULL) { + IPFERROR(70021); + return ESRCH; + } + + m = ipf_pool_findeq(arg, p, &node.ipn_addr, &node.ipn_mask); + if (m == NULL) { + IPFERROR(70022); + return ENOENT; + } + + if ((uid != 0) && (uid != m->ipn_uid)) { + IPFERROR(70024); + return EACCES; + } + + err = ipf_pool_remove_node(softc, arg, p, m); + + return err; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_pool_table_add */ +/* Returns: int - 0 = success, else error */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* arg(I) - pointer to local context to use */ +/* op(I) - pointer to lookup operatin data */ +/* */ +/* ------------------------------------------------------------------------ */ +static int +ipf_pool_table_add(softc, arg, op) + ipf_main_softc_t *softc; + void *arg; + iplookupop_t *op; +{ + int err; + + if (((op->iplo_arg & LOOKUP_ANON) == 0) && + (ipf_pool_find(arg, op->iplo_unit, op->iplo_name) != NULL)) { + IPFERROR(70023); + err = EEXIST; + } else { + err = ipf_pool_create(softc, arg, op); + } + + return err; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_pool_table_del */ +/* Returns: int - 0 = success, else error */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* arg(I) - pointer to local context to use */ +/* op(I) - pointer to lookup operatin data */ +/* */ +/* ------------------------------------------------------------------------ */ +static int +ipf_pool_table_del(softc, arg, op) + ipf_main_softc_t *softc; + void *arg; + iplookupop_t *op; +{ + return ipf_pool_destroy(softc, arg, op->iplo_unit, op->iplo_name); +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_pool_statistics */ +/* Returns: int - 0 = success, else error */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* arg(I) - pointer to local context to use */ +/* op(I) - pointer to lookup operatin data */ +/* */ +/* Copy the current statistics out into user space, collecting pool list */ +/* pointers as appropriate for later use. */ +/* ------------------------------------------------------------------------ */ +static int +ipf_pool_stats_get(softc, arg, op) + ipf_main_softc_t *softc; + void *arg; + iplookupop_t *op; +{ + ipf_pool_softc_t *softp = arg; + ipf_pool_stat_t stats; + int unit, i, err = 0; + + if (op->iplo_size != sizeof(ipf_pool_stat_t)) { + IPFERROR(70001); + return EINVAL; + } + + bcopy((char *)&softp->ipf_pool_stats, (char *)&stats, sizeof(stats)); + unit = op->iplo_unit; + if (unit == IPL_LOGALL) { + for (i = 0; i <= LOOKUP_POOL_MAX; i++) + stats.ipls_list[i] = softp->ipf_pool_list[i]; + } else if (unit >= 0 && unit <= IPL_LOGMAX) { + unit++; /* -1 => 0 */ + if (op->iplo_name[0] != '\0') + stats.ipls_list[unit] = ipf_pool_exists(softp, unit - 1, + op->iplo_name); + else + stats.ipls_list[unit] = softp->ipf_pool_list[unit]; + } else { + IPFERROR(70025); + err = EINVAL; + } + if (err == 0) { + err = COPYOUT(&stats, op->iplo_struct, sizeof(stats)); + if (err != 0) { + IPFERROR(70026); + return EFAULT; + } + } + return 0; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_pool_exists */ +/* Returns: int - 0 = success, else error */ +/* Parameters: softp(I) - pointer to soft context pool information */ +/* unit(I) - ipfilter device to which we are working on */ +/* name(I) - name of the pool */ /* */ /* Find a matching pool inside the collection of pools for a particular */ /* device, indicated by the unit number. */ /* ------------------------------------------------------------------------ */ -static void *ip_pool_exists(unit, name) -int unit; -char *name; +static void * +ipf_pool_exists(softp, unit, name) + ipf_pool_softc_t *softp; + int unit; + char *name; { ip_pool_t *p; + int i; - for (p = ip_pool_list[unit]; p != NULL; p = p->ipo_next) - if (strncmp(p->ipo_name, name, sizeof(p->ipo_name)) == 0) - break; + if (unit == IPL_LOGALL) { + for (i = 0; i <= LOOKUP_POOL_MAX; i++) { + for (p = softp->ipf_pool_list[i]; p != NULL; + p = p->ipo_next) { + if (strncmp(p->ipo_name, name, + sizeof(p->ipo_name)) == 0) + break; + } + if (p != NULL) + break; + } + } else { + for (p = softp->ipf_pool_list[unit + 1]; p != NULL; + p = p->ipo_next) + if (strncmp(p->ipo_name, name, + sizeof(p->ipo_name)) == 0) + break; + } return p; } /* ------------------------------------------------------------------------ */ -/* Function: ip_pool_find */ -/* Returns: int - 0 = success, else error */ -/* Parameters: ipo(I) - pointer to the pool getting the new node. */ +/* Function: ipf_pool_find */ +/* Returns: int - 0 = success, else error */ +/* Parameters: arg(I) - pointer to local context to use */ +/* unit(I) - ipfilter device to which we are working on */ +/* name(I) - name of the pool */ /* */ /* Find a matching pool inside the collection of pools for a particular */ /* device, indicated by the unit number. If it is marked for deletion then */ /* pretend it does not exist. */ /* ------------------------------------------------------------------------ */ -void *ip_pool_find(unit, name) -int unit; -char *name; +static void * +ipf_pool_find(arg, unit, name) + void *arg; + int unit; + char *name; { + ipf_pool_softc_t *softp = arg; ip_pool_t *p; - p = ip_pool_exists(unit, name); + p = ipf_pool_exists(softp, unit, name); if ((p != NULL) && (p->ipo_flags & IPOOL_DELETE)) return NULL; @@ -362,45 +703,75 @@ char *name; /* ------------------------------------------------------------------------ */ -/* Function: ip_pool_findeq */ +/* Function: ipf_pool_select_add_ref */ +/* Returns: int - 0 = success, else error */ +/* Parameters: arg(I) - pointer to local context to use */ +/* unit(I) - ipfilter device to which we are working on */ +/* name(I) - name of the pool */ +/* */ +/* ------------------------------------------------------------------------ */ +static void * +ipf_pool_select_add_ref(arg, unit, name) + void *arg; + int unit; + char *name; +{ + ip_pool_t *p; + + p = ipf_pool_find(arg, -1, name); + if (p == NULL) + p = ipf_pool_find(arg, unit, name); + if (p != NULL) { + ATOMIC_INC32(p->ipo_ref); + } + return p; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_pool_findeq */ /* Returns: int - 0 = success, else error */ -/* Parameters: ipo(I) - pointer to the pool getting the new node. */ -/* addr(I) - pointer to address information to delete */ -/* mask(I) - */ +/* Parameters: softp(I) - pointer to soft context pool information */ +/* ipo(I) - pointer to the pool getting the new node. */ +/* addr(I) - pointer to address information to match on */ +/* mask(I) - pointer to the address mask to match */ /* */ /* Searches for an exact match of an entry in the pool. */ /* ------------------------------------------------------------------------ */ -ip_pool_node_t *ip_pool_findeq(ipo, addr, mask) -ip_pool_t *ipo; -addrfamily_t *addr, *mask; +extern void printhostmask __P((int, u_32_t *, u_32_t *)); +static ip_pool_node_t * +ipf_pool_findeq(softp, ipo, addr, mask) + ipf_pool_softc_t *softp; + ip_pool_t *ipo; + addrfamily_t *addr, *mask; { - struct radix_node *n; - SPL_INT(s); + ipf_rdx_node_t *n; - SPL_NET(s); - RADIX_NODE_HEAD_LOCK(ipo->ipo_head); - n = ipo->ipo_head->rnh_lookup(addr, mask, ipo->ipo_head); - RADIX_NODE_HEAD_UNLOCK(ipo->ipo_head); - SPL_X(s); + n = ipo->ipo_head->lookup(ipo->ipo_head, addr, mask); return (ip_pool_node_t *)n; } /* ------------------------------------------------------------------------ */ -/* Function: ip_pool_search */ +/* Function: ipf_pool_search */ /* Returns: int - 0 == +ve match, -1 == error, 1 == -ve/no match */ -/* Parameters: tptr(I) - pointer to the pool to search */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* tptr(I) - pointer to the pool to search */ /* version(I) - IP protocol version (4 or 6) */ /* dptr(I) - pointer to address information */ +/* bytes(I) - length of packet */ /* */ /* Search the pool for a given address and return a search result. */ /* ------------------------------------------------------------------------ */ -int ip_pool_search(tptr, ipversion, dptr) -void *tptr; -int ipversion; -void *dptr; +static int +ipf_pool_search(softc, tptr, ipversion, dptr, bytes) + ipf_main_softc_t *softc; + void *tptr; + int ipversion; + void *dptr; + u_int bytes; { - struct radix_node *rn; + ipf_rdx_node_t *rn; ip_pool_node_t *m; i6addr_t *addr; addrfamily_t v; @@ -415,102 +786,154 @@ void *dptr; m = NULL; addr = (i6addr_t *)dptr; bzero(&v, sizeof(v)); - v.adf_len = offsetof(addrfamily_t, adf_addr); if (ipversion == 4) { - v.adf_len += sizeof(addr->in4); + v.adf_family = AF_INET; + v.adf_len = offsetof(addrfamily_t, adf_addr) + + sizeof(struct in_addr); v.adf_addr.in4 = addr->in4; #ifdef USE_INET6 } else if (ipversion == 6) { - v.adf_len += sizeof(addr->in6); + v.adf_family = AF_INET6; + v.adf_len = offsetof(addrfamily_t, adf_addr) + + sizeof(struct in6_addr); v.adf_addr.in6 = addr->in6; #endif } else return -1; - READ_ENTER(&ip_poolrw); + READ_ENTER(&softc->ipf_poolrw); - RADIX_NODE_HEAD_LOCK(ipo->ipo_head); - rn = ipo->ipo_head->rnh_matchaddr(&v, ipo->ipo_head); - RADIX_NODE_HEAD_UNLOCK(ipo->ipo_head); + rn = ipo->ipo_head->matchaddr(ipo->ipo_head, &v); - if ((rn != NULL) && ((rn->rn_flags & RNF_ROOT) == 0)) { + if ((rn != NULL) && (rn->root == 0)) { m = (ip_pool_node_t *)rn; ipo->ipo_hits++; + m->ipn_bytes += bytes; m->ipn_hits++; rv = m->ipn_info; } - RWLOCK_EXIT(&ip_poolrw); + RWLOCK_EXIT(&softc->ipf_poolrw); return rv; } /* ------------------------------------------------------------------------ */ -/* Function: ip_pool_insert */ -/* Returns: int - 0 = success, else error */ -/* Parameters: ipo(I) - pointer to the pool getting the new node. */ -/* addr(I) - address being added as a node */ -/* mask(I) - netmask to with the node being added */ -/* info(I) - extra information to store in this node. */ -/* Locks: WRITE(ip_poolrw) */ +/* Function: ipf_pool_insert_node */ +/* Returns: int - 0 = success, else error */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* softp(I) - pointer to soft context pool information */ +/* ipo(I) - pointer to the pool getting the new node. */ +/* node(I) - structure with address/mask to add */ +/* Locks: WRITE(ipf_poolrw) */ /* */ /* Add another node to the pool given by ipo. The three parameters passed */ /* in (addr, mask, info) shold all be stored in the node. */ /* ------------------------------------------------------------------------ */ -int ip_pool_insert(ipo, addr, mask, info) -ip_pool_t *ipo; -i6addr_t *addr, *mask; -int info; +static int +ipf_pool_insert_node(softc, softp, ipo, node) + ipf_main_softc_t *softc; + ipf_pool_softc_t *softp; + ip_pool_t *ipo; + struct ip_pool_node *node; { - struct radix_node *rn; + ipf_rdx_node_t *rn; ip_pool_node_t *x; + if ((node->ipn_addr.adf_len > sizeof(*rn)) || + (node->ipn_addr.adf_len < 4)) { + IPFERROR(70003); + return EINVAL; + } + + if ((node->ipn_mask.adf_len > sizeof(*rn)) || + (node->ipn_mask.adf_len < 4)) { + IPFERROR(70004); + return EINVAL; + } + KMALLOC(x, ip_pool_node_t *); if (x == NULL) { + IPFERROR(70002); return ENOMEM; } - bzero(x, sizeof(*x)); + *x = *node; + bzero((char *)x->ipn_nodes, sizeof(x->ipn_nodes)); + x->ipn_owner = ipo; + x->ipn_hits = 0; + x->ipn_next = NULL; + x->ipn_pnext = NULL; + x->ipn_dnext = NULL; + x->ipn_pdnext = NULL; - x->ipn_info = info; - (void)strncpy(x->ipn_name, ipo->ipo_name, sizeof(x->ipn_name)); + if (x->ipn_die != 0) { + /* + * If the new node has a given expiration time, insert it + * into the list of expiring nodes with the ones to be + * removed first added to the front of the list. The + * insertion is O(n) but it is kept sorted for quick scans + * at expiration interval checks. + */ + ip_pool_node_t *n; - bcopy(addr, &x->ipn_addr.adf_addr, sizeof(*addr)); - x->ipn_addr.adf_len = sizeof(x->ipn_addr); - bcopy(mask, &x->ipn_mask.adf_addr, sizeof(*mask)); - x->ipn_mask.adf_len = sizeof(x->ipn_mask); + x->ipn_die = softc->ipf_ticks + IPF_TTLVAL(x->ipn_die); + for (n = softp->ipf_node_explist; n != NULL; n = n->ipn_dnext) { + if (x->ipn_die < n->ipn_die) + break; + if (n->ipn_dnext == NULL) { + /* + * We've got to the last node and everything + * wanted to be expired before this new node, + * so we have to tack it on the end... + */ + n->ipn_dnext = x; + x->ipn_pdnext = &n->ipn_dnext; + n = NULL; + break; + } + } - RADIX_NODE_HEAD_LOCK(ipo->ipo_head); - rn = ipo->ipo_head->rnh_addaddr(&x->ipn_addr, &x->ipn_mask, - ipo->ipo_head, x->ipn_nodes); - RADIX_NODE_HEAD_UNLOCK(ipo->ipo_head); + if (softp->ipf_node_explist == NULL) { + softp->ipf_node_explist = x; + x->ipn_pdnext = &softp->ipf_node_explist; + } else if (n != NULL) { + x->ipn_dnext = n; + x->ipn_pdnext = n->ipn_pdnext; + n->ipn_pdnext = &x->ipn_dnext; + } + } + + rn = ipo->ipo_head->addaddr(ipo->ipo_head, &x->ipn_addr, &x->ipn_mask, + x->ipn_nodes); #ifdef DEBUG_POOL printf("Added %p at %p\n", x, rn); #endif if (rn == NULL) { KFREE(x); + IPFERROR(70005); return ENOMEM; } x->ipn_ref = 1; - x->ipn_next = ipo->ipo_list; - x->ipn_pnext = &ipo->ipo_list; - if (ipo->ipo_list != NULL) - ipo->ipo_list->ipn_pnext = &x->ipn_next; - ipo->ipo_list = x; + x->ipn_pnext = ipo->ipo_tail; + *ipo->ipo_tail = x; + ipo->ipo_tail = &x->ipn_next; - ipoolstat.ipls_nodes++; + softp->ipf_pool_stats.ipls_nodes++; return 0; } /* ------------------------------------------------------------------------ */ -/* Function: ip_pool_create */ -/* Returns: int - 0 = success, else error */ -/* Parameters: op(I) - pointer to iplookup struct with call details */ -/* Locks: WRITE(ip_poolrw) */ +/* Function: ipf_pool_create */ +/* Returns: int - 0 = success, else error */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* softp(I) - pointer to soft context pool information */ +/* op(I) - pointer to iplookup struct with call details */ +/* Locks: WRITE(ipf_poolrw) */ /* */ /* Creates a new group according to the paramters passed in via the */ /* iplookupop structure. Does not check to see if the group already exists */ @@ -522,8 +945,11 @@ int info; /* as this likely means we've tried to free a pool that is in use (flush) */ /* and now want to repopulate it with "new" data. */ /* ------------------------------------------------------------------------ */ -int ip_pool_create(op) -iplookupop_t *op; +static int +ipf_pool_create(softc, softp, op) + ipf_main_softc_t *softc; + ipf_pool_softc_t *softp; + iplookupop_t *op; { char name[FR_GROUPLEN]; int poolnum, unit; @@ -532,23 +958,27 @@ iplookupop_t *op; unit = op->iplo_unit; if ((op->iplo_arg & LOOKUP_ANON) == 0) { - h = ip_pool_exists(unit, op->iplo_name); + h = ipf_pool_exists(softp, unit, op->iplo_name); if (h != NULL) { - if ((h->ipo_flags & IPOOL_DELETE) == 0) + if ((h->ipo_flags & IPOOL_DELETE) == 0) { + IPFERROR(70006); return EEXIST; + } h->ipo_flags &= ~IPOOL_DELETE; return 0; } } KMALLOC(h, ip_pool_t *); - if (h == NULL) + if (h == NULL) { + IPFERROR(70007); return ENOMEM; + } bzero(h, sizeof(*h)); - if (rn_inithead((void **)&h->ipo_head, - offsetof(addrfamily_t, adf_addr) << 3) == 0) { + if (ipf_rx_inithead(softp->ipf_radix, &h->ipo_head) != 0) { KFREE(h); + IPFERROR(70008); return ENOMEM; } @@ -564,7 +994,7 @@ iplookupop_t *op; (void)sprintf(name, "%x", poolnum); #endif - for (p = ip_pool_list[unit]; p != NULL; ) { + for (p = softp->ipf_pool_list[unit + 1]; p != NULL; ) { if (strncmp(name, p->ipo_name, sizeof(p->ipo_name)) == 0) { poolnum++; @@ -573,7 +1003,7 @@ iplookupop_t *op; #else (void)sprintf(name, "%x", poolnum); #endif - p = ip_pool_list[unit]; + p = softp->ipf_pool_list[unit + 1]; } else p = p->ipo_next; } @@ -584,121 +1014,144 @@ iplookupop_t *op; (void)strncpy(h->ipo_name, op->iplo_name, sizeof(h->ipo_name)); } + h->ipo_radix = softp->ipf_radix; h->ipo_ref = 1; h->ipo_list = NULL; + h->ipo_tail = &h->ipo_list; h->ipo_unit = unit; - h->ipo_next = ip_pool_list[unit]; - if (ip_pool_list[unit] != NULL) - ip_pool_list[unit]->ipo_pnext = &h->ipo_next; - h->ipo_pnext = &ip_pool_list[unit]; - ip_pool_list[unit] = h; + h->ipo_next = softp->ipf_pool_list[unit + 1]; + if (softp->ipf_pool_list[unit + 1] != NULL) + softp->ipf_pool_list[unit + 1]->ipo_pnext = &h->ipo_next; + h->ipo_pnext = &softp->ipf_pool_list[unit + 1]; + softp->ipf_pool_list[unit + 1] = h; - ipoolstat.ipls_pools++; + softp->ipf_pool_stats.ipls_pools++; return 0; } /* ------------------------------------------------------------------------ */ -/* Function: ip_pool_remove */ -/* Returns: int - 0 = success, else error */ -/* Parameters: ipo(I) - pointer to the pool to remove the node from. */ -/* ipe(I) - address being deleted as a node */ -/* Locks: WRITE(ip_poolrw) */ +/* Function: ipf_pool_remove_node */ +/* Returns: int - 0 = success, else error */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* ipo(I) - pointer to the pool to remove the node from. */ +/* ipe(I) - address being deleted as a node */ +/* Locks: WRITE(ipf_poolrw) */ /* */ /* Remove a node from the pool given by ipo. */ /* ------------------------------------------------------------------------ */ -int ip_pool_remove(ipo, ipe) -ip_pool_t *ipo; -ip_pool_node_t *ipe; +static int +ipf_pool_remove_node(softc, softp, ipo, ipe) + ipf_main_softc_t *softc; + ipf_pool_softc_t *softp; + ip_pool_t *ipo; + ip_pool_node_t *ipe; { + void *ptr; + + if (ipo->ipo_tail == &ipe->ipn_next) + ipo->ipo_tail = ipe->ipn_pnext; if (ipe->ipn_pnext != NULL) *ipe->ipn_pnext = ipe->ipn_next; if (ipe->ipn_next != NULL) ipe->ipn_next->ipn_pnext = ipe->ipn_pnext; - RADIX_NODE_HEAD_LOCK(ipo->ipo_head); - ipo->ipo_head->rnh_deladdr(&ipe->ipn_addr, &ipe->ipn_mask, - ipo->ipo_head); - RADIX_NODE_HEAD_UNLOCK(ipo->ipo_head); + if (ipe->ipn_pdnext != NULL) + *ipe->ipn_pdnext = ipe->ipn_dnext; + if (ipe->ipn_dnext != NULL) + ipe->ipn_dnext->ipn_pdnext = ipe->ipn_pdnext; - ip_pool_node_deref(ipe); + ptr = ipo->ipo_head->deladdr(ipo->ipo_head, &ipe->ipn_addr, + &ipe->ipn_mask); - return 0; + if (ptr != NULL) { + ipf_pool_node_deref(softp, ipe); + return 0; + } + IPFERROR(70027); + return ESRCH; } /* ------------------------------------------------------------------------ */ -/* Function: ip_pool_destroy */ +/* Function: ipf_pool_destroy */ /* Returns: int - 0 = success, else error */ -/* Parameters: op(I) - information about the pool to remove */ -/* Locks: WRITE(ip_poolrw) or WRITE(ipf_global) */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* softp(I) - pointer to soft context pool information */ +/* unit(I) - ipfilter device to which we are working on */ +/* name(I) - name of the pool */ +/* Locks: WRITE(ipf_poolrw) or WRITE(ipf_global) */ /* */ /* Search for a pool using paramters passed in and if it's not otherwise */ /* busy, free it. If it is busy, clear all of its nodes, mark it for being */ /* deleted and return an error saying it is busy. */ /* */ -/* NOTE: Because this function is called out of ipfdetach() where ip_poolrw */ +/* NOTE: Because this function is called out of ipfdetach() where ipf_poolrw*/ /* may not be initialised, we can't use an ASSERT to enforce the locking */ -/* assertion that one of the two (ip_poolrw,ipf_global) is held. */ +/* assertion that one of the two (ipf_poolrw,ipf_global) is held. */ /* ------------------------------------------------------------------------ */ -int ip_pool_destroy(unit, name) -int unit; -char *name; +static int +ipf_pool_destroy(softc, softp, unit, name) + ipf_main_softc_t *softc; + ipf_pool_softc_t *softp; + int unit; + char *name; { ip_pool_t *ipo; - ipo = ip_pool_exists(unit, name); - if (ipo == NULL) + ipo = ipf_pool_exists(softp, unit, name); + if (ipo == NULL) { + IPFERROR(70009); return ESRCH; + } if (ipo->ipo_ref != 1) { - ip_pool_clearnodes(ipo); + ipf_pool_clearnodes(softc, softp, ipo); ipo->ipo_flags |= IPOOL_DELETE; return 0; } - ip_pool_free(ipo); + ipf_pool_free(softc, softp, ipo); return 0; } /* ------------------------------------------------------------------------ */ -/* Function: ip_pool_flush */ +/* Function: ipf_pool_flush */ /* Returns: int - number of pools deleted */ -/* Parameters: fp(I) - which pool(s) to flush */ -/* Locks: WRITE(ip_poolrw) or WRITE(ipf_global) */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* arg(I) - pointer to local context to use */ +/* fp(I) - which pool(s) to flush */ +/* Locks: WRITE(ipf_poolrw) or WRITE(ipf_global) */ /* */ /* Free all pools associated with the device that matches the unit number */ /* passed in with operation. */ /* */ -/* NOTE: Because this function is called out of ipfdetach() where ip_poolrw */ +/* NOTE: Because this function is called out of ipfdetach() where ipf_poolrw*/ /* may not be initialised, we can't use an ASSERT to enforce the locking */ -/* assertion that one of the two (ip_poolrw,ipf_global) is held. */ +/* assertion that one of the two (ipf_poolrw,ipf_global) is held. */ /* ------------------------------------------------------------------------ */ -int ip_pool_flush(fp) -iplookupflush_t *fp; +static size_t +ipf_pool_flush(softc, arg, fp) + ipf_main_softc_t *softc; + void *arg; + iplookupflush_t *fp; { + ipf_pool_softc_t *softp = arg; int i, num = 0, unit, err; ip_pool_t *p, *q; - iplookupop_t op; unit = fp->iplf_unit; - - for (i = 0; i <= IPL_LOGMAX; i++) { + for (i = -1; i <= IPL_LOGMAX; i++) { if (unit != IPLT_ALL && i != unit) continue; - for (q = ip_pool_list[i]; (p = q) != NULL; ) { - op.iplo_unit = i; - (void)strncpy(op.iplo_name, p->ipo_name, - sizeof(op.iplo_name)); + for (q = softp->ipf_pool_list[i + 1]; (p = q) != NULL; ) { q = p->ipo_next; - err = ip_pool_destroy(op.iplo_unit, op.iplo_name); + err = ipf_pool_destroy(softc, softp, i, p->ipo_name); if (err == 0) num++; - else - break; } } return num; @@ -706,125 +1159,140 @@ iplookupflush_t *fp; /* ------------------------------------------------------------------------ */ -/* Function: ip_pool_free */ +/* Function: ipf_pool_free */ /* Returns: void */ -/* Parameters: ipo(I) - pointer to pool structure */ -/* Locks: WRITE(ip_poolrw) or WRITE(ipf_global) */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* softp(I) - pointer to soft context pool information */ +/* ipo(I) - pointer to pool structure */ +/* Locks: WRITE(ipf_poolrw) or WRITE(ipf_global) */ /* */ /* Deletes the pool strucutre passed in from the list of pools and deletes */ /* all of the address information stored in it, including any tree data */ /* structures also allocated. */ /* */ -/* NOTE: Because this function is called out of ipfdetach() where ip_poolrw */ +/* NOTE: Because this function is called out of ipfdetach() where ipf_poolrw*/ /* may not be initialised, we can't use an ASSERT to enforce the locking */ -/* assertion that one of the two (ip_poolrw,ipf_global) is held. */ +/* assertion that one of the two (ipf_poolrw,ipf_global) is held. */ /* ------------------------------------------------------------------------ */ -void ip_pool_free(ipo) -ip_pool_t *ipo; +static void +ipf_pool_free(softc, softp, ipo) + ipf_main_softc_t *softc; + ipf_pool_softc_t *softp; + ip_pool_t *ipo; { - ip_pool_clearnodes(ipo); + ipf_pool_clearnodes(softc, softp, ipo); if (ipo->ipo_next != NULL) ipo->ipo_next->ipo_pnext = ipo->ipo_pnext; *ipo->ipo_pnext = ipo->ipo_next; - rn_freehead(ipo->ipo_head); + ipf_rx_freehead(ipo->ipo_head); KFREE(ipo); - ipoolstat.ipls_pools--; + softp->ipf_pool_stats.ipls_pools--; } /* ------------------------------------------------------------------------ */ -/* Function: ip_pool_clearnodes */ +/* Function: ipf_pool_clearnodes */ /* Returns: void */ -/* Parameters: ipo(I) - pointer to pool structure */ -/* Locks: WRITE(ip_poolrw) or WRITE(ipf_global) */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* softp(I) - pointer to soft context pool information */ +/* ipo(I) - pointer to pool structure */ +/* Locks: WRITE(ipf_poolrw) or WRITE(ipf_global) */ /* */ /* Deletes all nodes stored in a pool structure. */ /* ------------------------------------------------------------------------ */ -static void ip_pool_clearnodes(ipo) -ip_pool_t *ipo; +static void +ipf_pool_clearnodes(softc, softp, ipo) + ipf_main_softc_t *softc; + ipf_pool_softc_t *softp; + ip_pool_t *ipo; { - ip_pool_node_t *n; + ip_pool_node_t *n, **next; - RADIX_NODE_HEAD_LOCK(ipo->ipo_head); - while ((n = ipo->ipo_list) != NULL) { - ipo->ipo_head->rnh_deladdr(&n->ipn_addr, &n->ipn_mask, - ipo->ipo_head); - - *n->ipn_pnext = n->ipn_next; - if (n->ipn_next) - n->ipn_next->ipn_pnext = n->ipn_pnext; - - KFREE(n); - - ipoolstat.ipls_nodes--; - } - RADIX_NODE_HEAD_UNLOCK(ipo->ipo_head); + for (next = &ipo->ipo_list; (n = *next) != NULL; ) + ipf_pool_remove_node(softc, softp, ipo, n); ipo->ipo_list = NULL; } /* ------------------------------------------------------------------------ */ -/* Function: ip_pool_deref */ +/* Function: ipf_pool_deref */ /* Returns: void */ -/* Parameters: ipo(I) - pointer to pool structure */ -/* Locks: WRITE(ip_poolrw) */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* arg(I) - pointer to local context to use */ +/* pool(I) - pointer to pool structure */ +/* Locks: WRITE(ipf_poolrw) */ /* */ /* Drop the number of known references to this pool structure by one and if */ /* we arrive at zero known references, free it. */ /* ------------------------------------------------------------------------ */ -void ip_pool_deref(ipo) -ip_pool_t *ipo; +static int +ipf_pool_deref(softc, arg, pool) + ipf_main_softc_t *softc; + void *arg, *pool; { + ip_pool_t *ipo = pool; ipo->ipo_ref--; if (ipo->ipo_ref == 0) - ip_pool_free(ipo); + ipf_pool_free(softc, arg, ipo); else if ((ipo->ipo_ref == 1) && (ipo->ipo_flags & IPOOL_DELETE)) - ip_pool_destroy(ipo->ipo_unit, ipo->ipo_name); + ipf_pool_destroy(softc, arg, ipo->ipo_unit, ipo->ipo_name); + + return 0; } /* ------------------------------------------------------------------------ */ -/* Function: ip_pool_node_deref */ +/* Function: ipf_pool_node_deref */ /* Returns: void */ -/* Parameters: ipn(I) - pointer to pool structure */ -/* Locks: WRITE(ip_poolrw) */ +/* Parameters: softp(I) - pointer to soft context pool information */ +/* ipn(I) - pointer to pool structure */ +/* Locks: WRITE(ipf_poolrw) */ /* */ /* Drop a reference to the pool node passed in and if we're the last, free */ /* it all up and adjust the stats accordingly. */ /* ------------------------------------------------------------------------ */ -void ip_pool_node_deref(ipn) -ip_pool_node_t *ipn; +static void +ipf_pool_node_deref(softp, ipn) + ipf_pool_softc_t *softp; + ip_pool_node_t *ipn; { ipn->ipn_ref--; if (ipn->ipn_ref == 0) { KFREE(ipn); - ipoolstat.ipls_nodes--; + softp->ipf_pool_stats.ipls_nodes--; } } /* ------------------------------------------------------------------------ */ -/* Function: ip_pool_getnext */ +/* Function: ipf_pool_iter_next */ /* Returns: void */ -/* Parameters: token(I) - pointer to pool structure */ -/* Parameters: ilp(IO) - pointer to pool iterating structure */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* arg(I) - pointer to local context to use */ +/* token(I) - pointer to pool structure */ +/* ilp(IO) - pointer to pool iterating structure */ /* */ /* ------------------------------------------------------------------------ */ -int ip_pool_getnext(token, ilp) -ipftoken_t *token; -ipflookupiter_t *ilp; +static int +ipf_pool_iter_next(softc, arg, token, ilp) + ipf_main_softc_t *softc; + void *arg; + ipftoken_t *token; + ipflookupiter_t *ilp; { + ipf_pool_softc_t *softp = arg; ip_pool_node_t *node, zn, *nextnode; ip_pool_t *ipo, zp, *nextipo; + void *pnext; int err; err = 0; @@ -833,35 +1301,38 @@ ipflookupiter_t *ilp; ipo = NULL; nextipo = NULL; - READ_ENTER(&ip_poolrw); + READ_ENTER(&softc->ipf_poolrw); switch (ilp->ili_otype) { case IPFLOOKUPITER_LIST : ipo = token->ipt_data; if (ipo == NULL) { - nextipo = ip_pool_list[(int)ilp->ili_unit]; + nextipo = softp->ipf_pool_list[(int)ilp->ili_unit + 1]; } else { nextipo = ipo->ipo_next; } if (nextipo != NULL) { - ATOMIC_INC(nextipo->ipo_ref); + ATOMIC_INC32(nextipo->ipo_ref); token->ipt_data = nextipo; } else { bzero((char *)&zp, sizeof(zp)); nextipo = &zp; token->ipt_data = NULL; } + pnext = nextipo->ipo_next; break; case IPFLOOKUPITER_NODE : node = token->ipt_data; if (node == NULL) { - ipo = ip_pool_exists(ilp->ili_unit, ilp->ili_name); - if (ipo == NULL) + ipo = ipf_pool_exists(arg, ilp->ili_unit, + ilp->ili_name); + if (ipo == NULL) { + IPFERROR(70010); err = ESRCH; - else { + } else { nextnode = ipo->ipo_list; ipo = NULL; } @@ -870,123 +1341,149 @@ ipflookupiter_t *ilp; } if (nextnode != NULL) { - ATOMIC_INC(nextnode->ipn_ref); + ATOMIC_INC32(nextnode->ipn_ref); token->ipt_data = nextnode; } else { bzero((char *)&zn, sizeof(zn)); nextnode = &zn; token->ipt_data = NULL; } + pnext = nextnode->ipn_next; break; + default : + IPFERROR(70011); + pnext = NULL; err = EINVAL; break; } - RWLOCK_EXIT(&ip_poolrw); - + RWLOCK_EXIT(&softc->ipf_poolrw); if (err != 0) return err; switch (ilp->ili_otype) { case IPFLOOKUPITER_LIST : - if (ipo != NULL) { - WRITE_ENTER(&ip_poolrw); - ip_pool_deref(ipo); - RWLOCK_EXIT(&ip_poolrw); - } err = COPYOUT(nextipo, ilp->ili_data, sizeof(*nextipo)); - if (err != 0) + if (err != 0) { + IPFERROR(70012); err = EFAULT; + } + if (ipo != NULL) { + WRITE_ENTER(&softc->ipf_poolrw); + ipf_pool_deref(softc, softp, ipo); + RWLOCK_EXIT(&softc->ipf_poolrw); + } break; case IPFLOOKUPITER_NODE : - if (node != NULL) { - WRITE_ENTER(&ip_poolrw); - ip_pool_node_deref(node); - RWLOCK_EXIT(&ip_poolrw); - } err = COPYOUT(nextnode, ilp->ili_data, sizeof(*nextnode)); - if (err != 0) + if (err != 0) { + IPFERROR(70013); err = EFAULT; + } + if (node != NULL) { + WRITE_ENTER(&softc->ipf_poolrw); + ipf_pool_node_deref(softp, node); + RWLOCK_EXIT(&softc->ipf_poolrw); + } break; } + if (pnext == NULL) + ipf_token_mark_complete(token); return err; } /* ------------------------------------------------------------------------ */ -/* Function: ip_pool_iterderef */ +/* Function: ipf_pool_iterderef */ /* Returns: void */ -/* Parameters: ipn(I) - pointer to pool structure */ -/* Locks: WRITE(ip_poolrw) */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* arg(I) - pointer to local context to use */ +/* unit(I) - ipfilter device to which we are working on */ +/* Locks: WRITE(ipf_poolrw) */ /* */ /* ------------------------------------------------------------------------ */ -void ip_pool_iterderef(otype, unit, data) -u_int otype; -int unit; -void *data; +static int +ipf_pool_iter_deref(softc, arg, otype, unit, data) + ipf_main_softc_t *softc; + void *arg; + int otype; + int unit; + void *data; { + ipf_pool_softc_t *softp = arg; if (data == NULL) - return; + return EINVAL; if (unit < 0 || unit > IPL_LOGMAX) - return; + return EINVAL; switch (otype) { case IPFLOOKUPITER_LIST : - WRITE_ENTER(&ip_poolrw); - ip_pool_deref((ip_pool_t *)data); - RWLOCK_EXIT(&ip_poolrw); + ipf_pool_deref(softc, softp, (ip_pool_t *)data); break; case IPFLOOKUPITER_NODE : - WRITE_ENTER(&ip_poolrw); - ip_pool_node_deref((ip_pool_node_t *)data); - RWLOCK_EXIT(&ip_poolrw); + ipf_pool_node_deref(softp, (ip_pool_node_t *)data); break; default : break; } -} - -# if defined(_KERNEL) && ((BSD >= 198911) && !defined(__osf__) && \ - !defined(__hpux) && !defined(__sgi)) -static int -rn_freenode(struct radix_node *n, void *p) -{ - struct radix_node_head *rnh = p; - struct radix_node *d; - - d = rnh->rnh_deladdr(n->rn_key, NULL, rnh); - if (d != NULL) { - FreeS(d, max_keylen + 2 * sizeof (*d)); - } return 0; } -void -rn_freehead(rnh) - struct radix_node_head *rnh; +/* ------------------------------------------------------------------------ */ +/* Function: ipf_pool_expire */ +/* Returns: Nil */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* arg(I) - pointer to local context to use */ +/* */ +/* At present this function exists just to support temporary addition of */ +/* nodes to the address pool. */ +/* ------------------------------------------------------------------------ */ +static void +ipf_pool_expire(softc, arg) + ipf_main_softc_t *softc; + void *arg; { + ipf_pool_softc_t *softp = arg; + ip_pool_node_t *n; - RADIX_NODE_HEAD_LOCK(rnh); - (*rnh->rnh_walktree)(rnh, rn_freenode, rnh); - - rnh->rnh_addaddr = NULL; - rnh->rnh_deladdr = NULL; - rnh->rnh_matchaddr = NULL; - rnh->rnh_lookup = NULL; - rnh->rnh_walktree = NULL; - RADIX_NODE_HEAD_UNLOCK(rnh); - - Free(rnh); + while ((n = softp->ipf_node_explist) != NULL) { + /* + * Because the list is kept sorted on insertion, the fist + * one that dies in the future means no more work to do. + */ + if (n->ipn_die > softc->ipf_ticks) + break; + ipf_pool_remove_node(softc, softp, n->ipn_owner, n); + } } -# endif -#endif /* IPFILTER_LOOKUP */ + + + + +#ifndef _KERNEL +void +ipf_pool_dump(softc, arg) + ipf_main_softc_t *softc; + void *arg; +{ + ipf_pool_softc_t *softp = arg; + ip_pool_t *ipl; + int i; + + printf("List of configured pools\n"); + for (i = 0; i <= LOOKUP_POOL_MAX; i++) + for (ipl = softp->ipf_pool_list[i]; ipl != NULL; + ipl = ipl->ipo_next) + printpool(ipl, bcopywrap, NULL, opts, NULL); +} +#endif diff --git a/sys/contrib/ipfilter/netinet/ip_pool.h b/sys/contrib/ipfilter/netinet/ip_pool.h index 9968ef012f5..8524e607949 100644 --- a/sys/contrib/ipfilter/netinet/ip_pool.h +++ b/sys/contrib/ipfilter/netinet/ip_pool.h @@ -1,90 +1,67 @@ /* - * Copyright (C) 1993-2001, 2003 by Darren Reed. + * Copyright (C) 2012 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. * - * $Id: ip_pool.h,v 2.26.2.6 2007/10/10 09:51:43 darrenr Exp $ + * $Id$ */ #ifndef __IP_POOL_H__ #define __IP_POOL_H__ -#if defined(_KERNEL) && !defined(__osf__) && !defined(__hpux) && \ - !defined(linux) && !defined(sun) && !defined(AIX) -# include -extern void rn_freehead __P((struct radix_node_head *)); -# define FreeS(p, z) KFREES(p, z) -extern int max_keylen; -#else -# if defined(__osf__) || defined(__hpux) || defined(sun) -# include "radix_ipf_local.h" -# define radix_mask ipf_radix_mask -# define radix_node ipf_radix_node -# define radix_node_head ipf_radix_node_head -# else -# include "radix_ipf.h" -# endif -#endif #include "netinet/ip_lookup.h" +#include "radix_ipf.h" #define IP_POOL_NOMATCH 0 #define IP_POOL_POSITIVE 1 typedef struct ip_pool_node { - struct radix_node ipn_nodes[2]; + ipf_rdx_node_t ipn_nodes[2]; addrfamily_t ipn_addr; addrfamily_t ipn_mask; + int ipn_uid; int ipn_info; int ipn_ref; -char ipn_name[FR_GROUPLEN]; -u_long ipn_hits; + char ipn_name[FR_GROUPLEN]; + U_QUAD_T ipn_hits; + U_QUAD_T ipn_bytes; + u_long ipn_die; struct ip_pool_node *ipn_next, **ipn_pnext; + struct ip_pool_node *ipn_dnext, **ipn_pdnext; + struct ip_pool_s *ipn_owner; } ip_pool_node_t; typedef struct ip_pool_s { struct ip_pool_s *ipo_next; struct ip_pool_s **ipo_pnext; - struct radix_node_head *ipo_head; - ip_pool_node_t *ipo_list; - u_long ipo_hits; - int ipo_unit; - int ipo_flags; - int ipo_ref; - char ipo_name[FR_GROUPLEN]; + ipf_rdx_head_t *ipo_head; + ip_pool_node_t *ipo_list; + ip_pool_node_t **ipo_tail; + ip_pool_node_t *ipo_nextaddr; + void *ipo_radix; + u_long ipo_hits; + int ipo_unit; + int ipo_flags; + int ipo_ref; + char ipo_name[FR_GROUPLEN]; } ip_pool_t; #define IPOOL_DELETE 0x01 #define IPOOL_ANON 0x02 -typedef struct ip_pool_stat { - u_long ipls_pools; - u_long ipls_tables; - u_long ipls_nodes; - ip_pool_t *ipls_list[IPL_LOGSIZE]; -} ip_pool_stat_t; +typedef struct ipf_pool_stat { + u_long ipls_pools; + u_long ipls_tables; + u_long ipls_nodes; + ip_pool_t *ipls_list[LOOKUP_POOL_SZ]; +} ipf_pool_stat_t; +extern ipf_lookup_t ipf_pool_backend; -extern ip_pool_stat_t ipoolstat; -extern ip_pool_t *ip_pool_list[IPL_LOGSIZE]; - -extern int ip_pool_search __P((void *, int, void *)); -extern int ip_pool_init __P((void)); -extern void ip_pool_fini __P((void)); -extern int ip_pool_create __P((iplookupop_t *)); -extern int ip_pool_insert __P((ip_pool_t *, i6addr_t *, i6addr_t *, int)); -extern int ip_pool_remove __P((ip_pool_t *, ip_pool_node_t *)); -extern int ip_pool_destroy __P((int, char *)); -extern void ip_pool_free __P((ip_pool_t *)); -extern void ip_pool_deref __P((ip_pool_t *)); -extern void ip_pool_node_deref __P((ip_pool_node_t *)); -extern void *ip_pool_find __P((int, char *)); -extern ip_pool_node_t *ip_pool_findeq __P((ip_pool_t *, - addrfamily_t *, addrfamily_t *)); -extern int ip_pool_flush __P((iplookupflush_t *)); -extern int ip_pool_statistics __P((iplookupop_t *)); -extern int ip_pool_getnext __P((ipftoken_t *, ipflookupiter_t *)); -extern void ip_pool_iterderef __P((u_int, int, void *)); +#ifndef _KERNEL +extern void ipf_pool_dump __P((ipf_main_softc_t *, void *)); +#endif #endif /* __IP_POOL_H__ */ diff --git a/sys/contrib/ipfilter/netinet/ip_pptp_pxy.c b/sys/contrib/ipfilter/netinet/ip_pptp_pxy.c index 3924d4b829e..959b107cd6a 100644 --- a/sys/contrib/ipfilter/netinet/ip_pptp_pxy.c +++ b/sys/contrib/ipfilter/netinet/ip_pptp_pxy.c @@ -1,18 +1,42 @@ /* - * Copyright (C) 2002-2003 by Darren Reed + * Copyright (C) 2012 by Darren Reed. * * Simple PPTP transparent proxy for in-kernel use. For use with the NAT * code. * - * $Id: ip_pptp_pxy.c,v 2.10.2.15 2006/10/31 12:11:23 darrenr Exp $ + * $Id$ * */ #define IPF_PPTP_PROXY + + +/* + * PPTP proxy + */ +typedef struct pptp_side { + u_32_t pptps_nexthdr; + u_32_t pptps_next; + int pptps_state; + int pptps_gothdr; + int pptps_len; + int pptps_bytes; + char *pptps_wptr; + char pptps_buffer[512]; +} pptp_side_t; + +typedef struct pptp_pxy { + nat_t *pptp_nat; + struct ipstate *pptp_state; + u_short pptp_call[2]; + pptp_side_t pptp_side[2]; + ipnat_t *pptp_rule; +} pptp_pxy_t; + typedef struct pptp_hdr { - u_short pptph_len; - u_short pptph_type; - u_32_t pptph_cookie; + u_short pptph_len; + u_short pptph_type; + u_32_t pptph_cookie; } pptp_hdr_t; #define PPTP_MSGTYPE_CTL 1 @@ -33,41 +57,41 @@ typedef struct pptp_hdr { #define PPTP_MTCTL_LINKINFO 15 -int ippr_pptp_init __P((void)); -void ippr_pptp_fini __P((void)); -int ippr_pptp_new __P((fr_info_t *, ap_session_t *, nat_t *)); -void ippr_pptp_del __P((ap_session_t *)); -int ippr_pptp_inout __P((fr_info_t *, ap_session_t *, nat_t *)); -void ippr_pptp_donatstate __P((fr_info_t *, nat_t *, pptp_pxy_t *)); -int ippr_pptp_message __P((fr_info_t *, nat_t *, pptp_pxy_t *, pptp_side_t *)); -int ippr_pptp_nextmessage __P((fr_info_t *, nat_t *, pptp_pxy_t *, int)); -int ippr_pptp_mctl __P((fr_info_t *, nat_t *, pptp_pxy_t *, pptp_side_t *)); +void ipf_p_pptp_main_load __P((void)); +void ipf_p_pptp_main_unload __P((void)); +int ipf_p_pptp_new __P((void *, fr_info_t *, ap_session_t *, nat_t *)); +void ipf_p_pptp_del __P((ipf_main_softc_t *, ap_session_t *)); +int ipf_p_pptp_inout __P((void *, fr_info_t *, ap_session_t *, nat_t *)); +void ipf_p_pptp_donatstate __P((fr_info_t *, nat_t *, pptp_pxy_t *)); +int ipf_p_pptp_message __P((fr_info_t *, nat_t *, pptp_pxy_t *, pptp_side_t *)); +int ipf_p_pptp_nextmessage __P((fr_info_t *, nat_t *, pptp_pxy_t *, int)); +int ipf_p_pptp_mctl __P((fr_info_t *, nat_t *, pptp_pxy_t *, pptp_side_t *)); static frentry_t pptpfr; -int pptp_proxy_init = 0; -int ippr_pptp_debug = 0; -int ippr_pptp_gretimeout = IPF_TTLVAL(120); /* 2 minutes */ +static int pptp_proxy_init = 0; +static int ipf_p_pptp_debug = 0; +static int ipf_p_pptp_gretimeout = IPF_TTLVAL(120); /* 2 minutes */ /* * PPTP application proxy initialization. */ -int ippr_pptp_init() +void +ipf_p_pptp_main_load() { bzero((char *)&pptpfr, sizeof(pptpfr)); pptpfr.fr_ref = 1; - pptpfr.fr_age[0] = ippr_pptp_gretimeout; - pptpfr.fr_age[1] = ippr_pptp_gretimeout; + pptpfr.fr_age[0] = ipf_p_pptp_gretimeout; + pptpfr.fr_age[1] = ipf_p_pptp_gretimeout; pptpfr.fr_flags = FR_OUTQUE|FR_PASS|FR_QUICK|FR_KEEPSTATE; MUTEX_INIT(&pptpfr.fr_lock, "PPTP proxy rule lock"); pptp_proxy_init = 1; - - return 0; } -void ippr_pptp_fini() +void +ipf_p_pptp_main_unload() { if (pptp_proxy_init == 1) { MUTEX_DESTROY(&pptpfr.fr_lock); @@ -82,64 +106,83 @@ void ippr_pptp_fini() * NOTE: The printf's are broken up with %s in them to prevent them being * optimised into puts statements on FreeBSD (this doesn't exist in the kernel) */ -int ippr_pptp_new(fin, aps, nat) -fr_info_t *fin; -ap_session_t *aps; -nat_t *nat; +int +ipf_p_pptp_new(arg, fin, aps, nat) + void *arg; + fr_info_t *fin; + ap_session_t *aps; + nat_t *nat; { pptp_pxy_t *pptp; ipnat_t *ipn; + ipnat_t *np; + int size; ip_t *ip; + if (fin->fin_v != 4) + return -1; + ip = fin->fin_ip; + np = nat->nat_ptr; + size = np->in_size; - if (nat_outlookup(fin, 0, IPPROTO_GRE, nat->nat_inip, + if (ipf_nat_outlookup(fin, 0, IPPROTO_GRE, nat->nat_osrcip, ip->ip_dst) != NULL) { - if (ippr_pptp_debug > 0) - printf("ippr_pptp_new: GRE session %s\n", - "already exists"); + if (ipf_p_pptp_debug > 0) + printf("ipf_p_pptp_new: GRE session already exists\n"); return -1; } - aps->aps_psiz = sizeof(*pptp); - KMALLOCS(aps->aps_data, pptp_pxy_t *, sizeof(*pptp)); - if (aps->aps_data == NULL) { - if (ippr_pptp_debug > 0) - printf("ippr_pptp_new: malloc for aps_data %s\n", - "failed"); + KMALLOC(pptp, pptp_pxy_t *); + if (pptp == NULL) { + if (ipf_p_pptp_debug > 0) + printf("ipf_p_pptp_new: malloc for aps_data failed\n"); return -1; } + KMALLOCS(ipn, ipnat_t *, size); + if (ipn == NULL) { + KFREE(pptp); + return -1; + } + + aps->aps_data = pptp; + aps->aps_psiz = sizeof(*pptp); + bzero((char *)pptp, sizeof(*pptp)); + bzero((char *)ipn, size); + pptp->pptp_rule = ipn; /* * Create NAT rule against which the tunnel/transport mapping is * created. This is required because the current NAT rule does not * describe GRE but TCP instead. */ - pptp = aps->aps_data; - bzero((char *)pptp, sizeof(*pptp)); - ipn = &pptp->pptp_rule; + ipn->in_size = size; ipn->in_ifps[0] = fin->fin_ifp; ipn->in_apr = NULL; ipn->in_use = 1; ipn->in_hits = 1; ipn->in_ippip = 1; - if (nat->nat_dir == NAT_OUTBOUND) { - ipn->in_nip = ntohl(nat->nat_outip.s_addr); - ipn->in_outip = fin->fin_saddr; - ipn->in_redir = NAT_MAP; - } else if (nat->nat_dir == NAT_INBOUND) { - ipn->in_nip = 0; - ipn->in_outip = nat->nat_outip.s_addr; - ipn->in_redir = NAT_REDIRECT; - } - ipn->in_inip = nat->nat_inip.s_addr; - ipn->in_inmsk = 0xffffffff; - ipn->in_outmsk = 0xffffffff; - ipn->in_srcip = fin->fin_saddr; - ipn->in_srcmsk = 0xffffffff; - bcopy(nat->nat_ptr->in_ifnames[0], ipn->in_ifnames[0], - sizeof(ipn->in_ifnames[0])); - ipn->in_p = IPPROTO_GRE; + ipn->in_snip = ntohl(nat->nat_nsrcaddr); + ipn->in_nsrcaddr = fin->fin_saddr; + ipn->in_dnip = ntohl(nat->nat_ndstaddr); + ipn->in_ndstaddr = nat->nat_ndstaddr; + ipn->in_redir = np->in_redir; + ipn->in_osrcaddr = nat->nat_osrcaddr; + ipn->in_odstaddr = nat->nat_odstaddr; + ipn->in_osrcmsk = 0xffffffff; + ipn->in_nsrcmsk = 0xffffffff; + ipn->in_odstmsk = 0xffffffff; + ipn->in_ndstmsk = 0xffffffff; + ipn->in_flags = (np->in_flags | IPN_PROXYRULE); + MUTEX_INIT(&ipn->in_lock, "pptp proxy NAT rule"); + + ipn->in_namelen = np->in_namelen; + bcopy(np->in_names, ipn->in_ifnames, ipn->in_namelen); + ipn->in_ifnames[0] = np->in_ifnames[0]; + ipn->in_ifnames[1] = np->in_ifnames[1]; + + ipn->in_pr[0] = IPPROTO_GRE; + ipn->in_pr[1] = IPPROTO_GRE; pptp->pptp_side[0].pptps_wptr = pptp->pptp_side[0].pptps_buffer; pptp->pptp_side[1].pptps_wptr = pptp->pptp_side[1].pptps_buffer; @@ -147,11 +190,13 @@ nat_t *nat; } -void ippr_pptp_donatstate(fin, nat, pptp) -fr_info_t *fin; -nat_t *nat; -pptp_pxy_t *pptp; +void +ipf_p_pptp_donatstate(fin, nat, pptp) + fr_info_t *fin; + nat_t *nat; + pptp_pxy_t *pptp; { + ipf_main_softc_t *softc = fin->fin_main_soft; fr_info_t fi; grehdr_t gre; nat_t *nat2; @@ -165,8 +210,6 @@ pptp_pxy_t *pptp; if ((nat2 == NULL) || (pptp->pptp_state == NULL)) { bcopy((char *)fin, (char *)&fi, sizeof(fi)); bzero((char *)&gre, sizeof(gre)); - fi.fin_state = NULL; - fi.fin_nat = NULL; fi.fin_fi.fi_p = IPPROTO_GRE; fi.fin_fr = &pptpfr; if ((nat->nat_dir == NAT_OUTBOUND && fin->fin_out) || @@ -183,47 +226,47 @@ pptp_pxy_t *pptp; fi.fin_flx |= FI_IGNORE; fi.fin_dp = &gre; gre.gr_flags = htons(1 << 13); - if (fin->fin_out && nat->nat_dir == NAT_INBOUND) { - fi.fin_fi.fi_saddr = fin->fin_fi.fi_daddr; - fi.fin_fi.fi_daddr = nat->nat_outip.s_addr; - } else if (!fin->fin_out && nat->nat_dir == NAT_OUTBOUND) { - fi.fin_fi.fi_saddr = nat->nat_inip.s_addr; - fi.fin_fi.fi_daddr = fin->fin_fi.fi_saddr; - } + + fi.fin_fi.fi_saddr = nat->nat_osrcaddr; + fi.fin_fi.fi_daddr = nat->nat_odstaddr; } /* * Update NAT timeout/create NAT if missing. */ if (nat2 != NULL) - fr_queueback(&nat2->nat_tqe); + ipf_queueback(softc->ipf_ticks, &nat2->nat_tqe); else { - nat2 = nat_new(&fi, &pptp->pptp_rule, &pptp->pptp_nat, - NAT_SLAVE, nat->nat_dir); - pptp->pptp_nat = nat2; +#ifdef USE_MUTEXES + ipf_nat_softc_t *softn = softc->ipf_nat_soft; +#endif + + MUTEX_ENTER(&softn->ipf_nat_new); + nat2 = ipf_nat_add(&fi, pptp->pptp_rule, &pptp->pptp_nat, + NAT_SLAVE, nat->nat_dir); + MUTEX_EXIT(&softn->ipf_nat_new); if (nat2 != NULL) { - (void) nat_proto(&fi, nat2, 0); - nat_update(&fi, nat2, nat2->nat_ptr); + (void) ipf_nat_proto(&fi, nat2, 0); + MUTEX_ENTER(&nat2->nat_lock); + ipf_nat_update(&fi, nat2); + MUTEX_EXIT(&nat2->nat_lock); } } - READ_ENTER(&ipf_state); + READ_ENTER(&softc->ipf_state); if (pptp->pptp_state != NULL) { - fr_queueback(&pptp->pptp_state->is_sti); - RWLOCK_EXIT(&ipf_state); + ipf_queueback(softc->ipf_ticks, &pptp->pptp_state->is_sti); + RWLOCK_EXIT(&softc->ipf_state); } else { - RWLOCK_EXIT(&ipf_state); + RWLOCK_EXIT(&softc->ipf_state); if (nat2 != NULL) { if (nat->nat_dir == NAT_INBOUND) - fi.fin_fi.fi_daddr = nat2->nat_inip.s_addr; + fi.fin_fi.fi_daddr = nat2->nat_ndstaddr; else - fi.fin_fi.fi_saddr = nat2->nat_inip.s_addr; + fi.fin_fi.fi_saddr = nat2->nat_osrcaddr; } fi.fin_ifp = NULL; - pptp->pptp_state = fr_addstate(&fi, &pptp->pptp_state, - 0); - if (fi.fin_state != NULL) - fr_statederef((ipstate_t **)&fi.fin_state); + (void) ipf_state_add(softc, &fi, &pptp->pptp_state, 0); } ip->ip_p = p; return; @@ -235,13 +278,14 @@ pptp_pxy_t *pptp; * build it up completely (fits in our buffer) then pass it off to the message * parsing function. */ -int ippr_pptp_nextmessage(fin, nat, pptp, rev) -fr_info_t *fin; -nat_t *nat; -pptp_pxy_t *pptp; -int rev; +int +ipf_p_pptp_nextmessage(fin, nat, pptp, rev) + fr_info_t *fin; + nat_t *nat; + pptp_pxy_t *pptp; + int rev; { - static const char *funcname = "ippr_pptp_nextmessage"; + static const char *funcname = "ipf_p_pptp_nextmessage"; pptp_side_t *pptps; u_32_t start, end; pptp_hdr_t *hdr; @@ -271,7 +315,7 @@ int rev; return 0; if (pptps->pptps_next != start) { - if (ippr_pptp_debug > 5) + if (ipf_p_pptp_debug > 5) printf("%s: next (%x) != start (%x)\n", funcname, pptps->pptps_next, start); return -1; @@ -296,7 +340,7 @@ int rev; if (pptps->pptps_bytes == 8) { pptps->pptps_next += 8; if (ntohl(hdr->pptph_cookie) != 0x1a2b3c4d) { - if (ippr_pptp_debug > 1) + if (ipf_p_pptp_debug > 1) printf("%s: bad cookie (%x)\n", funcname, hdr->pptph_cookie); @@ -320,7 +364,7 @@ int rev; * bad data packet, anyway. */ if (len > sizeof(pptps->pptps_buffer)) { - if (ippr_pptp_debug > 3) + if (ipf_p_pptp_debug > 3) printf("%s: message too big (%d)\n", funcname, len); pptps->pptps_next = pptps->pptps_nexthdr; @@ -341,7 +385,7 @@ int rev; if (pptps->pptps_len > pptps->pptps_bytes) break; - ippr_pptp_message(fin, nat, pptp, pptps); + ipf_p_pptp_message(fin, nat, pptp, pptps); pptps->pptps_wptr = pptps->pptps_buffer; pptps->pptps_gothdr = 0; pptps->pptps_bytes = 0; @@ -359,18 +403,19 @@ int rev; /* * handle a complete PPTP message */ -int ippr_pptp_message(fin, nat, pptp, pptps) -fr_info_t *fin; -nat_t *nat; -pptp_pxy_t *pptp; -pptp_side_t *pptps; +int +ipf_p_pptp_message(fin, nat, pptp, pptps) + fr_info_t *fin; + nat_t *nat; + pptp_pxy_t *pptp; + pptp_side_t *pptps; { pptp_hdr_t *hdr = (pptp_hdr_t *)pptps->pptps_buffer; switch (ntohs(hdr->pptph_type)) { case PPTP_MSGTYPE_CTL : - ippr_pptp_mctl(fin, nat, pptp, pptps); + ipf_p_pptp_mctl(fin, nat, pptp, pptps); break; default : @@ -383,11 +428,12 @@ pptp_side_t *pptps; /* * handle a complete PPTP control message */ -int ippr_pptp_mctl(fin, nat, pptp, pptps) -fr_info_t *fin; -nat_t *nat; -pptp_pxy_t *pptp; -pptp_side_t *pptps; +int +ipf_p_pptp_mctl(fin, nat, pptp, pptps) + fr_info_t *fin; + nat_t *nat; + pptp_pxy_t *pptp; + pptp_side_t *pptps; { u_short *buffer = (u_short *)(pptps->pptps_buffer); pptp_side_t *pptpo; @@ -432,7 +478,7 @@ pptp_side_t *pptps; pptps->pptps_state = PPTP_MTCTL_OUTREP; pptp->pptp_call[0] = buffer[7]; pptp->pptp_call[1] = buffer[6]; - ippr_pptp_donatstate(fin, nat, pptp); + ipf_p_pptp_donatstate(fin, nat, pptp); } break; case PPTP_MTCTL_INREQ : @@ -443,7 +489,7 @@ pptp_side_t *pptps; pptps->pptps_state = PPTP_MTCTL_INREP; pptp->pptp_call[0] = buffer[7]; pptp->pptp_call[1] = buffer[6]; - ippr_pptp_donatstate(fin, nat, pptp); + ipf_p_pptp_donatstate(fin, nat, pptp); } break; case PPTP_MTCTL_INCONNECT : @@ -471,10 +517,12 @@ pptp_side_t *pptps; * For outgoing PPTP packets. refresh timeouts for NAT & state entries, if * we can. If they have disappeared, recreate them. */ -int ippr_pptp_inout(fin, aps, nat) -fr_info_t *fin; -ap_session_t *aps; -nat_t *nat; +int +ipf_p_pptp_inout(arg, fin, aps, nat) + void *arg; + fr_info_t *fin; + ap_session_t *aps; + nat_t *nat; { pptp_pxy_t *pptp; tcphdr_t *tcp; @@ -495,7 +543,7 @@ nat_t *nat; pptp->pptp_side[rev].pptps_next = ntohl(tcp->th_seq) + 1; pptp->pptp_side[rev].pptps_nexthdr = ntohl(tcp->th_seq) + 1; } - return ippr_pptp_nextmessage(fin, nat, (pptp_pxy_t *)aps->aps_data, + return ipf_p_pptp_nextmessage(fin, nat, (pptp_pxy_t *)aps->aps_data, rev); } @@ -503,8 +551,10 @@ nat_t *nat; /* * clean up after ourselves. */ -void ippr_pptp_del(aps) -ap_session_t *aps; +void +ipf_p_pptp_del(softc, aps) + ipf_main_softc_t *softc; + ap_session_t *aps; { pptp_pxy_t *pptp; @@ -516,15 +566,15 @@ ap_session_t *aps; * *_del() is on a callback from aps_free(), from nat_delete() */ - READ_ENTER(&ipf_state); + READ_ENTER(&softc->ipf_state); if (pptp->pptp_state != NULL) { - pptp->pptp_state->is_die = fr_ticks + 1; - pptp->pptp_state->is_me = NULL; - fr_queuefront(&pptp->pptp_state->is_sti); + ipf_state_setpending(softc, pptp->pptp_state); } - RWLOCK_EXIT(&ipf_state); + RWLOCK_EXIT(&softc->ipf_state); - pptp->pptp_state = NULL; - pptp->pptp_nat = NULL; + if (pptp->pptp_nat != NULL) + ipf_nat_setpending(softc, pptp->pptp_nat); + pptp->pptp_rule->in_flags |= IPN_DELETE; + ipf_nat_rule_deref(softc, &pptp->pptp_rule); } } diff --git a/sys/contrib/ipfilter/netinet/ip_proxy.c b/sys/contrib/ipfilter/netinet/ip_proxy.c index e492a33e574..39addb0f6b2 100644 --- a/sys/contrib/ipfilter/netinet/ip_proxy.c +++ b/sys/contrib/ipfilter/netinet/ip_proxy.c @@ -1,7 +1,7 @@ /* $FreeBSD$ */ /* - * Copyright (C) 1997-2003 by Darren Reed. + * Copyright (C) 2012 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. */ @@ -49,9 +49,6 @@ struct file; #if defined(_KERNEL) && (__FreeBSD_version >= 220000) # include # include -# if (__FreeBSD_version >= 300000) && !defined(IPFILTER_LKM) -# include "opt_ipfilter.h" -# endif #else # include #endif @@ -70,7 +67,6 @@ struct file; #ifdef sun # include #endif -#include #include #include #include @@ -90,9 +86,12 @@ struct file; # include #endif +/* END OF INCLUDES */ + #include "netinet/ip_ftp_pxy.c" +#include "netinet/ip_tftp_pxy.c" #include "netinet/ip_rcmd_pxy.c" -# include "netinet/ip_pptp_pxy.c" +#include "netinet/ip_pptp_pxy.c" #if defined(_KERNEL) # include "netinet/ip_irc_pxy.c" # include "netinet/ip_raudio_pxy.c" @@ -101,93 +100,474 @@ struct file; #include "netinet/ip_ipsec_pxy.c" #include "netinet/ip_rpcb_pxy.c" -/* END OF INCLUDES */ - #if !defined(lint) -static const char rcsid[] = "@(#)$Id: ip_proxy.c,v 2.62.2.21 2007/06/02 21:22:28 darrenr Exp $"; +static const char rcsid[] = "@(#)$Id$"; #endif -static int appr_fixseqack __P((fr_info_t *, ip_t *, ap_session_t *, int )); - #define AP_SESS_SIZE 53 -#if defined(_KERNEL) -int ipf_proxy_debug = 0; -#else -int ipf_proxy_debug = 2; -#endif -ap_session_t *ap_sess_tab[AP_SESS_SIZE]; -ap_session_t *ap_sess_list = NULL; -aproxy_t *ap_proxylist = NULL; -aproxy_t ap_proxies[] = { -#ifdef IPF_FTP_PROXY - { NULL, "ftp", (char)IPPROTO_TCP, 0, 0, ippr_ftp_init, ippr_ftp_fini, - ippr_ftp_new, NULL, ippr_ftp_in, ippr_ftp_out, NULL, NULL }, -#endif -#ifdef IPF_IRC_PROXY - { NULL, "irc", (char)IPPROTO_TCP, 0, 0, ippr_irc_init, ippr_irc_fini, - ippr_irc_new, NULL, NULL, ippr_irc_out, NULL, NULL }, -#endif -#ifdef IPF_RCMD_PROXY - { NULL, "rcmd", (char)IPPROTO_TCP, 0, 0, ippr_rcmd_init, ippr_rcmd_fini, - ippr_rcmd_new, NULL, ippr_rcmd_in, ippr_rcmd_out, NULL, NULL }, -#endif -#ifdef IPF_RAUDIO_PROXY - { NULL, "raudio", (char)IPPROTO_TCP, 0, 0, ippr_raudio_init, ippr_raudio_fini, - ippr_raudio_new, NULL, ippr_raudio_in, ippr_raudio_out, NULL, NULL }, -#endif -#ifdef IPF_MSNRPC_PROXY - { NULL, "msnrpc", (char)IPPROTO_TCP, 0, 0, ippr_msnrpc_init, ippr_msnrpc_fini, - ippr_msnrpc_new, NULL, ippr_msnrpc_in, ippr_msnrpc_out, NULL, NULL }, -#endif -#ifdef IPF_NETBIOS_PROXY - { NULL, "netbios", (char)IPPROTO_UDP, 0, 0, ippr_netbios_init, ippr_netbios_fini, - NULL, NULL, NULL, ippr_netbios_out, NULL, NULL }, -#endif -#ifdef IPF_IPSEC_PROXY - { NULL, "ipsec", (char)IPPROTO_UDP, 0, 0, - ippr_ipsec_init, ippr_ipsec_fini, ippr_ipsec_new, ippr_ipsec_del, - ippr_ipsec_inout, ippr_ipsec_inout, ippr_ipsec_match, NULL }, -#endif -#ifdef IPF_PPTP_PROXY - { NULL, "pptp", (char)IPPROTO_TCP, 0, 0, - ippr_pptp_init, ippr_pptp_fini, ippr_pptp_new, ippr_pptp_del, - ippr_pptp_inout, ippr_pptp_inout, NULL, NULL }, -#endif -#ifdef IPF_H323_PROXY - { NULL, "h323", (char)IPPROTO_TCP, 0, 0, ippr_h323_init, ippr_h323_fini, - ippr_h323_new, ippr_h323_del, ippr_h323_in, NULL, NULL, NULL }, - { NULL, "h245", (char)IPPROTO_TCP, 0, 0, NULL, NULL, - ippr_h245_new, NULL, NULL, ippr_h245_out, NULL, NULL }, -#endif -#ifdef IPF_RPCB_PROXY -# if 0 - { NULL, "rpcbt", (char)IPPROTO_TCP, 0, 0, - ippr_rpcb_init, ippr_rpcb_fini, ippr_rpcb_new, ippr_rpcb_del, - ippr_rpcb_in, ippr_rpcb_out, NULL, NULL }, -# endif - { NULL, "rpcbu", (char)IPPROTO_UDP, 0, 0, - ippr_rpcb_init, ippr_rpcb_fini, ippr_rpcb_new, ippr_rpcb_del, - ippr_rpcb_in, ippr_rpcb_out, NULL, NULL }, -#endif - { NULL, "", '\0', 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL } +static int ipf_proxy_fixseqack __P((fr_info_t *, ip_t *, ap_session_t *, int )); +static aproxy_t *ipf_proxy_create_clone __P((ipf_main_softc_t *, aproxy_t *)); + +typedef struct ipf_proxy_softc_s { + int ips_proxy_debug; + int ips_proxy_session_size; + ap_session_t **ips_sess_tab; + ap_session_t *ips_sess_list; + aproxy_t *ips_proxies; + int ips_init_run; + ipftuneable_t *ipf_proxy_tune; +} ipf_proxy_softc_t; + +static ipftuneable_t ipf_proxy_tuneables[] = { + { { (void *)offsetof(ipf_proxy_softc_t, ips_proxy_debug) }, + "proxy_debug", 0, 0x1f, + stsizeof(ipf_proxy_softc_t, ips_proxy_debug), + 0, NULL, NULL }, + { { NULL }, NULL, 0, 0, + 0, + 0, NULL, NULL} }; -/* - * Dynamically add a new kernel proxy. Ensure that it is unique in the - * collection compiled in and dynamically added. - */ -int appr_add(ap) -aproxy_t *ap; +static aproxy_t *ap_proxylist = NULL; +static aproxy_t ips_proxies[] = { +#ifdef IPF_FTP_PROXY + { NULL, NULL, "ftp", (char)IPPROTO_TCP, 0, 0, 0, + ipf_p_ftp_main_load, ipf_p_ftp_main_unload, + ipf_p_ftp_soft_create, ipf_p_ftp_soft_destroy, + NULL, NULL, + ipf_p_ftp_new, ipf_p_ftp_del, ipf_p_ftp_in, ipf_p_ftp_out, NULL, + NULL, NULL, NULL, NULL }, +#endif +#ifdef IPF_TFTP_PROXY + { NULL, NULL, "tftp", (char)IPPROTO_UDP, 0, 0, 0, + ipf_p_tftp_main_load, ipf_p_tftp_main_unload, + ipf_p_tftp_soft_create, ipf_p_tftp_soft_destroy, + NULL, NULL, + ipf_p_tftp_new, ipf_p_tftp_del, + ipf_p_tftp_in, ipf_p_tftp_out, NULL, + NULL, NULL, NULL, NULL }, +#endif +#ifdef IPF_IRC_PROXY + { NULL, NULL, "irc", (char)IPPROTO_TCP, 0, 0, 0, + ipf_p_irc_main_load, ipf_p_irc_main_unload, + NULL, NULL, + NULL, NULL, + ipf_p_irc_new, NULL, NULL, ipf_p_irc_out, NULL, + NULL, NULL, NULL, NULL }, +#endif +#ifdef IPF_RCMD_PROXY + { NULL, NULL, "rcmd", (char)IPPROTO_TCP, 0, 0, 0, + ipf_p_rcmd_main_load, ipf_p_rcmd_main_unload, + NULL, NULL, + NULL, NULL, + ipf_p_rcmd_new, ipf_p_rcmd_del, + ipf_p_rcmd_in, ipf_p_rcmd_out, NULL, + NULL, NULL, NULL, NULL }, +#endif +#ifdef IPF_RAUDIO_PROXY + { NULL, NULL, "raudio", (char)IPPROTO_TCP, 0, 0, 0, + ipf_p_raudio_main_load, ipf_p_raudio_main_unload, + NULL, NULL, + NULL, NULL, + ipf_p_raudio_new, NULL, ipf_p_raudio_in, ipf_p_raudio_out, NULL, + NULL, NULL, NULL, NULL }, +#endif +#ifdef IPF_MSNRPC_PROXY + { NULL, NULL, "msnrpc", (char)IPPROTO_TCP, 0, 0, 0, + ipf_p_msnrpc_init, ipf_p_msnrpc_fini, + NULL, NULL, + NULL, NULL, + ipf_p_msnrpc_new, NULL, ipf_p_msnrpc_in, ipf_p_msnrpc_out, NULL, + NULL, NULL, NULL, NULL }, +#endif +#ifdef IPF_NETBIOS_PROXY + { NULL, NULL, "netbios", (char)IPPROTO_UDP, 0, 0, 0, + ipf_p_netbios_main_load, ipf_p_netbios_main_unload, + NULL, NULL, + NULL, NULL, + NULL, NULL, NULL, ipf_p_netbios_out, NULL, + NULL, NULL, NULL, NULL }, +#endif +#ifdef IPF_IPSEC_PROXY + { NULL, NULL, "ipsec", (char)IPPROTO_UDP, 0, 0, 0, + NULL, NULL, + ipf_p_ipsec_soft_create, ipf_p_ipsec_soft_destroy, + ipf_p_ipsec_soft_init, ipf_p_ipsec_soft_fini, + ipf_p_ipsec_new, ipf_p_ipsec_del, + ipf_p_ipsec_inout, ipf_p_ipsec_inout, ipf_p_ipsec_match, + NULL, NULL, NULL, NULL }, +#endif +#ifdef IPF_DNS_PROXY + { NULL, NULL, "dns", (char)IPPROTO_UDP, 0, 0, 0, + NULL, NULL, + ipf_p_dns_soft_create, ipf_p_dns_soft_destroy, + NULL, NULL, + ipf_p_dns_new, ipf_p_ipsec_del, + ipf_p_dns_inout, ipf_p_dns_inout, ipf_p_dns_match, + ipf_p_dns_ctl, NULL, NULL, NULL }, +#endif +#ifdef IPF_PPTP_PROXY + { NULL, NULL, "pptp", (char)IPPROTO_TCP, 0, 0, 0, + ipf_p_pptp_main_load, ipf_p_pptp_main_unload, + NULL, NULL, + NULL, NULL, + ipf_p_pptp_new, ipf_p_pptp_del, + ipf_p_pptp_inout, ipf_p_pptp_inout, NULL, + NULL, NULL, NULL, NULL }, +#endif +#ifdef IPF_RPCB_PROXY +# ifndef _KERNEL + { NULL, NULL, "rpcbt", (char)IPPROTO_TCP, 0, 0, 0, + NULL, NULL, + NULL, NULL, + NULL, NULL, + ipf_p_rpcb_new, ipf_p_rpcb_del, + ipf_p_rpcb_in, ipf_p_rpcb_out, NULL, + NULL, NULL, NULL, NULL }, +# endif + { NULL, NULL, "rpcbu", (char)IPPROTO_UDP, 0, 0, 0, + ipf_p_rpcb_main_load, ipf_p_rpcb_main_unload, + NULL, NULL, + NULL, NULL, + ipf_p_rpcb_new, ipf_p_rpcb_del, + ipf_p_rpcb_in, ipf_p_rpcb_out, NULL, + NULL, NULL, NULL, NULL }, +#endif + { NULL, NULL, "", '\0', 0, 0, 0, + NULL, NULL, + NULL, NULL, + NULL, NULL, + NULL, NULL, + NULL, NULL, NULL, + NULL, NULL, NULL, NULL } +}; + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_proxy_main_load */ +/* Returns: int - 0 == success, else failure. */ +/* Parameters: Nil */ +/* */ +/* Initialise hook for kernel application proxies. */ +/* Call the initialise routine for all the compiled in kernel proxies. */ +/* ------------------------------------------------------------------------ */ +int +ipf_proxy_main_load() { + aproxy_t *ap; + + for (ap = ips_proxies; ap->apr_p; ap++) { + if (ap->apr_load != NULL) + (*ap->apr_load)(); + } + return 0; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_proxy_main_unload */ +/* Returns: int - 0 == success, else failure. */ +/* Parameters: Nil */ +/* */ +/* Unload hook for kernel application proxies. */ +/* Call the finialise routine for all the compiled in kernel proxies. */ +/* ------------------------------------------------------------------------ */ +int +ipf_proxy_main_unload() +{ + aproxy_t *ap; + + for (ap = ips_proxies; ap->apr_p; ap++) + if (ap->apr_unload != NULL) + (*ap->apr_unload)(); + for (ap = ap_proxylist; ap; ap = ap->apr_next) + if (ap->apr_unload != NULL) + (*ap->apr_unload)(); + + return 0; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_proxy_soft_create */ +/* Returns: void * - */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* */ +/* Build the structure to hold all of the run time data to support proxies. */ +/* ------------------------------------------------------------------------ */ +void * +ipf_proxy_soft_create(softc) + ipf_main_softc_t *softc; +{ + ipf_proxy_softc_t *softp; + aproxy_t *last; + aproxy_t *apn; + aproxy_t *ap; + + KMALLOC(softp, ipf_proxy_softc_t *); + if (softp == NULL) + return softp; + + bzero((char *)softp, sizeof(*softp)); + +#if defined(_KERNEL) + softp->ips_proxy_debug = 0; +#else + softp->ips_proxy_debug = 2; +#endif + softp->ips_proxy_session_size = AP_SESS_SIZE; + + softp->ipf_proxy_tune = ipf_tune_array_copy(softp, + sizeof(ipf_proxy_tuneables), + ipf_proxy_tuneables); + if (softp->ipf_proxy_tune == NULL) { + ipf_proxy_soft_destroy(softc, softp); + return NULL; + } + if (ipf_tune_array_link(softc, softp->ipf_proxy_tune) == -1) { + ipf_proxy_soft_destroy(softc, softp); + return NULL; + } + + last = NULL; + for (ap = ips_proxies; ap->apr_p; ap++) { + apn = ipf_proxy_create_clone(softc, ap); + if (apn == NULL) + goto failed; + if (last != NULL) + last->apr_next = apn; + else + softp->ips_proxies = apn; + last = apn; + } + for (ap = ips_proxies; ap != NULL; ap = ap->apr_next) { + apn = ipf_proxy_create_clone(softc, ap); + if (apn == NULL) + goto failed; + if (last != NULL) + last->apr_next = apn; + else + softp->ips_proxies = apn; + last = apn; + } + + return softp; +failed: + ipf_proxy_soft_destroy(softc, softp); + return NULL; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_proxy_soft_create */ +/* Returns: void * - */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* orig(I) - pointer to proxy definition to copy */ +/* */ +/* This function clones a proxy definition given by orig and returns a */ +/* a pointer to that copy. */ +/* ------------------------------------------------------------------------ */ +static aproxy_t * +ipf_proxy_create_clone(softc, orig) + ipf_main_softc_t *softc; + aproxy_t *orig; +{ + aproxy_t *apn; + + KMALLOC(apn, aproxy_t *); + if (apn == NULL) + return NULL; + + bcopy((char *)orig, (char *)apn, sizeof(*apn)); + apn->apr_next = NULL; + apn->apr_soft = NULL; + + if (apn->apr_create != NULL) { + apn->apr_soft = (*apn->apr_create)(softc); + if (apn->apr_soft == NULL) { + KFREE(apn); + return NULL; + } + } + + apn->apr_parent = orig; + orig->apr_clones++; + + return apn; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_proxy_soft_create */ +/* Returns: int - 0 == success, else failure. */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* arg(I) - pointer to proxy contect data */ +/* */ +/* Initialise the proxy context and walk through each of the proxies and */ +/* call its initialisation function. This allows for proxies to do any */ +/* local setup prior to actual use. */ +/* ------------------------------------------------------------------------ */ +int +ipf_proxy_soft_init(softc, arg) + ipf_main_softc_t *softc; + void *arg; +{ + ipf_proxy_softc_t *softp; + aproxy_t *ap; + u_int size; + int err; + + softp = arg; + size = softp->ips_proxy_session_size * sizeof(ap_session_t *); + + KMALLOCS(softp->ips_sess_tab, ap_session_t **, size); + + if (softp->ips_sess_tab == NULL) + return -1; + + bzero(softp->ips_sess_tab, size); + + for (ap = softp->ips_proxies; ap != NULL; ap = ap->apr_next) { + if (ap->apr_init != NULL) { + err = (*ap->apr_init)(softc, ap->apr_soft); + if (err != 0) + return -2; + } + } + softp->ips_init_run = 1; + + return 0; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_proxy_soft_create */ +/* Returns: int - 0 == success, else failure. */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* arg(I) - pointer to proxy contect data */ +/* */ +/* This function should always succeed. It is responsible for ensuring that */ +/* the proxy context can be safely called when ipf_proxy_soft_destroy is */ +/* called and suring all of the proxies have similarly been instructed. */ +/* ------------------------------------------------------------------------ */ +int +ipf_proxy_soft_fini(softc, arg) + ipf_main_softc_t *softc; + void *arg; +{ + ipf_proxy_softc_t *softp = arg; + aproxy_t *ap; + + for (ap = softp->ips_proxies; ap != NULL; ap = ap->apr_next) { + if (ap->apr_fini != NULL) { + (*ap->apr_fini)(softc, ap->apr_soft); + } + } + + if (softp->ips_sess_tab != NULL) { + KFREES(softp->ips_sess_tab, + softp->ips_proxy_session_size * sizeof(ap_session_t *)); + softp->ips_sess_tab = NULL; + } + softp->ips_init_run = 0; + + return 0; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_proxy_soft_destroy */ +/* Returns: Nil */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* arg(I) - pointer to proxy contect data */ +/* */ +/* Free up all of the local data structures allocated during creation. */ +/* ------------------------------------------------------------------------ */ +void +ipf_proxy_soft_destroy(softc, arg) + ipf_main_softc_t *softc; + void *arg; +{ + ipf_proxy_softc_t *softp = arg; + aproxy_t *ap; + + while ((ap = softp->ips_proxies) != NULL) { + softp->ips_proxies = ap->apr_next; + if (ap->apr_destroy != NULL) + (*ap->apr_destroy)(softc, ap->apr_soft); + ap->apr_parent->apr_clones--; + KFREE(ap); + } + + if (softp->ipf_proxy_tune != NULL) { + ipf_tune_array_unlink(softc, softp->ipf_proxy_tune); + KFREES(softp->ipf_proxy_tune, sizeof(ipf_proxy_tuneables)); + softp->ipf_proxy_tune = NULL; + } + + KFREE(softp); +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_proxy_flush */ +/* Returns: Nil */ +/* Parameters: arg(I) - pointer to proxy contect data */ +/* how(I) - indicates the type of flush operation */ +/* */ +/* Walk through all of the proxies and pass on the flush command as either */ +/* a flush or a clear. */ +/* ------------------------------------------------------------------------ */ +void +ipf_proxy_flush(arg, how) + void *arg; + int how; +{ + ipf_proxy_softc_t *softp = arg; + aproxy_t *ap; + + switch (how) + { + case 0 : + for (ap = softp->ips_proxies; ap; ap = ap->apr_next) + if (ap->apr_flush != NULL) + (*ap->apr_flush)(ap, how); + break; + case 1 : + for (ap = softp->ips_proxies; ap; ap = ap->apr_next) + if (ap->apr_clear != NULL) + (*ap->apr_clear)(ap); + break; + default : + break; + } +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_proxy_add */ +/* Returns: int - 0 == success, else failure. */ +/* Parameters: ap(I) - pointer to proxy structure */ +/* */ +/* Dynamically add a new kernel proxy. Ensure that it is unique in the */ +/* collection compiled in and dynamically added. */ +/* ------------------------------------------------------------------------ */ +int +ipf_proxy_add(arg, ap) + void *arg; + aproxy_t *ap; +{ + ipf_proxy_softc_t *softp = arg; + aproxy_t *a; - for (a = ap_proxies; a->apr_p; a++) + for (a = ips_proxies; a->apr_p; a++) if ((a->apr_p == ap->apr_p) && !strncmp(a->apr_label, ap->apr_label, sizeof(ap->apr_label))) { - if (ipf_proxy_debug > 1) - printf("appr_add: %s/%d already present (B)\n", + if (softp->ips_proxy_debug & 0x01) + printf("ipf_proxy_add: %s/%d present (B)\n", a->apr_label, a->apr_p); return -1; } @@ -196,89 +576,113 @@ aproxy_t *ap; if ((a->apr_p == ap->apr_p) && !strncmp(a->apr_label, ap->apr_label, sizeof(ap->apr_label))) { - if (ipf_proxy_debug > 1) - printf("appr_add: %s/%d already present (D)\n", + if (softp->ips_proxy_debug & 0x01) + printf("ipf_proxy_add: %s/%d present (D)\n", a->apr_label, a->apr_p); return -1; } ap->apr_next = ap_proxylist; ap_proxylist = ap; - if (ap->apr_init != NULL) - return (*ap->apr_init)(); + if (ap->apr_load != NULL) + (*ap->apr_load)(); return 0; } -/* - * Check to see if the proxy this control request has come through for - * exists, and if it does and it has a control function then invoke that - * control function. - */ -int appr_ctl(ctl) -ap_ctl_t *ctl; +/* ------------------------------------------------------------------------ */ +/* Function: ipf_proxy_ctl */ +/* Returns: int - 0 == success, else error */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* arg(I) - pointer to proxy context */ +/* ctl(I) - pointer to proxy control structure */ +/* */ +/* Check to see if the proxy this control request has come through for */ +/* exists, and if it does and it has a control function then invoke that */ +/* control function. */ +/* ------------------------------------------------------------------------ */ +int +ipf_proxy_ctl(softc, arg, ctl) + ipf_main_softc_t *softc; + void *arg; + ap_ctl_t *ctl; { + ipf_proxy_softc_t *softp = arg; aproxy_t *a; int error; - a = appr_lookup(ctl->apc_p, ctl->apc_label); + a = ipf_proxy_lookup(arg, ctl->apc_p, ctl->apc_label); if (a == NULL) { - if (ipf_proxy_debug > 1) - printf("appr_ctl: can't find %s/%d\n", + if (softp->ips_proxy_debug & 0x01) + printf("ipf_proxy_ctl: can't find %s/%d\n", ctl->apc_label, ctl->apc_p); + IPFERROR(80001); error = ESRCH; } else if (a->apr_ctl == NULL) { - if (ipf_proxy_debug > 1) - printf("appr_ctl: no ctl function for %s/%d\n", + if (softp->ips_proxy_debug & 0x01) + printf("ipf_proxy_ctl: no ctl function for %s/%d\n", ctl->apc_label, ctl->apc_p); + IPFERROR(80002); error = ENXIO; } else { - error = (*a->apr_ctl)(a, ctl); - if ((error != 0) && (ipf_proxy_debug > 1)) - printf("appr_ctl: %s/%d ctl error %d\n", + error = (*a->apr_ctl)(softc, a->apr_soft, ctl); + if ((error != 0) && (softp->ips_proxy_debug & 0x02)) + printf("ipf_proxy_ctl: %s/%d ctl error %d\n", a->apr_label, a->apr_p, error); } return error; } -/* - * Delete a proxy that has been added dynamically from those available. - * If it is in use, return 1 (do not destroy NOW), not in use 0 or -1 - * if it cannot be matched. - */ -int appr_del(ap) -aproxy_t *ap; +/* ------------------------------------------------------------------------ */ +/* Function: ipf_proxy_del */ +/* Returns: int - 0 == success, else failure. */ +/* Parameters: ap(I) - pointer to proxy structure */ +/* */ +/* Delete a proxy that has been added dynamically from those available. */ +/* If it is in use, return 1 (do not destroy NOW), not in use 0 or -1 */ +/* if it cannot be matched. */ +/* ------------------------------------------------------------------------ */ +int +ipf_proxy_del(ap) + aproxy_t *ap; { aproxy_t *a, **app; - for (app = &ap_proxylist; ((a = *app) != NULL); app = &a->apr_next) + for (app = &ap_proxylist; ((a = *app) != NULL); app = &a->apr_next) { if (a == ap) { a->apr_flags |= APR_DELETE; - *app = a->apr_next; - if (ap->apr_ref != 0) { - if (ipf_proxy_debug > 2) - printf("appr_del: orphaning %s/%d\n", - ap->apr_label, ap->apr_p); - return 1; + if (ap->apr_ref == 0 && ap->apr_clones == 0) { + *app = a->apr_next; + return 0; } - return 0; + return 1; } - if (ipf_proxy_debug > 1) - printf("appr_del: proxy %lx not found\n", (u_long)ap); + } + return -1; } -/* - * Return 1 if the packet is a good match against a proxy, else 0. - */ -int appr_ok(fin, tcp, nat) -fr_info_t *fin; -tcphdr_t *tcp; -ipnat_t *nat; +/* ------------------------------------------------------------------------ */ +/* Function: ipf_proxy_ok */ +/* Returns: int - 1 == good match else not. */ +/* Parameters: fin(I) - pointer to packet information */ +/* tcp(I) - pointer to TCP/UDP header */ +/* nat(I) - pointer to current NAT session */ +/* */ +/* This function extends the NAT matching to ensure that a packet that has */ +/* arrived matches the proxy information attached to the NAT rule. Notably, */ +/* if the proxy is scheduled to be deleted then packets will not match the */ +/* rule even if the rule is still active. */ +/* ------------------------------------------------------------------------ */ +int +ipf_proxy_ok(fin, tcp, np) + fr_info_t *fin; + tcphdr_t *tcp; + ipnat_t *np; { - aproxy_t *apr = nat->in_apr; - u_short dport = nat->in_dport; + aproxy_t *apr = np->in_apr; + u_short dport = np->in_odport; if ((apr == NULL) || (apr->apr_flags & APR_DELETE) || (fin->fin_p != apr->apr_p)) @@ -289,14 +693,26 @@ ipnat_t *nat; } -int appr_ioctl(data, cmd, mode, ctx) -caddr_t data; -ioctlcmd_t cmd; -int mode; -void *ctx; +/* ------------------------------------------------------------------------ */ +/* Function: ipf_proxy_ioctl */ +/* Returns: int - 0 == success, else error */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* data(I) - pointer to ioctl data */ +/* cmd(I) - ioctl command */ +/* mode(I) - mode bits for device */ +/* ctx(I) - pointer to context information */ +/* */ +/* ------------------------------------------------------------------------ */ +int +ipf_proxy_ioctl(softc, data, cmd, mode, ctx) + ipf_main_softc_t *softc; + caddr_t data; + ioctlcmd_t cmd; + int mode; + void *ctx; { ap_ctl_t ctl; - u_char *ptr; + caddr_t ptr; int error; mode = mode; /* LINT */ @@ -304,17 +720,19 @@ void *ctx; switch (cmd) { case SIOCPROXY : - error = BCOPYIN(data, &ctl, sizeof(ctl)); - if (error != 0) - return EFAULT; + error = ipf_inobj(softc, data, NULL, &ctl, IPFOBJ_PROXYCTL); + if (error != 0) { + return error; + } ptr = NULL; if (ctl.apc_dsize > 0) { - KMALLOCS(ptr, u_char *, ctl.apc_dsize); - if (ptr == NULL) + KMALLOCS(ptr, caddr_t, ctl.apc_dsize); + if (ptr == NULL) { + IPFERROR(80003); error = ENOMEM; - else { - error = copyinptr(ctl.apc_data, ptr, + } else { + error = copyinptr(softc, ctl.apc_data, ptr, ctl.apc_dsize); if (error == 0) ctl.apc_data = ptr; @@ -325,49 +743,61 @@ void *ctx; } if (error == 0) - error = appr_ctl(&ctl); + error = ipf_proxy_ctl(softc, softc->ipf_proxy_soft, + &ctl); - if (ptr != NULL) { + if ((error != 0) && (ptr != NULL)) { KFREES(ptr, ctl.apc_dsize); } break; default : + IPFERROR(80004); error = EINVAL; } return error; } -/* - * If a proxy has a match function, call that to do extended packet - * matching. - */ -int appr_match(fin, nat) -fr_info_t *fin; -nat_t *nat; +/* ------------------------------------------------------------------------ */ +/* Function: ipf_proxy_match */ +/* Returns: int - 0 == success, else error */ +/* Parameters: fin(I) - pointer to packet information */ +/* nat(I) - pointer to current NAT session */ +/* */ +/* If a proxy has a match function, call that to do extended packet */ +/* matching. Whilst other parts of the NAT code are rather lenient when it */ +/* comes to the quality of the packet that it will transform, the proxy */ +/* matching is not because they need to work with data, not just headers. */ +/* ------------------------------------------------------------------------ */ +int +ipf_proxy_match(fin, nat) + fr_info_t *fin; + nat_t *nat; { + ipf_main_softc_t *softc = fin->fin_main_soft; + ipf_proxy_softc_t *softp = softc->ipf_proxy_soft; aproxy_t *apr; ipnat_t *ipn; int result; ipn = nat->nat_ptr; - if (ipf_proxy_debug > 8) - printf("appr_match(%lx,%lx) aps %lx ptr %lx\n", + if (softp->ips_proxy_debug & 0x04) + printf("ipf_proxy_match(%lx,%lx) aps %lx ptr %lx\n", (u_long)fin, (u_long)nat, (u_long)nat->nat_aps, (u_long)ipn); if ((fin->fin_flx & (FI_SHORT|FI_BAD)) != 0) { - if (ipf_proxy_debug > 0) - printf("appr_match: flx 0x%x (BAD|SHORT)\n", + if (softp->ips_proxy_debug & 0x08) + printf("ipf_proxy_match: flx 0x%x (BAD|SHORT)\n", fin->fin_flx); return -1; } apr = ipn->in_apr; if ((apr == NULL) || (apr->apr_flags & APR_DELETE)) { - if (ipf_proxy_debug > 0) - printf("appr_match:apr %lx apr_flags 0x%x\n", + if (softp->ips_proxy_debug & 0x08) + printf("ipf_proxy_match:apr %lx apr_flags 0x%x\n", (u_long)apr, apr ? apr->apr_flags : 0); return -1; } @@ -375,8 +805,8 @@ nat_t *nat; if (apr->apr_match != NULL) { result = (*apr->apr_match)(fin, nat->nat_aps, nat); if (result != 0) { - if (ipf_proxy_debug > 4) - printf("appr_match: result %d\n", result); + if (softp->ips_proxy_debug & 0x08) + printf("ipf_proxy_match: result %d\n", result); return -1; } } @@ -384,24 +814,32 @@ nat_t *nat; } -/* - * Allocate a new application proxy structure and fill it in with the - * relevant details. call the init function once complete, prior to - * returning. - */ -int appr_new(fin, nat) -fr_info_t *fin; -nat_t *nat; +/* ------------------------------------------------------------------------ */ +/* Function: ipf_proxy_new */ +/* Returns: int - 0 == success, else error */ +/* Parameters: fin(I) - pointer to packet information */ +/* nat(I) - pointer to current NAT session */ +/* */ +/* Allocate a new application proxy structure and fill it in with the */ +/* relevant details. call the init function once complete, prior to */ +/* returning. */ +/* ------------------------------------------------------------------------ */ +int +ipf_proxy_new(fin, nat) + fr_info_t *fin; + nat_t *nat; { + ipf_main_softc_t *softc = fin->fin_main_soft; + ipf_proxy_softc_t *softp = softc->ipf_proxy_soft; register ap_session_t *aps; aproxy_t *apr; - if (ipf_proxy_debug > 8) - printf("appr_new(%lx,%lx) \n", (u_long)fin, (u_long)nat); + if (softp->ips_proxy_debug & 0x04) + printf("ipf_proxy_new(%lx,%lx) \n", (u_long)fin, (u_long)nat); if ((nat->nat_ptr == NULL) || (nat->nat_aps != NULL)) { - if (ipf_proxy_debug > 0) - printf("appr_new: nat_ptr %lx nat_aps %lx\n", + if (softp->ips_proxy_debug & 0x08) + printf("ipf_proxy_new: nat_ptr %lx nat_aps %lx\n", (u_long)nat->nat_ptr, (u_long)nat->nat_aps); return -1; } @@ -410,65 +848,71 @@ nat_t *nat; if ((apr->apr_flags & APR_DELETE) || (fin->fin_p != apr->apr_p)) { - if (ipf_proxy_debug > 2) - printf("appr_new: apr_flags 0x%x p %d/%d\n", + if (softp->ips_proxy_debug & 0x08) + printf("ipf_proxy_new: apr_flags 0x%x p %d/%d\n", apr->apr_flags, fin->fin_p, apr->apr_p); return -1; } KMALLOC(aps, ap_session_t *); if (!aps) { - if (ipf_proxy_debug > 0) - printf("appr_new: malloc failed (%lu)\n", + if (softp->ips_proxy_debug & 0x08) + printf("ipf_proxy_new: malloc failed (%lu)\n", (u_long)sizeof(ap_session_t)); return -1; } bzero((char *)aps, sizeof(*aps)); - aps->aps_p = fin->fin_p; aps->aps_data = NULL; aps->aps_apr = apr; aps->aps_psiz = 0; if (apr->apr_new != NULL) - if ((*apr->apr_new)(fin, aps, nat) == -1) { + if ((*apr->apr_new)(apr->apr_soft, fin, aps, nat) == -1) { if ((aps->aps_data != NULL) && (aps->aps_psiz != 0)) { KFREES(aps->aps_data, aps->aps_psiz); } KFREE(aps); - if (ipf_proxy_debug > 2) - printf("appr_new: new(%lx) failed\n", + if (softp->ips_proxy_debug & 0x08) + printf("ipf_proxy_new: new(%lx) failed\n", (u_long)apr->apr_new); return -1; } aps->aps_nat = nat; - aps->aps_next = ap_sess_list; - ap_sess_list = aps; + aps->aps_next = softp->ips_sess_list; + softp->ips_sess_list = aps; nat->nat_aps = aps; return 0; } -/* - * Check to see if a packet should be passed through an active proxy routine - * if one has been setup for it. We don't need to check the checksum here if - * IPFILTER_CKSUM is defined because if it is, a failed check causes FI_BAD - * to be set. - */ -int appr_check(fin, nat) -fr_info_t *fin; -nat_t *nat; +/* ------------------------------------------------------------------------ */ +/* Function: ipf_proxy_check */ +/* Returns: int - -1 == error, 0 == success */ +/* Parameters: fin(I) - pointer to packet information */ +/* nat(I) - pointer to current NAT session */ +/* */ +/* Check to see if a packet should be passed through an active proxy */ +/* routine if one has been setup for it. We don't need to check the */ +/* checksum here if IPFILTER_CKSUM is defined because if it is, a failed */ +/* check causes FI_BAD to be set. */ +/* ------------------------------------------------------------------------ */ +int +ipf_proxy_check(fin, nat) + fr_info_t *fin; + nat_t *nat; { -#if SOLARIS && defined(_KERNEL) && (SOLARIS2 >= 6) -# if defined(ICK_VALID) + ipf_main_softc_t *softc = fin->fin_main_soft; + ipf_proxy_softc_t *softp = softc->ipf_proxy_soft; +#if SOLARIS && defined(_KERNEL) && defined(ICK_VALID) mb_t *m; -# endif - int dosum = 1; #endif tcphdr_t *tcp = NULL; udphdr_t *udp = NULL; ap_session_t *aps; aproxy_t *apr; + short adjlen; + int dosum; ip_t *ip; short rv; int err; @@ -477,55 +921,54 @@ nat_t *nat; #endif if (fin->fin_flx & FI_BAD) { - if (ipf_proxy_debug > 0) - printf("appr_check: flx 0x%x (BAD)\n", fin->fin_flx); + if (softp->ips_proxy_debug & 0x08) + printf("ipf_proxy_check: flx 0x%x (BAD)\n", + fin->fin_flx); return -1; } #ifndef IPFILTER_CKSUM - if ((fin->fin_out == 0) && (fr_checkl4sum(fin) == -1)) { - if (ipf_proxy_debug > 0) - printf("appr_check: l4 checksum failure %d\n", + if ((fin->fin_out == 0) && (ipf_checkl4sum(fin) == -1)) { + if (softp->ips_proxy_debug & 0x08) + printf("ipf_proxy_check: l4 checksum failure %d\n", fin->fin_p); if (fin->fin_p == IPPROTO_TCP) - frstats[fin->fin_out].fr_tcpbad++; + softc->ipf_stats[fin->fin_out].fr_tcpbad++; return -1; } #endif aps = nat->nat_aps; - if ((aps != NULL) && (aps->aps_p == fin->fin_p)) { + if (aps != NULL) { /* * If there is data in this packet to be proxied then try and * get it all into the one buffer, else drop it. */ #if defined(MENTAT) || defined(HAVE_M_PULLDOWN) if ((fin->fin_dlen > 0) && !(fin->fin_flx & FI_COALESCE)) - if (fr_coalesce(fin) == -1) { - if (ipf_proxy_debug > 0) - printf("appr_check: fr_coalesce failed %x\n", fin->fin_flx); + if (ipf_coalesce(fin) == -1) { + if (softp->ips_proxy_debug & 0x08) + printf("ipf_proxy_check: %s %x\n", + "coalesce failed", fin->fin_flx); return -1; } #endif ip = fin->fin_ip; + if (fin->fin_cksum > FI_CK_SUMOK) + dosum = 0; + else + dosum = 1; switch (fin->fin_p) { case IPPROTO_TCP : tcp = (tcphdr_t *)fin->fin_dp; - -#if SOLARIS && defined(_KERNEL) && (SOLARIS2 >= 6) && defined(ICK_VALID) +#if SOLARIS && defined(_KERNEL) && defined(ICK_VALID) m = fin->fin_qfm; if (dohwcksum && (m->b_ick_flag == ICK_VALID)) dosum = 0; #endif - /* - * Don't bother the proxy with these...or in fact, - * should we free up proxy stuff when seen? - */ - if ((fin->fin_tcpf & TH_RST) != 0) - break; - /*FALLTHROUGH*/ + break; case IPPROTO_UDP : udp = (udphdr_t *)fin->fin_dp; break; @@ -537,22 +980,24 @@ nat_t *nat; err = 0; if (fin->fin_out != 0) { if (apr->apr_outpkt != NULL) - err = (*apr->apr_outpkt)(fin, aps, nat); + err = (*apr->apr_outpkt)(apr->apr_soft, fin, + aps, nat); } else { if (apr->apr_inpkt != NULL) - err = (*apr->apr_inpkt)(fin, aps, nat); + err = (*apr->apr_inpkt)(apr->apr_soft, fin, + aps, nat); } rv = APR_EXIT(err); - if (((ipf_proxy_debug > 0) && (rv != 0)) || - (ipf_proxy_debug > 8)) - printf("appr_check: out %d err %x rv %d\n", + if (((softp->ips_proxy_debug & 0x08) && (rv != 0)) || + (softp->ips_proxy_debug & 0x04)) + printf("ipf_proxy_check: out %d err %x rv %d\n", fin->fin_out, err, rv); if (rv == 1) return -1; if (rv == 2) { - appr_free(apr); + ipf_proxy_deref(apr); nat->nat_aps = NULL; return -1; } @@ -562,16 +1007,17 @@ nat_t *nat; * so we need to recalculate the header checksums for the * packet. */ + adjlen = APR_INC(err); #if !defined(_KERNEL) || defined(MENTAT) || defined(__sgi) - if (err != 0) { - short adjlen = err & 0xffff; - - s1 = LONG_SUM(fin->fin_plen - adjlen); - s2 = LONG_SUM(fin->fin_plen); - CALC_SUMD(s1, s2, sd); - fix_outcksum(fin, &ip->ip_sum, sd); - } + s1 = LONG_SUM(fin->fin_plen - adjlen); + s2 = LONG_SUM(fin->fin_plen); + CALC_SUMD(s1, s2, sd); + if ((err != 0) && (fin->fin_cksum < FI_CK_L4PART) && + fin->fin_v == 4) + ipf_fix_outcksum(0, &ip->ip_sum, sd, 0); #endif + if (fin->fin_flx & FI_DOCKSUM) + dosum = 1; /* * For TCP packets, we may need to adjust the sequence and @@ -583,28 +1029,24 @@ nat_t *nat; * changed or not. */ if (tcp != NULL) { - err = appr_fixseqack(fin, ip, aps, APR_INC(err)); -#if SOLARIS && defined(_KERNEL) && (SOLARIS2 >= 6) - if (dosum) - tcp->th_sum = fr_cksum(fin->fin_qfm, ip, - IPPROTO_TCP, tcp, - fin->fin_plen); -#else - tcp->th_sum = fr_cksum(fin->fin_m, ip, - IPPROTO_TCP, tcp, - fin->fin_plen); -#endif + err = ipf_proxy_fixseqack(fin, ip, aps, adjlen); + if (fin->fin_cksum == FI_CK_L4PART) { + u_short sum = ntohs(tcp->th_sum); + sum += adjlen; + tcp->th_sum = htons(sum); + } else if (fin->fin_cksum < FI_CK_L4PART) { + tcp->th_sum = fr_cksum(fin, ip, + IPPROTO_TCP, tcp); + } } else if ((udp != NULL) && (udp->uh_sum != 0)) { -#if SOLARIS && defined(_KERNEL) && (SOLARIS2 >= 6) - if (dosum) - udp->uh_sum = fr_cksum(fin->fin_qfm, ip, - IPPROTO_UDP, udp, - fin->fin_plen); -#else - udp->uh_sum = fr_cksum(fin->fin_m, ip, - IPPROTO_UDP, udp, - fin->fin_plen); -#endif + if (fin->fin_cksum == FI_CK_L4PART) { + u_short sum = ntohs(udp->uh_sum); + sum += adjlen; + udp->uh_sum = htons(sum); + } else if (dosum) { + udp->uh_sum = fr_cksum(fin, ip, + IPPROTO_UDP, udp); + } } aps->aps_bytes += fin->fin_plen; aps->aps_pkts++; @@ -614,54 +1056,78 @@ nat_t *nat; } -/* - * Search for an proxy by the protocol it is being used with and its name. - */ -aproxy_t *appr_lookup(pr, name) -u_int pr; -char *name; +/* ------------------------------------------------------------------------ */ +/* Function: ipf_proxy_lookup */ +/* Returns: int - -1 == error, 0 == success */ +/* Parameters: arg(I) - pointer to proxy context information */ +/* pr(I) - protocol number for proxy */ +/* name(I) - proxy name */ +/* */ +/* Search for an proxy by the protocol it is being used with and its name. */ +/* ------------------------------------------------------------------------ */ +aproxy_t * +ipf_proxy_lookup(arg, pr, name) + void *arg; + u_int pr; + char *name; { + ipf_proxy_softc_t *softp = arg; aproxy_t *ap; - if (ipf_proxy_debug > 8) - printf("appr_lookup(%d,%s)\n", pr, name); + if (softp->ips_proxy_debug & 0x04) + printf("ipf_proxy_lookup(%d,%s)\n", pr, name); - for (ap = ap_proxies; ap->apr_p; ap++) + for (ap = softp->ips_proxies; ap != NULL; ap = ap->apr_next) if ((ap->apr_p == pr) && !strncmp(name, ap->apr_label, sizeof(ap->apr_label))) { ap->apr_ref++; return ap; } - for (ap = ap_proxylist; ap; ap = ap->apr_next) - if ((ap->apr_p == pr) && - !strncmp(name, ap->apr_label, sizeof(ap->apr_label))) { - ap->apr_ref++; - return ap; - } - if (ipf_proxy_debug > 2) - printf("appr_lookup: failed for %d/%s\n", pr, name); + if (softp->ips_proxy_debug & 0x08) + printf("ipf_proxy_lookup: failed for %d/%s\n", pr, name); return NULL; } -void appr_free(ap) -aproxy_t *ap; +/* ------------------------------------------------------------------------ */ +/* Function: ipf_proxy_deref */ +/* Returns: Nil */ +/* Parameters: ap(I) - pointer to proxy structure */ +/* */ +/* Drop the reference counter associated with the proxy. */ +/* ------------------------------------------------------------------------ */ +void +ipf_proxy_deref(ap) + aproxy_t *ap; { ap->apr_ref--; } -void aps_free(aps) -ap_session_t *aps; +/* ------------------------------------------------------------------------ */ +/* Function: ipf_proxy_free */ +/* Returns: Nil */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* aps(I) - pointer to current proxy session */ +/* Locks Held: ipf_nat_new, ipf_nat(W) */ +/* */ +/* Free up proxy session information allocated to be used with a NAT */ +/* session. */ +/* ------------------------------------------------------------------------ */ +void +ipf_proxy_free(softc, aps) + ipf_main_softc_t *softc; + ap_session_t *aps; { + ipf_proxy_softc_t *softp = softc->ipf_proxy_soft; ap_session_t *a, **ap; aproxy_t *apr; if (!aps) return; - for (ap = &ap_sess_list; ((a = *ap) != NULL); ap = &a->aps_next) + for (ap = &softp->ips_sess_list; ((a = *ap) != NULL); ap = &a->aps_next) if (a == aps) { *ap = a->aps_next; break; @@ -669,7 +1135,7 @@ ap_session_t *aps; apr = aps->aps_apr; if ((apr != NULL) && (apr->apr_del != NULL)) - (*apr->apr_del)(aps); + (*apr->apr_del)(softc, aps); if ((aps->aps_data != NULL) && (aps->aps_psiz != 0)) KFREES(aps->aps_data, aps->aps_psiz); @@ -677,15 +1143,28 @@ ap_session_t *aps; } -/* - * returns 2 if ack or seq number in TCP header is changed, returns 0 otherwise - */ -static int appr_fixseqack(fin, ip, aps, inc) -fr_info_t *fin; -ip_t *ip; -ap_session_t *aps; -int inc; +/* ------------------------------------------------------------------------ */ +/* Function: ipf_proxy_fixseqack */ +/* Returns: int - 2 if TCP ack/seq is changed, else 0 */ +/* Parameters: fin(I) - pointer to packet information */ +/* ip(I) - pointer to IP header */ +/* nat(I) - pointer to current NAT session */ +/* inc(I) - delta to apply to TCP sequence numbering */ +/* */ +/* Adjust the TCP sequence/acknowledge numbers in the TCP header based on */ +/* whether or not the new header is past the point at which an adjustment */ +/* occurred. This might happen because of (say) an FTP string being changed */ +/* and the new string being a different length to the old. */ +/* ------------------------------------------------------------------------ */ +static int +ipf_proxy_fixseqack(fin, ip, aps, inc) + fr_info_t *fin; + ip_t *ip; + ap_session_t *aps; + int inc; { + ipf_main_softc_t *softc = fin->fin_main_soft; + ipf_proxy_softc_t *softp = softc->ipf_proxy_soft; int sel, ch = 0, out, nlen; u_32_t seq1, seq2; tcphdr_t *tcp; @@ -694,10 +1173,10 @@ int inc; tcp = (tcphdr_t *)fin->fin_dp; out = fin->fin_out; /* - * fin->fin_plen has already been adjusted by 'inc'. + * ip_len has already been adjusted by 'inc'. */ - nlen = fin->fin_plen; - nlen -= (IP_HL(ip) << 2) + (TCP_OFF(tcp) << 2); + nlen = fin->fin_dlen; + nlen -= (TCP_OFF(tcp) << 2); inc2 = inc; inc = (int)inc2; @@ -709,7 +1188,7 @@ int inc; /* switch to other set ? */ if ((aps->aps_seqmin[!sel] > aps->aps_seqmin[sel]) && (seq1 > aps->aps_seqmin[!sel])) { - if (ipf_proxy_debug > 7) + if (softp->ips_proxy_debug & 0x10) printf("proxy out switch set seq %d -> %d %x > %x\n", sel, !sel, seq1, aps->aps_seqmin[!sel]); @@ -729,7 +1208,7 @@ int inc; if (inc && (seq1 > aps->aps_seqmin[!sel])) { aps->aps_seqmin[sel] = seq1 + nlen - 1; aps->aps_seqoff[sel] = aps->aps_seqoff[sel] + inc; - if (ipf_proxy_debug > 7) + if (softp->ips_proxy_debug & 0x10) printf("proxy seq set %d at %x to %d + %d\n", sel, aps->aps_seqmin[sel], aps->aps_seqoff[sel], inc); @@ -743,7 +1222,7 @@ int inc; /* switch to other set ? */ if ((aps->aps_ackmin[!sel] > aps->aps_ackmin[sel]) && (seq1 > aps->aps_ackmin[!sel])) { - if (ipf_proxy_debug > 7) + if (softp->ips_proxy_debug & 0x10) printf("proxy out switch set ack %d -> %d %x > %x\n", sel, !sel, seq1, aps->aps_ackmin[!sel]); @@ -762,7 +1241,7 @@ int inc; /* switch to other set ? */ if ((aps->aps_ackmin[!sel] > aps->aps_ackmin[sel]) && (seq1 > aps->aps_ackmin[!sel])) { - if (ipf_proxy_debug > 7) + if (softp->ips_proxy_debug & 0x10) printf("proxy in switch set ack %d -> %d %x > %x\n", sel, !sel, seq1, aps->aps_ackmin[!sel]); sel = aps->aps_sel[out] = !sel; @@ -782,7 +1261,7 @@ int inc; aps->aps_ackmin[!sel] = seq1 + nlen - 1; aps->aps_ackoff[!sel] = aps->aps_ackoff[sel] + inc; - if (ipf_proxy_debug > 7) + if (softp->ips_proxy_debug & 0x10) printf("proxy ack set %d at %x to %d + %d\n", !sel, aps->aps_seqmin[!sel], aps->aps_seqoff[sel], inc); @@ -796,14 +1275,14 @@ int inc; /* switch to other set ? */ if ((aps->aps_seqmin[!sel] > aps->aps_seqmin[sel]) && (seq1 > aps->aps_seqmin[!sel])) { - if (ipf_proxy_debug > 7) + if (softp->ips_proxy_debug & 0x10) printf("proxy in switch set seq %d -> %d %x > %x\n", sel, !sel, seq1, aps->aps_seqmin[!sel]); sel = aps->aps_sel[1 - out] = !sel; } if (aps->aps_seqoff[sel] != 0) { - if (ipf_proxy_debug > 7) + if (softp->ips_proxy_debug & 0x10) printf("sel %d seqoff %d seq1 %x seqmin %x\n", sel, aps->aps_seqoff[sel], seq1, aps->aps_seqmin[sel]); @@ -815,45 +1294,175 @@ int inc; } } - if (ipf_proxy_debug > 8) - printf("appr_fixseqack: seq %x ack %x\n", + if (softp->ips_proxy_debug & 0x10) + printf("ipf_proxy_fixseqack: seq %u ack %u\n", (u_32_t)ntohl(tcp->th_seq), (u_32_t)ntohl(tcp->th_ack)); return ch ? 2 : 0; } -/* - * Initialise hook for kernel application proxies. - * Call the initialise routine for all the compiled in kernel proxies. - */ -int appr_init() +/* ------------------------------------------------------------------------ */ +/* Function: ipf_proxy_rule_rev */ +/* Returns: ipnat_t * - NULL = failure, else pointer to new rule */ +/* Parameters: nat(I) - pointer to NAT session to create rule from */ +/* */ +/* This function creates a NAT rule that is based upon the reverse packet */ +/* flow associated with this NAT session. Thus if this NAT session was */ +/* created with a map rule then this function will create a rdr rule. */ +/* Only address fields and network interfaces are assigned in this function */ +/* and the address fields are formed such that an exact is required. If the */ +/* original rule had a netmask, that is not replicated here not is it */ +/* desired. The ultimate goal here is to create a NAT rule to support a NAT */ +/* session being created that does not have a user configured rule. The */ +/* classic example is supporting the FTP proxy, where a data channel needs */ +/* to be setup, based on the addresses used for the control connection. In */ +/* that case, this function is used to handle creating NAT rules to support */ +/* data connections with the PORT and EPRT commands. */ +/* ------------------------------------------------------------------------ */ +ipnat_t * +ipf_proxy_rule_rev(nat) + nat_t *nat; { - aproxy_t *ap; - int err = 0; + ipnat_t *old; + ipnat_t *ipn; + int size; - for (ap = ap_proxies; ap->apr_p; ap++) { - if (ap->apr_init != NULL) { - err = (*ap->apr_init)(); - if (err != 0) - break; + old = nat->nat_ptr; + size = old->in_size; + + KMALLOCS(ipn, ipnat_t *, size); + if (ipn == NULL) + return NULL; + + bzero((char *)ipn, size); + + ipn->in_use = 1; + ipn->in_hits = 1; + ipn->in_ippip = 1; + ipn->in_apr = NULL; + ipn->in_size = size; + ipn->in_pr[0] = old->in_pr[1]; + ipn->in_pr[1] = old->in_pr[0]; + ipn->in_v[0] = old->in_v[1]; + ipn->in_v[1] = old->in_v[0]; + ipn->in_ifps[0] = old->in_ifps[1]; + ipn->in_ifps[1] = old->in_ifps[0]; + ipn->in_flags = (old->in_flags | IPN_PROXYRULE); + + ipn->in_nsrcip6 = nat->nat_odst6; + ipn->in_osrcip6 = nat->nat_ndst6; + + if ((old->in_redir & NAT_REDIRECT) != 0) { + ipn->in_redir = NAT_MAP; + if (ipn->in_v[0] == 4) { + ipn->in_snip = ntohl(nat->nat_odstaddr); + ipn->in_dnip = ntohl(nat->nat_nsrcaddr); + } else { +#ifdef USE_INET6 + ipn->in_snip6 = nat->nat_odst6; + ipn->in_dnip6 = nat->nat_nsrc6; +#endif } + ipn->in_ndstip6 = nat->nat_nsrc6; + ipn->in_odstip6 = nat->nat_osrc6; + } else { + ipn->in_redir = NAT_REDIRECT; + if (ipn->in_v[0] == 4) { + ipn->in_snip = ntohl(nat->nat_odstaddr); + ipn->in_dnip = ntohl(nat->nat_osrcaddr); + } else { +#ifdef USE_INET6 + ipn->in_snip6 = nat->nat_odst6; + ipn->in_dnip6 = nat->nat_osrc6; +#endif + } + ipn->in_ndstip6 = nat->nat_osrc6; + ipn->in_odstip6 = nat->nat_nsrc6; } - return err; + + IP6_SETONES(&ipn->in_osrcmsk6); + IP6_SETONES(&ipn->in_nsrcmsk6); + IP6_SETONES(&ipn->in_odstmsk6); + IP6_SETONES(&ipn->in_ndstmsk6); + + ipn->in_namelen = old->in_namelen; + ipn->in_ifnames[0] = old->in_ifnames[1]; + ipn->in_ifnames[1] = old->in_ifnames[0]; + bcopy(old->in_names, ipn->in_names, ipn->in_namelen); + MUTEX_INIT(&ipn->in_lock, "ipnat rev rule lock"); + + return ipn; } -/* - * Unload hook for kernel application proxies. - * Call the finialise routine for all the compiled in kernel proxies. - */ -void appr_unload() +/* ------------------------------------------------------------------------ */ +/* Function: ipf_proxy_rule_fwd */ +/* Returns: ipnat_t * - NULL = failure, else pointer to new rule */ +/* Parameters: nat(I) - pointer to NAT session to create rule from */ +/* */ +/* The purpose and rationale of this function is much the same as the above */ +/* function, ipf_proxy_rule_rev, except that a rule is created that matches */ +/* the same direction as that of the existing NAT session. Thus if this NAT */ +/* session was created with a map rule then this function will also create */ +/* a data structure to represent a map rule. Whereas ipf_proxy_rule_rev is */ +/* used to support PORT/EPRT, this function supports PASV/EPSV. */ +/* ------------------------------------------------------------------------ */ +ipnat_t * +ipf_proxy_rule_fwd(nat) + nat_t *nat; { - aproxy_t *ap; + ipnat_t *old; + ipnat_t *ipn; + int size; - for (ap = ap_proxies; ap->apr_p; ap++) - if (ap->apr_fini != NULL) - (*ap->apr_fini)(); - for (ap = ap_proxylist; ap; ap = ap->apr_next) - if (ap->apr_fini != NULL) - (*ap->apr_fini)(); + old = nat->nat_ptr; + size = old->in_size; + + KMALLOCS(ipn, ipnat_t *, size); + if (ipn == NULL) + return NULL; + + bzero((char *)ipn, size); + + ipn->in_use = 1; + ipn->in_hits = 1; + ipn->in_ippip = 1; + ipn->in_apr = NULL; + ipn->in_size = size; + ipn->in_pr[0] = old->in_pr[0]; + ipn->in_pr[1] = old->in_pr[1]; + ipn->in_v[0] = old->in_v[0]; + ipn->in_v[1] = old->in_v[1]; + ipn->in_ifps[0] = nat->nat_ifps[0]; + ipn->in_ifps[1] = nat->nat_ifps[1]; + ipn->in_flags = (old->in_flags | IPN_PROXYRULE); + + ipn->in_nsrcip6 = nat->nat_nsrc6; + ipn->in_osrcip6 = nat->nat_osrc6; + ipn->in_ndstip6 = nat->nat_ndst6; + ipn->in_odstip6 = nat->nat_odst6; + ipn->in_redir = old->in_redir; + + if (ipn->in_v[0] == 4) { + ipn->in_snip = ntohl(nat->nat_nsrcaddr); + ipn->in_dnip = ntohl(nat->nat_ndstaddr); + } else { +#ifdef USE_INET6 + ipn->in_snip6 = nat->nat_nsrc6; + ipn->in_dnip6 = nat->nat_ndst6; +#endif + } + + IP6_SETONES(&ipn->in_osrcmsk6); + IP6_SETONES(&ipn->in_nsrcmsk6); + IP6_SETONES(&ipn->in_odstmsk6); + IP6_SETONES(&ipn->in_ndstmsk6); + + ipn->in_namelen = old->in_namelen; + ipn->in_ifnames[0] = old->in_ifnames[0]; + ipn->in_ifnames[1] = old->in_ifnames[1]; + bcopy(old->in_names, ipn->in_names, ipn->in_namelen); + MUTEX_INIT(&ipn->in_lock, "ipnat fwd rule lock"); + + return ipn; } diff --git a/sys/contrib/ipfilter/netinet/ip_proxy.h b/sys/contrib/ipfilter/netinet/ip_proxy.h index 1bcfc6069e3..1a1fdfac36a 100644 --- a/sys/contrib/ipfilter/netinet/ip_proxy.h +++ b/sys/contrib/ipfilter/netinet/ip_proxy.h @@ -1,7 +1,7 @@ /* $FreeBSD$ */ /* - * Copyright (C) 1997-2001 by Darren Reed. + * Copyright (C) 2012 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. * @@ -53,14 +53,11 @@ typedef struct ap_session { struct ap_tcp apu_tcp; struct ap_udp apu_udp; } aps_un; - u_int aps_flags; U_QUAD_T aps_bytes; /* bytes sent */ U_QUAD_T aps_pkts; /* packets sent */ void *aps_nat; /* pointer back to nat struct */ void *aps_data; /* private data */ - int aps_p; /* protocol */ int aps_psiz; /* size of private data */ - struct ap_session *aps_hnext; struct ap_session *aps_next; } ap_session_t; @@ -76,6 +73,7 @@ typedef struct ap_session { typedef struct ap_control { char apc_label[APR_LABELLEN]; + char apc_config[APR_LABELLEN]; u_char apc_p; /* * The following fields are upto the proxy's apr_ctl routine to deal @@ -93,21 +91,36 @@ typedef struct ap_control { size_t apc_dsize; } ap_ctl_t; +#define APC_CMD_ADD 0 +#define APC_CMD_DEL 1 + typedef struct aproxy { struct aproxy *apr_next; + struct aproxy *apr_parent; char apr_label[APR_LABELLEN]; /* Proxy label # */ - u_char apr_p; /* protocol */ - int apr_ref; /* +1 per rule referencing it */ + u_char apr_p; /* protocol */ int apr_flags; - int (* apr_init) __P((void)); - void (* apr_fini) __P((void)); - int (* apr_new) __P((fr_info_t *, ap_session_t *, struct nat *)); - void (* apr_del) __P((ap_session_t *)); - int (* apr_inpkt) __P((fr_info_t *, ap_session_t *, struct nat *)); - int (* apr_outpkt) __P((fr_info_t *, ap_session_t *, struct nat *)); + int apr_ref; + int apr_clones; + void (* apr_load) __P((void)); + void (* apr_unload) __P((void)); + void *(* apr_create) __P((ipf_main_softc_t *)); + void (* apr_destroy) __P((ipf_main_softc_t *, void *)); + int (* apr_init) __P((ipf_main_softc_t *, void *)); + void (* apr_fini) __P((ipf_main_softc_t *, void *)); + int (* apr_new) __P((void *, fr_info_t *, ap_session_t *, + struct nat *)); + void (* apr_del) __P((ipf_main_softc_t *, ap_session_t *)); + int (* apr_inpkt) __P((void *, fr_info_t *, ap_session_t *, + struct nat *)); + int (* apr_outpkt) __P((void *, fr_info_t *, ap_session_t *, + struct nat *)); int (* apr_match) __P((fr_info_t *, ap_session_t *, struct nat *)); - int (* apr_ctl) __P((struct aproxy *, struct ap_control *)); + int (* apr_ctl) __P((ipf_main_softc_t *, void *, ap_ctl_t *)); + int (* apr_clear) __P((struct aproxy *)); + int (* apr_flush) __P((struct aproxy *, int)); + void *apr_soft; } aproxy_t; #define APR_DELETE 1 @@ -116,42 +129,37 @@ typedef struct aproxy { #define APR_EXIT(x) (((x) >> 16) & 0xffff) #define APR_INC(x) ((x) & 0xffff) + +#ifdef _KERNEL /* * Generic #define's to cover missing things in the kernel */ -#ifndef isdigit -#define isdigit(x) ((x) >= '0' && (x) <= '9') -#endif -#ifndef isupper -#define isupper(x) (((unsigned)(x) >= 'A') && ((unsigned)(x) <= 'Z')) -#endif -#ifndef islower -#define islower(x) (((unsigned)(x) >= 'a') && ((unsigned)(x) <= 'z')) -#endif -#ifndef isalpha -#define isalpha(x) (isupper(x) || islower(x)) -#endif -#ifndef toupper -#define toupper(x) (isupper(x) ? (x) : (x) - 'a' + 'A') -#endif -#ifndef isspace -#define isspace(x) (((x) == ' ') || ((x) == '\r') || ((x) == '\n') || \ +# ifndef isdigit +# define isdigit(x) ((x) >= '0' && (x) <= '9') +# endif +# ifndef isupper +# define isupper(x) (((unsigned)(x) >= 'A') && ((unsigned)(x) <= 'Z')) +# endif +# ifndef islower +# define islower(x) (((unsigned)(x) >= 'a') && ((unsigned)(x) <= 'z')) +# endif +# ifndef isalpha +# define isalpha(x) (isupper(x) || islower(x)) +# endif +# ifndef toupper +# define toupper(x) (isupper(x) ? (x) : (x) - 'a' + 'A') +# endif +# ifndef isspace +# define isspace(x) (((x) == ' ') || ((x) == '\r') || ((x) == '\n') || \ ((x) == '\t') || ((x) == '\b')) -#endif +# endif +#endif /* _KERNEL */ /* - * This is the scratch buffer size used to hold strings from the TCP stream - * that we may want to parse. It's an arbitrary size, really, but it must - * be at least as large as IPF_FTPBUFSZ. - */ -#define FTP_BUFSZ 120 - -/* - * This buffer, however, doesn't need to be nearly so big. It just needs to - * be able to squeeze in the largest command it needs to rewrite, Which ones - * does it rewrite? EPRT, PORT, 227 replies. + * For the ftp proxy. */ -#define IPF_FTPBUFSZ 80 /* This *MUST* be >= 53! */ +#define FTP_BUFSZ 160 +#define IPF_FTPBUFSZ 160 typedef struct ftpside { char *ftps_rptr; @@ -159,18 +167,36 @@ typedef struct ftpside { void *ftps_ifp; u_32_t ftps_seq[2]; u_32_t ftps_len; - int ftps_junk; /* 2 = no cr/lf yet, 1 = cannot parse */ + int ftps_junk; int ftps_cmds; + int ftps_cmd; char ftps_buf[FTP_BUFSZ]; } ftpside_t; typedef struct ftpinfo { int ftp_passok; int ftp_incok; + void *ftp_pendstate; + nat_t *ftp_pendnat; ftpside_t ftp_side[2]; } ftpinfo_t; +/* + * IPsec proxy + */ +typedef u_32_t ipsec_cookie_t[2]; + +typedef struct ipsec_pxy { + ipsec_cookie_t ipsc_icookie; + ipsec_cookie_t ipsc_rcookie; + int ipsc_rckset; + nat_t *ipsc_nat; + struct ipstate *ipsc_state; + ipnat_t *ipsc_rule; +} ipsec_pxy_t; + + /* * For the irc proxy. */ @@ -186,6 +212,16 @@ typedef struct ircinfo { } ircinfo_t; +/* + * For the DNS "proxy" + */ +typedef struct dnsinfo { + ipfmutex_t dnsi_lock; + u_short dnsi_id; + char dnsi_buffer[512]; +} dnsinfo_t; + + /* * Real audio proxy structure and #defines */ @@ -230,43 +266,6 @@ typedef struct msnrpcinfo { } msnrpcinfo_t; -/* - * IPSec proxy - */ -typedef u_32_t ipsec_cookie_t[2]; - -typedef struct ipsec_pxy { - ipsec_cookie_t ipsc_icookie; - ipsec_cookie_t ipsc_rcookie; - int ipsc_rckset; - ipnat_t ipsc_rule; - nat_t *ipsc_nat; - struct ipstate *ipsc_state; -} ipsec_pxy_t; - -/* - * PPTP proxy - */ -typedef struct pptp_side { - u_32_t pptps_nexthdr; - u_32_t pptps_next; - int pptps_state; - int pptps_gothdr; - int pptps_len; - int pptps_bytes; - char *pptps_wptr; - char pptps_buffer[512]; -} pptp_side_t; - -typedef struct pptp_pxy { - ipnat_t pptp_rule; - nat_t *pptp_nat; - struct ipstate *pptp_state; - u_short pptp_call[2]; - pptp_side_t pptp_side[2]; -} pptp_pxy_t; - - /* * Sun RPCBIND proxy */ @@ -439,24 +438,26 @@ typedef struct rpcb_session { */ #define XDRALIGN(x) ((((x) % 4) != 0) ? ((((x) + 3) / 4) * 4) : (x)) -extern ap_session_t *ap_sess_tab[AP_SESS_SIZE]; -extern ap_session_t *ap_sess_list; -extern aproxy_t ap_proxies[]; -extern int ippr_ftp_pasvonly; -extern int ipf_proxy_debug; - -extern int appr_add __P((aproxy_t *)); -extern int appr_ctl __P((ap_ctl_t *)); -extern int appr_del __P((aproxy_t *)); -extern int appr_init __P((void)); -extern void appr_unload __P((void)); -extern int appr_ok __P((fr_info_t *, tcphdr_t *, struct ipnat *)); -extern int appr_match __P((fr_info_t *, struct nat *)); -extern void appr_free __P((aproxy_t *)); -extern void aps_free __P((ap_session_t *)); -extern int appr_check __P((fr_info_t *, struct nat *)); -extern aproxy_t *appr_lookup __P((u_int, char *)); -extern int appr_new __P((fr_info_t *, struct nat *)); -extern int appr_ioctl __P((caddr_t, ioctlcmd_t, int, void *)); +extern int ipf_proxy_add __P((void *, aproxy_t *)); +extern int ipf_proxy_check __P((fr_info_t *, struct nat *)); +extern int ipf_proxy_ctl __P((ipf_main_softc_t *, void *, ap_ctl_t *)); +extern int ipf_proxy_del __P((aproxy_t *)); +extern void ipf_proxy_deref __P((aproxy_t *)); +extern void ipf_proxy_flush __P((void *, int)); +extern int ipf_proxy_init __P((void)); +extern int ipf_proxy_ioctl __P((ipf_main_softc_t *, caddr_t, ioctlcmd_t, int, void *)); +extern aproxy_t *ipf_proxy_lookup __P((void *, u_int, char *)); +extern int ipf_proxy_match __P((fr_info_t *, struct nat *)); +extern int ipf_proxy_new __P((fr_info_t *, struct nat *)); +extern int ipf_proxy_ok __P((fr_info_t *, tcphdr_t *, struct ipnat *)); +extern void ipf_proxy_free __P((ipf_main_softc_t *, ap_session_t *)); +extern int ipf_proxy_main_load __P((void)); +extern int ipf_proxy_main_unload __P((void)); +extern ipnat_t *ipf_proxy_rule_fwd __P((nat_t *)); +extern ipnat_t *ipf_proxy_rule_rev __P((nat_t *)); +extern void *ipf_proxy_soft_create __P((ipf_main_softc_t *)); +extern void ipf_proxy_soft_destroy __P((ipf_main_softc_t *, void *)); +extern int ipf_proxy_soft_init __P((ipf_main_softc_t *, void *)); +extern int ipf_proxy_soft_fini __P((ipf_main_softc_t *, void *)); #endif /* __IP_PROXY_H__ */ diff --git a/sys/contrib/ipfilter/netinet/ip_raudio_pxy.c b/sys/contrib/ipfilter/netinet/ip_raudio_pxy.c index 2729dc6725b..031363793ce 100644 --- a/sys/contrib/ipfilter/netinet/ip_raudio_pxy.c +++ b/sys/contrib/ipfilter/netinet/ip_raudio_pxy.c @@ -1,8 +1,7 @@ /* $FreeBSD$ */ /* - * $FreeBSD$ - * Copyright (C) 1998-2003 by Darren Reed + * Copyright (C) 2012 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. * @@ -12,11 +11,11 @@ #define IPF_RAUDIO_PROXY -int ippr_raudio_init __P((void)); -void ippr_raudio_fini __P((void)); -int ippr_raudio_new __P((fr_info_t *, ap_session_t *, nat_t *)); -int ippr_raudio_in __P((fr_info_t *, ap_session_t *, nat_t *)); -int ippr_raudio_out __P((fr_info_t *, ap_session_t *, nat_t *)); +void ipf_p_raudio_main_load __P((void)); +void ipf_p_raudio_main_unload __P((void)); +int ipf_p_raudio_new __P((void *, fr_info_t *, ap_session_t *, nat_t *)); +int ipf_p_raudio_in __P((void *, fr_info_t *, ap_session_t *, nat_t *)); +int ipf_p_raudio_out __P((void *, fr_info_t *, ap_session_t *, nat_t *)); static frentry_t raudiofr; @@ -26,19 +25,19 @@ int raudio_proxy_init = 0; /* * Real Audio application proxy initialization. */ -int ippr_raudio_init() +void +ipf_p_raudio_main_load() { bzero((char *)&raudiofr, sizeof(raudiofr)); raudiofr.fr_ref = 1; raudiofr.fr_flags = FR_INQUE|FR_PASS|FR_QUICK|FR_KEEPSTATE; MUTEX_INIT(&raudiofr.fr_lock, "Real Audio proxy rule lock"); raudio_proxy_init = 1; - - return 0; } -void ippr_raudio_fini() +void +ipf_p_raudio_main_unload() { if (raudio_proxy_init == 1) { MUTEX_DESTROY(&raudiofr.fr_lock); @@ -50,20 +49,24 @@ void ippr_raudio_fini() /* * Setup for a new proxy to handle Real Audio. */ -int ippr_raudio_new(fin, aps, nat) -fr_info_t *fin; -ap_session_t *aps; -nat_t *nat; +int +ipf_p_raudio_new(arg, fin, aps, nat) + void *arg; + fr_info_t *fin; + ap_session_t *aps; + nat_t *nat; { raudio_t *rap; + nat = nat; /* LINT */ + + if (fin->fin_v != 4) + return -1; + KMALLOCS(aps->aps_data, void *, sizeof(raudio_t)); if (aps->aps_data == NULL) return -1; - fin = fin; /* LINT */ - nat = nat; /* LINT */ - bzero(aps->aps_data, sizeof(raudio_t)); rap = aps->aps_data; aps->aps_psiz = sizeof(raudio_t); @@ -73,10 +76,12 @@ nat_t *nat; -int ippr_raudio_out(fin, aps, nat) -fr_info_t *fin; -ap_session_t *aps; -nat_t *nat; +int +ipf_p_raudio_out(arg, fin, aps, nat) + void *arg; + fr_info_t *fin; + ap_session_t *aps; + nat_t *nat; { raudio_t *rap = aps->aps_data; unsigned char membuf[512 + 1], *s; @@ -179,14 +184,18 @@ nat_t *nat; } -int ippr_raudio_in(fin, aps, nat) -fr_info_t *fin; -ap_session_t *aps; -nat_t *nat; +int +ipf_p_raudio_in(arg, fin, aps, nat) + void *arg; + fr_info_t *fin; + ap_session_t *aps; + nat_t *nat; { unsigned char membuf[IPF_MAXPORTLEN + 1], *s; tcphdr_t *tcp, tcph, *tcp2 = &tcph; raudio_t *rap = aps->aps_data; + ipf_main_softc_t *softc; + ipf_nat_softc_t *softn; struct in_addr swa, swb; int off, dlen, slen; int a1, a2, a3, a4; @@ -198,6 +207,8 @@ nat_t *nat; ip_t *ip; mb_t *m; + softc = fin->fin_main_soft; + softn = softc->ipf_nat_soft; /* * Wait until we've seen the end of the start messages and even then * only proceed further if we're using UDP. If they want to use TCP @@ -272,14 +283,12 @@ nat_t *nat; swb = ip->ip_dst; ip->ip_p = IPPROTO_UDP; - ip->ip_src = nat->nat_inip; - ip->ip_dst = nat->nat_oip; + ip->ip_src = nat->nat_ndstip; + ip->ip_dst = nat->nat_odstip; bcopy((char *)fin, (char *)&fi, sizeof(fi)); bzero((char *)tcp2, sizeof(*tcp2)); TCP_OFF_A(tcp2, 5); - fi.fin_state = NULL; - fi.fin_nat = NULL; fi.fin_flx |= FI_IGNORE; fi.fin_dp = (char *)tcp2; fi.fin_fr = &raudiofr; @@ -287,7 +296,7 @@ nat_t *nat; fi.fin_plen = fi.fin_hlen + sizeof(*tcp2); tcp2->th_win = htons(8192); slen = ip->ip_len; - ip->ip_len = fin->fin_hlen + sizeof(*tcp); + ip->ip_len = htons(fin->fin_hlen + sizeof(*tcp)); if (((rap->rap_mode & RAP_M_UDP_ROBUST) == RAP_M_UDP_ROBUST) && (rap->rap_srport != 0)) { @@ -298,16 +307,19 @@ nat_t *nat; fi.fin_data[0] = dp; fi.fin_data[1] = sp; fi.fin_out = 0; - nat2 = nat_new(&fi, nat->nat_ptr, NULL, + MUTEX_ENTER(&softn->ipf_nat_new); + nat2 = ipf_nat_add(&fi, nat->nat_ptr, NULL, NAT_SLAVE|IPN_UDP | (sp ? 0 : SI_W_SPORT), NAT_OUTBOUND); + MUTEX_EXIT(&softn->ipf_nat_new); if (nat2 != NULL) { - (void) nat_proto(&fi, nat2, IPN_UDP); - nat_update(&fi, nat2, nat2->nat_ptr); + (void) ipf_nat_proto(&fi, nat2, IPN_UDP); + MUTEX_ENTER(&nat2->nat_lock); + ipf_nat_update(&fi, nat2); + MUTEX_EXIT(&nat2->nat_lock); - (void) fr_addstate(&fi, NULL, (sp ? 0 : SI_W_SPORT)); - if (fi.fin_state != NULL) - fr_statederef((ipstate_t **)&fi.fin_state); + (void) ipf_state_add(softc, &fi, NULL, + (sp ? 0 : SI_W_SPORT)); } } @@ -318,16 +330,18 @@ nat_t *nat; fi.fin_data[0] = sp; fi.fin_data[1] = 0; fi.fin_out = 1; - nat2 = nat_new(&fi, nat->nat_ptr, NULL, + MUTEX_ENTER(&softn->ipf_nat_new); + nat2 = ipf_nat_add(&fi, nat->nat_ptr, NULL, NAT_SLAVE|IPN_UDP|SI_W_DPORT, NAT_OUTBOUND); + MUTEX_EXIT(&softn->ipf_nat_new); if (nat2 != NULL) { - (void) nat_proto(&fi, nat2, IPN_UDP); - nat_update(&fi, nat2, nat2->nat_ptr); + (void) ipf_nat_proto(&fi, nat2, IPN_UDP); + MUTEX_ENTER(&nat2->nat_lock); + ipf_nat_update(&fi, nat2); + MUTEX_EXIT(&nat2->nat_lock); - (void) fr_addstate(&fi, NULL, SI_W_DPORT); - if (fi.fin_state != NULL) - fr_statederef((ipstate_t **)&fi.fin_state); + (void) ipf_state_add(softc, &fi, NULL, SI_W_DPORT); } } diff --git a/sys/contrib/ipfilter/netinet/ip_rcmd_pxy.c b/sys/contrib/ipfilter/netinet/ip_rcmd_pxy.c index dc92bf54684..4b453c037d0 100644 --- a/sys/contrib/ipfilter/netinet/ip_rcmd_pxy.c +++ b/sys/contrib/ipfilter/netinet/ip_rcmd_pxy.c @@ -1,11 +1,11 @@ /* $FreeBSD$ */ /* - * Copyright (C) 1998-2003 by Darren Reed + * Copyright (C) 2012 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. * - * $Id: ip_rcmd_pxy.c,v 1.41.2.7 2006/07/14 06:12:18 darrenr Exp $ + * $Id$ * * Simple RCMD transparent proxy for in-kernel use. For use with the NAT * code. @@ -14,36 +14,45 @@ #define IPF_RCMD_PROXY +typedef struct rcmdinfo { + u_32_t rcmd_port; /* Port number seen */ + u_32_t rcmd_portseq; /* Sequence number where port is first seen */ + ipnat_t *rcmd_rule; /* Template rule for back connection */ +} rcmdinfo_t; -int ippr_rcmd_init __P((void)); -void ippr_rcmd_fini __P((void)); -int ippr_rcmd_new __P((fr_info_t *, ap_session_t *, nat_t *)); -int ippr_rcmd_out __P((fr_info_t *, ap_session_t *, nat_t *)); -int ippr_rcmd_in __P((fr_info_t *, ap_session_t *, nat_t *)); +void ipf_p_rcmd_main_load __P((void)); +void ipf_p_rcmd_main_unload __P((void)); + +int ipf_p_rcmd_init __P((void)); +void ipf_p_rcmd_fini __P((void)); +void ipf_p_rcmd_del __P((ipf_main_softc_t *, ap_session_t *)); +int ipf_p_rcmd_new __P((void *, fr_info_t *, ap_session_t *, nat_t *)); +int ipf_p_rcmd_out __P((void *, fr_info_t *, ap_session_t *, nat_t *)); +int ipf_p_rcmd_in __P((void *, fr_info_t *, ap_session_t *, nat_t *)); u_short ipf_rcmd_atoi __P((char *)); -int ippr_rcmd_portmsg __P((fr_info_t *, ap_session_t *, nat_t *)); +int ipf_p_rcmd_portmsg __P((fr_info_t *, ap_session_t *, nat_t *)); static frentry_t rcmdfr; -int rcmd_proxy_init = 0; +static int rcmd_proxy_init = 0; /* * RCMD application proxy initialization. */ -int ippr_rcmd_init() +void +ipf_p_rcmd_main_load() { bzero((char *)&rcmdfr, sizeof(rcmdfr)); rcmdfr.fr_ref = 1; rcmdfr.fr_flags = FR_INQUE|FR_PASS|FR_QUICK|FR_KEEPSTATE; MUTEX_INIT(&rcmdfr.fr_lock, "RCMD proxy rule lock"); rcmd_proxy_init = 1; - - return 0; } -void ippr_rcmd_fini() +void +ipf_p_rcmd_main_unload() { if (rcmd_proxy_init == 1) { MUTEX_DESTROY(&rcmdfr.fr_lock); @@ -55,36 +64,70 @@ void ippr_rcmd_fini() /* * Setup for a new RCMD proxy. */ -int ippr_rcmd_new(fin, aps, nat) -fr_info_t *fin; -ap_session_t *aps; -nat_t *nat; +int +ipf_p_rcmd_new(arg, fin, aps, nat) + void *arg; + fr_info_t *fin; + ap_session_t *aps; + nat_t *nat; { tcphdr_t *tcp = (tcphdr_t *)fin->fin_dp; + rcmdinfo_t *rc; + ipnat_t *ipn; + ipnat_t *np; + int size; fin = fin; /* LINT */ - nat = nat; /* LINT */ - aps->aps_psiz = sizeof(u_32_t); - KMALLOCS(aps->aps_data, u_32_t *, sizeof(u_32_t)); - if (aps->aps_data == NULL) { + np = nat->nat_ptr; + size = np->in_size; + KMALLOC(rc, rcmdinfo_t *); + if (rc == NULL) { #ifdef IP_RCMD_PROXY_DEBUG - printf("ippr_rcmd_new:KMALLOCS(%d) failed\n", sizeof(u_32_t)); + printf("ipf_p_rcmd_new:KMALLOCS(%d) failed\n", sizeof(*rc)); #endif return -1; } - *(u_32_t *)aps->aps_data = 0; aps->aps_sport = tcp->th_sport; aps->aps_dport = tcp->th_dport; + + ipn = ipf_proxy_rule_rev(nat); + if (ipn == NULL) { + KFREE(rc); + return -1; + } + + aps->aps_data = rc; + aps->aps_psiz = sizeof(*rc); + bzero((char *)rc, sizeof(*rc)); + + rc->rcmd_rule = ipn; + return 0; } +void +ipf_p_rcmd_del(softc, aps) + ipf_main_softc_t *softc; + ap_session_t *aps; +{ + rcmdinfo_t *rci; + + rci = aps->aps_data; + if (rci != NULL) { + rci->rcmd_rule->in_flags |= IPN_DELETE; + ipf_nat_rule_deref(softc, &rci->rcmd_rule); + } +} + + /* * ipf_rcmd_atoi - implement a simple version of atoi */ -u_short ipf_rcmd_atoi(ptr) -char *ptr; +u_short +ipf_rcmd_atoi(ptr) + char *ptr; { register char *s = ptr, c; register u_short i = 0; @@ -97,44 +140,50 @@ char *ptr; } -int ippr_rcmd_portmsg(fin, aps, nat) -fr_info_t *fin; -ap_session_t *aps; -nat_t *nat; +int +ipf_p_rcmd_portmsg(fin, aps, nat) + fr_info_t *fin; + ap_session_t *aps; + nat_t *nat; { tcphdr_t *tcp, tcph, *tcp2 = &tcph; - struct in_addr swip, swip2; - int off, dlen, nflags; + int off, dlen, nflags, direction; + ipf_main_softc_t *softc; + ipf_nat_softc_t *softn; char portbuf[8], *s; + rcmdinfo_t *rc; fr_info_t fi; u_short sp; nat_t *nat2; +#ifdef USE_INET6 + ip6_t *ip6; +#endif + int tcpsz; + int slen; ip_t *ip; mb_t *m; tcp = (tcphdr_t *)fin->fin_dp; - if (tcp->th_flags & TH_SYN) { - *(u_32_t *)aps->aps_data = htonl(ntohl(tcp->th_seq) + 1); - return 0; - } - - if ((*(u_32_t *)aps->aps_data != 0) && - (tcp->th_seq != *(u_32_t *)aps->aps_data)) - return 0; - m = fin->fin_m; ip = fin->fin_ip; - off = (char *)tcp - (char *)ip + (TCP_OFF(tcp) << 2) + fin->fin_ipoff; - -#ifdef __sgi - dlen = fin->fin_plen - off; -#else - dlen = MSGDSIZE(m) - off; + tcpsz = TCP_OFF(tcp) << 2; +#ifdef USE_INET6 + ip6 = (ip6_t *)fin->fin_ip; #endif + softc = fin->fin_main_soft; + softn = softc->ipf_nat_soft; + off = (char *)tcp - (char *)ip + tcpsz + fin->fin_ipoff; + + dlen = fin->fin_dlen - tcpsz; if (dlen <= 0) return 0; + rc = (rcmdinfo_t *)aps->aps_data; + if ((rc->rcmd_portseq != 0) && + (tcp->th_seq != rc->rcmd_portseq)) + return 0; + bzero(portbuf, sizeof(portbuf)); COPYDATA(m, off, MIN(sizeof(portbuf), dlen), portbuf); @@ -143,97 +192,161 @@ nat_t *nat; sp = ipf_rcmd_atoi(s); if (sp == 0) { #ifdef IP_RCMD_PROXY_DEBUG - printf("ippr_rcmd_portmsg:sp == 0 dlen %d [%s]\n", + printf("ipf_p_rcmd_portmsg:sp == 0 dlen %d [%s]\n", dlen, portbuf); #endif return 0; } + if (rc->rcmd_port != 0 && sp != rc->rcmd_port) { +#ifdef IP_RCMD_PROXY_DEBUG + printf("ipf_p_rcmd_portmsg:sp(%d) != rcmd_port(%d)\n", + sp, rc->rcmd_port); +#endif + return 0; + } + + rc->rcmd_port = sp; + rc->rcmd_portseq = tcp->th_seq; + /* - * Add skeleton NAT entry for connection which will come back the - * other way. + * Initialise the packet info structure so we can search the NAT + * table to see if there already is soemthing present that matches + * up with what we want to add. */ bcopy((char *)fin, (char *)&fi, sizeof(fi)); - fi.fin_state = NULL; - fi.fin_nat = NULL; fi.fin_flx |= FI_IGNORE; - fi.fin_data[0] = sp; - fi.fin_data[1] = 0; - if (nat->nat_dir == NAT_OUTBOUND) - nat2 = nat_outlookup(&fi, NAT_SEARCH|IPN_TCP, nat->nat_p, - nat->nat_inip, nat->nat_oip); - else - nat2 = nat_inlookup(&fi, NAT_SEARCH|IPN_TCP, nat->nat_p, - nat->nat_inip, nat->nat_oip); - if (nat2 == NULL) { - int slen; - - slen = ip->ip_len; - ip->ip_len = fin->fin_hlen + sizeof(*tcp); - bzero((char *)tcp2, sizeof(*tcp2)); - tcp2->th_win = htons(8192); - tcp2->th_sport = htons(sp); - tcp2->th_dport = 0; /* XXX - don't specify remote port */ - TCP_OFF_A(tcp2, 5); - tcp2->th_flags = TH_SYN; - fi.fin_dp = (char *)tcp2; - fi.fin_fr = &rcmdfr; - fi.fin_dlen = sizeof(*tcp2); - fi.fin_plen = fi.fin_hlen + sizeof(*tcp2); - fi.fin_flx &= FI_LOWTTL|FI_FRAG|FI_TCPUDP|FI_OPTIONS|FI_IGNORE; - nflags = NAT_SLAVE|IPN_TCP|SI_W_DPORT; - - swip = ip->ip_src; - swip2 = ip->ip_dst; + fi.fin_data[0] = 0; + fi.fin_data[1] = sp; + fi.fin_src6 = nat->nat_ndst6; + fi.fin_dst6 = nat->nat_nsrc6; + if (nat->nat_v[0] == 6) { +#ifdef USE_INET6 if (nat->nat_dir == NAT_OUTBOUND) { - fi.fin_fi.fi_saddr = nat->nat_inip.s_addr; - ip->ip_src = nat->nat_inip; + nat2 = ipf_nat6_outlookup(&fi, NAT_SEARCH|IPN_TCP, + nat->nat_pr[1], + &nat->nat_osrc6.in6, + &nat->nat_odst6.in6); } else { - fi.fin_fi.fi_saddr = nat->nat_oip.s_addr; - ip->ip_src = nat->nat_oip; - nflags |= NAT_NOTRULEPORT; + nat2 = ipf_nat6_inlookup(&fi, NAT_SEARCH|IPN_TCP, + nat->nat_pr[0], + &nat->nat_osrc6.in6, + &nat->nat_odst6.in6); } - - nat2 = nat_new(&fi, nat->nat_ptr, NULL, nflags, nat->nat_dir); - - if (nat2 != NULL) { - (void) nat_proto(&fi, nat2, IPN_TCP); - nat_update(&fi, nat2, nat2->nat_ptr); - fi.fin_ifp = NULL; - if (nat->nat_dir == NAT_INBOUND) { - fi.fin_fi.fi_daddr = nat->nat_inip.s_addr; - ip->ip_dst = nat->nat_inip; - } - (void) fr_addstate(&fi, NULL, SI_W_DPORT); - if (fi.fin_state != NULL) - fr_statederef((ipstate_t **)&fi.fin_state); +#else + nat2 = (void *)-1; +#endif + } else { + if (nat->nat_dir == NAT_OUTBOUND) { + nat2 = ipf_nat_outlookup(&fi, NAT_SEARCH|IPN_TCP, + nat->nat_pr[1], + nat->nat_osrcip, + nat->nat_odstip); + } else { + nat2 = ipf_nat_inlookup(&fi, NAT_SEARCH|IPN_TCP, + nat->nat_pr[0], + nat->nat_osrcip, + nat->nat_odstip); } - ip->ip_len = slen; - ip->ip_src = swip; - ip->ip_dst = swip2; } + if (nat2 != NULL) + return APR_ERR(1); + + /* + * Add skeleton NAT entry for connection which will come + * back the other way. + */ + + if (nat->nat_v[0] == 6) { +#ifdef USE_INET6 + slen = ip6->ip6_plen; + ip6->ip6_plen = htons(sizeof(*tcp)); +#endif + } else { + slen = ip->ip_len; + ip->ip_len = htons(fin->fin_hlen + sizeof(*tcp)); + } + + /* + * Fill out the fake TCP header with a few fields that ipfilter + * considers to be important. + */ + bzero((char *)tcp2, sizeof(*tcp2)); + tcp2->th_win = htons(8192); + TCP_OFF_A(tcp2, 5); + tcp2->th_flags = TH_SYN; + + fi.fin_dp = (char *)tcp2; + fi.fin_fr = &rcmdfr; + fi.fin_dlen = sizeof(*tcp2); + fi.fin_plen = fi.fin_hlen + sizeof(*tcp2); + fi.fin_flx &= FI_LOWTTL|FI_FRAG|FI_TCPUDP|FI_OPTIONS|FI_IGNORE; + + if (nat->nat_dir == NAT_OUTBOUND) { + fi.fin_out = 0; + direction = NAT_INBOUND; + } else { + fi.fin_out = 1; + direction = NAT_OUTBOUND; + } + nflags = SI_W_SPORT|NAT_SLAVE|IPN_TCP; + + MUTEX_ENTER(&softn->ipf_nat_new); + if (fin->fin_v == 4) + nat2 = ipf_nat_add(&fi, rc->rcmd_rule, NULL, nflags, + direction); +#ifdef USE_INET6 + else + nat2 = ipf_nat6_add(&fi, rc->rcmd_rule, NULL, nflags, + direction); +#endif + MUTEX_EXIT(&softn->ipf_nat_new); + + if (nat2 != NULL) { + (void) ipf_nat_proto(&fi, nat2, IPN_TCP); + MUTEX_ENTER(&nat2->nat_lock); + ipf_nat_update(&fi, nat2); + MUTEX_EXIT(&nat2->nat_lock); + fi.fin_ifp = NULL; + if (nat2->nat_dir == NAT_INBOUND) + fi.fin_dst6 = nat->nat_osrc6; + (void) ipf_state_add(softc, &fi, NULL, SI_W_SPORT); + } + if (nat->nat_v[0] == 6) { +#ifdef USE_INET6 + ip6->ip6_plen = slen; +#endif + } else { + ip->ip_len = slen; + } + if (nat2 == NULL) + return APR_ERR(1); return 0; } -int ippr_rcmd_out(fin, aps, nat) -fr_info_t *fin; -ap_session_t *aps; -nat_t *nat; +int +ipf_p_rcmd_out(arg, fin, aps, nat) + void *arg; + fr_info_t *fin; + ap_session_t *aps; + nat_t *nat; { if (nat->nat_dir == NAT_OUTBOUND) - return ippr_rcmd_portmsg(fin, aps, nat); + return ipf_p_rcmd_portmsg(fin, aps, nat); return 0; } -int ippr_rcmd_in(fin, aps, nat) -fr_info_t *fin; -ap_session_t *aps; -nat_t *nat; +int +ipf_p_rcmd_in(arg, fin, aps, nat) + void *arg; + fr_info_t *fin; + ap_session_t *aps; + nat_t *nat; { if (nat->nat_dir == NAT_INBOUND) - return ippr_rcmd_portmsg(fin, aps, nat); + return ipf_p_rcmd_portmsg(fin, aps, nat); return 0; } diff --git a/sys/contrib/ipfilter/netinet/ip_rpcb_pxy.c b/sys/contrib/ipfilter/netinet/ip_rpcb_pxy.c index da76fde4d15..9493d2bfae3 100644 --- a/sys/contrib/ipfilter/netinet/ip_rpcb_pxy.c +++ b/sys/contrib/ipfilter/netinet/ip_rpcb_pxy.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2003 by Ryan Beasley + * Copyright (C) 2002-2012 by Ryan Beasley * * See the IPFILTER.LICENCE file for details on licencing. */ @@ -37,44 +37,43 @@ * o The enclosed hack of STREAMS support is pretty sick and most likely * broken. * - * $Id: ip_rpcb_pxy.c,v 2.25.2.7 2007/06/04 09:16:31 darrenr Exp $ + * $Id$ */ - #define IPF_RPCB_PROXY /* * Function prototypes */ -int ippr_rpcb_init __P((void)); -void ippr_rpcb_fini __P((void)); -int ippr_rpcb_new __P((fr_info_t *, ap_session_t *, nat_t *)); -void ippr_rpcb_del __P((ap_session_t *)); -int ippr_rpcb_in __P((fr_info_t *, ap_session_t *, nat_t *)); -int ippr_rpcb_out __P((fr_info_t *, ap_session_t *, nat_t *)); +void ipf_p_rpcb_main_load __P((void)); +void ipf_p_rpcb_main_unload __P((void)); +int ipf_p_rpcb_new __P((void *, fr_info_t *, ap_session_t *, nat_t *)); +void ipf_p_rpcb_del __P((ipf_main_softc_t *, ap_session_t *)); +int ipf_p_rpcb_in __P((void *, fr_info_t *, ap_session_t *, nat_t *)); +int ipf_p_rpcb_out __P((void *, fr_info_t *, ap_session_t *, nat_t *)); -static void ippr_rpcb_flush __P((rpcb_session_t *)); -static int ippr_rpcb_decodereq __P((fr_info_t *, nat_t *, +static void ipf_p_rpcb_flush __P((rpcb_session_t *)); +static int ipf_p_rpcb_decodereq __P((fr_info_t *, nat_t *, rpcb_session_t *, rpc_msg_t *)); -static int ippr_rpcb_skipauth __P((rpc_msg_t *, xdr_auth_t *, u_32_t **)); -static int ippr_rpcb_insert __P((rpcb_session_t *, rpcb_xact_t *)); -static int ippr_rpcb_xdrrpcb __P((rpc_msg_t *, u_32_t *, rpcb_args_t *)); -static int ippr_rpcb_getuaddr __P((rpc_msg_t *, xdr_uaddr_t *, +static int ipf_p_rpcb_skipauth __P((rpc_msg_t *, xdr_auth_t *, u_32_t **)); +static int ipf_p_rpcb_insert __P((rpcb_session_t *, rpcb_xact_t *)); +static int ipf_p_rpcb_xdrrpcb __P((rpc_msg_t *, u_32_t *, rpcb_args_t *)); +static int ipf_p_rpcb_getuaddr __P((rpc_msg_t *, xdr_uaddr_t *, u_32_t **)); -static u_int ippr_rpcb_atoi __P((char *)); -static int ippr_rpcb_modreq __P((fr_info_t *, nat_t *, rpc_msg_t *, +static u_int ipf_p_rpcb_atoi __P((char *)); +static int ipf_p_rpcb_modreq __P((fr_info_t *, nat_t *, rpc_msg_t *, mb_t *, u_int)); -static int ippr_rpcb_decoderep __P((fr_info_t *, nat_t *, +static int ipf_p_rpcb_decoderep __P((fr_info_t *, nat_t *, rpcb_session_t *, rpc_msg_t *, rpcb_xact_t **)); -static rpcb_xact_t * ippr_rpcb_lookup __P((rpcb_session_t *, u_32_t)); -static void ippr_rpcb_deref __P((rpcb_session_t *, rpcb_xact_t *)); -static int ippr_rpcb_getproto __P((rpc_msg_t *, xdr_proto_t *, +static rpcb_xact_t * ipf_p_rpcb_lookup __P((rpcb_session_t *, u_32_t)); +static void ipf_p_rpcb_deref __P((rpcb_session_t *, rpcb_xact_t *)); +static int ipf_p_rpcb_getproto __P((rpc_msg_t *, xdr_proto_t *, u_32_t **)); -static int ippr_rpcb_getnat __P((fr_info_t *, nat_t *, u_int, u_int)); -static int ippr_rpcb_modv3 __P((fr_info_t *, nat_t *, rpc_msg_t *, +static int ipf_p_rpcb_getnat __P((fr_info_t *, nat_t *, u_int, u_int)); +static int ipf_p_rpcb_modv3 __P((fr_info_t *, nat_t *, rpc_msg_t *, mb_t *, u_int)); -static int ippr_rpcb_modv4 __P((fr_info_t *, nat_t *, rpc_msg_t *, +static int ipf_p_rpcb_modv4 __P((fr_info_t *, nat_t *, rpc_msg_t *, mb_t *, u_int)); -static void ippr_rpcb_fixlen __P((fr_info_t *, int)); +static void ipf_p_rpcb_fixlen __P((fr_info_t *, int)); /* * Global variables @@ -84,7 +83,7 @@ static frentry_t rpcbfr; /* Skeleton rule for reference by entities static int rpcbcnt; /* Upper bound of allocated RPCB sessions. */ /* XXX rpcbcnt still requires locking. */ -int rpcb_proxy_init = 0; +static int rpcb_proxy_init = 0; /* @@ -98,15 +97,15 @@ int rpcb_proxy_init = 0; * Public subroutines */ -/* -------------------------------------------------------------------- */ -/* Function: ippr_rpcb_init */ -/* Returns: int - 0 == success */ -/* Parameters: (void) */ -/* */ -/* Initialize the filter rule entry and session limiter. */ -/* -------------------------------------------------------------------- */ -int -ippr_rpcb_init() +/* -------------------------------------------------------------------- */ +/* Function: ipf_p_rpcb_main_load */ +/* Returns: void */ +/* Parameters: (void) */ +/* */ +/* Initialize the filter rule entry and session limiter. */ +/* -------------------------------------------------------------------- */ +void +ipf_p_rpcb_main_load() { rpcbcnt = 0; @@ -115,19 +114,17 @@ ippr_rpcb_init() rpcbfr.fr_flags = FR_PASS|FR_QUICK|FR_KEEPSTATE; MUTEX_INIT(&rpcbfr.fr_lock, "ipf Sun RPCB proxy rule lock"); rpcb_proxy_init = 1; - - return(0); } -/* -------------------------------------------------------------------- */ -/* Function: ippr_rpcb_fini */ -/* Returns: void */ -/* Parameters: (void) */ -/* */ -/* Destroy rpcbfr's mutex to avoid a lock leak. */ -/* -------------------------------------------------------------------- */ +/* -------------------------------------------------------------------- */ +/* Function: ipf_p_rpcb_main_unload */ +/* Returns: void */ +/* Parameters: (void) */ +/* */ +/* Destroy rpcbfr's mutex to avoid a lock leak. */ +/* -------------------------------------------------------------------- */ void -ippr_rpcb_fini() +ipf_p_rpcb_main_unload() { if (rpcb_proxy_init == 1) { MUTEX_DESTROY(&rpcbfr.fr_lock); @@ -136,7 +133,7 @@ ippr_rpcb_fini() } /* -------------------------------------------------------------------- */ -/* Function: ippr_rpcb_new */ +/* Function: ipf_p_rpcb_new */ /* Returns: int - -1 == failure, 0 == success */ /* Parameters: fin(I) - pointer to packet information */ /* aps(I) - pointer to proxy session structure */ @@ -145,16 +142,19 @@ ippr_rpcb_fini() /* Allocate resources for per-session proxy structures. */ /* -------------------------------------------------------------------- */ int -ippr_rpcb_new(fin, aps, nat) +ipf_p_rpcb_new(arg, fin, aps, nat) + void *arg; fr_info_t *fin; ap_session_t *aps; nat_t *nat; { rpcb_session_t *rs; - fin = fin; /* LINT */ nat = nat; /* LINT */ + if (fin->fin_v != 4) + return -1; + KMALLOC(rs, rpcb_session_t *); if (rs == NULL) return(-1); @@ -168,27 +168,28 @@ ippr_rpcb_new(fin, aps, nat) } /* -------------------------------------------------------------------- */ -/* Function: ippr_rpcb_del */ +/* Function: ipf_p_rpcb_del */ /* Returns: void */ /* Parameters: aps(I) - pointer to proxy session structure */ /* */ /* Free up a session's list of RPCB requests. */ /* -------------------------------------------------------------------- */ void -ippr_rpcb_del(aps) +ipf_p_rpcb_del(softc, aps) + ipf_main_softc_t *softc; ap_session_t *aps; { rpcb_session_t *rs; rs = (rpcb_session_t *)aps->aps_data; MUTEX_ENTER(&rs->rs_rxlock); - ippr_rpcb_flush(rs); + ipf_p_rpcb_flush(rs); MUTEX_EXIT(&rs->rs_rxlock); MUTEX_DESTROY(&rs->rs_rxlock); } /* -------------------------------------------------------------------- */ -/* Function: ippr_rpcb_in */ +/* Function: ipf_p_rpcb_in */ /* Returns: int - APR_ERR(1) == drop the packet, */ /* APR_ERR(2) == kill the proxy session, */ /* else change in packet length (in bytes) */ @@ -201,7 +202,8 @@ ippr_rpcb_del(aps) /* for decoding. Also pass packet off for a rewrite if necessary. */ /* -------------------------------------------------------------------- */ int -ippr_rpcb_in(fin, aps, nat) +ipf_p_rpcb_in(arg, fin, aps, nat) + void *arg; fr_info_t *fin; ap_session_t *aps; nat_t *nat; @@ -235,7 +237,7 @@ ippr_rpcb_in(fin, aps, nat) rm->rm_buflen = dlen; /* Send off to decode request. */ - rv = ippr_rpcb_decodereq(fin, nat, rs, rm); + rv = ipf_p_rpcb_decodereq(fin, nat, rs, rm); switch(rv) { @@ -246,18 +248,18 @@ ippr_rpcb_in(fin, aps, nat) case 0: break; case 1: - rv = ippr_rpcb_modreq(fin, nat, rm, m, off); + rv = ipf_p_rpcb_modreq(fin, nat, rm, m, off); break; default: /*CONSTANTCONDITION*/ - IPF_PANIC(1, ("illegal rv %d (ippr_rpcb_req)", rv)); + IPF_PANIC(1, ("illegal rv %d (ipf_p_rpcb_req)", rv)); } return(rv); } /* -------------------------------------------------------------------- */ -/* Function: ippr_rpcb_out */ +/* Function: ipf_p_rpcb_out */ /* Returns: int - APR_ERR(1) == drop the packet, */ /* APR_ERR(2) == kill the proxy session, */ /* else change in packet length (in bytes) */ @@ -272,7 +274,8 @@ ippr_rpcb_in(fin, aps, nat) /* allow direct communication between RPC client and server. */ /* -------------------------------------------------------------------- */ int -ippr_rpcb_out(fin, aps, nat) +ipf_p_rpcb_out(arg, fin, aps, nat) + void *arg; fr_info_t *fin; ap_session_t *aps; nat_t *nat; @@ -311,14 +314,14 @@ ippr_rpcb_out(fin, aps, nat) rx = NULL; /* XXX gcc */ /* Send off to decode reply. */ - rv = ippr_rpcb_decoderep(fin, nat, rs, rm, &rx); + rv = ipf_p_rpcb_decoderep(fin, nat, rs, rm, &rx); switch(rv) { case -1: /* Bad packet */ if (rx != NULL) { MUTEX_ENTER(&rs->rs_rxlock); - ippr_rpcb_deref(rs, rx); + ipf_p_rpcb_deref(rs, rx); MUTEX_EXIT(&rs->rs_rxlock); } return(APR_ERR(1)); @@ -334,16 +337,16 @@ ippr_rpcb_out(fin, aps, nat) * same. (i.e., this box is either a router or rpcbind * only listens on loopback.) */ - if (nat->nat_inip.s_addr != nat->nat_outip.s_addr) { + if (nat->nat_odstaddr != nat->nat_ndstaddr) { if (rx->rx_type == RPCB_RES_STRING) - diff = ippr_rpcb_modv3(fin, nat, rm, m, off); + diff = ipf_p_rpcb_modv3(fin, nat, rm, m, off); else if (rx->rx_type == RPCB_RES_LIST) - diff = ippr_rpcb_modv4(fin, nat, rm, m, off); + diff = ipf_p_rpcb_modv4(fin, nat, rm, m, off); } break; default: /*CONSTANTCONDITION*/ - IPF_PANIC(1, ("illegal rv %d (ippr_rpcb_decoderep)", rv)); + IPF_PANIC(1, ("illegal rv %d (ipf_p_rpcb_decoderep)", rv)); } if (rx != NULL) { @@ -354,8 +357,8 @@ ippr_rpcb_out(fin, aps, nat) * finished with rx, and the other signals that we've * processed its reply. */ - ippr_rpcb_deref(rs, rx); - ippr_rpcb_deref(rs, rx); + ipf_p_rpcb_deref(rs, rx); + ipf_p_rpcb_deref(rs, rx); MUTEX_EXIT(&rs->rs_rxlock); } @@ -367,14 +370,14 @@ ippr_rpcb_out(fin, aps, nat) */ /* -------------------------------------------------------------------- */ -/* Function: ippr_rpcb_flush */ +/* Function: ipf_p_rpcb_flush */ /* Returns: void */ /* Parameters: rs(I) - pointer to RPCB session structure */ /* */ /* Simply flushes the list of outstanding transactions, if any. */ /* -------------------------------------------------------------------- */ static void -ippr_rpcb_flush(rs) +ipf_p_rpcb_flush(rs) rpcb_session_t *rs; { rpcb_xact_t *r1, *r2; @@ -391,7 +394,7 @@ ippr_rpcb_flush(rs) } /* -------------------------------------------------------------------- */ -/* Function: ippr_rpcb_decodereq */ +/* Function: ipf_p_rpcb_decodereq */ /* Returns: int - -1 == bad request or critical failure, */ /* 0 == request successfully decoded, */ /* 1 == request successfully decoded; requires */ @@ -408,7 +411,7 @@ ippr_rpcb_flush(rs) /* is enough room in rs_buf for the basic RPC message "preamble". */ /* -------------------------------------------------------------------- */ static int -ippr_rpcb_decodereq(fin, nat, rs, rm) +ipf_p_rpcb_decodereq(fin, nat, rs, rm) fr_info_t *fin; nat_t *nat; rpcb_session_t *rs; @@ -440,9 +443,9 @@ ippr_rpcb_decodereq(fin, nat, rs, rm) rc->rc_proc = p++; /* Bypass RPC authentication stuff. */ - if (ippr_rpcb_skipauth(rm, &rc->rc_authcred, &p) != 0) + if (ipf_p_rpcb_skipauth(rm, &rc->rc_authcred, &p) != 0) return(-1); - if (ippr_rpcb_skipauth(rm, &rc->rc_authverf, &p) != 0) + if (ipf_p_rpcb_skipauth(rm, &rc->rc_authverf, &p) != 0) return(-1); /* Compare RPCB version and procedure numbers. */ @@ -488,17 +491,17 @@ ippr_rpcb_decodereq(fin, nat, rs, rm) ra = &rc->rc_rpcbargs; /* Decode the 'struct rpcb' request. */ - if (ippr_rpcb_xdrrpcb(rm, p, ra) != 0) + if (ipf_p_rpcb_xdrrpcb(rm, p, ra) != 0) return(-1); /* Are the target address & port valid? */ - if ((ra->ra_maddr.xu_ip != nat->nat_outip.s_addr) || - (ra->ra_maddr.xu_port != nat->nat_outport)) + if ((ra->ra_maddr.xu_ip != nat->nat_ndstaddr) || + (ra->ra_maddr.xu_port != nat->nat_ndport)) return(-1); /* Do we need to rewrite this packet? */ - if ((nat->nat_outip.s_addr != nat->nat_inip.s_addr) || - (nat->nat_outport != nat->nat_inport)) + if ((nat->nat_ndstaddr != nat->nat_odstaddr) || + (nat->nat_ndport != nat->nat_odport)) mod = 1; break; default: @@ -506,7 +509,7 @@ ippr_rpcb_decodereq(fin, nat, rs, rm) } MUTEX_ENTER(&rs->rs_rxlock); - if (ippr_rpcb_insert(rs, &rx) != 0) { + if (ipf_p_rpcb_insert(rs, &rx) != 0) { MUTEX_EXIT(&rs->rs_rxlock); return(-1); } @@ -516,7 +519,7 @@ ippr_rpcb_decodereq(fin, nat, rs, rm) } /* -------------------------------------------------------------------- */ -/* Function: ippr_rpcb_skipauth */ +/* Function: ipf_p_rpcb_skipauth */ /* Returns: int -- -1 == illegal auth parameters (lengths) */ /* 0 == valid parameters, pointer advanced */ /* Parameters: rm(I) - pointer to RPC message structure */ @@ -527,7 +530,7 @@ ippr_rpcb_decodereq(fin, nat, rs, rm) /* it. */ /* -------------------------------------------------------------------- */ static int -ippr_rpcb_skipauth(rm, auth, buf) +ipf_p_rpcb_skipauth(rm, auth, buf) rpc_msg_t *rm; xdr_auth_t *auth; u_32_t **buf; @@ -559,20 +562,20 @@ ippr_rpcb_skipauth(rm, auth, buf) } /* -------------------------------------------------------------------- */ -/* Function: ippr_rpcb_insert */ +/* Function: ipf_p_rpcb_insert */ /* Returns: int -- -1 == list insertion failed, */ /* 0 == item successfully added */ /* Parameters: rs(I) - pointer to RPCB session structure */ /* rx(I) - pointer to RPCB transaction structure */ /* -------------------------------------------------------------------- */ static int -ippr_rpcb_insert(rs, rx) +ipf_p_rpcb_insert(rs, rx) rpcb_session_t *rs; rpcb_xact_t *rx; { rpcb_xact_t *rxp; - rxp = ippr_rpcb_lookup(rs, rx->rx_xid); + rxp = ipf_p_rpcb_lookup(rs, rx->rx_xid); if (rxp != NULL) { ++rxp->rx_ref; return(0); @@ -602,7 +605,7 @@ ippr_rpcb_insert(rs, rx) } /* -------------------------------------------------------------------- */ -/* Function: ippr_rpcb_xdrrpcb */ +/* Function: ipf_p_rpcb_xdrrpcb */ /* Returns: int -- -1 == failure to properly decode the request */ /* 0 == rpcb successfully decoded */ /* Parameters: rs(I) - pointer to RPCB session structure */ @@ -613,7 +616,7 @@ ippr_rpcb_insert(rs, rx) /* within only the context of TCP/UDP over IP networks. */ /* -------------------------------------------------------------------- */ static int -ippr_rpcb_xdrrpcb(rm, p, ra) +ipf_p_rpcb_xdrrpcb(rm, p, ra) rpc_msg_t *rm; u_32_t *p; rpcb_args_t *ra; @@ -625,11 +628,11 @@ ippr_rpcb_xdrrpcb(rm, p, ra) p += 2; /* Decode r_netid. Must be "tcp" or "udp". */ - if (ippr_rpcb_getproto(rm, &ra->ra_netid, &p) != 0) + if (ipf_p_rpcb_getproto(rm, &ra->ra_netid, &p) != 0) return(-1); /* Decode r_maddr. */ - if (ippr_rpcb_getuaddr(rm, &ra->ra_maddr, &p) != 0) + if (ipf_p_rpcb_getuaddr(rm, &ra->ra_maddr, &p) != 0) return(-1); /* Advance to r_owner and make sure it's empty. */ @@ -640,7 +643,7 @@ ippr_rpcb_xdrrpcb(rm, p, ra) } /* -------------------------------------------------------------------- */ -/* Function: ippr_rpcb_getuaddr */ +/* Function: ipf_p_rpcb_getuaddr */ /* Returns: int -- -1 == illegal string, */ /* 0 == string parsed; contents recorded */ /* Parameters: rm(I) - pointer to RPC message structure */ @@ -650,7 +653,7 @@ ippr_rpcb_xdrrpcb(rm, p, ra) /* Decode the IP address / port at p and record them in xu. */ /* -------------------------------------------------------------------- */ static int -ippr_rpcb_getuaddr(rm, xu, p) +ipf_p_rpcb_getuaddr(rm, xu, p) rpc_msg_t *rm; xdr_uaddr_t *xu; u_32_t **p; @@ -699,7 +702,7 @@ ippr_rpcb_getuaddr(rm, xu, p) /* Check for ASCII byte. */ *c = '\0'; - t = ippr_rpcb_atoi(b); + t = ipf_p_rpcb_atoi(b); if (t > 255) return(-1); @@ -721,7 +724,7 @@ ippr_rpcb_getuaddr(rm, xu, p) return(-1); /* Handle the last byte (port low byte) */ - t = ippr_rpcb_atoi(b); + t = ipf_p_rpcb_atoi(b); if (t > 255) return(-1); pp[d - 4] = t & 0xff; @@ -730,14 +733,14 @@ ippr_rpcb_getuaddr(rm, xu, p) } /* -------------------------------------------------------------------- */ -/* Function: ippr_rpcb_atoi (XXX should be generic for all proxies) */ +/* Function: ipf_p_rpcb_atoi (XXX should be generic for all proxies) */ /* Returns: int -- integer representation of supplied string */ /* Parameters: ptr(I) - input string */ /* */ /* Simple version of atoi(3) ripped from ip_rcmd_pxy.c. */ /* -------------------------------------------------------------------- */ static u_int -ippr_rpcb_atoi(ptr) +ipf_p_rpcb_atoi(ptr) char *ptr; { register char *s = ptr, c; @@ -751,7 +754,7 @@ ippr_rpcb_atoi(ptr) } /* -------------------------------------------------------------------- */ -/* Function: ippr_rpcb_modreq */ +/* Function: ipf_p_rpcb_modreq */ /* Returns: int -- change in datagram length */ /* APR_ERR(2) - critical failure */ /* Parameters: fin(I) - pointer to packet information */ @@ -764,7 +767,7 @@ ippr_rpcb_atoi(ptr) /* with the latter. (This is exclusive to protocol versions 3 & 4). */ /* -------------------------------------------------------------------- */ static int -ippr_rpcb_modreq(fin, nat, rm, m, off) +ipf_p_rpcb_modreq(fin, nat, rm, m, off) fr_info_t *fin; nat_t *nat; rpc_msg_t *rm; @@ -779,8 +782,8 @@ ippr_rpcb_modreq(fin, nat, rm, m, off) int diff; ra = &rm->rm_call.rc_rpcbargs; - i = (char *)&nat->nat_inip.s_addr; - p = (char *)&nat->nat_inport; + i = (char *)&nat->nat_odstaddr; + p = (char *)&nat->nat_odport; /* Form new string. */ bzero(uaddr, sizeof(uaddr)); /* Just in case we need padding. */ @@ -821,9 +824,9 @@ ippr_rpcb_modreq(fin, nat, rm, m, off) if (diff != 0) { udp = fin->fin_dp; udp->uh_ulen = htons(ntohs(udp->uh_ulen) + diff); - fin->fin_ip->ip_len += diff; - fin->fin_dlen += diff; fin->fin_plen += diff; + fin->fin_ip->ip_len = htons(fin->fin_plen); + fin->fin_dlen += diff; /* XXX Storage lengths. */ } @@ -831,7 +834,7 @@ ippr_rpcb_modreq(fin, nat, rm, m, off) } /* -------------------------------------------------------------------- */ -/* Function: ippr_rpcb_decoderep */ +/* Function: ipf_p_rpcb_decoderep */ /* Returns: int - -1 == bad request or critical failure, */ /* 0 == valid, negative reply */ /* 1 == vaddlid, positive reply; needs no changes */ @@ -851,7 +854,7 @@ ippr_rpcb_modreq(fin, nat, rm, m, off) /* is enough room in rs_buf for the basic RPC message "preamble". */ /* -------------------------------------------------------------------- */ static int -ippr_rpcb_decoderep(fin, nat, rs, rm, rxp) +ipf_p_rpcb_decoderep(fin, nat, rs, rm, rxp) fr_info_t *fin; nat_t *nat; rpcb_session_t *rs; @@ -875,7 +878,7 @@ ippr_rpcb_decoderep(fin, nat, rs, rm, rxp) /* Lookup XID */ MUTEX_ENTER(&rs->rs_rxlock); - if ((rx = ippr_rpcb_lookup(rs, xdr)) == NULL) { + if ((rx = ipf_p_rpcb_lookup(rs, xdr)) == NULL) { MUTEX_EXIT(&rs->rs_rxlock); return(-1); } @@ -900,7 +903,7 @@ ippr_rpcb_decoderep(fin, nat, rs, rm, rxp) } /* Bypass RPC authentication stuff. */ - if (ippr_rpcb_skipauth(rm, &rr->rr_authverf, &p) != 0) + if (ipf_p_rpcb_skipauth(rm, &rr->rr_authverf, &p) != 0) return(-1); /* Test accept status */ @@ -916,20 +919,20 @@ ippr_rpcb_decoderep(fin, nat, rs, rm, rxp) /* There must be only one 4 byte argument. */ if (!RPCB_BUF_EQ(rm, p, 4)) return(-1); - + rr->rr_v2 = p; xdr = B(rr->rr_v2); - + /* Reply w/ a 0 port indicates service isn't registered */ if (xdr == 0) return(0); - + /* Is the value sane? */ if (xdr > 65535) return(-1); /* Create NAT & state table entries. */ - if (ippr_rpcb_getnat(fin, nat, rx->rx_proto, (u_int)xdr) != 0) + if (ipf_p_rpcb_getnat(fin, nat, rx->rx_proto, (u_int)xdr) != 0) return(-1); break; case RPCB_RES_STRING: @@ -947,15 +950,15 @@ ippr_rpcb_decoderep(fin, nat, rs, rm, rxp) return(0); /* Decode the target IP address / port. */ - if (ippr_rpcb_getuaddr(rm, &rr->rr_v3, &p) != 0) + if (ipf_p_rpcb_getuaddr(rm, &rr->rr_v3, &p) != 0) return(-1); /* Validate the IP address and port contained. */ - if (nat->nat_inip.s_addr != rr->rr_v3.xu_ip) + if (nat->nat_odstaddr != rr->rr_v3.xu_ip) return(-1); /* Create NAT & state table entries. */ - if (ippr_rpcb_getnat(fin, nat, rx->rx_proto, + if (ipf_p_rpcb_getnat(fin, nat, rx->rx_proto, (u_int)rr->rr_v3.xu_port) != 0) return(-1); break; @@ -980,9 +983,9 @@ ippr_rpcb_decoderep(fin, nat, rs, rm, rxp) for(;;) { re = &rl->rl_entries[rl->rl_cnt]; - if (ippr_rpcb_getuaddr(rm, &re->re_maddr, &p) != 0) + if (ipf_p_rpcb_getuaddr(rm, &re->re_maddr, &p) != 0) return(-1); - if (ippr_rpcb_getproto(rm, &re->re_netid, &p) != 0) + if (ipf_p_rpcb_getproto(rm, &re->re_netid, &p) != 0) return(-1); /* re_semantics & re_pfamily length */ if (!RPCB_BUF_GEQ(rm, p, 12)) @@ -992,7 +995,7 @@ ippr_rpcb_decoderep(fin, nat, rs, rm, rxp) if ((xdr != 4) || strncmp((char *)p, "inet", 4)) return(-1); p++; - if (ippr_rpcb_getproto(rm, &re->re_proto, &p) != 0) + if (ipf_p_rpcb_getproto(rm, &re->re_proto, &p) != 0) return(-1); if (!RPCB_BUF_GEQ(rm, p, 4)) return(-1); @@ -1011,7 +1014,7 @@ ippr_rpcb_decoderep(fin, nat, rs, rm, rxp) for(rl->rl_cnt = 0; rl->rl_cnt < cnt; rl->rl_cnt++) { re = &rl->rl_entries[rl->rl_cnt]; - rv = ippr_rpcb_getnat(fin, nat, + rv = ipf_p_rpcb_getnat(fin, nat, re->re_proto.xp_proto, (u_int)re->re_maddr.xu_port); if (rv != 0) @@ -1027,14 +1030,14 @@ ippr_rpcb_decoderep(fin, nat, rs, rm, rxp) } /* -------------------------------------------------------------------- */ -/* Function: ippr_rpcb_lookup */ +/* Function: ipf_p_rpcb_lookup */ /* Returns: rpcb_xact_t * - NULL == no matching record, */ /* else pointer to relevant entry */ /* Parameters: rs(I) - pointer to RPCB session */ /* xid(I) - XID to look for */ /* -------------------------------------------------------------------- */ static rpcb_xact_t * -ippr_rpcb_lookup(rs, xid) +ipf_p_rpcb_lookup(rs, xid) rpcb_session_t *rs; u_32_t xid; { @@ -1051,7 +1054,7 @@ ippr_rpcb_lookup(rs, xid) } /* -------------------------------------------------------------------- */ -/* Function: ippr_rpcb_deref */ +/* Function: ipf_p_rpcb_deref */ /* Returns: (void) */ /* Parameters: rs(I) - pointer to RPCB session */ /* rx(I) - pointer to RPC transaction struct to remove */ @@ -1062,7 +1065,7 @@ ippr_rpcb_lookup(rs, xid) /* Free the RPCB transaction record rx from the chain of entries. */ /* -------------------------------------------------------------------- */ static void -ippr_rpcb_deref(rs, rx) +ipf_p_rpcb_deref(rs, rx) rpcb_session_t *rs; rpcb_xact_t *rx; { @@ -1085,7 +1088,7 @@ ippr_rpcb_deref(rs, rx) } /* -------------------------------------------------------------------- */ -/* Function: ippr_rpcb_getproto */ +/* Function: ipf_p_rpcb_getproto */ /* Returns: int - -1 == illegal protocol/netid, */ /* 0 == legal protocol/netid */ /* Parameters: rm(I) - pointer to RPC message structure */ @@ -1095,7 +1098,7 @@ ippr_rpcb_deref(rs, rx) /* Decode netid/proto stored at p and record its numeric value. */ /* -------------------------------------------------------------------- */ static int -ippr_rpcb_getproto(rm, xp, p) +ipf_p_rpcb_getproto(rm, xp, p) rpc_msg_t *rm; xdr_proto_t *xp; u_32_t **p; @@ -1122,7 +1125,7 @@ ippr_rpcb_getproto(rm, xp, p) else { return(-1); } - + /* Advance past the string. */ (*p)++; @@ -1130,7 +1133,7 @@ ippr_rpcb_getproto(rm, xp, p) } /* -------------------------------------------------------------------- */ -/* Function: ippr_rpcb_getnat */ +/* Function: ipf_p_rpcb_getnat */ /* Returns: int -- -1 == failed to create table entries, */ /* 0 == success */ /* Parameters: fin(I) - pointer to packet information */ @@ -1142,12 +1145,13 @@ ippr_rpcb_getproto(rm, xp, p) /* attempt between RPC client and server. */ /* -------------------------------------------------------------------- */ static int -ippr_rpcb_getnat(fin, nat, proto, port) +ipf_p_rpcb_getnat(fin, nat, proto, port) fr_info_t *fin; nat_t *nat; u_int proto; u_int port; { + ipf_main_softc_t *softc = fin->fin_main_soft; ipnat_t *ipn, ipnat; tcphdr_t tcp; ipstate_t *is; @@ -1159,15 +1163,13 @@ ippr_rpcb_getnat(fin, nat, proto, port) /* Generate dummy fr_info */ bcopy((char *)fin, (char *)&fi, sizeof(fi)); - fi.fin_state = NULL; - fi.fin_nat = NULL; fi.fin_out = 0; - fi.fin_src = fin->fin_dst; - fi.fin_dst = nat->nat_outip; fi.fin_p = proto; fi.fin_sport = 0; fi.fin_dport = port & 0xffff; fi.fin_flx |= FI_IGNORE; + fi.fin_saddr = nat->nat_osrcaddr; + fi.fin_daddr = nat->nat_odstaddr; bzero((char *)&tcp, sizeof(tcp)); tcp.th_dport = htons(port); @@ -1195,18 +1197,18 @@ ippr_rpcb_getnat(fin, nat, proto, port) * If successful, fr_stlookup returns with ipf_state locked. We have * no use for this lock, so simply unlock it if necessary. */ - is = fr_stlookup(&fi, &tcp, NULL); + is = ipf_state_lookup(&fi, &tcp, NULL); if (is != NULL) { - RWLOCK_EXIT(&ipf_state); + RWLOCK_EXIT(&softc->ipf_state); } - RWLOCK_EXIT(&ipf_nat); + RWLOCK_EXIT(&softc->ipf_nat); - WRITE_ENTER(&ipf_nat); - natl = nat_inlookup(&fi, nflags, proto, fi.fin_src, fi.fin_dst); + WRITE_ENTER(&softc->ipf_nat); + natl = ipf_nat_inlookup(&fi, nflags, proto, fi.fin_src, fi.fin_dst); if ((natl != NULL) && (is != NULL)) { - MUTEX_DOWNGRADE(&ipf_nat); + MUTEX_DOWNGRADE(&softc->ipf_nat); return(0); } @@ -1220,6 +1222,10 @@ ippr_rpcb_getnat(fin, nat, proto, port) nflags &= ~NAT_SEARCH; if (natl == NULL) { +#ifdef USE_MUTEXES + ipf_nat_softc_t *softn = softc->ipf_nat_soft; +#endif + /* XXX Since we're just copying the original ipn contents * back, would we be better off just sending a pointer to * the 'temp' copy off to nat_new instead? @@ -1228,46 +1234,51 @@ ippr_rpcb_getnat(fin, nat, proto, port) bcopy((char *)ipn, (char *)&ipnat, sizeof(ipnat)); ipn->in_flags = nflags & IPN_TCPUDP; ipn->in_apr = NULL; - ipn->in_p = proto; - ipn->in_pmin = htons(fi.fin_dport); - ipn->in_pmax = htons(fi.fin_dport); - ipn->in_pnext = htons(fi.fin_dport); + ipn->in_pr[0] = proto; + ipn->in_pr[1] = proto; + ipn->in_dpmin = fi.fin_dport; + ipn->in_dpmax = fi.fin_dport; + ipn->in_dpnext = fi.fin_dport; ipn->in_space = 1; ipn->in_ippip = 1; if (ipn->in_flags & IPN_FILTER) { ipn->in_scmp = 0; ipn->in_dcmp = 0; } - *ipn->in_plabel = '\0'; + ipn->in_plabel = -1; /* Create NAT entry. return NULL if this fails. */ - natl = nat_new(&fi, ipn, NULL, nflags|SI_CLONE|NAT_SLAVE, + MUTEX_ENTER(&softn->ipf_nat_new); + natl = ipf_nat_add(&fi, ipn, NULL, nflags|SI_CLONE|NAT_SLAVE, NAT_INBOUND); + MUTEX_EXIT(&softn->ipf_nat_new); bcopy((char *)&ipnat, (char *)ipn, sizeof(ipnat)); if (natl == NULL) { - MUTEX_DOWNGRADE(&ipf_nat); + MUTEX_DOWNGRADE(&softc->ipf_nat); return(-1); } + natl->nat_ptr = ipn; + fi.fin_saddr = natl->nat_nsrcaddr; + fi.fin_daddr = natl->nat_ndstaddr; ipn->in_use++; - (void) nat_proto(&fi, natl, nflags); - nat_update(&fi, natl, natl->nat_ptr); + (void) ipf_nat_proto(&fi, natl, nflags); + MUTEX_ENTER(&natl->nat_lock); + ipf_nat_update(&fi, natl); + MUTEX_EXIT(&natl->nat_lock); } - MUTEX_DOWNGRADE(&ipf_nat); + MUTEX_DOWNGRADE(&softc->ipf_nat); if (is == NULL) { /* Create state entry. Return NULL if this fails. */ - fi.fin_dst = nat->nat_inip; - fi.fin_nat = (void *)natl; fi.fin_flx |= FI_NATED; fi.fin_flx &= ~FI_STATE; nflags &= NAT_TCPUDP; nflags |= SI_W_SPORT|SI_CLONE; - is = fr_addstate(&fi, NULL, nflags); - if (is == NULL) { + if (ipf_state_add(softc, &fi, NULL, nflags) != 0) { /* * XXX nat_delete is private to ip_nat.c. Should * check w/ Darren about this one. @@ -1276,15 +1287,13 @@ ippr_rpcb_getnat(fin, nat, proto, port) */ return(-1); } - if (fi.fin_state != NULL) - fr_statederef((ipstate_t **)&fi.fin_state); } return(0); } /* -------------------------------------------------------------------- */ -/* Function: ippr_rpcb_modv3 */ +/* Function: ipf_p_rpcb_modv3 */ /* Returns: int -- change in packet length */ /* Parameters: fin(I) - pointer to packet information */ /* nat(I) - pointer to NAT session */ @@ -1296,7 +1305,7 @@ ippr_rpcb_getnat(fin, nat, proto, port) /* lengths as necessary. */ /* -------------------------------------------------------------------- */ static int -ippr_rpcb_modv3(fin, nat, rm, m, off) +ipf_p_rpcb_modv3(fin, nat, rm, m, off) fr_info_t *fin; nat_t *nat; rpc_msg_t *rm; @@ -1310,7 +1319,7 @@ ippr_rpcb_modv3(fin, nat, rm, m, off) int diff; rr = &rm->rm_resp; - i = (char *)&nat->nat_outip.s_addr; + i = (char *)&nat->nat_ndstaddr; p = (char *)&rr->rr_v3.xu_port; /* Form new string. */ @@ -1336,7 +1345,7 @@ ippr_rpcb_modv3(fin, nat, rm, m, off) /* Write new string. */ COPYBACK(m, off, xlen, uaddr); - + /* Determine difference in data lengths. */ diff = xlen - XDRALIGN(B(rr->rr_v3.xu_xslen)); @@ -1345,13 +1354,13 @@ ippr_rpcb_modv3(fin, nat, rm, m, off) * adjustments. */ if (diff != 0) - ippr_rpcb_fixlen(fin, diff); + ipf_p_rpcb_fixlen(fin, diff); return(diff); } /* -------------------------------------------------------------------- */ -/* Function: ippr_rpcb_modv4 */ +/* Function: ipf_p_rpcb_modv4 */ /* Returns: int -- change in packet length */ /* Parameters: fin(I) - pointer to packet information */ /* nat(I) - pointer to NAT session */ @@ -1362,7 +1371,7 @@ ippr_rpcb_modv3(fin, nat, rm, m, off) /* Write new rpcb_entry list, adjusting lengths as necessary. */ /* -------------------------------------------------------------------- */ static int -ippr_rpcb_modv4(fin, nat, rm, m, off) +ipf_p_rpcb_modv4(fin, nat, rm, m, off) fr_info_t *fin; nat_t *nat; rpc_msg_t *rm; @@ -1381,7 +1390,7 @@ ippr_rpcb_modv4(fin, nat, rm, m, off) rr = &rm->rm_resp; rl = &rr->rr_v4; - i = (char *)&nat->nat_outip.s_addr; + i = (char *)&nat->nat_ndstaddr; /* Determine mbuf offset to write to. */ re = &rl->rl_entries[0]; @@ -1432,14 +1441,14 @@ ippr_rpcb_modv4(fin, nat, rm, m, off) * adjustments. */ if (diff != 0) - ippr_rpcb_fixlen(fin, diff); + ipf_p_rpcb_fixlen(fin, diff); return(diff); } /* -------------------------------------------------------------------- */ -/* Function: ippr_rpcb_fixlen */ +/* Function: ipf_p_rpcb_fixlen */ /* Returns: (void) */ /* Parameters: fin(I) - pointer to packet information */ /* len(I) - change in packet length */ @@ -1448,7 +1457,7 @@ ippr_rpcb_modv4(fin, nat, rm, m, off) /* header fields. */ /* -------------------------------------------------------------------- */ static void -ippr_rpcb_fixlen(fin, len) +ipf_p_rpcb_fixlen(fin, len) fr_info_t *fin; int len; { @@ -1456,9 +1465,9 @@ ippr_rpcb_fixlen(fin, len) udp = fin->fin_dp; udp->uh_ulen = htons(ntohs(udp->uh_ulen) + len); - fin->fin_ip->ip_len += len; - fin->fin_dlen += len; fin->fin_plen += len; + fin->fin_ip->ip_len = htons(fin->fin_plen); + fin->fin_dlen += len; } #undef B diff --git a/sys/contrib/ipfilter/netinet/ip_rules.c b/sys/contrib/ipfilter/netinet/ip_rules.c index f080ec5b832..434b9de937b 100644 --- a/sys/contrib/ipfilter/netinet/ip_rules.c +++ b/sys/contrib/ipfilter/netinet/ip_rules.c @@ -1,18 +1,29 @@ /* $FreeBSD$ */ /* -* Copyright (C) 1993-2000 by Darren Reed. +* Copyright (C) 2012 by Darren Reed. * * Redistribution and use in source and binary forms are permitted * provided that this notice is preserved and due credit is given * to the original author and the contributors. */ +#include #include #include #include -#if !defined(__FreeBSD__) && !defined(__OpenBSD__) && !defined(__sgi) -# include +#if defined(__FreeBSD_version) && (__FreeBSD_version >= 40000) +# if defined(_KERNEL) +# include +# else +# include +# endif +#endif +#if defined(__NetBSD_Version__) && (__NetBSD_Version__ >= 399000000) +#else +# if !defined(__FreeBSD__) && !defined(__OpenBSD__) && !defined(__sgi) +# include +# endif #endif #include #include @@ -40,18 +51,32 @@ #ifdef IPFILTER_COMPILED +extern ipf_main_softc_t ipfmain; + + static u_long in_rule__0[] = { -0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xffffffff, 0, 0, 0, 0, 0, 0, 0x1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x80000000, 0x8002, 0, 0, 0, 0xffff, 0, 0, 0x4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +0, 0, 0, 0, 0, 0, 0, 0x8070d88, 0, 0, 0, 0xffffffff, 0, 0, 0, 0, 0, 0, 0, 0xffffffff, 0x1b0, 0x1, 0, 0, 0, 0x2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x40000000, 0x8002, 0, 0, 0, 0xffff, 0, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xffffffff, 0, 0, 0, 0, 0, 0, 0, 0, 0xffffffff, 0, 0, 0, 0, 0, 0, 0, 0, 0xffffffff, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xffffffff, 0, 0, 0, 0 }; static u_long out_rule__0[] = { -0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xffffffff, 0, 0, 0, 0, 0, 0, 0x1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x80000000, 0x4002, 0, 0, 0, 0xffff, 0, 0, 0x4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +0, 0, 0, 0, 0, 0, 0, 0x8070d88, 0, 0, 0, 0xffffffff, 0, 0, 0, 0, 0, 0, 0, 0xffffffff, 0x1b0, 0x1, 0, 0, 0, 0x3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x40000000, 0x4002, 0, 0, 0, 0xffff, 0, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xffffffff, 0, 0, 0, 0, 0, 0, 0, 0, 0xffffffff, 0, 0, 0, 0, 0, 0, 0, 0, 0xffffffff, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xffffffff, 0, 0, 0, 0 }; frentry_t *ipf_rules_in_[1] = { (frentry_t *)&in_rule__0 }; +/* XXX This file (ip_rules.c) is not part of the ipfilter tarball, it is + XXX generated by the ipfilter build process. Unfortunately the build + XXX process did not generate the following lines so they are added + XXX by hand here. This is a bit of a hack but it works for now. Future + XXX imports/merges of ipfilter may generate this so the following will + XXX need to be removed following some future merge. + XXX */ +frentry_t *ipf_rules_out_[1] = { + (frentry_t *)&out_rule__0 +}; + frentry_t *ipfrule_match_in_(fin, passp) fr_info_t *fin; u_32_t *passp; @@ -62,10 +87,6 @@ u_32_t *passp; return fr; } -frentry_t *ipf_rules_out_[1] = { - (frentry_t *)&out_rule__0 -}; - frentry_t *ipfrule_match_out_(fin, passp) fr_info_t *fin; u_32_t *passp; @@ -87,9 +108,14 @@ int ipfrule_add_out_() fp = ipf_rules_out_[i]; fp->fr_next = NULL; for (j = i + 1; j < max; j++) - if (strncmp(fp->fr_group, + if (strncmp(fp->fr_names + fp->fr_group, + ipf_rules_out_[j]->fr_names + ipf_rules_out_[j]->fr_group, FR_GROUPLEN) == 0) { + if (ipf_rules_out_[j] != NULL) + ipf_rules_out_[j]->fr_pnext = + &fp->fr_next; + fp->fr_pnext = &ipf_rules_out_[j]; fp->fr_next = ipf_rules_out_[j]; break; } @@ -97,13 +123,14 @@ int ipfrule_add_out_() fp = &ipfrule_out_; bzero((char *)fp, sizeof(*fp)); - fp->fr_type = FR_T_CALLFUNC|FR_T_BUILTIN; + fp->fr_type = FR_T_CALLFUNC_BUILTIN; fp->fr_flags = FR_OUTQUE|FR_NOMATCH; fp->fr_data = (void *)ipf_rules_out_[0]; fp->fr_dsize = sizeof(ipf_rules_out_[0]); - fp->fr_v = 4; + fp->fr_family = AF_INET; fp->fr_func = (ipfunc_t)ipfrule_match_out_; - err = frrequest(IPL_LOGIPF, SIOCADDFR, (caddr_t)fp, fr_active, 0); + err = frrequest(&ipfmain, IPL_LOGIPF, SIOCADDFR, (caddr_t)fp, + ipfmain.ipf_active, 0); return err; } @@ -129,8 +156,9 @@ int ipfrule_remove_out_() } } if (err == 0) - err = frrequest(IPL_LOGIPF, SIOCDELFR, - (caddr_t)&ipfrule_out_, fr_active, 0); + err = frrequest(&ipfmain, IPL_LOGIPF, SIOCDELFR, + (caddr_t)&ipfrule_out_, + ipfmain.ipf_active, 0); if (err) return err; @@ -149,9 +177,14 @@ int ipfrule_add_in_() fp = ipf_rules_in_[i]; fp->fr_next = NULL; for (j = i + 1; j < max; j++) - if (strncmp(fp->fr_group, + if (strncmp(fp->fr_names + fp->fr_group, + ipf_rules_in_[j]->fr_names + ipf_rules_in_[j]->fr_group, FR_GROUPLEN) == 0) { + if (ipf_rules_in_[j] != NULL) + ipf_rules_in_[j]->fr_pnext = + &fp->fr_next; + fp->fr_pnext = &ipf_rules_in_[j]; fp->fr_next = ipf_rules_in_[j]; break; } @@ -159,13 +192,14 @@ int ipfrule_add_in_() fp = &ipfrule_in_; bzero((char *)fp, sizeof(*fp)); - fp->fr_type = FR_T_CALLFUNC|FR_T_BUILTIN; + fp->fr_type = FR_T_CALLFUNC_BUILTIN; fp->fr_flags = FR_INQUE|FR_NOMATCH; fp->fr_data = (void *)ipf_rules_in_[0]; fp->fr_dsize = sizeof(ipf_rules_in_[0]); - fp->fr_v = 4; + fp->fr_family = AF_INET; fp->fr_func = (ipfunc_t)ipfrule_match_in_; - err = frrequest(IPL_LOGIPF, SIOCADDFR, (caddr_t)fp, fr_active, 0); + err = frrequest(&ipfmain, IPL_LOGIPF, SIOCADDFR, (caddr_t)fp, + ipfmain.ipf_active, 0); return err; } @@ -191,8 +225,9 @@ int ipfrule_remove_in_() } } if (err == 0) - err = frrequest(IPL_LOGIPF, SIOCDELFR, - (caddr_t)&ipfrule_in_, fr_active, 0); + err = frrequest(&ipfmain, IPL_LOGIPF, SIOCDELFR, + (caddr_t)&ipfrule_in_, + ipfmain.ipf_active, 0); if (err) return err; diff --git a/sys/contrib/ipfilter/netinet/ip_scan.c b/sys/contrib/ipfilter/netinet/ip_scan.c index 54acb2aa429..5b7c77e4b10 100644 --- a/sys/contrib/ipfilter/netinet/ip_scan.c +++ b/sys/contrib/ipfilter/netinet/ip_scan.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 1995-2001 by Darren Reed. + * Copyright (C) 2012 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. */ @@ -58,17 +58,17 @@ struct file; #if !defined(lint) static const char sccsid[] = "@(#)ip_state.c 1.8 6/5/96 (C) 1993-2000 Darren Reed"; -static const char rcsid[] = "@(#)$Id: ip_scan.c,v 2.40.2.10 2007/06/02 21:22:28 darrenr Exp $"; +static const char rcsid[] = "@(#)$Id$"; #endif #ifdef IPFILTER_SCAN /* endif at bottom of file */ -ipscan_t *ipsc_list = NULL, - *ipsc_tail = NULL; -ipscanstat_t ipsc_stat; +ipscan_t *ipf_scan_list = NULL, + *ipf_scan_tail = NULL; +ipscanstat_t ipf_scan_stat; # ifdef USE_MUTEXES -ipfrwlock_t ipsc_rwlock; +ipfrwlock_t ipf_scan_rwlock; # endif # ifndef isalpha @@ -77,42 +77,47 @@ ipfrwlock_t ipsc_rwlock; # endif -int ipsc_add __P((caddr_t)); -int ipsc_delete __P((caddr_t)); -struct ipscan *ipsc_lookup __P((char *)); -int ipsc_matchstr __P((sinfo_t *, char *, int)); -int ipsc_matchisc __P((ipscan_t *, ipstate_t *, int, int, int *)); -int ipsc_match __P((ipstate_t *)); +int ipf_scan_add __P((caddr_t)); +int ipf_scan_remove __P((caddr_t)); +struct ipscan *ipf_scan_lookup __P((char *)); +int ipf_scan_matchstr __P((sinfo_t *, char *, int)); +int ipf_scan_matchisc __P((ipscan_t *, ipstate_t *, int, int, int *)); +int ipf_scan_match __P((ipstate_t *)); -static int ipsc_inited = 0; +static int ipf_scan_inited = 0; -int ipsc_init() +int +ipf_scan_init() { - RWLOCK_INIT(&ipsc_rwlock, "ip scan rwlock"); - ipsc_inited = 1; + RWLOCK_INIT(&ipf_scan_rwlock, "ip scan rwlock"); + ipf_scan_inited = 1; return 0; } -void fr_scanunload() +void +ipf_scan_unload(ipf_main_softc_t *arg) { - if (ipsc_inited == 1) { - RW_DESTROY(&ipsc_rwlock); - ipsc_inited = 0; + if (ipf_scan_inited == 1) { + RW_DESTROY(&ipf_scan_rwlock); + ipf_scan_inited = 0; } } -int ipsc_add(data) -caddr_t data; +int +ipf_scan_add(data) + caddr_t data; { ipscan_t *i, *isc; int err; KMALLOC(isc, ipscan_t *); - if (!isc) + if (!isc) { + ipf_interror = 90001; return ENOMEM; + } err = copyinptr(data, isc, sizeof(*isc)); if (err) { @@ -120,23 +125,24 @@ caddr_t data; return err; } - WRITE_ENTER(&ipsc_rwlock); + WRITE_ENTER(&ipf_scan_rwlock); - i = ipsc_lookup(isc->ipsc_tag); - if (i) { - RWLOCK_EXIT(&ipsc_rwlock); + i = ipf_scan_lookup(isc->ipsc_tag); + if (i != NULL) { + RWLOCK_EXIT(&ipf_scan_rwlock); KFREE(isc); + ipf_interror = 90002; return EEXIST; } - if (ipsc_tail) { - ipsc_tail->ipsc_next = isc; - isc->ipsc_pnext = &ipsc_tail->ipsc_next; - ipsc_tail = isc; + if (ipf_scan_tail) { + ipf_scan_tail->ipsc_next = isc; + isc->ipsc_pnext = &ipf_scan_tail->ipsc_next; + ipf_scan_tail = isc; } else { - ipsc_list = isc; - ipsc_tail = isc; - isc->ipsc_pnext = &ipsc_list; + ipf_scan_list = isc; + ipf_scan_tail = isc; + isc->ipsc_pnext = &ipf_scan_list; } isc->ipsc_next = NULL; @@ -145,14 +151,15 @@ caddr_t data; isc->ipsc_sref = 0; isc->ipsc_active = 0; - ipsc_stat.iscs_entries++; - RWLOCK_EXIT(&ipsc_rwlock); + ipf_scan_stat.iscs_entries++; + RWLOCK_EXIT(&ipf_scan_rwlock); return 0; } -int ipsc_delete(data) -caddr_t data; +int +ipf_scan_remove(data) + caddr_t data; { ipscan_t isc, *i; int err; @@ -161,14 +168,15 @@ caddr_t data; if (err) return err; - WRITE_ENTER(&ipsc_rwlock); + WRITE_ENTER(&ipf_scan_rwlock); - i = ipsc_lookup(isc.ipsc_tag); + i = ipf_scan_lookup(isc.ipsc_tag); if (i == NULL) err = ENOENT; else { if (i->ipsc_fref) { - RWLOCK_EXIT(&ipsc_rwlock); + RWLOCK_EXIT(&ipf_scan_rwlock); + ipf_interror = 90003; return EBUSY; } @@ -176,61 +184,66 @@ caddr_t data; if (i->ipsc_next) i->ipsc_next->ipsc_pnext = i->ipsc_pnext; else { - if (i->ipsc_pnext == &ipsc_list) - ipsc_tail = NULL; + if (i->ipsc_pnext == &ipf_scan_list) + ipf_scan_tail = NULL; else - ipsc_tail = *(*i->ipsc_pnext)->ipsc_pnext; + ipf_scan_tail = *(*i->ipsc_pnext)->ipsc_pnext; } - ipsc_stat.iscs_entries--; + ipf_scan_stat.iscs_entries--; KFREE(i); } - RWLOCK_EXIT(&ipsc_rwlock); + RWLOCK_EXIT(&ipf_scan_rwlock); return err; } -struct ipscan *ipsc_lookup(tag) -char *tag; +struct ipscan * +ipf_scan_lookup(tag) + char *tag; { ipscan_t *i; - for (i = ipsc_list; i; i = i->ipsc_next) + for (i = ipf_scan_list; i; i = i->ipsc_next) if (!strcmp(i->ipsc_tag, tag)) return i; return NULL; } -int ipsc_attachfr(fr) -struct frentry *fr; +int +ipf_scan_attachfr(fr) + struct frentry *fr; { ipscan_t *i; - if (fr->fr_isctag[0]) { - READ_ENTER(&ipsc_rwlock); - i = ipsc_lookup(fr->fr_isctag); + if (fr->fr_isctag != -1) { + READ_ENTER(&ipf_scan_rwlock); + i = ipf_scan_lookup(fr->fr_isctag + fr->fr_names); if (i != NULL) { ATOMIC_INC32(i->ipsc_fref); } - RWLOCK_EXIT(&ipsc_rwlock); - if (i == NULL) + RWLOCK_EXIT(&ipf_scan_rwlock); + if (i == NULL) { + ipf_interror = 90004; return ENOENT; + } fr->fr_isc = i; } return 0; } -int ipsc_attachis(is) -struct ipstate *is; +int +ipf_scan_attachis(is) + struct ipstate *is; { frentry_t *fr; ipscan_t *i; - READ_ENTER(&ipsc_rwlock); + READ_ENTER(&ipf_scan_rwlock); fr = is->is_rule; - if (fr) { + if (fr != NULL) { i = fr->fr_isc; if ((i != NULL) && (i != (ipscan_t *)-1)) { is->is_isc = i; @@ -245,13 +258,14 @@ struct ipstate *is; is->is_flags |= IS_SC_MATCHS; } } - RWLOCK_EXIT(&ipsc_rwlock); + RWLOCK_EXIT(&ipf_scan_rwlock); return 0; } -int ipsc_detachfr(fr) -struct frentry *fr; +int +ipf_scan_detachfr(fr) + struct frentry *fr; { ipscan_t *i; @@ -263,18 +277,19 @@ struct frentry *fr; } -int ipsc_detachis(is) -struct ipstate *is; +int +ipf_scan_detachis(is) + struct ipstate *is; { ipscan_t *i; - READ_ENTER(&ipsc_rwlock); + READ_ENTER(&ipf_scan_rwlock); if ((i = is->is_isc) && (i != (ipscan_t *)-1)) { ATOMIC_DEC32(i->ipsc_sref); is->is_isc = NULL; is->is_flags &= ~(IS_SC_CLIENT|IS_SC_SERVER); } - RWLOCK_EXIT(&ipsc_rwlock); + RWLOCK_EXIT(&ipf_scan_rwlock); return 0; } @@ -282,10 +297,11 @@ struct ipstate *is; /* * 'string' compare for scanning */ -int ipsc_matchstr(sp, str, n) -sinfo_t *sp; -char *str; -int n; +int +ipf_scan_matchstr(sp, str, n) + sinfo_t *sp; + char *str; + int n; { char *s, *t, *up; int i = n; @@ -316,10 +332,11 @@ int n; * Returns 3 if both server and client match, 2 if just server, * 1 if just client */ -int ipsc_matchisc(isc, is, cl, sl, maxm) -ipscan_t *isc; -ipstate_t *is; -int cl, sl, maxm[2]; +int +ipf_scan_matchisc(isc, is, cl, sl, maxm) + ipscan_t *isc; + ipstate_t *is; + int cl, sl, maxm[2]; { int i, j, k, n, ret = 0, flags; @@ -348,7 +365,8 @@ int cl, sl, maxm[2]; i = 0; n = MIN(cl, isc->ipsc_clen); if ((n > 0) && (!maxm || (n >= maxm[1]))) { - if (!ipsc_matchstr(&isc->ipsc_cl, is->is_sbuf[0], n)) { + if (!ipf_scan_matchstr(&isc->ipsc_cl, + is->is_sbuf[0], n)) { i++; ret |= 1; if (n > j) @@ -364,7 +382,8 @@ int cl, sl, maxm[2]; i = 0; n = MIN(cl, isc->ipsc_slen); if ((n > 0) && (!maxm || (n >= maxm[1]))) { - if (!ipsc_matchstr(&isc->ipsc_sl, is->is_sbuf[1], n)) { + if (!ipf_scan_matchstr(&isc->ipsc_sl, + is->is_sbuf[1], n)) { i++; ret |= 2; if (n > k) @@ -381,8 +400,9 @@ int cl, sl, maxm[2]; } -int ipsc_match(is) -ipstate_t *is; +int +ipf_scan_match(is) + ipstate_t *is; { int i, j, k, n, cl, sl, maxm[2]; ipscan_t *isc, *lm; @@ -399,7 +419,7 @@ ipstate_t *is; /* * Known object to scan for. */ - i = ipsc_matchisc(isc, is, cl, sl, NULL); + i = ipf_scan_matchisc(isc, is, cl, sl, NULL); if (i & 1) { is->is_flags |= IS_SC_MATCHC; is->is_flags &= ~IS_SC_CLIENT; @@ -415,8 +435,8 @@ ipstate_t *is; lm = NULL; maxm[0] = 0; maxm[1] = 0; - for (k = 0, isc = ipsc_list; isc; isc = isc->ipsc_next) { - i = ipsc_matchisc(isc, is, cl, sl, maxm); + for (k = 0, isc = ipf_scan_list; isc; isc = isc->ipsc_next) { + i = ipf_scan_matchisc(isc, is, cl, sl, maxm); if (i) { /* * We only want to remember the best match @@ -475,7 +495,7 @@ ipstate_t *is; j = ISC_A_NONE; if ((is->is_flags & IS_SC_MATCHALL) == IS_SC_MATCHALL) { j = isc->ipsc_action; - ipsc_stat.iscs_acted++; + ipf_scan_stat.iscs_acted++; } else if ((is->is_isc != NULL) && ((is->is_flags & IS_SC_MATCHALL) != IS_SC_MATCHALL) && !(is->is_flags & (IS_SC_CLIENT|IS_SC_SERVER))) { @@ -483,7 +503,7 @@ ipstate_t *is; * Matching failed... */ j = isc->ipsc_else; - ipsc_stat.iscs_else++; + ipf_scan_stat.iscs_else++; } switch (j) @@ -508,9 +528,10 @@ ipstate_t *is; /* * check if a packet matches what we're scanning for */ -int ipsc_packet(fin, is) -fr_info_t *fin; -ipstate_t *is; +int +ipf_scan_packet(fin, is) + fr_info_t *fin; + ipstate_t *is; { int i, j, rv, dlen, off, thoff; u_32_t seq, s0; @@ -552,7 +573,7 @@ ipstate_t *is; if (j == 0) return 1; - (void) ipsc_match(is); + (void) ipf_scan_match(is); #if 0 /* * There is the potential here for plain text passwords to get @@ -567,11 +588,12 @@ ipstate_t *is; } -int fr_scan_ioctl(data, cmd, mode, uid, ctx) -caddr_t data; -ioctlcmd_t cmd; -int mode, uid; -void *ctx; +int +ipf_scan_ioctl(data, cmd, mode, uid, ctx) + caddr_t data; + ioctlcmd_t cmd; + int mode, uid; + void *ctx; { ipscanstat_t ipscs; int err = 0; @@ -579,17 +601,19 @@ void *ctx; switch (cmd) { case SIOCADSCA : - err = ipsc_add(data); + err = ipf_scan_add(data); break; case SIOCRMSCA : - err = ipsc_delete(data); + err = ipf_scan_remove(data); break; case SIOCGSCST : - bcopy((char *)&ipsc_stat, (char *)&ipscs, sizeof(ipscs)); - ipscs.iscs_list = ipsc_list; + bcopy((char *)&ipf_scan_stat, (char *)&ipscs, sizeof(ipscs)); + ipscs.iscs_list = ipf_scan_list; err = BCOPYOUT(&ipscs, data, sizeof(ipscs)); - if (err != 0) + if (err != 0) { + ipf_interror = 90005; err = EFAULT; + } break; default : err = EINVAL; diff --git a/sys/contrib/ipfilter/netinet/ip_scan.h b/sys/contrib/ipfilter/netinet/ip_scan.h index 4772d28c012..99032095ba0 100644 --- a/sys/contrib/ipfilter/netinet/ip_scan.h +++ b/sys/contrib/ipfilter/netinet/ip_scan.h @@ -1,10 +1,10 @@ /* - * Copyright (C) 1993-2001 by Darren Reed. + * Copyright (C) 2012 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. * * @(#)ip_fil.h 1.35 6/5/96 - * $Id: ip_scan.h,v 2.9.2.2 2006/07/14 06:12:19 darrenr Exp $ + * $Id$ */ #ifndef __IP_SCAN_H__ @@ -94,13 +94,13 @@ typedef struct ipscanstat { } ipscanstat_t; -extern int fr_scan_ioctl __P((caddr_t, ioctlcmd_t, int, int, void *)); -extern int ipsc_init __P((void)); -extern int ipsc_attachis __P((struct ipstate *)); -extern int ipsc_attachfr __P((struct frentry *)); -extern int ipsc_detachis __P((struct ipstate *)); -extern int ipsc_detachfr __P((struct frentry *)); -extern int ipsc_packet __P((struct fr_info *, struct ipstate *)); -extern void fr_scanunload __P((void)); +extern int ipf_scan_ioctl __P((ipf_main_softc_t *, caddr_t, ioctlcmd_t, int, int, void *)); +extern int ipf_scan_init __P((void)); +extern int ipf_scan_attachis __P((struct ipstate *)); +extern int ipf_scan_attachfr __P((struct frentry *)); +extern int ipf_scan_detachis __P((struct ipstate *)); +extern int ipf_scan_detachfr __P((struct frentry *)); +extern int ipf_scan_packet __P((struct fr_info *, struct ipstate *)); +extern void ipf_scan_unload __P((ipf_main_softc_t *)); #endif /* __IP_SCAN_H__ */ diff --git a/sys/contrib/ipfilter/netinet/ip_state.c b/sys/contrib/ipfilter/netinet/ip_state.c index 6c8b158f0ba..9c6a244233d 100644 --- a/sys/contrib/ipfilter/netinet/ip_state.c +++ b/sys/contrib/ipfilter/netinet/ip_state.c @@ -1,9 +1,13 @@ /* $FreeBSD$ */ /* - * Copyright (C) 1995-2003 by Darren Reed. + * Copyright (C) 2012 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. + * + * Copyright 2008 Sun Microsystems. + * + * $Id$ */ #if defined(KERNEL) || defined(_KERNEL) # undef KERNEL @@ -15,14 +19,6 @@ #include #include #include -#if defined(__NetBSD__) && (NetBSD >= 199905) && !defined(IPFILTER_LKM) && \ - defined(_KERNEL) -# if (__NetBSD_Version__ < 399001400) -# include "opt_ipfilter_log.h" -# else -# include "opt_ipfilter.h" -# endif -#endif #if defined(_KERNEL) && defined(__FreeBSD_version) && \ (__FreeBSD_version >= 400000) && !defined(KLD_MODULE) #include "opt_inet6.h" @@ -41,9 +37,6 @@ struct file; #if defined(_KERNEL) && (__FreeBSD_version >= 220000) # include # include -# if (__FreeBSD_version >= 300000) && !defined(IPFILTER_LKM) -# include "opt_ipfilter.h" -# endif #else # include #endif @@ -72,36 +65,31 @@ struct file; #ifdef sun # include #endif -#include #include #include #include #include -#if !defined(linux) -# include -#endif #if !defined(__hpux) && !defined(linux) # include #endif #include #include +#if !defined(_KERNEL) +# include "ipf.h" +#endif #include "netinet/ip_compat.h" -#include #include "netinet/ip_fil.h" #include "netinet/ip_nat.h" #include "netinet/ip_frag.h" #include "netinet/ip_state.h" #include "netinet/ip_proxy.h" -#ifdef IPFILTER_SYNC +#include "netinet/ip_lookup.h" +#include "netinet/ip_dstlist.h" #include "netinet/ip_sync.h" -#endif -#ifdef IPFILTER_SCAN -#include "netinet/ip_scan.h" -#endif #ifdef USE_INET6 #include #endif -#if (__FreeBSD_version >= 300000) +#if FREEBSD_GE_REV(300000) # include # if defined(_KERNEL) && !defined(IPFILTER_LKM) # include @@ -113,342 +101,501 @@ struct file; #if !defined(lint) static const char sccsid[] = "@(#)ip_state.c 1.8 6/5/96 (C) 1993-2000 Darren Reed"; -static const char rcsid[] = "@(#)$Id: ip_state.c,v 2.186.2.80 2007/10/16 09:33:23 darrenr Exp $"; +static const char rcsid[] = "@(#)$Id$"; #endif -static ipstate_t **ips_table = NULL; -static u_long *ips_seed = NULL; -static int ips_num = 0; -static u_long ips_last_force_flush = 0; -ips_stat_t ips_stats; + +static ipftuneable_t ipf_state_tuneables[] = { + { { (void *)offsetof(ipf_state_softc_t, ipf_state_max) }, + "state_max", 1, 0x7fffffff, + stsizeof(ipf_state_softc_t, ipf_state_max), + 0, NULL, NULL }, + { { (void *)offsetof(ipf_state_softc_t, ipf_state_size) }, + "state_size", 1, 0x7fffffff, + stsizeof(ipf_state_softc_t, ipf_state_size), + 0, NULL, ipf_state_rehash }, + { { (void *)offsetof(ipf_state_softc_t, ipf_state_lock) }, + "state_lock", 0, 1, + stsizeof(ipf_state_softc_t, ipf_state_lock), + IPFT_RDONLY, NULL, NULL }, + { { (void *)offsetof(ipf_state_softc_t, ipf_state_maxbucket) }, + "state_maxbucket", 1, 0x7fffffff, + stsizeof(ipf_state_softc_t, ipf_state_maxbucket), + 0, NULL, NULL }, + { { (void *)offsetof(ipf_state_softc_t, ipf_state_logging) }, + "state_logging",0, 1, + stsizeof(ipf_state_softc_t, ipf_state_logging), + 0, NULL, NULL }, + { { (void *)offsetof(ipf_state_softc_t, ipf_state_wm_high) }, + "state_wm_high",2, 100, + stsizeof(ipf_state_softc_t, ipf_state_wm_high), + 0, NULL, NULL }, + { { (void *)offsetof(ipf_state_softc_t, ipf_state_wm_low) }, + "state_wm_low", 1, 99, + stsizeof(ipf_state_softc_t, ipf_state_wm_low), + 0, NULL, NULL }, + { { (void *)offsetof(ipf_state_softc_t, ipf_state_wm_freq) }, + "state_wm_freq",2, 999999, + stsizeof(ipf_state_softc_t, ipf_state_wm_freq), + 0, NULL, NULL }, + { { NULL }, + NULL, 0, 0, + 0, + 0, NULL, NULL } +}; + +#define SINCL(x) ATOMIC_INCL(softs->x) +#define SBUMP(x) (softs->x)++ +#define SBUMPD(x, y) do { (softs->x.y)++; DT(y); } while (0) +#define SBUMPDX(x, y, z)do { (softs->x.y)++; DT(z); } while (0) #ifdef USE_INET6 -static ipstate_t *fr_checkicmp6matchingstate __P((fr_info_t *)); +static ipstate_t *ipf_checkicmp6matchingstate __P((fr_info_t *)); #endif -static ipstate_t *fr_matchsrcdst __P((fr_info_t *, ipstate_t *, i6addr_t *, +static int ipf_allowstateicmp __P((fr_info_t *, ipstate_t *, i6addr_t *)); +static ipstate_t *ipf_matchsrcdst __P((fr_info_t *, ipstate_t *, i6addr_t *, i6addr_t *, tcphdr_t *, u_32_t)); -static ipstate_t *fr_checkicmpmatchingstate __P((fr_info_t *)); -static int fr_state_flush __P((int, int)); -static int fr_state_flush_entry __P((void *)); -static ips_stat_t *fr_statetstats __P((void)); -static int fr_delstate __P((ipstate_t *, int)); -static int fr_state_remove __P((caddr_t)); -static void fr_ipsmove __P((ipstate_t *, u_int)); -static int fr_tcpstate __P((fr_info_t *, tcphdr_t *, ipstate_t *)); -static int fr_tcpoptions __P((fr_info_t *, tcphdr_t *, tcpdata_t *)); -static ipstate_t *fr_stclone __P((fr_info_t *, tcphdr_t *, ipstate_t *)); -static void fr_fixinisn __P((fr_info_t *, ipstate_t *)); -static void fr_fixoutisn __P((fr_info_t *, ipstate_t *)); -static void fr_checknewisn __P((fr_info_t *, ipstate_t *)); -static int fr_stateiter __P((ipftoken_t *, ipfgeniter_t *)); -static int fr_stgettable __P((char *)); +static ipstate_t *ipf_checkicmpmatchingstate __P((fr_info_t *)); +static int ipf_state_flush_entry __P((ipf_main_softc_t *, void *)); +static ips_stat_t *ipf_state_stats __P((ipf_main_softc_t *)); +static int ipf_state_del __P((ipf_main_softc_t *, ipstate_t *, int)); +static int ipf_state_remove __P((ipf_main_softc_t *, caddr_t)); +static int ipf_state_match __P((ipstate_t *is1, ipstate_t *is2)); +static int ipf_state_matchaddresses __P((ipstate_t *is1, ipstate_t *is2)); +static int ipf_state_matchipv4addrs __P((ipstate_t *is1, ipstate_t *is2)); +static int ipf_state_matchipv6addrs __P((ipstate_t *is1, ipstate_t *is2)); +static int ipf_state_matchisps __P((ipstate_t *is1, ipstate_t *is2)); +static int ipf_state_matchports __P((udpinfo_t *is1, udpinfo_t *is2)); +static int ipf_state_matcharray __P((ipstate_t *, int *, u_long)); +static void ipf_ipsmove __P((ipf_state_softc_t *, ipstate_t *, u_int)); +static int ipf_state_tcp __P((ipf_main_softc_t *, ipf_state_softc_t *, + fr_info_t *, tcphdr_t *, ipstate_t *)); +static int ipf_tcpoptions __P((ipf_state_softc_t *, fr_info_t *, + tcphdr_t *, tcpdata_t *)); +static ipstate_t *ipf_state_clone __P((fr_info_t *, tcphdr_t *, ipstate_t *)); +static void ipf_fixinisn __P((fr_info_t *, ipstate_t *)); +static void ipf_fixoutisn __P((fr_info_t *, ipstate_t *)); +static void ipf_checknewisn __P((fr_info_t *, ipstate_t *)); +static int ipf_state_iter __P((ipf_main_softc_t *, ipftoken_t *, + ipfgeniter_t *, ipfobj_t *)); +static int ipf_state_gettable __P((ipf_main_softc_t *, ipf_state_softc_t *, + char *)); +static int ipf_state_tcpinwindow __P((struct fr_info *, struct tcpdata *, + struct tcpdata *, tcphdr_t *, int)); -int fr_stputent __P((caddr_t)); -int fr_stgetent __P((caddr_t)); +static int ipf_state_getent __P((ipf_main_softc_t *, ipf_state_softc_t *, + caddr_t)); +static int ipf_state_putent __P((ipf_main_softc_t *, ipf_state_softc_t *, + caddr_t)); #define ONE_DAY IPF_TTLVAL(1 * 86400) /* 1 day */ #define FIVE_DAYS (5 * ONE_DAY) -#define DOUBLE_HASH(x) (((x) + ips_seed[(x) % fr_statesize]) % fr_statesize) - -u_long fr_tcpidletimeout = FIVE_DAYS, - fr_tcpclosewait = IPF_TTLVAL(2 * TCP_MSL), - fr_tcplastack = IPF_TTLVAL(30), - fr_tcptimeout = IPF_TTLVAL(2 * TCP_MSL), - fr_tcptimewait = IPF_TTLVAL(2 * TCP_MSL), - fr_tcpclosed = IPF_TTLVAL(30), - fr_tcphalfclosed = IPF_TTLVAL(2 * 3600), /* 2 hours */ - fr_udptimeout = IPF_TTLVAL(120), - fr_udpacktimeout = IPF_TTLVAL(12), - fr_icmptimeout = IPF_TTLVAL(60), - fr_icmpacktimeout = IPF_TTLVAL(6), - fr_iptimeout = IPF_TTLVAL(60); -int fr_statemax = IPSTATE_MAX, - fr_statesize = IPSTATE_SIZE; -int fr_state_doflush = 0, - fr_state_lock = 0, - fr_state_maxbucket = 0, - fr_state_maxbucket_reset = 1, - fr_state_init = 0; -ipftq_t ips_tqtqb[IPF_TCP_NSTATES], - ips_udptq, - ips_udpacktq, - ips_iptq, - ips_icmptq, - ips_icmpacktq, - ips_deletetq, - *ips_utqe = NULL; -#ifdef IPFILTER_LOG -int ipstate_logging = 1; -#else -int ipstate_logging = 0; -#endif -ipstate_t *ips_list = NULL; +#define DOUBLE_HASH(x) (((x) + softs->ipf_state_seed[(x) % \ + softs->ipf_state_size]) % softs->ipf_state_size) /* ------------------------------------------------------------------------ */ -/* Function: fr_stateinit */ +/* Function: ipf_state_main_load */ /* Returns: int - 0 == success, -1 == failure */ /* Parameters: Nil */ /* */ -/* Initialise all the global variables used within the state code. */ -/* This action also includes initiailising locks. */ +/* A null-op function that exists as a placeholder so that the flow in */ +/* other functions is obvious. */ /* ------------------------------------------------------------------------ */ -int fr_stateinit() +int +ipf_state_main_load() { -#if defined(NEED_LOCAL_RAND) || !defined(_KERNEL) - struct timeval tv; -#endif - int i; - - KMALLOCS(ips_table, ipstate_t **, fr_statesize * sizeof(ipstate_t *)); - if (ips_table == NULL) - return -1; - bzero((char *)ips_table, fr_statesize * sizeof(ipstate_t *)); - - KMALLOCS(ips_seed, u_long *, fr_statesize * sizeof(*ips_seed)); - if (ips_seed == NULL) - return -2; -#if defined(NEED_LOCAL_RAND) || !defined(_KERNEL) - tv.tv_sec = 0; - GETKTIME(&tv); -#endif - for (i = 0; i < fr_statesize; i++) { - /* - * XXX - ips_seed[X] should be a random number of sorts. - */ -#if !defined(NEED_LOCAL_RAND) && defined(_KERNEL) - ips_seed[i] = arc4random(); -#else - ips_seed[i] = ((u_long)ips_seed + i) * fr_statesize; - ips_seed[i] += tv.tv_sec; - ips_seed[i] *= (u_long)ips_seed; - ips_seed[i] ^= 0x5a5aa5a5; - ips_seed[i] *= fr_statemax; -#endif - } -#if defined(NEED_LOCAL_RAND) && defined(_KERNEL) - ipf_rand_push(ips_seed, fr_statesize * sizeof(*ips_seed)); -#endif - - /* fill icmp reply type table */ - for (i = 0; i <= ICMP_MAXTYPE; i++) - icmpreplytype4[i] = -1; - icmpreplytype4[ICMP_ECHO] = ICMP_ECHOREPLY; - icmpreplytype4[ICMP_TSTAMP] = ICMP_TSTAMPREPLY; - icmpreplytype4[ICMP_IREQ] = ICMP_IREQREPLY; - icmpreplytype4[ICMP_MASKREQ] = ICMP_MASKREPLY; -#ifdef USE_INET6 - /* fill icmp reply type table */ - for (i = 0; i <= ICMP6_MAXTYPE; i++) - icmpreplytype6[i] = -1; - icmpreplytype6[ICMP6_ECHO_REQUEST] = ICMP6_ECHO_REPLY; - icmpreplytype6[ICMP6_MEMBERSHIP_QUERY] = ICMP6_MEMBERSHIP_REPORT; - icmpreplytype6[ICMP6_NI_QUERY] = ICMP6_NI_REPLY; - icmpreplytype6[ND_ROUTER_SOLICIT] = ND_ROUTER_ADVERT; - icmpreplytype6[ND_NEIGHBOR_SOLICIT] = ND_NEIGHBOR_ADVERT; -#endif - - KMALLOCS(ips_stats.iss_bucketlen, u_long *, - fr_statesize * sizeof(u_long)); - if (ips_stats.iss_bucketlen == NULL) - return -1; - bzero((char *)ips_stats.iss_bucketlen, fr_statesize * sizeof(u_long)); - - if (fr_state_maxbucket == 0) { - for (i = fr_statesize; i > 0; i >>= 1) - fr_state_maxbucket++; - fr_state_maxbucket *= 2; - } - - ips_stats.iss_tcptab = ips_tqtqb; - fr_sttab_init(ips_tqtqb); - ips_tqtqb[IPF_TCP_NSTATES - 1].ifq_next = &ips_udptq; - ips_udptq.ifq_ttl = (u_long)fr_udptimeout; - ips_udptq.ifq_ref = 1; - ips_udptq.ifq_head = NULL; - ips_udptq.ifq_tail = &ips_udptq.ifq_head; - MUTEX_INIT(&ips_udptq.ifq_lock, "ipftq udp tab"); - ips_udptq.ifq_next = &ips_udpacktq; - ips_udpacktq.ifq_ttl = (u_long)fr_udpacktimeout; - ips_udpacktq.ifq_ref = 1; - ips_udpacktq.ifq_head = NULL; - ips_udpacktq.ifq_tail = &ips_udpacktq.ifq_head; - MUTEX_INIT(&ips_udpacktq.ifq_lock, "ipftq udpack tab"); - ips_udpacktq.ifq_next = &ips_icmptq; - ips_icmptq.ifq_ttl = (u_long)fr_icmptimeout; - ips_icmptq.ifq_ref = 1; - ips_icmptq.ifq_head = NULL; - ips_icmptq.ifq_tail = &ips_icmptq.ifq_head; - MUTEX_INIT(&ips_icmptq.ifq_lock, "ipftq icmp tab"); - ips_icmptq.ifq_next = &ips_icmpacktq; - ips_icmpacktq.ifq_ttl = (u_long)fr_icmpacktimeout; - ips_icmpacktq.ifq_ref = 1; - ips_icmpacktq.ifq_head = NULL; - ips_icmpacktq.ifq_tail = &ips_icmpacktq.ifq_head; - MUTEX_INIT(&ips_icmpacktq.ifq_lock, "ipftq icmpack tab"); - ips_icmpacktq.ifq_next = &ips_iptq; - ips_iptq.ifq_ttl = (u_long)fr_iptimeout; - ips_iptq.ifq_ref = 1; - ips_iptq.ifq_head = NULL; - ips_iptq.ifq_tail = &ips_iptq.ifq_head; - MUTEX_INIT(&ips_iptq.ifq_lock, "ipftq ip tab"); - ips_iptq.ifq_next = &ips_deletetq; - ips_deletetq.ifq_ttl = (u_long)1; - ips_deletetq.ifq_ref = 1; - ips_deletetq.ifq_head = NULL; - ips_deletetq.ifq_tail = &ips_deletetq.ifq_head; - MUTEX_INIT(&ips_deletetq.ifq_lock, "state delete queue"); - ips_deletetq.ifq_next = NULL; - - RWLOCK_INIT(&ipf_state, "ipf IP state rwlock"); - MUTEX_INIT(&ipf_stinsert, "ipf state insert mutex"); - fr_state_init = 1; - - ips_last_force_flush = fr_ticks; return 0; } /* ------------------------------------------------------------------------ */ -/* Function: fr_stateunload */ -/* Returns: Nil */ +/* Function: ipf_state_main_unload */ +/* Returns: int - 0 == success, -1 == failure */ /* Parameters: Nil */ /* */ +/* A null-op function that exists as a placeholder so that the flow in */ +/* other functions is obvious. */ +/* ------------------------------------------------------------------------ */ +int +ipf_state_main_unload() +{ + return 0; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_state_soft_create */ +/* Returns: void * - NULL = failure, else pointer to soft context */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* */ +/* Create a new state soft context structure and populate it with the list */ +/* of tunables and other default settings. */ +/* ------------------------------------------------------------------------ */ +void * +ipf_state_soft_create(softc) + ipf_main_softc_t *softc; +{ + ipf_state_softc_t *softs; + + KMALLOC(softs, ipf_state_softc_t *); + if (softs == NULL) + return NULL; + + bzero((char *)softs, sizeof(*softs)); + + softs->ipf_state_tune = ipf_tune_array_copy(softs, + sizeof(ipf_state_tuneables), + ipf_state_tuneables); + if (softs->ipf_state_tune == NULL) { + ipf_state_soft_destroy(softc, softs); + return NULL; + } + if (ipf_tune_array_link(softc, softs->ipf_state_tune) == -1) { + ipf_state_soft_destroy(softc, softs); + return NULL; + } + +#ifdef IPFILTER_LOG + softs->ipf_state_logging = 1; +#else + softs->ipf_state_logging = 0; +#endif + softs->ipf_state_size = IPSTATE_SIZE, + softs->ipf_state_maxbucket = 0; + softs->ipf_state_wm_freq = IPF_TTLVAL(10); + softs->ipf_state_max = IPSTATE_MAX; + softs->ipf_state_wm_last = 0; + softs->ipf_state_wm_high = 99; + softs->ipf_state_wm_low = 90; + softs->ipf_state_inited = 0; + softs->ipf_state_lock = 0; + softs->ipf_state_doflush = 0; + + return softs; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_state_soft_destroy */ +/* Returns: Nil */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* arg(I) - pointer to local context to use */ +/* */ +/* Undo only what we did in soft create: unlink and free the tunables and */ +/* free the soft context structure itself. */ +/* ------------------------------------------------------------------------ */ +void +ipf_state_soft_destroy(softc, arg) + ipf_main_softc_t *softc; + void *arg; +{ + ipf_state_softc_t *softs = arg; + + if (softs->ipf_state_tune != NULL) { + ipf_tune_array_unlink(softc, softs->ipf_state_tune); + KFREES(softs->ipf_state_tune, sizeof(ipf_state_tuneables)); + softs->ipf_state_tune = NULL; + } + + KFREE(softs); +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_state_soft_init */ +/* Returns: int - 0 == success, -1 == failure */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* arg(I) - pointer to local context to use */ +/* */ +/* Initialise the state soft context structure so it is ready for use. */ +/* This involves: */ +/* - allocating a hash table and zero'ing it out */ +/* - building a secondary table of seeds for double hashing to make it more */ +/* difficult to attempt to attack the hash table itself (for DoS) */ +/* - initialise all of the timeout queues, including a table for TCP, some */ +/* pairs of query/response for UDP and other IP protocols (typically the */ +/* reply queue has a shorter timeout than the query) */ +/* ------------------------------------------------------------------------ */ +int +ipf_state_soft_init(softc, arg) + ipf_main_softc_t *softc; + void *arg; +{ + ipf_state_softc_t *softs = arg; + int i; + + KMALLOCS(softs->ipf_state_table, + ipstate_t **, softs->ipf_state_size * sizeof(ipstate_t *)); + if (softs->ipf_state_table == NULL) + return -1; + + bzero((char *)softs->ipf_state_table, + softs->ipf_state_size * sizeof(ipstate_t *)); + + KMALLOCS(softs->ipf_state_seed, u_long *, + softs->ipf_state_size * sizeof(*softs->ipf_state_seed)); + if (softs->ipf_state_seed == NULL) + return -2; + + for (i = 0; i < softs->ipf_state_size; i++) { + /* + * XXX - ipf_state_seed[X] should be a random number of sorts. + */ +#if FREEBSD_GE_REV(400000) + softs->ipf_state_seed[i] = arc4random(); +#else + softs->ipf_state_seed[i] = ((u_long)softs->ipf_state_seed + i) * + softs->ipf_state_size; + softs->ipf_state_seed[i] ^= 0xa5a55a5a; + softs->ipf_state_seed[i] *= (u_long)softs->ipf_state_seed; + softs->ipf_state_seed[i] ^= 0x5a5aa5a5; + softs->ipf_state_seed[i] *= softs->ipf_state_max; +#endif + } + + KMALLOCS(softs->ipf_state_stats.iss_bucketlen, u_int *, + softs->ipf_state_size * sizeof(u_int)); + if (softs->ipf_state_stats.iss_bucketlen == NULL) + return -3; + + bzero((char *)softs->ipf_state_stats.iss_bucketlen, + softs->ipf_state_size * sizeof(u_int)); + + if (softs->ipf_state_maxbucket == 0) { + for (i = softs->ipf_state_size; i > 0; i >>= 1) + softs->ipf_state_maxbucket++; + softs->ipf_state_maxbucket *= 2; + } + + ipf_sttab_init(softc, softs->ipf_state_tcptq); + softs->ipf_state_stats.iss_tcptab = softs->ipf_state_tcptq; + softs->ipf_state_tcptq[IPF_TCP_NSTATES - 1].ifq_next = + &softs->ipf_state_udptq; + + IPFTQ_INIT(&softs->ipf_state_udptq, softc->ipf_udptimeout, + "ipftq udp tab"); + softs->ipf_state_udptq.ifq_next = &softs->ipf_state_udpacktq; + + IPFTQ_INIT(&softs->ipf_state_udpacktq, softc->ipf_udpacktimeout, + "ipftq udpack tab"); + softs->ipf_state_udpacktq.ifq_next = &softs->ipf_state_icmptq; + + IPFTQ_INIT(&softs->ipf_state_icmptq, softc->ipf_icmptimeout, + "ipftq icmp tab"); + softs->ipf_state_icmptq.ifq_next = &softs->ipf_state_icmpacktq; + + IPFTQ_INIT(&softs->ipf_state_icmpacktq, softc->ipf_icmpacktimeout, + "ipftq icmpack tab"); + softs->ipf_state_icmpacktq.ifq_next = &softs->ipf_state_iptq; + + IPFTQ_INIT(&softs->ipf_state_iptq, softc->ipf_iptimeout, + "ipftq iptimeout tab"); + softs->ipf_state_iptq.ifq_next = &softs->ipf_state_pending; + + IPFTQ_INIT(&softs->ipf_state_pending, IPF_HZ_DIVIDE, "ipftq pending"); + softs->ipf_state_pending.ifq_next = &softs->ipf_state_deletetq; + + IPFTQ_INIT(&softs->ipf_state_deletetq, 1, "ipftq delete"); + softs->ipf_state_deletetq.ifq_next = NULL; + + MUTEX_INIT(&softs->ipf_stinsert, "ipf state insert mutex"); + + + softs->ipf_state_wm_last = softc->ipf_ticks; + softs->ipf_state_inited = 1; + + return 0; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_state_soft_fini */ +/* Returns: int - 0 = success, -1 = failure */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* arg(I) - pointer to local context to use */ +/* */ /* Release and destroy any resources acquired or initialised so that */ /* IPFilter can be unloaded or re-initialised. */ /* ------------------------------------------------------------------------ */ -void fr_stateunload() +int +ipf_state_soft_fini(softc, arg) + ipf_main_softc_t *softc; + void *arg; { + ipf_state_softc_t *softs = arg; ipftq_t *ifq, *ifqnext; ipstate_t *is; - while ((is = ips_list) != NULL) - fr_delstate(is, ISL_UNLOAD); + while ((is = softs->ipf_state_list) != NULL) + ipf_state_del(softc, is, ISL_UNLOAD); /* * Proxy timeout queues are not cleaned here because although they - * exist on the state list, appr_unload is called after fr_stateunload - * and the proxies actually are responsible for them being created. - * Should the proxy timeouts have their own list? There's no real - * justification as this is the only complicationA + * exist on the state list, appr_unload is called after + * ipf_state_unload and the proxies actually are responsible for them + * being created. Should the proxy timeouts have their own list? + * There's no real justification as this is the only complication. */ - for (ifq = ips_utqe; ifq != NULL; ifq = ifqnext) { + for (ifq = softs->ipf_state_usertq; ifq != NULL; ifq = ifqnext) { ifqnext = ifq->ifq_next; - if (((ifq->ifq_flags & IFQF_PROXY) == 0) && - (fr_deletetimeoutqueue(ifq) == 0)) - fr_freetimeoutqueue(ifq); + + if (ipf_deletetimeoutqueue(ifq) == 0) + ipf_freetimeoutqueue(softc, ifq); } - ips_stats.iss_inuse = 0; - ips_num = 0; + softs->ipf_state_stats.iss_inuse = 0; + softs->ipf_state_stats.iss_active = 0; - if (fr_state_init == 1) { - fr_sttab_destroy(ips_tqtqb); - MUTEX_DESTROY(&ips_udptq.ifq_lock); - MUTEX_DESTROY(&ips_icmptq.ifq_lock); - MUTEX_DESTROY(&ips_udpacktq.ifq_lock); - MUTEX_DESTROY(&ips_icmpacktq.ifq_lock); - MUTEX_DESTROY(&ips_iptq.ifq_lock); - MUTEX_DESTROY(&ips_deletetq.ifq_lock); + if (softs->ipf_state_inited == 1) { + softs->ipf_state_inited = 0; + ipf_sttab_destroy(softs->ipf_state_tcptq); + MUTEX_DESTROY(&softs->ipf_state_udptq.ifq_lock); + MUTEX_DESTROY(&softs->ipf_state_icmptq.ifq_lock); + MUTEX_DESTROY(&softs->ipf_state_udpacktq.ifq_lock); + MUTEX_DESTROY(&softs->ipf_state_icmpacktq.ifq_lock); + MUTEX_DESTROY(&softs->ipf_state_iptq.ifq_lock); + MUTEX_DESTROY(&softs->ipf_state_deletetq.ifq_lock); + MUTEX_DESTROY(&softs->ipf_state_pending.ifq_lock); + MUTEX_DESTROY(&softs->ipf_stinsert); } - if (ips_table != NULL) { - KFREES(ips_table, fr_statesize * sizeof(*ips_table)); - ips_table = NULL; + if (softs->ipf_state_table != NULL) { + KFREES(softs->ipf_state_table, + softs->ipf_state_size * sizeof(*softs->ipf_state_table)); + softs->ipf_state_table = NULL; } - if (ips_seed != NULL) { - KFREES(ips_seed, fr_statesize * sizeof(*ips_seed)); - ips_seed = NULL; + if (softs->ipf_state_seed != NULL) { + KFREES(softs->ipf_state_seed, + softs->ipf_state_size * sizeof(*softs->ipf_state_seed)); + softs->ipf_state_seed = NULL; } - if (ips_stats.iss_bucketlen != NULL) { - KFREES(ips_stats.iss_bucketlen, fr_statesize * sizeof(u_long)); - ips_stats.iss_bucketlen = NULL; + if (softs->ipf_state_stats.iss_bucketlen != NULL) { + KFREES(softs->ipf_state_stats.iss_bucketlen, + softs->ipf_state_size * sizeof(u_int)); + softs->ipf_state_stats.iss_bucketlen = NULL; } - if (fr_state_maxbucket_reset == 1) - fr_state_maxbucket = 0; - - if (fr_state_init == 1) { - fr_state_init = 0; - RW_DESTROY(&ipf_state); - MUTEX_DESTROY(&ipf_stinsert); - } + return 0; } /* ------------------------------------------------------------------------ */ -/* Function: fr_statetstats */ +/* Function: ipf_state_set_lock */ +/* Returns: Nil */ +/* Parameters: arg(I) - pointer to local context to use */ +/* tmp(I) - new value for lock */ +/* */ +/* Stub function that allows for external manipulation of ipf_state_lock */ +/* ------------------------------------------------------------------------ */ +void +ipf_state_setlock(arg, tmp) + void *arg; + int tmp; +{ + ipf_state_softc_t *softs = arg; + + softs->ipf_state_lock = tmp; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_state_stats */ /* Returns: ips_state_t* - pointer to state stats structure */ -/* Parameters: Nil */ +/* Parameters: softc(I) - pointer to soft context main structure */ /* */ /* Put all the current numbers and pointers into a single struct and return */ /* a pointer to it. */ /* ------------------------------------------------------------------------ */ -static ips_stat_t *fr_statetstats() +static ips_stat_t * +ipf_state_stats(softc) + ipf_main_softc_t *softc; { - ips_stats.iss_active = ips_num; - ips_stats.iss_statesize = fr_statesize; - ips_stats.iss_statemax = fr_statemax; - ips_stats.iss_table = ips_table; - ips_stats.iss_list = ips_list; - ips_stats.iss_ticks = fr_ticks; - return &ips_stats; + ipf_state_softc_t *softs = softc->ipf_state_soft; + ips_stat_t *issp = &softs->ipf_state_stats; + + issp->iss_state_size = softs->ipf_state_size; + issp->iss_state_max = softs->ipf_state_max; + issp->iss_table = softs->ipf_state_table; + issp->iss_list = softs->ipf_state_list; + issp->iss_ticks = softc->ipf_ticks; + +#ifdef IPFILTER_LOGGING + issp->iss_log_ok = ipf_log_logok(softc, IPF_LOGSTATE); + issp->iss_log_fail = ipf_log_failures(softc, IPF_LOGSTATE); +#else + issp->iss_log_ok = 0; + issp->iss_log_fail = 0; +#endif + return issp; } /* ------------------------------------------------------------------------ */ -/* Function: fr_state_remove */ +/* Function: ipf_state_remove */ /* Returns: int - 0 == success, != 0 == failure */ -/* Parameters: data(I) - pointer to state structure to delete from table */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* data(I) - pointer to state structure to delete from table */ /* */ /* Search for a state structure that matches the one passed, according to */ /* the IP addresses and other protocol specific information. */ /* ------------------------------------------------------------------------ */ -static int fr_state_remove(data) -caddr_t data; +static int +ipf_state_remove(softc, data) + ipf_main_softc_t *softc; + caddr_t data; { + ipf_state_softc_t *softs = softc->ipf_state_soft; ipstate_t *sp, st; int error; sp = &st; - error = fr_inobj(data, &st, IPFOBJ_IPSTATE); + error = ipf_inobj(softc, data, NULL, &st, IPFOBJ_IPSTATE); if (error) return EFAULT; - WRITE_ENTER(&ipf_state); - for (sp = ips_list; sp; sp = sp->is_next) + WRITE_ENTER(&softc->ipf_state); + for (sp = softs->ipf_state_list; sp; sp = sp->is_next) if ((sp->is_p == st.is_p) && (sp->is_v == st.is_v) && !bcmp((caddr_t)&sp->is_src, (caddr_t)&st.is_src, sizeof(st.is_src)) && - !bcmp((caddr_t)&sp->is_dst, (caddr_t)&st.is_src, + !bcmp((caddr_t)&sp->is_dst, (caddr_t)&st.is_dst, sizeof(st.is_dst)) && !bcmp((caddr_t)&sp->is_ps, (caddr_t)&st.is_ps, sizeof(st.is_ps))) { - fr_delstate(sp, ISL_REMOVE); - RWLOCK_EXIT(&ipf_state); + ipf_state_del(softc, sp, ISL_REMOVE); + RWLOCK_EXIT(&softc->ipf_state); return 0; } - RWLOCK_EXIT(&ipf_state); + RWLOCK_EXIT(&softc->ipf_state); + + IPFERROR(100001); return ESRCH; } /* ------------------------------------------------------------------------ */ -/* Function: fr_state_ioctl */ +/* Function: ipf_state_ioctl */ /* Returns: int - 0 == success, != 0 == failure */ -/* Parameters: data(I) - pointer to ioctl data */ -/* cmd(I) - ioctl command integer */ -/* mode(I) - file mode bits used with open */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* data(I) - pointer to ioctl data */ +/* cmd(I) - ioctl command integer */ +/* mode(I) - file mode bits used with open */ +/* uid(I) - uid of process making the ioctl call */ +/* ctx(I) - pointer specific to context of the call */ /* */ /* Processes an ioctl call made to operate on the IP Filter state device. */ /* ------------------------------------------------------------------------ */ -int fr_state_ioctl(data, cmd, mode, uid, ctx) -caddr_t data; -ioctlcmd_t cmd; -int mode, uid; -void *ctx; +int +ipf_state_ioctl(softc, data, cmd, mode, uid, ctx) + ipf_main_softc_t *softc; + caddr_t data; + ioctlcmd_t cmd; + int mode, uid; + void *ctx; { + ipf_state_softc_t *softs = softc->ipf_state_soft; int arg, ret, error = 0; SPL_INT(s); @@ -458,55 +605,75 @@ void *ctx; * Delete an entry from the state table. */ case SIOCDELST : - error = fr_state_remove(data); + error = ipf_state_remove(softc, data); break; /* * Flush the state table */ case SIOCIPFFL : - error = BCOPYIN(data, (char *)&arg, sizeof(arg)); + error = BCOPYIN(data, &arg, sizeof(arg)); if (error != 0) { + IPFERROR(100002); error = EFAULT; + } else { - WRITE_ENTER(&ipf_state); - ret = fr_state_flush(arg, 4); - RWLOCK_EXIT(&ipf_state); - error = BCOPYOUT((char *)&ret, data, sizeof(ret)); - if (error != 0) + WRITE_ENTER(&softc->ipf_state); + ret = ipf_state_flush(softc, arg, 4); + RWLOCK_EXIT(&softc->ipf_state); + + error = BCOPYOUT(&ret, data, sizeof(ret)); + if (error != 0) { + IPFERROR(100003); error = EFAULT; + } } break; #ifdef USE_INET6 case SIOCIPFL6 : - error = BCOPYIN(data, (char *)&arg, sizeof(arg)); + error = BCOPYIN(data, &arg, sizeof(arg)); if (error != 0) { + IPFERROR(100004); error = EFAULT; + } else { - WRITE_ENTER(&ipf_state); - ret = fr_state_flush(arg, 6); - RWLOCK_EXIT(&ipf_state); - error = BCOPYOUT((char *)&ret, data, sizeof(ret)); - if (error != 0) + WRITE_ENTER(&softc->ipf_state); + ret = ipf_state_flush(softc, arg, 6); + RWLOCK_EXIT(&softc->ipf_state); + + error = BCOPYOUT(&ret, data, sizeof(ret)); + if (error != 0) { + IPFERROR(100005); error = EFAULT; + } } break; #endif + + case SIOCMATCHFLUSH : + WRITE_ENTER(&softc->ipf_state); + error = ipf_state_matchflush(softc, data); + RWLOCK_EXIT(&softc->ipf_state); + break; + #ifdef IPFILTER_LOG /* * Flush the state log. */ case SIOCIPFFB : - if (!(mode & FWRITE)) + if (!(mode & FWRITE)) { + IPFERROR(100008); error = EPERM; - else { + } else { int tmp; - tmp = ipflog_clear(IPL_LOGSTATE); - error = BCOPYOUT((char *)&tmp, data, sizeof(tmp)); - if (error != 0) + tmp = ipf_log_clear(softc, IPL_LOGSTATE); + error = BCOPYOUT(&tmp, data, sizeof(tmp)); + if (error != 0) { + IPFERROR(100009); error = EFAULT; + } } break; @@ -514,13 +681,16 @@ void *ctx; * Turn logging of state information on/off. */ case SIOCSETLG : - if (!(mode & FWRITE)) + if (!(mode & FWRITE)) { + IPFERROR(100010); error = EPERM; - else { - error = BCOPYIN((char *)data, (char *)&ipstate_logging, - sizeof(ipstate_logging)); - if (error != 0) + } else { + error = BCOPYIN(data, &softs->ipf_state_logging, + sizeof(softs->ipf_state_logging)); + if (error != 0) { + IPFERROR(100011); error = EFAULT; + } } break; @@ -528,20 +698,24 @@ void *ctx; * Return the current state of logging. */ case SIOCGETLG : - error = BCOPYOUT((char *)&ipstate_logging, (char *)data, - sizeof(ipstate_logging)); - if (error != 0) + error = BCOPYOUT(&softs->ipf_state_logging, data, + sizeof(softs->ipf_state_logging)); + if (error != 0) { + IPFERROR(100012); error = EFAULT; + } break; /* * Return the number of bytes currently waiting to be read. */ case FIONREAD : - arg = iplused[IPL_LOGSTATE]; /* returned in an int */ - error = BCOPYOUT((char *)&arg, data, sizeof(arg)); - if (error != 0) + arg = ipf_log_bytesused(softc, IPL_LOGSTATE); + error = BCOPYOUT(&arg, data, sizeof(arg)); + if (error != 0) { + IPFERROR(100013); error = EFAULT; + } break; #endif @@ -549,7 +723,8 @@ void *ctx; * Get the current state statistics. */ case SIOCGETFS : - error = fr_outobj(data, fr_statetstats(), IPFOBJ_STATESTAT); + error = ipf_outobj(softc, data, ipf_state_stats(softc), + IPFOBJ_STATESTAT); break; /* @@ -558,9 +733,10 @@ void *ctx; */ case SIOCSTLCK : if (!(mode & FWRITE)) { + IPFERROR(100014); error = EPERM; } else { - error = fr_lock(data, &fr_state_lock); + error = ipf_lock(data, &softs->ipf_state_lock); } break; @@ -568,74 +744,86 @@ void *ctx; * Add an entry to the current state table. */ case SIOCSTPUT : - if (!fr_state_lock || !(mode &FWRITE)) { + if (!softs->ipf_state_lock || !(mode &FWRITE)) { + IPFERROR(100015); error = EACCES; break; } - error = fr_stputent(data); + error = ipf_state_putent(softc, softs, data); break; /* * Get a state table entry. */ case SIOCSTGET : - if (!fr_state_lock) { + if (!softs->ipf_state_lock) { + IPFERROR(100016); error = EACCES; break; } - error = fr_stgetent(data); + error = ipf_state_getent(softc, softs, data); break; /* * Return a copy of the hash table bucket lengths */ case SIOCSTAT1 : - error = BCOPYOUT(ips_stats.iss_bucketlen, data, - fr_statesize * sizeof(u_long)); - if (error != 0) + error = BCOPYOUT(softs->ipf_state_stats.iss_bucketlen, data, + softs->ipf_state_size * sizeof(u_int)); + if (error != 0) { + IPFERROR(100017); error = EFAULT; + } break; case SIOCGENITER : { ipftoken_t *token; ipfgeniter_t iter; + ipfobj_t obj; - error = fr_inobj(data, &iter, IPFOBJ_GENITER); + error = ipf_inobj(softc, data, &obj, &iter, IPFOBJ_GENITER); if (error != 0) break; SPL_SCHED(s); - token = ipf_findtoken(IPFGENITER_STATE, uid, ctx); - if (token != NULL) - error = fr_stateiter(token, &iter); - else + token = ipf_token_find(softc, IPFGENITER_STATE, uid, ctx); + if (token != NULL) { + error = ipf_state_iter(softc, token, &iter, &obj); + WRITE_ENTER(&softc->ipf_tokens); + ipf_token_deref(softc, token); + RWLOCK_EXIT(&softc->ipf_tokens); + } else { + IPFERROR(100018); error = ESRCH; - RWLOCK_EXIT(&ipf_tokens); + } SPL_X(s); break; } case SIOCGTABL : - error = fr_stgettable(data); + error = ipf_state_gettable(softc, softs, data); break; case SIOCIPFDELTOK : - error = BCOPYIN(data, (char *)&arg, sizeof(arg)); + error = BCOPYIN(data, &arg, sizeof(arg)); if (error != 0) { + IPFERROR(100019); error = EFAULT; } else { SPL_SCHED(s); - error = ipf_deltoken(arg, uid, ctx); + error = ipf_token_del(softc, arg, uid, ctx); SPL_X(s); } break; case SIOCGTQTAB : - error = fr_outobj(data, ips_tqtqb, IPFOBJ_STATETQTAB); + error = ipf_outobj(softc, data, softs->ipf_state_tcptq, + IPFOBJ_STATETQTAB); break; default : + IPFERROR(100020); error = EINVAL; break; } @@ -644,9 +832,11 @@ void *ctx; /* ------------------------------------------------------------------------ */ -/* Function: fr_stgetent */ +/* Function: ipf_state_getent */ /* Returns: int - 0 == success, != 0 == failure */ -/* Parameters: data(I) - pointer to state structure to retrieve from table */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* softs(I) - pointer to state context structure */ +/* data(I) - pointer to state structure to retrieve from table*/ /* */ /* Copy out state information from the kernel to a user space process. If */ /* there is a filter rule associated with the state entry, copy that out */ @@ -654,25 +844,30 @@ void *ctx; /* the struct passed in and if not null and not found in the list of current*/ /* state entries, the retrieval fails. */ /* ------------------------------------------------------------------------ */ -int fr_stgetent(data) -caddr_t data; +static int +ipf_state_getent(softc, softs, data) + ipf_main_softc_t *softc; + ipf_state_softc_t *softs; + caddr_t data; { ipstate_t *is, *isn; ipstate_save_t ips; int error; - error = fr_inobj(data, &ips, IPFOBJ_STATESAVE); - if (error != 0) - return error; + error = ipf_inobj(softc, data, NULL, &ips, IPFOBJ_STATESAVE); + if (error) + return EFAULT; - READ_ENTER(&ipf_state); + READ_ENTER(&softc->ipf_state); isn = ips.ips_next; if (isn == NULL) { - isn = ips_list; + isn = softs->ipf_state_list; if (isn == NULL) { - RWLOCK_EXIT(&ipf_state); - if (ips.ips_next == NULL) + if (ips.ips_next == NULL) { + RWLOCK_EXIT(&softc->ipf_state); + IPFERROR(100021); return ENOENT; + } return 0; } } else { @@ -681,11 +876,12 @@ caddr_t data; * current list of entries. Security precaution to prevent * copying of random kernel data. */ - for (is = ips_list; is; is = is->is_next) + for (is = softs->ipf_state_list; is; is = is->is_next) if (is == isn) break; - if (is == NULL) { - RWLOCK_EXIT(&ipf_state); + if (!is) { + RWLOCK_EXIT(&softc->ipf_state); + IPFERROR(100022); return ESRCH; } } @@ -695,24 +891,29 @@ caddr_t data; if (isn->is_rule != NULL) bcopy((char *)isn->is_rule, (char *)&ips.ips_fr, sizeof(ips.ips_fr)); - RWLOCK_EXIT(&ipf_state); - error = fr_outobj(data, &ips, IPFOBJ_STATESAVE); + RWLOCK_EXIT(&softc->ipf_state); + error = ipf_outobj(softc, data, &ips, IPFOBJ_STATESAVE); return error; } /* ------------------------------------------------------------------------ */ -/* Function: fr_stputent */ +/* Function: ipf_state_putent */ /* Returns: int - 0 == success, != 0 == failure */ -/* Parameters: data(I) - pointer to state information struct */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* softs(I) - pointer to state context structure */ +/* data(I) - pointer to state information struct */ /* */ /* This function implements the SIOCSTPUT ioctl: insert a state entry into */ /* the state table. If the state info. includes a pointer to a filter rule */ /* then also add in an orphaned rule (will not show up in any "ipfstat -io" */ /* output. */ /* ------------------------------------------------------------------------ */ -int fr_stputent(data) -caddr_t data; +int +ipf_state_putent(softc, softs, data) + ipf_main_softc_t *softc; + ipf_state_softc_t *softs; + caddr_t data; { ipstate_t *is, *isn; ipstate_save_t ips; @@ -720,13 +921,15 @@ caddr_t data; frentry_t *fr; char *name; - error = fr_inobj(data, &ips, IPFOBJ_STATESAVE); - if (error) - return EFAULT; + error = ipf_inobj(softc, data, NULL, &ips, IPFOBJ_STATESAVE); + if (error != 0) + return error; KMALLOC(isn, ipstate_t *); - if (isn == NULL) + if (isn == NULL) { + IPFERROR(100023); return ENOMEM; + } bcopy((char *)&ips.ips_is, (char *)isn, sizeof(*isn)); bzero((char *)isn, offsetof(struct ipstate, is_pkts)); @@ -742,17 +945,21 @@ caddr_t data; fr = ips.ips_rule; if (fr == NULL) { - READ_ENTER(&ipf_state); - fr_stinsert(isn, 0); + int inserr; + + READ_ENTER(&softc->ipf_state); + inserr = ipf_state_insert(softc, isn, 0); MUTEX_EXIT(&isn->is_lock); - RWLOCK_EXIT(&ipf_state); - return 0; + RWLOCK_EXIT(&softc->ipf_state); + + return inserr; } if (isn->is_flags & SI_NEWFR) { KMALLOC(fr, frentry_t *); if (fr == NULL) { KFREE(isn); + IPFERROR(100024); return ENOMEM; } bcopy((char *)&ips.ips_fr, (char *)fr, sizeof(*fr)); @@ -766,10 +973,19 @@ caddr_t data; * Look up all the interface names in the rule. */ for (i = 0; i < 4; i++) { - name = fr->fr_ifnames[i]; - fr->fr_ifas[i] = fr_resolvenic(name, fr->fr_v); + if (fr->fr_ifnames[i] == -1) { + fr->fr_ifas[i] = NULL; + continue; + } + name = fr->fr_names + fr->fr_ifnames[i]; + fr->fr_ifas[i] = ipf_resolvenic(softc, name, + fr->fr_family); + } + + for (i = 0; i < 4; i++) { name = isn->is_ifname[i]; - isn->is_ifp[i] = fr_resolvenic(name, isn->is_v); + isn->is_ifp[i] = ipf_resolvenic(softc, name, + isn->is_v); } fr->fr_ref = 0; @@ -777,31 +993,35 @@ caddr_t data; fr->fr_data = NULL; fr->fr_type = FR_T_NONE; - fr_resolvedest(&fr->fr_tifs[0], fr->fr_v); - fr_resolvedest(&fr->fr_tifs[1], fr->fr_v); - fr_resolvedest(&fr->fr_dif, fr->fr_v); + (void) ipf_resolvedest(softc, fr->fr_names, &fr->fr_tifs[0], + fr->fr_family); + (void) ipf_resolvedest(softc, fr->fr_names, &fr->fr_tifs[1], + fr->fr_family); + (void) ipf_resolvedest(softc, fr->fr_names, &fr->fr_dif, + fr->fr_family); /* * send a copy back to userland of what we ended up * to allow for verification. */ - error = fr_outobj(data, &ips, IPFOBJ_STATESAVE); - if (error) { + error = ipf_outobj(softc, data, &ips, IPFOBJ_STATESAVE); + if (error != 0) { KFREE(isn); MUTEX_DESTROY(&fr->fr_lock); KFREE(fr); + IPFERROR(100025); return EFAULT; } - READ_ENTER(&ipf_state); - fr_stinsert(isn, 0); + READ_ENTER(&softc->ipf_state); + error = ipf_state_insert(softc, isn, 0); MUTEX_EXIT(&isn->is_lock); - RWLOCK_EXIT(&ipf_state); + RWLOCK_EXIT(&softc->ipf_state); } else { - READ_ENTER(&ipf_state); - for (is = ips_list; is; is = is->is_next) + READ_ENTER(&softc->ipf_state); + for (is = softs->ipf_state_list; is; is = is->is_next) if (is->is_rule == fr) { - fr_stinsert(isn, 0); + error = ipf_state_insert(softc, isn, 0); MUTEX_EXIT(&isn->is_lock); break; } @@ -810,60 +1030,62 @@ caddr_t data; KFREE(isn); isn = NULL; } - RWLOCK_EXIT(&ipf_state); + RWLOCK_EXIT(&softc->ipf_state); - return (isn == NULL) ? ESRCH : 0; + if (isn == NULL) { + IPFERROR(100033); + error = ESRCH; + } } - return 0; + return error; } /* ------------------------------------------------------------------------ */ -/* Function: fr_stinsert */ -/* Returns: Nil */ -/* Parameters: is(I) - pointer to state structure */ -/* rev(I) - flag indicating forward/reverse direction of packet */ +/* Function: ipf_state_insert */ +/* Returns: int - 0 == success, -1 == failure */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* Parameters: is(I) - pointer to state structure */ +/* rev(I) - flag indicating direction of packet */ /* */ /* Inserts a state structure into the hash table (for lookups) and the list */ /* of state entries (for enumeration). Resolves all of the interface names */ /* to pointers and adjusts running stats for the hash table as appropriate. */ /* */ +/* This function can fail if the filter rule has had a population policy of */ +/* IP addresses used with stateful filteirng assigned to it. */ +/* */ /* Locking: it is assumed that some kind of lock on ipf_state is held. */ -/* Exits with is_lock initialised and held. */ +/* Exits with is_lock initialised and held - *EVEN IF ERROR*. */ /* ------------------------------------------------------------------------ */ -void fr_stinsert(is, rev) -ipstate_t *is; -int rev; +int +ipf_state_insert(softc, is, rev) + ipf_main_softc_t *softc; + ipstate_t *is; + int rev; { + ipf_state_softc_t *softs = softc->ipf_state_soft; frentry_t *fr; u_int hv; int i; - MUTEX_INIT(&is->is_lock, "ipf state entry"); - - fr = is->is_rule; - if (fr != NULL) { - MUTEX_ENTER(&fr->fr_lock); - fr->fr_ref++; - fr->fr_statecnt++; - MUTEX_EXIT(&fr->fr_lock); - } - /* * Look up all the interface names in the state entry. */ for (i = 0; i < 4; i++) { if (is->is_ifp[i] != NULL) continue; - is->is_ifp[i] = fr_resolvenic(is->is_ifname[i], is->is_v); + is->is_ifp[i] = ipf_resolvenic(softc, is->is_ifname[i], + is->is_v); } /* - * If we could trust is_hv, then the modulous would not be needed, but - * when running with IPFILTER_SYNC, this stops bad values. + * If we could trust is_hv, then the modulous would not be needed, + * but when running with IPFILTER_SYNC, this stops bad values. */ - hv = is->is_hv % fr_statesize; + hv = is->is_hv % softs->ipf_state_size; + /* TRACE is, hv */ is->is_hv = hv; /* @@ -871,37 +1093,265 @@ int rev; * possible that once the insert is complete another packet might * come along, match the entry and want to update it. */ + MUTEX_INIT(&is->is_lock, "ipf state entry"); MUTEX_ENTER(&is->is_lock); - MUTEX_ENTER(&ipf_stinsert); + MUTEX_ENTER(&softs->ipf_stinsert); + + fr = is->is_rule; + if (fr != NULL) { + if ((fr->fr_srctrack.ht_max_nodes != 0) && + (ipf_ht_node_add(softc, &fr->fr_srctrack, + is->is_family, &is->is_src) == -1)) { + SBUMPD(ipf_state_stats, iss_max_track); + MUTEX_EXIT(&softs->ipf_stinsert); + return -1; + } + + MUTEX_ENTER(&fr->fr_lock); + fr->fr_ref++; + MUTEX_EXIT(&fr->fr_lock); + fr->fr_statecnt++; + } + + if (is->is_flags & (SI_WILDP|SI_WILDA)) { + DT(iss_wild_plus_one); + SINCL(ipf_state_stats.iss_wild); + } + + SBUMP(ipf_state_stats.iss_proto[is->is_p]); + SBUMP(ipf_state_stats.iss_active_proto[is->is_p]); /* * add into list table. */ - if (ips_list != NULL) - ips_list->is_pnext = &is->is_next; - is->is_pnext = &ips_list; - is->is_next = ips_list; - ips_list = is; + if (softs->ipf_state_list != NULL) + softs->ipf_state_list->is_pnext = &is->is_next; + is->is_pnext = &softs->ipf_state_list; + is->is_next = softs->ipf_state_list; + softs->ipf_state_list = is; - if (ips_table[hv] != NULL) - ips_table[hv]->is_phnext = &is->is_hnext; + if (softs->ipf_state_table[hv] != NULL) + softs->ipf_state_table[hv]->is_phnext = &is->is_hnext; else - ips_stats.iss_inuse++; - is->is_phnext = ips_table + hv; - is->is_hnext = ips_table[hv]; - ips_table[hv] = is; - ips_stats.iss_bucketlen[hv]++; - ips_num++; - MUTEX_EXIT(&ipf_stinsert); + softs->ipf_state_stats.iss_inuse++; + is->is_phnext = softs->ipf_state_table + hv; + is->is_hnext = softs->ipf_state_table[hv]; + softs->ipf_state_table[hv] = is; + softs->ipf_state_stats.iss_bucketlen[hv]++; + softs->ipf_state_stats.iss_active++; + MUTEX_EXIT(&softs->ipf_stinsert); - fr_setstatequeue(is, rev); + ipf_state_setqueue(softc, is, rev); + + return 0; } /* ------------------------------------------------------------------------ */ -/* Function: fr_addstate */ -/* Returns: ipstate_t* - NULL == failure, else pointer to new state */ -/* Parameters: fin(I) - pointer to packet information */ +/* Function: ipf_state_matchipv4addrs */ +/* Returns: int - 2 addresses match (strong match), 1 reverse match, */ +/* 0 no match */ +/* Parameters: is1, is2 pointers to states we are checking */ +/* */ +/* Function matches IPv4 addresses it returns strong match for ICMP proto */ +/* even there is only reverse match */ +/* ------------------------------------------------------------------------ */ +static int +ipf_state_matchipv4addrs(is1, is2) + ipstate_t *is1, *is2; +{ + int rv; + + if (is1->is_saddr == is2->is_saddr && is1->is_daddr == is2->is_daddr) + rv = 2; + else if (is1->is_saddr == is2->is_daddr && + is1->is_daddr == is2->is_saddr) { + /* force strong match for ICMP protocol */ + rv = (is1->is_p == IPPROTO_ICMP) ? 2 : 1; + } + else + rv = 0; + + return (rv); +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_state_matchipv6addrs */ +/* Returns: int - 2 addresses match (strong match), 1 reverse match, */ +/* 0 no match */ +/* Parameters: is1, is2 pointers to states we are checking */ +/* */ +/* Function matches IPv6 addresses it returns strong match for ICMP proto */ +/* even there is only reverse match */ +/* ------------------------------------------------------------------------ */ +static int +ipf_state_matchipv6addrs(is1, is2) + ipstate_t *is1, *is2; +{ + int rv; + + if (IP6_EQ(&is1->is_src, &is2->is_src) && + IP6_EQ(&is1->is_dst, &is2->is_dst)) + rv = 2; + else if (IP6_EQ(&is1->is_src, &is2->is_dst) && + IP6_EQ(&is1->is_dst, &is2->is_src)) { + /* force strong match for ICMPv6 protocol */ + rv = (is1->is_p == IPPROTO_ICMPV6) ? 2 : 1; + } + else + rv = 0; + + return (rv); +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_state_matchaddresses */ +/* Returns: int - 2 addresses match, 1 reverse match, zero no match */ +/* Parameters: is1, is2 pointers to states we are checking */ +/* */ +/* function retruns true if two pairs of addresses belong to single */ +/* connection. suppose there are two endpoints: */ +/* endpoint1 1.1.1.1 */ +/* endpoint2 1.1.1.2 */ +/* */ +/* the state is established by packet flying from .1 to .2 so we see: */ +/* is1->src = 1.1.1.1 */ +/* is1->dst = 1.1.1.2 */ +/* now endpoint 1.1.1.2 sends answer */ +/* retreives is1 record created by first packat and compares it with is2 */ +/* temporal record, is2 is initialized as follows: */ +/* is2->src = 1.1.1.2 */ +/* is2->dst = 1.1.1.1 */ +/* in this case 1 will be returned */ +/* */ +/* the ipf_matchaddresses() assumes those two records to be same. of course */ +/* the ipf_matchaddresses() also assume records are same in case you pass */ +/* identical arguments (i.e. ipf_matchaddress(is1, is1) would return 2 */ +/* ------------------------------------------------------------------------ */ +static int +ipf_state_matchaddresses(is1, is2) + ipstate_t *is1, *is2; +{ + int rv; + + if (is1->is_v == 4) { + rv = ipf_state_matchipv4addrs(is1, is2); + } + else { + rv = ipf_state_matchipv6addrs(is1, is2); + } + + return (rv); +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_matchports */ +/* Returns: int - 2 match, 1 rverse match, 0 no match */ +/* Parameters: ppairs1, ppairs - src, dst ports we want to match */ +/* */ +/* performs the same match for isps members as for addresses */ +/* ------------------------------------------------------------------------ */ +static int +ipf_state_matchports(ppairs1, ppairs2) + udpinfo_t *ppairs1, *ppairs2; +{ + int rv; + + if (ppairs1->us_sport == ppairs2->us_sport && + ppairs1->us_dport == ppairs2->us_dport) + rv = 2; + else if (ppairs1->us_sport == ppairs2->us_dport && + ppairs1->us_dport == ppairs2->us_sport) + rv = 1; + else + rv = 0; + + return (rv); +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_matchisps */ +/* Returns: int - nonzero if isps members match, 0 nomatch */ +/* Parameters: is1, is2 - states we want to match */ +/* */ +/* performs the same match for isps members as for addresses */ +/* ------------------------------------------------------------------------ */ +static int +ipf_state_matchisps(is1, is2) + ipstate_t *is1, *is2; +{ + int rv; + + if (is1->is_p == is2->is_p) { + switch (is1->is_p) + { + case IPPROTO_TCP : + case IPPROTO_UDP : + case IPPROTO_GRE : + /* greinfo_t can be also interprted as port pair */ + rv = ipf_state_matchports(&is1->is_ps.is_us, + &is2->is_ps.is_us); + break; + + case IPPROTO_ICMP : + case IPPROTO_ICMPV6 : + /* force strong match for ICMP datagram. */ + if (bcmp(&is1->is_ps, &is2->is_ps, + sizeof(icmpinfo_t)) == 0) { + rv = 2; + } else { + rv = 0; + } + break; + + default: + rv = 0; + } + } else { + rv = 0; + } + + return (rv); +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_state_match */ +/* Returns: int - nonzero match, zero no match */ +/* Parameters: is1, is2 - states we want to match */ +/* */ +/* ------------------------------------------------------------------------ */ +static int +ipf_state_match(is1, is2) + ipstate_t *is1, *is2; +{ + int rv; + int amatch; + int pomatch; + + if (bcmp(&is1->is_pass, &is2->is_pass, + offsetof(struct ipstate, is_authmsk) - + offsetof(struct ipstate, is_pass)) == 0) { + + pomatch = ipf_state_matchisps(is1, is2); + amatch = ipf_state_matchaddresses(is1, is2); + rv = (amatch != 0) && (amatch == pomatch); + } else { + rv = 0; + } + + return (rv); +} + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_state_add */ +/* Returns: ipstate_t - 0 = success */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* fin(I) - pointer to packet information */ /* stsave(O) - pointer to place to save pointer to created */ /* state structure. */ /* flags(I) - flags to use when creating the structure */ @@ -916,25 +1366,49 @@ int rev; /* either outlive this (not expired) or will deref the ip_state_t */ /* when they are deleted. */ /* ------------------------------------------------------------------------ */ -ipstate_t *fr_addstate(fin, stsave, flags) -fr_info_t *fin; -ipstate_t **stsave; -u_int flags; +int +ipf_state_add(softc, fin, stsave, flags) + ipf_main_softc_t *softc; + fr_info_t *fin; + ipstate_t **stsave; + u_int flags; { + ipf_state_softc_t *softs = softc->ipf_state_soft; ipstate_t *is, ips; struct icmp *ic; u_int pass, hv; frentry_t *fr; tcphdr_t *tcp; - grehdr_t *gre; + frdest_t *fdp; int out; - if (fr_state_lock || - (fin->fin_flx & (FI_SHORT|FI_STATE|FI_FRAGBODY|FI_BAD))) - return NULL; + /* + * If a packet that was created locally is trying to go out but we + * do not match here here because of this lock, it is likely that + * the policy will block it and return network unreachable back up + * the stack. To mitigate this error, EAGAIN is returned instead, + * telling the IP stack to try sending this packet again later. + */ + if (softs->ipf_state_lock) { + SBUMPD(ipf_state_stats, iss_add_locked); + fin->fin_error = EAGAIN; + return -1; + } - if ((fin->fin_flx & FI_OOW) && !(fin->fin_tcpf & TH_SYN)) - return NULL; + if (fin->fin_flx & (FI_SHORT|FI_STATE|FI_FRAGBODY|FI_BAD)) { + SBUMPD(ipf_state_stats, iss_add_bad); + return -1; + } + + if ((fin->fin_flx & FI_OOW) && !(fin->fin_tcpf & TH_SYN)) { + SBUMPD(ipf_state_stats, iss_add_oow); + return -1; + } + + if ((softs->ipf_state_stats.iss_active * 100 / softs->ipf_state_max) > + softs->ipf_state_wm_high) { + softs->ipf_state_doflush = 1; + } /* * If a "keep state" rule has reached the maximum number of references @@ -948,26 +1422,49 @@ u_int flags; */ fr = fin->fin_fr; if (fr != NULL) { - if ((ips_num >= fr_statemax) && (fr->fr_statemax == 0)) { - ATOMIC_INCL(ips_stats.iss_max); - fr_state_doflush = 1; - return NULL; + if ((softs->ipf_state_stats.iss_active >= + softs->ipf_state_max) && (fr->fr_statemax == 0)) { + SBUMPD(ipf_state_stats, iss_max); + return 1; } if ((fr->fr_statemax != 0) && (fr->fr_statecnt >= fr->fr_statemax)) { - ATOMIC_INCL(ips_stats.iss_maxref); - return NULL; + SBUMPD(ipf_state_stats, iss_max_ref); + return 2; } } - pass = (fr == NULL) ? 0 : fr->fr_flags; + is = &ips; + if (fr == NULL) { + pass = softc->ipf_flags; + is->is_tag = FR_NOLOGTAG; + } else { + pass = fr->fr_flags; + } ic = NULL; tcp = NULL; out = fin->fin_out; - is = &ips; bzero((char *)is, sizeof(*is)); - is->is_die = 1 + fr_ticks; + is->is_die = 1 + softc->ipf_ticks; + /* + * We want to check everything that is a property of this packet, + * but we don't (automatically) care about it's fragment status as + * this may change. + */ + is->is_pass = pass; + is->is_v = fin->fin_v; + is->is_sec = fin->fin_secmsk; + is->is_secmsk = 0xffff; + is->is_auth = fin->fin_auth; + is->is_authmsk = 0xffff; + is->is_family = fin->fin_family; + is->is_opt[0] = fin->fin_optmsk; + is->is_optmsk[0] = 0xffffffff; + if (is->is_v == 6) { + is->is_opt[0] &= ~0x8; + is->is_optmsk[0] &= ~0x8; + } /* * Copy and calculate... @@ -1008,13 +1505,8 @@ u_int flags; #endif if ((fin->fin_v == 4) && (fin->fin_flx & (FI_MULTICAST|FI_BROADCAST|FI_MBCAST))) { - if (fin->fin_out == 0) { - flags |= SI_W_DADDR|SI_CLONE; - hv -= is->is_daddr; - } else { - flags |= SI_W_SADDR|SI_CLONE; - hv -= is->is_saddr; - } + flags |= SI_W_DADDR; + hv -= is->is_daddr; } switch (is->is_p) @@ -1026,9 +1518,8 @@ u_int flags; switch (ic->icmp_type) { case ICMP6_ECHO_REQUEST : - is->is_icmp.ici_type = ic->icmp_type; hv += (is->is_icmp.ici_id = ic->icmp_id); - break; + /*FALLTHROUGH*/ case ICMP6_MEMBERSHIP_QUERY : case ND_ROUTER_SOLICIT : case ND_NEIGHBOR_SOLICIT : @@ -1036,9 +1527,9 @@ u_int flags; is->is_icmp.ici_type = ic->icmp_type; break; default : - return NULL; + SBUMPD(ipf_state_stats, iss_icmp6_notquery); + return -2; } - ATOMIC_INCL(ips_stats.iss_icmp); break; #endif case IPPROTO_ICMP : @@ -1054,11 +1545,12 @@ u_int flags; hv += (is->is_icmp.ici_id = ic->icmp_id); break; default : - return NULL; + SBUMPD(ipf_state_stats, iss_icmp_notquery); + return -3; } - ATOMIC_INCL(ips_stats.iss_icmp); break; +#if 0 case IPPROTO_GRE : gre = fin->fin_dp; @@ -1069,12 +1561,18 @@ u_int flags; is->is_call[1] = fin->fin_data[1]; } break; +#endif case IPPROTO_TCP : tcp = fin->fin_dp; - if (tcp->th_flags & TH_RST) - return NULL; + if (tcp->th_flags & TH_RST) { + SBUMPD(ipf_state_stats, iss_tcp_rstadd); + return -4; + } + + /* TRACE is, flags, hv */ + /* * The endian of the ports doesn't matter, but the ack and * sequence numbers do as we do mathematics on them later. @@ -1086,6 +1584,8 @@ u_int flags; hv += is->is_dport; } + /* TRACE is, flags, hv */ + /* * If this is a real packet then initialise fields in the * state information structure from the TCP header information. @@ -1110,15 +1610,14 @@ u_int flags; if ((tcp->th_flags & ~(TH_FIN|TH_ACK|TH_ECNALL)) == TH_SYN && (TCP_OFF(tcp) > (sizeof(tcphdr_t) >> 2))) { - if (fr_tcpoptions(fin, tcp, - &is->is_tcp.ts_data[0]) == -1) { + if (ipf_tcpoptions(softs, fin, tcp, + &is->is_tcp.ts_data[0]) == -1) fin->fin_flx |= FI_BAD; - } } if ((fin->fin_out != 0) && (pass & FR_NEWISN) != 0) { - fr_checknewisn(fin, is); - fr_fixoutisn(fin, is); + ipf_checknewisn(fin, is); + ipf_fixoutisn(fin, is); } if ((tcp->th_flags & TH_OPENING) == TH_SYN) @@ -1132,11 +1631,10 @@ u_int flags; } /* - * If we're creating state for a starting connection, start the - * timer on it as we'll never see an error if it fails to - * connect. + * If we're creating state for a starting connection, start + * the timer on it as we'll never see an error if it fails + * to connect. */ - ATOMIC_INCL(ips_stats.iss_tcp); break; case IPPROTO_UDP : @@ -1148,7 +1646,6 @@ u_int flags; hv += tcp->th_dport; hv += tcp->th_sport; } - ATOMIC_INCL(ips_stats.iss_udp); break; default : @@ -1156,144 +1653,173 @@ u_int flags; } hv = DOUBLE_HASH(hv); is->is_hv = hv; - is->is_rule = fr; - is->is_flags = flags & IS_INHERITED; /* * Look for identical state. */ - for (is = ips_table[is->is_hv % fr_statesize]; is != NULL; - is = is->is_hnext) { - if (bcmp(&ips.is_src, &is->is_src, - offsetof(struct ipstate, is_ps) - - offsetof(struct ipstate, is_src)) == 0) + for (is = softs->ipf_state_table[hv % softs->ipf_state_size]; + is != NULL; is = is->is_hnext) { + if (ipf_state_match(&ips, is) == 1) break; } - if (is != NULL) - return NULL; + if (is != NULL) { + SBUMPD(ipf_state_stats, iss_add_dup); + return 3; + } - if (ips_stats.iss_bucketlen[hv] >= fr_state_maxbucket) { - ATOMIC_INCL(ips_stats.iss_bucketfull); - return NULL; + if (softs->ipf_state_stats.iss_bucketlen[hv] >= + softs->ipf_state_maxbucket) { + SBUMPD(ipf_state_stats, iss_bucket_full); + return 4; } KMALLOC(is, ipstate_t *); if (is == NULL) { - ATOMIC_INCL(ips_stats.iss_nomem); - return NULL; + SBUMPD(ipf_state_stats, iss_nomem); + return 5; } bcopy((char *)&ips, (char *)is, sizeof(*is)); + is->is_flags = flags & IS_INHERITED; + is->is_rulen = fin->fin_rule; + is->is_rule = fr; + /* - * Do not do the modulous here, it is done in fr_stinsert(). + * Do not do the modulous here, it is done in ipf_state_insert(). */ if (fr != NULL) { - (void) strncpy(is->is_group, fr->fr_group, FR_GROUPLEN); + ipftq_t *tq; + + (void) strncpy(is->is_group, FR_NAME(fr, fr_group), + FR_GROUPLEN); if (fr->fr_age[0] != 0) { - is->is_tqehead[0] = fr_addtimeoutqueue(&ips_utqe, - fr->fr_age[0]); + tq = ipf_addtimeoutqueue(softc, + &softs->ipf_state_usertq, + fr->fr_age[0]); + is->is_tqehead[0] = tq; is->is_sti.tqe_flags |= TQE_RULEBASED; } if (fr->fr_age[1] != 0) { - is->is_tqehead[1] = fr_addtimeoutqueue(&ips_utqe, - fr->fr_age[1]); + tq = ipf_addtimeoutqueue(softc, + &softs->ipf_state_usertq, + fr->fr_age[1]); + is->is_tqehead[1] = tq; is->is_sti.tqe_flags |= TQE_RULEBASED; } is->is_tag = fr->fr_logtag; - - /* - * The name '-' is special for network interfaces and causes - * a NULL name to be present, always, allowing packets to - * match it, regardless of their interface. - */ - if ((fin->fin_ifp == NULL) || - (fr->fr_ifnames[out << 1][0] == '-' && - fr->fr_ifnames[out << 1][1] == '\0')) { - is->is_ifp[out << 1] = fr->fr_ifas[0]; - strncpy(is->is_ifname[out << 1], fr->fr_ifnames[0], - sizeof(fr->fr_ifnames[0])); - } else { - is->is_ifp[out << 1] = fin->fin_ifp; - COPYIFNAME(is->is_v, fin->fin_ifp, - is->is_ifname[out << 1]); - } - - is->is_ifp[(out << 1) + 1] = fr->fr_ifas[1]; - strncpy(is->is_ifname[(out << 1) + 1], fr->fr_ifnames[1], - sizeof(fr->fr_ifnames[1])); - - is->is_ifp[(1 - out) << 1] = fr->fr_ifas[2]; - strncpy(is->is_ifname[((1 - out) << 1)], fr->fr_ifnames[2], - sizeof(fr->fr_ifnames[2])); - - is->is_ifp[((1 - out) << 1) + 1] = fr->fr_ifas[3]; - strncpy(is->is_ifname[((1 - out) << 1) + 1], fr->fr_ifnames[3], - sizeof(fr->fr_ifnames[3])); - } else { - pass = fr_flags; - is->is_tag = FR_NOLOGTAG; - - if (fin->fin_ifp != NULL) { - is->is_ifp[out << 1] = fin->fin_ifp; - COPYIFNAME(is->is_v, fin->fin_ifp, - is->is_ifname[out << 1]); - } } /* - * It may seem strange to set is_ref to 2, but fr_check() will call - * fr_statederef() after calling fr_addstate() and the idea is to - * have it exist at the end of fr_check() with is_ref == 1. + * It may seem strange to set is_ref to 2, but if stsave is not NULL + * then a copy of the pointer is being stored somewhere else and in + * the end, it will expect to be able to do osmething with it. */ - is->is_ref = 2; - is->is_pass = pass; + is->is_me = stsave; + if (stsave != NULL) { + *stsave = is; + is->is_ref = 2; + } else { + is->is_ref = 1; + } is->is_pkts[0] = 0, is->is_bytes[0] = 0; is->is_pkts[1] = 0, is->is_bytes[1] = 0; is->is_pkts[2] = 0, is->is_bytes[2] = 0; is->is_pkts[3] = 0, is->is_bytes[3] = 0; if ((fin->fin_flx & FI_IGNORE) == 0) { is->is_pkts[out] = 1; + fin->fin_pktnum = 1; is->is_bytes[out] = fin->fin_plen; is->is_flx[out][0] = fin->fin_flx & FI_CMP; is->is_flx[out][0] &= ~FI_OOW; } + if (pass & FR_STLOOSE) + is->is_flags |= IS_LOOSE; + if (pass & FR_STSTRICT) is->is_flags |= IS_STRICT; if (pass & FR_STATESYNC) is->is_flags |= IS_STATESYNC; - /* - * We want to check everything that is a property of this packet, - * but we don't (automatically) care about it's fragment status as - * this may change. - */ - is->is_v = fin->fin_v; - is->is_opt[0] = fin->fin_optmsk; - is->is_optmsk[0] = 0xffffffff; - is->is_optmsk[1] = 0xffffffff; - if (is->is_v == 6) { - is->is_opt[0] &= ~0x8; - is->is_optmsk[0] &= ~0x8; - is->is_optmsk[1] &= ~0x8; - } - is->is_me = stsave; - is->is_sec = fin->fin_secmsk; - is->is_secmsk = 0xffff; - is->is_auth = fin->fin_auth; - is->is_authmsk = 0xffff; - if (flags & (SI_WILDP|SI_WILDA)) { - ATOMIC_INCL(ips_stats.iss_wild); - } - is->is_rulen = fin->fin_rule; - - if (pass & FR_LOGFIRST) is->is_pass &= ~(FR_LOGFIRST|FR_LOG); - READ_ENTER(&ipf_state); + READ_ENTER(&softc->ipf_state); - fr_stinsert(is, fin->fin_rev); + if (ipf_state_insert(softc, is, fin->fin_rev) == -1) { + RWLOCK_EXIT(&softc->ipf_state); + /* + * This is a bit more manual than it should be but + * ipf_state_del cannot be called. + */ + MUTEX_EXIT(&is->is_lock); + MUTEX_DESTROY(&is->is_lock); + if (is->is_tqehead[0] != NULL) { + if (ipf_deletetimeoutqueue(is->is_tqehead[0]) == 0) + ipf_freetimeoutqueue(softc, is->is_tqehead[0]); + is->is_tqehead[0] = NULL; + } + if (is->is_tqehead[1] != NULL) { + if (ipf_deletetimeoutqueue(is->is_tqehead[1]) == 0) + ipf_freetimeoutqueue(softc, is->is_tqehead[1]); + is->is_tqehead[1] = NULL; + } + KFREE(is); + return -1; + } + + /* + * Filling in the interface name is after the insert so that an + * event (such as add/delete) of an interface that is referenced + * by this rule will see this state entry. + */ + if (fr != NULL) { + /* + * The name '-' is special for network interfaces and causes + * a NULL name to be present, always, allowing packets to + * match it, regardless of their interface. + */ + if ((fin->fin_ifp == NULL) || + (fr->fr_ifnames[out << 1] != -1 && + fr->fr_names[fr->fr_ifnames[out << 1] + 0] == '-' && + fr->fr_names[fr->fr_ifnames[out << 1] + 1] == '\0')) { + is->is_ifp[out << 1] = fr->fr_ifas[0]; + strncpy(is->is_ifname[out << 1], + fr->fr_names + fr->fr_ifnames[0], + sizeof(fr->fr_ifnames[0])); + } else { + is->is_ifp[out << 1] = fin->fin_ifp; + COPYIFNAME(fin->fin_v, fin->fin_ifp, + is->is_ifname[out << 1]); + } + + is->is_ifp[(out << 1) + 1] = fr->fr_ifas[1]; + if (fr->fr_ifnames[1] != -1) { + strncpy(is->is_ifname[(out << 1) + 1], + fr->fr_names + fr->fr_ifnames[1], + sizeof(fr->fr_ifnames[1])); + } + + is->is_ifp[(1 - out) << 1] = fr->fr_ifas[2]; + if (fr->fr_ifnames[2] != -1) { + strncpy(is->is_ifname[((1 - out) << 1)], + fr->fr_names + fr->fr_ifnames[2], + sizeof(fr->fr_ifnames[2])); + } + + is->is_ifp[((1 - out) << 1) + 1] = fr->fr_ifas[3]; + if (fr->fr_ifnames[3] != -1) { + strncpy(is->is_ifname[((1 - out) << 1) + 1], + fr->fr_names + fr->fr_ifnames[3], + sizeof(fr->fr_ifnames[3])); + } + } else { + if (fin->fin_ifp != NULL) { + is->is_ifp[out << 1] = fin->fin_ifp; + COPYIFNAME(fin->fin_v, fin->fin_ifp, + is->is_ifname[out << 1]); + } + } if (fin->fin_p == IPPROTO_TCP) { /* @@ -1301,56 +1827,79 @@ u_int flags; * timer on it as we'll never see an error if it fails to * connect. */ - (void) fr_tcp_age(&is->is_sti, fin, ips_tqtqb, is->is_flags); - MUTEX_EXIT(&is->is_lock); -#ifdef IPFILTER_SCAN - if ((is->is_flags & SI_CLONE) == 0) - (void) ipsc_attachis(is); -#endif - } else { - MUTEX_EXIT(&is->is_lock); + (void) ipf_tcp_age(&is->is_sti, fin, softs->ipf_state_tcptq, + is->is_flags, 2); } -#ifdef IPFILTER_SYNC + MUTEX_EXIT(&is->is_lock); if ((is->is_flags & IS_STATESYNC) && ((is->is_flags & SI_CLONE) == 0)) - is->is_sync = ipfsync_new(SMC_STATE, fin, is); -#endif - if (ipstate_logging) - ipstate_log(is, ISL_NEW); + is->is_sync = ipf_sync_new(softc, SMC_STATE, fin, is); + if (softs->ipf_state_logging) + ipf_state_log(softc, is, ISL_NEW); + + RWLOCK_EXIT(&softc->ipf_state); - RWLOCK_EXIT(&ipf_state); - fin->fin_state = is; - fin->fin_rev = IP6_NEQ(&is->is_dst, &fin->fin_daddr); fin->fin_flx |= FI_STATE; if (fin->fin_flx & FI_FRAG) - (void) fr_newfrag(fin, pass ^ FR_KEEPSTATE); + (void) ipf_frag_new(softc, fin, pass); - return is; + fdp = &fr->fr_tifs[0]; + if (fdp->fd_type == FRD_DSTLIST) { + ipf_dstlist_select_node(fin, fdp->fd_ptr, NULL, + &is->is_tifs[0]); + } else { + bcopy(fdp, &is->is_tifs[0], sizeof(*fdp)); + } + + fdp = &fr->fr_tifs[1]; + if (fdp->fd_type == FRD_DSTLIST) { + ipf_dstlist_select_node(fin, fdp->fd_ptr, NULL, + &is->is_tifs[1]); + } else { + bcopy(fdp, &is->is_tifs[1], sizeof(*fdp)); + } + fin->fin_tif = &is->is_tifs[fin->fin_rev]; + + fdp = &fr->fr_dif; + if (fdp->fd_type == FRD_DSTLIST) { + ipf_dstlist_select_node(fin, fdp->fd_ptr, NULL, + &is->is_dif); + } else { + bcopy(fdp, &is->is_dif, sizeof(*fdp)); + } + fin->fin_dif = &is->is_dif; + + return 0; } /* ------------------------------------------------------------------------ */ -/* Function: fr_tcpoptions */ +/* Function: ipf_tcpoptions */ /* Returns: int - 1 == packet matches state entry, 0 == it does not, */ /* -1 == packet has bad TCP options data */ -/* Parameters: fin(I) - pointer to packet information */ +/* Parameters: softs(I) - pointer to state context structure */ +/* fin(I) - pointer to packet information */ /* tcp(I) - pointer to TCP packet header */ /* td(I) - pointer to TCP data held as part of the state */ /* */ /* Look after the TCP header for any options and deal with those that are */ /* present. Record details about those that we recogise. */ /* ------------------------------------------------------------------------ */ -static int fr_tcpoptions(fin, tcp, td) -fr_info_t *fin; -tcphdr_t *tcp; -tcpdata_t *td; +static int +ipf_tcpoptions(softs, fin, tcp, td) + ipf_state_softc_t *softs; + fr_info_t *fin; + tcphdr_t *tcp; + tcpdata_t *td; { int off, mlen, ol, i, len, retval; char buf[64], *s, opt; mb_t *m = NULL; len = (TCP_OFF(tcp) << 2); - if (fin->fin_dlen < len) + if (fin->fin_dlen < len) { + SBUMPD(ipf_state_stats, iss_tcp_toosmall); return 0; + } len -= sizeof(*tcp); off = fin->fin_plen - fin->fin_dlen + sizeof(*tcp) + fin->fin_ipoff; @@ -1422,14 +1971,19 @@ tcpdata_t *td; len -= ol; s += ol; } + if (retval == -1) { + SBUMPD(ipf_state_stats, iss_tcp_badopt); + } return retval; } /* ------------------------------------------------------------------------ */ -/* Function: fr_tcpstate */ +/* Function: ipf_state_tcp */ /* Returns: int - 1 == packet matches state entry, 0 == it does not */ -/* Parameters: fin(I) - pointer to packet information */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* softs(I) - pointer to state context structure */ +/* fin(I) - pointer to packet information */ /* tcp(I) - pointer to TCP packet header */ /* is(I) - pointer to master state structure */ /* */ @@ -1437,16 +1991,19 @@ tcpdata_t *td; /* Change timeout depending on whether new packet is a SYN-ACK returning */ /* for a SYN or a RST or FIN which indicate time to close up shop. */ /* ------------------------------------------------------------------------ */ -static int fr_tcpstate(fin, tcp, is) -fr_info_t *fin; -tcphdr_t *tcp; -ipstate_t *is; +static int +ipf_state_tcp(softc, softs, fin, tcp, is) + ipf_main_softc_t *softc; + ipf_state_softc_t *softs; + fr_info_t *fin; + tcphdr_t *tcp; + ipstate_t *is; { - int source, ret = 0, flags; tcpdata_t *fdata, *tdata; + int source, ret, flags; source = !fin->fin_rev; - if (((is->is_flags & IS_TCPFSM) != 0) && (source == 1) && + if (((is->is_flags & IS_TCPFSM) != 0) && (source == 1) && (ntohs(is->is_sport) != fin->fin_data[0])) source = 0; fdata = &is->is_tcp.ts_data[!source]; @@ -1462,34 +2019,37 @@ ipstate_t *is; if ((is->is_state[0] > IPF_TCPS_ESTABLISHED) && (is->is_state[1] > IPF_TCPS_ESTABLISHED)) { is->is_state[!source] = IPF_TCPS_CLOSED; - fr_movequeue(&is->is_sti, is->is_sti.tqe_ifq, - &ips_deletetq); + ipf_movequeue(softc->ipf_ticks, &is->is_sti, + is->is_sti.tqe_ifq, + &softs->ipf_state_deletetq); MUTEX_EXIT(&is->is_lock); + DT1(iss_tcp_closing, ipstate_t *, is); + SBUMP(ipf_state_stats.iss_tcp_closing); return 0; } } - ret = fr_tcpinwindow(fin, fdata, tdata, tcp, is->is_flags); + if (is->is_flags & IS_LOOSE) + ret = 1; + else + ret = ipf_state_tcpinwindow(fin, fdata, tdata, tcp, + is->is_flags); if (ret > 0) { -#ifdef IPFILTER_SCAN - if (is->is_flags & (IS_SC_CLIENT|IS_SC_SERVER)) { - ipsc_packet(fin, is); - if (FR_ISBLOCK(is->is_pass)) { - MUTEX_EXIT(&is->is_lock); - return 1; - } - } -#endif - /* * Nearing end of connection, start timeout. */ - ret = fr_tcp_age(&is->is_sti, fin, ips_tqtqb, is->is_flags); + ret = ipf_tcp_age(&is->is_sti, fin, softs->ipf_state_tcptq, + is->is_flags, ret); if (ret == 0) { MUTEX_EXIT(&is->is_lock); + DT2(iss_tcp_fsm, fr_info_t *, fin, ipstate_t *, is); + SBUMP(ipf_state_stats.iss_tcp_fsm); return 0; } + if (softs->ipf_state_logging > 4) + ipf_state_log(softc, is, ISL_STATECHANGE); + /* * set s0's as appropriate. Use syn-ack packet as it * contains both pieces of required information. @@ -1503,25 +2063,29 @@ ipstate_t *is; is->is_s0[source] = ntohl(tcp->th_ack); is->is_s0[!source] = ntohl(tcp->th_seq) + 1; if ((TCP_OFF(tcp) > (sizeof(tcphdr_t) >> 2))) { - if (fr_tcpoptions(fin, tcp, fdata) == -1) + if (ipf_tcpoptions(softs, fin, tcp, + fdata) == -1) fin->fin_flx |= FI_BAD; } if ((fin->fin_out != 0) && (is->is_pass & FR_NEWISN)) - fr_checknewisn(fin, is); + ipf_checknewisn(fin, is); } else if (flags == TH_SYN) { is->is_s0[source] = ntohl(tcp->th_seq) + 1; if ((TCP_OFF(tcp) > (sizeof(tcphdr_t) >> 2))) { - if (fr_tcpoptions(fin, tcp, fdata) == -1) + if (ipf_tcpoptions(softs, fin, tcp, + fdata) == -1) fin->fin_flx |= FI_BAD; } if ((fin->fin_out != 0) && (is->is_pass & FR_NEWISN)) - fr_checknewisn(fin, is); + ipf_checknewisn(fin, is); } ret = 1; } else { - fin->fin_flx |= FI_OOW; + DT2(iss_tcp_oow, fr_info_t *, fin, ipstate_t *, is); + SBUMP(ipf_state_stats.iss_tcp_oow); + ret = 0; } MUTEX_EXIT(&is->is_lock); return ret; @@ -1529,7 +2093,7 @@ ipstate_t *is; /* ------------------------------------------------------------------------ */ -/* Function: fr_checknewisn */ +/* Function: ipf_checknewisn */ /* Returns: Nil */ /* Parameters: fin(I) - pointer to packet information */ /* is(I) - pointer to master state structure */ @@ -1540,9 +2104,10 @@ ipstate_t *is; /* NOTE: This does not actually change the sequence numbers, only gets new */ /* one ready. */ /* ------------------------------------------------------------------------ */ -static void fr_checknewisn(fin, is) -fr_info_t *fin; -ipstate_t *is; +static void +ipf_checknewisn(fin, is) + fr_info_t *fin; + ipstate_t *is; { u_32_t sumd, old, new; tcphdr_t *tcp; @@ -1554,7 +2119,7 @@ ipstate_t *is; if (((i == 0) && !(is->is_flags & IS_ISNSYN)) || ((i == 1) && !(is->is_flags & IS_ISNACK))) { old = ntohl(tcp->th_seq); - new = fr_newisn(fin); + new = ipf_newisn(fin); is->is_isninc[i] = new - old; CALC_SUMD(old, new, sumd); is->is_sumd[i] = (sumd & 0xffff) + (sumd >> 16); @@ -1565,9 +2130,8 @@ ipstate_t *is; /* ------------------------------------------------------------------------ */ -/* Function: fr_tcpinwindow */ -/* Returns: int - 1 == packet inside TCP "window", 0 == not inside, */ -/* 2 == packet seq number matches next expected */ +/* Function: ipf_state_tcpinwindow */ +/* Returns: int - 1 == packet inside TCP "window", 0 == not inside. */ /* Parameters: fin(I) - pointer to packet information */ /* fdata(I) - pointer to tcp state informatio (forward) */ /* tdata(I) - pointer to tcp state informatio (reverse) */ @@ -1577,12 +2141,15 @@ ipstate_t *is; /* within the TCP data window. In a show of generosity, allow packets that */ /* are within the window space behind the current sequence # as well. */ /* ------------------------------------------------------------------------ */ -int fr_tcpinwindow(fin, fdata, tdata, tcp, flags) -fr_info_t *fin; -tcpdata_t *fdata, *tdata; -tcphdr_t *tcp; -int flags; +static int +ipf_state_tcpinwindow(fin, fdata, tdata, tcp, flags) + fr_info_t *fin; + tcpdata_t *fdata, *tdata; + tcphdr_t *tcp; + int flags; { + ipf_main_softc_t *softc = fin->fin_main_soft; + ipf_state_softc_t *softs = softc->ipf_state_soft; tcp_seq seq, ack, end; int ackskew, tcpflags; u_32_t win, maxwin; @@ -1651,12 +2218,17 @@ int flags; /* * Strict sequencing only allows in-order delivery. */ - if (seq != fdata->td_end) { - if ((flags & IS_STRICT) != 0) { + if ((flags & IS_STRICT) != 0) { + if (seq != fdata->td_end) { + DT2(iss_tcp_struct, tcpdata_t *, fdata, int, seq); + SBUMP(ipf_state_stats.iss_tcp_strict); + fin->fin_flx |= FI_OOW; return 0; } } +#define SEQ_GE(a,b) ((int)((a) - (b)) >= 0) +#define SEQ_GT(a,b) ((int)((a) - (b)) > 0) inseq = 0; if ((SEQ_GE(fdata->td_maxend, end)) && (SEQ_GE(seq, fdata->td_end - maxwin)) && @@ -1672,6 +2244,8 @@ int flags; } else if ((seq == fdata->td_maxend) && (ackskew == 0) && (fdata->td_winflags & TCP_SACK_PERMIT) && (tdata->td_winflags & TCP_SACK_PERMIT)) { + DT2(iss_sinsack, tcpdata_t *, fdata, int, seq); + SBUMP(ipf_state_stats.iss_winsack); inseq = 1; /* * Sometimes a TCP RST will be generated with only the ACK field @@ -1693,7 +2267,7 @@ int flags; * accepted, even if it appears out of sequence. */ inseq = 1; - } else + } else #endif if (!(fdata->td_winflags & (TCP_WSCALE_SEEN|TCP_WSCALE_FIRST))) { @@ -1737,12 +2311,14 @@ int flags; tdata->td_maxend = ack + win; return 1; } + SBUMP(ipf_state_stats.iss_oow); + fin->fin_flx |= FI_OOW; return 0; } /* ------------------------------------------------------------------------ */ -/* Function: fr_stclone */ +/* Function: ipf_state_clone */ /* Returns: ipstate_t* - NULL == cloning failed, */ /* else pointer to new state structure */ /* Parameters: fin(I) - pointer to packet information */ @@ -1751,27 +2327,40 @@ int flags; /* */ /* Create a "duplcate" state table entry from the master. */ /* ------------------------------------------------------------------------ */ -static ipstate_t *fr_stclone(fin, tcp, is) -fr_info_t *fin; -tcphdr_t *tcp; -ipstate_t *is; +static ipstate_t * +ipf_state_clone(fin, tcp, is) + fr_info_t *fin; + tcphdr_t *tcp; + ipstate_t *is; { + ipf_main_softc_t *softc = fin->fin_main_soft; + ipf_state_softc_t *softs = softc->ipf_state_soft; ipstate_t *clone; u_32_t send; - if (ips_num == fr_statemax) { - ATOMIC_INCL(ips_stats.iss_max); - fr_state_doflush = 1; + if (softs->ipf_state_stats.iss_active == softs->ipf_state_max) { + SBUMPD(ipf_state_stats, iss_max); + softs->ipf_state_doflush = 1; return NULL; } KMALLOC(clone, ipstate_t *); - if (clone == NULL) + if (clone == NULL) { + SBUMPD(ipf_state_stats, iss_clone_nomem); return NULL; + } bcopy((char *)is, (char *)clone, sizeof(*clone)); MUTEX_NUKE(&clone->is_lock); + /* + * It has not yet been placed on any timeout queue, so make sure + * all of that data is zero'd out. + */ + clone->is_sti.tqe_pnext = NULL; + clone->is_sti.tqe_next = NULL; + clone->is_sti.tqe_ifq = NULL; + clone->is_sti.tqe_parent = clone; - clone->is_die = ONE_DAY + fr_ticks; + clone->is_die = ONE_DAY + softc->ipf_ticks; clone->is_state[0] = 0; clone->is_state[1] = 0; send = ntohl(tcp->th_seq) + fin->fin_dlen - (TCP_OFF(tcp) << 2) + @@ -1798,49 +2387,61 @@ ipstate_t *is; clone->is_flags &= ~SI_CLONE; clone->is_flags |= SI_CLONED; - fr_stinsert(clone, fin->fin_rev); - clone->is_ref = 2; + if (ipf_state_insert(softc, clone, fin->fin_rev) == -1) { + KFREE(clone); + return NULL; + } + + clone->is_ref = 1; if (clone->is_p == IPPROTO_TCP) { - (void) fr_tcp_age(&clone->is_sti, fin, ips_tqtqb, - clone->is_flags); + (void) ipf_tcp_age(&clone->is_sti, fin, softs->ipf_state_tcptq, + clone->is_flags, 2); } MUTEX_EXIT(&clone->is_lock); -#ifdef IPFILTER_SCAN - (void) ipsc_attachis(is); -#endif -#ifdef IPFILTER_SYNC if (is->is_flags & IS_STATESYNC) - clone->is_sync = ipfsync_new(SMC_STATE, fin, clone); -#endif + clone->is_sync = ipf_sync_new(softc, SMC_STATE, fin, clone); + DT2(iss_clone, ipstate_t *, is, ipstate_t *, clone); + SBUMP(ipf_state_stats.iss_cloned); return clone; } /* ------------------------------------------------------------------------ */ -/* Function: fr_matchsrcdst */ +/* Function: ipf_matchsrcdst */ /* Returns: Nil */ -/* Parameters: fin(I) - pointer to packet information */ -/* is(I) - pointer to state structure */ -/* src(I) - pointer to source address */ -/* dst(I) - pointer to destination address */ -/* tcp(I) - pointer to TCP/UDP header */ +/* Parameters: fin(I) - pointer to packet information */ +/* is(I) - pointer to state structure */ +/* src(I) - pointer to source address */ +/* dst(I) - pointer to destination address */ +/* tcp(I) - pointer to TCP/UDP header */ +/* cmask(I) - mask of FI_* bits to check */ /* */ /* Match a state table entry against an IP packet. The logic below is that */ /* ret gets set to one if the match succeeds, else remains 0. If it is */ /* still 0 after the test. no match. */ /* ------------------------------------------------------------------------ */ -static ipstate_t *fr_matchsrcdst(fin, is, src, dst, tcp, cmask) -fr_info_t *fin; -ipstate_t *is; -i6addr_t *src, *dst; -tcphdr_t *tcp; -u_32_t cmask; +static ipstate_t * +ipf_matchsrcdst(fin, is, src, dst, tcp, cmask) + fr_info_t *fin; + ipstate_t *is; + i6addr_t *src, *dst; + tcphdr_t *tcp; + u_32_t cmask; { + ipf_main_softc_t *softc = fin->fin_main_soft; + ipf_state_softc_t *softs = softc->ipf_state_soft; int ret = 0, rev, out, flags, flx = 0, idx; u_short sp, dp; u_32_t cflx; void *ifp; + /* + * If a connection is about to be deleted, no packets + * are allowed to match it. + */ + if (is->is_sti.tqe_ifq == &softs->ipf_state_deletetq) + return NULL; + rev = IP6_NEQ(&is->is_dst, dst); ifp = fin->fin_ifp; out = fin->fin_out; @@ -1872,8 +2473,12 @@ u_32_t cmask; *is->is_ifname[idx] == '*'))) ret = 1; - if (ret == 0) + if (ret == 0) { + DT2(iss_lookup_badifp, fr_info_t *, fin, ipstate_t *, is); + SBUMP(ipf_state_stats.iss_lookup_badifp); + /* TRACE is, out, rev, idx */ return NULL; + } ret = 0; /* @@ -1883,7 +2488,8 @@ u_32_t cmask; if ((IP6_EQ(&is->is_dst, dst) || (flags & SI_W_DADDR)) && (IP6_EQ(&is->is_src, src) || (flags & SI_W_SADDR))) { if (tcp) { - if ((sp == is->is_sport || flags & SI_W_SPORT)&& + if ((sp == is->is_sport || flags & SI_W_SPORT) + && (dp == is->is_dport || flags & SI_W_DPORT)) ret = 1; } else { @@ -1894,7 +2500,8 @@ u_32_t cmask; if ((IP6_EQ(&is->is_dst, src) || (flags & SI_W_DADDR)) && (IP6_EQ(&is->is_src, dst) || (flags & SI_W_SADDR))) { if (tcp) { - if ((dp == is->is_sport || flags & SI_W_SPORT)&& + if ((dp == is->is_sport || flags & SI_W_SPORT) + && (sp == is->is_dport || flags & SI_W_DPORT)) ret = 1; } else { @@ -1903,8 +2510,12 @@ u_32_t cmask; } } - if (ret == 0) + if (ret == 0) { + SBUMP(ipf_state_stats.iss_lookup_badport); + DT2(iss_lookup_badport, fr_info_t *, fin, ipstate_t *, is); + /* TRACE rev, is, sp, dp, src, dst */ return NULL; + } /* * Whether or not this should be here, is questionable, but the aim @@ -1925,55 +2536,27 @@ u_32_t cmask; if ((flags & SI_W_SADDR) != 0) { if (rev == 0) { -#ifdef USE_INET6 - if (is->is_v == 6 && - IN6_IS_ADDR_MULTICAST(&fi->fi_src.in6)) - /*EMPTY*/; - else -#endif - { - is->is_src = fi->fi_src; - is->is_flags &= ~SI_W_SADDR; - } + is->is_src = fi->fi_src; + is->is_flags &= ~SI_W_SADDR; } else { -#ifdef USE_INET6 - if (is->is_v == 6 && - IN6_IS_ADDR_MULTICAST(&fi->fi_dst.in6)) - /*EMPTY*/; - else -#endif - { + if (!(fin->fin_flx & (FI_MULTICAST|FI_MBCAST))){ is->is_src = fi->fi_dst; is->is_flags &= ~SI_W_SADDR; } } } else if ((flags & SI_W_DADDR) != 0) { if (rev == 0) { -#ifdef USE_INET6 - if (is->is_v == 6 && - IN6_IS_ADDR_MULTICAST(&fi->fi_dst.in6)) - /*EMPTY*/; - else -#endif - { + if (!(fin->fin_flx & (FI_MULTICAST|FI_MBCAST))){ is->is_dst = fi->fi_dst; is->is_flags &= ~SI_W_DADDR; } } else { -#ifdef USE_INET6 - if (is->is_v == 6 && - IN6_IS_ADDR_MULTICAST(&fi->fi_src.in6)) - /*EMPTY*/; - else -#endif - { - is->is_dst = fi->fi_src; - is->is_flags &= ~SI_W_DADDR; - } + is->is_dst = fi->fi_src; + is->is_flags &= ~SI_W_DADDR; } } if ((is->is_flags & (SI_WILDA|SI_WILDP)) == 0) { - ATOMIC_DECL(ips_stats.iss_wild); + ATOMIC_DECL(softs->ipf_state_stats.iss_wild); } } @@ -1986,29 +2569,31 @@ u_32_t cmask; if ((cflx && (flx != (cflx & cmask))) || ((fin->fin_optmsk & is->is_optmsk[rev]) != is->is_opt[rev]) || ((fin->fin_secmsk & is->is_secmsk) != is->is_sec) || - ((fin->fin_auth & is->is_authmsk) != is->is_auth)) + ((fin->fin_auth & is->is_authmsk) != is->is_auth)) { + SBUMPD(ipf_state_stats, iss_miss_mask); return NULL; + } + + if ((fin->fin_flx & FI_IGNORE) != 0) { + fin->fin_rev = rev; + return is; + } /* * Only one of the source or destination port can be flagged as a * wildcard. When filling it in, fill in a copy of the matched entry * if it has the cloning flag set. */ - if ((fin->fin_flx & FI_IGNORE) != 0) { - fin->fin_rev = rev; - return is; - } - if ((flags & (SI_W_SPORT|SI_W_DPORT))) { if ((flags & SI_CLONE) != 0) { ipstate_t *clone; - clone = fr_stclone(fin, tcp, is); + clone = ipf_state_clone(fin, tcp, is); if (clone == NULL) return NULL; is = clone; } else { - ATOMIC_DECL(ips_stats.iss_wild); + ATOMIC_DECL(softs->ipf_state_stats.iss_wild); } if ((flags & SI_W_SPORT) != 0) { @@ -2031,18 +2616,21 @@ u_32_t cmask; is->is_maxdend = is->is_dend + 1; } is->is_flags &= ~(SI_W_SPORT|SI_W_DPORT); - if ((flags & SI_CLONED) && ipstate_logging) - ipstate_log(is, ISL_CLONE); + if ((flags & SI_CLONED) && softs->ipf_state_logging) + ipf_state_log(softc, is, ISL_CLONE); } ret = -1; if (is->is_flx[out][rev] == 0) { is->is_flx[out][rev] = flx; - is->is_opt[rev] = fin->fin_optmsk; - if (is->is_v == 6) { - is->is_opt[rev] &= ~0x8; - is->is_optmsk[rev] &= ~0x8; + if (rev == 1 && is->is_optmsk[1] == 0) { + is->is_opt[1] = fin->fin_optmsk; + is->is_optmsk[1] = 0xffffffff; + if (is->is_v == 6) { + is->is_opt[1] &= ~0x8; + is->is_optmsk[1] &= ~0x8; + } } } @@ -2053,7 +2641,7 @@ u_32_t cmask; if (is->is_ifp[idx] == NULL && (*is->is_ifname[idx] == '\0' || *is->is_ifname[idx] == '*')) { is->is_ifp[idx] = ifp; - COPYIFNAME(is->is_v, ifp, is->is_ifname[idx]); + COPYIFNAME(fin->fin_v, ifp, is->is_ifname[idx]); } fin->fin_rev = rev; return is; @@ -2061,7 +2649,7 @@ u_32_t cmask; /* ------------------------------------------------------------------------ */ -/* Function: fr_checkicmpmatchingstate */ +/* Function: ipf_checkicmpmatchingstate */ /* Returns: Nil */ /* Parameters: fin(I) - pointer to packet information */ /* */ @@ -2071,13 +2659,13 @@ u_32_t cmask; /* If we return NULL then no lock on ipf_state is held. */ /* If we return non-null then a read-lock on ipf_state is held. */ /* ------------------------------------------------------------------------ */ -static ipstate_t *fr_checkicmpmatchingstate(fin) -fr_info_t *fin; +static ipstate_t * +ipf_checkicmpmatchingstate(fin) + fr_info_t *fin; { + ipf_main_softc_t *softc = fin->fin_main_soft; + ipf_state_softc_t *softs = softc->ipf_state_soft; ipstate_t *is, **isp; - u_short sport, dport; - u_char pr; - int backward, i, oi; i6addr_t dst, src; struct icmp *ic; u_short savelen; @@ -2085,6 +2673,7 @@ fr_info_t *fin; fr_info_t ofin; tcphdr_t *tcp; int type, len; + u_char pr; ip_t *oip; u_int hv; @@ -2096,8 +2685,10 @@ fr_info_t *fin; */ if ((fin->fin_v != 4) || (fin->fin_hlen != sizeof(ip_t)) || (fin->fin_plen < ICMPERR_MINPKTLEN) || - !(fin->fin_flx & FI_ICMPERR)) + !(fin->fin_flx & FI_ICMPERR)) { + SBUMPD(ipf_state_stats, iss_icmp_bad); return NULL; + } ic = fin->fin_dp; type = ic->icmp_type; @@ -2106,15 +2697,20 @@ fr_info_t *fin; * Check if the at least the old IP header (with options) and * 8 bytes of payload is present. */ - if (fin->fin_plen < ICMPERR_MAXPKTLEN + ((IP_HL(oip) - 5) << 2)) + if (fin->fin_plen < ICMPERR_MAXPKTLEN + ((IP_HL(oip) - 5) << 2)) { + SBUMPDX(ipf_state_stats, iss_icmp_short, iss_icmp_short_1); return NULL; + } /* * Sanity Checks. */ len = fin->fin_dlen - ICMPERR_ICMPHLEN; - if ((len <= 0) || ((IP_HL(oip) << 2) > len)) + if ((len <= 0) || ((IP_HL(oip) << 2) > len)) { + DT2(iss_icmp_len, fr_info_t *, fin, struct ip*, oip); + SBUMPDX(ipf_state_stats, iss_icmp_short, iss_icmp_short_1); return NULL; + } /* * Is the buffer big enough for all of it ? It's the size of the IP @@ -2122,7 +2718,7 @@ fr_info_t *fin; * may be too big to be in this buffer but not so big that it's * outside the ICMP packet, leading to TCP deref's causing problems. * This is possible because we don't know how big oip_hl is when we - * do the pullup early in fr_check() and thus can't guarantee it is + * do the pullup early in ipf_check() and thus can't guarantee it is * all here now. */ #ifdef _KERNEL @@ -2131,14 +2727,19 @@ fr_info_t *fin; m = fin->fin_m; # if defined(MENTAT) - if ((char *)oip + len > (char *)m->b_wptr) + if ((char *)oip + len > (char *)m->b_wptr) { + SBUMPDX(ipf_state_stats, iss_icmp_short, iss_icmp_short_2); return NULL; + } # else - if ((char *)oip + len > (char *)fin->fin_ip + m->m_len) + if ((char *)oip + len > (char *)fin->fin_ip + m->m_len) { + SBUMPDX(ipf_state_stats, iss_icmp_short, iss_icmp_short_3); return NULL; + } # endif } #endif + bcopy((char *)fin, (char *)&ofin, sizeof(*fin)); /* @@ -2154,37 +2755,42 @@ fr_info_t *fin; * matchsrcdst note that not all fields are encessary * but this is the cleanest way. Note further we fill * in fin_mp such that if someone uses it we'll get - * a kernel panic. fr_matchsrcdst does not use this. + * a kernel panic. ipf_matchsrcdst does not use this. * * watch out here, as ip is in host order and oip in network * order. Any change we make must be undone afterwards, like - * oip->ip_off - it is still in network byte order so fix it. + * oip->ip_len. */ savelen = oip->ip_len; - oip->ip_len = len; - oip->ip_off = ntohs(oip->ip_off); + oip->ip_len = htons(len); ofin.fin_flx = FI_NOCKSUM; ofin.fin_v = 4; ofin.fin_ip = oip; ofin.fin_m = NULL; /* if dereferenced, panic XXX */ ofin.fin_mp = NULL; /* if dereferenced, panic XXX */ - (void) fr_makefrip(IP_HL(oip) << 2, oip, &ofin); + (void) ipf_makefrip(IP_HL(oip) << 2, oip, &ofin); ofin.fin_ifp = fin->fin_ifp; ofin.fin_out = !fin->fin_out; + + hv = (pr = oip->ip_p); + src.in4 = oip->ip_src; + hv += src.in4.s_addr; + dst.in4 = oip->ip_dst; + hv += dst.in4.s_addr; + /* - * Reset the short and bad flag here because in fr_matchsrcdst() + * Reset the short and bad flag here because in ipf_matchsrcdst() * the flags for the current packet (fin_flx) are compared against * those for the existing session. */ ofin.fin_flx &= ~(FI_BAD|FI_SHORT); /* - * Put old values of ip_len and ip_off back as we don't know - * if we have to forward the packet (or process it again. + * Put old values of ip_len back as we don't know + * if we have to forward the packet or process it again. */ oip->ip_len = savelen; - oip->ip_off = htons(oip->ip_off); switch (oip->ip_p) { @@ -2196,75 +2802,52 @@ fr_info_t *fin; * XXX theoretically ICMP_ECHOREP and the other reply's are * ICMP query's as well, but adding them here seems strange XXX */ - if ((ofin.fin_flx & FI_ICMPERR) != 0) + if ((ofin.fin_flx & FI_ICMPERR) != 0) { + DT1(iss_icmp_icmperr, fr_info_t *, &ofin); + SBUMP(ipf_state_stats.iss_icmp_icmperr); return NULL; + } /* * perform a lookup of the ICMP packet in the state table */ icmp = (icmphdr_t *)((char *)oip + (IP_HL(oip) << 2)); - hv = (pr = oip->ip_p); - src.in4 = oip->ip_src; - hv += src.in4.s_addr; - dst.in4 = oip->ip_dst; - hv += dst.in4.s_addr; hv += icmp->icmp_id; hv = DOUBLE_HASH(hv); - READ_ENTER(&ipf_state); - for (isp = &ips_table[hv]; ((is = *isp) != NULL); ) { + READ_ENTER(&softc->ipf_state); + for (isp = &softs->ipf_state_table[hv]; + ((is = *isp) != NULL); ) { isp = &is->is_hnext; if ((is->is_p != pr) || (is->is_v != 4)) continue; if (is->is_pass & FR_NOICMPERR) continue; - is = fr_matchsrcdst(&ofin, is, &src, &dst, + + is = ipf_matchsrcdst(&ofin, is, &src, &dst, NULL, FI_ICMPCMP); - if (is != NULL) { - /* - * i : the index of this packet (the icmp - * unreachable) - * oi : the index of the original packet found - * in the icmp header (i.e. the packet - * causing this icmp) - * backward : original packet was backward - * compared to the state - */ - backward = IP6_NEQ(&is->is_src, &src); - fin->fin_rev = !backward; - i = (!backward << 1) + fin->fin_out; - oi = (backward << 1) + ofin.fin_out; - if (is->is_icmppkts[i] > is->is_pkts[oi]) - continue; - ips_stats.iss_hits++; - is->is_icmppkts[i]++; + if ((is != NULL) && !ipf_allowstateicmp(fin, is, &src)) return is; - } } - RWLOCK_EXIT(&ipf_state); + RWLOCK_EXIT(&softc->ipf_state); + SBUMPDX(ipf_state_stats, iss_icmp_miss, iss_icmp_miss_1); return NULL; case IPPROTO_TCP : case IPPROTO_UDP : break; default : + SBUMPDX(ipf_state_stats, iss_icmp_miss, iss_icmp_miss_2); return NULL; } tcp = (tcphdr_t *)((char *)oip + (IP_HL(oip) << 2)); - dport = tcp->th_dport; - sport = tcp->th_sport; - hv = (pr = oip->ip_p); - src.in4 = oip->ip_src; - hv += src.in4.s_addr; - dst.in4 = oip->ip_dst; - hv += dst.in4.s_addr; - hv += dport; - hv += sport; + hv += tcp->th_dport;; + hv += tcp->th_sport;; hv = DOUBLE_HASH(hv); - READ_ENTER(&ipf_state); - for (isp = &ips_table[hv]; ((is = *isp) != NULL); ) { + READ_ENTER(&softc->ipf_state); + for (isp = &softs->ipf_state_table[hv]; ((is = *isp) != NULL); ) { isp = &is->is_hnext; /* * Only allow this icmp though if the @@ -2276,40 +2859,93 @@ fr_info_t *fin; * short flag is set. */ if ((is->is_p == pr) && (is->is_v == 4) && - (is = fr_matchsrcdst(&ofin, is, &src, &dst, - tcp, FI_ICMPCMP))) { - /* - * i : the index of this packet (the icmp unreachable) - * oi : the index of the original packet found in the - * icmp header (i.e. the packet causing this icmp) - * backward : original packet was backward compared to - * the state - */ - backward = IP6_NEQ(&is->is_src, &src); - fin->fin_rev = !backward; - i = (!backward << 1) + fin->fin_out; - oi = (backward << 1) + ofin.fin_out; - - if (((is->is_pass & FR_NOICMPERR) != 0) || - (is->is_icmppkts[i] > is->is_pkts[oi])) - break; - ips_stats.iss_hits++; - is->is_icmppkts[i]++; - /* - * we deliberately do not touch the timeouts - * for the accompanying state table entry. - * It remains to be seen if that is correct. XXX - */ - return is; + (is = ipf_matchsrcdst(&ofin, is, &src, &dst, + tcp, FI_ICMPCMP))) { + if (ipf_allowstateicmp(fin, is, &src) == 0) + return is; } } - RWLOCK_EXIT(&ipf_state); + RWLOCK_EXIT(&softc->ipf_state); + SBUMPDX(ipf_state_stats, iss_icmp_miss, iss_icmp_miss_3); return NULL; } /* ------------------------------------------------------------------------ */ -/* Function: fr_ipsmove */ +/* Function: ipf_allowstateicmp */ +/* Returns: int - 1 = packet denied, 0 = packet allowed */ +/* Parameters: fin(I) - pointer to packet information */ +/* is(I) - pointer to state table entry */ +/* src(I) - source address to check permission for */ +/* */ +/* For an ICMP packet that has so far matched a state table entry, check if */ +/* there are any further refinements that might mean we want to block this */ +/* packet. This code isn't specific to either IPv4 or IPv6. */ +/* ------------------------------------------------------------------------ */ +static int +ipf_allowstateicmp(fin, is, src) + fr_info_t *fin; + ipstate_t *is; + i6addr_t *src; +{ + ipf_main_softc_t *softc = fin->fin_main_soft; + ipf_state_softc_t *softs = softc->ipf_state_soft; + frentry_t *savefr; + frentry_t *fr; + u_32_t ipass; + int backward; + int oi; + int i; + + fr = is->is_rule; + if (fr != NULL && fr->fr_icmpgrp != NULL) { + savefr = fin->fin_fr; + fin->fin_fr = fr->fr_icmpgrp->fg_start; + + ipass = ipf_scanlist(fin, softc->ipf_pass); + fin->fin_fr = savefr; + if (FR_ISBLOCK(ipass)) { + SBUMPD(ipf_state_stats, iss_icmp_headblock); + return 1; + } + } + + /* + * i : the index of this packet (the icmp unreachable) + * oi : the index of the original packet found in the + * icmp header (i.e. the packet causing this icmp) + * backward : original packet was backward compared to + * the state + */ + backward = IP6_NEQ(&is->is_src, src); + fin->fin_rev = !backward; + i = (!backward << 1) + fin->fin_out; + oi = (backward << 1) + !fin->fin_out; + + if (is->is_pass & FR_NOICMPERR) { + SBUMPD(ipf_state_stats, iss_icmp_banned); + return 1; + } + if (is->is_icmppkts[i] > is->is_pkts[oi]) { + SBUMPD(ipf_state_stats, iss_icmp_toomany); + return 1; + } + + DT2(iss_icmp_hits, fr_info_t *, fin, ipstate_t *, is); + SBUMP(ipf_state_stats.iss_icmp_hits); + is->is_icmppkts[i]++; + + /* + * we deliberately do not touch the timeouts + * for the accompanying state table entry. + * It remains to be seen if that is correct. XXX + */ + return 0; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_ipsmove */ /* Returns: Nil */ /* Parameters: is(I) - pointer to state table entry */ /* hv(I) - new hash value for state table entry */ @@ -2317,14 +2953,19 @@ fr_info_t *fin; /* */ /* Move a state entry from one position in the hash table to another. */ /* ------------------------------------------------------------------------ */ -static void fr_ipsmove(is, hv) -ipstate_t *is; -u_int hv; +static void +ipf_ipsmove(softs, is, hv) + ipf_state_softc_t *softs; + ipstate_t *is; + u_int hv; { ipstate_t **isp; u_int hvm; hvm = is->is_hv; + + /* TRACE is, is_hv, hvm */ + /* * Remove the hash from the old location... */ @@ -2332,21 +2973,24 @@ u_int hv; if (is->is_hnext) is->is_hnext->is_phnext = isp; *isp = is->is_hnext; - if (ips_table[hvm] == NULL) - ips_stats.iss_inuse--; - ips_stats.iss_bucketlen[hvm]--; + if (softs->ipf_state_table[hvm] == NULL) + softs->ipf_state_stats.iss_inuse--; + softs->ipf_state_stats.iss_bucketlen[hvm]--; /* * ...and put the hash in the new one. */ hvm = DOUBLE_HASH(hv); is->is_hv = hvm; - isp = &ips_table[hvm]; + + /* TRACE is, hv, is_hv, hvm */ + + isp = &softs->ipf_state_table[hvm]; if (*isp) (*isp)->is_phnext = &is->is_hnext; else - ips_stats.iss_inuse++; - ips_stats.iss_bucketlen[hvm]++; + softs->ipf_state_stats.iss_inuse++; + softs->ipf_state_stats.iss_bucketlen[hvm]++; is->is_phnext = isp; is->is_hnext = *isp; *isp = is; @@ -2354,23 +2998,28 @@ u_int hv; /* ------------------------------------------------------------------------ */ -/* Function: fr_stlookup */ +/* Function: ipf_state_lookup */ /* Returns: ipstate_t* - NULL == no matching state found, */ /* else pointer to state information is returned */ -/* Parameters: fin(I) - pointer to packet information */ -/* tcp(I) - pointer to TCP/UDP header. */ +/* Parameters: fin(I) - pointer to packet information */ +/* tcp(I) - pointer to TCP/UDP header. */ +/* ifqp(O) - pointer for storing tailq timeout */ /* */ /* Search the state table for a matching entry to the packet described by */ -/* the contents of *fin. */ +/* the contents of *fin. For certain protocols, when a match is found the */ +/* timeout queue is also selected and stored in ifpq if it is non-NULL. */ /* */ /* If we return NULL then no lock on ipf_state is held. */ /* If we return non-null then a read-lock on ipf_state is held. */ /* ------------------------------------------------------------------------ */ -ipstate_t *fr_stlookup(fin, tcp, ifqp) -fr_info_t *fin; -tcphdr_t *tcp; -ipftq_t **ifqp; +ipstate_t * +ipf_state_lookup(fin, tcp, ifqp) + fr_info_t *fin; + tcphdr_t *tcp; + ipftq_t **ifqp; { + ipf_main_softc_t *softc = fin->fin_main_soft; + ipf_state_softc_t *softs = softc->ipf_state_soft; u_int hv, hvm, pr, v, tryagain; ipstate_t *is, **isp; u_short dport, sport; @@ -2415,6 +3064,8 @@ ipftq_t **ifqp; } } + /* TRACE fin_saddr, fin_daddr, hv */ + /* * Search the hash table for matching packet header info. */ @@ -2429,28 +3080,22 @@ ipftq_t **ifqp; hv += ic->icmp_id; } } - READ_ENTER(&ipf_state); + READ_ENTER(&softc->ipf_state); icmp6again: hvm = DOUBLE_HASH(hv); - for (isp = &ips_table[hvm]; ((is = *isp) != NULL); ) { + for (isp = &softs->ipf_state_table[hvm]; + ((is = *isp) != NULL); ) { isp = &is->is_hnext; - /* - * If a connection is about to be deleted, no packets - * are allowed to match it. - */ - if (is->is_sti.tqe_ifq == &ips_deletetq) - continue; - if ((is->is_p != pr) || (is->is_v != v)) continue; - is = fr_matchsrcdst(fin, is, &src, &dst, NULL, FI_CMP); + is = ipf_matchsrcdst(fin, is, &src, &dst, NULL, FI_CMP); if (is != NULL && - fr_matchicmpqueryreply(v, &is->is_icmp, + ipf_matchicmpqueryreply(v, &is->is_icmp, ic, fin->fin_rev)) { if (fin->fin_rev) - ifq = &ips_icmpacktq; + ifq = &softs->ipf_state_icmpacktq; else - ifq = &ips_icmptq; + ifq = &softs->ipf_state_icmptq; break; } } @@ -2461,12 +3106,12 @@ icmp6again: hv += fin->fin_fi.fi_src.i6[1]; hv += fin->fin_fi.fi_src.i6[2]; hv += fin->fin_fi.fi_src.i6[3]; - fr_ipsmove(is, hv); - MUTEX_DOWNGRADE(&ipf_state); + ipf_ipsmove(softs, is, hv); + MUTEX_DOWNGRADE(&softc->ipf_state); } break; } - RWLOCK_EXIT(&ipf_state); + RWLOCK_EXIT(&softc->ipf_state); /* * No matching icmp state entry. Perhaps this is a @@ -2478,18 +3123,19 @@ icmp6again: * advantage of this requires some significant code changes * to handle the specific types where that is the case. */ - if ((ips_stats.iss_wild != 0) && (v == 6) && (tryagain == 0) && - !IN6_IS_ADDR_MULTICAST(&fin->fin_fi.fi_src.in6)) { + if ((softs->ipf_state_stats.iss_wild != 0) && + ((fin->fin_flx & FI_NOWILD) == 0) && + (v == 6) && (tryagain == 0)) { hv -= fin->fin_fi.fi_src.i6[0]; hv -= fin->fin_fi.fi_src.i6[1]; hv -= fin->fin_fi.fi_src.i6[2]; hv -= fin->fin_fi.fi_src.i6[3]; tryagain = 1; - WRITE_ENTER(&ipf_state); + WRITE_ENTER(&softc->ipf_state); goto icmp6again; } - is = fr_checkicmp6matchingstate(fin); + is = ipf_checkicmp6matchingstate(fin); if (is != NULL) return is; break; @@ -2500,25 +3146,26 @@ icmp6again: hv += ic->icmp_id; } hv = DOUBLE_HASH(hv); - READ_ENTER(&ipf_state); - for (isp = &ips_table[hv]; ((is = *isp) != NULL); ) { + READ_ENTER(&softc->ipf_state); + for (isp = &softs->ipf_state_table[hv]; + ((is = *isp) != NULL); ) { isp = &is->is_hnext; if ((is->is_p != pr) || (is->is_v != v)) continue; - is = fr_matchsrcdst(fin, is, &src, &dst, NULL, FI_CMP); + is = ipf_matchsrcdst(fin, is, &src, &dst, NULL, FI_CMP); if ((is != NULL) && (ic->icmp_id == is->is_icmp.ici_id) && - fr_matchicmpqueryreply(v, &is->is_icmp, + ipf_matchicmpqueryreply(v, &is->is_icmp, ic, fin->fin_rev)) { if (fin->fin_rev) - ifq = &ips_icmpacktq; + ifq = &softs->ipf_state_icmpacktq; else - ifq = &ips_icmptq; + ifq = &softs->ipf_state_icmptq; break; } } if (is == NULL) { - RWLOCK_EXIT(&ipf_state); + RWLOCK_EXIT(&softc->ipf_state); } break; @@ -2531,18 +3178,23 @@ icmp6again: hv += dport; oow = 0; tryagain = 0; - READ_ENTER(&ipf_state); + READ_ENTER(&softc->ipf_state); retry_tcpudp: hvm = DOUBLE_HASH(hv); - for (isp = &ips_table[hvm]; ((is = *isp) != NULL); ) { + + /* TRACE hv, hvm */ + + for (isp = &softs->ipf_state_table[hvm]; + ((is = *isp) != NULL); ) { isp = &is->is_hnext; if ((is->is_p != pr) || (is->is_v != v)) continue; fin->fin_flx &= ~FI_OOW; - is = fr_matchsrcdst(fin, is, &src, &dst, tcp, FI_CMP); + is = ipf_matchsrcdst(fin, is, &src, &dst, tcp, FI_CMP); if (is != NULL) { if (pr == IPPROTO_TCP) { - if (!fr_tcpstate(fin, tcp, is)) { + if (!ipf_state_tcp(softc, softs, fin, + tcp, is)) { oow |= fin->fin_flx & FI_OOW; continue; } @@ -2555,14 +3207,15 @@ retry_tcpudp: !(is->is_flags & (SI_CLONE|SI_WILDP|SI_WILDA))) { hv += dport; hv += sport; - fr_ipsmove(is, hv); - MUTEX_DOWNGRADE(&ipf_state); + ipf_ipsmove(softs, is, hv); + MUTEX_DOWNGRADE(&softc->ipf_state); } break; } - RWLOCK_EXIT(&ipf_state); + RWLOCK_EXIT(&softc->ipf_state); - if (ips_stats.iss_wild) { + if ((softs->ipf_state_stats.iss_wild != 0) && + ((fin->fin_flx & FI_NOWILD) == 0)) { if (tryagain == 0) { hv -= dport; hv -= sport; @@ -2584,7 +3237,7 @@ retry_tcpudp: } tryagain++; if (tryagain <= 2) { - WRITE_ENTER(&ipf_state); + WRITE_ENTER(&softc->ipf_state); goto retry_tcpudp; } } @@ -2602,19 +3255,20 @@ retry_tcpudp: default : ifqp = NULL; hvm = DOUBLE_HASH(hv); - READ_ENTER(&ipf_state); - for (isp = &ips_table[hvm]; ((is = *isp) != NULL); ) { + READ_ENTER(&softc->ipf_state); + for (isp = &softs->ipf_state_table[hvm]; + ((is = *isp) != NULL); ) { isp = &is->is_hnext; if ((is->is_p != pr) || (is->is_v != v)) continue; - is = fr_matchsrcdst(fin, is, &src, &dst, NULL, FI_CMP); + is = ipf_matchsrcdst(fin, is, &src, &dst, NULL, FI_CMP); if (is != NULL) { - ifq = &ips_iptq; + ifq = &softs->ipf_state_iptq; break; } } if (is == NULL) { - RWLOCK_EXIT(&ipf_state); + RWLOCK_EXIT(&softc->ipf_state); } break; } @@ -2625,90 +3279,45 @@ retry_tcpudp: ifq = is->is_tqehead[fin->fin_rev]; if (ifq != NULL && ifqp != NULL) *ifqp = ifq; + } else { + SBUMP(ipf_state_stats.iss_lookup_miss); } return is; } /* ------------------------------------------------------------------------ */ -/* Function: fr_updatestate */ -/* Returns: Nil */ -/* Parameters: fin(I) - pointer to packet information */ -/* is(I) - pointer to state table entry */ -/* Read Locks: ipf_state */ -/* */ -/* Updates packet and byte counters for a newly received packet. Seeds the */ -/* fragment cache with a new entry as required. */ -/* ------------------------------------------------------------------------ */ -void fr_updatestate(fin, is, ifq) -fr_info_t *fin; -ipstate_t *is; -ipftq_t *ifq; -{ - ipftqent_t *tqe; - int i, pass; - - i = (fin->fin_rev << 1) + fin->fin_out; - - /* - * For TCP packets, ifq == NULL. For all others, check if this new - * queue is different to the last one it was on and move it if so. - */ - tqe = &is->is_sti; - MUTEX_ENTER(&is->is_lock); - if ((tqe->tqe_flags & TQE_RULEBASED) != 0) - ifq = is->is_tqehead[fin->fin_rev]; - - if (ifq != NULL) - fr_movequeue(tqe, tqe->tqe_ifq, ifq); - - is->is_pkts[i]++; - is->is_bytes[i] += fin->fin_plen; - MUTEX_EXIT(&is->is_lock); - -#ifdef IPFILTER_SYNC - if (is->is_flags & IS_STATESYNC) - ipfsync_update(SMC_STATE, fin, is->is_sync); -#endif - - ATOMIC_INCL(ips_stats.iss_hits); - - fin->fin_fr = is->is_rule; - - /* - * If this packet is a fragment and the rule says to track fragments, - * then create a new fragment cache entry. - */ - pass = is->is_pass; - if ((fin->fin_flx & FI_FRAG) && FR_ISPASS(pass)) - (void) fr_newfrag(fin, pass ^ FR_KEEPSTATE); -} - - -/* ------------------------------------------------------------------------ */ -/* Function: fr_checkstate */ +/* Function: ipf_state_check */ /* Returns: frentry_t* - NULL == search failed, */ /* else pointer to rule for matching state */ -/* Parameters: ifp(I) - pointer to interface */ +/* Parameters: fin(I) - pointer to packet information */ /* passp(I) - pointer to filtering result flags */ /* */ /* Check if a packet is associated with an entry in the state table. */ /* ------------------------------------------------------------------------ */ -frentry_t *fr_checkstate(fin, passp) -fr_info_t *fin; -u_32_t *passp; +frentry_t * +ipf_state_check(fin, passp) + fr_info_t *fin; + u_32_t *passp; { + ipf_main_softc_t *softc = fin->fin_main_soft; + ipf_state_softc_t *softs = softc->ipf_state_soft; + ipftqent_t *tqe; ipstate_t *is; frentry_t *fr; tcphdr_t *tcp; ipftq_t *ifq; u_int pass; + int inout; - if (fr_state_lock || (ips_list == NULL) || - (fin->fin_flx & (FI_SHORT|FI_STATE|FI_FRAGBODY|FI_BAD))) + if (softs->ipf_state_lock || (softs->ipf_state_list == NULL)) return NULL; - is = NULL; + if (fin->fin_flx & (FI_SHORT|FI_FRAGBODY|FI_BAD)) { + SBUMPD(ipf_state_stats, iss_check_bad); + return NULL; + } + if ((fin->fin_flx & FI_TCPUDP) || (fin->fin_fi.fi_p == IPPROTO_ICMP) #ifdef USE_INET6 @@ -2719,13 +3328,12 @@ u_32_t *passp; else tcp = NULL; + ifq = NULL; /* * Search the hash table for matching packet header info. */ - ifq = NULL; - is = fin->fin_state; - if (is == NULL) - is = fr_stlookup(fin, tcp, &ifq); + is = ipf_state_lookup(fin, tcp, &ifq); + switch (fin->fin_p) { #ifdef USE_INET6 @@ -2733,9 +3341,7 @@ u_32_t *passp; if (is != NULL) break; if (fin->fin_v == 6) { - is = fr_checkicmp6matchingstate(fin); - if (is != NULL) - goto matched; + is = ipf_checkicmp6matchingstate(fin); } break; #endif @@ -2746,56 +3352,92 @@ u_32_t *passp; * No matching icmp state entry. Perhaps this is a * response to another state entry. */ - is = fr_checkicmpmatchingstate(fin); - if (is != NULL) - goto matched; + is = ipf_checkicmpmatchingstate(fin); break; + case IPPROTO_TCP : if (is == NULL) break; if (is->is_pass & FR_NEWISN) { if (fin->fin_out == 0) - fr_fixinisn(fin, is); + ipf_fixinisn(fin, is); else if (fin->fin_out == 1) - fr_fixoutisn(fin, is); + ipf_fixoutisn(fin, is); } break; default : if (fin->fin_rev) - ifq = &ips_udpacktq; + ifq = &softs->ipf_state_udpacktq; else - ifq = &ips_udptq; + ifq = &softs->ipf_state_udptq; break; } if (is == NULL) { - ATOMIC_INCL(ips_stats.iss_miss); + SBUMP(ipf_state_stats.iss_check_miss); return NULL; } -matched: fr = is->is_rule; if (fr != NULL) { if ((fin->fin_out == 0) && (fr->fr_nattag.ipt_num[0] != 0)) { - if (fin->fin_nattag == NULL) + if (fin->fin_nattag == NULL) { + RWLOCK_EXIT(&softc->ipf_state); + SBUMPD(ipf_state_stats, iss_check_notag); return NULL; - if (fr_matchtag(&fr->fr_nattag, fin->fin_nattag) != 0) + } + if (ipf_matchtag(&fr->fr_nattag, fin->fin_nattag)!=0) { + RWLOCK_EXIT(&softc->ipf_state); + SBUMPD(ipf_state_stats, iss_check_nattag); return NULL; + } } - (void) strncpy(fin->fin_group, fr->fr_group, FR_GROUPLEN); + (void) strncpy(fin->fin_group, FR_NAME(fr, fr_group), + FR_GROUPLEN); fin->fin_icode = fr->fr_icode; } fin->fin_rule = is->is_rulen; - pass = is->is_pass; - fr_updatestate(fin, is, ifq); + fin->fin_fr = fr; + + /* + * If this packet is a fragment and the rule says to track fragments, + * then create a new fragment cache entry. + */ + if ((fin->fin_flx & FI_FRAG) && FR_ISPASS(is->is_pass)) + (void) ipf_frag_new(softc, fin, is->is_pass); + + /* + * For TCP packets, ifq == NULL. For all others, check if this new + * queue is different to the last one it was on and move it if so. + */ + tqe = &is->is_sti; + if ((tqe->tqe_flags & TQE_RULEBASED) != 0) + ifq = is->is_tqehead[fin->fin_rev]; - fin->fin_state = is; - is->is_touched = fr_ticks; MUTEX_ENTER(&is->is_lock); - is->is_ref++; + + if (ifq != NULL) + ipf_movequeue(softc->ipf_ticks, tqe, tqe->tqe_ifq, ifq); + + inout = (fin->fin_rev << 1) + fin->fin_out; + is->is_pkts[inout]++; + is->is_bytes[inout] += fin->fin_plen; + fin->fin_pktnum = is->is_pkts[inout] + is->is_icmppkts[inout]; + MUTEX_EXIT(&is->is_lock); - RWLOCK_EXIT(&ipf_state); + + pass = is->is_pass; + + if (is->is_flags & IS_STATESYNC) + ipf_sync_update(softc, SMC_STATE, fin, is->is_sync); + + RWLOCK_EXIT(&softc->ipf_state); + + SBUMP(ipf_state_stats.iss_hits); + + fin->fin_dif = &is->is_dif; + fin->fin_tif = &is->is_tifs[fin->fin_rev]; fin->fin_flx |= FI_STATE; if ((pass & FR_LOGFIRST) != 0) pass &= ~(FR_LOGFIRST|FR_LOG); @@ -2805,17 +3447,18 @@ matched: /* ------------------------------------------------------------------------ */ -/* Function: fr_fixoutisn */ +/* Function: ipf_fixoutisn */ /* Returns: Nil */ -/* Parameters: fin(I) - pointer to packet information */ +/* Parameters: fin(I) - pointer to packet information */ /* is(I) - pointer to master state structure */ /* */ /* Called only for outbound packets, adjusts the sequence number and the */ /* TCP checksum to match that change. */ /* ------------------------------------------------------------------------ */ -static void fr_fixoutisn(fin, is) -fr_info_t *fin; -ipstate_t *is; +static void +ipf_fixoutisn(fin, is) + fr_info_t *fin; + ipstate_t *is; { tcphdr_t *tcp; int rev; @@ -2824,26 +3467,26 @@ ipstate_t *is; tcp = fin->fin_dp; rev = fin->fin_rev; if ((is->is_flags & IS_ISNSYN) != 0) { - if (rev == 0) { + if ((rev == 0) && (fin->fin_cksum < FI_CK_L4PART)) { seq = ntohl(tcp->th_seq); seq += is->is_isninc[0]; tcp->th_seq = htonl(seq); - fix_outcksum(fin, &tcp->th_sum, is->is_sumd[0]); + ipf_fix_outcksum(0, &tcp->th_sum, is->is_sumd[0], 0); } } if ((is->is_flags & IS_ISNACK) != 0) { - if (rev == 1) { + if ((rev == 1) && (fin->fin_cksum < FI_CK_L4PART)) { seq = ntohl(tcp->th_seq); seq += is->is_isninc[1]; tcp->th_seq = htonl(seq); - fix_outcksum(fin, &tcp->th_sum, is->is_sumd[1]); + ipf_fix_outcksum(0, &tcp->th_sum, is->is_sumd[1], 0); } } } /* ------------------------------------------------------------------------ */ -/* Function: fr_fixinisn */ +/* Function: ipf_fixinisn */ /* Returns: Nil */ /* Parameters: fin(I) - pointer to packet information */ /* is(I) - pointer to master state structure */ @@ -2851,9 +3494,10 @@ ipstate_t *is; /* Called only for inbound packets, adjusts the acknowledge number and the */ /* TCP checksum to match that change. */ /* ------------------------------------------------------------------------ */ -static void fr_fixinisn(fin, is) -fr_info_t *fin; -ipstate_t *is; +static void +ipf_fixinisn(fin, is) + fr_info_t *fin; + ipstate_t *is; { tcphdr_t *tcp; int rev; @@ -2862,28 +3506,29 @@ ipstate_t *is; tcp = fin->fin_dp; rev = fin->fin_rev; if ((is->is_flags & IS_ISNSYN) != 0) { - if (rev == 1) { + if ((rev == 1) && (fin->fin_cksum < FI_CK_L4PART)) { ack = ntohl(tcp->th_ack); ack -= is->is_isninc[0]; tcp->th_ack = htonl(ack); - fix_incksum(fin, &tcp->th_sum, is->is_sumd[0]); + ipf_fix_incksum(0, &tcp->th_sum, is->is_sumd[0], 0); } } if ((is->is_flags & IS_ISNACK) != 0) { - if (rev == 0) { + if ((rev == 0) && (fin->fin_cksum < FI_CK_L4PART)) { ack = ntohl(tcp->th_ack); ack -= is->is_isninc[1]; tcp->th_ack = htonl(ack); - fix_incksum(fin, &tcp->th_sum, is->is_sumd[1]); + ipf_fix_incksum(0, &tcp->th_sum, is->is_sumd[1], 0); } } } /* ------------------------------------------------------------------------ */ -/* Function: fr_statesync */ +/* Function: ipf_state_sync */ /* Returns: Nil */ -/* Parameters: ifp(I) - pointer to interface */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* ifp(I) - pointer to interface */ /* */ /* Walk through all state entries and if an interface pointer match is */ /* found then look it up again, based on its name in case the pointer has */ @@ -2892,40 +3537,45 @@ ipstate_t *is; /* If ifp is passed in as being non-null then we are only doing updates for */ /* existing, matching, uses of it. */ /* ------------------------------------------------------------------------ */ -void fr_statesync(ifp) -void *ifp; +void +ipf_state_sync(softc, ifp) + ipf_main_softc_t *softc; + void *ifp; { + ipf_state_softc_t *softs = softc->ipf_state_soft; ipstate_t *is; int i; - if (fr_running <= 0) + if (softc->ipf_running <= 0) return; - WRITE_ENTER(&ipf_state); + WRITE_ENTER(&softc->ipf_state); - if (fr_running <= 0) { - RWLOCK_EXIT(&ipf_state); + if (softc->ipf_running <= 0) { + RWLOCK_EXIT(&softc->ipf_state); return; } - for (is = ips_list; is; is = is->is_next) { + for (is = softs->ipf_state_list; is; is = is->is_next) { /* * Look up all the interface names in the state entry. */ for (i = 0; i < 4; i++) { if (ifp == NULL || ifp == is->is_ifp[i]) - is->is_ifp[i] = fr_resolvenic(is->is_ifname[i], + is->is_ifp[i] = ipf_resolvenic(softc, + is->is_ifname[i], is->is_v); } } - RWLOCK_EXIT(&ipf_state); + RWLOCK_EXIT(&softc->ipf_state); } /* ------------------------------------------------------------------------ */ -/* Function: fr_delstate */ -/* Returns: int - 0 = entry deleted, else reference count on struct */ -/* Parameters: is(I) - pointer to state structure to delete */ +/* Function: ipf_state_del */ +/* Returns: int - 0 = deleted, else refernce count on active struct */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* is(I) - pointer to state structure to delete */ /* why(I) - if not 0, log reason why it was deleted */ /* Write Locks: ipf_state */ /* */ @@ -2933,10 +3583,15 @@ void *ifp; /* and timeout queue lists. Make adjustments to hash table statistics and */ /* global counters as required. */ /* ------------------------------------------------------------------------ */ -static int fr_delstate(is, why) -ipstate_t *is; -int why; +static int +ipf_state_del(softc, is, why) + ipf_main_softc_t *softc; + ipstate_t *is; + int why; { + ipf_state_softc_t *softs = softc->ipf_state_soft; + int orphan = 1; + frentry_t *fr; /* * Since we want to delete this, remove it from the state table, @@ -2946,22 +3601,23 @@ int why; *is->is_phnext = is->is_hnext; if (is->is_hnext != NULL) is->is_hnext->is_phnext = is->is_phnext; - if (ips_table[is->is_hv] == NULL) - ips_stats.iss_inuse--; - ips_stats.iss_bucketlen[is->is_hv]--; + if (softs->ipf_state_table[is->is_hv] == NULL) + softs->ipf_state_stats.iss_inuse--; + softs->ipf_state_stats.iss_bucketlen[is->is_hv]--; is->is_phnext = NULL; is->is_hnext = NULL; + orphan = 0; } /* - * Because ips_stats.iss_wild is a count of entries in the state + * Because ipf_state_stats.iss_wild is a count of entries in the state * table that have wildcard flags set, only decerement it once * and do it here. */ if (is->is_flags & (SI_WILDP|SI_WILDA)) { if (!(is->is_flags & SI_CLONED)) { - ATOMIC_DECL(ips_stats.iss_wild); + ATOMIC_DECL(softs->ipf_state_stats.iss_wild); } is->is_flags &= ~(SI_WILDP|SI_WILDA); } @@ -2970,47 +3626,56 @@ int why; * Next, remove it from the timeout queue it is in. */ if (is->is_sti.tqe_ifq != NULL) - fr_deletequeueentry(&is->is_sti); - - if (is->is_me != NULL) { - *is->is_me = NULL; - is->is_me = NULL; - } + ipf_deletequeueentry(&is->is_sti); /* * If it is still in use by something else, do not go any further, * but note that at this point it is now an orphan. How can this - * be? fr_state_flush() calls fr_delete() directly because it wants + * be? ipf_state_flush() calls ipf_delete() directly because it wants * to empty the table out and if something has a hold on a state * entry (such as ipfstat), it'll do the deref path that'll bring * us back here to do the real delete & free. */ MUTEX_ENTER(&is->is_lock); - if (is->is_ref > 1) { + if (is->is_me != NULL) { + *is->is_me = NULL; + is->is_me = NULL; is->is_ref--; + } + if (is->is_ref > 1) { + int refs; + + is->is_ref--; + refs = is->is_ref; MUTEX_EXIT(&is->is_lock); - return is->is_ref; + if (!orphan) + softs->ipf_state_stats.iss_orphan++; + return refs; } MUTEX_EXIT(&is->is_lock); + fr = is->is_rule; + is->is_rule = NULL; + if (fr != NULL) { + if (fr->fr_srctrack.ht_max_nodes != 0) { + (void) ipf_ht_node_del(&fr->fr_srctrack, + is->is_family, &is->is_src); + } + } + is->is_ref = 0; if (is->is_tqehead[0] != NULL) { - if (fr_deletetimeoutqueue(is->is_tqehead[0]) == 0) - fr_freetimeoutqueue(is->is_tqehead[0]); + if (ipf_deletetimeoutqueue(is->is_tqehead[0]) == 0) + ipf_freetimeoutqueue(softc, is->is_tqehead[0]); } if (is->is_tqehead[1] != NULL) { - if (fr_deletetimeoutqueue(is->is_tqehead[1]) == 0) - fr_freetimeoutqueue(is->is_tqehead[1]); + if (ipf_deletetimeoutqueue(is->is_tqehead[1]) == 0) + ipf_freetimeoutqueue(softc, is->is_tqehead[1]); } -#ifdef IPFILTER_SYNC if (is->is_sync) - ipfsync_del(is->is_sync); -#endif -#ifdef IPFILTER_SCAN - (void) ipsc_detachis(is); -#endif + ipf_sync_del_state(softc->ipf_sync_soft, is->is_sync); /* * Now remove it from the linked list of known states @@ -3025,94 +3690,100 @@ int why; is->is_next = NULL; } - if (ipstate_logging != 0 && why != 0) - ipstate_log(is, why); + if (softs->ipf_state_logging != 0 && why != 0) + ipf_state_log(softc, is, why); if (is->is_p == IPPROTO_TCP) - ips_stats.iss_fin++; + softs->ipf_state_stats.iss_fin++; else - ips_stats.iss_expire++; + softs->ipf_state_stats.iss_expire++; + if (orphan) + softs->ipf_state_stats.iss_orphan--; - if (is->is_rule != NULL) { - is->is_rule->fr_statecnt--; - (void) fr_derefrule(&is->is_rule); + if (fr != NULL) { + fr->fr_statecnt--; + (void) ipf_derefrule(softc, &fr); } -#if defined(NEED_LOCAL_RAND) && defined(_KERNEL) - ipf_rand_push(is, sizeof(*is)); -#endif + softs->ipf_state_stats.iss_active_proto[is->is_p]--; MUTEX_DESTROY(&is->is_lock); KFREE(is); - ips_num--; + softs->ipf_state_stats.iss_active--; return 0; } /* ------------------------------------------------------------------------ */ -/* Function: fr_timeoutstate */ +/* Function: ipf_state_expire */ /* Returns: Nil */ -/* Parameters: Nil */ +/* Parameters: softc(I) - pointer to soft context main structure */ /* */ /* Slowly expire held state for thingslike UDP and ICMP. The algorithm */ /* used here is to keep the queue sorted with the oldest things at the top */ /* and the youngest at the bottom. So if the top one doesn't need to be */ /* expired then neither will any under it. */ /* ------------------------------------------------------------------------ */ -void fr_timeoutstate() +void +ipf_state_expire(softc) + ipf_main_softc_t *softc; { + ipf_state_softc_t *softs = softc->ipf_state_soft; ipftq_t *ifq, *ifqnext; ipftqent_t *tqe, *tqn; ipstate_t *is; SPL_INT(s); SPL_NET(s); - WRITE_ENTER(&ipf_state); - for (ifq = ips_tqtqb; ifq != NULL; ifq = ifq->ifq_next) + WRITE_ENTER(&softc->ipf_state); + for (ifq = softs->ipf_state_tcptq; ifq != NULL; ifq = ifq->ifq_next) for (tqn = ifq->ifq_head; ((tqe = tqn) != NULL); ) { - if (tqe->tqe_die > fr_ticks) + if (tqe->tqe_die > softc->ipf_ticks) break; tqn = tqe->tqe_next; is = tqe->tqe_parent; - fr_delstate(is, ISL_EXPIRE); + ipf_state_del(softc, is, ISL_EXPIRE); } - for (ifq = ips_utqe; ifq != NULL; ifq = ifqnext) { + for (ifq = softs->ipf_state_usertq; ifq != NULL; ifq = ifqnext) { ifqnext = ifq->ifq_next; for (tqn = ifq->ifq_head; ((tqe = tqn) != NULL); ) { - if (tqe->tqe_die > fr_ticks) + if (tqe->tqe_die > softc->ipf_ticks) break; tqn = tqe->tqe_next; is = tqe->tqe_parent; - fr_delstate(is, ISL_EXPIRE); + ipf_state_del(softc, is, ISL_EXPIRE); } } - for (ifq = ips_utqe; ifq != NULL; ifq = ifqnext) { + for (ifq = softs->ipf_state_usertq; ifq != NULL; ifq = ifqnext) { ifqnext = ifq->ifq_next; if (((ifq->ifq_flags & IFQF_DELETE) != 0) && (ifq->ifq_ref == 0)) { - fr_freetimeoutqueue(ifq); + ipf_freetimeoutqueue(softc, ifq); } } - if (fr_state_doflush) { - (void) fr_state_flush(2, 0); - fr_state_doflush = 0; + if (softs->ipf_state_doflush) { + (void) ipf_state_flush(softc, 2, 0); + softs->ipf_state_doflush = 0; + softs->ipf_state_wm_last = softc->ipf_ticks; } - RWLOCK_EXIT(&ipf_state); + RWLOCK_EXIT(&softc->ipf_state); SPL_X(s); } /* ------------------------------------------------------------------------ */ -/* Function: fr_state_flush */ +/* Function: ipf_state_flush */ /* Returns: int - 0 == success, -1 == failure */ -/* Parameters: Nil */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* which(I) - which flush action to perform */ +/* proto(I) - which protocol to flush (0 == ALL) */ /* Write Locks: ipf_state */ /* */ /* Flush state tables. Three actions currently defined: */ @@ -3126,12 +3797,15 @@ void fr_timeoutstate() /* If that too fails, then work backwards in 30 second intervals */ /* for the last 30 minutes to at worst 30 seconds idle. */ /* ------------------------------------------------------------------------ */ -static int fr_state_flush(which, proto) -int which, proto; +int +ipf_state_flush(softc, which, proto) + ipf_main_softc_t *softc; + int which, proto; { - ipftq_t *ifq, *ifqnext; + ipf_state_softc_t *softs = softc->ipf_state_soft; ipftqent_t *tqe, *tqn; ipstate_t *is, **isp; + ipftq_t *ifq; int removed; SPL_INT(s); @@ -3142,15 +3816,16 @@ int which, proto; switch (which) { case 0 : + SBUMP(ipf_state_stats.iss_flush_all); /* * Style 0 flush removes everything... */ - for (isp = &ips_list; ((is = *isp) != NULL); ) { + for (isp = &softs->ipf_state_list; ((is = *isp) != NULL); ) { if ((proto != 0) && (is->is_v != proto)) { isp = &is->is_next; continue; } - if (fr_delstate(is, ISL_FLUSH) == 0) + if (ipf_state_del(softc, is, ISL_FLUSH) == 0) removed++; else isp = &is->is_next; @@ -3158,19 +3833,20 @@ int which, proto; break; case 1 : + SBUMP(ipf_state_stats.iss_flush_closing); /* * Since we're only interested in things that are closing, * we can start with the appropriate timeout queue. */ - for (ifq = ips_tqtqb + IPF_TCPS_CLOSE_WAIT; ifq != NULL; - ifq = ifq->ifq_next) { + for (ifq = softs->ipf_state_tcptq + IPF_TCPS_CLOSE_WAIT; + ifq != NULL; ifq = ifq->ifq_next) { for (tqn = ifq->ifq_head; ((tqe = tqn) != NULL); ) { tqn = tqe->tqe_next; is = tqe->tqe_parent; if (is->is_p != IPPROTO_TCP) break; - if (fr_delstate(is, ISL_EXPIRE) == 0) + if (ipf_state_del(softc, is, ISL_FLUSH) == 0) removed++; } } @@ -3178,8 +3854,8 @@ int which, proto; /* * Also need to look through the user defined queues. */ - for (ifq = ips_utqe; ifq != NULL; ifq = ifqnext) { - ifqnext = ifq->ifq_next; + for (ifq = softs->ipf_state_usertq; ifq != NULL; + ifq = ifq->ifq_next) { for (tqn = ifq->ifq_head; ((tqe = tqn) != NULL); ) { tqn = tqe->tqe_next; is = tqe->tqe_parent; @@ -3188,7 +3864,8 @@ int which, proto; if ((is->is_state[0] > IPF_TCPS_ESTABLISHED) && (is->is_state[1] > IPF_TCPS_ESTABLISHED)) { - if (fr_delstate(is, ISL_EXPIRE) == 0) + if (ipf_state_del(softc, is, + ISL_FLUSH) == 0) removed++; } } @@ -3198,7 +3875,7 @@ int which, proto; case 2 : break; - /* + /* * Args 5-11 correspond to flushing those particular states * for TCP connections. */ @@ -3209,12 +3886,13 @@ int which, proto; case IPF_TCPS_FIN_WAIT_2 : case IPF_TCPS_TIME_WAIT : case IPF_TCPS_CLOSED : - tqn = ips_tqtqb[which].ifq_head; + SBUMP(ipf_state_stats.iss_flush_queue); + tqn = softs->ipf_state_tcptq[which].ifq_head; while (tqn != NULL) { tqe = tqn; tqn = tqe->tqe_next; is = tqe->tqe_parent; - if (fr_delstate(is, ISL_FLUSH) == 0) + if (ipf_state_del(softc, is, ISL_FLUSH) == 0) removed++; } break; @@ -3223,16 +3901,18 @@ int which, proto; if (which < 30) break; - /* + SBUMP(ipf_state_stats.iss_flush_state); + /* * Take a large arbitrary number to mean the number of seconds * for which which consider to be the maximum value we'll allow * the expiration to be. */ which = IPF_TTLVAL(which); - for (isp = &ips_list; ((is = *isp) != NULL); ) { + for (isp = &softs->ipf_state_list; ((is = *isp) != NULL); ) { if ((proto == 0) || (is->is_v == proto)) { - if (fr_ticks - is->is_touched > which) { - if (fr_delstate(is, ISL_FLUSH) == 0) { + if (softc->ipf_ticks - is->is_touched > which) { + if (ipf_state_del(softc, is, + ISL_FLUSH) == 0) { removed++; continue; } @@ -3248,13 +3928,23 @@ int which, proto; return removed; } + SBUMP(ipf_state_stats.iss_flush_timeout); /* - * Asked to remove inactive entries because the table is full. + * Asked to remove inactive entries because the table is full, try + * again, 3 times, if first attempt failed with a different criteria + * each time. The order tried in must be in decreasing age. + * Another alternative is to implement random drop and drop N entries + * at random until N have been freed up. */ - if (fr_ticks - ips_last_force_flush > IPF_TTLVAL(5)) { - ips_last_force_flush = fr_ticks; - removed = ipf_queueflush(fr_state_flush_entry, ips_tqtqb, - ips_utqe); + if (softc->ipf_ticks - softs->ipf_state_wm_last > + softs->ipf_state_wm_freq) { + removed = ipf_queueflush(softc, ipf_state_flush_entry, + softs->ipf_state_tcptq, + softs->ipf_state_usertq, + &softs->ipf_state_stats.iss_active, + softs->ipf_state_size, + softs->ipf_state_wm_low); + softs->ipf_state_wm_last = softc->ipf_ticks; } SPL_X(s); @@ -3263,29 +3953,33 @@ int which, proto; /* ------------------------------------------------------------------------ */ -/* Function: fr_state_flush_entry */ +/* Function: ipf_state_flush_entry */ /* Returns: int - 0 = entry deleted, else not deleted */ -/* Parameters: entry(I) - pointer to state structure to delete */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* entry(I) - pointer to state structure to delete */ /* Write Locks: ipf_state */ /* */ /* This function is a stepping stone between ipf_queueflush() and */ -/* fr_delstate(). It is used so we can provide a uniform interface via the */ -/* ipf_queueflush() function. */ +/* ipf_state_del(). It is used so we can provide a uniform interface via */ +/* the ipf_queueflush() function. */ /* ------------------------------------------------------------------------ */ -static int fr_state_flush_entry(entry) -void *entry; +static int +ipf_state_flush_entry(softc, entry) + ipf_main_softc_t *softc; + void *entry; { - return fr_delstate(entry, ISL_FLUSH); -} + return ipf_state_del(softc, entry, ISL_FLUSH); +} /* ------------------------------------------------------------------------ */ -/* Function: fr_tcp_age */ +/* Function: ipf_tcp_age */ /* Returns: int - 1 == state transition made, 0 == no change (rejected) */ -/* Parameters: tq(I) - pointer to timeout queue information */ +/* Parameters: tqe(I) - pointer to timeout queue information */ /* fin(I) - pointer to packet information */ /* tqtab(I) - TCP timeout queue table this is in */ /* flags(I) - flags from state/NAT entry */ +/* ok(I) - can we advance state */ /* */ /* Rewritten by Arjan de Vet , 2000-07-29: */ /* */ @@ -3297,7 +3991,7 @@ void *entry; /* */ /* - store the state of the source in state[0] such that ipfstat */ /* displays the state as source/dest instead of dest/source; the calls */ -/* to fr_tcp_age have been changed accordingly. */ +/* to ipf_tcp_age have been changed accordingly. */ /* */ /* Internal Parameters: */ /* */ @@ -3328,12 +4022,14 @@ void *entry; /* */ /* Locking: it is assumed that the parent of the tqe structure is locked. */ /* ------------------------------------------------------------------------ */ -int fr_tcp_age(tqe, fin, tqtab, flags) -ipftqent_t *tqe; -fr_info_t *fin; -ipftq_t *tqtab; -int flags; +int +ipf_tcp_age(tqe, fin, tqtab, flags, ok) + ipftqent_t *tqe; + fr_info_t *fin; + ipftq_t *tqtab; + int flags, ok; { + ipf_main_softc_t *softc = fin->fin_main_soft; int dlen, ostate, nstate, rval, dir; u_char tcpflags; tcphdr_t *tcp; @@ -3344,17 +4040,20 @@ int flags; dir = fin->fin_rev; tcpflags = tcp->th_flags; dlen = fin->fin_dlen - (TCP_OFF(tcp) << 2); + ostate = tqe->tqe_state[1 - dir]; + nstate = tqe->tqe_state[dir]; if (tcpflags & TH_RST) { if (!(tcpflags & TH_PUSH) && !dlen) nstate = IPF_TCPS_CLOSED; else nstate = IPF_TCPS_CLOSE_WAIT; + + if (ostate <= IPF_TCPS_ESTABLISHED) { + tqe->tqe_state[1 - dir] = IPF_TCPS_CLOSE_WAIT; + } rval = 1; } else { - ostate = tqe->tqe_state[1 - dir]; - nstate = tqe->tqe_state[dir]; - switch (nstate) { case IPF_TCPS_LISTEN: /* 0 */ @@ -3410,7 +4109,7 @@ int flags; if ((tcpflags & ~(TH_ECN|TH_CWR)) == TH_SYN) { /* * A retransmitted SYN packet. We do not reset - * the timeout here to fr_tcptimeout because a + * the timeout here to ipf_tcptimeout because a * connection connect timeout does not renew * after every packet that is sent. We need to * set rval so as to indicate the packet has @@ -3578,7 +4277,7 @@ int flags; * the FIN packet here? does the window code * guarantee that? */ - nstate = IPF_TCPS_TIME_WAIT; + nstate = IPF_TCPS_LAST_ACK; } else { /* * we closed our side of the connection @@ -3594,25 +4293,18 @@ int flags; if ((tcpflags & (TH_FIN|TH_ACK)) == TH_ACK) { nstate = IPF_TCPS_TIME_WAIT; } - rval = 2; + rval = 1; break; case IPF_TCPS_LAST_ACK: /* 8 */ if (tcpflags & TH_ACK) { - if ((tcpflags & TH_PUSH) || dlen) - /* - * there is still data to be delivered, - * reset timeout - */ - rval = 1; - else - rval = 2; + rval = 1; } /* - * we cannot detect when we go out of LAST_ACK state to - * CLOSED because that is based on the reception of ACK - * packets; ipfilter can only detect that a packet - * has been sent by a host + * we cannot detect when we go out of LAST_ACK state + * to CLOSED because that is based on the reception + * of ACK packets; ipfilter can only detect that a + * packet has been sent by a host */ break; @@ -3624,8 +4316,10 @@ int flags; /* we're in 2MSL timeout now */ if (ostate == IPF_TCPS_LAST_ACK) { nstate = IPF_TCPS_CLOSED; + rval = 1; + } else { + rval = 2; } - rval = 1; break; case IPF_TCPS_CLOSED: /* 11 */ @@ -3633,18 +4327,7 @@ int flags; break; default : -#if defined(_KERNEL) -# if SOLARIS - cmn_err(CE_NOTE, - "tcp %lx flags %x si %lx nstate %d ostate %d\n", - (u_long)tcp, tcpflags, (u_long)tqe, - nstate, ostate); -# else - printf("tcp %lx flags %x si %lx nstate %d ostate %d\n", - (u_long)tcp, tcpflags, (u_long)tqe, - nstate, ostate); -# endif -#else +#if !defined(_KERNEL) abort(); #endif break; @@ -3658,9 +4341,11 @@ int flags; if (rval == 2) rval = 1; else if (rval == 1) { - tqe->tqe_state[dir] = nstate; + if (ok) + tqe->tqe_state[dir] = nstate; if ((tqe->tqe_flags & TQE_RULEBASED) == 0) - fr_movequeue(tqe, tqe->tqe_ifq, tqtab + nstate); + ipf_movequeue(softc->ipf_ticks, tqe, tqe->tqe_ifq, + tqtab + nstate); } return rval; @@ -3668,18 +4353,21 @@ int flags; /* ------------------------------------------------------------------------ */ -/* Function: ipstate_log */ +/* Function: ipf_state_log */ /* Returns: Nil */ -/* Parameters: is(I) - pointer to state structure */ -/* type(I) - type of log entry to create */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* is(I) - pointer to state structure */ +/* type(I) - type of log entry to create */ /* */ /* Creates a state table log entry using the state structure and type info. */ /* passed in. Log packet/byte counts, source/destination address and other */ /* protocol specific information. */ /* ------------------------------------------------------------------------ */ -void ipstate_log(is, type) -struct ipstate *is; -u_int type; +void +ipf_state_log(softc, is, type) + ipf_main_softc_t *softc; + struct ipstate *is; + u_int type; { #ifdef IPFILTER_LOG struct ipslog ipsl; @@ -3729,18 +4417,14 @@ u_int type; sizes[0] = sizeof(ipsl); types[0] = 0; - if (ipllog(IPL_LOGSTATE, NULL, items, sizes, types, 1)) { - ATOMIC_INCL(ips_stats.iss_logged); - } else { - ATOMIC_INCL(ips_stats.iss_logfail); - } + (void) ipf_log_items(softc, IPL_LOGSTATE, NULL, items, sizes, types, 1); #endif } #ifdef USE_INET6 /* ------------------------------------------------------------------------ */ -/* Function: fr_checkicmp6matchingstate */ +/* Function: ipf_checkicmp6matchingstate */ /* Returns: ipstate_t* - NULL == no match found, */ /* else pointer to matching state entry */ /* Parameters: fin(I) - pointer to packet information */ @@ -3749,11 +4433,13 @@ u_int type; /* If we've got an ICMPv6 error message, using the information stored in */ /* the ICMPv6 packet, look for a matching state table entry. */ /* ------------------------------------------------------------------------ */ -static ipstate_t *fr_checkicmp6matchingstate(fin) -fr_info_t *fin; +static ipstate_t * +ipf_checkicmp6matchingstate(fin) + fr_info_t *fin; { + ipf_main_softc_t *softc = fin->fin_main_soft; + ipf_state_softc_t *softs = softc->ipf_state_soft; struct icmp6_hdr *ic6, *oic; - int type, backward, i; ipstate_t *is, **isp; u_short sport, dport; i6addr_t dst, src; @@ -3762,8 +4448,9 @@ fr_info_t *fin; fr_info_t ofin; tcphdr_t *tcp; ip6_t *oip6; - u_char pr; + u_char pr; u_int hv; + int type; /* * Does it at least have the return (basic) IP header ? @@ -3772,15 +4459,19 @@ fr_info_t *fin; * an ICMP error header. */ if ((fin->fin_v != 6) || (fin->fin_plen < ICMP6ERR_MINPKTLEN) || - !(fin->fin_flx & FI_ICMPERR)) + !(fin->fin_flx & FI_ICMPERR)) { + SBUMPD(ipf_state_stats, iss_icmp_bad); return NULL; + } ic6 = fin->fin_dp; type = ic6->icmp6_type; oip6 = (ip6_t *)((char *)ic6 + ICMPERR_ICMPHLEN); - if (fin->fin_plen < sizeof(*oip6)) + if (fin->fin_plen < sizeof(*oip6)) { + SBUMPD(ipf_state_stats, iss_icmp_short); return NULL; + } bcopy((char *)fin, (char *)&ofin, sizeof(*fin)); ofin.fin_v = 6; @@ -3794,21 +4485,31 @@ fr_info_t *fin; * matchsrcdst. Note that not all fields are necessary * but this is the cleanest way. Note further we fill * in fin_mp such that if someone uses it we'll get - * a kernel panic. fr_matchsrcdst does not use this. + * a kernel panic. ipf_matchsrcdst does not use this. * * watch out here, as ip is in host order and oip6 in network * order. Any change we make must be undone afterwards. */ savelen = oip6->ip6_plen; - oip6->ip6_plen = fin->fin_dlen - ICMPERR_ICMPHLEN; + oip6->ip6_plen = htons(fin->fin_dlen - ICMPERR_ICMPHLEN); ofin.fin_flx = FI_NOCKSUM; ofin.fin_ip = (ip_t *)oip6; - (void) fr_makefrip(sizeof(*oip6), (ip_t *)oip6, &ofin); + (void) ipf_makefrip(sizeof(*oip6), (ip_t *)oip6, &ofin); ofin.fin_flx &= ~(FI_BAD|FI_SHORT); oip6->ip6_plen = savelen; + pr = ofin.fin_p; + + /* + * an ICMP error can never generate an ICMP error in response. + */ + if (ofin.fin_flx & FI_ICMPERR) { + DT1(iss_icmp6_icmperr, fr_info_t *, &ofin); + SBUMP(ipf_state_stats.iss_icmp6_icmperr); + return NULL; + } if (oip6->ip6_nxt == IPPROTO_ICMPV6) { - oic = (struct icmp6_hdr *)(oip6 + 1); + oic = ofin.fin_dp; /* * an ICMP error can only be generated as a result of an * ICMP query, not as the response on an ICMP error @@ -3816,8 +4517,11 @@ fr_info_t *fin; * XXX theoretically ICMP_ECHOREP and the other reply's are * ICMP query's as well, but adding them here seems strange XXX */ - if (!(oic->icmp6_type & ICMP6_INFOMSG_MASK)) - return NULL; + if (!(oic->icmp6_type & ICMP6_INFOMSG_MASK)) { + DT1(iss_icmp6_notinfo, fr_info_t *, &ofin); + SBUMP(ipf_state_stats.iss_icmp6_notinfo); + return NULL; + } /* * perform a lookup of the ICMP packet in the state table @@ -3831,15 +4535,16 @@ fr_info_t *fin; hv += oic->icmp6_seq; hv = DOUBLE_HASH(hv); - READ_ENTER(&ipf_state); - for (isp = &ips_table[hv]; ((is = *isp) != NULL); ) { + READ_ENTER(&softc->ipf_state); + for (isp = &softs->ipf_state_table[hv]; + ((is = *isp) != NULL); ) { ic = &is->is_icmp; isp = &is->is_hnext; if ((is->is_p == pr) && !(is->is_pass & FR_NOICMPERR) && (oic->icmp6_id == ic->ici_id) && (oic->icmp6_seq == ic->ici_seq) && - (is = fr_matchsrcdst(&ofin, is, &src, + (is = ipf_matchsrcdst(&ofin, is, &src, &dst, NULL, FI_ICMPCMP))) { /* * in the state table ICMP query's are stored @@ -3849,16 +4554,13 @@ fr_info_t *fin; if (((ic->ici_type == ICMP6_ECHO_REPLY) && (oic->icmp6_type == ICMP6_ECHO_REQUEST)) || (ic->ici_type - 1 == oic->icmp6_type )) { - ips_stats.iss_hits++; - backward = IP6_NEQ(&is->is_dst, &src); - fin->fin_rev = !backward; - i = (backward << 1) + fin->fin_out; - is->is_icmppkts[i]++; - return is; + if (!ipf_allowstateicmp(fin, is, &src)) + return is; } } } - RWLOCK_EXIT(&ipf_state); + RWLOCK_EXIT(&softc->ipf_state); + SBUMPD(ipf_state_stats, iss_icmp6_miss); return NULL; } @@ -3874,18 +4576,33 @@ fr_info_t *fin; hv += dst.i6[2]; hv += dst.i6[3]; - if ((oip6->ip6_nxt == IPPROTO_TCP) || (oip6->ip6_nxt == IPPROTO_UDP)) { + tcp = NULL; + + switch (oip6->ip6_nxt) + { + case IPPROTO_TCP : + case IPPROTO_UDP : tcp = (tcphdr_t *)(oip6 + 1); dport = tcp->th_dport; sport = tcp->th_sport; hv += dport; hv += sport; - } else - tcp = NULL; + break; + + case IPPROTO_ICMPV6 : + oic = (struct icmp6_hdr *)(oip6 + 1); + hv += oic->icmp6_id; + hv += oic->icmp6_seq; + break; + + default : + break; + } + hv = DOUBLE_HASH(hv); - READ_ENTER(&ipf_state); - for (isp = &ips_table[hv]; ((is = *isp) != NULL); ) { + READ_ENTER(&softc->ipf_state); + for (isp = &softs->ipf_state_table[hv]; ((is = *isp) != NULL); ) { isp = &is->is_hnext; /* * Only allow this icmp though if the @@ -3897,73 +4614,63 @@ fr_info_t *fin; if ((is->is_p != pr) || (is->is_v != 6) || (is->is_pass & FR_NOICMPERR)) continue; - is = fr_matchsrcdst(&ofin, is, &src, &dst, tcp, FI_ICMPCMP); - if (is != NULL) { - ips_stats.iss_hits++; - backward = IP6_NEQ(&is->is_dst, &src); - fin->fin_rev = !backward; - i = (backward << 1) + fin->fin_out; - is->is_icmppkts[i]++; - /* - * we deliberately do not touch the timeouts - * for the accompanying state table entry. - * It remains to be seen if that is correct. XXX - */ + is = ipf_matchsrcdst(&ofin, is, &src, &dst, tcp, FI_ICMPCMP); + if ((is != NULL) && (ipf_allowstateicmp(fin, is, &src) == 0)) return is; - } } - RWLOCK_EXIT(&ipf_state); + RWLOCK_EXIT(&softc->ipf_state); + SBUMPD(ipf_state_stats, iss_icmp_miss); return NULL; } #endif /* ------------------------------------------------------------------------ */ -/* Function: fr_sttab_init */ +/* Function: ipf_sttab_init */ /* Returns: Nil */ -/* Parameters: tqp(I) - pointer to an array of timeout queues for TCP */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* tqp(I) - pointer to an array of timeout queues for TCP */ /* */ /* Initialise the array of timeout queues for TCP. */ /* ------------------------------------------------------------------------ */ -void fr_sttab_init(tqp) -ipftq_t *tqp; +void +ipf_sttab_init(softc, tqp) + ipf_main_softc_t *softc; + ipftq_t *tqp; { int i; for (i = IPF_TCP_NSTATES - 1; i >= 0; i--) { - tqp[i].ifq_ttl = 0; - tqp[i].ifq_ref = 1; - tqp[i].ifq_head = NULL; - tqp[i].ifq_tail = &tqp[i].ifq_head; + IPFTQ_INIT(&tqp[i], 0, "ipftq tcp tab"); tqp[i].ifq_next = tqp + i + 1; - MUTEX_INIT(&tqp[i].ifq_lock, "ipftq tcp tab"); } tqp[IPF_TCP_NSTATES - 1].ifq_next = NULL; - tqp[IPF_TCPS_CLOSED].ifq_ttl = fr_tcpclosed; - tqp[IPF_TCPS_LISTEN].ifq_ttl = fr_tcptimeout; - tqp[IPF_TCPS_SYN_SENT].ifq_ttl = fr_tcptimeout; - tqp[IPF_TCPS_SYN_RECEIVED].ifq_ttl = fr_tcptimeout; - tqp[IPF_TCPS_ESTABLISHED].ifq_ttl = fr_tcpidletimeout; - tqp[IPF_TCPS_CLOSE_WAIT].ifq_ttl = fr_tcphalfclosed; - tqp[IPF_TCPS_FIN_WAIT_1].ifq_ttl = fr_tcphalfclosed; - tqp[IPF_TCPS_CLOSING].ifq_ttl = fr_tcptimeout; - tqp[IPF_TCPS_LAST_ACK].ifq_ttl = fr_tcplastack; - tqp[IPF_TCPS_FIN_WAIT_2].ifq_ttl = fr_tcpclosewait; - tqp[IPF_TCPS_TIME_WAIT].ifq_ttl = fr_tcptimewait; - tqp[IPF_TCPS_HALF_ESTAB].ifq_ttl = fr_tcptimeout; + tqp[IPF_TCPS_CLOSED].ifq_ttl = softc->ipf_tcpclosed; + tqp[IPF_TCPS_LISTEN].ifq_ttl = softc->ipf_tcptimeout; + tqp[IPF_TCPS_SYN_SENT].ifq_ttl = softc->ipf_tcpsynsent; + tqp[IPF_TCPS_SYN_RECEIVED].ifq_ttl = softc->ipf_tcpsynrecv; + tqp[IPF_TCPS_ESTABLISHED].ifq_ttl = softc->ipf_tcpidletimeout; + tqp[IPF_TCPS_CLOSE_WAIT].ifq_ttl = softc->ipf_tcphalfclosed; + tqp[IPF_TCPS_FIN_WAIT_1].ifq_ttl = softc->ipf_tcphalfclosed; + tqp[IPF_TCPS_CLOSING].ifq_ttl = softc->ipf_tcptimeout; + tqp[IPF_TCPS_LAST_ACK].ifq_ttl = softc->ipf_tcplastack; + tqp[IPF_TCPS_FIN_WAIT_2].ifq_ttl = softc->ipf_tcpclosewait; + tqp[IPF_TCPS_TIME_WAIT].ifq_ttl = softc->ipf_tcptimewait; + tqp[IPF_TCPS_HALF_ESTAB].ifq_ttl = softc->ipf_tcptimeout; } /* ------------------------------------------------------------------------ */ -/* Function: fr_sttab_destroy */ +/* Function: ipf_sttab_destroy */ /* Returns: Nil */ /* Parameters: tqp(I) - pointer to an array of timeout queues for TCP */ /* */ /* Do whatever is necessary to "destroy" each of the entries in the array */ /* of timeout queues for TCP. */ /* ------------------------------------------------------------------------ */ -void fr_sttab_destroy(tqp) -ipftq_t *tqp; +void +ipf_sttab_destroy(tqp) + ipftq_t *tqp; { int i; @@ -3973,9 +4680,10 @@ ipftq_t *tqp; /* ------------------------------------------------------------------------ */ -/* Function: fr_statederef */ +/* Function: ipf_state_deref */ /* Returns: Nil */ -/* Parameters: isp(I) - pointer to pointer to state table entry */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* isp(I) - pointer to pointer to state table entry */ /* */ /* Decrement the reference counter for this state table entry and free it */ /* if there are no more things using it. */ @@ -4003,10 +4711,12 @@ ipftq_t *tqp; /* dir == 0 : a packet from source to dest */ /* dir == 1 : a packet from dest to source */ /* ------------------------------------------------------------------------ */ -void fr_statederef(isp) -ipstate_t **isp; +void +ipf_state_deref(softc, isp) + ipf_main_softc_t *softc; + ipstate_t **isp; { - ipstate_t *is; + ipstate_t *is = *isp; is = *isp; *isp = NULL; @@ -4017,37 +4727,40 @@ ipstate_t **isp; MUTEX_EXIT(&is->is_lock); #ifndef _KERNEL if ((is->is_sti.tqe_state[0] > IPF_TCPS_ESTABLISHED) || - (is->is_sti.tqe_state[1] > IPF_TCPS_ESTABLISHED)) { - fr_delstate(is, ISL_ORPHAN); + (is->is_sti.tqe_state[1] > IPF_TCPS_ESTABLISHED)) { + ipf_state_del(softc, is, ISL_EXPIRE); } #endif return; } MUTEX_EXIT(&is->is_lock); - WRITE_ENTER(&ipf_state); - fr_delstate(is, ISL_EXPIRE); - RWLOCK_EXIT(&ipf_state); + WRITE_ENTER(&softc->ipf_state); + ipf_state_del(softc, is, ISL_ORPHAN); + RWLOCK_EXIT(&softc->ipf_state); } /* ------------------------------------------------------------------------ */ -/* Function: fr_setstatequeue */ +/* Function: ipf_state_setqueue */ /* Returns: Nil */ -/* Parameters: is(I) - pointer to state structure */ -/* rev(I) - forward(0) or reverse(1) direction */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* is(I) - pointer to state structure */ +/* rev(I) - forward(0) or reverse(1) direction */ /* Locks: ipf_state (read or write) */ /* */ /* Put the state entry on its default queue entry, using rev as a helped in */ /* determining which queue it should be placed on. */ /* ------------------------------------------------------------------------ */ -void fr_setstatequeue(is, rev) -ipstate_t *is; -int rev; +void +ipf_state_setqueue(softc, is, rev) + ipf_main_softc_t *softc; + ipstate_t *is; + int rev; { + ipf_state_softc_t *softs = softc->ipf_state_soft; ipftq_t *oifq, *nifq; - if ((is->is_sti.tqe_flags & TQE_RULEBASED) != 0) nifq = is->is_tqehead[rev]; else @@ -4059,30 +4772,30 @@ int rev; #ifdef USE_INET6 case IPPROTO_ICMPV6 : if (rev == 1) - nifq = &ips_icmpacktq; + nifq = &softs->ipf_state_icmpacktq; else - nifq = &ips_icmptq; + nifq = &softs->ipf_state_icmptq; break; #endif case IPPROTO_ICMP : if (rev == 1) - nifq = &ips_icmpacktq; + nifq = &softs->ipf_state_icmpacktq; else - nifq = &ips_icmptq; + nifq = &softs->ipf_state_icmptq; break; case IPPROTO_TCP : - nifq = ips_tqtqb + is->is_state[rev]; + nifq = softs->ipf_state_tcptq + is->is_state[rev]; break; case IPPROTO_UDP : if (rev == 1) - nifq = &ips_udpacktq; + nifq = &softs->ipf_state_udpacktq; else - nifq = &ips_udptq; + nifq = &softs->ipf_state_udptq; break; default : - nifq = &ips_iptq; + nifq = &softs->ipf_state_iptq; break; } } @@ -4093,126 +4806,560 @@ int rev; * another, else put it on the end of the newly determined queue. */ if (oifq != NULL) - fr_movequeue(&is->is_sti, oifq, nifq); + ipf_movequeue(softc->ipf_ticks, &is->is_sti, oifq, nifq); else - fr_queueappend(&is->is_sti, nifq, is); + ipf_queueappend(softc->ipf_ticks, &is->is_sti, nifq, is); return; } /* ------------------------------------------------------------------------ */ -/* Function: fr_stateiter */ +/* Function: ipf_state_iter */ /* Returns: int - 0 == success, else error */ -/* Parameters: token(I) - pointer to ipftoken structure */ +/* Parameters: softc(I) - pointer to main soft context */ +/* token(I) - pointer to ipftoken structure */ /* itp(I) - pointer to ipfgeniter structure */ +/* obj(I) - pointer to data description structure */ /* */ /* This function handles the SIOCGENITER ioctl for the state tables and */ -/* walks through the list of entries in the state table list (ips_list.) */ +/* walks through the list of entries in the state table list (softs->ipf_state_list.) */ /* ------------------------------------------------------------------------ */ -static int fr_stateiter(token, itp) -ipftoken_t *token; -ipfgeniter_t *itp; +static int +ipf_state_iter(softc, token, itp, obj) + ipf_main_softc_t *softc; + ipftoken_t *token; + ipfgeniter_t *itp; + ipfobj_t *obj; { + ipf_state_softc_t *softs = softc->ipf_state_soft; ipstate_t *is, *next, zero; - int error, count; - char *dst; + int error; - if (itp->igi_data == NULL) + if (itp->igi_data == NULL) { + IPFERROR(100026); return EFAULT; + } - if (itp->igi_nitems < 1) + if (itp->igi_nitems < 1) { + IPFERROR(100027); return ENOSPC; + } - if (itp->igi_type != IPFGENITER_STATE) + if (itp->igi_type != IPFGENITER_STATE) { + IPFERROR(100028); return EINVAL; + } is = token->ipt_data; if (is == (void *)-1) { - ipf_freetoken(token); + IPFERROR(100029); return ESRCH; } error = 0; - dst = itp->igi_data; + obj->ipfo_type = IPFOBJ_IPSTATE; + obj->ipfo_size = sizeof(ipstate_t); - READ_ENTER(&ipf_state); + READ_ENTER(&softc->ipf_state); + + is = token->ipt_data; if (is == NULL) { - next = ips_list; + next = softs->ipf_state_list; } else { next = is->is_next; } - count = itp->igi_nitems; - for (;;) { - if (next != NULL) { - /* - * If we find a state entry to use, bump its - * reference count so that it can be used for - * is_next when we come back. - */ - if (count == 1) { - MUTEX_ENTER(&next->is_lock); - next->is_ref++; - MUTEX_EXIT(&next->is_lock); - token->ipt_data = next; - } - } else { - bzero(&zero, sizeof(zero)); - next = &zero; - count = 1; - token->ipt_data = NULL; - } - RWLOCK_EXIT(&ipf_state); - - /* - * This should arguably be via fr_outobj() so that the state - * structure can (if required) be massaged going out. - */ - error = COPYOUT(next, dst, sizeof(*next)); - if (error != 0) - error = EFAULT; - if ((count == 1) || (error != 0)) - break; - - dst += sizeof(*next); - count--; - - READ_ENTER(&ipf_state); - next = next->is_next; + /* + * If we find a state entry to use, bump its reference count so that + * it can be used for is_next when we come back. + */ + if (next != NULL) { + MUTEX_ENTER(&next->is_lock); + next->is_ref++; + MUTEX_EXIT(&next->is_lock); + token->ipt_data = next; + } else { + bzero(&zero, sizeof(zero)); + next = &zero; + token->ipt_data = NULL; } + if (next->is_next == NULL) + ipf_token_mark_complete(token); - if (is != NULL) { - fr_statederef(&is); - } + RWLOCK_EXIT(&softc->ipf_state); + + obj->ipfo_ptr = itp->igi_data; + error = ipf_outobjk(softc, obj, next); + if (is != NULL) + ipf_state_deref(softc, &is); return error; } /* ------------------------------------------------------------------------ */ -/* Function: fr_stgettable */ +/* Function: ipf_state_gettable */ /* Returns: int - 0 = success, else error */ -/* Parameters: data(I) - pointer to ioctl data */ +/* Parameters: softc(I) - pointer to main soft context */ +/* softs(I) - pointer to state context structure */ +/* data(I) - pointer to ioctl data */ /* */ /* This function handles ioctl requests for tables of state information. */ /* At present the only table it deals with is the hash bucket statistics. */ /* ------------------------------------------------------------------------ */ -static int fr_stgettable(data) -char *data; +static int +ipf_state_gettable(softc, softs, data) + ipf_main_softc_t *softc; + ipf_state_softc_t *softs; + char *data; { ipftable_t table; int error; - error = fr_inobj(data, &table, IPFOBJ_GTABLE); + error = ipf_inobj(softc, data, NULL, &table, IPFOBJ_GTABLE); if (error != 0) return error; - if (table.ita_type != IPFTABLE_BUCKETS) + if (table.ita_type != IPFTABLE_BUCKETS) { + IPFERROR(100031); return EINVAL; + } - error = COPYOUT(ips_stats.iss_bucketlen, table.ita_table, - fr_statesize * sizeof(u_long)); - if (error != 0) + error = COPYOUT(softs->ipf_state_stats.iss_bucketlen, table.ita_table, + softs->ipf_state_size * sizeof(u_int)); + if (error != 0) { + IPFERROR(100032); error = EFAULT; + } return error; } + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_state_setpending */ +/* Returns: Nil */ +/* Parameters: softc(I) - pointer to main soft context */ +/* is(I) - pointer to state structure */ +/* Locks: ipf_state (read or write) */ +/* */ +/* Put the state entry on to the pending queue - this queue has a very */ +/* short lifetime where items are put that can't be deleted straight away */ +/* because of locking issues but we want to delete them ASAP, anyway. */ +/* ------------------------------------------------------------------------ */ +void +ipf_state_setpending(softc, is) + ipf_main_softc_t *softc; + ipstate_t *is; +{ + ipf_state_softc_t *softs = softc->ipf_state_soft; + ipftq_t *oifq; + + oifq = is->is_sti.tqe_ifq; + if (oifq != NULL) + ipf_movequeue(softc->ipf_ticks, &is->is_sti, oifq, + &softs->ipf_state_pending); + else + ipf_queueappend(softc->ipf_ticks, &is->is_sti, + &softs->ipf_state_pending, is); + + MUTEX_ENTER(&is->is_lock); + if (is->is_me != NULL) { + *is->is_me = NULL; + is->is_me = NULL; + is->is_ref--; + } + MUTEX_EXIT(&is->is_lock); +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_state_matchflush */ +/* Returns: Nil */ +/* Parameters: softc(I) - pointer to main soft context */ +/* data(I) - pointer to state structure */ +/* Locks: ipf_state (read or write) */ +/* */ +/* Flush all entries from the list of state entries that match the */ +/* properties in the array loaded. */ +/* ------------------------------------------------------------------------ */ +int +ipf_state_matchflush(softc, data) + ipf_main_softc_t *softc; + caddr_t data; +{ + ipf_state_softc_t *softs = softc->ipf_state_soft; + int *array, flushed, error; + ipstate_t *state, *statenext; + ipfobj_t obj; + + error = ipf_matcharray_load(softc, data, &obj, &array); + if (error != 0) + return error; + + flushed = 0; + + for (state = softs->ipf_state_list; state != NULL; state = statenext) { + statenext = state->is_next; + if (ipf_state_matcharray(state, array, softc->ipf_ticks) == 0) { + ipf_state_del(softc, state, ISL_FLUSH); + flushed++; + } + } + + obj.ipfo_retval = flushed; + error = BCOPYOUT(&obj, data, sizeof(obj)); + + KFREES(array, array[0] * sizeof(*array)); + + return error; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_state_matcharray */ +/* Returns: int - 0 = no match, 1 = match */ +/* Parameters: state(I) - pointer to state structure */ +/* array(I) - pointer to ipf matching expression */ +/* ticks(I) - current value of ipfilter tick timer */ +/* Locks: ipf_state (read or write) */ +/* */ +/* Compare a state entry with the match array passed in and return a value */ +/* to indicate whether or not the matching was successful. */ +/* ------------------------------------------------------------------------ */ +static int +ipf_state_matcharray(state, array, ticks) + ipstate_t *state; + int *array; + u_long ticks; +{ + int i, n, *x, rv, p; + ipfexp_t *e; + + rv = 0; + n = array[0]; + x = array + 1; + + for (; n > 0; x += 3 + x[3], rv = 0) { + e = (ipfexp_t *)x; + n -= e->ipfe_size; + if (x[0] == IPF_EXP_END) + break; + + /* + * If we need to match the protocol and that doesn't match, + * don't even both with the instruction array. + */ + p = e->ipfe_cmd >> 16; + if ((p != 0) && (p != state->is_p)) + break; + + switch (e->ipfe_cmd) + { + case IPF_EXP_IP_PR : + for (i = 0; !rv && i < e->ipfe_narg; i++) { + rv |= (state->is_p == e->ipfe_arg0[i]); + } + break; + + case IPF_EXP_IP_SRCADDR : + if (state->is_v != 4) + break; + for (i = 0; !rv && i < e->ipfe_narg; i++) { + rv |= ((state->is_saddr & + e->ipfe_arg0[i * 2 + 1]) == + e->ipfe_arg0[i * 2]); + } + break; + + case IPF_EXP_IP_DSTADDR : + if (state->is_v != 4) + break; + for (i = 0; !rv && i < e->ipfe_narg; i++) { + rv |= ((state->is_daddr & + e->ipfe_arg0[i * 2 + 1]) == + e->ipfe_arg0[i * 2]); + } + break; + + case IPF_EXP_IP_ADDR : + if (state->is_v != 4) + break; + for (i = 0; !rv && i < e->ipfe_narg; i++) { + rv |= ((state->is_saddr & + e->ipfe_arg0[i * 2 + 1]) == + e->ipfe_arg0[i * 2]) || + ((state->is_daddr & + e->ipfe_arg0[i * 2 + 1]) == + e->ipfe_arg0[i * 2]); + } + break; + +#ifdef USE_INET6 + case IPF_EXP_IP6_SRCADDR : + if (state->is_v != 6) + break; + for (i = 0; !rv && i < x[3]; i++) { + rv |= IP6_MASKEQ(&state->is_src.in6, + &e->ipfe_arg0[i * 8 + 4], + &e->ipfe_arg0[i * 8]); + } + break; + + case IPF_EXP_IP6_DSTADDR : + if (state->is_v != 6) + break; + for (i = 0; !rv && i < x[3]; i++) { + rv |= IP6_MASKEQ(&state->is_dst.in6, + &e->ipfe_arg0[i * 8 + 4], + &e->ipfe_arg0[i * 8]); + } + break; + + case IPF_EXP_IP6_ADDR : + if (state->is_v != 6) + break; + for (i = 0; !rv && i < x[3]; i++) { + rv |= IP6_MASKEQ(&state->is_src.in6, + &e->ipfe_arg0[i * 8 + 4], + &e->ipfe_arg0[i * 8]) || + IP6_MASKEQ(&state->is_dst.in6, + &e->ipfe_arg0[i * 8 + 4], + &e->ipfe_arg0[i * 8]); + } + break; +#endif + + case IPF_EXP_UDP_PORT : + case IPF_EXP_TCP_PORT : + for (i = 0; !rv && i < e->ipfe_narg; i++) { + rv |= (state->is_sport == e->ipfe_arg0[i]) || + (state->is_dport == e->ipfe_arg0[i]); + } + break; + + case IPF_EXP_UDP_SPORT : + case IPF_EXP_TCP_SPORT : + for (i = 0; !rv && i < e->ipfe_narg; i++) { + rv |= (state->is_sport == e->ipfe_arg0[i]); + } + break; + + case IPF_EXP_UDP_DPORT : + case IPF_EXP_TCP_DPORT : + for (i = 0; !rv && i < e->ipfe_narg; i++) { + rv |= (state->is_dport == e->ipfe_arg0[i]); + } + break; + + case IPF_EXP_TCP_STATE : + for (i = 0; !rv && i < e->ipfe_narg; i++) { + rv |= (state->is_state[0] == e->ipfe_arg0[i]) || + (state->is_state[1] == e->ipfe_arg0[i]); + } + break; + + case IPF_EXP_IDLE_GT : + rv |= (ticks - state->is_touched > e->ipfe_arg0[0]); + break; + } + + /* + * Factor in doing a negative match. + */ + rv ^= e->ipfe_not; + + if (rv == 0) + break; + } + + return rv; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_state_settimeout */ +/* Returns: int 0 = success, else failure */ +/* Parameters: softc(I) - pointer to main soft context */ +/* t(I) - pointer to tuneable being changed */ +/* p(I) - pointer to the new value */ +/* */ +/* Sets a timeout value for one of the many timeout queues. We find the */ +/* correct queue using a somewhat manual process of comparing the timeout */ +/* names for each specific value available and calling ipf_apply_timeout on */ +/* that queue so that all of the items on it are updated accordingly. */ +/* ------------------------------------------------------------------------ */ +int +ipf_state_settimeout(softc, t, p) + struct ipf_main_softc_s *softc; + ipftuneable_t *t; + ipftuneval_t *p; +{ + ipf_state_softc_t *softs = softc->ipf_state_soft; + + /* + * In case there is nothing to do... + */ + if (*t->ipft_pint == p->ipftu_int) + return 0; + + if (!strncmp(t->ipft_name, "tcp_", 4)) + return ipf_settimeout_tcp(t, p, softs->ipf_state_tcptq); + + if (!strcmp(t->ipft_name, "udp_timeout")) { + ipf_apply_timeout(&softs->ipf_state_udptq, p->ipftu_int); + } else if (!strcmp(t->ipft_name, "udp_ack_timeout")) { + ipf_apply_timeout(&softs->ipf_state_udpacktq, p->ipftu_int); + } else if (!strcmp(t->ipft_name, "icmp_timeout")) { + ipf_apply_timeout(&softs->ipf_state_icmptq, p->ipftu_int); + } else if (!strcmp(t->ipft_name, "icmp_ack_timeout")) { + ipf_apply_timeout(&softs->ipf_state_icmpacktq, p->ipftu_int); + } else if (!strcmp(t->ipft_name, "ip_timeout")) { + ipf_apply_timeout(&softs->ipf_state_iptq, p->ipftu_int); + } else { + IPFERROR(100034); + return ESRCH; + } + + /* + * Update the tuneable being set. + */ + *t->ipft_pint = p->ipftu_int; + + return 0; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_state_rehash */ +/* Returns: int 0 = success, else failure */ +/* Parameters: softc(I) - pointer to main soft context */ +/* t(I) - pointer to tuneable being changed */ +/* p(I) - pointer to the new value */ +/* */ +/* To change the size of the state hash table at runtime, a new table has */ +/* to be allocated and then all of the existing entries put in it, bumping */ +/* up the bucketlength for it as we go along. */ +/* ------------------------------------------------------------------------ */ +int +ipf_state_rehash(softc, t, p) + ipf_main_softc_t *softc; + ipftuneable_t *t; + ipftuneval_t *p; +{ + ipf_state_softc_t *softs = softc->ipf_state_soft; + ipstate_t **newtab, *is; + u_int *bucketlens; + u_int maxbucket; + u_int newsize; + u_int hv; + int i; + + newsize = p->ipftu_int; + /* + * In case there is nothing to do... + */ + if (newsize == softs->ipf_state_size) + return 0; + + KMALLOCS(newtab, ipstate_t **, newsize * sizeof(ipstate_t *)); + if (newtab == NULL) { + IPFERROR(100035); + return ENOMEM; + } + + KMALLOCS(bucketlens, u_int *, newsize * sizeof(u_int)); + if (bucketlens == NULL) { + KFREES(newtab, newsize * sizeof(*softs->ipf_state_table)); + IPFERROR(100036); + return ENOMEM; + } + + for (maxbucket = 0, i = newsize; i > 0; i >>= 1) + maxbucket++; + maxbucket *= 2; + + bzero((char *)newtab, newsize * sizeof(ipstate_t *)); + bzero((char *)bucketlens, newsize * sizeof(u_int)); + + WRITE_ENTER(&softc->ipf_state); + + if (softs->ipf_state_table != NULL) { + KFREES(softs->ipf_state_table, + softs->ipf_state_size * sizeof(*softs->ipf_state_table)); + } + softs->ipf_state_table = newtab; + + if (softs->ipf_state_stats.iss_bucketlen != NULL) { + KFREES(softs->ipf_state_stats.iss_bucketlen, + softs->ipf_state_size * sizeof(u_int)); + } + softs->ipf_state_stats.iss_bucketlen = bucketlens; + softs->ipf_state_maxbucket = maxbucket; + softs->ipf_state_size = newsize; + + /* + * Walk through the entire list of state table entries and put them + * in the new state table, somewhere. Because we have a new table, + * we need to restart the counter of how many chains are in use. + */ + softs->ipf_state_stats.iss_inuse = 0; + for (is = softs->ipf_state_list; is != NULL; is = is->is_next) { + is->is_hnext = NULL; + is->is_phnext = NULL; + hv = is->is_hv % softs->ipf_state_size; + + if (softs->ipf_state_table[hv] != NULL) + softs->ipf_state_table[hv]->is_phnext = &is->is_hnext; + else + softs->ipf_state_stats.iss_inuse++; + is->is_phnext = softs->ipf_state_table + hv; + is->is_hnext = softs->ipf_state_table[hv]; + softs->ipf_state_table[hv] = is; + softs->ipf_state_stats.iss_bucketlen[hv]++; + } + RWLOCK_EXIT(&softc->ipf_state); + + return 0; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_state_add_tq */ +/* Returns: ipftq_t * - NULL = failure, else pointer to new timeout */ +/* queue */ +/* Parameters: softc(I) - pointer to main soft context */ +/* ttl(I) - pointer to the ttl for the new queue */ +/* */ +/* Request a pointer to a timeout queue that has a ttl as given by the */ +/* value being passed in. The timeout queue is added tot the list of those */ +/* used internally for stateful filtering. */ +/* ------------------------------------------------------------------------ */ +ipftq_t * +ipf_state_add_tq(softc, ttl) + ipf_main_softc_t *softc; + int ttl; +{ + ipf_state_softc_t *softs = softc->ipf_state_soft; + + return ipf_addtimeoutqueue(softc, &softs->ipf_state_usertq, ttl); +} + + +#ifndef _KERNEL +/* + * Display the built up state table rules and mapping entries. + */ +void +ipf_state_dump(softc, arg) + ipf_main_softc_t *softc; + void *arg; +{ + ipf_state_softc_t *softs = arg; + ipstate_t *ips; + + printf("List of active state sessions:\n"); + for (ips = softs->ipf_state_list; ips != NULL; ) + ips = printstate(ips, opts & (OPT_DEBUG|OPT_VERBOSE), + softc->ipf_ticks); +} +#endif diff --git a/sys/contrib/ipfilter/netinet/ip_state.h b/sys/contrib/ipfilter/netinet/ip_state.h index 9c4e8149d7f..e765ac77153 100644 --- a/sys/contrib/ipfilter/netinet/ip_state.h +++ b/sys/contrib/ipfilter/netinet/ip_state.h @@ -1,7 +1,7 @@ /* $FreeBSD$ */ /* - * Copyright (C) 1995-2001 by Darren Reed. + * Copyright (C) 2012 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. * @@ -27,8 +27,10 @@ struct ipscan; # define IPSTATE_MAX 4013 /* Maximum number of states held */ #endif -#define SEQ_GE(a,b) ((int)((a) - (b)) >= 0) -#define SEQ_GT(a,b) ((int)((a) - (b)) > 0) +#define PAIRS(s1,d1,s2,d2) ((((s1) == (s2)) && ((d1) == (d2))) ||\ + (((s1) == (d2)) && ((d1) == (s2)))) +#define IPPAIR(s1,d1,s2,d2) PAIRS((s1).s_addr, (d1).s_addr, \ + (s2).s_addr, (d2).s_addr) typedef struct ipstate { @@ -56,6 +58,7 @@ typedef struct ipstate { u_int is_pass; u_char is_p; /* Protocol */ u_char is_v; + int is_family; u_32_t is_hv; u_32_t is_tag; u_32_t is_opt[2]; /* packet options set */ @@ -75,6 +78,8 @@ typedef struct ipstate { u_32_t is_rulen; /* rule number when created */ u_32_t is_s0[2]; u_short is_smsk[2]; + frdest_t is_dif; + frdest_t is_tifs[2]; char is_group[FR_GROUPLEN]; char is_sbuf[2][16]; char is_ifname[4][LIFNAMSIZ]; @@ -87,7 +92,6 @@ typedef struct ipstate { #define is_daddr is_dst.in4.s_addr #define is_icmp is_ps.is_ics #define is_type is_icmp.ici_type -#define is_code is_icmp.ici_code #define is_tcp is_ps.is_ts #define is_udp is_ps.is_us #define is_send is_tcp.ts_data[0].td_end @@ -119,6 +123,7 @@ typedef struct ipstate { #define IS_ISNSYN 0x40000 #define IS_ISNACK 0x80000 #define IS_STATESYNC 0x100000 +#define IS_LOOSE 0x200000 /* * IS_SC flags are for scan-operations that need to be recognised in state. */ @@ -130,7 +135,7 @@ typedef struct ipstate { #define IS_SC_ALL (IS_SC_MATCHC|IS_SC_MATCHC|IS_SC_CLIENT|IS_SC_SERVER) /* - * Flags that can be passed into fr_addstate + * Flags that can be passed into ipf_addstate */ #define IS_INHERITED 0x0fffff00 @@ -181,6 +186,7 @@ typedef struct ipslog { #define ISL_NEW 0 #define ISL_CLONE 1 +#define ISL_STATECHANGE 2 #define ISL_EXPIRE 0xffff #define ISL_FLUSH 0xfffe #define ISL_REMOVE 0xfffd @@ -191,71 +197,141 @@ typedef struct ipslog { typedef struct ips_stat { - u_long iss_hits; - u_long iss_miss; - u_long iss_max; - u_long iss_maxref; - u_long iss_tcp; - u_long iss_udp; - u_long iss_icmp; - u_long iss_nomem; + u_int iss_active; + u_int iss_active_proto[256]; + u_long iss_add_bad; + u_long iss_add_dup; + u_long iss_add_locked; + u_long iss_add_oow; + u_long iss_bucket_full; + u_long iss_check_bad; + u_long iss_check_miss; + u_long iss_check_nattag; + u_long iss_check_notag; + u_long iss_clone_nomem; + u_long iss_cloned; u_long iss_expire; u_long iss_fin; - u_long iss_active; - u_long iss_logged; - u_long iss_logfail; - u_long iss_inuse; - u_long iss_wild; - u_long iss_killed; - u_long iss_ticks; - u_long iss_bucketfull; - int iss_statesize; - int iss_statemax; - ipstate_t **iss_table; + u_long iss_flush_all; + u_long iss_flush_closing; + u_long iss_flush_queue; + u_long iss_flush_state; + u_long iss_flush_timeout; + u_long iss_hits; + u_long iss_icmp6_icmperr; + u_long iss_icmp6_miss; + u_long iss_icmp6_notinfo; + u_long iss_icmp6_notquery; + u_long iss_icmp_bad; + u_long iss_icmp_banned; + u_long iss_icmp_headblock; + u_long iss_icmp_hits; + u_long iss_icmp_icmperr; + u_long iss_icmp_miss; + u_long iss_icmp_notquery; + u_long iss_icmp_short; + u_long iss_icmp_toomany; + u_int iss_inuse; ipstate_t *iss_list; - u_long *iss_bucketlen; + u_long iss_log_fail; + u_long iss_log_ok; + u_long iss_lookup_badifp; + u_long iss_lookup_badport; + u_long iss_lookup_miss; + u_long iss_max; + u_long iss_max_ref; + u_long iss_max_track; + u_long iss_miss_mask; + u_long iss_nomem; + u_long iss_oow; + u_long iss_orphan; + u_long iss_proto[256]; + u_long iss_scan_block; + u_long iss_state_max; + u_long iss_state_size; + u_long iss_states[IPF_TCP_NSTATES]; + ipstate_t **iss_table; + u_long iss_tcp_closing; + u_long iss_tcp_oow; + u_long iss_tcp_rstadd; + u_long iss_tcp_toosmall; + u_long iss_tcp_badopt; + u_long iss_tcp_fsm; + u_long iss_tcp_strict; ipftq_t *iss_tcptab; + u_int iss_ticks; + u_long iss_wild; + u_long iss_winsack; + u_int *iss_bucketlen; } ips_stat_t; -extern u_long fr_tcpidletimeout; -extern u_long fr_tcpclosewait; -extern u_long fr_tcplastack; -extern u_long fr_tcptimeout; -extern u_long fr_tcpclosed; -extern u_long fr_tcphalfclosed; -extern u_long fr_udptimeout; -extern u_long fr_udpacktimeout; -extern u_long fr_icmptimeout; -extern u_long fr_icmpacktimeout; -extern u_long fr_iptimeout; -extern int fr_statemax; -extern int fr_statesize; -extern int fr_state_lock; -extern int fr_state_maxbucket; -extern int fr_state_maxbucket_reset; -extern ipstate_t *ips_list; -extern ipftq_t *ips_utqe; -extern ipftq_t ips_tqtqb[IPF_TCP_NSTATES]; +typedef struct ipf_state_softc_s { + ipfmutex_t ipf_stinsert; + int ipf_state_logging; + int ipf_state_lock; + int ipf_state_doflush; + u_int ipf_state_inited; + u_int ipf_state_max; + u_int ipf_state_maxbucket; + u_int ipf_state_size; + u_int ipf_state_wm_freq; + u_int ipf_state_wm_high; + u_int ipf_state_wm_low; + u_int ipf_state_wm_last; + u_long *ipf_state_seed; + ipstate_t *ipf_state_list; + ipstate_t **ipf_state_table; + ipftuneable_t *ipf_state_tune; + ipftq_t *ipf_state_usertq; + ipftq_t ipf_state_pending; + ipftq_t ipf_state_deletetq; + ipftq_t ipf_state_udptq; + ipftq_t ipf_state_udpacktq; + ipftq_t ipf_state_iptq; + ipftq_t ipf_state_icmptq; + ipftq_t ipf_state_icmpacktq; + ipftq_t ipf_state_tcptq[IPF_TCP_NSTATES]; + ips_stat_t ipf_state_stats; +} ipf_state_softc_t; -extern int fr_stateinit __P((void)); -extern ipstate_t *fr_addstate __P((fr_info_t *, ipstate_t **, u_int)); -extern frentry_t *fr_checkstate __P((struct fr_info *, u_32_t *)); -extern ipstate_t *fr_stlookup __P((fr_info_t *, tcphdr_t *, ipftq_t **)); -extern void fr_statesync __P((void *)); -extern void fr_timeoutstate __P((void)); -extern int fr_tcp_age __P((struct ipftqent *, struct fr_info *, - struct ipftq *, int)); -extern int fr_tcpinwindow __P((struct fr_info *, struct tcpdata *, + +#ifndef _KERNEL +extern void ipf_state_dump __P((ipf_main_softc_t *, void *)); +#endif +extern int ipf_tcp_age __P((struct ipftqent *, struct fr_info *, + struct ipftq *, int, int)); +extern int ipf_tcpinwindow __P((struct fr_info *, struct tcpdata *, struct tcpdata *, tcphdr_t *, int)); -extern void fr_stateunload __P((void)); -extern void ipstate_log __P((struct ipstate *, u_int)); -extern int fr_state_ioctl __P((caddr_t, ioctlcmd_t, int, int, void *)); -extern void fr_stinsert __P((struct ipstate *, int)); -extern void fr_sttab_init __P((struct ipftq *)); -extern void fr_sttab_destroy __P((struct ipftq *)); -extern void fr_updatestate __P((fr_info_t *, ipstate_t *, ipftq_t *)); -extern void fr_statederef __P((ipstate_t **)); -extern void fr_setstatequeue __P((ipstate_t *, int)); + +extern int ipf_state_add __P((ipf_main_softc_t *, fr_info_t *, + ipstate_t **, u_int)); +extern frentry_t *ipf_state_check __P((struct fr_info *, u_32_t *)); +extern void ipf_state_deref __P((ipf_main_softc_t *, ipstate_t **)); +extern void ipf_state_expire __P((ipf_main_softc_t *)); +extern int ipf_state_flush __P((ipf_main_softc_t *, int, int)); +extern ipstate_t *ipf_state_lookup __P((fr_info_t *, tcphdr_t *, ipftq_t **)); +extern int ipf_state_init __P((void)); +extern int ipf_state_insert __P((ipf_main_softc_t *, struct ipstate *, int)); +extern int ipf_state_ioctl __P((ipf_main_softc_t *, caddr_t, ioctlcmd_t, int, int, void *)); +extern void ipf_state_log __P((ipf_main_softc_t *, struct ipstate *, u_int)); +extern int ipf_state_matchflush __P((ipf_main_softc_t *, caddr_t)); +extern int ipf_state_rehash __P((ipf_main_softc_t *, ipftuneable_t *, ipftuneval_t *)); +extern void ipf_state_setqueue __P((ipf_main_softc_t *, ipstate_t *, int)); +extern void ipf_state_setpending __P((ipf_main_softc_t *, ipstate_t *)); +extern int ipf_state_settimeout __P((struct ipf_main_softc_s *, ipftuneable_t *, ipftuneval_t *)); +extern void ipf_state_sync __P((ipf_main_softc_t *, void *)); +extern void ipf_state_update __P((fr_info_t *, ipstate_t *)); + +extern void ipf_sttab_init __P((ipf_main_softc_t *, struct ipftq *)); +extern void ipf_sttab_destroy __P((struct ipftq *)); +extern void ipf_state_setlock __P((void *, int)); +extern int ipf_state_main_load __P((void)); +extern int ipf_state_main_unload __P((void)); +extern void *ipf_state_soft_create __P((ipf_main_softc_t *)); +extern void ipf_state_soft_destroy __P((ipf_main_softc_t *, void *)); +extern int ipf_state_soft_init __P((ipf_main_softc_t *, void *)); +extern int ipf_state_soft_fini __P((ipf_main_softc_t *, void *)); +extern ipftq_t *ipf_state_add_tq __P((ipf_main_softc_t *, int)); #endif /* __IP_STATE_H__ */ diff --git a/sys/contrib/ipfilter/netinet/ip_sync.c b/sys/contrib/ipfilter/netinet/ip_sync.c index a72f50f34bd..0c2fe10b3ba 100644 --- a/sys/contrib/ipfilter/netinet/ip_sync.c +++ b/sys/contrib/ipfilter/netinet/ip_sync.c @@ -1,7 +1,7 @@ /* $FreeBSD$ */ /* - * Copyright (C) 1995-1998 by Darren Reed. + * Copyright (C) 2012 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. */ @@ -32,6 +32,10 @@ struct file; # if !defined(__SVR4) && !defined(__svr4__) # include # endif +# include +# if __FreeBSD_version >= 500000 +# include +# endif #endif #if defined(__NetBSD__) && (__NetBSD_Version__ >= 104000000) # include @@ -39,9 +43,6 @@ struct file; #if defined(_KERNEL) && (__FreeBSD_version >= 220000) # include # include -# if (__FreeBSD_version >= 300000) && !defined(IPFILTER_LKM) -# include "opt_ipfilter.h" -# endif #else # include #endif @@ -64,7 +65,6 @@ struct file; #ifdef sun # include #endif -#include #include #include #include @@ -98,66 +98,219 @@ struct file; /* END OF INCLUDES */ #if !defined(lint) -static const char rcsid[] = "@(#)$Id: ip_sync.c,v 2.40.2.9 2007/06/02 21:22:28 darrenr Exp $"; +static const char rcsid[] = "@(#)$Id$"; #endif #define SYNC_STATETABSZ 256 #define SYNC_NATTABSZ 256 -#ifdef IPFILTER_SYNC -ipfmutex_t ipf_syncadd, ipsl_mutex; -ipfrwlock_t ipf_syncstate, ipf_syncnat; +typedef struct ipf_sync_softc_s { + ipfmutex_t ipf_syncadd; + ipfmutex_t ipsl_mutex; + ipfrwlock_t ipf_syncstate; + ipfrwlock_t ipf_syncnat; #if SOLARIS && defined(_KERNEL) -kcondvar_t ipslwait; + kcondvar_t ipslwait; #endif -synclist_t *syncstatetab[SYNC_STATETABSZ]; -synclist_t *syncnattab[SYNC_NATTABSZ]; -synclogent_t synclog[SYNCLOG_SZ]; -syncupdent_t syncupd[SYNCLOG_SZ]; -u_int ipf_syncnum = 1; -u_int ipf_syncwrap = 0; -u_int sl_idx = 0, /* next available sync log entry */ - su_idx = 0, /* next available sync update entry */ - sl_tail = 0, /* next sync log entry to read */ - su_tail = 0; /* next sync update entry to read */ -int ipf_sync_debug = 0; +#if defined(linux) && defined(_KERNEL) + wait_queue_head_t sl_tail_linux; +#endif + synclist_t **syncstatetab; + synclist_t **syncnattab; + synclogent_t *synclog; + syncupdent_t *syncupd; + u_int ipf_sync_num; + u_int ipf_sync_wrap; + u_int sl_idx; /* next available sync log entry */ + u_int su_idx; /* next available sync update entry */ + u_int sl_tail; /* next sync log entry to read */ + u_int su_tail; /* next sync update entry to read */ + int ipf_sync_log_sz; + int ipf_sync_nat_tab_sz; + int ipf_sync_state_tab_sz; + int ipf_sync_debug; + int ipf_sync_events; + u_32_t ipf_sync_lastwakeup; + int ipf_sync_wake_interval; + int ipf_sync_event_high_wm; + int ipf_sync_queue_high_wm; + int ipf_sync_inited; +} ipf_sync_softc_t; +static int ipf_sync_flush_table __P((ipf_sync_softc_t *, int, synclist_t **)); +static void ipf_sync_wakeup __P((ipf_main_softc_t *)); +static void ipf_sync_del __P((ipf_sync_softc_t *, synclist_t *)); +static void ipf_sync_poll_wakeup __P((ipf_main_softc_t *)); +static int ipf_sync_nat __P((ipf_main_softc_t *, synchdr_t *, void *)); +static int ipf_sync_state __P((ipf_main_softc_t *, synchdr_t *, void *)); # if !defined(sparc) && !defined(__hppa) -void ipfsync_tcporder __P((int, struct tcpdata *)); -void ipfsync_natorder __P((int, struct nat *)); -void ipfsync_storder __P((int, struct ipstate *)); +void ipf_sync_tcporder __P((int, struct tcpdata *)); +void ipf_sync_natorder __P((int, struct nat *)); +void ipf_sync_storder __P((int, struct ipstate *)); # endif +void * +ipf_sync_soft_create(softc) + ipf_main_softc_t *softc; +{ + ipf_sync_softc_t *softs; + + KMALLOC(softs, ipf_sync_softc_t *); + if (softs == NULL) { + IPFERROR(110024); + return NULL; + } + + bzero((char *)softs, sizeof(*softs)); + + softs->ipf_sync_log_sz = SYNCLOG_SZ; + softs->ipf_sync_nat_tab_sz = SYNC_STATETABSZ; + softs->ipf_sync_state_tab_sz = SYNC_STATETABSZ; + softs->ipf_sync_event_high_wm = SYNCLOG_SZ * 100 / 90; /* 90% */ + softs->ipf_sync_queue_high_wm = SYNCLOG_SZ * 100 / 90; /* 90% */ + + return softs; +} + + /* ------------------------------------------------------------------------ */ -/* Function: ipfsync_init */ +/* Function: ipf_sync_init */ /* Returns: int - 0 == success, -1 == failure */ /* Parameters: Nil */ /* */ /* Initialise all of the locks required for the sync code and initialise */ /* any data structures, as required. */ /* ------------------------------------------------------------------------ */ -int ipfsync_init() +int +ipf_sync_soft_init(softc, arg) + ipf_main_softc_t *softc; + void *arg; { - RWLOCK_INIT(&ipf_syncstate, "add things to state sync table"); - RWLOCK_INIT(&ipf_syncnat, "add things to nat sync table"); - MUTEX_INIT(&ipf_syncadd, "add things to sync table"); - MUTEX_INIT(&ipsl_mutex, "add things to sync table"); -# if SOLARIS && defined(_KERNEL) - cv_init(&ipslwait, "ipsl condvar", CV_DRIVER, NULL); -# endif + ipf_sync_softc_t *softs = arg; - bzero((char *)syncnattab, sizeof(syncnattab)); - bzero((char *)syncstatetab, sizeof(syncstatetab)); + KMALLOCS(softs->synclog, synclogent_t *, + softs->ipf_sync_log_sz * sizeof(*softs->synclog)); + if (softs->synclog == NULL) + return -1; + bzero((char *)softs->synclog, + softs->ipf_sync_log_sz * sizeof(*softs->synclog)); + + KMALLOCS(softs->syncupd, syncupdent_t *, + softs->ipf_sync_log_sz * sizeof(*softs->syncupd)); + if (softs->syncupd == NULL) + return -2; + bzero((char *)softs->syncupd, + softs->ipf_sync_log_sz * sizeof(*softs->syncupd)); + + KMALLOCS(softs->syncstatetab, synclist_t **, + softs->ipf_sync_state_tab_sz * sizeof(*softs->syncstatetab)); + if (softs->syncstatetab == NULL) + return -3; + bzero((char *)softs->syncstatetab, + softs->ipf_sync_state_tab_sz * sizeof(*softs->syncstatetab)); + + KMALLOCS(softs->syncnattab, synclist_t **, + softs->ipf_sync_nat_tab_sz * sizeof(*softs->syncnattab)); + if (softs->syncnattab == NULL) + return -3; + bzero((char *)softs->syncnattab, + softs->ipf_sync_nat_tab_sz * sizeof(*softs->syncnattab)); + + softs->ipf_sync_num = 1; + softs->ipf_sync_wrap = 0; + softs->sl_idx = 0; + softs->su_idx = 0; + softs->sl_tail = 0; + softs->su_tail = 0; + softs->ipf_sync_events = 0; + softs->ipf_sync_lastwakeup = 0; + + +# if SOLARIS && defined(_KERNEL) + cv_init(&softs->ipslwait, "ipsl condvar", CV_DRIVER, NULL); +# endif + RWLOCK_INIT(&softs->ipf_syncstate, "add things to state sync table"); + RWLOCK_INIT(&softs->ipf_syncnat, "add things to nat sync table"); + MUTEX_INIT(&softs->ipf_syncadd, "add things to sync table"); + MUTEX_INIT(&softs->ipsl_mutex, "read ring lock"); + + softs->ipf_sync_inited = 1; return 0; } +/* ------------------------------------------------------------------------ */ +/* Function: ipf_sync_unload */ +/* Returns: int - 0 == success, -1 == failure */ +/* Parameters: Nil */ +/* */ +/* Destroy the locks created when initialising and free any memory in use */ +/* with the synchronisation tables. */ +/* ------------------------------------------------------------------------ */ +int +ipf_sync_soft_fini(softc, arg) + ipf_main_softc_t *softc; + void *arg; +{ + ipf_sync_softc_t *softs = arg; + + if (softs->syncnattab != NULL) { + ipf_sync_flush_table(softs, softs->ipf_sync_nat_tab_sz, + softs->syncnattab); + KFREES(softs->syncnattab, + softs->ipf_sync_nat_tab_sz * sizeof(*softs->syncnattab)); + softs->syncnattab = NULL; + } + + if (softs->syncstatetab != NULL) { + ipf_sync_flush_table(softs, softs->ipf_sync_state_tab_sz, + softs->syncstatetab); + KFREES(softs->syncstatetab, + softs->ipf_sync_state_tab_sz * + sizeof(*softs->syncstatetab)); + softs->syncstatetab = NULL; + } + + if (softs->syncupd != NULL) { + KFREES(softs->syncupd, + softs->ipf_sync_log_sz * sizeof(*softs->syncupd)); + softs->syncupd = NULL; + } + + if (softs->synclog != NULL) { + KFREES(softs->synclog, + softs->ipf_sync_log_sz * sizeof(*softs->synclog)); + softs->synclog = NULL; + } + + if (softs->ipf_sync_inited == 1) { + MUTEX_DESTROY(&softs->ipsl_mutex); + MUTEX_DESTROY(&softs->ipf_syncadd); + RW_DESTROY(&softs->ipf_syncnat); + RW_DESTROY(&softs->ipf_syncstate); + softs->ipf_sync_inited = 0; + } + + return 0; +} + +void +ipf_sync_soft_destroy(softc, arg) + ipf_main_softc_t *softc; + void *arg; +{ + ipf_sync_softc_t *softs = arg; + + KFREE(softs); +} + + # if !defined(sparc) && !defined(__hppa) /* ------------------------------------------------------------------------ */ -/* Function: ipfsync_tcporder */ +/* Function: ipf_sync_tcporder */ /* Returns: Nil */ /* Parameters: way(I) - direction of byte order conversion. */ /* td(IO) - pointer to data to be converted. */ @@ -165,9 +318,10 @@ int ipfsync_init() /* Do byte swapping on values in the TCP state information structure that */ /* need to be used at both ends by the host in their native byte order. */ /* ------------------------------------------------------------------------ */ -void ipfsync_tcporder(way, td) -int way; -tcpdata_t *td; +void +ipf_sync_tcporder(way, td) + int way; + tcpdata_t *td; { if (way) { td->td_maxwin = htons(td->td_maxwin); @@ -182,7 +336,7 @@ tcpdata_t *td; /* ------------------------------------------------------------------------ */ -/* Function: ipfsync_natorder */ +/* Function: ipf_sync_natorder */ /* Returns: Nil */ /* Parameters: way(I) - direction of byte order conversion. */ /* nat(IO) - pointer to data to be converted. */ @@ -190,9 +344,10 @@ tcpdata_t *td; /* Do byte swapping on values in the NAT data structure that need to be */ /* used at both ends by the host in their native byte order. */ /* ------------------------------------------------------------------------ */ -void ipfsync_natorder(way, n) -int way; -nat_t *n; +void +ipf_sync_natorder(way, n) + int way; + nat_t *n; { if (way) { n->nat_age = htonl(n->nat_age); @@ -211,7 +366,7 @@ nat_t *n; /* ------------------------------------------------------------------------ */ -/* Function: ipfsync_storder */ +/* Function: ipf_sync_storder */ /* Returns: Nil */ /* Parameters: way(I) - direction of byte order conversion. */ /* ips(IO) - pointer to data to be converted. */ @@ -219,12 +374,13 @@ nat_t *n; /* Do byte swapping on values in the IP state data structure that need to */ /* be used at both ends by the host in their native byte order. */ /* ------------------------------------------------------------------------ */ -void ipfsync_storder(way, ips) -int way; -ipstate_t *ips; +void +ipf_sync_storder(way, ips) + int way; + ipstate_t *ips; { - ipfsync_tcporder(way, &ips->is_tcp.ts_data[0]); - ipfsync_tcporder(way, &ips->is_tcp.ts_data[1]); + ipf_sync_tcporder(way, &ips->is_tcp.ts_data[0]); + ipf_sync_tcporder(way, &ips->is_tcp.ts_data[1]); if (way) { ips->is_hv = htonl(ips->is_hv); @@ -263,36 +419,37 @@ ipstate_t *ips; } } # else /* !defined(sparc) && !defined(__hppa) */ -# define ipfsync_tcporder(x,y) -# define ipfsync_natorder(x,y) -# define ipfsync_storder(x,y) +# define ipf_sync_tcporder(x,y) +# define ipf_sync_natorder(x,y) +# define ipf_sync_storder(x,y) # endif /* !defined(sparc) && !defined(__hppa) */ -/* enable this for debugging */ -# ifdef _KERNEL /* ------------------------------------------------------------------------ */ -/* Function: ipfsync_write */ +/* Function: ipf_sync_write */ /* Returns: int - 0 == success, else error value. */ /* Parameters: uio(I) - pointer to information about data to write */ /* */ /* Moves data from user space into the kernel and uses it for updating data */ /* structures in the state/NAT tables. */ /* ------------------------------------------------------------------------ */ -int ipfsync_write(uio) -struct uio *uio; +int +ipf_sync_write(softc, uio) + ipf_main_softc_t *softc; + struct uio *uio; { + ipf_sync_softc_t *softs = softc->ipf_sync_soft; synchdr_t sh; - /* + /* * THIS MUST BE SUFFICIENT LARGE TO STORE - * ANY POSSIBLE DATA TYPE + * ANY POSSIBLE DATA TYPE */ - char data[2048]; + char data[2048]; int err = 0; -# if (BSD >= 199306) || defined(__FreeBSD__) || defined(__osf__) +# if BSD_GE_YEAR(199306) || defined(__FreeBSD__) || defined(__osf__) uio->uio_rw = UIO_WRITE; # endif @@ -304,7 +461,7 @@ struct uio *uio; err = UIOMOVE(&sh, sizeof(sh), UIO_WRITE, uio); if (err) { - if (ipf_sync_debug > 2) + if (softs->ipf_sync_debug > 2) printf("uiomove(header) failed: %d\n", err); return err; @@ -315,59 +472,65 @@ struct uio *uio; sh.sm_len = ntohl(sh.sm_len); sh.sm_num = ntohl(sh.sm_num); - if (ipf_sync_debug > 8) + if (softs->ipf_sync_debug > 8) printf("[%d] Read v:%d p:%d cmd:%d table:%d rev:%d len:%d magic:%x\n", sh.sm_num, sh.sm_v, sh.sm_p, sh.sm_cmd, sh.sm_table, sh.sm_rev, sh.sm_len, sh.sm_magic); if (sh.sm_magic != SYNHDRMAGIC) { - if (ipf_sync_debug > 2) - printf("uiomove(header) invalud %s\n", + if (softs->ipf_sync_debug > 2) + printf("uiomove(header) invalid %s\n", "magic"); + IPFERROR(110001); return EINVAL; } if (sh.sm_v != 4 && sh.sm_v != 6) { - if (ipf_sync_debug > 2) + if (softs->ipf_sync_debug > 2) printf("uiomove(header) invalid %s\n", "protocol"); + IPFERROR(110002); return EINVAL; } if (sh.sm_cmd > SMC_MAXCMD) { - if (ipf_sync_debug > 2) + if (softs->ipf_sync_debug > 2) printf("uiomove(header) invalid %s\n", "command"); + IPFERROR(110003); return EINVAL; } if (sh.sm_table > SMC_MAXTBL) { - if (ipf_sync_debug > 2) + if (softs->ipf_sync_debug > 2) printf("uiomove(header) invalid %s\n", "table"); + IPFERROR(110004); return EINVAL; } } else { /* unsufficient data, wait until next call */ - if (ipf_sync_debug > 2) + if (softs->ipf_sync_debug > 2) printf("uiomove(header) insufficient data"); + IPFERROR(110005); return EAGAIN; } /* - * We have a header, so try to read the amount of data + * We have a header, so try to read the amount of data * needed for the request */ /* not supported */ if (sh.sm_len == 0) { - if (ipf_sync_debug > 2) + if (softs->ipf_sync_debug > 2) printf("uiomove(data zero length %s\n", "not supported"); + IPFERROR(110006); return EINVAL; } @@ -376,33 +539,34 @@ struct uio *uio; err = UIOMOVE(data, sh.sm_len, UIO_WRITE, uio); if (err) { - if (ipf_sync_debug > 2) + if (softs->ipf_sync_debug > 2) printf("uiomove(data) failed: %d\n", err); return err; } - if (ipf_sync_debug > 7) + if (softs->ipf_sync_debug > 7) printf("uiomove(data) %d bytes read\n", sh.sm_len); if (sh.sm_table == SMC_STATE) - err = ipfsync_state(&sh, data); + err = ipf_sync_state(softc, &sh, data); else if (sh.sm_table == SMC_NAT) - err = ipfsync_nat(&sh, data); - if (ipf_sync_debug > 7) + err = ipf_sync_nat(softc, &sh, data); + if (softs->ipf_sync_debug > 7) printf("[%d] Finished with error %d\n", sh.sm_num, err); } else { /* insufficient data, wait until next call */ - if (ipf_sync_debug > 2) + if (softs->ipf_sync_debug > 2) printf("uiomove(data) %s %d bytes, got %d\n", "insufficient data, need", - sh.sm_len, uio->uio_resid); + sh.sm_len, (int)uio->uio_resid); + IPFERROR(110007); return EAGAIN; } - } + } /* no more data */ return 0; @@ -410,7 +574,7 @@ struct uio *uio; /* ------------------------------------------------------------------------ */ -/* Function: ipfsync_read */ +/* Function: ipf_sync_read */ /* Returns: int - 0 == success, else error value. */ /* Parameters: uio(O) - pointer to information about where to store data */ /* */ @@ -418,89 +582,105 @@ struct uio *uio; /* for pending state/NAT updates. If no data is available, the caller is */ /* put to sleep, pending a wakeup from the "lower half" of this code. */ /* ------------------------------------------------------------------------ */ -int ipfsync_read(uio) -struct uio *uio; +int +ipf_sync_read(softc, uio) + ipf_main_softc_t *softc; + struct uio *uio; { + ipf_sync_softc_t *softs = softc->ipf_sync_soft; syncupdent_t *su; synclogent_t *sl; int err = 0; - if ((uio->uio_resid & 3) || (uio->uio_resid < 8)) + if ((uio->uio_resid & 3) || (uio->uio_resid < 8)) { + IPFERROR(110008); return EINVAL; + } -# if (BSD >= 199306) || defined(__FreeBSD__) || defined(__osf__) +# if BSD_GE_YEAR(199306) || defined(__FreeBSD__) || defined(__osf__) uio->uio_rw = UIO_READ; # endif - MUTEX_ENTER(&ipsl_mutex); - while ((sl_tail == sl_idx) && (su_tail == su_idx)) { -# if SOLARIS && defined(_KERNEL) - if (!cv_wait_sig(&ipslwait, &ipsl_mutex)) { - MUTEX_EXIT(&ipsl_mutex); + MUTEX_ENTER(&softs->ipsl_mutex); + while ((softs->sl_tail == softs->sl_idx) && + (softs->su_tail == softs->su_idx)) { +# if defined(_KERNEL) +# if SOLARIS + if (!cv_wait_sig(&softs->ipslwait, &softs->ipsl_mutex.ipf_lk)) { + MUTEX_EXIT(&softs->ipsl_mutex); + IPFERROR(110009); return EINTR; } -# else -# ifdef __hpux +# else +# ifdef __hpux { lock_t *l; - l = get_sleep_lock(&sl_tail); - err = sleep(&sl_tail, PZERO+1); + l = get_sleep_lock(&softs->sl_tail); + err = sleep(&softs->sl_tail, PZERO+1); if (err) { - MUTEX_EXIT(&ipsl_mutex); + MUTEX_EXIT(&softs->ipsl_mutex); + IPFERROR(110010); return EINTR; } spinunlock(l); } -# else /* __hpux */ -# ifdef __osf__ - err = mpsleep(&sl_tail, PSUSP|PCATCH, "ipl sleep", 0, - &ipsl_mutex, MS_LOCK_SIMPLE); - if (err) +# else /* __hpux */ +# ifdef __osf__ + err = mpsleep(&softs->sl_tail, PSUSP|PCATCH, "ipl sleep", 0, + &softs->ipsl_mutex, MS_LOCK_SIMPLE); + if (err) { + IPFERROR(110011); return EINTR; -# else - MUTEX_EXIT(&ipsl_mutex); - err = SLEEP(&sl_tail, "ipl sleep"); - if (err) + } +# else + MUTEX_EXIT(&softs->ipsl_mutex); + err = SLEEP(&softs->sl_tail, "ipl sleep"); + if (err) { + IPFERROR(110012); return EINTR; - MUTEX_ENTER(&ipsl_mutex); -# endif /* __osf__ */ -# endif /* __hpux */ -# endif /* SOLARIS */ + } + MUTEX_ENTER(&softs->ipsl_mutex); +# endif /* __osf__ */ +# endif /* __hpux */ +# endif /* SOLARIS */ +# endif /* _KERNEL */ } - MUTEX_EXIT(&ipsl_mutex); - READ_ENTER(&ipf_syncstate); - while ((sl_tail < sl_idx) && (uio->uio_resid > sizeof(*sl))) { - sl = synclog + sl_tail++; + while ((softs->sl_tail < softs->sl_idx) && + (uio->uio_resid > sizeof(*sl))) { + sl = softs->synclog + softs->sl_tail++; + MUTEX_EXIT(&softs->ipsl_mutex); err = UIOMOVE(sl, sizeof(*sl), UIO_READ, uio); if (err != 0) - break; + goto goterror; + MUTEX_ENTER(&softs->ipsl_mutex); } - while ((su_tail < su_idx) && (uio->uio_resid > sizeof(*su))) { - su = syncupd + su_tail; - su_tail++; + while ((softs->su_tail < softs->su_idx) && + (uio->uio_resid > sizeof(*su))) { + su = softs->syncupd + softs->su_tail; + softs->su_tail++; + MUTEX_EXIT(&softs->ipsl_mutex); err = UIOMOVE(su, sizeof(*su), UIO_READ, uio); if (err != 0) - break; + goto goterror; + MUTEX_ENTER(&softs->ipsl_mutex); if (su->sup_hdr.sm_sl != NULL) su->sup_hdr.sm_sl->sl_idx = -1; } - - MUTEX_ENTER(&ipf_syncadd); - if (su_tail == su_idx) - su_tail = su_idx = 0; - if (sl_tail == sl_idx) - sl_tail = sl_idx = 0; - MUTEX_EXIT(&ipf_syncadd); - RWLOCK_EXIT(&ipf_syncstate); + if (softs->sl_tail == softs->sl_idx) + softs->sl_tail = softs->sl_idx = 0; + if (softs->su_tail == softs->su_idx) + softs->su_tail = softs->su_idx = 0; + MUTEX_EXIT(&softs->ipsl_mutex); +goterror: return err; } /* ------------------------------------------------------------------------ */ -/* Function: ipfsync_state */ +/* Function: ipf_sync_state */ /* Returns: int - 0 == success, else error value. */ /* Parameters: sp(I) - pointer to sync packet data header */ /* uio(I) - pointer to user data for further information */ @@ -511,10 +691,13 @@ struct uio *uio; /* create a new state entry or update one. Deletion is left to the state */ /* structures being timed out correctly. */ /* ------------------------------------------------------------------------ */ -int ipfsync_state(sp, data) -synchdr_t *sp; -void *data; +static int +ipf_sync_state(softc, sp, data) + ipf_main_softc_t *softc; + synchdr_t *sp; + void *data; { + ipf_sync_softc_t *softs = softc->ipf_sync_soft; synctcp_update_t su; ipstate_t *is, sn; synclist_t *sl; @@ -522,7 +705,7 @@ void *data; u_int hv; int err = 0; - hv = sp->sm_num & (SYNC_STATETABSZ - 1); + hv = sp->sm_num & (softs->ipf_sync_state_tab_sz - 1); switch (sp->sm_cmd) { @@ -531,12 +714,14 @@ void *data; bcopy(data, &sn, sizeof(sn)); KMALLOC(is, ipstate_t *); if (is == NULL) { + IPFERROR(110013); err = ENOMEM; break; } KMALLOC(sl, synclist_t *); if (sl == NULL) { + IPFERROR(110014); err = ENOMEM; KFREE(is); break; @@ -545,23 +730,23 @@ void *data; bzero((char *)is, offsetof(ipstate_t, is_die)); bcopy((char *)&sn.is_die, (char *)&is->is_die, sizeof(*is) - offsetof(ipstate_t, is_die)); - ipfsync_storder(0, is); + ipf_sync_storder(0, is); /* * We need to find the same rule on the slave as was used on * the master to create this state entry. */ - READ_ENTER(&ipf_mutex); - fr = fr_getrulen(IPL_LOGIPF, sn.is_group, sn.is_rulen); + READ_ENTER(&softc->ipf_mutex); + fr = ipf_getrulen(softc, IPL_LOGIPF, sn.is_group, sn.is_rulen); if (fr != NULL) { MUTEX_ENTER(&fr->fr_lock); fr->fr_ref++; fr->fr_statecnt++; MUTEX_EXIT(&fr->fr_lock); } - RWLOCK_EXIT(&ipf_mutex); + RWLOCK_EXIT(&softc->ipf_mutex); - if (ipf_sync_debug > 4) + if (softs->ipf_sync_debug > 4) printf("[%d] Filter rules = %p\n", sp->sm_num, fr); is->is_rule = fr; @@ -571,16 +756,16 @@ void *data; sl->sl_ips = is; bcopy(sp, &sl->sl_hdr, sizeof(struct synchdr)); - WRITE_ENTER(&ipf_syncstate); - WRITE_ENTER(&ipf_state); + WRITE_ENTER(&softs->ipf_syncstate); + WRITE_ENTER(&softc->ipf_state); - sl->sl_pnext = syncstatetab + hv; - sl->sl_next = syncstatetab[hv]; - if (syncstatetab[hv] != NULL) - syncstatetab[hv]->sl_pnext = &sl->sl_next; - syncstatetab[hv] = sl; - MUTEX_DOWNGRADE(&ipf_syncstate); - fr_stinsert(is, sp->sm_rev); + sl->sl_pnext = softs->syncstatetab + hv; + sl->sl_next = softs->syncstatetab[hv]; + if (softs->syncstatetab[hv] != NULL) + softs->syncstatetab[hv]->sl_pnext = &sl->sl_next; + softs->syncstatetab[hv] = sl; + MUTEX_DOWNGRADE(&softs->ipf_syncstate); + ipf_state_insert(softc, is, sp->sm_rev); /* * Do not initialise the interface pointers for the state * entry as the full complement of interface names may not @@ -594,29 +779,31 @@ void *data; case SMC_UPDATE : bcopy(data, &su, sizeof(su)); - if (ipf_sync_debug > 4) + if (softs->ipf_sync_debug > 4) printf("[%d] Update age %lu state %d/%d \n", sp->sm_num, su.stu_age, su.stu_state[0], su.stu_state[1]); - READ_ENTER(&ipf_syncstate); - for (sl = syncstatetab[hv]; (sl != NULL); sl = sl->sl_next) + READ_ENTER(&softs->ipf_syncstate); + for (sl = softs->syncstatetab[hv]; (sl != NULL); + sl = sl->sl_next) if (sl->sl_hdr.sm_num == sp->sm_num) break; if (sl == NULL) { - if (ipf_sync_debug > 1) + if (softs->ipf_sync_debug > 1) printf("[%d] State not found - can't update\n", sp->sm_num); - RWLOCK_EXIT(&ipf_syncstate); + RWLOCK_EXIT(&softs->ipf_syncstate); + IPFERROR(110015); err = ENOENT; break; } - READ_ENTER(&ipf_state); + READ_ENTER(&softc->ipf_state); - if (ipf_sync_debug > 6) - printf("[%d] Data from state v:%d p:%d cmd:%d table:%d rev:%d\n", - sp->sm_num, sl->sl_hdr.sm_v, sl->sl_hdr.sm_p, + if (softs->ipf_sync_debug > 6) + printf("[%d] Data from state v:%d p:%d cmd:%d table:%d rev:%d\n", + sp->sm_num, sl->sl_hdr.sm_v, sl->sl_hdr.sm_p, sl->sl_hdr.sm_cmd, sl->sl_hdr.sm_table, sl->sl_hdr.sm_rev); @@ -640,56 +827,97 @@ void *data; break; } - if (ipf_sync_debug > 6) + if (softs->ipf_sync_debug > 6) printf("[%d] Setting timers for state\n", sp->sm_num); - fr_setstatequeue(is, sp->sm_rev); + ipf_state_setqueue(softc, is, sp->sm_rev); MUTEX_EXIT(&is->is_lock); break; default : + IPFERROR(110016); err = EINVAL; break; } if (err == 0) { - RWLOCK_EXIT(&ipf_state); - RWLOCK_EXIT(&ipf_syncstate); + RWLOCK_EXIT(&softc->ipf_state); + RWLOCK_EXIT(&softs->ipf_syncstate); } - if (ipf_sync_debug > 6) + if (softs->ipf_sync_debug > 6) printf("[%d] Update completed with error %d\n", sp->sm_num, err); return err; } -# endif /* _KERNEL */ /* ------------------------------------------------------------------------ */ -/* Function: ipfsync_del */ +/* Function: ipf_sync_del */ /* Returns: Nil */ /* Parameters: sl(I) - pointer to synclist object to delete */ /* */ -/* Deletes an object from the synclist table and free's its memory. */ +/* Deletes an object from the synclist. */ /* ------------------------------------------------------------------------ */ -void ipfsync_del(sl) -synclist_t *sl; +static void +ipf_sync_del(softs, sl) + ipf_sync_softc_t *softs; + synclist_t *sl; { - WRITE_ENTER(&ipf_syncstate); *sl->sl_pnext = sl->sl_next; if (sl->sl_next != NULL) sl->sl_next->sl_pnext = sl->sl_pnext; if (sl->sl_idx != -1) - syncupd[sl->sl_idx].sup_hdr.sm_sl = NULL; - RWLOCK_EXIT(&ipf_syncstate); + softs->syncupd[sl->sl_idx].sup_hdr.sm_sl = NULL; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_sync_del_state */ +/* Returns: Nil */ +/* Parameters: sl(I) - pointer to synclist object to delete */ +/* */ +/* Deletes an object from the synclist state table and free's its memory. */ +/* ------------------------------------------------------------------------ */ +void +ipf_sync_del_state(arg, sl) + void *arg; + synclist_t *sl; +{ + ipf_sync_softc_t *softs = arg; + + WRITE_ENTER(&softs->ipf_syncstate); + ipf_sync_del(softs, sl); + RWLOCK_EXIT(&softs->ipf_syncstate); KFREE(sl); } /* ------------------------------------------------------------------------ */ -/* Function: ipfsync_nat */ +/* Function: ipf_sync_del_nat */ +/* Returns: Nil */ +/* Parameters: sl(I) - pointer to synclist object to delete */ +/* */ +/* Deletes an object from the synclist nat table and free's its memory. */ +/* ------------------------------------------------------------------------ */ +void +ipf_sync_del_nat(arg, sl) + void *arg; + synclist_t *sl; +{ + ipf_sync_softc_t *softs = arg; + + WRITE_ENTER(&softs->ipf_syncnat); + ipf_sync_del(softs, sl); + RWLOCK_EXIT(&softs->ipf_syncnat); + KFREE(sl); +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_sync_nat */ /* Returns: int - 0 == success, else error value. */ /* Parameters: sp(I) - pointer to sync packet data header */ /* uio(I) - pointer to user data for further information */ @@ -700,29 +928,34 @@ synclist_t *sl; /* create a new NAT entry or update one. Deletion is left to the NAT */ /* structures being timed out correctly. */ /* ------------------------------------------------------------------------ */ -int ipfsync_nat(sp, data) -synchdr_t *sp; -void *data; +static int +ipf_sync_nat(softc, sp, data) + ipf_main_softc_t *softc; + synchdr_t *sp; + void *data; { + ipf_sync_softc_t *softs = softc->ipf_sync_soft; syncupdent_t su; nat_t *n, *nat; synclist_t *sl; u_int hv = 0; int err; - READ_ENTER(&ipf_syncstate); + READ_ENTER(&softs->ipf_syncnat); switch (sp->sm_cmd) { case SMC_CREATE : KMALLOC(n, nat_t *); if (n == NULL) { + IPFERROR(110017); err = ENOMEM; break; } KMALLOC(sl, synclist_t *); if (sl == NULL) { + IPFERROR(110018); err = ENOMEM; KFREE(n); break; @@ -732,59 +965,63 @@ void *data; bzero((char *)n, offsetof(nat_t, nat_age)); bcopy((char *)&nat->nat_age, (char *)&n->nat_age, sizeof(*n) - offsetof(nat_t, nat_age)); - ipfsync_natorder(0, n); + ipf_sync_natorder(0, n); n->nat_sync = sl; + n->nat_rev = sl->sl_rev; sl->sl_idx = -1; sl->sl_ipn = n; sl->sl_num = ntohl(sp->sm_num); - WRITE_ENTER(&ipf_nat); - sl->sl_pnext = syncstatetab + hv; - sl->sl_next = syncstatetab[hv]; - if (syncstatetab[hv] != NULL) - syncstatetab[hv]->sl_pnext = &sl->sl_next; - syncstatetab[hv] = sl; - nat_insert(n, sl->sl_rev); - RWLOCK_EXIT(&ipf_nat); + WRITE_ENTER(&softc->ipf_nat); + sl->sl_pnext = softs->syncnattab + hv; + sl->sl_next = softs->syncnattab[hv]; + if (softs->syncnattab[hv] != NULL) + softs->syncnattab[hv]->sl_pnext = &sl->sl_next; + softs->syncnattab[hv] = sl; + (void) ipf_nat_insert(softc, softc->ipf_nat_soft, n); + RWLOCK_EXIT(&softc->ipf_nat); break; case SMC_UPDATE : bcopy(data, &su, sizeof(su)); - READ_ENTER(&ipf_syncstate); - for (sl = syncstatetab[hv]; (sl != NULL); sl = sl->sl_next) + for (sl = softs->syncnattab[hv]; (sl != NULL); + sl = sl->sl_next) if (sl->sl_hdr.sm_num == sp->sm_num) break; if (sl == NULL) { + IPFERROR(110019); err = ENOENT; break; } - READ_ENTER(&ipf_nat); + READ_ENTER(&softc->ipf_nat); nat = sl->sl_ipn; + nat->nat_rev = sl->sl_rev; MUTEX_ENTER(&nat->nat_lock); - fr_setnatqueue(nat, sl->sl_rev); + ipf_nat_setqueue(softc, softc->ipf_nat_soft, nat); MUTEX_EXIT(&nat->nat_lock); - RWLOCK_EXIT(&ipf_nat); + RWLOCK_EXIT(&softc->ipf_nat); break; default : + IPFERROR(110020); err = EINVAL; break; } - RWLOCK_EXIT(&ipf_syncstate); + RWLOCK_EXIT(&softs->ipf_syncnat); return 0; } /* ------------------------------------------------------------------------ */ -/* Function: ipfsync_new */ +/* Function: ipf_sync_new */ /* Returns: synclist_t* - NULL == failure, else pointer to new synclist */ /* data structure. */ /* Parameters: tab(I) - type of synclist_t to create */ @@ -794,43 +1031,36 @@ void *data; /* Creates a new sync table entry and notifies any sleepers that it's there */ /* waiting to be processed. */ /* ------------------------------------------------------------------------ */ -synclist_t *ipfsync_new(tab, fin, ptr) -int tab; -fr_info_t *fin; -void *ptr; +synclist_t * +ipf_sync_new(softc, tab, fin, ptr) + ipf_main_softc_t *softc; + int tab; + fr_info_t *fin; + void *ptr; { + ipf_sync_softc_t *softs = softc->ipf_sync_soft; synclist_t *sl, *ss; synclogent_t *sle; u_int hv, sz; - if (sl_idx == SYNCLOG_SZ) + if (softs->sl_idx == softs->ipf_sync_log_sz) return NULL; KMALLOC(sl, synclist_t *); if (sl == NULL) return NULL; - MUTEX_ENTER(&ipf_syncadd); + MUTEX_ENTER(&softs->ipf_syncadd); /* * Get a unique number for this synclist_t. The number is only meant * to be unique for the lifetime of the structure and may be reused * later. */ - ipf_syncnum++; - if (ipf_syncnum == 0) { - ipf_syncnum = 1; - ipf_syncwrap = 1; + softs->ipf_sync_num++; + if (softs->ipf_sync_num == 0) { + softs->ipf_sync_num = 1; + softs->ipf_sync_wrap++; } - hv = ipf_syncnum & (SYNC_STATETABSZ - 1); - while (ipf_syncwrap != 0) { - for (ss = syncstatetab[hv]; ss; ss = ss->sl_next) - if (ss->sl_hdr.sm_num == ipf_syncnum) - break; - if (ss == NULL) - break; - ipf_syncnum++; - hv = ipf_syncnum & (SYNC_STATETABSZ - 1); - } /* * Use the synch number of the object as the hash key. Should end up * with relatively even distribution over time. @@ -839,11 +1069,48 @@ void *ptr; * nth connection they make, where n is a value in the interval * [0, SYNC_STATETABSZ-1]. */ - sl->sl_pnext = syncstatetab + hv; - sl->sl_next = syncstatetab[hv]; - syncstatetab[hv] = sl; - sl->sl_num = ipf_syncnum; - MUTEX_EXIT(&ipf_syncadd); + switch (tab) + { + case SMC_STATE : + hv = softs->ipf_sync_num & (softs->ipf_sync_state_tab_sz - 1); + while (softs->ipf_sync_wrap != 0) { + for (ss = softs->syncstatetab[hv]; ss; ss = ss->sl_next) + if (ss->sl_hdr.sm_num == softs->ipf_sync_num) + break; + if (ss == NULL) + break; + softs->ipf_sync_num++; + hv = softs->ipf_sync_num & + (softs->ipf_sync_state_tab_sz - 1); + } + sl->sl_pnext = softs->syncstatetab + hv; + sl->sl_next = softs->syncstatetab[hv]; + softs->syncstatetab[hv] = sl; + break; + + case SMC_NAT : + hv = softs->ipf_sync_num & (softs->ipf_sync_nat_tab_sz - 1); + while (softs->ipf_sync_wrap != 0) { + for (ss = softs->syncnattab[hv]; ss; ss = ss->sl_next) + if (ss->sl_hdr.sm_num == softs->ipf_sync_num) + break; + if (ss == NULL) + break; + softs->ipf_sync_num++; + hv = softs->ipf_sync_num & + (softs->ipf_sync_nat_tab_sz - 1); + } + sl->sl_pnext = softs->syncnattab + hv; + sl->sl_next = softs->syncnattab[hv]; + softs->syncnattab[hv] = sl; + break; + + default : + break; + } + + sl->sl_num = softs->ipf_sync_num; + MUTEX_EXIT(&softs->ipf_syncadd); sl->sl_magic = htonl(SYNHDRMAGIC); sl->sl_v = fin->fin_v; @@ -868,8 +1135,8 @@ void *ptr; * Create the log entry to be read by a user daemon. When it has been * finished and put on the queue, send a signal to wakeup any waiters. */ - MUTEX_ENTER(&ipf_syncadd); - sle = synclog + sl_idx++; + MUTEX_ENTER(&softs->ipf_syncadd); + sle = softs->synclog + softs->sl_idx++; bcopy((char *)&sl->sl_hdr, (char *)&sle->sle_hdr, sizeof(sle->sle_hdr)); sle->sle_hdr.sm_num = htonl(sle->sle_hdr.sm_num); @@ -877,31 +1144,20 @@ void *ptr; if (ptr != NULL) { bcopy((char *)ptr, (char *)&sle->sle_un, sz); if (tab == SMC_STATE) { - ipfsync_storder(1, &sle->sle_un.sleu_ips); + ipf_sync_storder(1, &sle->sle_un.sleu_ips); } else if (tab == SMC_NAT) { - ipfsync_natorder(1, &sle->sle_un.sleu_ipn); + ipf_sync_natorder(1, &sle->sle_un.sleu_ipn); } } - MUTEX_EXIT(&ipf_syncadd); + MUTEX_EXIT(&softs->ipf_syncadd); - MUTEX_ENTER(&ipsl_mutex); -# if SOLARIS -# ifdef _KERNEL - cv_signal(&ipslwait); -# endif - MUTEX_EXIT(&ipsl_mutex); -# else - MUTEX_EXIT(&ipsl_mutex); -# ifdef _KERNEL - wakeup(&sl_tail); -# endif -# endif + ipf_sync_wakeup(softc); return sl; } /* ------------------------------------------------------------------------ */ -/* Function: ipfsync_update */ +/* Function: ipf_sync_update */ /* Returns: Nil */ /* Parameters: tab(I) - type of synclist_t to create */ /* fin(I) - pointer to packet information */ @@ -910,24 +1166,36 @@ void *ptr; /* For outbound packets, only, create an sync update record for the user */ /* process to read. */ /* ------------------------------------------------------------------------ */ -void ipfsync_update(tab, fin, sl) -int tab; -fr_info_t *fin; -synclist_t *sl; +void +ipf_sync_update(softc, tab, fin, sl) + ipf_main_softc_t *softc; + int tab; + fr_info_t *fin; + synclist_t *sl; { + ipf_sync_softc_t *softs = softc->ipf_sync_soft; synctcp_update_t *st; syncupdent_t *slu; ipstate_t *ips; nat_t *nat; + ipfrwlock_t *lock; if (fin->fin_out == 0 || sl == NULL) return; - WRITE_ENTER(&ipf_syncstate); - MUTEX_ENTER(&ipf_syncadd); + if (tab == SMC_STATE) { + lock = &softs->ipf_syncstate; + } else { + lock = &softs->ipf_syncnat; + } + + READ_ENTER(lock); if (sl->sl_idx == -1) { - slu = syncupd + su_idx; - sl->sl_idx = su_idx++; + MUTEX_ENTER(&softs->ipf_syncadd); + slu = softs->syncupd + softs->su_idx; + sl->sl_idx = softs->su_idx++; + MUTEX_EXIT(&softs->ipf_syncadd); + bcopy((char *)&sl->sl_hdr, (char *)&slu->sup_hdr, sizeof(slu->sup_hdr)); slu->sup_hdr.sm_magic = htonl(SYNHDRMAGIC); @@ -944,9 +1212,7 @@ synclist_t *sl; } # endif } else - slu = syncupd + sl->sl_idx; - MUTEX_EXIT(&ipf_syncadd); - MUTEX_DOWNGRADE(&ipf_syncstate); + slu = softs->syncupd + sl->sl_idx; /* * Only TCP has complex timeouts, others just use default timeouts. @@ -970,25 +1236,61 @@ synclist_t *sl; st->stu_age = htonl(nat->nat_age); } } - RWLOCK_EXIT(&ipf_syncstate); + RWLOCK_EXIT(lock); - MUTEX_ENTER(&ipsl_mutex); -# if SOLARIS -# ifdef _KERNEL - cv_signal(&ipslwait); -# endif - MUTEX_EXIT(&ipsl_mutex); -# else - MUTEX_EXIT(&ipsl_mutex); -# ifdef _KERNEL - wakeup(&sl_tail); -# endif -# endif + ipf_sync_wakeup(softc); } /* ------------------------------------------------------------------------ */ -/* Function: fr_sync_ioctl */ +/* Function: ipf_sync_flush_table */ +/* Returns: int - number of entries freed by flushing table */ +/* Parameters: tabsize(I) - size of the array pointed to by table */ +/* table(I) - pointer to sync table to empty */ +/* */ +/* Walk through a table of sync entries and free each one. It is assumed */ +/* that some lock is held so that nobody else tries to access the table */ +/* during this cleanup. */ +/* ------------------------------------------------------------------------ */ +static int +ipf_sync_flush_table(softs, tabsize, table) + ipf_sync_softc_t *softs; + int tabsize; + synclist_t **table; +{ + synclist_t *sl; + int i, items; + + items = 0; + + for (i = 0; i < tabsize; i++) { + while ((sl = table[i]) != NULL) { + switch (sl->sl_table) { + case SMC_STATE : + if (sl->sl_ips != NULL) + sl->sl_ips->is_sync = NULL; + break; + case SMC_NAT : + if (sl->sl_ipn != NULL) + sl->sl_ipn->nat_sync = NULL; + break; + } + if (sl->sl_next != NULL) + sl->sl_next->sl_pnext = sl->sl_pnext; + table[i] = sl->sl_next; + if (sl->sl_idx != -1) + softs->syncupd[sl->sl_idx].sup_hdr.sm_sl = NULL; + KFREE(sl); + items++; + } + } + + return items; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_sync_ioctl */ /* Returns: int - 0 == success, != 0 == failure */ /* Parameters: data(I) - pointer to ioctl data */ /* cmd(I) - ioctl command integer */ @@ -997,24 +1299,199 @@ synclist_t *sl; /* This function currently does not handle any ioctls and so just returns */ /* EINVAL on all occasions. */ /* ------------------------------------------------------------------------ */ -int fr_sync_ioctl(data, cmd, mode, uid, ctx) -caddr_t data; -ioctlcmd_t cmd; -int mode, uid; -void *ctx; +int +ipf_sync_ioctl(softc, data, cmd, mode, uid, ctx) + ipf_main_softc_t *softc; + caddr_t data; + ioctlcmd_t cmd; + int mode, uid; + void *ctx; { - return EINVAL; + ipf_sync_softc_t *softs = softc->ipf_sync_soft; + int error, i; + SPL_INT(s); + + switch (cmd) + { + case SIOCIPFFL: + error = BCOPYIN(data, &i, sizeof(i)); + if (error != 0) { + IPFERROR(110023); + error = EFAULT; + break; + } + + switch (i) + { + case SMC_RLOG : + SPL_NET(s); + MUTEX_ENTER(&softs->ipsl_mutex); + i = (softs->sl_tail - softs->sl_idx) + + (softs->su_tail - softs->su_idx); + softs->sl_idx = 0; + softs->su_idx = 0; + softs->sl_tail = 0; + softs->su_tail = 0; + MUTEX_EXIT(&softs->ipsl_mutex); + SPL_X(s); + break; + + case SMC_NAT : + SPL_NET(s); + WRITE_ENTER(&softs->ipf_syncnat); + i = ipf_sync_flush_table(softs, SYNC_NATTABSZ, + softs->syncnattab); + RWLOCK_EXIT(&softs->ipf_syncnat); + SPL_X(s); + break; + + case SMC_STATE : + SPL_NET(s); + WRITE_ENTER(&softs->ipf_syncstate); + i = ipf_sync_flush_table(softs, SYNC_STATETABSZ, + softs->syncstatetab); + RWLOCK_EXIT(&softs->ipf_syncstate); + SPL_X(s); + break; + } + + error = BCOPYOUT(&i, data, sizeof(i)); + if (error != 0) { + IPFERROR(110022); + error = EFAULT; + } + break; + + default : + IPFERROR(110021); + error = EINVAL; + break; + } + + return error; } -int ipfsync_canread() +/* ------------------------------------------------------------------------ */ +/* Function: ipf_sync_canread */ +/* Returns: int - 0 == success, != 0 == failure */ +/* Parameters: Nil */ +/* */ +/* This function provides input to the poll handler about whether or not */ +/* there is data waiting to be read from the /dev/ipsync device. */ +/* ------------------------------------------------------------------------ */ +int +ipf_sync_canread(arg) + void *arg; { - return !((sl_tail == sl_idx) && (su_tail == su_idx)); + ipf_sync_softc_t *softs = arg; + return !((softs->sl_tail == softs->sl_idx) && + (softs->su_tail == softs->su_idx)); } -int ipfsync_canwrite() +/* ------------------------------------------------------------------------ */ +/* Function: ipf_sync_canwrite */ +/* Returns: int - 1 == can always write */ +/* Parameters: Nil */ +/* */ +/* This function lets the poll handler know that it is always ready willing */ +/* to accept write events. */ +/* XXX Maybe this should return false if the sync table is full? */ +/* ------------------------------------------------------------------------ */ +int +ipf_sync_canwrite(arg) + void *arg; { return 1; } -#endif /* IPFILTER_SYNC */ + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_sync_wakeup */ +/* Parameters: Nil */ +/* Returns: Nil */ +/* */ +/* This function implements the heuristics that decide how often to */ +/* generate a poll wakeup for programs that are waiting for information */ +/* about when they can do a read on /dev/ipsync. */ +/* */ +/* There are three different considerations here: */ +/* - do not keep a program waiting too long: ipf_sync_wake_interval is the */ +/* maximum number of ipf ticks to let pass by; */ +/* - do not let the queue of ouststanding things to generate notifies for */ +/* get too full (ipf_sync_queue_high_wm is the high water mark); */ +/* - do not let too many events get collapsed in before deciding that the */ +/* other host(s) need an update (ipf_sync_event_high_wm is the high water */ +/* mark for this counter.) */ +/* ------------------------------------------------------------------------ */ +static void +ipf_sync_wakeup(softc) + ipf_main_softc_t *softc; +{ + ipf_sync_softc_t *softs = softc->ipf_sync_soft; + + softs->ipf_sync_events++; + if ((softc->ipf_ticks > + softs->ipf_sync_lastwakeup + softs->ipf_sync_wake_interval) || + (softs->ipf_sync_events > softs->ipf_sync_event_high_wm) || + ((softs->sl_tail - softs->sl_idx) > + softs->ipf_sync_queue_high_wm) || + ((softs->su_tail - softs->su_idx) > + softs->ipf_sync_queue_high_wm)) { + + ipf_sync_poll_wakeup(softc); + } +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_sync_poll_wakeup */ +/* Parameters: Nil */ +/* Returns: Nil */ +/* */ +/* Deliver a poll wakeup and reset counters for two of the three heuristics */ +/* ------------------------------------------------------------------------ */ +static void +ipf_sync_poll_wakeup(softc) + ipf_main_softc_t *softc; +{ + ipf_sync_softc_t *softs = softc->ipf_sync_soft; + + softs->ipf_sync_events = 0; + softs->ipf_sync_lastwakeup = softc->ipf_ticks; + +# ifdef _KERNEL +# if SOLARIS + MUTEX_ENTER(&softs->ipsl_mutex); + cv_signal(&softs->ipslwait); + MUTEX_EXIT(&softs->ipsl_mutex); + pollwakeup(&softc->ipf_poll_head[IPL_LOGSYNC], POLLIN|POLLRDNORM); +# else + WAKEUP(&softs->sl_tail, 0); + POLLWAKEUP(IPL_LOGSYNC); +# endif +# endif +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_sync_expire */ +/* Parameters: Nil */ +/* Returns: Nil */ +/* */ +/* This is the function called even ipf_tick. It implements one of the */ +/* three heuristics above *IF* there are events waiting. */ +/* ------------------------------------------------------------------------ */ +void +ipf_sync_expire(softc) + ipf_main_softc_t *softc; +{ + ipf_sync_softc_t *softs = softc->ipf_sync_soft; + + if ((softs->ipf_sync_events > 0) && + (softc->ipf_ticks > + softs->ipf_sync_lastwakeup + softs->ipf_sync_wake_interval)) { + ipf_sync_poll_wakeup(softc); + } +} diff --git a/sys/contrib/ipfilter/netinet/ip_sync.h b/sys/contrib/ipfilter/netinet/ip_sync.h index 8104db3f2c3..d9d6d41046e 100644 --- a/sys/contrib/ipfilter/netinet/ip_sync.h +++ b/sys/contrib/ipfilter/netinet/ip_sync.h @@ -1,10 +1,10 @@ /* - * Copyright (C) 1993-2001 by Darren Reed. + * Copyright (C) 2012 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. * * @(#)ip_fil.h 1.35 6/5/96 - * $Id: ip_sync.h,v 2.11.2.4 2006/07/14 06:12:20 darrenr Exp $ + * $Id$ */ #ifndef __IP_SYNC_H__ @@ -36,6 +36,7 @@ typedef struct synchdr { /* * Tables */ +#define SMC_RLOG -2 /* Only used with SIOCIPFFL */ #define SMC_NAT 0 #define SMC_STATE 1 #define SMC_MAXTBL 1 @@ -99,19 +100,22 @@ typedef struct syncupdent { /* 28 or 32 bytes */ struct synctcp_update sup_tcp; } syncupdent_t; -extern synclogent_t synclog[SYNCLOG_SZ]; +extern void *ipf_sync_create __P((ipf_main_softc_t *)); +extern int ipf_sync_soft_init __P((ipf_main_softc_t *, void *)); +extern int ipf_sync_soft_fini __P((ipf_main_softc_t *, void *)); +extern int ipf_sync_canread __P((void *)); +extern int ipf_sync_canwrite __P((void *)); +extern void ipf_sync_del_nat __P((void *, synclist_t *)); +extern void ipf_sync_del_state __P((void *, synclist_t *)); +extern int ipf_sync_init __P((void)); +extern int ipf_sync_ioctl __P((ipf_main_softc_t *, caddr_t, ioctlcmd_t, int, int, void *)); +extern synclist_t *ipf_sync_new __P((ipf_main_softc_t *, int, fr_info_t *, void *)); +extern int ipf_sync_read __P((ipf_main_softc_t *, struct uio *uio)); +extern int ipf_sync_write __P((ipf_main_softc_t *, struct uio *uio)); +extern int ipf_sync_main_unload __P((void)); +extern void ipf_sync_update __P((ipf_main_softc_t *, int, fr_info_t *, synclist_t *)); +extern void ipf_sync_expire __P((ipf_main_softc_t *)); +extern void ipf_sync_soft_destroy __P((ipf_main_softc_t *, void *)); +extern void *ipf_sync_soft_create __P((ipf_main_softc_t *)); - -extern int fr_sync_ioctl __P((caddr_t, ioctlcmd_t, int, int, void *)); -extern synclist_t *ipfsync_new __P((int, fr_info_t *, void *)); -extern void ipfsync_del __P((synclist_t *)); -extern void ipfsync_update __P((int, fr_info_t *, synclist_t *)); -extern int ipfsync_init __P((void)); -extern int ipfsync_nat __P((synchdr_t *sp, void *data)); -extern int ipfsync_state __P((synchdr_t *sp, void *data)); -extern int ipfsync_read __P((struct uio *uio)); -extern int ipfsync_write __P((struct uio *uio)); -extern int ipfsync_canread __P((void)); -extern int ipfsync_canwrite __P((void)); - -#endif /* IP_SYNC */ +#endif /* __IP_SYNC_H__ */ diff --git a/sys/contrib/ipfilter/netinet/ip_tftp_pxy.c b/sys/contrib/ipfilter/netinet/ip_tftp_pxy.c new file mode 100644 index 00000000000..409b982fe10 --- /dev/null +++ b/sys/contrib/ipfilter/netinet/ip_tftp_pxy.c @@ -0,0 +1,508 @@ +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + * + * $Id: ip_tftp_pxy.c,v 1.1.2.9 2012/07/22 08:04:23 darren_r Exp $ + */ + +#define IPF_TFTP_PROXY + +typedef struct ipf_tftp_softc_s { + int ipf_p_tftp_readonly; + ipftuneable_t *ipf_p_tftp_tune; +} ipf_tftp_softc_t; + +int ipf_p_tftp_backchannel __P((fr_info_t *, ap_session_t *, nat_t *)); +int ipf_p_tftp_client __P((ipf_tftp_softc_t *, fr_info_t *, ap_session_t *, + nat_t *)); +int ipf_p_tftp_in __P((void *, fr_info_t *, ap_session_t *, nat_t *)); +void ipf_p_tftp_main_load __P((void)); +void ipf_p_tftp_main_unload __P((void)); +int ipf_p_tftp_new __P((void *, fr_info_t *, ap_session_t *, nat_t *)); +void ipf_p_tftp_del __P((ipf_main_softc_t *, ap_session_t *)); +int ipf_p_tftp_out __P((void *, fr_info_t *, ap_session_t *, nat_t *)); +int ipf_p_tftp_server __P((ipf_tftp_softc_t *, fr_info_t *, ap_session_t *, + nat_t *)); +void *ipf_p_tftp_soft_create __P((ipf_main_softc_t *)); +void ipf_p_tftp_soft_destroy __P((ipf_main_softc_t *, void *)); + +static frentry_t tftpfr; +static int tftp_proxy_init = 0; + +typedef enum tftp_cmd_e { + TFTP_CMD_READ = 1, + TFTP_CMD_WRITE = 2, + TFTP_CMD_DATA = 3, + TFTP_CMD_ACK = 4, + TFTP_CMD_ERROR = 5 +} tftp_cmd_t; + +typedef struct tftpinfo { + tftp_cmd_t ti_lastcmd; + int ti_nextblk; + int ti_lastblk; + int ti_lasterror; + char ti_filename[80]; + ipnat_t *ti_rule; +} tftpinfo_t; + +static ipftuneable_t ipf_tftp_tuneables[] = { + { { (void *)offsetof(ipf_tftp_softc_t, ipf_p_tftp_readonly) }, + "tftp_read_only", 0, 1, + stsizeof(ipf_tftp_softc_t, ipf_p_tftp_readonly), + 0, NULL, NULL }, + { { NULL }, NULL, 0, 0, 0, 0, NULL, NULL } +}; + + +/* + * TFTP application proxy initialization. + */ +void +ipf_p_tftp_main_load() +{ + + bzero((char *)&tftpfr, sizeof(tftpfr)); + tftpfr.fr_ref = 1; + tftpfr.fr_flags = FR_INQUE|FR_PASS|FR_QUICK|FR_KEEPSTATE; + MUTEX_INIT(&tftpfr.fr_lock, "TFTP proxy rule lock"); + tftp_proxy_init = 1; +} + + +void +ipf_p_tftp_main_unload() +{ + + if (tftp_proxy_init == 1) { + MUTEX_DESTROY(&tftpfr.fr_lock); + tftp_proxy_init = 0; + } +} + + +void * +ipf_p_tftp_soft_create(softc) + ipf_main_softc_t *softc; +{ + ipf_tftp_softc_t *softt; + + KMALLOC(softt, ipf_tftp_softc_t *); + if (softt == NULL) + return NULL; + + bzero((char *)softt, sizeof(*softt)); + + softt->ipf_p_tftp_tune = ipf_tune_array_copy(softt, + sizeof(ipf_tftp_tuneables), + ipf_tftp_tuneables); + if (softt->ipf_p_tftp_tune == NULL) { + ipf_p_tftp_soft_destroy(softc, softt); + return NULL; + } + if (ipf_tune_array_link(softc, softt->ipf_p_tftp_tune) == -1) { + ipf_p_tftp_soft_destroy(softc, softt); + return NULL; + } + + softt->ipf_p_tftp_readonly = 1; + + return softt; +} + + +void +ipf_p_tftp_soft_destroy(softc, arg) + ipf_main_softc_t *softc; + void *arg; +{ + ipf_tftp_softc_t *softt = arg; + + if (softt->ipf_p_tftp_tune != NULL) { + ipf_tune_array_unlink(softc, softt->ipf_p_tftp_tune); + KFREES(softt->ipf_p_tftp_tune, sizeof(ipf_tftp_tuneables)); + softt->ipf_p_tftp_tune = NULL; + } + + KFREE(softt); +} + + +int +ipf_p_tftp_out(arg, fin, aps, nat) + void *arg; + fr_info_t *fin; + ap_session_t *aps; + nat_t *nat; +{ + ipf_tftp_softc_t *softt = arg; + + fin->fin_flx |= FI_NOWILD; + if (nat->nat_dir == NAT_OUTBOUND) + return ipf_p_tftp_client(softt, fin, aps, nat); + return ipf_p_tftp_server(softt, fin, aps, nat); +} + + +int +ipf_p_tftp_in(arg, fin, aps, nat) + void *arg; + fr_info_t *fin; + ap_session_t *aps; + nat_t *nat; +{ + ipf_tftp_softc_t *softt = arg; + + fin->fin_flx |= FI_NOWILD; + if (nat->nat_dir == NAT_INBOUND) + return ipf_p_tftp_client(softt, fin, aps, nat); + return ipf_p_tftp_server(softt, fin, aps, nat); +} + + +int +ipf_p_tftp_new(arg, fin, aps, nat) + void *arg; + fr_info_t *fin; + ap_session_t *aps; + nat_t *nat; +{ + udphdr_t *udp; + tftpinfo_t *ti; + ipnat_t *ipn; + ipnat_t *np; + int size; + + fin = fin; /* LINT */ + + np = nat->nat_ptr; + size = np->in_size; + + KMALLOC(ti, tftpinfo_t *); + if (ti == NULL) + return -1; + KMALLOCS(ipn, ipnat_t *, size); + if (ipn == NULL) { + KFREE(ti); + return -1; + } + + aps->aps_data = ti; + aps->aps_psiz = sizeof(*ti); + bzero((char *)ti, sizeof(*ti)); + bzero((char *)ipn, size); + ti->ti_rule = ipn; + + udp = (udphdr_t *)fin->fin_dp; + aps->aps_sport = udp->uh_sport; + aps->aps_dport = udp->uh_dport; + + ipn->in_size = size; + ipn->in_apr = NULL; + ipn->in_use = 1; + ipn->in_hits = 1; + ipn->in_ippip = 1; + ipn->in_pr[0] = IPPROTO_UDP; + ipn->in_pr[1] = IPPROTO_UDP; + ipn->in_ifps[0] = nat->nat_ifps[0]; + ipn->in_ifps[1] = nat->nat_ifps[1]; + ipn->in_v[0] = nat->nat_ptr->in_v[1]; + ipn->in_v[1] = nat->nat_ptr->in_v[0]; + ipn->in_flags = IPN_UDP|IPN_FIXEDDPORT|IPN_PROXYRULE; + + ipn->in_nsrcip6 = nat->nat_odst6; + ipn->in_osrcip6 = nat->nat_ndst6; + + if ((np->in_redir & NAT_REDIRECT) != 0) { + ipn->in_redir = NAT_MAP; + if (ipn->in_v[0] == 4) { + ipn->in_snip = ntohl(nat->nat_odstaddr); + ipn->in_dnip = ntohl(nat->nat_nsrcaddr); + } else { +#ifdef USE_INET6 + ipn->in_snip6 = nat->nat_odst6; + ipn->in_dnip6 = nat->nat_nsrc6; +#endif + } + ipn->in_ndstip6 = nat->nat_nsrc6; + ipn->in_odstip6 = nat->nat_osrc6; + } else { + ipn->in_redir = NAT_REDIRECT; + if (ipn->in_v[0] == 4) { + ipn->in_snip = ntohl(nat->nat_odstaddr); + ipn->in_dnip = ntohl(nat->nat_osrcaddr); + } else { +#ifdef USE_INET6 + ipn->in_snip6 = nat->nat_odst6; + ipn->in_dnip6 = nat->nat_osrc6; +#endif + } + ipn->in_ndstip6 = nat->nat_osrc6; + ipn->in_odstip6 = nat->nat_nsrc6; + } + ipn->in_odport = htons(fin->fin_sport); + ipn->in_ndport = htons(fin->fin_sport); + + IP6_SETONES(&ipn->in_osrcmsk6); + IP6_SETONES(&ipn->in_nsrcmsk6); + IP6_SETONES(&ipn->in_odstmsk6); + IP6_SETONES(&ipn->in_ndstmsk6); + MUTEX_INIT(&ipn->in_lock, "tftp proxy NAT rule"); + + ipn->in_namelen = np->in_namelen; + bcopy(np->in_names, ipn->in_ifnames, ipn->in_namelen); + ipn->in_ifnames[0] = np->in_ifnames[0]; + ipn->in_ifnames[1] = np->in_ifnames[1]; + + ti->ti_lastcmd = 0; + + return 0; +} + + +void +ipf_p_tftp_del(softc, aps) + ipf_main_softc_t *softc; + ap_session_t *aps; +{ + tftpinfo_t *tftp; + + tftp = aps->aps_data; + if (tftp != NULL) { + tftp->ti_rule->in_flags |= IPN_DELETE; + ipf_nat_rule_deref(softc, &tftp->ti_rule); + } +} + + +/* + * Setup for a new TFTP proxy. + */ +int +ipf_p_tftp_backchannel(fin, aps, nat) + fr_info_t *fin; + ap_session_t *aps; + nat_t *nat; +{ + ipf_main_softc_t *softc = fin->fin_main_soft; +#ifdef USE_MUTEXES + ipf_nat_softc_t *softn = softc->ipf_nat_soft; +#endif +#ifdef USE_INET6 + i6addr_t swip6, sw2ip6; + ip6_t *ip6; +#endif + struct in_addr swip, sw2ip; + tftpinfo_t *ti; + udphdr_t udp; + fr_info_t fi; + u_short slen; + nat_t *nat2; + int nflags; + ip_t *ip; + int dir; + + ti = aps->aps_data; + /* + * Add skeleton NAT entry for connection which will come back the + * other way. + */ + bcopy((char *)fin, (char *)&fi, sizeof(fi)); + fi.fin_flx |= FI_IGNORE; + fi.fin_data[1] = 0; + + bzero((char *)&udp, sizeof(udp)); + udp.uh_sport = 0; /* XXX - don't specify remote port */ + udp.uh_dport = ti->ti_rule->in_ndport; + udp.uh_ulen = htons(sizeof(udp)); + udp.uh_sum = 0; + + fi.fin_fr = &tftpfr; + fi.fin_dp = (char *)&udp; + fi.fin_sport = 0; + fi.fin_dport = ntohs(ti->ti_rule->in_ndport); + fi.fin_dlen = sizeof(udp); + fi.fin_plen = fi.fin_hlen + sizeof(udp); + fi.fin_flx &= FI_LOWTTL|FI_FRAG|FI_TCPUDP|FI_OPTIONS|FI_IGNORE; + nflags = NAT_SLAVE|IPN_UDP|SI_W_SPORT; +#ifdef USE_INET6 + ip6 = (ip6_t *)fin->fin_ip; +#endif + ip = fin->fin_ip; + sw2ip.s_addr = 0; + swip.s_addr = 0; + + fi.fin_src6 = nat->nat_ndst6; + fi.fin_dst6 = nat->nat_nsrc6; + if (nat->nat_v[0] == 4) { + slen = ip->ip_len; + ip->ip_len = htons(fin->fin_hlen + sizeof(udp)); + swip = ip->ip_src; + sw2ip = ip->ip_dst; + ip->ip_src = nat->nat_ndstip; + ip->ip_dst = nat->nat_nsrcip; + } else { +#ifdef USE_INET6 + slen = ip6->ip6_plen; + ip6->ip6_plen = htons(sizeof(udp)); + swip6.in6 = ip6->ip6_src; + sw2ip6.in6 = ip6->ip6_dst; + ip6->ip6_src = nat->nat_ndst6.in6; + ip6->ip6_dst = nat->nat_nsrc6.in6; +#endif + } + + if (nat->nat_dir == NAT_INBOUND) { + dir = NAT_OUTBOUND; + fi.fin_out = 1; + } else { + dir = NAT_INBOUND; + fi.fin_out = 0; + } + nflags |= NAT_NOTRULEPORT; + + MUTEX_ENTER(&softn->ipf_nat_new); +#ifdef USE_INET6 + if (nat->nat_v[0] == 6) + nat2 = ipf_nat6_add(&fi, ti->ti_rule, NULL, nflags, dir); + else +#endif + nat2 = ipf_nat_add(&fi, ti->ti_rule, NULL, nflags, dir); + MUTEX_EXIT(&softn->ipf_nat_new); + if (nat2 != NULL) { + (void) ipf_nat_proto(&fi, nat2, IPN_UDP); + ipf_nat_update(&fi, nat2); + fi.fin_ifp = NULL; + if (ti->ti_rule->in_redir == NAT_MAP) { + fi.fin_src6 = nat->nat_ndst6; + fi.fin_dst6 = nat->nat_nsrc6; + if (nat->nat_v[0] == 4) { + ip->ip_src = nat->nat_ndstip; + ip->ip_dst = nat->nat_nsrcip; + } else { +#ifdef USE_INET6 + ip6->ip6_src = nat->nat_ndst6.in6; + ip6->ip6_dst = nat->nat_nsrc6.in6; +#endif + } + } else { + fi.fin_src6 = nat->nat_odst6; + fi.fin_dst6 = nat->nat_osrc6; + if (fin->fin_v == 4) { + ip->ip_src = nat->nat_odstip; + ip->ip_dst = nat->nat_osrcip; + } else { +#ifdef USE_INET6 + ip6->ip6_src = nat->nat_odst6.in6; + ip6->ip6_dst = nat->nat_osrc6.in6; +#endif + } + } + if (ipf_state_add(softc, &fi, NULL, SI_W_SPORT) != 0) { + ipf_nat_setpending(softc, nat2); + } + } + if (nat->nat_v[0] == 4) { + ip->ip_len = slen; + ip->ip_src = swip; + ip->ip_dst = sw2ip; + } else { +#ifdef USE_INET6 + ip6->ip6_plen = slen; + ip6->ip6_src = swip6.in6; + ip6->ip6_dst = sw2ip6.in6; +#endif + } + return 0; +} + + +int +ipf_p_tftp_client(softt, fin, aps, nat) + ipf_tftp_softc_t *softt; + fr_info_t *fin; + ap_session_t *aps; + nat_t *nat; +{ + u_char *msg, *s, *t; + tftpinfo_t *ti; + u_short opcode; + udphdr_t *udp; + int len; + + if (fin->fin_dlen < 4) + return 0; + + ti = aps->aps_data; + msg = fin->fin_dp; + msg += sizeof(udphdr_t); + opcode = (msg[0] << 8) | msg[1]; + DT3(tftp_cmd, fr_info_t *, fin, int, opcode, nat_t *, nat); + + switch (opcode) + { + case TFTP_CMD_WRITE : + if (softt->ipf_p_tftp_readonly != 0) + break; + /* FALLTHROUGH */ + case TFTP_CMD_READ : + len = fin->fin_dlen - sizeof(*udp) - 2; + if (len > sizeof(ti->ti_filename) - 1) + len = sizeof(ti->ti_filename) - 1; + s = msg + 2; + for (t = (u_char *)ti->ti_filename; (len > 0); len--, s++) { + *t++ = *s; + if (*s == '\0') + break; + } + ipf_p_tftp_backchannel(fin, aps, nat); + break; + default : + return -1; + } + + ti = aps->aps_data; + ti->ti_lastcmd = opcode; + return 0; +} + + +int +ipf_p_tftp_server(softt, fin, aps, nat) + ipf_tftp_softc_t *softt; + fr_info_t *fin; + ap_session_t *aps; + nat_t *nat; +{ + tftpinfo_t *ti; + u_short opcode; + u_short arg; + u_char *msg; + + if (fin->fin_dlen < 4) + return 0; + + ti = aps->aps_data; + msg = fin->fin_dp; + msg += sizeof(udphdr_t); + arg = (msg[2] << 8) | msg[3]; + opcode = (msg[0] << 8) | msg[1]; + + switch (opcode) + { + case TFTP_CMD_ACK : + ti->ti_lastblk = arg; + break; + + case TFTP_CMD_ERROR : + ti->ti_lasterror = arg; + break; + + default : + return -1; + } + + ti->ti_lastcmd = opcode; + return 0; +} diff --git a/sys/contrib/ipfilter/netinet/ipf_rb.h b/sys/contrib/ipfilter/netinet/ipf_rb.h new file mode 100644 index 00000000000..3d7a59d99d3 --- /dev/null +++ b/sys/contrib/ipfilter/netinet/ipf_rb.h @@ -0,0 +1,364 @@ +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + * + */ +typedef enum rbcolour_e { + C_BLACK = 0, + C_RED = 1 +} rbcolour_t; + +#define RBI_LINK(_n, _t) \ + struct _n##_rb_link { \ + struct _t *left; \ + struct _t *right; \ + struct _t *parent; \ + rbcolour_t colour; \ + } + +#define RBI_HEAD(_n, _t) \ +struct _n##_rb_head { \ + struct _t top; \ + int count; \ + int (* compare)(struct _t *, struct _t *); \ +} + +#define RBI_CODE(_n, _t, _f, _cmp) \ + \ +typedef void (*_n##_rb_walker_t)(_t *, void *); \ + \ +_t * _n##_rb_delete(struct _n##_rb_head *, _t *); \ +void _n##_rb_init(struct _n##_rb_head *); \ +void _n##_rb_insert(struct _n##_rb_head *, _t *); \ +_t * _n##_rb_search(struct _n##_rb_head *, void *); \ +void _n##_rb_walktree(struct _n##_rb_head *, _n##_rb_walker_t, void *);\ + \ +static void \ +rotate_left(struct _n##_rb_head *head, _t *node) \ +{ \ + _t *parent, *tmp1, *tmp2; \ + \ + parent = node->_f.parent; \ + tmp1 = node->_f.right; \ + tmp2 = tmp1->_f.left; \ + node->_f.right = tmp2; \ + if (tmp2 != & _n##_rb_zero) \ + tmp2->_f.parent = node; \ + if (parent == & _n##_rb_zero) \ + head->top._f.right = tmp1; \ + else if (parent->_f.right == node) \ + parent->_f.right = tmp1; \ + else \ + parent->_f.left = tmp1; \ + tmp1->_f.left = node; \ + tmp1->_f.parent = parent; \ + node->_f.parent = tmp1; \ +} \ + \ +static void \ +rotate_right(struct _n##_rb_head *head, _t *node) \ +{ \ + _t *parent, *tmp1, *tmp2; \ + \ + parent = node->_f.parent; \ + tmp1 = node->_f.left; \ + tmp2 = tmp1->_f.right; \ + node->_f.left = tmp2; \ + if (tmp2 != &_n##_rb_zero) \ + tmp2->_f.parent = node; \ + if (parent == &_n##_rb_zero) \ + head->top._f.right = tmp1; \ + else if (parent->_f.right == node) \ + parent->_f.right = tmp1; \ + else \ + parent->_f.left = tmp1; \ + tmp1->_f.right = node; \ + tmp1->_f.parent = parent; \ + node->_f.parent = tmp1; \ +} \ + \ +void \ +_n##_rb_insert(struct _n##_rb_head *head, _t *node) \ +{ \ + _t *n, *parent, **p, *tmp1, *gparent; \ + \ + parent = &head->top; \ + node->_f.left = &_n##_rb_zero; \ + node->_f.right = &_n##_rb_zero; \ + p = &head->top._f.right; \ + while ((n = *p) != &_n##_rb_zero) { \ + if (_cmp(node, n) < 0) \ + p = &n->_f.left; \ + else \ + p = &n->_f.right; \ + parent = n; \ + } \ + *p = node; \ + node->_f.colour = C_RED; \ + node->_f.parent = parent; \ + \ + while ((node != &_n##_rb_zero) && (parent->_f.colour == C_RED)){\ + gparent = parent->_f.parent; \ + if (parent == gparent->_f.left) { \ + tmp1 = gparent->_f.right; \ + if (tmp1->_f.colour == C_RED) { \ + parent->_f.colour = C_BLACK; \ + tmp1->_f.colour = C_BLACK; \ + gparent->_f.colour = C_RED; \ + node = gparent; \ + } else { \ + if (node == parent->_f.right) { \ + node = parent; \ + rotate_left(head, node); \ + parent = node->_f.parent; \ + } \ + parent->_f.colour = C_BLACK; \ + gparent->_f.colour = C_RED; \ + rotate_right(head, gparent); \ + } \ + } else { \ + tmp1 = gparent->_f.left; \ + if (tmp1->_f.colour == C_RED) { \ + parent->_f.colour = C_BLACK; \ + tmp1->_f.colour = C_BLACK; \ + gparent->_f.colour = C_RED; \ + node = gparent; \ + } else { \ + if (node == parent->_f.left) { \ + node = parent; \ + rotate_right(head, node); \ + parent = node->_f.parent; \ + } \ + parent->_f.colour = C_BLACK; \ + gparent->_f.colour = C_RED; \ + rotate_left(head, parent->_f.parent); \ + } \ + } \ + parent = node->_f.parent; \ + } \ + head->top._f.right->_f.colour = C_BLACK; \ + head->count++; \ +} \ + \ +static void \ +deleteblack(struct _n##_rb_head *head, _t *parent, _t *node) \ +{ \ + _t *tmp; \ + \ + while ((node == &_n##_rb_zero || node->_f.colour == C_BLACK) && \ + node != &head->top) { \ + if (parent->_f.left == node) { \ + tmp = parent->_f.right; \ + if (tmp->_f.colour == C_RED) { \ + tmp->_f.colour = C_BLACK; \ + parent->_f.colour = C_RED; \ + rotate_left(head, parent); \ + tmp = parent->_f.right; \ + } \ + if ((tmp->_f.left == &_n##_rb_zero || \ + tmp->_f.left->_f.colour == C_BLACK) && \ + (tmp->_f.right == &_n##_rb_zero || \ + tmp->_f.right->_f.colour == C_BLACK)) { \ + tmp->_f.colour = C_RED; \ + node = parent; \ + parent = node->_f.parent; \ + } else { \ + if (tmp->_f.right == &_n##_rb_zero || \ + tmp->_f.right->_f.colour == C_BLACK) {\ + _t *tmp2 = tmp->_f.left; \ + \ + if (tmp2 != &_n##_rb_zero) \ + tmp2->_f.colour = C_BLACK;\ + tmp->_f.colour = C_RED; \ + rotate_right(head, tmp); \ + tmp = parent->_f.right; \ + } \ + tmp->_f.colour = parent->_f.colour; \ + parent->_f.colour = C_BLACK; \ + if (tmp->_f.right != &_n##_rb_zero) \ + tmp->_f.right->_f.colour = C_BLACK;\ + rotate_left(head, parent); \ + node = head->top._f.right; \ + } \ + } else { \ + tmp = parent->_f.left; \ + if (tmp->_f.colour == C_RED) { \ + tmp->_f.colour = C_BLACK; \ + parent->_f.colour = C_RED; \ + rotate_right(head, parent); \ + tmp = parent->_f.left; \ + } \ + if ((tmp->_f.left == &_n##_rb_zero || \ + tmp->_f.left->_f.colour == C_BLACK) && \ + (tmp->_f.right == &_n##_rb_zero || \ + tmp->_f.right->_f.colour == C_BLACK)) { \ + tmp->_f.colour = C_RED; \ + node = parent; \ + parent = node->_f.parent; \ + } else { \ + if (tmp->_f.left == &_n##_rb_zero || \ + tmp->_f.left->_f.colour == C_BLACK) {\ + _t *tmp2 = tmp->_f.right; \ + \ + if (tmp2 != &_n##_rb_zero) \ + tmp2->_f.colour = C_BLACK;\ + tmp->_f.colour = C_RED; \ + rotate_left(head, tmp); \ + tmp = parent->_f.left; \ + } \ + tmp->_f.colour = parent->_f.colour; \ + parent->_f.colour = C_BLACK; \ + if (tmp->_f.left != &_n##_rb_zero) \ + tmp->_f.left->_f.colour = C_BLACK;\ + rotate_right(head, parent); \ + node = head->top._f.right; \ + break; \ + } \ + } \ + } \ + if (node != &_n##_rb_zero) \ + node->_f.colour = C_BLACK; \ +} \ + \ +_t * \ +_n##_rb_delete(struct _n##_rb_head *head, _t *node) \ +{ \ + _t *child, *parent, *old = node, *left; \ + rbcolour_t color; \ + \ + if (node->_f.left == &_n##_rb_zero) { \ + child = node->_f.right; \ + } else if (node->_f.right == &_n##_rb_zero) { \ + child = node->_f.left; \ + } else { \ + node = node->_f.right; \ + while ((left = node->_f.left) != &_n##_rb_zero) \ + node = left; \ + child = node->_f.right; \ + parent = node->_f.parent; \ + color = node->_f.colour; \ + if (child != &_n##_rb_zero) \ + child->_f.parent = parent; \ + if (parent != &_n##_rb_zero) { \ + if (parent->_f.left == node) \ + parent->_f.left = child; \ + else \ + parent->_f.right = child; \ + } else { \ + head->top._f.right = child; \ + } \ + if (node->_f.parent == old) \ + parent = node; \ + *node = *old; \ + if (old->_f.parent != &_n##_rb_zero) { \ + if (old->_f.parent->_f.left == old) \ + old->_f.parent->_f.left = node; \ + else \ + old->_f.parent->_f.right = node; \ + } else { \ + head->top._f.right = child; \ + } \ + old->_f.left->_f.parent = node; \ + if (old->_f.right != &_n##_rb_zero) \ + old->_f.right->_f.parent = node; \ + if (parent != &_n##_rb_zero) { \ + left = parent; \ + } \ + goto colour; \ + } \ + parent = node->_f.parent; \ + color= node->_f.colour; \ + if (child != &_n##_rb_zero) \ + child->_f.parent = parent; \ + if (parent != &_n##_rb_zero) { \ + if (parent->_f.left == node) \ + parent->_f.left = child; \ + else \ + parent->_f.right = child; \ + } else { \ + head->top._f.right = child; \ + } \ +colour: \ + if (color == C_BLACK) \ + deleteblack(head, parent, node); \ + head->count--; \ + return old; \ +} \ + \ +void \ +_n##_rb_init(struct _n##_rb_head *head) \ +{ \ + memset(head, 0, sizeof(*head)); \ + memset(&_n##_rb_zero, 0, sizeof(_n##_rb_zero)); \ + head->top._f.left = &_n##_rb_zero; \ + head->top._f.right = &_n##_rb_zero; \ + head->top._f.parent = &head->top; \ + _n##_rb_zero._f.left = &_n##_rb_zero; \ + _n##_rb_zero._f.right = &_n##_rb_zero; \ + _n##_rb_zero._f.parent = &_n##_rb_zero; \ +} \ + \ +void \ +_n##_rb_walktree(struct _n##_rb_head *head, _n##_rb_walker_t func, void *arg)\ +{ \ + _t *prev; \ + _t *next; \ + _t *node = head->top._f.right; \ + _t *base; \ + \ + while (node != &_n##_rb_zero) \ + node = node->_f.left; \ + \ + for (;;) { \ + base = node; \ + prev = node; \ + while ((node->_f.parent->_f.right == node) && \ + (node != &_n##_rb_zero)) { \ + prev = node; \ + node = node->_f.parent; \ + } \ + \ + node = prev; \ + for (node = node->_f.parent->_f.right; node != &_n##_rb_zero;\ + node = node->_f.left) \ + prev = node; \ + next = prev; \ + \ + if (node != &_n##_rb_zero) \ + func(node, arg); \ + \ + node = next; \ + if (node == &_n##_rb_zero) \ + break; \ + } \ +} \ + \ +_t * \ +_n##_rb_search(struct _n##_rb_head *head, void *key) \ +{ \ + int match; \ + _t *node; \ + node = head->top._f.right; \ + while (node != &_n##_rb_zero) { \ + match = _cmp(key, node); \ + if (match == 0) \ + break; \ + if (match< 0) \ + node = node->_f.left; \ + else \ + node = node->_f.right; \ + } \ + if (node == &_n##_rb_zero || match != 0) \ + return (NULL); \ + return (node); \ +} + +#define RBI_DELETE(_n, _h, _v) _n##_rb_delete(_h, _v) +#define RBI_FIELD(_n) struct _n##_rb_link +#define RBI_INIT(_n, _h) _n##_rb_init(_h) +#define RBI_INSERT(_n, _h, _v) _n##_rb_insert(_h, _v) +#define RBI_ISEMPTY(_h) ((_h)->count == 0) +#define RBI_SEARCH(_n, _h, _k) _n##_rb_search(_h, _k) +#define RBI_WALK(_n, _h, _w, _a) _n##_rb_walktree(_h, _w, _a) +#define RBI_ZERO(_n) _n##_rb_zero diff --git a/sys/contrib/ipfilter/netinet/ipl.h b/sys/contrib/ipfilter/netinet/ipl.h index 4f2f122821a..ca3baf2c926 100644 --- a/sys/contrib/ipfilter/netinet/ipl.h +++ b/sys/contrib/ipfilter/netinet/ipl.h @@ -1,7 +1,7 @@ /* $FreeBSD$ */ /* - * Copyright (C) 1993-2001, 2003 by Darren Reed. + * Copyright (C) 2012 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. * @@ -13,8 +13,8 @@ #ifndef __IPL_H__ #define __IPL_H__ -#define IPL_VERSION "IP Filter: v4.1.28" +#define IPL_VERSION "IP Filter: v5.1.2" -#define IPFILTER_VERSION 4012800 +#define IPFILTER_VERSION 5010200 -#endif +#endif /* __IPL_H__ */ diff --git a/sys/contrib/ipfilter/netinet/mlfk_ipl.c b/sys/contrib/ipfilter/netinet/mlfk_ipl.c index 6dcb82159d3..3f4f2e9edf2 100644 --- a/sys/contrib/ipfilter/netinet/mlfk_ipl.c +++ b/sys/contrib/ipfilter/netinet/mlfk_ipl.c @@ -1,7 +1,7 @@ /* $FreeBSD$ */ /* - * Copyright (C) 2000 by Darren Reed. + * Copyright (C) 2012 by Darren Reed. * * $FreeBSD$ * See the IPFILTER.LICENCE file for details on licencing. @@ -18,20 +18,22 @@ #include #if __FreeBSD_version >= 500000 # include -#endif +#endif #include #include #include -#include -#include -#include -#include -#include -#include -#include -#include +#include "netinet/ipl.h" +#include "netinet/ip_compat.h" +#include "netinet/ip_fil.h" +#include "netinet/ip_state.h" +#include "netinet/ip_nat.h" +#include "netinet/ip_auth.h" +#include "netinet/ip_frag.h" +#include "netinet/ip_sync.h" + +extern ipf_main_softc_t ipfmain; #if __FreeBSD_version >= 502116 static struct cdev *ipf_devs[IPL_LOGSIZE]; @@ -39,10 +41,34 @@ static struct cdev *ipf_devs[IPL_LOGSIZE]; static dev_t ipf_devs[IPL_LOGSIZE]; #endif +#if 0 static int sysctl_ipf_int ( SYSCTL_HANDLER_ARGS ); +#endif static int ipf_modload(void); static int ipf_modunload(void); +#if (__FreeBSD_version >= 500024) +# if (__FreeBSD_version >= 502116) +static int ipfopen __P((struct cdev*, int, int, struct thread *)); +static int ipfclose __P((struct cdev*, int, int, struct thread *)); +# else +static int ipfopen __P((dev_t, int, int, struct thread *)); +static int ipfclose __P((dev_t, int, int, struct thread *)); +# endif /* __FreeBSD_version >= 502116 */ +#else +static int ipfopen __P((dev_t, int, int, struct proc *)); +static int ipfclose __P((dev_t, int, int, struct proc *)); +#endif +#if (__FreeBSD_version >= 502116) +static int ipfread __P((struct cdev*, struct uio *, int)); +static int ipfwrite __P((struct cdev*, struct uio *, int)); +#else +static int ipfread __P((dev_t, struct uio *, int)); +static int ipfwrite __P((dev_t, struct uio *, int)); +#endif /* __FreeBSD_version >= 502116 */ + + + SYSCTL_DECL(_net_inet); #define SYSCTL_IPF(parent, nbr, name, access, ptr, val, descr) \ SYSCTL_OID(parent, nbr, name, CTLTYPE_INT|access, \ @@ -50,91 +76,91 @@ SYSCTL_DECL(_net_inet); #define CTLFLAG_OFF 0x00800000 /* IPFilter must be disabled */ #define CTLFLAG_RWO (CTLFLAG_RW|CTLFLAG_OFF) SYSCTL_NODE(_net_inet, OID_AUTO, ipf, CTLFLAG_RW, 0, "IPF"); -SYSCTL_IPF(_net_inet_ipf, OID_AUTO, fr_flags, CTLFLAG_RW, &fr_flags, 0, ""); -SYSCTL_IPF(_net_inet_ipf, OID_AUTO, fr_pass, CTLFLAG_RW, &fr_pass, 0, ""); -SYSCTL_IPF(_net_inet_ipf, OID_AUTO, fr_active, CTLFLAG_RD, &fr_active, 0, ""); +#if 0 +SYSCTL_IPF(_net_inet_ipf, OID_AUTO, fr_flags, CTLFLAG_RW, &ipf_flags, 0, ""); +SYSCTL_IPF(_net_inet_ipf, OID_AUTO, ipf_pass, CTLFLAG_RW, &ipf_pass, 0, ""); +SYSCTL_IPF(_net_inet_ipf, OID_AUTO, fr_active, CTLFLAG_RD, &ipf_active, 0, ""); SYSCTL_IPF(_net_inet_ipf, OID_AUTO, fr_tcpidletimeout, CTLFLAG_RWO, - &fr_tcpidletimeout, 0, ""); + &ipf_tcpidletimeout, 0, ""); SYSCTL_IPF(_net_inet_ipf, OID_AUTO, fr_tcphalfclosed, CTLFLAG_RWO, - &fr_tcphalfclosed, 0, ""); + &ipf_tcphalfclosed, 0, ""); SYSCTL_IPF(_net_inet_ipf, OID_AUTO, fr_tcpclosewait, CTLFLAG_RWO, - &fr_tcpclosewait, 0, ""); + &ipf_tcpclosewait, 0, ""); SYSCTL_IPF(_net_inet_ipf, OID_AUTO, fr_tcplastack, CTLFLAG_RWO, - &fr_tcplastack, 0, ""); + &ipf_tcplastack, 0, ""); SYSCTL_IPF(_net_inet_ipf, OID_AUTO, fr_tcptimeout, CTLFLAG_RWO, - &fr_tcptimeout, 0, ""); + &ipf_tcptimeout, 0, ""); SYSCTL_IPF(_net_inet_ipf, OID_AUTO, fr_tcpclosed, CTLFLAG_RWO, - &fr_tcpclosed, 0, ""); + &ipf_tcpclosed, 0, ""); SYSCTL_IPF(_net_inet_ipf, OID_AUTO, fr_udptimeout, CTLFLAG_RWO, - &fr_udptimeout, 0, ""); + &ipf_udptimeout, 0, ""); SYSCTL_IPF(_net_inet_ipf, OID_AUTO, fr_udpacktimeout, CTLFLAG_RWO, - &fr_udpacktimeout, 0, ""); + &ipf_udpacktimeout, 0, ""); SYSCTL_IPF(_net_inet_ipf, OID_AUTO, fr_icmptimeout, CTLFLAG_RWO, - &fr_icmptimeout, 0, ""); + &ipf_icmptimeout, 0, ""); SYSCTL_IPF(_net_inet_ipf, OID_AUTO, fr_defnatage, CTLFLAG_RWO, - &fr_defnatage, 0, ""); + &ipf_nat_defage, 0, ""); SYSCTL_IPF(_net_inet_ipf, OID_AUTO, fr_ipfrttl, CTLFLAG_RW, - &fr_ipfrttl, 0, ""); -SYSCTL_IPF(_net_inet_ipf, OID_AUTO, fr_running, CTLFLAG_RD, - &fr_running, 0, ""); + &ipf_ipfrttl, 0, ""); +SYSCTL_IPF(_net_inet_ipf, OID_AUTO, ipf_running, CTLFLAG_RD, + &ipf_running, 0, ""); SYSCTL_IPF(_net_inet_ipf, OID_AUTO, fr_statesize, CTLFLAG_RWO, - &fr_statesize, 0, ""); + &ipf_state_size, 0, ""); SYSCTL_IPF(_net_inet_ipf, OID_AUTO, fr_statemax, CTLFLAG_RWO, - &fr_statemax, 0, ""); + &ipf_state_max, 0, ""); SYSCTL_IPF(_net_inet_ipf, OID_AUTO, ipf_nattable_sz, CTLFLAG_RWO, - &ipf_nattable_sz, 0, ""); + &ipf_nat_table_sz, 0, ""); SYSCTL_IPF(_net_inet_ipf, OID_AUTO, ipf_natrules_sz, CTLFLAG_RWO, - &ipf_natrules_sz, 0, ""); + &ipf_nat_maprules_sz, 0, ""); SYSCTL_IPF(_net_inet_ipf, OID_AUTO, ipf_rdrrules_sz, CTLFLAG_RWO, - &ipf_rdrrules_sz, 0, ""); + &ipf_nat_rdrrules_sz, 0, ""); SYSCTL_IPF(_net_inet_ipf, OID_AUTO, ipf_hostmap_sz, CTLFLAG_RWO, - &ipf_hostmap_sz, 0, ""); + &ipf_nat_hostmap_sz, 0, ""); SYSCTL_IPF(_net_inet_ipf, OID_AUTO, fr_authsize, CTLFLAG_RWO, - &fr_authsize, 0, ""); + &ipf_auth_size, 0, ""); SYSCTL_IPF(_net_inet_ipf, OID_AUTO, fr_authused, CTLFLAG_RD, - &fr_authused, 0, ""); + &ipf_auth_used, 0, ""); SYSCTL_IPF(_net_inet_ipf, OID_AUTO, fr_defaultauthage, CTLFLAG_RW, - &fr_defaultauthage, 0, ""); -SYSCTL_IPF(_net_inet_ipf, OID_AUTO, fr_chksrc, CTLFLAG_RW, &fr_chksrc, 0, ""); -SYSCTL_IPF(_net_inet_ipf, OID_AUTO, fr_minttl, CTLFLAG_RW, &fr_minttl, 0, ""); + &ipf_auth_defaultage, 0, ""); +SYSCTL_IPF(_net_inet_ipf, OID_AUTO, fr_chksrc, CTLFLAG_RW, &ipf_chksrc, 0, ""); +SYSCTL_IPF(_net_inet_ipf, OID_AUTO, fr_minttl, CTLFLAG_RW, &ipf_minttl, 0, ""); +#endif #define CDEV_MAJOR 79 #include #if __FreeBSD_version >= 500043 # include -static int iplpoll(struct cdev *dev, int events, struct thread *td); +static int ipfpoll(struct cdev *dev, int events, struct thread *td); -static struct cdevsw ipl_cdevsw = { -# if __FreeBSD_version >= 502103 +static struct cdevsw ipf_cdevsw = { +#if __FreeBSD_version >= 502103 .d_version = D_VERSION, .d_flags = 0, /* D_NEEDGIANT - Should be SMP safe */ -# endif - .d_open = iplopen, - .d_close = iplclose, - .d_read = iplread, - .d_write = iplwrite, - .d_ioctl = iplioctl, - .d_name = "ipl", -# if __FreeBSD_version >= 500043 - .d_poll = iplpoll, -# endif -# if __FreeBSD_version < 600000 +#endif + .d_open = ipfopen, + .d_close = ipfclose, + .d_read = ipfread, + .d_write = ipfwrite, + .d_ioctl = ipfioctl, + .d_poll = ipfpoll, + .d_name = "ipf", +#if __FreeBSD_version < 600000 .d_maj = CDEV_MAJOR, -# endif +#endif }; #else -static int iplpoll(dev_t dev, int events, struct proc *p); +static int ipfpoll(dev_t dev, int events, struct proc *td); -static struct cdevsw ipl_cdevsw = { - /* open */ iplopen, - /* close */ iplclose, - /* read */ iplread, - /* write */ iplwrite, - /* ioctl */ iplioctl, - /* poll */ iplpoll, +static struct cdevsw ipf_cdevsw = { + /* open */ ipfopen, + /* close */ ipfclose, + /* read */ ipfread, + /* write */ ipfwrite, + /* ioctl */ ipfioctl, + /* poll */ ipfpoll, /* mmap */ nommap, /* strategy */ nostrategy, - /* name */ "ipl", + /* name */ "ipf", /* maj */ CDEV_MAJOR, /* dump */ nodump, /* psize */ nopsize, @@ -142,7 +168,7 @@ static struct cdevsw ipl_cdevsw = { # if (__FreeBSD_version < 500043) /* bmaj */ -1, # endif -# if (__FreeBSD_version > 430000) +# if (__FreeBSD_version >= 430000) /* kqfilter */ NULL # endif }; @@ -180,17 +206,15 @@ ipf_modload() char *defpass, *c, *str; int i, j, error; - RWLOCK_INIT(&ipf_global, "ipf filter load/unload mutex"); - RWLOCK_INIT(&ipf_mutex, "ipf filter rwlock"); - RWLOCK_INIT(&ipf_frcache, "ipf cache rwlock"); + if (ipf_load_all() != 0) + return EIO; - error = ipfattach(); - if (error) { - RW_DESTROY(&ipf_global); - RW_DESTROY(&ipf_mutex); - RW_DESTROY(&ipf_frcache); + if (ipf_create_all(&ipfmain) == NULL) + return EIO; + + error = ipfattach(&ipfmain); + if (error) return error; - } for (i = 0; i < IPL_LOGSIZE; i++) ipf_devs[i] = NULL; @@ -204,7 +228,7 @@ ipf_modload() } if (!c) c = str; - ipf_devs[i] = make_dev(&ipl_cdevsw, i, 0, 0, 0600, "%s", c); + ipf_devs[i] = make_dev(&ipf_cdevsw, i, 0, 0, 0600, "%s", c); } error = ipf_pfil_hook(); @@ -212,15 +236,15 @@ ipf_modload() return error; ipf_event_reg(); - if (FR_ISPASS(fr_pass)) + if (FR_ISPASS(ipfmain.ipf_pass)) defpass = "pass"; - else if (FR_ISBLOCK(fr_pass)) + else if (FR_ISBLOCK(ipfmain.ipf_pass)) defpass = "block"; - else + else defpass = "no-match -> block"; printf("%s initialized. Default = %s all, Logging = %s%s\n", - ipfilter_version, defpass, + ipfilter_version, defpass, #ifdef IPFILTER_LOG "enabled", #else @@ -231,7 +255,7 @@ ipf_modload() #else "" #endif - ); + ); return 0; } @@ -241,25 +265,24 @@ ipf_modunload() { int error, i; - if (fr_refcnt) + if (ipfmain.ipf_refcnt) return EBUSY; - if (fr_running >= 0) { - ipf_pfil_unhook(); - ipf_event_dereg(); - WRITE_ENTER(&ipf_global); - error = ipfdetach(); - RWLOCK_EXIT(&ipf_global); + error = ipf_pfil_unhook(); + if (error != 0) + return error; + + if (ipfmain.ipf_running >= 0) { + error = ipfdetach(&ipfmain); if (error != 0) return error; + + ipf_destroy_all(&ipfmain); + ipf_unload_all(); } else error = 0; - RW_DESTROY(&ipf_global); - RW_DESTROY(&ipf_mutex); - RW_DESTROY(&ipf_frcache); - - fr_running = -2; + ipfmain.ipf_running = -2; for (i = 0; ipf_devfiles[i]; i++) { if (ipf_devs[i] != NULL) @@ -285,6 +308,7 @@ MODULE_VERSION(ipfilter, 1); #endif +#if 0 #ifdef SYSCTL_IPF int sysctl_ipf_int ( SYSCTL_HANDLER_ARGS ) @@ -302,7 +326,7 @@ sysctl_ipf_int ( SYSCTL_HANDLER_ARGS ) if (!arg1) error = EPERM; else { - if ((oidp->oid_kind & CTLFLAG_OFF) && (fr_running > 0)) + if ((oidp->oid_kind & CTLFLAG_OFF) && (ipfmain.ipf_running > 0)) error = EBUSY; else error = SYSCTL_IN(req, arg1, sizeof(int)); @@ -310,44 +334,43 @@ sysctl_ipf_int ( SYSCTL_HANDLER_ARGS ) return (error); } #endif +#endif static int #if __FreeBSD_version >= 500043 -iplpoll(struct cdev *dev, int events, struct thread *td) +ipfpoll(struct cdev *dev, int events, struct thread *td) #else -iplpoll(dev_t dev, int events, struct proc *td) +ipfpoll(dev_t dev, int events, struct proc *td) #endif { - u_int xmin = GET_MINOR(dev); + int unit = GET_MINOR(dev); int revents; - if (xmin < 0 || xmin > IPL_LOGMAX) + if (unit < 0 || unit > IPL_LOGMAX) return 0; revents = 0; - switch (xmin) + switch (unit) { case IPL_LOGIPF : case IPL_LOGNAT : case IPL_LOGSTATE : #ifdef IPFILTER_LOG - if ((events & (POLLIN | POLLRDNORM)) && ipflog_canread(xmin)) + if ((events & (POLLIN | POLLRDNORM)) && ipf_log_canread(&ipfmain, unit)) revents |= events & (POLLIN | POLLRDNORM); -#endif +#endif break; case IPL_LOGAUTH : - if ((events & (POLLIN | POLLRDNORM)) && fr_auth_waiting()) + if ((events & (POLLIN | POLLRDNORM)) && ipf_auth_waiting(&ipfmain)) revents |= events & (POLLIN | POLLRDNORM); - break; + break; case IPL_LOGSYNC : -#ifdef IPFILTER_SYNC - if ((events & (POLLIN | POLLRDNORM)) && ipfsync_canread()) + if ((events & (POLLIN | POLLRDNORM)) && ipf_sync_canread(&ipfmain)) revents |= events & (POLLIN | POLLRDNORM); - if ((events & (POLLOUT | POLLWRNORM)) && ipfsync_canwrite()) + if ((events & (POLLOUT | POLLWRNORM)) && ipf_sync_canwrite(&ipfmain)) revents |= events & (POLLOUT | POLLWRNORM); -#endif break; case IPL_LOGSCAN : case IPL_LOGLOOKUP : @@ -356,7 +379,152 @@ iplpoll(dev_t dev, int events, struct proc *td) } if ((revents == 0) && ((events & (POLLIN|POLLRDNORM)) != 0)) - selrecord(td, &ipfselwait[xmin]); + selrecord(td, &ipfmain.ipf_selwait[unit]); return revents; } + + +/* + * routines below for saving IP headers to buffer + */ +static int ipfopen(dev, flags +#if ((BSD >= 199506) || (__FreeBSD_version >= 220000)) +, devtype, p) + int devtype; +# if (__FreeBSD_version >= 500024) + struct thread *p; +# else + struct proc *p; +# endif /* __FreeBSD_version >= 500024 */ +#else +) +#endif +#if (__FreeBSD_version >= 502116) + struct cdev *dev; +#else + dev_t dev; +#endif + int flags; +{ + int unit = GET_MINOR(dev); + int error; + + if (IPL_LOGMAX < unit) + error = ENXIO; + else { + switch (unit) + { + case IPL_LOGIPF : + case IPL_LOGNAT : + case IPL_LOGSTATE : + case IPL_LOGAUTH : + case IPL_LOGLOOKUP : + case IPL_LOGSYNC : +#ifdef IPFILTER_SCAN + case IPL_LOGSCAN : +#endif + error = 0; + break; + default : + error = ENXIO; + break; + } + } + return error; +} + + +static int ipfclose(dev, flags +#if ((BSD >= 199506) || (__FreeBSD_version >= 220000)) +, devtype, p) + int devtype; +# if (__FreeBSD_version >= 500024) + struct thread *p; +# else + struct proc *p; +# endif /* __FreeBSD_version >= 500024 */ +#else +) +#endif +#if (__FreeBSD_version >= 502116) + struct cdev *dev; +#else + dev_t dev; +#endif + int flags; +{ + int unit = GET_MINOR(dev); + + if (IPL_LOGMAX < unit) + unit = ENXIO; + else + unit = 0; + return unit; +} + +/* + * ipfread/ipflog + * both of these must operate with at least splnet() lest they be + * called during packet processing and cause an inconsistancy to appear in + * the filter lists. + */ +#if (BSD >= 199306) +static int ipfread(dev, uio, ioflag) + int ioflag; +#else +static int ipfread(dev, uio) +#endif +#if (__FreeBSD_version >= 502116) + struct cdev *dev; +#else + dev_t dev; +#endif + struct uio *uio; +{ + int unit = GET_MINOR(dev); + + if (unit < 0) + return ENXIO; + + if (ipfmain.ipf_running < 1) + return EIO; + + if (unit == IPL_LOGSYNC) + return ipf_sync_read(&ipfmain, uio); + +#ifdef IPFILTER_LOG + return ipf_log_read(&ipfmain, unit, uio); +#else + return ENXIO; +#endif +} + + +/* + * ipfwrite + * both of these must operate with at least splnet() lest they be + * called during packet processing and cause an inconsistancy to appear in + * the filter lists. + */ +#if (BSD >= 199306) +static int ipfwrite(dev, uio, ioflag) + int ioflag; +#else +static int ipfwrite(dev, uio) +#endif +#if (__FreeBSD_version >= 502116) + struct cdev *dev; +#else + dev_t dev; +#endif + struct uio *uio; +{ + + if (ipfmain.ipf_running < 1) + return EIO; + + if (GET_MINOR(dev) == IPL_LOGSYNC) + return ipf_sync_write(&ipfmain, uio); + return ENXIO; +} diff --git a/sys/contrib/ipfilter/netinet/radix_ipf.c b/sys/contrib/ipfilter/netinet/radix_ipf.c new file mode 100644 index 00000000000..f145c38a94d --- /dev/null +++ b/sys/contrib/ipfilter/netinet/radix_ipf.c @@ -0,0 +1,1528 @@ +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + */ +#include +#include +#include +#include +#include +#include +#if !defined(_KERNEL) +# include +# include +# include +# include +#endif +#include "netinet/ip_compat.h" +#include "netinet/ip_fil.h" +#ifdef RDX_DEBUG +# include +# include +# include +#endif +#include "netinet/radix_ipf.h" + +#define ADF_OFF offsetof(addrfamily_t, adf_addr) +#define ADF_OFF_BITS (ADF_OFF << 3) + +static ipf_rdx_node_t *ipf_rx_insert __P((ipf_rdx_head_t *, + ipf_rdx_node_t nodes[2], int *)); +static void ipf_rx_attach_mask __P((ipf_rdx_node_t *, ipf_rdx_mask_t *)); +static int count_mask_bits __P((addrfamily_t *, u_32_t **)); +static void buildnodes __P((addrfamily_t *, addrfamily_t *, + ipf_rdx_node_t n[2])); +static ipf_rdx_node_t *ipf_rx_find_addr __P((ipf_rdx_node_t *, u_32_t *)); +static ipf_rdx_node_t *ipf_rx_lookup __P((ipf_rdx_head_t *, addrfamily_t *, + addrfamily_t *)); +static ipf_rdx_node_t *ipf_rx_match __P((ipf_rdx_head_t *, addrfamily_t *)); + +/* + * Foreword. + * --------- + * The code in this file has been written to target using the addrfamily_t + * data structure to house the address information and no other. Thus there + * are certain aspects of thise code (such as offsets to the address itself) + * that are hard coded here whilst they might be more variable elsewhere. + * Similarly, this code enforces no maximum key length as that's implied by + * all keys needing to be stored in addrfamily_t. + */ + +/* ------------------------------------------------------------------------ */ +/* Function: count_mask_bits */ +/* Returns: number of consecutive bits starting at "mask". */ +/* */ +/* Count the number of bits set in the address section of addrfamily_t and */ +/* return both that number and a pointer to the last word with a bit set if */ +/* lastp is not NULL. The bit count is performed using network byte order */ +/* as the guide for which bit is the most significant bit. */ +/* ------------------------------------------------------------------------ */ +static int +count_mask_bits(mask, lastp) + addrfamily_t *mask; + u_32_t **lastp; +{ + u_32_t *mp = (u_32_t *)&mask->adf_addr; + u_32_t m; + int count = 0; + int mlen; + + mlen = mask->adf_len - offsetof(addrfamily_t, adf_addr); + for (; mlen > 0; mlen -= 4, mp++) { + if ((m = ntohl(*mp)) == 0) + break; + if (lastp != NULL) + *lastp = mp; + for (; m & 0x80000000; m <<= 1) + count++; + } + + return count; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: buildnodes */ +/* Returns: Nil */ +/* Parameters: addr(I) - network address for this radix node */ +/* mask(I) - netmask associated with the above address */ +/* nodes(O) - pair of ipf_rdx_node_t's to initialise with data */ +/* associated with addr and mask. */ +/* */ +/* Initialise the fields in a pair of radix tree nodes according to the */ +/* data supplied in the paramters "addr" and "mask". It is expected that */ +/* "mask" will contain a consecutive string of bits set. Masks with gaps in */ +/* the middle are not handled by this implementation. */ +/* ------------------------------------------------------------------------ */ +static void +buildnodes(addr, mask, nodes) + addrfamily_t *addr, *mask; + ipf_rdx_node_t nodes[2]; +{ + u_32_t maskbits; + u_32_t lastbits; + u_32_t lastmask; + u_32_t *last; + int masklen; + + last = NULL; + maskbits = count_mask_bits(mask, &last); + if (last == NULL) { + masklen = 0; + lastmask = 0; + } else { + masklen = last - (u_32_t *)mask; + lastmask = *last; + } + lastbits = maskbits & 0x1f; + + bzero(&nodes[0], sizeof(ipf_rdx_node_t) * 2); + nodes[0].maskbitcount = maskbits; + nodes[0].index = -1 - (ADF_OFF_BITS + maskbits); + nodes[0].addrkey = (u_32_t *)addr; + nodes[0].maskkey = (u_32_t *)mask; + nodes[0].addroff = nodes[0].addrkey + masklen; + nodes[0].maskoff = nodes[0].maskkey + masklen; + nodes[0].parent = &nodes[1]; + nodes[0].offset = masklen; + nodes[0].lastmask = lastmask; + nodes[1].offset = masklen; + nodes[1].left = &nodes[0]; + nodes[1].maskbitcount = maskbits; +#ifdef RDX_DEBUG + (void) strcpy(nodes[0].name, "_BUILD.0"); + (void) strcpy(nodes[1].name, "_BUILD.1"); +#endif +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_rx_find_addr */ +/* Returns: ipf_rdx_node_t * - pointer to a node in the radix tree. */ +/* Parameters: tree(I) - pointer to first right node in tree to search */ +/* addr(I) - pointer to address to match */ +/* */ +/* Walk the radix tree given by "tree", looking for a leaf node that is a */ +/* match for the address given by "addr". */ +/* ------------------------------------------------------------------------ */ +static ipf_rdx_node_t * +ipf_rx_find_addr(tree, addr) + ipf_rdx_node_t *tree; + u_32_t *addr; +{ + ipf_rdx_node_t *cur; + + for (cur = tree; cur->index >= 0;) { + if (cur->bitmask & addr[cur->offset]) { + cur = cur->right; + } else { + cur = cur->left; + } + } + + return (cur); +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_rx_match */ +/* Returns: ipf_rdx_node_t * - NULL on error, else pointer to the node */ +/* added to the tree. */ +/* Paramters: head(I) - pointer to tree head to search */ +/* addr(I) - pointer to address to find */ +/* */ +/* Search the radix tree for the best match to the address pointed to by */ +/* "addr" and return a pointer to that node. This search will not match the */ +/* address information stored in either of the root leaves as neither of */ +/* them are considered to be part of the tree of data being stored. */ +/* ------------------------------------------------------------------------ */ +static ipf_rdx_node_t * +ipf_rx_match(head, addr) + ipf_rdx_head_t *head; + addrfamily_t *addr; +{ + ipf_rdx_mask_t *masknode; + ipf_rdx_node_t *prev; + ipf_rdx_node_t *node; + ipf_rdx_node_t *cur; + u_32_t *data; + u_32_t *mask; + u_32_t *key; + u_32_t *end; + int len; + int i; + + len = addr->adf_len; + end = (u_32_t *)((u_char *)addr + len); + node = ipf_rx_find_addr(head->root, (u_32_t *)addr); + + /* + * Search the dupkey list for a potential match. + */ + for (cur = node; (cur != NULL) && (cur->root == 0); cur = cur->dupkey) { + i = cur[0].addroff - cur[0].addrkey; + data = cur[0].addrkey + i; + mask = cur[0].maskkey + i; + key = (u_32_t *)addr + i; + for (; key < end; data++, key++, mask++) + if ((*key & *mask) != *data) + break; + if ((end == key) && (cur->root == 0)) + return (cur); /* Equal keys */ + } + prev = node->parent; + key = (u_32_t *)addr; + + for (node = prev; node->root == 0; node = node->parent) { + /* + * We know that the node hasn't matched so therefore only + * the entries in the mask list are searched, not the top + * node nor the dupkey list. + */ + masknode = node->masks; + for (; masknode != NULL; masknode = masknode->next) { + if (masknode->maskbitcount > node->maskbitcount) + continue; + cur = masknode->node; + for (i = ADF_OFF >> 2; i <= node->offset; i++) { + if ((key[i] & masknode->mask[i]) == + cur->addrkey[i]) + return (cur); + } + } + } + + return NULL; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_rx_lookup */ +/* Returns: ipf_rdx_node_t * - NULL on error, else pointer to the node */ +/* added to the tree. */ +/* Paramters: head(I) - pointer to tree head to search */ +/* addr(I) - address part of the key to match */ +/* mask(I) - netmask part of the key to match */ +/* */ +/* ipf_rx_lookup searches for an exact match on (addr,mask). The intention */ +/* is to see if a given key is in the tree, not to see if a route exists. */ +/* ------------------------------------------------------------------------ */ +ipf_rdx_node_t * +ipf_rx_lookup(head, addr, mask) + ipf_rdx_head_t *head; + addrfamily_t *addr, *mask; +{ + ipf_rdx_node_t *found; + ipf_rdx_node_t *node; + u_32_t *akey; + int count; + + found = ipf_rx_find_addr(head->root, (u_32_t *)addr); + if (found->root == 1) + return NULL; + + /* + * It is possible to find a matching address in the tree but for the + * netmask to not match. If the netmask does not match and there is + * no list of alternatives present at dupkey, return a failure. + */ + count = count_mask_bits(mask, NULL); + if (count != found->maskbitcount && found->dupkey == NULL) + return (NULL); + + akey = (u_32_t *)addr; + if ((found->addrkey[found->offset] & found->maskkey[found->offset]) != + akey[found->offset]) + return NULL; + + if (found->dupkey != NULL) { + node = found; + while (node != NULL && node->maskbitcount != count) + node = node->dupkey; + if (node == NULL) + return (NULL); + found = node; + } + return found; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_rx_attach_mask */ +/* Returns: Nil */ +/* Parameters: node(I) - pointer to a radix tree node */ +/* mask(I) - pointer to mask structure to add */ +/* */ +/* Add the netmask to the given node in an ordering where the most specific */ +/* netmask is at the top of the list. */ +/* ------------------------------------------------------------------------ */ +static void +ipf_rx_attach_mask(node, mask) + ipf_rdx_node_t *node; + ipf_rdx_mask_t *mask; +{ + ipf_rdx_mask_t **pm; + ipf_rdx_mask_t *m; + + for (pm = &node->masks; (m = *pm) != NULL; pm = &m->next) + if (m->maskbitcount < mask->maskbitcount) + break; + mask->next = *pm; + *pm = mask; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_rx_insert */ +/* Returns: ipf_rdx_node_t * - NULL on error, else pointer to the node */ +/* added to the tree. */ +/* Paramters: head(I) - pointer to tree head to add nodes to */ +/* nodes(I) - pointer to radix nodes to be added */ +/* dup(O) - set to 1 if node is a duplicate, else 0. */ +/* */ +/* Add the new radix tree entry that owns nodes[] to the tree given by head.*/ +/* If there is already a matching key in the table, "dup" will be set to 1 */ +/* and the existing node pointer returned if there is a complete key match. */ +/* A complete key match is a matching of all key data that is presented by */ +/* by the netmask. */ +/* ------------------------------------------------------------------------ */ +static ipf_rdx_node_t * +ipf_rx_insert(head, nodes, dup) + ipf_rdx_head_t *head; + ipf_rdx_node_t nodes[2]; + int *dup; +{ + ipf_rdx_mask_t **pmask; + ipf_rdx_node_t *node; + ipf_rdx_node_t *prev; + ipf_rdx_mask_t *mask; + ipf_rdx_node_t *cur; + u_32_t nodemask; + u_32_t *addr; + u_32_t *data; + int nodebits; + u_32_t *key; + u_32_t *end; + u_32_t bits; + int nodekey; + int nodeoff; + int nlen; + int len; + + addr = nodes[0].addrkey; + + node = ipf_rx_find_addr(head->root, addr); + len = ((addrfamily_t *)addr)->adf_len; + key = (u_32_t *)&((addrfamily_t *)addr)->adf_addr; + data= (u_32_t *)&((addrfamily_t *)node->addrkey)->adf_addr; + end = (u_32_t *)((u_char *)addr + len); + for (nlen = 0; key < end; data++, key++, nlen += 32) + if (*key != *data) + break; + if (end == data) { + *dup = 1; + return (node); /* Equal keys */ + } + *dup = 0; + + bits = (ntohl(*data) ^ ntohl(*key)); + for (; bits != 0; nlen++) { + if ((bits & 0x80000000) != 0) + break; + bits <<= 1; + } + nlen += ADF_OFF_BITS; + nodes[1].index = nlen; + nodes[1].bitmask = htonl(0x80000000 >> (nlen & 0x1f)); + nodes[0].offset = nlen / 32; + nodes[1].offset = nlen / 32; + + /* + * Walk through the tree and look for the correct place to attach + * this node. ipf_rx_fin_addr is not used here because the place + * to attach this node may be an internal node (same key, different + * netmask.) Additionally, the depth of the search is forcibly limited + * here to not exceed the netmask, so that a short netmask will be + * added higher up the tree even if there are lower branches. + */ + cur = head->root; + key = nodes[0].addrkey; + do { + prev = cur; + if (key[cur->offset] & cur->bitmask) { + cur = cur->right; + } else { + cur = cur->left; + } + } while (nlen > (unsigned)cur->index); + + if ((key[prev->offset] & prev->bitmask) == 0) { + prev->left = &nodes[1]; + } else { + prev->right = &nodes[1]; + } + cur->parent = &nodes[1]; + nodes[1].parent = prev; + if ((key[nodes[1].offset] & nodes[1].bitmask) == 0) { + nodes[1].right = cur; + } else { + nodes[1].right = &nodes[0]; + nodes[1].left = cur; + } + + nodeoff = nodes[0].offset; + nodekey = nodes[0].addrkey[nodeoff]; + nodemask = nodes[0].lastmask; + nodebits = nodes[0].maskbitcount; + prev = NULL; + /* + * Find the node up the tree with the largest pattern that still + * matches the node being inserted to see if this mask can be + * moved there. + */ + for (cur = nodes[1].parent; cur->root == 0; cur = cur->parent) { + if (cur->maskbitcount <= nodebits) + break; + if (((cur - 1)->addrkey[nodeoff] & nodemask) != nodekey) + break; + prev = cur; + } + + KMALLOC(mask, ipf_rdx_mask_t *); + if (mask == NULL) + return NULL; + bzero(mask, sizeof(*mask)); + mask->next = NULL; + mask->node = &nodes[0]; + mask->maskbitcount = nodebits; + mask->mask = nodes[0].maskkey; + nodes[0].mymask = mask; + + if (prev != NULL) { + ipf_rdx_mask_t *m; + + for (pmask = &prev->masks; (m = *pmask) != NULL; + pmask = &m->next) { + if (m->maskbitcount < nodebits) + break; + } + } else { + /* + * No higher up nodes qualify, so attach mask locally. + */ + pmask = &nodes[0].masks; + } + mask->next = *pmask; + *pmask = mask; + + /* + * Search the mask list on each child to see if there are any masks + * there that can be moved up to this newly inserted node. + */ + cur = nodes[1].right; + if (cur->root == 0) { + for (pmask = &cur->masks; (mask = *pmask) != NULL; ) { + if (mask->maskbitcount < nodebits) { + *pmask = mask->next; + ipf_rx_attach_mask(&nodes[0], mask); + } else { + pmask = &mask->next; + } + } + } + cur = nodes[1].left; + if (cur->root == 0 && cur != &nodes[0]) { + for (pmask = &cur->masks; (mask = *pmask) != NULL; ) { + if (mask->maskbitcount < nodebits) { + *pmask = mask->next; + ipf_rx_attach_mask(&nodes[0], mask); + } else { + pmask = &mask->next; + } + } + } + return (&nodes[0]); +} + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_rx_addroute */ +/* Returns: ipf_rdx_node_t * - NULL on error, else pointer to the node */ +/* added to the tree. */ +/* Paramters: head(I) - pointer to tree head to search */ +/* addr(I) - address portion of "route" to add */ +/* mask(I) - netmask portion of "route" to add */ +/* nodes(I) - radix tree data nodes inside allocate structure */ +/* */ +/* Attempt to add a node to the radix tree. The key for the node is the */ +/* (addr,mask). No memory allocation for the radix nodes themselves is */ +/* performed here, the data structure that this radix node is being used to */ +/* find is expected to house the node data itself however the call to */ +/* ipf_rx_insert() will attempt to allocate memory in order for netmask to */ +/* be promoted further up the tree. */ +/* In this case, the ip_pool_node_t structure from ip_pool.h contains both */ +/* the key material (addr,mask) and the radix tree nodes[]. */ +/* */ +/* The mechanics of inserting the node into the tree is handled by the */ +/* function ipf_rx_insert() above. Here, the code deals with the case */ +/* where the data to be inserted is a duplicate. */ +/* ------------------------------------------------------------------------ */ +ipf_rdx_node_t * +ipf_rx_addroute(head, addr, mask, nodes) + ipf_rdx_head_t *head; + addrfamily_t *addr, *mask; + ipf_rdx_node_t *nodes; +{ + ipf_rdx_node_t *node; + ipf_rdx_node_t *prev; + ipf_rdx_node_t *x; + int dup; + + buildnodes(addr, mask, nodes); + x = ipf_rx_insert(head, nodes, &dup); + if (x == NULL) + return NULL; + + if (dup == 1) { + node = &nodes[0]; + prev = NULL; + /* + * The duplicate list is kept sorted with the longest + * mask at the top, meaning that the most specific entry + * in the listis found first. This list thus allows for + * duplicates such as 128.128.0.0/32 and 128.128.0.0/16. + */ + while ((x != NULL) && (x->maskbitcount > node->maskbitcount)) { + prev = x; + x = x->dupkey; + } + + /* + * Is it a complete duplicate? If so, return NULL and + * fail the insert. Otherwise, insert it into the list + * of netmasks active for this key. + */ + if ((x != NULL) && (x->maskbitcount == node->maskbitcount)) + return (NULL); + + if (prev != NULL) { + nodes[0].dupkey = x; + prev->dupkey = &nodes[0]; + nodes[0].parent = prev; + if (x != NULL) + x->parent = &nodes[0]; + } else { + nodes[0].dupkey = x->dupkey; + prev = x->parent; + nodes[0].parent = prev; + x->parent = &nodes[0]; + if (prev->left == x) + prev->left = &nodes[0]; + else + prev->right = &nodes[0]; + } + } + + return &nodes[0]; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_rx_delete */ +/* Returns: ipf_rdx_node_t * - NULL on error, else node removed from */ +/* the tree. */ +/* Paramters: head(I) - pointer to tree head to search */ +/* addr(I) - pointer to the address part of the key */ +/* mask(I) - pointer to the netmask part of the key */ +/* */ +/* Search for an entry in the radix tree that is an exact match for (addr, */ +/* mask) and remove it if it exists. In the case where (addr,mask) is a not */ +/* a unique key, the tree structure itself is not changed - only the list */ +/* of duplicate keys. */ +/* ------------------------------------------------------------------------ */ +ipf_rdx_node_t * +ipf_rx_delete(head, addr, mask) + ipf_rdx_head_t *head; + addrfamily_t *addr, *mask; +{ + ipf_rdx_mask_t **pmask; + ipf_rdx_node_t *parent; + ipf_rdx_node_t *found; + ipf_rdx_node_t *prev; + ipf_rdx_node_t *node; + ipf_rdx_node_t *cur; + ipf_rdx_mask_t *m; + int count; + + found = ipf_rx_find_addr(head->root, (u_32_t *)addr); + if (found == NULL) + return NULL; + if (found->root == 1) + return NULL; + count = count_mask_bits(mask, NULL); + parent = found->parent; + if (found->dupkey != NULL) { + node = found; + while (node != NULL && node->maskbitcount != count) + node = node->dupkey; + if (node == NULL) + return (NULL); + if (node != found) { + /* + * Remove from the dupkey list. Here, "parent" is + * the previous node on the list (rather than tree) + * and "dupkey" is the next node on the list. + */ + parent = node->parent; + parent->dupkey = node->dupkey; + node->dupkey->parent = parent; + } else { + /* + * + * When removing the top node of the dupkey list, + * the pointers at the top of the list that point + * to other tree nodes need to be preserved and + * any children must have their parent updated. + */ + node = node->dupkey; + node->parent = found->parent; + node->right = found->right; + node->left = found->left; + found->right->parent = node; + found->left->parent = node; + if (parent->left == found) + parent->left = node; + else + parent->right= node; + } + } else { + if (count != found->maskbitcount) + return (NULL); + /* + * Remove the node from the tree and reconnect the subtree + * below. + */ + /* + * If there is a tree to the left, look for something to + * attach in place of "found". + */ + prev = found + 1; + cur = parent->parent; + if (parent != found + 1) { + if ((found + 1)->parent->right == found + 1) + (found + 1)->parent->right = parent; + else + (found + 1)->parent->left = parent; + if (cur->right == parent) { + if (parent->left == found) { + cur->right = parent->right; + } else if (parent->left != parent - 1) { + cur->right = parent->left; + } else { + cur->right = parent - 1; + } + cur->right->parent = cur; + } else { + if (parent->right == found) { + cur->left = parent->left; + } else if (parent->right != parent - 1) { + cur->left = parent->right; + } else { + cur->left = parent - 1; + } + cur->left->parent = cur; + } + parent->left = (found + 1)->left; + if ((found + 1)->right != parent) + parent->right = (found + 1)->right; + parent->left->parent = parent; + parent->right->parent = parent; + parent->parent = (found + 1)->parent; + + parent->bitmask = prev->bitmask; + parent->offset = prev->offset; + parent->index = prev->index; + } else { + /* + * We found an edge node. + */ + cur = parent->parent; + if (cur->left == parent) { + if (parent->left == found) { + cur->left = parent->right; + parent->right->parent = cur; + } else { + cur->left = parent->left; + parent->left->parent = cur; + } + } else { + if (parent->right != found) { + cur->right = parent->right; + parent->right->parent = cur; + } else { + cur->right = parent->left; + prev->left->parent = cur; + } + } + } + } + + /* + * Remove mask associated with this node. + */ + for (cur = parent; cur->root == 0; cur = cur->parent) { + ipf_rdx_mask_t **pm; + + if (cur->maskbitcount <= found->maskbitcount) + break; + if (((cur - 1)->addrkey[found->offset] & found->bitmask) != + found->addrkey[found->offset]) + break; + for (pm = &cur->masks; (m = *pm) != NULL; ) + if (m->node == cur) { + *pm = m->next; + break; + } else { + pm = &m->next; + } + } + KFREE(found->mymask); + + /* + * Masks that have been brought up to this node from below need to + * be sent back down. + */ + for (pmask = &parent->masks; (m = *pmask) != NULL; ) { + *pmask = m->next; + cur = m->node; + if (cur == found) + continue; + if (found->addrkey[cur->offset] & cur->lastmask) { + ipf_rx_attach_mask(parent->right, m); + } else if (parent->left != found) { + ipf_rx_attach_mask(parent->left, m); + } + } + + return (found); +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_rx_walktree */ +/* Returns: Nil */ +/* Paramters: head(I) - pointer to tree head to search */ +/* walker(I) - function to call for each node in the tree */ +/* arg(I) - parameter to pass to walker, in addition to the */ +/* node pointer */ +/* */ +/* A standard tree walking function except that it is iterative, rather */ +/* than recursive and tracks the next node in case the "walker" function */ +/* should happen to delete and free the current node. It thus goes without */ +/* saying that the "walker" function is not permitted to cause any change */ +/* in the validity of the data found at either the left or right child. */ +/* ------------------------------------------------------------------------ */ +void +ipf_rx_walktree(head, walker, arg) + ipf_rdx_head_t *head; + radix_walk_func_t walker; + void *arg; +{ + ipf_rdx_node_t *next; + ipf_rdx_node_t *node = head->root; + ipf_rdx_node_t *base; + + while (node->index >= 0) + node = node->left; + + for (;;) { + base = node; + while ((node->parent->right == node) && (node->root == 0)) + node = node->parent; + + for (node = node->parent->right; node->index >= 0; ) + node = node->left; + next = node; + + for (node = base; node != NULL; node = base) { + base = node->dupkey; + if (node->root == 0) + walker(node, arg); + } + node = next; + if (node->root) + return; + } +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_rx_inithead */ +/* Returns: int - 0 = success, else failure */ +/* Paramters: softr(I) - pointer to radix context */ +/* headp(O) - location for where to store allocated tree head */ +/* */ +/* This function allocates and initialises a radix tree head structure. */ +/* As a traditional radix tree, node 0 is used as the "0" sentinel and node */ +/* "2" is used as the all ones sentinel, leaving node "1" as the root from */ +/* which the tree is hung with node "0" on its left and node "2" to the */ +/* right. The context, "softr", is used here to provide a common source of */ +/* the zeroes and ones data rather than have one per head. */ +/* ------------------------------------------------------------------------ */ +int +ipf_rx_inithead(softr, headp) + radix_softc_t *softr; + ipf_rdx_head_t **headp; +{ + ipf_rdx_head_t *ptr; + ipf_rdx_node_t *node; + + KMALLOC(ptr, ipf_rdx_head_t *); + *headp = ptr; + if (ptr == NULL) + return -1; + bzero(ptr, sizeof(*ptr)); + node = ptr->nodes; + ptr->root = node + 1; + node[0].index = ADF_OFF_BITS; + node[0].index = -1 - node[0].index; + node[1].index = ADF_OFF_BITS; + node[2].index = node[0].index; + node[0].parent = node + 1; + node[1].parent = node + 1; + node[2].parent = node + 1; + node[1].bitmask = htonl(0x80000000); + node[0].root = 1; + node[1].root = 1; + node[2].root = 1; + node[0].offset = ADF_OFF_BITS >> 5; + node[1].offset = ADF_OFF_BITS >> 5; + node[2].offset = ADF_OFF_BITS >> 5; + node[1].left = &node[0]; + node[1].right = &node[2]; + node[0].addrkey = (u_32_t *)softr->zeros; + node[2].addrkey = (u_32_t *)softr->ones; +#ifdef RDX_DEBUG + (void) strcpy(node[0].name, "0_ROOT"); + (void) strcpy(node[1].name, "1_ROOT"); + (void) strcpy(node[2].name, "2_ROOT"); +#endif + + ptr->addaddr = ipf_rx_addroute; + ptr->deladdr = ipf_rx_delete; + ptr->lookup = ipf_rx_lookup; + ptr->matchaddr = ipf_rx_match; + ptr->walktree = ipf_rx_walktree; + return 0; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_rx_freehead */ +/* Returns: Nil */ +/* Paramters: head(I) - pointer to tree head to free */ +/* */ +/* This function simply free's up the radix tree head. Prior to calling */ +/* this function, it is expected that the tree will have been emptied. */ +/* ------------------------------------------------------------------------ */ +void +ipf_rx_freehead(head) + ipf_rdx_head_t *head; +{ + KFREE(head); +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_rx_create */ +/* Parameters: Nil */ +/* */ +/* ------------------------------------------------------------------------ */ +void * +ipf_rx_create() +{ + radix_softc_t *softr; + + KMALLOC(softr, radix_softc_t *); + if (softr == NULL) + return NULL; + bzero((char *)softr, sizeof(*softr)); + + KMALLOCS(softr->zeros, u_char *, 3 * sizeof(addrfamily_t)); + if (softr->zeros == NULL) { + KFREE(softr); + return (NULL); + } + softr->ones = softr->zeros + sizeof(addrfamily_t); + + return softr; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_rx_init */ +/* Returns: int - 0 = success (always) */ +/* */ +/* ------------------------------------------------------------------------ */ +int +ipf_rx_init(ctx) + void *ctx; +{ + radix_softc_t *softr = ctx; + + memset(softr->zeros, 0, 3 * sizeof(addrfamily_t)); + memset(softr->ones, 0xff, sizeof(addrfamily_t)); + + return (0); +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_rx_destroy */ +/* Returns: Nil */ +/* */ +/* ------------------------------------------------------------------------ */ +void +ipf_rx_destroy(ctx) + void *ctx; +{ + radix_softc_t *softr = ctx; + + if (softr->zeros != NULL) + KFREES(softr->zeros, 3 * sizeof(addrfamily_t)); + KFREE(softr); +} + +/* ====================================================================== */ + +#ifdef RDX_DEBUG +/* + * To compile this file as a standalone test unit, use -DRDX_DEBUG=1 + */ +#define NAME(x) ((x)->index < 0 ? (x)->name : (x)->name) +#define GNAME(y) ((y) == NULL ? "NULL" : NAME(y)) + +typedef struct myst { + struct ipf_rdx_node nodes[2]; + addrfamily_t dst; + addrfamily_t mask; + struct myst *next; + int printed; +} myst_t; + +typedef struct tabe_s { + char *host; + char *mask; + char *what; +} tabe_t; + +tabe_t builtin[] = { +#if 1 + { "192:168:100::0", "48", "d" }, + { "192:168:100::2", "128", "d" }, +#else + { "127.192.0.0", "255.255.255.0", "d" }, + { "127.128.0.0", "255.255.255.0", "d" }, + { "127.96.0.0", "255.255.255.0", "d" }, + { "127.80.0.0", "255.255.255.0", "d" }, + { "127.72.0.0", "255.255.255.0", "d" }, + { "127.64.0.0", "255.255.255.0", "d" }, + { "127.56.0.0", "255.255.255.0", "d" }, + { "127.48.0.0", "255.255.255.0", "d" }, + { "127.40.0.0", "255.255.255.0", "d" }, + { "127.32.0.0", "255.255.255.0", "d" }, + { "127.24.0.0", "255.255.255.0", "d" }, + { "127.16.0.0", "255.255.255.0", "d" }, + { "127.8.0.0", "255.255.255.0", "d" }, + { "124.0.0.0", "255.0.0.0", "d" }, + { "125.0.0.0", "255.0.0.0", "d" }, + { "126.0.0.0", "255.0.0.0", "d" }, + { "127.0.0.0", "255.0.0.0", "d" }, + { "10.0.0.0", "255.0.0.0", "d" }, + { "128.250.0.0", "255.255.0.0", "d" }, + { "192.168.0.0", "255.255.0.0", "d" }, + { "192.168.1.0", "255.255.255.0", "d" }, +#endif + { NULL, NULL, NULL } +}; + +char *mtable[][1] = { +#if 1 + { "192:168:100::2" }, + { "192:168:101::2" }, +#else + { "9.0.0.0" }, + { "9.0.0.1" }, + { "11.0.0.0" }, + { "11.0.0.1" }, + { "127.0.0.1" }, + { "127.0.1.0" }, + { "255.255.255.0" }, + { "126.0.0.1" }, + { "128.251.0.0" }, + { "128.251.0.1" }, + { "128.251.255.255" }, + { "129.250.0.0" }, + { "129.250.0.1" }, + { "192.168.255.255" }, +#endif + { NULL } +}; + + +int forder[22] = { + 14, 13, 12, 5, 10, 3, 19, 7, 4, 20, 8, + 2, 17, 9, 16, 11, 15, 1, 6, 18, 0, 21 +}; + +static int nodecount = 0; +myst_t *myst_top = NULL; +tabe_t *ttable = NULL; + +void add_addr(ipf_rdx_head_t *, int , int); +void checktree(ipf_rdx_head_t *); +void delete_addr(ipf_rdx_head_t *rnh, int item); +void dumptree(ipf_rdx_head_t *rnh); +void nodeprinter(ipf_rdx_node_t *, void *); +void printroots(ipf_rdx_head_t *); +void random_add(ipf_rdx_head_t *); +void random_delete(ipf_rdx_head_t *); +void test_addr(ipf_rdx_head_t *rnh, int pref, addrfamily_t *, int); + + +static void +ipf_rx_freenode(node, arg) + ipf_rdx_node_t *node; + void *arg; +{ + ipf_rdx_head_t *head = arg; + ipf_rdx_node_t *rv; + myst_t *stp; + + stp = (myst_t *)node; + rv = ipf_rx_delete(head, &stp->dst, &stp->mask); + if (rv != NULL) { + free(rv); + } +} + + +const char * +addrname(ap) + addrfamily_t *ap; +{ + static char name[80]; + const char *txt; + + bzero((char *)name, sizeof(name)); + txt = inet_ntop(ap->adf_family, &ap->adf_addr, name, + sizeof(name)); + return txt; +} + + +void +fill6bits(bits, msk) + int bits; + u_int *msk; +{ + if (bits == 0) { + msk[0] = 0; + msk[1] = 0; + msk[2] = 0; + msk[3] = 0; + return; + } + + msk[0] = 0xffffffff; + msk[1] = 0xffffffff; + msk[2] = 0xffffffff; + msk[3] = 0xffffffff; + + if (bits == 128) + return; + if (bits > 96) { + msk[3] = htonl(msk[3] << (128 - bits)); + } else if (bits > 64) { + msk[3] = 0; + msk[2] = htonl(msk[2] << (96 - bits)); + } else if (bits > 32) { + msk[3] = 0; + msk[2] = 0; + msk[1] = htonl(msk[1] << (64 - bits)); + } else { + msk[3] = 0; + msk[2] = 0; + msk[1] = 0; + msk[0] = htonl(msk[0] << (32 - bits)); + } +} + + +void +setaddr(afp, str) + addrfamily_t *afp; + char *str; +{ + + bzero((char *)afp, sizeof(*afp)); + + if (strchr(str, ':') == NULL) { + afp->adf_family = AF_INET; + afp->adf_len = offsetof(addrfamily_t, adf_addr) + 4; + } else { + afp->adf_family = AF_INET6; + afp->adf_len = offsetof(addrfamily_t, adf_addr) + 16; + } + inet_pton(afp->adf_family, str, &afp->adf_addr); +} + + +void +setmask(afp, str) + addrfamily_t *afp; + char *str; +{ + if (strchr(str, '.') != NULL) { + afp->adf_addr.in4.s_addr = inet_addr(str); + afp->adf_len = offsetof(addrfamily_t, adf_addr) + 4; + } else if (afp->adf_family == AF_INET) { + afp->adf_addr.i6[0] = htonl(0xffffffff << (32 - atoi(str))); + afp->adf_len = offsetof(addrfamily_t, adf_addr) + 4; + } else if (afp->adf_family == AF_INET6) { + fill6bits(atoi(str), afp->adf_addr.i6); + afp->adf_len = offsetof(addrfamily_t, adf_addr) + 16; + } +} + + +void +nodeprinter(node, arg) + ipf_rdx_node_t *node; + void *arg; +{ + myst_t *stp = (myst_t *)node; + + printf("Node %-9.9s L %-9.9s R %-9.9s P %9.9s/%-9.9s %s/%d\n", + node[0].name, + GNAME(node[1].left), GNAME(node[1].right), + GNAME(node[0].parent), GNAME(node[1].parent), + addrname(&stp->dst), node[0].maskbitcount); + if (stp->printed == -1) + printf("!!! %d\n", stp->printed); + else + stp->printed = 1; +} + + +void +printnode(stp) + myst_t *stp; +{ + ipf_rdx_node_t *node = &stp->nodes[0]; + + if (stp->nodes[0].index > 0) + stp = (myst_t *)&stp->nodes[-1]; + + printf("Node %-9.9s ", node[0].name); + printf("L %-9.9s ", GNAME(node[1].left)); + printf("R %-9.9s ", GNAME(node[1].right)); + printf("P %9.9s", GNAME(node[0].parent)); + printf("/%-9.9s ", GNAME(node[1].parent)); + printf("%s P%d\n", addrname(&stp->dst), stp->printed); +} + + +void +buildtab(void) +{ + char line[80], *s; + tabe_t *tab; + int lines; + FILE *fp; + + lines = 0; + fp = fopen("hosts", "r"); + + while (fgets(line, sizeof(line), fp) != NULL) { + s = strchr(line, '\n'); + if (s != NULL) + *s = '\0'; + lines++; + if (lines == 1) + tab = malloc(sizeof(*tab) * 2); + else + tab = realloc(tab, (lines + 1) * sizeof(*tab)); + tab[lines - 1].host = strdup(line); + s = strchr(tab[lines - 1].host, '/'); + *s++ = '\0'; + tab[lines - 1].mask = s; + tab[lines - 1].what = "d"; + } + fclose(fp); + + tab[lines].host = NULL; + tab[lines].mask = NULL; + tab[lines].what = NULL; + ttable = tab; +} + + +void +printroots(rnh) + ipf_rdx_head_t *rnh; +{ + printf("Root.0.%s b %3d p %-9.9s l %-9.9s r %-9.9s\n", + GNAME(&rnh->nodes[0]), + rnh->nodes[0].index, GNAME(rnh->nodes[0].parent), + GNAME(rnh->nodes[0].left), GNAME(rnh->nodes[0].right)); + printf("Root.1.%s b %3d p %-9.9s l %-9.9s r %-9.9s\n", + GNAME(&rnh->nodes[1]), + rnh->nodes[1].index, GNAME(rnh->nodes[1].parent), + GNAME(rnh->nodes[1].left), GNAME(rnh->nodes[1].right)); + printf("Root.2.%s b %3d p %-9.9s l %-9.9s r %-9.9s\n", + GNAME(&rnh->nodes[2]), + rnh->nodes[2].index, GNAME(rnh->nodes[2].parent), + GNAME(rnh->nodes[2].left), GNAME(rnh->nodes[2].right)); +} + + +int +main(int argc, char *argv[]) +{ + addrfamily_t af; + ipf_rdx_head_t *rnh; + radix_softc_t *ctx; + int j; + int i; + + rnh = NULL; + + buildtab(); + ctx = ipf_rx_create(); + ipf_rx_init(ctx); + ipf_rx_inithead(ctx, &rnh); + + printf("=== ADD-0 ===\n"); + for (i = 0; ttable[i].host != NULL; i++) { + add_addr(rnh, i, i); + checktree(rnh); + } + printroots(rnh); + ipf_rx_walktree(rnh, nodeprinter, NULL); + printf("=== DELETE-0 ===\n"); + for (i = 0; ttable[i].host != NULL; i++) { + delete_addr(rnh, i); + printroots(rnh); + ipf_rx_walktree(rnh, nodeprinter, NULL); + } + printf("=== ADD-1 ===\n"); + for (i = 0; ttable[i].host != NULL; i++) { + setaddr(&af, ttable[i].host); + add_addr(rnh, i, i); /*forder[i]); */ + checktree(rnh); + } + dumptree(rnh); + ipf_rx_walktree(rnh, nodeprinter, NULL); + printf("=== TEST-1 ===\n"); + for (i = 0; ttable[i].host != NULL; i++) { + setaddr(&af, ttable[i].host); + test_addr(rnh, i, &af, -1); + } + + printf("=== TEST-2 ===\n"); + for (i = 0; mtable[i][0] != NULL; i++) { + setaddr(&af, mtable[i][0]); + test_addr(rnh, i, &af, -1); + } + printf("=== DELETE-1 ===\n"); + for (i = 0; ttable[i].host != NULL; i++) { + if (ttable[i].what[0] != 'd') + continue; + delete_addr(rnh, i); + for (j = 0; ttable[j].host != NULL; j++) { + setaddr(&af, ttable[j].host); + test_addr(rnh, i, &af, 3); + } + printroots(rnh); + ipf_rx_walktree(rnh, nodeprinter, NULL); + } + + dumptree(rnh); + + printf("=== ADD-2 ===\n"); + random_add(rnh); + checktree(rnh); + dumptree(rnh); + ipf_rx_walktree(rnh, nodeprinter, NULL); + printf("=== DELETE-2 ===\n"); + random_delete(rnh); + checktree(rnh); + dumptree(rnh); + + ipf_rx_walktree(rnh, ipf_rx_freenode, rnh); + + return 0; +} + + +void +dumptree(rnh) + ipf_rdx_head_t *rnh; +{ + myst_t *stp; + + printf("VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV\n"); + printroots(rnh); + for (stp = myst_top; stp; stp = stp->next) + printnode(stp); + printf("^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n"); +} + + +void +test_addr(rnh, pref, addr, limit) + ipf_rdx_head_t *rnh; + int pref; + addrfamily_t *addr; +{ + static int extras[14] = { 0, -1, 1, 3, 5, 8, 9, + 15, 16, 19, 255, 256, 65535, 65536 + }; + ipf_rdx_node_t *rn; + addrfamily_t af; + char name[80]; + myst_t *stp; + int i; + + memset(&af, 0, sizeof(af)); +#if 0 + if (limit < 0 || limit > 14) + limit = 14; + + for (i = 0; i < limit; i++) { + if (ttable[i].host == NULL) + break; + setaddr(&af, ttable[i].host); + printf("%d.%d.LOOKUP(%s)", pref, i, addrname(&af)); + rn = ipf_rx_match(rnh, &af); + stp = (myst_t *)rn; + printf(" = %s (%s/%d)\n", GNAME(rn), + rn ? addrname(&stp->dst) : "NULL", + rn ? rn->maskbitcount : 0); + } +#else + printf("%d.%d.LOOKUP(%s)", pref, -1, addrname(addr)); + rn = ipf_rx_match(rnh, addr); + stp = (myst_t *)rn; + printf(" = %s (%s/%d)\n", GNAME(rn), + rn ? addrname(&stp->dst) : "NULL", rn ? rn->maskbitcount : 0); +#endif +} + + +void +delete_addr(rnh, item) + ipf_rdx_head_t *rnh; + int item; +{ + ipf_rdx_node_t *rn; + addrfamily_t mask; + addrfamily_t af; + myst_t **pstp; + myst_t *stp; + + memset(&af, 0, sizeof(af)); + memset(&mask, 0, sizeof(mask)); + setaddr(&af, ttable[item].host); + mask.adf_family = af.adf_family; + setmask(&mask, ttable[item].mask); + + printf("DELETE(%s)\n", addrname(&af)); + rn = ipf_rx_delete(rnh, &af, &mask); + if (rn == NULL) { + printf("FAIL LOOKUP DELETE\n"); + checktree(rnh); + for (stp = myst_top; stp != NULL; stp = stp->next) + if (stp->printed != -1) + stp->printed = -2; + ipf_rx_walktree(rnh, nodeprinter, NULL); + dumptree(rnh); + abort(); + } + printf("%d.delete(%s) = %s\n", item, addrname(&af), GNAME(rn)); + + for (pstp = &myst_top; (stp = *pstp) != NULL; pstp = &stp->next) + if (stp == (myst_t *)rn) + break; + stp->printed = -1; + stp->nodes[0].parent = &stp->nodes[0]; + stp->nodes[1].parent = &stp->nodes[1]; + *pstp = stp->next; + free(stp); + nodecount--; + checktree(rnh); +} + + +void +add_addr(rnh, n, item) + ipf_rdx_head_t *rnh; + int n, item; +{ + ipf_rdx_node_t *rn; + myst_t *stp; + + stp = calloc(1, sizeof(*stp)); + rn = (ipf_rdx_node_t *)stp; + setaddr(&stp->dst, ttable[item].host); + stp->mask.adf_family = stp->dst.adf_family; + setmask(&stp->mask, ttable[item].mask); + stp->next = myst_top; + myst_top = stp; + (void) sprintf(rn[0].name, "_BORN.0"); + (void) sprintf(rn[1].name, "_BORN.1"); + rn = ipf_rx_addroute(rnh, &stp->dst, &stp->mask, stp->nodes); + (void) sprintf(rn[0].name, "%d_NODE.0", item); + (void) sprintf(rn[1].name, "%d_NODE.1", item); + printf("ADD %d/%d %s/%s\n", n, item, rn[0].name, rn[1].name); + nodecount++; + checktree(rnh); +} + + +void +checktree(ipf_rdx_head_t *head) +{ + myst_t *s1; + ipf_rdx_node_t *rn; + + if (nodecount <= 1) + return; + + for (s1 = myst_top; s1 != NULL; s1 = s1->next) { + int fault = 0; + if (s1->printed == -1) + continue; + rn = &s1->nodes[1]; + if (rn->right->parent != rn) + fault |= 1; + if (rn->left->parent != rn) + fault |= 2; + if (rn->parent->left != rn && rn->parent->right != rn) + fault |= 4; + if (fault != 0) { + printf("FAULT %#x %s\n", fault, rn->name); + dumptree(head); + ipf_rx_walktree(head, nodeprinter, NULL); + fflush(stdout); + fflush(stderr); + printf("--\n"); + abort(); + } + } +} + + +int * +randomize(int *pnitems) +{ + int *order; + int nitems; + int choice; + int j; + int i; + + nitems = sizeof(ttable) / sizeof(ttable[0]); + *pnitems = nitems; + order = calloc(nitems, sizeof(*order)); + srandom(getpid() * time(NULL)); + memset(order, 0xff, nitems * sizeof(*order)); + order[21] = 21; + for (i = 0; i < nitems - 1; i++) { + do { + choice = rand() % (nitems - 1); + for (j = 0; j < nitems; j++) + if (order[j] == choice) + break; + } while (j != nitems); + order[i] = choice; + } + + return order; +} + + +void +random_add(rnh) + ipf_rdx_head_t *rnh; +{ + int *order; + int nitems; + int i; + + order = randomize(&nitems); + + for (i = 0; i < nitems - 1; i++) { + add_addr(rnh, i, order[i]); + checktree(rnh); + } +} + + +void +random_delete(rnh) + ipf_rdx_head_t *rnh; +{ + int *order; + int nitems; + int i; + + order = randomize(&nitems); + + for (i = 0; i < nitems - 1; i++) { + delete_addr(rnh, i); + checktree(rnh); + } +} +#endif /* RDX_DEBUG */ diff --git a/sys/contrib/ipfilter/netinet/radix_ipf.h b/sys/contrib/ipfilter/netinet/radix_ipf.h new file mode 100644 index 00000000000..73e66b0aa16 --- /dev/null +++ b/sys/contrib/ipfilter/netinet/radix_ipf.h @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + */ +#ifndef __RADIX_IPF_H__ +#define __RADIX_IPF_H__ + +#ifndef U_32_T +typedef unsigned int u_32_t; +# define U_32_T 1 +#endif + +typedef struct ipf_rdx_mask { + struct ipf_rdx_mask *next; + struct ipf_rdx_node *node; + u_32_t *mask; + int maskbitcount; +} ipf_rdx_mask_t; + +typedef struct ipf_rdx_node { + struct ipf_rdx_node *left; + struct ipf_rdx_node *right; + struct ipf_rdx_node *parent; + struct ipf_rdx_node *dupkey; + struct ipf_rdx_mask *masks; + struct ipf_rdx_mask *mymask; + u_32_t *addrkey; + u_32_t *maskkey; + u_32_t *addroff; + u_32_t *maskoff; + u_32_t lastmask; + u_32_t bitmask; + int offset; + int index; + int maskbitcount; + int root; +#ifdef RDX_DEBUG + char name[40]; +#endif +} ipf_rdx_node_t; + +struct ipf_rdx_head; + +typedef void (* radix_walk_func_t)(ipf_rdx_node_t *, void *); +typedef ipf_rdx_node_t *(* idx_hamn_func_t)(struct ipf_rdx_head *, + addrfamily_t *, addrfamily_t *, + ipf_rdx_node_t *); +typedef ipf_rdx_node_t *(* idx_ham_func_t)(struct ipf_rdx_head *, + addrfamily_t *, addrfamily_t *); +typedef ipf_rdx_node_t *(* idx_ha_func_t)(struct ipf_rdx_head *, + addrfamily_t *); +typedef void (* idx_walk_func_t)(struct ipf_rdx_head *, + radix_walk_func_t, void *); + +typedef struct ipf_rdx_head { + ipf_rdx_node_t *root; + ipf_rdx_node_t nodes[3]; + ipfmutex_t lock; + idx_hamn_func_t addaddr; /* add addr/mask to tree */ + idx_ham_func_t deladdr; /* delete addr/mask from tree */ + idx_ham_func_t lookup; /* look for specific addr/mask */ + idx_ha_func_t matchaddr; /* search tree for address match */ + idx_walk_func_t walktree; /* walk entire tree */ +} ipf_rdx_head_t; + +typedef struct radix_softc { + u_char *zeros; + u_char *ones; +} radix_softc_t; + +#undef RADIX_NODE_HEAD_LOCK +#undef RADIX_NODE_HEAD_UNLOCK +#ifdef _KERNEL +# define RADIX_NODE_HEAD_LOCK(x) MUTEX_ENTER(&(x)->lock) +# define RADIX_NODE_HEAD_UNLOCK(x) MUTEX_UNLOCK(&(x)->lock) +#else +# define RADIX_NODE_HEAD_LOCK(x) +# define RADIX_NODE_HEAD_UNLOCK(x) +#endif + +extern void *ipf_rx_create __P((void)); +extern int ipf_rx_init __P((void *)); +extern void ipf_rx_destroy __P((void *)); +extern int ipf_rx_inithead __P((radix_softc_t *, ipf_rdx_head_t **)); +extern void ipf_rx_freehead __P((ipf_rdx_head_t *)); +extern ipf_rdx_node_t *ipf_rx_addroute __P((ipf_rdx_head_t *, + addrfamily_t *, addrfamily_t *, + ipf_rdx_node_t *)); +extern ipf_rdx_node_t *ipf_rx_delete __P((ipf_rdx_head_t *, addrfamily_t *, + addrfamily_t *)); +extern void ipf_rx_walktree __P((ipf_rdx_head_t *, radix_walk_func_t, + void *)); + +#endif /* __RADIX_IPF_H__ */ diff --git a/sys/contrib/rdma/krping/krping.c b/sys/contrib/rdma/krping/krping.c index 99d1924704d..c0acf0cf913 100644 --- a/sys/contrib/rdma/krping/krping.c +++ b/sys/contrib/rdma/krping/krping.c @@ -119,7 +119,7 @@ static void krping_wait(struct krping_cb *cb, int state) int rc; mtx_lock(&cb->lock); while (cb->state < state) { - rc = msleep(cb, &cb->lock, 0, "krping", 0); + rc = msleep(cb, &cb->lock, PCATCH, "krping", 0); if (rc && rc != ERESTART) { cb->state = ERROR; break; @@ -188,7 +188,12 @@ static int krping_cma_event_handler(struct rdma_cm_id *cma_id, case RDMA_CM_EVENT_DEVICE_REMOVAL: DEBUG_LOG(PFX "cma detected device removal!!!!\n"); - break; + cb->state = ERROR; + wakeup(cb); + mtx_unlock(&cb->lock); + krping_wait(cb, CLEANUP); + tsleep(cb, 0, "krping", 5000); + return 0; default: log(LOG_ERR, "oof bad type!\n"); @@ -603,6 +608,8 @@ static int krping_setup_qp(struct krping_cb *cb, struct rdma_cm_id *cm_id) } DEBUG_LOG(PFX "created pd %p\n", cb->pd); + strlcpy(cb->name, cb->pd->device->name, sizeof(cb->name)); + cb->cq = ib_create_cq(cm_id->device, krping_cq_event_handler, NULL, cb, cb->txdepth * 2, 0); if (IS_ERR(cb->cq)) { @@ -1164,7 +1171,7 @@ static void krping_wlat_test_server(struct krping_cb *cb) } wlat_test(cb); - + krping_wait(cb, ERROR); } static void krping_bw_test_server(struct krping_cb *cb) @@ -1776,6 +1783,12 @@ int krping_doit(char *cmd) else krping_run_client(cb); DEBUG_LOG(PFX "destroy cm_id %p\n", cb->cm_id); + + mtx_lock(&cb->lock); + cb->state = CLEANUP; + wakeup(cb); + mtx_unlock(&cb->lock); + rdma_destroy_id(cb->cm_id); out: mtx_lock(&krping_mutex); diff --git a/sys/contrib/rdma/krping/krping.h b/sys/contrib/rdma/krping/krping.h index d2348257cf7..5cced304b87 100644 --- a/sys/contrib/rdma/krping/krping.h +++ b/sys/contrib/rdma/krping/krping.h @@ -37,7 +37,8 @@ enum test_state { RDMA_READ_COMPLETE, RDMA_WRITE_ADV, RDMA_WRITE_COMPLETE, - ERROR + ERROR, + CLEANUP }; struct krping_rdma_info { @@ -100,13 +101,15 @@ struct krping_cb { /* listener on service side. */ struct rdma_cm_id *child_cm_id; /* connection on server side */ TAILQ_ENTRY(krping_cb) list; - + int rlat; /* run read latency test */ int wlat; /* run write latency test */ int bw; /* run write bw test */ int duplex; /* run write bw full duplex test */ int poll; /* poll vs block in rlat */ int txdepth; + + char name[16]; }; static __inline uint64_t diff --git a/sys/contrib/rdma/krping/krping_dev.c b/sys/contrib/rdma/krping/krping_dev.c index 92d954d5e98..d6ab00a1e23 100644 --- a/sys/contrib/rdma/krping/krping_dev.c +++ b/sys/contrib/rdma/krping/krping_dev.c @@ -112,12 +112,11 @@ krping_read(struct cdev *dev, struct uio *uio, int ioflag) mtx_unlock(&krping_mutex); while (!TAILQ_EMPTY(©_cbs)) { - cb = TAILQ_FIRST(©_cbs); TAILQ_REMOVE(©_cbs, cb, list); if (cb->pd) { uprintf("krping: %4d %10s %10u %10u %10u %10u %10u %10u %10u %10u\n", - num++, cb->pd->device->name, cb->stats.send_bytes, + num++, cb->name, cb->stats.send_bytes, cb->stats.send_msgs, cb->stats.recv_bytes, cb->stats.recv_msgs, cb->stats.write_bytes, cb->stats.write_msgs, diff --git a/sys/crypto/aesni/aesencdec.h b/sys/crypto/aesni/aesencdec.h new file mode 100644 index 00000000000..0c9bf5f2162 --- /dev/null +++ b/sys/crypto/aesni/aesencdec.h @@ -0,0 +1,136 @@ +/*- + * Copyright 2013 John-Mark Gurney + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + * $FreeBSD$ + * + */ + +#include + +static inline void +aesni_enc8(int rounds, const uint8_t *key_schedule, __m128i a, + __m128i b, __m128i c, __m128i d, __m128i e, __m128i f, __m128i g, + __m128i h, __m128i out[8]) +{ + const __m128i *keysched = (const __m128i *)key_schedule; + int i; + + a ^= keysched[0]; + b ^= keysched[0]; + c ^= keysched[0]; + d ^= keysched[0]; + e ^= keysched[0]; + f ^= keysched[0]; + g ^= keysched[0]; + h ^= keysched[0]; + + for (i = 0; i < rounds; i++) { + a = _mm_aesenc_si128(a, keysched[i + 1]); + b = _mm_aesenc_si128(b, keysched[i + 1]); + c = _mm_aesenc_si128(c, keysched[i + 1]); + d = _mm_aesenc_si128(d, keysched[i + 1]); + e = _mm_aesenc_si128(e, keysched[i + 1]); + f = _mm_aesenc_si128(f, keysched[i + 1]); + g = _mm_aesenc_si128(g, keysched[i + 1]); + h = _mm_aesenc_si128(h, keysched[i + 1]); + } + + out[0] = _mm_aesenclast_si128(a, keysched[i + 1]); + out[1] = _mm_aesenclast_si128(b, keysched[i + 1]); + out[2] = _mm_aesenclast_si128(c, keysched[i + 1]); + out[3] = _mm_aesenclast_si128(d, keysched[i + 1]); + out[4] = _mm_aesenclast_si128(e, keysched[i + 1]); + out[5] = _mm_aesenclast_si128(f, keysched[i + 1]); + out[6] = _mm_aesenclast_si128(g, keysched[i + 1]); + out[7] = _mm_aesenclast_si128(h, keysched[i + 1]); +} + +static inline void +aesni_dec8(int rounds, const uint8_t *key_schedule, __m128i a, + __m128i b, __m128i c, __m128i d, __m128i e, __m128i f, __m128i g, + __m128i h, __m128i out[8]) +{ + const __m128i *keysched = (const __m128i *)key_schedule; + int i; + + a ^= keysched[0]; + b ^= keysched[0]; + c ^= keysched[0]; + d ^= keysched[0]; + e ^= keysched[0]; + f ^= keysched[0]; + g ^= keysched[0]; + h ^= keysched[0]; + + for (i = 0; i < rounds; i++) { + a = _mm_aesdec_si128(a, keysched[i + 1]); + b = _mm_aesdec_si128(b, keysched[i + 1]); + c = _mm_aesdec_si128(c, keysched[i + 1]); + d = _mm_aesdec_si128(d, keysched[i + 1]); + e = _mm_aesdec_si128(e, keysched[i + 1]); + f = _mm_aesdec_si128(f, keysched[i + 1]); + g = _mm_aesdec_si128(g, keysched[i + 1]); + h = _mm_aesdec_si128(h, keysched[i + 1]); + } + + out[0] = _mm_aesdeclast_si128(a, keysched[i + 1]); + out[1] = _mm_aesdeclast_si128(b, keysched[i + 1]); + out[2] = _mm_aesdeclast_si128(c, keysched[i + 1]); + out[3] = _mm_aesdeclast_si128(d, keysched[i + 1]); + out[4] = _mm_aesdeclast_si128(e, keysched[i + 1]); + out[5] = _mm_aesdeclast_si128(f, keysched[i + 1]); + out[6] = _mm_aesdeclast_si128(g, keysched[i + 1]); + out[7] = _mm_aesdeclast_si128(h, keysched[i + 1]); +} + +static inline __m128i +aesni_enc(int rounds, const uint8_t *key_schedule, const __m128i from) +{ + __m128i tmp; + const __m128i *keysched = (const __m128i *)key_schedule; + int i; + + tmp = from ^ keysched[0]; + + for (i = 0; i < rounds; i++) + tmp = _mm_aesenc_si128(tmp, keysched[i + 1]); + + return _mm_aesenclast_si128(tmp, keysched[i + 1]); +} + +static inline __m128i +aesni_dec(int rounds, const uint8_t *key_schedule, const __m128i from) +{ + __m128i tmp; + const __m128i *keysched = (const __m128i *)key_schedule; + int i; + + tmp = from ^ keysched[0]; + + for (i = 0; i < rounds; i++) + tmp = _mm_aesdec_si128(tmp, keysched[i + 1]); + + return _mm_aesdeclast_si128(tmp, keysched[i + 1]); +} diff --git a/sys/crypto/aesni/aesencdec_amd64.S b/sys/crypto/aesni/aesencdec_amd64.S deleted file mode 100644 index f77918b8e9a..00000000000 --- a/sys/crypto/aesni/aesencdec_amd64.S +++ /dev/null @@ -1,135 +0,0 @@ -/*- - * Copyright (c) 2010 Konstantin Belousov - * 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. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHORS 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 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 - - .text - -ENTRY(aesni_enc) - .cfi_startproc - movdqu (%rdx),%xmm0 - cmpq $0,%r8 - je 1f - movdqu (%r8),%xmm1 /* unaligned load into reg */ - pxor %xmm1,%xmm0 /* pxor otherwise can fault on iv */ -1: - pxor (%rsi),%xmm0 -2: - addq $0x10,%rsi -// aesenc (%rsi),%xmm0 - .byte 0x66,0x0f,0x38,0xdc,0x06 - decl %edi - jne 2b - addq $0x10,%rsi -// aesenclast (%rsi),%xmm0 - .byte 0x66,0x0f,0x38,0xdd,0x06 - movdqu %xmm0,(%rcx) - retq - .cfi_endproc -END(aesni_enc) - -ENTRY(aesni_dec) - .cfi_startproc - movdqu (%rdx),%xmm0 - pxor (%rsi),%xmm0 -1: - addq $0x10,%rsi -// aesdec (%rsi),%xmm0 - .byte 0x66,0x0f,0x38,0xde,0x06 - decl %edi - jne 1b - addq $0x10,%rsi -// aesdeclast (%rsi),%xmm0 - .byte 0x66,0x0f,0x38,0xdf,0x06 - cmpq $0,%r8 - je 2f - movdqu (%r8),%xmm1 - pxor %xmm1,%xmm0 -2: - movdqu %xmm0,(%rcx) - retq - .cfi_endproc -END(aesni_dec) - -ENTRY(aesni_decrypt_cbc) - .cfi_startproc - shrq $4,%rdx - movdqu (%r8),%xmm1 -1: - movdqu (%rcx),%xmm0 - movdqa %xmm0,%xmm2 - pxor (%rsi),%xmm0 - cmpl $12,%edi -// aesdec 0x10(%rsi),%xmm0 - .byte 0x66,0x0f,0x38,0xde,0x46,0x10 -// aesdec 0x20(%rsi),%xmm0 - .byte 0x66,0x0f,0x38,0xde,0x46,0x20 -// aesdec 0x30(%rsi),%xmm0 - .byte 0x66,0x0f,0x38,0xde,0x46,0x30 -// aesdec 0x40(%rsi),%xmm0 - .byte 0x66,0x0f,0x38,0xde,0x46,0x40 -// aesdec 0x50(%rsi),%xmm0 - .byte 0x66,0x0f,0x38,0xde,0x46,0x50 -// aesdec 0x60(%rsi),%xmm0 - .byte 0x66,0x0f,0x38,0xde,0x46,0x60 -// aesdec 0x70(%rsi),%xmm0 - .byte 0x66,0x0f,0x38,0xde,0x46,0x70 -// aesdec 0x80(%rsi),%xmm0 - .byte 0x66,0x0f,0x38,0xde,0x86,0x80,0x00,0x00,0x00 -// aesdec 0x90(%rsi),%xmm0 - .byte 0x66,0x0f,0x38,0xde,0x86,0x90,0x00,0x00,0x00 - jge 2f -// aesdeclast 0xa0(%rsi),%xmm0 - .byte 0x66,0x0f,0x38,0xdf,0x86,0xa0,0x00,0x00,0x00 - jmp 4f -2: -// aesdec 0xa0(%rsi),%xmm0 - .byte 0x66,0x0f,0x38,0xde,0x86,0xa0,0x00,0x00,0x00 -// aesdec 0xb0(%rsi),%xmm0 - .byte 0x66,0x0f,0x38,0xde,0x86,0xb0,0x00,0x00,0x00 - jg 3f -// aesdeclast 0xc0(%rsi),%xmm0 - .byte 0x66,0x0f,0x38,0xdf,0x86,0xc0,0x00,0x00,0x00 - jmp 4f -3: -// aesdec 0xc0(%rsi),%xmm0 - .byte 0x66,0x0f,0x38,0xde,0x86,0xc0,0x00,0x00,0x00 -// aesdec 0xd0(%rsi),%xmm0 - .byte 0x66,0x0f,0x38,0xde,0x86,0xd0,0x00,0x00,0x00 -// aesdeclast 0xe0(%rsi),%xmm0 - .byte 0x66,0x0f,0x38,0xdf,0x86,0xe0,0x00,0x00,0x00 -4: - pxor %xmm1,%xmm0 - movdqu %xmm0,(%rcx) - movdqa %xmm2,%xmm1 // iv - addq $0x10,%rcx - decq %rdx - jne 1b - retq - .cfi_endproc -END(aesni_decrypt_cbc) - - .ident "$FreeBSD$" diff --git a/sys/crypto/aesni/aesencdec_i386.S b/sys/crypto/aesni/aesencdec_i386.S deleted file mode 100644 index 78de311f23c..00000000000 --- a/sys/crypto/aesni/aesencdec_i386.S +++ /dev/null @@ -1,166 +0,0 @@ -/*- - * Copyright (c) 2010 Konstantin Belousov - * 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. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHORS 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 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 - -ENTRY(aesni_enc) - .cfi_startproc - pushl %ebp - .cfi_adjust_cfa_offset 4 - movl %esp,%ebp - movl 8(%ebp),%ecx /* rounds */ - movl 16(%ebp),%edx - movdqu (%edx),%xmm0 /* from */ - movl 24(%ebp),%eax /* iv */ - cmpl $0,%eax - je 1f - movdqu (%eax),%xmm1 - pxor %xmm1,%xmm0 -1: - movl 12(%ebp),%eax /* key */ - pxor (%eax),%xmm0 -2: - addl $0x10,%eax -// aesenc (%eax),%xmm0 - .byte 0x66,0x0f,0x38,0xdc,0x00 - loopne 2b - addl $0x10,%eax -// aesenclast (%eax),%xmm0 - .byte 0x66,0x0f,0x38,0xdd,0x00 - movl 20(%ebp),%eax - movdqu %xmm0,(%eax) /* to */ - leave - .cfi_adjust_cfa_offset -4 - retl - .cfi_endproc -END(aesni_enc) - -ENTRY(aesni_dec) - .cfi_startproc - pushl %ebp - .cfi_adjust_cfa_offset 4 - movl %esp,%ebp - movl 8(%ebp),%ecx /* rounds */ - movl 16(%ebp),%edx - movdqu (%edx),%xmm0 /* from */ - movl 12(%ebp),%eax /* key */ - pxor (%eax),%xmm0 -1: - addl $0x10,%eax -// aesdec (%eax),%xmm0 - .byte 0x66,0x0f,0x38,0xde,0x00 - loopne 1b - addl $0x10,%eax -// aesdeclast (%eax),%xmm0 - .byte 0x66,0x0f,0x38,0xdf,0x00 - movl 24(%ebp),%eax - cmpl $0,%eax /* iv */ - je 2f - movdqu (%eax),%xmm1 - pxor %xmm1,%xmm0 -2: - movl 20(%ebp),%eax - movdqu %xmm0,(%eax) /* to */ - leave - .cfi_adjust_cfa_offset -4 - retl - .cfi_endproc -END(aesni_dec) - -ENTRY(aesni_decrypt_cbc) - .cfi_startproc - pushl %ebp - .cfi_adjust_cfa_offset 4 - movl %esp,%ebp - pushl %ebx - pushl %esi - movl 12(%ebp),%eax /* key */ - movl 16(%ebp),%ecx /* length */ - shrl $4,%ecx - movl 20(%ebp),%ebx /* buf */ - movl 24(%ebp),%esi - movdqu (%esi),%xmm1 /* iv */ - movl 8(%ebp),%esi /* rounds */ -1: - movdqu (%ebx),%xmm0 - movdqa %xmm0,%xmm2 - pxor (%eax),%xmm0 - cmpl $12,%esi -// aesdec 0x10(%eax),%xmm0 - .byte 0x66,0x0f,0x38,0xde,0x40,0x10 -// aesdec 0x20(%eax),%xmm0 - .byte 0x66,0x0f,0x38,0xde,0x40,0x20 -// aesdec 0x30(%eax),%xmm0 - .byte 0x66,0x0f,0x38,0xde,0x40,0x30 -// aesdec 0x40(%eax),%xmm0 - .byte 0x66,0x0f,0x38,0xde,0x40,0x40 -// aesdec 0x50(%eax),%xmm0 - .byte 0x66,0x0f,0x38,0xde,0x40,0x50 -// aesdec 0x60(%eax),%xmm0 - .byte 0x66,0x0f,0x38,0xde,0x40,0x60 -// aesdec 0x70(%eax),%xmm0 - .byte 0x66,0x0f,0x38,0xde,0x40,0x70 -// aesdec 0x80(%eax),%xmm0 - .byte 0x66,0x0f,0x38,0xde,0x80,0x80,0x00,0x00,0x00 -// aesdec 0x90(%eax),%xmm0 - .byte 0x66,0x0f,0x38,0xde,0x80,0x90,0x00,0x00,0x00 - jge 2f -// aesdeclast 0xa0(%eax),%xmm0 - .byte 0x66,0x0f,0x38,0xdf,0x80,0xa0,0x00,0x00,0x00 - jmp 4f -2: -// aesdec 0xa0(%eax),%xmm0 - .byte 0x66,0x0f,0x38,0xde,0x80,0xa0,0x00,0x00,0x00 -// aesdec 0xb0(%eax),%xmm0 - .byte 0x66,0x0f,0x38,0xde,0x80,0xb0,0x00,0x00,0x00 - jg 3f -// aesdeclast 0xc0(%eax),%xmm0 - .byte 0x66,0x0f,0x38,0xdf,0x80,0xc0,0x00,0x00,0x00 - jmp 4f -3: -// aesdec 0xc0(%eax),%xmm0 - .byte 0x66,0x0f,0x38,0xde,0x80,0xc0,0x00,0x00,0x00 -// aesdec 0xd0(%eax),%xmm0 - .byte 0x66,0x0f,0x38,0xde,0x80,0xd0,0x00,0x00,0x00 -// aesdeclast 0xe0(%eax),%xmm0 - .byte 0x66,0x0f,0x38,0xdf,0x80,0xe0,0x00,0x00,0x00 -4: - pxor %xmm1,%xmm0 - movdqu %xmm0,(%ebx) - movdqa %xmm2,%xmm1 - addl $0x10,%ebx - decl %ecx - jne 1b - - popl %esi - popl %ebx - leave - .cfi_adjust_cfa_offset -4 - retl - .cfi_endproc -END(aesni_decrypt_cbc) - - .ident "$FreeBSD$" diff --git a/sys/crypto/aesni/aeskeys_amd64.S b/sys/crypto/aesni/aeskeys_amd64.S index 23a4d3dc1fb..9b3e98c8437 100644 --- a/sys/crypto/aesni/aeskeys_amd64.S +++ b/sys/crypto/aesni/aeskeys_amd64.S @@ -125,103 +125,72 @@ ENTRY(aesni_set_enckey) movups 0x10(%rdi),%xmm2 # other user key movaps %xmm2,(%rsi) addq $0x10,%rsi -// aeskeygenassist $0x1,%xmm2,%xmm1 # round 1 - .byte 0x66,0x0f,0x3a,0xdf,0xca,0x01 + aeskeygenassist $0x1,%xmm2,%xmm1 # round 1 call _key_expansion_256a -// aeskeygenassist $0x1,%xmm0,%xmm1 - .byte 0x66,0x0f,0x3a,0xdf,0xc8,0x01 + aeskeygenassist $0x1,%xmm0,%xmm1 call _key_expansion_256b -// aeskeygenassist $0x2,%xmm2,%xmm1 # round 2 - .byte 0x66,0x0f,0x3a,0xdf,0xca,0x02 + aeskeygenassist $0x2,%xmm2,%xmm1 # round 2 call _key_expansion_256a -// aeskeygenassist $0x2,%xmm0,%xmm1 - .byte 0x66,0x0f,0x3a,0xdf,0xc8,0x02 + aeskeygenassist $0x2,%xmm0,%xmm1 call _key_expansion_256b -// aeskeygenassist $0x4,%xmm2,%xmm1 # round 3 - .byte 0x66,0x0f,0x3a,0xdf,0xca,0x04 + aeskeygenassist $0x4,%xmm2,%xmm1 # round 3 call _key_expansion_256a -// aeskeygenassist $0x4,%xmm0,%xmm1 - .byte 0x66,0x0f,0x3a,0xdf,0xc8,0x04 + aeskeygenassist $0x4,%xmm0,%xmm1 call _key_expansion_256b -// aeskeygenassist $0x8,%xmm2,%xmm1 # round 4 - .byte 0x66,0x0f,0x3a,0xdf,0xca,0x08 + aeskeygenassist $0x8,%xmm2,%xmm1 # round 4 call _key_expansion_256a -// aeskeygenassist $0x8,%xmm0,%xmm1 - .byte 0x66,0x0f,0x3a,0xdf,0xc8,0x08 + aeskeygenassist $0x8,%xmm0,%xmm1 call _key_expansion_256b -// aeskeygenassist $0x10,%xmm2,%xmm1 # round 5 - .byte 0x66,0x0f,0x3a,0xdf,0xca,0x10 + aeskeygenassist $0x10,%xmm2,%xmm1 # round 5 call _key_expansion_256a -// aeskeygenassist $0x10,%xmm0,%xmm1 - .byte 0x66,0x0f,0x3a,0xdf,0xc8,0x10 + aeskeygenassist $0x10,%xmm0,%xmm1 call _key_expansion_256b -// aeskeygenassist $0x20,%xmm2,%xmm1 # round 6 - .byte 0x66,0x0f,0x3a,0xdf,0xca,0x20 + aeskeygenassist $0x20,%xmm2,%xmm1 # round 6 call _key_expansion_256a -// aeskeygenassist $0x20,%xmm0,%xmm1 - .byte 0x66,0x0f,0x3a,0xdf,0xc8,0x20 + aeskeygenassist $0x20,%xmm0,%xmm1 call _key_expansion_256b -// aeskeygenassist $0x40,%xmm2,%xmm1 # round 7 - .byte 0x66,0x0f,0x3a,0xdf,0xca,0x40 + aeskeygenassist $0x40,%xmm2,%xmm1 # round 7 call _key_expansion_256a retq .Lenc_key192: movq 0x10(%rdi),%xmm2 # other user key -// aeskeygenassist $0x1,%xmm2,%xmm1 # round 1 - .byte 0x66,0x0f,0x3a,0xdf,0xca,0x01 + aeskeygenassist $0x1,%xmm2,%xmm1 # round 1 call _key_expansion_192a -// aeskeygenassist $0x2,%xmm2,%xmm1 # round 2 - .byte 0x66,0x0f,0x3a,0xdf,0xca,0x02 + aeskeygenassist $0x2,%xmm2,%xmm1 # round 2 call _key_expansion_192b -// aeskeygenassist $0x4,%xmm2,%xmm1 # round 3 - .byte 0x66,0x0f,0x3a,0xdf,0xca,0x04 + aeskeygenassist $0x4,%xmm2,%xmm1 # round 3 call _key_expansion_192a -// aeskeygenassist $0x8,%xmm2,%xmm1 # round 4 - .byte 0x66,0x0f,0x3a,0xdf,0xca,0x08 + aeskeygenassist $0x8,%xmm2,%xmm1 # round 4 call _key_expansion_192b -// aeskeygenassist $0x10,%xmm2,%xmm1 # round 5 - .byte 0x66,0x0f,0x3a,0xdf,0xca,0x10 + aeskeygenassist $0x10,%xmm2,%xmm1 # round 5 call _key_expansion_192a -// aeskeygenassist $0x20,%xmm2,%xmm1 # round 6 - .byte 0x66,0x0f,0x3a,0xdf,0xca,0x20 + aeskeygenassist $0x20,%xmm2,%xmm1 # round 6 call _key_expansion_192b -// aeskeygenassist $0x40,%xmm2,%xmm1 # round 7 - .byte 0x66,0x0f,0x3a,0xdf,0xca,0x40 + aeskeygenassist $0x40,%xmm2,%xmm1 # round 7 call _key_expansion_192a -// aeskeygenassist $0x80,%xmm2,%xmm1 # round 8 - .byte 0x66,0x0f,0x3a,0xdf,0xca,0x80 + aeskeygenassist $0x80,%xmm2,%xmm1 # round 8 call _key_expansion_192b retq .Lenc_key128: -// aeskeygenassist $0x1,%xmm0,%xmm1 # round 1 - .byte 0x66,0x0f,0x3a,0xdf,0xc8,0x01 + aeskeygenassist $0x1,%xmm0,%xmm1 # round 1 call _key_expansion_128 -// aeskeygenassist $0x2,%xmm0,%xmm1 # round 2 - .byte 0x66,0x0f,0x3a,0xdf,0xc8,0x02 + aeskeygenassist $0x2,%xmm0,%xmm1 # round 2 call _key_expansion_128 -// aeskeygenassist $0x4,%xmm0,%xmm1 # round 3 - .byte 0x66,0x0f,0x3a,0xdf,0xc8,0x04 + aeskeygenassist $0x4,%xmm0,%xmm1 # round 3 call _key_expansion_128 -// aeskeygenassist $0x8,%xmm0,%xmm1 # round 4 - .byte 0x66,0x0f,0x3a,0xdf,0xc8,0x08 + aeskeygenassist $0x8,%xmm0,%xmm1 # round 4 call _key_expansion_128 -// aeskeygenassist $0x10,%xmm0,%xmm1 # round 5 - .byte 0x66,0x0f,0x3a,0xdf,0xc8,0x10 + aeskeygenassist $0x10,%xmm0,%xmm1 # round 5 call _key_expansion_128 -// aeskeygenassist $0x20,%xmm0,%xmm1 # round 6 - .byte 0x66,0x0f,0x3a,0xdf,0xc8,0x20 + aeskeygenassist $0x20,%xmm0,%xmm1 # round 6 call _key_expansion_128 -// aeskeygenassist $0x40,%xmm0,%xmm1 # round 7 - .byte 0x66,0x0f,0x3a,0xdf,0xc8,0x40 + aeskeygenassist $0x40,%xmm0,%xmm1 # round 7 call _key_expansion_128 -// aeskeygenassist $0x80,%xmm0,%xmm1 # round 8 - .byte 0x66,0x0f,0x3a,0xdf,0xc8,0x80 + aeskeygenassist $0x80,%xmm0,%xmm1 # round 8 call _key_expansion_128 -// aeskeygenassist $0x1b,%xmm0,%xmm1 # round 9 - .byte 0x66,0x0f,0x3a,0xdf,0xc8,0x1b + aeskeygenassist $0x1b,%xmm0,%xmm1 # round 9 call _key_expansion_128 -// aeskeygenassist $0x36,%xmm0,%xmm1 # round 10 - .byte 0x66,0x0f,0x3a,0xdf,0xc8,0x36 + aeskeygenassist $0x36,%xmm0,%xmm1 # round 10 call _key_expansion_128 retq .cfi_endproc @@ -238,8 +207,7 @@ ENTRY(aesni_set_deckey) 1: addq $0x10,%rsi subq $0x10,%rdi -// aesimc (%rdi),%xmm1 - .byte 0x66,0x0f,0x38,0xdb,0x0f + aesimc (%rdi),%xmm1 movdqa %xmm1,(%rsi) decl %edx jne 1b diff --git a/sys/crypto/aesni/aesni.c b/sys/crypto/aesni/aesni.c index ca00a578cad..73eb28a16e3 100644 --- a/sys/crypto/aesni/aesni.c +++ b/sys/crypto/aesni/aesni.c @@ -40,7 +40,7 @@ __FBSDID("$FreeBSD$"); #include #include #include -#include "cryptodev_if.h" +#include struct aesni_softc { int32_t cid; @@ -74,6 +74,12 @@ aesni_probe(device_t dev) device_printf(dev, "No AESNI support.\n"); return (EINVAL); } + + if ((cpu_feature & CPUID_SSE2) == 0) { + device_printf(dev, "No SSE2 support but AESNI!?!\n"); + return (EINVAL); + } + device_set_desc_copy(dev, "AES-CBC,AES-XTS"); return (0); } diff --git a/sys/crypto/aesni/aesni.h b/sys/crypto/aesni/aesni.h index 78255b779a3..17ca9c5bf7f 100644 --- a/sys/crypto/aesni/aesni.h +++ b/sys/crypto/aesni/aesni.h @@ -71,12 +71,6 @@ struct aesni_session { /* * Internal functions, implemented in assembler. */ -void aesni_enc(int rounds, const uint8_t *key_schedule, - const uint8_t from[AES_BLOCK_LEN], uint8_t to[AES_BLOCK_LEN], - const uint8_t iv[AES_BLOCK_LEN]); -void aesni_dec(int rounds, const uint8_t *key_schedule, - const uint8_t from[AES_BLOCK_LEN], uint8_t to[AES_BLOCK_LEN], - const uint8_t iv[AES_BLOCK_LEN]); void aesni_set_enckey(const uint8_t *userkey, uint8_t *encrypt_schedule, int number_of_rounds); void aesni_set_deckey(const uint8_t *encrypt_schedule, @@ -88,12 +82,19 @@ void aesni_set_deckey(const uint8_t *encrypt_schedule, void aesni_encrypt_cbc(int rounds, const void *key_schedule, size_t len, const uint8_t *from, uint8_t *to, const uint8_t iv[AES_BLOCK_LEN]); void aesni_decrypt_cbc(int rounds, const void *key_schedule, size_t len, - const uint8_t *from, const uint8_t iv[AES_BLOCK_LEN]); + uint8_t *buf, const uint8_t iv[AES_BLOCK_LEN]); void aesni_encrypt_ecb(int rounds, const void *key_schedule, size_t len, const uint8_t from[AES_BLOCK_LEN], uint8_t to[AES_BLOCK_LEN]); void aesni_decrypt_ecb(int rounds, const void *key_schedule, size_t len, const uint8_t from[AES_BLOCK_LEN], uint8_t to[AES_BLOCK_LEN]); +void aesni_encrypt_xts(int rounds, const void *data_schedule, + const void *tweak_schedule, size_t len, const uint8_t *from, uint8_t *to, + const uint8_t iv[AES_BLOCK_LEN]); +void aesni_decrypt_xts(int rounds, const void *data_schedule, + const void *tweak_schedule, size_t len, const uint8_t *from, uint8_t *to, + const uint8_t iv[AES_BLOCK_LEN]); + int aesni_cipher_setup(struct aesni_session *ses, struct cryptoini *encini); int aesni_cipher_process(struct aesni_session *ses, diff --git a/sys/crypto/aesni/aesni_wrap.c b/sys/crypto/aesni/aesni_wrap.c index 3340b1fcb6c..197baf7f077 100644 --- a/sys/crypto/aesni/aesni_wrap.c +++ b/sys/crypto/aesni/aesni_wrap.c @@ -2,6 +2,7 @@ * Copyright (C) 2008 Damien Miller * Copyright (c) 2010 Konstantin Belousov * Copyright (c) 2010-2011 Pawel Jakub Dawidek + * Copyright 2012-2013 John-Mark Gurney * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -28,13 +29,15 @@ #include __FBSDID("$FreeBSD$"); - + #include #include #include #include #include #include + +#include "aesencdec.h" MALLOC_DECLARE(M_AESNI); @@ -42,28 +45,78 @@ void aesni_encrypt_cbc(int rounds, const void *key_schedule, size_t len, const uint8_t *from, uint8_t *to, const uint8_t iv[AES_BLOCK_LEN]) { - const uint8_t *ivp; + __m128i tot, ivreg; size_t i; len /= AES_BLOCK_LEN; - ivp = iv; + ivreg = _mm_loadu_si128((const __m128i *)iv); for (i = 0; i < len; i++) { - aesni_enc(rounds - 1, key_schedule, from, to, ivp); - ivp = to; + tot = aesni_enc(rounds - 1, key_schedule, + _mm_loadu_si128((const __m128i *)from) ^ ivreg); + ivreg = tot; + _mm_storeu_si128((__m128i *)to, tot); from += AES_BLOCK_LEN; to += AES_BLOCK_LEN; } } void -aesni_encrypt_ecb(int rounds, const void *key_schedule, size_t len, - const uint8_t from[AES_BLOCK_LEN], uint8_t to[AES_BLOCK_LEN]) +aesni_decrypt_cbc(int rounds, const void *key_schedule, size_t len, + uint8_t *buf, const uint8_t iv[AES_BLOCK_LEN]) { - size_t i; + __m128i blocks[8]; + __m128i *bufs; + __m128i ivreg, nextiv; + size_t i, j, cnt; - len /= AES_BLOCK_LEN; - for (i = 0; i < len; i++) { - aesni_enc(rounds - 1, key_schedule, from, to, NULL); + ivreg = _mm_loadu_si128((const __m128i *)iv); + cnt = len / AES_BLOCK_LEN / 8; + for (i = 0; i < cnt; i++) { + bufs = (__m128i *)buf; + aesni_dec8(rounds - 1, key_schedule, bufs[0], bufs[1], + bufs[2], bufs[3], bufs[4], bufs[5], bufs[6], + bufs[7], &blocks[0]); + for (j = 0; j < 8; j++) { + nextiv = bufs[j]; + bufs[j] = blocks[j] ^ ivreg; + ivreg = nextiv; + } + buf += AES_BLOCK_LEN * 8; + } + i *= 8; + cnt = len / AES_BLOCK_LEN; + for (; i < cnt; i++) { + bufs = (__m128i *)buf; + nextiv = bufs[0]; + bufs[0] = aesni_dec(rounds - 1, key_schedule, bufs[0]) ^ ivreg; + ivreg = nextiv; + buf += AES_BLOCK_LEN; + } +} + +void +aesni_encrypt_ecb(int rounds, const void *key_schedule, size_t len, + const uint8_t *from, uint8_t *to) +{ + __m128i tot; + const __m128i *blocks; + size_t i, cnt; + + cnt = len / AES_BLOCK_LEN / 8; + for (i = 0; i < cnt; i++) { + blocks = (const __m128i *)from; + aesni_enc8(rounds - 1, key_schedule, blocks[0], blocks[1], + blocks[2], blocks[3], blocks[4], blocks[5], blocks[6], + blocks[7], (__m128i *)to); + from += AES_BLOCK_LEN * 8; + to += AES_BLOCK_LEN * 8; + } + i *= 8; + cnt = len / AES_BLOCK_LEN; + for (; i < cnt; i++) { + tot = aesni_enc(rounds - 1, key_schedule, + _mm_loadu_si128((const __m128i *)from)); + _mm_storeu_si128((__m128i *)to, tot); from += AES_BLOCK_LEN; to += AES_BLOCK_LEN; } @@ -73,11 +126,25 @@ void aesni_decrypt_ecb(int rounds, const void *key_schedule, size_t len, const uint8_t from[AES_BLOCK_LEN], uint8_t to[AES_BLOCK_LEN]) { - size_t i; + __m128i tot; + const __m128i *blocks; + size_t i, cnt; - len /= AES_BLOCK_LEN; - for (i = 0; i < len; i++) { - aesni_dec(rounds - 1, key_schedule, from, to, NULL); + cnt = len / AES_BLOCK_LEN / 8; + for (i = 0; i < cnt; i++) { + blocks = (const __m128i *)from; + aesni_dec8(rounds - 1, key_schedule, blocks[0], blocks[1], + blocks[2], blocks[3], blocks[4], blocks[5], blocks[6], + blocks[7], (__m128i *)to); + from += AES_BLOCK_LEN * 8; + to += AES_BLOCK_LEN * 8; + } + i *= 8; + cnt = len / AES_BLOCK_LEN; + for (; i < cnt; i++) { + tot = aesni_dec(rounds - 1, key_schedule, + _mm_loadu_si128((const __m128i *)from)); + _mm_storeu_si128((__m128i *)to, tot); from += AES_BLOCK_LEN; to += AES_BLOCK_LEN; } @@ -87,34 +154,88 @@ aesni_decrypt_ecb(int rounds, const void *key_schedule, size_t len, #define AES_XTS_IVSIZE 8 #define AES_XTS_ALPHA 0x87 /* GF(2^128) generator polynomial */ -static void -aesni_crypt_xts_block(int rounds, const void *key_schedule, uint64_t *tweak, - const uint64_t *from, uint64_t *to, uint64_t *block, int do_encrypt) +static inline __m128i +xts_crank_lfsr(__m128i inp) { - int carry; + const __m128i alphamask = _mm_set_epi32(1, 1, 1, AES_XTS_ALPHA); + __m128i xtweak, ret; - block[0] = from[0] ^ tweak[0]; - block[1] = from[1] ^ tweak[1]; + /* set up xor mask */ + xtweak = _mm_shuffle_epi32(inp, 0x93); + xtweak = _mm_srai_epi32(xtweak, 31); + xtweak &= alphamask; + + /* next term */ + ret = _mm_slli_epi32(inp, 1); + ret ^= xtweak; + + return ret; +} + +static void +aesni_crypt_xts_block(int rounds, const void *key_schedule, __m128i *tweak, + const __m128i *from, __m128i *to, int do_encrypt) +{ + __m128i block; + + block = *from ^ *tweak; if (do_encrypt) - aesni_enc(rounds - 1, key_schedule, (uint8_t *)block, (uint8_t *)to, NULL); + block = aesni_enc(rounds - 1, key_schedule, block); else - aesni_dec(rounds - 1, key_schedule, (uint8_t *)block, (uint8_t *)to, NULL); + block = aesni_dec(rounds - 1, key_schedule, block); - to[0] ^= tweak[0]; - to[1] ^= tweak[1]; + *to = block ^ *tweak; - /* Exponentiate tweak. */ - carry = ((tweak[0] & 0x8000000000000000ULL) > 0); - tweak[0] <<= 1; - if (tweak[1] & 0x8000000000000000ULL) { - uint8_t *twk = (uint8_t *)tweak; + *tweak = xts_crank_lfsr(*tweak); +} - twk[0] ^= AES_XTS_ALPHA; - } - tweak[1] <<= 1; - if (carry) - tweak[1] |= 1; +static void +aesni_crypt_xts_block8(int rounds, const void *key_schedule, __m128i *tweak, + const __m128i *from, __m128i *to, int do_encrypt) +{ + __m128i tmptweak; + __m128i a, b, c, d, e, f, g, h; + __m128i tweaks[8]; + __m128i tmp[8]; + + tmptweak = *tweak; + + /* + * unroll the loop. This lets gcc put values directly in the + * register and saves memory accesses. + */ +#define PREPINP(v, pos) \ + do { \ + tweaks[(pos)] = tmptweak; \ + (v) = from[(pos)] ^ tmptweak; \ + tmptweak = xts_crank_lfsr(tmptweak); \ + } while (0) + PREPINP(a, 0); + PREPINP(b, 1); + PREPINP(c, 2); + PREPINP(d, 3); + PREPINP(e, 4); + PREPINP(f, 5); + PREPINP(g, 6); + PREPINP(h, 7); + *tweak = tmptweak; + + if (do_encrypt) + aesni_enc8(rounds - 1, key_schedule, a, b, c, d, e, f, g, h, + tmp); + else + aesni_dec8(rounds - 1, key_schedule, a, b, c, d, e, f, g, h, + tmp); + + to[0] = tmp[0] ^ tweaks[0]; + to[1] = tmp[1] ^ tweaks[1]; + to[2] = tmp[2] ^ tweaks[2]; + to[3] = tmp[3] ^ tweaks[3]; + to[4] = tmp[4] ^ tweaks[4]; + to[5] = tmp[5] ^ tweaks[5]; + to[6] = tmp[6] ^ tweaks[6]; + to[7] = tmp[7] ^ tweaks[7]; } static void @@ -122,9 +243,9 @@ aesni_crypt_xts(int rounds, const void *data_schedule, const void *tweak_schedule, size_t len, const uint8_t *from, uint8_t *to, const uint8_t iv[AES_BLOCK_LEN], int do_encrypt) { - uint64_t block[AES_XTS_BLOCKSIZE / 8]; - uint8_t tweak[AES_XTS_BLOCKSIZE]; - size_t i; + __m128i tweakreg; + uint8_t tweak[AES_XTS_BLOCKSIZE] __aligned(16); + size_t i, cnt; /* * Prepare tweak as E_k2(IV). IV is specified as LE representation @@ -137,21 +258,27 @@ aesni_crypt_xts(int rounds, const void *data_schedule, #else #error Only LITTLE_ENDIAN architectures are supported. #endif - aesni_enc(rounds - 1, tweak_schedule, tweak, tweak, NULL); + tweakreg = _mm_loadu_si128((__m128i *)&tweak[0]); + tweakreg = aesni_enc(rounds - 1, tweak_schedule, tweakreg); - len /= AES_XTS_BLOCKSIZE; - for (i = 0; i < len; i++) { - aesni_crypt_xts_block(rounds, data_schedule, (uint64_t *)tweak, - (const uint64_t *)from, (uint64_t *)to, block, do_encrypt); + cnt = len / AES_XTS_BLOCKSIZE / 8; + for (i = 0; i < cnt; i++) { + aesni_crypt_xts_block8(rounds, data_schedule, &tweakreg, + (const __m128i *)from, (__m128i *)to, do_encrypt); + from += AES_XTS_BLOCKSIZE * 8; + to += AES_XTS_BLOCKSIZE * 8; + } + i *= 8; + cnt = len / AES_XTS_BLOCKSIZE; + for (; i < cnt; i++) { + aesni_crypt_xts_block(rounds, data_schedule, &tweakreg, + (const __m128i *)from, (__m128i *)to, do_encrypt); from += AES_XTS_BLOCKSIZE; to += AES_XTS_BLOCKSIZE; } - - bzero(tweak, sizeof(tweak)); - bzero(block, sizeof(block)); } -static void +void aesni_encrypt_xts(int rounds, const void *data_schedule, const void *tweak_schedule, size_t len, const uint8_t *from, uint8_t *to, const uint8_t iv[AES_BLOCK_LEN]) @@ -161,7 +288,7 @@ aesni_encrypt_xts(int rounds, const void *data_schedule, iv, 1); } -static void +void aesni_decrypt_xts(int rounds, const void *data_schedule, const void *tweak_schedule, size_t len, const uint8_t *from, uint8_t *to, const uint8_t iv[AES_BLOCK_LEN]) diff --git a/sys/dev/aac/aac_linux.c b/sys/dev/aac/aac_linux.c index 049e2be78e5..591dfbbdcaa 100644 --- a/sys/dev/aac/aac_linux.c +++ b/sys/dev/aac/aac_linux.c @@ -75,11 +75,13 @@ MODULE_DEPEND(aac_linux, linux, 1, 1, 1); static int aac_linux_ioctl(struct thread *td, struct linux_ioctl_args *args) { + cap_rights_t rights; struct file *fp; u_long cmd; int error; - if ((error = fget(td, args->fd, CAP_IOCTL, &fp)) != 0) + error = fget(td, args->fd, cap_rights_init(&rights, CAP_IOCTL), &fp); + if (error != 0) return (error); cmd = args->cmd; diff --git a/sys/dev/aacraid/aacraid_linux.c b/sys/dev/aacraid/aacraid_linux.c index 3d85445fa82..e58d0a47a80 100644 --- a/sys/dev/aacraid/aacraid_linux.c +++ b/sys/dev/aacraid/aacraid_linux.c @@ -34,6 +34,9 @@ __FBSDID("$FreeBSD$"); */ #include +#if __FreeBSD_version >= 900000 +#include +#endif #include #include #include @@ -77,15 +80,19 @@ static int aacraid_linux_ioctl(struct thread *td, struct linux_ioctl_args *args) { struct file *fp; +#if __FreeBSD_version >= 900000 + cap_rights_t rights; +#endif u_long cmd; int error; + if ((error = fget(td, args->fd, #if __FreeBSD_version >= 900000 - if ((error = fget(td, args->fd, 0, &fp)) != 0) -#else - if ((error = fget(td, args->fd, &fp)) != 0) + cap_rights_init(&rights, CAP_IOCTL), #endif + &fp)) != 0) { return (error); + } cmd = args->cmd; /* diff --git a/sys/dev/aacraid/aacraid_pci.c b/sys/dev/aacraid/aacraid_pci.c index 6204fdf50bf..0b91f2df147 100644 --- a/sys/dev/aacraid/aacraid_pci.c +++ b/sys/dev/aacraid/aacraid_pci.c @@ -169,18 +169,12 @@ aacraid_pci_attach(device_t dev) /* * Verify that the adapter is correctly set up in PCI space. */ - command = pci_read_config(sc->aac_dev, PCIR_COMMAND, 2); - command |= PCIM_CMD_BUSMASTEREN; - pci_write_config(dev, PCIR_COMMAND, command, 2); + pci_enable_busmaster(dev); command = pci_read_config(sc->aac_dev, PCIR_COMMAND, 2); if (!(command & PCIM_CMD_BUSMASTEREN)) { device_printf(sc->aac_dev, "can't enable bus-master feature\n"); goto out; } - if ((command & PCIM_CMD_MEMEN) == 0) { - device_printf(sc->aac_dev, "memory window not available\n"); - goto out; - } /* * Detect the hardware interface version, set up the bus interface diff --git a/sys/dev/acpica/Osd/OsdSynch.c b/sys/dev/acpica/Osd/OsdSynch.c index b70a12051ee..99a69ec7752 100644 --- a/sys/dev/acpica/Osd/OsdSynch.c +++ b/sys/dev/acpica/Osd/OsdSynch.c @@ -566,8 +566,6 @@ AcpiOsReleaseLock(ACPI_SPINLOCK Handle, ACPI_CPU_FLAGS Flags) } /* Section 5.2.10.1: global lock acquire/release functions */ -#define GL_BIT_PENDING 0x01 -#define GL_BIT_OWNED 0x02 /* * Acquire the global lock. If busy, set the pending bit. The caller @@ -575,18 +573,18 @@ AcpiOsReleaseLock(ACPI_SPINLOCK Handle, ACPI_CPU_FLAGS Flags) * and then attempt to acquire it again. */ int -acpi_acquire_global_lock(uint32_t *lock) +acpi_acquire_global_lock(volatile uint32_t *lock) { uint32_t new, old; do { old = *lock; - new = (old & ~GL_BIT_PENDING) | GL_BIT_OWNED; - if ((old & GL_BIT_OWNED) != 0) - new |= GL_BIT_PENDING; - } while (atomic_cmpset_acq_int(lock, old, new) == 0); + new = (old & ~ACPI_GLOCK_PENDING) | ACPI_GLOCK_OWNED; + if ((old & ACPI_GLOCK_OWNED) != 0) + new |= ACPI_GLOCK_PENDING; + } while (atomic_cmpset_32(lock, old, new) == 0); - return ((new & GL_BIT_PENDING) == 0); + return ((new & ACPI_GLOCK_PENDING) == 0); } /* @@ -595,14 +593,14 @@ acpi_acquire_global_lock(uint32_t *lock) * releases the lock. */ int -acpi_release_global_lock(uint32_t *lock) +acpi_release_global_lock(volatile uint32_t *lock) { uint32_t new, old; do { old = *lock; - new = old & ~(GL_BIT_PENDING | GL_BIT_OWNED); - } while (atomic_cmpset_rel_int(lock, old, new) == 0); + new = old & ~(ACPI_GLOCK_PENDING | ACPI_GLOCK_OWNED); + } while (atomic_cmpset_32(lock, old, new) == 0); - return ((old & GL_BIT_PENDING) != 0); + return ((old & ACPI_GLOCK_PENDING) != 0); } diff --git a/sys/dev/acpica/acpi_thermal.c b/sys/dev/acpica/acpi_thermal.c index 485da9e34df..4acf3c4db60 100644 --- a/sys/dev/acpica/acpi_thermal.c +++ b/sys/dev/acpica/acpi_thermal.c @@ -111,6 +111,7 @@ struct acpi_tz_softc { struct acpi_tz_zone tz_zone; /*Thermal zone parameters*/ int tz_validchecks; + int tz_insane_tmp_notified; /* passive cooling */ struct proc *tz_cooling_proc; @@ -161,6 +162,8 @@ static driver_t acpi_tz_driver = { sizeof(struct acpi_tz_softc), }; +static char *acpi_tz_tmp_name = "_TMP"; + static devclass_t acpi_tz_devclass; DRIVER_MODULE(acpi_tz, acpi, acpi_tz_driver, acpi_tz_devclass, 0, 0); MODULE_DEPEND(acpi_tz, acpi, 1, 1, 1); @@ -456,12 +459,11 @@ acpi_tz_get_temperature(struct acpi_tz_softc *sc) { int temp; ACPI_STATUS status; - static char *tmp_name = "_TMP"; ACPI_FUNCTION_NAME ("acpi_tz_get_temperature"); /* Evaluate the thermal zone's _TMP method. */ - status = acpi_GetInteger(sc->tz_handle, tmp_name, &temp); + status = acpi_GetInteger(sc->tz_handle, acpi_tz_tmp_name, &temp); if (ACPI_FAILURE(status)) { ACPI_VPRINT(sc->tz_dev, acpi_device_get_parent_softc(sc->tz_dev), "error fetching current temperature -- %s\n", @@ -470,7 +472,7 @@ acpi_tz_get_temperature(struct acpi_tz_softc *sc) } /* Check it for validity. */ - acpi_tz_sanity(sc, &temp, tmp_name); + acpi_tz_sanity(sc, &temp, acpi_tz_tmp_name); if (temp == -1) return (FALSE); @@ -696,10 +698,29 @@ static void acpi_tz_sanity(struct acpi_tz_softc *sc, int *val, char *what) { if (*val != -1 && (*val < TZ_ZEROC || *val > TZ_ZEROC + 2000)) { - device_printf(sc->tz_dev, "%s value is absurd, ignored (%d.%dC)\n", - what, TZ_KELVTOC(*val)); + /* + * If the value we are checking is _TMP, warn the user only + * once. This avoids spamming messages if, for instance, the + * sensor is broken and always returns an invalid temperature. + * + * This is only done for _TMP; other values always emit a + * warning. + */ + if (what != acpi_tz_tmp_name || !sc->tz_insane_tmp_notified) { + device_printf(sc->tz_dev, "%s value is absurd, ignored (%d.%dC)\n", + what, TZ_KELVTOC(*val)); + + /* Don't warn the user again if the read value doesn't improve. */ + if (what == acpi_tz_tmp_name) + sc->tz_insane_tmp_notified = 1; + } *val = -1; + return; } + + /* This value is correct. Warn if it's incorrect again. */ + if (what == acpi_tz_tmp_name) + sc->tz_insane_tmp_notified = 0; } /* diff --git a/sys/dev/advansys/adw_pci.c b/sys/dev/advansys/adw_pci.c index a7158b5b61e..108647e2bec 100644 --- a/sys/dev/advansys/adw_pci.c +++ b/sys/dev/advansys/adw_pci.c @@ -199,14 +199,13 @@ adw_pci_attach(device_t dev) { struct adw_softc *adw; struct adw_pci_identity *entry; - u_int32_t command; + u_int16_t command; struct resource *regs; int regs_type; int regs_id; int error; int zero; - command = pci_read_config(dev, PCIR_COMMAND, /*bytes*/1); entry = adw_find_pci_device(dev); if (entry == NULL) return (ENXIO); @@ -214,14 +213,11 @@ adw_pci_attach(device_t dev) regs_type = 0; regs_id = 0; #ifdef ADW_ALLOW_MEMIO - if ((command & PCIM_CMD_MEMEN) != 0) { - regs_type = SYS_RES_MEMORY; - regs_id = ADW_PCI_MEMBASE; - regs = bus_alloc_resource_any(dev, regs_type, - ®s_id, RF_ACTIVE); - } + regs_type = SYS_RES_MEMORY; + regs_id = ADW_PCI_MEMBASE; + regs = bus_alloc_resource_any(dev, regs_type, ®s_id, RF_ACTIVE); #endif - if (regs == NULL && (command & PCIM_CMD_PORTEN) != 0) { + if (regs == NULL) { regs_type = SYS_RES_IOPORT; regs_id = ADW_PCI_IOBASE; regs = bus_alloc_resource_any(dev, regs_type, @@ -296,6 +292,7 @@ adw_pci_attach(device_t dev) * 'control_flag' CONTROL_FLAG_IGNORE_PERR flag to tell the microcode * to ignore DMA parity errors. */ + command = pci_read_config(dev, PCIR_COMMAND, /*bytes*/2); if ((command & PCIM_CMD_PERRESPEN) == 0) adw_lram_write_16(adw, ADW_MC_CONTROL_FLAG, adw_lram_read_16(adw, ADW_MC_CONTROL_FLAG) diff --git a/sys/dev/agp/agp.c b/sys/dev/agp/agp.c index 2c3e4b95145..597094399f2 100644 --- a/sys/dev/agp/agp.c +++ b/sys/dev/agp/agp.c @@ -555,7 +555,7 @@ agp_generic_bind_memory(device_t dev, struct agp_memory *mem, * the pages will be allocated and zeroed. */ m = vm_page_grab(mem->am_obj, OFF_TO_IDX(i), - VM_ALLOC_WIRED | VM_ALLOC_ZERO | VM_ALLOC_RETRY); + VM_ALLOC_WIRED | VM_ALLOC_ZERO); AGP_DPF("found page pa=%#jx\n", (uintmax_t)VM_PAGE_TO_PHYS(m)); } VM_OBJECT_WUNLOCK(mem->am_obj); @@ -600,7 +600,7 @@ agp_generic_bind_memory(device_t dev, struct agp_memory *mem, goto bad; } } - vm_page_wakeup(m); + vm_page_xunbusy(m); } VM_OBJECT_WUNLOCK(mem->am_obj); @@ -627,7 +627,7 @@ bad: for (k = 0; k < mem->am_size; k += PAGE_SIZE) { m = vm_page_lookup(mem->am_obj, OFF_TO_IDX(k)); if (k >= i) - vm_page_wakeup(m); + vm_page_xunbusy(m); vm_page_lock(m); vm_page_unwire(m, 0); vm_page_unlock(m); diff --git a/sys/dev/agp/agp_i810.c b/sys/dev/agp/agp_i810.c index 63679e8c6e5..414a7a590a9 100644 --- a/sys/dev/agp/agp_i810.c +++ b/sys/dev/agp/agp_i810.c @@ -1970,7 +1970,7 @@ agp_i810_alloc_memory(device_t dev, int type, vm_size_t size) */ VM_OBJECT_WLOCK(mem->am_obj); m = vm_page_grab(mem->am_obj, 0, VM_ALLOC_NOBUSY | - VM_ALLOC_WIRED | VM_ALLOC_ZERO | VM_ALLOC_RETRY); + VM_ALLOC_WIRED | VM_ALLOC_ZERO); VM_OBJECT_WUNLOCK(mem->am_obj); mem->am_physical = VM_PAGE_TO_PHYS(m); } else { diff --git a/sys/dev/ahci/ahci.c b/sys/dev/ahci/ahci.c index 7c29bf10894..e45bc507b4a 100644 --- a/sys/dev/ahci/ahci.c +++ b/sys/dev/ahci/ahci.c @@ -229,6 +229,7 @@ static struct { {0x91301b4b, 0x00, "Marvell 88SE9130", AHCI_Q_NOBSYRES|AHCI_Q_ALTSIG}, {0x91721b4b, 0x00, "Marvell 88SE9172", AHCI_Q_NOBSYRES}, {0x91821b4b, 0x00, "Marvell 88SE9182", AHCI_Q_NOBSYRES}, + {0x91831b4b, 0x00, "Marvell 88SS9183", AHCI_Q_NOBSYRES}, {0x91a01b4b, 0x00, "Marvell 88SE91Ax", AHCI_Q_NOBSYRES}, {0x92151b4b, 0x00, "Marvell 88SE9215", AHCI_Q_NOBSYRES}, {0x92201b4b, 0x00, "Marvell 88SE9220", AHCI_Q_NOBSYRES|AHCI_Q_ALTSIG}, diff --git a/sys/dev/aic7xxx/ahc_pci.c b/sys/dev/aic7xxx/ahc_pci.c index 2392298661b..95a9f03aeff 100644 --- a/sys/dev/aic7xxx/ahc_pci.c +++ b/sys/dev/aic7xxx/ahc_pci.c @@ -139,12 +139,10 @@ int ahc_pci_map_registers(struct ahc_softc *ahc) { struct resource *regs; - u_int command; int regs_type; int regs_id; int allow_memio; - command = aic_pci_read_config(ahc->dev_softc, PCIR_COMMAND, /*bytes*/1); regs = NULL; regs_type = 0; regs_id = 0; @@ -166,7 +164,7 @@ ahc_pci_map_registers(struct ahc_softc *ahc) #endif } - if ((allow_memio != 0) && (command & PCIM_CMD_MEMEN) != 0) { + if (allow_memio != 0) { regs_type = SYS_RES_MEMORY; regs_id = AHC_PCI_MEMADDR; @@ -190,16 +188,11 @@ ahc_pci_map_registers(struct ahc_softc *ahc) bus_release_resource(ahc->dev_softc, regs_type, regs_id, regs); regs = NULL; - } else { - command &= ~PCIM_CMD_PORTEN; - aic_pci_write_config(ahc->dev_softc, - PCIR_COMMAND, - command, /*bytes*/1); } } } - if (regs == NULL && (command & PCIM_CMD_PORTEN) != 0) { + if (regs == NULL) { regs_type = SYS_RES_IOPORT; regs_id = AHC_PCI_IOADDR; regs = bus_alloc_resource_any(ahc->dev_softc, regs_type, @@ -217,11 +210,6 @@ ahc_pci_map_registers(struct ahc_softc *ahc) bus_release_resource(ahc->dev_softc, regs_type, regs_id, regs); regs = NULL; - } else { - command &= ~PCIM_CMD_MEMEN; - aic_pci_write_config(ahc->dev_softc, - PCIR_COMMAND, - command, /*bytes*/1); } } } diff --git a/sys/dev/aic7xxx/ahd_pci.c b/sys/dev/aic7xxx/ahd_pci.c index 0c06673ca51..2524cd222e1 100644 --- a/sys/dev/aic7xxx/ahd_pci.c +++ b/sys/dev/aic7xxx/ahd_pci.c @@ -143,13 +143,11 @@ ahd_pci_map_registers(struct ahd_softc *ahd) { struct resource *regs; struct resource *regs2; - u_int command; int regs_type; int regs_id; int regs_id2; int allow_memio; - command = aic_pci_read_config(ahd->dev_softc, PCIR_COMMAND, /*bytes*/1); regs = NULL; regs2 = NULL; regs_type = 0; @@ -165,8 +163,7 @@ ahd_pci_map_registers(struct ahd_softc *ahd) allow_memio = 1; } - if ((command & PCIM_CMD_MEMEN) != 0 - && (ahd->bugs & AHD_PCIX_MMAPIO_BUG) == 0 + if ((ahd->bugs & AHD_PCIX_MMAPIO_BUG) == 0 && allow_memio != 0) { regs_type = SYS_RES_MEMORY; @@ -199,15 +196,10 @@ ahd_pci_map_registers(struct ahd_softc *ahd) regs_id, regs); regs = NULL; AHD_CORRECTABLE_ERROR(ahd); - } else { - command &= ~PCIM_CMD_PORTEN; - aic_pci_write_config(ahd->dev_softc, - PCIR_COMMAND, - command, /*bytes*/1); } } } - if (regs == NULL && (command & PCIM_CMD_PORTEN) != 0) { + if (regs == NULL) { regs_type = SYS_RES_IOPORT; regs_id = AHD_PCI_IOADDR0; regs = bus_alloc_resource_any(ahd->dev_softc, regs_type, @@ -233,9 +225,6 @@ ahd_pci_map_registers(struct ahd_softc *ahd) } ahd->tags[1] = rman_get_bustag(regs2); ahd->bshs[1] = rman_get_bushandle(regs2); - command &= ~PCIM_CMD_MEMEN; - aic_pci_write_config(ahd->dev_softc, PCIR_COMMAND, - command, /*bytes*/1); ahd->platform_data->regs_res_type[1] = regs_type; ahd->platform_data->regs_res_id[1] = regs_id2; ahd->platform_data->regs[1] = regs2; diff --git a/sys/dev/amdtemp/amdtemp.c b/sys/dev/amdtemp/amdtemp.c index c55e680b23d..553545b4aa2 100644 --- a/sys/dev/amdtemp/amdtemp.c +++ b/sys/dev/amdtemp/amdtemp.c @@ -76,6 +76,7 @@ struct amdtemp_softc { #define DEVICEID_AMD_MISC0F 0x1103 #define DEVICEID_AMD_MISC10 0x1203 #define DEVICEID_AMD_MISC11 0x1303 +#define DEVICEID_AMD_MISC12 0x1403 #define DEVICEID_AMD_MISC14 0x1703 #define DEVICEID_AMD_MISC15 0x1603 @@ -86,6 +87,7 @@ static struct amdtemp_product { { VENDORID_AMD, DEVICEID_AMD_MISC0F }, { VENDORID_AMD, DEVICEID_AMD_MISC10 }, { VENDORID_AMD, DEVICEID_AMD_MISC11 }, + { VENDORID_AMD, DEVICEID_AMD_MISC12 }, { VENDORID_AMD, DEVICEID_AMD_MISC14 }, { VENDORID_AMD, DEVICEID_AMD_MISC15 }, { 0, 0 } diff --git a/sys/dev/amr/amr_linux.c b/sys/dev/amr/amr_linux.c index 44e858ba804..5b1a17f530b 100644 --- a/sys/dev/amr/amr_linux.c +++ b/sys/dev/amr/amr_linux.c @@ -72,10 +72,12 @@ MODULE_DEPEND(amr, linux, 1, 1, 1); static int amr_linux_ioctl(struct thread *p, struct linux_ioctl_args *args) { + cap_rights_t rights; struct file *fp; int error; - if ((error = fget(p, args->fd, CAP_IOCTL, &fp)) != 0) + error = fget(p, args->fd, cap_rights_init(&rights, CAP_IOCTL), &fp); + if (error != 0) return (error); error = fo_ioctl(fp, args->cmd, (caddr_t)args->arg, p->td_ucred, p); fdrop(fp, p); diff --git a/sys/dev/amr/amr_pci.c b/sys/dev/amr/amr_pci.c index f47d408764d..2f11f501284 100644 --- a/sys/dev/amr/amr_pci.c +++ b/sys/dev/amr/amr_pci.c @@ -184,7 +184,6 @@ amr_pci_attach(device_t dev) struct amr_softc *sc; struct amr_ident *id; int rid, rtype, error; - u_int32_t command; debug_called(1); @@ -204,24 +203,8 @@ amr_pci_attach(device_t dev) if ((id = amr_find_ident(dev)) == NULL) return (ENXIO); - command = pci_read_config(dev, PCIR_COMMAND, 1); if (id->flags & AMR_ID_QUARTZ) { - /* - * Make sure we are going to be able to talk to this board. - */ - if ((command & PCIM_CMD_MEMEN) == 0) { - device_printf(dev, "memory window not available\n"); - return (ENXIO); - } sc->amr_type |= AMR_TYPE_QUARTZ; - } else { - /* - * Make sure we are going to be able to talk to this board. - */ - if ((command & PCIM_CMD_PORTEN) == 0) { - device_printf(dev, "I/O window not available\n"); - return (ENXIO); - } } if ((amr_force_sg32 == 0) && (id->flags & AMR_ID_DO_SG64) && @@ -231,11 +214,7 @@ amr_pci_attach(device_t dev) } /* force the busmaster enable bit on */ - if (!(command & PCIM_CMD_BUSMASTEREN)) { - device_printf(dev, "busmaster bit not set, enabling\n"); - command |= PCIM_CMD_BUSMASTEREN; - pci_write_config(dev, PCIR_COMMAND, command, 2); - } + pci_enable_busmaster(dev); /* * Allocate the PCI register window. diff --git a/sys/dev/an/if_an_pci.c b/sys/dev/an/if_an_pci.c index fe3467d83e0..1c3032c942e 100644 --- a/sys/dev/an/if_an_pci.c +++ b/sys/dev/an/if_an_pci.c @@ -141,7 +141,6 @@ static int an_attach_pci(dev) device_t dev; { - u_int32_t command; struct an_softc *sc; int flags, error = 0; @@ -153,19 +152,6 @@ an_attach_pci(dev) sc->mpi350 = 1; sc->port_rid = PCIR_BAR(0); } else { - /* - * Map control/status registers. - */ - command = pci_read_config(dev, PCIR_COMMAND, 4); - command |= PCIM_CMD_PORTEN; - pci_write_config(dev, PCIR_COMMAND, command, 4); - command = pci_read_config(dev, PCIR_COMMAND, 4); - - if (!(command & PCIM_CMD_PORTEN)) { - device_printf(dev, "failed to enable I/O ports!\n"); - error = ENXIO; - goto fail; - } sc->port_rid = AN_PCI_LOIO; } error = an_alloc_port(dev, sc->port_rid, 1); diff --git a/sys/dev/arcmsr/arcmsr.c b/sys/dev/arcmsr/arcmsr.c index 535004b120a..bafe233074d 100644 --- a/sys/dev/arcmsr/arcmsr.c +++ b/sys/dev/arcmsr/arcmsr.c @@ -4101,8 +4101,7 @@ static u_int32_t arcmsr_initialize(device_t dev) pci_command |= PCIM_CMD_BUSMASTEREN; pci_command |= PCIM_CMD_PERRESPEN; pci_command |= PCIM_CMD_MWRICEN; - /* Enable Busmaster/Mem */ - pci_command |= PCIM_CMD_MEMEN; + /* Enable Busmaster */ pci_write_config(dev, PCIR_COMMAND, pci_command, 2); switch(acb->adapter_type) { case ACB_ADAPTER_TYPE_A: { diff --git a/sys/dev/asr/asr.c b/sys/dev/asr/asr.c index 549ca1a11f9..8909d0c086e 100644 --- a/sys/dev/asr/asr.c +++ b/sys/dev/asr/asr.c @@ -2428,9 +2428,7 @@ asr_attach(device_t dev) return(ENXIO); } /* Enable if not formerly enabled */ - pci_write_config(dev, PCIR_COMMAND, - pci_read_config(dev, PCIR_COMMAND, sizeof(char)) | - PCIM_CMD_MEMEN | PCIM_CMD_BUSMASTEREN, sizeof(char)); + pci_enable_busmaster(dev); sc->ha_pciBusNum = pci_get_bus(dev); sc->ha_pciDeviceNum = (pci_get_slot(dev) << 3) | pci_get_function(dev); diff --git a/sys/dev/ata/ata-pci.c b/sys/dev/ata/ata-pci.c index 0f7e8af4435..00ad6c73c3e 100644 --- a/sys/dev/ata/ata-pci.c +++ b/sys/dev/ata/ata-pci.c @@ -98,11 +98,8 @@ ata_pci_attach(device_t dev) ctlr->dev = dev; /* if needed try to enable busmastering */ + pci_enable_busmaster(dev); cmd = pci_read_config(dev, PCIR_COMMAND, 2); - if (!(cmd & PCIM_CMD_BUSMASTEREN)) { - pci_write_config(dev, PCIR_COMMAND, cmd | PCIM_CMD_BUSMASTEREN, 2); - cmd = pci_read_config(dev, PCIR_COMMAND, 2); - } /* if busmastering mode "stuck" use it */ if ((cmd & PCIM_CMD_BUSMASTEREN) == PCIM_CMD_BUSMASTEREN) { diff --git a/sys/dev/ath/if_ath.c b/sys/dev/ath/if_ath.c index 3c475857974..b4df1d67fc2 100644 --- a/sys/dev/ath/if_ath.c +++ b/sys/dev/ath/if_ath.c @@ -4581,17 +4581,8 @@ ath_tx_freebuf(struct ath_softc *sc, struct ath_buf *bf, int status) /* Free the buffer, it's not needed any longer */ ath_freebuf(sc, bf); - if (ni != NULL) { - /* - * Do any callback and reclaim the node reference. - */ - if (m0->m_flags & M_TXCB) - ieee80211_process_callback(ni, m0, status); - ieee80211_free_node(ni); - } - - /* Finally, we don't need this mbuf any longer */ - m_freem(m0); + /* Pass the buffer back to net80211 - completing it */ + ieee80211_tx_complete(ni, m0, status); } static struct ath_buf * diff --git a/sys/dev/ath/if_ath_tx.c b/sys/dev/ath/if_ath_tx.c index b868bff0c0b..b6bab9b4fd9 100644 --- a/sys/dev/ath/if_ath_tx.c +++ b/sys/dev/ath/if_ath_tx.c @@ -3739,33 +3739,50 @@ ath_tx_tid_drain_print(struct ath_softc *sc, struct ath_node *an, tap = ath_tx_get_tx_tid(an, tid->tid); device_printf(sc->sc_dev, - "%s: %s: node %p: bf=%p: addbaw=%d, dobaw=%d, " + "%s: %s: %6D: bf=%p: addbaw=%d, dobaw=%d, " "seqno=%d, retry=%d\n", - __func__, pfx, ni, bf, + __func__, + pfx, + ni->ni_macaddr, + ":", + bf, bf->bf_state.bfs_addedbaw, bf->bf_state.bfs_dobaw, SEQNO(bf->bf_state.bfs_seqno), bf->bf_state.bfs_retries); device_printf(sc->sc_dev, - "%s: node %p: bf=%p: txq[%d] axq_depth=%d, axq_aggr_depth=%d\n", - __func__, ni, bf, + "%s: %s: %6D: bf=%p: txq[%d] axq_depth=%d, axq_aggr_depth=%d\n", + __func__, + pfx, + ni->ni_macaddr, + ":", + bf, txq->axq_qnum, txq->axq_depth, txq->axq_aggr_depth); device_printf(sc->sc_dev, - "%s: node %p: bf=%p: tid txq_depth=%d hwq_depth=%d, bar_wait=%d, isfiltered=%d\n", - __func__, ni, bf, + "%s: %s: %6D: bf=%p: tid txq_depth=%d hwq_depth=%d, bar_wait=%d, " + "isfiltered=%d\n", + __func__, + pfx, + ni->ni_macaddr, + ":", + bf, tid->axq_depth, tid->hwq_depth, tid->bar_wait, tid->isfiltered); device_printf(sc->sc_dev, - "%s: node %p: tid %d: " + "%s: %s: %6D: tid %d: " "sched=%d, paused=%d, " "incomp=%d, baw_head=%d, " "baw_tail=%d txa_start=%d, ni_txseqs=%d\n", - __func__, ni, tid->tid, + __func__, + pfx, + ni->ni_macaddr, + ":", + tid->tid, tid->sched, tid->paused, tid->incomp, tid->baw_head, tid->baw_tail, tap == NULL ? -1 : tap->txa_start, diff --git a/sys/dev/atkbdc/psm.c b/sys/dev/atkbdc/psm.c index 541624f5bd8..9a6ae72ae41 100644 --- a/sys/dev/atkbdc/psm.c +++ b/sys/dev/atkbdc/psm.c @@ -2601,14 +2601,14 @@ proc_synaptics(struct psm_softc *sc, packetbuf_t *pb, mousestatus_t *ms, static int guest_buttons; int w, x0, y0; - /* TouchPad PS/2 absolute mode message format + /* TouchPad PS/2 absolute mode message format with capFourButtons: * * Bits: 7 6 5 4 3 2 1 0 (LSB) * ------------------------------------------------ * ipacket[0]: 1 0 W3 W2 0 W1 R L * ipacket[1]: Yb Ya Y9 Y8 Xb Xa X9 X8 * ipacket[2]: Z7 Z6 Z5 Z4 Z3 Z2 Z1 Z0 - * ipacket[3]: 1 1 Yc Xc 0 W0 D U + * ipacket[3]: 1 1 Yc Xc 0 W0 D^R U^L * ipacket[4]: X7 X6 X5 X4 X3 X2 X1 X0 * ipacket[5]: Y7 Y6 Y5 Y4 Y3 Y2 Y1 Y0 * @@ -2622,6 +2622,21 @@ proc_synaptics(struct psm_softc *sc, packetbuf_t *pb, mousestatus_t *ms, * Y: y position * Z: pressure * + * Without capFourButtons but with nExtendeButtons and/or capMiddle + * + * Bits: 7 6 5 4 3 2 1 0 (LSB) + * ------------------------------------------------------ + * ipacket[3]: 1 1 Yc Xc 0 W0 E^R M^L + * ipacket[4]: X7 X6 X5 X4 X3|b7 X2|b5 X1|b3 X0|b1 + * ipacket[5]: Y7 Y6 Y5 Y4 Y3|b8 Y2|b6 Y1|b4 Y0|b2 + * + * Legend: + * M: Middle physical mouse button + * E: Extended mouse buttons reported instead of low bits of X and Y + * b1-b8: Extended mouse buttons + * Only ((nExtendedButtons + 1) >> 1) bits are used in packet + * 4 and 5, for reading X and Y value they should be zeroed. + * * Absolute reportable limits: 0 - 6143. * Typical bezel limits: 1472 - 5472. * Typical edge marings: 1632 - 5312. @@ -2675,8 +2690,10 @@ proc_synaptics(struct psm_softc *sc, packetbuf_t *pb, mousestatus_t *ms, w = 4; } - /* Handle packets from the guest device */ - /* XXX Documentation? */ + /* + * Handle packets from the guest device. See: + * Synaptics PS/2 TouchPad Interfacing Guide, Section 5.1 + */ if (w == 3 && sc->synhw.capPassthrough) { *x = ((pb->ipacket[1] & 0x10) ? pb->ipacket[4] - 256 : pb->ipacket[4]); @@ -2704,36 +2721,49 @@ proc_synaptics(struct psm_softc *sc, packetbuf_t *pb, mousestatus_t *ms, touchpad_buttons |= MOUSE_BUTTON3DOWN; if (sc->synhw.capExtended && sc->synhw.capFourButtons) { - if ((pb->ipacket[3] & 0x01) && (pb->ipacket[0] & 0x01) == 0) + if ((pb->ipacket[3] ^ pb->ipacket[0]) & 0x01) touchpad_buttons |= MOUSE_BUTTON4DOWN; - if ((pb->ipacket[3] & 0x02) && (pb->ipacket[0] & 0x02) == 0) + if ((pb->ipacket[3] ^ pb->ipacket[0]) & 0x02) touchpad_buttons |= MOUSE_BUTTON5DOWN; - } - - /* - * In newer pads - bit 0x02 in the third byte of - * the packet indicates that we have an extended - * button press. - */ - /* XXX Documentation? */ - if (pb->ipacket[3] & 0x02) { - /* - * if directional_scrolls is not 1, we treat any of - * the scrolling directions as middle-click. - */ - if (sc->syninfo.directional_scrolls) { - if (pb->ipacket[4] & 0x01) - touchpad_buttons |= MOUSE_BUTTON4DOWN; - if (pb->ipacket[5] & 0x01) - touchpad_buttons |= MOUSE_BUTTON5DOWN; - if (pb->ipacket[4] & 0x02) - touchpad_buttons |= MOUSE_BUTTON6DOWN; - if (pb->ipacket[5] & 0x02) - touchpad_buttons |= MOUSE_BUTTON7DOWN; - } else { - if ((pb->ipacket[4] & 0x0F) || - (pb->ipacket[5] & 0x0F)) + } else if (sc->synhw.capExtended && sc->synhw.capMiddle) { + /* Middle Button */ + if ((pb->ipacket[0] ^ pb->ipacket[3]) & 0x01) + touchpad_buttons |= MOUSE_BUTTON2DOWN; + } else if (sc->synhw.capExtended && (sc->synhw.nExtendedButtons > 0)) { + /* Extended Buttons */ + if ((pb->ipacket[0] ^ pb->ipacket[3]) & 0x02) { + if (sc->syninfo.directional_scrolls) { + if (pb->ipacket[4] & 0x01) + touchpad_buttons |= MOUSE_BUTTON4DOWN; + if (pb->ipacket[5] & 0x01) + touchpad_buttons |= MOUSE_BUTTON5DOWN; + if (pb->ipacket[4] & 0x02) + touchpad_buttons |= MOUSE_BUTTON6DOWN; + if (pb->ipacket[5] & 0x02) + touchpad_buttons |= MOUSE_BUTTON7DOWN; + } else { touchpad_buttons |= MOUSE_BUTTON2DOWN; + } + + /* + * Zero out bits used by extended buttons to avoid + * misinterpretation of the data absolute position. + * + * The bits represented by + * + * (nExtendedButtons + 1) >> 1 + * + * will be masked out in both bytes. + * The mask for n bits is computed with the formula + * + * (1 << n) - 1 + */ + int maskedbits = 0; + int mask = 0; + maskedbits = (sc->synhw.nExtendedButtons + 1) >> 1; + mask = (1 << maskedbits) - 1; + pb->ipacket[4] &= ~(mask); + pb->ipacket[5] &= ~(mask); } } @@ -4440,15 +4470,20 @@ enable_synaptics(KBDC kbdc, struct psm_softc *sc) buttons = 0; synhw.capExtended = (status[0] & 0x80) != 0; if (synhw.capExtended) { - synhw.capPassthrough = (status[2] & 0x80) != 0; - synhw.capSleep = (status[2] & 0x10) != 0; - synhw.capFourButtons = (status[2] & 0x08) != 0; - synhw.capMultiFinger = (status[2] & 0x02) != 0; - synhw.capPalmDetect = (status[2] & 0x01) != 0; + synhw.nExtendedQueries = (status[0] & 0x70) != 0; + synhw.capMiddle = (status[0] & 0x04) != 0; + synhw.capPassthrough = (status[2] & 0x80) != 0; + synhw.capSleep = (status[2] & 0x10) != 0; + synhw.capFourButtons = (status[2] & 0x08) != 0; + synhw.capMultiFinger = (status[2] & 0x02) != 0; + synhw.capPalmDetect = (status[2] & 0x01) != 0; if (verbose >= 2) { printf(" Extended capabilities:\n"); printf(" capExtended: %d\n", synhw.capExtended); + printf(" capMiddle: %d\n", synhw.capMiddle); + printf(" nExtendedQueries: %d\n", + synhw.nExtendedQueries); printf(" capPassthrough: %d\n", synhw.capPassthrough); printf(" capSleep: %d\n", synhw.capSleep); printf(" capFourButtons: %d\n", synhw.capFourButtons); @@ -4457,16 +4492,27 @@ enable_synaptics(KBDC kbdc, struct psm_softc *sc) } /* - * If we have bits set in status[0] & 0x70, then we can load + * If nExtendedQueries is 1 or greater, then the TouchPad + * supports this number of extended queries. We can load * more information about buttons using query 0x09. */ - if ((status[0] & 0x70) != 0) { + if (synhw.capExtended && synhw.nExtendedQueries) { if (mouse_ext_command(kbdc, 0x09) == 0) return (FALSE); if (get_mouse_status(kbdc, status, 0, 3) != 3) return (FALSE); - buttons = (status[1] & 0xf0) >> 4; + synhw.nExtendedButtons = (status[1] & 0xf0) >> 4; + /* + * Add the number of extended buttons to the total + * button support count, including the middle button + * if capMiddle support bit is set. + */ + buttons = synhw.nExtendedButtons + synhw.capMiddle; } else + /* + * If the capFourButtons support bit is set, + * add a fourth button to the total button count. + */ buttons = synhw.capFourButtons ? 1 : 0; } if (verbose >= 2) { @@ -4476,6 +4522,12 @@ enable_synaptics(KBDC kbdc, struct psm_softc *sc) printf(" No extended capabilities\n"); } + /* + * Add the default number of 3 buttons to the total + * count of supported buttons reported above. + */ + buttons += 3; + /* * Read the mode byte. * @@ -4503,7 +4555,6 @@ enable_synaptics(KBDC kbdc, struct psm_softc *sc) /* "Commit" the Set Mode Byte command sent above. */ set_mouse_sampling_rate(kbdc, 20); - buttons += 3; VLOG(3, (LOG_DEBUG, "synaptics: END init (%d buttons)\n", buttons)); if (sc != NULL) { diff --git a/sys/dev/bce/if_bce.c b/sys/dev/bce/if_bce.c index 644348cdb8a..3b27b8d446d 100644 --- a/sys/dev/bce/if_bce.c +++ b/sys/dev/bce/if_bce.c @@ -9821,9 +9821,7 @@ bce_dump_mbuf(struct bce_softc *sc, struct mbuf *m) if (mp->m_flags & M_PKTHDR) { BCE_PRINTF("- m_pkthdr: len = %d, flags = 0x%b, " "csum_flags = %b\n", mp->m_pkthdr.len, - mp->m_flags, "\20\12M_BCAST\13M_MCAST\14M_FRAG" - "\15M_FIRSTFRAG\16M_LASTFRAG\21M_VLANTAG" - "\22M_PROMISC\23M_NOFREE", + mp->m_flags, M_FLAG_PRINTF, mp->m_pkthdr.csum_flags, "\20\1CSUM_IP\2CSUM_TCP\3CSUM_UDP" "\5CSUM_FRAGMENT\6CSUM_TSO\11CSUM_IP_CHECKED" diff --git a/sys/dev/bge/if_bge.c b/sys/dev/bge/if_bge.c index 0cfbca73caf..6b2ceb42330 100644 --- a/sys/dev/bge/if_bge.c +++ b/sys/dev/bge/if_bge.c @@ -5542,6 +5542,8 @@ bge_init_locked(struct bge_softc *sc) mode = CSR_READ_4(sc, BGE_RX_MODE); if (BGE_IS_5755_PLUS(sc)) mode |= BGE_RXMODE_IPV6_ENABLE; + if (sc->bge_asicrev == BGE_ASICREV_BCM5762) + mode |= BGE_RXMODE_IPV4_FRAG_FIX; CSR_WRITE_4(sc,BGE_RX_MODE, mode | BGE_RXMODE_ENABLE); DELAY(10); diff --git a/sys/dev/bge/if_bgereg.h b/sys/dev/bge/if_bgereg.h index d6197c0d713..a1e6df16dfc 100644 --- a/sys/dev/bge/if_bgereg.h +++ b/sys/dev/bge/if_bgereg.h @@ -841,6 +841,7 @@ #define BGE_RXMODE_RX_NO_CRC_CHECK 0x00000200 #define BGE_RXMODE_RX_KEEP_VLAN_DIAG 0x00000400 #define BGE_RXMODE_IPV6_ENABLE 0x01000000 +#define BGE_RXMODE_IPV4_FRAG_FIX 0x02000000 /* Receive MAC status register */ #define BGE_RXSTAT_REMOTE_XOFFED 0x00000001 diff --git a/sys/dev/bktr/bktr_core.c b/sys/dev/bktr/bktr_core.c index bc5bdcf7878..bb2ae332c2e 100644 --- a/sys/dev/bktr/bktr_core.c +++ b/sys/dev/bktr/bktr_core.c @@ -109,6 +109,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include #include @@ -1801,8 +1802,10 @@ video_ioctl( bktr_ptr_t bktr, int unit, ioctl_cmd_t cmd, caddr_t arg, struct thr #else buf = get_bktr_mem(unit, temp*PAGE_SIZE); if (buf != 0) { - kmem_free(kernel_map, bktr->bigbuf, - (bktr->alloc_pages * PAGE_SIZE)); + contigfree( + (void *)(uintptr_t)bktr->bigbuf, + (bktr->alloc_pages * PAGE_SIZE), + M_DEVBUF); #endif bktr->bigbuf = buf; diff --git a/sys/dev/bktr/bktr_os.c b/sys/dev/bktr/bktr_os.c index 773ffb061ee..e5ea53bcf6b 100644 --- a/sys/dev/bktr/bktr_os.c +++ b/sys/dev/bktr/bktr_os.c @@ -318,7 +318,6 @@ bktr_attach( device_t dev ) { u_long latency; u_long fun; - u_long val; unsigned int rev; unsigned int unit; int error = 0; @@ -336,9 +335,7 @@ bktr_attach( device_t dev ) /* * Enable bus mastering and Memory Mapped device */ - val = pci_read_config(dev, PCIR_COMMAND, 4); - val |= (PCIM_CMD_MEMEN|PCIM_CMD_BUSMASTEREN); - pci_write_config(dev, PCIR_COMMAND, val, 4); + pci_enable_busmaster(dev); /* * Map control/status registers. diff --git a/sys/dev/buslogic/bt_pci.c b/sys/dev/buslogic/bt_pci.c index c3a36813f57..7615ae8039a 100644 --- a/sys/dev/buslogic/bt_pci.c +++ b/sys/dev/buslogic/bt_pci.c @@ -57,24 +57,19 @@ __FBSDID("$FreeBSD$"); static int bt_pci_alloc_resources(device_t dev) { - int command, type = 0, rid, zero; + int type = 0, rid, zero; struct resource *regs = 0; struct resource *irq = 0; - command = pci_read_config(dev, PCIR_COMMAND, /*bytes*/1); #if 0 /* XXX Memory Mapped I/O seems to cause problems */ - if (command & PCIM_CMD_MEMEN) { - type = SYS_RES_MEMORY; - rid = BT_PCI_MEMADDR; - regs = bus_alloc_resource_any(dev, type, &rid, RF_ACTIVE); - } + type = SYS_RES_MEMORY; + rid = BT_PCI_MEMADDR; + regs = bus_alloc_resource_any(dev, type, &rid, RF_ACTIVE); #else - if (!regs && (command & PCIM_CMD_PORTEN)) { - type = SYS_RES_IOPORT; - rid = BT_PCI_IOADDR; - regs = bus_alloc_resource_any(dev, type, &rid, RF_ACTIVE); - } + type = SYS_RES_IOPORT; + rid = BT_PCI_IOADDR; + regs = bus_alloc_resource_any(dev, type, &rid, RF_ACTIVE); #endif if (!regs) return (ENOMEM); diff --git a/sys/dev/bwi/bwimac.c b/sys/dev/bwi/bwimac.c index 703da7a71cb..30b19fcc2e6 100644 --- a/sys/dev/bwi/bwimac.c +++ b/sys/dev/bwi/bwimac.c @@ -1427,7 +1427,8 @@ bwi_mac_set_ackrates(struct bwi_mac *mac, const struct ieee80211_rate_table *rt, enum ieee80211_phytype modtype; uint16_t ofs; - modtype = ieee80211_rate2phytype(rt, rs->rs_rates[i]); + modtype = ieee80211_rate2phytype(rt, + rs->rs_rates[i] & IEEE80211_RATE_VAL); switch (modtype) { case IEEE80211_T_DS: ofs = 0x4c0; @@ -1438,7 +1439,9 @@ bwi_mac_set_ackrates(struct bwi_mac *mac, const struct ieee80211_rate_table *rt, default: panic("unsupported modtype %u\n", modtype); } - ofs += 2*(ieee80211_rate2plcp(rs->rs_rates[i], modtype) & 0xf); + ofs += 2*(ieee80211_rate2plcp( + rs->rs_rates[i] & IEEE80211_RATE_VAL, + modtype) & 0xf); MOBJ_WRITE_2(mac, BWI_COMM_MOBJ, ofs + 0x20, MOBJ_READ_2(mac, BWI_COMM_MOBJ, ofs)); diff --git a/sys/dev/bxe/if_bxe.c b/sys/dev/bxe/if_bxe.c index 9566b27c1e7..647f9647e6b 100644 --- a/sys/dev/bxe/if_bxe.c +++ b/sys/dev/bxe/if_bxe.c @@ -16265,9 +16265,7 @@ void bxe_dump_mbuf(struct bxe_softc *sc, struct mbuf *m) if (m->m_flags & M_PKTHDR) { BXE_PRINTF("- m_pkthdr: len = %d, flags = 0x%b, " "csum_flags = %b\n", m->m_pkthdr.len, - m->m_flags, "\20\12M_BCAST\13M_MCAST\14M_FRAG" - "\15M_FIRSTFRAG\16M_LASTFRAG\21M_VLANTAG" - "\22M_PROMISC\23M_NOFREE", + m->m_flags, M_FLAG_PRINTF, m->m_pkthdr.csum_flags, "\20\1CSUM_IP\2CSUM_TCP\3CSUM_UDP" "\5CSUM_FRAGMENT\6CSUM_TSO\11CSUM_IP_CHECKED" diff --git a/sys/dev/cas/if_cas.c b/sys/dev/cas/if_cas.c index eec8fa44c34..4f38a254b2c 100644 --- a/sys/dev/cas/if_cas.c +++ b/sys/dev/cas/if_cas.c @@ -132,7 +132,7 @@ static void cas_detach(struct cas_softc *sc); static int cas_disable_rx(struct cas_softc *sc); static int cas_disable_tx(struct cas_softc *sc); static void cas_eint(struct cas_softc *sc, u_int status); -static void cas_free(void *arg1, void* arg2); +static int cas_free(struct mbuf *m, void *arg1, void* arg2); static void cas_init(void *xsc); static void cas_init_locked(struct cas_softc *sc); static void cas_init_regs(struct cas_softc *sc); @@ -1887,8 +1887,8 @@ cas_rint(struct cas_softc *sc) #endif } -static void -cas_free(void *arg1, void *arg2) +static int +cas_free(struct mbuf *m, void *arg1, void *arg2) { struct cas_rxdsoft *rxds; struct cas_softc *sc; @@ -1904,7 +1904,7 @@ cas_free(void *arg1, void *arg2) rxds = &sc->sc_rxdsoft[idx]; #endif if (refcount_release(&rxds->rxds_refcount) == 0) - return; + return (EXT_FREE_OK); /* * NB: this function can be called via m_freem(9) within @@ -1915,6 +1915,7 @@ cas_free(void *arg1, void *arg2) cas_add_rxdesc(sc, idx); if (locked == 0) CAS_UNLOCK(sc); + return (EXT_FREE_OK); } static inline void diff --git a/sys/dev/cfi/cfi_bus_nexus.c b/sys/dev/cfi/cfi_bus_nexus.c index 1b317e61d81..4e1fa4ebfef 100644 --- a/sys/dev/cfi/cfi_bus_nexus.c +++ b/sys/dev/cfi/cfi_bus_nexus.c @@ -4,6 +4,11 @@ * Copyright (c) 2009 Sam Leffler, Errno Consulting * All rights reserved. * + * Portions of this software were developed by SRI International and the + * University of Cambridge Computer Laboratory under DARPA/AFRL contract + * (FA8750-10-C-0237) ("CTSRD"), as part of the DARPA CRASH research + * programme. + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: diff --git a/sys/dev/cfi/cfi_core.c b/sys/dev/cfi/cfi_core.c index 083f5fca370..f318ebc55ac 100644 --- a/sys/dev/cfi/cfi_core.c +++ b/sys/dev/cfi/cfi_core.c @@ -1,7 +1,13 @@ /*- * Copyright (c) 2007, Juniper Networks, Inc. + * Copyright (c) 2012-2013, SRI International * All rights reserved. * + * Portions of this software were developed by SRI International and the + * University of Cambridge Computer Laboratory under DARPA/AFRL contract + * (FA8750-10-C-0237) ("CTSRD"), as part of the DARPA CRASH research + * programme. + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: @@ -49,6 +55,8 @@ __FBSDID("$FreeBSD$"); #include #include +static void cfi_add_sysctls(struct cfi_softc *); + extern struct cdevsw cfi_cdevsw; char cfi_driver_name[] = "cfi"; @@ -262,6 +270,7 @@ cfi_attach(device_t dev) struct cfi_softc *sc; u_int blksz, blocks; u_int r, u; + uint64_t mtoexp, ttoexp; #ifdef CFI_SUPPORT_STRATAFLASH uint64_t ppr; char name[KENV_MNAMELEN], value[32]; @@ -279,11 +288,79 @@ cfi_attach(device_t dev) sc->sc_tag = rman_get_bustag(sc->sc_res); sc->sc_handle = rman_get_bushandle(sc->sc_res); - /* Get time-out values for erase and write. */ - sc->sc_write_timeout = 1 << cfi_read_qry(sc, CFI_QRY_TTO_WRITE); - sc->sc_erase_timeout = 1 << cfi_read_qry(sc, CFI_QRY_TTO_ERASE); - sc->sc_write_timeout *= 1 << cfi_read_qry(sc, CFI_QRY_MTO_WRITE); - sc->sc_erase_timeout *= 1 << cfi_read_qry(sc, CFI_QRY_MTO_ERASE); + /* Get time-out values for erase, write, and buffer write. */ + ttoexp = cfi_read_qry(sc, CFI_QRY_TTO_ERASE); + mtoexp = cfi_read_qry(sc, CFI_QRY_MTO_ERASE); + if (ttoexp == 0) { + device_printf(dev, "erase timeout == 0, using 2^16ms\n"); + ttoexp = 16; + } + if (ttoexp > 41) { + device_printf(dev, "insane timeout: 2^%jdms\n", ttoexp); + return (EINVAL); + } + if (mtoexp == 0) { + device_printf(dev, "max erase timeout == 0, using 2^%jdms\n", + ttoexp + 4); + mtoexp = 4; + } + if (ttoexp + mtoexp > 41) { + device_printf(dev, "insane max erase timeout: 2^%jd\n", + ttoexp + mtoexp); + return (EINVAL); + } + sc->sc_typical_timeouts[CFI_TIMEOUT_ERASE] = SBT_1MS * (1ULL << ttoexp); + sc->sc_max_timeouts[CFI_TIMEOUT_ERASE] = + sc->sc_typical_timeouts[CFI_TIMEOUT_ERASE] * (1ULL << mtoexp); + + ttoexp = cfi_read_qry(sc, CFI_QRY_TTO_WRITE); + mtoexp = cfi_read_qry(sc, CFI_QRY_MTO_WRITE); + if (ttoexp == 0) { + device_printf(dev, "write timeout == 0, using 2^18ns\n"); + ttoexp = 18; + } + if (ttoexp > 51) { + device_printf(dev, "insane write timeout: 2^%jdus\n", ttoexp); + return (EINVAL); + } + if (mtoexp == 0) { + device_printf(dev, "max write timeout == 0, using 2^%jdms\n", + ttoexp + 4); + mtoexp = 4; + } + if (ttoexp + mtoexp > 51) { + device_printf(dev, "insane max write timeout: 2^%jdus\n", + ttoexp + mtoexp); + return (EINVAL); + } + sc->sc_typical_timeouts[CFI_TIMEOUT_WRITE] = SBT_1US * (1ULL << ttoexp); + sc->sc_max_timeouts[CFI_TIMEOUT_WRITE] = + sc->sc_typical_timeouts[CFI_TIMEOUT_WRITE] * (1ULL << mtoexp); + + ttoexp = cfi_read_qry(sc, CFI_QRY_TTO_BUFWRITE); + mtoexp = cfi_read_qry(sc, CFI_QRY_MTO_BUFWRITE); + /* Don't check for 0, it means not-supported. */ + if (ttoexp > 51) { + device_printf(dev, "insane write timeout: 2^%jdus\n", ttoexp); + return (EINVAL); + } + if (ttoexp + mtoexp > 51) { + device_printf(dev, "insane max write timeout: 2^%jdus\n", + ttoexp + mtoexp); + return (EINVAL); + } + sc->sc_typical_timeouts[CFI_TIMEOUT_BUFWRITE] = + SBT_1US * (1ULL << cfi_read_qry(sc, CFI_QRY_TTO_BUFWRITE)); + sc->sc_max_timeouts[CFI_TIMEOUT_BUFWRITE] = + sc->sc_typical_timeouts[CFI_TIMEOUT_BUFWRITE] * + (1ULL << cfi_read_qry(sc, CFI_QRY_MTO_BUFWRITE)); + + /* Get the maximum size of a multibyte program */ + if (sc->sc_typical_timeouts[CFI_TIMEOUT_BUFWRITE] != 0) + sc->sc_maxbuf = 1 << (cfi_read_qry(sc, CFI_QRY_MAXBUF) | + cfi_read_qry(sc, CFI_QRY_MAXBUF) << 8); + else + sc->sc_maxbuf = 0; /* Get erase regions. */ sc->sc_regions = cfi_read_qry(sc, CFI_QRY_NREGIONS); @@ -317,6 +394,8 @@ cfi_attach(device_t dev) "%s%u", cfi_driver_name, u); sc->sc_nod->si_drv1 = sc; + cfi_add_sysctls(sc); + #ifdef CFI_SUPPORT_STRATAFLASH /* * Store the Intel factory PPR in the environment. In some @@ -337,6 +416,45 @@ cfi_attach(device_t dev) return (0); } +static void +cfi_add_sysctls(struct cfi_softc *sc) +{ + struct sysctl_ctx_list *ctx; + struct sysctl_oid_list *children; + + ctx = device_get_sysctl_ctx(sc->sc_dev); + children = SYSCTL_CHILDREN(device_get_sysctl_tree(sc->sc_dev)); + + SYSCTL_ADD_UINT(ctx, children, OID_AUTO, + "typical_erase_timout_count", + CTLFLAG_RD, &sc->sc_tto_counts[CFI_TIMEOUT_ERASE], + 0, "Number of times the typical erase timeout was exceeded"); + SYSCTL_ADD_UINT(ctx, children, OID_AUTO, + "max_erase_timout_count", + CTLFLAG_RD, &sc->sc_mto_counts[CFI_TIMEOUT_ERASE], 0, + "Number of times the maximum erase timeout was exceeded"); + SYSCTL_ADD_UINT(ctx, children, OID_AUTO, + "typical_write_timout_count", + CTLFLAG_RD, &sc->sc_tto_counts[CFI_TIMEOUT_WRITE], 0, + "Number of times the typical write timeout was exceeded"); + SYSCTL_ADD_UINT(ctx, children, OID_AUTO, + "max_write_timout_count", + CTLFLAG_RD, &sc->sc_mto_counts[CFI_TIMEOUT_WRITE], 0, + "Number of times the maximum write timeout was exceeded"); + if (sc->sc_maxbuf > 0) { + SYSCTL_ADD_UINT(ctx, children, OID_AUTO, + "typical_bufwrite_timout_count", + CTLFLAG_RD, &sc->sc_tto_counts[CFI_TIMEOUT_BUFWRITE], 0, + "Number of times the typical buffered write timeout was " + "exceeded"); + SYSCTL_ADD_UINT(ctx, children, OID_AUTO, + "max_bufwrite_timout_count", + CTLFLAG_RD, &sc->sc_mto_counts[CFI_TIMEOUT_BUFWRITE], 0, + "Number of times the maximum buffered write timeout was " + "exceeded"); + } +} + int cfi_detach(device_t dev) { @@ -351,17 +469,22 @@ cfi_detach(device_t dev) } static int -cfi_wait_ready(struct cfi_softc *sc, u_int ofs, u_int timeout) +cfi_wait_ready(struct cfi_softc *sc, u_int ofs, sbintime_t start, + enum cfi_wait_cmd cmd) { - int done, error; + int done, error, tto_exceeded; uint32_t st0 = 0, st = 0; + sbintime_t now; done = 0; error = 0; - timeout *= 10; - while (!done && !error && timeout) { - DELAY(100); - timeout--; + tto_exceeded = 0; + while (!done && !error) { + /* + * Save time before we start so we always do one check + * after the timeout has expired. + */ + now = sbinuptime(); switch (sc->sc_cmdset) { case CFI_VEND_INTEL_ECS: @@ -390,6 +513,25 @@ cfi_wait_ready(struct cfi_softc *sc, u_int ofs, u_int timeout) done = ((st & 0x40) == (st0 & 0x40)) ? 1 : 0; break; } + + if (tto_exceeded || + now > start + sc->sc_typical_timeouts[cmd]) { + if (!tto_exceeded) { + tto_exceeded = 1; + sc->sc_tto_counts[cmd]++; +#ifdef CFI_DEBUG_TIMEOUT + device_printf(sc->sc_dev, + "typical timeout exceeded (cmd %d)", cmd); +#endif + } + if (now > start + sc->sc_max_timeouts[cmd]) { + sc->sc_mto_counts[cmd]++; +#ifdef CFI_DEBUG_TIMEOUT + device_printf(sc->sc_dev, + "max timeout exceeded (cmd %d)", cmd); +#endif + } + } } if (!done && !error) error = ETIMEDOUT; @@ -405,9 +547,12 @@ cfi_write_block(struct cfi_softc *sc) uint8_t *x8; uint16_t *x16; uint32_t *x32; - } ptr; + } ptr, cpyprt; register_t intr; - int error, i; + int error, i, neederase = 0; + uint32_t st; + u_int wlen; + sbintime_t start; /* Intel flash must be unlocked before modification */ switch (sc->sc_cmdset) { @@ -419,31 +564,124 @@ cfi_write_block(struct cfi_softc *sc) break; } - /* Erase the block. */ - switch (sc->sc_cmdset) { - case CFI_VEND_INTEL_ECS: - case CFI_VEND_INTEL_SCS: - cfi_write(sc, sc->sc_wrofs, CFI_BCS_BLOCK_ERASE); - cfi_write(sc, sc->sc_wrofs, CFI_BCS_CONFIRM); - break; - case CFI_VEND_AMD_SCS: - case CFI_VEND_AMD_ECS: - cfi_amd_write(sc, sc->sc_wrofs, AMD_ADDR_START, - CFI_AMD_ERASE_SECTOR); - cfi_amd_write(sc, sc->sc_wrofs, 0, CFI_AMD_BLOCK_ERASE); - break; - default: - /* Better safe than sorry... */ - return (ENODEV); - } - error = cfi_wait_ready(sc, sc->sc_wrofs, sc->sc_erase_timeout); - if (error) - goto out; + /* Check if an erase is required. */ + for (i = 0; i < sc->sc_wrbufsz; i++) + if ((sc->sc_wrbuf[i] & sc->sc_wrbufcpy[i]) != sc->sc_wrbuf[i]) { + neederase = 1; + break; + } - /* Write the block. */ + if (neederase) { + intr = intr_disable(); + start = sbinuptime(); + /* Erase the block. */ + switch (sc->sc_cmdset) { + case CFI_VEND_INTEL_ECS: + case CFI_VEND_INTEL_SCS: + cfi_write(sc, sc->sc_wrofs, CFI_BCS_BLOCK_ERASE); + cfi_write(sc, sc->sc_wrofs, CFI_BCS_CONFIRM); + break; + case CFI_VEND_AMD_SCS: + case CFI_VEND_AMD_ECS: + cfi_amd_write(sc, sc->sc_wrofs, AMD_ADDR_START, + CFI_AMD_ERASE_SECTOR); + cfi_amd_write(sc, sc->sc_wrofs, 0, CFI_AMD_BLOCK_ERASE); + break; + default: + /* Better safe than sorry... */ + intr_restore(intr); + return (ENODEV); + } + intr_restore(intr); + error = cfi_wait_ready(sc, sc->sc_wrofs, start, + CFI_TIMEOUT_ERASE); + if (error) + goto out; + } else + error = 0; + + /* Write the block using a multibyte write if supported. */ ptr.x8 = sc->sc_wrbuf; + cpyprt.x8 = sc->sc_wrbufcpy; + if (sc->sc_maxbuf > sc->sc_width) { + switch (sc->sc_cmdset) { + case CFI_VEND_INTEL_ECS: + case CFI_VEND_INTEL_SCS: + for (i = 0; i < sc->sc_wrbufsz; i += wlen) { + wlen = MIN(sc->sc_maxbuf, sc->sc_wrbufsz - i); + + intr = intr_disable(); + + start = sbinuptime(); + do { + cfi_write(sc, sc->sc_wrofs + i, + CFI_BCS_BUF_PROG_SETUP); + if (sbinuptime() > start + sc->sc_max_timeouts[CFI_TIMEOUT_BUFWRITE]) { + error = ETIMEDOUT; + goto out; + } + st = cfi_read(sc, sc->sc_wrofs + i); + } while (! (st & CFI_INTEL_STATUS_WSMS)); + + cfi_write(sc, sc->sc_wrofs + i, + (wlen / sc->sc_width) - 1); + switch (sc->sc_width) { + case 1: + bus_space_write_region_1(sc->sc_tag, + sc->sc_handle, sc->sc_wrofs + i, + ptr.x8 + i, wlen); + break; + case 2: + bus_space_write_region_2(sc->sc_tag, + sc->sc_handle, sc->sc_wrofs + i, + ptr.x16 + i / 2, wlen / 2); + break; + case 4: + bus_space_write_region_4(sc->sc_tag, + sc->sc_handle, sc->sc_wrofs + i, + ptr.x32 + i / 4, wlen / 4); + break; + } + + cfi_write(sc, sc->sc_wrofs + i, + CFI_BCS_CONFIRM); + + intr_restore(intr); + + error = cfi_wait_ready(sc, sc->sc_wrofs + i, + start, CFI_TIMEOUT_BUFWRITE); + if (error != 0) + goto out; + } + goto out; + default: + /* Fall through to single word case */ + break; + } + + } + + /* Write the block one byte/word at a time. */ for (i = 0; i < sc->sc_wrbufsz; i += sc->sc_width) { + /* Avoid writing unless we are actually changing bits */ + if (!neederase) { + switch (sc->sc_width) { + case 1: + if(*(ptr.x8 + i) == *(cpyprt.x8 + i)) + continue; + break; + case 2: + if(*(ptr.x16 + i / 2) == *(cpyprt.x16 + i / 2)) + continue; + break; + case 4: + if(*(ptr.x32 + i / 4) == *(cpyprt.x32 + i / 4)) + continue; + break; + } + } + /* * Make sure the command to start a write and the * actual write happens back-to-back without any @@ -451,6 +689,7 @@ cfi_write_block(struct cfi_softc *sc) */ intr = intr_disable(); + start = sbinuptime(); switch (sc->sc_cmdset) { case CFI_VEND_INTEL_ECS: case CFI_VEND_INTEL_SCS: @@ -464,21 +703,22 @@ cfi_write_block(struct cfi_softc *sc) switch (sc->sc_width) { case 1: bus_space_write_1(sc->sc_tag, sc->sc_handle, - sc->sc_wrofs + i, *(ptr.x8)++); + sc->sc_wrofs + i, *(ptr.x8 + i)); break; case 2: bus_space_write_2(sc->sc_tag, sc->sc_handle, - sc->sc_wrofs + i, *(ptr.x16)++); + sc->sc_wrofs + i, *(ptr.x16 + i / 2)); break; case 4: bus_space_write_4(sc->sc_tag, sc->sc_handle, - sc->sc_wrofs + i, *(ptr.x32)++); + sc->sc_wrofs + i, *(ptr.x32 + i / 4)); break; } - + intr_restore(intr); - error = cfi_wait_ready(sc, sc->sc_wrofs, sc->sc_write_timeout); + error = cfi_wait_ready(sc, sc->sc_wrofs, start, + CFI_TIMEOUT_WRITE); if (error) goto out; } @@ -576,6 +816,7 @@ cfi_intel_set_oem_pr(struct cfi_softc *sc, uint64_t id) #ifdef CFI_ARMEDANDDANGEROUS register_t intr; int i, error; + sbintime_t start; #endif if (sc->sc_cmdset != CFI_VEND_INTEL_ECS) @@ -585,11 +826,12 @@ cfi_intel_set_oem_pr(struct cfi_softc *sc, uint64_t id) #ifdef CFI_ARMEDANDDANGEROUS for (i = 7; i >= 4; i--, id >>= 16) { intr = intr_disable(); + start = sbinuptime(); cfi_write(sc, 0, CFI_INTEL_PP_SETUP); cfi_put16(sc, CFI_INTEL_PR(i), id&0xffff); intr_restore(intr); - error = cfi_wait_ready(sc, CFI_BCS_READ_STATUS, - sc->sc_write_timeout); + error = cfi_wait_ready(sc, CFI_BCS_READ_STATUS, start, + CFI_TIMEOUT_WRITE); if (error) break; } @@ -629,6 +871,7 @@ cfi_intel_set_plr(struct cfi_softc *sc) #ifdef CFI_ARMEDANDDANGEROUS register_t intr; int error; + sbintime_t start; #endif if (sc->sc_cmdset != CFI_VEND_INTEL_ECS) return EOPNOTSUPP; @@ -638,10 +881,12 @@ cfi_intel_set_plr(struct cfi_softc *sc) /* worthy of console msg */ device_printf(sc->sc_dev, "set PLR\n"); intr = intr_disable(); + binuptime(&start); cfi_write(sc, 0, CFI_INTEL_PP_SETUP); cfi_put16(sc, CFI_INTEL_PLR, 0xFFFD); intr_restore(intr); - error = cfi_wait_ready(sc, CFI_BCS_READ_STATUS, sc->sc_write_timeout); + error = cfi_wait_ready(sc, CFI_BCS_READ_STATUS, start, + CFI_TIMEOUT_WRITE); cfi_write(sc, 0, CFI_BCS_READ_ARRAY); return error; #else diff --git a/sys/dev/cfi/cfi_dev.c b/sys/dev/cfi/cfi_dev.c index d511eacd661..7d1f92bed7d 100644 --- a/sys/dev/cfi/cfi_dev.c +++ b/sys/dev/cfi/cfi_dev.c @@ -1,7 +1,13 @@ /*- * Copyright (c) 2007, Juniper Networks, Inc. + * Copyright (c) 2012-2013, SRI International * All rights reserved. * + * Portions of this software were developed by SRI International and the + * University of Cambridge Computer Laboratory under DARPA/AFRL contract + * (FA8750-10-C-0237) ("CTSRD"), as part of the DARPA CRASH research + * programme. + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: @@ -72,7 +78,8 @@ struct cdevsw cfi_cdevsw = { * Begin writing into a new block/sector. We read the sector into * memory and keep updating that, until we move into another sector * or the process stops writing. At that time we write the whole - * sector to flash (see cfi_block_finish). + * sector to flash (see cfi_block_finish). To avoid unneeded erase + * cycles, keep a pristine copy of the sector on hand. */ int cfi_block_start(struct cfi_softc *sc, u_int ofs) @@ -116,6 +123,8 @@ cfi_block_start(struct cfi_softc *sc, u_int ofs) break; } } + sc->sc_wrbufcpy = malloc(sc->sc_wrbufsz, M_TEMP, M_WAITOK); + memcpy(sc->sc_wrbufcpy, sc->sc_wrbuf, sc->sc_wrbufsz); sc->sc_writing = 1; return (0); } @@ -131,6 +140,7 @@ cfi_block_finish(struct cfi_softc *sc) error = cfi_write_block(sc); free(sc->sc_wrbuf, M_TEMP); + free(sc->sc_wrbufcpy, M_TEMP); sc->sc_wrbuf = NULL; sc->sc_wrbufsz = 0; sc->sc_wrofs = 0; diff --git a/sys/dev/cfi/cfi_disk.c b/sys/dev/cfi/cfi_disk.c index f5bcb1b67fa..79807225d14 100644 --- a/sys/dev/cfi/cfi_disk.c +++ b/sys/dev/cfi/cfi_disk.c @@ -1,7 +1,13 @@ /*- * Copyright (c) 2009 Sam Leffler, Errno Consulting + * Copyright (c) 2012-2013, SRI International * All rights reserved. * + * Portions of this software were developed by SRI International and the + * University of Cambridge Computer Laboratory under DARPA/AFRL contract + * (FA8750-10-C-0237) ("CTSRD"), as part of the DARPA CRASH research + * programme. + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: diff --git a/sys/dev/cfi/cfi_reg.h b/sys/dev/cfi/cfi_reg.h index 7c22211c370..c810e3f7448 100644 --- a/sys/dev/cfi/cfi_reg.h +++ b/sys/dev/cfi/cfi_reg.h @@ -1,7 +1,13 @@ /*- * Copyright (c) 2007, Juniper Networks, Inc. + * Copyright (c) 2012-2013, SRI International * All rights reserved. * + * Portions of this software were developed by SRI International and the + * University of Cambridge Computer Laboratory under DARPA/AFRL contract + * (FA8750-10-C-0237) ("CTSRD"), as part of the DARPA CRASH research + * programme. + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: @@ -44,8 +50,8 @@ struct cfi_qry { u_char max_vcc; u_char min_vpp; u_char max_vpp; - u_char tto_byte_write; /* 2**n milliseconds. */ - u_char tto_buf_write; /* 2**n milliseconds. */ + u_char tto_byte_write; /* 2**n microseconds. */ + u_char tto_buf_write; /* 2**n microseconds. */ u_char tto_block_erase; /* 2**n milliseconds. */ u_char tto_chip_erase; /* 2**n milliseconds. */ u_char mto_byte_write; /* 2**n times typical t/o. */ @@ -70,12 +76,15 @@ struct cfi_qry { #define CFI_QRY_VEND offsetof(struct cfi_qry, pri_vend) #define CFI_QRY_TTO_WRITE offsetof(struct cfi_qry, tto_byte_write) +#define CFI_QRY_TTO_BUFWRITE offsetof(struct cfi_qry, tto_buf_write) #define CFI_QRY_TTO_ERASE offsetof(struct cfi_qry, tto_block_erase) #define CFI_QRY_MTO_WRITE offsetof(struct cfi_qry, mto_byte_write) +#define CFI_QRY_MTO_BUFWRITE offsetof(struct cfi_qry, mto_buf_write) #define CFI_QRY_MTO_ERASE offsetof(struct cfi_qry, mto_block_erase) #define CFI_QRY_SIZE offsetof(struct cfi_qry, size) #define CFI_QRY_IFACE offsetof(struct cfi_qry, iface) +#define CFI_QRY_MAXBUF offsetof(struct cfi_qry, max_buf_write_size) #define CFI_QRY_NREGIONS offsetof(struct cfi_qry, nregions) #define CFI_QRY_REGION0 offsetof(struct cfi_qry, region) #define CFI_QRY_REGION(x) (CFI_QRY_REGION0 + (x) * 4) @@ -102,6 +111,7 @@ struct cfi_qry { #define CFI_BCS_ERASE_SUSPEND 0xb0 #define CFI_BCS_ERASE_RESUME 0xd0 /* Equals CONFIRM */ #define CFI_BCS_CONFIRM 0xd0 +#define CFI_BCS_BUF_PROG_SETUP 0xe8 #define CFI_BCS_READ_ARRAY 0xff /* Intel commands. */ diff --git a/sys/dev/cfi/cfi_var.h b/sys/dev/cfi/cfi_var.h index 15c77692ee3..e218a4dd5d6 100644 --- a/sys/dev/cfi/cfi_var.h +++ b/sys/dev/cfi/cfi_var.h @@ -1,7 +1,13 @@ /*- * Copyright (c) 2007, Juniper Networks, Inc. + * Copyright (c) 2012-2013, SRI International * All rights reserved. * + * Portions of this software were developed by SRI International and the + * University of Cambridge Computer Laboratory under DARPA/AFRL contract + * (FA8750-10-C-0237) ("CTSRD"), as part of the DARPA CRASH research + * programme. + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: @@ -32,6 +38,12 @@ #ifndef _DEV_CFI_VAR_H_ #define _DEV_CFI_VAR_H_ +enum cfi_wait_cmd { + CFI_TIMEOUT_ERASE, + CFI_TIMEOUT_WRITE, + CFI_TIMEOUT_BUFWRITE +}; + struct cfi_region { u_int r_blocks; u_int r_blksz; @@ -51,13 +63,18 @@ struct cfi_softc { struct cfi_region *sc_region; /* Array of region info. */ u_int sc_cmdset; - u_int sc_erase_timeout; - u_int sc_write_timeout; + sbintime_t sc_typical_timeouts[3]; + sbintime_t sc_max_timeouts[3]; + u_int sc_tto_counts[3]; + u_int sc_mto_counts[3]; + + u_int sc_maxbuf; struct cdev *sc_nod; struct proc *sc_opened; /* Process that has us opened. */ u_char *sc_wrbuf; + u_char *sc_wrbufcpy; u_int sc_wrbufsz; u_int sc_wrofs; u_int sc_writing; diff --git a/sys/dev/cpuctl/cpuctl.c b/sys/dev/cpuctl/cpuctl.c index 742ef0db813..317fc08d106 100644 --- a/sys/dev/cpuctl/cpuctl.c +++ b/sys/dev/cpuctl/cpuctl.c @@ -295,10 +295,10 @@ cpuctl_do_update(int cpu, cpuctl_update_args_t *data, struct thread *td) static int update_intel(int cpu, cpuctl_update_args_t *args, struct thread *td) { - void *ptr = NULL; + void *ptr; uint64_t rev0, rev1; uint32_t tmp[4]; - int is_bound = 0; + int is_bound; int oldcpu; int ret; @@ -312,10 +312,11 @@ update_intel(int cpu, cpuctl_update_args_t *args, struct thread *td) } /* - * 16 byte alignment required. + * 16 byte alignment required. Rely on the fact that + * malloc(9) always returns the pointer aligned at least on + * the size of the allocation. */ ptr = malloc(args->size + 16, M_CPUCTL, M_WAITOK); - ptr = (void *)(16 + ((intptr_t)ptr & ~0xf)); if (copyin(args->data, ptr, args->size) != 0) { DPRINTF("[cpuctl,%d]: copyin %p->%p of %zd bytes failed", __LINE__, args->data, ptr, args->size); @@ -346,8 +347,7 @@ update_intel(int cpu, cpuctl_update_args_t *args, struct thread *td) else ret = EEXIST; fail: - if (ptr != NULL) - contigfree(ptr, args->size, M_CPUCTL); + free(ptr, M_CPUCTL); return (ret); } @@ -409,10 +409,10 @@ fail: static int update_via(int cpu, cpuctl_update_args_t *args, struct thread *td) { - void *ptr = NULL; + void *ptr; uint64_t rev0, rev1, res; uint32_t tmp[4]; - int is_bound = 0; + int is_bound; int oldcpu; int ret; @@ -428,8 +428,7 @@ update_via(int cpu, cpuctl_update_args_t *args, struct thread *td) /* * 4 byte alignment required. */ - ptr = malloc(args->size + 16, M_CPUCTL, M_WAITOK); - ptr = (void *)(16 + ((intptr_t)ptr & ~0xf)); + ptr = malloc(args->size, M_CPUCTL, M_WAITOK); if (copyin(args->data, ptr, args->size) != 0) { DPRINTF("[cpuctl,%d]: copyin %p->%p of %zd bytes failed", __LINE__, args->data, ptr, args->size); @@ -476,8 +475,7 @@ update_via(int cpu, cpuctl_update_args_t *args, struct thread *td) else ret = 0; fail: - if (ptr != NULL) - contigfree(ptr, args->size, M_CPUCTL); + free(ptr, M_CPUCTL); return (ret); } diff --git a/sys/dev/cxgb/cxgb_sge.c b/sys/dev/cxgb/cxgb_sge.c index 3a5d8f6fbfd..bf669b9e34c 100644 --- a/sys/dev/cxgb/cxgb_sge.c +++ b/sys/dev/cxgb/cxgb_sge.c @@ -1470,9 +1470,9 @@ t3_encap(struct sge_qset *qs, struct mbuf **m) hdr->len = htonl(mlen | 0x80000000); if (__predict_false(mlen < TCPPKTHDRSIZE)) { - printf("mbuf=%p,len=%d,tso_segsz=%d,csum_flags=%#x,flags=%#x", + printf("mbuf=%p,len=%d,tso_segsz=%d,csum_flags=%b,flags=%#x", m0, mlen, m0->m_pkthdr.tso_segsz, - m0->m_pkthdr.csum_flags, m0->m_flags); + (int)m0->m_pkthdr.csum_flags, CSUM_BITS, m0->m_flags); panic("tx tso packet too small"); } @@ -2634,7 +2634,6 @@ t3_rx_eth(struct adapter *adap, struct mbuf *m, int ethpad) } m->m_pkthdr.rcvif = ifp; - m->m_pkthdr.header = mtod(m, uint8_t *) + sizeof(*cpl) + ethpad; /* * adjust after conversion to mbuf chain */ diff --git a/sys/dev/cxgb/ulp/iw_cxgb/iw_cxgb_qp.c b/sys/dev/cxgb/ulp/iw_cxgb/iw_cxgb_qp.c index 7734fd02976..648d96b867a 100644 --- a/sys/dev/cxgb/ulp/iw_cxgb/iw_cxgb_qp.c +++ b/sys/dev/cxgb/ulp/iw_cxgb/iw_cxgb_qp.c @@ -1097,9 +1097,8 @@ out: * If free is 1, then we've disassociated the EP from the QP * and we need to dereference the EP. */ - if (free) + if (free) put_ep(&ep->com); - CTR2(KTR_IW_CXGB, "%s exit state %d", __FUNCTION__, qhp->attr.state); return ret; diff --git a/sys/dev/cxgbe/adapter.h b/sys/dev/cxgbe/adapter.h index 2d118c4c8b4..89358d28c40 100644 --- a/sys/dev/cxgbe/adapter.h +++ b/sys/dev/cxgbe/adapter.h @@ -128,9 +128,9 @@ enum { RX_FL_ESIZE = EQ_ESIZE, /* 8 64bit addresses */ #if MJUMPAGESIZE != MCLBYTES - FL_BUF_SIZES = 4, /* cluster, jumbop, jumbo9k, jumbo16k */ + FL_BUF_SIZES_MAX = 5, /* cluster, jumbop, jumbo9k, jumbo16k, extra */ #else - FL_BUF_SIZES = 3, /* cluster, jumbo9k, jumbo16k */ + FL_BUF_SIZES_MAX = 4, /* cluster, jumbo9k, jumbo16k, extra */ #endif CTRL_EQ_QSIZE = 128, @@ -165,6 +165,7 @@ enum { MASTER_PF = (1 << 3), ADAP_SYSCTL_CTX = (1 << 4), TOM_INIT_DONE = (1 << 5), + BUF_PACKING_OK = (1 << 6), CXGBE_BUSY = (1 << 9), @@ -232,12 +233,11 @@ struct port_info { }; struct fl_sdesc { - struct mbuf *m; bus_dmamap_t map; caddr_t cl; - uint8_t tag_idx; /* the sc->fl_tag this map comes from */ + uint8_t tag_idx; /* the fl->tag entry this map comes from */ #ifdef INVARIANTS - __be64 ba_tag; + __be64 ba_hwtag; #endif }; @@ -359,9 +359,22 @@ struct sge_eq { uint32_t unstalled; /* recovered from stall */ }; +struct fl_buf_info { + u_int size; + int type; + int hwtag:4; /* tag in low 4 bits of the pa. */ + uma_zone_t zone; +}; +#define FL_BUF_SIZES(sc) (sc->sge.fl_buf_sizes) +#define FL_BUF_SIZE(sc, x) (sc->sge.fl_buf_info[x].size) +#define FL_BUF_TYPE(sc, x) (sc->sge.fl_buf_info[x].type) +#define FL_BUF_HWTAG(sc, x) (sc->sge.fl_buf_info[x].hwtag) +#define FL_BUF_ZONE(sc, x) (sc->sge.fl_buf_info[x].zone) + enum { FL_STARVING = (1 << 0), /* on the adapter's list of starving fl's */ FL_DOOMED = (1 << 1), /* about to be destroyed */ + FL_BUF_PACKING = (1 << 2), /* buffer packing enabled */ }; #define FL_RUNNING_LOW(fl) (fl->cap - fl->needed <= fl->lowat) @@ -370,7 +383,8 @@ enum { struct sge_fl { bus_dma_tag_t desc_tag; bus_dmamap_t desc_map; - bus_dma_tag_t tag[FL_BUF_SIZES]; + bus_dma_tag_t tag[FL_BUF_SIZES_MAX]; /* only first FL_BUF_SIZES(sc) are + valid */ uint8_t tag_idx; struct mtx fl_lock; char lockname[16]; @@ -383,11 +397,13 @@ struct sge_fl { uint16_t qsize; /* size (# of entries) of the queue */ uint16_t cntxt_id; /* SGE context id for the freelist */ uint32_t cidx; /* consumer idx (buffer idx, NOT hw desc idx) */ + uint32_t rx_offset; /* offset in fl buf (when buffer packing) */ uint32_t pidx; /* producer idx (buffer idx, NOT hw desc idx) */ uint32_t needed; /* # of buffers needed to fill up fl. */ uint32_t lowat; /* # of buffers <= this means fl needs help */ uint32_t pending; /* # of bufs allocated since last doorbell */ - unsigned int dmamap_failed; + u_int dmamap_failed; + struct mbuf *mstash[8]; TAILQ_ENTRY(sge_fl) link; /* All starving freelists */ }; @@ -519,6 +535,9 @@ struct sge { int eq_start; struct sge_iq **iqmap; /* iq->cntxt_id to iq mapping */ struct sge_eq **eqmap; /* eq->cntxt_id to eq mapping */ + + u_int fl_buf_sizes __aligned(CACHE_LINE_SIZE); + struct fl_buf_info fl_buf_info[FL_BUF_SIZES_MAX]; }; struct rss_header; @@ -559,6 +578,7 @@ struct adapter { bus_dma_tag_t dmat; /* Parent DMA tag */ struct sge sge; + int lro_timeout; struct taskqueue *tq[NCHAN]; /* taskqueues that flush data out */ struct port_info *port[MAX_NPORTS]; @@ -567,6 +587,7 @@ struct adapter { #ifdef TCP_OFFLOAD void *tom_softc; /* (struct tom_data *) */ struct tom_tunables tt; + void *iwarp_softc; /* (struct c4iw_dev *) */ #endif struct l2t_data *l2t; /* L2 table */ struct tid_info tids; diff --git a/sys/dev/cxgbe/offload.h b/sys/dev/cxgbe/offload.h index 7f4b38e72e2..8d3cf66208f 100644 --- a/sys/dev/cxgbe/offload.h +++ b/sys/dev/cxgbe/offload.h @@ -123,6 +123,7 @@ struct t4_virt_res { /* virtualized HW resources */ #ifdef TCP_OFFLOAD enum { ULD_TOM = 1, + ULD_IWARP = 2, }; struct adapter; diff --git a/sys/dev/cxgbe/osdep.h b/sys/dev/cxgbe/osdep.h index 5575f6155cf..403b535c0dd 100644 --- a/sys/dev/cxgbe/osdep.h +++ b/sys/dev/cxgbe/osdep.h @@ -45,6 +45,7 @@ #define CH_ALERT(adap, fmt, ...) log(LOG_ALERT, fmt, ##__VA_ARGS__) #define CH_WARN_RATELIMIT(adap, fmt, ...) log(LOG_WARNING, fmt, ##__VA_ARGS__) +#ifndef LINUX_TYPES_DEFINED typedef int8_t s8; typedef int16_t s16; typedef int32_t s32; @@ -156,5 +157,6 @@ strstrip(char *s) return (r); } +#endif /* LINUX_TYPES_DEFINED */ #endif diff --git a/sys/dev/cxgbe/t4_main.c b/sys/dev/cxgbe/t4_main.c index 3ff068f8c89..d905cd5aabd 100644 --- a/sys/dev/cxgbe/t4_main.c +++ b/sys/dev/cxgbe/t4_main.c @@ -160,10 +160,10 @@ MALLOC_DEFINE(M_CXGBE, "cxgbe", "Chelsio T4/T5 Ethernet driver and services"); * Correct lock order when you need to acquire multiple locks is t4_list_lock, * then ADAPTER_LOCK, then t4_uld_list_lock. */ -static struct mtx t4_list_lock; +static struct sx t4_list_lock; static SLIST_HEAD(, adapter) t4_list; #ifdef TCP_OFFLOAD -static struct mtx t4_uld_list_lock; +static struct sx t4_uld_list_lock; static SLIST_HEAD(, uld_info) t4_uld_list; #endif @@ -568,9 +568,9 @@ t4_attach(device_t dev) snprintf(sc->lockname, sizeof(sc->lockname), "%s", device_get_nameunit(dev)); mtx_init(&sc->sc_lock, sc->lockname, 0, MTX_DEF); - mtx_lock(&t4_list_lock); + sx_xlock(&t4_list_lock); SLIST_INSERT_HEAD(&t4_list, sc, link); - mtx_unlock(&t4_list_lock); + sx_xunlock(&t4_list_lock); mtx_init(&sc->sfl_lock, "starving freelists", 0, MTX_DEF); TAILQ_INIT(&sc->sfl); @@ -600,7 +600,6 @@ t4_attach(device_t dev) t4_register_cpl_handler(sc, CPL_TRACE_PKT_T5, t5_trace_pkt); t4_init_sge_cpl_handlers(sc); - /* Prepare the adapter for operation */ rc = -t4_prep_adapter(sc); if (rc != 0) { @@ -918,9 +917,9 @@ t4_detach(device_t dev) free(sc->tids.ftid_tab, M_CXGBE); t4_destroy_dma_tag(sc); if (mtx_initialized(&sc->sc_lock)) { - mtx_lock(&t4_list_lock); + sx_xlock(&t4_list_lock); SLIST_REMOVE(&t4_list, sc, adapter, link); - mtx_unlock(&t4_list_lock); + sx_xunlock(&t4_list_lock); mtx_destroy(&sc->sc_lock); } @@ -2492,7 +2491,7 @@ get_params__post_init(struct adapter *sc) param[3] = FW_PARAM_PFVF(CQ_END); param[4] = FW_PARAM_PFVF(OCQ_START); param[5] = FW_PARAM_PFVF(OCQ_END); - rc = -t4_query_params(sc, 0, 0, 0, 6, param, val); + rc = -t4_query_params(sc, sc->mbox, sc->pf, 0, 6, param, val); if (rc != 0) { device_printf(sc->dev, "failed to query RDMA parameters(2): %d.\n", rc); @@ -2549,9 +2548,9 @@ t4_set_desc(struct adapter *sc) char buf[128]; struct adapter_params *p = &sc->params; - snprintf(buf, sizeof(buf), "Chelsio %s %sNIC (rev %d), S/N:%s, E/C:%s", - p->vpd.id, is_offload(sc) ? "R" : "", chip_rev(sc), p->vpd.sn, - p->vpd.ec); + snprintf(buf, sizeof(buf), "Chelsio %s %sNIC (rev %d), S/N:%s, " + "P/N:%s, E/C:%s", p->vpd.id, is_offload(sc) ? "R" : "", + chip_rev(sc), p->vpd.sn, p->vpd.pn, p->vpd.ec); device_set_desc_copy(sc->dev, buf); } @@ -4230,6 +4229,10 @@ t4_sysctls(struct adapter *sc) t4_sge_sysctls(sc, ctx, children); + sc->lro_timeout = 100; + SYSCTL_ADD_INT(ctx, children, OID_AUTO, "lro_timeout", CTLFLAG_RW, + &sc->lro_timeout, 0, "lro inactive-flush timeout (in us)"); + #ifdef SBUF_DRAIN /* * dev.t4nex.X.misc. Marked CTLFLAG_SKIP to avoid information overload. @@ -7342,7 +7345,7 @@ t4_iterate(void (*func)(struct adapter *, void *), void *arg) { struct adapter *sc; - mtx_lock(&t4_list_lock); + sx_slock(&t4_list_lock); SLIST_FOREACH(sc, &t4_list, link) { /* * func should not make any assumptions about what state sc is @@ -7350,7 +7353,7 @@ t4_iterate(void (*func)(struct adapter *, void *), void *arg) */ func(sc, arg); } - mtx_unlock(&t4_list_lock); + sx_sunlock(&t4_list_lock); } static int @@ -7578,7 +7581,7 @@ t4_register_uld(struct uld_info *ui) int rc = 0; struct uld_info *u; - mtx_lock(&t4_uld_list_lock); + sx_xlock(&t4_uld_list_lock); SLIST_FOREACH(u, &t4_uld_list, link) { if (u->uld_id == ui->uld_id) { rc = EEXIST; @@ -7589,7 +7592,7 @@ t4_register_uld(struct uld_info *ui) SLIST_INSERT_HEAD(&t4_uld_list, ui, link); ui->refcount = 0; done: - mtx_unlock(&t4_uld_list_lock); + sx_xunlock(&t4_uld_list_lock); return (rc); } @@ -7599,7 +7602,7 @@ t4_unregister_uld(struct uld_info *ui) int rc = EINVAL; struct uld_info *u; - mtx_lock(&t4_uld_list_lock); + sx_xlock(&t4_uld_list_lock); SLIST_FOREACH(u, &t4_uld_list, link) { if (u == ui) { @@ -7614,7 +7617,7 @@ t4_unregister_uld(struct uld_info *ui) } } done: - mtx_unlock(&t4_uld_list_lock); + sx_xunlock(&t4_uld_list_lock); return (rc); } @@ -7626,7 +7629,7 @@ t4_activate_uld(struct adapter *sc, int id) ASSERT_SYNCHRONIZED_OP(sc); - mtx_lock(&t4_uld_list_lock); + sx_slock(&t4_uld_list_lock); SLIST_FOREACH(ui, &t4_uld_list, link) { if (ui->uld_id == id) { @@ -7637,7 +7640,7 @@ t4_activate_uld(struct adapter *sc, int id) } } done: - mtx_unlock(&t4_uld_list_lock); + sx_sunlock(&t4_uld_list_lock); return (rc); } @@ -7650,7 +7653,7 @@ t4_deactivate_uld(struct adapter *sc, int id) ASSERT_SYNCHRONIZED_OP(sc); - mtx_lock(&t4_uld_list_lock); + sx_slock(&t4_uld_list_lock); SLIST_FOREACH(ui, &t4_uld_list, link) { if (ui->uld_id == id) { @@ -7661,7 +7664,7 @@ t4_deactivate_uld(struct adapter *sc, int id) } } done: - mtx_unlock(&t4_uld_list_lock); + sx_sunlock(&t4_uld_list_lock); return (rc); } @@ -7742,10 +7745,10 @@ mod_event(module_t mod, int cmd, void *arg) if (atomic_fetchadd_int(&loaded, 1)) break; t4_sge_modload(); - mtx_init(&t4_list_lock, "T4 adapters", 0, MTX_DEF); + sx_init(&t4_list_lock, "T4/T5 adapters"); SLIST_INIT(&t4_list); #ifdef TCP_OFFLOAD - mtx_init(&t4_uld_list_lock, "T4 ULDs", 0, MTX_DEF); + sx_init(&t4_uld_list_lock, "T4/T5 ULDs"); SLIST_INIT(&t4_uld_list); #endif t4_tracer_modload(); @@ -7757,23 +7760,23 @@ mod_event(module_t mod, int cmd, void *arg) break; t4_tracer_modunload(); #ifdef TCP_OFFLOAD - mtx_lock(&t4_uld_list_lock); + sx_slock(&t4_uld_list_lock); if (!SLIST_EMPTY(&t4_uld_list)) { rc = EBUSY; - mtx_unlock(&t4_uld_list_lock); + sx_sunlock(&t4_uld_list_lock); break; } - mtx_unlock(&t4_uld_list_lock); - mtx_destroy(&t4_uld_list_lock); + sx_sunlock(&t4_uld_list_lock); + sx_destroy(&t4_uld_list_lock); #endif - mtx_lock(&t4_list_lock); + sx_slock(&t4_list_lock); if (!SLIST_EMPTY(&t4_list)) { rc = EBUSY; - mtx_unlock(&t4_list_lock); + sx_sunlock(&t4_list_lock); break; } - mtx_unlock(&t4_list_lock); - mtx_destroy(&t4_list_lock); + sx_sunlock(&t4_list_lock); + sx_destroy(&t4_list_lock); break; } diff --git a/sys/dev/cxgbe/t4_sge.c b/sys/dev/cxgbe/t4_sge.c index 5064d7031e1..776b6c5a734 100644 --- a/sys/dev/cxgbe/t4_sge.c +++ b/sys/dev/cxgbe/t4_sge.c @@ -39,6 +39,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include #include @@ -55,19 +56,6 @@ __FBSDID("$FreeBSD$"); #include "common/t4_regs_values.h" #include "common/t4_msg.h" -struct fl_buf_info { - int size; - int type; - uma_zone_t zone; -}; - -/* Filled up by t4_sge_modload */ -static struct fl_buf_info fl_buf_info[FL_BUF_SIZES]; - -#define FL_BUF_SIZE(x) (fl_buf_info[x].size) -#define FL_BUF_TYPE(x) (fl_buf_info[x].type) -#define FL_BUF_ZONE(x) (fl_buf_info[x].zone) - #ifdef T4_PKT_TIMESTAMP #define RX_COPY_THRESHOLD (MINCLSIZE - 8) #else @@ -84,7 +72,8 @@ TUNABLE_INT("hw.cxgbe.fl_pktshift", &fl_pktshift); /* * Pad ethernet payload up to this boundary. * -1: driver should figure out a good value. - * Any power of 2, from 32 to 4096 (both inclusive) is a valid value. + * 0: disable padding. + * Any power of 2 from 32 to 4096 (both inclusive) is also a valid value. */ static int fl_pad = -1; TUNABLE_INT("hw.cxgbe.fl_pad", &fl_pad); @@ -106,6 +95,33 @@ TUNABLE_INT("hw.cxgbe.spg_len", &spg_len); static int cong_drop = 0; TUNABLE_INT("hw.cxgbe.cong_drop", &cong_drop); +/* + * Deliver multiple frames in the same free list buffer if they fit. + * -1: let the driver decide whether to enable buffer packing or not. + * 0: disable buffer packing. + * 1: enable buffer packing. + */ +static int buffer_packing = -1; +TUNABLE_INT("hw.cxgbe.buffer_packing", &buffer_packing); + +/* + * Start next frame in a packed buffer at this boundary. + * -1: driver should figure out a good value. + * T4: + * --- + * if fl_pad != 0 + * value specified here will be overridden by fl_pad. + * else + * power of 2 from 32 to 4096 (both inclusive) is a valid value here. + * T5: + * --- + * 16, or a power of 2 from 64 to 4096 (both inclusive) is a valid value. + */ +static int fl_pack = -1; +static int t4_fl_pack; +static int t5_fl_pack; +TUNABLE_INT("hw.cxgbe.fl_pack", &fl_pack); + /* Used to track coalesced tx work request */ struct txpkts { uint64_t *flitp; /* ptr to flit where next pkt should start */ @@ -122,12 +138,15 @@ struct sgl { }; static int service_iq(struct sge_iq *, int); -static struct mbuf *get_fl_payload(struct adapter *, struct sge_fl *, uint32_t, +static struct mbuf *get_fl_payload1(struct adapter *, struct sge_fl *, uint32_t, + int *); +static struct mbuf *get_fl_payload2(struct adapter *, struct sge_fl *, uint32_t, int *); static int t4_eth_rx(struct sge_iq *, const struct rss_header *, struct mbuf *); static inline void init_iq(struct sge_iq *, struct adapter *, int, int, int, int); -static inline void init_fl(struct sge_fl *, int, int, char *); +static inline void init_fl(struct adapter *, struct sge_fl *, int, int, int, + char *); static inline void init_eq(struct sge_eq *, int, int, uint8_t, uint16_t, char *); static int alloc_ring(struct adapter *, size_t, bus_dma_tag_t *, bus_dmamap_t *, @@ -169,8 +188,8 @@ static inline void ring_fl_db(struct adapter *, struct sge_fl *); static int refill_fl(struct adapter *, struct sge_fl *, int); static void refill_sfl(void *); static int alloc_fl_sdesc(struct sge_fl *); -static void free_fl_sdesc(struct sge_fl *); -static void set_fl_tag_idx(struct sge_fl *, int); +static void free_fl_sdesc(struct adapter *, struct sge_fl *); +static void set_fl_tag_idx(struct adapter *, struct sge_fl *, int); static void add_fl_to_sfl(struct adapter *, struct sge_fl *); static int get_pkt_sgl(struct sge_txq *, struct mbuf **, struct sgl *, int); @@ -201,27 +220,20 @@ extern u_int cpu_clflush_line_size; #endif /* - * Called on MOD_LOAD. Fills up fl_buf_info[] and validates/calculates the SGE - * tunables. + * Called on MOD_LOAD. Validates and calculates the SGE tunables. */ void t4_sge_modload(void) { - int i; - int bufsize[FL_BUF_SIZES] = { - MCLBYTES, -#if MJUMPAGESIZE != MCLBYTES - MJUMPAGESIZE, -#endif - MJUM9BYTES, - MJUM16BYTES - }; + int pad; - for (i = 0; i < FL_BUF_SIZES; i++) { - FL_BUF_SIZE(i) = bufsize[i]; - FL_BUF_TYPE(i) = m_gettype(bufsize[i]); - FL_BUF_ZONE(i) = m_getzone(bufsize[i]); - } + /* set pad to a reasonable powerof2 between 16 and 4096 (inclusive) */ +#if defined(__i386__) || defined(__amd64__) + pad = max(cpu_clflush_line_size, 16); +#else + pad = max(CACHE_LINE_SIZE, 16); +#endif + pad = min(pad, 4096); if (fl_pktshift < 0 || fl_pktshift > 7) { printf("Invalid hw.cxgbe.fl_pktshift value (%d)," @@ -229,23 +241,35 @@ t4_sge_modload(void) fl_pktshift = 2; } - if (fl_pad < 32 || fl_pad > 4096 || !powerof2(fl_pad)) { - int pad; - -#if defined(__i386__) || defined(__amd64__) - pad = max(cpu_clflush_line_size, 32); -#else - pad = max(CACHE_LINE_SIZE, 32); -#endif - pad = min(pad, 4096); + if (fl_pad != 0 && + (fl_pad < 32 || fl_pad > 4096 || !powerof2(fl_pad))) { if (fl_pad != -1) { printf("Invalid hw.cxgbe.fl_pad value (%d)," - " using %d instead.\n", fl_pad, pad); + " using %d instead.\n", fl_pad, max(pad, 32)); } - fl_pad = pad; + fl_pad = max(pad, 32); } + /* + * T4 has the same pad and pack boundary. If a pad boundary is set, + * pack boundary must be set to the same value. Otherwise take the + * specified value or auto-calculate something reasonable. + */ + if (fl_pad) + t4_fl_pack = fl_pad; + else if (fl_pack < 32 || fl_pack > 4096 || !powerof2(fl_pack)) + t4_fl_pack = max(pad, 32); + else + t4_fl_pack = fl_pack; + + /* T5's pack boundary is independent of the pad boundary. */ + if (fl_pack < 16 || fl_pack == 32 || fl_pack > 4096 || + !powerof2(fl_pack)) + t5_fl_pack = max(pad, 64); + else + t5_fl_pack = fl_pack; + if (spg_len != 64 && spg_len != 128) { int len; @@ -291,17 +315,41 @@ t4_tweak_chip_settings(struct adapter *sc) int timer_max = M_TIMERVALUE0 * 1000 / sc->params.vpd.cclk; int intr_pktcount[SGE_NCOUNTERS] = {1, 8, 16, 32}; /* 63 max */ uint16_t indsz = min(RX_COPY_THRESHOLD - 1, M_INDICATESIZE); + int sw_flbuf_sizes[] = { + MCLBYTES, +#if MJUMPAGESIZE != MCLBYTES + MJUMPAGESIZE, +#endif + MJUM9BYTES, + MJUM16BYTES, + MJUMPAGESIZE - MSIZE + }; KASSERT(sc->flags & MASTER_PF, ("%s: trying to change chip settings when not master.", __func__)); - m = V_PKTSHIFT(M_PKTSHIFT) | F_RXPKTCPLMODE | - V_INGPADBOUNDARY(M_INGPADBOUNDARY) | F_EGRSTATUSPAGESIZE; + m = V_PKTSHIFT(M_PKTSHIFT) | F_RXPKTCPLMODE | F_EGRSTATUSPAGESIZE; v = V_PKTSHIFT(fl_pktshift) | F_RXPKTCPLMODE | - V_INGPADBOUNDARY(ilog2(fl_pad) - 5) | V_EGRSTATUSPAGESIZE(spg_len == 128); + if (is_t4(sc) && (fl_pad || buffer_packing)) { + /* t4_fl_pack has the correct value even when fl_pad = 0 */ + m |= V_INGPADBOUNDARY(M_INGPADBOUNDARY); + v |= V_INGPADBOUNDARY(ilog2(t4_fl_pack) - 5); + } else if (is_t5(sc) && fl_pad) { + m |= V_INGPADBOUNDARY(M_INGPADBOUNDARY); + v |= V_INGPADBOUNDARY(ilog2(fl_pad) - 5); + } t4_set_reg_field(sc, A_SGE_CONTROL, m, v); + if (is_t5(sc) && buffer_packing) { + m = V_INGPACKBOUNDARY(M_INGPACKBOUNDARY); + if (t5_fl_pack == 16) + v = V_INGPACKBOUNDARY(0); + else + v = V_INGPACKBOUNDARY(ilog2(t5_fl_pack) - 5); + t4_set_reg_field(sc, A_SGE_CONTROL2, m, v); + } + v = V_HOSTPAGESIZEPF0(PAGE_SHIFT - 10) | V_HOSTPAGESIZEPF1(PAGE_SHIFT - 10) | V_HOSTPAGESIZEPF2(PAGE_SHIFT - 10) | @@ -312,9 +360,9 @@ t4_tweak_chip_settings(struct adapter *sc) V_HOSTPAGESIZEPF7(PAGE_SHIFT - 10); t4_write_reg(sc, A_SGE_HOST_PAGE_SIZE, v); - for (i = 0; i < FL_BUF_SIZES; i++) { + for (i = 0; i < min(nitems(sw_flbuf_sizes), 16); i++) { t4_write_reg(sc, A_SGE_FL_BUFFER_SIZE0 + (4 * i), - FL_BUF_SIZE(i)); + sw_flbuf_sizes[i]); } v = V_THRESHOLD_0(intr_pktcount[0]) | V_THRESHOLD_1(intr_pktcount[1]) | @@ -375,21 +423,48 @@ int t4_read_chip_settings(struct adapter *sc) { struct sge *s = &sc->sge; - int i, rc = 0; + int i, j, n, rc = 0; uint32_t m, v, r; uint16_t indsz = min(RX_COPY_THRESHOLD - 1, M_INDICATESIZE); + uint32_t sge_flbuf_sizes[16], sw_flbuf_sizes[] = { + MCLBYTES, +#if MJUMPAGESIZE != MCLBYTES + MJUMPAGESIZE, +#endif + MJUM9BYTES, + MJUM16BYTES + }; - m = V_PKTSHIFT(M_PKTSHIFT) | F_RXPKTCPLMODE | - V_INGPADBOUNDARY(M_INGPADBOUNDARY) | F_EGRSTATUSPAGESIZE; + m = V_PKTSHIFT(M_PKTSHIFT) | F_RXPKTCPLMODE | F_EGRSTATUSPAGESIZE; v = V_PKTSHIFT(fl_pktshift) | F_RXPKTCPLMODE | - V_INGPADBOUNDARY(ilog2(fl_pad) - 5) | V_EGRSTATUSPAGESIZE(spg_len == 128); + if (is_t4(sc) && (fl_pad || buffer_packing)) { + m |= V_INGPADBOUNDARY(M_INGPADBOUNDARY); + v |= V_INGPADBOUNDARY(ilog2(t4_fl_pack) - 5); + } else if (is_t5(sc) && fl_pad) { + m |= V_INGPADBOUNDARY(M_INGPADBOUNDARY); + v |= V_INGPADBOUNDARY(ilog2(fl_pad) - 5); + } r = t4_read_reg(sc, A_SGE_CONTROL); if ((r & m) != v) { device_printf(sc->dev, "invalid SGE_CONTROL(0x%x)\n", r); rc = EINVAL; } + if (is_t5(sc) && buffer_packing) { + m = V_INGPACKBOUNDARY(M_INGPACKBOUNDARY); + if (t5_fl_pack == 16) + v = V_INGPACKBOUNDARY(0); + else + v = V_INGPACKBOUNDARY(ilog2(t5_fl_pack) - 5); + r = t4_read_reg(sc, A_SGE_CONTROL2); + if ((r & m) != v) { + device_printf(sc->dev, + "invalid SGE_CONTROL2(0x%x)\n", r); + rc = EINVAL; + } + } + v = V_HOSTPAGESIZEPF0(PAGE_SHIFT - 10) | V_HOSTPAGESIZEPF1(PAGE_SHIFT - 10) | V_HOSTPAGESIZEPF2(PAGE_SHIFT - 10) | @@ -404,14 +479,45 @@ t4_read_chip_settings(struct adapter *sc) rc = EINVAL; } - for (i = 0; i < FL_BUF_SIZES; i++) { - v = t4_read_reg(sc, A_SGE_FL_BUFFER_SIZE0 + (4 * i)); - if (v != FL_BUF_SIZE(i)) { - device_printf(sc->dev, - "invalid SGE_FL_BUFFER_SIZE[%d](0x%x)\n", i, v); - rc = EINVAL; + /* + * Make a list of SGE FL buffer sizes programmed in the chip and tally + * it with the FL buffer sizes that we'd like to use. + */ + n = 0; + for (i = 0; i < nitems(sge_flbuf_sizes); i++) { + r = t4_read_reg(sc, A_SGE_FL_BUFFER_SIZE0 + (4 * i)); + sge_flbuf_sizes[i] = r; + if (r == MJUMPAGESIZE - MSIZE && + (sc->flags & BUF_PACKING_OK) == 0) { + sc->flags |= BUF_PACKING_OK; + FL_BUF_HWTAG(sc, n) = i; + FL_BUF_SIZE(sc, n) = MJUMPAGESIZE - MSIZE; + FL_BUF_TYPE(sc, n) = m_gettype(MJUMPAGESIZE); + FL_BUF_ZONE(sc, n) = m_getzone(MJUMPAGESIZE); + n++; } } + for (i = 0; i < nitems(sw_flbuf_sizes); i++) { + for (j = 0; j < nitems(sge_flbuf_sizes); j++) { + if (sw_flbuf_sizes[i] != sge_flbuf_sizes[j]) + continue; + FL_BUF_HWTAG(sc, n) = j; + FL_BUF_SIZE(sc, n) = sw_flbuf_sizes[i]; + FL_BUF_TYPE(sc, n) = m_gettype(sw_flbuf_sizes[i]); + FL_BUF_ZONE(sc, n) = m_getzone(sw_flbuf_sizes[i]); + n++; + break; + } + } + if (n == 0) { + device_printf(sc->dev, "no usable SGE FL buffer size.\n"); + rc = EINVAL; + } else if (n == 1 && (sc->flags & BUF_PACKING_OK)) { + device_printf(sc->dev, + "no usable SGE FL buffer size when not packing buffers.\n"); + rc = EINVAL; + } + FL_BUF_SIZES(sc) = n; r = t4_read_reg(sc, A_SGE_INGRESS_RX_THRESHOLD); s->counter_val[0] = G_THRESHOLD_0(r); @@ -498,6 +604,17 @@ t4_create_dma_tag(struct adapter *sc) return (rc); } +static inline int +enable_buffer_packing(struct adapter *sc) +{ + + if (sc->flags & BUF_PACKING_OK && + ((is_t5(sc) && buffer_packing) || /* 1 or -1 both ok for T5 */ + (is_t4(sc) && buffer_packing == 1))) + return (1); + return (0); +} + void t4_sge_sysctls(struct adapter *sc, struct sysctl_ctx_list *ctx, struct sysctl_oid_list *children) @@ -514,6 +631,14 @@ t4_sge_sysctls(struct adapter *sc, struct sysctl_ctx_list *ctx, SYSCTL_ADD_INT(ctx, children, OID_AUTO, "cong_drop", CTLFLAG_RD, NULL, cong_drop, "congestion drop setting"); + + SYSCTL_ADD_INT(ctx, children, OID_AUTO, "buffer_packing", CTLFLAG_RD, + NULL, enable_buffer_packing(sc), + "pack multiple frames in one fl buffer"); + + SYSCTL_ADD_INT(ctx, children, OID_AUTO, "fl_pack", CTLFLAG_RD, + NULL, is_t5(sc) ? t5_fl_pack : t4_fl_pack, + "payload pack boundary (bytes)"); } int @@ -705,7 +830,7 @@ t4_setup_port_queues(struct port_info *pi) struct ifnet *ifp = pi->ifp; struct sysctl_oid *oid = device_get_sysctl_tree(pi->dev); struct sysctl_oid_list *children = SYSCTL_CHILDREN(oid); - int bufsize; + int bufsize, pack; oid = SYSCTL_ADD_NODE(&pi->ctx, children, OID_AUTO, "rxq", CTLFLAG_RD, NULL, "rx queues"); @@ -727,6 +852,7 @@ t4_setup_port_queues(struct port_info *pi) * b) allocate queue iff it will take direct interrupts. */ bufsize = mtu_to_bufsize(ifp->if_mtu); + pack = enable_buffer_packing(sc); for_each_rxq(pi, i, rxq) { init_iq(&rxq->iq, sc, pi->tmr_idx, pi->pktc_idx, pi->qsize_rxq, @@ -734,7 +860,7 @@ t4_setup_port_queues(struct port_info *pi) snprintf(name, sizeof(name), "%s rxq%d-fl", device_get_nameunit(pi->dev), i); - init_fl(&rxq->fl, pi->qsize_rxq / 8, bufsize, name); + init_fl(sc, &rxq->fl, pi->qsize_rxq / 8, bufsize, pack, name); if (sc->flags & INTR_DIRECT #ifdef TCP_OFFLOAD @@ -751,6 +877,7 @@ t4_setup_port_queues(struct port_info *pi) #ifdef TCP_OFFLOAD bufsize = mtu_to_bufsize_toe(sc, ifp->if_mtu); + pack = 0; /* XXX: think about this some more */ for_each_ofld_rxq(pi, i, ofld_rxq) { init_iq(&ofld_rxq->iq, sc, pi->tmr_idx, pi->pktc_idx, @@ -758,7 +885,8 @@ t4_setup_port_queues(struct port_info *pi) snprintf(name, sizeof(name), "%s ofld_rxq%d-fl", device_get_nameunit(pi->dev), i); - init_fl(&ofld_rxq->fl, pi->qsize_rxq / 8, bufsize, name); + init_fl(sc, &ofld_rxq->fl, pi->qsize_rxq / 8, bufsize, pack, + name); if (sc->flags & INTR_DIRECT || (sc->intr_count > 1 && pi->nofldrxq > pi->nrxq)) { @@ -1006,6 +1134,9 @@ service_iq(struct sge_iq *iq, int budget) uint32_t lq; struct mbuf *m0; STAILQ_HEAD(, sge_iq) iql = STAILQ_HEAD_INITIALIZER(iql); +#if defined(INET) || defined(INET6) + const struct timeval lro_timeout = {0, sc->lro_timeout}; +#endif limit = budget ? budget : iq->qsize / 8; @@ -1032,7 +1163,12 @@ service_iq(struct sge_iq *iq, int budget) ("%s: data for an iq (%p) with no freelist", __func__, iq)); - m0 = get_fl_payload(sc, fl, lq, &fl_bufs_used); + m0 = fl->flags & FL_BUF_PACKING ? + get_fl_payload1(sc, fl, lq, &fl_bufs_used) : + get_fl_payload2(sc, fl, lq, &fl_bufs_used); + + if (__predict_false(m0 == NULL)) + goto process_iql; #ifdef T4_PKT_TIMESTAMP /* * 60 bit timestamp for the payload is @@ -1069,6 +1205,17 @@ service_iq(struct sge_iq *iq, int budget) ("%s: budget %u, rsp_type %u", __func__, budget, rsp_type)); + /* + * There are 1K interrupt-capable queues (qids 0 + * through 1023). A response type indicating a + * forwarded interrupt with a qid >= 1K is an + * iWARP async notification. + */ + if (lq >= 1024) { + sc->an_handler(iq, ctrl); + break; + } + q = sc->sge.iqmap[lq - sc->sge.iq_start]; if (atomic_cmpset_int(&q->state, IQS_IDLE, IQS_BUSY)) { @@ -1083,7 +1230,12 @@ service_iq(struct sge_iq *iq, int budget) break; default: - sc->an_handler(iq, ctrl); + KASSERT(0, + ("%s: illegal response type %d on iq %p", + __func__, rsp_type, iq)); + log(LOG_ERR, + "%s: illegal response type %d on iq %p", + device_get_nameunit(sc->dev), rsp_type, iq); break; } @@ -1095,6 +1247,14 @@ service_iq(struct sge_iq *iq, int budget) V_SEINTARM(V_QINTR_TIMER_IDX(X_TIMERREG_UPDATE_CIDX))); ndescs = 0; +#if defined(INET) || defined(INET6) + if (iq->flags & IQ_LRO_ENABLED && + sc->lro_timeout != 0) { + tcp_lro_flush_inactive(&rxq->lro, + &lro_timeout); + } +#endif + if (fl_bufs_used > 0) { FL_LOCK(fl); fl->needed += fl_bufs_used; @@ -1108,6 +1268,7 @@ service_iq(struct sge_iq *iq, int budget) } } +process_iql: if (STAILQ_EMPTY(&iql)) break; @@ -1153,8 +1314,249 @@ service_iq(struct sge_iq *iq, int budget) return (0); } +static int +fill_mbuf_stash(struct sge_fl *fl) +{ + int i; + + for (i = 0; i < nitems(fl->mstash); i++) { + if (fl->mstash[i] == NULL) { + struct mbuf *m; + if ((m = m_get(M_NOWAIT, MT_NOINIT)) == NULL) + return (ENOBUFS); + fl->mstash[i] = m; + } + } + return (0); +} + static struct mbuf * -get_fl_payload(struct adapter *sc, struct sge_fl *fl, uint32_t len_newbuf, +get_mbuf_from_stash(struct sge_fl *fl) +{ + int i; + + for (i = 0; i < nitems(fl->mstash); i++) { + if (fl->mstash[i] != NULL) { + struct mbuf *m; + + m = fl->mstash[i]; + fl->mstash[i] = NULL; + return (m); + } else + fl->mstash[i] = m_get(M_NOWAIT, MT_NOINIT); + } + + return (m_get(M_NOWAIT, MT_NOINIT)); +} + +static void +return_mbuf_to_stash(struct sge_fl *fl, struct mbuf *m) +{ + int i; + + if (m == NULL) + return; + + for (i = 0; i < nitems(fl->mstash); i++) { + if (fl->mstash[i] == NULL) { + fl->mstash[i] = m; + return; + } + } + m_init(m, NULL, 0, M_NOWAIT, MT_DATA, 0); + m_free(m); +} + +/* buf can be any address within the buffer */ +static inline u_int * +find_buf_refcnt(caddr_t buf) +{ + uintptr_t ptr = (uintptr_t)buf; + + return ((u_int *)((ptr & ~(MJUMPAGESIZE - 1)) + MSIZE - sizeof(u_int))); +} + +static inline struct mbuf * +find_buf_mbuf(caddr_t buf) +{ + uintptr_t ptr = (uintptr_t)buf; + + return ((struct mbuf *)(ptr & ~(MJUMPAGESIZE - 1))); +} + +static int +rxb_free(struct mbuf *m, void *arg1, void *arg2) +{ + uma_zone_t zone = arg1; + caddr_t cl = arg2; +#ifdef INVARIANTS + u_int refcount; + + refcount = *find_buf_refcnt(cl); + KASSERT(refcount == 0, ("%s: cl %p refcount is %u", __func__, + cl - MSIZE, refcount)); +#endif + cl -= MSIZE; + uma_zfree(zone, cl); + + return (EXT_FREE_OK); +} + +static struct mbuf * +get_fl_payload1(struct adapter *sc, struct sge_fl *fl, uint32_t len_newbuf, + int *fl_bufs_used) +{ + struct mbuf *m0, *m; + struct fl_sdesc *sd = &fl->sdesc[fl->cidx]; + unsigned int nbuf, len; + int pack_boundary = is_t4(sc) ? t4_fl_pack : t5_fl_pack; + + /* + * No assertion for the fl lock because we don't need it. This routine + * is called only from the rx interrupt handler and it only updates + * fl->cidx. (Contrast that with fl->pidx/fl->needed which could be + * updated in the rx interrupt handler or the starvation helper routine. + * That's why code that manipulates fl->pidx/fl->needed needs the fl + * lock but this routine does not). + */ + + KASSERT(fl->flags & FL_BUF_PACKING, + ("%s: buffer packing disabled for fl %p", __func__, fl)); + + len = G_RSPD_LEN(len_newbuf); + + if ((len_newbuf & F_RSPD_NEWBUF) == 0) { + KASSERT(fl->rx_offset > 0, + ("%s: packed frame but driver at offset=0", __func__)); + + /* A packed frame is guaranteed to fit entirely in this buf. */ + KASSERT(FL_BUF_SIZE(sc, sd->tag_idx) - fl->rx_offset >= len, + ("%s: packing error. bufsz=%u, offset=%u, len=%u", + __func__, FL_BUF_SIZE(sc, sd->tag_idx), fl->rx_offset, + len)); + + m0 = get_mbuf_from_stash(fl); + if (m0 == NULL || + m_init(m0, NULL, 0, M_NOWAIT, MT_DATA, M_PKTHDR) != 0) { + return_mbuf_to_stash(fl, m0); + return (NULL); + } + + bus_dmamap_sync(fl->tag[sd->tag_idx], sd->map, + BUS_DMASYNC_POSTREAD); + if (len < RX_COPY_THRESHOLD) { +#ifdef T4_PKT_TIMESTAMP + /* Leave room for a timestamp */ + m0->m_data += 8; +#endif + bcopy(sd->cl + fl->rx_offset, mtod(m0, caddr_t), len); + m0->m_pkthdr.len = len; + m0->m_len = len; + } else { + m0->m_pkthdr.len = len; + m0->m_len = len; + m_extaddref(m0, sd->cl + fl->rx_offset, + roundup2(m0->m_len, fl_pad), + find_buf_refcnt(sd->cl), rxb_free, + FL_BUF_ZONE(sc, sd->tag_idx), sd->cl); + } + fl->rx_offset += len; + fl->rx_offset = roundup2(fl->rx_offset, fl_pad); + fl->rx_offset = roundup2(fl->rx_offset, pack_boundary); + if (fl->rx_offset >= FL_BUF_SIZE(sc, sd->tag_idx)) { + fl->rx_offset = 0; + (*fl_bufs_used) += 1; + if (__predict_false(++fl->cidx == fl->cap)) + fl->cidx = 0; + } + + return (m0); + } + + KASSERT(len_newbuf & F_RSPD_NEWBUF, + ("%s: only new buffer handled here", __func__)); + + nbuf = 0; + + /* + * Move to the start of the next buffer if we are still in the middle of + * some buffer. This is the case where there was some room left in the + * previous buffer but not enough to fit this frame in its entirety. + */ + if (fl->rx_offset > 0) { + KASSERT(roundup2(len, fl_pad) > FL_BUF_SIZE(sc, sd->tag_idx) - + fl->rx_offset, ("%s: frame (%u bytes) should have fit at " + "cidx %u offset %u bufsize %u", __func__, len, fl->cidx, + fl->rx_offset, FL_BUF_SIZE(sc, sd->tag_idx))); + nbuf++; + fl->rx_offset = 0; + sd++; + if (__predict_false(++fl->cidx == fl->cap)) { + sd = fl->sdesc; + fl->cidx = 0; + } + } + + m0 = find_buf_mbuf(sd->cl); + if (m_init(m0, NULL, 0, M_NOWAIT, MT_DATA, M_PKTHDR | M_NOFREE)) + goto done; + bus_dmamap_sync(fl->tag[sd->tag_idx], sd->map, BUS_DMASYNC_POSTREAD); + m0->m_len = min(len, FL_BUF_SIZE(sc, sd->tag_idx)); + m_extaddref(m0, sd->cl, roundup2(m0->m_len, fl_pad), + find_buf_refcnt(sd->cl), rxb_free, FL_BUF_ZONE(sc, sd->tag_idx), + sd->cl); + m0->m_pkthdr.len = len; + + fl->rx_offset = roundup2(m0->m_len, fl_pad); + fl->rx_offset = roundup2(fl->rx_offset, pack_boundary); + if (fl->rx_offset >= FL_BUF_SIZE(sc, sd->tag_idx)) { + fl->rx_offset = 0; + nbuf++; + sd++; + if (__predict_false(++fl->cidx == fl->cap)) { + sd = fl->sdesc; + fl->cidx = 0; + } + } + + m = m0; + len -= m->m_len; + + while (len > 0) { + m->m_next = find_buf_mbuf(sd->cl); + m = m->m_next; + + bus_dmamap_sync(fl->tag[sd->tag_idx], sd->map, + BUS_DMASYNC_POSTREAD); + + /* m_init for !M_PKTHDR can't fail so don't bother */ + m_init(m, NULL, 0, M_NOWAIT, MT_DATA, M_NOFREE); + m->m_len = min(len, FL_BUF_SIZE(sc, sd->tag_idx)); + m_extaddref(m, sd->cl, roundup2(m->m_len, fl_pad), + find_buf_refcnt(sd->cl), rxb_free, + FL_BUF_ZONE(sc, sd->tag_idx), sd->cl); + + fl->rx_offset = roundup2(m->m_len, fl_pad); + fl->rx_offset = roundup2(fl->rx_offset, pack_boundary); + if (fl->rx_offset >= FL_BUF_SIZE(sc, sd->tag_idx)) { + fl->rx_offset = 0; + nbuf++; + sd++; + if (__predict_false(++fl->cidx == fl->cap)) { + sd = fl->sdesc; + fl->cidx = 0; + } + } + + len -= m->m_len; + } +done: + (*fl_bufs_used) += nbuf; + return (m0); +} + +static struct mbuf * +get_fl_payload2(struct adapter *sc, struct sge_fl *fl, uint32_t len_newbuf, int *fl_bufs_used) { struct mbuf *m0, *m; @@ -1170,29 +1572,42 @@ get_fl_payload(struct adapter *sc, struct sge_fl *fl, uint32_t len_newbuf, * lock but this routine does not). */ + KASSERT((fl->flags & FL_BUF_PACKING) == 0, + ("%s: buffer packing enabled for fl %p", __func__, fl)); if (__predict_false((len_newbuf & F_RSPD_NEWBUF) == 0)) panic("%s: cannot handle packed frames", __func__); len = G_RSPD_LEN(len_newbuf); - m0 = sd->m; - sd->m = NULL; /* consumed */ + /* + * We never want to run out of mbufs in between a frame when a frame + * spans multiple fl buffers. If the fl's mbuf stash isn't full and + * can't be filled up to the brim then fail early. + */ + if (len > FL_BUF_SIZE(sc, sd->tag_idx) && fill_mbuf_stash(fl) != 0) + return (NULL); + + m0 = get_mbuf_from_stash(fl); + if (m0 == NULL || + m_init(m0, NULL, 0, M_NOWAIT, MT_DATA, M_PKTHDR) != 0) { + return_mbuf_to_stash(fl, m0); + return (NULL); + } bus_dmamap_sync(fl->tag[sd->tag_idx], sd->map, BUS_DMASYNC_POSTREAD); - m_init(m0, NULL, 0, M_NOWAIT, MT_DATA, M_PKTHDR); -#ifdef T4_PKT_TIMESTAMP - /* Leave room for a timestamp */ - m0->m_data += 8; -#endif if (len < RX_COPY_THRESHOLD) { +#ifdef T4_PKT_TIMESTAMP + /* Leave room for a timestamp */ + m0->m_data += 8; +#endif /* copy data to mbuf, buffer will be recycled */ bcopy(sd->cl, mtod(m0, caddr_t), len); m0->m_len = len; } else { bus_dmamap_unload(fl->tag[sd->tag_idx], sd->map); - m_cljset(m0, sd->cl, FL_BUF_TYPE(sd->tag_idx)); + m_cljset(m0, sd->cl, FL_BUF_TYPE(sc, sd->tag_idx)); sd->cl = NULL; /* consumed */ - m0->m_len = min(len, FL_BUF_SIZE(sd->tag_idx)); + m0->m_len = min(len, FL_BUF_SIZE(sc, sd->tag_idx)); } m0->m_pkthdr.len = len; @@ -1207,23 +1622,23 @@ get_fl_payload(struct adapter *sc, struct sge_fl *fl, uint32_t len_newbuf, nbuf = 1; /* # of fl buffers used */ while (len > 0) { - m->m_next = sd->m; - sd->m = NULL; /* consumed */ + /* Can't fail, we checked earlier that the stash was full. */ + m->m_next = get_mbuf_from_stash(fl); m = m->m_next; bus_dmamap_sync(fl->tag[sd->tag_idx], sd->map, BUS_DMASYNC_POSTREAD); + /* m_init for !M_PKTHDR can't fail so don't bother */ m_init(m, NULL, 0, M_NOWAIT, MT_DATA, 0); if (len <= MLEN) { bcopy(sd->cl, mtod(m, caddr_t), len); m->m_len = len; } else { - bus_dmamap_unload(fl->tag[sd->tag_idx], - sd->map); - m_cljset(m, sd->cl, FL_BUF_TYPE(sd->tag_idx)); + bus_dmamap_unload(fl->tag[sd->tag_idx], sd->map); + m_cljset(m, sd->cl, FL_BUF_TYPE(sc, sd->tag_idx)); sd->cl = NULL; /* consumed */ - m->m_len = min(len, FL_BUF_SIZE(sd->tag_idx)); + m->m_len = min(len, FL_BUF_SIZE(sc, sd->tag_idx)); } sd++; @@ -1588,6 +2003,7 @@ void t4_update_fl_bufsize(struct ifnet *ifp) { struct port_info *pi = ifp->if_softc; + struct adapter *sc = pi->adapter; struct sge_rxq *rxq; #ifdef TCP_OFFLOAD struct sge_ofld_rxq *ofld_rxq; @@ -1600,7 +2016,7 @@ t4_update_fl_bufsize(struct ifnet *ifp) fl = &rxq->fl; FL_LOCK(fl); - set_fl_tag_idx(fl, bufsize); + set_fl_tag_idx(sc, fl, bufsize); FL_UNLOCK(fl); } #ifdef TCP_OFFLOAD @@ -1609,7 +2025,7 @@ t4_update_fl_bufsize(struct ifnet *ifp) fl = &ofld_rxq->fl; FL_LOCK(fl); - set_fl_tag_idx(fl, bufsize); + set_fl_tag_idx(sc, fl, bufsize); FL_UNLOCK(fl); } #endif @@ -1643,11 +2059,15 @@ init_iq(struct sge_iq *iq, struct adapter *sc, int tmr_idx, int pktc_idx, } static inline void -init_fl(struct sge_fl *fl, int qsize, int bufsize, char *name) +init_fl(struct adapter *sc, struct sge_fl *fl, int qsize, int bufsize, int pack, + char *name) { + fl->qsize = qsize; strlcpy(fl->lockname, name, sizeof(fl->lockname)); - set_fl_tag_idx(fl, bufsize); + if (pack) + fl->flags |= FL_BUF_PACKING; + set_fl_tag_idx(sc, fl, bufsize); } static inline void @@ -1776,7 +2196,7 @@ alloc_iq_fl(struct port_info *pi, struct sge_iq *iq, struct sge_fl *fl, if (fl) { mtx_init(&fl->fl_lock, fl->lockname, NULL, MTX_DEF); - for (i = 0; i < FL_BUF_SIZES; i++) { + for (i = 0; i < FL_BUF_SIZES(sc); i++) { /* * A freelist buffer must be 16 byte aligned as the SGE @@ -1785,8 +2205,8 @@ alloc_iq_fl(struct port_info *pi, struct sge_iq *iq, struct sge_fl *fl, */ rc = bus_dma_tag_create(sc->dmat, 16, 0, BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR, NULL, NULL, - FL_BUF_SIZE(i), 1, FL_BUF_SIZE(i), BUS_DMA_ALLOCNOW, - NULL, NULL, &fl->tag[i]); + FL_BUF_SIZE(sc, i), 1, FL_BUF_SIZE(sc, i), + BUS_DMA_ALLOCNOW, NULL, NULL, &fl->tag[i]); if (rc != 0) { device_printf(sc->dev, "failed to create fl DMA tag[%d]: %d\n", @@ -1802,9 +2222,7 @@ alloc_iq_fl(struct port_info *pi, struct sge_iq *iq, struct sge_fl *fl, /* Allocate space for one software descriptor per buffer. */ fl->cap = (fl->qsize - spg_len / RX_FL_ESIZE) * 8; - FL_LOCK(fl); rc = alloc_fl_sdesc(fl); - FL_UNLOCK(fl); if (rc != 0) { device_printf(sc->dev, "failed to setup fl software descriptors: %d\n", @@ -1817,7 +2235,9 @@ alloc_iq_fl(struct port_info *pi, struct sge_iq *iq, struct sge_fl *fl, c.iqns_to_fl0congen |= htobe32(V_FW_IQ_CMD_FL0HOSTFCMODE(X_HOSTFCMODE_NONE) | F_FW_IQ_CMD_FL0FETCHRO | F_FW_IQ_CMD_FL0DATARO | - F_FW_IQ_CMD_FL0PADEN); + (fl_pad ? F_FW_IQ_CMD_FL0PADEN : 0) | + (fl->flags & FL_BUF_PACKING ? F_FW_IQ_CMD_FL0PACKEN : + 0)); if (cong >= 0) { c.iqns_to_fl0congen |= htobe32(V_FW_IQ_CMD_FL0CNGCHMAP(cong) | @@ -1937,16 +2357,22 @@ free_iq_fl(struct port_info *pi, struct sge_iq *iq, struct sge_fl *fl) free_ring(sc, fl->desc_tag, fl->desc_map, fl->ba, fl->desc); - if (fl->sdesc) { - FL_LOCK(fl); - free_fl_sdesc(fl); - FL_UNLOCK(fl); + if (fl->sdesc) + free_fl_sdesc(sc, fl); + + for (i = 0; i < nitems(fl->mstash); i++) { + struct mbuf *m = fl->mstash[i]; + + if (m != NULL) { + m_init(m, NULL, 0, M_NOWAIT, MT_DATA, 0); + m_free(m); + } } if (mtx_initialized(&fl->fl_lock)) mtx_destroy(&fl->fl_lock); - for (i = 0; i < FL_BUF_SIZES; i++) { + for (i = 0; i < FL_BUF_SIZES(sc); i++) { if (fl->tag[i]) bus_dma_tag_destroy(fl->tag[i]); } @@ -2107,6 +2533,10 @@ alloc_rxq(struct port_info *pi, struct sge_rxq *rxq, int intr_idx, int idx, "SGE context id of the queue"); SYSCTL_ADD_UINT(&pi->ctx, children, OID_AUTO, "cidx", CTLFLAG_RD, &rxq->fl.cidx, 0, "consumer index"); + if (rxq->fl.flags & FL_BUF_PACKING) { + SYSCTL_ADD_UINT(&pi->ctx, children, OID_AUTO, "rx_offset", + CTLFLAG_RD, &rxq->fl.rx_offset, 0, "packing rx offset"); + } SYSCTL_ADD_UINT(&pi->ctx, children, OID_AUTO, "pidx", CTLFLAG_RD, &rxq->fl.pidx, 0, "producer index"); @@ -2668,6 +3098,12 @@ refill_fl(struct adapter *sc, struct sge_fl *fl, int nbufs) int rc; FL_LOCK_ASSERT_OWNED(fl); +#ifdef INVARIANTS + if (fl->flags & FL_BUF_PACKING) + KASSERT(sd->tag_idx == 0, + ("%s: expected tag 0 but found tag %d at pidx %u instead", + __func__, sd->tag_idx, fl->pidx)); +#endif if (nbufs > fl->needed) nbufs = fl->needed; @@ -2676,24 +3112,34 @@ refill_fl(struct adapter *sc, struct sge_fl *fl, int nbufs) if (sd->cl != NULL) { - /* - * This happens when a frame small enough to fit - * entirely in an mbuf was received in cl last time. - * We'd held on to cl and can reuse it now. Note that - * we reuse a cluster of the old size if fl->tag_idx is - * no longer the same as sd->tag_idx. - */ - - KASSERT(*d == sd->ba_tag, + KASSERT(*d == sd->ba_hwtag, ("%s: recyling problem at pidx %d", __func__, fl->pidx)); - d++; - goto recycled; + if (fl->flags & FL_BUF_PACKING) { + u_int *refcount = find_buf_refcnt(sd->cl); + + if (atomic_fetchadd_int(refcount, -1) == 1) { + *refcount = 1; /* reinstate */ + d++; + goto recycled; + } + sd->cl = NULL; /* gave up my reference */ + } else { + /* + * This happens when a frame small enough to fit + * entirely in an mbuf was received in cl last + * time. We'd held on to cl and can reuse it + * now. Note that we reuse a cluster of the old + * size if fl->tag_idx is no longer the same as + * sd->tag_idx. + */ + d++; + goto recycled; + } } - - if (fl->tag_idx != sd->tag_idx) { + if (__predict_false(fl->tag_idx != sd->tag_idx)) { bus_dmamap_t map; bus_dma_tag_t newtag = fl->tag[fl->tag_idx]; bus_dma_tag_t oldtag = fl->tag[sd->tag_idx]; @@ -2713,33 +3159,32 @@ refill_fl(struct adapter *sc, struct sge_fl *fl, int nbufs) tag = fl->tag[sd->tag_idx]; - cl = m_cljget(NULL, M_NOWAIT, FL_BUF_SIZE(sd->tag_idx)); + cl = uma_zalloc(FL_BUF_ZONE(sc, sd->tag_idx), M_NOWAIT); if (cl == NULL) break; + if (fl->flags & FL_BUF_PACKING) { + *find_buf_refcnt(cl) = 1; + cl += MSIZE; + } - rc = bus_dmamap_load(tag, sd->map, cl, FL_BUF_SIZE(sd->tag_idx), - oneseg_dma_callback, &pa, 0); + rc = bus_dmamap_load(tag, sd->map, cl, + FL_BUF_SIZE(sc, sd->tag_idx), oneseg_dma_callback, &pa, 0); if (rc != 0 || pa == 0) { fl->dmamap_failed++; - uma_zfree(FL_BUF_ZONE(sd->tag_idx), cl); + if (fl->flags & FL_BUF_PACKING) + cl -= MSIZE; + uma_zfree(FL_BUF_ZONE(sc, sd->tag_idx), cl); break; } sd->cl = cl; - *d++ = htobe64(pa | sd->tag_idx); + *d++ = htobe64(pa | FL_BUF_HWTAG(sc, sd->tag_idx)); #ifdef INVARIANTS - sd->ba_tag = htobe64(pa | sd->tag_idx); + sd->ba_hwtag = htobe64(pa | FL_BUF_HWTAG(sc, sd->tag_idx)); #endif recycled: - /* sd->m is never recycled, should always be NULL */ - KASSERT(sd->m == NULL, ("%s: stray mbuf", __func__)); - - sd->m = m_gethdr(M_NOWAIT, MT_NOINIT); - if (sd->m == NULL) - break; - fl->pending++; fl->needed--; sd++; @@ -2788,8 +3233,6 @@ alloc_fl_sdesc(struct sge_fl *fl) bus_dma_tag_t tag; int i, rc; - FL_LOCK_ASSERT_OWNED(fl); - fl->sdesc = malloc(fl->cap * sizeof(struct fl_sdesc), M_CXGBE, M_ZERO | M_WAITOK); @@ -2808,11 +3251,6 @@ failed: while (--i >= 0) { sd--; bus_dmamap_destroy(tag, sd->map); - if (sd->m) { - m_init(sd->m, NULL, 0, M_NOWAIT, MT_DATA, 0); - m_free(sd->m); - sd->m = NULL; - } } KASSERT(sd == fl->sdesc, ("%s: EDOOFUS", __func__)); @@ -2823,25 +3261,17 @@ failed: } static void -free_fl_sdesc(struct sge_fl *fl) +free_fl_sdesc(struct adapter *sc, struct sge_fl *fl) { struct fl_sdesc *sd; int i; - FL_LOCK_ASSERT_OWNED(fl); - sd = fl->sdesc; for (i = 0; i < fl->cap; i++, sd++) { - if (sd->m) { - m_init(sd->m, NULL, 0, M_NOWAIT, MT_DATA, 0); - m_free(sd->m); - sd->m = NULL; - } - if (sd->cl) { bus_dmamap_unload(fl->tag[sd->tag_idx], sd->map); - uma_zfree(FL_BUF_ZONE(sd->tag_idx), sd->cl); + uma_zfree(FL_BUF_ZONE(sc, sd->tag_idx), sd->cl); sd->cl = NULL; } @@ -3696,17 +4126,52 @@ get_flit(bus_dma_segment_t *sgl, int nsegs, int idx) return (0); } +/* + * Find an SGE FL buffer size to use for the given bufsize. Look for the the + * smallest size that is large enough to hold bufsize or pick the largest size + * if all sizes are less than bufsize. + */ static void -set_fl_tag_idx(struct sge_fl *fl, int bufsize) +set_fl_tag_idx(struct adapter *sc, struct sge_fl *fl, int bufsize) { - int i; + int i, largest, best, delta, start; - for (i = 0; i < FL_BUF_SIZES - 1; i++) { - if (FL_BUF_SIZE(i) >= bufsize) - break; + if (fl->flags & FL_BUF_PACKING) { + fl->tag_idx = 0; /* first tag is the one for packing */ + return; } - fl->tag_idx = i; + start = sc->flags & BUF_PACKING_OK ? 1 : 0; + delta = FL_BUF_SIZE(sc, start) - bufsize; + if (delta == 0) { + fl->tag_idx = start; /* ideal fit, look no further */ + return; + } + best = start; + largest = start; + + for (i = start + 1; i < FL_BUF_SIZES(sc); i++) { + int d, fl_buf_size; + + fl_buf_size = FL_BUF_SIZE(sc, i); + d = fl_buf_size - bufsize; + + if (d == 0) { + fl->tag_idx = i; /* ideal fit, look no further */ + return; + } + if (fl_buf_size > FL_BUF_SIZE(sc, largest)) + largest = i; + if (d > 0 && (delta < 0 || delta > d)) { + delta = d; + best = i; + } + } + + if (delta > 0) + fl->tag_idx = best; /* Found a buf bigger than bufsize */ + else + fl->tag_idx = largest; /* No buf large enough for bufsize */ } static void diff --git a/sys/dev/cxgbe/t4_tracer.c b/sys/dev/cxgbe/t4_tracer.c index 04f64e3c01e..e9727f5f1fe 100644 --- a/sys/dev/cxgbe/t4_tracer.c +++ b/sys/dev/cxgbe/t4_tracer.c @@ -121,14 +121,13 @@ match_name(struct adapter *sc, void *arg) static int t4_cloner_match(struct if_clone *ifc, const char *name) { - struct match_rr mrr; - mrr.name = name; - mrr.lock = 0; - mrr.sc = NULL; - t4_iterate(match_name, &mrr); - - return (mrr.sc != NULL); + if (strncmp(name, "t4nex", 5) != 0 && + strncmp(name, "t5nex", 5) != 0) + return (0); + if (name[5] < '0' || name[5] > '9') + return (0); + return (1); } static int @@ -466,7 +465,7 @@ tracer_ioctl(struct ifnet *ifp, unsigned long cmd, caddr_t data) switch (cmd) { case SIOCSIFMTU: case SIOCSIFFLAGS: - case SIOCADDMULTI: + case SIOCADDMULTI: case SIOCDELMULTI: case SIOCSIFCAP: break; diff --git a/sys/dev/cxgbe/tom/t4_cpl_io.c b/sys/dev/cxgbe/tom/t4_cpl_io.c index 8bb90f5e6d3..2409b5d33cf 100644 --- a/sys/dev/cxgbe/tom/t4_cpl_io.c +++ b/sys/dev/cxgbe/tom/t4_cpl_io.c @@ -444,22 +444,12 @@ max_dsgl_nsegs(int tx_credits) static inline void write_tx_wr(void *dst, struct toepcb *toep, unsigned int immdlen, - unsigned int plen, uint8_t credits, int more_to_come) + unsigned int plen, uint8_t credits, int shove) { struct fw_ofld_tx_data_wr *txwr = dst; - int shove = !more_to_come; - int compl = 1; - - /* - * We always request completion notifications from the firmware. The - * only exception is when we know we'll get more data to send shortly - * and that we'll have some tx credits remaining to transmit that data. - */ - if (more_to_come && toep->tx_credits - credits >= MIN_OFLD_TX_CREDITS) - compl = 0; txwr->op_to_immdlen = htobe32(V_WR_OP(FW_OFLD_TX_DATA_WR) | - V_FW_WR_COMPL(compl) | V_FW_WR_IMMDLEN(immdlen)); + V_FW_WR_IMMDLEN(immdlen)); txwr->flowid_len16 = htobe32(V_FW_WR_FLOWID(toep->tid) | V_FW_WR_LEN16(credits)); txwr->tunnel_to_proxy = @@ -529,35 +519,46 @@ write_tx_sgl(void *dst, struct mbuf *start, struct mbuf *stop, int nsegs, int n) * The socket's so_snd buffer consists of a stream of data starting with sb_mb * and linked together with m_next. sb_sndptr, if set, is the last mbuf that * was transmitted. + * + * drop indicates the number of bytes that should be dropped from the head of + * the send buffer. It is an optimization that lets do_fw4_ack avoid creating + * contention on the send buffer lock (before this change it used to do + * sowwakeup and then t4_push_frames right after that when recovering from tx + * stalls). When drop is set this function MUST drop the bytes and wake up any + * writers. */ static void -t4_push_frames(struct adapter *sc, struct toepcb *toep) +t4_push_frames(struct adapter *sc, struct toepcb *toep, int drop) { struct mbuf *sndptr, *m, *sb_sndptr; struct fw_ofld_tx_data_wr *txwr; struct wrqe *wr; - unsigned int plen, nsegs, credits, max_imm, max_nsegs, max_nsegs_1mbuf; + u_int plen, nsegs, credits, max_imm, max_nsegs, max_nsegs_1mbuf; struct inpcb *inp = toep->inp; struct tcpcb *tp = intotcpcb(inp); struct socket *so = inp->inp_socket; struct sockbuf *sb = &so->so_snd; - int tx_credits; + int tx_credits, shove, compl, space, sowwakeup; struct ofld_tx_sdesc *txsd = &toep->txsd[toep->txsd_pidx]; INP_WLOCK_ASSERT(inp); KASSERT(toep->flags & TPF_FLOWC_WR_SENT, ("%s: flowc_wr not sent for tid %u.", __func__, toep->tid)); - if (__predict_false(toep->ulp_mode != ULP_MODE_NONE && - toep->ulp_mode != ULP_MODE_TCPDDP)) - CXGBE_UNIMPLEMENTED("ulp_mode"); + KASSERT(toep->ulp_mode == ULP_MODE_NONE || + toep->ulp_mode == ULP_MODE_TCPDDP || + toep->ulp_mode == ULP_MODE_RDMA, + ("%s: ulp_mode %u for toep %p", __func__, toep->ulp_mode, toep)); /* * This function doesn't resume by itself. Someone else must clear the * flag and call this function. */ - if (__predict_false(toep->flags & TPF_TX_SUSPENDED)) + if (__predict_false(toep->flags & TPF_TX_SUSPENDED)) { + KASSERT(drop == 0, + ("%s: drop (%d) != 0 but tx is suspended", __func__, drop)); return; + } do { tx_credits = min(toep->tx_credits, MAX_OFLD_TX_CREDITS); @@ -565,6 +566,11 @@ t4_push_frames(struct adapter *sc, struct toepcb *toep) max_nsegs = max_dsgl_nsegs(tx_credits); SOCKBUF_LOCK(sb); + sowwakeup = drop; + if (drop) { + sbdrop_locked(sb, drop); + drop = 0; + } sb_sndptr = sb->sb_sndptr; sndptr = sb_sndptr ? sb_sndptr->m_next : sb->sb_mb; plen = 0; @@ -583,7 +589,11 @@ t4_push_frames(struct adapter *sc, struct toepcb *toep) if (plen == 0) { /* Too few credits */ toep->flags |= TPF_TX_SUSPENDED; - SOCKBUF_UNLOCK(sb); + if (sowwakeup) + sowwakeup_locked(so); + else + SOCKBUF_UNLOCK(sb); + SOCKBUF_UNLOCK_ASSERT(sb); return; } break; @@ -600,23 +610,32 @@ t4_push_frames(struct adapter *sc, struct toepcb *toep) } } + shove = m == NULL && !(tp->t_flags & TF_MORETOCOME); + space = sbspace(sb); + + if (space <= sb->sb_hiwat * 3 / 8 && + toep->plen_nocompl + plen >= sb->sb_hiwat / 4) + compl = 1; + else + compl = 0; + if (sb->sb_flags & SB_AUTOSIZE && V_tcp_do_autosndbuf && sb->sb_hiwat < V_tcp_autosndbuf_max && - sbspace(sb) < sb->sb_hiwat / 8 * 7) { + space < sb->sb_hiwat / 8) { int newsize = min(sb->sb_hiwat + V_tcp_autosndbuf_inc, V_tcp_autosndbuf_max); if (!sbreserve_locked(sb, newsize, so, NULL)) sb->sb_flags &= ~SB_AUTOSIZE; - else { - sowwakeup_locked(so); /* room available */ - SOCKBUF_UNLOCK_ASSERT(sb); - goto unlocked; - } + else + sowwakeup = 1; /* room available */ } - SOCKBUF_UNLOCK(sb); -unlocked: + if (sowwakeup) + sowwakeup_locked(so); + else + SOCKBUF_UNLOCK(sb); + SOCKBUF_UNLOCK_ASSERT(sb); /* nothing to send */ if (plen == 0) { @@ -641,9 +660,9 @@ unlocked: } txwr = wrtod(wr); credits = howmany(wr->wr_len, 16); - write_tx_wr(txwr, toep, plen, plen, credits, - tp->t_flags & TF_MORETOCOME); + write_tx_wr(txwr, toep, plen, plen, credits, shove); m_copydata(sndptr, 0, plen, (void *)(txwr + 1)); + nsegs = 0; } else { int wr_len; @@ -659,8 +678,7 @@ unlocked: } txwr = wrtod(wr); credits = howmany(wr_len, 16); - write_tx_wr(txwr, toep, 0, plen, credits, - tp->t_flags & TF_MORETOCOME); + write_tx_wr(txwr, toep, 0, plen, credits, shove); write_tx_sgl(txwr + 1, sndptr, m, nsegs, max_nsegs_1mbuf); if (wr_len & 0xf) { @@ -674,6 +692,17 @@ unlocked: ("%s: not enough credits", __func__)); toep->tx_credits -= credits; + toep->tx_nocompl += credits; + toep->plen_nocompl += plen; + if (toep->tx_credits <= toep->tx_total * 3 / 8 && + toep->tx_nocompl >= toep->tx_total / 4) + compl = 1; + + if (compl) { + txwr->op_to_immdlen |= htobe32(F_FW_WR_COMPL); + toep->tx_nocompl = 0; + toep->plen_nocompl = 0; + } tp->snd_nxt += plen; tp->snd_max += plen; @@ -684,6 +713,8 @@ unlocked: SOCKBUF_UNLOCK(sb); toep->flags |= TPF_TX_DATA_SENT; + if (toep->tx_credits < MIN_OFLD_TX_CREDITS) + toep->flags |= TPF_TX_SUSPENDED; KASSERT(toep->txsd_avail > 0, ("%s: no txsd", __func__)); txsd->plen = plen; @@ -717,7 +748,7 @@ t4_tod_output(struct toedev *tod, struct tcpcb *tp) ("%s: inp %p dropped.", __func__, inp)); KASSERT(toep != NULL, ("%s: toep is NULL", __func__)); - t4_push_frames(sc, toep); + t4_push_frames(sc, toep, 0); return (0); } @@ -737,7 +768,8 @@ t4_send_fin(struct toedev *tod, struct tcpcb *tp) KASSERT(toep != NULL, ("%s: toep is NULL", __func__)); toep->flags |= TPF_SEND_FIN; - t4_push_frames(sc, toep); + if (tp->t_state >= TCPS_ESTABLISHED) + t4_push_frames(sc, toep, 0); return (0); } @@ -843,9 +875,11 @@ do_peer_close(struct sge_iq *iq, const struct rss_header *rss, struct mbuf *m) } socantrcvmore_locked(so); /* unlocks the sockbuf */ - KASSERT(tp->rcv_nxt == be32toh(cpl->rcv_nxt), - ("%s: rcv_nxt mismatch: %u %u", __func__, tp->rcv_nxt, - be32toh(cpl->rcv_nxt))); + if (toep->ulp_mode != ULP_MODE_RDMA) { + KASSERT(tp->rcv_nxt == be32toh(cpl->rcv_nxt), + ("%s: rcv_nxt mismatch: %u %u", __func__, tp->rcv_nxt, + be32toh(cpl->rcv_nxt))); + } switch (tp->t_state) { case TCPS_SYN_RECEIVED: @@ -1366,7 +1400,16 @@ do_fw4_ack(struct sge_iq *iq, const struct rss_header *rss, struct mbuf *m) } } - if (plen > 0) { + if (toep->tx_credits == toep->tx_total) { + toep->tx_nocompl = 0; + toep->plen_nocompl = 0; + } + + if (toep->flags & TPF_TX_SUSPENDED && + toep->tx_credits >= toep->tx_total / 4) { + toep->flags &= ~TPF_TX_SUSPENDED; + t4_push_frames(sc, toep, plen); + } else if (plen > 0) { struct sockbuf *sb = &so->so_snd; SOCKBUF_LOCK(sb); @@ -1375,14 +1418,6 @@ do_fw4_ack(struct sge_iq *iq, const struct rss_header *rss, struct mbuf *m) SOCKBUF_UNLOCK_ASSERT(sb); } - /* XXX */ - if ((toep->flags & TPF_TX_SUSPENDED && - toep->tx_credits >= MIN_OFLD_TX_CREDITS) || - toep->tx_credits == toep->txsd_total * - howmany((sizeof(struct fw_ofld_tx_data_wr) + 1), 16)) { - toep->flags &= ~TPF_TX_SUSPENDED; - t4_push_frames(sc, toep); - } INP_WUNLOCK(inp); return (0); diff --git a/sys/dev/cxgbe/tom/t4_listen.c b/sys/dev/cxgbe/tom/t4_listen.c index 9e1dc8041d5..17f4adb65dc 100644 --- a/sys/dev/cxgbe/tom/t4_listen.c +++ b/sys/dev/cxgbe/tom/t4_listen.c @@ -1007,7 +1007,7 @@ calc_opt2p(struct adapter *sc, struct port_info *pi, int rxqid, opt2 |= F_TSTAMPS_EN; if (tcpopt->sack) opt2 |= F_SACK_EN; - if (tcpopt->wsf > 0) + if (tcpopt->wsf <= 14) opt2 |= F_WND_SCALE_EN; } diff --git a/sys/dev/cxgbe/tom/t4_tom.c b/sys/dev/cxgbe/tom/t4_tom.c index 671174d36d9..8c787a75e3f 100644 --- a/sys/dev/cxgbe/tom/t4_tom.c +++ b/sys/dev/cxgbe/tom/t4_tom.c @@ -148,6 +148,7 @@ alloc_toepcb(struct port_info *pi, int txqid, int rxqid, int flags) toep->td = sc->tom_softc; toep->port = pi; + toep->tx_total = tx_credits; toep->tx_credits = tx_credits; toep->ofld_txq = &sc->sge.ofld_txq[txqid]; toep->ofld_rxq = &sc->sge.ofld_rxq[rxqid]; @@ -1064,14 +1065,14 @@ t4_tom_mod_load(void) static void tom_uninit(struct adapter *sc, void *arg __unused) { - if (begin_synchronized_op(sc, NULL, HOLD_LOCK, "t4tomun")) + if (begin_synchronized_op(sc, NULL, SLEEP_OK | INTR_OK, "t4tomun")) return; /* Try to free resources (works only if no port has IFCAP_TOE) */ if (sc->flags & TOM_INIT_DONE) t4_deactivate_uld(sc, ULD_TOM); - end_synchronized_op(sc, LOCK_HELD); + end_synchronized_op(sc, 0); } static int diff --git a/sys/dev/cxgbe/tom/t4_tom.h b/sys/dev/cxgbe/tom/t4_tom.h index b86a76ce28f..927bc94e1e0 100644 --- a/sys/dev/cxgbe/tom/t4_tom.h +++ b/sys/dev/cxgbe/tom/t4_tom.h @@ -99,7 +99,7 @@ struct ddp_buffer { struct toepcb { TAILQ_ENTRY(toepcb) link; /* toep_list */ - unsigned int flags; /* miscellaneous flags */ + u_int flags; /* miscellaneous flags */ struct tom_data *td; struct inpcb *inp; /* backpointer to host stack's PCB */ struct port_info *port; /* physical port */ @@ -109,13 +109,20 @@ struct toepcb { struct l2t_entry *l2te; /* L2 table entry used by this connection */ struct clip_entry *ce; /* CLIP table entry used by this tid */ int tid; /* Connection identifier */ - unsigned int tx_credits;/* tx WR credits (in 16 byte units) remaining */ - unsigned int sb_cc; /* last noted value of so_rcv->sb_cc */ + + /* tx credit handling */ + u_int tx_total; /* total tx WR credits (in 16B units) */ + u_int tx_credits; /* tx WR credits (in 16B units) available */ + u_int tx_nocompl; /* tx WR credits since last compl request */ + u_int plen_nocompl; /* payload since last compl request */ + + /* rx credit handling */ + u_int sb_cc; /* last noted value of so_rcv->sb_cc */ int rx_credits; /* rx credits (in bytes) to be returned to hw */ - unsigned int ulp_mode; /* ULP mode */ + u_int ulp_mode; /* ULP mode */ - unsigned int ddp_flags; + u_int ddp_flags; struct ddp_buffer *db[2]; time_t ddp_disabled; uint8_t ddp_score; diff --git a/sys/dev/dpt/dpt_pci.c b/sys/dev/dpt/dpt_pci.c index 150d5cd02a8..f547a221415 100644 --- a/sys/dev/dpt/dpt_pci.c +++ b/sys/dev/dpt/dpt_pci.c @@ -77,23 +77,17 @@ dpt_pci_attach (device_t dev) dpt_softc_t * dpt; int error = 0; - u_int32_t command; - dpt = device_get_softc(dev); dpt->dev = dev; dpt_alloc(dev); - command = pci_read_config(dev, PCIR_COMMAND, /*bytes*/1); - #ifdef DPT_ALLOW_MMIO - if ((command & PCIM_CMD_MEMEN) != 0) { - dpt->io_rid = DPT_PCI_MEMADDR; - dpt->io_type = SYS_RES_MEMORY; - dpt->io_res = bus_alloc_resource_any(dev, dpt->io_type, - &dpt->io_rid, RF_ACTIVE); - } + dpt->io_rid = DPT_PCI_MEMADDR; + dpt->io_type = SYS_RES_MEMORY; + dpt->io_res = bus_alloc_resource_any(dev, dpt->io_type, + &dpt->io_rid, RF_ACTIVE); #endif - if (dpt->io_res == NULL && (command & PCIM_CMD_PORTEN) != 0) { + if (dpt->io_res == NULL) { dpt->io_rid = DPT_PCI_IOADDR; dpt->io_type = SYS_RES_IOPORT; dpt->io_res = bus_alloc_resource_any(dev, dpt->io_type, diff --git a/sys/dev/drm/drm_scatter.c b/sys/dev/drm/drm_scatter.c index 9a1a4b1e1d3..1a786166372 100644 --- a/sys/dev/drm/drm_scatter.c +++ b/sys/dev/drm/drm_scatter.c @@ -52,7 +52,7 @@ drm_sg_alloc(struct drm_device *dev, struct drm_scatter_gather *request) entry->busaddr = malloc(entry->pages * sizeof(*entry->busaddr), DRM_MEM_SGLISTS, M_WAITOK | M_ZERO); - entry->vaddr = kmem_alloc_attr(kernel_map, size, M_WAITOK | M_ZERO, + entry->vaddr = kmem_alloc_attr(kernel_arena, size, M_WAITOK | M_ZERO, 0, BUS_SPACE_MAXADDR_32BIT, VM_MEMATTR_WRITE_COMBINING); if (entry->vaddr == 0) { drm_sg_cleanup(entry); @@ -99,7 +99,7 @@ drm_sg_cleanup(struct drm_sg_mem *entry) return; if (entry->vaddr != 0) - kmem_free(kernel_map, entry->vaddr, IDX_TO_OFF(entry->pages)); + kmem_free(kernel_arena, entry->vaddr, IDX_TO_OFF(entry->pages)); free(entry->busaddr, DRM_MEM_SGLISTS); free(entry, DRM_MEM_DRIVER); diff --git a/sys/dev/drm2/ati_pcigart.c b/sys/dev/drm2/ati_pcigart.c new file mode 100644 index 00000000000..5d3033fc37c --- /dev/null +++ b/sys/dev/drm2/ati_pcigart.c @@ -0,0 +1,210 @@ +/** + * \file ati_pcigart.c + * ATI PCI GART support + * + * \author Gareth Hughes + */ + +/* + * Created: Wed Dec 13 21:52:19 2000 by gareth@valinux.com + * + * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. + * All Rights Reserved. + * + * 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 (including the next + * paragraph) 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 + * PRECISION INSIGHT AND/OR ITS SUPPLIERS 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 +__FBSDID("$FreeBSD$"); + +#include + +# define ATI_PCIGART_PAGE_SIZE 4096 /**< PCI GART page size */ + +static int drm_ati_alloc_pcigart_table(struct drm_device *dev, + struct drm_ati_pcigart_info *gart_info) +{ + gart_info->table_handle = drm_pci_alloc(dev, gart_info->table_size, + PAGE_SIZE, 0xFFFFFFFFUL); + if (gart_info->table_handle == NULL) + return -ENOMEM; + + return 0; +} + +static void drm_ati_free_pcigart_table(struct drm_device *dev, + struct drm_ati_pcigart_info *gart_info) +{ + drm_pci_free(dev, gart_info->table_handle); + gart_info->table_handle = NULL; +} + +int drm_ati_pcigart_cleanup(struct drm_device *dev, struct drm_ati_pcigart_info *gart_info) +{ + struct drm_sg_mem *entry = dev->sg; +#ifdef __linux__ + unsigned long pages; + int i; + int max_pages; +#endif + + /* we need to support large memory configurations */ + if (!entry) { + DRM_ERROR("no scatter/gather memory!\n"); + return 0; + } + + if (gart_info->bus_addr) { +#ifdef __linux__ + + max_pages = (gart_info->table_size / sizeof(u32)); + pages = (entry->pages <= max_pages) + ? entry->pages : max_pages; + + for (i = 0; i < pages; i++) { + if (!entry->busaddr[i]) + break; + pci_unmap_page(dev->pdev, entry->busaddr[i], + PAGE_SIZE, PCI_DMA_BIDIRECTIONAL); + } +#endif + + if (gart_info->gart_table_location == DRM_ATI_GART_MAIN) + gart_info->bus_addr = 0; + } + + if (gart_info->gart_table_location == DRM_ATI_GART_MAIN && + gart_info->table_handle) { + drm_ati_free_pcigart_table(dev, gart_info); + } + + return 1; +} + +int drm_ati_pcigart_init(struct drm_device *dev, struct drm_ati_pcigart_info *gart_info) +{ + struct drm_local_map *map = &gart_info->mapping; + struct drm_sg_mem *entry = dev->sg; + void *address = NULL; + unsigned long pages; + u32 *pci_gart = NULL, page_base, gart_idx; + dma_addr_t bus_address = 0; + int i, j, ret = 0; + int max_ati_pages, max_real_pages; + + if (!entry) { + DRM_ERROR("no scatter/gather memory!\n"); + goto done; + } + + if (gart_info->gart_table_location == DRM_ATI_GART_MAIN) { + DRM_DEBUG("PCI: no table in VRAM: using normal RAM\n"); + +#ifdef __linux__ + if (pci_set_dma_mask(dev->pdev, gart_info->table_mask)) { + DRM_ERROR("fail to set dma mask to 0x%Lx\n", + (unsigned long long)gart_info->table_mask); + ret = 1; + goto done; + } +#endif + + ret = drm_ati_alloc_pcigart_table(dev, gart_info); + if (ret) { + DRM_ERROR("cannot allocate PCI GART page!\n"); + goto done; + } + + pci_gart = gart_info->table_handle->vaddr; + address = gart_info->table_handle->vaddr; + bus_address = gart_info->table_handle->busaddr; + } else { + address = gart_info->addr; + bus_address = gart_info->bus_addr; + DRM_DEBUG("PCI: Gart Table: VRAM %08LX mapped at %08lX\n", + (unsigned long long)bus_address, + (unsigned long)address); + } + + + max_ati_pages = (gart_info->table_size / sizeof(u32)); + max_real_pages = max_ati_pages / (PAGE_SIZE / ATI_PCIGART_PAGE_SIZE); + pages = (entry->pages <= max_real_pages) + ? entry->pages : max_real_pages; + + if (gart_info->gart_table_location == DRM_ATI_GART_MAIN) { + memset(pci_gart, 0, max_ati_pages * sizeof(u32)); + } else { + memset_io((void __iomem *)map->handle, 0, max_ati_pages * sizeof(u32)); + } + + gart_idx = 0; + for (i = 0; i < pages; i++) { +#ifdef __linux__ + /* we need to support large memory configurations */ + entry->busaddr[i] = pci_map_page(dev->pdev, entry->pagelist[i], + 0, PAGE_SIZE, PCI_DMA_BIDIRECTIONAL); + if (pci_dma_mapping_error(dev->pdev, entry->busaddr[i])) { + DRM_ERROR("unable to map PCIGART pages!\n"); + drm_ati_pcigart_cleanup(dev, gart_info); + address = NULL; + bus_address = 0; + goto done; + } +#endif + page_base = (u32) entry->busaddr[i]; + + for (j = 0; j < (PAGE_SIZE / ATI_PCIGART_PAGE_SIZE); j++) { + u32 val; + + switch(gart_info->gart_reg_if) { + case DRM_ATI_GART_IGP: + val = page_base | 0xc; + break; + case DRM_ATI_GART_PCIE: + val = (page_base >> 8) | 0xc; + break; + default: + case DRM_ATI_GART_PCI: + val = page_base; + break; + } + if (gart_info->gart_table_location == + DRM_ATI_GART_MAIN) + pci_gart[gart_idx] = cpu_to_le32(val); + else + DRM_WRITE32(map, gart_idx * sizeof(u32), val); + gart_idx++; + page_base += ATI_PCIGART_PAGE_SIZE; + } + } + ret = 1; + +#if defined(__i386) || defined(__amd64) + wbinvd(); +#else + mb(); +#endif + + done: + gart_info->addr = address; + gart_info->bus_addr = bus_address; + return ret; +} diff --git a/sys/dev/drm2/drmP.h b/sys/dev/drm2/drmP.h index 8a456f1eb2c..25765c4902b 100644 --- a/sys/dev/drm2/drmP.h +++ b/sys/dev/drm2/drmP.h @@ -1,9 +1,15 @@ -/* drmP.h -- Private header for Direct Rendering Manager -*- linux-c -*- - * Created: Mon Jan 4 10:05:05 1999 by faith@precisioninsight.com +/** + * \file drmP.h + * Private header for Direct Rendering Manager + * + * \author Rickard E. (Rik) Faith + * \author Gareth Hughes */ -/*- + +/* * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. + * Copyright (c) 2009-2010, Code Aurora Forum. * All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a @@ -24,11 +30,6 @@ * 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. - * - * Authors: - * Rickard E. (Rik) Faith - * Gareth Hughes - * */ #include @@ -39,9 +40,6 @@ __FBSDID("$FreeBSD$"); #if defined(_KERNEL) || defined(__KERNEL__) -struct drm_device; -struct drm_file; - #include #include #include @@ -99,12 +97,19 @@ struct drm_file; #include #include +#include + #include #include #include #include -#include + +struct drm_file; +struct drm_device; + +#include #include +#include #include "opt_compat.h" #include "opt_drm.h" @@ -120,6 +125,10 @@ struct drm_file; #undef DRM_LINUX #define DRM_LINUX 0 +/***********************************************************************/ +/** \name DRM template customization defaults */ +/*@{*/ + /* driver capabilities and requirements mask */ #define DRIVER_USE_AGP 0x1 #define DRIVER_REQUIRE_AGP 0x2 @@ -135,8 +144,8 @@ struct drm_file; #define DRIVER_IRQ_VBL2 0x800 #define DRIVER_GEM 0x1000 #define DRIVER_MODESET 0x2000 -#define DRIVER_USE_PLATFORM_DEVICE 0x4000 -#define DRIVER_LOCKLESS_IRQ 0x8000 +#define DRIVER_PRIME 0x4000 +#define DRIVER_LOCKLESS_IRQ 0x8000 #define DRM_HASH_SIZE 16 /* Size of key hash table */ @@ -177,7 +186,10 @@ SYSCTL_DECL(_hw_drm); #define DRM_MAX_CTXBITMAP (PAGE_SIZE * 8) - /* Internal types and structures */ +/***********************************************************************/ +/** \name Internal types and structures */ +/*@{*/ + #define DRM_ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0])) #define DRM_MIN(a,b) ((a)<(b)?(a):(b)) #define DRM_MAX(a,b) ((a)>(b)?(a):(b)) @@ -228,12 +240,6 @@ typedef void irqreturn_t; #define IRQ_HANDLED /* nothing */ #define IRQ_NONE /* nothing */ -#define unlikely(x) __builtin_expect(!!(x), 0) -#define likely(x) __builtin_expect(!!(x), 1) -#define container_of(ptr, type, member) ({ \ - __typeof( ((type *)0)->member ) *__mptr = (ptr); \ - (type *)( (char *)__mptr - offsetof(type,member) );}) - enum { DRM_IS_NOT_AGP, DRM_IS_AGP, @@ -255,16 +261,6 @@ enum { #define time_after_eq(a,b) ((long)(b) - (long)(a) <= 0) #define drm_msleep(x, msg) pause((msg), ((int64_t)(x)) * hz / 1000) -typedef vm_paddr_t dma_addr_t; -typedef uint64_t u64; -typedef uint32_t u32; -typedef uint16_t u16; -typedef uint8_t u8; -typedef int64_t s64; -typedef int32_t s32; -typedef int16_t s16; -typedef int8_t s8; - /* DRM_READMEMORYBARRIER() prevents reordering of reads. * DRM_WRITEMEMORYBARRIER() prevents reordering of writes. * DRM_MEMORYBARRIER() prevents reordering of reads and writes. @@ -312,13 +308,6 @@ typedef int8_t s8; #define DRM_GET_USER_UNCHECKED(val, uaddr) \ ((val) = fuword32(uaddr), 0) -#define cpu_to_le32(x) htole32(x) -#define le32_to_cpu(x) le32toh(x) - -#define DRM_HZ hz -#define DRM_UDELAY(udelay) DELAY(udelay) -#define DRM_TIME_SLICE (hz/20) /* Time slice for GLXContexts */ - #define DRM_GET_PRIV_SAREA(_dev, _ctx, _map) do { \ (_map) = (_dev)->context_sareas[_ctx]; \ } while(0) @@ -369,6 +358,18 @@ for ( ret = 0 ; !ret && !(condition) ; ) { \ __func__ , ##__VA_ARGS__); \ } while (0) +#define dev_err(dev, fmt, ...) \ + device_printf((dev), "error: " fmt, ## __VA_ARGS__) +#define dev_warn(dev, fmt, ...) \ + device_printf((dev), "warning: " fmt, ## __VA_ARGS__) +#define dev_info(dev, fmt, ...) \ + device_printf((dev), "info: " fmt, ## __VA_ARGS__) +#define dev_dbg(dev, fmt, ...) do { \ + if ((drm_debug_flag& DRM_DEBUGBITS_KMS) != 0) { \ + device_printf((dev), "debug: " fmt, ## __VA_ARGS__); \ + } \ +} while (0) + typedef struct drm_pci_id_list { int vendor; @@ -384,7 +385,7 @@ struct drm_msi_blacklist_entry }; #define DRM_AUTH 0x1 -#define DRM_MASTER 0x2 +#define DRM_MASTER 0x2 #define DRM_ROOT_ONLY 0x4 #define DRM_CONTROL_ALLOW 0x8 #define DRM_UNLOCKED 0x10 @@ -394,7 +395,9 @@ typedef struct drm_ioctl_desc { int (*func)(struct drm_device *dev, void *data, struct drm_file *file_priv); int flags; + unsigned int cmd_drv; } drm_ioctl_desc_t; + /** * Creates a driver or general drm_ioctl_desc array entry for the given * ioctl, for use by drm_ioctl(). @@ -402,6 +405,9 @@ typedef struct drm_ioctl_desc { #define DRM_IOCTL_DEF(ioctl, func, flags) \ [DRM_IOCTL_NR(ioctl)] = {ioctl, func, flags} +#define DRM_IOCTL_DEF_DRV(ioctl, _func, _flags) \ + [DRM_IOCTL_NR(DRM_##ioctl)] = {.cmd = DRM_##ioctl, .func = _func, .flags = _flags, .cmd_drv = DRM_IOCTL_##ioctl} + typedef struct drm_magic_entry { drm_magic_t magic; struct drm_file *priv; @@ -414,28 +420,30 @@ typedef struct drm_magic_head { } drm_magic_head_t; typedef struct drm_buf { - int idx; /* Index into master buflist */ - int total; /* Buffer size */ - int order; /* log-base-2(total) */ - int used; /* Amount of buffer in use (for DMA) */ - unsigned long offset; /* Byte offset (used internally) */ - void *address; /* Address of buffer */ - unsigned long bus_address; /* Bus address of buffer */ - struct drm_buf *next; /* Kernel-only: used for free list */ - __volatile__ int pending; /* On hardware DMA queue */ - struct drm_file *file_priv; /* Unique identifier of holding process */ - int context; /* Kernel queue for this buffer */ + int idx; /**< Index into master buflist */ + int total; /**< Buffer size */ + int order; /**< log-base-2(total) */ + int used; /**< Amount of buffer in use (for DMA) */ + unsigned long offset; /**< Byte offset (used internally) */ + void *address; /**< Address of buffer */ + unsigned long bus_address; /**< Bus address of buffer */ + struct drm_buf *next; /**< Kernel-only: used for free list */ + __volatile__ int waiting; /**< On kernel DMA queue */ + __volatile__ int pending; /**< On hardware DMA queue */ + struct drm_file *file_priv; /**< Private of holding file descr */ + int context; /**< Kernel queue for this buffer */ + int while_locked; /**< Dispatch this buffer while locked */ enum { - DRM_LIST_NONE = 0, - DRM_LIST_FREE = 1, - DRM_LIST_WAIT = 2, - DRM_LIST_PEND = 3, - DRM_LIST_PRIO = 4, + DRM_LIST_NONE = 0, + DRM_LIST_FREE = 1, + DRM_LIST_WAIT = 2, + DRM_LIST_PEND = 3, + DRM_LIST_PRIO = 4, DRM_LIST_RECLAIM = 5 - } list; /* Which list we're on */ + } list; /**< Which list we're on */ - int dev_priv_size; /* Size of buffer private stoarge */ - void *dev_private; /* Per-buffer private storage */ + int dev_priv_size; /**< Size of buffer private storage */ + void *dev_private; /**< Per-buffer private storage */ } drm_buf_t; typedef struct drm_freelist { @@ -475,6 +483,14 @@ struct drm_pending_event { void (*destroy)(struct drm_pending_event *event); }; +/* initial implementaton using a linked list - todo hashtab */ +struct drm_prime_file_private { + struct list_head head; +#ifdef DUMBBELL_WIP + struct mutex lock; +#endif /* DUMBBELL_WIP */ +}; + typedef TAILQ_HEAD(drm_file_list, drm_file) drm_file_list_t; struct drm_file { TAILQ_ENTRY(drm_file) link; @@ -497,6 +513,8 @@ struct drm_file { struct list_head event_list; int event_space; struct selinfo event_poll; + + struct drm_prime_file_private prime; }; typedef struct drm_lock_data { @@ -515,17 +533,21 @@ typedef struct drm_lock_data { * concurrently accessed, so no locking is needed. */ typedef struct drm_device_dma { - drm_buf_entry_t bufs[DRM_MAX_ORDER+1]; - int buf_count; - drm_buf_t **buflist; /* Vector of pointers info bufs */ - int seg_count; - int page_count; - unsigned long *pagelist; - unsigned long byte_count; + + struct drm_buf_entry bufs[DRM_MAX_ORDER + 1]; /**< buffers, grouped by their size order */ + int buf_count; /**< total number of buffers */ + struct drm_buf **buflist; /**< Vector of pointers into drm_device_dma::bufs */ + int seg_count; + int page_count; /**< number of pages */ + unsigned long *pagelist; /**< page list */ + unsigned long byte_count; enum { _DRM_DMA_USE_AGP = 0x01, - _DRM_DMA_USE_SG = 0x02 + _DRM_DMA_USE_SG = 0x02, + _DRM_DMA_USE_FB = 0x04, + _DRM_DMA_USE_PCI_RO = 0x08 } flags; + } drm_device_dma_t; typedef struct drm_agp_mem { @@ -590,28 +612,13 @@ struct drm_vblank_info { int inmodeset; /* Display driver is setting mode */ }; -/* Size of ringbuffer for vblank timestamps. Just double-buffer - * in initial implementation. - */ -#define DRM_VBLANKTIME_RBSIZE 2 - -/* Flags and return codes for get_vblank_timestamp() driver function. */ -#define DRM_CALLED_FROM_VBLIRQ 1 -#define DRM_VBLANKTIME_SCANOUTPOS_METHOD (1 << 0) -#define DRM_VBLANKTIME_INVBL (1 << 1) - -/* get_scanout_position() return flags */ -#define DRM_SCANOUTPOS_VALID (1 << 0) -#define DRM_SCANOUTPOS_INVBL (1 << 1) -#define DRM_SCANOUTPOS_ACCURATE (1 << 2) - /* location of GART table */ #define DRM_ATI_GART_MAIN 1 #define DRM_ATI_GART_FB 2 -#define DRM_ATI_GART_PCI 1 +#define DRM_ATI_GART_PCI 1 #define DRM_ATI_GART_PCIE 2 -#define DRM_ATI_GART_IGP 3 +#define DRM_ATI_GART_IGP 3 struct drm_ati_pcigart_info { int gart_table_location; @@ -683,10 +690,58 @@ struct drm_gem_object { uint32_t pending_write_domain; void *driver_private; + +#ifdef DUMBBELL_WIP + /* dma buf exported from this GEM object */ + struct dma_buf *export_dma_buf; + + /* dma buf attachment backing this object */ + struct dma_buf_attachment *import_attach; +#endif /* DUMBBELL_WIP */ }; #include "drm_crtc.h" +/* per-master structure */ +struct drm_master { + + u_int refcount; /* refcount for this master */ + + struct list_head head; /**< each minor contains a list of masters */ + struct drm_minor *minor; /**< link back to minor we are a master for */ + + char *unique; /**< Unique identifier: e.g., busid */ + int unique_len; /**< Length of unique field */ + int unique_size; /**< amount allocated */ + + int blocked; /**< Blocked due to VC switch? */ + + /** \name Authentication */ + /*@{ */ + struct drm_open_hash magiclist; + struct list_head magicfree; + /*@} */ + + struct drm_lock_data lock; /**< Information on hardware lock */ + + void *driver_priv; /**< Private structure for driver to use */ +}; + +/* Size of ringbuffer for vblank timestamps. Just double-buffer + * in initial implementation. + */ +#define DRM_VBLANKTIME_RBSIZE 2 + +/* Flags and return codes for get_vblank_timestamp() driver function. */ +#define DRM_CALLED_FROM_VBLIRQ 1 +#define DRM_VBLANKTIME_SCANOUTPOS_METHOD (1 << 0) +#define DRM_VBLANKTIME_INVBL (1 << 1) + +/* get_scanout_position() return flags */ +#define DRM_SCANOUTPOS_VALID (1 << 0) +#define DRM_SCANOUTPOS_INVBL (1 << 1) +#define DRM_SCANOUTPOS_ACCURATE (1 << 2) + #ifndef DMA_BIT_MASK #define DMA_BIT_MASK(n) (((n) == 64) ? ~0ULL : (1ULL<<(n)) - 1) #endif @@ -695,6 +750,7 @@ struct drm_gem_object { struct drm_driver_info { int (*load)(struct drm_device *, unsigned long flags); + int (*use_msi)(struct drm_device *, unsigned long flags); int (*firstopen)(struct drm_device *); int (*open)(struct drm_device *, struct drm_file *); void (*preclose)(struct drm_device *, struct drm_file *file_priv); @@ -733,6 +789,8 @@ struct drm_driver_info { int (*gem_init_object)(struct drm_gem_object *obj); void (*gem_free_object)(struct drm_gem_object *obj); + int (*gem_open_object)(struct drm_gem_object *, struct drm_file *); + void (*gem_close_object)(struct drm_gem_object *, struct drm_file *); struct cdev_pager_ops *gem_pager_ops; @@ -755,7 +813,7 @@ struct drm_driver_info { * * \param dev DRM device handle * - * \returns + * \returns * One of three values is returned depending on whether or not the * card is absolutely \b not AGP (return of 0), absolutely \b is AGP * (return of 1), or may or may not be AGP (return of 2). @@ -810,6 +868,7 @@ struct drm_cmdline_mode { enum drm_connector_force force; }; + struct drm_pending_vblank_event { struct drm_pending_event base; int pipe; @@ -819,15 +878,18 @@ struct drm_pending_vblank_event { /* Length for the array of resource pointers for drm_get_resource_*. */ #define DRM_MAX_PCI_RESOURCE 6 -/** - * DRM device functions structure +/** + * DRM device structure. This structure represent a complete card that + * may contain multiple heads. */ struct drm_device { struct drm_driver_info *driver; drm_pci_id_list_t *id_entry; /* PCI ID, name, and chipset private */ - u_int16_t pci_device; /* PCI device id */ - u_int16_t pci_vendor; /* PCI vendor id */ + uint16_t pci_device; /* PCI device id */ + uint16_t pci_vendor; /* PCI vendor id */ + uint16_t pci_subdevice; /* PCI subsystem device id */ + uint16_t pci_subvendor; /* PCI subsystem vendor id */ char *unique; /* Unique identifier: e.g., busid */ int unique_len; /* Length of unique field */ @@ -899,7 +961,7 @@ struct drm_device { drm_agp_head_t *agp; drm_sg_mem_t *sg; /* Scatter gather memory */ - atomic_t *ctx_bitmap; + unsigned long *ctx_bitmap; void *dev_private; unsigned int agp_buffer_token; drm_local_map_t *agp_buffer_map; @@ -907,7 +969,7 @@ struct drm_device { struct drm_minor *control; /**< Control node for card */ struct drm_minor *primary; /**< render type primary screen head */ - void *drm_ttm_bo; + void *drm_ttm_bdev; struct unrhdr *drw_unrhdr; /* RB tree of drawable infos */ RB_HEAD(drawable_tree, bsd_drm_drawable_info) drw_head; @@ -942,8 +1004,14 @@ struct drm_device { void *sysctl_private; char busid_str[128]; int modesetting; + + int switch_power_state; }; +#define DRM_SWITCH_POWER_ON 0 +#define DRM_SWITCH_POWER_OFF 1 +#define DRM_SWITCH_POWER_CHANGING 2 + static __inline__ int drm_core_check_feature(struct drm_device *dev, int feature) { @@ -1023,6 +1091,41 @@ extern int drm_open_helper(struct cdev *kdev, int flags, int fmt, DRM_STRUCTPROC *p, struct drm_device *dev); +#ifdef DUMBBELL_WIP +extern int drm_gem_prime_handle_to_fd(struct drm_device *dev, + struct drm_file *file_priv, uint32_t handle, uint32_t flags, + int *prime_fd); +extern int drm_gem_prime_fd_to_handle(struct drm_device *dev, + struct drm_file *file_priv, int prime_fd, uint32_t *handle); + +extern int drm_prime_handle_to_fd_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv); +extern int drm_prime_fd_to_handle_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv); + +#ifdef DUMBBELL_WIP +/* + * See drm_prime.c + * -- dumbbell@ + */ +extern int drm_prime_sg_to_page_addr_arrays(struct sg_table *sgt, vm_page_t *pages, + dma_addr_t *addrs, int max_pages); +#endif /* DUMBBELL_WIP */ +extern struct sg_table *drm_prime_pages_to_sg(vm_page_t *pages, int nr_pages); +extern void drm_prime_gem_destroy(struct drm_gem_object *obj, struct sg_table *sg); + + +void drm_prime_init_file_private(struct drm_prime_file_private *prime_fpriv); +void drm_prime_destroy_file_private(struct drm_prime_file_private *prime_fpriv); +int drm_prime_add_imported_buf_handle(struct drm_prime_file_private *prime_fpriv, struct dma_buf *dma_buf, uint32_t handle); +int drm_prime_lookup_imported_buf_handle(struct drm_prime_file_private *prime_fpriv, struct dma_buf *dma_buf, uint32_t *handle); +void drm_prime_remove_imported_buf_handle(struct drm_prime_file_private *prime_fpriv, struct dma_buf *dma_buf); + +int drm_prime_add_dma_buf(struct drm_device *dev, struct drm_gem_object *obj); +int drm_prime_lookup_obj(struct drm_device *dev, struct dma_buf *buf, + struct drm_gem_object **obj); +#endif /* DUMBBELL_WIP */ + /* Memory management support (drm_memory.c) */ void drm_mem_init(void); void drm_mem_uninit(void); @@ -1311,6 +1414,8 @@ void drm_gem_pager_dtr(void *obj); struct ttm_bo_device; int ttm_bo_mmap_single(struct ttm_bo_device *bdev, vm_ooffset_t *offset, vm_size_t size, struct vm_object **obj_res, int nprot); +struct ttm_buffer_object; +void ttm_bo_release_mmap(struct ttm_buffer_object *bo); void drm_device_lock_mtx(struct drm_device *dev); void drm_device_unlock_mtx(struct drm_device *dev); @@ -1399,14 +1504,13 @@ static __inline__ void drm_core_dropmap(struct drm_map *map) { } -#define KIB_NOTYET() \ -do { \ - if (drm_debug_flag && drm_notyet_flag) \ - printf("NOTYET: %s at %s:%d\n", __func__, __FILE__, __LINE__); \ -} while (0) +#define DRM_PCIE_SPEED_25 1 +#define DRM_PCIE_SPEED_50 2 +#define DRM_PCIE_SPEED_80 4 -#define KTR_DRM KTR_DEV -#define KTR_DRM_REG KTR_SPARE3 +extern int drm_pcie_get_speed_cap_mask(struct drm_device *dev, u32 *speed_mask); + +#define drm_can_sleep() (DRM_HZ & 1) #endif /* __KERNEL__ */ #endif /* _DRM_P_H_ */ diff --git a/sys/dev/drm2/drm_atomic.h b/sys/dev/drm2/drm_atomic.h index e7dbed98be8..eb86373f51b 100644 --- a/sys/dev/drm2/drm_atomic.h +++ b/sys/dev/drm2/drm_atomic.h @@ -7,6 +7,7 @@ /*- * Copyright 2004 Eric Anholt + * Copyright 2013 Jung-uk Kim * All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a @@ -32,62 +33,54 @@ #include __FBSDID("$FreeBSD$"); -/* Many of these implementations are rather fake, but good enough. */ +typedef u_int atomic_t; +typedef uint64_t atomic64_t; -typedef u_int32_t atomic_t; +#define BITS_PER_LONG (sizeof(long) * NBBY) +#define BITS_TO_LONGS(x) howmany(x, BITS_PER_LONG) -#define atomic_set(p, v) (*(p) = (v)) -#define atomic_read(p) (*(p)) -#define atomic_inc(p) atomic_add_int(p, 1) -#define atomic_dec(p) atomic_subtract_int(p, 1) -#define atomic_add(n, p) atomic_add_int(p, n) -#define atomic_sub(n, p) atomic_subtract_int(p, n) +#define atomic_read(p) (*(volatile u_int *)(p)) +#define atomic_set(p, v) do { *(u_int *)(p) = (v); } while (0) -static __inline atomic_t -test_and_set_bit(int b, volatile void *p) +#define atomic_add(v, p) atomic_add_int(p, v) +#define atomic_sub(v, p) atomic_subtract_int(p, v) +#define atomic_inc(p) atomic_add(1, p) +#define atomic_dec(p) atomic_sub(1, p) + +#define atomic_add_return(v, p) (atomic_fetchadd_int(p, v) + (v)) +#define atomic_sub_return(v, p) (atomic_fetchadd_int(p, -(v)) - (v)) +#define atomic_inc_return(p) atomic_add_return(1, p) +#define atomic_dec_return(p) atomic_sub_return(1, p) + +#define atomic_add_and_test(v, p) (atomic_add_return(v, p) == 0) +#define atomic_sub_and_test(v, p) (atomic_sub_return(v, p) == 0) +#define atomic_inc_and_test(p) (atomic_inc_return(p) == 0) +#define atomic_dec_and_test(p) (atomic_dec_return(p) == 0) + +#define atomic_xchg(p, v) atomic_swap_int(p, v) +#define atomic64_xchg(p, v) atomic_swap_64(p, v) + +#define __bit_word(b) ((b) / BITS_PER_LONG) +#define __bit_mask(b) (1UL << (b) % BITS_PER_LONG) +#define __bit_addr(p, b) ((volatile u_long *)(p) + __bit_word(b)) + +#define clear_bit(b, p) \ + atomic_clear_long(__bit_addr(p, b), __bit_mask(b)) +#define set_bit(b, p) \ + atomic_set_long(__bit_addr(p, b), __bit_mask(b)) +#define test_bit(b, p) \ + ((*__bit_addr(p, b) & __bit_mask(b)) != 0) + +static __inline u_long +find_first_zero_bit(const u_long *p, u_long max) { - int s = splhigh(); - unsigned int m = 1<> 5), 1 << (b & 0x1f)); -} - -static __inline void -set_bit(int b, volatile void *p) -{ - atomic_set_int(((volatile int *)p) + (b >> 5), 1 << (b & 0x1f)); -} - -static __inline int -test_bit(int b, volatile void *p) -{ - return ((volatile int *)p)[b >> 5] & (1 << (b & 0x1f)); -} - -static __inline int -find_first_zero_bit(volatile void *p, int max) -{ - int b; - volatile int *ptr = (volatile int *)p; - - for (b = 0; b < max; b += 32) { - if (ptr[b >> 5] != ~0) { - for (;;) { - if ((ptr[b >> 5] & (1 << (b & 0x1f))) == 0) - return b; - b++; - } - } + KASSERT(max % BITS_PER_LONG == 0, ("invalid bitmap size %lu", max)); + for (i = 0; i < max / BITS_PER_LONG; i++) { + n = ~p[i]; + if (n != 0) + return (i * BITS_PER_LONG + ffsl(n) - 1); } - return max; + return (max); } - -#define BITS_TO_LONGS(x) (howmany((x), NBBY * sizeof(long))) diff --git a/sys/dev/drm2/drm_buffer.c b/sys/dev/drm2/drm_buffer.c new file mode 100644 index 00000000000..cdc91a4a509 --- /dev/null +++ b/sys/dev/drm2/drm_buffer.c @@ -0,0 +1,183 @@ +/************************************************************************** + * + * Copyright 2010 Pauli Nieminen. + * All Rights Reserved. + * + * 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, sub license, 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 (including the + * next paragraph) 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 NON-INFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS 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. + * + * + **************************************************************************/ +/* + * Multipart buffer for coping data which is larger than the page size. + * + * Authors: + * Pauli Nieminen + */ + +#include +__FBSDID("$FreeBSD$"); + +#include + +/** + * Allocate the drm buffer object. + * + * buf: Pointer to a pointer where the object is stored. + * size: The number of bytes to allocate. + */ +int drm_buffer_alloc(struct drm_buffer **buf, int size) +{ + int nr_pages = size / PAGE_SIZE + 1; + int idx; + + /* Allocating pointer table to end of structure makes drm_buffer + * variable sized */ + *buf = malloc(sizeof(struct drm_buffer) + nr_pages*sizeof(char *), + DRM_MEM_DRIVER, M_ZERO | M_WAITOK); + + if (*buf == NULL) { + DRM_ERROR("Failed to allocate drm buffer object to hold" + " %d bytes in %d pages.\n", + size, nr_pages); + return -ENOMEM; + } + + (*buf)->size = size; + + for (idx = 0; idx < nr_pages; ++idx) { + + (*buf)->data[idx] = + malloc(min(PAGE_SIZE, size - idx * PAGE_SIZE), + DRM_MEM_DRIVER, M_WAITOK); + + + if ((*buf)->data[idx] == NULL) { + DRM_ERROR("Failed to allocate %dth page for drm" + " buffer with %d bytes and %d pages.\n", + idx + 1, size, nr_pages); + goto error_out; + } + + } + + return 0; + +error_out: + + /* Only last element can be null pointer so check for it first. */ + if ((*buf)->data[idx]) + free((*buf)->data[idx], DRM_MEM_DRIVER); + + for (--idx; idx >= 0; --idx) + free((*buf)->data[idx], DRM_MEM_DRIVER); + + free(*buf, DRM_MEM_DRIVER); + return -ENOMEM; +} + +/** + * Copy the user data to the begin of the buffer and reset the processing + * iterator. + * + * user_data: A pointer the data that is copied to the buffer. + * size: The Number of bytes to copy. + */ +int drm_buffer_copy_from_user(struct drm_buffer *buf, + void __user *user_data, int size) +{ + int nr_pages = size / PAGE_SIZE + 1; + int idx; + + if (size > buf->size) { + DRM_ERROR("Requesting to copy %d bytes to a drm buffer with" + " %d bytes space\n", + size, buf->size); + return -EFAULT; + } + + for (idx = 0; idx < nr_pages; ++idx) { + + if (DRM_COPY_FROM_USER(buf->data[idx], + (char *)user_data + idx * PAGE_SIZE, + min(PAGE_SIZE, size - idx * PAGE_SIZE))) { + DRM_ERROR("Failed to copy user data (%p) to drm buffer" + " (%p) %dth page.\n", + user_data, buf, idx); + return -EFAULT; + + } + } + buf->iterator = 0; + return 0; +} + +/** + * Free the drm buffer object + */ +void drm_buffer_free(struct drm_buffer *buf) +{ + + if (buf != NULL) { + + int nr_pages = buf->size / PAGE_SIZE + 1; + int idx; + for (idx = 0; idx < nr_pages; ++idx) + free(buf->data[idx], DRM_MEM_DRIVER); + + free(buf, DRM_MEM_DRIVER); + } +} + +/** + * Read an object from buffer that may be split to multiple parts. If object + * is not split function just returns the pointer to object in buffer. But in + * case of split object data is copied to given stack object that is suplied + * by caller. + * + * The processing location of the buffer is also advanced to the next byte + * after the object. + * + * objsize: The size of the objet in bytes. + * stack_obj: A pointer to a memory location where object can be copied. + */ +void *drm_buffer_read_object(struct drm_buffer *buf, + int objsize, void *stack_obj) +{ + int idx = drm_buffer_index(buf); + int page = drm_buffer_page(buf); + void *obj = NULL; + + if (idx + objsize <= PAGE_SIZE) { + obj = &buf->data[page][idx]; + } else { + /* The object is split which forces copy to temporary object.*/ + int beginsz = PAGE_SIZE - idx; + memcpy(stack_obj, &buf->data[page][idx], beginsz); + + memcpy((char *)stack_obj + beginsz, &buf->data[page + 1][0], + objsize - beginsz); + + obj = stack_obj; + } + + drm_buffer_advance(buf, objsize); + return obj; +} diff --git a/sys/dev/drm2/drm_buffer.h b/sys/dev/drm2/drm_buffer.h new file mode 100644 index 00000000000..776660ca190 --- /dev/null +++ b/sys/dev/drm2/drm_buffer.h @@ -0,0 +1,151 @@ +/************************************************************************** + * + * Copyright 2010 Pauli Nieminen. + * All Rights Reserved. + * + * 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, sub license, 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 (including the + * next paragraph) 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 NON-INFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS 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. + * + * + **************************************************************************/ +/* + * Multipart buffer for coping data which is larger than the page size. + * + * Authors: + * Pauli Nieminen + */ + +#include +__FBSDID("$FreeBSD$"); + +#ifndef _DRM_BUFFER_H_ +#define _DRM_BUFFER_H_ + +#include + +struct drm_buffer { + int iterator; + int size; + char *data[]; +}; + + +/** + * Return the index of page that buffer is currently pointing at. + */ +static inline int drm_buffer_page(struct drm_buffer *buf) +{ + return buf->iterator / PAGE_SIZE; +} +/** + * Return the index of the current byte in the page + */ +static inline int drm_buffer_index(struct drm_buffer *buf) +{ + return buf->iterator & (PAGE_SIZE - 1); +} +/** + * Return number of bytes that is left to process + */ +static inline int drm_buffer_unprocessed(struct drm_buffer *buf) +{ + return buf->size - buf->iterator; +} + +/** + * Advance the buffer iterator number of bytes that is given. + */ +static inline void drm_buffer_advance(struct drm_buffer *buf, int bytes) +{ + buf->iterator += bytes; +} + +/** + * Allocate the drm buffer object. + * + * buf: A pointer to a pointer where the object is stored. + * size: The number of bytes to allocate. + */ +extern int drm_buffer_alloc(struct drm_buffer **buf, int size); + +/** + * Copy the user data to the begin of the buffer and reset the processing + * iterator. + * + * user_data: A pointer the data that is copied to the buffer. + * size: The Number of bytes to copy. + */ +extern int drm_buffer_copy_from_user(struct drm_buffer *buf, + void __user *user_data, int size); + +/** + * Free the drm buffer object + */ +extern void drm_buffer_free(struct drm_buffer *buf); + +/** + * Read an object from buffer that may be split to multiple parts. If object + * is not split function just returns the pointer to object in buffer. But in + * case of split object data is copied to given stack object that is suplied + * by caller. + * + * The processing location of the buffer is also advanced to the next byte + * after the object. + * + * objsize: The size of the objet in bytes. + * stack_obj: A pointer to a memory location where object can be copied. + */ +extern void *drm_buffer_read_object(struct drm_buffer *buf, + int objsize, void *stack_obj); + +/** + * Returns the pointer to the dword which is offset number of elements from the + * current processing location. + * + * Caller must make sure that dword is not split in the buffer. This + * requirement is easily met if all the sizes of objects in buffer are + * multiples of dword and PAGE_SIZE is multiple dword. + * + * Call to this function doesn't change the processing location. + * + * offset: The index of the dword relative to the internat iterator. + */ +static inline void *drm_buffer_pointer_to_dword(struct drm_buffer *buffer, + int offset) +{ + int iter = buffer->iterator + offset * 4; + return &buffer->data[iter / PAGE_SIZE][iter & (PAGE_SIZE - 1)]; +} +/** + * Returns the pointer to the dword which is offset number of elements from + * the current processing location. + * + * Call to this function doesn't change the processing location. + * + * offset: The index of the byte relative to the internat iterator. + */ +static inline void *drm_buffer_pointer_to_byte(struct drm_buffer *buffer, + int offset) +{ + int iter = buffer->iterator + offset; + return &buffer->data[iter / PAGE_SIZE][iter & (PAGE_SIZE - 1)]; +} + +#endif diff --git a/sys/dev/drm2/drm_context.c b/sys/dev/drm2/drm_context.c index c844a39dff2..9ca941e6a8a 100644 --- a/sys/dev/drm2/drm_context.c +++ b/sys/dev/drm2/drm_context.c @@ -182,7 +182,7 @@ bad: int drm_context_switch(struct drm_device *dev, int old, int new) { - if (test_and_set_bit(0, &dev->context_flag)) { + if (atomic_xchg(&dev->context_flag, 1) != 0) { DRM_ERROR("Reentering -- FIXME\n"); return EBUSY; } @@ -190,7 +190,7 @@ int drm_context_switch(struct drm_device *dev, int old, int new) DRM_DEBUG("Context switch from %d to %d\n", old, new); if (new == dev->last_context) { - clear_bit(0, &dev->context_flag); + atomic_xchg(&dev->context_flag, 0); return 0; } @@ -208,7 +208,7 @@ int drm_context_switch_complete(struct drm_device *dev, int new) /* If a context switch is ever initiated when the kernel holds the lock, release that lock here. */ - clear_bit(0, &dev->context_flag); + atomic_xchg(&dev->context_flag, 0); return 0; } diff --git a/sys/dev/drm2/drm_core.h b/sys/dev/drm2/drm_core.h new file mode 100644 index 00000000000..4cccb7b4dff --- /dev/null +++ b/sys/dev/drm2/drm_core.h @@ -0,0 +1,38 @@ +/* + * Copyright 2004 Jon Smirl + * + * 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, sub license, + * 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 (including the + * next paragraph) 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 NON-INFRINGEMENT. IN NO EVENT SHALL + * VIA, S3 GRAPHICS, AND/OR ITS SUPPLIERS 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 +__FBSDID("$FreeBSD$"); + +#define CORE_AUTHOR "Gareth Hughes, Leif Delgass, José Fonseca, Jon Smirl" + +#define CORE_NAME "drm" +#define CORE_DESC "DRM shared core routines" +#define CORE_DATE "20060810" + +#define DRM_IF_MAJOR 1 +#define DRM_IF_MINOR 4 + +#define CORE_MAJOR 1 +#define CORE_MINOR 1 +#define CORE_PATCHLEVEL 0 diff --git a/sys/dev/drm2/drm_crtc.c b/sys/dev/drm2/drm_crtc.c index f6e462e7193..ae40eb2e283 100644 --- a/sys/dev/drm2/drm_crtc.c +++ b/sys/dev/drm2/drm_crtc.c @@ -170,6 +170,9 @@ static struct drm_prop_enum_list drm_encoder_enum_list[] = { DRM_MODE_ENCODER_TVDAC, "TV" }, }; +static void drm_property_destroy_blob(struct drm_device *dev, + struct drm_property_blob *blob); + char *drm_get_encoder_name(struct drm_encoder *encoder) { static char buf[32]; @@ -520,6 +523,8 @@ void drm_connector_cleanup(struct drm_connector *connector) drm_mode_remove(connector, mode); sx_xlock(&dev->mode_config.mutex); + if (connector->edid_blob_ptr) + drm_property_destroy_blob(dev, connector->edid_blob_ptr); drm_mode_object_put(dev, &connector->base); list_del(&connector->head); dev->mode_config.num_connector--; diff --git a/sys/dev/drm2/drm_crtc.h b/sys/dev/drm2/drm_crtc.h index a69c5378f57..0ab2bf48ff8 100644 --- a/sys/dev/drm2/drm_crtc.h +++ b/sys/dev/drm2/drm_crtc.h @@ -659,7 +659,7 @@ struct drm_mode_config { int min_width, min_height; int max_width, max_height; - struct drm_mode_config_funcs *funcs; + const struct drm_mode_config_funcs *funcs; resource_size_t fb_base; /* output poll support */ diff --git a/sys/dev/drm2/drm_crtc_helper.h b/sys/dev/drm2/drm_crtc_helper.h index 76542632fa3..0110949b170 100644 --- a/sys/dev/drm2/drm_crtc_helper.h +++ b/sys/dev/drm2/drm_crtc_helper.h @@ -51,7 +51,7 @@ struct drm_crtc_helper_funcs { /* Provider can fixup or change mode timings before modeset occurs */ bool (*mode_fixup)(struct drm_crtc *crtc, - struct drm_display_mode *mode, + const struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode); /* Actually set the mode */ int (*mode_set)(struct drm_crtc *crtc, struct drm_display_mode *mode, @@ -78,7 +78,7 @@ struct drm_encoder_helper_funcs { void (*restore)(struct drm_encoder *encoder); bool (*mode_fixup)(struct drm_encoder *encoder, - struct drm_display_mode *mode, + const struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode); void (*prepare)(struct drm_encoder *encoder); void (*commit)(struct drm_encoder *encoder); diff --git a/sys/dev/drm2/drm_dp_helper.c b/sys/dev/drm2/drm_dp_helper.c new file mode 100644 index 00000000000..d4a03603ed0 --- /dev/null +++ b/sys/dev/drm2/drm_dp_helper.c @@ -0,0 +1,147 @@ +/* + * Copyright © 2009 Keith Packard + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting documentation, and + * that the name of the copyright holders not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. The copyright holders make no representations + * about the suitability of this software for any purpose. It is provided "as + * is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, 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 +__FBSDID("$FreeBSD$"); + +#include +#include + +/** + * DOC: dp helpers + * + * These functions contain some common logic and helpers at various abstraction + * levels to deal with Display Port sink devices and related things like DP aux + * channel transfers, EDID reading over DP aux channels, decoding certain DPCD + * blocks, ... + */ + +static u8 dp_link_status(u8 link_status[DP_LINK_STATUS_SIZE], int r) +{ + return link_status[r - DP_LANE0_1_STATUS]; +} + +static u8 dp_get_lane_status(u8 link_status[DP_LINK_STATUS_SIZE], + int lane) +{ + int i = DP_LANE0_1_STATUS + (lane >> 1); + int s = (lane & 1) * 4; + u8 l = dp_link_status(link_status, i); + return (l >> s) & 0xf; +} + +bool drm_dp_channel_eq_ok(u8 link_status[DP_LINK_STATUS_SIZE], + int lane_count) +{ + u8 lane_align; + u8 lane_status; + int lane; + + lane_align = dp_link_status(link_status, + DP_LANE_ALIGN_STATUS_UPDATED); + if ((lane_align & DP_INTERLANE_ALIGN_DONE) == 0) + return false; + for (lane = 0; lane < lane_count; lane++) { + lane_status = dp_get_lane_status(link_status, lane); + if ((lane_status & DP_CHANNEL_EQ_BITS) != DP_CHANNEL_EQ_BITS) + return false; + } + return true; +} + +bool drm_dp_clock_recovery_ok(u8 link_status[DP_LINK_STATUS_SIZE], + int lane_count) +{ + int lane; + u8 lane_status; + + for (lane = 0; lane < lane_count; lane++) { + lane_status = dp_get_lane_status(link_status, lane); + if ((lane_status & DP_LANE_CR_DONE) == 0) + return false; + } + return true; +} + +u8 drm_dp_get_adjust_request_voltage(u8 link_status[DP_LINK_STATUS_SIZE], + int lane) +{ + int i = DP_ADJUST_REQUEST_LANE0_1 + (lane >> 1); + int s = ((lane & 1) ? + DP_ADJUST_VOLTAGE_SWING_LANE1_SHIFT : + DP_ADJUST_VOLTAGE_SWING_LANE0_SHIFT); + u8 l = dp_link_status(link_status, i); + + return ((l >> s) & 0x3) << DP_TRAIN_VOLTAGE_SWING_SHIFT; +} + +u8 drm_dp_get_adjust_request_pre_emphasis(u8 link_status[DP_LINK_STATUS_SIZE], + int lane) +{ + int i = DP_ADJUST_REQUEST_LANE0_1 + (lane >> 1); + int s = ((lane & 1) ? + DP_ADJUST_PRE_EMPHASIS_LANE1_SHIFT : + DP_ADJUST_PRE_EMPHASIS_LANE0_SHIFT); + u8 l = dp_link_status(link_status, i); + + return ((l >> s) & 0x3) << DP_TRAIN_PRE_EMPHASIS_SHIFT; +} + +void drm_dp_link_train_clock_recovery_delay(u8 dpcd[DP_RECEIVER_CAP_SIZE]) { + if (dpcd[DP_TRAINING_AUX_RD_INTERVAL] == 0) + DRM_UDELAY(100); + else + DRM_MDELAY(dpcd[DP_TRAINING_AUX_RD_INTERVAL] * 4); +} + +void drm_dp_link_train_channel_eq_delay(u8 dpcd[DP_RECEIVER_CAP_SIZE]) { + if (dpcd[DP_TRAINING_AUX_RD_INTERVAL] == 0) + DRM_UDELAY(400); + else + DRM_MDELAY(dpcd[DP_TRAINING_AUX_RD_INTERVAL] * 4); +} + +u8 drm_dp_link_rate_to_bw_code(int link_rate) +{ + switch (link_rate) { + case 162000: + default: + return DP_LINK_BW_1_62; + case 270000: + return DP_LINK_BW_2_7; + case 540000: + return DP_LINK_BW_5_4; + } +} + +int drm_dp_bw_code_to_link_rate(u8 link_bw) +{ + switch (link_bw) { + case DP_LINK_BW_1_62: + default: + return 162000; + case DP_LINK_BW_2_7: + return 270000; + case DP_LINK_BW_5_4: + return 540000; + } +} diff --git a/sys/dev/drm2/drm_dp_helper.h b/sys/dev/drm2/drm_dp_helper.h index 4eea73beec8..b6d84d72c83 100644 --- a/sys/dev/drm2/drm_dp_helper.h +++ b/sys/dev/drm2/drm_dp_helper.h @@ -25,7 +25,19 @@ #ifndef _DRM_DP_HELPER_H_ #define _DRM_DP_HELPER_H_ -/* From the VESA DisplayPort spec */ +/* + * Unless otherwise noted, all values are from the DP 1.1a spec. Note that + * DP and DPCD versions are independent. Differences from 1.0 are not noted, + * 1.0 devices basically don't exist in the wild. + * + * Abbreviations, in chronological order: + * + * eDP: Embedded DisplayPort version 1 + * DPI: DisplayPort Interoperability Guideline v1.1a + * 1.2: DisplayPort 1.2 + * + * 1.2 formally includes both eDP and DPI definitions. + */ #define AUX_NATIVE_WRITE 0x8 #define AUX_NATIVE_READ 0x9 @@ -52,7 +64,7 @@ #define DP_MAX_LANE_COUNT 0x002 # define DP_MAX_LANE_COUNT_MASK 0x1f -# define DP_TPS3_SUPPORTED (1 << 6) +# define DP_TPS3_SUPPORTED (1 << 6) /* 1.2 */ # define DP_ENHANCED_FRAME_CAP (1 << 7) #define DP_MAX_DOWNSPREAD 0x003 @@ -68,14 +80,33 @@ /* 10b = TMDS or HDMI */ /* 11b = Other */ # define DP_FORMAT_CONVERSION (1 << 3) +# define DP_DETAILED_CAP_INFO_AVAILABLE (1 << 4) /* DPI */ #define DP_MAIN_LINK_CHANNEL_CODING 0x006 -#define DP_TRAINING_AUX_RD_INTERVAL 0x00e +#define DP_DOWN_STREAM_PORT_COUNT 0x007 +# define DP_PORT_COUNT_MASK 0x0f +# define DP_MSA_TIMING_PAR_IGNORED (1 << 6) /* eDP */ +# define DP_OUI_SUPPORT (1 << 7) -#define DP_PSR_SUPPORT 0x070 +#define DP_I2C_SPEED_CAP 0x00c /* DPI */ +# define DP_I2C_SPEED_1K 0x01 +# define DP_I2C_SPEED_5K 0x02 +# define DP_I2C_SPEED_10K 0x04 +# define DP_I2C_SPEED_100K 0x08 +# define DP_I2C_SPEED_400K 0x10 +# define DP_I2C_SPEED_1M 0x20 + +#define DP_EDP_CONFIGURATION_CAP 0x00d /* XXX 1.2? */ +#define DP_TRAINING_AUX_RD_INTERVAL 0x00e /* XXX 1.2? */ + +/* Multiple stream transport */ +#define DP_MSTM_CAP 0x021 /* 1.2 */ +# define DP_MST_CAP (1 << 0) + +#define DP_PSR_SUPPORT 0x070 /* XXX 1.2? */ # define DP_PSR_IS_SUPPORTED 1 -#define DP_PSR_CAPS 0x071 +#define DP_PSR_CAPS 0x071 /* XXX 1.2? */ # define DP_PSR_NO_TRAIN_ON_EXIT 1 # define DP_PSR_SETUP_TIME_330 (0 << 1) # define DP_PSR_SETUP_TIME_275 (1 << 1) @@ -87,11 +118,36 @@ # define DP_PSR_SETUP_TIME_MASK (7 << 1) # define DP_PSR_SETUP_TIME_SHIFT 1 +/* + * 0x80-0x8f describe downstream port capabilities, but there are two layouts + * based on whether DP_DETAILED_CAP_INFO_AVAILABLE was set. If it was not, + * each port's descriptor is one byte wide. If it was set, each port's is + * four bytes wide, starting with the one byte from the base info. As of + * DP interop v1.1a only VGA defines additional detail. + */ + +/* offset 0 */ +#define DP_DOWNSTREAM_PORT_0 0x80 +# define DP_DS_PORT_TYPE_MASK (7 << 0) +# define DP_DS_PORT_TYPE_DP 0 +# define DP_DS_PORT_TYPE_VGA 1 +# define DP_DS_PORT_TYPE_DVI 2 +# define DP_DS_PORT_TYPE_HDMI 3 +# define DP_DS_PORT_TYPE_NON_EDID 4 +# define DP_DS_PORT_HPD (1 << 3) +/* offset 1 for VGA is maximum megapixels per second / 8 */ +/* offset 2 */ +# define DP_DS_VGA_MAX_BPC_MASK (3 << 0) +# define DP_DS_VGA_8BPC 0 +# define DP_DS_VGA_10BPC 1 +# define DP_DS_VGA_12BPC 2 +# define DP_DS_VGA_16BPC 3 + /* link configuration */ #define DP_LINK_BW_SET 0x100 # define DP_LINK_BW_1_62 0x06 # define DP_LINK_BW_2_7 0x0a -# define DP_LINK_BW_5_4 0x14 +# define DP_LINK_BW_5_4 0x14 /* 1.2 */ #define DP_LANE_COUNT_SET 0x101 # define DP_LANE_COUNT_MASK 0x0f @@ -101,7 +157,7 @@ # define DP_TRAINING_PATTERN_DISABLE 0 # define DP_TRAINING_PATTERN_1 1 # define DP_TRAINING_PATTERN_2 2 -# define DP_TRAINING_PATTERN_3 3 +# define DP_TRAINING_PATTERN_3 3 /* 1.2 */ # define DP_TRAINING_PATTERN_MASK 0x3 # define DP_LINK_QUAL_PATTERN_DISABLE (0 << 2) @@ -142,16 +198,32 @@ #define DP_DOWNSPREAD_CTRL 0x107 # define DP_SPREAD_AMP_0_5 (1 << 4) +# define DP_MSA_TIMING_PAR_IGNORE_EN (1 << 7) /* eDP */ #define DP_MAIN_LINK_CHANNEL_CODING_SET 0x108 # define DP_SET_ANSI_8B10B (1 << 0) -#define DP_PSR_EN_CFG 0x170 +#define DP_I2C_SPEED_CONTROL_STATUS 0x109 /* DPI */ +/* bitmask as for DP_I2C_SPEED_CAP */ + +#define DP_EDP_CONFIGURATION_SET 0x10a /* XXX 1.2? */ + +#define DP_MSTM_CTRL 0x111 /* 1.2 */ +# define DP_MST_EN (1 << 0) +# define DP_UP_REQ_EN (1 << 1) +# define DP_UPSTREAM_IS_SRC (1 << 2) + +#define DP_PSR_EN_CFG 0x170 /* XXX 1.2? */ # define DP_PSR_ENABLE (1 << 0) # define DP_PSR_MAIN_LINK_ACTIVE (1 << 1) # define DP_PSR_CRC_VERIFICATION (1 << 2) # define DP_PSR_FRAME_CAPTURE (1 << 3) +#define DP_SINK_COUNT 0x200 +/* prior to 1.2 bit 7 was reserved mbz */ +# define DP_GET_SINK_COUNT(x) ((((x) & 0x80) >> 1) | ((x) & 0x3f)) +# define DP_SINK_CP_READY (1 << 6) + #define DP_DEVICE_SERVICE_IRQ_VECTOR 0x201 # define DP_REMOTE_CONTROL_COMMAND_PENDING (1 << 0) # define DP_AUTOMATED_TEST_REQUEST (1 << 1) @@ -209,18 +281,22 @@ # define DP_TEST_NAK (1 << 1) # define DP_TEST_EDID_CHECKSUM_WRITE (1 << 2) +#define DP_SOURCE_OUI 0x300 +#define DP_SINK_OUI 0x400 +#define DP_BRANCH_OUI 0x500 + #define DP_SET_POWER 0x600 # define DP_SET_POWER_D0 0x1 # define DP_SET_POWER_D3 0x2 -#define DP_PSR_ERROR_STATUS 0x2006 +#define DP_PSR_ERROR_STATUS 0x2006 /* XXX 1.2? */ # define DP_PSR_LINK_CRC_ERROR (1 << 0) # define DP_PSR_RFB_STORAGE_ERROR (1 << 1) -#define DP_PSR_ESI 0x2007 +#define DP_PSR_ESI 0x2007 /* XXX 1.2? */ # define DP_PSR_CAPS_CHANGE (1 << 0) -#define DP_PSR_STATUS 0x2008 +#define DP_PSR_STATUS 0x2008 /* XXX 1.2? */ # define DP_PSR_SINK_INACTIVE 0 # define DP_PSR_SINK_ACTIVE_SRC_SYNCED 1 # define DP_PSR_SINK_ACTIVE_RFB 2 @@ -247,4 +323,34 @@ int iic_dp_aux_add_bus(device_t dev, const char *name, int (*ch)(device_t idev, int mode, uint8_t write_byte, uint8_t *read_byte), void *priv, device_t *bus, device_t *adapter); + +#define DP_LINK_STATUS_SIZE 6 +bool drm_dp_channel_eq_ok(u8 link_status[DP_LINK_STATUS_SIZE], + int lane_count); +bool drm_dp_clock_recovery_ok(u8 link_status[DP_LINK_STATUS_SIZE], + int lane_count); +u8 drm_dp_get_adjust_request_voltage(u8 link_status[DP_LINK_STATUS_SIZE], + int lane); +u8 drm_dp_get_adjust_request_pre_emphasis(u8 link_status[DP_LINK_STATUS_SIZE], + int lane); + +#define DP_RECEIVER_CAP_SIZE 0xf +void drm_dp_link_train_clock_recovery_delay(u8 dpcd[DP_RECEIVER_CAP_SIZE]); +void drm_dp_link_train_channel_eq_delay(u8 dpcd[DP_RECEIVER_CAP_SIZE]); + +u8 drm_dp_link_rate_to_bw_code(int link_rate); +int drm_dp_bw_code_to_link_rate(u8 link_bw); + +static inline int +drm_dp_max_link_rate(u8 dpcd[DP_RECEIVER_CAP_SIZE]) +{ + return drm_dp_bw_code_to_link_rate(dpcd[DP_MAX_LINK_RATE]); +} + +static inline u8 +drm_dp_max_lane_count(u8 dpcd[DP_RECEIVER_CAP_SIZE]) +{ + return dpcd[DP_MAX_LANE_COUNT] & DP_MAX_LANE_COUNT_MASK; +} + #endif /* _DRM_DP_HELPER_H_ */ diff --git a/sys/dev/drm2/drm_dp_iic_helper.c b/sys/dev/drm2/drm_dp_iic_helper.c index 71319e35aad..c4e580307fb 100644 --- a/sys/dev/drm2/drm_dp_iic_helper.c +++ b/sys/dev/drm2/drm_dp_iic_helper.c @@ -216,22 +216,6 @@ iic_dp_aux_attach(device_t idev) return (0); } -static int -iic_dp_aux_detach(device_t idev) -{ - struct iic_dp_aux_data *aux_data; - device_t port; - - aux_data = device_get_softc(idev); - - port = aux_data->port; - bus_generic_detach(idev); - if (port != NULL) - device_delete_child(idev, port); - - return (0); -} - int iic_dp_aux_add_bus(device_t dev, const char *name, int (*ch)(device_t idev, int mode, uint8_t write_byte, uint8_t *read_byte), @@ -277,7 +261,7 @@ iic_dp_aux_add_bus(device_t dev, const char *name, static device_method_t drm_iic_dp_aux_methods[] = { DEVMETHOD(device_probe, iic_dp_aux_probe), DEVMETHOD(device_attach, iic_dp_aux_attach), - DEVMETHOD(device_detach, iic_dp_aux_detach), + DEVMETHOD(device_detach, bus_generic_detach), DEVMETHOD(iicbus_reset, iic_dp_aux_reset), DEVMETHOD(iicbus_transfer, iic_dp_aux_xfer), DEVMETHOD_END diff --git a/sys/dev/drm2/drm_drv.c b/sys/dev/drm2/drm_drv.c index c45bda19577..c84c72a4dd5 100644 --- a/sys/dev/drm2/drm_drv.c +++ b/sys/dev/drm2/drm_drv.c @@ -40,6 +40,8 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include +#include #include #include @@ -78,7 +80,7 @@ static moduledata_t drm_mod = { "drmn", drm_modevent, 0 -}; +}; DECLARE_MODULE(drmn, drm_mod, SI_SUB_DRIVERS, SI_ORDER_FIRST); MODULE_VERSION(drmn, 1); MODULE_DEPEND(drmn, agp, 1, 1, 1); @@ -207,13 +209,22 @@ static struct drm_msi_blacklist_entry drm_msi_blacklist[] = { {0, 0} }; -static int drm_msi_is_blacklisted(int vendor, int device) +static int drm_msi_is_blacklisted(struct drm_device *dev, unsigned long flags) { int i = 0; - + + if (dev->driver->use_msi != NULL) { + int use_msi; + + use_msi = dev->driver->use_msi(dev, flags); + + return (!use_msi); + } + + /* TODO: Maybe move this to a callback in i915? */ for (i = 0; drm_msi_blacklist[i].vendor != 0; i++) { - if ((drm_msi_blacklist[i].vendor == vendor) && - (drm_msi_blacklist[i].device == device)) { + if ((drm_msi_blacklist[i].vendor == dev->pci_vendor) && + (drm_msi_blacklist[i].device == dev->pci_device)) { return 1; } } @@ -262,10 +273,16 @@ int drm_attach(device_t kdev, drm_pci_id_list_t *idlist) dev->pci_vendor = pci_get_vendor(dev->device); dev->pci_device = pci_get_device(dev->device); + dev->pci_subvendor = pci_get_subvendor(dev->device); + dev->pci_subdevice = pci_get_subdevice(dev->device); + + id_entry = drm_find_description(dev->pci_vendor, + dev->pci_device, idlist); + dev->id_entry = id_entry; if (drm_core_check_feature(dev, DRIVER_HAVE_IRQ)) { if (drm_msi && - !drm_msi_is_blacklisted(dev->pci_vendor, dev->pci_device)) { + !drm_msi_is_blacklisted(dev, dev->id_entry->driver_private)) { msicount = pci_msi_count(dev->device); DRM_DEBUG("MSI count = %d\n", msicount); if (msicount > 1) @@ -295,13 +312,23 @@ int drm_attach(device_t kdev, drm_pci_id_list_t *idlist) mtx_init(&dev->event_lock, "drmev", NULL, MTX_DEF); sx_init(&dev->dev_struct_lock, "drmslk"); - id_entry = drm_find_description(dev->pci_vendor, - dev->pci_device, idlist); - dev->id_entry = id_entry; - error = drm_load(dev); - if (error == 0) - error = drm_create_cdevs(kdev); + if (error) + goto error; + + error = drm_create_cdevs(kdev); + if (error) + goto error; + + return (error); +error: + if (dev->irqr) { + bus_release_resource(dev->device, SYS_RES_IRQ, + dev->irqrid, dev->irqr); + } + if (dev->msi_enabled) { + pci_release_msi(dev->device); + } return (error); } @@ -349,7 +376,7 @@ drm_pci_id_list_t *drm_find_description(int vendor, int device, drm_pci_id_list_t *idlist) { int i = 0; - + for (i = 0; idlist[i].vendor != 0; i++) { if ((idlist[i].vendor == vendor) && ((idlist[i].device == device) || @@ -559,7 +586,7 @@ static int drm_load(struct drm_device *dev) DRM_ERROR("Request to enable bus-master failed.\n"); DRM_UNLOCK(dev); if (retcode != 0) - goto error; + goto error1; } DRM_INFO("Initialized %s %d.%d.%d %s\n", @@ -573,7 +600,9 @@ static int drm_load(struct drm_device *dev) error1: delete_unrhdr(dev->drw_unrhdr); + drm_gem_destroy(dev); error: + drm_ctxbitmap_cleanup(dev); drm_sysctl_cleanup(dev); DRM_LOCK(dev); drm_lastclose(dev); @@ -746,7 +775,7 @@ void drm_close(void *data) drm_lock_free(&dev->lock, _DRM_LOCKING_CONTEXT(dev->lock.hw_lock->lock)); - + /* FIXME: may require heavy-handed reset of hardware at this point, possibly processed via a callback to the X @@ -809,7 +838,7 @@ extern drm_ioctl_desc_t drm_compat_ioctls[]; /* drm_ioctl is called whenever a process performs an ioctl on /dev/drm. */ -int drm_ioctl(struct cdev *kdev, u_long cmd, caddr_t data, int flags, +int drm_ioctl(struct cdev *kdev, u_long cmd, caddr_t data, int flags, DRM_STRUCTPROC *p) { struct drm_device *dev = drm_get_device_from_kdev(kdev); @@ -964,11 +993,11 @@ drm_mmap_single(struct cdev *kdev, vm_ooffset_t *offset, vm_size_t size, struct drm_device *dev; dev = drm_get_device_from_kdev(kdev); - if ((dev->driver->driver_features & DRIVER_GEM) != 0) { - return (drm_gem_mmap_single(dev, offset, size, obj_res, nprot)); - } else if (dev->drm_ttm_bo != NULL) { - return (ttm_bo_mmap_single(dev->drm_ttm_bo, offset, size, + if (dev->drm_ttm_bdev != NULL) { + return (ttm_bo_mmap_single(dev->drm_ttm_bdev, offset, size, obj_res, nprot)); + } else if ((dev->driver->driver_features & DRIVER_GEM) != 0) { + return (drm_gem_mmap_single(dev, offset, size, obj_res, nprot)); } else { return (ENODEV); } @@ -984,14 +1013,9 @@ MODULE_DEPEND(DRIVER_NAME, linux, 1, 1, 1); #define LINUX_IOCTL_DRM_MAX 0x64ff static linux_ioctl_function_t drm_linux_ioctl; -static struct linux_ioctl_handler drm_handler = {drm_linux_ioctl, +static struct linux_ioctl_handler drm_handler = {drm_linux_ioctl, LINUX_IOCTL_DRM_MIN, LINUX_IOCTL_DRM_MAX}; -SYSINIT(drm_register, SI_SUB_KLD, SI_ORDER_MIDDLE, - linux_ioctl_register_handler, &drm_handler); -SYSUNINIT(drm_unregister, SI_SUB_KLD, SI_ORDER_MIDDLE, - linux_ioctl_unregister_handler, &drm_handler); - /* The bits for in/out are switched on Linux */ #define LINUX_IOC_IN IOC_OUT #define LINUX_IOC_OUT IOC_IN @@ -1007,13 +1031,45 @@ drm_linux_ioctl(DRM_STRUCTPROC *p, struct linux_ioctl_args* args) args->cmd |= IOC_IN; if (cmd & LINUX_IOC_OUT) args->cmd |= IOC_OUT; - + error = ioctl(p, (struct ioctl_args *)args); return error; } #endif /* DRM_LINUX */ + +static int +drm_core_init(void *arg) +{ + + drm_global_init(); + +#if DRM_LINUX + linux_ioctl_register_handler(&drm_handler); +#endif /* DRM_LINUX */ + + DRM_INFO("Initialized %s %d.%d.%d %s\n", + CORE_NAME, CORE_MAJOR, CORE_MINOR, CORE_PATCHLEVEL, CORE_DATE); + return 0; +} + +static void +drm_core_exit(void *arg) +{ + +#if DRM_LINUX + linux_ioctl_unregister_handler(&drm_handler); +#endif /* DRM_LINUX */ + + drm_global_release(); +} + +SYSINIT(drm_register, SI_SUB_KLD, SI_ORDER_MIDDLE, + drm_core_init, NULL); +SYSUNINIT(drm_unregister, SI_SUB_KLD, SI_ORDER_MIDDLE, + drm_core_exit, NULL); + bool dmi_check_system(const struct dmi_system_id *sysid) { @@ -1021,4 +1077,3 @@ dmi_check_system(const struct dmi_system_id *sysid) /* XXXKIB */ return (false); } - diff --git a/sys/dev/drm2/drm_edid.c b/sys/dev/drm2/drm_edid.c index 827677db569..86306b9d8fe 100644 --- a/sys/dev/drm2/drm_edid.c +++ b/sys/dev/drm2/drm_edid.c @@ -253,6 +253,8 @@ drm_do_probe_ddc_edid(device_t adapter, unsigned char *buf, int block, int len) { unsigned char start = block * EDID_LENGTH; + unsigned char segment = block >> 1; + unsigned char xfers = segment ? 3 : 2; int ret, retries = 5; /* The core i2c driver will automatically retry the transfer if the @@ -264,6 +266,11 @@ drm_do_probe_ddc_edid(device_t adapter, unsigned char *buf, do { struct iic_msg msgs[] = { { + .slave = DDC_SEGMENT_ADDR << 1, + .flags = 0, + .len = 1, + .buf = &segment, + }, { .slave = DDC_ADDR << 1, .flags = IIC_M_WR, .len = 1, @@ -275,7 +282,13 @@ drm_do_probe_ddc_edid(device_t adapter, unsigned char *buf, .buf = buf, } }; - ret = iicbus_transfer(adapter, msgs, 2); + + /* + * Avoid sending the segment addr to not upset non-compliant ddc + * monitors. + */ + ret = iicbus_transfer(adapter, &msgs[3 - xfers], xfers); + if (ret != 0) DRM_DEBUG_KMS("iicbus_transfer countdown %d error %d\n", retries, ret); diff --git a/sys/dev/drm2/drm_fixed.h b/sys/dev/drm2/drm_fixed.h new file mode 100644 index 00000000000..066a5b6ad4b --- /dev/null +++ b/sys/dev/drm2/drm_fixed.h @@ -0,0 +1,72 @@ +/* + * Copyright 2009 Red Hat Inc. + * + * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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. + * + * Authors: Dave Airlie + */ + +#include +__FBSDID("$FreeBSD$"); + +#ifndef DRM_FIXED_H +#define DRM_FIXED_H + +typedef union dfixed { + u32 full; +} fixed20_12; + + +#define dfixed_const(A) (u32)(((A) << 12))/* + ((B + 0.000122)*4096)) */ +#define dfixed_const_half(A) (u32)(((A) << 12) + 2048) +#define dfixed_const_666(A) (u32)(((A) << 12) + 2731) +#define dfixed_const_8(A) (u32)(((A) << 12) + 3277) +#define dfixed_mul(A, B) ((u64)((u64)(A).full * (B).full + 2048) >> 12) +#define dfixed_init(A) { .full = dfixed_const((A)) } +#define dfixed_init_half(A) { .full = dfixed_const_half((A)) } +#define dfixed_trunc(A) ((A).full >> 12) +#define dfixed_frac(A) ((A).full & ((1 << 12) - 1)) + +static inline u32 dfixed_floor(fixed20_12 A) +{ + u32 non_frac = dfixed_trunc(A); + + return dfixed_const(non_frac); +} + +static inline u32 dfixed_ceil(fixed20_12 A) +{ + u32 non_frac = dfixed_trunc(A); + + if (A.full > dfixed_const(non_frac)) + return dfixed_const(non_frac + 1); + else + return dfixed_const(non_frac); +} + +static inline u32 dfixed_div(fixed20_12 A, fixed20_12 B) +{ + u64 tmp = ((u64)A.full << 13); + + do_div(tmp, B.full); + tmp += 1; + tmp /= 2; + return lower_32_bits(tmp); +} +#endif diff --git a/sys/dev/drm2/drm_gem.c b/sys/dev/drm2/drm_gem.c index a7928390c2d..04b7659b1e0 100644 --- a/sys/dev/drm2/drm_gem.c +++ b/sys/dev/drm2/drm_gem.c @@ -121,7 +121,7 @@ drm_gem_private_object_init(struct drm_device *dev, struct drm_gem_object *obj, obj->vm_obj = NULL; obj->refcount = 1; - atomic_set(&obj->handle_count, 0); + atomic_store_rel_int(&obj->handle_count, 0); obj->size = size; return (0); @@ -163,7 +163,7 @@ void drm_gem_object_reference(struct drm_gem_object *obj) { - KASSERT(obj->refcount > 0, ("Dandling obj %p", obj)); + KASSERT(obj->refcount > 0, ("Dangling obj %p", obj)); refcount_acquire(&obj->refcount); } @@ -242,24 +242,40 @@ int drm_gem_handle_create(struct drm_file *file_priv, struct drm_gem_object *obj, uint32_t *handle) { - int error; + struct drm_device *dev = obj->dev; + int ret; - error = drm_gem_name_create(&file_priv->object_names, obj, handle); - if (error != 0) - return (error); + ret = drm_gem_name_create(&file_priv->object_names, obj, handle); + if (ret != 0) + return (ret); drm_gem_object_handle_reference(obj); + + if (dev->driver->gem_open_object) { + ret = dev->driver->gem_open_object(obj, file_priv); + if (ret) { + drm_gem_handle_delete(file_priv, *handle); + return ret; + } + } + return (0); } int drm_gem_handle_delete(struct drm_file *file_priv, uint32_t handle) { + struct drm_device *dev; struct drm_gem_object *obj; obj = drm_gem_names_remove(&file_priv->object_names, handle); if (obj == NULL) return (EINVAL); + + dev = obj->dev; + if (dev->driver->gem_close_object) + dev->driver->gem_close_object(obj, file_priv); drm_gem_object_handle_unreference_unlocked(obj); + return (0); } @@ -312,9 +328,17 @@ drm_gem_open(struct drm_device *dev, struct drm_file *file_priv) static int drm_gem_object_release_handle(uint32_t name, void *ptr, void *arg) { + struct drm_file *file_priv; struct drm_gem_object *obj; + struct drm_device *dev; + file_priv = arg; obj = ptr; + dev = obj->dev; + + if (dev->driver->gem_close_object) + dev->driver->gem_close_object(obj, file_priv); + drm_gem_object_handle_unreference(obj); return (0); } @@ -324,7 +348,7 @@ drm_gem_release(struct drm_device *dev, struct drm_file *file_priv) { drm_gem_names_foreach(&file_priv->object_names, - drm_gem_object_release_handle, NULL); + drm_gem_object_release_handle, file_priv); drm_gem_names_fini(&file_priv->object_names); } diff --git a/sys/dev/drm2/drm_gem_names.c b/sys/dev/drm2/drm_gem_names.c index 2577d132206..084d50f0478 100644 --- a/sys/dev/drm2/drm_gem_names.c +++ b/sys/dev/drm2/drm_gem_names.c @@ -132,12 +132,12 @@ drm_gem_name_create(struct drm_gem_names *names, void *p, uint32_t *name) { struct drm_gem_name *np; - np = malloc(sizeof(struct drm_gem_name), M_GEM_NAMES, M_WAITOK); - mtx_lock(&names->lock); if (*name != 0) { - mtx_unlock(&names->lock); return (EALREADY); } + + np = malloc(sizeof(struct drm_gem_name), M_GEM_NAMES, M_WAITOK); + mtx_lock(&names->lock); np->name = alloc_unr(names->unr); if (np->name == -1) { mtx_unlock(&names->lock); diff --git a/sys/dev/drm2/drm_ioctl.c b/sys/dev/drm2/drm_ioctl.c index b2c7affc593..96dcac2be0c 100644 --- a/sys/dev/drm2/drm_ioctl.c +++ b/sys/dev/drm2/drm_ioctl.c @@ -37,6 +37,7 @@ __FBSDID("$FreeBSD$"); */ #include +#include /* * Beginning in revision 1.1 of the DRM interface, getunique will return @@ -255,10 +256,6 @@ int drm_getcap(struct drm_device *dev, void *data, struct drm_file *file_priv) return 0; } - -#define DRM_IF_MAJOR 1 -#define DRM_IF_MINOR 2 - int drm_setversion(struct drm_device *dev, void *data, struct drm_file *file_priv) { diff --git a/sys/dev/drm2/drm_irq.c b/sys/dev/drm2/drm_irq.c index 0324e8cff1a..3a60fa971c0 100644 --- a/sys/dev/drm2/drm_irq.c +++ b/sys/dev/drm2/drm_irq.c @@ -210,7 +210,7 @@ struct timeval ns_to_timeval(const int64_t nsec) { struct timeval tv; - uint32_t rem; + long rem; if (nsec == 0) { tv.tv_sec = 0; @@ -786,7 +786,7 @@ int drm_vblank_get(struct drm_device *dev, int crtc) mtx_lock(&dev->vbl_lock); /* Going from 0->1 means we have to enable interrupts again */ - if (atomic_fetchadd_int(&dev->vblank_refcount[crtc], 1) == 0) { + if (atomic_add_return(1, &dev->vblank_refcount[crtc]) == 1) { mtx_lock(&dev->vblank_time_lock); if (!dev->vblank_enabled[crtc]) { /* Enable vblank irqs under vblank_time_lock protection. @@ -831,7 +831,7 @@ void drm_vblank_put(struct drm_device *dev, int crtc) ("Too many drm_vblank_put for crtc %d", crtc)); /* Last user schedules interrupt disable */ - if (atomic_fetchadd_int(&dev->vblank_refcount[crtc], -1) == 1 && + if (atomic_dec_and_test(&dev->vblank_refcount[crtc]) && (drm_vblank_offdelay > 0)) callout_reset(&dev->vblank_disable_callout, (drm_vblank_offdelay * DRM_HZ) / 1000, diff --git a/sys/dev/drm2/drm_linux_list.h b/sys/dev/drm2/drm_linux_list.h index 3b23a30de64..f21614395e2 100644 --- a/sys/dev/drm2/drm_linux_list.h +++ b/sys/dev/drm2/drm_linux_list.h @@ -144,6 +144,11 @@ list_del_init(struct list_head *entry) { &pos->member != (head); \ pos = n, n = list_entry(n->member.next, __typeof(*n), member)) +#define list_for_each_entry_safe_from(pos, n, head, member) \ + for (n = list_entry(pos->member.next, __typeof(*pos), member); \ + &pos->member != (head); \ + pos = n, n = list_entry(n->member.next, __typeof(*n), member)) + #define list_first_entry(ptr, type, member) \ list_entry((ptr)->next, type, member) diff --git a/sys/dev/drm2/drm_os_freebsd.h b/sys/dev/drm2/drm_os_freebsd.h new file mode 100644 index 00000000000..d3d987af984 --- /dev/null +++ b/sys/dev/drm2/drm_os_freebsd.h @@ -0,0 +1,144 @@ +/** + * \file drm_os_freebsd.h + * OS abstraction macros. + */ + +#include +__FBSDID("$FreeBSD$"); + +#if _BYTE_ORDER == _BIG_ENDIAN +#define __BIG_ENDIAN 4321 +#else +#define __LITTLE_ENDIAN 1234 +#endif + +#define cpu_to_le16(x) htole16(x) +#define le16_to_cpu(x) le16toh(x) +#define cpu_to_le32(x) htole32(x) +#define le32_to_cpu(x) le32toh(x) + +#define cpu_to_be16(x) htobe16(x) +#define be16_to_cpu(x) be16toh(x) +#define cpu_to_be32(x) htobe32(x) +#define be32_to_cpu(x) be32toh(x) +#define be32_to_cpup(x) be32toh(*x) + +typedef vm_paddr_t dma_addr_t; +typedef uint64_t u64; +typedef uint32_t u32; +typedef uint16_t u16; +typedef uint8_t u8; +typedef int64_t s64; +typedef int32_t s32; +typedef int16_t s16; +typedef int8_t s8; +typedef int32_t __be32; + +#define unlikely(x) __builtin_expect(!!(x), 0) +#define likely(x) __builtin_expect(!!(x), 1) +#define container_of(ptr, type, member) ({ \ + __typeof( ((type *)0)->member ) *__mptr = (ptr); \ + (type *)( (char *)__mptr - offsetof(type,member) );}) + +#define DRM_HZ hz +#define DRM_UDELAY(udelay) DELAY(udelay) +#define DRM_MDELAY(msecs) do { int loops = (msecs); \ + while (loops--) DELAY(1000); \ + } while (0) +#define DRM_MSLEEP(msecs) drm_msleep((msecs), "drm_msleep") +#define DRM_TIME_SLICE (hz/20) /* Time slice for GLXContexts */ + +#define do_div(a, b) ((a) /= (b)) +#define lower_32_bits(n) ((u32)(n)) + +#define min_t(type, x, y) ({ \ + type __min1 = (x); \ + type __min2 = (y); \ + __min1 < __min2 ? __min1 : __min2; }) + +#define max_t(type, x, y) ({ \ + type __max1 = (x); \ + type __max2 = (y); \ + __max1 > __max2 ? __max1 : __max2; }) + +#define memset_io(a, b, c) memset((a), (b), (c)) +#define memcpy_fromio(a, b, c) memcpy((a), (b), (c)) +#define memcpy_toio(a, b, c) memcpy((a), (b), (c)) + +/* XXXKIB what is the right code for the FreeBSD ? */ +/* kib@ used ENXIO here -- dumbbell@ */ +#define EREMOTEIO EIO +#define ERESTARTSYS ERESTART + +#define KTR_DRM KTR_DEV +#define KTR_DRM_REG KTR_SPARE3 + +#define PCI_VENDOR_ID_APPLE 0x106b +#define PCI_VENDOR_ID_ASUSTEK 0x1043 +#define PCI_VENDOR_ID_ATI 0x1002 +#define PCI_VENDOR_ID_DELL 0x1028 +#define PCI_VENDOR_ID_HP 0x103c +#define PCI_VENDOR_ID_IBM 0x1014 +#define PCI_VENDOR_ID_INTEL 0x8086 +#define PCI_VENDOR_ID_SERVERWORKS 0x1166 +#define PCI_VENDOR_ID_SONY 0x104d +#define PCI_VENDOR_ID_VIA 0x1106 + +#define DIV_ROUND_UP(n,d) (((n) + (d) - 1) / (d)) +#define hweight32(i) bitcount32(i) + +static inline unsigned long +roundup_pow_of_two(unsigned long x) +{ + return (1UL << flsl(x - 1)); +} + +/** + * ror32 - rotate a 32-bit value right + * @word: value to rotate + * @shift: bits to roll + * + * Source: include/linux/bitops.h + */ +static inline uint32_t ror32(uint32_t word, unsigned int shift) +{ + return (word >> shift) | (word << (32 - shift)); +} + +#define IS_ALIGNED(x, y) (((x) & ((y) - 1)) == 0) +#define get_unaligned(ptr) \ + ({ __typeof__(*(ptr)) __tmp; \ + memcpy(&__tmp, (ptr), sizeof(*(ptr))); __tmp; }) + +#if _BYTE_ORDER == _LITTLE_ENDIAN +/* Taken from linux/include/linux/unaligned/le_struct.h. */ +struct __una_u32 { u32 x; } __packed; + +static inline u32 __get_unaligned_cpu32(const void *p) +{ + const struct __una_u32 *ptr = (const struct __una_u32 *)p; + return ptr->x; +} + +static inline u32 get_unaligned_le32(const void *p) +{ + return __get_unaligned_cpu32((const u8 *)p); +} +#else +/* Taken from linux/include/linux/unaligned/le_byteshift.h. */ +static inline u32 __get_unaligned_le32(const u8 *p) +{ + return p[0] | p[1] << 8 | p[2] << 16 | p[3] << 24; +} + +static inline u32 get_unaligned_le32(const void *p) +{ + return __get_unaligned_le32((const u8 *)p); +} +#endif + +#define KIB_NOTYET() \ +do { \ + if (drm_debug_flag && drm_notyet_flag) \ + printf("NOTYET: %s at %s:%d\n", __func__, __FILE__, __LINE__); \ +} while (0) diff --git a/sys/dev/drm2/drm_pci.c b/sys/dev/drm2/drm_pci.c index 3d7a3cc7feb..ab45278d1af 100644 --- a/sys/dev/drm2/drm_pci.c +++ b/sys/dev/drm2/drm_pci.c @@ -123,3 +123,53 @@ drm_pci_free(struct drm_device *dev, drm_dma_handle_t *dmah) } /*@}*/ + +int drm_pcie_get_speed_cap_mask(struct drm_device *dev, u32 *mask) +{ + device_t root; + int pos; + u32 lnkcap = 0, lnkcap2 = 0; + + *mask = 0; + if (!drm_device_is_pcie(dev)) + return -EINVAL; + + root = device_get_parent(dev->device); + + pos = 0; + pci_find_cap(root, PCIY_EXPRESS, &pos); + if (!pos) + return -EINVAL; + + /* we've been informed via and serverworks don't make the cut */ + if (pci_get_vendor(root) == PCI_VENDOR_ID_VIA || + pci_get_vendor(root) == PCI_VENDOR_ID_SERVERWORKS) + return -EINVAL; + + lnkcap = pci_read_config(root, pos + PCIER_LINK_CAP, 4); + lnkcap2 = pci_read_config(root, pos + PCIER_LINK_CAP2, 4); + + lnkcap &= PCIEM_LINK_CAP_MAX_SPEED; + lnkcap2 &= 0xfe; + +#define PCI_EXP_LNKCAP2_SLS_2_5GB 0x02 /* Supported Link Speed 2.5GT/s */ +#define PCI_EXP_LNKCAP2_SLS_5_0GB 0x04 /* Supported Link Speed 5.0GT/s */ +#define PCI_EXP_LNKCAP2_SLS_8_0GB 0x08 /* Supported Link Speed 8.0GT/s */ + + if (lnkcap2) { /* PCIE GEN 3.0 */ + if (lnkcap2 & PCI_EXP_LNKCAP2_SLS_2_5GB) + *mask |= DRM_PCIE_SPEED_25; + if (lnkcap2 & PCI_EXP_LNKCAP2_SLS_5_0GB) + *mask |= DRM_PCIE_SPEED_50; + if (lnkcap2 & PCI_EXP_LNKCAP2_SLS_8_0GB) + *mask |= DRM_PCIE_SPEED_80; + } else { + if (lnkcap & 1) + *mask |= DRM_PCIE_SPEED_25; + if (lnkcap & 2) + *mask |= DRM_PCIE_SPEED_50; + } + + DRM_INFO("probing gen 2 caps for device %x:%x = %x/%x\n", pci_get_vendor(root), pci_get_device(root), lnkcap, lnkcap2); + return 0; +} diff --git a/sys/dev/drm2/drm_pciids.h b/sys/dev/drm2/drm_pciids.h index 0a9bc4ddb61..50fb932adc5 100644 --- a/sys/dev/drm2/drm_pciids.h +++ b/sys/dev/drm2/drm_pciids.h @@ -1,14 +1,348 @@ /* * $FreeBSD$ */ + /* - This file is auto-generated from the drm_pciids.txt in the DRM CVS - Please contact dri-devel@lists.sf.net to add new cards to this list -*/ + * Generated by gen-drm_pciids from: + * o previous FreeBSD's drm_pciids.h + * o Linux' drm_pciids.h + * o the PCI ID repository (http://pciids.sourceforge.net/) + * + * See tools/tools/drm/gen-drm_pciids. + */ + +#define ffb_PCI_IDS \ + {0, 0, 0, NULL} + +#define gamma_PCI_IDS \ + {0x3D3D, 0x0008, 0, "3DLabs GLINT Gamma G1"}, \ + {0, 0, 0, NULL} + +#define i810_PCI_IDS \ + {0x8086, 0x1132, 0, "Intel i815 GMCH"}, \ + {0x8086, 0x7121, 0, "Intel i810 GMCH"}, \ + {0x8086, 0x7123, 0, "Intel i810-DC100 GMCH"}, \ + {0x8086, 0x7125, 0, "Intel i810E GMCH"}, \ + {0, 0, 0, NULL} + +#define i830_PCI_IDS \ + {0x8086, 0x2562, 0, "Intel i845G GMCH"}, \ + {0x8086, 0x2572, 0, "Intel i865G GMCH"}, \ + {0x8086, 0x3577, 0, "Intel i830M GMCH"}, \ + {0x8086, 0x3582, 0, "Intel i852GM/i855GM GMCH"}, \ + {0, 0, 0, NULL} + +#define i915_PCI_IDS \ + {0x8086, 0x0042, CHIP_I9XX|CHIP_I915, "Intel IronLake"}, \ + {0x8086, 0x0046, CHIP_I9XX|CHIP_I915, "Intel IronLake"}, \ + {0x8086, 0x0102, CHIP_I9XX|CHIP_I915, "Intel SandyBridge"}, \ + {0x8086, 0x0106, CHIP_I9XX|CHIP_I915, "Intel SandyBridge (M)"}, \ + {0x8086, 0x010A, CHIP_I9XX|CHIP_I915, "Intel SandyBridge (M)"}, \ + {0x8086, 0x0112, CHIP_I9XX|CHIP_I915, "Intel SandyBridge"}, \ + {0x8086, 0x0116, CHIP_I9XX|CHIP_I915, "Intel SandyBridge (M)"}, \ + {0x8086, 0x0122, CHIP_I9XX|CHIP_I915, "Intel SandyBridge"}, \ + {0x8086, 0x0126, CHIP_I9XX|CHIP_I915, "Intel SandyBridge (M)"}, \ + {0x8086, 0x0152, CHIP_I9XX|CHIP_I915, "Intel IvyBridge"}, \ + {0x8086, 0x0156, CHIP_I9XX|CHIP_I915, "Intel IvyBridge (M)"}, \ + {0x8086, 0x015A, CHIP_I9XX|CHIP_I915, "Intel IvyBridge (S)"}, \ + {0x8086, 0x0162, CHIP_I9XX|CHIP_I915, "Intel IvyBridge"}, \ + {0x8086, 0x0166, CHIP_I9XX|CHIP_I915, "Intel IvyBridge (M)"}, \ + {0x8086, 0x016A, CHIP_I9XX|CHIP_I915, "Intel IvyBridge (S)"}, \ + {0x8086, 0x2562, CHIP_I8XX, "Intel i845G GMCH"}, \ + {0x8086, 0x2572, CHIP_I8XX, "Intel i865G GMCH"}, \ + {0x8086, 0x2582, CHIP_I9XX|CHIP_I915, "Intel i915G"}, \ + {0x8086, 0x258A, CHIP_I9XX|CHIP_I915, "Intel E7221 (i915)"}, \ + {0x8086, 0x2592, CHIP_I9XX|CHIP_I915, "Intel i915GM"}, \ + {0x8086, 0x2772, CHIP_I9XX|CHIP_I915, "Intel i945G"}, \ + {0x8086, 0x27A2, CHIP_I9XX|CHIP_I915, "Intel i945GM"}, \ + {0x8086, 0x27AE, CHIP_I9XX|CHIP_I915, "Intel i945GME"}, \ + {0x8086, 0x2972, CHIP_I9XX|CHIP_I965, "Intel i946GZ"}, \ + {0x8086, 0x2982, CHIP_I9XX|CHIP_I965, "Intel i965G"}, \ + {0x8086, 0x2992, CHIP_I9XX|CHIP_I965, "Intel i965Q"}, \ + {0x8086, 0x29A2, CHIP_I9XX|CHIP_I965, "Intel i965G"}, \ + {0x8086, 0x29B2, CHIP_I9XX|CHIP_I915, "Intel Q35"}, \ + {0x8086, 0x29C2, CHIP_I9XX|CHIP_I915, "Intel G33"}, \ + {0x8086, 0x29D2, CHIP_I9XX|CHIP_I915, "Intel Q33"}, \ + {0x8086, 0x2A02, CHIP_I9XX|CHIP_I965, "Intel i965GM"}, \ + {0x8086, 0x2A12, CHIP_I9XX|CHIP_I965, "Intel i965GME/GLE"}, \ + {0x8086, 0x2A42, CHIP_I9XX|CHIP_I965, "Mobile Intel® GM45 Express Chipset"}, \ + {0x8086, 0x2E02, CHIP_I9XX|CHIP_I965, "Intel Eaglelake"}, \ + {0x8086, 0x2E12, CHIP_I9XX|CHIP_I965, "Intel Q45/Q43"}, \ + {0x8086, 0x2E22, CHIP_I9XX|CHIP_I965, "Intel G45/G43"}, \ + {0x8086, 0x2E32, CHIP_I9XX|CHIP_I965, "Intel G41"}, \ + {0x8086, 0x2E42, CHIP_I9XX|CHIP_I915, "Intel G43 ?"}, \ + {0x8086, 0x2E92, CHIP_I9XX|CHIP_I915, "Intel G43 ?"}, \ + {0x8086, 0x3577, CHIP_I8XX, "Intel i830M GMCH"}, \ + {0x8086, 0x3582, CHIP_I8XX, "Intel i852GM/i855GM GMCH"}, \ + {0x8086, 0x358E, CHIP_I8XX, "Intel i852GM/i855GM GMCH"}, \ + {0x8086, 0xA001, CHIP_I9XX|CHIP_I965, "Intel Pineview"}, \ + {0x8086, 0xA011, CHIP_I9XX|CHIP_I965, "Intel Pineview (M)"}, \ + {0, 0, 0, NULL} + +#define imagine_PCI_IDS \ + {0x105D, 0x2309, IMAGINE_128, "Imagine 128"}, \ + {0x105D, 0x2339, IMAGINE_128_2, "Imagine 128-II"}, \ + {0x105D, 0x493D, IMAGINE_T2R, "Ticket to Ride"}, \ + {0x105D, 0x5348, IMAGINE_REV4, "Revolution IV"}, \ + {0, 0, 0, NULL} + +#define mach64_PCI_IDS \ + {0x1002, 0x4742, 0, "3D Rage Pro AGP 1X/2X"}, \ + {0x1002, 0x4744, 0, "3D Rage Pro AGP 1X"}, \ + {0x1002, 0x4749, 0, "3D Rage Pro"}, \ + {0x1002, 0x474C, 0, "Rage XC"}, \ + {0x1002, 0x474D, 0, "Rage XL AGP 2X"}, \ + {0x1002, 0x474E, 0, "Rage XC AGP"}, \ + {0x1002, 0x474F, 0, "Rage XL"}, \ + {0x1002, 0x4750, 0, "3D Rage Pro 215GP"}, \ + {0x1002, 0x4751, 0, "3D Rage Pro 215GQ"}, \ + {0x1002, 0x4752, 0, "Rage XL"}, \ + {0x1002, 0x4753, 0, "Rage XC"}, \ + {0x1002, 0x4C42, 0, "3D Rage LT Pro AGP-133"}, \ + {0x1002, 0x4C44, 0, "3D Rage LT Pro AGP-66"}, \ + {0x1002, 0x4C49, 0, "3D Rage LT Pro"}, \ + {0x1002, 0x4C4D, 0, "Rage Mobility P/M AGP 2X"}, \ + {0x1002, 0x4C4E, 0, "Rage Mobility L AGP 2X"}, \ + {0x1002, 0x4C50, 0, "3D Rage LT Pro"}, \ + {0x1002, 0x4C51, 0, "3D Rage LT Pro"}, \ + {0x1002, 0x4C52, 0, "Rage Mobility P/M"}, \ + {0x1002, 0x4C53, 0, "Rage Mobility L"}, \ + {0, 0, 0, NULL} + +#define mga_PCI_IDS \ + {0x102B, 0x0520, MGA_CARD_TYPE_G200, "Matrox G200 (PCI)"}, \ + {0x102B, 0x0521, MGA_CARD_TYPE_G200, "Matrox G200 (AGP)"}, \ + {0x102B, 0x0525, MGA_CARD_TYPE_G400, "Matrox G400/G450 (AGP)"}, \ + {0x102B, 0x2527, MGA_CARD_TYPE_G550, "Matrox G550 (AGP)"}, \ + {0, 0, 0, NULL} + +#define nv_PCI_IDS \ + {0x10DE, 0x0020, NV04, "NVidia RIVA TNT"}, \ + {0x10DE, 0x0028, NV04, "NVidia RIVA TNT2"}, \ + {0x10DE, 0x0029, NV04, "NVidia RIVA TNT2 Ultra"}, \ + {0x10DE, 0x002A, NV04, "NVidia Unknown TNT2"}, \ + {0x10DE, 0x002C, NV04, "NVidia Vanta"}, \ + {0x10DE, 0x002D, NV04, "NVidia RIVA TNT2 Model 64"}, \ + {0x10DE, 0x0040, NV40, "NVidia GeForce 6800 Ultra"}, \ + {0x10DE, 0x0041, NV40, "NVidia GeForce 6800"}, \ + {0x10DE, 0x0042, NV40, "NVidia GeForce 6800 LE"}, \ + {0x10DE, 0x0043, NV40, "NVidia 0x0043"}, \ + {0x10DE, 0x0045, NV40, "NVidia GeForce 6800 GT"}, \ + {0x10DE, 0x0046, NV40, "NVidia GeForce 6800 GT"}, \ + {0x10DE, 0x0049, NV40, "NVidia 0x0049"}, \ + {0x10DE, 0x004E, NV40, "NVidia Quadro FX 4000"}, \ + {0x10DE, 0x0090, NV40, "NVidia 0x0090"}, \ + {0x10DE, 0x0091, NV40, "NVidia GeForce 7800 GTX"}, \ + {0x10DE, 0x0092, NV40, "NVidia 0x0092"}, \ + {0x10DE, 0x0093, NV40, "NVidia 0x0093"}, \ + {0x10DE, 0x0094, NV40, "NVidia 0x0094"}, \ + {0x10DE, 0x0098, NV40, "NVidia 0x0098"}, \ + {0x10DE, 0x0099, NV40, "NVidia GeForce Go 7800 GTX"}, \ + {0x10DE, 0x009C, NV40, "NVidia 0x009C"}, \ + {0x10DE, 0x009D, NV40, "NVidia Quadro FX 4500"}, \ + {0x10DE, 0x009E, NV40, "NVidia 0x009E"}, \ + {0x10DE, 0x00A0, NV04, "NVidia Aladdin TNT2"}, \ + {0x10DE, 0x00C0, NV40, "NVidia 0x00C0"}, \ + {0x10DE, 0x00C1, NV40, "NVidia GeForce 6800"}, \ + {0x10DE, 0x00C2, NV40, "NVidia GeForce 6800 LE"}, \ + {0x10DE, 0x00C8, NV40, "NVidia GeForce Go 6800"}, \ + {0x10DE, 0x00C9, NV40, "NVidia GeForce Go 6800 Ultra"}, \ + {0x10DE, 0x00CC, NV40, "NVidia Quadro FX Go1400"}, \ + {0x10DE, 0x00CD, NV40, "NVidia Quadro FX 3450/4000 SDI"}, \ + {0x10DE, 0x00CE, NV40, "NVidia Quadro FX 1400"}, \ + {0x10DE, 0x00F0, NV40, "Nvidia GeForce 6600 GT"}, \ + {0x10DE, 0x00F1, NV40, "Nvidia GeForce 6600 GT"}, \ + {0x10DE, 0x0100, NV10, "NVidia GeForce 256"}, \ + {0x10DE, 0x0101, NV10, "NVidia GeForce DDR"}, \ + {0x10DE, 0x0103, NV10, "NVidia Quadro"}, \ + {0x10DE, 0x0110, NV10, "NVidia GeForce2 MX/MX 400"}, \ + {0x10DE, 0x0111, NV10, "NVidia GeForce2 MX 100/200"}, \ + {0x10DE, 0x0112, NV10, "NVidia GeForce2 Go"}, \ + {0x10DE, 0x0113, NV10, "NVidia Quadro2 MXR/EX/Go"}, \ + {0x10DE, 0x0140, NV40, "NVidia GeForce 6600 GT"}, \ + {0x10DE, 0x0141, NV40, "NVidia GeForce 6600"}, \ + {0x10DE, 0x0142, NV40, "NVidia GeForce 6600 LE"}, \ + {0x10DE, 0x0143, NV40, "NVidia 0x0143"}, \ + {0x10DE, 0x0144, NV40, "NVidia GeForce Go 6600"}, \ + {0x10DE, 0x0145, NV40, "NVidia GeForce 6610 XL"}, \ + {0x10DE, 0x0146, NV40, "NVidia GeForce Go 6600 TE/6200 TE"}, \ + {0x10DE, 0x0147, NV40, "NVidia GeForce 6700 XL"}, \ + {0x10DE, 0x0148, NV40, "NVidia GeForce Go 6600"}, \ + {0x10DE, 0x0149, NV40, "NVidia GeForce Go 6600 GT"}, \ + {0x10DE, 0x014B, NV40, "NVidia 0x014B"}, \ + {0x10DE, 0x014C, NV40, "NVidia 0x014C"}, \ + {0x10DE, 0x014D, NV40, "NVidia 0x014D"}, \ + {0x10DE, 0x014E, NV40, "NVidia Quadro FX 540"}, \ + {0x10DE, 0x014F, NV40, "NVidia GeForce 6200"}, \ + {0x10DE, 0x0150, NV10, "NVidia GeForce2 GTS"}, \ + {0x10DE, 0x0151, NV10, "NVidia GeForce2 Ti"}, \ + {0x10DE, 0x0152, NV10, "NVidia GeForce2 Ultra"}, \ + {0x10DE, 0x0153, NV10, "NVidia Quadro2 Pro"}, \ + {0x10DE, 0x0160, NV40, "NVidia 0x0160"}, \ + {0x10DE, 0x0161, NV40, "NVidia GeForce 6200 TurboCache(TM)"}, \ + {0x10DE, 0x0162, NV40, "NVidia GeForce 6200SE TurboCache(TM)"}, \ + {0x10DE, 0x0163, NV40, "NVidia 0x0163"}, \ + {0x10DE, 0x0164, NV40, "NVidia GeForce Go 6200"}, \ + {0x10DE, 0x0165, NV40, "NVidia Quadro NVS 285"}, \ + {0x10DE, 0x0166, NV40, "NVidia GeForce Go 6400"}, \ + {0x10DE, 0x0167, NV40, "NVidia GeForce Go 6200"}, \ + {0x10DE, 0x0168, NV40, "NVidia GeForce Go 6400"}, \ + {0x10DE, 0x0169, NV40, "NVidia 0x0169"}, \ + {0x10DE, 0x016B, NV40, "NVidia 0x016B"}, \ + {0x10DE, 0x016C, NV40, "NVidia 0x016C"}, \ + {0x10DE, 0x016D, NV40, "NVidia 0x016D"}, \ + {0x10DE, 0x016E, NV40, "NVidia 0x016E"}, \ + {0x10DE, 0x0170, NV10, "NVidia GeForce4 MX 460"}, \ + {0x10DE, 0x0171, NV10, "NVidia GeForce4 MX 440"}, \ + {0x10DE, 0x0172, NV10, "NVidia GeForce4 MX 420"}, \ + {0x10DE, 0x0173, NV10, "NVidia GeForce4 MX 440-SE"}, \ + {0x10DE, 0x0174, NV10, "NVidia GeForce4 440 Go"}, \ + {0x10DE, 0x0175, NV10, "NVidia GeForce4 420 Go"}, \ + {0x10DE, 0x0176, NV10, "NVidia GeForce4 420 Go 32M"}, \ + {0x10DE, 0x0177, NV10, "NVidia GeForce4 460 Go"}, \ + {0x10DE, 0x0178, NV10, "NVidia Quadro4 550 XGL"}, \ + {0x10DE, 0x0179, NV10, "NVidia GeForce4"}, \ + {0x10DE, 0x017A, NV10, "NVidia Quadro4 NVS"}, \ + {0x10DE, 0x017C, NV10, "NVidia Quadro4 500 GoGL"}, \ + {0x10DE, 0x017D, NV10, "NVidia GeForce4 410 Go 16M"}, \ + {0x10DE, 0x0181, NV10, "NVidia GeForce4 MX 440 with AGP8X"}, \ + {0x10DE, 0x0182, NV10, "NVidia GeForce4 MX 440SE with AGP8X"}, \ + {0x10DE, 0x0183, NV10, "NVidia GeForce4 MX 420 with AGP8X"}, \ + {0x10DE, 0x0185, NV10, "NVidia GeForce4 MX 4000"}, \ + {0x10DE, 0x0186, NV10, "NVidia GeForce4 448 Go"}, \ + {0x10DE, 0x0187, NV10, "NVidia GeForce4 488 Go"}, \ + {0x10DE, 0x0188, NV10, "NVidia Quadro4 580 XGL"}, \ + {0x10DE, 0x0189, NV10, "NVidia GeForce4 MX with AGP8X (Mac)"}, \ + {0x10DE, 0x018A, NV10, "NVidia Quadro4 280 NVS"}, \ + {0x10DE, 0x018B, NV10, "NVidia Quadro4 380 XGL"}, \ + {0x10DE, 0x018C, NV10, "NVidia Quadro NVS 50 PCI"}, \ + {0x10DE, 0x018D, NV10, "NVidia GeForce4 448 Go"}, \ + {0x10DE, 0x01A0, NV10, "NVidia GeForce2 Integrated GPU"}, \ + {0x10DE, 0x01F0, NV10, "NVidia GeForce4 MX Integrated GPU"}, \ + {0x10DE, 0x0200, NV20, "NVidia GeForce3"}, \ + {0x10DE, 0x0201, NV20, "NVidia GeForce3 Ti 200"}, \ + {0x10DE, 0x0202, NV20, "NVidia GeForce3 Ti 500"}, \ + {0x10DE, 0x0203, NV20, "NVidia Quadro DCC"}, \ + {0x10DE, 0x0210, NV40, "NVidia 0x0210"}, \ + {0x10DE, 0x0211, NV40, "NVidia GeForce 6800"}, \ + {0x10DE, 0x0212, NV40, "NVidia GeForce 6800 LE"}, \ + {0x10DE, 0x0215, NV40, "NVidia GeForce 6800 GT"}, \ + {0x10DE, 0x0220, NV40, "NVidia 0x0220"}, \ + {0x10DE, 0x0221, NV40, "NVidia GeForce 6200"}, \ + {0x10DE, 0x0222, NV40, "NVidia 0x0222"}, \ + {0x10DE, 0x0228, NV40, "NVidia 0x0228"}, \ + {0x10DE, 0x0250, NV20, "NVidia GeForce4 Ti 4600"}, \ + {0x10DE, 0x0251, NV20, "NVidia GeForce4 Ti 4400"}, \ + {0x10DE, 0x0252, NV20, "NVidia 0x0252"}, \ + {0x10DE, 0x0253, NV20, "NVidia GeForce4 Ti 4200"}, \ + {0x10DE, 0x0258, NV20, "NVidia Quadro4 900 XGL"}, \ + {0x10DE, 0x0259, NV20, "NVidia Quadro4 750 XGL"}, \ + {0x10DE, 0x025B, NV20, "NVidia Quadro4 700 XGL"}, \ + {0x10DE, 0x0280, NV20, "NVidia GeForce4 Ti 4800"}, \ + {0x10DE, 0x0281, NV20, "NVidia GeForce4 Ti 4200 with AGP8X"}, \ + {0x10DE, 0x0282, NV20, "NVidia GeForce4 Ti 4800 SE"}, \ + {0x10DE, 0x0286, NV20, "NVidia GeForce4 4200 Go"}, \ + {0x10DE, 0x0288, NV20, "NVidia Quadro4 980 XGL"}, \ + {0x10DE, 0x0289, NV20, "NVidia Quadro4 780 XGL"}, \ + {0x10DE, 0x028C, NV20, "NVidia Quadro4 700 GoGL"}, \ + {0x10DE, 0x0301, NV30, "NVidia GeForce FX 5800 Ultra"}, \ + {0x10DE, 0x0302, NV30, "NVidia GeForce FX 5800"}, \ + {0x10DE, 0x0308, NV30, "NVidia Quadro FX 2000"}, \ + {0x10DE, 0x0309, NV30, "NVidia Quadro FX 1000"}, \ + {0x10DE, 0x0311, NV30, "NVidia GeForce FX 5600 Ultra"}, \ + {0x10DE, 0x0312, NV30, "NVidia GeForce FX 5600"}, \ + {0x10DE, 0x0313, NV30, "NVidia 0x0313"}, \ + {0x10DE, 0x0314, NV30, "NVidia GeForce FX 5600SE"}, \ + {0x10DE, 0x0316, NV30, "NVidia 0x0316"}, \ + {0x10DE, 0x0317, NV30, "NVidia 0x0317"}, \ + {0x10DE, 0x031A, NV30, "NVidia GeForce FX Go5600"}, \ + {0x10DE, 0x031B, NV30, "NVidia GeForce FX Go5650"}, \ + {0x10DE, 0x031C, NV30, "NVidia Quadro FX Go700"}, \ + {0x10DE, 0x031D, NV30, "NVidia 0x031D"}, \ + {0x10DE, 0x031E, NV30, "NVidia 0x031E"}, \ + {0x10DE, 0x031F, NV30, "NVidia 0x031F"}, \ + {0x10DE, 0x0320, NV30, "NVidia GeForce FX 5200"}, \ + {0x10DE, 0x0321, NV30, "NVidia GeForce FX 5200 Ultra"}, \ + {0x10DE, 0x0322, NV30, "NVidia GeForce FX 5200"}, \ + {0x10DE, 0x0323, NV30, "NVidia GeForce FX 5200SE"}, \ + {0x10DE, 0x0324, NV30, "NVidia GeForce FX Go5200"}, \ + {0x10DE, 0x0325, NV30, "NVidia GeForce FX Go5250"}, \ + {0x10DE, 0x0326, NV30, "NVidia GeForce FX 5500"}, \ + {0x10DE, 0x0327, NV30, "NVidia GeForce FX 5100"}, \ + {0x10DE, 0x0328, NV30, "NVidia GeForce FX Go5200 32M/64M"}, \ + {0x10DE, 0x0329, NV30, "NVidia GeForce FX 5200 (Mac)"}, \ + {0x10DE, 0x032A, NV30, "NVidia Quadro NVS 280 PCI"}, \ + {0x10DE, 0x032B, NV30, "NVidia Quadro FX 500/600 PCI"}, \ + {0x10DE, 0x032C, NV30, "NVidia GeForce FX Go53xx Series"}, \ + {0x10DE, 0x032D, NV30, "NVidia GeForce FX Go5100"}, \ + {0x10DE, 0x032F, NV30, "NVidia 0x032F"}, \ + {0x10DE, 0x0330, NV30, "NVidia GeForce FX 5900 Ultra"}, \ + {0x10DE, 0x0331, NV30, "NVidia GeForce FX 5900"}, \ + {0x10DE, 0x0332, NV30, "NVidia GeForce FX 5900XT"}, \ + {0x10DE, 0x0333, NV30, "NVidia GeForce FX 5950 Ultra"}, \ + {0x10DE, 0x0334, NV30, "NVidia GeForce FX 5900ZT"}, \ + {0x10DE, 0x0338, NV30, "NVidia Quadro FX 3000"}, \ + {0x10DE, 0x033F, NV30, "NVidia Quadro FX 700"}, \ + {0x10DE, 0x0341, NV30, "NVidia GeForce FX 5700 Ultra"}, \ + {0x10DE, 0x0342, NV30, "NVidia GeForce FX 5700"}, \ + {0x10DE, 0x0343, NV30, "NVidia GeForce FX 5700LE"}, \ + {0x10DE, 0x0344, NV30, "NVidia GeForce FX 5700VE"}, \ + {0x10DE, 0x0345, NV30, "NVidia 0x0345"}, \ + {0x10DE, 0x0347, NV30, "NVidia GeForce FX Go5700"}, \ + {0x10DE, 0x0348, NV30, "NVidia GeForce FX Go5700"}, \ + {0x10DE, 0x0349, NV30, "NVidia 0x0349"}, \ + {0x10DE, 0x034B, NV30, "NVidia 0x034B"}, \ + {0x10DE, 0x034C, NV30, "NVidia Quadro FX Go1000"}, \ + {0x10DE, 0x034E, NV30, "NVidia Quadro FX 1100"}, \ + {0x10DE, 0x034F, NV30, "NVidia 0x034F"}, \ + {0, 0, 0, NULL} + +#define r128_PCI_IDS \ + {0x1002, 0x4C45, 0, "ATI Rage 128 Mobility LE (PCI)"}, \ + {0x1002, 0x4C46, 0, "ATI Rage 128 Mobility LF (AGP)"}, \ + {0x1002, 0x4D46, 0, "ATI Rage 128 Mobility MF (AGP)"}, \ + {0x1002, 0x4D4C, 0, "ATI Rage 128 Mobility ML (AGP)"}, \ + {0x1002, 0x5041, 0, "ATI Rage 128 Pro PA (PCI)"}, \ + {0x1002, 0x5042, 0, "ATI Rage 128 Pro PB (AGP)"}, \ + {0x1002, 0x5043, 0, "ATI Rage 128 Pro PC (AGP)"}, \ + {0x1002, 0x5044, 0, "ATI Rage 128 Pro PD (PCI)"}, \ + {0x1002, 0x5045, 0, "ATI Rage 128 Pro PE (AGP)"}, \ + {0x1002, 0x5046, 0, "ATI Rage 128 Pro PF (AGP)"}, \ + {0x1002, 0x5047, 0, "ATI Rage 128 Pro PG (PCI)"}, \ + {0x1002, 0x5048, 0, "ATI Rage 128 Pro PH (AGP)"}, \ + {0x1002, 0x5049, 0, "ATI Rage 128 Pro PI (AGP)"}, \ + {0x1002, 0x504A, 0, "ATI Rage 128 Pro PJ (PCI)"}, \ + {0x1002, 0x504B, 0, "ATI Rage 128 Pro PK (AGP)"}, \ + {0x1002, 0x504C, 0, "ATI Rage 128 Pro PL (AGP)"}, \ + {0x1002, 0x504D, 0, "ATI Rage 128 Pro PM (PCI)"}, \ + {0x1002, 0x504E, 0, "ATI Rage 128 Pro PN (AGP)"}, \ + {0x1002, 0x504F, 0, "ATI Rage 128 Pro PO (AGP)"}, \ + {0x1002, 0x5050, 0, "ATI Rage 128 Pro PP (PCI)"}, \ + {0x1002, 0x5051, 0, "ATI Rage 128 Pro PQ (AGP)"}, \ + {0x1002, 0x5052, 0, "ATI Rage 128 Pro PR (PCI)"}, \ + {0x1002, 0x5053, 0, "ATI Rage 128 Pro PS (PCI)"}, \ + {0x1002, 0x5054, 0, "ATI Rage 128 Pro PT (AGP)"}, \ + {0x1002, 0x5055, 0, "ATI Rage 128 Pro PU (AGP)"}, \ + {0x1002, 0x5056, 0, "ATI Rage 128 Pro PV (PCI)"}, \ + {0x1002, 0x5057, 0, "ATI Rage 128 Pro PW (AGP)"}, \ + {0x1002, 0x5058, 0, "ATI Rage 128 Pro PX (AGP)"}, \ + {0x1002, 0x5245, 0, "ATI Rage 128 RE (PCI)"}, \ + {0x1002, 0x5246, 0, "ATI Rage 128 RF (AGP)"}, \ + {0x1002, 0x5247, 0, "ATI Rage 128 RG (AGP)"}, \ + {0x1002, 0x524B, 0, "ATI Rage 128 RK (PCI)"}, \ + {0x1002, 0x524C, 0, "ATI Rage 128 RL (AGP)"}, \ + {0x1002, 0x534D, 0, "ATI Rage 128 SM (AGP)"}, \ + {0x1002, 0x5446, 0, "ATI Rage 128 Pro Ultra TF (AGP)"}, \ + {0x1002, 0x544C, 0, "ATI Rage 128 Pro Ultra TL (AGP)"}, \ + {0x1002, 0x5452, 0, "ATI Rage 128 Pro Ultra TR (AGP)"}, \ + {0, 0, 0, NULL} + #define radeon_PCI_IDS \ {0x1002, 0x3150, CHIP_RV380|RADEON_IS_MOBILITY, "ATI Radeon Mobility X600 M24"}, \ + {0x1002, 0x3151, CHIP_RV380|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP, "M24 [FireMV 2400]"}, \ {0x1002, 0x3152, CHIP_RV380|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP, "ATI Radeon Mobility X300 M24"}, \ {0x1002, 0x3154, CHIP_RV380|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP, "ATI FireGL M24 GL"}, \ + {0x1002, 0x3155, CHIP_RV380|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP, "RV380 [FireMV 2400]"}, \ {0x1002, 0x3E50, CHIP_RV380|RADEON_NEW_MEMMAP, "ATI Radeon RV380 X600"}, \ {0x1002, 0x3E54, CHIP_RV380|RADEON_NEW_MEMMAP, "ATI FireGL V3200 RV380"}, \ {0x1002, 0x4136, CHIP_RS100|RADEON_IS_IGP, "ATI Radeon RS100 IGP 320"}, \ @@ -46,6 +380,7 @@ {0x1002, 0x4A4F, CHIP_R420|RADEON_NEW_MEMMAP, "ATI Radeon JO R420 X800 SE"}, \ {0x1002, 0x4A50, CHIP_R420|RADEON_NEW_MEMMAP, "ATI Radeon JP R420 X800 XT PE"}, \ {0x1002, 0x4A54, CHIP_R420|RADEON_NEW_MEMMAP, "ATI Radeon JT R420 AIW X800 VE"}, \ + {0x1002, 0x4B48, CHIP_R420|RADEON_NEW_MEMMAP, "R481 [Radeon X850 PCIe]"}, \ {0x1002, 0x4B49, CHIP_R420|RADEON_NEW_MEMMAP, "ATI Radeon R481 X850 XT"}, \ {0x1002, 0x4B4A, CHIP_R420|RADEON_NEW_MEMMAP, "ATI Radeon R481 X850 SE"}, \ {0x1002, 0x4B4B, CHIP_R420|RADEON_NEW_MEMMAP, "ATI Radeon R481 X850 Pro"}, \ @@ -57,6 +392,7 @@ {0x1002, 0x4C64, CHIP_RV250|RADEON_IS_MOBILITY, "ATI Radeon Ld RV250 Mobility 9000 M9"}, \ {0x1002, 0x4C66, CHIP_RV250, "ATI Radeon Lf RV250 Mobility 9000 M9 / FireMV 2400 PCI"}, \ {0x1002, 0x4C67, CHIP_RV250|RADEON_IS_MOBILITY, "ATI Radeon Lg RV250 Mobility 9000 M9"}, \ + {0x1002, 0x4C6E, CHIP_RV280|RADEON_IS_MOBILITY, "Radeon RV250 Ln [Radeon Mobility 9000 M9] (Secondary)"}, \ {0x1002, 0x4E44, CHIP_R300, "ATI Radeon ND R300 9700 Pro"}, \ {0x1002, 0x4E45, CHIP_R300, "ATI Radeon NE R300 9500 Pro / 9700"}, \ {0x1002, 0x4E46, CHIP_R300, "ATI Radeon NF R300 9600TX"}, \ @@ -108,41 +444,199 @@ {0x1002, 0x5835, CHIP_RS300|RADEON_IS_IGP|RADEON_IS_MOBILITY, "ATI Radeon RS300 Mobility IGP"}, \ {0x1002, 0x5954, CHIP_RS480|RADEON_IS_IGP|RADEON_IS_IGPGART, "ATI RS480 XPRESS 200G"}, \ {0x1002, 0x5955, CHIP_RS480|RADEON_IS_IGP|RADEON_IS_MOBILITY|RADEON_IS_IGPGART, "ATI Radeon XPRESS 200M 5955"}, \ - {0x1002, 0x5974, CHIP_RS480|RADEON_IS_IGP|RADEON_IS_MOBILITY|RADEON_IS_IGPGART, "ATI Radeon RS482 XPRESS 200"}, \ - {0x1002, 0x5975, CHIP_RS480|RADEON_IS_IGP|RADEON_IS_MOBILITY|RADEON_IS_IGPGART, "ATI Radeon RS485 XPRESS 1100 IGP"}, \ {0x1002, 0x5960, CHIP_RV280, "ATI Radeon RV280 9250"}, \ {0x1002, 0x5961, CHIP_RV280, "ATI Radeon RV280 9200"}, \ {0x1002, 0x5962, CHIP_RV280, "ATI Radeon RV280 9200"}, \ {0x1002, 0x5964, CHIP_RV280, "ATI Radeon RV280 9200 SE"}, \ {0x1002, 0x5965, CHIP_RV280, "ATI FireMV 2200 PCI"}, \ {0x1002, 0x5969, CHIP_RV100, "ATI ES1000 RN50"}, \ - {0x1002, 0x5a41, CHIP_RS400|RADEON_IS_IGP|RADEON_IS_IGPGART, "ATI Radeon XPRESS 200 5A41 (PCIE)"}, \ - {0x1002, 0x5a42, CHIP_RS400|RADEON_IS_IGP|RADEON_IS_MOBILITY|RADEON_IS_IGPGART, "ATI Radeon XPRESS 200M 5A42 (PCIE)"}, \ - {0x1002, 0x5a61, CHIP_RS400|RADEON_IS_IGP|RADEON_IS_IGPGART, "ATI Radeon RC410 XPRESS 200"}, \ - {0x1002, 0x5a62, CHIP_RS400|RADEON_IS_IGP|RADEON_IS_MOBILITY|RADEON_IS_IGPGART, "ATI Radeon RC410 XPRESS 200M"}, \ - {0x1002, 0x5b60, CHIP_RV380|RADEON_NEW_MEMMAP, "ATI Radeon RV370 X300 SE"}, \ - {0x1002, 0x5b62, CHIP_RV380|RADEON_NEW_MEMMAP, "ATI Radeon RV370 X600 Pro"}, \ - {0x1002, 0x5b63, CHIP_RV380|RADEON_NEW_MEMMAP, "ATI Radeon RV370 X550"}, \ - {0x1002, 0x5b64, CHIP_RV380|RADEON_NEW_MEMMAP, "ATI FireGL V3100 (RV370) 5B64"}, \ - {0x1002, 0x5b65, CHIP_RV380|RADEON_NEW_MEMMAP, "ATI FireMV 2200 PCIE (RV370) 5B65"}, \ - {0x1002, 0x5c61, CHIP_RV280|RADEON_IS_MOBILITY, "ATI Radeon RV280 Mobility"}, \ - {0x1002, 0x5c63, CHIP_RV280|RADEON_IS_MOBILITY, "ATI Radeon RV280 Mobility"}, \ - {0x1002, 0x5d48, CHIP_R423|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP, "ATI Mobility Radeon X800 XT M28"}, \ - {0x1002, 0x5d49, CHIP_R423|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP, "ATI Mobility FireGL V5100 M28"}, \ - {0x1002, 0x5d4a, CHIP_R423|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP, "ATI Mobility Radeon X800 M28"}, \ - {0x1002, 0x5d4c, CHIP_R423|RADEON_NEW_MEMMAP, "ATI Radeon R480 X850"}, \ - {0x1002, 0x5d4d, CHIP_R423|RADEON_NEW_MEMMAP, "ATI Radeon R480 X850 XT PE"}, \ - {0x1002, 0x5d4e, CHIP_R423|RADEON_NEW_MEMMAP, "ATI Radeon R480 X850 SE"}, \ - {0x1002, 0x5d4f, CHIP_R423|RADEON_NEW_MEMMAP, "ATI Radeon R480 X850 Pro"}, \ - {0x1002, 0x5d50, CHIP_R423|RADEON_NEW_MEMMAP, "ATI unknown Radeon / FireGL R480"}, \ - {0x1002, 0x5d52, CHIP_R423|RADEON_NEW_MEMMAP, "ATI Radeon R480 X850 XT"}, \ - {0x1002, 0x5d57, CHIP_R423|RADEON_NEW_MEMMAP, "ATI Radeon R423 X800 XT"}, \ - {0x1002, 0x5e48, CHIP_RV410|RADEON_NEW_MEMMAP, "ATI FireGL V5000 RV410"}, \ - {0x1002, 0x5e4a, CHIP_RV410|RADEON_NEW_MEMMAP, "ATI Radeon RV410 X700 XT"}, \ - {0x1002, 0x5e4b, CHIP_RV410|RADEON_NEW_MEMMAP, "ATI Radeon RV410 X700 Pro"}, \ - {0x1002, 0x5e4c, CHIP_RV410|RADEON_NEW_MEMMAP, "ATI Radeon RV410 X700 SE"}, \ - {0x1002, 0x5e4d, CHIP_RV410|RADEON_NEW_MEMMAP, "ATI Radeon RV410 X700"}, \ - {0x1002, 0x5e4f, CHIP_RV410|RADEON_NEW_MEMMAP, "ATI Radeon RV410 X700 SE"}, \ + {0x1002, 0x5974, CHIP_RS480|RADEON_IS_IGP|RADEON_IS_MOBILITY|RADEON_IS_IGPGART, "ATI Radeon RS482 XPRESS 200"}, \ + {0x1002, 0x5975, CHIP_RS480|RADEON_IS_IGP|RADEON_IS_MOBILITY|RADEON_IS_IGPGART, "ATI Radeon RS485 XPRESS 1100 IGP"}, \ + {0x1002, 0x5A41, CHIP_RS400|RADEON_IS_IGP|RADEON_IS_IGPGART, "ATI Radeon XPRESS 200 5A41 (PCIE)"}, \ + {0x1002, 0x5A42, CHIP_RS400|RADEON_IS_IGP|RADEON_IS_MOBILITY|RADEON_IS_IGPGART, "ATI Radeon XPRESS 200M 5A42 (PCIE)"}, \ + {0x1002, 0x5A61, CHIP_RS400|RADEON_IS_IGP|RADEON_IS_IGPGART, "ATI Radeon RC410 XPRESS 200"}, \ + {0x1002, 0x5A62, CHIP_RS400|RADEON_IS_IGP|RADEON_IS_MOBILITY|RADEON_IS_IGPGART, "ATI Radeon RC410 XPRESS 200M"}, \ + {0x1002, 0x5B60, CHIP_RV380|RADEON_NEW_MEMMAP, "ATI Radeon RV370 X300 SE"}, \ + {0x1002, 0x5B62, CHIP_RV380|RADEON_NEW_MEMMAP, "ATI Radeon RV370 X600 Pro"}, \ + {0x1002, 0x5B63, CHIP_RV380|RADEON_NEW_MEMMAP, "ATI Radeon RV370 X550"}, \ + {0x1002, 0x5B64, CHIP_RV380|RADEON_NEW_MEMMAP, "ATI FireGL V3100 (RV370) 5B64"}, \ + {0x1002, 0x5B65, CHIP_RV380|RADEON_NEW_MEMMAP, "ATI FireMV 2200 PCIE (RV370) 5B65"}, \ + {0x1002, 0x5C61, CHIP_RV280|RADEON_IS_MOBILITY, "ATI Radeon RV280 Mobility"}, \ + {0x1002, 0x5C63, CHIP_RV280|RADEON_IS_MOBILITY, "ATI Radeon RV280 Mobility"}, \ + {0x1002, 0x5D48, CHIP_R423|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP, "ATI Mobility Radeon X800 XT M28"}, \ + {0x1002, 0x5D49, CHIP_R423|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP, "ATI Mobility FireGL V5100 M28"}, \ + {0x1002, 0x5D4A, CHIP_R423|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP, "ATI Mobility Radeon X800 M28"}, \ + {0x1002, 0x5D4C, CHIP_R423|RADEON_NEW_MEMMAP, "ATI Radeon R480 X850"}, \ + {0x1002, 0x5D4D, CHIP_R423|RADEON_NEW_MEMMAP, "ATI Radeon R480 X850 XT PE"}, \ + {0x1002, 0x5D4E, CHIP_R423|RADEON_NEW_MEMMAP, "ATI Radeon R480 X850 SE"}, \ + {0x1002, 0x5D4F, CHIP_R423|RADEON_NEW_MEMMAP, "ATI Radeon R480 X850 Pro"}, \ + {0x1002, 0x5D50, CHIP_R423|RADEON_NEW_MEMMAP, "ATI unknown Radeon / FireGL R480"}, \ + {0x1002, 0x5D52, CHIP_R423|RADEON_NEW_MEMMAP, "ATI Radeon R480 X850 XT"}, \ + {0x1002, 0x5D57, CHIP_R423|RADEON_NEW_MEMMAP, "ATI Radeon R423 X800 XT"}, \ + {0x1002, 0x5E48, CHIP_RV410|RADEON_NEW_MEMMAP, "ATI FireGL V5000 RV410"}, \ + {0x1002, 0x5E4A, CHIP_RV410|RADEON_NEW_MEMMAP, "ATI Radeon RV410 X700 XT"}, \ + {0x1002, 0x5E4B, CHIP_RV410|RADEON_NEW_MEMMAP, "ATI Radeon RV410 X700 Pro"}, \ + {0x1002, 0x5E4C, CHIP_RV410|RADEON_NEW_MEMMAP, "ATI Radeon RV410 X700 SE"}, \ + {0x1002, 0x5E4D, CHIP_RV410|RADEON_NEW_MEMMAP, "ATI Radeon RV410 X700"}, \ + {0x1002, 0x5E4F, CHIP_RV410|RADEON_NEW_MEMMAP, "ATI Radeon RV410 X700 SE"}, \ + {0x1002, 0x6700, CHIP_CAYMAN|RADEON_NEW_MEMMAP, "Cayman GL XT [ATI FirePro V (FireGL V) Graphics Adapter]"}, \ + {0x1002, 0x6701, CHIP_CAYMAN|RADEON_NEW_MEMMAP, "Cayman GL XT [ATI FirePro V (FireGL V) Graphics Adapter]"}, \ + {0x1002, 0x6702, CHIP_CAYMAN|RADEON_NEW_MEMMAP, "Cayman GL XT [ATI FirePro V (FireGL V) Graphics Adapter]"}, \ + {0x1002, 0x6703, CHIP_CAYMAN|RADEON_NEW_MEMMAP, "Cayman GL XT [ATI FirePro V (FireGL V) Graphics Adapter]"}, \ + {0x1002, 0x6704, CHIP_CAYMAN|RADEON_NEW_MEMMAP, "Cayman PRO GL [FirePro V7900]"}, \ + {0x1002, 0x6705, CHIP_CAYMAN|RADEON_NEW_MEMMAP, "Cayman GL PRO [ATI FirePro V (FireGL V) Graphics Adapter]"}, \ + {0x1002, 0x6706, CHIP_CAYMAN|RADEON_NEW_MEMMAP, "Cayman GL [ATI FirePro V (FireGL V) Graphics Adapter]"}, \ + {0x1002, 0x6707, CHIP_CAYMAN|RADEON_NEW_MEMMAP, "Cayman LE GL [FirePro V5900]"}, \ + {0x1002, 0x6708, CHIP_CAYMAN|RADEON_NEW_MEMMAP, "Cayman GL [ATI FirePro V (FireGL V) Graphics Adapter]"}, \ + {0x1002, 0x6709, CHIP_CAYMAN|RADEON_NEW_MEMMAP, "Cayman GL [ATI FirePro V (FireGL V) Graphics Adapter]"}, \ + {0x1002, 0x6718, CHIP_CAYMAN|RADEON_NEW_MEMMAP, "Cayman XT [Radeon HD 6970]"}, \ + {0x1002, 0x6719, CHIP_CAYMAN|RADEON_NEW_MEMMAP, "Cayman PRO [Radeon HD 6950]"}, \ + {0x1002, 0x671C, CHIP_CAYMAN|RADEON_NEW_MEMMAP, "Antilles [Radeon HD 6990]"}, \ + {0x1002, 0x671D, CHIP_CAYMAN|RADEON_NEW_MEMMAP, "Antilles [AMD Radeon HD 6990]"}, \ + {0x1002, 0x671F, CHIP_CAYMAN|RADEON_NEW_MEMMAP, "Cayman [Radeon HD 6900 Series]"}, \ + {0x1002, 0x6720, CHIP_BARTS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP, "Blackcomb [Radeon HD 6900M series]"}, \ + {0x1002, 0x6721, CHIP_BARTS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP, "Blackcomb [Mobility Radeon HD 6000 series]"}, \ + {0x1002, 0x6722, CHIP_BARTS|RADEON_NEW_MEMMAP, "Barts GL [ATI FirePro V (FireGL V) Graphics Adapter]"}, \ + {0x1002, 0x6723, CHIP_BARTS|RADEON_NEW_MEMMAP, "Barts GL [ATI FirePro V (FireGL V) Graphics Adapter]"}, \ + {0x1002, 0x6724, CHIP_BARTS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP, "Blackcomb [Mobility Radeon HD 6000 series]"}, \ + {0x1002, 0x6725, CHIP_BARTS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP, "Blackcomb [Mobility Radeon HD 6000 series]"}, \ + {0x1002, 0x6726, CHIP_BARTS|RADEON_NEW_MEMMAP, "Barts GL [ATI FirePro V (FireGL V) Graphics Adapter]"}, \ + {0x1002, 0x6727, CHIP_BARTS|RADEON_NEW_MEMMAP, "Barts GL [ATI FirePro V (FireGL V) Graphics Adapter]"}, \ + {0x1002, 0x6728, CHIP_BARTS|RADEON_NEW_MEMMAP, "Barts GL [ATI FirePro V (FireGL V) Graphics Adapter]"}, \ + {0x1002, 0x6729, CHIP_BARTS|RADEON_NEW_MEMMAP, "Barts GL [ATI FirePro V (FireGL V) Graphics Adapter]"}, \ + {0x1002, 0x6738, CHIP_BARTS|RADEON_NEW_MEMMAP, "Barts XT [Radeon HD 6800 Series]"}, \ + {0x1002, 0x6739, CHIP_BARTS|RADEON_NEW_MEMMAP, "Barts PRO [Radeon HD 6800 Series]"}, \ + {0x1002, 0x673E, CHIP_BARTS|RADEON_NEW_MEMMAP, "Barts LE [AMD Radeon HD 6700 Series]"}, \ + {0x1002, 0x6740, CHIP_TURKS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP, "Whistler XT [AMD Radeon HD 6700M Series]"}, \ + {0x1002, 0x6741, CHIP_TURKS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP, "Whistler [AMD Radeon HD 6600M Series]"}, \ + {0x1002, 0x6742, CHIP_TURKS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP, "Whistler LE [AMD Radeon HD 6625M Graphics]"}, \ + {0x1002, 0x6743, CHIP_TURKS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP, "Whistler [Radeon E6760]"}, \ + {0x1002, 0x6744, CHIP_TURKS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP, "Whistler [ATI Mobility Radeon HD 6000 series]"}, \ + {0x1002, 0x6745, CHIP_TURKS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP, "Whistler"}, \ + {0x1002, 0x6746, CHIP_TURKS|RADEON_NEW_MEMMAP, "Turks GL [ATI FirePro V (FireGL V) Graphics Adapter]"}, \ + {0x1002, 0x6747, CHIP_TURKS|RADEON_NEW_MEMMAP, "Turks GL [ATI FirePro V (FireGL V) Graphics Adapter]"}, \ + {0x1002, 0x6748, CHIP_TURKS|RADEON_NEW_MEMMAP, "Turks GL [ATI FirePro V (FireGL V) Graphics Adapter]"}, \ + {0x1002, 0x6749, CHIP_TURKS|RADEON_NEW_MEMMAP, "Turks [FirePro V4900]"}, \ + {0x1002, 0x674A, CHIP_TURKS|RADEON_NEW_MEMMAP, "Turks [AMD FirePro V3900]"}, \ + {0x1002, 0x6750, CHIP_TURKS|RADEON_NEW_MEMMAP, "Turks [AMD Radeon HD 6570]"}, \ + {0x1002, 0x6751, CHIP_TURKS|RADEON_NEW_MEMMAP, "Turks [Radeon HD 7600A Series]"}, \ + {0x1002, 0x6758, CHIP_TURKS|RADEON_NEW_MEMMAP, "Turks [Radeon HD 6670]"}, \ + {0x1002, 0x6759, CHIP_TURKS|RADEON_NEW_MEMMAP, "Turks [Radeon HD 6570]"}, \ + {0x1002, 0x675B, CHIP_TURKS|RADEON_NEW_MEMMAP, "Unknown device name"}, \ + {0x1002, 0x675D, CHIP_TURKS|RADEON_NEW_MEMMAP, "Turks [Radeon HD 7500 Series]"}, \ + {0x1002, 0x675F, CHIP_TURKS|RADEON_NEW_MEMMAP, "Turks LE [Radeon HD 5500/7510 Series]"}, \ + {0x1002, 0x6760, CHIP_CAICOS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP, "Caicos [Radeon HD 6400M/7400M Series]"}, \ + {0x1002, 0x6761, CHIP_CAICOS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP, "Seymour LP [Radeon HD 6430M]"}, \ + {0x1002, 0x6762, CHIP_CAICOS|RADEON_NEW_MEMMAP, "Caicos GL [ATI FirePro V (FireGL V) Graphics Adapter]"}, \ + {0x1002, 0x6763, CHIP_CAICOS|RADEON_NEW_MEMMAP, "Seymour [Radeon E6460]"}, \ + {0x1002, 0x6764, CHIP_CAICOS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP, "Seymour [Mobility Radeon HD 6000 series]"}, \ + {0x1002, 0x6765, CHIP_CAICOS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP, "Seymour [Mobility Radeon HD 6000 series]"}, \ + {0x1002, 0x6766, CHIP_CAICOS|RADEON_NEW_MEMMAP, "Caicos"}, \ + {0x1002, 0x6767, CHIP_CAICOS|RADEON_NEW_MEMMAP, "Caicos"}, \ + {0x1002, 0x6768, CHIP_CAICOS|RADEON_NEW_MEMMAP, "Caicos"}, \ + {0x1002, 0x6770, CHIP_CAICOS|RADEON_NEW_MEMMAP, "Caicos [Radeon HD 6400 Series]"}, \ + {0x1002, 0x6771, CHIP_CAICOS|RADEON_NEW_MEMMAP, "Caicos"}, \ + {0x1002, 0x6772, CHIP_CAICOS|RADEON_NEW_MEMMAP, "Caicos [Radeon HD 7400A Series]"}, \ + {0x1002, 0x6778, CHIP_CAICOS|RADEON_NEW_MEMMAP, "Caicos [Radeon HD 7000 Series]"}, \ + {0x1002, 0x6779, CHIP_CAICOS|RADEON_NEW_MEMMAP, "Caicos [Radeon HD 6450]"}, \ + {0x1002, 0x677B, CHIP_CAICOS|RADEON_NEW_MEMMAP, "Caicos [Radeon HD 7400 Series]"}, \ + {0x1002, 0x6780, CHIP_TAHITI|RADEON_NEW_MEMMAP, "Tahiti [ATI FirePro V (FireGL V) Graphics Adapter]"}, \ + {0x1002, 0x6784, CHIP_TAHITI|RADEON_NEW_MEMMAP, "Tahiti [ATI FirePro V (FireGL V) Graphics Adapter]"}, \ + {0x1002, 0x6788, CHIP_TAHITI|RADEON_NEW_MEMMAP, "Tahiti [ATI FirePro V (FireGL V) Graphics Adapter]"}, \ + {0x1002, 0x678A, CHIP_TAHITI|RADEON_NEW_MEMMAP, "Tahiti [ATI FirePro V (FireGL V) Graphics Adapter]"}, \ + {0x1002, 0x6790, CHIP_TAHITI|RADEON_NEW_MEMMAP, "Tahiti"}, \ + {0x1002, 0x6791, CHIP_TAHITI|RADEON_NEW_MEMMAP, "Tahiti"}, \ + {0x1002, 0x6792, CHIP_TAHITI|RADEON_NEW_MEMMAP, "Tahiti"}, \ + {0x1002, 0x6798, CHIP_TAHITI|RADEON_NEW_MEMMAP, "Tahiti XT [Radeon HD 7970]"}, \ + {0x1002, 0x6799, CHIP_TAHITI|RADEON_NEW_MEMMAP, "New Zealand [Radeon HD 7990]"}, \ + {0x1002, 0x679A, CHIP_TAHITI|RADEON_NEW_MEMMAP, "Tahiti PRO [Radeon HD 7950]"}, \ + {0x1002, 0x679B, CHIP_TAHITI|RADEON_NEW_MEMMAP, "Tahiti [Radeon HD 7900 Series]"}, \ + {0x1002, 0x679E, CHIP_TAHITI|RADEON_NEW_MEMMAP, "Tahiti LE [Radeon HD 7800 Series]"}, \ + {0x1002, 0x679F, CHIP_TAHITI|RADEON_NEW_MEMMAP, "Tahiti"}, \ + {0x1002, 0x6800, CHIP_PITCAIRN|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP, "Wimbledon XT [Radeon HD 7970M]"}, \ + {0x1002, 0x6801, CHIP_PITCAIRN|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP, "Wimbledon"}, \ + {0x1002, 0x6802, CHIP_PITCAIRN|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP, "Wimbledon"}, \ + {0x1002, 0x6806, CHIP_PITCAIRN|RADEON_NEW_MEMMAP, "Pitcairn"}, \ + {0x1002, 0x6808, CHIP_PITCAIRN|RADEON_NEW_MEMMAP, "Pitcairn [ATI FirePro V(FireGL V) Graphics Adapter]"}, \ + {0x1002, 0x6809, CHIP_PITCAIRN|RADEON_NEW_MEMMAP, "Pitcairn [ATI FirePro V(FireGL V) Graphics Adapter]"}, \ + {0x1002, 0x6810, CHIP_PITCAIRN|RADEON_NEW_MEMMAP, "Pitcairn"}, \ + {0x1002, 0x6811, CHIP_PITCAIRN|RADEON_NEW_MEMMAP, "Pitcairn"}, \ + {0x1002, 0x6816, CHIP_PITCAIRN|RADEON_NEW_MEMMAP, "Pitcairn"}, \ + {0x1002, 0x6817, CHIP_PITCAIRN|RADEON_NEW_MEMMAP, "Pitcairn"}, \ + {0x1002, 0x6818, CHIP_PITCAIRN|RADEON_NEW_MEMMAP, "Pitcairn [Radeon HD 7800]"}, \ + {0x1002, 0x6819, CHIP_PITCAIRN|RADEON_NEW_MEMMAP, "Pitcairn PRO [Radeon HD 7800]"}, \ + {0x1002, 0x6820, CHIP_VERDE|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP, "Radeon HD 8800M Series"}, \ + {0x1002, 0x6821, CHIP_VERDE|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP, "Radeon HD 8800M Series"}, \ + {0x1002, 0x6823, CHIP_VERDE|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP, "Radeon HD 8800M Series"}, \ + {0x1002, 0x6824, CHIP_VERDE|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP, "Chelsea [Radeon HD 7700M Series]"}, \ + {0x1002, 0x6825, CHIP_VERDE|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP, "Cape Verde [Radeon HD 7800M Series]"}, \ + {0x1002, 0x6826, CHIP_VERDE|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP, "Chelsea [Radeon HD 7700M Series]"}, \ + {0x1002, 0x6827, CHIP_VERDE|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP, "Cape Verde [Radeon HD 7800M Series]"}, \ + {0x1002, 0x6828, CHIP_VERDE|RADEON_NEW_MEMMAP, "Cape Verde"}, \ + {0x1002, 0x6829, CHIP_VERDE|RADEON_NEW_MEMMAP, "Cape Verde"}, \ + {0x1002, 0x682B, CHIP_VERDE|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP, "Radeon HD 8800M Series"}, \ + {0x1002, 0x682D, CHIP_VERDE|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP, "Unknown device name"}, \ + {0x1002, 0x682F, CHIP_VERDE|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP, "Cape Verde [Radeon HD 7700M Series]"}, \ + {0x1002, 0x6830, CHIP_VERDE|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP, "Cape Verde [Radeon HD 7800M Series]"}, \ + {0x1002, 0x6831, CHIP_VERDE|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP, "Cape Verde [AMD Radeon HD 7700M Series]"}, \ + {0x1002, 0x6837, CHIP_VERDE|RADEON_NEW_MEMMAP, "Cape Verde LE [Radeon HD 7700 Series]"}, \ + {0x1002, 0x6838, CHIP_VERDE|RADEON_NEW_MEMMAP, "Cape Verde"}, \ + {0x1002, 0x6839, CHIP_VERDE|RADEON_NEW_MEMMAP, "Cape Verde"}, \ + {0x1002, 0x683B, CHIP_VERDE|RADEON_NEW_MEMMAP, "Cape Verde [Radeon HD 7700 Series]"}, \ + {0x1002, 0x683D, CHIP_VERDE|RADEON_NEW_MEMMAP, "Cape Verde [Radeon HD 7700 Series]"}, \ + {0x1002, 0x683F, CHIP_VERDE|RADEON_NEW_MEMMAP, "Cape Verde PRO [Radeon HD 7700 Series]"}, \ + {0x1002, 0x6840, CHIP_TURKS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP, "Thames XT/GL [Radeon HD 7600M Series]"}, \ + {0x1002, 0x6841, CHIP_TURKS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP, "Thames [Radeon 7500M/7600M Series]"}, \ + {0x1002, 0x6842, CHIP_TURKS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP, "Thames LE [Radeon HD 7000M Series]"}, \ + {0x1002, 0x6843, CHIP_TURKS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP, "Thames [Radeon HD 7670M]"}, \ + {0x1002, 0x6849, CHIP_TURKS|RADEON_NEW_MEMMAP, "Lombok [AMD Radeon HD 7400 Series]"}, \ + {0x1002, 0x684C, CHIP_PITCAIRN|RADEON_NEW_MEMMAP, "Pitcairn [ATI FirePro V(FireGL V) Graphics Adapter]"}, \ + {0x1002, 0x6850, CHIP_TURKS|RADEON_NEW_MEMMAP, "Lombok GL AIO [Radeon HD 7570]"}, \ + {0x1002, 0x6858, CHIP_TURKS|RADEON_NEW_MEMMAP, "Lombok [Radeon HD 7400 series]"}, \ + {0x1002, 0x6859, CHIP_TURKS|RADEON_NEW_MEMMAP, "Unknown device name"}, \ + {0x1002, 0x6880, CHIP_CYPRESS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP, "Cypress"}, \ + {0x1002, 0x6888, CHIP_CYPRESS|RADEON_NEW_MEMMAP, "Cypress [FirePro 3D V8800]"}, \ + {0x1002, 0x6889, CHIP_CYPRESS|RADEON_NEW_MEMMAP, "Cypress [FirePro V7800]"}, \ + {0x1002, 0x688A, CHIP_CYPRESS|RADEON_NEW_MEMMAP, "Cypress XT [FirePro 3D V9800]"}, \ + {0x1002, 0x688C, CHIP_CYPRESS|RADEON_NEW_MEMMAP, "Cypress [AMD FireStream 9370]"}, \ + {0x1002, 0x688D, CHIP_CYPRESS|RADEON_NEW_MEMMAP, "Cypress [AMD FireStream 9350]"}, \ + {0x1002, 0x6898, CHIP_CYPRESS|RADEON_NEW_MEMMAP, "Cypress XT [Radeon HD 5870]"}, \ + {0x1002, 0x6899, CHIP_CYPRESS|RADEON_NEW_MEMMAP, "Cypress PRO [Radeon HD 5800 Series]"}, \ + {0x1002, 0x689B, CHIP_CYPRESS|RADEON_NEW_MEMMAP, "Cypress [Radeon HD 6800 Series]"}, \ + {0x1002, 0x689C, CHIP_HEMLOCK|RADEON_NEW_MEMMAP, "Hemlock [Radeon HD 5900 Series]"}, \ + {0x1002, 0x689D, CHIP_HEMLOCK|RADEON_NEW_MEMMAP, "Hemlock [ATI Radeon HD 5900 Series]"}, \ + {0x1002, 0x689E, CHIP_CYPRESS|RADEON_NEW_MEMMAP, "Cypress LE [Radeon HD 5800 Series]"}, \ + {0x1002, 0x68A0, CHIP_JUNIPER|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP, "ATI Mobility Radeon HD 5870"}, \ + {0x1002, 0x68A1, CHIP_JUNIPER|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP, "Broadway PRO [Mobility Radeon HD 5800 Series]"}, \ + {0x1002, 0x68A8, CHIP_JUNIPER|RADEON_NEW_MEMMAP, "Broadway [ATI Mobility Radeon HD 6800 Series]"}, \ + {0x1002, 0x68A9, CHIP_JUNIPER|RADEON_NEW_MEMMAP, "Juniper XT [FirePro 3D V5800]"}, \ + {0x1002, 0x68B0, CHIP_JUNIPER|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP, "Unknown device name"}, \ + {0x1002, 0x68B8, CHIP_JUNIPER|RADEON_NEW_MEMMAP, "Juniper [Radeon HD 5700 Series]"}, \ + {0x1002, 0x68B9, CHIP_JUNIPER|RADEON_NEW_MEMMAP, "Juniper [Radeon HD 5600/5700]"}, \ + {0x1002, 0x68BA, CHIP_JUNIPER|RADEON_NEW_MEMMAP, "Juniper XT [AMD Radeon HD 6000 Series]"}, \ + {0x1002, 0x68BE, CHIP_JUNIPER|RADEON_NEW_MEMMAP, "Juniper [Radeon HD 5700 Series]"}, \ + {0x1002, 0x68BF, CHIP_JUNIPER|RADEON_NEW_MEMMAP, "Juniper LE [Radeon HD 6700 Series]"}, \ + {0x1002, 0x68C0, CHIP_REDWOOD|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP, "Madison [Mobility Radeon HD 5000 Series]"}, \ + {0x1002, 0x68C1, CHIP_REDWOOD|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP, "Madison [Radeon HD 5000M Series]"}, \ + {0x1002, 0x68C7, CHIP_REDWOOD|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP, "Pinewood [Radeon HD 5570]"}, \ + {0x1002, 0x68C8, CHIP_REDWOOD|RADEON_NEW_MEMMAP, "FirePro V4800"}, \ + {0x1002, 0x68C9, CHIP_REDWOOD|RADEON_NEW_MEMMAP, "Redwood [FirePro 3800 (FireGL)]"}, \ + {0x1002, 0x68D8, CHIP_REDWOOD|RADEON_NEW_MEMMAP, "Redwood [Radeon HD 5670]"}, \ + {0x1002, 0x68D9, CHIP_REDWOOD|RADEON_NEW_MEMMAP, "Redwood PRO [Radeon HD 5500 Series]"}, \ + {0x1002, 0x68DA, CHIP_REDWOOD|RADEON_NEW_MEMMAP, "Redwood PRO [Radeon HD 5500 Series]"}, \ + {0x1002, 0x68DE, CHIP_REDWOOD|RADEON_NEW_MEMMAP, "Redwood"}, \ + {0x1002, 0x68E0, CHIP_CEDAR|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP, "Manhattan [Mobility Radeon HD 5400 Series]"}, \ + {0x1002, 0x68E1, CHIP_CEDAR|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP, "Manhattan [Mobility Radeon HD 5430 Series]"}, \ + {0x1002, 0x68E4, CHIP_CEDAR|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP, "Robson CE [AMD Radeon HD 6300 Series]"}, \ + {0x1002, 0x68E5, CHIP_CEDAR|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP, "Robson LE [AMD Radeon HD 6300M Series]"}, \ + {0x1002, 0x68E8, CHIP_CEDAR|RADEON_NEW_MEMMAP, "Cedar"}, \ + {0x1002, 0x68E9, CHIP_CEDAR|RADEON_NEW_MEMMAP, "Cedar [ATI FirePro (FireGL) Graphics Adapter]"}, \ + {0x1002, 0x68F1, CHIP_CEDAR|RADEON_NEW_MEMMAP, "Cedar [FirePro 2460]"}, \ + {0x1002, 0x68F2, CHIP_CEDAR|RADEON_NEW_MEMMAP, "Cedar [FirePro 2270]"}, \ + {0x1002, 0x68F8, CHIP_CEDAR|RADEON_NEW_MEMMAP, "Cedar [Radeon HD 7300 Series]"}, \ + {0x1002, 0x68F9, CHIP_CEDAR|RADEON_NEW_MEMMAP, "Cedar PRO [Radeon HD 5450/6350]"}, \ + {0x1002, 0x68FA, CHIP_CEDAR|RADEON_NEW_MEMMAP, "EG Cedar [Radeon HD 7300 Series]"}, \ + {0x1002, 0x68FE, CHIP_CEDAR|RADEON_NEW_MEMMAP, "Cedar LE"}, \ {0x1002, 0x7100, CHIP_R520|RADEON_NEW_MEMMAP, "ATI Radeon X1800"}, \ {0x1002, 0x7101, CHIP_R520|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP, "ATI Mobility Radeon X1800 XT"}, \ {0x1002, 0x7102, CHIP_R520|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP, "ATI Mobility Radeon X1800"}, \ @@ -240,15 +734,15 @@ {0x1002, 0x7297, CHIP_RV560|RADEON_NEW_MEMMAP, "ATI RV560"}, \ {0x1002, 0x7834, CHIP_RS300|RADEON_IS_IGP|RADEON_NEW_MEMMAP, "ATI Radeon RS350 9000/9100 IGP"}, \ {0x1002, 0x7835, CHIP_RS300|RADEON_IS_IGP|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP, "ATI Radeon RS350 Mobility IGP"}, \ - {0x1002, 0x793f, CHIP_RS600|RADEON_IS_IGP|RADEON_NEW_MEMMAP, "ATI Radeon X1200"}, \ + {0x1002, 0x791E, CHIP_RS690|RADEON_IS_IGP|RADEON_NEW_MEMMAP|RADEON_IS_IGPGART, "ATI Radeon RS690 X1250 IGP"}, \ + {0x1002, 0x791F, CHIP_RS690|RADEON_IS_IGP|RADEON_NEW_MEMMAP|RADEON_IS_IGPGART, "ATI Radeon RS690 X1270 IGP"}, \ + {0x1002, 0x793F, CHIP_RS600|RADEON_IS_IGP|RADEON_NEW_MEMMAP, "ATI Radeon X1200"}, \ {0x1002, 0x7941, CHIP_RS600|RADEON_IS_IGP|RADEON_NEW_MEMMAP, "ATI Radeon X1200"}, \ {0x1002, 0x7942, CHIP_RS600|RADEON_IS_IGP|RADEON_NEW_MEMMAP, "ATI Radeon X1200"}, \ - {0x1002, 0x791e, CHIP_RS690|RADEON_IS_IGP|RADEON_NEW_MEMMAP|RADEON_IS_IGPGART, "ATI Radeon RS690 X1250 IGP"}, \ - {0x1002, 0x791f, CHIP_RS690|RADEON_IS_IGP|RADEON_NEW_MEMMAP|RADEON_IS_IGPGART, "ATI Radeon RS690 X1270 IGP"}, \ - {0x1002, 0x796c, CHIP_RS740|RADEON_IS_IGP|RADEON_NEW_MEMMAP|RADEON_IS_IGPGART, "ATI Radeon RS740 HD2100 IGP"}, \ - {0x1002, 0x796d, CHIP_RS740|RADEON_IS_IGP|RADEON_NEW_MEMMAP|RADEON_IS_IGPGART, "ATI Radeon RS740 HD2100 IGP"}, \ - {0x1002, 0x796e, CHIP_RS740|RADEON_IS_IGP|RADEON_NEW_MEMMAP|RADEON_IS_IGPGART, "ATI Radeon RS740 HD2100 IGP"}, \ - {0x1002, 0x796f, CHIP_RS740|RADEON_IS_IGP|RADEON_NEW_MEMMAP|RADEON_IS_IGPGART, "ATI Radeon RS740 HD2100 IGP"}, \ + {0x1002, 0x796C, CHIP_RS740|RADEON_IS_IGP|RADEON_NEW_MEMMAP|RADEON_IS_IGPGART, "ATI Radeon RS740 HD2100 IGP"}, \ + {0x1002, 0x796D, CHIP_RS740|RADEON_IS_IGP|RADEON_NEW_MEMMAP|RADEON_IS_IGPGART, "ATI Radeon RS740 HD2100 IGP"}, \ + {0x1002, 0x796E, CHIP_RS740|RADEON_IS_IGP|RADEON_NEW_MEMMAP|RADEON_IS_IGPGART, "ATI Radeon RS740 HD2100 IGP"}, \ + {0x1002, 0x796F, CHIP_RS740|RADEON_IS_IGP|RADEON_NEW_MEMMAP|RADEON_IS_IGPGART, "ATI Radeon RS740 HD2100 IGP"}, \ {0x1002, 0x9400, CHIP_R600|RADEON_NEW_MEMMAP, "ATI Radeon HD 2900 XT"}, \ {0x1002, 0x9401, CHIP_R600|RADEON_NEW_MEMMAP, "ATI Radeon HD 2900 XT"}, \ {0x1002, 0x9402, CHIP_R600|RADEON_NEW_MEMMAP, "ATI Radeon HD 2900 XT"}, \ @@ -257,6 +751,41 @@ {0x1002, 0x940A, CHIP_R600|RADEON_NEW_MEMMAP, "ATI FireGL V8650"}, \ {0x1002, 0x940B, CHIP_R600|RADEON_NEW_MEMMAP, "ATI FireGL V8600"}, \ {0x1002, 0x940F, CHIP_R600|RADEON_NEW_MEMMAP, "ATI FireGL V7600"}, \ + {0x1002, 0x9440, CHIP_RV770|RADEON_NEW_MEMMAP, "ATI Radeon 4800 Series"}, \ + {0x1002, 0x9441, CHIP_RV770|RADEON_NEW_MEMMAP, "ATI Radeon 4870 X2"}, \ + {0x1002, 0x9442, CHIP_RV770|RADEON_NEW_MEMMAP, "ATI Radeon 4800 Series"}, \ + {0x1002, 0x9443, CHIP_RV770|RADEON_NEW_MEMMAP, "ATI Radeon 4850 X2"}, \ + {0x1002, 0x9444, CHIP_RV770|RADEON_NEW_MEMMAP, "ATI FirePro V8750 (FireGL)"}, \ + {0x1002, 0x9446, CHIP_RV770|RADEON_NEW_MEMMAP, "ATI FirePro V7760 (FireGL)"}, \ + {0x1002, 0x944A, CHIP_RV770|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP, "ATI Mobility Radeon HD 4850"}, \ + {0x1002, 0x944B, CHIP_RV770|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP, "ATI Mobility Radeon HD 4850 X2"}, \ + {0x1002, 0x944C, CHIP_RV770|RADEON_NEW_MEMMAP, "ATI Radeon 4800 Series"}, \ + {0x1002, 0x944E, CHIP_RV770|RADEON_NEW_MEMMAP, "ATI FirePro RV770"}, \ + {0x1002, 0x9450, CHIP_RV770|RADEON_NEW_MEMMAP, "AMD FireStream 9270"}, \ + {0x1002, 0x9452, CHIP_RV770|RADEON_NEW_MEMMAP, "AMD FireStream 9250"}, \ + {0x1002, 0x9456, CHIP_RV770|RADEON_NEW_MEMMAP, "ATI FirePro V8700 (FireGL)"}, \ + {0x1002, 0x945A, CHIP_RV770|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP, "ATI Mobility Radeon HD 4870"}, \ + {0x1002, 0x945B, CHIP_RV770|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP, "ATI Mobility Radeon M98"}, \ + {0x1002, 0x945E, CHIP_RV770|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP, "RV770"}, \ + {0x1002, 0x9460, CHIP_RV770|RADEON_NEW_MEMMAP, "ATI Radeon 4800 Series"}, \ + {0x1002, 0x9462, CHIP_RV770|RADEON_NEW_MEMMAP, "ATI Radeon 4800 Series"}, \ + {0x1002, 0x946A, CHIP_RV770|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP, "ATI FirePro M7750"}, \ + {0x1002, 0x946B, CHIP_RV770|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP, "ATI M98"}, \ + {0x1002, 0x947A, CHIP_RV770|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP, "ATI M98"}, \ + {0x1002, 0x947B, CHIP_RV770|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP, "ATI M98"}, \ + {0x1002, 0x9480, CHIP_RV730|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP, "ATI Mobility Radeon HD 4650"}, \ + {0x1002, 0x9487, CHIP_RV730|RADEON_NEW_MEMMAP, "ATI Radeon RV730 (AGP)"}, \ + {0x1002, 0x9488, CHIP_RV730|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP, "ATI Mobility Radeon HD 4670"}, \ + {0x1002, 0x9489, CHIP_RV730|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP, "ATI FirePro M5750"}, \ + {0x1002, 0x948A, CHIP_RV730|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP, "RV730"}, \ + {0x1002, 0x948F, CHIP_RV730|RADEON_NEW_MEMMAP, "ATI Radeon RV730 (AGP)"}, \ + {0x1002, 0x9490, CHIP_RV730|RADEON_NEW_MEMMAP, "ATI Radeon HD 4670"}, \ + {0x1002, 0x9491, CHIP_RV730|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP, "ATI RADEON E4600"}, \ + {0x1002, 0x9495, CHIP_RV730|RADEON_NEW_MEMMAP, "ATI Radeon HD 4600 Series"}, \ + {0x1002, 0x9498, CHIP_RV730|RADEON_NEW_MEMMAP, "ATI Radeon HD 4650"}, \ + {0x1002, 0x949C, CHIP_RV730|RADEON_NEW_MEMMAP, "ATI FirePro V7750 (FireGL)"}, \ + {0x1002, 0x949E, CHIP_RV730|RADEON_NEW_MEMMAP, "ATI FirePro V5700 (FireGL)"}, \ + {0x1002, 0x949F, CHIP_RV730|RADEON_NEW_MEMMAP, "ATI FirePro V3750 (FireGL)"}, \ {0x1002, 0x94A0, CHIP_RV740|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP, "ATI Mobility Radeon HD 4830"}, \ {0x1002, 0x94A1, CHIP_RV740|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP, "ATI Mobility Radeon HD 4850"}, \ {0x1002, 0x94A3, CHIP_RV740|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP, "ATI FirePro M7740"}, \ @@ -290,6 +819,16 @@ {0x1002, 0x9515, CHIP_RV670|RADEON_NEW_MEMMAP, "ATI Radeon HD3850"}, \ {0x1002, 0x9517, CHIP_RV670|RADEON_NEW_MEMMAP, "ATI Radeon HD3690"}, \ {0x1002, 0x9519, CHIP_RV670|RADEON_NEW_MEMMAP, "AMD Firestream 9170"}, \ + {0x1002, 0x9540, CHIP_RV710|RADEON_NEW_MEMMAP, "ATI Radeon HD 4550"}, \ + {0x1002, 0x9541, CHIP_RV710|RADEON_NEW_MEMMAP, "ATI Radeon RV710"}, \ + {0x1002, 0x9542, CHIP_RV710|RADEON_NEW_MEMMAP, "ATI Radeon RV710"}, \ + {0x1002, 0x954E, CHIP_RV710|RADEON_NEW_MEMMAP, "ATI Radeon RV710"}, \ + {0x1002, 0x954F, CHIP_RV710|RADEON_NEW_MEMMAP, "ATI Radeon HD 4350"}, \ + {0x1002, 0x9552, CHIP_RV710|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP, "ATI Mobility Radeon 4300 Series"}, \ + {0x1002, 0x9553, CHIP_RV710|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP, "ATI Mobility Radeon 4500 Series"}, \ + {0x1002, 0x9555, CHIP_RV710|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP, "ATI Mobility Radeon 4500 Series"}, \ + {0x1002, 0x9557, CHIP_RV710|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP, "ATI FirePro RG220"}, \ + {0x1002, 0x955F, CHIP_RV710|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP, "RV710 [Mobility Radeon HD 4330]"}, \ {0x1002, 0x9580, CHIP_RV630|RADEON_NEW_MEMMAP, "ATI RV630"}, \ {0x1002, 0x9581, CHIP_RV630|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP, "ATI Mobility Radeon HD 2600"}, \ {0x1002, 0x9583, CHIP_RV630|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP, "ATI Mobility Radeon HD 2600 XT"}, \ @@ -303,26 +842,26 @@ {0x1002, 0x958D, CHIP_RV630|RADEON_NEW_MEMMAP, "ATI FireGL V3600"}, \ {0x1002, 0x958E, CHIP_RV630|RADEON_NEW_MEMMAP, "ATI Radeon HD 2600 LE"}, \ {0x1002, 0x958F, CHIP_RV630|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP, "ATI Mobility FireGL Graphics Processor"}, \ - {0x1002, 0x95C0, CHIP_RV620|RADEON_NEW_MEMMAP, "ATI Radeon HD 3470"}, \ - {0x1002, 0x95C5, CHIP_RV620|RADEON_NEW_MEMMAP, "ATI Radeon HD 3450"}, \ - {0x1002, 0x95C6, CHIP_RV620|RADEON_NEW_MEMMAP, "ATI Radeon HD 3450"}, \ - {0x1002, 0x95C7, CHIP_RV620|RADEON_NEW_MEMMAP, "ATI Radeon HD 3430"}, \ - {0x1002, 0x95C9, CHIP_RV620|RADEON_NEW_MEMMAP, "ATI Radeon HD 3450"}, \ - {0x1002, 0x95C2, CHIP_RV620|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP, "ATI Mobility Radeon HD 3430"}, \ - {0x1002, 0x95C4, CHIP_RV620|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP, "ATI Mobility Radeon HD 3400 Series"}, \ - {0x1002, 0x95CC, CHIP_RV620|RADEON_NEW_MEMMAP, "ATI FirePro V3700"}, \ - {0x1002, 0x95CD, CHIP_RV620|RADEON_NEW_MEMMAP, "ATI FireMV 2450"}, \ - {0x1002, 0x95CE, CHIP_RV620|RADEON_NEW_MEMMAP, "ATI FireMV 2260"}, \ - {0x1002, 0x95CF, CHIP_RV620|RADEON_NEW_MEMMAP, "ATI FireMV 2260"}, \ {0x1002, 0x9590, CHIP_RV635|RADEON_NEW_MEMMAP, "ATI ATI Radeon HD 3600 Series"}, \ + {0x1002, 0x9591, CHIP_RV635|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP, "ATI Mobility Radeon HD 3650"}, \ + {0x1002, 0x9593, CHIP_RV635|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP, "ATI Mobility Radeon HD 3670"}, \ + {0x1002, 0x9595, CHIP_RV635|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP, "ATI Mobility FireGL V5700"}, \ {0x1002, 0x9596, CHIP_RV635|RADEON_NEW_MEMMAP, "ATI ATI Radeon HD 3650 AGP"}, \ {0x1002, 0x9597, CHIP_RV635|RADEON_NEW_MEMMAP, "ATI ATI Radeon HD 3600 PRO"}, \ {0x1002, 0x9598, CHIP_RV635|RADEON_NEW_MEMMAP, "ATI ATI Radeon HD 3600 XT"}, \ {0x1002, 0x9599, CHIP_RV635|RADEON_NEW_MEMMAP, "ATI ATI Radeon HD 3600 PRO"}, \ - {0x1002, 0x9591, CHIP_RV635|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP, "ATI Mobility Radeon HD 3650"}, \ - {0x1002, 0x9593, CHIP_RV635|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP, "ATI Mobility Radeon HD 3670"}, \ - {0x1002, 0x9595, CHIP_RV635|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP, "ATI Mobility FireGL V5700"}, \ {0x1002, 0x959B, CHIP_RV635|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP, "ATI Mobility FireGL V5725"}, \ + {0x1002, 0x95C0, CHIP_RV620|RADEON_NEW_MEMMAP, "ATI Radeon HD 3470"}, \ + {0x1002, 0x95C2, CHIP_RV620|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP, "ATI Mobility Radeon HD 3430"}, \ + {0x1002, 0x95C4, CHIP_RV620|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP, "ATI Mobility Radeon HD 3400 Series"}, \ + {0x1002, 0x95C5, CHIP_RV620|RADEON_NEW_MEMMAP, "ATI Radeon HD 3450"}, \ + {0x1002, 0x95C6, CHIP_RV620|RADEON_NEW_MEMMAP, "ATI Radeon HD 3450"}, \ + {0x1002, 0x95C7, CHIP_RV620|RADEON_NEW_MEMMAP, "ATI Radeon HD 3430"}, \ + {0x1002, 0x95C9, CHIP_RV620|RADEON_NEW_MEMMAP, "ATI Radeon HD 3450"}, \ + {0x1002, 0x95CC, CHIP_RV620|RADEON_NEW_MEMMAP, "ATI FirePro V3700"}, \ + {0x1002, 0x95CD, CHIP_RV620|RADEON_NEW_MEMMAP, "ATI FireMV 2450"}, \ + {0x1002, 0x95CE, CHIP_RV620|RADEON_NEW_MEMMAP, "ATI FireMV 2260"}, \ + {0x1002, 0x95CF, CHIP_RV620|RADEON_NEW_MEMMAP, "ATI FireMV 2260"}, \ {0x1002, 0x9610, CHIP_RS780|RADEON_NEW_MEMMAP|RADEON_IS_IGP, "ATI Radeon HD 3200 Graphics"}, \ {0x1002, 0x9611, CHIP_RS780|RADEON_NEW_MEMMAP|RADEON_IS_IGP, "ATI Radeon 3100 Graphics"}, \ {0x1002, 0x9612, CHIP_RS780|RADEON_NEW_MEMMAP|RADEON_IS_IGP, "ATI Radeon HD 3200 Graphics"}, \ @@ -330,441 +869,120 @@ {0x1002, 0x9614, CHIP_RS780|RADEON_NEW_MEMMAP|RADEON_IS_IGP, "ATI Radeon 3300 Graphics"}, \ {0x1002, 0x9615, CHIP_RS780|RADEON_NEW_MEMMAP|RADEON_IS_IGP, "ATI Radeon 3200 Graphics"}, \ {0x1002, 0x9616, CHIP_RS780|RADEON_NEW_MEMMAP|RADEON_IS_IGP, "ATI Radeon 3000 Graphics"}, \ + {0x1002, 0x9640, CHIP_SUMO|RADEON_NEW_MEMMAP|RADEON_IS_IGP, "BeaverCreek [Radeon HD 6550D]"}, \ + {0x1002, 0x9641, CHIP_SUMO|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP, "BeaverCreek [Mobility Radeon HD 6620G]"}, \ + {0x1002, 0x9642, CHIP_SUMO2|RADEON_NEW_MEMMAP|RADEON_IS_IGP, "Sumo [Radeon HD 6370D]"}, \ + {0x1002, 0x9643, CHIP_SUMO2|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP, "Sumo [Radeon HD 6380G]"}, \ + {0x1002, 0x9644, CHIP_SUMO2|RADEON_NEW_MEMMAP|RADEON_IS_IGP, "Sumo [Radeon HD 6410D]"}, \ + {0x1002, 0x9645, CHIP_SUMO2|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP, "Sumo [Radeon HD 6410D]"}, \ + {0x1002, 0x9647, CHIP_SUMO|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP, "BeaverCreek [Radeon HD 6520G]"}, \ + {0x1002, 0x9648, CHIP_SUMO|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP, "Sumo [Radeon HD 6480G]"}, \ + {0x1002, 0x9649, CHIP_SUMO|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP, "Sumo [Radeon HD 6480G]"}, \ + {0x1002, 0x964A, CHIP_SUMO|RADEON_NEW_MEMMAP|RADEON_IS_IGP, "BeaverCreek [Radeon HD 6530D]"}, \ + {0x1002, 0x964B, CHIP_SUMO|RADEON_NEW_MEMMAP|RADEON_IS_IGP, "Sumo"}, \ + {0x1002, 0x964C, CHIP_SUMO|RADEON_NEW_MEMMAP|RADEON_IS_IGP, "Sumo"}, \ + {0x1002, 0x964E, CHIP_SUMO|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP, "Sumo"}, \ + {0x1002, 0x964F, CHIP_SUMO|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP, "Sumo"}, \ {0x1002, 0x9710, CHIP_RS880|RADEON_NEW_MEMMAP|RADEON_IS_IGP, "ATI Radeon HD 4200"}, \ {0x1002, 0x9711, CHIP_RS880|RADEON_NEW_MEMMAP|RADEON_IS_IGP, "ATI Radeon 4100"}, \ {0x1002, 0x9712, CHIP_RS880|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP, "ATI Mobility Radeon HD 4200"}, \ {0x1002, 0x9713, CHIP_RS880|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP, "ATI Mobility Radeon 4100"}, \ {0x1002, 0x9714, CHIP_RS880|RADEON_NEW_MEMMAP|RADEON_IS_IGP, "ATI RS880"}, \ {0x1002, 0x9715, CHIP_RS880|RADEON_NEW_MEMMAP|RADEON_IS_IGP, "ATI Radeon HD 4250"}, \ - {0x1002, 0x9440, CHIP_RV770|RADEON_NEW_MEMMAP, "ATI Radeon 4800 Series"}, \ - {0x1002, 0x9441, CHIP_RV770|RADEON_NEW_MEMMAP, "ATI Radeon 4870 X2"}, \ - {0x1002, 0x9442, CHIP_RV770|RADEON_NEW_MEMMAP, "ATI Radeon 4800 Series"}, \ - {0x1002, 0x9443, CHIP_RV770|RADEON_NEW_MEMMAP, "ATI Radeon 4850 X2"}, \ - {0x1002, 0x944C, CHIP_RV770|RADEON_NEW_MEMMAP, "ATI Radeon 4800 Series"}, \ - {0x1002, 0x9450, CHIP_RV770|RADEON_NEW_MEMMAP, "AMD FireStream 9270"}, \ - {0x1002, 0x9452, CHIP_RV770|RADEON_NEW_MEMMAP, "AMD FireStream 9250"}, \ - {0x1002, 0x9444, CHIP_RV770|RADEON_NEW_MEMMAP, "ATI FirePro V8750 (FireGL)"}, \ - {0x1002, 0x9446, CHIP_RV770|RADEON_NEW_MEMMAP, "ATI FirePro V7760 (FireGL)"}, \ - {0x1002, 0x9456, CHIP_RV770|RADEON_NEW_MEMMAP, "ATI FirePro V8700 (FireGL)"}, \ - {0x1002, 0x944E, CHIP_RV770|RADEON_NEW_MEMMAP, "ATI FirePro RV770"}, \ - {0x1002, 0x944A, CHIP_RV770|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP, "ATI Mobility Radeon HD 4850"}, \ - {0x1002, 0x944B, CHIP_RV770|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP, "ATI Mobility Radeon HD 4850 X2"}, \ - {0x1002, 0x945A, CHIP_RV770|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP, "ATI Mobility Radeon HD 4870"}, \ - {0x1002, 0x945B, CHIP_RV770|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP, "ATI Mobility Radeon M98"}, \ - {0x1002, 0x9460, CHIP_RV770|RADEON_NEW_MEMMAP, "ATI Radeon 4800 Series"}, \ - {0x1002, 0x9462, CHIP_RV770|RADEON_NEW_MEMMAP, "ATI Radeon 4800 Series"}, \ - {0x1002, 0x946A, CHIP_RV770|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP, "ATI FirePro M7750"}, \ - {0x1002, 0x946B, CHIP_RV770|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP, "ATI M98"}, \ - {0x1002, 0x947A, CHIP_RV770|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP, "ATI M98"}, \ - {0x1002, 0x947B, CHIP_RV770|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP, "ATI M98"}, \ - {0x1002, 0x9487, CHIP_RV730|RADEON_NEW_MEMMAP, "ATI Radeon RV730 (AGP)"}, \ - {0x1002, 0x948F, CHIP_RV730|RADEON_NEW_MEMMAP, "ATI Radeon RV730 (AGP)"}, \ - {0x1002, 0x9490, CHIP_RV730|RADEON_NEW_MEMMAP, "ATI Radeon HD 4670"}, \ - {0x1002, 0x9495, CHIP_RV730|RADEON_NEW_MEMMAP, "ATI Radeon HD 4600 Series"}, \ - {0x1002, 0x9498, CHIP_RV730|RADEON_NEW_MEMMAP, "ATI Radeon HD 4650"}, \ - {0x1002, 0x9480, CHIP_RV730|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP, "ATI Mobility Radeon HD 4650"}, \ - {0x1002, 0x9488, CHIP_RV730|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP, "ATI Mobility Radeon HD 4670"}, \ - {0x1002, 0x9489, CHIP_RV730|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP, "ATI FirePro M5750"}, \ - {0x1002, 0x9491, CHIP_RV730|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP, "ATI RADEON E4600"}, \ - {0x1002, 0x949C, CHIP_RV730|RADEON_NEW_MEMMAP, "ATI FirePro V7750 (FireGL)"}, \ - {0x1002, 0x949E, CHIP_RV730|RADEON_NEW_MEMMAP, "ATI FirePro V5700 (FireGL)"}, \ - {0x1002, 0x949F, CHIP_RV730|RADEON_NEW_MEMMAP, "ATI FirePro V3750 (FireGL)"}, \ - {0x1002, 0x9540, CHIP_RV710|RADEON_NEW_MEMMAP, "ATI Radeon HD 4550"}, \ - {0x1002, 0x9541, CHIP_RV710|RADEON_NEW_MEMMAP, "ATI Radeon RV710"}, \ - {0x1002, 0x9542, CHIP_RV710|RADEON_NEW_MEMMAP, "ATI Radeon RV710"}, \ - {0x1002, 0x954E, CHIP_RV710|RADEON_NEW_MEMMAP, "ATI Radeon RV710"}, \ - {0x1002, 0x954F, CHIP_RV710|RADEON_NEW_MEMMAP, "ATI Radeon HD 4350"}, \ - {0x1002, 0x9552, CHIP_RV710|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP, "ATI Mobility Radeon 4300 Series"}, \ - {0x1002, 0x9553, CHIP_RV710|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP, "ATI Mobility Radeon 4500 Series"}, \ - {0x1002, 0x9555, CHIP_RV710|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP, "ATI Mobility Radeon 4500 Series"}, \ - {0x1002, 0x9557, CHIP_RV710|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP, "ATI FirePro RG220"}, \ + {0x1002, 0x9802, CHIP_PALM|RADEON_NEW_MEMMAP|RADEON_IS_IGP, "Wrestler [Radeon HD 6310]"}, \ + {0x1002, 0x9803, CHIP_PALM|RADEON_NEW_MEMMAP|RADEON_IS_IGP, "Wrestler [Radeon HD 6310]"}, \ + {0x1002, 0x9804, CHIP_PALM|RADEON_NEW_MEMMAP|RADEON_IS_IGP, "Wrestler [Radeon HD 6250]"}, \ + {0x1002, 0x9805, CHIP_PALM|RADEON_NEW_MEMMAP|RADEON_IS_IGP, "Wrestler [Radeon HD 6250]"}, \ + {0x1002, 0x9806, CHIP_PALM|RADEON_NEW_MEMMAP|RADEON_IS_IGP, "Wrestler [Radeon HD 6320]"}, \ + {0x1002, 0x9807, CHIP_PALM|RADEON_NEW_MEMMAP|RADEON_IS_IGP, "Wrestler [Radeon HD 6290]"}, \ + {0x1002, 0x9808, CHIP_PALM|RADEON_NEW_MEMMAP|RADEON_IS_IGP, "Wrestler [Radeon HD 7340]"}, \ + {0x1002, 0x9809, CHIP_PALM|RADEON_NEW_MEMMAP|RADEON_IS_IGP, "Wrestler [Radeon HD 7310]"}, \ + {0x1002, 0x980A, CHIP_PALM|RADEON_NEW_MEMMAP|RADEON_IS_IGP, "Wrestler [Radeon HD 7290]"}, \ + {0x1002, 0x9900, CHIP_ARUBA|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP, "Unknown device name"}, \ + {0x1002, 0x9901, CHIP_ARUBA|RADEON_NEW_MEMMAP|RADEON_IS_IGP, "Trinity [Radeon HD 7660D]"}, \ + {0x1002, 0x9903, CHIP_ARUBA|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP, "Trinity [Radeon HD 7640G]"}, \ + {0x1002, 0x9904, CHIP_ARUBA|RADEON_NEW_MEMMAP|RADEON_IS_IGP, "Trinity [Radeon HD 7560D]"}, \ + {0x1002, 0x9905, CHIP_ARUBA|RADEON_NEW_MEMMAP|RADEON_IS_IGP, "Trinity [FirePro A300 Series Graphics]"}, \ + {0x1002, 0x9906, CHIP_ARUBA|RADEON_NEW_MEMMAP|RADEON_IS_IGP, "Trinity [FirePro A300 Series Graphics]"}, \ + {0x1002, 0x9907, CHIP_ARUBA|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP, "Trinity [Radeon HD 7620G]"}, \ + {0x1002, 0x9908, CHIP_ARUBA|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP, "Trinity [Radeon HD 7600G]"}, \ + {0x1002, 0x9909, CHIP_ARUBA|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP, "Trinity [Radeon HD 7500G]"}, \ + {0x1002, 0x990A, CHIP_ARUBA|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP, "Trinity [Radeon HD 7500G]"}, \ + {0x1002, 0x990F, CHIP_ARUBA|RADEON_NEW_MEMMAP|RADEON_IS_IGP, "Unknown device name"}, \ + {0x1002, 0x9910, CHIP_ARUBA|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP, "Trinity [Radeon HD 7660G]"}, \ + {0x1002, 0x9913, CHIP_ARUBA|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP, "Trinity [Radeon HD 7640G]"}, \ + {0x1002, 0x9917, CHIP_ARUBA|RADEON_NEW_MEMMAP|RADEON_IS_IGP, "Trinity [Radeon HD 7620G]"}, \ + {0x1002, 0x9918, CHIP_ARUBA|RADEON_NEW_MEMMAP|RADEON_IS_IGP, "Trinity [Radeon HD 7600G]"}, \ + {0x1002, 0x9919, CHIP_ARUBA|RADEON_NEW_MEMMAP|RADEON_IS_IGP, "Trinity [Radeon HD 7500G]"}, \ + {0x1002, 0x9990, CHIP_ARUBA|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP, "Trinity [Radeon HD 7520G]"}, \ + {0x1002, 0x9991, CHIP_ARUBA|RADEON_NEW_MEMMAP|RADEON_IS_IGP, "Trinity [Radeon HD 7540D]"}, \ + {0x1002, 0x9992, CHIP_ARUBA|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP, "Trinity [Radeon HD 7420G]"}, \ + {0x1002, 0x9993, CHIP_ARUBA|RADEON_NEW_MEMMAP|RADEON_IS_IGP, "Trinity [Radeon HD 7480D]"}, \ + {0x1002, 0x9994, CHIP_ARUBA|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP, "Trinity [Radeon HD 7400G]"}, \ + {0x1002, 0x99A0, CHIP_ARUBA|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP, "Trinity [Radeon HD 7520G]"}, \ + {0x1002, 0x99A2, CHIP_ARUBA|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP, "Trinity [Radeon HD 7420G]"}, \ + {0x1002, 0x99A4, CHIP_ARUBA|RADEON_NEW_MEMMAP|RADEON_IS_IGP, "Trinity [Radeon HD 7400G]"}, \ {0, 0, 0, NULL} -#define r128_PCI_IDS \ - {0x1002, 0x4c45, 0, "ATI Rage 128 Mobility LE (PCI)"}, \ - {0x1002, 0x4c46, 0, "ATI Rage 128 Mobility LF (AGP)"}, \ - {0x1002, 0x4d46, 0, "ATI Rage 128 Mobility MF (AGP)"}, \ - {0x1002, 0x4d4c, 0, "ATI Rage 128 Mobility ML (AGP)"}, \ - {0x1002, 0x5041, 0, "ATI Rage 128 Pro PA (PCI)"}, \ - {0x1002, 0x5042, 0, "ATI Rage 128 Pro PB (AGP)"}, \ - {0x1002, 0x5043, 0, "ATI Rage 128 Pro PC (AGP)"}, \ - {0x1002, 0x5044, 0, "ATI Rage 128 Pro PD (PCI)"}, \ - {0x1002, 0x5045, 0, "ATI Rage 128 Pro PE (AGP)"}, \ - {0x1002, 0x5046, 0, "ATI Rage 128 Pro PF (AGP)"}, \ - {0x1002, 0x5047, 0, "ATI Rage 128 Pro PG (PCI)"}, \ - {0x1002, 0x5048, 0, "ATI Rage 128 Pro PH (AGP)"}, \ - {0x1002, 0x5049, 0, "ATI Rage 128 Pro PI (AGP)"}, \ - {0x1002, 0x504A, 0, "ATI Rage 128 Pro PJ (PCI)"}, \ - {0x1002, 0x504B, 0, "ATI Rage 128 Pro PK (AGP)"}, \ - {0x1002, 0x504C, 0, "ATI Rage 128 Pro PL (AGP)"}, \ - {0x1002, 0x504D, 0, "ATI Rage 128 Pro PM (PCI)"}, \ - {0x1002, 0x504E, 0, "ATI Rage 128 Pro PN (AGP)"}, \ - {0x1002, 0x504F, 0, "ATI Rage 128 Pro PO (AGP)"}, \ - {0x1002, 0x5050, 0, "ATI Rage 128 Pro PP (PCI)"}, \ - {0x1002, 0x5051, 0, "ATI Rage 128 Pro PQ (AGP)"}, \ - {0x1002, 0x5052, 0, "ATI Rage 128 Pro PR (PCI)"}, \ - {0x1002, 0x5053, 0, "ATI Rage 128 Pro PS (PCI)"}, \ - {0x1002, 0x5054, 0, "ATI Rage 128 Pro PT (AGP)"}, \ - {0x1002, 0x5055, 0, "ATI Rage 128 Pro PU (AGP)"}, \ - {0x1002, 0x5056, 0, "ATI Rage 128 Pro PV (PCI)"}, \ - {0x1002, 0x5057, 0, "ATI Rage 128 Pro PW (AGP)"}, \ - {0x1002, 0x5058, 0, "ATI Rage 128 Pro PX (AGP)"}, \ - {0x1002, 0x5245, 0, "ATI Rage 128 RE (PCI)"}, \ - {0x1002, 0x5246, 0, "ATI Rage 128 RF (AGP)"}, \ - {0x1002, 0x5247, 0, "ATI Rage 128 RG (AGP)"}, \ - {0x1002, 0x524b, 0, "ATI Rage 128 RK (PCI)"}, \ - {0x1002, 0x524c, 0, "ATI Rage 128 RL (AGP)"}, \ - {0x1002, 0x534d, 0, "ATI Rage 128 SM (AGP)"}, \ - {0x1002, 0x5446, 0, "ATI Rage 128 Pro Ultra TF (AGP)"}, \ - {0x1002, 0x544C, 0, "ATI Rage 128 Pro Ultra TL (AGP)"}, \ - {0x1002, 0x5452, 0, "ATI Rage 128 Pro Ultra TR (AGP)"}, \ - {0, 0, 0, NULL} - -#define mga_PCI_IDS \ - {0x102b, 0x0520, MGA_CARD_TYPE_G200, "Matrox G200 (PCI)"}, \ - {0x102b, 0x0521, MGA_CARD_TYPE_G200, "Matrox G200 (AGP)"}, \ - {0x102b, 0x0525, MGA_CARD_TYPE_G400, "Matrox G400/G450 (AGP)"}, \ - {0x102b, 0x2527, MGA_CARD_TYPE_G550, "Matrox G550 (AGP)"}, \ - {0, 0, 0, NULL} - -#define mach64_PCI_IDS \ - {0x1002, 0x4749, 0, "3D Rage Pro"}, \ - {0x1002, 0x4750, 0, "3D Rage Pro 215GP"}, \ - {0x1002, 0x4751, 0, "3D Rage Pro 215GQ"}, \ - {0x1002, 0x4742, 0, "3D Rage Pro AGP 1X/2X"}, \ - {0x1002, 0x4744, 0, "3D Rage Pro AGP 1X"}, \ - {0x1002, 0x4c49, 0, "3D Rage LT Pro"}, \ - {0x1002, 0x4c50, 0, "3D Rage LT Pro"}, \ - {0x1002, 0x4c51, 0, "3D Rage LT Pro"}, \ - {0x1002, 0x4c42, 0, "3D Rage LT Pro AGP-133"}, \ - {0x1002, 0x4c44, 0, "3D Rage LT Pro AGP-66"}, \ - {0x1002, 0x474c, 0, "Rage XC"}, \ - {0x1002, 0x474f, 0, "Rage XL"}, \ - {0x1002, 0x4752, 0, "Rage XL"}, \ - {0x1002, 0x4753, 0, "Rage XC"}, \ - {0x1002, 0x474d, 0, "Rage XL AGP 2X"}, \ - {0x1002, 0x474e, 0, "Rage XC AGP"}, \ - {0x1002, 0x4c52, 0, "Rage Mobility P/M"}, \ - {0x1002, 0x4c53, 0, "Rage Mobility L"}, \ - {0x1002, 0x4c4d, 0, "Rage Mobility P/M AGP 2X"}, \ - {0x1002, 0x4c4e, 0, "Rage Mobility L AGP 2X"}, \ +#define savage_PCI_IDS \ + {0x5333, 0x8A20, S3_SAVAGE3D, "Savage 3D"}, \ + {0x5333, 0x8A21, S3_SAVAGE3D, "Savage 3D/MV"}, \ + {0x5333, 0x8A22, S3_SAVAGE4, "Savage4"}, \ + {0x5333, 0x8A23, S3_SAVAGE4, "Savage4"}, \ + {0x5333, 0x8A25, S3_PROSAVAGE, "ProSavage PM133"}, \ + {0x5333, 0x8A26, S3_PROSAVAGE, "ProSavage KM133"}, \ + {0x5333, 0x8C10, S3_SAVAGE_MX, "Savage/MX-MV"}, \ + {0x5333, 0x8C11, S3_SAVAGE_MX, "Savage/MX"}, \ + {0x5333, 0x8C12, S3_SAVAGE_MX, "Savage/IX-MV"}, \ + {0x5333, 0x8C13, S3_SAVAGE_MX, "Savage/IX"}, \ + {0x5333, 0x8C22, S3_SUPERSAVAGE, "SuperSavage MX/128"}, \ + {0x5333, 0x8C24, S3_SUPERSAVAGE, "SuperSavage MX/64"}, \ + {0x5333, 0x8C26, S3_SUPERSAVAGE, "SuperSavage MX/64C"}, \ + {0x5333, 0x8C2A, S3_SUPERSAVAGE, "SuperSavage IX/128 SDR"}, \ + {0x5333, 0x8C2B, S3_SUPERSAVAGE, "SuperSavage IX/128 DDR"}, \ + {0x5333, 0x8C2C, S3_SUPERSAVAGE, "SuperSavage IX/64 SDR"}, \ + {0x5333, 0x8C2D, S3_SUPERSAVAGE, "SuperSavage IX/64 DDR"}, \ + {0x5333, 0x8C2E, S3_SUPERSAVAGE, "SuperSavage IX/C SDR"}, \ + {0x5333, 0x8C2F, S3_SUPERSAVAGE, "SuperSavage IX/C DDR"}, \ + {0x5333, 0x8D01, S3_TWISTER, "ProSavage Twister PN133"}, \ + {0x5333, 0x8D02, S3_TWISTER, "ProSavage Twister KN133"}, \ + {0x5333, 0x8D03, S3_PROSAVAGEDDR, "ProSavage DDR"}, \ + {0x5333, 0x8D04, S3_PROSAVAGEDDR, "ProSavage DDR-K"}, \ {0, 0, 0, NULL} #define sis_PCI_IDS \ + {0x18CA, 0x0040, SIS_CHIP_315, "Volari V3XT/V5/V8"}, \ + {0x18CA, 0x0042, SIS_CHIP_315, "Volari Unknown"}, \ {0x1039, 0x0300, 0, "SiS 300/305"}, \ {0x1039, 0x5300, 0, "SiS 540"}, \ {0x1039, 0x6300, 0, "SiS 630"}, \ {0x1039, 0x6330, SIS_CHIP_315, "SiS 661"}, \ {0x1039, 0x7300, 0, "SiS 730"}, \ - {0x18CA, 0x0040, SIS_CHIP_315, "Volari V3XT/V5/V8"}, \ - {0x18CA, 0x0042, SIS_CHIP_315, "Volari Unknown"}, \ {0, 0, 0, NULL} #define tdfx_PCI_IDS \ - {0x121a, 0x0003, 0, "3dfx Voodoo Banshee"}, \ - {0x121a, 0x0004, 0, "3dfx Voodoo3 2000"}, \ - {0x121a, 0x0005, 0, "3dfx Voodoo3 3000"}, \ - {0x121a, 0x0007, 0, "3dfx Voodoo4 4500"}, \ - {0x121a, 0x0009, 0, "3dfx Voodoo5 5500"}, \ - {0x121a, 0x000b, 0, "3dfx Voodoo4 4200"}, \ + {0x121A, 0x0003, 0, "3dfx Voodoo Banshee"}, \ + {0x121A, 0x0004, 0, "3dfx Voodoo3 2000"}, \ + {0x121A, 0x0005, 0, "3dfx Voodoo3 3000"}, \ + {0x121A, 0x0007, 0, "3dfx Voodoo4 4500"}, \ + {0x121A, 0x0009, 0, "3dfx Voodoo5 5500"}, \ + {0x121A, 0x000B, 0, "3dfx Voodoo4 4200"}, \ {0, 0, 0, NULL} #define viadrv_PCI_IDS \ {0x1106, 0x3022, 0, "VIA CLE266 3022"}, \ + {0x1106, 0x3108, 0, "VIA K8M800"}, \ {0x1106, 0x3118, VIA_PRO_GROUP_A, "VIA CN400 / PM8X0"}, \ {0x1106, 0x3122, 0, "VIA CLE266"}, \ - {0x1106, 0x7205, 0, "VIA KM400"}, \ - {0x1106, 0x3108, 0, "VIA K8M800"}, \ - {0x1106, 0x3344, 0, "VIA CN700 / VM800 / P4M800Pro"}, \ - {0x1106, 0x3343, 0, "VIA P4M890"}, \ - {0x1106, 0x3230, VIA_DX9_0, "VIA K8M890"}, \ {0x1106, 0x3157, VIA_PRO_GROUP_A, "VIA CX700"}, \ + {0x1106, 0x3230, VIA_DX9_0, "VIA K8M890"}, \ + {0x1106, 0x3343, 0, "VIA P4M890"}, \ + {0x1106, 0x3344, 0, "VIA CN700 / VM800 / P4M800Pro"}, \ {0x1106, 0x3371, VIA_DX9_0, "VIA P4M900 / VN896"}, \ - {0, 0, 0, NULL} - -#define i810_PCI_IDS \ - {0x8086, 0x7121, 0, "Intel i810 GMCH"}, \ - {0x8086, 0x7123, 0, "Intel i810-DC100 GMCH"}, \ - {0x8086, 0x7125, 0, "Intel i810E GMCH"}, \ - {0x8086, 0x1132, 0, "Intel i815 GMCH"}, \ - {0, 0, 0, NULL} - -#define i830_PCI_IDS \ - {0x8086, 0x3577, 0, "Intel i830M GMCH"}, \ - {0x8086, 0x2562, 0, "Intel i845G GMCH"}, \ - {0x8086, 0x3582, 0, "Intel i852GM/i855GM GMCH"}, \ - {0x8086, 0x2572, 0, "Intel i865G GMCH"}, \ - {0, 0, 0, NULL} - -#define gamma_PCI_IDS \ - {0x3d3d, 0x0008, 0, "3DLabs GLINT Gamma G1"}, \ - {0, 0, 0, NULL} - -#define savage_PCI_IDS \ - {0x5333, 0x8a20, S3_SAVAGE3D, "Savage 3D"}, \ - {0x5333, 0x8a21, S3_SAVAGE3D, "Savage 3D/MV"}, \ - {0x5333, 0x8a22, S3_SAVAGE4, "Savage4"}, \ - {0x5333, 0x8a23, S3_SAVAGE4, "Savage4"}, \ - {0x5333, 0x8c10, S3_SAVAGE_MX, "Savage/MX-MV"}, \ - {0x5333, 0x8c11, S3_SAVAGE_MX, "Savage/MX"}, \ - {0x5333, 0x8c12, S3_SAVAGE_MX, "Savage/IX-MV"}, \ - {0x5333, 0x8c13, S3_SAVAGE_MX, "Savage/IX"}, \ - {0x5333, 0x8c22, S3_SUPERSAVAGE, "SuperSavage MX/128"}, \ - {0x5333, 0x8c24, S3_SUPERSAVAGE, "SuperSavage MX/64"}, \ - {0x5333, 0x8c26, S3_SUPERSAVAGE, "SuperSavage MX/64C"}, \ - {0x5333, 0x8c2a, S3_SUPERSAVAGE, "SuperSavage IX/128 SDR"}, \ - {0x5333, 0x8c2b, S3_SUPERSAVAGE, "SuperSavage IX/128 DDR"}, \ - {0x5333, 0x8c2c, S3_SUPERSAVAGE, "SuperSavage IX/64 SDR"}, \ - {0x5333, 0x8c2d, S3_SUPERSAVAGE, "SuperSavage IX/64 DDR"}, \ - {0x5333, 0x8c2e, S3_SUPERSAVAGE, "SuperSavage IX/C SDR"}, \ - {0x5333, 0x8c2f, S3_SUPERSAVAGE, "SuperSavage IX/C DDR"}, \ - {0x5333, 0x8a25, S3_PROSAVAGE, "ProSavage PM133"}, \ - {0x5333, 0x8a26, S3_PROSAVAGE, "ProSavage KM133"}, \ - {0x5333, 0x8d01, S3_TWISTER, "ProSavage Twister PN133"}, \ - {0x5333, 0x8d02, S3_TWISTER, "ProSavage Twister KN133"}, \ - {0x5333, 0x8d03, S3_PROSAVAGEDDR, "ProSavage DDR"}, \ - {0x5333, 0x8d04, S3_PROSAVAGEDDR, "ProSavage DDR-K"}, \ - {0, 0, 0, NULL} - -#define ffb_PCI_IDS \ - {0, 0, 0, NULL} - -#define i915_PCI_IDS \ - {0x8086, 0x3577, CHIP_I8XX, "Intel i830M GMCH"}, \ - {0x8086, 0x2562, CHIP_I8XX, "Intel i845G GMCH"}, \ - {0x8086, 0x3582, CHIP_I8XX, "Intel i852GM/i855GM GMCH"}, \ - {0x8086, 0x358e, CHIP_I8XX, "Intel i852GM/i855GM GMCH"}, \ - {0x8086, 0x2572, CHIP_I8XX, "Intel i865G GMCH"}, \ - {0x8086, 0x2582, CHIP_I9XX|CHIP_I915, "Intel i915G"}, \ - {0x8086, 0x258a, CHIP_I9XX|CHIP_I915, "Intel E7221 (i915)"}, \ - {0x8086, 0x2592, CHIP_I9XX|CHIP_I915, "Intel i915GM"}, \ - {0x8086, 0x2772, CHIP_I9XX|CHIP_I915, "Intel i945G"}, \ - {0x8086, 0x27A2, CHIP_I9XX|CHIP_I915, "Intel i945GM"}, \ - {0x8086, 0x27AE, CHIP_I9XX|CHIP_I915, "Intel i945GME"}, \ - {0x8086, 0x2972, CHIP_I9XX|CHIP_I965, "Intel i946GZ"}, \ - {0x8086, 0x2982, CHIP_I9XX|CHIP_I965, "Intel i965G"}, \ - {0x8086, 0x2992, CHIP_I9XX|CHIP_I965, "Intel i965Q"}, \ - {0x8086, 0x29A2, CHIP_I9XX|CHIP_I965, "Intel i965G"}, \ - {0x8086, 0x29B2, CHIP_I9XX|CHIP_I915, "Intel Q35"}, \ - {0x8086, 0x29C2, CHIP_I9XX|CHIP_I915, "Intel G33"}, \ - {0x8086, 0x29D2, CHIP_I9XX|CHIP_I915, "Intel Q33"}, \ - {0x8086, 0x2A02, CHIP_I9XX|CHIP_I965, "Intel i965GM"}, \ - {0x8086, 0x2A12, CHIP_I9XX|CHIP_I965, "Intel i965GME/GLE"}, \ - {0x8086, 0x2A42, CHIP_I9XX|CHIP_I965, "Mobile Intel® GM45 Express Chipset"}, \ - {0x8086, 0x2E02, CHIP_I9XX|CHIP_I965, "Intel Eaglelake"}, \ - {0x8086, 0x2E12, CHIP_I9XX|CHIP_I965, "Intel Q45/Q43"}, \ - {0x8086, 0x2E22, CHIP_I9XX|CHIP_I965, "Intel G45/G43"}, \ - {0x8086, 0x2E32, CHIP_I9XX|CHIP_I965, "Intel G41"}, \ - {0x8086, 0x2e42, CHIP_I9XX|CHIP_I915, "Intel G43 ?"}, \ - {0x8086, 0x2e92, CHIP_I9XX|CHIP_I915, "Intel G43 ?"}, \ - {0x8086, 0x0042, CHIP_I9XX|CHIP_I915, "Intel IronLake"}, \ - {0x8086, 0x0046, CHIP_I9XX|CHIP_I915, "Intel IronLake"}, \ - {0x8086, 0x0102, CHIP_I9XX|CHIP_I915, "Intel SandyBridge"}, \ - {0x8086, 0x0112, CHIP_I9XX|CHIP_I915, "Intel SandyBridge"}, \ - {0x8086, 0x0122, CHIP_I9XX|CHIP_I915, "Intel SandyBridge"}, \ - {0x8086, 0x0106, CHIP_I9XX|CHIP_I915, "Intel SandyBridge (M)"}, \ - {0x8086, 0x0116, CHIP_I9XX|CHIP_I915, "Intel SandyBridge (M)"}, \ - {0x8086, 0x0126, CHIP_I9XX|CHIP_I915, "Intel SandyBridge (M)"}, \ - {0x8086, 0x010A, CHIP_I9XX|CHIP_I915, "Intel SandyBridge (M)"}, \ - {0x8086, 0x0152, CHIP_I9XX|CHIP_I915, "Intel IvyBridge"}, \ - {0x8086, 0x0162, CHIP_I9XX|CHIP_I915, "Intel IvyBridge"}, \ - {0x8086, 0x0156, CHIP_I9XX|CHIP_I915, "Intel IvyBridge (M)"}, \ - {0x8086, 0x0166, CHIP_I9XX|CHIP_I915, "Intel IvyBridge (M)"}, \ - {0x8086, 0x015A, CHIP_I9XX|CHIP_I915, "Intel IvyBridge (S)"}, \ - {0x8086, 0x016A, CHIP_I9XX|CHIP_I915, "Intel IvyBridge (S)"}, \ - {0x8086, 0xA001, CHIP_I9XX|CHIP_I965, "Intel Pineview"}, \ - {0x8086, 0xA011, CHIP_I9XX|CHIP_I965, "Intel Pineview (M)"}, \ - {0, 0, 0, NULL} - -#define imagine_PCI_IDS \ - {0x105d, 0x2309, IMAGINE_128, "Imagine 128"}, \ - {0x105d, 0x2339, IMAGINE_128_2, "Imagine 128-II"}, \ - {0x105d, 0x493d, IMAGINE_T2R, "Ticket to Ride"}, \ - {0x105d, 0x5348, IMAGINE_REV4, "Revolution IV"}, \ - {0, 0, 0, NULL} - -#define nv_PCI_IDS \ - {0x10DE, 0x0020, NV04, "NVidia RIVA TNT"}, \ - {0x10DE, 0x0028, NV04, "NVidia RIVA TNT2"}, \ - {0x10DE, 0x002A, NV04, "NVidia Unknown TNT2"}, \ - {0x10DE, 0x002C, NV04, "NVidia Vanta"}, \ - {0x10DE, 0x0029, NV04, "NVidia RIVA TNT2 Ultra"}, \ - {0x10DE, 0x002D, NV04, "NVidia RIVA TNT2 Model 64"}, \ - {0x10DE, 0x00A0, NV04, "NVidia Aladdin TNT2"}, \ - {0x10DE, 0x0100, NV10, "NVidia GeForce 256"}, \ - {0x10DE, 0x0101, NV10, "NVidia GeForce DDR"}, \ - {0x10DE, 0x0103, NV10, "NVidia Quadro"}, \ - {0x10DE, 0x0110, NV10, "NVidia GeForce2 MX/MX 400"}, \ - {0x10DE, 0x0111, NV10, "NVidia GeForce2 MX 100/200"}, \ - {0x10DE, 0x0112, NV10, "NVidia GeForce2 Go"}, \ - {0x10DE, 0x0113, NV10, "NVidia Quadro2 MXR/EX/Go"}, \ - {0x10DE, 0x0150, NV10, "NVidia GeForce2 GTS"}, \ - {0x10DE, 0x0151, NV10, "NVidia GeForce2 Ti"}, \ - {0x10DE, 0x0152, NV10, "NVidia GeForce2 Ultra"}, \ - {0x10DE, 0x0153, NV10, "NVidia Quadro2 Pro"}, \ - {0x10DE, 0x0170, NV10, "NVidia GeForce4 MX 460"}, \ - {0x10DE, 0x0171, NV10, "NVidia GeForce4 MX 440"}, \ - {0x10DE, 0x0172, NV10, "NVidia GeForce4 MX 420"}, \ - {0x10DE, 0x0173, NV10, "NVidia GeForce4 MX 440-SE"}, \ - {0x10DE, 0x0174, NV10, "NVidia GeForce4 440 Go"}, \ - {0x10DE, 0x0175, NV10, "NVidia GeForce4 420 Go"}, \ - {0x10DE, 0x0176, NV10, "NVidia GeForce4 420 Go 32M"}, \ - {0x10DE, 0x0177, NV10, "NVidia GeForce4 460 Go"}, \ - {0x10DE, 0x0178, NV10, "NVidia Quadro4 550 XGL"}, \ - {0x10DE, 0x0179, NV10, "NVidia GeForce4"}, \ - {0x10DE, 0x017A, NV10, "NVidia Quadro4 NVS"}, \ - {0x10DE, 0x017C, NV10, "NVidia Quadro4 500 GoGL"}, \ - {0x10DE, 0x017D, NV10, "NVidia GeForce4 410 Go 16M"}, \ - {0x10DE, 0x0181, NV10, "NVidia GeForce4 MX 440 with AGP8X"}, \ - {0x10DE, 0x0182, NV10, "NVidia GeForce4 MX 440SE with AGP8X"}, \ - {0x10DE, 0x0183, NV10, "NVidia GeForce4 MX 420 with AGP8X"}, \ - {0x10DE, 0x0185, NV10, "NVidia GeForce4 MX 4000"}, \ - {0x10DE, 0x0186, NV10, "NVidia GeForce4 448 Go"}, \ - {0x10DE, 0x0187, NV10, "NVidia GeForce4 488 Go"}, \ - {0x10DE, 0x0188, NV10, "NVidia Quadro4 580 XGL"}, \ - {0x10DE, 0x0189, NV10, "NVidia GeForce4 MX with AGP8X (Mac)"}, \ - {0x10DE, 0x018A, NV10, "NVidia Quadro4 280 NVS"}, \ - {0x10DE, 0x018B, NV10, "NVidia Quadro4 380 XGL"}, \ - {0x10DE, 0x018C, NV10, "NVidia Quadro NVS 50 PCI"}, \ - {0x10DE, 0x018D, NV10, "NVidia GeForce4 448 Go"}, \ - {0x10DE, 0x01A0, NV10, "NVidia GeForce2 Integrated GPU"}, \ - {0x10DE, 0x01F0, NV10, "NVidia GeForce4 MX Integrated GPU"}, \ - {0x10DE, 0x0200, NV20, "NVidia GeForce3"}, \ - {0x10DE, 0x0201, NV20, "NVidia GeForce3 Ti 200"}, \ - {0x10DE, 0x0202, NV20, "NVidia GeForce3 Ti 500"}, \ - {0x10DE, 0x0203, NV20, "NVidia Quadro DCC"}, \ - {0x10DE, 0x0250, NV20, "NVidia GeForce4 Ti 4600"}, \ - {0x10DE, 0x0251, NV20, "NVidia GeForce4 Ti 4400"}, \ - {0x10DE, 0x0252, NV20, "NVidia 0x0252"}, \ - {0x10DE, 0x0253, NV20, "NVidia GeForce4 Ti 4200"}, \ - {0x10DE, 0x0258, NV20, "NVidia Quadro4 900 XGL"}, \ - {0x10DE, 0x0259, NV20, "NVidia Quadro4 750 XGL"}, \ - {0x10DE, 0x025B, NV20, "NVidia Quadro4 700 XGL"}, \ - {0x10DE, 0x0280, NV20, "NVidia GeForce4 Ti 4800"}, \ - {0x10DE, 0x0281, NV20, "NVidia GeForce4 Ti 4200 with AGP8X"}, \ - {0x10DE, 0x0282, NV20, "NVidia GeForce4 Ti 4800 SE"}, \ - {0x10DE, 0x0286, NV20, "NVidia GeForce4 4200 Go"}, \ - {0x10DE, 0x028C, NV20, "NVidia Quadro4 700 GoGL"}, \ - {0x10DE, 0x0288, NV20, "NVidia Quadro4 980 XGL"}, \ - {0x10DE, 0x0289, NV20, "NVidia Quadro4 780 XGL"}, \ - {0x10DE, 0x0301, NV30, "NVidia GeForce FX 5800 Ultra"}, \ - {0x10DE, 0x0302, NV30, "NVidia GeForce FX 5800"}, \ - {0x10DE, 0x0308, NV30, "NVidia Quadro FX 2000"}, \ - {0x10DE, 0x0309, NV30, "NVidia Quadro FX 1000"}, \ - {0x10DE, 0x0311, NV30, "NVidia GeForce FX 5600 Ultra"}, \ - {0x10DE, 0x0312, NV30, "NVidia GeForce FX 5600"}, \ - {0x10DE, 0x0313, NV30, "NVidia 0x0313"}, \ - {0x10DE, 0x0314, NV30, "NVidia GeForce FX 5600SE"}, \ - {0x10DE, 0x0316, NV30, "NVidia 0x0316"}, \ - {0x10DE, 0x0317, NV30, "NVidia 0x0317"}, \ - {0x10DE, 0x031A, NV30, "NVidia GeForce FX Go5600"}, \ - {0x10DE, 0x031B, NV30, "NVidia GeForce FX Go5650"}, \ - {0x10DE, 0x031C, NV30, "NVidia Quadro FX Go700"}, \ - {0x10DE, 0x031D, NV30, "NVidia 0x031D"}, \ - {0x10DE, 0x031E, NV30, "NVidia 0x031E"}, \ - {0x10DE, 0x031F, NV30, "NVidia 0x031F"}, \ - {0x10DE, 0x0320, NV30, "NVidia GeForce FX 5200"}, \ - {0x10DE, 0x0321, NV30, "NVidia GeForce FX 5200 Ultra"}, \ - {0x10DE, 0x0322, NV30, "NVidia GeForce FX 5200"}, \ - {0x10DE, 0x0323, NV30, "NVidia GeForce FX 5200SE"}, \ - {0x10DE, 0x0324, NV30, "NVidia GeForce FX Go5200"}, \ - {0x10DE, 0x0325, NV30, "NVidia GeForce FX Go5250"}, \ - {0x10DE, 0x0326, NV30, "NVidia GeForce FX 5500"}, \ - {0x10DE, 0x0327, NV30, "NVidia GeForce FX 5100"}, \ - {0x10DE, 0x0328, NV30, "NVidia GeForce FX Go5200 32M/64M"}, \ - {0x10DE, 0x0329, NV30, "NVidia GeForce FX 5200 (Mac)"}, \ - {0x10DE, 0x032A, NV30, "NVidia Quadro NVS 280 PCI"}, \ - {0x10DE, 0x032B, NV30, "NVidia Quadro FX 500/600 PCI"}, \ - {0x10DE, 0x032C, NV30, "NVidia GeForce FX Go53xx Series"}, \ - {0x10DE, 0x032D, NV30, "NVidia GeForce FX Go5100"}, \ - {0x10DE, 0x032F, NV30, "NVidia 0x032F"}, \ - {0x10DE, 0x0330, NV30, "NVidia GeForce FX 5900 Ultra"}, \ - {0x10DE, 0x0331, NV30, "NVidia GeForce FX 5900"}, \ - {0x10DE, 0x0332, NV30, "NVidia GeForce FX 5900XT"}, \ - {0x10DE, 0x0333, NV30, "NVidia GeForce FX 5950 Ultra"}, \ - {0x10DE, 0x033F, NV30, "NVidia Quadro FX 700"}, \ - {0x10DE, 0x0334, NV30, "NVidia GeForce FX 5900ZT"}, \ - {0x10DE, 0x0338, NV30, "NVidia Quadro FX 3000"}, \ - {0x10DE, 0x0341, NV30, "NVidia GeForce FX 5700 Ultra"}, \ - {0x10DE, 0x0342, NV30, "NVidia GeForce FX 5700"}, \ - {0x10DE, 0x0343, NV30, "NVidia GeForce FX 5700LE"}, \ - {0x10DE, 0x0344, NV30, "NVidia GeForce FX 5700VE"}, \ - {0x10DE, 0x0345, NV30, "NVidia 0x0345"}, \ - {0x10DE, 0x0347, NV30, "NVidia GeForce FX Go5700"}, \ - {0x10DE, 0x0348, NV30, "NVidia GeForce FX Go5700"}, \ - {0x10DE, 0x0349, NV30, "NVidia 0x0349"}, \ - {0x10DE, 0x034B, NV30, "NVidia 0x034B"}, \ - {0x10DE, 0x034C, NV30, "NVidia Quadro FX Go1000"}, \ - {0x10DE, 0x034E, NV30, "NVidia Quadro FX 1100"}, \ - {0x10DE, 0x034F, NV30, "NVidia 0x034F"}, \ - {0x10DE, 0x0040, NV40, "NVidia GeForce 6800 Ultra"}, \ - {0x10DE, 0x0041, NV40, "NVidia GeForce 6800"}, \ - {0x10DE, 0x0042, NV40, "NVidia GeForce 6800 LE"}, \ - {0x10DE, 0x0043, NV40, "NVidia 0x0043"}, \ - {0x10DE, 0x0045, NV40, "NVidia GeForce 6800 GT"}, \ - {0x10DE, 0x0046, NV40, "NVidia GeForce 6800 GT"}, \ - {0x10DE, 0x0049, NV40, "NVidia 0x0049"}, \ - {0x10DE, 0x004E, NV40, "NVidia Quadro FX 4000"}, \ - {0x10DE, 0x00C0, NV40, "NVidia 0x00C0"}, \ - {0x10DE, 0x00C1, NV40, "NVidia GeForce 6800"}, \ - {0x10DE, 0x00C2, NV40, "NVidia GeForce 6800 LE"}, \ - {0x10DE, 0x00C8, NV40, "NVidia GeForce Go 6800"}, \ - {0x10DE, 0x00C9, NV40, "NVidia GeForce Go 6800 Ultra"}, \ - {0x10DE, 0x00CC, NV40, "NVidia Quadro FX Go1400"}, \ - {0x10DE, 0x00CD, NV40, "NVidia Quadro FX 3450/4000 SDI"}, \ - {0x10DE, 0x00CE, NV40, "NVidia Quadro FX 1400"}, \ - {0x10de, 0x00f0, NV40, "Nvidia GeForce 6600 GT"}, \ - {0x10de, 0x00f1, NV40, "Nvidia GeForce 6600 GT"}, \ - {0x10DE, 0x0140, NV40, "NVidia GeForce 6600 GT"}, \ - {0x10DE, 0x0141, NV40, "NVidia GeForce 6600"}, \ - {0x10DE, 0x0142, NV40, "NVidia GeForce 6600 LE"}, \ - {0x10DE, 0x0143, NV40, "NVidia 0x0143"}, \ - {0x10DE, 0x0144, NV40, "NVidia GeForce Go 6600"}, \ - {0x10DE, 0x0145, NV40, "NVidia GeForce 6610 XL"}, \ - {0x10DE, 0x0146, NV40, "NVidia GeForce Go 6600 TE/6200 TE"}, \ - {0x10DE, 0x0147, NV40, "NVidia GeForce 6700 XL"}, \ - {0x10DE, 0x0148, NV40, "NVidia GeForce Go 6600"}, \ - {0x10DE, 0x0149, NV40, "NVidia GeForce Go 6600 GT"}, \ - {0x10DE, 0x014B, NV40, "NVidia 0x014B"}, \ - {0x10DE, 0x014C, NV40, "NVidia 0x014C"}, \ - {0x10DE, 0x014D, NV40, "NVidia 0x014D"}, \ - {0x10DE, 0x014E, NV40, "NVidia Quadro FX 540"}, \ - {0x10DE, 0x014F, NV40, "NVidia GeForce 6200"}, \ - {0x10DE, 0x0160, NV40, "NVidia 0x0160"}, \ - {0x10DE, 0x0161, NV40, "NVidia GeForce 6200 TurboCache(TM)"}, \ - {0x10DE, 0x0162, NV40, "NVidia GeForce 6200SE TurboCache(TM)"}, \ - {0x10DE, 0x0163, NV40, "NVidia 0x0163"}, \ - {0x10DE, 0x0164, NV40, "NVidia GeForce Go 6200"}, \ - {0x10DE, 0x0165, NV40, "NVidia Quadro NVS 285"}, \ - {0x10DE, 0x0166, NV40, "NVidia GeForce Go 6400"}, \ - {0x10DE, 0x0167, NV40, "NVidia GeForce Go 6200"}, \ - {0x10DE, 0x0168, NV40, "NVidia GeForce Go 6400"}, \ - {0x10DE, 0x0169, NV40, "NVidia 0x0169"}, \ - {0x10DE, 0x016B, NV40, "NVidia 0x016B"}, \ - {0x10DE, 0x016C, NV40, "NVidia 0x016C"}, \ - {0x10DE, 0x016D, NV40, "NVidia 0x016D"}, \ - {0x10DE, 0x016E, NV40, "NVidia 0x016E"}, \ - {0x10DE, 0x0210, NV40, "NVidia 0x0210"}, \ - {0x10DE, 0x0211, NV40, "NVidia GeForce 6800"}, \ - {0x10DE, 0x0212, NV40, "NVidia GeForce 6800 LE"}, \ - {0x10DE, 0x0215, NV40, "NVidia GeForce 6800 GT"}, \ - {0x10DE, 0x0220, NV40, "NVidia 0x0220"}, \ - {0x10DE, 0x0221, NV40, "NVidia GeForce 6200"}, \ - {0x10DE, 0x0222, NV40, "NVidia 0x0222"}, \ - {0x10DE, 0x0228, NV40, "NVidia 0x0228"}, \ - {0x10DE, 0x0090, NV40, "NVidia 0x0090"}, \ - {0x10DE, 0x0091, NV40, "NVidia GeForce 7800 GTX"}, \ - {0x10DE, 0x0092, NV40, "NVidia 0x0092"}, \ - {0x10DE, 0x0093, NV40, "NVidia 0x0093"}, \ - {0x10DE, 0x0094, NV40, "NVidia 0x0094"}, \ - {0x10DE, 0x0098, NV40, "NVidia 0x0098"}, \ - {0x10DE, 0x0099, NV40, "NVidia GeForce Go 7800 GTX"}, \ - {0x10DE, 0x009C, NV40, "NVidia 0x009C"}, \ - {0x10DE, 0x009D, NV40, "NVidia Quadro FX 4500"}, \ - {0x10DE, 0x009E, NV40, "NVidia 0x009E"}, \ + {0x1106, 0x7205, 0, "VIA KM400"}, \ {0, 0, 0, NULL} #define xgi_PCI_IDS \ - {0x18ca, 0x2200, 0, "XP5"}, \ - {0x18ca, 0x0047, 0, "XP10 / XG47"}, \ + {0x18CA, 0x0047, 0, "XP10 / XG47"}, \ + {0x18CA, 0x2200, 0, "XP5"}, \ {0, 0, 0, NULL} diff --git a/sys/dev/drm2/drm_scatter.c b/sys/dev/drm2/drm_scatter.c index ecf231f27c7..3ff923cbc01 100644 --- a/sys/dev/drm2/drm_scatter.c +++ b/sys/dev/drm2/drm_scatter.c @@ -52,7 +52,7 @@ drm_sg_alloc(struct drm_device *dev, struct drm_scatter_gather *request) entry->busaddr = malloc(entry->pages * sizeof(*entry->busaddr), DRM_MEM_SGLISTS, M_WAITOK | M_ZERO); - entry->vaddr = kmem_alloc_attr(kernel_map, size, M_WAITOK | M_ZERO, + entry->vaddr = kmem_alloc_attr(kernel_arena, size, M_WAITOK | M_ZERO, 0, BUS_SPACE_MAXADDR_32BIT, VM_MEMATTR_WRITE_COMBINING); if (entry->vaddr == 0) { drm_sg_cleanup(entry); @@ -99,7 +99,7 @@ drm_sg_cleanup(struct drm_sg_mem *entry) return; if (entry->vaddr != 0) - kmem_free(kernel_map, entry->vaddr, IDX_TO_OFF(entry->pages)); + kmem_free(kernel_arena, entry->vaddr, IDX_TO_OFF(entry->pages)); free(entry->busaddr, DRM_MEM_SGLISTS); free(entry, DRM_MEM_DRIVER); diff --git a/sys/dev/drm2/drm_sysctl.c b/sys/dev/drm2/drm_sysctl.c index 33048c78a5d..a30c7ab237a 100644 --- a/sys/dev/drm2/drm_sysctl.c +++ b/sys/dev/drm2/drm_sysctl.c @@ -177,7 +177,15 @@ static int drm_vm_info DRM_SYSCTL_HANDLER_ARGS { struct drm_device *dev = arg1; drm_local_map_t *map, *tempmaps; - const char *types[] = { "FB", "REG", "SHM", "AGP", "SG" }; + const char *types[] = { + [_DRM_FRAME_BUFFER] = "FB", + [_DRM_REGISTERS] = "REG", + [_DRM_SHM] = "SHM", + [_DRM_AGP] = "AGP", + [_DRM_SCATTER_GATHER] = "SG", + [_DRM_CONSISTENT] = "CONS", + [_DRM_GEM] = "GEM" + }; const char *type, *yesno; int i, mapcount; char buf[128]; @@ -211,10 +219,20 @@ static int drm_vm_info DRM_SYSCTL_HANDLER_ARGS for (i = 0; i < mapcount; i++) { map = &tempmaps[i]; - if (map->type < 0 || map->type > 4) + switch(map->type) { + default: type = "??"; - else + break; + case _DRM_FRAME_BUFFER: + case _DRM_REGISTERS: + case _DRM_SHM: + case _DRM_AGP: + case _DRM_SCATTER_GATHER: + case _DRM_CONSISTENT: + case _DRM_GEM: type = types[map->type]; + break; + } if (!map->mtrr) yesno = "no"; diff --git a/sys/dev/drm2/i915/i915_gem.c b/sys/dev/drm2/i915/i915_gem.c index 6b8bce351f1..9af50d8f32d 100644 --- a/sys/dev/drm2/i915/i915_gem.c +++ b/sys/dev/drm2/i915/i915_gem.c @@ -64,6 +64,9 @@ __FBSDID("$FreeBSD$"); #include #include +#include +#include + static void i915_gem_object_flush_cpu_write_domain( struct drm_i915_gem_object *obj); static uint32_t i915_gem_get_gtt_size(struct drm_device *dev, uint32_t size, @@ -138,7 +141,7 @@ i915_gem_wait_for_error(struct drm_device *dev) } mtx_unlock(&dev_priv->error_completion_lock); - if (atomic_read(&dev_priv->mm.wedged)) { + if (atomic_load_acq_int(&dev_priv->mm.wedged)) { mtx_lock(&dev_priv->error_completion_lock); dev_priv->error_completion++; mtx_unlock(&dev_priv->error_completion_lock); @@ -740,7 +743,7 @@ i915_gem_ring_throttle(struct drm_device *dev, struct drm_file *file) int ret; dev_priv = dev->dev_private; - if (atomic_read(&dev_priv->mm.wedged)) + if (atomic_load_acq_int(&dev_priv->mm.wedged)) return (-EIO); file_priv = file->driver_priv; @@ -765,15 +768,15 @@ i915_gem_ring_throttle(struct drm_device *dev, struct drm_file *file) if (ring->irq_get(ring)) { while (ret == 0 && !(i915_seqno_passed(ring->get_seqno(ring), seqno) || - atomic_read(&dev_priv->mm.wedged))) + atomic_load_acq_int(&dev_priv->mm.wedged))) ret = -msleep(ring, &ring->irq_lock, PCATCH, "915thr", 0); ring->irq_put(ring); - if (ret == 0 && atomic_read(&dev_priv->mm.wedged)) + if (ret == 0 && atomic_load_acq_int(&dev_priv->mm.wedged)) ret = -EIO; } else if (_intel_wait_for(dev, i915_seqno_passed(ring->get_seqno(ring), seqno) || - atomic_read(&dev_priv->mm.wedged), 3000, 0, "915rtr")) { + atomic_load_acq_int(&dev_priv->mm.wedged), 3000, 0, "915rtr")) { ret = -EBUSY; } } @@ -1288,7 +1291,7 @@ i915_gem_mmap_ioctl(struct drm_device *dev, void *data, addr = 0; vm_object_reference(obj->vm_obj); DRM_UNLOCK(dev); - rv = vm_map_find(map, obj->vm_obj, args->offset, &addr, args->size, + rv = vm_map_find(map, obj->vm_obj, args->offset, &addr, args->size, 0, VMFS_OPTIMAL_SPACE, VM_PROT_READ | VM_PROT_WRITE, VM_PROT_READ | VM_PROT_WRITE, MAP_INHERIT_SHARE); if (rv != KERN_SUCCESS) { @@ -1356,9 +1359,8 @@ i915_gem_pager_fault(vm_object_t vm_obj, vm_ooffset_t offset, int prot, *mres = NULL; } else oldm = NULL; -retry: VM_OBJECT_WUNLOCK(vm_obj); -unlocked_vmobj: +retry: cause = ret = 0; m = NULL; @@ -1379,9 +1381,11 @@ unlocked_vmobj: VM_OBJECT_WLOCK(vm_obj); m = vm_page_lookup(vm_obj, OFF_TO_IDX(offset)); if (m != NULL) { - if ((m->flags & VPO_BUSY) != 0) { + if (vm_page_busied(m)) { DRM_UNLOCK(dev); - vm_page_sleep(m, "915pee"); + vm_page_lock(m); + VM_OBJECT_WUNLOCK(vm_obj); + vm_page_busy_sleep(m, "915pee"); goto retry; } goto have_page; @@ -1435,16 +1439,24 @@ unlocked_vmobj: ("not fictitious %p", m)); KASSERT(m->wire_count == 1, ("wire_count not 1 %p", m)); - if ((m->flags & VPO_BUSY) != 0) { + if (vm_page_busied(m)) { DRM_UNLOCK(dev); - vm_page_sleep(m, "915pbs"); + vm_page_lock(m); + VM_OBJECT_WUNLOCK(vm_obj); + vm_page_busy_sleep(m, "915pbs"); + goto retry; + } + if (vm_page_insert(m, vm_obj, OFF_TO_IDX(offset))) { + DRM_UNLOCK(dev); + VM_OBJECT_WUNLOCK(vm_obj); + VM_WAIT; + VM_OBJECT_WLOCK(vm_obj); goto retry; } m->valid = VM_PAGE_BITS_ALL; - vm_page_insert(m, vm_obj, OFF_TO_IDX(offset)); have_page: *mres = m; - vm_page_busy(m); + vm_page_xbusy(m); CTR4(KTR_DRM, "fault %p %jx %x phys %x", gem_obj, offset, prot, m->phys_addr); @@ -1465,7 +1477,7 @@ out: -ret, cause); if (ret == -EAGAIN || ret == -EIO || ret == -EINTR) { kern_yield(PRI_USER); - goto unlocked_vmobj; + goto retry; } VM_OBJECT_WLOCK(vm_obj); vm_object_pip_wakeup(vm_obj); @@ -2087,9 +2099,7 @@ i915_gem_object_bind_to_gtt(struct drm_i915_gem_object *obj, obj->gtt_space = NULL; /* * i915_gem_object_get_pages_gtt() cannot return - * ENOMEM, since we use vm_page_grab(VM_ALLOC_RETRY) - * (which does not support operation without a flag - * anyway). + * ENOMEM, since we use vm_page_grab(). */ return (ret); } @@ -2330,7 +2340,7 @@ retry: m = vm_page_lookup(devobj, i); if (m == NULL) continue; - if (vm_page_sleep_if_busy(m, true, "915unm")) + if (vm_page_sleep_if_busy(m, "915unm")) goto retry; cdev_pager_free_page(devobj, m); } @@ -2504,10 +2514,8 @@ i915_gem_wire_page(vm_object_t object, vm_pindex_t pindex) int rv; VM_OBJECT_ASSERT_WLOCKED(object); - m = vm_page_grab(object, pindex, VM_ALLOC_NORMAL | VM_ALLOC_NOBUSY | - VM_ALLOC_RETRY); + m = vm_page_grab(object, pindex, VM_ALLOC_NORMAL); if (m->valid != VM_PAGE_BITS_ALL) { - vm_page_busy(m); if (vm_pager_has_page(object, pindex, NULL, NULL)) { rv = vm_pager_get_pages(object, &m, 1, 0); m = vm_page_lookup(object, pindex); @@ -2524,11 +2532,11 @@ i915_gem_wire_page(vm_object_t object, vm_pindex_t pindex) m->valid = VM_PAGE_BITS_ALL; m->dirty = 0; } - vm_page_wakeup(m); } vm_page_lock(m); vm_page_wire(m); vm_page_unlock(m); + vm_page_xunbusy(m); atomic_add_long(&i915_gem_wired_pages_cnt, 1); return (m); } diff --git a/sys/dev/drm2/i915/i915_gem_execbuffer.c b/sys/dev/drm2/i915/i915_gem_execbuffer.c index dbd5c4e09f6..21e331f1b9f 100644 --- a/sys/dev/drm2/i915/i915_gem_execbuffer.c +++ b/sys/dev/drm2/i915/i915_gem_execbuffer.c @@ -192,7 +192,7 @@ i915_gem_object_set_to_gpu_domain(struct drm_i915_gem_object *obj, i915_gem_clflush_object(obj); if (obj->base.pending_write_domain) - cd->flips |= atomic_read(&obj->pending_flip); + cd->flips |= atomic_load_acq_int(&obj->pending_flip); /* The actual obj->write_domain will be updated with * pending_write_domain after we emit the accumulated flush for all diff --git a/sys/dev/drm2/i915/intel_crt.c b/sys/dev/drm2/i915/intel_crt.c index 9072553285a..5d96d2f22cb 100644 --- a/sys/dev/drm2/i915/intel_crt.c +++ b/sys/dev/drm2/i915/intel_crt.c @@ -111,7 +111,7 @@ static int intel_crt_mode_valid(struct drm_connector *connector, } static bool intel_crt_mode_fixup(struct drm_encoder *encoder, - struct drm_display_mode *mode, + const struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode) { return true; diff --git a/sys/dev/drm2/i915/intel_display.c b/sys/dev/drm2/i915/intel_display.c index 21f8f535566..ccff1e42f2b 100644 --- a/sys/dev/drm2/i915/intel_display.c +++ b/sys/dev/drm2/i915/intel_display.c @@ -2261,8 +2261,8 @@ intel_finish_fb(struct drm_framebuffer *old_fb) int ret; mtx_lock(&dev->event_lock); - while (!atomic_read(&dev_priv->mm.wedged) && - atomic_read(&obj->pending_flip) != 0) { + while (!atomic_load_acq_int(&dev_priv->mm.wedged) && + atomic_load_acq_int(&obj->pending_flip) != 0) { msleep(&obj->pending_flip, &dev->event_lock, 0, "915flp", 0); } @@ -2948,7 +2948,7 @@ static void intel_crtc_wait_for_pending_flips(struct drm_crtc *crtc) dev = crtc->dev; dev_priv = dev->dev_private; mtx_lock(&dev->event_lock); - while (atomic_read(&obj->pending_flip) != 0) + while (atomic_load_acq_int(&obj->pending_flip) != 0) msleep(&obj->pending_flip, &dev->event_lock, 0, "915wfl", 0); mtx_unlock(&dev->event_lock); } @@ -3512,7 +3512,7 @@ void intel_encoder_destroy(struct drm_encoder *encoder) } static bool intel_crtc_mode_fixup(struct drm_crtc *crtc, - struct drm_display_mode *mode, + const struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode) { struct drm_device *dev = crtc->dev; @@ -7333,7 +7333,7 @@ static void do_intel_finish_page_flip(struct drm_device *dev, obj = work->old_fb_obj; atomic_clear_int(&obj->pending_flip, 1 << intel_crtc->plane); - if (atomic_read(&obj->pending_flip) == 0) + if (atomic_load_acq_int(&obj->pending_flip) == 0) wakeup(&obj->pending_flip); mtx_unlock(&dev->event_lock); @@ -7640,7 +7640,7 @@ static int intel_crtc_page_flip(struct drm_crtc *crtc, return 0; cleanup_pending: - atomic_sub(1 << intel_crtc->plane, &work->old_fb_obj->pending_flip); + atomic_clear_int(&work->old_fb_obj->pending_flip, 1 << intel_crtc->plane); drm_gem_object_unreference(&work->old_fb_obj->base); drm_gem_object_unreference(&obj->base); DRM_UNLOCK(dev); diff --git a/sys/dev/drm2/i915/intel_dp.c b/sys/dev/drm2/i915/intel_dp.c index 4820e4548f3..e0df0bc2749 100644 --- a/sys/dev/drm2/i915/intel_dp.c +++ b/sys/dev/drm2/i915/intel_dp.c @@ -43,9 +43,6 @@ __FBSDID("$FreeBSD$"); #define DP_LINK_CONFIGURATION_SIZE 9 -/* XXXKIB what is the right code for the FreeBSD ? */ -#define EREMOTEIO ENXIO - struct intel_dp { struct intel_encoder base; uint32_t output_reg; @@ -224,7 +221,7 @@ intel_dp_max_data_rate(int max_link_clock, int max_lanes) static bool intel_dp_adjust_dithering(struct intel_dp *intel_dp, - struct drm_display_mode *mode, + const struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode) { int max_link_clock = intel_dp_link_clock(intel_dp_max_link_bw(intel_dp)); @@ -676,7 +673,7 @@ intel_dp_i2c_init(struct intel_dp *intel_dp, } static bool -intel_dp_mode_fixup(struct drm_encoder *encoder, struct drm_display_mode *mode, +intel_dp_mode_fixup(struct drm_encoder *encoder, const struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode) { struct drm_device *dev = encoder->dev; @@ -691,14 +688,9 @@ intel_dp_mode_fixup(struct drm_encoder *encoder, struct drm_display_mode *mode, intel_fixed_panel_mode(intel_dp->panel_fixed_mode, adjusted_mode); intel_pch_panel_fitting(dev, DRM_MODE_SCALE_FULLSCREEN, mode, adjusted_mode); - /* - * the mode->clock is used to calculate the Data&Link M/N - * of the pipe. For the eDP the fixed clock should be used. - */ - mode->clock = intel_dp->panel_fixed_mode->clock; } - if (!intel_dp_adjust_dithering(intel_dp, mode, adjusted_mode)) + if (!intel_dp_adjust_dithering(intel_dp, adjusted_mode, adjusted_mode)) return false; bpp = adjusted_mode->private_flags & INTEL_MODE_DP_FORCE_6BPC ? 18 : 24; @@ -707,7 +699,7 @@ intel_dp_mode_fixup(struct drm_encoder *encoder, struct drm_display_mode *mode, for (clock = 0; clock <= max_clock; clock++) { int link_avail = intel_dp_max_data_rate(intel_dp_link_clock(bws[clock]), lane_count); - if (intel_dp_link_required(mode->clock, bpp) + if (intel_dp_link_required(adjusted_mode->clock, bpp) <= link_avail) { intel_dp->link_bw = bws[clock]; intel_dp->lane_count = lane_count; diff --git a/sys/dev/drm2/i915/intel_drv.h b/sys/dev/drm2/i915/intel_drv.h index 803966278c7..bc2c55c6b91 100644 --- a/sys/dev/drm2/i915/intel_drv.h +++ b/sys/dev/drm2/i915/intel_drv.h @@ -320,7 +320,7 @@ extern void intel_fixed_panel_mode(struct drm_display_mode *fixed_mode, struct drm_display_mode *adjusted_mode); extern void intel_pch_panel_fitting(struct drm_device *dev, int fitting_mode, - struct drm_display_mode *mode, + const struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode); extern u32 intel_panel_get_max_backlight(struct drm_device *dev); extern u32 intel_panel_get_backlight(struct drm_device *dev); diff --git a/sys/dev/drm2/i915/intel_hdmi.c b/sys/dev/drm2/i915/intel_hdmi.c index ccb9dced597..f076608d30d 100644 --- a/sys/dev/drm2/i915/intel_hdmi.c +++ b/sys/dev/drm2/i915/intel_hdmi.c @@ -318,7 +318,7 @@ static int intel_hdmi_mode_valid(struct drm_connector *connector, } static bool intel_hdmi_mode_fixup(struct drm_encoder *encoder, - struct drm_display_mode *mode, + const struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode) { return true; diff --git a/sys/dev/drm2/i915/intel_lvds.c b/sys/dev/drm2/i915/intel_lvds.c index 4e29b91d6a0..cbf3ea4f7c2 100644 --- a/sys/dev/drm2/i915/intel_lvds.c +++ b/sys/dev/drm2/i915/intel_lvds.c @@ -230,7 +230,7 @@ static inline u32 panel_fitter_scaling(u32 source, u32 target) } static bool intel_lvds_mode_fixup(struct drm_encoder *encoder, - struct drm_display_mode *mode, + const struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode) { struct drm_device *dev = encoder->dev; diff --git a/sys/dev/drm2/i915/intel_panel.c b/sys/dev/drm2/i915/intel_panel.c index 815848b2208..504e2ac4de4 100644 --- a/sys/dev/drm2/i915/intel_panel.c +++ b/sys/dev/drm2/i915/intel_panel.c @@ -59,7 +59,7 @@ intel_fixed_panel_mode(struct drm_display_mode *fixed_mode, void intel_pch_panel_fitting(struct drm_device *dev, int fitting_mode, - struct drm_display_mode *mode, + const struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode) { struct drm_i915_private *dev_priv = dev->dev_private; diff --git a/sys/dev/drm2/i915/intel_ringbuffer.c b/sys/dev/drm2/i915/intel_ringbuffer.c index 107a211e285..7d6bd947b90 100644 --- a/sys/dev/drm2/i915/intel_ringbuffer.c +++ b/sys/dev/drm2/i915/intel_ringbuffer.c @@ -361,7 +361,7 @@ init_pipe_control(struct intel_ring_buffer *ring) goto err_unref; pc->gtt_offset = obj->gtt_offset; - pc->cpu_page = (uint32_t *)kmem_alloc_nofault(kernel_map, PAGE_SIZE); + pc->cpu_page = (uint32_t *)kva_alloc(PAGE_SIZE); if (pc->cpu_page == NULL) goto err_unpin; pmap_qenter((uintptr_t)pc->cpu_page, &obj->pages[0], 1); @@ -392,7 +392,7 @@ cleanup_pipe_control(struct intel_ring_buffer *ring) obj = pc->obj; pmap_qremove((vm_offset_t)pc->cpu_page, 1); - kmem_free(kernel_map, (uintptr_t)pc->cpu_page, PAGE_SIZE); + kva_free((uintptr_t)pc->cpu_page, PAGE_SIZE); i915_gem_object_unpin(obj); drm_gem_object_unreference(&obj->base); @@ -968,7 +968,7 @@ static void cleanup_status_page(struct intel_ring_buffer *ring) return; pmap_qremove((vm_offset_t)ring->status_page.page_addr, 1); - kmem_free(kernel_map, (vm_offset_t)ring->status_page.page_addr, + kva_free((vm_offset_t)ring->status_page.page_addr, PAGE_SIZE); i915_gem_object_unpin(obj); drm_gem_object_unreference(&obj->base); @@ -999,8 +999,7 @@ static int init_status_page(struct intel_ring_buffer *ring) } ring->status_page.gfx_addr = obj->gtt_offset; - ring->status_page.page_addr = (void *)kmem_alloc_nofault(kernel_map, - PAGE_SIZE); + ring->status_page.page_addr = (void *)kva_alloc(PAGE_SIZE); if (ring->status_page.page_addr == NULL) { memset(&dev_priv->hws_map, 0, sizeof(dev_priv->hws_map)); goto err_unpin; diff --git a/sys/dev/drm2/i915/intel_sdvo.c b/sys/dev/drm2/i915/intel_sdvo.c index 601990b2679..8cae7662d5d 100644 --- a/sys/dev/drm2/i915/intel_sdvo.c +++ b/sys/dev/drm2/i915/intel_sdvo.c @@ -924,7 +924,7 @@ static bool intel_sdvo_set_tv_format(struct intel_sdvo *intel_sdvo) static bool intel_sdvo_set_output_timings_from_mode(struct intel_sdvo *intel_sdvo, - struct drm_display_mode *mode) + const struct drm_display_mode *mode) { struct intel_sdvo_dtd output_dtd; @@ -941,7 +941,7 @@ intel_sdvo_set_output_timings_from_mode(struct intel_sdvo *intel_sdvo, static bool intel_sdvo_set_input_timings_for_mode(struct intel_sdvo *intel_sdvo, - struct drm_display_mode *mode, + const struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode) { /* Reset the input timing to the screen. Assume always input 0. */ @@ -964,7 +964,7 @@ intel_sdvo_set_input_timings_for_mode(struct intel_sdvo *intel_sdvo, } static bool intel_sdvo_mode_fixup(struct drm_encoder *encoder, - struct drm_display_mode *mode, + const struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode) { struct intel_sdvo *intel_sdvo = to_intel_sdvo(encoder); diff --git a/sys/dev/drm2/i915/intel_tv.c b/sys/dev/drm2/i915/intel_tv.c index 93fcf0a3eec..57a2181f1aa 100644 --- a/sys/dev/drm2/i915/intel_tv.c +++ b/sys/dev/drm2/i915/intel_tv.c @@ -846,7 +846,7 @@ intel_tv_mode_valid(struct drm_connector *connector, static bool -intel_tv_mode_fixup(struct drm_encoder *encoder, struct drm_display_mode *mode, +intel_tv_mode_fixup(struct drm_encoder *encoder, const struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode) { struct drm_device *dev = encoder->dev; diff --git a/sys/dev/drm2/radeon/ObjectID.h b/sys/dev/drm2/radeon/ObjectID.h new file mode 100644 index 00000000000..db559da6a2d --- /dev/null +++ b/sys/dev/drm2/radeon/ObjectID.h @@ -0,0 +1,699 @@ +/* +* Copyright 2006-2007 Advanced Micro Devices, Inc. +* +* 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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. +*/ +/* based on stg/asic_reg/drivers/inc/asic_reg/ObjectID.h ver 23 */ + +#ifndef _OBJECTID_H +#define _OBJECTID_H + +#include +__FBSDID("$FreeBSD$"); + +#if defined(_X86_) +#pragma pack(1) +#endif + +/****************************************************/ +/* Graphics Object Type Definition */ +/****************************************************/ +#define GRAPH_OBJECT_TYPE_NONE 0x0 +#define GRAPH_OBJECT_TYPE_GPU 0x1 +#define GRAPH_OBJECT_TYPE_ENCODER 0x2 +#define GRAPH_OBJECT_TYPE_CONNECTOR 0x3 +#define GRAPH_OBJECT_TYPE_ROUTER 0x4 +/* deleted */ +#define GRAPH_OBJECT_TYPE_DISPLAY_PATH 0x6 +#define GRAPH_OBJECT_TYPE_GENERIC 0x7 + +/****************************************************/ +/* Encoder Object ID Definition */ +/****************************************************/ +#define ENCODER_OBJECT_ID_NONE 0x00 + +/* Radeon Class Display Hardware */ +#define ENCODER_OBJECT_ID_INTERNAL_LVDS 0x01 +#define ENCODER_OBJECT_ID_INTERNAL_TMDS1 0x02 +#define ENCODER_OBJECT_ID_INTERNAL_TMDS2 0x03 +#define ENCODER_OBJECT_ID_INTERNAL_DAC1 0x04 +#define ENCODER_OBJECT_ID_INTERNAL_DAC2 0x05 /* TV/CV DAC */ +#define ENCODER_OBJECT_ID_INTERNAL_SDVOA 0x06 +#define ENCODER_OBJECT_ID_INTERNAL_SDVOB 0x07 + +/* External Third Party Encoders */ +#define ENCODER_OBJECT_ID_SI170B 0x08 +#define ENCODER_OBJECT_ID_CH7303 0x09 +#define ENCODER_OBJECT_ID_CH7301 0x0A +#define ENCODER_OBJECT_ID_INTERNAL_DVO1 0x0B /* This belongs to Radeon Class Display Hardware */ +#define ENCODER_OBJECT_ID_EXTERNAL_SDVOA 0x0C +#define ENCODER_OBJECT_ID_EXTERNAL_SDVOB 0x0D +#define ENCODER_OBJECT_ID_TITFP513 0x0E +#define ENCODER_OBJECT_ID_INTERNAL_LVTM1 0x0F /* not used for Radeon */ +#define ENCODER_OBJECT_ID_VT1623 0x10 +#define ENCODER_OBJECT_ID_HDMI_SI1930 0x11 +#define ENCODER_OBJECT_ID_HDMI_INTERNAL 0x12 +#define ENCODER_OBJECT_ID_ALMOND 0x22 +#define ENCODER_OBJECT_ID_TRAVIS 0x23 +#define ENCODER_OBJECT_ID_NUTMEG 0x22 +/* Kaleidoscope (KLDSCP) Class Display Hardware (internal) */ +#define ENCODER_OBJECT_ID_INTERNAL_KLDSCP_TMDS1 0x13 +#define ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DVO1 0x14 +#define ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC1 0x15 +#define ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC2 0x16 /* Shared with CV/TV and CRT */ +#define ENCODER_OBJECT_ID_SI178 0X17 /* External TMDS (dual link, no HDCP.) */ +#define ENCODER_OBJECT_ID_MVPU_FPGA 0x18 /* MVPU FPGA chip */ +#define ENCODER_OBJECT_ID_INTERNAL_DDI 0x19 +#define ENCODER_OBJECT_ID_VT1625 0x1A +#define ENCODER_OBJECT_ID_HDMI_SI1932 0x1B +#define ENCODER_OBJECT_ID_DP_AN9801 0x1C +#define ENCODER_OBJECT_ID_DP_DP501 0x1D +#define ENCODER_OBJECT_ID_INTERNAL_UNIPHY 0x1E +#define ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA 0x1F +#define ENCODER_OBJECT_ID_INTERNAL_UNIPHY1 0x20 +#define ENCODER_OBJECT_ID_INTERNAL_UNIPHY2 0x21 +#define ENCODER_OBJECT_ID_INTERNAL_VCE 0x24 + +#define ENCODER_OBJECT_ID_GENERAL_EXTERNAL_DVO 0xFF + +/****************************************************/ +/* Connector Object ID Definition */ +/****************************************************/ +#define CONNECTOR_OBJECT_ID_NONE 0x00 +#define CONNECTOR_OBJECT_ID_SINGLE_LINK_DVI_I 0x01 +#define CONNECTOR_OBJECT_ID_DUAL_LINK_DVI_I 0x02 +#define CONNECTOR_OBJECT_ID_SINGLE_LINK_DVI_D 0x03 +#define CONNECTOR_OBJECT_ID_DUAL_LINK_DVI_D 0x04 +#define CONNECTOR_OBJECT_ID_VGA 0x05 +#define CONNECTOR_OBJECT_ID_COMPOSITE 0x06 +#define CONNECTOR_OBJECT_ID_SVIDEO 0x07 +#define CONNECTOR_OBJECT_ID_YPbPr 0x08 +#define CONNECTOR_OBJECT_ID_D_CONNECTOR 0x09 +#define CONNECTOR_OBJECT_ID_9PIN_DIN 0x0A /* Supports both CV & TV */ +#define CONNECTOR_OBJECT_ID_SCART 0x0B +#define CONNECTOR_OBJECT_ID_HDMI_TYPE_A 0x0C +#define CONNECTOR_OBJECT_ID_HDMI_TYPE_B 0x0D +#define CONNECTOR_OBJECT_ID_LVDS 0x0E +#define CONNECTOR_OBJECT_ID_7PIN_DIN 0x0F +#define CONNECTOR_OBJECT_ID_PCIE_CONNECTOR 0x10 +#define CONNECTOR_OBJECT_ID_CROSSFIRE 0x11 +#define CONNECTOR_OBJECT_ID_HARDCODE_DVI 0x12 +#define CONNECTOR_OBJECT_ID_DISPLAYPORT 0x13 +#define CONNECTOR_OBJECT_ID_eDP 0x14 +#define CONNECTOR_OBJECT_ID_MXM 0x15 +#define CONNECTOR_OBJECT_ID_LVDS_eDP 0x16 + +/* deleted */ + +/****************************************************/ +/* Router Object ID Definition */ +/****************************************************/ +#define ROUTER_OBJECT_ID_NONE 0x00 +#define ROUTER_OBJECT_ID_I2C_EXTENDER_CNTL 0x01 + +/****************************************************/ +/* Generic Object ID Definition */ +/****************************************************/ +#define GENERIC_OBJECT_ID_NONE 0x00 +#define GENERIC_OBJECT_ID_GLSYNC 0x01 +#define GENERIC_OBJECT_ID_PX2_NON_DRIVABLE 0x02 +#define GENERIC_OBJECT_ID_MXM_OPM 0x03 +#define GENERIC_OBJECT_ID_STEREO_PIN 0x04 //This object could show up from Misc Object table, it follows ATOM_OBJECT format, and contains one ATOM_OBJECT_GPIO_CNTL_RECORD for the stereo pin + +/****************************************************/ +/* Graphics Object ENUM ID Definition */ +/****************************************************/ +#define GRAPH_OBJECT_ENUM_ID1 0x01 +#define GRAPH_OBJECT_ENUM_ID2 0x02 +#define GRAPH_OBJECT_ENUM_ID3 0x03 +#define GRAPH_OBJECT_ENUM_ID4 0x04 +#define GRAPH_OBJECT_ENUM_ID5 0x05 +#define GRAPH_OBJECT_ENUM_ID6 0x06 +#define GRAPH_OBJECT_ENUM_ID7 0x07 + +/****************************************************/ +/* Graphics Object ID Bit definition */ +/****************************************************/ +#define OBJECT_ID_MASK 0x00FF +#define ENUM_ID_MASK 0x0700 +#define RESERVED1_ID_MASK 0x0800 +#define OBJECT_TYPE_MASK 0x7000 +#define RESERVED2_ID_MASK 0x8000 + +#define OBJECT_ID_SHIFT 0x00 +#define ENUM_ID_SHIFT 0x08 +#define OBJECT_TYPE_SHIFT 0x0C + + +/****************************************************/ +/* Graphics Object family definition */ +/****************************************************/ +#define CONSTRUCTOBJECTFAMILYID(GRAPHICS_OBJECT_TYPE, GRAPHICS_OBJECT_ID) (GRAPHICS_OBJECT_TYPE << OBJECT_TYPE_SHIFT | \ + GRAPHICS_OBJECT_ID << OBJECT_ID_SHIFT) +/****************************************************/ +/* GPU Object ID definition - Shared with BIOS */ +/****************************************************/ +#define GPU_ENUM_ID1 ( GRAPH_OBJECT_TYPE_GPU << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT) + +/****************************************************/ +/* Encoder Object ID definition - Shared with BIOS */ +/****************************************************/ +/* +#define ENCODER_INTERNAL_LVDS_ENUM_ID1 0x2101 +#define ENCODER_INTERNAL_TMDS1_ENUM_ID1 0x2102 +#define ENCODER_INTERNAL_TMDS2_ENUM_ID1 0x2103 +#define ENCODER_INTERNAL_DAC1_ENUM_ID1 0x2104 +#define ENCODER_INTERNAL_DAC2_ENUM_ID1 0x2105 +#define ENCODER_INTERNAL_SDVOA_ENUM_ID1 0x2106 +#define ENCODER_INTERNAL_SDVOB_ENUM_ID1 0x2107 +#define ENCODER_SIL170B_ENUM_ID1 0x2108 +#define ENCODER_CH7303_ENUM_ID1 0x2109 +#define ENCODER_CH7301_ENUM_ID1 0x210A +#define ENCODER_INTERNAL_DVO1_ENUM_ID1 0x210B +#define ENCODER_EXTERNAL_SDVOA_ENUM_ID1 0x210C +#define ENCODER_EXTERNAL_SDVOB_ENUM_ID1 0x210D +#define ENCODER_TITFP513_ENUM_ID1 0x210E +#define ENCODER_INTERNAL_LVTM1_ENUM_ID1 0x210F +#define ENCODER_VT1623_ENUM_ID1 0x2110 +#define ENCODER_HDMI_SI1930_ENUM_ID1 0x2111 +#define ENCODER_HDMI_INTERNAL_ENUM_ID1 0x2112 +#define ENCODER_INTERNAL_KLDSCP_TMDS1_ENUM_ID1 0x2113 +#define ENCODER_INTERNAL_KLDSCP_DVO1_ENUM_ID1 0x2114 +#define ENCODER_INTERNAL_KLDSCP_DAC1_ENUM_ID1 0x2115 +#define ENCODER_INTERNAL_KLDSCP_DAC2_ENUM_ID1 0x2116 +#define ENCODER_SI178_ENUM_ID1 0x2117 +#define ENCODER_MVPU_FPGA_ENUM_ID1 0x2118 +#define ENCODER_INTERNAL_DDI_ENUM_ID1 0x2119 +#define ENCODER_VT1625_ENUM_ID1 0x211A +#define ENCODER_HDMI_SI1932_ENUM_ID1 0x211B +#define ENCODER_ENCODER_DP_AN9801_ENUM_ID1 0x211C +#define ENCODER_DP_DP501_ENUM_ID1 0x211D +#define ENCODER_INTERNAL_UNIPHY_ENUM_ID1 0x211E +*/ +#define ENCODER_INTERNAL_LVDS_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ + ENCODER_OBJECT_ID_INTERNAL_LVDS << OBJECT_ID_SHIFT) + +#define ENCODER_INTERNAL_TMDS1_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ + ENCODER_OBJECT_ID_INTERNAL_TMDS1 << OBJECT_ID_SHIFT) + +#define ENCODER_INTERNAL_TMDS2_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ + ENCODER_OBJECT_ID_INTERNAL_TMDS2 << OBJECT_ID_SHIFT) + +#define ENCODER_INTERNAL_DAC1_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ + ENCODER_OBJECT_ID_INTERNAL_DAC1 << OBJECT_ID_SHIFT) + +#define ENCODER_INTERNAL_DAC2_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ + ENCODER_OBJECT_ID_INTERNAL_DAC2 << OBJECT_ID_SHIFT) + +#define ENCODER_INTERNAL_SDVOA_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ + ENCODER_OBJECT_ID_INTERNAL_SDVOA << OBJECT_ID_SHIFT) + +#define ENCODER_INTERNAL_SDVOA_ENUM_ID2 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\ + ENCODER_OBJECT_ID_INTERNAL_SDVOA << OBJECT_ID_SHIFT) + +#define ENCODER_INTERNAL_SDVOB_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ + ENCODER_OBJECT_ID_INTERNAL_SDVOB << OBJECT_ID_SHIFT) + +#define ENCODER_SIL170B_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ + ENCODER_OBJECT_ID_SI170B << OBJECT_ID_SHIFT) + +#define ENCODER_CH7303_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ + ENCODER_OBJECT_ID_CH7303 << OBJECT_ID_SHIFT) + +#define ENCODER_CH7301_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ + ENCODER_OBJECT_ID_CH7301 << OBJECT_ID_SHIFT) + +#define ENCODER_INTERNAL_DVO1_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ + ENCODER_OBJECT_ID_INTERNAL_DVO1 << OBJECT_ID_SHIFT) + +#define ENCODER_EXTERNAL_SDVOA_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ + ENCODER_OBJECT_ID_EXTERNAL_SDVOA << OBJECT_ID_SHIFT) + +#define ENCODER_EXTERNAL_SDVOA_ENUM_ID2 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\ + ENCODER_OBJECT_ID_EXTERNAL_SDVOA << OBJECT_ID_SHIFT) + + +#define ENCODER_EXTERNAL_SDVOB_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ + ENCODER_OBJECT_ID_EXTERNAL_SDVOB << OBJECT_ID_SHIFT) + + +#define ENCODER_TITFP513_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ + ENCODER_OBJECT_ID_TITFP513 << OBJECT_ID_SHIFT) + +#define ENCODER_INTERNAL_LVTM1_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ + ENCODER_OBJECT_ID_INTERNAL_LVTM1 << OBJECT_ID_SHIFT) + +#define ENCODER_VT1623_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ + ENCODER_OBJECT_ID_VT1623 << OBJECT_ID_SHIFT) + +#define ENCODER_HDMI_SI1930_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ + ENCODER_OBJECT_ID_HDMI_SI1930 << OBJECT_ID_SHIFT) + +#define ENCODER_HDMI_INTERNAL_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ + ENCODER_OBJECT_ID_HDMI_INTERNAL << OBJECT_ID_SHIFT) + +#define ENCODER_INTERNAL_KLDSCP_TMDS1_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ + ENCODER_OBJECT_ID_INTERNAL_KLDSCP_TMDS1 << OBJECT_ID_SHIFT) + + +#define ENCODER_INTERNAL_KLDSCP_TMDS1_ENUM_ID2 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\ + ENCODER_OBJECT_ID_INTERNAL_KLDSCP_TMDS1 << OBJECT_ID_SHIFT) + + +#define ENCODER_INTERNAL_KLDSCP_DVO1_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ + ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DVO1 << OBJECT_ID_SHIFT) + +#define ENCODER_INTERNAL_KLDSCP_DAC1_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ + ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC1 << OBJECT_ID_SHIFT) + +#define ENCODER_INTERNAL_KLDSCP_DAC2_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ + ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC2 << OBJECT_ID_SHIFT) // Shared with CV/TV and CRT + +#define ENCODER_SI178_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ + ENCODER_OBJECT_ID_SI178 << OBJECT_ID_SHIFT) + +#define ENCODER_MVPU_FPGA_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ + ENCODER_OBJECT_ID_MVPU_FPGA << OBJECT_ID_SHIFT) + +#define ENCODER_INTERNAL_DDI_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ + ENCODER_OBJECT_ID_INTERNAL_DDI << OBJECT_ID_SHIFT) + +#define ENCODER_VT1625_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ + ENCODER_OBJECT_ID_VT1625 << OBJECT_ID_SHIFT) + +#define ENCODER_HDMI_SI1932_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ + ENCODER_OBJECT_ID_HDMI_SI1932 << OBJECT_ID_SHIFT) + +#define ENCODER_DP_DP501_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ + ENCODER_OBJECT_ID_DP_DP501 << OBJECT_ID_SHIFT) + +#define ENCODER_DP_AN9801_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ + ENCODER_OBJECT_ID_DP_AN9801 << OBJECT_ID_SHIFT) + +#define ENCODER_INTERNAL_UNIPHY_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ + ENCODER_OBJECT_ID_INTERNAL_UNIPHY << OBJECT_ID_SHIFT) + +#define ENCODER_INTERNAL_UNIPHY_ENUM_ID2 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\ + ENCODER_OBJECT_ID_INTERNAL_UNIPHY << OBJECT_ID_SHIFT) + +#define ENCODER_INTERNAL_KLDSCP_LVTMA_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ + ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA << OBJECT_ID_SHIFT) + +#define ENCODER_INTERNAL_UNIPHY1_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ + ENCODER_OBJECT_ID_INTERNAL_UNIPHY1 << OBJECT_ID_SHIFT) + +#define ENCODER_INTERNAL_UNIPHY1_ENUM_ID2 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\ + ENCODER_OBJECT_ID_INTERNAL_UNIPHY1 << OBJECT_ID_SHIFT) + +#define ENCODER_INTERNAL_UNIPHY2_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ + ENCODER_OBJECT_ID_INTERNAL_UNIPHY2 << OBJECT_ID_SHIFT) + +#define ENCODER_INTERNAL_UNIPHY2_ENUM_ID2 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\ + ENCODER_OBJECT_ID_INTERNAL_UNIPHY2 << OBJECT_ID_SHIFT) + +#define ENCODER_GENERAL_EXTERNAL_DVO_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ + ENCODER_OBJECT_ID_GENERAL_EXTERNAL_DVO << OBJECT_ID_SHIFT) + +#define ENCODER_ALMOND_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ + ENCODER_OBJECT_ID_ALMOND << OBJECT_ID_SHIFT) + +#define ENCODER_ALMOND_ENUM_ID2 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\ + ENCODER_OBJECT_ID_ALMOND << OBJECT_ID_SHIFT) + +#define ENCODER_TRAVIS_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ + ENCODER_OBJECT_ID_TRAVIS << OBJECT_ID_SHIFT) + +#define ENCODER_TRAVIS_ENUM_ID2 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\ + ENCODER_OBJECT_ID_TRAVIS << OBJECT_ID_SHIFT) + +#define ENCODER_NUTMEG_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ + ENCODER_OBJECT_ID_NUTMEG << OBJECT_ID_SHIFT) + +#define ENCODER_VCE_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ + ENCODER_OBJECT_ID_INTERNAL_VCE << OBJECT_ID_SHIFT) + +/****************************************************/ +/* Connector Object ID definition - Shared with BIOS */ +/****************************************************/ +/* +#define CONNECTOR_SINGLE_LINK_DVI_I_ENUM_ID1 0x3101 +#define CONNECTOR_DUAL_LINK_DVI_I_ENUM_ID1 0x3102 +#define CONNECTOR_SINGLE_LINK_DVI_D_ENUM_ID1 0x3103 +#define CONNECTOR_DUAL_LINK_DVI_D_ENUM_ID1 0x3104 +#define CONNECTOR_VGA_ENUM_ID1 0x3105 +#define CONNECTOR_COMPOSITE_ENUM_ID1 0x3106 +#define CONNECTOR_SVIDEO_ENUM_ID1 0x3107 +#define CONNECTOR_YPbPr_ENUM_ID1 0x3108 +#define CONNECTOR_D_CONNECTORE_ENUM_ID1 0x3109 +#define CONNECTOR_9PIN_DIN_ENUM_ID1 0x310A +#define CONNECTOR_SCART_ENUM_ID1 0x310B +#define CONNECTOR_HDMI_TYPE_A_ENUM_ID1 0x310C +#define CONNECTOR_HDMI_TYPE_B_ENUM_ID1 0x310D +#define CONNECTOR_LVDS_ENUM_ID1 0x310E +#define CONNECTOR_7PIN_DIN_ENUM_ID1 0x310F +#define CONNECTOR_PCIE_CONNECTOR_ENUM_ID1 0x3110 +*/ +#define CONNECTOR_LVDS_ENUM_ID1 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ + CONNECTOR_OBJECT_ID_LVDS << OBJECT_ID_SHIFT) + +#define CONNECTOR_LVDS_ENUM_ID2 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\ + CONNECTOR_OBJECT_ID_LVDS << OBJECT_ID_SHIFT) + +#define CONNECTOR_eDP_ENUM_ID1 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ + CONNECTOR_OBJECT_ID_eDP << OBJECT_ID_SHIFT) + +#define CONNECTOR_eDP_ENUM_ID2 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\ + CONNECTOR_OBJECT_ID_eDP << OBJECT_ID_SHIFT) + +#define CONNECTOR_SINGLE_LINK_DVI_I_ENUM_ID1 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ + CONNECTOR_OBJECT_ID_SINGLE_LINK_DVI_I << OBJECT_ID_SHIFT) + +#define CONNECTOR_SINGLE_LINK_DVI_I_ENUM_ID2 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\ + CONNECTOR_OBJECT_ID_SINGLE_LINK_DVI_I << OBJECT_ID_SHIFT) + +#define CONNECTOR_DUAL_LINK_DVI_I_ENUM_ID1 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ + CONNECTOR_OBJECT_ID_DUAL_LINK_DVI_I << OBJECT_ID_SHIFT) + +#define CONNECTOR_DUAL_LINK_DVI_I_ENUM_ID2 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\ + CONNECTOR_OBJECT_ID_DUAL_LINK_DVI_I << OBJECT_ID_SHIFT) + +#define CONNECTOR_SINGLE_LINK_DVI_D_ENUM_ID1 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ + CONNECTOR_OBJECT_ID_SINGLE_LINK_DVI_D << OBJECT_ID_SHIFT) + +#define CONNECTOR_SINGLE_LINK_DVI_D_ENUM_ID2 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\ + CONNECTOR_OBJECT_ID_SINGLE_LINK_DVI_D << OBJECT_ID_SHIFT) + +#define CONNECTOR_SINGLE_LINK_DVI_D_ENUM_ID3 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID3 << ENUM_ID_SHIFT |\ + CONNECTOR_OBJECT_ID_SINGLE_LINK_DVI_D << OBJECT_ID_SHIFT) + +#define CONNECTOR_SINGLE_LINK_DVI_D_ENUM_ID4 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID4 << ENUM_ID_SHIFT |\ + CONNECTOR_OBJECT_ID_SINGLE_LINK_DVI_D << OBJECT_ID_SHIFT) + +#define CONNECTOR_DUAL_LINK_DVI_D_ENUM_ID1 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ + CONNECTOR_OBJECT_ID_DUAL_LINK_DVI_D << OBJECT_ID_SHIFT) + +#define CONNECTOR_DUAL_LINK_DVI_D_ENUM_ID2 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\ + CONNECTOR_OBJECT_ID_DUAL_LINK_DVI_D << OBJECT_ID_SHIFT) + +#define CONNECTOR_DUAL_LINK_DVI_D_ENUM_ID3 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID3 << ENUM_ID_SHIFT |\ + CONNECTOR_OBJECT_ID_DUAL_LINK_DVI_D << OBJECT_ID_SHIFT) + +#define CONNECTOR_VGA_ENUM_ID1 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ + CONNECTOR_OBJECT_ID_VGA << OBJECT_ID_SHIFT) + +#define CONNECTOR_VGA_ENUM_ID2 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\ + CONNECTOR_OBJECT_ID_VGA << OBJECT_ID_SHIFT) + +#define CONNECTOR_COMPOSITE_ENUM_ID1 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ + CONNECTOR_OBJECT_ID_COMPOSITE << OBJECT_ID_SHIFT) + +#define CONNECTOR_COMPOSITE_ENUM_ID2 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\ + CONNECTOR_OBJECT_ID_COMPOSITE << OBJECT_ID_SHIFT) + +#define CONNECTOR_SVIDEO_ENUM_ID1 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ + CONNECTOR_OBJECT_ID_SVIDEO << OBJECT_ID_SHIFT) + +#define CONNECTOR_SVIDEO_ENUM_ID2 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\ + CONNECTOR_OBJECT_ID_SVIDEO << OBJECT_ID_SHIFT) + +#define CONNECTOR_YPbPr_ENUM_ID1 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ + CONNECTOR_OBJECT_ID_YPbPr << OBJECT_ID_SHIFT) + +#define CONNECTOR_YPbPr_ENUM_ID2 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\ + CONNECTOR_OBJECT_ID_YPbPr << OBJECT_ID_SHIFT) + +#define CONNECTOR_D_CONNECTOR_ENUM_ID1 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ + CONNECTOR_OBJECT_ID_D_CONNECTOR << OBJECT_ID_SHIFT) + +#define CONNECTOR_D_CONNECTOR_ENUM_ID2 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\ + CONNECTOR_OBJECT_ID_D_CONNECTOR << OBJECT_ID_SHIFT) + +#define CONNECTOR_9PIN_DIN_ENUM_ID1 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ + CONNECTOR_OBJECT_ID_9PIN_DIN << OBJECT_ID_SHIFT) + +#define CONNECTOR_9PIN_DIN_ENUM_ID2 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\ + CONNECTOR_OBJECT_ID_9PIN_DIN << OBJECT_ID_SHIFT) + +#define CONNECTOR_SCART_ENUM_ID1 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ + CONNECTOR_OBJECT_ID_SCART << OBJECT_ID_SHIFT) + +#define CONNECTOR_SCART_ENUM_ID2 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\ + CONNECTOR_OBJECT_ID_SCART << OBJECT_ID_SHIFT) + +#define CONNECTOR_HDMI_TYPE_A_ENUM_ID1 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ + CONNECTOR_OBJECT_ID_HDMI_TYPE_A << OBJECT_ID_SHIFT) + +#define CONNECTOR_HDMI_TYPE_A_ENUM_ID2 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\ + CONNECTOR_OBJECT_ID_HDMI_TYPE_A << OBJECT_ID_SHIFT) + +#define CONNECTOR_HDMI_TYPE_A_ENUM_ID3 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID3 << ENUM_ID_SHIFT |\ + CONNECTOR_OBJECT_ID_HDMI_TYPE_A << OBJECT_ID_SHIFT) + +#define CONNECTOR_HDMI_TYPE_B_ENUM_ID1 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ + CONNECTOR_OBJECT_ID_HDMI_TYPE_B << OBJECT_ID_SHIFT) + +#define CONNECTOR_HDMI_TYPE_B_ENUM_ID2 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\ + CONNECTOR_OBJECT_ID_HDMI_TYPE_B << OBJECT_ID_SHIFT) + +#define CONNECTOR_7PIN_DIN_ENUM_ID1 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ + CONNECTOR_OBJECT_ID_7PIN_DIN << OBJECT_ID_SHIFT) + +#define CONNECTOR_7PIN_DIN_ENUM_ID2 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\ + CONNECTOR_OBJECT_ID_7PIN_DIN << OBJECT_ID_SHIFT) + +#define CONNECTOR_PCIE_CONNECTOR_ENUM_ID1 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ + CONNECTOR_OBJECT_ID_PCIE_CONNECTOR << OBJECT_ID_SHIFT) + +#define CONNECTOR_PCIE_CONNECTOR_ENUM_ID2 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\ + CONNECTOR_OBJECT_ID_PCIE_CONNECTOR << OBJECT_ID_SHIFT) + +#define CONNECTOR_CROSSFIRE_ENUM_ID1 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ + CONNECTOR_OBJECT_ID_CROSSFIRE << OBJECT_ID_SHIFT) + +#define CONNECTOR_CROSSFIRE_ENUM_ID2 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\ + CONNECTOR_OBJECT_ID_CROSSFIRE << OBJECT_ID_SHIFT) + + +#define CONNECTOR_HARDCODE_DVI_ENUM_ID1 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ + CONNECTOR_OBJECT_ID_HARDCODE_DVI << OBJECT_ID_SHIFT) + +#define CONNECTOR_HARDCODE_DVI_ENUM_ID2 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\ + CONNECTOR_OBJECT_ID_HARDCODE_DVI << OBJECT_ID_SHIFT) + +#define CONNECTOR_DISPLAYPORT_ENUM_ID1 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ + CONNECTOR_OBJECT_ID_DISPLAYPORT << OBJECT_ID_SHIFT) + +#define CONNECTOR_DISPLAYPORT_ENUM_ID2 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\ + CONNECTOR_OBJECT_ID_DISPLAYPORT << OBJECT_ID_SHIFT) + +#define CONNECTOR_DISPLAYPORT_ENUM_ID3 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID3 << ENUM_ID_SHIFT |\ + CONNECTOR_OBJECT_ID_DISPLAYPORT << OBJECT_ID_SHIFT) + +#define CONNECTOR_DISPLAYPORT_ENUM_ID4 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID4 << ENUM_ID_SHIFT |\ + CONNECTOR_OBJECT_ID_DISPLAYPORT << OBJECT_ID_SHIFT) + +#define CONNECTOR_DISPLAYPORT_ENUM_ID5 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID5 << ENUM_ID_SHIFT |\ + CONNECTOR_OBJECT_ID_DISPLAYPORT << OBJECT_ID_SHIFT) + +#define CONNECTOR_DISPLAYPORT_ENUM_ID6 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID6 << ENUM_ID_SHIFT |\ + CONNECTOR_OBJECT_ID_DISPLAYPORT << OBJECT_ID_SHIFT) + +#define CONNECTOR_MXM_ENUM_ID1 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ + CONNECTOR_OBJECT_ID_MXM << OBJECT_ID_SHIFT) //Mapping to MXM_DP_A + +#define CONNECTOR_MXM_ENUM_ID2 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\ + CONNECTOR_OBJECT_ID_MXM << OBJECT_ID_SHIFT) //Mapping to MXM_DP_B + +#define CONNECTOR_MXM_ENUM_ID3 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID3 << ENUM_ID_SHIFT |\ + CONNECTOR_OBJECT_ID_MXM << OBJECT_ID_SHIFT) //Mapping to MXM_DP_C + +#define CONNECTOR_MXM_ENUM_ID4 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID4 << ENUM_ID_SHIFT |\ + CONNECTOR_OBJECT_ID_MXM << OBJECT_ID_SHIFT) //Mapping to MXM_DP_D + +#define CONNECTOR_MXM_ENUM_ID5 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID5 << ENUM_ID_SHIFT |\ + CONNECTOR_OBJECT_ID_MXM << OBJECT_ID_SHIFT) //Mapping to MXM_LVDS_TXxx + +#define CONNECTOR_MXM_ENUM_ID6 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID6 << ENUM_ID_SHIFT |\ + CONNECTOR_OBJECT_ID_MXM << OBJECT_ID_SHIFT) //Mapping to MXM_LVDS_UXxx + +#define CONNECTOR_MXM_ENUM_ID7 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID7 << ENUM_ID_SHIFT |\ + CONNECTOR_OBJECT_ID_MXM << OBJECT_ID_SHIFT) //Mapping to MXM_DAC + +#define CONNECTOR_LVDS_eDP_ENUM_ID1 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ + CONNECTOR_OBJECT_ID_LVDS_eDP << OBJECT_ID_SHIFT) + +#define CONNECTOR_LVDS_eDP_ENUM_ID2 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\ + CONNECTOR_OBJECT_ID_LVDS_eDP << OBJECT_ID_SHIFT) + +/****************************************************/ +/* Router Object ID definition - Shared with BIOS */ +/****************************************************/ +#define ROUTER_I2C_EXTENDER_CNTL_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ROUTER << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ + ROUTER_OBJECT_ID_I2C_EXTENDER_CNTL << OBJECT_ID_SHIFT) + +/* deleted */ + +/****************************************************/ +/* Generic Object ID definition - Shared with BIOS */ +/****************************************************/ +#define GENERICOBJECT_GLSYNC_ENUM_ID1 (GRAPH_OBJECT_TYPE_GENERIC << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ + GENERIC_OBJECT_ID_GLSYNC << OBJECT_ID_SHIFT) + +#define GENERICOBJECT_PX2_NON_DRIVABLE_ID1 (GRAPH_OBJECT_TYPE_GENERIC << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ + GENERIC_OBJECT_ID_PX2_NON_DRIVABLE<< OBJECT_ID_SHIFT) + +#define GENERICOBJECT_PX2_NON_DRIVABLE_ID2 (GRAPH_OBJECT_TYPE_GENERIC << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\ + GENERIC_OBJECT_ID_PX2_NON_DRIVABLE<< OBJECT_ID_SHIFT) + +#define GENERICOBJECT_MXM_OPM_ENUM_ID1 (GRAPH_OBJECT_TYPE_GENERIC << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ + GENERIC_OBJECT_ID_MXM_OPM << OBJECT_ID_SHIFT) + +#define GENERICOBJECT_STEREO_PIN_ENUM_ID1 (GRAPH_OBJECT_TYPE_GENERIC << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ + GENERIC_OBJECT_ID_STEREO_PIN << OBJECT_ID_SHIFT) + +/****************************************************/ +/* Object Cap definition - Shared with BIOS */ +/****************************************************/ +#define GRAPHICS_OBJECT_CAP_I2C 0x00000001L +#define GRAPHICS_OBJECT_CAP_TABLE_ID 0x00000002L + + +#define GRAPHICS_OBJECT_I2CCOMMAND_TABLE_ID 0x01 +#define GRAPHICS_OBJECT_HOTPLUGDETECTIONINTERUPT_TABLE_ID 0x02 +#define GRAPHICS_OBJECT_ENCODER_OUTPUT_PROTECTION_TABLE_ID 0x03 + +#if defined(_X86_) +#pragma pack() +#endif + +#endif /*GRAPHICTYPE */ + + + + diff --git a/sys/dev/drm2/radeon/README b/sys/dev/drm2/radeon/README new file mode 100644 index 00000000000..fad030f068c --- /dev/null +++ b/sys/dev/drm2/radeon/README @@ -0,0 +1,6 @@ +# $FreeBSD$ + +== Updates to reg_srcs/ files == + +When a file in the "reg_srcs" subdirectory in updated, be sure to regen +headers by running "make" in tools/tools/drm/radeon/mkregtable. diff --git a/sys/dev/drm2/radeon/atom-bits.h b/sys/dev/drm2/radeon/atom-bits.h new file mode 100644 index 00000000000..ab099f9964a --- /dev/null +++ b/sys/dev/drm2/radeon/atom-bits.h @@ -0,0 +1,51 @@ +/* + * Copyright 2008 Advanced Micro Devices, Inc. + * + * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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. + * + * Author: Stanislaw Skowronek + */ + +#ifndef ATOM_BITS_H +#define ATOM_BITS_H + +#include +__FBSDID("$FreeBSD$"); + +static inline uint8_t get_u8(void *bios, int ptr) +{ + return ((unsigned char *)bios)[ptr]; +} +#define U8(ptr) get_u8(ctx->ctx->bios, (ptr)) +#define CU8(ptr) get_u8(ctx->bios, (ptr)) +static inline uint16_t get_u16(void *bios, int ptr) +{ + return get_u8(bios ,ptr)|(((uint16_t)get_u8(bios, ptr+1))<<8); +} +#define U16(ptr) get_u16(ctx->ctx->bios, (ptr)) +#define CU16(ptr) get_u16(ctx->bios, (ptr)) +static inline uint32_t get_u32(void *bios, int ptr) +{ + return get_u16(bios, ptr)|(((uint32_t)get_u16(bios, ptr+2))<<16); +} +#define U32(ptr) get_u32(ctx->ctx->bios, (ptr)) +#define CU32(ptr) get_u32(ctx->bios, (ptr)) +#define CSTR(ptr) (((char *)(ctx->bios))+(ptr)) + +#endif diff --git a/sys/dev/drm2/radeon/atom-names.h b/sys/dev/drm2/radeon/atom-names.h new file mode 100644 index 00000000000..737ea7d84f9 --- /dev/null +++ b/sys/dev/drm2/radeon/atom-names.h @@ -0,0 +1,103 @@ +/* + * Copyright 2008 Advanced Micro Devices, Inc. + * + * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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. + * + * Author: Stanislaw Skowronek + */ + +#ifndef ATOM_NAMES_H +#define ATOM_NAMES_H + +#include +__FBSDID("$FreeBSD$"); + +#include "atom.h" + +#ifdef ATOM_DEBUG + +#define ATOM_OP_NAMES_CNT 123 +static char *atom_op_names[ATOM_OP_NAMES_CNT] = { +"RESERVED", "MOVE_REG", "MOVE_PS", "MOVE_WS", "MOVE_FB", "MOVE_PLL", +"MOVE_MC", "AND_REG", "AND_PS", "AND_WS", "AND_FB", "AND_PLL", "AND_MC", +"OR_REG", "OR_PS", "OR_WS", "OR_FB", "OR_PLL", "OR_MC", "SHIFT_LEFT_REG", +"SHIFT_LEFT_PS", "SHIFT_LEFT_WS", "SHIFT_LEFT_FB", "SHIFT_LEFT_PLL", +"SHIFT_LEFT_MC", "SHIFT_RIGHT_REG", "SHIFT_RIGHT_PS", "SHIFT_RIGHT_WS", +"SHIFT_RIGHT_FB", "SHIFT_RIGHT_PLL", "SHIFT_RIGHT_MC", "MUL_REG", +"MUL_PS", "MUL_WS", "MUL_FB", "MUL_PLL", "MUL_MC", "DIV_REG", "DIV_PS", +"DIV_WS", "DIV_FB", "DIV_PLL", "DIV_MC", "ADD_REG", "ADD_PS", "ADD_WS", +"ADD_FB", "ADD_PLL", "ADD_MC", "SUB_REG", "SUB_PS", "SUB_WS", "SUB_FB", +"SUB_PLL", "SUB_MC", "SET_ATI_PORT", "SET_PCI_PORT", "SET_SYS_IO_PORT", +"SET_REG_BLOCK", "SET_FB_BASE", "COMPARE_REG", "COMPARE_PS", +"COMPARE_WS", "COMPARE_FB", "COMPARE_PLL", "COMPARE_MC", "SWITCH", +"JUMP", "JUMP_EQUAL", "JUMP_BELOW", "JUMP_ABOVE", "JUMP_BELOW_OR_EQUAL", +"JUMP_ABOVE_OR_EQUAL", "JUMP_NOT_EQUAL", "TEST_REG", "TEST_PS", "TEST_WS", +"TEST_FB", "TEST_PLL", "TEST_MC", "DELAY_MILLISEC", "DELAY_MICROSEC", +"CALL_TABLE", "REPEAT", "CLEAR_REG", "CLEAR_PS", "CLEAR_WS", "CLEAR_FB", +"CLEAR_PLL", "CLEAR_MC", "NOP", "EOT", "MASK_REG", "MASK_PS", "MASK_WS", +"MASK_FB", "MASK_PLL", "MASK_MC", "POST_CARD", "BEEP", "SAVE_REG", +"RESTORE_REG", "SET_DATA_BLOCK", "XOR_REG", "XOR_PS", "XOR_WS", "XOR_FB", +"XOR_PLL", "XOR_MC", "SHL_REG", "SHL_PS", "SHL_WS", "SHL_FB", "SHL_PLL", +"SHL_MC", "SHR_REG", "SHR_PS", "SHR_WS", "SHR_FB", "SHR_PLL", "SHR_MC", +"DEBUG", "CTB_DS", +}; + +#define ATOM_TABLE_NAMES_CNT 74 +static char *atom_table_names[ATOM_TABLE_NAMES_CNT] = { +"ASIC_Init", "GetDisplaySurfaceSize", "ASIC_RegistersInit", +"VRAM_BlockVenderDetection", "SetClocksRatio", "MemoryControllerInit", +"GPIO_PinInit", "MemoryParamAdjust", "DVOEncoderControl", +"GPIOPinControl", "SetEngineClock", "SetMemoryClock", "SetPixelClock", +"DynamicClockGating", "ResetMemoryDLL", "ResetMemoryDevice", +"MemoryPLLInit", "EnableMemorySelfRefresh", "AdjustMemoryController", +"EnableASIC_StaticPwrMgt", "ASIC_StaticPwrMgtStatusChange", +"DAC_LoadDetection", "TMDS2EncoderControl", "LCD1OutputControl", +"DAC1EncoderControl", "DAC2EncoderControl", "DVOOutputControl", +"CV1OutputControl", "SetCRTC_DPM_State", "TVEncoderControl", +"TMDS1EncoderControl", "LVDSEncoderControl", "TV1OutputControl", +"EnableScaler", "BlankCRTC", "EnableCRTC", "GetPixelClock", +"EnableVGA_Render", "EnableVGA_Access", "SetCRTC_Timing", +"SetCRTC_OverScan", "SetCRTC_Replication", "SelectCRTC_Source", +"EnableGraphSurfaces", "UpdateCRTC_DoubleBufferRegisters", +"LUT_AutoFill", "EnableHW_IconCursor", "GetMemoryClock", +"GetEngineClock", "SetCRTC_UsingDTDTiming", "TVBootUpStdPinDetection", +"DFP2OutputControl", "VRAM_BlockDetectionByStrap", "MemoryCleanUp", +"ReadEDIDFromHWAssistedI2C", "WriteOneByteToHWAssistedI2C", +"ReadHWAssistedI2CStatus", "SpeedFanControl", "PowerConnectorDetection", +"MC_Synchronization", "ComputeMemoryEnginePLL", "MemoryRefreshConversion", +"VRAM_GetCurrentInfoBlock", "DynamicMemorySettings", "MemoryTraining", +"EnableLVDS_SS", "DFP1OutputControl", "SetVoltage", "CRT1OutputControl", +"CRT2OutputControl", "SetupHWAssistedI2CStatus", "ClockSource", +"MemoryDeviceInit", "EnableYUV", +}; + +#define ATOM_IO_NAMES_CNT 5 +static char *atom_io_names[ATOM_IO_NAMES_CNT] = { +"MM", "PLL", "MC", "PCIE", "PCIE PORT", +}; + +#else + +#define ATOM_OP_NAMES_CNT 0 +#define ATOM_TABLE_NAMES_CNT 0 +#define ATOM_IO_NAMES_CNT 0 + +#endif + +#endif diff --git a/sys/dev/drm2/radeon/atom-types.h b/sys/dev/drm2/radeon/atom-types.h new file mode 100644 index 00000000000..7009de7bacf --- /dev/null +++ b/sys/dev/drm2/radeon/atom-types.h @@ -0,0 +1,45 @@ +/* + * Copyright 2008 Red Hat Inc. + * + * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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. + * + * Author: Dave Airlie + */ + +#ifndef ATOM_TYPES_H +#define ATOM_TYPES_H + +#include +__FBSDID("$FreeBSD$"); + +/* sync atom types to kernel types */ + +typedef uint16_t USHORT; +typedef uint32_t ULONG; +typedef uint8_t UCHAR; + + +#ifndef ATOM_BIG_ENDIAN +#if defined(__BIG_ENDIAN) +#define ATOM_BIG_ENDIAN 1 +#else +#define ATOM_BIG_ENDIAN 0 +#endif +#endif +#endif diff --git a/sys/dev/drm2/radeon/atom.c b/sys/dev/drm2/radeon/atom.c new file mode 100644 index 00000000000..7cecd07a8bb --- /dev/null +++ b/sys/dev/drm2/radeon/atom.c @@ -0,0 +1,1403 @@ +/* + * Copyright 2008 Advanced Micro Devices, Inc. + * + * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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. + * + * Author: Stanislaw Skowronek + */ + +#include +__FBSDID("$FreeBSD$"); + +#define ATOM_DEBUG + +#include "atom.h" +#include "atom-names.h" +#include "atom-bits.h" +#include "radeon.h" + +#define ATOM_COND_ABOVE 0 +#define ATOM_COND_ABOVEOREQUAL 1 +#define ATOM_COND_ALWAYS 2 +#define ATOM_COND_BELOW 3 +#define ATOM_COND_BELOWOREQUAL 4 +#define ATOM_COND_EQUAL 5 +#define ATOM_COND_NOTEQUAL 6 + +#define ATOM_PORT_ATI 0 +#define ATOM_PORT_PCI 1 +#define ATOM_PORT_SYSIO 2 + +#define ATOM_UNIT_MICROSEC 0 +#define ATOM_UNIT_MILLISEC 1 + +#define PLL_INDEX 2 +#define PLL_DATA 3 + +typedef struct { + struct atom_context *ctx; + uint32_t *ps, *ws; + int ps_shift; + uint16_t start; + unsigned last_jump; + unsigned long last_jump_jiffies; + bool abort; +} atom_exec_context; + +int atom_debug = 0; +static int atom_execute_table_locked(struct atom_context *ctx, int index, uint32_t * params); + +static uint32_t atom_arg_mask[8] = + { 0xFFFFFFFF, 0xFFFF, 0xFFFF00, 0xFFFF0000, 0xFF, 0xFF00, 0xFF0000, +0xFF000000 }; +static int atom_arg_shift[8] = { 0, 0, 8, 16, 0, 8, 16, 24 }; + +static int atom_dst_to_src[8][4] = { + /* translate destination alignment field to the source alignment encoding */ + {0, 0, 0, 0}, + {1, 2, 3, 0}, + {1, 2, 3, 0}, + {1, 2, 3, 0}, + {4, 5, 6, 7}, + {4, 5, 6, 7}, + {4, 5, 6, 7}, + {4, 5, 6, 7}, +}; +static int atom_def_dst[8] = { 0, 0, 1, 2, 0, 1, 2, 3 }; + +static int debug_depth = 0; +#ifdef ATOM_DEBUG +static void debug_print_spaces(int n) +{ + while (n--) + printf(" "); +} + +#define ATOM_DEBUG_PRINT(...) do if (atom_debug) { printf(__FILE__ __VA_ARGS__); } while (0) +#define ATOM_SDEBUG_PRINT(...) do if (atom_debug) { printf(__FILE__); debug_print_spaces(debug_depth); printf(__VA_ARGS__); } while (0) +#else +#define ATOM_DEBUG_PRINT(...) do { } while (0) +#define ATOM_SDEBUG_PRINT(...) do { } while (0) +#endif + +static uint32_t atom_iio_execute(struct atom_context *ctx, int base, + uint32_t index, uint32_t data) +{ + struct radeon_device *rdev = ctx->card->dev->dev_private; + uint32_t temp = 0xCDCDCDCD; + + while (1) + switch (CU8(base)) { + case ATOM_IIO_NOP: + base++; + break; + case ATOM_IIO_READ: + temp = ctx->card->ioreg_read(ctx->card, CU16(base + 1)); + base += 3; + break; + case ATOM_IIO_WRITE: + if (rdev->family == CHIP_RV515) + (void)ctx->card->ioreg_read(ctx->card, CU16(base + 1)); + ctx->card->ioreg_write(ctx->card, CU16(base + 1), temp); + base += 3; + break; + case ATOM_IIO_CLEAR: + temp &= + ~((0xFFFFFFFF >> (32 - CU8(base + 1))) << + CU8(base + 2)); + base += 3; + break; + case ATOM_IIO_SET: + temp |= + (0xFFFFFFFF >> (32 - CU8(base + 1))) << CU8(base + + 2); + base += 3; + break; + case ATOM_IIO_MOVE_INDEX: + temp &= + ~((0xFFFFFFFF >> (32 - CU8(base + 1))) << + CU8(base + 3)); + temp |= + ((index >> CU8(base + 2)) & + (0xFFFFFFFF >> (32 - CU8(base + 1)))) << CU8(base + + 3); + base += 4; + break; + case ATOM_IIO_MOVE_DATA: + temp &= + ~((0xFFFFFFFF >> (32 - CU8(base + 1))) << + CU8(base + 3)); + temp |= + ((data >> CU8(base + 2)) & + (0xFFFFFFFF >> (32 - CU8(base + 1)))) << CU8(base + + 3); + base += 4; + break; + case ATOM_IIO_MOVE_ATTR: + temp &= + ~((0xFFFFFFFF >> (32 - CU8(base + 1))) << + CU8(base + 3)); + temp |= + ((ctx-> + io_attr >> CU8(base + 2)) & (0xFFFFFFFF >> (32 - + CU8 + (base + + + 1)))) + << CU8(base + 3); + base += 4; + break; + case ATOM_IIO_END: + return temp; + default: + DRM_INFO("Unknown IIO opcode.\n"); + return 0; + } +} + +static uint32_t atom_get_src_int(atom_exec_context *ctx, uint8_t attr, + int *ptr, uint32_t *saved, int print) +{ + uint32_t idx, val = 0xCDCDCDCD, align, arg; + struct atom_context *gctx = ctx->ctx; + arg = attr & 7; + align = (attr >> 3) & 7; + switch (arg) { + case ATOM_ARG_REG: + idx = U16(*ptr); + (*ptr) += 2; + if (print) + ATOM_DEBUG_PRINT("REG[0x%04X]", idx); + idx += gctx->reg_block; + switch (gctx->io_mode) { + case ATOM_IO_MM: + val = gctx->card->reg_read(gctx->card, idx); + break; + case ATOM_IO_PCI: + DRM_INFO( + "PCI registers are not implemented.\n"); + return 0; + case ATOM_IO_SYSIO: + DRM_INFO( + "SYSIO registers are not implemented.\n"); + return 0; + default: + if (!(gctx->io_mode & 0x80)) { + DRM_INFO("Bad IO mode.\n"); + return 0; + } + if (!gctx->iio[gctx->io_mode & 0x7F]) { + DRM_INFO( + "Undefined indirect IO read method %d.\n", + gctx->io_mode & 0x7F); + return 0; + } + val = + atom_iio_execute(gctx, + gctx->iio[gctx->io_mode & 0x7F], + idx, 0); + } + break; + case ATOM_ARG_PS: + idx = U8(*ptr); + (*ptr)++; + /* get_unaligned_le32 avoids unaligned accesses from atombios + * tables, noticed on a DEC Alpha. */ + val = get_unaligned_le32((u32 *)&ctx->ps[idx]); + if (print) + ATOM_DEBUG_PRINT("PS[0x%02X,0x%04X]", idx, val); + break; + case ATOM_ARG_WS: + idx = U8(*ptr); + (*ptr)++; + if (print) + ATOM_DEBUG_PRINT("WS[0x%02X]", idx); + switch (idx) { + case ATOM_WS_QUOTIENT: + val = gctx->divmul[0]; + break; + case ATOM_WS_REMAINDER: + val = gctx->divmul[1]; + break; + case ATOM_WS_DATAPTR: + val = gctx->data_block; + break; + case ATOM_WS_SHIFT: + val = gctx->shift; + break; + case ATOM_WS_OR_MASK: + val = 1 << gctx->shift; + break; + case ATOM_WS_AND_MASK: + val = ~(1 << gctx->shift); + break; + case ATOM_WS_FB_WINDOW: + val = gctx->fb_base; + break; + case ATOM_WS_ATTRIBUTES: + val = gctx->io_attr; + break; + case ATOM_WS_REGPTR: + val = gctx->reg_block; + break; + default: + val = ctx->ws[idx]; + } + break; + case ATOM_ARG_ID: + idx = U16(*ptr); + (*ptr) += 2; + if (print) { + if (gctx->data_block) + ATOM_DEBUG_PRINT("ID[0x%04X+%04X]", idx, gctx->data_block); + else + ATOM_DEBUG_PRINT("ID[0x%04X]", idx); + } + val = U32(idx + gctx->data_block); + break; + case ATOM_ARG_FB: + idx = U8(*ptr); + (*ptr)++; + if ((gctx->fb_base + (idx * 4)) > gctx->scratch_size_bytes) { + DRM_ERROR("ATOM: fb read beyond scratch region: %d vs. %d\n", + gctx->fb_base + (idx * 4), gctx->scratch_size_bytes); + val = 0; + } else + val = gctx->scratch[(gctx->fb_base / 4) + idx]; + if (print) + ATOM_DEBUG_PRINT("FB[0x%02X]", idx); + break; + case ATOM_ARG_IMM: + switch (align) { + case ATOM_SRC_DWORD: + val = U32(*ptr); + (*ptr) += 4; + if (print) + ATOM_DEBUG_PRINT("IMM 0x%08X\n", val); + return val; + case ATOM_SRC_WORD0: + case ATOM_SRC_WORD8: + case ATOM_SRC_WORD16: + val = U16(*ptr); + (*ptr) += 2; + if (print) + ATOM_DEBUG_PRINT("IMM 0x%04X\n", val); + return val; + case ATOM_SRC_BYTE0: + case ATOM_SRC_BYTE8: + case ATOM_SRC_BYTE16: + case ATOM_SRC_BYTE24: + val = U8(*ptr); + (*ptr)++; + if (print) + ATOM_DEBUG_PRINT("IMM 0x%02X\n", val); + return val; + } + return 0; + case ATOM_ARG_PLL: + idx = U8(*ptr); + (*ptr)++; + if (print) + ATOM_DEBUG_PRINT("PLL[0x%02X]", idx); + val = gctx->card->pll_read(gctx->card, idx); + break; + case ATOM_ARG_MC: + idx = U8(*ptr); + (*ptr)++; + if (print) + ATOM_DEBUG_PRINT("MC[0x%02X]", idx); + val = gctx->card->mc_read(gctx->card, idx); + break; + } + if (saved) + *saved = val; + val &= atom_arg_mask[align]; + val >>= atom_arg_shift[align]; + if (print) + switch (align) { + case ATOM_SRC_DWORD: + ATOM_DEBUG_PRINT(".[31:0] -> 0x%08X\n", val); + break; + case ATOM_SRC_WORD0: + ATOM_DEBUG_PRINT(".[15:0] -> 0x%04X\n", val); + break; + case ATOM_SRC_WORD8: + ATOM_DEBUG_PRINT(".[23:8] -> 0x%04X\n", val); + break; + case ATOM_SRC_WORD16: + ATOM_DEBUG_PRINT(".[31:16] -> 0x%04X\n", val); + break; + case ATOM_SRC_BYTE0: + ATOM_DEBUG_PRINT(".[7:0] -> 0x%02X\n", val); + break; + case ATOM_SRC_BYTE8: + ATOM_DEBUG_PRINT(".[15:8] -> 0x%02X\n", val); + break; + case ATOM_SRC_BYTE16: + ATOM_DEBUG_PRINT(".[23:16] -> 0x%02X\n", val); + break; + case ATOM_SRC_BYTE24: + ATOM_DEBUG_PRINT(".[31:24] -> 0x%02X\n", val); + break; + } + return val; +} + +static void atom_skip_src_int(atom_exec_context *ctx, uint8_t attr, int *ptr) +{ + uint32_t align = (attr >> 3) & 7, arg = attr & 7; + switch (arg) { + case ATOM_ARG_REG: + case ATOM_ARG_ID: + (*ptr) += 2; + break; + case ATOM_ARG_PLL: + case ATOM_ARG_MC: + case ATOM_ARG_PS: + case ATOM_ARG_WS: + case ATOM_ARG_FB: + (*ptr)++; + break; + case ATOM_ARG_IMM: + switch (align) { + case ATOM_SRC_DWORD: + (*ptr) += 4; + return; + case ATOM_SRC_WORD0: + case ATOM_SRC_WORD8: + case ATOM_SRC_WORD16: + (*ptr) += 2; + return; + case ATOM_SRC_BYTE0: + case ATOM_SRC_BYTE8: + case ATOM_SRC_BYTE16: + case ATOM_SRC_BYTE24: + (*ptr)++; + return; + } + return; + } +} + +static uint32_t atom_get_src(atom_exec_context *ctx, uint8_t attr, int *ptr) +{ + return atom_get_src_int(ctx, attr, ptr, NULL, 1); +} + +static uint32_t atom_get_src_direct(atom_exec_context *ctx, uint8_t align, int *ptr) +{ + uint32_t val = 0xCDCDCDCD; + + switch (align) { + case ATOM_SRC_DWORD: + val = U32(*ptr); + (*ptr) += 4; + break; + case ATOM_SRC_WORD0: + case ATOM_SRC_WORD8: + case ATOM_SRC_WORD16: + val = U16(*ptr); + (*ptr) += 2; + break; + case ATOM_SRC_BYTE0: + case ATOM_SRC_BYTE8: + case ATOM_SRC_BYTE16: + case ATOM_SRC_BYTE24: + val = U8(*ptr); + (*ptr)++; + break; + } + return val; +} + +static uint32_t atom_get_dst(atom_exec_context *ctx, int arg, uint8_t attr, + int *ptr, uint32_t *saved, int print) +{ + return atom_get_src_int(ctx, + arg | atom_dst_to_src[(attr >> 3) & + 7][(attr >> 6) & 3] << 3, + ptr, saved, print); +} + +static void atom_skip_dst(atom_exec_context *ctx, int arg, uint8_t attr, int *ptr) +{ + atom_skip_src_int(ctx, + arg | atom_dst_to_src[(attr >> 3) & 7][(attr >> 6) & + 3] << 3, ptr); +} + +static void atom_put_dst(atom_exec_context *ctx, int arg, uint8_t attr, + int *ptr, uint32_t val, uint32_t saved) +{ + uint32_t align = + atom_dst_to_src[(attr >> 3) & 7][(attr >> 6) & 3], old_val = + val, idx; + struct atom_context *gctx = ctx->ctx; + old_val &= atom_arg_mask[align] >> atom_arg_shift[align]; + val <<= atom_arg_shift[align]; + val &= atom_arg_mask[align]; + saved &= ~atom_arg_mask[align]; + val |= saved; + switch (arg) { + case ATOM_ARG_REG: + idx = U16(*ptr); + (*ptr) += 2; + ATOM_DEBUG_PRINT("REG[0x%04X]", idx); + idx += gctx->reg_block; + switch (gctx->io_mode) { + case ATOM_IO_MM: + if (idx == 0) + gctx->card->reg_write(gctx->card, idx, + val << 2); + else + gctx->card->reg_write(gctx->card, idx, val); + break; + case ATOM_IO_PCI: + DRM_INFO( + "PCI registers are not implemented.\n"); + return; + case ATOM_IO_SYSIO: + DRM_INFO( + "SYSIO registers are not implemented.\n"); + return; + default: + if (!(gctx->io_mode & 0x80)) { + DRM_INFO("Bad IO mode.\n"); + return; + } + if (!gctx->iio[gctx->io_mode & 0xFF]) { + DRM_INFO( + "Undefined indirect IO write method %d.\n", + gctx->io_mode & 0x7F); + return; + } + atom_iio_execute(gctx, gctx->iio[gctx->io_mode & 0xFF], + idx, val); + } + break; + case ATOM_ARG_PS: + idx = U8(*ptr); + (*ptr)++; + ATOM_DEBUG_PRINT("PS[0x%02X]", idx); + ctx->ps[idx] = cpu_to_le32(val); + break; + case ATOM_ARG_WS: + idx = U8(*ptr); + (*ptr)++; + ATOM_DEBUG_PRINT("WS[0x%02X]", idx); + switch (idx) { + case ATOM_WS_QUOTIENT: + gctx->divmul[0] = val; + break; + case ATOM_WS_REMAINDER: + gctx->divmul[1] = val; + break; + case ATOM_WS_DATAPTR: + gctx->data_block = val; + break; + case ATOM_WS_SHIFT: + gctx->shift = val; + break; + case ATOM_WS_OR_MASK: + case ATOM_WS_AND_MASK: + break; + case ATOM_WS_FB_WINDOW: + gctx->fb_base = val; + break; + case ATOM_WS_ATTRIBUTES: + gctx->io_attr = val; + break; + case ATOM_WS_REGPTR: + gctx->reg_block = val; + break; + default: + ctx->ws[idx] = val; + } + break; + case ATOM_ARG_FB: + idx = U8(*ptr); + (*ptr)++; + if ((gctx->fb_base + (idx * 4)) > gctx->scratch_size_bytes) { + DRM_ERROR("ATOM: fb write beyond scratch region: %d vs. %d\n", + gctx->fb_base + (idx * 4), gctx->scratch_size_bytes); + } else + gctx->scratch[(gctx->fb_base / 4) + idx] = val; + ATOM_DEBUG_PRINT("FB[0x%02X]", idx); + break; + case ATOM_ARG_PLL: + idx = U8(*ptr); + (*ptr)++; + ATOM_DEBUG_PRINT("PLL[0x%02X]", idx); + gctx->card->pll_write(gctx->card, idx, val); + break; + case ATOM_ARG_MC: + idx = U8(*ptr); + (*ptr)++; + ATOM_DEBUG_PRINT("MC[0x%02X]", idx); + gctx->card->mc_write(gctx->card, idx, val); + return; + } + switch (align) { + case ATOM_SRC_DWORD: + ATOM_DEBUG_PRINT(".[31:0] <- 0x%08X\n", old_val); + break; + case ATOM_SRC_WORD0: + ATOM_DEBUG_PRINT(".[15:0] <- 0x%04X\n", old_val); + break; + case ATOM_SRC_WORD8: + ATOM_DEBUG_PRINT(".[23:8] <- 0x%04X\n", old_val); + break; + case ATOM_SRC_WORD16: + ATOM_DEBUG_PRINT(".[31:16] <- 0x%04X\n", old_val); + break; + case ATOM_SRC_BYTE0: + ATOM_DEBUG_PRINT(".[7:0] <- 0x%02X\n", old_val); + break; + case ATOM_SRC_BYTE8: + ATOM_DEBUG_PRINT(".[15:8] <- 0x%02X\n", old_val); + break; + case ATOM_SRC_BYTE16: + ATOM_DEBUG_PRINT(".[23:16] <- 0x%02X\n", old_val); + break; + case ATOM_SRC_BYTE24: + ATOM_DEBUG_PRINT(".[31:24] <- 0x%02X\n", old_val); + break; + } +} + +static void atom_op_add(atom_exec_context *ctx, int *ptr, int arg) +{ + uint8_t attr = U8((*ptr)++); + uint32_t dst, src, saved; + int dptr = *ptr; + ATOM_SDEBUG_PRINT(" dst: "); + dst = atom_get_dst(ctx, arg, attr, ptr, &saved, 1); + ATOM_SDEBUG_PRINT(" src: "); + src = atom_get_src(ctx, attr, ptr); + dst += src; + ATOM_SDEBUG_PRINT(" dst: "); + atom_put_dst(ctx, arg, attr, &dptr, dst, saved); +} + +static void atom_op_and(atom_exec_context *ctx, int *ptr, int arg) +{ + uint8_t attr = U8((*ptr)++); + uint32_t dst, src, saved; + int dptr = *ptr; + ATOM_SDEBUG_PRINT(" dst: "); + dst = atom_get_dst(ctx, arg, attr, ptr, &saved, 1); + ATOM_SDEBUG_PRINT(" src: "); + src = atom_get_src(ctx, attr, ptr); + dst &= src; + ATOM_SDEBUG_PRINT(" dst: "); + atom_put_dst(ctx, arg, attr, &dptr, dst, saved); +} + +static void atom_op_beep(atom_exec_context *ctx, int *ptr, int arg) +{ + DRM_INFO("ATOM BIOS beeped!\n"); +} + +static void atom_op_calltable(atom_exec_context *ctx, int *ptr, int arg) +{ + int idx = U8((*ptr)++); + int r = 0; + + if (idx < ATOM_TABLE_NAMES_CNT) + ATOM_SDEBUG_PRINT(" table: %d (%s)\n", idx, atom_table_names[idx]); + else + ATOM_SDEBUG_PRINT(" table: %d\n", idx); + if (U16(ctx->ctx->cmd_table + 4 + 2 * idx)) + r = atom_execute_table_locked(ctx->ctx, idx, ctx->ps + ctx->ps_shift); + if (r) { + ctx->abort = true; + } +} + +static void atom_op_clear(atom_exec_context *ctx, int *ptr, int arg) +{ + uint8_t attr = U8((*ptr)++); + uint32_t saved; + int dptr = *ptr; + attr &= 0x38; + attr |= atom_def_dst[attr >> 3] << 6; + atom_get_dst(ctx, arg, attr, ptr, &saved, 0); + ATOM_SDEBUG_PRINT(" dst: "); + atom_put_dst(ctx, arg, attr, &dptr, 0, saved); +} + +static void atom_op_compare(atom_exec_context *ctx, int *ptr, int arg) +{ + uint8_t attr = U8((*ptr)++); + uint32_t dst, src; + ATOM_SDEBUG_PRINT(" src1: "); + dst = atom_get_dst(ctx, arg, attr, ptr, NULL, 1); + ATOM_SDEBUG_PRINT(" src2: "); + src = atom_get_src(ctx, attr, ptr); + ctx->ctx->cs_equal = (dst == src); + ctx->ctx->cs_above = (dst > src); + ATOM_SDEBUG_PRINT(" result: %s %s\n", ctx->ctx->cs_equal ? "EQ" : "NE", + ctx->ctx->cs_above ? "GT" : "LE"); +} + +static void atom_op_delay(atom_exec_context *ctx, int *ptr, int arg) +{ + unsigned count = U8((*ptr)++); + ATOM_SDEBUG_PRINT(" count: %d\n", count); + if (arg == ATOM_UNIT_MICROSEC) + DRM_UDELAY(count); + else if (!drm_can_sleep()) + DRM_MDELAY(count); + else + DRM_MSLEEP(count); +} + +static void atom_op_div(atom_exec_context *ctx, int *ptr, int arg) +{ + uint8_t attr = U8((*ptr)++); + uint32_t dst, src; + ATOM_SDEBUG_PRINT(" src1: "); + dst = atom_get_dst(ctx, arg, attr, ptr, NULL, 1); + ATOM_SDEBUG_PRINT(" src2: "); + src = atom_get_src(ctx, attr, ptr); + if (src != 0) { + ctx->ctx->divmul[0] = dst / src; + ctx->ctx->divmul[1] = dst % src; + } else { + ctx->ctx->divmul[0] = 0; + ctx->ctx->divmul[1] = 0; + } +} + +static void atom_op_eot(atom_exec_context *ctx, int *ptr, int arg) +{ + /* functionally, a nop */ +} + +static void atom_op_jump(atom_exec_context *ctx, int *ptr, int arg) +{ + int execute = 0, target = U16(*ptr); + unsigned long cjiffies; + + (*ptr) += 2; + switch (arg) { + case ATOM_COND_ABOVE: + execute = ctx->ctx->cs_above; + break; + case ATOM_COND_ABOVEOREQUAL: + execute = ctx->ctx->cs_above || ctx->ctx->cs_equal; + break; + case ATOM_COND_ALWAYS: + execute = 1; + break; + case ATOM_COND_BELOW: + execute = !(ctx->ctx->cs_above || ctx->ctx->cs_equal); + break; + case ATOM_COND_BELOWOREQUAL: + execute = !ctx->ctx->cs_above; + break; + case ATOM_COND_EQUAL: + execute = ctx->ctx->cs_equal; + break; + case ATOM_COND_NOTEQUAL: + execute = !ctx->ctx->cs_equal; + break; + } + if (arg != ATOM_COND_ALWAYS) + ATOM_SDEBUG_PRINT(" taken: %s\n", execute ? "yes" : "no"); + ATOM_SDEBUG_PRINT(" target: 0x%04X\n", target); + if (execute) { + if (ctx->last_jump == (ctx->start + target)) { + cjiffies = jiffies; + if (time_after(cjiffies, ctx->last_jump_jiffies)) { + cjiffies -= ctx->last_jump_jiffies; + if ((jiffies_to_msecs(cjiffies) > 5000)) { + DRM_ERROR("atombios stuck in loop for more than 5secs aborting\n"); + ctx->abort = true; + } + } else { + /* jiffies wrap around we will just wait a little longer */ + ctx->last_jump_jiffies = jiffies; + } + } else { + ctx->last_jump = ctx->start + target; + ctx->last_jump_jiffies = jiffies; + } + *ptr = ctx->start + target; + } +} + +static void atom_op_mask(atom_exec_context *ctx, int *ptr, int arg) +{ + uint8_t attr = U8((*ptr)++); + uint32_t dst, mask, src, saved; + int dptr = *ptr; + ATOM_SDEBUG_PRINT(" dst: "); + dst = atom_get_dst(ctx, arg, attr, ptr, &saved, 1); + mask = atom_get_src_direct(ctx, ((attr >> 3) & 7), ptr); + ATOM_SDEBUG_PRINT(" mask: 0x%08x", mask); + ATOM_SDEBUG_PRINT(" src: "); + src = atom_get_src(ctx, attr, ptr); + dst &= mask; + dst |= src; + ATOM_SDEBUG_PRINT(" dst: "); + atom_put_dst(ctx, arg, attr, &dptr, dst, saved); +} + +static void atom_op_move(atom_exec_context *ctx, int *ptr, int arg) +{ + uint8_t attr = U8((*ptr)++); + uint32_t src, saved; + int dptr = *ptr; + if (((attr >> 3) & 7) != ATOM_SRC_DWORD) + atom_get_dst(ctx, arg, attr, ptr, &saved, 0); + else { + atom_skip_dst(ctx, arg, attr, ptr); + saved = 0xCDCDCDCD; + } + ATOM_SDEBUG_PRINT(" src: "); + src = atom_get_src(ctx, attr, ptr); + ATOM_SDEBUG_PRINT(" dst: "); + atom_put_dst(ctx, arg, attr, &dptr, src, saved); +} + +static void atom_op_mul(atom_exec_context *ctx, int *ptr, int arg) +{ + uint8_t attr = U8((*ptr)++); + uint32_t dst, src; + ATOM_SDEBUG_PRINT(" src1: "); + dst = atom_get_dst(ctx, arg, attr, ptr, NULL, 1); + ATOM_SDEBUG_PRINT(" src2: "); + src = atom_get_src(ctx, attr, ptr); + ctx->ctx->divmul[0] = dst * src; +} + +static void atom_op_nop(atom_exec_context *ctx, int *ptr, int arg) +{ + /* nothing */ +} + +static void atom_op_or(atom_exec_context *ctx, int *ptr, int arg) +{ + uint8_t attr = U8((*ptr)++); + uint32_t dst, src, saved; + int dptr = *ptr; + ATOM_SDEBUG_PRINT(" dst: "); + dst = atom_get_dst(ctx, arg, attr, ptr, &saved, 1); + ATOM_SDEBUG_PRINT(" src: "); + src = atom_get_src(ctx, attr, ptr); + dst |= src; + ATOM_SDEBUG_PRINT(" dst: "); + atom_put_dst(ctx, arg, attr, &dptr, dst, saved); +} + +static void atom_op_postcard(atom_exec_context *ctx, int *ptr, int arg) +{ + uint8_t val = U8((*ptr)++); + ATOM_SDEBUG_PRINT("POST card output: 0x%02X\n", val); +} + +static void atom_op_repeat(atom_exec_context *ctx, int *ptr, int arg) +{ + DRM_INFO("unimplemented!\n"); +} + +static void atom_op_restorereg(atom_exec_context *ctx, int *ptr, int arg) +{ + DRM_INFO("unimplemented!\n"); +} + +static void atom_op_savereg(atom_exec_context *ctx, int *ptr, int arg) +{ + DRM_INFO("unimplemented!\n"); +} + +static void atom_op_setdatablock(atom_exec_context *ctx, int *ptr, int arg) +{ + int idx = U8(*ptr); + (*ptr)++; + ATOM_SDEBUG_PRINT(" block: %d\n", idx); + if (!idx) + ctx->ctx->data_block = 0; + else if (idx == 255) + ctx->ctx->data_block = ctx->start; + else + ctx->ctx->data_block = U16(ctx->ctx->data_table + 4 + 2 * idx); + ATOM_SDEBUG_PRINT(" base: 0x%04X\n", ctx->ctx->data_block); +} + +static void atom_op_setfbbase(atom_exec_context *ctx, int *ptr, int arg) +{ + uint8_t attr = U8((*ptr)++); + ATOM_SDEBUG_PRINT(" fb_base: "); + ctx->ctx->fb_base = atom_get_src(ctx, attr, ptr); +} + +static void atom_op_setport(atom_exec_context *ctx, int *ptr, int arg) +{ + int port; + switch (arg) { + case ATOM_PORT_ATI: + port = U16(*ptr); + if (port < ATOM_IO_NAMES_CNT) + ATOM_SDEBUG_PRINT(" port: %d (%s)\n", port, atom_io_names[port]); + else + ATOM_SDEBUG_PRINT(" port: %d\n", port); + if (!port) + ctx->ctx->io_mode = ATOM_IO_MM; + else + ctx->ctx->io_mode = ATOM_IO_IIO | port; + (*ptr) += 2; + break; + case ATOM_PORT_PCI: + ctx->ctx->io_mode = ATOM_IO_PCI; + (*ptr)++; + break; + case ATOM_PORT_SYSIO: + ctx->ctx->io_mode = ATOM_IO_SYSIO; + (*ptr)++; + break; + } +} + +static void atom_op_setregblock(atom_exec_context *ctx, int *ptr, int arg) +{ + ctx->ctx->reg_block = U16(*ptr); + (*ptr) += 2; + ATOM_SDEBUG_PRINT(" base: 0x%04X\n", ctx->ctx->reg_block); +} + +static void atom_op_shift_left(atom_exec_context *ctx, int *ptr, int arg) +{ + uint8_t attr = U8((*ptr)++), shift; + uint32_t saved, dst; + int dptr = *ptr; + attr &= 0x38; + attr |= atom_def_dst[attr >> 3] << 6; + ATOM_SDEBUG_PRINT(" dst: "); + dst = atom_get_dst(ctx, arg, attr, ptr, &saved, 1); + shift = atom_get_src_direct(ctx, ATOM_SRC_BYTE0, ptr); + ATOM_SDEBUG_PRINT(" shift: %d\n", shift); + dst <<= shift; + ATOM_SDEBUG_PRINT(" dst: "); + atom_put_dst(ctx, arg, attr, &dptr, dst, saved); +} + +static void atom_op_shift_right(atom_exec_context *ctx, int *ptr, int arg) +{ + uint8_t attr = U8((*ptr)++), shift; + uint32_t saved, dst; + int dptr = *ptr; + attr &= 0x38; + attr |= atom_def_dst[attr >> 3] << 6; + ATOM_SDEBUG_PRINT(" dst: "); + dst = atom_get_dst(ctx, arg, attr, ptr, &saved, 1); + shift = atom_get_src_direct(ctx, ATOM_SRC_BYTE0, ptr); + ATOM_SDEBUG_PRINT(" shift: %d\n", shift); + dst >>= shift; + ATOM_SDEBUG_PRINT(" dst: "); + atom_put_dst(ctx, arg, attr, &dptr, dst, saved); +} + +static void atom_op_shl(atom_exec_context *ctx, int *ptr, int arg) +{ + uint8_t attr = U8((*ptr)++), shift; + uint32_t saved, dst; + int dptr = *ptr; + uint32_t dst_align = atom_dst_to_src[(attr >> 3) & 7][(attr >> 6) & 3]; + ATOM_SDEBUG_PRINT(" dst: "); + dst = atom_get_dst(ctx, arg, attr, ptr, &saved, 1); + /* op needs to full dst value */ + dst = saved; + shift = atom_get_src(ctx, attr, ptr); + ATOM_SDEBUG_PRINT(" shift: %d\n", shift); + dst <<= shift; + dst &= atom_arg_mask[dst_align]; + dst >>= atom_arg_shift[dst_align]; + ATOM_SDEBUG_PRINT(" dst: "); + atom_put_dst(ctx, arg, attr, &dptr, dst, saved); +} + +static void atom_op_shr(atom_exec_context *ctx, int *ptr, int arg) +{ + uint8_t attr = U8((*ptr)++), shift; + uint32_t saved, dst; + int dptr = *ptr; + uint32_t dst_align = atom_dst_to_src[(attr >> 3) & 7][(attr >> 6) & 3]; + ATOM_SDEBUG_PRINT(" dst: "); + dst = atom_get_dst(ctx, arg, attr, ptr, &saved, 1); + /* op needs to full dst value */ + dst = saved; + shift = atom_get_src(ctx, attr, ptr); + ATOM_SDEBUG_PRINT(" shift: %d\n", shift); + dst >>= shift; + dst &= atom_arg_mask[dst_align]; + dst >>= atom_arg_shift[dst_align]; + ATOM_SDEBUG_PRINT(" dst: "); + atom_put_dst(ctx, arg, attr, &dptr, dst, saved); +} + +static void atom_op_sub(atom_exec_context *ctx, int *ptr, int arg) +{ + uint8_t attr = U8((*ptr)++); + uint32_t dst, src, saved; + int dptr = *ptr; + ATOM_SDEBUG_PRINT(" dst: "); + dst = atom_get_dst(ctx, arg, attr, ptr, &saved, 1); + ATOM_SDEBUG_PRINT(" src: "); + src = atom_get_src(ctx, attr, ptr); + dst -= src; + ATOM_SDEBUG_PRINT(" dst: "); + atom_put_dst(ctx, arg, attr, &dptr, dst, saved); +} + +static void atom_op_switch(atom_exec_context *ctx, int *ptr, int arg) +{ + uint8_t attr = U8((*ptr)++); + uint32_t src, val, target; + ATOM_SDEBUG_PRINT(" switch: "); + src = atom_get_src(ctx, attr, ptr); + while (U16(*ptr) != ATOM_CASE_END) + if (U8(*ptr) == ATOM_CASE_MAGIC) { + (*ptr)++; + ATOM_SDEBUG_PRINT(" case: "); + val = + atom_get_src(ctx, (attr & 0x38) | ATOM_ARG_IMM, + ptr); + target = U16(*ptr); + if (val == src) { + ATOM_SDEBUG_PRINT(" target: %04X\n", target); + *ptr = ctx->start + target; + return; + } + (*ptr) += 2; + } else { + DRM_INFO("Bad case.\n"); + return; + } + (*ptr) += 2; +} + +static void atom_op_test(atom_exec_context *ctx, int *ptr, int arg) +{ + uint8_t attr = U8((*ptr)++); + uint32_t dst, src; + ATOM_SDEBUG_PRINT(" src1: "); + dst = atom_get_dst(ctx, arg, attr, ptr, NULL, 1); + ATOM_SDEBUG_PRINT(" src2: "); + src = atom_get_src(ctx, attr, ptr); + ctx->ctx->cs_equal = ((dst & src) == 0); + ATOM_SDEBUG_PRINT(" result: %s\n", ctx->ctx->cs_equal ? "EQ" : "NE"); +} + +static void atom_op_xor(atom_exec_context *ctx, int *ptr, int arg) +{ + uint8_t attr = U8((*ptr)++); + uint32_t dst, src, saved; + int dptr = *ptr; + ATOM_SDEBUG_PRINT(" dst: "); + dst = atom_get_dst(ctx, arg, attr, ptr, &saved, 1); + ATOM_SDEBUG_PRINT(" src: "); + src = atom_get_src(ctx, attr, ptr); + dst ^= src; + ATOM_SDEBUG_PRINT(" dst: "); + atom_put_dst(ctx, arg, attr, &dptr, dst, saved); +} + +static void atom_op_debug(atom_exec_context *ctx, int *ptr, int arg) +{ + DRM_INFO("unimplemented!\n"); +} + +static struct { + void (*func) (atom_exec_context *, int *, int); + int arg; +} opcode_table[ATOM_OP_CNT] = { + { + NULL, 0}, { + atom_op_move, ATOM_ARG_REG}, { + atom_op_move, ATOM_ARG_PS}, { + atom_op_move, ATOM_ARG_WS}, { + atom_op_move, ATOM_ARG_FB}, { + atom_op_move, ATOM_ARG_PLL}, { + atom_op_move, ATOM_ARG_MC}, { + atom_op_and, ATOM_ARG_REG}, { + atom_op_and, ATOM_ARG_PS}, { + atom_op_and, ATOM_ARG_WS}, { + atom_op_and, ATOM_ARG_FB}, { + atom_op_and, ATOM_ARG_PLL}, { + atom_op_and, ATOM_ARG_MC}, { + atom_op_or, ATOM_ARG_REG}, { + atom_op_or, ATOM_ARG_PS}, { + atom_op_or, ATOM_ARG_WS}, { + atom_op_or, ATOM_ARG_FB}, { + atom_op_or, ATOM_ARG_PLL}, { + atom_op_or, ATOM_ARG_MC}, { + atom_op_shift_left, ATOM_ARG_REG}, { + atom_op_shift_left, ATOM_ARG_PS}, { + atom_op_shift_left, ATOM_ARG_WS}, { + atom_op_shift_left, ATOM_ARG_FB}, { + atom_op_shift_left, ATOM_ARG_PLL}, { + atom_op_shift_left, ATOM_ARG_MC}, { + atom_op_shift_right, ATOM_ARG_REG}, { + atom_op_shift_right, ATOM_ARG_PS}, { + atom_op_shift_right, ATOM_ARG_WS}, { + atom_op_shift_right, ATOM_ARG_FB}, { + atom_op_shift_right, ATOM_ARG_PLL}, { + atom_op_shift_right, ATOM_ARG_MC}, { + atom_op_mul, ATOM_ARG_REG}, { + atom_op_mul, ATOM_ARG_PS}, { + atom_op_mul, ATOM_ARG_WS}, { + atom_op_mul, ATOM_ARG_FB}, { + atom_op_mul, ATOM_ARG_PLL}, { + atom_op_mul, ATOM_ARG_MC}, { + atom_op_div, ATOM_ARG_REG}, { + atom_op_div, ATOM_ARG_PS}, { + atom_op_div, ATOM_ARG_WS}, { + atom_op_div, ATOM_ARG_FB}, { + atom_op_div, ATOM_ARG_PLL}, { + atom_op_div, ATOM_ARG_MC}, { + atom_op_add, ATOM_ARG_REG}, { + atom_op_add, ATOM_ARG_PS}, { + atom_op_add, ATOM_ARG_WS}, { + atom_op_add, ATOM_ARG_FB}, { + atom_op_add, ATOM_ARG_PLL}, { + atom_op_add, ATOM_ARG_MC}, { + atom_op_sub, ATOM_ARG_REG}, { + atom_op_sub, ATOM_ARG_PS}, { + atom_op_sub, ATOM_ARG_WS}, { + atom_op_sub, ATOM_ARG_FB}, { + atom_op_sub, ATOM_ARG_PLL}, { + atom_op_sub, ATOM_ARG_MC}, { + atom_op_setport, ATOM_PORT_ATI}, { + atom_op_setport, ATOM_PORT_PCI}, { + atom_op_setport, ATOM_PORT_SYSIO}, { + atom_op_setregblock, 0}, { + atom_op_setfbbase, 0}, { + atom_op_compare, ATOM_ARG_REG}, { + atom_op_compare, ATOM_ARG_PS}, { + atom_op_compare, ATOM_ARG_WS}, { + atom_op_compare, ATOM_ARG_FB}, { + atom_op_compare, ATOM_ARG_PLL}, { + atom_op_compare, ATOM_ARG_MC}, { + atom_op_switch, 0}, { + atom_op_jump, ATOM_COND_ALWAYS}, { + atom_op_jump, ATOM_COND_EQUAL}, { + atom_op_jump, ATOM_COND_BELOW}, { + atom_op_jump, ATOM_COND_ABOVE}, { + atom_op_jump, ATOM_COND_BELOWOREQUAL}, { + atom_op_jump, ATOM_COND_ABOVEOREQUAL}, { + atom_op_jump, ATOM_COND_NOTEQUAL}, { + atom_op_test, ATOM_ARG_REG}, { + atom_op_test, ATOM_ARG_PS}, { + atom_op_test, ATOM_ARG_WS}, { + atom_op_test, ATOM_ARG_FB}, { + atom_op_test, ATOM_ARG_PLL}, { + atom_op_test, ATOM_ARG_MC}, { + atom_op_delay, ATOM_UNIT_MILLISEC}, { + atom_op_delay, ATOM_UNIT_MICROSEC}, { + atom_op_calltable, 0}, { + atom_op_repeat, 0}, { + atom_op_clear, ATOM_ARG_REG}, { + atom_op_clear, ATOM_ARG_PS}, { + atom_op_clear, ATOM_ARG_WS}, { + atom_op_clear, ATOM_ARG_FB}, { + atom_op_clear, ATOM_ARG_PLL}, { + atom_op_clear, ATOM_ARG_MC}, { + atom_op_nop, 0}, { + atom_op_eot, 0}, { + atom_op_mask, ATOM_ARG_REG}, { + atom_op_mask, ATOM_ARG_PS}, { + atom_op_mask, ATOM_ARG_WS}, { + atom_op_mask, ATOM_ARG_FB}, { + atom_op_mask, ATOM_ARG_PLL}, { + atom_op_mask, ATOM_ARG_MC}, { + atom_op_postcard, 0}, { + atom_op_beep, 0}, { + atom_op_savereg, 0}, { + atom_op_restorereg, 0}, { + atom_op_setdatablock, 0}, { + atom_op_xor, ATOM_ARG_REG}, { + atom_op_xor, ATOM_ARG_PS}, { + atom_op_xor, ATOM_ARG_WS}, { + atom_op_xor, ATOM_ARG_FB}, { + atom_op_xor, ATOM_ARG_PLL}, { + atom_op_xor, ATOM_ARG_MC}, { + atom_op_shl, ATOM_ARG_REG}, { + atom_op_shl, ATOM_ARG_PS}, { + atom_op_shl, ATOM_ARG_WS}, { + atom_op_shl, ATOM_ARG_FB}, { + atom_op_shl, ATOM_ARG_PLL}, { + atom_op_shl, ATOM_ARG_MC}, { + atom_op_shr, ATOM_ARG_REG}, { + atom_op_shr, ATOM_ARG_PS}, { + atom_op_shr, ATOM_ARG_WS}, { + atom_op_shr, ATOM_ARG_FB}, { + atom_op_shr, ATOM_ARG_PLL}, { + atom_op_shr, ATOM_ARG_MC}, { +atom_op_debug, 0},}; + +static int atom_execute_table_locked(struct atom_context *ctx, int index, uint32_t * params) +{ + int base = CU16(ctx->cmd_table + 4 + 2 * index); + int len, ws, ps, ptr; + unsigned char op; + atom_exec_context ectx; + int ret = 0; + + if (!base) + return -EINVAL; + + len = CU16(base + ATOM_CT_SIZE_PTR); + ws = CU8(base + ATOM_CT_WS_PTR); + ps = CU8(base + ATOM_CT_PS_PTR) & ATOM_CT_PS_MASK; + ptr = base + ATOM_CT_CODE_PTR; + + ATOM_SDEBUG_PRINT(">> execute %04X (len %d, WS %d, PS %d)\n", base, len, ws, ps); + + ectx.ctx = ctx; + ectx.ps_shift = ps / 4; + ectx.start = base; + ectx.ps = params; + ectx.abort = false; + ectx.last_jump = 0; + if (ws) + ectx.ws = malloc(4 * ws, DRM_MEM_DRIVER, M_ZERO | M_WAITOK); + else + ectx.ws = NULL; + + debug_depth++; + while (1) { + op = CU8(ptr++); + if (op < ATOM_OP_NAMES_CNT) + ATOM_SDEBUG_PRINT("%s @ 0x%04X\n", atom_op_names[op], ptr - 1); + else + ATOM_SDEBUG_PRINT("[%d] @ 0x%04X\n", op, ptr - 1); + if (ectx.abort) { + DRM_ERROR("atombios stuck executing %04X (len %d, WS %d, PS %d) @ 0x%04X\n", + base, len, ws, ps, ptr - 1); + ret = -EINVAL; + goto free; + } + + if (op < ATOM_OP_CNT && op > 0) + opcode_table[op].func(&ectx, &ptr, + opcode_table[op].arg); + else + break; + + if (op == ATOM_OP_EOT) + break; + } + debug_depth--; + ATOM_SDEBUG_PRINT("<<\n"); + +free: + if (ws) + free(ectx.ws, DRM_MEM_DRIVER); + return ret; +} + +int atom_execute_table(struct atom_context *ctx, int index, uint32_t * params) +{ + int r; + + sx_xlock(&ctx->mutex); + /* reset reg block */ + ctx->reg_block = 0; + /* reset fb window */ + ctx->fb_base = 0; + /* reset io mode */ + ctx->io_mode = ATOM_IO_MM; + r = atom_execute_table_locked(ctx, index, params); + sx_xunlock(&ctx->mutex); + return r; +} + +static int atom_iio_len[] = { 1, 2, 3, 3, 3, 3, 4, 4, 4, 3 }; + +static void atom_index_iio(struct atom_context *ctx, int base) +{ + ctx->iio = malloc(2 * 256, DRM_MEM_DRIVER, M_ZERO | M_WAITOK); + while (CU8(base) == ATOM_IIO_START) { + ctx->iio[CU8(base + 1)] = base + 2; + base += 2; + while (CU8(base) != ATOM_IIO_END) + base += atom_iio_len[CU8(base)]; + base += 3; + } +} + +struct atom_context *atom_parse(struct card_info *card, void *bios) +{ + int base; + struct atom_context *ctx = + malloc(sizeof(struct atom_context), DRM_MEM_DRIVER, M_ZERO | M_WAITOK); + char *str; + char name[512]; + int i; + + if (!ctx) + return NULL; + + ctx->card = card; + ctx->bios = bios; + + if (CU16(0) != ATOM_BIOS_MAGIC) { + DRM_INFO("Invalid BIOS magic.\n"); + free(ctx, DRM_MEM_DRIVER); + return NULL; + } + if (strncmp + (CSTR(ATOM_ATI_MAGIC_PTR), ATOM_ATI_MAGIC, + strlen(ATOM_ATI_MAGIC))) { + DRM_INFO("Invalid ATI magic.\n"); + free(ctx, DRM_MEM_DRIVER); + return NULL; + } + + base = CU16(ATOM_ROM_TABLE_PTR); + if (strncmp + (CSTR(base + ATOM_ROM_MAGIC_PTR), ATOM_ROM_MAGIC, + strlen(ATOM_ROM_MAGIC))) { + DRM_INFO("Invalid ATOM magic.\n"); + free(ctx, DRM_MEM_DRIVER); + return NULL; + } + + ctx->cmd_table = CU16(base + ATOM_ROM_CMD_PTR); + ctx->data_table = CU16(base + ATOM_ROM_DATA_PTR); + atom_index_iio(ctx, CU16(ctx->data_table + ATOM_DATA_IIO_PTR) + 4); + + str = CSTR(CU16(base + ATOM_ROM_MSG_PTR)); + while (*str && ((*str == '\n') || (*str == '\r'))) + str++; + /* name string isn't always 0 terminated */ + for (i = 0; i < 511; i++) { + name[i] = str[i]; + if (name[i] < '.' || name[i] > 'z') { + name[i] = 0; + break; + } + } + DRM_INFO("ATOM BIOS: %s\n", name); + + return ctx; +} + +int atom_asic_init(struct atom_context *ctx) +{ + struct radeon_device *rdev = ctx->card->dev->dev_private; + int hwi = CU16(ctx->data_table + ATOM_DATA_FWI_PTR); + uint32_t ps[16]; + int ret; + + memset(ps, 0, 64); + + ps[0] = cpu_to_le32(CU32(hwi + ATOM_FWI_DEFSCLK_PTR)); + ps[1] = cpu_to_le32(CU32(hwi + ATOM_FWI_DEFMCLK_PTR)); + if (!ps[0] || !ps[1]) + return 1; + + if (!CU16(ctx->cmd_table + 4 + 2 * ATOM_CMD_INIT)) + return 1; + ret = atom_execute_table(ctx, ATOM_CMD_INIT, ps); + if (ret) + return ret; + + memset(ps, 0, 64); + + if (rdev->family < CHIP_R600) { + if (CU16(ctx->cmd_table + 4 + 2 * ATOM_CMD_SPDFANCNTL)) + atom_execute_table(ctx, ATOM_CMD_SPDFANCNTL, ps); + } + return ret; +} + +void atom_destroy(struct atom_context *ctx) +{ + if (ctx->iio) + free(ctx->iio, DRM_MEM_DRIVER); + free(ctx, DRM_MEM_DRIVER); +} + +bool atom_parse_data_header(struct atom_context *ctx, int index, + uint16_t * size, uint8_t * frev, uint8_t * crev, + uint16_t * data_start) +{ + int offset = index * 2 + 4; + int idx = CU16(ctx->data_table + offset); + u16 *mdt = (u16 *)((char *)ctx->bios + ctx->data_table + 4); + + if (!mdt[index]) + return false; + + if (size) + *size = CU16(idx); + if (frev) + *frev = CU8(idx + 2); + if (crev) + *crev = CU8(idx + 3); + *data_start = idx; + return true; +} + +bool atom_parse_cmd_header(struct atom_context *ctx, int index, uint8_t * frev, + uint8_t * crev) +{ + int offset = index * 2 + 4; + int idx = CU16(ctx->cmd_table + offset); + u16 *mct = (u16 *)((char *)ctx->bios + ctx->cmd_table + 4); + + if (!mct[index]) + return false; + + if (frev) + *frev = CU8(idx + 2); + if (crev) + *crev = CU8(idx + 3); + return true; +} + +int atom_allocate_fb_scratch(struct atom_context *ctx) +{ + int index = GetIndexIntoMasterTable(DATA, VRAM_UsageByFirmware); + uint16_t data_offset; + int usage_bytes = 0; + struct _ATOM_VRAM_USAGE_BY_FIRMWARE *firmware_usage; + + if (atom_parse_data_header(ctx, index, NULL, NULL, NULL, &data_offset)) { + firmware_usage = (struct _ATOM_VRAM_USAGE_BY_FIRMWARE *)((char *)ctx->bios + data_offset); + + DRM_DEBUG("atom firmware requested %08x %dkb\n", + firmware_usage->asFirmwareVramReserveInfo[0].ulStartAddrUsedByFirmware, + firmware_usage->asFirmwareVramReserveInfo[0].usFirmwareUseInKb); + + usage_bytes = firmware_usage->asFirmwareVramReserveInfo[0].usFirmwareUseInKb * 1024; + } + ctx->scratch_size_bytes = 0; + if (usage_bytes == 0) + usage_bytes = 20 * 1024; + /* allocate some scratch memory */ + ctx->scratch = malloc(usage_bytes, DRM_MEM_DRIVER, M_ZERO | M_WAITOK); + if (!ctx->scratch) + return -ENOMEM; + ctx->scratch_size_bytes = usage_bytes; + return 0; +} diff --git a/sys/dev/drm2/radeon/atom.h b/sys/dev/drm2/radeon/atom.h new file mode 100644 index 00000000000..dbca87ba888 --- /dev/null +++ b/sys/dev/drm2/radeon/atom.h @@ -0,0 +1,161 @@ +/* + * Copyright 2008 Advanced Micro Devices, Inc. + * + * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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. + * + * Author: Stanislaw Skowronek + */ + +#include +__FBSDID("$FreeBSD$"); + +#ifndef ATOM_H +#define ATOM_H + +#include + +#define ATOM_BIOS_MAGIC 0xAA55 +#define ATOM_ATI_MAGIC_PTR 0x30 +#define ATOM_ATI_MAGIC " 761295520" +#define ATOM_ROM_TABLE_PTR 0x48 + +#define ATOM_ROM_MAGIC "ATOM" +#define ATOM_ROM_MAGIC_PTR 4 + +#define ATOM_ROM_MSG_PTR 0x10 +#define ATOM_ROM_CMD_PTR 0x1E +#define ATOM_ROM_DATA_PTR 0x20 + +#define ATOM_CMD_INIT 0 +#define ATOM_CMD_SETSCLK 0x0A +#define ATOM_CMD_SETMCLK 0x0B +#define ATOM_CMD_SETPCLK 0x0C +#define ATOM_CMD_SPDFANCNTL 0x39 + +#define ATOM_DATA_FWI_PTR 0xC +#define ATOM_DATA_IIO_PTR 0x32 + +#define ATOM_FWI_DEFSCLK_PTR 8 +#define ATOM_FWI_DEFMCLK_PTR 0xC +#define ATOM_FWI_MAXSCLK_PTR 0x24 +#define ATOM_FWI_MAXMCLK_PTR 0x28 + +#define ATOM_CT_SIZE_PTR 0 +#define ATOM_CT_WS_PTR 4 +#define ATOM_CT_PS_PTR 5 +#define ATOM_CT_PS_MASK 0x7F +#define ATOM_CT_CODE_PTR 6 + +#define ATOM_OP_CNT 123 +#define ATOM_OP_EOT 91 + +#define ATOM_CASE_MAGIC 0x63 +#define ATOM_CASE_END 0x5A5A + +#define ATOM_ARG_REG 0 +#define ATOM_ARG_PS 1 +#define ATOM_ARG_WS 2 +#define ATOM_ARG_FB 3 +#define ATOM_ARG_ID 4 +#define ATOM_ARG_IMM 5 +#define ATOM_ARG_PLL 6 +#define ATOM_ARG_MC 7 + +#define ATOM_SRC_DWORD 0 +#define ATOM_SRC_WORD0 1 +#define ATOM_SRC_WORD8 2 +#define ATOM_SRC_WORD16 3 +#define ATOM_SRC_BYTE0 4 +#define ATOM_SRC_BYTE8 5 +#define ATOM_SRC_BYTE16 6 +#define ATOM_SRC_BYTE24 7 + +#define ATOM_WS_QUOTIENT 0x40 +#define ATOM_WS_REMAINDER 0x41 +#define ATOM_WS_DATAPTR 0x42 +#define ATOM_WS_SHIFT 0x43 +#define ATOM_WS_OR_MASK 0x44 +#define ATOM_WS_AND_MASK 0x45 +#define ATOM_WS_FB_WINDOW 0x46 +#define ATOM_WS_ATTRIBUTES 0x47 +#define ATOM_WS_REGPTR 0x48 + +#define ATOM_IIO_NOP 0 +#define ATOM_IIO_START 1 +#define ATOM_IIO_READ 2 +#define ATOM_IIO_WRITE 3 +#define ATOM_IIO_CLEAR 4 +#define ATOM_IIO_SET 5 +#define ATOM_IIO_MOVE_INDEX 6 +#define ATOM_IIO_MOVE_ATTR 7 +#define ATOM_IIO_MOVE_DATA 8 +#define ATOM_IIO_END 9 + +#define ATOM_IO_MM 0 +#define ATOM_IO_PCI 1 +#define ATOM_IO_SYSIO 2 +#define ATOM_IO_IIO 0x80 + +struct card_info { + struct drm_device *dev; + void (* reg_write)(struct card_info *, uint32_t, uint32_t); /* filled by driver */ + uint32_t (* reg_read)(struct card_info *, uint32_t); /* filled by driver */ + void (* ioreg_write)(struct card_info *, uint32_t, uint32_t); /* filled by driver */ + uint32_t (* ioreg_read)(struct card_info *, uint32_t); /* filled by driver */ + void (* mc_write)(struct card_info *, uint32_t, uint32_t); /* filled by driver */ + uint32_t (* mc_read)(struct card_info *, uint32_t); /* filled by driver */ + void (* pll_write)(struct card_info *, uint32_t, uint32_t); /* filled by driver */ + uint32_t (* pll_read)(struct card_info *, uint32_t); /* filled by driver */ +}; + +struct atom_context { + struct card_info *card; + struct sx mutex; + void *bios; + uint32_t cmd_table, data_table; + uint16_t *iio; + + uint16_t data_block; + uint32_t fb_base; + uint32_t divmul[2]; + uint16_t io_attr; + uint16_t reg_block; + uint8_t shift; + int cs_equal, cs_above; + int io_mode; + uint32_t *scratch; + int scratch_size_bytes; +}; + +extern int atom_debug; + +struct atom_context *atom_parse(struct card_info *, void *); +int atom_execute_table(struct atom_context *, int, uint32_t *); +int atom_asic_init(struct atom_context *); +void atom_destroy(struct atom_context *); +bool atom_parse_data_header(struct atom_context *ctx, int index, uint16_t *size, + uint8_t *frev, uint8_t *crev, uint16_t *data_start); +bool atom_parse_cmd_header(struct atom_context *ctx, int index, + uint8_t *frev, uint8_t *crev); +int atom_allocate_fb_scratch(struct atom_context *ctx); +#include "atom-types.h" +#include "atombios.h" +#include "ObjectID.h" + +#endif diff --git a/sys/dev/drm2/radeon/atombios.h b/sys/dev/drm2/radeon/atombios.h new file mode 100644 index 00000000000..8ef8196fb30 --- /dev/null +++ b/sys/dev/drm2/radeon/atombios.h @@ -0,0 +1,8013 @@ +/* + * Copyright 2006-2007 Advanced Micro Devices, Inc. + * + * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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 +__FBSDID("$FreeBSD$"); + + +/****************************************************************************/ +/*Portion I: Definitions shared between VBIOS and Driver */ +/****************************************************************************/ + + +#ifndef _ATOMBIOS_H +#define _ATOMBIOS_H + +#define ATOM_VERSION_MAJOR 0x00020000 +#define ATOM_VERSION_MINOR 0x00000002 + +#define ATOM_HEADER_VERSION (ATOM_VERSION_MAJOR | ATOM_VERSION_MINOR) + +/* Endianness should be specified before inclusion, + * default to little endian + */ +#ifndef ATOM_BIG_ENDIAN +#error Endian not specified +#endif + +#ifdef _H2INC + #ifndef ULONG + typedef unsigned long ULONG; + #endif + + #ifndef UCHAR + typedef unsigned char UCHAR; + #endif + + #ifndef USHORT + typedef unsigned short USHORT; + #endif +#endif + +#define ATOM_DAC_A 0 +#define ATOM_DAC_B 1 +#define ATOM_EXT_DAC 2 + +#define ATOM_CRTC1 0 +#define ATOM_CRTC2 1 +#define ATOM_CRTC3 2 +#define ATOM_CRTC4 3 +#define ATOM_CRTC5 4 +#define ATOM_CRTC6 5 +#define ATOM_CRTC_INVALID 0xFF + +#define ATOM_DIGA 0 +#define ATOM_DIGB 1 + +#define ATOM_PPLL1 0 +#define ATOM_PPLL2 1 +#define ATOM_DCPLL 2 +#define ATOM_PPLL0 2 +#define ATOM_EXT_PLL1 8 +#define ATOM_EXT_PLL2 9 +#define ATOM_EXT_CLOCK 10 +#define ATOM_PPLL_INVALID 0xFF + +#define ENCODER_REFCLK_SRC_P1PLL 0 +#define ENCODER_REFCLK_SRC_P2PLL 1 +#define ENCODER_REFCLK_SRC_DCPLL 2 +#define ENCODER_REFCLK_SRC_EXTCLK 3 +#define ENCODER_REFCLK_SRC_INVALID 0xFF + +#define ATOM_SCALER1 0 +#define ATOM_SCALER2 1 + +#define ATOM_SCALER_DISABLE 0 +#define ATOM_SCALER_CENTER 1 +#define ATOM_SCALER_EXPANSION 2 +#define ATOM_SCALER_MULTI_EX 3 + +#define ATOM_DISABLE 0 +#define ATOM_ENABLE 1 +#define ATOM_LCD_BLOFF (ATOM_DISABLE+2) +#define ATOM_LCD_BLON (ATOM_ENABLE+2) +#define ATOM_LCD_BL_BRIGHTNESS_CONTROL (ATOM_ENABLE+3) +#define ATOM_LCD_SELFTEST_START (ATOM_DISABLE+5) +#define ATOM_LCD_SELFTEST_STOP (ATOM_ENABLE+5) +#define ATOM_ENCODER_INIT (ATOM_DISABLE+7) +#define ATOM_INIT (ATOM_DISABLE+7) +#define ATOM_GET_STATUS (ATOM_DISABLE+8) + +#define ATOM_BLANKING 1 +#define ATOM_BLANKING_OFF 0 + +#define ATOM_CURSOR1 0 +#define ATOM_CURSOR2 1 + +#define ATOM_ICON1 0 +#define ATOM_ICON2 1 + +#define ATOM_CRT1 0 +#define ATOM_CRT2 1 + +#define ATOM_TV_NTSC 1 +#define ATOM_TV_NTSCJ 2 +#define ATOM_TV_PAL 3 +#define ATOM_TV_PALM 4 +#define ATOM_TV_PALCN 5 +#define ATOM_TV_PALN 6 +#define ATOM_TV_PAL60 7 +#define ATOM_TV_SECAM 8 +#define ATOM_TV_CV 16 + +#define ATOM_DAC1_PS2 1 +#define ATOM_DAC1_CV 2 +#define ATOM_DAC1_NTSC 3 +#define ATOM_DAC1_PAL 4 + +#define ATOM_DAC2_PS2 ATOM_DAC1_PS2 +#define ATOM_DAC2_CV ATOM_DAC1_CV +#define ATOM_DAC2_NTSC ATOM_DAC1_NTSC +#define ATOM_DAC2_PAL ATOM_DAC1_PAL + +#define ATOM_PM_ON 0 +#define ATOM_PM_STANDBY 1 +#define ATOM_PM_SUSPEND 2 +#define ATOM_PM_OFF 3 + +/* Bit0:{=0:single, =1:dual}, + Bit1 {=0:666RGB, =1:888RGB}, + Bit2:3:{Grey level} + Bit4:{=0:LDI format for RGB888, =1 FPDI format for RGB888}*/ + +#define ATOM_PANEL_MISC_DUAL 0x00000001 +#define ATOM_PANEL_MISC_888RGB 0x00000002 +#define ATOM_PANEL_MISC_GREY_LEVEL 0x0000000C +#define ATOM_PANEL_MISC_FPDI 0x00000010 +#define ATOM_PANEL_MISC_GREY_LEVEL_SHIFT 2 +#define ATOM_PANEL_MISC_SPATIAL 0x00000020 +#define ATOM_PANEL_MISC_TEMPORAL 0x00000040 +#define ATOM_PANEL_MISC_API_ENABLED 0x00000080 + + +#define MEMTYPE_DDR1 "DDR1" +#define MEMTYPE_DDR2 "DDR2" +#define MEMTYPE_DDR3 "DDR3" +#define MEMTYPE_DDR4 "DDR4" + +#define ASIC_BUS_TYPE_PCI "PCI" +#define ASIC_BUS_TYPE_AGP "AGP" +#define ASIC_BUS_TYPE_PCIE "PCI_EXPRESS" + +/* Maximum size of that FireGL flag string */ + +#define ATOM_FIREGL_FLAG_STRING "FGL" //Flag used to enable FireGL Support +#define ATOM_MAX_SIZE_OF_FIREGL_FLAG_STRING 3 //sizeof( ATOM_FIREGL_FLAG_STRING ) + +#define ATOM_FAKE_DESKTOP_STRING "DSK" //Flag used to enable mobile ASIC on Desktop +#define ATOM_MAX_SIZE_OF_FAKE_DESKTOP_STRING ATOM_MAX_SIZE_OF_FIREGL_FLAG_STRING + +#define ATOM_M54T_FLAG_STRING "M54T" //Flag used to enable M54T Support +#define ATOM_MAX_SIZE_OF_M54T_FLAG_STRING 4 //sizeof( ATOM_M54T_FLAG_STRING ) + +#define HW_ASSISTED_I2C_STATUS_FAILURE 2 +#define HW_ASSISTED_I2C_STATUS_SUCCESS 1 + +#pragma pack(1) /* BIOS data must use byte aligment */ + +/* Define offset to location of ROM header. */ + +#define OFFSET_TO_POINTER_TO_ATOM_ROM_HEADER 0x00000048L +#define OFFSET_TO_ATOM_ROM_IMAGE_SIZE 0x00000002L + +#define OFFSET_TO_ATOMBIOS_ASIC_BUS_MEM_TYPE 0x94 +#define MAXSIZE_OF_ATOMBIOS_ASIC_BUS_MEM_TYPE 20 /* including the terminator 0x0! */ +#define OFFSET_TO_GET_ATOMBIOS_STRINGS_NUMBER 0x002f +#define OFFSET_TO_GET_ATOMBIOS_STRINGS_START 0x006e + +/* Common header for all ROM Data tables. + Every table pointed _ATOM_MASTER_DATA_TABLE has this common header. + And the pointer actually points to this header. */ + +typedef struct _ATOM_COMMON_TABLE_HEADER +{ + USHORT usStructureSize; + UCHAR ucTableFormatRevision; /*Change it when the Parser is not backward compatible */ + UCHAR ucTableContentRevision; /*Change it only when the table needs to change but the firmware */ + /*Image can't be updated, while Driver needs to carry the new table! */ +}ATOM_COMMON_TABLE_HEADER; + +/****************************************************************************/ +// Structure stores the ROM header. +/****************************************************************************/ +typedef struct _ATOM_ROM_HEADER +{ + ATOM_COMMON_TABLE_HEADER sHeader; + UCHAR uaFirmWareSignature[4]; /*Signature to distinguish between Atombios and non-atombios, + atombios should init it as "ATOM", don't change the position */ + USHORT usBiosRuntimeSegmentAddress; + USHORT usProtectedModeInfoOffset; + USHORT usConfigFilenameOffset; + USHORT usCRC_BlockOffset; + USHORT usBIOS_BootupMessageOffset; + USHORT usInt10Offset; + USHORT usPciBusDevInitCode; + USHORT usIoBaseAddress; + USHORT usSubsystemVendorID; + USHORT usSubsystemID; + USHORT usPCI_InfoOffset; + USHORT usMasterCommandTableOffset; /*Offset for SW to get all command table offsets, Don't change the position */ + USHORT usMasterDataTableOffset; /*Offset for SW to get all data table offsets, Don't change the position */ + UCHAR ucExtendedFunctionCode; + UCHAR ucReserved; +}ATOM_ROM_HEADER; + +/*==============================Command Table Portion==================================== */ + +#ifdef UEFI_BUILD + #define UTEMP USHORT + #define USHORT void* +#endif + +/****************************************************************************/ +// Structures used in Command.mtb +/****************************************************************************/ +typedef struct _ATOM_MASTER_LIST_OF_COMMAND_TABLES{ + USHORT ASIC_Init; //Function Table, used by various SW components,latest version 1.1 + USHORT GetDisplaySurfaceSize; //Atomic Table, Used by Bios when enabling HW ICON + USHORT ASIC_RegistersInit; //Atomic Table, indirectly used by various SW components,called from ASIC_Init + USHORT VRAM_BlockVenderDetection; //Atomic Table, used only by Bios + USHORT DIGxEncoderControl; //Only used by Bios + USHORT MemoryControllerInit; //Atomic Table, indirectly used by various SW components,called from ASIC_Init + USHORT EnableCRTCMemReq; //Function Table,directly used by various SW components,latest version 2.1 + USHORT MemoryParamAdjust; //Atomic Table, indirectly used by various SW components,called from SetMemoryClock if needed + USHORT DVOEncoderControl; //Function Table,directly used by various SW components,latest version 1.2 + USHORT GPIOPinControl; //Atomic Table, only used by Bios + USHORT SetEngineClock; //Function Table,directly used by various SW components,latest version 1.1 + USHORT SetMemoryClock; //Function Table,directly used by various SW components,latest version 1.1 + USHORT SetPixelClock; //Function Table,directly used by various SW components,latest version 1.2 + USHORT EnableDispPowerGating; //Atomic Table, indirectly used by various SW components,called from ASIC_Init + USHORT ResetMemoryDLL; //Atomic Table, indirectly used by various SW components,called from SetMemoryClock + USHORT ResetMemoryDevice; //Atomic Table, indirectly used by various SW components,called from SetMemoryClock + USHORT MemoryPLLInit; //Atomic Table, used only by Bios + USHORT AdjustDisplayPll; //Atomic Table, used by various SW componentes. + USHORT AdjustMemoryController; //Atomic Table, indirectly used by various SW components,called from SetMemoryClock + USHORT EnableASIC_StaticPwrMgt; //Atomic Table, only used by Bios + USHORT ASIC_StaticPwrMgtStatusChange; //Obsolete , only used by Bios + USHORT DAC_LoadDetection; //Atomic Table, directly used by various SW components,latest version 1.2 + USHORT LVTMAEncoderControl; //Atomic Table,directly used by various SW components,latest version 1.3 + USHORT HW_Misc_Operation; //Atomic Table, directly used by various SW components,latest version 1.1 + USHORT DAC1EncoderControl; //Atomic Table, directly used by various SW components,latest version 1.1 + USHORT DAC2EncoderControl; //Atomic Table, directly used by various SW components,latest version 1.1 + USHORT DVOOutputControl; //Atomic Table, directly used by various SW components,latest version 1.1 + USHORT CV1OutputControl; //Atomic Table, Atomic Table, Obsolete from Ry6xx, use DAC2 Output instead + USHORT GetConditionalGoldenSetting; //Only used by Bios + USHORT TVEncoderControl; //Function Table,directly used by various SW components,latest version 1.1 + USHORT PatchMCSetting; //only used by BIOS + USHORT MC_SEQ_Control; //only used by BIOS + USHORT TV1OutputControl; //Atomic Table, Obsolete from Ry6xx, use DAC2 Output instead + USHORT EnableScaler; //Atomic Table, used only by Bios + USHORT BlankCRTC; //Atomic Table, directly used by various SW components,latest version 1.1 + USHORT EnableCRTC; //Atomic Table, directly used by various SW components,latest version 1.1 + USHORT GetPixelClock; //Atomic Table, directly used by various SW components,latest version 1.1 + USHORT EnableVGA_Render; //Function Table,directly used by various SW components,latest version 1.1 + USHORT GetSCLKOverMCLKRatio; //Atomic Table, only used by Bios + USHORT SetCRTC_Timing; //Atomic Table, directly used by various SW components,latest version 1.1 + USHORT SetCRTC_OverScan; //Atomic Table, used by various SW components,latest version 1.1 + USHORT SetCRTC_Replication; //Atomic Table, used only by Bios + USHORT SelectCRTC_Source; //Atomic Table, directly used by various SW components,latest version 1.1 + USHORT EnableGraphSurfaces; //Atomic Table, used only by Bios + USHORT UpdateCRTC_DoubleBufferRegisters; //Atomic Table, used only by Bios + USHORT LUT_AutoFill; //Atomic Table, only used by Bios + USHORT EnableHW_IconCursor; //Atomic Table, only used by Bios + USHORT GetMemoryClock; //Atomic Table, directly used by various SW components,latest version 1.1 + USHORT GetEngineClock; //Atomic Table, directly used by various SW components,latest version 1.1 + USHORT SetCRTC_UsingDTDTiming; //Atomic Table, directly used by various SW components,latest version 1.1 + USHORT ExternalEncoderControl; //Atomic Table, directly used by various SW components,latest version 2.1 + USHORT LVTMAOutputControl; //Atomic Table, directly used by various SW components,latest version 1.1 + USHORT VRAM_BlockDetectionByStrap; //Atomic Table, used only by Bios + USHORT MemoryCleanUp; //Atomic Table, only used by Bios + USHORT ProcessI2cChannelTransaction; //Function Table,only used by Bios + USHORT WriteOneByteToHWAssistedI2C; //Function Table,indirectly used by various SW components + USHORT ReadHWAssistedI2CStatus; //Atomic Table, indirectly used by various SW components + USHORT SpeedFanControl; //Function Table,indirectly used by various SW components,called from ASIC_Init + USHORT PowerConnectorDetection; //Atomic Table, directly used by various SW components,latest version 1.1 + USHORT MC_Synchronization; //Atomic Table, indirectly used by various SW components,called from SetMemoryClock + USHORT ComputeMemoryEnginePLL; //Atomic Table, indirectly used by various SW components,called from SetMemory/EngineClock + USHORT MemoryRefreshConversion; //Atomic Table, indirectly used by various SW components,called from SetMemory or SetEngineClock + USHORT VRAM_GetCurrentInfoBlock; //Atomic Table, used only by Bios + USHORT DynamicMemorySettings; //Atomic Table, indirectly used by various SW components,called from SetMemoryClock + USHORT MemoryTraining; //Atomic Table, used only by Bios + USHORT EnableSpreadSpectrumOnPPLL; //Atomic Table, directly used by various SW components,latest version 1.2 + USHORT TMDSAOutputControl; //Atomic Table, directly used by various SW components,latest version 1.1 + USHORT SetVoltage; //Function Table,directly and/or indirectly used by various SW components,latest version 1.1 + USHORT DAC1OutputControl; //Atomic Table, directly used by various SW components,latest version 1.1 + USHORT DAC2OutputControl; //Atomic Table, directly used by various SW components,latest version 1.1 + USHORT ComputeMemoryClockParam; //Function Table,only used by Bios, obsolete soon.Switch to use "ReadEDIDFromHWAssistedI2C" + USHORT ClockSource; //Atomic Table, indirectly used by various SW components,called from ASIC_Init + USHORT MemoryDeviceInit; //Atomic Table, indirectly used by various SW components,called from SetMemoryClock + USHORT GetDispObjectInfo; //Atomic Table, indirectly used by various SW components,called from EnableVGARender + USHORT DIG1EncoderControl; //Atomic Table,directly used by various SW components,latest version 1.1 + USHORT DIG2EncoderControl; //Atomic Table,directly used by various SW components,latest version 1.1 + USHORT DIG1TransmitterControl; //Atomic Table,directly used by various SW components,latest version 1.1 + USHORT DIG2TransmitterControl; //Atomic Table,directly used by various SW components,latest version 1.1 + USHORT ProcessAuxChannelTransaction; //Function Table,only used by Bios + USHORT DPEncoderService; //Function Table,only used by Bios + USHORT GetVoltageInfo; //Function Table,only used by Bios since SI +}ATOM_MASTER_LIST_OF_COMMAND_TABLES; + +// For backward compatible +#define ReadEDIDFromHWAssistedI2C ProcessI2cChannelTransaction +#define DPTranslatorControl DIG2EncoderControl +#define UNIPHYTransmitterControl DIG1TransmitterControl +#define LVTMATransmitterControl DIG2TransmitterControl +#define SetCRTC_DPM_State GetConditionalGoldenSetting +#define SetUniphyInstance ASIC_StaticPwrMgtStatusChange +#define HPDInterruptService ReadHWAssistedI2CStatus +#define EnableVGA_Access GetSCLKOverMCLKRatio +#define EnableYUV GetDispObjectInfo +#define DynamicClockGating EnableDispPowerGating +#define SetupHWAssistedI2CStatus ComputeMemoryClockParam + +#define TMDSAEncoderControl PatchMCSetting +#define LVDSEncoderControl MC_SEQ_Control +#define LCD1OutputControl HW_Misc_Operation + + +typedef struct _ATOM_MASTER_COMMAND_TABLE +{ + ATOM_COMMON_TABLE_HEADER sHeader; + ATOM_MASTER_LIST_OF_COMMAND_TABLES ListOfCommandTables; +}ATOM_MASTER_COMMAND_TABLE; + +/****************************************************************************/ +// Structures used in every command table +/****************************************************************************/ +typedef struct _ATOM_TABLE_ATTRIBUTE +{ +#if ATOM_BIG_ENDIAN + USHORT UpdatedByUtility:1; //[15]=Table updated by utility flag + USHORT PS_SizeInBytes:7; //[14:8]=Size of parameter space in Bytes (multiple of a dword), + USHORT WS_SizeInBytes:8; //[7:0]=Size of workspace in Bytes (in multiple of a dword), +#else + USHORT WS_SizeInBytes:8; //[7:0]=Size of workspace in Bytes (in multiple of a dword), + USHORT PS_SizeInBytes:7; //[14:8]=Size of parameter space in Bytes (multiple of a dword), + USHORT UpdatedByUtility:1; //[15]=Table updated by utility flag +#endif +}ATOM_TABLE_ATTRIBUTE; + +typedef union _ATOM_TABLE_ATTRIBUTE_ACCESS +{ + ATOM_TABLE_ATTRIBUTE sbfAccess; + USHORT susAccess; +}ATOM_TABLE_ATTRIBUTE_ACCESS; + +/****************************************************************************/ +// Common header for all command tables. +// Every table pointed by _ATOM_MASTER_COMMAND_TABLE has this common header. +// And the pointer actually points to this header. +/****************************************************************************/ +typedef struct _ATOM_COMMON_ROM_COMMAND_TABLE_HEADER +{ + ATOM_COMMON_TABLE_HEADER CommonHeader; + ATOM_TABLE_ATTRIBUTE TableAttribute; +}ATOM_COMMON_ROM_COMMAND_TABLE_HEADER; + +/****************************************************************************/ +// Structures used by ComputeMemoryEnginePLLTable +/****************************************************************************/ +#define COMPUTE_MEMORY_PLL_PARAM 1 +#define COMPUTE_ENGINE_PLL_PARAM 2 +#define ADJUST_MC_SETTING_PARAM 3 + +/****************************************************************************/ +// Structures used by AdjustMemoryControllerTable +/****************************************************************************/ +typedef struct _ATOM_ADJUST_MEMORY_CLOCK_FREQ +{ +#if ATOM_BIG_ENDIAN + ULONG ulPointerReturnFlag:1; // BYTE_3[7]=1 - Return the pointer to the right Data Block; BYTE_3[7]=0 - Program the right Data Block + ULONG ulMemoryModuleNumber:7; // BYTE_3[6:0] + ULONG ulClockFreq:24; +#else + ULONG ulClockFreq:24; + ULONG ulMemoryModuleNumber:7; // BYTE_3[6:0] + ULONG ulPointerReturnFlag:1; // BYTE_3[7]=1 - Return the pointer to the right Data Block; BYTE_3[7]=0 - Program the right Data Block +#endif +}ATOM_ADJUST_MEMORY_CLOCK_FREQ; +#define POINTER_RETURN_FLAG 0x80 + +typedef struct _COMPUTE_MEMORY_ENGINE_PLL_PARAMETERS +{ + ULONG ulClock; //When returen, it's the re-calculated clock based on given Fb_div Post_Div and ref_div + UCHAR ucAction; //0:reserved //1:Memory //2:Engine + UCHAR ucReserved; //may expand to return larger Fbdiv later + UCHAR ucFbDiv; //return value + UCHAR ucPostDiv; //return value +}COMPUTE_MEMORY_ENGINE_PLL_PARAMETERS; + +typedef struct _COMPUTE_MEMORY_ENGINE_PLL_PARAMETERS_V2 +{ + ULONG ulClock; //When return, [23:0] return real clock + UCHAR ucAction; //0:reserved;COMPUTE_MEMORY_PLL_PARAM:Memory;COMPUTE_ENGINE_PLL_PARAM:Engine. it return ref_div to be written to register + USHORT usFbDiv; //return Feedback value to be written to register + UCHAR ucPostDiv; //return post div to be written to register +}COMPUTE_MEMORY_ENGINE_PLL_PARAMETERS_V2; +#define COMPUTE_MEMORY_ENGINE_PLL_PARAMETERS_PS_ALLOCATION COMPUTE_MEMORY_ENGINE_PLL_PARAMETERS + + +#define SET_CLOCK_FREQ_MASK 0x00FFFFFF //Clock change tables only take bit [23:0] as the requested clock value +#define USE_NON_BUS_CLOCK_MASK 0x01000000 //Applicable to both memory and engine clock change, when set, it uses another clock as the temporary clock (engine uses memory and vice versa) +#define USE_MEMORY_SELF_REFRESH_MASK 0x02000000 //Only applicable to memory clock change, when set, using memory self refresh during clock transition +#define SKIP_INTERNAL_MEMORY_PARAMETER_CHANGE 0x04000000 //Only applicable to memory clock change, when set, the table will skip predefined internal memory parameter change +#define FIRST_TIME_CHANGE_CLOCK 0x08000000 //Applicable to both memory and engine clock change,when set, it means this is 1st time to change clock after ASIC bootup +#define SKIP_SW_PROGRAM_PLL 0x10000000 //Applicable to both memory and engine clock change, when set, it means the table will not program SPLL/MPLL +#define USE_SS_ENABLED_PIXEL_CLOCK USE_NON_BUS_CLOCK_MASK + +#define b3USE_NON_BUS_CLOCK_MASK 0x01 //Applicable to both memory and engine clock change, when set, it uses another clock as the temporary clock (engine uses memory and vice versa) +#define b3USE_MEMORY_SELF_REFRESH 0x02 //Only applicable to memory clock change, when set, using memory self refresh during clock transition +#define b3SKIP_INTERNAL_MEMORY_PARAMETER_CHANGE 0x04 //Only applicable to memory clock change, when set, the table will skip predefined internal memory parameter change +#define b3FIRST_TIME_CHANGE_CLOCK 0x08 //Applicable to both memory and engine clock change,when set, it means this is 1st time to change clock after ASIC bootup +#define b3SKIP_SW_PROGRAM_PLL 0x10 //Applicable to both memory and engine clock change, when set, it means the table will not program SPLL/MPLL + +typedef struct _ATOM_COMPUTE_CLOCK_FREQ +{ +#if ATOM_BIG_ENDIAN + ULONG ulComputeClockFlag:8; // =1: COMPUTE_MEMORY_PLL_PARAM, =2: COMPUTE_ENGINE_PLL_PARAM + ULONG ulClockFreq:24; // in unit of 10kHz +#else + ULONG ulClockFreq:24; // in unit of 10kHz + ULONG ulComputeClockFlag:8; // =1: COMPUTE_MEMORY_PLL_PARAM, =2: COMPUTE_ENGINE_PLL_PARAM +#endif +}ATOM_COMPUTE_CLOCK_FREQ; + +typedef struct _ATOM_S_MPLL_FB_DIVIDER +{ + USHORT usFbDivFrac; + USHORT usFbDiv; +}ATOM_S_MPLL_FB_DIVIDER; + +typedef struct _COMPUTE_MEMORY_ENGINE_PLL_PARAMETERS_V3 +{ + union + { + ATOM_COMPUTE_CLOCK_FREQ ulClock; //Input Parameter + ATOM_S_MPLL_FB_DIVIDER ulFbDiv; //Output Parameter + }; + UCHAR ucRefDiv; //Output Parameter + UCHAR ucPostDiv; //Output Parameter + UCHAR ucCntlFlag; //Output Parameter + UCHAR ucReserved; +}COMPUTE_MEMORY_ENGINE_PLL_PARAMETERS_V3; + +// ucCntlFlag +#define ATOM_PLL_CNTL_FLAG_PLL_POST_DIV_EN 1 +#define ATOM_PLL_CNTL_FLAG_MPLL_VCO_MODE 2 +#define ATOM_PLL_CNTL_FLAG_FRACTION_DISABLE 4 +#define ATOM_PLL_CNTL_FLAG_SPLL_ISPARE_9 8 + + +// V4 are only used for APU which PLL outside GPU +typedef struct _COMPUTE_MEMORY_ENGINE_PLL_PARAMETERS_V4 +{ +#if ATOM_BIG_ENDIAN + ULONG ucPostDiv; //return parameter: post divider which is used to program to register directly + ULONG ulClock:24; //Input= target clock, output = actual clock +#else + ULONG ulClock:24; //Input= target clock, output = actual clock + ULONG ucPostDiv; //return parameter: post divider which is used to program to register directly +#endif +}COMPUTE_MEMORY_ENGINE_PLL_PARAMETERS_V4; + +typedef struct _COMPUTE_MEMORY_ENGINE_PLL_PARAMETERS_V5 +{ + union + { + ATOM_COMPUTE_CLOCK_FREQ ulClock; //Input Parameter + ATOM_S_MPLL_FB_DIVIDER ulFbDiv; //Output Parameter + }; + UCHAR ucRefDiv; //Output Parameter + UCHAR ucPostDiv; //Output Parameter + union + { + UCHAR ucCntlFlag; //Output Flags + UCHAR ucInputFlag; //Input Flags. ucInputFlag[0] - Strobe(1)/Performance(0) mode + }; + UCHAR ucReserved; +}COMPUTE_MEMORY_ENGINE_PLL_PARAMETERS_V5; + +// ucInputFlag +#define ATOM_PLL_INPUT_FLAG_PLL_STROBE_MODE_EN 1 // 1-StrobeMode, 0-PerformanceMode + +// use for ComputeMemoryClockParamTable +typedef struct _COMPUTE_MEMORY_CLOCK_PARAM_PARAMETERS_V2_1 +{ + union + { + ULONG ulClock; + ATOM_S_MPLL_FB_DIVIDER ulFbDiv; //Output:UPPER_WORD=FB_DIV_INTEGER, LOWER_WORD=FB_DIV_FRAC shl (16-FB_FRACTION_BITS) + }; + UCHAR ucDllSpeed; //Output + UCHAR ucPostDiv; //Output + union{ + UCHAR ucInputFlag; //Input : ATOM_PLL_INPUT_FLAG_PLL_STROBE_MODE_EN: 1-StrobeMode, 0-PerformanceMode + UCHAR ucPllCntlFlag; //Output: + }; + UCHAR ucBWCntl; +}COMPUTE_MEMORY_CLOCK_PARAM_PARAMETERS_V2_1; + +// definition of ucInputFlag +#define MPLL_INPUT_FLAG_STROBE_MODE_EN 0x01 +// definition of ucPllCntlFlag +#define MPLL_CNTL_FLAG_VCO_MODE_MASK 0x03 +#define MPLL_CNTL_FLAG_BYPASS_DQ_PLL 0x04 +#define MPLL_CNTL_FLAG_QDR_ENABLE 0x08 +#define MPLL_CNTL_FLAG_AD_HALF_RATE 0x10 + +//MPLL_CNTL_FLAG_BYPASS_AD_PLL has a wrong name, should be BYPASS_DQ_PLL +#define MPLL_CNTL_FLAG_BYPASS_AD_PLL 0x04 + +typedef struct _DYNAMICE_MEMORY_SETTINGS_PARAMETER +{ + ATOM_COMPUTE_CLOCK_FREQ ulClock; + ULONG ulReserved[2]; +}DYNAMICE_MEMORY_SETTINGS_PARAMETER; + +typedef struct _DYNAMICE_ENGINE_SETTINGS_PARAMETER +{ + ATOM_COMPUTE_CLOCK_FREQ ulClock; + ULONG ulMemoryClock; + ULONG ulReserved; +}DYNAMICE_ENGINE_SETTINGS_PARAMETER; + +/****************************************************************************/ +// Structures used by SetEngineClockTable +/****************************************************************************/ +typedef struct _SET_ENGINE_CLOCK_PARAMETERS +{ + ULONG ulTargetEngineClock; //In 10Khz unit +}SET_ENGINE_CLOCK_PARAMETERS; + +typedef struct _SET_ENGINE_CLOCK_PS_ALLOCATION +{ + ULONG ulTargetEngineClock; //In 10Khz unit + COMPUTE_MEMORY_ENGINE_PLL_PARAMETERS_PS_ALLOCATION sReserved; +}SET_ENGINE_CLOCK_PS_ALLOCATION; + +/****************************************************************************/ +// Structures used by SetMemoryClockTable +/****************************************************************************/ +typedef struct _SET_MEMORY_CLOCK_PARAMETERS +{ + ULONG ulTargetMemoryClock; //In 10Khz unit +}SET_MEMORY_CLOCK_PARAMETERS; + +typedef struct _SET_MEMORY_CLOCK_PS_ALLOCATION +{ + ULONG ulTargetMemoryClock; //In 10Khz unit + COMPUTE_MEMORY_ENGINE_PLL_PARAMETERS_PS_ALLOCATION sReserved; +}SET_MEMORY_CLOCK_PS_ALLOCATION; + +/****************************************************************************/ +// Structures used by ASIC_Init.ctb +/****************************************************************************/ +typedef struct _ASIC_INIT_PARAMETERS +{ + ULONG ulDefaultEngineClock; //In 10Khz unit + ULONG ulDefaultMemoryClock; //In 10Khz unit +}ASIC_INIT_PARAMETERS; + +typedef struct _ASIC_INIT_PS_ALLOCATION +{ + ASIC_INIT_PARAMETERS sASICInitClocks; + SET_ENGINE_CLOCK_PS_ALLOCATION sReserved; //Caller doesn't need to init this structure +}ASIC_INIT_PS_ALLOCATION; + +/****************************************************************************/ +// Structure used by DynamicClockGatingTable.ctb +/****************************************************************************/ +typedef struct _DYNAMIC_CLOCK_GATING_PARAMETERS +{ + UCHAR ucEnable; // ATOM_ENABLE or ATOM_DISABLE + UCHAR ucPadding[3]; +}DYNAMIC_CLOCK_GATING_PARAMETERS; +#define DYNAMIC_CLOCK_GATING_PS_ALLOCATION DYNAMIC_CLOCK_GATING_PARAMETERS + +/****************************************************************************/ +// Structure used by EnableDispPowerGatingTable.ctb +/****************************************************************************/ +typedef struct _ENABLE_DISP_POWER_GATING_PARAMETERS_V2_1 +{ + UCHAR ucDispPipeId; // ATOM_CRTC1, ATOM_CRTC2, ... + UCHAR ucEnable; // ATOM_ENABLE or ATOM_DISABLE + UCHAR ucPadding[2]; +}ENABLE_DISP_POWER_GATING_PARAMETERS_V2_1; + +/****************************************************************************/ +// Structure used by EnableASIC_StaticPwrMgtTable.ctb +/****************************************************************************/ +typedef struct _ENABLE_ASIC_STATIC_PWR_MGT_PARAMETERS +{ + UCHAR ucEnable; // ATOM_ENABLE or ATOM_DISABLE + UCHAR ucPadding[3]; +}ENABLE_ASIC_STATIC_PWR_MGT_PARAMETERS; +#define ENABLE_ASIC_STATIC_PWR_MGT_PS_ALLOCATION ENABLE_ASIC_STATIC_PWR_MGT_PARAMETERS + +/****************************************************************************/ +// Structures used by DAC_LoadDetectionTable.ctb +/****************************************************************************/ +typedef struct _DAC_LOAD_DETECTION_PARAMETERS +{ + USHORT usDeviceID; //{ATOM_DEVICE_CRTx_SUPPORT,ATOM_DEVICE_TVx_SUPPORT,ATOM_DEVICE_CVx_SUPPORT} + UCHAR ucDacType; //{ATOM_DAC_A,ATOM_DAC_B, ATOM_EXT_DAC} + UCHAR ucMisc; //Valid only when table revision =1.3 and above +}DAC_LOAD_DETECTION_PARAMETERS; + +// DAC_LOAD_DETECTION_PARAMETERS.ucMisc +#define DAC_LOAD_MISC_YPrPb 0x01 + +typedef struct _DAC_LOAD_DETECTION_PS_ALLOCATION +{ + DAC_LOAD_DETECTION_PARAMETERS sDacload; + ULONG Reserved[2];// Don't set this one, allocation for EXT DAC +}DAC_LOAD_DETECTION_PS_ALLOCATION; + +/****************************************************************************/ +// Structures used by DAC1EncoderControlTable.ctb and DAC2EncoderControlTable.ctb +/****************************************************************************/ +typedef struct _DAC_ENCODER_CONTROL_PARAMETERS +{ + USHORT usPixelClock; // in 10KHz; for bios convenient + UCHAR ucDacStandard; // See definition of ATOM_DACx_xxx, For DEC3.0, bit 7 used as internal flag to indicate DAC2 (==1) or DAC1 (==0) + UCHAR ucAction; // 0: turn off encoder + // 1: setup and turn on encoder + // 7: ATOM_ENCODER_INIT Initialize DAC +}DAC_ENCODER_CONTROL_PARAMETERS; + +#define DAC_ENCODER_CONTROL_PS_ALLOCATION DAC_ENCODER_CONTROL_PARAMETERS + +/****************************************************************************/ +// Structures used by DIG1EncoderControlTable +// DIG2EncoderControlTable +// ExternalEncoderControlTable +/****************************************************************************/ +typedef struct _DIG_ENCODER_CONTROL_PARAMETERS +{ + USHORT usPixelClock; // in 10KHz; for bios convenient + UCHAR ucConfig; + // [2] Link Select: + // =0: PHY linkA if bfLane<3 + // =1: PHY linkB if bfLanes<3 + // =0: PHY linkA+B if bfLanes=3 + // [3] Transmitter Sel + // =0: UNIPHY or PCIEPHY + // =1: LVTMA + UCHAR ucAction; // =0: turn off encoder + // =1: turn on encoder + UCHAR ucEncoderMode; + // =0: DP encoder + // =1: LVDS encoder + // =2: DVI encoder + // =3: HDMI encoder + // =4: SDVO encoder + UCHAR ucLaneNum; // how many lanes to enable + UCHAR ucReserved[2]; +}DIG_ENCODER_CONTROL_PARAMETERS; +#define DIG_ENCODER_CONTROL_PS_ALLOCATION DIG_ENCODER_CONTROL_PARAMETERS +#define EXTERNAL_ENCODER_CONTROL_PARAMETER DIG_ENCODER_CONTROL_PARAMETERS + +//ucConfig +#define ATOM_ENCODER_CONFIG_DPLINKRATE_MASK 0x01 +#define ATOM_ENCODER_CONFIG_DPLINKRATE_1_62GHZ 0x00 +#define ATOM_ENCODER_CONFIG_DPLINKRATE_2_70GHZ 0x01 +#define ATOM_ENCODER_CONFIG_DPLINKRATE_5_40GHZ 0x02 +#define ATOM_ENCODER_CONFIG_LINK_SEL_MASK 0x04 +#define ATOM_ENCODER_CONFIG_LINKA 0x00 +#define ATOM_ENCODER_CONFIG_LINKB 0x04 +#define ATOM_ENCODER_CONFIG_LINKA_B ATOM_TRANSMITTER_CONFIG_LINKA +#define ATOM_ENCODER_CONFIG_LINKB_A ATOM_ENCODER_CONFIG_LINKB +#define ATOM_ENCODER_CONFIG_TRANSMITTER_SEL_MASK 0x08 +#define ATOM_ENCODER_CONFIG_UNIPHY 0x00 +#define ATOM_ENCODER_CONFIG_LVTMA 0x08 +#define ATOM_ENCODER_CONFIG_TRANSMITTER1 0x00 +#define ATOM_ENCODER_CONFIG_TRANSMITTER2 0x08 +#define ATOM_ENCODER_CONFIG_DIGB 0x80 // VBIOS Internal use, outside SW should set this bit=0 +// ucAction +// ATOM_ENABLE: Enable Encoder +// ATOM_DISABLE: Disable Encoder + +//ucEncoderMode +#define ATOM_ENCODER_MODE_DP 0 +#define ATOM_ENCODER_MODE_LVDS 1 +#define ATOM_ENCODER_MODE_DVI 2 +#define ATOM_ENCODER_MODE_HDMI 3 +#define ATOM_ENCODER_MODE_SDVO 4 +#define ATOM_ENCODER_MODE_DP_AUDIO 5 +#define ATOM_ENCODER_MODE_TV 13 +#define ATOM_ENCODER_MODE_CV 14 +#define ATOM_ENCODER_MODE_CRT 15 +#define ATOM_ENCODER_MODE_DVO 16 +#define ATOM_ENCODER_MODE_DP_SST ATOM_ENCODER_MODE_DP // For DP1.2 +#define ATOM_ENCODER_MODE_DP_MST 5 // For DP1.2 + +typedef struct _ATOM_DIG_ENCODER_CONFIG_V2 +{ +#if ATOM_BIG_ENDIAN + UCHAR ucReserved1:2; + UCHAR ucTransmitterSel:2; // =0: UniphyAB, =1: UniphyCD =2: UniphyEF + UCHAR ucLinkSel:1; // =0: linkA/C/E =1: linkB/D/F + UCHAR ucReserved:1; + UCHAR ucDPLinkRate:1; // =0: 1.62Ghz, =1: 2.7Ghz +#else + UCHAR ucDPLinkRate:1; // =0: 1.62Ghz, =1: 2.7Ghz + UCHAR ucReserved:1; + UCHAR ucLinkSel:1; // =0: linkA/C/E =1: linkB/D/F + UCHAR ucTransmitterSel:2; // =0: UniphyAB, =1: UniphyCD =2: UniphyEF + UCHAR ucReserved1:2; +#endif +}ATOM_DIG_ENCODER_CONFIG_V2; + + +typedef struct _DIG_ENCODER_CONTROL_PARAMETERS_V2 +{ + USHORT usPixelClock; // in 10KHz; for bios convenient + ATOM_DIG_ENCODER_CONFIG_V2 acConfig; + UCHAR ucAction; + UCHAR ucEncoderMode; + // =0: DP encoder + // =1: LVDS encoder + // =2: DVI encoder + // =3: HDMI encoder + // =4: SDVO encoder + UCHAR ucLaneNum; // how many lanes to enable + UCHAR ucStatus; // = DP_LINK_TRAINING_COMPLETE or DP_LINK_TRAINING_INCOMPLETE, only used by VBIOS with command ATOM_ENCODER_CMD_QUERY_DP_LINK_TRAINING_STATUS + UCHAR ucReserved; +}DIG_ENCODER_CONTROL_PARAMETERS_V2; + +//ucConfig +#define ATOM_ENCODER_CONFIG_V2_DPLINKRATE_MASK 0x01 +#define ATOM_ENCODER_CONFIG_V2_DPLINKRATE_1_62GHZ 0x00 +#define ATOM_ENCODER_CONFIG_V2_DPLINKRATE_2_70GHZ 0x01 +#define ATOM_ENCODER_CONFIG_V2_LINK_SEL_MASK 0x04 +#define ATOM_ENCODER_CONFIG_V2_LINKA 0x00 +#define ATOM_ENCODER_CONFIG_V2_LINKB 0x04 +#define ATOM_ENCODER_CONFIG_V2_TRANSMITTER_SEL_MASK 0x18 +#define ATOM_ENCODER_CONFIG_V2_TRANSMITTER1 0x00 +#define ATOM_ENCODER_CONFIG_V2_TRANSMITTER2 0x08 +#define ATOM_ENCODER_CONFIG_V2_TRANSMITTER3 0x10 + +// ucAction: +// ATOM_DISABLE +// ATOM_ENABLE +#define ATOM_ENCODER_CMD_DP_LINK_TRAINING_START 0x08 +#define ATOM_ENCODER_CMD_DP_LINK_TRAINING_PATTERN1 0x09 +#define ATOM_ENCODER_CMD_DP_LINK_TRAINING_PATTERN2 0x0a +#define ATOM_ENCODER_CMD_DP_LINK_TRAINING_PATTERN3 0x13 +#define ATOM_ENCODER_CMD_DP_LINK_TRAINING_COMPLETE 0x0b +#define ATOM_ENCODER_CMD_DP_VIDEO_OFF 0x0c +#define ATOM_ENCODER_CMD_DP_VIDEO_ON 0x0d +#define ATOM_ENCODER_CMD_QUERY_DP_LINK_TRAINING_STATUS 0x0e +#define ATOM_ENCODER_CMD_SETUP 0x0f +#define ATOM_ENCODER_CMD_SETUP_PANEL_MODE 0x10 + +// ucStatus +#define ATOM_ENCODER_STATUS_LINK_TRAINING_COMPLETE 0x10 +#define ATOM_ENCODER_STATUS_LINK_TRAINING_INCOMPLETE 0x00 + +//ucTableFormatRevision=1 +//ucTableContentRevision=3 +// Following function ENABLE sub-function will be used by driver when TMDS/HDMI/LVDS is used, disable function will be used by driver +typedef struct _ATOM_DIG_ENCODER_CONFIG_V3 +{ +#if ATOM_BIG_ENDIAN + UCHAR ucReserved1:1; + UCHAR ucDigSel:3; // =0/1/2/3/4/5: DIG0/1/2/3/4/5 (In register spec also referred as DIGA/B/C/D/E/F) + UCHAR ucReserved:3; + UCHAR ucDPLinkRate:1; // =0: 1.62Ghz, =1: 2.7Ghz +#else + UCHAR ucDPLinkRate:1; // =0: 1.62Ghz, =1: 2.7Ghz + UCHAR ucReserved:3; + UCHAR ucDigSel:3; // =0/1/2/3/4/5: DIG0/1/2/3/4/5 (In register spec also referred as DIGA/B/C/D/E/F) + UCHAR ucReserved1:1; +#endif +}ATOM_DIG_ENCODER_CONFIG_V3; + +#define ATOM_ENCODER_CONFIG_V3_DPLINKRATE_MASK 0x03 +#define ATOM_ENCODER_CONFIG_V3_DPLINKRATE_1_62GHZ 0x00 +#define ATOM_ENCODER_CONFIG_V3_DPLINKRATE_2_70GHZ 0x01 +#define ATOM_ENCODER_CONFIG_V3_ENCODER_SEL 0x70 +#define ATOM_ENCODER_CONFIG_V3_DIG0_ENCODER 0x00 +#define ATOM_ENCODER_CONFIG_V3_DIG1_ENCODER 0x10 +#define ATOM_ENCODER_CONFIG_V3_DIG2_ENCODER 0x20 +#define ATOM_ENCODER_CONFIG_V3_DIG3_ENCODER 0x30 +#define ATOM_ENCODER_CONFIG_V3_DIG4_ENCODER 0x40 +#define ATOM_ENCODER_CONFIG_V3_DIG5_ENCODER 0x50 + +typedef struct _DIG_ENCODER_CONTROL_PARAMETERS_V3 +{ + USHORT usPixelClock; // in 10KHz; for bios convenient + ATOM_DIG_ENCODER_CONFIG_V3 acConfig; + UCHAR ucAction; + union { + UCHAR ucEncoderMode; + // =0: DP encoder + // =1: LVDS encoder + // =2: DVI encoder + // =3: HDMI encoder + // =4: SDVO encoder + // =5: DP audio + UCHAR ucPanelMode; // only valid when ucAction == ATOM_ENCODER_CMD_SETUP_PANEL_MODE + // =0: external DP + // =1: internal DP2 + // =0x11: internal DP1 for NutMeg/Travis DP translator + }; + UCHAR ucLaneNum; // how many lanes to enable + UCHAR ucBitPerColor; // only valid for DP mode when ucAction = ATOM_ENCODER_CMD_SETUP + UCHAR ucReserved; +}DIG_ENCODER_CONTROL_PARAMETERS_V3; + +//ucTableFormatRevision=1 +//ucTableContentRevision=4 +// start from NI +// Following function ENABLE sub-function will be used by driver when TMDS/HDMI/LVDS is used, disable function will be used by driver +typedef struct _ATOM_DIG_ENCODER_CONFIG_V4 +{ +#if ATOM_BIG_ENDIAN + UCHAR ucReserved1:1; + UCHAR ucDigSel:3; // =0/1/2/3/4/5: DIG0/1/2/3/4/5 (In register spec also referred as DIGA/B/C/D/E/F) + UCHAR ucReserved:2; + UCHAR ucDPLinkRate:2; // =0: 1.62Ghz, =1: 2.7Ghz, 2=5.4Ghz <= Changed comparing to previous version +#else + UCHAR ucDPLinkRate:2; // =0: 1.62Ghz, =1: 2.7Ghz, 2=5.4Ghz <= Changed comparing to previous version + UCHAR ucReserved:2; + UCHAR ucDigSel:3; // =0/1/2/3/4/5: DIG0/1/2/3/4/5 (In register spec also referred as DIGA/B/C/D/E/F) + UCHAR ucReserved1:1; +#endif +}ATOM_DIG_ENCODER_CONFIG_V4; + +#define ATOM_ENCODER_CONFIG_V4_DPLINKRATE_MASK 0x03 +#define ATOM_ENCODER_CONFIG_V4_DPLINKRATE_1_62GHZ 0x00 +#define ATOM_ENCODER_CONFIG_V4_DPLINKRATE_2_70GHZ 0x01 +#define ATOM_ENCODER_CONFIG_V4_DPLINKRATE_5_40GHZ 0x02 +#define ATOM_ENCODER_CONFIG_V4_DPLINKRATE_3_24GHZ 0x03 +#define ATOM_ENCODER_CONFIG_V4_ENCODER_SEL 0x70 +#define ATOM_ENCODER_CONFIG_V4_DIG0_ENCODER 0x00 +#define ATOM_ENCODER_CONFIG_V4_DIG1_ENCODER 0x10 +#define ATOM_ENCODER_CONFIG_V4_DIG2_ENCODER 0x20 +#define ATOM_ENCODER_CONFIG_V4_DIG3_ENCODER 0x30 +#define ATOM_ENCODER_CONFIG_V4_DIG4_ENCODER 0x40 +#define ATOM_ENCODER_CONFIG_V4_DIG5_ENCODER 0x50 +#define ATOM_ENCODER_CONFIG_V4_DIG6_ENCODER 0x60 + +typedef struct _DIG_ENCODER_CONTROL_PARAMETERS_V4 +{ + USHORT usPixelClock; // in 10KHz; for bios convenient + union{ + ATOM_DIG_ENCODER_CONFIG_V4 acConfig; + UCHAR ucConfig; + }; + UCHAR ucAction; + union { + UCHAR ucEncoderMode; + // =0: DP encoder + // =1: LVDS encoder + // =2: DVI encoder + // =3: HDMI encoder + // =4: SDVO encoder + // =5: DP audio + UCHAR ucPanelMode; // only valid when ucAction == ATOM_ENCODER_CMD_SETUP_PANEL_MODE + // =0: external DP + // =1: internal DP2 + // =0x11: internal DP1 for NutMeg/Travis DP translator + }; + UCHAR ucLaneNum; // how many lanes to enable + UCHAR ucBitPerColor; // only valid for DP mode when ucAction = ATOM_ENCODER_CMD_SETUP + UCHAR ucHPD_ID; // HPD ID (1-6). =0 means to skip HDP programming. New comparing to previous version +}DIG_ENCODER_CONTROL_PARAMETERS_V4; + +// define ucBitPerColor: +#define PANEL_BPC_UNDEFINE 0x00 +#define PANEL_6BIT_PER_COLOR 0x01 +#define PANEL_8BIT_PER_COLOR 0x02 +#define PANEL_10BIT_PER_COLOR 0x03 +#define PANEL_12BIT_PER_COLOR 0x04 +#define PANEL_16BIT_PER_COLOR 0x05 + +//define ucPanelMode +#define DP_PANEL_MODE_EXTERNAL_DP_MODE 0x00 +#define DP_PANEL_MODE_INTERNAL_DP2_MODE 0x01 +#define DP_PANEL_MODE_INTERNAL_DP1_MODE 0x11 + +/****************************************************************************/ +// Structures used by UNIPHYTransmitterControlTable +// LVTMATransmitterControlTable +// DVOOutputControlTable +/****************************************************************************/ +typedef struct _ATOM_DP_VS_MODE +{ + UCHAR ucLaneSel; + UCHAR ucLaneSet; +}ATOM_DP_VS_MODE; + +typedef struct _DIG_TRANSMITTER_CONTROL_PARAMETERS +{ + union + { + USHORT usPixelClock; // in 10KHz; for bios convenient + USHORT usInitInfo; // when init uniphy,lower 8bit is used for connector type defined in objectid.h + ATOM_DP_VS_MODE asMode; // DP Voltage swing mode + }; + UCHAR ucConfig; + // [0]=0: 4 lane Link, + // =1: 8 lane Link ( Dual Links TMDS ) + // [1]=0: InCoherent mode + // =1: Coherent Mode + // [2] Link Select: + // =0: PHY linkA if bfLane<3 + // =1: PHY linkB if bfLanes<3 + // =0: PHY linkA+B if bfLanes=3 + // [5:4]PCIE lane Sel + // =0: lane 0~3 or 0~7 + // =1: lane 4~7 + // =2: lane 8~11 or 8~15 + // =3: lane 12~15 + UCHAR ucAction; // =0: turn off encoder + // =1: turn on encoder + UCHAR ucReserved[4]; +}DIG_TRANSMITTER_CONTROL_PARAMETERS; + +#define DIG_TRANSMITTER_CONTROL_PS_ALLOCATION DIG_TRANSMITTER_CONTROL_PARAMETERS + +//ucInitInfo +#define ATOM_TRAMITTER_INITINFO_CONNECTOR_MASK 0x00ff + +//ucConfig +#define ATOM_TRANSMITTER_CONFIG_8LANE_LINK 0x01 +#define ATOM_TRANSMITTER_CONFIG_COHERENT 0x02 +#define ATOM_TRANSMITTER_CONFIG_LINK_SEL_MASK 0x04 +#define ATOM_TRANSMITTER_CONFIG_LINKA 0x00 +#define ATOM_TRANSMITTER_CONFIG_LINKB 0x04 +#define ATOM_TRANSMITTER_CONFIG_LINKA_B 0x00 +#define ATOM_TRANSMITTER_CONFIG_LINKB_A 0x04 + +#define ATOM_TRANSMITTER_CONFIG_ENCODER_SEL_MASK 0x08 // only used when ATOM_TRANSMITTER_ACTION_ENABLE +#define ATOM_TRANSMITTER_CONFIG_DIG1_ENCODER 0x00 // only used when ATOM_TRANSMITTER_ACTION_ENABLE +#define ATOM_TRANSMITTER_CONFIG_DIG2_ENCODER 0x08 // only used when ATOM_TRANSMITTER_ACTION_ENABLE + +#define ATOM_TRANSMITTER_CONFIG_CLKSRC_MASK 0x30 +#define ATOM_TRANSMITTER_CONFIG_CLKSRC_PPLL 0x00 +#define ATOM_TRANSMITTER_CONFIG_CLKSRC_PCIE 0x20 +#define ATOM_TRANSMITTER_CONFIG_CLKSRC_XTALIN 0x30 +#define ATOM_TRANSMITTER_CONFIG_LANE_SEL_MASK 0xc0 +#define ATOM_TRANSMITTER_CONFIG_LANE_0_3 0x00 +#define ATOM_TRANSMITTER_CONFIG_LANE_0_7 0x00 +#define ATOM_TRANSMITTER_CONFIG_LANE_4_7 0x40 +#define ATOM_TRANSMITTER_CONFIG_LANE_8_11 0x80 +#define ATOM_TRANSMITTER_CONFIG_LANE_8_15 0x80 +#define ATOM_TRANSMITTER_CONFIG_LANE_12_15 0xc0 + +//ucAction +#define ATOM_TRANSMITTER_ACTION_DISABLE 0 +#define ATOM_TRANSMITTER_ACTION_ENABLE 1 +#define ATOM_TRANSMITTER_ACTION_LCD_BLOFF 2 +#define ATOM_TRANSMITTER_ACTION_LCD_BLON 3 +#define ATOM_TRANSMITTER_ACTION_BL_BRIGHTNESS_CONTROL 4 +#define ATOM_TRANSMITTER_ACTION_LCD_SELFTEST_START 5 +#define ATOM_TRANSMITTER_ACTION_LCD_SELFTEST_STOP 6 +#define ATOM_TRANSMITTER_ACTION_INIT 7 +#define ATOM_TRANSMITTER_ACTION_DISABLE_OUTPUT 8 +#define ATOM_TRANSMITTER_ACTION_ENABLE_OUTPUT 9 +#define ATOM_TRANSMITTER_ACTION_SETUP 10 +#define ATOM_TRANSMITTER_ACTION_SETUP_VSEMPH 11 +#define ATOM_TRANSMITTER_ACTION_POWER_ON 12 +#define ATOM_TRANSMITTER_ACTION_POWER_OFF 13 + +// Following are used for DigTransmitterControlTable ver1.2 +typedef struct _ATOM_DIG_TRANSMITTER_CONFIG_V2 +{ +#if ATOM_BIG_ENDIAN + UCHAR ucTransmitterSel:2; //bit7:6: =0 Dig Transmitter 1 ( Uniphy AB ) + // =1 Dig Transmitter 2 ( Uniphy CD ) + // =2 Dig Transmitter 3 ( Uniphy EF ) + UCHAR ucReserved:1; + UCHAR fDPConnector:1; //bit4=0: DP connector =1: None DP connector + UCHAR ucEncoderSel:1; //bit3=0: Data/Clk path source from DIGA( DIG inst0 ). =1: Data/clk path source from DIGB ( DIG inst1 ) + UCHAR ucLinkSel:1; //bit2=0: Uniphy LINKA or C or E when fDualLinkConnector=0. when fDualLinkConnector=1, it means master link of dual link is A or C or E + // =1: Uniphy LINKB or D or F when fDualLinkConnector=0. when fDualLinkConnector=1, it means master link of dual link is B or D or F + + UCHAR fCoherentMode:1; //bit1=1: Coherent Mode ( for DVI/HDMI mode ) + UCHAR fDualLinkConnector:1; //bit0=1: Dual Link DVI connector +#else + UCHAR fDualLinkConnector:1; //bit0=1: Dual Link DVI connector + UCHAR fCoherentMode:1; //bit1=1: Coherent Mode ( for DVI/HDMI mode ) + UCHAR ucLinkSel:1; //bit2=0: Uniphy LINKA or C or E when fDualLinkConnector=0. when fDualLinkConnector=1, it means master link of dual link is A or C or E + // =1: Uniphy LINKB or D or F when fDualLinkConnector=0. when fDualLinkConnector=1, it means master link of dual link is B or D or F + UCHAR ucEncoderSel:1; //bit3=0: Data/Clk path source from DIGA( DIG inst0 ). =1: Data/clk path source from DIGB ( DIG inst1 ) + UCHAR fDPConnector:1; //bit4=0: DP connector =1: None DP connector + UCHAR ucReserved:1; + UCHAR ucTransmitterSel:2; //bit7:6: =0 Dig Transmitter 1 ( Uniphy AB ) + // =1 Dig Transmitter 2 ( Uniphy CD ) + // =2 Dig Transmitter 3 ( Uniphy EF ) +#endif +}ATOM_DIG_TRANSMITTER_CONFIG_V2; + +//ucConfig +//Bit0 +#define ATOM_TRANSMITTER_CONFIG_V2_DUAL_LINK_CONNECTOR 0x01 + +//Bit1 +#define ATOM_TRANSMITTER_CONFIG_V2_COHERENT 0x02 + +//Bit2 +#define ATOM_TRANSMITTER_CONFIG_V2_LINK_SEL_MASK 0x04 +#define ATOM_TRANSMITTER_CONFIG_V2_LINKA 0x00 +#define ATOM_TRANSMITTER_CONFIG_V2_LINKB 0x04 + +// Bit3 +#define ATOM_TRANSMITTER_CONFIG_V2_ENCODER_SEL_MASK 0x08 +#define ATOM_TRANSMITTER_CONFIG_V2_DIG1_ENCODER 0x00 // only used when ucAction == ATOM_TRANSMITTER_ACTION_ENABLE or ATOM_TRANSMITTER_ACTION_SETUP +#define ATOM_TRANSMITTER_CONFIG_V2_DIG2_ENCODER 0x08 // only used when ucAction == ATOM_TRANSMITTER_ACTION_ENABLE or ATOM_TRANSMITTER_ACTION_SETUP + +// Bit4 +#define ATOM_TRASMITTER_CONFIG_V2_DP_CONNECTOR 0x10 + +// Bit7:6 +#define ATOM_TRANSMITTER_CONFIG_V2_TRANSMITTER_SEL_MASK 0xC0 +#define ATOM_TRANSMITTER_CONFIG_V2_TRANSMITTER1 0x00 //AB +#define ATOM_TRANSMITTER_CONFIG_V2_TRANSMITTER2 0x40 //CD +#define ATOM_TRANSMITTER_CONFIG_V2_TRANSMITTER3 0x80 //EF + +typedef struct _DIG_TRANSMITTER_CONTROL_PARAMETERS_V2 +{ + union + { + USHORT usPixelClock; // in 10KHz; for bios convenient + USHORT usInitInfo; // when init uniphy,lower 8bit is used for connector type defined in objectid.h + ATOM_DP_VS_MODE asMode; // DP Voltage swing mode + }; + ATOM_DIG_TRANSMITTER_CONFIG_V2 acConfig; + UCHAR ucAction; // define as ATOM_TRANSMITER_ACTION_XXX + UCHAR ucReserved[4]; +}DIG_TRANSMITTER_CONTROL_PARAMETERS_V2; + +typedef struct _ATOM_DIG_TRANSMITTER_CONFIG_V3 +{ +#if ATOM_BIG_ENDIAN + UCHAR ucTransmitterSel:2; //bit7:6: =0 Dig Transmitter 1 ( Uniphy AB ) + // =1 Dig Transmitter 2 ( Uniphy CD ) + // =2 Dig Transmitter 3 ( Uniphy EF ) + UCHAR ucRefClkSource:2; //bit5:4: PPLL1 =0, PPLL2=1, EXT_CLK=2 + UCHAR ucEncoderSel:1; //bit3=0: Data/Clk path source from DIGA/C/E. =1: Data/clk path source from DIGB/D/F + UCHAR ucLinkSel:1; //bit2=0: Uniphy LINKA or C or E when fDualLinkConnector=0. when fDualLinkConnector=1, it means master link of dual link is A or C or E + // =1: Uniphy LINKB or D or F when fDualLinkConnector=0. when fDualLinkConnector=1, it means master link of dual link is B or D or F + UCHAR fCoherentMode:1; //bit1=1: Coherent Mode ( for DVI/HDMI mode ) + UCHAR fDualLinkConnector:1; //bit0=1: Dual Link DVI connector +#else + UCHAR fDualLinkConnector:1; //bit0=1: Dual Link DVI connector + UCHAR fCoherentMode:1; //bit1=1: Coherent Mode ( for DVI/HDMI mode ) + UCHAR ucLinkSel:1; //bit2=0: Uniphy LINKA or C or E when fDualLinkConnector=0. when fDualLinkConnector=1, it means master link of dual link is A or C or E + // =1: Uniphy LINKB or D or F when fDualLinkConnector=0. when fDualLinkConnector=1, it means master link of dual link is B or D or F + UCHAR ucEncoderSel:1; //bit3=0: Data/Clk path source from DIGA/C/E. =1: Data/clk path source from DIGB/D/F + UCHAR ucRefClkSource:2; //bit5:4: PPLL1 =0, PPLL2=1, EXT_CLK=2 + UCHAR ucTransmitterSel:2; //bit7:6: =0 Dig Transmitter 1 ( Uniphy AB ) + // =1 Dig Transmitter 2 ( Uniphy CD ) + // =2 Dig Transmitter 3 ( Uniphy EF ) +#endif +}ATOM_DIG_TRANSMITTER_CONFIG_V3; + + +typedef struct _DIG_TRANSMITTER_CONTROL_PARAMETERS_V3 +{ + union + { + USHORT usPixelClock; // in 10KHz; for bios convenient + USHORT usInitInfo; // when init uniphy,lower 8bit is used for connector type defined in objectid.h + ATOM_DP_VS_MODE asMode; // DP Voltage swing mode + }; + ATOM_DIG_TRANSMITTER_CONFIG_V3 acConfig; + UCHAR ucAction; // define as ATOM_TRANSMITER_ACTION_XXX + UCHAR ucLaneNum; + UCHAR ucReserved[3]; +}DIG_TRANSMITTER_CONTROL_PARAMETERS_V3; + +//ucConfig +//Bit0 +#define ATOM_TRANSMITTER_CONFIG_V3_DUAL_LINK_CONNECTOR 0x01 + +//Bit1 +#define ATOM_TRANSMITTER_CONFIG_V3_COHERENT 0x02 + +//Bit2 +#define ATOM_TRANSMITTER_CONFIG_V3_LINK_SEL_MASK 0x04 +#define ATOM_TRANSMITTER_CONFIG_V3_LINKA 0x00 +#define ATOM_TRANSMITTER_CONFIG_V3_LINKB 0x04 + +// Bit3 +#define ATOM_TRANSMITTER_CONFIG_V3_ENCODER_SEL_MASK 0x08 +#define ATOM_TRANSMITTER_CONFIG_V3_DIG1_ENCODER 0x00 +#define ATOM_TRANSMITTER_CONFIG_V3_DIG2_ENCODER 0x08 + +// Bit5:4 +#define ATOM_TRASMITTER_CONFIG_V3_REFCLK_SEL_MASK 0x30 +#define ATOM_TRASMITTER_CONFIG_V3_P1PLL 0x00 +#define ATOM_TRASMITTER_CONFIG_V3_P2PLL 0x10 +#define ATOM_TRASMITTER_CONFIG_V3_REFCLK_SRC_EXT 0x20 + +// Bit7:6 +#define ATOM_TRANSMITTER_CONFIG_V3_TRANSMITTER_SEL_MASK 0xC0 +#define ATOM_TRANSMITTER_CONFIG_V3_TRANSMITTER1 0x00 //AB +#define ATOM_TRANSMITTER_CONFIG_V3_TRANSMITTER2 0x40 //CD +#define ATOM_TRANSMITTER_CONFIG_V3_TRANSMITTER3 0x80 //EF + + +/****************************************************************************/ +// Structures used by UNIPHYTransmitterControlTable V1.4 +// ASIC Families: NI +// ucTableFormatRevision=1 +// ucTableContentRevision=4 +/****************************************************************************/ +typedef struct _ATOM_DP_VS_MODE_V4 +{ + UCHAR ucLaneSel; + union + { + UCHAR ucLaneSet; + struct { +#if ATOM_BIG_ENDIAN + UCHAR ucPOST_CURSOR2:2; //Bit[7:6] Post Cursor2 Level <= New in V4 + UCHAR ucPRE_EMPHASIS:3; //Bit[5:3] Pre-emphasis Level + UCHAR ucVOLTAGE_SWING:3; //Bit[2:0] Voltage Swing Level +#else + UCHAR ucVOLTAGE_SWING:3; //Bit[2:0] Voltage Swing Level + UCHAR ucPRE_EMPHASIS:3; //Bit[5:3] Pre-emphasis Level + UCHAR ucPOST_CURSOR2:2; //Bit[7:6] Post Cursor2 Level <= New in V4 +#endif + }; + }; +}ATOM_DP_VS_MODE_V4; + +typedef struct _ATOM_DIG_TRANSMITTER_CONFIG_V4 +{ +#if ATOM_BIG_ENDIAN + UCHAR ucTransmitterSel:2; //bit7:6: =0 Dig Transmitter 1 ( Uniphy AB ) + // =1 Dig Transmitter 2 ( Uniphy CD ) + // =2 Dig Transmitter 3 ( Uniphy EF ) + UCHAR ucRefClkSource:2; //bit5:4: PPLL1 =0, PPLL2=1, DCPLL=2, EXT_CLK=3 <= New + UCHAR ucEncoderSel:1; //bit3=0: Data/Clk path source from DIGA/C/E. =1: Data/clk path source from DIGB/D/F + UCHAR ucLinkSel:1; //bit2=0: Uniphy LINKA or C or E when fDualLinkConnector=0. when fDualLinkConnector=1, it means master link of dual link is A or C or E + // =1: Uniphy LINKB or D or F when fDualLinkConnector=0. when fDualLinkConnector=1, it means master link of dual link is B or D or F + UCHAR fCoherentMode:1; //bit1=1: Coherent Mode ( for DVI/HDMI mode ) + UCHAR fDualLinkConnector:1; //bit0=1: Dual Link DVI connector +#else + UCHAR fDualLinkConnector:1; //bit0=1: Dual Link DVI connector + UCHAR fCoherentMode:1; //bit1=1: Coherent Mode ( for DVI/HDMI mode ) + UCHAR ucLinkSel:1; //bit2=0: Uniphy LINKA or C or E when fDualLinkConnector=0. when fDualLinkConnector=1, it means master link of dual link is A or C or E + // =1: Uniphy LINKB or D or F when fDualLinkConnector=0. when fDualLinkConnector=1, it means master link of dual link is B or D or F + UCHAR ucEncoderSel:1; //bit3=0: Data/Clk path source from DIGA/C/E. =1: Data/clk path source from DIGB/D/F + UCHAR ucRefClkSource:2; //bit5:4: PPLL1 =0, PPLL2=1, DCPLL=2, EXT_CLK=3 <= New + UCHAR ucTransmitterSel:2; //bit7:6: =0 Dig Transmitter 1 ( Uniphy AB ) + // =1 Dig Transmitter 2 ( Uniphy CD ) + // =2 Dig Transmitter 3 ( Uniphy EF ) +#endif +}ATOM_DIG_TRANSMITTER_CONFIG_V4; + +typedef struct _DIG_TRANSMITTER_CONTROL_PARAMETERS_V4 +{ + union + { + USHORT usPixelClock; // in 10KHz; for bios convenient + USHORT usInitInfo; // when init uniphy,lower 8bit is used for connector type defined in objectid.h + ATOM_DP_VS_MODE_V4 asMode; // DP Voltage swing mode Redefined comparing to previous version + }; + union + { + ATOM_DIG_TRANSMITTER_CONFIG_V4 acConfig; + UCHAR ucConfig; + }; + UCHAR ucAction; // define as ATOM_TRANSMITER_ACTION_XXX + UCHAR ucLaneNum; + UCHAR ucReserved[3]; +}DIG_TRANSMITTER_CONTROL_PARAMETERS_V4; + +//ucConfig +//Bit0 +#define ATOM_TRANSMITTER_CONFIG_V4_DUAL_LINK_CONNECTOR 0x01 +//Bit1 +#define ATOM_TRANSMITTER_CONFIG_V4_COHERENT 0x02 +//Bit2 +#define ATOM_TRANSMITTER_CONFIG_V4_LINK_SEL_MASK 0x04 +#define ATOM_TRANSMITTER_CONFIG_V4_LINKA 0x00 +#define ATOM_TRANSMITTER_CONFIG_V4_LINKB 0x04 +// Bit3 +#define ATOM_TRANSMITTER_CONFIG_V4_ENCODER_SEL_MASK 0x08 +#define ATOM_TRANSMITTER_CONFIG_V4_DIG1_ENCODER 0x00 +#define ATOM_TRANSMITTER_CONFIG_V4_DIG2_ENCODER 0x08 +// Bit5:4 +#define ATOM_TRANSMITTER_CONFIG_V4_REFCLK_SEL_MASK 0x30 +#define ATOM_TRANSMITTER_CONFIG_V4_P1PLL 0x00 +#define ATOM_TRANSMITTER_CONFIG_V4_P2PLL 0x10 +#define ATOM_TRANSMITTER_CONFIG_V4_DCPLL 0x20 // New in _V4 +#define ATOM_TRANSMITTER_CONFIG_V4_REFCLK_SRC_EXT 0x30 // Changed comparing to V3 +// Bit7:6 +#define ATOM_TRANSMITTER_CONFIG_V4_TRANSMITTER_SEL_MASK 0xC0 +#define ATOM_TRANSMITTER_CONFIG_V4_TRANSMITTER1 0x00 //AB +#define ATOM_TRANSMITTER_CONFIG_V4_TRANSMITTER2 0x40 //CD +#define ATOM_TRANSMITTER_CONFIG_V4_TRANSMITTER3 0x80 //EF + + +typedef struct _ATOM_DIG_TRANSMITTER_CONFIG_V5 +{ +#if ATOM_BIG_ENDIAN + UCHAR ucReservd1:1; + UCHAR ucHPDSel:3; + UCHAR ucPhyClkSrcId:2; + UCHAR ucCoherentMode:1; + UCHAR ucReserved:1; +#else + UCHAR ucReserved:1; + UCHAR ucCoherentMode:1; + UCHAR ucPhyClkSrcId:2; + UCHAR ucHPDSel:3; + UCHAR ucReservd1:1; +#endif +}ATOM_DIG_TRANSMITTER_CONFIG_V5; + +typedef struct _DIG_TRANSMITTER_CONTROL_PARAMETERS_V1_5 +{ + USHORT usSymClock; // Encoder Clock in 10kHz,(DP mode)= linkclock/10, (TMDS/LVDS/HDMI)= pixel clock, (HDMI deep color), =pixel clock * deep_color_ratio + UCHAR ucPhyId; // 0=UNIPHYA, 1=UNIPHYB, 2=UNIPHYC, 3=UNIPHYD, 4= UNIPHYE 5=UNIPHYF + UCHAR ucAction; // define as ATOM_TRANSMITER_ACTION_xxx + UCHAR ucLaneNum; // indicate lane number 1-8 + UCHAR ucConnObjId; // Connector Object Id defined in ObjectId.h + UCHAR ucDigMode; // indicate DIG mode + union{ + ATOM_DIG_TRANSMITTER_CONFIG_V5 asConfig; + UCHAR ucConfig; + }; + UCHAR ucDigEncoderSel; // indicate DIG front end encoder + UCHAR ucDPLaneSet; + UCHAR ucReserved; + UCHAR ucReserved1; +}DIG_TRANSMITTER_CONTROL_PARAMETERS_V1_5; + +//ucPhyId +#define ATOM_PHY_ID_UNIPHYA 0 +#define ATOM_PHY_ID_UNIPHYB 1 +#define ATOM_PHY_ID_UNIPHYC 2 +#define ATOM_PHY_ID_UNIPHYD 3 +#define ATOM_PHY_ID_UNIPHYE 4 +#define ATOM_PHY_ID_UNIPHYF 5 +#define ATOM_PHY_ID_UNIPHYG 6 + +// ucDigEncoderSel +#define ATOM_TRANMSITTER_V5__DIGA_SEL 0x01 +#define ATOM_TRANMSITTER_V5__DIGB_SEL 0x02 +#define ATOM_TRANMSITTER_V5__DIGC_SEL 0x04 +#define ATOM_TRANMSITTER_V5__DIGD_SEL 0x08 +#define ATOM_TRANMSITTER_V5__DIGE_SEL 0x10 +#define ATOM_TRANMSITTER_V5__DIGF_SEL 0x20 +#define ATOM_TRANMSITTER_V5__DIGG_SEL 0x40 + +// ucDigMode +#define ATOM_TRANSMITTER_DIGMODE_V5_DP 0 +#define ATOM_TRANSMITTER_DIGMODE_V5_LVDS 1 +#define ATOM_TRANSMITTER_DIGMODE_V5_DVI 2 +#define ATOM_TRANSMITTER_DIGMODE_V5_HDMI 3 +#define ATOM_TRANSMITTER_DIGMODE_V5_SDVO 4 +#define ATOM_TRANSMITTER_DIGMODE_V5_DP_MST 5 + +// ucDPLaneSet +#define DP_LANE_SET__0DB_0_4V 0x00 +#define DP_LANE_SET__0DB_0_6V 0x01 +#define DP_LANE_SET__0DB_0_8V 0x02 +#define DP_LANE_SET__0DB_1_2V 0x03 +#define DP_LANE_SET__3_5DB_0_4V 0x08 +#define DP_LANE_SET__3_5DB_0_6V 0x09 +#define DP_LANE_SET__3_5DB_0_8V 0x0a +#define DP_LANE_SET__6DB_0_4V 0x10 +#define DP_LANE_SET__6DB_0_6V 0x11 +#define DP_LANE_SET__9_5DB_0_4V 0x18 + +// ATOM_DIG_TRANSMITTER_CONFIG_V5 asConfig; +// Bit1 +#define ATOM_TRANSMITTER_CONFIG_V5_COHERENT 0x02 + +// Bit3:2 +#define ATOM_TRANSMITTER_CONFIG_V5_REFCLK_SEL_MASK 0x0c +#define ATOM_TRANSMITTER_CONFIG_V5_REFCLK_SEL_SHIFT 0x02 + +#define ATOM_TRANSMITTER_CONFIG_V5_P1PLL 0x00 +#define ATOM_TRANSMITTER_CONFIG_V5_P2PLL 0x04 +#define ATOM_TRANSMITTER_CONFIG_V5_P0PLL 0x08 +#define ATOM_TRANSMITTER_CONFIG_V5_REFCLK_SRC_EXT 0x0c +// Bit6:4 +#define ATOM_TRANSMITTER_CONFIG_V5_HPD_SEL_MASK 0x70 +#define ATOM_TRANSMITTER_CONFIG_V5_HPD_SEL_SHIFT 0x04 + +#define ATOM_TRANSMITTER_CONFIG_V5_NO_HPD_SEL 0x00 +#define ATOM_TRANSMITTER_CONFIG_V5_HPD1_SEL 0x10 +#define ATOM_TRANSMITTER_CONFIG_V5_HPD2_SEL 0x20 +#define ATOM_TRANSMITTER_CONFIG_V5_HPD3_SEL 0x30 +#define ATOM_TRANSMITTER_CONFIG_V5_HPD4_SEL 0x40 +#define ATOM_TRANSMITTER_CONFIG_V5_HPD5_SEL 0x50 +#define ATOM_TRANSMITTER_CONFIG_V5_HPD6_SEL 0x60 + +#define DIG_TRANSMITTER_CONTROL_PS_ALLOCATION_V1_5 DIG_TRANSMITTER_CONTROL_PARAMETERS_V1_5 + + +/****************************************************************************/ +// Structures used by ExternalEncoderControlTable V1.3 +// ASIC Families: Evergreen, Llano, NI +// ucTableFormatRevision=1 +// ucTableContentRevision=3 +/****************************************************************************/ + +typedef struct _EXTERNAL_ENCODER_CONTROL_PARAMETERS_V3 +{ + union{ + USHORT usPixelClock; // pixel clock in 10Khz, valid when ucAction=SETUP/ENABLE_OUTPUT + USHORT usConnectorId; // connector id, valid when ucAction = INIT + }; + UCHAR ucConfig; // indicate which encoder, and DP link rate when ucAction = SETUP/ENABLE_OUTPUT + UCHAR ucAction; // + UCHAR ucEncoderMode; // encoder mode, only used when ucAction = SETUP/ENABLE_OUTPUT + UCHAR ucLaneNum; // lane number, only used when ucAction = SETUP/ENABLE_OUTPUT + UCHAR ucBitPerColor; // output bit per color, only valid when ucAction = SETUP/ENABLE_OUTPUT and ucEncodeMode= DP + UCHAR ucReserved; +}EXTERNAL_ENCODER_CONTROL_PARAMETERS_V3; + +// ucAction +#define EXTERNAL_ENCODER_ACTION_V3_DISABLE_OUTPUT 0x00 +#define EXTERNAL_ENCODER_ACTION_V3_ENABLE_OUTPUT 0x01 +#define EXTERNAL_ENCODER_ACTION_V3_ENCODER_INIT 0x07 +#define EXTERNAL_ENCODER_ACTION_V3_ENCODER_SETUP 0x0f +#define EXTERNAL_ENCODER_ACTION_V3_ENCODER_BLANKING_OFF 0x10 +#define EXTERNAL_ENCODER_ACTION_V3_ENCODER_BLANKING 0x11 +#define EXTERNAL_ENCODER_ACTION_V3_DACLOAD_DETECTION 0x12 +#define EXTERNAL_ENCODER_ACTION_V3_DDC_SETUP 0x14 + +// ucConfig +#define EXTERNAL_ENCODER_CONFIG_V3_DPLINKRATE_MASK 0x03 +#define EXTERNAL_ENCODER_CONFIG_V3_DPLINKRATE_1_62GHZ 0x00 +#define EXTERNAL_ENCODER_CONFIG_V3_DPLINKRATE_2_70GHZ 0x01 +#define EXTERNAL_ENCODER_CONFIG_V3_DPLINKRATE_5_40GHZ 0x02 +#define EXTERNAL_ENCODER_CONFIG_V3_ENCODER_SEL_MASK 0x70 +#define EXTERNAL_ENCODER_CONFIG_V3_ENCODER1 0x00 +#define EXTERNAL_ENCODER_CONFIG_V3_ENCODER2 0x10 +#define EXTERNAL_ENCODER_CONFIG_V3_ENCODER3 0x20 + +typedef struct _EXTERNAL_ENCODER_CONTROL_PS_ALLOCATION_V3 +{ + EXTERNAL_ENCODER_CONTROL_PARAMETERS_V3 sExtEncoder; + ULONG ulReserved[2]; +}EXTERNAL_ENCODER_CONTROL_PS_ALLOCATION_V3; + + +/****************************************************************************/ +// Structures used by DAC1OuputControlTable +// DAC2OuputControlTable +// LVTMAOutputControlTable (Before DEC30) +// TMDSAOutputControlTable (Before DEC30) +/****************************************************************************/ +typedef struct _DISPLAY_DEVICE_OUTPUT_CONTROL_PARAMETERS +{ + UCHAR ucAction; // Possible input:ATOM_ENABLE||ATOMDISABLE + // When the display is LCD, in addition to above: + // ATOM_LCD_BLOFF|| ATOM_LCD_BLON ||ATOM_LCD_BL_BRIGHTNESS_CONTROL||ATOM_LCD_SELFTEST_START|| + // ATOM_LCD_SELFTEST_STOP + + UCHAR aucPadding[3]; // padding to DWORD aligned +}DISPLAY_DEVICE_OUTPUT_CONTROL_PARAMETERS; + +#define DISPLAY_DEVICE_OUTPUT_CONTROL_PS_ALLOCATION DISPLAY_DEVICE_OUTPUT_CONTROL_PARAMETERS + + +#define CRT1_OUTPUT_CONTROL_PARAMETERS DISPLAY_DEVICE_OUTPUT_CONTROL_PARAMETERS +#define CRT1_OUTPUT_CONTROL_PS_ALLOCATION DISPLAY_DEVICE_OUTPUT_CONTROL_PS_ALLOCATION + +#define CRT2_OUTPUT_CONTROL_PARAMETERS DISPLAY_DEVICE_OUTPUT_CONTROL_PARAMETERS +#define CRT2_OUTPUT_CONTROL_PS_ALLOCATION DISPLAY_DEVICE_OUTPUT_CONTROL_PS_ALLOCATION + +#define CV1_OUTPUT_CONTROL_PARAMETERS DISPLAY_DEVICE_OUTPUT_CONTROL_PARAMETERS +#define CV1_OUTPUT_CONTROL_PS_ALLOCATION DISPLAY_DEVICE_OUTPUT_CONTROL_PS_ALLOCATION + +#define TV1_OUTPUT_CONTROL_PARAMETERS DISPLAY_DEVICE_OUTPUT_CONTROL_PARAMETERS +#define TV1_OUTPUT_CONTROL_PS_ALLOCATION DISPLAY_DEVICE_OUTPUT_CONTROL_PS_ALLOCATION + +#define DFP1_OUTPUT_CONTROL_PARAMETERS DISPLAY_DEVICE_OUTPUT_CONTROL_PARAMETERS +#define DFP1_OUTPUT_CONTROL_PS_ALLOCATION DISPLAY_DEVICE_OUTPUT_CONTROL_PS_ALLOCATION + +#define DFP2_OUTPUT_CONTROL_PARAMETERS DISPLAY_DEVICE_OUTPUT_CONTROL_PARAMETERS +#define DFP2_OUTPUT_CONTROL_PS_ALLOCATION DISPLAY_DEVICE_OUTPUT_CONTROL_PS_ALLOCATION + +#define LCD1_OUTPUT_CONTROL_PARAMETERS DISPLAY_DEVICE_OUTPUT_CONTROL_PARAMETERS +#define LCD1_OUTPUT_CONTROL_PS_ALLOCATION DISPLAY_DEVICE_OUTPUT_CONTROL_PS_ALLOCATION + +#define DVO_OUTPUT_CONTROL_PARAMETERS DISPLAY_DEVICE_OUTPUT_CONTROL_PARAMETERS +#define DVO_OUTPUT_CONTROL_PS_ALLOCATION DIG_TRANSMITTER_CONTROL_PS_ALLOCATION +#define DVO_OUTPUT_CONTROL_PARAMETERS_V3 DIG_TRANSMITTER_CONTROL_PARAMETERS + +/****************************************************************************/ +// Structures used by BlankCRTCTable +/****************************************************************************/ +typedef struct _BLANK_CRTC_PARAMETERS +{ + UCHAR ucCRTC; // ATOM_CRTC1 or ATOM_CRTC2 + UCHAR ucBlanking; // ATOM_BLANKING or ATOM_BLANKINGOFF + USHORT usBlackColorRCr; + USHORT usBlackColorGY; + USHORT usBlackColorBCb; +}BLANK_CRTC_PARAMETERS; +#define BLANK_CRTC_PS_ALLOCATION BLANK_CRTC_PARAMETERS + +/****************************************************************************/ +// Structures used by EnableCRTCTable +// EnableCRTCMemReqTable +// UpdateCRTC_DoubleBufferRegistersTable +/****************************************************************************/ +typedef struct _ENABLE_CRTC_PARAMETERS +{ + UCHAR ucCRTC; // ATOM_CRTC1 or ATOM_CRTC2 + UCHAR ucEnable; // ATOM_ENABLE or ATOM_DISABLE + UCHAR ucPadding[2]; +}ENABLE_CRTC_PARAMETERS; +#define ENABLE_CRTC_PS_ALLOCATION ENABLE_CRTC_PARAMETERS + +/****************************************************************************/ +// Structures used by SetCRTC_OverScanTable +/****************************************************************************/ +typedef struct _SET_CRTC_OVERSCAN_PARAMETERS +{ + USHORT usOverscanRight; // right + USHORT usOverscanLeft; // left + USHORT usOverscanBottom; // bottom + USHORT usOverscanTop; // top + UCHAR ucCRTC; // ATOM_CRTC1 or ATOM_CRTC2 + UCHAR ucPadding[3]; +}SET_CRTC_OVERSCAN_PARAMETERS; +#define SET_CRTC_OVERSCAN_PS_ALLOCATION SET_CRTC_OVERSCAN_PARAMETERS + +/****************************************************************************/ +// Structures used by SetCRTC_ReplicationTable +/****************************************************************************/ +typedef struct _SET_CRTC_REPLICATION_PARAMETERS +{ + UCHAR ucH_Replication; // horizontal replication + UCHAR ucV_Replication; // vertical replication + UCHAR usCRTC; // ATOM_CRTC1 or ATOM_CRTC2 + UCHAR ucPadding; +}SET_CRTC_REPLICATION_PARAMETERS; +#define SET_CRTC_REPLICATION_PS_ALLOCATION SET_CRTC_REPLICATION_PARAMETERS + +/****************************************************************************/ +// Structures used by SelectCRTC_SourceTable +/****************************************************************************/ +typedef struct _SELECT_CRTC_SOURCE_PARAMETERS +{ + UCHAR ucCRTC; // ATOM_CRTC1 or ATOM_CRTC2 + UCHAR ucDevice; // ATOM_DEVICE_CRT1|ATOM_DEVICE_CRT2|.... + UCHAR ucPadding[2]; +}SELECT_CRTC_SOURCE_PARAMETERS; +#define SELECT_CRTC_SOURCE_PS_ALLOCATION SELECT_CRTC_SOURCE_PARAMETERS + +typedef struct _SELECT_CRTC_SOURCE_PARAMETERS_V2 +{ + UCHAR ucCRTC; // ATOM_CRTC1 or ATOM_CRTC2 + UCHAR ucEncoderID; // DAC1/DAC2/TVOUT/DIG1/DIG2/DVO + UCHAR ucEncodeMode; // Encoding mode, only valid when using DIG1/DIG2/DVO + UCHAR ucPadding; +}SELECT_CRTC_SOURCE_PARAMETERS_V2; + +//ucEncoderID +//#define ASIC_INT_DAC1_ENCODER_ID 0x00 +//#define ASIC_INT_TV_ENCODER_ID 0x02 +//#define ASIC_INT_DIG1_ENCODER_ID 0x03 +//#define ASIC_INT_DAC2_ENCODER_ID 0x04 +//#define ASIC_EXT_TV_ENCODER_ID 0x06 +//#define ASIC_INT_DVO_ENCODER_ID 0x07 +//#define ASIC_INT_DIG2_ENCODER_ID 0x09 +//#define ASIC_EXT_DIG_ENCODER_ID 0x05 + +//ucEncodeMode +//#define ATOM_ENCODER_MODE_DP 0 +//#define ATOM_ENCODER_MODE_LVDS 1 +//#define ATOM_ENCODER_MODE_DVI 2 +//#define ATOM_ENCODER_MODE_HDMI 3 +//#define ATOM_ENCODER_MODE_SDVO 4 +//#define ATOM_ENCODER_MODE_TV 13 +//#define ATOM_ENCODER_MODE_CV 14 +//#define ATOM_ENCODER_MODE_CRT 15 + +/****************************************************************************/ +// Structures used by SetPixelClockTable +// GetPixelClockTable +/****************************************************************************/ +//Major revision=1., Minor revision=1 +typedef struct _PIXEL_CLOCK_PARAMETERS +{ + USHORT usPixelClock; // in 10kHz unit; for bios convenient = (RefClk*FB_Div)/(Ref_Div*Post_Div) + // 0 means disable PPLL + USHORT usRefDiv; // Reference divider + USHORT usFbDiv; // feedback divider + UCHAR ucPostDiv; // post divider + UCHAR ucFracFbDiv; // fractional feedback divider + UCHAR ucPpll; // ATOM_PPLL1 or ATOM_PPL2 + UCHAR ucRefDivSrc; // ATOM_PJITTER or ATO_NONPJITTER + UCHAR ucCRTC; // Which CRTC uses this Ppll + UCHAR ucPadding; +}PIXEL_CLOCK_PARAMETERS; + +//Major revision=1., Minor revision=2, add ucMiscIfno +//ucMiscInfo: +#define MISC_FORCE_REPROG_PIXEL_CLOCK 0x1 +#define MISC_DEVICE_INDEX_MASK 0xF0 +#define MISC_DEVICE_INDEX_SHIFT 4 + +typedef struct _PIXEL_CLOCK_PARAMETERS_V2 +{ + USHORT usPixelClock; // in 10kHz unit; for bios convenient = (RefClk*FB_Div)/(Ref_Div*Post_Div) + // 0 means disable PPLL + USHORT usRefDiv; // Reference divider + USHORT usFbDiv; // feedback divider + UCHAR ucPostDiv; // post divider + UCHAR ucFracFbDiv; // fractional feedback divider + UCHAR ucPpll; // ATOM_PPLL1 or ATOM_PPL2 + UCHAR ucRefDivSrc; // ATOM_PJITTER or ATO_NONPJITTER + UCHAR ucCRTC; // Which CRTC uses this Ppll + UCHAR ucMiscInfo; // Different bits for different purpose, bit [7:4] as device index, bit[0]=Force prog +}PIXEL_CLOCK_PARAMETERS_V2; + +//Major revision=1., Minor revision=3, structure/definition change +//ucEncoderMode: +//ATOM_ENCODER_MODE_DP +//ATOM_ENOCDER_MODE_LVDS +//ATOM_ENOCDER_MODE_DVI +//ATOM_ENOCDER_MODE_HDMI +//ATOM_ENOCDER_MODE_SDVO +//ATOM_ENCODER_MODE_TV 13 +//ATOM_ENCODER_MODE_CV 14 +//ATOM_ENCODER_MODE_CRT 15 + +//ucDVOConfig +//#define DVO_ENCODER_CONFIG_RATE_SEL 0x01 +//#define DVO_ENCODER_CONFIG_DDR_SPEED 0x00 +//#define DVO_ENCODER_CONFIG_SDR_SPEED 0x01 +//#define DVO_ENCODER_CONFIG_OUTPUT_SEL 0x0c +//#define DVO_ENCODER_CONFIG_LOW12BIT 0x00 +//#define DVO_ENCODER_CONFIG_UPPER12BIT 0x04 +//#define DVO_ENCODER_CONFIG_24BIT 0x08 + +//ucMiscInfo: also changed, see below +#define PIXEL_CLOCK_MISC_FORCE_PROG_PPLL 0x01 +#define PIXEL_CLOCK_MISC_VGA_MODE 0x02 +#define PIXEL_CLOCK_MISC_CRTC_SEL_MASK 0x04 +#define PIXEL_CLOCK_MISC_CRTC_SEL_CRTC1 0x00 +#define PIXEL_CLOCK_MISC_CRTC_SEL_CRTC2 0x04 +#define PIXEL_CLOCK_MISC_USE_ENGINE_FOR_DISPCLK 0x08 +#define PIXEL_CLOCK_MISC_REF_DIV_SRC 0x10 +// V1.4 for RoadRunner +#define PIXEL_CLOCK_V4_MISC_SS_ENABLE 0x10 +#define PIXEL_CLOCK_V4_MISC_COHERENT_MODE 0x20 + + +typedef struct _PIXEL_CLOCK_PARAMETERS_V3 +{ + USHORT usPixelClock; // in 10kHz unit; for bios convenient = (RefClk*FB_Div)/(Ref_Div*Post_Div) + // 0 means disable PPLL. For VGA PPLL,make sure this value is not 0. + USHORT usRefDiv; // Reference divider + USHORT usFbDiv; // feedback divider + UCHAR ucPostDiv; // post divider + UCHAR ucFracFbDiv; // fractional feedback divider + UCHAR ucPpll; // ATOM_PPLL1 or ATOM_PPL2 + UCHAR ucTransmitterId; // graphic encoder id defined in objectId.h + union + { + UCHAR ucEncoderMode; // encoder type defined as ATOM_ENCODER_MODE_DP/DVI/HDMI/ + UCHAR ucDVOConfig; // when use DVO, need to know SDR/DDR, 12bit or 24bit + }; + UCHAR ucMiscInfo; // bit[0]=Force program, bit[1]= set pclk for VGA, b[2]= CRTC sel + // bit[3]=0:use PPLL for dispclk source, =1: use engine clock for dispclock source + // bit[4]=0:use XTALIN as the source of reference divider,=1 use the pre-defined clock as the source of reference divider +}PIXEL_CLOCK_PARAMETERS_V3; + +#define PIXEL_CLOCK_PARAMETERS_LAST PIXEL_CLOCK_PARAMETERS_V2 +#define GET_PIXEL_CLOCK_PS_ALLOCATION PIXEL_CLOCK_PARAMETERS_LAST + +typedef struct _PIXEL_CLOCK_PARAMETERS_V5 +{ + UCHAR ucCRTC; // ATOM_CRTC1~6, indicate the CRTC controller to + // drive the pixel clock. not used for DCPLL case. + union{ + UCHAR ucReserved; + UCHAR ucFracFbDiv; // [gphan] temporary to prevent build problem. remove it after driver code is changed. + }; + USHORT usPixelClock; // target the pixel clock to drive the CRTC timing + // 0 means disable PPLL/DCPLL. + USHORT usFbDiv; // feedback divider integer part. + UCHAR ucPostDiv; // post divider. + UCHAR ucRefDiv; // Reference divider + UCHAR ucPpll; // ATOM_PPLL1/ATOM_PPLL2/ATOM_DCPLL + UCHAR ucTransmitterID; // ASIC encoder id defined in objectId.h, + // indicate which graphic encoder will be used. + UCHAR ucEncoderMode; // Encoder mode: + UCHAR ucMiscInfo; // bit[0]= Force program PPLL + // bit[1]= when VGA timing is used. + // bit[3:2]= HDMI panel bit depth: =0: 24bpp =1:30bpp, =2:32bpp + // bit[4]= RefClock source for PPLL. + // =0: XTLAIN( default mode ) + // =1: other external clock source, which is pre-defined + // by VBIOS depend on the feature required. + // bit[7:5]: reserved. + ULONG ulFbDivDecFrac; // 20 bit feedback divider decimal fraction part, range from 1~999999 ( 0.000001 to 0.999999 ) + +}PIXEL_CLOCK_PARAMETERS_V5; + +#define PIXEL_CLOCK_V5_MISC_FORCE_PROG_PPLL 0x01 +#define PIXEL_CLOCK_V5_MISC_VGA_MODE 0x02 +#define PIXEL_CLOCK_V5_MISC_HDMI_BPP_MASK 0x0c +#define PIXEL_CLOCK_V5_MISC_HDMI_24BPP 0x00 +#define PIXEL_CLOCK_V5_MISC_HDMI_30BPP 0x04 +#define PIXEL_CLOCK_V5_MISC_HDMI_32BPP 0x08 +#define PIXEL_CLOCK_V5_MISC_REF_DIV_SRC 0x10 + +typedef struct _CRTC_PIXEL_CLOCK_FREQ +{ +#if ATOM_BIG_ENDIAN + ULONG ucCRTC:8; // ATOM_CRTC1~6, indicate the CRTC controller to + // drive the pixel clock. not used for DCPLL case. + ULONG ulPixelClock:24; // target the pixel clock to drive the CRTC timing. + // 0 means disable PPLL/DCPLL. Expanded to 24 bits comparing to previous version. +#else + ULONG ulPixelClock:24; // target the pixel clock to drive the CRTC timing. + // 0 means disable PPLL/DCPLL. Expanded to 24 bits comparing to previous version. + ULONG ucCRTC:8; // ATOM_CRTC1~6, indicate the CRTC controller to + // drive the pixel clock. not used for DCPLL case. +#endif +}CRTC_PIXEL_CLOCK_FREQ; + +typedef struct _PIXEL_CLOCK_PARAMETERS_V6 +{ + union{ + CRTC_PIXEL_CLOCK_FREQ ulCrtcPclkFreq; // pixel clock and CRTC id frequency + ULONG ulDispEngClkFreq; // dispclk frequency + }; + USHORT usFbDiv; // feedback divider integer part. + UCHAR ucPostDiv; // post divider. + UCHAR ucRefDiv; // Reference divider + UCHAR ucPpll; // ATOM_PPLL1/ATOM_PPLL2/ATOM_DCPLL + UCHAR ucTransmitterID; // ASIC encoder id defined in objectId.h, + // indicate which graphic encoder will be used. + UCHAR ucEncoderMode; // Encoder mode: + UCHAR ucMiscInfo; // bit[0]= Force program PPLL + // bit[1]= when VGA timing is used. + // bit[3:2]= HDMI panel bit depth: =0: 24bpp =1:30bpp, =2:32bpp + // bit[4]= RefClock source for PPLL. + // =0: XTLAIN( default mode ) + // =1: other external clock source, which is pre-defined + // by VBIOS depend on the feature required. + // bit[7:5]: reserved. + ULONG ulFbDivDecFrac; // 20 bit feedback divider decimal fraction part, range from 1~999999 ( 0.000001 to 0.999999 ) + +}PIXEL_CLOCK_PARAMETERS_V6; + +#define PIXEL_CLOCK_V6_MISC_FORCE_PROG_PPLL 0x01 +#define PIXEL_CLOCK_V6_MISC_VGA_MODE 0x02 +#define PIXEL_CLOCK_V6_MISC_HDMI_BPP_MASK 0x0c +#define PIXEL_CLOCK_V6_MISC_HDMI_24BPP 0x00 +#define PIXEL_CLOCK_V6_MISC_HDMI_36BPP 0x04 +#define PIXEL_CLOCK_V6_MISC_HDMI_30BPP 0x08 +#define PIXEL_CLOCK_V6_MISC_HDMI_48BPP 0x0c +#define PIXEL_CLOCK_V6_MISC_REF_DIV_SRC 0x10 + +typedef struct _GET_DISP_PLL_STATUS_INPUT_PARAMETERS_V2 +{ + PIXEL_CLOCK_PARAMETERS_V3 sDispClkInput; +}GET_DISP_PLL_STATUS_INPUT_PARAMETERS_V2; + +typedef struct _GET_DISP_PLL_STATUS_OUTPUT_PARAMETERS_V2 +{ + UCHAR ucStatus; + UCHAR ucRefDivSrc; // =1: reference clock source from XTALIN, =0: source from PCIE ref clock + UCHAR ucReserved[2]; +}GET_DISP_PLL_STATUS_OUTPUT_PARAMETERS_V2; + +typedef struct _GET_DISP_PLL_STATUS_INPUT_PARAMETERS_V3 +{ + PIXEL_CLOCK_PARAMETERS_V5 sDispClkInput; +}GET_DISP_PLL_STATUS_INPUT_PARAMETERS_V3; + +/****************************************************************************/ +// Structures used by AdjustDisplayPllTable +/****************************************************************************/ +typedef struct _ADJUST_DISPLAY_PLL_PARAMETERS +{ + USHORT usPixelClock; + UCHAR ucTransmitterID; + UCHAR ucEncodeMode; + union + { + UCHAR ucDVOConfig; //if DVO, need passing link rate and output 12bitlow or 24bit + UCHAR ucConfig; //if none DVO, not defined yet + }; + UCHAR ucReserved[3]; +}ADJUST_DISPLAY_PLL_PARAMETERS; + +#define ADJUST_DISPLAY_CONFIG_SS_ENABLE 0x10 +#define ADJUST_DISPLAY_PLL_PS_ALLOCATION ADJUST_DISPLAY_PLL_PARAMETERS + +typedef struct _ADJUST_DISPLAY_PLL_INPUT_PARAMETERS_V3 +{ + USHORT usPixelClock; // target pixel clock + UCHAR ucTransmitterID; // GPU transmitter id defined in objectid.h + UCHAR ucEncodeMode; // encoder mode: CRT, LVDS, DP, TMDS or HDMI + UCHAR ucDispPllConfig; // display pll configure parameter defined as following DISPPLL_CONFIG_XXXX + UCHAR ucExtTransmitterID; // external encoder id. + UCHAR ucReserved[2]; +}ADJUST_DISPLAY_PLL_INPUT_PARAMETERS_V3; + +// usDispPllConfig v1.2 for RoadRunner +#define DISPPLL_CONFIG_DVO_RATE_SEL 0x0001 // need only when ucTransmitterID = DVO +#define DISPPLL_CONFIG_DVO_DDR_SPEED 0x0000 // need only when ucTransmitterID = DVO +#define DISPPLL_CONFIG_DVO_SDR_SPEED 0x0001 // need only when ucTransmitterID = DVO +#define DISPPLL_CONFIG_DVO_OUTPUT_SEL 0x000c // need only when ucTransmitterID = DVO +#define DISPPLL_CONFIG_DVO_LOW12BIT 0x0000 // need only when ucTransmitterID = DVO +#define DISPPLL_CONFIG_DVO_UPPER12BIT 0x0004 // need only when ucTransmitterID = DVO +#define DISPPLL_CONFIG_DVO_24BIT 0x0008 // need only when ucTransmitterID = DVO +#define DISPPLL_CONFIG_SS_ENABLE 0x0010 // Only used when ucEncoderMode = DP or LVDS +#define DISPPLL_CONFIG_COHERENT_MODE 0x0020 // Only used when ucEncoderMode = TMDS or HDMI +#define DISPPLL_CONFIG_DUAL_LINK 0x0040 // Only used when ucEncoderMode = TMDS or LVDS + + +typedef struct _ADJUST_DISPLAY_PLL_OUTPUT_PARAMETERS_V3 +{ + ULONG ulDispPllFreq; // return display PPLL freq which is used to generate the pixclock, and related idclk, symclk etc + UCHAR ucRefDiv; // if it is none-zero, it is used to be calculated the other ppll parameter fb_divider and post_div ( if it is not given ) + UCHAR ucPostDiv; // if it is none-zero, it is used to be calculated the other ppll parameter fb_divider + UCHAR ucReserved[2]; +}ADJUST_DISPLAY_PLL_OUTPUT_PARAMETERS_V3; + +typedef struct _ADJUST_DISPLAY_PLL_PS_ALLOCATION_V3 +{ + union + { + ADJUST_DISPLAY_PLL_INPUT_PARAMETERS_V3 sInput; + ADJUST_DISPLAY_PLL_OUTPUT_PARAMETERS_V3 sOutput; + }; +} ADJUST_DISPLAY_PLL_PS_ALLOCATION_V3; + +/****************************************************************************/ +// Structures used by EnableYUVTable +/****************************************************************************/ +typedef struct _ENABLE_YUV_PARAMETERS +{ + UCHAR ucEnable; // ATOM_ENABLE:Enable YUV or ATOM_DISABLE:Disable YUV (RGB) + UCHAR ucCRTC; // Which CRTC needs this YUV or RGB format + UCHAR ucPadding[2]; +}ENABLE_YUV_PARAMETERS; +#define ENABLE_YUV_PS_ALLOCATION ENABLE_YUV_PARAMETERS + +/****************************************************************************/ +// Structures used by GetMemoryClockTable +/****************************************************************************/ +typedef struct _GET_MEMORY_CLOCK_PARAMETERS +{ + ULONG ulReturnMemoryClock; // current memory speed in 10KHz unit +} GET_MEMORY_CLOCK_PARAMETERS; +#define GET_MEMORY_CLOCK_PS_ALLOCATION GET_MEMORY_CLOCK_PARAMETERS + +/****************************************************************************/ +// Structures used by GetEngineClockTable +/****************************************************************************/ +typedef struct _GET_ENGINE_CLOCK_PARAMETERS +{ + ULONG ulReturnEngineClock; // current engine speed in 10KHz unit +} GET_ENGINE_CLOCK_PARAMETERS; +#define GET_ENGINE_CLOCK_PS_ALLOCATION GET_ENGINE_CLOCK_PARAMETERS + +/****************************************************************************/ +// Following Structures and constant may be obsolete +/****************************************************************************/ +//Maxium 8 bytes,the data read in will be placed in the parameter space. +//Read operaion successeful when the paramter space is non-zero, otherwise read operation failed +typedef struct _READ_EDID_FROM_HW_I2C_DATA_PARAMETERS +{ + USHORT usPrescale; //Ratio between Engine clock and I2C clock + USHORT usVRAMAddress; //Address in Frame Buffer where to pace raw EDID + USHORT usStatus; //When use output: lower byte EDID checksum, high byte hardware status + //WHen use input: lower byte as 'byte to read':currently limited to 128byte or 1byte + UCHAR ucSlaveAddr; //Read from which slave + UCHAR ucLineNumber; //Read from which HW assisted line +}READ_EDID_FROM_HW_I2C_DATA_PARAMETERS; +#define READ_EDID_FROM_HW_I2C_DATA_PS_ALLOCATION READ_EDID_FROM_HW_I2C_DATA_PARAMETERS + + +#define ATOM_WRITE_I2C_FORMAT_PSOFFSET_PSDATABYTE 0 +#define ATOM_WRITE_I2C_FORMAT_PSOFFSET_PSTWODATABYTES 1 +#define ATOM_WRITE_I2C_FORMAT_PSCOUNTER_PSOFFSET_IDDATABLOCK 2 +#define ATOM_WRITE_I2C_FORMAT_PSCOUNTER_IDOFFSET_PLUS_IDDATABLOCK 3 +#define ATOM_WRITE_I2C_FORMAT_IDCOUNTER_IDOFFSET_IDDATABLOCK 4 + +typedef struct _WRITE_ONE_BYTE_HW_I2C_DATA_PARAMETERS +{ + USHORT usPrescale; //Ratio between Engine clock and I2C clock + USHORT usByteOffset; //Write to which byte + //Upper portion of usByteOffset is Format of data + //1bytePS+offsetPS + //2bytesPS+offsetPS + //blockID+offsetPS + //blockID+offsetID + //blockID+counterID+offsetID + UCHAR ucData; //PS data1 + UCHAR ucStatus; //Status byte 1=success, 2=failure, Also is used as PS data2 + UCHAR ucSlaveAddr; //Write to which slave + UCHAR ucLineNumber; //Write from which HW assisted line +}WRITE_ONE_BYTE_HW_I2C_DATA_PARAMETERS; + +#define WRITE_ONE_BYTE_HW_I2C_DATA_PS_ALLOCATION WRITE_ONE_BYTE_HW_I2C_DATA_PARAMETERS + +typedef struct _SET_UP_HW_I2C_DATA_PARAMETERS +{ + USHORT usPrescale; //Ratio between Engine clock and I2C clock + UCHAR ucSlaveAddr; //Write to which slave + UCHAR ucLineNumber; //Write from which HW assisted line +}SET_UP_HW_I2C_DATA_PARAMETERS; + + +/**************************************************************************/ +#define SPEED_FAN_CONTROL_PS_ALLOCATION WRITE_ONE_BYTE_HW_I2C_DATA_PARAMETERS + + +/****************************************************************************/ +// Structures used by PowerConnectorDetectionTable +/****************************************************************************/ +typedef struct _POWER_CONNECTOR_DETECTION_PARAMETERS +{ + UCHAR ucPowerConnectorStatus; //Used for return value 0: detected, 1:not detected + UCHAR ucPwrBehaviorId; + USHORT usPwrBudget; //how much power currently boot to in unit of watt +}POWER_CONNECTOR_DETECTION_PARAMETERS; + +typedef struct POWER_CONNECTOR_DETECTION_PS_ALLOCATION +{ + UCHAR ucPowerConnectorStatus; //Used for return value 0: detected, 1:not detected + UCHAR ucReserved; + USHORT usPwrBudget; //how much power currently boot to in unit of watt + WRITE_ONE_BYTE_HW_I2C_DATA_PS_ALLOCATION sReserved; +}POWER_CONNECTOR_DETECTION_PS_ALLOCATION; + +/****************************LVDS SS Command Table Definitions**********************/ + +/****************************************************************************/ +// Structures used by EnableSpreadSpectrumOnPPLLTable +/****************************************************************************/ +typedef struct _ENABLE_LVDS_SS_PARAMETERS +{ + USHORT usSpreadSpectrumPercentage; + UCHAR ucSpreadSpectrumType; //Bit1=0 Down Spread,=1 Center Spread. Bit1=1 Ext. =0 Int. Others:TBD + UCHAR ucSpreadSpectrumStepSize_Delay; //bits3:2 SS_STEP_SIZE; bit 6:4 SS_DELAY + UCHAR ucEnable; //ATOM_ENABLE or ATOM_DISABLE + UCHAR ucPadding[3]; +}ENABLE_LVDS_SS_PARAMETERS; + +//ucTableFormatRevision=1,ucTableContentRevision=2 +typedef struct _ENABLE_LVDS_SS_PARAMETERS_V2 +{ + USHORT usSpreadSpectrumPercentage; + UCHAR ucSpreadSpectrumType; //Bit1=0 Down Spread,=1 Center Spread. Bit1=1 Ext. =0 Int. Others:TBD + UCHAR ucSpreadSpectrumStep; // + UCHAR ucEnable; //ATOM_ENABLE or ATOM_DISABLE + UCHAR ucSpreadSpectrumDelay; + UCHAR ucSpreadSpectrumRange; + UCHAR ucPadding; +}ENABLE_LVDS_SS_PARAMETERS_V2; + +//This new structure is based on ENABLE_LVDS_SS_PARAMETERS but expands to SS on PPLL, so other devices can use SS. +typedef struct _ENABLE_SPREAD_SPECTRUM_ON_PPLL +{ + USHORT usSpreadSpectrumPercentage; + UCHAR ucSpreadSpectrumType; // Bit1=0 Down Spread,=1 Center Spread. Bit1=1 Ext. =0 Int. Others:TBD + UCHAR ucSpreadSpectrumStep; // + UCHAR ucEnable; // ATOM_ENABLE or ATOM_DISABLE + UCHAR ucSpreadSpectrumDelay; + UCHAR ucSpreadSpectrumRange; + UCHAR ucPpll; // ATOM_PPLL1/ATOM_PPLL2 +}ENABLE_SPREAD_SPECTRUM_ON_PPLL; + +typedef struct _ENABLE_SPREAD_SPECTRUM_ON_PPLL_V2 +{ + USHORT usSpreadSpectrumPercentage; + UCHAR ucSpreadSpectrumType; // Bit[0]: 0-Down Spread,1-Center Spread. + // Bit[1]: 1-Ext. 0-Int. + // Bit[3:2]: =0 P1PLL =1 P2PLL =2 DCPLL + // Bits[7:4] reserved + UCHAR ucEnable; // ATOM_ENABLE or ATOM_DISABLE + USHORT usSpreadSpectrumAmount; // Includes SS_AMOUNT_FBDIV[7:0] and SS_AMOUNT_NFRAC_SLIP[11:8] + USHORT usSpreadSpectrumStep; // SS_STEP_SIZE_DSFRAC +}ENABLE_SPREAD_SPECTRUM_ON_PPLL_V2; + +#define ATOM_PPLL_SS_TYPE_V2_DOWN_SPREAD 0x00 +#define ATOM_PPLL_SS_TYPE_V2_CENTRE_SPREAD 0x01 +#define ATOM_PPLL_SS_TYPE_V2_EXT_SPREAD 0x02 +#define ATOM_PPLL_SS_TYPE_V2_PPLL_SEL_MASK 0x0c +#define ATOM_PPLL_SS_TYPE_V2_P1PLL 0x00 +#define ATOM_PPLL_SS_TYPE_V2_P2PLL 0x04 +#define ATOM_PPLL_SS_TYPE_V2_DCPLL 0x08 +#define ATOM_PPLL_SS_AMOUNT_V2_FBDIV_MASK 0x00FF +#define ATOM_PPLL_SS_AMOUNT_V2_FBDIV_SHIFT 0 +#define ATOM_PPLL_SS_AMOUNT_V2_NFRAC_MASK 0x0F00 +#define ATOM_PPLL_SS_AMOUNT_V2_NFRAC_SHIFT 8 + +// Used by DCE5.0 + typedef struct _ENABLE_SPREAD_SPECTRUM_ON_PPLL_V3 +{ + USHORT usSpreadSpectrumAmountFrac; // SS_AMOUNT_DSFRAC New in DCE5.0 + UCHAR ucSpreadSpectrumType; // Bit[0]: 0-Down Spread,1-Center Spread. + // Bit[1]: 1-Ext. 0-Int. + // Bit[3:2]: =0 P1PLL =1 P2PLL =2 DCPLL + // Bits[7:4] reserved + UCHAR ucEnable; // ATOM_ENABLE or ATOM_DISABLE + USHORT usSpreadSpectrumAmount; // Includes SS_AMOUNT_FBDIV[7:0] and SS_AMOUNT_NFRAC_SLIP[11:8] + USHORT usSpreadSpectrumStep; // SS_STEP_SIZE_DSFRAC +}ENABLE_SPREAD_SPECTRUM_ON_PPLL_V3; + +#define ATOM_PPLL_SS_TYPE_V3_DOWN_SPREAD 0x00 +#define ATOM_PPLL_SS_TYPE_V3_CENTRE_SPREAD 0x01 +#define ATOM_PPLL_SS_TYPE_V3_EXT_SPREAD 0x02 +#define ATOM_PPLL_SS_TYPE_V3_PPLL_SEL_MASK 0x0c +#define ATOM_PPLL_SS_TYPE_V3_P1PLL 0x00 +#define ATOM_PPLL_SS_TYPE_V3_P2PLL 0x04 +#define ATOM_PPLL_SS_TYPE_V3_DCPLL 0x08 +#define ATOM_PPLL_SS_TYPE_V3_P0PLL ATOM_PPLL_SS_TYPE_V3_DCPLL +#define ATOM_PPLL_SS_AMOUNT_V3_FBDIV_MASK 0x00FF +#define ATOM_PPLL_SS_AMOUNT_V3_FBDIV_SHIFT 0 +#define ATOM_PPLL_SS_AMOUNT_V3_NFRAC_MASK 0x0F00 +#define ATOM_PPLL_SS_AMOUNT_V3_NFRAC_SHIFT 8 + +#define ENABLE_SPREAD_SPECTRUM_ON_PPLL_PS_ALLOCATION ENABLE_SPREAD_SPECTRUM_ON_PPLL + +/**************************************************************************/ + +typedef struct _SET_PIXEL_CLOCK_PS_ALLOCATION +{ + PIXEL_CLOCK_PARAMETERS sPCLKInput; + ENABLE_SPREAD_SPECTRUM_ON_PPLL sReserved;//Caller doesn't need to init this portion +}SET_PIXEL_CLOCK_PS_ALLOCATION; + +#define ENABLE_VGA_RENDER_PS_ALLOCATION SET_PIXEL_CLOCK_PS_ALLOCATION + +/****************************************************************************/ +// Structures used by ### +/****************************************************************************/ +typedef struct _MEMORY_TRAINING_PARAMETERS +{ + ULONG ulTargetMemoryClock; //In 10Khz unit +}MEMORY_TRAINING_PARAMETERS; +#define MEMORY_TRAINING_PS_ALLOCATION MEMORY_TRAINING_PARAMETERS + + +/****************************LVDS and other encoder command table definitions **********************/ + + +/****************************************************************************/ +// Structures used by LVDSEncoderControlTable (Before DCE30) +// LVTMAEncoderControlTable (Before DCE30) +// TMDSAEncoderControlTable (Before DCE30) +/****************************************************************************/ +typedef struct _LVDS_ENCODER_CONTROL_PARAMETERS +{ + USHORT usPixelClock; // in 10KHz; for bios convenient + UCHAR ucMisc; // bit0=0: Enable single link + // =1: Enable dual link + // Bit1=0: 666RGB + // =1: 888RGB + UCHAR ucAction; // 0: turn off encoder + // 1: setup and turn on encoder +}LVDS_ENCODER_CONTROL_PARAMETERS; + +#define LVDS_ENCODER_CONTROL_PS_ALLOCATION LVDS_ENCODER_CONTROL_PARAMETERS + +#define TMDS1_ENCODER_CONTROL_PARAMETERS LVDS_ENCODER_CONTROL_PARAMETERS +#define TMDS1_ENCODER_CONTROL_PS_ALLOCATION TMDS1_ENCODER_CONTROL_PARAMETERS + +#define TMDS2_ENCODER_CONTROL_PARAMETERS TMDS1_ENCODER_CONTROL_PARAMETERS +#define TMDS2_ENCODER_CONTROL_PS_ALLOCATION TMDS2_ENCODER_CONTROL_PARAMETERS + + +//ucTableFormatRevision=1,ucTableContentRevision=2 +typedef struct _LVDS_ENCODER_CONTROL_PARAMETERS_V2 +{ + USHORT usPixelClock; // in 10KHz; for bios convenient + UCHAR ucMisc; // see PANEL_ENCODER_MISC_xx defintions below + UCHAR ucAction; // 0: turn off encoder + // 1: setup and turn on encoder + UCHAR ucTruncate; // bit0=0: Disable truncate + // =1: Enable truncate + // bit4=0: 666RGB + // =1: 888RGB + UCHAR ucSpatial; // bit0=0: Disable spatial dithering + // =1: Enable spatial dithering + // bit4=0: 666RGB + // =1: 888RGB + UCHAR ucTemporal; // bit0=0: Disable temporal dithering + // =1: Enable temporal dithering + // bit4=0: 666RGB + // =1: 888RGB + // bit5=0: Gray level 2 + // =1: Gray level 4 + UCHAR ucFRC; // bit4=0: 25FRC_SEL pattern E + // =1: 25FRC_SEL pattern F + // bit6:5=0: 50FRC_SEL pattern A + // =1: 50FRC_SEL pattern B + // =2: 50FRC_SEL pattern C + // =3: 50FRC_SEL pattern D + // bit7=0: 75FRC_SEL pattern E + // =1: 75FRC_SEL pattern F +}LVDS_ENCODER_CONTROL_PARAMETERS_V2; + +#define LVDS_ENCODER_CONTROL_PS_ALLOCATION_V2 LVDS_ENCODER_CONTROL_PARAMETERS_V2 + +#define TMDS1_ENCODER_CONTROL_PARAMETERS_V2 LVDS_ENCODER_CONTROL_PARAMETERS_V2 +#define TMDS1_ENCODER_CONTROL_PS_ALLOCATION_V2 TMDS1_ENCODER_CONTROL_PARAMETERS_V2 + +#define TMDS2_ENCODER_CONTROL_PARAMETERS_V2 TMDS1_ENCODER_CONTROL_PARAMETERS_V2 +#define TMDS2_ENCODER_CONTROL_PS_ALLOCATION_V2 TMDS2_ENCODER_CONTROL_PARAMETERS_V2 + +#define LVDS_ENCODER_CONTROL_PARAMETERS_V3 LVDS_ENCODER_CONTROL_PARAMETERS_V2 +#define LVDS_ENCODER_CONTROL_PS_ALLOCATION_V3 LVDS_ENCODER_CONTROL_PARAMETERS_V3 + +#define TMDS1_ENCODER_CONTROL_PARAMETERS_V3 LVDS_ENCODER_CONTROL_PARAMETERS_V3 +#define TMDS1_ENCODER_CONTROL_PS_ALLOCATION_V3 TMDS1_ENCODER_CONTROL_PARAMETERS_V3 + +#define TMDS2_ENCODER_CONTROL_PARAMETERS_V3 LVDS_ENCODER_CONTROL_PARAMETERS_V3 +#define TMDS2_ENCODER_CONTROL_PS_ALLOCATION_V3 TMDS2_ENCODER_CONTROL_PARAMETERS_V3 + +/****************************************************************************/ +// Structures used by ### +/****************************************************************************/ +typedef struct _ENABLE_EXTERNAL_TMDS_ENCODER_PARAMETERS +{ + UCHAR ucEnable; // Enable or Disable External TMDS encoder + UCHAR ucMisc; // Bit0=0:Enable Single link;=1:Enable Dual link;Bit1 {=0:666RGB, =1:888RGB} + UCHAR ucPadding[2]; +}ENABLE_EXTERNAL_TMDS_ENCODER_PARAMETERS; + +typedef struct _ENABLE_EXTERNAL_TMDS_ENCODER_PS_ALLOCATION +{ + ENABLE_EXTERNAL_TMDS_ENCODER_PARAMETERS sXTmdsEncoder; + WRITE_ONE_BYTE_HW_I2C_DATA_PS_ALLOCATION sReserved; //Caller doesn't need to init this portion +}ENABLE_EXTERNAL_TMDS_ENCODER_PS_ALLOCATION; + +#define ENABLE_EXTERNAL_TMDS_ENCODER_PARAMETERS_V2 LVDS_ENCODER_CONTROL_PARAMETERS_V2 + +typedef struct _ENABLE_EXTERNAL_TMDS_ENCODER_PS_ALLOCATION_V2 +{ + ENABLE_EXTERNAL_TMDS_ENCODER_PARAMETERS_V2 sXTmdsEncoder; + WRITE_ONE_BYTE_HW_I2C_DATA_PS_ALLOCATION sReserved; //Caller doesn't need to init this portion +}ENABLE_EXTERNAL_TMDS_ENCODER_PS_ALLOCATION_V2; + +typedef struct _EXTERNAL_ENCODER_CONTROL_PS_ALLOCATION +{ + DIG_ENCODER_CONTROL_PARAMETERS sDigEncoder; + WRITE_ONE_BYTE_HW_I2C_DATA_PS_ALLOCATION sReserved; +}EXTERNAL_ENCODER_CONTROL_PS_ALLOCATION; + +/****************************************************************************/ +// Structures used by DVOEncoderControlTable +/****************************************************************************/ +//ucTableFormatRevision=1,ucTableContentRevision=3 + +//ucDVOConfig: +#define DVO_ENCODER_CONFIG_RATE_SEL 0x01 +#define DVO_ENCODER_CONFIG_DDR_SPEED 0x00 +#define DVO_ENCODER_CONFIG_SDR_SPEED 0x01 +#define DVO_ENCODER_CONFIG_OUTPUT_SEL 0x0c +#define DVO_ENCODER_CONFIG_LOW12BIT 0x00 +#define DVO_ENCODER_CONFIG_UPPER12BIT 0x04 +#define DVO_ENCODER_CONFIG_24BIT 0x08 + +typedef struct _DVO_ENCODER_CONTROL_PARAMETERS_V3 +{ + USHORT usPixelClock; + UCHAR ucDVOConfig; + UCHAR ucAction; //ATOM_ENABLE/ATOM_DISABLE/ATOM_HPD_INIT + UCHAR ucReseved[4]; +}DVO_ENCODER_CONTROL_PARAMETERS_V3; +#define DVO_ENCODER_CONTROL_PS_ALLOCATION_V3 DVO_ENCODER_CONTROL_PARAMETERS_V3 + +//ucTableFormatRevision=1 +//ucTableContentRevision=3 structure is not changed but usMisc add bit 1 as another input for +// bit1=0: non-coherent mode +// =1: coherent mode + +//========================================================================================== +//Only change is here next time when changing encoder parameter definitions again! +#define LVDS_ENCODER_CONTROL_PARAMETERS_LAST LVDS_ENCODER_CONTROL_PARAMETERS_V3 +#define LVDS_ENCODER_CONTROL_PS_ALLOCATION_LAST LVDS_ENCODER_CONTROL_PARAMETERS_LAST + +#define TMDS1_ENCODER_CONTROL_PARAMETERS_LAST LVDS_ENCODER_CONTROL_PARAMETERS_V3 +#define TMDS1_ENCODER_CONTROL_PS_ALLOCATION_LAST TMDS1_ENCODER_CONTROL_PARAMETERS_LAST + +#define TMDS2_ENCODER_CONTROL_PARAMETERS_LAST LVDS_ENCODER_CONTROL_PARAMETERS_V3 +#define TMDS2_ENCODER_CONTROL_PS_ALLOCATION_LAST TMDS2_ENCODER_CONTROL_PARAMETERS_LAST + +#define DVO_ENCODER_CONTROL_PARAMETERS_LAST DVO_ENCODER_CONTROL_PARAMETERS +#define DVO_ENCODER_CONTROL_PS_ALLOCATION_LAST DVO_ENCODER_CONTROL_PS_ALLOCATION + +//========================================================================================== +#define PANEL_ENCODER_MISC_DUAL 0x01 +#define PANEL_ENCODER_MISC_COHERENT 0x02 +#define PANEL_ENCODER_MISC_TMDS_LINKB 0x04 +#define PANEL_ENCODER_MISC_HDMI_TYPE 0x08 + +#define PANEL_ENCODER_ACTION_DISABLE ATOM_DISABLE +#define PANEL_ENCODER_ACTION_ENABLE ATOM_ENABLE +#define PANEL_ENCODER_ACTION_COHERENTSEQ (ATOM_ENABLE+1) + +#define PANEL_ENCODER_TRUNCATE_EN 0x01 +#define PANEL_ENCODER_TRUNCATE_DEPTH 0x10 +#define PANEL_ENCODER_SPATIAL_DITHER_EN 0x01 +#define PANEL_ENCODER_SPATIAL_DITHER_DEPTH 0x10 +#define PANEL_ENCODER_TEMPORAL_DITHER_EN 0x01 +#define PANEL_ENCODER_TEMPORAL_DITHER_DEPTH 0x10 +#define PANEL_ENCODER_TEMPORAL_LEVEL_4 0x20 +#define PANEL_ENCODER_25FRC_MASK 0x10 +#define PANEL_ENCODER_25FRC_E 0x00 +#define PANEL_ENCODER_25FRC_F 0x10 +#define PANEL_ENCODER_50FRC_MASK 0x60 +#define PANEL_ENCODER_50FRC_A 0x00 +#define PANEL_ENCODER_50FRC_B 0x20 +#define PANEL_ENCODER_50FRC_C 0x40 +#define PANEL_ENCODER_50FRC_D 0x60 +#define PANEL_ENCODER_75FRC_MASK 0x80 +#define PANEL_ENCODER_75FRC_E 0x00 +#define PANEL_ENCODER_75FRC_F 0x80 + +/****************************************************************************/ +// Structures used by SetVoltageTable +/****************************************************************************/ +#define SET_VOLTAGE_TYPE_ASIC_VDDC 1 +#define SET_VOLTAGE_TYPE_ASIC_MVDDC 2 +#define SET_VOLTAGE_TYPE_ASIC_MVDDQ 3 +#define SET_VOLTAGE_TYPE_ASIC_VDDCI 4 +#define SET_VOLTAGE_INIT_MODE 5 +#define SET_VOLTAGE_GET_MAX_VOLTAGE 6 //Gets the Max. voltage for the soldered Asic + +#define SET_ASIC_VOLTAGE_MODE_ALL_SOURCE 0x1 +#define SET_ASIC_VOLTAGE_MODE_SOURCE_A 0x2 +#define SET_ASIC_VOLTAGE_MODE_SOURCE_B 0x4 + +#define SET_ASIC_VOLTAGE_MODE_SET_VOLTAGE 0x0 +#define SET_ASIC_VOLTAGE_MODE_GET_GPIOVAL 0x1 +#define SET_ASIC_VOLTAGE_MODE_GET_GPIOMASK 0x2 + +typedef struct _SET_VOLTAGE_PARAMETERS +{ + UCHAR ucVoltageType; // To tell which voltage to set up, VDDC/MVDDC/MVDDQ + UCHAR ucVoltageMode; // To set all, to set source A or source B or ... + UCHAR ucVoltageIndex; // An index to tell which voltage level + UCHAR ucReserved; +}SET_VOLTAGE_PARAMETERS; + +typedef struct _SET_VOLTAGE_PARAMETERS_V2 +{ + UCHAR ucVoltageType; // To tell which voltage to set up, VDDC/MVDDC/MVDDQ + UCHAR ucVoltageMode; // Not used, maybe use for state machine for differen power mode + USHORT usVoltageLevel; // real voltage level +}SET_VOLTAGE_PARAMETERS_V2; + + +typedef struct _SET_VOLTAGE_PARAMETERS_V1_3 +{ + UCHAR ucVoltageType; // To tell which voltage to set up, VDDC/MVDDC/MVDDQ/VDDCI + UCHAR ucVoltageMode; // Indicate action: Set voltage level + USHORT usVoltageLevel; // real voltage level in unit of mv or Voltage Phase (0, 1, 2, .. ) +}SET_VOLTAGE_PARAMETERS_V1_3; + +//ucVoltageType +#define VOLTAGE_TYPE_VDDC 1 +#define VOLTAGE_TYPE_MVDDC 2 +#define VOLTAGE_TYPE_MVDDQ 3 +#define VOLTAGE_TYPE_VDDCI 4 + +//SET_VOLTAGE_PARAMETERS_V3.ucVoltageMode +#define ATOM_SET_VOLTAGE 0 //Set voltage Level +#define ATOM_INIT_VOLTAGE_REGULATOR 3 //Init Regulator +#define ATOM_SET_VOLTAGE_PHASE 4 //Set Vregulator Phase +#define ATOM_GET_MAX_VOLTAGE 6 //Get Max Voltage, not used in SetVoltageTable v1.3 +#define ATOM_GET_VOLTAGE_LEVEL 6 //Get Voltage level from vitual voltage ID + +// define vitual voltage id in usVoltageLevel +#define ATOM_VIRTUAL_VOLTAGE_ID0 0xff01 +#define ATOM_VIRTUAL_VOLTAGE_ID1 0xff02 +#define ATOM_VIRTUAL_VOLTAGE_ID2 0xff03 +#define ATOM_VIRTUAL_VOLTAGE_ID3 0xff04 + +typedef struct _SET_VOLTAGE_PS_ALLOCATION +{ + SET_VOLTAGE_PARAMETERS sASICSetVoltage; + WRITE_ONE_BYTE_HW_I2C_DATA_PS_ALLOCATION sReserved; +}SET_VOLTAGE_PS_ALLOCATION; + +// New Added from SI for GetVoltageInfoTable, input parameter structure +typedef struct _GET_VOLTAGE_INFO_INPUT_PARAMETER_V1_1 +{ + UCHAR ucVoltageType; // Input: To tell which voltage to set up, VDDC/MVDDC/MVDDQ/VDDCI + UCHAR ucVoltageMode; // Input: Indicate action: Get voltage info + USHORT usVoltageLevel; // Input: real voltage level in unit of mv or Voltage Phase (0, 1, 2, .. ) or Leakage Id + ULONG ulReserved; +}GET_VOLTAGE_INFO_INPUT_PARAMETER_V1_1; + +// New Added from SI for GetVoltageInfoTable, output parameter structure when ucVotlageMode == ATOM_GET_VOLTAGE_VID +typedef struct _GET_VOLTAGE_INFO_OUTPUT_PARAMETER_V1_1 +{ + ULONG ulVotlageGpioState; + ULONG ulVoltageGPioMask; +}GET_VOLTAGE_INFO_OUTPUT_PARAMETER_V1_1; + +// New Added from SI for GetVoltageInfoTable, output parameter structure when ucVotlageMode == ATOM_GET_VOLTAGE_STATEx_LEAKAGE_VID +typedef struct _GET_LEAKAGE_VOLTAGE_INFO_OUTPUT_PARAMETER_V1_1 +{ + USHORT usVoltageLevel; + USHORT usVoltageId; // Voltage Id programmed in Voltage Regulator + ULONG ulReseved; +}GET_LEAKAGE_VOLTAGE_INFO_OUTPUT_PARAMETER_V1_1; + + +// GetVoltageInfo v1.1 ucVoltageMode +#define ATOM_GET_VOLTAGE_VID 0x00 +#define ATOM_GET_VOTLAGE_INIT_SEQ 0x03 +#define ATOM_GET_VOLTTAGE_PHASE_PHASE_VID 0x04 +// for SI, this state map to 0xff02 voltage state in Power Play table, which is power boost state +#define ATOM_GET_VOLTAGE_STATE0_LEAKAGE_VID 0x10 + +// for SI, this state map to 0xff01 voltage state in Power Play table, which is performance state +#define ATOM_GET_VOLTAGE_STATE1_LEAKAGE_VID 0x11 +// undefined power state +#define ATOM_GET_VOLTAGE_STATE2_LEAKAGE_VID 0x12 +#define ATOM_GET_VOLTAGE_STATE3_LEAKAGE_VID 0x13 + +/****************************************************************************/ +// Structures used by TVEncoderControlTable +/****************************************************************************/ +typedef struct _TV_ENCODER_CONTROL_PARAMETERS +{ + USHORT usPixelClock; // in 10KHz; for bios convenient + UCHAR ucTvStandard; // See definition "ATOM_TV_NTSC ..." + UCHAR ucAction; // 0: turn off encoder + // 1: setup and turn on encoder +}TV_ENCODER_CONTROL_PARAMETERS; + +typedef struct _TV_ENCODER_CONTROL_PS_ALLOCATION +{ + TV_ENCODER_CONTROL_PARAMETERS sTVEncoder; + WRITE_ONE_BYTE_HW_I2C_DATA_PS_ALLOCATION sReserved; // Don't set this one +}TV_ENCODER_CONTROL_PS_ALLOCATION; + +//==============================Data Table Portion==================================== + +/****************************************************************************/ +// Structure used in Data.mtb +/****************************************************************************/ +typedef struct _ATOM_MASTER_LIST_OF_DATA_TABLES +{ + USHORT UtilityPipeLine; // Offest for the utility to get parser info,Don't change this position! + USHORT MultimediaCapabilityInfo; // Only used by MM Lib,latest version 1.1, not configuable from Bios, need to include the table to build Bios + USHORT MultimediaConfigInfo; // Only used by MM Lib,latest version 2.1, not configuable from Bios, need to include the table to build Bios + USHORT StandardVESA_Timing; // Only used by Bios + USHORT FirmwareInfo; // Shared by various SW components,latest version 1.4 + USHORT PaletteData; // Only used by BIOS + USHORT LCD_Info; // Shared by various SW components,latest version 1.3, was called LVDS_Info + USHORT DIGTransmitterInfo; // Internal used by VBIOS only version 3.1 + USHORT AnalogTV_Info; // Shared by various SW components,latest version 1.1 + USHORT SupportedDevicesInfo; // Will be obsolete from R600 + USHORT GPIO_I2C_Info; // Shared by various SW components,latest version 1.2 will be used from R600 + USHORT VRAM_UsageByFirmware; // Shared by various SW components,latest version 1.3 will be used from R600 + USHORT GPIO_Pin_LUT; // Shared by various SW components,latest version 1.1 + USHORT VESA_ToInternalModeLUT; // Only used by Bios + USHORT ComponentVideoInfo; // Shared by various SW components,latest version 2.1 will be used from R600 + USHORT PowerPlayInfo; // Shared by various SW components,latest version 2.1,new design from R600 + USHORT CompassionateData; // Will be obsolete from R600 + USHORT SaveRestoreInfo; // Only used by Bios + USHORT PPLL_SS_Info; // Shared by various SW components,latest version 1.2, used to call SS_Info, change to new name because of int ASIC SS info + USHORT OemInfo; // Defined and used by external SW, should be obsolete soon + USHORT XTMDS_Info; // Will be obsolete from R600 + USHORT MclkSS_Info; // Shared by various SW components,latest version 1.1, only enabled when ext SS chip is used + USHORT Object_Header; // Shared by various SW components,latest version 1.1 + USHORT IndirectIOAccess; // Only used by Bios,this table position can't change at all!! + USHORT MC_InitParameter; // Only used by command table + USHORT ASIC_VDDC_Info; // Will be obsolete from R600 + USHORT ASIC_InternalSS_Info; // New tabel name from R600, used to be called "ASIC_MVDDC_Info" + USHORT TV_VideoMode; // Only used by command table + USHORT VRAM_Info; // Only used by command table, latest version 1.3 + USHORT MemoryTrainingInfo; // Used for VBIOS and Diag utility for memory training purpose since R600. the new table rev start from 2.1 + USHORT IntegratedSystemInfo; // Shared by various SW components + USHORT ASIC_ProfilingInfo; // New table name from R600, used to be called "ASIC_VDDCI_Info" for pre-R600 + USHORT VoltageObjectInfo; // Shared by various SW components, latest version 1.1 + USHORT PowerSourceInfo; // Shared by various SW components, latest versoin 1.1 +}ATOM_MASTER_LIST_OF_DATA_TABLES; + +typedef struct _ATOM_MASTER_DATA_TABLE +{ + ATOM_COMMON_TABLE_HEADER sHeader; + ATOM_MASTER_LIST_OF_DATA_TABLES ListOfDataTables; +}ATOM_MASTER_DATA_TABLE; + +// For backward compatible +#define LVDS_Info LCD_Info +#define DAC_Info PaletteData +#define TMDS_Info DIGTransmitterInfo + +/****************************************************************************/ +// Structure used in MultimediaCapabilityInfoTable +/****************************************************************************/ +typedef struct _ATOM_MULTIMEDIA_CAPABILITY_INFO +{ + ATOM_COMMON_TABLE_HEADER sHeader; + ULONG ulSignature; // HW info table signature string "$ATI" + UCHAR ucI2C_Type; // I2C type (normal GP_IO, ImpactTV GP_IO, Dedicated I2C pin, etc) + UCHAR ucTV_OutInfo; // Type of TV out supported (3:0) and video out crystal frequency (6:4) and TV data port (7) + UCHAR ucVideoPortInfo; // Provides the video port capabilities + UCHAR ucHostPortInfo; // Provides host port configuration information +}ATOM_MULTIMEDIA_CAPABILITY_INFO; + +/****************************************************************************/ +// Structure used in MultimediaConfigInfoTable +/****************************************************************************/ +typedef struct _ATOM_MULTIMEDIA_CONFIG_INFO +{ + ATOM_COMMON_TABLE_HEADER sHeader; + ULONG ulSignature; // MM info table signature sting "$MMT" + UCHAR ucTunerInfo; // Type of tuner installed on the adapter (4:0) and video input for tuner (7:5) + UCHAR ucAudioChipInfo; // List the audio chip type (3:0) product type (4) and OEM revision (7:5) + UCHAR ucProductID; // Defines as OEM ID or ATI board ID dependent on product type setting + UCHAR ucMiscInfo1; // Tuner voltage (1:0) HW teletext support (3:2) FM audio decoder (5:4) reserved (6) audio scrambling (7) + UCHAR ucMiscInfo2; // I2S input config (0) I2S output config (1) I2S Audio Chip (4:2) SPDIF Output Config (5) reserved (7:6) + UCHAR ucMiscInfo3; // Video Decoder Type (3:0) Video In Standard/Crystal (7:4) + UCHAR ucMiscInfo4; // Video Decoder Host Config (2:0) reserved (7:3) + UCHAR ucVideoInput0Info;// Video Input 0 Type (1:0) F/B setting (2) physical connector ID (5:3) reserved (7:6) + UCHAR ucVideoInput1Info;// Video Input 1 Type (1:0) F/B setting (2) physical connector ID (5:3) reserved (7:6) + UCHAR ucVideoInput2Info;// Video Input 2 Type (1:0) F/B setting (2) physical connector ID (5:3) reserved (7:6) + UCHAR ucVideoInput3Info;// Video Input 3 Type (1:0) F/B setting (2) physical connector ID (5:3) reserved (7:6) + UCHAR ucVideoInput4Info;// Video Input 4 Type (1:0) F/B setting (2) physical connector ID (5:3) reserved (7:6) +}ATOM_MULTIMEDIA_CONFIG_INFO; + + +/****************************************************************************/ +// Structures used in FirmwareInfoTable +/****************************************************************************/ + +// usBIOSCapability Definition: +// Bit 0 = 0: Bios image is not Posted, =1:Bios image is Posted; +// Bit 1 = 0: Dual CRTC is not supported, =1: Dual CRTC is supported; +// Bit 2 = 0: Extended Desktop is not supported, =1: Extended Desktop is supported; +// Others: Reserved +#define ATOM_BIOS_INFO_ATOM_FIRMWARE_POSTED 0x0001 +#define ATOM_BIOS_INFO_DUAL_CRTC_SUPPORT 0x0002 +#define ATOM_BIOS_INFO_EXTENDED_DESKTOP_SUPPORT 0x0004 +#define ATOM_BIOS_INFO_MEMORY_CLOCK_SS_SUPPORT 0x0008 // (valid from v1.1 ~v1.4):=1: memclk SS enable, =0 memclk SS disable. +#define ATOM_BIOS_INFO_ENGINE_CLOCK_SS_SUPPORT 0x0010 // (valid from v1.1 ~v1.4):=1: engclk SS enable, =0 engclk SS disable. +#define ATOM_BIOS_INFO_BL_CONTROLLED_BY_GPU 0x0020 +#define ATOM_BIOS_INFO_WMI_SUPPORT 0x0040 +#define ATOM_BIOS_INFO_PPMODE_ASSIGNGED_BY_SYSTEM 0x0080 +#define ATOM_BIOS_INFO_HYPERMEMORY_SUPPORT 0x0100 +#define ATOM_BIOS_INFO_HYPERMEMORY_SIZE_MASK 0x1E00 +#define ATOM_BIOS_INFO_VPOST_WITHOUT_FIRST_MODE_SET 0x2000 +#define ATOM_BIOS_INFO_BIOS_SCRATCH6_SCL2_REDEFINE 0x4000 +#define ATOM_BIOS_INFO_MEMORY_CLOCK_EXT_SS_SUPPORT 0x0008 // (valid from v2.1 ): =1: memclk ss enable with external ss chip +#define ATOM_BIOS_INFO_ENGINE_CLOCK_EXT_SS_SUPPORT 0x0010 // (valid from v2.1 ): =1: engclk ss enable with external ss chip + +#ifndef _H2INC + +//Please don't add or expand this bitfield structure below, this one will retire soon.! +typedef struct _ATOM_FIRMWARE_CAPABILITY +{ +#if ATOM_BIG_ENDIAN + USHORT Reserved:1; + USHORT SCL2Redefined:1; + USHORT PostWithoutModeSet:1; + USHORT HyperMemory_Size:4; + USHORT HyperMemory_Support:1; + USHORT PPMode_Assigned:1; + USHORT WMI_SUPPORT:1; + USHORT GPUControlsBL:1; + USHORT EngineClockSS_Support:1; + USHORT MemoryClockSS_Support:1; + USHORT ExtendedDesktopSupport:1; + USHORT DualCRTC_Support:1; + USHORT FirmwarePosted:1; +#else + USHORT FirmwarePosted:1; + USHORT DualCRTC_Support:1; + USHORT ExtendedDesktopSupport:1; + USHORT MemoryClockSS_Support:1; + USHORT EngineClockSS_Support:1; + USHORT GPUControlsBL:1; + USHORT WMI_SUPPORT:1; + USHORT PPMode_Assigned:1; + USHORT HyperMemory_Support:1; + USHORT HyperMemory_Size:4; + USHORT PostWithoutModeSet:1; + USHORT SCL2Redefined:1; + USHORT Reserved:1; +#endif +}ATOM_FIRMWARE_CAPABILITY; + +typedef union _ATOM_FIRMWARE_CAPABILITY_ACCESS +{ + ATOM_FIRMWARE_CAPABILITY sbfAccess; + USHORT susAccess; +}ATOM_FIRMWARE_CAPABILITY_ACCESS; + +#else + +typedef union _ATOM_FIRMWARE_CAPABILITY_ACCESS +{ + USHORT susAccess; +}ATOM_FIRMWARE_CAPABILITY_ACCESS; + +#endif + +typedef struct _ATOM_FIRMWARE_INFO +{ + ATOM_COMMON_TABLE_HEADER sHeader; + ULONG ulFirmwareRevision; + ULONG ulDefaultEngineClock; //In 10Khz unit + ULONG ulDefaultMemoryClock; //In 10Khz unit + ULONG ulDriverTargetEngineClock; //In 10Khz unit + ULONG ulDriverTargetMemoryClock; //In 10Khz unit + ULONG ulMaxEngineClockPLL_Output; //In 10Khz unit + ULONG ulMaxMemoryClockPLL_Output; //In 10Khz unit + ULONG ulMaxPixelClockPLL_Output; //In 10Khz unit + ULONG ulASICMaxEngineClock; //In 10Khz unit + ULONG ulASICMaxMemoryClock; //In 10Khz unit + UCHAR ucASICMaxTemperature; + UCHAR ucPadding[3]; //Don't use them + ULONG aulReservedForBIOS[3]; //Don't use them + USHORT usMinEngineClockPLL_Input; //In 10Khz unit + USHORT usMaxEngineClockPLL_Input; //In 10Khz unit + USHORT usMinEngineClockPLL_Output; //In 10Khz unit + USHORT usMinMemoryClockPLL_Input; //In 10Khz unit + USHORT usMaxMemoryClockPLL_Input; //In 10Khz unit + USHORT usMinMemoryClockPLL_Output; //In 10Khz unit + USHORT usMaxPixelClock; //In 10Khz unit, Max. Pclk + USHORT usMinPixelClockPLL_Input; //In 10Khz unit + USHORT usMaxPixelClockPLL_Input; //In 10Khz unit + USHORT usMinPixelClockPLL_Output; //In 10Khz unit, the definitions above can't change!!! + ATOM_FIRMWARE_CAPABILITY_ACCESS usFirmwareCapability; + USHORT usReferenceClock; //In 10Khz unit + USHORT usPM_RTS_Location; //RTS PM4 starting location in ROM in 1Kb unit + UCHAR ucPM_RTS_StreamSize; //RTS PM4 packets in Kb unit + UCHAR ucDesign_ID; //Indicate what is the board design + UCHAR ucMemoryModule_ID; //Indicate what is the board design +}ATOM_FIRMWARE_INFO; + +typedef struct _ATOM_FIRMWARE_INFO_V1_2 +{ + ATOM_COMMON_TABLE_HEADER sHeader; + ULONG ulFirmwareRevision; + ULONG ulDefaultEngineClock; //In 10Khz unit + ULONG ulDefaultMemoryClock; //In 10Khz unit + ULONG ulDriverTargetEngineClock; //In 10Khz unit + ULONG ulDriverTargetMemoryClock; //In 10Khz unit + ULONG ulMaxEngineClockPLL_Output; //In 10Khz unit + ULONG ulMaxMemoryClockPLL_Output; //In 10Khz unit + ULONG ulMaxPixelClockPLL_Output; //In 10Khz unit + ULONG ulASICMaxEngineClock; //In 10Khz unit + ULONG ulASICMaxMemoryClock; //In 10Khz unit + UCHAR ucASICMaxTemperature; + UCHAR ucMinAllowedBL_Level; + UCHAR ucPadding[2]; //Don't use them + ULONG aulReservedForBIOS[2]; //Don't use them + ULONG ulMinPixelClockPLL_Output; //In 10Khz unit + USHORT usMinEngineClockPLL_Input; //In 10Khz unit + USHORT usMaxEngineClockPLL_Input; //In 10Khz unit + USHORT usMinEngineClockPLL_Output; //In 10Khz unit + USHORT usMinMemoryClockPLL_Input; //In 10Khz unit + USHORT usMaxMemoryClockPLL_Input; //In 10Khz unit + USHORT usMinMemoryClockPLL_Output; //In 10Khz unit + USHORT usMaxPixelClock; //In 10Khz unit, Max. Pclk + USHORT usMinPixelClockPLL_Input; //In 10Khz unit + USHORT usMaxPixelClockPLL_Input; //In 10Khz unit + USHORT usMinPixelClockPLL_Output; //In 10Khz unit - lower 16bit of ulMinPixelClockPLL_Output + ATOM_FIRMWARE_CAPABILITY_ACCESS usFirmwareCapability; + USHORT usReferenceClock; //In 10Khz unit + USHORT usPM_RTS_Location; //RTS PM4 starting location in ROM in 1Kb unit + UCHAR ucPM_RTS_StreamSize; //RTS PM4 packets in Kb unit + UCHAR ucDesign_ID; //Indicate what is the board design + UCHAR ucMemoryModule_ID; //Indicate what is the board design +}ATOM_FIRMWARE_INFO_V1_2; + +typedef struct _ATOM_FIRMWARE_INFO_V1_3 +{ + ATOM_COMMON_TABLE_HEADER sHeader; + ULONG ulFirmwareRevision; + ULONG ulDefaultEngineClock; //In 10Khz unit + ULONG ulDefaultMemoryClock; //In 10Khz unit + ULONG ulDriverTargetEngineClock; //In 10Khz unit + ULONG ulDriverTargetMemoryClock; //In 10Khz unit + ULONG ulMaxEngineClockPLL_Output; //In 10Khz unit + ULONG ulMaxMemoryClockPLL_Output; //In 10Khz unit + ULONG ulMaxPixelClockPLL_Output; //In 10Khz unit + ULONG ulASICMaxEngineClock; //In 10Khz unit + ULONG ulASICMaxMemoryClock; //In 10Khz unit + UCHAR ucASICMaxTemperature; + UCHAR ucMinAllowedBL_Level; + UCHAR ucPadding[2]; //Don't use them + ULONG aulReservedForBIOS; //Don't use them + ULONG ul3DAccelerationEngineClock;//In 10Khz unit + ULONG ulMinPixelClockPLL_Output; //In 10Khz unit + USHORT usMinEngineClockPLL_Input; //In 10Khz unit + USHORT usMaxEngineClockPLL_Input; //In 10Khz unit + USHORT usMinEngineClockPLL_Output; //In 10Khz unit + USHORT usMinMemoryClockPLL_Input; //In 10Khz unit + USHORT usMaxMemoryClockPLL_Input; //In 10Khz unit + USHORT usMinMemoryClockPLL_Output; //In 10Khz unit + USHORT usMaxPixelClock; //In 10Khz unit, Max. Pclk + USHORT usMinPixelClockPLL_Input; //In 10Khz unit + USHORT usMaxPixelClockPLL_Input; //In 10Khz unit + USHORT usMinPixelClockPLL_Output; //In 10Khz unit - lower 16bit of ulMinPixelClockPLL_Output + ATOM_FIRMWARE_CAPABILITY_ACCESS usFirmwareCapability; + USHORT usReferenceClock; //In 10Khz unit + USHORT usPM_RTS_Location; //RTS PM4 starting location in ROM in 1Kb unit + UCHAR ucPM_RTS_StreamSize; //RTS PM4 packets in Kb unit + UCHAR ucDesign_ID; //Indicate what is the board design + UCHAR ucMemoryModule_ID; //Indicate what is the board design +}ATOM_FIRMWARE_INFO_V1_3; + +typedef struct _ATOM_FIRMWARE_INFO_V1_4 +{ + ATOM_COMMON_TABLE_HEADER sHeader; + ULONG ulFirmwareRevision; + ULONG ulDefaultEngineClock; //In 10Khz unit + ULONG ulDefaultMemoryClock; //In 10Khz unit + ULONG ulDriverTargetEngineClock; //In 10Khz unit + ULONG ulDriverTargetMemoryClock; //In 10Khz unit + ULONG ulMaxEngineClockPLL_Output; //In 10Khz unit + ULONG ulMaxMemoryClockPLL_Output; //In 10Khz unit + ULONG ulMaxPixelClockPLL_Output; //In 10Khz unit + ULONG ulASICMaxEngineClock; //In 10Khz unit + ULONG ulASICMaxMemoryClock; //In 10Khz unit + UCHAR ucASICMaxTemperature; + UCHAR ucMinAllowedBL_Level; + USHORT usBootUpVDDCVoltage; //In MV unit + USHORT usLcdMinPixelClockPLL_Output; // In MHz unit + USHORT usLcdMaxPixelClockPLL_Output; // In MHz unit + ULONG ul3DAccelerationEngineClock;//In 10Khz unit + ULONG ulMinPixelClockPLL_Output; //In 10Khz unit + USHORT usMinEngineClockPLL_Input; //In 10Khz unit + USHORT usMaxEngineClockPLL_Input; //In 10Khz unit + USHORT usMinEngineClockPLL_Output; //In 10Khz unit + USHORT usMinMemoryClockPLL_Input; //In 10Khz unit + USHORT usMaxMemoryClockPLL_Input; //In 10Khz unit + USHORT usMinMemoryClockPLL_Output; //In 10Khz unit + USHORT usMaxPixelClock; //In 10Khz unit, Max. Pclk + USHORT usMinPixelClockPLL_Input; //In 10Khz unit + USHORT usMaxPixelClockPLL_Input; //In 10Khz unit + USHORT usMinPixelClockPLL_Output; //In 10Khz unit - lower 16bit of ulMinPixelClockPLL_Output + ATOM_FIRMWARE_CAPABILITY_ACCESS usFirmwareCapability; + USHORT usReferenceClock; //In 10Khz unit + USHORT usPM_RTS_Location; //RTS PM4 starting location in ROM in 1Kb unit + UCHAR ucPM_RTS_StreamSize; //RTS PM4 packets in Kb unit + UCHAR ucDesign_ID; //Indicate what is the board design + UCHAR ucMemoryModule_ID; //Indicate what is the board design +}ATOM_FIRMWARE_INFO_V1_4; + +//the structure below to be used from Cypress +typedef struct _ATOM_FIRMWARE_INFO_V2_1 +{ + ATOM_COMMON_TABLE_HEADER sHeader; + ULONG ulFirmwareRevision; + ULONG ulDefaultEngineClock; //In 10Khz unit + ULONG ulDefaultMemoryClock; //In 10Khz unit + ULONG ulReserved1; + ULONG ulReserved2; + ULONG ulMaxEngineClockPLL_Output; //In 10Khz unit + ULONG ulMaxMemoryClockPLL_Output; //In 10Khz unit + ULONG ulMaxPixelClockPLL_Output; //In 10Khz unit + ULONG ulBinaryAlteredInfo; //Was ulASICMaxEngineClock + ULONG ulDefaultDispEngineClkFreq; //In 10Khz unit + UCHAR ucReserved1; //Was ucASICMaxTemperature; + UCHAR ucMinAllowedBL_Level; + USHORT usBootUpVDDCVoltage; //In MV unit + USHORT usLcdMinPixelClockPLL_Output; // In MHz unit + USHORT usLcdMaxPixelClockPLL_Output; // In MHz unit + ULONG ulReserved4; //Was ulAsicMaximumVoltage + ULONG ulMinPixelClockPLL_Output; //In 10Khz unit + USHORT usMinEngineClockPLL_Input; //In 10Khz unit + USHORT usMaxEngineClockPLL_Input; //In 10Khz unit + USHORT usMinEngineClockPLL_Output; //In 10Khz unit + USHORT usMinMemoryClockPLL_Input; //In 10Khz unit + USHORT usMaxMemoryClockPLL_Input; //In 10Khz unit + USHORT usMinMemoryClockPLL_Output; //In 10Khz unit + USHORT usMaxPixelClock; //In 10Khz unit, Max. Pclk + USHORT usMinPixelClockPLL_Input; //In 10Khz unit + USHORT usMaxPixelClockPLL_Input; //In 10Khz unit + USHORT usMinPixelClockPLL_Output; //In 10Khz unit - lower 16bit of ulMinPixelClockPLL_Output + ATOM_FIRMWARE_CAPABILITY_ACCESS usFirmwareCapability; + USHORT usCoreReferenceClock; //In 10Khz unit + USHORT usMemoryReferenceClock; //In 10Khz unit + USHORT usUniphyDPModeExtClkFreq; //In 10Khz unit, if it is 0, In DP Mode Uniphy Input clock from internal PPLL, otherwise Input clock from external Spread clock + UCHAR ucMemoryModule_ID; //Indicate what is the board design + UCHAR ucReserved4[3]; +}ATOM_FIRMWARE_INFO_V2_1; + +//the structure below to be used from NI +//ucTableFormatRevision=2 +//ucTableContentRevision=2 +typedef struct _ATOM_FIRMWARE_INFO_V2_2 +{ + ATOM_COMMON_TABLE_HEADER sHeader; + ULONG ulFirmwareRevision; + ULONG ulDefaultEngineClock; //In 10Khz unit + ULONG ulDefaultMemoryClock; //In 10Khz unit + ULONG ulReserved[2]; + ULONG ulReserved1; //Was ulMaxEngineClockPLL_Output; //In 10Khz unit* + ULONG ulReserved2; //Was ulMaxMemoryClockPLL_Output; //In 10Khz unit* + ULONG ulMaxPixelClockPLL_Output; //In 10Khz unit + ULONG ulBinaryAlteredInfo; //Was ulASICMaxEngineClock ? + ULONG ulDefaultDispEngineClkFreq; //In 10Khz unit. This is the frequency before DCDTO, corresponding to usBootUpVDDCVoltage. + UCHAR ucReserved3; //Was ucASICMaxTemperature; + UCHAR ucMinAllowedBL_Level; + USHORT usBootUpVDDCVoltage; //In MV unit + USHORT usLcdMinPixelClockPLL_Output; // In MHz unit + USHORT usLcdMaxPixelClockPLL_Output; // In MHz unit + ULONG ulReserved4; //Was ulAsicMaximumVoltage + ULONG ulMinPixelClockPLL_Output; //In 10Khz unit + UCHAR ucRemoteDisplayConfig; + UCHAR ucReserved5[3]; //Was usMinEngineClockPLL_Input and usMaxEngineClockPLL_Input + ULONG ulReserved6; //Was usMinEngineClockPLL_Output and usMinMemoryClockPLL_Input + ULONG ulReserved7; //Was usMaxMemoryClockPLL_Input and usMinMemoryClockPLL_Output + USHORT usReserved11; //Was usMaxPixelClock; //In 10Khz unit, Max. Pclk used only for DAC + USHORT usMinPixelClockPLL_Input; //In 10Khz unit + USHORT usMaxPixelClockPLL_Input; //In 10Khz unit + USHORT usBootUpVDDCIVoltage; //In unit of mv; Was usMinPixelClockPLL_Output; + ATOM_FIRMWARE_CAPABILITY_ACCESS usFirmwareCapability; + USHORT usCoreReferenceClock; //In 10Khz unit + USHORT usMemoryReferenceClock; //In 10Khz unit + USHORT usUniphyDPModeExtClkFreq; //In 10Khz unit, if it is 0, In DP Mode Uniphy Input clock from internal PPLL, otherwise Input clock from external Spread clock + UCHAR ucMemoryModule_ID; //Indicate what is the board design + UCHAR ucReserved9[3]; + USHORT usBootUpMVDDCVoltage; //In unit of mv; Was usMinPixelClockPLL_Output; + USHORT usReserved12; + ULONG ulReserved10[3]; // New added comparing to previous version +}ATOM_FIRMWARE_INFO_V2_2; + +#define ATOM_FIRMWARE_INFO_LAST ATOM_FIRMWARE_INFO_V2_2 + + +// definition of ucRemoteDisplayConfig +#define REMOTE_DISPLAY_DISABLE 0x00 +#define REMOTE_DISPLAY_ENABLE 0x01 + +/****************************************************************************/ +// Structures used in IntegratedSystemInfoTable +/****************************************************************************/ +#define IGP_CAP_FLAG_DYNAMIC_CLOCK_EN 0x2 +#define IGP_CAP_FLAG_AC_CARD 0x4 +#define IGP_CAP_FLAG_SDVO_CARD 0x8 +#define IGP_CAP_FLAG_POSTDIV_BY_2_MODE 0x10 + +typedef struct _ATOM_INTEGRATED_SYSTEM_INFO +{ + ATOM_COMMON_TABLE_HEADER sHeader; + ULONG ulBootUpEngineClock; //in 10kHz unit + ULONG ulBootUpMemoryClock; //in 10kHz unit + ULONG ulMaxSystemMemoryClock; //in 10kHz unit + ULONG ulMinSystemMemoryClock; //in 10kHz unit + UCHAR ucNumberOfCyclesInPeriodHi; + UCHAR ucLCDTimingSel; //=0:not valid.!=0 sel this timing descriptor from LCD EDID. + USHORT usReserved1; + USHORT usInterNBVoltageLow; //An intermidiate PMW value to set the voltage + USHORT usInterNBVoltageHigh; //Another intermidiate PMW value to set the voltage + ULONG ulReserved[2]; + + USHORT usFSBClock; //In MHz unit + USHORT usCapabilityFlag; //Bit0=1 indicates the fake HDMI support,Bit1=0/1 for Dynamic clocking dis/enable + //Bit[3:2]== 0:No PCIE card, 1:AC card, 2:SDVO card + //Bit[4]==1: P/2 mode, ==0: P/1 mode + USHORT usPCIENBCfgReg7; //bit[7:0]=MUX_Sel, bit[9:8]=MUX_SEL_LEVEL2, bit[10]=Lane_Reversal + USHORT usK8MemoryClock; //in MHz unit + USHORT usK8SyncStartDelay; //in 0.01 us unit + USHORT usK8DataReturnTime; //in 0.01 us unit + UCHAR ucMaxNBVoltage; + UCHAR ucMinNBVoltage; + UCHAR ucMemoryType; //[7:4]=1:DDR1;=2:DDR2;=3:DDR3.[3:0] is reserved + UCHAR ucNumberOfCyclesInPeriod; //CG.FVTHROT_PWM_CTRL_REG0.NumberOfCyclesInPeriod + UCHAR ucStartingPWM_HighTime; //CG.FVTHROT_PWM_CTRL_REG0.StartingPWM_HighTime + UCHAR ucHTLinkWidth; //16 bit vs. 8 bit + UCHAR ucMaxNBVoltageHigh; + UCHAR ucMinNBVoltageHigh; +}ATOM_INTEGRATED_SYSTEM_INFO; + +/* Explanation on entries in ATOM_INTEGRATED_SYSTEM_INFO +ulBootUpMemoryClock: For Intel IGP,it's the UMA system memory clock + For AMD IGP,it's 0 if no SidePort memory installed or it's the boot-up SidePort memory clock +ulMaxSystemMemoryClock: For Intel IGP,it's the Max freq from memory SPD if memory runs in ASYNC mode or otherwise (SYNC mode) it's 0 + For AMD IGP,for now this can be 0 +ulMinSystemMemoryClock: For Intel IGP,it's 133MHz if memory runs in ASYNC mode or otherwise (SYNC mode) it's 0 + For AMD IGP,for now this can be 0 + +usFSBClock: For Intel IGP,it's FSB Freq + For AMD IGP,it's HT Link Speed + +usK8MemoryClock: For AMD IGP only. For RevF CPU, set it to 200 +usK8SyncStartDelay: For AMD IGP only. Memory access latency in K8, required for watermark calculation +usK8DataReturnTime: For AMD IGP only. Memory access latency in K8, required for watermark calculation + +VC:Voltage Control +ucMaxNBVoltage: Voltage regulator dependent PWM value. Low 8 bits of the value for the max voltage.Set this one to 0xFF if VC without PWM. Set this to 0x0 if no VC at all. +ucMinNBVoltage: Voltage regulator dependent PWM value. Low 8 bits of the value for the min voltage.Set this one to 0x00 if VC without PWM or no VC at all. + +ucNumberOfCyclesInPeriod: Indicate how many cycles when PWM duty is 100%. low 8 bits of the value. +ucNumberOfCyclesInPeriodHi: Indicate how many cycles when PWM duty is 100%. high 8 bits of the value.If the PWM has an inverter,set bit [7]==1,otherwise set it 0 + +ucMaxNBVoltageHigh: Voltage regulator dependent PWM value. High 8 bits of the value for the max voltage.Set this one to 0xFF if VC without PWM. Set this to 0x0 if no VC at all. +ucMinNBVoltageHigh: Voltage regulator dependent PWM value. High 8 bits of the value for the min voltage.Set this one to 0x00 if VC without PWM or no VC at all. + + +usInterNBVoltageLow: Voltage regulator dependent PWM value. The value makes the the voltage >=Min NB voltage but <=InterNBVoltageHigh. Set this to 0x0000 if VC without PWM or no VC at all. +usInterNBVoltageHigh: Voltage regulator dependent PWM value. The value makes the the voltage >=InterNBVoltageLow but <=Max NB voltage.Set this to 0x0000 if VC without PWM or no VC at all. +*/ + + +/* +The following IGP table is introduced from RS780, which is supposed to be put by SBIOS in FB before IGP VBIOS starts VPOST; +Then VBIOS will copy the whole structure to its image so all GPU SW components can access this data structure to get whatever they need. +The enough reservation should allow us to never change table revisions. Whenever needed, a GPU SW component can use reserved portion for new data entries. + +SW components can access the IGP system infor structure in the same way as before +*/ + + +typedef struct _ATOM_INTEGRATED_SYSTEM_INFO_V2 +{ + ATOM_COMMON_TABLE_HEADER sHeader; + ULONG ulBootUpEngineClock; //in 10kHz unit + ULONG ulReserved1[2]; //must be 0x0 for the reserved + ULONG ulBootUpUMAClock; //in 10kHz unit + ULONG ulBootUpSidePortClock; //in 10kHz unit + ULONG ulMinSidePortClock; //in 10kHz unit + ULONG ulReserved2[6]; //must be 0x0 for the reserved + ULONG ulSystemConfig; //see explanation below + ULONG ulBootUpReqDisplayVector; + ULONG ulOtherDisplayMisc; + ULONG ulDDISlot1Config; + ULONG ulDDISlot2Config; + UCHAR ucMemoryType; //[3:0]=1:DDR1;=2:DDR2;=3:DDR3.[7:4] is reserved + UCHAR ucUMAChannelNumber; + UCHAR ucDockingPinBit; + UCHAR ucDockingPinPolarity; + ULONG ulDockingPinCFGInfo; + ULONG ulCPUCapInfo; + USHORT usNumberOfCyclesInPeriod; + USHORT usMaxNBVoltage; + USHORT usMinNBVoltage; + USHORT usBootUpNBVoltage; + ULONG ulHTLinkFreq; //in 10Khz + USHORT usMinHTLinkWidth; + USHORT usMaxHTLinkWidth; + USHORT usUMASyncStartDelay; + USHORT usUMADataReturnTime; + USHORT usLinkStatusZeroTime; + USHORT usDACEfuse; //for storing badgap value (for RS880 only) + ULONG ulHighVoltageHTLinkFreq; // in 10Khz + ULONG ulLowVoltageHTLinkFreq; // in 10Khz + USHORT usMaxUpStreamHTLinkWidth; + USHORT usMaxDownStreamHTLinkWidth; + USHORT usMinUpStreamHTLinkWidth; + USHORT usMinDownStreamHTLinkWidth; + USHORT usFirmwareVersion; //0 means FW is not supported. Otherwise it's the FW version loaded by SBIOS and driver should enable FW. + USHORT usFullT0Time; // Input to calculate minimum HT link change time required by NB P-State. Unit is 0.01us. + ULONG ulReserved3[96]; //must be 0x0 +}ATOM_INTEGRATED_SYSTEM_INFO_V2; + +/* +ulBootUpEngineClock: Boot-up Engine Clock in 10Khz; +ulBootUpUMAClock: Boot-up UMA Clock in 10Khz; it must be 0x0 when UMA is not present +ulBootUpSidePortClock: Boot-up SidePort Clock in 10Khz; it must be 0x0 when SidePort Memory is not present,this could be equal to or less than maximum supported Sideport memory clock + +ulSystemConfig: +Bit[0]=1: PowerExpress mode =0 Non-PowerExpress mode; +Bit[1]=1: system boots up at AMD overdrived state or user customized mode. In this case, driver will just stick to this boot-up mode. No other PowerPlay state + =0: system boots up at driver control state. Power state depends on PowerPlay table. +Bit[2]=1: PWM method is used on NB voltage control. =0: GPIO method is used. +Bit[3]=1: Only one power state(Performance) will be supported. + =0: Multiple power states supported from PowerPlay table. +Bit[4]=1: CLMC is supported and enabled on current system. + =0: CLMC is not supported or enabled on current system. SBIOS need to support HT link/freq change through ATIF interface. +Bit[5]=1: Enable CDLW for all driver control power states. Max HT width is from SBIOS, while Min HT width is determined by display requirement. + =0: CDLW is disabled. If CLMC is enabled case, Min HT width will be set equal to Max HT width. If CLMC disabled case, Max HT width will be applied. +Bit[6]=1: High Voltage requested for all power states. In this case, voltage will be forced at 1.1v and powerplay table voltage drop/throttling request will be ignored. + =0: Voltage settings is determined by powerplay table. +Bit[7]=1: Enable CLMC as hybrid Mode. CDLD and CILR will be disabled in this case and we're using legacy C1E. This is workaround for CPU(Griffin) performance issue. + =0: Enable CLMC as regular mode, CDLD and CILR will be enabled. +Bit[8]=1: CDLF is supported and enabled on current system. + =0: CDLF is not supported or enabled on current system. +Bit[9]=1: DLL Shut Down feature is enabled on current system. + =0: DLL Shut Down feature is not enabled or supported on current system. + +ulBootUpReqDisplayVector: This dword is a bit vector indicates what display devices are requested during boot-up. Refer to ATOM_DEVICE_xxx_SUPPORT for the bit vector definitions. + +ulOtherDisplayMisc: [15:8]- Bootup LCD Expansion selection; 0-center, 1-full panel size expansion; + [7:0] - BootupTV standard selection; This is a bit vector to indicate what TV standards are supported by the system. Refer to ucTVSupportedStd definition; + +ulDDISlot1Config: Describes the PCIE lane configuration on this DDI PCIE slot (ADD2 card) or connector (Mobile design). + [3:0] - Bit vector to indicate PCIE lane config of the DDI slot/connector on chassis (bit 0=1 lane 3:0; bit 1=1 lane 7:4; bit 2=1 lane 11:8; bit 3=1 lane 15:12) + [7:4] - Bit vector to indicate PCIE lane config of the same DDI slot/connector on docking station (bit 4=1 lane 3:0; bit 5=1 lane 7:4; bit 6=1 lane 11:8; bit 7=1 lane 15:12) + When a DDI connector is not "paired" (meaming two connections mutualexclusive on chassis or docking, only one of them can be connected at one time. + in both chassis and docking, SBIOS has to duplicate the same PCIE lane info from chassis to docking or vice versa. For example: + one DDI connector is only populated in docking with PCIE lane 8-11, but there is no paired connection on chassis, SBIOS has to copy bit 6 to bit 2. + + [15:8] - Lane configuration attribute; + [23:16]- Connector type, possible value: + CONNECTOR_OBJECT_ID_SINGLE_LINK_DVI_D + CONNECTOR_OBJECT_ID_DUAL_LINK_DVI_D + CONNECTOR_OBJECT_ID_HDMI_TYPE_A + CONNECTOR_OBJECT_ID_DISPLAYPORT + CONNECTOR_OBJECT_ID_eDP + [31:24]- Reserved + +ulDDISlot2Config: Same as Slot1. +ucMemoryType: SidePort memory type, set it to 0x0 when Sideport memory is not installed. Driver needs this info to change sideport memory clock. Not for display in CCC. +For IGP, Hypermemory is the only memory type showed in CCC. + +ucUMAChannelNumber: how many channels for the UMA; + +ulDockingPinCFGInfo: [15:0]-Bus/Device/Function # to CFG to read this Docking Pin; [31:16]-reg offset in CFG to read this pin +ucDockingPinBit: which bit in this register to read the pin status; +ucDockingPinPolarity:Polarity of the pin when docked; + +ulCPUCapInfo: [7:0]=1:Griffin;[7:0]=2:Greyhound;[7:0]=3:K8, [7:0]=4:Pharaoh, other bits reserved for now and must be 0x0 + +usNumberOfCyclesInPeriod:Indicate how many cycles when PWM duty is 100%. + +usMaxNBVoltage:Max. voltage control value in either PWM or GPIO mode. +usMinNBVoltage:Min. voltage control value in either PWM or GPIO mode. + GPIO mode: both usMaxNBVoltage & usMinNBVoltage have a valid value ulSystemConfig.SYSTEM_CONFIG_USE_PWM_ON_VOLTAGE=0 + PWM mode: both usMaxNBVoltage & usMinNBVoltage have a valid value ulSystemConfig.SYSTEM_CONFIG_USE_PWM_ON_VOLTAGE=1 + GPU SW don't control mode: usMaxNBVoltage & usMinNBVoltage=0 and no care about ulSystemConfig.SYSTEM_CONFIG_USE_PWM_ON_VOLTAGE + +usBootUpNBVoltage:Boot-up voltage regulator dependent PWM value. + +ulHTLinkFreq: Bootup HT link Frequency in 10Khz. +usMinHTLinkWidth: Bootup minimum HT link width. If CDLW disabled, this is equal to usMaxHTLinkWidth. + If CDLW enabled, both upstream and downstream width should be the same during bootup. +usMaxHTLinkWidth: Bootup maximum HT link width. If CDLW disabled, this is equal to usMinHTLinkWidth. + If CDLW enabled, both upstream and downstream width should be the same during bootup. + +usUMASyncStartDelay: Memory access latency, required for watermark calculation +usUMADataReturnTime: Memory access latency, required for watermark calculation +usLinkStatusZeroTime:Memory access latency required for watermark calculation, set this to 0x0 for K8 CPU, set a proper value in 0.01 the unit of us +for Griffin or Greyhound. SBIOS needs to convert to actual time by: + if T0Ttime [5:4]=00b, then usLinkStatusZeroTime=T0Ttime [3:0]*0.1us (0.0 to 1.5us) + if T0Ttime [5:4]=01b, then usLinkStatusZeroTime=T0Ttime [3:0]*0.5us (0.0 to 7.5us) + if T0Ttime [5:4]=10b, then usLinkStatusZeroTime=T0Ttime [3:0]*2.0us (0.0 to 30us) + if T0Ttime [5:4]=11b, and T0Ttime [3:0]=0x0 to 0xa, then usLinkStatusZeroTime=T0Ttime [3:0]*20us (0.0 to 200us) + +ulHighVoltageHTLinkFreq: HT link frequency for power state with low voltage. If boot up runs in HT1, this must be 0. + This must be less than or equal to ulHTLinkFreq(bootup frequency). +ulLowVoltageHTLinkFreq: HT link frequency for power state with low voltage or voltage scaling 1.0v~1.1v. If boot up runs in HT1, this must be 0. + This must be less than or equal to ulHighVoltageHTLinkFreq. + +usMaxUpStreamHTLinkWidth: Asymmetric link width support in the future, to replace usMaxHTLinkWidth. Not used for now. +usMaxDownStreamHTLinkWidth: same as above. +usMinUpStreamHTLinkWidth: Asymmetric link width support in the future, to replace usMinHTLinkWidth. Not used for now. +usMinDownStreamHTLinkWidth: same as above. +*/ + +// ATOM_INTEGRATED_SYSTEM_INFO::ulCPUCapInfo - CPU type definition +#define INTEGRATED_SYSTEM_INFO__UNKNOWN_CPU 0 +#define INTEGRATED_SYSTEM_INFO__AMD_CPU__GRIFFIN 1 +#define INTEGRATED_SYSTEM_INFO__AMD_CPU__GREYHOUND 2 +#define INTEGRATED_SYSTEM_INFO__AMD_CPU__K8 3 +#define INTEGRATED_SYSTEM_INFO__AMD_CPU__PHARAOH 4 +#define INTEGRATED_SYSTEM_INFO__AMD_CPU__OROCHI 5 + +#define INTEGRATED_SYSTEM_INFO__AMD_CPU__MAX_CODE INTEGRATED_SYSTEM_INFO__AMD_CPU__OROCHI // this deff reflects max defined CPU code + +#define SYSTEM_CONFIG_POWEREXPRESS_ENABLE 0x00000001 +#define SYSTEM_CONFIG_RUN_AT_OVERDRIVE_ENGINE 0x00000002 +#define SYSTEM_CONFIG_USE_PWM_ON_VOLTAGE 0x00000004 +#define SYSTEM_CONFIG_PERFORMANCE_POWERSTATE_ONLY 0x00000008 +#define SYSTEM_CONFIG_CLMC_ENABLED 0x00000010 +#define SYSTEM_CONFIG_CDLW_ENABLED 0x00000020 +#define SYSTEM_CONFIG_HIGH_VOLTAGE_REQUESTED 0x00000040 +#define SYSTEM_CONFIG_CLMC_HYBRID_MODE_ENABLED 0x00000080 +#define SYSTEM_CONFIG_CDLF_ENABLED 0x00000100 +#define SYSTEM_CONFIG_DLL_SHUTDOWN_ENABLED 0x00000200 + +#define IGP_DDI_SLOT_LANE_CONFIG_MASK 0x000000FF + +#define b0IGP_DDI_SLOT_LANE_MAP_MASK 0x0F +#define b0IGP_DDI_SLOT_DOCKING_LANE_MAP_MASK 0xF0 +#define b0IGP_DDI_SLOT_CONFIG_LANE_0_3 0x01 +#define b0IGP_DDI_SLOT_CONFIG_LANE_4_7 0x02 +#define b0IGP_DDI_SLOT_CONFIG_LANE_8_11 0x04 +#define b0IGP_DDI_SLOT_CONFIG_LANE_12_15 0x08 + +#define IGP_DDI_SLOT_ATTRIBUTE_MASK 0x0000FF00 +#define IGP_DDI_SLOT_CONFIG_REVERSED 0x00000100 +#define b1IGP_DDI_SLOT_CONFIG_REVERSED 0x01 + +#define IGP_DDI_SLOT_CONNECTOR_TYPE_MASK 0x00FF0000 + +// IntegratedSystemInfoTable new Rev is V5 after V2, because of the real rev of V2 is v1.4. This rev is used for RR +typedef struct _ATOM_INTEGRATED_SYSTEM_INFO_V5 +{ + ATOM_COMMON_TABLE_HEADER sHeader; + ULONG ulBootUpEngineClock; //in 10kHz unit + ULONG ulDentistVCOFreq; //Dentist VCO clock in 10kHz unit, the source of GPU SCLK, LCLK, UCLK and VCLK. + ULONG ulLClockFreq; //GPU Lclk freq in 10kHz unit, have relationship with NCLK in NorthBridge + ULONG ulBootUpUMAClock; //in 10kHz unit + ULONG ulReserved1[8]; //must be 0x0 for the reserved + ULONG ulBootUpReqDisplayVector; + ULONG ulOtherDisplayMisc; + ULONG ulReserved2[4]; //must be 0x0 for the reserved + ULONG ulSystemConfig; //TBD + ULONG ulCPUCapInfo; //TBD + USHORT usMaxNBVoltage; //high NB voltage, calculated using current VDDNB (D24F2xDC) and VDDNB offset fuse; + USHORT usMinNBVoltage; //low NB voltage, calculated using current VDDNB (D24F2xDC) and VDDNB offset fuse; + USHORT usBootUpNBVoltage; //boot up NB voltage + UCHAR ucHtcTmpLmt; //bit [22:16] of D24F3x64 Hardware Thermal Control (HTC) Register, may not be needed, TBD + UCHAR ucTjOffset; //bit [28:22] of D24F3xE4 Thermtrip Status Register,may not be needed, TBD + ULONG ulReserved3[4]; //must be 0x0 for the reserved + ULONG ulDDISlot1Config; //see above ulDDISlot1Config definition + ULONG ulDDISlot2Config; + ULONG ulDDISlot3Config; + ULONG ulDDISlot4Config; + ULONG ulReserved4[4]; //must be 0x0 for the reserved + UCHAR ucMemoryType; //[3:0]=1:DDR1;=2:DDR2;=3:DDR3.[7:4] is reserved + UCHAR ucUMAChannelNumber; + USHORT usReserved; + ULONG ulReserved5[4]; //must be 0x0 for the reserved + ULONG ulCSR_M3_ARB_CNTL_DEFAULT[10];//arrays with values for CSR M3 arbiter for default + ULONG ulCSR_M3_ARB_CNTL_UVD[10]; //arrays with values for CSR M3 arbiter for UVD playback + ULONG ulCSR_M3_ARB_CNTL_FS3D[10];//arrays with values for CSR M3 arbiter for Full Screen 3D applications + ULONG ulReserved6[61]; //must be 0x0 +}ATOM_INTEGRATED_SYSTEM_INFO_V5; + +#define ATOM_CRT_INT_ENCODER1_INDEX 0x00000000 +#define ATOM_LCD_INT_ENCODER1_INDEX 0x00000001 +#define ATOM_TV_INT_ENCODER1_INDEX 0x00000002 +#define ATOM_DFP_INT_ENCODER1_INDEX 0x00000003 +#define ATOM_CRT_INT_ENCODER2_INDEX 0x00000004 +#define ATOM_LCD_EXT_ENCODER1_INDEX 0x00000005 +#define ATOM_TV_EXT_ENCODER1_INDEX 0x00000006 +#define ATOM_DFP_EXT_ENCODER1_INDEX 0x00000007 +#define ATOM_CV_INT_ENCODER1_INDEX 0x00000008 +#define ATOM_DFP_INT_ENCODER2_INDEX 0x00000009 +#define ATOM_CRT_EXT_ENCODER1_INDEX 0x0000000A +#define ATOM_CV_EXT_ENCODER1_INDEX 0x0000000B +#define ATOM_DFP_INT_ENCODER3_INDEX 0x0000000C +#define ATOM_DFP_INT_ENCODER4_INDEX 0x0000000D + +// define ASIC internal encoder id ( bit vector ), used for CRTC_SourceSelTable +#define ASIC_INT_DAC1_ENCODER_ID 0x00 +#define ASIC_INT_TV_ENCODER_ID 0x02 +#define ASIC_INT_DIG1_ENCODER_ID 0x03 +#define ASIC_INT_DAC2_ENCODER_ID 0x04 +#define ASIC_EXT_TV_ENCODER_ID 0x06 +#define ASIC_INT_DVO_ENCODER_ID 0x07 +#define ASIC_INT_DIG2_ENCODER_ID 0x09 +#define ASIC_EXT_DIG_ENCODER_ID 0x05 +#define ASIC_EXT_DIG2_ENCODER_ID 0x08 +#define ASIC_INT_DIG3_ENCODER_ID 0x0a +#define ASIC_INT_DIG4_ENCODER_ID 0x0b +#define ASIC_INT_DIG5_ENCODER_ID 0x0c +#define ASIC_INT_DIG6_ENCODER_ID 0x0d +#define ASIC_INT_DIG7_ENCODER_ID 0x0e + +//define Encoder attribute +#define ATOM_ANALOG_ENCODER 0 +#define ATOM_DIGITAL_ENCODER 1 +#define ATOM_DP_ENCODER 2 + +#define ATOM_ENCODER_ENUM_MASK 0x70 +#define ATOM_ENCODER_ENUM_ID1 0x00 +#define ATOM_ENCODER_ENUM_ID2 0x10 +#define ATOM_ENCODER_ENUM_ID3 0x20 +#define ATOM_ENCODER_ENUM_ID4 0x30 +#define ATOM_ENCODER_ENUM_ID5 0x40 +#define ATOM_ENCODER_ENUM_ID6 0x50 + +#define ATOM_DEVICE_CRT1_INDEX 0x00000000 +#define ATOM_DEVICE_LCD1_INDEX 0x00000001 +#define ATOM_DEVICE_TV1_INDEX 0x00000002 +#define ATOM_DEVICE_DFP1_INDEX 0x00000003 +#define ATOM_DEVICE_CRT2_INDEX 0x00000004 +#define ATOM_DEVICE_LCD2_INDEX 0x00000005 +#define ATOM_DEVICE_DFP6_INDEX 0x00000006 +#define ATOM_DEVICE_DFP2_INDEX 0x00000007 +#define ATOM_DEVICE_CV_INDEX 0x00000008 +#define ATOM_DEVICE_DFP3_INDEX 0x00000009 +#define ATOM_DEVICE_DFP4_INDEX 0x0000000A +#define ATOM_DEVICE_DFP5_INDEX 0x0000000B + +#define ATOM_DEVICE_RESERVEDC_INDEX 0x0000000C +#define ATOM_DEVICE_RESERVEDD_INDEX 0x0000000D +#define ATOM_DEVICE_RESERVEDE_INDEX 0x0000000E +#define ATOM_DEVICE_RESERVEDF_INDEX 0x0000000F +#define ATOM_MAX_SUPPORTED_DEVICE_INFO (ATOM_DEVICE_DFP3_INDEX+1) +#define ATOM_MAX_SUPPORTED_DEVICE_INFO_2 ATOM_MAX_SUPPORTED_DEVICE_INFO +#define ATOM_MAX_SUPPORTED_DEVICE_INFO_3 (ATOM_DEVICE_DFP5_INDEX + 1 ) + +#define ATOM_MAX_SUPPORTED_DEVICE (ATOM_DEVICE_RESERVEDF_INDEX+1) + +#define ATOM_DEVICE_CRT1_SUPPORT (0x1L << ATOM_DEVICE_CRT1_INDEX ) +#define ATOM_DEVICE_LCD1_SUPPORT (0x1L << ATOM_DEVICE_LCD1_INDEX ) +#define ATOM_DEVICE_TV1_SUPPORT (0x1L << ATOM_DEVICE_TV1_INDEX ) +#define ATOM_DEVICE_DFP1_SUPPORT (0x1L << ATOM_DEVICE_DFP1_INDEX ) +#define ATOM_DEVICE_CRT2_SUPPORT (0x1L << ATOM_DEVICE_CRT2_INDEX ) +#define ATOM_DEVICE_LCD2_SUPPORT (0x1L << ATOM_DEVICE_LCD2_INDEX ) +#define ATOM_DEVICE_DFP6_SUPPORT (0x1L << ATOM_DEVICE_DFP6_INDEX ) +#define ATOM_DEVICE_DFP2_SUPPORT (0x1L << ATOM_DEVICE_DFP2_INDEX ) +#define ATOM_DEVICE_CV_SUPPORT (0x1L << ATOM_DEVICE_CV_INDEX ) +#define ATOM_DEVICE_DFP3_SUPPORT (0x1L << ATOM_DEVICE_DFP3_INDEX ) +#define ATOM_DEVICE_DFP4_SUPPORT (0x1L << ATOM_DEVICE_DFP4_INDEX ) +#define ATOM_DEVICE_DFP5_SUPPORT (0x1L << ATOM_DEVICE_DFP5_INDEX ) + +#define ATOM_DEVICE_CRT_SUPPORT (ATOM_DEVICE_CRT1_SUPPORT | ATOM_DEVICE_CRT2_SUPPORT) +#define ATOM_DEVICE_DFP_SUPPORT (ATOM_DEVICE_DFP1_SUPPORT | ATOM_DEVICE_DFP2_SUPPORT | ATOM_DEVICE_DFP3_SUPPORT | ATOM_DEVICE_DFP4_SUPPORT | ATOM_DEVICE_DFP5_SUPPORT | ATOM_DEVICE_DFP6_SUPPORT) +#define ATOM_DEVICE_TV_SUPPORT (ATOM_DEVICE_TV1_SUPPORT) +#define ATOM_DEVICE_LCD_SUPPORT (ATOM_DEVICE_LCD1_SUPPORT | ATOM_DEVICE_LCD2_SUPPORT) + +#define ATOM_DEVICE_CONNECTOR_TYPE_MASK 0x000000F0 +#define ATOM_DEVICE_CONNECTOR_TYPE_SHIFT 0x00000004 +#define ATOM_DEVICE_CONNECTOR_VGA 0x00000001 +#define ATOM_DEVICE_CONNECTOR_DVI_I 0x00000002 +#define ATOM_DEVICE_CONNECTOR_DVI_D 0x00000003 +#define ATOM_DEVICE_CONNECTOR_DVI_A 0x00000004 +#define ATOM_DEVICE_CONNECTOR_SVIDEO 0x00000005 +#define ATOM_DEVICE_CONNECTOR_COMPOSITE 0x00000006 +#define ATOM_DEVICE_CONNECTOR_LVDS 0x00000007 +#define ATOM_DEVICE_CONNECTOR_DIGI_LINK 0x00000008 +#define ATOM_DEVICE_CONNECTOR_SCART 0x00000009 +#define ATOM_DEVICE_CONNECTOR_HDMI_TYPE_A 0x0000000A +#define ATOM_DEVICE_CONNECTOR_HDMI_TYPE_B 0x0000000B +#define ATOM_DEVICE_CONNECTOR_CASE_1 0x0000000E +#define ATOM_DEVICE_CONNECTOR_DISPLAYPORT 0x0000000F + + +#define ATOM_DEVICE_DAC_INFO_MASK 0x0000000F +#define ATOM_DEVICE_DAC_INFO_SHIFT 0x00000000 +#define ATOM_DEVICE_DAC_INFO_NODAC 0x00000000 +#define ATOM_DEVICE_DAC_INFO_DACA 0x00000001 +#define ATOM_DEVICE_DAC_INFO_DACB 0x00000002 +#define ATOM_DEVICE_DAC_INFO_EXDAC 0x00000003 + +#define ATOM_DEVICE_I2C_ID_NOI2C 0x00000000 + +#define ATOM_DEVICE_I2C_LINEMUX_MASK 0x0000000F +#define ATOM_DEVICE_I2C_LINEMUX_SHIFT 0x00000000 + +#define ATOM_DEVICE_I2C_ID_MASK 0x00000070 +#define ATOM_DEVICE_I2C_ID_SHIFT 0x00000004 +#define ATOM_DEVICE_I2C_ID_IS_FOR_NON_MM_USE 0x00000001 +#define ATOM_DEVICE_I2C_ID_IS_FOR_MM_USE 0x00000002 +#define ATOM_DEVICE_I2C_ID_IS_FOR_SDVO_USE 0x00000003 //For IGP RS600 +#define ATOM_DEVICE_I2C_ID_IS_FOR_DAC_SCL 0x00000004 //For IGP RS690 + +#define ATOM_DEVICE_I2C_HARDWARE_CAP_MASK 0x00000080 +#define ATOM_DEVICE_I2C_HARDWARE_CAP_SHIFT 0x00000007 +#define ATOM_DEVICE_USES_SOFTWARE_ASSISTED_I2C 0x00000000 +#define ATOM_DEVICE_USES_HARDWARE_ASSISTED_I2C 0x00000001 + +// usDeviceSupport: +// Bits0 = 0 - no CRT1 support= 1- CRT1 is supported +// Bit 1 = 0 - no LCD1 support= 1- LCD1 is supported +// Bit 2 = 0 - no TV1 support= 1- TV1 is supported +// Bit 3 = 0 - no DFP1 support= 1- DFP1 is supported +// Bit 4 = 0 - no CRT2 support= 1- CRT2 is supported +// Bit 5 = 0 - no LCD2 support= 1- LCD2 is supported +// Bit 6 = 0 - no DFP6 support= 1- DFP6 is supported +// Bit 7 = 0 - no DFP2 support= 1- DFP2 is supported +// Bit 8 = 0 - no CV support= 1- CV is supported +// Bit 9 = 0 - no DFP3 support= 1- DFP3 is supported +// Bit 10 = 0 - no DFP4 support= 1- DFP4 is supported +// Bit 11 = 0 - no DFP5 support= 1- DFP5 is supported +// +// + +/****************************************************************************/ +/* Structure used in MclkSS_InfoTable */ +/****************************************************************************/ +// ucI2C_ConfigID +// [7:0] - I2C LINE Associate ID +// = 0 - no I2C +// [7] - HW_Cap = 1, [6:0]=HW assisted I2C ID(HW line selection) +// = 0, [6:0]=SW assisted I2C ID +// [6-4] - HW_ENGINE_ID = 1, HW engine for NON multimedia use +// = 2, HW engine for Multimedia use +// = 3-7 Reserved for future I2C engines +// [3-0] - I2C_LINE_MUX = A Mux number when it's HW assisted I2C or GPIO ID when it's SW I2C + +typedef struct _ATOM_I2C_ID_CONFIG +{ +#if ATOM_BIG_ENDIAN + UCHAR bfHW_Capable:1; + UCHAR bfHW_EngineID:3; + UCHAR bfI2C_LineMux:4; +#else + UCHAR bfI2C_LineMux:4; + UCHAR bfHW_EngineID:3; + UCHAR bfHW_Capable:1; +#endif +}ATOM_I2C_ID_CONFIG; + +typedef union _ATOM_I2C_ID_CONFIG_ACCESS +{ + ATOM_I2C_ID_CONFIG sbfAccess; + UCHAR ucAccess; +}ATOM_I2C_ID_CONFIG_ACCESS; + + +/****************************************************************************/ +// Structure used in GPIO_I2C_InfoTable +/****************************************************************************/ +typedef struct _ATOM_GPIO_I2C_ASSIGMENT +{ + USHORT usClkMaskRegisterIndex; + USHORT usClkEnRegisterIndex; + USHORT usClkY_RegisterIndex; + USHORT usClkA_RegisterIndex; + USHORT usDataMaskRegisterIndex; + USHORT usDataEnRegisterIndex; + USHORT usDataY_RegisterIndex; + USHORT usDataA_RegisterIndex; + ATOM_I2C_ID_CONFIG_ACCESS sucI2cId; + UCHAR ucClkMaskShift; + UCHAR ucClkEnShift; + UCHAR ucClkY_Shift; + UCHAR ucClkA_Shift; + UCHAR ucDataMaskShift; + UCHAR ucDataEnShift; + UCHAR ucDataY_Shift; + UCHAR ucDataA_Shift; + UCHAR ucReserved1; + UCHAR ucReserved2; +}ATOM_GPIO_I2C_ASSIGMENT; + +typedef struct _ATOM_GPIO_I2C_INFO +{ + ATOM_COMMON_TABLE_HEADER sHeader; + ATOM_GPIO_I2C_ASSIGMENT asGPIO_Info[ATOM_MAX_SUPPORTED_DEVICE]; +}ATOM_GPIO_I2C_INFO; + +/****************************************************************************/ +// Common Structure used in other structures +/****************************************************************************/ + +#ifndef _H2INC + +//Please don't add or expand this bitfield structure below, this one will retire soon.! +typedef struct _ATOM_MODE_MISC_INFO +{ +#if ATOM_BIG_ENDIAN + USHORT Reserved:6; + USHORT RGB888:1; + USHORT DoubleClock:1; + USHORT Interlace:1; + USHORT CompositeSync:1; + USHORT V_ReplicationBy2:1; + USHORT H_ReplicationBy2:1; + USHORT VerticalCutOff:1; + USHORT VSyncPolarity:1; //0=Active High, 1=Active Low + USHORT HSyncPolarity:1; //0=Active High, 1=Active Low + USHORT HorizontalCutOff:1; +#else + USHORT HorizontalCutOff:1; + USHORT HSyncPolarity:1; //0=Active High, 1=Active Low + USHORT VSyncPolarity:1; //0=Active High, 1=Active Low + USHORT VerticalCutOff:1; + USHORT H_ReplicationBy2:1; + USHORT V_ReplicationBy2:1; + USHORT CompositeSync:1; + USHORT Interlace:1; + USHORT DoubleClock:1; + USHORT RGB888:1; + USHORT Reserved:6; +#endif +}ATOM_MODE_MISC_INFO; + +typedef union _ATOM_MODE_MISC_INFO_ACCESS +{ + ATOM_MODE_MISC_INFO sbfAccess; + USHORT usAccess; +}ATOM_MODE_MISC_INFO_ACCESS; + +#else + +typedef union _ATOM_MODE_MISC_INFO_ACCESS +{ + USHORT usAccess; +}ATOM_MODE_MISC_INFO_ACCESS; + +#endif + +// usModeMiscInfo- +#define ATOM_H_CUTOFF 0x01 +#define ATOM_HSYNC_POLARITY 0x02 //0=Active High, 1=Active Low +#define ATOM_VSYNC_POLARITY 0x04 //0=Active High, 1=Active Low +#define ATOM_V_CUTOFF 0x08 +#define ATOM_H_REPLICATIONBY2 0x10 +#define ATOM_V_REPLICATIONBY2 0x20 +#define ATOM_COMPOSITESYNC 0x40 +#define ATOM_INTERLACE 0x80 +#define ATOM_DOUBLE_CLOCK_MODE 0x100 +#define ATOM_RGB888_MODE 0x200 + +//usRefreshRate- +#define ATOM_REFRESH_43 43 +#define ATOM_REFRESH_47 47 +#define ATOM_REFRESH_56 56 +#define ATOM_REFRESH_60 60 +#define ATOM_REFRESH_65 65 +#define ATOM_REFRESH_70 70 +#define ATOM_REFRESH_72 72 +#define ATOM_REFRESH_75 75 +#define ATOM_REFRESH_85 85 + +// ATOM_MODE_TIMING data are exactly the same as VESA timing data. +// Translation from EDID to ATOM_MODE_TIMING, use the following formula. +// +// VESA_HTOTAL = VESA_ACTIVE + 2* VESA_BORDER + VESA_BLANK +// = EDID_HA + EDID_HBL +// VESA_HDISP = VESA_ACTIVE = EDID_HA +// VESA_HSYNC_START = VESA_ACTIVE + VESA_BORDER + VESA_FRONT_PORCH +// = EDID_HA + EDID_HSO +// VESA_HSYNC_WIDTH = VESA_HSYNC_TIME = EDID_HSPW +// VESA_BORDER = EDID_BORDER + +/****************************************************************************/ +// Structure used in SetCRTC_UsingDTDTimingTable +/****************************************************************************/ +typedef struct _SET_CRTC_USING_DTD_TIMING_PARAMETERS +{ + USHORT usH_Size; + USHORT usH_Blanking_Time; + USHORT usV_Size; + USHORT usV_Blanking_Time; + USHORT usH_SyncOffset; + USHORT usH_SyncWidth; + USHORT usV_SyncOffset; + USHORT usV_SyncWidth; + ATOM_MODE_MISC_INFO_ACCESS susModeMiscInfo; + UCHAR ucH_Border; // From DFP EDID + UCHAR ucV_Border; + UCHAR ucCRTC; // ATOM_CRTC1 or ATOM_CRTC2 + UCHAR ucPadding[3]; +}SET_CRTC_USING_DTD_TIMING_PARAMETERS; + +/****************************************************************************/ +// Structure used in SetCRTC_TimingTable +/****************************************************************************/ +typedef struct _SET_CRTC_TIMING_PARAMETERS +{ + USHORT usH_Total; // horizontal total + USHORT usH_Disp; // horizontal display + USHORT usH_SyncStart; // horozontal Sync start + USHORT usH_SyncWidth; // horizontal Sync width + USHORT usV_Total; // vertical total + USHORT usV_Disp; // vertical display + USHORT usV_SyncStart; // vertical Sync start + USHORT usV_SyncWidth; // vertical Sync width + ATOM_MODE_MISC_INFO_ACCESS susModeMiscInfo; + UCHAR ucCRTC; // ATOM_CRTC1 or ATOM_CRTC2 + UCHAR ucOverscanRight; // right + UCHAR ucOverscanLeft; // left + UCHAR ucOverscanBottom; // bottom + UCHAR ucOverscanTop; // top + UCHAR ucReserved; +}SET_CRTC_TIMING_PARAMETERS; +#define SET_CRTC_TIMING_PARAMETERS_PS_ALLOCATION SET_CRTC_TIMING_PARAMETERS + +/****************************************************************************/ +// Structure used in StandardVESA_TimingTable +// AnalogTV_InfoTable +// ComponentVideoInfoTable +/****************************************************************************/ +typedef struct _ATOM_MODE_TIMING +{ + USHORT usCRTC_H_Total; + USHORT usCRTC_H_Disp; + USHORT usCRTC_H_SyncStart; + USHORT usCRTC_H_SyncWidth; + USHORT usCRTC_V_Total; + USHORT usCRTC_V_Disp; + USHORT usCRTC_V_SyncStart; + USHORT usCRTC_V_SyncWidth; + USHORT usPixelClock; //in 10Khz unit + ATOM_MODE_MISC_INFO_ACCESS susModeMiscInfo; + USHORT usCRTC_OverscanRight; + USHORT usCRTC_OverscanLeft; + USHORT usCRTC_OverscanBottom; + USHORT usCRTC_OverscanTop; + USHORT usReserve; + UCHAR ucInternalModeNumber; + UCHAR ucRefreshRate; +}ATOM_MODE_TIMING; + +typedef struct _ATOM_DTD_FORMAT +{ + USHORT usPixClk; + USHORT usHActive; + USHORT usHBlanking_Time; + USHORT usVActive; + USHORT usVBlanking_Time; + USHORT usHSyncOffset; + USHORT usHSyncWidth; + USHORT usVSyncOffset; + USHORT usVSyncWidth; + USHORT usImageHSize; + USHORT usImageVSize; + UCHAR ucHBorder; + UCHAR ucVBorder; + ATOM_MODE_MISC_INFO_ACCESS susModeMiscInfo; + UCHAR ucInternalModeNumber; + UCHAR ucRefreshRate; +}ATOM_DTD_FORMAT; + +/****************************************************************************/ +// Structure used in LVDS_InfoTable +// * Need a document to describe this table +/****************************************************************************/ +#define SUPPORTED_LCD_REFRESHRATE_30Hz 0x0004 +#define SUPPORTED_LCD_REFRESHRATE_40Hz 0x0008 +#define SUPPORTED_LCD_REFRESHRATE_50Hz 0x0010 +#define SUPPORTED_LCD_REFRESHRATE_60Hz 0x0020 + +//ucTableFormatRevision=1 +//ucTableContentRevision=1 +typedef struct _ATOM_LVDS_INFO +{ + ATOM_COMMON_TABLE_HEADER sHeader; + ATOM_DTD_FORMAT sLCDTiming; + USHORT usModePatchTableOffset; + USHORT usSupportedRefreshRate; //Refer to panel info table in ATOMBIOS extension Spec. + USHORT usOffDelayInMs; + UCHAR ucPowerSequenceDigOntoDEin10Ms; + UCHAR ucPowerSequenceDEtoBLOnin10Ms; + UCHAR ucLVDS_Misc; // Bit0:{=0:single, =1:dual},Bit1 {=0:666RGB, =1:888RGB},Bit2:3:{Grey level} + // Bit4:{=0:LDI format for RGB888, =1 FPDI format for RGB888} + // Bit5:{=0:Spatial Dithering disabled;1 Spatial Dithering enabled} + // Bit6:{=0:Temporal Dithering disabled;1 Temporal Dithering enabled} + UCHAR ucPanelDefaultRefreshRate; + UCHAR ucPanelIdentification; + UCHAR ucSS_Id; +}ATOM_LVDS_INFO; + +//ucTableFormatRevision=1 +//ucTableContentRevision=2 +typedef struct _ATOM_LVDS_INFO_V12 +{ + ATOM_COMMON_TABLE_HEADER sHeader; + ATOM_DTD_FORMAT sLCDTiming; + USHORT usExtInfoTableOffset; + USHORT usSupportedRefreshRate; //Refer to panel info table in ATOMBIOS extension Spec. + USHORT usOffDelayInMs; + UCHAR ucPowerSequenceDigOntoDEin10Ms; + UCHAR ucPowerSequenceDEtoBLOnin10Ms; + UCHAR ucLVDS_Misc; // Bit0:{=0:single, =1:dual},Bit1 {=0:666RGB, =1:888RGB},Bit2:3:{Grey level} + // Bit4:{=0:LDI format for RGB888, =1 FPDI format for RGB888} + // Bit5:{=0:Spatial Dithering disabled;1 Spatial Dithering enabled} + // Bit6:{=0:Temporal Dithering disabled;1 Temporal Dithering enabled} + UCHAR ucPanelDefaultRefreshRate; + UCHAR ucPanelIdentification; + UCHAR ucSS_Id; + USHORT usLCDVenderID; + USHORT usLCDProductID; + UCHAR ucLCDPanel_SpecialHandlingCap; + UCHAR ucPanelInfoSize; // start from ATOM_DTD_FORMAT to end of panel info, include ExtInfoTable + UCHAR ucReserved[2]; +}ATOM_LVDS_INFO_V12; + +//Definitions for ucLCDPanel_SpecialHandlingCap: + +//Once DAL sees this CAP is set, it will read EDID from LCD on its own instead of using sLCDTiming in ATOM_LVDS_INFO_V12. +//Other entries in ATOM_LVDS_INFO_V12 are still valid/useful to DAL +#define LCDPANEL_CAP_READ_EDID 0x1 + +//If a design supports DRR (dynamic refresh rate) on internal panels (LVDS or EDP), this cap is set in ucLCDPanel_SpecialHandlingCap together +//with multiple supported refresh rates@usSupportedRefreshRate. This cap should not be set when only slow refresh rate is supported (static +//refresh rate switch by SW. This is only valid from ATOM_LVDS_INFO_V12 +#define LCDPANEL_CAP_DRR_SUPPORTED 0x2 + +//Use this cap bit for a quick reference whether an embadded panel (LCD1 ) is LVDS or eDP. +#define LCDPANEL_CAP_eDP 0x4 + + +//Color Bit Depth definition in EDID V1.4 @BYTE 14h +//Bit 6 5 4 + // 0 0 0 - Color bit depth is undefined + // 0 0 1 - 6 Bits per Primary Color + // 0 1 0 - 8 Bits per Primary Color + // 0 1 1 - 10 Bits per Primary Color + // 1 0 0 - 12 Bits per Primary Color + // 1 0 1 - 14 Bits per Primary Color + // 1 1 0 - 16 Bits per Primary Color + // 1 1 1 - Reserved + +#define PANEL_COLOR_BIT_DEPTH_MASK 0x70 + +// Bit7:{=0:Random Dithering disabled;1 Random Dithering enabled} +#define PANEL_RANDOM_DITHER 0x80 +#define PANEL_RANDOM_DITHER_MASK 0x80 + +#define ATOM_LVDS_INFO_LAST ATOM_LVDS_INFO_V12 // no need to change this + +/****************************************************************************/ +// Structures used by LCD_InfoTable V1.3 Note: previous version was called ATOM_LVDS_INFO_V12 +// ASIC Families: NI +// ucTableFormatRevision=1 +// ucTableContentRevision=3 +/****************************************************************************/ +typedef struct _ATOM_LCD_INFO_V13 +{ + ATOM_COMMON_TABLE_HEADER sHeader; + ATOM_DTD_FORMAT sLCDTiming; + USHORT usExtInfoTableOffset; + USHORT usSupportedRefreshRate; //Refer to panel info table in ATOMBIOS extension Spec. + ULONG ulReserved0; + UCHAR ucLCD_Misc; // Reorganized in V13 + // Bit0: {=0:single, =1:dual}, + // Bit1: {=0:LDI format for RGB888, =1 FPDI format for RGB888} // was {=0:666RGB, =1:888RGB}, + // Bit3:2: {Grey level} + // Bit6:4 Color Bit Depth definition (see below definition in EDID V1.4 @BYTE 14h) + // Bit7 Reserved. was for ATOM_PANEL_MISC_API_ENABLED, still need it? + UCHAR ucPanelDefaultRefreshRate; + UCHAR ucPanelIdentification; + UCHAR ucSS_Id; + USHORT usLCDVenderID; + USHORT usLCDProductID; + UCHAR ucLCDPanel_SpecialHandlingCap; // Reorganized in V13 + // Bit0: Once DAL sees this CAP is set, it will read EDID from LCD on its own + // Bit1: See LCDPANEL_CAP_DRR_SUPPORTED + // Bit2: a quick reference whether an embadded panel (LCD1 ) is LVDS (0) or eDP (1) + // Bit7-3: Reserved + UCHAR ucPanelInfoSize; // start from ATOM_DTD_FORMAT to end of panel info, include ExtInfoTable + USHORT usBacklightPWM; // Backlight PWM in Hz. New in _V13 + + UCHAR ucPowerSequenceDIGONtoDE_in4Ms; + UCHAR ucPowerSequenceDEtoVARY_BL_in4Ms; + UCHAR ucPowerSequenceVARY_BLtoDE_in4Ms; + UCHAR ucPowerSequenceDEtoDIGON_in4Ms; + + UCHAR ucOffDelay_in4Ms; + UCHAR ucPowerSequenceVARY_BLtoBLON_in4Ms; + UCHAR ucPowerSequenceBLONtoVARY_BL_in4Ms; + UCHAR ucReserved1; + + UCHAR ucDPCD_eDP_CONFIGURATION_CAP; // dpcd 0dh + UCHAR ucDPCD_MAX_LINK_RATE; // dpcd 01h + UCHAR ucDPCD_MAX_LANE_COUNT; // dpcd 02h + UCHAR ucDPCD_MAX_DOWNSPREAD; // dpcd 03h + + USHORT usMaxPclkFreqInSingleLink; // Max PixelClock frequency in single link mode. + UCHAR uceDPToLVDSRxId; + UCHAR ucLcdReservd; + ULONG ulReserved[2]; +}ATOM_LCD_INFO_V13; + +#define ATOM_LCD_INFO_LAST ATOM_LCD_INFO_V13 + +//Definitions for ucLCD_Misc +#define ATOM_PANEL_MISC_V13_DUAL 0x00000001 +#define ATOM_PANEL_MISC_V13_FPDI 0x00000002 +#define ATOM_PANEL_MISC_V13_GREY_LEVEL 0x0000000C +#define ATOM_PANEL_MISC_V13_GREY_LEVEL_SHIFT 2 +#define ATOM_PANEL_MISC_V13_COLOR_BIT_DEPTH_MASK 0x70 +#define ATOM_PANEL_MISC_V13_6BIT_PER_COLOR 0x10 +#define ATOM_PANEL_MISC_V13_8BIT_PER_COLOR 0x20 + +//Color Bit Depth definition in EDID V1.4 @BYTE 14h +//Bit 6 5 4 + // 0 0 0 - Color bit depth is undefined + // 0 0 1 - 6 Bits per Primary Color + // 0 1 0 - 8 Bits per Primary Color + // 0 1 1 - 10 Bits per Primary Color + // 1 0 0 - 12 Bits per Primary Color + // 1 0 1 - 14 Bits per Primary Color + // 1 1 0 - 16 Bits per Primary Color + // 1 1 1 - Reserved + +//Definitions for ucLCDPanel_SpecialHandlingCap: + +//Once DAL sees this CAP is set, it will read EDID from LCD on its own instead of using sLCDTiming in ATOM_LVDS_INFO_V12. +//Other entries in ATOM_LVDS_INFO_V12 are still valid/useful to DAL +#define LCDPANEL_CAP_V13_READ_EDID 0x1 // = LCDPANEL_CAP_READ_EDID no change comparing to previous version + +//If a design supports DRR (dynamic refresh rate) on internal panels (LVDS or EDP), this cap is set in ucLCDPanel_SpecialHandlingCap together +//with multiple supported refresh rates@usSupportedRefreshRate. This cap should not be set when only slow refresh rate is supported (static +//refresh rate switch by SW. This is only valid from ATOM_LVDS_INFO_V12 +#define LCDPANEL_CAP_V13_DRR_SUPPORTED 0x2 // = LCDPANEL_CAP_DRR_SUPPORTED no change comparing to previous version + +//Use this cap bit for a quick reference whether an embadded panel (LCD1 ) is LVDS or eDP. +#define LCDPANEL_CAP_V13_eDP 0x4 // = LCDPANEL_CAP_eDP no change comparing to previous version + +//uceDPToLVDSRxId +#define eDP_TO_LVDS_RX_DISABLE 0x00 // no eDP->LVDS translator chip +#define eDP_TO_LVDS_COMMON_ID 0x01 // common eDP->LVDS translator chip without AMD SW init +#define eDP_TO_LVDS_RT_ID 0x02 // RT tanslator which require AMD SW init + +typedef struct _ATOM_PATCH_RECORD_MODE +{ + UCHAR ucRecordType; + USHORT usHDisp; + USHORT usVDisp; +}ATOM_PATCH_RECORD_MODE; + +typedef struct _ATOM_LCD_RTS_RECORD +{ + UCHAR ucRecordType; + UCHAR ucRTSValue; +}ATOM_LCD_RTS_RECORD; + +//!! If the record below exits, it shoud always be the first record for easy use in command table!!! +// The record below is only used when LVDS_Info is present. From ATOM_LVDS_INFO_V12, use ucLCDPanel_SpecialHandlingCap instead. +typedef struct _ATOM_LCD_MODE_CONTROL_CAP +{ + UCHAR ucRecordType; + USHORT usLCDCap; +}ATOM_LCD_MODE_CONTROL_CAP; + +#define LCD_MODE_CAP_BL_OFF 1 +#define LCD_MODE_CAP_CRTC_OFF 2 +#define LCD_MODE_CAP_PANEL_OFF 4 + +typedef struct _ATOM_FAKE_EDID_PATCH_RECORD +{ + UCHAR ucRecordType; + UCHAR ucFakeEDIDLength; + UCHAR ucFakeEDIDString[1]; // This actually has ucFakeEdidLength elements. +} ATOM_FAKE_EDID_PATCH_RECORD; + +typedef struct _ATOM_PANEL_RESOLUTION_PATCH_RECORD +{ + UCHAR ucRecordType; + USHORT usHSize; + USHORT usVSize; +}ATOM_PANEL_RESOLUTION_PATCH_RECORD; + +#define LCD_MODE_PATCH_RECORD_MODE_TYPE 1 +#define LCD_RTS_RECORD_TYPE 2 +#define LCD_CAP_RECORD_TYPE 3 +#define LCD_FAKE_EDID_PATCH_RECORD_TYPE 4 +#define LCD_PANEL_RESOLUTION_RECORD_TYPE 5 +#define LCD_EDID_OFFSET_PATCH_RECORD_TYPE 6 +#define ATOM_RECORD_END_TYPE 0xFF + +/****************************Spread Spectrum Info Table Definitions **********************/ + +//ucTableFormatRevision=1 +//ucTableContentRevision=2 +typedef struct _ATOM_SPREAD_SPECTRUM_ASSIGNMENT +{ + USHORT usSpreadSpectrumPercentage; + UCHAR ucSpreadSpectrumType; //Bit1=0 Down Spread,=1 Center Spread. Bit1=1 Ext. =0 Int. Bit2=1: PCIE REFCLK SS =0 iternal PPLL SS Others:TBD + UCHAR ucSS_Step; + UCHAR ucSS_Delay; + UCHAR ucSS_Id; + UCHAR ucRecommendedRef_Div; + UCHAR ucSS_Range; //it was reserved for V11 +}ATOM_SPREAD_SPECTRUM_ASSIGNMENT; + +#define ATOM_MAX_SS_ENTRY 16 +#define ATOM_DP_SS_ID1 0x0f1 // SS ID for internal DP stream at 2.7Ghz. if ATOM_DP_SS_ID2 does not exist in SS_InfoTable, it is used for internal DP stream at 1.62Ghz as well. +#define ATOM_DP_SS_ID2 0x0f2 // SS ID for internal DP stream at 1.62Ghz, if it exists in SS_InfoTable. +#define ATOM_LVLINK_2700MHz_SS_ID 0x0f3 // SS ID for LV link translator chip at 2.7Ghz +#define ATOM_LVLINK_1620MHz_SS_ID 0x0f4 // SS ID for LV link translator chip at 1.62Ghz + + +#define ATOM_SS_DOWN_SPREAD_MODE_MASK 0x00000000 +#define ATOM_SS_DOWN_SPREAD_MODE 0x00000000 +#define ATOM_SS_CENTRE_SPREAD_MODE_MASK 0x00000001 +#define ATOM_SS_CENTRE_SPREAD_MODE 0x00000001 +#define ATOM_INTERNAL_SS_MASK 0x00000000 +#define ATOM_EXTERNAL_SS_MASK 0x00000002 +#define EXEC_SS_STEP_SIZE_SHIFT 2 +#define EXEC_SS_DELAY_SHIFT 4 +#define ACTIVEDATA_TO_BLON_DELAY_SHIFT 4 + +typedef struct _ATOM_SPREAD_SPECTRUM_INFO +{ + ATOM_COMMON_TABLE_HEADER sHeader; + ATOM_SPREAD_SPECTRUM_ASSIGNMENT asSS_Info[ATOM_MAX_SS_ENTRY]; +}ATOM_SPREAD_SPECTRUM_INFO; + +/****************************************************************************/ +// Structure used in AnalogTV_InfoTable (Top level) +/****************************************************************************/ +//ucTVBootUpDefaultStd definition: + +//ATOM_TV_NTSC 1 +//ATOM_TV_NTSCJ 2 +//ATOM_TV_PAL 3 +//ATOM_TV_PALM 4 +//ATOM_TV_PALCN 5 +//ATOM_TV_PALN 6 +//ATOM_TV_PAL60 7 +//ATOM_TV_SECAM 8 + +//ucTVSupportedStd definition: +#define NTSC_SUPPORT 0x1 +#define NTSCJ_SUPPORT 0x2 + +#define PAL_SUPPORT 0x4 +#define PALM_SUPPORT 0x8 +#define PALCN_SUPPORT 0x10 +#define PALN_SUPPORT 0x20 +#define PAL60_SUPPORT 0x40 +#define SECAM_SUPPORT 0x80 + +#define MAX_SUPPORTED_TV_TIMING 2 + +typedef struct _ATOM_ANALOG_TV_INFO +{ + ATOM_COMMON_TABLE_HEADER sHeader; + UCHAR ucTV_SupportedStandard; + UCHAR ucTV_BootUpDefaultStandard; + UCHAR ucExt_TV_ASIC_ID; + UCHAR ucExt_TV_ASIC_SlaveAddr; + /*ATOM_DTD_FORMAT aModeTimings[MAX_SUPPORTED_TV_TIMING];*/ + ATOM_MODE_TIMING aModeTimings[MAX_SUPPORTED_TV_TIMING]; +}ATOM_ANALOG_TV_INFO; + +#define MAX_SUPPORTED_TV_TIMING_V1_2 3 + +typedef struct _ATOM_ANALOG_TV_INFO_V1_2 +{ + ATOM_COMMON_TABLE_HEADER sHeader; + UCHAR ucTV_SupportedStandard; + UCHAR ucTV_BootUpDefaultStandard; + UCHAR ucExt_TV_ASIC_ID; + UCHAR ucExt_TV_ASIC_SlaveAddr; + ATOM_DTD_FORMAT aModeTimings[MAX_SUPPORTED_TV_TIMING_V1_2]; +}ATOM_ANALOG_TV_INFO_V1_2; + +typedef struct _ATOM_DPCD_INFO +{ + UCHAR ucRevisionNumber; //10h : Revision 1.0; 11h : Revision 1.1 + UCHAR ucMaxLinkRate; //06h : 1.62Gbps per lane; 0Ah = 2.7Gbps per lane + UCHAR ucMaxLane; //Bits 4:0 = MAX_LANE_COUNT (1/2/4). Bit 7 = ENHANCED_FRAME_CAP + UCHAR ucMaxDownSpread; //Bit0 = 0: No Down spread; Bit0 = 1: 0.5% (Subject to change according to DP spec) +}ATOM_DPCD_INFO; + +#define ATOM_DPCD_MAX_LANE_MASK 0x1F + +/**************************************************************************/ +// VRAM usage and their defintions + +// One chunk of VRAM used by Bios are for HWICON surfaces,EDID data. +// Current Mode timing and Dail Timing and/or STD timing data EACH device. They can be broken down as below. +// All the addresses below are the offsets from the frame buffer start.They all MUST be Dword aligned! +// To driver: The physical address of this memory portion=mmFB_START(4K aligned)+ATOMBIOS_VRAM_USAGE_START_ADDR+ATOM_x_ADDR +// To Bios: ATOMBIOS_VRAM_USAGE_START_ADDR+ATOM_x_ADDR->MM_INDEX + +#ifndef VESA_MEMORY_IN_64K_BLOCK +#define VESA_MEMORY_IN_64K_BLOCK 0x100 //256*64K=16Mb (Max. VESA memory is 16Mb!) +#endif + +#define ATOM_EDID_RAW_DATASIZE 256 //In Bytes +#define ATOM_HWICON_SURFACE_SIZE 4096 //In Bytes +#define ATOM_HWICON_INFOTABLE_SIZE 32 +#define MAX_DTD_MODE_IN_VRAM 6 +#define ATOM_DTD_MODE_SUPPORT_TBL_SIZE (MAX_DTD_MODE_IN_VRAM*28) //28= (SIZEOF ATOM_DTD_FORMAT) +#define ATOM_STD_MODE_SUPPORT_TBL_SIZE 32*8 //32 is a predefined number,8= (SIZEOF ATOM_STD_FORMAT) +//20 bytes for Encoder Type and DPCD in STD EDID area +#define DFP_ENCODER_TYPE_OFFSET (ATOM_EDID_RAW_DATASIZE + ATOM_DTD_MODE_SUPPORT_TBL_SIZE + ATOM_STD_MODE_SUPPORT_TBL_SIZE - 20) +#define ATOM_DP_DPCD_OFFSET (DFP_ENCODER_TYPE_OFFSET + 4 ) + +#define ATOM_HWICON1_SURFACE_ADDR 0 +#define ATOM_HWICON2_SURFACE_ADDR (ATOM_HWICON1_SURFACE_ADDR + ATOM_HWICON_SURFACE_SIZE) +#define ATOM_HWICON_INFOTABLE_ADDR (ATOM_HWICON2_SURFACE_ADDR + ATOM_HWICON_SURFACE_SIZE) +#define ATOM_CRT1_EDID_ADDR (ATOM_HWICON_INFOTABLE_ADDR + ATOM_HWICON_INFOTABLE_SIZE) +#define ATOM_CRT1_DTD_MODE_TBL_ADDR (ATOM_CRT1_EDID_ADDR + ATOM_EDID_RAW_DATASIZE) +#define ATOM_CRT1_STD_MODE_TBL_ADDR (ATOM_CRT1_DTD_MODE_TBL_ADDR + ATOM_DTD_MODE_SUPPORT_TBL_SIZE) + +#define ATOM_LCD1_EDID_ADDR (ATOM_CRT1_STD_MODE_TBL_ADDR + ATOM_STD_MODE_SUPPORT_TBL_SIZE) +#define ATOM_LCD1_DTD_MODE_TBL_ADDR (ATOM_LCD1_EDID_ADDR + ATOM_EDID_RAW_DATASIZE) +#define ATOM_LCD1_STD_MODE_TBL_ADDR (ATOM_LCD1_DTD_MODE_TBL_ADDR + ATOM_DTD_MODE_SUPPORT_TBL_SIZE) + +#define ATOM_TV1_DTD_MODE_TBL_ADDR (ATOM_LCD1_STD_MODE_TBL_ADDR + ATOM_STD_MODE_SUPPORT_TBL_SIZE) + +#define ATOM_DFP1_EDID_ADDR (ATOM_TV1_DTD_MODE_TBL_ADDR + ATOM_DTD_MODE_SUPPORT_TBL_SIZE) +#define ATOM_DFP1_DTD_MODE_TBL_ADDR (ATOM_DFP1_EDID_ADDR + ATOM_EDID_RAW_DATASIZE) +#define ATOM_DFP1_STD_MODE_TBL_ADDR (ATOM_DFP1_DTD_MODE_TBL_ADDR + ATOM_DTD_MODE_SUPPORT_TBL_SIZE) + +#define ATOM_CRT2_EDID_ADDR (ATOM_DFP1_STD_MODE_TBL_ADDR + ATOM_STD_MODE_SUPPORT_TBL_SIZE) +#define ATOM_CRT2_DTD_MODE_TBL_ADDR (ATOM_CRT2_EDID_ADDR + ATOM_EDID_RAW_DATASIZE) +#define ATOM_CRT2_STD_MODE_TBL_ADDR (ATOM_CRT2_DTD_MODE_TBL_ADDR + ATOM_DTD_MODE_SUPPORT_TBL_SIZE) + +#define ATOM_LCD2_EDID_ADDR (ATOM_CRT2_STD_MODE_TBL_ADDR + ATOM_STD_MODE_SUPPORT_TBL_SIZE) +#define ATOM_LCD2_DTD_MODE_TBL_ADDR (ATOM_LCD2_EDID_ADDR + ATOM_EDID_RAW_DATASIZE) +#define ATOM_LCD2_STD_MODE_TBL_ADDR (ATOM_LCD2_DTD_MODE_TBL_ADDR + ATOM_DTD_MODE_SUPPORT_TBL_SIZE) + +#define ATOM_DFP6_EDID_ADDR (ATOM_LCD2_STD_MODE_TBL_ADDR + ATOM_STD_MODE_SUPPORT_TBL_SIZE) +#define ATOM_DFP6_DTD_MODE_TBL_ADDR (ATOM_DFP6_EDID_ADDR + ATOM_EDID_RAW_DATASIZE) +#define ATOM_DFP6_STD_MODE_TBL_ADDR (ATOM_DFP6_DTD_MODE_TBL_ADDR + ATOM_DTD_MODE_SUPPORT_TBL_SIZE) + +#define ATOM_DFP2_EDID_ADDR (ATOM_DFP6_STD_MODE_TBL_ADDR + ATOM_STD_MODE_SUPPORT_TBL_SIZE) +#define ATOM_DFP2_DTD_MODE_TBL_ADDR (ATOM_DFP2_EDID_ADDR + ATOM_EDID_RAW_DATASIZE) +#define ATOM_DFP2_STD_MODE_TBL_ADDR (ATOM_DFP2_DTD_MODE_TBL_ADDR + ATOM_DTD_MODE_SUPPORT_TBL_SIZE) + +#define ATOM_CV_EDID_ADDR (ATOM_DFP2_STD_MODE_TBL_ADDR + ATOM_STD_MODE_SUPPORT_TBL_SIZE) +#define ATOM_CV_DTD_MODE_TBL_ADDR (ATOM_CV_EDID_ADDR + ATOM_EDID_RAW_DATASIZE) +#define ATOM_CV_STD_MODE_TBL_ADDR (ATOM_CV_DTD_MODE_TBL_ADDR + ATOM_DTD_MODE_SUPPORT_TBL_SIZE) + +#define ATOM_DFP3_EDID_ADDR (ATOM_CV_STD_MODE_TBL_ADDR + ATOM_STD_MODE_SUPPORT_TBL_SIZE) +#define ATOM_DFP3_DTD_MODE_TBL_ADDR (ATOM_DFP3_EDID_ADDR + ATOM_EDID_RAW_DATASIZE) +#define ATOM_DFP3_STD_MODE_TBL_ADDR (ATOM_DFP3_DTD_MODE_TBL_ADDR + ATOM_DTD_MODE_SUPPORT_TBL_SIZE) + +#define ATOM_DFP4_EDID_ADDR (ATOM_DFP3_STD_MODE_TBL_ADDR + ATOM_STD_MODE_SUPPORT_TBL_SIZE) +#define ATOM_DFP4_DTD_MODE_TBL_ADDR (ATOM_DFP4_EDID_ADDR + ATOM_EDID_RAW_DATASIZE) +#define ATOM_DFP4_STD_MODE_TBL_ADDR (ATOM_DFP4_DTD_MODE_TBL_ADDR + ATOM_DTD_MODE_SUPPORT_TBL_SIZE) + +#define ATOM_DFP5_EDID_ADDR (ATOM_DFP4_STD_MODE_TBL_ADDR + ATOM_STD_MODE_SUPPORT_TBL_SIZE) +#define ATOM_DFP5_DTD_MODE_TBL_ADDR (ATOM_DFP5_EDID_ADDR + ATOM_EDID_RAW_DATASIZE) +#define ATOM_DFP5_STD_MODE_TBL_ADDR (ATOM_DFP5_DTD_MODE_TBL_ADDR + ATOM_DTD_MODE_SUPPORT_TBL_SIZE) + +#define ATOM_DP_TRAINING_TBL_ADDR (ATOM_DFP5_STD_MODE_TBL_ADDR + ATOM_STD_MODE_SUPPORT_TBL_SIZE) + +#define ATOM_STACK_STORAGE_START (ATOM_DP_TRAINING_TBL_ADDR + 1024) +#define ATOM_STACK_STORAGE_END ATOM_STACK_STORAGE_START + 512 + +//The size below is in Kb! +#define ATOM_VRAM_RESERVE_SIZE ((((ATOM_STACK_STORAGE_END - ATOM_HWICON1_SURFACE_ADDR)>>10)+4)&0xFFFC) + +#define ATOM_VRAM_RESERVE_V2_SIZE 32 + +#define ATOM_VRAM_OPERATION_FLAGS_MASK 0xC0000000L +#define ATOM_VRAM_OPERATION_FLAGS_SHIFT 30 +#define ATOM_VRAM_BLOCK_NEEDS_NO_RESERVATION 0x1 +#define ATOM_VRAM_BLOCK_NEEDS_RESERVATION 0x0 + +/***********************************************************************************/ +// Structure used in VRAM_UsageByFirmwareTable +// Note1: This table is filled by SetBiosReservationStartInFB in CoreCommSubs.asm +// at running time. +// note2: From RV770, the memory is more than 32bit addressable, so we will change +// ucTableFormatRevision=1,ucTableContentRevision=4, the strcuture remains +// exactly same as 1.1 and 1.2 (1.3 is never in use), but ulStartAddrUsedByFirmware +// (in offset to start of memory address) is KB aligned instead of byte aligend. +/***********************************************************************************/ +// Note3: +/* If we change usReserved to "usFBUsedbyDrvInKB", then to VBIOS this usFBUsedbyDrvInKB is a predefined, unchanged constant across VGA or non VGA adapter, +for CAIL, The size of FB access area is known, only thing missing is the Offset of FB Access area, so we can have: + +If (ulStartAddrUsedByFirmware!=0) +FBAccessAreaOffset= ulStartAddrUsedByFirmware - usFBUsedbyDrvInKB; +Reserved area has been claimed by VBIOS including this FB access area; CAIL doesn't need to reserve any extra area for this purpose +else //Non VGA case + if (FB_Size<=2Gb) + FBAccessAreaOffset= FB_Size - usFBUsedbyDrvInKB; + else + FBAccessAreaOffset= Aper_Size - usFBUsedbyDrvInKB + +CAIL needs to claim an reserved area defined by FBAccessAreaOffset and usFBUsedbyDrvInKB in non VGA case.*/ + +/***********************************************************************************/ +#define ATOM_MAX_FIRMWARE_VRAM_USAGE_INFO 1 + +typedef struct _ATOM_FIRMWARE_VRAM_RESERVE_INFO +{ + ULONG ulStartAddrUsedByFirmware; + USHORT usFirmwareUseInKb; + USHORT usReserved; +}ATOM_FIRMWARE_VRAM_RESERVE_INFO; + +typedef struct _ATOM_VRAM_USAGE_BY_FIRMWARE +{ + ATOM_COMMON_TABLE_HEADER sHeader; + ATOM_FIRMWARE_VRAM_RESERVE_INFO asFirmwareVramReserveInfo[ATOM_MAX_FIRMWARE_VRAM_USAGE_INFO]; +}ATOM_VRAM_USAGE_BY_FIRMWARE; + +// change verion to 1.5, when allow driver to allocate the vram area for command table access. +typedef struct _ATOM_FIRMWARE_VRAM_RESERVE_INFO_V1_5 +{ + ULONG ulStartAddrUsedByFirmware; + USHORT usFirmwareUseInKb; + USHORT usFBUsedByDrvInKb; +}ATOM_FIRMWARE_VRAM_RESERVE_INFO_V1_5; + +typedef struct _ATOM_VRAM_USAGE_BY_FIRMWARE_V1_5 +{ + ATOM_COMMON_TABLE_HEADER sHeader; + ATOM_FIRMWARE_VRAM_RESERVE_INFO_V1_5 asFirmwareVramReserveInfo[ATOM_MAX_FIRMWARE_VRAM_USAGE_INFO]; +}ATOM_VRAM_USAGE_BY_FIRMWARE_V1_5; + +/****************************************************************************/ +// Structure used in GPIO_Pin_LUTTable +/****************************************************************************/ +typedef struct _ATOM_GPIO_PIN_ASSIGNMENT +{ + USHORT usGpioPin_AIndex; + UCHAR ucGpioPinBitShift; + UCHAR ucGPIO_ID; +}ATOM_GPIO_PIN_ASSIGNMENT; + +typedef struct _ATOM_GPIO_PIN_LUT +{ + ATOM_COMMON_TABLE_HEADER sHeader; + ATOM_GPIO_PIN_ASSIGNMENT asGPIO_Pin[1]; +}ATOM_GPIO_PIN_LUT; + +/****************************************************************************/ +// Structure used in ComponentVideoInfoTable +/****************************************************************************/ +#define GPIO_PIN_ACTIVE_HIGH 0x1 + +#define MAX_SUPPORTED_CV_STANDARDS 5 + +// definitions for ATOM_D_INFO.ucSettings +#define ATOM_GPIO_SETTINGS_BITSHIFT_MASK 0x1F // [4:0] +#define ATOM_GPIO_SETTINGS_RESERVED_MASK 0x60 // [6:5] = must be zeroed out +#define ATOM_GPIO_SETTINGS_ACTIVE_MASK 0x80 // [7] + +typedef struct _ATOM_GPIO_INFO +{ + USHORT usAOffset; + UCHAR ucSettings; + UCHAR ucReserved; +}ATOM_GPIO_INFO; + +// definitions for ATOM_COMPONENT_VIDEO_INFO.ucMiscInfo (bit vector) +#define ATOM_CV_RESTRICT_FORMAT_SELECTION 0x2 + +// definitions for ATOM_COMPONENT_VIDEO_INFO.uc480i/uc480p/uc720p/uc1080i +#define ATOM_GPIO_DEFAULT_MODE_EN 0x80 //[7]; +#define ATOM_GPIO_SETTING_PERMODE_MASK 0x7F //[6:0] + +// definitions for ATOM_COMPONENT_VIDEO_INFO.ucLetterBoxMode +//Line 3 out put 5V. +#define ATOM_CV_LINE3_ASPECTRATIO_16_9_GPIO_A 0x01 //represent gpio 3 state for 16:9 +#define ATOM_CV_LINE3_ASPECTRATIO_16_9_GPIO_B 0x02 //represent gpio 4 state for 16:9 +#define ATOM_CV_LINE3_ASPECTRATIO_16_9_GPIO_SHIFT 0x0 + +//Line 3 out put 2.2V +#define ATOM_CV_LINE3_ASPECTRATIO_4_3_LETBOX_GPIO_A 0x04 //represent gpio 3 state for 4:3 Letter box +#define ATOM_CV_LINE3_ASPECTRATIO_4_3_LETBOX_GPIO_B 0x08 //represent gpio 4 state for 4:3 Letter box +#define ATOM_CV_LINE3_ASPECTRATIO_4_3_LETBOX_GPIO_SHIFT 0x2 + +//Line 3 out put 0V +#define ATOM_CV_LINE3_ASPECTRATIO_4_3_GPIO_A 0x10 //represent gpio 3 state for 4:3 +#define ATOM_CV_LINE3_ASPECTRATIO_4_3_GPIO_B 0x20 //represent gpio 4 state for 4:3 +#define ATOM_CV_LINE3_ASPECTRATIO_4_3_GPIO_SHIFT 0x4 + +#define ATOM_CV_LINE3_ASPECTRATIO_MASK 0x3F // bit [5:0] + +#define ATOM_CV_LINE3_ASPECTRATIO_EXIST 0x80 //bit 7 + +//GPIO bit index in gpio setting per mode value, also represend the block no. in gpio blocks. +#define ATOM_GPIO_INDEX_LINE3_ASPECRATIO_GPIO_A 3 //bit 3 in uc480i/uc480p/uc720p/uc1080i, which represend the default gpio bit setting for the mode. +#define ATOM_GPIO_INDEX_LINE3_ASPECRATIO_GPIO_B 4 //bit 4 in uc480i/uc480p/uc720p/uc1080i, which represend the default gpio bit setting for the mode. + + +typedef struct _ATOM_COMPONENT_VIDEO_INFO +{ + ATOM_COMMON_TABLE_HEADER sHeader; + USHORT usMask_PinRegisterIndex; + USHORT usEN_PinRegisterIndex; + USHORT usY_PinRegisterIndex; + USHORT usA_PinRegisterIndex; + UCHAR ucBitShift; + UCHAR ucPinActiveState; //ucPinActiveState: Bit0=1 active high, =0 active low + ATOM_DTD_FORMAT sReserved; // must be zeroed out + UCHAR ucMiscInfo; + UCHAR uc480i; + UCHAR uc480p; + UCHAR uc720p; + UCHAR uc1080i; + UCHAR ucLetterBoxMode; + UCHAR ucReserved[3]; + UCHAR ucNumOfWbGpioBlocks; //For Component video D-Connector support. If zere, NTSC type connector + ATOM_GPIO_INFO aWbGpioStateBlock[MAX_SUPPORTED_CV_STANDARDS]; + ATOM_DTD_FORMAT aModeTimings[MAX_SUPPORTED_CV_STANDARDS]; +}ATOM_COMPONENT_VIDEO_INFO; + +//ucTableFormatRevision=2 +//ucTableContentRevision=1 +typedef struct _ATOM_COMPONENT_VIDEO_INFO_V21 +{ + ATOM_COMMON_TABLE_HEADER sHeader; + UCHAR ucMiscInfo; + UCHAR uc480i; + UCHAR uc480p; + UCHAR uc720p; + UCHAR uc1080i; + UCHAR ucReserved; + UCHAR ucLetterBoxMode; + UCHAR ucNumOfWbGpioBlocks; //For Component video D-Connector support. If zere, NTSC type connector + ATOM_GPIO_INFO aWbGpioStateBlock[MAX_SUPPORTED_CV_STANDARDS]; + ATOM_DTD_FORMAT aModeTimings[MAX_SUPPORTED_CV_STANDARDS]; +}ATOM_COMPONENT_VIDEO_INFO_V21; + +#define ATOM_COMPONENT_VIDEO_INFO_LAST ATOM_COMPONENT_VIDEO_INFO_V21 + +/****************************************************************************/ +// Structure used in object_InfoTable +/****************************************************************************/ +typedef struct _ATOM_OBJECT_HEADER +{ + ATOM_COMMON_TABLE_HEADER sHeader; + USHORT usDeviceSupport; + USHORT usConnectorObjectTableOffset; + USHORT usRouterObjectTableOffset; + USHORT usEncoderObjectTableOffset; + USHORT usProtectionObjectTableOffset; //only available when Protection block is independent. + USHORT usDisplayPathTableOffset; +}ATOM_OBJECT_HEADER; + +typedef struct _ATOM_OBJECT_HEADER_V3 +{ + ATOM_COMMON_TABLE_HEADER sHeader; + USHORT usDeviceSupport; + USHORT usConnectorObjectTableOffset; + USHORT usRouterObjectTableOffset; + USHORT usEncoderObjectTableOffset; + USHORT usProtectionObjectTableOffset; //only available when Protection block is independent. + USHORT usDisplayPathTableOffset; + USHORT usMiscObjectTableOffset; +}ATOM_OBJECT_HEADER_V3; + +typedef struct _ATOM_DISPLAY_OBJECT_PATH +{ + USHORT usDeviceTag; //supported device + USHORT usSize; //the size of ATOM_DISPLAY_OBJECT_PATH + USHORT usConnObjectId; //Connector Object ID + USHORT usGPUObjectId; //GPU ID + USHORT usGraphicObjIds[1]; //1st Encoder Obj source from GPU to last Graphic Obj destinate to connector. +}ATOM_DISPLAY_OBJECT_PATH; + +typedef struct _ATOM_DISPLAY_EXTERNAL_OBJECT_PATH +{ + USHORT usDeviceTag; //supported device + USHORT usSize; //the size of ATOM_DISPLAY_OBJECT_PATH + USHORT usConnObjectId; //Connector Object ID + USHORT usGPUObjectId; //GPU ID + USHORT usGraphicObjIds[2]; //usGraphicObjIds[0]= GPU internal encoder, usGraphicObjIds[1]= external encoder +}ATOM_DISPLAY_EXTERNAL_OBJECT_PATH; + +typedef struct _ATOM_DISPLAY_OBJECT_PATH_TABLE +{ + UCHAR ucNumOfDispPath; + UCHAR ucVersion; + UCHAR ucPadding[2]; + ATOM_DISPLAY_OBJECT_PATH asDispPath[1]; +}ATOM_DISPLAY_OBJECT_PATH_TABLE; + + +typedef struct _ATOM_OBJECT //each object has this structure +{ + USHORT usObjectID; + USHORT usSrcDstTableOffset; + USHORT usRecordOffset; //this pointing to a bunch of records defined below + USHORT usReserved; +}ATOM_OBJECT; + +typedef struct _ATOM_OBJECT_TABLE //Above 4 object table offset pointing to a bunch of objects all have this structure +{ + UCHAR ucNumberOfObjects; + UCHAR ucPadding[3]; + ATOM_OBJECT asObjects[1]; +}ATOM_OBJECT_TABLE; + +typedef struct _ATOM_SRC_DST_TABLE_FOR_ONE_OBJECT //usSrcDstTableOffset pointing to this structure +{ + UCHAR ucNumberOfSrc; + USHORT usSrcObjectID[1]; + UCHAR ucNumberOfDst; + USHORT usDstObjectID[1]; +}ATOM_SRC_DST_TABLE_FOR_ONE_OBJECT; + + +//Two definitions below are for OPM on MXM module designs + +#define EXT_HPDPIN_LUTINDEX_0 0 +#define EXT_HPDPIN_LUTINDEX_1 1 +#define EXT_HPDPIN_LUTINDEX_2 2 +#define EXT_HPDPIN_LUTINDEX_3 3 +#define EXT_HPDPIN_LUTINDEX_4 4 +#define EXT_HPDPIN_LUTINDEX_5 5 +#define EXT_HPDPIN_LUTINDEX_6 6 +#define EXT_HPDPIN_LUTINDEX_7 7 +#define MAX_NUMBER_OF_EXT_HPDPIN_LUT_ENTRIES (EXT_HPDPIN_LUTINDEX_7+1) + +#define EXT_AUXDDC_LUTINDEX_0 0 +#define EXT_AUXDDC_LUTINDEX_1 1 +#define EXT_AUXDDC_LUTINDEX_2 2 +#define EXT_AUXDDC_LUTINDEX_3 3 +#define EXT_AUXDDC_LUTINDEX_4 4 +#define EXT_AUXDDC_LUTINDEX_5 5 +#define EXT_AUXDDC_LUTINDEX_6 6 +#define EXT_AUXDDC_LUTINDEX_7 7 +#define MAX_NUMBER_OF_EXT_AUXDDC_LUT_ENTRIES (EXT_AUXDDC_LUTINDEX_7+1) + +//ucChannelMapping are defined as following +//for DP connector, eDP, DP to VGA/LVDS +//Bit[1:0]: Define which pin connect to DP connector DP_Lane0, =0: source from GPU pin TX0, =1: from GPU pin TX1, =2: from GPU pin TX2, =3 from GPU pin TX3 +//Bit[3:2]: Define which pin connect to DP connector DP_Lane1, =0: source from GPU pin TX0, =1: from GPU pin TX1, =2: from GPU pin TX2, =3 from GPU pin TX3 +//Bit[5:4]: Define which pin connect to DP connector DP_Lane2, =0: source from GPU pin TX0, =1: from GPU pin TX1, =2: from GPU pin TX2, =3 from GPU pin TX3 +//Bit[7:6]: Define which pin connect to DP connector DP_Lane3, =0: source from GPU pin TX0, =1: from GPU pin TX1, =2: from GPU pin TX2, =3 from GPU pin TX3 +typedef struct _ATOM_DP_CONN_CHANNEL_MAPPING +{ +#if ATOM_BIG_ENDIAN + UCHAR ucDP_Lane3_Source:2; + UCHAR ucDP_Lane2_Source:2; + UCHAR ucDP_Lane1_Source:2; + UCHAR ucDP_Lane0_Source:2; +#else + UCHAR ucDP_Lane0_Source:2; + UCHAR ucDP_Lane1_Source:2; + UCHAR ucDP_Lane2_Source:2; + UCHAR ucDP_Lane3_Source:2; +#endif +}ATOM_DP_CONN_CHANNEL_MAPPING; + +//for DVI/HDMI, in dual link case, both links have to have same mapping. +//Bit[1:0]: Define which pin connect to DVI connector data Lane2, =0: source from GPU pin TX0, =1: from GPU pin TX1, =2: from GPU pin TX2, =3 from GPU pin TX3 +//Bit[3:2]: Define which pin connect to DVI connector data Lane1, =0: source from GPU pin TX0, =1: from GPU pin TX1, =2: from GPU pin TX2, =3 from GPU pin TX3 +//Bit[5:4]: Define which pin connect to DVI connector data Lane0, =0: source from GPU pin TX0, =1: from GPU pin TX1, =2: from GPU pin TX2, =3 from GPU pin TX3 +//Bit[7:6]: Define which pin connect to DVI connector clock lane, =0: source from GPU pin TX0, =1: from GPU pin TX1, =2: from GPU pin TX2, =3 from GPU pin TX3 +typedef struct _ATOM_DVI_CONN_CHANNEL_MAPPING +{ +#if ATOM_BIG_ENDIAN + UCHAR ucDVI_CLK_Source:2; + UCHAR ucDVI_DATA0_Source:2; + UCHAR ucDVI_DATA1_Source:2; + UCHAR ucDVI_DATA2_Source:2; +#else + UCHAR ucDVI_DATA2_Source:2; + UCHAR ucDVI_DATA1_Source:2; + UCHAR ucDVI_DATA0_Source:2; + UCHAR ucDVI_CLK_Source:2; +#endif +}ATOM_DVI_CONN_CHANNEL_MAPPING; + +typedef struct _EXT_DISPLAY_PATH +{ + USHORT usDeviceTag; //A bit vector to show what devices are supported + USHORT usDeviceACPIEnum; //16bit device ACPI id. + USHORT usDeviceConnector; //A physical connector for displays to plug in, using object connector definitions + UCHAR ucExtAUXDDCLutIndex; //An index into external AUX/DDC channel LUT + UCHAR ucExtHPDPINLutIndex; //An index into external HPD pin LUT + USHORT usExtEncoderObjId; //external encoder object id + union{ + UCHAR ucChannelMapping; // if ucChannelMapping=0, using default one to one mapping + ATOM_DP_CONN_CHANNEL_MAPPING asDPMapping; + ATOM_DVI_CONN_CHANNEL_MAPPING asDVIMapping; + }; + UCHAR ucChPNInvert; // bit vector for up to 8 lanes, =0: P and N is not invert, =1 P and N is inverted + USHORT usCaps; + USHORT usReserved; +}EXT_DISPLAY_PATH; + +#define NUMBER_OF_UCHAR_FOR_GUID 16 +#define MAX_NUMBER_OF_EXT_DISPLAY_PATH 7 + +//usCaps +#define EXT_DISPLAY_PATH_CAPS__HBR2_DISABLE 0x01 + +typedef struct _ATOM_EXTERNAL_DISPLAY_CONNECTION_INFO +{ + ATOM_COMMON_TABLE_HEADER sHeader; + UCHAR ucGuid [NUMBER_OF_UCHAR_FOR_GUID]; // a GUID is a 16 byte long string + EXT_DISPLAY_PATH sPath[MAX_NUMBER_OF_EXT_DISPLAY_PATH]; // total of fixed 7 entries. + UCHAR ucChecksum; // a simple Checksum of the sum of whole structure equal to 0x0. + UCHAR uc3DStereoPinId; // use for eDP panel + UCHAR ucRemoteDisplayConfig; + UCHAR uceDPToLVDSRxId; + UCHAR Reserved[4]; // for potential expansion +}ATOM_EXTERNAL_DISPLAY_CONNECTION_INFO; + +//Related definitions, all records are different but they have a commond header +typedef struct _ATOM_COMMON_RECORD_HEADER +{ + UCHAR ucRecordType; //An emun to indicate the record type + UCHAR ucRecordSize; //The size of the whole record in byte +}ATOM_COMMON_RECORD_HEADER; + + +#define ATOM_I2C_RECORD_TYPE 1 +#define ATOM_HPD_INT_RECORD_TYPE 2 +#define ATOM_OUTPUT_PROTECTION_RECORD_TYPE 3 +#define ATOM_CONNECTOR_DEVICE_TAG_RECORD_TYPE 4 +#define ATOM_CONNECTOR_DVI_EXT_INPUT_RECORD_TYPE 5 //Obsolete, switch to use GPIO_CNTL_RECORD_TYPE +#define ATOM_ENCODER_FPGA_CONTROL_RECORD_TYPE 6 //Obsolete, switch to use GPIO_CNTL_RECORD_TYPE +#define ATOM_CONNECTOR_CVTV_SHARE_DIN_RECORD_TYPE 7 +#define ATOM_JTAG_RECORD_TYPE 8 //Obsolete, switch to use GPIO_CNTL_RECORD_TYPE +#define ATOM_OBJECT_GPIO_CNTL_RECORD_TYPE 9 +#define ATOM_ENCODER_DVO_CF_RECORD_TYPE 10 +#define ATOM_CONNECTOR_CF_RECORD_TYPE 11 +#define ATOM_CONNECTOR_HARDCODE_DTD_RECORD_TYPE 12 +#define ATOM_CONNECTOR_PCIE_SUBCONNECTOR_RECORD_TYPE 13 +#define ATOM_ROUTER_DDC_PATH_SELECT_RECORD_TYPE 14 +#define ATOM_ROUTER_DATA_CLOCK_PATH_SELECT_RECORD_TYPE 15 +#define ATOM_CONNECTOR_HPDPIN_LUT_RECORD_TYPE 16 //This is for the case when connectors are not known to object table +#define ATOM_CONNECTOR_AUXDDC_LUT_RECORD_TYPE 17 //This is for the case when connectors are not known to object table +#define ATOM_OBJECT_LINK_RECORD_TYPE 18 //Once this record is present under one object, it indicats the oobject is linked to another obj described by the record +#define ATOM_CONNECTOR_REMOTE_CAP_RECORD_TYPE 19 +#define ATOM_ENCODER_CAP_RECORD_TYPE 20 + + +//Must be updated when new record type is added,equal to that record definition! +#define ATOM_MAX_OBJECT_RECORD_NUMBER ATOM_ENCODER_CAP_RECORD_TYPE + +typedef struct _ATOM_I2C_RECORD +{ + ATOM_COMMON_RECORD_HEADER sheader; + ATOM_I2C_ID_CONFIG sucI2cId; + UCHAR ucI2CAddr; //The slave address, it's 0 when the record is attached to connector for DDC +}ATOM_I2C_RECORD; + +typedef struct _ATOM_HPD_INT_RECORD +{ + ATOM_COMMON_RECORD_HEADER sheader; + UCHAR ucHPDIntGPIOID; //Corresponding block in GPIO_PIN_INFO table gives the pin info + UCHAR ucPlugged_PinState; +}ATOM_HPD_INT_RECORD; + + +typedef struct _ATOM_OUTPUT_PROTECTION_RECORD +{ + ATOM_COMMON_RECORD_HEADER sheader; + UCHAR ucProtectionFlag; + UCHAR ucReserved; +}ATOM_OUTPUT_PROTECTION_RECORD; + +typedef struct _ATOM_CONNECTOR_DEVICE_TAG +{ + ULONG ulACPIDeviceEnum; //Reserved for now + USHORT usDeviceID; //This Id is same as "ATOM_DEVICE_XXX_SUPPORT" + USHORT usPadding; +}ATOM_CONNECTOR_DEVICE_TAG; + +typedef struct _ATOM_CONNECTOR_DEVICE_TAG_RECORD +{ + ATOM_COMMON_RECORD_HEADER sheader; + UCHAR ucNumberOfDevice; + UCHAR ucReserved; + ATOM_CONNECTOR_DEVICE_TAG asDeviceTag[1]; //This Id is same as "ATOM_DEVICE_XXX_SUPPORT", 1 is only for allocation +}ATOM_CONNECTOR_DEVICE_TAG_RECORD; + + +typedef struct _ATOM_CONNECTOR_DVI_EXT_INPUT_RECORD +{ + ATOM_COMMON_RECORD_HEADER sheader; + UCHAR ucConfigGPIOID; + UCHAR ucConfigGPIOState; //Set to 1 when it's active high to enable external flow in + UCHAR ucFlowinGPIPID; + UCHAR ucExtInGPIPID; +}ATOM_CONNECTOR_DVI_EXT_INPUT_RECORD; + +typedef struct _ATOM_ENCODER_FPGA_CONTROL_RECORD +{ + ATOM_COMMON_RECORD_HEADER sheader; + UCHAR ucCTL1GPIO_ID; + UCHAR ucCTL1GPIOState; //Set to 1 when it's active high + UCHAR ucCTL2GPIO_ID; + UCHAR ucCTL2GPIOState; //Set to 1 when it's active high + UCHAR ucCTL3GPIO_ID; + UCHAR ucCTL3GPIOState; //Set to 1 when it's active high + UCHAR ucCTLFPGA_IN_ID; + UCHAR ucPadding[3]; +}ATOM_ENCODER_FPGA_CONTROL_RECORD; + +typedef struct _ATOM_CONNECTOR_CVTV_SHARE_DIN_RECORD +{ + ATOM_COMMON_RECORD_HEADER sheader; + UCHAR ucGPIOID; //Corresponding block in GPIO_PIN_INFO table gives the pin info + UCHAR ucTVActiveState; //Indicating when the pin==0 or 1 when TV is connected +}ATOM_CONNECTOR_CVTV_SHARE_DIN_RECORD; + +typedef struct _ATOM_JTAG_RECORD +{ + ATOM_COMMON_RECORD_HEADER sheader; + UCHAR ucTMSGPIO_ID; + UCHAR ucTMSGPIOState; //Set to 1 when it's active high + UCHAR ucTCKGPIO_ID; + UCHAR ucTCKGPIOState; //Set to 1 when it's active high + UCHAR ucTDOGPIO_ID; + UCHAR ucTDOGPIOState; //Set to 1 when it's active high + UCHAR ucTDIGPIO_ID; + UCHAR ucTDIGPIOState; //Set to 1 when it's active high + UCHAR ucPadding[2]; +}ATOM_JTAG_RECORD; + + +//The following generic object gpio pin control record type will replace JTAG_RECORD/FPGA_CONTROL_RECORD/DVI_EXT_INPUT_RECORD above gradually +typedef struct _ATOM_GPIO_PIN_CONTROL_PAIR +{ + UCHAR ucGPIOID; // GPIO_ID, find the corresponding ID in GPIO_LUT table + UCHAR ucGPIO_PinState; // Pin state showing how to set-up the pin +}ATOM_GPIO_PIN_CONTROL_PAIR; + +typedef struct _ATOM_OBJECT_GPIO_CNTL_RECORD +{ + ATOM_COMMON_RECORD_HEADER sheader; + UCHAR ucFlags; // Future expnadibility + UCHAR ucNumberOfPins; // Number of GPIO pins used to control the object + ATOM_GPIO_PIN_CONTROL_PAIR asGpio[1]; // the real gpio pin pair determined by number of pins ucNumberOfPins +}ATOM_OBJECT_GPIO_CNTL_RECORD; + +//Definitions for GPIO pin state +#define GPIO_PIN_TYPE_INPUT 0x00 +#define GPIO_PIN_TYPE_OUTPUT 0x10 +#define GPIO_PIN_TYPE_HW_CONTROL 0x20 + +//For GPIO_PIN_TYPE_OUTPUT the following is defined +#define GPIO_PIN_OUTPUT_STATE_MASK 0x01 +#define GPIO_PIN_OUTPUT_STATE_SHIFT 0 +#define GPIO_PIN_STATE_ACTIVE_LOW 0x0 +#define GPIO_PIN_STATE_ACTIVE_HIGH 0x1 + +// Indexes to GPIO array in GLSync record +// GLSync record is for Frame Lock/Gen Lock feature. +#define ATOM_GPIO_INDEX_GLSYNC_REFCLK 0 +#define ATOM_GPIO_INDEX_GLSYNC_HSYNC 1 +#define ATOM_GPIO_INDEX_GLSYNC_VSYNC 2 +#define ATOM_GPIO_INDEX_GLSYNC_SWAP_REQ 3 +#define ATOM_GPIO_INDEX_GLSYNC_SWAP_GNT 4 +#define ATOM_GPIO_INDEX_GLSYNC_INTERRUPT 5 +#define ATOM_GPIO_INDEX_GLSYNC_V_RESET 6 +#define ATOM_GPIO_INDEX_GLSYNC_SWAP_CNTL 7 +#define ATOM_GPIO_INDEX_GLSYNC_SWAP_SEL 8 +#define ATOM_GPIO_INDEX_GLSYNC_MAX 9 + +typedef struct _ATOM_ENCODER_DVO_CF_RECORD +{ + ATOM_COMMON_RECORD_HEADER sheader; + ULONG ulStrengthControl; // DVOA strength control for CF + UCHAR ucPadding[2]; +}ATOM_ENCODER_DVO_CF_RECORD; + +// Bit maps for ATOM_ENCODER_CAP_RECORD.ucEncoderCap +#define ATOM_ENCODER_CAP_RECORD_HBR2 0x01 // DP1.2 HBR2 is supported by HW encoder +#define ATOM_ENCODER_CAP_RECORD_HBR2_EN 0x02 // DP1.2 HBR2 setting is qualified and HBR2 can be enabled + +typedef struct _ATOM_ENCODER_CAP_RECORD +{ + ATOM_COMMON_RECORD_HEADER sheader; + union { + USHORT usEncoderCap; + struct { +#if ATOM_BIG_ENDIAN + USHORT usReserved:14; // Bit1-15 may be defined for other capability in future + USHORT usHBR2En:1; // Bit1 is for DP1.2 HBR2 enable + USHORT usHBR2Cap:1; // Bit0 is for DP1.2 HBR2 capability. +#else + USHORT usHBR2Cap:1; // Bit0 is for DP1.2 HBR2 capability. + USHORT usHBR2En:1; // Bit1 is for DP1.2 HBR2 enable + USHORT usReserved:14; // Bit1-15 may be defined for other capability in future +#endif + }; + }; +}ATOM_ENCODER_CAP_RECORD; + +// value for ATOM_CONNECTOR_CF_RECORD.ucConnectedDvoBundle +#define ATOM_CONNECTOR_CF_RECORD_CONNECTED_UPPER12BITBUNDLEA 1 +#define ATOM_CONNECTOR_CF_RECORD_CONNECTED_LOWER12BITBUNDLEB 2 + +typedef struct _ATOM_CONNECTOR_CF_RECORD +{ + ATOM_COMMON_RECORD_HEADER sheader; + USHORT usMaxPixClk; + UCHAR ucFlowCntlGpioId; + UCHAR ucSwapCntlGpioId; + UCHAR ucConnectedDvoBundle; + UCHAR ucPadding; +}ATOM_CONNECTOR_CF_RECORD; + +typedef struct _ATOM_CONNECTOR_HARDCODE_DTD_RECORD +{ + ATOM_COMMON_RECORD_HEADER sheader; + ATOM_DTD_FORMAT asTiming; +}ATOM_CONNECTOR_HARDCODE_DTD_RECORD; + +typedef struct _ATOM_CONNECTOR_PCIE_SUBCONNECTOR_RECORD +{ + ATOM_COMMON_RECORD_HEADER sheader; //ATOM_CONNECTOR_PCIE_SUBCONNECTOR_RECORD_TYPE + UCHAR ucSubConnectorType; //CONNECTOR_OBJECT_ID_SINGLE_LINK_DVI_D|X_ID_DUAL_LINK_DVI_D|HDMI_TYPE_A + UCHAR ucReserved; +}ATOM_CONNECTOR_PCIE_SUBCONNECTOR_RECORD; + + +typedef struct _ATOM_ROUTER_DDC_PATH_SELECT_RECORD +{ + ATOM_COMMON_RECORD_HEADER sheader; + UCHAR ucMuxType; //decide the number of ucMuxState, =0, no pin state, =1: single state with complement, >1: multiple state + UCHAR ucMuxControlPin; + UCHAR ucMuxState[2]; //for alligment purpose +}ATOM_ROUTER_DDC_PATH_SELECT_RECORD; + +typedef struct _ATOM_ROUTER_DATA_CLOCK_PATH_SELECT_RECORD +{ + ATOM_COMMON_RECORD_HEADER sheader; + UCHAR ucMuxType; + UCHAR ucMuxControlPin; + UCHAR ucMuxState[2]; //for alligment purpose +}ATOM_ROUTER_DATA_CLOCK_PATH_SELECT_RECORD; + +// define ucMuxType +#define ATOM_ROUTER_MUX_PIN_STATE_MASK 0x0f +#define ATOM_ROUTER_MUX_PIN_SINGLE_STATE_COMPLEMENT 0x01 + +typedef struct _ATOM_CONNECTOR_HPDPIN_LUT_RECORD //record for ATOM_CONNECTOR_HPDPIN_LUT_RECORD_TYPE +{ + ATOM_COMMON_RECORD_HEADER sheader; + UCHAR ucHPDPINMap[MAX_NUMBER_OF_EXT_HPDPIN_LUT_ENTRIES]; //An fixed size array which maps external pins to internal GPIO_PIN_INFO table +}ATOM_CONNECTOR_HPDPIN_LUT_RECORD; + +typedef struct _ATOM_CONNECTOR_AUXDDC_LUT_RECORD //record for ATOM_CONNECTOR_AUXDDC_LUT_RECORD_TYPE +{ + ATOM_COMMON_RECORD_HEADER sheader; + ATOM_I2C_ID_CONFIG ucAUXDDCMap[MAX_NUMBER_OF_EXT_AUXDDC_LUT_ENTRIES]; //An fixed size array which maps external pins to internal DDC ID +}ATOM_CONNECTOR_AUXDDC_LUT_RECORD; + +typedef struct _ATOM_OBJECT_LINK_RECORD +{ + ATOM_COMMON_RECORD_HEADER sheader; + USHORT usObjectID; //could be connector, encorder or other object in object.h +}ATOM_OBJECT_LINK_RECORD; + +typedef struct _ATOM_CONNECTOR_REMOTE_CAP_RECORD +{ + ATOM_COMMON_RECORD_HEADER sheader; + USHORT usReserved; +}ATOM_CONNECTOR_REMOTE_CAP_RECORD; + +/****************************************************************************/ +// ASIC voltage data table +/****************************************************************************/ +typedef struct _ATOM_VOLTAGE_INFO_HEADER +{ + USHORT usVDDCBaseLevel; //In number of 50mv unit + USHORT usReserved; //For possible extension table offset + UCHAR ucNumOfVoltageEntries; + UCHAR ucBytesPerVoltageEntry; + UCHAR ucVoltageStep; //Indicating in how many mv increament is one step, 0.5mv unit + UCHAR ucDefaultVoltageEntry; + UCHAR ucVoltageControlI2cLine; + UCHAR ucVoltageControlAddress; + UCHAR ucVoltageControlOffset; +}ATOM_VOLTAGE_INFO_HEADER; + +typedef struct _ATOM_VOLTAGE_INFO +{ + ATOM_COMMON_TABLE_HEADER sHeader; + ATOM_VOLTAGE_INFO_HEADER viHeader; + UCHAR ucVoltageEntries[64]; //64 is for allocation, the actual number of entry is present at ucNumOfVoltageEntries*ucBytesPerVoltageEntry +}ATOM_VOLTAGE_INFO; + + +typedef struct _ATOM_VOLTAGE_FORMULA +{ + USHORT usVoltageBaseLevel; // In number of 1mv unit + USHORT usVoltageStep; // Indicating in how many mv increament is one step, 1mv unit + UCHAR ucNumOfVoltageEntries; // Number of Voltage Entry, which indicate max Voltage + UCHAR ucFlag; // bit0=0 :step is 1mv =1 0.5mv + UCHAR ucBaseVID; // if there is no lookup table, VID= BaseVID + ( Vol - BaseLevle ) /VoltageStep + UCHAR ucReserved; + UCHAR ucVIDAdjustEntries[32]; // 32 is for allocation, the actual number of entry is present at ucNumOfVoltageEntries +}ATOM_VOLTAGE_FORMULA; + +typedef struct _VOLTAGE_LUT_ENTRY +{ + USHORT usVoltageCode; // The Voltage ID, either GPIO or I2C code + USHORT usVoltageValue; // The corresponding Voltage Value, in mV +}VOLTAGE_LUT_ENTRY; + +typedef struct _ATOM_VOLTAGE_FORMULA_V2 +{ + UCHAR ucNumOfVoltageEntries; // Number of Voltage Entry, which indicate max Voltage + UCHAR ucReserved[3]; + VOLTAGE_LUT_ENTRY asVIDAdjustEntries[32];// 32 is for allocation, the actual number of entries is in ucNumOfVoltageEntries +}ATOM_VOLTAGE_FORMULA_V2; + +typedef struct _ATOM_VOLTAGE_CONTROL +{ + UCHAR ucVoltageControlId; //Indicate it is controlled by I2C or GPIO or HW state machine + UCHAR ucVoltageControlI2cLine; + UCHAR ucVoltageControlAddress; + UCHAR ucVoltageControlOffset; + USHORT usGpioPin_AIndex; //GPIO_PAD register index + UCHAR ucGpioPinBitShift[9]; //at most 8 pin support 255 VIDs, termintate with 0xff + UCHAR ucReserved; +}ATOM_VOLTAGE_CONTROL; + +// Define ucVoltageControlId +#define VOLTAGE_CONTROLLED_BY_HW 0x00 +#define VOLTAGE_CONTROLLED_BY_I2C_MASK 0x7F +#define VOLTAGE_CONTROLLED_BY_GPIO 0x80 +#define VOLTAGE_CONTROL_ID_LM64 0x01 //I2C control, used for R5xx Core Voltage +#define VOLTAGE_CONTROL_ID_DAC 0x02 //I2C control, used for R5xx/R6xx MVDDC,MVDDQ or VDDCI +#define VOLTAGE_CONTROL_ID_VT116xM 0x03 //I2C control, used for R6xx Core Voltage +#define VOLTAGE_CONTROL_ID_DS4402 0x04 +#define VOLTAGE_CONTROL_ID_UP6266 0x05 +#define VOLTAGE_CONTROL_ID_SCORPIO 0x06 +#define VOLTAGE_CONTROL_ID_VT1556M 0x07 +#define VOLTAGE_CONTROL_ID_CHL822x 0x08 +#define VOLTAGE_CONTROL_ID_VT1586M 0x09 +#define VOLTAGE_CONTROL_ID_UP1637 0x0A + +typedef struct _ATOM_VOLTAGE_OBJECT +{ + UCHAR ucVoltageType; //Indicate Voltage Source: VDDC, MVDDC, MVDDQ or MVDDCI + UCHAR ucSize; //Size of Object + ATOM_VOLTAGE_CONTROL asControl; //describ how to control + ATOM_VOLTAGE_FORMULA asFormula; //Indicate How to convert real Voltage to VID +}ATOM_VOLTAGE_OBJECT; + +typedef struct _ATOM_VOLTAGE_OBJECT_V2 +{ + UCHAR ucVoltageType; //Indicate Voltage Source: VDDC, MVDDC, MVDDQ or MVDDCI + UCHAR ucSize; //Size of Object + ATOM_VOLTAGE_CONTROL asControl; //describ how to control + ATOM_VOLTAGE_FORMULA_V2 asFormula; //Indicate How to convert real Voltage to VID +}ATOM_VOLTAGE_OBJECT_V2; + +typedef struct _ATOM_VOLTAGE_OBJECT_INFO +{ + ATOM_COMMON_TABLE_HEADER sHeader; + ATOM_VOLTAGE_OBJECT asVoltageObj[3]; //Info for Voltage control +}ATOM_VOLTAGE_OBJECT_INFO; + +typedef struct _ATOM_VOLTAGE_OBJECT_INFO_V2 +{ + ATOM_COMMON_TABLE_HEADER sHeader; + ATOM_VOLTAGE_OBJECT_V2 asVoltageObj[3]; //Info for Voltage control +}ATOM_VOLTAGE_OBJECT_INFO_V2; + +typedef struct _ATOM_LEAKID_VOLTAGE +{ + UCHAR ucLeakageId; + UCHAR ucReserved; + USHORT usVoltage; +}ATOM_LEAKID_VOLTAGE; + +typedef struct _ATOM_VOLTAGE_OBJECT_HEADER_V3{ + UCHAR ucVoltageType; //Indicate Voltage Source: VDDC, MVDDC, MVDDQ or MVDDCI + UCHAR ucVoltageMode; //Indicate voltage control mode: Init/Set/Leakage/Set phase + USHORT usSize; //Size of Object +}ATOM_VOLTAGE_OBJECT_HEADER_V3; + +typedef struct _VOLTAGE_LUT_ENTRY_V2 +{ + ULONG ulVoltageId; // The Voltage ID which is used to program GPIO register + USHORT usVoltageValue; // The corresponding Voltage Value, in mV +}VOLTAGE_LUT_ENTRY_V2; + +typedef struct _LEAKAGE_VOLTAGE_LUT_ENTRY_V2 +{ + USHORT usVoltageLevel; // The Voltage ID which is used to program GPIO register + USHORT usVoltageId; + USHORT usLeakageId; // The corresponding Voltage Value, in mV +}LEAKAGE_VOLTAGE_LUT_ENTRY_V2; + +typedef struct _ATOM_I2C_VOLTAGE_OBJECT_V3 +{ + ATOM_VOLTAGE_OBJECT_HEADER_V3 sHeader; + UCHAR ucVoltageRegulatorId; //Indicate Voltage Regulator Id + UCHAR ucVoltageControlI2cLine; + UCHAR ucVoltageControlAddress; + UCHAR ucVoltageControlOffset; + ULONG ulReserved; + VOLTAGE_LUT_ENTRY asVolI2cLut[1]; // end with 0xff +}ATOM_I2C_VOLTAGE_OBJECT_V3; + +typedef struct _ATOM_GPIO_VOLTAGE_OBJECT_V3 +{ + ATOM_VOLTAGE_OBJECT_HEADER_V3 sHeader; + UCHAR ucVoltageGpioCntlId; // default is 0 which indicate control through CG VID mode + UCHAR ucGpioEntryNum; // indiate the entry numbers of Votlage/Gpio value Look up table + UCHAR ucPhaseDelay; // phase delay in unit of micro second + UCHAR ucReserved; + ULONG ulGpioMaskVal; // GPIO Mask value + VOLTAGE_LUT_ENTRY_V2 asVolGpioLut[1]; +}ATOM_GPIO_VOLTAGE_OBJECT_V3; + +typedef struct _ATOM_LEAKAGE_VOLTAGE_OBJECT_V3 +{ + ATOM_VOLTAGE_OBJECT_HEADER_V3 sHeader; + UCHAR ucLeakageCntlId; // default is 0 + UCHAR ucLeakageEntryNum; // indicate the entry number of LeakageId/Voltage Lut table + UCHAR ucReserved[2]; + ULONG ulMaxVoltageLevel; + LEAKAGE_VOLTAGE_LUT_ENTRY_V2 asLeakageIdLut[1]; +}ATOM_LEAKAGE_VOLTAGE_OBJECT_V3; + +typedef union _ATOM_VOLTAGE_OBJECT_V3{ + ATOM_GPIO_VOLTAGE_OBJECT_V3 asGpioVoltageObj; + ATOM_I2C_VOLTAGE_OBJECT_V3 asI2cVoltageObj; + ATOM_LEAKAGE_VOLTAGE_OBJECT_V3 asLeakageObj; +}ATOM_VOLTAGE_OBJECT_V3; + +typedef struct _ATOM_VOLTAGE_OBJECT_INFO_V3_1 +{ + ATOM_COMMON_TABLE_HEADER sHeader; + ATOM_VOLTAGE_OBJECT_V3 asVoltageObj[3]; //Info for Voltage control +}ATOM_VOLTAGE_OBJECT_INFO_V3_1; + +typedef struct _ATOM_ASIC_PROFILE_VOLTAGE +{ + UCHAR ucProfileId; + UCHAR ucReserved; + USHORT usSize; + USHORT usEfuseSpareStartAddr; + USHORT usFuseIndex[8]; //from LSB to MSB, Max 8bit,end of 0xffff if less than 8 efuse id, + ATOM_LEAKID_VOLTAGE asLeakVol[2]; //Leakid and relatd voltage +}ATOM_ASIC_PROFILE_VOLTAGE; + +//ucProfileId +#define ATOM_ASIC_PROFILE_ID_EFUSE_VOLTAGE 1 +#define ATOM_ASIC_PROFILE_ID_EFUSE_PERFORMANCE_VOLTAGE 1 +#define ATOM_ASIC_PROFILE_ID_EFUSE_THERMAL_VOLTAGE 2 + +typedef struct _ATOM_ASIC_PROFILING_INFO +{ + ATOM_COMMON_TABLE_HEADER asHeader; + ATOM_ASIC_PROFILE_VOLTAGE asVoltage; +}ATOM_ASIC_PROFILING_INFO; + +typedef struct _ATOM_POWER_SOURCE_OBJECT +{ + UCHAR ucPwrSrcId; // Power source + UCHAR ucPwrSensorType; // GPIO, I2C or none + UCHAR ucPwrSensId; // if GPIO detect, it is GPIO id, if I2C detect, it is I2C id + UCHAR ucPwrSensSlaveAddr; // Slave address if I2C detect + UCHAR ucPwrSensRegIndex; // I2C register Index if I2C detect + UCHAR ucPwrSensRegBitMask; // detect which bit is used if I2C detect + UCHAR ucPwrSensActiveState; // high active or low active + UCHAR ucReserve[3]; // reserve + USHORT usSensPwr; // in unit of watt +}ATOM_POWER_SOURCE_OBJECT; + +typedef struct _ATOM_POWER_SOURCE_INFO +{ + ATOM_COMMON_TABLE_HEADER asHeader; + UCHAR asPwrbehave[16]; + ATOM_POWER_SOURCE_OBJECT asPwrObj[1]; +}ATOM_POWER_SOURCE_INFO; + + +//Define ucPwrSrcId +#define POWERSOURCE_PCIE_ID1 0x00 +#define POWERSOURCE_6PIN_CONNECTOR_ID1 0x01 +#define POWERSOURCE_8PIN_CONNECTOR_ID1 0x02 +#define POWERSOURCE_6PIN_CONNECTOR_ID2 0x04 +#define POWERSOURCE_8PIN_CONNECTOR_ID2 0x08 + +//define ucPwrSensorId +#define POWER_SENSOR_ALWAYS 0x00 +#define POWER_SENSOR_GPIO 0x01 +#define POWER_SENSOR_I2C 0x02 + +typedef struct _ATOM_CLK_VOLT_CAPABILITY +{ + ULONG ulVoltageIndex; // The Voltage Index indicated by FUSE, same voltage index shared with SCLK DPM fuse table + ULONG ulMaximumSupportedCLK; // Maximum clock supported with specified voltage index, unit in 10kHz +}ATOM_CLK_VOLT_CAPABILITY; + +typedef struct _ATOM_AVAILABLE_SCLK_LIST +{ + ULONG ulSupportedSCLK; // Maximum clock supported with specified voltage index, unit in 10kHz + USHORT usVoltageIndex; // The Voltage Index indicated by FUSE for specified SCLK + USHORT usVoltageID; // The Voltage ID indicated by FUSE for specified SCLK +}ATOM_AVAILABLE_SCLK_LIST; + +// ATOM_INTEGRATED_SYSTEM_INFO_V6 ulSystemConfig cap definition +#define ATOM_IGP_INFO_V6_SYSTEM_CONFIG__PCIE_POWER_GATING_ENABLE 1 // refer to ulSystemConfig bit[0] + +// this IntegrateSystemInfoTable is used for Liano/Ontario APU +typedef struct _ATOM_INTEGRATED_SYSTEM_INFO_V6 +{ + ATOM_COMMON_TABLE_HEADER sHeader; + ULONG ulBootUpEngineClock; + ULONG ulDentistVCOFreq; + ULONG ulBootUpUMAClock; + ATOM_CLK_VOLT_CAPABILITY sDISPCLK_Voltage[4]; + ULONG ulBootUpReqDisplayVector; + ULONG ulOtherDisplayMisc; + ULONG ulGPUCapInfo; + ULONG ulSB_MMIO_Base_Addr; + USHORT usRequestedPWMFreqInHz; + UCHAR ucHtcTmpLmt; + UCHAR ucHtcHystLmt; + ULONG ulMinEngineClock; + ULONG ulSystemConfig; + ULONG ulCPUCapInfo; + USHORT usNBP0Voltage; + USHORT usNBP1Voltage; + USHORT usBootUpNBVoltage; + USHORT usExtDispConnInfoOffset; + USHORT usPanelRefreshRateRange; + UCHAR ucMemoryType; + UCHAR ucUMAChannelNumber; + ULONG ulCSR_M3_ARB_CNTL_DEFAULT[10]; + ULONG ulCSR_M3_ARB_CNTL_UVD[10]; + ULONG ulCSR_M3_ARB_CNTL_FS3D[10]; + ATOM_AVAILABLE_SCLK_LIST sAvail_SCLK[5]; + ULONG ulGMCRestoreResetTime; + ULONG ulMinimumNClk; + ULONG ulIdleNClk; + ULONG ulDDR_DLL_PowerUpTime; + ULONG ulDDR_PLL_PowerUpTime; + USHORT usPCIEClkSSPercentage; + USHORT usPCIEClkSSType; + USHORT usLvdsSSPercentage; + USHORT usLvdsSSpreadRateIn10Hz; + USHORT usHDMISSPercentage; + USHORT usHDMISSpreadRateIn10Hz; + USHORT usDVISSPercentage; + USHORT usDVISSpreadRateIn10Hz; + ULONG SclkDpmBoostMargin; + ULONG SclkDpmThrottleMargin; + USHORT SclkDpmTdpLimitPG; + USHORT SclkDpmTdpLimitBoost; + ULONG ulBoostEngineCLock; + UCHAR ulBoostVid_2bit; + UCHAR EnableBoost; + USHORT GnbTdpLimit; + USHORT usMaxLVDSPclkFreqInSingleLink; + UCHAR ucLvdsMisc; + UCHAR ucLVDSReserved; + ULONG ulReserved3[15]; + ATOM_EXTERNAL_DISPLAY_CONNECTION_INFO sExtDispConnInfo; +}ATOM_INTEGRATED_SYSTEM_INFO_V6; + +// ulGPUCapInfo +#define INTEGRATED_SYSTEM_INFO_V6_GPUCAPINFO__TMDSHDMI_COHERENT_SINGLEPLL_MODE 0x01 +#define INTEGRATED_SYSTEM_INFO_V6_GPUCAPINFO__DISABLE_AUX_HW_MODE_DETECTION 0x08 + +//ucLVDSMisc: +#define SYS_INFO_LVDSMISC__888_FPDI_MODE 0x01 +#define SYS_INFO_LVDSMISC__DL_CH_SWAP 0x02 +#define SYS_INFO_LVDSMISC__888_BPC 0x04 +#define SYS_INFO_LVDSMISC__OVERRIDE_EN 0x08 +#define SYS_INFO_LVDSMISC__BLON_ACTIVE_LOW 0x10 + +// not used any more +#define SYS_INFO_LVDSMISC__VSYNC_ACTIVE_LOW 0x04 +#define SYS_INFO_LVDSMISC__HSYNC_ACTIVE_LOW 0x08 + +/********************************************************************************************************************** + ATOM_INTEGRATED_SYSTEM_INFO_V6 Description +ulBootUpEngineClock: VBIOS bootup Engine clock frequency, in 10kHz unit. if it is equal 0, then VBIOS use pre-defined bootup engine clock +ulDentistVCOFreq: Dentist VCO clock in 10kHz unit. +ulBootUpUMAClock: System memory boot up clock frequency in 10Khz unit. +sDISPCLK_Voltage: Report Display clock voltage requirement. + +ulBootUpReqDisplayVector: VBIOS boot up display IDs, following are supported devices in Liano/Ontaio projects: + ATOM_DEVICE_CRT1_SUPPORT 0x0001 + ATOM_DEVICE_CRT2_SUPPORT 0x0010 + ATOM_DEVICE_DFP1_SUPPORT 0x0008 + ATOM_DEVICE_DFP6_SUPPORT 0x0040 + ATOM_DEVICE_DFP2_SUPPORT 0x0080 + ATOM_DEVICE_DFP3_SUPPORT 0x0200 + ATOM_DEVICE_DFP4_SUPPORT 0x0400 + ATOM_DEVICE_DFP5_SUPPORT 0x0800 + ATOM_DEVICE_LCD1_SUPPORT 0x0002 +ulOtherDisplayMisc: Other display related flags, not defined yet. +ulGPUCapInfo: bit[0]=0: TMDS/HDMI Coherent Mode use cascade PLL mode. + =1: TMDS/HDMI Coherent Mode use signel PLL mode. + bit[3]=0: Enable HW AUX mode detection logic + =1: Disable HW AUX mode dettion logic +ulSB_MMIO_Base_Addr: Physical Base address to SB MMIO space. Driver needs to initialize it for SMU usage. + +usRequestedPWMFreqInHz: When it's set to 0x0 by SBIOS: the LCD BackLight is not controlled by GPU(SW). + Any attempt to change BL using VBIOS function or enable VariBri from PP table is not effective since ATOM_BIOS_INFO_BL_CONTROLLED_BY_GPU==0; + + When it's set to a non-zero frequency, the BackLight is controlled by GPU (SW) in one of two ways below: + 1. SW uses the GPU BL PWM output to control the BL, in chis case, this non-zero frequency determines what freq GPU should use; + VBIOS will set up proper PWM frequency and ATOM_BIOS_INFO_BL_CONTROLLED_BY_GPU==1,as the result, + Changing BL using VBIOS function is functional in both driver and non-driver present environment; + and enabling VariBri under the driver environment from PP table is optional. + + 2. SW uses other means to control BL (like DPCD),this non-zero frequency serves as a flag only indicating + that BL control from GPU is expected. + VBIOS will NOT set up PWM frequency but make ATOM_BIOS_INFO_BL_CONTROLLED_BY_GPU==1 + Changing BL using VBIOS function could be functional in both driver and non-driver present environment,but + it's per platform + and enabling VariBri under the driver environment from PP table is optional. + +ucHtcTmpLmt: Refer to D18F3x64 bit[22:16], HtcTmpLmt. + Threshold on value to enter HTC_active state. +ucHtcHystLmt: Refer to D18F3x64 bit[27:24], HtcHystLmt. + To calculate threshold off value to exit HTC_active state, which is Threshold on vlaue minus ucHtcHystLmt. +ulMinEngineClock: Minimum SCLK allowed in 10kHz unit. This is calculated based on WRCK Fuse settings. +ulSystemConfig: Bit[0]=0: PCIE Power Gating Disabled + =1: PCIE Power Gating Enabled + Bit[1]=0: DDR-DLL shut-down feature disabled. + 1: DDR-DLL shut-down feature enabled. + Bit[2]=0: DDR-PLL Power down feature disabled. + 1: DDR-PLL Power down feature enabled. +ulCPUCapInfo: TBD +usNBP0Voltage: VID for voltage on NB P0 State +usNBP1Voltage: VID for voltage on NB P1 State +usBootUpNBVoltage: Voltage Index of GNB voltage configured by SBIOS, which is suffcient to support VBIOS DISPCLK requirement. +usExtDispConnInfoOffset: Offset to sExtDispConnInfo inside the structure +usPanelRefreshRateRange: Bit vector for LCD supported refresh rate range. If DRR is requestd by the platform, at least two bits need to be set + to indicate a range. + SUPPORTED_LCD_REFRESHRATE_30Hz 0x0004 + SUPPORTED_LCD_REFRESHRATE_40Hz 0x0008 + SUPPORTED_LCD_REFRESHRATE_50Hz 0x0010 + SUPPORTED_LCD_REFRESHRATE_60Hz 0x0020 +ucMemoryType: [3:0]=1:DDR1;=2:DDR2;=3:DDR3.[7:4] is reserved. +ucUMAChannelNumber: System memory channel numbers. +ulCSR_M3_ARB_CNTL_DEFAULT[10]: Arrays with values for CSR M3 arbiter for default +ulCSR_M3_ARB_CNTL_UVD[10]: Arrays with values for CSR M3 arbiter for UVD playback. +ulCSR_M3_ARB_CNTL_FS3D[10]: Arrays with values for CSR M3 arbiter for Full Screen 3D applications. +sAvail_SCLK[5]: Arrays to provide availabe list of SLCK and corresponding voltage, order from low to high +ulGMCRestoreResetTime: GMC power restore and GMC reset time to calculate data reconnection latency. Unit in ns. +ulMinimumNClk: Minimum NCLK speed among all NB-Pstates to calcualte data reconnection latency. Unit in 10kHz. +ulIdleNClk: NCLK speed while memory runs in self-refresh state. Unit in 10kHz. +ulDDR_DLL_PowerUpTime: DDR PHY DLL power up time. Unit in ns. +ulDDR_PLL_PowerUpTime: DDR PHY PLL power up time. Unit in ns. +usPCIEClkSSPercentage: PCIE Clock Spred Spectrum Percentage in unit 0.01%; 100 mean 1%. +usPCIEClkSSType: PCIE Clock Spred Spectrum Type. 0 for Down spread(default); 1 for Center spread. +usLvdsSSPercentage: LVDS panel ( not include eDP ) Spread Spectrum Percentage in unit of 0.01%, =0, use VBIOS default setting. +usLvdsSSpreadRateIn10Hz: LVDS panel ( not include eDP ) Spread Spectrum frequency in unit of 10Hz, =0, use VBIOS default setting. +usHDMISSPercentage: HDMI Spread Spectrum Percentage in unit 0.01%; 100 mean 1%, =0, use VBIOS default setting. +usHDMISSpreadRateIn10Hz: HDMI Spread Spectrum frequency in unit of 10Hz, =0, use VBIOS default setting. +usDVISSPercentage: DVI Spread Spectrum Percentage in unit 0.01%; 100 mean 1%, =0, use VBIOS default setting. +usDVISSpreadRateIn10Hz: DVI Spread Spectrum frequency in unit of 10Hz, =0, use VBIOS default setting. +usMaxLVDSPclkFreqInSingleLink: Max pixel clock LVDS panel single link, if=0 means VBIOS use default threhold, right now it is 85Mhz +ucLVDSMisc: [bit0] LVDS 888bit panel mode =0: LVDS 888 panel in LDI mode, =1: LVDS 888 panel in FPDI mode + [bit1] LVDS panel lower and upper link mapping =0: lower link and upper link not swap, =1: lower link and upper link are swapped + [bit2] LVDS 888bit per color mode =0: 666 bit per color =1:888 bit per color + [bit3] LVDS parameter override enable =0: ucLvdsMisc parameter are not used =1: ucLvdsMisc parameter should be used + [bit4] Polarity of signal sent to digital BLON output pin. =0: not inverted(active high) =1: inverted ( active low ) +**********************************************************************************************************************/ + +// this Table is used for Liano/Ontario APU +typedef struct _ATOM_FUSION_SYSTEM_INFO_V1 +{ + ATOM_INTEGRATED_SYSTEM_INFO_V6 sIntegratedSysInfo; + ULONG ulPowerplayTable[128]; +}ATOM_FUSION_SYSTEM_INFO_V1; +/********************************************************************************************************************** + ATOM_FUSION_SYSTEM_INFO_V1 Description +sIntegratedSysInfo: refer to ATOM_INTEGRATED_SYSTEM_INFO_V6 definition. +ulPowerplayTable[128]: This 512 bytes memory is used to save ATOM_PPLIB_POWERPLAYTABLE3, starting form ulPowerplayTable[0] +**********************************************************************************************************************/ + +// this IntegrateSystemInfoTable is used for Trinity APU +typedef struct _ATOM_INTEGRATED_SYSTEM_INFO_V1_7 +{ + ATOM_COMMON_TABLE_HEADER sHeader; + ULONG ulBootUpEngineClock; + ULONG ulDentistVCOFreq; + ULONG ulBootUpUMAClock; + ATOM_CLK_VOLT_CAPABILITY sDISPCLK_Voltage[4]; + ULONG ulBootUpReqDisplayVector; + ULONG ulOtherDisplayMisc; + ULONG ulGPUCapInfo; + ULONG ulSB_MMIO_Base_Addr; + USHORT usRequestedPWMFreqInHz; + UCHAR ucHtcTmpLmt; + UCHAR ucHtcHystLmt; + ULONG ulMinEngineClock; + ULONG ulSystemConfig; + ULONG ulCPUCapInfo; + USHORT usNBP0Voltage; + USHORT usNBP1Voltage; + USHORT usBootUpNBVoltage; + USHORT usExtDispConnInfoOffset; + USHORT usPanelRefreshRateRange; + UCHAR ucMemoryType; + UCHAR ucUMAChannelNumber; + UCHAR strVBIOSMsg[40]; + ULONG ulReserved[20]; + ATOM_AVAILABLE_SCLK_LIST sAvail_SCLK[5]; + ULONG ulGMCRestoreResetTime; + ULONG ulMinimumNClk; + ULONG ulIdleNClk; + ULONG ulDDR_DLL_PowerUpTime; + ULONG ulDDR_PLL_PowerUpTime; + USHORT usPCIEClkSSPercentage; + USHORT usPCIEClkSSType; + USHORT usLvdsSSPercentage; + USHORT usLvdsSSpreadRateIn10Hz; + USHORT usHDMISSPercentage; + USHORT usHDMISSpreadRateIn10Hz; + USHORT usDVISSPercentage; + USHORT usDVISSpreadRateIn10Hz; + ULONG SclkDpmBoostMargin; + ULONG SclkDpmThrottleMargin; + USHORT SclkDpmTdpLimitPG; + USHORT SclkDpmTdpLimitBoost; + ULONG ulBoostEngineCLock; + UCHAR ulBoostVid_2bit; + UCHAR EnableBoost; + USHORT GnbTdpLimit; + USHORT usMaxLVDSPclkFreqInSingleLink; + UCHAR ucLvdsMisc; + UCHAR ucLVDSReserved; + UCHAR ucLVDSPwrOnSeqDIGONtoDE_in4Ms; + UCHAR ucLVDSPwrOnSeqDEtoVARY_BL_in4Ms; + UCHAR ucLVDSPwrOffSeqVARY_BLtoDE_in4Ms; + UCHAR ucLVDSPwrOffSeqDEtoDIGON_in4Ms; + UCHAR ucLVDSOffToOnDelay_in4Ms; + UCHAR ucLVDSPwrOnSeqVARY_BLtoBLON_in4Ms; + UCHAR ucLVDSPwrOffSeqBLONtoVARY_BL_in4Ms; + UCHAR ucLVDSReserved1; + ULONG ulLCDBitDepthControlVal; + ULONG ulNbpStateMemclkFreq[4]; + USHORT usNBP2Voltage; + USHORT usNBP3Voltage; + ULONG ulNbpStateNClkFreq[4]; + UCHAR ucNBDPMEnable; + UCHAR ucReserved[3]; + UCHAR ucDPMState0VclkFid; + UCHAR ucDPMState0DclkFid; + UCHAR ucDPMState1VclkFid; + UCHAR ucDPMState1DclkFid; + UCHAR ucDPMState2VclkFid; + UCHAR ucDPMState2DclkFid; + UCHAR ucDPMState3VclkFid; + UCHAR ucDPMState3DclkFid; + ATOM_EXTERNAL_DISPLAY_CONNECTION_INFO sExtDispConnInfo; +}ATOM_INTEGRATED_SYSTEM_INFO_V1_7; + +// ulOtherDisplayMisc +#define INTEGRATED_SYSTEM_INFO__GET_EDID_CALLBACK_FUNC_SUPPORT 0x01 +#define INTEGRATED_SYSTEM_INFO__GET_BOOTUP_DISPLAY_CALLBACK_FUNC_SUPPORT 0x02 +#define INTEGRATED_SYSTEM_INFO__GET_EXPANSION_CALLBACK_FUNC_SUPPORT 0x04 +#define INTEGRATED_SYSTEM_INFO__FAST_BOOT_SUPPORT 0x08 + +// ulGPUCapInfo +#define SYS_INFO_GPUCAPS__TMDSHDMI_COHERENT_SINGLEPLL_MODE 0x01 +#define SYS_INFO_GPUCAPS__DP_SINGLEPLL_MODE 0x02 +#define SYS_INFO_GPUCAPS__DISABLE_AUX_MODE_DETECT 0x08 + +/********************************************************************************************************************** + ATOM_INTEGRATED_SYSTEM_INFO_V1_7 Description +ulBootUpEngineClock: VBIOS bootup Engine clock frequency, in 10kHz unit. if it is equal 0, then VBIOS use pre-defined bootup engine clock +ulDentistVCOFreq: Dentist VCO clock in 10kHz unit. +ulBootUpUMAClock: System memory boot up clock frequency in 10Khz unit. +sDISPCLK_Voltage: Report Display clock voltage requirement. + +ulBootUpReqDisplayVector: VBIOS boot up display IDs, following are supported devices in Trinity projects: + ATOM_DEVICE_CRT1_SUPPORT 0x0001 + ATOM_DEVICE_DFP1_SUPPORT 0x0008 + ATOM_DEVICE_DFP6_SUPPORT 0x0040 + ATOM_DEVICE_DFP2_SUPPORT 0x0080 + ATOM_DEVICE_DFP3_SUPPORT 0x0200 + ATOM_DEVICE_DFP4_SUPPORT 0x0400 + ATOM_DEVICE_DFP5_SUPPORT 0x0800 + ATOM_DEVICE_LCD1_SUPPORT 0x0002 +ulOtherDisplayMisc: bit[0]=0: INT15 callback function Get LCD EDID ( ax=4e08, bl=1b ) is not supported by SBIOS. + =1: INT15 callback function Get LCD EDID ( ax=4e08, bl=1b ) is supported by SBIOS. + bit[1]=0: INT15 callback function Get boot display( ax=4e08, bl=01h) is not supported by SBIOS + =1: INT15 callback function Get boot display( ax=4e08, bl=01h) is supported by SBIOS + bit[2]=0: INT15 callback function Get panel Expansion ( ax=4e08, bl=02h) is not supported by SBIOS + =1: INT15 callback function Get panel Expansion ( ax=4e08, bl=02h) is supported by SBIOS + bit[3]=0: VBIOS fast boot is disable + =1: VBIOS fast boot is enable. ( VBIOS skip display device detection in every set mode if LCD panel is connect and LID is open) +ulGPUCapInfo: bit[0]=0: TMDS/HDMI Coherent Mode use cascade PLL mode. + =1: TMDS/HDMI Coherent Mode use signel PLL mode. + bit[1]=0: DP mode use cascade PLL mode ( New for Trinity ) + =1: DP mode use single PLL mode + bit[3]=0: Enable AUX HW mode detection logic + =1: Disable AUX HW mode detection logic + +ulSB_MMIO_Base_Addr: Physical Base address to SB MMIO space. Driver needs to initialize it for SMU usage. + +usRequestedPWMFreqInHz: When it's set to 0x0 by SBIOS: the LCD BackLight is not controlled by GPU(SW). + Any attempt to change BL using VBIOS function or enable VariBri from PP table is not effective since ATOM_BIOS_INFO_BL_CONTROLLED_BY_GPU==0; + + When it's set to a non-zero frequency, the BackLight is controlled by GPU (SW) in one of two ways below: + 1. SW uses the GPU BL PWM output to control the BL, in chis case, this non-zero frequency determines what freq GPU should use; + VBIOS will set up proper PWM frequency and ATOM_BIOS_INFO_BL_CONTROLLED_BY_GPU==1,as the result, + Changing BL using VBIOS function is functional in both driver and non-driver present environment; + and enabling VariBri under the driver environment from PP table is optional. + + 2. SW uses other means to control BL (like DPCD),this non-zero frequency serves as a flag only indicating + that BL control from GPU is expected. + VBIOS will NOT set up PWM frequency but make ATOM_BIOS_INFO_BL_CONTROLLED_BY_GPU==1 + Changing BL using VBIOS function could be functional in both driver and non-driver present environment,but + it's per platform + and enabling VariBri under the driver environment from PP table is optional. + +ucHtcTmpLmt: Refer to D18F3x64 bit[22:16], HtcTmpLmt. + Threshold on value to enter HTC_active state. +ucHtcHystLmt: Refer to D18F3x64 bit[27:24], HtcHystLmt. + To calculate threshold off value to exit HTC_active state, which is Threshold on vlaue minus ucHtcHystLmt. +ulMinEngineClock: Minimum SCLK allowed in 10kHz unit. This is calculated based on WRCK Fuse settings. +ulSystemConfig: Bit[0]=0: PCIE Power Gating Disabled + =1: PCIE Power Gating Enabled + Bit[1]=0: DDR-DLL shut-down feature disabled. + 1: DDR-DLL shut-down feature enabled. + Bit[2]=0: DDR-PLL Power down feature disabled. + 1: DDR-PLL Power down feature enabled. +ulCPUCapInfo: TBD +usNBP0Voltage: VID for voltage on NB P0 State +usNBP1Voltage: VID for voltage on NB P1 State +usNBP2Voltage: VID for voltage on NB P2 State +usNBP3Voltage: VID for voltage on NB P3 State +usBootUpNBVoltage: Voltage Index of GNB voltage configured by SBIOS, which is suffcient to support VBIOS DISPCLK requirement. +usExtDispConnInfoOffset: Offset to sExtDispConnInfo inside the structure +usPanelRefreshRateRange: Bit vector for LCD supported refresh rate range. If DRR is requestd by the platform, at least two bits need to be set + to indicate a range. + SUPPORTED_LCD_REFRESHRATE_30Hz 0x0004 + SUPPORTED_LCD_REFRESHRATE_40Hz 0x0008 + SUPPORTED_LCD_REFRESHRATE_50Hz 0x0010 + SUPPORTED_LCD_REFRESHRATE_60Hz 0x0020 +ucMemoryType: [3:0]=1:DDR1;=2:DDR2;=3:DDR3.[7:4] is reserved. +ucUMAChannelNumber: System memory channel numbers. +ulCSR_M3_ARB_CNTL_DEFAULT[10]: Arrays with values for CSR M3 arbiter for default +ulCSR_M3_ARB_CNTL_UVD[10]: Arrays with values for CSR M3 arbiter for UVD playback. +ulCSR_M3_ARB_CNTL_FS3D[10]: Arrays with values for CSR M3 arbiter for Full Screen 3D applications. +sAvail_SCLK[5]: Arrays to provide availabe list of SLCK and corresponding voltage, order from low to high +ulGMCRestoreResetTime: GMC power restore and GMC reset time to calculate data reconnection latency. Unit in ns. +ulMinimumNClk: Minimum NCLK speed among all NB-Pstates to calcualte data reconnection latency. Unit in 10kHz. +ulIdleNClk: NCLK speed while memory runs in self-refresh state. Unit in 10kHz. +ulDDR_DLL_PowerUpTime: DDR PHY DLL power up time. Unit in ns. +ulDDR_PLL_PowerUpTime: DDR PHY PLL power up time. Unit in ns. +usPCIEClkSSPercentage: PCIE Clock Spread Spectrum Percentage in unit 0.01%; 100 mean 1%. +usPCIEClkSSType: PCIE Clock Spread Spectrum Type. 0 for Down spread(default); 1 for Center spread. +usLvdsSSPercentage: LVDS panel ( not include eDP ) Spread Spectrum Percentage in unit of 0.01%, =0, use VBIOS default setting. +usLvdsSSpreadRateIn10Hz: LVDS panel ( not include eDP ) Spread Spectrum frequency in unit of 10Hz, =0, use VBIOS default setting. +usHDMISSPercentage: HDMI Spread Spectrum Percentage in unit 0.01%; 100 mean 1%, =0, use VBIOS default setting. +usHDMISSpreadRateIn10Hz: HDMI Spread Spectrum frequency in unit of 10Hz, =0, use VBIOS default setting. +usDVISSPercentage: DVI Spread Spectrum Percentage in unit 0.01%; 100 mean 1%, =0, use VBIOS default setting. +usDVISSpreadRateIn10Hz: DVI Spread Spectrum frequency in unit of 10Hz, =0, use VBIOS default setting. +usMaxLVDSPclkFreqInSingleLink: Max pixel clock LVDS panel single link, if=0 means VBIOS use default threhold, right now it is 85Mhz +ucLVDSMisc: [bit0] LVDS 888bit panel mode =0: LVDS 888 panel in LDI mode, =1: LVDS 888 panel in FPDI mode + [bit1] LVDS panel lower and upper link mapping =0: lower link and upper link not swap, =1: lower link and upper link are swapped + [bit2] LVDS 888bit per color mode =0: 666 bit per color =1:888 bit per color + [bit3] LVDS parameter override enable =0: ucLvdsMisc parameter are not used =1: ucLvdsMisc parameter should be used + [bit4] Polarity of signal sent to digital BLON output pin. =0: not inverted(active high) =1: inverted ( active low ) +ucLVDSPwrOnSeqDIGONtoDE_in4Ms: LVDS power up sequence time in unit of 4ms, time delay from DIGON signal active to data enable signal active( DE ). + =0 mean use VBIOS default which is 8 ( 32ms ). The LVDS power up sequence is as following: DIGON->DE->VARY_BL->BLON. + This parameter is used by VBIOS only. VBIOS will patch LVDS_InfoTable. +ucLVDSPwrOnDEtoVARY_BL_in4Ms: LVDS power up sequence time in unit of 4ms., time delay from DE( data enable ) active to Vary Brightness enable signal active( VARY_BL ). + =0 mean use VBIOS default which is 90 ( 360ms ). The LVDS power up sequence is as following: DIGON->DE->VARY_BL->BLON. + This parameter is used by VBIOS only. VBIOS will patch LVDS_InfoTable. + +ucLVDSPwrOffVARY_BLtoDE_in4Ms: LVDS power down sequence time in unit of 4ms, time delay from data enable ( DE ) signal off to LCDVCC (DIGON) off. + =0 mean use VBIOS default delay which is 8 ( 32ms ). The LVDS power down sequence is as following: BLON->VARY_BL->DE->DIGON + This parameter is used by VBIOS only. VBIOS will patch LVDS_InfoTable. + +ucLVDSPwrOffDEtoDIGON_in4Ms: LVDS power down sequence time in unit of 4ms, time delay from vary brightness enable signal( VARY_BL) off to data enable ( DE ) signal off. + =0 mean use VBIOS default which is 90 ( 360ms ). The LVDS power down sequence is as following: BLON->VARY_BL->DE->DIGON + This parameter is used by VBIOS only. VBIOS will patch LVDS_InfoTable. + +ucLVDSOffToOnDelay_in4Ms: LVDS power down sequence time in unit of 4ms. Time delay from DIGON signal off to DIGON signal active. + =0 means to use VBIOS default delay which is 125 ( 500ms ). + This parameter is used by VBIOS only. VBIOS will patch LVDS_InfoTable. + +ucLVDSPwrOnVARY_BLtoBLON_in4Ms: LVDS power up sequence time in unit of 4ms. Time delay from VARY_BL signal on to DLON signal active. + =0 means to use VBIOS default delay which is 0 ( 0ms ). + This parameter is used by VBIOS only. VBIOS will patch LVDS_InfoTable. + +ucLVDSPwrOffBLONtoVARY_BL_in4Ms: LVDS power down sequence time in unit of 4ms. Time delay from BLON signal off to VARY_BL signal off. + =0 means to use VBIOS default delay which is 0 ( 0ms ). + This parameter is used by VBIOS only. VBIOS will patch LVDS_InfoTable. + +ulNbpStateMemclkFreq[4]: system memory clock frequncey in unit of 10Khz in different NB pstate. + +**********************************************************************************************************************/ + +/**************************************************************************/ +// This portion is only used when ext thermal chip or engine/memory clock SS chip is populated on a design +//Memory SS Info Table +//Define Memory Clock SS chip ID +#define ICS91719 1 +#define ICS91720 2 + +//Define one structure to inform SW a "block of data" writing to external SS chip via I2C protocol +typedef struct _ATOM_I2C_DATA_RECORD +{ + UCHAR ucNunberOfBytes; //Indicates how many bytes SW needs to write to the external ASIC for one block, besides to "Start" and "Stop" + UCHAR ucI2CData[1]; //I2C data in bytes, should be less than 16 bytes usually +}ATOM_I2C_DATA_RECORD; + + +//Define one structure to inform SW how many blocks of data writing to external SS chip via I2C protocol, in addition to other information +typedef struct _ATOM_I2C_DEVICE_SETUP_INFO +{ + ATOM_I2C_ID_CONFIG_ACCESS sucI2cId; //I2C line and HW/SW assisted cap. + UCHAR ucSSChipID; //SS chip being used + UCHAR ucSSChipSlaveAddr; //Slave Address to set up this SS chip + UCHAR ucNumOfI2CDataRecords; //number of data block + ATOM_I2C_DATA_RECORD asI2CData[1]; +}ATOM_I2C_DEVICE_SETUP_INFO; + +//========================================================================================== +typedef struct _ATOM_ASIC_MVDD_INFO +{ + ATOM_COMMON_TABLE_HEADER sHeader; + ATOM_I2C_DEVICE_SETUP_INFO asI2CSetup[1]; +}ATOM_ASIC_MVDD_INFO; + +//========================================================================================== +#define ATOM_MCLK_SS_INFO ATOM_ASIC_MVDD_INFO + +//========================================================================================== +/**************************************************************************/ + +typedef struct _ATOM_ASIC_SS_ASSIGNMENT +{ + ULONG ulTargetClockRange; //Clock Out frequence (VCO ), in unit of 10Khz + USHORT usSpreadSpectrumPercentage; //in unit of 0.01% + USHORT usSpreadRateInKhz; //in unit of kHz, modulation freq + UCHAR ucClockIndication; //Indicate which clock source needs SS + UCHAR ucSpreadSpectrumMode; //Bit1=0 Down Spread,=1 Center Spread. + UCHAR ucReserved[2]; +}ATOM_ASIC_SS_ASSIGNMENT; + +//Define ucClockIndication, SW uses the IDs below to search if the SS is required/enabled on a clock branch/signal type. +//SS is not required or enabled if a match is not found. +#define ASIC_INTERNAL_MEMORY_SS 1 +#define ASIC_INTERNAL_ENGINE_SS 2 +#define ASIC_INTERNAL_UVD_SS 3 +#define ASIC_INTERNAL_SS_ON_TMDS 4 +#define ASIC_INTERNAL_SS_ON_HDMI 5 +#define ASIC_INTERNAL_SS_ON_LVDS 6 +#define ASIC_INTERNAL_SS_ON_DP 7 +#define ASIC_INTERNAL_SS_ON_DCPLL 8 +#define ASIC_EXTERNAL_SS_ON_DP_CLOCK 9 +#define ASIC_INTERNAL_VCE_SS 10 + +typedef struct _ATOM_ASIC_SS_ASSIGNMENT_V2 +{ + ULONG ulTargetClockRange; //For mem/engine/uvd, Clock Out frequence (VCO ), in unit of 10Khz + //For TMDS/HDMI/LVDS, it is pixel clock , for DP, it is link clock ( 27000 or 16200 ) + USHORT usSpreadSpectrumPercentage; //in unit of 0.01% + USHORT usSpreadRateIn10Hz; //in unit of 10Hz, modulation freq + UCHAR ucClockIndication; //Indicate which clock source needs SS + UCHAR ucSpreadSpectrumMode; //Bit0=0 Down Spread,=1 Center Spread, bit1=0: internal SS bit1=1: external SS + UCHAR ucReserved[2]; +}ATOM_ASIC_SS_ASSIGNMENT_V2; + +//ucSpreadSpectrumMode +//#define ATOM_SS_DOWN_SPREAD_MODE_MASK 0x00000000 +//#define ATOM_SS_DOWN_SPREAD_MODE 0x00000000 +//#define ATOM_SS_CENTRE_SPREAD_MODE_MASK 0x00000001 +//#define ATOM_SS_CENTRE_SPREAD_MODE 0x00000001 +//#define ATOM_INTERNAL_SS_MASK 0x00000000 +//#define ATOM_EXTERNAL_SS_MASK 0x00000002 + +typedef struct _ATOM_ASIC_INTERNAL_SS_INFO +{ + ATOM_COMMON_TABLE_HEADER sHeader; + ATOM_ASIC_SS_ASSIGNMENT asSpreadSpectrum[4]; +}ATOM_ASIC_INTERNAL_SS_INFO; + +typedef struct _ATOM_ASIC_INTERNAL_SS_INFO_V2 +{ + ATOM_COMMON_TABLE_HEADER sHeader; + ATOM_ASIC_SS_ASSIGNMENT_V2 asSpreadSpectrum[1]; //this is point only. +}ATOM_ASIC_INTERNAL_SS_INFO_V2; + +typedef struct _ATOM_ASIC_SS_ASSIGNMENT_V3 +{ + ULONG ulTargetClockRange; //For mem/engine/uvd, Clock Out frequence (VCO ), in unit of 10Khz + //For TMDS/HDMI/LVDS, it is pixel clock , for DP, it is link clock ( 27000 or 16200 ) + USHORT usSpreadSpectrumPercentage; //in unit of 0.01% + USHORT usSpreadRateIn10Hz; //in unit of 10Hz, modulation freq + UCHAR ucClockIndication; //Indicate which clock source needs SS + UCHAR ucSpreadSpectrumMode; //Bit0=0 Down Spread,=1 Center Spread, bit1=0: internal SS bit1=1: external SS + UCHAR ucReserved[2]; +}ATOM_ASIC_SS_ASSIGNMENT_V3; + +typedef struct _ATOM_ASIC_INTERNAL_SS_INFO_V3 +{ + ATOM_COMMON_TABLE_HEADER sHeader; + ATOM_ASIC_SS_ASSIGNMENT_V3 asSpreadSpectrum[1]; //this is pointer only. +}ATOM_ASIC_INTERNAL_SS_INFO_V3; + + +//==============================Scratch Pad Definition Portion=============================== +#define ATOM_DEVICE_CONNECT_INFO_DEF 0 +#define ATOM_ROM_LOCATION_DEF 1 +#define ATOM_TV_STANDARD_DEF 2 +#define ATOM_ACTIVE_INFO_DEF 3 +#define ATOM_LCD_INFO_DEF 4 +#define ATOM_DOS_REQ_INFO_DEF 5 +#define ATOM_ACC_CHANGE_INFO_DEF 6 +#define ATOM_DOS_MODE_INFO_DEF 7 +#define ATOM_I2C_CHANNEL_STATUS_DEF 8 +#define ATOM_I2C_CHANNEL_STATUS1_DEF 9 +#define ATOM_INTERNAL_TIMER_DEF 10 + +// BIOS_0_SCRATCH Definition +#define ATOM_S0_CRT1_MONO 0x00000001L +#define ATOM_S0_CRT1_COLOR 0x00000002L +#define ATOM_S0_CRT1_MASK (ATOM_S0_CRT1_MONO+ATOM_S0_CRT1_COLOR) + +#define ATOM_S0_TV1_COMPOSITE_A 0x00000004L +#define ATOM_S0_TV1_SVIDEO_A 0x00000008L +#define ATOM_S0_TV1_MASK_A (ATOM_S0_TV1_COMPOSITE_A+ATOM_S0_TV1_SVIDEO_A) + +#define ATOM_S0_CV_A 0x00000010L +#define ATOM_S0_CV_DIN_A 0x00000020L +#define ATOM_S0_CV_MASK_A (ATOM_S0_CV_A+ATOM_S0_CV_DIN_A) + + +#define ATOM_S0_CRT2_MONO 0x00000100L +#define ATOM_S0_CRT2_COLOR 0x00000200L +#define ATOM_S0_CRT2_MASK (ATOM_S0_CRT2_MONO+ATOM_S0_CRT2_COLOR) + +#define ATOM_S0_TV1_COMPOSITE 0x00000400L +#define ATOM_S0_TV1_SVIDEO 0x00000800L +#define ATOM_S0_TV1_SCART 0x00004000L +#define ATOM_S0_TV1_MASK (ATOM_S0_TV1_COMPOSITE+ATOM_S0_TV1_SVIDEO+ATOM_S0_TV1_SCART) + +#define ATOM_S0_CV 0x00001000L +#define ATOM_S0_CV_DIN 0x00002000L +#define ATOM_S0_CV_MASK (ATOM_S0_CV+ATOM_S0_CV_DIN) + +#define ATOM_S0_DFP1 0x00010000L +#define ATOM_S0_DFP2 0x00020000L +#define ATOM_S0_LCD1 0x00040000L +#define ATOM_S0_LCD2 0x00080000L +#define ATOM_S0_DFP6 0x00100000L +#define ATOM_S0_DFP3 0x00200000L +#define ATOM_S0_DFP4 0x00400000L +#define ATOM_S0_DFP5 0x00800000L + +#define ATOM_S0_DFP_MASK ATOM_S0_DFP1 | ATOM_S0_DFP2 | ATOM_S0_DFP3 | ATOM_S0_DFP4 | ATOM_S0_DFP5 | ATOM_S0_DFP6 + +#define ATOM_S0_FAD_REGISTER_BUG 0x02000000L // If set, indicates we are running a PCIE asic with + // the FAD/HDP reg access bug. Bit is read by DAL, this is obsolete from RV5xx + +#define ATOM_S0_THERMAL_STATE_MASK 0x1C000000L +#define ATOM_S0_THERMAL_STATE_SHIFT 26 + +#define ATOM_S0_SYSTEM_POWER_STATE_MASK 0xE0000000L +#define ATOM_S0_SYSTEM_POWER_STATE_SHIFT 29 + +#define ATOM_S0_SYSTEM_POWER_STATE_VALUE_AC 1 +#define ATOM_S0_SYSTEM_POWER_STATE_VALUE_DC 2 +#define ATOM_S0_SYSTEM_POWER_STATE_VALUE_LITEAC 3 +#define ATOM_S0_SYSTEM_POWER_STATE_VALUE_LIT2AC 4 + +//Byte aligned definition for BIOS usage +#define ATOM_S0_CRT1_MONOb0 0x01 +#define ATOM_S0_CRT1_COLORb0 0x02 +#define ATOM_S0_CRT1_MASKb0 (ATOM_S0_CRT1_MONOb0+ATOM_S0_CRT1_COLORb0) + +#define ATOM_S0_TV1_COMPOSITEb0 0x04 +#define ATOM_S0_TV1_SVIDEOb0 0x08 +#define ATOM_S0_TV1_MASKb0 (ATOM_S0_TV1_COMPOSITEb0+ATOM_S0_TV1_SVIDEOb0) + +#define ATOM_S0_CVb0 0x10 +#define ATOM_S0_CV_DINb0 0x20 +#define ATOM_S0_CV_MASKb0 (ATOM_S0_CVb0+ATOM_S0_CV_DINb0) + +#define ATOM_S0_CRT2_MONOb1 0x01 +#define ATOM_S0_CRT2_COLORb1 0x02 +#define ATOM_S0_CRT2_MASKb1 (ATOM_S0_CRT2_MONOb1+ATOM_S0_CRT2_COLORb1) + +#define ATOM_S0_TV1_COMPOSITEb1 0x04 +#define ATOM_S0_TV1_SVIDEOb1 0x08 +#define ATOM_S0_TV1_SCARTb1 0x40 +#define ATOM_S0_TV1_MASKb1 (ATOM_S0_TV1_COMPOSITEb1+ATOM_S0_TV1_SVIDEOb1+ATOM_S0_TV1_SCARTb1) + +#define ATOM_S0_CVb1 0x10 +#define ATOM_S0_CV_DINb1 0x20 +#define ATOM_S0_CV_MASKb1 (ATOM_S0_CVb1+ATOM_S0_CV_DINb1) + +#define ATOM_S0_DFP1b2 0x01 +#define ATOM_S0_DFP2b2 0x02 +#define ATOM_S0_LCD1b2 0x04 +#define ATOM_S0_LCD2b2 0x08 +#define ATOM_S0_DFP6b2 0x10 +#define ATOM_S0_DFP3b2 0x20 +#define ATOM_S0_DFP4b2 0x40 +#define ATOM_S0_DFP5b2 0x80 + + +#define ATOM_S0_THERMAL_STATE_MASKb3 0x1C +#define ATOM_S0_THERMAL_STATE_SHIFTb3 2 + +#define ATOM_S0_SYSTEM_POWER_STATE_MASKb3 0xE0 +#define ATOM_S0_LCD1_SHIFT 18 + +// BIOS_1_SCRATCH Definition +#define ATOM_S1_ROM_LOCATION_MASK 0x0000FFFFL +#define ATOM_S1_PCI_BUS_DEV_MASK 0xFFFF0000L + +// BIOS_2_SCRATCH Definition +#define ATOM_S2_TV1_STANDARD_MASK 0x0000000FL +#define ATOM_S2_CURRENT_BL_LEVEL_MASK 0x0000FF00L +#define ATOM_S2_CURRENT_BL_LEVEL_SHIFT 8 + +#define ATOM_S2_FORCEDLOWPWRMODE_STATE_MASK 0x0C000000L +#define ATOM_S2_FORCEDLOWPWRMODE_STATE_MASK_SHIFT 26 +#define ATOM_S2_FORCEDLOWPWRMODE_STATE_CHANGE 0x10000000L + +#define ATOM_S2_DEVICE_DPMS_STATE 0x00010000L +#define ATOM_S2_VRI_BRIGHT_ENABLE 0x20000000L + +#define ATOM_S2_DISPLAY_ROTATION_0_DEGREE 0x0 +#define ATOM_S2_DISPLAY_ROTATION_90_DEGREE 0x1 +#define ATOM_S2_DISPLAY_ROTATION_180_DEGREE 0x2 +#define ATOM_S2_DISPLAY_ROTATION_270_DEGREE 0x3 +#define ATOM_S2_DISPLAY_ROTATION_DEGREE_SHIFT 30 +#define ATOM_S2_DISPLAY_ROTATION_ANGLE_MASK 0xC0000000L + + +//Byte aligned definition for BIOS usage +#define ATOM_S2_TV1_STANDARD_MASKb0 0x0F +#define ATOM_S2_CURRENT_BL_LEVEL_MASKb1 0xFF +#define ATOM_S2_DEVICE_DPMS_STATEb2 0x01 + +#define ATOM_S2_DEVICE_DPMS_MASKw1 0x3FF +#define ATOM_S2_FORCEDLOWPWRMODE_STATE_MASKb3 0x0C +#define ATOM_S2_FORCEDLOWPWRMODE_STATE_CHANGEb3 0x10 +#define ATOM_S2_TMDS_COHERENT_MODEb3 0x10 // used by VBIOS code only, use coherent mode for TMDS/HDMI mode +#define ATOM_S2_VRI_BRIGHT_ENABLEb3 0x20 +#define ATOM_S2_ROTATION_STATE_MASKb3 0xC0 + + +// BIOS_3_SCRATCH Definition +#define ATOM_S3_CRT1_ACTIVE 0x00000001L +#define ATOM_S3_LCD1_ACTIVE 0x00000002L +#define ATOM_S3_TV1_ACTIVE 0x00000004L +#define ATOM_S3_DFP1_ACTIVE 0x00000008L +#define ATOM_S3_CRT2_ACTIVE 0x00000010L +#define ATOM_S3_LCD2_ACTIVE 0x00000020L +#define ATOM_S3_DFP6_ACTIVE 0x00000040L +#define ATOM_S3_DFP2_ACTIVE 0x00000080L +#define ATOM_S3_CV_ACTIVE 0x00000100L +#define ATOM_S3_DFP3_ACTIVE 0x00000200L +#define ATOM_S3_DFP4_ACTIVE 0x00000400L +#define ATOM_S3_DFP5_ACTIVE 0x00000800L + +#define ATOM_S3_DEVICE_ACTIVE_MASK 0x00000FFFL + +#define ATOM_S3_LCD_FULLEXPANSION_ACTIVE 0x00001000L +#define ATOM_S3_LCD_EXPANSION_ASPEC_RATIO_ACTIVE 0x00002000L + +#define ATOM_S3_CRT1_CRTC_ACTIVE 0x00010000L +#define ATOM_S3_LCD1_CRTC_ACTIVE 0x00020000L +#define ATOM_S3_TV1_CRTC_ACTIVE 0x00040000L +#define ATOM_S3_DFP1_CRTC_ACTIVE 0x00080000L +#define ATOM_S3_CRT2_CRTC_ACTIVE 0x00100000L +#define ATOM_S3_LCD2_CRTC_ACTIVE 0x00200000L +#define ATOM_S3_DFP6_CRTC_ACTIVE 0x00400000L +#define ATOM_S3_DFP2_CRTC_ACTIVE 0x00800000L +#define ATOM_S3_CV_CRTC_ACTIVE 0x01000000L +#define ATOM_S3_DFP3_CRTC_ACTIVE 0x02000000L +#define ATOM_S3_DFP4_CRTC_ACTIVE 0x04000000L +#define ATOM_S3_DFP5_CRTC_ACTIVE 0x08000000L + +#define ATOM_S3_DEVICE_CRTC_ACTIVE_MASK 0x0FFF0000L +#define ATOM_S3_ASIC_GUI_ENGINE_HUNG 0x20000000L +//Below two definitions are not supported in pplib, but in the old powerplay in DAL +#define ATOM_S3_ALLOW_FAST_PWR_SWITCH 0x40000000L +#define ATOM_S3_RQST_GPU_USE_MIN_PWR 0x80000000L + +//Byte aligned definition for BIOS usage +#define ATOM_S3_CRT1_ACTIVEb0 0x01 +#define ATOM_S3_LCD1_ACTIVEb0 0x02 +#define ATOM_S3_TV1_ACTIVEb0 0x04 +#define ATOM_S3_DFP1_ACTIVEb0 0x08 +#define ATOM_S3_CRT2_ACTIVEb0 0x10 +#define ATOM_S3_LCD2_ACTIVEb0 0x20 +#define ATOM_S3_DFP6_ACTIVEb0 0x40 +#define ATOM_S3_DFP2_ACTIVEb0 0x80 +#define ATOM_S3_CV_ACTIVEb1 0x01 +#define ATOM_S3_DFP3_ACTIVEb1 0x02 +#define ATOM_S3_DFP4_ACTIVEb1 0x04 +#define ATOM_S3_DFP5_ACTIVEb1 0x08 + +#define ATOM_S3_ACTIVE_CRTC1w0 0xFFF + +#define ATOM_S3_CRT1_CRTC_ACTIVEb2 0x01 +#define ATOM_S3_LCD1_CRTC_ACTIVEb2 0x02 +#define ATOM_S3_TV1_CRTC_ACTIVEb2 0x04 +#define ATOM_S3_DFP1_CRTC_ACTIVEb2 0x08 +#define ATOM_S3_CRT2_CRTC_ACTIVEb2 0x10 +#define ATOM_S3_LCD2_CRTC_ACTIVEb2 0x20 +#define ATOM_S3_DFP6_CRTC_ACTIVEb2 0x40 +#define ATOM_S3_DFP2_CRTC_ACTIVEb2 0x80 +#define ATOM_S3_CV_CRTC_ACTIVEb3 0x01 +#define ATOM_S3_DFP3_CRTC_ACTIVEb3 0x02 +#define ATOM_S3_DFP4_CRTC_ACTIVEb3 0x04 +#define ATOM_S3_DFP5_CRTC_ACTIVEb3 0x08 + +#define ATOM_S3_ACTIVE_CRTC2w1 0xFFF + +// BIOS_4_SCRATCH Definition +#define ATOM_S4_LCD1_PANEL_ID_MASK 0x000000FFL +#define ATOM_S4_LCD1_REFRESH_MASK 0x0000FF00L +#define ATOM_S4_LCD1_REFRESH_SHIFT 8 + +//Byte aligned definition for BIOS usage +#define ATOM_S4_LCD1_PANEL_ID_MASKb0 0x0FF +#define ATOM_S4_LCD1_REFRESH_MASKb1 ATOM_S4_LCD1_PANEL_ID_MASKb0 +#define ATOM_S4_VRAM_INFO_MASKb2 ATOM_S4_LCD1_PANEL_ID_MASKb0 + +// BIOS_5_SCRATCH Definition, BIOS_5_SCRATCH is used by Firmware only !!!! +#define ATOM_S5_DOS_REQ_CRT1b0 0x01 +#define ATOM_S5_DOS_REQ_LCD1b0 0x02 +#define ATOM_S5_DOS_REQ_TV1b0 0x04 +#define ATOM_S5_DOS_REQ_DFP1b0 0x08 +#define ATOM_S5_DOS_REQ_CRT2b0 0x10 +#define ATOM_S5_DOS_REQ_LCD2b0 0x20 +#define ATOM_S5_DOS_REQ_DFP6b0 0x40 +#define ATOM_S5_DOS_REQ_DFP2b0 0x80 +#define ATOM_S5_DOS_REQ_CVb1 0x01 +#define ATOM_S5_DOS_REQ_DFP3b1 0x02 +#define ATOM_S5_DOS_REQ_DFP4b1 0x04 +#define ATOM_S5_DOS_REQ_DFP5b1 0x08 + +#define ATOM_S5_DOS_REQ_DEVICEw0 0x0FFF + +#define ATOM_S5_DOS_REQ_CRT1 0x0001 +#define ATOM_S5_DOS_REQ_LCD1 0x0002 +#define ATOM_S5_DOS_REQ_TV1 0x0004 +#define ATOM_S5_DOS_REQ_DFP1 0x0008 +#define ATOM_S5_DOS_REQ_CRT2 0x0010 +#define ATOM_S5_DOS_REQ_LCD2 0x0020 +#define ATOM_S5_DOS_REQ_DFP6 0x0040 +#define ATOM_S5_DOS_REQ_DFP2 0x0080 +#define ATOM_S5_DOS_REQ_CV 0x0100 +#define ATOM_S5_DOS_REQ_DFP3 0x0200 +#define ATOM_S5_DOS_REQ_DFP4 0x0400 +#define ATOM_S5_DOS_REQ_DFP5 0x0800 + +#define ATOM_S5_DOS_FORCE_CRT1b2 ATOM_S5_DOS_REQ_CRT1b0 +#define ATOM_S5_DOS_FORCE_TV1b2 ATOM_S5_DOS_REQ_TV1b0 +#define ATOM_S5_DOS_FORCE_CRT2b2 ATOM_S5_DOS_REQ_CRT2b0 +#define ATOM_S5_DOS_FORCE_CVb3 ATOM_S5_DOS_REQ_CVb1 +#define ATOM_S5_DOS_FORCE_DEVICEw1 (ATOM_S5_DOS_FORCE_CRT1b2+ATOM_S5_DOS_FORCE_TV1b2+ATOM_S5_DOS_FORCE_CRT2b2+\ + (ATOM_S5_DOS_FORCE_CVb3<<8)) + +// BIOS_6_SCRATCH Definition +#define ATOM_S6_DEVICE_CHANGE 0x00000001L +#define ATOM_S6_SCALER_CHANGE 0x00000002L +#define ATOM_S6_LID_CHANGE 0x00000004L +#define ATOM_S6_DOCKING_CHANGE 0x00000008L +#define ATOM_S6_ACC_MODE 0x00000010L +#define ATOM_S6_EXT_DESKTOP_MODE 0x00000020L +#define ATOM_S6_LID_STATE 0x00000040L +#define ATOM_S6_DOCK_STATE 0x00000080L +#define ATOM_S6_CRITICAL_STATE 0x00000100L +#define ATOM_S6_HW_I2C_BUSY_STATE 0x00000200L +#define ATOM_S6_THERMAL_STATE_CHANGE 0x00000400L +#define ATOM_S6_INTERRUPT_SET_BY_BIOS 0x00000800L +#define ATOM_S6_REQ_LCD_EXPANSION_FULL 0x00001000L //Normal expansion Request bit for LCD +#define ATOM_S6_REQ_LCD_EXPANSION_ASPEC_RATIO 0x00002000L //Aspect ratio expansion Request bit for LCD + +#define ATOM_S6_DISPLAY_STATE_CHANGE 0x00004000L //This bit is recycled when ATOM_BIOS_INFO_BIOS_SCRATCH6_SCL2_REDEFINE is set,previously it's SCL2_H_expansion +#define ATOM_S6_I2C_STATE_CHANGE 0x00008000L //This bit is recycled,when ATOM_BIOS_INFO_BIOS_SCRATCH6_SCL2_REDEFINE is set,previously it's SCL2_V_expansion + +#define ATOM_S6_ACC_REQ_CRT1 0x00010000L +#define ATOM_S6_ACC_REQ_LCD1 0x00020000L +#define ATOM_S6_ACC_REQ_TV1 0x00040000L +#define ATOM_S6_ACC_REQ_DFP1 0x00080000L +#define ATOM_S6_ACC_REQ_CRT2 0x00100000L +#define ATOM_S6_ACC_REQ_LCD2 0x00200000L +#define ATOM_S6_ACC_REQ_DFP6 0x00400000L +#define ATOM_S6_ACC_REQ_DFP2 0x00800000L +#define ATOM_S6_ACC_REQ_CV 0x01000000L +#define ATOM_S6_ACC_REQ_DFP3 0x02000000L +#define ATOM_S6_ACC_REQ_DFP4 0x04000000L +#define ATOM_S6_ACC_REQ_DFP5 0x08000000L + +#define ATOM_S6_ACC_REQ_MASK 0x0FFF0000L +#define ATOM_S6_SYSTEM_POWER_MODE_CHANGE 0x10000000L +#define ATOM_S6_ACC_BLOCK_DISPLAY_SWITCH 0x20000000L +#define ATOM_S6_VRI_BRIGHTNESS_CHANGE 0x40000000L +#define ATOM_S6_CONFIG_DISPLAY_CHANGE_MASK 0x80000000L + +//Byte aligned definition for BIOS usage +#define ATOM_S6_DEVICE_CHANGEb0 0x01 +#define ATOM_S6_SCALER_CHANGEb0 0x02 +#define ATOM_S6_LID_CHANGEb0 0x04 +#define ATOM_S6_DOCKING_CHANGEb0 0x08 +#define ATOM_S6_ACC_MODEb0 0x10 +#define ATOM_S6_EXT_DESKTOP_MODEb0 0x20 +#define ATOM_S6_LID_STATEb0 0x40 +#define ATOM_S6_DOCK_STATEb0 0x80 +#define ATOM_S6_CRITICAL_STATEb1 0x01 +#define ATOM_S6_HW_I2C_BUSY_STATEb1 0x02 +#define ATOM_S6_THERMAL_STATE_CHANGEb1 0x04 +#define ATOM_S6_INTERRUPT_SET_BY_BIOSb1 0x08 +#define ATOM_S6_REQ_LCD_EXPANSION_FULLb1 0x10 +#define ATOM_S6_REQ_LCD_EXPANSION_ASPEC_RATIOb1 0x20 + +#define ATOM_S6_ACC_REQ_CRT1b2 0x01 +#define ATOM_S6_ACC_REQ_LCD1b2 0x02 +#define ATOM_S6_ACC_REQ_TV1b2 0x04 +#define ATOM_S6_ACC_REQ_DFP1b2 0x08 +#define ATOM_S6_ACC_REQ_CRT2b2 0x10 +#define ATOM_S6_ACC_REQ_LCD2b2 0x20 +#define ATOM_S6_ACC_REQ_DFP6b2 0x40 +#define ATOM_S6_ACC_REQ_DFP2b2 0x80 +#define ATOM_S6_ACC_REQ_CVb3 0x01 +#define ATOM_S6_ACC_REQ_DFP3b3 0x02 +#define ATOM_S6_ACC_REQ_DFP4b3 0x04 +#define ATOM_S6_ACC_REQ_DFP5b3 0x08 + +#define ATOM_S6_ACC_REQ_DEVICEw1 ATOM_S5_DOS_REQ_DEVICEw0 +#define ATOM_S6_SYSTEM_POWER_MODE_CHANGEb3 0x10 +#define ATOM_S6_ACC_BLOCK_DISPLAY_SWITCHb3 0x20 +#define ATOM_S6_VRI_BRIGHTNESS_CHANGEb3 0x40 +#define ATOM_S6_CONFIG_DISPLAY_CHANGEb3 0x80 + +#define ATOM_S6_DEVICE_CHANGE_SHIFT 0 +#define ATOM_S6_SCALER_CHANGE_SHIFT 1 +#define ATOM_S6_LID_CHANGE_SHIFT 2 +#define ATOM_S6_DOCKING_CHANGE_SHIFT 3 +#define ATOM_S6_ACC_MODE_SHIFT 4 +#define ATOM_S6_EXT_DESKTOP_MODE_SHIFT 5 +#define ATOM_S6_LID_STATE_SHIFT 6 +#define ATOM_S6_DOCK_STATE_SHIFT 7 +#define ATOM_S6_CRITICAL_STATE_SHIFT 8 +#define ATOM_S6_HW_I2C_BUSY_STATE_SHIFT 9 +#define ATOM_S6_THERMAL_STATE_CHANGE_SHIFT 10 +#define ATOM_S6_INTERRUPT_SET_BY_BIOS_SHIFT 11 +#define ATOM_S6_REQ_SCALER_SHIFT 12 +#define ATOM_S6_REQ_SCALER_ARATIO_SHIFT 13 +#define ATOM_S6_DISPLAY_STATE_CHANGE_SHIFT 14 +#define ATOM_S6_I2C_STATE_CHANGE_SHIFT 15 +#define ATOM_S6_SYSTEM_POWER_MODE_CHANGE_SHIFT 28 +#define ATOM_S6_ACC_BLOCK_DISPLAY_SWITCH_SHIFT 29 +#define ATOM_S6_VRI_BRIGHTNESS_CHANGE_SHIFT 30 +#define ATOM_S6_CONFIG_DISPLAY_CHANGE_SHIFT 31 + +// BIOS_7_SCRATCH Definition, BIOS_7_SCRATCH is used by Firmware only !!!! +#define ATOM_S7_DOS_MODE_TYPEb0 0x03 +#define ATOM_S7_DOS_MODE_VGAb0 0x00 +#define ATOM_S7_DOS_MODE_VESAb0 0x01 +#define ATOM_S7_DOS_MODE_EXTb0 0x02 +#define ATOM_S7_DOS_MODE_PIXEL_DEPTHb0 0x0C +#define ATOM_S7_DOS_MODE_PIXEL_FORMATb0 0xF0 +#define ATOM_S7_DOS_8BIT_DAC_ENb1 0x01 +#define ATOM_S7_DOS_MODE_NUMBERw1 0x0FFFF + +#define ATOM_S7_DOS_8BIT_DAC_EN_SHIFT 8 + +// BIOS_8_SCRATCH Definition +#define ATOM_S8_I2C_CHANNEL_BUSY_MASK 0x00000FFFF +#define ATOM_S8_I2C_HW_ENGINE_BUSY_MASK 0x0FFFF0000 + +#define ATOM_S8_I2C_CHANNEL_BUSY_SHIFT 0 +#define ATOM_S8_I2C_ENGINE_BUSY_SHIFT 16 + +// BIOS_9_SCRATCH Definition +#ifndef ATOM_S9_I2C_CHANNEL_COMPLETED_MASK +#define ATOM_S9_I2C_CHANNEL_COMPLETED_MASK 0x0000FFFF +#endif +#ifndef ATOM_S9_I2C_CHANNEL_ABORTED_MASK +#define ATOM_S9_I2C_CHANNEL_ABORTED_MASK 0xFFFF0000 +#endif +#ifndef ATOM_S9_I2C_CHANNEL_COMPLETED_SHIFT +#define ATOM_S9_I2C_CHANNEL_COMPLETED_SHIFT 0 +#endif +#ifndef ATOM_S9_I2C_CHANNEL_ABORTED_SHIFT +#define ATOM_S9_I2C_CHANNEL_ABORTED_SHIFT 16 +#endif + + +#define ATOM_FLAG_SET 0x20 +#define ATOM_FLAG_CLEAR 0 +#define CLEAR_ATOM_S6_ACC_MODE ((ATOM_ACC_CHANGE_INFO_DEF << 8 )|ATOM_S6_ACC_MODE_SHIFT | ATOM_FLAG_CLEAR) +#define SET_ATOM_S6_DEVICE_CHANGE ((ATOM_ACC_CHANGE_INFO_DEF << 8 )|ATOM_S6_DEVICE_CHANGE_SHIFT | ATOM_FLAG_SET) +#define SET_ATOM_S6_VRI_BRIGHTNESS_CHANGE ((ATOM_ACC_CHANGE_INFO_DEF << 8 )|ATOM_S6_VRI_BRIGHTNESS_CHANGE_SHIFT | ATOM_FLAG_SET) +#define SET_ATOM_S6_SCALER_CHANGE ((ATOM_ACC_CHANGE_INFO_DEF << 8 )|ATOM_S6_SCALER_CHANGE_SHIFT | ATOM_FLAG_SET) +#define SET_ATOM_S6_LID_CHANGE ((ATOM_ACC_CHANGE_INFO_DEF << 8 )|ATOM_S6_LID_CHANGE_SHIFT | ATOM_FLAG_SET) + +#define SET_ATOM_S6_LID_STATE ((ATOM_ACC_CHANGE_INFO_DEF << 8 )|ATOM_S6_LID_STATE_SHIFT | ATOM_FLAG_SET) +#define CLEAR_ATOM_S6_LID_STATE ((ATOM_ACC_CHANGE_INFO_DEF << 8 )|ATOM_S6_LID_STATE_SHIFT | ATOM_FLAG_CLEAR) + +#define SET_ATOM_S6_DOCK_CHANGE ((ATOM_ACC_CHANGE_INFO_DEF << 8 )|ATOM_S6_DOCKING_CHANGE_SHIFT | ATOM_FLAG_SET) +#define SET_ATOM_S6_DOCK_STATE ((ATOM_ACC_CHANGE_INFO_DEF << 8 )|ATOM_S6_DOCK_STATE_SHIFT | ATOM_FLAG_SET) +#define CLEAR_ATOM_S6_DOCK_STATE ((ATOM_ACC_CHANGE_INFO_DEF << 8 )|ATOM_S6_DOCK_STATE_SHIFT | ATOM_FLAG_CLEAR) + +#define SET_ATOM_S6_THERMAL_STATE_CHANGE ((ATOM_ACC_CHANGE_INFO_DEF << 8 )|ATOM_S6_THERMAL_STATE_CHANGE_SHIFT | ATOM_FLAG_SET) +#define SET_ATOM_S6_SYSTEM_POWER_MODE_CHANGE ((ATOM_ACC_CHANGE_INFO_DEF << 8 )|ATOM_S6_SYSTEM_POWER_MODE_CHANGE_SHIFT | ATOM_FLAG_SET) +#define SET_ATOM_S6_INTERRUPT_SET_BY_BIOS ((ATOM_ACC_CHANGE_INFO_DEF << 8 )|ATOM_S6_INTERRUPT_SET_BY_BIOS_SHIFT | ATOM_FLAG_SET) + +#define SET_ATOM_S6_CRITICAL_STATE ((ATOM_ACC_CHANGE_INFO_DEF << 8 )|ATOM_S6_CRITICAL_STATE_SHIFT | ATOM_FLAG_SET) +#define CLEAR_ATOM_S6_CRITICAL_STATE ((ATOM_ACC_CHANGE_INFO_DEF << 8 )|ATOM_S6_CRITICAL_STATE_SHIFT | ATOM_FLAG_CLEAR) + +#define SET_ATOM_S6_REQ_SCALER ((ATOM_ACC_CHANGE_INFO_DEF << 8 )|ATOM_S6_REQ_SCALER_SHIFT | ATOM_FLAG_SET) +#define CLEAR_ATOM_S6_REQ_SCALER ((ATOM_ACC_CHANGE_INFO_DEF << 8 )|ATOM_S6_REQ_SCALER_SHIFT | ATOM_FLAG_CLEAR ) + +#define SET_ATOM_S6_REQ_SCALER_ARATIO ((ATOM_ACC_CHANGE_INFO_DEF << 8 )|ATOM_S6_REQ_SCALER_ARATIO_SHIFT | ATOM_FLAG_SET ) +#define CLEAR_ATOM_S6_REQ_SCALER_ARATIO ((ATOM_ACC_CHANGE_INFO_DEF << 8 )|ATOM_S6_REQ_SCALER_ARATIO_SHIFT | ATOM_FLAG_CLEAR ) + +#define SET_ATOM_S6_I2C_STATE_CHANGE ((ATOM_ACC_CHANGE_INFO_DEF << 8 )|ATOM_S6_I2C_STATE_CHANGE_SHIFT | ATOM_FLAG_SET ) + +#define SET_ATOM_S6_DISPLAY_STATE_CHANGE ((ATOM_ACC_CHANGE_INFO_DEF << 8 )|ATOM_S6_DISPLAY_STATE_CHANGE_SHIFT | ATOM_FLAG_SET ) + +#define SET_ATOM_S6_DEVICE_RECONFIG ((ATOM_ACC_CHANGE_INFO_DEF << 8 )|ATOM_S6_CONFIG_DISPLAY_CHANGE_SHIFT | ATOM_FLAG_SET) +#define CLEAR_ATOM_S0_LCD1 ((ATOM_DEVICE_CONNECT_INFO_DEF << 8 )| ATOM_S0_LCD1_SHIFT | ATOM_FLAG_CLEAR ) +#define SET_ATOM_S7_DOS_8BIT_DAC_EN ((ATOM_DOS_MODE_INFO_DEF << 8 )|ATOM_S7_DOS_8BIT_DAC_EN_SHIFT | ATOM_FLAG_SET ) +#define CLEAR_ATOM_S7_DOS_8BIT_DAC_EN ((ATOM_DOS_MODE_INFO_DEF << 8 )|ATOM_S7_DOS_8BIT_DAC_EN_SHIFT | ATOM_FLAG_CLEAR ) + +/****************************************************************************/ +//Portion II: Definitinos only used in Driver +/****************************************************************************/ + +// Macros used by driver +#ifdef __cplusplus +#define GetIndexIntoMasterTable(MasterOrData, FieldName) ((reinterpret_cast(&(static_cast(0))->FieldName)-static_cast(0))/sizeof(USHORT)) + +#define GET_COMMAND_TABLE_COMMANDSET_REVISION(TABLE_HEADER_OFFSET) (((static_cast(TABLE_HEADER_OFFSET))->ucTableFormatRevision )&0x3F) +#define GET_COMMAND_TABLE_PARAMETER_REVISION(TABLE_HEADER_OFFSET) (((static_cast(TABLE_HEADER_OFFSET))->ucTableContentRevision)&0x3F) +#else // not __cplusplus +#define GetIndexIntoMasterTable(MasterOrData, FieldName) (((char*)(&((ATOM_MASTER_LIST_OF_##MasterOrData##_TABLES*)0)->FieldName)-(char*)0)/sizeof(USHORT)) + +#define GET_COMMAND_TABLE_COMMANDSET_REVISION(TABLE_HEADER_OFFSET) ((((ATOM_COMMON_TABLE_HEADER*)TABLE_HEADER_OFFSET)->ucTableFormatRevision)&0x3F) +#define GET_COMMAND_TABLE_PARAMETER_REVISION(TABLE_HEADER_OFFSET) ((((ATOM_COMMON_TABLE_HEADER*)TABLE_HEADER_OFFSET)->ucTableContentRevision)&0x3F) +#endif // __cplusplus + +#define GET_DATA_TABLE_MAJOR_REVISION GET_COMMAND_TABLE_COMMANDSET_REVISION +#define GET_DATA_TABLE_MINOR_REVISION GET_COMMAND_TABLE_PARAMETER_REVISION + +/****************************************************************************/ +//Portion III: Definitinos only used in VBIOS +/****************************************************************************/ +#define ATOM_DAC_SRC 0x80 +#define ATOM_SRC_DAC1 0 +#define ATOM_SRC_DAC2 0x80 + +typedef struct _MEMORY_PLLINIT_PARAMETERS +{ + ULONG ulTargetMemoryClock; //In 10Khz unit + UCHAR ucAction; //not define yet + UCHAR ucFbDiv_Hi; //Fbdiv Hi byte + UCHAR ucFbDiv; //FB value + UCHAR ucPostDiv; //Post div +}MEMORY_PLLINIT_PARAMETERS; + +#define MEMORY_PLLINIT_PS_ALLOCATION MEMORY_PLLINIT_PARAMETERS + + +#define GPIO_PIN_WRITE 0x01 +#define GPIO_PIN_READ 0x00 + +typedef struct _GPIO_PIN_CONTROL_PARAMETERS +{ + UCHAR ucGPIO_ID; //return value, read from GPIO pins + UCHAR ucGPIOBitShift; //define which bit in uGPIOBitVal need to be update + UCHAR ucGPIOBitVal; //Set/Reset corresponding bit defined in ucGPIOBitMask + UCHAR ucAction; //=GPIO_PIN_WRITE: Read; =GPIO_PIN_READ: Write +}GPIO_PIN_CONTROL_PARAMETERS; + +typedef struct _ENABLE_SCALER_PARAMETERS +{ + UCHAR ucScaler; // ATOM_SCALER1, ATOM_SCALER2 + UCHAR ucEnable; // ATOM_SCALER_DISABLE or ATOM_SCALER_CENTER or ATOM_SCALER_EXPANSION + UCHAR ucTVStandard; // + UCHAR ucPadding[1]; +}ENABLE_SCALER_PARAMETERS; +#define ENABLE_SCALER_PS_ALLOCATION ENABLE_SCALER_PARAMETERS + +//ucEnable: +#define SCALER_BYPASS_AUTO_CENTER_NO_REPLICATION 0 +#define SCALER_BYPASS_AUTO_CENTER_AUTO_REPLICATION 1 +#define SCALER_ENABLE_2TAP_ALPHA_MODE 2 +#define SCALER_ENABLE_MULTITAP_MODE 3 + +typedef struct _ENABLE_HARDWARE_ICON_CURSOR_PARAMETERS +{ + ULONG usHWIconHorzVertPosn; // Hardware Icon Vertical position + UCHAR ucHWIconVertOffset; // Hardware Icon Vertical offset + UCHAR ucHWIconHorzOffset; // Hardware Icon Horizontal offset + UCHAR ucSelection; // ATOM_CURSOR1 or ATOM_ICON1 or ATOM_CURSOR2 or ATOM_ICON2 + UCHAR ucEnable; // ATOM_ENABLE or ATOM_DISABLE +}ENABLE_HARDWARE_ICON_CURSOR_PARAMETERS; + +typedef struct _ENABLE_HARDWARE_ICON_CURSOR_PS_ALLOCATION +{ + ENABLE_HARDWARE_ICON_CURSOR_PARAMETERS sEnableIcon; + ENABLE_CRTC_PARAMETERS sReserved; +}ENABLE_HARDWARE_ICON_CURSOR_PS_ALLOCATION; + +typedef struct _ENABLE_GRAPH_SURFACE_PARAMETERS +{ + USHORT usHight; // Image Hight + USHORT usWidth; // Image Width + UCHAR ucSurface; // Surface 1 or 2 + UCHAR ucPadding[3]; +}ENABLE_GRAPH_SURFACE_PARAMETERS; + +typedef struct _ENABLE_GRAPH_SURFACE_PARAMETERS_V1_2 +{ + USHORT usHight; // Image Hight + USHORT usWidth; // Image Width + UCHAR ucSurface; // Surface 1 or 2 + UCHAR ucEnable; // ATOM_ENABLE or ATOM_DISABLE + UCHAR ucPadding[2]; +}ENABLE_GRAPH_SURFACE_PARAMETERS_V1_2; + +typedef struct _ENABLE_GRAPH_SURFACE_PARAMETERS_V1_3 +{ + USHORT usHight; // Image Hight + USHORT usWidth; // Image Width + UCHAR ucSurface; // Surface 1 or 2 + UCHAR ucEnable; // ATOM_ENABLE or ATOM_DISABLE + USHORT usDeviceId; // Active Device Id for this surface. If no device, set to 0. +}ENABLE_GRAPH_SURFACE_PARAMETERS_V1_3; + +typedef struct _ENABLE_GRAPH_SURFACE_PARAMETERS_V1_4 +{ + USHORT usHight; // Image Hight + USHORT usWidth; // Image Width + USHORT usGraphPitch; + UCHAR ucColorDepth; + UCHAR ucPixelFormat; + UCHAR ucSurface; // Surface 1 or 2 + UCHAR ucEnable; // ATOM_ENABLE or ATOM_DISABLE + UCHAR ucModeType; + UCHAR ucReserved; +}ENABLE_GRAPH_SURFACE_PARAMETERS_V1_4; + +// ucEnable +#define ATOM_GRAPH_CONTROL_SET_PITCH 0x0f +#define ATOM_GRAPH_CONTROL_SET_DISP_START 0x10 + +typedef struct _ENABLE_GRAPH_SURFACE_PS_ALLOCATION +{ + ENABLE_GRAPH_SURFACE_PARAMETERS sSetSurface; + ENABLE_YUV_PS_ALLOCATION sReserved; // Don't set this one +}ENABLE_GRAPH_SURFACE_PS_ALLOCATION; + +typedef struct _MEMORY_CLEAN_UP_PARAMETERS +{ + USHORT usMemoryStart; //in 8Kb boundary, offset from memory base address + USHORT usMemorySize; //8Kb blocks aligned +}MEMORY_CLEAN_UP_PARAMETERS; +#define MEMORY_CLEAN_UP_PS_ALLOCATION MEMORY_CLEAN_UP_PARAMETERS + +typedef struct _GET_DISPLAY_SURFACE_SIZE_PARAMETERS +{ + USHORT usX_Size; //When use as input parameter, usX_Size indicates which CRTC + USHORT usY_Size; +}GET_DISPLAY_SURFACE_SIZE_PARAMETERS; + +typedef struct _GET_DISPLAY_SURFACE_SIZE_PARAMETERS_V2 +{ + union{ + USHORT usX_Size; //When use as input parameter, usX_Size indicates which CRTC + USHORT usSurface; + }; + USHORT usY_Size; + USHORT usDispXStart; + USHORT usDispYStart; +}GET_DISPLAY_SURFACE_SIZE_PARAMETERS_V2; + + +typedef struct _PALETTE_DATA_CONTROL_PARAMETERS_V3 +{ + UCHAR ucLutId; + UCHAR ucAction; + USHORT usLutStartIndex; + USHORT usLutLength; + USHORT usLutOffsetInVram; +}PALETTE_DATA_CONTROL_PARAMETERS_V3; + +// ucAction: +#define PALETTE_DATA_AUTO_FILL 1 +#define PALETTE_DATA_READ 2 +#define PALETTE_DATA_WRITE 3 + + +typedef struct _INTERRUPT_SERVICE_PARAMETERS_V2 +{ + UCHAR ucInterruptId; + UCHAR ucServiceId; + UCHAR ucStatus; + UCHAR ucReserved; +}INTERRUPT_SERVICE_PARAMETER_V2; + +// ucInterruptId +#define HDP1_INTERRUPT_ID 1 +#define HDP2_INTERRUPT_ID 2 +#define HDP3_INTERRUPT_ID 3 +#define HDP4_INTERRUPT_ID 4 +#define HDP5_INTERRUPT_ID 5 +#define HDP6_INTERRUPT_ID 6 +#define SW_INTERRUPT_ID 11 + +// ucAction +#define INTERRUPT_SERVICE_GEN_SW_INT 1 +#define INTERRUPT_SERVICE_GET_STATUS 2 + + // ucStatus +#define INTERRUPT_STATUS__INT_TRIGGER 1 +#define INTERRUPT_STATUS__HPD_HIGH 2 + +typedef struct _INDIRECT_IO_ACCESS +{ + ATOM_COMMON_TABLE_HEADER sHeader; + UCHAR IOAccessSequence[256]; +} INDIRECT_IO_ACCESS; + +#define INDIRECT_READ 0x00 +#define INDIRECT_WRITE 0x80 + +#define INDIRECT_IO_MM 0 +#define INDIRECT_IO_PLL 1 +#define INDIRECT_IO_MC 2 +#define INDIRECT_IO_PCIE 3 +#define INDIRECT_IO_PCIEP 4 +#define INDIRECT_IO_NBMISC 5 + +#define INDIRECT_IO_PLL_READ INDIRECT_IO_PLL | INDIRECT_READ +#define INDIRECT_IO_PLL_WRITE INDIRECT_IO_PLL | INDIRECT_WRITE +#define INDIRECT_IO_MC_READ INDIRECT_IO_MC | INDIRECT_READ +#define INDIRECT_IO_MC_WRITE INDIRECT_IO_MC | INDIRECT_WRITE +#define INDIRECT_IO_PCIE_READ INDIRECT_IO_PCIE | INDIRECT_READ +#define INDIRECT_IO_PCIE_WRITE INDIRECT_IO_PCIE | INDIRECT_WRITE +#define INDIRECT_IO_PCIEP_READ INDIRECT_IO_PCIEP | INDIRECT_READ +#define INDIRECT_IO_PCIEP_WRITE INDIRECT_IO_PCIEP | INDIRECT_WRITE +#define INDIRECT_IO_NBMISC_READ INDIRECT_IO_NBMISC | INDIRECT_READ +#define INDIRECT_IO_NBMISC_WRITE INDIRECT_IO_NBMISC | INDIRECT_WRITE + +typedef struct _ATOM_OEM_INFO +{ + ATOM_COMMON_TABLE_HEADER sHeader; + ATOM_I2C_ID_CONFIG_ACCESS sucI2cId; +}ATOM_OEM_INFO; + +typedef struct _ATOM_TV_MODE +{ + UCHAR ucVMode_Num; //Video mode number + UCHAR ucTV_Mode_Num; //Internal TV mode number +}ATOM_TV_MODE; + +typedef struct _ATOM_BIOS_INT_TVSTD_MODE +{ + ATOM_COMMON_TABLE_HEADER sHeader; + USHORT usTV_Mode_LUT_Offset; // Pointer to standard to internal number conversion table + USHORT usTV_FIFO_Offset; // Pointer to FIFO entry table + USHORT usNTSC_Tbl_Offset; // Pointer to SDTV_Mode_NTSC table + USHORT usPAL_Tbl_Offset; // Pointer to SDTV_Mode_PAL table + USHORT usCV_Tbl_Offset; // Pointer to SDTV_Mode_PAL table +}ATOM_BIOS_INT_TVSTD_MODE; + + +typedef struct _ATOM_TV_MODE_SCALER_PTR +{ + USHORT ucFilter0_Offset; //Pointer to filter format 0 coefficients + USHORT usFilter1_Offset; //Pointer to filter format 0 coefficients + UCHAR ucTV_Mode_Num; +}ATOM_TV_MODE_SCALER_PTR; + +typedef struct _ATOM_STANDARD_VESA_TIMING +{ + ATOM_COMMON_TABLE_HEADER sHeader; + ATOM_DTD_FORMAT aModeTimings[16]; // 16 is not the real array number, just for initial allocation +}ATOM_STANDARD_VESA_TIMING; + + +typedef struct _ATOM_STD_FORMAT +{ + USHORT usSTD_HDisp; + USHORT usSTD_VDisp; + USHORT usSTD_RefreshRate; + USHORT usReserved; +}ATOM_STD_FORMAT; + +typedef struct _ATOM_VESA_TO_EXTENDED_MODE +{ + USHORT usVESA_ModeNumber; + USHORT usExtendedModeNumber; +}ATOM_VESA_TO_EXTENDED_MODE; + +typedef struct _ATOM_VESA_TO_INTENAL_MODE_LUT +{ + ATOM_COMMON_TABLE_HEADER sHeader; + ATOM_VESA_TO_EXTENDED_MODE asVESA_ToExtendedModeInfo[76]; +}ATOM_VESA_TO_INTENAL_MODE_LUT; + +/*************** ATOM Memory Related Data Structure ***********************/ +typedef struct _ATOM_MEMORY_VENDOR_BLOCK{ + UCHAR ucMemoryType; + UCHAR ucMemoryVendor; + UCHAR ucAdjMCId; + UCHAR ucDynClkId; + ULONG ulDllResetClkRange; +}ATOM_MEMORY_VENDOR_BLOCK; + + +typedef struct _ATOM_MEMORY_SETTING_ID_CONFIG{ +#if ATOM_BIG_ENDIAN + ULONG ucMemBlkId:8; + ULONG ulMemClockRange:24; +#else + ULONG ulMemClockRange:24; + ULONG ucMemBlkId:8; +#endif +}ATOM_MEMORY_SETTING_ID_CONFIG; + +typedef union _ATOM_MEMORY_SETTING_ID_CONFIG_ACCESS +{ + ATOM_MEMORY_SETTING_ID_CONFIG slAccess; + ULONG ulAccess; +}ATOM_MEMORY_SETTING_ID_CONFIG_ACCESS; + + +typedef struct _ATOM_MEMORY_SETTING_DATA_BLOCK{ + ATOM_MEMORY_SETTING_ID_CONFIG_ACCESS ulMemoryID; + ULONG aulMemData[1]; +}ATOM_MEMORY_SETTING_DATA_BLOCK; + + +typedef struct _ATOM_INIT_REG_INDEX_FORMAT{ + USHORT usRegIndex; // MC register index + UCHAR ucPreRegDataLength; // offset in ATOM_INIT_REG_DATA_BLOCK.saRegDataBuf +}ATOM_INIT_REG_INDEX_FORMAT; + + +typedef struct _ATOM_INIT_REG_BLOCK{ + USHORT usRegIndexTblSize; //size of asRegIndexBuf + USHORT usRegDataBlkSize; //size of ATOM_MEMORY_SETTING_DATA_BLOCK + ATOM_INIT_REG_INDEX_FORMAT asRegIndexBuf[1]; + ATOM_MEMORY_SETTING_DATA_BLOCK asRegDataBuf[1]; +}ATOM_INIT_REG_BLOCK; + +#define END_OF_REG_INDEX_BLOCK 0x0ffff +#define END_OF_REG_DATA_BLOCK 0x00000000 +#define ATOM_INIT_REG_MASK_FLAG 0x80 //Not used in BIOS +#define CLOCK_RANGE_HIGHEST 0x00ffffff + +#define VALUE_DWORD SIZEOF ULONG +#define VALUE_SAME_AS_ABOVE 0 +#define VALUE_MASK_DWORD 0x84 + +#define INDEX_ACCESS_RANGE_BEGIN (VALUE_DWORD + 1) +#define INDEX_ACCESS_RANGE_END (INDEX_ACCESS_RANGE_BEGIN + 1) +#define VALUE_INDEX_ACCESS_SINGLE (INDEX_ACCESS_RANGE_END + 1) +//#define ACCESS_MCIODEBUGIND 0x40 //defined in BIOS code +#define ACCESS_PLACEHOLDER 0x80 + +typedef struct _ATOM_MC_INIT_PARAM_TABLE +{ + ATOM_COMMON_TABLE_HEADER sHeader; + USHORT usAdjustARB_SEQDataOffset; + USHORT usMCInitMemTypeTblOffset; + USHORT usMCInitCommonTblOffset; + USHORT usMCInitPowerDownTblOffset; + ULONG ulARB_SEQDataBuf[32]; + ATOM_INIT_REG_BLOCK asMCInitMemType; + ATOM_INIT_REG_BLOCK asMCInitCommon; +}ATOM_MC_INIT_PARAM_TABLE; + + +#define _4Mx16 0x2 +#define _4Mx32 0x3 +#define _8Mx16 0x12 +#define _8Mx32 0x13 +#define _16Mx16 0x22 +#define _16Mx32 0x23 +#define _32Mx16 0x32 +#define _32Mx32 0x33 +#define _64Mx8 0x41 +#define _64Mx16 0x42 +#define _64Mx32 0x43 +#define _128Mx8 0x51 +#define _128Mx16 0x52 +#define _256Mx8 0x61 +#define _256Mx16 0x62 + +#define SAMSUNG 0x1 +#define INFINEON 0x2 +#define ELPIDA 0x3 +#define ETRON 0x4 +#define NANYA 0x5 +#define HYNIX 0x6 +#define MOSEL 0x7 +#define WINBOND 0x8 +#define ESMT 0x9 +#define MICRON 0xF + +#define QIMONDA INFINEON +#define PROMOS MOSEL +#define KRETON INFINEON +#define ELIXIR NANYA + +/////////////Support for GDDR5 MC uCode to reside in upper 64K of ROM///////////// + +#define UCODE_ROM_START_ADDRESS 0x1b800 +#define UCODE_SIGNATURE 0x4375434d // 'MCuC' - MC uCode + +//uCode block header for reference + +typedef struct _MCuCodeHeader +{ + ULONG ulSignature; + UCHAR ucRevision; + UCHAR ucChecksum; + UCHAR ucReserved1; + UCHAR ucReserved2; + USHORT usParametersLength; + USHORT usUCodeLength; + USHORT usReserved1; + USHORT usReserved2; +} MCuCodeHeader; + +////////////////////////////////////////////////////////////////////////////////// + +#define ATOM_MAX_NUMBER_OF_VRAM_MODULE 16 + +#define ATOM_VRAM_MODULE_MEMORY_VENDOR_ID_MASK 0xF +typedef struct _ATOM_VRAM_MODULE_V1 +{ + ULONG ulReserved; + USHORT usEMRSValue; + USHORT usMRSValue; + USHORT usReserved; + UCHAR ucExtMemoryID; // An external indicator (by hardcode, callback or pin) to tell what is the current memory module + UCHAR ucMemoryType; // [7:4]=0x1:DDR1;=0x2:DDR2;=0x3:DDR3;=0x4:DDR4;[3:0] reserved; + UCHAR ucMemoryVenderID; // Predefined,never change across designs or memory type/vender + UCHAR ucMemoryDeviceCfg; // [7:4]=0x0:4M;=0x1:8M;=0x2:16M;0x3:32M....[3:0]=0x0:x4;=0x1:x8;=0x2:x16;=0x3:x32... + UCHAR ucRow; // Number of Row,in power of 2; + UCHAR ucColumn; // Number of Column,in power of 2; + UCHAR ucBank; // Nunber of Bank; + UCHAR ucRank; // Number of Rank, in power of 2 + UCHAR ucChannelNum; // Number of channel; + UCHAR ucChannelConfig; // [3:0]=Indication of what channel combination;[4:7]=Channel bit width, in number of 2 + UCHAR ucDefaultMVDDQ_ID; // Default MVDDQ setting for this memory block, ID linking to MVDDQ info table to find real set-up data; + UCHAR ucDefaultMVDDC_ID; // Default MVDDC setting for this memory block, ID linking to MVDDC info table to find real set-up data; + UCHAR ucReserved[2]; +}ATOM_VRAM_MODULE_V1; + + +typedef struct _ATOM_VRAM_MODULE_V2 +{ + ULONG ulReserved; + ULONG ulFlags; // To enable/disable functionalities based on memory type + ULONG ulEngineClock; // Override of default engine clock for particular memory type + ULONG ulMemoryClock; // Override of default memory clock for particular memory type + USHORT usEMRS2Value; // EMRS2 Value is used for GDDR2 and GDDR4 memory type + USHORT usEMRS3Value; // EMRS3 Value is used for GDDR2 and GDDR4 memory type + USHORT usEMRSValue; + USHORT usMRSValue; + USHORT usReserved; + UCHAR ucExtMemoryID; // An external indicator (by hardcode, callback or pin) to tell what is the current memory module + UCHAR ucMemoryType; // [7:4]=0x1:DDR1;=0x2:DDR2;=0x3:DDR3;=0x4:DDR4;[3:0] - must not be used for now; + UCHAR ucMemoryVenderID; // Predefined,never change across designs or memory type/vender. If not predefined, vendor detection table gets executed + UCHAR ucMemoryDeviceCfg; // [7:4]=0x0:4M;=0x1:8M;=0x2:16M;0x3:32M....[3:0]=0x0:x4;=0x1:x8;=0x2:x16;=0x3:x32... + UCHAR ucRow; // Number of Row,in power of 2; + UCHAR ucColumn; // Number of Column,in power of 2; + UCHAR ucBank; // Nunber of Bank; + UCHAR ucRank; // Number of Rank, in power of 2 + UCHAR ucChannelNum; // Number of channel; + UCHAR ucChannelConfig; // [3:0]=Indication of what channel combination;[4:7]=Channel bit width, in number of 2 + UCHAR ucDefaultMVDDQ_ID; // Default MVDDQ setting for this memory block, ID linking to MVDDQ info table to find real set-up data; + UCHAR ucDefaultMVDDC_ID; // Default MVDDC setting for this memory block, ID linking to MVDDC info table to find real set-up data; + UCHAR ucRefreshRateFactor; + UCHAR ucReserved[3]; +}ATOM_VRAM_MODULE_V2; + + +typedef struct _ATOM_MEMORY_TIMING_FORMAT +{ + ULONG ulClkRange; // memory clock in 10kHz unit, when target memory clock is below this clock, use this memory timing + union{ + USHORT usMRS; // mode register + USHORT usDDR3_MR0; + }; + union{ + USHORT usEMRS; // extended mode register + USHORT usDDR3_MR1; + }; + UCHAR ucCL; // CAS latency + UCHAR ucWL; // WRITE Latency + UCHAR uctRAS; // tRAS + UCHAR uctRC; // tRC + UCHAR uctRFC; // tRFC + UCHAR uctRCDR; // tRCDR + UCHAR uctRCDW; // tRCDW + UCHAR uctRP; // tRP + UCHAR uctRRD; // tRRD + UCHAR uctWR; // tWR + UCHAR uctWTR; // tWTR + UCHAR uctPDIX; // tPDIX + UCHAR uctFAW; // tFAW + UCHAR uctAOND; // tAOND + union + { + struct { + UCHAR ucflag; // flag to control memory timing calculation. bit0= control EMRS2 Infineon + UCHAR ucReserved; + }; + USHORT usDDR3_MR2; + }; +}ATOM_MEMORY_TIMING_FORMAT; + + +typedef struct _ATOM_MEMORY_TIMING_FORMAT_V1 +{ + ULONG ulClkRange; // memory clock in 10kHz unit, when target memory clock is below this clock, use this memory timing + USHORT usMRS; // mode register + USHORT usEMRS; // extended mode register + UCHAR ucCL; // CAS latency + UCHAR ucWL; // WRITE Latency + UCHAR uctRAS; // tRAS + UCHAR uctRC; // tRC + UCHAR uctRFC; // tRFC + UCHAR uctRCDR; // tRCDR + UCHAR uctRCDW; // tRCDW + UCHAR uctRP; // tRP + UCHAR uctRRD; // tRRD + UCHAR uctWR; // tWR + UCHAR uctWTR; // tWTR + UCHAR uctPDIX; // tPDIX + UCHAR uctFAW; // tFAW + UCHAR uctAOND; // tAOND + UCHAR ucflag; // flag to control memory timing calculation. bit0= control EMRS2 Infineon +////////////////////////////////////GDDR parameters/////////////////////////////////// + UCHAR uctCCDL; // + UCHAR uctCRCRL; // + UCHAR uctCRCWL; // + UCHAR uctCKE; // + UCHAR uctCKRSE; // + UCHAR uctCKRSX; // + UCHAR uctFAW32; // + UCHAR ucMR5lo; // + UCHAR ucMR5hi; // + UCHAR ucTerminator; +}ATOM_MEMORY_TIMING_FORMAT_V1; + +typedef struct _ATOM_MEMORY_TIMING_FORMAT_V2 +{ + ULONG ulClkRange; // memory clock in 10kHz unit, when target memory clock is below this clock, use this memory timing + USHORT usMRS; // mode register + USHORT usEMRS; // extended mode register + UCHAR ucCL; // CAS latency + UCHAR ucWL; // WRITE Latency + UCHAR uctRAS; // tRAS + UCHAR uctRC; // tRC + UCHAR uctRFC; // tRFC + UCHAR uctRCDR; // tRCDR + UCHAR uctRCDW; // tRCDW + UCHAR uctRP; // tRP + UCHAR uctRRD; // tRRD + UCHAR uctWR; // tWR + UCHAR uctWTR; // tWTR + UCHAR uctPDIX; // tPDIX + UCHAR uctFAW; // tFAW + UCHAR uctAOND; // tAOND + UCHAR ucflag; // flag to control memory timing calculation. bit0= control EMRS2 Infineon +////////////////////////////////////GDDR parameters/////////////////////////////////// + UCHAR uctCCDL; // + UCHAR uctCRCRL; // + UCHAR uctCRCWL; // + UCHAR uctCKE; // + UCHAR uctCKRSE; // + UCHAR uctCKRSX; // + UCHAR uctFAW32; // + UCHAR ucMR4lo; // + UCHAR ucMR4hi; // + UCHAR ucMR5lo; // + UCHAR ucMR5hi; // + UCHAR ucTerminator; + UCHAR ucReserved; +}ATOM_MEMORY_TIMING_FORMAT_V2; + +typedef struct _ATOM_MEMORY_FORMAT +{ + ULONG ulDllDisClock; // memory DLL will be disable when target memory clock is below this clock + union{ + USHORT usEMRS2Value; // EMRS2 Value is used for GDDR2 and GDDR4 memory type + USHORT usDDR3_Reserved; // Not used for DDR3 memory + }; + union{ + USHORT usEMRS3Value; // EMRS3 Value is used for GDDR2 and GDDR4 memory type + USHORT usDDR3_MR3; // Used for DDR3 memory + }; + UCHAR ucMemoryType; // [7:4]=0x1:DDR1;=0x2:DDR2;=0x3:DDR3;=0x4:DDR4;[3:0] - must not be used for now; + UCHAR ucMemoryVenderID; // Predefined,never change across designs or memory type/vender. If not predefined, vendor detection table gets executed + UCHAR ucRow; // Number of Row,in power of 2; + UCHAR ucColumn; // Number of Column,in power of 2; + UCHAR ucBank; // Nunber of Bank; + UCHAR ucRank; // Number of Rank, in power of 2 + UCHAR ucBurstSize; // burst size, 0= burst size=4 1= burst size=8 + UCHAR ucDllDisBit; // position of DLL Enable/Disable bit in EMRS ( Extended Mode Register ) + UCHAR ucRefreshRateFactor; // memory refresh rate in unit of ms + UCHAR ucDensity; // _8Mx32, _16Mx32, _16Mx16, _32Mx16 + UCHAR ucPreamble; //[7:4] Write Preamble, [3:0] Read Preamble + UCHAR ucMemAttrib; // Memory Device Addribute, like RDBI/WDBI etc + ATOM_MEMORY_TIMING_FORMAT asMemTiming[5]; //Memory Timing block sort from lower clock to higher clock +}ATOM_MEMORY_FORMAT; + + +typedef struct _ATOM_VRAM_MODULE_V3 +{ + ULONG ulChannelMapCfg; // board dependent paramenter:Channel combination + USHORT usSize; // size of ATOM_VRAM_MODULE_V3 + USHORT usDefaultMVDDQ; // board dependent parameter:Default Memory Core Voltage + USHORT usDefaultMVDDC; // board dependent parameter:Default Memory IO Voltage + UCHAR ucExtMemoryID; // An external indicator (by hardcode, callback or pin) to tell what is the current memory module + UCHAR ucChannelNum; // board dependent parameter:Number of channel; + UCHAR ucChannelSize; // board dependent parameter:32bit or 64bit + UCHAR ucVREFI; // board dependnt parameter: EXT or INT +160mv to -140mv + UCHAR ucNPL_RT; // board dependent parameter:NPL round trip delay, used for calculate memory timing parameters + UCHAR ucFlag; // To enable/disable functionalities based on memory type + ATOM_MEMORY_FORMAT asMemory; // describ all of video memory parameters from memory spec +}ATOM_VRAM_MODULE_V3; + + +//ATOM_VRAM_MODULE_V3.ucNPL_RT +#define NPL_RT_MASK 0x0f +#define BATTERY_ODT_MASK 0xc0 + +#define ATOM_VRAM_MODULE ATOM_VRAM_MODULE_V3 + +typedef struct _ATOM_VRAM_MODULE_V4 +{ + ULONG ulChannelMapCfg; // board dependent parameter: Channel combination + USHORT usModuleSize; // size of ATOM_VRAM_MODULE_V4, make it easy for VBIOS to look for next entry of VRAM_MODULE + USHORT usPrivateReserved; // BIOS internal reserved space to optimize code size, updated by the compiler, shouldn't be modified manually!! + // MC_ARB_RAMCFG (includes NOOFBANK,NOOFRANKS,NOOFROWS,NOOFCOLS) + USHORT usReserved; + UCHAR ucExtMemoryID; // An external indicator (by hardcode, callback or pin) to tell what is the current memory module + UCHAR ucMemoryType; // [7:4]=0x1:DDR1;=0x2:DDR2;=0x3:DDR3;=0x4:DDR4; 0x5:DDR5 [3:0] - Must be 0x0 for now; + UCHAR ucChannelNum; // Number of channels present in this module config + UCHAR ucChannelWidth; // 0 - 32 bits; 1 - 64 bits + UCHAR ucDensity; // _8Mx32, _16Mx32, _16Mx16, _32Mx16 + UCHAR ucFlag; // To enable/disable functionalities based on memory type + UCHAR ucMisc; // bit0: 0 - single rank; 1 - dual rank; bit2: 0 - burstlength 4, 1 - burstlength 8 + UCHAR ucVREFI; // board dependent parameter + UCHAR ucNPL_RT; // board dependent parameter:NPL round trip delay, used for calculate memory timing parameters + UCHAR ucPreamble; // [7:4] Write Preamble, [3:0] Read Preamble + UCHAR ucMemorySize; // BIOS internal reserved space to optimize code size, updated by the compiler, shouldn't be modified manually!! + // Total memory size in unit of 16MB for CONFIG_MEMSIZE - bit[23:0] zeros + UCHAR ucReserved[3]; + +//compare with V3, we flat the struct by merging ATOM_MEMORY_FORMAT (as is) into V4 as the same level + union{ + USHORT usEMRS2Value; // EMRS2 Value is used for GDDR2 and GDDR4 memory type + USHORT usDDR3_Reserved; + }; + union{ + USHORT usEMRS3Value; // EMRS3 Value is used for GDDR2 and GDDR4 memory type + USHORT usDDR3_MR3; // Used for DDR3 memory + }; + UCHAR ucMemoryVenderID; // Predefined, If not predefined, vendor detection table gets executed + UCHAR ucRefreshRateFactor; // [1:0]=RefreshFactor (00=8ms, 01=16ms, 10=32ms,11=64ms) + UCHAR ucReserved2[2]; + ATOM_MEMORY_TIMING_FORMAT asMemTiming[5];//Memory Timing block sort from lower clock to higher clock +}ATOM_VRAM_MODULE_V4; + +#define VRAM_MODULE_V4_MISC_RANK_MASK 0x3 +#define VRAM_MODULE_V4_MISC_DUAL_RANK 0x1 +#define VRAM_MODULE_V4_MISC_BL_MASK 0x4 +#define VRAM_MODULE_V4_MISC_BL8 0x4 +#define VRAM_MODULE_V4_MISC_DUAL_CS 0x10 + +typedef struct _ATOM_VRAM_MODULE_V5 +{ + ULONG ulChannelMapCfg; // board dependent parameter: Channel combination + USHORT usModuleSize; // size of ATOM_VRAM_MODULE_V4, make it easy for VBIOS to look for next entry of VRAM_MODULE + USHORT usPrivateReserved; // BIOS internal reserved space to optimize code size, updated by the compiler, shouldn't be modified manually!! + // MC_ARB_RAMCFG (includes NOOFBANK,NOOFRANKS,NOOFROWS,NOOFCOLS) + USHORT usReserved; + UCHAR ucExtMemoryID; // An external indicator (by hardcode, callback or pin) to tell what is the current memory module + UCHAR ucMemoryType; // [7:4]=0x1:DDR1;=0x2:DDR2;=0x3:DDR3;=0x4:DDR4; 0x5:DDR5 [3:0] - Must be 0x0 for now; + UCHAR ucChannelNum; // Number of channels present in this module config + UCHAR ucChannelWidth; // 0 - 32 bits; 1 - 64 bits + UCHAR ucDensity; // _8Mx32, _16Mx32, _16Mx16, _32Mx16 + UCHAR ucFlag; // To enable/disable functionalities based on memory type + UCHAR ucMisc; // bit0: 0 - single rank; 1 - dual rank; bit2: 0 - burstlength 4, 1 - burstlength 8 + UCHAR ucVREFI; // board dependent parameter + UCHAR ucNPL_RT; // board dependent parameter:NPL round trip delay, used for calculate memory timing parameters + UCHAR ucPreamble; // [7:4] Write Preamble, [3:0] Read Preamble + UCHAR ucMemorySize; // BIOS internal reserved space to optimize code size, updated by the compiler, shouldn't be modified manually!! + // Total memory size in unit of 16MB for CONFIG_MEMSIZE - bit[23:0] zeros + UCHAR ucReserved[3]; + +//compare with V3, we flat the struct by merging ATOM_MEMORY_FORMAT (as is) into V4 as the same level + USHORT usEMRS2Value; // EMRS2 Value is used for GDDR2 and GDDR4 memory type + USHORT usEMRS3Value; // EMRS3 Value is used for GDDR2 and GDDR4 memory type + UCHAR ucMemoryVenderID; // Predefined, If not predefined, vendor detection table gets executed + UCHAR ucRefreshRateFactor; // [1:0]=RefreshFactor (00=8ms, 01=16ms, 10=32ms,11=64ms) + UCHAR ucFIFODepth; // FIFO depth supposes to be detected during vendor detection, but if we dont do vendor detection we have to hardcode FIFO Depth + UCHAR ucCDR_Bandwidth; // [0:3]=Read CDR bandwidth, [4:7] - Write CDR Bandwidth + ATOM_MEMORY_TIMING_FORMAT_V1 asMemTiming[5];//Memory Timing block sort from lower clock to higher clock +}ATOM_VRAM_MODULE_V5; + +typedef struct _ATOM_VRAM_MODULE_V6 +{ + ULONG ulChannelMapCfg; // board dependent parameter: Channel combination + USHORT usModuleSize; // size of ATOM_VRAM_MODULE_V4, make it easy for VBIOS to look for next entry of VRAM_MODULE + USHORT usPrivateReserved; // BIOS internal reserved space to optimize code size, updated by the compiler, shouldn't be modified manually!! + // MC_ARB_RAMCFG (includes NOOFBANK,NOOFRANKS,NOOFROWS,NOOFCOLS) + USHORT usReserved; + UCHAR ucExtMemoryID; // An external indicator (by hardcode, callback or pin) to tell what is the current memory module + UCHAR ucMemoryType; // [7:4]=0x1:DDR1;=0x2:DDR2;=0x3:DDR3;=0x4:DDR4; 0x5:DDR5 [3:0] - Must be 0x0 for now; + UCHAR ucChannelNum; // Number of channels present in this module config + UCHAR ucChannelWidth; // 0 - 32 bits; 1 - 64 bits + UCHAR ucDensity; // _8Mx32, _16Mx32, _16Mx16, _32Mx16 + UCHAR ucFlag; // To enable/disable functionalities based on memory type + UCHAR ucMisc; // bit0: 0 - single rank; 1 - dual rank; bit2: 0 - burstlength 4, 1 - burstlength 8 + UCHAR ucVREFI; // board dependent parameter + UCHAR ucNPL_RT; // board dependent parameter:NPL round trip delay, used for calculate memory timing parameters + UCHAR ucPreamble; // [7:4] Write Preamble, [3:0] Read Preamble + UCHAR ucMemorySize; // BIOS internal reserved space to optimize code size, updated by the compiler, shouldn't be modified manually!! + // Total memory size in unit of 16MB for CONFIG_MEMSIZE - bit[23:0] zeros + UCHAR ucReserved[3]; + +//compare with V3, we flat the struct by merging ATOM_MEMORY_FORMAT (as is) into V4 as the same level + USHORT usEMRS2Value; // EMRS2 Value is used for GDDR2 and GDDR4 memory type + USHORT usEMRS3Value; // EMRS3 Value is used for GDDR2 and GDDR4 memory type + UCHAR ucMemoryVenderID; // Predefined, If not predefined, vendor detection table gets executed + UCHAR ucRefreshRateFactor; // [1:0]=RefreshFactor (00=8ms, 01=16ms, 10=32ms,11=64ms) + UCHAR ucFIFODepth; // FIFO depth supposes to be detected during vendor detection, but if we dont do vendor detection we have to hardcode FIFO Depth + UCHAR ucCDR_Bandwidth; // [0:3]=Read CDR bandwidth, [4:7] - Write CDR Bandwidth + ATOM_MEMORY_TIMING_FORMAT_V2 asMemTiming[5];//Memory Timing block sort from lower clock to higher clock +}ATOM_VRAM_MODULE_V6; + +typedef struct _ATOM_VRAM_MODULE_V7 +{ +// Design Specific Values + ULONG ulChannelMapCfg; // mmMC_SHARED_CHREMAP + USHORT usModuleSize; // Size of ATOM_VRAM_MODULE_V7 + USHORT usPrivateReserved; // MC_ARB_RAMCFG (includes NOOFBANK,NOOFRANKS,NOOFROWS,NOOFCOLS) + USHORT usEnableChannels; // bit vector which indicate which channels are enabled + UCHAR ucExtMemoryID; // Current memory module ID + UCHAR ucMemoryType; // MEM_TYPE_DDR2/DDR3/GDDR3/GDDR5 + UCHAR ucChannelNum; // Number of mem. channels supported in this module + UCHAR ucChannelWidth; // CHANNEL_16BIT/CHANNEL_32BIT/CHANNEL_64BIT + UCHAR ucDensity; // _8Mx32, _16Mx32, _16Mx16, _32Mx16 + UCHAR ucReserve; // Former container for Mx_FLAGS like DBI_AC_MODE_ENABLE_ASIC for GDDR4. Not used now. + UCHAR ucMisc; // RANK_OF_THISMEMORY etc. + UCHAR ucVREFI; // Not used. + UCHAR ucNPL_RT; // Round trip delay (MC_SEQ_CAS_TIMING [28:24]:TCL=CL+NPL_RT-2). Always 2. + UCHAR ucPreamble; // [7:4] Write Preamble, [3:0] Read Preamble + UCHAR ucMemorySize; // Total memory size in unit of 16MB for CONFIG_MEMSIZE - bit[23:0] zeros + USHORT usSEQSettingOffset; + UCHAR ucReserved; +// Memory Module specific values + USHORT usEMRS2Value; // EMRS2/MR2 Value. + USHORT usEMRS3Value; // EMRS3/MR3 Value. + UCHAR ucMemoryVenderID; // [7:4] Revision, [3:0] Vendor code + UCHAR ucRefreshRateFactor; // [1:0]=RefreshFactor (00=8ms, 01=16ms, 10=32ms,11=64ms) + UCHAR ucFIFODepth; // FIFO depth can be detected during vendor detection, here is hardcoded per memory + UCHAR ucCDR_Bandwidth; // [0:3]=Read CDR bandwidth, [4:7] - Write CDR Bandwidth + char strMemPNString[20]; // part number end with '0'. +}ATOM_VRAM_MODULE_V7; + +typedef struct _ATOM_VRAM_INFO_V2 +{ + ATOM_COMMON_TABLE_HEADER sHeader; + UCHAR ucNumOfVRAMModule; + ATOM_VRAM_MODULE aVramInfo[ATOM_MAX_NUMBER_OF_VRAM_MODULE]; // just for allocation, real number of blocks is in ucNumOfVRAMModule; +}ATOM_VRAM_INFO_V2; + +typedef struct _ATOM_VRAM_INFO_V3 +{ + ATOM_COMMON_TABLE_HEADER sHeader; + USHORT usMemAdjustTblOffset; // offset of ATOM_INIT_REG_BLOCK structure for memory vendor specific MC adjust setting + USHORT usMemClkPatchTblOffset; // offset of ATOM_INIT_REG_BLOCK structure for memory clock specific MC setting + USHORT usRerseved; + UCHAR aVID_PinsShift[9]; // 8 bit strap maximum+terminator + UCHAR ucNumOfVRAMModule; + ATOM_VRAM_MODULE aVramInfo[ATOM_MAX_NUMBER_OF_VRAM_MODULE]; // just for allocation, real number of blocks is in ucNumOfVRAMModule; + ATOM_INIT_REG_BLOCK asMemPatch; // for allocation + // ATOM_INIT_REG_BLOCK aMemAdjust; +}ATOM_VRAM_INFO_V3; + +#define ATOM_VRAM_INFO_LAST ATOM_VRAM_INFO_V3 + +typedef struct _ATOM_VRAM_INFO_V4 +{ + ATOM_COMMON_TABLE_HEADER sHeader; + USHORT usMemAdjustTblOffset; // offset of ATOM_INIT_REG_BLOCK structure for memory vendor specific MC adjust setting + USHORT usMemClkPatchTblOffset; // offset of ATOM_INIT_REG_BLOCK structure for memory clock specific MC setting + USHORT usRerseved; + UCHAR ucMemDQ7_0ByteRemap; // DQ line byte remap, =0: Memory Data line BYTE0, =1: BYTE1, =2: BYTE2, =3: BYTE3 + ULONG ulMemDQ7_0BitRemap; // each DQ line ( 7~0) use 3bits, like: DQ0=Bit[2:0], DQ1:[5:3], ... DQ7:[23:21] + UCHAR ucReservde[4]; + UCHAR ucNumOfVRAMModule; + ATOM_VRAM_MODULE_V4 aVramInfo[ATOM_MAX_NUMBER_OF_VRAM_MODULE]; // just for allocation, real number of blocks is in ucNumOfVRAMModule; + ATOM_INIT_REG_BLOCK asMemPatch; // for allocation + // ATOM_INIT_REG_BLOCK aMemAdjust; +}ATOM_VRAM_INFO_V4; + +typedef struct _ATOM_VRAM_INFO_HEADER_V2_1 +{ + ATOM_COMMON_TABLE_HEADER sHeader; + USHORT usMemAdjustTblOffset; // offset of ATOM_INIT_REG_BLOCK structure for memory vendor specific MC adjust setting + USHORT usMemClkPatchTblOffset; // offset of ATOM_INIT_REG_BLOCK structure for memory clock specific MC setting + USHORT usPerBytePresetOffset; // offset of ATOM_INIT_REG_BLOCK structure for Per Byte Offset Preset Settings + USHORT usReserved[3]; + UCHAR ucNumOfVRAMModule; // indicate number of VRAM module + UCHAR ucMemoryClkPatchTblVer; // version of memory AC timing register list + UCHAR ucVramModuleVer; // indicate ATOM_VRAM_MODUE version + UCHAR ucReserved; + ATOM_VRAM_MODULE_V7 aVramInfo[ATOM_MAX_NUMBER_OF_VRAM_MODULE]; // just for allocation, real number of blocks is in ucNumOfVRAMModule; +}ATOM_VRAM_INFO_HEADER_V2_1; + + +typedef struct _ATOM_VRAM_GPIO_DETECTION_INFO +{ + ATOM_COMMON_TABLE_HEADER sHeader; + UCHAR aVID_PinsShift[9]; //8 bit strap maximum+terminator +}ATOM_VRAM_GPIO_DETECTION_INFO; + + +typedef struct _ATOM_MEMORY_TRAINING_INFO +{ + ATOM_COMMON_TABLE_HEADER sHeader; + UCHAR ucTrainingLoop; + UCHAR ucReserved[3]; + ATOM_INIT_REG_BLOCK asMemTrainingSetting; +}ATOM_MEMORY_TRAINING_INFO; + + +typedef struct SW_I2C_CNTL_DATA_PARAMETERS +{ + UCHAR ucControl; + UCHAR ucData; + UCHAR ucSatus; + UCHAR ucTemp; +} SW_I2C_CNTL_DATA_PARAMETERS; + +#define SW_I2C_CNTL_DATA_PS_ALLOCATION SW_I2C_CNTL_DATA_PARAMETERS + +typedef struct _SW_I2C_IO_DATA_PARAMETERS +{ + USHORT GPIO_Info; + UCHAR ucAct; + UCHAR ucData; + } SW_I2C_IO_DATA_PARAMETERS; + +#define SW_I2C_IO_DATA_PS_ALLOCATION SW_I2C_IO_DATA_PARAMETERS + +/****************************SW I2C CNTL DEFINITIONS**********************/ +#define SW_I2C_IO_RESET 0 +#define SW_I2C_IO_GET 1 +#define SW_I2C_IO_DRIVE 2 +#define SW_I2C_IO_SET 3 +#define SW_I2C_IO_START 4 + +#define SW_I2C_IO_CLOCK 0 +#define SW_I2C_IO_DATA 0x80 + +#define SW_I2C_IO_ZERO 0 +#define SW_I2C_IO_ONE 0x100 + +#define SW_I2C_CNTL_READ 0 +#define SW_I2C_CNTL_WRITE 1 +#define SW_I2C_CNTL_START 2 +#define SW_I2C_CNTL_STOP 3 +#define SW_I2C_CNTL_OPEN 4 +#define SW_I2C_CNTL_CLOSE 5 +#define SW_I2C_CNTL_WRITE1BIT 6 + +//==============================VESA definition Portion=============================== +#define VESA_OEM_PRODUCT_REV "01.00" +#define VESA_MODE_ATTRIBUTE_MODE_SUPPORT 0xBB //refer to VBE spec p.32, no TTY support +#define VESA_MODE_WIN_ATTRIBUTE 7 +#define VESA_WIN_SIZE 64 + +typedef struct _PTR_32_BIT_STRUCTURE +{ + USHORT Offset16; + USHORT Segment16; +} PTR_32_BIT_STRUCTURE; + +typedef union _PTR_32_BIT_UNION +{ + PTR_32_BIT_STRUCTURE SegmentOffset; + ULONG Ptr32_Bit; +} PTR_32_BIT_UNION; + +typedef struct _VBE_1_2_INFO_BLOCK_UPDATABLE +{ + UCHAR VbeSignature[4]; + USHORT VbeVersion; + PTR_32_BIT_UNION OemStringPtr; + UCHAR Capabilities[4]; + PTR_32_BIT_UNION VideoModePtr; + USHORT TotalMemory; +} VBE_1_2_INFO_BLOCK_UPDATABLE; + + +typedef struct _VBE_2_0_INFO_BLOCK_UPDATABLE +{ + VBE_1_2_INFO_BLOCK_UPDATABLE CommonBlock; + USHORT OemSoftRev; + PTR_32_BIT_UNION OemVendorNamePtr; + PTR_32_BIT_UNION OemProductNamePtr; + PTR_32_BIT_UNION OemProductRevPtr; +} VBE_2_0_INFO_BLOCK_UPDATABLE; + +typedef union _VBE_VERSION_UNION +{ + VBE_2_0_INFO_BLOCK_UPDATABLE VBE_2_0_InfoBlock; + VBE_1_2_INFO_BLOCK_UPDATABLE VBE_1_2_InfoBlock; +} VBE_VERSION_UNION; + +typedef struct _VBE_INFO_BLOCK +{ + VBE_VERSION_UNION UpdatableVBE_Info; + UCHAR Reserved[222]; + UCHAR OemData[256]; +} VBE_INFO_BLOCK; + +typedef struct _VBE_FP_INFO +{ + USHORT HSize; + USHORT VSize; + USHORT FPType; + UCHAR RedBPP; + UCHAR GreenBPP; + UCHAR BlueBPP; + UCHAR ReservedBPP; + ULONG RsvdOffScrnMemSize; + ULONG RsvdOffScrnMEmPtr; + UCHAR Reserved[14]; +} VBE_FP_INFO; + +typedef struct _VESA_MODE_INFO_BLOCK +{ +// Mandatory information for all VBE revisions + USHORT ModeAttributes; // dw ? ; mode attributes + UCHAR WinAAttributes; // db ? ; window A attributes + UCHAR WinBAttributes; // db ? ; window B attributes + USHORT WinGranularity; // dw ? ; window granularity + USHORT WinSize; // dw ? ; window size + USHORT WinASegment; // dw ? ; window A start segment + USHORT WinBSegment; // dw ? ; window B start segment + ULONG WinFuncPtr; // dd ? ; real mode pointer to window function + USHORT BytesPerScanLine;// dw ? ; bytes per scan line + +//; Mandatory information for VBE 1.2 and above + USHORT XResolution; // dw ? ; horizontal resolution in pixels or characters + USHORT YResolution; // dw ? ; vertical resolution in pixels or characters + UCHAR XCharSize; // db ? ; character cell width in pixels + UCHAR YCharSize; // db ? ; character cell height in pixels + UCHAR NumberOfPlanes; // db ? ; number of memory planes + UCHAR BitsPerPixel; // db ? ; bits per pixel + UCHAR NumberOfBanks; // db ? ; number of banks + UCHAR MemoryModel; // db ? ; memory model type + UCHAR BankSize; // db ? ; bank size in KB + UCHAR NumberOfImagePages;// db ? ; number of images + UCHAR ReservedForPageFunction;//db 1 ; reserved for page function + +//; Direct Color fields(required for direct/6 and YUV/7 memory models) + UCHAR RedMaskSize; // db ? ; size of direct color red mask in bits + UCHAR RedFieldPosition; // db ? ; bit position of lsb of red mask + UCHAR GreenMaskSize; // db ? ; size of direct color green mask in bits + UCHAR GreenFieldPosition; // db ? ; bit position of lsb of green mask + UCHAR BlueMaskSize; // db ? ; size of direct color blue mask in bits + UCHAR BlueFieldPosition; // db ? ; bit position of lsb of blue mask + UCHAR RsvdMaskSize; // db ? ; size of direct color reserved mask in bits + UCHAR RsvdFieldPosition; // db ? ; bit position of lsb of reserved mask + UCHAR DirectColorModeInfo;// db ? ; direct color mode attributes + +//; Mandatory information for VBE 2.0 and above + ULONG PhysBasePtr; // dd ? ; physical address for flat memory frame buffer + ULONG Reserved_1; // dd 0 ; reserved - always set to 0 + USHORT Reserved_2; // dw 0 ; reserved - always set to 0 + +//; Mandatory information for VBE 3.0 and above + USHORT LinBytesPerScanLine; // dw ? ; bytes per scan line for linear modes + UCHAR BnkNumberOfImagePages;// db ? ; number of images for banked modes + UCHAR LinNumberOfImagPages; // db ? ; number of images for linear modes + UCHAR LinRedMaskSize; // db ? ; size of direct color red mask(linear modes) + UCHAR LinRedFieldPosition; // db ? ; bit position of lsb of red mask(linear modes) + UCHAR LinGreenMaskSize; // db ? ; size of direct color green mask(linear modes) + UCHAR LinGreenFieldPosition;// db ? ; bit position of lsb of green mask(linear modes) + UCHAR LinBlueMaskSize; // db ? ; size of direct color blue mask(linear modes) + UCHAR LinBlueFieldPosition; // db ? ; bit position of lsb of blue mask(linear modes) + UCHAR LinRsvdMaskSize; // db ? ; size of direct color reserved mask(linear modes) + UCHAR LinRsvdFieldPosition; // db ? ; bit position of lsb of reserved mask(linear modes) + ULONG MaxPixelClock; // dd ? ; maximum pixel clock(in Hz) for graphics mode + UCHAR Reserved; // db 190 dup (0) +} VESA_MODE_INFO_BLOCK; + +// BIOS function CALLS +#define ATOM_BIOS_EXTENDED_FUNCTION_CODE 0xA0 // ATI Extended Function code +#define ATOM_BIOS_FUNCTION_COP_MODE 0x00 +#define ATOM_BIOS_FUNCTION_SHORT_QUERY1 0x04 +#define ATOM_BIOS_FUNCTION_SHORT_QUERY2 0x05 +#define ATOM_BIOS_FUNCTION_SHORT_QUERY3 0x06 +#define ATOM_BIOS_FUNCTION_GET_DDC 0x0B +#define ATOM_BIOS_FUNCTION_ASIC_DSTATE 0x0E +#define ATOM_BIOS_FUNCTION_DEBUG_PLAY 0x0F +#define ATOM_BIOS_FUNCTION_STV_STD 0x16 +#define ATOM_BIOS_FUNCTION_DEVICE_DET 0x17 +#define ATOM_BIOS_FUNCTION_DEVICE_SWITCH 0x18 + +#define ATOM_BIOS_FUNCTION_PANEL_CONTROL 0x82 +#define ATOM_BIOS_FUNCTION_OLD_DEVICE_DET 0x83 +#define ATOM_BIOS_FUNCTION_OLD_DEVICE_SWITCH 0x84 +#define ATOM_BIOS_FUNCTION_HW_ICON 0x8A +#define ATOM_BIOS_FUNCTION_SET_CMOS 0x8B +#define SUB_FUNCTION_UPDATE_DISPLAY_INFO 0x8000 // Sub function 80 +#define SUB_FUNCTION_UPDATE_EXPANSION_INFO 0x8100 // Sub function 80 + +#define ATOM_BIOS_FUNCTION_DISPLAY_INFO 0x8D +#define ATOM_BIOS_FUNCTION_DEVICE_ON_OFF 0x8E +#define ATOM_BIOS_FUNCTION_VIDEO_STATE 0x8F +#define ATOM_SUB_FUNCTION_GET_CRITICAL_STATE 0x0300 // Sub function 03 +#define ATOM_SUB_FUNCTION_GET_LIDSTATE 0x0700 // Sub function 7 +#define ATOM_SUB_FUNCTION_THERMAL_STATE_NOTICE 0x1400 // Notify caller the current thermal state +#define ATOM_SUB_FUNCTION_CRITICAL_STATE_NOTICE 0x8300 // Notify caller the current critical state +#define ATOM_SUB_FUNCTION_SET_LIDSTATE 0x8500 // Sub function 85 +#define ATOM_SUB_FUNCTION_GET_REQ_DISPLAY_FROM_SBIOS_MODE 0x8900// Sub function 89 +#define ATOM_SUB_FUNCTION_INFORM_ADC_SUPPORT 0x9400 // Notify caller that ADC is supported + + +#define ATOM_BIOS_FUNCTION_VESA_DPMS 0x4F10 // Set DPMS +#define ATOM_SUB_FUNCTION_SET_DPMS 0x0001 // BL: Sub function 01 +#define ATOM_SUB_FUNCTION_GET_DPMS 0x0002 // BL: Sub function 02 +#define ATOM_PARAMETER_VESA_DPMS_ON 0x0000 // BH Parameter for DPMS ON. +#define ATOM_PARAMETER_VESA_DPMS_STANDBY 0x0100 // BH Parameter for DPMS STANDBY +#define ATOM_PARAMETER_VESA_DPMS_SUSPEND 0x0200 // BH Parameter for DPMS SUSPEND +#define ATOM_PARAMETER_VESA_DPMS_OFF 0x0400 // BH Parameter for DPMS OFF +#define ATOM_PARAMETER_VESA_DPMS_REDUCE_ON 0x0800 // BH Parameter for DPMS REDUCE ON (NOT SUPPORTED) + +#define ATOM_BIOS_RETURN_CODE_MASK 0x0000FF00L +#define ATOM_BIOS_REG_HIGH_MASK 0x0000FF00L +#define ATOM_BIOS_REG_LOW_MASK 0x000000FFL + +// structure used for VBIOS only + +//DispOutInfoTable +typedef struct _ASIC_TRANSMITTER_INFO +{ + USHORT usTransmitterObjId; + USHORT usSupportDevice; + UCHAR ucTransmitterCmdTblId; + UCHAR ucConfig; + UCHAR ucEncoderID; //available 1st encoder ( default ) + UCHAR ucOptionEncoderID; //available 2nd encoder ( optional ) + UCHAR uc2ndEncoderID; + UCHAR ucReserved; +}ASIC_TRANSMITTER_INFO; + +#define ASIC_TRANSMITTER_INFO_CONFIG__DVO_SDR_MODE 0x01 +#define ASIC_TRANSMITTER_INFO_CONFIG__COHERENT_MODE 0x02 +#define ASIC_TRANSMITTER_INFO_CONFIG__ENCODEROBJ_ID_MASK 0xc4 +#define ASIC_TRANSMITTER_INFO_CONFIG__ENCODER_A 0x00 +#define ASIC_TRANSMITTER_INFO_CONFIG__ENCODER_B 0x04 +#define ASIC_TRANSMITTER_INFO_CONFIG__ENCODER_C 0x40 +#define ASIC_TRANSMITTER_INFO_CONFIG__ENCODER_D 0x44 +#define ASIC_TRANSMITTER_INFO_CONFIG__ENCODER_E 0x80 +#define ASIC_TRANSMITTER_INFO_CONFIG__ENCODER_F 0x84 + +typedef struct _ASIC_ENCODER_INFO +{ + UCHAR ucEncoderID; + UCHAR ucEncoderConfig; + USHORT usEncoderCmdTblId; +}ASIC_ENCODER_INFO; + +typedef struct _ATOM_DISP_OUT_INFO +{ + ATOM_COMMON_TABLE_HEADER sHeader; + USHORT ptrTransmitterInfo; + USHORT ptrEncoderInfo; + ASIC_TRANSMITTER_INFO asTransmitterInfo[1]; + ASIC_ENCODER_INFO asEncoderInfo[1]; +}ATOM_DISP_OUT_INFO; + +typedef struct _ATOM_DISP_OUT_INFO_V2 +{ + ATOM_COMMON_TABLE_HEADER sHeader; + USHORT ptrTransmitterInfo; + USHORT ptrEncoderInfo; + USHORT ptrMainCallParserFar; // direct address of main parser call in VBIOS binary. + ASIC_TRANSMITTER_INFO asTransmitterInfo[1]; + ASIC_ENCODER_INFO asEncoderInfo[1]; +}ATOM_DISP_OUT_INFO_V2; + + +typedef struct _ATOM_DISP_CLOCK_ID { + UCHAR ucPpllId; + UCHAR ucPpllAttribute; +}ATOM_DISP_CLOCK_ID; + +// ucPpllAttribute +#define CLOCK_SOURCE_SHAREABLE 0x01 +#define CLOCK_SOURCE_DP_MODE 0x02 +#define CLOCK_SOURCE_NONE_DP_MODE 0x04 + +//DispOutInfoTable +typedef struct _ASIC_TRANSMITTER_INFO_V2 +{ + USHORT usTransmitterObjId; + USHORT usDispClkIdOffset; // point to clock source id list supported by Encoder Object + UCHAR ucTransmitterCmdTblId; + UCHAR ucConfig; + UCHAR ucEncoderID; // available 1st encoder ( default ) + UCHAR ucOptionEncoderID; // available 2nd encoder ( optional ) + UCHAR uc2ndEncoderID; + UCHAR ucReserved; +}ASIC_TRANSMITTER_INFO_V2; + +typedef struct _ATOM_DISP_OUT_INFO_V3 +{ + ATOM_COMMON_TABLE_HEADER sHeader; + USHORT ptrTransmitterInfo; + USHORT ptrEncoderInfo; + USHORT ptrMainCallParserFar; // direct address of main parser call in VBIOS binary. + USHORT usReserved; + UCHAR ucDCERevision; + UCHAR ucMaxDispEngineNum; + UCHAR ucMaxActiveDispEngineNum; + UCHAR ucMaxPPLLNum; + UCHAR ucCoreRefClkSource; // value of CORE_REF_CLK_SOURCE + UCHAR ucReserved[3]; + ASIC_TRANSMITTER_INFO_V2 asTransmitterInfo[1]; // for alligment only +}ATOM_DISP_OUT_INFO_V3; + +typedef enum CORE_REF_CLK_SOURCE{ + CLOCK_SRC_XTALIN=0, + CLOCK_SRC_XO_IN=1, + CLOCK_SRC_XO_IN2=2, +}CORE_REF_CLK_SOURCE; + +// DispDevicePriorityInfo +typedef struct _ATOM_DISPLAY_DEVICE_PRIORITY_INFO +{ + ATOM_COMMON_TABLE_HEADER sHeader; + USHORT asDevicePriority[16]; +}ATOM_DISPLAY_DEVICE_PRIORITY_INFO; + +//ProcessAuxChannelTransactionTable +typedef struct _PROCESS_AUX_CHANNEL_TRANSACTION_PARAMETERS +{ + USHORT lpAuxRequest; + USHORT lpDataOut; + UCHAR ucChannelID; + union + { + UCHAR ucReplyStatus; + UCHAR ucDelay; + }; + UCHAR ucDataOutLen; + UCHAR ucReserved; +}PROCESS_AUX_CHANNEL_TRANSACTION_PARAMETERS; + +//ProcessAuxChannelTransactionTable +typedef struct _PROCESS_AUX_CHANNEL_TRANSACTION_PARAMETERS_V2 +{ + USHORT lpAuxRequest; + USHORT lpDataOut; + UCHAR ucChannelID; + union + { + UCHAR ucReplyStatus; + UCHAR ucDelay; + }; + UCHAR ucDataOutLen; + UCHAR ucHPD_ID; //=0: HPD1, =1: HPD2, =2: HPD3, =3: HPD4, =4: HPD5, =5: HPD6 +}PROCESS_AUX_CHANNEL_TRANSACTION_PARAMETERS_V2; + +#define PROCESS_AUX_CHANNEL_TRANSACTION_PS_ALLOCATION PROCESS_AUX_CHANNEL_TRANSACTION_PARAMETERS + +//GetSinkType + +typedef struct _DP_ENCODER_SERVICE_PARAMETERS +{ + USHORT ucLinkClock; + union + { + UCHAR ucConfig; // for DP training command + UCHAR ucI2cId; // use for GET_SINK_TYPE command + }; + UCHAR ucAction; + UCHAR ucStatus; + UCHAR ucLaneNum; + UCHAR ucReserved[2]; +}DP_ENCODER_SERVICE_PARAMETERS; + +// ucAction +#define ATOM_DP_ACTION_GET_SINK_TYPE 0x01 +/* obselete */ +#define ATOM_DP_ACTION_TRAINING_START 0x02 +#define ATOM_DP_ACTION_TRAINING_COMPLETE 0x03 +#define ATOM_DP_ACTION_TRAINING_PATTERN_SEL 0x04 +#define ATOM_DP_ACTION_SET_VSWING_PREEMP 0x05 +#define ATOM_DP_ACTION_GET_VSWING_PREEMP 0x06 +#define ATOM_DP_ACTION_BLANKING 0x07 + +// ucConfig +#define ATOM_DP_CONFIG_ENCODER_SEL_MASK 0x03 +#define ATOM_DP_CONFIG_DIG1_ENCODER 0x00 +#define ATOM_DP_CONFIG_DIG2_ENCODER 0x01 +#define ATOM_DP_CONFIG_EXTERNAL_ENCODER 0x02 +#define ATOM_DP_CONFIG_LINK_SEL_MASK 0x04 +#define ATOM_DP_CONFIG_LINK_A 0x00 +#define ATOM_DP_CONFIG_LINK_B 0x04 +/* /obselete */ +#define DP_ENCODER_SERVICE_PS_ALLOCATION WRITE_ONE_BYTE_HW_I2C_DATA_PARAMETERS + + +typedef struct _DP_ENCODER_SERVICE_PARAMETERS_V2 +{ + USHORT usExtEncoderObjId; // External Encoder Object Id, output parameter only, use when ucAction = DP_SERVICE_V2_ACTION_DET_EXT_CONNECTION + UCHAR ucAuxId; + UCHAR ucAction; + UCHAR ucSinkType; // Iput and Output parameters. + UCHAR ucHPDId; // Input parameter, used when ucAction = DP_SERVICE_V2_ACTION_DET_EXT_CONNECTION + UCHAR ucReserved[2]; +}DP_ENCODER_SERVICE_PARAMETERS_V2; + +typedef struct _DP_ENCODER_SERVICE_PS_ALLOCATION_V2 +{ + DP_ENCODER_SERVICE_PARAMETERS_V2 asDPServiceParam; + PROCESS_AUX_CHANNEL_TRANSACTION_PARAMETERS_V2 asAuxParam; +}DP_ENCODER_SERVICE_PS_ALLOCATION_V2; + +// ucAction +#define DP_SERVICE_V2_ACTION_GET_SINK_TYPE 0x01 +#define DP_SERVICE_V2_ACTION_DET_LCD_CONNECTION 0x02 + + +// DP_TRAINING_TABLE +#define DPCD_SET_LINKRATE_LANENUM_PATTERN1_TBL_ADDR ATOM_DP_TRAINING_TBL_ADDR +#define DPCD_SET_SS_CNTL_TBL_ADDR (ATOM_DP_TRAINING_TBL_ADDR + 8 ) +#define DPCD_SET_LANE_VSWING_PREEMP_TBL_ADDR (ATOM_DP_TRAINING_TBL_ADDR + 16 ) +#define DPCD_SET_TRAINING_PATTERN0_TBL_ADDR (ATOM_DP_TRAINING_TBL_ADDR + 24 ) +#define DPCD_SET_TRAINING_PATTERN2_TBL_ADDR (ATOM_DP_TRAINING_TBL_ADDR + 32) +#define DPCD_GET_LINKRATE_LANENUM_SS_TBL_ADDR (ATOM_DP_TRAINING_TBL_ADDR + 40) +#define DPCD_GET_LANE_STATUS_ADJUST_TBL_ADDR (ATOM_DP_TRAINING_TBL_ADDR + 48) +#define DP_I2C_AUX_DDC_WRITE_START_TBL_ADDR (ATOM_DP_TRAINING_TBL_ADDR + 60) +#define DP_I2C_AUX_DDC_WRITE_TBL_ADDR (ATOM_DP_TRAINING_TBL_ADDR + 64) +#define DP_I2C_AUX_DDC_READ_START_TBL_ADDR (ATOM_DP_TRAINING_TBL_ADDR + 72) +#define DP_I2C_AUX_DDC_READ_TBL_ADDR (ATOM_DP_TRAINING_TBL_ADDR + 76) +#define DP_I2C_AUX_DDC_WRITE_END_TBL_ADDR (ATOM_DP_TRAINING_TBL_ADDR + 80) +#define DP_I2C_AUX_DDC_READ_END_TBL_ADDR (ATOM_DP_TRAINING_TBL_ADDR + 84) + +typedef struct _PROCESS_I2C_CHANNEL_TRANSACTION_PARAMETERS +{ + UCHAR ucI2CSpeed; + union + { + UCHAR ucRegIndex; + UCHAR ucStatus; + }; + USHORT lpI2CDataOut; + UCHAR ucFlag; + UCHAR ucTransBytes; + UCHAR ucSlaveAddr; + UCHAR ucLineNumber; +}PROCESS_I2C_CHANNEL_TRANSACTION_PARAMETERS; + +#define PROCESS_I2C_CHANNEL_TRANSACTION_PS_ALLOCATION PROCESS_I2C_CHANNEL_TRANSACTION_PARAMETERS + +//ucFlag +#define HW_I2C_WRITE 1 +#define HW_I2C_READ 0 +#define I2C_2BYTE_ADDR 0x02 + +/****************************************************************************/ +// Structures used by HW_Misc_OperationTable +/****************************************************************************/ +typedef struct _ATOM_HW_MISC_OPERATION_INPUT_PARAMETER_V1_1 +{ + UCHAR ucCmd; // Input: To tell which action to take + UCHAR ucReserved[3]; + ULONG ulReserved; +}ATOM_HW_MISC_OPERATION_INPUT_PARAMETER_V1_1; + +typedef struct _ATOM_HW_MISC_OPERATION_OUTPUT_PARAMETER_V1_1 +{ + UCHAR ucReturnCode; // Output: Return value base on action was taken + UCHAR ucReserved[3]; + ULONG ulReserved; +}ATOM_HW_MISC_OPERATION_OUTPUT_PARAMETER_V1_1; + +// Actions code +#define ATOM_GET_SDI_SUPPORT 0xF0 + +// Return code +#define ATOM_UNKNOWN_CMD 0 +#define ATOM_FEATURE_NOT_SUPPORTED 1 +#define ATOM_FEATURE_SUPPORTED 2 + +typedef struct _ATOM_HW_MISC_OPERATION_PS_ALLOCATION +{ + ATOM_HW_MISC_OPERATION_INPUT_PARAMETER_V1_1 sInput_Output; + PROCESS_I2C_CHANNEL_TRANSACTION_PARAMETERS sReserved; +}ATOM_HW_MISC_OPERATION_PS_ALLOCATION; + +/****************************************************************************/ + +typedef struct _SET_HWBLOCK_INSTANCE_PARAMETER_V2 +{ + UCHAR ucHWBlkInst; // HW block instance, 0, 1, 2, ... + UCHAR ucReserved[3]; +}SET_HWBLOCK_INSTANCE_PARAMETER_V2; + +#define HWBLKINST_INSTANCE_MASK 0x07 +#define HWBLKINST_HWBLK_MASK 0xF0 +#define HWBLKINST_HWBLK_SHIFT 0x04 + +//ucHWBlock +#define SELECT_DISP_ENGINE 0 +#define SELECT_DISP_PLL 1 +#define SELECT_DCIO_UNIPHY_LINK0 2 +#define SELECT_DCIO_UNIPHY_LINK1 3 +#define SELECT_DCIO_IMPCAL 4 +#define SELECT_DCIO_DIG 6 +#define SELECT_CRTC_PIXEL_RATE 7 +#define SELECT_VGA_BLK 8 + +// DIGTransmitterInfoTable structure used to program UNIPHY settings +typedef struct _DIG_TRANSMITTER_INFO_HEADER_V3_1{ + ATOM_COMMON_TABLE_HEADER sHeader; + USHORT usDPVsPreEmphSettingOffset; // offset of PHY_ANALOG_SETTING_INFO * with DP Voltage Swing and Pre-Emphasis for each Link clock + USHORT usPhyAnalogRegListOffset; // offset of CLOCK_CONDITION_REGESTER_INFO* with None-DP mode Analog Setting's register Info + USHORT usPhyAnalogSettingOffset; // offset of CLOCK_CONDITION_SETTING_ENTRY* with None-DP mode Analog Setting for each link clock range + USHORT usPhyPllRegListOffset; // offset of CLOCK_CONDITION_REGESTER_INFO* with Phy Pll register Info + USHORT usPhyPllSettingOffset; // offset of CLOCK_CONDITION_SETTING_ENTRY* with Phy Pll Settings +}DIG_TRANSMITTER_INFO_HEADER_V3_1; + +typedef struct _CLOCK_CONDITION_REGESTER_INFO{ + USHORT usRegisterIndex; + UCHAR ucStartBit; + UCHAR ucEndBit; +}CLOCK_CONDITION_REGESTER_INFO; + +typedef struct _CLOCK_CONDITION_SETTING_ENTRY{ + USHORT usMaxClockFreq; + UCHAR ucEncodeMode; + UCHAR ucPhySel; + ULONG ulAnalogSetting[1]; +}CLOCK_CONDITION_SETTING_ENTRY; + +typedef struct _CLOCK_CONDITION_SETTING_INFO{ + USHORT usEntrySize; + CLOCK_CONDITION_SETTING_ENTRY asClkCondSettingEntry[1]; +}CLOCK_CONDITION_SETTING_INFO; + +typedef struct _PHY_CONDITION_REG_VAL{ + ULONG ulCondition; + ULONG ulRegVal; +}PHY_CONDITION_REG_VAL; + +typedef struct _PHY_CONDITION_REG_INFO{ + USHORT usRegIndex; + USHORT usSize; + PHY_CONDITION_REG_VAL asRegVal[1]; +}PHY_CONDITION_REG_INFO; + +typedef struct _PHY_ANALOG_SETTING_INFO{ + UCHAR ucEncodeMode; + UCHAR ucPhySel; + USHORT usSize; + PHY_CONDITION_REG_INFO asAnalogSetting[1]; +}PHY_ANALOG_SETTING_INFO; + +/****************************************************************************/ +//Portion VI: Definitinos for vbios MC scratch registers that driver used +/****************************************************************************/ + +#define MC_MISC0__MEMORY_TYPE_MASK 0xF0000000 +#define MC_MISC0__MEMORY_TYPE__GDDR1 0x10000000 +#define MC_MISC0__MEMORY_TYPE__DDR2 0x20000000 +#define MC_MISC0__MEMORY_TYPE__GDDR3 0x30000000 +#define MC_MISC0__MEMORY_TYPE__GDDR4 0x40000000 +#define MC_MISC0__MEMORY_TYPE__GDDR5 0x50000000 +#define MC_MISC0__MEMORY_TYPE__DDR3 0xB0000000 + +/****************************************************************************/ +//Portion VI: Definitinos being oboselete +/****************************************************************************/ + +//========================================================================================== +//Remove the definitions below when driver is ready! +typedef struct _ATOM_DAC_INFO +{ + ATOM_COMMON_TABLE_HEADER sHeader; + USHORT usMaxFrequency; // in 10kHz unit + USHORT usReserved; +}ATOM_DAC_INFO; + + +typedef struct _COMPASSIONATE_DATA +{ + ATOM_COMMON_TABLE_HEADER sHeader; + + //============================== DAC1 portion + UCHAR ucDAC1_BG_Adjustment; + UCHAR ucDAC1_DAC_Adjustment; + USHORT usDAC1_FORCE_Data; + //============================== DAC2 portion + UCHAR ucDAC2_CRT2_BG_Adjustment; + UCHAR ucDAC2_CRT2_DAC_Adjustment; + USHORT usDAC2_CRT2_FORCE_Data; + USHORT usDAC2_CRT2_MUX_RegisterIndex; + UCHAR ucDAC2_CRT2_MUX_RegisterInfo; //Bit[4:0]=Bit position,Bit[7]=1:Active High;=0 Active Low + UCHAR ucDAC2_NTSC_BG_Adjustment; + UCHAR ucDAC2_NTSC_DAC_Adjustment; + USHORT usDAC2_TV1_FORCE_Data; + USHORT usDAC2_TV1_MUX_RegisterIndex; + UCHAR ucDAC2_TV1_MUX_RegisterInfo; //Bit[4:0]=Bit position,Bit[7]=1:Active High;=0 Active Low + UCHAR ucDAC2_CV_BG_Adjustment; + UCHAR ucDAC2_CV_DAC_Adjustment; + USHORT usDAC2_CV_FORCE_Data; + USHORT usDAC2_CV_MUX_RegisterIndex; + UCHAR ucDAC2_CV_MUX_RegisterInfo; //Bit[4:0]=Bit position,Bit[7]=1:Active High;=0 Active Low + UCHAR ucDAC2_PAL_BG_Adjustment; + UCHAR ucDAC2_PAL_DAC_Adjustment; + USHORT usDAC2_TV2_FORCE_Data; +}COMPASSIONATE_DATA; + +/****************************Supported Device Info Table Definitions**********************/ +// ucConnectInfo: +// [7:4] - connector type +// = 1 - VGA connector +// = 2 - DVI-I +// = 3 - DVI-D +// = 4 - DVI-A +// = 5 - SVIDEO +// = 6 - COMPOSITE +// = 7 - LVDS +// = 8 - DIGITAL LINK +// = 9 - SCART +// = 0xA - HDMI_type A +// = 0xB - HDMI_type B +// = 0xE - Special case1 (DVI+DIN) +// Others=TBD +// [3:0] - DAC Associated +// = 0 - no DAC +// = 1 - DACA +// = 2 - DACB +// = 3 - External DAC +// Others=TBD +// + +typedef struct _ATOM_CONNECTOR_INFO +{ +#if ATOM_BIG_ENDIAN + UCHAR bfConnectorType:4; + UCHAR bfAssociatedDAC:4; +#else + UCHAR bfAssociatedDAC:4; + UCHAR bfConnectorType:4; +#endif +}ATOM_CONNECTOR_INFO; + +typedef union _ATOM_CONNECTOR_INFO_ACCESS +{ + ATOM_CONNECTOR_INFO sbfAccess; + UCHAR ucAccess; +}ATOM_CONNECTOR_INFO_ACCESS; + +typedef struct _ATOM_CONNECTOR_INFO_I2C +{ + ATOM_CONNECTOR_INFO_ACCESS sucConnectorInfo; + ATOM_I2C_ID_CONFIG_ACCESS sucI2cId; +}ATOM_CONNECTOR_INFO_I2C; + + +typedef struct _ATOM_SUPPORTED_DEVICES_INFO +{ + ATOM_COMMON_TABLE_HEADER sHeader; + USHORT usDeviceSupport; + ATOM_CONNECTOR_INFO_I2C asConnInfo[ATOM_MAX_SUPPORTED_DEVICE_INFO]; +}ATOM_SUPPORTED_DEVICES_INFO; + +#define NO_INT_SRC_MAPPED 0xFF + +typedef struct _ATOM_CONNECTOR_INC_SRC_BITMAP +{ + UCHAR ucIntSrcBitmap; +}ATOM_CONNECTOR_INC_SRC_BITMAP; + +typedef struct _ATOM_SUPPORTED_DEVICES_INFO_2 +{ + ATOM_COMMON_TABLE_HEADER sHeader; + USHORT usDeviceSupport; + ATOM_CONNECTOR_INFO_I2C asConnInfo[ATOM_MAX_SUPPORTED_DEVICE_INFO_2]; + ATOM_CONNECTOR_INC_SRC_BITMAP asIntSrcInfo[ATOM_MAX_SUPPORTED_DEVICE_INFO_2]; +}ATOM_SUPPORTED_DEVICES_INFO_2; + +typedef struct _ATOM_SUPPORTED_DEVICES_INFO_2d1 +{ + ATOM_COMMON_TABLE_HEADER sHeader; + USHORT usDeviceSupport; + ATOM_CONNECTOR_INFO_I2C asConnInfo[ATOM_MAX_SUPPORTED_DEVICE]; + ATOM_CONNECTOR_INC_SRC_BITMAP asIntSrcInfo[ATOM_MAX_SUPPORTED_DEVICE]; +}ATOM_SUPPORTED_DEVICES_INFO_2d1; + +#define ATOM_SUPPORTED_DEVICES_INFO_LAST ATOM_SUPPORTED_DEVICES_INFO_2d1 + + + +typedef struct _ATOM_MISC_CONTROL_INFO +{ + USHORT usFrequency; + UCHAR ucPLL_ChargePump; // PLL charge-pump gain control + UCHAR ucPLL_DutyCycle; // PLL duty cycle control + UCHAR ucPLL_VCO_Gain; // PLL VCO gain control + UCHAR ucPLL_VoltageSwing; // PLL driver voltage swing control +}ATOM_MISC_CONTROL_INFO; + + +#define ATOM_MAX_MISC_INFO 4 + +typedef struct _ATOM_TMDS_INFO +{ + ATOM_COMMON_TABLE_HEADER sHeader; + USHORT usMaxFrequency; // in 10Khz + ATOM_MISC_CONTROL_INFO asMiscInfo[ATOM_MAX_MISC_INFO]; +}ATOM_TMDS_INFO; + + +typedef struct _ATOM_ENCODER_ANALOG_ATTRIBUTE +{ + UCHAR ucTVStandard; //Same as TV standards defined above, + UCHAR ucPadding[1]; +}ATOM_ENCODER_ANALOG_ATTRIBUTE; + +typedef struct _ATOM_ENCODER_DIGITAL_ATTRIBUTE +{ + UCHAR ucAttribute; //Same as other digital encoder attributes defined above + UCHAR ucPadding[1]; +}ATOM_ENCODER_DIGITAL_ATTRIBUTE; + +typedef union _ATOM_ENCODER_ATTRIBUTE +{ + ATOM_ENCODER_ANALOG_ATTRIBUTE sAlgAttrib; + ATOM_ENCODER_DIGITAL_ATTRIBUTE sDigAttrib; +}ATOM_ENCODER_ATTRIBUTE; + + +typedef struct _DVO_ENCODER_CONTROL_PARAMETERS +{ + USHORT usPixelClock; + USHORT usEncoderID; + UCHAR ucDeviceType; //Use ATOM_DEVICE_xxx1_Index to indicate device type only. + UCHAR ucAction; //ATOM_ENABLE/ATOM_DISABLE/ATOM_HPD_INIT + ATOM_ENCODER_ATTRIBUTE usDevAttr; +}DVO_ENCODER_CONTROL_PARAMETERS; + +typedef struct _DVO_ENCODER_CONTROL_PS_ALLOCATION +{ + DVO_ENCODER_CONTROL_PARAMETERS sDVOEncoder; + WRITE_ONE_BYTE_HW_I2C_DATA_PS_ALLOCATION sReserved; //Caller doesn't need to init this portion +}DVO_ENCODER_CONTROL_PS_ALLOCATION; + + +#define ATOM_XTMDS_ASIC_SI164_ID 1 +#define ATOM_XTMDS_ASIC_SI178_ID 2 +#define ATOM_XTMDS_ASIC_TFP513_ID 3 +#define ATOM_XTMDS_SUPPORTED_SINGLELINK 0x00000001 +#define ATOM_XTMDS_SUPPORTED_DUALLINK 0x00000002 +#define ATOM_XTMDS_MVPU_FPGA 0x00000004 + + +typedef struct _ATOM_XTMDS_INFO +{ + ATOM_COMMON_TABLE_HEADER sHeader; + USHORT usSingleLinkMaxFrequency; + ATOM_I2C_ID_CONFIG_ACCESS sucI2cId; //Point the ID on which I2C is used to control external chip + UCHAR ucXtransimitterID; + UCHAR ucSupportedLink; // Bit field, bit0=1, single link supported;bit1=1,dual link supported + UCHAR ucSequnceAlterID; // Even with the same external TMDS asic, it's possible that the program seqence alters + // due to design. This ID is used to alert driver that the sequence is not "standard"! + UCHAR ucMasterAddress; // Address to control Master xTMDS Chip + UCHAR ucSlaveAddress; // Address to control Slave xTMDS Chip +}ATOM_XTMDS_INFO; + +typedef struct _DFP_DPMS_STATUS_CHANGE_PARAMETERS +{ + UCHAR ucEnable; // ATOM_ENABLE=On or ATOM_DISABLE=Off + UCHAR ucDevice; // ATOM_DEVICE_DFP1_INDEX.... + UCHAR ucPadding[2]; +}DFP_DPMS_STATUS_CHANGE_PARAMETERS; + +/****************************Legacy Power Play Table Definitions **********************/ + +//Definitions for ulPowerPlayMiscInfo +#define ATOM_PM_MISCINFO_SPLIT_CLOCK 0x00000000L +#define ATOM_PM_MISCINFO_USING_MCLK_SRC 0x00000001L +#define ATOM_PM_MISCINFO_USING_SCLK_SRC 0x00000002L + +#define ATOM_PM_MISCINFO_VOLTAGE_DROP_SUPPORT 0x00000004L +#define ATOM_PM_MISCINFO_VOLTAGE_DROP_ACTIVE_HIGH 0x00000008L + +#define ATOM_PM_MISCINFO_LOAD_PERFORMANCE_EN 0x00000010L + +#define ATOM_PM_MISCINFO_ENGINE_CLOCK_CONTRL_EN 0x00000020L +#define ATOM_PM_MISCINFO_MEMORY_CLOCK_CONTRL_EN 0x00000040L +#define ATOM_PM_MISCINFO_PROGRAM_VOLTAGE 0x00000080L //When this bit set, ucVoltageDropIndex is not an index for GPIO pin, but a voltage ID that SW needs program + +#define ATOM_PM_MISCINFO_ASIC_REDUCED_SPEED_SCLK_EN 0x00000100L +#define ATOM_PM_MISCINFO_ASIC_DYNAMIC_VOLTAGE_EN 0x00000200L +#define ATOM_PM_MISCINFO_ASIC_SLEEP_MODE_EN 0x00000400L +#define ATOM_PM_MISCINFO_LOAD_BALANCE_EN 0x00000800L +#define ATOM_PM_MISCINFO_DEFAULT_DC_STATE_ENTRY_TRUE 0x00001000L +#define ATOM_PM_MISCINFO_DEFAULT_LOW_DC_STATE_ENTRY_TRUE 0x00002000L +#define ATOM_PM_MISCINFO_LOW_LCD_REFRESH_RATE 0x00004000L + +#define ATOM_PM_MISCINFO_DRIVER_DEFAULT_MODE 0x00008000L +#define ATOM_PM_MISCINFO_OVER_CLOCK_MODE 0x00010000L +#define ATOM_PM_MISCINFO_OVER_DRIVE_MODE 0x00020000L +#define ATOM_PM_MISCINFO_POWER_SAVING_MODE 0x00040000L +#define ATOM_PM_MISCINFO_THERMAL_DIODE_MODE 0x00080000L + +#define ATOM_PM_MISCINFO_FRAME_MODULATION_MASK 0x00300000L //0-FM Disable, 1-2 level FM, 2-4 level FM, 3-Reserved +#define ATOM_PM_MISCINFO_FRAME_MODULATION_SHIFT 20 + +#define ATOM_PM_MISCINFO_DYN_CLK_3D_IDLE 0x00400000L +#define ATOM_PM_MISCINFO_DYNAMIC_CLOCK_DIVIDER_BY_2 0x00800000L +#define ATOM_PM_MISCINFO_DYNAMIC_CLOCK_DIVIDER_BY_4 0x01000000L +#define ATOM_PM_MISCINFO_DYNAMIC_HDP_BLOCK_EN 0x02000000L //When set, Dynamic +#define ATOM_PM_MISCINFO_DYNAMIC_MC_HOST_BLOCK_EN 0x04000000L //When set, Dynamic +#define ATOM_PM_MISCINFO_3D_ACCELERATION_EN 0x08000000L //When set, This mode is for acceleated 3D mode + +#define ATOM_PM_MISCINFO_POWERPLAY_SETTINGS_GROUP_MASK 0x70000000L //1-Optimal Battery Life Group, 2-High Battery, 3-Balanced, 4-High Performance, 5- Optimal Performance (Default state with Default clocks) +#define ATOM_PM_MISCINFO_POWERPLAY_SETTINGS_GROUP_SHIFT 28 +#define ATOM_PM_MISCINFO_ENABLE_BACK_BIAS 0x80000000L + +#define ATOM_PM_MISCINFO2_SYSTEM_AC_LITE_MODE 0x00000001L +#define ATOM_PM_MISCINFO2_MULTI_DISPLAY_SUPPORT 0x00000002L +#define ATOM_PM_MISCINFO2_DYNAMIC_BACK_BIAS_EN 0x00000004L +#define ATOM_PM_MISCINFO2_FS3D_OVERDRIVE_INFO 0x00000008L +#define ATOM_PM_MISCINFO2_FORCEDLOWPWR_MODE 0x00000010L +#define ATOM_PM_MISCINFO2_VDDCI_DYNAMIC_VOLTAGE_EN 0x00000020L +#define ATOM_PM_MISCINFO2_VIDEO_PLAYBACK_CAPABLE 0x00000040L //If this bit is set in multi-pp mode, then driver will pack up one with the minior power consumption. + //If it's not set in any pp mode, driver will use its default logic to pick a pp mode in video playback +#define ATOM_PM_MISCINFO2_NOT_VALID_ON_DC 0x00000080L +#define ATOM_PM_MISCINFO2_STUTTER_MODE_EN 0x00000100L +#define ATOM_PM_MISCINFO2_UVD_SUPPORT_MODE 0x00000200L + +//ucTableFormatRevision=1 +//ucTableContentRevision=1 +typedef struct _ATOM_POWERMODE_INFO +{ + ULONG ulMiscInfo; //The power level should be arranged in ascending order + ULONG ulReserved1; // must set to 0 + ULONG ulReserved2; // must set to 0 + USHORT usEngineClock; + USHORT usMemoryClock; + UCHAR ucVoltageDropIndex; // index to GPIO table + UCHAR ucSelectedPanel_RefreshRate;// panel refresh rate + UCHAR ucMinTemperature; + UCHAR ucMaxTemperature; + UCHAR ucNumPciELanes; // number of PCIE lanes +}ATOM_POWERMODE_INFO; + +//ucTableFormatRevision=2 +//ucTableContentRevision=1 +typedef struct _ATOM_POWERMODE_INFO_V2 +{ + ULONG ulMiscInfo; //The power level should be arranged in ascending order + ULONG ulMiscInfo2; + ULONG ulEngineClock; + ULONG ulMemoryClock; + UCHAR ucVoltageDropIndex; // index to GPIO table + UCHAR ucSelectedPanel_RefreshRate;// panel refresh rate + UCHAR ucMinTemperature; + UCHAR ucMaxTemperature; + UCHAR ucNumPciELanes; // number of PCIE lanes +}ATOM_POWERMODE_INFO_V2; + +//ucTableFormatRevision=2 +//ucTableContentRevision=2 +typedef struct _ATOM_POWERMODE_INFO_V3 +{ + ULONG ulMiscInfo; //The power level should be arranged in ascending order + ULONG ulMiscInfo2; + ULONG ulEngineClock; + ULONG ulMemoryClock; + UCHAR ucVoltageDropIndex; // index to Core (VDDC) votage table + UCHAR ucSelectedPanel_RefreshRate;// panel refresh rate + UCHAR ucMinTemperature; + UCHAR ucMaxTemperature; + UCHAR ucNumPciELanes; // number of PCIE lanes + UCHAR ucVDDCI_VoltageDropIndex; // index to VDDCI votage table +}ATOM_POWERMODE_INFO_V3; + + +#define ATOM_MAX_NUMBEROF_POWER_BLOCK 8 + +#define ATOM_PP_OVERDRIVE_INTBITMAP_AUXWIN 0x01 +#define ATOM_PP_OVERDRIVE_INTBITMAP_OVERDRIVE 0x02 + +#define ATOM_PP_OVERDRIVE_THERMALCONTROLLER_LM63 0x01 +#define ATOM_PP_OVERDRIVE_THERMALCONTROLLER_ADM1032 0x02 +#define ATOM_PP_OVERDRIVE_THERMALCONTROLLER_ADM1030 0x03 +#define ATOM_PP_OVERDRIVE_THERMALCONTROLLER_MUA6649 0x04 +#define ATOM_PP_OVERDRIVE_THERMALCONTROLLER_LM64 0x05 +#define ATOM_PP_OVERDRIVE_THERMALCONTROLLER_F75375 0x06 +#define ATOM_PP_OVERDRIVE_THERMALCONTROLLER_ASC7512 0x07 // Andigilog + + +typedef struct _ATOM_POWERPLAY_INFO +{ + ATOM_COMMON_TABLE_HEADER sHeader; + UCHAR ucOverdriveThermalController; + UCHAR ucOverdriveI2cLine; + UCHAR ucOverdriveIntBitmap; + UCHAR ucOverdriveControllerAddress; + UCHAR ucSizeOfPowerModeEntry; + UCHAR ucNumOfPowerModeEntries; + ATOM_POWERMODE_INFO asPowerPlayInfo[ATOM_MAX_NUMBEROF_POWER_BLOCK]; +}ATOM_POWERPLAY_INFO; + +typedef struct _ATOM_POWERPLAY_INFO_V2 +{ + ATOM_COMMON_TABLE_HEADER sHeader; + UCHAR ucOverdriveThermalController; + UCHAR ucOverdriveI2cLine; + UCHAR ucOverdriveIntBitmap; + UCHAR ucOverdriveControllerAddress; + UCHAR ucSizeOfPowerModeEntry; + UCHAR ucNumOfPowerModeEntries; + ATOM_POWERMODE_INFO_V2 asPowerPlayInfo[ATOM_MAX_NUMBEROF_POWER_BLOCK]; +}ATOM_POWERPLAY_INFO_V2; + +typedef struct _ATOM_POWERPLAY_INFO_V3 +{ + ATOM_COMMON_TABLE_HEADER sHeader; + UCHAR ucOverdriveThermalController; + UCHAR ucOverdriveI2cLine; + UCHAR ucOverdriveIntBitmap; + UCHAR ucOverdriveControllerAddress; + UCHAR ucSizeOfPowerModeEntry; + UCHAR ucNumOfPowerModeEntries; + ATOM_POWERMODE_INFO_V3 asPowerPlayInfo[ATOM_MAX_NUMBEROF_POWER_BLOCK]; +}ATOM_POWERPLAY_INFO_V3; + +/* New PPlib */ +/**************************************************************************/ +typedef struct _ATOM_PPLIB_THERMALCONTROLLER + +{ + UCHAR ucType; // one of ATOM_PP_THERMALCONTROLLER_* + UCHAR ucI2cLine; // as interpreted by DAL I2C + UCHAR ucI2cAddress; + UCHAR ucFanParameters; // Fan Control Parameters. + UCHAR ucFanMinRPM; // Fan Minimum RPM (hundreds) -- for display purposes only. + UCHAR ucFanMaxRPM; // Fan Maximum RPM (hundreds) -- for display purposes only. + UCHAR ucReserved; // ---- + UCHAR ucFlags; // to be defined +} ATOM_PPLIB_THERMALCONTROLLER; + +#define ATOM_PP_FANPARAMETERS_TACHOMETER_PULSES_PER_REVOLUTION_MASK 0x0f +#define ATOM_PP_FANPARAMETERS_NOFAN 0x80 // No fan is connected to this controller. + +#define ATOM_PP_THERMALCONTROLLER_NONE 0 +#define ATOM_PP_THERMALCONTROLLER_LM63 1 // Not used by PPLib +#define ATOM_PP_THERMALCONTROLLER_ADM1032 2 // Not used by PPLib +#define ATOM_PP_THERMALCONTROLLER_ADM1030 3 // Not used by PPLib +#define ATOM_PP_THERMALCONTROLLER_MUA6649 4 // Not used by PPLib +#define ATOM_PP_THERMALCONTROLLER_LM64 5 +#define ATOM_PP_THERMALCONTROLLER_F75375 6 // Not used by PPLib +#define ATOM_PP_THERMALCONTROLLER_RV6xx 7 +#define ATOM_PP_THERMALCONTROLLER_RV770 8 +#define ATOM_PP_THERMALCONTROLLER_ADT7473 9 +#define ATOM_PP_THERMALCONTROLLER_EXTERNAL_GPIO 11 +#define ATOM_PP_THERMALCONTROLLER_EVERGREEN 12 +#define ATOM_PP_THERMALCONTROLLER_EMC2103 13 /* 0x0D */ // Only fan control will be implemented, do NOT show this in PPGen. +#define ATOM_PP_THERMALCONTROLLER_SUMO 14 /* 0x0E */ // Sumo type, used internally +#define ATOM_PP_THERMALCONTROLLER_NISLANDS 15 +#define ATOM_PP_THERMALCONTROLLER_SISLANDS 16 +#define ATOM_PP_THERMALCONTROLLER_LM96163 17 + +// Thermal controller 'combo type' to use an external controller for Fan control and an internal controller for thermal. +// We probably should reserve the bit 0x80 for this use. +// To keep the number of these types low we should also use the same code for all ASICs (i.e. do not distinguish RV6xx and RV7xx Internal here). +// The driver can pick the correct internal controller based on the ASIC. + +#define ATOM_PP_THERMALCONTROLLER_ADT7473_WITH_INTERNAL 0x89 // ADT7473 Fan Control + Internal Thermal Controller +#define ATOM_PP_THERMALCONTROLLER_EMC2103_WITH_INTERNAL 0x8D // EMC2103 Fan Control + Internal Thermal Controller + +typedef struct _ATOM_PPLIB_STATE +{ + UCHAR ucNonClockStateIndex; + UCHAR ucClockStateIndices[1]; // variable-sized +} ATOM_PPLIB_STATE; + + +typedef struct _ATOM_PPLIB_FANTABLE +{ + UCHAR ucFanTableFormat; // Change this if the table format changes or version changes so that the other fields are not the same. + UCHAR ucTHyst; // Temperature hysteresis. Integer. + USHORT usTMin; // The temperature, in 0.01 centigrades, below which we just run at a minimal PWM. + USHORT usTMed; // The middle temperature where we change slopes. + USHORT usTHigh; // The high point above TMed for adjusting the second slope. + USHORT usPWMMin; // The minimum PWM value in percent (0.01% increments). + USHORT usPWMMed; // The PWM value (in percent) at TMed. + USHORT usPWMHigh; // The PWM value at THigh. +} ATOM_PPLIB_FANTABLE; + +typedef struct _ATOM_PPLIB_FANTABLE2 +{ + ATOM_PPLIB_FANTABLE basicTable; + USHORT usTMax; // The max temperature +} ATOM_PPLIB_FANTABLE2; + +typedef struct _ATOM_PPLIB_EXTENDEDHEADER +{ + USHORT usSize; + ULONG ulMaxEngineClock; // For Overdrive. + ULONG ulMaxMemoryClock; // For Overdrive. + // Add extra system parameters here, always adjust size to include all fields. + USHORT usVCETableOffset; //points to ATOM_PPLIB_VCE_Table + USHORT usUVDTableOffset; //points to ATOM_PPLIB_UVD_Table +} ATOM_PPLIB_EXTENDEDHEADER; + +//// ATOM_PPLIB_POWERPLAYTABLE::ulPlatformCaps +#define ATOM_PP_PLATFORM_CAP_BACKBIAS 1 +#define ATOM_PP_PLATFORM_CAP_POWERPLAY 2 +#define ATOM_PP_PLATFORM_CAP_SBIOSPOWERSOURCE 4 +#define ATOM_PP_PLATFORM_CAP_ASPM_L0s 8 +#define ATOM_PP_PLATFORM_CAP_ASPM_L1 16 +#define ATOM_PP_PLATFORM_CAP_HARDWAREDC 32 +#define ATOM_PP_PLATFORM_CAP_GEMINIPRIMARY 64 +#define ATOM_PP_PLATFORM_CAP_STEPVDDC 128 +#define ATOM_PP_PLATFORM_CAP_VOLTAGECONTROL 256 +#define ATOM_PP_PLATFORM_CAP_SIDEPORTCONTROL 512 +#define ATOM_PP_PLATFORM_CAP_TURNOFFPLL_ASPML1 1024 +#define ATOM_PP_PLATFORM_CAP_HTLINKCONTROL 2048 +#define ATOM_PP_PLATFORM_CAP_MVDDCONTROL 4096 +#define ATOM_PP_PLATFORM_CAP_GOTO_BOOT_ON_ALERT 0x2000 // Go to boot state on alerts, e.g. on an AC->DC transition. +#define ATOM_PP_PLATFORM_CAP_DONT_WAIT_FOR_VBLANK_ON_ALERT 0x4000 // Do NOT wait for VBLANK during an alert (e.g. AC->DC transition). +#define ATOM_PP_PLATFORM_CAP_VDDCI_CONTROL 0x8000 // Does the driver control VDDCI independently from VDDC. +#define ATOM_PP_PLATFORM_CAP_REGULATOR_HOT 0x00010000 // Enable the 'regulator hot' feature. +#define ATOM_PP_PLATFORM_CAP_BACO 0x00020000 // Does the driver supports BACO state. + + +typedef struct _ATOM_PPLIB_POWERPLAYTABLE +{ + ATOM_COMMON_TABLE_HEADER sHeader; + + UCHAR ucDataRevision; + + UCHAR ucNumStates; + UCHAR ucStateEntrySize; + UCHAR ucClockInfoSize; + UCHAR ucNonClockSize; + + // offset from start of this table to array of ucNumStates ATOM_PPLIB_STATE structures + USHORT usStateArrayOffset; + + // offset from start of this table to array of ASIC-specific structures, + // currently ATOM_PPLIB_CLOCK_INFO. + USHORT usClockInfoArrayOffset; + + // offset from start of this table to array of ATOM_PPLIB_NONCLOCK_INFO + USHORT usNonClockInfoArrayOffset; + + USHORT usBackbiasTime; // in microseconds + USHORT usVoltageTime; // in microseconds + USHORT usTableSize; //the size of this structure, or the extended structure + + ULONG ulPlatformCaps; // See ATOM_PPLIB_CAPS_* + + ATOM_PPLIB_THERMALCONTROLLER sThermalController; + + USHORT usBootClockInfoOffset; + USHORT usBootNonClockInfoOffset; + +} ATOM_PPLIB_POWERPLAYTABLE; + +typedef struct _ATOM_PPLIB_POWERPLAYTABLE2 +{ + ATOM_PPLIB_POWERPLAYTABLE basicTable; + UCHAR ucNumCustomThermalPolicy; + USHORT usCustomThermalPolicyArrayOffset; +}ATOM_PPLIB_POWERPLAYTABLE2, *LPATOM_PPLIB_POWERPLAYTABLE2; + +typedef struct _ATOM_PPLIB_POWERPLAYTABLE3 +{ + ATOM_PPLIB_POWERPLAYTABLE2 basicTable2; + USHORT usFormatID; // To be used ONLY by PPGen. + USHORT usFanTableOffset; + USHORT usExtendendedHeaderOffset; +} ATOM_PPLIB_POWERPLAYTABLE3, *LPATOM_PPLIB_POWERPLAYTABLE3; + +typedef struct _ATOM_PPLIB_POWERPLAYTABLE4 +{ + ATOM_PPLIB_POWERPLAYTABLE3 basicTable3; + ULONG ulGoldenPPID; // PPGen use only + ULONG ulGoldenRevision; // PPGen use only + USHORT usVddcDependencyOnSCLKOffset; + USHORT usVddciDependencyOnMCLKOffset; + USHORT usVddcDependencyOnMCLKOffset; + USHORT usMaxClockVoltageOnDCOffset; + USHORT usVddcPhaseShedLimitsTableOffset; // Points to ATOM_PPLIB_PhaseSheddingLimits_Table + USHORT usReserved; +} ATOM_PPLIB_POWERPLAYTABLE4, *LPATOM_PPLIB_POWERPLAYTABLE4; + +typedef struct _ATOM_PPLIB_POWERPLAYTABLE5 +{ + ATOM_PPLIB_POWERPLAYTABLE4 basicTable4; + ULONG ulTDPLimit; + ULONG ulNearTDPLimit; + ULONG ulSQRampingThreshold; + USHORT usCACLeakageTableOffset; // Points to ATOM_PPLIB_CAC_Leakage_Table + ULONG ulCACLeakage; // The iLeakage for driver calculated CAC leakage table + USHORT usTDPODLimit; + USHORT usLoadLineSlope; // in milliOhms * 100 +} ATOM_PPLIB_POWERPLAYTABLE5, *LPATOM_PPLIB_POWERPLAYTABLE5; + +//// ATOM_PPLIB_NONCLOCK_INFO::usClassification +#define ATOM_PPLIB_CLASSIFICATION_UI_MASK 0x0007 +#define ATOM_PPLIB_CLASSIFICATION_UI_SHIFT 0 +#define ATOM_PPLIB_CLASSIFICATION_UI_NONE 0 +#define ATOM_PPLIB_CLASSIFICATION_UI_BATTERY 1 +#define ATOM_PPLIB_CLASSIFICATION_UI_BALANCED 3 +#define ATOM_PPLIB_CLASSIFICATION_UI_PERFORMANCE 5 +// 2, 4, 6, 7 are reserved + +#define ATOM_PPLIB_CLASSIFICATION_BOOT 0x0008 +#define ATOM_PPLIB_CLASSIFICATION_THERMAL 0x0010 +#define ATOM_PPLIB_CLASSIFICATION_LIMITEDPOWERSOURCE 0x0020 +#define ATOM_PPLIB_CLASSIFICATION_REST 0x0040 +#define ATOM_PPLIB_CLASSIFICATION_FORCED 0x0080 +#define ATOM_PPLIB_CLASSIFICATION_3DPERFORMANCE 0x0100 +#define ATOM_PPLIB_CLASSIFICATION_OVERDRIVETEMPLATE 0x0200 +#define ATOM_PPLIB_CLASSIFICATION_UVDSTATE 0x0400 +#define ATOM_PPLIB_CLASSIFICATION_3DLOW 0x0800 +#define ATOM_PPLIB_CLASSIFICATION_ACPI 0x1000 +#define ATOM_PPLIB_CLASSIFICATION_HD2STATE 0x2000 +#define ATOM_PPLIB_CLASSIFICATION_HDSTATE 0x4000 +#define ATOM_PPLIB_CLASSIFICATION_SDSTATE 0x8000 + +//// ATOM_PPLIB_NONCLOCK_INFO::usClassification2 +#define ATOM_PPLIB_CLASSIFICATION2_LIMITEDPOWERSOURCE_2 0x0001 +#define ATOM_PPLIB_CLASSIFICATION2_ULV 0x0002 +#define ATOM_PPLIB_CLASSIFICATION2_MVC 0x0004 //Multi-View Codec (BD-3D) + +//// ATOM_PPLIB_NONCLOCK_INFO::ulCapsAndSettings +#define ATOM_PPLIB_SINGLE_DISPLAY_ONLY 0x00000001 +#define ATOM_PPLIB_SUPPORTS_VIDEO_PLAYBACK 0x00000002 + +// 0 is 2.5Gb/s, 1 is 5Gb/s +#define ATOM_PPLIB_PCIE_LINK_SPEED_MASK 0x00000004 +#define ATOM_PPLIB_PCIE_LINK_SPEED_SHIFT 2 + +// lanes - 1: 1, 2, 4, 8, 12, 16 permitted by PCIE spec +#define ATOM_PPLIB_PCIE_LINK_WIDTH_MASK 0x000000F8 +#define ATOM_PPLIB_PCIE_LINK_WIDTH_SHIFT 3 + +// lookup into reduced refresh-rate table +#define ATOM_PPLIB_LIMITED_REFRESHRATE_VALUE_MASK 0x00000F00 +#define ATOM_PPLIB_LIMITED_REFRESHRATE_VALUE_SHIFT 8 + +#define ATOM_PPLIB_LIMITED_REFRESHRATE_UNLIMITED 0 +#define ATOM_PPLIB_LIMITED_REFRESHRATE_50HZ 1 +// 2-15 TBD as needed. + +#define ATOM_PPLIB_SOFTWARE_DISABLE_LOADBALANCING 0x00001000 +#define ATOM_PPLIB_SOFTWARE_ENABLE_SLEEP_FOR_TIMESTAMPS 0x00002000 + +#define ATOM_PPLIB_DISALLOW_ON_DC 0x00004000 + +#define ATOM_PPLIB_ENABLE_VARIBRIGHT 0x00008000 + +//memory related flags +#define ATOM_PPLIB_SWSTATE_MEMORY_DLL_OFF 0x000010000 + +//M3 Arb //2bits, current 3 sets of parameters in total +#define ATOM_PPLIB_M3ARB_MASK 0x00060000 +#define ATOM_PPLIB_M3ARB_SHIFT 17 + +#define ATOM_PPLIB_ENABLE_DRR 0x00080000 + +// remaining 16 bits are reserved +typedef struct _ATOM_PPLIB_THERMAL_STATE +{ + UCHAR ucMinTemperature; + UCHAR ucMaxTemperature; + UCHAR ucThermalAction; +}ATOM_PPLIB_THERMAL_STATE, *LPATOM_PPLIB_THERMAL_STATE; + +// Contained in an array starting at the offset +// in ATOM_PPLIB_POWERPLAYTABLE::usNonClockInfoArrayOffset. +// referenced from ATOM_PPLIB_STATE_INFO::ucNonClockStateIndex +#define ATOM_PPLIB_NONCLOCKINFO_VER1 12 +#define ATOM_PPLIB_NONCLOCKINFO_VER2 24 +typedef struct _ATOM_PPLIB_NONCLOCK_INFO +{ + USHORT usClassification; + UCHAR ucMinTemperature; + UCHAR ucMaxTemperature; + ULONG ulCapsAndSettings; + UCHAR ucRequiredPower; + USHORT usClassification2; + ULONG ulVCLK; + ULONG ulDCLK; + UCHAR ucUnused[5]; +} ATOM_PPLIB_NONCLOCK_INFO; + +// Contained in an array starting at the offset +// in ATOM_PPLIB_POWERPLAYTABLE::usClockInfoArrayOffset. +// referenced from ATOM_PPLIB_STATE::ucClockStateIndices +typedef struct _ATOM_PPLIB_R600_CLOCK_INFO +{ + USHORT usEngineClockLow; + UCHAR ucEngineClockHigh; + + USHORT usMemoryClockLow; + UCHAR ucMemoryClockHigh; + + USHORT usVDDC; + USHORT usUnused1; + USHORT usUnused2; + + ULONG ulFlags; // ATOM_PPLIB_R600_FLAGS_* + +} ATOM_PPLIB_R600_CLOCK_INFO; + +// ulFlags in ATOM_PPLIB_R600_CLOCK_INFO +#define ATOM_PPLIB_R600_FLAGS_PCIEGEN2 1 +#define ATOM_PPLIB_R600_FLAGS_UVDSAFE 2 +#define ATOM_PPLIB_R600_FLAGS_BACKBIASENABLE 4 +#define ATOM_PPLIB_R600_FLAGS_MEMORY_ODT_OFF 8 +#define ATOM_PPLIB_R600_FLAGS_MEMORY_DLL_OFF 16 +#define ATOM_PPLIB_R600_FLAGS_LOWPOWER 32 // On the RV770 use 'low power' setting (sequencer S0). + +typedef struct _ATOM_PPLIB_EVERGREEN_CLOCK_INFO +{ + USHORT usEngineClockLow; + UCHAR ucEngineClockHigh; + + USHORT usMemoryClockLow; + UCHAR ucMemoryClockHigh; + + USHORT usVDDC; + USHORT usVDDCI; + USHORT usUnused; + + ULONG ulFlags; // ATOM_PPLIB_R600_FLAGS_* + +} ATOM_PPLIB_EVERGREEN_CLOCK_INFO; + +typedef struct _ATOM_PPLIB_SI_CLOCK_INFO +{ + USHORT usEngineClockLow; + UCHAR ucEngineClockHigh; + + USHORT usMemoryClockLow; + UCHAR ucMemoryClockHigh; + + USHORT usVDDC; + USHORT usVDDCI; + UCHAR ucPCIEGen; + UCHAR ucUnused1; + + ULONG ulFlags; // ATOM_PPLIB_SI_FLAGS_*, no flag is necessary for now + +} ATOM_PPLIB_SI_CLOCK_INFO; + + +typedef struct _ATOM_PPLIB_RS780_CLOCK_INFO + +{ + USHORT usLowEngineClockLow; // Low Engine clock in MHz (the same way as on the R600). + UCHAR ucLowEngineClockHigh; + USHORT usHighEngineClockLow; // High Engine clock in MHz. + UCHAR ucHighEngineClockHigh; + USHORT usMemoryClockLow; // For now one of the ATOM_PPLIB_RS780_SPMCLK_XXXX constants. + UCHAR ucMemoryClockHigh; // Currentyl unused. + UCHAR ucPadding; // For proper alignment and size. + USHORT usVDDC; // For the 780, use: None, Low, High, Variable + UCHAR ucMaxHTLinkWidth; // From SBIOS - {2, 4, 8, 16} + UCHAR ucMinHTLinkWidth; // From SBIOS - {2, 4, 8, 16}. Effective only if CDLW enabled. Minimum down stream width could be bigger as display BW requriement. + USHORT usHTLinkFreq; // See definition ATOM_PPLIB_RS780_HTLINKFREQ_xxx or in MHz(>=200). + ULONG ulFlags; +} ATOM_PPLIB_RS780_CLOCK_INFO; + +#define ATOM_PPLIB_RS780_VOLTAGE_NONE 0 +#define ATOM_PPLIB_RS780_VOLTAGE_LOW 1 +#define ATOM_PPLIB_RS780_VOLTAGE_HIGH 2 +#define ATOM_PPLIB_RS780_VOLTAGE_VARIABLE 3 + +#define ATOM_PPLIB_RS780_SPMCLK_NONE 0 // We cannot change the side port memory clock, leave it as it is. +#define ATOM_PPLIB_RS780_SPMCLK_LOW 1 +#define ATOM_PPLIB_RS780_SPMCLK_HIGH 2 + +#define ATOM_PPLIB_RS780_HTLINKFREQ_NONE 0 +#define ATOM_PPLIB_RS780_HTLINKFREQ_LOW 1 +#define ATOM_PPLIB_RS780_HTLINKFREQ_HIGH 2 + +typedef struct _ATOM_PPLIB_SUMO_CLOCK_INFO{ + USHORT usEngineClockLow; //clockfrequency & 0xFFFF. The unit is in 10khz + UCHAR ucEngineClockHigh; //clockfrequency >> 16. + UCHAR vddcIndex; //2-bit vddc index; + USHORT tdpLimit; + //please initalize to 0 + USHORT rsv1; + //please initialize to 0s + ULONG rsv2[2]; +}ATOM_PPLIB_SUMO_CLOCK_INFO; + + + +typedef struct _ATOM_PPLIB_STATE_V2 +{ + //number of valid dpm levels in this state; Driver uses it to calculate the whole + //size of the state: sizeof(ATOM_PPLIB_STATE_V2) + (ucNumDPMLevels - 1) * sizeof(UCHAR) + UCHAR ucNumDPMLevels; + + //a index to the array of nonClockInfos + UCHAR nonClockInfoIndex; + /** + * Driver will read the first ucNumDPMLevels in this array + */ + UCHAR clockInfoIndex[1]; +} ATOM_PPLIB_STATE_V2; + +typedef struct _StateArray{ + //how many states we have + UCHAR ucNumEntries; + + ATOM_PPLIB_STATE_V2 states[1]; +}StateArray; + + +typedef struct _ClockInfoArray{ + //how many clock levels we have + UCHAR ucNumEntries; + + //sizeof(ATOM_PPLIB_CLOCK_INFO) + UCHAR ucEntrySize; + + UCHAR clockInfo[1]; +}ClockInfoArray; + +typedef struct _NonClockInfoArray{ + + //how many non-clock levels we have. normally should be same as number of states + UCHAR ucNumEntries; + //sizeof(ATOM_PPLIB_NONCLOCK_INFO) + UCHAR ucEntrySize; + + ATOM_PPLIB_NONCLOCK_INFO nonClockInfo[1]; +}NonClockInfoArray; + +typedef struct _ATOM_PPLIB_Clock_Voltage_Dependency_Record +{ + USHORT usClockLow; + UCHAR ucClockHigh; + USHORT usVoltage; +}ATOM_PPLIB_Clock_Voltage_Dependency_Record; + +typedef struct _ATOM_PPLIB_Clock_Voltage_Dependency_Table +{ + UCHAR ucNumEntries; // Number of entries. + ATOM_PPLIB_Clock_Voltage_Dependency_Record entries[1]; // Dynamically allocate entries. +}ATOM_PPLIB_Clock_Voltage_Dependency_Table; + +typedef struct _ATOM_PPLIB_Clock_Voltage_Limit_Record +{ + USHORT usSclkLow; + UCHAR ucSclkHigh; + USHORT usMclkLow; + UCHAR ucMclkHigh; + USHORT usVddc; + USHORT usVddci; +}ATOM_PPLIB_Clock_Voltage_Limit_Record; + +typedef struct _ATOM_PPLIB_Clock_Voltage_Limit_Table +{ + UCHAR ucNumEntries; // Number of entries. + ATOM_PPLIB_Clock_Voltage_Limit_Record entries[1]; // Dynamically allocate entries. +}ATOM_PPLIB_Clock_Voltage_Limit_Table; + +typedef struct _ATOM_PPLIB_CAC_Leakage_Record +{ + USHORT usVddc; // We use this field for the "fake" standardized VDDC for power calculations + ULONG ulLeakageValue; +}ATOM_PPLIB_CAC_Leakage_Record; + +typedef struct _ATOM_PPLIB_CAC_Leakage_Table +{ + UCHAR ucNumEntries; // Number of entries. + ATOM_PPLIB_CAC_Leakage_Record entries[1]; // Dynamically allocate entries. +}ATOM_PPLIB_CAC_Leakage_Table; + +typedef struct _ATOM_PPLIB_PhaseSheddingLimits_Record +{ + USHORT usVoltage; + USHORT usSclkLow; + UCHAR ucSclkHigh; + USHORT usMclkLow; + UCHAR ucMclkHigh; +}ATOM_PPLIB_PhaseSheddingLimits_Record; + +typedef struct _ATOM_PPLIB_PhaseSheddingLimits_Table +{ + UCHAR ucNumEntries; // Number of entries. + ATOM_PPLIB_PhaseSheddingLimits_Record entries[1]; // Dynamically allocate entries. +}ATOM_PPLIB_PhaseSheddingLimits_Table; + +typedef struct _VCEClockInfo{ + USHORT usEVClkLow; + UCHAR ucEVClkHigh; + USHORT usECClkLow; + UCHAR ucECClkHigh; +}VCEClockInfo; + +typedef struct _VCEClockInfoArray{ + UCHAR ucNumEntries; + VCEClockInfo entries[1]; +}VCEClockInfoArray; + +typedef struct _ATOM_PPLIB_VCE_Clock_Voltage_Limit_Record +{ + USHORT usVoltage; + UCHAR ucVCEClockInfoIndex; +}ATOM_PPLIB_VCE_Clock_Voltage_Limit_Record; + +typedef struct _ATOM_PPLIB_VCE_Clock_Voltage_Limit_Table +{ + UCHAR numEntries; + ATOM_PPLIB_VCE_Clock_Voltage_Limit_Record entries[1]; +}ATOM_PPLIB_VCE_Clock_Voltage_Limit_Table; + +typedef struct _ATOM_PPLIB_VCE_State_Record +{ + UCHAR ucVCEClockInfoIndex; + UCHAR ucClockInfoIndex; //highest 2 bits indicates memory p-states, lower 6bits indicates index to ClockInfoArrary +}ATOM_PPLIB_VCE_State_Record; + +typedef struct _ATOM_PPLIB_VCE_State_Table +{ + UCHAR numEntries; + ATOM_PPLIB_VCE_State_Record entries[1]; +}ATOM_PPLIB_VCE_State_Table; + + +typedef struct _ATOM_PPLIB_VCE_Table +{ + UCHAR revid; +// VCEClockInfoArray array; +// ATOM_PPLIB_VCE_Clock_Voltage_Limit_Table limits; +// ATOM_PPLIB_VCE_State_Table states; +}ATOM_PPLIB_VCE_Table; + + +typedef struct _UVDClockInfo{ + USHORT usVClkLow; + UCHAR ucVClkHigh; + USHORT usDClkLow; + UCHAR ucDClkHigh; +}UVDClockInfo; + +typedef struct _UVDClockInfoArray{ + UCHAR ucNumEntries; + UVDClockInfo entries[1]; +}UVDClockInfoArray; + +typedef struct _ATOM_PPLIB_UVD_Clock_Voltage_Limit_Record +{ + USHORT usVoltage; + UCHAR ucUVDClockInfoIndex; +}ATOM_PPLIB_UVD_Clock_Voltage_Limit_Record; + +typedef struct _ATOM_PPLIB_UVD_Clock_Voltage_Limit_Table +{ + UCHAR numEntries; + ATOM_PPLIB_UVD_Clock_Voltage_Limit_Record entries[1]; +}ATOM_PPLIB_UVD_Clock_Voltage_Limit_Table; + +typedef struct _ATOM_PPLIB_UVD_State_Record +{ + UCHAR ucUVDClockInfoIndex; + UCHAR ucClockInfoIndex; //highest 2 bits indicates memory p-states, lower 6bits indicates index to ClockInfoArrary +}ATOM_PPLIB_UVD_State_Record; + +typedef struct _ATOM_PPLIB_UVD_State_Table +{ + UCHAR numEntries; + ATOM_PPLIB_UVD_State_Record entries[1]; +}ATOM_PPLIB_UVD_State_Table; + + +typedef struct _ATOM_PPLIB_UVD_Table +{ + UCHAR revid; +// UVDClockInfoArray array; +// ATOM_PPLIB_UVD_Clock_Voltage_Limit_Table limits; +// ATOM_PPLIB_UVD_State_Table states; +}ATOM_PPLIB_UVD_Table; + +/**************************************************************************/ + + +// Following definitions are for compatibility issue in different SW components. +#define ATOM_MASTER_DATA_TABLE_REVISION 0x01 +#define Object_Info Object_Header +#define AdjustARB_SEQ MC_InitParameter +#define VRAM_GPIO_DetectionInfo VoltageObjectInfo +#define ASIC_VDDCI_Info ASIC_ProfilingInfo +#define ASIC_MVDDQ_Info MemoryTrainingInfo +#define SS_Info PPLL_SS_Info +#define ASIC_MVDDC_Info ASIC_InternalSS_Info +#define DispDevicePriorityInfo SaveRestoreInfo +#define DispOutInfo TV_VideoMode + + +#define ATOM_ENCODER_OBJECT_TABLE ATOM_OBJECT_TABLE +#define ATOM_CONNECTOR_OBJECT_TABLE ATOM_OBJECT_TABLE + +//New device naming, remove them when both DAL/VBIOS is ready +#define DFP2I_OUTPUT_CONTROL_PARAMETERS CRT1_OUTPUT_CONTROL_PARAMETERS +#define DFP2I_OUTPUT_CONTROL_PS_ALLOCATION DFP2I_OUTPUT_CONTROL_PARAMETERS + +#define DFP1X_OUTPUT_CONTROL_PARAMETERS CRT1_OUTPUT_CONTROL_PARAMETERS +#define DFP1X_OUTPUT_CONTROL_PS_ALLOCATION DFP1X_OUTPUT_CONTROL_PARAMETERS + +#define DFP1I_OUTPUT_CONTROL_PARAMETERS DFP1_OUTPUT_CONTROL_PARAMETERS +#define DFP1I_OUTPUT_CONTROL_PS_ALLOCATION DFP1_OUTPUT_CONTROL_PS_ALLOCATION + +#define ATOM_DEVICE_DFP1I_SUPPORT ATOM_DEVICE_DFP1_SUPPORT +#define ATOM_DEVICE_DFP1X_SUPPORT ATOM_DEVICE_DFP2_SUPPORT + +#define ATOM_DEVICE_DFP1I_INDEX ATOM_DEVICE_DFP1_INDEX +#define ATOM_DEVICE_DFP1X_INDEX ATOM_DEVICE_DFP2_INDEX + +#define ATOM_DEVICE_DFP2I_INDEX 0x00000009 +#define ATOM_DEVICE_DFP2I_SUPPORT (0x1L << ATOM_DEVICE_DFP2I_INDEX) + +#define ATOM_S0_DFP1I ATOM_S0_DFP1 +#define ATOM_S0_DFP1X ATOM_S0_DFP2 + +#define ATOM_S0_DFP2I 0x00200000L +#define ATOM_S0_DFP2Ib2 0x20 + +#define ATOM_S2_DFP1I_DPMS_STATE ATOM_S2_DFP1_DPMS_STATE +#define ATOM_S2_DFP1X_DPMS_STATE ATOM_S2_DFP2_DPMS_STATE + +#define ATOM_S2_DFP2I_DPMS_STATE 0x02000000L +#define ATOM_S2_DFP2I_DPMS_STATEb3 0x02 + +#define ATOM_S3_DFP2I_ACTIVEb1 0x02 + +#define ATOM_S3_DFP1I_ACTIVE ATOM_S3_DFP1_ACTIVE +#define ATOM_S3_DFP1X_ACTIVE ATOM_S3_DFP2_ACTIVE + +#define ATOM_S3_DFP2I_ACTIVE 0x00000200L + +#define ATOM_S3_DFP1I_CRTC_ACTIVE ATOM_S3_DFP1_CRTC_ACTIVE +#define ATOM_S3_DFP1X_CRTC_ACTIVE ATOM_S3_DFP2_CRTC_ACTIVE +#define ATOM_S3_DFP2I_CRTC_ACTIVE 0x02000000L + +#define ATOM_S3_DFP2I_CRTC_ACTIVEb3 0x02 +#define ATOM_S5_DOS_REQ_DFP2Ib1 0x02 + +#define ATOM_S5_DOS_REQ_DFP2I 0x0200 +#define ATOM_S6_ACC_REQ_DFP1I ATOM_S6_ACC_REQ_DFP1 +#define ATOM_S6_ACC_REQ_DFP1X ATOM_S6_ACC_REQ_DFP2 + +#define ATOM_S6_ACC_REQ_DFP2Ib3 0x02 +#define ATOM_S6_ACC_REQ_DFP2I 0x02000000L + +#define TMDS1XEncoderControl DVOEncoderControl +#define DFP1XOutputControl DVOOutputControl + +#define ExternalDFPOutputControl DFP1XOutputControl +#define EnableExternalTMDS_Encoder TMDS1XEncoderControl + +#define DFP1IOutputControl TMDSAOutputControl +#define DFP2IOutputControl LVTMAOutputControl + +#define DAC1_ENCODER_CONTROL_PARAMETERS DAC_ENCODER_CONTROL_PARAMETERS +#define DAC1_ENCODER_CONTROL_PS_ALLOCATION DAC_ENCODER_CONTROL_PS_ALLOCATION + +#define DAC2_ENCODER_CONTROL_PARAMETERS DAC_ENCODER_CONTROL_PARAMETERS +#define DAC2_ENCODER_CONTROL_PS_ALLOCATION DAC_ENCODER_CONTROL_PS_ALLOCATION + +#define ucDac1Standard ucDacStandard +#define ucDac2Standard ucDacStandard + +#define TMDS1EncoderControl TMDSAEncoderControl +#define TMDS2EncoderControl LVTMAEncoderControl + +#define DFP1OutputControl TMDSAOutputControl +#define DFP2OutputControl LVTMAOutputControl +#define CRT1OutputControl DAC1OutputControl +#define CRT2OutputControl DAC2OutputControl + +//These two lines will be removed for sure in a few days, will follow up with Michael V. +#define EnableLVDS_SS EnableSpreadSpectrumOnPPLL +#define ENABLE_LVDS_SS_PARAMETERS_V3 ENABLE_SPREAD_SPECTRUM_ON_PPLL + +//#define ATOM_S2_CRT1_DPMS_STATE 0x00010000L +//#define ATOM_S2_LCD1_DPMS_STATE ATOM_S2_CRT1_DPMS_STATE +//#define ATOM_S2_TV1_DPMS_STATE ATOM_S2_CRT1_DPMS_STATE +//#define ATOM_S2_DFP1_DPMS_STATE ATOM_S2_CRT1_DPMS_STATE +//#define ATOM_S2_CRT2_DPMS_STATE ATOM_S2_CRT1_DPMS_STATE + +#define ATOM_S6_ACC_REQ_TV2 0x00400000L +#define ATOM_DEVICE_TV2_INDEX 0x00000006 +#define ATOM_DEVICE_TV2_SUPPORT (0x1L << ATOM_DEVICE_TV2_INDEX) +#define ATOM_S0_TV2 0x00100000L +#define ATOM_S3_TV2_ACTIVE ATOM_S3_DFP6_ACTIVE +#define ATOM_S3_TV2_CRTC_ACTIVE ATOM_S3_DFP6_CRTC_ACTIVE + +// +#define ATOM_S2_CRT1_DPMS_STATE 0x00010000L +#define ATOM_S2_LCD1_DPMS_STATE 0x00020000L +#define ATOM_S2_TV1_DPMS_STATE 0x00040000L +#define ATOM_S2_DFP1_DPMS_STATE 0x00080000L +#define ATOM_S2_CRT2_DPMS_STATE 0x00100000L +#define ATOM_S2_LCD2_DPMS_STATE 0x00200000L +#define ATOM_S2_TV2_DPMS_STATE 0x00400000L +#define ATOM_S2_DFP2_DPMS_STATE 0x00800000L +#define ATOM_S2_CV_DPMS_STATE 0x01000000L +#define ATOM_S2_DFP3_DPMS_STATE 0x02000000L +#define ATOM_S2_DFP4_DPMS_STATE 0x04000000L +#define ATOM_S2_DFP5_DPMS_STATE 0x08000000L + +#define ATOM_S2_CRT1_DPMS_STATEb2 0x01 +#define ATOM_S2_LCD1_DPMS_STATEb2 0x02 +#define ATOM_S2_TV1_DPMS_STATEb2 0x04 +#define ATOM_S2_DFP1_DPMS_STATEb2 0x08 +#define ATOM_S2_CRT2_DPMS_STATEb2 0x10 +#define ATOM_S2_LCD2_DPMS_STATEb2 0x20 +#define ATOM_S2_TV2_DPMS_STATEb2 0x40 +#define ATOM_S2_DFP2_DPMS_STATEb2 0x80 +#define ATOM_S2_CV_DPMS_STATEb3 0x01 +#define ATOM_S2_DFP3_DPMS_STATEb3 0x02 +#define ATOM_S2_DFP4_DPMS_STATEb3 0x04 +#define ATOM_S2_DFP5_DPMS_STATEb3 0x08 + +#define ATOM_S3_ASIC_GUI_ENGINE_HUNGb3 0x20 +#define ATOM_S3_ALLOW_FAST_PWR_SWITCHb3 0x40 +#define ATOM_S3_RQST_GPU_USE_MIN_PWRb3 0x80 + +/*********************************************************************************/ + +#pragma pack() // BIOS data must use byte aligment + +// +// AMD ACPI Table +// +#pragma pack(1) + +typedef struct { + ULONG Signature; + ULONG TableLength; //Length + UCHAR Revision; + UCHAR Checksum; + UCHAR OemId[6]; + UCHAR OemTableId[8]; //UINT64 OemTableId; + ULONG OemRevision; + ULONG CreatorId; + ULONG CreatorRevision; +} AMD_ACPI_DESCRIPTION_HEADER; +/* +//EFI_ACPI_DESCRIPTION_HEADER from AcpiCommon.h +typedef struct { + UINT32 Signature; //0x0 + UINT32 Length; //0x4 + UINT8 Revision; //0x8 + UINT8 Checksum; //0x9 + UINT8 OemId[6]; //0xA + UINT64 OemTableId; //0x10 + UINT32 OemRevision; //0x18 + UINT32 CreatorId; //0x1C + UINT32 CreatorRevision; //0x20 +}EFI_ACPI_DESCRIPTION_HEADER; +*/ +typedef struct { + AMD_ACPI_DESCRIPTION_HEADER SHeader; + UCHAR TableUUID[16]; //0x24 + ULONG VBIOSImageOffset; //0x34. Offset to the first GOP_VBIOS_CONTENT block from the beginning of the stucture. + ULONG Lib1ImageOffset; //0x38. Offset to the first GOP_LIB1_CONTENT block from the beginning of the stucture. + ULONG Reserved[4]; //0x3C +}UEFI_ACPI_VFCT; + +typedef struct { + ULONG PCIBus; //0x4C + ULONG PCIDevice; //0x50 + ULONG PCIFunction; //0x54 + USHORT VendorID; //0x58 + USHORT DeviceID; //0x5A + USHORT SSVID; //0x5C + USHORT SSID; //0x5E + ULONG Revision; //0x60 + ULONG ImageLength; //0x64 +}VFCT_IMAGE_HEADER; + + +typedef struct { + VFCT_IMAGE_HEADER VbiosHeader; + UCHAR VbiosContent[1]; +}GOP_VBIOS_CONTENT; + +typedef struct { + VFCT_IMAGE_HEADER Lib1Header; + UCHAR Lib1Content[1]; +}GOP_LIB1_CONTENT; + +#pragma pack() + + +#endif /* _ATOMBIOS_H */ diff --git a/sys/dev/drm2/radeon/atombios_crtc.c b/sys/dev/drm2/radeon/atombios_crtc.c new file mode 100644 index 00000000000..c4c5c4606fa --- /dev/null +++ b/sys/dev/drm2/radeon/atombios_crtc.c @@ -0,0 +1,1938 @@ +/* + * Copyright 2007-8 Advanced Micro Devices, Inc. + * Copyright 2008 Red Hat Inc. + * + * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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. + * + * Authors: Dave Airlie + * Alex Deucher + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include "radeon.h" +#include "atom.h" +#include "atom-bits.h" + +static void atombios_overscan_setup(struct drm_crtc *crtc, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + struct drm_device *dev = crtc->dev; + struct radeon_device *rdev = dev->dev_private; + struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); + SET_CRTC_OVERSCAN_PS_ALLOCATION args; + int index = GetIndexIntoMasterTable(COMMAND, SetCRTC_OverScan); + int a1, a2; + + memset(&args, 0, sizeof(args)); + + args.ucCRTC = radeon_crtc->crtc_id; + + switch (radeon_crtc->rmx_type) { + case RMX_CENTER: + args.usOverscanTop = cpu_to_le16((adjusted_mode->crtc_vdisplay - mode->crtc_vdisplay) / 2); + args.usOverscanBottom = cpu_to_le16((adjusted_mode->crtc_vdisplay - mode->crtc_vdisplay) / 2); + args.usOverscanLeft = cpu_to_le16((adjusted_mode->crtc_hdisplay - mode->crtc_hdisplay) / 2); + args.usOverscanRight = cpu_to_le16((adjusted_mode->crtc_hdisplay - mode->crtc_hdisplay) / 2); + break; + case RMX_ASPECT: + a1 = mode->crtc_vdisplay * adjusted_mode->crtc_hdisplay; + a2 = adjusted_mode->crtc_vdisplay * mode->crtc_hdisplay; + + if (a1 > a2) { + args.usOverscanLeft = cpu_to_le16((adjusted_mode->crtc_hdisplay - (a2 / mode->crtc_vdisplay)) / 2); + args.usOverscanRight = cpu_to_le16((adjusted_mode->crtc_hdisplay - (a2 / mode->crtc_vdisplay)) / 2); + } else if (a2 > a1) { + args.usOverscanTop = cpu_to_le16((adjusted_mode->crtc_vdisplay - (a1 / mode->crtc_hdisplay)) / 2); + args.usOverscanBottom = cpu_to_le16((adjusted_mode->crtc_vdisplay - (a1 / mode->crtc_hdisplay)) / 2); + } + break; + case RMX_FULL: + default: + args.usOverscanRight = cpu_to_le16(radeon_crtc->h_border); + args.usOverscanLeft = cpu_to_le16(radeon_crtc->h_border); + args.usOverscanBottom = cpu_to_le16(radeon_crtc->v_border); + args.usOverscanTop = cpu_to_le16(radeon_crtc->v_border); + break; + } + atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args); +} + +static void atombios_scaler_setup(struct drm_crtc *crtc) +{ + struct drm_device *dev = crtc->dev; + struct radeon_device *rdev = dev->dev_private; + struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); + ENABLE_SCALER_PS_ALLOCATION args; + int index = GetIndexIntoMasterTable(COMMAND, EnableScaler); + struct radeon_encoder *radeon_encoder = + to_radeon_encoder(radeon_crtc->encoder); + /* fixme - fill in enc_priv for atom dac */ + enum radeon_tv_std tv_std = TV_STD_NTSC; + bool is_tv = false, is_cv = false; + + if (!ASIC_IS_AVIVO(rdev) && radeon_crtc->crtc_id) + return; + + if (radeon_encoder->active_device & ATOM_DEVICE_TV_SUPPORT) { + struct radeon_encoder_atom_dac *tv_dac = radeon_encoder->enc_priv; + tv_std = tv_dac->tv_std; + is_tv = true; + } + + memset(&args, 0, sizeof(args)); + + args.ucScaler = radeon_crtc->crtc_id; + + if (is_tv) { + switch (tv_std) { + case TV_STD_NTSC: + default: + args.ucTVStandard = ATOM_TV_NTSC; + break; + case TV_STD_PAL: + args.ucTVStandard = ATOM_TV_PAL; + break; + case TV_STD_PAL_M: + args.ucTVStandard = ATOM_TV_PALM; + break; + case TV_STD_PAL_60: + args.ucTVStandard = ATOM_TV_PAL60; + break; + case TV_STD_NTSC_J: + args.ucTVStandard = ATOM_TV_NTSCJ; + break; + case TV_STD_SCART_PAL: + args.ucTVStandard = ATOM_TV_PAL; /* ??? */ + break; + case TV_STD_SECAM: + args.ucTVStandard = ATOM_TV_SECAM; + break; + case TV_STD_PAL_CN: + args.ucTVStandard = ATOM_TV_PALCN; + break; + } + args.ucEnable = SCALER_ENABLE_MULTITAP_MODE; + } else if (is_cv) { + args.ucTVStandard = ATOM_TV_CV; + args.ucEnable = SCALER_ENABLE_MULTITAP_MODE; + } else { + switch (radeon_crtc->rmx_type) { + case RMX_FULL: + args.ucEnable = ATOM_SCALER_EXPANSION; + break; + case RMX_CENTER: + args.ucEnable = ATOM_SCALER_CENTER; + break; + case RMX_ASPECT: + args.ucEnable = ATOM_SCALER_EXPANSION; + break; + default: + if (ASIC_IS_AVIVO(rdev)) + args.ucEnable = ATOM_SCALER_DISABLE; + else + args.ucEnable = ATOM_SCALER_CENTER; + break; + } + } + atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args); + if ((is_tv || is_cv) + && rdev->family >= CHIP_RV515 && rdev->family <= CHIP_R580) { + atom_rv515_force_tv_scaler(rdev, radeon_crtc); + } +} + +static void atombios_lock_crtc(struct drm_crtc *crtc, int lock) +{ + struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); + struct drm_device *dev = crtc->dev; + struct radeon_device *rdev = dev->dev_private; + int index = + GetIndexIntoMasterTable(COMMAND, UpdateCRTC_DoubleBufferRegisters); + ENABLE_CRTC_PS_ALLOCATION args; + + memset(&args, 0, sizeof(args)); + + args.ucCRTC = radeon_crtc->crtc_id; + args.ucEnable = lock; + + atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args); +} + +static void atombios_enable_crtc(struct drm_crtc *crtc, int state) +{ + struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); + struct drm_device *dev = crtc->dev; + struct radeon_device *rdev = dev->dev_private; + int index = GetIndexIntoMasterTable(COMMAND, EnableCRTC); + ENABLE_CRTC_PS_ALLOCATION args; + + memset(&args, 0, sizeof(args)); + + args.ucCRTC = radeon_crtc->crtc_id; + args.ucEnable = state; + + atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args); +} + +static void atombios_enable_crtc_memreq(struct drm_crtc *crtc, int state) +{ + struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); + struct drm_device *dev = crtc->dev; + struct radeon_device *rdev = dev->dev_private; + int index = GetIndexIntoMasterTable(COMMAND, EnableCRTCMemReq); + ENABLE_CRTC_PS_ALLOCATION args; + + memset(&args, 0, sizeof(args)); + + args.ucCRTC = radeon_crtc->crtc_id; + args.ucEnable = state; + + atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args); +} + +static void atombios_blank_crtc(struct drm_crtc *crtc, int state) +{ + struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); + struct drm_device *dev = crtc->dev; + struct radeon_device *rdev = dev->dev_private; + int index = GetIndexIntoMasterTable(COMMAND, BlankCRTC); + BLANK_CRTC_PS_ALLOCATION args; + + memset(&args, 0, sizeof(args)); + + args.ucCRTC = radeon_crtc->crtc_id; + args.ucBlanking = state; + + atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args); +} + +static void atombios_powergate_crtc(struct drm_crtc *crtc, int state) +{ + struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); + struct drm_device *dev = crtc->dev; + struct radeon_device *rdev = dev->dev_private; + int index = GetIndexIntoMasterTable(COMMAND, EnableDispPowerGating); + ENABLE_DISP_POWER_GATING_PARAMETERS_V2_1 args; + + memset(&args, 0, sizeof(args)); + + args.ucDispPipeId = radeon_crtc->crtc_id; + args.ucEnable = state; + + atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args); +} + +void atombios_crtc_dpms(struct drm_crtc *crtc, int mode) +{ + struct drm_device *dev = crtc->dev; + struct radeon_device *rdev = dev->dev_private; + struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); + + switch (mode) { + case DRM_MODE_DPMS_ON: + radeon_crtc->enabled = true; + /* adjust pm to dpms changes BEFORE enabling crtcs */ + radeon_pm_compute_clocks(rdev); + if (ASIC_IS_DCE6(rdev) && !radeon_crtc->in_mode_set) + atombios_powergate_crtc(crtc, ATOM_DISABLE); + atombios_enable_crtc(crtc, ATOM_ENABLE); + if (ASIC_IS_DCE3(rdev) && !ASIC_IS_DCE6(rdev)) + atombios_enable_crtc_memreq(crtc, ATOM_ENABLE); + atombios_blank_crtc(crtc, ATOM_DISABLE); + drm_vblank_post_modeset(dev, radeon_crtc->crtc_id); + radeon_crtc_load_lut(crtc); + break; + case DRM_MODE_DPMS_STANDBY: + case DRM_MODE_DPMS_SUSPEND: + case DRM_MODE_DPMS_OFF: + drm_vblank_pre_modeset(dev, radeon_crtc->crtc_id); + if (radeon_crtc->enabled) + atombios_blank_crtc(crtc, ATOM_ENABLE); + if (ASIC_IS_DCE3(rdev) && !ASIC_IS_DCE6(rdev)) + atombios_enable_crtc_memreq(crtc, ATOM_DISABLE); + atombios_enable_crtc(crtc, ATOM_DISABLE); + radeon_crtc->enabled = false; + if (ASIC_IS_DCE6(rdev) && !radeon_crtc->in_mode_set) + atombios_powergate_crtc(crtc, ATOM_ENABLE); + /* adjust pm to dpms changes AFTER disabling crtcs */ + radeon_pm_compute_clocks(rdev); + break; + } +} + +static void +atombios_set_crtc_dtd_timing(struct drm_crtc *crtc, + struct drm_display_mode *mode) +{ + struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); + struct drm_device *dev = crtc->dev; + struct radeon_device *rdev = dev->dev_private; + SET_CRTC_USING_DTD_TIMING_PARAMETERS args; + int index = GetIndexIntoMasterTable(COMMAND, SetCRTC_UsingDTDTiming); + u16 misc = 0; + + memset(&args, 0, sizeof(args)); + args.usH_Size = cpu_to_le16(mode->crtc_hdisplay - (radeon_crtc->h_border * 2)); + args.usH_Blanking_Time = + cpu_to_le16(mode->crtc_hblank_end - mode->crtc_hdisplay + (radeon_crtc->h_border * 2)); + args.usV_Size = cpu_to_le16(mode->crtc_vdisplay - (radeon_crtc->v_border * 2)); + args.usV_Blanking_Time = + cpu_to_le16(mode->crtc_vblank_end - mode->crtc_vdisplay + (radeon_crtc->v_border * 2)); + args.usH_SyncOffset = + cpu_to_le16(mode->crtc_hsync_start - mode->crtc_hdisplay + radeon_crtc->h_border); + args.usH_SyncWidth = + cpu_to_le16(mode->crtc_hsync_end - mode->crtc_hsync_start); + args.usV_SyncOffset = + cpu_to_le16(mode->crtc_vsync_start - mode->crtc_vdisplay + radeon_crtc->v_border); + args.usV_SyncWidth = + cpu_to_le16(mode->crtc_vsync_end - mode->crtc_vsync_start); + args.ucH_Border = radeon_crtc->h_border; + args.ucV_Border = radeon_crtc->v_border; + + if (mode->flags & DRM_MODE_FLAG_NVSYNC) + misc |= ATOM_VSYNC_POLARITY; + if (mode->flags & DRM_MODE_FLAG_NHSYNC) + misc |= ATOM_HSYNC_POLARITY; + if (mode->flags & DRM_MODE_FLAG_CSYNC) + misc |= ATOM_COMPOSITESYNC; + if (mode->flags & DRM_MODE_FLAG_INTERLACE) + misc |= ATOM_INTERLACE; + if (mode->flags & DRM_MODE_FLAG_DBLSCAN) + misc |= ATOM_DOUBLE_CLOCK_MODE; + + args.susModeMiscInfo.usAccess = cpu_to_le16(misc); + args.ucCRTC = radeon_crtc->crtc_id; + + atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args); +} + +static void atombios_crtc_set_timing(struct drm_crtc *crtc, + struct drm_display_mode *mode) +{ + struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); + struct drm_device *dev = crtc->dev; + struct radeon_device *rdev = dev->dev_private; + SET_CRTC_TIMING_PARAMETERS_PS_ALLOCATION args; + int index = GetIndexIntoMasterTable(COMMAND, SetCRTC_Timing); + u16 misc = 0; + + memset(&args, 0, sizeof(args)); + args.usH_Total = cpu_to_le16(mode->crtc_htotal); + args.usH_Disp = cpu_to_le16(mode->crtc_hdisplay); + args.usH_SyncStart = cpu_to_le16(mode->crtc_hsync_start); + args.usH_SyncWidth = + cpu_to_le16(mode->crtc_hsync_end - mode->crtc_hsync_start); + args.usV_Total = cpu_to_le16(mode->crtc_vtotal); + args.usV_Disp = cpu_to_le16(mode->crtc_vdisplay); + args.usV_SyncStart = cpu_to_le16(mode->crtc_vsync_start); + args.usV_SyncWidth = + cpu_to_le16(mode->crtc_vsync_end - mode->crtc_vsync_start); + + args.ucOverscanRight = radeon_crtc->h_border; + args.ucOverscanLeft = radeon_crtc->h_border; + args.ucOverscanBottom = radeon_crtc->v_border; + args.ucOverscanTop = radeon_crtc->v_border; + + if (mode->flags & DRM_MODE_FLAG_NVSYNC) + misc |= ATOM_VSYNC_POLARITY; + if (mode->flags & DRM_MODE_FLAG_NHSYNC) + misc |= ATOM_HSYNC_POLARITY; + if (mode->flags & DRM_MODE_FLAG_CSYNC) + misc |= ATOM_COMPOSITESYNC; + if (mode->flags & DRM_MODE_FLAG_INTERLACE) + misc |= ATOM_INTERLACE; + if (mode->flags & DRM_MODE_FLAG_DBLSCAN) + misc |= ATOM_DOUBLE_CLOCK_MODE; + + args.susModeMiscInfo.usAccess = cpu_to_le16(misc); + args.ucCRTC = radeon_crtc->crtc_id; + + atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args); +} + +static void atombios_disable_ss(struct radeon_device *rdev, int pll_id) +{ + u32 ss_cntl; + + if (ASIC_IS_DCE4(rdev)) { + switch (pll_id) { + case ATOM_PPLL1: + ss_cntl = RREG32(EVERGREEN_P1PLL_SS_CNTL); + ss_cntl &= ~EVERGREEN_PxPLL_SS_EN; + WREG32(EVERGREEN_P1PLL_SS_CNTL, ss_cntl); + break; + case ATOM_PPLL2: + ss_cntl = RREG32(EVERGREEN_P2PLL_SS_CNTL); + ss_cntl &= ~EVERGREEN_PxPLL_SS_EN; + WREG32(EVERGREEN_P2PLL_SS_CNTL, ss_cntl); + break; + case ATOM_DCPLL: + case ATOM_PPLL_INVALID: + return; + } + } else if (ASIC_IS_AVIVO(rdev)) { + switch (pll_id) { + case ATOM_PPLL1: + ss_cntl = RREG32(AVIVO_P1PLL_INT_SS_CNTL); + ss_cntl &= ~1; + WREG32(AVIVO_P1PLL_INT_SS_CNTL, ss_cntl); + break; + case ATOM_PPLL2: + ss_cntl = RREG32(AVIVO_P2PLL_INT_SS_CNTL); + ss_cntl &= ~1; + WREG32(AVIVO_P2PLL_INT_SS_CNTL, ss_cntl); + break; + case ATOM_DCPLL: + case ATOM_PPLL_INVALID: + return; + } + } +} + + +union atom_enable_ss { + ENABLE_LVDS_SS_PARAMETERS lvds_ss; + ENABLE_LVDS_SS_PARAMETERS_V2 lvds_ss_2; + ENABLE_SPREAD_SPECTRUM_ON_PPLL_PS_ALLOCATION v1; + ENABLE_SPREAD_SPECTRUM_ON_PPLL_V2 v2; + ENABLE_SPREAD_SPECTRUM_ON_PPLL_V3 v3; +}; + +static void atombios_crtc_program_ss(struct radeon_device *rdev, + int enable, + int pll_id, + int crtc_id, + struct radeon_atom_ss *ss) +{ + unsigned i; + int index = GetIndexIntoMasterTable(COMMAND, EnableSpreadSpectrumOnPPLL); + union atom_enable_ss args; + + if (!enable) { + for (i = 0; i < rdev->num_crtc; i++) { + if (rdev->mode_info.crtcs[i] && + rdev->mode_info.crtcs[i]->enabled && + i != crtc_id && + pll_id == rdev->mode_info.crtcs[i]->pll_id) { + /* one other crtc is using this pll don't turn + * off spread spectrum as it might turn off + * display on active crtc + */ + return; + } + } + } + + memset(&args, 0, sizeof(args)); + + if (ASIC_IS_DCE5(rdev)) { + args.v3.usSpreadSpectrumAmountFrac = cpu_to_le16(0); + args.v3.ucSpreadSpectrumType = ss->type & ATOM_SS_CENTRE_SPREAD_MODE_MASK; + switch (pll_id) { + case ATOM_PPLL1: + args.v3.ucSpreadSpectrumType |= ATOM_PPLL_SS_TYPE_V3_P1PLL; + break; + case ATOM_PPLL2: + args.v3.ucSpreadSpectrumType |= ATOM_PPLL_SS_TYPE_V3_P2PLL; + break; + case ATOM_DCPLL: + args.v3.ucSpreadSpectrumType |= ATOM_PPLL_SS_TYPE_V3_DCPLL; + break; + case ATOM_PPLL_INVALID: + return; + } + args.v3.usSpreadSpectrumAmount = cpu_to_le16(ss->amount); + args.v3.usSpreadSpectrumStep = cpu_to_le16(ss->step); + args.v3.ucEnable = enable; + if ((ss->percentage == 0) || (ss->type & ATOM_EXTERNAL_SS_MASK) || ASIC_IS_DCE61(rdev)) + args.v3.ucEnable = ATOM_DISABLE; + } else if (ASIC_IS_DCE4(rdev)) { + args.v2.usSpreadSpectrumPercentage = cpu_to_le16(ss->percentage); + args.v2.ucSpreadSpectrumType = ss->type & ATOM_SS_CENTRE_SPREAD_MODE_MASK; + switch (pll_id) { + case ATOM_PPLL1: + args.v2.ucSpreadSpectrumType |= ATOM_PPLL_SS_TYPE_V2_P1PLL; + break; + case ATOM_PPLL2: + args.v2.ucSpreadSpectrumType |= ATOM_PPLL_SS_TYPE_V2_P2PLL; + break; + case ATOM_DCPLL: + args.v2.ucSpreadSpectrumType |= ATOM_PPLL_SS_TYPE_V2_DCPLL; + break; + case ATOM_PPLL_INVALID: + return; + } + args.v2.usSpreadSpectrumAmount = cpu_to_le16(ss->amount); + args.v2.usSpreadSpectrumStep = cpu_to_le16(ss->step); + args.v2.ucEnable = enable; + if ((ss->percentage == 0) || (ss->type & ATOM_EXTERNAL_SS_MASK) || ASIC_IS_DCE41(rdev)) + args.v2.ucEnable = ATOM_DISABLE; + } else if (ASIC_IS_DCE3(rdev)) { + args.v1.usSpreadSpectrumPercentage = cpu_to_le16(ss->percentage); + args.v1.ucSpreadSpectrumType = ss->type & ATOM_SS_CENTRE_SPREAD_MODE_MASK; + args.v1.ucSpreadSpectrumStep = ss->step; + args.v1.ucSpreadSpectrumDelay = ss->delay; + args.v1.ucSpreadSpectrumRange = ss->range; + args.v1.ucPpll = pll_id; + args.v1.ucEnable = enable; + } else if (ASIC_IS_AVIVO(rdev)) { + if ((enable == ATOM_DISABLE) || (ss->percentage == 0) || + (ss->type & ATOM_EXTERNAL_SS_MASK)) { + atombios_disable_ss(rdev, pll_id); + return; + } + args.lvds_ss_2.usSpreadSpectrumPercentage = cpu_to_le16(ss->percentage); + args.lvds_ss_2.ucSpreadSpectrumType = ss->type & ATOM_SS_CENTRE_SPREAD_MODE_MASK; + args.lvds_ss_2.ucSpreadSpectrumStep = ss->step; + args.lvds_ss_2.ucSpreadSpectrumDelay = ss->delay; + args.lvds_ss_2.ucSpreadSpectrumRange = ss->range; + args.lvds_ss_2.ucEnable = enable; + } else { + if ((enable == ATOM_DISABLE) || (ss->percentage == 0) || + (ss->type & ATOM_EXTERNAL_SS_MASK)) { + atombios_disable_ss(rdev, pll_id); + return; + } + args.lvds_ss.usSpreadSpectrumPercentage = cpu_to_le16(ss->percentage); + args.lvds_ss.ucSpreadSpectrumType = ss->type & ATOM_SS_CENTRE_SPREAD_MODE_MASK; + args.lvds_ss.ucSpreadSpectrumStepSize_Delay = (ss->step & 3) << 2; + args.lvds_ss.ucSpreadSpectrumStepSize_Delay |= (ss->delay & 7) << 4; + args.lvds_ss.ucEnable = enable; + } + atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args); +} + +union adjust_pixel_clock { + ADJUST_DISPLAY_PLL_PS_ALLOCATION v1; + ADJUST_DISPLAY_PLL_PS_ALLOCATION_V3 v3; +}; + +static u32 atombios_adjust_pll(struct drm_crtc *crtc, + struct drm_display_mode *mode) +{ + struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); + struct drm_device *dev = crtc->dev; + struct radeon_device *rdev = dev->dev_private; + struct drm_encoder *encoder = radeon_crtc->encoder; + struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); + struct drm_connector *connector = radeon_get_connector_for_encoder(encoder); + u32 adjusted_clock = mode->clock; + int encoder_mode = atombios_get_encoder_mode(encoder); + u32 dp_clock = mode->clock; + int bpc = radeon_get_monitor_bpc(connector); + bool is_duallink = radeon_dig_monitor_is_duallink(encoder, mode->clock); + + /* reset the pll flags */ + radeon_crtc->pll_flags = 0; + + if (ASIC_IS_AVIVO(rdev)) { + if ((rdev->family == CHIP_RS600) || + (rdev->family == CHIP_RS690) || + (rdev->family == CHIP_RS740)) + radeon_crtc->pll_flags |= (/*RADEON_PLL_USE_FRAC_FB_DIV |*/ + RADEON_PLL_PREFER_CLOSEST_LOWER); + + if (ASIC_IS_DCE32(rdev) && mode->clock > 200000) /* range limits??? */ + radeon_crtc->pll_flags |= RADEON_PLL_PREFER_HIGH_FB_DIV; + else + radeon_crtc->pll_flags |= RADEON_PLL_PREFER_LOW_REF_DIV; + + if (rdev->family < CHIP_RV770) + radeon_crtc->pll_flags |= RADEON_PLL_PREFER_MINM_OVER_MAXP; + /* use frac fb div on APUs */ + if (ASIC_IS_DCE41(rdev) || ASIC_IS_DCE61(rdev)) + radeon_crtc->pll_flags |= RADEON_PLL_USE_FRAC_FB_DIV; + if (ASIC_IS_DCE32(rdev) && mode->clock > 165000) + radeon_crtc->pll_flags |= RADEON_PLL_USE_FRAC_FB_DIV; + } else { + radeon_crtc->pll_flags |= RADEON_PLL_LEGACY; + + if (mode->clock > 200000) /* range limits??? */ + radeon_crtc->pll_flags |= RADEON_PLL_PREFER_HIGH_FB_DIV; + else + radeon_crtc->pll_flags |= RADEON_PLL_PREFER_LOW_REF_DIV; + } + + if ((radeon_encoder->devices & (ATOM_DEVICE_LCD_SUPPORT | ATOM_DEVICE_DFP_SUPPORT)) || + (radeon_encoder_get_dp_bridge_encoder_id(encoder) != ENCODER_OBJECT_ID_NONE)) { + if (connector) { + struct radeon_connector *radeon_connector = to_radeon_connector(connector); + struct radeon_connector_atom_dig *dig_connector = + radeon_connector->con_priv; + + dp_clock = dig_connector->dp_clock; + } + } + + /* use recommended ref_div for ss */ + if (radeon_encoder->devices & (ATOM_DEVICE_LCD_SUPPORT)) { + if (radeon_crtc->ss_enabled) { + if (radeon_crtc->ss.refdiv) { + radeon_crtc->pll_flags |= RADEON_PLL_USE_REF_DIV; + radeon_crtc->pll_reference_div = radeon_crtc->ss.refdiv; + if (ASIC_IS_AVIVO(rdev)) + radeon_crtc->pll_flags |= RADEON_PLL_USE_FRAC_FB_DIV; + } + } + } + + if (ASIC_IS_AVIVO(rdev)) { + /* DVO wants 2x pixel clock if the DVO chip is in 12 bit mode */ + if (radeon_encoder->encoder_id == ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DVO1) + adjusted_clock = mode->clock * 2; + if (radeon_encoder->active_device & (ATOM_DEVICE_TV_SUPPORT)) + radeon_crtc->pll_flags |= RADEON_PLL_PREFER_CLOSEST_LOWER; + if (radeon_encoder->devices & (ATOM_DEVICE_LCD_SUPPORT)) + radeon_crtc->pll_flags |= RADEON_PLL_IS_LCD; + } else { + if (encoder->encoder_type != DRM_MODE_ENCODER_DAC) + radeon_crtc->pll_flags |= RADEON_PLL_NO_ODD_POST_DIV; + if (encoder->encoder_type == DRM_MODE_ENCODER_LVDS) + radeon_crtc->pll_flags |= RADEON_PLL_USE_REF_DIV; + } + + /* DCE3+ has an AdjustDisplayPll that will adjust the pixel clock + * accordingly based on the encoder/transmitter to work around + * special hw requirements. + */ + if (ASIC_IS_DCE3(rdev)) { + union adjust_pixel_clock args; + u8 frev, crev; + int index; + + index = GetIndexIntoMasterTable(COMMAND, AdjustDisplayPll); + if (!atom_parse_cmd_header(rdev->mode_info.atom_context, index, &frev, + &crev)) + return adjusted_clock; + + memset(&args, 0, sizeof(args)); + + switch (frev) { + case 1: + switch (crev) { + case 1: + case 2: + args.v1.usPixelClock = cpu_to_le16(mode->clock / 10); + args.v1.ucTransmitterID = radeon_encoder->encoder_id; + args.v1.ucEncodeMode = encoder_mode; + if (radeon_crtc->ss_enabled && radeon_crtc->ss.percentage) + args.v1.ucConfig |= + ADJUST_DISPLAY_CONFIG_SS_ENABLE; + + atom_execute_table(rdev->mode_info.atom_context, + index, (uint32_t *)&args); + adjusted_clock = le16_to_cpu(args.v1.usPixelClock) * 10; + break; + case 3: + args.v3.sInput.usPixelClock = cpu_to_le16(mode->clock / 10); + args.v3.sInput.ucTransmitterID = radeon_encoder->encoder_id; + args.v3.sInput.ucEncodeMode = encoder_mode; + args.v3.sInput.ucDispPllConfig = 0; + if (radeon_crtc->ss_enabled && radeon_crtc->ss.percentage) + args.v3.sInput.ucDispPllConfig |= + DISPPLL_CONFIG_SS_ENABLE; + if (ENCODER_MODE_IS_DP(encoder_mode)) { + args.v3.sInput.ucDispPllConfig |= + DISPPLL_CONFIG_COHERENT_MODE; + /* 16200 or 27000 */ + args.v3.sInput.usPixelClock = cpu_to_le16(dp_clock / 10); + } else if (radeon_encoder->devices & (ATOM_DEVICE_DFP_SUPPORT)) { + struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv; + if (encoder_mode == ATOM_ENCODER_MODE_HDMI) + /* deep color support */ + args.v3.sInput.usPixelClock = + cpu_to_le16((mode->clock * bpc / 8) / 10); + if (dig->coherent_mode) + args.v3.sInput.ucDispPllConfig |= + DISPPLL_CONFIG_COHERENT_MODE; + if (is_duallink) + args.v3.sInput.ucDispPllConfig |= + DISPPLL_CONFIG_DUAL_LINK; + } + if (radeon_encoder_get_dp_bridge_encoder_id(encoder) != + ENCODER_OBJECT_ID_NONE) + args.v3.sInput.ucExtTransmitterID = + radeon_encoder_get_dp_bridge_encoder_id(encoder); + else + args.v3.sInput.ucExtTransmitterID = 0; + + atom_execute_table(rdev->mode_info.atom_context, + index, (uint32_t *)&args); + adjusted_clock = le32_to_cpu(args.v3.sOutput.ulDispPllFreq) * 10; + if (args.v3.sOutput.ucRefDiv) { + radeon_crtc->pll_flags |= RADEON_PLL_USE_FRAC_FB_DIV; + radeon_crtc->pll_flags |= RADEON_PLL_USE_REF_DIV; + radeon_crtc->pll_reference_div = args.v3.sOutput.ucRefDiv; + } + if (args.v3.sOutput.ucPostDiv) { + radeon_crtc->pll_flags |= RADEON_PLL_USE_FRAC_FB_DIV; + radeon_crtc->pll_flags |= RADEON_PLL_USE_POST_DIV; + radeon_crtc->pll_post_div = args.v3.sOutput.ucPostDiv; + } + break; + default: + DRM_ERROR("Unknown table version %d %d\n", frev, crev); + return adjusted_clock; + } + break; + default: + DRM_ERROR("Unknown table version %d %d\n", frev, crev); + return adjusted_clock; + } + } + return adjusted_clock; +} + +union set_pixel_clock { + SET_PIXEL_CLOCK_PS_ALLOCATION base; + PIXEL_CLOCK_PARAMETERS v1; + PIXEL_CLOCK_PARAMETERS_V2 v2; + PIXEL_CLOCK_PARAMETERS_V3 v3; + PIXEL_CLOCK_PARAMETERS_V5 v5; + PIXEL_CLOCK_PARAMETERS_V6 v6; +}; + +/* on DCE5, make sure the voltage is high enough to support the + * required disp clk. + */ +static void atombios_crtc_set_disp_eng_pll(struct radeon_device *rdev, + u32 dispclk) +{ + u8 frev, crev; + int index; + union set_pixel_clock args; + + memset(&args, 0, sizeof(args)); + + index = GetIndexIntoMasterTable(COMMAND, SetPixelClock); + if (!atom_parse_cmd_header(rdev->mode_info.atom_context, index, &frev, + &crev)) + return; + + switch (frev) { + case 1: + switch (crev) { + case 5: + /* if the default dcpll clock is specified, + * SetPixelClock provides the dividers + */ + args.v5.ucCRTC = ATOM_CRTC_INVALID; + args.v5.usPixelClock = cpu_to_le16(dispclk); + args.v5.ucPpll = ATOM_DCPLL; + break; + case 6: + /* if the default dcpll clock is specified, + * SetPixelClock provides the dividers + */ + args.v6.ulDispEngClkFreq = cpu_to_le32(dispclk); + if (ASIC_IS_DCE61(rdev)) + args.v6.ucPpll = ATOM_EXT_PLL1; + else if (ASIC_IS_DCE6(rdev)) + args.v6.ucPpll = ATOM_PPLL0; + else + args.v6.ucPpll = ATOM_DCPLL; + break; + default: + DRM_ERROR("Unknown table version %d %d\n", frev, crev); + return; + } + break; + default: + DRM_ERROR("Unknown table version %d %d\n", frev, crev); + return; + } + atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args); +} + +static void atombios_crtc_program_pll(struct drm_crtc *crtc, + u32 crtc_id, + int pll_id, + u32 encoder_mode, + u32 encoder_id, + u32 clock, + u32 ref_div, + u32 fb_div, + u32 frac_fb_div, + u32 post_div, + int bpc, + bool ss_enabled, + struct radeon_atom_ss *ss) +{ + struct drm_device *dev = crtc->dev; + struct radeon_device *rdev = dev->dev_private; + u8 frev, crev; + int index = GetIndexIntoMasterTable(COMMAND, SetPixelClock); + union set_pixel_clock args; + + memset(&args, 0, sizeof(args)); + + if (!atom_parse_cmd_header(rdev->mode_info.atom_context, index, &frev, + &crev)) + return; + + switch (frev) { + case 1: + switch (crev) { + case 1: + if (clock == ATOM_DISABLE) + return; + args.v1.usPixelClock = cpu_to_le16(clock / 10); + args.v1.usRefDiv = cpu_to_le16(ref_div); + args.v1.usFbDiv = cpu_to_le16(fb_div); + args.v1.ucFracFbDiv = frac_fb_div; + args.v1.ucPostDiv = post_div; + args.v1.ucPpll = pll_id; + args.v1.ucCRTC = crtc_id; + args.v1.ucRefDivSrc = 1; + break; + case 2: + args.v2.usPixelClock = cpu_to_le16(clock / 10); + args.v2.usRefDiv = cpu_to_le16(ref_div); + args.v2.usFbDiv = cpu_to_le16(fb_div); + args.v2.ucFracFbDiv = frac_fb_div; + args.v2.ucPostDiv = post_div; + args.v2.ucPpll = pll_id; + args.v2.ucCRTC = crtc_id; + args.v2.ucRefDivSrc = 1; + break; + case 3: + args.v3.usPixelClock = cpu_to_le16(clock / 10); + args.v3.usRefDiv = cpu_to_le16(ref_div); + args.v3.usFbDiv = cpu_to_le16(fb_div); + args.v3.ucFracFbDiv = frac_fb_div; + args.v3.ucPostDiv = post_div; + args.v3.ucPpll = pll_id; + if (crtc_id == ATOM_CRTC2) + args.v3.ucMiscInfo = PIXEL_CLOCK_MISC_CRTC_SEL_CRTC2; + else + args.v3.ucMiscInfo = PIXEL_CLOCK_MISC_CRTC_SEL_CRTC1; + if (ss_enabled && (ss->type & ATOM_EXTERNAL_SS_MASK)) + args.v3.ucMiscInfo |= PIXEL_CLOCK_MISC_REF_DIV_SRC; + args.v3.ucTransmitterId = encoder_id; + args.v3.ucEncoderMode = encoder_mode; + break; + case 5: + args.v5.ucCRTC = crtc_id; + args.v5.usPixelClock = cpu_to_le16(clock / 10); + args.v5.ucRefDiv = ref_div; + args.v5.usFbDiv = cpu_to_le16(fb_div); + args.v5.ulFbDivDecFrac = cpu_to_le32(frac_fb_div * 100000); + args.v5.ucPostDiv = post_div; + args.v5.ucMiscInfo = 0; /* HDMI depth, etc. */ + if (ss_enabled && (ss->type & ATOM_EXTERNAL_SS_MASK)) + args.v5.ucMiscInfo |= PIXEL_CLOCK_V5_MISC_REF_DIV_SRC; + switch (bpc) { + case 8: + default: + args.v5.ucMiscInfo |= PIXEL_CLOCK_V5_MISC_HDMI_24BPP; + break; + case 10: + args.v5.ucMiscInfo |= PIXEL_CLOCK_V5_MISC_HDMI_30BPP; + break; + } + args.v5.ucTransmitterID = encoder_id; + args.v5.ucEncoderMode = encoder_mode; + args.v5.ucPpll = pll_id; + break; + case 6: + args.v6.ulDispEngClkFreq = cpu_to_le32(crtc_id << 24 | clock / 10); + args.v6.ucRefDiv = ref_div; + args.v6.usFbDiv = cpu_to_le16(fb_div); + args.v6.ulFbDivDecFrac = cpu_to_le32(frac_fb_div * 100000); + args.v6.ucPostDiv = post_div; + args.v6.ucMiscInfo = 0; /* HDMI depth, etc. */ + if (ss_enabled && (ss->type & ATOM_EXTERNAL_SS_MASK)) + args.v6.ucMiscInfo |= PIXEL_CLOCK_V6_MISC_REF_DIV_SRC; + switch (bpc) { + case 8: + default: + args.v6.ucMiscInfo |= PIXEL_CLOCK_V6_MISC_HDMI_24BPP; + break; + case 10: + args.v6.ucMiscInfo |= PIXEL_CLOCK_V6_MISC_HDMI_30BPP; + break; + case 12: + args.v6.ucMiscInfo |= PIXEL_CLOCK_V6_MISC_HDMI_36BPP; + break; + case 16: + args.v6.ucMiscInfo |= PIXEL_CLOCK_V6_MISC_HDMI_48BPP; + break; + } + args.v6.ucTransmitterID = encoder_id; + args.v6.ucEncoderMode = encoder_mode; + args.v6.ucPpll = pll_id; + break; + default: + DRM_ERROR("Unknown table version %d %d\n", frev, crev); + return; + } + break; + default: + DRM_ERROR("Unknown table version %d %d\n", frev, crev); + return; + } + + atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args); +} + +static bool atombios_crtc_prepare_pll(struct drm_crtc *crtc, struct drm_display_mode *mode) +{ + struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); + struct drm_device *dev = crtc->dev; + struct radeon_device *rdev = dev->dev_private; + struct radeon_encoder *radeon_encoder = + to_radeon_encoder(radeon_crtc->encoder); + int encoder_mode = atombios_get_encoder_mode(radeon_crtc->encoder); + + radeon_crtc->bpc = 8; + radeon_crtc->ss_enabled = false; + + if ((radeon_encoder->active_device & (ATOM_DEVICE_LCD_SUPPORT | ATOM_DEVICE_DFP_SUPPORT)) || + (radeon_encoder_get_dp_bridge_encoder_id(radeon_crtc->encoder) != ENCODER_OBJECT_ID_NONE)) { + struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv; + struct drm_connector *connector = + radeon_get_connector_for_encoder(radeon_crtc->encoder); + struct radeon_connector *radeon_connector = + to_radeon_connector(connector); + struct radeon_connector_atom_dig *dig_connector = + radeon_connector->con_priv; + int dp_clock; + radeon_crtc->bpc = radeon_get_monitor_bpc(connector); + + switch (encoder_mode) { + case ATOM_ENCODER_MODE_DP_MST: + case ATOM_ENCODER_MODE_DP: + /* DP/eDP */ + dp_clock = dig_connector->dp_clock / 10; + if (ASIC_IS_DCE4(rdev)) + radeon_crtc->ss_enabled = + radeon_atombios_get_asic_ss_info(rdev, &radeon_crtc->ss, + ASIC_INTERNAL_SS_ON_DP, + dp_clock); + else { + if (dp_clock == 16200) { + radeon_crtc->ss_enabled = + radeon_atombios_get_ppll_ss_info(rdev, + &radeon_crtc->ss, + ATOM_DP_SS_ID2); + if (!radeon_crtc->ss_enabled) + radeon_crtc->ss_enabled = + radeon_atombios_get_ppll_ss_info(rdev, + &radeon_crtc->ss, + ATOM_DP_SS_ID1); + } else + radeon_crtc->ss_enabled = + radeon_atombios_get_ppll_ss_info(rdev, + &radeon_crtc->ss, + ATOM_DP_SS_ID1); + } + break; + case ATOM_ENCODER_MODE_LVDS: + if (ASIC_IS_DCE4(rdev)) + radeon_crtc->ss_enabled = + radeon_atombios_get_asic_ss_info(rdev, + &radeon_crtc->ss, + dig->lcd_ss_id, + mode->clock / 10); + else + radeon_crtc->ss_enabled = + radeon_atombios_get_ppll_ss_info(rdev, + &radeon_crtc->ss, + dig->lcd_ss_id); + break; + case ATOM_ENCODER_MODE_DVI: + if (ASIC_IS_DCE4(rdev)) + radeon_crtc->ss_enabled = + radeon_atombios_get_asic_ss_info(rdev, + &radeon_crtc->ss, + ASIC_INTERNAL_SS_ON_TMDS, + mode->clock / 10); + break; + case ATOM_ENCODER_MODE_HDMI: + if (ASIC_IS_DCE4(rdev)) + radeon_crtc->ss_enabled = + radeon_atombios_get_asic_ss_info(rdev, + &radeon_crtc->ss, + ASIC_INTERNAL_SS_ON_HDMI, + mode->clock / 10); + break; + default: + break; + } + } + + /* adjust pixel clock as needed */ + radeon_crtc->adjusted_clock = atombios_adjust_pll(crtc, mode); + + return true; +} + +static void atombios_crtc_set_pll(struct drm_crtc *crtc, struct drm_display_mode *mode) +{ + struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); + struct drm_device *dev = crtc->dev; + struct radeon_device *rdev = dev->dev_private; + struct radeon_encoder *radeon_encoder = + to_radeon_encoder(radeon_crtc->encoder); + u32 pll_clock = mode->clock; + u32 ref_div = 0, fb_div = 0, frac_fb_div = 0, post_div = 0; + struct radeon_pll *pll; + int encoder_mode = atombios_get_encoder_mode(radeon_crtc->encoder); + + switch (radeon_crtc->pll_id) { + case ATOM_PPLL1: + pll = &rdev->clock.p1pll; + break; + case ATOM_PPLL2: + pll = &rdev->clock.p2pll; + break; + case ATOM_DCPLL: + case ATOM_PPLL_INVALID: + default: + pll = &rdev->clock.dcpll; + break; + } + + /* update pll params */ + pll->flags = radeon_crtc->pll_flags; + pll->reference_div = radeon_crtc->pll_reference_div; + pll->post_div = radeon_crtc->pll_post_div; + + if (radeon_encoder->active_device & (ATOM_DEVICE_TV_SUPPORT)) + /* TV seems to prefer the legacy algo on some boards */ + radeon_compute_pll_legacy(pll, radeon_crtc->adjusted_clock, &pll_clock, + &fb_div, &frac_fb_div, &ref_div, &post_div); + else if (ASIC_IS_AVIVO(rdev)) + radeon_compute_pll_avivo(pll, radeon_crtc->adjusted_clock, &pll_clock, + &fb_div, &frac_fb_div, &ref_div, &post_div); + else + radeon_compute_pll_legacy(pll, radeon_crtc->adjusted_clock, &pll_clock, + &fb_div, &frac_fb_div, &ref_div, &post_div); + + atombios_crtc_program_ss(rdev, ATOM_DISABLE, radeon_crtc->pll_id, + radeon_crtc->crtc_id, &radeon_crtc->ss); + + atombios_crtc_program_pll(crtc, radeon_crtc->crtc_id, radeon_crtc->pll_id, + encoder_mode, radeon_encoder->encoder_id, mode->clock, + ref_div, fb_div, frac_fb_div, post_div, + radeon_crtc->bpc, radeon_crtc->ss_enabled, &radeon_crtc->ss); + + if (radeon_crtc->ss_enabled) { + /* calculate ss amount and step size */ + if (ASIC_IS_DCE4(rdev)) { + u32 step_size; + u32 amount = (((fb_div * 10) + frac_fb_div) * radeon_crtc->ss.percentage) / 10000; + radeon_crtc->ss.amount = (amount / 10) & ATOM_PPLL_SS_AMOUNT_V2_FBDIV_MASK; + radeon_crtc->ss.amount |= ((amount - (amount / 10)) << ATOM_PPLL_SS_AMOUNT_V2_NFRAC_SHIFT) & + ATOM_PPLL_SS_AMOUNT_V2_NFRAC_MASK; + if (radeon_crtc->ss.type & ATOM_PPLL_SS_TYPE_V2_CENTRE_SPREAD) + step_size = (4 * amount * ref_div * (radeon_crtc->ss.rate * 2048)) / + (125 * 25 * pll->reference_freq / 100); + else + step_size = (2 * amount * ref_div * (radeon_crtc->ss.rate * 2048)) / + (125 * 25 * pll->reference_freq / 100); + radeon_crtc->ss.step = step_size; + } + + atombios_crtc_program_ss(rdev, ATOM_ENABLE, radeon_crtc->pll_id, + radeon_crtc->crtc_id, &radeon_crtc->ss); + } +} + +static int dce4_crtc_do_set_base(struct drm_crtc *crtc, + struct drm_framebuffer *fb, + int x, int y, int atomic) +{ + struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); + struct drm_device *dev = crtc->dev; + struct radeon_device *rdev = dev->dev_private; + struct radeon_framebuffer *radeon_fb; + struct drm_framebuffer *target_fb; + struct drm_gem_object *obj; + struct radeon_bo *rbo; + uint64_t fb_location; + uint32_t fb_format, fb_pitch_pixels, tiling_flags; + unsigned bankw, bankh, mtaspect, tile_split; + u32 fb_swap = EVERGREEN_GRPH_ENDIAN_SWAP(EVERGREEN_GRPH_ENDIAN_NONE); + u32 tmp, viewport_w, viewport_h; + int r; + + /* no fb bound */ + if (!atomic && !crtc->fb) { + DRM_DEBUG_KMS("No FB bound\n"); + return 0; + } + + if (atomic) { + radeon_fb = to_radeon_framebuffer(fb); + target_fb = fb; + } + else { + radeon_fb = to_radeon_framebuffer(crtc->fb); + target_fb = crtc->fb; + } + + /* If atomic, assume fb object is pinned & idle & fenced and + * just update base pointers + */ + obj = radeon_fb->obj; + rbo = gem_to_radeon_bo(obj); + r = radeon_bo_reserve(rbo, false); + if (unlikely(r != 0)) + return r; + + if (atomic) + fb_location = radeon_bo_gpu_offset(rbo); + else { + r = radeon_bo_pin(rbo, RADEON_GEM_DOMAIN_VRAM, &fb_location); + if (unlikely(r != 0)) { + radeon_bo_unreserve(rbo); + return -EINVAL; + } + } + + radeon_bo_get_tiling_flags(rbo, &tiling_flags, NULL); + radeon_bo_unreserve(rbo); + + switch (target_fb->bits_per_pixel) { + case 8: + fb_format = (EVERGREEN_GRPH_DEPTH(EVERGREEN_GRPH_DEPTH_8BPP) | + EVERGREEN_GRPH_FORMAT(EVERGREEN_GRPH_FORMAT_INDEXED)); + break; + case 15: + fb_format = (EVERGREEN_GRPH_DEPTH(EVERGREEN_GRPH_DEPTH_16BPP) | + EVERGREEN_GRPH_FORMAT(EVERGREEN_GRPH_FORMAT_ARGB1555)); + break; + case 16: + fb_format = (EVERGREEN_GRPH_DEPTH(EVERGREEN_GRPH_DEPTH_16BPP) | + EVERGREEN_GRPH_FORMAT(EVERGREEN_GRPH_FORMAT_ARGB565)); +#ifdef __BIG_ENDIAN + fb_swap = EVERGREEN_GRPH_ENDIAN_SWAP(EVERGREEN_GRPH_ENDIAN_8IN16); +#endif + break; + case 24: + case 32: + fb_format = (EVERGREEN_GRPH_DEPTH(EVERGREEN_GRPH_DEPTH_32BPP) | + EVERGREEN_GRPH_FORMAT(EVERGREEN_GRPH_FORMAT_ARGB8888)); +#ifdef __BIG_ENDIAN + fb_swap = EVERGREEN_GRPH_ENDIAN_SWAP(EVERGREEN_GRPH_ENDIAN_8IN32); +#endif + break; + default: + DRM_ERROR("Unsupported screen depth %d\n", + target_fb->bits_per_pixel); + return -EINVAL; + } + + if (tiling_flags & RADEON_TILING_MACRO) { + if (rdev->family >= CHIP_TAHITI) + tmp = rdev->config.si.tile_config; + else if (rdev->family >= CHIP_CAYMAN) + tmp = rdev->config.cayman.tile_config; + else + tmp = rdev->config.evergreen.tile_config; + + switch ((tmp & 0xf0) >> 4) { + case 0: /* 4 banks */ + fb_format |= EVERGREEN_GRPH_NUM_BANKS(EVERGREEN_ADDR_SURF_4_BANK); + break; + case 1: /* 8 banks */ + default: + fb_format |= EVERGREEN_GRPH_NUM_BANKS(EVERGREEN_ADDR_SURF_8_BANK); + break; + case 2: /* 16 banks */ + fb_format |= EVERGREEN_GRPH_NUM_BANKS(EVERGREEN_ADDR_SURF_16_BANK); + break; + } + + fb_format |= EVERGREEN_GRPH_ARRAY_MODE(EVERGREEN_GRPH_ARRAY_2D_TILED_THIN1); + + evergreen_tiling_fields(tiling_flags, &bankw, &bankh, &mtaspect, &tile_split); + fb_format |= EVERGREEN_GRPH_TILE_SPLIT(tile_split); + fb_format |= EVERGREEN_GRPH_BANK_WIDTH(bankw); + fb_format |= EVERGREEN_GRPH_BANK_HEIGHT(bankh); + fb_format |= EVERGREEN_GRPH_MACRO_TILE_ASPECT(mtaspect); + } else if (tiling_flags & RADEON_TILING_MICRO) + fb_format |= EVERGREEN_GRPH_ARRAY_MODE(EVERGREEN_GRPH_ARRAY_1D_TILED_THIN1); + + if ((rdev->family == CHIP_TAHITI) || + (rdev->family == CHIP_PITCAIRN)) + fb_format |= SI_GRPH_PIPE_CONFIG(SI_ADDR_SURF_P8_32x32_8x16); + else if (rdev->family == CHIP_VERDE) + fb_format |= SI_GRPH_PIPE_CONFIG(SI_ADDR_SURF_P4_8x16); + + switch (radeon_crtc->crtc_id) { + case 0: + WREG32(AVIVO_D1VGA_CONTROL, 0); + break; + case 1: + WREG32(AVIVO_D2VGA_CONTROL, 0); + break; + case 2: + WREG32(EVERGREEN_D3VGA_CONTROL, 0); + break; + case 3: + WREG32(EVERGREEN_D4VGA_CONTROL, 0); + break; + case 4: + WREG32(EVERGREEN_D5VGA_CONTROL, 0); + break; + case 5: + WREG32(EVERGREEN_D6VGA_CONTROL, 0); + break; + default: + break; + } + + WREG32(EVERGREEN_GRPH_PRIMARY_SURFACE_ADDRESS_HIGH + radeon_crtc->crtc_offset, + upper_32_bits(fb_location)); + WREG32(EVERGREEN_GRPH_SECONDARY_SURFACE_ADDRESS_HIGH + radeon_crtc->crtc_offset, + upper_32_bits(fb_location)); + WREG32(EVERGREEN_GRPH_PRIMARY_SURFACE_ADDRESS + radeon_crtc->crtc_offset, + (u32)fb_location & EVERGREEN_GRPH_SURFACE_ADDRESS_MASK); + WREG32(EVERGREEN_GRPH_SECONDARY_SURFACE_ADDRESS + radeon_crtc->crtc_offset, + (u32) fb_location & EVERGREEN_GRPH_SURFACE_ADDRESS_MASK); + WREG32(EVERGREEN_GRPH_CONTROL + radeon_crtc->crtc_offset, fb_format); + WREG32(EVERGREEN_GRPH_SWAP_CONTROL + radeon_crtc->crtc_offset, fb_swap); + + WREG32(EVERGREEN_GRPH_SURFACE_OFFSET_X + radeon_crtc->crtc_offset, 0); + WREG32(EVERGREEN_GRPH_SURFACE_OFFSET_Y + radeon_crtc->crtc_offset, 0); + WREG32(EVERGREEN_GRPH_X_START + radeon_crtc->crtc_offset, 0); + WREG32(EVERGREEN_GRPH_Y_START + radeon_crtc->crtc_offset, 0); + WREG32(EVERGREEN_GRPH_X_END + radeon_crtc->crtc_offset, target_fb->width); + WREG32(EVERGREEN_GRPH_Y_END + radeon_crtc->crtc_offset, target_fb->height); + + fb_pitch_pixels = target_fb->pitches[0] / (target_fb->bits_per_pixel / 8); + WREG32(EVERGREEN_GRPH_PITCH + radeon_crtc->crtc_offset, fb_pitch_pixels); + WREG32(EVERGREEN_GRPH_ENABLE + radeon_crtc->crtc_offset, 1); + + WREG32(EVERGREEN_DESKTOP_HEIGHT + radeon_crtc->crtc_offset, + target_fb->height); + x &= ~3; + y &= ~1; + WREG32(EVERGREEN_VIEWPORT_START + radeon_crtc->crtc_offset, + (x << 16) | y); + viewport_w = crtc->mode.hdisplay; + viewport_h = (crtc->mode.vdisplay + 1) & ~1; + WREG32(EVERGREEN_VIEWPORT_SIZE + radeon_crtc->crtc_offset, + (viewport_w << 16) | viewport_h); + + /* pageflip setup */ + /* make sure flip is at vb rather than hb */ + tmp = RREG32(EVERGREEN_GRPH_FLIP_CONTROL + radeon_crtc->crtc_offset); + tmp &= ~EVERGREEN_GRPH_SURFACE_UPDATE_H_RETRACE_EN; + WREG32(EVERGREEN_GRPH_FLIP_CONTROL + radeon_crtc->crtc_offset, tmp); + + /* set pageflip to happen anywhere in vblank interval */ + WREG32(EVERGREEN_MASTER_UPDATE_MODE + radeon_crtc->crtc_offset, 0); + + if (!atomic && fb && fb != crtc->fb) { + radeon_fb = to_radeon_framebuffer(fb); + rbo = gem_to_radeon_bo(radeon_fb->obj); + r = radeon_bo_reserve(rbo, false); + if (unlikely(r != 0)) + return r; + radeon_bo_unpin(rbo); + radeon_bo_unreserve(rbo); + } + + /* Bytes per pixel may have changed */ + radeon_bandwidth_update(rdev); + + return 0; +} + +static int avivo_crtc_do_set_base(struct drm_crtc *crtc, + struct drm_framebuffer *fb, + int x, int y, int atomic) +{ + struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); + struct drm_device *dev = crtc->dev; + struct radeon_device *rdev = dev->dev_private; + struct radeon_framebuffer *radeon_fb; + struct drm_gem_object *obj; + struct radeon_bo *rbo; + struct drm_framebuffer *target_fb; + uint64_t fb_location; + uint32_t fb_format, fb_pitch_pixels, tiling_flags; + u32 fb_swap = R600_D1GRPH_SWAP_ENDIAN_NONE; + u32 tmp, viewport_w, viewport_h; + int r; + + /* no fb bound */ + if (!atomic && !crtc->fb) { + DRM_DEBUG_KMS("No FB bound\n"); + return 0; + } + + if (atomic) { + radeon_fb = to_radeon_framebuffer(fb); + target_fb = fb; + } + else { + radeon_fb = to_radeon_framebuffer(crtc->fb); + target_fb = crtc->fb; + } + + obj = radeon_fb->obj; + rbo = gem_to_radeon_bo(obj); + r = radeon_bo_reserve(rbo, false); + if (unlikely(r != 0)) + return r; + + /* If atomic, assume fb object is pinned & idle & fenced and + * just update base pointers + */ + if (atomic) + fb_location = radeon_bo_gpu_offset(rbo); + else { + r = radeon_bo_pin(rbo, RADEON_GEM_DOMAIN_VRAM, &fb_location); + if (unlikely(r != 0)) { + radeon_bo_unreserve(rbo); + return -EINVAL; + } + } + radeon_bo_get_tiling_flags(rbo, &tiling_flags, NULL); + radeon_bo_unreserve(rbo); + + switch (target_fb->bits_per_pixel) { + case 8: + fb_format = + AVIVO_D1GRPH_CONTROL_DEPTH_8BPP | + AVIVO_D1GRPH_CONTROL_8BPP_INDEXED; + break; + case 15: + fb_format = + AVIVO_D1GRPH_CONTROL_DEPTH_16BPP | + AVIVO_D1GRPH_CONTROL_16BPP_ARGB1555; + break; + case 16: + fb_format = + AVIVO_D1GRPH_CONTROL_DEPTH_16BPP | + AVIVO_D1GRPH_CONTROL_16BPP_RGB565; +#ifdef __BIG_ENDIAN + fb_swap = R600_D1GRPH_SWAP_ENDIAN_16BIT; +#endif + break; + case 24: + case 32: + fb_format = + AVIVO_D1GRPH_CONTROL_DEPTH_32BPP | + AVIVO_D1GRPH_CONTROL_32BPP_ARGB8888; +#ifdef __BIG_ENDIAN + fb_swap = R600_D1GRPH_SWAP_ENDIAN_32BIT; +#endif + break; + default: + DRM_ERROR("Unsupported screen depth %d\n", + target_fb->bits_per_pixel); + return -EINVAL; + } + + if (rdev->family >= CHIP_R600) { + if (tiling_flags & RADEON_TILING_MACRO) + fb_format |= R600_D1GRPH_ARRAY_MODE_2D_TILED_THIN1; + else if (tiling_flags & RADEON_TILING_MICRO) + fb_format |= R600_D1GRPH_ARRAY_MODE_1D_TILED_THIN1; + } else { + if (tiling_flags & RADEON_TILING_MACRO) + fb_format |= AVIVO_D1GRPH_MACRO_ADDRESS_MODE; + + if (tiling_flags & RADEON_TILING_MICRO) + fb_format |= AVIVO_D1GRPH_TILED; + } + + if (radeon_crtc->crtc_id == 0) + WREG32(AVIVO_D1VGA_CONTROL, 0); + else + WREG32(AVIVO_D2VGA_CONTROL, 0); + + if (rdev->family >= CHIP_RV770) { + if (radeon_crtc->crtc_id) { + WREG32(R700_D2GRPH_PRIMARY_SURFACE_ADDRESS_HIGH, upper_32_bits(fb_location)); + WREG32(R700_D2GRPH_SECONDARY_SURFACE_ADDRESS_HIGH, upper_32_bits(fb_location)); + } else { + WREG32(R700_D1GRPH_PRIMARY_SURFACE_ADDRESS_HIGH, upper_32_bits(fb_location)); + WREG32(R700_D1GRPH_SECONDARY_SURFACE_ADDRESS_HIGH, upper_32_bits(fb_location)); + } + } + WREG32(AVIVO_D1GRPH_PRIMARY_SURFACE_ADDRESS + radeon_crtc->crtc_offset, + (u32) fb_location); + WREG32(AVIVO_D1GRPH_SECONDARY_SURFACE_ADDRESS + + radeon_crtc->crtc_offset, (u32) fb_location); + WREG32(AVIVO_D1GRPH_CONTROL + radeon_crtc->crtc_offset, fb_format); + if (rdev->family >= CHIP_R600) + WREG32(R600_D1GRPH_SWAP_CONTROL + radeon_crtc->crtc_offset, fb_swap); + + WREG32(AVIVO_D1GRPH_SURFACE_OFFSET_X + radeon_crtc->crtc_offset, 0); + WREG32(AVIVO_D1GRPH_SURFACE_OFFSET_Y + radeon_crtc->crtc_offset, 0); + WREG32(AVIVO_D1GRPH_X_START + radeon_crtc->crtc_offset, 0); + WREG32(AVIVO_D1GRPH_Y_START + radeon_crtc->crtc_offset, 0); + WREG32(AVIVO_D1GRPH_X_END + radeon_crtc->crtc_offset, target_fb->width); + WREG32(AVIVO_D1GRPH_Y_END + radeon_crtc->crtc_offset, target_fb->height); + + fb_pitch_pixels = target_fb->pitches[0] / (target_fb->bits_per_pixel / 8); + WREG32(AVIVO_D1GRPH_PITCH + radeon_crtc->crtc_offset, fb_pitch_pixels); + WREG32(AVIVO_D1GRPH_ENABLE + radeon_crtc->crtc_offset, 1); + + WREG32(AVIVO_D1MODE_DESKTOP_HEIGHT + radeon_crtc->crtc_offset, + target_fb->height); + x &= ~3; + y &= ~1; + WREG32(AVIVO_D1MODE_VIEWPORT_START + radeon_crtc->crtc_offset, + (x << 16) | y); + viewport_w = crtc->mode.hdisplay; + viewport_h = (crtc->mode.vdisplay + 1) & ~1; + WREG32(AVIVO_D1MODE_VIEWPORT_SIZE + radeon_crtc->crtc_offset, + (viewport_w << 16) | viewport_h); + + /* pageflip setup */ + /* make sure flip is at vb rather than hb */ + tmp = RREG32(AVIVO_D1GRPH_FLIP_CONTROL + radeon_crtc->crtc_offset); + tmp &= ~AVIVO_D1GRPH_SURFACE_UPDATE_H_RETRACE_EN; + WREG32(AVIVO_D1GRPH_FLIP_CONTROL + radeon_crtc->crtc_offset, tmp); + + /* set pageflip to happen anywhere in vblank interval */ + WREG32(AVIVO_D1MODE_MASTER_UPDATE_MODE + radeon_crtc->crtc_offset, 0); + + if (!atomic && fb && fb != crtc->fb) { + radeon_fb = to_radeon_framebuffer(fb); + rbo = gem_to_radeon_bo(radeon_fb->obj); + r = radeon_bo_reserve(rbo, false); + if (unlikely(r != 0)) + return r; + radeon_bo_unpin(rbo); + radeon_bo_unreserve(rbo); + } + + /* Bytes per pixel may have changed */ + radeon_bandwidth_update(rdev); + + return 0; +} + +int atombios_crtc_set_base(struct drm_crtc *crtc, int x, int y, + struct drm_framebuffer *old_fb) +{ + struct drm_device *dev = crtc->dev; + struct radeon_device *rdev = dev->dev_private; + + if (ASIC_IS_DCE4(rdev)) + return dce4_crtc_do_set_base(crtc, old_fb, x, y, 0); + else if (ASIC_IS_AVIVO(rdev)) + return avivo_crtc_do_set_base(crtc, old_fb, x, y, 0); + else + return radeon_crtc_do_set_base(crtc, old_fb, x, y, 0); +} + +int atombios_crtc_set_base_atomic(struct drm_crtc *crtc, + struct drm_framebuffer *fb, + int x, int y, enum mode_set_atomic state) +{ + struct drm_device *dev = crtc->dev; + struct radeon_device *rdev = dev->dev_private; + + if (ASIC_IS_DCE4(rdev)) + return dce4_crtc_do_set_base(crtc, fb, x, y, 1); + else if (ASIC_IS_AVIVO(rdev)) + return avivo_crtc_do_set_base(crtc, fb, x, y, 1); + else + return radeon_crtc_do_set_base(crtc, fb, x, y, 1); +} + +/* properly set additional regs when using atombios */ +static void radeon_legacy_atom_fixup(struct drm_crtc *crtc) +{ + struct drm_device *dev = crtc->dev; + struct radeon_device *rdev = dev->dev_private; + struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); + u32 disp_merge_cntl; + + switch (radeon_crtc->crtc_id) { + case 0: + disp_merge_cntl = RREG32(RADEON_DISP_MERGE_CNTL); + disp_merge_cntl &= ~RADEON_DISP_RGB_OFFSET_EN; + WREG32(RADEON_DISP_MERGE_CNTL, disp_merge_cntl); + break; + case 1: + disp_merge_cntl = RREG32(RADEON_DISP2_MERGE_CNTL); + disp_merge_cntl &= ~RADEON_DISP2_RGB_OFFSET_EN; + WREG32(RADEON_DISP2_MERGE_CNTL, disp_merge_cntl); + WREG32(RADEON_FP_H2_SYNC_STRT_WID, RREG32(RADEON_CRTC2_H_SYNC_STRT_WID)); + WREG32(RADEON_FP_V2_SYNC_STRT_WID, RREG32(RADEON_CRTC2_V_SYNC_STRT_WID)); + break; + } +} + +/** + * radeon_get_pll_use_mask - look up a mask of which pplls are in use + * + * @crtc: drm crtc + * + * Returns the mask of which PPLLs (Pixel PLLs) are in use. + */ +static u32 radeon_get_pll_use_mask(struct drm_crtc *crtc) +{ + struct drm_device *dev = crtc->dev; + struct drm_crtc *test_crtc; + struct radeon_crtc *test_radeon_crtc; + u32 pll_in_use = 0; + + list_for_each_entry(test_crtc, &dev->mode_config.crtc_list, head) { + if (crtc == test_crtc) + continue; + + test_radeon_crtc = to_radeon_crtc(test_crtc); + if (test_radeon_crtc->pll_id != ATOM_PPLL_INVALID) + pll_in_use |= (1 << test_radeon_crtc->pll_id); + } + return pll_in_use; +} + +/** + * radeon_get_shared_dp_ppll - return the PPLL used by another crtc for DP + * + * @crtc: drm crtc + * + * Returns the PPLL (Pixel PLL) used by another crtc/encoder which is + * also in DP mode. For DP, a single PPLL can be used for all DP + * crtcs/encoders. + */ +static int radeon_get_shared_dp_ppll(struct drm_crtc *crtc) +{ + struct drm_device *dev = crtc->dev; + struct drm_crtc *test_crtc; + struct radeon_crtc *test_radeon_crtc; + + list_for_each_entry(test_crtc, &dev->mode_config.crtc_list, head) { + if (crtc == test_crtc) + continue; + test_radeon_crtc = to_radeon_crtc(test_crtc); + if (test_radeon_crtc->encoder && + ENCODER_MODE_IS_DP(atombios_get_encoder_mode(test_radeon_crtc->encoder))) { + /* for DP use the same PLL for all */ + if (test_radeon_crtc->pll_id != ATOM_PPLL_INVALID) + return test_radeon_crtc->pll_id; + } + } + return ATOM_PPLL_INVALID; +} + +/** + * radeon_get_shared_nondp_ppll - return the PPLL used by another non-DP crtc + * + * @crtc: drm crtc + * @encoder: drm encoder + * + * Returns the PPLL (Pixel PLL) used by another non-DP crtc/encoder which can + * be shared (i.e., same clock). + */ +static int radeon_get_shared_nondp_ppll(struct drm_crtc *crtc) +{ + struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); + struct drm_device *dev = crtc->dev; + struct drm_crtc *test_crtc; + struct radeon_crtc *test_radeon_crtc; + u32 adjusted_clock, test_adjusted_clock; + + adjusted_clock = radeon_crtc->adjusted_clock; + + if (adjusted_clock == 0) + return ATOM_PPLL_INVALID; + + list_for_each_entry(test_crtc, &dev->mode_config.crtc_list, head) { + if (crtc == test_crtc) + continue; + test_radeon_crtc = to_radeon_crtc(test_crtc); + if (test_radeon_crtc->encoder && + !ENCODER_MODE_IS_DP(atombios_get_encoder_mode(test_radeon_crtc->encoder))) { + /* check if we are already driving this connector with another crtc */ + if (test_radeon_crtc->connector == radeon_crtc->connector) { + /* if we are, return that pll */ + if (test_radeon_crtc->pll_id != ATOM_PPLL_INVALID) + return test_radeon_crtc->pll_id; + } + /* for non-DP check the clock */ + test_adjusted_clock = test_radeon_crtc->adjusted_clock; + if ((crtc->mode.clock == test_crtc->mode.clock) && + (adjusted_clock == test_adjusted_clock) && + (radeon_crtc->ss_enabled == test_radeon_crtc->ss_enabled) && + (test_radeon_crtc->pll_id != ATOM_PPLL_INVALID)) + return test_radeon_crtc->pll_id; + } + } + return ATOM_PPLL_INVALID; +} + +/** + * radeon_atom_pick_pll - Allocate a PPLL for use by the crtc. + * + * @crtc: drm crtc + * + * Returns the PPLL (Pixel PLL) to be used by the crtc. For DP monitors + * a single PPLL can be used for all DP crtcs/encoders. For non-DP + * monitors a dedicated PPLL must be used. If a particular board has + * an external DP PLL, return ATOM_PPLL_INVALID to skip PLL programming + * as there is no need to program the PLL itself. If we are not able to + * allocate a PLL, return ATOM_PPLL_INVALID to skip PLL programming to + * avoid messing up an existing monitor. + * + * Asic specific PLL information + * + * DCE 6.1 + * - PPLL2 is only available to UNIPHYA (both DP and non-DP) + * - PPLL0, PPLL1 are available for UNIPHYB/C/D/E/F (both DP and non-DP) + * + * DCE 6.0 + * - PPLL0 is available to all UNIPHY (DP only) + * - PPLL1, PPLL2 are available for all UNIPHY (both DP and non-DP) and DAC + * + * DCE 5.0 + * - DCPLL is available to all UNIPHY (DP only) + * - PPLL1, PPLL2 are available for all UNIPHY (both DP and non-DP) and DAC + * + * DCE 3.0/4.0/4.1 + * - PPLL1, PPLL2 are available for all UNIPHY (both DP and non-DP) and DAC + * + */ +static int radeon_atom_pick_pll(struct drm_crtc *crtc) +{ + struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); + struct drm_device *dev = crtc->dev; + struct radeon_device *rdev = dev->dev_private; + struct radeon_encoder *radeon_encoder = + to_radeon_encoder(radeon_crtc->encoder); + u32 pll_in_use; + int pll; + + if (ASIC_IS_DCE61(rdev)) { + struct radeon_encoder_atom_dig *dig = + radeon_encoder->enc_priv; + + if ((radeon_encoder->encoder_id == ENCODER_OBJECT_ID_INTERNAL_UNIPHY) && + (dig->linkb == false)) + /* UNIPHY A uses PPLL2 */ + return ATOM_PPLL2; + else if (ENCODER_MODE_IS_DP(atombios_get_encoder_mode(radeon_crtc->encoder))) { + /* UNIPHY B/C/D/E/F */ + if (rdev->clock.dp_extclk) + /* skip PPLL programming if using ext clock */ + return ATOM_PPLL_INVALID; + else { + /* use the same PPLL for all DP monitors */ + pll = radeon_get_shared_dp_ppll(crtc); + if (pll != ATOM_PPLL_INVALID) + return pll; + } + } else { + /* use the same PPLL for all monitors with the same clock */ + pll = radeon_get_shared_nondp_ppll(crtc); + if (pll != ATOM_PPLL_INVALID) + return pll; + } + /* UNIPHY B/C/D/E/F */ + pll_in_use = radeon_get_pll_use_mask(crtc); + if (!(pll_in_use & (1 << ATOM_PPLL0))) + return ATOM_PPLL0; + if (!(pll_in_use & (1 << ATOM_PPLL1))) + return ATOM_PPLL1; + DRM_ERROR("unable to allocate a PPLL\n"); + return ATOM_PPLL_INVALID; + } else if (ASIC_IS_DCE4(rdev)) { + /* in DP mode, the DP ref clock can come from PPLL, DCPLL, or ext clock, + * depending on the asic: + * DCE4: PPLL or ext clock + * DCE5: PPLL, DCPLL, or ext clock + * DCE6: PPLL, PPLL0, or ext clock + * + * Setting ATOM_PPLL_INVALID will cause SetPixelClock to skip + * PPLL/DCPLL programming and only program the DP DTO for the + * crtc virtual pixel clock. + */ + if (ENCODER_MODE_IS_DP(atombios_get_encoder_mode(radeon_crtc->encoder))) { + if (rdev->clock.dp_extclk) + /* skip PPLL programming if using ext clock */ + return ATOM_PPLL_INVALID; + else if (ASIC_IS_DCE6(rdev)) + /* use PPLL0 for all DP */ + return ATOM_PPLL0; + else if (ASIC_IS_DCE5(rdev)) + /* use DCPLL for all DP */ + return ATOM_DCPLL; + else { + /* use the same PPLL for all DP monitors */ + pll = radeon_get_shared_dp_ppll(crtc); + if (pll != ATOM_PPLL_INVALID) + return pll; + } + } else { + /* use the same PPLL for all monitors with the same clock */ + pll = radeon_get_shared_nondp_ppll(crtc); + if (pll != ATOM_PPLL_INVALID) + return pll; + } + /* all other cases */ + pll_in_use = radeon_get_pll_use_mask(crtc); + if (!(pll_in_use & (1 << ATOM_PPLL1))) + return ATOM_PPLL1; + if (!(pll_in_use & (1 << ATOM_PPLL2))) + return ATOM_PPLL2; + DRM_ERROR("unable to allocate a PPLL\n"); + return ATOM_PPLL_INVALID; + } else { + /* on pre-R5xx asics, the crtc to pll mapping is hardcoded */ + /* some atombios (observed in some DCE2/DCE3) code have a bug, + * the matching btw pll and crtc is done through + * PCLK_CRTC[1|2]_CNTL (0x480/0x484) but atombios code use the + * pll (1 or 2) to select which register to write. ie if using + * pll1 it will use PCLK_CRTC1_CNTL (0x480) and if using pll2 + * it will use PCLK_CRTC2_CNTL (0x484), it then use crtc id to + * choose which value to write. Which is reverse order from + * register logic. So only case that works is when pllid is + * same as crtcid or when both pll and crtc are enabled and + * both use same clock. + * + * So just return crtc id as if crtc and pll were hard linked + * together even if they aren't + */ + return radeon_crtc->crtc_id; + } +} + +void radeon_atom_disp_eng_pll_init(struct radeon_device *rdev) +{ + /* always set DCPLL */ + if (ASIC_IS_DCE6(rdev)) + atombios_crtc_set_disp_eng_pll(rdev, rdev->clock.default_dispclk); + else if (ASIC_IS_DCE4(rdev)) { + struct radeon_atom_ss ss; + bool ss_enabled = radeon_atombios_get_asic_ss_info(rdev, &ss, + ASIC_INTERNAL_SS_ON_DCPLL, + rdev->clock.default_dispclk); + if (ss_enabled) + atombios_crtc_program_ss(rdev, ATOM_DISABLE, ATOM_DCPLL, -1, &ss); + /* XXX: DCE5, make sure voltage, dispclk is high enough */ + atombios_crtc_set_disp_eng_pll(rdev, rdev->clock.default_dispclk); + if (ss_enabled) + atombios_crtc_program_ss(rdev, ATOM_ENABLE, ATOM_DCPLL, -1, &ss); + } + +} + +int atombios_crtc_mode_set(struct drm_crtc *crtc, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode, + int x, int y, struct drm_framebuffer *old_fb) +{ + struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); + struct drm_device *dev = crtc->dev; + struct radeon_device *rdev = dev->dev_private; + struct radeon_encoder *radeon_encoder = + to_radeon_encoder(radeon_crtc->encoder); + bool is_tvcv = false; + + if (radeon_encoder->active_device & + (ATOM_DEVICE_TV_SUPPORT | ATOM_DEVICE_CV_SUPPORT)) + is_tvcv = true; + + atombios_crtc_set_pll(crtc, adjusted_mode); + + if (ASIC_IS_DCE4(rdev)) + atombios_set_crtc_dtd_timing(crtc, adjusted_mode); + else if (ASIC_IS_AVIVO(rdev)) { + if (is_tvcv) + atombios_crtc_set_timing(crtc, adjusted_mode); + else + atombios_set_crtc_dtd_timing(crtc, adjusted_mode); + } else { + atombios_crtc_set_timing(crtc, adjusted_mode); + if (radeon_crtc->crtc_id == 0) + atombios_set_crtc_dtd_timing(crtc, adjusted_mode); + radeon_legacy_atom_fixup(crtc); + } + atombios_crtc_set_base(crtc, x, y, old_fb); + atombios_overscan_setup(crtc, mode, adjusted_mode); + atombios_scaler_setup(crtc); + return 0; +} + +static bool atombios_crtc_mode_fixup(struct drm_crtc *crtc, + const struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); + struct drm_device *dev = crtc->dev; + struct drm_encoder *encoder; + + /* assign the encoder to the radeon crtc to avoid repeated lookups later */ + list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { + if (encoder->crtc == crtc) { + radeon_crtc->encoder = encoder; + radeon_crtc->connector = radeon_get_connector_for_encoder(encoder); + break; + } + } + if ((radeon_crtc->encoder == NULL) || (radeon_crtc->connector == NULL)) { + radeon_crtc->encoder = NULL; + radeon_crtc->connector = NULL; + return false; + } + if (!radeon_crtc_scaling_mode_fixup(crtc, mode, adjusted_mode)) + return false; + if (!atombios_crtc_prepare_pll(crtc, adjusted_mode)) + return false; + /* pick pll */ + radeon_crtc->pll_id = radeon_atom_pick_pll(crtc); + /* if we can't get a PPLL for a non-DP encoder, fail */ + if ((radeon_crtc->pll_id == ATOM_PPLL_INVALID) && + !ENCODER_MODE_IS_DP(atombios_get_encoder_mode(radeon_crtc->encoder))) + return false; + + return true; +} + +static void atombios_crtc_prepare(struct drm_crtc *crtc) +{ + struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); + struct drm_device *dev = crtc->dev; + struct radeon_device *rdev = dev->dev_private; + + radeon_crtc->in_mode_set = true; + + /* disable crtc pair power gating before programming */ + if (ASIC_IS_DCE6(rdev)) + atombios_powergate_crtc(crtc, ATOM_DISABLE); + + atombios_lock_crtc(crtc, ATOM_ENABLE); + atombios_crtc_dpms(crtc, DRM_MODE_DPMS_OFF); +} + +static void atombios_crtc_commit(struct drm_crtc *crtc) +{ + struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); + + atombios_crtc_dpms(crtc, DRM_MODE_DPMS_ON); + atombios_lock_crtc(crtc, ATOM_DISABLE); + radeon_crtc->in_mode_set = false; +} + +static void atombios_crtc_disable(struct drm_crtc *crtc) +{ + struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); + struct drm_device *dev = crtc->dev; + struct radeon_device *rdev = dev->dev_private; + struct radeon_atom_ss ss; + int i; + + atombios_crtc_dpms(crtc, DRM_MODE_DPMS_OFF); + + for (i = 0; i < rdev->num_crtc; i++) { + if (rdev->mode_info.crtcs[i] && + rdev->mode_info.crtcs[i]->enabled && + i != radeon_crtc->crtc_id && + radeon_crtc->pll_id == rdev->mode_info.crtcs[i]->pll_id) { + /* one other crtc is using this pll don't turn + * off the pll + */ + goto done; + } + } + + switch (radeon_crtc->pll_id) { + case ATOM_PPLL1: + case ATOM_PPLL2: + /* disable the ppll */ + atombios_crtc_program_pll(crtc, radeon_crtc->crtc_id, radeon_crtc->pll_id, + 0, 0, ATOM_DISABLE, 0, 0, 0, 0, 0, false, &ss); + break; + case ATOM_PPLL0: + /* disable the ppll */ + if (ASIC_IS_DCE61(rdev)) + atombios_crtc_program_pll(crtc, radeon_crtc->crtc_id, radeon_crtc->pll_id, + 0, 0, ATOM_DISABLE, 0, 0, 0, 0, 0, false, &ss); + break; + default: + break; + } +done: + radeon_crtc->pll_id = ATOM_PPLL_INVALID; + radeon_crtc->adjusted_clock = 0; + radeon_crtc->encoder = NULL; + radeon_crtc->connector = NULL; +} + +static const struct drm_crtc_helper_funcs atombios_helper_funcs = { + .dpms = atombios_crtc_dpms, + .mode_fixup = atombios_crtc_mode_fixup, + .mode_set = atombios_crtc_mode_set, + .mode_set_base = atombios_crtc_set_base, + .mode_set_base_atomic = atombios_crtc_set_base_atomic, + .prepare = atombios_crtc_prepare, + .commit = atombios_crtc_commit, + .load_lut = radeon_crtc_load_lut, + .disable = atombios_crtc_disable, +}; + +void radeon_atombios_init_crtc(struct drm_device *dev, + struct radeon_crtc *radeon_crtc) +{ + struct radeon_device *rdev = dev->dev_private; + + if (ASIC_IS_DCE4(rdev)) { + switch (radeon_crtc->crtc_id) { + case 0: + default: + radeon_crtc->crtc_offset = EVERGREEN_CRTC0_REGISTER_OFFSET; + break; + case 1: + radeon_crtc->crtc_offset = EVERGREEN_CRTC1_REGISTER_OFFSET; + break; + case 2: + radeon_crtc->crtc_offset = EVERGREEN_CRTC2_REGISTER_OFFSET; + break; + case 3: + radeon_crtc->crtc_offset = EVERGREEN_CRTC3_REGISTER_OFFSET; + break; + case 4: + radeon_crtc->crtc_offset = EVERGREEN_CRTC4_REGISTER_OFFSET; + break; + case 5: + radeon_crtc->crtc_offset = EVERGREEN_CRTC5_REGISTER_OFFSET; + break; + } + } else { + if (radeon_crtc->crtc_id == 1) + radeon_crtc->crtc_offset = + AVIVO_D2CRTC_H_TOTAL - AVIVO_D1CRTC_H_TOTAL; + else + radeon_crtc->crtc_offset = 0; + } + radeon_crtc->pll_id = ATOM_PPLL_INVALID; + radeon_crtc->adjusted_clock = 0; + radeon_crtc->encoder = NULL; + radeon_crtc->connector = NULL; + drm_crtc_helper_add(&radeon_crtc->base, &atombios_helper_funcs); +} diff --git a/sys/dev/drm2/radeon/atombios_dp.c b/sys/dev/drm2/radeon/atombios_dp.c new file mode 100644 index 00000000000..3c0f7f563da --- /dev/null +++ b/sys/dev/drm2/radeon/atombios_dp.c @@ -0,0 +1,893 @@ +/* + * Copyright 2007-8 Advanced Micro Devices, Inc. + * Copyright 2008 Red Hat Inc. + * + * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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. + * + * Authors: Dave Airlie + * Alex Deucher + * Jerome Glisse + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include "radeon.h" + +#include "atom.h" +#include "atom-bits.h" +#include + +/* move these to drm_dp_helper.c/h */ +#define DP_LINK_CONFIGURATION_SIZE 9 +#define DP_DPCD_SIZE DP_RECEIVER_CAP_SIZE + +static char *voltage_names[] = { + "0.4V", "0.6V", "0.8V", "1.2V" +}; +static char *pre_emph_names[] = { + "0dB", "3.5dB", "6dB", "9.5dB" +}; + +/***** radeon AUX functions *****/ +union aux_channel_transaction { + PROCESS_AUX_CHANNEL_TRANSACTION_PS_ALLOCATION v1; + PROCESS_AUX_CHANNEL_TRANSACTION_PARAMETERS_V2 v2; +}; + +static int radeon_process_aux_ch(struct radeon_i2c_chan *chan, + u8 *send, int send_bytes, + u8 *recv, int recv_size, + u8 delay, u8 *ack) +{ + struct drm_device *dev = chan->dev; + struct radeon_device *rdev = dev->dev_private; + union aux_channel_transaction args; + int index = GetIndexIntoMasterTable(COMMAND, ProcessAuxChannelTransaction); + unsigned char *base; + int recv_bytes; + + memset(&args, 0, sizeof(args)); + + base = (unsigned char *)(rdev->mode_info.atom_context->scratch + 1); + + memcpy(base, send, send_bytes); + + args.v1.lpAuxRequest = 0 + 4; + args.v1.lpDataOut = 16 + 4; + args.v1.ucDataOutLen = 0; + args.v1.ucChannelID = chan->rec.i2c_id; + args.v1.ucDelay = delay / 10; + if (ASIC_IS_DCE4(rdev)) + args.v2.ucHPD_ID = chan->rec.hpd; + + atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args); + + *ack = args.v1.ucReplyStatus; + + /* timeout */ + if (args.v1.ucReplyStatus == 1) { + DRM_DEBUG_KMS("dp_aux_ch timeout\n"); + return -ETIMEDOUT; + } + + /* flags not zero */ + if (args.v1.ucReplyStatus == 2) { + DRM_DEBUG_KMS("dp_aux_ch flags not zero\n"); + return -EBUSY; + } + + /* error */ + if (args.v1.ucReplyStatus == 3) { + DRM_DEBUG_KMS("dp_aux_ch error\n"); + return -EIO; + } + + recv_bytes = args.v1.ucDataOutLen; + if (recv_bytes > recv_size) + recv_bytes = recv_size; + + if (recv && recv_size) + memcpy(recv, base + 16, recv_bytes); + + return recv_bytes; +} + +static int radeon_dp_aux_native_write(struct radeon_connector *radeon_connector, + u16 address, u8 *send, u8 send_bytes, u8 delay) +{ + struct radeon_connector_atom_dig *dig_connector = radeon_connector->con_priv; + int ret; + u8 msg[20]; + int msg_bytes = send_bytes + 4; + u8 ack; + unsigned retry; + + if (send_bytes > 16) + return -1; + + msg[0] = address; + msg[1] = address >> 8; + msg[2] = AUX_NATIVE_WRITE << 4; + msg[3] = (msg_bytes << 4) | (send_bytes - 1); + memcpy(&msg[4], send, send_bytes); + + for (retry = 0; retry < 4; retry++) { + ret = radeon_process_aux_ch(dig_connector->dp_i2c_bus, + msg, msg_bytes, NULL, 0, delay, &ack); + if (ret == -EBUSY) + continue; + else if (ret < 0) + return ret; + if ((ack & AUX_NATIVE_REPLY_MASK) == AUX_NATIVE_REPLY_ACK) + return send_bytes; + else if ((ack & AUX_NATIVE_REPLY_MASK) == AUX_NATIVE_REPLY_DEFER) + DRM_UDELAY(400); + else + return -EIO; + } + + return -EIO; +} + +static int radeon_dp_aux_native_read(struct radeon_connector *radeon_connector, + u16 address, u8 *recv, int recv_bytes, u8 delay) +{ + struct radeon_connector_atom_dig *dig_connector = radeon_connector->con_priv; + u8 msg[4]; + int msg_bytes = 4; + u8 ack; + int ret; + unsigned retry; + + msg[0] = address; + msg[1] = address >> 8; + msg[2] = AUX_NATIVE_READ << 4; + msg[3] = (msg_bytes << 4) | (recv_bytes - 1); + + for (retry = 0; retry < 4; retry++) { + ret = radeon_process_aux_ch(dig_connector->dp_i2c_bus, + msg, msg_bytes, recv, recv_bytes, delay, &ack); + if (ret == -EBUSY) + continue; + else if (ret < 0) + return ret; + if ((ack & AUX_NATIVE_REPLY_MASK) == AUX_NATIVE_REPLY_ACK) + return ret; + else if ((ack & AUX_NATIVE_REPLY_MASK) == AUX_NATIVE_REPLY_DEFER) + DRM_UDELAY(400); + else if (ret == 0) + return -EPROTO; + else + return -EIO; + } + + return -EIO; +} + +static void radeon_write_dpcd_reg(struct radeon_connector *radeon_connector, + u16 reg, u8 val) +{ + radeon_dp_aux_native_write(radeon_connector, reg, &val, 1, 0); +} + +static u8 radeon_read_dpcd_reg(struct radeon_connector *radeon_connector, + u16 reg) +{ + u8 val = 0; + + radeon_dp_aux_native_read(radeon_connector, reg, &val, 1, 0); + + return val; +} + +int radeon_dp_i2c_aux_ch(device_t dev, int mode, u8 write_byte, u8 *read_byte) +{ + struct iic_dp_aux_data *algo_data = device_get_softc(dev); + struct radeon_i2c_chan *auxch = algo_data->priv; + u16 address = algo_data->address; + u8 msg[5]; + u8 reply[2]; + unsigned retry; + int msg_bytes; + int reply_bytes = 1; + int ret; + u8 ack; + + /* Set up the command byte */ + if (mode & MODE_I2C_READ) + msg[2] = AUX_I2C_READ << 4; + else + msg[2] = AUX_I2C_WRITE << 4; + + if (!(mode & MODE_I2C_STOP)) + msg[2] |= AUX_I2C_MOT << 4; + + msg[0] = address; + msg[1] = address >> 8; + + switch (mode) { + case MODE_I2C_WRITE: + msg_bytes = 5; + msg[3] = msg_bytes << 4; + msg[4] = write_byte; + break; + case MODE_I2C_READ: + msg_bytes = 4; + msg[3] = msg_bytes << 4; + break; + default: + msg_bytes = 4; + msg[3] = 3 << 4; + break; + } + + for (retry = 0; retry < 4; retry++) { + ret = radeon_process_aux_ch(auxch, + msg, msg_bytes, reply, reply_bytes, 0, &ack); + if (ret == -EBUSY) + continue; + else if (ret < 0) { + DRM_DEBUG_KMS("aux_ch failed %d\n", ret); + return ret; + } + + switch (ack & AUX_NATIVE_REPLY_MASK) { + case AUX_NATIVE_REPLY_ACK: + /* I2C-over-AUX Reply field is only valid + * when paired with AUX ACK. + */ + break; + case AUX_NATIVE_REPLY_NACK: + DRM_DEBUG_KMS("aux_ch native nack\n"); + return -EREMOTEIO; + case AUX_NATIVE_REPLY_DEFER: + DRM_DEBUG_KMS("aux_ch native defer\n"); + DRM_UDELAY(400); + continue; + default: + DRM_ERROR("aux_ch invalid native reply 0x%02x\n", ack); + return -EREMOTEIO; + } + + switch (ack & AUX_I2C_REPLY_MASK) { + case AUX_I2C_REPLY_ACK: + if (mode == MODE_I2C_READ) + *read_byte = reply[0]; + return ret; + case AUX_I2C_REPLY_NACK: + DRM_DEBUG_KMS("aux_i2c nack\n"); + return -EREMOTEIO; + case AUX_I2C_REPLY_DEFER: + DRM_DEBUG_KMS("aux_i2c defer\n"); + DRM_UDELAY(400); + break; + default: + DRM_ERROR("aux_i2c invalid reply 0x%02x\n", ack); + return -EREMOTEIO; + } + } + + DRM_DEBUG_KMS("aux i2c too many retries, giving up\n"); + return -EREMOTEIO; +} + +/***** general DP utility functions *****/ + +#define DP_VOLTAGE_MAX DP_TRAIN_VOLTAGE_SWING_1200 +#define DP_PRE_EMPHASIS_MAX DP_TRAIN_PRE_EMPHASIS_9_5 + +static void dp_get_adjust_train(u8 link_status[DP_LINK_STATUS_SIZE], + int lane_count, + u8 train_set[4]) +{ + u8 v = 0; + u8 p = 0; + int lane; + + for (lane = 0; lane < lane_count; lane++) { + u8 this_v = drm_dp_get_adjust_request_voltage(link_status, lane); + u8 this_p = drm_dp_get_adjust_request_pre_emphasis(link_status, lane); + + DRM_DEBUG_KMS("requested signal parameters: lane %d voltage %s pre_emph %s\n", + lane, + voltage_names[this_v >> DP_TRAIN_VOLTAGE_SWING_SHIFT], + pre_emph_names[this_p >> DP_TRAIN_PRE_EMPHASIS_SHIFT]); + + if (this_v > v) + v = this_v; + if (this_p > p) + p = this_p; + } + + if (v >= DP_VOLTAGE_MAX) + v |= DP_TRAIN_MAX_SWING_REACHED; + + if (p >= DP_PRE_EMPHASIS_MAX) + p |= DP_TRAIN_MAX_PRE_EMPHASIS_REACHED; + + DRM_DEBUG_KMS("using signal parameters: voltage %s pre_emph %s\n", + voltage_names[(v & DP_TRAIN_VOLTAGE_SWING_MASK) >> DP_TRAIN_VOLTAGE_SWING_SHIFT], + pre_emph_names[(p & DP_TRAIN_PRE_EMPHASIS_MASK) >> DP_TRAIN_PRE_EMPHASIS_SHIFT]); + + for (lane = 0; lane < 4; lane++) + train_set[lane] = v | p; +} + +/* convert bits per color to bits per pixel */ +/* get bpc from the EDID */ +static int convert_bpc_to_bpp(int bpc) +{ + if (bpc == 0) + return 24; + else + return bpc * 3; +} + +/* get the max pix clock supported by the link rate and lane num */ +static int dp_get_max_dp_pix_clock(int link_rate, + int lane_num, + int bpp) +{ + return (link_rate * lane_num * 8) / bpp; +} + +/***** radeon specific DP functions *****/ + +/* First get the min lane# when low rate is used according to pixel clock + * (prefer low rate), second check max lane# supported by DP panel, + * if the max lane# < low rate lane# then use max lane# instead. + */ +static int radeon_dp_get_dp_lane_number(struct drm_connector *connector, + u8 dpcd[DP_DPCD_SIZE], + int pix_clock) +{ + int bpp = convert_bpc_to_bpp(radeon_get_monitor_bpc(connector)); + int max_link_rate = drm_dp_max_link_rate(dpcd); + int max_lane_num = drm_dp_max_lane_count(dpcd); + int lane_num; + int max_dp_pix_clock; + + for (lane_num = 1; lane_num < max_lane_num; lane_num <<= 1) { + max_dp_pix_clock = dp_get_max_dp_pix_clock(max_link_rate, lane_num, bpp); + if (pix_clock <= max_dp_pix_clock) + break; + } + + return lane_num; +} + +static int radeon_dp_get_dp_link_clock(struct drm_connector *connector, + u8 dpcd[DP_DPCD_SIZE], + int pix_clock) +{ + int bpp = convert_bpc_to_bpp(radeon_get_monitor_bpc(connector)); + int lane_num, max_pix_clock; + + if (radeon_connector_encoder_get_dp_bridge_encoder_id(connector) == + ENCODER_OBJECT_ID_NUTMEG) + return 270000; + + lane_num = radeon_dp_get_dp_lane_number(connector, dpcd, pix_clock); + max_pix_clock = dp_get_max_dp_pix_clock(162000, lane_num, bpp); + if (pix_clock <= max_pix_clock) + return 162000; + max_pix_clock = dp_get_max_dp_pix_clock(270000, lane_num, bpp); + if (pix_clock <= max_pix_clock) + return 270000; + if (radeon_connector_is_dp12_capable(connector)) { + max_pix_clock = dp_get_max_dp_pix_clock(540000, lane_num, bpp); + if (pix_clock <= max_pix_clock) + return 540000; + } + + return drm_dp_max_link_rate(dpcd); +} + +static u8 radeon_dp_encoder_service(struct radeon_device *rdev, + int action, int dp_clock, + u8 ucconfig, u8 lane_num) +{ + DP_ENCODER_SERVICE_PARAMETERS args; + int index = GetIndexIntoMasterTable(COMMAND, DPEncoderService); + + memset(&args, 0, sizeof(args)); + args.ucLinkClock = dp_clock / 10; + args.ucConfig = ucconfig; + args.ucAction = action; + args.ucLaneNum = lane_num; + args.ucStatus = 0; + + atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args); + return args.ucStatus; +} + +u8 radeon_dp_getsinktype(struct radeon_connector *radeon_connector) +{ + struct radeon_connector_atom_dig *dig_connector = radeon_connector->con_priv; + struct drm_device *dev = radeon_connector->base.dev; + struct radeon_device *rdev = dev->dev_private; + + return radeon_dp_encoder_service(rdev, ATOM_DP_ACTION_GET_SINK_TYPE, 0, + dig_connector->dp_i2c_bus->rec.i2c_id, 0); +} + +static void radeon_dp_probe_oui(struct radeon_connector *radeon_connector) +{ + struct radeon_connector_atom_dig *dig_connector = radeon_connector->con_priv; + u8 buf[3]; + + if (!(dig_connector->dpcd[DP_DOWN_STREAM_PORT_COUNT] & DP_OUI_SUPPORT)) + return; + + if (radeon_dp_aux_native_read(radeon_connector, DP_SINK_OUI, buf, 3, 0)) + DRM_DEBUG_KMS("Sink OUI: %02hhx%02hhx%02hhx\n", + buf[0], buf[1], buf[2]); + + if (radeon_dp_aux_native_read(radeon_connector, DP_BRANCH_OUI, buf, 3, 0)) + DRM_DEBUG_KMS("Branch OUI: %02hhx%02hhx%02hhx\n", + buf[0], buf[1], buf[2]); +} + +bool radeon_dp_getdpcd(struct radeon_connector *radeon_connector) +{ + struct radeon_connector_atom_dig *dig_connector = radeon_connector->con_priv; + u8 msg[DP_DPCD_SIZE]; + int ret, i; + + ret = radeon_dp_aux_native_read(radeon_connector, DP_DPCD_REV, msg, + DP_DPCD_SIZE, 0); + if (ret > 0) { + memcpy(dig_connector->dpcd, msg, DP_DPCD_SIZE); + DRM_DEBUG_KMS("DPCD: "); + for (i = 0; i < DP_DPCD_SIZE; i++) + DRM_DEBUG_KMS("%02x ", msg[i]); + DRM_DEBUG_KMS("\n"); + + radeon_dp_probe_oui(radeon_connector); + + return true; + } + dig_connector->dpcd[0] = 0; + return false; +} + +int radeon_dp_get_panel_mode(struct drm_encoder *encoder, + struct drm_connector *connector) +{ + struct drm_device *dev = encoder->dev; + struct radeon_device *rdev = dev->dev_private; + struct radeon_connector *radeon_connector = to_radeon_connector(connector); + int panel_mode = DP_PANEL_MODE_EXTERNAL_DP_MODE; + u16 dp_bridge = radeon_connector_encoder_get_dp_bridge_encoder_id(connector); + u8 tmp; + + if (!ASIC_IS_DCE4(rdev)) + return panel_mode; + + if (dp_bridge != ENCODER_OBJECT_ID_NONE) { + /* DP bridge chips */ + tmp = radeon_read_dpcd_reg(radeon_connector, DP_EDP_CONFIGURATION_CAP); + if (tmp & 1) + panel_mode = DP_PANEL_MODE_INTERNAL_DP2_MODE; + else if ((dp_bridge == ENCODER_OBJECT_ID_NUTMEG) || + (dp_bridge == ENCODER_OBJECT_ID_TRAVIS)) + panel_mode = DP_PANEL_MODE_INTERNAL_DP1_MODE; + else + panel_mode = DP_PANEL_MODE_EXTERNAL_DP_MODE; + } else if (connector->connector_type == DRM_MODE_CONNECTOR_eDP) { + /* eDP */ + tmp = radeon_read_dpcd_reg(radeon_connector, DP_EDP_CONFIGURATION_CAP); + if (tmp & 1) + panel_mode = DP_PANEL_MODE_INTERNAL_DP2_MODE; + } + + return panel_mode; +} + +void radeon_dp_set_link_config(struct drm_connector *connector, + const struct drm_display_mode *mode) +{ + struct radeon_connector *radeon_connector = to_radeon_connector(connector); + struct radeon_connector_atom_dig *dig_connector; + + if (!radeon_connector->con_priv) + return; + dig_connector = radeon_connector->con_priv; + + if ((dig_connector->dp_sink_type == CONNECTOR_OBJECT_ID_DISPLAYPORT) || + (dig_connector->dp_sink_type == CONNECTOR_OBJECT_ID_eDP)) { + dig_connector->dp_clock = + radeon_dp_get_dp_link_clock(connector, dig_connector->dpcd, mode->clock); + dig_connector->dp_lane_count = + radeon_dp_get_dp_lane_number(connector, dig_connector->dpcd, mode->clock); + } +} + +int radeon_dp_mode_valid_helper(struct drm_connector *connector, + struct drm_display_mode *mode) +{ + struct radeon_connector *radeon_connector = to_radeon_connector(connector); + struct radeon_connector_atom_dig *dig_connector; + int dp_clock; + + if (!radeon_connector->con_priv) + return MODE_CLOCK_HIGH; + dig_connector = radeon_connector->con_priv; + + dp_clock = + radeon_dp_get_dp_link_clock(connector, dig_connector->dpcd, mode->clock); + + if ((dp_clock == 540000) && + (!radeon_connector_is_dp12_capable(connector))) + return MODE_CLOCK_HIGH; + + return MODE_OK; +} + +static bool radeon_dp_get_link_status(struct radeon_connector *radeon_connector, + u8 link_status[DP_LINK_STATUS_SIZE]) +{ + int ret; + ret = radeon_dp_aux_native_read(radeon_connector, DP_LANE0_1_STATUS, + link_status, DP_LINK_STATUS_SIZE, 100); + if (ret <= 0) { + return false; + } + + DRM_DEBUG_KMS("link status %*ph\n", 6, link_status); + return true; +} + +bool radeon_dp_needs_link_train(struct radeon_connector *radeon_connector) +{ + u8 link_status[DP_LINK_STATUS_SIZE]; + struct radeon_connector_atom_dig *dig = radeon_connector->con_priv; + + if (!radeon_dp_get_link_status(radeon_connector, link_status)) + return false; + if (drm_dp_channel_eq_ok(link_status, dig->dp_lane_count)) + return false; + return true; +} + +struct radeon_dp_link_train_info { + struct radeon_device *rdev; + struct drm_encoder *encoder; + struct drm_connector *connector; + struct radeon_connector *radeon_connector; + int enc_id; + int dp_clock; + int dp_lane_count; + bool tp3_supported; + u8 dpcd[DP_RECEIVER_CAP_SIZE]; + u8 train_set[4]; + u8 link_status[DP_LINK_STATUS_SIZE]; + u8 tries; + bool use_dpencoder; +}; + +static void radeon_dp_update_vs_emph(struct radeon_dp_link_train_info *dp_info) +{ + /* set the initial vs/emph on the source */ + atombios_dig_transmitter_setup(dp_info->encoder, + ATOM_TRANSMITTER_ACTION_SETUP_VSEMPH, + 0, dp_info->train_set[0]); /* sets all lanes at once */ + + /* set the vs/emph on the sink */ + radeon_dp_aux_native_write(dp_info->radeon_connector, DP_TRAINING_LANE0_SET, + dp_info->train_set, dp_info->dp_lane_count, 0); +} + +static void radeon_dp_set_tp(struct radeon_dp_link_train_info *dp_info, int tp) +{ + int rtp = 0; + + /* set training pattern on the source */ + if (ASIC_IS_DCE4(dp_info->rdev) || !dp_info->use_dpencoder) { + switch (tp) { + case DP_TRAINING_PATTERN_1: + rtp = ATOM_ENCODER_CMD_DP_LINK_TRAINING_PATTERN1; + break; + case DP_TRAINING_PATTERN_2: + rtp = ATOM_ENCODER_CMD_DP_LINK_TRAINING_PATTERN2; + break; + case DP_TRAINING_PATTERN_3: + rtp = ATOM_ENCODER_CMD_DP_LINK_TRAINING_PATTERN3; + break; + } + atombios_dig_encoder_setup(dp_info->encoder, rtp, 0); + } else { + switch (tp) { + case DP_TRAINING_PATTERN_1: + rtp = 0; + break; + case DP_TRAINING_PATTERN_2: + rtp = 1; + break; + } + radeon_dp_encoder_service(dp_info->rdev, ATOM_DP_ACTION_TRAINING_PATTERN_SEL, + dp_info->dp_clock, dp_info->enc_id, rtp); + } + + /* enable training pattern on the sink */ + radeon_write_dpcd_reg(dp_info->radeon_connector, DP_TRAINING_PATTERN_SET, tp); +} + +static int radeon_dp_link_train_init(struct radeon_dp_link_train_info *dp_info) +{ + struct radeon_encoder *radeon_encoder = to_radeon_encoder(dp_info->encoder); + struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv; + u8 tmp; + + /* power up the sink */ + if (dp_info->dpcd[0] >= 0x11) + radeon_write_dpcd_reg(dp_info->radeon_connector, + DP_SET_POWER, DP_SET_POWER_D0); + + /* possibly enable downspread on the sink */ + if (dp_info->dpcd[3] & 0x1) + radeon_write_dpcd_reg(dp_info->radeon_connector, + DP_DOWNSPREAD_CTRL, DP_SPREAD_AMP_0_5); + else + radeon_write_dpcd_reg(dp_info->radeon_connector, + DP_DOWNSPREAD_CTRL, 0); + + if ((dp_info->connector->connector_type == DRM_MODE_CONNECTOR_eDP) && + (dig->panel_mode == DP_PANEL_MODE_INTERNAL_DP2_MODE)) { + radeon_write_dpcd_reg(dp_info->radeon_connector, DP_EDP_CONFIGURATION_SET, 1); + } + + /* set the lane count on the sink */ + tmp = dp_info->dp_lane_count; + if (dp_info->dpcd[DP_DPCD_REV] >= 0x11 && + dp_info->dpcd[DP_MAX_LANE_COUNT] & DP_ENHANCED_FRAME_CAP) + tmp |= DP_LANE_COUNT_ENHANCED_FRAME_EN; + radeon_write_dpcd_reg(dp_info->radeon_connector, DP_LANE_COUNT_SET, tmp); + + /* set the link rate on the sink */ + tmp = drm_dp_link_rate_to_bw_code(dp_info->dp_clock); + radeon_write_dpcd_reg(dp_info->radeon_connector, DP_LINK_BW_SET, tmp); + + /* start training on the source */ + if (ASIC_IS_DCE4(dp_info->rdev) || !dp_info->use_dpencoder) + atombios_dig_encoder_setup(dp_info->encoder, + ATOM_ENCODER_CMD_DP_LINK_TRAINING_START, 0); + else + radeon_dp_encoder_service(dp_info->rdev, ATOM_DP_ACTION_TRAINING_START, + dp_info->dp_clock, dp_info->enc_id, 0); + + /* disable the training pattern on the sink */ + radeon_write_dpcd_reg(dp_info->radeon_connector, + DP_TRAINING_PATTERN_SET, + DP_TRAINING_PATTERN_DISABLE); + + return 0; +} + +static int radeon_dp_link_train_finish(struct radeon_dp_link_train_info *dp_info) +{ + DRM_UDELAY(400); + + /* disable the training pattern on the sink */ + radeon_write_dpcd_reg(dp_info->radeon_connector, + DP_TRAINING_PATTERN_SET, + DP_TRAINING_PATTERN_DISABLE); + + /* disable the training pattern on the source */ + if (ASIC_IS_DCE4(dp_info->rdev) || !dp_info->use_dpencoder) + atombios_dig_encoder_setup(dp_info->encoder, + ATOM_ENCODER_CMD_DP_LINK_TRAINING_COMPLETE, 0); + else + radeon_dp_encoder_service(dp_info->rdev, ATOM_DP_ACTION_TRAINING_COMPLETE, + dp_info->dp_clock, dp_info->enc_id, 0); + + return 0; +} + +static int radeon_dp_link_train_cr(struct radeon_dp_link_train_info *dp_info) +{ + bool clock_recovery; + u8 voltage; + int i; + + radeon_dp_set_tp(dp_info, DP_TRAINING_PATTERN_1); + memset(dp_info->train_set, 0, 4); + radeon_dp_update_vs_emph(dp_info); + + DRM_UDELAY(400); + + /* clock recovery loop */ + clock_recovery = false; + dp_info->tries = 0; + voltage = 0xff; + while (1) { + drm_dp_link_train_clock_recovery_delay(dp_info->dpcd); + + if (!radeon_dp_get_link_status(dp_info->radeon_connector, dp_info->link_status)) { + DRM_ERROR("displayport link status failed\n"); + break; + } + + if (drm_dp_clock_recovery_ok(dp_info->link_status, dp_info->dp_lane_count)) { + clock_recovery = true; + break; + } + + for (i = 0; i < dp_info->dp_lane_count; i++) { + if ((dp_info->train_set[i] & DP_TRAIN_MAX_SWING_REACHED) == 0) + break; + } + if (i == dp_info->dp_lane_count) { + DRM_ERROR("clock recovery reached max voltage\n"); + break; + } + + if ((dp_info->train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK) == voltage) { + ++dp_info->tries; + if (dp_info->tries == 5) { + DRM_ERROR("clock recovery tried 5 times\n"); + break; + } + } else + dp_info->tries = 0; + + voltage = dp_info->train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK; + + /* Compute new train_set as requested by sink */ + dp_get_adjust_train(dp_info->link_status, dp_info->dp_lane_count, dp_info->train_set); + + radeon_dp_update_vs_emph(dp_info); + } + if (!clock_recovery) { + DRM_ERROR("clock recovery failed\n"); + return -1; + } else { + DRM_DEBUG_KMS("clock recovery at voltage %d pre-emphasis %d\n", + dp_info->train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK, + (dp_info->train_set[0] & DP_TRAIN_PRE_EMPHASIS_MASK) >> + DP_TRAIN_PRE_EMPHASIS_SHIFT); + return 0; + } +} + +static int radeon_dp_link_train_ce(struct radeon_dp_link_train_info *dp_info) +{ + bool channel_eq; + + if (dp_info->tp3_supported) + radeon_dp_set_tp(dp_info, DP_TRAINING_PATTERN_3); + else + radeon_dp_set_tp(dp_info, DP_TRAINING_PATTERN_2); + + /* channel equalization loop */ + dp_info->tries = 0; + channel_eq = false; + while (1) { + drm_dp_link_train_channel_eq_delay(dp_info->dpcd); + + if (!radeon_dp_get_link_status(dp_info->radeon_connector, dp_info->link_status)) { + DRM_ERROR("displayport link status failed\n"); + break; + } + + if (drm_dp_channel_eq_ok(dp_info->link_status, dp_info->dp_lane_count)) { + channel_eq = true; + break; + } + + /* Try 5 times */ + if (dp_info->tries > 5) { + DRM_ERROR("channel eq failed: 5 tries\n"); + break; + } + + /* Compute new train_set as requested by sink */ + dp_get_adjust_train(dp_info->link_status, dp_info->dp_lane_count, dp_info->train_set); + + radeon_dp_update_vs_emph(dp_info); + dp_info->tries++; + } + + if (!channel_eq) { + DRM_ERROR("channel eq failed\n"); + return -1; + } else { + DRM_DEBUG_KMS("channel eq at voltage %d pre-emphasis %d\n", + dp_info->train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK, + (dp_info->train_set[0] & DP_TRAIN_PRE_EMPHASIS_MASK) + >> DP_TRAIN_PRE_EMPHASIS_SHIFT); + return 0; + } +} + +void radeon_dp_link_train(struct drm_encoder *encoder, + struct drm_connector *connector) +{ + struct drm_device *dev = encoder->dev; + struct radeon_device *rdev = dev->dev_private; + struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); + struct radeon_encoder_atom_dig *dig; + struct radeon_connector *radeon_connector; + struct radeon_connector_atom_dig *dig_connector; + struct radeon_dp_link_train_info dp_info; + int index; + u8 tmp, frev, crev; + + if (!radeon_encoder->enc_priv) + return; + dig = radeon_encoder->enc_priv; + + radeon_connector = to_radeon_connector(connector); + if (!radeon_connector->con_priv) + return; + dig_connector = radeon_connector->con_priv; + + if ((dig_connector->dp_sink_type != CONNECTOR_OBJECT_ID_DISPLAYPORT) && + (dig_connector->dp_sink_type != CONNECTOR_OBJECT_ID_eDP)) + return; + + /* DPEncoderService newer than 1.1 can't program properly the + * training pattern. When facing such version use the + * DIGXEncoderControl (X== 1 | 2) + */ + dp_info.use_dpencoder = true; + index = GetIndexIntoMasterTable(COMMAND, DPEncoderService); + if (atom_parse_cmd_header(rdev->mode_info.atom_context, index, &frev, &crev)) { + if (crev > 1) { + dp_info.use_dpencoder = false; + } + } + + dp_info.enc_id = 0; + if (dig->dig_encoder) + dp_info.enc_id |= ATOM_DP_CONFIG_DIG2_ENCODER; + else + dp_info.enc_id |= ATOM_DP_CONFIG_DIG1_ENCODER; + if (dig->linkb) + dp_info.enc_id |= ATOM_DP_CONFIG_LINK_B; + else + dp_info.enc_id |= ATOM_DP_CONFIG_LINK_A; + + tmp = radeon_read_dpcd_reg(radeon_connector, DP_MAX_LANE_COUNT); + if (ASIC_IS_DCE5(rdev) && (tmp & DP_TPS3_SUPPORTED)) + dp_info.tp3_supported = true; + else + dp_info.tp3_supported = false; + + memcpy(dp_info.dpcd, dig_connector->dpcd, DP_RECEIVER_CAP_SIZE); + dp_info.rdev = rdev; + dp_info.encoder = encoder; + dp_info.connector = connector; + dp_info.radeon_connector = radeon_connector; + dp_info.dp_lane_count = dig_connector->dp_lane_count; + dp_info.dp_clock = dig_connector->dp_clock; + + if (radeon_dp_link_train_init(&dp_info)) + goto done; + if (radeon_dp_link_train_cr(&dp_info)) + goto done; + if (radeon_dp_link_train_ce(&dp_info)) + goto done; +done: + if (radeon_dp_link_train_finish(&dp_info)) + return; +} diff --git a/sys/dev/drm2/radeon/atombios_encoders.c b/sys/dev/drm2/radeon/atombios_encoders.c new file mode 100644 index 00000000000..db23f137104 --- /dev/null +++ b/sys/dev/drm2/radeon/atombios_encoders.c @@ -0,0 +1,2660 @@ +/* + * Copyright 2007-11 Advanced Micro Devices, Inc. + * Copyright 2008 Red Hat Inc. + * + * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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. + * + * Authors: Dave Airlie + * Alex Deucher + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include "radeon.h" +#include "radeon_asic.h" /* Declares several prototypes; clang is pleased. */ +#include "atom.h" + +static u8 +radeon_atom_get_backlight_level_from_reg(struct radeon_device *rdev) +{ + u8 backlight_level; + u32 bios_2_scratch; + + if (rdev->family >= CHIP_R600) + bios_2_scratch = RREG32(R600_BIOS_2_SCRATCH); + else + bios_2_scratch = RREG32(RADEON_BIOS_2_SCRATCH); + + backlight_level = ((bios_2_scratch & ATOM_S2_CURRENT_BL_LEVEL_MASK) >> + ATOM_S2_CURRENT_BL_LEVEL_SHIFT); + + return backlight_level; +} + +static void +radeon_atom_set_backlight_level_to_reg(struct radeon_device *rdev, + u8 backlight_level) +{ + u32 bios_2_scratch; + + if (rdev->family >= CHIP_R600) + bios_2_scratch = RREG32(R600_BIOS_2_SCRATCH); + else + bios_2_scratch = RREG32(RADEON_BIOS_2_SCRATCH); + + bios_2_scratch &= ~ATOM_S2_CURRENT_BL_LEVEL_MASK; + bios_2_scratch |= ((backlight_level << ATOM_S2_CURRENT_BL_LEVEL_SHIFT) & + ATOM_S2_CURRENT_BL_LEVEL_MASK); + + if (rdev->family >= CHIP_R600) + WREG32(R600_BIOS_2_SCRATCH, bios_2_scratch); + else + WREG32(RADEON_BIOS_2_SCRATCH, bios_2_scratch); +} + +u8 +atombios_get_backlight_level(struct radeon_encoder *radeon_encoder) +{ + struct drm_device *dev = radeon_encoder->base.dev; + struct radeon_device *rdev = dev->dev_private; + + if (!(rdev->mode_info.firmware_flags & ATOM_BIOS_INFO_BL_CONTROLLED_BY_GPU)) + return 0; + + return radeon_atom_get_backlight_level_from_reg(rdev); +} + +void +atombios_set_backlight_level(struct radeon_encoder *radeon_encoder, u8 level) +{ + struct drm_encoder *encoder = &radeon_encoder->base; + struct drm_device *dev = radeon_encoder->base.dev; + struct radeon_device *rdev = dev->dev_private; + struct radeon_encoder_atom_dig *dig; + DISPLAY_DEVICE_OUTPUT_CONTROL_PS_ALLOCATION args; + int index; + + if (!(rdev->mode_info.firmware_flags & ATOM_BIOS_INFO_BL_CONTROLLED_BY_GPU)) + return; + + if ((radeon_encoder->devices & (ATOM_DEVICE_LCD_SUPPORT)) && + radeon_encoder->enc_priv) { + dig = radeon_encoder->enc_priv; + dig->backlight_level = level; + radeon_atom_set_backlight_level_to_reg(rdev, dig->backlight_level); + + switch (radeon_encoder->encoder_id) { + case ENCODER_OBJECT_ID_INTERNAL_LVDS: + case ENCODER_OBJECT_ID_INTERNAL_LVTM1: + index = GetIndexIntoMasterTable(COMMAND, LCD1OutputControl); + if (dig->backlight_level == 0) { + args.ucAction = ATOM_LCD_BLOFF; + atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args); + } else { + args.ucAction = ATOM_LCD_BL_BRIGHTNESS_CONTROL; + atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args); + args.ucAction = ATOM_LCD_BLON; + atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args); + } + break; + case ENCODER_OBJECT_ID_INTERNAL_UNIPHY: + case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA: + case ENCODER_OBJECT_ID_INTERNAL_UNIPHY1: + case ENCODER_OBJECT_ID_INTERNAL_UNIPHY2: + if (dig->backlight_level == 0) + atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_LCD_BLOFF, 0, 0); + else { + atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_BL_BRIGHTNESS_CONTROL, 0, 0); + atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_LCD_BLON, 0, 0); + } + break; + default: + break; + } + } +} + +#if defined(CONFIG_BACKLIGHT_CLASS_DEVICE) || defined(CONFIG_BACKLIGHT_CLASS_DEVICE_MODULE) + +static u8 radeon_atom_bl_level(struct backlight_device *bd) +{ + u8 level; + + /* Convert brightness to hardware level */ + if (bd->props.brightness < 0) + level = 0; + else if (bd->props.brightness > RADEON_MAX_BL_LEVEL) + level = RADEON_MAX_BL_LEVEL; + else + level = bd->props.brightness; + + return level; +} + +static int radeon_atom_backlight_update_status(struct backlight_device *bd) +{ + struct radeon_backlight_privdata *pdata = bl_get_data(bd); + struct radeon_encoder *radeon_encoder = pdata->encoder; + + atombios_set_backlight_level(radeon_encoder, radeon_atom_bl_level(bd)); + + return 0; +} + +static int radeon_atom_backlight_get_brightness(struct backlight_device *bd) +{ + struct radeon_backlight_privdata *pdata = bl_get_data(bd); + struct radeon_encoder *radeon_encoder = pdata->encoder; + struct drm_device *dev = radeon_encoder->base.dev; + struct radeon_device *rdev = dev->dev_private; + + return radeon_atom_get_backlight_level_from_reg(rdev); +} + +static const struct backlight_ops radeon_atom_backlight_ops = { + .get_brightness = radeon_atom_backlight_get_brightness, + .update_status = radeon_atom_backlight_update_status, +}; + +void radeon_atom_backlight_init(struct radeon_encoder *radeon_encoder, + struct drm_connector *drm_connector) +{ + struct drm_device *dev = radeon_encoder->base.dev; + struct radeon_device *rdev = dev->dev_private; + struct backlight_device *bd; + struct backlight_properties props; + struct radeon_backlight_privdata *pdata; + struct radeon_encoder_atom_dig *dig; + u8 backlight_level; + char bl_name[16]; + + if (!radeon_encoder->enc_priv) + return; + + if (!rdev->is_atom_bios) + return; + + if (!(rdev->mode_info.firmware_flags & ATOM_BIOS_INFO_BL_CONTROLLED_BY_GPU)) + return; + + pdata = malloc(sizeof(struct radeon_backlight_privdata), DRM_MEM_DRIVER, M_WAITOK); + if (!pdata) { + DRM_ERROR("Memory allocation failed\n"); + goto error; + } + + memset(&props, 0, sizeof(props)); + props.max_brightness = RADEON_MAX_BL_LEVEL; + props.type = BACKLIGHT_RAW; + snprintf(bl_name, sizeof(bl_name), + "radeon_bl%d", dev->primary->index); + bd = backlight_device_register(bl_name, &drm_connector->kdev, + pdata, &radeon_atom_backlight_ops, &props); + if (IS_ERR(bd)) { + DRM_ERROR("Backlight registration failed\n"); + goto error; + } + + pdata->encoder = radeon_encoder; + + backlight_level = radeon_atom_get_backlight_level_from_reg(rdev); + + dig = radeon_encoder->enc_priv; + dig->bl_dev = bd; + + bd->props.brightness = radeon_atom_backlight_get_brightness(bd); + bd->props.power = FB_BLANK_UNBLANK; + backlight_update_status(bd); + + DRM_INFO("radeon atom DIG backlight initialized\n"); + + return; + +error: + free(pdata, DRM_MEM_DRIVER); + return; +} + +static void radeon_atom_backlight_exit(struct radeon_encoder *radeon_encoder) +{ + struct drm_device *dev = radeon_encoder->base.dev; + struct radeon_device *rdev = dev->dev_private; + struct backlight_device *bd = NULL; + struct radeon_encoder_atom_dig *dig; + + if (!radeon_encoder->enc_priv) + return; + + if (!rdev->is_atom_bios) + return; + + if (!(rdev->mode_info.firmware_flags & ATOM_BIOS_INFO_BL_CONTROLLED_BY_GPU)) + return; + + dig = radeon_encoder->enc_priv; + bd = dig->bl_dev; + dig->bl_dev = NULL; + + if (bd) { + struct radeon_legacy_backlight_privdata *pdata; + + pdata = bl_get_data(bd); + backlight_device_unregister(bd); + free(pdata, DRM_MEM_DRIVER); + + DRM_INFO("radeon atom LVDS backlight unloaded\n"); + } +} + +#else /* !CONFIG_BACKLIGHT_CLASS_DEVICE */ + +void radeon_atom_backlight_init(struct radeon_encoder *radeon_encoder, + struct drm_connector *drm_connector) +{ +} + +static void radeon_atom_backlight_exit(struct radeon_encoder *encoder) +{ +} + +#endif + + +static inline bool radeon_encoder_is_digital(struct drm_encoder *encoder) +{ + struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); + switch (radeon_encoder->encoder_id) { + case ENCODER_OBJECT_ID_INTERNAL_LVDS: + case ENCODER_OBJECT_ID_INTERNAL_TMDS1: + case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_TMDS1: + case ENCODER_OBJECT_ID_INTERNAL_LVTM1: + case ENCODER_OBJECT_ID_INTERNAL_DVO1: + case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DVO1: + case ENCODER_OBJECT_ID_INTERNAL_DDI: + case ENCODER_OBJECT_ID_INTERNAL_UNIPHY: + case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA: + case ENCODER_OBJECT_ID_INTERNAL_UNIPHY1: + case ENCODER_OBJECT_ID_INTERNAL_UNIPHY2: + return true; + default: + return false; + } +} + +static bool radeon_atom_mode_fixup(struct drm_encoder *encoder, + const struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); + struct drm_device *dev = encoder->dev; + struct radeon_device *rdev = dev->dev_private; + + /* set the active encoder to connector routing */ + radeon_encoder_set_active_device(encoder); + drm_mode_set_crtcinfo(adjusted_mode, 0); + + /* hw bug */ + if ((mode->flags & DRM_MODE_FLAG_INTERLACE) + && (mode->crtc_vsync_start < (mode->crtc_vdisplay + 2))) + adjusted_mode->crtc_vsync_start = adjusted_mode->crtc_vdisplay + 2; + + /* get the native mode for LVDS */ + if (radeon_encoder->active_device & (ATOM_DEVICE_LCD_SUPPORT)) + radeon_panel_mode_fixup(encoder, adjusted_mode); + + /* get the native mode for TV */ + if (radeon_encoder->active_device & (ATOM_DEVICE_TV_SUPPORT)) { + struct radeon_encoder_atom_dac *tv_dac = radeon_encoder->enc_priv; + if (tv_dac) { + if (tv_dac->tv_std == TV_STD_NTSC || + tv_dac->tv_std == TV_STD_NTSC_J || + tv_dac->tv_std == TV_STD_PAL_M) + radeon_atom_get_tv_timings(rdev, 0, adjusted_mode); + else + radeon_atom_get_tv_timings(rdev, 1, adjusted_mode); + } + } + + if (ASIC_IS_DCE3(rdev) && + ((radeon_encoder->active_device & (ATOM_DEVICE_DFP_SUPPORT | ATOM_DEVICE_LCD_SUPPORT)) || + (radeon_encoder_get_dp_bridge_encoder_id(encoder) != ENCODER_OBJECT_ID_NONE))) { + struct drm_connector *connector = radeon_get_connector_for_encoder(encoder); + radeon_dp_set_link_config(connector, adjusted_mode); + } + + return true; +} + +static void +atombios_dac_setup(struct drm_encoder *encoder, int action) +{ + struct drm_device *dev = encoder->dev; + struct radeon_device *rdev = dev->dev_private; + struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); + DAC_ENCODER_CONTROL_PS_ALLOCATION args; + int index = 0; + struct radeon_encoder_atom_dac *dac_info = radeon_encoder->enc_priv; + + memset(&args, 0, sizeof(args)); + + switch (radeon_encoder->encoder_id) { + case ENCODER_OBJECT_ID_INTERNAL_DAC1: + case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC1: + index = GetIndexIntoMasterTable(COMMAND, DAC1EncoderControl); + break; + case ENCODER_OBJECT_ID_INTERNAL_DAC2: + case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC2: + index = GetIndexIntoMasterTable(COMMAND, DAC2EncoderControl); + break; + } + + args.ucAction = action; + + if (radeon_encoder->active_device & (ATOM_DEVICE_CRT_SUPPORT)) + args.ucDacStandard = ATOM_DAC1_PS2; + else if (radeon_encoder->active_device & (ATOM_DEVICE_CV_SUPPORT)) + args.ucDacStandard = ATOM_DAC1_CV; + else { + switch (dac_info->tv_std) { + case TV_STD_PAL: + case TV_STD_PAL_M: + case TV_STD_SCART_PAL: + case TV_STD_SECAM: + case TV_STD_PAL_CN: + args.ucDacStandard = ATOM_DAC1_PAL; + break; + case TV_STD_NTSC: + case TV_STD_NTSC_J: + case TV_STD_PAL_60: + default: + args.ucDacStandard = ATOM_DAC1_NTSC; + break; + } + } + args.usPixelClock = cpu_to_le16(radeon_encoder->pixel_clock / 10); + + atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args); + +} + +static void +atombios_tv_setup(struct drm_encoder *encoder, int action) +{ + struct drm_device *dev = encoder->dev; + struct radeon_device *rdev = dev->dev_private; + struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); + TV_ENCODER_CONTROL_PS_ALLOCATION args; + int index = 0; + struct radeon_encoder_atom_dac *dac_info = radeon_encoder->enc_priv; + + memset(&args, 0, sizeof(args)); + + index = GetIndexIntoMasterTable(COMMAND, TVEncoderControl); + + args.sTVEncoder.ucAction = action; + + if (radeon_encoder->active_device & (ATOM_DEVICE_CV_SUPPORT)) + args.sTVEncoder.ucTvStandard = ATOM_TV_CV; + else { + switch (dac_info->tv_std) { + case TV_STD_NTSC: + args.sTVEncoder.ucTvStandard = ATOM_TV_NTSC; + break; + case TV_STD_PAL: + args.sTVEncoder.ucTvStandard = ATOM_TV_PAL; + break; + case TV_STD_PAL_M: + args.sTVEncoder.ucTvStandard = ATOM_TV_PALM; + break; + case TV_STD_PAL_60: + args.sTVEncoder.ucTvStandard = ATOM_TV_PAL60; + break; + case TV_STD_NTSC_J: + args.sTVEncoder.ucTvStandard = ATOM_TV_NTSCJ; + break; + case TV_STD_SCART_PAL: + args.sTVEncoder.ucTvStandard = ATOM_TV_PAL; /* ??? */ + break; + case TV_STD_SECAM: + args.sTVEncoder.ucTvStandard = ATOM_TV_SECAM; + break; + case TV_STD_PAL_CN: + args.sTVEncoder.ucTvStandard = ATOM_TV_PALCN; + break; + default: + args.sTVEncoder.ucTvStandard = ATOM_TV_NTSC; + break; + } + } + + args.sTVEncoder.usPixelClock = cpu_to_le16(radeon_encoder->pixel_clock / 10); + + atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args); + +} + +static u8 radeon_atom_get_bpc(struct drm_encoder *encoder) +{ + struct drm_connector *connector = radeon_get_connector_for_encoder(encoder); + int bpc = 8; + + if (connector) + bpc = radeon_get_monitor_bpc(connector); + + switch (bpc) { + case 0: + return PANEL_BPC_UNDEFINE; + case 6: + return PANEL_6BIT_PER_COLOR; + case 8: + default: + return PANEL_8BIT_PER_COLOR; + case 10: + return PANEL_10BIT_PER_COLOR; + case 12: + return PANEL_12BIT_PER_COLOR; + case 16: + return PANEL_16BIT_PER_COLOR; + } +} + + +union dvo_encoder_control { + ENABLE_EXTERNAL_TMDS_ENCODER_PS_ALLOCATION ext_tmds; + DVO_ENCODER_CONTROL_PS_ALLOCATION dvo; + DVO_ENCODER_CONTROL_PS_ALLOCATION_V3 dvo_v3; +}; + +void +atombios_dvo_setup(struct drm_encoder *encoder, int action) +{ + struct drm_device *dev = encoder->dev; + struct radeon_device *rdev = dev->dev_private; + struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); + union dvo_encoder_control args; + int index = GetIndexIntoMasterTable(COMMAND, DVOEncoderControl); + uint8_t frev, crev; + + memset(&args, 0, sizeof(args)); + + if (!atom_parse_cmd_header(rdev->mode_info.atom_context, index, &frev, &crev)) + return; + + /* some R4xx chips have the wrong frev */ + if (rdev->family <= CHIP_RV410) + frev = 1; + + switch (frev) { + case 1: + switch (crev) { + case 1: + /* R4xx, R5xx */ + args.ext_tmds.sXTmdsEncoder.ucEnable = action; + + if (radeon_dig_monitor_is_duallink(encoder, radeon_encoder->pixel_clock)) + args.ext_tmds.sXTmdsEncoder.ucMisc |= PANEL_ENCODER_MISC_DUAL; + + args.ext_tmds.sXTmdsEncoder.ucMisc |= ATOM_PANEL_MISC_888RGB; + break; + case 2: + /* RS600/690/740 */ + args.dvo.sDVOEncoder.ucAction = action; + args.dvo.sDVOEncoder.usPixelClock = cpu_to_le16(radeon_encoder->pixel_clock / 10); + /* DFP1, CRT1, TV1 depending on the type of port */ + args.dvo.sDVOEncoder.ucDeviceType = ATOM_DEVICE_DFP1_INDEX; + + if (radeon_dig_monitor_is_duallink(encoder, radeon_encoder->pixel_clock)) + args.dvo.sDVOEncoder.usDevAttr.sDigAttrib.ucAttribute |= PANEL_ENCODER_MISC_DUAL; + break; + case 3: + /* R6xx */ + args.dvo_v3.ucAction = action; + args.dvo_v3.usPixelClock = cpu_to_le16(radeon_encoder->pixel_clock / 10); + args.dvo_v3.ucDVOConfig = 0; /* XXX */ + break; + default: + DRM_ERROR("Unknown table version %d, %d\n", frev, crev); + break; + } + break; + default: + DRM_ERROR("Unknown table version %d, %d\n", frev, crev); + break; + } + + atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args); +} + +union lvds_encoder_control { + LVDS_ENCODER_CONTROL_PS_ALLOCATION v1; + LVDS_ENCODER_CONTROL_PS_ALLOCATION_V2 v2; +}; + +void +atombios_digital_setup(struct drm_encoder *encoder, int action) +{ + struct drm_device *dev = encoder->dev; + struct radeon_device *rdev = dev->dev_private; + struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); + struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv; + union lvds_encoder_control args; + int index = 0; + int hdmi_detected = 0; + uint8_t frev, crev; + + if (!dig) + return; + + if (atombios_get_encoder_mode(encoder) == ATOM_ENCODER_MODE_HDMI) + hdmi_detected = 1; + + memset(&args, 0, sizeof(args)); + + switch (radeon_encoder->encoder_id) { + case ENCODER_OBJECT_ID_INTERNAL_LVDS: + index = GetIndexIntoMasterTable(COMMAND, LVDSEncoderControl); + break; + case ENCODER_OBJECT_ID_INTERNAL_TMDS1: + case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_TMDS1: + index = GetIndexIntoMasterTable(COMMAND, TMDS1EncoderControl); + break; + case ENCODER_OBJECT_ID_INTERNAL_LVTM1: + if (radeon_encoder->devices & (ATOM_DEVICE_LCD_SUPPORT)) + index = GetIndexIntoMasterTable(COMMAND, LVDSEncoderControl); + else + index = GetIndexIntoMasterTable(COMMAND, TMDS2EncoderControl); + break; + } + + if (!atom_parse_cmd_header(rdev->mode_info.atom_context, index, &frev, &crev)) + return; + + switch (frev) { + case 1: + case 2: + switch (crev) { + case 1: + args.v1.ucMisc = 0; + args.v1.ucAction = action; + if (hdmi_detected) + args.v1.ucMisc |= PANEL_ENCODER_MISC_HDMI_TYPE; + args.v1.usPixelClock = cpu_to_le16(radeon_encoder->pixel_clock / 10); + if (radeon_encoder->devices & (ATOM_DEVICE_LCD_SUPPORT)) { + if (dig->lcd_misc & ATOM_PANEL_MISC_DUAL) + args.v1.ucMisc |= PANEL_ENCODER_MISC_DUAL; + if (dig->lcd_misc & ATOM_PANEL_MISC_888RGB) + args.v1.ucMisc |= ATOM_PANEL_MISC_888RGB; + } else { + if (dig->linkb) + args.v1.ucMisc |= PANEL_ENCODER_MISC_TMDS_LINKB; + if (radeon_dig_monitor_is_duallink(encoder, radeon_encoder->pixel_clock)) + args.v1.ucMisc |= PANEL_ENCODER_MISC_DUAL; + /*if (pScrn->rgbBits == 8) */ + args.v1.ucMisc |= ATOM_PANEL_MISC_888RGB; + } + break; + case 2: + case 3: + args.v2.ucMisc = 0; + args.v2.ucAction = action; + if (crev == 3) { + if (dig->coherent_mode) + args.v2.ucMisc |= PANEL_ENCODER_MISC_COHERENT; + } + if (hdmi_detected) + args.v2.ucMisc |= PANEL_ENCODER_MISC_HDMI_TYPE; + args.v2.usPixelClock = cpu_to_le16(radeon_encoder->pixel_clock / 10); + args.v2.ucTruncate = 0; + args.v2.ucSpatial = 0; + args.v2.ucTemporal = 0; + args.v2.ucFRC = 0; + if (radeon_encoder->devices & (ATOM_DEVICE_LCD_SUPPORT)) { + if (dig->lcd_misc & ATOM_PANEL_MISC_DUAL) + args.v2.ucMisc |= PANEL_ENCODER_MISC_DUAL; + if (dig->lcd_misc & ATOM_PANEL_MISC_SPATIAL) { + args.v2.ucSpatial = PANEL_ENCODER_SPATIAL_DITHER_EN; + if (dig->lcd_misc & ATOM_PANEL_MISC_888RGB) + args.v2.ucSpatial |= PANEL_ENCODER_SPATIAL_DITHER_DEPTH; + } + if (dig->lcd_misc & ATOM_PANEL_MISC_TEMPORAL) { + args.v2.ucTemporal = PANEL_ENCODER_TEMPORAL_DITHER_EN; + if (dig->lcd_misc & ATOM_PANEL_MISC_888RGB) + args.v2.ucTemporal |= PANEL_ENCODER_TEMPORAL_DITHER_DEPTH; + if (((dig->lcd_misc >> ATOM_PANEL_MISC_GREY_LEVEL_SHIFT) & 0x3) == 2) + args.v2.ucTemporal |= PANEL_ENCODER_TEMPORAL_LEVEL_4; + } + } else { + if (dig->linkb) + args.v2.ucMisc |= PANEL_ENCODER_MISC_TMDS_LINKB; + if (radeon_dig_monitor_is_duallink(encoder, radeon_encoder->pixel_clock)) + args.v2.ucMisc |= PANEL_ENCODER_MISC_DUAL; + } + break; + default: + DRM_ERROR("Unknown table version %d, %d\n", frev, crev); + break; + } + break; + default: + DRM_ERROR("Unknown table version %d, %d\n", frev, crev); + break; + } + + atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args); +} + +int +atombios_get_encoder_mode(struct drm_encoder *encoder) +{ + struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); + struct drm_connector *connector; + struct radeon_connector *radeon_connector; + struct radeon_connector_atom_dig *dig_connector; + + /* dp bridges are always DP */ + if (radeon_encoder_get_dp_bridge_encoder_id(encoder) != ENCODER_OBJECT_ID_NONE) + return ATOM_ENCODER_MODE_DP; + + /* DVO is always DVO */ + if ((radeon_encoder->encoder_id == ENCODER_OBJECT_ID_INTERNAL_DVO1) || + (radeon_encoder->encoder_id == ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DVO1)) + return ATOM_ENCODER_MODE_DVO; + + connector = radeon_get_connector_for_encoder(encoder); + /* if we don't have an active device yet, just use one of + * the connectors tied to the encoder. + */ + if (!connector) + connector = radeon_get_connector_for_encoder_init(encoder); + radeon_connector = to_radeon_connector(connector); + + switch (connector->connector_type) { + case DRM_MODE_CONNECTOR_DVII: + case DRM_MODE_CONNECTOR_HDMIB: /* HDMI-B is basically DL-DVI; analog works fine */ + if (drm_detect_hdmi_monitor(radeon_connector->edid) && + radeon_audio) + return ATOM_ENCODER_MODE_HDMI; + else if (radeon_connector->use_digital) + return ATOM_ENCODER_MODE_DVI; + else + return ATOM_ENCODER_MODE_CRT; + break; + case DRM_MODE_CONNECTOR_DVID: + case DRM_MODE_CONNECTOR_HDMIA: + default: + if (drm_detect_hdmi_monitor(radeon_connector->edid) && + radeon_audio) + return ATOM_ENCODER_MODE_HDMI; + else + return ATOM_ENCODER_MODE_DVI; + break; + case DRM_MODE_CONNECTOR_LVDS: + return ATOM_ENCODER_MODE_LVDS; + break; + case DRM_MODE_CONNECTOR_DisplayPort: + dig_connector = radeon_connector->con_priv; + if ((dig_connector->dp_sink_type == CONNECTOR_OBJECT_ID_DISPLAYPORT) || + (dig_connector->dp_sink_type == CONNECTOR_OBJECT_ID_eDP)) + return ATOM_ENCODER_MODE_DP; + else if (drm_detect_hdmi_monitor(radeon_connector->edid) && + radeon_audio) + return ATOM_ENCODER_MODE_HDMI; + else + return ATOM_ENCODER_MODE_DVI; + break; + case DRM_MODE_CONNECTOR_eDP: + return ATOM_ENCODER_MODE_DP; + case DRM_MODE_CONNECTOR_DVIA: + case DRM_MODE_CONNECTOR_VGA: + return ATOM_ENCODER_MODE_CRT; + break; + case DRM_MODE_CONNECTOR_Composite: + case DRM_MODE_CONNECTOR_SVIDEO: + case DRM_MODE_CONNECTOR_9PinDIN: + /* fix me */ + return ATOM_ENCODER_MODE_TV; + /*return ATOM_ENCODER_MODE_CV;*/ + break; + } +} + +/* + * DIG Encoder/Transmitter Setup + * + * DCE 3.0/3.1 + * - 2 DIG transmitter blocks. UNIPHY (links A and B) and LVTMA. + * Supports up to 3 digital outputs + * - 2 DIG encoder blocks. + * DIG1 can drive UNIPHY link A or link B + * DIG2 can drive UNIPHY link B or LVTMA + * + * DCE 3.2 + * - 3 DIG transmitter blocks. UNIPHY0/1/2 (links A and B). + * Supports up to 5 digital outputs + * - 2 DIG encoder blocks. + * DIG1/2 can drive UNIPHY0/1/2 link A or link B + * + * DCE 4.0/5.0/6.0 + * - 3 DIG transmitter blocks UNIPHY0/1/2 (links A and B). + * Supports up to 6 digital outputs + * - 6 DIG encoder blocks. + * - DIG to PHY mapping is hardcoded + * DIG1 drives UNIPHY0 link A, A+B + * DIG2 drives UNIPHY0 link B + * DIG3 drives UNIPHY1 link A, A+B + * DIG4 drives UNIPHY1 link B + * DIG5 drives UNIPHY2 link A, A+B + * DIG6 drives UNIPHY2 link B + * + * DCE 4.1 + * - 3 DIG transmitter blocks UNIPHY0/1/2 (links A and B). + * Supports up to 6 digital outputs + * - 2 DIG encoder blocks. + * llano + * DIG1/2 can drive UNIPHY0/1/2 link A or link B + * ontario + * DIG1 drives UNIPHY0/1/2 link A + * DIG2 drives UNIPHY0/1/2 link B + * + * Routing + * crtc -> dig encoder -> UNIPHY/LVTMA (1 or 2 links) + * Examples: + * crtc0 -> dig2 -> LVTMA links A+B -> TMDS/HDMI + * crtc1 -> dig1 -> UNIPHY0 link B -> DP + * crtc0 -> dig1 -> UNIPHY2 link A -> LVDS + * crtc1 -> dig2 -> UNIPHY1 link B+A -> TMDS/HDMI + */ + +union dig_encoder_control { + DIG_ENCODER_CONTROL_PS_ALLOCATION v1; + DIG_ENCODER_CONTROL_PARAMETERS_V2 v2; + DIG_ENCODER_CONTROL_PARAMETERS_V3 v3; + DIG_ENCODER_CONTROL_PARAMETERS_V4 v4; +}; + +void +atombios_dig_encoder_setup(struct drm_encoder *encoder, int action, int panel_mode) +{ + struct drm_device *dev = encoder->dev; + struct radeon_device *rdev = dev->dev_private; + struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); + struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv; + struct drm_connector *connector = radeon_get_connector_for_encoder(encoder); + union dig_encoder_control args; + int index = 0; + uint8_t frev, crev; + int dp_clock = 0; + int dp_lane_count = 0; + int hpd_id = RADEON_HPD_NONE; + + if (connector) { + struct radeon_connector *radeon_connector = to_radeon_connector(connector); + struct radeon_connector_atom_dig *dig_connector = + radeon_connector->con_priv; + + dp_clock = dig_connector->dp_clock; + dp_lane_count = dig_connector->dp_lane_count; + hpd_id = radeon_connector->hpd.hpd; + } + + /* no dig encoder assigned */ + if (dig->dig_encoder == -1) + return; + + memset(&args, 0, sizeof(args)); + + if (ASIC_IS_DCE4(rdev)) + index = GetIndexIntoMasterTable(COMMAND, DIGxEncoderControl); + else { + if (dig->dig_encoder) + index = GetIndexIntoMasterTable(COMMAND, DIG2EncoderControl); + else + index = GetIndexIntoMasterTable(COMMAND, DIG1EncoderControl); + } + + if (!atom_parse_cmd_header(rdev->mode_info.atom_context, index, &frev, &crev)) + return; + + switch (frev) { + case 1: + switch (crev) { + case 1: + args.v1.ucAction = action; + args.v1.usPixelClock = cpu_to_le16(radeon_encoder->pixel_clock / 10); + if (action == ATOM_ENCODER_CMD_SETUP_PANEL_MODE) + args.v3.ucPanelMode = panel_mode; + else + args.v1.ucEncoderMode = atombios_get_encoder_mode(encoder); + + if (ENCODER_MODE_IS_DP(args.v1.ucEncoderMode)) + args.v1.ucLaneNum = dp_lane_count; + else if (radeon_dig_monitor_is_duallink(encoder, radeon_encoder->pixel_clock)) + args.v1.ucLaneNum = 8; + else + args.v1.ucLaneNum = 4; + + if (ENCODER_MODE_IS_DP(args.v1.ucEncoderMode) && (dp_clock == 270000)) + args.v1.ucConfig |= ATOM_ENCODER_CONFIG_DPLINKRATE_2_70GHZ; + switch (radeon_encoder->encoder_id) { + case ENCODER_OBJECT_ID_INTERNAL_UNIPHY: + args.v1.ucConfig = ATOM_ENCODER_CONFIG_V2_TRANSMITTER1; + break; + case ENCODER_OBJECT_ID_INTERNAL_UNIPHY1: + case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA: + args.v1.ucConfig = ATOM_ENCODER_CONFIG_V2_TRANSMITTER2; + break; + case ENCODER_OBJECT_ID_INTERNAL_UNIPHY2: + args.v1.ucConfig = ATOM_ENCODER_CONFIG_V2_TRANSMITTER3; + break; + } + if (dig->linkb) + args.v1.ucConfig |= ATOM_ENCODER_CONFIG_LINKB; + else + args.v1.ucConfig |= ATOM_ENCODER_CONFIG_LINKA; + break; + case 2: + case 3: + args.v3.ucAction = action; + args.v3.usPixelClock = cpu_to_le16(radeon_encoder->pixel_clock / 10); + if (action == ATOM_ENCODER_CMD_SETUP_PANEL_MODE) + args.v3.ucPanelMode = panel_mode; + else + args.v3.ucEncoderMode = atombios_get_encoder_mode(encoder); + + if (ENCODER_MODE_IS_DP(args.v3.ucEncoderMode)) + args.v3.ucLaneNum = dp_lane_count; + else if (radeon_dig_monitor_is_duallink(encoder, radeon_encoder->pixel_clock)) + args.v3.ucLaneNum = 8; + else + args.v3.ucLaneNum = 4; + + if (ENCODER_MODE_IS_DP(args.v3.ucEncoderMode) && (dp_clock == 270000)) + args.v1.ucConfig |= ATOM_ENCODER_CONFIG_V3_DPLINKRATE_2_70GHZ; + args.v3.acConfig.ucDigSel = dig->dig_encoder; + args.v3.ucBitPerColor = radeon_atom_get_bpc(encoder); + break; + case 4: + args.v4.ucAction = action; + args.v4.usPixelClock = cpu_to_le16(radeon_encoder->pixel_clock / 10); + if (action == ATOM_ENCODER_CMD_SETUP_PANEL_MODE) + args.v4.ucPanelMode = panel_mode; + else + args.v4.ucEncoderMode = atombios_get_encoder_mode(encoder); + + if (ENCODER_MODE_IS_DP(args.v4.ucEncoderMode)) + args.v4.ucLaneNum = dp_lane_count; + else if (radeon_dig_monitor_is_duallink(encoder, radeon_encoder->pixel_clock)) + args.v4.ucLaneNum = 8; + else + args.v4.ucLaneNum = 4; + + if (ENCODER_MODE_IS_DP(args.v4.ucEncoderMode)) { + if (dp_clock == 270000) + args.v1.ucConfig |= ATOM_ENCODER_CONFIG_V4_DPLINKRATE_2_70GHZ; + else if (dp_clock == 540000) + args.v1.ucConfig |= ATOM_ENCODER_CONFIG_V4_DPLINKRATE_5_40GHZ; + } + args.v4.acConfig.ucDigSel = dig->dig_encoder; + args.v4.ucBitPerColor = radeon_atom_get_bpc(encoder); + if (hpd_id == RADEON_HPD_NONE) + args.v4.ucHPD_ID = 0; + else + args.v4.ucHPD_ID = hpd_id + 1; + break; + default: + DRM_ERROR("Unknown table version %d, %d\n", frev, crev); + break; + } + break; + default: + DRM_ERROR("Unknown table version %d, %d\n", frev, crev); + break; + } + + atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args); + +} + +union dig_transmitter_control { + DIG_TRANSMITTER_CONTROL_PS_ALLOCATION v1; + DIG_TRANSMITTER_CONTROL_PARAMETERS_V2 v2; + DIG_TRANSMITTER_CONTROL_PARAMETERS_V3 v3; + DIG_TRANSMITTER_CONTROL_PARAMETERS_V4 v4; + DIG_TRANSMITTER_CONTROL_PARAMETERS_V1_5 v5; +}; + +void +atombios_dig_transmitter_setup(struct drm_encoder *encoder, int action, uint8_t lane_num, uint8_t lane_set) +{ + struct drm_device *dev = encoder->dev; + struct radeon_device *rdev = dev->dev_private; + struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); + struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv; + struct drm_connector *connector; + union dig_transmitter_control args; + int index = 0; + uint8_t frev, crev; + bool is_dp = false; + int pll_id = 0; + int dp_clock = 0; + int dp_lane_count = 0; + int connector_object_id = 0; + int igp_lane_info = 0; + int dig_encoder = dig->dig_encoder; + int hpd_id = RADEON_HPD_NONE; + + if (action == ATOM_TRANSMITTER_ACTION_INIT) { + connector = radeon_get_connector_for_encoder_init(encoder); + /* just needed to avoid bailing in the encoder check. the encoder + * isn't used for init + */ + dig_encoder = 0; + } else + connector = radeon_get_connector_for_encoder(encoder); + + if (connector) { + struct radeon_connector *radeon_connector = to_radeon_connector(connector); + struct radeon_connector_atom_dig *dig_connector = + radeon_connector->con_priv; + + hpd_id = radeon_connector->hpd.hpd; + dp_clock = dig_connector->dp_clock; + dp_lane_count = dig_connector->dp_lane_count; + connector_object_id = + (radeon_connector->connector_object_id & OBJECT_ID_MASK) >> OBJECT_ID_SHIFT; + igp_lane_info = dig_connector->igp_lane_info; + } + + if (encoder->crtc) { + struct radeon_crtc *radeon_crtc = to_radeon_crtc(encoder->crtc); + pll_id = radeon_crtc->pll_id; + } + + /* no dig encoder assigned */ + if (dig_encoder == -1) + return; + + if (ENCODER_MODE_IS_DP(atombios_get_encoder_mode(encoder))) + is_dp = true; + + memset(&args, 0, sizeof(args)); + + switch (radeon_encoder->encoder_id) { + case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DVO1: + index = GetIndexIntoMasterTable(COMMAND, DVOOutputControl); + break; + case ENCODER_OBJECT_ID_INTERNAL_UNIPHY: + case ENCODER_OBJECT_ID_INTERNAL_UNIPHY1: + case ENCODER_OBJECT_ID_INTERNAL_UNIPHY2: + index = GetIndexIntoMasterTable(COMMAND, UNIPHYTransmitterControl); + break; + case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA: + index = GetIndexIntoMasterTable(COMMAND, LVTMATransmitterControl); + break; + } + + if (!atom_parse_cmd_header(rdev->mode_info.atom_context, index, &frev, &crev)) + return; + + switch (frev) { + case 1: + switch (crev) { + case 1: + args.v1.ucAction = action; + if (action == ATOM_TRANSMITTER_ACTION_INIT) { + args.v1.usInitInfo = cpu_to_le16(connector_object_id); + } else if (action == ATOM_TRANSMITTER_ACTION_SETUP_VSEMPH) { + args.v1.asMode.ucLaneSel = lane_num; + args.v1.asMode.ucLaneSet = lane_set; + } else { + if (is_dp) + args.v1.usPixelClock = cpu_to_le16(dp_clock / 10); + else if (radeon_dig_monitor_is_duallink(encoder, radeon_encoder->pixel_clock)) + args.v1.usPixelClock = cpu_to_le16((radeon_encoder->pixel_clock / 2) / 10); + else + args.v1.usPixelClock = cpu_to_le16(radeon_encoder->pixel_clock / 10); + } + + args.v1.ucConfig = ATOM_TRANSMITTER_CONFIG_CLKSRC_PPLL; + + if (dig_encoder) + args.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_DIG2_ENCODER; + else + args.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_DIG1_ENCODER; + + if ((rdev->flags & RADEON_IS_IGP) && + (radeon_encoder->encoder_id == ENCODER_OBJECT_ID_INTERNAL_UNIPHY)) { + if (is_dp || + !radeon_dig_monitor_is_duallink(encoder, radeon_encoder->pixel_clock)) { + if (igp_lane_info & 0x1) + args.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_LANE_0_3; + else if (igp_lane_info & 0x2) + args.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_LANE_4_7; + else if (igp_lane_info & 0x4) + args.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_LANE_8_11; + else if (igp_lane_info & 0x8) + args.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_LANE_12_15; + } else { + if (igp_lane_info & 0x3) + args.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_LANE_0_7; + else if (igp_lane_info & 0xc) + args.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_LANE_8_15; + } + } + + if (dig->linkb) + args.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_LINKB; + else + args.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_LINKA; + + if (is_dp) + args.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_COHERENT; + else if (radeon_encoder->devices & (ATOM_DEVICE_DFP_SUPPORT)) { + if (dig->coherent_mode) + args.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_COHERENT; + if (radeon_dig_monitor_is_duallink(encoder, radeon_encoder->pixel_clock)) + args.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_8LANE_LINK; + } + break; + case 2: + args.v2.ucAction = action; + if (action == ATOM_TRANSMITTER_ACTION_INIT) { + args.v2.usInitInfo = cpu_to_le16(connector_object_id); + } else if (action == ATOM_TRANSMITTER_ACTION_SETUP_VSEMPH) { + args.v2.asMode.ucLaneSel = lane_num; + args.v2.asMode.ucLaneSet = lane_set; + } else { + if (is_dp) + args.v2.usPixelClock = cpu_to_le16(dp_clock / 10); + else if (radeon_dig_monitor_is_duallink(encoder, radeon_encoder->pixel_clock)) + args.v2.usPixelClock = cpu_to_le16((radeon_encoder->pixel_clock / 2) / 10); + else + args.v2.usPixelClock = cpu_to_le16(radeon_encoder->pixel_clock / 10); + } + + args.v2.acConfig.ucEncoderSel = dig_encoder; + if (dig->linkb) + args.v2.acConfig.ucLinkSel = 1; + + switch (radeon_encoder->encoder_id) { + case ENCODER_OBJECT_ID_INTERNAL_UNIPHY: + args.v2.acConfig.ucTransmitterSel = 0; + break; + case ENCODER_OBJECT_ID_INTERNAL_UNIPHY1: + args.v2.acConfig.ucTransmitterSel = 1; + break; + case ENCODER_OBJECT_ID_INTERNAL_UNIPHY2: + args.v2.acConfig.ucTransmitterSel = 2; + break; + } + + if (is_dp) { + args.v2.acConfig.fCoherentMode = 1; + args.v2.acConfig.fDPConnector = 1; + } else if (radeon_encoder->devices & (ATOM_DEVICE_DFP_SUPPORT)) { + if (dig->coherent_mode) + args.v2.acConfig.fCoherentMode = 1; + if (radeon_dig_monitor_is_duallink(encoder, radeon_encoder->pixel_clock)) + args.v2.acConfig.fDualLinkConnector = 1; + } + break; + case 3: + args.v3.ucAction = action; + if (action == ATOM_TRANSMITTER_ACTION_INIT) { + args.v3.usInitInfo = cpu_to_le16(connector_object_id); + } else if (action == ATOM_TRANSMITTER_ACTION_SETUP_VSEMPH) { + args.v3.asMode.ucLaneSel = lane_num; + args.v3.asMode.ucLaneSet = lane_set; + } else { + if (is_dp) + args.v3.usPixelClock = cpu_to_le16(dp_clock / 10); + else if (radeon_dig_monitor_is_duallink(encoder, radeon_encoder->pixel_clock)) + args.v3.usPixelClock = cpu_to_le16((radeon_encoder->pixel_clock / 2) / 10); + else + args.v3.usPixelClock = cpu_to_le16(radeon_encoder->pixel_clock / 10); + } + + if (is_dp) + args.v3.ucLaneNum = dp_lane_count; + else if (radeon_dig_monitor_is_duallink(encoder, radeon_encoder->pixel_clock)) + args.v3.ucLaneNum = 8; + else + args.v3.ucLaneNum = 4; + + if (dig->linkb) + args.v3.acConfig.ucLinkSel = 1; + if (dig_encoder & 1) + args.v3.acConfig.ucEncoderSel = 1; + + /* Select the PLL for the PHY + * DP PHY should be clocked from external src if there is + * one. + */ + /* On DCE4, if there is an external clock, it generates the DP ref clock */ + if (is_dp && rdev->clock.dp_extclk) + args.v3.acConfig.ucRefClkSource = 2; /* external src */ + else + args.v3.acConfig.ucRefClkSource = pll_id; + + switch (radeon_encoder->encoder_id) { + case ENCODER_OBJECT_ID_INTERNAL_UNIPHY: + args.v3.acConfig.ucTransmitterSel = 0; + break; + case ENCODER_OBJECT_ID_INTERNAL_UNIPHY1: + args.v3.acConfig.ucTransmitterSel = 1; + break; + case ENCODER_OBJECT_ID_INTERNAL_UNIPHY2: + args.v3.acConfig.ucTransmitterSel = 2; + break; + } + + if (is_dp) + args.v3.acConfig.fCoherentMode = 1; /* DP requires coherent */ + else if (radeon_encoder->devices & (ATOM_DEVICE_DFP_SUPPORT)) { + if (dig->coherent_mode) + args.v3.acConfig.fCoherentMode = 1; + if (radeon_dig_monitor_is_duallink(encoder, radeon_encoder->pixel_clock)) + args.v3.acConfig.fDualLinkConnector = 1; + } + break; + case 4: + args.v4.ucAction = action; + if (action == ATOM_TRANSMITTER_ACTION_INIT) { + args.v4.usInitInfo = cpu_to_le16(connector_object_id); + } else if (action == ATOM_TRANSMITTER_ACTION_SETUP_VSEMPH) { + args.v4.asMode.ucLaneSel = lane_num; + args.v4.asMode.ucLaneSet = lane_set; + } else { + if (is_dp) + args.v4.usPixelClock = cpu_to_le16(dp_clock / 10); + else if (radeon_dig_monitor_is_duallink(encoder, radeon_encoder->pixel_clock)) + args.v4.usPixelClock = cpu_to_le16((radeon_encoder->pixel_clock / 2) / 10); + else + args.v4.usPixelClock = cpu_to_le16(radeon_encoder->pixel_clock / 10); + } + + if (is_dp) + args.v4.ucLaneNum = dp_lane_count; + else if (radeon_dig_monitor_is_duallink(encoder, radeon_encoder->pixel_clock)) + args.v4.ucLaneNum = 8; + else + args.v4.ucLaneNum = 4; + + if (dig->linkb) + args.v4.acConfig.ucLinkSel = 1; + if (dig_encoder & 1) + args.v4.acConfig.ucEncoderSel = 1; + + /* Select the PLL for the PHY + * DP PHY should be clocked from external src if there is + * one. + */ + /* On DCE5 DCPLL usually generates the DP ref clock */ + if (is_dp) { + if (rdev->clock.dp_extclk) + args.v4.acConfig.ucRefClkSource = ENCODER_REFCLK_SRC_EXTCLK; + else + args.v4.acConfig.ucRefClkSource = ENCODER_REFCLK_SRC_DCPLL; + } else + args.v4.acConfig.ucRefClkSource = pll_id; + + switch (radeon_encoder->encoder_id) { + case ENCODER_OBJECT_ID_INTERNAL_UNIPHY: + args.v4.acConfig.ucTransmitterSel = 0; + break; + case ENCODER_OBJECT_ID_INTERNAL_UNIPHY1: + args.v4.acConfig.ucTransmitterSel = 1; + break; + case ENCODER_OBJECT_ID_INTERNAL_UNIPHY2: + args.v4.acConfig.ucTransmitterSel = 2; + break; + } + + if (is_dp) + args.v4.acConfig.fCoherentMode = 1; /* DP requires coherent */ + else if (radeon_encoder->devices & (ATOM_DEVICE_DFP_SUPPORT)) { + if (dig->coherent_mode) + args.v4.acConfig.fCoherentMode = 1; + if (radeon_dig_monitor_is_duallink(encoder, radeon_encoder->pixel_clock)) + args.v4.acConfig.fDualLinkConnector = 1; + } + break; + case 5: + args.v5.ucAction = action; + if (is_dp) + args.v5.usSymClock = cpu_to_le16(dp_clock / 10); + else + args.v5.usSymClock = cpu_to_le16(radeon_encoder->pixel_clock / 10); + + switch (radeon_encoder->encoder_id) { + case ENCODER_OBJECT_ID_INTERNAL_UNIPHY: + if (dig->linkb) + args.v5.ucPhyId = ATOM_PHY_ID_UNIPHYB; + else + args.v5.ucPhyId = ATOM_PHY_ID_UNIPHYA; + break; + case ENCODER_OBJECT_ID_INTERNAL_UNIPHY1: + if (dig->linkb) + args.v5.ucPhyId = ATOM_PHY_ID_UNIPHYD; + else + args.v5.ucPhyId = ATOM_PHY_ID_UNIPHYC; + break; + case ENCODER_OBJECT_ID_INTERNAL_UNIPHY2: + if (dig->linkb) + args.v5.ucPhyId = ATOM_PHY_ID_UNIPHYF; + else + args.v5.ucPhyId = ATOM_PHY_ID_UNIPHYE; + break; + } + if (is_dp) + args.v5.ucLaneNum = dp_lane_count; + else if (radeon_encoder->pixel_clock > 165000) + args.v5.ucLaneNum = 8; + else + args.v5.ucLaneNum = 4; + args.v5.ucConnObjId = connector_object_id; + args.v5.ucDigMode = atombios_get_encoder_mode(encoder); + + if (is_dp && rdev->clock.dp_extclk) + args.v5.asConfig.ucPhyClkSrcId = ENCODER_REFCLK_SRC_EXTCLK; + else + args.v5.asConfig.ucPhyClkSrcId = pll_id; + + if (is_dp) + args.v5.asConfig.ucCoherentMode = 1; /* DP requires coherent */ + else if (radeon_encoder->devices & (ATOM_DEVICE_DFP_SUPPORT)) { + if (dig->coherent_mode) + args.v5.asConfig.ucCoherentMode = 1; + } + if (hpd_id == RADEON_HPD_NONE) + args.v5.asConfig.ucHPDSel = 0; + else + args.v5.asConfig.ucHPDSel = hpd_id + 1; + args.v5.ucDigEncoderSel = 1 << dig_encoder; + args.v5.ucDPLaneSet = lane_set; + break; + default: + DRM_ERROR("Unknown table version %d, %d\n", frev, crev); + break; + } + break; + default: + DRM_ERROR("Unknown table version %d, %d\n", frev, crev); + break; + } + + atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args); +} + +bool +atombios_set_edp_panel_power(struct drm_connector *connector, int action) +{ + struct radeon_connector *radeon_connector = to_radeon_connector(connector); + struct drm_device *dev = radeon_connector->base.dev; + struct radeon_device *rdev = dev->dev_private; + union dig_transmitter_control args; + int index = GetIndexIntoMasterTable(COMMAND, UNIPHYTransmitterControl); + uint8_t frev, crev; + + if (connector->connector_type != DRM_MODE_CONNECTOR_eDP) + goto done; + + if (!ASIC_IS_DCE4(rdev)) + goto done; + + if ((action != ATOM_TRANSMITTER_ACTION_POWER_ON) && + (action != ATOM_TRANSMITTER_ACTION_POWER_OFF)) + goto done; + + if (!atom_parse_cmd_header(rdev->mode_info.atom_context, index, &frev, &crev)) + goto done; + + memset(&args, 0, sizeof(args)); + + args.v1.ucAction = action; + + atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args); + + /* wait for the panel to power up */ + if (action == ATOM_TRANSMITTER_ACTION_POWER_ON) { + int i; + + for (i = 0; i < 300; i++) { + if (radeon_hpd_sense(rdev, radeon_connector->hpd.hpd)) + return true; + DRM_MDELAY(1); + } + return false; + } +done: + return true; +} + +union external_encoder_control { + EXTERNAL_ENCODER_CONTROL_PS_ALLOCATION v1; + EXTERNAL_ENCODER_CONTROL_PS_ALLOCATION_V3 v3; +}; + +static void +atombios_external_encoder_setup(struct drm_encoder *encoder, + struct drm_encoder *ext_encoder, + int action) +{ + struct drm_device *dev = encoder->dev; + struct radeon_device *rdev = dev->dev_private; + struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); + struct radeon_encoder *ext_radeon_encoder = to_radeon_encoder(ext_encoder); + union external_encoder_control args; + struct drm_connector *connector; + int index = GetIndexIntoMasterTable(COMMAND, ExternalEncoderControl); + u8 frev, crev; + int dp_clock = 0; + int dp_lane_count = 0; + int connector_object_id = 0; + u32 ext_enum = (ext_radeon_encoder->encoder_enum & ENUM_ID_MASK) >> ENUM_ID_SHIFT; + + if (action == EXTERNAL_ENCODER_ACTION_V3_ENCODER_INIT) + connector = radeon_get_connector_for_encoder_init(encoder); + else + connector = radeon_get_connector_for_encoder(encoder); + + if (connector) { + struct radeon_connector *radeon_connector = to_radeon_connector(connector); + struct radeon_connector_atom_dig *dig_connector = + radeon_connector->con_priv; + + dp_clock = dig_connector->dp_clock; + dp_lane_count = dig_connector->dp_lane_count; + connector_object_id = + (radeon_connector->connector_object_id & OBJECT_ID_MASK) >> OBJECT_ID_SHIFT; + } + + memset(&args, 0, sizeof(args)); + + if (!atom_parse_cmd_header(rdev->mode_info.atom_context, index, &frev, &crev)) + return; + + switch (frev) { + case 1: + /* no params on frev 1 */ + break; + case 2: + switch (crev) { + case 1: + case 2: + args.v1.sDigEncoder.ucAction = action; + args.v1.sDigEncoder.usPixelClock = cpu_to_le16(radeon_encoder->pixel_clock / 10); + args.v1.sDigEncoder.ucEncoderMode = atombios_get_encoder_mode(encoder); + + if (ENCODER_MODE_IS_DP(args.v1.sDigEncoder.ucEncoderMode)) { + if (dp_clock == 270000) + args.v1.sDigEncoder.ucConfig |= ATOM_ENCODER_CONFIG_DPLINKRATE_2_70GHZ; + args.v1.sDigEncoder.ucLaneNum = dp_lane_count; + } else if (radeon_dig_monitor_is_duallink(encoder, radeon_encoder->pixel_clock)) + args.v1.sDigEncoder.ucLaneNum = 8; + else + args.v1.sDigEncoder.ucLaneNum = 4; + break; + case 3: + args.v3.sExtEncoder.ucAction = action; + if (action == EXTERNAL_ENCODER_ACTION_V3_ENCODER_INIT) + args.v3.sExtEncoder.usConnectorId = cpu_to_le16(connector_object_id); + else + args.v3.sExtEncoder.usPixelClock = cpu_to_le16(radeon_encoder->pixel_clock / 10); + args.v3.sExtEncoder.ucEncoderMode = atombios_get_encoder_mode(encoder); + + if (ENCODER_MODE_IS_DP(args.v3.sExtEncoder.ucEncoderMode)) { + if (dp_clock == 270000) + args.v3.sExtEncoder.ucConfig |= EXTERNAL_ENCODER_CONFIG_V3_DPLINKRATE_2_70GHZ; + else if (dp_clock == 540000) + args.v3.sExtEncoder.ucConfig |= EXTERNAL_ENCODER_CONFIG_V3_DPLINKRATE_5_40GHZ; + args.v3.sExtEncoder.ucLaneNum = dp_lane_count; + } else if (radeon_dig_monitor_is_duallink(encoder, radeon_encoder->pixel_clock)) + args.v3.sExtEncoder.ucLaneNum = 8; + else + args.v3.sExtEncoder.ucLaneNum = 4; + switch (ext_enum) { + case GRAPH_OBJECT_ENUM_ID1: + args.v3.sExtEncoder.ucConfig |= EXTERNAL_ENCODER_CONFIG_V3_ENCODER1; + break; + case GRAPH_OBJECT_ENUM_ID2: + args.v3.sExtEncoder.ucConfig |= EXTERNAL_ENCODER_CONFIG_V3_ENCODER2; + break; + case GRAPH_OBJECT_ENUM_ID3: + args.v3.sExtEncoder.ucConfig |= EXTERNAL_ENCODER_CONFIG_V3_ENCODER3; + break; + } + args.v3.sExtEncoder.ucBitPerColor = radeon_atom_get_bpc(encoder); + break; + default: + DRM_ERROR("Unknown table version: %d, %d\n", frev, crev); + return; + } + break; + default: + DRM_ERROR("Unknown table version: %d, %d\n", frev, crev); + return; + } + atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args); +} + +static void +atombios_yuv_setup(struct drm_encoder *encoder, bool enable) +{ + struct drm_device *dev = encoder->dev; + struct radeon_device *rdev = dev->dev_private; + struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); + struct radeon_crtc *radeon_crtc = to_radeon_crtc(encoder->crtc); + ENABLE_YUV_PS_ALLOCATION args; + int index = GetIndexIntoMasterTable(COMMAND, EnableYUV); + uint32_t temp, reg; + + memset(&args, 0, sizeof(args)); + + if (rdev->family >= CHIP_R600) + reg = R600_BIOS_3_SCRATCH; + else + reg = RADEON_BIOS_3_SCRATCH; + + /* XXX: fix up scratch reg handling */ + temp = RREG32(reg); + if (radeon_encoder->active_device & (ATOM_DEVICE_TV_SUPPORT)) + WREG32(reg, (ATOM_S3_TV1_ACTIVE | + (radeon_crtc->crtc_id << 18))); + else if (radeon_encoder->active_device & (ATOM_DEVICE_CV_SUPPORT)) + WREG32(reg, (ATOM_S3_CV_ACTIVE | (radeon_crtc->crtc_id << 24))); + else + WREG32(reg, 0); + + if (enable) + args.ucEnable = ATOM_ENABLE; + args.ucCRTC = radeon_crtc->crtc_id; + + atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args); + + WREG32(reg, temp); +} + +static void +radeon_atom_encoder_dpms_avivo(struct drm_encoder *encoder, int mode) +{ + struct drm_device *dev = encoder->dev; + struct radeon_device *rdev = dev->dev_private; + struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); + DISPLAY_DEVICE_OUTPUT_CONTROL_PS_ALLOCATION args; + int index = 0; + + memset(&args, 0, sizeof(args)); + + switch (radeon_encoder->encoder_id) { + case ENCODER_OBJECT_ID_INTERNAL_TMDS1: + case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_TMDS1: + index = GetIndexIntoMasterTable(COMMAND, TMDSAOutputControl); + break; + case ENCODER_OBJECT_ID_INTERNAL_DVO1: + case ENCODER_OBJECT_ID_INTERNAL_DDI: + case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DVO1: + index = GetIndexIntoMasterTable(COMMAND, DVOOutputControl); + break; + case ENCODER_OBJECT_ID_INTERNAL_LVDS: + index = GetIndexIntoMasterTable(COMMAND, LCD1OutputControl); + break; + case ENCODER_OBJECT_ID_INTERNAL_LVTM1: + if (radeon_encoder->devices & (ATOM_DEVICE_LCD_SUPPORT)) + index = GetIndexIntoMasterTable(COMMAND, LCD1OutputControl); + else + index = GetIndexIntoMasterTable(COMMAND, LVTMAOutputControl); + break; + case ENCODER_OBJECT_ID_INTERNAL_DAC1: + case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC1: + if (radeon_encoder->active_device & (ATOM_DEVICE_TV_SUPPORT)) + index = GetIndexIntoMasterTable(COMMAND, TV1OutputControl); + else if (radeon_encoder->active_device & (ATOM_DEVICE_CV_SUPPORT)) + index = GetIndexIntoMasterTable(COMMAND, CV1OutputControl); + else + index = GetIndexIntoMasterTable(COMMAND, DAC1OutputControl); + break; + case ENCODER_OBJECT_ID_INTERNAL_DAC2: + case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC2: + if (radeon_encoder->active_device & (ATOM_DEVICE_TV_SUPPORT)) + index = GetIndexIntoMasterTable(COMMAND, TV1OutputControl); + else if (radeon_encoder->active_device & (ATOM_DEVICE_CV_SUPPORT)) + index = GetIndexIntoMasterTable(COMMAND, CV1OutputControl); + else + index = GetIndexIntoMasterTable(COMMAND, DAC2OutputControl); + break; + default: + return; + } + + switch (mode) { + case DRM_MODE_DPMS_ON: + args.ucAction = ATOM_ENABLE; + /* workaround for DVOOutputControl on some RS690 systems */ + if (radeon_encoder->encoder_id == ENCODER_OBJECT_ID_INTERNAL_DDI) { + u32 reg = RREG32(RADEON_BIOS_3_SCRATCH); + WREG32(RADEON_BIOS_3_SCRATCH, reg & ~ATOM_S3_DFP2I_ACTIVE); + atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args); + WREG32(RADEON_BIOS_3_SCRATCH, reg); + } else + atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args); + if (radeon_encoder->devices & (ATOM_DEVICE_LCD_SUPPORT)) { + args.ucAction = ATOM_LCD_BLON; + atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args); + } + break; + case DRM_MODE_DPMS_STANDBY: + case DRM_MODE_DPMS_SUSPEND: + case DRM_MODE_DPMS_OFF: + args.ucAction = ATOM_DISABLE; + atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args); + if (radeon_encoder->devices & (ATOM_DEVICE_LCD_SUPPORT)) { + args.ucAction = ATOM_LCD_BLOFF; + atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args); + } + break; + } +} + +static void +radeon_atom_encoder_dpms_dig(struct drm_encoder *encoder, int mode) +{ + struct drm_device *dev = encoder->dev; + struct radeon_device *rdev = dev->dev_private; + struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); + struct drm_encoder *ext_encoder = radeon_get_external_encoder(encoder); + struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv; + struct drm_connector *connector = radeon_get_connector_for_encoder(encoder); + struct radeon_connector *radeon_connector = NULL; + struct radeon_connector_atom_dig *radeon_dig_connector = NULL; + + if (connector) { + radeon_connector = to_radeon_connector(connector); + radeon_dig_connector = radeon_connector->con_priv; + } + + switch (mode) { + case DRM_MODE_DPMS_ON: + if (ASIC_IS_DCE41(rdev) || ASIC_IS_DCE5(rdev)) { + if (!connector) + dig->panel_mode = DP_PANEL_MODE_EXTERNAL_DP_MODE; + else + dig->panel_mode = radeon_dp_get_panel_mode(encoder, connector); + + /* setup and enable the encoder */ + atombios_dig_encoder_setup(encoder, ATOM_ENCODER_CMD_SETUP, 0); + atombios_dig_encoder_setup(encoder, + ATOM_ENCODER_CMD_SETUP_PANEL_MODE, + dig->panel_mode); + if (ext_encoder) { + if (ASIC_IS_DCE41(rdev) || ASIC_IS_DCE61(rdev)) + atombios_external_encoder_setup(encoder, ext_encoder, + EXTERNAL_ENCODER_ACTION_V3_ENCODER_SETUP); + } + atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_ENABLE, 0, 0); + } else if (ASIC_IS_DCE4(rdev)) { + /* setup and enable the encoder */ + atombios_dig_encoder_setup(encoder, ATOM_ENCODER_CMD_SETUP, 0); + /* enable the transmitter */ + atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_ENABLE, 0, 0); + atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_ENABLE_OUTPUT, 0, 0); + } else { + /* setup and enable the encoder and transmitter */ + atombios_dig_encoder_setup(encoder, ATOM_ENABLE, 0); + atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_SETUP, 0, 0); + atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_ENABLE, 0, 0); + /* some early dce3.2 boards have a bug in their transmitter control table */ + if ((rdev->family != CHIP_RV710) && (rdev->family != CHIP_RV730)) + atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_ENABLE_OUTPUT, 0, 0); + } + if (ENCODER_MODE_IS_DP(atombios_get_encoder_mode(encoder)) && connector) { + if (connector->connector_type == DRM_MODE_CONNECTOR_eDP) { + atombios_set_edp_panel_power(connector, + ATOM_TRANSMITTER_ACTION_POWER_ON); + radeon_dig_connector->edp_on = true; + } + radeon_dp_link_train(encoder, connector); + if (ASIC_IS_DCE4(rdev)) + atombios_dig_encoder_setup(encoder, ATOM_ENCODER_CMD_DP_VIDEO_ON, 0); + } + if (radeon_encoder->devices & (ATOM_DEVICE_LCD_SUPPORT)) + atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_LCD_BLON, 0, 0); + break; + case DRM_MODE_DPMS_STANDBY: + case DRM_MODE_DPMS_SUSPEND: + case DRM_MODE_DPMS_OFF: + if (ASIC_IS_DCE41(rdev) || ASIC_IS_DCE5(rdev)) { + /* disable the transmitter */ + atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_DISABLE, 0, 0); + } else if (ASIC_IS_DCE4(rdev)) { + /* disable the transmitter */ + atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_DISABLE_OUTPUT, 0, 0); + atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_DISABLE, 0, 0); + } else { + /* disable the encoder and transmitter */ + atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_DISABLE_OUTPUT, 0, 0); + atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_DISABLE, 0, 0); + atombios_dig_encoder_setup(encoder, ATOM_DISABLE, 0); + } + if (ENCODER_MODE_IS_DP(atombios_get_encoder_mode(encoder)) && connector) { + if (ASIC_IS_DCE4(rdev)) + atombios_dig_encoder_setup(encoder, ATOM_ENCODER_CMD_DP_VIDEO_OFF, 0); + if (connector->connector_type == DRM_MODE_CONNECTOR_eDP) { + atombios_set_edp_panel_power(connector, + ATOM_TRANSMITTER_ACTION_POWER_OFF); + radeon_dig_connector->edp_on = false; + } + } + if (radeon_encoder->devices & (ATOM_DEVICE_LCD_SUPPORT)) + atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_LCD_BLOFF, 0, 0); + break; + } +} + +static void +radeon_atom_encoder_dpms_ext(struct drm_encoder *encoder, + struct drm_encoder *ext_encoder, + int mode) +{ + struct drm_device *dev = encoder->dev; + struct radeon_device *rdev = dev->dev_private; + + switch (mode) { + case DRM_MODE_DPMS_ON: + default: + if (ASIC_IS_DCE41(rdev) || ASIC_IS_DCE61(rdev)) { + atombios_external_encoder_setup(encoder, ext_encoder, + EXTERNAL_ENCODER_ACTION_V3_ENABLE_OUTPUT); + atombios_external_encoder_setup(encoder, ext_encoder, + EXTERNAL_ENCODER_ACTION_V3_ENCODER_BLANKING_OFF); + } else + atombios_external_encoder_setup(encoder, ext_encoder, ATOM_ENABLE); + break; + case DRM_MODE_DPMS_STANDBY: + case DRM_MODE_DPMS_SUSPEND: + case DRM_MODE_DPMS_OFF: + if (ASIC_IS_DCE41(rdev) || ASIC_IS_DCE61(rdev)) { + atombios_external_encoder_setup(encoder, ext_encoder, + EXTERNAL_ENCODER_ACTION_V3_ENCODER_BLANKING); + atombios_external_encoder_setup(encoder, ext_encoder, + EXTERNAL_ENCODER_ACTION_V3_DISABLE_OUTPUT); + } else + atombios_external_encoder_setup(encoder, ext_encoder, ATOM_DISABLE); + break; + } +} + +static void +radeon_atom_encoder_dpms(struct drm_encoder *encoder, int mode) +{ + struct drm_device *dev = encoder->dev; + struct radeon_device *rdev = dev->dev_private; + struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); + struct drm_encoder *ext_encoder = radeon_get_external_encoder(encoder); + + DRM_DEBUG_KMS("encoder dpms %d to mode %d, devices %08x, active_devices %08x\n", + radeon_encoder->encoder_id, mode, radeon_encoder->devices, + radeon_encoder->active_device); + switch (radeon_encoder->encoder_id) { + case ENCODER_OBJECT_ID_INTERNAL_TMDS1: + case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_TMDS1: + case ENCODER_OBJECT_ID_INTERNAL_LVDS: + case ENCODER_OBJECT_ID_INTERNAL_LVTM1: + case ENCODER_OBJECT_ID_INTERNAL_DVO1: + case ENCODER_OBJECT_ID_INTERNAL_DDI: + case ENCODER_OBJECT_ID_INTERNAL_DAC2: + case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC2: + radeon_atom_encoder_dpms_avivo(encoder, mode); + break; + case ENCODER_OBJECT_ID_INTERNAL_UNIPHY: + case ENCODER_OBJECT_ID_INTERNAL_UNIPHY1: + case ENCODER_OBJECT_ID_INTERNAL_UNIPHY2: + case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA: + radeon_atom_encoder_dpms_dig(encoder, mode); + break; + case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DVO1: + if (ASIC_IS_DCE5(rdev)) { + switch (mode) { + case DRM_MODE_DPMS_ON: + atombios_dvo_setup(encoder, ATOM_ENABLE); + break; + case DRM_MODE_DPMS_STANDBY: + case DRM_MODE_DPMS_SUSPEND: + case DRM_MODE_DPMS_OFF: + atombios_dvo_setup(encoder, ATOM_DISABLE); + break; + } + } else if (ASIC_IS_DCE3(rdev)) + radeon_atom_encoder_dpms_dig(encoder, mode); + else + radeon_atom_encoder_dpms_avivo(encoder, mode); + break; + case ENCODER_OBJECT_ID_INTERNAL_DAC1: + case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC1: + if (ASIC_IS_DCE5(rdev)) { + switch (mode) { + case DRM_MODE_DPMS_ON: + atombios_dac_setup(encoder, ATOM_ENABLE); + break; + case DRM_MODE_DPMS_STANDBY: + case DRM_MODE_DPMS_SUSPEND: + case DRM_MODE_DPMS_OFF: + atombios_dac_setup(encoder, ATOM_DISABLE); + break; + } + } else + radeon_atom_encoder_dpms_avivo(encoder, mode); + break; + default: + return; + } + + if (ext_encoder) + radeon_atom_encoder_dpms_ext(encoder, ext_encoder, mode); + + radeon_atombios_encoder_dpms_scratch_regs(encoder, (mode == DRM_MODE_DPMS_ON) ? true : false); + +} + +union crtc_source_param { + SELECT_CRTC_SOURCE_PS_ALLOCATION v1; + SELECT_CRTC_SOURCE_PARAMETERS_V2 v2; +}; + +static void +atombios_set_encoder_crtc_source(struct drm_encoder *encoder) +{ + struct drm_device *dev = encoder->dev; + struct radeon_device *rdev = dev->dev_private; + struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); + struct radeon_crtc *radeon_crtc = to_radeon_crtc(encoder->crtc); + union crtc_source_param args; + int index = GetIndexIntoMasterTable(COMMAND, SelectCRTC_Source); + uint8_t frev, crev; + struct radeon_encoder_atom_dig *dig; + + memset(&args, 0, sizeof(args)); + + if (!atom_parse_cmd_header(rdev->mode_info.atom_context, index, &frev, &crev)) + return; + + switch (frev) { + case 1: + switch (crev) { + case 1: + default: + if (ASIC_IS_AVIVO(rdev)) + args.v1.ucCRTC = radeon_crtc->crtc_id; + else { + if (radeon_encoder->encoder_id == ENCODER_OBJECT_ID_INTERNAL_DAC1) { + args.v1.ucCRTC = radeon_crtc->crtc_id; + } else { + args.v1.ucCRTC = radeon_crtc->crtc_id << 2; + } + } + switch (radeon_encoder->encoder_id) { + case ENCODER_OBJECT_ID_INTERNAL_TMDS1: + case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_TMDS1: + args.v1.ucDevice = ATOM_DEVICE_DFP1_INDEX; + break; + case ENCODER_OBJECT_ID_INTERNAL_LVDS: + case ENCODER_OBJECT_ID_INTERNAL_LVTM1: + if (radeon_encoder->devices & ATOM_DEVICE_LCD1_SUPPORT) + args.v1.ucDevice = ATOM_DEVICE_LCD1_INDEX; + else + args.v1.ucDevice = ATOM_DEVICE_DFP3_INDEX; + break; + case ENCODER_OBJECT_ID_INTERNAL_DVO1: + case ENCODER_OBJECT_ID_INTERNAL_DDI: + case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DVO1: + args.v1.ucDevice = ATOM_DEVICE_DFP2_INDEX; + break; + case ENCODER_OBJECT_ID_INTERNAL_DAC1: + case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC1: + if (radeon_encoder->active_device & (ATOM_DEVICE_TV_SUPPORT)) + args.v1.ucDevice = ATOM_DEVICE_TV1_INDEX; + else if (radeon_encoder->active_device & (ATOM_DEVICE_CV_SUPPORT)) + args.v1.ucDevice = ATOM_DEVICE_CV_INDEX; + else + args.v1.ucDevice = ATOM_DEVICE_CRT1_INDEX; + break; + case ENCODER_OBJECT_ID_INTERNAL_DAC2: + case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC2: + if (radeon_encoder->active_device & (ATOM_DEVICE_TV_SUPPORT)) + args.v1.ucDevice = ATOM_DEVICE_TV1_INDEX; + else if (radeon_encoder->active_device & (ATOM_DEVICE_CV_SUPPORT)) + args.v1.ucDevice = ATOM_DEVICE_CV_INDEX; + else + args.v1.ucDevice = ATOM_DEVICE_CRT2_INDEX; + break; + } + break; + case 2: + args.v2.ucCRTC = radeon_crtc->crtc_id; + if (radeon_encoder_get_dp_bridge_encoder_id(encoder) != ENCODER_OBJECT_ID_NONE) { + struct drm_connector *connector = radeon_get_connector_for_encoder(encoder); + + if (connector->connector_type == DRM_MODE_CONNECTOR_LVDS) + args.v2.ucEncodeMode = ATOM_ENCODER_MODE_LVDS; + else if (connector->connector_type == DRM_MODE_CONNECTOR_VGA) + args.v2.ucEncodeMode = ATOM_ENCODER_MODE_CRT; + else + args.v2.ucEncodeMode = atombios_get_encoder_mode(encoder); + } else + args.v2.ucEncodeMode = atombios_get_encoder_mode(encoder); + switch (radeon_encoder->encoder_id) { + case ENCODER_OBJECT_ID_INTERNAL_UNIPHY: + case ENCODER_OBJECT_ID_INTERNAL_UNIPHY1: + case ENCODER_OBJECT_ID_INTERNAL_UNIPHY2: + case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA: + dig = radeon_encoder->enc_priv; + switch (dig->dig_encoder) { + case 0: + args.v2.ucEncoderID = ASIC_INT_DIG1_ENCODER_ID; + break; + case 1: + args.v2.ucEncoderID = ASIC_INT_DIG2_ENCODER_ID; + break; + case 2: + args.v2.ucEncoderID = ASIC_INT_DIG3_ENCODER_ID; + break; + case 3: + args.v2.ucEncoderID = ASIC_INT_DIG4_ENCODER_ID; + break; + case 4: + args.v2.ucEncoderID = ASIC_INT_DIG5_ENCODER_ID; + break; + case 5: + args.v2.ucEncoderID = ASIC_INT_DIG6_ENCODER_ID; + break; + } + break; + case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DVO1: + args.v2.ucEncoderID = ASIC_INT_DVO_ENCODER_ID; + break; + case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC1: + if (radeon_encoder->active_device & (ATOM_DEVICE_TV_SUPPORT)) + args.v2.ucEncoderID = ASIC_INT_TV_ENCODER_ID; + else if (radeon_encoder->active_device & (ATOM_DEVICE_CV_SUPPORT)) + args.v2.ucEncoderID = ASIC_INT_TV_ENCODER_ID; + else + args.v2.ucEncoderID = ASIC_INT_DAC1_ENCODER_ID; + break; + case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC2: + if (radeon_encoder->active_device & (ATOM_DEVICE_TV_SUPPORT)) + args.v2.ucEncoderID = ASIC_INT_TV_ENCODER_ID; + else if (radeon_encoder->active_device & (ATOM_DEVICE_CV_SUPPORT)) + args.v2.ucEncoderID = ASIC_INT_TV_ENCODER_ID; + else + args.v2.ucEncoderID = ASIC_INT_DAC2_ENCODER_ID; + break; + } + break; + } + break; + default: + DRM_ERROR("Unknown table version: %d, %d\n", frev, crev); + return; + } + + atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args); + + /* update scratch regs with new routing */ + radeon_atombios_encoder_crtc_scratch_regs(encoder, radeon_crtc->crtc_id); +} + +static void +atombios_apply_encoder_quirks(struct drm_encoder *encoder, + struct drm_display_mode *mode) +{ + struct drm_device *dev = encoder->dev; + struct radeon_device *rdev = dev->dev_private; + struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); + struct radeon_crtc *radeon_crtc = to_radeon_crtc(encoder->crtc); + + /* Funky macbooks */ + if ((dev->pci_device == 0x71C5) && + (dev->pci_subvendor == 0x106b) && + (dev->pci_subdevice == 0x0080)) { + if (radeon_encoder->devices & ATOM_DEVICE_LCD1_SUPPORT) { + uint32_t lvtma_bit_depth_control = RREG32(AVIVO_LVTMA_BIT_DEPTH_CONTROL); + + lvtma_bit_depth_control &= ~AVIVO_LVTMA_BIT_DEPTH_CONTROL_TRUNCATE_EN; + lvtma_bit_depth_control &= ~AVIVO_LVTMA_BIT_DEPTH_CONTROL_SPATIAL_DITHER_EN; + + WREG32(AVIVO_LVTMA_BIT_DEPTH_CONTROL, lvtma_bit_depth_control); + } + } + + /* set scaler clears this on some chips */ + if (ASIC_IS_AVIVO(rdev) && + (!(radeon_encoder->active_device & (ATOM_DEVICE_TV_SUPPORT)))) { + if (ASIC_IS_DCE4(rdev)) { + if (mode->flags & DRM_MODE_FLAG_INTERLACE) + WREG32(EVERGREEN_DATA_FORMAT + radeon_crtc->crtc_offset, + EVERGREEN_INTERLEAVE_EN); + else + WREG32(EVERGREEN_DATA_FORMAT + radeon_crtc->crtc_offset, 0); + } else { + if (mode->flags & DRM_MODE_FLAG_INTERLACE) + WREG32(AVIVO_D1MODE_DATA_FORMAT + radeon_crtc->crtc_offset, + AVIVO_D1MODE_INTERLEAVE_EN); + else + WREG32(AVIVO_D1MODE_DATA_FORMAT + radeon_crtc->crtc_offset, 0); + } + } +} + +static int radeon_atom_pick_dig_encoder(struct drm_encoder *encoder) +{ + struct drm_device *dev = encoder->dev; + struct radeon_device *rdev = dev->dev_private; + struct radeon_crtc *radeon_crtc = to_radeon_crtc(encoder->crtc); + struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); + struct drm_encoder *test_encoder; + struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv; + uint32_t dig_enc_in_use = 0; + + if (ASIC_IS_DCE6(rdev)) { + /* DCE6 */ + switch (radeon_encoder->encoder_id) { + case ENCODER_OBJECT_ID_INTERNAL_UNIPHY: + if (dig->linkb) + return 1; + else + return 0; + break; + case ENCODER_OBJECT_ID_INTERNAL_UNIPHY1: + if (dig->linkb) + return 3; + else + return 2; + break; + case ENCODER_OBJECT_ID_INTERNAL_UNIPHY2: + if (dig->linkb) + return 5; + else + return 4; + break; + } + } else if (ASIC_IS_DCE4(rdev)) { + /* DCE4/5 */ + if (ASIC_IS_DCE41(rdev) && !ASIC_IS_DCE61(rdev)) { + /* ontario follows DCE4 */ + if (rdev->family == CHIP_PALM) { + if (dig->linkb) + return 1; + else + return 0; + } else + /* llano follows DCE3.2 */ + return radeon_crtc->crtc_id; + } else { + switch (radeon_encoder->encoder_id) { + case ENCODER_OBJECT_ID_INTERNAL_UNIPHY: + if (dig->linkb) + return 1; + else + return 0; + break; + case ENCODER_OBJECT_ID_INTERNAL_UNIPHY1: + if (dig->linkb) + return 3; + else + return 2; + break; + case ENCODER_OBJECT_ID_INTERNAL_UNIPHY2: + if (dig->linkb) + return 5; + else + return 4; + break; + } + } + } + + /* on DCE32 and encoder can driver any block so just crtc id */ + if (ASIC_IS_DCE32(rdev)) { + return radeon_crtc->crtc_id; + } + + /* on DCE3 - LVTMA can only be driven by DIGB */ + list_for_each_entry(test_encoder, &dev->mode_config.encoder_list, head) { + struct radeon_encoder *radeon_test_encoder; + + if (encoder == test_encoder) + continue; + + if (!radeon_encoder_is_digital(test_encoder)) + continue; + + radeon_test_encoder = to_radeon_encoder(test_encoder); + dig = radeon_test_encoder->enc_priv; + + if (dig->dig_encoder >= 0) + dig_enc_in_use |= (1 << dig->dig_encoder); + } + + if (radeon_encoder->encoder_id == ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA) { + if (dig_enc_in_use & 0x2) + DRM_ERROR("LVDS required digital encoder 2 but it was in use - stealing\n"); + return 1; + } + if (!(dig_enc_in_use & 1)) + return 0; + return 1; +} + +/* This only needs to be called once at startup */ +void +radeon_atom_encoder_init(struct radeon_device *rdev) +{ + struct drm_device *dev = rdev->ddev; + struct drm_encoder *encoder; + + list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { + struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); + struct drm_encoder *ext_encoder = radeon_get_external_encoder(encoder); + + switch (radeon_encoder->encoder_id) { + case ENCODER_OBJECT_ID_INTERNAL_UNIPHY: + case ENCODER_OBJECT_ID_INTERNAL_UNIPHY1: + case ENCODER_OBJECT_ID_INTERNAL_UNIPHY2: + case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA: + atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_INIT, 0, 0); + break; + default: + break; + } + + if (ext_encoder && (ASIC_IS_DCE41(rdev) || ASIC_IS_DCE61(rdev))) + atombios_external_encoder_setup(encoder, ext_encoder, + EXTERNAL_ENCODER_ACTION_V3_ENCODER_INIT); + } +} + +static void +radeon_atom_encoder_mode_set(struct drm_encoder *encoder, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + struct drm_device *dev = encoder->dev; + struct radeon_device *rdev = dev->dev_private; + struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); + + radeon_encoder->pixel_clock = adjusted_mode->clock; + + /* need to call this here rather than in prepare() since we need some crtc info */ + radeon_atom_encoder_dpms(encoder, DRM_MODE_DPMS_OFF); + + if (ASIC_IS_AVIVO(rdev) && !ASIC_IS_DCE4(rdev)) { + if (radeon_encoder->active_device & (ATOM_DEVICE_CV_SUPPORT | ATOM_DEVICE_TV_SUPPORT)) + atombios_yuv_setup(encoder, true); + else + atombios_yuv_setup(encoder, false); + } + + switch (radeon_encoder->encoder_id) { + case ENCODER_OBJECT_ID_INTERNAL_TMDS1: + case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_TMDS1: + case ENCODER_OBJECT_ID_INTERNAL_LVDS: + case ENCODER_OBJECT_ID_INTERNAL_LVTM1: + atombios_digital_setup(encoder, PANEL_ENCODER_ACTION_ENABLE); + break; + case ENCODER_OBJECT_ID_INTERNAL_UNIPHY: + case ENCODER_OBJECT_ID_INTERNAL_UNIPHY1: + case ENCODER_OBJECT_ID_INTERNAL_UNIPHY2: + case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA: + /* handled in dpms */ + break; + case ENCODER_OBJECT_ID_INTERNAL_DDI: + case ENCODER_OBJECT_ID_INTERNAL_DVO1: + case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DVO1: + atombios_dvo_setup(encoder, ATOM_ENABLE); + break; + case ENCODER_OBJECT_ID_INTERNAL_DAC1: + case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC1: + case ENCODER_OBJECT_ID_INTERNAL_DAC2: + case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC2: + atombios_dac_setup(encoder, ATOM_ENABLE); + if (radeon_encoder->devices & (ATOM_DEVICE_TV_SUPPORT | ATOM_DEVICE_CV_SUPPORT)) { + if (radeon_encoder->active_device & (ATOM_DEVICE_TV_SUPPORT | ATOM_DEVICE_CV_SUPPORT)) + atombios_tv_setup(encoder, ATOM_ENABLE); + else + atombios_tv_setup(encoder, ATOM_DISABLE); + } + break; + } + + atombios_apply_encoder_quirks(encoder, adjusted_mode); + + if (atombios_get_encoder_mode(encoder) == ATOM_ENCODER_MODE_HDMI) { + r600_hdmi_enable(encoder); + if (ASIC_IS_DCE6(rdev)) + ; /* TODO (use pointers instead of if-s?) */ + else if (ASIC_IS_DCE4(rdev)) + evergreen_hdmi_setmode(encoder, adjusted_mode); + else + r600_hdmi_setmode(encoder, adjusted_mode); + } +} + +static bool +atombios_dac_load_detect(struct drm_encoder *encoder, struct drm_connector *connector) +{ + struct drm_device *dev = encoder->dev; + struct radeon_device *rdev = dev->dev_private; + struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); + struct radeon_connector *radeon_connector = to_radeon_connector(connector); + + if (radeon_encoder->devices & (ATOM_DEVICE_TV_SUPPORT | + ATOM_DEVICE_CV_SUPPORT | + ATOM_DEVICE_CRT_SUPPORT)) { + DAC_LOAD_DETECTION_PS_ALLOCATION args; + int index = GetIndexIntoMasterTable(COMMAND, DAC_LoadDetection); + uint8_t frev, crev; + + memset(&args, 0, sizeof(args)); + + if (!atom_parse_cmd_header(rdev->mode_info.atom_context, index, &frev, &crev)) + return false; + + args.sDacload.ucMisc = 0; + + if ((radeon_encoder->encoder_id == ENCODER_OBJECT_ID_INTERNAL_DAC1) || + (radeon_encoder->encoder_id == ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC1)) + args.sDacload.ucDacType = ATOM_DAC_A; + else + args.sDacload.ucDacType = ATOM_DAC_B; + + if (radeon_connector->devices & ATOM_DEVICE_CRT1_SUPPORT) + args.sDacload.usDeviceID = cpu_to_le16(ATOM_DEVICE_CRT1_SUPPORT); + else if (radeon_connector->devices & ATOM_DEVICE_CRT2_SUPPORT) + args.sDacload.usDeviceID = cpu_to_le16(ATOM_DEVICE_CRT2_SUPPORT); + else if (radeon_connector->devices & ATOM_DEVICE_CV_SUPPORT) { + args.sDacload.usDeviceID = cpu_to_le16(ATOM_DEVICE_CV_SUPPORT); + if (crev >= 3) + args.sDacload.ucMisc = DAC_LOAD_MISC_YPrPb; + } else if (radeon_connector->devices & ATOM_DEVICE_TV1_SUPPORT) { + args.sDacload.usDeviceID = cpu_to_le16(ATOM_DEVICE_TV1_SUPPORT); + if (crev >= 3) + args.sDacload.ucMisc = DAC_LOAD_MISC_YPrPb; + } + + atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args); + + return true; + } else + return false; +} + +static enum drm_connector_status +radeon_atom_dac_detect(struct drm_encoder *encoder, struct drm_connector *connector) +{ + struct drm_device *dev = encoder->dev; + struct radeon_device *rdev = dev->dev_private; + struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); + struct radeon_connector *radeon_connector = to_radeon_connector(connector); + uint32_t bios_0_scratch; + + if (!atombios_dac_load_detect(encoder, connector)) { + DRM_DEBUG_KMS("detect returned false \n"); + return connector_status_unknown; + } + + if (rdev->family >= CHIP_R600) + bios_0_scratch = RREG32(R600_BIOS_0_SCRATCH); + else + bios_0_scratch = RREG32(RADEON_BIOS_0_SCRATCH); + + DRM_DEBUG_KMS("Bios 0 scratch %x %08x\n", bios_0_scratch, radeon_encoder->devices); + if (radeon_connector->devices & ATOM_DEVICE_CRT1_SUPPORT) { + if (bios_0_scratch & ATOM_S0_CRT1_MASK) + return connector_status_connected; + } + if (radeon_connector->devices & ATOM_DEVICE_CRT2_SUPPORT) { + if (bios_0_scratch & ATOM_S0_CRT2_MASK) + return connector_status_connected; + } + if (radeon_connector->devices & ATOM_DEVICE_CV_SUPPORT) { + if (bios_0_scratch & (ATOM_S0_CV_MASK|ATOM_S0_CV_MASK_A)) + return connector_status_connected; + } + if (radeon_connector->devices & ATOM_DEVICE_TV1_SUPPORT) { + if (bios_0_scratch & (ATOM_S0_TV1_COMPOSITE | ATOM_S0_TV1_COMPOSITE_A)) + return connector_status_connected; /* CTV */ + else if (bios_0_scratch & (ATOM_S0_TV1_SVIDEO | ATOM_S0_TV1_SVIDEO_A)) + return connector_status_connected; /* STV */ + } + return connector_status_disconnected; +} + +static enum drm_connector_status +radeon_atom_dig_detect(struct drm_encoder *encoder, struct drm_connector *connector) +{ + struct drm_device *dev = encoder->dev; + struct radeon_device *rdev = dev->dev_private; + struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); + struct radeon_connector *radeon_connector = to_radeon_connector(connector); + struct drm_encoder *ext_encoder = radeon_get_external_encoder(encoder); + u32 bios_0_scratch; + + if (!ASIC_IS_DCE4(rdev)) + return connector_status_unknown; + + if (!ext_encoder) + return connector_status_unknown; + + if ((radeon_connector->devices & ATOM_DEVICE_CRT_SUPPORT) == 0) + return connector_status_unknown; + + /* load detect on the dp bridge */ + atombios_external_encoder_setup(encoder, ext_encoder, + EXTERNAL_ENCODER_ACTION_V3_DACLOAD_DETECTION); + + bios_0_scratch = RREG32(R600_BIOS_0_SCRATCH); + + DRM_DEBUG_KMS("Bios 0 scratch %x %08x\n", bios_0_scratch, radeon_encoder->devices); + if (radeon_connector->devices & ATOM_DEVICE_CRT1_SUPPORT) { + if (bios_0_scratch & ATOM_S0_CRT1_MASK) + return connector_status_connected; + } + if (radeon_connector->devices & ATOM_DEVICE_CRT2_SUPPORT) { + if (bios_0_scratch & ATOM_S0_CRT2_MASK) + return connector_status_connected; + } + if (radeon_connector->devices & ATOM_DEVICE_CV_SUPPORT) { + if (bios_0_scratch & (ATOM_S0_CV_MASK|ATOM_S0_CV_MASK_A)) + return connector_status_connected; + } + if (radeon_connector->devices & ATOM_DEVICE_TV1_SUPPORT) { + if (bios_0_scratch & (ATOM_S0_TV1_COMPOSITE | ATOM_S0_TV1_COMPOSITE_A)) + return connector_status_connected; /* CTV */ + else if (bios_0_scratch & (ATOM_S0_TV1_SVIDEO | ATOM_S0_TV1_SVIDEO_A)) + return connector_status_connected; /* STV */ + } + return connector_status_disconnected; +} + +void +radeon_atom_ext_encoder_setup_ddc(struct drm_encoder *encoder) +{ + struct drm_encoder *ext_encoder = radeon_get_external_encoder(encoder); + + if (ext_encoder) + /* ddc_setup on the dp bridge */ + atombios_external_encoder_setup(encoder, ext_encoder, + EXTERNAL_ENCODER_ACTION_V3_DDC_SETUP); + +} + +static void radeon_atom_encoder_prepare(struct drm_encoder *encoder) +{ + struct radeon_device *rdev = encoder->dev->dev_private; + struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); + struct drm_connector *connector = radeon_get_connector_for_encoder(encoder); + + if ((radeon_encoder->active_device & + (ATOM_DEVICE_DFP_SUPPORT | ATOM_DEVICE_LCD_SUPPORT)) || + (radeon_encoder_get_dp_bridge_encoder_id(encoder) != + ENCODER_OBJECT_ID_NONE)) { + struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv; + if (dig) { + dig->dig_encoder = radeon_atom_pick_dig_encoder(encoder); + if (radeon_encoder->active_device & ATOM_DEVICE_DFP_SUPPORT) { + if (rdev->family >= CHIP_R600) + dig->afmt = rdev->mode_info.afmt[dig->dig_encoder]; + else + /* RS600/690/740 have only 1 afmt block */ + dig->afmt = rdev->mode_info.afmt[0]; + } + } + } + + radeon_atom_output_lock(encoder, true); + + if (connector) { + struct radeon_connector *radeon_connector = to_radeon_connector(connector); + + /* select the clock/data port if it uses a router */ + if (radeon_connector->router.cd_valid) + radeon_router_select_cd_port(radeon_connector); + + /* turn eDP panel on for mode set */ + if (connector->connector_type == DRM_MODE_CONNECTOR_eDP) + atombios_set_edp_panel_power(connector, + ATOM_TRANSMITTER_ACTION_POWER_ON); + } + + /* this is needed for the pll/ss setup to work correctly in some cases */ + atombios_set_encoder_crtc_source(encoder); +} + +static void radeon_atom_encoder_commit(struct drm_encoder *encoder) +{ + /* need to call this here as we need the crtc set up */ + radeon_atom_encoder_dpms(encoder, DRM_MODE_DPMS_ON); + radeon_atom_output_lock(encoder, false); +} + +static void radeon_atom_encoder_disable(struct drm_encoder *encoder) +{ + struct drm_device *dev = encoder->dev; + struct radeon_device *rdev = dev->dev_private; + struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); + struct radeon_encoder_atom_dig *dig; + + /* check for pre-DCE3 cards with shared encoders; + * can't really use the links individually, so don't disable + * the encoder if it's in use by another connector + */ + if (!ASIC_IS_DCE3(rdev)) { + struct drm_encoder *other_encoder; + struct radeon_encoder *other_radeon_encoder; + + list_for_each_entry(other_encoder, &dev->mode_config.encoder_list, head) { + other_radeon_encoder = to_radeon_encoder(other_encoder); + if ((radeon_encoder->encoder_id == other_radeon_encoder->encoder_id) && + drm_helper_encoder_in_use(other_encoder)) + goto disable_done; + } + } + + radeon_atom_encoder_dpms(encoder, DRM_MODE_DPMS_OFF); + + switch (radeon_encoder->encoder_id) { + case ENCODER_OBJECT_ID_INTERNAL_TMDS1: + case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_TMDS1: + case ENCODER_OBJECT_ID_INTERNAL_LVDS: + case ENCODER_OBJECT_ID_INTERNAL_LVTM1: + atombios_digital_setup(encoder, PANEL_ENCODER_ACTION_DISABLE); + break; + case ENCODER_OBJECT_ID_INTERNAL_UNIPHY: + case ENCODER_OBJECT_ID_INTERNAL_UNIPHY1: + case ENCODER_OBJECT_ID_INTERNAL_UNIPHY2: + case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA: + /* handled in dpms */ + break; + case ENCODER_OBJECT_ID_INTERNAL_DDI: + case ENCODER_OBJECT_ID_INTERNAL_DVO1: + case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DVO1: + atombios_dvo_setup(encoder, ATOM_DISABLE); + break; + case ENCODER_OBJECT_ID_INTERNAL_DAC1: + case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC1: + case ENCODER_OBJECT_ID_INTERNAL_DAC2: + case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC2: + atombios_dac_setup(encoder, ATOM_DISABLE); + if (radeon_encoder->devices & (ATOM_DEVICE_TV_SUPPORT | ATOM_DEVICE_CV_SUPPORT)) + atombios_tv_setup(encoder, ATOM_DISABLE); + break; + } + +disable_done: + if (radeon_encoder_is_digital(encoder)) { + if (atombios_get_encoder_mode(encoder) == ATOM_ENCODER_MODE_HDMI) + r600_hdmi_disable(encoder); + dig = radeon_encoder->enc_priv; + dig->dig_encoder = -1; + } + radeon_encoder->active_device = 0; +} + +/* these are handled by the primary encoders */ +static void radeon_atom_ext_prepare(struct drm_encoder *encoder) +{ + +} + +static void radeon_atom_ext_commit(struct drm_encoder *encoder) +{ + +} + +static void +radeon_atom_ext_mode_set(struct drm_encoder *encoder, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + +} + +static void radeon_atom_ext_disable(struct drm_encoder *encoder) +{ + +} + +static void +radeon_atom_ext_dpms(struct drm_encoder *encoder, int mode) +{ + +} + +static bool radeon_atom_ext_mode_fixup(struct drm_encoder *encoder, + const struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + return true; +} + +static const struct drm_encoder_helper_funcs radeon_atom_ext_helper_funcs = { + .dpms = radeon_atom_ext_dpms, + .mode_fixup = radeon_atom_ext_mode_fixup, + .prepare = radeon_atom_ext_prepare, + .mode_set = radeon_atom_ext_mode_set, + .commit = radeon_atom_ext_commit, + .disable = radeon_atom_ext_disable, + /* no detect for TMDS/LVDS yet */ +}; + +static const struct drm_encoder_helper_funcs radeon_atom_dig_helper_funcs = { + .dpms = radeon_atom_encoder_dpms, + .mode_fixup = radeon_atom_mode_fixup, + .prepare = radeon_atom_encoder_prepare, + .mode_set = radeon_atom_encoder_mode_set, + .commit = radeon_atom_encoder_commit, + .disable = radeon_atom_encoder_disable, + .detect = radeon_atom_dig_detect, +}; + +static const struct drm_encoder_helper_funcs radeon_atom_dac_helper_funcs = { + .dpms = radeon_atom_encoder_dpms, + .mode_fixup = radeon_atom_mode_fixup, + .prepare = radeon_atom_encoder_prepare, + .mode_set = radeon_atom_encoder_mode_set, + .commit = radeon_atom_encoder_commit, + .detect = radeon_atom_dac_detect, +}; + +void radeon_enc_destroy(struct drm_encoder *encoder) +{ + struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); + if (radeon_encoder->devices & (ATOM_DEVICE_LCD_SUPPORT)) + radeon_atom_backlight_exit(radeon_encoder); + free(radeon_encoder->enc_priv, DRM_MEM_DRIVER); + drm_encoder_cleanup(encoder); + free(radeon_encoder, DRM_MEM_DRIVER); +} + +static const struct drm_encoder_funcs radeon_atom_enc_funcs = { + .destroy = radeon_enc_destroy, +}; + +static struct radeon_encoder_atom_dac * +radeon_atombios_set_dac_info(struct radeon_encoder *radeon_encoder) +{ + struct drm_device *dev = radeon_encoder->base.dev; + struct radeon_device *rdev = dev->dev_private; + struct radeon_encoder_atom_dac *dac = malloc(sizeof(struct radeon_encoder_atom_dac), + DRM_MEM_DRIVER, M_ZERO | M_WAITOK); + + if (!dac) + return NULL; + + dac->tv_std = radeon_atombios_get_tv_info(rdev); + return dac; +} + +static struct radeon_encoder_atom_dig * +radeon_atombios_set_dig_info(struct radeon_encoder *radeon_encoder) +{ + int encoder_enum = (radeon_encoder->encoder_enum & ENUM_ID_MASK) >> ENUM_ID_SHIFT; + struct radeon_encoder_atom_dig *dig = malloc(sizeof(struct radeon_encoder_atom_dig), + DRM_MEM_DRIVER, M_ZERO | M_WAITOK); + + if (!dig) + return NULL; + + /* coherent mode by default */ + dig->coherent_mode = true; + dig->dig_encoder = -1; + + if (encoder_enum == 2) + dig->linkb = true; + else + dig->linkb = false; + + return dig; +} + +void +radeon_add_atom_encoder(struct drm_device *dev, + uint32_t encoder_enum, + uint32_t supported_device, + u16 caps) +{ + struct radeon_device *rdev = dev->dev_private; + struct drm_encoder *encoder; + struct radeon_encoder *radeon_encoder; + + /* see if we already added it */ + list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { + radeon_encoder = to_radeon_encoder(encoder); + if (radeon_encoder->encoder_enum == encoder_enum) { + radeon_encoder->devices |= supported_device; + return; + } + + } + + /* add a new one */ + radeon_encoder = malloc(sizeof(struct radeon_encoder), + DRM_MEM_DRIVER, M_ZERO | M_WAITOK); + if (!radeon_encoder) + return; + + encoder = &radeon_encoder->base; + switch (rdev->num_crtc) { + case 1: + encoder->possible_crtcs = 0x1; + break; + case 2: + default: + encoder->possible_crtcs = 0x3; + break; + case 4: + encoder->possible_crtcs = 0xf; + break; + case 6: + encoder->possible_crtcs = 0x3f; + break; + } + + radeon_encoder->enc_priv = NULL; + + radeon_encoder->encoder_enum = encoder_enum; + radeon_encoder->encoder_id = (encoder_enum & OBJECT_ID_MASK) >> OBJECT_ID_SHIFT; + radeon_encoder->devices = supported_device; + radeon_encoder->rmx_type = RMX_OFF; + radeon_encoder->underscan_type = UNDERSCAN_OFF; + radeon_encoder->is_ext_encoder = false; + radeon_encoder->caps = caps; + + switch (radeon_encoder->encoder_id) { + case ENCODER_OBJECT_ID_INTERNAL_LVDS: + case ENCODER_OBJECT_ID_INTERNAL_TMDS1: + case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_TMDS1: + case ENCODER_OBJECT_ID_INTERNAL_LVTM1: + if (radeon_encoder->devices & (ATOM_DEVICE_LCD_SUPPORT)) { + radeon_encoder->rmx_type = RMX_FULL; + drm_encoder_init(dev, encoder, &radeon_atom_enc_funcs, DRM_MODE_ENCODER_LVDS); + radeon_encoder->enc_priv = radeon_atombios_get_lvds_info(radeon_encoder); + } else { + drm_encoder_init(dev, encoder, &radeon_atom_enc_funcs, DRM_MODE_ENCODER_TMDS); + radeon_encoder->enc_priv = radeon_atombios_set_dig_info(radeon_encoder); + } + drm_encoder_helper_add(encoder, &radeon_atom_dig_helper_funcs); + break; + case ENCODER_OBJECT_ID_INTERNAL_DAC1: + drm_encoder_init(dev, encoder, &radeon_atom_enc_funcs, DRM_MODE_ENCODER_DAC); + radeon_encoder->enc_priv = radeon_atombios_set_dac_info(radeon_encoder); + drm_encoder_helper_add(encoder, &radeon_atom_dac_helper_funcs); + break; + case ENCODER_OBJECT_ID_INTERNAL_DAC2: + case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC1: + case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC2: + drm_encoder_init(dev, encoder, &radeon_atom_enc_funcs, DRM_MODE_ENCODER_TVDAC); + radeon_encoder->enc_priv = radeon_atombios_set_dac_info(radeon_encoder); + drm_encoder_helper_add(encoder, &radeon_atom_dac_helper_funcs); + break; + case ENCODER_OBJECT_ID_INTERNAL_DVO1: + case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DVO1: + case ENCODER_OBJECT_ID_INTERNAL_DDI: + case ENCODER_OBJECT_ID_INTERNAL_UNIPHY: + case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA: + case ENCODER_OBJECT_ID_INTERNAL_UNIPHY1: + case ENCODER_OBJECT_ID_INTERNAL_UNIPHY2: + if (radeon_encoder->devices & (ATOM_DEVICE_LCD_SUPPORT)) { + radeon_encoder->rmx_type = RMX_FULL; + drm_encoder_init(dev, encoder, &radeon_atom_enc_funcs, DRM_MODE_ENCODER_LVDS); + radeon_encoder->enc_priv = radeon_atombios_get_lvds_info(radeon_encoder); + } else if (radeon_encoder->devices & (ATOM_DEVICE_CRT_SUPPORT)) { + drm_encoder_init(dev, encoder, &radeon_atom_enc_funcs, DRM_MODE_ENCODER_DAC); + radeon_encoder->enc_priv = radeon_atombios_set_dig_info(radeon_encoder); + } else { + drm_encoder_init(dev, encoder, &radeon_atom_enc_funcs, DRM_MODE_ENCODER_TMDS); + radeon_encoder->enc_priv = radeon_atombios_set_dig_info(radeon_encoder); + } + drm_encoder_helper_add(encoder, &radeon_atom_dig_helper_funcs); + break; + case ENCODER_OBJECT_ID_SI170B: + case ENCODER_OBJECT_ID_CH7303: + case ENCODER_OBJECT_ID_EXTERNAL_SDVOA: + case ENCODER_OBJECT_ID_EXTERNAL_SDVOB: + case ENCODER_OBJECT_ID_TITFP513: + case ENCODER_OBJECT_ID_VT1623: + case ENCODER_OBJECT_ID_HDMI_SI1930: + case ENCODER_OBJECT_ID_TRAVIS: + case ENCODER_OBJECT_ID_NUTMEG: + /* these are handled by the primary encoders */ + radeon_encoder->is_ext_encoder = true; + if (radeon_encoder->devices & (ATOM_DEVICE_LCD_SUPPORT)) + drm_encoder_init(dev, encoder, &radeon_atom_enc_funcs, DRM_MODE_ENCODER_LVDS); + else if (radeon_encoder->devices & (ATOM_DEVICE_CRT_SUPPORT)) + drm_encoder_init(dev, encoder, &radeon_atom_enc_funcs, DRM_MODE_ENCODER_DAC); + else + drm_encoder_init(dev, encoder, &radeon_atom_enc_funcs, DRM_MODE_ENCODER_TMDS); + drm_encoder_helper_add(encoder, &radeon_atom_ext_helper_funcs); + break; + } +} diff --git a/sys/dev/drm2/radeon/atombios_i2c.c b/sys/dev/drm2/radeon/atombios_i2c.c new file mode 100644 index 00000000000..40ba15be931 --- /dev/null +++ b/sys/dev/drm2/radeon/atombios_i2c.c @@ -0,0 +1,203 @@ +/* + * Copyright 2011 Advanced Micro Devices, Inc. + * + * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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. + * + * Authors: Alex Deucher + * + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include "radeon.h" +#include "atom.h" +#include "iicbus_if.h" +#include "iicbb_if.h" + +#define TARGET_HW_I2C_CLOCK 50 + +/* these are a limitation of ProcessI2cChannelTransaction not the hw */ +#define ATOM_MAX_HW_I2C_WRITE 2 +#define ATOM_MAX_HW_I2C_READ 255 + +static int radeon_process_i2c_ch(struct radeon_i2c_chan *chan, + u8 slave_addr, u8 flags, + u8 *buf, u8 num) +{ + struct drm_device *dev = chan->dev; + struct radeon_device *rdev = dev->dev_private; + PROCESS_I2C_CHANNEL_TRANSACTION_PS_ALLOCATION args; + int index = GetIndexIntoMasterTable(COMMAND, ProcessI2cChannelTransaction); + unsigned char *base; + u16 out; + + memset(&args, 0, sizeof(args)); + + base = (unsigned char *)rdev->mode_info.atom_context->scratch; + + if (flags & HW_I2C_WRITE) { + if (num > ATOM_MAX_HW_I2C_WRITE) { + DRM_ERROR("hw i2c: tried to write too many bytes (%d vs 2)\n", num); + return EINVAL; + } + memcpy(&out, buf, num); + args.lpI2CDataOut = cpu_to_le16(out); + } + + args.ucI2CSpeed = TARGET_HW_I2C_CLOCK; + args.ucRegIndex = 0; + args.ucTransBytes = num; + args.ucSlaveAddr = slave_addr << 1; + args.ucLineNumber = chan->rec.i2c_id; + + atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args); + + /* error */ + if (args.ucStatus != HW_ASSISTED_I2C_STATUS_SUCCESS) { + DRM_DEBUG_KMS("hw_i2c error\n"); + return EIO; + } + + if (!(flags & HW_I2C_WRITE)) + memcpy(buf, base, num); + + return 0; +} + +static int +radeon_atom_hw_i2c_xfer(device_t dev, struct iic_msg *msgs, u_int num) +{ + struct radeon_i2c_chan *i2c = device_get_softc(dev); + struct iic_msg *p; + int i, remaining, current_count, buffer_offset, max_bytes, ret; + u8 buf = 0, flags; + + /* check for bus probe */ + p = &msgs[0]; + if ((num == 1) && (p->len == 0)) { + ret = radeon_process_i2c_ch(i2c, + p->slave, HW_I2C_WRITE, + &buf, 1); + if (ret) + return ret; + else + return (0); + } + + for (i = 0; i < num; i++) { + p = &msgs[i]; + remaining = p->len; + buffer_offset = 0; + /* max_bytes are a limitation of ProcessI2cChannelTransaction not the hw */ + if (p->flags & IIC_M_RD) { + max_bytes = ATOM_MAX_HW_I2C_READ; + flags = HW_I2C_READ; + } else { + max_bytes = ATOM_MAX_HW_I2C_WRITE; + flags = HW_I2C_WRITE; + } + while (remaining) { + if (remaining > max_bytes) + current_count = max_bytes; + else + current_count = remaining; + ret = radeon_process_i2c_ch(i2c, + p->slave, flags, + &p->buf[buffer_offset], current_count); + if (ret) + return ret; + remaining -= current_count; + buffer_offset += current_count; + } + } + + return (0); +} + +static int +radeon_atom_hw_i2c_probe(device_t dev) +{ + + return (BUS_PROBE_SPECIFIC); +} + +static int +radeon_atom_hw_i2c_attach(device_t dev) +{ + struct radeon_i2c_chan *i2c; + device_t iic_dev; + + i2c = device_get_softc(dev); + device_set_desc(dev, i2c->name); + + /* add generic bit-banging code */ + iic_dev = device_add_child(dev, "iicbus", -1); + if (iic_dev == NULL) + return (ENXIO); + device_quiet(iic_dev); + + /* attach and probe added child */ + bus_generic_attach(dev); + + return (0); +} + +static int +radeon_atom_hw_i2c_detach(device_t dev) +{ + /* detach bit-banding code. */ + bus_generic_detach(dev); + + /* delete bit-banding code. */ + device_delete_children(dev); + return (0); +} + +static int +radeon_atom_hw_i2c_reset(device_t dev, u_char speed, + u_char addr, u_char *oldaddr) +{ + + return (0); +} + +static device_method_t radeon_atom_hw_i2c_methods[] = { + DEVMETHOD(device_probe, radeon_atom_hw_i2c_probe), + DEVMETHOD(device_attach, radeon_atom_hw_i2c_attach), + DEVMETHOD(device_detach, radeon_atom_hw_i2c_detach), + DEVMETHOD(iicbus_reset, radeon_atom_hw_i2c_reset), + DEVMETHOD(iicbus_transfer, radeon_atom_hw_i2c_xfer), + DEVMETHOD_END +}; + +static driver_t radeon_atom_hw_i2c_driver = { + "radeon_atom_hw_i2c", + radeon_atom_hw_i2c_methods, + 0 +}; + +static devclass_t radeon_atom_hw_i2c_devclass; +DRIVER_MODULE_ORDERED(radeon_atom_hw_i2c, drmn, radeon_atom_hw_i2c_driver, + radeon_atom_hw_i2c_devclass, 0, 0, SI_ORDER_ANY); diff --git a/sys/dev/drm2/radeon/avivod.h b/sys/dev/drm2/radeon/avivod.h new file mode 100644 index 00000000000..4f792ebc0fb --- /dev/null +++ b/sys/dev/drm2/radeon/avivod.h @@ -0,0 +1,65 @@ +/* + * Copyright 2009 Advanced Micro Devices, Inc. + * Copyright 2009 Red Hat Inc. + * + * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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. + * + * Authors: Dave Airlie + * Alex Deucher + * Jerome Glisse + */ +#ifndef AVIVOD_H +#define AVIVOD_H + +#include +__FBSDID("$FreeBSD$"); + + +#define D1CRTC_CONTROL 0x6080 +#define CRTC_EN (1 << 0) +#define D1CRTC_STATUS 0x609c +#define D1CRTC_UPDATE_LOCK 0x60E8 +#define D1GRPH_PRIMARY_SURFACE_ADDRESS 0x6110 +#define D1GRPH_SECONDARY_SURFACE_ADDRESS 0x6118 + +#define D2CRTC_CONTROL 0x6880 +#define D2CRTC_STATUS 0x689c +#define D2CRTC_UPDATE_LOCK 0x68E8 +#define D2GRPH_PRIMARY_SURFACE_ADDRESS 0x6910 +#define D2GRPH_SECONDARY_SURFACE_ADDRESS 0x6918 + +#define D1VGA_CONTROL 0x0330 +#define DVGA_CONTROL_MODE_ENABLE (1 << 0) +#define DVGA_CONTROL_TIMING_SELECT (1 << 8) +#define DVGA_CONTROL_SYNC_POLARITY_SELECT (1 << 9) +#define DVGA_CONTROL_OVERSCAN_TIMING_SELECT (1 << 10) +#define DVGA_CONTROL_OVERSCAN_COLOR_EN (1 << 16) +#define DVGA_CONTROL_ROTATE (1 << 24) +#define D2VGA_CONTROL 0x0338 + +#define VGA_HDP_CONTROL 0x328 +#define VGA_MEM_PAGE_SELECT_EN (1 << 0) +#define VGA_MEMORY_DISABLE (1 << 4) +#define VGA_RBBM_LOCK_DISABLE (1 << 8) +#define VGA_SOFT_RESET (1 << 16) +#define VGA_MEMORY_BASE_ADDRESS 0x0310 +#define VGA_RENDER_CONTROL 0x0300 +#define VGA_VSTATUS_CNTL_MASK 0x00030000 + +#endif diff --git a/sys/dev/drm2/radeon/cayman_blit_shaders.c b/sys/dev/drm2/radeon/cayman_blit_shaders.c new file mode 100644 index 00000000000..779f305b6a1 --- /dev/null +++ b/sys/dev/drm2/radeon/cayman_blit_shaders.c @@ -0,0 +1,375 @@ +/* + * Copyright 2010 Advanced Micro Devices, Inc. + * + * 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 (including the next + * paragraph) 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 COPYRIGHT HOLDER(S) AND/OR ITS SUPPLIERS 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. + * + * Authors: + * Alex Deucher + */ + +#include +__FBSDID("$FreeBSD$"); + +#include + +/* + * evergreen cards need to use the 3D engine to blit data which requires + * quite a bit of hw state setup. Rather than pull the whole 3D driver + * (which normally generates the 3D state) into the DRM, we opt to use + * statically generated state tables. The regsiter state and shaders + * were hand generated to support blitting functionality. See the 3D + * driver or documentation for descriptions of the registers and + * shader instructions. + */ + +const u32 cayman_default_state[] = +{ + 0xc0066900, + 0x00000000, + 0x00000060, /* DB_RENDER_CONTROL */ + 0x00000000, /* DB_COUNT_CONTROL */ + 0x00000000, /* DB_DEPTH_VIEW */ + 0x0000002a, /* DB_RENDER_OVERRIDE */ + 0x00000000, /* DB_RENDER_OVERRIDE2 */ + 0x00000000, /* DB_HTILE_DATA_BASE */ + + 0xc0026900, + 0x0000000a, + 0x00000000, /* DB_STENCIL_CLEAR */ + 0x00000000, /* DB_DEPTH_CLEAR */ + + 0xc0036900, + 0x0000000f, + 0x00000000, /* DB_DEPTH_INFO */ + 0x00000000, /* DB_Z_INFO */ + 0x00000000, /* DB_STENCIL_INFO */ + + 0xc0016900, + 0x00000080, + 0x00000000, /* PA_SC_WINDOW_OFFSET */ + + 0xc00d6900, + 0x00000083, + 0x0000ffff, /* PA_SC_CLIPRECT_RULE */ + 0x00000000, /* PA_SC_CLIPRECT_0_TL */ + 0x20002000, /* PA_SC_CLIPRECT_0_BR */ + 0x00000000, + 0x20002000, + 0x00000000, + 0x20002000, + 0x00000000, + 0x20002000, + 0xaaaaaaaa, /* PA_SC_EDGERULE */ + 0x00000000, /* PA_SU_HARDWARE_SCREEN_OFFSET */ + 0x0000000f, /* CB_TARGET_MASK */ + 0x0000000f, /* CB_SHADER_MASK */ + + 0xc0226900, + 0x00000094, + 0x80000000, /* PA_SC_VPORT_SCISSOR_0_TL */ + 0x20002000, /* PA_SC_VPORT_SCISSOR_0_BR */ + 0x80000000, + 0x20002000, + 0x80000000, + 0x20002000, + 0x80000000, + 0x20002000, + 0x80000000, + 0x20002000, + 0x80000000, + 0x20002000, + 0x80000000, + 0x20002000, + 0x80000000, + 0x20002000, + 0x80000000, + 0x20002000, + 0x80000000, + 0x20002000, + 0x80000000, + 0x20002000, + 0x80000000, + 0x20002000, + 0x80000000, + 0x20002000, + 0x80000000, + 0x20002000, + 0x80000000, + 0x20002000, + 0x80000000, + 0x20002000, + 0x00000000, /* PA_SC_VPORT_ZMIN_0 */ + 0x3f800000, /* PA_SC_VPORT_ZMAX_0 */ + + 0xc0016900, + 0x000000d4, + 0x00000000, /* SX_MISC */ + + 0xc0026900, + 0x000000d9, + 0x00000000, /* CP_RINGID */ + 0x00000000, /* CP_VMID */ + + 0xc0096900, + 0x00000100, + 0x00ffffff, /* VGT_MAX_VTX_INDX */ + 0x00000000, /* VGT_MIN_VTX_INDX */ + 0x00000000, /* VGT_INDX_OFFSET */ + 0x00000000, /* VGT_MULTI_PRIM_IB_RESET_INDX */ + 0x00000000, /* SX_ALPHA_TEST_CONTROL */ + 0x00000000, /* CB_BLEND_RED */ + 0x00000000, /* CB_BLEND_GREEN */ + 0x00000000, /* CB_BLEND_BLUE */ + 0x00000000, /* CB_BLEND_ALPHA */ + + 0xc0016900, + 0x00000187, + 0x00000100, /* SPI_VS_OUT_ID_0 */ + + 0xc0026900, + 0x00000191, + 0x00000100, /* SPI_PS_INPUT_CNTL_0 */ + 0x00000101, /* SPI_PS_INPUT_CNTL_1 */ + + 0xc0016900, + 0x000001b1, + 0x00000000, /* SPI_VS_OUT_CONFIG */ + + 0xc0106900, + 0x000001b3, + 0x20000001, /* SPI_PS_IN_CONTROL_0 */ + 0x00000000, /* SPI_PS_IN_CONTROL_1 */ + 0x00000000, /* SPI_INTERP_CONTROL_0 */ + 0x00000000, /* SPI_INPUT_Z */ + 0x00000000, /* SPI_FOG_CNTL */ + 0x00100000, /* SPI_BARYC_CNTL */ + 0x00000000, /* SPI_PS_IN_CONTROL_2 */ + 0x00000000, /* SPI_COMPUTE_INPUT_CNTL */ + 0x00000000, /* SPI_COMPUTE_NUM_THREAD_X */ + 0x00000000, /* SPI_COMPUTE_NUM_THREAD_Y */ + 0x00000000, /* SPI_COMPUTE_NUM_THREAD_Z */ + 0x00000000, /* SPI_GPR_MGMT */ + 0x00000000, /* SPI_LDS_MGMT */ + 0x00000000, /* SPI_STACK_MGMT */ + 0x00000000, /* SPI_WAVE_MGMT_1 */ + 0x00000000, /* SPI_WAVE_MGMT_2 */ + + 0xc0016900, + 0x000001e0, + 0x00000000, /* CB_BLEND0_CONTROL */ + + 0xc00e6900, + 0x00000200, + 0x00000000, /* DB_DEPTH_CONTROL */ + 0x00000000, /* DB_EQAA */ + 0x00cc0010, /* CB_COLOR_CONTROL */ + 0x00000210, /* DB_SHADER_CONTROL */ + 0x00010000, /* PA_CL_CLIP_CNTL */ + 0x00000004, /* PA_SU_SC_MODE_CNTL */ + 0x00000100, /* PA_CL_VTE_CNTL */ + 0x00000000, /* PA_CL_VS_OUT_CNTL */ + 0x00000000, /* PA_CL_NANINF_CNTL */ + 0x00000000, /* PA_SU_LINE_STIPPLE_CNTL */ + 0x00000000, /* PA_SU_LINE_STIPPLE_SCALE */ + 0x00000000, /* PA_SU_PRIM_FILTER_CNTL */ + 0x00000000, /* */ + 0x00000000, /* */ + + 0xc0026900, + 0x00000229, + 0x00000000, /* SQ_PGM_START_FS */ + 0x00000000, + + 0xc0016900, + 0x0000023b, + 0x00000000, /* SQ_LDS_ALLOC_PS */ + + 0xc0066900, + 0x00000240, + 0x00000000, /* SQ_ESGS_RING_ITEMSIZE */ + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + + 0xc0046900, + 0x00000247, + 0x00000000, /* SQ_GS_VERT_ITEMSIZE */ + 0x00000000, + 0x00000000, + 0x00000000, + + 0xc0116900, + 0x00000280, + 0x00000000, /* PA_SU_POINT_SIZE */ + 0x00000000, /* PA_SU_POINT_MINMAX */ + 0x00000008, /* PA_SU_LINE_CNTL */ + 0x00000000, /* PA_SC_LINE_STIPPLE */ + 0x00000000, /* VGT_OUTPUT_PATH_CNTL */ + 0x00000000, /* VGT_HOS_CNTL */ + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, /* VGT_GS_MODE */ + + 0xc0026900, + 0x00000292, + 0x00000000, /* PA_SC_MODE_CNTL_0 */ + 0x00000000, /* PA_SC_MODE_CNTL_1 */ + + 0xc0016900, + 0x000002a1, + 0x00000000, /* VGT_PRIMITIVEID_EN */ + + 0xc0016900, + 0x000002a5, + 0x00000000, /* VGT_MULTI_PRIM_IB_RESET_EN */ + + 0xc0026900, + 0x000002a8, + 0x00000000, /* VGT_INSTANCE_STEP_RATE_0 */ + 0x00000000, + + 0xc0026900, + 0x000002ad, + 0x00000000, /* VGT_REUSE_OFF */ + 0x00000000, + + 0xc0016900, + 0x000002d5, + 0x00000000, /* VGT_SHADER_STAGES_EN */ + + 0xc0016900, + 0x000002dc, + 0x0000aa00, /* DB_ALPHA_TO_MASK */ + + 0xc0066900, + 0x000002de, + 0x00000000, /* PA_SU_POLY_OFFSET_DB_FMT_CNTL */ + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + + 0xc0026900, + 0x000002e5, + 0x00000000, /* VGT_STRMOUT_CONFIG */ + 0x00000000, + + 0xc01b6900, + 0x000002f5, + 0x76543210, /* PA_SC_CENTROID_PRIORITY_0 */ + 0xfedcba98, /* PA_SC_CENTROID_PRIORITY_1 */ + 0x00000000, /* PA_SC_LINE_CNTL */ + 0x00000000, /* PA_SC_AA_CONFIG */ + 0x00000005, /* PA_SU_VTX_CNTL */ + 0x3f800000, /* PA_CL_GB_VERT_CLIP_ADJ */ + 0x3f800000, /* PA_CL_GB_VERT_DISC_ADJ */ + 0x3f800000, /* PA_CL_GB_HORZ_CLIP_ADJ */ + 0x3f800000, /* PA_CL_GB_HORZ_DISC_ADJ */ + 0x00000000, /* PA_SC_AA_SAMPLE_LOCS_PIXEL_X0Y0_0 */ + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0xffffffff, /* PA_SC_AA_MASK_X0Y0_X1Y0 */ + 0xffffffff, + + 0xc0026900, + 0x00000316, + 0x0000000e, /* VGT_VERTEX_REUSE_BLOCK_CNTL */ + 0x00000010, /* */ +}; + +const u32 cayman_vs[] = +{ + 0x00000004, + 0x80400400, + 0x0000a03c, + 0x95000688, + 0x00004000, + 0x15000688, + 0x00000000, + 0x88000000, + 0x04000000, + 0x67961001, +#ifdef __BIG_ENDIAN + 0x00020000, +#else + 0x00000000, +#endif + 0x00000000, + 0x04000000, + 0x67961000, +#ifdef __BIG_ENDIAN + 0x00020008, +#else + 0x00000008, +#endif + 0x00000000, +}; + +const u32 cayman_ps[] = +{ + 0x00000004, + 0xa00c0000, + 0x00000008, + 0x80400000, + 0x00000000, + 0x95000688, + 0x00000000, + 0x88000000, + 0x00380400, + 0x00146b10, + 0x00380000, + 0x20146b10, + 0x00380400, + 0x40146b00, + 0x80380000, + 0x60146b00, + 0x00000010, + 0x000d1000, + 0xb0800000, + 0x00000000, +}; + +const u32 cayman_ps_size = DRM_ARRAY_SIZE(cayman_ps); +const u32 cayman_vs_size = DRM_ARRAY_SIZE(cayman_vs); +const u32 cayman_default_size = DRM_ARRAY_SIZE(cayman_default_state); diff --git a/sys/dev/drm2/radeon/cayman_blit_shaders.h b/sys/dev/drm2/radeon/cayman_blit_shaders.h new file mode 100644 index 00000000000..621019b5c43 --- /dev/null +++ b/sys/dev/drm2/radeon/cayman_blit_shaders.h @@ -0,0 +1,38 @@ +/* + * Copyright 2010 Advanced Micro Devices, Inc. + * + * 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 (including the next + * paragraph) 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 COPYRIGHT HOLDER(S) AND/OR ITS SUPPLIERS 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 CAYMAN_BLIT_SHADERS_H +#define CAYMAN_BLIT_SHADERS_H + +#include +__FBSDID("$FreeBSD$"); + +extern const u32 cayman_ps[]; +extern const u32 cayman_vs[]; +extern const u32 cayman_default_state[]; + +extern const u32 cayman_ps_size, cayman_vs_size; +extern const u32 cayman_default_size; + +#endif diff --git a/sys/dev/drm2/radeon/cayman_reg_safe.h b/sys/dev/drm2/radeon/cayman_reg_safe.h new file mode 100644 index 00000000000..2802df6eb4a --- /dev/null +++ b/sys/dev/drm2/radeon/cayman_reg_safe.h @@ -0,0 +1,517 @@ +#include +__FBSDID("$FreeBSD$"); + +static const unsigned cayman_reg_safe_bm[2047] = { + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFEF7FF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0x7FFFFFFF, 0xFFFFFFFF, 0xCFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFDDEFFF, 0xCF3FFFFF, 0xFFFFE00F, + 0xFEFFFFDF, 0xFFFFFFFF, 0xFFFFFFEF, 0xEFFFFFFF, + 0xFFFFFFCC, 0xFFFFFFFF, 0xFFFFFFFF, 0xBFFFFFD7, + 0xFFFBF8FF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFF7FFE, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFB, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFDF0FFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xC0000000, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFC3E4, 0xFFFFFFFF, 0x0000FFFF, 0x00000000, + 0x000CC000, 0x00000000, 0xFF500000, 0x00000000, + 0x00000E00, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0xFFFFFCF8, 0xFE07FF00, + 0x3CF1F003, 0xE39E7BCF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xE7020000, 0xDDD898DD, 0x279FA3FD, 0x011FFFF0, + 0xBFFF0000, 0xEFC3DF87, 0x7BF0F7E1, 0x1EFC3DF8, + 0xDFBF0F7E, 0xFFFFF7EF, 0xFFFFFFFF, 0x00000000, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xCFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFF8, +}; diff --git a/sys/dev/drm2/radeon/evergreen.c b/sys/dev/drm2/radeon/evergreen.c new file mode 100644 index 00000000000..3c98d53a9bd --- /dev/null +++ b/sys/dev/drm2/radeon/evergreen.c @@ -0,0 +1,3759 @@ +/* + * Copyright 2010 Advanced Micro Devices, Inc. + * + * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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. + * + * Authors: Alex Deucher + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include "radeon.h" +#include "radeon_asic.h" +#include +#include "evergreend.h" +#include "atom.h" +#include "avivod.h" +#include "evergreen_reg.h" +#include "evergreen_blit_shaders.h" + +#define EVERGREEN_PFP_UCODE_SIZE 1120 +#define EVERGREEN_PM4_UCODE_SIZE 1376 + +static const u32 crtc_offsets[6] = +{ + EVERGREEN_CRTC0_REGISTER_OFFSET, + EVERGREEN_CRTC1_REGISTER_OFFSET, + EVERGREEN_CRTC2_REGISTER_OFFSET, + EVERGREEN_CRTC3_REGISTER_OFFSET, + EVERGREEN_CRTC4_REGISTER_OFFSET, + EVERGREEN_CRTC5_REGISTER_OFFSET +}; + +static void evergreen_gpu_init(struct radeon_device *rdev); +void evergreen_pcie_gen2_enable(struct radeon_device *rdev); + +void evergreen_tiling_fields(unsigned tiling_flags, unsigned *bankw, + unsigned *bankh, unsigned *mtaspect, + unsigned *tile_split) +{ + *bankw = (tiling_flags >> RADEON_TILING_EG_BANKW_SHIFT) & RADEON_TILING_EG_BANKW_MASK; + *bankh = (tiling_flags >> RADEON_TILING_EG_BANKH_SHIFT) & RADEON_TILING_EG_BANKH_MASK; + *mtaspect = (tiling_flags >> RADEON_TILING_EG_MACRO_TILE_ASPECT_SHIFT) & RADEON_TILING_EG_MACRO_TILE_ASPECT_MASK; + *tile_split = (tiling_flags >> RADEON_TILING_EG_TILE_SPLIT_SHIFT) & RADEON_TILING_EG_TILE_SPLIT_MASK; + switch (*bankw) { + default: + case 1: *bankw = EVERGREEN_ADDR_SURF_BANK_WIDTH_1; break; + case 2: *bankw = EVERGREEN_ADDR_SURF_BANK_WIDTH_2; break; + case 4: *bankw = EVERGREEN_ADDR_SURF_BANK_WIDTH_4; break; + case 8: *bankw = EVERGREEN_ADDR_SURF_BANK_WIDTH_8; break; + } + switch (*bankh) { + default: + case 1: *bankh = EVERGREEN_ADDR_SURF_BANK_HEIGHT_1; break; + case 2: *bankh = EVERGREEN_ADDR_SURF_BANK_HEIGHT_2; break; + case 4: *bankh = EVERGREEN_ADDR_SURF_BANK_HEIGHT_4; break; + case 8: *bankh = EVERGREEN_ADDR_SURF_BANK_HEIGHT_8; break; + } + switch (*mtaspect) { + default: + case 1: *mtaspect = EVERGREEN_ADDR_SURF_MACRO_TILE_ASPECT_1; break; + case 2: *mtaspect = EVERGREEN_ADDR_SURF_MACRO_TILE_ASPECT_2; break; + case 4: *mtaspect = EVERGREEN_ADDR_SURF_MACRO_TILE_ASPECT_4; break; + case 8: *mtaspect = EVERGREEN_ADDR_SURF_MACRO_TILE_ASPECT_8; break; + } +} + +void evergreen_fix_pci_max_read_req_size(struct radeon_device *rdev) +{ + u16 ctl, v; + int err, cap; + + err = pci_find_cap(rdev->dev, PCIY_EXPRESS, &cap); + if (err) + return; + + cap += PCIER_DEVICE_CTL; + + ctl = pci_read_config(rdev->dev, cap, 2); + + v = (ctl & PCIEM_CTL_MAX_READ_REQUEST) >> 12; + + /* if bios or OS sets MAX_READ_REQUEST_SIZE to an invalid value, fix it + * to avoid hangs or perfomance issues + */ + if ((v == 0) || (v == 6) || (v == 7)) { + ctl &= ~PCIEM_CTL_MAX_READ_REQUEST; + ctl |= (2 << 12); + pci_write_config(rdev->dev, cap, ctl, 2); + } +} + +/** + * dce4_wait_for_vblank - vblank wait asic callback. + * + * @rdev: radeon_device pointer + * @crtc: crtc to wait for vblank on + * + * Wait for vblank on the requested crtc (evergreen+). + */ +void dce4_wait_for_vblank(struct radeon_device *rdev, int crtc) +{ + int i; + + if (crtc >= rdev->num_crtc) + return; + + if (RREG32(EVERGREEN_CRTC_CONTROL + crtc_offsets[crtc]) & EVERGREEN_CRTC_MASTER_EN) { + for (i = 0; i < rdev->usec_timeout; i++) { + if (!(RREG32(EVERGREEN_CRTC_STATUS + crtc_offsets[crtc]) & EVERGREEN_CRTC_V_BLANK)) + break; + DRM_UDELAY(1); + } + for (i = 0; i < rdev->usec_timeout; i++) { + if (RREG32(EVERGREEN_CRTC_STATUS + crtc_offsets[crtc]) & EVERGREEN_CRTC_V_BLANK) + break; + DRM_UDELAY(1); + } + } +} + +/** + * radeon_irq_kms_pflip_irq_get - pre-pageflip callback. + * + * @rdev: radeon_device pointer + * @crtc: crtc to prepare for pageflip on + * + * Pre-pageflip callback (evergreen+). + * Enables the pageflip irq (vblank irq). + */ +void evergreen_pre_page_flip(struct radeon_device *rdev, int crtc) +{ + /* enable the pflip int */ + radeon_irq_kms_pflip_irq_get(rdev, crtc); +} + +/** + * evergreen_post_page_flip - pos-pageflip callback. + * + * @rdev: radeon_device pointer + * @crtc: crtc to cleanup pageflip on + * + * Post-pageflip callback (evergreen+). + * Disables the pageflip irq (vblank irq). + */ +void evergreen_post_page_flip(struct radeon_device *rdev, int crtc) +{ + /* disable the pflip int */ + radeon_irq_kms_pflip_irq_put(rdev, crtc); +} + +/** + * evergreen_page_flip - pageflip callback. + * + * @rdev: radeon_device pointer + * @crtc_id: crtc to cleanup pageflip on + * @crtc_base: new address of the crtc (GPU MC address) + * + * Does the actual pageflip (evergreen+). + * During vblank we take the crtc lock and wait for the update_pending + * bit to go high, when it does, we release the lock, and allow the + * double buffered update to take place. + * Returns the current update pending status. + */ +u32 evergreen_page_flip(struct radeon_device *rdev, int crtc_id, u64 crtc_base) +{ + struct radeon_crtc *radeon_crtc = rdev->mode_info.crtcs[crtc_id]; + u32 tmp = RREG32(EVERGREEN_GRPH_UPDATE + radeon_crtc->crtc_offset); + int i; + + /* Lock the graphics update lock */ + tmp |= EVERGREEN_GRPH_UPDATE_LOCK; + WREG32(EVERGREEN_GRPH_UPDATE + radeon_crtc->crtc_offset, tmp); + + /* update the scanout addresses */ + WREG32(EVERGREEN_GRPH_SECONDARY_SURFACE_ADDRESS_HIGH + radeon_crtc->crtc_offset, + upper_32_bits(crtc_base)); + WREG32(EVERGREEN_GRPH_SECONDARY_SURFACE_ADDRESS + radeon_crtc->crtc_offset, + (u32)crtc_base); + + WREG32(EVERGREEN_GRPH_PRIMARY_SURFACE_ADDRESS_HIGH + radeon_crtc->crtc_offset, + upper_32_bits(crtc_base)); + WREG32(EVERGREEN_GRPH_PRIMARY_SURFACE_ADDRESS + radeon_crtc->crtc_offset, + (u32)crtc_base); + + /* Wait for update_pending to go high. */ + for (i = 0; i < rdev->usec_timeout; i++) { + if (RREG32(EVERGREEN_GRPH_UPDATE + radeon_crtc->crtc_offset) & EVERGREEN_GRPH_SURFACE_UPDATE_PENDING) + break; + DRM_UDELAY(1); + } + DRM_DEBUG("Update pending now high. Unlocking vupdate_lock.\n"); + + /* Unlock the lock, so double-buffering can take place inside vblank */ + tmp &= ~EVERGREEN_GRPH_UPDATE_LOCK; + WREG32(EVERGREEN_GRPH_UPDATE + radeon_crtc->crtc_offset, tmp); + + /* Return current update_pending status: */ + return RREG32(EVERGREEN_GRPH_UPDATE + radeon_crtc->crtc_offset) & EVERGREEN_GRPH_SURFACE_UPDATE_PENDING; +} + +/* get temperature in millidegrees */ +int evergreen_get_temp(struct radeon_device *rdev) +{ + u32 temp, toffset; + int actual_temp = 0; + + if (rdev->family == CHIP_JUNIPER) { + toffset = (RREG32(CG_THERMAL_CTRL) & TOFFSET_MASK) >> + TOFFSET_SHIFT; + temp = (RREG32(CG_TS0_STATUS) & TS0_ADC_DOUT_MASK) >> + TS0_ADC_DOUT_SHIFT; + + if (toffset & 0x100) + actual_temp = temp / 2 - (0x200 - toffset); + else + actual_temp = temp / 2 + toffset; + + actual_temp = actual_temp * 1000; + + } else { + temp = (RREG32(CG_MULT_THERMAL_STATUS) & ASIC_T_MASK) >> + ASIC_T_SHIFT; + + if (temp & 0x400) + actual_temp = -256; + else if (temp & 0x200) + actual_temp = 255; + else if (temp & 0x100) { + actual_temp = temp & 0x1ff; + actual_temp |= ~0x1ff; + } else + actual_temp = temp & 0xff; + + actual_temp = (actual_temp * 1000) / 2; + } + + return actual_temp; +} + +int sumo_get_temp(struct radeon_device *rdev) +{ + u32 temp = RREG32(CG_THERMAL_STATUS) & 0xff; + int actual_temp = temp - 49; + + return actual_temp * 1000; +} + +/** + * sumo_pm_init_profile - Initialize power profiles callback. + * + * @rdev: radeon_device pointer + * + * Initialize the power states used in profile mode + * (sumo, trinity, SI). + * Used for profile mode only. + */ +void sumo_pm_init_profile(struct radeon_device *rdev) +{ + int idx; + + /* default */ + rdev->pm.profiles[PM_PROFILE_DEFAULT_IDX].dpms_off_ps_idx = rdev->pm.default_power_state_index; + rdev->pm.profiles[PM_PROFILE_DEFAULT_IDX].dpms_on_ps_idx = rdev->pm.default_power_state_index; + rdev->pm.profiles[PM_PROFILE_DEFAULT_IDX].dpms_off_cm_idx = 0; + rdev->pm.profiles[PM_PROFILE_DEFAULT_IDX].dpms_on_cm_idx = 0; + + /* low,mid sh/mh */ + if (rdev->flags & RADEON_IS_MOBILITY) + idx = radeon_pm_get_type_index(rdev, POWER_STATE_TYPE_BATTERY, 0); + else + idx = radeon_pm_get_type_index(rdev, POWER_STATE_TYPE_PERFORMANCE, 0); + + rdev->pm.profiles[PM_PROFILE_LOW_SH_IDX].dpms_off_ps_idx = idx; + rdev->pm.profiles[PM_PROFILE_LOW_SH_IDX].dpms_on_ps_idx = idx; + rdev->pm.profiles[PM_PROFILE_LOW_SH_IDX].dpms_off_cm_idx = 0; + rdev->pm.profiles[PM_PROFILE_LOW_SH_IDX].dpms_on_cm_idx = 0; + + rdev->pm.profiles[PM_PROFILE_LOW_MH_IDX].dpms_off_ps_idx = idx; + rdev->pm.profiles[PM_PROFILE_LOW_MH_IDX].dpms_on_ps_idx = idx; + rdev->pm.profiles[PM_PROFILE_LOW_MH_IDX].dpms_off_cm_idx = 0; + rdev->pm.profiles[PM_PROFILE_LOW_MH_IDX].dpms_on_cm_idx = 0; + + rdev->pm.profiles[PM_PROFILE_MID_SH_IDX].dpms_off_ps_idx = idx; + rdev->pm.profiles[PM_PROFILE_MID_SH_IDX].dpms_on_ps_idx = idx; + rdev->pm.profiles[PM_PROFILE_MID_SH_IDX].dpms_off_cm_idx = 0; + rdev->pm.profiles[PM_PROFILE_MID_SH_IDX].dpms_on_cm_idx = 0; + + rdev->pm.profiles[PM_PROFILE_MID_MH_IDX].dpms_off_ps_idx = idx; + rdev->pm.profiles[PM_PROFILE_MID_MH_IDX].dpms_on_ps_idx = idx; + rdev->pm.profiles[PM_PROFILE_MID_MH_IDX].dpms_off_cm_idx = 0; + rdev->pm.profiles[PM_PROFILE_MID_MH_IDX].dpms_on_cm_idx = 0; + + /* high sh/mh */ + idx = radeon_pm_get_type_index(rdev, POWER_STATE_TYPE_PERFORMANCE, 0); + rdev->pm.profiles[PM_PROFILE_HIGH_SH_IDX].dpms_off_ps_idx = idx; + rdev->pm.profiles[PM_PROFILE_HIGH_SH_IDX].dpms_on_ps_idx = idx; + rdev->pm.profiles[PM_PROFILE_HIGH_SH_IDX].dpms_off_cm_idx = 0; + rdev->pm.profiles[PM_PROFILE_HIGH_SH_IDX].dpms_on_cm_idx = + rdev->pm.power_state[idx].num_clock_modes - 1; + + rdev->pm.profiles[PM_PROFILE_HIGH_MH_IDX].dpms_off_ps_idx = idx; + rdev->pm.profiles[PM_PROFILE_HIGH_MH_IDX].dpms_on_ps_idx = idx; + rdev->pm.profiles[PM_PROFILE_HIGH_MH_IDX].dpms_off_cm_idx = 0; + rdev->pm.profiles[PM_PROFILE_HIGH_MH_IDX].dpms_on_cm_idx = + rdev->pm.power_state[idx].num_clock_modes - 1; +} + +/** + * btc_pm_init_profile - Initialize power profiles callback. + * + * @rdev: radeon_device pointer + * + * Initialize the power states used in profile mode + * (BTC, cayman). + * Used for profile mode only. + */ +void btc_pm_init_profile(struct radeon_device *rdev) +{ + int idx; + + /* default */ + rdev->pm.profiles[PM_PROFILE_DEFAULT_IDX].dpms_off_ps_idx = rdev->pm.default_power_state_index; + rdev->pm.profiles[PM_PROFILE_DEFAULT_IDX].dpms_on_ps_idx = rdev->pm.default_power_state_index; + rdev->pm.profiles[PM_PROFILE_DEFAULT_IDX].dpms_off_cm_idx = 0; + rdev->pm.profiles[PM_PROFILE_DEFAULT_IDX].dpms_on_cm_idx = 2; + /* starting with BTC, there is one state that is used for both + * MH and SH. Difference is that we always use the high clock index for + * mclk. + */ + if (rdev->flags & RADEON_IS_MOBILITY) + idx = radeon_pm_get_type_index(rdev, POWER_STATE_TYPE_BATTERY, 0); + else + idx = radeon_pm_get_type_index(rdev, POWER_STATE_TYPE_PERFORMANCE, 0); + /* low sh */ + rdev->pm.profiles[PM_PROFILE_LOW_SH_IDX].dpms_off_ps_idx = idx; + rdev->pm.profiles[PM_PROFILE_LOW_SH_IDX].dpms_on_ps_idx = idx; + rdev->pm.profiles[PM_PROFILE_LOW_SH_IDX].dpms_off_cm_idx = 0; + rdev->pm.profiles[PM_PROFILE_LOW_SH_IDX].dpms_on_cm_idx = 0; + /* mid sh */ + rdev->pm.profiles[PM_PROFILE_MID_SH_IDX].dpms_off_ps_idx = idx; + rdev->pm.profiles[PM_PROFILE_MID_SH_IDX].dpms_on_ps_idx = idx; + rdev->pm.profiles[PM_PROFILE_MID_SH_IDX].dpms_off_cm_idx = 0; + rdev->pm.profiles[PM_PROFILE_MID_SH_IDX].dpms_on_cm_idx = 1; + /* high sh */ + rdev->pm.profiles[PM_PROFILE_HIGH_SH_IDX].dpms_off_ps_idx = idx; + rdev->pm.profiles[PM_PROFILE_HIGH_SH_IDX].dpms_on_ps_idx = idx; + rdev->pm.profiles[PM_PROFILE_HIGH_SH_IDX].dpms_off_cm_idx = 0; + rdev->pm.profiles[PM_PROFILE_HIGH_SH_IDX].dpms_on_cm_idx = 2; + /* low mh */ + rdev->pm.profiles[PM_PROFILE_LOW_MH_IDX].dpms_off_ps_idx = idx; + rdev->pm.profiles[PM_PROFILE_LOW_MH_IDX].dpms_on_ps_idx = idx; + rdev->pm.profiles[PM_PROFILE_LOW_MH_IDX].dpms_off_cm_idx = 0; + rdev->pm.profiles[PM_PROFILE_LOW_MH_IDX].dpms_on_cm_idx = 0; + /* mid mh */ + rdev->pm.profiles[PM_PROFILE_MID_MH_IDX].dpms_off_ps_idx = idx; + rdev->pm.profiles[PM_PROFILE_MID_MH_IDX].dpms_on_ps_idx = idx; + rdev->pm.profiles[PM_PROFILE_MID_MH_IDX].dpms_off_cm_idx = 0; + rdev->pm.profiles[PM_PROFILE_MID_MH_IDX].dpms_on_cm_idx = 1; + /* high mh */ + rdev->pm.profiles[PM_PROFILE_HIGH_MH_IDX].dpms_off_ps_idx = idx; + rdev->pm.profiles[PM_PROFILE_HIGH_MH_IDX].dpms_on_ps_idx = idx; + rdev->pm.profiles[PM_PROFILE_HIGH_MH_IDX].dpms_off_cm_idx = 0; + rdev->pm.profiles[PM_PROFILE_HIGH_MH_IDX].dpms_on_cm_idx = 2; +} + +/** + * evergreen_pm_misc - set additional pm hw parameters callback. + * + * @rdev: radeon_device pointer + * + * Set non-clock parameters associated with a power state + * (voltage, etc.) (evergreen+). + */ +void evergreen_pm_misc(struct radeon_device *rdev) +{ + int req_ps_idx = rdev->pm.requested_power_state_index; + int req_cm_idx = rdev->pm.requested_clock_mode_index; + struct radeon_power_state *ps = &rdev->pm.power_state[req_ps_idx]; + struct radeon_voltage *voltage = &ps->clock_info[req_cm_idx].voltage; + + if (voltage->type == VOLTAGE_SW) { + /* 0xff01 is a flag rather then an actual voltage */ + if (voltage->voltage == 0xff01) + return; + if (voltage->voltage && (voltage->voltage != rdev->pm.current_vddc)) { + radeon_atom_set_voltage(rdev, voltage->voltage, SET_VOLTAGE_TYPE_ASIC_VDDC); + rdev->pm.current_vddc = voltage->voltage; + DRM_DEBUG("Setting: vddc: %d\n", voltage->voltage); + } + /* 0xff01 is a flag rather then an actual voltage */ + if (voltage->vddci == 0xff01) + return; + if (voltage->vddci && (voltage->vddci != rdev->pm.current_vddci)) { + radeon_atom_set_voltage(rdev, voltage->vddci, SET_VOLTAGE_TYPE_ASIC_VDDCI); + rdev->pm.current_vddci = voltage->vddci; + DRM_DEBUG("Setting: vddci: %d\n", voltage->vddci); + } + } +} + +/** + * evergreen_pm_prepare - pre-power state change callback. + * + * @rdev: radeon_device pointer + * + * Prepare for a power state change (evergreen+). + */ +void evergreen_pm_prepare(struct radeon_device *rdev) +{ + struct drm_device *ddev = rdev->ddev; + struct drm_crtc *crtc; + struct radeon_crtc *radeon_crtc; + u32 tmp; + + /* disable any active CRTCs */ + list_for_each_entry(crtc, &ddev->mode_config.crtc_list, head) { + radeon_crtc = to_radeon_crtc(crtc); + if (radeon_crtc->enabled) { + tmp = RREG32(EVERGREEN_CRTC_CONTROL + radeon_crtc->crtc_offset); + tmp |= EVERGREEN_CRTC_DISP_READ_REQUEST_DISABLE; + WREG32(EVERGREEN_CRTC_CONTROL + radeon_crtc->crtc_offset, tmp); + } + } +} + +/** + * evergreen_pm_finish - post-power state change callback. + * + * @rdev: radeon_device pointer + * + * Clean up after a power state change (evergreen+). + */ +void evergreen_pm_finish(struct radeon_device *rdev) +{ + struct drm_device *ddev = rdev->ddev; + struct drm_crtc *crtc; + struct radeon_crtc *radeon_crtc; + u32 tmp; + + /* enable any active CRTCs */ + list_for_each_entry(crtc, &ddev->mode_config.crtc_list, head) { + radeon_crtc = to_radeon_crtc(crtc); + if (radeon_crtc->enabled) { + tmp = RREG32(EVERGREEN_CRTC_CONTROL + radeon_crtc->crtc_offset); + tmp &= ~EVERGREEN_CRTC_DISP_READ_REQUEST_DISABLE; + WREG32(EVERGREEN_CRTC_CONTROL + radeon_crtc->crtc_offset, tmp); + } + } +} + +/** + * evergreen_hpd_sense - hpd sense callback. + * + * @rdev: radeon_device pointer + * @hpd: hpd (hotplug detect) pin + * + * Checks if a digital monitor is connected (evergreen+). + * Returns true if connected, false if not connected. + */ +bool evergreen_hpd_sense(struct radeon_device *rdev, enum radeon_hpd_id hpd) +{ + bool connected = false; + + switch (hpd) { + case RADEON_HPD_1: + if (RREG32(DC_HPD1_INT_STATUS) & DC_HPDx_SENSE) + connected = true; + break; + case RADEON_HPD_2: + if (RREG32(DC_HPD2_INT_STATUS) & DC_HPDx_SENSE) + connected = true; + break; + case RADEON_HPD_3: + if (RREG32(DC_HPD3_INT_STATUS) & DC_HPDx_SENSE) + connected = true; + break; + case RADEON_HPD_4: + if (RREG32(DC_HPD4_INT_STATUS) & DC_HPDx_SENSE) + connected = true; + break; + case RADEON_HPD_5: + if (RREG32(DC_HPD5_INT_STATUS) & DC_HPDx_SENSE) + connected = true; + break; + case RADEON_HPD_6: + if (RREG32(DC_HPD6_INT_STATUS) & DC_HPDx_SENSE) + connected = true; + break; + default: + break; + } + + return connected; +} + +/** + * evergreen_hpd_set_polarity - hpd set polarity callback. + * + * @rdev: radeon_device pointer + * @hpd: hpd (hotplug detect) pin + * + * Set the polarity of the hpd pin (evergreen+). + */ +void evergreen_hpd_set_polarity(struct radeon_device *rdev, + enum radeon_hpd_id hpd) +{ + u32 tmp; + bool connected = evergreen_hpd_sense(rdev, hpd); + + switch (hpd) { + case RADEON_HPD_1: + tmp = RREG32(DC_HPD1_INT_CONTROL); + if (connected) + tmp &= ~DC_HPDx_INT_POLARITY; + else + tmp |= DC_HPDx_INT_POLARITY; + WREG32(DC_HPD1_INT_CONTROL, tmp); + break; + case RADEON_HPD_2: + tmp = RREG32(DC_HPD2_INT_CONTROL); + if (connected) + tmp &= ~DC_HPDx_INT_POLARITY; + else + tmp |= DC_HPDx_INT_POLARITY; + WREG32(DC_HPD2_INT_CONTROL, tmp); + break; + case RADEON_HPD_3: + tmp = RREG32(DC_HPD3_INT_CONTROL); + if (connected) + tmp &= ~DC_HPDx_INT_POLARITY; + else + tmp |= DC_HPDx_INT_POLARITY; + WREG32(DC_HPD3_INT_CONTROL, tmp); + break; + case RADEON_HPD_4: + tmp = RREG32(DC_HPD4_INT_CONTROL); + if (connected) + tmp &= ~DC_HPDx_INT_POLARITY; + else + tmp |= DC_HPDx_INT_POLARITY; + WREG32(DC_HPD4_INT_CONTROL, tmp); + break; + case RADEON_HPD_5: + tmp = RREG32(DC_HPD5_INT_CONTROL); + if (connected) + tmp &= ~DC_HPDx_INT_POLARITY; + else + tmp |= DC_HPDx_INT_POLARITY; + WREG32(DC_HPD5_INT_CONTROL, tmp); + break; + case RADEON_HPD_6: + tmp = RREG32(DC_HPD6_INT_CONTROL); + if (connected) + tmp &= ~DC_HPDx_INT_POLARITY; + else + tmp |= DC_HPDx_INT_POLARITY; + WREG32(DC_HPD6_INT_CONTROL, tmp); + break; + default: + break; + } +} + +/** + * evergreen_hpd_init - hpd setup callback. + * + * @rdev: radeon_device pointer + * + * Setup the hpd pins used by the card (evergreen+). + * Enable the pin, set the polarity, and enable the hpd interrupts. + */ +void evergreen_hpd_init(struct radeon_device *rdev) +{ + struct drm_device *dev = rdev->ddev; + struct drm_connector *connector; + unsigned enabled = 0; + u32 tmp = DC_HPDx_CONNECTION_TIMER(0x9c4) | + DC_HPDx_RX_INT_TIMER(0xfa) | DC_HPDx_EN; + + list_for_each_entry(connector, &dev->mode_config.connector_list, head) { + struct radeon_connector *radeon_connector = to_radeon_connector(connector); + switch (radeon_connector->hpd.hpd) { + case RADEON_HPD_1: + WREG32(DC_HPD1_CONTROL, tmp); + break; + case RADEON_HPD_2: + WREG32(DC_HPD2_CONTROL, tmp); + break; + case RADEON_HPD_3: + WREG32(DC_HPD3_CONTROL, tmp); + break; + case RADEON_HPD_4: + WREG32(DC_HPD4_CONTROL, tmp); + break; + case RADEON_HPD_5: + WREG32(DC_HPD5_CONTROL, tmp); + break; + case RADEON_HPD_6: + WREG32(DC_HPD6_CONTROL, tmp); + break; + default: + break; + } + radeon_hpd_set_polarity(rdev, radeon_connector->hpd.hpd); + enabled |= 1 << radeon_connector->hpd.hpd; + } + radeon_irq_kms_enable_hpd(rdev, enabled); +} + +/** + * evergreen_hpd_fini - hpd tear down callback. + * + * @rdev: radeon_device pointer + * + * Tear down the hpd pins used by the card (evergreen+). + * Disable the hpd interrupts. + */ +void evergreen_hpd_fini(struct radeon_device *rdev) +{ + struct drm_device *dev = rdev->ddev; + struct drm_connector *connector; + unsigned disabled = 0; + + list_for_each_entry(connector, &dev->mode_config.connector_list, head) { + struct radeon_connector *radeon_connector = to_radeon_connector(connector); + switch (radeon_connector->hpd.hpd) { + case RADEON_HPD_1: + WREG32(DC_HPD1_CONTROL, 0); + break; + case RADEON_HPD_2: + WREG32(DC_HPD2_CONTROL, 0); + break; + case RADEON_HPD_3: + WREG32(DC_HPD3_CONTROL, 0); + break; + case RADEON_HPD_4: + WREG32(DC_HPD4_CONTROL, 0); + break; + case RADEON_HPD_5: + WREG32(DC_HPD5_CONTROL, 0); + break; + case RADEON_HPD_6: + WREG32(DC_HPD6_CONTROL, 0); + break; + default: + break; + } + disabled |= 1 << radeon_connector->hpd.hpd; + } + radeon_irq_kms_disable_hpd(rdev, disabled); +} + +/* watermark setup */ + +static u32 evergreen_line_buffer_adjust(struct radeon_device *rdev, + struct radeon_crtc *radeon_crtc, + struct drm_display_mode *mode, + struct drm_display_mode *other_mode) +{ + u32 tmp; + /* + * Line Buffer Setup + * There are 3 line buffers, each one shared by 2 display controllers. + * DC_LB_MEMORY_SPLIT controls how that line buffer is shared between + * the display controllers. The paritioning is done via one of four + * preset allocations specified in bits 2:0: + * first display controller + * 0 - first half of lb (3840 * 2) + * 1 - first 3/4 of lb (5760 * 2) + * 2 - whole lb (7680 * 2), other crtc must be disabled + * 3 - first 1/4 of lb (1920 * 2) + * second display controller + * 4 - second half of lb (3840 * 2) + * 5 - second 3/4 of lb (5760 * 2) + * 6 - whole lb (7680 * 2), other crtc must be disabled + * 7 - last 1/4 of lb (1920 * 2) + */ + /* this can get tricky if we have two large displays on a paired group + * of crtcs. Ideally for multiple large displays we'd assign them to + * non-linked crtcs for maximum line buffer allocation. + */ + if (radeon_crtc->base.enabled && mode) { + if (other_mode) + tmp = 0; /* 1/2 */ + else + tmp = 2; /* whole */ + } else + tmp = 0; + + /* second controller of the pair uses second half of the lb */ + if (radeon_crtc->crtc_id % 2) + tmp += 4; + WREG32(DC_LB_MEMORY_SPLIT + radeon_crtc->crtc_offset, tmp); + + if (radeon_crtc->base.enabled && mode) { + switch (tmp) { + case 0: + case 4: + default: + if (ASIC_IS_DCE5(rdev)) + return 4096 * 2; + else + return 3840 * 2; + case 1: + case 5: + if (ASIC_IS_DCE5(rdev)) + return 6144 * 2; + else + return 5760 * 2; + case 2: + case 6: + if (ASIC_IS_DCE5(rdev)) + return 8192 * 2; + else + return 7680 * 2; + case 3: + case 7: + if (ASIC_IS_DCE5(rdev)) + return 2048 * 2; + else + return 1920 * 2; + } + } + + /* controller not enabled, so no lb used */ + return 0; +} + +u32 evergreen_get_number_of_dram_channels(struct radeon_device *rdev) +{ + u32 tmp = RREG32(MC_SHARED_CHMAP); + + switch ((tmp & NOOFCHAN_MASK) >> NOOFCHAN_SHIFT) { + case 0: + default: + return 1; + case 1: + return 2; + case 2: + return 4; + case 3: + return 8; + } +} + +struct evergreen_wm_params { + u32 dram_channels; /* number of dram channels */ + u32 yclk; /* bandwidth per dram data pin in kHz */ + u32 sclk; /* engine clock in kHz */ + u32 disp_clk; /* display clock in kHz */ + u32 src_width; /* viewport width */ + u32 active_time; /* active display time in ns */ + u32 blank_time; /* blank time in ns */ + bool interlaced; /* mode is interlaced */ + fixed20_12 vsc; /* vertical scale ratio */ + u32 num_heads; /* number of active crtcs */ + u32 bytes_per_pixel; /* bytes per pixel display + overlay */ + u32 lb_size; /* line buffer allocated to pipe */ + u32 vtaps; /* vertical scaler taps */ +}; + +static u32 evergreen_dram_bandwidth(struct evergreen_wm_params *wm) +{ + /* Calculate DRAM Bandwidth and the part allocated to display. */ + fixed20_12 dram_efficiency; /* 0.7 */ + fixed20_12 yclk, dram_channels, bandwidth; + fixed20_12 a; + + a.full = dfixed_const(1000); + yclk.full = dfixed_const(wm->yclk); + yclk.full = dfixed_div(yclk, a); + dram_channels.full = dfixed_const(wm->dram_channels * 4); + a.full = dfixed_const(10); + dram_efficiency.full = dfixed_const(7); + dram_efficiency.full = dfixed_div(dram_efficiency, a); + bandwidth.full = dfixed_mul(dram_channels, yclk); + bandwidth.full = dfixed_mul(bandwidth, dram_efficiency); + + return dfixed_trunc(bandwidth); +} + +static u32 evergreen_dram_bandwidth_for_display(struct evergreen_wm_params *wm) +{ + /* Calculate DRAM Bandwidth and the part allocated to display. */ + fixed20_12 disp_dram_allocation; /* 0.3 to 0.7 */ + fixed20_12 yclk, dram_channels, bandwidth; + fixed20_12 a; + + a.full = dfixed_const(1000); + yclk.full = dfixed_const(wm->yclk); + yclk.full = dfixed_div(yclk, a); + dram_channels.full = dfixed_const(wm->dram_channels * 4); + a.full = dfixed_const(10); + disp_dram_allocation.full = dfixed_const(3); /* XXX worse case value 0.3 */ + disp_dram_allocation.full = dfixed_div(disp_dram_allocation, a); + bandwidth.full = dfixed_mul(dram_channels, yclk); + bandwidth.full = dfixed_mul(bandwidth, disp_dram_allocation); + + return dfixed_trunc(bandwidth); +} + +static u32 evergreen_data_return_bandwidth(struct evergreen_wm_params *wm) +{ + /* Calculate the display Data return Bandwidth */ + fixed20_12 return_efficiency; /* 0.8 */ + fixed20_12 sclk, bandwidth; + fixed20_12 a; + + a.full = dfixed_const(1000); + sclk.full = dfixed_const(wm->sclk); + sclk.full = dfixed_div(sclk, a); + a.full = dfixed_const(10); + return_efficiency.full = dfixed_const(8); + return_efficiency.full = dfixed_div(return_efficiency, a); + a.full = dfixed_const(32); + bandwidth.full = dfixed_mul(a, sclk); + bandwidth.full = dfixed_mul(bandwidth, return_efficiency); + + return dfixed_trunc(bandwidth); +} + +static u32 evergreen_dmif_request_bandwidth(struct evergreen_wm_params *wm) +{ + /* Calculate the DMIF Request Bandwidth */ + fixed20_12 disp_clk_request_efficiency; /* 0.8 */ + fixed20_12 disp_clk, bandwidth; + fixed20_12 a; + + a.full = dfixed_const(1000); + disp_clk.full = dfixed_const(wm->disp_clk); + disp_clk.full = dfixed_div(disp_clk, a); + a.full = dfixed_const(10); + disp_clk_request_efficiency.full = dfixed_const(8); + disp_clk_request_efficiency.full = dfixed_div(disp_clk_request_efficiency, a); + a.full = dfixed_const(32); + bandwidth.full = dfixed_mul(a, disp_clk); + bandwidth.full = dfixed_mul(bandwidth, disp_clk_request_efficiency); + + return dfixed_trunc(bandwidth); +} + +static u32 evergreen_available_bandwidth(struct evergreen_wm_params *wm) +{ + /* Calculate the Available bandwidth. Display can use this temporarily but not in average. */ + u32 dram_bandwidth = evergreen_dram_bandwidth(wm); + u32 data_return_bandwidth = evergreen_data_return_bandwidth(wm); + u32 dmif_req_bandwidth = evergreen_dmif_request_bandwidth(wm); + + return min(dram_bandwidth, min(data_return_bandwidth, dmif_req_bandwidth)); +} + +static u32 evergreen_average_bandwidth(struct evergreen_wm_params *wm) +{ + /* Calculate the display mode Average Bandwidth + * DisplayMode should contain the source and destination dimensions, + * timing, etc. + */ + fixed20_12 bpp; + fixed20_12 line_time; + fixed20_12 src_width; + fixed20_12 bandwidth; + fixed20_12 a; + + a.full = dfixed_const(1000); + line_time.full = dfixed_const(wm->active_time + wm->blank_time); + line_time.full = dfixed_div(line_time, a); + bpp.full = dfixed_const(wm->bytes_per_pixel); + src_width.full = dfixed_const(wm->src_width); + bandwidth.full = dfixed_mul(src_width, bpp); + bandwidth.full = dfixed_mul(bandwidth, wm->vsc); + bandwidth.full = dfixed_div(bandwidth, line_time); + + return dfixed_trunc(bandwidth); +} + +static u32 evergreen_latency_watermark(struct evergreen_wm_params *wm) +{ + /* First calcualte the latency in ns */ + u32 mc_latency = 2000; /* 2000 ns. */ + u32 available_bandwidth = evergreen_available_bandwidth(wm); + u32 worst_chunk_return_time = (512 * 8 * 1000) / available_bandwidth; + u32 cursor_line_pair_return_time = (128 * 4 * 1000) / available_bandwidth; + u32 dc_latency = 40000000 / wm->disp_clk; /* dc pipe latency */ + u32 other_heads_data_return_time = ((wm->num_heads + 1) * worst_chunk_return_time) + + (wm->num_heads * cursor_line_pair_return_time); + u32 latency = mc_latency + other_heads_data_return_time + dc_latency; + u32 max_src_lines_per_dst_line, lb_fill_bw, line_fill_time; + fixed20_12 a, b, c; + + if (wm->num_heads == 0) + return 0; + + a.full = dfixed_const(2); + b.full = dfixed_const(1); + if ((wm->vsc.full > a.full) || + ((wm->vsc.full > b.full) && (wm->vtaps >= 3)) || + (wm->vtaps >= 5) || + ((wm->vsc.full >= a.full) && wm->interlaced)) + max_src_lines_per_dst_line = 4; + else + max_src_lines_per_dst_line = 2; + + a.full = dfixed_const(available_bandwidth); + b.full = dfixed_const(wm->num_heads); + a.full = dfixed_div(a, b); + + b.full = dfixed_const(1000); + c.full = dfixed_const(wm->disp_clk); + b.full = dfixed_div(c, b); + c.full = dfixed_const(wm->bytes_per_pixel); + b.full = dfixed_mul(b, c); + + lb_fill_bw = min(dfixed_trunc(a), dfixed_trunc(b)); + + a.full = dfixed_const(max_src_lines_per_dst_line * wm->src_width * wm->bytes_per_pixel); + b.full = dfixed_const(1000); + c.full = dfixed_const(lb_fill_bw); + b.full = dfixed_div(c, b); + a.full = dfixed_div(a, b); + line_fill_time = dfixed_trunc(a); + + if (line_fill_time < wm->active_time) + return latency; + else + return latency + (line_fill_time - wm->active_time); + +} + +static bool evergreen_average_bandwidth_vs_dram_bandwidth_for_display(struct evergreen_wm_params *wm) +{ + if (evergreen_average_bandwidth(wm) <= + (evergreen_dram_bandwidth_for_display(wm) / wm->num_heads)) + return true; + else + return false; +}; + +static bool evergreen_average_bandwidth_vs_available_bandwidth(struct evergreen_wm_params *wm) +{ + if (evergreen_average_bandwidth(wm) <= + (evergreen_available_bandwidth(wm) / wm->num_heads)) + return true; + else + return false; +}; + +static bool evergreen_check_latency_hiding(struct evergreen_wm_params *wm) +{ + u32 lb_partitions = wm->lb_size / wm->src_width; + u32 line_time = wm->active_time + wm->blank_time; + u32 latency_tolerant_lines; + u32 latency_hiding; + fixed20_12 a; + + a.full = dfixed_const(1); + if (wm->vsc.full > a.full) + latency_tolerant_lines = 1; + else { + if (lb_partitions <= (wm->vtaps + 1)) + latency_tolerant_lines = 1; + else + latency_tolerant_lines = 2; + } + + latency_hiding = (latency_tolerant_lines * line_time + wm->blank_time); + + if (evergreen_latency_watermark(wm) <= latency_hiding) + return true; + else + return false; +} + +static void evergreen_program_watermarks(struct radeon_device *rdev, + struct radeon_crtc *radeon_crtc, + u32 lb_size, u32 num_heads) +{ + struct drm_display_mode *mode = &radeon_crtc->base.mode; + struct evergreen_wm_params wm; + u32 pixel_period; + u32 line_time = 0; + u32 latency_watermark_a = 0, latency_watermark_b = 0; + u32 priority_a_mark = 0, priority_b_mark = 0; + u32 priority_a_cnt = PRIORITY_OFF; + u32 priority_b_cnt = PRIORITY_OFF; + u32 pipe_offset = radeon_crtc->crtc_id * 16; + u32 tmp, arb_control3; + fixed20_12 a, b, c; + + if (radeon_crtc->base.enabled && num_heads && mode) { + pixel_period = 1000000 / (u32)mode->clock; + line_time = min((u32)mode->crtc_htotal * pixel_period, (u32)65535); + priority_a_cnt = 0; + priority_b_cnt = 0; + + wm.yclk = rdev->pm.current_mclk * 10; + wm.sclk = rdev->pm.current_sclk * 10; + wm.disp_clk = mode->clock; + wm.src_width = mode->crtc_hdisplay; + wm.active_time = mode->crtc_hdisplay * pixel_period; + wm.blank_time = line_time - wm.active_time; + wm.interlaced = false; + if (mode->flags & DRM_MODE_FLAG_INTERLACE) + wm.interlaced = true; + wm.vsc = radeon_crtc->vsc; + wm.vtaps = 1; + if (radeon_crtc->rmx_type != RMX_OFF) + wm.vtaps = 2; + wm.bytes_per_pixel = 4; /* XXX: get this from fb config */ + wm.lb_size = lb_size; + wm.dram_channels = evergreen_get_number_of_dram_channels(rdev); + wm.num_heads = num_heads; + + /* set for high clocks */ + latency_watermark_a = min(evergreen_latency_watermark(&wm), (u32)65535); + /* set for low clocks */ + /* wm.yclk = low clk; wm.sclk = low clk */ + latency_watermark_b = min(evergreen_latency_watermark(&wm), (u32)65535); + + /* possibly force display priority to high */ + /* should really do this at mode validation time... */ + if (!evergreen_average_bandwidth_vs_dram_bandwidth_for_display(&wm) || + !evergreen_average_bandwidth_vs_available_bandwidth(&wm) || + !evergreen_check_latency_hiding(&wm) || + (rdev->disp_priority == 2)) { + DRM_DEBUG_KMS("force priority to high\n"); + priority_a_cnt |= PRIORITY_ALWAYS_ON; + priority_b_cnt |= PRIORITY_ALWAYS_ON; + } + + a.full = dfixed_const(1000); + b.full = dfixed_const(mode->clock); + b.full = dfixed_div(b, a); + c.full = dfixed_const(latency_watermark_a); + c.full = dfixed_mul(c, b); + c.full = dfixed_mul(c, radeon_crtc->hsc); + c.full = dfixed_div(c, a); + a.full = dfixed_const(16); + c.full = dfixed_div(c, a); + priority_a_mark = dfixed_trunc(c); + priority_a_cnt |= priority_a_mark & PRIORITY_MARK_MASK; + + a.full = dfixed_const(1000); + b.full = dfixed_const(mode->clock); + b.full = dfixed_div(b, a); + c.full = dfixed_const(latency_watermark_b); + c.full = dfixed_mul(c, b); + c.full = dfixed_mul(c, radeon_crtc->hsc); + c.full = dfixed_div(c, a); + a.full = dfixed_const(16); + c.full = dfixed_div(c, a); + priority_b_mark = dfixed_trunc(c); + priority_b_cnt |= priority_b_mark & PRIORITY_MARK_MASK; + } + + /* select wm A */ + arb_control3 = RREG32(PIPE0_ARBITRATION_CONTROL3 + pipe_offset); + tmp = arb_control3; + tmp &= ~LATENCY_WATERMARK_MASK(3); + tmp |= LATENCY_WATERMARK_MASK(1); + WREG32(PIPE0_ARBITRATION_CONTROL3 + pipe_offset, tmp); + WREG32(PIPE0_LATENCY_CONTROL + pipe_offset, + (LATENCY_LOW_WATERMARK(latency_watermark_a) | + LATENCY_HIGH_WATERMARK(line_time))); + /* select wm B */ + tmp = RREG32(PIPE0_ARBITRATION_CONTROL3 + pipe_offset); + tmp &= ~LATENCY_WATERMARK_MASK(3); + tmp |= LATENCY_WATERMARK_MASK(2); + WREG32(PIPE0_ARBITRATION_CONTROL3 + pipe_offset, tmp); + WREG32(PIPE0_LATENCY_CONTROL + pipe_offset, + (LATENCY_LOW_WATERMARK(latency_watermark_b) | + LATENCY_HIGH_WATERMARK(line_time))); + /* restore original selection */ + WREG32(PIPE0_ARBITRATION_CONTROL3 + pipe_offset, arb_control3); + + /* write the priority marks */ + WREG32(PRIORITY_A_CNT + radeon_crtc->crtc_offset, priority_a_cnt); + WREG32(PRIORITY_B_CNT + radeon_crtc->crtc_offset, priority_b_cnt); + +} + +/** + * evergreen_bandwidth_update - update display watermarks callback. + * + * @rdev: radeon_device pointer + * + * Update the display watermarks based on the requested mode(s) + * (evergreen+). + */ +void evergreen_bandwidth_update(struct radeon_device *rdev) +{ + struct drm_display_mode *mode0 = NULL; + struct drm_display_mode *mode1 = NULL; + u32 num_heads = 0, lb_size; + int i; + + radeon_update_display_priority(rdev); + + for (i = 0; i < rdev->num_crtc; i++) { + if (rdev->mode_info.crtcs[i]->base.enabled) + num_heads++; + } + for (i = 0; i < rdev->num_crtc; i += 2) { + mode0 = &rdev->mode_info.crtcs[i]->base.mode; + mode1 = &rdev->mode_info.crtcs[i+1]->base.mode; + lb_size = evergreen_line_buffer_adjust(rdev, rdev->mode_info.crtcs[i], mode0, mode1); + evergreen_program_watermarks(rdev, rdev->mode_info.crtcs[i], lb_size, num_heads); + lb_size = evergreen_line_buffer_adjust(rdev, rdev->mode_info.crtcs[i+1], mode1, mode0); + evergreen_program_watermarks(rdev, rdev->mode_info.crtcs[i+1], lb_size, num_heads); + } +} + +/** + * evergreen_mc_wait_for_idle - wait for MC idle callback. + * + * @rdev: radeon_device pointer + * + * Wait for the MC (memory controller) to be idle. + * (evergreen+). + * Returns 0 if the MC is idle, -1 if not. + */ +int evergreen_mc_wait_for_idle(struct radeon_device *rdev) +{ + unsigned i; + u32 tmp; + + for (i = 0; i < rdev->usec_timeout; i++) { + /* read MC_STATUS */ + tmp = RREG32(SRBM_STATUS) & 0x1F00; + if (!tmp) + return 0; + DRM_UDELAY(1); + } + return -1; +} + +/* + * GART + */ +void evergreen_pcie_gart_tlb_flush(struct radeon_device *rdev) +{ + unsigned i; + u32 tmp; + + WREG32(HDP_MEM_COHERENCY_FLUSH_CNTL, 0x1); + + WREG32(VM_CONTEXT0_REQUEST_RESPONSE, REQUEST_TYPE(1)); + for (i = 0; i < rdev->usec_timeout; i++) { + /* read MC_STATUS */ + tmp = RREG32(VM_CONTEXT0_REQUEST_RESPONSE); + tmp = (tmp & RESPONSE_TYPE_MASK) >> RESPONSE_TYPE_SHIFT; + if (tmp == 2) { + DRM_ERROR("[drm] r600 flush TLB failed\n"); + return; + } + if (tmp) { + return; + } + DRM_UDELAY(1); + } +} + +static int evergreen_pcie_gart_enable(struct radeon_device *rdev) +{ + u32 tmp; + int r; + + if (rdev->gart.robj == NULL) { + dev_err(rdev->dev, "No VRAM object for PCIE GART.\n"); + return -EINVAL; + } + r = radeon_gart_table_vram_pin(rdev); + if (r) + return r; + radeon_gart_restore(rdev); + /* Setup L2 cache */ + WREG32(VM_L2_CNTL, ENABLE_L2_CACHE | ENABLE_L2_FRAGMENT_PROCESSING | + ENABLE_L2_PTE_CACHE_LRU_UPDATE_BY_WRITE | + EFFECTIVE_L2_QUEUE_SIZE(7)); + WREG32(VM_L2_CNTL2, 0); + WREG32(VM_L2_CNTL3, BANK_SELECT(0) | CACHE_UPDATE_MODE(2)); + /* Setup TLB control */ + tmp = ENABLE_L1_TLB | ENABLE_L1_FRAGMENT_PROCESSING | + SYSTEM_ACCESS_MODE_NOT_IN_SYS | + SYSTEM_APERTURE_UNMAPPED_ACCESS_PASS_THRU | + EFFECTIVE_L1_TLB_SIZE(5) | EFFECTIVE_L1_QUEUE_SIZE(5); + if (rdev->flags & RADEON_IS_IGP) { + WREG32(FUS_MC_VM_MD_L1_TLB0_CNTL, tmp); + WREG32(FUS_MC_VM_MD_L1_TLB1_CNTL, tmp); + WREG32(FUS_MC_VM_MD_L1_TLB2_CNTL, tmp); + } else { + WREG32(MC_VM_MD_L1_TLB0_CNTL, tmp); + WREG32(MC_VM_MD_L1_TLB1_CNTL, tmp); + WREG32(MC_VM_MD_L1_TLB2_CNTL, tmp); + if ((rdev->family == CHIP_JUNIPER) || + (rdev->family == CHIP_CYPRESS) || + (rdev->family == CHIP_HEMLOCK) || + (rdev->family == CHIP_BARTS)) + WREG32(MC_VM_MD_L1_TLB3_CNTL, tmp); + } + WREG32(MC_VM_MB_L1_TLB0_CNTL, tmp); + WREG32(MC_VM_MB_L1_TLB1_CNTL, tmp); + WREG32(MC_VM_MB_L1_TLB2_CNTL, tmp); + WREG32(MC_VM_MB_L1_TLB3_CNTL, tmp); + WREG32(VM_CONTEXT0_PAGE_TABLE_START_ADDR, rdev->mc.gtt_start >> 12); + WREG32(VM_CONTEXT0_PAGE_TABLE_END_ADDR, rdev->mc.gtt_end >> 12); + WREG32(VM_CONTEXT0_PAGE_TABLE_BASE_ADDR, rdev->gart.table_addr >> 12); + WREG32(VM_CONTEXT0_CNTL, ENABLE_CONTEXT | PAGE_TABLE_DEPTH(0) | + RANGE_PROTECTION_FAULT_ENABLE_DEFAULT); + WREG32(VM_CONTEXT0_PROTECTION_FAULT_DEFAULT_ADDR, + (u32)(rdev->dummy_page.addr >> 12)); + WREG32(VM_CONTEXT1_CNTL, 0); + + evergreen_pcie_gart_tlb_flush(rdev); + DRM_INFO("PCIE GART of %uM enabled (table at 0x%016llX).\n", + (unsigned)(rdev->mc.gtt_size >> 20), + (unsigned long long)rdev->gart.table_addr); + rdev->gart.ready = true; + return 0; +} + +static void evergreen_pcie_gart_disable(struct radeon_device *rdev) +{ + u32 tmp; + + /* Disable all tables */ + WREG32(VM_CONTEXT0_CNTL, 0); + WREG32(VM_CONTEXT1_CNTL, 0); + + /* Setup L2 cache */ + WREG32(VM_L2_CNTL, ENABLE_L2_FRAGMENT_PROCESSING | + EFFECTIVE_L2_QUEUE_SIZE(7)); + WREG32(VM_L2_CNTL2, 0); + WREG32(VM_L2_CNTL3, BANK_SELECT(0) | CACHE_UPDATE_MODE(2)); + /* Setup TLB control */ + tmp = EFFECTIVE_L1_TLB_SIZE(5) | EFFECTIVE_L1_QUEUE_SIZE(5); + WREG32(MC_VM_MD_L1_TLB0_CNTL, tmp); + WREG32(MC_VM_MD_L1_TLB1_CNTL, tmp); + WREG32(MC_VM_MD_L1_TLB2_CNTL, tmp); + WREG32(MC_VM_MB_L1_TLB0_CNTL, tmp); + WREG32(MC_VM_MB_L1_TLB1_CNTL, tmp); + WREG32(MC_VM_MB_L1_TLB2_CNTL, tmp); + WREG32(MC_VM_MB_L1_TLB3_CNTL, tmp); + radeon_gart_table_vram_unpin(rdev); +} + +static void evergreen_pcie_gart_fini(struct radeon_device *rdev) +{ + evergreen_pcie_gart_disable(rdev); + radeon_gart_table_vram_free(rdev); + radeon_gart_fini(rdev); +} + + +static void evergreen_agp_enable(struct radeon_device *rdev) +{ + u32 tmp; + + /* Setup L2 cache */ + WREG32(VM_L2_CNTL, ENABLE_L2_CACHE | ENABLE_L2_FRAGMENT_PROCESSING | + ENABLE_L2_PTE_CACHE_LRU_UPDATE_BY_WRITE | + EFFECTIVE_L2_QUEUE_SIZE(7)); + WREG32(VM_L2_CNTL2, 0); + WREG32(VM_L2_CNTL3, BANK_SELECT(0) | CACHE_UPDATE_MODE(2)); + /* Setup TLB control */ + tmp = ENABLE_L1_TLB | ENABLE_L1_FRAGMENT_PROCESSING | + SYSTEM_ACCESS_MODE_NOT_IN_SYS | + SYSTEM_APERTURE_UNMAPPED_ACCESS_PASS_THRU | + EFFECTIVE_L1_TLB_SIZE(5) | EFFECTIVE_L1_QUEUE_SIZE(5); + WREG32(MC_VM_MD_L1_TLB0_CNTL, tmp); + WREG32(MC_VM_MD_L1_TLB1_CNTL, tmp); + WREG32(MC_VM_MD_L1_TLB2_CNTL, tmp); + WREG32(MC_VM_MB_L1_TLB0_CNTL, tmp); + WREG32(MC_VM_MB_L1_TLB1_CNTL, tmp); + WREG32(MC_VM_MB_L1_TLB2_CNTL, tmp); + WREG32(MC_VM_MB_L1_TLB3_CNTL, tmp); + WREG32(VM_CONTEXT0_CNTL, 0); + WREG32(VM_CONTEXT1_CNTL, 0); +} + +void evergreen_mc_stop(struct radeon_device *rdev, struct evergreen_mc_save *save) +{ + u32 crtc_enabled, tmp, frame_count, blackout; + int i, j; + + save->vga_render_control = RREG32(VGA_RENDER_CONTROL); + save->vga_hdp_control = RREG32(VGA_HDP_CONTROL); + + /* disable VGA render */ + WREG32(VGA_RENDER_CONTROL, 0); + /* blank the display controllers */ + for (i = 0; i < rdev->num_crtc; i++) { + crtc_enabled = RREG32(EVERGREEN_CRTC_CONTROL + crtc_offsets[i]) & EVERGREEN_CRTC_MASTER_EN; + if (crtc_enabled) { + save->crtc_enabled[i] = true; + if (ASIC_IS_DCE6(rdev)) { + tmp = RREG32(EVERGREEN_CRTC_BLANK_CONTROL + crtc_offsets[i]); + if (!(tmp & EVERGREEN_CRTC_BLANK_DATA_EN)) { + radeon_wait_for_vblank(rdev, i); + tmp |= EVERGREEN_CRTC_BLANK_DATA_EN; + WREG32(EVERGREEN_CRTC_UPDATE_LOCK + crtc_offsets[i], 1); + WREG32(EVERGREEN_CRTC_BLANK_CONTROL + crtc_offsets[i], tmp); + WREG32(EVERGREEN_CRTC_UPDATE_LOCK + crtc_offsets[i], 0); + } + } else { + tmp = RREG32(EVERGREEN_CRTC_CONTROL + crtc_offsets[i]); + if (!(tmp & EVERGREEN_CRTC_DISP_READ_REQUEST_DISABLE)) { + radeon_wait_for_vblank(rdev, i); + tmp |= EVERGREEN_CRTC_DISP_READ_REQUEST_DISABLE; + WREG32(EVERGREEN_CRTC_UPDATE_LOCK + crtc_offsets[i], 1); + WREG32(EVERGREEN_CRTC_CONTROL + crtc_offsets[i], tmp); + WREG32(EVERGREEN_CRTC_UPDATE_LOCK + crtc_offsets[i], 0); + } + } + /* wait for the next frame */ + frame_count = radeon_get_vblank_counter(rdev, i); + for (j = 0; j < rdev->usec_timeout; j++) { + if (radeon_get_vblank_counter(rdev, i) != frame_count) + break; + DRM_UDELAY(1); + } + } else { + save->crtc_enabled[i] = false; + } + } + + radeon_mc_wait_for_idle(rdev); + + blackout = RREG32(MC_SHARED_BLACKOUT_CNTL); + if ((blackout & BLACKOUT_MODE_MASK) != 1) { + /* Block CPU access */ + WREG32(BIF_FB_EN, 0); + /* blackout the MC */ + blackout &= ~BLACKOUT_MODE_MASK; + WREG32(MC_SHARED_BLACKOUT_CNTL, blackout | 1); + } + /* wait for the MC to settle */ + DRM_UDELAY(100); +} + +void evergreen_mc_resume(struct radeon_device *rdev, struct evergreen_mc_save *save) +{ + u32 tmp, frame_count; + int i, j; + + /* update crtc base addresses */ + for (i = 0; i < rdev->num_crtc; i++) { + WREG32(EVERGREEN_GRPH_PRIMARY_SURFACE_ADDRESS_HIGH + crtc_offsets[i], + upper_32_bits(rdev->mc.vram_start)); + WREG32(EVERGREEN_GRPH_SECONDARY_SURFACE_ADDRESS_HIGH + crtc_offsets[i], + upper_32_bits(rdev->mc.vram_start)); + WREG32(EVERGREEN_GRPH_PRIMARY_SURFACE_ADDRESS + crtc_offsets[i], + (u32)rdev->mc.vram_start); + WREG32(EVERGREEN_GRPH_SECONDARY_SURFACE_ADDRESS + crtc_offsets[i], + (u32)rdev->mc.vram_start); + } + WREG32(EVERGREEN_VGA_MEMORY_BASE_ADDRESS_HIGH, upper_32_bits(rdev->mc.vram_start)); + WREG32(EVERGREEN_VGA_MEMORY_BASE_ADDRESS, (u32)rdev->mc.vram_start); + + /* unblackout the MC */ + tmp = RREG32(MC_SHARED_BLACKOUT_CNTL); + tmp &= ~BLACKOUT_MODE_MASK; + WREG32(MC_SHARED_BLACKOUT_CNTL, tmp); + /* allow CPU access */ + WREG32(BIF_FB_EN, FB_READ_EN | FB_WRITE_EN); + + for (i = 0; i < rdev->num_crtc; i++) { + if (save->crtc_enabled[i]) { + if (ASIC_IS_DCE6(rdev)) { + tmp = RREG32(EVERGREEN_CRTC_BLANK_CONTROL + crtc_offsets[i]); + tmp |= EVERGREEN_CRTC_BLANK_DATA_EN; + WREG32(EVERGREEN_CRTC_UPDATE_LOCK + crtc_offsets[i], 1); + WREG32(EVERGREEN_CRTC_BLANK_CONTROL + crtc_offsets[i], tmp); + WREG32(EVERGREEN_CRTC_UPDATE_LOCK + crtc_offsets[i], 0); + } else { + tmp = RREG32(EVERGREEN_CRTC_CONTROL + crtc_offsets[i]); + tmp &= ~EVERGREEN_CRTC_DISP_READ_REQUEST_DISABLE; + WREG32(EVERGREEN_CRTC_UPDATE_LOCK + crtc_offsets[i], 1); + WREG32(EVERGREEN_CRTC_CONTROL + crtc_offsets[i], tmp); + WREG32(EVERGREEN_CRTC_UPDATE_LOCK + crtc_offsets[i], 0); + } + /* wait for the next frame */ + frame_count = radeon_get_vblank_counter(rdev, i); + for (j = 0; j < rdev->usec_timeout; j++) { + if (radeon_get_vblank_counter(rdev, i) != frame_count) + break; + DRM_UDELAY(1); + } + } + } + /* Unlock vga access */ + WREG32(VGA_HDP_CONTROL, save->vga_hdp_control); + DRM_MDELAY(1); + WREG32(VGA_RENDER_CONTROL, save->vga_render_control); +} + +void evergreen_mc_program(struct radeon_device *rdev) +{ + struct evergreen_mc_save save; + u32 tmp; + int i, j; + + /* Initialize HDP */ + for (i = 0, j = 0; i < 32; i++, j += 0x18) { + WREG32((0x2c14 + j), 0x00000000); + WREG32((0x2c18 + j), 0x00000000); + WREG32((0x2c1c + j), 0x00000000); + WREG32((0x2c20 + j), 0x00000000); + WREG32((0x2c24 + j), 0x00000000); + } + WREG32(HDP_REG_COHERENCY_FLUSH_CNTL, 0); + + evergreen_mc_stop(rdev, &save); + if (evergreen_mc_wait_for_idle(rdev)) { + dev_warn(rdev->dev, "Wait for MC idle timedout !\n"); + } + /* Lockout access through VGA aperture*/ + WREG32(VGA_HDP_CONTROL, VGA_MEMORY_DISABLE); + /* Update configuration */ + if (rdev->flags & RADEON_IS_AGP) { + if (rdev->mc.vram_start < rdev->mc.gtt_start) { + /* VRAM before AGP */ + WREG32(MC_VM_SYSTEM_APERTURE_LOW_ADDR, + rdev->mc.vram_start >> 12); + WREG32(MC_VM_SYSTEM_APERTURE_HIGH_ADDR, + rdev->mc.gtt_end >> 12); + } else { + /* VRAM after AGP */ + WREG32(MC_VM_SYSTEM_APERTURE_LOW_ADDR, + rdev->mc.gtt_start >> 12); + WREG32(MC_VM_SYSTEM_APERTURE_HIGH_ADDR, + rdev->mc.vram_end >> 12); + } + } else { + WREG32(MC_VM_SYSTEM_APERTURE_LOW_ADDR, + rdev->mc.vram_start >> 12); + WREG32(MC_VM_SYSTEM_APERTURE_HIGH_ADDR, + rdev->mc.vram_end >> 12); + } + WREG32(MC_VM_SYSTEM_APERTURE_DEFAULT_ADDR, rdev->vram_scratch.gpu_addr >> 12); + /* llano/ontario only */ + if ((rdev->family == CHIP_PALM) || + (rdev->family == CHIP_SUMO) || + (rdev->family == CHIP_SUMO2)) { + tmp = RREG32(MC_FUS_VM_FB_OFFSET) & 0x000FFFFF; + tmp |= ((rdev->mc.vram_end >> 20) & 0xF) << 24; + tmp |= ((rdev->mc.vram_start >> 20) & 0xF) << 20; + WREG32(MC_FUS_VM_FB_OFFSET, tmp); + } + tmp = ((rdev->mc.vram_end >> 24) & 0xFFFF) << 16; + tmp |= ((rdev->mc.vram_start >> 24) & 0xFFFF); + WREG32(MC_VM_FB_LOCATION, tmp); + WREG32(HDP_NONSURFACE_BASE, (rdev->mc.vram_start >> 8)); + WREG32(HDP_NONSURFACE_INFO, (2 << 7) | (1 << 30)); + WREG32(HDP_NONSURFACE_SIZE, 0x3FFFFFFF); + if (rdev->flags & RADEON_IS_AGP) { + WREG32(MC_VM_AGP_TOP, rdev->mc.gtt_end >> 16); + WREG32(MC_VM_AGP_BOT, rdev->mc.gtt_start >> 16); + WREG32(MC_VM_AGP_BASE, rdev->mc.agp_base >> 22); + } else { + WREG32(MC_VM_AGP_BASE, 0); + WREG32(MC_VM_AGP_TOP, 0x0FFFFFFF); + WREG32(MC_VM_AGP_BOT, 0x0FFFFFFF); + } + if (evergreen_mc_wait_for_idle(rdev)) { + dev_warn(rdev->dev, "Wait for MC idle timedout !\n"); + } + evergreen_mc_resume(rdev, &save); + /* we need to own VRAM, so turn off the VGA renderer here + * to stop it overwriting our objects */ + rv515_vga_render_disable(rdev); +} + +/* + * CP. + */ +void evergreen_ring_ib_execute(struct radeon_device *rdev, struct radeon_ib *ib) +{ + struct radeon_ring *ring = &rdev->ring[ib->ring]; + u32 next_rptr; + + /* set to DX10/11 mode */ + radeon_ring_write(ring, PACKET3(PACKET3_MODE_CONTROL, 0)); + radeon_ring_write(ring, 1); + + if (ring->rptr_save_reg) { + next_rptr = ring->wptr + 3 + 4; + radeon_ring_write(ring, PACKET3(PACKET3_SET_CONFIG_REG, 1)); + radeon_ring_write(ring, ((ring->rptr_save_reg - + PACKET3_SET_CONFIG_REG_START) >> 2)); + radeon_ring_write(ring, next_rptr); + } else if (rdev->wb.enabled) { + next_rptr = ring->wptr + 5 + 4; + radeon_ring_write(ring, PACKET3(PACKET3_MEM_WRITE, 3)); + radeon_ring_write(ring, ring->next_rptr_gpu_addr & 0xfffffffc); + radeon_ring_write(ring, (upper_32_bits(ring->next_rptr_gpu_addr) & 0xff) | (1 << 18)); + radeon_ring_write(ring, next_rptr); + radeon_ring_write(ring, 0); + } + + radeon_ring_write(ring, PACKET3(PACKET3_INDIRECT_BUFFER, 2)); + radeon_ring_write(ring, +#ifdef __BIG_ENDIAN + (2 << 0) | +#endif + (ib->gpu_addr & 0xFFFFFFFC)); + radeon_ring_write(ring, upper_32_bits(ib->gpu_addr) & 0xFF); + radeon_ring_write(ring, ib->length_dw); +} + + +static int evergreen_cp_load_microcode(struct radeon_device *rdev) +{ + const __be32 *fw_data; + int i; + + if (!rdev->me_fw || !rdev->pfp_fw) + return -EINVAL; + + r700_cp_stop(rdev); + WREG32(CP_RB_CNTL, +#ifdef __BIG_ENDIAN + BUF_SWAP_32BIT | +#endif + RB_NO_UPDATE | RB_BLKSZ(15) | RB_BUFSZ(3)); + + fw_data = (const __be32 *)rdev->pfp_fw->data; + WREG32(CP_PFP_UCODE_ADDR, 0); + for (i = 0; i < EVERGREEN_PFP_UCODE_SIZE; i++) + WREG32(CP_PFP_UCODE_DATA, be32_to_cpup(fw_data++)); + WREG32(CP_PFP_UCODE_ADDR, 0); + + fw_data = (const __be32 *)rdev->me_fw->data; + WREG32(CP_ME_RAM_WADDR, 0); + for (i = 0; i < EVERGREEN_PM4_UCODE_SIZE; i++) + WREG32(CP_ME_RAM_DATA, be32_to_cpup(fw_data++)); + + WREG32(CP_PFP_UCODE_ADDR, 0); + WREG32(CP_ME_RAM_WADDR, 0); + WREG32(CP_ME_RAM_RADDR, 0); + return 0; +} + +static int evergreen_cp_start(struct radeon_device *rdev) +{ + struct radeon_ring *ring = &rdev->ring[RADEON_RING_TYPE_GFX_INDEX]; + int r, i; + uint32_t cp_me; + + r = radeon_ring_lock(rdev, ring, 7); + if (r) { + DRM_ERROR("radeon: cp failed to lock ring (%d).\n", r); + return r; + } + radeon_ring_write(ring, PACKET3(PACKET3_ME_INITIALIZE, 5)); + radeon_ring_write(ring, 0x1); + radeon_ring_write(ring, 0x0); + radeon_ring_write(ring, rdev->config.evergreen.max_hw_contexts - 1); + radeon_ring_write(ring, PACKET3_ME_INITIALIZE_DEVICE_ID(1)); + radeon_ring_write(ring, 0); + radeon_ring_write(ring, 0); + radeon_ring_unlock_commit(rdev, ring); + + cp_me = 0xff; + WREG32(CP_ME_CNTL, cp_me); + + r = radeon_ring_lock(rdev, ring, evergreen_default_size + 19); + if (r) { + DRM_ERROR("radeon: cp failed to lock ring (%d).\n", r); + return r; + } + + /* setup clear context state */ + radeon_ring_write(ring, PACKET3(PACKET3_PREAMBLE_CNTL, 0)); + radeon_ring_write(ring, PACKET3_PREAMBLE_BEGIN_CLEAR_STATE); + + for (i = 0; i < evergreen_default_size; i++) + radeon_ring_write(ring, evergreen_default_state[i]); + + radeon_ring_write(ring, PACKET3(PACKET3_PREAMBLE_CNTL, 0)); + radeon_ring_write(ring, PACKET3_PREAMBLE_END_CLEAR_STATE); + + /* set clear context state */ + radeon_ring_write(ring, PACKET3(PACKET3_CLEAR_STATE, 0)); + radeon_ring_write(ring, 0); + + /* SQ_VTX_BASE_VTX_LOC */ + radeon_ring_write(ring, 0xc0026f00); + radeon_ring_write(ring, 0x00000000); + radeon_ring_write(ring, 0x00000000); + radeon_ring_write(ring, 0x00000000); + + /* Clear consts */ + radeon_ring_write(ring, 0xc0036f00); + radeon_ring_write(ring, 0x00000bc4); + radeon_ring_write(ring, 0xffffffff); + radeon_ring_write(ring, 0xffffffff); + radeon_ring_write(ring, 0xffffffff); + + radeon_ring_write(ring, 0xc0026900); + radeon_ring_write(ring, 0x00000316); + radeon_ring_write(ring, 0x0000000e); /* VGT_VERTEX_REUSE_BLOCK_CNTL */ + radeon_ring_write(ring, 0x00000010); /* */ + + radeon_ring_unlock_commit(rdev, ring); + + return 0; +} + +static int evergreen_cp_resume(struct radeon_device *rdev) +{ + struct radeon_ring *ring = &rdev->ring[RADEON_RING_TYPE_GFX_INDEX]; + u32 tmp; + u32 rb_bufsz; + int r; + + /* Reset cp; if cp is reset, then PA, SH, VGT also need to be reset */ + WREG32(GRBM_SOFT_RESET, (SOFT_RESET_CP | + SOFT_RESET_PA | + SOFT_RESET_SH | + SOFT_RESET_VGT | + SOFT_RESET_SPI | + SOFT_RESET_SX)); + RREG32(GRBM_SOFT_RESET); + DRM_MDELAY(15); + WREG32(GRBM_SOFT_RESET, 0); + RREG32(GRBM_SOFT_RESET); + + /* Set ring buffer size */ + rb_bufsz = drm_order(ring->ring_size / 8); + tmp = (drm_order(RADEON_GPU_PAGE_SIZE/8) << 8) | rb_bufsz; +#ifdef __BIG_ENDIAN + tmp |= BUF_SWAP_32BIT; +#endif + WREG32(CP_RB_CNTL, tmp); + WREG32(CP_SEM_WAIT_TIMER, 0x0); + WREG32(CP_SEM_INCOMPLETE_TIMER_CNTL, 0x0); + + /* Set the write pointer delay */ + WREG32(CP_RB_WPTR_DELAY, 0); + + /* Initialize the ring buffer's read and write pointers */ + WREG32(CP_RB_CNTL, tmp | RB_RPTR_WR_ENA); + WREG32(CP_RB_RPTR_WR, 0); + ring->wptr = 0; + WREG32(CP_RB_WPTR, ring->wptr); + + /* set the wb address whether it's enabled or not */ + WREG32(CP_RB_RPTR_ADDR, + ((rdev->wb.gpu_addr + RADEON_WB_CP_RPTR_OFFSET) & 0xFFFFFFFC)); + WREG32(CP_RB_RPTR_ADDR_HI, upper_32_bits(rdev->wb.gpu_addr + RADEON_WB_CP_RPTR_OFFSET) & 0xFF); + WREG32(SCRATCH_ADDR, ((rdev->wb.gpu_addr + RADEON_WB_SCRATCH_OFFSET) >> 8) & 0xFFFFFFFF); + + if (rdev->wb.enabled) + WREG32(SCRATCH_UMSK, 0xff); + else { + tmp |= RB_NO_UPDATE; + WREG32(SCRATCH_UMSK, 0); + } + + DRM_MDELAY(1); + WREG32(CP_RB_CNTL, tmp); + + WREG32(CP_RB_BASE, ring->gpu_addr >> 8); + WREG32(CP_DEBUG, (1 << 27) | (1 << 28)); + + ring->rptr = RREG32(CP_RB_RPTR); + + evergreen_cp_start(rdev); + ring->ready = true; + r = radeon_ring_test(rdev, RADEON_RING_TYPE_GFX_INDEX, ring); + if (r) { + ring->ready = false; + return r; + } + return 0; +} + +/* + * Core functions + */ +static void evergreen_gpu_init(struct radeon_device *rdev) +{ + u32 gb_addr_config; + u32 mc_shared_chmap, mc_arb_ramcfg; + u32 sx_debug_1; + u32 smx_dc_ctl0; + u32 sq_config; + u32 sq_lds_resource_mgmt; + u32 sq_gpr_resource_mgmt_1; + u32 sq_gpr_resource_mgmt_2; + u32 sq_gpr_resource_mgmt_3; + u32 sq_thread_resource_mgmt; + u32 sq_thread_resource_mgmt_2; + u32 sq_stack_resource_mgmt_1; + u32 sq_stack_resource_mgmt_2; + u32 sq_stack_resource_mgmt_3; + u32 vgt_cache_invalidation; + u32 hdp_host_path_cntl, tmp; + u32 disabled_rb_mask; + int i, j, num_shader_engines, ps_thread_count; + + switch (rdev->family) { + case CHIP_CYPRESS: + case CHIP_HEMLOCK: + rdev->config.evergreen.num_ses = 2; + rdev->config.evergreen.max_pipes = 4; + rdev->config.evergreen.max_tile_pipes = 8; + rdev->config.evergreen.max_simds = 10; + rdev->config.evergreen.max_backends = 4 * rdev->config.evergreen.num_ses; + rdev->config.evergreen.max_gprs = 256; + rdev->config.evergreen.max_threads = 248; + rdev->config.evergreen.max_gs_threads = 32; + rdev->config.evergreen.max_stack_entries = 512; + rdev->config.evergreen.sx_num_of_sets = 4; + rdev->config.evergreen.sx_max_export_size = 256; + rdev->config.evergreen.sx_max_export_pos_size = 64; + rdev->config.evergreen.sx_max_export_smx_size = 192; + rdev->config.evergreen.max_hw_contexts = 8; + rdev->config.evergreen.sq_num_cf_insts = 2; + + rdev->config.evergreen.sc_prim_fifo_size = 0x100; + rdev->config.evergreen.sc_hiz_tile_fifo_size = 0x30; + rdev->config.evergreen.sc_earlyz_tile_fifo_size = 0x130; + gb_addr_config = CYPRESS_GB_ADDR_CONFIG_GOLDEN; + break; + case CHIP_JUNIPER: + rdev->config.evergreen.num_ses = 1; + rdev->config.evergreen.max_pipes = 4; + rdev->config.evergreen.max_tile_pipes = 4; + rdev->config.evergreen.max_simds = 10; + rdev->config.evergreen.max_backends = 4 * rdev->config.evergreen.num_ses; + rdev->config.evergreen.max_gprs = 256; + rdev->config.evergreen.max_threads = 248; + rdev->config.evergreen.max_gs_threads = 32; + rdev->config.evergreen.max_stack_entries = 512; + rdev->config.evergreen.sx_num_of_sets = 4; + rdev->config.evergreen.sx_max_export_size = 256; + rdev->config.evergreen.sx_max_export_pos_size = 64; + rdev->config.evergreen.sx_max_export_smx_size = 192; + rdev->config.evergreen.max_hw_contexts = 8; + rdev->config.evergreen.sq_num_cf_insts = 2; + + rdev->config.evergreen.sc_prim_fifo_size = 0x100; + rdev->config.evergreen.sc_hiz_tile_fifo_size = 0x30; + rdev->config.evergreen.sc_earlyz_tile_fifo_size = 0x130; + gb_addr_config = JUNIPER_GB_ADDR_CONFIG_GOLDEN; + break; + case CHIP_REDWOOD: + rdev->config.evergreen.num_ses = 1; + rdev->config.evergreen.max_pipes = 4; + rdev->config.evergreen.max_tile_pipes = 4; + rdev->config.evergreen.max_simds = 5; + rdev->config.evergreen.max_backends = 2 * rdev->config.evergreen.num_ses; + rdev->config.evergreen.max_gprs = 256; + rdev->config.evergreen.max_threads = 248; + rdev->config.evergreen.max_gs_threads = 32; + rdev->config.evergreen.max_stack_entries = 256; + rdev->config.evergreen.sx_num_of_sets = 4; + rdev->config.evergreen.sx_max_export_size = 256; + rdev->config.evergreen.sx_max_export_pos_size = 64; + rdev->config.evergreen.sx_max_export_smx_size = 192; + rdev->config.evergreen.max_hw_contexts = 8; + rdev->config.evergreen.sq_num_cf_insts = 2; + + rdev->config.evergreen.sc_prim_fifo_size = 0x100; + rdev->config.evergreen.sc_hiz_tile_fifo_size = 0x30; + rdev->config.evergreen.sc_earlyz_tile_fifo_size = 0x130; + gb_addr_config = REDWOOD_GB_ADDR_CONFIG_GOLDEN; + break; + case CHIP_CEDAR: + default: + rdev->config.evergreen.num_ses = 1; + rdev->config.evergreen.max_pipes = 2; + rdev->config.evergreen.max_tile_pipes = 2; + rdev->config.evergreen.max_simds = 2; + rdev->config.evergreen.max_backends = 1 * rdev->config.evergreen.num_ses; + rdev->config.evergreen.max_gprs = 256; + rdev->config.evergreen.max_threads = 192; + rdev->config.evergreen.max_gs_threads = 16; + rdev->config.evergreen.max_stack_entries = 256; + rdev->config.evergreen.sx_num_of_sets = 4; + rdev->config.evergreen.sx_max_export_size = 128; + rdev->config.evergreen.sx_max_export_pos_size = 32; + rdev->config.evergreen.sx_max_export_smx_size = 96; + rdev->config.evergreen.max_hw_contexts = 4; + rdev->config.evergreen.sq_num_cf_insts = 1; + + rdev->config.evergreen.sc_prim_fifo_size = 0x40; + rdev->config.evergreen.sc_hiz_tile_fifo_size = 0x30; + rdev->config.evergreen.sc_earlyz_tile_fifo_size = 0x130; + gb_addr_config = CEDAR_GB_ADDR_CONFIG_GOLDEN; + break; + case CHIP_PALM: + rdev->config.evergreen.num_ses = 1; + rdev->config.evergreen.max_pipes = 2; + rdev->config.evergreen.max_tile_pipes = 2; + rdev->config.evergreen.max_simds = 2; + rdev->config.evergreen.max_backends = 1 * rdev->config.evergreen.num_ses; + rdev->config.evergreen.max_gprs = 256; + rdev->config.evergreen.max_threads = 192; + rdev->config.evergreen.max_gs_threads = 16; + rdev->config.evergreen.max_stack_entries = 256; + rdev->config.evergreen.sx_num_of_sets = 4; + rdev->config.evergreen.sx_max_export_size = 128; + rdev->config.evergreen.sx_max_export_pos_size = 32; + rdev->config.evergreen.sx_max_export_smx_size = 96; + rdev->config.evergreen.max_hw_contexts = 4; + rdev->config.evergreen.sq_num_cf_insts = 1; + + rdev->config.evergreen.sc_prim_fifo_size = 0x40; + rdev->config.evergreen.sc_hiz_tile_fifo_size = 0x30; + rdev->config.evergreen.sc_earlyz_tile_fifo_size = 0x130; + gb_addr_config = CEDAR_GB_ADDR_CONFIG_GOLDEN; + break; + case CHIP_SUMO: + rdev->config.evergreen.num_ses = 1; + rdev->config.evergreen.max_pipes = 4; + rdev->config.evergreen.max_tile_pipes = 4; + if (rdev->ddev->pci_device == 0x9648) + rdev->config.evergreen.max_simds = 3; + else if ((rdev->ddev->pci_device == 0x9647) || + (rdev->ddev->pci_device == 0x964a)) + rdev->config.evergreen.max_simds = 4; + else + rdev->config.evergreen.max_simds = 5; + rdev->config.evergreen.max_backends = 2 * rdev->config.evergreen.num_ses; + rdev->config.evergreen.max_gprs = 256; + rdev->config.evergreen.max_threads = 248; + rdev->config.evergreen.max_gs_threads = 32; + rdev->config.evergreen.max_stack_entries = 256; + rdev->config.evergreen.sx_num_of_sets = 4; + rdev->config.evergreen.sx_max_export_size = 256; + rdev->config.evergreen.sx_max_export_pos_size = 64; + rdev->config.evergreen.sx_max_export_smx_size = 192; + rdev->config.evergreen.max_hw_contexts = 8; + rdev->config.evergreen.sq_num_cf_insts = 2; + + rdev->config.evergreen.sc_prim_fifo_size = 0x40; + rdev->config.evergreen.sc_hiz_tile_fifo_size = 0x30; + rdev->config.evergreen.sc_earlyz_tile_fifo_size = 0x130; + gb_addr_config = SUMO_GB_ADDR_CONFIG_GOLDEN; + break; + case CHIP_SUMO2: + rdev->config.evergreen.num_ses = 1; + rdev->config.evergreen.max_pipes = 4; + rdev->config.evergreen.max_tile_pipes = 4; + rdev->config.evergreen.max_simds = 2; + rdev->config.evergreen.max_backends = 1 * rdev->config.evergreen.num_ses; + rdev->config.evergreen.max_gprs = 256; + rdev->config.evergreen.max_threads = 248; + rdev->config.evergreen.max_gs_threads = 32; + rdev->config.evergreen.max_stack_entries = 512; + rdev->config.evergreen.sx_num_of_sets = 4; + rdev->config.evergreen.sx_max_export_size = 256; + rdev->config.evergreen.sx_max_export_pos_size = 64; + rdev->config.evergreen.sx_max_export_smx_size = 192; + rdev->config.evergreen.max_hw_contexts = 8; + rdev->config.evergreen.sq_num_cf_insts = 2; + + rdev->config.evergreen.sc_prim_fifo_size = 0x40; + rdev->config.evergreen.sc_hiz_tile_fifo_size = 0x30; + rdev->config.evergreen.sc_earlyz_tile_fifo_size = 0x130; + gb_addr_config = SUMO2_GB_ADDR_CONFIG_GOLDEN; + break; + case CHIP_BARTS: + rdev->config.evergreen.num_ses = 2; + rdev->config.evergreen.max_pipes = 4; + rdev->config.evergreen.max_tile_pipes = 8; + rdev->config.evergreen.max_simds = 7; + rdev->config.evergreen.max_backends = 4 * rdev->config.evergreen.num_ses; + rdev->config.evergreen.max_gprs = 256; + rdev->config.evergreen.max_threads = 248; + rdev->config.evergreen.max_gs_threads = 32; + rdev->config.evergreen.max_stack_entries = 512; + rdev->config.evergreen.sx_num_of_sets = 4; + rdev->config.evergreen.sx_max_export_size = 256; + rdev->config.evergreen.sx_max_export_pos_size = 64; + rdev->config.evergreen.sx_max_export_smx_size = 192; + rdev->config.evergreen.max_hw_contexts = 8; + rdev->config.evergreen.sq_num_cf_insts = 2; + + rdev->config.evergreen.sc_prim_fifo_size = 0x100; + rdev->config.evergreen.sc_hiz_tile_fifo_size = 0x30; + rdev->config.evergreen.sc_earlyz_tile_fifo_size = 0x130; + gb_addr_config = BARTS_GB_ADDR_CONFIG_GOLDEN; + break; + case CHIP_TURKS: + rdev->config.evergreen.num_ses = 1; + rdev->config.evergreen.max_pipes = 4; + rdev->config.evergreen.max_tile_pipes = 4; + rdev->config.evergreen.max_simds = 6; + rdev->config.evergreen.max_backends = 2 * rdev->config.evergreen.num_ses; + rdev->config.evergreen.max_gprs = 256; + rdev->config.evergreen.max_threads = 248; + rdev->config.evergreen.max_gs_threads = 32; + rdev->config.evergreen.max_stack_entries = 256; + rdev->config.evergreen.sx_num_of_sets = 4; + rdev->config.evergreen.sx_max_export_size = 256; + rdev->config.evergreen.sx_max_export_pos_size = 64; + rdev->config.evergreen.sx_max_export_smx_size = 192; + rdev->config.evergreen.max_hw_contexts = 8; + rdev->config.evergreen.sq_num_cf_insts = 2; + + rdev->config.evergreen.sc_prim_fifo_size = 0x100; + rdev->config.evergreen.sc_hiz_tile_fifo_size = 0x30; + rdev->config.evergreen.sc_earlyz_tile_fifo_size = 0x130; + gb_addr_config = TURKS_GB_ADDR_CONFIG_GOLDEN; + break; + case CHIP_CAICOS: + rdev->config.evergreen.num_ses = 1; + rdev->config.evergreen.max_pipes = 2; + rdev->config.evergreen.max_tile_pipes = 2; + rdev->config.evergreen.max_simds = 2; + rdev->config.evergreen.max_backends = 1 * rdev->config.evergreen.num_ses; + rdev->config.evergreen.max_gprs = 256; + rdev->config.evergreen.max_threads = 192; + rdev->config.evergreen.max_gs_threads = 16; + rdev->config.evergreen.max_stack_entries = 256; + rdev->config.evergreen.sx_num_of_sets = 4; + rdev->config.evergreen.sx_max_export_size = 128; + rdev->config.evergreen.sx_max_export_pos_size = 32; + rdev->config.evergreen.sx_max_export_smx_size = 96; + rdev->config.evergreen.max_hw_contexts = 4; + rdev->config.evergreen.sq_num_cf_insts = 1; + + rdev->config.evergreen.sc_prim_fifo_size = 0x40; + rdev->config.evergreen.sc_hiz_tile_fifo_size = 0x30; + rdev->config.evergreen.sc_earlyz_tile_fifo_size = 0x130; + gb_addr_config = CAICOS_GB_ADDR_CONFIG_GOLDEN; + break; + } + + /* Initialize HDP */ + for (i = 0, j = 0; i < 32; i++, j += 0x18) { + WREG32((0x2c14 + j), 0x00000000); + WREG32((0x2c18 + j), 0x00000000); + WREG32((0x2c1c + j), 0x00000000); + WREG32((0x2c20 + j), 0x00000000); + WREG32((0x2c24 + j), 0x00000000); + } + + WREG32(GRBM_CNTL, GRBM_READ_TIMEOUT(0xff)); + + evergreen_fix_pci_max_read_req_size(rdev); + + mc_shared_chmap = RREG32(MC_SHARED_CHMAP); + if ((rdev->family == CHIP_PALM) || + (rdev->family == CHIP_SUMO) || + (rdev->family == CHIP_SUMO2)) + mc_arb_ramcfg = RREG32(FUS_MC_ARB_RAMCFG); + else + mc_arb_ramcfg = RREG32(MC_ARB_RAMCFG); + + /* setup tiling info dword. gb_addr_config is not adequate since it does + * not have bank info, so create a custom tiling dword. + * bits 3:0 num_pipes + * bits 7:4 num_banks + * bits 11:8 group_size + * bits 15:12 row_size + */ + rdev->config.evergreen.tile_config = 0; + switch (rdev->config.evergreen.max_tile_pipes) { + case 1: + default: + rdev->config.evergreen.tile_config |= (0 << 0); + break; + case 2: + rdev->config.evergreen.tile_config |= (1 << 0); + break; + case 4: + rdev->config.evergreen.tile_config |= (2 << 0); + break; + case 8: + rdev->config.evergreen.tile_config |= (3 << 0); + break; + } + /* num banks is 8 on all fusion asics. 0 = 4, 1 = 8, 2 = 16 */ + if (rdev->flags & RADEON_IS_IGP) + rdev->config.evergreen.tile_config |= 1 << 4; + else { + switch ((mc_arb_ramcfg & NOOFBANK_MASK) >> NOOFBANK_SHIFT) { + case 0: /* four banks */ + rdev->config.evergreen.tile_config |= 0 << 4; + break; + case 1: /* eight banks */ + rdev->config.evergreen.tile_config |= 1 << 4; + break; + case 2: /* sixteen banks */ + default: + rdev->config.evergreen.tile_config |= 2 << 4; + break; + } + } + rdev->config.evergreen.tile_config |= 0 << 8; + rdev->config.evergreen.tile_config |= + ((gb_addr_config & 0x30000000) >> 28) << 12; + + num_shader_engines = (gb_addr_config & NUM_SHADER_ENGINES(3) >> 12) + 1; + + if ((rdev->family >= CHIP_CEDAR) && (rdev->family <= CHIP_HEMLOCK)) { + u32 efuse_straps_4; + u32 efuse_straps_3; + + WREG32(RCU_IND_INDEX, 0x204); + efuse_straps_4 = RREG32(RCU_IND_DATA); + WREG32(RCU_IND_INDEX, 0x203); + efuse_straps_3 = RREG32(RCU_IND_DATA); + tmp = (((efuse_straps_4 & 0xf) << 4) | + ((efuse_straps_3 & 0xf0000000) >> 28)); + } else { + tmp = 0; + for (i = (rdev->config.evergreen.num_ses - 1); i >= 0; i--) { + u32 rb_disable_bitmap; + + WREG32(GRBM_GFX_INDEX, INSTANCE_BROADCAST_WRITES | SE_INDEX(i)); + WREG32(RLC_GFX_INDEX, INSTANCE_BROADCAST_WRITES | SE_INDEX(i)); + rb_disable_bitmap = (RREG32(CC_RB_BACKEND_DISABLE) & 0x00ff0000) >> 16; + tmp <<= 4; + tmp |= rb_disable_bitmap; + } + } + /* enabled rb are just the one not disabled :) */ + disabled_rb_mask = tmp; + + WREG32(GRBM_GFX_INDEX, INSTANCE_BROADCAST_WRITES | SE_BROADCAST_WRITES); + WREG32(RLC_GFX_INDEX, INSTANCE_BROADCAST_WRITES | SE_BROADCAST_WRITES); + + WREG32(GB_ADDR_CONFIG, gb_addr_config); + WREG32(DMIF_ADDR_CONFIG, gb_addr_config); + WREG32(HDP_ADDR_CONFIG, gb_addr_config); + WREG32(DMA_TILING_CONFIG, gb_addr_config); + + if ((rdev->config.evergreen.max_backends == 1) && + (rdev->flags & RADEON_IS_IGP)) { + if ((disabled_rb_mask & 3) == 1) { + /* RB0 disabled, RB1 enabled */ + tmp = 0x11111111; + } else { + /* RB1 disabled, RB0 enabled */ + tmp = 0x00000000; + } + } else { + tmp = gb_addr_config & NUM_PIPES_MASK; + tmp = r6xx_remap_render_backend(rdev, tmp, rdev->config.evergreen.max_backends, + EVERGREEN_MAX_BACKENDS, disabled_rb_mask); + } + WREG32(GB_BACKEND_MAP, tmp); + + WREG32(CGTS_SYS_TCC_DISABLE, 0); + WREG32(CGTS_TCC_DISABLE, 0); + WREG32(CGTS_USER_SYS_TCC_DISABLE, 0); + WREG32(CGTS_USER_TCC_DISABLE, 0); + + /* set HW defaults for 3D engine */ + WREG32(CP_QUEUE_THRESHOLDS, (ROQ_IB1_START(0x16) | + ROQ_IB2_START(0x2b))); + + WREG32(CP_MEQ_THRESHOLDS, STQ_SPLIT(0x30)); + + WREG32(TA_CNTL_AUX, (DISABLE_CUBE_ANISO | + SYNC_GRADIENT | + SYNC_WALKER | + SYNC_ALIGNER)); + + sx_debug_1 = RREG32(SX_DEBUG_1); + sx_debug_1 |= ENABLE_NEW_SMX_ADDRESS; + WREG32(SX_DEBUG_1, sx_debug_1); + + + smx_dc_ctl0 = RREG32(SMX_DC_CTL0); + smx_dc_ctl0 &= ~NUMBER_OF_SETS(0x1ff); + smx_dc_ctl0 |= NUMBER_OF_SETS(rdev->config.evergreen.sx_num_of_sets); + WREG32(SMX_DC_CTL0, smx_dc_ctl0); + + if (rdev->family <= CHIP_SUMO2) + WREG32(SMX_SAR_CTL0, 0x00010000); + + WREG32(SX_EXPORT_BUFFER_SIZES, (COLOR_BUFFER_SIZE((rdev->config.evergreen.sx_max_export_size / 4) - 1) | + POSITION_BUFFER_SIZE((rdev->config.evergreen.sx_max_export_pos_size / 4) - 1) | + SMX_BUFFER_SIZE((rdev->config.evergreen.sx_max_export_smx_size / 4) - 1))); + + WREG32(PA_SC_FIFO_SIZE, (SC_PRIM_FIFO_SIZE(rdev->config.evergreen.sc_prim_fifo_size) | + SC_HIZ_TILE_FIFO_SIZE(rdev->config.evergreen.sc_hiz_tile_fifo_size) | + SC_EARLYZ_TILE_FIFO_SIZE(rdev->config.evergreen.sc_earlyz_tile_fifo_size))); + + WREG32(VGT_NUM_INSTANCES, 1); + WREG32(SPI_CONFIG_CNTL, 0); + WREG32(SPI_CONFIG_CNTL_1, VTX_DONE_DELAY(4)); + WREG32(CP_PERFMON_CNTL, 0); + + WREG32(SQ_MS_FIFO_SIZES, (CACHE_FIFO_SIZE(16 * rdev->config.evergreen.sq_num_cf_insts) | + FETCH_FIFO_HIWATER(0x4) | + DONE_FIFO_HIWATER(0xe0) | + ALU_UPDATE_FIFO_HIWATER(0x8))); + + sq_config = RREG32(SQ_CONFIG); + sq_config &= ~(PS_PRIO(3) | + VS_PRIO(3) | + GS_PRIO(3) | + ES_PRIO(3)); + sq_config |= (VC_ENABLE | + EXPORT_SRC_C | + PS_PRIO(0) | + VS_PRIO(1) | + GS_PRIO(2) | + ES_PRIO(3)); + + switch (rdev->family) { + case CHIP_CEDAR: + case CHIP_PALM: + case CHIP_SUMO: + case CHIP_SUMO2: + case CHIP_CAICOS: + /* no vertex cache */ + sq_config &= ~VC_ENABLE; + break; + default: + break; + } + + sq_lds_resource_mgmt = RREG32(SQ_LDS_RESOURCE_MGMT); + + sq_gpr_resource_mgmt_1 = NUM_PS_GPRS((rdev->config.evergreen.max_gprs - (4 * 2))* 12 / 32); + sq_gpr_resource_mgmt_1 |= NUM_VS_GPRS((rdev->config.evergreen.max_gprs - (4 * 2)) * 6 / 32); + sq_gpr_resource_mgmt_1 |= NUM_CLAUSE_TEMP_GPRS(4); + sq_gpr_resource_mgmt_2 = NUM_GS_GPRS((rdev->config.evergreen.max_gprs - (4 * 2)) * 4 / 32); + sq_gpr_resource_mgmt_2 |= NUM_ES_GPRS((rdev->config.evergreen.max_gprs - (4 * 2)) * 4 / 32); + sq_gpr_resource_mgmt_3 = NUM_HS_GPRS((rdev->config.evergreen.max_gprs - (4 * 2)) * 3 / 32); + sq_gpr_resource_mgmt_3 |= NUM_LS_GPRS((rdev->config.evergreen.max_gprs - (4 * 2)) * 3 / 32); + + switch (rdev->family) { + case CHIP_CEDAR: + case CHIP_PALM: + case CHIP_SUMO: + case CHIP_SUMO2: + ps_thread_count = 96; + break; + default: + ps_thread_count = 128; + break; + } + + sq_thread_resource_mgmt = NUM_PS_THREADS(ps_thread_count); + sq_thread_resource_mgmt |= NUM_VS_THREADS((((rdev->config.evergreen.max_threads - ps_thread_count) / 6) / 8) * 8); + sq_thread_resource_mgmt |= NUM_GS_THREADS((((rdev->config.evergreen.max_threads - ps_thread_count) / 6) / 8) * 8); + sq_thread_resource_mgmt |= NUM_ES_THREADS((((rdev->config.evergreen.max_threads - ps_thread_count) / 6) / 8) * 8); + sq_thread_resource_mgmt_2 = NUM_HS_THREADS((((rdev->config.evergreen.max_threads - ps_thread_count) / 6) / 8) * 8); + sq_thread_resource_mgmt_2 |= NUM_LS_THREADS((((rdev->config.evergreen.max_threads - ps_thread_count) / 6) / 8) * 8); + + sq_stack_resource_mgmt_1 = NUM_PS_STACK_ENTRIES((rdev->config.evergreen.max_stack_entries * 1) / 6); + sq_stack_resource_mgmt_1 |= NUM_VS_STACK_ENTRIES((rdev->config.evergreen.max_stack_entries * 1) / 6); + sq_stack_resource_mgmt_2 = NUM_GS_STACK_ENTRIES((rdev->config.evergreen.max_stack_entries * 1) / 6); + sq_stack_resource_mgmt_2 |= NUM_ES_STACK_ENTRIES((rdev->config.evergreen.max_stack_entries * 1) / 6); + sq_stack_resource_mgmt_3 = NUM_HS_STACK_ENTRIES((rdev->config.evergreen.max_stack_entries * 1) / 6); + sq_stack_resource_mgmt_3 |= NUM_LS_STACK_ENTRIES((rdev->config.evergreen.max_stack_entries * 1) / 6); + + WREG32(SQ_CONFIG, sq_config); + WREG32(SQ_GPR_RESOURCE_MGMT_1, sq_gpr_resource_mgmt_1); + WREG32(SQ_GPR_RESOURCE_MGMT_2, sq_gpr_resource_mgmt_2); + WREG32(SQ_GPR_RESOURCE_MGMT_3, sq_gpr_resource_mgmt_3); + WREG32(SQ_THREAD_RESOURCE_MGMT, sq_thread_resource_mgmt); + WREG32(SQ_THREAD_RESOURCE_MGMT_2, sq_thread_resource_mgmt_2); + WREG32(SQ_STACK_RESOURCE_MGMT_1, sq_stack_resource_mgmt_1); + WREG32(SQ_STACK_RESOURCE_MGMT_2, sq_stack_resource_mgmt_2); + WREG32(SQ_STACK_RESOURCE_MGMT_3, sq_stack_resource_mgmt_3); + WREG32(SQ_DYN_GPR_CNTL_PS_FLUSH_REQ, 0); + WREG32(SQ_LDS_RESOURCE_MGMT, sq_lds_resource_mgmt); + + WREG32(PA_SC_FORCE_EOV_MAX_CNTS, (FORCE_EOV_MAX_CLK_CNT(4095) | + FORCE_EOV_MAX_REZ_CNT(255))); + + switch (rdev->family) { + case CHIP_CEDAR: + case CHIP_PALM: + case CHIP_SUMO: + case CHIP_SUMO2: + case CHIP_CAICOS: + vgt_cache_invalidation = CACHE_INVALIDATION(TC_ONLY); + break; + default: + vgt_cache_invalidation = CACHE_INVALIDATION(VC_AND_TC); + break; + } + vgt_cache_invalidation |= AUTO_INVLD_EN(ES_AND_GS_AUTO); + WREG32(VGT_CACHE_INVALIDATION, vgt_cache_invalidation); + + WREG32(VGT_GS_VERTEX_REUSE, 16); + WREG32(PA_SU_LINE_STIPPLE_VALUE, 0); + WREG32(PA_SC_LINE_STIPPLE_STATE, 0); + + WREG32(VGT_VERTEX_REUSE_BLOCK_CNTL, 14); + WREG32(VGT_OUT_DEALLOC_CNTL, 16); + + WREG32(CB_PERF_CTR0_SEL_0, 0); + WREG32(CB_PERF_CTR0_SEL_1, 0); + WREG32(CB_PERF_CTR1_SEL_0, 0); + WREG32(CB_PERF_CTR1_SEL_1, 0); + WREG32(CB_PERF_CTR2_SEL_0, 0); + WREG32(CB_PERF_CTR2_SEL_1, 0); + WREG32(CB_PERF_CTR3_SEL_0, 0); + WREG32(CB_PERF_CTR3_SEL_1, 0); + + /* clear render buffer base addresses */ + WREG32(CB_COLOR0_BASE, 0); + WREG32(CB_COLOR1_BASE, 0); + WREG32(CB_COLOR2_BASE, 0); + WREG32(CB_COLOR3_BASE, 0); + WREG32(CB_COLOR4_BASE, 0); + WREG32(CB_COLOR5_BASE, 0); + WREG32(CB_COLOR6_BASE, 0); + WREG32(CB_COLOR7_BASE, 0); + WREG32(CB_COLOR8_BASE, 0); + WREG32(CB_COLOR9_BASE, 0); + WREG32(CB_COLOR10_BASE, 0); + WREG32(CB_COLOR11_BASE, 0); + + /* set the shader const cache sizes to 0 */ + for (i = SQ_ALU_CONST_BUFFER_SIZE_PS_0; i < 0x28200; i += 4) + WREG32(i, 0); + for (i = SQ_ALU_CONST_BUFFER_SIZE_HS_0; i < 0x29000; i += 4) + WREG32(i, 0); + + tmp = RREG32(HDP_MISC_CNTL); + tmp |= HDP_FLUSH_INVALIDATE_CACHE; + WREG32(HDP_MISC_CNTL, tmp); + + hdp_host_path_cntl = RREG32(HDP_HOST_PATH_CNTL); + WREG32(HDP_HOST_PATH_CNTL, hdp_host_path_cntl); + + WREG32(PA_CL_ENHANCE, CLIP_VTX_REORDER_ENA | NUM_CLIP_SEQ(3)); + + DRM_UDELAY(50); + +} + +int evergreen_mc_init(struct radeon_device *rdev) +{ + u32 tmp; + int chansize, numchan; + + /* Get VRAM informations */ + rdev->mc.vram_is_ddr = true; + if ((rdev->family == CHIP_PALM) || + (rdev->family == CHIP_SUMO) || + (rdev->family == CHIP_SUMO2)) + tmp = RREG32(FUS_MC_ARB_RAMCFG); + else + tmp = RREG32(MC_ARB_RAMCFG); + if (tmp & CHANSIZE_OVERRIDE) { + chansize = 16; + } else if (tmp & CHANSIZE_MASK) { + chansize = 64; + } else { + chansize = 32; + } + tmp = RREG32(MC_SHARED_CHMAP); + switch ((tmp & NOOFCHAN_MASK) >> NOOFCHAN_SHIFT) { + case 0: + default: + numchan = 1; + break; + case 1: + numchan = 2; + break; + case 2: + numchan = 4; + break; + case 3: + numchan = 8; + break; + } + rdev->mc.vram_width = numchan * chansize; + /* Could aper size report 0 ? */ + rdev->mc.aper_base = drm_get_resource_start(rdev->ddev, 0); + rdev->mc.aper_size = drm_get_resource_len(rdev->ddev, 0); + /* Setup GPU memory space */ + if ((rdev->family == CHIP_PALM) || + (rdev->family == CHIP_SUMO) || + (rdev->family == CHIP_SUMO2)) { + /* size in bytes on fusion */ + rdev->mc.mc_vram_size = RREG32(CONFIG_MEMSIZE); + rdev->mc.real_vram_size = RREG32(CONFIG_MEMSIZE); + } else { + /* size in MB on evergreen/cayman/tn */ + rdev->mc.mc_vram_size = RREG32(CONFIG_MEMSIZE) * 1024 * 1024; + rdev->mc.real_vram_size = RREG32(CONFIG_MEMSIZE) * 1024 * 1024; + } + rdev->mc.visible_vram_size = rdev->mc.aper_size; + r700_vram_gtt_location(rdev, &rdev->mc); + radeon_update_bandwidth_info(rdev); + + return 0; +} + +bool evergreen_gpu_is_lockup(struct radeon_device *rdev, struct radeon_ring *ring) +{ + u32 srbm_status; + u32 grbm_status; + u32 grbm_status_se0, grbm_status_se1; + + srbm_status = RREG32(SRBM_STATUS); + grbm_status = RREG32(GRBM_STATUS); + grbm_status_se0 = RREG32(GRBM_STATUS_SE0); + grbm_status_se1 = RREG32(GRBM_STATUS_SE1); + if (!(grbm_status & GUI_ACTIVE)) { + radeon_ring_lockup_update(ring); + return false; + } + /* force CP activities */ + radeon_ring_force_activity(rdev, ring); + return radeon_ring_test_lockup(rdev, ring); +} + +static void evergreen_gpu_soft_reset_gfx(struct radeon_device *rdev) +{ + u32 grbm_reset = 0; + + if (!(RREG32(GRBM_STATUS) & GUI_ACTIVE)) + return; + + dev_info(rdev->dev, " GRBM_STATUS = 0x%08X\n", + RREG32(GRBM_STATUS)); + dev_info(rdev->dev, " GRBM_STATUS_SE0 = 0x%08X\n", + RREG32(GRBM_STATUS_SE0)); + dev_info(rdev->dev, " GRBM_STATUS_SE1 = 0x%08X\n", + RREG32(GRBM_STATUS_SE1)); + dev_info(rdev->dev, " SRBM_STATUS = 0x%08X\n", + RREG32(SRBM_STATUS)); + dev_info(rdev->dev, " R_008674_CP_STALLED_STAT1 = 0x%08X\n", + RREG32(CP_STALLED_STAT1)); + dev_info(rdev->dev, " R_008678_CP_STALLED_STAT2 = 0x%08X\n", + RREG32(CP_STALLED_STAT2)); + dev_info(rdev->dev, " R_00867C_CP_BUSY_STAT = 0x%08X\n", + RREG32(CP_BUSY_STAT)); + dev_info(rdev->dev, " R_008680_CP_STAT = 0x%08X\n", + RREG32(CP_STAT)); + + /* Disable CP parsing/prefetching */ + WREG32(CP_ME_CNTL, CP_ME_HALT | CP_PFP_HALT); + + /* reset all the gfx blocks */ + grbm_reset = (SOFT_RESET_CP | + SOFT_RESET_CB | + SOFT_RESET_DB | + SOFT_RESET_PA | + SOFT_RESET_SC | + SOFT_RESET_SPI | + SOFT_RESET_SH | + SOFT_RESET_SX | + SOFT_RESET_TC | + SOFT_RESET_TA | + SOFT_RESET_VC | + SOFT_RESET_VGT); + + dev_info(rdev->dev, " GRBM_SOFT_RESET=0x%08X\n", grbm_reset); + WREG32(GRBM_SOFT_RESET, grbm_reset); + (void)RREG32(GRBM_SOFT_RESET); + DRM_UDELAY(50); + WREG32(GRBM_SOFT_RESET, 0); + (void)RREG32(GRBM_SOFT_RESET); + + dev_info(rdev->dev, " GRBM_STATUS = 0x%08X\n", + RREG32(GRBM_STATUS)); + dev_info(rdev->dev, " GRBM_STATUS_SE0 = 0x%08X\n", + RREG32(GRBM_STATUS_SE0)); + dev_info(rdev->dev, " GRBM_STATUS_SE1 = 0x%08X\n", + RREG32(GRBM_STATUS_SE1)); + dev_info(rdev->dev, " SRBM_STATUS = 0x%08X\n", + RREG32(SRBM_STATUS)); + dev_info(rdev->dev, " R_008674_CP_STALLED_STAT1 = 0x%08X\n", + RREG32(CP_STALLED_STAT1)); + dev_info(rdev->dev, " R_008678_CP_STALLED_STAT2 = 0x%08X\n", + RREG32(CP_STALLED_STAT2)); + dev_info(rdev->dev, " R_00867C_CP_BUSY_STAT = 0x%08X\n", + RREG32(CP_BUSY_STAT)); + dev_info(rdev->dev, " R_008680_CP_STAT = 0x%08X\n", + RREG32(CP_STAT)); +} + +static void evergreen_gpu_soft_reset_dma(struct radeon_device *rdev) +{ + u32 tmp; + + if (RREG32(DMA_STATUS_REG) & DMA_IDLE) + return; + + dev_info(rdev->dev, " R_00D034_DMA_STATUS_REG = 0x%08X\n", + RREG32(DMA_STATUS_REG)); + + /* Disable DMA */ + tmp = RREG32(DMA_RB_CNTL); + tmp &= ~DMA_RB_ENABLE; + WREG32(DMA_RB_CNTL, tmp); + + /* Reset dma */ + WREG32(SRBM_SOFT_RESET, SOFT_RESET_DMA); + RREG32(SRBM_SOFT_RESET); + DRM_UDELAY(50); + WREG32(SRBM_SOFT_RESET, 0); + + dev_info(rdev->dev, " R_00D034_DMA_STATUS_REG = 0x%08X\n", + RREG32(DMA_STATUS_REG)); +} + +static int evergreen_gpu_soft_reset(struct radeon_device *rdev, u32 reset_mask) +{ + struct evergreen_mc_save save; + + if (!(RREG32(GRBM_STATUS) & GUI_ACTIVE)) + reset_mask &= ~(RADEON_RESET_GFX | RADEON_RESET_COMPUTE); + + if (RREG32(DMA_STATUS_REG) & DMA_IDLE) + reset_mask &= ~RADEON_RESET_DMA; + + if (reset_mask == 0) + return 0; + + dev_info(rdev->dev, "GPU softreset: 0x%08X\n", reset_mask); + + evergreen_mc_stop(rdev, &save); + if (evergreen_mc_wait_for_idle(rdev)) { + dev_warn(rdev->dev, "Wait for MC idle timedout !\n"); + } + + if (reset_mask & (RADEON_RESET_GFX | RADEON_RESET_COMPUTE)) + evergreen_gpu_soft_reset_gfx(rdev); + + if (reset_mask & RADEON_RESET_DMA) + evergreen_gpu_soft_reset_dma(rdev); + + /* Wait a little for things to settle down */ + DRM_UDELAY(50); + + evergreen_mc_resume(rdev, &save); + return 0; +} + +int evergreen_asic_reset(struct radeon_device *rdev) +{ + return evergreen_gpu_soft_reset(rdev, (RADEON_RESET_GFX | + RADEON_RESET_COMPUTE | + RADEON_RESET_DMA)); +} + +/* Interrupts */ + +u32 evergreen_get_vblank_counter(struct radeon_device *rdev, int crtc) +{ + if (crtc >= rdev->num_crtc) + return 0; + else + return RREG32(CRTC_STATUS_FRAME_COUNT + crtc_offsets[crtc]); +} + +void evergreen_disable_interrupt_state(struct radeon_device *rdev) +{ + u32 tmp; + + if (rdev->family >= CHIP_CAYMAN) { + cayman_cp_int_cntl_setup(rdev, 0, + CNTX_BUSY_INT_ENABLE | CNTX_EMPTY_INT_ENABLE); + cayman_cp_int_cntl_setup(rdev, 1, 0); + cayman_cp_int_cntl_setup(rdev, 2, 0); + tmp = RREG32(CAYMAN_DMA1_CNTL) & ~TRAP_ENABLE; + WREG32(CAYMAN_DMA1_CNTL, tmp); + } else + WREG32(CP_INT_CNTL, CNTX_BUSY_INT_ENABLE | CNTX_EMPTY_INT_ENABLE); + tmp = RREG32(DMA_CNTL) & ~TRAP_ENABLE; + WREG32(DMA_CNTL, tmp); + WREG32(GRBM_INT_CNTL, 0); + WREG32(INT_MASK + EVERGREEN_CRTC0_REGISTER_OFFSET, 0); + WREG32(INT_MASK + EVERGREEN_CRTC1_REGISTER_OFFSET, 0); + if (rdev->num_crtc >= 4) { + WREG32(INT_MASK + EVERGREEN_CRTC2_REGISTER_OFFSET, 0); + WREG32(INT_MASK + EVERGREEN_CRTC3_REGISTER_OFFSET, 0); + } + if (rdev->num_crtc >= 6) { + WREG32(INT_MASK + EVERGREEN_CRTC4_REGISTER_OFFSET, 0); + WREG32(INT_MASK + EVERGREEN_CRTC5_REGISTER_OFFSET, 0); + } + + WREG32(GRPH_INT_CONTROL + EVERGREEN_CRTC0_REGISTER_OFFSET, 0); + WREG32(GRPH_INT_CONTROL + EVERGREEN_CRTC1_REGISTER_OFFSET, 0); + if (rdev->num_crtc >= 4) { + WREG32(GRPH_INT_CONTROL + EVERGREEN_CRTC2_REGISTER_OFFSET, 0); + WREG32(GRPH_INT_CONTROL + EVERGREEN_CRTC3_REGISTER_OFFSET, 0); + } + if (rdev->num_crtc >= 6) { + WREG32(GRPH_INT_CONTROL + EVERGREEN_CRTC4_REGISTER_OFFSET, 0); + WREG32(GRPH_INT_CONTROL + EVERGREEN_CRTC5_REGISTER_OFFSET, 0); + } + + /* only one DAC on DCE6 */ + if (!ASIC_IS_DCE6(rdev)) + WREG32(DACA_AUTODETECT_INT_CONTROL, 0); + WREG32(DACB_AUTODETECT_INT_CONTROL, 0); + + tmp = RREG32(DC_HPD1_INT_CONTROL) & DC_HPDx_INT_POLARITY; + WREG32(DC_HPD1_INT_CONTROL, tmp); + tmp = RREG32(DC_HPD2_INT_CONTROL) & DC_HPDx_INT_POLARITY; + WREG32(DC_HPD2_INT_CONTROL, tmp); + tmp = RREG32(DC_HPD3_INT_CONTROL) & DC_HPDx_INT_POLARITY; + WREG32(DC_HPD3_INT_CONTROL, tmp); + tmp = RREG32(DC_HPD4_INT_CONTROL) & DC_HPDx_INT_POLARITY; + WREG32(DC_HPD4_INT_CONTROL, tmp); + tmp = RREG32(DC_HPD5_INT_CONTROL) & DC_HPDx_INT_POLARITY; + WREG32(DC_HPD5_INT_CONTROL, tmp); + tmp = RREG32(DC_HPD6_INT_CONTROL) & DC_HPDx_INT_POLARITY; + WREG32(DC_HPD6_INT_CONTROL, tmp); + +} + +int evergreen_irq_set(struct radeon_device *rdev) +{ + u32 cp_int_cntl = CNTX_BUSY_INT_ENABLE | CNTX_EMPTY_INT_ENABLE; + u32 cp_int_cntl1 = 0, cp_int_cntl2 = 0; + u32 crtc1 = 0, crtc2 = 0, crtc3 = 0, crtc4 = 0, crtc5 = 0, crtc6 = 0; + u32 hpd1, hpd2, hpd3, hpd4, hpd5, hpd6; + u32 grbm_int_cntl = 0; + u32 grph1 = 0, grph2 = 0, grph3 = 0, grph4 = 0, grph5 = 0, grph6 = 0; + u32 afmt1 = 0, afmt2 = 0, afmt3 = 0, afmt4 = 0, afmt5 = 0, afmt6 = 0; + u32 dma_cntl, dma_cntl1 = 0; + + if (!rdev->irq.installed) { + dev_warn(rdev->dev, "Can't enable IRQ/MSI because no handler is installed\n"); + return -EINVAL; + } + /* don't enable anything if the ih is disabled */ + if (!rdev->ih.enabled) { + r600_disable_interrupts(rdev); + /* force the active interrupt state to all disabled */ + evergreen_disable_interrupt_state(rdev); + return 0; + } + + hpd1 = RREG32(DC_HPD1_INT_CONTROL) & ~DC_HPDx_INT_EN; + hpd2 = RREG32(DC_HPD2_INT_CONTROL) & ~DC_HPDx_INT_EN; + hpd3 = RREG32(DC_HPD3_INT_CONTROL) & ~DC_HPDx_INT_EN; + hpd4 = RREG32(DC_HPD4_INT_CONTROL) & ~DC_HPDx_INT_EN; + hpd5 = RREG32(DC_HPD5_INT_CONTROL) & ~DC_HPDx_INT_EN; + hpd6 = RREG32(DC_HPD6_INT_CONTROL) & ~DC_HPDx_INT_EN; + + afmt1 = RREG32(AFMT_AUDIO_PACKET_CONTROL + EVERGREEN_CRTC0_REGISTER_OFFSET) & ~AFMT_AZ_FORMAT_WTRIG_MASK; + afmt2 = RREG32(AFMT_AUDIO_PACKET_CONTROL + EVERGREEN_CRTC1_REGISTER_OFFSET) & ~AFMT_AZ_FORMAT_WTRIG_MASK; + afmt3 = RREG32(AFMT_AUDIO_PACKET_CONTROL + EVERGREEN_CRTC2_REGISTER_OFFSET) & ~AFMT_AZ_FORMAT_WTRIG_MASK; + afmt4 = RREG32(AFMT_AUDIO_PACKET_CONTROL + EVERGREEN_CRTC3_REGISTER_OFFSET) & ~AFMT_AZ_FORMAT_WTRIG_MASK; + afmt5 = RREG32(AFMT_AUDIO_PACKET_CONTROL + EVERGREEN_CRTC4_REGISTER_OFFSET) & ~AFMT_AZ_FORMAT_WTRIG_MASK; + afmt6 = RREG32(AFMT_AUDIO_PACKET_CONTROL + EVERGREEN_CRTC5_REGISTER_OFFSET) & ~AFMT_AZ_FORMAT_WTRIG_MASK; + + dma_cntl = RREG32(DMA_CNTL) & ~TRAP_ENABLE; + + if (rdev->family >= CHIP_CAYMAN) { + /* enable CP interrupts on all rings */ + if (atomic_read(&rdev->irq.ring_int[RADEON_RING_TYPE_GFX_INDEX])) { + DRM_DEBUG("evergreen_irq_set: sw int gfx\n"); + cp_int_cntl |= TIME_STAMP_INT_ENABLE; + } + if (atomic_read(&rdev->irq.ring_int[CAYMAN_RING_TYPE_CP1_INDEX])) { + DRM_DEBUG("evergreen_irq_set: sw int cp1\n"); + cp_int_cntl1 |= TIME_STAMP_INT_ENABLE; + } + if (atomic_read(&rdev->irq.ring_int[CAYMAN_RING_TYPE_CP2_INDEX])) { + DRM_DEBUG("evergreen_irq_set: sw int cp2\n"); + cp_int_cntl2 |= TIME_STAMP_INT_ENABLE; + } + } else { + if (atomic_read(&rdev->irq.ring_int[RADEON_RING_TYPE_GFX_INDEX])) { + DRM_DEBUG("evergreen_irq_set: sw int gfx\n"); + cp_int_cntl |= RB_INT_ENABLE; + cp_int_cntl |= TIME_STAMP_INT_ENABLE; + } + } + + if (atomic_read(&rdev->irq.ring_int[R600_RING_TYPE_DMA_INDEX])) { + DRM_DEBUG("r600_irq_set: sw int dma\n"); + dma_cntl |= TRAP_ENABLE; + } + + if (rdev->family >= CHIP_CAYMAN) { + dma_cntl1 = RREG32(CAYMAN_DMA1_CNTL) & ~TRAP_ENABLE; + if (atomic_read(&rdev->irq.ring_int[CAYMAN_RING_TYPE_DMA1_INDEX])) { + DRM_DEBUG("r600_irq_set: sw int dma1\n"); + dma_cntl1 |= TRAP_ENABLE; + } + } + + if (rdev->irq.crtc_vblank_int[0] || + atomic_read(&rdev->irq.pflip[0])) { + DRM_DEBUG("evergreen_irq_set: vblank 0\n"); + crtc1 |= VBLANK_INT_MASK; + } + if (rdev->irq.crtc_vblank_int[1] || + atomic_read(&rdev->irq.pflip[1])) { + DRM_DEBUG("evergreen_irq_set: vblank 1\n"); + crtc2 |= VBLANK_INT_MASK; + } + if (rdev->irq.crtc_vblank_int[2] || + atomic_read(&rdev->irq.pflip[2])) { + DRM_DEBUG("evergreen_irq_set: vblank 2\n"); + crtc3 |= VBLANK_INT_MASK; + } + if (rdev->irq.crtc_vblank_int[3] || + atomic_read(&rdev->irq.pflip[3])) { + DRM_DEBUG("evergreen_irq_set: vblank 3\n"); + crtc4 |= VBLANK_INT_MASK; + } + if (rdev->irq.crtc_vblank_int[4] || + atomic_read(&rdev->irq.pflip[4])) { + DRM_DEBUG("evergreen_irq_set: vblank 4\n"); + crtc5 |= VBLANK_INT_MASK; + } + if (rdev->irq.crtc_vblank_int[5] || + atomic_read(&rdev->irq.pflip[5])) { + DRM_DEBUG("evergreen_irq_set: vblank 5\n"); + crtc6 |= VBLANK_INT_MASK; + } + if (rdev->irq.hpd[0]) { + DRM_DEBUG("evergreen_irq_set: hpd 1\n"); + hpd1 |= DC_HPDx_INT_EN; + } + if (rdev->irq.hpd[1]) { + DRM_DEBUG("evergreen_irq_set: hpd 2\n"); + hpd2 |= DC_HPDx_INT_EN; + } + if (rdev->irq.hpd[2]) { + DRM_DEBUG("evergreen_irq_set: hpd 3\n"); + hpd3 |= DC_HPDx_INT_EN; + } + if (rdev->irq.hpd[3]) { + DRM_DEBUG("evergreen_irq_set: hpd 4\n"); + hpd4 |= DC_HPDx_INT_EN; + } + if (rdev->irq.hpd[4]) { + DRM_DEBUG("evergreen_irq_set: hpd 5\n"); + hpd5 |= DC_HPDx_INT_EN; + } + if (rdev->irq.hpd[5]) { + DRM_DEBUG("evergreen_irq_set: hpd 6\n"); + hpd6 |= DC_HPDx_INT_EN; + } + if (rdev->irq.afmt[0]) { + DRM_DEBUG("evergreen_irq_set: hdmi 0\n"); + afmt1 |= AFMT_AZ_FORMAT_WTRIG_MASK; + } + if (rdev->irq.afmt[1]) { + DRM_DEBUG("evergreen_irq_set: hdmi 1\n"); + afmt2 |= AFMT_AZ_FORMAT_WTRIG_MASK; + } + if (rdev->irq.afmt[2]) { + DRM_DEBUG("evergreen_irq_set: hdmi 2\n"); + afmt3 |= AFMT_AZ_FORMAT_WTRIG_MASK; + } + if (rdev->irq.afmt[3]) { + DRM_DEBUG("evergreen_irq_set: hdmi 3\n"); + afmt4 |= AFMT_AZ_FORMAT_WTRIG_MASK; + } + if (rdev->irq.afmt[4]) { + DRM_DEBUG("evergreen_irq_set: hdmi 4\n"); + afmt5 |= AFMT_AZ_FORMAT_WTRIG_MASK; + } + if (rdev->irq.afmt[5]) { + DRM_DEBUG("evergreen_irq_set: hdmi 5\n"); + afmt6 |= AFMT_AZ_FORMAT_WTRIG_MASK; + } + + if (rdev->family >= CHIP_CAYMAN) { + cayman_cp_int_cntl_setup(rdev, 0, cp_int_cntl); + cayman_cp_int_cntl_setup(rdev, 1, cp_int_cntl1); + cayman_cp_int_cntl_setup(rdev, 2, cp_int_cntl2); + } else + WREG32(CP_INT_CNTL, cp_int_cntl); + + WREG32(DMA_CNTL, dma_cntl); + + if (rdev->family >= CHIP_CAYMAN) + WREG32(CAYMAN_DMA1_CNTL, dma_cntl1); + + WREG32(GRBM_INT_CNTL, grbm_int_cntl); + + WREG32(INT_MASK + EVERGREEN_CRTC0_REGISTER_OFFSET, crtc1); + WREG32(INT_MASK + EVERGREEN_CRTC1_REGISTER_OFFSET, crtc2); + if (rdev->num_crtc >= 4) { + WREG32(INT_MASK + EVERGREEN_CRTC2_REGISTER_OFFSET, crtc3); + WREG32(INT_MASK + EVERGREEN_CRTC3_REGISTER_OFFSET, crtc4); + } + if (rdev->num_crtc >= 6) { + WREG32(INT_MASK + EVERGREEN_CRTC4_REGISTER_OFFSET, crtc5); + WREG32(INT_MASK + EVERGREEN_CRTC5_REGISTER_OFFSET, crtc6); + } + + WREG32(GRPH_INT_CONTROL + EVERGREEN_CRTC0_REGISTER_OFFSET, grph1); + WREG32(GRPH_INT_CONTROL + EVERGREEN_CRTC1_REGISTER_OFFSET, grph2); + if (rdev->num_crtc >= 4) { + WREG32(GRPH_INT_CONTROL + EVERGREEN_CRTC2_REGISTER_OFFSET, grph3); + WREG32(GRPH_INT_CONTROL + EVERGREEN_CRTC3_REGISTER_OFFSET, grph4); + } + if (rdev->num_crtc >= 6) { + WREG32(GRPH_INT_CONTROL + EVERGREEN_CRTC4_REGISTER_OFFSET, grph5); + WREG32(GRPH_INT_CONTROL + EVERGREEN_CRTC5_REGISTER_OFFSET, grph6); + } + + WREG32(DC_HPD1_INT_CONTROL, hpd1); + WREG32(DC_HPD2_INT_CONTROL, hpd2); + WREG32(DC_HPD3_INT_CONTROL, hpd3); + WREG32(DC_HPD4_INT_CONTROL, hpd4); + WREG32(DC_HPD5_INT_CONTROL, hpd5); + WREG32(DC_HPD6_INT_CONTROL, hpd6); + + WREG32(AFMT_AUDIO_PACKET_CONTROL + EVERGREEN_CRTC0_REGISTER_OFFSET, afmt1); + WREG32(AFMT_AUDIO_PACKET_CONTROL + EVERGREEN_CRTC1_REGISTER_OFFSET, afmt2); + WREG32(AFMT_AUDIO_PACKET_CONTROL + EVERGREEN_CRTC2_REGISTER_OFFSET, afmt3); + WREG32(AFMT_AUDIO_PACKET_CONTROL + EVERGREEN_CRTC3_REGISTER_OFFSET, afmt4); + WREG32(AFMT_AUDIO_PACKET_CONTROL + EVERGREEN_CRTC4_REGISTER_OFFSET, afmt5); + WREG32(AFMT_AUDIO_PACKET_CONTROL + EVERGREEN_CRTC5_REGISTER_OFFSET, afmt6); + + return 0; +} + +static void evergreen_irq_ack(struct radeon_device *rdev) +{ + u32 tmp; + + rdev->irq.stat_regs.evergreen.disp_int = RREG32(DISP_INTERRUPT_STATUS); + rdev->irq.stat_regs.evergreen.disp_int_cont = RREG32(DISP_INTERRUPT_STATUS_CONTINUE); + rdev->irq.stat_regs.evergreen.disp_int_cont2 = RREG32(DISP_INTERRUPT_STATUS_CONTINUE2); + rdev->irq.stat_regs.evergreen.disp_int_cont3 = RREG32(DISP_INTERRUPT_STATUS_CONTINUE3); + rdev->irq.stat_regs.evergreen.disp_int_cont4 = RREG32(DISP_INTERRUPT_STATUS_CONTINUE4); + rdev->irq.stat_regs.evergreen.disp_int_cont5 = RREG32(DISP_INTERRUPT_STATUS_CONTINUE5); + rdev->irq.stat_regs.evergreen.d1grph_int = RREG32(GRPH_INT_STATUS + EVERGREEN_CRTC0_REGISTER_OFFSET); + rdev->irq.stat_regs.evergreen.d2grph_int = RREG32(GRPH_INT_STATUS + EVERGREEN_CRTC1_REGISTER_OFFSET); + if (rdev->num_crtc >= 4) { + rdev->irq.stat_regs.evergreen.d3grph_int = RREG32(GRPH_INT_STATUS + EVERGREEN_CRTC2_REGISTER_OFFSET); + rdev->irq.stat_regs.evergreen.d4grph_int = RREG32(GRPH_INT_STATUS + EVERGREEN_CRTC3_REGISTER_OFFSET); + } + if (rdev->num_crtc >= 6) { + rdev->irq.stat_regs.evergreen.d5grph_int = RREG32(GRPH_INT_STATUS + EVERGREEN_CRTC4_REGISTER_OFFSET); + rdev->irq.stat_regs.evergreen.d6grph_int = RREG32(GRPH_INT_STATUS + EVERGREEN_CRTC5_REGISTER_OFFSET); + } + + rdev->irq.stat_regs.evergreen.afmt_status1 = RREG32(AFMT_STATUS + EVERGREEN_CRTC0_REGISTER_OFFSET); + rdev->irq.stat_regs.evergreen.afmt_status2 = RREG32(AFMT_STATUS + EVERGREEN_CRTC1_REGISTER_OFFSET); + rdev->irq.stat_regs.evergreen.afmt_status3 = RREG32(AFMT_STATUS + EVERGREEN_CRTC2_REGISTER_OFFSET); + rdev->irq.stat_regs.evergreen.afmt_status4 = RREG32(AFMT_STATUS + EVERGREEN_CRTC3_REGISTER_OFFSET); + rdev->irq.stat_regs.evergreen.afmt_status5 = RREG32(AFMT_STATUS + EVERGREEN_CRTC4_REGISTER_OFFSET); + rdev->irq.stat_regs.evergreen.afmt_status6 = RREG32(AFMT_STATUS + EVERGREEN_CRTC5_REGISTER_OFFSET); + + if (rdev->irq.stat_regs.evergreen.d1grph_int & GRPH_PFLIP_INT_OCCURRED) + WREG32(GRPH_INT_STATUS + EVERGREEN_CRTC0_REGISTER_OFFSET, GRPH_PFLIP_INT_CLEAR); + if (rdev->irq.stat_regs.evergreen.d2grph_int & GRPH_PFLIP_INT_OCCURRED) + WREG32(GRPH_INT_STATUS + EVERGREEN_CRTC1_REGISTER_OFFSET, GRPH_PFLIP_INT_CLEAR); + if (rdev->irq.stat_regs.evergreen.disp_int & LB_D1_VBLANK_INTERRUPT) + WREG32(VBLANK_STATUS + EVERGREEN_CRTC0_REGISTER_OFFSET, VBLANK_ACK); + if (rdev->irq.stat_regs.evergreen.disp_int & LB_D1_VLINE_INTERRUPT) + WREG32(VLINE_STATUS + EVERGREEN_CRTC0_REGISTER_OFFSET, VLINE_ACK); + if (rdev->irq.stat_regs.evergreen.disp_int_cont & LB_D2_VBLANK_INTERRUPT) + WREG32(VBLANK_STATUS + EVERGREEN_CRTC1_REGISTER_OFFSET, VBLANK_ACK); + if (rdev->irq.stat_regs.evergreen.disp_int_cont & LB_D2_VLINE_INTERRUPT) + WREG32(VLINE_STATUS + EVERGREEN_CRTC1_REGISTER_OFFSET, VLINE_ACK); + + if (rdev->num_crtc >= 4) { + if (rdev->irq.stat_regs.evergreen.d3grph_int & GRPH_PFLIP_INT_OCCURRED) + WREG32(GRPH_INT_STATUS + EVERGREEN_CRTC2_REGISTER_OFFSET, GRPH_PFLIP_INT_CLEAR); + if (rdev->irq.stat_regs.evergreen.d4grph_int & GRPH_PFLIP_INT_OCCURRED) + WREG32(GRPH_INT_STATUS + EVERGREEN_CRTC3_REGISTER_OFFSET, GRPH_PFLIP_INT_CLEAR); + if (rdev->irq.stat_regs.evergreen.disp_int_cont2 & LB_D3_VBLANK_INTERRUPT) + WREG32(VBLANK_STATUS + EVERGREEN_CRTC2_REGISTER_OFFSET, VBLANK_ACK); + if (rdev->irq.stat_regs.evergreen.disp_int_cont2 & LB_D3_VLINE_INTERRUPT) + WREG32(VLINE_STATUS + EVERGREEN_CRTC2_REGISTER_OFFSET, VLINE_ACK); + if (rdev->irq.stat_regs.evergreen.disp_int_cont3 & LB_D4_VBLANK_INTERRUPT) + WREG32(VBLANK_STATUS + EVERGREEN_CRTC3_REGISTER_OFFSET, VBLANK_ACK); + if (rdev->irq.stat_regs.evergreen.disp_int_cont3 & LB_D4_VLINE_INTERRUPT) + WREG32(VLINE_STATUS + EVERGREEN_CRTC3_REGISTER_OFFSET, VLINE_ACK); + } + + if (rdev->num_crtc >= 6) { + if (rdev->irq.stat_regs.evergreen.d5grph_int & GRPH_PFLIP_INT_OCCURRED) + WREG32(GRPH_INT_STATUS + EVERGREEN_CRTC4_REGISTER_OFFSET, GRPH_PFLIP_INT_CLEAR); + if (rdev->irq.stat_regs.evergreen.d6grph_int & GRPH_PFLIP_INT_OCCURRED) + WREG32(GRPH_INT_STATUS + EVERGREEN_CRTC5_REGISTER_OFFSET, GRPH_PFLIP_INT_CLEAR); + if (rdev->irq.stat_regs.evergreen.disp_int_cont4 & LB_D5_VBLANK_INTERRUPT) + WREG32(VBLANK_STATUS + EVERGREEN_CRTC4_REGISTER_OFFSET, VBLANK_ACK); + if (rdev->irq.stat_regs.evergreen.disp_int_cont4 & LB_D5_VLINE_INTERRUPT) + WREG32(VLINE_STATUS + EVERGREEN_CRTC4_REGISTER_OFFSET, VLINE_ACK); + if (rdev->irq.stat_regs.evergreen.disp_int_cont5 & LB_D6_VBLANK_INTERRUPT) + WREG32(VBLANK_STATUS + EVERGREEN_CRTC5_REGISTER_OFFSET, VBLANK_ACK); + if (rdev->irq.stat_regs.evergreen.disp_int_cont5 & LB_D6_VLINE_INTERRUPT) + WREG32(VLINE_STATUS + EVERGREEN_CRTC5_REGISTER_OFFSET, VLINE_ACK); + } + + if (rdev->irq.stat_regs.evergreen.disp_int & DC_HPD1_INTERRUPT) { + tmp = RREG32(DC_HPD1_INT_CONTROL); + tmp |= DC_HPDx_INT_ACK; + WREG32(DC_HPD1_INT_CONTROL, tmp); + } + if (rdev->irq.stat_regs.evergreen.disp_int_cont & DC_HPD2_INTERRUPT) { + tmp = RREG32(DC_HPD2_INT_CONTROL); + tmp |= DC_HPDx_INT_ACK; + WREG32(DC_HPD2_INT_CONTROL, tmp); + } + if (rdev->irq.stat_regs.evergreen.disp_int_cont2 & DC_HPD3_INTERRUPT) { + tmp = RREG32(DC_HPD3_INT_CONTROL); + tmp |= DC_HPDx_INT_ACK; + WREG32(DC_HPD3_INT_CONTROL, tmp); + } + if (rdev->irq.stat_regs.evergreen.disp_int_cont3 & DC_HPD4_INTERRUPT) { + tmp = RREG32(DC_HPD4_INT_CONTROL); + tmp |= DC_HPDx_INT_ACK; + WREG32(DC_HPD4_INT_CONTROL, tmp); + } + if (rdev->irq.stat_regs.evergreen.disp_int_cont4 & DC_HPD5_INTERRUPT) { + tmp = RREG32(DC_HPD5_INT_CONTROL); + tmp |= DC_HPDx_INT_ACK; + WREG32(DC_HPD5_INT_CONTROL, tmp); + } + if (rdev->irq.stat_regs.evergreen.disp_int_cont5 & DC_HPD6_INTERRUPT) { + tmp = RREG32(DC_HPD5_INT_CONTROL); + tmp |= DC_HPDx_INT_ACK; + WREG32(DC_HPD6_INT_CONTROL, tmp); + } + if (rdev->irq.stat_regs.evergreen.afmt_status1 & AFMT_AZ_FORMAT_WTRIG) { + tmp = RREG32(AFMT_AUDIO_PACKET_CONTROL + EVERGREEN_CRTC0_REGISTER_OFFSET); + tmp |= AFMT_AZ_FORMAT_WTRIG_ACK; + WREG32(AFMT_AUDIO_PACKET_CONTROL + EVERGREEN_CRTC0_REGISTER_OFFSET, tmp); + } + if (rdev->irq.stat_regs.evergreen.afmt_status2 & AFMT_AZ_FORMAT_WTRIG) { + tmp = RREG32(AFMT_AUDIO_PACKET_CONTROL + EVERGREEN_CRTC1_REGISTER_OFFSET); + tmp |= AFMT_AZ_FORMAT_WTRIG_ACK; + WREG32(AFMT_AUDIO_PACKET_CONTROL + EVERGREEN_CRTC1_REGISTER_OFFSET, tmp); + } + if (rdev->irq.stat_regs.evergreen.afmt_status3 & AFMT_AZ_FORMAT_WTRIG) { + tmp = RREG32(AFMT_AUDIO_PACKET_CONTROL + EVERGREEN_CRTC2_REGISTER_OFFSET); + tmp |= AFMT_AZ_FORMAT_WTRIG_ACK; + WREG32(AFMT_AUDIO_PACKET_CONTROL + EVERGREEN_CRTC2_REGISTER_OFFSET, tmp); + } + if (rdev->irq.stat_regs.evergreen.afmt_status4 & AFMT_AZ_FORMAT_WTRIG) { + tmp = RREG32(AFMT_AUDIO_PACKET_CONTROL + EVERGREEN_CRTC3_REGISTER_OFFSET); + tmp |= AFMT_AZ_FORMAT_WTRIG_ACK; + WREG32(AFMT_AUDIO_PACKET_CONTROL + EVERGREEN_CRTC3_REGISTER_OFFSET, tmp); + } + if (rdev->irq.stat_regs.evergreen.afmt_status5 & AFMT_AZ_FORMAT_WTRIG) { + tmp = RREG32(AFMT_AUDIO_PACKET_CONTROL + EVERGREEN_CRTC4_REGISTER_OFFSET); + tmp |= AFMT_AZ_FORMAT_WTRIG_ACK; + WREG32(AFMT_AUDIO_PACKET_CONTROL + EVERGREEN_CRTC4_REGISTER_OFFSET, tmp); + } + if (rdev->irq.stat_regs.evergreen.afmt_status6 & AFMT_AZ_FORMAT_WTRIG) { + tmp = RREG32(AFMT_AUDIO_PACKET_CONTROL + EVERGREEN_CRTC5_REGISTER_OFFSET); + tmp |= AFMT_AZ_FORMAT_WTRIG_ACK; + WREG32(AFMT_AUDIO_PACKET_CONTROL + EVERGREEN_CRTC5_REGISTER_OFFSET, tmp); + } +} + +static void evergreen_irq_disable(struct radeon_device *rdev) +{ + r600_disable_interrupts(rdev); + /* Wait and acknowledge irq */ + DRM_MDELAY(1); + evergreen_irq_ack(rdev); + evergreen_disable_interrupt_state(rdev); +} + +void evergreen_irq_suspend(struct radeon_device *rdev) +{ + evergreen_irq_disable(rdev); + r600_rlc_stop(rdev); +} + +static u32 evergreen_get_ih_wptr(struct radeon_device *rdev) +{ + u32 wptr, tmp; + + if (rdev->wb.enabled) + wptr = le32_to_cpu(rdev->wb.wb[R600_WB_IH_WPTR_OFFSET/4]); + else + wptr = RREG32(IH_RB_WPTR); + + if (wptr & RB_OVERFLOW) { + /* When a ring buffer overflow happen start parsing interrupt + * from the last not overwritten vector (wptr + 16). Hopefully + * this should allow us to catchup. + */ + dev_warn(rdev->dev, "IH ring buffer overflow (0x%08X, %d, %d)\n", + wptr, rdev->ih.rptr, (wptr + 16) + rdev->ih.ptr_mask); + rdev->ih.rptr = (wptr + 16) & rdev->ih.ptr_mask; + tmp = RREG32(IH_RB_CNTL); + tmp |= IH_WPTR_OVERFLOW_CLEAR; + WREG32(IH_RB_CNTL, tmp); + } + return (wptr & rdev->ih.ptr_mask); +} + +irqreturn_t evergreen_irq_process(struct radeon_device *rdev) +{ + u32 wptr; + u32 rptr; + u32 src_id, src_data; + u32 ring_index; + bool queue_hotplug = false; + bool queue_hdmi = false; + + if (!rdev->ih.enabled || rdev->shutdown) + return IRQ_NONE; + + wptr = evergreen_get_ih_wptr(rdev); + +restart_ih: + /* is somebody else already processing irqs? */ + if (atomic_xchg(&rdev->ih.lock, 1)) + return IRQ_NONE; + + rptr = rdev->ih.rptr; + DRM_DEBUG("r600_irq_process start: rptr %d, wptr %d\n", rptr, wptr); + + /* Order reading of wptr vs. reading of IH ring data */ + rmb(); + + /* display interrupts */ + evergreen_irq_ack(rdev); + + while (rptr != wptr) { + /* wptr/rptr are in bytes! */ + ring_index = rptr / 4; + src_id = le32_to_cpu(rdev->ih.ring[ring_index]) & 0xff; + src_data = le32_to_cpu(rdev->ih.ring[ring_index + 1]) & 0xfffffff; + + switch (src_id) { + case 1: /* D1 vblank/vline */ + switch (src_data) { + case 0: /* D1 vblank */ + if (rdev->irq.stat_regs.evergreen.disp_int & LB_D1_VBLANK_INTERRUPT) { + if (rdev->irq.crtc_vblank_int[0]) { + drm_handle_vblank(rdev->ddev, 0); + rdev->pm.vblank_sync = true; + DRM_WAKEUP(&rdev->irq.vblank_queue); + } + if (atomic_read(&rdev->irq.pflip[0])) + radeon_crtc_handle_flip(rdev, 0); + rdev->irq.stat_regs.evergreen.disp_int &= ~LB_D1_VBLANK_INTERRUPT; + DRM_DEBUG("IH: D1 vblank\n"); + } + break; + case 1: /* D1 vline */ + if (rdev->irq.stat_regs.evergreen.disp_int & LB_D1_VLINE_INTERRUPT) { + rdev->irq.stat_regs.evergreen.disp_int &= ~LB_D1_VLINE_INTERRUPT; + DRM_DEBUG("IH: D1 vline\n"); + } + break; + default: + DRM_DEBUG("Unhandled interrupt: %d %d\n", src_id, src_data); + break; + } + break; + case 2: /* D2 vblank/vline */ + switch (src_data) { + case 0: /* D2 vblank */ + if (rdev->irq.stat_regs.evergreen.disp_int_cont & LB_D2_VBLANK_INTERRUPT) { + if (rdev->irq.crtc_vblank_int[1]) { + drm_handle_vblank(rdev->ddev, 1); + rdev->pm.vblank_sync = true; + DRM_WAKEUP(&rdev->irq.vblank_queue); + } + if (atomic_read(&rdev->irq.pflip[1])) + radeon_crtc_handle_flip(rdev, 1); + rdev->irq.stat_regs.evergreen.disp_int_cont &= ~LB_D2_VBLANK_INTERRUPT; + DRM_DEBUG("IH: D2 vblank\n"); + } + break; + case 1: /* D2 vline */ + if (rdev->irq.stat_regs.evergreen.disp_int_cont & LB_D2_VLINE_INTERRUPT) { + rdev->irq.stat_regs.evergreen.disp_int_cont &= ~LB_D2_VLINE_INTERRUPT; + DRM_DEBUG("IH: D2 vline\n"); + } + break; + default: + DRM_DEBUG("Unhandled interrupt: %d %d\n", src_id, src_data); + break; + } + break; + case 3: /* D3 vblank/vline */ + switch (src_data) { + case 0: /* D3 vblank */ + if (rdev->irq.stat_regs.evergreen.disp_int_cont2 & LB_D3_VBLANK_INTERRUPT) { + if (rdev->irq.crtc_vblank_int[2]) { + drm_handle_vblank(rdev->ddev, 2); + rdev->pm.vblank_sync = true; + DRM_WAKEUP(&rdev->irq.vblank_queue); + } + if (atomic_read(&rdev->irq.pflip[2])) + radeon_crtc_handle_flip(rdev, 2); + rdev->irq.stat_regs.evergreen.disp_int_cont2 &= ~LB_D3_VBLANK_INTERRUPT; + DRM_DEBUG("IH: D3 vblank\n"); + } + break; + case 1: /* D3 vline */ + if (rdev->irq.stat_regs.evergreen.disp_int_cont2 & LB_D3_VLINE_INTERRUPT) { + rdev->irq.stat_regs.evergreen.disp_int_cont2 &= ~LB_D3_VLINE_INTERRUPT; + DRM_DEBUG("IH: D3 vline\n"); + } + break; + default: + DRM_DEBUG("Unhandled interrupt: %d %d\n", src_id, src_data); + break; + } + break; + case 4: /* D4 vblank/vline */ + switch (src_data) { + case 0: /* D4 vblank */ + if (rdev->irq.stat_regs.evergreen.disp_int_cont3 & LB_D4_VBLANK_INTERRUPT) { + if (rdev->irq.crtc_vblank_int[3]) { + drm_handle_vblank(rdev->ddev, 3); + rdev->pm.vblank_sync = true; + DRM_WAKEUP(&rdev->irq.vblank_queue); + } + if (atomic_read(&rdev->irq.pflip[3])) + radeon_crtc_handle_flip(rdev, 3); + rdev->irq.stat_regs.evergreen.disp_int_cont3 &= ~LB_D4_VBLANK_INTERRUPT; + DRM_DEBUG("IH: D4 vblank\n"); + } + break; + case 1: /* D4 vline */ + if (rdev->irq.stat_regs.evergreen.disp_int_cont3 & LB_D4_VLINE_INTERRUPT) { + rdev->irq.stat_regs.evergreen.disp_int_cont3 &= ~LB_D4_VLINE_INTERRUPT; + DRM_DEBUG("IH: D4 vline\n"); + } + break; + default: + DRM_DEBUG("Unhandled interrupt: %d %d\n", src_id, src_data); + break; + } + break; + case 5: /* D5 vblank/vline */ + switch (src_data) { + case 0: /* D5 vblank */ + if (rdev->irq.stat_regs.evergreen.disp_int_cont4 & LB_D5_VBLANK_INTERRUPT) { + if (rdev->irq.crtc_vblank_int[4]) { + drm_handle_vblank(rdev->ddev, 4); + rdev->pm.vblank_sync = true; + DRM_WAKEUP(&rdev->irq.vblank_queue); + } + if (atomic_read(&rdev->irq.pflip[4])) + radeon_crtc_handle_flip(rdev, 4); + rdev->irq.stat_regs.evergreen.disp_int_cont4 &= ~LB_D5_VBLANK_INTERRUPT; + DRM_DEBUG("IH: D5 vblank\n"); + } + break; + case 1: /* D5 vline */ + if (rdev->irq.stat_regs.evergreen.disp_int_cont4 & LB_D5_VLINE_INTERRUPT) { + rdev->irq.stat_regs.evergreen.disp_int_cont4 &= ~LB_D5_VLINE_INTERRUPT; + DRM_DEBUG("IH: D5 vline\n"); + } + break; + default: + DRM_DEBUG("Unhandled interrupt: %d %d\n", src_id, src_data); + break; + } + break; + case 6: /* D6 vblank/vline */ + switch (src_data) { + case 0: /* D6 vblank */ + if (rdev->irq.stat_regs.evergreen.disp_int_cont5 & LB_D6_VBLANK_INTERRUPT) { + if (rdev->irq.crtc_vblank_int[5]) { + drm_handle_vblank(rdev->ddev, 5); + rdev->pm.vblank_sync = true; + DRM_WAKEUP(&rdev->irq.vblank_queue); + } + if (atomic_read(&rdev->irq.pflip[5])) + radeon_crtc_handle_flip(rdev, 5); + rdev->irq.stat_regs.evergreen.disp_int_cont5 &= ~LB_D6_VBLANK_INTERRUPT; + DRM_DEBUG("IH: D6 vblank\n"); + } + break; + case 1: /* D6 vline */ + if (rdev->irq.stat_regs.evergreen.disp_int_cont5 & LB_D6_VLINE_INTERRUPT) { + rdev->irq.stat_regs.evergreen.disp_int_cont5 &= ~LB_D6_VLINE_INTERRUPT; + DRM_DEBUG("IH: D6 vline\n"); + } + break; + default: + DRM_DEBUG("Unhandled interrupt: %d %d\n", src_id, src_data); + break; + } + break; + case 42: /* HPD hotplug */ + switch (src_data) { + case 0: + if (rdev->irq.stat_regs.evergreen.disp_int & DC_HPD1_INTERRUPT) { + rdev->irq.stat_regs.evergreen.disp_int &= ~DC_HPD1_INTERRUPT; + queue_hotplug = true; + DRM_DEBUG("IH: HPD1\n"); + } + break; + case 1: + if (rdev->irq.stat_regs.evergreen.disp_int_cont & DC_HPD2_INTERRUPT) { + rdev->irq.stat_regs.evergreen.disp_int_cont &= ~DC_HPD2_INTERRUPT; + queue_hotplug = true; + DRM_DEBUG("IH: HPD2\n"); + } + break; + case 2: + if (rdev->irq.stat_regs.evergreen.disp_int_cont2 & DC_HPD3_INTERRUPT) { + rdev->irq.stat_regs.evergreen.disp_int_cont2 &= ~DC_HPD3_INTERRUPT; + queue_hotplug = true; + DRM_DEBUG("IH: HPD3\n"); + } + break; + case 3: + if (rdev->irq.stat_regs.evergreen.disp_int_cont3 & DC_HPD4_INTERRUPT) { + rdev->irq.stat_regs.evergreen.disp_int_cont3 &= ~DC_HPD4_INTERRUPT; + queue_hotplug = true; + DRM_DEBUG("IH: HPD4\n"); + } + break; + case 4: + if (rdev->irq.stat_regs.evergreen.disp_int_cont4 & DC_HPD5_INTERRUPT) { + rdev->irq.stat_regs.evergreen.disp_int_cont4 &= ~DC_HPD5_INTERRUPT; + queue_hotplug = true; + DRM_DEBUG("IH: HPD5\n"); + } + break; + case 5: + if (rdev->irq.stat_regs.evergreen.disp_int_cont5 & DC_HPD6_INTERRUPT) { + rdev->irq.stat_regs.evergreen.disp_int_cont5 &= ~DC_HPD6_INTERRUPT; + queue_hotplug = true; + DRM_DEBUG("IH: HPD6\n"); + } + break; + default: + DRM_DEBUG("Unhandled interrupt: %d %d\n", src_id, src_data); + break; + } + break; + case 44: /* hdmi */ + switch (src_data) { + case 0: + if (rdev->irq.stat_regs.evergreen.afmt_status1 & AFMT_AZ_FORMAT_WTRIG) { + rdev->irq.stat_regs.evergreen.afmt_status1 &= ~AFMT_AZ_FORMAT_WTRIG; + queue_hdmi = true; + DRM_DEBUG("IH: HDMI0\n"); + } + break; + case 1: + if (rdev->irq.stat_regs.evergreen.afmt_status2 & AFMT_AZ_FORMAT_WTRIG) { + rdev->irq.stat_regs.evergreen.afmt_status2 &= ~AFMT_AZ_FORMAT_WTRIG; + queue_hdmi = true; + DRM_DEBUG("IH: HDMI1\n"); + } + break; + case 2: + if (rdev->irq.stat_regs.evergreen.afmt_status3 & AFMT_AZ_FORMAT_WTRIG) { + rdev->irq.stat_regs.evergreen.afmt_status3 &= ~AFMT_AZ_FORMAT_WTRIG; + queue_hdmi = true; + DRM_DEBUG("IH: HDMI2\n"); + } + break; + case 3: + if (rdev->irq.stat_regs.evergreen.afmt_status4 & AFMT_AZ_FORMAT_WTRIG) { + rdev->irq.stat_regs.evergreen.afmt_status4 &= ~AFMT_AZ_FORMAT_WTRIG; + queue_hdmi = true; + DRM_DEBUG("IH: HDMI3\n"); + } + break; + case 4: + if (rdev->irq.stat_regs.evergreen.afmt_status5 & AFMT_AZ_FORMAT_WTRIG) { + rdev->irq.stat_regs.evergreen.afmt_status5 &= ~AFMT_AZ_FORMAT_WTRIG; + queue_hdmi = true; + DRM_DEBUG("IH: HDMI4\n"); + } + break; + case 5: + if (rdev->irq.stat_regs.evergreen.afmt_status6 & AFMT_AZ_FORMAT_WTRIG) { + rdev->irq.stat_regs.evergreen.afmt_status6 &= ~AFMT_AZ_FORMAT_WTRIG; + queue_hdmi = true; + DRM_DEBUG("IH: HDMI5\n"); + } + break; + default: + DRM_ERROR("Unhandled interrupt: %d %d\n", src_id, src_data); + break; + } + break; + case 146: + case 147: + dev_err(rdev->dev, "GPU fault detected: %d 0x%08x\n", src_id, src_data); + dev_err(rdev->dev, " VM_CONTEXT1_PROTECTION_FAULT_ADDR 0x%08X\n", + RREG32(VM_CONTEXT1_PROTECTION_FAULT_ADDR)); + dev_err(rdev->dev, " VM_CONTEXT1_PROTECTION_FAULT_STATUS 0x%08X\n", + RREG32(VM_CONTEXT1_PROTECTION_FAULT_STATUS)); + /* reset addr and status */ + WREG32_P(VM_CONTEXT1_CNTL2, 1, ~1); + break; + case 176: /* CP_INT in ring buffer */ + case 177: /* CP_INT in IB1 */ + case 178: /* CP_INT in IB2 */ + DRM_DEBUG("IH: CP int: 0x%08x\n", src_data); + radeon_fence_process(rdev, RADEON_RING_TYPE_GFX_INDEX); + break; + case 181: /* CP EOP event */ + DRM_DEBUG("IH: CP EOP\n"); + if (rdev->family >= CHIP_CAYMAN) { + switch (src_data) { + case 0: + radeon_fence_process(rdev, RADEON_RING_TYPE_GFX_INDEX); + break; + case 1: + radeon_fence_process(rdev, CAYMAN_RING_TYPE_CP1_INDEX); + break; + case 2: + radeon_fence_process(rdev, CAYMAN_RING_TYPE_CP2_INDEX); + break; + } + } else + radeon_fence_process(rdev, RADEON_RING_TYPE_GFX_INDEX); + break; + case 224: /* DMA trap event */ + DRM_DEBUG("IH: DMA trap\n"); + radeon_fence_process(rdev, R600_RING_TYPE_DMA_INDEX); + break; + case 233: /* GUI IDLE */ + DRM_DEBUG("IH: GUI idle\n"); + break; + case 244: /* DMA trap event */ + if (rdev->family >= CHIP_CAYMAN) { + DRM_DEBUG("IH: DMA1 trap\n"); + radeon_fence_process(rdev, CAYMAN_RING_TYPE_DMA1_INDEX); + } + break; + default: + DRM_DEBUG("Unhandled interrupt: %d %d\n", src_id, src_data); + break; + } + + /* wptr/rptr are in bytes! */ + rptr += 16; + rptr &= rdev->ih.ptr_mask; + } + if (queue_hotplug) + taskqueue_enqueue(rdev->tq, &rdev->hotplug_work); + if (queue_hdmi) + taskqueue_enqueue(rdev->tq, &rdev->audio_work); + rdev->ih.rptr = rptr; + WREG32(IH_RB_RPTR, rdev->ih.rptr); + atomic_set(&rdev->ih.lock, 0); + + /* make sure wptr hasn't changed while processing */ + wptr = evergreen_get_ih_wptr(rdev); + if (wptr != rptr) + goto restart_ih; + + return IRQ_HANDLED; +} + +/** + * evergreen_dma_fence_ring_emit - emit a fence on the DMA ring + * + * @rdev: radeon_device pointer + * @fence: radeon fence object + * + * Add a DMA fence packet to the ring to write + * the fence seq number and DMA trap packet to generate + * an interrupt if needed (evergreen-SI). + */ +void evergreen_dma_fence_ring_emit(struct radeon_device *rdev, + struct radeon_fence *fence) +{ + struct radeon_ring *ring = &rdev->ring[fence->ring]; + u64 addr = rdev->fence_drv[fence->ring].gpu_addr; + /* write the fence */ + radeon_ring_write(ring, DMA_PACKET(DMA_PACKET_FENCE, 0, 0, 0)); + radeon_ring_write(ring, addr & 0xfffffffc); + radeon_ring_write(ring, (upper_32_bits(addr) & 0xff)); + radeon_ring_write(ring, fence->seq); + /* generate an interrupt */ + radeon_ring_write(ring, DMA_PACKET(DMA_PACKET_TRAP, 0, 0, 0)); + /* flush HDP */ + radeon_ring_write(ring, DMA_PACKET(DMA_PACKET_SRBM_WRITE, 0, 0, 0)); + radeon_ring_write(ring, (0xf << 16) | (HDP_MEM_COHERENCY_FLUSH_CNTL >> 2)); + radeon_ring_write(ring, 1); +} + +/** + * evergreen_dma_ring_ib_execute - schedule an IB on the DMA engine + * + * @rdev: radeon_device pointer + * @ib: IB object to schedule + * + * Schedule an IB in the DMA ring (evergreen). + */ +void evergreen_dma_ring_ib_execute(struct radeon_device *rdev, + struct radeon_ib *ib) +{ + struct radeon_ring *ring = &rdev->ring[ib->ring]; + + if (rdev->wb.enabled) { + u32 next_rptr = ring->wptr + 4; + while ((next_rptr & 7) != 5) + next_rptr++; + next_rptr += 3; + radeon_ring_write(ring, DMA_PACKET(DMA_PACKET_WRITE, 0, 0, 1)); + radeon_ring_write(ring, ring->next_rptr_gpu_addr & 0xfffffffc); + radeon_ring_write(ring, upper_32_bits(ring->next_rptr_gpu_addr) & 0xff); + radeon_ring_write(ring, next_rptr); + } + + /* The indirect buffer packet must end on an 8 DW boundary in the DMA ring. + * Pad as necessary with NOPs. + */ + while ((ring->wptr & 7) != 5) + radeon_ring_write(ring, DMA_PACKET(DMA_PACKET_NOP, 0, 0, 0)); + radeon_ring_write(ring, DMA_PACKET(DMA_PACKET_INDIRECT_BUFFER, 0, 0, 0)); + radeon_ring_write(ring, (ib->gpu_addr & 0xFFFFFFE0)); + radeon_ring_write(ring, (ib->length_dw << 12) | (upper_32_bits(ib->gpu_addr) & 0xFF)); + +} + +/** + * evergreen_copy_dma - copy pages using the DMA engine + * + * @rdev: radeon_device pointer + * @src_offset: src GPU address + * @dst_offset: dst GPU address + * @num_gpu_pages: number of GPU pages to xfer + * @fence: radeon fence object + * + * Copy GPU paging using the DMA engine (evergreen-cayman). + * Used by the radeon ttm implementation to move pages if + * registered as the asic copy callback. + */ +int evergreen_copy_dma(struct radeon_device *rdev, + uint64_t src_offset, uint64_t dst_offset, + unsigned num_gpu_pages, + struct radeon_fence **fence) +{ + struct radeon_semaphore *sem = NULL; + int ring_index = rdev->asic->copy.dma_ring_index; + struct radeon_ring *ring = &rdev->ring[ring_index]; + u32 size_in_dw, cur_size_in_dw; + int i, num_loops; + int r = 0; + + r = radeon_semaphore_create(rdev, &sem); + if (r) { + DRM_ERROR("radeon: moving bo (%d).\n", r); + return r; + } + + size_in_dw = (num_gpu_pages << RADEON_GPU_PAGE_SHIFT) / 4; + num_loops = DIV_ROUND_UP(size_in_dw, 0xfffff); + r = radeon_ring_lock(rdev, ring, num_loops * 5 + 11); + if (r) { + DRM_ERROR("radeon: moving bo (%d).\n", r); + radeon_semaphore_free(rdev, &sem, NULL); + return r; + } + + if (radeon_fence_need_sync(*fence, ring->idx)) { + radeon_semaphore_sync_rings(rdev, sem, (*fence)->ring, + ring->idx); + radeon_fence_note_sync(*fence, ring->idx); + } else { + radeon_semaphore_free(rdev, &sem, NULL); + } + + for (i = 0; i < num_loops; i++) { + cur_size_in_dw = size_in_dw; + if (cur_size_in_dw > 0xFFFFF) + cur_size_in_dw = 0xFFFFF; + size_in_dw -= cur_size_in_dw; + radeon_ring_write(ring, DMA_PACKET(DMA_PACKET_COPY, 0, 0, cur_size_in_dw)); + radeon_ring_write(ring, dst_offset & 0xfffffffc); + radeon_ring_write(ring, src_offset & 0xfffffffc); + radeon_ring_write(ring, upper_32_bits(dst_offset) & 0xff); + radeon_ring_write(ring, upper_32_bits(src_offset) & 0xff); + src_offset += cur_size_in_dw * 4; + dst_offset += cur_size_in_dw * 4; + } + + r = radeon_fence_emit(rdev, fence, ring->idx); + if (r) { + radeon_ring_unlock_undo(rdev, ring); + return r; + } + + radeon_ring_unlock_commit(rdev, ring); + radeon_semaphore_free(rdev, &sem, *fence); + + return r; +} + +static int evergreen_startup(struct radeon_device *rdev) +{ + struct radeon_ring *ring = &rdev->ring[RADEON_RING_TYPE_GFX_INDEX]; + int r; + + /* enable pcie gen2 link */ + evergreen_pcie_gen2_enable(rdev); + + if (ASIC_IS_DCE5(rdev)) { + if (!rdev->me_fw || !rdev->pfp_fw || !rdev->rlc_fw || !rdev->mc_fw) { + r = ni_init_microcode(rdev); + if (r) { + DRM_ERROR("Failed to load firmware!\n"); + return r; + } + } + r = ni_mc_load_microcode(rdev); + if (r) { + DRM_ERROR("Failed to load MC firmware!\n"); + return r; + } + } else { + if (!rdev->me_fw || !rdev->pfp_fw || !rdev->rlc_fw) { + r = r600_init_microcode(rdev); + if (r) { + DRM_ERROR("Failed to load firmware!\n"); + return r; + } + } + } + + r = r600_vram_scratch_init(rdev); + if (r) + return r; + + evergreen_mc_program(rdev); + if (rdev->flags & RADEON_IS_AGP) { + evergreen_agp_enable(rdev); + } else { + r = evergreen_pcie_gart_enable(rdev); + if (r) + return r; + } + evergreen_gpu_init(rdev); + + r = evergreen_blit_init(rdev); + if (r) { + r600_blit_fini(rdev); + rdev->asic->copy.copy = NULL; + dev_warn(rdev->dev, "failed blitter (%d) falling back to memcpy\n", r); + } + + /* allocate wb buffer */ + r = radeon_wb_init(rdev); + if (r) + return r; + + r = radeon_fence_driver_start_ring(rdev, RADEON_RING_TYPE_GFX_INDEX); + if (r) { + dev_err(rdev->dev, "failed initializing CP fences (%d).\n", r); + return r; + } + + r = radeon_fence_driver_start_ring(rdev, R600_RING_TYPE_DMA_INDEX); + if (r) { + dev_err(rdev->dev, "failed initializing DMA fences (%d).\n", r); + return r; + } + + /* Enable IRQ */ + r = r600_irq_init(rdev); + if (r) { + DRM_ERROR("radeon: IH init failed (%d).\n", r); + radeon_irq_kms_fini(rdev); + return r; + } + evergreen_irq_set(rdev); + + r = radeon_ring_init(rdev, ring, ring->ring_size, RADEON_WB_CP_RPTR_OFFSET, + R600_CP_RB_RPTR, R600_CP_RB_WPTR, + 0, 0xfffff, RADEON_CP_PACKET2); + if (r) + return r; + + ring = &rdev->ring[R600_RING_TYPE_DMA_INDEX]; + r = radeon_ring_init(rdev, ring, ring->ring_size, R600_WB_DMA_RPTR_OFFSET, + DMA_RB_RPTR, DMA_RB_WPTR, + 2, 0x3fffc, DMA_PACKET(DMA_PACKET_NOP, 0, 0, 0)); + if (r) + return r; + + r = evergreen_cp_load_microcode(rdev); + if (r) + return r; + r = evergreen_cp_resume(rdev); + if (r) + return r; + r = r600_dma_resume(rdev); + if (r) + return r; + + r = radeon_ib_pool_init(rdev); + if (r) { + dev_err(rdev->dev, "IB initialization failed (%d).\n", r); + return r; + } + + r = r600_audio_init(rdev); + if (r) { + DRM_ERROR("radeon: audio init failed\n"); + return r; + } + + return 0; +} + +int evergreen_resume(struct radeon_device *rdev) +{ + int r; + + /* reset the asic, the gfx blocks are often in a bad state + * after the driver is unloaded or after a resume + */ + if (radeon_asic_reset(rdev)) + dev_warn(rdev->dev, "GPU reset failed !\n"); + /* Do not reset GPU before posting, on rv770 hw unlike on r500 hw, + * posting will perform necessary task to bring back GPU into good + * shape. + */ + /* post card */ + atom_asic_init(rdev->mode_info.atom_context); + + rdev->accel_working = true; + r = evergreen_startup(rdev); + if (r) { + DRM_ERROR("evergreen startup failed on resume\n"); + rdev->accel_working = false; + return r; + } + + return r; + +} + +int evergreen_suspend(struct radeon_device *rdev) +{ + r600_audio_fini(rdev); + r700_cp_stop(rdev); + r600_dma_stop(rdev); + evergreen_irq_suspend(rdev); + radeon_wb_disable(rdev); + evergreen_pcie_gart_disable(rdev); + + return 0; +} + +/* Plan is to move initialization in that function and use + * helper function so that radeon_device_init pretty much + * do nothing more than calling asic specific function. This + * should also allow to remove a bunch of callback function + * like vram_info. + */ +int evergreen_init(struct radeon_device *rdev) +{ + int r; + + /* Read BIOS */ + if (!radeon_get_bios(rdev)) { + if (ASIC_IS_AVIVO(rdev)) + return -EINVAL; + } + /* Must be an ATOMBIOS */ + if (!rdev->is_atom_bios) { + dev_err(rdev->dev, "Expecting atombios for evergreen GPU\n"); + return -EINVAL; + } + r = radeon_atombios_init(rdev); + if (r) + return r; + /* reset the asic, the gfx blocks are often in a bad state + * after the driver is unloaded or after a resume + */ + if (radeon_asic_reset(rdev)) + dev_warn(rdev->dev, "GPU reset failed !\n"); + /* Post card if necessary */ + if (!radeon_card_posted(rdev)) { + if (!rdev->bios) { + dev_err(rdev->dev, "Card not posted and no BIOS - ignoring\n"); + return -EINVAL; + } + DRM_INFO("GPU not posted. posting now...\n"); + atom_asic_init(rdev->mode_info.atom_context); + } + /* Initialize scratch registers */ + r600_scratch_init(rdev); + /* Initialize surface registers */ + radeon_surface_init(rdev); + /* Initialize clocks */ + radeon_get_clock_info(rdev->ddev); + /* Fence driver */ + r = radeon_fence_driver_init(rdev); + if (r) + return r; + /* initialize AGP */ + if (rdev->flags & RADEON_IS_AGP) { + r = radeon_agp_init(rdev); + if (r) + radeon_agp_disable(rdev); + } + /* initialize memory controller */ + r = evergreen_mc_init(rdev); + if (r) + return r; + /* Memory manager */ + r = radeon_bo_init(rdev); + if (r) + return r; + + r = radeon_irq_kms_init(rdev); + if (r) + return r; + + rdev->ring[RADEON_RING_TYPE_GFX_INDEX].ring_obj = NULL; + r600_ring_init(rdev, &rdev->ring[RADEON_RING_TYPE_GFX_INDEX], 1024 * 1024); + + rdev->ring[R600_RING_TYPE_DMA_INDEX].ring_obj = NULL; + r600_ring_init(rdev, &rdev->ring[R600_RING_TYPE_DMA_INDEX], 64 * 1024); + + rdev->ih.ring_obj = NULL; + r600_ih_ring_init(rdev, 64 * 1024); + + r = r600_pcie_gart_init(rdev); + if (r) + return r; + + rdev->accel_working = true; + r = evergreen_startup(rdev); + if (r) { + dev_err(rdev->dev, "disabling GPU acceleration\n"); + r700_cp_fini(rdev); + r600_dma_fini(rdev); + r600_irq_fini(rdev); + radeon_wb_fini(rdev); + radeon_ib_pool_fini(rdev); + radeon_irq_kms_fini(rdev); + evergreen_pcie_gart_fini(rdev); + rdev->accel_working = false; + } + + /* Don't start up if the MC ucode is missing on BTC parts. + * The default clocks and voltages before the MC ucode + * is loaded are not suffient for advanced operations. + */ + if (ASIC_IS_DCE5(rdev)) { + if (!rdev->mc_fw && !(rdev->flags & RADEON_IS_IGP)) { + DRM_ERROR("radeon: MC ucode required for NI+.\n"); + return -EINVAL; + } + } + + return 0; +} + +void evergreen_fini(struct radeon_device *rdev) +{ + r600_audio_fini(rdev); + r600_blit_fini(rdev); + r700_cp_fini(rdev); + r600_dma_fini(rdev); + r600_irq_fini(rdev); + radeon_wb_fini(rdev); + radeon_ib_pool_fini(rdev); + radeon_irq_kms_fini(rdev); + evergreen_pcie_gart_fini(rdev); + r600_vram_scratch_fini(rdev); + radeon_gem_fini(rdev); + radeon_fence_driver_fini(rdev); + radeon_agp_fini(rdev); + radeon_bo_fini(rdev); + radeon_atombios_fini(rdev); + if (ASIC_IS_DCE5(rdev)) + ni_fini_microcode(rdev); + else + r600_fini_microcode(rdev); + free(rdev->bios, DRM_MEM_DRIVER); + rdev->bios = NULL; +} + +void evergreen_pcie_gen2_enable(struct radeon_device *rdev) +{ + u32 link_width_cntl, speed_cntl, mask; + int ret; + + if (radeon_pcie_gen2 == 0) + return; + + if (rdev->flags & RADEON_IS_IGP) + return; + + if (!(rdev->flags & RADEON_IS_PCIE)) + return; + + /* x2 cards have a special sequence */ + if (ASIC_IS_X2(rdev)) + return; + + ret = drm_pcie_get_speed_cap_mask(rdev->ddev, &mask); + if (ret != 0) + return; + + if (!(mask & DRM_PCIE_SPEED_50)) + return; + + speed_cntl = RREG32_PCIE_P(PCIE_LC_SPEED_CNTL); + if (speed_cntl & LC_CURRENT_DATA_RATE) { + DRM_INFO("PCIE gen 2 link speeds already enabled\n"); + return; + } + + DRM_INFO("enabling PCIE gen 2 link speeds, disable with radeon.pcie_gen2=0\n"); + + if ((speed_cntl & LC_OTHER_SIDE_EVER_SENT_GEN2) || + (speed_cntl & LC_OTHER_SIDE_SUPPORTS_GEN2)) { + + link_width_cntl = RREG32_PCIE_P(PCIE_LC_LINK_WIDTH_CNTL); + link_width_cntl &= ~LC_UPCONFIGURE_DIS; + WREG32_PCIE_P(PCIE_LC_LINK_WIDTH_CNTL, link_width_cntl); + + speed_cntl = RREG32_PCIE_P(PCIE_LC_SPEED_CNTL); + speed_cntl &= ~LC_TARGET_LINK_SPEED_OVERRIDE_EN; + WREG32_PCIE_P(PCIE_LC_SPEED_CNTL, speed_cntl); + + speed_cntl = RREG32_PCIE_P(PCIE_LC_SPEED_CNTL); + speed_cntl |= LC_CLR_FAILED_SPD_CHANGE_CNT; + WREG32_PCIE_P(PCIE_LC_SPEED_CNTL, speed_cntl); + + speed_cntl = RREG32_PCIE_P(PCIE_LC_SPEED_CNTL); + speed_cntl &= ~LC_CLR_FAILED_SPD_CHANGE_CNT; + WREG32_PCIE_P(PCIE_LC_SPEED_CNTL, speed_cntl); + + speed_cntl = RREG32_PCIE_P(PCIE_LC_SPEED_CNTL); + speed_cntl |= LC_GEN2_EN_STRAP; + WREG32_PCIE_P(PCIE_LC_SPEED_CNTL, speed_cntl); + + } else { + link_width_cntl = RREG32_PCIE_P(PCIE_LC_LINK_WIDTH_CNTL); + /* XXX: only disable it if gen1 bridge vendor == 0x111d or 0x1106 */ + if (1) + link_width_cntl |= LC_UPCONFIGURE_DIS; + else + link_width_cntl &= ~LC_UPCONFIGURE_DIS; + WREG32_PCIE_P(PCIE_LC_LINK_WIDTH_CNTL, link_width_cntl); + } +} diff --git a/sys/dev/drm2/radeon/evergreen_blit_kms.c b/sys/dev/drm2/radeon/evergreen_blit_kms.c new file mode 100644 index 00000000000..1012f3f35f3 --- /dev/null +++ b/sys/dev/drm2/radeon/evergreen_blit_kms.c @@ -0,0 +1,733 @@ +/* + * Copyright 2010 Advanced Micro Devices, Inc. + * + * 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 (including the next + * paragraph) 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 COPYRIGHT HOLDER(S) AND/OR ITS SUPPLIERS 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. + * + * Authors: + * Alex Deucher + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include "radeon.h" +#include "radeon_asic.h" + +#include "evergreend.h" +#include "evergreen_blit_shaders.h" +#include "cayman_blit_shaders.h" +#include "radeon_blit_common.h" + +/* emits 17 */ +static void +set_render_target(struct radeon_device *rdev, int format, + int w, int h, u64 gpu_addr) +{ + struct radeon_ring *ring = &rdev->ring[RADEON_RING_TYPE_GFX_INDEX]; + u32 cb_color_info; + int pitch, slice; + + h = roundup2(h, 8); + if (h < 8) + h = 8; + + cb_color_info = CB_FORMAT(format) | + CB_SOURCE_FORMAT(CB_SF_EXPORT_NORM) | + CB_ARRAY_MODE(ARRAY_1D_TILED_THIN1); + pitch = (w / 8) - 1; + slice = ((w * h) / 64) - 1; + + radeon_ring_write(ring, PACKET3(PACKET3_SET_CONTEXT_REG, 15)); + radeon_ring_write(ring, (CB_COLOR0_BASE - PACKET3_SET_CONTEXT_REG_START) >> 2); + radeon_ring_write(ring, gpu_addr >> 8); + radeon_ring_write(ring, pitch); + radeon_ring_write(ring, slice); + radeon_ring_write(ring, 0); + radeon_ring_write(ring, cb_color_info); + radeon_ring_write(ring, 0); + radeon_ring_write(ring, (w - 1) | ((h - 1) << 16)); + radeon_ring_write(ring, 0); + radeon_ring_write(ring, 0); + radeon_ring_write(ring, 0); + radeon_ring_write(ring, 0); + radeon_ring_write(ring, 0); + radeon_ring_write(ring, 0); + radeon_ring_write(ring, 0); + radeon_ring_write(ring, 0); +} + +/* emits 5dw */ +static void +cp_set_surface_sync(struct radeon_device *rdev, + u32 sync_type, u32 size, + u64 mc_addr) +{ + struct radeon_ring *ring = &rdev->ring[RADEON_RING_TYPE_GFX_INDEX]; + u32 cp_coher_size; + + if (size == 0xffffffff) + cp_coher_size = 0xffffffff; + else + cp_coher_size = ((size + 255) >> 8); + + if (rdev->family >= CHIP_CAYMAN) { + /* CP_COHER_CNTL2 has to be set manually when submitting a surface_sync + * to the RB directly. For IBs, the CP programs this as part of the + * surface_sync packet. + */ + radeon_ring_write(ring, PACKET3(PACKET3_SET_CONFIG_REG, 1)); + radeon_ring_write(ring, (0x85e8 - PACKET3_SET_CONFIG_REG_START) >> 2); + radeon_ring_write(ring, 0); /* CP_COHER_CNTL2 */ + } + radeon_ring_write(ring, PACKET3(PACKET3_SURFACE_SYNC, 3)); + radeon_ring_write(ring, sync_type); + radeon_ring_write(ring, cp_coher_size); + radeon_ring_write(ring, mc_addr >> 8); + radeon_ring_write(ring, 10); /* poll interval */ +} + +/* emits 11dw + 1 surface sync = 16dw */ +static void +set_shaders(struct radeon_device *rdev) +{ + struct radeon_ring *ring = &rdev->ring[RADEON_RING_TYPE_GFX_INDEX]; + u64 gpu_addr; + + /* VS */ + gpu_addr = rdev->r600_blit.shader_gpu_addr + rdev->r600_blit.vs_offset; + radeon_ring_write(ring, PACKET3(PACKET3_SET_CONTEXT_REG, 3)); + radeon_ring_write(ring, (SQ_PGM_START_VS - PACKET3_SET_CONTEXT_REG_START) >> 2); + radeon_ring_write(ring, gpu_addr >> 8); + radeon_ring_write(ring, 2); + radeon_ring_write(ring, 0); + + /* PS */ + gpu_addr = rdev->r600_blit.shader_gpu_addr + rdev->r600_blit.ps_offset; + radeon_ring_write(ring, PACKET3(PACKET3_SET_CONTEXT_REG, 4)); + radeon_ring_write(ring, (SQ_PGM_START_PS - PACKET3_SET_CONTEXT_REG_START) >> 2); + radeon_ring_write(ring, gpu_addr >> 8); + radeon_ring_write(ring, 1); + radeon_ring_write(ring, 0); + radeon_ring_write(ring, 2); + + gpu_addr = rdev->r600_blit.shader_gpu_addr + rdev->r600_blit.vs_offset; + cp_set_surface_sync(rdev, PACKET3_SH_ACTION_ENA, 512, gpu_addr); +} + +/* emits 10 + 1 sync (5) = 15 */ +static void +set_vtx_resource(struct radeon_device *rdev, u64 gpu_addr) +{ + struct radeon_ring *ring = &rdev->ring[RADEON_RING_TYPE_GFX_INDEX]; + u32 sq_vtx_constant_word2, sq_vtx_constant_word3; + + /* high addr, stride */ + sq_vtx_constant_word2 = SQ_VTXC_BASE_ADDR_HI(upper_32_bits(gpu_addr) & 0xff) | + SQ_VTXC_STRIDE(16); +#ifdef __BIG_ENDIAN + sq_vtx_constant_word2 |= SQ_VTXC_ENDIAN_SWAP(SQ_ENDIAN_8IN32); +#endif + /* xyzw swizzles */ + sq_vtx_constant_word3 = SQ_VTCX_SEL_X(SQ_SEL_X) | + SQ_VTCX_SEL_Y(SQ_SEL_Y) | + SQ_VTCX_SEL_Z(SQ_SEL_Z) | + SQ_VTCX_SEL_W(SQ_SEL_W); + + radeon_ring_write(ring, PACKET3(PACKET3_SET_RESOURCE, 8)); + radeon_ring_write(ring, 0x580); + radeon_ring_write(ring, gpu_addr & 0xffffffff); + radeon_ring_write(ring, 48 - 1); /* size */ + radeon_ring_write(ring, sq_vtx_constant_word2); + radeon_ring_write(ring, sq_vtx_constant_word3); + radeon_ring_write(ring, 0); + radeon_ring_write(ring, 0); + radeon_ring_write(ring, 0); + radeon_ring_write(ring, S__SQ_CONSTANT_TYPE(SQ_TEX_VTX_VALID_BUFFER)); + + if ((rdev->family == CHIP_CEDAR) || + (rdev->family == CHIP_PALM) || + (rdev->family == CHIP_SUMO) || + (rdev->family == CHIP_SUMO2) || + (rdev->family == CHIP_CAICOS)) + cp_set_surface_sync(rdev, + PACKET3_TC_ACTION_ENA, 48, gpu_addr); + else + cp_set_surface_sync(rdev, + PACKET3_VC_ACTION_ENA, 48, gpu_addr); + +} + +/* emits 10 */ +static void +set_tex_resource(struct radeon_device *rdev, + int format, int w, int h, int pitch, + u64 gpu_addr, u32 size) +{ + struct radeon_ring *ring = &rdev->ring[RADEON_RING_TYPE_GFX_INDEX]; + u32 sq_tex_resource_word0, sq_tex_resource_word1; + u32 sq_tex_resource_word4, sq_tex_resource_word7; + + if (h < 1) + h = 1; + + sq_tex_resource_word0 = TEX_DIM(SQ_TEX_DIM_2D); + sq_tex_resource_word0 |= ((((pitch >> 3) - 1) << 6) | + ((w - 1) << 18)); + sq_tex_resource_word1 = ((h - 1) << 0) | + TEX_ARRAY_MODE(ARRAY_1D_TILED_THIN1); + /* xyzw swizzles */ + sq_tex_resource_word4 = TEX_DST_SEL_X(SQ_SEL_X) | + TEX_DST_SEL_Y(SQ_SEL_Y) | + TEX_DST_SEL_Z(SQ_SEL_Z) | + TEX_DST_SEL_W(SQ_SEL_W); + + sq_tex_resource_word7 = format | + S__SQ_CONSTANT_TYPE(SQ_TEX_VTX_VALID_TEXTURE); + + cp_set_surface_sync(rdev, + PACKET3_TC_ACTION_ENA, size, gpu_addr); + + radeon_ring_write(ring, PACKET3(PACKET3_SET_RESOURCE, 8)); + radeon_ring_write(ring, 0); + radeon_ring_write(ring, sq_tex_resource_word0); + radeon_ring_write(ring, sq_tex_resource_word1); + radeon_ring_write(ring, gpu_addr >> 8); + radeon_ring_write(ring, gpu_addr >> 8); + radeon_ring_write(ring, sq_tex_resource_word4); + radeon_ring_write(ring, 0); + radeon_ring_write(ring, 0); + radeon_ring_write(ring, sq_tex_resource_word7); +} + +/* emits 12 */ +static void +set_scissors(struct radeon_device *rdev, int x1, int y1, + int x2, int y2) +{ + struct radeon_ring *ring = &rdev->ring[RADEON_RING_TYPE_GFX_INDEX]; + /* workaround some hw bugs */ + if (x2 == 0) + x1 = 1; + if (y2 == 0) + y1 = 1; + if (rdev->family >= CHIP_CAYMAN) { + if ((x2 == 1) && (y2 == 1)) + x2 = 2; + } + + radeon_ring_write(ring, PACKET3(PACKET3_SET_CONTEXT_REG, 2)); + radeon_ring_write(ring, (PA_SC_SCREEN_SCISSOR_TL - PACKET3_SET_CONTEXT_REG_START) >> 2); + radeon_ring_write(ring, (x1 << 0) | (y1 << 16)); + radeon_ring_write(ring, (x2 << 0) | (y2 << 16)); + + radeon_ring_write(ring, PACKET3(PACKET3_SET_CONTEXT_REG, 2)); + radeon_ring_write(ring, (PA_SC_GENERIC_SCISSOR_TL - PACKET3_SET_CONTEXT_REG_START) >> 2); + radeon_ring_write(ring, (x1 << 0) | (y1 << 16) | (1 << 31)); + radeon_ring_write(ring, (x2 << 0) | (y2 << 16)); + + radeon_ring_write(ring, PACKET3(PACKET3_SET_CONTEXT_REG, 2)); + radeon_ring_write(ring, (PA_SC_WINDOW_SCISSOR_TL - PACKET3_SET_CONTEXT_REG_START) >> 2); + radeon_ring_write(ring, (x1 << 0) | (y1 << 16) | (1 << 31)); + radeon_ring_write(ring, (x2 << 0) | (y2 << 16)); +} + +/* emits 10 */ +static void +draw_auto(struct radeon_device *rdev) +{ + struct radeon_ring *ring = &rdev->ring[RADEON_RING_TYPE_GFX_INDEX]; + radeon_ring_write(ring, PACKET3(PACKET3_SET_CONFIG_REG, 1)); + radeon_ring_write(ring, (VGT_PRIMITIVE_TYPE - PACKET3_SET_CONFIG_REG_START) >> 2); + radeon_ring_write(ring, DI_PT_RECTLIST); + + radeon_ring_write(ring, PACKET3(PACKET3_INDEX_TYPE, 0)); + radeon_ring_write(ring, +#ifdef __BIG_ENDIAN + (2 << 2) | +#endif + DI_INDEX_SIZE_16_BIT); + + radeon_ring_write(ring, PACKET3(PACKET3_NUM_INSTANCES, 0)); + radeon_ring_write(ring, 1); + + radeon_ring_write(ring, PACKET3(PACKET3_DRAW_INDEX_AUTO, 1)); + radeon_ring_write(ring, 3); + radeon_ring_write(ring, DI_SRC_SEL_AUTO_INDEX); + +} + +/* emits 39 */ +static void +set_default_state(struct radeon_device *rdev) +{ + struct radeon_ring *ring = &rdev->ring[RADEON_RING_TYPE_GFX_INDEX]; + u32 sq_config, sq_gpr_resource_mgmt_1, sq_gpr_resource_mgmt_2, sq_gpr_resource_mgmt_3; + u32 sq_thread_resource_mgmt, sq_thread_resource_mgmt_2; + u32 sq_stack_resource_mgmt_1, sq_stack_resource_mgmt_2, sq_stack_resource_mgmt_3; + int num_ps_gprs, num_vs_gprs, num_temp_gprs; + int num_gs_gprs, num_es_gprs, num_hs_gprs, num_ls_gprs; + int num_ps_threads, num_vs_threads, num_gs_threads, num_es_threads; + int num_hs_threads, num_ls_threads; + int num_ps_stack_entries, num_vs_stack_entries, num_gs_stack_entries, num_es_stack_entries; + int num_hs_stack_entries, num_ls_stack_entries; + u64 gpu_addr; + int dwords; + + /* set clear context state */ + radeon_ring_write(ring, PACKET3(PACKET3_CLEAR_STATE, 0)); + radeon_ring_write(ring, 0); + + if (rdev->family < CHIP_CAYMAN) { + switch (rdev->family) { + case CHIP_CEDAR: + default: + num_ps_gprs = 93; + num_vs_gprs = 46; + num_temp_gprs = 4; + num_gs_gprs = 31; + num_es_gprs = 31; + num_hs_gprs = 23; + num_ls_gprs = 23; + num_ps_threads = 96; + num_vs_threads = 16; + num_gs_threads = 16; + num_es_threads = 16; + num_hs_threads = 16; + num_ls_threads = 16; + num_ps_stack_entries = 42; + num_vs_stack_entries = 42; + num_gs_stack_entries = 42; + num_es_stack_entries = 42; + num_hs_stack_entries = 42; + num_ls_stack_entries = 42; + break; + case CHIP_REDWOOD: + num_ps_gprs = 93; + num_vs_gprs = 46; + num_temp_gprs = 4; + num_gs_gprs = 31; + num_es_gprs = 31; + num_hs_gprs = 23; + num_ls_gprs = 23; + num_ps_threads = 128; + num_vs_threads = 20; + num_gs_threads = 20; + num_es_threads = 20; + num_hs_threads = 20; + num_ls_threads = 20; + num_ps_stack_entries = 42; + num_vs_stack_entries = 42; + num_gs_stack_entries = 42; + num_es_stack_entries = 42; + num_hs_stack_entries = 42; + num_ls_stack_entries = 42; + break; + case CHIP_JUNIPER: + num_ps_gprs = 93; + num_vs_gprs = 46; + num_temp_gprs = 4; + num_gs_gprs = 31; + num_es_gprs = 31; + num_hs_gprs = 23; + num_ls_gprs = 23; + num_ps_threads = 128; + num_vs_threads = 20; + num_gs_threads = 20; + num_es_threads = 20; + num_hs_threads = 20; + num_ls_threads = 20; + num_ps_stack_entries = 85; + num_vs_stack_entries = 85; + num_gs_stack_entries = 85; + num_es_stack_entries = 85; + num_hs_stack_entries = 85; + num_ls_stack_entries = 85; + break; + case CHIP_CYPRESS: + case CHIP_HEMLOCK: + num_ps_gprs = 93; + num_vs_gprs = 46; + num_temp_gprs = 4; + num_gs_gprs = 31; + num_es_gprs = 31; + num_hs_gprs = 23; + num_ls_gprs = 23; + num_ps_threads = 128; + num_vs_threads = 20; + num_gs_threads = 20; + num_es_threads = 20; + num_hs_threads = 20; + num_ls_threads = 20; + num_ps_stack_entries = 85; + num_vs_stack_entries = 85; + num_gs_stack_entries = 85; + num_es_stack_entries = 85; + num_hs_stack_entries = 85; + num_ls_stack_entries = 85; + break; + case CHIP_PALM: + num_ps_gprs = 93; + num_vs_gprs = 46; + num_temp_gprs = 4; + num_gs_gprs = 31; + num_es_gprs = 31; + num_hs_gprs = 23; + num_ls_gprs = 23; + num_ps_threads = 96; + num_vs_threads = 16; + num_gs_threads = 16; + num_es_threads = 16; + num_hs_threads = 16; + num_ls_threads = 16; + num_ps_stack_entries = 42; + num_vs_stack_entries = 42; + num_gs_stack_entries = 42; + num_es_stack_entries = 42; + num_hs_stack_entries = 42; + num_ls_stack_entries = 42; + break; + case CHIP_SUMO: + num_ps_gprs = 93; + num_vs_gprs = 46; + num_temp_gprs = 4; + num_gs_gprs = 31; + num_es_gprs = 31; + num_hs_gprs = 23; + num_ls_gprs = 23; + num_ps_threads = 96; + num_vs_threads = 25; + num_gs_threads = 25; + num_es_threads = 25; + num_hs_threads = 25; + num_ls_threads = 25; + num_ps_stack_entries = 42; + num_vs_stack_entries = 42; + num_gs_stack_entries = 42; + num_es_stack_entries = 42; + num_hs_stack_entries = 42; + num_ls_stack_entries = 42; + break; + case CHIP_SUMO2: + num_ps_gprs = 93; + num_vs_gprs = 46; + num_temp_gprs = 4; + num_gs_gprs = 31; + num_es_gprs = 31; + num_hs_gprs = 23; + num_ls_gprs = 23; + num_ps_threads = 96; + num_vs_threads = 25; + num_gs_threads = 25; + num_es_threads = 25; + num_hs_threads = 25; + num_ls_threads = 25; + num_ps_stack_entries = 85; + num_vs_stack_entries = 85; + num_gs_stack_entries = 85; + num_es_stack_entries = 85; + num_hs_stack_entries = 85; + num_ls_stack_entries = 85; + break; + case CHIP_BARTS: + num_ps_gprs = 93; + num_vs_gprs = 46; + num_temp_gprs = 4; + num_gs_gprs = 31; + num_es_gprs = 31; + num_hs_gprs = 23; + num_ls_gprs = 23; + num_ps_threads = 128; + num_vs_threads = 20; + num_gs_threads = 20; + num_es_threads = 20; + num_hs_threads = 20; + num_ls_threads = 20; + num_ps_stack_entries = 85; + num_vs_stack_entries = 85; + num_gs_stack_entries = 85; + num_es_stack_entries = 85; + num_hs_stack_entries = 85; + num_ls_stack_entries = 85; + break; + case CHIP_TURKS: + num_ps_gprs = 93; + num_vs_gprs = 46; + num_temp_gprs = 4; + num_gs_gprs = 31; + num_es_gprs = 31; + num_hs_gprs = 23; + num_ls_gprs = 23; + num_ps_threads = 128; + num_vs_threads = 20; + num_gs_threads = 20; + num_es_threads = 20; + num_hs_threads = 20; + num_ls_threads = 20; + num_ps_stack_entries = 42; + num_vs_stack_entries = 42; + num_gs_stack_entries = 42; + num_es_stack_entries = 42; + num_hs_stack_entries = 42; + num_ls_stack_entries = 42; + break; + case CHIP_CAICOS: + num_ps_gprs = 93; + num_vs_gprs = 46; + num_temp_gprs = 4; + num_gs_gprs = 31; + num_es_gprs = 31; + num_hs_gprs = 23; + num_ls_gprs = 23; + num_ps_threads = 128; + num_vs_threads = 10; + num_gs_threads = 10; + num_es_threads = 10; + num_hs_threads = 10; + num_ls_threads = 10; + num_ps_stack_entries = 42; + num_vs_stack_entries = 42; + num_gs_stack_entries = 42; + num_es_stack_entries = 42; + num_hs_stack_entries = 42; + num_ls_stack_entries = 42; + break; + } + + if ((rdev->family == CHIP_CEDAR) || + (rdev->family == CHIP_PALM) || + (rdev->family == CHIP_SUMO) || + (rdev->family == CHIP_SUMO2) || + (rdev->family == CHIP_CAICOS)) + sq_config = 0; + else + sq_config = VC_ENABLE; + + sq_config |= (EXPORT_SRC_C | + CS_PRIO(0) | + LS_PRIO(0) | + HS_PRIO(0) | + PS_PRIO(0) | + VS_PRIO(1) | + GS_PRIO(2) | + ES_PRIO(3)); + + sq_gpr_resource_mgmt_1 = (NUM_PS_GPRS(num_ps_gprs) | + NUM_VS_GPRS(num_vs_gprs) | + NUM_CLAUSE_TEMP_GPRS(num_temp_gprs)); + sq_gpr_resource_mgmt_2 = (NUM_GS_GPRS(num_gs_gprs) | + NUM_ES_GPRS(num_es_gprs)); + sq_gpr_resource_mgmt_3 = (NUM_HS_GPRS(num_hs_gprs) | + NUM_LS_GPRS(num_ls_gprs)); + sq_thread_resource_mgmt = (NUM_PS_THREADS(num_ps_threads) | + NUM_VS_THREADS(num_vs_threads) | + NUM_GS_THREADS(num_gs_threads) | + NUM_ES_THREADS(num_es_threads)); + sq_thread_resource_mgmt_2 = (NUM_HS_THREADS(num_hs_threads) | + NUM_LS_THREADS(num_ls_threads)); + sq_stack_resource_mgmt_1 = (NUM_PS_STACK_ENTRIES(num_ps_stack_entries) | + NUM_VS_STACK_ENTRIES(num_vs_stack_entries)); + sq_stack_resource_mgmt_2 = (NUM_GS_STACK_ENTRIES(num_gs_stack_entries) | + NUM_ES_STACK_ENTRIES(num_es_stack_entries)); + sq_stack_resource_mgmt_3 = (NUM_HS_STACK_ENTRIES(num_hs_stack_entries) | + NUM_LS_STACK_ENTRIES(num_ls_stack_entries)); + + /* disable dyn gprs */ + radeon_ring_write(ring, PACKET3(PACKET3_SET_CONFIG_REG, 1)); + radeon_ring_write(ring, (SQ_DYN_GPR_CNTL_PS_FLUSH_REQ - PACKET3_SET_CONFIG_REG_START) >> 2); + radeon_ring_write(ring, 0); + + /* setup LDS */ + radeon_ring_write(ring, PACKET3(PACKET3_SET_CONFIG_REG, 1)); + radeon_ring_write(ring, (SQ_LDS_RESOURCE_MGMT - PACKET3_SET_CONFIG_REG_START) >> 2); + radeon_ring_write(ring, 0x10001000); + + /* SQ config */ + radeon_ring_write(ring, PACKET3(PACKET3_SET_CONFIG_REG, 11)); + radeon_ring_write(ring, (SQ_CONFIG - PACKET3_SET_CONFIG_REG_START) >> 2); + radeon_ring_write(ring, sq_config); + radeon_ring_write(ring, sq_gpr_resource_mgmt_1); + radeon_ring_write(ring, sq_gpr_resource_mgmt_2); + radeon_ring_write(ring, sq_gpr_resource_mgmt_3); + radeon_ring_write(ring, 0); + radeon_ring_write(ring, 0); + radeon_ring_write(ring, sq_thread_resource_mgmt); + radeon_ring_write(ring, sq_thread_resource_mgmt_2); + radeon_ring_write(ring, sq_stack_resource_mgmt_1); + radeon_ring_write(ring, sq_stack_resource_mgmt_2); + radeon_ring_write(ring, sq_stack_resource_mgmt_3); + } + + /* CONTEXT_CONTROL */ + radeon_ring_write(ring, 0xc0012800); + radeon_ring_write(ring, 0x80000000); + radeon_ring_write(ring, 0x80000000); + + /* SQ_VTX_BASE_VTX_LOC */ + radeon_ring_write(ring, 0xc0026f00); + radeon_ring_write(ring, 0x00000000); + radeon_ring_write(ring, 0x00000000); + radeon_ring_write(ring, 0x00000000); + + /* SET_SAMPLER */ + radeon_ring_write(ring, 0xc0036e00); + radeon_ring_write(ring, 0x00000000); + radeon_ring_write(ring, 0x00000012); + radeon_ring_write(ring, 0x00000000); + radeon_ring_write(ring, 0x00000000); + + /* set to DX10/11 mode */ + radeon_ring_write(ring, PACKET3(PACKET3_MODE_CONTROL, 0)); + radeon_ring_write(ring, 1); + + /* emit an IB pointing at default state */ + dwords = roundup2(rdev->r600_blit.state_len, 0x10); + gpu_addr = rdev->r600_blit.shader_gpu_addr + rdev->r600_blit.state_offset; + radeon_ring_write(ring, PACKET3(PACKET3_INDIRECT_BUFFER, 2)); + radeon_ring_write(ring, gpu_addr & 0xFFFFFFFC); + radeon_ring_write(ring, upper_32_bits(gpu_addr) & 0xFF); + radeon_ring_write(ring, dwords); + +} + +int evergreen_blit_init(struct radeon_device *rdev) +{ + u32 obj_size; + int i, r, dwords; + void *ptr; + u32 packet2s[16]; + int num_packet2s = 0; + + rdev->r600_blit.primitives.set_render_target = set_render_target; + rdev->r600_blit.primitives.cp_set_surface_sync = cp_set_surface_sync; + rdev->r600_blit.primitives.set_shaders = set_shaders; + rdev->r600_blit.primitives.set_vtx_resource = set_vtx_resource; + rdev->r600_blit.primitives.set_tex_resource = set_tex_resource; + rdev->r600_blit.primitives.set_scissors = set_scissors; + rdev->r600_blit.primitives.draw_auto = draw_auto; + rdev->r600_blit.primitives.set_default_state = set_default_state; + + rdev->r600_blit.ring_size_common = 8; /* sync semaphore */ + rdev->r600_blit.ring_size_common += 55; /* shaders + def state */ + rdev->r600_blit.ring_size_common += 16; /* fence emit for VB IB */ + rdev->r600_blit.ring_size_common += 5; /* done copy */ + rdev->r600_blit.ring_size_common += 16; /* fence emit for done copy */ + + rdev->r600_blit.ring_size_per_loop = 74; + if (rdev->family >= CHIP_CAYMAN) + rdev->r600_blit.ring_size_per_loop += 9; /* additional DWs for surface sync */ + + rdev->r600_blit.max_dim = 16384; + + rdev->r600_blit.state_offset = 0; + + if (rdev->family < CHIP_CAYMAN) + rdev->r600_blit.state_len = evergreen_default_size; + else + rdev->r600_blit.state_len = cayman_default_size; + + dwords = rdev->r600_blit.state_len; + while (dwords & 0xf) { + packet2s[num_packet2s++] = cpu_to_le32(PACKET2(0)); + dwords++; + } + + obj_size = dwords * 4; + obj_size = roundup2(obj_size, 256); + + rdev->r600_blit.vs_offset = obj_size; + if (rdev->family < CHIP_CAYMAN) + obj_size += evergreen_vs_size * 4; + else + obj_size += cayman_vs_size * 4; + obj_size = roundup2(obj_size, 256); + + rdev->r600_blit.ps_offset = obj_size; + if (rdev->family < CHIP_CAYMAN) + obj_size += evergreen_ps_size * 4; + else + obj_size += cayman_ps_size * 4; + obj_size = roundup2(obj_size, 256); + + /* pin copy shader into vram if not already initialized */ + if (!rdev->r600_blit.shader_obj) { + r = radeon_bo_create(rdev, obj_size, PAGE_SIZE, true, + RADEON_GEM_DOMAIN_VRAM, + NULL, &rdev->r600_blit.shader_obj); + if (r) { + DRM_ERROR("evergreen failed to allocate shader\n"); + return r; + } + + r = radeon_bo_reserve(rdev->r600_blit.shader_obj, false); + if (unlikely(r != 0)) + return r; + r = radeon_bo_pin(rdev->r600_blit.shader_obj, RADEON_GEM_DOMAIN_VRAM, + &rdev->r600_blit.shader_gpu_addr); + radeon_bo_unreserve(rdev->r600_blit.shader_obj); + if (r) { + dev_err(rdev->dev, "(%d) pin blit object failed\n", r); + return r; + } + } + + DRM_DEBUG("evergreen blit allocated bo %08x vs %08x ps %08x\n", + obj_size, + rdev->r600_blit.vs_offset, rdev->r600_blit.ps_offset); + + r = radeon_bo_reserve(rdev->r600_blit.shader_obj, false); + if (unlikely(r != 0)) + return r; + r = radeon_bo_kmap(rdev->r600_blit.shader_obj, &ptr); + if (r) { + DRM_ERROR("failed to map blit object %d\n", r); + return r; + } + + if (rdev->family < CHIP_CAYMAN) { + memcpy_toio((char *)ptr + rdev->r600_blit.state_offset, + evergreen_default_state, rdev->r600_blit.state_len * 4); + + if (num_packet2s) + memcpy_toio((char *)ptr + rdev->r600_blit.state_offset + (rdev->r600_blit.state_len * 4), + packet2s, num_packet2s * 4); + for (i = 0; i < evergreen_vs_size; i++) + *(u32 *)((unsigned long)ptr + rdev->r600_blit.vs_offset + i * 4) = cpu_to_le32(evergreen_vs[i]); + for (i = 0; i < evergreen_ps_size; i++) + *(u32 *)((unsigned long)ptr + rdev->r600_blit.ps_offset + i * 4) = cpu_to_le32(evergreen_ps[i]); + } else { + memcpy_toio((char *)ptr + rdev->r600_blit.state_offset, + cayman_default_state, rdev->r600_blit.state_len * 4); + + if (num_packet2s) + memcpy_toio((char *)ptr + rdev->r600_blit.state_offset + (rdev->r600_blit.state_len * 4), + packet2s, num_packet2s * 4); + for (i = 0; i < cayman_vs_size; i++) + *(u32 *)((unsigned long)ptr + rdev->r600_blit.vs_offset + i * 4) = cpu_to_le32(cayman_vs[i]); + for (i = 0; i < cayman_ps_size; i++) + *(u32 *)((unsigned long)ptr + rdev->r600_blit.ps_offset + i * 4) = cpu_to_le32(cayman_ps[i]); + } + radeon_bo_kunmap(rdev->r600_blit.shader_obj); + radeon_bo_unreserve(rdev->r600_blit.shader_obj); + + radeon_ttm_set_active_vram_size(rdev, rdev->mc.real_vram_size); + return 0; +} diff --git a/sys/dev/drm2/radeon/evergreen_blit_shaders.c b/sys/dev/drm2/radeon/evergreen_blit_shaders.c new file mode 100644 index 00000000000..6db3cef0255 --- /dev/null +++ b/sys/dev/drm2/radeon/evergreen_blit_shaders.c @@ -0,0 +1,358 @@ +/* + * Copyright 2010 Advanced Micro Devices, Inc. + * + * 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 (including the next + * paragraph) 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 COPYRIGHT HOLDER(S) AND/OR ITS SUPPLIERS 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. + * + * Authors: + * Alex Deucher + */ + +#include +__FBSDID("$FreeBSD$"); + +#include + +/* + * evergreen cards need to use the 3D engine to blit data which requires + * quite a bit of hw state setup. Rather than pull the whole 3D driver + * (which normally generates the 3D state) into the DRM, we opt to use + * statically generated state tables. The regsiter state and shaders + * were hand generated to support blitting functionality. See the 3D + * driver or documentation for descriptions of the registers and + * shader instructions. + */ + +const u32 evergreen_default_state[] = +{ + 0xc0016900, + 0x0000023b, + 0x00000000, /* SQ_LDS_ALLOC_PS */ + + 0xc0066900, + 0x00000240, + 0x00000000, /* SQ_ESGS_RING_ITEMSIZE */ + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + + 0xc0046900, + 0x00000247, + 0x00000000, /* SQ_GS_VERT_ITEMSIZE */ + 0x00000000, + 0x00000000, + 0x00000000, + + 0xc0026900, + 0x00000010, + 0x00000000, /* DB_Z_INFO */ + 0x00000000, /* DB_STENCIL_INFO */ + + 0xc0016900, + 0x00000200, + 0x00000000, /* DB_DEPTH_CONTROL */ + + 0xc0066900, + 0x00000000, + 0x00000060, /* DB_RENDER_CONTROL */ + 0x00000000, /* DB_COUNT_CONTROL */ + 0x00000000, /* DB_DEPTH_VIEW */ + 0x0000002a, /* DB_RENDER_OVERRIDE */ + 0x00000000, /* DB_RENDER_OVERRIDE2 */ + 0x00000000, /* DB_HTILE_DATA_BASE */ + + 0xc0026900, + 0x0000000a, + 0x00000000, /* DB_STENCIL_CLEAR */ + 0x00000000, /* DB_DEPTH_CLEAR */ + + 0xc0016900, + 0x000002dc, + 0x0000aa00, /* DB_ALPHA_TO_MASK */ + + 0xc0016900, + 0x00000080, + 0x00000000, /* PA_SC_WINDOW_OFFSET */ + + 0xc00d6900, + 0x00000083, + 0x0000ffff, /* PA_SC_CLIPRECT_RULE */ + 0x00000000, /* PA_SC_CLIPRECT_0_TL */ + 0x20002000, /* PA_SC_CLIPRECT_0_BR */ + 0x00000000, + 0x20002000, + 0x00000000, + 0x20002000, + 0x00000000, + 0x20002000, + 0xaaaaaaaa, /* PA_SC_EDGERULE */ + 0x00000000, /* PA_SU_HARDWARE_SCREEN_OFFSET */ + 0x0000000f, /* CB_TARGET_MASK */ + 0x0000000f, /* CB_SHADER_MASK */ + + 0xc0226900, + 0x00000094, + 0x80000000, /* PA_SC_VPORT_SCISSOR_0_TL */ + 0x20002000, /* PA_SC_VPORT_SCISSOR_0_BR */ + 0x80000000, + 0x20002000, + 0x80000000, + 0x20002000, + 0x80000000, + 0x20002000, + 0x80000000, + 0x20002000, + 0x80000000, + 0x20002000, + 0x80000000, + 0x20002000, + 0x80000000, + 0x20002000, + 0x80000000, + 0x20002000, + 0x80000000, + 0x20002000, + 0x80000000, + 0x20002000, + 0x80000000, + 0x20002000, + 0x80000000, + 0x20002000, + 0x80000000, + 0x20002000, + 0x80000000, + 0x20002000, + 0x80000000, + 0x20002000, + 0x00000000, /* PA_SC_VPORT_ZMIN_0 */ + 0x3f800000, /* PA_SC_VPORT_ZMAX_0 */ + + 0xc0016900, + 0x000000d4, + 0x00000000, /* SX_MISC */ + + 0xc0026900, + 0x00000292, + 0x00000000, /* PA_SC_MODE_CNTL_0 */ + 0x00000000, /* PA_SC_MODE_CNTL_1 */ + + 0xc0106900, + 0x00000300, + 0x00000000, /* PA_SC_LINE_CNTL */ + 0x00000000, /* PA_SC_AA_CONFIG */ + 0x00000005, /* PA_SU_VTX_CNTL */ + 0x3f800000, /* PA_CL_GB_VERT_CLIP_ADJ */ + 0x3f800000, /* PA_CL_GB_VERT_DISC_ADJ */ + 0x3f800000, /* PA_CL_GB_HORZ_CLIP_ADJ */ + 0x3f800000, /* PA_CL_GB_HORZ_DISC_ADJ */ + 0x00000000, /* PA_SC_AA_SAMPLE_LOCS_0 */ + 0x00000000, /* */ + 0x00000000, /* */ + 0x00000000, /* */ + 0x00000000, /* */ + 0x00000000, /* */ + 0x00000000, /* */ + 0x00000000, /* PA_SC_AA_SAMPLE_LOCS_7 */ + 0xffffffff, /* PA_SC_AA_MASK */ + + 0xc00d6900, + 0x00000202, + 0x00cc0010, /* CB_COLOR_CONTROL */ + 0x00000210, /* DB_SHADER_CONTROL */ + 0x00010000, /* PA_CL_CLIP_CNTL */ + 0x00000004, /* PA_SU_SC_MODE_CNTL */ + 0x00000100, /* PA_CL_VTE_CNTL */ + 0x00000000, /* PA_CL_VS_OUT_CNTL */ + 0x00000000, /* PA_CL_NANINF_CNTL */ + 0x00000000, /* PA_SU_LINE_STIPPLE_CNTL */ + 0x00000000, /* PA_SU_LINE_STIPPLE_SCALE */ + 0x00000000, /* PA_SU_PRIM_FILTER_CNTL */ + 0x00000000, /* */ + 0x00000000, /* */ + 0x00000000, /* SQ_DYN_GPR_RESOURCE_LIMIT_1 */ + + 0xc0066900, + 0x000002de, + 0x00000000, /* PA_SU_POLY_OFFSET_DB_FMT_CNTL */ + 0x00000000, /* */ + 0x00000000, /* */ + 0x00000000, /* */ + 0x00000000, /* */ + 0x00000000, /* */ + + 0xc0016900, + 0x00000229, + 0x00000000, /* SQ_PGM_START_FS */ + + 0xc0016900, + 0x0000022a, + 0x00000000, /* SQ_PGM_RESOURCES_FS */ + + 0xc0096900, + 0x00000100, + 0x00ffffff, /* VGT_MAX_VTX_INDX */ + 0x00000000, /* */ + 0x00000000, /* */ + 0x00000000, /* */ + 0x00000000, /* SX_ALPHA_TEST_CONTROL */ + 0x00000000, /* CB_BLEND_RED */ + 0x00000000, /* CB_BLEND_GREEN */ + 0x00000000, /* CB_BLEND_BLUE */ + 0x00000000, /* CB_BLEND_ALPHA */ + + 0xc0026900, + 0x000002a8, + 0x00000000, /* VGT_INSTANCE_STEP_RATE_0 */ + 0x00000000, /* */ + + 0xc0026900, + 0x000002ad, + 0x00000000, /* VGT_REUSE_OFF */ + 0x00000000, /* */ + + 0xc0116900, + 0x00000280, + 0x00000000, /* PA_SU_POINT_SIZE */ + 0x00000000, /* PA_SU_POINT_MINMAX */ + 0x00000008, /* PA_SU_LINE_CNTL */ + 0x00000000, /* PA_SC_LINE_STIPPLE */ + 0x00000000, /* VGT_OUTPUT_PATH_CNTL */ + 0x00000000, /* VGT_HOS_CNTL */ + 0x00000000, /* */ + 0x00000000, /* */ + 0x00000000, /* */ + 0x00000000, /* */ + 0x00000000, /* */ + 0x00000000, /* */ + 0x00000000, /* */ + 0x00000000, /* */ + 0x00000000, /* */ + 0x00000000, /* */ + 0x00000000, /* VGT_GS_MODE */ + + 0xc0016900, + 0x000002a1, + 0x00000000, /* VGT_PRIMITIVEID_EN */ + + 0xc0016900, + 0x000002a5, + 0x00000000, /* VGT_MULTI_PRIM_IB_RESET_EN */ + + 0xc0016900, + 0x000002d5, + 0x00000000, /* VGT_SHADER_STAGES_EN */ + + 0xc0026900, + 0x000002e5, + 0x00000000, /* VGT_STRMOUT_CONFIG */ + 0x00000000, /* */ + + 0xc0016900, + 0x000001e0, + 0x00000000, /* CB_BLEND0_CONTROL */ + + 0xc0016900, + 0x000001b1, + 0x00000000, /* SPI_VS_OUT_CONFIG */ + + 0xc0016900, + 0x00000187, + 0x00000000, /* SPI_VS_OUT_ID_0 */ + + 0xc0016900, + 0x00000191, + 0x00000100, /* SPI_PS_INPUT_CNTL_0 */ + + 0xc00b6900, + 0x000001b3, + 0x20000001, /* SPI_PS_IN_CONTROL_0 */ + 0x00000000, /* SPI_PS_IN_CONTROL_1 */ + 0x00000000, /* SPI_INTERP_CONTROL_0 */ + 0x00000000, /* SPI_INPUT_Z */ + 0x00000000, /* SPI_FOG_CNTL */ + 0x00100000, /* SPI_BARYC_CNTL */ + 0x00000000, /* SPI_PS_IN_CONTROL_2 */ + 0x00000000, /* */ + 0x00000000, /* */ + 0x00000000, /* */ + 0x00000000, /* */ + + 0xc0026900, + 0x00000316, + 0x0000000e, /* VGT_VERTEX_REUSE_BLOCK_CNTL */ + 0x00000010, /* */ +}; + +const u32 evergreen_vs[] = +{ + 0x00000004, + 0x80800400, + 0x0000a03c, + 0x95000688, + 0x00004000, + 0x15200688, + 0x00000000, + 0x00000000, + 0x3c000000, + 0x67961001, +#ifdef __BIG_ENDIAN + 0x000a0000, +#else + 0x00080000, +#endif + 0x00000000, + 0x1c000000, + 0x67961000, +#ifdef __BIG_ENDIAN + 0x00020008, +#else + 0x00000008, +#endif + 0x00000000, +}; + +const u32 evergreen_ps[] = +{ + 0x00000003, + 0xa00c0000, + 0x00000008, + 0x80400000, + 0x00000000, + 0x95200688, + 0x00380400, + 0x00146b10, + 0x00380000, + 0x20146b10, + 0x00380400, + 0x40146b00, + 0x80380000, + 0x60146b00, + 0x00000000, + 0x00000000, + 0x00000010, + 0x000d1000, + 0xb0800000, + 0x00000000, +}; + +const u32 evergreen_ps_size = DRM_ARRAY_SIZE(evergreen_ps); +const u32 evergreen_vs_size = DRM_ARRAY_SIZE(evergreen_vs); +const u32 evergreen_default_size = DRM_ARRAY_SIZE(evergreen_default_state); diff --git a/sys/dev/drm2/radeon/evergreen_blit_shaders.h b/sys/dev/drm2/radeon/evergreen_blit_shaders.h new file mode 100644 index 00000000000..a5e2dc3490d --- /dev/null +++ b/sys/dev/drm2/radeon/evergreen_blit_shaders.h @@ -0,0 +1,38 @@ +/* + * Copyright 2009 Advanced Micro Devices, Inc. + * + * 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 (including the next + * paragraph) 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 COPYRIGHT HOLDER(S) AND/OR ITS SUPPLIERS 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 +__FBSDID("$FreeBSD$"); + +#ifndef EVERGREEN_BLIT_SHADERS_H +#define EVERGREEN_BLIT_SHADERS_H + +extern const u32 evergreen_ps[]; +extern const u32 evergreen_vs[]; +extern const u32 evergreen_default_state[]; + +extern const u32 evergreen_ps_size, evergreen_vs_size; +extern const u32 evergreen_default_size; + +#endif diff --git a/sys/dev/drm2/radeon/evergreen_cs.c b/sys/dev/drm2/radeon/evergreen_cs.c new file mode 100644 index 00000000000..693c38c2900 --- /dev/null +++ b/sys/dev/drm2/radeon/evergreen_cs.c @@ -0,0 +1,3727 @@ +/* + * Copyright 2010 Advanced Micro Devices, Inc. + * Copyright 2008 Red Hat Inc. + * Copyright 2009 Jerome Glisse. + * + * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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. + * + * Authors: Dave Airlie + * Alex Deucher + * Jerome Glisse + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include "radeon.h" +#include "radeon_asic.h" +#include "evergreend.h" +#include "evergreen_reg_safe.h" +#include "cayman_reg_safe.h" +#include "r600_cs.h" + +#define MAX(a,b) (((a)>(b))?(a):(b)) +#define MIN(a,b) (((a)<(b))?(a):(b)) + +static int evergreen_cs_packet_next_reloc(struct radeon_cs_parser *p, + struct radeon_cs_reloc **cs_reloc); + +struct evergreen_cs_track { + u32 group_size; + u32 nbanks; + u32 npipes; + u32 row_size; + /* value we track */ + u32 nsamples; /* unused */ + struct radeon_bo *cb_color_bo[12]; + u32 cb_color_bo_offset[12]; + struct radeon_bo *cb_color_fmask_bo[8]; /* unused */ + struct radeon_bo *cb_color_cmask_bo[8]; /* unused */ + u32 cb_color_info[12]; + u32 cb_color_view[12]; + u32 cb_color_pitch[12]; + u32 cb_color_slice[12]; + u32 cb_color_slice_idx[12]; + u32 cb_color_attrib[12]; + u32 cb_color_cmask_slice[8];/* unused */ + u32 cb_color_fmask_slice[8];/* unused */ + u32 cb_target_mask; + u32 cb_shader_mask; /* unused */ + u32 vgt_strmout_config; + u32 vgt_strmout_buffer_config; + struct radeon_bo *vgt_strmout_bo[4]; + u32 vgt_strmout_bo_offset[4]; + u32 vgt_strmout_size[4]; + u32 db_depth_control; + u32 db_depth_view; + u32 db_depth_slice; + u32 db_depth_size; + u32 db_z_info; + u32 db_z_read_offset; + u32 db_z_write_offset; + struct radeon_bo *db_z_read_bo; + struct radeon_bo *db_z_write_bo; + u32 db_s_info; + u32 db_s_read_offset; + u32 db_s_write_offset; + struct radeon_bo *db_s_read_bo; + struct radeon_bo *db_s_write_bo; + bool sx_misc_kill_all_prims; + bool cb_dirty; + bool db_dirty; + bool streamout_dirty; + u32 htile_offset; + u32 htile_surface; + struct radeon_bo *htile_bo; +}; + +static u32 evergreen_cs_get_aray_mode(u32 tiling_flags) +{ + if (tiling_flags & RADEON_TILING_MACRO) + return ARRAY_2D_TILED_THIN1; + else if (tiling_flags & RADEON_TILING_MICRO) + return ARRAY_1D_TILED_THIN1; + else + return ARRAY_LINEAR_GENERAL; +} + +static u32 evergreen_cs_get_num_banks(u32 nbanks) +{ + switch (nbanks) { + case 2: + return ADDR_SURF_2_BANK; + case 4: + return ADDR_SURF_4_BANK; + case 8: + default: + return ADDR_SURF_8_BANK; + case 16: + return ADDR_SURF_16_BANK; + } +} + +static void evergreen_cs_track_init(struct evergreen_cs_track *track) +{ + int i; + + for (i = 0; i < 8; i++) { + track->cb_color_fmask_bo[i] = NULL; + track->cb_color_cmask_bo[i] = NULL; + track->cb_color_cmask_slice[i] = 0; + track->cb_color_fmask_slice[i] = 0; + } + + for (i = 0; i < 12; i++) { + track->cb_color_bo[i] = NULL; + track->cb_color_bo_offset[i] = 0xFFFFFFFF; + track->cb_color_info[i] = 0; + track->cb_color_view[i] = 0xFFFFFFFF; + track->cb_color_pitch[i] = 0; + track->cb_color_slice[i] = 0xfffffff; + track->cb_color_slice_idx[i] = 0; + } + track->cb_target_mask = 0xFFFFFFFF; + track->cb_shader_mask = 0xFFFFFFFF; + track->cb_dirty = true; + + track->db_depth_slice = 0xffffffff; + track->db_depth_view = 0xFFFFC000; + track->db_depth_size = 0xFFFFFFFF; + track->db_depth_control = 0xFFFFFFFF; + track->db_z_info = 0xFFFFFFFF; + track->db_z_read_offset = 0xFFFFFFFF; + track->db_z_write_offset = 0xFFFFFFFF; + track->db_z_read_bo = NULL; + track->db_z_write_bo = NULL; + track->db_s_info = 0xFFFFFFFF; + track->db_s_read_offset = 0xFFFFFFFF; + track->db_s_write_offset = 0xFFFFFFFF; + track->db_s_read_bo = NULL; + track->db_s_write_bo = NULL; + track->db_dirty = true; + track->htile_bo = NULL; + track->htile_offset = 0xFFFFFFFF; + track->htile_surface = 0; + + for (i = 0; i < 4; i++) { + track->vgt_strmout_size[i] = 0; + track->vgt_strmout_bo[i] = NULL; + track->vgt_strmout_bo_offset[i] = 0xFFFFFFFF; + } + track->streamout_dirty = true; + track->sx_misc_kill_all_prims = false; +} + +struct eg_surface { + /* value gathered from cs */ + unsigned nbx; + unsigned nby; + unsigned format; + unsigned mode; + unsigned nbanks; + unsigned bankw; + unsigned bankh; + unsigned tsplit; + unsigned mtilea; + unsigned nsamples; + /* output value */ + unsigned bpe; + unsigned layer_size; + unsigned palign; + unsigned halign; + unsigned long base_align; +}; + +static int evergreen_surface_check_linear(struct radeon_cs_parser *p, + struct eg_surface *surf, + const char *prefix) +{ + surf->layer_size = surf->nbx * surf->nby * surf->bpe * surf->nsamples; + surf->base_align = surf->bpe; + surf->palign = 1; + surf->halign = 1; + return 0; +} + +static int evergreen_surface_check_linear_aligned(struct radeon_cs_parser *p, + struct eg_surface *surf, + const char *prefix) +{ + struct evergreen_cs_track *track = p->track; + unsigned palign; + + palign = MAX(64, track->group_size / surf->bpe); + surf->layer_size = surf->nbx * surf->nby * surf->bpe * surf->nsamples; + surf->base_align = track->group_size; + surf->palign = palign; + surf->halign = 1; + if (surf->nbx & (palign - 1)) { + if (prefix) { + dev_warn(p->dev, "%s:%d %s pitch %d invalid must be aligned with %d\n", + __func__, __LINE__, prefix, surf->nbx, palign); + } + return -EINVAL; + } + return 0; +} + +static int evergreen_surface_check_1d(struct radeon_cs_parser *p, + struct eg_surface *surf, + const char *prefix) +{ + struct evergreen_cs_track *track = p->track; + unsigned palign; + + palign = track->group_size / (8 * surf->bpe * surf->nsamples); + palign = MAX(8, palign); + surf->layer_size = surf->nbx * surf->nby * surf->bpe; + surf->base_align = track->group_size; + surf->palign = palign; + surf->halign = 8; + if ((surf->nbx & (palign - 1))) { + if (prefix) { + dev_warn(p->dev, "%s:%d %s pitch %d invalid must be aligned with %d (%d %d %d)\n", + __func__, __LINE__, prefix, surf->nbx, palign, + track->group_size, surf->bpe, surf->nsamples); + } + return -EINVAL; + } + if ((surf->nby & (8 - 1))) { + if (prefix) { + dev_warn(p->dev, "%s:%d %s height %d invalid must be aligned with 8\n", + __func__, __LINE__, prefix, surf->nby); + } + return -EINVAL; + } + return 0; +} + +static int evergreen_surface_check_2d(struct radeon_cs_parser *p, + struct eg_surface *surf, + const char *prefix) +{ + struct evergreen_cs_track *track = p->track; + unsigned palign, halign, tileb, slice_pt; + unsigned mtile_pr, mtile_ps, mtileb; + + tileb = 64 * surf->bpe * surf->nsamples; + slice_pt = 1; + if (tileb > surf->tsplit) { + slice_pt = tileb / surf->tsplit; + } + tileb = tileb / slice_pt; + /* macro tile width & height */ + palign = (8 * surf->bankw * track->npipes) * surf->mtilea; + halign = (8 * surf->bankh * surf->nbanks) / surf->mtilea; + mtileb = (palign / 8) * (halign / 8) * tileb; + mtile_pr = surf->nbx / palign; + mtile_ps = (mtile_pr * surf->nby) / halign; + surf->layer_size = mtile_ps * mtileb * slice_pt; + surf->base_align = (palign / 8) * (halign / 8) * tileb; + surf->palign = palign; + surf->halign = halign; + + if ((surf->nbx & (palign - 1))) { + if (prefix) { + dev_warn(p->dev, "%s:%d %s pitch %d invalid must be aligned with %d\n", + __func__, __LINE__, prefix, surf->nbx, palign); + } + return -EINVAL; + } + if ((surf->nby & (halign - 1))) { + if (prefix) { + dev_warn(p->dev, "%s:%d %s height %d invalid must be aligned with %d\n", + __func__, __LINE__, prefix, surf->nby, halign); + } + return -EINVAL; + } + + return 0; +} + +static int evergreen_surface_check(struct radeon_cs_parser *p, + struct eg_surface *surf, + const char *prefix) +{ + /* some common value computed here */ + surf->bpe = r600_fmt_get_blocksize(surf->format); + + switch (surf->mode) { + case ARRAY_LINEAR_GENERAL: + return evergreen_surface_check_linear(p, surf, prefix); + case ARRAY_LINEAR_ALIGNED: + return evergreen_surface_check_linear_aligned(p, surf, prefix); + case ARRAY_1D_TILED_THIN1: + return evergreen_surface_check_1d(p, surf, prefix); + case ARRAY_2D_TILED_THIN1: + return evergreen_surface_check_2d(p, surf, prefix); + default: + dev_warn(p->dev, "%s:%d %s invalid array mode %d\n", + __func__, __LINE__, prefix, surf->mode); + return -EINVAL; + } + return -EINVAL; +} + +static int evergreen_surface_value_conv_check(struct radeon_cs_parser *p, + struct eg_surface *surf, + const char *prefix) +{ + switch (surf->mode) { + case ARRAY_2D_TILED_THIN1: + break; + case ARRAY_LINEAR_GENERAL: + case ARRAY_LINEAR_ALIGNED: + case ARRAY_1D_TILED_THIN1: + return 0; + default: + dev_warn(p->dev, "%s:%d %s invalid array mode %d\n", + __func__, __LINE__, prefix, surf->mode); + return -EINVAL; + } + + switch (surf->nbanks) { + case 0: surf->nbanks = 2; break; + case 1: surf->nbanks = 4; break; + case 2: surf->nbanks = 8; break; + case 3: surf->nbanks = 16; break; + default: + dev_warn(p->dev, "%s:%d %s invalid number of banks %d\n", + __func__, __LINE__, prefix, surf->nbanks); + return -EINVAL; + } + switch (surf->bankw) { + case 0: surf->bankw = 1; break; + case 1: surf->bankw = 2; break; + case 2: surf->bankw = 4; break; + case 3: surf->bankw = 8; break; + default: + dev_warn(p->dev, "%s:%d %s invalid bankw %d\n", + __func__, __LINE__, prefix, surf->bankw); + return -EINVAL; + } + switch (surf->bankh) { + case 0: surf->bankh = 1; break; + case 1: surf->bankh = 2; break; + case 2: surf->bankh = 4; break; + case 3: surf->bankh = 8; break; + default: + dev_warn(p->dev, "%s:%d %s invalid bankh %d\n", + __func__, __LINE__, prefix, surf->bankh); + return -EINVAL; + } + switch (surf->mtilea) { + case 0: surf->mtilea = 1; break; + case 1: surf->mtilea = 2; break; + case 2: surf->mtilea = 4; break; + case 3: surf->mtilea = 8; break; + default: + dev_warn(p->dev, "%s:%d %s invalid macro tile aspect %d\n", + __func__, __LINE__, prefix, surf->mtilea); + return -EINVAL; + } + switch (surf->tsplit) { + case 0: surf->tsplit = 64; break; + case 1: surf->tsplit = 128; break; + case 2: surf->tsplit = 256; break; + case 3: surf->tsplit = 512; break; + case 4: surf->tsplit = 1024; break; + case 5: surf->tsplit = 2048; break; + case 6: surf->tsplit = 4096; break; + default: + dev_warn(p->dev, "%s:%d %s invalid tile split %d\n", + __func__, __LINE__, prefix, surf->tsplit); + return -EINVAL; + } + return 0; +} + +static int evergreen_cs_track_validate_cb(struct radeon_cs_parser *p, unsigned id) +{ + struct evergreen_cs_track *track = p->track; + struct eg_surface surf; + unsigned pitch, slice, mslice; + unsigned long offset; + int r; + + mslice = G_028C6C_SLICE_MAX(track->cb_color_view[id]) + 1; + pitch = track->cb_color_pitch[id]; + slice = track->cb_color_slice[id]; + surf.nbx = (pitch + 1) * 8; + surf.nby = ((slice + 1) * 64) / surf.nbx; + surf.mode = G_028C70_ARRAY_MODE(track->cb_color_info[id]); + surf.format = G_028C70_FORMAT(track->cb_color_info[id]); + surf.tsplit = G_028C74_TILE_SPLIT(track->cb_color_attrib[id]); + surf.nbanks = G_028C74_NUM_BANKS(track->cb_color_attrib[id]); + surf.bankw = G_028C74_BANK_WIDTH(track->cb_color_attrib[id]); + surf.bankh = G_028C74_BANK_HEIGHT(track->cb_color_attrib[id]); + surf.mtilea = G_028C74_MACRO_TILE_ASPECT(track->cb_color_attrib[id]); + surf.nsamples = 1; + + if (!r600_fmt_is_valid_color(surf.format)) { + dev_warn(p->dev, "%s:%d cb invalid format %d for %d (0x%08x)\n", + __func__, __LINE__, surf.format, + id, track->cb_color_info[id]); + return -EINVAL; + } + + r = evergreen_surface_value_conv_check(p, &surf, "cb"); + if (r) { + return r; + } + + r = evergreen_surface_check(p, &surf, "cb"); + if (r) { + dev_warn(p->dev, "%s:%d cb[%d] invalid (0x%08x 0x%08x 0x%08x 0x%08x)\n", + __func__, __LINE__, id, track->cb_color_pitch[id], + track->cb_color_slice[id], track->cb_color_attrib[id], + track->cb_color_info[id]); + return r; + } + + offset = track->cb_color_bo_offset[id] << 8; + if (offset & (surf.base_align - 1)) { + dev_warn(p->dev, "%s:%d cb[%d] bo base %ld not aligned with %ld\n", + __func__, __LINE__, id, offset, surf.base_align); + return -EINVAL; + } + + offset += surf.layer_size * mslice; + if (offset > radeon_bo_size(track->cb_color_bo[id])) { + /* old ddx are broken they allocate bo with w*h*bpp but + * program slice with ALIGN(h, 8), catch this and patch + * command stream. + */ + if (!surf.mode) { + volatile u32 *ib = p->ib.ptr; + unsigned long tmp, nby, bsize, size, min = 0; + + /* find the height the ddx wants */ + if (surf.nby > 8) { + min = surf.nby - 8; + } + bsize = radeon_bo_size(track->cb_color_bo[id]); + tmp = track->cb_color_bo_offset[id] << 8; + for (nby = surf.nby; nby > min; nby--) { + size = nby * surf.nbx * surf.bpe * surf.nsamples; + if ((tmp + size * mslice) <= bsize) { + break; + } + } + if (nby > min) { + surf.nby = nby; + slice = ((nby * surf.nbx) / 64) - 1; + if (!evergreen_surface_check(p, &surf, "cb")) { + /* check if this one works */ + tmp += surf.layer_size * mslice; + if (tmp <= bsize) { + ib[track->cb_color_slice_idx[id]] = slice; + goto old_ddx_ok; + } + } + } + } + dev_warn(p->dev, "%s:%d cb[%d] bo too small (layer size %d, " + "offset %d, max layer %d, bo size %ld, slice %d)\n", + __func__, __LINE__, id, surf.layer_size, + track->cb_color_bo_offset[id] << 8, mslice, + radeon_bo_size(track->cb_color_bo[id]), slice); + dev_warn(p->dev, "%s:%d problematic surf: (%d %d) (%d %d %d %d %d %d %d)\n", + __func__, __LINE__, surf.nbx, surf.nby, + surf.mode, surf.bpe, surf.nsamples, + surf.bankw, surf.bankh, + surf.tsplit, surf.mtilea); + return -EINVAL; + } +old_ddx_ok: + + return 0; +} + +static int evergreen_cs_track_validate_htile(struct radeon_cs_parser *p, + unsigned nbx, unsigned nby) +{ + struct evergreen_cs_track *track = p->track; + unsigned long size; + + if (track->htile_bo == NULL) { + dev_warn(p->dev, "%s:%d htile enabled without htile surface 0x%08x\n", + __func__, __LINE__, track->db_z_info); + return -EINVAL; + } + + if (G_028ABC_LINEAR(track->htile_surface)) { + /* pitch must be 16 htiles aligned == 16 * 8 pixel aligned */ + nbx = roundup(nbx, 16 * 8); + /* height is npipes htiles aligned == npipes * 8 pixel aligned */ + nby = roundup(nby, track->npipes * 8); + } else { + /* always assume 8x8 htile */ + /* align is htile align * 8, htile align vary according to + * number of pipe and tile width and nby + */ + switch (track->npipes) { + case 8: + /* HTILE_WIDTH = 8 & HTILE_HEIGHT = 8*/ + nbx = roundup(nbx, 64 * 8); + nby = roundup(nby, 64 * 8); + break; + case 4: + /* HTILE_WIDTH = 8 & HTILE_HEIGHT = 8*/ + nbx = roundup(nbx, 64 * 8); + nby = roundup(nby, 32 * 8); + break; + case 2: + /* HTILE_WIDTH = 8 & HTILE_HEIGHT = 8*/ + nbx = roundup(nbx, 32 * 8); + nby = roundup(nby, 32 * 8); + break; + case 1: + /* HTILE_WIDTH = 8 & HTILE_HEIGHT = 8*/ + nbx = roundup(nbx, 32 * 8); + nby = roundup(nby, 16 * 8); + break; + default: + dev_warn(p->dev, "%s:%d invalid num pipes %d\n", + __func__, __LINE__, track->npipes); + return -EINVAL; + } + } + /* compute number of htile */ + nbx = nbx >> 3; + nby = nby >> 3; + /* size must be aligned on npipes * 2K boundary */ + size = roundup(nbx * nby * 4, track->npipes * (2 << 10)); + size += track->htile_offset; + + if (size > radeon_bo_size(track->htile_bo)) { + dev_warn(p->dev, "%s:%d htile surface too small %ld for %ld (%d %d)\n", + __func__, __LINE__, radeon_bo_size(track->htile_bo), + size, nbx, nby); + return -EINVAL; + } + return 0; +} + +static int evergreen_cs_track_validate_stencil(struct radeon_cs_parser *p) +{ + struct evergreen_cs_track *track = p->track; + struct eg_surface surf; + unsigned pitch, slice, mslice; + unsigned long offset; + int r; + + mslice = G_028008_SLICE_MAX(track->db_depth_view) + 1; + pitch = G_028058_PITCH_TILE_MAX(track->db_depth_size); + slice = track->db_depth_slice; + surf.nbx = (pitch + 1) * 8; + surf.nby = ((slice + 1) * 64) / surf.nbx; + surf.mode = G_028040_ARRAY_MODE(track->db_z_info); + surf.format = G_028044_FORMAT(track->db_s_info); + surf.tsplit = G_028044_TILE_SPLIT(track->db_s_info); + surf.nbanks = G_028040_NUM_BANKS(track->db_z_info); + surf.bankw = G_028040_BANK_WIDTH(track->db_z_info); + surf.bankh = G_028040_BANK_HEIGHT(track->db_z_info); + surf.mtilea = G_028040_MACRO_TILE_ASPECT(track->db_z_info); + surf.nsamples = 1; + + if (surf.format != 1) { + dev_warn(p->dev, "%s:%d stencil invalid format %d\n", + __func__, __LINE__, surf.format); + return -EINVAL; + } + /* replace by color format so we can use same code */ + surf.format = V_028C70_COLOR_8; + + r = evergreen_surface_value_conv_check(p, &surf, "stencil"); + if (r) { + return r; + } + + r = evergreen_surface_check(p, &surf, NULL); + if (r) { + /* old userspace doesn't compute proper depth/stencil alignment + * check that alignment against a bigger byte per elements and + * only report if that alignment is wrong too. + */ + surf.format = V_028C70_COLOR_8_8_8_8; + r = evergreen_surface_check(p, &surf, "stencil"); + if (r) { + dev_warn(p->dev, "%s:%d stencil invalid (0x%08x 0x%08x 0x%08x 0x%08x)\n", + __func__, __LINE__, track->db_depth_size, + track->db_depth_slice, track->db_s_info, track->db_z_info); + } + return r; + } + + offset = track->db_s_read_offset << 8; + if (offset & (surf.base_align - 1)) { + dev_warn(p->dev, "%s:%d stencil read bo base %ld not aligned with %ld\n", + __func__, __LINE__, offset, surf.base_align); + return -EINVAL; + } + offset += surf.layer_size * mslice; + if (offset > radeon_bo_size(track->db_s_read_bo)) { + dev_warn(p->dev, "%s:%d stencil read bo too small (layer size %d, " + "offset %ld, max layer %d, bo size %ld)\n", + __func__, __LINE__, surf.layer_size, + (unsigned long)track->db_s_read_offset << 8, mslice, + radeon_bo_size(track->db_s_read_bo)); + dev_warn(p->dev, "%s:%d stencil invalid (0x%08x 0x%08x 0x%08x 0x%08x)\n", + __func__, __LINE__, track->db_depth_size, + track->db_depth_slice, track->db_s_info, track->db_z_info); + return -EINVAL; + } + + offset = track->db_s_write_offset << 8; + if (offset & (surf.base_align - 1)) { + dev_warn(p->dev, "%s:%d stencil write bo base %ld not aligned with %ld\n", + __func__, __LINE__, offset, surf.base_align); + return -EINVAL; + } + offset += surf.layer_size * mslice; + if (offset > radeon_bo_size(track->db_s_write_bo)) { + dev_warn(p->dev, "%s:%d stencil write bo too small (layer size %d, " + "offset %ld, max layer %d, bo size %ld)\n", + __func__, __LINE__, surf.layer_size, + (unsigned long)track->db_s_write_offset << 8, mslice, + radeon_bo_size(track->db_s_write_bo)); + return -EINVAL; + } + + /* hyperz */ + if (G_028040_TILE_SURFACE_ENABLE(track->db_z_info)) { + r = evergreen_cs_track_validate_htile(p, surf.nbx, surf.nby); + if (r) { + return r; + } + } + + return 0; +} + +static int evergreen_cs_track_validate_depth(struct radeon_cs_parser *p) +{ + struct evergreen_cs_track *track = p->track; + struct eg_surface surf; + unsigned pitch, slice, mslice; + unsigned long offset; + int r; + + mslice = G_028008_SLICE_MAX(track->db_depth_view) + 1; + pitch = G_028058_PITCH_TILE_MAX(track->db_depth_size); + slice = track->db_depth_slice; + surf.nbx = (pitch + 1) * 8; + surf.nby = ((slice + 1) * 64) / surf.nbx; + surf.mode = G_028040_ARRAY_MODE(track->db_z_info); + surf.format = G_028040_FORMAT(track->db_z_info); + surf.tsplit = G_028040_TILE_SPLIT(track->db_z_info); + surf.nbanks = G_028040_NUM_BANKS(track->db_z_info); + surf.bankw = G_028040_BANK_WIDTH(track->db_z_info); + surf.bankh = G_028040_BANK_HEIGHT(track->db_z_info); + surf.mtilea = G_028040_MACRO_TILE_ASPECT(track->db_z_info); + surf.nsamples = 1; + + switch (surf.format) { + case V_028040_Z_16: + surf.format = V_028C70_COLOR_16; + break; + case V_028040_Z_24: + case V_028040_Z_32_FLOAT: + surf.format = V_028C70_COLOR_8_8_8_8; + break; + default: + dev_warn(p->dev, "%s:%d depth invalid format %d\n", + __func__, __LINE__, surf.format); + return -EINVAL; + } + + r = evergreen_surface_value_conv_check(p, &surf, "depth"); + if (r) { + dev_warn(p->dev, "%s:%d depth invalid (0x%08x 0x%08x 0x%08x)\n", + __func__, __LINE__, track->db_depth_size, + track->db_depth_slice, track->db_z_info); + return r; + } + + r = evergreen_surface_check(p, &surf, "depth"); + if (r) { + dev_warn(p->dev, "%s:%d depth invalid (0x%08x 0x%08x 0x%08x)\n", + __func__, __LINE__, track->db_depth_size, + track->db_depth_slice, track->db_z_info); + return r; + } + + offset = track->db_z_read_offset << 8; + if (offset & (surf.base_align - 1)) { + dev_warn(p->dev, "%s:%d stencil read bo base %ld not aligned with %ld\n", + __func__, __LINE__, offset, surf.base_align); + return -EINVAL; + } + offset += surf.layer_size * mslice; + if (offset > radeon_bo_size(track->db_z_read_bo)) { + dev_warn(p->dev, "%s:%d depth read bo too small (layer size %d, " + "offset %ld, max layer %d, bo size %ld)\n", + __func__, __LINE__, surf.layer_size, + (unsigned long)track->db_z_read_offset << 8, mslice, + radeon_bo_size(track->db_z_read_bo)); + return -EINVAL; + } + + offset = track->db_z_write_offset << 8; + if (offset & (surf.base_align - 1)) { + dev_warn(p->dev, "%s:%d stencil write bo base %ld not aligned with %ld\n", + __func__, __LINE__, offset, surf.base_align); + return -EINVAL; + } + offset += surf.layer_size * mslice; + if (offset > radeon_bo_size(track->db_z_write_bo)) { + dev_warn(p->dev, "%s:%d depth write bo too small (layer size %d, " + "offset %ld, max layer %d, bo size %ld)\n", + __func__, __LINE__, surf.layer_size, + (unsigned long)track->db_z_write_offset << 8, mslice, + radeon_bo_size(track->db_z_write_bo)); + return -EINVAL; + } + + /* hyperz */ + if (G_028040_TILE_SURFACE_ENABLE(track->db_z_info)) { + r = evergreen_cs_track_validate_htile(p, surf.nbx, surf.nby); + if (r) { + return r; + } + } + + return 0; +} + +static int evergreen_cs_track_validate_texture(struct radeon_cs_parser *p, + struct radeon_bo *texture, + struct radeon_bo *mipmap, + unsigned idx) +{ + struct eg_surface surf; + unsigned long toffset, moffset; + unsigned dim, llevel, mslice, width, height, depth, i; + u32 texdw[8]; + int r; + + texdw[0] = radeon_get_ib_value(p, idx + 0); + texdw[1] = radeon_get_ib_value(p, idx + 1); + texdw[2] = radeon_get_ib_value(p, idx + 2); + texdw[3] = radeon_get_ib_value(p, idx + 3); + texdw[4] = radeon_get_ib_value(p, idx + 4); + texdw[5] = radeon_get_ib_value(p, idx + 5); + texdw[6] = radeon_get_ib_value(p, idx + 6); + texdw[7] = radeon_get_ib_value(p, idx + 7); + dim = G_030000_DIM(texdw[0]); + llevel = G_030014_LAST_LEVEL(texdw[5]); + mslice = G_030014_LAST_ARRAY(texdw[5]) + 1; + width = G_030000_TEX_WIDTH(texdw[0]) + 1; + height = G_030004_TEX_HEIGHT(texdw[1]) + 1; + depth = G_030004_TEX_DEPTH(texdw[1]) + 1; + surf.format = G_03001C_DATA_FORMAT(texdw[7]); + surf.nbx = (G_030000_PITCH(texdw[0]) + 1) * 8; + surf.nbx = r600_fmt_get_nblocksx(surf.format, surf.nbx); + surf.nby = r600_fmt_get_nblocksy(surf.format, height); + surf.mode = G_030004_ARRAY_MODE(texdw[1]); + surf.tsplit = G_030018_TILE_SPLIT(texdw[6]); + surf.nbanks = G_03001C_NUM_BANKS(texdw[7]); + surf.bankw = G_03001C_BANK_WIDTH(texdw[7]); + surf.bankh = G_03001C_BANK_HEIGHT(texdw[7]); + surf.mtilea = G_03001C_MACRO_TILE_ASPECT(texdw[7]); + surf.nsamples = 1; + toffset = texdw[2] << 8; + moffset = texdw[3] << 8; + + if (!r600_fmt_is_valid_texture(surf.format, p->family)) { + dev_warn(p->dev, "%s:%d texture invalid format %d\n", + __func__, __LINE__, surf.format); + return -EINVAL; + } + switch (dim) { + case V_030000_SQ_TEX_DIM_1D: + case V_030000_SQ_TEX_DIM_2D: + case V_030000_SQ_TEX_DIM_CUBEMAP: + case V_030000_SQ_TEX_DIM_1D_ARRAY: + case V_030000_SQ_TEX_DIM_2D_ARRAY: + depth = 1; + break; + case V_030000_SQ_TEX_DIM_2D_MSAA: + case V_030000_SQ_TEX_DIM_2D_ARRAY_MSAA: + surf.nsamples = 1 << llevel; + llevel = 0; + depth = 1; + break; + case V_030000_SQ_TEX_DIM_3D: + break; + default: + dev_warn(p->dev, "%s:%d texture invalid dimension %d\n", + __func__, __LINE__, dim); + return -EINVAL; + } + + r = evergreen_surface_value_conv_check(p, &surf, "texture"); + if (r) { + return r; + } + + /* align height */ + evergreen_surface_check(p, &surf, NULL); + surf.nby = roundup(surf.nby, surf.halign); + + r = evergreen_surface_check(p, &surf, "texture"); + if (r) { + dev_warn(p->dev, "%s:%d texture invalid 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x\n", + __func__, __LINE__, texdw[0], texdw[1], texdw[4], + texdw[5], texdw[6], texdw[7]); + return r; + } + + /* check texture size */ + if (toffset & (surf.base_align - 1)) { + dev_warn(p->dev, "%s:%d texture bo base %ld not aligned with %ld\n", + __func__, __LINE__, toffset, surf.base_align); + return -EINVAL; + } + if (moffset & (surf.base_align - 1)) { + dev_warn(p->dev, "%s:%d mipmap bo base %ld not aligned with %ld\n", + __func__, __LINE__, moffset, surf.base_align); + return -EINVAL; + } + if (dim == SQ_TEX_DIM_3D) { + toffset += surf.layer_size * depth; + } else { + toffset += surf.layer_size * mslice; + } + if (toffset > radeon_bo_size(texture)) { + dev_warn(p->dev, "%s:%d texture bo too small (layer size %d, " + "offset %ld, max layer %d, depth %d, bo size %ld) (%d %d)\n", + __func__, __LINE__, surf.layer_size, + (unsigned long)texdw[2] << 8, mslice, + depth, radeon_bo_size(texture), + surf.nbx, surf.nby); + return -EINVAL; + } + + if (!mipmap) { + if (llevel) { + dev_warn(p->dev, "%s:%i got NULL MIP_ADDRESS relocation\n", + __func__, __LINE__); + return -EINVAL; + } else { + return 0; /* everything's ok */ + } + } + + /* check mipmap size */ + for (i = 1; i <= llevel; i++) { + unsigned w, h, d; + + w = r600_mip_minify(width, i); + h = r600_mip_minify(height, i); + d = r600_mip_minify(depth, i); + surf.nbx = r600_fmt_get_nblocksx(surf.format, w); + surf.nby = r600_fmt_get_nblocksy(surf.format, h); + + switch (surf.mode) { + case ARRAY_2D_TILED_THIN1: + if (surf.nbx < surf.palign || surf.nby < surf.halign) { + surf.mode = ARRAY_1D_TILED_THIN1; + } + /* recompute alignment */ + evergreen_surface_check(p, &surf, NULL); + break; + case ARRAY_LINEAR_GENERAL: + case ARRAY_LINEAR_ALIGNED: + case ARRAY_1D_TILED_THIN1: + break; + default: + dev_warn(p->dev, "%s:%d invalid array mode %d\n", + __func__, __LINE__, surf.mode); + return -EINVAL; + } + surf.nbx = roundup(surf.nbx, surf.palign); + surf.nby = roundup(surf.nby, surf.halign); + + r = evergreen_surface_check(p, &surf, "mipmap"); + if (r) { + return r; + } + + if (dim == SQ_TEX_DIM_3D) { + moffset += surf.layer_size * d; + } else { + moffset += surf.layer_size * mslice; + } + if (moffset > radeon_bo_size(mipmap)) { + dev_warn(p->dev, "%s:%d mipmap [%d] bo too small (layer size %d, " + "offset %ld, coffset %ld, max layer %d, depth %d, " + "bo size %ld) level0 (%d %d %d)\n", + __func__, __LINE__, i, surf.layer_size, + (unsigned long)texdw[3] << 8, moffset, mslice, + d, radeon_bo_size(mipmap), + width, height, depth); + dev_warn(p->dev, "%s:%d problematic surf: (%d %d) (%d %d %d %d %d %d %d)\n", + __func__, __LINE__, surf.nbx, surf.nby, + surf.mode, surf.bpe, surf.nsamples, + surf.bankw, surf.bankh, + surf.tsplit, surf.mtilea); + return -EINVAL; + } + } + + return 0; +} + +static int evergreen_cs_track_check(struct radeon_cs_parser *p) +{ + struct evergreen_cs_track *track = p->track; + unsigned tmp, i; + int r; + unsigned buffer_mask = 0; + + /* check streamout */ + if (track->streamout_dirty && track->vgt_strmout_config) { + for (i = 0; i < 4; i++) { + if (track->vgt_strmout_config & (1 << i)) { + buffer_mask |= (track->vgt_strmout_buffer_config >> (i * 4)) & 0xf; + } + } + + for (i = 0; i < 4; i++) { + if (buffer_mask & (1 << i)) { + if (track->vgt_strmout_bo[i]) { + u64 offset = (u64)track->vgt_strmout_bo_offset[i] + + (u64)track->vgt_strmout_size[i]; + if (offset > radeon_bo_size(track->vgt_strmout_bo[i])) { + DRM_ERROR("streamout %d bo too small: 0x%jx, 0x%lx\n", + i, (uintmax_t)offset, + radeon_bo_size(track->vgt_strmout_bo[i])); + return -EINVAL; + } + } else { + dev_warn(p->dev, "No buffer for streamout %d\n", i); + return -EINVAL; + } + } + } + track->streamout_dirty = false; + } + + if (track->sx_misc_kill_all_prims) + return 0; + + /* check that we have a cb for each enabled target + */ + if (track->cb_dirty) { + tmp = track->cb_target_mask; + for (i = 0; i < 8; i++) { + if ((tmp >> (i * 4)) & 0xF) { + /* at least one component is enabled */ + if (track->cb_color_bo[i] == NULL) { + dev_warn(p->dev, "%s:%d mask 0x%08X | 0x%08X no cb for %d\n", + __func__, __LINE__, track->cb_target_mask, track->cb_shader_mask, i); + return -EINVAL; + } + /* check cb */ + r = evergreen_cs_track_validate_cb(p, i); + if (r) { + return r; + } + } + } + track->cb_dirty = false; + } + + if (track->db_dirty) { + /* Check stencil buffer */ + if (G_028044_FORMAT(track->db_s_info) != V_028044_STENCIL_INVALID && + G_028800_STENCIL_ENABLE(track->db_depth_control)) { + r = evergreen_cs_track_validate_stencil(p); + if (r) + return r; + } + /* Check depth buffer */ + if (G_028040_FORMAT(track->db_z_info) != V_028040_Z_INVALID && + G_028800_Z_ENABLE(track->db_depth_control)) { + r = evergreen_cs_track_validate_depth(p); + if (r) + return r; + } + track->db_dirty = false; + } + + return 0; +} + +/** + * evergreen_cs_packet_parse() - parse cp packet and point ib index to next packet + * @parser: parser structure holding parsing context. + * @pkt: where to store packet informations + * + * Assume that chunk_ib_index is properly set. Will return -EINVAL + * if packet is bigger than remaining ib size. or if packets is unknown. + **/ +static int evergreen_cs_packet_parse(struct radeon_cs_parser *p, + struct radeon_cs_packet *pkt, + unsigned idx) +{ + struct radeon_cs_chunk *ib_chunk = &p->chunks[p->chunk_ib_idx]; + uint32_t header; + + if (idx >= ib_chunk->length_dw) { + DRM_ERROR("Can not parse packet at %d after CS end %d !\n", + idx, ib_chunk->length_dw); + return -EINVAL; + } + header = radeon_get_ib_value(p, idx); + pkt->idx = idx; + pkt->type = CP_PACKET_GET_TYPE(header); + pkt->count = CP_PACKET_GET_COUNT(header); + pkt->one_reg_wr = 0; + switch (pkt->type) { + case PACKET_TYPE0: + pkt->reg = CP_PACKET0_GET_REG(header); + break; + case PACKET_TYPE3: + pkt->opcode = CP_PACKET3_GET_OPCODE(header); + break; + case PACKET_TYPE2: + pkt->count = -1; + break; + default: + DRM_ERROR("Unknown packet type %d at %d !\n", pkt->type, idx); + return -EINVAL; + } + if ((pkt->count + 1 + pkt->idx) >= ib_chunk->length_dw) { + DRM_ERROR("Packet (%d:%d:%d) end after CS buffer (%d) !\n", + pkt->idx, pkt->type, pkt->count, ib_chunk->length_dw); + return -EINVAL; + } + return 0; +} + +/** + * evergreen_cs_packet_next_reloc() - parse next packet which should be reloc packet3 + * @parser: parser structure holding parsing context. + * @data: pointer to relocation data + * @offset_start: starting offset + * @offset_mask: offset mask (to align start offset on) + * @reloc: reloc informations + * + * Check next packet is relocation packet3, do bo validation and compute + * GPU offset using the provided start. + **/ +static int evergreen_cs_packet_next_reloc(struct radeon_cs_parser *p, + struct radeon_cs_reloc **cs_reloc) +{ + struct radeon_cs_chunk *relocs_chunk; + struct radeon_cs_packet p3reloc; + unsigned idx; + int r; + + if (p->chunk_relocs_idx == -1) { + DRM_ERROR("No relocation chunk !\n"); + return -EINVAL; + } + *cs_reloc = NULL; + relocs_chunk = &p->chunks[p->chunk_relocs_idx]; + r = evergreen_cs_packet_parse(p, &p3reloc, p->idx); + if (r) { + return r; + } + p->idx += p3reloc.count + 2; + if (p3reloc.type != PACKET_TYPE3 || p3reloc.opcode != PACKET3_NOP) { + DRM_ERROR("No packet3 for relocation for packet at %d.\n", + p3reloc.idx); + return -EINVAL; + } + idx = radeon_get_ib_value(p, p3reloc.idx + 1); + if (idx >= relocs_chunk->length_dw) { + DRM_ERROR("Relocs at %d after relocations chunk end %d !\n", + idx, relocs_chunk->length_dw); + return -EINVAL; + } + /* FIXME: we assume reloc size is 4 dwords */ + *cs_reloc = p->relocs_ptr[(idx / 4)]; + return 0; +} + +/** + * evergreen_cs_packet_next_is_pkt3_nop() - test if the next packet is NOP + * @p: structure holding the parser context. + * + * Check if the next packet is a relocation packet3. + **/ +static bool evergreen_cs_packet_next_is_pkt3_nop(struct radeon_cs_parser *p) +{ + struct radeon_cs_packet p3reloc; + int r; + + r = evergreen_cs_packet_parse(p, &p3reloc, p->idx); + if (r) { + return false; + } + if (p3reloc.type != PACKET_TYPE3 || p3reloc.opcode != PACKET3_NOP) { + return false; + } + return true; +} + +/** + * evergreen_cs_packet_next_vline() - parse userspace VLINE packet + * @parser: parser structure holding parsing context. + * + * Userspace sends a special sequence for VLINE waits. + * PACKET0 - VLINE_START_END + value + * PACKET3 - WAIT_REG_MEM poll vline status reg + * RELOC (P3) - crtc_id in reloc. + * + * This function parses this and relocates the VLINE START END + * and WAIT_REG_MEM packets to the correct crtc. + * It also detects a switched off crtc and nulls out the + * wait in that case. + */ +static int evergreen_cs_packet_parse_vline(struct radeon_cs_parser *p) +{ + struct drm_mode_object *obj; + struct drm_crtc *crtc; + struct radeon_crtc *radeon_crtc; + struct radeon_cs_packet p3reloc, wait_reg_mem; + int crtc_id; + int r; + uint32_t header, h_idx, reg, wait_reg_mem_info; + volatile uint32_t *ib; + + ib = p->ib.ptr; + + /* parse the WAIT_REG_MEM */ + r = evergreen_cs_packet_parse(p, &wait_reg_mem, p->idx); + if (r) + return r; + + /* check its a WAIT_REG_MEM */ + if (wait_reg_mem.type != PACKET_TYPE3 || + wait_reg_mem.opcode != PACKET3_WAIT_REG_MEM) { + DRM_ERROR("vline wait missing WAIT_REG_MEM segment\n"); + return -EINVAL; + } + + wait_reg_mem_info = radeon_get_ib_value(p, wait_reg_mem.idx + 1); + /* bit 4 is reg (0) or mem (1) */ + if (wait_reg_mem_info & 0x10) { + DRM_ERROR("vline WAIT_REG_MEM waiting on MEM rather than REG\n"); + return -EINVAL; + } + /* waiting for value to be equal */ + if ((wait_reg_mem_info & 0x7) != 0x3) { + DRM_ERROR("vline WAIT_REG_MEM function not equal\n"); + return -EINVAL; + } + if ((radeon_get_ib_value(p, wait_reg_mem.idx + 2) << 2) != EVERGREEN_VLINE_STATUS) { + DRM_ERROR("vline WAIT_REG_MEM bad reg\n"); + return -EINVAL; + } + + if (radeon_get_ib_value(p, wait_reg_mem.idx + 5) != EVERGREEN_VLINE_STAT) { + DRM_ERROR("vline WAIT_REG_MEM bad bit mask\n"); + return -EINVAL; + } + + /* jump over the NOP */ + r = evergreen_cs_packet_parse(p, &p3reloc, p->idx + wait_reg_mem.count + 2); + if (r) + return r; + + h_idx = p->idx - 2; + p->idx += wait_reg_mem.count + 2; + p->idx += p3reloc.count + 2; + + header = radeon_get_ib_value(p, h_idx); + crtc_id = radeon_get_ib_value(p, h_idx + 2 + 7 + 1); + reg = CP_PACKET0_GET_REG(header); + obj = drm_mode_object_find(p->rdev->ddev, crtc_id, DRM_MODE_OBJECT_CRTC); + if (!obj) { + DRM_ERROR("cannot find crtc %d\n", crtc_id); + return -EINVAL; + } + crtc = obj_to_crtc(obj); + radeon_crtc = to_radeon_crtc(crtc); + crtc_id = radeon_crtc->crtc_id; + + if (!crtc->enabled) { + /* if the CRTC isn't enabled - we need to nop out the WAIT_REG_MEM */ + ib[h_idx + 2] = PACKET2(0); + ib[h_idx + 3] = PACKET2(0); + ib[h_idx + 4] = PACKET2(0); + ib[h_idx + 5] = PACKET2(0); + ib[h_idx + 6] = PACKET2(0); + ib[h_idx + 7] = PACKET2(0); + ib[h_idx + 8] = PACKET2(0); + } else { + switch (reg) { + case EVERGREEN_VLINE_START_END: + header &= ~R600_CP_PACKET0_REG_MASK; + header |= (EVERGREEN_VLINE_START_END + radeon_crtc->crtc_offset) >> 2; + ib[h_idx] = header; + ib[h_idx + 4] = (EVERGREEN_VLINE_STATUS + radeon_crtc->crtc_offset) >> 2; + break; + default: + DRM_ERROR("unknown crtc reloc\n"); + return -EINVAL; + } + } + return 0; +} + +static int evergreen_packet0_check(struct radeon_cs_parser *p, + struct radeon_cs_packet *pkt, + unsigned idx, unsigned reg) +{ + int r; + + switch (reg) { + case EVERGREEN_VLINE_START_END: + r = evergreen_cs_packet_parse_vline(p); + if (r) { + DRM_ERROR("No reloc for ib[%d]=0x%04X\n", + idx, reg); + return r; + } + break; + default: + DRM_ERROR("Forbidden register 0x%04X in cs at %d\n", + reg, idx); + return -EINVAL; + } + return 0; +} + +static int evergreen_cs_parse_packet0(struct radeon_cs_parser *p, + struct radeon_cs_packet *pkt) +{ + unsigned reg, i; + unsigned idx; + int r; + + idx = pkt->idx + 1; + reg = pkt->reg; + for (i = 0; i <= pkt->count; i++, idx++, reg += 4) { + r = evergreen_packet0_check(p, pkt, idx, reg); + if (r) { + return r; + } + } + return 0; +} + +/** + * evergreen_cs_check_reg() - check if register is authorized or not + * @parser: parser structure holding parsing context + * @reg: register we are testing + * @idx: index into the cs buffer + * + * This function will test against evergreen_reg_safe_bm and return 0 + * if register is safe. If register is not flag as safe this function + * will test it against a list of register needind special handling. + */ +static int evergreen_cs_check_reg(struct radeon_cs_parser *p, u32 reg, u32 idx) +{ + struct evergreen_cs_track *track = (struct evergreen_cs_track *)p->track; + struct radeon_cs_reloc *reloc; + u32 last_reg; + u32 m, i, tmp, *ib; + int r; + + if (p->rdev->family >= CHIP_CAYMAN) + last_reg = DRM_ARRAY_SIZE(cayman_reg_safe_bm); + else + last_reg = DRM_ARRAY_SIZE(evergreen_reg_safe_bm); + + i = (reg >> 7); + if (i >= last_reg) { + dev_warn(p->dev, "forbidden register 0x%08x at %d\n", reg, idx); + return -EINVAL; + } + m = 1 << ((reg >> 2) & 31); + if (p->rdev->family >= CHIP_CAYMAN) { + if (!(cayman_reg_safe_bm[i] & m)) + return 0; + } else { + if (!(evergreen_reg_safe_bm[i] & m)) + return 0; + } + ib = p->ib.ptr; + switch (reg) { + /* force following reg to 0 in an attempt to disable out buffer + * which will need us to better understand how it works to perform + * security check on it (Jerome) + */ + case SQ_ESGS_RING_SIZE: + case SQ_GSVS_RING_SIZE: + case SQ_ESTMP_RING_SIZE: + case SQ_GSTMP_RING_SIZE: + case SQ_HSTMP_RING_SIZE: + case SQ_LSTMP_RING_SIZE: + case SQ_PSTMP_RING_SIZE: + case SQ_VSTMP_RING_SIZE: + case SQ_ESGS_RING_ITEMSIZE: + case SQ_ESTMP_RING_ITEMSIZE: + case SQ_GSTMP_RING_ITEMSIZE: + case SQ_GSVS_RING_ITEMSIZE: + case SQ_GS_VERT_ITEMSIZE: + case SQ_GS_VERT_ITEMSIZE_1: + case SQ_GS_VERT_ITEMSIZE_2: + case SQ_GS_VERT_ITEMSIZE_3: + case SQ_GSVS_RING_OFFSET_1: + case SQ_GSVS_RING_OFFSET_2: + case SQ_GSVS_RING_OFFSET_3: + case SQ_HSTMP_RING_ITEMSIZE: + case SQ_LSTMP_RING_ITEMSIZE: + case SQ_PSTMP_RING_ITEMSIZE: + case SQ_VSTMP_RING_ITEMSIZE: + case VGT_TF_RING_SIZE: + /* get value to populate the IB don't remove */ + /*tmp =radeon_get_ib_value(p, idx); + ib[idx] = 0;*/ + break; + case SQ_ESGS_RING_BASE: + case SQ_GSVS_RING_BASE: + case SQ_ESTMP_RING_BASE: + case SQ_GSTMP_RING_BASE: + case SQ_HSTMP_RING_BASE: + case SQ_LSTMP_RING_BASE: + case SQ_PSTMP_RING_BASE: + case SQ_VSTMP_RING_BASE: + r = evergreen_cs_packet_next_reloc(p, &reloc); + if (r) { + dev_warn(p->dev, "bad SET_CONTEXT_REG " + "0x%04X\n", reg); + return -EINVAL; + } + ib[idx] += (u32)((reloc->lobj.gpu_offset >> 8) & 0xffffffff); + break; + case DB_DEPTH_CONTROL: + track->db_depth_control = radeon_get_ib_value(p, idx); + track->db_dirty = true; + break; + case CAYMAN_DB_EQAA: + if (p->rdev->family < CHIP_CAYMAN) { + dev_warn(p->dev, "bad SET_CONTEXT_REG " + "0x%04X\n", reg); + return -EINVAL; + } + break; + case CAYMAN_DB_DEPTH_INFO: + if (p->rdev->family < CHIP_CAYMAN) { + dev_warn(p->dev, "bad SET_CONTEXT_REG " + "0x%04X\n", reg); + return -EINVAL; + } + break; + case DB_Z_INFO: + track->db_z_info = radeon_get_ib_value(p, idx); + if (!(p->cs_flags & RADEON_CS_KEEP_TILING_FLAGS)) { + r = evergreen_cs_packet_next_reloc(p, &reloc); + if (r) { + dev_warn(p->dev, "bad SET_CONTEXT_REG " + "0x%04X\n", reg); + return -EINVAL; + } + ib[idx] &= ~Z_ARRAY_MODE(0xf); + track->db_z_info &= ~Z_ARRAY_MODE(0xf); + ib[idx] |= Z_ARRAY_MODE(evergreen_cs_get_aray_mode(reloc->lobj.tiling_flags)); + track->db_z_info |= Z_ARRAY_MODE(evergreen_cs_get_aray_mode(reloc->lobj.tiling_flags)); + if (reloc->lobj.tiling_flags & RADEON_TILING_MACRO) { + unsigned bankw, bankh, mtaspect, tile_split; + + evergreen_tiling_fields(reloc->lobj.tiling_flags, + &bankw, &bankh, &mtaspect, + &tile_split); + ib[idx] |= DB_NUM_BANKS(evergreen_cs_get_num_banks(track->nbanks)); + ib[idx] |= DB_TILE_SPLIT(tile_split) | + DB_BANK_WIDTH(bankw) | + DB_BANK_HEIGHT(bankh) | + DB_MACRO_TILE_ASPECT(mtaspect); + } + } + track->db_dirty = true; + break; + case DB_STENCIL_INFO: + track->db_s_info = radeon_get_ib_value(p, idx); + track->db_dirty = true; + break; + case DB_DEPTH_VIEW: + track->db_depth_view = radeon_get_ib_value(p, idx); + track->db_dirty = true; + break; + case DB_DEPTH_SIZE: + track->db_depth_size = radeon_get_ib_value(p, idx); + track->db_dirty = true; + break; + case R_02805C_DB_DEPTH_SLICE: + track->db_depth_slice = radeon_get_ib_value(p, idx); + track->db_dirty = true; + break; + case DB_Z_READ_BASE: + r = evergreen_cs_packet_next_reloc(p, &reloc); + if (r) { + dev_warn(p->dev, "bad SET_CONTEXT_REG " + "0x%04X\n", reg); + return -EINVAL; + } + track->db_z_read_offset = radeon_get_ib_value(p, idx); + ib[idx] += (u32)((reloc->lobj.gpu_offset >> 8) & 0xffffffff); + track->db_z_read_bo = reloc->robj; + track->db_dirty = true; + break; + case DB_Z_WRITE_BASE: + r = evergreen_cs_packet_next_reloc(p, &reloc); + if (r) { + dev_warn(p->dev, "bad SET_CONTEXT_REG " + "0x%04X\n", reg); + return -EINVAL; + } + track->db_z_write_offset = radeon_get_ib_value(p, idx); + ib[idx] += (u32)((reloc->lobj.gpu_offset >> 8) & 0xffffffff); + track->db_z_write_bo = reloc->robj; + track->db_dirty = true; + break; + case DB_STENCIL_READ_BASE: + r = evergreen_cs_packet_next_reloc(p, &reloc); + if (r) { + dev_warn(p->dev, "bad SET_CONTEXT_REG " + "0x%04X\n", reg); + return -EINVAL; + } + track->db_s_read_offset = radeon_get_ib_value(p, idx); + ib[idx] += (u32)((reloc->lobj.gpu_offset >> 8) & 0xffffffff); + track->db_s_read_bo = reloc->robj; + track->db_dirty = true; + break; + case DB_STENCIL_WRITE_BASE: + r = evergreen_cs_packet_next_reloc(p, &reloc); + if (r) { + dev_warn(p->dev, "bad SET_CONTEXT_REG " + "0x%04X\n", reg); + return -EINVAL; + } + track->db_s_write_offset = radeon_get_ib_value(p, idx); + ib[idx] += (u32)((reloc->lobj.gpu_offset >> 8) & 0xffffffff); + track->db_s_write_bo = reloc->robj; + track->db_dirty = true; + break; + case VGT_STRMOUT_CONFIG: + track->vgt_strmout_config = radeon_get_ib_value(p, idx); + track->streamout_dirty = true; + break; + case VGT_STRMOUT_BUFFER_CONFIG: + track->vgt_strmout_buffer_config = radeon_get_ib_value(p, idx); + track->streamout_dirty = true; + break; + case VGT_STRMOUT_BUFFER_BASE_0: + case VGT_STRMOUT_BUFFER_BASE_1: + case VGT_STRMOUT_BUFFER_BASE_2: + case VGT_STRMOUT_BUFFER_BASE_3: + r = evergreen_cs_packet_next_reloc(p, &reloc); + if (r) { + dev_warn(p->dev, "bad SET_CONTEXT_REG " + "0x%04X\n", reg); + return -EINVAL; + } + tmp = (reg - VGT_STRMOUT_BUFFER_BASE_0) / 16; + track->vgt_strmout_bo_offset[tmp] = radeon_get_ib_value(p, idx) << 8; + ib[idx] += (u32)((reloc->lobj.gpu_offset >> 8) & 0xffffffff); + track->vgt_strmout_bo[tmp] = reloc->robj; + track->streamout_dirty = true; + break; + case VGT_STRMOUT_BUFFER_SIZE_0: + case VGT_STRMOUT_BUFFER_SIZE_1: + case VGT_STRMOUT_BUFFER_SIZE_2: + case VGT_STRMOUT_BUFFER_SIZE_3: + tmp = (reg - VGT_STRMOUT_BUFFER_SIZE_0) / 16; + /* size in register is DWs, convert to bytes */ + track->vgt_strmout_size[tmp] = radeon_get_ib_value(p, idx) * 4; + track->streamout_dirty = true; + break; + case CP_COHER_BASE: + r = evergreen_cs_packet_next_reloc(p, &reloc); + if (r) { + dev_warn(p->dev, "missing reloc for CP_COHER_BASE " + "0x%04X\n", reg); + return -EINVAL; + } + ib[idx] += (u32)((reloc->lobj.gpu_offset >> 8) & 0xffffffff); + case CB_TARGET_MASK: + track->cb_target_mask = radeon_get_ib_value(p, idx); + track->cb_dirty = true; + break; + case CB_SHADER_MASK: + track->cb_shader_mask = radeon_get_ib_value(p, idx); + track->cb_dirty = true; + break; + case PA_SC_AA_CONFIG: + if (p->rdev->family >= CHIP_CAYMAN) { + dev_warn(p->dev, "bad SET_CONTEXT_REG " + "0x%04X\n", reg); + return -EINVAL; + } + tmp = radeon_get_ib_value(p, idx) & MSAA_NUM_SAMPLES_MASK; + track->nsamples = 1 << tmp; + break; + case CAYMAN_PA_SC_AA_CONFIG: + if (p->rdev->family < CHIP_CAYMAN) { + dev_warn(p->dev, "bad SET_CONTEXT_REG " + "0x%04X\n", reg); + return -EINVAL; + } + tmp = radeon_get_ib_value(p, idx) & CAYMAN_MSAA_NUM_SAMPLES_MASK; + track->nsamples = 1 << tmp; + break; + case CB_COLOR0_VIEW: + case CB_COLOR1_VIEW: + case CB_COLOR2_VIEW: + case CB_COLOR3_VIEW: + case CB_COLOR4_VIEW: + case CB_COLOR5_VIEW: + case CB_COLOR6_VIEW: + case CB_COLOR7_VIEW: + tmp = (reg - CB_COLOR0_VIEW) / 0x3c; + track->cb_color_view[tmp] = radeon_get_ib_value(p, idx); + track->cb_dirty = true; + break; + case CB_COLOR8_VIEW: + case CB_COLOR9_VIEW: + case CB_COLOR10_VIEW: + case CB_COLOR11_VIEW: + tmp = ((reg - CB_COLOR8_VIEW) / 0x1c) + 8; + track->cb_color_view[tmp] = radeon_get_ib_value(p, idx); + track->cb_dirty = true; + break; + case CB_COLOR0_INFO: + case CB_COLOR1_INFO: + case CB_COLOR2_INFO: + case CB_COLOR3_INFO: + case CB_COLOR4_INFO: + case CB_COLOR5_INFO: + case CB_COLOR6_INFO: + case CB_COLOR7_INFO: + tmp = (reg - CB_COLOR0_INFO) / 0x3c; + track->cb_color_info[tmp] = radeon_get_ib_value(p, idx); + if (!(p->cs_flags & RADEON_CS_KEEP_TILING_FLAGS)) { + r = evergreen_cs_packet_next_reloc(p, &reloc); + if (r) { + dev_warn(p->dev, "bad SET_CONTEXT_REG " + "0x%04X\n", reg); + return -EINVAL; + } + ib[idx] |= CB_ARRAY_MODE(evergreen_cs_get_aray_mode(reloc->lobj.tiling_flags)); + track->cb_color_info[tmp] |= CB_ARRAY_MODE(evergreen_cs_get_aray_mode(reloc->lobj.tiling_flags)); + } + track->cb_dirty = true; + break; + case CB_COLOR8_INFO: + case CB_COLOR9_INFO: + case CB_COLOR10_INFO: + case CB_COLOR11_INFO: + tmp = ((reg - CB_COLOR8_INFO) / 0x1c) + 8; + track->cb_color_info[tmp] = radeon_get_ib_value(p, idx); + if (!(p->cs_flags & RADEON_CS_KEEP_TILING_FLAGS)) { + r = evergreen_cs_packet_next_reloc(p, &reloc); + if (r) { + dev_warn(p->dev, "bad SET_CONTEXT_REG " + "0x%04X\n", reg); + return -EINVAL; + } + ib[idx] |= CB_ARRAY_MODE(evergreen_cs_get_aray_mode(reloc->lobj.tiling_flags)); + track->cb_color_info[tmp] |= CB_ARRAY_MODE(evergreen_cs_get_aray_mode(reloc->lobj.tiling_flags)); + } + track->cb_dirty = true; + break; + case CB_COLOR0_PITCH: + case CB_COLOR1_PITCH: + case CB_COLOR2_PITCH: + case CB_COLOR3_PITCH: + case CB_COLOR4_PITCH: + case CB_COLOR5_PITCH: + case CB_COLOR6_PITCH: + case CB_COLOR7_PITCH: + tmp = (reg - CB_COLOR0_PITCH) / 0x3c; + track->cb_color_pitch[tmp] = radeon_get_ib_value(p, idx); + track->cb_dirty = true; + break; + case CB_COLOR8_PITCH: + case CB_COLOR9_PITCH: + case CB_COLOR10_PITCH: + case CB_COLOR11_PITCH: + tmp = ((reg - CB_COLOR8_PITCH) / 0x1c) + 8; + track->cb_color_pitch[tmp] = radeon_get_ib_value(p, idx); + track->cb_dirty = true; + break; + case CB_COLOR0_SLICE: + case CB_COLOR1_SLICE: + case CB_COLOR2_SLICE: + case CB_COLOR3_SLICE: + case CB_COLOR4_SLICE: + case CB_COLOR5_SLICE: + case CB_COLOR6_SLICE: + case CB_COLOR7_SLICE: + tmp = (reg - CB_COLOR0_SLICE) / 0x3c; + track->cb_color_slice[tmp] = radeon_get_ib_value(p, idx); + track->cb_color_slice_idx[tmp] = idx; + track->cb_dirty = true; + break; + case CB_COLOR8_SLICE: + case CB_COLOR9_SLICE: + case CB_COLOR10_SLICE: + case CB_COLOR11_SLICE: + tmp = ((reg - CB_COLOR8_SLICE) / 0x1c) + 8; + track->cb_color_slice[tmp] = radeon_get_ib_value(p, idx); + track->cb_color_slice_idx[tmp] = idx; + track->cb_dirty = true; + break; + case CB_COLOR0_ATTRIB: + case CB_COLOR1_ATTRIB: + case CB_COLOR2_ATTRIB: + case CB_COLOR3_ATTRIB: + case CB_COLOR4_ATTRIB: + case CB_COLOR5_ATTRIB: + case CB_COLOR6_ATTRIB: + case CB_COLOR7_ATTRIB: + r = evergreen_cs_packet_next_reloc(p, &reloc); + if (r) { + dev_warn(p->dev, "bad SET_CONTEXT_REG " + "0x%04X\n", reg); + return -EINVAL; + } + if (!(p->cs_flags & RADEON_CS_KEEP_TILING_FLAGS)) { + if (reloc->lobj.tiling_flags & RADEON_TILING_MACRO) { + unsigned bankw, bankh, mtaspect, tile_split; + + evergreen_tiling_fields(reloc->lobj.tiling_flags, + &bankw, &bankh, &mtaspect, + &tile_split); + ib[idx] |= CB_NUM_BANKS(evergreen_cs_get_num_banks(track->nbanks)); + ib[idx] |= CB_TILE_SPLIT(tile_split) | + CB_BANK_WIDTH(bankw) | + CB_BANK_HEIGHT(bankh) | + CB_MACRO_TILE_ASPECT(mtaspect); + } + } + tmp = ((reg - CB_COLOR0_ATTRIB) / 0x3c); + track->cb_color_attrib[tmp] = ib[idx]; + track->cb_dirty = true; + break; + case CB_COLOR8_ATTRIB: + case CB_COLOR9_ATTRIB: + case CB_COLOR10_ATTRIB: + case CB_COLOR11_ATTRIB: + r = evergreen_cs_packet_next_reloc(p, &reloc); + if (r) { + dev_warn(p->dev, "bad SET_CONTEXT_REG " + "0x%04X\n", reg); + return -EINVAL; + } + if (!(p->cs_flags & RADEON_CS_KEEP_TILING_FLAGS)) { + if (reloc->lobj.tiling_flags & RADEON_TILING_MACRO) { + unsigned bankw, bankh, mtaspect, tile_split; + + evergreen_tiling_fields(reloc->lobj.tiling_flags, + &bankw, &bankh, &mtaspect, + &tile_split); + ib[idx] |= CB_NUM_BANKS(evergreen_cs_get_num_banks(track->nbanks)); + ib[idx] |= CB_TILE_SPLIT(tile_split) | + CB_BANK_WIDTH(bankw) | + CB_BANK_HEIGHT(bankh) | + CB_MACRO_TILE_ASPECT(mtaspect); + } + } + tmp = ((reg - CB_COLOR8_ATTRIB) / 0x1c) + 8; + track->cb_color_attrib[tmp] = ib[idx]; + track->cb_dirty = true; + break; + case CB_COLOR0_FMASK: + case CB_COLOR1_FMASK: + case CB_COLOR2_FMASK: + case CB_COLOR3_FMASK: + case CB_COLOR4_FMASK: + case CB_COLOR5_FMASK: + case CB_COLOR6_FMASK: + case CB_COLOR7_FMASK: + tmp = (reg - CB_COLOR0_FMASK) / 0x3c; + r = evergreen_cs_packet_next_reloc(p, &reloc); + if (r) { + dev_err(p->dev, "bad SET_CONTEXT_REG 0x%04X\n", reg); + return -EINVAL; + } + ib[idx] += (u32)((reloc->lobj.gpu_offset >> 8) & 0xffffffff); + track->cb_color_fmask_bo[tmp] = reloc->robj; + break; + case CB_COLOR0_CMASK: + case CB_COLOR1_CMASK: + case CB_COLOR2_CMASK: + case CB_COLOR3_CMASK: + case CB_COLOR4_CMASK: + case CB_COLOR5_CMASK: + case CB_COLOR6_CMASK: + case CB_COLOR7_CMASK: + tmp = (reg - CB_COLOR0_CMASK) / 0x3c; + r = evergreen_cs_packet_next_reloc(p, &reloc); + if (r) { + dev_err(p->dev, "bad SET_CONTEXT_REG 0x%04X\n", reg); + return -EINVAL; + } + ib[idx] += (u32)((reloc->lobj.gpu_offset >> 8) & 0xffffffff); + track->cb_color_cmask_bo[tmp] = reloc->robj; + break; + case CB_COLOR0_FMASK_SLICE: + case CB_COLOR1_FMASK_SLICE: + case CB_COLOR2_FMASK_SLICE: + case CB_COLOR3_FMASK_SLICE: + case CB_COLOR4_FMASK_SLICE: + case CB_COLOR5_FMASK_SLICE: + case CB_COLOR6_FMASK_SLICE: + case CB_COLOR7_FMASK_SLICE: + tmp = (reg - CB_COLOR0_FMASK_SLICE) / 0x3c; + track->cb_color_fmask_slice[tmp] = radeon_get_ib_value(p, idx); + break; + case CB_COLOR0_CMASK_SLICE: + case CB_COLOR1_CMASK_SLICE: + case CB_COLOR2_CMASK_SLICE: + case CB_COLOR3_CMASK_SLICE: + case CB_COLOR4_CMASK_SLICE: + case CB_COLOR5_CMASK_SLICE: + case CB_COLOR6_CMASK_SLICE: + case CB_COLOR7_CMASK_SLICE: + tmp = (reg - CB_COLOR0_CMASK_SLICE) / 0x3c; + track->cb_color_cmask_slice[tmp] = radeon_get_ib_value(p, idx); + break; + case CB_COLOR0_BASE: + case CB_COLOR1_BASE: + case CB_COLOR2_BASE: + case CB_COLOR3_BASE: + case CB_COLOR4_BASE: + case CB_COLOR5_BASE: + case CB_COLOR6_BASE: + case CB_COLOR7_BASE: + r = evergreen_cs_packet_next_reloc(p, &reloc); + if (r) { + dev_warn(p->dev, "bad SET_CONTEXT_REG " + "0x%04X\n", reg); + return -EINVAL; + } + tmp = (reg - CB_COLOR0_BASE) / 0x3c; + track->cb_color_bo_offset[tmp] = radeon_get_ib_value(p, idx); + ib[idx] += (u32)((reloc->lobj.gpu_offset >> 8) & 0xffffffff); + track->cb_color_bo[tmp] = reloc->robj; + track->cb_dirty = true; + break; + case CB_COLOR8_BASE: + case CB_COLOR9_BASE: + case CB_COLOR10_BASE: + case CB_COLOR11_BASE: + r = evergreen_cs_packet_next_reloc(p, &reloc); + if (r) { + dev_warn(p->dev, "bad SET_CONTEXT_REG " + "0x%04X\n", reg); + return -EINVAL; + } + tmp = ((reg - CB_COLOR8_BASE) / 0x1c) + 8; + track->cb_color_bo_offset[tmp] = radeon_get_ib_value(p, idx); + ib[idx] += (u32)((reloc->lobj.gpu_offset >> 8) & 0xffffffff); + track->cb_color_bo[tmp] = reloc->robj; + track->cb_dirty = true; + break; + case DB_HTILE_DATA_BASE: + r = evergreen_cs_packet_next_reloc(p, &reloc); + if (r) { + dev_warn(p->dev, "bad SET_CONTEXT_REG " + "0x%04X\n", reg); + return -EINVAL; + } + track->htile_offset = radeon_get_ib_value(p, idx); + ib[idx] += (u32)((reloc->lobj.gpu_offset >> 8) & 0xffffffff); + track->htile_bo = reloc->robj; + track->db_dirty = true; + break; + case DB_HTILE_SURFACE: + /* 8x8 only */ + track->htile_surface = radeon_get_ib_value(p, idx); + /* force 8x8 htile width and height */ + ib[idx] |= 3; + track->db_dirty = true; + break; + case CB_IMMED0_BASE: + case CB_IMMED1_BASE: + case CB_IMMED2_BASE: + case CB_IMMED3_BASE: + case CB_IMMED4_BASE: + case CB_IMMED5_BASE: + case CB_IMMED6_BASE: + case CB_IMMED7_BASE: + case CB_IMMED8_BASE: + case CB_IMMED9_BASE: + case CB_IMMED10_BASE: + case CB_IMMED11_BASE: + case SQ_PGM_START_FS: + case SQ_PGM_START_ES: + case SQ_PGM_START_VS: + case SQ_PGM_START_GS: + case SQ_PGM_START_PS: + case SQ_PGM_START_HS: + case SQ_PGM_START_LS: + case SQ_CONST_MEM_BASE: + case SQ_ALU_CONST_CACHE_GS_0: + case SQ_ALU_CONST_CACHE_GS_1: + case SQ_ALU_CONST_CACHE_GS_2: + case SQ_ALU_CONST_CACHE_GS_3: + case SQ_ALU_CONST_CACHE_GS_4: + case SQ_ALU_CONST_CACHE_GS_5: + case SQ_ALU_CONST_CACHE_GS_6: + case SQ_ALU_CONST_CACHE_GS_7: + case SQ_ALU_CONST_CACHE_GS_8: + case SQ_ALU_CONST_CACHE_GS_9: + case SQ_ALU_CONST_CACHE_GS_10: + case SQ_ALU_CONST_CACHE_GS_11: + case SQ_ALU_CONST_CACHE_GS_12: + case SQ_ALU_CONST_CACHE_GS_13: + case SQ_ALU_CONST_CACHE_GS_14: + case SQ_ALU_CONST_CACHE_GS_15: + case SQ_ALU_CONST_CACHE_PS_0: + case SQ_ALU_CONST_CACHE_PS_1: + case SQ_ALU_CONST_CACHE_PS_2: + case SQ_ALU_CONST_CACHE_PS_3: + case SQ_ALU_CONST_CACHE_PS_4: + case SQ_ALU_CONST_CACHE_PS_5: + case SQ_ALU_CONST_CACHE_PS_6: + case SQ_ALU_CONST_CACHE_PS_7: + case SQ_ALU_CONST_CACHE_PS_8: + case SQ_ALU_CONST_CACHE_PS_9: + case SQ_ALU_CONST_CACHE_PS_10: + case SQ_ALU_CONST_CACHE_PS_11: + case SQ_ALU_CONST_CACHE_PS_12: + case SQ_ALU_CONST_CACHE_PS_13: + case SQ_ALU_CONST_CACHE_PS_14: + case SQ_ALU_CONST_CACHE_PS_15: + case SQ_ALU_CONST_CACHE_VS_0: + case SQ_ALU_CONST_CACHE_VS_1: + case SQ_ALU_CONST_CACHE_VS_2: + case SQ_ALU_CONST_CACHE_VS_3: + case SQ_ALU_CONST_CACHE_VS_4: + case SQ_ALU_CONST_CACHE_VS_5: + case SQ_ALU_CONST_CACHE_VS_6: + case SQ_ALU_CONST_CACHE_VS_7: + case SQ_ALU_CONST_CACHE_VS_8: + case SQ_ALU_CONST_CACHE_VS_9: + case SQ_ALU_CONST_CACHE_VS_10: + case SQ_ALU_CONST_CACHE_VS_11: + case SQ_ALU_CONST_CACHE_VS_12: + case SQ_ALU_CONST_CACHE_VS_13: + case SQ_ALU_CONST_CACHE_VS_14: + case SQ_ALU_CONST_CACHE_VS_15: + case SQ_ALU_CONST_CACHE_HS_0: + case SQ_ALU_CONST_CACHE_HS_1: + case SQ_ALU_CONST_CACHE_HS_2: + case SQ_ALU_CONST_CACHE_HS_3: + case SQ_ALU_CONST_CACHE_HS_4: + case SQ_ALU_CONST_CACHE_HS_5: + case SQ_ALU_CONST_CACHE_HS_6: + case SQ_ALU_CONST_CACHE_HS_7: + case SQ_ALU_CONST_CACHE_HS_8: + case SQ_ALU_CONST_CACHE_HS_9: + case SQ_ALU_CONST_CACHE_HS_10: + case SQ_ALU_CONST_CACHE_HS_11: + case SQ_ALU_CONST_CACHE_HS_12: + case SQ_ALU_CONST_CACHE_HS_13: + case SQ_ALU_CONST_CACHE_HS_14: + case SQ_ALU_CONST_CACHE_HS_15: + case SQ_ALU_CONST_CACHE_LS_0: + case SQ_ALU_CONST_CACHE_LS_1: + case SQ_ALU_CONST_CACHE_LS_2: + case SQ_ALU_CONST_CACHE_LS_3: + case SQ_ALU_CONST_CACHE_LS_4: + case SQ_ALU_CONST_CACHE_LS_5: + case SQ_ALU_CONST_CACHE_LS_6: + case SQ_ALU_CONST_CACHE_LS_7: + case SQ_ALU_CONST_CACHE_LS_8: + case SQ_ALU_CONST_CACHE_LS_9: + case SQ_ALU_CONST_CACHE_LS_10: + case SQ_ALU_CONST_CACHE_LS_11: + case SQ_ALU_CONST_CACHE_LS_12: + case SQ_ALU_CONST_CACHE_LS_13: + case SQ_ALU_CONST_CACHE_LS_14: + case SQ_ALU_CONST_CACHE_LS_15: + r = evergreen_cs_packet_next_reloc(p, &reloc); + if (r) { + dev_warn(p->dev, "bad SET_CONTEXT_REG " + "0x%04X\n", reg); + return -EINVAL; + } + ib[idx] += (u32)((reloc->lobj.gpu_offset >> 8) & 0xffffffff); + break; + case SX_MEMORY_EXPORT_BASE: + if (p->rdev->family >= CHIP_CAYMAN) { + dev_warn(p->dev, "bad SET_CONFIG_REG " + "0x%04X\n", reg); + return -EINVAL; + } + r = evergreen_cs_packet_next_reloc(p, &reloc); + if (r) { + dev_warn(p->dev, "bad SET_CONFIG_REG " + "0x%04X\n", reg); + return -EINVAL; + } + ib[idx] += (u32)((reloc->lobj.gpu_offset >> 8) & 0xffffffff); + break; + case CAYMAN_SX_SCATTER_EXPORT_BASE: + if (p->rdev->family < CHIP_CAYMAN) { + dev_warn(p->dev, "bad SET_CONTEXT_REG " + "0x%04X\n", reg); + return -EINVAL; + } + r = evergreen_cs_packet_next_reloc(p, &reloc); + if (r) { + dev_warn(p->dev, "bad SET_CONTEXT_REG " + "0x%04X\n", reg); + return -EINVAL; + } + ib[idx] += (u32)((reloc->lobj.gpu_offset >> 8) & 0xffffffff); + break; + case SX_MISC: + track->sx_misc_kill_all_prims = (radeon_get_ib_value(p, idx) & 0x1) != 0; + break; + default: + dev_warn(p->dev, "forbidden register 0x%08x at %d\n", reg, idx); + return -EINVAL; + } + return 0; +} + +static bool evergreen_is_safe_reg(struct radeon_cs_parser *p, u32 reg, u32 idx) +{ + u32 last_reg, m, i; + + if (p->rdev->family >= CHIP_CAYMAN) + last_reg = DRM_ARRAY_SIZE(cayman_reg_safe_bm); + else + last_reg = DRM_ARRAY_SIZE(evergreen_reg_safe_bm); + + i = (reg >> 7); + if (i >= last_reg) { + dev_warn(p->dev, "forbidden register 0x%08x at %d\n", reg, idx); + return false; + } + m = 1 << ((reg >> 2) & 31); + if (p->rdev->family >= CHIP_CAYMAN) { + if (!(cayman_reg_safe_bm[i] & m)) + return true; + } else { + if (!(evergreen_reg_safe_bm[i] & m)) + return true; + } + dev_warn(p->dev, "forbidden register 0x%08x at %d\n", reg, idx); + return false; +} + +static int evergreen_packet3_check(struct radeon_cs_parser *p, + struct radeon_cs_packet *pkt) +{ + struct radeon_cs_reloc *reloc; + struct evergreen_cs_track *track; + volatile u32 *ib; + unsigned idx; + unsigned i; + unsigned start_reg, end_reg, reg; + int r; + u32 idx_value; + + track = (struct evergreen_cs_track *)p->track; + ib = p->ib.ptr; + idx = pkt->idx + 1; + idx_value = radeon_get_ib_value(p, idx); + + switch (pkt->opcode) { + case PACKET3_SET_PREDICATION: + { + int pred_op; + int tmp; + uint64_t offset; + + if (pkt->count != 1) { + DRM_ERROR("bad SET PREDICATION\n"); + return -EINVAL; + } + + tmp = radeon_get_ib_value(p, idx + 1); + pred_op = (tmp >> 16) & 0x7; + + /* for the clear predicate operation */ + if (pred_op == 0) + return 0; + + if (pred_op > 2) { + DRM_ERROR("bad SET PREDICATION operation %d\n", pred_op); + return -EINVAL; + } + + r = evergreen_cs_packet_next_reloc(p, &reloc); + if (r) { + DRM_ERROR("bad SET PREDICATION\n"); + return -EINVAL; + } + + offset = reloc->lobj.gpu_offset + + (idx_value & 0xfffffff0) + + ((u64)(tmp & 0xff) << 32); + + ib[idx + 0] = offset; + ib[idx + 1] = (tmp & 0xffffff00) | (upper_32_bits(offset) & 0xff); + } + break; + case PACKET3_CONTEXT_CONTROL: + if (pkt->count != 1) { + DRM_ERROR("bad CONTEXT_CONTROL\n"); + return -EINVAL; + } + break; + case PACKET3_INDEX_TYPE: + case PACKET3_NUM_INSTANCES: + case PACKET3_CLEAR_STATE: + if (pkt->count) { + DRM_ERROR("bad INDEX_TYPE/NUM_INSTANCES/CLEAR_STATE\n"); + return -EINVAL; + } + break; + case CAYMAN_PACKET3_DEALLOC_STATE: + if (p->rdev->family < CHIP_CAYMAN) { + DRM_ERROR("bad PACKET3_DEALLOC_STATE\n"); + return -EINVAL; + } + if (pkt->count) { + DRM_ERROR("bad INDEX_TYPE/NUM_INSTANCES/CLEAR_STATE\n"); + return -EINVAL; + } + break; + case PACKET3_INDEX_BASE: + { + uint64_t offset; + + if (pkt->count != 1) { + DRM_ERROR("bad INDEX_BASE\n"); + return -EINVAL; + } + r = evergreen_cs_packet_next_reloc(p, &reloc); + if (r) { + DRM_ERROR("bad INDEX_BASE\n"); + return -EINVAL; + } + + offset = reloc->lobj.gpu_offset + + idx_value + + ((u64)(radeon_get_ib_value(p, idx+1) & 0xff) << 32); + + ib[idx+0] = offset; + ib[idx+1] = upper_32_bits(offset) & 0xff; + + r = evergreen_cs_track_check(p); + if (r) { + dev_warn(p->dev, "%s:%d invalid cmd stream\n", __func__, __LINE__); + return r; + } + break; + } + case PACKET3_DRAW_INDEX: + { + uint64_t offset; + if (pkt->count != 3) { + DRM_ERROR("bad DRAW_INDEX\n"); + return -EINVAL; + } + r = evergreen_cs_packet_next_reloc(p, &reloc); + if (r) { + DRM_ERROR("bad DRAW_INDEX\n"); + return -EINVAL; + } + + offset = reloc->lobj.gpu_offset + + idx_value + + ((u64)(radeon_get_ib_value(p, idx+1) & 0xff) << 32); + + ib[idx+0] = offset; + ib[idx+1] = upper_32_bits(offset) & 0xff; + + r = evergreen_cs_track_check(p); + if (r) { + dev_warn(p->dev, "%s:%d invalid cmd stream\n", __func__, __LINE__); + return r; + } + break; + } + case PACKET3_DRAW_INDEX_2: + { + uint64_t offset; + + if (pkt->count != 4) { + DRM_ERROR("bad DRAW_INDEX_2\n"); + return -EINVAL; + } + r = evergreen_cs_packet_next_reloc(p, &reloc); + if (r) { + DRM_ERROR("bad DRAW_INDEX_2\n"); + return -EINVAL; + } + + offset = reloc->lobj.gpu_offset + + radeon_get_ib_value(p, idx+1) + + ((u64)(radeon_get_ib_value(p, idx+2) & 0xff) << 32); + + ib[idx+1] = offset; + ib[idx+2] = upper_32_bits(offset) & 0xff; + + r = evergreen_cs_track_check(p); + if (r) { + dev_warn(p->dev, "%s:%d invalid cmd stream\n", __func__, __LINE__); + return r; + } + break; + } + case PACKET3_DRAW_INDEX_AUTO: + if (pkt->count != 1) { + DRM_ERROR("bad DRAW_INDEX_AUTO\n"); + return -EINVAL; + } + r = evergreen_cs_track_check(p); + if (r) { + dev_warn(p->dev, "%s:%d invalid cmd stream %d\n", __func__, __LINE__, idx); + return r; + } + break; + case PACKET3_DRAW_INDEX_MULTI_AUTO: + if (pkt->count != 2) { + DRM_ERROR("bad DRAW_INDEX_MULTI_AUTO\n"); + return -EINVAL; + } + r = evergreen_cs_track_check(p); + if (r) { + dev_warn(p->dev, "%s:%d invalid cmd stream %d\n", __func__, __LINE__, idx); + return r; + } + break; + case PACKET3_DRAW_INDEX_IMMD: + if (pkt->count < 2) { + DRM_ERROR("bad DRAW_INDEX_IMMD\n"); + return -EINVAL; + } + r = evergreen_cs_track_check(p); + if (r) { + dev_warn(p->dev, "%s:%d invalid cmd stream\n", __func__, __LINE__); + return r; + } + break; + case PACKET3_DRAW_INDEX_OFFSET: + if (pkt->count != 2) { + DRM_ERROR("bad DRAW_INDEX_OFFSET\n"); + return -EINVAL; + } + r = evergreen_cs_track_check(p); + if (r) { + dev_warn(p->dev, "%s:%d invalid cmd stream\n", __func__, __LINE__); + return r; + } + break; + case PACKET3_DRAW_INDEX_OFFSET_2: + if (pkt->count != 3) { + DRM_ERROR("bad DRAW_INDEX_OFFSET_2\n"); + return -EINVAL; + } + r = evergreen_cs_track_check(p); + if (r) { + dev_warn(p->dev, "%s:%d invalid cmd stream\n", __func__, __LINE__); + return r; + } + break; + case PACKET3_DISPATCH_DIRECT: + if (pkt->count != 3) { + DRM_ERROR("bad DISPATCH_DIRECT\n"); + return -EINVAL; + } + r = evergreen_cs_track_check(p); + if (r) { + dev_warn(p->dev, "%s:%d invalid cmd stream %d\n", __func__, __LINE__, idx); + return r; + } + break; + case PACKET3_DISPATCH_INDIRECT: + if (pkt->count != 1) { + DRM_ERROR("bad DISPATCH_INDIRECT\n"); + return -EINVAL; + } + r = evergreen_cs_packet_next_reloc(p, &reloc); + if (r) { + DRM_ERROR("bad DISPATCH_INDIRECT\n"); + return -EINVAL; + } + ib[idx+0] = idx_value + (u32)(reloc->lobj.gpu_offset & 0xffffffff); + r = evergreen_cs_track_check(p); + if (r) { + dev_warn(p->dev, "%s:%d invalid cmd stream\n", __func__, __LINE__); + return r; + } + break; + case PACKET3_WAIT_REG_MEM: + if (pkt->count != 5) { + DRM_ERROR("bad WAIT_REG_MEM\n"); + return -EINVAL; + } + /* bit 4 is reg (0) or mem (1) */ + if (idx_value & 0x10) { + uint64_t offset; + + r = evergreen_cs_packet_next_reloc(p, &reloc); + if (r) { + DRM_ERROR("bad WAIT_REG_MEM\n"); + return -EINVAL; + } + + offset = reloc->lobj.gpu_offset + + (radeon_get_ib_value(p, idx+1) & 0xfffffffc) + + ((u64)(radeon_get_ib_value(p, idx+2) & 0xff) << 32); + + ib[idx+1] = (ib[idx+1] & 0x3) | (offset & 0xfffffffc); + ib[idx+2] = upper_32_bits(offset) & 0xff; + } + break; + case PACKET3_CP_DMA: + { + u32 command, size, info; + u64 offset, tmp; + if (pkt->count != 4) { + DRM_ERROR("bad CP DMA\n"); + return -EINVAL; + } + command = radeon_get_ib_value(p, idx+4); + size = command & 0x1fffff; + info = radeon_get_ib_value(p, idx+1); + if ((((info & 0x60000000) >> 29) != 0) || /* src = GDS or DATA */ + (((info & 0x00300000) >> 20) != 0) || /* dst = GDS */ + ((((info & 0x00300000) >> 20) == 0) && + (command & PACKET3_CP_DMA_CMD_DAS)) || /* dst = register */ + ((((info & 0x60000000) >> 29) == 0) && + (command & PACKET3_CP_DMA_CMD_SAS))) { /* src = register */ + /* non mem to mem copies requires dw aligned count */ + if (size % 4) { + DRM_ERROR("CP DMA command requires dw count alignment\n"); + return -EINVAL; + } + } + if (command & PACKET3_CP_DMA_CMD_SAS) { + /* src address space is register */ + /* GDS is ok */ + if (((info & 0x60000000) >> 29) != 1) { + DRM_ERROR("CP DMA SAS not supported\n"); + return -EINVAL; + } + } else { + if (command & PACKET3_CP_DMA_CMD_SAIC) { + DRM_ERROR("CP DMA SAIC only supported for registers\n"); + return -EINVAL; + } + /* src address space is memory */ + if (((info & 0x60000000) >> 29) == 0) { + r = evergreen_cs_packet_next_reloc(p, &reloc); + if (r) { + DRM_ERROR("bad CP DMA SRC\n"); + return -EINVAL; + } + + tmp = radeon_get_ib_value(p, idx) + + ((u64)(radeon_get_ib_value(p, idx+1) & 0xff) << 32); + + offset = reloc->lobj.gpu_offset + tmp; + + if ((tmp + size) > radeon_bo_size(reloc->robj)) { + dev_warn(p->dev, "CP DMA src buffer too small (%ju %lu)\n", + (uintmax_t)tmp + size, radeon_bo_size(reloc->robj)); + return -EINVAL; + } + + ib[idx] = offset; + ib[idx+1] = (ib[idx+1] & 0xffffff00) | (upper_32_bits(offset) & 0xff); + } else if (((info & 0x60000000) >> 29) != 2) { + DRM_ERROR("bad CP DMA SRC_SEL\n"); + return -EINVAL; + } + } + if (command & PACKET3_CP_DMA_CMD_DAS) { + /* dst address space is register */ + /* GDS is ok */ + if (((info & 0x00300000) >> 20) != 1) { + DRM_ERROR("CP DMA DAS not supported\n"); + return -EINVAL; + } + } else { + /* dst address space is memory */ + if (command & PACKET3_CP_DMA_CMD_DAIC) { + DRM_ERROR("CP DMA DAIC only supported for registers\n"); + return -EINVAL; + } + if (((info & 0x00300000) >> 20) == 0) { + r = evergreen_cs_packet_next_reloc(p, &reloc); + if (r) { + DRM_ERROR("bad CP DMA DST\n"); + return -EINVAL; + } + + tmp = radeon_get_ib_value(p, idx+2) + + ((u64)(radeon_get_ib_value(p, idx+3) & 0xff) << 32); + + offset = reloc->lobj.gpu_offset + tmp; + + if ((tmp + size) > radeon_bo_size(reloc->robj)) { + dev_warn(p->dev, "CP DMA dst buffer too small (%ju %lu)\n", + (uintmax_t)tmp + size, radeon_bo_size(reloc->robj)); + return -EINVAL; + } + + ib[idx+2] = offset; + ib[idx+3] = upper_32_bits(offset) & 0xff; + } else { + DRM_ERROR("bad CP DMA DST_SEL\n"); + return -EINVAL; + } + } + break; + } + case PACKET3_SURFACE_SYNC: + if (pkt->count != 3) { + DRM_ERROR("bad SURFACE_SYNC\n"); + return -EINVAL; + } + /* 0xffffffff/0x0 is flush all cache flag */ + if (radeon_get_ib_value(p, idx + 1) != 0xffffffff || + radeon_get_ib_value(p, idx + 2) != 0) { + r = evergreen_cs_packet_next_reloc(p, &reloc); + if (r) { + DRM_ERROR("bad SURFACE_SYNC\n"); + return -EINVAL; + } + ib[idx+2] += (u32)((reloc->lobj.gpu_offset >> 8) & 0xffffffff); + } + break; + case PACKET3_EVENT_WRITE: + if (pkt->count != 2 && pkt->count != 0) { + DRM_ERROR("bad EVENT_WRITE\n"); + return -EINVAL; + } + if (pkt->count) { + uint64_t offset; + + r = evergreen_cs_packet_next_reloc(p, &reloc); + if (r) { + DRM_ERROR("bad EVENT_WRITE\n"); + return -EINVAL; + } + offset = reloc->lobj.gpu_offset + + (radeon_get_ib_value(p, idx+1) & 0xfffffff8) + + ((u64)(radeon_get_ib_value(p, idx+2) & 0xff) << 32); + + ib[idx+1] = offset & 0xfffffff8; + ib[idx+2] = upper_32_bits(offset) & 0xff; + } + break; + case PACKET3_EVENT_WRITE_EOP: + { + uint64_t offset; + + if (pkt->count != 4) { + DRM_ERROR("bad EVENT_WRITE_EOP\n"); + return -EINVAL; + } + r = evergreen_cs_packet_next_reloc(p, &reloc); + if (r) { + DRM_ERROR("bad EVENT_WRITE_EOP\n"); + return -EINVAL; + } + + offset = reloc->lobj.gpu_offset + + (radeon_get_ib_value(p, idx+1) & 0xfffffffc) + + ((u64)(radeon_get_ib_value(p, idx+2) & 0xff) << 32); + + ib[idx+1] = offset & 0xfffffffc; + ib[idx+2] = (ib[idx+2] & 0xffffff00) | (upper_32_bits(offset) & 0xff); + break; + } + case PACKET3_EVENT_WRITE_EOS: + { + uint64_t offset; + + if (pkt->count != 3) { + DRM_ERROR("bad EVENT_WRITE_EOS\n"); + return -EINVAL; + } + r = evergreen_cs_packet_next_reloc(p, &reloc); + if (r) { + DRM_ERROR("bad EVENT_WRITE_EOS\n"); + return -EINVAL; + } + + offset = reloc->lobj.gpu_offset + + (radeon_get_ib_value(p, idx+1) & 0xfffffffc) + + ((u64)(radeon_get_ib_value(p, idx+2) & 0xff) << 32); + + ib[idx+1] = offset & 0xfffffffc; + ib[idx+2] = (ib[idx+2] & 0xffffff00) | (upper_32_bits(offset) & 0xff); + break; + } + case PACKET3_SET_CONFIG_REG: + start_reg = (idx_value << 2) + PACKET3_SET_CONFIG_REG_START; + end_reg = 4 * pkt->count + start_reg - 4; + if ((start_reg < PACKET3_SET_CONFIG_REG_START) || + (start_reg >= PACKET3_SET_CONFIG_REG_END) || + (end_reg >= PACKET3_SET_CONFIG_REG_END)) { + DRM_ERROR("bad PACKET3_SET_CONFIG_REG\n"); + return -EINVAL; + } + for (i = 0; i < pkt->count; i++) { + reg = start_reg + (4 * i); + r = evergreen_cs_check_reg(p, reg, idx+1+i); + if (r) + return r; + } + break; + case PACKET3_SET_CONTEXT_REG: + start_reg = (idx_value << 2) + PACKET3_SET_CONTEXT_REG_START; + end_reg = 4 * pkt->count + start_reg - 4; + if ((start_reg < PACKET3_SET_CONTEXT_REG_START) || + (start_reg >= PACKET3_SET_CONTEXT_REG_END) || + (end_reg >= PACKET3_SET_CONTEXT_REG_END)) { + DRM_ERROR("bad PACKET3_SET_CONTEXT_REG\n"); + return -EINVAL; + } + for (i = 0; i < pkt->count; i++) { + reg = start_reg + (4 * i); + r = evergreen_cs_check_reg(p, reg, idx+1+i); + if (r) + return r; + } + break; + case PACKET3_SET_RESOURCE: + if (pkt->count % 8) { + DRM_ERROR("bad SET_RESOURCE\n"); + return -EINVAL; + } + start_reg = (idx_value << 2) + PACKET3_SET_RESOURCE_START; + end_reg = 4 * pkt->count + start_reg - 4; + if ((start_reg < PACKET3_SET_RESOURCE_START) || + (start_reg >= PACKET3_SET_RESOURCE_END) || + (end_reg >= PACKET3_SET_RESOURCE_END)) { + DRM_ERROR("bad SET_RESOURCE\n"); + return -EINVAL; + } + for (i = 0; i < (pkt->count / 8); i++) { + struct radeon_bo *texture, *mipmap; + u32 toffset, moffset; + u32 size, offset, mip_address, tex_dim; + + switch (G__SQ_CONSTANT_TYPE(radeon_get_ib_value(p, idx+1+(i*8)+7))) { + case SQ_TEX_VTX_VALID_TEXTURE: + /* tex base */ + r = evergreen_cs_packet_next_reloc(p, &reloc); + if (r) { + DRM_ERROR("bad SET_RESOURCE (tex)\n"); + return -EINVAL; + } + if (!(p->cs_flags & RADEON_CS_KEEP_TILING_FLAGS)) { + ib[idx+1+(i*8)+1] |= + TEX_ARRAY_MODE(evergreen_cs_get_aray_mode(reloc->lobj.tiling_flags)); + if (reloc->lobj.tiling_flags & RADEON_TILING_MACRO) { + unsigned bankw, bankh, mtaspect, tile_split; + + evergreen_tiling_fields(reloc->lobj.tiling_flags, + &bankw, &bankh, &mtaspect, + &tile_split); + ib[idx+1+(i*8)+6] |= TEX_TILE_SPLIT(tile_split); + ib[idx+1+(i*8)+7] |= + TEX_BANK_WIDTH(bankw) | + TEX_BANK_HEIGHT(bankh) | + MACRO_TILE_ASPECT(mtaspect) | + TEX_NUM_BANKS(evergreen_cs_get_num_banks(track->nbanks)); + } + } + texture = reloc->robj; + toffset = (u32)((reloc->lobj.gpu_offset >> 8) & 0xffffffff); + + /* tex mip base */ + tex_dim = ib[idx+1+(i*8)+0] & 0x7; + mip_address = ib[idx+1+(i*8)+3]; + + if ((tex_dim == SQ_TEX_DIM_2D_MSAA || tex_dim == SQ_TEX_DIM_2D_ARRAY_MSAA) && + !mip_address && + !evergreen_cs_packet_next_is_pkt3_nop(p)) { + /* MIP_ADDRESS should point to FMASK for an MSAA texture. + * It should be 0 if FMASK is disabled. */ + moffset = 0; + mipmap = NULL; + } else { + r = evergreen_cs_packet_next_reloc(p, &reloc); + if (r) { + DRM_ERROR("bad SET_RESOURCE (tex)\n"); + return -EINVAL; + } + moffset = (u32)((reloc->lobj.gpu_offset >> 8) & 0xffffffff); + mipmap = reloc->robj; + } + + r = evergreen_cs_track_validate_texture(p, texture, mipmap, idx+1+(i*8)); + if (r) + return r; + ib[idx+1+(i*8)+2] += toffset; + ib[idx+1+(i*8)+3] += moffset; + break; + case SQ_TEX_VTX_VALID_BUFFER: + { + uint64_t offset64; + /* vtx base */ + r = evergreen_cs_packet_next_reloc(p, &reloc); + if (r) { + DRM_ERROR("bad SET_RESOURCE (vtx)\n"); + return -EINVAL; + } + offset = radeon_get_ib_value(p, idx+1+(i*8)+0); + size = radeon_get_ib_value(p, idx+1+(i*8)+1); + if (p->rdev && (size + offset) > radeon_bo_size(reloc->robj)) { + /* force size to size of the buffer */ + dev_warn(p->dev, "vbo resource seems too big for the bo\n"); + ib[idx+1+(i*8)+1] = radeon_bo_size(reloc->robj) - offset; + } + + offset64 = reloc->lobj.gpu_offset + offset; + ib[idx+1+(i*8)+0] = offset64; + ib[idx+1+(i*8)+2] = (ib[idx+1+(i*8)+2] & 0xffffff00) | + (upper_32_bits(offset64) & 0xff); + break; + } + case SQ_TEX_VTX_INVALID_TEXTURE: + case SQ_TEX_VTX_INVALID_BUFFER: + default: + DRM_ERROR("bad SET_RESOURCE\n"); + return -EINVAL; + } + } + break; + case PACKET3_SET_ALU_CONST: + /* XXX fix me ALU const buffers only */ + break; + case PACKET3_SET_BOOL_CONST: + start_reg = (idx_value << 2) + PACKET3_SET_BOOL_CONST_START; + end_reg = 4 * pkt->count + start_reg - 4; + if ((start_reg < PACKET3_SET_BOOL_CONST_START) || + (start_reg >= PACKET3_SET_BOOL_CONST_END) || + (end_reg >= PACKET3_SET_BOOL_CONST_END)) { + DRM_ERROR("bad SET_BOOL_CONST\n"); + return -EINVAL; + } + break; + case PACKET3_SET_LOOP_CONST: + start_reg = (idx_value << 2) + PACKET3_SET_LOOP_CONST_START; + end_reg = 4 * pkt->count + start_reg - 4; + if ((start_reg < PACKET3_SET_LOOP_CONST_START) || + (start_reg >= PACKET3_SET_LOOP_CONST_END) || + (end_reg >= PACKET3_SET_LOOP_CONST_END)) { + DRM_ERROR("bad SET_LOOP_CONST\n"); + return -EINVAL; + } + break; + case PACKET3_SET_CTL_CONST: + start_reg = (idx_value << 2) + PACKET3_SET_CTL_CONST_START; + end_reg = 4 * pkt->count + start_reg - 4; + if ((start_reg < PACKET3_SET_CTL_CONST_START) || + (start_reg >= PACKET3_SET_CTL_CONST_END) || + (end_reg >= PACKET3_SET_CTL_CONST_END)) { + DRM_ERROR("bad SET_CTL_CONST\n"); + return -EINVAL; + } + break; + case PACKET3_SET_SAMPLER: + if (pkt->count % 3) { + DRM_ERROR("bad SET_SAMPLER\n"); + return -EINVAL; + } + start_reg = (idx_value << 2) + PACKET3_SET_SAMPLER_START; + end_reg = 4 * pkt->count + start_reg - 4; + if ((start_reg < PACKET3_SET_SAMPLER_START) || + (start_reg >= PACKET3_SET_SAMPLER_END) || + (end_reg >= PACKET3_SET_SAMPLER_END)) { + DRM_ERROR("bad SET_SAMPLER\n"); + return -EINVAL; + } + break; + case PACKET3_STRMOUT_BUFFER_UPDATE: + if (pkt->count != 4) { + DRM_ERROR("bad STRMOUT_BUFFER_UPDATE (invalid count)\n"); + return -EINVAL; + } + /* Updating memory at DST_ADDRESS. */ + if (idx_value & 0x1) { + u64 offset; + r = evergreen_cs_packet_next_reloc(p, &reloc); + if (r) { + DRM_ERROR("bad STRMOUT_BUFFER_UPDATE (missing dst reloc)\n"); + return -EINVAL; + } + offset = radeon_get_ib_value(p, idx+1); + offset += ((u64)(radeon_get_ib_value(p, idx+2) & 0xff)) << 32; + if ((offset + 4) > radeon_bo_size(reloc->robj)) { + DRM_ERROR("bad STRMOUT_BUFFER_UPDATE dst bo too small: 0x%jx, 0x%lx\n", + (uintmax_t)offset + 4, radeon_bo_size(reloc->robj)); + return -EINVAL; + } + offset += reloc->lobj.gpu_offset; + ib[idx+1] = offset; + ib[idx+2] = upper_32_bits(offset) & 0xff; + } + /* Reading data from SRC_ADDRESS. */ + if (((idx_value >> 1) & 0x3) == 2) { + u64 offset; + r = evergreen_cs_packet_next_reloc(p, &reloc); + if (r) { + DRM_ERROR("bad STRMOUT_BUFFER_UPDATE (missing src reloc)\n"); + return -EINVAL; + } + offset = radeon_get_ib_value(p, idx+3); + offset += ((u64)(radeon_get_ib_value(p, idx+4) & 0xff)) << 32; + if ((offset + 4) > radeon_bo_size(reloc->robj)) { + DRM_ERROR("bad STRMOUT_BUFFER_UPDATE src bo too small: 0x%jx, 0x%lx\n", + (uintmax_t)offset + 4, radeon_bo_size(reloc->robj)); + return -EINVAL; + } + offset += reloc->lobj.gpu_offset; + ib[idx+3] = offset; + ib[idx+4] = upper_32_bits(offset) & 0xff; + } + break; + case PACKET3_MEM_WRITE: + { + u64 offset; + + if (pkt->count != 3) { + DRM_ERROR("bad MEM_WRITE (invalid count)\n"); + return -EINVAL; + } + r = evergreen_cs_packet_next_reloc(p, &reloc); + if (r) { + DRM_ERROR("bad MEM_WRITE (missing reloc)\n"); + return -EINVAL; + } + offset = radeon_get_ib_value(p, idx+0); + offset += ((u64)(radeon_get_ib_value(p, idx+1) & 0xff)) << 32UL; + if (offset & 0x7) { + DRM_ERROR("bad MEM_WRITE (address not qwords aligned)\n"); + return -EINVAL; + } + if ((offset + 8) > radeon_bo_size(reloc->robj)) { + DRM_ERROR("bad MEM_WRITE bo too small: 0x%jx, 0x%lx\n", + (uintmax_t)offset + 8, radeon_bo_size(reloc->robj)); + return -EINVAL; + } + offset += reloc->lobj.gpu_offset; + ib[idx+0] = offset; + ib[idx+1] = upper_32_bits(offset) & 0xff; + break; + } + case PACKET3_COPY_DW: + if (pkt->count != 4) { + DRM_ERROR("bad COPY_DW (invalid count)\n"); + return -EINVAL; + } + if (idx_value & 0x1) { + u64 offset; + /* SRC is memory. */ + r = evergreen_cs_packet_next_reloc(p, &reloc); + if (r) { + DRM_ERROR("bad COPY_DW (missing src reloc)\n"); + return -EINVAL; + } + offset = radeon_get_ib_value(p, idx+1); + offset += ((u64)(radeon_get_ib_value(p, idx+2) & 0xff)) << 32; + if ((offset + 4) > radeon_bo_size(reloc->robj)) { + DRM_ERROR("bad COPY_DW src bo too small: 0x%jx, 0x%lx\n", + (uintmax_t)offset + 4, radeon_bo_size(reloc->robj)); + return -EINVAL; + } + offset += reloc->lobj.gpu_offset; + ib[idx+1] = offset; + ib[idx+2] = upper_32_bits(offset) & 0xff; + } else { + /* SRC is a reg. */ + reg = radeon_get_ib_value(p, idx+1) << 2; + if (!evergreen_is_safe_reg(p, reg, idx+1)) + return -EINVAL; + } + if (idx_value & 0x2) { + u64 offset; + /* DST is memory. */ + r = evergreen_cs_packet_next_reloc(p, &reloc); + if (r) { + DRM_ERROR("bad COPY_DW (missing dst reloc)\n"); + return -EINVAL; + } + offset = radeon_get_ib_value(p, idx+3); + offset += ((u64)(radeon_get_ib_value(p, idx+4) & 0xff)) << 32; + if ((offset + 4) > radeon_bo_size(reloc->robj)) { + DRM_ERROR("bad COPY_DW dst bo too small: 0x%jx, 0x%lx\n", + (uintmax_t)offset + 4, radeon_bo_size(reloc->robj)); + return -EINVAL; + } + offset += reloc->lobj.gpu_offset; + ib[idx+3] = offset; + ib[idx+4] = upper_32_bits(offset) & 0xff; + } else { + /* DST is a reg. */ + reg = radeon_get_ib_value(p, idx+3) << 2; + if (!evergreen_is_safe_reg(p, reg, idx+3)) + return -EINVAL; + } + break; + case PACKET3_NOP: + break; + default: + DRM_ERROR("Packet3 opcode %x not supported\n", pkt->opcode); + return -EINVAL; + } + return 0; +} + +int evergreen_cs_parse(struct radeon_cs_parser *p) +{ + struct radeon_cs_packet pkt; + struct evergreen_cs_track *track; + u32 tmp; + int r; + + if (p->track == NULL) { + /* initialize tracker, we are in kms */ + track = malloc(sizeof(*track), DRM_MEM_DRIVER, M_ZERO | M_WAITOK); + if (track == NULL) + return -ENOMEM; + evergreen_cs_track_init(track); + if (p->rdev->family >= CHIP_CAYMAN) + tmp = p->rdev->config.cayman.tile_config; + else + tmp = p->rdev->config.evergreen.tile_config; + + switch (tmp & 0xf) { + case 0: + track->npipes = 1; + break; + case 1: + default: + track->npipes = 2; + break; + case 2: + track->npipes = 4; + break; + case 3: + track->npipes = 8; + break; + } + + switch ((tmp & 0xf0) >> 4) { + case 0: + track->nbanks = 4; + break; + case 1: + default: + track->nbanks = 8; + break; + case 2: + track->nbanks = 16; + break; + } + + switch ((tmp & 0xf00) >> 8) { + case 0: + track->group_size = 256; + break; + case 1: + default: + track->group_size = 512; + break; + } + + switch ((tmp & 0xf000) >> 12) { + case 0: + track->row_size = 1; + break; + case 1: + default: + track->row_size = 2; + break; + case 2: + track->row_size = 4; + break; + } + + p->track = track; + } + do { + r = evergreen_cs_packet_parse(p, &pkt, p->idx); + if (r) { + free(p->track, DRM_MEM_DRIVER); + p->track = NULL; + return r; + } + p->idx += pkt.count + 2; + switch (pkt.type) { + case PACKET_TYPE0: + r = evergreen_cs_parse_packet0(p, &pkt); + break; + case PACKET_TYPE2: + break; + case PACKET_TYPE3: + r = evergreen_packet3_check(p, &pkt); + break; + default: + DRM_ERROR("Unknown packet type %d !\n", pkt.type); + free(p->track, DRM_MEM_DRIVER); + p->track = NULL; + return -EINVAL; + } + if (r) { + free(p->track, DRM_MEM_DRIVER); + p->track = NULL; + return r; + } + } while (p->idx < p->chunks[p->chunk_ib_idx].length_dw); +#if 0 + for (r = 0; r < p->ib.length_dw; r++) { + DRM_INFO("%05d 0x%08X\n", r, p->ib.ptr[r]); + mdelay(1); + } +#endif + free(p->track, DRM_MEM_DRIVER); + p->track = NULL; + return 0; +} + +/* + * DMA + */ + +#define GET_DMA_CMD(h) (((h) & 0xf0000000) >> 28) +#define GET_DMA_COUNT(h) ((h) & 0x000fffff) +#define GET_DMA_T(h) (((h) & 0x00800000) >> 23) +#define GET_DMA_NEW(h) (((h) & 0x04000000) >> 26) +#define GET_DMA_MISC(h) (((h) & 0x0700000) >> 20) + +/** + * evergreen_dma_cs_parse() - parse the DMA IB + * @p: parser structure holding parsing context. + * + * Parses the DMA IB from the CS ioctl and updates + * the GPU addresses based on the reloc information and + * checks for errors. (Evergreen-Cayman) + * Returns 0 for success and an error on failure. + **/ +int evergreen_dma_cs_parse(struct radeon_cs_parser *p) +{ + struct radeon_cs_chunk *ib_chunk = &p->chunks[p->chunk_ib_idx]; + struct radeon_cs_reloc *src_reloc, *dst_reloc, *dst2_reloc; + u32 header, cmd, count, tiled, new_cmd, misc; + volatile u32 *ib = p->ib.ptr; + u32 idx, idx_value; + u64 src_offset, dst_offset, dst2_offset; + int r; + + do { + if (p->idx >= ib_chunk->length_dw) { + DRM_ERROR("Can not parse packet at %d after CS end %d !\n", + p->idx, ib_chunk->length_dw); + return -EINVAL; + } + idx = p->idx; + header = radeon_get_ib_value(p, idx); + cmd = GET_DMA_CMD(header); + count = GET_DMA_COUNT(header); + tiled = GET_DMA_T(header); + new_cmd = GET_DMA_NEW(header); + misc = GET_DMA_MISC(header); + + switch (cmd) { + case DMA_PACKET_WRITE: + r = r600_dma_cs_next_reloc(p, &dst_reloc); + if (r) { + DRM_ERROR("bad DMA_PACKET_WRITE\n"); + return -EINVAL; + } + if (tiled) { + dst_offset = radeon_get_ib_value(p, idx+1); + dst_offset <<= 8; + + ib[idx+1] += (u32)(dst_reloc->lobj.gpu_offset >> 8); + p->idx += count + 7; + } else { + dst_offset = radeon_get_ib_value(p, idx+1); + dst_offset |= ((u64)(radeon_get_ib_value(p, idx+2) & 0xff)) << 32; + + ib[idx+1] += (u32)(dst_reloc->lobj.gpu_offset & 0xfffffffc); + ib[idx+2] += upper_32_bits(dst_reloc->lobj.gpu_offset) & 0xff; + p->idx += count + 3; + } + if ((dst_offset + (count * 4)) > radeon_bo_size(dst_reloc->robj)) { + dev_warn(p->dev, "DMA write buffer too small (%ju %lu)\n", + (uintmax_t)dst_offset, radeon_bo_size(dst_reloc->robj)); + return -EINVAL; + } + break; + case DMA_PACKET_COPY: + r = r600_dma_cs_next_reloc(p, &src_reloc); + if (r) { + DRM_ERROR("bad DMA_PACKET_COPY\n"); + return -EINVAL; + } + r = r600_dma_cs_next_reloc(p, &dst_reloc); + if (r) { + DRM_ERROR("bad DMA_PACKET_COPY\n"); + return -EINVAL; + } + if (tiled) { + idx_value = radeon_get_ib_value(p, idx + 2); + if (new_cmd) { + switch (misc) { + case 0: + /* L2T, frame to fields */ + if (idx_value & (1 << 31)) { + DRM_ERROR("bad L2T, frame to fields DMA_PACKET_COPY\n"); + return -EINVAL; + } + r = r600_dma_cs_next_reloc(p, &dst2_reloc); + if (r) { + DRM_ERROR("bad L2T, frame to fields DMA_PACKET_COPY\n"); + return -EINVAL; + } + dst_offset = radeon_get_ib_value(p, idx+1); + dst_offset <<= 8; + dst2_offset = radeon_get_ib_value(p, idx+2); + dst2_offset <<= 8; + src_offset = radeon_get_ib_value(p, idx+8); + src_offset |= ((u64)(radeon_get_ib_value(p, idx+9) & 0xff)) << 32; + if ((src_offset + (count * 4)) > radeon_bo_size(src_reloc->robj)) { + dev_warn(p->dev, "DMA L2T, frame to fields src buffer too small (%ju %lu)\n", + (uintmax_t)src_offset + (count * 4), radeon_bo_size(src_reloc->robj)); + return -EINVAL; + } + if ((dst_offset + (count * 4)) > radeon_bo_size(dst_reloc->robj)) { + dev_warn(p->dev, "DMA L2T, frame to fields buffer too small (%ju %lu)\n", + (uintmax_t)dst_offset + (count * 4), radeon_bo_size(dst_reloc->robj)); + return -EINVAL; + } + if ((dst2_offset + (count * 4)) > radeon_bo_size(dst2_reloc->robj)) { + dev_warn(p->dev, "DMA L2T, frame to fields buffer too small (%ju %lu)\n", + (uintmax_t)dst2_offset + (count * 4), radeon_bo_size(dst2_reloc->robj)); + return -EINVAL; + } + ib[idx+1] += (u32)(dst_reloc->lobj.gpu_offset >> 8); + ib[idx+2] += (u32)(dst2_reloc->lobj.gpu_offset >> 8); + ib[idx+8] += (u32)(src_reloc->lobj.gpu_offset & 0xfffffffc); + ib[idx+9] += upper_32_bits(src_reloc->lobj.gpu_offset) & 0xff; + p->idx += 10; + break; + case 1: + /* L2T, T2L partial */ + if (p->family < CHIP_CAYMAN) { + DRM_ERROR("L2T, T2L Partial is cayman only !\n"); + return -EINVAL; + } + /* detile bit */ + if (idx_value & (1 << 31)) { + /* tiled src, linear dst */ + ib[idx+1] += (u32)(src_reloc->lobj.gpu_offset >> 8); + + ib[idx+7] += (u32)(dst_reloc->lobj.gpu_offset & 0xfffffffc); + ib[idx+8] += upper_32_bits(dst_reloc->lobj.gpu_offset) & 0xff; + } else { + /* linear src, tiled dst */ + ib[idx+7] += (u32)(src_reloc->lobj.gpu_offset & 0xfffffffc); + ib[idx+8] += upper_32_bits(src_reloc->lobj.gpu_offset) & 0xff; + + ib[idx+1] += (u32)(dst_reloc->lobj.gpu_offset >> 8); + } + p->idx += 12; + break; + case 3: + /* L2T, broadcast */ + if (idx_value & (1 << 31)) { + DRM_ERROR("bad L2T, broadcast DMA_PACKET_COPY\n"); + return -EINVAL; + } + r = r600_dma_cs_next_reloc(p, &dst2_reloc); + if (r) { + DRM_ERROR("bad L2T, broadcast DMA_PACKET_COPY\n"); + return -EINVAL; + } + dst_offset = radeon_get_ib_value(p, idx+1); + dst_offset <<= 8; + dst2_offset = radeon_get_ib_value(p, idx+2); + dst2_offset <<= 8; + src_offset = radeon_get_ib_value(p, idx+8); + src_offset |= ((u64)(radeon_get_ib_value(p, idx+9) & 0xff)) << 32; + if ((src_offset + (count * 4)) > radeon_bo_size(src_reloc->robj)) { + dev_warn(p->dev, "DMA L2T, broadcast src buffer too small (%ju %lu)\n", + (uintmax_t)src_offset + (count * 4), radeon_bo_size(src_reloc->robj)); + return -EINVAL; + } + if ((dst_offset + (count * 4)) > radeon_bo_size(dst_reloc->robj)) { + dev_warn(p->dev, "DMA L2T, broadcast dst buffer too small (%ju %lu)\n", + (uintmax_t)dst_offset + (count * 4), radeon_bo_size(dst_reloc->robj)); + return -EINVAL; + } + if ((dst2_offset + (count * 4)) > radeon_bo_size(dst2_reloc->robj)) { + dev_warn(p->dev, "DMA L2T, broadcast dst2 buffer too small (%ju %lu)\n", + (uintmax_t)dst2_offset + (count * 4), radeon_bo_size(dst2_reloc->robj)); + return -EINVAL; + } + ib[idx+1] += (u32)(dst_reloc->lobj.gpu_offset >> 8); + ib[idx+2] += (u32)(dst2_reloc->lobj.gpu_offset >> 8); + ib[idx+8] += (u32)(src_reloc->lobj.gpu_offset & 0xfffffffc); + ib[idx+9] += upper_32_bits(src_reloc->lobj.gpu_offset) & 0xff; + p->idx += 10; + break; + case 4: + /* L2T, T2L */ + /* detile bit */ + if (idx_value & (1 << 31)) { + /* tiled src, linear dst */ + src_offset = radeon_get_ib_value(p, idx+1); + src_offset <<= 8; + ib[idx+1] += (u32)(src_reloc->lobj.gpu_offset >> 8); + + dst_offset = radeon_get_ib_value(p, idx+7); + dst_offset |= ((u64)(radeon_get_ib_value(p, idx+8) & 0xff)) << 32; + ib[idx+7] += (u32)(dst_reloc->lobj.gpu_offset & 0xfffffffc); + ib[idx+8] += upper_32_bits(dst_reloc->lobj.gpu_offset) & 0xff; + } else { + /* linear src, tiled dst */ + src_offset = radeon_get_ib_value(p, idx+7); + src_offset |= ((u64)(radeon_get_ib_value(p, idx+8) & 0xff)) << 32; + ib[idx+7] += (u32)(src_reloc->lobj.gpu_offset & 0xfffffffc); + ib[idx+8] += upper_32_bits(src_reloc->lobj.gpu_offset) & 0xff; + + dst_offset = radeon_get_ib_value(p, idx+1); + dst_offset <<= 8; + ib[idx+1] += (u32)(dst_reloc->lobj.gpu_offset >> 8); + } + if ((src_offset + (count * 4)) > radeon_bo_size(src_reloc->robj)) { + dev_warn(p->dev, "DMA L2T, T2L src buffer too small (%ju %lu)\n", + (uintmax_t)src_offset + (count * 4), radeon_bo_size(src_reloc->robj)); + return -EINVAL; + } + if ((dst_offset + (count * 4)) > radeon_bo_size(dst_reloc->robj)) { + dev_warn(p->dev, "DMA L2T, T2L dst buffer too small (%ju %lu)\n", + (uintmax_t)dst_offset + (count * 4), radeon_bo_size(dst_reloc->robj)); + return -EINVAL; + } + p->idx += 9; + break; + case 5: + /* T2T partial */ + if (p->family < CHIP_CAYMAN) { + DRM_ERROR("L2T, T2L Partial is cayman only !\n"); + return -EINVAL; + } + ib[idx+1] += (u32)(src_reloc->lobj.gpu_offset >> 8); + ib[idx+4] += (u32)(dst_reloc->lobj.gpu_offset >> 8); + p->idx += 13; + break; + case 7: + /* L2T, broadcast */ + if (idx_value & (1 << 31)) { + DRM_ERROR("bad L2T, broadcast DMA_PACKET_COPY\n"); + return -EINVAL; + } + r = r600_dma_cs_next_reloc(p, &dst2_reloc); + if (r) { + DRM_ERROR("bad L2T, broadcast DMA_PACKET_COPY\n"); + return -EINVAL; + } + dst_offset = radeon_get_ib_value(p, idx+1); + dst_offset <<= 8; + dst2_offset = radeon_get_ib_value(p, idx+2); + dst2_offset <<= 8; + src_offset = radeon_get_ib_value(p, idx+8); + src_offset |= ((u64)(radeon_get_ib_value(p, idx+9) & 0xff)) << 32; + if ((src_offset + (count * 4)) > radeon_bo_size(src_reloc->robj)) { + dev_warn(p->dev, "DMA L2T, broadcast src buffer too small (%ju %lu)\n", + (uintmax_t)src_offset + (count * 4), radeon_bo_size(src_reloc->robj)); + return -EINVAL; + } + if ((dst_offset + (count * 4)) > radeon_bo_size(dst_reloc->robj)) { + dev_warn(p->dev, "DMA L2T, broadcast dst buffer too small (%ju %lu)\n", + (uintmax_t)dst_offset + (count * 4), radeon_bo_size(dst_reloc->robj)); + return -EINVAL; + } + if ((dst2_offset + (count * 4)) > radeon_bo_size(dst2_reloc->robj)) { + dev_warn(p->dev, "DMA L2T, broadcast dst2 buffer too small (%ju %lu)\n", + (uintmax_t)dst2_offset + (count * 4), radeon_bo_size(dst2_reloc->robj)); + return -EINVAL; + } + ib[idx+1] += (u32)(dst_reloc->lobj.gpu_offset >> 8); + ib[idx+2] += (u32)(dst2_reloc->lobj.gpu_offset >> 8); + ib[idx+8] += (u32)(src_reloc->lobj.gpu_offset & 0xfffffffc); + ib[idx+9] += upper_32_bits(src_reloc->lobj.gpu_offset) & 0xff; + p->idx += 10; + break; + default: + DRM_ERROR("bad DMA_PACKET_COPY misc %u\n", misc); + return -EINVAL; + } + } else { + switch (misc) { + case 0: + /* detile bit */ + if (idx_value & (1 << 31)) { + /* tiled src, linear dst */ + src_offset = radeon_get_ib_value(p, idx+1); + src_offset <<= 8; + ib[idx+1] += (u32)(src_reloc->lobj.gpu_offset >> 8); + + dst_offset = radeon_get_ib_value(p, idx+7); + dst_offset |= ((u64)(radeon_get_ib_value(p, idx+8) & 0xff)) << 32; + ib[idx+7] += (u32)(dst_reloc->lobj.gpu_offset & 0xfffffffc); + ib[idx+8] += upper_32_bits(dst_reloc->lobj.gpu_offset) & 0xff; + } else { + /* linear src, tiled dst */ + src_offset = radeon_get_ib_value(p, idx+7); + src_offset |= ((u64)(radeon_get_ib_value(p, idx+8) & 0xff)) << 32; + ib[idx+7] += (u32)(src_reloc->lobj.gpu_offset & 0xfffffffc); + ib[idx+8] += upper_32_bits(src_reloc->lobj.gpu_offset) & 0xff; + + dst_offset = radeon_get_ib_value(p, idx+1); + dst_offset <<= 8; + ib[idx+1] += (u32)(dst_reloc->lobj.gpu_offset >> 8); + } + if ((src_offset + (count * 4)) > radeon_bo_size(src_reloc->robj)) { + dev_warn(p->dev, "DMA L2T, broadcast src buffer too small (%ju %lu)\n", + (uintmax_t)src_offset + (count * 4), radeon_bo_size(src_reloc->robj)); + return -EINVAL; + } + if ((dst_offset + (count * 4)) > radeon_bo_size(dst_reloc->robj)) { + dev_warn(p->dev, "DMA L2T, broadcast dst buffer too small (%ju %lu)\n", + (uintmax_t)dst_offset + (count * 4), radeon_bo_size(dst_reloc->robj)); + return -EINVAL; + } + p->idx += 9; + break; + default: + DRM_ERROR("bad DMA_PACKET_COPY misc %u\n", misc); + return -EINVAL; + } + } + } else { + if (new_cmd) { + switch (misc) { + case 0: + /* L2L, byte */ + src_offset = radeon_get_ib_value(p, idx+2); + src_offset |= ((u64)(radeon_get_ib_value(p, idx+4) & 0xff)) << 32; + dst_offset = radeon_get_ib_value(p, idx+1); + dst_offset |= ((u64)(radeon_get_ib_value(p, idx+3) & 0xff)) << 32; + if ((src_offset + count) > radeon_bo_size(src_reloc->robj)) { + dev_warn(p->dev, "DMA L2L, byte src buffer too small (%ju %lu)\n", + (uintmax_t)src_offset + count, radeon_bo_size(src_reloc->robj)); + return -EINVAL; + } + if ((dst_offset + count) > radeon_bo_size(dst_reloc->robj)) { + dev_warn(p->dev, "DMA L2L, byte dst buffer too small (%ju %lu)\n", + (uintmax_t)dst_offset + count, radeon_bo_size(dst_reloc->robj)); + return -EINVAL; + } + ib[idx+1] += (u32)(dst_reloc->lobj.gpu_offset & 0xffffffff); + ib[idx+2] += (u32)(src_reloc->lobj.gpu_offset & 0xffffffff); + ib[idx+3] += upper_32_bits(dst_reloc->lobj.gpu_offset) & 0xff; + ib[idx+4] += upper_32_bits(src_reloc->lobj.gpu_offset) & 0xff; + p->idx += 5; + break; + case 1: + /* L2L, partial */ + if (p->family < CHIP_CAYMAN) { + DRM_ERROR("L2L Partial is cayman only !\n"); + return -EINVAL; + } + ib[idx+1] += (u32)(src_reloc->lobj.gpu_offset & 0xffffffff); + ib[idx+2] += upper_32_bits(src_reloc->lobj.gpu_offset) & 0xff; + ib[idx+4] += (u32)(dst_reloc->lobj.gpu_offset & 0xffffffff); + ib[idx+5] += upper_32_bits(dst_reloc->lobj.gpu_offset) & 0xff; + + p->idx += 9; + break; + case 4: + /* L2L, dw, broadcast */ + r = r600_dma_cs_next_reloc(p, &dst2_reloc); + if (r) { + DRM_ERROR("bad L2L, dw, broadcast DMA_PACKET_COPY\n"); + return -EINVAL; + } + dst_offset = radeon_get_ib_value(p, idx+1); + dst_offset |= ((u64)(radeon_get_ib_value(p, idx+4) & 0xff)) << 32; + dst2_offset = radeon_get_ib_value(p, idx+2); + dst2_offset |= ((u64)(radeon_get_ib_value(p, idx+5) & 0xff)) << 32; + src_offset = radeon_get_ib_value(p, idx+3); + src_offset |= ((u64)(radeon_get_ib_value(p, idx+6) & 0xff)) << 32; + if ((src_offset + (count * 4)) > radeon_bo_size(src_reloc->robj)) { + dev_warn(p->dev, "DMA L2L, dw, broadcast src buffer too small (%ju %lu)\n", + (uintmax_t)src_offset + (count * 4), radeon_bo_size(src_reloc->robj)); + return -EINVAL; + } + if ((dst_offset + (count * 4)) > radeon_bo_size(dst_reloc->robj)) { + dev_warn(p->dev, "DMA L2L, dw, broadcast dst buffer too small (%ju %lu)\n", + (uintmax_t)dst_offset + (count * 4), radeon_bo_size(dst_reloc->robj)); + return -EINVAL; + } + if ((dst2_offset + (count * 4)) > radeon_bo_size(dst2_reloc->robj)) { + dev_warn(p->dev, "DMA L2L, dw, broadcast dst2 buffer too small (%ju %lu)\n", + (uintmax_t)dst2_offset + (count * 4), radeon_bo_size(dst2_reloc->robj)); + return -EINVAL; + } + ib[idx+1] += (u32)(dst_reloc->lobj.gpu_offset & 0xfffffffc); + ib[idx+2] += (u32)(dst2_reloc->lobj.gpu_offset & 0xfffffffc); + ib[idx+3] += (u32)(src_reloc->lobj.gpu_offset & 0xfffffffc); + ib[idx+4] += upper_32_bits(dst_reloc->lobj.gpu_offset) & 0xff; + ib[idx+5] += upper_32_bits(dst2_reloc->lobj.gpu_offset) & 0xff; + ib[idx+6] += upper_32_bits(src_reloc->lobj.gpu_offset) & 0xff; + p->idx += 7; + break; + default: + DRM_ERROR("bad DMA_PACKET_COPY misc %u\n", misc); + return -EINVAL; + } + } else { + /* L2L, dw */ + src_offset = radeon_get_ib_value(p, idx+2); + src_offset |= ((u64)(radeon_get_ib_value(p, idx+4) & 0xff)) << 32; + dst_offset = radeon_get_ib_value(p, idx+1); + dst_offset |= ((u64)(radeon_get_ib_value(p, idx+3) & 0xff)) << 32; + if ((src_offset + (count * 4)) > radeon_bo_size(src_reloc->robj)) { + dev_warn(p->dev, "DMA L2L, dw src buffer too small (%ju %lu)\n", + (uintmax_t)src_offset + (count * 4), radeon_bo_size(src_reloc->robj)); + return -EINVAL; + } + if ((dst_offset + (count * 4)) > radeon_bo_size(dst_reloc->robj)) { + dev_warn(p->dev, "DMA L2L, dw dst buffer too small (%ju %lu)\n", + (uintmax_t)dst_offset + (count * 4), radeon_bo_size(dst_reloc->robj)); + return -EINVAL; + } + ib[idx+1] += (u32)(dst_reloc->lobj.gpu_offset & 0xfffffffc); + ib[idx+2] += (u32)(src_reloc->lobj.gpu_offset & 0xfffffffc); + ib[idx+3] += upper_32_bits(dst_reloc->lobj.gpu_offset) & 0xff; + ib[idx+4] += upper_32_bits(src_reloc->lobj.gpu_offset) & 0xff; + p->idx += 5; + } + } + break; + case DMA_PACKET_CONSTANT_FILL: + r = r600_dma_cs_next_reloc(p, &dst_reloc); + if (r) { + DRM_ERROR("bad DMA_PACKET_CONSTANT_FILL\n"); + return -EINVAL; + } + dst_offset = radeon_get_ib_value(p, idx+1); + dst_offset |= ((u64)(radeon_get_ib_value(p, idx+3) & 0x00ff0000)) << 16; + if ((dst_offset + (count * 4)) > radeon_bo_size(dst_reloc->robj)) { + dev_warn(p->dev, "DMA constant fill buffer too small (%ju %lu)\n", + (uintmax_t)dst_offset, radeon_bo_size(dst_reloc->robj)); + return -EINVAL; + } + ib[idx+1] += (u32)(dst_reloc->lobj.gpu_offset & 0xfffffffc); + ib[idx+3] += (upper_32_bits(dst_reloc->lobj.gpu_offset) << 16) & 0x00ff0000; + p->idx += 4; + break; + case DMA_PACKET_NOP: + p->idx += 1; + break; + default: + DRM_ERROR("Unknown packet type %d at %d !\n", cmd, idx); + return -EINVAL; + } + } while (p->idx < p->chunks[p->chunk_ib_idx].length_dw); +#if 0 + for (r = 0; r < p->ib->length_dw; r++) { + DRM_INFO("%05d 0x%08X\n", r, p->ib.ptr[r]); + mdelay(1); + } +#endif + return 0; +} + +/* vm parser */ +static bool evergreen_vm_reg_valid(u32 reg) +{ + /* context regs are fine */ + if (reg >= 0x28000) + return true; + + /* check config regs */ + switch (reg) { + case WAIT_UNTIL: + case GRBM_GFX_INDEX: + case CP_STRMOUT_CNTL: + case CP_COHER_CNTL: + case CP_COHER_SIZE: + case VGT_VTX_VECT_EJECT_REG: + case VGT_CACHE_INVALIDATION: + case VGT_GS_VERTEX_REUSE: + case VGT_PRIMITIVE_TYPE: + case VGT_INDEX_TYPE: + case VGT_NUM_INDICES: + case VGT_NUM_INSTANCES: + case VGT_COMPUTE_DIM_X: + case VGT_COMPUTE_DIM_Y: + case VGT_COMPUTE_DIM_Z: + case VGT_COMPUTE_START_X: + case VGT_COMPUTE_START_Y: + case VGT_COMPUTE_START_Z: + case VGT_COMPUTE_INDEX: + case VGT_COMPUTE_THREAD_GROUP_SIZE: + case VGT_HS_OFFCHIP_PARAM: + case PA_CL_ENHANCE: + case PA_SU_LINE_STIPPLE_VALUE: + case PA_SC_LINE_STIPPLE_STATE: + case PA_SC_ENHANCE: + case SQ_DYN_GPR_CNTL_PS_FLUSH_REQ: + case SQ_DYN_GPR_SIMD_LOCK_EN: + case SQ_CONFIG: + case SQ_GPR_RESOURCE_MGMT_1: + case SQ_GLOBAL_GPR_RESOURCE_MGMT_1: + case SQ_GLOBAL_GPR_RESOURCE_MGMT_2: + case SQ_CONST_MEM_BASE: + case SQ_STATIC_THREAD_MGMT_1: + case SQ_STATIC_THREAD_MGMT_2: + case SQ_STATIC_THREAD_MGMT_3: + case SPI_CONFIG_CNTL: + case SPI_CONFIG_CNTL_1: + case TA_CNTL_AUX: + case DB_DEBUG: + case DB_DEBUG2: + case DB_DEBUG3: + case DB_DEBUG4: + case DB_WATERMARKS: + case TD_PS_BORDER_COLOR_INDEX: + case TD_PS_BORDER_COLOR_RED: + case TD_PS_BORDER_COLOR_GREEN: + case TD_PS_BORDER_COLOR_BLUE: + case TD_PS_BORDER_COLOR_ALPHA: + case TD_VS_BORDER_COLOR_INDEX: + case TD_VS_BORDER_COLOR_RED: + case TD_VS_BORDER_COLOR_GREEN: + case TD_VS_BORDER_COLOR_BLUE: + case TD_VS_BORDER_COLOR_ALPHA: + case TD_GS_BORDER_COLOR_INDEX: + case TD_GS_BORDER_COLOR_RED: + case TD_GS_BORDER_COLOR_GREEN: + case TD_GS_BORDER_COLOR_BLUE: + case TD_GS_BORDER_COLOR_ALPHA: + case TD_HS_BORDER_COLOR_INDEX: + case TD_HS_BORDER_COLOR_RED: + case TD_HS_BORDER_COLOR_GREEN: + case TD_HS_BORDER_COLOR_BLUE: + case TD_HS_BORDER_COLOR_ALPHA: + case TD_LS_BORDER_COLOR_INDEX: + case TD_LS_BORDER_COLOR_RED: + case TD_LS_BORDER_COLOR_GREEN: + case TD_LS_BORDER_COLOR_BLUE: + case TD_LS_BORDER_COLOR_ALPHA: + case TD_CS_BORDER_COLOR_INDEX: + case TD_CS_BORDER_COLOR_RED: + case TD_CS_BORDER_COLOR_GREEN: + case TD_CS_BORDER_COLOR_BLUE: + case TD_CS_BORDER_COLOR_ALPHA: + case SQ_ESGS_RING_SIZE: + case SQ_GSVS_RING_SIZE: + case SQ_ESTMP_RING_SIZE: + case SQ_GSTMP_RING_SIZE: + case SQ_HSTMP_RING_SIZE: + case SQ_LSTMP_RING_SIZE: + case SQ_PSTMP_RING_SIZE: + case SQ_VSTMP_RING_SIZE: + case SQ_ESGS_RING_ITEMSIZE: + case SQ_ESTMP_RING_ITEMSIZE: + case SQ_GSTMP_RING_ITEMSIZE: + case SQ_GSVS_RING_ITEMSIZE: + case SQ_GS_VERT_ITEMSIZE: + case SQ_GS_VERT_ITEMSIZE_1: + case SQ_GS_VERT_ITEMSIZE_2: + case SQ_GS_VERT_ITEMSIZE_3: + case SQ_GSVS_RING_OFFSET_1: + case SQ_GSVS_RING_OFFSET_2: + case SQ_GSVS_RING_OFFSET_3: + case SQ_HSTMP_RING_ITEMSIZE: + case SQ_LSTMP_RING_ITEMSIZE: + case SQ_PSTMP_RING_ITEMSIZE: + case SQ_VSTMP_RING_ITEMSIZE: + case VGT_TF_RING_SIZE: + case SQ_ESGS_RING_BASE: + case SQ_GSVS_RING_BASE: + case SQ_ESTMP_RING_BASE: + case SQ_GSTMP_RING_BASE: + case SQ_HSTMP_RING_BASE: + case SQ_LSTMP_RING_BASE: + case SQ_PSTMP_RING_BASE: + case SQ_VSTMP_RING_BASE: + case CAYMAN_VGT_OFFCHIP_LDS_BASE: + case CAYMAN_SQ_EX_ALLOC_TABLE_SLOTS: + return true; + default: + DRM_ERROR("Invalid register 0x%x in CS\n", reg); + return false; + } +} + +static int evergreen_vm_packet3_check(struct radeon_device *rdev, + u32 *ib, struct radeon_cs_packet *pkt) +{ + u32 idx = pkt->idx + 1; + u32 idx_value = ib[idx]; + u32 start_reg, end_reg, reg, i; + u32 command, info; + + switch (pkt->opcode) { + case PACKET3_NOP: + case PACKET3_SET_BASE: + case PACKET3_CLEAR_STATE: + case PACKET3_INDEX_BUFFER_SIZE: + case PACKET3_DISPATCH_DIRECT: + case PACKET3_DISPATCH_INDIRECT: + case PACKET3_MODE_CONTROL: + case PACKET3_SET_PREDICATION: + case PACKET3_COND_EXEC: + case PACKET3_PRED_EXEC: + case PACKET3_DRAW_INDIRECT: + case PACKET3_DRAW_INDEX_INDIRECT: + case PACKET3_INDEX_BASE: + case PACKET3_DRAW_INDEX_2: + case PACKET3_CONTEXT_CONTROL: + case PACKET3_DRAW_INDEX_OFFSET: + case PACKET3_INDEX_TYPE: + case PACKET3_DRAW_INDEX: + case PACKET3_DRAW_INDEX_AUTO: + case PACKET3_DRAW_INDEX_IMMD: + case PACKET3_NUM_INSTANCES: + case PACKET3_DRAW_INDEX_MULTI_AUTO: + case PACKET3_STRMOUT_BUFFER_UPDATE: + case PACKET3_DRAW_INDEX_OFFSET_2: + case PACKET3_DRAW_INDEX_MULTI_ELEMENT: + case PACKET3_MPEG_INDEX: + case PACKET3_WAIT_REG_MEM: + case PACKET3_MEM_WRITE: + case PACKET3_SURFACE_SYNC: + case PACKET3_EVENT_WRITE: + case PACKET3_EVENT_WRITE_EOP: + case PACKET3_EVENT_WRITE_EOS: + case PACKET3_SET_CONTEXT_REG: + case PACKET3_SET_BOOL_CONST: + case PACKET3_SET_LOOP_CONST: + case PACKET3_SET_RESOURCE: + case PACKET3_SET_SAMPLER: + case PACKET3_SET_CTL_CONST: + case PACKET3_SET_RESOURCE_OFFSET: + case PACKET3_SET_CONTEXT_REG_INDIRECT: + case PACKET3_SET_RESOURCE_INDIRECT: + case CAYMAN_PACKET3_DEALLOC_STATE: + break; + case PACKET3_COND_WRITE: + if (idx_value & 0x100) { + reg = ib[idx + 5] * 4; + if (!evergreen_vm_reg_valid(reg)) + return -EINVAL; + } + break; + case PACKET3_COPY_DW: + if (idx_value & 0x2) { + reg = ib[idx + 3] * 4; + if (!evergreen_vm_reg_valid(reg)) + return -EINVAL; + } + break; + case PACKET3_SET_CONFIG_REG: + start_reg = (idx_value << 2) + PACKET3_SET_CONFIG_REG_START; + end_reg = 4 * pkt->count + start_reg - 4; + if ((start_reg < PACKET3_SET_CONFIG_REG_START) || + (start_reg >= PACKET3_SET_CONFIG_REG_END) || + (end_reg >= PACKET3_SET_CONFIG_REG_END)) { + DRM_ERROR("bad PACKET3_SET_CONFIG_REG\n"); + return -EINVAL; + } + for (i = 0; i < pkt->count; i++) { + reg = start_reg + (4 * i); + if (!evergreen_vm_reg_valid(reg)) + return -EINVAL; + } + break; + case PACKET3_CP_DMA: + command = ib[idx + 4]; + info = ib[idx + 1]; + if ((((info & 0x60000000) >> 29) != 0) || /* src = GDS or DATA */ + (((info & 0x00300000) >> 20) != 0) || /* dst = GDS */ + ((((info & 0x00300000) >> 20) == 0) && + (command & PACKET3_CP_DMA_CMD_DAS)) || /* dst = register */ + ((((info & 0x60000000) >> 29) == 0) && + (command & PACKET3_CP_DMA_CMD_SAS))) { /* src = register */ + /* non mem to mem copies requires dw aligned count */ + if ((command & 0x1fffff) % 4) { + DRM_ERROR("CP DMA command requires dw count alignment\n"); + return -EINVAL; + } + } + if (command & PACKET3_CP_DMA_CMD_SAS) { + /* src address space is register */ + if (((info & 0x60000000) >> 29) == 0) { + start_reg = idx_value << 2; + if (command & PACKET3_CP_DMA_CMD_SAIC) { + reg = start_reg; + if (!evergreen_vm_reg_valid(reg)) { + DRM_ERROR("CP DMA Bad SRC register\n"); + return -EINVAL; + } + } else { + for (i = 0; i < (command & 0x1fffff); i++) { + reg = start_reg + (4 * i); + if (!evergreen_vm_reg_valid(reg)) { + DRM_ERROR("CP DMA Bad SRC register\n"); + return -EINVAL; + } + } + } + } + } + if (command & PACKET3_CP_DMA_CMD_DAS) { + /* dst address space is register */ + if (((info & 0x00300000) >> 20) == 0) { + start_reg = ib[idx + 2]; + if (command & PACKET3_CP_DMA_CMD_DAIC) { + reg = start_reg; + if (!evergreen_vm_reg_valid(reg)) { + DRM_ERROR("CP DMA Bad DST register\n"); + return -EINVAL; + } + } else { + for (i = 0; i < (command & 0x1fffff); i++) { + reg = start_reg + (4 * i); + if (!evergreen_vm_reg_valid(reg)) { + DRM_ERROR("CP DMA Bad DST register\n"); + return -EINVAL; + } + } + } + } + } + break; + default: + return -EINVAL; + } + return 0; +} + +int evergreen_ib_parse(struct radeon_device *rdev, struct radeon_ib *ib) +{ + int ret = 0; + u32 idx = 0; + struct radeon_cs_packet pkt; + + do { + pkt.idx = idx; + pkt.type = CP_PACKET_GET_TYPE(ib->ptr[idx]); + pkt.count = CP_PACKET_GET_COUNT(ib->ptr[idx]); + pkt.one_reg_wr = 0; + switch (pkt.type) { + case PACKET_TYPE0: + dev_err(rdev->dev, "Packet0 not allowed!\n"); + ret = -EINVAL; + break; + case PACKET_TYPE2: + idx += 1; + break; + case PACKET_TYPE3: + pkt.opcode = CP_PACKET3_GET_OPCODE(ib->ptr[idx]); + ret = evergreen_vm_packet3_check(rdev, ib->ptr, &pkt); + idx += pkt.count + 2; + break; + default: + dev_err(rdev->dev, "Unknown packet type %d !\n", pkt.type); + ret = -EINVAL; + break; + } + if (ret) + break; + } while (idx < ib->length_dw); + + return ret; +} + +/** + * evergreen_dma_ib_parse() - parse the DMA IB for VM + * @rdev: radeon_device pointer + * @ib: radeon_ib pointer + * + * Parses the DMA IB from the VM CS ioctl + * checks for errors. (Cayman-SI) + * Returns 0 for success and an error on failure. + **/ +int evergreen_dma_ib_parse(struct radeon_device *rdev, struct radeon_ib *ib) +{ + u32 idx = 0; + u32 header, cmd, count, tiled, new_cmd, misc; + + do { + header = ib->ptr[idx]; + cmd = GET_DMA_CMD(header); + count = GET_DMA_COUNT(header); + tiled = GET_DMA_T(header); + new_cmd = GET_DMA_NEW(header); + misc = GET_DMA_MISC(header); + + switch (cmd) { + case DMA_PACKET_WRITE: + if (tiled) + idx += count + 7; + else + idx += count + 3; + break; + case DMA_PACKET_COPY: + if (tiled) { + if (new_cmd) { + switch (misc) { + case 0: + /* L2T, frame to fields */ + idx += 10; + break; + case 1: + /* L2T, T2L partial */ + idx += 12; + break; + case 3: + /* L2T, broadcast */ + idx += 10; + break; + case 4: + /* L2T, T2L */ + idx += 9; + break; + case 5: + /* T2T partial */ + idx += 13; + break; + case 7: + /* L2T, broadcast */ + idx += 10; + break; + default: + DRM_ERROR("bad DMA_PACKET_COPY misc %u\n", misc); + return -EINVAL; + } + } else { + switch (misc) { + case 0: + idx += 9; + break; + default: + DRM_ERROR("bad DMA_PACKET_COPY misc %u\n", misc); + return -EINVAL; + } + } + } else { + if (new_cmd) { + switch (misc) { + case 0: + /* L2L, byte */ + idx += 5; + break; + case 1: + /* L2L, partial */ + idx += 9; + break; + case 4: + /* L2L, dw, broadcast */ + idx += 7; + break; + default: + DRM_ERROR("bad DMA_PACKET_COPY misc %u\n", misc); + return -EINVAL; + } + } else { + /* L2L, dw */ + idx += 5; + } + } + break; + case DMA_PACKET_CONSTANT_FILL: + idx += 4; + break; + case DMA_PACKET_NOP: + idx += 1; + break; + default: + DRM_ERROR("Unknown packet type %d at %d !\n", cmd, idx); + return -EINVAL; + } + } while (idx < ib->length_dw); + + return 0; +} diff --git a/sys/dev/drm2/radeon/evergreen_hdmi.c b/sys/dev/drm2/radeon/evergreen_hdmi.c new file mode 100644 index 00000000000..e4702dae26c --- /dev/null +++ b/sys/dev/drm2/radeon/evergreen_hdmi.c @@ -0,0 +1,217 @@ +/* + * Copyright 2008 Advanced Micro Devices, Inc. + * Copyright 2008 Red Hat Inc. + * Copyright 2009 Christian König. + * + * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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. + * + * Authors: Christian König + * Rafał Miłecki + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include "radeon.h" +#include "radeon_asic.h" +#include "evergreend.h" +#include "atom.h" + +/* + * update the N and CTS parameters for a given pixel clock rate + */ +static void evergreen_hdmi_update_ACR(struct drm_encoder *encoder, uint32_t clock) +{ + struct drm_device *dev = encoder->dev; + struct radeon_device *rdev = dev->dev_private; + struct radeon_hdmi_acr acr = r600_hdmi_acr(clock); + struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); + struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv; + uint32_t offset = dig->afmt->offset; + + WREG32(HDMI_ACR_32_0 + offset, HDMI_ACR_CTS_32(acr.cts_32khz)); + WREG32(HDMI_ACR_32_1 + offset, acr.n_32khz); + + WREG32(HDMI_ACR_44_0 + offset, HDMI_ACR_CTS_44(acr.cts_44_1khz)); + WREG32(HDMI_ACR_44_1 + offset, acr.n_44_1khz); + + WREG32(HDMI_ACR_48_0 + offset, HDMI_ACR_CTS_48(acr.cts_48khz)); + WREG32(HDMI_ACR_48_1 + offset, acr.n_48khz); +} + +/* + * calculate the crc for a given info frame + */ +static void evergreen_hdmi_infoframe_checksum(uint8_t packetType, + uint8_t versionNumber, + uint8_t length, + uint8_t *frame) +{ + int i; + frame[0] = packetType + versionNumber + length; + for (i = 1; i <= length; i++) + frame[0] += frame[i]; + frame[0] = 0x100 - frame[0]; +} + +/* + * build a HDMI Video Info Frame + */ +static void evergreen_hdmi_videoinfoframe( + struct drm_encoder *encoder, + uint8_t color_format, + int active_information_present, + uint8_t active_format_aspect_ratio, + uint8_t scan_information, + uint8_t colorimetry, + uint8_t ex_colorimetry, + uint8_t quantization, + int ITC, + uint8_t picture_aspect_ratio, + uint8_t video_format_identification, + uint8_t pixel_repetition, + uint8_t non_uniform_picture_scaling, + uint8_t bar_info_data_valid, + uint16_t top_bar, + uint16_t bottom_bar, + uint16_t left_bar, + uint16_t right_bar +) +{ + struct drm_device *dev = encoder->dev; + struct radeon_device *rdev = dev->dev_private; + struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); + struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv; + uint32_t offset = dig->afmt->offset; + + uint8_t frame[14]; + + frame[0x0] = 0; + frame[0x1] = + (scan_information & 0x3) | + ((bar_info_data_valid & 0x3) << 2) | + ((active_information_present & 0x1) << 4) | + ((color_format & 0x3) << 5); + frame[0x2] = + (active_format_aspect_ratio & 0xF) | + ((picture_aspect_ratio & 0x3) << 4) | + ((colorimetry & 0x3) << 6); + frame[0x3] = + (non_uniform_picture_scaling & 0x3) | + ((quantization & 0x3) << 2) | + ((ex_colorimetry & 0x7) << 4) | + ((ITC & 0x1) << 7); + frame[0x4] = (video_format_identification & 0x7F); + frame[0x5] = (pixel_repetition & 0xF); + frame[0x6] = (top_bar & 0xFF); + frame[0x7] = (top_bar >> 8); + frame[0x8] = (bottom_bar & 0xFF); + frame[0x9] = (bottom_bar >> 8); + frame[0xA] = (left_bar & 0xFF); + frame[0xB] = (left_bar >> 8); + frame[0xC] = (right_bar & 0xFF); + frame[0xD] = (right_bar >> 8); + + evergreen_hdmi_infoframe_checksum(0x82, 0x02, 0x0D, frame); + /* Our header values (type, version, length) should be alright, Intel + * is using the same. Checksum function also seems to be OK, it works + * fine for audio infoframe. However calculated value is always lower + * by 2 in comparison to fglrx. It breaks displaying anything in case + * of TVs that strictly check the checksum. Hack it manually here to + * workaround this issue. */ + frame[0x0] += 2; + + WREG32(AFMT_AVI_INFO0 + offset, + frame[0x0] | (frame[0x1] << 8) | (frame[0x2] << 16) | (frame[0x3] << 24)); + WREG32(AFMT_AVI_INFO1 + offset, + frame[0x4] | (frame[0x5] << 8) | (frame[0x6] << 16) | (frame[0x7] << 24)); + WREG32(AFMT_AVI_INFO2 + offset, + frame[0x8] | (frame[0x9] << 8) | (frame[0xA] << 16) | (frame[0xB] << 24)); + WREG32(AFMT_AVI_INFO3 + offset, + frame[0xC] | (frame[0xD] << 8)); +} + +/* + * update the info frames with the data from the current display mode + */ +void evergreen_hdmi_setmode(struct drm_encoder *encoder, struct drm_display_mode *mode) +{ + struct drm_device *dev = encoder->dev; + struct radeon_device *rdev = dev->dev_private; + struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); + struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv; + uint32_t offset; + + /* Silent, r600_hdmi_enable will raise WARN for us */ + if (!dig->afmt->enabled) + return; + offset = dig->afmt->offset; + + r600_audio_set_clock(encoder, mode->clock); + + WREG32(HDMI_VBI_PACKET_CONTROL + offset, + HDMI_NULL_SEND); /* send null packets when required */ + + WREG32(AFMT_AUDIO_CRC_CONTROL + offset, 0x1000); + + WREG32(HDMI_AUDIO_PACKET_CONTROL + offset, + HDMI_AUDIO_DELAY_EN(1) | /* set the default audio delay */ + HDMI_AUDIO_PACKETS_PER_LINE(3)); /* should be suffient for all audio modes and small enough for all hblanks */ + + WREG32(AFMT_AUDIO_PACKET_CONTROL + offset, + AFMT_AUDIO_SAMPLE_SEND | /* send audio packets */ + AFMT_60958_CS_UPDATE); /* allow 60958 channel status fields to be updated */ + + WREG32(HDMI_ACR_PACKET_CONTROL + offset, + HDMI_ACR_AUTO_SEND | /* allow hw to sent ACR packets when required */ + HDMI_ACR_SOURCE); /* select SW CTS value */ + + WREG32(HDMI_VBI_PACKET_CONTROL + offset, + HDMI_NULL_SEND | /* send null packets when required */ + HDMI_GC_SEND | /* send general control packets */ + HDMI_GC_CONT); /* send general control packets every frame */ + + WREG32(HDMI_INFOFRAME_CONTROL0 + offset, + HDMI_AVI_INFO_SEND | /* enable AVI info frames */ + HDMI_AVI_INFO_CONT | /* send AVI info frames every frame/field */ + HDMI_AUDIO_INFO_SEND | /* enable audio info frames (frames won't be set until audio is enabled) */ + HDMI_AUDIO_INFO_CONT); /* required for audio info values to be updated */ + + WREG32(AFMT_INFOFRAME_CONTROL0 + offset, + AFMT_AUDIO_INFO_UPDATE); /* required for audio info values to be updated */ + + WREG32(HDMI_INFOFRAME_CONTROL1 + offset, + HDMI_AVI_INFO_LINE(2) | /* anything other than 0 */ + HDMI_AUDIO_INFO_LINE(2)); /* anything other than 0 */ + + WREG32(HDMI_GC + offset, 0); /* unset HDMI_GC_AVMUTE */ + + evergreen_hdmi_videoinfoframe(encoder, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0); + + evergreen_hdmi_update_ACR(encoder, mode->clock); + + /* it's unknown what these bits do excatly, but it's indeed quite useful for debugging */ + WREG32(AFMT_RAMP_CONTROL0 + offset, 0x00FFFFFF); + WREG32(AFMT_RAMP_CONTROL1 + offset, 0x007FFFFF); + WREG32(AFMT_RAMP_CONTROL2 + offset, 0x00000001); + WREG32(AFMT_RAMP_CONTROL3 + offset, 0x00000001); +} diff --git a/sys/dev/drm2/radeon/evergreen_reg.h b/sys/dev/drm2/radeon/evergreen_reg.h new file mode 100644 index 00000000000..7603168d274 --- /dev/null +++ b/sys/dev/drm2/radeon/evergreen_reg.h @@ -0,0 +1,241 @@ +/* + * Copyright 2010 Advanced Micro Devices, Inc. + * + * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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. + * + * Authors: Alex Deucher + */ + +#include +__FBSDID("$FreeBSD$"); + +#ifndef __EVERGREEN_REG_H__ +#define __EVERGREEN_REG_H__ + +/* evergreen */ +#define EVERGREEN_VGA_MEMORY_BASE_ADDRESS 0x310 +#define EVERGREEN_VGA_MEMORY_BASE_ADDRESS_HIGH 0x324 +#define EVERGREEN_D3VGA_CONTROL 0x3e0 +#define EVERGREEN_D4VGA_CONTROL 0x3e4 +#define EVERGREEN_D5VGA_CONTROL 0x3e8 +#define EVERGREEN_D6VGA_CONTROL 0x3ec + +#define EVERGREEN_P1PLL_SS_CNTL 0x414 +#define EVERGREEN_P2PLL_SS_CNTL 0x454 +# define EVERGREEN_PxPLL_SS_EN (1 << 12) + +#define EVERGREEN_AUDIO_PLL1_MUL 0x5b0 +#define EVERGREEN_AUDIO_PLL1_DIV 0x5b4 +#define EVERGREEN_AUDIO_PLL1_UNK 0x5bc + +#define EVERGREEN_AUDIO_ENABLE 0x5e78 +#define EVERGREEN_AUDIO_VENDOR_ID 0x5ec0 + +/* GRPH blocks at 0x6800, 0x7400, 0x10000, 0x10c00, 0x11800, 0x12400 */ +#define EVERGREEN_GRPH_ENABLE 0x6800 +#define EVERGREEN_GRPH_CONTROL 0x6804 +# define EVERGREEN_GRPH_DEPTH(x) (((x) & 0x3) << 0) +# define EVERGREEN_GRPH_DEPTH_8BPP 0 +# define EVERGREEN_GRPH_DEPTH_16BPP 1 +# define EVERGREEN_GRPH_DEPTH_32BPP 2 +# define EVERGREEN_GRPH_NUM_BANKS(x) (((x) & 0x3) << 2) +# define EVERGREEN_ADDR_SURF_2_BANK 0 +# define EVERGREEN_ADDR_SURF_4_BANK 1 +# define EVERGREEN_ADDR_SURF_8_BANK 2 +# define EVERGREEN_ADDR_SURF_16_BANK 3 +# define EVERGREEN_GRPH_Z(x) (((x) & 0x3) << 4) +# define EVERGREEN_GRPH_BANK_WIDTH(x) (((x) & 0x3) << 6) +# define EVERGREEN_ADDR_SURF_BANK_WIDTH_1 0 +# define EVERGREEN_ADDR_SURF_BANK_WIDTH_2 1 +# define EVERGREEN_ADDR_SURF_BANK_WIDTH_4 2 +# define EVERGREEN_ADDR_SURF_BANK_WIDTH_8 3 +# define EVERGREEN_GRPH_FORMAT(x) (((x) & 0x7) << 8) +/* 8 BPP */ +# define EVERGREEN_GRPH_FORMAT_INDEXED 0 +/* 16 BPP */ +# define EVERGREEN_GRPH_FORMAT_ARGB1555 0 +# define EVERGREEN_GRPH_FORMAT_ARGB565 1 +# define EVERGREEN_GRPH_FORMAT_ARGB4444 2 +# define EVERGREEN_GRPH_FORMAT_AI88 3 +# define EVERGREEN_GRPH_FORMAT_MONO16 4 +# define EVERGREEN_GRPH_FORMAT_BGRA5551 5 +/* 32 BPP */ +# define EVERGREEN_GRPH_FORMAT_ARGB8888 0 +# define EVERGREEN_GRPH_FORMAT_ARGB2101010 1 +# define EVERGREEN_GRPH_FORMAT_32BPP_DIG 2 +# define EVERGREEN_GRPH_FORMAT_8B_ARGB2101010 3 +# define EVERGREEN_GRPH_FORMAT_BGRA1010102 4 +# define EVERGREEN_GRPH_FORMAT_8B_BGRA1010102 5 +# define EVERGREEN_GRPH_FORMAT_RGB111110 6 +# define EVERGREEN_GRPH_FORMAT_BGR101111 7 +# define EVERGREEN_GRPH_BANK_HEIGHT(x) (((x) & 0x3) << 11) +# define EVERGREEN_ADDR_SURF_BANK_HEIGHT_1 0 +# define EVERGREEN_ADDR_SURF_BANK_HEIGHT_2 1 +# define EVERGREEN_ADDR_SURF_BANK_HEIGHT_4 2 +# define EVERGREEN_ADDR_SURF_BANK_HEIGHT_8 3 +# define EVERGREEN_GRPH_TILE_SPLIT(x) (((x) & 0x7) << 13) +# define EVERGREEN_ADDR_SURF_TILE_SPLIT_64B 0 +# define EVERGREEN_ADDR_SURF_TILE_SPLIT_128B 1 +# define EVERGREEN_ADDR_SURF_TILE_SPLIT_256B 2 +# define EVERGREEN_ADDR_SURF_TILE_SPLIT_512B 3 +# define EVERGREEN_ADDR_SURF_TILE_SPLIT_1KB 4 +# define EVERGREEN_ADDR_SURF_TILE_SPLIT_2KB 5 +# define EVERGREEN_ADDR_SURF_TILE_SPLIT_4KB 6 +# define EVERGREEN_GRPH_MACRO_TILE_ASPECT(x) (((x) & 0x3) << 18) +# define EVERGREEN_ADDR_SURF_MACRO_TILE_ASPECT_1 0 +# define EVERGREEN_ADDR_SURF_MACRO_TILE_ASPECT_2 1 +# define EVERGREEN_ADDR_SURF_MACRO_TILE_ASPECT_4 2 +# define EVERGREEN_ADDR_SURF_MACRO_TILE_ASPECT_8 3 +# define EVERGREEN_GRPH_ARRAY_MODE(x) (((x) & 0x7) << 20) +# define EVERGREEN_GRPH_ARRAY_LINEAR_GENERAL 0 +# define EVERGREEN_GRPH_ARRAY_LINEAR_ALIGNED 1 +# define EVERGREEN_GRPH_ARRAY_1D_TILED_THIN1 2 +# define EVERGREEN_GRPH_ARRAY_2D_TILED_THIN1 4 +#define EVERGREEN_GRPH_SWAP_CONTROL 0x680c +# define EVERGREEN_GRPH_ENDIAN_SWAP(x) (((x) & 0x3) << 0) +# define EVERGREEN_GRPH_ENDIAN_NONE 0 +# define EVERGREEN_GRPH_ENDIAN_8IN16 1 +# define EVERGREEN_GRPH_ENDIAN_8IN32 2 +# define EVERGREEN_GRPH_ENDIAN_8IN64 3 +# define EVERGREEN_GRPH_RED_CROSSBAR(x) (((x) & 0x3) << 4) +# define EVERGREEN_GRPH_RED_SEL_R 0 +# define EVERGREEN_GRPH_RED_SEL_G 1 +# define EVERGREEN_GRPH_RED_SEL_B 2 +# define EVERGREEN_GRPH_RED_SEL_A 3 +# define EVERGREEN_GRPH_GREEN_CROSSBAR(x) (((x) & 0x3) << 6) +# define EVERGREEN_GRPH_GREEN_SEL_G 0 +# define EVERGREEN_GRPH_GREEN_SEL_B 1 +# define EVERGREEN_GRPH_GREEN_SEL_A 2 +# define EVERGREEN_GRPH_GREEN_SEL_R 3 +# define EVERGREEN_GRPH_BLUE_CROSSBAR(x) (((x) & 0x3) << 8) +# define EVERGREEN_GRPH_BLUE_SEL_B 0 +# define EVERGREEN_GRPH_BLUE_SEL_A 1 +# define EVERGREEN_GRPH_BLUE_SEL_R 2 +# define EVERGREEN_GRPH_BLUE_SEL_G 3 +# define EVERGREEN_GRPH_ALPHA_CROSSBAR(x) (((x) & 0x3) << 10) +# define EVERGREEN_GRPH_ALPHA_SEL_A 0 +# define EVERGREEN_GRPH_ALPHA_SEL_R 1 +# define EVERGREEN_GRPH_ALPHA_SEL_G 2 +# define EVERGREEN_GRPH_ALPHA_SEL_B 3 +#define EVERGREEN_GRPH_PRIMARY_SURFACE_ADDRESS 0x6810 +#define EVERGREEN_GRPH_SECONDARY_SURFACE_ADDRESS 0x6814 +# define EVERGREEN_GRPH_DFQ_ENABLE (1 << 0) +# define EVERGREEN_GRPH_SURFACE_ADDRESS_MASK 0xffffff00 +#define EVERGREEN_GRPH_PITCH 0x6818 +#define EVERGREEN_GRPH_PRIMARY_SURFACE_ADDRESS_HIGH 0x681c +#define EVERGREEN_GRPH_SECONDARY_SURFACE_ADDRESS_HIGH 0x6820 +#define EVERGREEN_GRPH_SURFACE_OFFSET_X 0x6824 +#define EVERGREEN_GRPH_SURFACE_OFFSET_Y 0x6828 +#define EVERGREEN_GRPH_X_START 0x682c +#define EVERGREEN_GRPH_Y_START 0x6830 +#define EVERGREEN_GRPH_X_END 0x6834 +#define EVERGREEN_GRPH_Y_END 0x6838 +#define EVERGREEN_GRPH_UPDATE 0x6844 +# define EVERGREEN_GRPH_SURFACE_UPDATE_PENDING (1 << 2) +# define EVERGREEN_GRPH_UPDATE_LOCK (1 << 16) +#define EVERGREEN_GRPH_FLIP_CONTROL 0x6848 +# define EVERGREEN_GRPH_SURFACE_UPDATE_H_RETRACE_EN (1 << 0) + +/* CUR blocks at 0x6998, 0x7598, 0x10198, 0x10d98, 0x11998, 0x12598 */ +#define EVERGREEN_CUR_CONTROL 0x6998 +# define EVERGREEN_CURSOR_EN (1 << 0) +# define EVERGREEN_CURSOR_MODE(x) (((x) & 0x3) << 8) +# define EVERGREEN_CURSOR_MONO 0 +# define EVERGREEN_CURSOR_24_1 1 +# define EVERGREEN_CURSOR_24_8_PRE_MULT 2 +# define EVERGREEN_CURSOR_24_8_UNPRE_MULT 3 +# define EVERGREEN_CURSOR_2X_MAGNIFY (1 << 16) +# define EVERGREEN_CURSOR_FORCE_MC_ON (1 << 20) +# define EVERGREEN_CURSOR_URGENT_CONTROL(x) (((x) & 0x7) << 24) +# define EVERGREEN_CURSOR_URGENT_ALWAYS 0 +# define EVERGREEN_CURSOR_URGENT_1_8 1 +# define EVERGREEN_CURSOR_URGENT_1_4 2 +# define EVERGREEN_CURSOR_URGENT_3_8 3 +# define EVERGREEN_CURSOR_URGENT_1_2 4 +#define EVERGREEN_CUR_SURFACE_ADDRESS 0x699c +# define EVERGREEN_CUR_SURFACE_ADDRESS_MASK 0xfffff000 +#define EVERGREEN_CUR_SIZE 0x69a0 +#define EVERGREEN_CUR_SURFACE_ADDRESS_HIGH 0x69a4 +#define EVERGREEN_CUR_POSITION 0x69a8 +#define EVERGREEN_CUR_HOT_SPOT 0x69ac +#define EVERGREEN_CUR_COLOR1 0x69b0 +#define EVERGREEN_CUR_COLOR2 0x69b4 +#define EVERGREEN_CUR_UPDATE 0x69b8 +# define EVERGREEN_CURSOR_UPDATE_PENDING (1 << 0) +# define EVERGREEN_CURSOR_UPDATE_TAKEN (1 << 1) +# define EVERGREEN_CURSOR_UPDATE_LOCK (1 << 16) +# define EVERGREEN_CURSOR_DISABLE_MULTIPLE_UPDATE (1 << 24) + +/* LUT blocks at 0x69e0, 0x75e0, 0x101e0, 0x10de0, 0x119e0, 0x125e0 */ +#define EVERGREEN_DC_LUT_RW_MODE 0x69e0 +#define EVERGREEN_DC_LUT_RW_INDEX 0x69e4 +#define EVERGREEN_DC_LUT_SEQ_COLOR 0x69e8 +#define EVERGREEN_DC_LUT_PWL_DATA 0x69ec +#define EVERGREEN_DC_LUT_30_COLOR 0x69f0 +#define EVERGREEN_DC_LUT_VGA_ACCESS_ENABLE 0x69f4 +#define EVERGREEN_DC_LUT_WRITE_EN_MASK 0x69f8 +#define EVERGREEN_DC_LUT_AUTOFILL 0x69fc +#define EVERGREEN_DC_LUT_CONTROL 0x6a00 +#define EVERGREEN_DC_LUT_BLACK_OFFSET_BLUE 0x6a04 +#define EVERGREEN_DC_LUT_BLACK_OFFSET_GREEN 0x6a08 +#define EVERGREEN_DC_LUT_BLACK_OFFSET_RED 0x6a0c +#define EVERGREEN_DC_LUT_WHITE_OFFSET_BLUE 0x6a10 +#define EVERGREEN_DC_LUT_WHITE_OFFSET_GREEN 0x6a14 +#define EVERGREEN_DC_LUT_WHITE_OFFSET_RED 0x6a18 + +#define EVERGREEN_DATA_FORMAT 0x6b00 +# define EVERGREEN_INTERLEAVE_EN (1 << 0) +#define EVERGREEN_DESKTOP_HEIGHT 0x6b04 +#define EVERGREEN_VLINE_START_END 0x6b08 +#define EVERGREEN_VLINE_STATUS 0x6bb8 +# define EVERGREEN_VLINE_STAT (1 << 12) + +#define EVERGREEN_VIEWPORT_START 0x6d70 +#define EVERGREEN_VIEWPORT_SIZE 0x6d74 + +/* display controller offsets used for crtc/cur/lut/grph/viewport/etc. */ +#define EVERGREEN_CRTC0_REGISTER_OFFSET (0x6df0 - 0x6df0) +#define EVERGREEN_CRTC1_REGISTER_OFFSET (0x79f0 - 0x6df0) +#define EVERGREEN_CRTC2_REGISTER_OFFSET (0x105f0 - 0x6df0) +#define EVERGREEN_CRTC3_REGISTER_OFFSET (0x111f0 - 0x6df0) +#define EVERGREEN_CRTC4_REGISTER_OFFSET (0x11df0 - 0x6df0) +#define EVERGREEN_CRTC5_REGISTER_OFFSET (0x129f0 - 0x6df0) + +/* CRTC blocks at 0x6df0, 0x79f0, 0x105f0, 0x111f0, 0x11df0, 0x129f0 */ +#define EVERGREEN_CRTC_V_BLANK_START_END 0x6e34 +#define EVERGREEN_CRTC_CONTROL 0x6e70 +# define EVERGREEN_CRTC_MASTER_EN (1 << 0) +# define EVERGREEN_CRTC_DISP_READ_REQUEST_DISABLE (1 << 24) +#define EVERGREEN_CRTC_BLANK_CONTROL 0x6e74 +# define EVERGREEN_CRTC_BLANK_DATA_EN (1 << 8) +#define EVERGREEN_CRTC_STATUS 0x6e8c +# define EVERGREEN_CRTC_V_BLANK (1 << 0) +#define EVERGREEN_CRTC_STATUS_POSITION 0x6e90 +#define EVERGREEN_MASTER_UPDATE_MODE 0x6ef8 +#define EVERGREEN_CRTC_UPDATE_LOCK 0x6ed4 + +#define EVERGREEN_DC_GPIO_HPD_MASK 0x64b0 +#define EVERGREEN_DC_GPIO_HPD_A 0x64b4 +#define EVERGREEN_DC_GPIO_HPD_EN 0x64b8 +#define EVERGREEN_DC_GPIO_HPD_Y 0x64bc + +/* HDMI blocks at 0x7030, 0x7c30, 0x10830, 0x11430, 0x12030, 0x12c30 */ +#define EVERGREEN_HDMI_BASE 0x7030 + +#endif diff --git a/sys/dev/drm2/radeon/evergreen_reg_safe.h b/sys/dev/drm2/radeon/evergreen_reg_safe.h new file mode 100644 index 00000000000..9d56fe7a536 --- /dev/null +++ b/sys/dev/drm2/radeon/evergreen_reg_safe.h @@ -0,0 +1,517 @@ +#include +__FBSDID("$FreeBSD$"); + +static const unsigned evergreen_reg_safe_bm[2047] = { + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFF0F7FF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0x7FFFFFFF, 0xFFFFFFFF, 0xCFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFDDEFFF, 0xCF3FFFFF, 0xFFFFF40F, + 0xFEFFFFDF, 0xFFFFFFFF, 0xFFFFFFEF, 0xEFFFFFFF, + 0xFFFFF800, 0xFFFFFFFF, 0xFFFFFFFF, 0xBFFFFF07, + 0xFFFBF0FF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFDF, 0xFFFFFFFF, 0xFFFF7FFE, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFB, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFDE, 0xFFFFFFFF, + 0xFFDF0FFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xC0000000, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFC3E4, 0xFFFFFFFF, 0x0000FFFF, 0x00000000, + 0x000CC000, 0x00000000, 0xFFD00000, 0x00000000, + 0x00000E00, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0xC0000000, 0xFFFFF8FF, 0xFE07FF00, + 0x3CF1B003, 0xE39E7BCF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xF7020000, 0xDDD89CDD, 0x201FA3FD, 0xFFFFFFF0, + 0xBFFF0002, 0xEFC3DF87, 0x7BF0F7E1, 0x1EFC3DF8, + 0xDFBF0F7E, 0xFFFFF7EF, 0xFFFFFFFF, 0x00000000, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xCFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFF8, +}; diff --git a/sys/dev/drm2/radeon/evergreend.h b/sys/dev/drm2/radeon/evergreend.h new file mode 100644 index 00000000000..3fd74ce1e49 --- /dev/null +++ b/sys/dev/drm2/radeon/evergreend.h @@ -0,0 +1,2046 @@ +/* + * Copyright 2010 Advanced Micro Devices, Inc. + * + * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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. + * + * Authors: Alex Deucher + */ + +#include +__FBSDID("$FreeBSD$"); + +#ifndef EVERGREEND_H +#define EVERGREEND_H + +#define EVERGREEN_MAX_SH_GPRS 256 +#define EVERGREEN_MAX_TEMP_GPRS 16 +#define EVERGREEN_MAX_SH_THREADS 256 +#define EVERGREEN_MAX_SH_STACK_ENTRIES 4096 +#define EVERGREEN_MAX_FRC_EOV_CNT 16384 +#define EVERGREEN_MAX_BACKENDS 8 +#define EVERGREEN_MAX_BACKENDS_MASK 0xFF +#define EVERGREEN_MAX_SIMDS 16 +#define EVERGREEN_MAX_SIMDS_MASK 0xFFFF +#define EVERGREEN_MAX_PIPES 8 +#define EVERGREEN_MAX_PIPES_MASK 0xFF +#define EVERGREEN_MAX_LDS_NUM 0xFFFF + +#define CYPRESS_GB_ADDR_CONFIG_GOLDEN 0x02011003 +#define BARTS_GB_ADDR_CONFIG_GOLDEN 0x02011003 +#define CAYMAN_GB_ADDR_CONFIG_GOLDEN 0x02011003 +#define JUNIPER_GB_ADDR_CONFIG_GOLDEN 0x02010002 +#define REDWOOD_GB_ADDR_CONFIG_GOLDEN 0x02010002 +#define TURKS_GB_ADDR_CONFIG_GOLDEN 0x02010002 +#define CEDAR_GB_ADDR_CONFIG_GOLDEN 0x02010001 +#define CAICOS_GB_ADDR_CONFIG_GOLDEN 0x02010001 +#define SUMO_GB_ADDR_CONFIG_GOLDEN 0x02010002 +#define SUMO2_GB_ADDR_CONFIG_GOLDEN 0x02010002 + +/* Registers */ + +#define RCU_IND_INDEX 0x100 +#define RCU_IND_DATA 0x104 + +#define GRBM_GFX_INDEX 0x802C +#define INSTANCE_INDEX(x) ((x) << 0) +#define SE_INDEX(x) ((x) << 16) +#define INSTANCE_BROADCAST_WRITES (1 << 30) +#define SE_BROADCAST_WRITES (1 << 31) +#define RLC_GFX_INDEX 0x3fC4 +#define CC_GC_SHADER_PIPE_CONFIG 0x8950 +#define WRITE_DIS (1 << 0) +#define CC_RB_BACKEND_DISABLE 0x98F4 +#define BACKEND_DISABLE(x) ((x) << 16) +#define GB_ADDR_CONFIG 0x98F8 +#define NUM_PIPES(x) ((x) << 0) +#define NUM_PIPES_MASK 0x0000000f +#define PIPE_INTERLEAVE_SIZE(x) ((x) << 4) +#define BANK_INTERLEAVE_SIZE(x) ((x) << 8) +#define NUM_SHADER_ENGINES(x) ((x) << 12) +#define SHADER_ENGINE_TILE_SIZE(x) ((x) << 16) +#define NUM_GPUS(x) ((x) << 20) +#define MULTI_GPU_TILE_SIZE(x) ((x) << 24) +#define ROW_SIZE(x) ((x) << 28) +#define GB_BACKEND_MAP 0x98FC +#define DMIF_ADDR_CONFIG 0xBD4 +#define HDP_ADDR_CONFIG 0x2F48 +#define HDP_MISC_CNTL 0x2F4C +#define HDP_FLUSH_INVALIDATE_CACHE (1 << 0) + +#define CC_SYS_RB_BACKEND_DISABLE 0x3F88 +#define GC_USER_RB_BACKEND_DISABLE 0x9B7C + +#define CGTS_SYS_TCC_DISABLE 0x3F90 +#define CGTS_TCC_DISABLE 0x9148 +#define CGTS_USER_SYS_TCC_DISABLE 0x3F94 +#define CGTS_USER_TCC_DISABLE 0x914C + +#define CONFIG_MEMSIZE 0x5428 + +#define BIF_FB_EN 0x5490 +#define FB_READ_EN (1 << 0) +#define FB_WRITE_EN (1 << 1) + +#define CP_STRMOUT_CNTL 0x84FC + +#define CP_COHER_CNTL 0x85F0 +#define CP_COHER_SIZE 0x85F4 +#define CP_COHER_BASE 0x85F8 +#define CP_STALLED_STAT1 0x8674 +#define CP_STALLED_STAT2 0x8678 +#define CP_BUSY_STAT 0x867C +#define CP_STAT 0x8680 +#define CP_ME_CNTL 0x86D8 +#define CP_ME_HALT (1 << 28) +#define CP_PFP_HALT (1 << 26) +#define CP_ME_RAM_DATA 0xC160 +#define CP_ME_RAM_RADDR 0xC158 +#define CP_ME_RAM_WADDR 0xC15C +#define CP_MEQ_THRESHOLDS 0x8764 +#define STQ_SPLIT(x) ((x) << 0) +#define CP_PERFMON_CNTL 0x87FC +#define CP_PFP_UCODE_ADDR 0xC150 +#define CP_PFP_UCODE_DATA 0xC154 +#define CP_QUEUE_THRESHOLDS 0x8760 +#define ROQ_IB1_START(x) ((x) << 0) +#define ROQ_IB2_START(x) ((x) << 8) +#define CP_RB_BASE 0xC100 +#define CP_RB_CNTL 0xC104 +#define RB_BUFSZ(x) ((x) << 0) +#define RB_BLKSZ(x) ((x) << 8) +#define RB_NO_UPDATE (1 << 27) +#define RB_RPTR_WR_ENA (1 << 31) +#define BUF_SWAP_32BIT (2 << 16) +#define CP_RB_RPTR 0x8700 +#define CP_RB_RPTR_ADDR 0xC10C +#define RB_RPTR_SWAP(x) ((x) << 0) +#define CP_RB_RPTR_ADDR_HI 0xC110 +#define CP_RB_RPTR_WR 0xC108 +#define CP_RB_WPTR 0xC114 +#define CP_RB_WPTR_ADDR 0xC118 +#define CP_RB_WPTR_ADDR_HI 0xC11C +#define CP_RB_WPTR_DELAY 0x8704 +#define CP_SEM_WAIT_TIMER 0x85BC +#define CP_SEM_INCOMPLETE_TIMER_CNTL 0x85C8 +#define CP_DEBUG 0xC1FC + +/* Audio clocks */ +#define DCCG_AUDIO_DTO_SOURCE 0x05ac +# define DCCG_AUDIO_DTO0_SOURCE_SEL(x) ((x) << 0) /* crtc0 - crtc5 */ +# define DCCG_AUDIO_DTO_SEL (1 << 4) /* 0=dto0 1=dto1 */ + +#define DCCG_AUDIO_DTO0_PHASE 0x05b0 +#define DCCG_AUDIO_DTO0_MODULE 0x05b4 +#define DCCG_AUDIO_DTO0_LOAD 0x05b8 +#define DCCG_AUDIO_DTO0_CNTL 0x05bc + +#define DCCG_AUDIO_DTO1_PHASE 0x05c0 +#define DCCG_AUDIO_DTO1_MODULE 0x05c4 +#define DCCG_AUDIO_DTO1_LOAD 0x05c8 +#define DCCG_AUDIO_DTO1_CNTL 0x05cc + +/* DCE 4.0 AFMT */ +#define HDMI_CONTROL 0x7030 +# define HDMI_KEEPOUT_MODE (1 << 0) +# define HDMI_PACKET_GEN_VERSION (1 << 4) /* 0 = r6xx compat */ +# define HDMI_ERROR_ACK (1 << 8) +# define HDMI_ERROR_MASK (1 << 9) +# define HDMI_DEEP_COLOR_ENABLE (1 << 24) +# define HDMI_DEEP_COLOR_DEPTH (((x) & 3) << 28) +# define HDMI_24BIT_DEEP_COLOR 0 +# define HDMI_30BIT_DEEP_COLOR 1 +# define HDMI_36BIT_DEEP_COLOR 2 +#define HDMI_STATUS 0x7034 +# define HDMI_ACTIVE_AVMUTE (1 << 0) +# define HDMI_AUDIO_PACKET_ERROR (1 << 16) +# define HDMI_VBI_PACKET_ERROR (1 << 20) +#define HDMI_AUDIO_PACKET_CONTROL 0x7038 +# define HDMI_AUDIO_DELAY_EN(x) (((x) & 3) << 4) +# define HDMI_AUDIO_PACKETS_PER_LINE(x) (((x) & 0x1f) << 16) +#define HDMI_ACR_PACKET_CONTROL 0x703c +# define HDMI_ACR_SEND (1 << 0) +# define HDMI_ACR_CONT (1 << 1) +# define HDMI_ACR_SELECT(x) (((x) & 3) << 4) +# define HDMI_ACR_HW 0 +# define HDMI_ACR_32 1 +# define HDMI_ACR_44 2 +# define HDMI_ACR_48 3 +# define HDMI_ACR_SOURCE (1 << 8) /* 0 - hw; 1 - cts value */ +# define HDMI_ACR_AUTO_SEND (1 << 12) +# define HDMI_ACR_N_MULTIPLE(x) (((x) & 7) << 16) +# define HDMI_ACR_X1 1 +# define HDMI_ACR_X2 2 +# define HDMI_ACR_X4 4 +# define HDMI_ACR_AUDIO_PRIORITY (1 << 31) +#define HDMI_VBI_PACKET_CONTROL 0x7040 +# define HDMI_NULL_SEND (1 << 0) +# define HDMI_GC_SEND (1 << 4) +# define HDMI_GC_CONT (1 << 5) /* 0 - once; 1 - every frame */ +#define HDMI_INFOFRAME_CONTROL0 0x7044 +# define HDMI_AVI_INFO_SEND (1 << 0) +# define HDMI_AVI_INFO_CONT (1 << 1) +# define HDMI_AUDIO_INFO_SEND (1 << 4) +# define HDMI_AUDIO_INFO_CONT (1 << 5) +# define HDMI_MPEG_INFO_SEND (1 << 8) +# define HDMI_MPEG_INFO_CONT (1 << 9) +#define HDMI_INFOFRAME_CONTROL1 0x7048 +# define HDMI_AVI_INFO_LINE(x) (((x) & 0x3f) << 0) +# define HDMI_AUDIO_INFO_LINE(x) (((x) & 0x3f) << 8) +# define HDMI_MPEG_INFO_LINE(x) (((x) & 0x3f) << 16) +#define HDMI_GENERIC_PACKET_CONTROL 0x704c +# define HDMI_GENERIC0_SEND (1 << 0) +# define HDMI_GENERIC0_CONT (1 << 1) +# define HDMI_GENERIC1_SEND (1 << 4) +# define HDMI_GENERIC1_CONT (1 << 5) +# define HDMI_GENERIC0_LINE(x) (((x) & 0x3f) << 16) +# define HDMI_GENERIC1_LINE(x) (((x) & 0x3f) << 24) +#define HDMI_GC 0x7058 +# define HDMI_GC_AVMUTE (1 << 0) +# define HDMI_GC_AVMUTE_CONT (1 << 2) +#define AFMT_AUDIO_PACKET_CONTROL2 0x705c +# define AFMT_AUDIO_LAYOUT_OVRD (1 << 0) +# define AFMT_AUDIO_LAYOUT_SELECT (1 << 1) +# define AFMT_60958_CS_SOURCE (1 << 4) +# define AFMT_AUDIO_CHANNEL_ENABLE(x) (((x) & 0xff) << 8) +# define AFMT_DP_AUDIO_STREAM_ID(x) (((x) & 0xff) << 16) +#define AFMT_AVI_INFO0 0x7084 +# define AFMT_AVI_INFO_CHECKSUM(x) (((x) & 0xff) << 0) +# define AFMT_AVI_INFO_S(x) (((x) & 3) << 8) +# define AFMT_AVI_INFO_B(x) (((x) & 3) << 10) +# define AFMT_AVI_INFO_A(x) (((x) & 1) << 12) +# define AFMT_AVI_INFO_Y(x) (((x) & 3) << 13) +# define AFMT_AVI_INFO_Y_RGB 0 +# define AFMT_AVI_INFO_Y_YCBCR422 1 +# define AFMT_AVI_INFO_Y_YCBCR444 2 +# define AFMT_AVI_INFO_Y_A_B_S(x) (((x) & 0xff) << 8) +# define AFMT_AVI_INFO_R(x) (((x) & 0xf) << 16) +# define AFMT_AVI_INFO_M(x) (((x) & 0x3) << 20) +# define AFMT_AVI_INFO_C(x) (((x) & 0x3) << 22) +# define AFMT_AVI_INFO_C_M_R(x) (((x) & 0xff) << 16) +# define AFMT_AVI_INFO_SC(x) (((x) & 0x3) << 24) +# define AFMT_AVI_INFO_Q(x) (((x) & 0x3) << 26) +# define AFMT_AVI_INFO_EC(x) (((x) & 0x3) << 28) +# define AFMT_AVI_INFO_ITC(x) (((x) & 0x1) << 31) +# define AFMT_AVI_INFO_ITC_EC_Q_SC(x) (((x) & 0xff) << 24) +#define AFMT_AVI_INFO1 0x7088 +# define AFMT_AVI_INFO_VIC(x) (((x) & 0x7f) << 0) /* don't use avi infoframe v1 */ +# define AFMT_AVI_INFO_PR(x) (((x) & 0xf) << 8) /* don't use avi infoframe v1 */ +# define AFMT_AVI_INFO_CN(x) (((x) & 0x3) << 12) +# define AFMT_AVI_INFO_YQ(x) (((x) & 0x3) << 14) +# define AFMT_AVI_INFO_TOP(x) (((x) & 0xffff) << 16) +#define AFMT_AVI_INFO2 0x708c +# define AFMT_AVI_INFO_BOTTOM(x) (((x) & 0xffff) << 0) +# define AFMT_AVI_INFO_LEFT(x) (((x) & 0xffff) << 16) +#define AFMT_AVI_INFO3 0x7090 +# define AFMT_AVI_INFO_RIGHT(x) (((x) & 0xffff) << 0) +# define AFMT_AVI_INFO_VERSION(x) (((x) & 3) << 24) +#define AFMT_MPEG_INFO0 0x7094 +# define AFMT_MPEG_INFO_CHECKSUM(x) (((x) & 0xff) << 0) +# define AFMT_MPEG_INFO_MB0(x) (((x) & 0xff) << 8) +# define AFMT_MPEG_INFO_MB1(x) (((x) & 0xff) << 16) +# define AFMT_MPEG_INFO_MB2(x) (((x) & 0xff) << 24) +#define AFMT_MPEG_INFO1 0x7098 +# define AFMT_MPEG_INFO_MB3(x) (((x) & 0xff) << 0) +# define AFMT_MPEG_INFO_MF(x) (((x) & 3) << 8) +# define AFMT_MPEG_INFO_FR(x) (((x) & 1) << 12) +#define AFMT_GENERIC0_HDR 0x709c +#define AFMT_GENERIC0_0 0x70a0 +#define AFMT_GENERIC0_1 0x70a4 +#define AFMT_GENERIC0_2 0x70a8 +#define AFMT_GENERIC0_3 0x70ac +#define AFMT_GENERIC0_4 0x70b0 +#define AFMT_GENERIC0_5 0x70b4 +#define AFMT_GENERIC0_6 0x70b8 +#define AFMT_GENERIC1_HDR 0x70bc +#define AFMT_GENERIC1_0 0x70c0 +#define AFMT_GENERIC1_1 0x70c4 +#define AFMT_GENERIC1_2 0x70c8 +#define AFMT_GENERIC1_3 0x70cc +#define AFMT_GENERIC1_4 0x70d0 +#define AFMT_GENERIC1_5 0x70d4 +#define AFMT_GENERIC1_6 0x70d8 +#define HDMI_ACR_32_0 0x70dc +# define HDMI_ACR_CTS_32(x) (((x) & 0xfffff) << 12) +#define HDMI_ACR_32_1 0x70e0 +# define HDMI_ACR_N_32(x) (((x) & 0xfffff) << 0) +#define HDMI_ACR_44_0 0x70e4 +# define HDMI_ACR_CTS_44(x) (((x) & 0xfffff) << 12) +#define HDMI_ACR_44_1 0x70e8 +# define HDMI_ACR_N_44(x) (((x) & 0xfffff) << 0) +#define HDMI_ACR_48_0 0x70ec +# define HDMI_ACR_CTS_48(x) (((x) & 0xfffff) << 12) +#define HDMI_ACR_48_1 0x70f0 +# define HDMI_ACR_N_48(x) (((x) & 0xfffff) << 0) +#define HDMI_ACR_STATUS_0 0x70f4 +#define HDMI_ACR_STATUS_1 0x70f8 +#define AFMT_AUDIO_INFO0 0x70fc +# define AFMT_AUDIO_INFO_CHECKSUM(x) (((x) & 0xff) << 0) +# define AFMT_AUDIO_INFO_CC(x) (((x) & 7) << 8) +# define AFMT_AUDIO_INFO_CT(x) (((x) & 0xf) << 11) +# define AFMT_AUDIO_INFO_CHECKSUM_OFFSET(x) (((x) & 0xff) << 16) +# define AFMT_AUDIO_INFO_CXT(x) (((x) & 0x1f) << 24) +#define AFMT_AUDIO_INFO1 0x7100 +# define AFMT_AUDIO_INFO_CA(x) (((x) & 0xff) << 0) +# define AFMT_AUDIO_INFO_LSV(x) (((x) & 0xf) << 11) +# define AFMT_AUDIO_INFO_DM_INH(x) (((x) & 1) << 15) +# define AFMT_AUDIO_INFO_DM_INH_LSV(x) (((x) & 0xff) << 8) +# define AFMT_AUDIO_INFO_LFEBPL(x) (((x) & 3) << 16) +#define AFMT_60958_0 0x7104 +# define AFMT_60958_CS_A(x) (((x) & 1) << 0) +# define AFMT_60958_CS_B(x) (((x) & 1) << 1) +# define AFMT_60958_CS_C(x) (((x) & 1) << 2) +# define AFMT_60958_CS_D(x) (((x) & 3) << 3) +# define AFMT_60958_CS_MODE(x) (((x) & 3) << 6) +# define AFMT_60958_CS_CATEGORY_CODE(x) (((x) & 0xff) << 8) +# define AFMT_60958_CS_SOURCE_NUMBER(x) (((x) & 0xf) << 16) +# define AFMT_60958_CS_CHANNEL_NUMBER_L(x) (((x) & 0xf) << 20) +# define AFMT_60958_CS_SAMPLING_FREQUENCY(x) (((x) & 0xf) << 24) +# define AFMT_60958_CS_CLOCK_ACCURACY(x) (((x) & 3) << 28) +#define AFMT_60958_1 0x7108 +# define AFMT_60958_CS_WORD_LENGTH(x) (((x) & 0xf) << 0) +# define AFMT_60958_CS_ORIGINAL_SAMPLING_FREQUENCY(x) (((x) & 0xf) << 4) +# define AFMT_60958_CS_VALID_L(x) (((x) & 1) << 16) +# define AFMT_60958_CS_VALID_R(x) (((x) & 1) << 18) +# define AFMT_60958_CS_CHANNEL_NUMBER_R(x) (((x) & 0xf) << 20) +#define AFMT_AUDIO_CRC_CONTROL 0x710c +# define AFMT_AUDIO_CRC_EN (1 << 0) +#define AFMT_RAMP_CONTROL0 0x7110 +# define AFMT_RAMP_MAX_COUNT(x) (((x) & 0xffffff) << 0) +# define AFMT_RAMP_DATA_SIGN (1 << 31) +#define AFMT_RAMP_CONTROL1 0x7114 +# define AFMT_RAMP_MIN_COUNT(x) (((x) & 0xffffff) << 0) +# define AFMT_AUDIO_TEST_CH_DISABLE(x) (((x) & 0xff) << 24) +#define AFMT_RAMP_CONTROL2 0x7118 +# define AFMT_RAMP_INC_COUNT(x) (((x) & 0xffffff) << 0) +#define AFMT_RAMP_CONTROL3 0x711c +# define AFMT_RAMP_DEC_COUNT(x) (((x) & 0xffffff) << 0) +#define AFMT_60958_2 0x7120 +# define AFMT_60958_CS_CHANNEL_NUMBER_2(x) (((x) & 0xf) << 0) +# define AFMT_60958_CS_CHANNEL_NUMBER_3(x) (((x) & 0xf) << 4) +# define AFMT_60958_CS_CHANNEL_NUMBER_4(x) (((x) & 0xf) << 8) +# define AFMT_60958_CS_CHANNEL_NUMBER_5(x) (((x) & 0xf) << 12) +# define AFMT_60958_CS_CHANNEL_NUMBER_6(x) (((x) & 0xf) << 16) +# define AFMT_60958_CS_CHANNEL_NUMBER_7(x) (((x) & 0xf) << 20) +#define AFMT_STATUS 0x7128 +# define AFMT_AUDIO_ENABLE (1 << 4) +# define AFMT_AUDIO_HBR_ENABLE (1 << 8) +# define AFMT_AZ_FORMAT_WTRIG (1 << 28) +# define AFMT_AZ_FORMAT_WTRIG_INT (1 << 29) +# define AFMT_AZ_AUDIO_ENABLE_CHG (1 << 30) +#define AFMT_AUDIO_PACKET_CONTROL 0x712c +# define AFMT_AUDIO_SAMPLE_SEND (1 << 0) +# define AFMT_RESET_FIFO_WHEN_AUDIO_DIS (1 << 11) /* set to 1 */ +# define AFMT_AUDIO_TEST_EN (1 << 12) +# define AFMT_AUDIO_CHANNEL_SWAP (1 << 24) +# define AFMT_60958_CS_UPDATE (1 << 26) +# define AFMT_AZ_AUDIO_ENABLE_CHG_MASK (1 << 27) +# define AFMT_AZ_FORMAT_WTRIG_MASK (1 << 28) +# define AFMT_AZ_FORMAT_WTRIG_ACK (1 << 29) +# define AFMT_AZ_AUDIO_ENABLE_CHG_ACK (1 << 30) +#define AFMT_VBI_PACKET_CONTROL 0x7130 +# define AFMT_GENERIC0_UPDATE (1 << 2) +#define AFMT_INFOFRAME_CONTROL0 0x7134 +# define AFMT_AUDIO_INFO_SOURCE (1 << 6) /* 0 - sound block; 1 - afmt regs */ +# define AFMT_AUDIO_INFO_UPDATE (1 << 7) +# define AFMT_MPEG_INFO_UPDATE (1 << 10) +#define AFMT_GENERIC0_7 0x7138 + +/* DCE4/5 ELD audio interface */ +#define AZ_F0_CODEC_PIN0_CONTROL_AUDIO_DESCRIPTOR0 0x5f84 /* LPCM */ +#define AZ_F0_CODEC_PIN0_CONTROL_AUDIO_DESCRIPTOR1 0x5f88 /* AC3 */ +#define AZ_F0_CODEC_PIN0_CONTROL_AUDIO_DESCRIPTOR2 0x5f8c /* MPEG1 */ +#define AZ_F0_CODEC_PIN0_CONTROL_AUDIO_DESCRIPTOR3 0x5f90 /* MP3 */ +#define AZ_F0_CODEC_PIN0_CONTROL_AUDIO_DESCRIPTOR4 0x5f94 /* MPEG2 */ +#define AZ_F0_CODEC_PIN0_CONTROL_AUDIO_DESCRIPTOR5 0x5f98 /* AAC */ +#define AZ_F0_CODEC_PIN0_CONTROL_AUDIO_DESCRIPTOR6 0x5f9c /* DTS */ +#define AZ_F0_CODEC_PIN0_CONTROL_AUDIO_DESCRIPTOR7 0x5fa0 /* ATRAC */ +#define AZ_F0_CODEC_PIN0_CONTROL_AUDIO_DESCRIPTOR8 0x5fa4 /* one bit audio - leave at 0 (default) */ +#define AZ_F0_CODEC_PIN0_CONTROL_AUDIO_DESCRIPTOR9 0x5fa8 /* Dolby Digital */ +#define AZ_F0_CODEC_PIN0_CONTROL_AUDIO_DESCRIPTOR10 0x5fac /* DTS-HD */ +#define AZ_F0_CODEC_PIN0_CONTROL_AUDIO_DESCRIPTOR11 0x5fb0 /* MAT-MLP */ +#define AZ_F0_CODEC_PIN0_CONTROL_AUDIO_DESCRIPTOR12 0x5fb4 /* DTS */ +#define AZ_F0_CODEC_PIN0_CONTROL_AUDIO_DESCRIPTOR13 0x5fb8 /* WMA Pro */ +# define MAX_CHANNELS(x) (((x) & 0x7) << 0) +/* max channels minus one. 7 = 8 channels */ +# define SUPPORTED_FREQUENCIES(x) (((x) & 0xff) << 8) +# define DESCRIPTOR_BYTE_2(x) (((x) & 0xff) << 16) +# define SUPPORTED_FREQUENCIES_STEREO(x) (((x) & 0xff) << 24) /* LPCM only */ +/* SUPPORTED_FREQUENCIES, SUPPORTED_FREQUENCIES_STEREO + * bit0 = 32 kHz + * bit1 = 44.1 kHz + * bit2 = 48 kHz + * bit3 = 88.2 kHz + * bit4 = 96 kHz + * bit5 = 176.4 kHz + * bit6 = 192 kHz + */ + +#define AZ_HOT_PLUG_CONTROL 0x5e78 +# define AZ_FORCE_CODEC_WAKE (1 << 0) +# define PIN0_JACK_DETECTION_ENABLE (1 << 4) +# define PIN1_JACK_DETECTION_ENABLE (1 << 5) +# define PIN2_JACK_DETECTION_ENABLE (1 << 6) +# define PIN3_JACK_DETECTION_ENABLE (1 << 7) +# define PIN0_UNSOLICITED_RESPONSE_ENABLE (1 << 8) +# define PIN1_UNSOLICITED_RESPONSE_ENABLE (1 << 9) +# define PIN2_UNSOLICITED_RESPONSE_ENABLE (1 << 10) +# define PIN3_UNSOLICITED_RESPONSE_ENABLE (1 << 11) +# define CODEC_HOT_PLUG_ENABLE (1 << 12) +# define PIN0_AUDIO_ENABLED (1 << 24) +# define PIN1_AUDIO_ENABLED (1 << 25) +# define PIN2_AUDIO_ENABLED (1 << 26) +# define PIN3_AUDIO_ENABLED (1 << 27) +# define AUDIO_ENABLED (1 << 31) + + +#define GC_USER_SHADER_PIPE_CONFIG 0x8954 +#define INACTIVE_QD_PIPES(x) ((x) << 8) +#define INACTIVE_QD_PIPES_MASK 0x0000FF00 +#define INACTIVE_SIMDS(x) ((x) << 16) +#define INACTIVE_SIMDS_MASK 0x00FF0000 + +#define GRBM_CNTL 0x8000 +#define GRBM_READ_TIMEOUT(x) ((x) << 0) +#define GRBM_SOFT_RESET 0x8020 +#define SOFT_RESET_CP (1 << 0) +#define SOFT_RESET_CB (1 << 1) +#define SOFT_RESET_DB (1 << 3) +#define SOFT_RESET_PA (1 << 5) +#define SOFT_RESET_SC (1 << 6) +#define SOFT_RESET_SPI (1 << 8) +#define SOFT_RESET_SH (1 << 9) +#define SOFT_RESET_SX (1 << 10) +#define SOFT_RESET_TC (1 << 11) +#define SOFT_RESET_TA (1 << 12) +#define SOFT_RESET_VC (1 << 13) +#define SOFT_RESET_VGT (1 << 14) + +#define GRBM_STATUS 0x8010 +#define CMDFIFO_AVAIL_MASK 0x0000000F +#define SRBM_RQ_PENDING (1 << 5) +#define CF_RQ_PENDING (1 << 7) +#define PF_RQ_PENDING (1 << 8) +#define GRBM_EE_BUSY (1 << 10) +#define SX_CLEAN (1 << 11) +#define DB_CLEAN (1 << 12) +#define CB_CLEAN (1 << 13) +#define TA_BUSY (1 << 14) +#define VGT_BUSY_NO_DMA (1 << 16) +#define VGT_BUSY (1 << 17) +#define SX_BUSY (1 << 20) +#define SH_BUSY (1 << 21) +#define SPI_BUSY (1 << 22) +#define SC_BUSY (1 << 24) +#define PA_BUSY (1 << 25) +#define DB_BUSY (1 << 26) +#define CP_COHERENCY_BUSY (1 << 28) +#define CP_BUSY (1 << 29) +#define CB_BUSY (1 << 30) +#define GUI_ACTIVE (1 << 31) +#define GRBM_STATUS_SE0 0x8014 +#define GRBM_STATUS_SE1 0x8018 +#define SE_SX_CLEAN (1 << 0) +#define SE_DB_CLEAN (1 << 1) +#define SE_CB_CLEAN (1 << 2) +#define SE_TA_BUSY (1 << 25) +#define SE_SX_BUSY (1 << 26) +#define SE_SPI_BUSY (1 << 27) +#define SE_SH_BUSY (1 << 28) +#define SE_SC_BUSY (1 << 29) +#define SE_DB_BUSY (1 << 30) +#define SE_CB_BUSY (1 << 31) +/* evergreen */ +#define CG_THERMAL_CTRL 0x72c +#define TOFFSET_MASK 0x00003FE0 +#define TOFFSET_SHIFT 5 +#define CG_MULT_THERMAL_STATUS 0x740 +#define ASIC_T(x) ((x) << 16) +#define ASIC_T_MASK 0x07FF0000 +#define ASIC_T_SHIFT 16 +#define CG_TS0_STATUS 0x760 +#define TS0_ADC_DOUT_MASK 0x000003FF +#define TS0_ADC_DOUT_SHIFT 0 +/* APU */ +#define CG_THERMAL_STATUS 0x678 + +#define HDP_HOST_PATH_CNTL 0x2C00 +#define HDP_NONSURFACE_BASE 0x2C04 +#define HDP_NONSURFACE_INFO 0x2C08 +#define HDP_NONSURFACE_SIZE 0x2C0C +#define HDP_MEM_COHERENCY_FLUSH_CNTL 0x5480 +#define HDP_REG_COHERENCY_FLUSH_CNTL 0x54A0 +#define HDP_TILING_CONFIG 0x2F3C + +#define MC_SHARED_CHMAP 0x2004 +#define NOOFCHAN_SHIFT 12 +#define NOOFCHAN_MASK 0x00003000 +#define MC_SHARED_CHREMAP 0x2008 + +#define MC_SHARED_BLACKOUT_CNTL 0x20ac +#define BLACKOUT_MODE_MASK 0x00000007 + +#define MC_ARB_RAMCFG 0x2760 +#define NOOFBANK_SHIFT 0 +#define NOOFBANK_MASK 0x00000003 +#define NOOFRANK_SHIFT 2 +#define NOOFRANK_MASK 0x00000004 +#define NOOFROWS_SHIFT 3 +#define NOOFROWS_MASK 0x00000038 +#define NOOFCOLS_SHIFT 6 +#define NOOFCOLS_MASK 0x000000C0 +#define CHANSIZE_SHIFT 8 +#define CHANSIZE_MASK 0x00000100 +#define BURSTLENGTH_SHIFT 9 +#define BURSTLENGTH_MASK 0x00000200 +#define CHANSIZE_OVERRIDE (1 << 11) +#define FUS_MC_ARB_RAMCFG 0x2768 +#define MC_VM_AGP_TOP 0x2028 +#define MC_VM_AGP_BOT 0x202C +#define MC_VM_AGP_BASE 0x2030 +#define MC_VM_FB_LOCATION 0x2024 +#define MC_FUS_VM_FB_OFFSET 0x2898 +#define MC_VM_MB_L1_TLB0_CNTL 0x2234 +#define MC_VM_MB_L1_TLB1_CNTL 0x2238 +#define MC_VM_MB_L1_TLB2_CNTL 0x223C +#define MC_VM_MB_L1_TLB3_CNTL 0x2240 +#define ENABLE_L1_TLB (1 << 0) +#define ENABLE_L1_FRAGMENT_PROCESSING (1 << 1) +#define SYSTEM_ACCESS_MODE_PA_ONLY (0 << 3) +#define SYSTEM_ACCESS_MODE_USE_SYS_MAP (1 << 3) +#define SYSTEM_ACCESS_MODE_IN_SYS (2 << 3) +#define SYSTEM_ACCESS_MODE_NOT_IN_SYS (3 << 3) +#define SYSTEM_APERTURE_UNMAPPED_ACCESS_PASS_THRU (0 << 5) +#define EFFECTIVE_L1_TLB_SIZE(x) ((x)<<15) +#define EFFECTIVE_L1_QUEUE_SIZE(x) ((x)<<18) +#define MC_VM_MD_L1_TLB0_CNTL 0x2654 +#define MC_VM_MD_L1_TLB1_CNTL 0x2658 +#define MC_VM_MD_L1_TLB2_CNTL 0x265C +#define MC_VM_MD_L1_TLB3_CNTL 0x2698 + +#define FUS_MC_VM_MD_L1_TLB0_CNTL 0x265C +#define FUS_MC_VM_MD_L1_TLB1_CNTL 0x2660 +#define FUS_MC_VM_MD_L1_TLB2_CNTL 0x2664 + +#define MC_VM_SYSTEM_APERTURE_DEFAULT_ADDR 0x203C +#define MC_VM_SYSTEM_APERTURE_HIGH_ADDR 0x2038 +#define MC_VM_SYSTEM_APERTURE_LOW_ADDR 0x2034 + +#define PA_CL_ENHANCE 0x8A14 +#define CLIP_VTX_REORDER_ENA (1 << 0) +#define NUM_CLIP_SEQ(x) ((x) << 1) +#define PA_SC_ENHANCE 0x8BF0 +#define PA_SC_AA_CONFIG 0x28C04 +#define MSAA_NUM_SAMPLES_SHIFT 0 +#define MSAA_NUM_SAMPLES_MASK 0x3 +#define PA_SC_CLIPRECT_RULE 0x2820C +#define PA_SC_EDGERULE 0x28230 +#define PA_SC_FIFO_SIZE 0x8BCC +#define SC_PRIM_FIFO_SIZE(x) ((x) << 0) +#define SC_HIZ_TILE_FIFO_SIZE(x) ((x) << 12) +#define SC_EARLYZ_TILE_FIFO_SIZE(x) ((x) << 20) +#define PA_SC_FORCE_EOV_MAX_CNTS 0x8B24 +#define FORCE_EOV_MAX_CLK_CNT(x) ((x) << 0) +#define FORCE_EOV_MAX_REZ_CNT(x) ((x) << 16) +#define PA_SC_LINE_STIPPLE 0x28A0C +#define PA_SU_LINE_STIPPLE_VALUE 0x8A60 +#define PA_SC_LINE_STIPPLE_STATE 0x8B10 + +#define SCRATCH_REG0 0x8500 +#define SCRATCH_REG1 0x8504 +#define SCRATCH_REG2 0x8508 +#define SCRATCH_REG3 0x850C +#define SCRATCH_REG4 0x8510 +#define SCRATCH_REG5 0x8514 +#define SCRATCH_REG6 0x8518 +#define SCRATCH_REG7 0x851C +#define SCRATCH_UMSK 0x8540 +#define SCRATCH_ADDR 0x8544 + +#define SMX_SAR_CTL0 0xA008 +#define SMX_DC_CTL0 0xA020 +#define USE_HASH_FUNCTION (1 << 0) +#define NUMBER_OF_SETS(x) ((x) << 1) +#define FLUSH_ALL_ON_EVENT (1 << 10) +#define STALL_ON_EVENT (1 << 11) +#define SMX_EVENT_CTL 0xA02C +#define ES_FLUSH_CTL(x) ((x) << 0) +#define GS_FLUSH_CTL(x) ((x) << 3) +#define ACK_FLUSH_CTL(x) ((x) << 6) +#define SYNC_FLUSH_CTL (1 << 8) + +#define SPI_CONFIG_CNTL 0x9100 +#define GPR_WRITE_PRIORITY(x) ((x) << 0) +#define SPI_CONFIG_CNTL_1 0x913C +#define VTX_DONE_DELAY(x) ((x) << 0) +#define INTERP_ONE_PRIM_PER_ROW (1 << 4) +#define SPI_INPUT_Z 0x286D8 +#define SPI_PS_IN_CONTROL_0 0x286CC +#define NUM_INTERP(x) ((x)<<0) +#define POSITION_ENA (1<<8) +#define POSITION_CENTROID (1<<9) +#define POSITION_ADDR(x) ((x)<<10) +#define PARAM_GEN(x) ((x)<<15) +#define PARAM_GEN_ADDR(x) ((x)<<19) +#define BARYC_SAMPLE_CNTL(x) ((x)<<26) +#define PERSP_GRADIENT_ENA (1<<28) +#define LINEAR_GRADIENT_ENA (1<<29) +#define POSITION_SAMPLE (1<<30) +#define BARYC_AT_SAMPLE_ENA (1<<31) + +#define SQ_CONFIG 0x8C00 +#define VC_ENABLE (1 << 0) +#define EXPORT_SRC_C (1 << 1) +#define CS_PRIO(x) ((x) << 18) +#define LS_PRIO(x) ((x) << 20) +#define HS_PRIO(x) ((x) << 22) +#define PS_PRIO(x) ((x) << 24) +#define VS_PRIO(x) ((x) << 26) +#define GS_PRIO(x) ((x) << 28) +#define ES_PRIO(x) ((x) << 30) +#define SQ_GPR_RESOURCE_MGMT_1 0x8C04 +#define NUM_PS_GPRS(x) ((x) << 0) +#define NUM_VS_GPRS(x) ((x) << 16) +#define NUM_CLAUSE_TEMP_GPRS(x) ((x) << 28) +#define SQ_GPR_RESOURCE_MGMT_2 0x8C08 +#define NUM_GS_GPRS(x) ((x) << 0) +#define NUM_ES_GPRS(x) ((x) << 16) +#define SQ_GPR_RESOURCE_MGMT_3 0x8C0C +#define NUM_HS_GPRS(x) ((x) << 0) +#define NUM_LS_GPRS(x) ((x) << 16) +#define SQ_GLOBAL_GPR_RESOURCE_MGMT_1 0x8C10 +#define SQ_GLOBAL_GPR_RESOURCE_MGMT_2 0x8C14 +#define SQ_THREAD_RESOURCE_MGMT 0x8C18 +#define NUM_PS_THREADS(x) ((x) << 0) +#define NUM_VS_THREADS(x) ((x) << 8) +#define NUM_GS_THREADS(x) ((x) << 16) +#define NUM_ES_THREADS(x) ((x) << 24) +#define SQ_THREAD_RESOURCE_MGMT_2 0x8C1C +#define NUM_HS_THREADS(x) ((x) << 0) +#define NUM_LS_THREADS(x) ((x) << 8) +#define SQ_STACK_RESOURCE_MGMT_1 0x8C20 +#define NUM_PS_STACK_ENTRIES(x) ((x) << 0) +#define NUM_VS_STACK_ENTRIES(x) ((x) << 16) +#define SQ_STACK_RESOURCE_MGMT_2 0x8C24 +#define NUM_GS_STACK_ENTRIES(x) ((x) << 0) +#define NUM_ES_STACK_ENTRIES(x) ((x) << 16) +#define SQ_STACK_RESOURCE_MGMT_3 0x8C28 +#define NUM_HS_STACK_ENTRIES(x) ((x) << 0) +#define NUM_LS_STACK_ENTRIES(x) ((x) << 16) +#define SQ_DYN_GPR_CNTL_PS_FLUSH_REQ 0x8D8C +#define SQ_DYN_GPR_SIMD_LOCK_EN 0x8D94 +#define SQ_STATIC_THREAD_MGMT_1 0x8E20 +#define SQ_STATIC_THREAD_MGMT_2 0x8E24 +#define SQ_STATIC_THREAD_MGMT_3 0x8E28 +#define SQ_LDS_RESOURCE_MGMT 0x8E2C + +#define SQ_MS_FIFO_SIZES 0x8CF0 +#define CACHE_FIFO_SIZE(x) ((x) << 0) +#define FETCH_FIFO_HIWATER(x) ((x) << 8) +#define DONE_FIFO_HIWATER(x) ((x) << 16) +#define ALU_UPDATE_FIFO_HIWATER(x) ((x) << 24) + +#define SX_DEBUG_1 0x9058 +#define ENABLE_NEW_SMX_ADDRESS (1 << 16) +#define SX_EXPORT_BUFFER_SIZES 0x900C +#define COLOR_BUFFER_SIZE(x) ((x) << 0) +#define POSITION_BUFFER_SIZE(x) ((x) << 8) +#define SMX_BUFFER_SIZE(x) ((x) << 16) +#define SX_MEMORY_EXPORT_BASE 0x9010 +#define SX_MISC 0x28350 + +#define CB_PERF_CTR0_SEL_0 0x9A20 +#define CB_PERF_CTR0_SEL_1 0x9A24 +#define CB_PERF_CTR1_SEL_0 0x9A28 +#define CB_PERF_CTR1_SEL_1 0x9A2C +#define CB_PERF_CTR2_SEL_0 0x9A30 +#define CB_PERF_CTR2_SEL_1 0x9A34 +#define CB_PERF_CTR3_SEL_0 0x9A38 +#define CB_PERF_CTR3_SEL_1 0x9A3C + +#define TA_CNTL_AUX 0x9508 +#define DISABLE_CUBE_WRAP (1 << 0) +#define DISABLE_CUBE_ANISO (1 << 1) +#define SYNC_GRADIENT (1 << 24) +#define SYNC_WALKER (1 << 25) +#define SYNC_ALIGNER (1 << 26) + +#define TCP_CHAN_STEER_LO 0x960c +#define TCP_CHAN_STEER_HI 0x9610 + +#define VGT_CACHE_INVALIDATION 0x88C4 +#define CACHE_INVALIDATION(x) ((x) << 0) +#define VC_ONLY 0 +#define TC_ONLY 1 +#define VC_AND_TC 2 +#define AUTO_INVLD_EN(x) ((x) << 6) +#define NO_AUTO 0 +#define ES_AUTO 1 +#define GS_AUTO 2 +#define ES_AND_GS_AUTO 3 +#define VGT_GS_VERTEX_REUSE 0x88D4 +#define VGT_NUM_INSTANCES 0x8974 +#define VGT_OUT_DEALLOC_CNTL 0x28C5C +#define DEALLOC_DIST_MASK 0x0000007F +#define VGT_VERTEX_REUSE_BLOCK_CNTL 0x28C58 +#define VTX_REUSE_DEPTH_MASK 0x000000FF + +#define VM_CONTEXT0_CNTL 0x1410 +#define ENABLE_CONTEXT (1 << 0) +#define PAGE_TABLE_DEPTH(x) (((x) & 3) << 1) +#define RANGE_PROTECTION_FAULT_ENABLE_DEFAULT (1 << 4) +#define VM_CONTEXT1_CNTL 0x1414 +#define VM_CONTEXT1_CNTL2 0x1434 +#define VM_CONTEXT0_PAGE_TABLE_BASE_ADDR 0x153C +#define VM_CONTEXT0_PAGE_TABLE_END_ADDR 0x157C +#define VM_CONTEXT0_PAGE_TABLE_START_ADDR 0x155C +#define VM_CONTEXT0_PROTECTION_FAULT_DEFAULT_ADDR 0x1518 +#define VM_CONTEXT0_REQUEST_RESPONSE 0x1470 +#define REQUEST_TYPE(x) (((x) & 0xf) << 0) +#define RESPONSE_TYPE_MASK 0x000000F0 +#define RESPONSE_TYPE_SHIFT 4 +#define VM_L2_CNTL 0x1400 +#define ENABLE_L2_CACHE (1 << 0) +#define ENABLE_L2_FRAGMENT_PROCESSING (1 << 1) +#define ENABLE_L2_PTE_CACHE_LRU_UPDATE_BY_WRITE (1 << 9) +#define EFFECTIVE_L2_QUEUE_SIZE(x) (((x) & 7) << 14) +#define VM_L2_CNTL2 0x1404 +#define INVALIDATE_ALL_L1_TLBS (1 << 0) +#define INVALIDATE_L2_CACHE (1 << 1) +#define VM_L2_CNTL3 0x1408 +#define BANK_SELECT(x) ((x) << 0) +#define CACHE_UPDATE_MODE(x) ((x) << 6) +#define VM_L2_STATUS 0x140C +#define L2_BUSY (1 << 0) +#define VM_CONTEXT1_PROTECTION_FAULT_ADDR 0x14FC +#define VM_CONTEXT1_PROTECTION_FAULT_STATUS 0x14DC + +#define WAIT_UNTIL 0x8040 + +#define SRBM_STATUS 0x0E50 +#define SRBM_SOFT_RESET 0x0E60 +#define SRBM_SOFT_RESET_ALL_MASK 0x00FEEFA6 +#define SOFT_RESET_BIF (1 << 1) +#define SOFT_RESET_CG (1 << 2) +#define SOFT_RESET_DC (1 << 5) +#define SOFT_RESET_GRBM (1 << 8) +#define SOFT_RESET_HDP (1 << 9) +#define SOFT_RESET_IH (1 << 10) +#define SOFT_RESET_MC (1 << 11) +#define SOFT_RESET_RLC (1 << 13) +#define SOFT_RESET_ROM (1 << 14) +#define SOFT_RESET_SEM (1 << 15) +#define SOFT_RESET_VMC (1 << 17) +#define SOFT_RESET_DMA (1 << 20) +#define SOFT_RESET_TST (1 << 21) +#define SOFT_RESET_REGBB (1 << 22) +#define SOFT_RESET_ORB (1 << 23) + +/* display watermarks */ +#define DC_LB_MEMORY_SPLIT 0x6b0c +#define PRIORITY_A_CNT 0x6b18 +#define PRIORITY_MARK_MASK 0x7fff +#define PRIORITY_OFF (1 << 16) +#define PRIORITY_ALWAYS_ON (1 << 20) +#define PRIORITY_B_CNT 0x6b1c +#define PIPE0_ARBITRATION_CONTROL3 0x0bf0 +# define LATENCY_WATERMARK_MASK(x) ((x) << 16) +#define PIPE0_LATENCY_CONTROL 0x0bf4 +# define LATENCY_LOW_WATERMARK(x) ((x) << 0) +# define LATENCY_HIGH_WATERMARK(x) ((x) << 16) + +#define IH_RB_CNTL 0x3e00 +# define IH_RB_ENABLE (1 << 0) +# define IH_IB_SIZE(x) ((x) << 1) /* log2 */ +# define IH_RB_FULL_DRAIN_ENABLE (1 << 6) +# define IH_WPTR_WRITEBACK_ENABLE (1 << 8) +# define IH_WPTR_WRITEBACK_TIMER(x) ((x) << 9) /* log2 */ +# define IH_WPTR_OVERFLOW_ENABLE (1 << 16) +# define IH_WPTR_OVERFLOW_CLEAR (1 << 31) +#define IH_RB_BASE 0x3e04 +#define IH_RB_RPTR 0x3e08 +#define IH_RB_WPTR 0x3e0c +# define RB_OVERFLOW (1 << 0) +# define WPTR_OFFSET_MASK 0x3fffc +#define IH_RB_WPTR_ADDR_HI 0x3e10 +#define IH_RB_WPTR_ADDR_LO 0x3e14 +#define IH_CNTL 0x3e18 +# define ENABLE_INTR (1 << 0) +# define IH_MC_SWAP(x) ((x) << 1) +# define IH_MC_SWAP_NONE 0 +# define IH_MC_SWAP_16BIT 1 +# define IH_MC_SWAP_32BIT 2 +# define IH_MC_SWAP_64BIT 3 +# define RPTR_REARM (1 << 4) +# define MC_WRREQ_CREDIT(x) ((x) << 15) +# define MC_WR_CLEAN_CNT(x) ((x) << 20) + +#define CP_INT_CNTL 0xc124 +# define CNTX_BUSY_INT_ENABLE (1 << 19) +# define CNTX_EMPTY_INT_ENABLE (1 << 20) +# define SCRATCH_INT_ENABLE (1 << 25) +# define TIME_STAMP_INT_ENABLE (1 << 26) +# define IB2_INT_ENABLE (1 << 29) +# define IB1_INT_ENABLE (1 << 30) +# define RB_INT_ENABLE (1 << 31) +#define CP_INT_STATUS 0xc128 +# define SCRATCH_INT_STAT (1 << 25) +# define TIME_STAMP_INT_STAT (1 << 26) +# define IB2_INT_STAT (1 << 29) +# define IB1_INT_STAT (1 << 30) +# define RB_INT_STAT (1 << 31) + +#define GRBM_INT_CNTL 0x8060 +# define RDERR_INT_ENABLE (1 << 0) +# define GUI_IDLE_INT_ENABLE (1 << 19) + +/* 0x6e98, 0x7a98, 0x10698, 0x11298, 0x11e98, 0x12a98 */ +#define CRTC_STATUS_FRAME_COUNT 0x6e98 + +/* 0x6bb8, 0x77b8, 0x103b8, 0x10fb8, 0x11bb8, 0x127b8 */ +#define VLINE_STATUS 0x6bb8 +# define VLINE_OCCURRED (1 << 0) +# define VLINE_ACK (1 << 4) +# define VLINE_STAT (1 << 12) +# define VLINE_INTERRUPT (1 << 16) +# define VLINE_INTERRUPT_TYPE (1 << 17) +/* 0x6bbc, 0x77bc, 0x103bc, 0x10fbc, 0x11bbc, 0x127bc */ +#define VBLANK_STATUS 0x6bbc +# define VBLANK_OCCURRED (1 << 0) +# define VBLANK_ACK (1 << 4) +# define VBLANK_STAT (1 << 12) +# define VBLANK_INTERRUPT (1 << 16) +# define VBLANK_INTERRUPT_TYPE (1 << 17) + +/* 0x6b40, 0x7740, 0x10340, 0x10f40, 0x11b40, 0x12740 */ +#define INT_MASK 0x6b40 +# define VBLANK_INT_MASK (1 << 0) +# define VLINE_INT_MASK (1 << 4) + +#define DISP_INTERRUPT_STATUS 0x60f4 +# define LB_D1_VLINE_INTERRUPT (1 << 2) +# define LB_D1_VBLANK_INTERRUPT (1 << 3) +# define DC_HPD1_INTERRUPT (1 << 17) +# define DC_HPD1_RX_INTERRUPT (1 << 18) +# define DACA_AUTODETECT_INTERRUPT (1 << 22) +# define DACB_AUTODETECT_INTERRUPT (1 << 23) +# define DC_I2C_SW_DONE_INTERRUPT (1 << 24) +# define DC_I2C_HW_DONE_INTERRUPT (1 << 25) +#define DISP_INTERRUPT_STATUS_CONTINUE 0x60f8 +# define LB_D2_VLINE_INTERRUPT (1 << 2) +# define LB_D2_VBLANK_INTERRUPT (1 << 3) +# define DC_HPD2_INTERRUPT (1 << 17) +# define DC_HPD2_RX_INTERRUPT (1 << 18) +# define DISP_TIMER_INTERRUPT (1 << 24) +#define DISP_INTERRUPT_STATUS_CONTINUE2 0x60fc +# define LB_D3_VLINE_INTERRUPT (1 << 2) +# define LB_D3_VBLANK_INTERRUPT (1 << 3) +# define DC_HPD3_INTERRUPT (1 << 17) +# define DC_HPD3_RX_INTERRUPT (1 << 18) +#define DISP_INTERRUPT_STATUS_CONTINUE3 0x6100 +# define LB_D4_VLINE_INTERRUPT (1 << 2) +# define LB_D4_VBLANK_INTERRUPT (1 << 3) +# define DC_HPD4_INTERRUPT (1 << 17) +# define DC_HPD4_RX_INTERRUPT (1 << 18) +#define DISP_INTERRUPT_STATUS_CONTINUE4 0x614c +# define LB_D5_VLINE_INTERRUPT (1 << 2) +# define LB_D5_VBLANK_INTERRUPT (1 << 3) +# define DC_HPD5_INTERRUPT (1 << 17) +# define DC_HPD5_RX_INTERRUPT (1 << 18) +#define DISP_INTERRUPT_STATUS_CONTINUE5 0x6150 +# define LB_D6_VLINE_INTERRUPT (1 << 2) +# define LB_D6_VBLANK_INTERRUPT (1 << 3) +# define DC_HPD6_INTERRUPT (1 << 17) +# define DC_HPD6_RX_INTERRUPT (1 << 18) + +/* 0x6858, 0x7458, 0x10058, 0x10c58, 0x11858, 0x12458 */ +#define GRPH_INT_STATUS 0x6858 +# define GRPH_PFLIP_INT_OCCURRED (1 << 0) +# define GRPH_PFLIP_INT_CLEAR (1 << 8) +/* 0x685c, 0x745c, 0x1005c, 0x10c5c, 0x1185c, 0x1245c */ +#define GRPH_INT_CONTROL 0x685c +# define GRPH_PFLIP_INT_MASK (1 << 0) +# define GRPH_PFLIP_INT_TYPE (1 << 8) + +#define DACA_AUTODETECT_INT_CONTROL 0x66c8 +#define DACB_AUTODETECT_INT_CONTROL 0x67c8 + +#define DC_HPD1_INT_STATUS 0x601c +#define DC_HPD2_INT_STATUS 0x6028 +#define DC_HPD3_INT_STATUS 0x6034 +#define DC_HPD4_INT_STATUS 0x6040 +#define DC_HPD5_INT_STATUS 0x604c +#define DC_HPD6_INT_STATUS 0x6058 +# define DC_HPDx_INT_STATUS (1 << 0) +# define DC_HPDx_SENSE (1 << 1) +# define DC_HPDx_RX_INT_STATUS (1 << 8) + +#define DC_HPD1_INT_CONTROL 0x6020 +#define DC_HPD2_INT_CONTROL 0x602c +#define DC_HPD3_INT_CONTROL 0x6038 +#define DC_HPD4_INT_CONTROL 0x6044 +#define DC_HPD5_INT_CONTROL 0x6050 +#define DC_HPD6_INT_CONTROL 0x605c +# define DC_HPDx_INT_ACK (1 << 0) +# define DC_HPDx_INT_POLARITY (1 << 8) +# define DC_HPDx_INT_EN (1 << 16) +# define DC_HPDx_RX_INT_ACK (1 << 20) +# define DC_HPDx_RX_INT_EN (1 << 24) + +#define DC_HPD1_CONTROL 0x6024 +#define DC_HPD2_CONTROL 0x6030 +#define DC_HPD3_CONTROL 0x603c +#define DC_HPD4_CONTROL 0x6048 +#define DC_HPD5_CONTROL 0x6054 +#define DC_HPD6_CONTROL 0x6060 +# define DC_HPDx_CONNECTION_TIMER(x) ((x) << 0) +# define DC_HPDx_RX_INT_TIMER(x) ((x) << 16) +# define DC_HPDx_EN (1 << 28) + +/* ASYNC DMA */ +#define DMA_RB_RPTR 0xd008 +#define DMA_RB_WPTR 0xd00c + +#define DMA_CNTL 0xd02c +# define TRAP_ENABLE (1 << 0) +# define SEM_INCOMPLETE_INT_ENABLE (1 << 1) +# define SEM_WAIT_INT_ENABLE (1 << 2) +# define DATA_SWAP_ENABLE (1 << 3) +# define FENCE_SWAP_ENABLE (1 << 4) +# define CTXEMPTY_INT_ENABLE (1 << 28) +#define DMA_TILING_CONFIG 0xD0B8 + +#define CAYMAN_DMA1_CNTL 0xd82c + +/* async DMA packets */ +#define DMA_PACKET(cmd, t, s, n) ((((cmd) & 0xF) << 28) | \ + (((t) & 0x1) << 23) | \ + (((s) & 0x1) << 22) | \ + (((n) & 0xFFFFF) << 0)) +/* async DMA Packet types */ +#define DMA_PACKET_WRITE 0x2 +#define DMA_PACKET_COPY 0x3 +#define DMA_PACKET_INDIRECT_BUFFER 0x4 +#define DMA_PACKET_SEMAPHORE 0x5 +#define DMA_PACKET_FENCE 0x6 +#define DMA_PACKET_TRAP 0x7 +#define DMA_PACKET_SRBM_WRITE 0x9 +#define DMA_PACKET_CONSTANT_FILL 0xd +#define DMA_PACKET_NOP 0xf + +/* PCIE link stuff */ +#define PCIE_LC_TRAINING_CNTL 0xa1 /* PCIE_P */ +#define PCIE_LC_LINK_WIDTH_CNTL 0xa2 /* PCIE_P */ +# define LC_LINK_WIDTH_SHIFT 0 +# define LC_LINK_WIDTH_MASK 0x7 +# define LC_LINK_WIDTH_X0 0 +# define LC_LINK_WIDTH_X1 1 +# define LC_LINK_WIDTH_X2 2 +# define LC_LINK_WIDTH_X4 3 +# define LC_LINK_WIDTH_X8 4 +# define LC_LINK_WIDTH_X16 6 +# define LC_LINK_WIDTH_RD_SHIFT 4 +# define LC_LINK_WIDTH_RD_MASK 0x70 +# define LC_RECONFIG_ARC_MISSING_ESCAPE (1 << 7) +# define LC_RECONFIG_NOW (1 << 8) +# define LC_RENEGOTIATION_SUPPORT (1 << 9) +# define LC_RENEGOTIATE_EN (1 << 10) +# define LC_SHORT_RECONFIG_EN (1 << 11) +# define LC_UPCONFIGURE_SUPPORT (1 << 12) +# define LC_UPCONFIGURE_DIS (1 << 13) +#define PCIE_LC_SPEED_CNTL 0xa4 /* PCIE_P */ +# define LC_GEN2_EN_STRAP (1 << 0) +# define LC_TARGET_LINK_SPEED_OVERRIDE_EN (1 << 1) +# define LC_FORCE_EN_HW_SPEED_CHANGE (1 << 5) +# define LC_FORCE_DIS_HW_SPEED_CHANGE (1 << 6) +# define LC_SPEED_CHANGE_ATTEMPTS_ALLOWED_MASK (0x3 << 8) +# define LC_SPEED_CHANGE_ATTEMPTS_ALLOWED_SHIFT 3 +# define LC_CURRENT_DATA_RATE (1 << 11) +# define LC_VOLTAGE_TIMER_SEL_MASK (0xf << 14) +# define LC_CLR_FAILED_SPD_CHANGE_CNT (1 << 21) +# define LC_OTHER_SIDE_EVER_SENT_GEN2 (1 << 23) +# define LC_OTHER_SIDE_SUPPORTS_GEN2 (1 << 24) +#define MM_CFGREGS_CNTL 0x544c +# define MM_WR_TO_CFG_EN (1 << 3) +#define LINK_CNTL2 0x88 /* F0 */ +# define TARGET_LINK_SPEED_MASK (0xf << 0) +# define SELECTABLE_DEEMPHASIS (1 << 6) + +/* + * PM4 + */ +#define PACKET_TYPE0 0 +#define PACKET_TYPE1 1 +#define PACKET_TYPE2 2 +#define PACKET_TYPE3 3 + +#define CP_PACKET_GET_TYPE(h) (((h) >> 30) & 3) +#define CP_PACKET_GET_COUNT(h) (((h) >> 16) & 0x3FFF) +#define CP_PACKET0_GET_REG(h) (((h) & 0xFFFF) << 2) +#define CP_PACKET3_GET_OPCODE(h) (((h) >> 8) & 0xFF) +#define PACKET0(reg, n) ((PACKET_TYPE0 << 30) | \ + (((reg) >> 2) & 0xFFFF) | \ + ((n) & 0x3FFF) << 16) +#define CP_PACKET2 0x80000000 +#define PACKET2_PAD_SHIFT 0 +#define PACKET2_PAD_MASK (0x3fffffff << 0) + +#define PACKET2(v) (CP_PACKET2 | REG_SET(PACKET2_PAD, (v))) + +#define PACKET3(op, n) ((PACKET_TYPE3 << 30) | \ + (((op) & 0xFF) << 8) | \ + ((n) & 0x3FFF) << 16) + +/* Packet 3 types */ +#define PACKET3_NOP 0x10 +#define PACKET3_SET_BASE 0x11 +#define PACKET3_CLEAR_STATE 0x12 +#define PACKET3_INDEX_BUFFER_SIZE 0x13 +#define PACKET3_DISPATCH_DIRECT 0x15 +#define PACKET3_DISPATCH_INDIRECT 0x16 +#define PACKET3_INDIRECT_BUFFER_END 0x17 +#define PACKET3_MODE_CONTROL 0x18 +#define PACKET3_SET_PREDICATION 0x20 +#define PACKET3_REG_RMW 0x21 +#define PACKET3_COND_EXEC 0x22 +#define PACKET3_PRED_EXEC 0x23 +#define PACKET3_DRAW_INDIRECT 0x24 +#define PACKET3_DRAW_INDEX_INDIRECT 0x25 +#define PACKET3_INDEX_BASE 0x26 +#define PACKET3_DRAW_INDEX_2 0x27 +#define PACKET3_CONTEXT_CONTROL 0x28 +#define PACKET3_DRAW_INDEX_OFFSET 0x29 +#define PACKET3_INDEX_TYPE 0x2A +#define PACKET3_DRAW_INDEX 0x2B +#define PACKET3_DRAW_INDEX_AUTO 0x2D +#define PACKET3_DRAW_INDEX_IMMD 0x2E +#define PACKET3_NUM_INSTANCES 0x2F +#define PACKET3_DRAW_INDEX_MULTI_AUTO 0x30 +#define PACKET3_STRMOUT_BUFFER_UPDATE 0x34 +#define PACKET3_DRAW_INDEX_OFFSET_2 0x35 +#define PACKET3_DRAW_INDEX_MULTI_ELEMENT 0x36 +#define PACKET3_MEM_SEMAPHORE 0x39 +#define PACKET3_MPEG_INDEX 0x3A +#define PACKET3_COPY_DW 0x3B +#define PACKET3_WAIT_REG_MEM 0x3C +#define PACKET3_MEM_WRITE 0x3D +#define PACKET3_INDIRECT_BUFFER 0x32 +#define PACKET3_CP_DMA 0x41 +/* 1. header + * 2. SRC_ADDR_LO or DATA [31:0] + * 3. CP_SYNC [31] | SRC_SEL [30:29] | ENGINE [27] | DST_SEL [21:20] | + * SRC_ADDR_HI [7:0] + * 4. DST_ADDR_LO [31:0] + * 5. DST_ADDR_HI [7:0] + * 6. COMMAND [29:22] | BYTE_COUNT [20:0] + */ +# define PACKET3_CP_DMA_DST_SEL(x) ((x) << 20) + /* 0 - SRC_ADDR + * 1 - GDS + */ +# define PACKET3_CP_DMA_ENGINE(x) ((x) << 27) + /* 0 - ME + * 1 - PFP + */ +# define PACKET3_CP_DMA_SRC_SEL(x) ((x) << 29) + /* 0 - SRC_ADDR + * 1 - GDS + * 2 - DATA + */ +# define PACKET3_CP_DMA_CP_SYNC (1 << 31) +/* COMMAND */ +# define PACKET3_CP_DMA_DIS_WC (1 << 21) +# define PACKET3_CP_DMA_CMD_SRC_SWAP(x) ((x) << 23) + /* 0 - none + * 1 - 8 in 16 + * 2 - 8 in 32 + * 3 - 8 in 64 + */ +# define PACKET3_CP_DMA_CMD_DST_SWAP(x) ((x) << 24) + /* 0 - none + * 1 - 8 in 16 + * 2 - 8 in 32 + * 3 - 8 in 64 + */ +# define PACKET3_CP_DMA_CMD_SAS (1 << 26) + /* 0 - memory + * 1 - register + */ +# define PACKET3_CP_DMA_CMD_DAS (1 << 27) + /* 0 - memory + * 1 - register + */ +# define PACKET3_CP_DMA_CMD_SAIC (1 << 28) +# define PACKET3_CP_DMA_CMD_DAIC (1 << 29) +#define PACKET3_SURFACE_SYNC 0x43 +# define PACKET3_CB0_DEST_BASE_ENA (1 << 6) +# define PACKET3_CB1_DEST_BASE_ENA (1 << 7) +# define PACKET3_CB2_DEST_BASE_ENA (1 << 8) +# define PACKET3_CB3_DEST_BASE_ENA (1 << 9) +# define PACKET3_CB4_DEST_BASE_ENA (1 << 10) +# define PACKET3_CB5_DEST_BASE_ENA (1 << 11) +# define PACKET3_CB6_DEST_BASE_ENA (1 << 12) +# define PACKET3_CB7_DEST_BASE_ENA (1 << 13) +# define PACKET3_DB_DEST_BASE_ENA (1 << 14) +# define PACKET3_CB8_DEST_BASE_ENA (1 << 15) +# define PACKET3_CB9_DEST_BASE_ENA (1 << 16) +# define PACKET3_CB10_DEST_BASE_ENA (1 << 17) +# define PACKET3_CB11_DEST_BASE_ENA (1 << 18) +# define PACKET3_FULL_CACHE_ENA (1 << 20) +# define PACKET3_TC_ACTION_ENA (1 << 23) +# define PACKET3_VC_ACTION_ENA (1 << 24) +# define PACKET3_CB_ACTION_ENA (1 << 25) +# define PACKET3_DB_ACTION_ENA (1 << 26) +# define PACKET3_SH_ACTION_ENA (1 << 27) +# define PACKET3_SX_ACTION_ENA (1 << 28) +#define PACKET3_ME_INITIALIZE 0x44 +#define PACKET3_ME_INITIALIZE_DEVICE_ID(x) ((x) << 16) +#define PACKET3_COND_WRITE 0x45 +#define PACKET3_EVENT_WRITE 0x46 +#define PACKET3_EVENT_WRITE_EOP 0x47 +#define PACKET3_EVENT_WRITE_EOS 0x48 +#define PACKET3_PREAMBLE_CNTL 0x4A +# define PACKET3_PREAMBLE_BEGIN_CLEAR_STATE (2 << 28) +# define PACKET3_PREAMBLE_END_CLEAR_STATE (3 << 28) +#define PACKET3_RB_OFFSET 0x4B +#define PACKET3_ALU_PS_CONST_BUFFER_COPY 0x4C +#define PACKET3_ALU_VS_CONST_BUFFER_COPY 0x4D +#define PACKET3_ALU_PS_CONST_UPDATE 0x4E +#define PACKET3_ALU_VS_CONST_UPDATE 0x4F +#define PACKET3_ONE_REG_WRITE 0x57 +#define PACKET3_SET_CONFIG_REG 0x68 +#define PACKET3_SET_CONFIG_REG_START 0x00008000 +#define PACKET3_SET_CONFIG_REG_END 0x0000ac00 +#define PACKET3_SET_CONTEXT_REG 0x69 +#define PACKET3_SET_CONTEXT_REG_START 0x00028000 +#define PACKET3_SET_CONTEXT_REG_END 0x00029000 +#define PACKET3_SET_ALU_CONST 0x6A +/* alu const buffers only; no reg file */ +#define PACKET3_SET_BOOL_CONST 0x6B +#define PACKET3_SET_BOOL_CONST_START 0x0003a500 +#define PACKET3_SET_BOOL_CONST_END 0x0003a518 +#define PACKET3_SET_LOOP_CONST 0x6C +#define PACKET3_SET_LOOP_CONST_START 0x0003a200 +#define PACKET3_SET_LOOP_CONST_END 0x0003a500 +#define PACKET3_SET_RESOURCE 0x6D +#define PACKET3_SET_RESOURCE_START 0x00030000 +#define PACKET3_SET_RESOURCE_END 0x00038000 +#define PACKET3_SET_SAMPLER 0x6E +#define PACKET3_SET_SAMPLER_START 0x0003c000 +#define PACKET3_SET_SAMPLER_END 0x0003c600 +#define PACKET3_SET_CTL_CONST 0x6F +#define PACKET3_SET_CTL_CONST_START 0x0003cff0 +#define PACKET3_SET_CTL_CONST_END 0x0003ff0c +#define PACKET3_SET_RESOURCE_OFFSET 0x70 +#define PACKET3_SET_ALU_CONST_VS 0x71 +#define PACKET3_SET_ALU_CONST_DI 0x72 +#define PACKET3_SET_CONTEXT_REG_INDIRECT 0x73 +#define PACKET3_SET_RESOURCE_INDIRECT 0x74 +#define PACKET3_SET_APPEND_CNT 0x75 + +#define SQ_RESOURCE_CONSTANT_WORD7_0 0x3001c +#define S__SQ_CONSTANT_TYPE(x) (((x) & 3) << 30) +#define G__SQ_CONSTANT_TYPE(x) (((x) >> 30) & 3) +#define SQ_TEX_VTX_INVALID_TEXTURE 0x0 +#define SQ_TEX_VTX_INVALID_BUFFER 0x1 +#define SQ_TEX_VTX_VALID_TEXTURE 0x2 +#define SQ_TEX_VTX_VALID_BUFFER 0x3 + +#define VGT_VTX_VECT_EJECT_REG 0x88b0 + +#define SQ_CONST_MEM_BASE 0x8df8 + +#define SQ_ESGS_RING_BASE 0x8c40 +#define SQ_ESGS_RING_SIZE 0x8c44 +#define SQ_GSVS_RING_BASE 0x8c48 +#define SQ_GSVS_RING_SIZE 0x8c4c +#define SQ_ESTMP_RING_BASE 0x8c50 +#define SQ_ESTMP_RING_SIZE 0x8c54 +#define SQ_GSTMP_RING_BASE 0x8c58 +#define SQ_GSTMP_RING_SIZE 0x8c5c +#define SQ_VSTMP_RING_BASE 0x8c60 +#define SQ_VSTMP_RING_SIZE 0x8c64 +#define SQ_PSTMP_RING_BASE 0x8c68 +#define SQ_PSTMP_RING_SIZE 0x8c6c +#define SQ_LSTMP_RING_BASE 0x8e10 +#define SQ_LSTMP_RING_SIZE 0x8e14 +#define SQ_HSTMP_RING_BASE 0x8e18 +#define SQ_HSTMP_RING_SIZE 0x8e1c +#define VGT_TF_RING_SIZE 0x8988 + +#define SQ_ESGS_RING_ITEMSIZE 0x28900 +#define SQ_GSVS_RING_ITEMSIZE 0x28904 +#define SQ_ESTMP_RING_ITEMSIZE 0x28908 +#define SQ_GSTMP_RING_ITEMSIZE 0x2890c +#define SQ_VSTMP_RING_ITEMSIZE 0x28910 +#define SQ_PSTMP_RING_ITEMSIZE 0x28914 +#define SQ_LSTMP_RING_ITEMSIZE 0x28830 +#define SQ_HSTMP_RING_ITEMSIZE 0x28834 + +#define SQ_GS_VERT_ITEMSIZE 0x2891c +#define SQ_GS_VERT_ITEMSIZE_1 0x28920 +#define SQ_GS_VERT_ITEMSIZE_2 0x28924 +#define SQ_GS_VERT_ITEMSIZE_3 0x28928 +#define SQ_GSVS_RING_OFFSET_1 0x2892c +#define SQ_GSVS_RING_OFFSET_2 0x28930 +#define SQ_GSVS_RING_OFFSET_3 0x28934 + +#define SQ_ALU_CONST_BUFFER_SIZE_PS_0 0x28140 +#define SQ_ALU_CONST_BUFFER_SIZE_HS_0 0x28f80 + +#define SQ_ALU_CONST_CACHE_PS_0 0x28940 +#define SQ_ALU_CONST_CACHE_PS_1 0x28944 +#define SQ_ALU_CONST_CACHE_PS_2 0x28948 +#define SQ_ALU_CONST_CACHE_PS_3 0x2894c +#define SQ_ALU_CONST_CACHE_PS_4 0x28950 +#define SQ_ALU_CONST_CACHE_PS_5 0x28954 +#define SQ_ALU_CONST_CACHE_PS_6 0x28958 +#define SQ_ALU_CONST_CACHE_PS_7 0x2895c +#define SQ_ALU_CONST_CACHE_PS_8 0x28960 +#define SQ_ALU_CONST_CACHE_PS_9 0x28964 +#define SQ_ALU_CONST_CACHE_PS_10 0x28968 +#define SQ_ALU_CONST_CACHE_PS_11 0x2896c +#define SQ_ALU_CONST_CACHE_PS_12 0x28970 +#define SQ_ALU_CONST_CACHE_PS_13 0x28974 +#define SQ_ALU_CONST_CACHE_PS_14 0x28978 +#define SQ_ALU_CONST_CACHE_PS_15 0x2897c +#define SQ_ALU_CONST_CACHE_VS_0 0x28980 +#define SQ_ALU_CONST_CACHE_VS_1 0x28984 +#define SQ_ALU_CONST_CACHE_VS_2 0x28988 +#define SQ_ALU_CONST_CACHE_VS_3 0x2898c +#define SQ_ALU_CONST_CACHE_VS_4 0x28990 +#define SQ_ALU_CONST_CACHE_VS_5 0x28994 +#define SQ_ALU_CONST_CACHE_VS_6 0x28998 +#define SQ_ALU_CONST_CACHE_VS_7 0x2899c +#define SQ_ALU_CONST_CACHE_VS_8 0x289a0 +#define SQ_ALU_CONST_CACHE_VS_9 0x289a4 +#define SQ_ALU_CONST_CACHE_VS_10 0x289a8 +#define SQ_ALU_CONST_CACHE_VS_11 0x289ac +#define SQ_ALU_CONST_CACHE_VS_12 0x289b0 +#define SQ_ALU_CONST_CACHE_VS_13 0x289b4 +#define SQ_ALU_CONST_CACHE_VS_14 0x289b8 +#define SQ_ALU_CONST_CACHE_VS_15 0x289bc +#define SQ_ALU_CONST_CACHE_GS_0 0x289c0 +#define SQ_ALU_CONST_CACHE_GS_1 0x289c4 +#define SQ_ALU_CONST_CACHE_GS_2 0x289c8 +#define SQ_ALU_CONST_CACHE_GS_3 0x289cc +#define SQ_ALU_CONST_CACHE_GS_4 0x289d0 +#define SQ_ALU_CONST_CACHE_GS_5 0x289d4 +#define SQ_ALU_CONST_CACHE_GS_6 0x289d8 +#define SQ_ALU_CONST_CACHE_GS_7 0x289dc +#define SQ_ALU_CONST_CACHE_GS_8 0x289e0 +#define SQ_ALU_CONST_CACHE_GS_9 0x289e4 +#define SQ_ALU_CONST_CACHE_GS_10 0x289e8 +#define SQ_ALU_CONST_CACHE_GS_11 0x289ec +#define SQ_ALU_CONST_CACHE_GS_12 0x289f0 +#define SQ_ALU_CONST_CACHE_GS_13 0x289f4 +#define SQ_ALU_CONST_CACHE_GS_14 0x289f8 +#define SQ_ALU_CONST_CACHE_GS_15 0x289fc +#define SQ_ALU_CONST_CACHE_HS_0 0x28f00 +#define SQ_ALU_CONST_CACHE_HS_1 0x28f04 +#define SQ_ALU_CONST_CACHE_HS_2 0x28f08 +#define SQ_ALU_CONST_CACHE_HS_3 0x28f0c +#define SQ_ALU_CONST_CACHE_HS_4 0x28f10 +#define SQ_ALU_CONST_CACHE_HS_5 0x28f14 +#define SQ_ALU_CONST_CACHE_HS_6 0x28f18 +#define SQ_ALU_CONST_CACHE_HS_7 0x28f1c +#define SQ_ALU_CONST_CACHE_HS_8 0x28f20 +#define SQ_ALU_CONST_CACHE_HS_9 0x28f24 +#define SQ_ALU_CONST_CACHE_HS_10 0x28f28 +#define SQ_ALU_CONST_CACHE_HS_11 0x28f2c +#define SQ_ALU_CONST_CACHE_HS_12 0x28f30 +#define SQ_ALU_CONST_CACHE_HS_13 0x28f34 +#define SQ_ALU_CONST_CACHE_HS_14 0x28f38 +#define SQ_ALU_CONST_CACHE_HS_15 0x28f3c +#define SQ_ALU_CONST_CACHE_LS_0 0x28f40 +#define SQ_ALU_CONST_CACHE_LS_1 0x28f44 +#define SQ_ALU_CONST_CACHE_LS_2 0x28f48 +#define SQ_ALU_CONST_CACHE_LS_3 0x28f4c +#define SQ_ALU_CONST_CACHE_LS_4 0x28f50 +#define SQ_ALU_CONST_CACHE_LS_5 0x28f54 +#define SQ_ALU_CONST_CACHE_LS_6 0x28f58 +#define SQ_ALU_CONST_CACHE_LS_7 0x28f5c +#define SQ_ALU_CONST_CACHE_LS_8 0x28f60 +#define SQ_ALU_CONST_CACHE_LS_9 0x28f64 +#define SQ_ALU_CONST_CACHE_LS_10 0x28f68 +#define SQ_ALU_CONST_CACHE_LS_11 0x28f6c +#define SQ_ALU_CONST_CACHE_LS_12 0x28f70 +#define SQ_ALU_CONST_CACHE_LS_13 0x28f74 +#define SQ_ALU_CONST_CACHE_LS_14 0x28f78 +#define SQ_ALU_CONST_CACHE_LS_15 0x28f7c + +#define PA_SC_SCREEN_SCISSOR_TL 0x28030 +#define PA_SC_GENERIC_SCISSOR_TL 0x28240 +#define PA_SC_WINDOW_SCISSOR_TL 0x28204 + +#define VGT_PRIMITIVE_TYPE 0x8958 +#define VGT_INDEX_TYPE 0x895C + +#define VGT_NUM_INDICES 0x8970 + +#define VGT_COMPUTE_DIM_X 0x8990 +#define VGT_COMPUTE_DIM_Y 0x8994 +#define VGT_COMPUTE_DIM_Z 0x8998 +#define VGT_COMPUTE_START_X 0x899C +#define VGT_COMPUTE_START_Y 0x89A0 +#define VGT_COMPUTE_START_Z 0x89A4 +#define VGT_COMPUTE_INDEX 0x89A8 +#define VGT_COMPUTE_THREAD_GROUP_SIZE 0x89AC +#define VGT_HS_OFFCHIP_PARAM 0x89B0 + +#define DB_DEBUG 0x9830 +#define DB_DEBUG2 0x9834 +#define DB_DEBUG3 0x9838 +#define DB_DEBUG4 0x983C +#define DB_WATERMARKS 0x9854 +#define DB_DEPTH_CONTROL 0x28800 +#define R_028800_DB_DEPTH_CONTROL 0x028800 +#define S_028800_STENCIL_ENABLE(x) (((x) & 0x1) << 0) +#define G_028800_STENCIL_ENABLE(x) (((x) >> 0) & 0x1) +#define C_028800_STENCIL_ENABLE 0xFFFFFFFE +#define S_028800_Z_ENABLE(x) (((x) & 0x1) << 1) +#define G_028800_Z_ENABLE(x) (((x) >> 1) & 0x1) +#define C_028800_Z_ENABLE 0xFFFFFFFD +#define S_028800_Z_WRITE_ENABLE(x) (((x) & 0x1) << 2) +#define G_028800_Z_WRITE_ENABLE(x) (((x) >> 2) & 0x1) +#define C_028800_Z_WRITE_ENABLE 0xFFFFFFFB +#define S_028800_ZFUNC(x) (((x) & 0x7) << 4) +#define G_028800_ZFUNC(x) (((x) >> 4) & 0x7) +#define C_028800_ZFUNC 0xFFFFFF8F +#define S_028800_BACKFACE_ENABLE(x) (((x) & 0x1) << 7) +#define G_028800_BACKFACE_ENABLE(x) (((x) >> 7) & 0x1) +#define C_028800_BACKFACE_ENABLE 0xFFFFFF7F +#define S_028800_STENCILFUNC(x) (((x) & 0x7) << 8) +#define G_028800_STENCILFUNC(x) (((x) >> 8) & 0x7) +#define C_028800_STENCILFUNC 0xFFFFF8FF +#define V_028800_STENCILFUNC_NEVER 0x00000000 +#define V_028800_STENCILFUNC_LESS 0x00000001 +#define V_028800_STENCILFUNC_EQUAL 0x00000002 +#define V_028800_STENCILFUNC_LEQUAL 0x00000003 +#define V_028800_STENCILFUNC_GREATER 0x00000004 +#define V_028800_STENCILFUNC_NOTEQUAL 0x00000005 +#define V_028800_STENCILFUNC_GEQUAL 0x00000006 +#define V_028800_STENCILFUNC_ALWAYS 0x00000007 +#define S_028800_STENCILFAIL(x) (((x) & 0x7) << 11) +#define G_028800_STENCILFAIL(x) (((x) >> 11) & 0x7) +#define C_028800_STENCILFAIL 0xFFFFC7FF +#define V_028800_STENCIL_KEEP 0x00000000 +#define V_028800_STENCIL_ZERO 0x00000001 +#define V_028800_STENCIL_REPLACE 0x00000002 +#define V_028800_STENCIL_INCR 0x00000003 +#define V_028800_STENCIL_DECR 0x00000004 +#define V_028800_STENCIL_INVERT 0x00000005 +#define V_028800_STENCIL_INCR_WRAP 0x00000006 +#define V_028800_STENCIL_DECR_WRAP 0x00000007 +#define S_028800_STENCILZPASS(x) (((x) & 0x7) << 14) +#define G_028800_STENCILZPASS(x) (((x) >> 14) & 0x7) +#define C_028800_STENCILZPASS 0xFFFE3FFF +#define S_028800_STENCILZFAIL(x) (((x) & 0x7) << 17) +#define G_028800_STENCILZFAIL(x) (((x) >> 17) & 0x7) +#define C_028800_STENCILZFAIL 0xFFF1FFFF +#define S_028800_STENCILFUNC_BF(x) (((x) & 0x7) << 20) +#define G_028800_STENCILFUNC_BF(x) (((x) >> 20) & 0x7) +#define C_028800_STENCILFUNC_BF 0xFF8FFFFF +#define S_028800_STENCILFAIL_BF(x) (((x) & 0x7) << 23) +#define G_028800_STENCILFAIL_BF(x) (((x) >> 23) & 0x7) +#define C_028800_STENCILFAIL_BF 0xFC7FFFFF +#define S_028800_STENCILZPASS_BF(x) (((x) & 0x7) << 26) +#define G_028800_STENCILZPASS_BF(x) (((x) >> 26) & 0x7) +#define C_028800_STENCILZPASS_BF 0xE3FFFFFF +#define S_028800_STENCILZFAIL_BF(x) (((x) & 0x7) << 29) +#define G_028800_STENCILZFAIL_BF(x) (((x) >> 29) & 0x7) +#define C_028800_STENCILZFAIL_BF 0x1FFFFFFF +#define DB_DEPTH_VIEW 0x28008 +#define R_028008_DB_DEPTH_VIEW 0x00028008 +#define S_028008_SLICE_START(x) (((x) & 0x7FF) << 0) +#define G_028008_SLICE_START(x) (((x) >> 0) & 0x7FF) +#define C_028008_SLICE_START 0xFFFFF800 +#define S_028008_SLICE_MAX(x) (((x) & 0x7FF) << 13) +#define G_028008_SLICE_MAX(x) (((x) >> 13) & 0x7FF) +#define C_028008_SLICE_MAX 0xFF001FFF +#define DB_HTILE_DATA_BASE 0x28014 +#define DB_HTILE_SURFACE 0x28abc +#define S_028ABC_HTILE_WIDTH(x) (((x) & 0x1) << 0) +#define G_028ABC_HTILE_WIDTH(x) (((x) >> 0) & 0x1) +#define C_028ABC_HTILE_WIDTH 0xFFFFFFFE +#define S_028ABC_HTILE_HEIGHT(x) (((x) & 0x1) << 1) +#define G_028ABC_HTILE_HEIGHT(x) (((x) >> 1) & 0x1) +#define C_028ABC_HTILE_HEIGHT 0xFFFFFFFD +#define G_028ABC_LINEAR(x) (((x) >> 2) & 0x1) +#define DB_Z_INFO 0x28040 +# define Z_ARRAY_MODE(x) ((x) << 4) +# define DB_TILE_SPLIT(x) (((x) & 0x7) << 8) +# define DB_NUM_BANKS(x) (((x) & 0x3) << 12) +# define DB_BANK_WIDTH(x) (((x) & 0x3) << 16) +# define DB_BANK_HEIGHT(x) (((x) & 0x3) << 20) +# define DB_MACRO_TILE_ASPECT(x) (((x) & 0x3) << 24) +#define R_028040_DB_Z_INFO 0x028040 +#define S_028040_FORMAT(x) (((x) & 0x3) << 0) +#define G_028040_FORMAT(x) (((x) >> 0) & 0x3) +#define C_028040_FORMAT 0xFFFFFFFC +#define V_028040_Z_INVALID 0x00000000 +#define V_028040_Z_16 0x00000001 +#define V_028040_Z_24 0x00000002 +#define V_028040_Z_32_FLOAT 0x00000003 +#define S_028040_ARRAY_MODE(x) (((x) & 0xF) << 4) +#define G_028040_ARRAY_MODE(x) (((x) >> 4) & 0xF) +#define C_028040_ARRAY_MODE 0xFFFFFF0F +#define S_028040_READ_SIZE(x) (((x) & 0x1) << 28) +#define G_028040_READ_SIZE(x) (((x) >> 28) & 0x1) +#define C_028040_READ_SIZE 0xEFFFFFFF +#define S_028040_TILE_SURFACE_ENABLE(x) (((x) & 0x1) << 29) +#define G_028040_TILE_SURFACE_ENABLE(x) (((x) >> 29) & 0x1) +#define C_028040_TILE_SURFACE_ENABLE 0xDFFFFFFF +#define S_028040_ZRANGE_PRECISION(x) (((x) & 0x1) << 31) +#define G_028040_ZRANGE_PRECISION(x) (((x) >> 31) & 0x1) +#define C_028040_ZRANGE_PRECISION 0x7FFFFFFF +#define S_028040_TILE_SPLIT(x) (((x) & 0x7) << 8) +#define G_028040_TILE_SPLIT(x) (((x) >> 8) & 0x7) +#define S_028040_NUM_BANKS(x) (((x) & 0x3) << 12) +#define G_028040_NUM_BANKS(x) (((x) >> 12) & 0x3) +#define S_028040_BANK_WIDTH(x) (((x) & 0x3) << 16) +#define G_028040_BANK_WIDTH(x) (((x) >> 16) & 0x3) +#define S_028040_BANK_HEIGHT(x) (((x) & 0x3) << 20) +#define G_028040_BANK_HEIGHT(x) (((x) >> 20) & 0x3) +#define S_028040_MACRO_TILE_ASPECT(x) (((x) & 0x3) << 24) +#define G_028040_MACRO_TILE_ASPECT(x) (((x) >> 24) & 0x3) +#define DB_STENCIL_INFO 0x28044 +#define R_028044_DB_STENCIL_INFO 0x028044 +#define S_028044_FORMAT(x) (((x) & 0x1) << 0) +#define G_028044_FORMAT(x) (((x) >> 0) & 0x1) +#define C_028044_FORMAT 0xFFFFFFFE +#define V_028044_STENCIL_INVALID 0 +#define V_028044_STENCIL_8 1 +#define G_028044_TILE_SPLIT(x) (((x) >> 8) & 0x7) +#define DB_Z_READ_BASE 0x28048 +#define DB_STENCIL_READ_BASE 0x2804c +#define DB_Z_WRITE_BASE 0x28050 +#define DB_STENCIL_WRITE_BASE 0x28054 +#define DB_DEPTH_SIZE 0x28058 +#define R_028058_DB_DEPTH_SIZE 0x028058 +#define S_028058_PITCH_TILE_MAX(x) (((x) & 0x7FF) << 0) +#define G_028058_PITCH_TILE_MAX(x) (((x) >> 0) & 0x7FF) +#define C_028058_PITCH_TILE_MAX 0xFFFFF800 +#define S_028058_HEIGHT_TILE_MAX(x) (((x) & 0x7FF) << 11) +#define G_028058_HEIGHT_TILE_MAX(x) (((x) >> 11) & 0x7FF) +#define C_028058_HEIGHT_TILE_MAX 0xFFC007FF +#define R_02805C_DB_DEPTH_SLICE 0x02805C +#define S_02805C_SLICE_TILE_MAX(x) (((x) & 0x3FFFFF) << 0) +#define G_02805C_SLICE_TILE_MAX(x) (((x) >> 0) & 0x3FFFFF) +#define C_02805C_SLICE_TILE_MAX 0xFFC00000 + +#define SQ_PGM_START_PS 0x28840 +#define SQ_PGM_START_VS 0x2885c +#define SQ_PGM_START_GS 0x28874 +#define SQ_PGM_START_ES 0x2888c +#define SQ_PGM_START_FS 0x288a4 +#define SQ_PGM_START_HS 0x288b8 +#define SQ_PGM_START_LS 0x288d0 + +#define VGT_STRMOUT_BUFFER_BASE_0 0x28AD8 +#define VGT_STRMOUT_BUFFER_BASE_1 0x28AE8 +#define VGT_STRMOUT_BUFFER_BASE_2 0x28AF8 +#define VGT_STRMOUT_BUFFER_BASE_3 0x28B08 +#define VGT_STRMOUT_BUFFER_SIZE_0 0x28AD0 +#define VGT_STRMOUT_BUFFER_SIZE_1 0x28AE0 +#define VGT_STRMOUT_BUFFER_SIZE_2 0x28AF0 +#define VGT_STRMOUT_BUFFER_SIZE_3 0x28B00 +#define VGT_STRMOUT_CONFIG 0x28b94 +#define VGT_STRMOUT_BUFFER_CONFIG 0x28b98 + +#define CB_TARGET_MASK 0x28238 +#define CB_SHADER_MASK 0x2823c + +#define GDS_ADDR_BASE 0x28720 + +#define CB_IMMED0_BASE 0x28b9c +#define CB_IMMED1_BASE 0x28ba0 +#define CB_IMMED2_BASE 0x28ba4 +#define CB_IMMED3_BASE 0x28ba8 +#define CB_IMMED4_BASE 0x28bac +#define CB_IMMED5_BASE 0x28bb0 +#define CB_IMMED6_BASE 0x28bb4 +#define CB_IMMED7_BASE 0x28bb8 +#define CB_IMMED8_BASE 0x28bbc +#define CB_IMMED9_BASE 0x28bc0 +#define CB_IMMED10_BASE 0x28bc4 +#define CB_IMMED11_BASE 0x28bc8 + +/* all 12 CB blocks have these regs */ +#define CB_COLOR0_BASE 0x28c60 +#define CB_COLOR0_PITCH 0x28c64 +#define CB_COLOR0_SLICE 0x28c68 +#define CB_COLOR0_VIEW 0x28c6c +#define R_028C6C_CB_COLOR0_VIEW 0x00028C6C +#define S_028C6C_SLICE_START(x) (((x) & 0x7FF) << 0) +#define G_028C6C_SLICE_START(x) (((x) >> 0) & 0x7FF) +#define C_028C6C_SLICE_START 0xFFFFF800 +#define S_028C6C_SLICE_MAX(x) (((x) & 0x7FF) << 13) +#define G_028C6C_SLICE_MAX(x) (((x) >> 13) & 0x7FF) +#define C_028C6C_SLICE_MAX 0xFF001FFF +#define R_028C70_CB_COLOR0_INFO 0x028C70 +#define S_028C70_ENDIAN(x) (((x) & 0x3) << 0) +#define G_028C70_ENDIAN(x) (((x) >> 0) & 0x3) +#define C_028C70_ENDIAN 0xFFFFFFFC +#define S_028C70_FORMAT(x) (((x) & 0x3F) << 2) +#define G_028C70_FORMAT(x) (((x) >> 2) & 0x3F) +#define C_028C70_FORMAT 0xFFFFFF03 +#define V_028C70_COLOR_INVALID 0x00000000 +#define V_028C70_COLOR_8 0x00000001 +#define V_028C70_COLOR_4_4 0x00000002 +#define V_028C70_COLOR_3_3_2 0x00000003 +#define V_028C70_COLOR_16 0x00000005 +#define V_028C70_COLOR_16_FLOAT 0x00000006 +#define V_028C70_COLOR_8_8 0x00000007 +#define V_028C70_COLOR_5_6_5 0x00000008 +#define V_028C70_COLOR_6_5_5 0x00000009 +#define V_028C70_COLOR_1_5_5_5 0x0000000A +#define V_028C70_COLOR_4_4_4_4 0x0000000B +#define V_028C70_COLOR_5_5_5_1 0x0000000C +#define V_028C70_COLOR_32 0x0000000D +#define V_028C70_COLOR_32_FLOAT 0x0000000E +#define V_028C70_COLOR_16_16 0x0000000F +#define V_028C70_COLOR_16_16_FLOAT 0x00000010 +#define V_028C70_COLOR_8_24 0x00000011 +#define V_028C70_COLOR_8_24_FLOAT 0x00000012 +#define V_028C70_COLOR_24_8 0x00000013 +#define V_028C70_COLOR_24_8_FLOAT 0x00000014 +#define V_028C70_COLOR_10_11_11 0x00000015 +#define V_028C70_COLOR_10_11_11_FLOAT 0x00000016 +#define V_028C70_COLOR_11_11_10 0x00000017 +#define V_028C70_COLOR_11_11_10_FLOAT 0x00000018 +#define V_028C70_COLOR_2_10_10_10 0x00000019 +#define V_028C70_COLOR_8_8_8_8 0x0000001A +#define V_028C70_COLOR_10_10_10_2 0x0000001B +#define V_028C70_COLOR_X24_8_32_FLOAT 0x0000001C +#define V_028C70_COLOR_32_32 0x0000001D +#define V_028C70_COLOR_32_32_FLOAT 0x0000001E +#define V_028C70_COLOR_16_16_16_16 0x0000001F +#define V_028C70_COLOR_16_16_16_16_FLOAT 0x00000020 +#define V_028C70_COLOR_32_32_32_32 0x00000022 +#define V_028C70_COLOR_32_32_32_32_FLOAT 0x00000023 +#define V_028C70_COLOR_32_32_32_FLOAT 0x00000030 +#define S_028C70_ARRAY_MODE(x) (((x) & 0xF) << 8) +#define G_028C70_ARRAY_MODE(x) (((x) >> 8) & 0xF) +#define C_028C70_ARRAY_MODE 0xFFFFF0FF +#define V_028C70_ARRAY_LINEAR_GENERAL 0x00000000 +#define V_028C70_ARRAY_LINEAR_ALIGNED 0x00000001 +#define V_028C70_ARRAY_1D_TILED_THIN1 0x00000002 +#define V_028C70_ARRAY_2D_TILED_THIN1 0x00000004 +#define S_028C70_NUMBER_TYPE(x) (((x) & 0x7) << 12) +#define G_028C70_NUMBER_TYPE(x) (((x) >> 12) & 0x7) +#define C_028C70_NUMBER_TYPE 0xFFFF8FFF +#define V_028C70_NUMBER_UNORM 0x00000000 +#define V_028C70_NUMBER_SNORM 0x00000001 +#define V_028C70_NUMBER_USCALED 0x00000002 +#define V_028C70_NUMBER_SSCALED 0x00000003 +#define V_028C70_NUMBER_UINT 0x00000004 +#define V_028C70_NUMBER_SINT 0x00000005 +#define V_028C70_NUMBER_SRGB 0x00000006 +#define V_028C70_NUMBER_FLOAT 0x00000007 +#define S_028C70_COMP_SWAP(x) (((x) & 0x3) << 15) +#define G_028C70_COMP_SWAP(x) (((x) >> 15) & 0x3) +#define C_028C70_COMP_SWAP 0xFFFE7FFF +#define V_028C70_SWAP_STD 0x00000000 +#define V_028C70_SWAP_ALT 0x00000001 +#define V_028C70_SWAP_STD_REV 0x00000002 +#define V_028C70_SWAP_ALT_REV 0x00000003 +#define S_028C70_FAST_CLEAR(x) (((x) & 0x1) << 17) +#define G_028C70_FAST_CLEAR(x) (((x) >> 17) & 0x1) +#define C_028C70_FAST_CLEAR 0xFFFDFFFF +#define S_028C70_COMPRESSION(x) (((x) & 0x3) << 18) +#define G_028C70_COMPRESSION(x) (((x) >> 18) & 0x3) +#define C_028C70_COMPRESSION 0xFFF3FFFF +#define S_028C70_BLEND_CLAMP(x) (((x) & 0x1) << 19) +#define G_028C70_BLEND_CLAMP(x) (((x) >> 19) & 0x1) +#define C_028C70_BLEND_CLAMP 0xFFF7FFFF +#define S_028C70_BLEND_BYPASS(x) (((x) & 0x1) << 20) +#define G_028C70_BLEND_BYPASS(x) (((x) >> 20) & 0x1) +#define C_028C70_BLEND_BYPASS 0xFFEFFFFF +#define S_028C70_SIMPLE_FLOAT(x) (((x) & 0x1) << 21) +#define G_028C70_SIMPLE_FLOAT(x) (((x) >> 21) & 0x1) +#define C_028C70_SIMPLE_FLOAT 0xFFDFFFFF +#define S_028C70_ROUND_MODE(x) (((x) & 0x1) << 22) +#define G_028C70_ROUND_MODE(x) (((x) >> 22) & 0x1) +#define C_028C70_ROUND_MODE 0xFFBFFFFF +#define S_028C70_TILE_COMPACT(x) (((x) & 0x1) << 23) +#define G_028C70_TILE_COMPACT(x) (((x) >> 23) & 0x1) +#define C_028C70_TILE_COMPACT 0xFF7FFFFF +#define S_028C70_SOURCE_FORMAT(x) (((x) & 0x3) << 24) +#define G_028C70_SOURCE_FORMAT(x) (((x) >> 24) & 0x3) +#define C_028C70_SOURCE_FORMAT 0xFCFFFFFF +#define V_028C70_EXPORT_4C_32BPC 0x0 +#define V_028C70_EXPORT_4C_16BPC 0x1 +#define V_028C70_EXPORT_2C_32BPC 0x2 /* Do not use */ +#define S_028C70_RAT(x) (((x) & 0x1) << 26) +#define G_028C70_RAT(x) (((x) >> 26) & 0x1) +#define C_028C70_RAT 0xFBFFFFFF +#define S_028C70_RESOURCE_TYPE(x) (((x) & 0x7) << 27) +#define G_028C70_RESOURCE_TYPE(x) (((x) >> 27) & 0x7) +#define C_028C70_RESOURCE_TYPE 0xC7FFFFFF + +#define CB_COLOR0_INFO 0x28c70 +# define CB_FORMAT(x) ((x) << 2) +# define CB_ARRAY_MODE(x) ((x) << 8) +# define ARRAY_LINEAR_GENERAL 0 +# define ARRAY_LINEAR_ALIGNED 1 +# define ARRAY_1D_TILED_THIN1 2 +# define ARRAY_2D_TILED_THIN1 4 +# define CB_SOURCE_FORMAT(x) ((x) << 24) +# define CB_SF_EXPORT_FULL 0 +# define CB_SF_EXPORT_NORM 1 +#define R_028C74_CB_COLOR0_ATTRIB 0x028C74 +#define S_028C74_NON_DISP_TILING_ORDER(x) (((x) & 0x1) << 4) +#define G_028C74_NON_DISP_TILING_ORDER(x) (((x) >> 4) & 0x1) +#define C_028C74_NON_DISP_TILING_ORDER 0xFFFFFFEF +#define S_028C74_TILE_SPLIT(x) (((x) & 0xf) << 5) +#define G_028C74_TILE_SPLIT(x) (((x) >> 5) & 0xf) +#define S_028C74_NUM_BANKS(x) (((x) & 0x3) << 10) +#define G_028C74_NUM_BANKS(x) (((x) >> 10) & 0x3) +#define S_028C74_BANK_WIDTH(x) (((x) & 0x3) << 13) +#define G_028C74_BANK_WIDTH(x) (((x) >> 13) & 0x3) +#define S_028C74_BANK_HEIGHT(x) (((x) & 0x3) << 16) +#define G_028C74_BANK_HEIGHT(x) (((x) >> 16) & 0x3) +#define S_028C74_MACRO_TILE_ASPECT(x) (((x) & 0x3) << 19) +#define G_028C74_MACRO_TILE_ASPECT(x) (((x) >> 19) & 0x3) +#define CB_COLOR0_ATTRIB 0x28c74 +# define CB_TILE_SPLIT(x) (((x) & 0x7) << 5) +# define ADDR_SURF_TILE_SPLIT_64B 0 +# define ADDR_SURF_TILE_SPLIT_128B 1 +# define ADDR_SURF_TILE_SPLIT_256B 2 +# define ADDR_SURF_TILE_SPLIT_512B 3 +# define ADDR_SURF_TILE_SPLIT_1KB 4 +# define ADDR_SURF_TILE_SPLIT_2KB 5 +# define ADDR_SURF_TILE_SPLIT_4KB 6 +# define CB_NUM_BANKS(x) (((x) & 0x3) << 10) +# define ADDR_SURF_2_BANK 0 +# define ADDR_SURF_4_BANK 1 +# define ADDR_SURF_8_BANK 2 +# define ADDR_SURF_16_BANK 3 +# define CB_BANK_WIDTH(x) (((x) & 0x3) << 13) +# define ADDR_SURF_BANK_WIDTH_1 0 +# define ADDR_SURF_BANK_WIDTH_2 1 +# define ADDR_SURF_BANK_WIDTH_4 2 +# define ADDR_SURF_BANK_WIDTH_8 3 +# define CB_BANK_HEIGHT(x) (((x) & 0x3) << 16) +# define ADDR_SURF_BANK_HEIGHT_1 0 +# define ADDR_SURF_BANK_HEIGHT_2 1 +# define ADDR_SURF_BANK_HEIGHT_4 2 +# define ADDR_SURF_BANK_HEIGHT_8 3 +# define CB_MACRO_TILE_ASPECT(x) (((x) & 0x3) << 19) +#define CB_COLOR0_DIM 0x28c78 +/* only CB0-7 blocks have these regs */ +#define CB_COLOR0_CMASK 0x28c7c +#define CB_COLOR0_CMASK_SLICE 0x28c80 +#define CB_COLOR0_FMASK 0x28c84 +#define CB_COLOR0_FMASK_SLICE 0x28c88 +#define CB_COLOR0_CLEAR_WORD0 0x28c8c +#define CB_COLOR0_CLEAR_WORD1 0x28c90 +#define CB_COLOR0_CLEAR_WORD2 0x28c94 +#define CB_COLOR0_CLEAR_WORD3 0x28c98 + +#define CB_COLOR1_BASE 0x28c9c +#define CB_COLOR2_BASE 0x28cd8 +#define CB_COLOR3_BASE 0x28d14 +#define CB_COLOR4_BASE 0x28d50 +#define CB_COLOR5_BASE 0x28d8c +#define CB_COLOR6_BASE 0x28dc8 +#define CB_COLOR7_BASE 0x28e04 +#define CB_COLOR8_BASE 0x28e40 +#define CB_COLOR9_BASE 0x28e5c +#define CB_COLOR10_BASE 0x28e78 +#define CB_COLOR11_BASE 0x28e94 + +#define CB_COLOR1_PITCH 0x28ca0 +#define CB_COLOR2_PITCH 0x28cdc +#define CB_COLOR3_PITCH 0x28d18 +#define CB_COLOR4_PITCH 0x28d54 +#define CB_COLOR5_PITCH 0x28d90 +#define CB_COLOR6_PITCH 0x28dcc +#define CB_COLOR7_PITCH 0x28e08 +#define CB_COLOR8_PITCH 0x28e44 +#define CB_COLOR9_PITCH 0x28e60 +#define CB_COLOR10_PITCH 0x28e7c +#define CB_COLOR11_PITCH 0x28e98 + +#define CB_COLOR1_SLICE 0x28ca4 +#define CB_COLOR2_SLICE 0x28ce0 +#define CB_COLOR3_SLICE 0x28d1c +#define CB_COLOR4_SLICE 0x28d58 +#define CB_COLOR5_SLICE 0x28d94 +#define CB_COLOR6_SLICE 0x28dd0 +#define CB_COLOR7_SLICE 0x28e0c +#define CB_COLOR8_SLICE 0x28e48 +#define CB_COLOR9_SLICE 0x28e64 +#define CB_COLOR10_SLICE 0x28e80 +#define CB_COLOR11_SLICE 0x28e9c + +#define CB_COLOR1_VIEW 0x28ca8 +#define CB_COLOR2_VIEW 0x28ce4 +#define CB_COLOR3_VIEW 0x28d20 +#define CB_COLOR4_VIEW 0x28d5c +#define CB_COLOR5_VIEW 0x28d98 +#define CB_COLOR6_VIEW 0x28dd4 +#define CB_COLOR7_VIEW 0x28e10 +#define CB_COLOR8_VIEW 0x28e4c +#define CB_COLOR9_VIEW 0x28e68 +#define CB_COLOR10_VIEW 0x28e84 +#define CB_COLOR11_VIEW 0x28ea0 + +#define CB_COLOR1_INFO 0x28cac +#define CB_COLOR2_INFO 0x28ce8 +#define CB_COLOR3_INFO 0x28d24 +#define CB_COLOR4_INFO 0x28d60 +#define CB_COLOR5_INFO 0x28d9c +#define CB_COLOR6_INFO 0x28dd8 +#define CB_COLOR7_INFO 0x28e14 +#define CB_COLOR8_INFO 0x28e50 +#define CB_COLOR9_INFO 0x28e6c +#define CB_COLOR10_INFO 0x28e88 +#define CB_COLOR11_INFO 0x28ea4 + +#define CB_COLOR1_ATTRIB 0x28cb0 +#define CB_COLOR2_ATTRIB 0x28cec +#define CB_COLOR3_ATTRIB 0x28d28 +#define CB_COLOR4_ATTRIB 0x28d64 +#define CB_COLOR5_ATTRIB 0x28da0 +#define CB_COLOR6_ATTRIB 0x28ddc +#define CB_COLOR7_ATTRIB 0x28e18 +#define CB_COLOR8_ATTRIB 0x28e54 +#define CB_COLOR9_ATTRIB 0x28e70 +#define CB_COLOR10_ATTRIB 0x28e8c +#define CB_COLOR11_ATTRIB 0x28ea8 + +#define CB_COLOR1_DIM 0x28cb4 +#define CB_COLOR2_DIM 0x28cf0 +#define CB_COLOR3_DIM 0x28d2c +#define CB_COLOR4_DIM 0x28d68 +#define CB_COLOR5_DIM 0x28da4 +#define CB_COLOR6_DIM 0x28de0 +#define CB_COLOR7_DIM 0x28e1c +#define CB_COLOR8_DIM 0x28e58 +#define CB_COLOR9_DIM 0x28e74 +#define CB_COLOR10_DIM 0x28e90 +#define CB_COLOR11_DIM 0x28eac + +#define CB_COLOR1_CMASK 0x28cb8 +#define CB_COLOR2_CMASK 0x28cf4 +#define CB_COLOR3_CMASK 0x28d30 +#define CB_COLOR4_CMASK 0x28d6c +#define CB_COLOR5_CMASK 0x28da8 +#define CB_COLOR6_CMASK 0x28de4 +#define CB_COLOR7_CMASK 0x28e20 + +#define CB_COLOR1_CMASK_SLICE 0x28cbc +#define CB_COLOR2_CMASK_SLICE 0x28cf8 +#define CB_COLOR3_CMASK_SLICE 0x28d34 +#define CB_COLOR4_CMASK_SLICE 0x28d70 +#define CB_COLOR5_CMASK_SLICE 0x28dac +#define CB_COLOR6_CMASK_SLICE 0x28de8 +#define CB_COLOR7_CMASK_SLICE 0x28e24 + +#define CB_COLOR1_FMASK 0x28cc0 +#define CB_COLOR2_FMASK 0x28cfc +#define CB_COLOR3_FMASK 0x28d38 +#define CB_COLOR4_FMASK 0x28d74 +#define CB_COLOR5_FMASK 0x28db0 +#define CB_COLOR6_FMASK 0x28dec +#define CB_COLOR7_FMASK 0x28e28 + +#define CB_COLOR1_FMASK_SLICE 0x28cc4 +#define CB_COLOR2_FMASK_SLICE 0x28d00 +#define CB_COLOR3_FMASK_SLICE 0x28d3c +#define CB_COLOR4_FMASK_SLICE 0x28d78 +#define CB_COLOR5_FMASK_SLICE 0x28db4 +#define CB_COLOR6_FMASK_SLICE 0x28df0 +#define CB_COLOR7_FMASK_SLICE 0x28e2c + +#define CB_COLOR1_CLEAR_WORD0 0x28cc8 +#define CB_COLOR2_CLEAR_WORD0 0x28d04 +#define CB_COLOR3_CLEAR_WORD0 0x28d40 +#define CB_COLOR4_CLEAR_WORD0 0x28d7c +#define CB_COLOR5_CLEAR_WORD0 0x28db8 +#define CB_COLOR6_CLEAR_WORD0 0x28df4 +#define CB_COLOR7_CLEAR_WORD0 0x28e30 + +#define CB_COLOR1_CLEAR_WORD1 0x28ccc +#define CB_COLOR2_CLEAR_WORD1 0x28d08 +#define CB_COLOR3_CLEAR_WORD1 0x28d44 +#define CB_COLOR4_CLEAR_WORD1 0x28d80 +#define CB_COLOR5_CLEAR_WORD1 0x28dbc +#define CB_COLOR6_CLEAR_WORD1 0x28df8 +#define CB_COLOR7_CLEAR_WORD1 0x28e34 + +#define CB_COLOR1_CLEAR_WORD2 0x28cd0 +#define CB_COLOR2_CLEAR_WORD2 0x28d0c +#define CB_COLOR3_CLEAR_WORD2 0x28d48 +#define CB_COLOR4_CLEAR_WORD2 0x28d84 +#define CB_COLOR5_CLEAR_WORD2 0x28dc0 +#define CB_COLOR6_CLEAR_WORD2 0x28dfc +#define CB_COLOR7_CLEAR_WORD2 0x28e38 + +#define CB_COLOR1_CLEAR_WORD3 0x28cd4 +#define CB_COLOR2_CLEAR_WORD3 0x28d10 +#define CB_COLOR3_CLEAR_WORD3 0x28d4c +#define CB_COLOR4_CLEAR_WORD3 0x28d88 +#define CB_COLOR5_CLEAR_WORD3 0x28dc4 +#define CB_COLOR6_CLEAR_WORD3 0x28e00 +#define CB_COLOR7_CLEAR_WORD3 0x28e3c + +#define SQ_TEX_RESOURCE_WORD0_0 0x30000 +# define TEX_DIM(x) ((x) << 0) +# define SQ_TEX_DIM_1D 0 +# define SQ_TEX_DIM_2D 1 +# define SQ_TEX_DIM_3D 2 +# define SQ_TEX_DIM_CUBEMAP 3 +# define SQ_TEX_DIM_1D_ARRAY 4 +# define SQ_TEX_DIM_2D_ARRAY 5 +# define SQ_TEX_DIM_2D_MSAA 6 +# define SQ_TEX_DIM_2D_ARRAY_MSAA 7 +#define SQ_TEX_RESOURCE_WORD1_0 0x30004 +# define TEX_ARRAY_MODE(x) ((x) << 28) +#define SQ_TEX_RESOURCE_WORD2_0 0x30008 +#define SQ_TEX_RESOURCE_WORD3_0 0x3000C +#define SQ_TEX_RESOURCE_WORD4_0 0x30010 +# define TEX_DST_SEL_X(x) ((x) << 16) +# define TEX_DST_SEL_Y(x) ((x) << 19) +# define TEX_DST_SEL_Z(x) ((x) << 22) +# define TEX_DST_SEL_W(x) ((x) << 25) +# define SQ_SEL_X 0 +# define SQ_SEL_Y 1 +# define SQ_SEL_Z 2 +# define SQ_SEL_W 3 +# define SQ_SEL_0 4 +# define SQ_SEL_1 5 +#define SQ_TEX_RESOURCE_WORD5_0 0x30014 +#define SQ_TEX_RESOURCE_WORD6_0 0x30018 +# define TEX_TILE_SPLIT(x) (((x) & 0x7) << 29) +#define SQ_TEX_RESOURCE_WORD7_0 0x3001c +# define MACRO_TILE_ASPECT(x) (((x) & 0x3) << 6) +# define TEX_BANK_WIDTH(x) (((x) & 0x3) << 8) +# define TEX_BANK_HEIGHT(x) (((x) & 0x3) << 10) +# define TEX_NUM_BANKS(x) (((x) & 0x3) << 16) +#define R_030000_SQ_TEX_RESOURCE_WORD0_0 0x030000 +#define S_030000_DIM(x) (((x) & 0x7) << 0) +#define G_030000_DIM(x) (((x) >> 0) & 0x7) +#define C_030000_DIM 0xFFFFFFF8 +#define V_030000_SQ_TEX_DIM_1D 0x00000000 +#define V_030000_SQ_TEX_DIM_2D 0x00000001 +#define V_030000_SQ_TEX_DIM_3D 0x00000002 +#define V_030000_SQ_TEX_DIM_CUBEMAP 0x00000003 +#define V_030000_SQ_TEX_DIM_1D_ARRAY 0x00000004 +#define V_030000_SQ_TEX_DIM_2D_ARRAY 0x00000005 +#define V_030000_SQ_TEX_DIM_2D_MSAA 0x00000006 +#define V_030000_SQ_TEX_DIM_2D_ARRAY_MSAA 0x00000007 +#define S_030000_NON_DISP_TILING_ORDER(x) (((x) & 0x1) << 5) +#define G_030000_NON_DISP_TILING_ORDER(x) (((x) >> 5) & 0x1) +#define C_030000_NON_DISP_TILING_ORDER 0xFFFFFFDF +#define S_030000_PITCH(x) (((x) & 0xFFF) << 6) +#define G_030000_PITCH(x) (((x) >> 6) & 0xFFF) +#define C_030000_PITCH 0xFFFC003F +#define S_030000_TEX_WIDTH(x) (((x) & 0x3FFF) << 18) +#define G_030000_TEX_WIDTH(x) (((x) >> 18) & 0x3FFF) +#define C_030000_TEX_WIDTH 0x0003FFFF +#define R_030004_SQ_TEX_RESOURCE_WORD1_0 0x030004 +#define S_030004_TEX_HEIGHT(x) (((x) & 0x3FFF) << 0) +#define G_030004_TEX_HEIGHT(x) (((x) >> 0) & 0x3FFF) +#define C_030004_TEX_HEIGHT 0xFFFFC000 +#define S_030004_TEX_DEPTH(x) (((x) & 0x1FFF) << 14) +#define G_030004_TEX_DEPTH(x) (((x) >> 14) & 0x1FFF) +#define C_030004_TEX_DEPTH 0xF8003FFF +#define S_030004_ARRAY_MODE(x) (((x) & 0xF) << 28) +#define G_030004_ARRAY_MODE(x) (((x) >> 28) & 0xF) +#define C_030004_ARRAY_MODE 0x0FFFFFFF +#define R_030008_SQ_TEX_RESOURCE_WORD2_0 0x030008 +#define S_030008_BASE_ADDRESS(x) (((x) & 0xFFFFFFFF) << 0) +#define G_030008_BASE_ADDRESS(x) (((x) >> 0) & 0xFFFFFFFF) +#define C_030008_BASE_ADDRESS 0x00000000 +#define R_03000C_SQ_TEX_RESOURCE_WORD3_0 0x03000C +#define S_03000C_MIP_ADDRESS(x) (((x) & 0xFFFFFFFF) << 0) +#define G_03000C_MIP_ADDRESS(x) (((x) >> 0) & 0xFFFFFFFF) +#define C_03000C_MIP_ADDRESS 0x00000000 +#define R_030010_SQ_TEX_RESOURCE_WORD4_0 0x030010 +#define S_030010_FORMAT_COMP_X(x) (((x) & 0x3) << 0) +#define G_030010_FORMAT_COMP_X(x) (((x) >> 0) & 0x3) +#define C_030010_FORMAT_COMP_X 0xFFFFFFFC +#define V_030010_SQ_FORMAT_COMP_UNSIGNED 0x00000000 +#define V_030010_SQ_FORMAT_COMP_SIGNED 0x00000001 +#define V_030010_SQ_FORMAT_COMP_UNSIGNED_BIASED 0x00000002 +#define S_030010_FORMAT_COMP_Y(x) (((x) & 0x3) << 2) +#define G_030010_FORMAT_COMP_Y(x) (((x) >> 2) & 0x3) +#define C_030010_FORMAT_COMP_Y 0xFFFFFFF3 +#define S_030010_FORMAT_COMP_Z(x) (((x) & 0x3) << 4) +#define G_030010_FORMAT_COMP_Z(x) (((x) >> 4) & 0x3) +#define C_030010_FORMAT_COMP_Z 0xFFFFFFCF +#define S_030010_FORMAT_COMP_W(x) (((x) & 0x3) << 6) +#define G_030010_FORMAT_COMP_W(x) (((x) >> 6) & 0x3) +#define C_030010_FORMAT_COMP_W 0xFFFFFF3F +#define S_030010_NUM_FORMAT_ALL(x) (((x) & 0x3) << 8) +#define G_030010_NUM_FORMAT_ALL(x) (((x) >> 8) & 0x3) +#define C_030010_NUM_FORMAT_ALL 0xFFFFFCFF +#define V_030010_SQ_NUM_FORMAT_NORM 0x00000000 +#define V_030010_SQ_NUM_FORMAT_INT 0x00000001 +#define V_030010_SQ_NUM_FORMAT_SCALED 0x00000002 +#define S_030010_SRF_MODE_ALL(x) (((x) & 0x1) << 10) +#define G_030010_SRF_MODE_ALL(x) (((x) >> 10) & 0x1) +#define C_030010_SRF_MODE_ALL 0xFFFFFBFF +#define V_030010_SRF_MODE_ZERO_CLAMP_MINUS_ONE 0x00000000 +#define V_030010_SRF_MODE_NO_ZERO 0x00000001 +#define S_030010_FORCE_DEGAMMA(x) (((x) & 0x1) << 11) +#define G_030010_FORCE_DEGAMMA(x) (((x) >> 11) & 0x1) +#define C_030010_FORCE_DEGAMMA 0xFFFFF7FF +#define S_030010_ENDIAN_SWAP(x) (((x) & 0x3) << 12) +#define G_030010_ENDIAN_SWAP(x) (((x) >> 12) & 0x3) +#define C_030010_ENDIAN_SWAP 0xFFFFCFFF +#define S_030010_DST_SEL_X(x) (((x) & 0x7) << 16) +#define G_030010_DST_SEL_X(x) (((x) >> 16) & 0x7) +#define C_030010_DST_SEL_X 0xFFF8FFFF +#define V_030010_SQ_SEL_X 0x00000000 +#define V_030010_SQ_SEL_Y 0x00000001 +#define V_030010_SQ_SEL_Z 0x00000002 +#define V_030010_SQ_SEL_W 0x00000003 +#define V_030010_SQ_SEL_0 0x00000004 +#define V_030010_SQ_SEL_1 0x00000005 +#define S_030010_DST_SEL_Y(x) (((x) & 0x7) << 19) +#define G_030010_DST_SEL_Y(x) (((x) >> 19) & 0x7) +#define C_030010_DST_SEL_Y 0xFFC7FFFF +#define S_030010_DST_SEL_Z(x) (((x) & 0x7) << 22) +#define G_030010_DST_SEL_Z(x) (((x) >> 22) & 0x7) +#define C_030010_DST_SEL_Z 0xFE3FFFFF +#define S_030010_DST_SEL_W(x) (((x) & 0x7) << 25) +#define G_030010_DST_SEL_W(x) (((x) >> 25) & 0x7) +#define C_030010_DST_SEL_W 0xF1FFFFFF +#define S_030010_BASE_LEVEL(x) (((x) & 0xF) << 28) +#define G_030010_BASE_LEVEL(x) (((x) >> 28) & 0xF) +#define C_030010_BASE_LEVEL 0x0FFFFFFF +#define R_030014_SQ_TEX_RESOURCE_WORD5_0 0x030014 +#define S_030014_LAST_LEVEL(x) (((x) & 0xF) << 0) +#define G_030014_LAST_LEVEL(x) (((x) >> 0) & 0xF) +#define C_030014_LAST_LEVEL 0xFFFFFFF0 +#define S_030014_BASE_ARRAY(x) (((x) & 0x1FFF) << 4) +#define G_030014_BASE_ARRAY(x) (((x) >> 4) & 0x1FFF) +#define C_030014_BASE_ARRAY 0xFFFE000F +#define S_030014_LAST_ARRAY(x) (((x) & 0x1FFF) << 17) +#define G_030014_LAST_ARRAY(x) (((x) >> 17) & 0x1FFF) +#define C_030014_LAST_ARRAY 0xC001FFFF +#define R_030018_SQ_TEX_RESOURCE_WORD6_0 0x030018 +#define S_030018_MAX_ANISO(x) (((x) & 0x7) << 0) +#define G_030018_MAX_ANISO(x) (((x) >> 0) & 0x7) +#define C_030018_MAX_ANISO 0xFFFFFFF8 +#define S_030018_PERF_MODULATION(x) (((x) & 0x7) << 3) +#define G_030018_PERF_MODULATION(x) (((x) >> 3) & 0x7) +#define C_030018_PERF_MODULATION 0xFFFFFFC7 +#define S_030018_INTERLACED(x) (((x) & 0x1) << 6) +#define G_030018_INTERLACED(x) (((x) >> 6) & 0x1) +#define C_030018_INTERLACED 0xFFFFFFBF +#define S_030018_TILE_SPLIT(x) (((x) & 0x7) << 29) +#define G_030018_TILE_SPLIT(x) (((x) >> 29) & 0x7) +#define R_03001C_SQ_TEX_RESOURCE_WORD7_0 0x03001C +#define S_03001C_MACRO_TILE_ASPECT(x) (((x) & 0x3) << 6) +#define G_03001C_MACRO_TILE_ASPECT(x) (((x) >> 6) & 0x3) +#define S_03001C_BANK_WIDTH(x) (((x) & 0x3) << 8) +#define G_03001C_BANK_WIDTH(x) (((x) >> 8) & 0x3) +#define S_03001C_BANK_HEIGHT(x) (((x) & 0x3) << 10) +#define G_03001C_BANK_HEIGHT(x) (((x) >> 10) & 0x3) +#define S_03001C_NUM_BANKS(x) (((x) & 0x3) << 16) +#define G_03001C_NUM_BANKS(x) (((x) >> 16) & 0x3) +#define S_03001C_TYPE(x) (((x) & 0x3) << 30) +#define G_03001C_TYPE(x) (((x) >> 30) & 0x3) +#define C_03001C_TYPE 0x3FFFFFFF +#define V_03001C_SQ_TEX_VTX_INVALID_TEXTURE 0x00000000 +#define V_03001C_SQ_TEX_VTX_INVALID_BUFFER 0x00000001 +#define V_03001C_SQ_TEX_VTX_VALID_TEXTURE 0x00000002 +#define V_03001C_SQ_TEX_VTX_VALID_BUFFER 0x00000003 +#define S_03001C_DATA_FORMAT(x) (((x) & 0x3F) << 0) +#define G_03001C_DATA_FORMAT(x) (((x) >> 0) & 0x3F) +#define C_03001C_DATA_FORMAT 0xFFFFFFC0 + +#define SQ_VTX_CONSTANT_WORD0_0 0x30000 +#define SQ_VTX_CONSTANT_WORD1_0 0x30004 +#define SQ_VTX_CONSTANT_WORD2_0 0x30008 +# define SQ_VTXC_BASE_ADDR_HI(x) ((x) << 0) +# define SQ_VTXC_STRIDE(x) ((x) << 8) +# define SQ_VTXC_ENDIAN_SWAP(x) ((x) << 30) +# define SQ_ENDIAN_NONE 0 +# define SQ_ENDIAN_8IN16 1 +# define SQ_ENDIAN_8IN32 2 +#define SQ_VTX_CONSTANT_WORD3_0 0x3000C +# define SQ_VTCX_SEL_X(x) ((x) << 3) +# define SQ_VTCX_SEL_Y(x) ((x) << 6) +# define SQ_VTCX_SEL_Z(x) ((x) << 9) +# define SQ_VTCX_SEL_W(x) ((x) << 12) +#define SQ_VTX_CONSTANT_WORD4_0 0x30010 +#define SQ_VTX_CONSTANT_WORD5_0 0x30014 +#define SQ_VTX_CONSTANT_WORD6_0 0x30018 +#define SQ_VTX_CONSTANT_WORD7_0 0x3001c + +#define TD_PS_BORDER_COLOR_INDEX 0xA400 +#define TD_PS_BORDER_COLOR_RED 0xA404 +#define TD_PS_BORDER_COLOR_GREEN 0xA408 +#define TD_PS_BORDER_COLOR_BLUE 0xA40C +#define TD_PS_BORDER_COLOR_ALPHA 0xA410 +#define TD_VS_BORDER_COLOR_INDEX 0xA414 +#define TD_VS_BORDER_COLOR_RED 0xA418 +#define TD_VS_BORDER_COLOR_GREEN 0xA41C +#define TD_VS_BORDER_COLOR_BLUE 0xA420 +#define TD_VS_BORDER_COLOR_ALPHA 0xA424 +#define TD_GS_BORDER_COLOR_INDEX 0xA428 +#define TD_GS_BORDER_COLOR_RED 0xA42C +#define TD_GS_BORDER_COLOR_GREEN 0xA430 +#define TD_GS_BORDER_COLOR_BLUE 0xA434 +#define TD_GS_BORDER_COLOR_ALPHA 0xA438 +#define TD_HS_BORDER_COLOR_INDEX 0xA43C +#define TD_HS_BORDER_COLOR_RED 0xA440 +#define TD_HS_BORDER_COLOR_GREEN 0xA444 +#define TD_HS_BORDER_COLOR_BLUE 0xA448 +#define TD_HS_BORDER_COLOR_ALPHA 0xA44C +#define TD_LS_BORDER_COLOR_INDEX 0xA450 +#define TD_LS_BORDER_COLOR_RED 0xA454 +#define TD_LS_BORDER_COLOR_GREEN 0xA458 +#define TD_LS_BORDER_COLOR_BLUE 0xA45C +#define TD_LS_BORDER_COLOR_ALPHA 0xA460 +#define TD_CS_BORDER_COLOR_INDEX 0xA464 +#define TD_CS_BORDER_COLOR_RED 0xA468 +#define TD_CS_BORDER_COLOR_GREEN 0xA46C +#define TD_CS_BORDER_COLOR_BLUE 0xA470 +#define TD_CS_BORDER_COLOR_ALPHA 0xA474 + +/* cayman 3D regs */ +#define CAYMAN_VGT_OFFCHIP_LDS_BASE 0x89B4 +#define CAYMAN_SQ_EX_ALLOC_TABLE_SLOTS 0x8E48 +#define CAYMAN_DB_EQAA 0x28804 +#define CAYMAN_DB_DEPTH_INFO 0x2803C +#define CAYMAN_PA_SC_AA_CONFIG 0x28BE0 +#define CAYMAN_MSAA_NUM_SAMPLES_SHIFT 0 +#define CAYMAN_MSAA_NUM_SAMPLES_MASK 0x7 +#define CAYMAN_SX_SCATTER_EXPORT_BASE 0x28358 +/* cayman packet3 addition */ +#define CAYMAN_PACKET3_DEALLOC_STATE 0x14 + +/* DMA regs common on r6xx/r7xx/evergreen/ni */ +#define DMA_RB_CNTL 0xd000 +# define DMA_RB_ENABLE (1 << 0) +# define DMA_RB_SIZE(x) ((x) << 1) /* log2 */ +# define DMA_RB_SWAP_ENABLE (1 << 9) /* 8IN32 */ +# define DMA_RPTR_WRITEBACK_ENABLE (1 << 12) +# define DMA_RPTR_WRITEBACK_SWAP_ENABLE (1 << 13) /* 8IN32 */ +# define DMA_RPTR_WRITEBACK_TIMER(x) ((x) << 16) /* log2 */ +#define DMA_STATUS_REG 0xd034 +# define DMA_IDLE (1 << 0) + +#endif diff --git a/sys/dev/drm2/radeon/ni.c b/sys/dev/drm2/radeon/ni.c new file mode 100644 index 00000000000..7995684409d --- /dev/null +++ b/sys/dev/drm2/radeon/ni.c @@ -0,0 +1,1980 @@ +/* + * Copyright 2010 Advanced Micro Devices, Inc. + * + * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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. + * + * Authors: Alex Deucher + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include "radeon.h" +#include "radeon_asic.h" +#include +#include "nid.h" +#include "atom.h" +#include "ni_reg.h" +#include "cayman_blit_shaders.h" + +extern void evergreen_pcie_gen2_enable(struct radeon_device *rdev); + +#define EVERGREEN_PFP_UCODE_SIZE 1120 +#define EVERGREEN_PM4_UCODE_SIZE 1376 +#define EVERGREEN_RLC_UCODE_SIZE 768 +#define BTC_MC_UCODE_SIZE 6024 + +#define CAYMAN_PFP_UCODE_SIZE 2176 +#define CAYMAN_PM4_UCODE_SIZE 2176 +#define CAYMAN_RLC_UCODE_SIZE 1024 +#define CAYMAN_MC_UCODE_SIZE 6037 + +#define ARUBA_RLC_UCODE_SIZE 1536 + +#define BTC_IO_MC_REGS_SIZE 29 + +static const u32 barts_io_mc_regs[BTC_IO_MC_REGS_SIZE][2] = { + {0x00000077, 0xff010100}, + {0x00000078, 0x00000000}, + {0x00000079, 0x00001434}, + {0x0000007a, 0xcc08ec08}, + {0x0000007b, 0x00040000}, + {0x0000007c, 0x000080c0}, + {0x0000007d, 0x09000000}, + {0x0000007e, 0x00210404}, + {0x00000081, 0x08a8e800}, + {0x00000082, 0x00030444}, + {0x00000083, 0x00000000}, + {0x00000085, 0x00000001}, + {0x00000086, 0x00000002}, + {0x00000087, 0x48490000}, + {0x00000088, 0x20244647}, + {0x00000089, 0x00000005}, + {0x0000008b, 0x66030000}, + {0x0000008c, 0x00006603}, + {0x0000008d, 0x00000100}, + {0x0000008f, 0x00001c0a}, + {0x00000090, 0xff000001}, + {0x00000094, 0x00101101}, + {0x00000095, 0x00000fff}, + {0x00000096, 0x00116fff}, + {0x00000097, 0x60010000}, + {0x00000098, 0x10010000}, + {0x00000099, 0x00006000}, + {0x0000009a, 0x00001000}, + {0x0000009f, 0x00946a00} +}; + +static const u32 turks_io_mc_regs[BTC_IO_MC_REGS_SIZE][2] = { + {0x00000077, 0xff010100}, + {0x00000078, 0x00000000}, + {0x00000079, 0x00001434}, + {0x0000007a, 0xcc08ec08}, + {0x0000007b, 0x00040000}, + {0x0000007c, 0x000080c0}, + {0x0000007d, 0x09000000}, + {0x0000007e, 0x00210404}, + {0x00000081, 0x08a8e800}, + {0x00000082, 0x00030444}, + {0x00000083, 0x00000000}, + {0x00000085, 0x00000001}, + {0x00000086, 0x00000002}, + {0x00000087, 0x48490000}, + {0x00000088, 0x20244647}, + {0x00000089, 0x00000005}, + {0x0000008b, 0x66030000}, + {0x0000008c, 0x00006603}, + {0x0000008d, 0x00000100}, + {0x0000008f, 0x00001c0a}, + {0x00000090, 0xff000001}, + {0x00000094, 0x00101101}, + {0x00000095, 0x00000fff}, + {0x00000096, 0x00116fff}, + {0x00000097, 0x60010000}, + {0x00000098, 0x10010000}, + {0x00000099, 0x00006000}, + {0x0000009a, 0x00001000}, + {0x0000009f, 0x00936a00} +}; + +static const u32 caicos_io_mc_regs[BTC_IO_MC_REGS_SIZE][2] = { + {0x00000077, 0xff010100}, + {0x00000078, 0x00000000}, + {0x00000079, 0x00001434}, + {0x0000007a, 0xcc08ec08}, + {0x0000007b, 0x00040000}, + {0x0000007c, 0x000080c0}, + {0x0000007d, 0x09000000}, + {0x0000007e, 0x00210404}, + {0x00000081, 0x08a8e800}, + {0x00000082, 0x00030444}, + {0x00000083, 0x00000000}, + {0x00000085, 0x00000001}, + {0x00000086, 0x00000002}, + {0x00000087, 0x48490000}, + {0x00000088, 0x20244647}, + {0x00000089, 0x00000005}, + {0x0000008b, 0x66030000}, + {0x0000008c, 0x00006603}, + {0x0000008d, 0x00000100}, + {0x0000008f, 0x00001c0a}, + {0x00000090, 0xff000001}, + {0x00000094, 0x00101101}, + {0x00000095, 0x00000fff}, + {0x00000096, 0x00116fff}, + {0x00000097, 0x60010000}, + {0x00000098, 0x10010000}, + {0x00000099, 0x00006000}, + {0x0000009a, 0x00001000}, + {0x0000009f, 0x00916a00} +}; + +static const u32 cayman_io_mc_regs[BTC_IO_MC_REGS_SIZE][2] = { + {0x00000077, 0xff010100}, + {0x00000078, 0x00000000}, + {0x00000079, 0x00001434}, + {0x0000007a, 0xcc08ec08}, + {0x0000007b, 0x00040000}, + {0x0000007c, 0x000080c0}, + {0x0000007d, 0x09000000}, + {0x0000007e, 0x00210404}, + {0x00000081, 0x08a8e800}, + {0x00000082, 0x00030444}, + {0x00000083, 0x00000000}, + {0x00000085, 0x00000001}, + {0x00000086, 0x00000002}, + {0x00000087, 0x48490000}, + {0x00000088, 0x20244647}, + {0x00000089, 0x00000005}, + {0x0000008b, 0x66030000}, + {0x0000008c, 0x00006603}, + {0x0000008d, 0x00000100}, + {0x0000008f, 0x00001c0a}, + {0x00000090, 0xff000001}, + {0x00000094, 0x00101101}, + {0x00000095, 0x00000fff}, + {0x00000096, 0x00116fff}, + {0x00000097, 0x60010000}, + {0x00000098, 0x10010000}, + {0x00000099, 0x00006000}, + {0x0000009a, 0x00001000}, + {0x0000009f, 0x00976b00} +}; + +int ni_mc_load_microcode(struct radeon_device *rdev) +{ + const __be32 *fw_data; + u32 mem_type, running, blackout = 0; + u32 *io_mc_regs; + int i, ucode_size, regs_size; + + if (!rdev->mc_fw) + return -EINVAL; + + switch (rdev->family) { + case CHIP_BARTS: + io_mc_regs = (u32 *)&barts_io_mc_regs; + ucode_size = BTC_MC_UCODE_SIZE; + regs_size = BTC_IO_MC_REGS_SIZE; + break; + case CHIP_TURKS: + io_mc_regs = (u32 *)&turks_io_mc_regs; + ucode_size = BTC_MC_UCODE_SIZE; + regs_size = BTC_IO_MC_REGS_SIZE; + break; + case CHIP_CAICOS: + default: + io_mc_regs = (u32 *)&caicos_io_mc_regs; + ucode_size = BTC_MC_UCODE_SIZE; + regs_size = BTC_IO_MC_REGS_SIZE; + break; + case CHIP_CAYMAN: + io_mc_regs = (u32 *)&cayman_io_mc_regs; + ucode_size = CAYMAN_MC_UCODE_SIZE; + regs_size = BTC_IO_MC_REGS_SIZE; + break; + } + + mem_type = (RREG32(MC_SEQ_MISC0) & MC_SEQ_MISC0_GDDR5_MASK) >> MC_SEQ_MISC0_GDDR5_SHIFT; + running = RREG32(MC_SEQ_SUP_CNTL) & RUN_MASK; + + if ((mem_type == MC_SEQ_MISC0_GDDR5_VALUE) && (running == 0)) { + if (running) { + blackout = RREG32(MC_SHARED_BLACKOUT_CNTL); + WREG32(MC_SHARED_BLACKOUT_CNTL, 1); + } + + /* reset the engine and set to writable */ + WREG32(MC_SEQ_SUP_CNTL, 0x00000008); + WREG32(MC_SEQ_SUP_CNTL, 0x00000010); + + /* load mc io regs */ + for (i = 0; i < regs_size; i++) { + WREG32(MC_SEQ_IO_DEBUG_INDEX, io_mc_regs[(i << 1)]); + WREG32(MC_SEQ_IO_DEBUG_DATA, io_mc_regs[(i << 1) + 1]); + } + /* load the MC ucode */ + fw_data = (const __be32 *)rdev->mc_fw->data; + for (i = 0; i < ucode_size; i++) + WREG32(MC_SEQ_SUP_PGM, be32_to_cpup(fw_data++)); + + /* put the engine back into the active state */ + WREG32(MC_SEQ_SUP_CNTL, 0x00000008); + WREG32(MC_SEQ_SUP_CNTL, 0x00000004); + WREG32(MC_SEQ_SUP_CNTL, 0x00000001); + + /* wait for training to complete */ + for (i = 0; i < rdev->usec_timeout; i++) { + if (RREG32(MC_IO_PAD_CNTL_D0) & MEM_FALL_OUT_CMD) + break; + DRM_UDELAY(1); + } + + if (running) + WREG32(MC_SHARED_BLACKOUT_CNTL, blackout); + } + + return 0; +} + +int ni_init_microcode(struct radeon_device *rdev) +{ + const char *chip_name; + const char *rlc_chip_name; + size_t pfp_req_size, me_req_size, rlc_req_size, mc_req_size; + char fw_name[30]; + int err; + + DRM_DEBUG("\n"); + + switch (rdev->family) { + case CHIP_BARTS: + chip_name = "BARTS"; + rlc_chip_name = "BTC"; + pfp_req_size = EVERGREEN_PFP_UCODE_SIZE * 4; + me_req_size = EVERGREEN_PM4_UCODE_SIZE * 4; + rlc_req_size = EVERGREEN_RLC_UCODE_SIZE * 4; + mc_req_size = BTC_MC_UCODE_SIZE * 4; + break; + case CHIP_TURKS: + chip_name = "TURKS"; + rlc_chip_name = "BTC"; + pfp_req_size = EVERGREEN_PFP_UCODE_SIZE * 4; + me_req_size = EVERGREEN_PM4_UCODE_SIZE * 4; + rlc_req_size = EVERGREEN_RLC_UCODE_SIZE * 4; + mc_req_size = BTC_MC_UCODE_SIZE * 4; + break; + case CHIP_CAICOS: + chip_name = "CAICOS"; + rlc_chip_name = "BTC"; + pfp_req_size = EVERGREEN_PFP_UCODE_SIZE * 4; + me_req_size = EVERGREEN_PM4_UCODE_SIZE * 4; + rlc_req_size = EVERGREEN_RLC_UCODE_SIZE * 4; + mc_req_size = BTC_MC_UCODE_SIZE * 4; + break; + case CHIP_CAYMAN: + chip_name = "CAYMAN"; + rlc_chip_name = "CAYMAN"; + pfp_req_size = CAYMAN_PFP_UCODE_SIZE * 4; + me_req_size = CAYMAN_PM4_UCODE_SIZE * 4; + rlc_req_size = CAYMAN_RLC_UCODE_SIZE * 4; + mc_req_size = CAYMAN_MC_UCODE_SIZE * 4; + break; + case CHIP_ARUBA: + chip_name = "ARUBA"; + rlc_chip_name = "ARUBA"; + /* pfp/me same size as CAYMAN */ + pfp_req_size = CAYMAN_PFP_UCODE_SIZE * 4; + me_req_size = CAYMAN_PM4_UCODE_SIZE * 4; + rlc_req_size = ARUBA_RLC_UCODE_SIZE * 4; + mc_req_size = 0; + break; + default: panic("%s: Unsupported family %d", __func__, rdev->family); + } + + DRM_INFO("Loading %s Microcode\n", chip_name); + err = 0; + + snprintf(fw_name, sizeof(fw_name), "radeonkmsfw_%s_pfp", chip_name); + rdev->pfp_fw = firmware_get(fw_name); + if (rdev->pfp_fw == NULL) { + err = -ENOENT; + goto out; + } + if (rdev->pfp_fw->datasize != pfp_req_size) { + DRM_ERROR( + "ni_cp: Bogus length %zu in firmware \"%s\"\n", + rdev->pfp_fw->datasize, fw_name); + err = -EINVAL; + goto out; + } + + snprintf(fw_name, sizeof(fw_name), "radeonkmsfw_%s_me", chip_name); + rdev->me_fw = firmware_get(fw_name); + if (rdev->me_fw == NULL) { + err = -ENOENT; + goto out; + } + if (rdev->me_fw->datasize != me_req_size) { + DRM_ERROR( + "ni_cp: Bogus length %zu in firmware \"%s\"\n", + rdev->me_fw->datasize, fw_name); + err = -EINVAL; + } + + snprintf(fw_name, sizeof(fw_name), "radeonkmsfw_%s_rlc", rlc_chip_name); + rdev->rlc_fw = firmware_get(fw_name); + if (rdev->rlc_fw == NULL) { + err = -ENOENT; + goto out; + } + if (rdev->rlc_fw->datasize != rlc_req_size) { + DRM_ERROR( + "ni_rlc: Bogus length %zu in firmware \"%s\"\n", + rdev->rlc_fw->datasize, fw_name); + err = -EINVAL; + } + + /* no MC ucode on TN */ + if (!(rdev->flags & RADEON_IS_IGP)) { + snprintf(fw_name, sizeof(fw_name), "radeonkmsfw_%s_mc", chip_name); + rdev->mc_fw = firmware_get(fw_name); + if (rdev->mc_fw == NULL) { + err = -ENOENT; + goto out; + } + if (rdev->mc_fw->datasize != mc_req_size) { + DRM_ERROR( + "ni_mc: Bogus length %zu in firmware \"%s\"\n", + rdev->mc_fw->datasize, fw_name); + err = -EINVAL; + } + } +out: + if (err) { + if (err != -EINVAL) + DRM_ERROR( + "ni_cp: Failed to load firmware \"%s\"\n", + fw_name); + if (rdev->pfp_fw != NULL) { + firmware_put(rdev->pfp_fw, FIRMWARE_UNLOAD); + rdev->pfp_fw = NULL; + } + if (rdev->me_fw != NULL) { + firmware_put(rdev->me_fw, FIRMWARE_UNLOAD); + rdev->me_fw = NULL; + } + if (rdev->rlc_fw != NULL) { + firmware_put(rdev->rlc_fw, FIRMWARE_UNLOAD); + rdev->rlc_fw = NULL; + } + if (rdev->mc_fw != NULL) { + firmware_put(rdev->mc_fw, FIRMWARE_UNLOAD); + rdev->mc_fw = NULL; + } + } + return err; +} + +/** + * ni_fini_microcode - drop the firmwares image references + * + * @rdev: radeon_device pointer + * + * Drop the pfp, me, mc and rlc firmwares image references. + * Called at driver shutdown. + */ +void ni_fini_microcode(struct radeon_device *rdev) +{ + + if (rdev->pfp_fw != NULL) { + firmware_put(rdev->pfp_fw, FIRMWARE_UNLOAD); + rdev->pfp_fw = NULL; + } + + if (rdev->me_fw != NULL) { + firmware_put(rdev->me_fw, FIRMWARE_UNLOAD); + rdev->me_fw = NULL; + } + + if (rdev->rlc_fw != NULL) { + firmware_put(rdev->rlc_fw, FIRMWARE_UNLOAD); + rdev->rlc_fw = NULL; + } + + if (rdev->mc_fw != NULL) { + firmware_put(rdev->mc_fw, FIRMWARE_UNLOAD); + rdev->mc_fw = NULL; + } +} + + +/* + * Core functions + */ +static void cayman_gpu_init(struct radeon_device *rdev) +{ + u32 gb_addr_config = 0; + u32 mc_shared_chmap, mc_arb_ramcfg; + u32 cgts_tcc_disable; + u32 sx_debug_1; + u32 smx_dc_ctl0; + u32 cgts_sm_ctrl_reg; + u32 hdp_host_path_cntl; + u32 tmp; + u32 disabled_rb_mask; + int i, j; + + switch (rdev->family) { + case CHIP_CAYMAN: + rdev->config.cayman.max_shader_engines = 2; + rdev->config.cayman.max_pipes_per_simd = 4; + rdev->config.cayman.max_tile_pipes = 8; + rdev->config.cayman.max_simds_per_se = 12; + rdev->config.cayman.max_backends_per_se = 4; + rdev->config.cayman.max_texture_channel_caches = 8; + rdev->config.cayman.max_gprs = 256; + rdev->config.cayman.max_threads = 256; + rdev->config.cayman.max_gs_threads = 32; + rdev->config.cayman.max_stack_entries = 512; + rdev->config.cayman.sx_num_of_sets = 8; + rdev->config.cayman.sx_max_export_size = 256; + rdev->config.cayman.sx_max_export_pos_size = 64; + rdev->config.cayman.sx_max_export_smx_size = 192; + rdev->config.cayman.max_hw_contexts = 8; + rdev->config.cayman.sq_num_cf_insts = 2; + + rdev->config.cayman.sc_prim_fifo_size = 0x100; + rdev->config.cayman.sc_hiz_tile_fifo_size = 0x30; + rdev->config.cayman.sc_earlyz_tile_fifo_size = 0x130; + gb_addr_config = CAYMAN_GB_ADDR_CONFIG_GOLDEN; + break; + case CHIP_ARUBA: + default: + rdev->config.cayman.max_shader_engines = 1; + rdev->config.cayman.max_pipes_per_simd = 4; + rdev->config.cayman.max_tile_pipes = 2; + if ((rdev->ddev->pci_device == 0x9900) || + (rdev->ddev->pci_device == 0x9901) || + (rdev->ddev->pci_device == 0x9905) || + (rdev->ddev->pci_device == 0x9906) || + (rdev->ddev->pci_device == 0x9907) || + (rdev->ddev->pci_device == 0x9908) || + (rdev->ddev->pci_device == 0x9909) || + (rdev->ddev->pci_device == 0x9910) || + (rdev->ddev->pci_device == 0x9917)) { + rdev->config.cayman.max_simds_per_se = 6; + rdev->config.cayman.max_backends_per_se = 2; + } else if ((rdev->ddev->pci_device == 0x9903) || + (rdev->ddev->pci_device == 0x9904) || + (rdev->ddev->pci_device == 0x990A) || + (rdev->ddev->pci_device == 0x9913) || + (rdev->ddev->pci_device == 0x9918)) { + rdev->config.cayman.max_simds_per_se = 4; + rdev->config.cayman.max_backends_per_se = 2; + } else if ((rdev->ddev->pci_device == 0x9919) || + (rdev->ddev->pci_device == 0x9990) || + (rdev->ddev->pci_device == 0x9991) || + (rdev->ddev->pci_device == 0x9994) || + (rdev->ddev->pci_device == 0x99A0)) { + rdev->config.cayman.max_simds_per_se = 3; + rdev->config.cayman.max_backends_per_se = 1; + } else { + rdev->config.cayman.max_simds_per_se = 2; + rdev->config.cayman.max_backends_per_se = 1; + } + rdev->config.cayman.max_texture_channel_caches = 2; + rdev->config.cayman.max_gprs = 256; + rdev->config.cayman.max_threads = 256; + rdev->config.cayman.max_gs_threads = 32; + rdev->config.cayman.max_stack_entries = 512; + rdev->config.cayman.sx_num_of_sets = 8; + rdev->config.cayman.sx_max_export_size = 256; + rdev->config.cayman.sx_max_export_pos_size = 64; + rdev->config.cayman.sx_max_export_smx_size = 192; + rdev->config.cayman.max_hw_contexts = 8; + rdev->config.cayman.sq_num_cf_insts = 2; + + rdev->config.cayman.sc_prim_fifo_size = 0x40; + rdev->config.cayman.sc_hiz_tile_fifo_size = 0x30; + rdev->config.cayman.sc_earlyz_tile_fifo_size = 0x130; + gb_addr_config = ARUBA_GB_ADDR_CONFIG_GOLDEN; + break; + } + + /* Initialize HDP */ + for (i = 0, j = 0; i < 32; i++, j += 0x18) { + WREG32((0x2c14 + j), 0x00000000); + WREG32((0x2c18 + j), 0x00000000); + WREG32((0x2c1c + j), 0x00000000); + WREG32((0x2c20 + j), 0x00000000); + WREG32((0x2c24 + j), 0x00000000); + } + + WREG32(GRBM_CNTL, GRBM_READ_TIMEOUT(0xff)); + + evergreen_fix_pci_max_read_req_size(rdev); + + mc_shared_chmap = RREG32(MC_SHARED_CHMAP); + mc_arb_ramcfg = RREG32(MC_ARB_RAMCFG); + + tmp = (mc_arb_ramcfg & NOOFCOLS_MASK) >> NOOFCOLS_SHIFT; + rdev->config.cayman.mem_row_size_in_kb = (4 * (1 << (8 + tmp))) / 1024; + if (rdev->config.cayman.mem_row_size_in_kb > 4) + rdev->config.cayman.mem_row_size_in_kb = 4; + /* XXX use MC settings? */ + rdev->config.cayman.shader_engine_tile_size = 32; + rdev->config.cayman.num_gpus = 1; + rdev->config.cayman.multi_gpu_tile_size = 64; + + tmp = (gb_addr_config & NUM_PIPES_MASK) >> NUM_PIPES_SHIFT; + rdev->config.cayman.num_tile_pipes = (1 << tmp); + tmp = (gb_addr_config & PIPE_INTERLEAVE_SIZE_MASK) >> PIPE_INTERLEAVE_SIZE_SHIFT; + rdev->config.cayman.mem_max_burst_length_bytes = (tmp + 1) * 256; + tmp = (gb_addr_config & NUM_SHADER_ENGINES_MASK) >> NUM_SHADER_ENGINES_SHIFT; + rdev->config.cayman.num_shader_engines = tmp + 1; + tmp = (gb_addr_config & NUM_GPUS_MASK) >> NUM_GPUS_SHIFT; + rdev->config.cayman.num_gpus = tmp + 1; + tmp = (gb_addr_config & MULTI_GPU_TILE_SIZE_MASK) >> MULTI_GPU_TILE_SIZE_SHIFT; + rdev->config.cayman.multi_gpu_tile_size = 1 << tmp; + tmp = (gb_addr_config & ROW_SIZE_MASK) >> ROW_SIZE_SHIFT; + rdev->config.cayman.mem_row_size_in_kb = 1 << tmp; + + + /* setup tiling info dword. gb_addr_config is not adequate since it does + * not have bank info, so create a custom tiling dword. + * bits 3:0 num_pipes + * bits 7:4 num_banks + * bits 11:8 group_size + * bits 15:12 row_size + */ + rdev->config.cayman.tile_config = 0; + switch (rdev->config.cayman.num_tile_pipes) { + case 1: + default: + rdev->config.cayman.tile_config |= (0 << 0); + break; + case 2: + rdev->config.cayman.tile_config |= (1 << 0); + break; + case 4: + rdev->config.cayman.tile_config |= (2 << 0); + break; + case 8: + rdev->config.cayman.tile_config |= (3 << 0); + break; + } + + /* num banks is 8 on all fusion asics. 0 = 4, 1 = 8, 2 = 16 */ + if (rdev->flags & RADEON_IS_IGP) + rdev->config.cayman.tile_config |= 1 << 4; + else { + switch ((mc_arb_ramcfg & NOOFBANK_MASK) >> NOOFBANK_SHIFT) { + case 0: /* four banks */ + rdev->config.cayman.tile_config |= 0 << 4; + break; + case 1: /* eight banks */ + rdev->config.cayman.tile_config |= 1 << 4; + break; + case 2: /* sixteen banks */ + default: + rdev->config.cayman.tile_config |= 2 << 4; + break; + } + } + rdev->config.cayman.tile_config |= + ((gb_addr_config & PIPE_INTERLEAVE_SIZE_MASK) >> PIPE_INTERLEAVE_SIZE_SHIFT) << 8; + rdev->config.cayman.tile_config |= + ((gb_addr_config & ROW_SIZE_MASK) >> ROW_SIZE_SHIFT) << 12; + + tmp = 0; + for (i = (rdev->config.cayman.max_shader_engines - 1); i >= 0; i--) { + u32 rb_disable_bitmap; + + WREG32(GRBM_GFX_INDEX, INSTANCE_BROADCAST_WRITES | SE_INDEX(i)); + WREG32(RLC_GFX_INDEX, INSTANCE_BROADCAST_WRITES | SE_INDEX(i)); + rb_disable_bitmap = (RREG32(CC_RB_BACKEND_DISABLE) & 0x00ff0000) >> 16; + tmp <<= 4; + tmp |= rb_disable_bitmap; + } + /* enabled rb are just the one not disabled :) */ + disabled_rb_mask = tmp; + + WREG32(GRBM_GFX_INDEX, INSTANCE_BROADCAST_WRITES | SE_BROADCAST_WRITES); + WREG32(RLC_GFX_INDEX, INSTANCE_BROADCAST_WRITES | SE_BROADCAST_WRITES); + + WREG32(GB_ADDR_CONFIG, gb_addr_config); + WREG32(DMIF_ADDR_CONFIG, gb_addr_config); + WREG32(HDP_ADDR_CONFIG, gb_addr_config); + WREG32(DMA_TILING_CONFIG + DMA0_REGISTER_OFFSET, gb_addr_config); + WREG32(DMA_TILING_CONFIG + DMA1_REGISTER_OFFSET, gb_addr_config); + + tmp = gb_addr_config & NUM_PIPES_MASK; + tmp = r6xx_remap_render_backend(rdev, tmp, + rdev->config.cayman.max_backends_per_se * + rdev->config.cayman.max_shader_engines, + CAYMAN_MAX_BACKENDS, disabled_rb_mask); + WREG32(GB_BACKEND_MAP, tmp); + + cgts_tcc_disable = 0xffff0000; + for (i = 0; i < rdev->config.cayman.max_texture_channel_caches; i++) + cgts_tcc_disable &= ~(1 << (16 + i)); + WREG32(CGTS_TCC_DISABLE, cgts_tcc_disable); + WREG32(CGTS_SYS_TCC_DISABLE, cgts_tcc_disable); + WREG32(CGTS_USER_SYS_TCC_DISABLE, cgts_tcc_disable); + WREG32(CGTS_USER_TCC_DISABLE, cgts_tcc_disable); + + /* reprogram the shader complex */ + cgts_sm_ctrl_reg = RREG32(CGTS_SM_CTRL_REG); + for (i = 0; i < 16; i++) + WREG32(CGTS_SM_CTRL_REG, OVERRIDE); + WREG32(CGTS_SM_CTRL_REG, cgts_sm_ctrl_reg); + + /* set HW defaults for 3D engine */ + WREG32(CP_MEQ_THRESHOLDS, MEQ1_START(0x30) | MEQ2_START(0x60)); + + sx_debug_1 = RREG32(SX_DEBUG_1); + sx_debug_1 |= ENABLE_NEW_SMX_ADDRESS; + WREG32(SX_DEBUG_1, sx_debug_1); + + smx_dc_ctl0 = RREG32(SMX_DC_CTL0); + smx_dc_ctl0 &= ~NUMBER_OF_SETS(0x1ff); + smx_dc_ctl0 |= NUMBER_OF_SETS(rdev->config.cayman.sx_num_of_sets); + WREG32(SMX_DC_CTL0, smx_dc_ctl0); + + WREG32(SPI_CONFIG_CNTL_1, VTX_DONE_DELAY(4) | CRC_SIMD_ID_WADDR_DISABLE); + + /* need to be explicitly zero-ed */ + WREG32(VGT_OFFCHIP_LDS_BASE, 0); + WREG32(SQ_LSTMP_RING_BASE, 0); + WREG32(SQ_HSTMP_RING_BASE, 0); + WREG32(SQ_ESTMP_RING_BASE, 0); + WREG32(SQ_GSTMP_RING_BASE, 0); + WREG32(SQ_VSTMP_RING_BASE, 0); + WREG32(SQ_PSTMP_RING_BASE, 0); + + WREG32(TA_CNTL_AUX, DISABLE_CUBE_ANISO); + + WREG32(SX_EXPORT_BUFFER_SIZES, (COLOR_BUFFER_SIZE((rdev->config.cayman.sx_max_export_size / 4) - 1) | + POSITION_BUFFER_SIZE((rdev->config.cayman.sx_max_export_pos_size / 4) - 1) | + SMX_BUFFER_SIZE((rdev->config.cayman.sx_max_export_smx_size / 4) - 1))); + + WREG32(PA_SC_FIFO_SIZE, (SC_PRIM_FIFO_SIZE(rdev->config.cayman.sc_prim_fifo_size) | + SC_HIZ_TILE_FIFO_SIZE(rdev->config.cayman.sc_hiz_tile_fifo_size) | + SC_EARLYZ_TILE_FIFO_SIZE(rdev->config.cayman.sc_earlyz_tile_fifo_size))); + + + WREG32(VGT_NUM_INSTANCES, 1); + + WREG32(CP_PERFMON_CNTL, 0); + + WREG32(SQ_MS_FIFO_SIZES, (CACHE_FIFO_SIZE(16 * rdev->config.cayman.sq_num_cf_insts) | + FETCH_FIFO_HIWATER(0x4) | + DONE_FIFO_HIWATER(0xe0) | + ALU_UPDATE_FIFO_HIWATER(0x8))); + + WREG32(SQ_GPR_RESOURCE_MGMT_1, NUM_CLAUSE_TEMP_GPRS(4)); + WREG32(SQ_CONFIG, (VC_ENABLE | + EXPORT_SRC_C | + GFX_PRIO(0) | + CS1_PRIO(0) | + CS2_PRIO(1))); + WREG32(SQ_DYN_GPR_CNTL_PS_FLUSH_REQ, DYN_GPR_ENABLE); + + WREG32(PA_SC_FORCE_EOV_MAX_CNTS, (FORCE_EOV_MAX_CLK_CNT(4095) | + FORCE_EOV_MAX_REZ_CNT(255))); + + WREG32(VGT_CACHE_INVALIDATION, CACHE_INVALIDATION(VC_AND_TC) | + AUTO_INVLD_EN(ES_AND_GS_AUTO)); + + WREG32(VGT_GS_VERTEX_REUSE, 16); + WREG32(PA_SC_LINE_STIPPLE_STATE, 0); + + WREG32(CB_PERF_CTR0_SEL_0, 0); + WREG32(CB_PERF_CTR0_SEL_1, 0); + WREG32(CB_PERF_CTR1_SEL_0, 0); + WREG32(CB_PERF_CTR1_SEL_1, 0); + WREG32(CB_PERF_CTR2_SEL_0, 0); + WREG32(CB_PERF_CTR2_SEL_1, 0); + WREG32(CB_PERF_CTR3_SEL_0, 0); + WREG32(CB_PERF_CTR3_SEL_1, 0); + + tmp = RREG32(HDP_MISC_CNTL); + tmp |= HDP_FLUSH_INVALIDATE_CACHE; + WREG32(HDP_MISC_CNTL, tmp); + + hdp_host_path_cntl = RREG32(HDP_HOST_PATH_CNTL); + WREG32(HDP_HOST_PATH_CNTL, hdp_host_path_cntl); + + WREG32(PA_CL_ENHANCE, CLIP_VTX_REORDER_ENA | NUM_CLIP_SEQ(3)); + + DRM_UDELAY(50); +} + +/* + * GART + */ +void cayman_pcie_gart_tlb_flush(struct radeon_device *rdev) +{ + /* flush hdp cache */ + WREG32(HDP_MEM_COHERENCY_FLUSH_CNTL, 0x1); + + /* bits 0-7 are the VM contexts0-7 */ + WREG32(VM_INVALIDATE_REQUEST, 1); +} + +static int cayman_pcie_gart_enable(struct radeon_device *rdev) +{ + int i, r; + + if (rdev->gart.robj == NULL) { + dev_err(rdev->dev, "No VRAM object for PCIE GART.\n"); + return -EINVAL; + } + r = radeon_gart_table_vram_pin(rdev); + if (r) + return r; + radeon_gart_restore(rdev); + /* Setup TLB control */ + WREG32(MC_VM_MX_L1_TLB_CNTL, + (0xA << 7) | + ENABLE_L1_TLB | + ENABLE_L1_FRAGMENT_PROCESSING | + SYSTEM_ACCESS_MODE_NOT_IN_SYS | + ENABLE_ADVANCED_DRIVER_MODEL | + SYSTEM_APERTURE_UNMAPPED_ACCESS_PASS_THRU); + /* Setup L2 cache */ + WREG32(VM_L2_CNTL, ENABLE_L2_CACHE | + ENABLE_L2_PTE_CACHE_LRU_UPDATE_BY_WRITE | + ENABLE_L2_PDE0_CACHE_LRU_UPDATE_BY_WRITE | + EFFECTIVE_L2_QUEUE_SIZE(7) | + CONTEXT1_IDENTITY_ACCESS_MODE(1)); + WREG32(VM_L2_CNTL2, INVALIDATE_ALL_L1_TLBS | INVALIDATE_L2_CACHE); + WREG32(VM_L2_CNTL3, L2_CACHE_BIGK_ASSOCIATIVITY | + L2_CACHE_BIGK_FRAGMENT_SIZE(6)); + /* setup context0 */ + WREG32(VM_CONTEXT0_PAGE_TABLE_START_ADDR, rdev->mc.gtt_start >> 12); + WREG32(VM_CONTEXT0_PAGE_TABLE_END_ADDR, rdev->mc.gtt_end >> 12); + WREG32(VM_CONTEXT0_PAGE_TABLE_BASE_ADDR, rdev->gart.table_addr >> 12); + WREG32(VM_CONTEXT0_PROTECTION_FAULT_DEFAULT_ADDR, + (u32)(rdev->dummy_page.addr >> 12)); + WREG32(VM_CONTEXT0_CNTL2, 0); + WREG32(VM_CONTEXT0_CNTL, ENABLE_CONTEXT | PAGE_TABLE_DEPTH(0) | + RANGE_PROTECTION_FAULT_ENABLE_DEFAULT); + + WREG32(0x15D4, 0); + WREG32(0x15D8, 0); + WREG32(0x15DC, 0); + + /* empty context1-7 */ + /* Assign the pt base to something valid for now; the pts used for + * the VMs are determined by the application and setup and assigned + * on the fly in the vm part of radeon_gart.c + */ + for (i = 1; i < 8; i++) { + WREG32(VM_CONTEXT0_PAGE_TABLE_START_ADDR + (i << 2), 0); + WREG32(VM_CONTEXT0_PAGE_TABLE_END_ADDR + (i << 2), rdev->vm_manager.max_pfn); + WREG32(VM_CONTEXT0_PAGE_TABLE_BASE_ADDR + (i << 2), + rdev->gart.table_addr >> 12); + } + + /* enable context1-7 */ + WREG32(VM_CONTEXT1_PROTECTION_FAULT_DEFAULT_ADDR, + (u32)(rdev->dummy_page.addr >> 12)); + WREG32(VM_CONTEXT1_CNTL2, 4); + WREG32(VM_CONTEXT1_CNTL, ENABLE_CONTEXT | PAGE_TABLE_DEPTH(1) | + RANGE_PROTECTION_FAULT_ENABLE_INTERRUPT | + RANGE_PROTECTION_FAULT_ENABLE_DEFAULT | + DUMMY_PAGE_PROTECTION_FAULT_ENABLE_INTERRUPT | + DUMMY_PAGE_PROTECTION_FAULT_ENABLE_DEFAULT | + PDE0_PROTECTION_FAULT_ENABLE_INTERRUPT | + PDE0_PROTECTION_FAULT_ENABLE_DEFAULT | + VALID_PROTECTION_FAULT_ENABLE_INTERRUPT | + VALID_PROTECTION_FAULT_ENABLE_DEFAULT | + READ_PROTECTION_FAULT_ENABLE_INTERRUPT | + READ_PROTECTION_FAULT_ENABLE_DEFAULT | + WRITE_PROTECTION_FAULT_ENABLE_INTERRUPT | + WRITE_PROTECTION_FAULT_ENABLE_DEFAULT); + + cayman_pcie_gart_tlb_flush(rdev); + DRM_INFO("PCIE GART of %uM enabled (table at 0x%016llX).\n", + (unsigned)(rdev->mc.gtt_size >> 20), + (unsigned long long)rdev->gart.table_addr); + rdev->gart.ready = true; + return 0; +} + +static void cayman_pcie_gart_disable(struct radeon_device *rdev) +{ + /* Disable all tables */ + WREG32(VM_CONTEXT0_CNTL, 0); + WREG32(VM_CONTEXT1_CNTL, 0); + /* Setup TLB control */ + WREG32(MC_VM_MX_L1_TLB_CNTL, ENABLE_L1_FRAGMENT_PROCESSING | + SYSTEM_ACCESS_MODE_NOT_IN_SYS | + SYSTEM_APERTURE_UNMAPPED_ACCESS_PASS_THRU); + /* Setup L2 cache */ + WREG32(VM_L2_CNTL, ENABLE_L2_PTE_CACHE_LRU_UPDATE_BY_WRITE | + ENABLE_L2_PDE0_CACHE_LRU_UPDATE_BY_WRITE | + EFFECTIVE_L2_QUEUE_SIZE(7) | + CONTEXT1_IDENTITY_ACCESS_MODE(1)); + WREG32(VM_L2_CNTL2, 0); + WREG32(VM_L2_CNTL3, L2_CACHE_BIGK_ASSOCIATIVITY | + L2_CACHE_BIGK_FRAGMENT_SIZE(6)); + radeon_gart_table_vram_unpin(rdev); +} + +static void cayman_pcie_gart_fini(struct radeon_device *rdev) +{ + cayman_pcie_gart_disable(rdev); + radeon_gart_table_vram_free(rdev); + radeon_gart_fini(rdev); +} + +void cayman_cp_int_cntl_setup(struct radeon_device *rdev, + int ring, u32 cp_int_cntl) +{ + u32 srbm_gfx_cntl = RREG32(SRBM_GFX_CNTL) & ~3; + + WREG32(SRBM_GFX_CNTL, srbm_gfx_cntl | (ring & 3)); + WREG32(CP_INT_CNTL, cp_int_cntl); +} + +/* + * CP. + */ +void cayman_fence_ring_emit(struct radeon_device *rdev, + struct radeon_fence *fence) +{ + struct radeon_ring *ring = &rdev->ring[fence->ring]; + u64 addr = rdev->fence_drv[fence->ring].gpu_addr; + + /* flush read cache over gart for this vmid */ + radeon_ring_write(ring, PACKET3(PACKET3_SET_CONFIG_REG, 1)); + radeon_ring_write(ring, (CP_COHER_CNTL2 - PACKET3_SET_CONFIG_REG_START) >> 2); + radeon_ring_write(ring, 0); + radeon_ring_write(ring, PACKET3(PACKET3_SURFACE_SYNC, 3)); + radeon_ring_write(ring, PACKET3_TC_ACTION_ENA | PACKET3_SH_ACTION_ENA); + radeon_ring_write(ring, 0xFFFFFFFF); + radeon_ring_write(ring, 0); + radeon_ring_write(ring, 10); /* poll interval */ + /* EVENT_WRITE_EOP - flush caches, send int */ + radeon_ring_write(ring, PACKET3(PACKET3_EVENT_WRITE_EOP, 4)); + radeon_ring_write(ring, EVENT_TYPE(CACHE_FLUSH_AND_INV_EVENT_TS) | EVENT_INDEX(5)); + radeon_ring_write(ring, addr & 0xffffffff); + radeon_ring_write(ring, (upper_32_bits(addr) & 0xff) | DATA_SEL(1) | INT_SEL(2)); + radeon_ring_write(ring, fence->seq); + radeon_ring_write(ring, 0); +} + +void cayman_ring_ib_execute(struct radeon_device *rdev, struct radeon_ib *ib) +{ + struct radeon_ring *ring = &rdev->ring[ib->ring]; + + /* set to DX10/11 mode */ + radeon_ring_write(ring, PACKET3(PACKET3_MODE_CONTROL, 0)); + radeon_ring_write(ring, 1); + + if (ring->rptr_save_reg) { + uint32_t next_rptr = ring->wptr + 3 + 4 + 8; + radeon_ring_write(ring, PACKET3(PACKET3_SET_CONFIG_REG, 1)); + radeon_ring_write(ring, ((ring->rptr_save_reg - + PACKET3_SET_CONFIG_REG_START) >> 2)); + radeon_ring_write(ring, next_rptr); + } + + radeon_ring_write(ring, PACKET3(PACKET3_INDIRECT_BUFFER, 2)); + radeon_ring_write(ring, +#ifdef __BIG_ENDIAN + (2 << 0) | +#endif + (ib->gpu_addr & 0xFFFFFFFC)); + radeon_ring_write(ring, upper_32_bits(ib->gpu_addr) & 0xFF); + radeon_ring_write(ring, ib->length_dw | + (ib->vm ? (ib->vm->id << 24) : 0)); + + /* flush read cache over gart for this vmid */ + radeon_ring_write(ring, PACKET3(PACKET3_SET_CONFIG_REG, 1)); + radeon_ring_write(ring, (CP_COHER_CNTL2 - PACKET3_SET_CONFIG_REG_START) >> 2); + radeon_ring_write(ring, ib->vm ? ib->vm->id : 0); + radeon_ring_write(ring, PACKET3(PACKET3_SURFACE_SYNC, 3)); + radeon_ring_write(ring, PACKET3_TC_ACTION_ENA | PACKET3_SH_ACTION_ENA); + radeon_ring_write(ring, 0xFFFFFFFF); + radeon_ring_write(ring, 0); + radeon_ring_write(ring, 10); /* poll interval */ +} + +static void cayman_cp_enable(struct radeon_device *rdev, bool enable) +{ + if (enable) + WREG32(CP_ME_CNTL, 0); + else { + radeon_ttm_set_active_vram_size(rdev, rdev->mc.visible_vram_size); + WREG32(CP_ME_CNTL, (CP_ME_HALT | CP_PFP_HALT)); + WREG32(SCRATCH_UMSK, 0); + rdev->ring[RADEON_RING_TYPE_GFX_INDEX].ready = false; + } +} + +static int cayman_cp_load_microcode(struct radeon_device *rdev) +{ + const __be32 *fw_data; + int i; + + if (!rdev->me_fw || !rdev->pfp_fw) + return -EINVAL; + + cayman_cp_enable(rdev, false); + + fw_data = (const __be32 *)rdev->pfp_fw->data; + WREG32(CP_PFP_UCODE_ADDR, 0); + for (i = 0; i < CAYMAN_PFP_UCODE_SIZE; i++) + WREG32(CP_PFP_UCODE_DATA, be32_to_cpup(fw_data++)); + WREG32(CP_PFP_UCODE_ADDR, 0); + + fw_data = (const __be32 *)rdev->me_fw->data; + WREG32(CP_ME_RAM_WADDR, 0); + for (i = 0; i < CAYMAN_PM4_UCODE_SIZE; i++) + WREG32(CP_ME_RAM_DATA, be32_to_cpup(fw_data++)); + + WREG32(CP_PFP_UCODE_ADDR, 0); + WREG32(CP_ME_RAM_WADDR, 0); + WREG32(CP_ME_RAM_RADDR, 0); + return 0; +} + +static int cayman_cp_start(struct radeon_device *rdev) +{ + struct radeon_ring *ring = &rdev->ring[RADEON_RING_TYPE_GFX_INDEX]; + int r, i; + + r = radeon_ring_lock(rdev, ring, 7); + if (r) { + DRM_ERROR("radeon: cp failed to lock ring (%d).\n", r); + return r; + } + radeon_ring_write(ring, PACKET3(PACKET3_ME_INITIALIZE, 5)); + radeon_ring_write(ring, 0x1); + radeon_ring_write(ring, 0x0); + radeon_ring_write(ring, rdev->config.cayman.max_hw_contexts - 1); + radeon_ring_write(ring, PACKET3_ME_INITIALIZE_DEVICE_ID(1)); + radeon_ring_write(ring, 0); + radeon_ring_write(ring, 0); + radeon_ring_unlock_commit(rdev, ring); + + cayman_cp_enable(rdev, true); + + r = radeon_ring_lock(rdev, ring, cayman_default_size + 19); + if (r) { + DRM_ERROR("radeon: cp failed to lock ring (%d).\n", r); + return r; + } + + /* setup clear context state */ + radeon_ring_write(ring, PACKET3(PACKET3_PREAMBLE_CNTL, 0)); + radeon_ring_write(ring, PACKET3_PREAMBLE_BEGIN_CLEAR_STATE); + + for (i = 0; i < cayman_default_size; i++) + radeon_ring_write(ring, cayman_default_state[i]); + + radeon_ring_write(ring, PACKET3(PACKET3_PREAMBLE_CNTL, 0)); + radeon_ring_write(ring, PACKET3_PREAMBLE_END_CLEAR_STATE); + + /* set clear context state */ + radeon_ring_write(ring, PACKET3(PACKET3_CLEAR_STATE, 0)); + radeon_ring_write(ring, 0); + + /* SQ_VTX_BASE_VTX_LOC */ + radeon_ring_write(ring, 0xc0026f00); + radeon_ring_write(ring, 0x00000000); + radeon_ring_write(ring, 0x00000000); + radeon_ring_write(ring, 0x00000000); + + /* Clear consts */ + radeon_ring_write(ring, 0xc0036f00); + radeon_ring_write(ring, 0x00000bc4); + radeon_ring_write(ring, 0xffffffff); + radeon_ring_write(ring, 0xffffffff); + radeon_ring_write(ring, 0xffffffff); + + radeon_ring_write(ring, 0xc0026900); + radeon_ring_write(ring, 0x00000316); + radeon_ring_write(ring, 0x0000000e); /* VGT_VERTEX_REUSE_BLOCK_CNTL */ + radeon_ring_write(ring, 0x00000010); /* */ + + radeon_ring_unlock_commit(rdev, ring); + + /* XXX init other rings */ + + return 0; +} + +static void cayman_cp_fini(struct radeon_device *rdev) +{ + struct radeon_ring *ring = &rdev->ring[RADEON_RING_TYPE_GFX_INDEX]; + cayman_cp_enable(rdev, false); + radeon_ring_fini(rdev, ring); + radeon_scratch_free(rdev, ring->rptr_save_reg); +} + +static int cayman_cp_resume(struct radeon_device *rdev) +{ + static const int ridx[] = { + RADEON_RING_TYPE_GFX_INDEX, + CAYMAN_RING_TYPE_CP1_INDEX, + CAYMAN_RING_TYPE_CP2_INDEX + }; + static const unsigned cp_rb_cntl[] = { + CP_RB0_CNTL, + CP_RB1_CNTL, + CP_RB2_CNTL, + }; + static const unsigned cp_rb_rptr_addr[] = { + CP_RB0_RPTR_ADDR, + CP_RB1_RPTR_ADDR, + CP_RB2_RPTR_ADDR + }; + static const unsigned cp_rb_rptr_addr_hi[] = { + CP_RB0_RPTR_ADDR_HI, + CP_RB1_RPTR_ADDR_HI, + CP_RB2_RPTR_ADDR_HI + }; + static const unsigned cp_rb_base[] = { + CP_RB0_BASE, + CP_RB1_BASE, + CP_RB2_BASE + }; + struct radeon_ring *ring; + int i, r; + + /* Reset cp; if cp is reset, then PA, SH, VGT also need to be reset */ + WREG32(GRBM_SOFT_RESET, (SOFT_RESET_CP | + SOFT_RESET_PA | + SOFT_RESET_SH | + SOFT_RESET_VGT | + SOFT_RESET_SPI | + SOFT_RESET_SX)); + RREG32(GRBM_SOFT_RESET); + DRM_MDELAY(15); + WREG32(GRBM_SOFT_RESET, 0); + RREG32(GRBM_SOFT_RESET); + + WREG32(CP_SEM_WAIT_TIMER, 0x0); + WREG32(CP_SEM_INCOMPLETE_TIMER_CNTL, 0x0); + + /* Set the write pointer delay */ + WREG32(CP_RB_WPTR_DELAY, 0); + + WREG32(CP_DEBUG, (1 << 27)); + + /* set the wb address whether it's enabled or not */ + WREG32(SCRATCH_ADDR, ((rdev->wb.gpu_addr + RADEON_WB_SCRATCH_OFFSET) >> 8) & 0xFFFFFFFF); + WREG32(SCRATCH_UMSK, 0xff); + + for (i = 0; i < 3; ++i) { + uint32_t rb_cntl; + uint64_t addr; + + /* Set ring buffer size */ + ring = &rdev->ring[ridx[i]]; + rb_cntl = drm_order(ring->ring_size / 8); + rb_cntl |= drm_order(RADEON_GPU_PAGE_SIZE/8) << 8; +#ifdef __BIG_ENDIAN + rb_cntl |= BUF_SWAP_32BIT; +#endif + WREG32(cp_rb_cntl[i], rb_cntl); + + /* set the wb address whether it's enabled or not */ + addr = rdev->wb.gpu_addr + RADEON_WB_CP_RPTR_OFFSET; + WREG32(cp_rb_rptr_addr[i], addr & 0xFFFFFFFC); + WREG32(cp_rb_rptr_addr_hi[i], upper_32_bits(addr) & 0xFF); + } + + /* set the rb base addr, this causes an internal reset of ALL rings */ + for (i = 0; i < 3; ++i) { + ring = &rdev->ring[ridx[i]]; + WREG32(cp_rb_base[i], ring->gpu_addr >> 8); + } + + for (i = 0; i < 3; ++i) { + /* Initialize the ring buffer's read and write pointers */ + ring = &rdev->ring[ridx[i]]; + WREG32_P(cp_rb_cntl[i], RB_RPTR_WR_ENA, ~RB_RPTR_WR_ENA); + + ring->rptr = ring->wptr = 0; + WREG32(ring->rptr_reg, ring->rptr); + WREG32(ring->wptr_reg, ring->wptr); + + DRM_MDELAY(1); + WREG32_P(cp_rb_cntl[i], 0, ~RB_RPTR_WR_ENA); + } + + /* start the rings */ + cayman_cp_start(rdev); + rdev->ring[RADEON_RING_TYPE_GFX_INDEX].ready = true; + rdev->ring[CAYMAN_RING_TYPE_CP1_INDEX].ready = false; + rdev->ring[CAYMAN_RING_TYPE_CP2_INDEX].ready = false; + /* this only test cp0 */ + r = radeon_ring_test(rdev, RADEON_RING_TYPE_GFX_INDEX, &rdev->ring[RADEON_RING_TYPE_GFX_INDEX]); + if (r) { + rdev->ring[RADEON_RING_TYPE_GFX_INDEX].ready = false; + rdev->ring[CAYMAN_RING_TYPE_CP1_INDEX].ready = false; + rdev->ring[CAYMAN_RING_TYPE_CP2_INDEX].ready = false; + return r; + } + + return 0; +} + +/* + * DMA + * Starting with R600, the GPU has an asynchronous + * DMA engine. The programming model is very similar + * to the 3D engine (ring buffer, IBs, etc.), but the + * DMA controller has it's own packet format that is + * different form the PM4 format used by the 3D engine. + * It supports copying data, writing embedded data, + * solid fills, and a number of other things. It also + * has support for tiling/detiling of buffers. + * Cayman and newer support two asynchronous DMA engines. + */ +/** + * cayman_dma_ring_ib_execute - Schedule an IB on the DMA engine + * + * @rdev: radeon_device pointer + * @ib: IB object to schedule + * + * Schedule an IB in the DMA ring (cayman-SI). + */ +void cayman_dma_ring_ib_execute(struct radeon_device *rdev, + struct radeon_ib *ib) +{ + struct radeon_ring *ring = &rdev->ring[ib->ring]; + + if (rdev->wb.enabled) { + u32 next_rptr = ring->wptr + 4; + while ((next_rptr & 7) != 5) + next_rptr++; + next_rptr += 3; + radeon_ring_write(ring, DMA_PACKET(DMA_PACKET_WRITE, 0, 0, 1)); + radeon_ring_write(ring, ring->next_rptr_gpu_addr & 0xfffffffc); + radeon_ring_write(ring, upper_32_bits(ring->next_rptr_gpu_addr) & 0xff); + radeon_ring_write(ring, next_rptr); + } + + /* The indirect buffer packet must end on an 8 DW boundary in the DMA ring. + * Pad as necessary with NOPs. + */ + while ((ring->wptr & 7) != 5) + radeon_ring_write(ring, DMA_PACKET(DMA_PACKET_NOP, 0, 0, 0)); + radeon_ring_write(ring, DMA_IB_PACKET(DMA_PACKET_INDIRECT_BUFFER, ib->vm ? ib->vm->id : 0, 0)); + radeon_ring_write(ring, (ib->gpu_addr & 0xFFFFFFE0)); + radeon_ring_write(ring, (ib->length_dw << 12) | (upper_32_bits(ib->gpu_addr) & 0xFF)); + +} + +/** + * cayman_dma_stop - stop the async dma engines + * + * @rdev: radeon_device pointer + * + * Stop the async dma engines (cayman-SI). + */ +void cayman_dma_stop(struct radeon_device *rdev) +{ + u32 rb_cntl; + + radeon_ttm_set_active_vram_size(rdev, rdev->mc.visible_vram_size); + + /* dma0 */ + rb_cntl = RREG32(DMA_RB_CNTL + DMA0_REGISTER_OFFSET); + rb_cntl &= ~DMA_RB_ENABLE; + WREG32(DMA_RB_CNTL + DMA0_REGISTER_OFFSET, rb_cntl); + + /* dma1 */ + rb_cntl = RREG32(DMA_RB_CNTL + DMA1_REGISTER_OFFSET); + rb_cntl &= ~DMA_RB_ENABLE; + WREG32(DMA_RB_CNTL + DMA1_REGISTER_OFFSET, rb_cntl); + + rdev->ring[R600_RING_TYPE_DMA_INDEX].ready = false; + rdev->ring[CAYMAN_RING_TYPE_DMA1_INDEX].ready = false; +} + +/** + * cayman_dma_resume - setup and start the async dma engines + * + * @rdev: radeon_device pointer + * + * Set up the DMA ring buffers and enable them. (cayman-SI). + * Returns 0 for success, error for failure. + */ +int cayman_dma_resume(struct radeon_device *rdev) +{ + struct radeon_ring *ring; + u32 rb_cntl, dma_cntl, ib_cntl; + u32 rb_bufsz; + u32 reg_offset, wb_offset; + int i, r; + + /* Reset dma */ + WREG32(SRBM_SOFT_RESET, SOFT_RESET_DMA | SOFT_RESET_DMA1); + RREG32(SRBM_SOFT_RESET); + DRM_UDELAY(50); + WREG32(SRBM_SOFT_RESET, 0); + + for (i = 0; i < 2; i++) { + if (i == 0) { + ring = &rdev->ring[R600_RING_TYPE_DMA_INDEX]; + reg_offset = DMA0_REGISTER_OFFSET; + wb_offset = R600_WB_DMA_RPTR_OFFSET; + } else { + ring = &rdev->ring[CAYMAN_RING_TYPE_DMA1_INDEX]; + reg_offset = DMA1_REGISTER_OFFSET; + wb_offset = CAYMAN_WB_DMA1_RPTR_OFFSET; + } + + WREG32(DMA_SEM_INCOMPLETE_TIMER_CNTL + reg_offset, 0); + WREG32(DMA_SEM_WAIT_FAIL_TIMER_CNTL + reg_offset, 0); + + /* Set ring buffer size in dwords */ + rb_bufsz = drm_order(ring->ring_size / 4); + rb_cntl = rb_bufsz << 1; +#ifdef __BIG_ENDIAN + rb_cntl |= DMA_RB_SWAP_ENABLE | DMA_RPTR_WRITEBACK_SWAP_ENABLE; +#endif + WREG32(DMA_RB_CNTL + reg_offset, rb_cntl); + + /* Initialize the ring buffer's read and write pointers */ + WREG32(DMA_RB_RPTR + reg_offset, 0); + WREG32(DMA_RB_WPTR + reg_offset, 0); + + /* set the wb address whether it's enabled or not */ + WREG32(DMA_RB_RPTR_ADDR_HI + reg_offset, + upper_32_bits(rdev->wb.gpu_addr + wb_offset) & 0xFF); + WREG32(DMA_RB_RPTR_ADDR_LO + reg_offset, + ((rdev->wb.gpu_addr + wb_offset) & 0xFFFFFFFC)); + + if (rdev->wb.enabled) + rb_cntl |= DMA_RPTR_WRITEBACK_ENABLE; + + WREG32(DMA_RB_BASE + reg_offset, ring->gpu_addr >> 8); + + /* enable DMA IBs */ + ib_cntl = DMA_IB_ENABLE | CMD_VMID_FORCE; +#ifdef __BIG_ENDIAN + ib_cntl |= DMA_IB_SWAP_ENABLE; +#endif + WREG32(DMA_IB_CNTL + reg_offset, ib_cntl); + + dma_cntl = RREG32(DMA_CNTL + reg_offset); + dma_cntl &= ~CTXEMPTY_INT_ENABLE; + WREG32(DMA_CNTL + reg_offset, dma_cntl); + + ring->wptr = 0; + WREG32(DMA_RB_WPTR + reg_offset, ring->wptr << 2); + + ring->rptr = RREG32(DMA_RB_RPTR + reg_offset) >> 2; + + WREG32(DMA_RB_CNTL + reg_offset, rb_cntl | DMA_RB_ENABLE); + + ring->ready = true; + + r = radeon_ring_test(rdev, ring->idx, ring); + if (r) { + ring->ready = false; + return r; + } + } + + radeon_ttm_set_active_vram_size(rdev, rdev->mc.real_vram_size); + + return 0; +} + +/** + * cayman_dma_fini - tear down the async dma engines + * + * @rdev: radeon_device pointer + * + * Stop the async dma engines and free the rings (cayman-SI). + */ +void cayman_dma_fini(struct radeon_device *rdev) +{ + cayman_dma_stop(rdev); + radeon_ring_fini(rdev, &rdev->ring[R600_RING_TYPE_DMA_INDEX]); + radeon_ring_fini(rdev, &rdev->ring[CAYMAN_RING_TYPE_DMA1_INDEX]); +} + +static void cayman_gpu_soft_reset_gfx(struct radeon_device *rdev) +{ + u32 grbm_reset = 0; + + if (!(RREG32(GRBM_STATUS) & GUI_ACTIVE)) + return; + + dev_info(rdev->dev, " GRBM_STATUS = 0x%08X\n", + RREG32(GRBM_STATUS)); + dev_info(rdev->dev, " GRBM_STATUS_SE0 = 0x%08X\n", + RREG32(GRBM_STATUS_SE0)); + dev_info(rdev->dev, " GRBM_STATUS_SE1 = 0x%08X\n", + RREG32(GRBM_STATUS_SE1)); + dev_info(rdev->dev, " SRBM_STATUS = 0x%08X\n", + RREG32(SRBM_STATUS)); + dev_info(rdev->dev, " R_008674_CP_STALLED_STAT1 = 0x%08X\n", + RREG32(CP_STALLED_STAT1)); + dev_info(rdev->dev, " R_008678_CP_STALLED_STAT2 = 0x%08X\n", + RREG32(CP_STALLED_STAT2)); + dev_info(rdev->dev, " R_00867C_CP_BUSY_STAT = 0x%08X\n", + RREG32(CP_BUSY_STAT)); + dev_info(rdev->dev, " R_008680_CP_STAT = 0x%08X\n", + RREG32(CP_STAT)); + + /* Disable CP parsing/prefetching */ + WREG32(CP_ME_CNTL, CP_ME_HALT | CP_PFP_HALT); + + /* reset all the gfx blocks */ + grbm_reset = (SOFT_RESET_CP | + SOFT_RESET_CB | + SOFT_RESET_DB | + SOFT_RESET_GDS | + SOFT_RESET_PA | + SOFT_RESET_SC | + SOFT_RESET_SPI | + SOFT_RESET_SH | + SOFT_RESET_SX | + SOFT_RESET_TC | + SOFT_RESET_TA | + SOFT_RESET_VGT | + SOFT_RESET_IA); + + dev_info(rdev->dev, " GRBM_SOFT_RESET=0x%08X\n", grbm_reset); + WREG32(GRBM_SOFT_RESET, grbm_reset); + (void)RREG32(GRBM_SOFT_RESET); + DRM_UDELAY(50); + WREG32(GRBM_SOFT_RESET, 0); + (void)RREG32(GRBM_SOFT_RESET); + + dev_info(rdev->dev, " GRBM_STATUS = 0x%08X\n", + RREG32(GRBM_STATUS)); + dev_info(rdev->dev, " GRBM_STATUS_SE0 = 0x%08X\n", + RREG32(GRBM_STATUS_SE0)); + dev_info(rdev->dev, " GRBM_STATUS_SE1 = 0x%08X\n", + RREG32(GRBM_STATUS_SE1)); + dev_info(rdev->dev, " SRBM_STATUS = 0x%08X\n", + RREG32(SRBM_STATUS)); + dev_info(rdev->dev, " R_008674_CP_STALLED_STAT1 = 0x%08X\n", + RREG32(CP_STALLED_STAT1)); + dev_info(rdev->dev, " R_008678_CP_STALLED_STAT2 = 0x%08X\n", + RREG32(CP_STALLED_STAT2)); + dev_info(rdev->dev, " R_00867C_CP_BUSY_STAT = 0x%08X\n", + RREG32(CP_BUSY_STAT)); + dev_info(rdev->dev, " R_008680_CP_STAT = 0x%08X\n", + RREG32(CP_STAT)); + +} + +static void cayman_gpu_soft_reset_dma(struct radeon_device *rdev) +{ + u32 tmp; + + if (RREG32(DMA_STATUS_REG) & DMA_IDLE) + return; + + dev_info(rdev->dev, " R_00D034_DMA_STATUS_REG = 0x%08X\n", + RREG32(DMA_STATUS_REG)); + + /* dma0 */ + tmp = RREG32(DMA_RB_CNTL + DMA0_REGISTER_OFFSET); + tmp &= ~DMA_RB_ENABLE; + WREG32(DMA_RB_CNTL + DMA0_REGISTER_OFFSET, tmp); + + /* dma1 */ + tmp = RREG32(DMA_RB_CNTL + DMA1_REGISTER_OFFSET); + tmp &= ~DMA_RB_ENABLE; + WREG32(DMA_RB_CNTL + DMA1_REGISTER_OFFSET, tmp); + + /* Reset dma */ + WREG32(SRBM_SOFT_RESET, SOFT_RESET_DMA | SOFT_RESET_DMA1); + RREG32(SRBM_SOFT_RESET); + DRM_UDELAY(50); + WREG32(SRBM_SOFT_RESET, 0); + + dev_info(rdev->dev, " R_00D034_DMA_STATUS_REG = 0x%08X\n", + RREG32(DMA_STATUS_REG)); + +} + +static int cayman_gpu_soft_reset(struct radeon_device *rdev, u32 reset_mask) +{ + struct evergreen_mc_save save; + + if (!(RREG32(GRBM_STATUS) & GUI_ACTIVE)) + reset_mask &= ~(RADEON_RESET_GFX | RADEON_RESET_COMPUTE); + + if (RREG32(DMA_STATUS_REG) & DMA_IDLE) + reset_mask &= ~RADEON_RESET_DMA; + + if (reset_mask == 0) + return 0; + + dev_info(rdev->dev, "GPU softreset: 0x%08X\n", reset_mask); + + dev_info(rdev->dev, " VM_CONTEXT0_PROTECTION_FAULT_ADDR 0x%08X\n", + RREG32(0x14F8)); + dev_info(rdev->dev, " VM_CONTEXT0_PROTECTION_FAULT_STATUS 0x%08X\n", + RREG32(0x14D8)); + dev_info(rdev->dev, " VM_CONTEXT1_PROTECTION_FAULT_ADDR 0x%08X\n", + RREG32(0x14FC)); + dev_info(rdev->dev, " VM_CONTEXT1_PROTECTION_FAULT_STATUS 0x%08X\n", + RREG32(0x14DC)); + + evergreen_mc_stop(rdev, &save); + if (evergreen_mc_wait_for_idle(rdev)) { + dev_warn(rdev->dev, "Wait for MC idle timedout !\n"); + } + + if (reset_mask & (RADEON_RESET_GFX | RADEON_RESET_COMPUTE)) + cayman_gpu_soft_reset_gfx(rdev); + + if (reset_mask & RADEON_RESET_DMA) + cayman_gpu_soft_reset_dma(rdev); + + /* Wait a little for things to settle down */ + DRM_UDELAY(50); + + evergreen_mc_resume(rdev, &save); + return 0; +} + +int cayman_asic_reset(struct radeon_device *rdev) +{ + return cayman_gpu_soft_reset(rdev, (RADEON_RESET_GFX | + RADEON_RESET_COMPUTE | + RADEON_RESET_DMA)); +} + +/** + * cayman_dma_is_lockup - Check if the DMA engine is locked up + * + * @rdev: radeon_device pointer + * @ring: radeon_ring structure holding ring information + * + * Check if the async DMA engine is locked up (cayman-SI). + * Returns true if the engine appears to be locked up, false if not. + */ +bool cayman_dma_is_lockup(struct radeon_device *rdev, struct radeon_ring *ring) +{ + u32 dma_status_reg; + + if (ring->idx == R600_RING_TYPE_DMA_INDEX) + dma_status_reg = RREG32(DMA_STATUS_REG + DMA0_REGISTER_OFFSET); + else + dma_status_reg = RREG32(DMA_STATUS_REG + DMA1_REGISTER_OFFSET); + if (dma_status_reg & DMA_IDLE) { + radeon_ring_lockup_update(ring); + return false; + } + /* force ring activities */ + radeon_ring_force_activity(rdev, ring); + return radeon_ring_test_lockup(rdev, ring); +} + +static int cayman_startup(struct radeon_device *rdev) +{ + struct radeon_ring *ring = &rdev->ring[RADEON_RING_TYPE_GFX_INDEX]; + int r; + + /* enable pcie gen2 link */ + evergreen_pcie_gen2_enable(rdev); + + if (rdev->flags & RADEON_IS_IGP) { + if (!rdev->me_fw || !rdev->pfp_fw || !rdev->rlc_fw) { + r = ni_init_microcode(rdev); + if (r) { + DRM_ERROR("Failed to load firmware!\n"); + return r; + } + } + } else { + if (!rdev->me_fw || !rdev->pfp_fw || !rdev->rlc_fw || !rdev->mc_fw) { + r = ni_init_microcode(rdev); + if (r) { + DRM_ERROR("Failed to load firmware!\n"); + return r; + } + } + + r = ni_mc_load_microcode(rdev); + if (r) { + DRM_ERROR("Failed to load MC firmware!\n"); + return r; + } + } + + r = r600_vram_scratch_init(rdev); + if (r) + return r; + + evergreen_mc_program(rdev); + r = cayman_pcie_gart_enable(rdev); + if (r) + return r; + cayman_gpu_init(rdev); + + r = evergreen_blit_init(rdev); + if (r) { + r600_blit_fini(rdev); + rdev->asic->copy.copy = NULL; + dev_warn(rdev->dev, "failed blitter (%d) falling back to memcpy\n", r); + } + + /* allocate rlc buffers */ + if (rdev->flags & RADEON_IS_IGP) { + r = si_rlc_init(rdev); + if (r) { + DRM_ERROR("Failed to init rlc BOs!\n"); + return r; + } + } + + /* allocate wb buffer */ + r = radeon_wb_init(rdev); + if (r) + return r; + + r = radeon_fence_driver_start_ring(rdev, RADEON_RING_TYPE_GFX_INDEX); + if (r) { + dev_err(rdev->dev, "failed initializing CP fences (%d).\n", r); + return r; + } + + r = radeon_fence_driver_start_ring(rdev, CAYMAN_RING_TYPE_CP1_INDEX); + if (r) { + dev_err(rdev->dev, "failed initializing CP fences (%d).\n", r); + return r; + } + + r = radeon_fence_driver_start_ring(rdev, CAYMAN_RING_TYPE_CP2_INDEX); + if (r) { + dev_err(rdev->dev, "failed initializing CP fences (%d).\n", r); + return r; + } + + r = radeon_fence_driver_start_ring(rdev, R600_RING_TYPE_DMA_INDEX); + if (r) { + dev_err(rdev->dev, "failed initializing DMA fences (%d).\n", r); + return r; + } + + r = radeon_fence_driver_start_ring(rdev, CAYMAN_RING_TYPE_DMA1_INDEX); + if (r) { + dev_err(rdev->dev, "failed initializing DMA fences (%d).\n", r); + return r; + } + + /* Enable IRQ */ + r = r600_irq_init(rdev); + if (r) { + DRM_ERROR("radeon: IH init failed (%d).\n", r); + radeon_irq_kms_fini(rdev); + return r; + } + evergreen_irq_set(rdev); + + r = radeon_ring_init(rdev, ring, ring->ring_size, RADEON_WB_CP_RPTR_OFFSET, + CP_RB0_RPTR, CP_RB0_WPTR, + 0, 0xfffff, RADEON_CP_PACKET2); + if (r) + return r; + + ring = &rdev->ring[R600_RING_TYPE_DMA_INDEX]; + r = radeon_ring_init(rdev, ring, ring->ring_size, R600_WB_DMA_RPTR_OFFSET, + DMA_RB_RPTR + DMA0_REGISTER_OFFSET, + DMA_RB_WPTR + DMA0_REGISTER_OFFSET, + 2, 0x3fffc, DMA_PACKET(DMA_PACKET_NOP, 0, 0, 0)); + if (r) + return r; + + ring = &rdev->ring[CAYMAN_RING_TYPE_DMA1_INDEX]; + r = radeon_ring_init(rdev, ring, ring->ring_size, CAYMAN_WB_DMA1_RPTR_OFFSET, + DMA_RB_RPTR + DMA1_REGISTER_OFFSET, + DMA_RB_WPTR + DMA1_REGISTER_OFFSET, + 2, 0x3fffc, DMA_PACKET(DMA_PACKET_NOP, 0, 0, 0)); + if (r) + return r; + + r = cayman_cp_load_microcode(rdev); + if (r) + return r; + r = cayman_cp_resume(rdev); + if (r) + return r; + + r = cayman_dma_resume(rdev); + if (r) + return r; + + r = radeon_ib_pool_init(rdev); + if (r) { + dev_err(rdev->dev, "IB initialization failed (%d).\n", r); + return r; + } + + r = radeon_vm_manager_init(rdev); + if (r) { + dev_err(rdev->dev, "vm manager initialization failed (%d).\n", r); + return r; + } + + r = r600_audio_init(rdev); + if (r) + return r; + + return 0; +} + +int cayman_resume(struct radeon_device *rdev) +{ + int r; + + /* Do not reset GPU before posting, on rv770 hw unlike on r500 hw, + * posting will perform necessary task to bring back GPU into good + * shape. + */ + /* post card */ + atom_asic_init(rdev->mode_info.atom_context); + + rdev->accel_working = true; + r = cayman_startup(rdev); + if (r) { + DRM_ERROR("cayman startup failed on resume\n"); + rdev->accel_working = false; + return r; + } + return r; +} + +int cayman_suspend(struct radeon_device *rdev) +{ + r600_audio_fini(rdev); + cayman_cp_enable(rdev, false); + cayman_dma_stop(rdev); + evergreen_irq_suspend(rdev); + radeon_wb_disable(rdev); + cayman_pcie_gart_disable(rdev); + return 0; +} + +/* Plan is to move initialization in that function and use + * helper function so that radeon_device_init pretty much + * do nothing more than calling asic specific function. This + * should also allow to remove a bunch of callback function + * like vram_info. + */ +int cayman_init(struct radeon_device *rdev) +{ + struct radeon_ring *ring = &rdev->ring[RADEON_RING_TYPE_GFX_INDEX]; + int r; + + /* Read BIOS */ + if (!radeon_get_bios(rdev)) { + if (ASIC_IS_AVIVO(rdev)) + return -EINVAL; + } + /* Must be an ATOMBIOS */ + if (!rdev->is_atom_bios) { + dev_err(rdev->dev, "Expecting atombios for cayman GPU\n"); + return -EINVAL; + } + r = radeon_atombios_init(rdev); + if (r) + return r; + + /* Post card if necessary */ + if (!radeon_card_posted(rdev)) { + if (!rdev->bios) { + dev_err(rdev->dev, "Card not posted and no BIOS - ignoring\n"); + return -EINVAL; + } + DRM_INFO("GPU not posted. posting now...\n"); + atom_asic_init(rdev->mode_info.atom_context); + } + /* Initialize scratch registers */ + r600_scratch_init(rdev); + /* Initialize surface registers */ + radeon_surface_init(rdev); + /* Initialize clocks */ + radeon_get_clock_info(rdev->ddev); + /* Fence driver */ + r = radeon_fence_driver_init(rdev); + if (r) + return r; + /* initialize memory controller */ + r = evergreen_mc_init(rdev); + if (r) + return r; + /* Memory manager */ + r = radeon_bo_init(rdev); + if (r) + return r; + + r = radeon_irq_kms_init(rdev); + if (r) + return r; + + ring->ring_obj = NULL; + r600_ring_init(rdev, ring, 1024 * 1024); + + ring = &rdev->ring[R600_RING_TYPE_DMA_INDEX]; + ring->ring_obj = NULL; + r600_ring_init(rdev, ring, 64 * 1024); + + ring = &rdev->ring[CAYMAN_RING_TYPE_DMA1_INDEX]; + ring->ring_obj = NULL; + r600_ring_init(rdev, ring, 64 * 1024); + + rdev->ih.ring_obj = NULL; + r600_ih_ring_init(rdev, 64 * 1024); + + r = r600_pcie_gart_init(rdev); + if (r) + return r; + + rdev->accel_working = true; + r = cayman_startup(rdev); + if (r) { + dev_err(rdev->dev, "disabling GPU acceleration\n"); + cayman_cp_fini(rdev); + cayman_dma_fini(rdev); + r600_irq_fini(rdev); + if (rdev->flags & RADEON_IS_IGP) + si_rlc_fini(rdev); + radeon_wb_fini(rdev); + radeon_ib_pool_fini(rdev); + radeon_vm_manager_fini(rdev); + radeon_irq_kms_fini(rdev); + cayman_pcie_gart_fini(rdev); + rdev->accel_working = false; + } + + /* Don't start up if the MC ucode is missing. + * The default clocks and voltages before the MC ucode + * is loaded are not suffient for advanced operations. + * + * We can skip this check for TN, because there is no MC + * ucode. + */ + if (!rdev->mc_fw && !(rdev->flags & RADEON_IS_IGP)) { + DRM_ERROR("radeon: MC ucode required for NI+.\n"); + return -EINVAL; + } + + return 0; +} + +void cayman_fini(struct radeon_device *rdev) +{ + r600_blit_fini(rdev); + cayman_cp_fini(rdev); + cayman_dma_fini(rdev); + r600_irq_fini(rdev); + if (rdev->flags & RADEON_IS_IGP) + si_rlc_fini(rdev); + radeon_wb_fini(rdev); + radeon_vm_manager_fini(rdev); + radeon_ib_pool_fini(rdev); + radeon_irq_kms_fini(rdev); + cayman_pcie_gart_fini(rdev); + r600_vram_scratch_fini(rdev); + radeon_gem_fini(rdev); + radeon_fence_driver_fini(rdev); + radeon_bo_fini(rdev); + radeon_atombios_fini(rdev); + ni_fini_microcode(rdev); + free(rdev->bios, DRM_MEM_DRIVER); + rdev->bios = NULL; +} + +/* + * vm + */ +int cayman_vm_init(struct radeon_device *rdev) +{ + /* number of VMs */ + rdev->vm_manager.nvm = 8; + /* base offset of vram pages */ + if (rdev->flags & RADEON_IS_IGP) { + u64 tmp = RREG32(FUS_MC_VM_FB_OFFSET); + tmp <<= 22; + rdev->vm_manager.vram_base_offset = tmp; + } else + rdev->vm_manager.vram_base_offset = 0; + return 0; +} + +void cayman_vm_fini(struct radeon_device *rdev) +{ +} + +#define R600_ENTRY_VALID (1 << 0) +#define R600_PTE_SYSTEM (1 << 1) +#define R600_PTE_SNOOPED (1 << 2) +#define R600_PTE_READABLE (1 << 5) +#define R600_PTE_WRITEABLE (1 << 6) + +uint32_t cayman_vm_page_flags(struct radeon_device *rdev, uint32_t flags) +{ + uint32_t r600_flags = 0; + r600_flags |= (flags & RADEON_VM_PAGE_VALID) ? R600_ENTRY_VALID : 0; + r600_flags |= (flags & RADEON_VM_PAGE_READABLE) ? R600_PTE_READABLE : 0; + r600_flags |= (flags & RADEON_VM_PAGE_WRITEABLE) ? R600_PTE_WRITEABLE : 0; + if (flags & RADEON_VM_PAGE_SYSTEM) { + r600_flags |= R600_PTE_SYSTEM; + r600_flags |= (flags & RADEON_VM_PAGE_SNOOPED) ? R600_PTE_SNOOPED : 0; + } + return r600_flags; +} + +/** + * cayman_vm_set_page - update the page tables using the CP + * + * @rdev: radeon_device pointer + * @pe: addr of the page entry + * @addr: dst addr to write into pe + * @count: number of page entries to update + * @incr: increase next addr by incr bytes + * @flags: access flags + * + * Update the page tables using the CP (cayman-si). + */ +void cayman_vm_set_page(struct radeon_device *rdev, uint64_t pe, + uint64_t addr, unsigned count, + uint32_t incr, uint32_t flags) +{ + struct radeon_ring *ring = &rdev->ring[rdev->asic->vm.pt_ring_index]; + uint32_t r600_flags = cayman_vm_page_flags(rdev, flags); + uint64_t value; + unsigned ndw; + + if (rdev->asic->vm.pt_ring_index == RADEON_RING_TYPE_GFX_INDEX) { + while (count) { + ndw = 1 + count * 2; + if (ndw > 0x3FFF) + ndw = 0x3FFF; + + radeon_ring_write(ring, PACKET3(PACKET3_ME_WRITE, ndw)); + radeon_ring_write(ring, pe); + radeon_ring_write(ring, upper_32_bits(pe) & 0xff); + for (; ndw > 1; ndw -= 2, --count, pe += 8) { + if (flags & RADEON_VM_PAGE_SYSTEM) { + value = radeon_vm_map_gart(rdev, addr); + value &= 0xFFFFFFFFFFFFF000ULL; + } else if (flags & RADEON_VM_PAGE_VALID) { + value = addr; + } else { + value = 0; + } + addr += incr; + value |= r600_flags; + radeon_ring_write(ring, value); + radeon_ring_write(ring, upper_32_bits(value)); + } + } + } else { + while (count) { + ndw = count * 2; + if (ndw > 0xFFFFE) + ndw = 0xFFFFE; + + /* for non-physically contiguous pages (system) */ + radeon_ring_write(ring, DMA_PACKET(DMA_PACKET_WRITE, 0, 0, ndw)); + radeon_ring_write(ring, pe); + radeon_ring_write(ring, upper_32_bits(pe) & 0xff); + for (; ndw > 0; ndw -= 2, --count, pe += 8) { + if (flags & RADEON_VM_PAGE_SYSTEM) { + value = radeon_vm_map_gart(rdev, addr); + value &= 0xFFFFFFFFFFFFF000ULL; + } else if (flags & RADEON_VM_PAGE_VALID) { + value = addr; + } else { + value = 0; + } + addr += incr; + value |= r600_flags; + radeon_ring_write(ring, value); + radeon_ring_write(ring, upper_32_bits(value)); + } + } + } +} + +/** + * cayman_vm_flush - vm flush using the CP + * + * @rdev: radeon_device pointer + * + * Update the page table base and flush the VM TLB + * using the CP (cayman-si). + */ +void cayman_vm_flush(struct radeon_device *rdev, int ridx, struct radeon_vm *vm) +{ + struct radeon_ring *ring = &rdev->ring[ridx]; + + if (vm == NULL) + return; + + radeon_ring_write(ring, PACKET0(VM_CONTEXT0_PAGE_TABLE_BASE_ADDR + (vm->id << 2), 0)); + radeon_ring_write(ring, vm->pd_gpu_addr >> 12); + + /* flush hdp cache */ + radeon_ring_write(ring, PACKET0(HDP_MEM_COHERENCY_FLUSH_CNTL, 0)); + radeon_ring_write(ring, 0x1); + + /* bits 0-7 are the VM contexts0-7 */ + radeon_ring_write(ring, PACKET0(VM_INVALIDATE_REQUEST, 0)); + radeon_ring_write(ring, 1 << vm->id); + + /* sync PFP to ME, otherwise we might get invalid PFP reads */ + radeon_ring_write(ring, PACKET3(PACKET3_PFP_SYNC_ME, 0)); + radeon_ring_write(ring, 0x0); +} + +void cayman_dma_vm_flush(struct radeon_device *rdev, int ridx, struct radeon_vm *vm) +{ + struct radeon_ring *ring = &rdev->ring[ridx]; + + if (vm == NULL) + return; + + radeon_ring_write(ring, DMA_PACKET(DMA_PACKET_SRBM_WRITE, 0, 0, 0)); + radeon_ring_write(ring, (0xf << 16) | ((VM_CONTEXT0_PAGE_TABLE_BASE_ADDR + (vm->id << 2)) >> 2)); + radeon_ring_write(ring, vm->pd_gpu_addr >> 12); + + /* flush hdp cache */ + radeon_ring_write(ring, DMA_PACKET(DMA_PACKET_SRBM_WRITE, 0, 0, 0)); + radeon_ring_write(ring, (0xf << 16) | (HDP_MEM_COHERENCY_FLUSH_CNTL >> 2)); + radeon_ring_write(ring, 1); + + /* bits 0-7 are the VM contexts0-7 */ + radeon_ring_write(ring, DMA_PACKET(DMA_PACKET_SRBM_WRITE, 0, 0, 0)); + radeon_ring_write(ring, (0xf << 16) | (VM_INVALIDATE_REQUEST >> 2)); + radeon_ring_write(ring, 1 << vm->id); +} + diff --git a/sys/dev/drm2/radeon/ni_reg.h b/sys/dev/drm2/radeon/ni_reg.h new file mode 100644 index 00000000000..f72c01c09b1 --- /dev/null +++ b/sys/dev/drm2/radeon/ni_reg.h @@ -0,0 +1,89 @@ +/* + * Copyright 2010 Advanced Micro Devices, Inc. + * + * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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. + * + * Authors: Alex Deucher + */ +#ifndef __NI_REG_H__ +#define __NI_REG_H__ + +#include +__FBSDID("$FreeBSD$"); + +/* northern islands - DCE5 */ + +#define NI_INPUT_GAMMA_CONTROL 0x6840 +# define NI_GRPH_INPUT_GAMMA_MODE(x) (((x) & 0x3) << 0) +# define NI_INPUT_GAMMA_USE_LUT 0 +# define NI_INPUT_GAMMA_BYPASS 1 +# define NI_INPUT_GAMMA_SRGB_24 2 +# define NI_INPUT_GAMMA_XVYCC_222 3 +# define NI_OVL_INPUT_GAMMA_MODE(x) (((x) & 0x3) << 4) + +#define NI_PRESCALE_GRPH_CONTROL 0x68b4 +# define NI_GRPH_PRESCALE_BYPASS (1 << 4) + +#define NI_PRESCALE_OVL_CONTROL 0x68c4 +# define NI_OVL_PRESCALE_BYPASS (1 << 4) + +#define NI_INPUT_CSC_CONTROL 0x68d4 +# define NI_INPUT_CSC_GRPH_MODE(x) (((x) & 0x3) << 0) +# define NI_INPUT_CSC_BYPASS 0 +# define NI_INPUT_CSC_PROG_COEFF 1 +# define NI_INPUT_CSC_PROG_SHARED_MATRIXA 2 +# define NI_INPUT_CSC_OVL_MODE(x) (((x) & 0x3) << 4) + +#define NI_OUTPUT_CSC_CONTROL 0x68f0 +# define NI_OUTPUT_CSC_GRPH_MODE(x) (((x) & 0x7) << 0) +# define NI_OUTPUT_CSC_BYPASS 0 +# define NI_OUTPUT_CSC_TV_RGB 1 +# define NI_OUTPUT_CSC_YCBCR_601 2 +# define NI_OUTPUT_CSC_YCBCR_709 3 +# define NI_OUTPUT_CSC_PROG_COEFF 4 +# define NI_OUTPUT_CSC_PROG_SHARED_MATRIXB 5 +# define NI_OUTPUT_CSC_OVL_MODE(x) (((x) & 0x7) << 4) + +#define NI_DEGAMMA_CONTROL 0x6960 +# define NI_GRPH_DEGAMMA_MODE(x) (((x) & 0x3) << 0) +# define NI_DEGAMMA_BYPASS 0 +# define NI_DEGAMMA_SRGB_24 1 +# define NI_DEGAMMA_XVYCC_222 2 +# define NI_OVL_DEGAMMA_MODE(x) (((x) & 0x3) << 4) +# define NI_ICON_DEGAMMA_MODE(x) (((x) & 0x3) << 8) +# define NI_CURSOR_DEGAMMA_MODE(x) (((x) & 0x3) << 12) + +#define NI_GAMUT_REMAP_CONTROL 0x6964 +# define NI_GRPH_GAMUT_REMAP_MODE(x) (((x) & 0x3) << 0) +# define NI_GAMUT_REMAP_BYPASS 0 +# define NI_GAMUT_REMAP_PROG_COEFF 1 +# define NI_GAMUT_REMAP_PROG_SHARED_MATRIXA 2 +# define NI_GAMUT_REMAP_PROG_SHARED_MATRIXB 3 +# define NI_OVL_GAMUT_REMAP_MODE(x) (((x) & 0x3) << 4) + +#define NI_REGAMMA_CONTROL 0x6a80 +# define NI_GRPH_REGAMMA_MODE(x) (((x) & 0x7) << 0) +# define NI_REGAMMA_BYPASS 0 +# define NI_REGAMMA_SRGB_24 1 +# define NI_REGAMMA_XVYCC_222 2 +# define NI_REGAMMA_PROG_A 3 +# define NI_REGAMMA_PROG_B 4 +# define NI_OVL_REGAMMA_MODE(x) (((x) & 0x7) << 4) + +#endif diff --git a/sys/dev/drm2/radeon/nid.h b/sys/dev/drm2/radeon/nid.h new file mode 100644 index 00000000000..afe7a328698 --- /dev/null +++ b/sys/dev/drm2/radeon/nid.h @@ -0,0 +1,680 @@ +/* + * Copyright 2010 Advanced Micro Devices, Inc. + * + * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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. + * + * Authors: Alex Deucher + */ +#ifndef NI_H +#define NI_H + +#include +__FBSDID("$FreeBSD$"); + +#define CAYMAN_MAX_SH_GPRS 256 +#define CAYMAN_MAX_TEMP_GPRS 16 +#define CAYMAN_MAX_SH_THREADS 256 +#define CAYMAN_MAX_SH_STACK_ENTRIES 4096 +#define CAYMAN_MAX_FRC_EOV_CNT 16384 +#define CAYMAN_MAX_BACKENDS 8 +#define CAYMAN_MAX_BACKENDS_MASK 0xFF +#define CAYMAN_MAX_BACKENDS_PER_SE_MASK 0xF +#define CAYMAN_MAX_SIMDS 16 +#define CAYMAN_MAX_SIMDS_MASK 0xFFFF +#define CAYMAN_MAX_SIMDS_PER_SE_MASK 0xFFF +#define CAYMAN_MAX_PIPES 8 +#define CAYMAN_MAX_PIPES_MASK 0xFF +#define CAYMAN_MAX_LDS_NUM 0xFFFF +#define CAYMAN_MAX_TCC 16 +#define CAYMAN_MAX_TCC_MASK 0xFF + +#define CAYMAN_GB_ADDR_CONFIG_GOLDEN 0x02011003 +#define ARUBA_GB_ADDR_CONFIG_GOLDEN 0x12010001 + +#define DMIF_ADDR_CONFIG 0xBD4 +#define SRBM_GFX_CNTL 0x0E44 +#define RINGID(x) (((x) & 0x3) << 0) +#define VMID(x) (((x) & 0x7) << 0) +#define SRBM_STATUS 0x0E50 + +#define SRBM_SOFT_RESET 0x0E60 +#define SOFT_RESET_BIF (1 << 1) +#define SOFT_RESET_CG (1 << 2) +#define SOFT_RESET_DC (1 << 5) +#define SOFT_RESET_DMA1 (1 << 6) +#define SOFT_RESET_GRBM (1 << 8) +#define SOFT_RESET_HDP (1 << 9) +#define SOFT_RESET_IH (1 << 10) +#define SOFT_RESET_MC (1 << 11) +#define SOFT_RESET_RLC (1 << 13) +#define SOFT_RESET_ROM (1 << 14) +#define SOFT_RESET_SEM (1 << 15) +#define SOFT_RESET_VMC (1 << 17) +#define SOFT_RESET_DMA (1 << 20) +#define SOFT_RESET_TST (1 << 21) +#define SOFT_RESET_REGBB (1 << 22) +#define SOFT_RESET_ORB (1 << 23) + +#define VM_CONTEXT0_REQUEST_RESPONSE 0x1470 +#define REQUEST_TYPE(x) (((x) & 0xf) << 0) +#define RESPONSE_TYPE_MASK 0x000000F0 +#define RESPONSE_TYPE_SHIFT 4 +#define VM_L2_CNTL 0x1400 +#define ENABLE_L2_CACHE (1 << 0) +#define ENABLE_L2_FRAGMENT_PROCESSING (1 << 1) +#define ENABLE_L2_PTE_CACHE_LRU_UPDATE_BY_WRITE (1 << 9) +#define ENABLE_L2_PDE0_CACHE_LRU_UPDATE_BY_WRITE (1 << 10) +#define EFFECTIVE_L2_QUEUE_SIZE(x) (((x) & 7) << 14) +#define CONTEXT1_IDENTITY_ACCESS_MODE(x) (((x) & 3) << 18) +/* CONTEXT1_IDENTITY_ACCESS_MODE + * 0 physical = logical + * 1 logical via context1 page table + * 2 inside identity aperture use translation, outside physical = logical + * 3 inside identity aperture physical = logical, outside use translation + */ +#define VM_L2_CNTL2 0x1404 +#define INVALIDATE_ALL_L1_TLBS (1 << 0) +#define INVALIDATE_L2_CACHE (1 << 1) +#define VM_L2_CNTL3 0x1408 +#define BANK_SELECT(x) ((x) << 0) +#define CACHE_UPDATE_MODE(x) ((x) << 6) +#define L2_CACHE_BIGK_ASSOCIATIVITY (1 << 20) +#define L2_CACHE_BIGK_FRAGMENT_SIZE(x) ((x) << 15) +#define VM_L2_STATUS 0x140C +#define L2_BUSY (1 << 0) +#define VM_CONTEXT0_CNTL 0x1410 +#define ENABLE_CONTEXT (1 << 0) +#define PAGE_TABLE_DEPTH(x) (((x) & 3) << 1) +#define RANGE_PROTECTION_FAULT_ENABLE_INTERRUPT (1 << 3) +#define RANGE_PROTECTION_FAULT_ENABLE_DEFAULT (1 << 4) +#define DUMMY_PAGE_PROTECTION_FAULT_ENABLE_INTERRUPT (1 << 6) +#define DUMMY_PAGE_PROTECTION_FAULT_ENABLE_DEFAULT (1 << 7) +#define PDE0_PROTECTION_FAULT_ENABLE_INTERRUPT (1 << 9) +#define PDE0_PROTECTION_FAULT_ENABLE_DEFAULT (1 << 10) +#define VALID_PROTECTION_FAULT_ENABLE_INTERRUPT (1 << 12) +#define VALID_PROTECTION_FAULT_ENABLE_DEFAULT (1 << 13) +#define READ_PROTECTION_FAULT_ENABLE_INTERRUPT (1 << 15) +#define READ_PROTECTION_FAULT_ENABLE_DEFAULT (1 << 16) +#define WRITE_PROTECTION_FAULT_ENABLE_INTERRUPT (1 << 18) +#define WRITE_PROTECTION_FAULT_ENABLE_DEFAULT (1 << 19) +#define VM_CONTEXT1_CNTL 0x1414 +#define VM_CONTEXT0_CNTL2 0x1430 +#define VM_CONTEXT1_CNTL2 0x1434 +#define VM_INVALIDATE_REQUEST 0x1478 +#define VM_INVALIDATE_RESPONSE 0x147c +#define VM_CONTEXT0_PROTECTION_FAULT_DEFAULT_ADDR 0x1518 +#define VM_CONTEXT1_PROTECTION_FAULT_DEFAULT_ADDR 0x151c +#define VM_CONTEXT0_PAGE_TABLE_BASE_ADDR 0x153C +#define VM_CONTEXT0_PAGE_TABLE_START_ADDR 0x155C +#define VM_CONTEXT0_PAGE_TABLE_END_ADDR 0x157C + +#define MC_SHARED_CHMAP 0x2004 +#define NOOFCHAN_SHIFT 12 +#define NOOFCHAN_MASK 0x00003000 +#define MC_SHARED_CHREMAP 0x2008 + +#define MC_VM_SYSTEM_APERTURE_LOW_ADDR 0x2034 +#define MC_VM_SYSTEM_APERTURE_HIGH_ADDR 0x2038 +#define MC_VM_SYSTEM_APERTURE_DEFAULT_ADDR 0x203C +#define MC_VM_MX_L1_TLB_CNTL 0x2064 +#define ENABLE_L1_TLB (1 << 0) +#define ENABLE_L1_FRAGMENT_PROCESSING (1 << 1) +#define SYSTEM_ACCESS_MODE_PA_ONLY (0 << 3) +#define SYSTEM_ACCESS_MODE_USE_SYS_MAP (1 << 3) +#define SYSTEM_ACCESS_MODE_IN_SYS (2 << 3) +#define SYSTEM_ACCESS_MODE_NOT_IN_SYS (3 << 3) +#define SYSTEM_APERTURE_UNMAPPED_ACCESS_PASS_THRU (0 << 5) +#define ENABLE_ADVANCED_DRIVER_MODEL (1 << 6) +#define FUS_MC_VM_FB_OFFSET 0x2068 + +#define MC_SHARED_BLACKOUT_CNTL 0x20ac +#define MC_ARB_RAMCFG 0x2760 +#define NOOFBANK_SHIFT 0 +#define NOOFBANK_MASK 0x00000003 +#define NOOFRANK_SHIFT 2 +#define NOOFRANK_MASK 0x00000004 +#define NOOFROWS_SHIFT 3 +#define NOOFROWS_MASK 0x00000038 +#define NOOFCOLS_SHIFT 6 +#define NOOFCOLS_MASK 0x000000C0 +#define CHANSIZE_SHIFT 8 +#define CHANSIZE_MASK 0x00000100 +#define BURSTLENGTH_SHIFT 9 +#define BURSTLENGTH_MASK 0x00000200 +#define CHANSIZE_OVERRIDE (1 << 11) +#define MC_SEQ_SUP_CNTL 0x28c8 +#define RUN_MASK (1 << 0) +#define MC_SEQ_SUP_PGM 0x28cc +#define MC_IO_PAD_CNTL_D0 0x29d0 +#define MEM_FALL_OUT_CMD (1 << 8) +#define MC_SEQ_MISC0 0x2a00 +#define MC_SEQ_MISC0_GDDR5_SHIFT 28 +#define MC_SEQ_MISC0_GDDR5_MASK 0xf0000000 +#define MC_SEQ_MISC0_GDDR5_VALUE 5 +#define MC_SEQ_IO_DEBUG_INDEX 0x2a44 +#define MC_SEQ_IO_DEBUG_DATA 0x2a48 + +#define HDP_HOST_PATH_CNTL 0x2C00 +#define HDP_NONSURFACE_BASE 0x2C04 +#define HDP_NONSURFACE_INFO 0x2C08 +#define HDP_NONSURFACE_SIZE 0x2C0C +#define HDP_ADDR_CONFIG 0x2F48 +#define HDP_MISC_CNTL 0x2F4C +#define HDP_FLUSH_INVALIDATE_CACHE (1 << 0) + +#define CC_SYS_RB_BACKEND_DISABLE 0x3F88 +#define GC_USER_SYS_RB_BACKEND_DISABLE 0x3F8C +#define CGTS_SYS_TCC_DISABLE 0x3F90 +#define CGTS_USER_SYS_TCC_DISABLE 0x3F94 + +#define RLC_GFX_INDEX 0x3FC4 + +#define CONFIG_MEMSIZE 0x5428 + +#define HDP_MEM_COHERENCY_FLUSH_CNTL 0x5480 +#define HDP_REG_COHERENCY_FLUSH_CNTL 0x54A0 + +#define GRBM_CNTL 0x8000 +#define GRBM_READ_TIMEOUT(x) ((x) << 0) +#define GRBM_STATUS 0x8010 +#define CMDFIFO_AVAIL_MASK 0x0000000F +#define RING2_RQ_PENDING (1 << 4) +#define SRBM_RQ_PENDING (1 << 5) +#define RING1_RQ_PENDING (1 << 6) +#define CF_RQ_PENDING (1 << 7) +#define PF_RQ_PENDING (1 << 8) +#define GDS_DMA_RQ_PENDING (1 << 9) +#define GRBM_EE_BUSY (1 << 10) +#define SX_CLEAN (1 << 11) +#define DB_CLEAN (1 << 12) +#define CB_CLEAN (1 << 13) +#define TA_BUSY (1 << 14) +#define GDS_BUSY (1 << 15) +#define VGT_BUSY_NO_DMA (1 << 16) +#define VGT_BUSY (1 << 17) +#define IA_BUSY_NO_DMA (1 << 18) +#define IA_BUSY (1 << 19) +#define SX_BUSY (1 << 20) +#define SH_BUSY (1 << 21) +#define SPI_BUSY (1 << 22) +#define SC_BUSY (1 << 24) +#define PA_BUSY (1 << 25) +#define DB_BUSY (1 << 26) +#define CP_COHERENCY_BUSY (1 << 28) +#define CP_BUSY (1 << 29) +#define CB_BUSY (1 << 30) +#define GUI_ACTIVE (1 << 31) +#define GRBM_STATUS_SE0 0x8014 +#define GRBM_STATUS_SE1 0x8018 +#define SE_SX_CLEAN (1 << 0) +#define SE_DB_CLEAN (1 << 1) +#define SE_CB_CLEAN (1 << 2) +#define SE_VGT_BUSY (1 << 23) +#define SE_PA_BUSY (1 << 24) +#define SE_TA_BUSY (1 << 25) +#define SE_SX_BUSY (1 << 26) +#define SE_SPI_BUSY (1 << 27) +#define SE_SH_BUSY (1 << 28) +#define SE_SC_BUSY (1 << 29) +#define SE_DB_BUSY (1 << 30) +#define SE_CB_BUSY (1 << 31) +#define GRBM_SOFT_RESET 0x8020 +#define SOFT_RESET_CP (1 << 0) +#define SOFT_RESET_CB (1 << 1) +#define SOFT_RESET_DB (1 << 3) +#define SOFT_RESET_GDS (1 << 4) +#define SOFT_RESET_PA (1 << 5) +#define SOFT_RESET_SC (1 << 6) +#define SOFT_RESET_SPI (1 << 8) +#define SOFT_RESET_SH (1 << 9) +#define SOFT_RESET_SX (1 << 10) +#define SOFT_RESET_TC (1 << 11) +#define SOFT_RESET_TA (1 << 12) +#define SOFT_RESET_VGT (1 << 14) +#define SOFT_RESET_IA (1 << 15) + +#define GRBM_GFX_INDEX 0x802C +#define INSTANCE_INDEX(x) ((x) << 0) +#define SE_INDEX(x) ((x) << 16) +#define INSTANCE_BROADCAST_WRITES (1 << 30) +#define SE_BROADCAST_WRITES (1 << 31) + +#define SCRATCH_REG0 0x8500 +#define SCRATCH_REG1 0x8504 +#define SCRATCH_REG2 0x8508 +#define SCRATCH_REG3 0x850C +#define SCRATCH_REG4 0x8510 +#define SCRATCH_REG5 0x8514 +#define SCRATCH_REG6 0x8518 +#define SCRATCH_REG7 0x851C +#define SCRATCH_UMSK 0x8540 +#define SCRATCH_ADDR 0x8544 +#define CP_SEM_WAIT_TIMER 0x85BC +#define CP_SEM_INCOMPLETE_TIMER_CNTL 0x85C8 +#define CP_COHER_CNTL2 0x85E8 +#define CP_STALLED_STAT1 0x8674 +#define CP_STALLED_STAT2 0x8678 +#define CP_BUSY_STAT 0x867C +#define CP_STAT 0x8680 +#define CP_ME_CNTL 0x86D8 +#define CP_ME_HALT (1 << 28) +#define CP_PFP_HALT (1 << 26) +#define CP_RB2_RPTR 0x86f8 +#define CP_RB1_RPTR 0x86fc +#define CP_RB0_RPTR 0x8700 +#define CP_RB_WPTR_DELAY 0x8704 +#define CP_MEQ_THRESHOLDS 0x8764 +#define MEQ1_START(x) ((x) << 0) +#define MEQ2_START(x) ((x) << 8) +#define CP_PERFMON_CNTL 0x87FC + +#define VGT_CACHE_INVALIDATION 0x88C4 +#define CACHE_INVALIDATION(x) ((x) << 0) +#define VC_ONLY 0 +#define TC_ONLY 1 +#define VC_AND_TC 2 +#define AUTO_INVLD_EN(x) ((x) << 6) +#define NO_AUTO 0 +#define ES_AUTO 1 +#define GS_AUTO 2 +#define ES_AND_GS_AUTO 3 +#define VGT_GS_VERTEX_REUSE 0x88D4 + +#define CC_GC_SHADER_PIPE_CONFIG 0x8950 +#define GC_USER_SHADER_PIPE_CONFIG 0x8954 +#define INACTIVE_QD_PIPES(x) ((x) << 8) +#define INACTIVE_QD_PIPES_MASK 0x0000FF00 +#define INACTIVE_QD_PIPES_SHIFT 8 +#define INACTIVE_SIMDS(x) ((x) << 16) +#define INACTIVE_SIMDS_MASK 0xFFFF0000 +#define INACTIVE_SIMDS_SHIFT 16 + +#define VGT_PRIMITIVE_TYPE 0x8958 +#define VGT_NUM_INSTANCES 0x8974 +#define VGT_TF_RING_SIZE 0x8988 +#define VGT_OFFCHIP_LDS_BASE 0x89b4 + +#define PA_SC_LINE_STIPPLE_STATE 0x8B10 +#define PA_CL_ENHANCE 0x8A14 +#define CLIP_VTX_REORDER_ENA (1 << 0) +#define NUM_CLIP_SEQ(x) ((x) << 1) +#define PA_SC_FIFO_SIZE 0x8BCC +#define SC_PRIM_FIFO_SIZE(x) ((x) << 0) +#define SC_HIZ_TILE_FIFO_SIZE(x) ((x) << 12) +#define SC_EARLYZ_TILE_FIFO_SIZE(x) ((x) << 20) +#define PA_SC_FORCE_EOV_MAX_CNTS 0x8B24 +#define FORCE_EOV_MAX_CLK_CNT(x) ((x) << 0) +#define FORCE_EOV_MAX_REZ_CNT(x) ((x) << 16) + +#define SQ_CONFIG 0x8C00 +#define VC_ENABLE (1 << 0) +#define EXPORT_SRC_C (1 << 1) +#define GFX_PRIO(x) ((x) << 2) +#define CS1_PRIO(x) ((x) << 4) +#define CS2_PRIO(x) ((x) << 6) +#define SQ_GPR_RESOURCE_MGMT_1 0x8C04 +#define NUM_PS_GPRS(x) ((x) << 0) +#define NUM_VS_GPRS(x) ((x) << 16) +#define NUM_CLAUSE_TEMP_GPRS(x) ((x) << 28) +#define SQ_ESGS_RING_SIZE 0x8c44 +#define SQ_GSVS_RING_SIZE 0x8c4c +#define SQ_ESTMP_RING_BASE 0x8c50 +#define SQ_ESTMP_RING_SIZE 0x8c54 +#define SQ_GSTMP_RING_BASE 0x8c58 +#define SQ_GSTMP_RING_SIZE 0x8c5c +#define SQ_VSTMP_RING_BASE 0x8c60 +#define SQ_VSTMP_RING_SIZE 0x8c64 +#define SQ_PSTMP_RING_BASE 0x8c68 +#define SQ_PSTMP_RING_SIZE 0x8c6c +#define SQ_MS_FIFO_SIZES 0x8CF0 +#define CACHE_FIFO_SIZE(x) ((x) << 0) +#define FETCH_FIFO_HIWATER(x) ((x) << 8) +#define DONE_FIFO_HIWATER(x) ((x) << 16) +#define ALU_UPDATE_FIFO_HIWATER(x) ((x) << 24) +#define SQ_LSTMP_RING_BASE 0x8e10 +#define SQ_LSTMP_RING_SIZE 0x8e14 +#define SQ_HSTMP_RING_BASE 0x8e18 +#define SQ_HSTMP_RING_SIZE 0x8e1c +#define SQ_DYN_GPR_CNTL_PS_FLUSH_REQ 0x8D8C +#define DYN_GPR_ENABLE (1 << 8) +#define SQ_CONST_MEM_BASE 0x8df8 + +#define SX_EXPORT_BUFFER_SIZES 0x900C +#define COLOR_BUFFER_SIZE(x) ((x) << 0) +#define POSITION_BUFFER_SIZE(x) ((x) << 8) +#define SMX_BUFFER_SIZE(x) ((x) << 16) +#define SX_DEBUG_1 0x9058 +#define ENABLE_NEW_SMX_ADDRESS (1 << 16) + +#define SPI_CONFIG_CNTL 0x9100 +#define GPR_WRITE_PRIORITY(x) ((x) << 0) +#define SPI_CONFIG_CNTL_1 0x913C +#define VTX_DONE_DELAY(x) ((x) << 0) +#define INTERP_ONE_PRIM_PER_ROW (1 << 4) +#define CRC_SIMD_ID_WADDR_DISABLE (1 << 8) + +#define CGTS_TCC_DISABLE 0x9148 +#define CGTS_USER_TCC_DISABLE 0x914C +#define TCC_DISABLE_MASK 0xFFFF0000 +#define TCC_DISABLE_SHIFT 16 +#define CGTS_SM_CTRL_REG 0x9150 +#define OVERRIDE (1 << 21) + +#define TA_CNTL_AUX 0x9508 +#define DISABLE_CUBE_WRAP (1 << 0) +#define DISABLE_CUBE_ANISO (1 << 1) + +#define TCP_CHAN_STEER_LO 0x960c +#define TCP_CHAN_STEER_HI 0x9610 + +#define CC_RB_BACKEND_DISABLE 0x98F4 +#define BACKEND_DISABLE(x) ((x) << 16) +#define GB_ADDR_CONFIG 0x98F8 +#define NUM_PIPES(x) ((x) << 0) +#define NUM_PIPES_MASK 0x00000007 +#define NUM_PIPES_SHIFT 0 +#define PIPE_INTERLEAVE_SIZE(x) ((x) << 4) +#define PIPE_INTERLEAVE_SIZE_MASK 0x00000070 +#define PIPE_INTERLEAVE_SIZE_SHIFT 4 +#define BANK_INTERLEAVE_SIZE(x) ((x) << 8) +#define NUM_SHADER_ENGINES(x) ((x) << 12) +#define NUM_SHADER_ENGINES_MASK 0x00003000 +#define NUM_SHADER_ENGINES_SHIFT 12 +#define SHADER_ENGINE_TILE_SIZE(x) ((x) << 16) +#define SHADER_ENGINE_TILE_SIZE_MASK 0x00070000 +#define SHADER_ENGINE_TILE_SIZE_SHIFT 16 +#define NUM_GPUS(x) ((x) << 20) +#define NUM_GPUS_MASK 0x00700000 +#define NUM_GPUS_SHIFT 20 +#define MULTI_GPU_TILE_SIZE(x) ((x) << 24) +#define MULTI_GPU_TILE_SIZE_MASK 0x03000000 +#define MULTI_GPU_TILE_SIZE_SHIFT 24 +#define ROW_SIZE(x) ((x) << 28) +#define ROW_SIZE_MASK 0x30000000 +#define ROW_SIZE_SHIFT 28 +#define NUM_LOWER_PIPES(x) ((x) << 30) +#define NUM_LOWER_PIPES_MASK 0x40000000 +#define NUM_LOWER_PIPES_SHIFT 30 +#define GB_BACKEND_MAP 0x98FC + +#define CB_PERF_CTR0_SEL_0 0x9A20 +#define CB_PERF_CTR0_SEL_1 0x9A24 +#define CB_PERF_CTR1_SEL_0 0x9A28 +#define CB_PERF_CTR1_SEL_1 0x9A2C +#define CB_PERF_CTR2_SEL_0 0x9A30 +#define CB_PERF_CTR2_SEL_1 0x9A34 +#define CB_PERF_CTR3_SEL_0 0x9A38 +#define CB_PERF_CTR3_SEL_1 0x9A3C + +#define GC_USER_RB_BACKEND_DISABLE 0x9B7C +#define BACKEND_DISABLE_MASK 0x00FF0000 +#define BACKEND_DISABLE_SHIFT 16 + +#define SMX_DC_CTL0 0xA020 +#define USE_HASH_FUNCTION (1 << 0) +#define NUMBER_OF_SETS(x) ((x) << 1) +#define FLUSH_ALL_ON_EVENT (1 << 10) +#define STALL_ON_EVENT (1 << 11) +#define SMX_EVENT_CTL 0xA02C +#define ES_FLUSH_CTL(x) ((x) << 0) +#define GS_FLUSH_CTL(x) ((x) << 3) +#define ACK_FLUSH_CTL(x) ((x) << 6) +#define SYNC_FLUSH_CTL (1 << 8) + +#define CP_RB0_BASE 0xC100 +#define CP_RB0_CNTL 0xC104 +#define RB_BUFSZ(x) ((x) << 0) +#define RB_BLKSZ(x) ((x) << 8) +#define RB_NO_UPDATE (1 << 27) +#define RB_RPTR_WR_ENA (1 << 31) +#define BUF_SWAP_32BIT (2 << 16) +#define CP_RB0_RPTR_ADDR 0xC10C +#define CP_RB0_RPTR_ADDR_HI 0xC110 +#define CP_RB0_WPTR 0xC114 + +#define CP_INT_CNTL 0xC124 +# define CNTX_BUSY_INT_ENABLE (1 << 19) +# define CNTX_EMPTY_INT_ENABLE (1 << 20) +# define TIME_STAMP_INT_ENABLE (1 << 26) + +#define CP_RB1_BASE 0xC180 +#define CP_RB1_CNTL 0xC184 +#define CP_RB1_RPTR_ADDR 0xC188 +#define CP_RB1_RPTR_ADDR_HI 0xC18C +#define CP_RB1_WPTR 0xC190 +#define CP_RB2_BASE 0xC194 +#define CP_RB2_CNTL 0xC198 +#define CP_RB2_RPTR_ADDR 0xC19C +#define CP_RB2_RPTR_ADDR_HI 0xC1A0 +#define CP_RB2_WPTR 0xC1A4 +#define CP_PFP_UCODE_ADDR 0xC150 +#define CP_PFP_UCODE_DATA 0xC154 +#define CP_ME_RAM_RADDR 0xC158 +#define CP_ME_RAM_WADDR 0xC15C +#define CP_ME_RAM_DATA 0xC160 +#define CP_DEBUG 0xC1FC + +#define VGT_EVENT_INITIATOR 0x28a90 +# define CACHE_FLUSH_AND_INV_EVENT_TS (0x14 << 0) +# define CACHE_FLUSH_AND_INV_EVENT (0x16 << 0) + +/* + * PM4 + */ +#define PACKET_TYPE0 0 +#define PACKET_TYPE1 1 +#define PACKET_TYPE2 2 +#define PACKET_TYPE3 3 + +#define CP_PACKET_GET_TYPE(h) (((h) >> 30) & 3) +#define CP_PACKET_GET_COUNT(h) (((h) >> 16) & 0x3FFF) +#define CP_PACKET0_GET_REG(h) (((h) & 0xFFFF) << 2) +#define CP_PACKET3_GET_OPCODE(h) (((h) >> 8) & 0xFF) +#define PACKET0(reg, n) ((PACKET_TYPE0 << 30) | \ + (((reg) >> 2) & 0xFFFF) | \ + ((n) & 0x3FFF) << 16) +#define CP_PACKET2 0x80000000 +#define PACKET2_PAD_SHIFT 0 +#define PACKET2_PAD_MASK (0x3fffffff << 0) + +#define PACKET2(v) (CP_PACKET2 | REG_SET(PACKET2_PAD, (v))) + +#define PACKET3(op, n) ((PACKET_TYPE3 << 30) | \ + (((op) & 0xFF) << 8) | \ + ((n) & 0x3FFF) << 16) + +/* Packet 3 types */ +#define PACKET3_NOP 0x10 +#define PACKET3_SET_BASE 0x11 +#define PACKET3_CLEAR_STATE 0x12 +#define PACKET3_INDEX_BUFFER_SIZE 0x13 +#define PACKET3_DEALLOC_STATE 0x14 +#define PACKET3_DISPATCH_DIRECT 0x15 +#define PACKET3_DISPATCH_INDIRECT 0x16 +#define PACKET3_INDIRECT_BUFFER_END 0x17 +#define PACKET3_MODE_CONTROL 0x18 +#define PACKET3_SET_PREDICATION 0x20 +#define PACKET3_REG_RMW 0x21 +#define PACKET3_COND_EXEC 0x22 +#define PACKET3_PRED_EXEC 0x23 +#define PACKET3_DRAW_INDIRECT 0x24 +#define PACKET3_DRAW_INDEX_INDIRECT 0x25 +#define PACKET3_INDEX_BASE 0x26 +#define PACKET3_DRAW_INDEX_2 0x27 +#define PACKET3_CONTEXT_CONTROL 0x28 +#define PACKET3_DRAW_INDEX_OFFSET 0x29 +#define PACKET3_INDEX_TYPE 0x2A +#define PACKET3_DRAW_INDEX 0x2B +#define PACKET3_DRAW_INDEX_AUTO 0x2D +#define PACKET3_DRAW_INDEX_IMMD 0x2E +#define PACKET3_NUM_INSTANCES 0x2F +#define PACKET3_DRAW_INDEX_MULTI_AUTO 0x30 +#define PACKET3_INDIRECT_BUFFER 0x32 +#define PACKET3_STRMOUT_BUFFER_UPDATE 0x34 +#define PACKET3_DRAW_INDEX_OFFSET_2 0x35 +#define PACKET3_DRAW_INDEX_MULTI_ELEMENT 0x36 +#define PACKET3_WRITE_DATA 0x37 +#define PACKET3_MEM_SEMAPHORE 0x39 +#define PACKET3_MPEG_INDEX 0x3A +#define PACKET3_WAIT_REG_MEM 0x3C +#define PACKET3_MEM_WRITE 0x3D +#define PACKET3_PFP_SYNC_ME 0x42 +#define PACKET3_SURFACE_SYNC 0x43 +# define PACKET3_CB0_DEST_BASE_ENA (1 << 6) +# define PACKET3_CB1_DEST_BASE_ENA (1 << 7) +# define PACKET3_CB2_DEST_BASE_ENA (1 << 8) +# define PACKET3_CB3_DEST_BASE_ENA (1 << 9) +# define PACKET3_CB4_DEST_BASE_ENA (1 << 10) +# define PACKET3_CB5_DEST_BASE_ENA (1 << 11) +# define PACKET3_CB6_DEST_BASE_ENA (1 << 12) +# define PACKET3_CB7_DEST_BASE_ENA (1 << 13) +# define PACKET3_DB_DEST_BASE_ENA (1 << 14) +# define PACKET3_CB8_DEST_BASE_ENA (1 << 15) +# define PACKET3_CB9_DEST_BASE_ENA (1 << 16) +# define PACKET3_CB10_DEST_BASE_ENA (1 << 17) +# define PACKET3_CB11_DEST_BASE_ENA (1 << 18) +# define PACKET3_FULL_CACHE_ENA (1 << 20) +# define PACKET3_TC_ACTION_ENA (1 << 23) +# define PACKET3_CB_ACTION_ENA (1 << 25) +# define PACKET3_DB_ACTION_ENA (1 << 26) +# define PACKET3_SH_ACTION_ENA (1 << 27) +# define PACKET3_SX_ACTION_ENA (1 << 28) +#define PACKET3_ME_INITIALIZE 0x44 +#define PACKET3_ME_INITIALIZE_DEVICE_ID(x) ((x) << 16) +#define PACKET3_COND_WRITE 0x45 +#define PACKET3_EVENT_WRITE 0x46 +#define EVENT_TYPE(x) ((x) << 0) +#define EVENT_INDEX(x) ((x) << 8) + /* 0 - any non-TS event + * 1 - ZPASS_DONE + * 2 - SAMPLE_PIPELINESTAT + * 3 - SAMPLE_STREAMOUTSTAT* + * 4 - *S_PARTIAL_FLUSH + * 5 - TS events + */ +#define PACKET3_EVENT_WRITE_EOP 0x47 +#define DATA_SEL(x) ((x) << 29) + /* 0 - discard + * 1 - send low 32bit data + * 2 - send 64bit data + * 3 - send 64bit counter value + */ +#define INT_SEL(x) ((x) << 24) + /* 0 - none + * 1 - interrupt only (DATA_SEL = 0) + * 2 - interrupt when data write is confirmed + */ +#define PACKET3_EVENT_WRITE_EOS 0x48 +#define PACKET3_PREAMBLE_CNTL 0x4A +# define PACKET3_PREAMBLE_BEGIN_CLEAR_STATE (2 << 28) +# define PACKET3_PREAMBLE_END_CLEAR_STATE (3 << 28) +#define PACKET3_ALU_PS_CONST_BUFFER_COPY 0x4C +#define PACKET3_ALU_VS_CONST_BUFFER_COPY 0x4D +#define PACKET3_ALU_PS_CONST_UPDATE 0x4E +#define PACKET3_ALU_VS_CONST_UPDATE 0x4F +#define PACKET3_ONE_REG_WRITE 0x57 +#define PACKET3_SET_CONFIG_REG 0x68 +#define PACKET3_SET_CONFIG_REG_START 0x00008000 +#define PACKET3_SET_CONFIG_REG_END 0x0000ac00 +#define PACKET3_SET_CONTEXT_REG 0x69 +#define PACKET3_SET_CONTEXT_REG_START 0x00028000 +#define PACKET3_SET_CONTEXT_REG_END 0x00029000 +#define PACKET3_SET_ALU_CONST 0x6A +/* alu const buffers only; no reg file */ +#define PACKET3_SET_BOOL_CONST 0x6B +#define PACKET3_SET_BOOL_CONST_START 0x0003a500 +#define PACKET3_SET_BOOL_CONST_END 0x0003a518 +#define PACKET3_SET_LOOP_CONST 0x6C +#define PACKET3_SET_LOOP_CONST_START 0x0003a200 +#define PACKET3_SET_LOOP_CONST_END 0x0003a500 +#define PACKET3_SET_RESOURCE 0x6D +#define PACKET3_SET_RESOURCE_START 0x00030000 +#define PACKET3_SET_RESOURCE_END 0x00038000 +#define PACKET3_SET_SAMPLER 0x6E +#define PACKET3_SET_SAMPLER_START 0x0003c000 +#define PACKET3_SET_SAMPLER_END 0x0003c600 +#define PACKET3_SET_CTL_CONST 0x6F +#define PACKET3_SET_CTL_CONST_START 0x0003cff0 +#define PACKET3_SET_CTL_CONST_END 0x0003ff0c +#define PACKET3_SET_RESOURCE_OFFSET 0x70 +#define PACKET3_SET_ALU_CONST_VS 0x71 +#define PACKET3_SET_ALU_CONST_DI 0x72 +#define PACKET3_SET_CONTEXT_REG_INDIRECT 0x73 +#define PACKET3_SET_RESOURCE_INDIRECT 0x74 +#define PACKET3_SET_APPEND_CNT 0x75 +#define PACKET3_ME_WRITE 0x7A + +/* ASYNC DMA - first instance at 0xd000, second at 0xd800 */ +#define DMA0_REGISTER_OFFSET 0x0 /* not a register */ +#define DMA1_REGISTER_OFFSET 0x800 /* not a register */ + +#define DMA_RB_CNTL 0xd000 +# define DMA_RB_ENABLE (1 << 0) +# define DMA_RB_SIZE(x) ((x) << 1) /* log2 */ +# define DMA_RB_SWAP_ENABLE (1 << 9) /* 8IN32 */ +# define DMA_RPTR_WRITEBACK_ENABLE (1 << 12) +# define DMA_RPTR_WRITEBACK_SWAP_ENABLE (1 << 13) /* 8IN32 */ +# define DMA_RPTR_WRITEBACK_TIMER(x) ((x) << 16) /* log2 */ +#define DMA_RB_BASE 0xd004 +#define DMA_RB_RPTR 0xd008 +#define DMA_RB_WPTR 0xd00c + +#define DMA_RB_RPTR_ADDR_HI 0xd01c +#define DMA_RB_RPTR_ADDR_LO 0xd020 + +#define DMA_IB_CNTL 0xd024 +# define DMA_IB_ENABLE (1 << 0) +# define DMA_IB_SWAP_ENABLE (1 << 4) +# define CMD_VMID_FORCE (1 << 31) +#define DMA_IB_RPTR 0xd028 +#define DMA_CNTL 0xd02c +# define TRAP_ENABLE (1 << 0) +# define SEM_INCOMPLETE_INT_ENABLE (1 << 1) +# define SEM_WAIT_INT_ENABLE (1 << 2) +# define DATA_SWAP_ENABLE (1 << 3) +# define FENCE_SWAP_ENABLE (1 << 4) +# define CTXEMPTY_INT_ENABLE (1 << 28) +#define DMA_STATUS_REG 0xd034 +# define DMA_IDLE (1 << 0) +#define DMA_SEM_INCOMPLETE_TIMER_CNTL 0xd044 +#define DMA_SEM_WAIT_FAIL_TIMER_CNTL 0xd048 +#define DMA_TILING_CONFIG 0xd0b8 +#define DMA_MODE 0xd0bc + +#define DMA_PACKET(cmd, t, s, n) ((((cmd) & 0xF) << 28) | \ + (((t) & 0x1) << 23) | \ + (((s) & 0x1) << 22) | \ + (((n) & 0xFFFFF) << 0)) + +#define DMA_IB_PACKET(cmd, vmid, n) ((((cmd) & 0xF) << 28) | \ + (((vmid) & 0xF) << 20) | \ + (((n) & 0xFFFFF) << 0)) + +/* async DMA Packet types */ +#define DMA_PACKET_WRITE 0x2 +#define DMA_PACKET_COPY 0x3 +#define DMA_PACKET_INDIRECT_BUFFER 0x4 +#define DMA_PACKET_SEMAPHORE 0x5 +#define DMA_PACKET_FENCE 0x6 +#define DMA_PACKET_TRAP 0x7 +#define DMA_PACKET_SRBM_WRITE 0x9 +#define DMA_PACKET_CONSTANT_FILL 0xd +#define DMA_PACKET_NOP 0xf + +#endif diff --git a/sys/dev/drm2/radeon/r100.c b/sys/dev/drm2/radeon/r100.c new file mode 100644 index 00000000000..a66c7c2418b --- /dev/null +++ b/sys/dev/drm2/radeon/r100.c @@ -0,0 +1,4201 @@ +/* + * Copyright 2008 Advanced Micro Devices, Inc. + * Copyright 2008 Red Hat Inc. + * Copyright 2009 Jerome Glisse. + * + * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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. + * + * Authors: Dave Airlie + * Alex Deucher + * Jerome Glisse + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include "radeon_reg.h" +#include "radeon.h" +#include "radeon_asic.h" +#include "r100d.h" +#include "rs100d.h" +#include "rv200d.h" +#include "rv250d.h" +#include "atom.h" + +#include "r100_reg_safe.h" +#include "rn50_reg_safe.h" + +/* Firmware Names */ +#define FIRMWARE_R100 "radeonkmsfw_R100_cp" +#define FIRMWARE_R200 "radeonkmsfw_R200_cp" +#define FIRMWARE_R300 "radeonkmsfw_R300_cp" +#define FIRMWARE_R420 "radeonkmsfw_R420_cp" +#define FIRMWARE_RS690 "radeonkmsfw_RS690_cp" +#define FIRMWARE_RS600 "radeonkmsfw_RS600_cp" +#define FIRMWARE_R520 "radeonkmsfw_R520_cp" + +#include "r100_track.h" + +/* This files gather functions specifics to: + * r100,rv100,rs100,rv200,rs200,r200,rv250,rs300,rv280 + * and others in some cases. + */ + +/** + * r100_wait_for_vblank - vblank wait asic callback. + * + * @rdev: radeon_device pointer + * @crtc: crtc to wait for vblank on + * + * Wait for vblank on the requested crtc (r1xx-r4xx). + */ +void r100_wait_for_vblank(struct radeon_device *rdev, int crtc) +{ + int i; + + if (crtc >= rdev->num_crtc) + return; + + if (crtc == 0) { + if (RREG32(RADEON_CRTC_GEN_CNTL) & RADEON_CRTC_EN) { + for (i = 0; i < rdev->usec_timeout; i++) { + if (!(RREG32(RADEON_CRTC_STATUS) & RADEON_CRTC_VBLANK_CUR)) + break; + DRM_UDELAY(1); + } + for (i = 0; i < rdev->usec_timeout; i++) { + if (RREG32(RADEON_CRTC_STATUS) & RADEON_CRTC_VBLANK_CUR) + break; + DRM_UDELAY(1); + } + } + } else { + if (RREG32(RADEON_CRTC2_GEN_CNTL) & RADEON_CRTC2_EN) { + for (i = 0; i < rdev->usec_timeout; i++) { + if (!(RREG32(RADEON_CRTC2_STATUS) & RADEON_CRTC2_VBLANK_CUR)) + break; + DRM_UDELAY(1); + } + for (i = 0; i < rdev->usec_timeout; i++) { + if (RREG32(RADEON_CRTC2_STATUS) & RADEON_CRTC2_VBLANK_CUR) + break; + DRM_UDELAY(1); + } + } + } +} + +/** + * r100_pre_page_flip - pre-pageflip callback. + * + * @rdev: radeon_device pointer + * @crtc: crtc to prepare for pageflip on + * + * Pre-pageflip callback (r1xx-r4xx). + * Enables the pageflip irq (vblank irq). + */ +void r100_pre_page_flip(struct radeon_device *rdev, int crtc) +{ + /* enable the pflip int */ + radeon_irq_kms_pflip_irq_get(rdev, crtc); +} + +/** + * r100_post_page_flip - pos-pageflip callback. + * + * @rdev: radeon_device pointer + * @crtc: crtc to cleanup pageflip on + * + * Post-pageflip callback (r1xx-r4xx). + * Disables the pageflip irq (vblank irq). + */ +void r100_post_page_flip(struct radeon_device *rdev, int crtc) +{ + /* disable the pflip int */ + radeon_irq_kms_pflip_irq_put(rdev, crtc); +} + +/** + * r100_page_flip - pageflip callback. + * + * @rdev: radeon_device pointer + * @crtc_id: crtc to cleanup pageflip on + * @crtc_base: new address of the crtc (GPU MC address) + * + * Does the actual pageflip (r1xx-r4xx). + * During vblank we take the crtc lock and wait for the update_pending + * bit to go high, when it does, we release the lock, and allow the + * double buffered update to take place. + * Returns the current update pending status. + */ +u32 r100_page_flip(struct radeon_device *rdev, int crtc_id, u64 crtc_base) +{ + struct radeon_crtc *radeon_crtc = rdev->mode_info.crtcs[crtc_id]; + u32 tmp = ((u32)crtc_base) | RADEON_CRTC_OFFSET__OFFSET_LOCK; + int i; + + /* Lock the graphics update lock */ + /* update the scanout addresses */ + WREG32(RADEON_CRTC_OFFSET + radeon_crtc->crtc_offset, tmp); + + /* Wait for update_pending to go high. */ + for (i = 0; i < rdev->usec_timeout; i++) { + if (RREG32(RADEON_CRTC_OFFSET + radeon_crtc->crtc_offset) & RADEON_CRTC_OFFSET__GUI_TRIG_OFFSET) + break; + DRM_UDELAY(1); + } + DRM_DEBUG("Update pending now high. Unlocking vupdate_lock.\n"); + + /* Unlock the lock, so double-buffering can take place inside vblank */ + tmp &= ~RADEON_CRTC_OFFSET__OFFSET_LOCK; + WREG32(RADEON_CRTC_OFFSET + radeon_crtc->crtc_offset, tmp); + + /* Return current update_pending status: */ + return RREG32(RADEON_CRTC_OFFSET + radeon_crtc->crtc_offset) & RADEON_CRTC_OFFSET__GUI_TRIG_OFFSET; +} + +/** + * r100_pm_get_dynpm_state - look up dynpm power state callback. + * + * @rdev: radeon_device pointer + * + * Look up the optimal power state based on the + * current state of the GPU (r1xx-r5xx). + * Used for dynpm only. + */ +void r100_pm_get_dynpm_state(struct radeon_device *rdev) +{ + int i; + rdev->pm.dynpm_can_upclock = true; + rdev->pm.dynpm_can_downclock = true; + + switch (rdev->pm.dynpm_planned_action) { + case DYNPM_ACTION_MINIMUM: + rdev->pm.requested_power_state_index = 0; + rdev->pm.dynpm_can_downclock = false; + break; + case DYNPM_ACTION_DOWNCLOCK: + if (rdev->pm.current_power_state_index == 0) { + rdev->pm.requested_power_state_index = rdev->pm.current_power_state_index; + rdev->pm.dynpm_can_downclock = false; + } else { + if (rdev->pm.active_crtc_count > 1) { + for (i = 0; i < rdev->pm.num_power_states; i++) { + if (rdev->pm.power_state[i].flags & RADEON_PM_STATE_SINGLE_DISPLAY_ONLY) + continue; + else if (i >= rdev->pm.current_power_state_index) { + rdev->pm.requested_power_state_index = rdev->pm.current_power_state_index; + break; + } else { + rdev->pm.requested_power_state_index = i; + break; + } + } + } else + rdev->pm.requested_power_state_index = + rdev->pm.current_power_state_index - 1; + } + /* don't use the power state if crtcs are active and no display flag is set */ + if ((rdev->pm.active_crtc_count > 0) && + (rdev->pm.power_state[rdev->pm.requested_power_state_index].clock_info[0].flags & + RADEON_PM_MODE_NO_DISPLAY)) { + rdev->pm.requested_power_state_index++; + } + break; + case DYNPM_ACTION_UPCLOCK: + if (rdev->pm.current_power_state_index == (rdev->pm.num_power_states - 1)) { + rdev->pm.requested_power_state_index = rdev->pm.current_power_state_index; + rdev->pm.dynpm_can_upclock = false; + } else { + if (rdev->pm.active_crtc_count > 1) { + for (i = (rdev->pm.num_power_states - 1); i >= 0; i--) { + if (rdev->pm.power_state[i].flags & RADEON_PM_STATE_SINGLE_DISPLAY_ONLY) + continue; + else if (i <= rdev->pm.current_power_state_index) { + rdev->pm.requested_power_state_index = rdev->pm.current_power_state_index; + break; + } else { + rdev->pm.requested_power_state_index = i; + break; + } + } + } else + rdev->pm.requested_power_state_index = + rdev->pm.current_power_state_index + 1; + } + break; + case DYNPM_ACTION_DEFAULT: + rdev->pm.requested_power_state_index = rdev->pm.default_power_state_index; + rdev->pm.dynpm_can_upclock = false; + break; + case DYNPM_ACTION_NONE: + default: + DRM_ERROR("Requested mode for not defined action\n"); + return; + } + /* only one clock mode per power state */ + rdev->pm.requested_clock_mode_index = 0; + + DRM_DEBUG_DRIVER("Requested: e: %d m: %d p: %d\n", + rdev->pm.power_state[rdev->pm.requested_power_state_index]. + clock_info[rdev->pm.requested_clock_mode_index].sclk, + rdev->pm.power_state[rdev->pm.requested_power_state_index]. + clock_info[rdev->pm.requested_clock_mode_index].mclk, + rdev->pm.power_state[rdev->pm.requested_power_state_index]. + pcie_lanes); +} + +/** + * r100_pm_init_profile - Initialize power profiles callback. + * + * @rdev: radeon_device pointer + * + * Initialize the power states used in profile mode + * (r1xx-r3xx). + * Used for profile mode only. + */ +void r100_pm_init_profile(struct radeon_device *rdev) +{ + /* default */ + rdev->pm.profiles[PM_PROFILE_DEFAULT_IDX].dpms_off_ps_idx = rdev->pm.default_power_state_index; + rdev->pm.profiles[PM_PROFILE_DEFAULT_IDX].dpms_on_ps_idx = rdev->pm.default_power_state_index; + rdev->pm.profiles[PM_PROFILE_DEFAULT_IDX].dpms_off_cm_idx = 0; + rdev->pm.profiles[PM_PROFILE_DEFAULT_IDX].dpms_on_cm_idx = 0; + /* low sh */ + rdev->pm.profiles[PM_PROFILE_LOW_SH_IDX].dpms_off_ps_idx = 0; + rdev->pm.profiles[PM_PROFILE_LOW_SH_IDX].dpms_on_ps_idx = 0; + rdev->pm.profiles[PM_PROFILE_LOW_SH_IDX].dpms_off_cm_idx = 0; + rdev->pm.profiles[PM_PROFILE_LOW_SH_IDX].dpms_on_cm_idx = 0; + /* mid sh */ + rdev->pm.profiles[PM_PROFILE_MID_SH_IDX].dpms_off_ps_idx = 0; + rdev->pm.profiles[PM_PROFILE_MID_SH_IDX].dpms_on_ps_idx = 0; + rdev->pm.profiles[PM_PROFILE_MID_SH_IDX].dpms_off_cm_idx = 0; + rdev->pm.profiles[PM_PROFILE_MID_SH_IDX].dpms_on_cm_idx = 0; + /* high sh */ + rdev->pm.profiles[PM_PROFILE_HIGH_SH_IDX].dpms_off_ps_idx = 0; + rdev->pm.profiles[PM_PROFILE_HIGH_SH_IDX].dpms_on_ps_idx = rdev->pm.default_power_state_index; + rdev->pm.profiles[PM_PROFILE_HIGH_SH_IDX].dpms_off_cm_idx = 0; + rdev->pm.profiles[PM_PROFILE_HIGH_SH_IDX].dpms_on_cm_idx = 0; + /* low mh */ + rdev->pm.profiles[PM_PROFILE_LOW_MH_IDX].dpms_off_ps_idx = 0; + rdev->pm.profiles[PM_PROFILE_LOW_MH_IDX].dpms_on_ps_idx = rdev->pm.default_power_state_index; + rdev->pm.profiles[PM_PROFILE_LOW_MH_IDX].dpms_off_cm_idx = 0; + rdev->pm.profiles[PM_PROFILE_LOW_MH_IDX].dpms_on_cm_idx = 0; + /* mid mh */ + rdev->pm.profiles[PM_PROFILE_MID_MH_IDX].dpms_off_ps_idx = 0; + rdev->pm.profiles[PM_PROFILE_MID_MH_IDX].dpms_on_ps_idx = rdev->pm.default_power_state_index; + rdev->pm.profiles[PM_PROFILE_MID_MH_IDX].dpms_off_cm_idx = 0; + rdev->pm.profiles[PM_PROFILE_MID_MH_IDX].dpms_on_cm_idx = 0; + /* high mh */ + rdev->pm.profiles[PM_PROFILE_HIGH_MH_IDX].dpms_off_ps_idx = 0; + rdev->pm.profiles[PM_PROFILE_HIGH_MH_IDX].dpms_on_ps_idx = rdev->pm.default_power_state_index; + rdev->pm.profiles[PM_PROFILE_HIGH_MH_IDX].dpms_off_cm_idx = 0; + rdev->pm.profiles[PM_PROFILE_HIGH_MH_IDX].dpms_on_cm_idx = 0; +} + +/** + * r100_pm_misc - set additional pm hw parameters callback. + * + * @rdev: radeon_device pointer + * + * Set non-clock parameters associated with a power state + * (voltage, pcie lanes, etc.) (r1xx-r4xx). + */ +void r100_pm_misc(struct radeon_device *rdev) +{ + int requested_index = rdev->pm.requested_power_state_index; + struct radeon_power_state *ps = &rdev->pm.power_state[requested_index]; + struct radeon_voltage *voltage = &ps->clock_info[0].voltage; + u32 tmp, sclk_cntl, sclk_cntl2, sclk_more_cntl; + + if ((voltage->type == VOLTAGE_GPIO) && (voltage->gpio.valid)) { + if (ps->misc & ATOM_PM_MISCINFO_VOLTAGE_DROP_SUPPORT) { + tmp = RREG32(voltage->gpio.reg); + if (voltage->active_high) + tmp |= voltage->gpio.mask; + else + tmp &= ~(voltage->gpio.mask); + WREG32(voltage->gpio.reg, tmp); + if (voltage->delay) + DRM_UDELAY(voltage->delay); + } else { + tmp = RREG32(voltage->gpio.reg); + if (voltage->active_high) + tmp &= ~voltage->gpio.mask; + else + tmp |= voltage->gpio.mask; + WREG32(voltage->gpio.reg, tmp); + if (voltage->delay) + DRM_UDELAY(voltage->delay); + } + } + + sclk_cntl = RREG32_PLL(SCLK_CNTL); + sclk_cntl2 = RREG32_PLL(SCLK_CNTL2); + sclk_cntl2 &= ~REDUCED_SPEED_SCLK_SEL(3); + sclk_more_cntl = RREG32_PLL(SCLK_MORE_CNTL); + sclk_more_cntl &= ~VOLTAGE_DELAY_SEL(3); + if (ps->misc & ATOM_PM_MISCINFO_ASIC_REDUCED_SPEED_SCLK_EN) { + sclk_more_cntl |= REDUCED_SPEED_SCLK_EN; + if (ps->misc & ATOM_PM_MISCINFO_DYN_CLK_3D_IDLE) + sclk_cntl2 |= REDUCED_SPEED_SCLK_MODE; + else + sclk_cntl2 &= ~REDUCED_SPEED_SCLK_MODE; + if (ps->misc & ATOM_PM_MISCINFO_DYNAMIC_CLOCK_DIVIDER_BY_2) + sclk_cntl2 |= REDUCED_SPEED_SCLK_SEL(0); + else if (ps->misc & ATOM_PM_MISCINFO_DYNAMIC_CLOCK_DIVIDER_BY_4) + sclk_cntl2 |= REDUCED_SPEED_SCLK_SEL(2); + } else + sclk_more_cntl &= ~REDUCED_SPEED_SCLK_EN; + + if (ps->misc & ATOM_PM_MISCINFO_ASIC_DYNAMIC_VOLTAGE_EN) { + sclk_more_cntl |= IO_CG_VOLTAGE_DROP; + if (voltage->delay) { + sclk_more_cntl |= VOLTAGE_DROP_SYNC; + switch (voltage->delay) { + case 33: + sclk_more_cntl |= VOLTAGE_DELAY_SEL(0); + break; + case 66: + sclk_more_cntl |= VOLTAGE_DELAY_SEL(1); + break; + case 99: + sclk_more_cntl |= VOLTAGE_DELAY_SEL(2); + break; + case 132: + sclk_more_cntl |= VOLTAGE_DELAY_SEL(3); + break; + } + } else + sclk_more_cntl &= ~VOLTAGE_DROP_SYNC; + } else + sclk_more_cntl &= ~IO_CG_VOLTAGE_DROP; + + if (ps->misc & ATOM_PM_MISCINFO_DYNAMIC_HDP_BLOCK_EN) + sclk_cntl &= ~FORCE_HDP; + else + sclk_cntl |= FORCE_HDP; + + WREG32_PLL(SCLK_CNTL, sclk_cntl); + WREG32_PLL(SCLK_CNTL2, sclk_cntl2); + WREG32_PLL(SCLK_MORE_CNTL, sclk_more_cntl); + + /* set pcie lanes */ + if ((rdev->flags & RADEON_IS_PCIE) && + !(rdev->flags & RADEON_IS_IGP) && + rdev->asic->pm.set_pcie_lanes && + (ps->pcie_lanes != + rdev->pm.power_state[rdev->pm.current_power_state_index].pcie_lanes)) { + radeon_set_pcie_lanes(rdev, + ps->pcie_lanes); + DRM_DEBUG_DRIVER("Setting: p: %d\n", ps->pcie_lanes); + } +} + +/** + * r100_pm_prepare - pre-power state change callback. + * + * @rdev: radeon_device pointer + * + * Prepare for a power state change (r1xx-r4xx). + */ +void r100_pm_prepare(struct radeon_device *rdev) +{ + struct drm_device *ddev = rdev->ddev; + struct drm_crtc *crtc; + struct radeon_crtc *radeon_crtc; + u32 tmp; + + /* disable any active CRTCs */ + list_for_each_entry(crtc, &ddev->mode_config.crtc_list, head) { + radeon_crtc = to_radeon_crtc(crtc); + if (radeon_crtc->enabled) { + if (radeon_crtc->crtc_id) { + tmp = RREG32(RADEON_CRTC2_GEN_CNTL); + tmp |= RADEON_CRTC2_DISP_REQ_EN_B; + WREG32(RADEON_CRTC2_GEN_CNTL, tmp); + } else { + tmp = RREG32(RADEON_CRTC_GEN_CNTL); + tmp |= RADEON_CRTC_DISP_REQ_EN_B; + WREG32(RADEON_CRTC_GEN_CNTL, tmp); + } + } + } +} + +/** + * r100_pm_finish - post-power state change callback. + * + * @rdev: radeon_device pointer + * + * Clean up after a power state change (r1xx-r4xx). + */ +void r100_pm_finish(struct radeon_device *rdev) +{ + struct drm_device *ddev = rdev->ddev; + struct drm_crtc *crtc; + struct radeon_crtc *radeon_crtc; + u32 tmp; + + /* enable any active CRTCs */ + list_for_each_entry(crtc, &ddev->mode_config.crtc_list, head) { + radeon_crtc = to_radeon_crtc(crtc); + if (radeon_crtc->enabled) { + if (radeon_crtc->crtc_id) { + tmp = RREG32(RADEON_CRTC2_GEN_CNTL); + tmp &= ~RADEON_CRTC2_DISP_REQ_EN_B; + WREG32(RADEON_CRTC2_GEN_CNTL, tmp); + } else { + tmp = RREG32(RADEON_CRTC_GEN_CNTL); + tmp &= ~RADEON_CRTC_DISP_REQ_EN_B; + WREG32(RADEON_CRTC_GEN_CNTL, tmp); + } + } + } +} + +/** + * r100_gui_idle - gui idle callback. + * + * @rdev: radeon_device pointer + * + * Check of the GUI (2D/3D engines) are idle (r1xx-r5xx). + * Returns true if idle, false if not. + */ +bool r100_gui_idle(struct radeon_device *rdev) +{ + if (RREG32(RADEON_RBBM_STATUS) & RADEON_RBBM_ACTIVE) + return false; + else + return true; +} + +/* hpd for digital panel detect/disconnect */ +/** + * r100_hpd_sense - hpd sense callback. + * + * @rdev: radeon_device pointer + * @hpd: hpd (hotplug detect) pin + * + * Checks if a digital monitor is connected (r1xx-r4xx). + * Returns true if connected, false if not connected. + */ +bool r100_hpd_sense(struct radeon_device *rdev, enum radeon_hpd_id hpd) +{ + bool connected = false; + + switch (hpd) { + case RADEON_HPD_1: + if (RREG32(RADEON_FP_GEN_CNTL) & RADEON_FP_DETECT_SENSE) + connected = true; + break; + case RADEON_HPD_2: + if (RREG32(RADEON_FP2_GEN_CNTL) & RADEON_FP2_DETECT_SENSE) + connected = true; + break; + default: + break; + } + return connected; +} + +/** + * r100_hpd_set_polarity - hpd set polarity callback. + * + * @rdev: radeon_device pointer + * @hpd: hpd (hotplug detect) pin + * + * Set the polarity of the hpd pin (r1xx-r4xx). + */ +void r100_hpd_set_polarity(struct radeon_device *rdev, + enum radeon_hpd_id hpd) +{ + u32 tmp; + bool connected = r100_hpd_sense(rdev, hpd); + + switch (hpd) { + case RADEON_HPD_1: + tmp = RREG32(RADEON_FP_GEN_CNTL); + if (connected) + tmp &= ~RADEON_FP_DETECT_INT_POL; + else + tmp |= RADEON_FP_DETECT_INT_POL; + WREG32(RADEON_FP_GEN_CNTL, tmp); + break; + case RADEON_HPD_2: + tmp = RREG32(RADEON_FP2_GEN_CNTL); + if (connected) + tmp &= ~RADEON_FP2_DETECT_INT_POL; + else + tmp |= RADEON_FP2_DETECT_INT_POL; + WREG32(RADEON_FP2_GEN_CNTL, tmp); + break; + default: + break; + } +} + +/** + * r100_hpd_init - hpd setup callback. + * + * @rdev: radeon_device pointer + * + * Setup the hpd pins used by the card (r1xx-r4xx). + * Set the polarity, and enable the hpd interrupts. + */ +void r100_hpd_init(struct radeon_device *rdev) +{ + struct drm_device *dev = rdev->ddev; + struct drm_connector *connector; + unsigned enable = 0; + + list_for_each_entry(connector, &dev->mode_config.connector_list, head) { + struct radeon_connector *radeon_connector = to_radeon_connector(connector); + enable |= 1 << radeon_connector->hpd.hpd; + radeon_hpd_set_polarity(rdev, radeon_connector->hpd.hpd); + } + radeon_irq_kms_enable_hpd(rdev, enable); +} + +/** + * r100_hpd_fini - hpd tear down callback. + * + * @rdev: radeon_device pointer + * + * Tear down the hpd pins used by the card (r1xx-r4xx). + * Disable the hpd interrupts. + */ +void r100_hpd_fini(struct radeon_device *rdev) +{ + struct drm_device *dev = rdev->ddev; + struct drm_connector *connector; + unsigned disable = 0; + + list_for_each_entry(connector, &dev->mode_config.connector_list, head) { + struct radeon_connector *radeon_connector = to_radeon_connector(connector); + disable |= 1 << radeon_connector->hpd.hpd; + } + radeon_irq_kms_disable_hpd(rdev, disable); +} + +/* + * PCI GART + */ +void r100_pci_gart_tlb_flush(struct radeon_device *rdev) +{ + /* TODO: can we do somethings here ? */ + /* It seems hw only cache one entry so we should discard this + * entry otherwise if first GPU GART read hit this entry it + * could end up in wrong address. */ +} + +int r100_pci_gart_init(struct radeon_device *rdev) +{ + int r; + + if (rdev->gart.ptr) { + DRM_ERROR("R100 PCI GART already initialized\n"); + return 0; + } + /* Initialize common gart structure */ + r = radeon_gart_init(rdev); + if (r) + return r; + rdev->gart.table_size = rdev->gart.num_gpu_pages * 4; + rdev->asic->gart.tlb_flush = &r100_pci_gart_tlb_flush; + rdev->asic->gart.set_page = &r100_pci_gart_set_page; + return radeon_gart_table_ram_alloc(rdev); +} + +int r100_pci_gart_enable(struct radeon_device *rdev) +{ + uint32_t tmp; + + radeon_gart_restore(rdev); + /* discard memory request outside of configured range */ + tmp = RREG32(RADEON_AIC_CNTL) | RADEON_DIS_OUT_OF_PCI_GART_ACCESS; + WREG32(RADEON_AIC_CNTL, tmp); + /* set address range for PCI address translate */ + WREG32(RADEON_AIC_LO_ADDR, rdev->mc.gtt_start); + WREG32(RADEON_AIC_HI_ADDR, rdev->mc.gtt_end); + /* set PCI GART page-table base address */ + WREG32(RADEON_AIC_PT_BASE, rdev->gart.table_addr); + tmp = RREG32(RADEON_AIC_CNTL) | RADEON_PCIGART_TRANSLATE_EN; + WREG32(RADEON_AIC_CNTL, tmp); + r100_pci_gart_tlb_flush(rdev); + DRM_INFO("PCI GART of %uM enabled (table at 0x%016llX).\n", + (unsigned)(rdev->mc.gtt_size >> 20), + (unsigned long long)rdev->gart.table_addr); + rdev->gart.ready = true; + return 0; +} + +void r100_pci_gart_disable(struct radeon_device *rdev) +{ + uint32_t tmp; + + /* discard memory request outside of configured range */ + tmp = RREG32(RADEON_AIC_CNTL) | RADEON_DIS_OUT_OF_PCI_GART_ACCESS; + WREG32(RADEON_AIC_CNTL, tmp & ~RADEON_PCIGART_TRANSLATE_EN); + WREG32(RADEON_AIC_LO_ADDR, 0); + WREG32(RADEON_AIC_HI_ADDR, 0); +} + +int r100_pci_gart_set_page(struct radeon_device *rdev, int i, uint64_t addr) +{ + u32 *gtt = rdev->gart.ptr; + + if (i < 0 || i > rdev->gart.num_gpu_pages) { + return -EINVAL; + } + gtt[i] = cpu_to_le32(lower_32_bits(addr)); + return 0; +} + +void r100_pci_gart_fini(struct radeon_device *rdev) +{ + radeon_gart_fini(rdev); + r100_pci_gart_disable(rdev); + radeon_gart_table_ram_free(rdev); +} + +int r100_irq_set(struct radeon_device *rdev) +{ + uint32_t tmp = 0; + + if (!rdev->irq.installed) { + DRM_ERROR("Can't enable IRQ/MSI because no handler is installed\n"); + WREG32(R_000040_GEN_INT_CNTL, 0); + return -EINVAL; + } + if (atomic_read(&rdev->irq.ring_int[RADEON_RING_TYPE_GFX_INDEX])) { + tmp |= RADEON_SW_INT_ENABLE; + } + if (rdev->irq.crtc_vblank_int[0] || + atomic_read(&rdev->irq.pflip[0])) { + tmp |= RADEON_CRTC_VBLANK_MASK; + } + if (rdev->irq.crtc_vblank_int[1] || + atomic_read(&rdev->irq.pflip[1])) { + tmp |= RADEON_CRTC2_VBLANK_MASK; + } + if (rdev->irq.hpd[0]) { + tmp |= RADEON_FP_DETECT_MASK; + } + if (rdev->irq.hpd[1]) { + tmp |= RADEON_FP2_DETECT_MASK; + } + WREG32(RADEON_GEN_INT_CNTL, tmp); + return 0; +} + +void r100_irq_disable(struct radeon_device *rdev) +{ + u32 tmp; + + WREG32(R_000040_GEN_INT_CNTL, 0); + /* Wait and acknowledge irq */ + DRM_MDELAY(1); + tmp = RREG32(R_000044_GEN_INT_STATUS); + WREG32(R_000044_GEN_INT_STATUS, tmp); +} + +static uint32_t r100_irq_ack(struct radeon_device *rdev) +{ + uint32_t irqs = RREG32(RADEON_GEN_INT_STATUS); + uint32_t irq_mask = RADEON_SW_INT_TEST | + RADEON_CRTC_VBLANK_STAT | RADEON_CRTC2_VBLANK_STAT | + RADEON_FP_DETECT_STAT | RADEON_FP2_DETECT_STAT; + + if (irqs) { + WREG32(RADEON_GEN_INT_STATUS, irqs); + } + return irqs & irq_mask; +} + +irqreturn_t r100_irq_process(struct radeon_device *rdev) +{ + uint32_t status, msi_rearm; + bool queue_hotplug = false; + + status = r100_irq_ack(rdev); + if (!status) { + return IRQ_NONE; + } + if (rdev->shutdown) { + return IRQ_NONE; + } + while (status) { + /* SW interrupt */ + if (status & RADEON_SW_INT_TEST) { + radeon_fence_process(rdev, RADEON_RING_TYPE_GFX_INDEX); + } + /* Vertical blank interrupts */ + if (status & RADEON_CRTC_VBLANK_STAT) { + if (rdev->irq.crtc_vblank_int[0]) { + drm_handle_vblank(rdev->ddev, 0); + rdev->pm.vblank_sync = true; + DRM_WAKEUP(&rdev->irq.vblank_queue); + } + if (atomic_read(&rdev->irq.pflip[0])) + radeon_crtc_handle_flip(rdev, 0); + } + if (status & RADEON_CRTC2_VBLANK_STAT) { + if (rdev->irq.crtc_vblank_int[1]) { + drm_handle_vblank(rdev->ddev, 1); + rdev->pm.vblank_sync = true; + DRM_WAKEUP(&rdev->irq.vblank_queue); + } + if (atomic_read(&rdev->irq.pflip[1])) + radeon_crtc_handle_flip(rdev, 1); + } + if (status & RADEON_FP_DETECT_STAT) { + queue_hotplug = true; + DRM_DEBUG("HPD1\n"); + } + if (status & RADEON_FP2_DETECT_STAT) { + queue_hotplug = true; + DRM_DEBUG("HPD2\n"); + } + status = r100_irq_ack(rdev); + } + if (queue_hotplug) + taskqueue_enqueue(rdev->tq, &rdev->hotplug_work); + if (rdev->msi_enabled) { + switch (rdev->family) { + case CHIP_RS400: + case CHIP_RS480: + msi_rearm = RREG32(RADEON_AIC_CNTL) & ~RS400_MSI_REARM; + WREG32(RADEON_AIC_CNTL, msi_rearm); + WREG32(RADEON_AIC_CNTL, msi_rearm | RS400_MSI_REARM); + break; + default: + WREG32(RADEON_MSI_REARM_EN, RV370_MSI_REARM_EN); + break; + } + } + return IRQ_HANDLED; +} + +u32 r100_get_vblank_counter(struct radeon_device *rdev, int crtc) +{ + if (crtc == 0) + return RREG32(RADEON_CRTC_CRNT_FRAME); + else + return RREG32(RADEON_CRTC2_CRNT_FRAME); +} + +/* Who ever call radeon_fence_emit should call ring_lock and ask + * for enough space (today caller are ib schedule and buffer move) */ +void r100_fence_ring_emit(struct radeon_device *rdev, + struct radeon_fence *fence) +{ + struct radeon_ring *ring = &rdev->ring[fence->ring]; + + /* We have to make sure that caches are flushed before + * CPU might read something from VRAM. */ + radeon_ring_write(ring, PACKET0(RADEON_RB3D_DSTCACHE_CTLSTAT, 0)); + radeon_ring_write(ring, RADEON_RB3D_DC_FLUSH_ALL); + radeon_ring_write(ring, PACKET0(RADEON_RB3D_ZCACHE_CTLSTAT, 0)); + radeon_ring_write(ring, RADEON_RB3D_ZC_FLUSH_ALL); + /* Wait until IDLE & CLEAN */ + radeon_ring_write(ring, PACKET0(RADEON_WAIT_UNTIL, 0)); + radeon_ring_write(ring, RADEON_WAIT_2D_IDLECLEAN | RADEON_WAIT_3D_IDLECLEAN); + radeon_ring_write(ring, PACKET0(RADEON_HOST_PATH_CNTL, 0)); + radeon_ring_write(ring, rdev->config.r100.hdp_cntl | + RADEON_HDP_READ_BUFFER_INVALIDATE); + radeon_ring_write(ring, PACKET0(RADEON_HOST_PATH_CNTL, 0)); + radeon_ring_write(ring, rdev->config.r100.hdp_cntl); + /* Emit fence sequence & fire IRQ */ + radeon_ring_write(ring, PACKET0(rdev->fence_drv[fence->ring].scratch_reg, 0)); + radeon_ring_write(ring, fence->seq); + radeon_ring_write(ring, PACKET0(RADEON_GEN_INT_STATUS, 0)); + radeon_ring_write(ring, RADEON_SW_INT_FIRE); +} + +void r100_semaphore_ring_emit(struct radeon_device *rdev, + struct radeon_ring *ring, + struct radeon_semaphore *semaphore, + bool emit_wait) +{ + /* Unused on older asics, since we don't have semaphores or multiple rings */ + panic("%s: Unused on older asics", __func__); +} + +int r100_copy_blit(struct radeon_device *rdev, + uint64_t src_offset, + uint64_t dst_offset, + unsigned num_gpu_pages, + struct radeon_fence **fence) +{ + struct radeon_ring *ring = &rdev->ring[RADEON_RING_TYPE_GFX_INDEX]; + uint32_t cur_pages; + uint32_t stride_bytes = RADEON_GPU_PAGE_SIZE; + uint32_t pitch; + uint32_t stride_pixels; + unsigned ndw; + int num_loops; + int r = 0; + + /* radeon limited to 16k stride */ + stride_bytes &= 0x3fff; + /* radeon pitch is /64 */ + pitch = stride_bytes / 64; + stride_pixels = stride_bytes / 4; + num_loops = DIV_ROUND_UP(num_gpu_pages, 8191); + + /* Ask for enough room for blit + flush + fence */ + ndw = 64 + (10 * num_loops); + r = radeon_ring_lock(rdev, ring, ndw); + if (r) { + DRM_ERROR("radeon: moving bo (%d) asking for %u dw.\n", r, ndw); + return -EINVAL; + } + while (num_gpu_pages > 0) { + cur_pages = num_gpu_pages; + if (cur_pages > 8191) { + cur_pages = 8191; + } + num_gpu_pages -= cur_pages; + + /* pages are in Y direction - height + page width in X direction - width */ + radeon_ring_write(ring, PACKET3(PACKET3_BITBLT_MULTI, 8)); + radeon_ring_write(ring, + RADEON_GMC_SRC_PITCH_OFFSET_CNTL | + RADEON_GMC_DST_PITCH_OFFSET_CNTL | + RADEON_GMC_SRC_CLIPPING | + RADEON_GMC_DST_CLIPPING | + RADEON_GMC_BRUSH_NONE | + (RADEON_COLOR_FORMAT_ARGB8888 << 8) | + RADEON_GMC_SRC_DATATYPE_COLOR | + RADEON_ROP3_S | + RADEON_DP_SRC_SOURCE_MEMORY | + RADEON_GMC_CLR_CMP_CNTL_DIS | + RADEON_GMC_WR_MSK_DIS); + radeon_ring_write(ring, (pitch << 22) | (src_offset >> 10)); + radeon_ring_write(ring, (pitch << 22) | (dst_offset >> 10)); + radeon_ring_write(ring, (0x1fff) | (0x1fff << 16)); + radeon_ring_write(ring, 0); + radeon_ring_write(ring, (0x1fff) | (0x1fff << 16)); + radeon_ring_write(ring, num_gpu_pages); + radeon_ring_write(ring, num_gpu_pages); + radeon_ring_write(ring, cur_pages | (stride_pixels << 16)); + } + radeon_ring_write(ring, PACKET0(RADEON_DSTCACHE_CTLSTAT, 0)); + radeon_ring_write(ring, RADEON_RB2D_DC_FLUSH_ALL); + radeon_ring_write(ring, PACKET0(RADEON_WAIT_UNTIL, 0)); + radeon_ring_write(ring, + RADEON_WAIT_2D_IDLECLEAN | + RADEON_WAIT_HOST_IDLECLEAN | + RADEON_WAIT_DMA_GUI_IDLE); + if (fence) { + r = radeon_fence_emit(rdev, fence, RADEON_RING_TYPE_GFX_INDEX); + } + radeon_ring_unlock_commit(rdev, ring); + return r; +} + +static int r100_cp_wait_for_idle(struct radeon_device *rdev) +{ + unsigned i; + u32 tmp; + + for (i = 0; i < rdev->usec_timeout; i++) { + tmp = RREG32(R_000E40_RBBM_STATUS); + if (!G_000E40_CP_CMDSTRM_BUSY(tmp)) { + return 0; + } + DRM_UDELAY(1); + } + return -1; +} + +void r100_ring_start(struct radeon_device *rdev, struct radeon_ring *ring) +{ + int r; + + r = radeon_ring_lock(rdev, ring, 2); + if (r) { + return; + } + radeon_ring_write(ring, PACKET0(RADEON_ISYNC_CNTL, 0)); + radeon_ring_write(ring, + RADEON_ISYNC_ANY2D_IDLE3D | + RADEON_ISYNC_ANY3D_IDLE2D | + RADEON_ISYNC_WAIT_IDLEGUI | + RADEON_ISYNC_CPSCRATCH_IDLEGUI); + radeon_ring_unlock_commit(rdev, ring); +} + + +/* Load the microcode for the CP */ +static int r100_cp_init_microcode(struct radeon_device *rdev) +{ + const char *fw_name = NULL; + int err; + + DRM_DEBUG_KMS("\n"); + + if ((rdev->family == CHIP_R100) || (rdev->family == CHIP_RV100) || + (rdev->family == CHIP_RV200) || (rdev->family == CHIP_RS100) || + (rdev->family == CHIP_RS200)) { + DRM_INFO("Loading R100 Microcode\n"); + fw_name = FIRMWARE_R100; + } else if ((rdev->family == CHIP_R200) || + (rdev->family == CHIP_RV250) || + (rdev->family == CHIP_RV280) || + (rdev->family == CHIP_RS300)) { + DRM_INFO("Loading R200 Microcode\n"); + fw_name = FIRMWARE_R200; + } else if ((rdev->family == CHIP_R300) || + (rdev->family == CHIP_R350) || + (rdev->family == CHIP_RV350) || + (rdev->family == CHIP_RV380) || + (rdev->family == CHIP_RS400) || + (rdev->family == CHIP_RS480)) { + DRM_INFO("Loading R300 Microcode\n"); + fw_name = FIRMWARE_R300; + } else if ((rdev->family == CHIP_R420) || + (rdev->family == CHIP_R423) || + (rdev->family == CHIP_RV410)) { + DRM_INFO("Loading R400 Microcode\n"); + fw_name = FIRMWARE_R420; + } else if ((rdev->family == CHIP_RS690) || + (rdev->family == CHIP_RS740)) { + DRM_INFO("Loading RS690/RS740 Microcode\n"); + fw_name = FIRMWARE_RS690; + } else if (rdev->family == CHIP_RS600) { + DRM_INFO("Loading RS600 Microcode\n"); + fw_name = FIRMWARE_RS600; + } else if ((rdev->family == CHIP_RV515) || + (rdev->family == CHIP_R520) || + (rdev->family == CHIP_RV530) || + (rdev->family == CHIP_R580) || + (rdev->family == CHIP_RV560) || + (rdev->family == CHIP_RV570)) { + DRM_INFO("Loading R500 Microcode\n"); + fw_name = FIRMWARE_R520; + } + + err = 0; + rdev->me_fw = firmware_get(fw_name); + if (rdev->me_fw == NULL) { + DRM_ERROR("radeon_cp: Failed to load firmware \"%s\"\n", + fw_name); + err = -ENOENT; + } else if (rdev->me_fw->datasize % 8) { + DRM_ERROR( + "radeon_cp: Bogus length %zu in firmware \"%s\"\n", + rdev->me_fw->datasize, fw_name); + err = -EINVAL; + firmware_put(rdev->me_fw, FIRMWARE_UNLOAD); + rdev->me_fw = NULL; + } + return err; +} + +/** + * r100_cp_fini_microcode - drop the firmware image reference + * + * @rdev: radeon_device pointer + * + * Drop the me firmware image reference. + * Called at driver shutdown. + */ +static void r100_cp_fini_microcode (struct radeon_device *rdev) +{ + + if (rdev->me_fw != NULL) { + firmware_put(rdev->me_fw, FIRMWARE_UNLOAD); + rdev->me_fw = NULL; + } +} + +static void r100_cp_load_microcode(struct radeon_device *rdev) +{ + const __be32 *fw_data; + int i, size; + + if (r100_gui_wait_for_idle(rdev)) { + DRM_ERROR("Failed to wait GUI idle while " + "programming pipes. Bad things might happen.\n"); + } + + if (rdev->me_fw) { + size = rdev->me_fw->datasize / 4; + fw_data = (const __be32 *)rdev->me_fw->data; + WREG32(RADEON_CP_ME_RAM_ADDR, 0); + for (i = 0; i < size; i += 2) { + WREG32(RADEON_CP_ME_RAM_DATAH, + be32_to_cpup(&fw_data[i])); + WREG32(RADEON_CP_ME_RAM_DATAL, + be32_to_cpup(&fw_data[i + 1])); + } + } +} + +int r100_cp_init(struct radeon_device *rdev, unsigned ring_size) +{ + struct radeon_ring *ring = &rdev->ring[RADEON_RING_TYPE_GFX_INDEX]; + unsigned rb_bufsz; + unsigned rb_blksz; + unsigned max_fetch; + unsigned pre_write_timer; + unsigned pre_write_limit; + unsigned indirect2_start; + unsigned indirect1_start; + uint32_t tmp; + int r; + + if (r100_debugfs_cp_init(rdev)) { + DRM_ERROR("Failed to register debugfs file for CP !\n"); + } + if (!rdev->me_fw) { + r = r100_cp_init_microcode(rdev); + if (r) { + DRM_ERROR("Failed to load firmware!\n"); + return r; + } + } + + /* Align ring size */ + rb_bufsz = drm_order(ring_size / 8); + ring_size = (1 << (rb_bufsz + 1)) * 4; + r100_cp_load_microcode(rdev); + r = radeon_ring_init(rdev, ring, ring_size, RADEON_WB_CP_RPTR_OFFSET, + RADEON_CP_RB_RPTR, RADEON_CP_RB_WPTR, + 0, 0x7fffff, RADEON_CP_PACKET2); + if (r) { + return r; + } + /* Each time the cp read 1024 bytes (16 dword/quadword) update + * the rptr copy in system ram */ + rb_blksz = 9; + /* cp will read 128bytes at a time (4 dwords) */ + max_fetch = 1; + ring->align_mask = 16 - 1; + /* Write to CP_RB_WPTR will be delayed for pre_write_timer clocks */ + pre_write_timer = 64; + /* Force CP_RB_WPTR write if written more than one time before the + * delay expire + */ + pre_write_limit = 0; + /* Setup the cp cache like this (cache size is 96 dwords) : + * RING 0 to 15 + * INDIRECT1 16 to 79 + * INDIRECT2 80 to 95 + * So ring cache size is 16dwords (> (2 * max_fetch = 2 * 4dwords)) + * indirect1 cache size is 64dwords (> (2 * max_fetch = 2 * 4dwords)) + * indirect2 cache size is 16dwords (> (2 * max_fetch = 2 * 4dwords)) + * Idea being that most of the gpu cmd will be through indirect1 buffer + * so it gets the bigger cache. + */ + indirect2_start = 80; + indirect1_start = 16; + /* cp setup */ + WREG32(0x718, pre_write_timer | (pre_write_limit << 28)); + tmp = (REG_SET(RADEON_RB_BUFSZ, rb_bufsz) | + REG_SET(RADEON_RB_BLKSZ, rb_blksz) | + REG_SET(RADEON_MAX_FETCH, max_fetch)); +#ifdef __BIG_ENDIAN + tmp |= RADEON_BUF_SWAP_32BIT; +#endif + WREG32(RADEON_CP_RB_CNTL, tmp | RADEON_RB_NO_UPDATE); + + /* Set ring address */ + DRM_INFO("radeon: ring at 0x%016lX\n", (unsigned long)ring->gpu_addr); + WREG32(RADEON_CP_RB_BASE, ring->gpu_addr); + /* Force read & write ptr to 0 */ + WREG32(RADEON_CP_RB_CNTL, tmp | RADEON_RB_RPTR_WR_ENA | RADEON_RB_NO_UPDATE); + WREG32(RADEON_CP_RB_RPTR_WR, 0); + ring->wptr = 0; + WREG32(RADEON_CP_RB_WPTR, ring->wptr); + + /* set the wb address whether it's enabled or not */ + WREG32(R_00070C_CP_RB_RPTR_ADDR, + S_00070C_RB_RPTR_ADDR((rdev->wb.gpu_addr + RADEON_WB_CP_RPTR_OFFSET) >> 2)); + WREG32(R_000774_SCRATCH_ADDR, rdev->wb.gpu_addr + RADEON_WB_SCRATCH_OFFSET); + + if (rdev->wb.enabled) + WREG32(R_000770_SCRATCH_UMSK, 0xff); + else { + tmp |= RADEON_RB_NO_UPDATE; + WREG32(R_000770_SCRATCH_UMSK, 0); + } + + WREG32(RADEON_CP_RB_CNTL, tmp); + DRM_UDELAY(10); + ring->rptr = RREG32(RADEON_CP_RB_RPTR); + /* Set cp mode to bus mastering & enable cp*/ + WREG32(RADEON_CP_CSQ_MODE, + REG_SET(RADEON_INDIRECT2_START, indirect2_start) | + REG_SET(RADEON_INDIRECT1_START, indirect1_start)); + WREG32(RADEON_CP_RB_WPTR_DELAY, 0); + WREG32(RADEON_CP_CSQ_MODE, 0x00004D4D); + WREG32(RADEON_CP_CSQ_CNTL, RADEON_CSQ_PRIBM_INDBM); + + /* at this point everything should be setup correctly to enable master */ + pci_enable_busmaster(rdev->dev); + + radeon_ring_start(rdev, RADEON_RING_TYPE_GFX_INDEX, &rdev->ring[RADEON_RING_TYPE_GFX_INDEX]); + r = radeon_ring_test(rdev, RADEON_RING_TYPE_GFX_INDEX, ring); + if (r) { + DRM_ERROR("radeon: cp isn't working (%d).\n", r); + return r; + } + ring->ready = true; + radeon_ttm_set_active_vram_size(rdev, rdev->mc.real_vram_size); + + if (!ring->rptr_save_reg /* not resuming from suspend */ + && radeon_ring_supports_scratch_reg(rdev, ring)) { + r = radeon_scratch_get(rdev, &ring->rptr_save_reg); + if (r) { + DRM_ERROR("failed to get scratch reg for rptr save (%d).\n", r); + ring->rptr_save_reg = 0; + } + } + return 0; +} + +void r100_cp_fini(struct radeon_device *rdev) +{ + if (r100_cp_wait_for_idle(rdev)) { + DRM_ERROR("Wait for CP idle timeout, shutting down CP.\n"); + } + /* Disable ring */ + r100_cp_disable(rdev); + radeon_scratch_free(rdev, rdev->ring[RADEON_RING_TYPE_GFX_INDEX].rptr_save_reg); + radeon_ring_fini(rdev, &rdev->ring[RADEON_RING_TYPE_GFX_INDEX]); + DRM_INFO("radeon: cp finalized\n"); +} + +void r100_cp_disable(struct radeon_device *rdev) +{ + /* Disable ring */ + radeon_ttm_set_active_vram_size(rdev, rdev->mc.visible_vram_size); + rdev->ring[RADEON_RING_TYPE_GFX_INDEX].ready = false; + WREG32(RADEON_CP_CSQ_MODE, 0); + WREG32(RADEON_CP_CSQ_CNTL, 0); + WREG32(R_000770_SCRATCH_UMSK, 0); + if (r100_gui_wait_for_idle(rdev)) { + DRM_ERROR("Failed to wait GUI idle while " + "programming pipes. Bad things might happen.\n"); + } +} + +/* + * CS functions + */ +int r100_reloc_pitch_offset(struct radeon_cs_parser *p, + struct radeon_cs_packet *pkt, + unsigned idx, + unsigned reg) +{ + int r; + u32 tile_flags = 0; + u32 tmp; + struct radeon_cs_reloc *reloc; + u32 value; + + r = r100_cs_packet_next_reloc(p, &reloc); + if (r) { + DRM_ERROR("No reloc for ib[%d]=0x%04X\n", + idx, reg); + r100_cs_dump_packet(p, pkt); + return r; + } + + value = radeon_get_ib_value(p, idx); + tmp = value & 0x003fffff; + tmp += (((u32)reloc->lobj.gpu_offset) >> 10); + + if (!(p->cs_flags & RADEON_CS_KEEP_TILING_FLAGS)) { + if (reloc->lobj.tiling_flags & RADEON_TILING_MACRO) + tile_flags |= RADEON_DST_TILE_MACRO; + if (reloc->lobj.tiling_flags & RADEON_TILING_MICRO) { + if (reg == RADEON_SRC_PITCH_OFFSET) { + DRM_ERROR("Cannot src blit from microtiled surface\n"); + r100_cs_dump_packet(p, pkt); + return -EINVAL; + } + tile_flags |= RADEON_DST_TILE_MICRO; + } + + tmp |= tile_flags; + p->ib.ptr[idx] = (value & 0x3fc00000) | tmp; + } else + p->ib.ptr[idx] = (value & 0xffc00000) | tmp; + return 0; +} + +int r100_packet3_load_vbpntr(struct radeon_cs_parser *p, + struct radeon_cs_packet *pkt, + int idx) +{ + unsigned c, i; + struct radeon_cs_reloc *reloc; + struct r100_cs_track *track; + int r = 0; + volatile uint32_t *ib; + u32 idx_value; + + ib = p->ib.ptr; + track = (struct r100_cs_track *)p->track; + c = radeon_get_ib_value(p, idx++) & 0x1F; + if (c > 16) { + DRM_ERROR("Only 16 vertex buffers are allowed %d\n", + pkt->opcode); + r100_cs_dump_packet(p, pkt); + return -EINVAL; + } + track->num_arrays = c; + for (i = 0; i < (c - 1); i+=2, idx+=3) { + r = r100_cs_packet_next_reloc(p, &reloc); + if (r) { + DRM_ERROR("No reloc for packet3 %d\n", + pkt->opcode); + r100_cs_dump_packet(p, pkt); + return r; + } + idx_value = radeon_get_ib_value(p, idx); + ib[idx+1] = radeon_get_ib_value(p, idx + 1) + ((u32)reloc->lobj.gpu_offset); + + track->arrays[i + 0].esize = idx_value >> 8; + track->arrays[i + 0].robj = reloc->robj; + track->arrays[i + 0].esize &= 0x7F; + r = r100_cs_packet_next_reloc(p, &reloc); + if (r) { + DRM_ERROR("No reloc for packet3 %d\n", + pkt->opcode); + r100_cs_dump_packet(p, pkt); + return r; + } + ib[idx+2] = radeon_get_ib_value(p, idx + 2) + ((u32)reloc->lobj.gpu_offset); + track->arrays[i + 1].robj = reloc->robj; + track->arrays[i + 1].esize = idx_value >> 24; + track->arrays[i + 1].esize &= 0x7F; + } + if (c & 1) { + r = r100_cs_packet_next_reloc(p, &reloc); + if (r) { + DRM_ERROR("No reloc for packet3 %d\n", + pkt->opcode); + r100_cs_dump_packet(p, pkt); + return r; + } + idx_value = radeon_get_ib_value(p, idx); + ib[idx+1] = radeon_get_ib_value(p, idx + 1) + ((u32)reloc->lobj.gpu_offset); + track->arrays[i + 0].robj = reloc->robj; + track->arrays[i + 0].esize = idx_value >> 8; + track->arrays[i + 0].esize &= 0x7F; + } + return r; +} + +int r100_cs_parse_packet0(struct radeon_cs_parser *p, + struct radeon_cs_packet *pkt, + const unsigned *auth, unsigned n, + radeon_packet0_check_t check) +{ + unsigned reg; + unsigned i, j, m; + unsigned idx; + int r; + + idx = pkt->idx + 1; + reg = pkt->reg; + /* Check that register fall into register range + * determined by the number of entry (n) in the + * safe register bitmap. + */ + if (pkt->one_reg_wr) { + if ((reg >> 7) > n) { + return -EINVAL; + } + } else { + if (((reg + (pkt->count << 2)) >> 7) > n) { + return -EINVAL; + } + } + for (i = 0; i <= pkt->count; i++, idx++) { + j = (reg >> 7); + m = 1 << ((reg >> 2) & 31); + if (auth[j] & m) { + r = check(p, pkt, idx, reg); + if (r) { + return r; + } + } + if (pkt->one_reg_wr) { + if (!(auth[j] & m)) { + break; + } + } else { + reg += 4; + } + } + return 0; +} + +void r100_cs_dump_packet(struct radeon_cs_parser *p, + struct radeon_cs_packet *pkt) +{ + volatile uint32_t *ib; + unsigned i; + unsigned idx; + + ib = p->ib.ptr; + idx = pkt->idx; + for (i = 0; i <= (pkt->count + 1); i++, idx++) { + DRM_INFO("ib[%d]=0x%08X\n", idx, ib[idx]); + } +} + +/** + * r100_cs_packet_parse() - parse cp packet and point ib index to next packet + * @parser: parser structure holding parsing context. + * @pkt: where to store packet informations + * + * Assume that chunk_ib_index is properly set. Will return -EINVAL + * if packet is bigger than remaining ib size. or if packets is unknown. + **/ +int r100_cs_packet_parse(struct radeon_cs_parser *p, + struct radeon_cs_packet *pkt, + unsigned idx) +{ + struct radeon_cs_chunk *ib_chunk = &p->chunks[p->chunk_ib_idx]; + uint32_t header; + + if (idx >= ib_chunk->length_dw) { + DRM_ERROR("Can not parse packet at %d after CS end %d !\n", + idx, ib_chunk->length_dw); + return -EINVAL; + } + header = radeon_get_ib_value(p, idx); + pkt->idx = idx; + pkt->type = CP_PACKET_GET_TYPE(header); + pkt->count = CP_PACKET_GET_COUNT(header); + switch (pkt->type) { + case PACKET_TYPE0: + pkt->reg = CP_PACKET0_GET_REG(header); + pkt->one_reg_wr = CP_PACKET0_GET_ONE_REG_WR(header); + break; + case PACKET_TYPE3: + pkt->opcode = CP_PACKET3_GET_OPCODE(header); + break; + case PACKET_TYPE2: + pkt->count = -1; + break; + default: + DRM_ERROR("Unknown packet type %d at %d !\n", pkt->type, idx); + return -EINVAL; + } + if ((pkt->count + 1 + pkt->idx) >= ib_chunk->length_dw) { + DRM_ERROR("Packet (%d:%d:%d) end after CS buffer (%d) !\n", + pkt->idx, pkt->type, pkt->count, ib_chunk->length_dw); + return -EINVAL; + } + return 0; +} + +/** + * r100_cs_packet_next_vline() - parse userspace VLINE packet + * @parser: parser structure holding parsing context. + * + * Userspace sends a special sequence for VLINE waits. + * PACKET0 - VLINE_START_END + value + * PACKET0 - WAIT_UNTIL +_value + * RELOC (P3) - crtc_id in reloc. + * + * This function parses this and relocates the VLINE START END + * and WAIT UNTIL packets to the correct crtc. + * It also detects a switched off crtc and nulls out the + * wait in that case. + */ +int r100_cs_packet_parse_vline(struct radeon_cs_parser *p) +{ + struct drm_mode_object *obj; + struct drm_crtc *crtc; + struct radeon_crtc *radeon_crtc; + struct radeon_cs_packet p3reloc, waitreloc; + int crtc_id; + int r; + uint32_t header, h_idx, reg; + volatile uint32_t *ib; + + ib = p->ib.ptr; + + /* parse the wait until */ + r = r100_cs_packet_parse(p, &waitreloc, p->idx); + if (r) + return r; + + /* check its a wait until and only 1 count */ + if (waitreloc.reg != RADEON_WAIT_UNTIL || + waitreloc.count != 0) { + DRM_ERROR("vline wait had illegal wait until segment\n"); + return -EINVAL; + } + + if (radeon_get_ib_value(p, waitreloc.idx + 1) != RADEON_WAIT_CRTC_VLINE) { + DRM_ERROR("vline wait had illegal wait until\n"); + return -EINVAL; + } + + /* jump over the NOP */ + r = r100_cs_packet_parse(p, &p3reloc, p->idx + waitreloc.count + 2); + if (r) + return r; + + h_idx = p->idx - 2; + p->idx += waitreloc.count + 2; + p->idx += p3reloc.count + 2; + + header = radeon_get_ib_value(p, h_idx); + crtc_id = radeon_get_ib_value(p, h_idx + 5); + reg = CP_PACKET0_GET_REG(header); + obj = drm_mode_object_find(p->rdev->ddev, crtc_id, DRM_MODE_OBJECT_CRTC); + if (!obj) { + DRM_ERROR("cannot find crtc %d\n", crtc_id); + return -EINVAL; + } + crtc = obj_to_crtc(obj); + radeon_crtc = to_radeon_crtc(crtc); + crtc_id = radeon_crtc->crtc_id; + + if (!crtc->enabled) { + /* if the CRTC isn't enabled - we need to nop out the wait until */ + ib[h_idx + 2] = PACKET2(0); + ib[h_idx + 3] = PACKET2(0); + } else if (crtc_id == 1) { + switch (reg) { + case AVIVO_D1MODE_VLINE_START_END: + header &= ~R300_CP_PACKET0_REG_MASK; + header |= AVIVO_D2MODE_VLINE_START_END >> 2; + break; + case RADEON_CRTC_GUI_TRIG_VLINE: + header &= ~R300_CP_PACKET0_REG_MASK; + header |= RADEON_CRTC2_GUI_TRIG_VLINE >> 2; + break; + default: + DRM_ERROR("unknown crtc reloc\n"); + return -EINVAL; + } + ib[h_idx] = header; + ib[h_idx + 3] |= RADEON_ENG_DISPLAY_SELECT_CRTC1; + } + + return 0; +} + +/** + * r100_cs_packet_next_reloc() - parse next packet which should be reloc packet3 + * @parser: parser structure holding parsing context. + * @data: pointer to relocation data + * @offset_start: starting offset + * @offset_mask: offset mask (to align start offset on) + * @reloc: reloc informations + * + * Check next packet is relocation packet3, do bo validation and compute + * GPU offset using the provided start. + **/ +int r100_cs_packet_next_reloc(struct radeon_cs_parser *p, + struct radeon_cs_reloc **cs_reloc) +{ + struct radeon_cs_chunk *relocs_chunk; + struct radeon_cs_packet p3reloc; + unsigned idx; + int r; + + if (p->chunk_relocs_idx == -1) { + DRM_ERROR("No relocation chunk !\n"); + return -EINVAL; + } + *cs_reloc = NULL; + relocs_chunk = &p->chunks[p->chunk_relocs_idx]; + r = r100_cs_packet_parse(p, &p3reloc, p->idx); + if (r) { + return r; + } + p->idx += p3reloc.count + 2; + if (p3reloc.type != PACKET_TYPE3 || p3reloc.opcode != PACKET3_NOP) { + DRM_ERROR("No packet3 for relocation for packet at %d.\n", + p3reloc.idx); + r100_cs_dump_packet(p, &p3reloc); + return -EINVAL; + } + idx = radeon_get_ib_value(p, p3reloc.idx + 1); + if (idx >= relocs_chunk->length_dw) { + DRM_ERROR("Relocs at %d after relocations chunk end %d !\n", + idx, relocs_chunk->length_dw); + r100_cs_dump_packet(p, &p3reloc); + return -EINVAL; + } + /* FIXME: we assume reloc size is 4 dwords */ + *cs_reloc = p->relocs_ptr[(idx / 4)]; + return 0; +} + +static int r100_get_vtx_size(uint32_t vtx_fmt) +{ + int vtx_size; + vtx_size = 2; + /* ordered according to bits in spec */ + if (vtx_fmt & RADEON_SE_VTX_FMT_W0) + vtx_size++; + if (vtx_fmt & RADEON_SE_VTX_FMT_FPCOLOR) + vtx_size += 3; + if (vtx_fmt & RADEON_SE_VTX_FMT_FPALPHA) + vtx_size++; + if (vtx_fmt & RADEON_SE_VTX_FMT_PKCOLOR) + vtx_size++; + if (vtx_fmt & RADEON_SE_VTX_FMT_FPSPEC) + vtx_size += 3; + if (vtx_fmt & RADEON_SE_VTX_FMT_FPFOG) + vtx_size++; + if (vtx_fmt & RADEON_SE_VTX_FMT_PKSPEC) + vtx_size++; + if (vtx_fmt & RADEON_SE_VTX_FMT_ST0) + vtx_size += 2; + if (vtx_fmt & RADEON_SE_VTX_FMT_ST1) + vtx_size += 2; + if (vtx_fmt & RADEON_SE_VTX_FMT_Q1) + vtx_size++; + if (vtx_fmt & RADEON_SE_VTX_FMT_ST2) + vtx_size += 2; + if (vtx_fmt & RADEON_SE_VTX_FMT_Q2) + vtx_size++; + if (vtx_fmt & RADEON_SE_VTX_FMT_ST3) + vtx_size += 2; + if (vtx_fmt & RADEON_SE_VTX_FMT_Q3) + vtx_size++; + if (vtx_fmt & RADEON_SE_VTX_FMT_Q0) + vtx_size++; + /* blend weight */ + if (vtx_fmt & (0x7 << 15)) + vtx_size += (vtx_fmt >> 15) & 0x7; + if (vtx_fmt & RADEON_SE_VTX_FMT_N0) + vtx_size += 3; + if (vtx_fmt & RADEON_SE_VTX_FMT_XY1) + vtx_size += 2; + if (vtx_fmt & RADEON_SE_VTX_FMT_Z1) + vtx_size++; + if (vtx_fmt & RADEON_SE_VTX_FMT_W1) + vtx_size++; + if (vtx_fmt & RADEON_SE_VTX_FMT_N1) + vtx_size++; + if (vtx_fmt & RADEON_SE_VTX_FMT_Z) + vtx_size++; + return vtx_size; +} + +static int r100_packet0_check(struct radeon_cs_parser *p, + struct radeon_cs_packet *pkt, + unsigned idx, unsigned reg) +{ + struct radeon_cs_reloc *reloc; + struct r100_cs_track *track; + volatile uint32_t *ib; + uint32_t tmp; + int r; + int i, face; + u32 tile_flags = 0; + u32 idx_value; + + ib = p->ib.ptr; + track = (struct r100_cs_track *)p->track; + + idx_value = radeon_get_ib_value(p, idx); + + switch (reg) { + case RADEON_CRTC_GUI_TRIG_VLINE: + r = r100_cs_packet_parse_vline(p); + if (r) { + DRM_ERROR("No reloc for ib[%d]=0x%04X\n", + idx, reg); + r100_cs_dump_packet(p, pkt); + return r; + } + break; + /* FIXME: only allow PACKET3 blit? easier to check for out of + * range access */ + case RADEON_DST_PITCH_OFFSET: + case RADEON_SRC_PITCH_OFFSET: + r = r100_reloc_pitch_offset(p, pkt, idx, reg); + if (r) + return r; + break; + case RADEON_RB3D_DEPTHOFFSET: + r = r100_cs_packet_next_reloc(p, &reloc); + if (r) { + DRM_ERROR("No reloc for ib[%d]=0x%04X\n", + idx, reg); + r100_cs_dump_packet(p, pkt); + return r; + } + track->zb.robj = reloc->robj; + track->zb.offset = idx_value; + track->zb_dirty = true; + ib[idx] = idx_value + ((u32)reloc->lobj.gpu_offset); + break; + case RADEON_RB3D_COLOROFFSET: + r = r100_cs_packet_next_reloc(p, &reloc); + if (r) { + DRM_ERROR("No reloc for ib[%d]=0x%04X\n", + idx, reg); + r100_cs_dump_packet(p, pkt); + return r; + } + track->cb[0].robj = reloc->robj; + track->cb[0].offset = idx_value; + track->cb_dirty = true; + ib[idx] = idx_value + ((u32)reloc->lobj.gpu_offset); + break; + case RADEON_PP_TXOFFSET_0: + case RADEON_PP_TXOFFSET_1: + case RADEON_PP_TXOFFSET_2: + i = (reg - RADEON_PP_TXOFFSET_0) / 24; + r = r100_cs_packet_next_reloc(p, &reloc); + if (r) { + DRM_ERROR("No reloc for ib[%d]=0x%04X\n", + idx, reg); + r100_cs_dump_packet(p, pkt); + return r; + } + if (!(p->cs_flags & RADEON_CS_KEEP_TILING_FLAGS)) { + if (reloc->lobj.tiling_flags & RADEON_TILING_MACRO) + tile_flags |= RADEON_TXO_MACRO_TILE; + if (reloc->lobj.tiling_flags & RADEON_TILING_MICRO) + tile_flags |= RADEON_TXO_MICRO_TILE_X2; + + tmp = idx_value & ~(0x7 << 2); + tmp |= tile_flags; + ib[idx] = tmp + ((u32)reloc->lobj.gpu_offset); + } else + ib[idx] = idx_value + ((u32)reloc->lobj.gpu_offset); + track->textures[i].robj = reloc->robj; + track->tex_dirty = true; + break; + case RADEON_PP_CUBIC_OFFSET_T0_0: + case RADEON_PP_CUBIC_OFFSET_T0_1: + case RADEON_PP_CUBIC_OFFSET_T0_2: + case RADEON_PP_CUBIC_OFFSET_T0_3: + case RADEON_PP_CUBIC_OFFSET_T0_4: + i = (reg - RADEON_PP_CUBIC_OFFSET_T0_0) / 4; + r = r100_cs_packet_next_reloc(p, &reloc); + if (r) { + DRM_ERROR("No reloc for ib[%d]=0x%04X\n", + idx, reg); + r100_cs_dump_packet(p, pkt); + return r; + } + track->textures[0].cube_info[i].offset = idx_value; + ib[idx] = idx_value + ((u32)reloc->lobj.gpu_offset); + track->textures[0].cube_info[i].robj = reloc->robj; + track->tex_dirty = true; + break; + case RADEON_PP_CUBIC_OFFSET_T1_0: + case RADEON_PP_CUBIC_OFFSET_T1_1: + case RADEON_PP_CUBIC_OFFSET_T1_2: + case RADEON_PP_CUBIC_OFFSET_T1_3: + case RADEON_PP_CUBIC_OFFSET_T1_4: + i = (reg - RADEON_PP_CUBIC_OFFSET_T1_0) / 4; + r = r100_cs_packet_next_reloc(p, &reloc); + if (r) { + DRM_ERROR("No reloc for ib[%d]=0x%04X\n", + idx, reg); + r100_cs_dump_packet(p, pkt); + return r; + } + track->textures[1].cube_info[i].offset = idx_value; + ib[idx] = idx_value + ((u32)reloc->lobj.gpu_offset); + track->textures[1].cube_info[i].robj = reloc->robj; + track->tex_dirty = true; + break; + case RADEON_PP_CUBIC_OFFSET_T2_0: + case RADEON_PP_CUBIC_OFFSET_T2_1: + case RADEON_PP_CUBIC_OFFSET_T2_2: + case RADEON_PP_CUBIC_OFFSET_T2_3: + case RADEON_PP_CUBIC_OFFSET_T2_4: + i = (reg - RADEON_PP_CUBIC_OFFSET_T2_0) / 4; + r = r100_cs_packet_next_reloc(p, &reloc); + if (r) { + DRM_ERROR("No reloc for ib[%d]=0x%04X\n", + idx, reg); + r100_cs_dump_packet(p, pkt); + return r; + } + track->textures[2].cube_info[i].offset = idx_value; + ib[idx] = idx_value + ((u32)reloc->lobj.gpu_offset); + track->textures[2].cube_info[i].robj = reloc->robj; + track->tex_dirty = true; + break; + case RADEON_RE_WIDTH_HEIGHT: + track->maxy = ((idx_value >> 16) & 0x7FF); + track->cb_dirty = true; + track->zb_dirty = true; + break; + case RADEON_RB3D_COLORPITCH: + r = r100_cs_packet_next_reloc(p, &reloc); + if (r) { + DRM_ERROR("No reloc for ib[%d]=0x%04X\n", + idx, reg); + r100_cs_dump_packet(p, pkt); + return r; + } + if (!(p->cs_flags & RADEON_CS_KEEP_TILING_FLAGS)) { + if (reloc->lobj.tiling_flags & RADEON_TILING_MACRO) + tile_flags |= RADEON_COLOR_TILE_ENABLE; + if (reloc->lobj.tiling_flags & RADEON_TILING_MICRO) + tile_flags |= RADEON_COLOR_MICROTILE_ENABLE; + + tmp = idx_value & ~(0x7 << 16); + tmp |= tile_flags; + ib[idx] = tmp; + } else + ib[idx] = idx_value; + + track->cb[0].pitch = idx_value & RADEON_COLORPITCH_MASK; + track->cb_dirty = true; + break; + case RADEON_RB3D_DEPTHPITCH: + track->zb.pitch = idx_value & RADEON_DEPTHPITCH_MASK; + track->zb_dirty = true; + break; + case RADEON_RB3D_CNTL: + switch ((idx_value >> RADEON_RB3D_COLOR_FORMAT_SHIFT) & 0x1f) { + case 7: + case 8: + case 9: + case 11: + case 12: + track->cb[0].cpp = 1; + break; + case 3: + case 4: + case 15: + track->cb[0].cpp = 2; + break; + case 6: + track->cb[0].cpp = 4; + break; + default: + DRM_ERROR("Invalid color buffer format (%d) !\n", + ((idx_value >> RADEON_RB3D_COLOR_FORMAT_SHIFT) & 0x1f)); + return -EINVAL; + } + track->z_enabled = !!(idx_value & RADEON_Z_ENABLE); + track->cb_dirty = true; + track->zb_dirty = true; + break; + case RADEON_RB3D_ZSTENCILCNTL: + switch (idx_value & 0xf) { + case 0: + track->zb.cpp = 2; + break; + case 2: + case 3: + case 4: + case 5: + case 9: + case 11: + track->zb.cpp = 4; + break; + default: + break; + } + track->zb_dirty = true; + break; + case RADEON_RB3D_ZPASS_ADDR: + r = r100_cs_packet_next_reloc(p, &reloc); + if (r) { + DRM_ERROR("No reloc for ib[%d]=0x%04X\n", + idx, reg); + r100_cs_dump_packet(p, pkt); + return r; + } + ib[idx] = idx_value + ((u32)reloc->lobj.gpu_offset); + break; + case RADEON_PP_CNTL: + { + uint32_t temp = idx_value >> 4; + for (i = 0; i < track->num_texture; i++) + track->textures[i].enabled = !!(temp & (1 << i)); + track->tex_dirty = true; + } + break; + case RADEON_SE_VF_CNTL: + track->vap_vf_cntl = idx_value; + break; + case RADEON_SE_VTX_FMT: + track->vtx_size = r100_get_vtx_size(idx_value); + break; + case RADEON_PP_TEX_SIZE_0: + case RADEON_PP_TEX_SIZE_1: + case RADEON_PP_TEX_SIZE_2: + i = (reg - RADEON_PP_TEX_SIZE_0) / 8; + track->textures[i].width = (idx_value & RADEON_TEX_USIZE_MASK) + 1; + track->textures[i].height = ((idx_value & RADEON_TEX_VSIZE_MASK) >> RADEON_TEX_VSIZE_SHIFT) + 1; + track->tex_dirty = true; + break; + case RADEON_PP_TEX_PITCH_0: + case RADEON_PP_TEX_PITCH_1: + case RADEON_PP_TEX_PITCH_2: + i = (reg - RADEON_PP_TEX_PITCH_0) / 8; + track->textures[i].pitch = idx_value + 32; + track->tex_dirty = true; + break; + case RADEON_PP_TXFILTER_0: + case RADEON_PP_TXFILTER_1: + case RADEON_PP_TXFILTER_2: + i = (reg - RADEON_PP_TXFILTER_0) / 24; + track->textures[i].num_levels = ((idx_value & RADEON_MAX_MIP_LEVEL_MASK) + >> RADEON_MAX_MIP_LEVEL_SHIFT); + tmp = (idx_value >> 23) & 0x7; + if (tmp == 2 || tmp == 6) + track->textures[i].roundup_w = false; + tmp = (idx_value >> 27) & 0x7; + if (tmp == 2 || tmp == 6) + track->textures[i].roundup_h = false; + track->tex_dirty = true; + break; + case RADEON_PP_TXFORMAT_0: + case RADEON_PP_TXFORMAT_1: + case RADEON_PP_TXFORMAT_2: + i = (reg - RADEON_PP_TXFORMAT_0) / 24; + if (idx_value & RADEON_TXFORMAT_NON_POWER2) { + track->textures[i].use_pitch = 1; + } else { + track->textures[i].use_pitch = 0; + track->textures[i].width = 1 << ((idx_value >> RADEON_TXFORMAT_WIDTH_SHIFT) & RADEON_TXFORMAT_WIDTH_MASK); + track->textures[i].height = 1 << ((idx_value >> RADEON_TXFORMAT_HEIGHT_SHIFT) & RADEON_TXFORMAT_HEIGHT_MASK); + } + if (idx_value & RADEON_TXFORMAT_CUBIC_MAP_ENABLE) + track->textures[i].tex_coord_type = 2; + switch ((idx_value & RADEON_TXFORMAT_FORMAT_MASK)) { + case RADEON_TXFORMAT_I8: + case RADEON_TXFORMAT_RGB332: + case RADEON_TXFORMAT_Y8: + track->textures[i].cpp = 1; + track->textures[i].compress_format = R100_TRACK_COMP_NONE; + break; + case RADEON_TXFORMAT_AI88: + case RADEON_TXFORMAT_ARGB1555: + case RADEON_TXFORMAT_RGB565: + case RADEON_TXFORMAT_ARGB4444: + case RADEON_TXFORMAT_VYUY422: + case RADEON_TXFORMAT_YVYU422: + case RADEON_TXFORMAT_SHADOW16: + case RADEON_TXFORMAT_LDUDV655: + case RADEON_TXFORMAT_DUDV88: + track->textures[i].cpp = 2; + track->textures[i].compress_format = R100_TRACK_COMP_NONE; + break; + case RADEON_TXFORMAT_ARGB8888: + case RADEON_TXFORMAT_RGBA8888: + case RADEON_TXFORMAT_SHADOW32: + case RADEON_TXFORMAT_LDUDUV8888: + track->textures[i].cpp = 4; + track->textures[i].compress_format = R100_TRACK_COMP_NONE; + break; + case RADEON_TXFORMAT_DXT1: + track->textures[i].cpp = 1; + track->textures[i].compress_format = R100_TRACK_COMP_DXT1; + break; + case RADEON_TXFORMAT_DXT23: + case RADEON_TXFORMAT_DXT45: + track->textures[i].cpp = 1; + track->textures[i].compress_format = R100_TRACK_COMP_DXT35; + break; + } + track->textures[i].cube_info[4].width = 1 << ((idx_value >> 16) & 0xf); + track->textures[i].cube_info[4].height = 1 << ((idx_value >> 20) & 0xf); + track->tex_dirty = true; + break; + case RADEON_PP_CUBIC_FACES_0: + case RADEON_PP_CUBIC_FACES_1: + case RADEON_PP_CUBIC_FACES_2: + tmp = idx_value; + i = (reg - RADEON_PP_CUBIC_FACES_0) / 4; + for (face = 0; face < 4; face++) { + track->textures[i].cube_info[face].width = 1 << ((tmp >> (face * 8)) & 0xf); + track->textures[i].cube_info[face].height = 1 << ((tmp >> ((face * 8) + 4)) & 0xf); + } + track->tex_dirty = true; + break; + default: + DRM_ERROR("Forbidden register 0x%04X in cs at %d\n", + reg, idx); + return -EINVAL; + } + return 0; +} + +int r100_cs_track_check_pkt3_indx_buffer(struct radeon_cs_parser *p, + struct radeon_cs_packet *pkt, + struct radeon_bo *robj) +{ + unsigned idx; + u32 value; + idx = pkt->idx + 1; + value = radeon_get_ib_value(p, idx + 2); + if ((value + 1) > radeon_bo_size(robj)) { + DRM_ERROR("[drm] Buffer too small for PACKET3 INDX_BUFFER " + "(need %u have %lu) !\n", + value + 1, + radeon_bo_size(robj)); + return -EINVAL; + } + return 0; +} + +static int r100_packet3_check(struct radeon_cs_parser *p, + struct radeon_cs_packet *pkt) +{ + struct radeon_cs_reloc *reloc; + struct r100_cs_track *track; + unsigned idx; + volatile uint32_t *ib; + int r; + + ib = p->ib.ptr; + idx = pkt->idx + 1; + track = (struct r100_cs_track *)p->track; + switch (pkt->opcode) { + case PACKET3_3D_LOAD_VBPNTR: + r = r100_packet3_load_vbpntr(p, pkt, idx); + if (r) + return r; + break; + case PACKET3_INDX_BUFFER: + r = r100_cs_packet_next_reloc(p, &reloc); + if (r) { + DRM_ERROR("No reloc for packet3 %d\n", pkt->opcode); + r100_cs_dump_packet(p, pkt); + return r; + } + ib[idx+1] = radeon_get_ib_value(p, idx+1) + ((u32)reloc->lobj.gpu_offset); + r = r100_cs_track_check_pkt3_indx_buffer(p, pkt, reloc->robj); + if (r) { + return r; + } + break; + case 0x23: + /* 3D_RNDR_GEN_INDX_PRIM on r100/r200 */ + r = r100_cs_packet_next_reloc(p, &reloc); + if (r) { + DRM_ERROR("No reloc for packet3 %d\n", pkt->opcode); + r100_cs_dump_packet(p, pkt); + return r; + } + ib[idx] = radeon_get_ib_value(p, idx) + ((u32)reloc->lobj.gpu_offset); + track->num_arrays = 1; + track->vtx_size = r100_get_vtx_size(radeon_get_ib_value(p, idx + 2)); + + track->arrays[0].robj = reloc->robj; + track->arrays[0].esize = track->vtx_size; + + track->max_indx = radeon_get_ib_value(p, idx+1); + + track->vap_vf_cntl = radeon_get_ib_value(p, idx+3); + track->immd_dwords = pkt->count - 1; + r = r100_cs_track_check(p->rdev, track); + if (r) + return r; + break; + case PACKET3_3D_DRAW_IMMD: + if (((radeon_get_ib_value(p, idx + 1) >> 4) & 0x3) != 3) { + DRM_ERROR("PRIM_WALK must be 3 for IMMD draw\n"); + return -EINVAL; + } + track->vtx_size = r100_get_vtx_size(radeon_get_ib_value(p, idx + 0)); + track->vap_vf_cntl = radeon_get_ib_value(p, idx + 1); + track->immd_dwords = pkt->count - 1; + r = r100_cs_track_check(p->rdev, track); + if (r) + return r; + break; + /* triggers drawing using in-packet vertex data */ + case PACKET3_3D_DRAW_IMMD_2: + if (((radeon_get_ib_value(p, idx) >> 4) & 0x3) != 3) { + DRM_ERROR("PRIM_WALK must be 3 for IMMD draw\n"); + return -EINVAL; + } + track->vap_vf_cntl = radeon_get_ib_value(p, idx); + track->immd_dwords = pkt->count; + r = r100_cs_track_check(p->rdev, track); + if (r) + return r; + break; + /* triggers drawing using in-packet vertex data */ + case PACKET3_3D_DRAW_VBUF_2: + track->vap_vf_cntl = radeon_get_ib_value(p, idx); + r = r100_cs_track_check(p->rdev, track); + if (r) + return r; + break; + /* triggers drawing of vertex buffers setup elsewhere */ + case PACKET3_3D_DRAW_INDX_2: + track->vap_vf_cntl = radeon_get_ib_value(p, idx); + r = r100_cs_track_check(p->rdev, track); + if (r) + return r; + break; + /* triggers drawing using indices to vertex buffer */ + case PACKET3_3D_DRAW_VBUF: + track->vap_vf_cntl = radeon_get_ib_value(p, idx + 1); + r = r100_cs_track_check(p->rdev, track); + if (r) + return r; + break; + /* triggers drawing of vertex buffers setup elsewhere */ + case PACKET3_3D_DRAW_INDX: + track->vap_vf_cntl = radeon_get_ib_value(p, idx + 1); + r = r100_cs_track_check(p->rdev, track); + if (r) + return r; + break; + /* triggers drawing using indices to vertex buffer */ + case PACKET3_3D_CLEAR_HIZ: + case PACKET3_3D_CLEAR_ZMASK: + if (p->rdev->hyperz_filp != p->filp) + return -EINVAL; + break; + case PACKET3_NOP: + break; + default: + DRM_ERROR("Packet3 opcode %x not supported\n", pkt->opcode); + return -EINVAL; + } + return 0; +} + +int r100_cs_parse(struct radeon_cs_parser *p) +{ + struct radeon_cs_packet pkt; + struct r100_cs_track *track; + int r; + + track = malloc(sizeof(*track), DRM_MEM_DRIVER, M_ZERO | M_WAITOK); + if (!track) + return -ENOMEM; + r100_cs_track_clear(p->rdev, track); + p->track = track; + do { + r = r100_cs_packet_parse(p, &pkt, p->idx); + if (r) { + free(p->track, DRM_MEM_DRIVER); + p->track = NULL; + return r; + } + p->idx += pkt.count + 2; + switch (pkt.type) { + case PACKET_TYPE0: + if (p->rdev->family >= CHIP_R200) + r = r100_cs_parse_packet0(p, &pkt, + p->rdev->config.r100.reg_safe_bm, + p->rdev->config.r100.reg_safe_bm_size, + &r200_packet0_check); + else + r = r100_cs_parse_packet0(p, &pkt, + p->rdev->config.r100.reg_safe_bm, + p->rdev->config.r100.reg_safe_bm_size, + &r100_packet0_check); + break; + case PACKET_TYPE2: + break; + case PACKET_TYPE3: + r = r100_packet3_check(p, &pkt); + break; + default: + DRM_ERROR("Unknown packet type %d !\n", + pkt.type); + free(p->track, DRM_MEM_DRIVER); + p->track = NULL; + return -EINVAL; + } + if (r) { + free(p->track, DRM_MEM_DRIVER); + p->track = NULL; + return r; + } + } while (p->idx < p->chunks[p->chunk_ib_idx].length_dw); + free(p->track, DRM_MEM_DRIVER); + p->track = NULL; + return 0; +} + +static void r100_cs_track_texture_print(struct r100_cs_track_texture *t) +{ + DRM_ERROR("pitch %d\n", t->pitch); + DRM_ERROR("use_pitch %d\n", t->use_pitch); + DRM_ERROR("width %d\n", t->width); + DRM_ERROR("width_11 %d\n", t->width_11); + DRM_ERROR("height %d\n", t->height); + DRM_ERROR("height_11 %d\n", t->height_11); + DRM_ERROR("num levels %d\n", t->num_levels); + DRM_ERROR("depth %d\n", t->txdepth); + DRM_ERROR("bpp %d\n", t->cpp); + DRM_ERROR("coordinate type %d\n", t->tex_coord_type); + DRM_ERROR("width round to power of 2 %d\n", t->roundup_w); + DRM_ERROR("height round to power of 2 %d\n", t->roundup_h); + DRM_ERROR("compress format %d\n", t->compress_format); +} + +static int r100_track_compress_size(int compress_format, int w, int h) +{ + int block_width, block_height, block_bytes; + int wblocks, hblocks; + int min_wblocks; + int sz; + + block_width = 4; + block_height = 4; + + switch (compress_format) { + case R100_TRACK_COMP_DXT1: + block_bytes = 8; + min_wblocks = 4; + break; + default: + case R100_TRACK_COMP_DXT35: + block_bytes = 16; + min_wblocks = 2; + break; + } + + hblocks = (h + block_height - 1) / block_height; + wblocks = (w + block_width - 1) / block_width; + if (wblocks < min_wblocks) + wblocks = min_wblocks; + sz = wblocks * hblocks * block_bytes; + return sz; +} + +static int r100_cs_track_cube(struct radeon_device *rdev, + struct r100_cs_track *track, unsigned idx) +{ + unsigned face, w, h; + struct radeon_bo *cube_robj; + unsigned long size; + unsigned compress_format = track->textures[idx].compress_format; + + for (face = 0; face < 5; face++) { + cube_robj = track->textures[idx].cube_info[face].robj; + w = track->textures[idx].cube_info[face].width; + h = track->textures[idx].cube_info[face].height; + + if (compress_format) { + size = r100_track_compress_size(compress_format, w, h); + } else + size = w * h; + size *= track->textures[idx].cpp; + + size += track->textures[idx].cube_info[face].offset; + + if (size > radeon_bo_size(cube_robj)) { + DRM_ERROR("Cube texture offset greater than object size %lu %lu\n", + size, radeon_bo_size(cube_robj)); + r100_cs_track_texture_print(&track->textures[idx]); + return -1; + } + } + return 0; +} + +static int r100_cs_track_texture_check(struct radeon_device *rdev, + struct r100_cs_track *track) +{ + struct radeon_bo *robj; + unsigned long size; + unsigned u, i, w, h, d; + int ret; + + for (u = 0; u < track->num_texture; u++) { + if (!track->textures[u].enabled) + continue; + if (track->textures[u].lookup_disable) + continue; + robj = track->textures[u].robj; + if (robj == NULL) { + DRM_ERROR("No texture bound to unit %u\n", u); + return -EINVAL; + } + size = 0; + for (i = 0; i <= track->textures[u].num_levels; i++) { + if (track->textures[u].use_pitch) { + if (rdev->family < CHIP_R300) + w = (track->textures[u].pitch / track->textures[u].cpp) / (1 << i); + else + w = track->textures[u].pitch / (1 << i); + } else { + w = track->textures[u].width; + if (rdev->family >= CHIP_RV515) + w |= track->textures[u].width_11; + w = w / (1 << i); + if (track->textures[u].roundup_w) + w = roundup_pow_of_two(w); + } + h = track->textures[u].height; + if (rdev->family >= CHIP_RV515) + h |= track->textures[u].height_11; + h = h / (1 << i); + if (track->textures[u].roundup_h) + h = roundup_pow_of_two(h); + if (track->textures[u].tex_coord_type == 1) { + d = (1 << track->textures[u].txdepth) / (1 << i); + if (!d) + d = 1; + } else { + d = 1; + } + if (track->textures[u].compress_format) { + + size += r100_track_compress_size(track->textures[u].compress_format, w, h) * d; + /* compressed textures are block based */ + } else + size += w * h * d; + } + size *= track->textures[u].cpp; + + switch (track->textures[u].tex_coord_type) { + case 0: + case 1: + break; + case 2: + if (track->separate_cube) { + ret = r100_cs_track_cube(rdev, track, u); + if (ret) + return ret; + } else + size *= 6; + break; + default: + DRM_ERROR("Invalid texture coordinate type %u for unit " + "%u\n", track->textures[u].tex_coord_type, u); + return -EINVAL; + } + if (size > radeon_bo_size(robj)) { + DRM_ERROR("Texture of unit %u needs %lu bytes but is " + "%lu\n", u, size, radeon_bo_size(robj)); + r100_cs_track_texture_print(&track->textures[u]); + return -EINVAL; + } + } + return 0; +} + +int r100_cs_track_check(struct radeon_device *rdev, struct r100_cs_track *track) +{ + unsigned i; + unsigned long size; + unsigned prim_walk; + unsigned nverts; + unsigned num_cb = track->cb_dirty ? track->num_cb : 0; + + if (num_cb && !track->zb_cb_clear && !track->color_channel_mask && + !track->blend_read_enable) + num_cb = 0; + + for (i = 0; i < num_cb; i++) { + if (track->cb[i].robj == NULL) { + DRM_ERROR("[drm] No buffer for color buffer %d !\n", i); + return -EINVAL; + } + size = track->cb[i].pitch * track->cb[i].cpp * track->maxy; + size += track->cb[i].offset; + if (size > radeon_bo_size(track->cb[i].robj)) { + DRM_ERROR("[drm] Buffer too small for color buffer %d " + "(need %lu have %lu) !\n", i, size, + radeon_bo_size(track->cb[i].robj)); + DRM_ERROR("[drm] color buffer %d (%u %u %u %u)\n", + i, track->cb[i].pitch, track->cb[i].cpp, + track->cb[i].offset, track->maxy); + return -EINVAL; + } + } + track->cb_dirty = false; + + if (track->zb_dirty && track->z_enabled) { + if (track->zb.robj == NULL) { + DRM_ERROR("[drm] No buffer for z buffer !\n"); + return -EINVAL; + } + size = track->zb.pitch * track->zb.cpp * track->maxy; + size += track->zb.offset; + if (size > radeon_bo_size(track->zb.robj)) { + DRM_ERROR("[drm] Buffer too small for z buffer " + "(need %lu have %lu) !\n", size, + radeon_bo_size(track->zb.robj)); + DRM_ERROR("[drm] zbuffer (%u %u %u %u)\n", + track->zb.pitch, track->zb.cpp, + track->zb.offset, track->maxy); + return -EINVAL; + } + } + track->zb_dirty = false; + + if (track->aa_dirty && track->aaresolve) { + if (track->aa.robj == NULL) { + DRM_ERROR("[drm] No buffer for AA resolve buffer %d !\n", i); + return -EINVAL; + } + /* I believe the format comes from colorbuffer0. */ + size = track->aa.pitch * track->cb[0].cpp * track->maxy; + size += track->aa.offset; + if (size > radeon_bo_size(track->aa.robj)) { + DRM_ERROR("[drm] Buffer too small for AA resolve buffer %d " + "(need %lu have %lu) !\n", i, size, + radeon_bo_size(track->aa.robj)); + DRM_ERROR("[drm] AA resolve buffer %d (%u %u %u %u)\n", + i, track->aa.pitch, track->cb[0].cpp, + track->aa.offset, track->maxy); + return -EINVAL; + } + } + track->aa_dirty = false; + + prim_walk = (track->vap_vf_cntl >> 4) & 0x3; + if (track->vap_vf_cntl & (1 << 14)) { + nverts = track->vap_alt_nverts; + } else { + nverts = (track->vap_vf_cntl >> 16) & 0xFFFF; + } + switch (prim_walk) { + case 1: + for (i = 0; i < track->num_arrays; i++) { + size = track->arrays[i].esize * track->max_indx * 4; + if (track->arrays[i].robj == NULL) { + DRM_ERROR("(PW %u) Vertex array %u no buffer " + "bound\n", prim_walk, i); + return -EINVAL; + } + if (size > radeon_bo_size(track->arrays[i].robj)) { + dev_err(rdev->dev, "(PW %u) Vertex array %u " + "need %lu dwords have %lu dwords\n", + prim_walk, i, size >> 2, + radeon_bo_size(track->arrays[i].robj) + >> 2); + DRM_ERROR("Max indices %u\n", track->max_indx); + return -EINVAL; + } + } + break; + case 2: + for (i = 0; i < track->num_arrays; i++) { + size = track->arrays[i].esize * (nverts - 1) * 4; + if (track->arrays[i].robj == NULL) { + DRM_ERROR("(PW %u) Vertex array %u no buffer " + "bound\n", prim_walk, i); + return -EINVAL; + } + if (size > radeon_bo_size(track->arrays[i].robj)) { + dev_err(rdev->dev, "(PW %u) Vertex array %u " + "need %lu dwords have %lu dwords\n", + prim_walk, i, size >> 2, + radeon_bo_size(track->arrays[i].robj) + >> 2); + return -EINVAL; + } + } + break; + case 3: + size = track->vtx_size * nverts; + if (size != track->immd_dwords) { + DRM_ERROR("IMMD draw %u dwors but needs %lu dwords\n", + track->immd_dwords, size); + DRM_ERROR("VAP_VF_CNTL.NUM_VERTICES %u, VTX_SIZE %u\n", + nverts, track->vtx_size); + return -EINVAL; + } + break; + default: + DRM_ERROR("[drm] Invalid primitive walk %d for VAP_VF_CNTL\n", + prim_walk); + return -EINVAL; + } + + if (track->tex_dirty) { + track->tex_dirty = false; + return r100_cs_track_texture_check(rdev, track); + } + return 0; +} + +void r100_cs_track_clear(struct radeon_device *rdev, struct r100_cs_track *track) +{ + unsigned i, face; + + track->cb_dirty = true; + track->zb_dirty = true; + track->tex_dirty = true; + track->aa_dirty = true; + + if (rdev->family < CHIP_R300) { + track->num_cb = 1; + if (rdev->family <= CHIP_RS200) + track->num_texture = 3; + else + track->num_texture = 6; + track->maxy = 2048; + track->separate_cube = 1; + } else { + track->num_cb = 4; + track->num_texture = 16; + track->maxy = 4096; + track->separate_cube = 0; + track->aaresolve = false; + track->aa.robj = NULL; + } + + for (i = 0; i < track->num_cb; i++) { + track->cb[i].robj = NULL; + track->cb[i].pitch = 8192; + track->cb[i].cpp = 16; + track->cb[i].offset = 0; + } + track->z_enabled = true; + track->zb.robj = NULL; + track->zb.pitch = 8192; + track->zb.cpp = 4; + track->zb.offset = 0; + track->vtx_size = 0x7F; + track->immd_dwords = 0xFFFFFFFFUL; + track->num_arrays = 11; + track->max_indx = 0x00FFFFFFUL; + for (i = 0; i < track->num_arrays; i++) { + track->arrays[i].robj = NULL; + track->arrays[i].esize = 0x7F; + } + for (i = 0; i < track->num_texture; i++) { + track->textures[i].compress_format = R100_TRACK_COMP_NONE; + track->textures[i].pitch = 16536; + track->textures[i].width = 16536; + track->textures[i].height = 16536; + track->textures[i].width_11 = 1 << 11; + track->textures[i].height_11 = 1 << 11; + track->textures[i].num_levels = 12; + if (rdev->family <= CHIP_RS200) { + track->textures[i].tex_coord_type = 0; + track->textures[i].txdepth = 0; + } else { + track->textures[i].txdepth = 16; + track->textures[i].tex_coord_type = 1; + } + track->textures[i].cpp = 64; + track->textures[i].robj = NULL; + /* CS IB emission code makes sure texture unit are disabled */ + track->textures[i].enabled = false; + track->textures[i].lookup_disable = false; + track->textures[i].roundup_w = true; + track->textures[i].roundup_h = true; + if (track->separate_cube) + for (face = 0; face < 5; face++) { + track->textures[i].cube_info[face].robj = NULL; + track->textures[i].cube_info[face].width = 16536; + track->textures[i].cube_info[face].height = 16536; + track->textures[i].cube_info[face].offset = 0; + } + } +} + +/* + * Global GPU functions + */ +static void r100_errata(struct radeon_device *rdev) +{ + rdev->pll_errata = 0; + + if (rdev->family == CHIP_RV200 || rdev->family == CHIP_RS200) { + rdev->pll_errata |= CHIP_ERRATA_PLL_DUMMYREADS; + } + + if (rdev->family == CHIP_RV100 || + rdev->family == CHIP_RS100 || + rdev->family == CHIP_RS200) { + rdev->pll_errata |= CHIP_ERRATA_PLL_DELAY; + } +} + +static int r100_rbbm_fifo_wait_for_entry(struct radeon_device *rdev, unsigned n) +{ + unsigned i; + uint32_t tmp; + + for (i = 0; i < rdev->usec_timeout; i++) { + tmp = RREG32(RADEON_RBBM_STATUS) & RADEON_RBBM_FIFOCNT_MASK; + if (tmp >= n) { + return 0; + } + DRM_UDELAY(1); + } + return -1; +} + +int r100_gui_wait_for_idle(struct radeon_device *rdev) +{ + unsigned i; + uint32_t tmp; + + if (r100_rbbm_fifo_wait_for_entry(rdev, 64)) { + DRM_ERROR("radeon: wait for empty RBBM fifo failed !" + " Bad things might happen.\n"); + } + for (i = 0; i < rdev->usec_timeout; i++) { + tmp = RREG32(RADEON_RBBM_STATUS); + if (!(tmp & RADEON_RBBM_ACTIVE)) { + return 0; + } + DRM_UDELAY(1); + } + return -1; +} + +int r100_mc_wait_for_idle(struct radeon_device *rdev) +{ + unsigned i; + uint32_t tmp; + + for (i = 0; i < rdev->usec_timeout; i++) { + /* read MC_STATUS */ + tmp = RREG32(RADEON_MC_STATUS); + if (tmp & RADEON_MC_IDLE) { + return 0; + } + DRM_UDELAY(1); + } + return -1; +} + +bool r100_gpu_is_lockup(struct radeon_device *rdev, struct radeon_ring *ring) +{ + u32 rbbm_status; + + rbbm_status = RREG32(R_000E40_RBBM_STATUS); + if (!G_000E40_GUI_ACTIVE(rbbm_status)) { + radeon_ring_lockup_update(ring); + return false; + } + /* force CP activities */ + radeon_ring_force_activity(rdev, ring); + return radeon_ring_test_lockup(rdev, ring); +} + +/* required on r1xx, r2xx, r300, r(v)350, r420/r481, rs400/rs480 */ +void r100_enable_bm(struct radeon_device *rdev) +{ + uint32_t tmp; + /* Enable bus mastering */ + tmp = RREG32(RADEON_BUS_CNTL) & ~RADEON_BUS_MASTER_DIS; + WREG32(RADEON_BUS_CNTL, tmp); +} + +void r100_bm_disable(struct radeon_device *rdev) +{ + u32 tmp; + + /* disable bus mastering */ + tmp = RREG32(R_000030_BUS_CNTL); + WREG32(R_000030_BUS_CNTL, (tmp & 0xFFFFFFFF) | 0x00000044); + DRM_MDELAY(1); + WREG32(R_000030_BUS_CNTL, (tmp & 0xFFFFFFFF) | 0x00000042); + DRM_MDELAY(1); + WREG32(R_000030_BUS_CNTL, (tmp & 0xFFFFFFFF) | 0x00000040); + tmp = RREG32(RADEON_BUS_CNTL); + DRM_MDELAY(1); + pci_disable_busmaster(rdev->dev); + DRM_MDELAY(1); +} + +int r100_asic_reset(struct radeon_device *rdev) +{ + struct r100_mc_save save; + u32 status, tmp; + int ret = 0; + + status = RREG32(R_000E40_RBBM_STATUS); + if (!G_000E40_GUI_ACTIVE(status)) { + return 0; + } + r100_mc_stop(rdev, &save); + status = RREG32(R_000E40_RBBM_STATUS); + dev_info(rdev->dev, "(%s:%d) RBBM_STATUS=0x%08X\n", __func__, __LINE__, status); + /* stop CP */ + WREG32(RADEON_CP_CSQ_CNTL, 0); + tmp = RREG32(RADEON_CP_RB_CNTL); + WREG32(RADEON_CP_RB_CNTL, tmp | RADEON_RB_RPTR_WR_ENA); + WREG32(RADEON_CP_RB_RPTR_WR, 0); + WREG32(RADEON_CP_RB_WPTR, 0); + WREG32(RADEON_CP_RB_CNTL, tmp); + /* save PCI state */ + pci_save_state(device_get_parent(rdev->dev)); + /* disable bus mastering */ + r100_bm_disable(rdev); + WREG32(R_0000F0_RBBM_SOFT_RESET, S_0000F0_SOFT_RESET_SE(1) | + S_0000F0_SOFT_RESET_RE(1) | + S_0000F0_SOFT_RESET_PP(1) | + S_0000F0_SOFT_RESET_RB(1)); + RREG32(R_0000F0_RBBM_SOFT_RESET); + DRM_MDELAY(500); + WREG32(R_0000F0_RBBM_SOFT_RESET, 0); + DRM_MDELAY(1); + status = RREG32(R_000E40_RBBM_STATUS); + dev_info(rdev->dev, "(%s:%d) RBBM_STATUS=0x%08X\n", __func__, __LINE__, status); + /* reset CP */ + WREG32(R_0000F0_RBBM_SOFT_RESET, S_0000F0_SOFT_RESET_CP(1)); + RREG32(R_0000F0_RBBM_SOFT_RESET); + DRM_MDELAY(500); + WREG32(R_0000F0_RBBM_SOFT_RESET, 0); + DRM_MDELAY(1); + status = RREG32(R_000E40_RBBM_STATUS); + dev_info(rdev->dev, "(%s:%d) RBBM_STATUS=0x%08X\n", __func__, __LINE__, status); + /* restore PCI & busmastering */ + pci_restore_state(device_get_parent(rdev->dev)); + r100_enable_bm(rdev); + /* Check if GPU is idle */ + if (G_000E40_SE_BUSY(status) || G_000E40_RE_BUSY(status) || + G_000E40_TAM_BUSY(status) || G_000E40_PB_BUSY(status)) { + dev_err(rdev->dev, "failed to reset GPU\n"); + ret = -1; + } else + dev_info(rdev->dev, "GPU reset succeed\n"); + r100_mc_resume(rdev, &save); + return ret; +} + +void r100_set_common_regs(struct radeon_device *rdev) +{ + struct drm_device *dev = rdev->ddev; + bool force_dac2 = false; + u32 tmp; + + /* set these so they don't interfere with anything */ + WREG32(RADEON_OV0_SCALE_CNTL, 0); + WREG32(RADEON_SUBPIC_CNTL, 0); + WREG32(RADEON_VIPH_CONTROL, 0); + WREG32(RADEON_I2C_CNTL_1, 0); + WREG32(RADEON_DVI_I2C_CNTL_1, 0); + WREG32(RADEON_CAP0_TRIG_CNTL, 0); + WREG32(RADEON_CAP1_TRIG_CNTL, 0); + + /* always set up dac2 on rn50 and some rv100 as lots + * of servers seem to wire it up to a VGA port but + * don't report it in the bios connector + * table. + */ + switch (dev->pci_device) { + /* RN50 */ + case 0x515e: + case 0x5969: + force_dac2 = true; + break; + /* RV100*/ + case 0x5159: + case 0x515a: + /* DELL triple head servers */ + if ((dev->pci_subvendor == 0x1028 /* DELL */) && + ((dev->pci_subdevice == 0x016c) || + (dev->pci_subdevice == 0x016d) || + (dev->pci_subdevice == 0x016e) || + (dev->pci_subdevice == 0x016f) || + (dev->pci_subdevice == 0x0170) || + (dev->pci_subdevice == 0x017d) || + (dev->pci_subdevice == 0x017e) || + (dev->pci_subdevice == 0x0183) || + (dev->pci_subdevice == 0x018a) || + (dev->pci_subdevice == 0x019a))) + force_dac2 = true; + break; + } + + if (force_dac2) { + u32 disp_hw_debug = RREG32(RADEON_DISP_HW_DEBUG); + u32 tv_dac_cntl = RREG32(RADEON_TV_DAC_CNTL); + u32 dac2_cntl = RREG32(RADEON_DAC_CNTL2); + + /* For CRT on DAC2, don't turn it on if BIOS didn't + enable it, even it's detected. + */ + + /* force it to crtc0 */ + dac2_cntl &= ~RADEON_DAC2_DAC_CLK_SEL; + dac2_cntl |= RADEON_DAC2_DAC2_CLK_SEL; + disp_hw_debug |= RADEON_CRT2_DISP1_SEL; + + /* set up the TV DAC */ + tv_dac_cntl &= ~(RADEON_TV_DAC_PEDESTAL | + RADEON_TV_DAC_STD_MASK | + RADEON_TV_DAC_RDACPD | + RADEON_TV_DAC_GDACPD | + RADEON_TV_DAC_BDACPD | + RADEON_TV_DAC_BGADJ_MASK | + RADEON_TV_DAC_DACADJ_MASK); + tv_dac_cntl |= (RADEON_TV_DAC_NBLANK | + RADEON_TV_DAC_NHOLD | + RADEON_TV_DAC_STD_PS2 | + (0x58 << 16)); + + WREG32(RADEON_TV_DAC_CNTL, tv_dac_cntl); + WREG32(RADEON_DISP_HW_DEBUG, disp_hw_debug); + WREG32(RADEON_DAC_CNTL2, dac2_cntl); + } + + /* switch PM block to ACPI mode */ + tmp = RREG32_PLL(RADEON_PLL_PWRMGT_CNTL); + tmp &= ~RADEON_PM_MODE_SEL; + WREG32_PLL(RADEON_PLL_PWRMGT_CNTL, tmp); + +} + +/* + * VRAM info + */ +static void r100_vram_get_type(struct radeon_device *rdev) +{ + uint32_t tmp; + + rdev->mc.vram_is_ddr = false; + if (rdev->flags & RADEON_IS_IGP) + rdev->mc.vram_is_ddr = true; + else if (RREG32(RADEON_MEM_SDRAM_MODE_REG) & RADEON_MEM_CFG_TYPE_DDR) + rdev->mc.vram_is_ddr = true; + if ((rdev->family == CHIP_RV100) || + (rdev->family == CHIP_RS100) || + (rdev->family == CHIP_RS200)) { + tmp = RREG32(RADEON_MEM_CNTL); + if (tmp & RV100_HALF_MODE) { + rdev->mc.vram_width = 32; + } else { + rdev->mc.vram_width = 64; + } + if (rdev->flags & RADEON_SINGLE_CRTC) { + rdev->mc.vram_width /= 4; + rdev->mc.vram_is_ddr = true; + } + } else if (rdev->family <= CHIP_RV280) { + tmp = RREG32(RADEON_MEM_CNTL); + if (tmp & RADEON_MEM_NUM_CHANNELS_MASK) { + rdev->mc.vram_width = 128; + } else { + rdev->mc.vram_width = 64; + } + } else { + /* newer IGPs */ + rdev->mc.vram_width = 128; + } +} + +static u32 r100_get_accessible_vram(struct radeon_device *rdev) +{ + u32 aper_size; + u8 byte; + + aper_size = RREG32(RADEON_CONFIG_APER_SIZE); + + /* Set HDP_APER_CNTL only on cards that are known not to be broken, + * that is has the 2nd generation multifunction PCI interface + */ + if (rdev->family == CHIP_RV280 || + rdev->family >= CHIP_RV350) { + WREG32_P(RADEON_HOST_PATH_CNTL, RADEON_HDP_APER_CNTL, + ~RADEON_HDP_APER_CNTL); + DRM_INFO("Generation 2 PCI interface, using max accessible memory\n"); + return aper_size * 2; + } + + /* Older cards have all sorts of funny issues to deal with. First + * check if it's a multifunction card by reading the PCI config + * header type... Limit those to one aperture size + */ + byte = pci_read_config(rdev->dev, 0xe, 1); + if (byte & 0x80) { + DRM_INFO("Generation 1 PCI interface in multifunction mode\n"); + DRM_INFO("Limiting VRAM to one aperture\n"); + return aper_size; + } + + /* Single function older card. We read HDP_APER_CNTL to see how the BIOS + * have set it up. We don't write this as it's broken on some ASICs but + * we expect the BIOS to have done the right thing (might be too optimistic...) + */ + if (RREG32(RADEON_HOST_PATH_CNTL) & RADEON_HDP_APER_CNTL) + return aper_size * 2; + return aper_size; +} + +void r100_vram_init_sizes(struct radeon_device *rdev) +{ + u64 config_aper_size; + + /* work out accessible VRAM */ + rdev->mc.aper_base = drm_get_resource_start(rdev->ddev, 0); + rdev->mc.aper_size = drm_get_resource_len(rdev->ddev, 0); + rdev->mc.visible_vram_size = r100_get_accessible_vram(rdev); + /* FIXME we don't use the second aperture yet when we could use it */ + if (rdev->mc.visible_vram_size > rdev->mc.aper_size) + rdev->mc.visible_vram_size = rdev->mc.aper_size; + config_aper_size = RREG32(RADEON_CONFIG_APER_SIZE); + if (rdev->flags & RADEON_IS_IGP) { + uint32_t tom; + /* read NB_TOM to get the amount of ram stolen for the GPU */ + tom = RREG32(RADEON_NB_TOM); + rdev->mc.real_vram_size = (((tom >> 16) - (tom & 0xffff) + 1) << 16); + WREG32(RADEON_CONFIG_MEMSIZE, rdev->mc.real_vram_size); + rdev->mc.mc_vram_size = rdev->mc.real_vram_size; + } else { + rdev->mc.real_vram_size = RREG32(RADEON_CONFIG_MEMSIZE); + /* Some production boards of m6 will report 0 + * if it's 8 MB + */ + if (rdev->mc.real_vram_size == 0) { + rdev->mc.real_vram_size = 8192 * 1024; + WREG32(RADEON_CONFIG_MEMSIZE, rdev->mc.real_vram_size); + } + /* Fix for RN50, M6, M7 with 8/16/32(??) MBs of VRAM - + * Novell bug 204882 + along with lots of ubuntu ones + */ + if (rdev->mc.aper_size > config_aper_size) + config_aper_size = rdev->mc.aper_size; + + if (config_aper_size > rdev->mc.real_vram_size) + rdev->mc.mc_vram_size = config_aper_size; + else + rdev->mc.mc_vram_size = rdev->mc.real_vram_size; + } +} + +void r100_vga_set_state(struct radeon_device *rdev, bool state) +{ + uint32_t temp; + + temp = RREG32(RADEON_CONFIG_CNTL); + if (state == false) { + temp &= ~RADEON_CFG_VGA_RAM_EN; + temp |= RADEON_CFG_VGA_IO_DIS; + } else { + temp &= ~RADEON_CFG_VGA_IO_DIS; + } + WREG32(RADEON_CONFIG_CNTL, temp); +} + +static void r100_mc_init(struct radeon_device *rdev) +{ + u64 base; + + r100_vram_get_type(rdev); + r100_vram_init_sizes(rdev); + base = rdev->mc.aper_base; + if (rdev->flags & RADEON_IS_IGP) + base = (RREG32(RADEON_NB_TOM) & 0xffff) << 16; + radeon_vram_location(rdev, &rdev->mc, base); + rdev->mc.gtt_base_align = 0; + if (!(rdev->flags & RADEON_IS_AGP)) + radeon_gtt_location(rdev, &rdev->mc); + radeon_update_bandwidth_info(rdev); +} + + +/* + * Indirect registers accessor + */ +void r100_pll_errata_after_index(struct radeon_device *rdev) +{ + if (rdev->pll_errata & CHIP_ERRATA_PLL_DUMMYREADS) { + (void)RREG32(RADEON_CLOCK_CNTL_DATA); + (void)RREG32(RADEON_CRTC_GEN_CNTL); + } +} + +static void r100_pll_errata_after_data(struct radeon_device *rdev) +{ + /* This workarounds is necessary on RV100, RS100 and RS200 chips + * or the chip could hang on a subsequent access + */ + if (rdev->pll_errata & CHIP_ERRATA_PLL_DELAY) { + DRM_MDELAY(5); + } + + /* This function is required to workaround a hardware bug in some (all?) + * revisions of the R300. This workaround should be called after every + * CLOCK_CNTL_INDEX register access. If not, register reads afterward + * may not be correct. + */ + if (rdev->pll_errata & CHIP_ERRATA_R300_CG) { + uint32_t save, tmp; + + save = RREG32(RADEON_CLOCK_CNTL_INDEX); + tmp = save & ~(0x3f | RADEON_PLL_WR_EN); + WREG32(RADEON_CLOCK_CNTL_INDEX, tmp); + tmp = RREG32(RADEON_CLOCK_CNTL_DATA); + WREG32(RADEON_CLOCK_CNTL_INDEX, save); + } +} + +uint32_t r100_pll_rreg(struct radeon_device *rdev, uint32_t reg) +{ + uint32_t data; + + WREG8(RADEON_CLOCK_CNTL_INDEX, reg & 0x3f); + r100_pll_errata_after_index(rdev); + data = RREG32(RADEON_CLOCK_CNTL_DATA); + r100_pll_errata_after_data(rdev); + return data; +} + +void r100_pll_wreg(struct radeon_device *rdev, uint32_t reg, uint32_t v) +{ + WREG8(RADEON_CLOCK_CNTL_INDEX, ((reg & 0x3f) | RADEON_PLL_WR_EN)); + r100_pll_errata_after_index(rdev); + WREG32(RADEON_CLOCK_CNTL_DATA, v); + r100_pll_errata_after_data(rdev); +} + +static void r100_set_safe_registers(struct radeon_device *rdev) +{ + if (ASIC_IS_RN50(rdev)) { + rdev->config.r100.reg_safe_bm = rn50_reg_safe_bm; + rdev->config.r100.reg_safe_bm_size = DRM_ARRAY_SIZE(rn50_reg_safe_bm); + } else if (rdev->family < CHIP_R200) { + rdev->config.r100.reg_safe_bm = r100_reg_safe_bm; + rdev->config.r100.reg_safe_bm_size = DRM_ARRAY_SIZE(r100_reg_safe_bm); + } else { + r200_set_safe_registers(rdev); + } +} + +/* + * Debugfs info + */ +#if defined(CONFIG_DEBUG_FS) +static int r100_debugfs_rbbm_info(struct seq_file *m, void *data) +{ + struct drm_info_node *node = (struct drm_info_node *) m->private; + struct drm_device *dev = node->minor->dev; + struct radeon_device *rdev = dev->dev_private; + uint32_t reg, value; + unsigned i; + + seq_printf(m, "RBBM_STATUS 0x%08x\n", RREG32(RADEON_RBBM_STATUS)); + seq_printf(m, "RBBM_CMDFIFO_STAT 0x%08x\n", RREG32(0xE7C)); + seq_printf(m, "CP_STAT 0x%08x\n", RREG32(RADEON_CP_STAT)); + for (i = 0; i < 64; i++) { + WREG32(RADEON_RBBM_CMDFIFO_ADDR, i | 0x100); + reg = (RREG32(RADEON_RBBM_CMDFIFO_DATA) - 1) >> 2; + WREG32(RADEON_RBBM_CMDFIFO_ADDR, i); + value = RREG32(RADEON_RBBM_CMDFIFO_DATA); + seq_printf(m, "[0x%03X] 0x%04X=0x%08X\n", i, reg, value); + } + return 0; +} + +static int r100_debugfs_cp_ring_info(struct seq_file *m, void *data) +{ + struct drm_info_node *node = (struct drm_info_node *) m->private; + struct drm_device *dev = node->minor->dev; + struct radeon_device *rdev = dev->dev_private; + struct radeon_ring *ring = &rdev->ring[RADEON_RING_TYPE_GFX_INDEX]; + uint32_t rdp, wdp; + unsigned count, i, j; + + radeon_ring_free_size(rdev, ring); + rdp = RREG32(RADEON_CP_RB_RPTR); + wdp = RREG32(RADEON_CP_RB_WPTR); + count = (rdp + ring->ring_size - wdp) & ring->ptr_mask; + seq_printf(m, "CP_STAT 0x%08x\n", RREG32(RADEON_CP_STAT)); + seq_printf(m, "CP_RB_WPTR 0x%08x\n", wdp); + seq_printf(m, "CP_RB_RPTR 0x%08x\n", rdp); + seq_printf(m, "%u free dwords in ring\n", ring->ring_free_dw); + seq_printf(m, "%u dwords in ring\n", count); + for (j = 0; j <= count; j++) { + i = (rdp + j) & ring->ptr_mask; + seq_printf(m, "r[%04d]=0x%08x\n", i, ring->ring[i]); + } + return 0; +} + + +static int r100_debugfs_cp_csq_fifo(struct seq_file *m, void *data) +{ + struct drm_info_node *node = (struct drm_info_node *) m->private; + struct drm_device *dev = node->minor->dev; + struct radeon_device *rdev = dev->dev_private; + uint32_t csq_stat, csq2_stat, tmp; + unsigned r_rptr, r_wptr, ib1_rptr, ib1_wptr, ib2_rptr, ib2_wptr; + unsigned i; + + seq_printf(m, "CP_STAT 0x%08x\n", RREG32(RADEON_CP_STAT)); + seq_printf(m, "CP_CSQ_MODE 0x%08x\n", RREG32(RADEON_CP_CSQ_MODE)); + csq_stat = RREG32(RADEON_CP_CSQ_STAT); + csq2_stat = RREG32(RADEON_CP_CSQ2_STAT); + r_rptr = (csq_stat >> 0) & 0x3ff; + r_wptr = (csq_stat >> 10) & 0x3ff; + ib1_rptr = (csq_stat >> 20) & 0x3ff; + ib1_wptr = (csq2_stat >> 0) & 0x3ff; + ib2_rptr = (csq2_stat >> 10) & 0x3ff; + ib2_wptr = (csq2_stat >> 20) & 0x3ff; + seq_printf(m, "CP_CSQ_STAT 0x%08x\n", csq_stat); + seq_printf(m, "CP_CSQ2_STAT 0x%08x\n", csq2_stat); + seq_printf(m, "Ring rptr %u\n", r_rptr); + seq_printf(m, "Ring wptr %u\n", r_wptr); + seq_printf(m, "Indirect1 rptr %u\n", ib1_rptr); + seq_printf(m, "Indirect1 wptr %u\n", ib1_wptr); + seq_printf(m, "Indirect2 rptr %u\n", ib2_rptr); + seq_printf(m, "Indirect2 wptr %u\n", ib2_wptr); + /* FIXME: 0, 128, 640 depends on fifo setup see cp_init_kms + * 128 = indirect1_start * 8 & 640 = indirect2_start * 8 */ + seq_printf(m, "Ring fifo:\n"); + for (i = 0; i < 256; i++) { + WREG32(RADEON_CP_CSQ_ADDR, i << 2); + tmp = RREG32(RADEON_CP_CSQ_DATA); + seq_printf(m, "rfifo[%04d]=0x%08X\n", i, tmp); + } + seq_printf(m, "Indirect1 fifo:\n"); + for (i = 256; i <= 512; i++) { + WREG32(RADEON_CP_CSQ_ADDR, i << 2); + tmp = RREG32(RADEON_CP_CSQ_DATA); + seq_printf(m, "ib1fifo[%04d]=0x%08X\n", i, tmp); + } + seq_printf(m, "Indirect2 fifo:\n"); + for (i = 640; i < ib1_wptr; i++) { + WREG32(RADEON_CP_CSQ_ADDR, i << 2); + tmp = RREG32(RADEON_CP_CSQ_DATA); + seq_printf(m, "ib2fifo[%04d]=0x%08X\n", i, tmp); + } + return 0; +} + +static int r100_debugfs_mc_info(struct seq_file *m, void *data) +{ + struct drm_info_node *node = (struct drm_info_node *) m->private; + struct drm_device *dev = node->minor->dev; + struct radeon_device *rdev = dev->dev_private; + uint32_t tmp; + + tmp = RREG32(RADEON_CONFIG_MEMSIZE); + seq_printf(m, "CONFIG_MEMSIZE 0x%08x\n", tmp); + tmp = RREG32(RADEON_MC_FB_LOCATION); + seq_printf(m, "MC_FB_LOCATION 0x%08x\n", tmp); + tmp = RREG32(RADEON_BUS_CNTL); + seq_printf(m, "BUS_CNTL 0x%08x\n", tmp); + tmp = RREG32(RADEON_MC_AGP_LOCATION); + seq_printf(m, "MC_AGP_LOCATION 0x%08x\n", tmp); + tmp = RREG32(RADEON_AGP_BASE); + seq_printf(m, "AGP_BASE 0x%08x\n", tmp); + tmp = RREG32(RADEON_HOST_PATH_CNTL); + seq_printf(m, "HOST_PATH_CNTL 0x%08x\n", tmp); + tmp = RREG32(0x01D0); + seq_printf(m, "AIC_CTRL 0x%08x\n", tmp); + tmp = RREG32(RADEON_AIC_LO_ADDR); + seq_printf(m, "AIC_LO_ADDR 0x%08x\n", tmp); + tmp = RREG32(RADEON_AIC_HI_ADDR); + seq_printf(m, "AIC_HI_ADDR 0x%08x\n", tmp); + tmp = RREG32(0x01E4); + seq_printf(m, "AIC_TLB_ADDR 0x%08x\n", tmp); + return 0; +} + +static struct drm_info_list r100_debugfs_rbbm_list[] = { + {"r100_rbbm_info", r100_debugfs_rbbm_info, 0, NULL}, +}; + +static struct drm_info_list r100_debugfs_cp_list[] = { + {"r100_cp_ring_info", r100_debugfs_cp_ring_info, 0, NULL}, + {"r100_cp_csq_fifo", r100_debugfs_cp_csq_fifo, 0, NULL}, +}; + +static struct drm_info_list r100_debugfs_mc_info_list[] = { + {"r100_mc_info", r100_debugfs_mc_info, 0, NULL}, +}; +#endif + +int r100_debugfs_rbbm_init(struct radeon_device *rdev) +{ +#if defined(CONFIG_DEBUG_FS) + return radeon_debugfs_add_files(rdev, r100_debugfs_rbbm_list, 1); +#else + return 0; +#endif +} + +int r100_debugfs_cp_init(struct radeon_device *rdev) +{ +#if defined(CONFIG_DEBUG_FS) + return radeon_debugfs_add_files(rdev, r100_debugfs_cp_list, 2); +#else + return 0; +#endif +} + +int r100_debugfs_mc_info_init(struct radeon_device *rdev) +{ +#if defined(CONFIG_DEBUG_FS) + return radeon_debugfs_add_files(rdev, r100_debugfs_mc_info_list, 1); +#else + return 0; +#endif +} + +int r100_set_surface_reg(struct radeon_device *rdev, int reg, + uint32_t tiling_flags, uint32_t pitch, + uint32_t offset, uint32_t obj_size) +{ + int surf_index = reg * 16; + int flags = 0; + + if (rdev->family <= CHIP_RS200) { + if ((tiling_flags & (RADEON_TILING_MACRO|RADEON_TILING_MICRO)) + == (RADEON_TILING_MACRO|RADEON_TILING_MICRO)) + flags |= RADEON_SURF_TILE_COLOR_BOTH; + if (tiling_flags & RADEON_TILING_MACRO) + flags |= RADEON_SURF_TILE_COLOR_MACRO; + } else if (rdev->family <= CHIP_RV280) { + if (tiling_flags & (RADEON_TILING_MACRO)) + flags |= R200_SURF_TILE_COLOR_MACRO; + if (tiling_flags & RADEON_TILING_MICRO) + flags |= R200_SURF_TILE_COLOR_MICRO; + } else { + if (tiling_flags & RADEON_TILING_MACRO) + flags |= R300_SURF_TILE_MACRO; + if (tiling_flags & RADEON_TILING_MICRO) + flags |= R300_SURF_TILE_MICRO; + } + + if (tiling_flags & RADEON_TILING_SWAP_16BIT) + flags |= RADEON_SURF_AP0_SWP_16BPP | RADEON_SURF_AP1_SWP_16BPP; + if (tiling_flags & RADEON_TILING_SWAP_32BIT) + flags |= RADEON_SURF_AP0_SWP_32BPP | RADEON_SURF_AP1_SWP_32BPP; + + /* when we aren't tiling the pitch seems to needs to be furtherdivided down. - tested on power5 + rn50 server */ + if (tiling_flags & (RADEON_TILING_SWAP_16BIT | RADEON_TILING_SWAP_32BIT)) { + if (!(tiling_flags & (RADEON_TILING_MACRO | RADEON_TILING_MICRO))) + if (ASIC_IS_RN50(rdev)) + pitch /= 16; + } + + /* r100/r200 divide by 16 */ + if (rdev->family < CHIP_R300) + flags |= pitch / 16; + else + flags |= pitch / 8; + + + DRM_DEBUG_KMS("writing surface %d %d %x %x\n", reg, flags, offset, offset+obj_size-1); + WREG32(RADEON_SURFACE0_INFO + surf_index, flags); + WREG32(RADEON_SURFACE0_LOWER_BOUND + surf_index, offset); + WREG32(RADEON_SURFACE0_UPPER_BOUND + surf_index, offset + obj_size - 1); + return 0; +} + +void r100_clear_surface_reg(struct radeon_device *rdev, int reg) +{ + int surf_index = reg * 16; + WREG32(RADEON_SURFACE0_INFO + surf_index, 0); +} + +void r100_bandwidth_update(struct radeon_device *rdev) +{ + fixed20_12 trcd_ff, trp_ff, tras_ff, trbs_ff, tcas_ff; + fixed20_12 sclk_ff, mclk_ff, sclk_eff_ff, sclk_delay_ff; + fixed20_12 peak_disp_bw, mem_bw, pix_clk, pix_clk2, temp_ff, crit_point_ff; + uint32_t temp, data, mem_trcd, mem_trp, mem_tras; + fixed20_12 memtcas_ff[8] = { + dfixed_init(1), + dfixed_init(2), + dfixed_init(3), + dfixed_init(0), + dfixed_init_half(1), + dfixed_init_half(2), + dfixed_init(0), + }; + fixed20_12 memtcas_rs480_ff[8] = { + dfixed_init(0), + dfixed_init(1), + dfixed_init(2), + dfixed_init(3), + dfixed_init(0), + dfixed_init_half(1), + dfixed_init_half(2), + dfixed_init_half(3), + }; + fixed20_12 memtcas2_ff[8] = { + dfixed_init(0), + dfixed_init(1), + dfixed_init(2), + dfixed_init(3), + dfixed_init(4), + dfixed_init(5), + dfixed_init(6), + dfixed_init(7), + }; + fixed20_12 memtrbs[8] = { + dfixed_init(1), + dfixed_init_half(1), + dfixed_init(2), + dfixed_init_half(2), + dfixed_init(3), + dfixed_init_half(3), + dfixed_init(4), + dfixed_init_half(4) + }; + fixed20_12 memtrbs_r4xx[8] = { + dfixed_init(4), + dfixed_init(5), + dfixed_init(6), + dfixed_init(7), + dfixed_init(8), + dfixed_init(9), + dfixed_init(10), + dfixed_init(11) + }; + fixed20_12 min_mem_eff; + fixed20_12 mc_latency_sclk, mc_latency_mclk, k1; + fixed20_12 cur_latency_mclk, cur_latency_sclk; + fixed20_12 disp_latency, disp_latency_overhead, disp_drain_rate, + disp_drain_rate2, read_return_rate; + fixed20_12 time_disp1_drop_priority; + int c; + int cur_size = 16; /* in octawords */ + int critical_point = 0, critical_point2; +/* uint32_t read_return_rate, time_disp1_drop_priority; */ + int stop_req, max_stop_req; + struct drm_display_mode *mode1 = NULL; + struct drm_display_mode *mode2 = NULL; + uint32_t pixel_bytes1 = 0; + uint32_t pixel_bytes2 = 0; + + radeon_update_display_priority(rdev); + + if (rdev->mode_info.crtcs[0]->base.enabled) { + mode1 = &rdev->mode_info.crtcs[0]->base.mode; + pixel_bytes1 = rdev->mode_info.crtcs[0]->base.fb->bits_per_pixel / 8; + } + if (!(rdev->flags & RADEON_SINGLE_CRTC)) { + if (rdev->mode_info.crtcs[1]->base.enabled) { + mode2 = &rdev->mode_info.crtcs[1]->base.mode; + pixel_bytes2 = rdev->mode_info.crtcs[1]->base.fb->bits_per_pixel / 8; + } + } + + min_mem_eff.full = dfixed_const_8(0); + /* get modes */ + if ((rdev->disp_priority == 2) && ASIC_IS_R300(rdev)) { + uint32_t mc_init_misc_lat_timer = RREG32(R300_MC_INIT_MISC_LAT_TIMER); + mc_init_misc_lat_timer &= ~(R300_MC_DISP1R_INIT_LAT_MASK << R300_MC_DISP1R_INIT_LAT_SHIFT); + mc_init_misc_lat_timer &= ~(R300_MC_DISP0R_INIT_LAT_MASK << R300_MC_DISP0R_INIT_LAT_SHIFT); + /* check crtc enables */ + if (mode2) + mc_init_misc_lat_timer |= (1 << R300_MC_DISP1R_INIT_LAT_SHIFT); + if (mode1) + mc_init_misc_lat_timer |= (1 << R300_MC_DISP0R_INIT_LAT_SHIFT); + WREG32(R300_MC_INIT_MISC_LAT_TIMER, mc_init_misc_lat_timer); + } + + /* + * determine is there is enough bw for current mode + */ + sclk_ff = rdev->pm.sclk; + mclk_ff = rdev->pm.mclk; + + temp = (rdev->mc.vram_width / 8) * (rdev->mc.vram_is_ddr ? 2 : 1); + temp_ff.full = dfixed_const(temp); + mem_bw.full = dfixed_mul(mclk_ff, temp_ff); + + pix_clk.full = 0; + pix_clk2.full = 0; + peak_disp_bw.full = 0; + if (mode1) { + temp_ff.full = dfixed_const(1000); + pix_clk.full = dfixed_const(mode1->clock); /* convert to fixed point */ + pix_clk.full = dfixed_div(pix_clk, temp_ff); + temp_ff.full = dfixed_const(pixel_bytes1); + peak_disp_bw.full += dfixed_mul(pix_clk, temp_ff); + } + if (mode2) { + temp_ff.full = dfixed_const(1000); + pix_clk2.full = dfixed_const(mode2->clock); /* convert to fixed point */ + pix_clk2.full = dfixed_div(pix_clk2, temp_ff); + temp_ff.full = dfixed_const(pixel_bytes2); + peak_disp_bw.full += dfixed_mul(pix_clk2, temp_ff); + } + + mem_bw.full = dfixed_mul(mem_bw, min_mem_eff); + if (peak_disp_bw.full >= mem_bw.full) { + DRM_ERROR("You may not have enough display bandwidth for current mode\n" + "If you have flickering problem, try to lower resolution, refresh rate, or color depth\n"); + } + + /* Get values from the EXT_MEM_CNTL register...converting its contents. */ + temp = RREG32(RADEON_MEM_TIMING_CNTL); + if ((rdev->family == CHIP_RV100) || (rdev->flags & RADEON_IS_IGP)) { /* RV100, M6, IGPs */ + mem_trcd = ((temp >> 2) & 0x3) + 1; + mem_trp = ((temp & 0x3)) + 1; + mem_tras = ((temp & 0x70) >> 4) + 1; + } else if (rdev->family == CHIP_R300 || + rdev->family == CHIP_R350) { /* r300, r350 */ + mem_trcd = (temp & 0x7) + 1; + mem_trp = ((temp >> 8) & 0x7) + 1; + mem_tras = ((temp >> 11) & 0xf) + 4; + } else if (rdev->family == CHIP_RV350 || + rdev->family <= CHIP_RV380) { + /* rv3x0 */ + mem_trcd = (temp & 0x7) + 3; + mem_trp = ((temp >> 8) & 0x7) + 3; + mem_tras = ((temp >> 11) & 0xf) + 6; + } else if (rdev->family == CHIP_R420 || + rdev->family == CHIP_R423 || + rdev->family == CHIP_RV410) { + /* r4xx */ + mem_trcd = (temp & 0xf) + 3; + if (mem_trcd > 15) + mem_trcd = 15; + mem_trp = ((temp >> 8) & 0xf) + 3; + if (mem_trp > 15) + mem_trp = 15; + mem_tras = ((temp >> 12) & 0x1f) + 6; + if (mem_tras > 31) + mem_tras = 31; + } else { /* RV200, R200 */ + mem_trcd = (temp & 0x7) + 1; + mem_trp = ((temp >> 8) & 0x7) + 1; + mem_tras = ((temp >> 12) & 0xf) + 4; + } + /* convert to FF */ + trcd_ff.full = dfixed_const(mem_trcd); + trp_ff.full = dfixed_const(mem_trp); + tras_ff.full = dfixed_const(mem_tras); + + /* Get values from the MEM_SDRAM_MODE_REG register...converting its */ + temp = RREG32(RADEON_MEM_SDRAM_MODE_REG); + data = (temp & (7 << 20)) >> 20; + if ((rdev->family == CHIP_RV100) || rdev->flags & RADEON_IS_IGP) { + if (rdev->family == CHIP_RS480) /* don't think rs400 */ + tcas_ff = memtcas_rs480_ff[data]; + else + tcas_ff = memtcas_ff[data]; + } else + tcas_ff = memtcas2_ff[data]; + + if (rdev->family == CHIP_RS400 || + rdev->family == CHIP_RS480) { + /* extra cas latency stored in bits 23-25 0-4 clocks */ + data = (temp >> 23) & 0x7; + if (data < 5) + tcas_ff.full += dfixed_const(data); + } + + if (ASIC_IS_R300(rdev) && !(rdev->flags & RADEON_IS_IGP)) { + /* on the R300, Tcas is included in Trbs. + */ + temp = RREG32(RADEON_MEM_CNTL); + data = (R300_MEM_NUM_CHANNELS_MASK & temp); + if (data == 1) { + if (R300_MEM_USE_CD_CH_ONLY & temp) { + temp = RREG32(R300_MC_IND_INDEX); + temp &= ~R300_MC_IND_ADDR_MASK; + temp |= R300_MC_READ_CNTL_CD_mcind; + WREG32(R300_MC_IND_INDEX, temp); + temp = RREG32(R300_MC_IND_DATA); + data = (R300_MEM_RBS_POSITION_C_MASK & temp); + } else { + temp = RREG32(R300_MC_READ_CNTL_AB); + data = (R300_MEM_RBS_POSITION_A_MASK & temp); + } + } else { + temp = RREG32(R300_MC_READ_CNTL_AB); + data = (R300_MEM_RBS_POSITION_A_MASK & temp); + } + if (rdev->family == CHIP_RV410 || + rdev->family == CHIP_R420 || + rdev->family == CHIP_R423) + trbs_ff = memtrbs_r4xx[data]; + else + trbs_ff = memtrbs[data]; + tcas_ff.full += trbs_ff.full; + } + + sclk_eff_ff.full = sclk_ff.full; + + if (rdev->flags & RADEON_IS_AGP) { + fixed20_12 agpmode_ff; + agpmode_ff.full = dfixed_const(radeon_agpmode); + temp_ff.full = dfixed_const_666(16); + sclk_eff_ff.full -= dfixed_mul(agpmode_ff, temp_ff); + } + /* TODO PCIE lanes may affect this - agpmode == 16?? */ + + if (ASIC_IS_R300(rdev)) { + sclk_delay_ff.full = dfixed_const(250); + } else { + if ((rdev->family == CHIP_RV100) || + rdev->flags & RADEON_IS_IGP) { + if (rdev->mc.vram_is_ddr) + sclk_delay_ff.full = dfixed_const(41); + else + sclk_delay_ff.full = dfixed_const(33); + } else { + if (rdev->mc.vram_width == 128) + sclk_delay_ff.full = dfixed_const(57); + else + sclk_delay_ff.full = dfixed_const(41); + } + } + + mc_latency_sclk.full = dfixed_div(sclk_delay_ff, sclk_eff_ff); + + if (rdev->mc.vram_is_ddr) { + if (rdev->mc.vram_width == 32) { + k1.full = dfixed_const(40); + c = 3; + } else { + k1.full = dfixed_const(20); + c = 1; + } + } else { + k1.full = dfixed_const(40); + c = 3; + } + + temp_ff.full = dfixed_const(2); + mc_latency_mclk.full = dfixed_mul(trcd_ff, temp_ff); + temp_ff.full = dfixed_const(c); + mc_latency_mclk.full += dfixed_mul(tcas_ff, temp_ff); + temp_ff.full = dfixed_const(4); + mc_latency_mclk.full += dfixed_mul(tras_ff, temp_ff); + mc_latency_mclk.full += dfixed_mul(trp_ff, temp_ff); + mc_latency_mclk.full += k1.full; + + mc_latency_mclk.full = dfixed_div(mc_latency_mclk, mclk_ff); + mc_latency_mclk.full += dfixed_div(temp_ff, sclk_eff_ff); + + /* + HW cursor time assuming worst case of full size colour cursor. + */ + temp_ff.full = dfixed_const((2 * (cur_size - (rdev->mc.vram_is_ddr + 1)))); + temp_ff.full += trcd_ff.full; + if (temp_ff.full < tras_ff.full) + temp_ff.full = tras_ff.full; + cur_latency_mclk.full = dfixed_div(temp_ff, mclk_ff); + + temp_ff.full = dfixed_const(cur_size); + cur_latency_sclk.full = dfixed_div(temp_ff, sclk_eff_ff); + /* + Find the total latency for the display data. + */ + disp_latency_overhead.full = dfixed_const(8); + disp_latency_overhead.full = dfixed_div(disp_latency_overhead, sclk_ff); + mc_latency_mclk.full += disp_latency_overhead.full + cur_latency_mclk.full; + mc_latency_sclk.full += disp_latency_overhead.full + cur_latency_sclk.full; + + if (mc_latency_mclk.full > mc_latency_sclk.full) + disp_latency.full = mc_latency_mclk.full; + else + disp_latency.full = mc_latency_sclk.full; + + /* setup Max GRPH_STOP_REQ default value */ + if (ASIC_IS_RV100(rdev)) + max_stop_req = 0x5c; + else + max_stop_req = 0x7c; + + if (mode1) { + /* CRTC1 + Set GRPH_BUFFER_CNTL register using h/w defined optimal values. + GRPH_STOP_REQ <= MIN[ 0x7C, (CRTC_H_DISP + 1) * (bit depth) / 0x10 ] + */ + stop_req = mode1->hdisplay * pixel_bytes1 / 16; + + if (stop_req > max_stop_req) + stop_req = max_stop_req; + + /* + Find the drain rate of the display buffer. + */ + temp_ff.full = dfixed_const((16/pixel_bytes1)); + disp_drain_rate.full = dfixed_div(pix_clk, temp_ff); + + /* + Find the critical point of the display buffer. + */ + crit_point_ff.full = dfixed_mul(disp_drain_rate, disp_latency); + crit_point_ff.full += dfixed_const_half(0); + + critical_point = dfixed_trunc(crit_point_ff); + + if (rdev->disp_priority == 2) { + critical_point = 0; + } + + /* + The critical point should never be above max_stop_req-4. Setting + GRPH_CRITICAL_CNTL = 0 will thus force high priority all the time. + */ + if (max_stop_req - critical_point < 4) + critical_point = 0; + + if (critical_point == 0 && mode2 && rdev->family == CHIP_R300) { + /* some R300 cards have problem with this set to 0, when CRTC2 is enabled.*/ + critical_point = 0x10; + } + + temp = RREG32(RADEON_GRPH_BUFFER_CNTL); + temp &= ~(RADEON_GRPH_STOP_REQ_MASK); + temp |= (stop_req << RADEON_GRPH_STOP_REQ_SHIFT); + temp &= ~(RADEON_GRPH_START_REQ_MASK); + if ((rdev->family == CHIP_R350) && + (stop_req > 0x15)) { + stop_req -= 0x10; + } + temp |= (stop_req << RADEON_GRPH_START_REQ_SHIFT); + temp |= RADEON_GRPH_BUFFER_SIZE; + temp &= ~(RADEON_GRPH_CRITICAL_CNTL | + RADEON_GRPH_CRITICAL_AT_SOF | + RADEON_GRPH_STOP_CNTL); + /* + Write the result into the register. + */ + WREG32(RADEON_GRPH_BUFFER_CNTL, ((temp & ~RADEON_GRPH_CRITICAL_POINT_MASK) | + (critical_point << RADEON_GRPH_CRITICAL_POINT_SHIFT))); + +#if 0 + if ((rdev->family == CHIP_RS400) || + (rdev->family == CHIP_RS480)) { + /* attempt to program RS400 disp regs correctly ??? */ + temp = RREG32(RS400_DISP1_REG_CNTL); + temp &= ~(RS400_DISP1_START_REQ_LEVEL_MASK | + RS400_DISP1_STOP_REQ_LEVEL_MASK); + WREG32(RS400_DISP1_REQ_CNTL1, (temp | + (critical_point << RS400_DISP1_START_REQ_LEVEL_SHIFT) | + (critical_point << RS400_DISP1_STOP_REQ_LEVEL_SHIFT))); + temp = RREG32(RS400_DMIF_MEM_CNTL1); + temp &= ~(RS400_DISP1_CRITICAL_POINT_START_MASK | + RS400_DISP1_CRITICAL_POINT_STOP_MASK); + WREG32(RS400_DMIF_MEM_CNTL1, (temp | + (critical_point << RS400_DISP1_CRITICAL_POINT_START_SHIFT) | + (critical_point << RS400_DISP1_CRITICAL_POINT_STOP_SHIFT))); + } +#endif + + DRM_DEBUG_KMS("GRPH_BUFFER_CNTL from to %x\n", + /* (unsigned int)info->SavedReg->grph_buffer_cntl, */ + (unsigned int)RREG32(RADEON_GRPH_BUFFER_CNTL)); + } + + if (mode2) { + u32 grph2_cntl; + stop_req = mode2->hdisplay * pixel_bytes2 / 16; + + if (stop_req > max_stop_req) + stop_req = max_stop_req; + + /* + Find the drain rate of the display buffer. + */ + temp_ff.full = dfixed_const((16/pixel_bytes2)); + disp_drain_rate2.full = dfixed_div(pix_clk2, temp_ff); + + grph2_cntl = RREG32(RADEON_GRPH2_BUFFER_CNTL); + grph2_cntl &= ~(RADEON_GRPH_STOP_REQ_MASK); + grph2_cntl |= (stop_req << RADEON_GRPH_STOP_REQ_SHIFT); + grph2_cntl &= ~(RADEON_GRPH_START_REQ_MASK); + if ((rdev->family == CHIP_R350) && + (stop_req > 0x15)) { + stop_req -= 0x10; + } + grph2_cntl |= (stop_req << RADEON_GRPH_START_REQ_SHIFT); + grph2_cntl |= RADEON_GRPH_BUFFER_SIZE; + grph2_cntl &= ~(RADEON_GRPH_CRITICAL_CNTL | + RADEON_GRPH_CRITICAL_AT_SOF | + RADEON_GRPH_STOP_CNTL); + + if ((rdev->family == CHIP_RS100) || + (rdev->family == CHIP_RS200)) + critical_point2 = 0; + else { + temp = (rdev->mc.vram_width * rdev->mc.vram_is_ddr + 1)/128; + temp_ff.full = dfixed_const(temp); + temp_ff.full = dfixed_mul(mclk_ff, temp_ff); + if (sclk_ff.full < temp_ff.full) + temp_ff.full = sclk_ff.full; + + read_return_rate.full = temp_ff.full; + + if (mode1) { + temp_ff.full = read_return_rate.full - disp_drain_rate.full; + time_disp1_drop_priority.full = dfixed_div(crit_point_ff, temp_ff); + } else { + time_disp1_drop_priority.full = 0; + } + crit_point_ff.full = disp_latency.full + time_disp1_drop_priority.full + disp_latency.full; + crit_point_ff.full = dfixed_mul(crit_point_ff, disp_drain_rate2); + crit_point_ff.full += dfixed_const_half(0); + + critical_point2 = dfixed_trunc(crit_point_ff); + + if (rdev->disp_priority == 2) { + critical_point2 = 0; + } + + if (max_stop_req - critical_point2 < 4) + critical_point2 = 0; + + } + + if (critical_point2 == 0 && rdev->family == CHIP_R300) { + /* some R300 cards have problem with this set to 0 */ + critical_point2 = 0x10; + } + + WREG32(RADEON_GRPH2_BUFFER_CNTL, ((grph2_cntl & ~RADEON_GRPH_CRITICAL_POINT_MASK) | + (critical_point2 << RADEON_GRPH_CRITICAL_POINT_SHIFT))); + + if ((rdev->family == CHIP_RS400) || + (rdev->family == CHIP_RS480)) { +#if 0 + /* attempt to program RS400 disp2 regs correctly ??? */ + temp = RREG32(RS400_DISP2_REQ_CNTL1); + temp &= ~(RS400_DISP2_START_REQ_LEVEL_MASK | + RS400_DISP2_STOP_REQ_LEVEL_MASK); + WREG32(RS400_DISP2_REQ_CNTL1, (temp | + (critical_point2 << RS400_DISP1_START_REQ_LEVEL_SHIFT) | + (critical_point2 << RS400_DISP1_STOP_REQ_LEVEL_SHIFT))); + temp = RREG32(RS400_DISP2_REQ_CNTL2); + temp &= ~(RS400_DISP2_CRITICAL_POINT_START_MASK | + RS400_DISP2_CRITICAL_POINT_STOP_MASK); + WREG32(RS400_DISP2_REQ_CNTL2, (temp | + (critical_point2 << RS400_DISP2_CRITICAL_POINT_START_SHIFT) | + (critical_point2 << RS400_DISP2_CRITICAL_POINT_STOP_SHIFT))); +#endif + WREG32(RS400_DISP2_REQ_CNTL1, 0x105DC1CC); + WREG32(RS400_DISP2_REQ_CNTL2, 0x2749D000); + WREG32(RS400_DMIF_MEM_CNTL1, 0x29CA71DC); + WREG32(RS400_DISP1_REQ_CNTL1, 0x28FBC3AC); + } + + DRM_DEBUG_KMS("GRPH2_BUFFER_CNTL from to %x\n", + (unsigned int)RREG32(RADEON_GRPH2_BUFFER_CNTL)); + } +} + +int r100_ring_test(struct radeon_device *rdev, struct radeon_ring *ring) +{ + uint32_t scratch; + uint32_t tmp = 0; + unsigned i; + int r; + + r = radeon_scratch_get(rdev, &scratch); + if (r) { + DRM_ERROR("radeon: cp failed to get scratch reg (%d).\n", r); + return r; + } + WREG32(scratch, 0xCAFEDEAD); + r = radeon_ring_lock(rdev, ring, 2); + if (r) { + DRM_ERROR("radeon: cp failed to lock ring (%d).\n", r); + radeon_scratch_free(rdev, scratch); + return r; + } + radeon_ring_write(ring, PACKET0(scratch, 0)); + radeon_ring_write(ring, 0xDEADBEEF); + radeon_ring_unlock_commit(rdev, ring); + for (i = 0; i < rdev->usec_timeout; i++) { + tmp = RREG32(scratch); + if (tmp == 0xDEADBEEF) { + break; + } + DRM_UDELAY(1); + } + if (i < rdev->usec_timeout) { + DRM_INFO("ring test succeeded in %d usecs\n", i); + } else { + DRM_ERROR("radeon: ring test failed (scratch(0x%04X)=0x%08X)\n", + scratch, tmp); + r = -EINVAL; + } + radeon_scratch_free(rdev, scratch); + return r; +} + +void r100_ring_ib_execute(struct radeon_device *rdev, struct radeon_ib *ib) +{ + struct radeon_ring *ring = &rdev->ring[RADEON_RING_TYPE_GFX_INDEX]; + + if (ring->rptr_save_reg) { + u32 next_rptr = ring->wptr + 2 + 3; + radeon_ring_write(ring, PACKET0(ring->rptr_save_reg, 0)); + radeon_ring_write(ring, next_rptr); + } + + radeon_ring_write(ring, PACKET0(RADEON_CP_IB_BASE, 1)); + radeon_ring_write(ring, ib->gpu_addr); + radeon_ring_write(ring, ib->length_dw); +} + +int r100_ib_test(struct radeon_device *rdev, struct radeon_ring *ring) +{ + struct radeon_ib ib; + uint32_t scratch; + uint32_t tmp = 0; + unsigned i; + int r; + + r = radeon_scratch_get(rdev, &scratch); + if (r) { + DRM_ERROR("radeon: failed to get scratch reg (%d).\n", r); + return r; + } + WREG32(scratch, 0xCAFEDEAD); + r = radeon_ib_get(rdev, RADEON_RING_TYPE_GFX_INDEX, &ib, NULL, 256); + if (r) { + DRM_ERROR("radeon: failed to get ib (%d).\n", r); + goto free_scratch; + } + ib.ptr[0] = PACKET0(scratch, 0); + ib.ptr[1] = 0xDEADBEEF; + ib.ptr[2] = PACKET2(0); + ib.ptr[3] = PACKET2(0); + ib.ptr[4] = PACKET2(0); + ib.ptr[5] = PACKET2(0); + ib.ptr[6] = PACKET2(0); + ib.ptr[7] = PACKET2(0); + ib.length_dw = 8; + r = radeon_ib_schedule(rdev, &ib, NULL); + if (r) { + DRM_ERROR("radeon: failed to schedule ib (%d).\n", r); + goto free_ib; + } + r = radeon_fence_wait(ib.fence, false); + if (r) { + DRM_ERROR("radeon: fence wait failed (%d).\n", r); + goto free_ib; + } + for (i = 0; i < rdev->usec_timeout; i++) { + tmp = RREG32(scratch); + if (tmp == 0xDEADBEEF) { + break; + } + DRM_UDELAY(1); + } + if (i < rdev->usec_timeout) { + DRM_INFO("ib test succeeded in %u usecs\n", i); + } else { + DRM_ERROR("radeon: ib test failed (scratch(0x%04X)=0x%08X)\n", + scratch, tmp); + r = -EINVAL; + } +free_ib: + radeon_ib_free(rdev, &ib); +free_scratch: + radeon_scratch_free(rdev, scratch); + return r; +} + +void r100_mc_stop(struct radeon_device *rdev, struct r100_mc_save *save) +{ + /* Shutdown CP we shouldn't need to do that but better be safe than + * sorry + */ + rdev->ring[RADEON_RING_TYPE_GFX_INDEX].ready = false; + WREG32(R_000740_CP_CSQ_CNTL, 0); + + /* Save few CRTC registers */ + save->GENMO_WT = RREG8(R_0003C2_GENMO_WT); + save->CRTC_EXT_CNTL = RREG32(R_000054_CRTC_EXT_CNTL); + save->CRTC_GEN_CNTL = RREG32(R_000050_CRTC_GEN_CNTL); + save->CUR_OFFSET = RREG32(R_000260_CUR_OFFSET); + if (!(rdev->flags & RADEON_SINGLE_CRTC)) { + save->CRTC2_GEN_CNTL = RREG32(R_0003F8_CRTC2_GEN_CNTL); + save->CUR2_OFFSET = RREG32(R_000360_CUR2_OFFSET); + } + + /* Disable VGA aperture access */ + WREG8(R_0003C2_GENMO_WT, C_0003C2_VGA_RAM_EN & save->GENMO_WT); + /* Disable cursor, overlay, crtc */ + WREG32(R_000260_CUR_OFFSET, save->CUR_OFFSET | S_000260_CUR_LOCK(1)); + WREG32(R_000054_CRTC_EXT_CNTL, save->CRTC_EXT_CNTL | + S_000054_CRTC_DISPLAY_DIS(1)); + WREG32(R_000050_CRTC_GEN_CNTL, + (C_000050_CRTC_CUR_EN & save->CRTC_GEN_CNTL) | + S_000050_CRTC_DISP_REQ_EN_B(1)); + WREG32(R_000420_OV0_SCALE_CNTL, + C_000420_OV0_OVERLAY_EN & RREG32(R_000420_OV0_SCALE_CNTL)); + WREG32(R_000260_CUR_OFFSET, C_000260_CUR_LOCK & save->CUR_OFFSET); + if (!(rdev->flags & RADEON_SINGLE_CRTC)) { + WREG32(R_000360_CUR2_OFFSET, save->CUR2_OFFSET | + S_000360_CUR2_LOCK(1)); + WREG32(R_0003F8_CRTC2_GEN_CNTL, + (C_0003F8_CRTC2_CUR_EN & save->CRTC2_GEN_CNTL) | + S_0003F8_CRTC2_DISPLAY_DIS(1) | + S_0003F8_CRTC2_DISP_REQ_EN_B(1)); + WREG32(R_000360_CUR2_OFFSET, + C_000360_CUR2_LOCK & save->CUR2_OFFSET); + } +} + +void r100_mc_resume(struct radeon_device *rdev, struct r100_mc_save *save) +{ + /* Update base address for crtc */ + WREG32(R_00023C_DISPLAY_BASE_ADDR, rdev->mc.vram_start); + if (!(rdev->flags & RADEON_SINGLE_CRTC)) { + WREG32(R_00033C_CRTC2_DISPLAY_BASE_ADDR, rdev->mc.vram_start); + } + /* Restore CRTC registers */ + WREG8(R_0003C2_GENMO_WT, save->GENMO_WT); + WREG32(R_000054_CRTC_EXT_CNTL, save->CRTC_EXT_CNTL); + WREG32(R_000050_CRTC_GEN_CNTL, save->CRTC_GEN_CNTL); + if (!(rdev->flags & RADEON_SINGLE_CRTC)) { + WREG32(R_0003F8_CRTC2_GEN_CNTL, save->CRTC2_GEN_CNTL); + } +} + +void r100_vga_render_disable(struct radeon_device *rdev) +{ + u32 tmp; + + tmp = RREG8(R_0003C2_GENMO_WT); + WREG8(R_0003C2_GENMO_WT, C_0003C2_VGA_RAM_EN & tmp); +} + +static void r100_debugfs(struct radeon_device *rdev) +{ + int r; + + r = r100_debugfs_mc_info_init(rdev); + if (r) + dev_warn(rdev->dev, "Failed to create r100_mc debugfs file.\n"); +} + +static void r100_mc_program(struct radeon_device *rdev) +{ + struct r100_mc_save save; + + /* Stops all mc clients */ + r100_mc_stop(rdev, &save); + if (rdev->flags & RADEON_IS_AGP) { + WREG32(R_00014C_MC_AGP_LOCATION, + S_00014C_MC_AGP_START(rdev->mc.gtt_start >> 16) | + S_00014C_MC_AGP_TOP(rdev->mc.gtt_end >> 16)); + WREG32(R_000170_AGP_BASE, lower_32_bits(rdev->mc.agp_base)); + if (rdev->family > CHIP_RV200) + WREG32(R_00015C_AGP_BASE_2, + upper_32_bits(rdev->mc.agp_base) & 0xff); + } else { + WREG32(R_00014C_MC_AGP_LOCATION, 0x0FFFFFFF); + WREG32(R_000170_AGP_BASE, 0); + if (rdev->family > CHIP_RV200) + WREG32(R_00015C_AGP_BASE_2, 0); + } + /* Wait for mc idle */ + if (r100_mc_wait_for_idle(rdev)) + dev_warn(rdev->dev, "Wait for MC idle timeout.\n"); + /* Program MC, should be a 32bits limited address space */ + WREG32(R_000148_MC_FB_LOCATION, + S_000148_MC_FB_START(rdev->mc.vram_start >> 16) | + S_000148_MC_FB_TOP(rdev->mc.vram_end >> 16)); + r100_mc_resume(rdev, &save); +} + +static void r100_clock_startup(struct radeon_device *rdev) +{ + u32 tmp; + + if (radeon_dynclks != -1 && radeon_dynclks) + radeon_legacy_set_clock_gating(rdev, 1); + /* We need to force on some of the block */ + tmp = RREG32_PLL(R_00000D_SCLK_CNTL); + tmp |= S_00000D_FORCE_CP(1) | S_00000D_FORCE_VIP(1); + if ((rdev->family == CHIP_RV250) || (rdev->family == CHIP_RV280)) + tmp |= S_00000D_FORCE_DISP1(1) | S_00000D_FORCE_DISP2(1); + WREG32_PLL(R_00000D_SCLK_CNTL, tmp); +} + +static int r100_startup(struct radeon_device *rdev) +{ + int r; + + /* set common regs */ + r100_set_common_regs(rdev); + /* program mc */ + r100_mc_program(rdev); + /* Resume clock */ + r100_clock_startup(rdev); + /* Initialize GART (initialize after TTM so we can allocate + * memory through TTM but finalize after TTM) */ + r100_enable_bm(rdev); + if (rdev->flags & RADEON_IS_PCI) { + r = r100_pci_gart_enable(rdev); + if (r) + return r; + } + + /* allocate wb buffer */ + r = radeon_wb_init(rdev); + if (r) + return r; + + r = radeon_fence_driver_start_ring(rdev, RADEON_RING_TYPE_GFX_INDEX); + if (r) { + dev_err(rdev->dev, "failed initializing CP fences (%d).\n", r); + return r; + } + + /* Enable IRQ */ + r100_irq_set(rdev); + rdev->config.r100.hdp_cntl = RREG32(RADEON_HOST_PATH_CNTL); + /* 1M ring buffer */ + r = r100_cp_init(rdev, 1024 * 1024); + if (r) { + dev_err(rdev->dev, "failed initializing CP (%d).\n", r); + return r; + } + + r = radeon_ib_pool_init(rdev); + if (r) { + dev_err(rdev->dev, "IB initialization failed (%d).\n", r); + return r; + } + + return 0; +} + +int r100_resume(struct radeon_device *rdev) +{ + int r; + + /* Make sur GART are not working */ + if (rdev->flags & RADEON_IS_PCI) + r100_pci_gart_disable(rdev); + /* Resume clock before doing reset */ + r100_clock_startup(rdev); + /* Reset gpu before posting otherwise ATOM will enter infinite loop */ + if (radeon_asic_reset(rdev)) { + dev_warn(rdev->dev, "GPU reset failed ! (0xE40=0x%08X, 0x7C0=0x%08X)\n", + RREG32(R_000E40_RBBM_STATUS), + RREG32(R_0007C0_CP_STAT)); + } + /* post */ + radeon_combios_asic_init(rdev->ddev); + /* Resume clock after posting */ + r100_clock_startup(rdev); + /* Initialize surface registers */ + radeon_surface_init(rdev); + + rdev->accel_working = true; + r = r100_startup(rdev); + if (r) { + rdev->accel_working = false; + } + return r; +} + +int r100_suspend(struct radeon_device *rdev) +{ + r100_cp_disable(rdev); + radeon_wb_disable(rdev); + r100_irq_disable(rdev); + if (rdev->flags & RADEON_IS_PCI) + r100_pci_gart_disable(rdev); + return 0; +} + +void r100_fini(struct radeon_device *rdev) +{ + r100_cp_fini(rdev); + radeon_wb_fini(rdev); + radeon_ib_pool_fini(rdev); + radeon_gem_fini(rdev); + if (rdev->flags & RADEON_IS_PCI) + r100_pci_gart_fini(rdev); + radeon_agp_fini(rdev); + radeon_irq_kms_fini(rdev); + radeon_fence_driver_fini(rdev); + radeon_bo_fini(rdev); + radeon_atombios_fini(rdev); + r100_cp_fini_microcode(rdev); + free(rdev->bios, DRM_MEM_DRIVER); + rdev->bios = NULL; +} + +/* + * Due to how kexec works, it can leave the hw fully initialised when it + * boots the new kernel. However doing our init sequence with the CP and + * WB stuff setup causes GPU hangs on the RN50 at least. So at startup + * do some quick sanity checks and restore sane values to avoid this + * problem. + */ +void r100_restore_sanity(struct radeon_device *rdev) +{ + u32 tmp; + + tmp = RREG32(RADEON_CP_CSQ_CNTL); + if (tmp) { + WREG32(RADEON_CP_CSQ_CNTL, 0); + } + tmp = RREG32(RADEON_CP_RB_CNTL); + if (tmp) { + WREG32(RADEON_CP_RB_CNTL, 0); + } + tmp = RREG32(RADEON_SCRATCH_UMSK); + if (tmp) { + WREG32(RADEON_SCRATCH_UMSK, 0); + } +} + +int r100_init(struct radeon_device *rdev) +{ + int r; + + /* Register debugfs file specific to this group of asics */ + r100_debugfs(rdev); + /* Disable VGA */ + r100_vga_render_disable(rdev); + /* Initialize scratch registers */ + radeon_scratch_init(rdev); + /* Initialize surface registers */ + radeon_surface_init(rdev); + /* sanity check some register to avoid hangs like after kexec */ + r100_restore_sanity(rdev); + /* TODO: disable VGA need to use VGA request */ + /* BIOS*/ + if (!radeon_get_bios(rdev)) { + if (ASIC_IS_AVIVO(rdev)) + return -EINVAL; + } + if (rdev->is_atom_bios) { + dev_err(rdev->dev, "Expecting combios for RS400/RS480 GPU\n"); + return -EINVAL; + } else { + r = radeon_combios_init(rdev); + if (r) + return r; + } + /* Reset gpu before posting otherwise ATOM will enter infinite loop */ + if (radeon_asic_reset(rdev)) { + dev_warn(rdev->dev, + "GPU reset failed ! (0xE40=0x%08X, 0x7C0=0x%08X)\n", + RREG32(R_000E40_RBBM_STATUS), + RREG32(R_0007C0_CP_STAT)); + } + /* check if cards are posted or not */ + if (radeon_boot_test_post_card(rdev) == false) + return -EINVAL; + /* Set asic errata */ + r100_errata(rdev); + /* Initialize clocks */ + radeon_get_clock_info(rdev->ddev); + /* initialize AGP */ + if (rdev->flags & RADEON_IS_AGP) { + r = radeon_agp_init(rdev); + if (r) { + radeon_agp_disable(rdev); + } + } + /* initialize VRAM */ + r100_mc_init(rdev); + /* Fence driver */ + r = radeon_fence_driver_init(rdev); + if (r) + return r; + r = radeon_irq_kms_init(rdev); + if (r) + return r; + /* Memory manager */ + r = radeon_bo_init(rdev); + if (r) + return r; + if (rdev->flags & RADEON_IS_PCI) { + r = r100_pci_gart_init(rdev); + if (r) + return r; + } + r100_set_safe_registers(rdev); + + rdev->accel_working = true; + r = r100_startup(rdev); + if (r) { + /* Somethings want wront with the accel init stop accel */ + dev_err(rdev->dev, "Disabling GPU acceleration\n"); + r100_cp_fini(rdev); + radeon_wb_fini(rdev); + radeon_ib_pool_fini(rdev); + radeon_irq_kms_fini(rdev); + if (rdev->flags & RADEON_IS_PCI) + r100_pci_gart_fini(rdev); + rdev->accel_working = false; + } + return 0; +} + +uint32_t r100_mm_rreg(struct radeon_device *rdev, uint32_t reg, + bool always_indirect) +{ + if (reg < rdev->rmmio_size && !always_indirect) + return bus_read_4(rdev->rmmio, reg); + else { + unsigned long flags; + uint32_t ret; + + DRM_SPINLOCK_IRQSAVE(&rdev->mmio_idx_lock, flags); + bus_write_4(rdev->rmmio, RADEON_MM_INDEX, reg); + ret = bus_read_4(rdev->rmmio, RADEON_MM_DATA); + DRM_SPINUNLOCK_IRQRESTORE(&rdev->mmio_idx_lock, flags); + + return ret; + } +} + +void r100_mm_wreg(struct radeon_device *rdev, uint32_t reg, uint32_t v, + bool always_indirect) +{ + if (reg < rdev->rmmio_size && !always_indirect) + bus_write_4(rdev->rmmio, reg, v); + else { + unsigned long flags; + + DRM_SPINLOCK_IRQSAVE(&rdev->mmio_idx_lock, flags); + bus_write_4(rdev->rmmio, RADEON_MM_INDEX, reg); + bus_write_4(rdev->rmmio, RADEON_MM_DATA, v); + DRM_SPINUNLOCK_IRQRESTORE(&rdev->mmio_idx_lock, flags); + } +} + +u32 r100_io_rreg(struct radeon_device *rdev, u32 reg) +{ + if (reg < rdev->rio_mem_size) + return bus_read_4(rdev->rio_mem, reg); + else { + /* XXX No locking? -- dumbbell@ */ + bus_write_4(rdev->rio_mem, RADEON_MM_INDEX, reg); + return bus_read_4(rdev->rio_mem, RADEON_MM_DATA); + } +} + +void r100_io_wreg(struct radeon_device *rdev, u32 reg, u32 v) +{ + if (reg < rdev->rio_mem_size) + bus_write_4(rdev->rio_mem, reg, v); + else { + /* XXX No locking? -- dumbbell@ */ + bus_write_4(rdev->rio_mem, RADEON_MM_INDEX, reg); + bus_write_4(rdev->rio_mem, RADEON_MM_DATA, v); + } +} diff --git a/sys/dev/drm2/radeon/r100_reg_safe.h b/sys/dev/drm2/radeon/r100_reg_safe.h new file mode 100644 index 00000000000..23049ad1166 --- /dev/null +++ b/sys/dev/drm2/radeon/r100_reg_safe.h @@ -0,0 +1,31 @@ +#include +__FBSDID("$FreeBSD$"); + +static const unsigned r100_reg_safe_bm[102] = { + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0x17FF1FFF, 0xFFFFFFFC, 0xFFFFFFFF, 0xFF30FFBF, + 0xFFFFFFF8, 0xC3E6FFFF, 0xFFFFF6DF, 0xFFFFFFFF, + 0xFFFFFFCF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFF9F, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0x38E7FE1F, 0xFFC3FF8E, 0x7FF8FFFF, 0xFFFF803C, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFEFFFF, 0xFFFFFFFF, + 0x00000000, 0xFFFFFFFD, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFCFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFEF, +}; diff --git a/sys/dev/drm2/radeon/r100_track.h b/sys/dev/drm2/radeon/r100_track.h new file mode 100644 index 00000000000..c4c5d8e4a34 --- /dev/null +++ b/sys/dev/drm2/radeon/r100_track.h @@ -0,0 +1,103 @@ +#include +__FBSDID("$FreeBSD$"); + +#define R100_TRACK_MAX_TEXTURE 3 +#define R200_TRACK_MAX_TEXTURE 6 +#define R300_TRACK_MAX_TEXTURE 16 + +#define R100_MAX_CB 1 +#define R300_MAX_CB 4 + +/* + * CS functions + */ +struct r100_cs_track_cb { + struct radeon_bo *robj; + unsigned pitch; + unsigned cpp; + unsigned offset; +}; + +struct r100_cs_track_array { + struct radeon_bo *robj; + unsigned esize; +}; + +struct r100_cs_cube_info { + struct radeon_bo *robj; + unsigned offset; + unsigned width; + unsigned height; +}; + +#define R100_TRACK_COMP_NONE 0 +#define R100_TRACK_COMP_DXT1 1 +#define R100_TRACK_COMP_DXT35 2 + +struct r100_cs_track_texture { + struct radeon_bo *robj; + struct r100_cs_cube_info cube_info[5]; /* info for 5 non-primary faces */ + unsigned pitch; + unsigned width; + unsigned height; + unsigned num_levels; + unsigned cpp; + unsigned tex_coord_type; + unsigned txdepth; + unsigned width_11; + unsigned height_11; + bool use_pitch; + bool enabled; + bool lookup_disable; + bool roundup_w; + bool roundup_h; + unsigned compress_format; +}; + +struct r100_cs_track { + unsigned num_cb; + unsigned num_texture; + unsigned maxy; + unsigned vtx_size; + unsigned vap_vf_cntl; + unsigned vap_alt_nverts; + unsigned immd_dwords; + unsigned num_arrays; + unsigned max_indx; + unsigned color_channel_mask; + struct r100_cs_track_array arrays[16]; + struct r100_cs_track_cb cb[R300_MAX_CB]; + struct r100_cs_track_cb zb; + struct r100_cs_track_cb aa; + struct r100_cs_track_texture textures[R300_TRACK_MAX_TEXTURE]; + bool z_enabled; + bool separate_cube; + bool zb_cb_clear; + bool blend_read_enable; + bool cb_dirty; + bool zb_dirty; + bool tex_dirty; + bool aa_dirty; + bool aaresolve; +}; + +int r100_cs_track_check(struct radeon_device *rdev, struct r100_cs_track *track); +void r100_cs_track_clear(struct radeon_device *rdev, struct r100_cs_track *track); +int r100_cs_packet_next_reloc(struct radeon_cs_parser *p, + struct radeon_cs_reloc **cs_reloc); +void r100_cs_dump_packet(struct radeon_cs_parser *p, + struct radeon_cs_packet *pkt); + +int r100_cs_packet_parse_vline(struct radeon_cs_parser *p); + +int r200_packet0_check(struct radeon_cs_parser *p, + struct radeon_cs_packet *pkt, + unsigned idx, unsigned reg); + +int r100_reloc_pitch_offset(struct radeon_cs_parser *p, + struct radeon_cs_packet *pkt, + unsigned idx, + unsigned reg); +int r100_packet3_load_vbpntr(struct radeon_cs_parser *p, + struct radeon_cs_packet *pkt, + int idx); diff --git a/sys/dev/drm2/radeon/r100d.h b/sys/dev/drm2/radeon/r100d.h new file mode 100644 index 00000000000..96418ca47a2 --- /dev/null +++ b/sys/dev/drm2/radeon/r100d.h @@ -0,0 +1,883 @@ +/* + * Copyright 2008 Advanced Micro Devices, Inc. + * Copyright 2008 Red Hat Inc. + * Copyright 2009 Jerome Glisse. + * + * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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. + * + * Authors: Dave Airlie + * Alex Deucher + * Jerome Glisse + */ +#ifndef __R100D_H__ +#define __R100D_H__ + +#include +__FBSDID("$FreeBSD$"); + +#define CP_PACKET0 0x00000000 +#define PACKET0_BASE_INDEX_SHIFT 0 +#define PACKET0_BASE_INDEX_MASK (0x1ffff << 0) +#define PACKET0_COUNT_SHIFT 16 +#define PACKET0_COUNT_MASK (0x3fff << 16) +#define CP_PACKET1 0x40000000 +#define CP_PACKET2 0x80000000 +#define PACKET2_PAD_SHIFT 0 +#define PACKET2_PAD_MASK (0x3fffffff << 0) +#define CP_PACKET3 0xC0000000 +#define PACKET3_IT_OPCODE_SHIFT 8 +#define PACKET3_IT_OPCODE_MASK (0xff << 8) +#define PACKET3_COUNT_SHIFT 16 +#define PACKET3_COUNT_MASK (0x3fff << 16) +/* PACKET3 op code */ +#define PACKET3_NOP 0x10 +#define PACKET3_3D_DRAW_VBUF 0x28 +#define PACKET3_3D_DRAW_IMMD 0x29 +#define PACKET3_3D_DRAW_INDX 0x2A +#define PACKET3_3D_LOAD_VBPNTR 0x2F +#define PACKET3_3D_CLEAR_ZMASK 0x32 +#define PACKET3_INDX_BUFFER 0x33 +#define PACKET3_3D_DRAW_VBUF_2 0x34 +#define PACKET3_3D_DRAW_IMMD_2 0x35 +#define PACKET3_3D_DRAW_INDX_2 0x36 +#define PACKET3_3D_CLEAR_HIZ 0x37 +#define PACKET3_BITBLT_MULTI 0x9B + +#define PACKET0(reg, n) (CP_PACKET0 | \ + REG_SET(PACKET0_BASE_INDEX, (reg) >> 2) | \ + REG_SET(PACKET0_COUNT, (n))) +#define PACKET2(v) (CP_PACKET2 | REG_SET(PACKET2_PAD, (v))) +#define PACKET3(op, n) (CP_PACKET3 | \ + REG_SET(PACKET3_IT_OPCODE, (op)) | \ + REG_SET(PACKET3_COUNT, (n))) + +#define PACKET_TYPE0 0 +#define PACKET_TYPE1 1 +#define PACKET_TYPE2 2 +#define PACKET_TYPE3 3 + +#define CP_PACKET_GET_TYPE(h) (((h) >> 30) & 3) +#define CP_PACKET_GET_COUNT(h) (((h) >> 16) & 0x3FFF) +#define CP_PACKET0_GET_REG(h) (((h) & 0x1FFF) << 2) +#define CP_PACKET0_GET_ONE_REG_WR(h) (((h) >> 15) & 1) +#define CP_PACKET3_GET_OPCODE(h) (((h) >> 8) & 0xFF) + +/* Registers */ +#define R_0000F0_RBBM_SOFT_RESET 0x0000F0 +#define S_0000F0_SOFT_RESET_CP(x) (((x) & 0x1) << 0) +#define G_0000F0_SOFT_RESET_CP(x) (((x) >> 0) & 0x1) +#define C_0000F0_SOFT_RESET_CP 0xFFFFFFFE +#define S_0000F0_SOFT_RESET_HI(x) (((x) & 0x1) << 1) +#define G_0000F0_SOFT_RESET_HI(x) (((x) >> 1) & 0x1) +#define C_0000F0_SOFT_RESET_HI 0xFFFFFFFD +#define S_0000F0_SOFT_RESET_SE(x) (((x) & 0x1) << 2) +#define G_0000F0_SOFT_RESET_SE(x) (((x) >> 2) & 0x1) +#define C_0000F0_SOFT_RESET_SE 0xFFFFFFFB +#define S_0000F0_SOFT_RESET_RE(x) (((x) & 0x1) << 3) +#define G_0000F0_SOFT_RESET_RE(x) (((x) >> 3) & 0x1) +#define C_0000F0_SOFT_RESET_RE 0xFFFFFFF7 +#define S_0000F0_SOFT_RESET_PP(x) (((x) & 0x1) << 4) +#define G_0000F0_SOFT_RESET_PP(x) (((x) >> 4) & 0x1) +#define C_0000F0_SOFT_RESET_PP 0xFFFFFFEF +#define S_0000F0_SOFT_RESET_E2(x) (((x) & 0x1) << 5) +#define G_0000F0_SOFT_RESET_E2(x) (((x) >> 5) & 0x1) +#define C_0000F0_SOFT_RESET_E2 0xFFFFFFDF +#define S_0000F0_SOFT_RESET_RB(x) (((x) & 0x1) << 6) +#define G_0000F0_SOFT_RESET_RB(x) (((x) >> 6) & 0x1) +#define C_0000F0_SOFT_RESET_RB 0xFFFFFFBF +#define S_0000F0_SOFT_RESET_HDP(x) (((x) & 0x1) << 7) +#define G_0000F0_SOFT_RESET_HDP(x) (((x) >> 7) & 0x1) +#define C_0000F0_SOFT_RESET_HDP 0xFFFFFF7F +#define S_0000F0_SOFT_RESET_MC(x) (((x) & 0x1) << 8) +#define G_0000F0_SOFT_RESET_MC(x) (((x) >> 8) & 0x1) +#define C_0000F0_SOFT_RESET_MC 0xFFFFFEFF +#define S_0000F0_SOFT_RESET_AIC(x) (((x) & 0x1) << 9) +#define G_0000F0_SOFT_RESET_AIC(x) (((x) >> 9) & 0x1) +#define C_0000F0_SOFT_RESET_AIC 0xFFFFFDFF +#define S_0000F0_SOFT_RESET_VIP(x) (((x) & 0x1) << 10) +#define G_0000F0_SOFT_RESET_VIP(x) (((x) >> 10) & 0x1) +#define C_0000F0_SOFT_RESET_VIP 0xFFFFFBFF +#define S_0000F0_SOFT_RESET_DISP(x) (((x) & 0x1) << 11) +#define G_0000F0_SOFT_RESET_DISP(x) (((x) >> 11) & 0x1) +#define C_0000F0_SOFT_RESET_DISP 0xFFFFF7FF +#define S_0000F0_SOFT_RESET_CG(x) (((x) & 0x1) << 12) +#define G_0000F0_SOFT_RESET_CG(x) (((x) >> 12) & 0x1) +#define C_0000F0_SOFT_RESET_CG 0xFFFFEFFF +#define R_000030_BUS_CNTL 0x000030 +#define S_000030_BUS_DBL_RESYNC(x) (((x) & 0x1) << 0) +#define G_000030_BUS_DBL_RESYNC(x) (((x) >> 0) & 0x1) +#define C_000030_BUS_DBL_RESYNC 0xFFFFFFFE +#define S_000030_BUS_MSTR_RESET(x) (((x) & 0x1) << 1) +#define G_000030_BUS_MSTR_RESET(x) (((x) >> 1) & 0x1) +#define C_000030_BUS_MSTR_RESET 0xFFFFFFFD +#define S_000030_BUS_FLUSH_BUF(x) (((x) & 0x1) << 2) +#define G_000030_BUS_FLUSH_BUF(x) (((x) >> 2) & 0x1) +#define C_000030_BUS_FLUSH_BUF 0xFFFFFFFB +#define S_000030_BUS_STOP_REQ_DIS(x) (((x) & 0x1) << 3) +#define G_000030_BUS_STOP_REQ_DIS(x) (((x) >> 3) & 0x1) +#define C_000030_BUS_STOP_REQ_DIS 0xFFFFFFF7 +#define S_000030_BUS_PM4_READ_COMBINE_EN(x) (((x) & 0x1) << 4) +#define G_000030_BUS_PM4_READ_COMBINE_EN(x) (((x) >> 4) & 0x1) +#define C_000030_BUS_PM4_READ_COMBINE_EN 0xFFFFFFEF +#define S_000030_BUS_WRT_COMBINE_EN(x) (((x) & 0x1) << 5) +#define G_000030_BUS_WRT_COMBINE_EN(x) (((x) >> 5) & 0x1) +#define C_000030_BUS_WRT_COMBINE_EN 0xFFFFFFDF +#define S_000030_BUS_MASTER_DIS(x) (((x) & 0x1) << 6) +#define G_000030_BUS_MASTER_DIS(x) (((x) >> 6) & 0x1) +#define C_000030_BUS_MASTER_DIS 0xFFFFFFBF +#define S_000030_BIOS_ROM_WRT_EN(x) (((x) & 0x1) << 7) +#define G_000030_BIOS_ROM_WRT_EN(x) (((x) >> 7) & 0x1) +#define C_000030_BIOS_ROM_WRT_EN 0xFFFFFF7F +#define S_000030_BM_DAC_CRIPPLE(x) (((x) & 0x1) << 8) +#define G_000030_BM_DAC_CRIPPLE(x) (((x) >> 8) & 0x1) +#define C_000030_BM_DAC_CRIPPLE 0xFFFFFEFF +#define S_000030_BUS_NON_PM4_READ_COMBINE_EN(x) (((x) & 0x1) << 9) +#define G_000030_BUS_NON_PM4_READ_COMBINE_EN(x) (((x) >> 9) & 0x1) +#define C_000030_BUS_NON_PM4_READ_COMBINE_EN 0xFFFFFDFF +#define S_000030_BUS_XFERD_DISCARD_EN(x) (((x) & 0x1) << 10) +#define G_000030_BUS_XFERD_DISCARD_EN(x) (((x) >> 10) & 0x1) +#define C_000030_BUS_XFERD_DISCARD_EN 0xFFFFFBFF +#define S_000030_BUS_SGL_READ_DISABLE(x) (((x) & 0x1) << 11) +#define G_000030_BUS_SGL_READ_DISABLE(x) (((x) >> 11) & 0x1) +#define C_000030_BUS_SGL_READ_DISABLE 0xFFFFF7FF +#define S_000030_BIOS_DIS_ROM(x) (((x) & 0x1) << 12) +#define G_000030_BIOS_DIS_ROM(x) (((x) >> 12) & 0x1) +#define C_000030_BIOS_DIS_ROM 0xFFFFEFFF +#define S_000030_BUS_PCI_READ_RETRY_EN(x) (((x) & 0x1) << 13) +#define G_000030_BUS_PCI_READ_RETRY_EN(x) (((x) >> 13) & 0x1) +#define C_000030_BUS_PCI_READ_RETRY_EN 0xFFFFDFFF +#define S_000030_BUS_AGP_AD_STEPPING_EN(x) (((x) & 0x1) << 14) +#define G_000030_BUS_AGP_AD_STEPPING_EN(x) (((x) >> 14) & 0x1) +#define C_000030_BUS_AGP_AD_STEPPING_EN 0xFFFFBFFF +#define S_000030_BUS_PCI_WRT_RETRY_EN(x) (((x) & 0x1) << 15) +#define G_000030_BUS_PCI_WRT_RETRY_EN(x) (((x) >> 15) & 0x1) +#define C_000030_BUS_PCI_WRT_RETRY_EN 0xFFFF7FFF +#define S_000030_BUS_RETRY_WS(x) (((x) & 0xF) << 16) +#define G_000030_BUS_RETRY_WS(x) (((x) >> 16) & 0xF) +#define C_000030_BUS_RETRY_WS 0xFFF0FFFF +#define S_000030_BUS_MSTR_RD_MULT(x) (((x) & 0x1) << 20) +#define G_000030_BUS_MSTR_RD_MULT(x) (((x) >> 20) & 0x1) +#define C_000030_BUS_MSTR_RD_MULT 0xFFEFFFFF +#define S_000030_BUS_MSTR_RD_LINE(x) (((x) & 0x1) << 21) +#define G_000030_BUS_MSTR_RD_LINE(x) (((x) >> 21) & 0x1) +#define C_000030_BUS_MSTR_RD_LINE 0xFFDFFFFF +#define S_000030_BUS_SUSPEND(x) (((x) & 0x1) << 22) +#define G_000030_BUS_SUSPEND(x) (((x) >> 22) & 0x1) +#define C_000030_BUS_SUSPEND 0xFFBFFFFF +#define S_000030_LAT_16X(x) (((x) & 0x1) << 23) +#define G_000030_LAT_16X(x) (((x) >> 23) & 0x1) +#define C_000030_LAT_16X 0xFF7FFFFF +#define S_000030_BUS_RD_DISCARD_EN(x) (((x) & 0x1) << 24) +#define G_000030_BUS_RD_DISCARD_EN(x) (((x) >> 24) & 0x1) +#define C_000030_BUS_RD_DISCARD_EN 0xFEFFFFFF +#define S_000030_ENFRCWRDY(x) (((x) & 0x1) << 25) +#define G_000030_ENFRCWRDY(x) (((x) >> 25) & 0x1) +#define C_000030_ENFRCWRDY 0xFDFFFFFF +#define S_000030_BUS_MSTR_WS(x) (((x) & 0x1) << 26) +#define G_000030_BUS_MSTR_WS(x) (((x) >> 26) & 0x1) +#define C_000030_BUS_MSTR_WS 0xFBFFFFFF +#define S_000030_BUS_PARKING_DIS(x) (((x) & 0x1) << 27) +#define G_000030_BUS_PARKING_DIS(x) (((x) >> 27) & 0x1) +#define C_000030_BUS_PARKING_DIS 0xF7FFFFFF +#define S_000030_BUS_MSTR_DISCONNECT_EN(x) (((x) & 0x1) << 28) +#define G_000030_BUS_MSTR_DISCONNECT_EN(x) (((x) >> 28) & 0x1) +#define C_000030_BUS_MSTR_DISCONNECT_EN 0xEFFFFFFF +#define S_000030_SERR_EN(x) (((x) & 0x1) << 29) +#define G_000030_SERR_EN(x) (((x) >> 29) & 0x1) +#define C_000030_SERR_EN 0xDFFFFFFF +#define S_000030_BUS_READ_BURST(x) (((x) & 0x1) << 30) +#define G_000030_BUS_READ_BURST(x) (((x) >> 30) & 0x1) +#define C_000030_BUS_READ_BURST 0xBFFFFFFF +#define S_000030_BUS_RDY_READ_DLY(x) (((x) & 0x1) << 31) +#define G_000030_BUS_RDY_READ_DLY(x) (((x) >> 31) & 0x1) +#define C_000030_BUS_RDY_READ_DLY 0x7FFFFFFF +#define R_000040_GEN_INT_CNTL 0x000040 +#define S_000040_CRTC_VBLANK(x) (((x) & 0x1) << 0) +#define G_000040_CRTC_VBLANK(x) (((x) >> 0) & 0x1) +#define C_000040_CRTC_VBLANK 0xFFFFFFFE +#define S_000040_CRTC_VLINE(x) (((x) & 0x1) << 1) +#define G_000040_CRTC_VLINE(x) (((x) >> 1) & 0x1) +#define C_000040_CRTC_VLINE 0xFFFFFFFD +#define S_000040_CRTC_VSYNC(x) (((x) & 0x1) << 2) +#define G_000040_CRTC_VSYNC(x) (((x) >> 2) & 0x1) +#define C_000040_CRTC_VSYNC 0xFFFFFFFB +#define S_000040_SNAPSHOT(x) (((x) & 0x1) << 3) +#define G_000040_SNAPSHOT(x) (((x) >> 3) & 0x1) +#define C_000040_SNAPSHOT 0xFFFFFFF7 +#define S_000040_FP_DETECT(x) (((x) & 0x1) << 4) +#define G_000040_FP_DETECT(x) (((x) >> 4) & 0x1) +#define C_000040_FP_DETECT 0xFFFFFFEF +#define S_000040_CRTC2_VLINE(x) (((x) & 0x1) << 5) +#define G_000040_CRTC2_VLINE(x) (((x) >> 5) & 0x1) +#define C_000040_CRTC2_VLINE 0xFFFFFFDF +#define S_000040_DMA_VIPH0_INT_EN(x) (((x) & 0x1) << 12) +#define G_000040_DMA_VIPH0_INT_EN(x) (((x) >> 12) & 0x1) +#define C_000040_DMA_VIPH0_INT_EN 0xFFFFEFFF +#define S_000040_CRTC2_VSYNC(x) (((x) & 0x1) << 6) +#define G_000040_CRTC2_VSYNC(x) (((x) >> 6) & 0x1) +#define C_000040_CRTC2_VSYNC 0xFFFFFFBF +#define S_000040_SNAPSHOT2(x) (((x) & 0x1) << 7) +#define G_000040_SNAPSHOT2(x) (((x) >> 7) & 0x1) +#define C_000040_SNAPSHOT2 0xFFFFFF7F +#define S_000040_CRTC2_VBLANK(x) (((x) & 0x1) << 9) +#define G_000040_CRTC2_VBLANK(x) (((x) >> 9) & 0x1) +#define C_000040_CRTC2_VBLANK 0xFFFFFDFF +#define S_000040_FP2_DETECT(x) (((x) & 0x1) << 10) +#define G_000040_FP2_DETECT(x) (((x) >> 10) & 0x1) +#define C_000040_FP2_DETECT 0xFFFFFBFF +#define S_000040_VSYNC_DIFF_OVER_LIMIT(x) (((x) & 0x1) << 11) +#define G_000040_VSYNC_DIFF_OVER_LIMIT(x) (((x) >> 11) & 0x1) +#define C_000040_VSYNC_DIFF_OVER_LIMIT 0xFFFFF7FF +#define S_000040_DMA_VIPH1_INT_EN(x) (((x) & 0x1) << 13) +#define G_000040_DMA_VIPH1_INT_EN(x) (((x) >> 13) & 0x1) +#define C_000040_DMA_VIPH1_INT_EN 0xFFFFDFFF +#define S_000040_DMA_VIPH2_INT_EN(x) (((x) & 0x1) << 14) +#define G_000040_DMA_VIPH2_INT_EN(x) (((x) >> 14) & 0x1) +#define C_000040_DMA_VIPH2_INT_EN 0xFFFFBFFF +#define S_000040_DMA_VIPH3_INT_EN(x) (((x) & 0x1) << 15) +#define G_000040_DMA_VIPH3_INT_EN(x) (((x) >> 15) & 0x1) +#define C_000040_DMA_VIPH3_INT_EN 0xFFFF7FFF +#define S_000040_I2C_INT_EN(x) (((x) & 0x1) << 17) +#define G_000040_I2C_INT_EN(x) (((x) >> 17) & 0x1) +#define C_000040_I2C_INT_EN 0xFFFDFFFF +#define S_000040_GUI_IDLE(x) (((x) & 0x1) << 19) +#define G_000040_GUI_IDLE(x) (((x) >> 19) & 0x1) +#define C_000040_GUI_IDLE 0xFFF7FFFF +#define S_000040_VIPH_INT_EN(x) (((x) & 0x1) << 24) +#define G_000040_VIPH_INT_EN(x) (((x) >> 24) & 0x1) +#define C_000040_VIPH_INT_EN 0xFEFFFFFF +#define S_000040_SW_INT_EN(x) (((x) & 0x1) << 25) +#define G_000040_SW_INT_EN(x) (((x) >> 25) & 0x1) +#define C_000040_SW_INT_EN 0xFDFFFFFF +#define S_000040_GEYSERVILLE(x) (((x) & 0x1) << 27) +#define G_000040_GEYSERVILLE(x) (((x) >> 27) & 0x1) +#define C_000040_GEYSERVILLE 0xF7FFFFFF +#define S_000040_HDCP_AUTHORIZED_INT(x) (((x) & 0x1) << 28) +#define G_000040_HDCP_AUTHORIZED_INT(x) (((x) >> 28) & 0x1) +#define C_000040_HDCP_AUTHORIZED_INT 0xEFFFFFFF +#define S_000040_DVI_I2C_INT(x) (((x) & 0x1) << 29) +#define G_000040_DVI_I2C_INT(x) (((x) >> 29) & 0x1) +#define C_000040_DVI_I2C_INT 0xDFFFFFFF +#define S_000040_GUIDMA(x) (((x) & 0x1) << 30) +#define G_000040_GUIDMA(x) (((x) >> 30) & 0x1) +#define C_000040_GUIDMA 0xBFFFFFFF +#define S_000040_VIDDMA(x) (((x) & 0x1) << 31) +#define G_000040_VIDDMA(x) (((x) >> 31) & 0x1) +#define C_000040_VIDDMA 0x7FFFFFFF +#define R_000044_GEN_INT_STATUS 0x000044 +#define S_000044_CRTC_VBLANK_STAT(x) (((x) & 0x1) << 0) +#define G_000044_CRTC_VBLANK_STAT(x) (((x) >> 0) & 0x1) +#define C_000044_CRTC_VBLANK_STAT 0xFFFFFFFE +#define S_000044_CRTC_VBLANK_STAT_AK(x) (((x) & 0x1) << 0) +#define G_000044_CRTC_VBLANK_STAT_AK(x) (((x) >> 0) & 0x1) +#define C_000044_CRTC_VBLANK_STAT_AK 0xFFFFFFFE +#define S_000044_CRTC_VLINE_STAT(x) (((x) & 0x1) << 1) +#define G_000044_CRTC_VLINE_STAT(x) (((x) >> 1) & 0x1) +#define C_000044_CRTC_VLINE_STAT 0xFFFFFFFD +#define S_000044_CRTC_VLINE_STAT_AK(x) (((x) & 0x1) << 1) +#define G_000044_CRTC_VLINE_STAT_AK(x) (((x) >> 1) & 0x1) +#define C_000044_CRTC_VLINE_STAT_AK 0xFFFFFFFD +#define S_000044_CRTC_VSYNC_STAT(x) (((x) & 0x1) << 2) +#define G_000044_CRTC_VSYNC_STAT(x) (((x) >> 2) & 0x1) +#define C_000044_CRTC_VSYNC_STAT 0xFFFFFFFB +#define S_000044_CRTC_VSYNC_STAT_AK(x) (((x) & 0x1) << 2) +#define G_000044_CRTC_VSYNC_STAT_AK(x) (((x) >> 2) & 0x1) +#define C_000044_CRTC_VSYNC_STAT_AK 0xFFFFFFFB +#define S_000044_SNAPSHOT_STAT(x) (((x) & 0x1) << 3) +#define G_000044_SNAPSHOT_STAT(x) (((x) >> 3) & 0x1) +#define C_000044_SNAPSHOT_STAT 0xFFFFFFF7 +#define S_000044_SNAPSHOT_STAT_AK(x) (((x) & 0x1) << 3) +#define G_000044_SNAPSHOT_STAT_AK(x) (((x) >> 3) & 0x1) +#define C_000044_SNAPSHOT_STAT_AK 0xFFFFFFF7 +#define S_000044_FP_DETECT_STAT(x) (((x) & 0x1) << 4) +#define G_000044_FP_DETECT_STAT(x) (((x) >> 4) & 0x1) +#define C_000044_FP_DETECT_STAT 0xFFFFFFEF +#define S_000044_FP_DETECT_STAT_AK(x) (((x) & 0x1) << 4) +#define G_000044_FP_DETECT_STAT_AK(x) (((x) >> 4) & 0x1) +#define C_000044_FP_DETECT_STAT_AK 0xFFFFFFEF +#define S_000044_CRTC2_VLINE_STAT(x) (((x) & 0x1) << 5) +#define G_000044_CRTC2_VLINE_STAT(x) (((x) >> 5) & 0x1) +#define C_000044_CRTC2_VLINE_STAT 0xFFFFFFDF +#define S_000044_CRTC2_VLINE_STAT_AK(x) (((x) & 0x1) << 5) +#define G_000044_CRTC2_VLINE_STAT_AK(x) (((x) >> 5) & 0x1) +#define C_000044_CRTC2_VLINE_STAT_AK 0xFFFFFFDF +#define S_000044_CRTC2_VSYNC_STAT(x) (((x) & 0x1) << 6) +#define G_000044_CRTC2_VSYNC_STAT(x) (((x) >> 6) & 0x1) +#define C_000044_CRTC2_VSYNC_STAT 0xFFFFFFBF +#define S_000044_CRTC2_VSYNC_STAT_AK(x) (((x) & 0x1) << 6) +#define G_000044_CRTC2_VSYNC_STAT_AK(x) (((x) >> 6) & 0x1) +#define C_000044_CRTC2_VSYNC_STAT_AK 0xFFFFFFBF +#define S_000044_SNAPSHOT2_STAT(x) (((x) & 0x1) << 7) +#define G_000044_SNAPSHOT2_STAT(x) (((x) >> 7) & 0x1) +#define C_000044_SNAPSHOT2_STAT 0xFFFFFF7F +#define S_000044_SNAPSHOT2_STAT_AK(x) (((x) & 0x1) << 7) +#define G_000044_SNAPSHOT2_STAT_AK(x) (((x) >> 7) & 0x1) +#define C_000044_SNAPSHOT2_STAT_AK 0xFFFFFF7F +#define S_000044_CAP0_INT_ACTIVE(x) (((x) & 0x1) << 8) +#define G_000044_CAP0_INT_ACTIVE(x) (((x) >> 8) & 0x1) +#define C_000044_CAP0_INT_ACTIVE 0xFFFFFEFF +#define S_000044_CRTC2_VBLANK_STAT(x) (((x) & 0x1) << 9) +#define G_000044_CRTC2_VBLANK_STAT(x) (((x) >> 9) & 0x1) +#define C_000044_CRTC2_VBLANK_STAT 0xFFFFFDFF +#define S_000044_CRTC2_VBLANK_STAT_AK(x) (((x) & 0x1) << 9) +#define G_000044_CRTC2_VBLANK_STAT_AK(x) (((x) >> 9) & 0x1) +#define C_000044_CRTC2_VBLANK_STAT_AK 0xFFFFFDFF +#define S_000044_FP2_DETECT_STAT(x) (((x) & 0x1) << 10) +#define G_000044_FP2_DETECT_STAT(x) (((x) >> 10) & 0x1) +#define C_000044_FP2_DETECT_STAT 0xFFFFFBFF +#define S_000044_FP2_DETECT_STAT_AK(x) (((x) & 0x1) << 10) +#define G_000044_FP2_DETECT_STAT_AK(x) (((x) >> 10) & 0x1) +#define C_000044_FP2_DETECT_STAT_AK 0xFFFFFBFF +#define S_000044_VSYNC_DIFF_OVER_LIMIT_STAT(x) (((x) & 0x1) << 11) +#define G_000044_VSYNC_DIFF_OVER_LIMIT_STAT(x) (((x) >> 11) & 0x1) +#define C_000044_VSYNC_DIFF_OVER_LIMIT_STAT 0xFFFFF7FF +#define S_000044_VSYNC_DIFF_OVER_LIMIT_STAT_AK(x) (((x) & 0x1) << 11) +#define G_000044_VSYNC_DIFF_OVER_LIMIT_STAT_AK(x) (((x) >> 11) & 0x1) +#define C_000044_VSYNC_DIFF_OVER_LIMIT_STAT_AK 0xFFFFF7FF +#define S_000044_DMA_VIPH0_INT(x) (((x) & 0x1) << 12) +#define G_000044_DMA_VIPH0_INT(x) (((x) >> 12) & 0x1) +#define C_000044_DMA_VIPH0_INT 0xFFFFEFFF +#define S_000044_DMA_VIPH0_INT_AK(x) (((x) & 0x1) << 12) +#define G_000044_DMA_VIPH0_INT_AK(x) (((x) >> 12) & 0x1) +#define C_000044_DMA_VIPH0_INT_AK 0xFFFFEFFF +#define S_000044_DMA_VIPH1_INT(x) (((x) & 0x1) << 13) +#define G_000044_DMA_VIPH1_INT(x) (((x) >> 13) & 0x1) +#define C_000044_DMA_VIPH1_INT 0xFFFFDFFF +#define S_000044_DMA_VIPH1_INT_AK(x) (((x) & 0x1) << 13) +#define G_000044_DMA_VIPH1_INT_AK(x) (((x) >> 13) & 0x1) +#define C_000044_DMA_VIPH1_INT_AK 0xFFFFDFFF +#define S_000044_DMA_VIPH2_INT(x) (((x) & 0x1) << 14) +#define G_000044_DMA_VIPH2_INT(x) (((x) >> 14) & 0x1) +#define C_000044_DMA_VIPH2_INT 0xFFFFBFFF +#define S_000044_DMA_VIPH2_INT_AK(x) (((x) & 0x1) << 14) +#define G_000044_DMA_VIPH2_INT_AK(x) (((x) >> 14) & 0x1) +#define C_000044_DMA_VIPH2_INT_AK 0xFFFFBFFF +#define S_000044_DMA_VIPH3_INT(x) (((x) & 0x1) << 15) +#define G_000044_DMA_VIPH3_INT(x) (((x) >> 15) & 0x1) +#define C_000044_DMA_VIPH3_INT 0xFFFF7FFF +#define S_000044_DMA_VIPH3_INT_AK(x) (((x) & 0x1) << 15) +#define G_000044_DMA_VIPH3_INT_AK(x) (((x) >> 15) & 0x1) +#define C_000044_DMA_VIPH3_INT_AK 0xFFFF7FFF +#define S_000044_I2C_INT(x) (((x) & 0x1) << 17) +#define G_000044_I2C_INT(x) (((x) >> 17) & 0x1) +#define C_000044_I2C_INT 0xFFFDFFFF +#define S_000044_I2C_INT_AK(x) (((x) & 0x1) << 17) +#define G_000044_I2C_INT_AK(x) (((x) >> 17) & 0x1) +#define C_000044_I2C_INT_AK 0xFFFDFFFF +#define S_000044_GUI_IDLE_STAT(x) (((x) & 0x1) << 19) +#define G_000044_GUI_IDLE_STAT(x) (((x) >> 19) & 0x1) +#define C_000044_GUI_IDLE_STAT 0xFFF7FFFF +#define S_000044_GUI_IDLE_STAT_AK(x) (((x) & 0x1) << 19) +#define G_000044_GUI_IDLE_STAT_AK(x) (((x) >> 19) & 0x1) +#define C_000044_GUI_IDLE_STAT_AK 0xFFF7FFFF +#define S_000044_VIPH_INT(x) (((x) & 0x1) << 24) +#define G_000044_VIPH_INT(x) (((x) >> 24) & 0x1) +#define C_000044_VIPH_INT 0xFEFFFFFF +#define S_000044_SW_INT(x) (((x) & 0x1) << 25) +#define G_000044_SW_INT(x) (((x) >> 25) & 0x1) +#define C_000044_SW_INT 0xFDFFFFFF +#define S_000044_SW_INT_AK(x) (((x) & 0x1) << 25) +#define G_000044_SW_INT_AK(x) (((x) >> 25) & 0x1) +#define C_000044_SW_INT_AK 0xFDFFFFFF +#define S_000044_SW_INT_SET(x) (((x) & 0x1) << 26) +#define G_000044_SW_INT_SET(x) (((x) >> 26) & 0x1) +#define C_000044_SW_INT_SET 0xFBFFFFFF +#define S_000044_GEYSERVILLE_STAT(x) (((x) & 0x1) << 27) +#define G_000044_GEYSERVILLE_STAT(x) (((x) >> 27) & 0x1) +#define C_000044_GEYSERVILLE_STAT 0xF7FFFFFF +#define S_000044_GEYSERVILLE_STAT_AK(x) (((x) & 0x1) << 27) +#define G_000044_GEYSERVILLE_STAT_AK(x) (((x) >> 27) & 0x1) +#define C_000044_GEYSERVILLE_STAT_AK 0xF7FFFFFF +#define S_000044_HDCP_AUTHORIZED_INT_STAT(x) (((x) & 0x1) << 28) +#define G_000044_HDCP_AUTHORIZED_INT_STAT(x) (((x) >> 28) & 0x1) +#define C_000044_HDCP_AUTHORIZED_INT_STAT 0xEFFFFFFF +#define S_000044_HDCP_AUTHORIZED_INT_AK(x) (((x) & 0x1) << 28) +#define G_000044_HDCP_AUTHORIZED_INT_AK(x) (((x) >> 28) & 0x1) +#define C_000044_HDCP_AUTHORIZED_INT_AK 0xEFFFFFFF +#define S_000044_DVI_I2C_INT_STAT(x) (((x) & 0x1) << 29) +#define G_000044_DVI_I2C_INT_STAT(x) (((x) >> 29) & 0x1) +#define C_000044_DVI_I2C_INT_STAT 0xDFFFFFFF +#define S_000044_DVI_I2C_INT_AK(x) (((x) & 0x1) << 29) +#define G_000044_DVI_I2C_INT_AK(x) (((x) >> 29) & 0x1) +#define C_000044_DVI_I2C_INT_AK 0xDFFFFFFF +#define S_000044_GUIDMA_STAT(x) (((x) & 0x1) << 30) +#define G_000044_GUIDMA_STAT(x) (((x) >> 30) & 0x1) +#define C_000044_GUIDMA_STAT 0xBFFFFFFF +#define S_000044_GUIDMA_AK(x) (((x) & 0x1) << 30) +#define G_000044_GUIDMA_AK(x) (((x) >> 30) & 0x1) +#define C_000044_GUIDMA_AK 0xBFFFFFFF +#define S_000044_VIDDMA_STAT(x) (((x) & 0x1) << 31) +#define G_000044_VIDDMA_STAT(x) (((x) >> 31) & 0x1) +#define C_000044_VIDDMA_STAT 0x7FFFFFFF +#define S_000044_VIDDMA_AK(x) (((x) & 0x1) << 31) +#define G_000044_VIDDMA_AK(x) (((x) >> 31) & 0x1) +#define C_000044_VIDDMA_AK 0x7FFFFFFF +#define R_000050_CRTC_GEN_CNTL 0x000050 +#define S_000050_CRTC_DBL_SCAN_EN(x) (((x) & 0x1) << 0) +#define G_000050_CRTC_DBL_SCAN_EN(x) (((x) >> 0) & 0x1) +#define C_000050_CRTC_DBL_SCAN_EN 0xFFFFFFFE +#define S_000050_CRTC_INTERLACE_EN(x) (((x) & 0x1) << 1) +#define G_000050_CRTC_INTERLACE_EN(x) (((x) >> 1) & 0x1) +#define C_000050_CRTC_INTERLACE_EN 0xFFFFFFFD +#define S_000050_CRTC_C_SYNC_EN(x) (((x) & 0x1) << 4) +#define G_000050_CRTC_C_SYNC_EN(x) (((x) >> 4) & 0x1) +#define C_000050_CRTC_C_SYNC_EN 0xFFFFFFEF +#define S_000050_CRTC_PIX_WIDTH(x) (((x) & 0xF) << 8) +#define G_000050_CRTC_PIX_WIDTH(x) (((x) >> 8) & 0xF) +#define C_000050_CRTC_PIX_WIDTH 0xFFFFF0FF +#define S_000050_CRTC_ICON_EN(x) (((x) & 0x1) << 15) +#define G_000050_CRTC_ICON_EN(x) (((x) >> 15) & 0x1) +#define C_000050_CRTC_ICON_EN 0xFFFF7FFF +#define S_000050_CRTC_CUR_EN(x) (((x) & 0x1) << 16) +#define G_000050_CRTC_CUR_EN(x) (((x) >> 16) & 0x1) +#define C_000050_CRTC_CUR_EN 0xFFFEFFFF +#define S_000050_CRTC_VSTAT_MODE(x) (((x) & 0x3) << 17) +#define G_000050_CRTC_VSTAT_MODE(x) (((x) >> 17) & 0x3) +#define C_000050_CRTC_VSTAT_MODE 0xFFF9FFFF +#define S_000050_CRTC_CUR_MODE(x) (((x) & 0x7) << 20) +#define G_000050_CRTC_CUR_MODE(x) (((x) >> 20) & 0x7) +#define C_000050_CRTC_CUR_MODE 0xFF8FFFFF +#define S_000050_CRTC_EXT_DISP_EN(x) (((x) & 0x1) << 24) +#define G_000050_CRTC_EXT_DISP_EN(x) (((x) >> 24) & 0x1) +#define C_000050_CRTC_EXT_DISP_EN 0xFEFFFFFF +#define S_000050_CRTC_EN(x) (((x) & 0x1) << 25) +#define G_000050_CRTC_EN(x) (((x) >> 25) & 0x1) +#define C_000050_CRTC_EN 0xFDFFFFFF +#define S_000050_CRTC_DISP_REQ_EN_B(x) (((x) & 0x1) << 26) +#define G_000050_CRTC_DISP_REQ_EN_B(x) (((x) >> 26) & 0x1) +#define C_000050_CRTC_DISP_REQ_EN_B 0xFBFFFFFF +#define R_000054_CRTC_EXT_CNTL 0x000054 +#define S_000054_CRTC_VGA_XOVERSCAN(x) (((x) & 0x1) << 0) +#define G_000054_CRTC_VGA_XOVERSCAN(x) (((x) >> 0) & 0x1) +#define C_000054_CRTC_VGA_XOVERSCAN 0xFFFFFFFE +#define S_000054_VGA_BLINK_RATE(x) (((x) & 0x3) << 1) +#define G_000054_VGA_BLINK_RATE(x) (((x) >> 1) & 0x3) +#define C_000054_VGA_BLINK_RATE 0xFFFFFFF9 +#define S_000054_VGA_ATI_LINEAR(x) (((x) & 0x1) << 3) +#define G_000054_VGA_ATI_LINEAR(x) (((x) >> 3) & 0x1) +#define C_000054_VGA_ATI_LINEAR 0xFFFFFFF7 +#define S_000054_VGA_128KAP_PAGING(x) (((x) & 0x1) << 4) +#define G_000054_VGA_128KAP_PAGING(x) (((x) >> 4) & 0x1) +#define C_000054_VGA_128KAP_PAGING 0xFFFFFFEF +#define S_000054_VGA_TEXT_132(x) (((x) & 0x1) << 5) +#define G_000054_VGA_TEXT_132(x) (((x) >> 5) & 0x1) +#define C_000054_VGA_TEXT_132 0xFFFFFFDF +#define S_000054_VGA_XCRT_CNT_EN(x) (((x) & 0x1) << 6) +#define G_000054_VGA_XCRT_CNT_EN(x) (((x) >> 6) & 0x1) +#define C_000054_VGA_XCRT_CNT_EN 0xFFFFFFBF +#define S_000054_CRTC_HSYNC_DIS(x) (((x) & 0x1) << 8) +#define G_000054_CRTC_HSYNC_DIS(x) (((x) >> 8) & 0x1) +#define C_000054_CRTC_HSYNC_DIS 0xFFFFFEFF +#define S_000054_CRTC_VSYNC_DIS(x) (((x) & 0x1) << 9) +#define G_000054_CRTC_VSYNC_DIS(x) (((x) >> 9) & 0x1) +#define C_000054_CRTC_VSYNC_DIS 0xFFFFFDFF +#define S_000054_CRTC_DISPLAY_DIS(x) (((x) & 0x1) << 10) +#define G_000054_CRTC_DISPLAY_DIS(x) (((x) >> 10) & 0x1) +#define C_000054_CRTC_DISPLAY_DIS 0xFFFFFBFF +#define S_000054_CRTC_SYNC_TRISTATE(x) (((x) & 0x1) << 11) +#define G_000054_CRTC_SYNC_TRISTATE(x) (((x) >> 11) & 0x1) +#define C_000054_CRTC_SYNC_TRISTATE 0xFFFFF7FF +#define S_000054_CRTC_HSYNC_TRISTATE(x) (((x) & 0x1) << 12) +#define G_000054_CRTC_HSYNC_TRISTATE(x) (((x) >> 12) & 0x1) +#define C_000054_CRTC_HSYNC_TRISTATE 0xFFFFEFFF +#define S_000054_CRTC_VSYNC_TRISTATE(x) (((x) & 0x1) << 13) +#define G_000054_CRTC_VSYNC_TRISTATE(x) (((x) >> 13) & 0x1) +#define C_000054_CRTC_VSYNC_TRISTATE 0xFFFFDFFF +#define S_000054_CRT_ON(x) (((x) & 0x1) << 15) +#define G_000054_CRT_ON(x) (((x) >> 15) & 0x1) +#define C_000054_CRT_ON 0xFFFF7FFF +#define S_000054_VGA_CUR_B_TEST(x) (((x) & 0x1) << 17) +#define G_000054_VGA_CUR_B_TEST(x) (((x) >> 17) & 0x1) +#define C_000054_VGA_CUR_B_TEST 0xFFFDFFFF +#define S_000054_VGA_PACK_DIS(x) (((x) & 0x1) << 18) +#define G_000054_VGA_PACK_DIS(x) (((x) >> 18) & 0x1) +#define C_000054_VGA_PACK_DIS 0xFFFBFFFF +#define S_000054_VGA_MEM_PS_EN(x) (((x) & 0x1) << 19) +#define G_000054_VGA_MEM_PS_EN(x) (((x) >> 19) & 0x1) +#define C_000054_VGA_MEM_PS_EN 0xFFF7FFFF +#define S_000054_VCRTC_IDX_MASTER(x) (((x) & 0x7F) << 24) +#define G_000054_VCRTC_IDX_MASTER(x) (((x) >> 24) & 0x7F) +#define C_000054_VCRTC_IDX_MASTER 0x80FFFFFF +#define R_000148_MC_FB_LOCATION 0x000148 +#define S_000148_MC_FB_START(x) (((x) & 0xFFFF) << 0) +#define G_000148_MC_FB_START(x) (((x) >> 0) & 0xFFFF) +#define C_000148_MC_FB_START 0xFFFF0000 +#define S_000148_MC_FB_TOP(x) (((x) & 0xFFFF) << 16) +#define G_000148_MC_FB_TOP(x) (((x) >> 16) & 0xFFFF) +#define C_000148_MC_FB_TOP 0x0000FFFF +#define R_00014C_MC_AGP_LOCATION 0x00014C +#define S_00014C_MC_AGP_START(x) (((x) & 0xFFFF) << 0) +#define G_00014C_MC_AGP_START(x) (((x) >> 0) & 0xFFFF) +#define C_00014C_MC_AGP_START 0xFFFF0000 +#define S_00014C_MC_AGP_TOP(x) (((x) & 0xFFFF) << 16) +#define G_00014C_MC_AGP_TOP(x) (((x) >> 16) & 0xFFFF) +#define C_00014C_MC_AGP_TOP 0x0000FFFF +#define R_000170_AGP_BASE 0x000170 +#define S_000170_AGP_BASE_ADDR(x) (((x) & 0xFFFFFFFF) << 0) +#define G_000170_AGP_BASE_ADDR(x) (((x) >> 0) & 0xFFFFFFFF) +#define C_000170_AGP_BASE_ADDR 0x00000000 +#define R_00023C_DISPLAY_BASE_ADDR 0x00023C +#define S_00023C_DISPLAY_BASE_ADDR(x) (((x) & 0xFFFFFFFF) << 0) +#define G_00023C_DISPLAY_BASE_ADDR(x) (((x) >> 0) & 0xFFFFFFFF) +#define C_00023C_DISPLAY_BASE_ADDR 0x00000000 +#define R_000260_CUR_OFFSET 0x000260 +#define S_000260_CUR_OFFSET(x) (((x) & 0x7FFFFFF) << 0) +#define G_000260_CUR_OFFSET(x) (((x) >> 0) & 0x7FFFFFF) +#define C_000260_CUR_OFFSET 0xF8000000 +#define S_000260_CUR_LOCK(x) (((x) & 0x1) << 31) +#define G_000260_CUR_LOCK(x) (((x) >> 31) & 0x1) +#define C_000260_CUR_LOCK 0x7FFFFFFF +#define R_00033C_CRTC2_DISPLAY_BASE_ADDR 0x00033C +#define S_00033C_CRTC2_DISPLAY_BASE_ADDR(x) (((x) & 0xFFFFFFFF) << 0) +#define G_00033C_CRTC2_DISPLAY_BASE_ADDR(x) (((x) >> 0) & 0xFFFFFFFF) +#define C_00033C_CRTC2_DISPLAY_BASE_ADDR 0x00000000 +#define R_000360_CUR2_OFFSET 0x000360 +#define S_000360_CUR2_OFFSET(x) (((x) & 0x7FFFFFF) << 0) +#define G_000360_CUR2_OFFSET(x) (((x) >> 0) & 0x7FFFFFF) +#define C_000360_CUR2_OFFSET 0xF8000000 +#define S_000360_CUR2_LOCK(x) (((x) & 0x1) << 31) +#define G_000360_CUR2_LOCK(x) (((x) >> 31) & 0x1) +#define C_000360_CUR2_LOCK 0x7FFFFFFF +#define R_0003C2_GENMO_WT 0x0003C2 +#define S_0003C2_GENMO_MONO_ADDRESS_B(x) (((x) & 0x1) << 0) +#define G_0003C2_GENMO_MONO_ADDRESS_B(x) (((x) >> 0) & 0x1) +#define C_0003C2_GENMO_MONO_ADDRESS_B 0xFE +#define S_0003C2_VGA_RAM_EN(x) (((x) & 0x1) << 1) +#define G_0003C2_VGA_RAM_EN(x) (((x) >> 1) & 0x1) +#define C_0003C2_VGA_RAM_EN 0xFD +#define S_0003C2_VGA_CKSEL(x) (((x) & 0x3) << 2) +#define G_0003C2_VGA_CKSEL(x) (((x) >> 2) & 0x3) +#define C_0003C2_VGA_CKSEL 0xF3 +#define S_0003C2_ODD_EVEN_MD_PGSEL(x) (((x) & 0x1) << 5) +#define G_0003C2_ODD_EVEN_MD_PGSEL(x) (((x) >> 5) & 0x1) +#define C_0003C2_ODD_EVEN_MD_PGSEL 0xDF +#define S_0003C2_VGA_HSYNC_POL(x) (((x) & 0x1) << 6) +#define G_0003C2_VGA_HSYNC_POL(x) (((x) >> 6) & 0x1) +#define C_0003C2_VGA_HSYNC_POL 0xBF +#define S_0003C2_VGA_VSYNC_POL(x) (((x) & 0x1) << 7) +#define G_0003C2_VGA_VSYNC_POL(x) (((x) >> 7) & 0x1) +#define C_0003C2_VGA_VSYNC_POL 0x7F +#define R_0003F8_CRTC2_GEN_CNTL 0x0003F8 +#define S_0003F8_CRTC2_DBL_SCAN_EN(x) (((x) & 0x1) << 0) +#define G_0003F8_CRTC2_DBL_SCAN_EN(x) (((x) >> 0) & 0x1) +#define C_0003F8_CRTC2_DBL_SCAN_EN 0xFFFFFFFE +#define S_0003F8_CRTC2_INTERLACE_EN(x) (((x) & 0x1) << 1) +#define G_0003F8_CRTC2_INTERLACE_EN(x) (((x) >> 1) & 0x1) +#define C_0003F8_CRTC2_INTERLACE_EN 0xFFFFFFFD +#define S_0003F8_CRTC2_SYNC_TRISTATE(x) (((x) & 0x1) << 4) +#define G_0003F8_CRTC2_SYNC_TRISTATE(x) (((x) >> 4) & 0x1) +#define C_0003F8_CRTC2_SYNC_TRISTATE 0xFFFFFFEF +#define S_0003F8_CRTC2_HSYNC_TRISTATE(x) (((x) & 0x1) << 5) +#define G_0003F8_CRTC2_HSYNC_TRISTATE(x) (((x) >> 5) & 0x1) +#define C_0003F8_CRTC2_HSYNC_TRISTATE 0xFFFFFFDF +#define S_0003F8_CRTC2_VSYNC_TRISTATE(x) (((x) & 0x1) << 6) +#define G_0003F8_CRTC2_VSYNC_TRISTATE(x) (((x) >> 6) & 0x1) +#define C_0003F8_CRTC2_VSYNC_TRISTATE 0xFFFFFFBF +#define S_0003F8_CRT2_ON(x) (((x) & 0x1) << 7) +#define G_0003F8_CRT2_ON(x) (((x) >> 7) & 0x1) +#define C_0003F8_CRT2_ON 0xFFFFFF7F +#define S_0003F8_CRTC2_PIX_WIDTH(x) (((x) & 0xF) << 8) +#define G_0003F8_CRTC2_PIX_WIDTH(x) (((x) >> 8) & 0xF) +#define C_0003F8_CRTC2_PIX_WIDTH 0xFFFFF0FF +#define S_0003F8_CRTC2_ICON_EN(x) (((x) & 0x1) << 15) +#define G_0003F8_CRTC2_ICON_EN(x) (((x) >> 15) & 0x1) +#define C_0003F8_CRTC2_ICON_EN 0xFFFF7FFF +#define S_0003F8_CRTC2_CUR_EN(x) (((x) & 0x1) << 16) +#define G_0003F8_CRTC2_CUR_EN(x) (((x) >> 16) & 0x1) +#define C_0003F8_CRTC2_CUR_EN 0xFFFEFFFF +#define S_0003F8_CRTC2_CUR_MODE(x) (((x) & 0x7) << 20) +#define G_0003F8_CRTC2_CUR_MODE(x) (((x) >> 20) & 0x7) +#define C_0003F8_CRTC2_CUR_MODE 0xFF8FFFFF +#define S_0003F8_CRTC2_DISPLAY_DIS(x) (((x) & 0x1) << 23) +#define G_0003F8_CRTC2_DISPLAY_DIS(x) (((x) >> 23) & 0x1) +#define C_0003F8_CRTC2_DISPLAY_DIS 0xFF7FFFFF +#define S_0003F8_CRTC2_EN(x) (((x) & 0x1) << 25) +#define G_0003F8_CRTC2_EN(x) (((x) >> 25) & 0x1) +#define C_0003F8_CRTC2_EN 0xFDFFFFFF +#define S_0003F8_CRTC2_DISP_REQ_EN_B(x) (((x) & 0x1) << 26) +#define G_0003F8_CRTC2_DISP_REQ_EN_B(x) (((x) >> 26) & 0x1) +#define C_0003F8_CRTC2_DISP_REQ_EN_B 0xFBFFFFFF +#define S_0003F8_CRTC2_C_SYNC_EN(x) (((x) & 0x1) << 27) +#define G_0003F8_CRTC2_C_SYNC_EN(x) (((x) >> 27) & 0x1) +#define C_0003F8_CRTC2_C_SYNC_EN 0xF7FFFFFF +#define S_0003F8_CRTC2_HSYNC_DIS(x) (((x) & 0x1) << 28) +#define G_0003F8_CRTC2_HSYNC_DIS(x) (((x) >> 28) & 0x1) +#define C_0003F8_CRTC2_HSYNC_DIS 0xEFFFFFFF +#define S_0003F8_CRTC2_VSYNC_DIS(x) (((x) & 0x1) << 29) +#define G_0003F8_CRTC2_VSYNC_DIS(x) (((x) >> 29) & 0x1) +#define C_0003F8_CRTC2_VSYNC_DIS 0xDFFFFFFF +#define R_000420_OV0_SCALE_CNTL 0x000420 +#define S_000420_OV0_NO_READ_BEHIND_SCAN(x) (((x) & 0x1) << 1) +#define G_000420_OV0_NO_READ_BEHIND_SCAN(x) (((x) >> 1) & 0x1) +#define C_000420_OV0_NO_READ_BEHIND_SCAN 0xFFFFFFFD +#define S_000420_OV0_HORZ_PICK_NEAREST(x) (((x) & 0x1) << 2) +#define G_000420_OV0_HORZ_PICK_NEAREST(x) (((x) >> 2) & 0x1) +#define C_000420_OV0_HORZ_PICK_NEAREST 0xFFFFFFFB +#define S_000420_OV0_VERT_PICK_NEAREST(x) (((x) & 0x1) << 3) +#define G_000420_OV0_VERT_PICK_NEAREST(x) (((x) >> 3) & 0x1) +#define C_000420_OV0_VERT_PICK_NEAREST 0xFFFFFFF7 +#define S_000420_OV0_SIGNED_UV(x) (((x) & 0x1) << 4) +#define G_000420_OV0_SIGNED_UV(x) (((x) >> 4) & 0x1) +#define C_000420_OV0_SIGNED_UV 0xFFFFFFEF +#define S_000420_OV0_GAMMA_SEL(x) (((x) & 0x7) << 5) +#define G_000420_OV0_GAMMA_SEL(x) (((x) >> 5) & 0x7) +#define C_000420_OV0_GAMMA_SEL 0xFFFFFF1F +#define S_000420_OV0_SURFACE_FORMAT(x) (((x) & 0xF) << 8) +#define G_000420_OV0_SURFACE_FORMAT(x) (((x) >> 8) & 0xF) +#define C_000420_OV0_SURFACE_FORMAT 0xFFFFF0FF +#define S_000420_OV0_ADAPTIVE_DEINT(x) (((x) & 0x1) << 12) +#define G_000420_OV0_ADAPTIVE_DEINT(x) (((x) >> 12) & 0x1) +#define C_000420_OV0_ADAPTIVE_DEINT 0xFFFFEFFF +#define S_000420_OV0_CRTC_SEL(x) (((x) & 0x1) << 14) +#define G_000420_OV0_CRTC_SEL(x) (((x) >> 14) & 0x1) +#define C_000420_OV0_CRTC_SEL 0xFFFFBFFF +#define S_000420_OV0_BURST_PER_PLANE(x) (((x) & 0x7F) << 16) +#define G_000420_OV0_BURST_PER_PLANE(x) (((x) >> 16) & 0x7F) +#define C_000420_OV0_BURST_PER_PLANE 0xFF80FFFF +#define S_000420_OV0_DOUBLE_BUFFER_REGS(x) (((x) & 0x1) << 24) +#define G_000420_OV0_DOUBLE_BUFFER_REGS(x) (((x) >> 24) & 0x1) +#define C_000420_OV0_DOUBLE_BUFFER_REGS 0xFEFFFFFF +#define S_000420_OV0_BANDWIDTH(x) (((x) & 0x1) << 26) +#define G_000420_OV0_BANDWIDTH(x) (((x) >> 26) & 0x1) +#define C_000420_OV0_BANDWIDTH 0xFBFFFFFF +#define S_000420_OV0_LIN_TRANS_BYPASS(x) (((x) & 0x1) << 28) +#define G_000420_OV0_LIN_TRANS_BYPASS(x) (((x) >> 28) & 0x1) +#define C_000420_OV0_LIN_TRANS_BYPASS 0xEFFFFFFF +#define S_000420_OV0_INT_EMU(x) (((x) & 0x1) << 29) +#define G_000420_OV0_INT_EMU(x) (((x) >> 29) & 0x1) +#define C_000420_OV0_INT_EMU 0xDFFFFFFF +#define S_000420_OV0_OVERLAY_EN(x) (((x) & 0x1) << 30) +#define G_000420_OV0_OVERLAY_EN(x) (((x) >> 30) & 0x1) +#define C_000420_OV0_OVERLAY_EN 0xBFFFFFFF +#define S_000420_OV0_SOFT_RESET(x) (((x) & 0x1) << 31) +#define G_000420_OV0_SOFT_RESET(x) (((x) >> 31) & 0x1) +#define C_000420_OV0_SOFT_RESET 0x7FFFFFFF +#define R_00070C_CP_RB_RPTR_ADDR 0x00070C +#define S_00070C_RB_RPTR_SWAP(x) (((x) & 0x3) << 0) +#define G_00070C_RB_RPTR_SWAP(x) (((x) >> 0) & 0x3) +#define C_00070C_RB_RPTR_SWAP 0xFFFFFFFC +#define S_00070C_RB_RPTR_ADDR(x) (((x) & 0x3FFFFFFF) << 2) +#define G_00070C_RB_RPTR_ADDR(x) (((x) >> 2) & 0x3FFFFFFF) +#define C_00070C_RB_RPTR_ADDR 0x00000003 +#define R_000740_CP_CSQ_CNTL 0x000740 +#define S_000740_CSQ_CNT_PRIMARY(x) (((x) & 0xFF) << 0) +#define G_000740_CSQ_CNT_PRIMARY(x) (((x) >> 0) & 0xFF) +#define C_000740_CSQ_CNT_PRIMARY 0xFFFFFF00 +#define S_000740_CSQ_CNT_INDIRECT(x) (((x) & 0xFF) << 8) +#define G_000740_CSQ_CNT_INDIRECT(x) (((x) >> 8) & 0xFF) +#define C_000740_CSQ_CNT_INDIRECT 0xFFFF00FF +#define S_000740_CSQ_MODE(x) (((x) & 0xF) << 28) +#define G_000740_CSQ_MODE(x) (((x) >> 28) & 0xF) +#define C_000740_CSQ_MODE 0x0FFFFFFF +#define R_000770_SCRATCH_UMSK 0x000770 +#define S_000770_SCRATCH_UMSK(x) (((x) & 0x3F) << 0) +#define G_000770_SCRATCH_UMSK(x) (((x) >> 0) & 0x3F) +#define C_000770_SCRATCH_UMSK 0xFFFFFFC0 +#define S_000770_SCRATCH_SWAP(x) (((x) & 0x3) << 16) +#define G_000770_SCRATCH_SWAP(x) (((x) >> 16) & 0x3) +#define C_000770_SCRATCH_SWAP 0xFFFCFFFF +#define R_000774_SCRATCH_ADDR 0x000774 +#define S_000774_SCRATCH_ADDR(x) (((x) & 0x7FFFFFF) << 5) +#define G_000774_SCRATCH_ADDR(x) (((x) >> 5) & 0x7FFFFFF) +#define C_000774_SCRATCH_ADDR 0x0000001F +#define R_0007C0_CP_STAT 0x0007C0 +#define S_0007C0_MRU_BUSY(x) (((x) & 0x1) << 0) +#define G_0007C0_MRU_BUSY(x) (((x) >> 0) & 0x1) +#define C_0007C0_MRU_BUSY 0xFFFFFFFE +#define S_0007C0_MWU_BUSY(x) (((x) & 0x1) << 1) +#define G_0007C0_MWU_BUSY(x) (((x) >> 1) & 0x1) +#define C_0007C0_MWU_BUSY 0xFFFFFFFD +#define S_0007C0_RSIU_BUSY(x) (((x) & 0x1) << 2) +#define G_0007C0_RSIU_BUSY(x) (((x) >> 2) & 0x1) +#define C_0007C0_RSIU_BUSY 0xFFFFFFFB +#define S_0007C0_RCIU_BUSY(x) (((x) & 0x1) << 3) +#define G_0007C0_RCIU_BUSY(x) (((x) >> 3) & 0x1) +#define C_0007C0_RCIU_BUSY 0xFFFFFFF7 +#define S_0007C0_CSF_PRIMARY_BUSY(x) (((x) & 0x1) << 9) +#define G_0007C0_CSF_PRIMARY_BUSY(x) (((x) >> 9) & 0x1) +#define C_0007C0_CSF_PRIMARY_BUSY 0xFFFFFDFF +#define S_0007C0_CSF_INDIRECT_BUSY(x) (((x) & 0x1) << 10) +#define G_0007C0_CSF_INDIRECT_BUSY(x) (((x) >> 10) & 0x1) +#define C_0007C0_CSF_INDIRECT_BUSY 0xFFFFFBFF +#define S_0007C0_CSQ_PRIMARY_BUSY(x) (((x) & 0x1) << 11) +#define G_0007C0_CSQ_PRIMARY_BUSY(x) (((x) >> 11) & 0x1) +#define C_0007C0_CSQ_PRIMARY_BUSY 0xFFFFF7FF +#define S_0007C0_CSQ_INDIRECT_BUSY(x) (((x) & 0x1) << 12) +#define G_0007C0_CSQ_INDIRECT_BUSY(x) (((x) >> 12) & 0x1) +#define C_0007C0_CSQ_INDIRECT_BUSY 0xFFFFEFFF +#define S_0007C0_CSI_BUSY(x) (((x) & 0x1) << 13) +#define G_0007C0_CSI_BUSY(x) (((x) >> 13) & 0x1) +#define C_0007C0_CSI_BUSY 0xFFFFDFFF +#define S_0007C0_GUIDMA_BUSY(x) (((x) & 0x1) << 28) +#define G_0007C0_GUIDMA_BUSY(x) (((x) >> 28) & 0x1) +#define C_0007C0_GUIDMA_BUSY 0xEFFFFFFF +#define S_0007C0_VIDDMA_BUSY(x) (((x) & 0x1) << 29) +#define G_0007C0_VIDDMA_BUSY(x) (((x) >> 29) & 0x1) +#define C_0007C0_VIDDMA_BUSY 0xDFFFFFFF +#define S_0007C0_CMDSTRM_BUSY(x) (((x) & 0x1) << 30) +#define G_0007C0_CMDSTRM_BUSY(x) (((x) >> 30) & 0x1) +#define C_0007C0_CMDSTRM_BUSY 0xBFFFFFFF +#define S_0007C0_CP_BUSY(x) (((x) & 0x1) << 31) +#define G_0007C0_CP_BUSY(x) (((x) >> 31) & 0x1) +#define C_0007C0_CP_BUSY 0x7FFFFFFF +#define R_000E40_RBBM_STATUS 0x000E40 +#define S_000E40_CMDFIFO_AVAIL(x) (((x) & 0x7F) << 0) +#define G_000E40_CMDFIFO_AVAIL(x) (((x) >> 0) & 0x7F) +#define C_000E40_CMDFIFO_AVAIL 0xFFFFFF80 +#define S_000E40_HIRQ_ON_RBB(x) (((x) & 0x1) << 8) +#define G_000E40_HIRQ_ON_RBB(x) (((x) >> 8) & 0x1) +#define C_000E40_HIRQ_ON_RBB 0xFFFFFEFF +#define S_000E40_CPRQ_ON_RBB(x) (((x) & 0x1) << 9) +#define G_000E40_CPRQ_ON_RBB(x) (((x) >> 9) & 0x1) +#define C_000E40_CPRQ_ON_RBB 0xFFFFFDFF +#define S_000E40_CFRQ_ON_RBB(x) (((x) & 0x1) << 10) +#define G_000E40_CFRQ_ON_RBB(x) (((x) >> 10) & 0x1) +#define C_000E40_CFRQ_ON_RBB 0xFFFFFBFF +#define S_000E40_HIRQ_IN_RTBUF(x) (((x) & 0x1) << 11) +#define G_000E40_HIRQ_IN_RTBUF(x) (((x) >> 11) & 0x1) +#define C_000E40_HIRQ_IN_RTBUF 0xFFFFF7FF +#define S_000E40_CPRQ_IN_RTBUF(x) (((x) & 0x1) << 12) +#define G_000E40_CPRQ_IN_RTBUF(x) (((x) >> 12) & 0x1) +#define C_000E40_CPRQ_IN_RTBUF 0xFFFFEFFF +#define S_000E40_CFRQ_IN_RTBUF(x) (((x) & 0x1) << 13) +#define G_000E40_CFRQ_IN_RTBUF(x) (((x) >> 13) & 0x1) +#define C_000E40_CFRQ_IN_RTBUF 0xFFFFDFFF +#define S_000E40_CF_PIPE_BUSY(x) (((x) & 0x1) << 14) +#define G_000E40_CF_PIPE_BUSY(x) (((x) >> 14) & 0x1) +#define C_000E40_CF_PIPE_BUSY 0xFFFFBFFF +#define S_000E40_ENG_EV_BUSY(x) (((x) & 0x1) << 15) +#define G_000E40_ENG_EV_BUSY(x) (((x) >> 15) & 0x1) +#define C_000E40_ENG_EV_BUSY 0xFFFF7FFF +#define S_000E40_CP_CMDSTRM_BUSY(x) (((x) & 0x1) << 16) +#define G_000E40_CP_CMDSTRM_BUSY(x) (((x) >> 16) & 0x1) +#define C_000E40_CP_CMDSTRM_BUSY 0xFFFEFFFF +#define S_000E40_E2_BUSY(x) (((x) & 0x1) << 17) +#define G_000E40_E2_BUSY(x) (((x) >> 17) & 0x1) +#define C_000E40_E2_BUSY 0xFFFDFFFF +#define S_000E40_RB2D_BUSY(x) (((x) & 0x1) << 18) +#define G_000E40_RB2D_BUSY(x) (((x) >> 18) & 0x1) +#define C_000E40_RB2D_BUSY 0xFFFBFFFF +#define S_000E40_RB3D_BUSY(x) (((x) & 0x1) << 19) +#define G_000E40_RB3D_BUSY(x) (((x) >> 19) & 0x1) +#define C_000E40_RB3D_BUSY 0xFFF7FFFF +#define S_000E40_SE_BUSY(x) (((x) & 0x1) << 20) +#define G_000E40_SE_BUSY(x) (((x) >> 20) & 0x1) +#define C_000E40_SE_BUSY 0xFFEFFFFF +#define S_000E40_RE_BUSY(x) (((x) & 0x1) << 21) +#define G_000E40_RE_BUSY(x) (((x) >> 21) & 0x1) +#define C_000E40_RE_BUSY 0xFFDFFFFF +#define S_000E40_TAM_BUSY(x) (((x) & 0x1) << 22) +#define G_000E40_TAM_BUSY(x) (((x) >> 22) & 0x1) +#define C_000E40_TAM_BUSY 0xFFBFFFFF +#define S_000E40_TDM_BUSY(x) (((x) & 0x1) << 23) +#define G_000E40_TDM_BUSY(x) (((x) >> 23) & 0x1) +#define C_000E40_TDM_BUSY 0xFF7FFFFF +#define S_000E40_PB_BUSY(x) (((x) & 0x1) << 24) +#define G_000E40_PB_BUSY(x) (((x) >> 24) & 0x1) +#define C_000E40_PB_BUSY 0xFEFFFFFF +#define S_000E40_GUI_ACTIVE(x) (((x) & 0x1) << 31) +#define G_000E40_GUI_ACTIVE(x) (((x) >> 31) & 0x1) +#define C_000E40_GUI_ACTIVE 0x7FFFFFFF + + +#define R_00000D_SCLK_CNTL 0x00000D +#define S_00000D_SCLK_SRC_SEL(x) (((x) & 0x7) << 0) +#define G_00000D_SCLK_SRC_SEL(x) (((x) >> 0) & 0x7) +#define C_00000D_SCLK_SRC_SEL 0xFFFFFFF8 +#define S_00000D_TCLK_SRC_SEL(x) (((x) & 0x7) << 8) +#define G_00000D_TCLK_SRC_SEL(x) (((x) >> 8) & 0x7) +#define C_00000D_TCLK_SRC_SEL 0xFFFFF8FF +#define S_00000D_FORCE_CP(x) (((x) & 0x1) << 16) +#define G_00000D_FORCE_CP(x) (((x) >> 16) & 0x1) +#define C_00000D_FORCE_CP 0xFFFEFFFF +#define S_00000D_FORCE_HDP(x) (((x) & 0x1) << 17) +#define G_00000D_FORCE_HDP(x) (((x) >> 17) & 0x1) +#define C_00000D_FORCE_HDP 0xFFFDFFFF +#define S_00000D_FORCE_DISP(x) (((x) & 0x1) << 18) +#define G_00000D_FORCE_DISP(x) (((x) >> 18) & 0x1) +#define C_00000D_FORCE_DISP 0xFFFBFFFF +#define S_00000D_FORCE_TOP(x) (((x) & 0x1) << 19) +#define G_00000D_FORCE_TOP(x) (((x) >> 19) & 0x1) +#define C_00000D_FORCE_TOP 0xFFF7FFFF +#define S_00000D_FORCE_E2(x) (((x) & 0x1) << 20) +#define G_00000D_FORCE_E2(x) (((x) >> 20) & 0x1) +#define C_00000D_FORCE_E2 0xFFEFFFFF +#define S_00000D_FORCE_SE(x) (((x) & 0x1) << 21) +#define G_00000D_FORCE_SE(x) (((x) >> 21) & 0x1) +#define C_00000D_FORCE_SE 0xFFDFFFFF +#define S_00000D_FORCE_IDCT(x) (((x) & 0x1) << 22) +#define G_00000D_FORCE_IDCT(x) (((x) >> 22) & 0x1) +#define C_00000D_FORCE_IDCT 0xFFBFFFFF +#define S_00000D_FORCE_VIP(x) (((x) & 0x1) << 23) +#define G_00000D_FORCE_VIP(x) (((x) >> 23) & 0x1) +#define C_00000D_FORCE_VIP 0xFF7FFFFF +#define S_00000D_FORCE_RE(x) (((x) & 0x1) << 24) +#define G_00000D_FORCE_RE(x) (((x) >> 24) & 0x1) +#define C_00000D_FORCE_RE 0xFEFFFFFF +#define S_00000D_FORCE_PB(x) (((x) & 0x1) << 25) +#define G_00000D_FORCE_PB(x) (((x) >> 25) & 0x1) +#define C_00000D_FORCE_PB 0xFDFFFFFF +#define S_00000D_FORCE_TAM(x) (((x) & 0x1) << 26) +#define G_00000D_FORCE_TAM(x) (((x) >> 26) & 0x1) +#define C_00000D_FORCE_TAM 0xFBFFFFFF +#define S_00000D_FORCE_TDM(x) (((x) & 0x1) << 27) +#define G_00000D_FORCE_TDM(x) (((x) >> 27) & 0x1) +#define C_00000D_FORCE_TDM 0xF7FFFFFF +#define S_00000D_FORCE_RB(x) (((x) & 0x1) << 28) +#define G_00000D_FORCE_RB(x) (((x) >> 28) & 0x1) +#define C_00000D_FORCE_RB 0xEFFFFFFF + +/* PLL regs */ +#define SCLK_CNTL 0xd +#define FORCE_HDP (1 << 17) +#define CLK_PWRMGT_CNTL 0x14 +#define GLOBAL_PMAN_EN (1 << 10) +#define DISP_PM (1 << 20) +#define PLL_PWRMGT_CNTL 0x15 +#define MPLL_TURNOFF (1 << 0) +#define SPLL_TURNOFF (1 << 1) +#define PPLL_TURNOFF (1 << 2) +#define P2PLL_TURNOFF (1 << 3) +#define TVPLL_TURNOFF (1 << 4) +#define MOBILE_SU (1 << 16) +#define SU_SCLK_USE_BCLK (1 << 17) +#define SCLK_CNTL2 0x1e +#define REDUCED_SPEED_SCLK_MODE (1 << 16) +#define REDUCED_SPEED_SCLK_SEL(x) ((x) << 17) +#define MCLK_MISC 0x1f +#define EN_MCLK_TRISTATE_IN_SUSPEND (1 << 18) +#define SCLK_MORE_CNTL 0x35 +#define REDUCED_SPEED_SCLK_EN (1 << 16) +#define IO_CG_VOLTAGE_DROP (1 << 17) +#define VOLTAGE_DELAY_SEL(x) ((x) << 20) +#define VOLTAGE_DROP_SYNC (1 << 19) + +/* mmreg */ +#define DISP_PWR_MAN 0xd08 +#define DISP_D3_GRPH_RST (1 << 18) +#define DISP_D3_SUBPIC_RST (1 << 19) +#define DISP_D3_OV0_RST (1 << 20) +#define DISP_D1D2_GRPH_RST (1 << 21) +#define DISP_D1D2_SUBPIC_RST (1 << 22) +#define DISP_D1D2_OV0_RST (1 << 23) +#define DISP_DVO_ENABLE_RST (1 << 24) +#define TV_ENABLE_RST (1 << 25) +#define AUTO_PWRUP_EN (1 << 26) + +#endif diff --git a/sys/dev/drm2/radeon/r200.c b/sys/dev/drm2/radeon/r200.c new file mode 100644 index 00000000000..fe8b768046c --- /dev/null +++ b/sys/dev/drm2/radeon/r200.c @@ -0,0 +1,552 @@ +/* + * Copyright 2008 Advanced Micro Devices, Inc. + * Copyright 2008 Red Hat Inc. + * Copyright 2009 Jerome Glisse. + * + * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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. + * + * Authors: Dave Airlie + * Alex Deucher + * Jerome Glisse + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include "radeon_reg.h" +#include "radeon.h" +#include "radeon_asic.h" + +#include "r100d.h" +#include "r200_reg_safe.h" + +#include "r100_track.h" + +static int r200_get_vtx_size_0(uint32_t vtx_fmt_0) +{ + int vtx_size, i; + vtx_size = 2; + + if (vtx_fmt_0 & R200_VTX_Z0) + vtx_size++; + if (vtx_fmt_0 & R200_VTX_W0) + vtx_size++; + /* blend weight */ + if (vtx_fmt_0 & (0x7 << R200_VTX_WEIGHT_COUNT_SHIFT)) + vtx_size += (vtx_fmt_0 >> R200_VTX_WEIGHT_COUNT_SHIFT) & 0x7; + if (vtx_fmt_0 & R200_VTX_PV_MATRIX_SEL) + vtx_size++; + if (vtx_fmt_0 & R200_VTX_N0) + vtx_size += 3; + if (vtx_fmt_0 & R200_VTX_POINT_SIZE) + vtx_size++; + if (vtx_fmt_0 & R200_VTX_DISCRETE_FOG) + vtx_size++; + if (vtx_fmt_0 & R200_VTX_SHININESS_0) + vtx_size++; + if (vtx_fmt_0 & R200_VTX_SHININESS_1) + vtx_size++; + for (i = 0; i < 8; i++) { + int color_size = (vtx_fmt_0 >> (11 + 2*i)) & 0x3; + switch (color_size) { + case 0: break; + case 1: vtx_size++; break; + case 2: vtx_size += 3; break; + case 3: vtx_size += 4; break; + } + } + if (vtx_fmt_0 & R200_VTX_XY1) + vtx_size += 2; + if (vtx_fmt_0 & R200_VTX_Z1) + vtx_size++; + if (vtx_fmt_0 & R200_VTX_W1) + vtx_size++; + if (vtx_fmt_0 & R200_VTX_N1) + vtx_size += 3; + return vtx_size; +} + +int r200_copy_dma(struct radeon_device *rdev, + uint64_t src_offset, + uint64_t dst_offset, + unsigned num_gpu_pages, + struct radeon_fence **fence) +{ + struct radeon_ring *ring = &rdev->ring[RADEON_RING_TYPE_GFX_INDEX]; + uint32_t size; + uint32_t cur_size; + int i, num_loops; + int r = 0; + + /* radeon pitch is /64 */ + size = num_gpu_pages << RADEON_GPU_PAGE_SHIFT; + num_loops = DIV_ROUND_UP(size, 0x1FFFFF); + r = radeon_ring_lock(rdev, ring, num_loops * 4 + 64); + if (r) { + DRM_ERROR("radeon: moving bo (%d).\n", r); + return r; + } + /* Must wait for 2D idle & clean before DMA or hangs might happen */ + radeon_ring_write(ring, PACKET0(RADEON_WAIT_UNTIL, 0)); + radeon_ring_write(ring, (1 << 16)); + for (i = 0; i < num_loops; i++) { + cur_size = size; + if (cur_size > 0x1FFFFF) { + cur_size = 0x1FFFFF; + } + size -= cur_size; + radeon_ring_write(ring, PACKET0(0x720, 2)); + radeon_ring_write(ring, src_offset); + radeon_ring_write(ring, dst_offset); + radeon_ring_write(ring, cur_size | (1 << 31) | (1 << 30)); + src_offset += cur_size; + dst_offset += cur_size; + } + radeon_ring_write(ring, PACKET0(RADEON_WAIT_UNTIL, 0)); + radeon_ring_write(ring, RADEON_WAIT_DMA_GUI_IDLE); + if (fence) { + r = radeon_fence_emit(rdev, fence, RADEON_RING_TYPE_GFX_INDEX); + } + radeon_ring_unlock_commit(rdev, ring); + return r; +} + + +static int r200_get_vtx_size_1(uint32_t vtx_fmt_1) +{ + int vtx_size, i, tex_size; + vtx_size = 0; + for (i = 0; i < 6; i++) { + tex_size = (vtx_fmt_1 >> (i * 3)) & 0x7; + if (tex_size > 4) + continue; + vtx_size += tex_size; + } + return vtx_size; +} + +int r200_packet0_check(struct radeon_cs_parser *p, + struct radeon_cs_packet *pkt, + unsigned idx, unsigned reg) +{ + struct radeon_cs_reloc *reloc; + struct r100_cs_track *track; + volatile uint32_t *ib; + uint32_t tmp; + int r; + int i; + int face; + u32 tile_flags = 0; + u32 idx_value; + + ib = p->ib.ptr; + track = (struct r100_cs_track *)p->track; + idx_value = radeon_get_ib_value(p, idx); + switch (reg) { + case RADEON_CRTC_GUI_TRIG_VLINE: + r = r100_cs_packet_parse_vline(p); + if (r) { + DRM_ERROR("No reloc for ib[%d]=0x%04X\n", + idx, reg); + r100_cs_dump_packet(p, pkt); + return r; + } + break; + /* FIXME: only allow PACKET3 blit? easier to check for out of + * range access */ + case RADEON_DST_PITCH_OFFSET: + case RADEON_SRC_PITCH_OFFSET: + r = r100_reloc_pitch_offset(p, pkt, idx, reg); + if (r) + return r; + break; + case RADEON_RB3D_DEPTHOFFSET: + r = r100_cs_packet_next_reloc(p, &reloc); + if (r) { + DRM_ERROR("No reloc for ib[%d]=0x%04X\n", + idx, reg); + r100_cs_dump_packet(p, pkt); + return r; + } + track->zb.robj = reloc->robj; + track->zb.offset = idx_value; + track->zb_dirty = true; + ib[idx] = idx_value + ((u32)reloc->lobj.gpu_offset); + break; + case RADEON_RB3D_COLOROFFSET: + r = r100_cs_packet_next_reloc(p, &reloc); + if (r) { + DRM_ERROR("No reloc for ib[%d]=0x%04X\n", + idx, reg); + r100_cs_dump_packet(p, pkt); + return r; + } + track->cb[0].robj = reloc->robj; + track->cb[0].offset = idx_value; + track->cb_dirty = true; + ib[idx] = idx_value + ((u32)reloc->lobj.gpu_offset); + break; + case R200_PP_TXOFFSET_0: + case R200_PP_TXOFFSET_1: + case R200_PP_TXOFFSET_2: + case R200_PP_TXOFFSET_3: + case R200_PP_TXOFFSET_4: + case R200_PP_TXOFFSET_5: + i = (reg - R200_PP_TXOFFSET_0) / 24; + r = r100_cs_packet_next_reloc(p, &reloc); + if (r) { + DRM_ERROR("No reloc for ib[%d]=0x%04X\n", + idx, reg); + r100_cs_dump_packet(p, pkt); + return r; + } + if (!(p->cs_flags & RADEON_CS_KEEP_TILING_FLAGS)) { + if (reloc->lobj.tiling_flags & RADEON_TILING_MACRO) + tile_flags |= R200_TXO_MACRO_TILE; + if (reloc->lobj.tiling_flags & RADEON_TILING_MICRO) + tile_flags |= R200_TXO_MICRO_TILE; + + tmp = idx_value & ~(0x7 << 2); + tmp |= tile_flags; + ib[idx] = tmp + ((u32)reloc->lobj.gpu_offset); + } else + ib[idx] = idx_value + ((u32)reloc->lobj.gpu_offset); + track->textures[i].robj = reloc->robj; + track->tex_dirty = true; + break; + case R200_PP_CUBIC_OFFSET_F1_0: + case R200_PP_CUBIC_OFFSET_F2_0: + case R200_PP_CUBIC_OFFSET_F3_0: + case R200_PP_CUBIC_OFFSET_F4_0: + case R200_PP_CUBIC_OFFSET_F5_0: + case R200_PP_CUBIC_OFFSET_F1_1: + case R200_PP_CUBIC_OFFSET_F2_1: + case R200_PP_CUBIC_OFFSET_F3_1: + case R200_PP_CUBIC_OFFSET_F4_1: + case R200_PP_CUBIC_OFFSET_F5_1: + case R200_PP_CUBIC_OFFSET_F1_2: + case R200_PP_CUBIC_OFFSET_F2_2: + case R200_PP_CUBIC_OFFSET_F3_2: + case R200_PP_CUBIC_OFFSET_F4_2: + case R200_PP_CUBIC_OFFSET_F5_2: + case R200_PP_CUBIC_OFFSET_F1_3: + case R200_PP_CUBIC_OFFSET_F2_3: + case R200_PP_CUBIC_OFFSET_F3_3: + case R200_PP_CUBIC_OFFSET_F4_3: + case R200_PP_CUBIC_OFFSET_F5_3: + case R200_PP_CUBIC_OFFSET_F1_4: + case R200_PP_CUBIC_OFFSET_F2_4: + case R200_PP_CUBIC_OFFSET_F3_4: + case R200_PP_CUBIC_OFFSET_F4_4: + case R200_PP_CUBIC_OFFSET_F5_4: + case R200_PP_CUBIC_OFFSET_F1_5: + case R200_PP_CUBIC_OFFSET_F2_5: + case R200_PP_CUBIC_OFFSET_F3_5: + case R200_PP_CUBIC_OFFSET_F4_5: + case R200_PP_CUBIC_OFFSET_F5_5: + i = (reg - R200_PP_TXOFFSET_0) / 24; + face = (reg - ((i * 24) + R200_PP_TXOFFSET_0)) / 4; + r = r100_cs_packet_next_reloc(p, &reloc); + if (r) { + DRM_ERROR("No reloc for ib[%d]=0x%04X\n", + idx, reg); + r100_cs_dump_packet(p, pkt); + return r; + } + track->textures[i].cube_info[face - 1].offset = idx_value; + ib[idx] = idx_value + ((u32)reloc->lobj.gpu_offset); + track->textures[i].cube_info[face - 1].robj = reloc->robj; + track->tex_dirty = true; + break; + case RADEON_RE_WIDTH_HEIGHT: + track->maxy = ((idx_value >> 16) & 0x7FF); + track->cb_dirty = true; + track->zb_dirty = true; + break; + case RADEON_RB3D_COLORPITCH: + r = r100_cs_packet_next_reloc(p, &reloc); + if (r) { + DRM_ERROR("No reloc for ib[%d]=0x%04X\n", + idx, reg); + r100_cs_dump_packet(p, pkt); + return r; + } + + if (!(p->cs_flags & RADEON_CS_KEEP_TILING_FLAGS)) { + if (reloc->lobj.tiling_flags & RADEON_TILING_MACRO) + tile_flags |= RADEON_COLOR_TILE_ENABLE; + if (reloc->lobj.tiling_flags & RADEON_TILING_MICRO) + tile_flags |= RADEON_COLOR_MICROTILE_ENABLE; + + tmp = idx_value & ~(0x7 << 16); + tmp |= tile_flags; + ib[idx] = tmp; + } else + ib[idx] = idx_value; + + track->cb[0].pitch = idx_value & RADEON_COLORPITCH_MASK; + track->cb_dirty = true; + break; + case RADEON_RB3D_DEPTHPITCH: + track->zb.pitch = idx_value & RADEON_DEPTHPITCH_MASK; + track->zb_dirty = true; + break; + case RADEON_RB3D_CNTL: + switch ((idx_value >> RADEON_RB3D_COLOR_FORMAT_SHIFT) & 0x1f) { + case 7: + case 8: + case 9: + case 11: + case 12: + track->cb[0].cpp = 1; + break; + case 3: + case 4: + case 15: + track->cb[0].cpp = 2; + break; + case 6: + track->cb[0].cpp = 4; + break; + default: + DRM_ERROR("Invalid color buffer format (%d) !\n", + ((idx_value >> RADEON_RB3D_COLOR_FORMAT_SHIFT) & 0x1f)); + return -EINVAL; + } + if (idx_value & RADEON_DEPTHXY_OFFSET_ENABLE) { + DRM_ERROR("No support for depth xy offset in kms\n"); + return -EINVAL; + } + + track->z_enabled = !!(idx_value & RADEON_Z_ENABLE); + track->cb_dirty = true; + track->zb_dirty = true; + break; + case RADEON_RB3D_ZSTENCILCNTL: + switch (idx_value & 0xf) { + case 0: + track->zb.cpp = 2; + break; + case 2: + case 3: + case 4: + case 5: + case 9: + case 11: + track->zb.cpp = 4; + break; + default: + break; + } + track->zb_dirty = true; + break; + case RADEON_RB3D_ZPASS_ADDR: + r = r100_cs_packet_next_reloc(p, &reloc); + if (r) { + DRM_ERROR("No reloc for ib[%d]=0x%04X\n", + idx, reg); + r100_cs_dump_packet(p, pkt); + return r; + } + ib[idx] = idx_value + ((u32)reloc->lobj.gpu_offset); + break; + case RADEON_PP_CNTL: + { + uint32_t temp = idx_value >> 4; + for (i = 0; i < track->num_texture; i++) + track->textures[i].enabled = !!(temp & (1 << i)); + track->tex_dirty = true; + } + break; + case RADEON_SE_VF_CNTL: + track->vap_vf_cntl = idx_value; + break; + case 0x210c: + /* VAP_VF_MAX_VTX_INDX */ + track->max_indx = idx_value & 0x00FFFFFFUL; + break; + case R200_SE_VTX_FMT_0: + track->vtx_size = r200_get_vtx_size_0(idx_value); + break; + case R200_SE_VTX_FMT_1: + track->vtx_size += r200_get_vtx_size_1(idx_value); + break; + case R200_PP_TXSIZE_0: + case R200_PP_TXSIZE_1: + case R200_PP_TXSIZE_2: + case R200_PP_TXSIZE_3: + case R200_PP_TXSIZE_4: + case R200_PP_TXSIZE_5: + i = (reg - R200_PP_TXSIZE_0) / 32; + track->textures[i].width = (idx_value & RADEON_TEX_USIZE_MASK) + 1; + track->textures[i].height = ((idx_value & RADEON_TEX_VSIZE_MASK) >> RADEON_TEX_VSIZE_SHIFT) + 1; + track->tex_dirty = true; + break; + case R200_PP_TXPITCH_0: + case R200_PP_TXPITCH_1: + case R200_PP_TXPITCH_2: + case R200_PP_TXPITCH_3: + case R200_PP_TXPITCH_4: + case R200_PP_TXPITCH_5: + i = (reg - R200_PP_TXPITCH_0) / 32; + track->textures[i].pitch = idx_value + 32; + track->tex_dirty = true; + break; + case R200_PP_TXFILTER_0: + case R200_PP_TXFILTER_1: + case R200_PP_TXFILTER_2: + case R200_PP_TXFILTER_3: + case R200_PP_TXFILTER_4: + case R200_PP_TXFILTER_5: + i = (reg - R200_PP_TXFILTER_0) / 32; + track->textures[i].num_levels = ((idx_value & R200_MAX_MIP_LEVEL_MASK) + >> R200_MAX_MIP_LEVEL_SHIFT); + tmp = (idx_value >> 23) & 0x7; + if (tmp == 2 || tmp == 6) + track->textures[i].roundup_w = false; + tmp = (idx_value >> 27) & 0x7; + if (tmp == 2 || tmp == 6) + track->textures[i].roundup_h = false; + track->tex_dirty = true; + break; + case R200_PP_TXMULTI_CTL_0: + case R200_PP_TXMULTI_CTL_1: + case R200_PP_TXMULTI_CTL_2: + case R200_PP_TXMULTI_CTL_3: + case R200_PP_TXMULTI_CTL_4: + case R200_PP_TXMULTI_CTL_5: + i = (reg - R200_PP_TXMULTI_CTL_0) / 32; + break; + case R200_PP_TXFORMAT_X_0: + case R200_PP_TXFORMAT_X_1: + case R200_PP_TXFORMAT_X_2: + case R200_PP_TXFORMAT_X_3: + case R200_PP_TXFORMAT_X_4: + case R200_PP_TXFORMAT_X_5: + i = (reg - R200_PP_TXFORMAT_X_0) / 32; + track->textures[i].txdepth = idx_value & 0x7; + tmp = (idx_value >> 16) & 0x3; + /* 2D, 3D, CUBE */ + switch (tmp) { + case 0: + case 3: + case 4: + case 5: + case 6: + case 7: + /* 1D/2D */ + track->textures[i].tex_coord_type = 0; + break; + case 1: + /* CUBE */ + track->textures[i].tex_coord_type = 2; + break; + case 2: + /* 3D */ + track->textures[i].tex_coord_type = 1; + break; + } + track->tex_dirty = true; + break; + case R200_PP_TXFORMAT_0: + case R200_PP_TXFORMAT_1: + case R200_PP_TXFORMAT_2: + case R200_PP_TXFORMAT_3: + case R200_PP_TXFORMAT_4: + case R200_PP_TXFORMAT_5: + i = (reg - R200_PP_TXFORMAT_0) / 32; + if (idx_value & R200_TXFORMAT_NON_POWER2) { + track->textures[i].use_pitch = 1; + } else { + track->textures[i].use_pitch = 0; + track->textures[i].width = 1 << ((idx_value >> RADEON_TXFORMAT_WIDTH_SHIFT) & RADEON_TXFORMAT_WIDTH_MASK); + track->textures[i].height = 1 << ((idx_value >> RADEON_TXFORMAT_HEIGHT_SHIFT) & RADEON_TXFORMAT_HEIGHT_MASK); + } + if (idx_value & R200_TXFORMAT_LOOKUP_DISABLE) + track->textures[i].lookup_disable = true; + switch ((idx_value & RADEON_TXFORMAT_FORMAT_MASK)) { + case R200_TXFORMAT_I8: + case R200_TXFORMAT_RGB332: + case R200_TXFORMAT_Y8: + track->textures[i].cpp = 1; + track->textures[i].compress_format = R100_TRACK_COMP_NONE; + break; + case R200_TXFORMAT_AI88: + case R200_TXFORMAT_ARGB1555: + case R200_TXFORMAT_RGB565: + case R200_TXFORMAT_ARGB4444: + case R200_TXFORMAT_VYUY422: + case R200_TXFORMAT_YVYU422: + case R200_TXFORMAT_LDVDU655: + case R200_TXFORMAT_DVDU88: + case R200_TXFORMAT_AVYU4444: + track->textures[i].cpp = 2; + track->textures[i].compress_format = R100_TRACK_COMP_NONE; + break; + case R200_TXFORMAT_ARGB8888: + case R200_TXFORMAT_RGBA8888: + case R200_TXFORMAT_ABGR8888: + case R200_TXFORMAT_BGR111110: + case R200_TXFORMAT_LDVDU8888: + track->textures[i].cpp = 4; + track->textures[i].compress_format = R100_TRACK_COMP_NONE; + break; + case R200_TXFORMAT_DXT1: + track->textures[i].cpp = 1; + track->textures[i].compress_format = R100_TRACK_COMP_DXT1; + break; + case R200_TXFORMAT_DXT23: + case R200_TXFORMAT_DXT45: + track->textures[i].cpp = 1; + track->textures[i].compress_format = R100_TRACK_COMP_DXT1; + break; + } + track->textures[i].cube_info[4].width = 1 << ((idx_value >> 16) & 0xf); + track->textures[i].cube_info[4].height = 1 << ((idx_value >> 20) & 0xf); + track->tex_dirty = true; + break; + case R200_PP_CUBIC_FACES_0: + case R200_PP_CUBIC_FACES_1: + case R200_PP_CUBIC_FACES_2: + case R200_PP_CUBIC_FACES_3: + case R200_PP_CUBIC_FACES_4: + case R200_PP_CUBIC_FACES_5: + tmp = idx_value; + i = (reg - R200_PP_CUBIC_FACES_0) / 32; + for (face = 0; face < 4; face++) { + track->textures[i].cube_info[face].width = 1 << ((tmp >> (face * 8)) & 0xf); + track->textures[i].cube_info[face].height = 1 << ((tmp >> ((face * 8) + 4)) & 0xf); + } + track->tex_dirty = true; + break; + default: + DRM_ERROR("Forbidden register 0x%04X in cs at %d\n", + reg, idx); + return -EINVAL; + } + return 0; +} + +void r200_set_safe_registers(struct radeon_device *rdev) +{ + rdev->config.r100.reg_safe_bm = r200_reg_safe_bm; + rdev->config.r100.reg_safe_bm_size = DRM_ARRAY_SIZE(r200_reg_safe_bm); +} diff --git a/sys/dev/drm2/radeon/r200_reg_safe.h b/sys/dev/drm2/radeon/r200_reg_safe.h new file mode 100644 index 00000000000..3ef5980f147 --- /dev/null +++ b/sys/dev/drm2/radeon/r200_reg_safe.h @@ -0,0 +1,31 @@ +#include +__FBSDID("$FreeBSD$"); + +static const unsigned r200_reg_safe_bm[102] = { + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0x17FF1FFF, 0xFFFFFFFC, 0xFFFFFFFF, 0xFF30FFBF, + 0xFFFFFFF8, 0xC3E6FFFF, 0xFFFFF6DF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFE7FE1F, 0xF003FFFF, 0x7EFFFFFF, 0xFFFF803C, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFEFCE, 0xFFFEFFFF, 0xFFFFFFFE, + 0x020E0FF0, 0xFFCC83FD, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFBFFFF, 0xEFFCFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xDFDFDFDF, 0x3FFDDFDF, 0xFFFFFFFF, 0xFFFFFF7F, + 0xFFFFFFFF, 0x00FFFFFF, 0x00000000, 0x00000000, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFE3F, 0xFFFFFFEF, +}; diff --git a/sys/dev/drm2/radeon/r300.c b/sys/dev/drm2/radeon/r300.c new file mode 100644 index 00000000000..aa9073cf270 --- /dev/null +++ b/sys/dev/drm2/radeon/r300.c @@ -0,0 +1,1566 @@ +/* + * Copyright 2008 Advanced Micro Devices, Inc. + * Copyright 2008 Red Hat Inc. + * Copyright 2009 Jerome Glisse. + * + * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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. + * + * Authors: Dave Airlie + * Alex Deucher + * Jerome Glisse + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include "radeon_reg.h" +#include "radeon.h" +#include "radeon_asic.h" +#include +#include "r100_track.h" +#include "r300d.h" +#include "rv350d.h" +#include "r300_reg_safe.h" + +/* This files gather functions specifics to: r300,r350,rv350,rv370,rv380 + * + * GPU Errata: + * - HOST_PATH_CNTL: r300 family seems to dislike write to HOST_PATH_CNTL + * using MMIO to flush host path read cache, this lead to HARDLOCKUP. + * However, scheduling such write to the ring seems harmless, i suspect + * the CP read collide with the flush somehow, or maybe the MC, hard to + * tell. (Jerome Glisse) + */ + +/* + * rv370,rv380 PCIE GART + */ +static int rv370_debugfs_pcie_gart_info_init(struct radeon_device *rdev); + +void rv370_pcie_gart_tlb_flush(struct radeon_device *rdev) +{ + uint32_t tmp; + int i; + + /* Workaround HW bug do flush 2 times */ + for (i = 0; i < 2; i++) { + tmp = RREG32_PCIE(RADEON_PCIE_TX_GART_CNTL); + WREG32_PCIE(RADEON_PCIE_TX_GART_CNTL, tmp | RADEON_PCIE_TX_GART_INVALIDATE_TLB); + (void)RREG32_PCIE(RADEON_PCIE_TX_GART_CNTL); + WREG32_PCIE(RADEON_PCIE_TX_GART_CNTL, tmp); + } + mb(); +} + +#define R300_PTE_WRITEABLE (1 << 2) +#define R300_PTE_READABLE (1 << 3) + +int rv370_pcie_gart_set_page(struct radeon_device *rdev, int i, uint64_t addr) +{ + volatile uint32_t *ptr = rdev->gart.ptr; + + if (i < 0 || i > rdev->gart.num_gpu_pages) { + return -EINVAL; + } + addr = (lower_32_bits(addr) >> 8) | + ((upper_32_bits(addr) & 0xff) << 24) | + R300_PTE_WRITEABLE | R300_PTE_READABLE; + /* on x86 we want this to be CPU endian, on powerpc + * on powerpc without HW swappers, it'll get swapped on way + * into VRAM - so no need for cpu_to_le32 on VRAM tables */ + ptr += i; + *ptr = (uint32_t)addr; + return 0; +} + +int rv370_pcie_gart_init(struct radeon_device *rdev) +{ + int r; + + if (rdev->gart.robj) { + DRM_ERROR("RV370 PCIE GART already initialized\n"); + return 0; + } + /* Initialize common gart structure */ + r = radeon_gart_init(rdev); + if (r) + return r; + r = rv370_debugfs_pcie_gart_info_init(rdev); + if (r) + DRM_ERROR("Failed to register debugfs file for PCIE gart !\n"); + rdev->gart.table_size = rdev->gart.num_gpu_pages * 4; + rdev->asic->gart.tlb_flush = &rv370_pcie_gart_tlb_flush; + rdev->asic->gart.set_page = &rv370_pcie_gart_set_page; + return radeon_gart_table_vram_alloc(rdev); +} + +int rv370_pcie_gart_enable(struct radeon_device *rdev) +{ + uint32_t table_addr; + uint32_t tmp; + int r; + + if (rdev->gart.robj == NULL) { + dev_err(rdev->dev, "No VRAM object for PCIE GART.\n"); + return -EINVAL; + } + r = radeon_gart_table_vram_pin(rdev); + if (r) + return r; + radeon_gart_restore(rdev); + /* discard memory request outside of configured range */ + tmp = RADEON_PCIE_TX_GART_UNMAPPED_ACCESS_DISCARD; + WREG32_PCIE(RADEON_PCIE_TX_GART_CNTL, tmp); + WREG32_PCIE(RADEON_PCIE_TX_GART_START_LO, rdev->mc.gtt_start); + tmp = rdev->mc.gtt_end & ~RADEON_GPU_PAGE_MASK; + WREG32_PCIE(RADEON_PCIE_TX_GART_END_LO, tmp); + WREG32_PCIE(RADEON_PCIE_TX_GART_START_HI, 0); + WREG32_PCIE(RADEON_PCIE_TX_GART_END_HI, 0); + table_addr = rdev->gart.table_addr; + WREG32_PCIE(RADEON_PCIE_TX_GART_BASE, table_addr); + /* FIXME: setup default page */ + WREG32_PCIE(RADEON_PCIE_TX_DISCARD_RD_ADDR_LO, rdev->mc.vram_start); + WREG32_PCIE(RADEON_PCIE_TX_DISCARD_RD_ADDR_HI, 0); + /* Clear error */ + WREG32_PCIE(RADEON_PCIE_TX_GART_ERROR, 0); + tmp = RREG32_PCIE(RADEON_PCIE_TX_GART_CNTL); + tmp |= RADEON_PCIE_TX_GART_EN; + tmp |= RADEON_PCIE_TX_GART_UNMAPPED_ACCESS_DISCARD; + WREG32_PCIE(RADEON_PCIE_TX_GART_CNTL, tmp); + rv370_pcie_gart_tlb_flush(rdev); + DRM_INFO("PCIE GART of %uM enabled (table at 0x%016llX).\n", + (unsigned)(rdev->mc.gtt_size >> 20), + (unsigned long long)table_addr); + rdev->gart.ready = true; + return 0; +} + +void rv370_pcie_gart_disable(struct radeon_device *rdev) +{ + u32 tmp; + + WREG32_PCIE(RADEON_PCIE_TX_GART_START_LO, 0); + WREG32_PCIE(RADEON_PCIE_TX_GART_END_LO, 0); + WREG32_PCIE(RADEON_PCIE_TX_GART_START_HI, 0); + WREG32_PCIE(RADEON_PCIE_TX_GART_END_HI, 0); + tmp = RREG32_PCIE(RADEON_PCIE_TX_GART_CNTL); + tmp |= RADEON_PCIE_TX_GART_UNMAPPED_ACCESS_DISCARD; + WREG32_PCIE(RADEON_PCIE_TX_GART_CNTL, tmp & ~RADEON_PCIE_TX_GART_EN); + radeon_gart_table_vram_unpin(rdev); +} + +void rv370_pcie_gart_fini(struct radeon_device *rdev) +{ + radeon_gart_fini(rdev); + rv370_pcie_gart_disable(rdev); + radeon_gart_table_vram_free(rdev); +} + +void r300_fence_ring_emit(struct radeon_device *rdev, + struct radeon_fence *fence) +{ + struct radeon_ring *ring = &rdev->ring[fence->ring]; + + /* Who ever call radeon_fence_emit should call ring_lock and ask + * for enough space (today caller are ib schedule and buffer move) */ + /* Write SC register so SC & US assert idle */ + radeon_ring_write(ring, PACKET0(R300_RE_SCISSORS_TL, 0)); + radeon_ring_write(ring, 0); + radeon_ring_write(ring, PACKET0(R300_RE_SCISSORS_BR, 0)); + radeon_ring_write(ring, 0); + /* Flush 3D cache */ + radeon_ring_write(ring, PACKET0(R300_RB3D_DSTCACHE_CTLSTAT, 0)); + radeon_ring_write(ring, R300_RB3D_DC_FLUSH); + radeon_ring_write(ring, PACKET0(R300_RB3D_ZCACHE_CTLSTAT, 0)); + radeon_ring_write(ring, R300_ZC_FLUSH); + /* Wait until IDLE & CLEAN */ + radeon_ring_write(ring, PACKET0(RADEON_WAIT_UNTIL, 0)); + radeon_ring_write(ring, (RADEON_WAIT_3D_IDLECLEAN | + RADEON_WAIT_2D_IDLECLEAN | + RADEON_WAIT_DMA_GUI_IDLE)); + radeon_ring_write(ring, PACKET0(RADEON_HOST_PATH_CNTL, 0)); + radeon_ring_write(ring, rdev->config.r300.hdp_cntl | + RADEON_HDP_READ_BUFFER_INVALIDATE); + radeon_ring_write(ring, PACKET0(RADEON_HOST_PATH_CNTL, 0)); + radeon_ring_write(ring, rdev->config.r300.hdp_cntl); + /* Emit fence sequence & fire IRQ */ + radeon_ring_write(ring, PACKET0(rdev->fence_drv[fence->ring].scratch_reg, 0)); + radeon_ring_write(ring, fence->seq); + radeon_ring_write(ring, PACKET0(RADEON_GEN_INT_STATUS, 0)); + radeon_ring_write(ring, RADEON_SW_INT_FIRE); +} + +void r300_ring_start(struct radeon_device *rdev, struct radeon_ring *ring) +{ + unsigned gb_tile_config; + int r; + + /* Sub pixel 1/12 so we can have 4K rendering according to doc */ + gb_tile_config = (R300_ENABLE_TILING | R300_TILE_SIZE_16); + switch(rdev->num_gb_pipes) { + case 2: + gb_tile_config |= R300_PIPE_COUNT_R300; + break; + case 3: + gb_tile_config |= R300_PIPE_COUNT_R420_3P; + break; + case 4: + gb_tile_config |= R300_PIPE_COUNT_R420; + break; + case 1: + default: + gb_tile_config |= R300_PIPE_COUNT_RV350; + break; + } + + r = radeon_ring_lock(rdev, ring, 64); + if (r) { + return; + } + radeon_ring_write(ring, PACKET0(RADEON_ISYNC_CNTL, 0)); + radeon_ring_write(ring, + RADEON_ISYNC_ANY2D_IDLE3D | + RADEON_ISYNC_ANY3D_IDLE2D | + RADEON_ISYNC_WAIT_IDLEGUI | + RADEON_ISYNC_CPSCRATCH_IDLEGUI); + radeon_ring_write(ring, PACKET0(R300_GB_TILE_CONFIG, 0)); + radeon_ring_write(ring, gb_tile_config); + radeon_ring_write(ring, PACKET0(RADEON_WAIT_UNTIL, 0)); + radeon_ring_write(ring, + RADEON_WAIT_2D_IDLECLEAN | + RADEON_WAIT_3D_IDLECLEAN); + radeon_ring_write(ring, PACKET0(R300_DST_PIPE_CONFIG, 0)); + radeon_ring_write(ring, R300_PIPE_AUTO_CONFIG); + radeon_ring_write(ring, PACKET0(R300_GB_SELECT, 0)); + radeon_ring_write(ring, 0); + radeon_ring_write(ring, PACKET0(R300_GB_ENABLE, 0)); + radeon_ring_write(ring, 0); + radeon_ring_write(ring, PACKET0(R300_RB3D_DSTCACHE_CTLSTAT, 0)); + radeon_ring_write(ring, R300_RB3D_DC_FLUSH | R300_RB3D_DC_FREE); + radeon_ring_write(ring, PACKET0(R300_RB3D_ZCACHE_CTLSTAT, 0)); + radeon_ring_write(ring, R300_ZC_FLUSH | R300_ZC_FREE); + radeon_ring_write(ring, PACKET0(RADEON_WAIT_UNTIL, 0)); + radeon_ring_write(ring, + RADEON_WAIT_2D_IDLECLEAN | + RADEON_WAIT_3D_IDLECLEAN); + radeon_ring_write(ring, PACKET0(R300_GB_AA_CONFIG, 0)); + radeon_ring_write(ring, 0); + radeon_ring_write(ring, PACKET0(R300_RB3D_DSTCACHE_CTLSTAT, 0)); + radeon_ring_write(ring, R300_RB3D_DC_FLUSH | R300_RB3D_DC_FREE); + radeon_ring_write(ring, PACKET0(R300_RB3D_ZCACHE_CTLSTAT, 0)); + radeon_ring_write(ring, R300_ZC_FLUSH | R300_ZC_FREE); + radeon_ring_write(ring, PACKET0(R300_GB_MSPOS0, 0)); + radeon_ring_write(ring, + ((6 << R300_MS_X0_SHIFT) | + (6 << R300_MS_Y0_SHIFT) | + (6 << R300_MS_X1_SHIFT) | + (6 << R300_MS_Y1_SHIFT) | + (6 << R300_MS_X2_SHIFT) | + (6 << R300_MS_Y2_SHIFT) | + (6 << R300_MSBD0_Y_SHIFT) | + (6 << R300_MSBD0_X_SHIFT))); + radeon_ring_write(ring, PACKET0(R300_GB_MSPOS1, 0)); + radeon_ring_write(ring, + ((6 << R300_MS_X3_SHIFT) | + (6 << R300_MS_Y3_SHIFT) | + (6 << R300_MS_X4_SHIFT) | + (6 << R300_MS_Y4_SHIFT) | + (6 << R300_MS_X5_SHIFT) | + (6 << R300_MS_Y5_SHIFT) | + (6 << R300_MSBD1_SHIFT))); + radeon_ring_write(ring, PACKET0(R300_GA_ENHANCE, 0)); + radeon_ring_write(ring, R300_GA_DEADLOCK_CNTL | R300_GA_FASTSYNC_CNTL); + radeon_ring_write(ring, PACKET0(R300_GA_POLY_MODE, 0)); + radeon_ring_write(ring, + R300_FRONT_PTYPE_TRIANGE | R300_BACK_PTYPE_TRIANGE); + radeon_ring_write(ring, PACKET0(R300_GA_ROUND_MODE, 0)); + radeon_ring_write(ring, + R300_GEOMETRY_ROUND_NEAREST | + R300_COLOR_ROUND_NEAREST); + radeon_ring_unlock_commit(rdev, ring); +} + +static void r300_errata(struct radeon_device *rdev) +{ + rdev->pll_errata = 0; + + if (rdev->family == CHIP_R300 && + (RREG32(RADEON_CONFIG_CNTL) & RADEON_CFG_ATI_REV_ID_MASK) == RADEON_CFG_ATI_REV_A11) { + rdev->pll_errata |= CHIP_ERRATA_R300_CG; + } +} + +int r300_mc_wait_for_idle(struct radeon_device *rdev) +{ + unsigned i; + uint32_t tmp; + + for (i = 0; i < rdev->usec_timeout; i++) { + /* read MC_STATUS */ + tmp = RREG32(RADEON_MC_STATUS); + if (tmp & R300_MC_IDLE) { + return 0; + } + DRM_UDELAY(1); + } + return -1; +} + +static void r300_gpu_init(struct radeon_device *rdev) +{ + uint32_t gb_tile_config, tmp; + + if ((rdev->family == CHIP_R300 && rdev->ddev->pci_device != 0x4144) || + (rdev->family == CHIP_R350 && rdev->ddev->pci_device != 0x4148)) { + /* r300,r350 */ + rdev->num_gb_pipes = 2; + } else { + /* rv350,rv370,rv380,r300 AD, r350 AH */ + rdev->num_gb_pipes = 1; + } + rdev->num_z_pipes = 1; + gb_tile_config = (R300_ENABLE_TILING | R300_TILE_SIZE_16); + switch (rdev->num_gb_pipes) { + case 2: + gb_tile_config |= R300_PIPE_COUNT_R300; + break; + case 3: + gb_tile_config |= R300_PIPE_COUNT_R420_3P; + break; + case 4: + gb_tile_config |= R300_PIPE_COUNT_R420; + break; + default: + case 1: + gb_tile_config |= R300_PIPE_COUNT_RV350; + break; + } + WREG32(R300_GB_TILE_CONFIG, gb_tile_config); + + if (r100_gui_wait_for_idle(rdev)) { + DRM_ERROR("Failed to wait GUI idle while " + "programming pipes. Bad things might happen.\n"); + } + + tmp = RREG32(R300_DST_PIPE_CONFIG); + WREG32(R300_DST_PIPE_CONFIG, tmp | R300_PIPE_AUTO_CONFIG); + + WREG32(R300_RB2D_DSTCACHE_MODE, + R300_DC_AUTOFLUSH_ENABLE | + R300_DC_DC_DISABLE_IGNORE_PE); + + if (r100_gui_wait_for_idle(rdev)) { + DRM_ERROR("Failed to wait GUI idle while " + "programming pipes. Bad things might happen.\n"); + } + if (r300_mc_wait_for_idle(rdev)) { + DRM_ERROR("Failed to wait MC idle while " + "programming pipes. Bad things might happen.\n"); + } + DRM_INFO("radeon: %d quad pipes, %d Z pipes initialized.\n", + rdev->num_gb_pipes, rdev->num_z_pipes); +} + +int r300_asic_reset(struct radeon_device *rdev) +{ + struct r100_mc_save save; + u32 status, tmp; + int ret = 0; + + status = RREG32(R_000E40_RBBM_STATUS); + if (!G_000E40_GUI_ACTIVE(status)) { + return 0; + } + r100_mc_stop(rdev, &save); + status = RREG32(R_000E40_RBBM_STATUS); + dev_info(rdev->dev, "(%s:%d) RBBM_STATUS=0x%08X\n", __func__, __LINE__, status); + /* stop CP */ + WREG32(RADEON_CP_CSQ_CNTL, 0); + tmp = RREG32(RADEON_CP_RB_CNTL); + WREG32(RADEON_CP_RB_CNTL, tmp | RADEON_RB_RPTR_WR_ENA); + WREG32(RADEON_CP_RB_RPTR_WR, 0); + WREG32(RADEON_CP_RB_WPTR, 0); + WREG32(RADEON_CP_RB_CNTL, tmp); + /* save PCI state */ + pci_save_state(device_get_parent(rdev->dev)); + /* disable bus mastering */ + r100_bm_disable(rdev); + WREG32(R_0000F0_RBBM_SOFT_RESET, S_0000F0_SOFT_RESET_VAP(1) | + S_0000F0_SOFT_RESET_GA(1)); + RREG32(R_0000F0_RBBM_SOFT_RESET); + DRM_MDELAY(500); + WREG32(R_0000F0_RBBM_SOFT_RESET, 0); + DRM_MDELAY(1); + status = RREG32(R_000E40_RBBM_STATUS); + dev_info(rdev->dev, "(%s:%d) RBBM_STATUS=0x%08X\n", __func__, __LINE__, status); + /* resetting the CP seems to be problematic sometimes it end up + * hard locking the computer, but it's necessary for successful + * reset more test & playing is needed on R3XX/R4XX to find a + * reliable (if any solution) + */ + WREG32(R_0000F0_RBBM_SOFT_RESET, S_0000F0_SOFT_RESET_CP(1)); + RREG32(R_0000F0_RBBM_SOFT_RESET); + DRM_MDELAY(500); + WREG32(R_0000F0_RBBM_SOFT_RESET, 0); + DRM_MDELAY(1); + status = RREG32(R_000E40_RBBM_STATUS); + dev_info(rdev->dev, "(%s:%d) RBBM_STATUS=0x%08X\n", __func__, __LINE__, status); + /* restore PCI & busmastering */ + pci_restore_state(device_get_parent(rdev->dev)); + r100_enable_bm(rdev); + /* Check if GPU is idle */ + if (G_000E40_GA_BUSY(status) || G_000E40_VAP_BUSY(status)) { + dev_err(rdev->dev, "failed to reset GPU\n"); + ret = -1; + } else + dev_info(rdev->dev, "GPU reset succeed\n"); + r100_mc_resume(rdev, &save); + return ret; +} + +/* + * r300,r350,rv350,rv380 VRAM info + */ +void r300_mc_init(struct radeon_device *rdev) +{ + u64 base; + u32 tmp; + + /* DDR for all card after R300 & IGP */ + rdev->mc.vram_is_ddr = true; + tmp = RREG32(RADEON_MEM_CNTL); + tmp &= R300_MEM_NUM_CHANNELS_MASK; + switch (tmp) { + case 0: rdev->mc.vram_width = 64; break; + case 1: rdev->mc.vram_width = 128; break; + case 2: rdev->mc.vram_width = 256; break; + default: rdev->mc.vram_width = 128; break; + } + r100_vram_init_sizes(rdev); + base = rdev->mc.aper_base; + if (rdev->flags & RADEON_IS_IGP) + base = (RREG32(RADEON_NB_TOM) & 0xffff) << 16; + radeon_vram_location(rdev, &rdev->mc, base); + rdev->mc.gtt_base_align = 0; + if (!(rdev->flags & RADEON_IS_AGP)) + radeon_gtt_location(rdev, &rdev->mc); + radeon_update_bandwidth_info(rdev); +} + +void rv370_set_pcie_lanes(struct radeon_device *rdev, int lanes) +{ + uint32_t link_width_cntl, mask; + + if (rdev->flags & RADEON_IS_IGP) + return; + + if (!(rdev->flags & RADEON_IS_PCIE)) + return; + + /* FIXME wait for idle */ + + switch (lanes) { + case 0: + mask = RADEON_PCIE_LC_LINK_WIDTH_X0; + break; + case 1: + mask = RADEON_PCIE_LC_LINK_WIDTH_X1; + break; + case 2: + mask = RADEON_PCIE_LC_LINK_WIDTH_X2; + break; + case 4: + mask = RADEON_PCIE_LC_LINK_WIDTH_X4; + break; + case 8: + mask = RADEON_PCIE_LC_LINK_WIDTH_X8; + break; + case 12: + mask = RADEON_PCIE_LC_LINK_WIDTH_X12; + break; + case 16: + default: + mask = RADEON_PCIE_LC_LINK_WIDTH_X16; + break; + } + + link_width_cntl = RREG32_PCIE(RADEON_PCIE_LC_LINK_WIDTH_CNTL); + + if ((link_width_cntl & RADEON_PCIE_LC_LINK_WIDTH_RD_MASK) == + (mask << RADEON_PCIE_LC_LINK_WIDTH_RD_SHIFT)) + return; + + link_width_cntl &= ~(RADEON_PCIE_LC_LINK_WIDTH_MASK | + RADEON_PCIE_LC_RECONFIG_NOW | + RADEON_PCIE_LC_RECONFIG_LATER | + RADEON_PCIE_LC_SHORT_RECONFIG_EN); + link_width_cntl |= mask; + WREG32_PCIE(RADEON_PCIE_LC_LINK_WIDTH_CNTL, link_width_cntl); + WREG32_PCIE(RADEON_PCIE_LC_LINK_WIDTH_CNTL, (link_width_cntl | + RADEON_PCIE_LC_RECONFIG_NOW)); + + /* wait for lane set to complete */ + link_width_cntl = RREG32_PCIE(RADEON_PCIE_LC_LINK_WIDTH_CNTL); + while (link_width_cntl == 0xffffffff) + link_width_cntl = RREG32_PCIE(RADEON_PCIE_LC_LINK_WIDTH_CNTL); + +} + +int rv370_get_pcie_lanes(struct radeon_device *rdev) +{ + u32 link_width_cntl; + + if (rdev->flags & RADEON_IS_IGP) + return 0; + + if (!(rdev->flags & RADEON_IS_PCIE)) + return 0; + + /* FIXME wait for idle */ + + link_width_cntl = RREG32_PCIE(RADEON_PCIE_LC_LINK_WIDTH_CNTL); + + switch ((link_width_cntl & RADEON_PCIE_LC_LINK_WIDTH_RD_MASK) >> RADEON_PCIE_LC_LINK_WIDTH_RD_SHIFT) { + case RADEON_PCIE_LC_LINK_WIDTH_X0: + return 0; + case RADEON_PCIE_LC_LINK_WIDTH_X1: + return 1; + case RADEON_PCIE_LC_LINK_WIDTH_X2: + return 2; + case RADEON_PCIE_LC_LINK_WIDTH_X4: + return 4; + case RADEON_PCIE_LC_LINK_WIDTH_X8: + return 8; + case RADEON_PCIE_LC_LINK_WIDTH_X16: + default: + return 16; + } +} + +#if defined(CONFIG_DEBUG_FS) +static int rv370_debugfs_pcie_gart_info(struct seq_file *m, void *data) +{ + struct drm_info_node *node = (struct drm_info_node *) m->private; + struct drm_device *dev = node->minor->dev; + struct radeon_device *rdev = dev->dev_private; + uint32_t tmp; + + tmp = RREG32_PCIE(RADEON_PCIE_TX_GART_CNTL); + seq_printf(m, "PCIE_TX_GART_CNTL 0x%08x\n", tmp); + tmp = RREG32_PCIE(RADEON_PCIE_TX_GART_BASE); + seq_printf(m, "PCIE_TX_GART_BASE 0x%08x\n", tmp); + tmp = RREG32_PCIE(RADEON_PCIE_TX_GART_START_LO); + seq_printf(m, "PCIE_TX_GART_START_LO 0x%08x\n", tmp); + tmp = RREG32_PCIE(RADEON_PCIE_TX_GART_START_HI); + seq_printf(m, "PCIE_TX_GART_START_HI 0x%08x\n", tmp); + tmp = RREG32_PCIE(RADEON_PCIE_TX_GART_END_LO); + seq_printf(m, "PCIE_TX_GART_END_LO 0x%08x\n", tmp); + tmp = RREG32_PCIE(RADEON_PCIE_TX_GART_END_HI); + seq_printf(m, "PCIE_TX_GART_END_HI 0x%08x\n", tmp); + tmp = RREG32_PCIE(RADEON_PCIE_TX_GART_ERROR); + seq_printf(m, "PCIE_TX_GART_ERROR 0x%08x\n", tmp); + return 0; +} + +static struct drm_info_list rv370_pcie_gart_info_list[] = { + {"rv370_pcie_gart_info", rv370_debugfs_pcie_gart_info, 0, NULL}, +}; +#endif + +static int rv370_debugfs_pcie_gart_info_init(struct radeon_device *rdev) +{ +#if defined(CONFIG_DEBUG_FS) + return radeon_debugfs_add_files(rdev, rv370_pcie_gart_info_list, 1); +#else + return 0; +#endif +} + +static int r300_packet0_check(struct radeon_cs_parser *p, + struct radeon_cs_packet *pkt, + unsigned idx, unsigned reg) +{ + struct radeon_cs_reloc *reloc; + struct r100_cs_track *track; + volatile uint32_t *ib; + uint32_t tmp, tile_flags = 0; + unsigned i; + int r; + u32 idx_value; + + ib = p->ib.ptr; + track = (struct r100_cs_track *)p->track; + idx_value = radeon_get_ib_value(p, idx); + + switch(reg) { + case AVIVO_D1MODE_VLINE_START_END: + case RADEON_CRTC_GUI_TRIG_VLINE: + r = r100_cs_packet_parse_vline(p); + if (r) { + DRM_ERROR("No reloc for ib[%d]=0x%04X\n", + idx, reg); + r100_cs_dump_packet(p, pkt); + return r; + } + break; + case RADEON_DST_PITCH_OFFSET: + case RADEON_SRC_PITCH_OFFSET: + r = r100_reloc_pitch_offset(p, pkt, idx, reg); + if (r) + return r; + break; + case R300_RB3D_COLOROFFSET0: + case R300_RB3D_COLOROFFSET1: + case R300_RB3D_COLOROFFSET2: + case R300_RB3D_COLOROFFSET3: + i = (reg - R300_RB3D_COLOROFFSET0) >> 2; + r = r100_cs_packet_next_reloc(p, &reloc); + if (r) { + DRM_ERROR("No reloc for ib[%d]=0x%04X\n", + idx, reg); + r100_cs_dump_packet(p, pkt); + return r; + } + track->cb[i].robj = reloc->robj; + track->cb[i].offset = idx_value; + track->cb_dirty = true; + ib[idx] = idx_value + ((u32)reloc->lobj.gpu_offset); + break; + case R300_ZB_DEPTHOFFSET: + r = r100_cs_packet_next_reloc(p, &reloc); + if (r) { + DRM_ERROR("No reloc for ib[%d]=0x%04X\n", + idx, reg); + r100_cs_dump_packet(p, pkt); + return r; + } + track->zb.robj = reloc->robj; + track->zb.offset = idx_value; + track->zb_dirty = true; + ib[idx] = idx_value + ((u32)reloc->lobj.gpu_offset); + break; + case R300_TX_OFFSET_0: + case R300_TX_OFFSET_0+4: + case R300_TX_OFFSET_0+8: + case R300_TX_OFFSET_0+12: + case R300_TX_OFFSET_0+16: + case R300_TX_OFFSET_0+20: + case R300_TX_OFFSET_0+24: + case R300_TX_OFFSET_0+28: + case R300_TX_OFFSET_0+32: + case R300_TX_OFFSET_0+36: + case R300_TX_OFFSET_0+40: + case R300_TX_OFFSET_0+44: + case R300_TX_OFFSET_0+48: + case R300_TX_OFFSET_0+52: + case R300_TX_OFFSET_0+56: + case R300_TX_OFFSET_0+60: + i = (reg - R300_TX_OFFSET_0) >> 2; + r = r100_cs_packet_next_reloc(p, &reloc); + if (r) { + DRM_ERROR("No reloc for ib[%d]=0x%04X\n", + idx, reg); + r100_cs_dump_packet(p, pkt); + return r; + } + + if (p->cs_flags & RADEON_CS_KEEP_TILING_FLAGS) { + ib[idx] = (idx_value & 31) | /* keep the 1st 5 bits */ + ((idx_value & ~31) + (u32)reloc->lobj.gpu_offset); + } else { + if (reloc->lobj.tiling_flags & RADEON_TILING_MACRO) + tile_flags |= R300_TXO_MACRO_TILE; + if (reloc->lobj.tiling_flags & RADEON_TILING_MICRO) + tile_flags |= R300_TXO_MICRO_TILE; + else if (reloc->lobj.tiling_flags & RADEON_TILING_MICRO_SQUARE) + tile_flags |= R300_TXO_MICRO_TILE_SQUARE; + + tmp = idx_value + ((u32)reloc->lobj.gpu_offset); + tmp |= tile_flags; + ib[idx] = tmp; + } + track->textures[i].robj = reloc->robj; + track->tex_dirty = true; + break; + /* Tracked registers */ + case 0x2084: + /* VAP_VF_CNTL */ + track->vap_vf_cntl = idx_value; + break; + case 0x20B4: + /* VAP_VTX_SIZE */ + track->vtx_size = idx_value & 0x7F; + break; + case 0x2134: + /* VAP_VF_MAX_VTX_INDX */ + track->max_indx = idx_value & 0x00FFFFFFUL; + break; + case 0x2088: + /* VAP_ALT_NUM_VERTICES - only valid on r500 */ + if (p->rdev->family < CHIP_RV515) + goto fail; + track->vap_alt_nverts = idx_value & 0xFFFFFF; + break; + case 0x43E4: + /* SC_SCISSOR1 */ + track->maxy = ((idx_value >> 13) & 0x1FFF) + 1; + if (p->rdev->family < CHIP_RV515) { + track->maxy -= 1440; + } + track->cb_dirty = true; + track->zb_dirty = true; + break; + case 0x4E00: + /* RB3D_CCTL */ + if ((idx_value & (1 << 10)) && /* CMASK_ENABLE */ + p->rdev->cmask_filp != p->filp) { + DRM_ERROR("Invalid RB3D_CCTL: Cannot enable CMASK.\n"); + return -EINVAL; + } + track->num_cb = ((idx_value >> 5) & 0x3) + 1; + track->cb_dirty = true; + break; + case 0x4E38: + case 0x4E3C: + case 0x4E40: + case 0x4E44: + /* RB3D_COLORPITCH0 */ + /* RB3D_COLORPITCH1 */ + /* RB3D_COLORPITCH2 */ + /* RB3D_COLORPITCH3 */ + if (!(p->cs_flags & RADEON_CS_KEEP_TILING_FLAGS)) { + r = r100_cs_packet_next_reloc(p, &reloc); + if (r) { + DRM_ERROR("No reloc for ib[%d]=0x%04X\n", + idx, reg); + r100_cs_dump_packet(p, pkt); + return r; + } + + if (reloc->lobj.tiling_flags & RADEON_TILING_MACRO) + tile_flags |= R300_COLOR_TILE_ENABLE; + if (reloc->lobj.tiling_flags & RADEON_TILING_MICRO) + tile_flags |= R300_COLOR_MICROTILE_ENABLE; + else if (reloc->lobj.tiling_flags & RADEON_TILING_MICRO_SQUARE) + tile_flags |= R300_COLOR_MICROTILE_SQUARE_ENABLE; + + tmp = idx_value & ~(0x7 << 16); + tmp |= tile_flags; + ib[idx] = tmp; + } + i = (reg - 0x4E38) >> 2; + track->cb[i].pitch = idx_value & 0x3FFE; + switch (((idx_value >> 21) & 0xF)) { + case 9: + case 11: + case 12: + track->cb[i].cpp = 1; + break; + case 3: + case 4: + case 13: + case 15: + track->cb[i].cpp = 2; + break; + case 5: + if (p->rdev->family < CHIP_RV515) { + DRM_ERROR("Invalid color buffer format (%d)!\n", + ((idx_value >> 21) & 0xF)); + return -EINVAL; + } + /* Pass through. */ + case 6: + track->cb[i].cpp = 4; + break; + case 10: + track->cb[i].cpp = 8; + break; + case 7: + track->cb[i].cpp = 16; + break; + default: + DRM_ERROR("Invalid color buffer format (%d) !\n", + ((idx_value >> 21) & 0xF)); + return -EINVAL; + } + track->cb_dirty = true; + break; + case 0x4F00: + /* ZB_CNTL */ + if (idx_value & 2) { + track->z_enabled = true; + } else { + track->z_enabled = false; + } + track->zb_dirty = true; + break; + case 0x4F10: + /* ZB_FORMAT */ + switch ((idx_value & 0xF)) { + case 0: + case 1: + track->zb.cpp = 2; + break; + case 2: + track->zb.cpp = 4; + break; + default: + DRM_ERROR("Invalid z buffer format (%d) !\n", + (idx_value & 0xF)); + return -EINVAL; + } + track->zb_dirty = true; + break; + case 0x4F24: + /* ZB_DEPTHPITCH */ + if (!(p->cs_flags & RADEON_CS_KEEP_TILING_FLAGS)) { + r = r100_cs_packet_next_reloc(p, &reloc); + if (r) { + DRM_ERROR("No reloc for ib[%d]=0x%04X\n", + idx, reg); + r100_cs_dump_packet(p, pkt); + return r; + } + + if (reloc->lobj.tiling_flags & RADEON_TILING_MACRO) + tile_flags |= R300_DEPTHMACROTILE_ENABLE; + if (reloc->lobj.tiling_flags & RADEON_TILING_MICRO) + tile_flags |= R300_DEPTHMICROTILE_TILED; + else if (reloc->lobj.tiling_flags & RADEON_TILING_MICRO_SQUARE) + tile_flags |= R300_DEPTHMICROTILE_TILED_SQUARE; + + tmp = idx_value & ~(0x7 << 16); + tmp |= tile_flags; + ib[idx] = tmp; + } + track->zb.pitch = idx_value & 0x3FFC; + track->zb_dirty = true; + break; + case 0x4104: + /* TX_ENABLE */ + for (i = 0; i < 16; i++) { + bool enabled; + + enabled = !!(idx_value & (1 << i)); + track->textures[i].enabled = enabled; + } + track->tex_dirty = true; + break; + case 0x44C0: + case 0x44C4: + case 0x44C8: + case 0x44CC: + case 0x44D0: + case 0x44D4: + case 0x44D8: + case 0x44DC: + case 0x44E0: + case 0x44E4: + case 0x44E8: + case 0x44EC: + case 0x44F0: + case 0x44F4: + case 0x44F8: + case 0x44FC: + /* TX_FORMAT1_[0-15] */ + i = (reg - 0x44C0) >> 2; + tmp = (idx_value >> 25) & 0x3; + track->textures[i].tex_coord_type = tmp; + switch ((idx_value & 0x1F)) { + case R300_TX_FORMAT_X8: + case R300_TX_FORMAT_Y4X4: + case R300_TX_FORMAT_Z3Y3X2: + track->textures[i].cpp = 1; + track->textures[i].compress_format = R100_TRACK_COMP_NONE; + break; + case R300_TX_FORMAT_X16: + case R300_TX_FORMAT_FL_I16: + case R300_TX_FORMAT_Y8X8: + case R300_TX_FORMAT_Z5Y6X5: + case R300_TX_FORMAT_Z6Y5X5: + case R300_TX_FORMAT_W4Z4Y4X4: + case R300_TX_FORMAT_W1Z5Y5X5: + case R300_TX_FORMAT_D3DMFT_CxV8U8: + case R300_TX_FORMAT_B8G8_B8G8: + case R300_TX_FORMAT_G8R8_G8B8: + track->textures[i].cpp = 2; + track->textures[i].compress_format = R100_TRACK_COMP_NONE; + break; + case R300_TX_FORMAT_Y16X16: + case R300_TX_FORMAT_FL_I16A16: + case R300_TX_FORMAT_Z11Y11X10: + case R300_TX_FORMAT_Z10Y11X11: + case R300_TX_FORMAT_W8Z8Y8X8: + case R300_TX_FORMAT_W2Z10Y10X10: + case 0x17: + case R300_TX_FORMAT_FL_I32: + case 0x1e: + track->textures[i].cpp = 4; + track->textures[i].compress_format = R100_TRACK_COMP_NONE; + break; + case R300_TX_FORMAT_W16Z16Y16X16: + case R300_TX_FORMAT_FL_R16G16B16A16: + case R300_TX_FORMAT_FL_I32A32: + track->textures[i].cpp = 8; + track->textures[i].compress_format = R100_TRACK_COMP_NONE; + break; + case R300_TX_FORMAT_FL_R32G32B32A32: + track->textures[i].cpp = 16; + track->textures[i].compress_format = R100_TRACK_COMP_NONE; + break; + case R300_TX_FORMAT_DXT1: + track->textures[i].cpp = 1; + track->textures[i].compress_format = R100_TRACK_COMP_DXT1; + break; + case R300_TX_FORMAT_ATI2N: + if (p->rdev->family < CHIP_R420) { + DRM_ERROR("Invalid texture format %u\n", + (idx_value & 0x1F)); + return -EINVAL; + } + /* The same rules apply as for DXT3/5. */ + /* Pass through. */ + case R300_TX_FORMAT_DXT3: + case R300_TX_FORMAT_DXT5: + track->textures[i].cpp = 1; + track->textures[i].compress_format = R100_TRACK_COMP_DXT35; + break; + default: + DRM_ERROR("Invalid texture format %u\n", + (idx_value & 0x1F)); + return -EINVAL; + } + track->tex_dirty = true; + break; + case 0x4400: + case 0x4404: + case 0x4408: + case 0x440C: + case 0x4410: + case 0x4414: + case 0x4418: + case 0x441C: + case 0x4420: + case 0x4424: + case 0x4428: + case 0x442C: + case 0x4430: + case 0x4434: + case 0x4438: + case 0x443C: + /* TX_FILTER0_[0-15] */ + i = (reg - 0x4400) >> 2; + tmp = idx_value & 0x7; + if (tmp == 2 || tmp == 4 || tmp == 6) { + track->textures[i].roundup_w = false; + } + tmp = (idx_value >> 3) & 0x7; + if (tmp == 2 || tmp == 4 || tmp == 6) { + track->textures[i].roundup_h = false; + } + track->tex_dirty = true; + break; + case 0x4500: + case 0x4504: + case 0x4508: + case 0x450C: + case 0x4510: + case 0x4514: + case 0x4518: + case 0x451C: + case 0x4520: + case 0x4524: + case 0x4528: + case 0x452C: + case 0x4530: + case 0x4534: + case 0x4538: + case 0x453C: + /* TX_FORMAT2_[0-15] */ + i = (reg - 0x4500) >> 2; + tmp = idx_value & 0x3FFF; + track->textures[i].pitch = tmp + 1; + if (p->rdev->family >= CHIP_RV515) { + tmp = ((idx_value >> 15) & 1) << 11; + track->textures[i].width_11 = tmp; + tmp = ((idx_value >> 16) & 1) << 11; + track->textures[i].height_11 = tmp; + + /* ATI1N */ + if (idx_value & (1 << 14)) { + /* The same rules apply as for DXT1. */ + track->textures[i].compress_format = + R100_TRACK_COMP_DXT1; + } + } else if (idx_value & (1 << 14)) { + DRM_ERROR("Forbidden bit TXFORMAT_MSB\n"); + return -EINVAL; + } + track->tex_dirty = true; + break; + case 0x4480: + case 0x4484: + case 0x4488: + case 0x448C: + case 0x4490: + case 0x4494: + case 0x4498: + case 0x449C: + case 0x44A0: + case 0x44A4: + case 0x44A8: + case 0x44AC: + case 0x44B0: + case 0x44B4: + case 0x44B8: + case 0x44BC: + /* TX_FORMAT0_[0-15] */ + i = (reg - 0x4480) >> 2; + tmp = idx_value & 0x7FF; + track->textures[i].width = tmp + 1; + tmp = (idx_value >> 11) & 0x7FF; + track->textures[i].height = tmp + 1; + tmp = (idx_value >> 26) & 0xF; + track->textures[i].num_levels = tmp; + tmp = idx_value & (1 << 31); + track->textures[i].use_pitch = !!tmp; + tmp = (idx_value >> 22) & 0xF; + track->textures[i].txdepth = tmp; + track->tex_dirty = true; + break; + case R300_ZB_ZPASS_ADDR: + r = r100_cs_packet_next_reloc(p, &reloc); + if (r) { + DRM_ERROR("No reloc for ib[%d]=0x%04X\n", + idx, reg); + r100_cs_dump_packet(p, pkt); + return r; + } + ib[idx] = idx_value + ((u32)reloc->lobj.gpu_offset); + break; + case 0x4e0c: + /* RB3D_COLOR_CHANNEL_MASK */ + track->color_channel_mask = idx_value; + track->cb_dirty = true; + break; + case 0x43a4: + /* SC_HYPERZ_EN */ + /* r300c emits this register - we need to disable hyperz for it + * without complaining */ + if (p->rdev->hyperz_filp != p->filp) { + if (idx_value & 0x1) + ib[idx] = idx_value & ~1; + } + break; + case 0x4f1c: + /* ZB_BW_CNTL */ + track->zb_cb_clear = !!(idx_value & (1 << 5)); + track->cb_dirty = true; + track->zb_dirty = true; + if (p->rdev->hyperz_filp != p->filp) { + if (idx_value & (R300_HIZ_ENABLE | + R300_RD_COMP_ENABLE | + R300_WR_COMP_ENABLE | + R300_FAST_FILL_ENABLE)) + goto fail; + } + break; + case 0x4e04: + /* RB3D_BLENDCNTL */ + track->blend_read_enable = !!(idx_value & (1 << 2)); + track->cb_dirty = true; + break; + case R300_RB3D_AARESOLVE_OFFSET: + r = r100_cs_packet_next_reloc(p, &reloc); + if (r) { + DRM_ERROR("No reloc for ib[%d]=0x%04X\n", + idx, reg); + r100_cs_dump_packet(p, pkt); + return r; + } + track->aa.robj = reloc->robj; + track->aa.offset = idx_value; + track->aa_dirty = true; + ib[idx] = idx_value + ((u32)reloc->lobj.gpu_offset); + break; + case R300_RB3D_AARESOLVE_PITCH: + track->aa.pitch = idx_value & 0x3FFE; + track->aa_dirty = true; + break; + case R300_RB3D_AARESOLVE_CTL: + track->aaresolve = idx_value & 0x1; + track->aa_dirty = true; + break; + case 0x4f30: /* ZB_MASK_OFFSET */ + case 0x4f34: /* ZB_ZMASK_PITCH */ + case 0x4f44: /* ZB_HIZ_OFFSET */ + case 0x4f54: /* ZB_HIZ_PITCH */ + if (idx_value && (p->rdev->hyperz_filp != p->filp)) + goto fail; + break; + case 0x4028: + if (idx_value && (p->rdev->hyperz_filp != p->filp)) + goto fail; + /* GB_Z_PEQ_CONFIG */ + if (p->rdev->family >= CHIP_RV350) + break; + goto fail; + break; + case 0x4be8: + /* valid register only on RV530 */ + if (p->rdev->family == CHIP_RV530) + break; + /* fallthrough do not move */ + default: + goto fail; + } + return 0; +fail: + DRM_ERROR("Forbidden register 0x%04X in cs at %d (val=%08x)\n", + reg, idx, idx_value); + return -EINVAL; +} + +static int r300_packet3_check(struct radeon_cs_parser *p, + struct radeon_cs_packet *pkt) +{ + struct radeon_cs_reloc *reloc; + struct r100_cs_track *track; + volatile uint32_t *ib; + unsigned idx; + int r; + + ib = p->ib.ptr; + idx = pkt->idx + 1; + track = (struct r100_cs_track *)p->track; + switch(pkt->opcode) { + case PACKET3_3D_LOAD_VBPNTR: + r = r100_packet3_load_vbpntr(p, pkt, idx); + if (r) + return r; + break; + case PACKET3_INDX_BUFFER: + r = r100_cs_packet_next_reloc(p, &reloc); + if (r) { + DRM_ERROR("No reloc for packet3 %d\n", pkt->opcode); + r100_cs_dump_packet(p, pkt); + return r; + } + ib[idx+1] = radeon_get_ib_value(p, idx + 1) + ((u32)reloc->lobj.gpu_offset); + r = r100_cs_track_check_pkt3_indx_buffer(p, pkt, reloc->robj); + if (r) { + return r; + } + break; + /* Draw packet */ + case PACKET3_3D_DRAW_IMMD: + /* Number of dwords is vtx_size * (num_vertices - 1) + * PRIM_WALK must be equal to 3 vertex data in embedded + * in cmd stream */ + if (((radeon_get_ib_value(p, idx + 1) >> 4) & 0x3) != 3) { + DRM_ERROR("PRIM_WALK must be 3 for IMMD draw\n"); + return -EINVAL; + } + track->vap_vf_cntl = radeon_get_ib_value(p, idx + 1); + track->immd_dwords = pkt->count - 1; + r = r100_cs_track_check(p->rdev, track); + if (r) { + return r; + } + break; + case PACKET3_3D_DRAW_IMMD_2: + /* Number of dwords is vtx_size * (num_vertices - 1) + * PRIM_WALK must be equal to 3 vertex data in embedded + * in cmd stream */ + if (((radeon_get_ib_value(p, idx) >> 4) & 0x3) != 3) { + DRM_ERROR("PRIM_WALK must be 3 for IMMD draw\n"); + return -EINVAL; + } + track->vap_vf_cntl = radeon_get_ib_value(p, idx); + track->immd_dwords = pkt->count; + r = r100_cs_track_check(p->rdev, track); + if (r) { + return r; + } + break; + case PACKET3_3D_DRAW_VBUF: + track->vap_vf_cntl = radeon_get_ib_value(p, idx + 1); + r = r100_cs_track_check(p->rdev, track); + if (r) { + return r; + } + break; + case PACKET3_3D_DRAW_VBUF_2: + track->vap_vf_cntl = radeon_get_ib_value(p, idx); + r = r100_cs_track_check(p->rdev, track); + if (r) { + return r; + } + break; + case PACKET3_3D_DRAW_INDX: + track->vap_vf_cntl = radeon_get_ib_value(p, idx + 1); + r = r100_cs_track_check(p->rdev, track); + if (r) { + return r; + } + break; + case PACKET3_3D_DRAW_INDX_2: + track->vap_vf_cntl = radeon_get_ib_value(p, idx); + r = r100_cs_track_check(p->rdev, track); + if (r) { + return r; + } + break; + case PACKET3_3D_CLEAR_HIZ: + case PACKET3_3D_CLEAR_ZMASK: + if (p->rdev->hyperz_filp != p->filp) + return -EINVAL; + break; + case PACKET3_3D_CLEAR_CMASK: + if (p->rdev->cmask_filp != p->filp) + return -EINVAL; + break; + case PACKET3_NOP: + break; + default: + DRM_ERROR("Packet3 opcode %x not supported\n", pkt->opcode); + return -EINVAL; + } + return 0; +} + +int r300_cs_parse(struct radeon_cs_parser *p) +{ + struct radeon_cs_packet pkt; + struct r100_cs_track *track; + int r; + + track = malloc(sizeof(*track), DRM_MEM_DRIVER, M_ZERO | M_WAITOK); + if (track == NULL) + return -ENOMEM; + r100_cs_track_clear(p->rdev, track); + p->track = track; + do { + r = r100_cs_packet_parse(p, &pkt, p->idx); + if (r) { + free(p->track, DRM_MEM_DRIVER); + p->track = NULL; + return r; + } + p->idx += pkt.count + 2; + switch (pkt.type) { + case PACKET_TYPE0: + r = r100_cs_parse_packet0(p, &pkt, + p->rdev->config.r300.reg_safe_bm, + p->rdev->config.r300.reg_safe_bm_size, + &r300_packet0_check); + break; + case PACKET_TYPE2: + break; + case PACKET_TYPE3: + r = r300_packet3_check(p, &pkt); + break; + default: + DRM_ERROR("Unknown packet type %d !\n", pkt.type); + free(p->track, DRM_MEM_DRIVER); + p->track = NULL; + return -EINVAL; + } + if (r) { + free(p->track, DRM_MEM_DRIVER); + p->track = NULL; + return r; + } + } while (p->idx < p->chunks[p->chunk_ib_idx].length_dw); + free(p->track, DRM_MEM_DRIVER); + p->track = NULL; + return 0; +} + +void r300_set_reg_safe(struct radeon_device *rdev) +{ + rdev->config.r300.reg_safe_bm = r300_reg_safe_bm; + rdev->config.r300.reg_safe_bm_size = DRM_ARRAY_SIZE(r300_reg_safe_bm); +} + +void r300_mc_program(struct radeon_device *rdev) +{ + struct r100_mc_save save; + int r; + + r = r100_debugfs_mc_info_init(rdev); + if (r) { + dev_err(rdev->dev, "Failed to create r100_mc debugfs file.\n"); + } + + /* Stops all mc clients */ + r100_mc_stop(rdev, &save); + if (rdev->flags & RADEON_IS_AGP) { + WREG32(R_00014C_MC_AGP_LOCATION, + S_00014C_MC_AGP_START(rdev->mc.gtt_start >> 16) | + S_00014C_MC_AGP_TOP(rdev->mc.gtt_end >> 16)); + WREG32(R_000170_AGP_BASE, lower_32_bits(rdev->mc.agp_base)); + WREG32(R_00015C_AGP_BASE_2, + upper_32_bits(rdev->mc.agp_base) & 0xff); + } else { + WREG32(R_00014C_MC_AGP_LOCATION, 0x0FFFFFFF); + WREG32(R_000170_AGP_BASE, 0); + WREG32(R_00015C_AGP_BASE_2, 0); + } + /* Wait for mc idle */ + if (r300_mc_wait_for_idle(rdev)) + DRM_INFO("Failed to wait MC idle before programming MC.\n"); + /* Program MC, should be a 32bits limited address space */ + WREG32(R_000148_MC_FB_LOCATION, + S_000148_MC_FB_START(rdev->mc.vram_start >> 16) | + S_000148_MC_FB_TOP(rdev->mc.vram_end >> 16)); + r100_mc_resume(rdev, &save); +} + +void r300_clock_startup(struct radeon_device *rdev) +{ + u32 tmp; + + if (radeon_dynclks != -1 && radeon_dynclks) + radeon_legacy_set_clock_gating(rdev, 1); + /* We need to force on some of the block */ + tmp = RREG32_PLL(R_00000D_SCLK_CNTL); + tmp |= S_00000D_FORCE_CP(1) | S_00000D_FORCE_VIP(1); + if ((rdev->family == CHIP_RV350) || (rdev->family == CHIP_RV380)) + tmp |= S_00000D_FORCE_VAP(1); + WREG32_PLL(R_00000D_SCLK_CNTL, tmp); +} + +static int r300_startup(struct radeon_device *rdev) +{ + int r; + + /* set common regs */ + r100_set_common_regs(rdev); + /* program mc */ + r300_mc_program(rdev); + /* Resume clock */ + r300_clock_startup(rdev); + /* Initialize GPU configuration (# pipes, ...) */ + r300_gpu_init(rdev); + /* Initialize GART (initialize after TTM so we can allocate + * memory through TTM but finalize after TTM) */ + if (rdev->flags & RADEON_IS_PCIE) { + r = rv370_pcie_gart_enable(rdev); + if (r) + return r; + } + + if (rdev->family == CHIP_R300 || + rdev->family == CHIP_R350 || + rdev->family == CHIP_RV350) + r100_enable_bm(rdev); + + if (rdev->flags & RADEON_IS_PCI) { + r = r100_pci_gart_enable(rdev); + if (r) + return r; + } + + /* allocate wb buffer */ + r = radeon_wb_init(rdev); + if (r) + return r; + + r = radeon_fence_driver_start_ring(rdev, RADEON_RING_TYPE_GFX_INDEX); + if (r) { + dev_err(rdev->dev, "failed initializing CP fences (%d).\n", r); + return r; + } + + /* Enable IRQ */ + r100_irq_set(rdev); + rdev->config.r300.hdp_cntl = RREG32(RADEON_HOST_PATH_CNTL); + /* 1M ring buffer */ + r = r100_cp_init(rdev, 1024 * 1024); + if (r) { + dev_err(rdev->dev, "failed initializing CP (%d).\n", r); + return r; + } + + r = radeon_ib_pool_init(rdev); + if (r) { + dev_err(rdev->dev, "IB initialization failed (%d).\n", r); + return r; + } + + return 0; +} + +int r300_resume(struct radeon_device *rdev) +{ + int r; + + /* Make sur GART are not working */ + if (rdev->flags & RADEON_IS_PCIE) + rv370_pcie_gart_disable(rdev); + if (rdev->flags & RADEON_IS_PCI) + r100_pci_gart_disable(rdev); + /* Resume clock before doing reset */ + r300_clock_startup(rdev); + /* Reset gpu before posting otherwise ATOM will enter infinite loop */ + if (radeon_asic_reset(rdev)) { + dev_warn(rdev->dev, "GPU reset failed ! (0xE40=0x%08X, 0x7C0=0x%08X)\n", + RREG32(R_000E40_RBBM_STATUS), + RREG32(R_0007C0_CP_STAT)); + } + /* post */ + radeon_combios_asic_init(rdev->ddev); + /* Resume clock after posting */ + r300_clock_startup(rdev); + /* Initialize surface registers */ + radeon_surface_init(rdev); + + rdev->accel_working = true; + r = r300_startup(rdev); + if (r) { + rdev->accel_working = false; + } + return r; +} + +int r300_suspend(struct radeon_device *rdev) +{ + r100_cp_disable(rdev); + radeon_wb_disable(rdev); + r100_irq_disable(rdev); + if (rdev->flags & RADEON_IS_PCIE) + rv370_pcie_gart_disable(rdev); + if (rdev->flags & RADEON_IS_PCI) + r100_pci_gart_disable(rdev); + return 0; +} + +void r300_fini(struct radeon_device *rdev) +{ + r100_cp_fini(rdev); + radeon_wb_fini(rdev); + radeon_ib_pool_fini(rdev); + radeon_gem_fini(rdev); + if (rdev->flags & RADEON_IS_PCIE) + rv370_pcie_gart_fini(rdev); + if (rdev->flags & RADEON_IS_PCI) + r100_pci_gart_fini(rdev); + radeon_agp_fini(rdev); + radeon_irq_kms_fini(rdev); + radeon_fence_driver_fini(rdev); + radeon_bo_fini(rdev); + radeon_atombios_fini(rdev); + free(rdev->bios, DRM_MEM_DRIVER); + rdev->bios = NULL; +} + +int r300_init(struct radeon_device *rdev) +{ + int r; + + /* Disable VGA */ + r100_vga_render_disable(rdev); + /* Initialize scratch registers */ + radeon_scratch_init(rdev); + /* Initialize surface registers */ + radeon_surface_init(rdev); + /* TODO: disable VGA need to use VGA request */ + /* restore some register to sane defaults */ + r100_restore_sanity(rdev); + /* BIOS*/ + if (!radeon_get_bios(rdev)) { + if (ASIC_IS_AVIVO(rdev)) + return -EINVAL; + } + if (rdev->is_atom_bios) { + dev_err(rdev->dev, "Expecting combios for RS400/RS480 GPU\n"); + return -EINVAL; + } else { + r = radeon_combios_init(rdev); + if (r) + return r; + } + /* Reset gpu before posting otherwise ATOM will enter infinite loop */ + if (radeon_asic_reset(rdev)) { + dev_warn(rdev->dev, + "GPU reset failed ! (0xE40=0x%08X, 0x7C0=0x%08X)\n", + RREG32(R_000E40_RBBM_STATUS), + RREG32(R_0007C0_CP_STAT)); + } + /* check if cards are posted or not */ + if (radeon_boot_test_post_card(rdev) == false) + return -EINVAL; + /* Set asic errata */ + r300_errata(rdev); + /* Initialize clocks */ + radeon_get_clock_info(rdev->ddev); + /* initialize AGP */ + if (rdev->flags & RADEON_IS_AGP) { + r = radeon_agp_init(rdev); + if (r) { + radeon_agp_disable(rdev); + } + } + /* initialize memory controller */ + r300_mc_init(rdev); + /* Fence driver */ + r = radeon_fence_driver_init(rdev); + if (r) + return r; + r = radeon_irq_kms_init(rdev); + if (r) + return r; + /* Memory manager */ + r = radeon_bo_init(rdev); + if (r) + return r; + if (rdev->flags & RADEON_IS_PCIE) { + r = rv370_pcie_gart_init(rdev); + if (r) + return r; + } + if (rdev->flags & RADEON_IS_PCI) { + r = r100_pci_gart_init(rdev); + if (r) + return r; + } + r300_set_reg_safe(rdev); + + rdev->accel_working = true; + r = r300_startup(rdev); + if (r) { + /* Somethings want wront with the accel init stop accel */ + dev_err(rdev->dev, "Disabling GPU acceleration\n"); + r100_cp_fini(rdev); + radeon_wb_fini(rdev); + radeon_ib_pool_fini(rdev); + radeon_irq_kms_fini(rdev); + if (rdev->flags & RADEON_IS_PCIE) + rv370_pcie_gart_fini(rdev); + if (rdev->flags & RADEON_IS_PCI) + r100_pci_gart_fini(rdev); + radeon_agp_fini(rdev); + rdev->accel_working = false; + } + return 0; +} diff --git a/sys/dev/drm2/radeon/r300_cmdbuf.c b/sys/dev/drm2/radeon/r300_cmdbuf.c new file mode 100644 index 00000000000..4f41b13a27b --- /dev/null +++ b/sys/dev/drm2/radeon/r300_cmdbuf.c @@ -0,0 +1,1185 @@ +/* r300_cmdbuf.c -- Command buffer emission for R300 -*- linux-c -*- + * + * Copyright (C) The Weather Channel, Inc. 2002. + * Copyright (C) 2004 Nicolai Haehnle. + * All Rights Reserved. + * + * The Weather Channel (TM) funded Tungsten Graphics to develop the + * initial release of the Radeon 8500 driver under the XFree86 license. + * This notice must be preserved. + * + * 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 (including the next + * paragraph) 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 + * PRECISION INSIGHT AND/OR ITS SUPPLIERS 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. + * + * Authors: + * Nicolai Haehnle + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include "radeon_drv.h" +#include "r300_reg.h" + +#define R300_SIMULTANEOUS_CLIPRECTS 4 + +/* Values for R300_RE_CLIPRECT_CNTL depending on the number of cliprects + */ +static const int r300_cliprect_cntl[4] = { + 0xAAAA, + 0xEEEE, + 0xFEFE, + 0xFFFE +}; + +/** + * Emit up to R300_SIMULTANEOUS_CLIPRECTS cliprects from the given command + * buffer, starting with index n. + */ +static int r300_emit_cliprects(drm_radeon_private_t *dev_priv, + drm_radeon_kcmd_buffer_t *cmdbuf, int n) +{ + struct drm_clip_rect box; + int nr; + int i; + RING_LOCALS; + + nr = cmdbuf->nbox - n; + if (nr > R300_SIMULTANEOUS_CLIPRECTS) + nr = R300_SIMULTANEOUS_CLIPRECTS; + + DRM_DEBUG("%i cliprects\n", nr); + + if (nr) { + BEGIN_RING(6 + nr * 2); + OUT_RING(CP_PACKET0(R300_RE_CLIPRECT_TL_0, nr * 2 - 1)); + + for (i = 0; i < nr; ++i) { + if (DRM_COPY_FROM_USER_UNCHECKED + (&box, &cmdbuf->boxes[n + i], sizeof(box))) { + DRM_ERROR("copy cliprect faulted\n"); + return -EFAULT; + } + + box.x2--; /* Hardware expects inclusive bottom-right corner */ + box.y2--; + + if ((dev_priv->flags & RADEON_FAMILY_MASK) >= CHIP_RV515) { + box.x1 = (box.x1) & + R300_CLIPRECT_MASK; + box.y1 = (box.y1) & + R300_CLIPRECT_MASK; + box.x2 = (box.x2) & + R300_CLIPRECT_MASK; + box.y2 = (box.y2) & + R300_CLIPRECT_MASK; + } else { + box.x1 = (box.x1 + R300_CLIPRECT_OFFSET) & + R300_CLIPRECT_MASK; + box.y1 = (box.y1 + R300_CLIPRECT_OFFSET) & + R300_CLIPRECT_MASK; + box.x2 = (box.x2 + R300_CLIPRECT_OFFSET) & + R300_CLIPRECT_MASK; + box.y2 = (box.y2 + R300_CLIPRECT_OFFSET) & + R300_CLIPRECT_MASK; + } + + OUT_RING((box.x1 << R300_CLIPRECT_X_SHIFT) | + (box.y1 << R300_CLIPRECT_Y_SHIFT)); + OUT_RING((box.x2 << R300_CLIPRECT_X_SHIFT) | + (box.y2 << R300_CLIPRECT_Y_SHIFT)); + + } + + OUT_RING_REG(R300_RE_CLIPRECT_CNTL, r300_cliprect_cntl[nr - 1]); + + /* TODO/SECURITY: Force scissors to a safe value, otherwise the + * client might be able to trample over memory. + * The impact should be very limited, but I'd rather be safe than + * sorry. + */ + OUT_RING(CP_PACKET0(R300_RE_SCISSORS_TL, 1)); + OUT_RING(0); + OUT_RING(R300_SCISSORS_X_MASK | R300_SCISSORS_Y_MASK); + ADVANCE_RING(); + } else { + /* Why we allow zero cliprect rendering: + * There are some commands in a command buffer that must be submitted + * even when there are no cliprects, e.g. DMA buffer discard + * or state setting (though state setting could be avoided by + * simulating a loss of context). + * + * Now since the cmdbuf interface is so chaotic right now (and is + * bound to remain that way for a bit until things settle down), + * it is basically impossible to filter out the commands that are + * necessary and those that aren't. + * + * So I choose the safe way and don't do any filtering at all; + * instead, I simply set up the engine so that all rendering + * can't produce any fragments. + */ + BEGIN_RING(2); + OUT_RING_REG(R300_RE_CLIPRECT_CNTL, 0); + ADVANCE_RING(); + } + + /* flus cache and wait idle clean after cliprect change */ + BEGIN_RING(2); + OUT_RING(CP_PACKET0(R300_RB3D_DSTCACHE_CTLSTAT, 0)); + OUT_RING(R300_RB3D_DC_FLUSH); + ADVANCE_RING(); + BEGIN_RING(2); + OUT_RING(CP_PACKET0(RADEON_WAIT_UNTIL, 0)); + OUT_RING(RADEON_WAIT_3D_IDLECLEAN); + ADVANCE_RING(); + /* set flush flag */ + dev_priv->track_flush |= RADEON_FLUSH_EMITED; + + return 0; +} + +static u8 r300_reg_flags[0x10000 >> 2]; + +void r300_init_reg_flags(struct drm_device *dev) +{ + int i; + drm_radeon_private_t *dev_priv = dev->dev_private; + + memset(r300_reg_flags, 0, 0x10000 >> 2); +#define ADD_RANGE_MARK(reg, count,mark) \ + for(i=((reg)>>2);i<((reg)>>2)+(count);i++)\ + r300_reg_flags[i]|=(mark); + +#define MARK_SAFE 1 +#define MARK_CHECK_OFFSET 2 + +#define ADD_RANGE(reg, count) ADD_RANGE_MARK(reg, count, MARK_SAFE) + + /* these match cmducs() command in r300_driver/r300/r300_cmdbuf.c */ + ADD_RANGE(R300_SE_VPORT_XSCALE, 6); + ADD_RANGE(R300_VAP_CNTL, 1); + ADD_RANGE(R300_SE_VTE_CNTL, 2); + ADD_RANGE(0x2134, 2); + ADD_RANGE(R300_VAP_CNTL_STATUS, 1); + ADD_RANGE(R300_VAP_INPUT_CNTL_0, 2); + ADD_RANGE(0x21DC, 1); + ADD_RANGE(R300_VAP_UNKNOWN_221C, 1); + ADD_RANGE(R300_VAP_CLIP_X_0, 4); + ADD_RANGE(R300_VAP_PVS_STATE_FLUSH_REG, 1); + ADD_RANGE(R300_VAP_UNKNOWN_2288, 1); + ADD_RANGE(R300_VAP_OUTPUT_VTX_FMT_0, 2); + ADD_RANGE(R300_VAP_PVS_CNTL_1, 3); + ADD_RANGE(R300_GB_ENABLE, 1); + ADD_RANGE(R300_GB_MSPOS0, 5); + ADD_RANGE(R300_TX_INVALTAGS, 1); + ADD_RANGE(R300_TX_ENABLE, 1); + ADD_RANGE(0x4200, 4); + ADD_RANGE(0x4214, 1); + ADD_RANGE(R300_RE_POINTSIZE, 1); + ADD_RANGE(0x4230, 3); + ADD_RANGE(R300_RE_LINE_CNT, 1); + ADD_RANGE(R300_RE_UNK4238, 1); + ADD_RANGE(0x4260, 3); + ADD_RANGE(R300_RE_SHADE, 4); + ADD_RANGE(R300_RE_POLYGON_MODE, 5); + ADD_RANGE(R300_RE_ZBIAS_CNTL, 1); + ADD_RANGE(R300_RE_ZBIAS_T_FACTOR, 4); + ADD_RANGE(R300_RE_OCCLUSION_CNTL, 1); + ADD_RANGE(R300_RE_CULL_CNTL, 1); + ADD_RANGE(0x42C0, 2); + ADD_RANGE(R300_RS_CNTL_0, 2); + + ADD_RANGE(R300_SU_REG_DEST, 1); + if ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RV530) + ADD_RANGE(RV530_FG_ZBREG_DEST, 1); + + ADD_RANGE(R300_SC_HYPERZ, 2); + ADD_RANGE(0x43E8, 1); + + ADD_RANGE(0x46A4, 5); + + ADD_RANGE(R300_RE_FOG_STATE, 1); + ADD_RANGE(R300_FOG_COLOR_R, 3); + ADD_RANGE(R300_PP_ALPHA_TEST, 2); + ADD_RANGE(0x4BD8, 1); + ADD_RANGE(R300_PFS_PARAM_0_X, 64); + ADD_RANGE(0x4E00, 1); + ADD_RANGE(R300_RB3D_CBLEND, 2); + ADD_RANGE(R300_RB3D_COLORMASK, 1); + ADD_RANGE(R300_RB3D_BLEND_COLOR, 3); + ADD_RANGE_MARK(R300_RB3D_COLOROFFSET0, 1, MARK_CHECK_OFFSET); /* check offset */ + ADD_RANGE(R300_RB3D_COLORPITCH0, 1); + ADD_RANGE(0x4E50, 9); + ADD_RANGE(0x4E88, 1); + ADD_RANGE(0x4EA0, 2); + ADD_RANGE(R300_ZB_CNTL, 3); + ADD_RANGE(R300_ZB_FORMAT, 4); + ADD_RANGE_MARK(R300_ZB_DEPTHOFFSET, 1, MARK_CHECK_OFFSET); /* check offset */ + ADD_RANGE(R300_ZB_DEPTHPITCH, 1); + ADD_RANGE(R300_ZB_DEPTHCLEARVALUE, 1); + ADD_RANGE(R300_ZB_ZMASK_OFFSET, 13); + ADD_RANGE(R300_ZB_ZPASS_DATA, 2); /* ZB_ZPASS_DATA, ZB_ZPASS_ADDR */ + + ADD_RANGE(R300_TX_FILTER_0, 16); + ADD_RANGE(R300_TX_FILTER1_0, 16); + ADD_RANGE(R300_TX_SIZE_0, 16); + ADD_RANGE(R300_TX_FORMAT_0, 16); + ADD_RANGE(R300_TX_PITCH_0, 16); + /* Texture offset is dangerous and needs more checking */ + ADD_RANGE_MARK(R300_TX_OFFSET_0, 16, MARK_CHECK_OFFSET); + ADD_RANGE(R300_TX_CHROMA_KEY_0, 16); + ADD_RANGE(R300_TX_BORDER_COLOR_0, 16); + + /* Sporadic registers used as primitives are emitted */ + ADD_RANGE(R300_ZB_ZCACHE_CTLSTAT, 1); + ADD_RANGE(R300_RB3D_DSTCACHE_CTLSTAT, 1); + ADD_RANGE(R300_VAP_INPUT_ROUTE_0_0, 8); + ADD_RANGE(R300_VAP_INPUT_ROUTE_1_0, 8); + + if ((dev_priv->flags & RADEON_FAMILY_MASK) >= CHIP_RV515) { + ADD_RANGE(R500_VAP_INDEX_OFFSET, 1); + ADD_RANGE(R500_US_CONFIG, 2); + ADD_RANGE(R500_US_CODE_ADDR, 3); + ADD_RANGE(R500_US_FC_CTRL, 1); + ADD_RANGE(R500_RS_IP_0, 16); + ADD_RANGE(R500_RS_INST_0, 16); + ADD_RANGE(R500_RB3D_COLOR_CLEAR_VALUE_AR, 2); + ADD_RANGE(R500_RB3D_CONSTANT_COLOR_AR, 2); + ADD_RANGE(R500_ZB_FIFO_SIZE, 2); + } else { + ADD_RANGE(R300_PFS_CNTL_0, 3); + ADD_RANGE(R300_PFS_NODE_0, 4); + ADD_RANGE(R300_PFS_TEXI_0, 64); + ADD_RANGE(R300_PFS_INSTR0_0, 64); + ADD_RANGE(R300_PFS_INSTR1_0, 64); + ADD_RANGE(R300_PFS_INSTR2_0, 64); + ADD_RANGE(R300_PFS_INSTR3_0, 64); + ADD_RANGE(R300_RS_INTERP_0, 8); + ADD_RANGE(R300_RS_ROUTE_0, 8); + + } +} + +static __inline__ int r300_check_range(unsigned reg, int count) +{ + int i; + if (reg & ~0xffff) + return -1; + for (i = (reg >> 2); i < (reg >> 2) + count; i++) + if (r300_reg_flags[i] != MARK_SAFE) + return 1; + return 0; +} + +static __inline__ int r300_emit_carefully_checked_packet0(drm_radeon_private_t * + dev_priv, + drm_radeon_kcmd_buffer_t + * cmdbuf, + drm_r300_cmd_header_t + header) +{ + int reg; + int sz; + int i; + u32 *value; + RING_LOCALS; + + sz = header.packet0.count; + reg = (header.packet0.reghi << 8) | header.packet0.reglo; + + if ((sz > 64) || (sz < 0)) { + DRM_ERROR("Cannot emit more than 64 values at a time (reg=%04x sz=%d)\n", + reg, sz); + return -EINVAL; + } + + for (i = 0; i < sz; i++) { + switch (r300_reg_flags[(reg >> 2) + i]) { + case MARK_SAFE: + break; + case MARK_CHECK_OFFSET: + value = drm_buffer_pointer_to_dword(cmdbuf->buffer, i); + if (!radeon_check_offset(dev_priv, *value)) { + DRM_ERROR("Offset failed range check (reg=%04x sz=%d)\n", + reg, sz); + return -EINVAL; + } + break; + default: + DRM_ERROR("Register %04x failed check as flag=%02x\n", + reg + i * 4, r300_reg_flags[(reg >> 2) + i]); + return -EINVAL; + } + } + + BEGIN_RING(1 + sz); + OUT_RING(CP_PACKET0(reg, sz - 1)); + OUT_RING_DRM_BUFFER(cmdbuf->buffer, sz); + ADVANCE_RING(); + + return 0; +} + +/** + * Emits a packet0 setting arbitrary registers. + * Called by r300_do_cp_cmdbuf. + * + * Note that checks are performed on contents and addresses of the registers + */ +static __inline__ int r300_emit_packet0(drm_radeon_private_t *dev_priv, + drm_radeon_kcmd_buffer_t *cmdbuf, + drm_r300_cmd_header_t header) +{ + int reg; + int sz; + RING_LOCALS; + + sz = header.packet0.count; + reg = (header.packet0.reghi << 8) | header.packet0.reglo; + + if (!sz) + return 0; + + if (sz * 4 > drm_buffer_unprocessed(cmdbuf->buffer)) + return -EINVAL; + + if (reg + sz * 4 >= 0x10000) { + DRM_ERROR("No such registers in hardware reg=%04x sz=%d\n", reg, + sz); + return -EINVAL; + } + + if (r300_check_range(reg, sz)) { + /* go and check everything */ + return r300_emit_carefully_checked_packet0(dev_priv, cmdbuf, + header); + } + /* the rest of the data is safe to emit, whatever the values the user passed */ + + BEGIN_RING(1 + sz); + OUT_RING(CP_PACKET0(reg, sz - 1)); + OUT_RING_DRM_BUFFER(cmdbuf->buffer, sz); + ADVANCE_RING(); + + return 0; +} + +/** + * Uploads user-supplied vertex program instructions or parameters onto + * the graphics card. + * Called by r300_do_cp_cmdbuf. + */ +static __inline__ int r300_emit_vpu(drm_radeon_private_t *dev_priv, + drm_radeon_kcmd_buffer_t *cmdbuf, + drm_r300_cmd_header_t header) +{ + int sz; + int addr; + RING_LOCALS; + + sz = header.vpu.count; + addr = (header.vpu.adrhi << 8) | header.vpu.adrlo; + + if (!sz) + return 0; + if (sz * 16 > drm_buffer_unprocessed(cmdbuf->buffer)) + return -EINVAL; + + /* VAP is very sensitive so we purge cache before we program it + * and we also flush its state before & after */ + BEGIN_RING(6); + OUT_RING(CP_PACKET0(R300_RB3D_DSTCACHE_CTLSTAT, 0)); + OUT_RING(R300_RB3D_DC_FLUSH); + OUT_RING(CP_PACKET0(RADEON_WAIT_UNTIL, 0)); + OUT_RING(RADEON_WAIT_3D_IDLECLEAN); + OUT_RING(CP_PACKET0(R300_VAP_PVS_STATE_FLUSH_REG, 0)); + OUT_RING(0); + ADVANCE_RING(); + /* set flush flag */ + dev_priv->track_flush |= RADEON_FLUSH_EMITED; + + BEGIN_RING(3 + sz * 4); + OUT_RING_REG(R300_VAP_PVS_UPLOAD_ADDRESS, addr); + OUT_RING(CP_PACKET0_TABLE(R300_VAP_PVS_UPLOAD_DATA, sz * 4 - 1)); + OUT_RING_DRM_BUFFER(cmdbuf->buffer, sz * 4); + ADVANCE_RING(); + + BEGIN_RING(2); + OUT_RING(CP_PACKET0(R300_VAP_PVS_STATE_FLUSH_REG, 0)); + OUT_RING(0); + ADVANCE_RING(); + + return 0; +} + +/** + * Emit a clear packet from userspace. + * Called by r300_emit_packet3. + */ +static __inline__ int r300_emit_clear(drm_radeon_private_t *dev_priv, + drm_radeon_kcmd_buffer_t *cmdbuf) +{ + RING_LOCALS; + + if (8 * 4 > drm_buffer_unprocessed(cmdbuf->buffer)) + return -EINVAL; + + BEGIN_RING(10); + OUT_RING(CP_PACKET3(R200_3D_DRAW_IMMD_2, 8)); + OUT_RING(R300_PRIM_TYPE_POINT | R300_PRIM_WALK_RING | + (1 << R300_PRIM_NUM_VERTICES_SHIFT)); + OUT_RING_DRM_BUFFER(cmdbuf->buffer, 8); + ADVANCE_RING(); + + BEGIN_RING(4); + OUT_RING(CP_PACKET0(R300_RB3D_DSTCACHE_CTLSTAT, 0)); + OUT_RING(R300_RB3D_DC_FLUSH); + OUT_RING(CP_PACKET0(RADEON_WAIT_UNTIL, 0)); + OUT_RING(RADEON_WAIT_3D_IDLECLEAN); + ADVANCE_RING(); + /* set flush flag */ + dev_priv->track_flush |= RADEON_FLUSH_EMITED; + + return 0; +} + +static __inline__ int r300_emit_3d_load_vbpntr(drm_radeon_private_t *dev_priv, + drm_radeon_kcmd_buffer_t *cmdbuf, + u32 header) +{ + int count, i, k; +#define MAX_ARRAY_PACKET 64 + u32 *data; + u32 narrays; + RING_LOCALS; + + count = (header & RADEON_CP_PACKET_COUNT_MASK) >> 16; + + if ((count + 1) > MAX_ARRAY_PACKET) { + DRM_ERROR("Too large payload in 3D_LOAD_VBPNTR (count=%d)\n", + count); + return -EINVAL; + } + /* carefully check packet contents */ + + /* We have already read the header so advance the buffer. */ + drm_buffer_advance(cmdbuf->buffer, 4); + + narrays = *(u32 *)drm_buffer_pointer_to_dword(cmdbuf->buffer, 0); + k = 0; + i = 1; + while ((k < narrays) && (i < (count + 1))) { + i++; /* skip attribute field */ + data = drm_buffer_pointer_to_dword(cmdbuf->buffer, i); + if (!radeon_check_offset(dev_priv, *data)) { + DRM_ERROR + ("Offset failed range check (k=%d i=%d) while processing 3D_LOAD_VBPNTR packet.\n", + k, i); + return -EINVAL; + } + k++; + i++; + if (k == narrays) + break; + /* have one more to process, they come in pairs */ + data = drm_buffer_pointer_to_dword(cmdbuf->buffer, i); + if (!radeon_check_offset(dev_priv, *data)) { + DRM_ERROR + ("Offset failed range check (k=%d i=%d) while processing 3D_LOAD_VBPNTR packet.\n", + k, i); + return -EINVAL; + } + k++; + i++; + } + /* do the counts match what we expect ? */ + if ((k != narrays) || (i != (count + 1))) { + DRM_ERROR + ("Malformed 3D_LOAD_VBPNTR packet (k=%d i=%d narrays=%d count+1=%d).\n", + k, i, narrays, count + 1); + return -EINVAL; + } + + /* all clear, output packet */ + + BEGIN_RING(count + 2); + OUT_RING(header); + OUT_RING_DRM_BUFFER(cmdbuf->buffer, count + 1); + ADVANCE_RING(); + + return 0; +} + +static __inline__ int r300_emit_bitblt_multi(drm_radeon_private_t *dev_priv, + drm_radeon_kcmd_buffer_t *cmdbuf) +{ + u32 *cmd = drm_buffer_pointer_to_dword(cmdbuf->buffer, 0); + int count, ret; + RING_LOCALS; + + + count = (*cmd & RADEON_CP_PACKET_COUNT_MASK) >> 16; + + if (*cmd & 0x8000) { + u32 offset; + u32 *cmd1 = drm_buffer_pointer_to_dword(cmdbuf->buffer, 1); + if (*cmd1 & (RADEON_GMC_SRC_PITCH_OFFSET_CNTL + | RADEON_GMC_DST_PITCH_OFFSET_CNTL)) { + + u32 *cmd2 = drm_buffer_pointer_to_dword(cmdbuf->buffer, 2); + offset = *cmd2 << 10; + ret = !radeon_check_offset(dev_priv, offset); + if (ret) { + DRM_ERROR("Invalid bitblt first offset is %08X\n", offset); + return -EINVAL; + } + } + + if ((*cmd1 & RADEON_GMC_SRC_PITCH_OFFSET_CNTL) && + (*cmd1 & RADEON_GMC_DST_PITCH_OFFSET_CNTL)) { + u32 *cmd3 = drm_buffer_pointer_to_dword(cmdbuf->buffer, 3); + offset = *cmd3 << 10; + ret = !radeon_check_offset(dev_priv, offset); + if (ret) { + DRM_ERROR("Invalid bitblt second offset is %08X\n", offset); + return -EINVAL; + } + + } + } + + BEGIN_RING(count+2); + OUT_RING_DRM_BUFFER(cmdbuf->buffer, count + 2); + ADVANCE_RING(); + + return 0; +} + +static __inline__ int r300_emit_draw_indx_2(drm_radeon_private_t *dev_priv, + drm_radeon_kcmd_buffer_t *cmdbuf) +{ + u32 *cmd = drm_buffer_pointer_to_dword(cmdbuf->buffer, 0); + u32 *cmd1 = drm_buffer_pointer_to_dword(cmdbuf->buffer, 1); + int count; + int expected_count; + RING_LOCALS; + + count = (*cmd & RADEON_CP_PACKET_COUNT_MASK) >> 16; + + expected_count = *cmd1 >> 16; + if (!(*cmd1 & R300_VAP_VF_CNTL__INDEX_SIZE_32bit)) + expected_count = (expected_count+1)/2; + + if (count && count != expected_count) { + DRM_ERROR("3D_DRAW_INDX_2: packet size %i, expected %i\n", + count, expected_count); + return -EINVAL; + } + + BEGIN_RING(count+2); + OUT_RING_DRM_BUFFER(cmdbuf->buffer, count + 2); + ADVANCE_RING(); + + if (!count) { + drm_r300_cmd_header_t stack_header, *header; + u32 *cmd1, *cmd2, *cmd3; + + if (drm_buffer_unprocessed(cmdbuf->buffer) + < 4*4 + sizeof(stack_header)) { + DRM_ERROR("3D_DRAW_INDX_2: expect subsequent INDX_BUFFER, but stream is too short.\n"); + return -EINVAL; + } + + header = drm_buffer_read_object(cmdbuf->buffer, + sizeof(stack_header), &stack_header); + + cmd = drm_buffer_pointer_to_dword(cmdbuf->buffer, 0); + cmd1 = drm_buffer_pointer_to_dword(cmdbuf->buffer, 1); + cmd2 = drm_buffer_pointer_to_dword(cmdbuf->buffer, 2); + cmd3 = drm_buffer_pointer_to_dword(cmdbuf->buffer, 3); + + if (header->header.cmd_type != R300_CMD_PACKET3 || + header->packet3.packet != R300_CMD_PACKET3_RAW || + *cmd != CP_PACKET3(RADEON_CP_INDX_BUFFER, 2)) { + DRM_ERROR("3D_DRAW_INDX_2: expect subsequent INDX_BUFFER.\n"); + return -EINVAL; + } + + if ((*cmd1 & 0x8000ffff) != 0x80000810) { + DRM_ERROR("Invalid indx_buffer reg address %08X\n", + *cmd1); + return -EINVAL; + } + if (!radeon_check_offset(dev_priv, *cmd2)) { + DRM_ERROR("Invalid indx_buffer offset is %08X\n", + *cmd2); + return -EINVAL; + } + if (*cmd3 != expected_count) { + DRM_ERROR("INDX_BUFFER: buffer size %i, expected %i\n", + *cmd3, expected_count); + return -EINVAL; + } + + BEGIN_RING(4); + OUT_RING_DRM_BUFFER(cmdbuf->buffer, 4); + ADVANCE_RING(); + } + + return 0; +} + +static __inline__ int r300_emit_raw_packet3(drm_radeon_private_t *dev_priv, + drm_radeon_kcmd_buffer_t *cmdbuf) +{ + u32 *header; + int count; + RING_LOCALS; + + if (4 > drm_buffer_unprocessed(cmdbuf->buffer)) + return -EINVAL; + + /* Fixme !! This simply emits a packet without much checking. + We need to be smarter. */ + + /* obtain first word - actual packet3 header */ + header = drm_buffer_pointer_to_dword(cmdbuf->buffer, 0); + + /* Is it packet 3 ? */ + if ((*header >> 30) != 0x3) { + DRM_ERROR("Not a packet3 header (0x%08x)\n", *header); + return -EINVAL; + } + + count = (*header >> 16) & 0x3fff; + + /* Check again now that we know how much data to expect */ + if ((count + 2) * 4 > drm_buffer_unprocessed(cmdbuf->buffer)) { + DRM_ERROR + ("Expected packet3 of length %d but have only %d bytes left\n", + (count + 2) * 4, drm_buffer_unprocessed(cmdbuf->buffer)); + return -EINVAL; + } + + /* Is it a packet type we know about ? */ + switch (*header & 0xff00) { + case RADEON_3D_LOAD_VBPNTR: /* load vertex array pointers */ + return r300_emit_3d_load_vbpntr(dev_priv, cmdbuf, *header); + + case RADEON_CNTL_BITBLT_MULTI: + return r300_emit_bitblt_multi(dev_priv, cmdbuf); + + case RADEON_CP_INDX_BUFFER: + DRM_ERROR("packet3 INDX_BUFFER without preceding 3D_DRAW_INDX_2 is illegal.\n"); + return -EINVAL; + case RADEON_CP_3D_DRAW_IMMD_2: + /* triggers drawing using in-packet vertex data */ + case RADEON_CP_3D_DRAW_VBUF_2: + /* triggers drawing of vertex buffers setup elsewhere */ + dev_priv->track_flush &= ~(RADEON_FLUSH_EMITED | + RADEON_PURGE_EMITED); + break; + case RADEON_CP_3D_DRAW_INDX_2: + /* triggers drawing using indices to vertex buffer */ + /* whenever we send vertex we clear flush & purge */ + dev_priv->track_flush &= ~(RADEON_FLUSH_EMITED | + RADEON_PURGE_EMITED); + return r300_emit_draw_indx_2(dev_priv, cmdbuf); + case RADEON_WAIT_FOR_IDLE: + case RADEON_CP_NOP: + /* these packets are safe */ + break; + default: + DRM_ERROR("Unknown packet3 header (0x%08x)\n", *header); + return -EINVAL; + } + + BEGIN_RING(count + 2); + OUT_RING_DRM_BUFFER(cmdbuf->buffer, count + 2); + ADVANCE_RING(); + + return 0; +} + +/** + * Emit a rendering packet3 from userspace. + * Called by r300_do_cp_cmdbuf. + */ +static __inline__ int r300_emit_packet3(drm_radeon_private_t *dev_priv, + drm_radeon_kcmd_buffer_t *cmdbuf, + drm_r300_cmd_header_t header) +{ + int n; + int ret; + int orig_iter = cmdbuf->buffer->iterator; + + /* This is a do-while-loop so that we run the interior at least once, + * even if cmdbuf->nbox is 0. Compare r300_emit_cliprects for rationale. + */ + n = 0; + do { + if (cmdbuf->nbox > R300_SIMULTANEOUS_CLIPRECTS) { + ret = r300_emit_cliprects(dev_priv, cmdbuf, n); + if (ret) + return ret; + + cmdbuf->buffer->iterator = orig_iter; + } + + switch (header.packet3.packet) { + case R300_CMD_PACKET3_CLEAR: + DRM_DEBUG("R300_CMD_PACKET3_CLEAR\n"); + ret = r300_emit_clear(dev_priv, cmdbuf); + if (ret) { + DRM_ERROR("r300_emit_clear failed\n"); + return ret; + } + break; + + case R300_CMD_PACKET3_RAW: + DRM_DEBUG("R300_CMD_PACKET3_RAW\n"); + ret = r300_emit_raw_packet3(dev_priv, cmdbuf); + if (ret) { + DRM_ERROR("r300_emit_raw_packet3 failed\n"); + return ret; + } + break; + + default: + DRM_ERROR("bad packet3 type %i at byte %d\n", + header.packet3.packet, + cmdbuf->buffer->iterator - (int)sizeof(header)); + return -EINVAL; + } + + n += R300_SIMULTANEOUS_CLIPRECTS; + } while (n < cmdbuf->nbox); + + return 0; +} + +/* Some of the R300 chips seem to be extremely touchy about the two registers + * that are configured in r300_pacify. + * Among the worst offenders seems to be the R300 ND (0x4E44): When userspace + * sends a command buffer that contains only state setting commands and a + * vertex program/parameter upload sequence, this will eventually lead to a + * lockup, unless the sequence is bracketed by calls to r300_pacify. + * So we should take great care to *always* call r300_pacify before + * *anything* 3D related, and again afterwards. This is what the + * call bracket in r300_do_cp_cmdbuf is for. + */ + +/** + * Emit the sequence to pacify R300. + */ +static void r300_pacify(drm_radeon_private_t *dev_priv) +{ + uint32_t cache_z, cache_3d, cache_2d; + RING_LOCALS; + + cache_z = R300_ZC_FLUSH; + cache_2d = R300_RB2D_DC_FLUSH; + cache_3d = R300_RB3D_DC_FLUSH; + if (!(dev_priv->track_flush & RADEON_PURGE_EMITED)) { + /* we can purge, primitive where draw since last purge */ + cache_z |= R300_ZC_FREE; + cache_2d |= R300_RB2D_DC_FREE; + cache_3d |= R300_RB3D_DC_FREE; + } + + /* flush & purge zbuffer */ + BEGIN_RING(2); + OUT_RING(CP_PACKET0(R300_ZB_ZCACHE_CTLSTAT, 0)); + OUT_RING(cache_z); + ADVANCE_RING(); + /* flush & purge 3d */ + BEGIN_RING(2); + OUT_RING(CP_PACKET0(R300_RB3D_DSTCACHE_CTLSTAT, 0)); + OUT_RING(cache_3d); + ADVANCE_RING(); + /* flush & purge texture */ + BEGIN_RING(2); + OUT_RING(CP_PACKET0(R300_TX_INVALTAGS, 0)); + OUT_RING(0); + ADVANCE_RING(); + /* FIXME: is this one really needed ? */ + BEGIN_RING(2); + OUT_RING(CP_PACKET0(R300_RB3D_AARESOLVE_CTL, 0)); + OUT_RING(0); + ADVANCE_RING(); + BEGIN_RING(2); + OUT_RING(CP_PACKET0(RADEON_WAIT_UNTIL, 0)); + OUT_RING(RADEON_WAIT_3D_IDLECLEAN); + ADVANCE_RING(); + /* flush & purge 2d through E2 as RB2D will trigger lockup */ + BEGIN_RING(4); + OUT_RING(CP_PACKET0(R300_DSTCACHE_CTLSTAT, 0)); + OUT_RING(cache_2d); + OUT_RING(CP_PACKET0(RADEON_WAIT_UNTIL, 0)); + OUT_RING(RADEON_WAIT_2D_IDLECLEAN | + RADEON_WAIT_HOST_IDLECLEAN); + ADVANCE_RING(); + /* set flush & purge flags */ + dev_priv->track_flush |= RADEON_FLUSH_EMITED | RADEON_PURGE_EMITED; +} + +/** + * Called by r300_do_cp_cmdbuf to update the internal buffer age and state. + * The actual age emit is done by r300_do_cp_cmdbuf, which is why you must + * be careful about how this function is called. + */ +static void r300_discard_buffer(struct drm_device *dev, struct drm_master *master, struct drm_buf *buf) +{ + drm_radeon_buf_priv_t *buf_priv = buf->dev_private; + struct drm_radeon_master_private *master_priv = master->driver_priv; + + buf_priv->age = ++master_priv->sarea_priv->last_dispatch; + buf->pending = 1; + buf->used = 0; +} + +static void r300_cmd_wait(drm_radeon_private_t * dev_priv, + drm_r300_cmd_header_t header) +{ + u32 wait_until; + RING_LOCALS; + + if (!header.wait.flags) + return; + + wait_until = 0; + + switch(header.wait.flags) { + case R300_WAIT_2D: + wait_until = RADEON_WAIT_2D_IDLE; + break; + case R300_WAIT_3D: + wait_until = RADEON_WAIT_3D_IDLE; + break; + case R300_NEW_WAIT_2D_3D: + wait_until = RADEON_WAIT_2D_IDLE|RADEON_WAIT_3D_IDLE; + break; + case R300_NEW_WAIT_2D_2D_CLEAN: + wait_until = RADEON_WAIT_2D_IDLE|RADEON_WAIT_2D_IDLECLEAN; + break; + case R300_NEW_WAIT_3D_3D_CLEAN: + wait_until = RADEON_WAIT_3D_IDLE|RADEON_WAIT_3D_IDLECLEAN; + break; + case R300_NEW_WAIT_2D_2D_CLEAN_3D_3D_CLEAN: + wait_until = RADEON_WAIT_2D_IDLE|RADEON_WAIT_2D_IDLECLEAN; + wait_until |= RADEON_WAIT_3D_IDLE|RADEON_WAIT_3D_IDLECLEAN; + break; + default: + return; + } + + BEGIN_RING(2); + OUT_RING(CP_PACKET0(RADEON_WAIT_UNTIL, 0)); + OUT_RING(wait_until); + ADVANCE_RING(); +} + +static int r300_scratch(drm_radeon_private_t *dev_priv, + drm_radeon_kcmd_buffer_t *cmdbuf, + drm_r300_cmd_header_t header) +{ + u32 *ref_age_base; + u32 i, *buf_idx, h_pending; + u64 *ptr_addr; + u64 stack_ptr_addr; + RING_LOCALS; + + if (drm_buffer_unprocessed(cmdbuf->buffer) < + (sizeof(u64) + header.scratch.n_bufs * sizeof(*buf_idx))) { + return -EINVAL; + } + + if (header.scratch.reg >= 5) { + return -EINVAL; + } + + dev_priv->scratch_ages[header.scratch.reg]++; + + ptr_addr = drm_buffer_read_object(cmdbuf->buffer, + sizeof(stack_ptr_addr), &stack_ptr_addr); + ref_age_base = (u32 *)(unsigned long)get_unaligned(ptr_addr); + + for (i=0; i < header.scratch.n_bufs; i++) { + buf_idx = drm_buffer_pointer_to_dword(cmdbuf->buffer, 0); + *buf_idx *= 2; /* 8 bytes per buf */ + + if (DRM_COPY_TO_USER(ref_age_base + *buf_idx, + &dev_priv->scratch_ages[header.scratch.reg], + sizeof(u32))) + return -EINVAL; + + if (DRM_COPY_FROM_USER(&h_pending, + ref_age_base + *buf_idx + 1, + sizeof(u32))) + return -EINVAL; + + if (h_pending == 0) + return -EINVAL; + + h_pending--; + + if (DRM_COPY_TO_USER(ref_age_base + *buf_idx + 1, + &h_pending, + sizeof(u32))) + return -EINVAL; + + drm_buffer_advance(cmdbuf->buffer, sizeof(*buf_idx)); + } + + BEGIN_RING(2); + OUT_RING( CP_PACKET0( RADEON_SCRATCH_REG0 + header.scratch.reg * 4, 0 ) ); + OUT_RING( dev_priv->scratch_ages[header.scratch.reg] ); + ADVANCE_RING(); + + return 0; +} + +/** + * Uploads user-supplied vertex program instructions or parameters onto + * the graphics card. + * Called by r300_do_cp_cmdbuf. + */ +static inline int r300_emit_r500fp(drm_radeon_private_t *dev_priv, + drm_radeon_kcmd_buffer_t *cmdbuf, + drm_r300_cmd_header_t header) +{ + int sz; + int addr; + int type; + int isclamp; + int stride; + RING_LOCALS; + + sz = header.r500fp.count; + /* address is 9 bits 0 - 8, bit 1 of flags is part of address */ + addr = ((header.r500fp.adrhi_flags & 1) << 8) | header.r500fp.adrlo; + + type = !!(header.r500fp.adrhi_flags & R500FP_CONSTANT_TYPE); + isclamp = !!(header.r500fp.adrhi_flags & R500FP_CONSTANT_CLAMP); + + addr |= (type << 16); + addr |= (isclamp << 17); + + stride = type ? 4 : 6; + + DRM_DEBUG("r500fp %d %d type: %d\n", sz, addr, type); + if (!sz) + return 0; + if (sz * stride * 4 > drm_buffer_unprocessed(cmdbuf->buffer)) + return -EINVAL; + + BEGIN_RING(3 + sz * stride); + OUT_RING_REG(R500_GA_US_VECTOR_INDEX, addr); + OUT_RING(CP_PACKET0_TABLE(R500_GA_US_VECTOR_DATA, sz * stride - 1)); + OUT_RING_DRM_BUFFER(cmdbuf->buffer, sz * stride); + + ADVANCE_RING(); + + return 0; +} + + +/** + * Parses and validates a user-supplied command buffer and emits appropriate + * commands on the DMA ring buffer. + * Called by the ioctl handler function radeon_cp_cmdbuf. + */ +int r300_do_cp_cmdbuf(struct drm_device *dev, + struct drm_file *file_priv, + drm_radeon_kcmd_buffer_t *cmdbuf) +{ + drm_radeon_private_t *dev_priv = dev->dev_private; + struct drm_radeon_master_private *master_priv = file_priv->masterp->driver_priv; + struct drm_device_dma *dma = dev->dma; + struct drm_buf *buf = NULL; + int emit_dispatch_age = 0; + int ret = 0; + + DRM_DEBUG("\n"); + + /* pacify */ + r300_pacify(dev_priv); + + if (cmdbuf->nbox <= R300_SIMULTANEOUS_CLIPRECTS) { + ret = r300_emit_cliprects(dev_priv, cmdbuf, 0); + if (ret) + goto cleanup; + } + + while (drm_buffer_unprocessed(cmdbuf->buffer) + >= sizeof(drm_r300_cmd_header_t)) { + int idx; + drm_r300_cmd_header_t *header, stack_header; + + header = drm_buffer_read_object(cmdbuf->buffer, + sizeof(stack_header), &stack_header); + + switch (header->header.cmd_type) { + case R300_CMD_PACKET0: + DRM_DEBUG("R300_CMD_PACKET0\n"); + ret = r300_emit_packet0(dev_priv, cmdbuf, *header); + if (ret) { + DRM_ERROR("r300_emit_packet0 failed\n"); + goto cleanup; + } + break; + + case R300_CMD_VPU: + DRM_DEBUG("R300_CMD_VPU\n"); + ret = r300_emit_vpu(dev_priv, cmdbuf, *header); + if (ret) { + DRM_ERROR("r300_emit_vpu failed\n"); + goto cleanup; + } + break; + + case R300_CMD_PACKET3: + DRM_DEBUG("R300_CMD_PACKET3\n"); + ret = r300_emit_packet3(dev_priv, cmdbuf, *header); + if (ret) { + DRM_ERROR("r300_emit_packet3 failed\n"); + goto cleanup; + } + break; + + case R300_CMD_END3D: + DRM_DEBUG("R300_CMD_END3D\n"); + /* TODO: + Ideally userspace driver should not need to issue this call, + i.e. the drm driver should issue it automatically and prevent + lockups. + + In practice, we do not understand why this call is needed and what + it does (except for some vague guesses that it has to do with cache + coherence) and so the user space driver does it. + + Once we are sure which uses prevent lockups the code could be moved + into the kernel and the userspace driver will not + need to use this command. + + Note that issuing this command does not hurt anything + except, possibly, performance */ + r300_pacify(dev_priv); + break; + + case R300_CMD_CP_DELAY: + /* simple enough, we can do it here */ + DRM_DEBUG("R300_CMD_CP_DELAY\n"); + { + int i; + RING_LOCALS; + + BEGIN_RING(header->delay.count); + for (i = 0; i < header->delay.count; i++) + OUT_RING(RADEON_CP_PACKET2); + ADVANCE_RING(); + } + break; + + case R300_CMD_DMA_DISCARD: + DRM_DEBUG("RADEON_CMD_DMA_DISCARD\n"); + idx = header->dma.buf_idx; + if (idx < 0 || idx >= dma->buf_count) { + DRM_ERROR("buffer index %d (of %d max)\n", + idx, dma->buf_count - 1); + ret = -EINVAL; + goto cleanup; + } + + buf = dma->buflist[idx]; + if (buf->file_priv != file_priv || buf->pending) { + DRM_ERROR("bad buffer %p %p %d\n", + buf->file_priv, file_priv, + buf->pending); + ret = -EINVAL; + goto cleanup; + } + + emit_dispatch_age = 1; + r300_discard_buffer(dev, file_priv->masterp, buf); + break; + + case R300_CMD_WAIT: + DRM_DEBUG("R300_CMD_WAIT\n"); + r300_cmd_wait(dev_priv, *header); + break; + + case R300_CMD_SCRATCH: + DRM_DEBUG("R300_CMD_SCRATCH\n"); + ret = r300_scratch(dev_priv, cmdbuf, *header); + if (ret) { + DRM_ERROR("r300_scratch failed\n"); + goto cleanup; + } + break; + + case R300_CMD_R500FP: + if ((dev_priv->flags & RADEON_FAMILY_MASK) < CHIP_RV515) { + DRM_ERROR("Calling r500 command on r300 card\n"); + ret = -EINVAL; + goto cleanup; + } + DRM_DEBUG("R300_CMD_R500FP\n"); + ret = r300_emit_r500fp(dev_priv, cmdbuf, *header); + if (ret) { + DRM_ERROR("r300_emit_r500fp failed\n"); + goto cleanup; + } + break; + default: + DRM_ERROR("bad cmd_type %i at byte %d\n", + header->header.cmd_type, + cmdbuf->buffer->iterator - (int)sizeof(*header)); + ret = -EINVAL; + goto cleanup; + } + } + + DRM_DEBUG("END\n"); + + cleanup: + r300_pacify(dev_priv); + + /* We emit the vertex buffer age here, outside the pacifier "brackets" + * for two reasons: + * (1) This may coalesce multiple age emissions into a single one and + * (2) more importantly, some chips lock up hard when scratch registers + * are written inside the pacifier bracket. + */ + if (emit_dispatch_age) { + RING_LOCALS; + + /* Emit the vertex buffer age */ + BEGIN_RING(2); + RADEON_DISPATCH_AGE(master_priv->sarea_priv->last_dispatch); + ADVANCE_RING(); + } + + COMMIT_RING(); + + return ret; +} diff --git a/sys/dev/drm2/radeon/r300_reg.h b/sys/dev/drm2/radeon/r300_reg.h new file mode 100644 index 00000000000..3478d00920d --- /dev/null +++ b/sys/dev/drm2/radeon/r300_reg.h @@ -0,0 +1,1792 @@ +/* + * Copyright 2005 Nicolai Haehnle et al. + * Copyright 2008 Advanced Micro Devices, Inc. + * Copyright 2009 Jerome Glisse. + * + * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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. + * + * Authors: Nicolai Haehnle + * Jerome Glisse + */ +#ifndef _R300_REG_H_ +#define _R300_REG_H_ + +#include +__FBSDID("$FreeBSD$"); + +#define R300_SURF_TILE_MACRO (1<<16) +#define R300_SURF_TILE_MICRO (2<<16) +#define R300_SURF_TILE_BOTH (3<<16) + + +#define R300_MC_INIT_MISC_LAT_TIMER 0x180 +# define R300_MC_MISC__MC_CPR_INIT_LAT_SHIFT 0 +# define R300_MC_MISC__MC_VF_INIT_LAT_SHIFT 4 +# define R300_MC_MISC__MC_DISP0R_INIT_LAT_SHIFT 8 +# define R300_MC_MISC__MC_DISP1R_INIT_LAT_SHIFT 12 +# define R300_MC_MISC__MC_FIXED_INIT_LAT_SHIFT 16 +# define R300_MC_MISC__MC_E2R_INIT_LAT_SHIFT 20 +# define R300_MC_MISC__MC_SAME_PAGE_PRIO_SHIFT 24 +# define R300_MC_MISC__MC_GLOBW_INIT_LAT_SHIFT 28 + +#define R300_MC_INIT_GFX_LAT_TIMER 0x154 +# define R300_MC_MISC__MC_G3D0R_INIT_LAT_SHIFT 0 +# define R300_MC_MISC__MC_G3D1R_INIT_LAT_SHIFT 4 +# define R300_MC_MISC__MC_G3D2R_INIT_LAT_SHIFT 8 +# define R300_MC_MISC__MC_G3D3R_INIT_LAT_SHIFT 12 +# define R300_MC_MISC__MC_TX0R_INIT_LAT_SHIFT 16 +# define R300_MC_MISC__MC_TX1R_INIT_LAT_SHIFT 20 +# define R300_MC_MISC__MC_GLOBR_INIT_LAT_SHIFT 24 +# define R300_MC_MISC__MC_GLOBW_FULL_LAT_SHIFT 28 + +/* + * This file contains registers and constants for the R300. They have been + * found mostly by examining command buffers captured using glxtest, as well + * as by extrapolating some known registers and constants from the R200. + * I am fairly certain that they are correct unless stated otherwise + * in comments. + */ + +#define R300_SE_VPORT_XSCALE 0x1D98 +#define R300_SE_VPORT_XOFFSET 0x1D9C +#define R300_SE_VPORT_YSCALE 0x1DA0 +#define R300_SE_VPORT_YOFFSET 0x1DA4 +#define R300_SE_VPORT_ZSCALE 0x1DA8 +#define R300_SE_VPORT_ZOFFSET 0x1DAC + + +/* + * Vertex Array Processing (VAP) Control + * Stolen from r200 code from Christoph Brill (It's a guess!) + */ +#define R300_VAP_CNTL 0x2080 + +/* This register is written directly and also starts data section + * in many 3d CP_PACKET3's + */ +#define R300_VAP_VF_CNTL 0x2084 +# define R300_VAP_VF_CNTL__PRIM_TYPE__SHIFT 0 +# define R300_VAP_VF_CNTL__PRIM_NONE (0<<0) +# define R300_VAP_VF_CNTL__PRIM_POINTS (1<<0) +# define R300_VAP_VF_CNTL__PRIM_LINES (2<<0) +# define R300_VAP_VF_CNTL__PRIM_LINE_STRIP (3<<0) +# define R300_VAP_VF_CNTL__PRIM_TRIANGLES (4<<0) +# define R300_VAP_VF_CNTL__PRIM_TRIANGLE_FAN (5<<0) +# define R300_VAP_VF_CNTL__PRIM_TRIANGLE_STRIP (6<<0) +# define R300_VAP_VF_CNTL__PRIM_LINE_LOOP (12<<0) +# define R300_VAP_VF_CNTL__PRIM_QUADS (13<<0) +# define R300_VAP_VF_CNTL__PRIM_QUAD_STRIP (14<<0) +# define R300_VAP_VF_CNTL__PRIM_POLYGON (15<<0) + +# define R300_VAP_VF_CNTL__PRIM_WALK__SHIFT 4 + /* State based - direct writes to registers trigger vertex + generation */ +# define R300_VAP_VF_CNTL__PRIM_WALK_STATE_BASED (0<<4) +# define R300_VAP_VF_CNTL__PRIM_WALK_INDICES (1<<4) +# define R300_VAP_VF_CNTL__PRIM_WALK_VERTEX_LIST (2<<4) +# define R300_VAP_VF_CNTL__PRIM_WALK_VERTEX_EMBEDDED (3<<4) + + /* I don't think I saw these three used.. */ +# define R300_VAP_VF_CNTL__COLOR_ORDER__SHIFT 6 +# define R300_VAP_VF_CNTL__TCL_OUTPUT_CTL_ENA__SHIFT 9 +# define R300_VAP_VF_CNTL__PROG_STREAM_ENA__SHIFT 10 + + /* index size - when not set the indices are assumed to be 16 bit */ +# define R300_VAP_VF_CNTL__INDEX_SIZE_32bit (1<<11) + /* number of vertices */ +# define R300_VAP_VF_CNTL__NUM_VERTICES__SHIFT 16 + +/* BEGIN: Wild guesses */ +#define R300_VAP_OUTPUT_VTX_FMT_0 0x2090 +# define R300_VAP_OUTPUT_VTX_FMT_0__POS_PRESENT (1<<0) +# define R300_VAP_OUTPUT_VTX_FMT_0__COLOR_PRESENT (1<<1) +# define R300_VAP_OUTPUT_VTX_FMT_0__COLOR_1_PRESENT (1<<2) /* GUESS */ +# define R300_VAP_OUTPUT_VTX_FMT_0__COLOR_2_PRESENT (1<<3) /* GUESS */ +# define R300_VAP_OUTPUT_VTX_FMT_0__COLOR_3_PRESENT (1<<4) /* GUESS */ +# define R300_VAP_OUTPUT_VTX_FMT_0__PT_SIZE_PRESENT (1<<16) /* GUESS */ + +#define R300_VAP_OUTPUT_VTX_FMT_1 0x2094 + /* each of the following is 3 bits wide, specifies number + of components */ +# define R300_VAP_OUTPUT_VTX_FMT_1__TEX_0_COMP_CNT_SHIFT 0 +# define R300_VAP_OUTPUT_VTX_FMT_1__TEX_1_COMP_CNT_SHIFT 3 +# define R300_VAP_OUTPUT_VTX_FMT_1__TEX_2_COMP_CNT_SHIFT 6 +# define R300_VAP_OUTPUT_VTX_FMT_1__TEX_3_COMP_CNT_SHIFT 9 +# define R300_VAP_OUTPUT_VTX_FMT_1__TEX_4_COMP_CNT_SHIFT 12 +# define R300_VAP_OUTPUT_VTX_FMT_1__TEX_5_COMP_CNT_SHIFT 15 +# define R300_VAP_OUTPUT_VTX_FMT_1__TEX_6_COMP_CNT_SHIFT 18 +# define R300_VAP_OUTPUT_VTX_FMT_1__TEX_7_COMP_CNT_SHIFT 21 +/* END: Wild guesses */ + +#define R300_SE_VTE_CNTL 0x20b0 +# define R300_VPORT_X_SCALE_ENA 0x00000001 +# define R300_VPORT_X_OFFSET_ENA 0x00000002 +# define R300_VPORT_Y_SCALE_ENA 0x00000004 +# define R300_VPORT_Y_OFFSET_ENA 0x00000008 +# define R300_VPORT_Z_SCALE_ENA 0x00000010 +# define R300_VPORT_Z_OFFSET_ENA 0x00000020 +# define R300_VTX_XY_FMT 0x00000100 +# define R300_VTX_Z_FMT 0x00000200 +# define R300_VTX_W0_FMT 0x00000400 +# define R300_VTX_W0_NORMALIZE 0x00000800 +# define R300_VTX_ST_DENORMALIZED 0x00001000 + +/* BEGIN: Vertex data assembly - lots of uncertainties */ + +/* gap */ + +#define R300_VAP_CNTL_STATUS 0x2140 +# define R300_VC_NO_SWAP (0 << 0) +# define R300_VC_16BIT_SWAP (1 << 0) +# define R300_VC_32BIT_SWAP (2 << 0) +# define R300_VAP_TCL_BYPASS (1 << 8) + +/* gap */ + +/* Where do we get our vertex data? + * + * Vertex data either comes either from immediate mode registers or from + * vertex arrays. + * There appears to be no mixed mode (though we can force the pitch of + * vertex arrays to 0, effectively reusing the same element over and over + * again). + * + * Immediate mode is controlled by the INPUT_CNTL registers. I am not sure + * if these registers influence vertex array processing. + * + * Vertex arrays are controlled via the 3D_LOAD_VBPNTR packet3. + * + * In both cases, vertex attributes are then passed through INPUT_ROUTE. + * + * Beginning with INPUT_ROUTE_0_0 is a list of WORDs that route vertex data + * into the vertex processor's input registers. + * The first word routes the first input, the second word the second, etc. + * The corresponding input is routed into the register with the given index. + * The list is ended by a word with INPUT_ROUTE_END set. + * + * Always set COMPONENTS_4 in immediate mode. + */ + +#define R300_VAP_INPUT_ROUTE_0_0 0x2150 +# define R300_INPUT_ROUTE_COMPONENTS_1 (0 << 0) +# define R300_INPUT_ROUTE_COMPONENTS_2 (1 << 0) +# define R300_INPUT_ROUTE_COMPONENTS_3 (2 << 0) +# define R300_INPUT_ROUTE_COMPONENTS_4 (3 << 0) +# define R300_INPUT_ROUTE_COMPONENTS_RGBA (4 << 0) /* GUESS */ +# define R300_VAP_INPUT_ROUTE_IDX_SHIFT 8 +# define R300_VAP_INPUT_ROUTE_IDX_MASK (31 << 8) /* GUESS */ +# define R300_VAP_INPUT_ROUTE_END (1 << 13) +# define R300_INPUT_ROUTE_IMMEDIATE_MODE (0 << 14) /* GUESS */ +# define R300_INPUT_ROUTE_FLOAT (1 << 14) /* GUESS */ +# define R300_INPUT_ROUTE_UNSIGNED_BYTE (2 << 14) /* GUESS */ +# define R300_INPUT_ROUTE_FLOAT_COLOR (3 << 14) /* GUESS */ +#define R300_VAP_INPUT_ROUTE_0_1 0x2154 +#define R300_VAP_INPUT_ROUTE_0_2 0x2158 +#define R300_VAP_INPUT_ROUTE_0_3 0x215C +#define R300_VAP_INPUT_ROUTE_0_4 0x2160 +#define R300_VAP_INPUT_ROUTE_0_5 0x2164 +#define R300_VAP_INPUT_ROUTE_0_6 0x2168 +#define R300_VAP_INPUT_ROUTE_0_7 0x216C + +/* gap */ + +/* Notes: + * - always set up to produce at least two attributes: + * if vertex program uses only position, fglrx will set normal, too + * - INPUT_CNTL_0_COLOR and INPUT_CNTL_COLOR bits are always equal. + */ +#define R300_VAP_INPUT_CNTL_0 0x2180 +# define R300_INPUT_CNTL_0_COLOR 0x00000001 +#define R300_VAP_INPUT_CNTL_1 0x2184 +# define R300_INPUT_CNTL_POS 0x00000001 +# define R300_INPUT_CNTL_NORMAL 0x00000002 +# define R300_INPUT_CNTL_COLOR 0x00000004 +# define R300_INPUT_CNTL_TC0 0x00000400 +# define R300_INPUT_CNTL_TC1 0x00000800 +# define R300_INPUT_CNTL_TC2 0x00001000 /* GUESS */ +# define R300_INPUT_CNTL_TC3 0x00002000 /* GUESS */ +# define R300_INPUT_CNTL_TC4 0x00004000 /* GUESS */ +# define R300_INPUT_CNTL_TC5 0x00008000 /* GUESS */ +# define R300_INPUT_CNTL_TC6 0x00010000 /* GUESS */ +# define R300_INPUT_CNTL_TC7 0x00020000 /* GUESS */ + +/* gap */ + +/* Words parallel to INPUT_ROUTE_0; All words that are active in INPUT_ROUTE_0 + * are set to a swizzling bit pattern, other words are 0. + * + * In immediate mode, the pattern is always set to xyzw. In vertex array + * mode, the swizzling pattern is e.g. used to set zw components in texture + * coordinates with only tweo components. + */ +#define R300_VAP_INPUT_ROUTE_1_0 0x21E0 +# define R300_INPUT_ROUTE_SELECT_X 0 +# define R300_INPUT_ROUTE_SELECT_Y 1 +# define R300_INPUT_ROUTE_SELECT_Z 2 +# define R300_INPUT_ROUTE_SELECT_W 3 +# define R300_INPUT_ROUTE_SELECT_ZERO 4 +# define R300_INPUT_ROUTE_SELECT_ONE 5 +# define R300_INPUT_ROUTE_SELECT_MASK 7 +# define R300_INPUT_ROUTE_X_SHIFT 0 +# define R300_INPUT_ROUTE_Y_SHIFT 3 +# define R300_INPUT_ROUTE_Z_SHIFT 6 +# define R300_INPUT_ROUTE_W_SHIFT 9 +# define R300_INPUT_ROUTE_ENABLE (15 << 12) +#define R300_VAP_INPUT_ROUTE_1_1 0x21E4 +#define R300_VAP_INPUT_ROUTE_1_2 0x21E8 +#define R300_VAP_INPUT_ROUTE_1_3 0x21EC +#define R300_VAP_INPUT_ROUTE_1_4 0x21F0 +#define R300_VAP_INPUT_ROUTE_1_5 0x21F4 +#define R300_VAP_INPUT_ROUTE_1_6 0x21F8 +#define R300_VAP_INPUT_ROUTE_1_7 0x21FC + +/* END: Vertex data assembly */ + +/* gap */ + +/* BEGIN: Upload vertex program and data */ + +/* + * The programmable vertex shader unit has a memory bank of unknown size + * that can be written to in 16 byte units by writing the address into + * UPLOAD_ADDRESS, followed by data in UPLOAD_DATA (multiples of 4 DWORDs). + * + * Pointers into the memory bank are always in multiples of 16 bytes. + * + * The memory bank is divided into areas with fixed meaning. + * + * Starting at address UPLOAD_PROGRAM: Vertex program instructions. + * Native limits reported by drivers from ATI suggest size 256 (i.e. 4KB), + * whereas the difference between known addresses suggests size 512. + * + * Starting at address UPLOAD_PARAMETERS: Vertex program parameters. + * Native reported limits and the VPI layout suggest size 256, whereas + * difference between known addresses suggests size 512. + * + * At address UPLOAD_POINTSIZE is a vector (0, 0, ps, 0), where ps is the + * floating point pointsize. The exact purpose of this state is uncertain, + * as there is also the R300_RE_POINTSIZE register. + * + * Multiple vertex programs and parameter sets can be loaded at once, + * which could explain the size discrepancy. + */ +#define R300_VAP_PVS_UPLOAD_ADDRESS 0x2200 +# define R300_PVS_UPLOAD_PROGRAM 0x00000000 +# define R300_PVS_UPLOAD_PARAMETERS 0x00000200 +# define R300_PVS_UPLOAD_POINTSIZE 0x00000406 + +/* gap */ + +#define R300_VAP_PVS_UPLOAD_DATA 0x2208 + +/* END: Upload vertex program and data */ + +/* gap */ + +/* I do not know the purpose of this register. However, I do know that + * it is set to 221C_CLEAR for clear operations and to 221C_NORMAL + * for normal rendering. + */ +#define R300_VAP_UNKNOWN_221C 0x221C +# define R300_221C_NORMAL 0x00000000 +# define R300_221C_CLEAR 0x0001C000 + +/* These seem to be per-pixel and per-vertex X and Y clipping planes. The first + * plane is per-pixel and the second plane is per-vertex. + * + * This was determined by experimentation alone but I believe it is correct. + * + * These registers are called X_QUAD0_1_FL to X_QUAD0_4_FL by glxtest. + */ +#define R300_VAP_CLIP_X_0 0x2220 +#define R300_VAP_CLIP_X_1 0x2224 +#define R300_VAP_CLIP_Y_0 0x2228 +#define R300_VAP_CLIP_Y_1 0x2230 + +/* gap */ + +/* Sometimes, END_OF_PKT and 0x2284=0 are the only commands sent between + * rendering commands and overwriting vertex program parameters. + * Therefore, I suspect writing zero to 0x2284 synchronizes the engine and + * avoids bugs caused by still running shaders reading bad data from memory. + */ +#define R300_VAP_PVS_STATE_FLUSH_REG 0x2284 + +/* Absolutely no clue what this register is about. */ +#define R300_VAP_UNKNOWN_2288 0x2288 +# define R300_2288_R300 0x00750000 /* -- nh */ +# define R300_2288_RV350 0x0000FFFF /* -- Vladimir */ + +/* gap */ + +/* Addresses are relative to the vertex program instruction area of the + * memory bank. PROGRAM_END points to the last instruction of the active + * program + * + * The meaning of the two UNKNOWN fields is obviously not known. However, + * experiments so far have shown that both *must* point to an instruction + * inside the vertex program, otherwise the GPU locks up. + * + * fglrx usually sets CNTL_3_UNKNOWN to the end of the program and + * R300_PVS_CNTL_1_POS_END_SHIFT points to instruction where last write to + * position takes place. + * + * Most likely this is used to ignore rest of the program in cases + * where group of verts arent visible. For some reason this "section" + * is sometimes accepted other instruction that have no relationship with + * position calculations. + */ +#define R300_VAP_PVS_CNTL_1 0x22D0 +# define R300_PVS_CNTL_1_PROGRAM_START_SHIFT 0 +# define R300_PVS_CNTL_1_POS_END_SHIFT 10 +# define R300_PVS_CNTL_1_PROGRAM_END_SHIFT 20 +/* Addresses are relative the the vertex program parameters area. */ +#define R300_VAP_PVS_CNTL_2 0x22D4 +# define R300_PVS_CNTL_2_PARAM_OFFSET_SHIFT 0 +# define R300_PVS_CNTL_2_PARAM_COUNT_SHIFT 16 +#define R300_VAP_PVS_CNTL_3 0x22D8 +# define R300_PVS_CNTL_3_PROGRAM_UNKNOWN_SHIFT 10 +# define R300_PVS_CNTL_3_PROGRAM_UNKNOWN2_SHIFT 0 + +/* The entire range from 0x2300 to 0x2AC inclusive seems to be used for + * immediate vertices + */ +#define R300_VAP_VTX_COLOR_R 0x2464 +#define R300_VAP_VTX_COLOR_G 0x2468 +#define R300_VAP_VTX_COLOR_B 0x246C +#define R300_VAP_VTX_POS_0_X_1 0x2490 /* used for glVertex2*() */ +#define R300_VAP_VTX_POS_0_Y_1 0x2494 +#define R300_VAP_VTX_COLOR_PKD 0x249C /* RGBA */ +#define R300_VAP_VTX_POS_0_X_2 0x24A0 /* used for glVertex3*() */ +#define R300_VAP_VTX_POS_0_Y_2 0x24A4 +#define R300_VAP_VTX_POS_0_Z_2 0x24A8 +/* write 0 to indicate end of packet? */ +#define R300_VAP_VTX_END_OF_PKT 0x24AC + +/* gap */ + +/* These are values from r300_reg/r300_reg.h - they are known to be correct + * and are here so we can use one register file instead of several + * - Vladimir + */ +#define R300_GB_VAP_RASTER_VTX_FMT_0 0x4000 +# define R300_GB_VAP_RASTER_VTX_FMT_0__POS_PRESENT (1<<0) +# define R300_GB_VAP_RASTER_VTX_FMT_0__COLOR_0_PRESENT (1<<1) +# define R300_GB_VAP_RASTER_VTX_FMT_0__COLOR_1_PRESENT (1<<2) +# define R300_GB_VAP_RASTER_VTX_FMT_0__COLOR_2_PRESENT (1<<3) +# define R300_GB_VAP_RASTER_VTX_FMT_0__COLOR_3_PRESENT (1<<4) +# define R300_GB_VAP_RASTER_VTX_FMT_0__COLOR_SPACE (0xf<<5) +# define R300_GB_VAP_RASTER_VTX_FMT_0__PT_SIZE_PRESENT (0x1<<16) + +#define R300_GB_VAP_RASTER_VTX_FMT_1 0x4004 + /* each of the following is 3 bits wide, specifies number + of components */ +# define R300_GB_VAP_RASTER_VTX_FMT_1__TEX_0_COMP_CNT_SHIFT 0 +# define R300_GB_VAP_RASTER_VTX_FMT_1__TEX_1_COMP_CNT_SHIFT 3 +# define R300_GB_VAP_RASTER_VTX_FMT_1__TEX_2_COMP_CNT_SHIFT 6 +# define R300_GB_VAP_RASTER_VTX_FMT_1__TEX_3_COMP_CNT_SHIFT 9 +# define R300_GB_VAP_RASTER_VTX_FMT_1__TEX_4_COMP_CNT_SHIFT 12 +# define R300_GB_VAP_RASTER_VTX_FMT_1__TEX_5_COMP_CNT_SHIFT 15 +# define R300_GB_VAP_RASTER_VTX_FMT_1__TEX_6_COMP_CNT_SHIFT 18 +# define R300_GB_VAP_RASTER_VTX_FMT_1__TEX_7_COMP_CNT_SHIFT 21 + +/* UNK30 seems to enables point to quad transformation on textures + * (or something closely related to that). + * This bit is rather fatal at the time being due to lackings at pixel + * shader side + */ +#define R300_GB_ENABLE 0x4008 +# define R300_GB_POINT_STUFF_ENABLE (1<<0) +# define R300_GB_LINE_STUFF_ENABLE (1<<1) +# define R300_GB_TRIANGLE_STUFF_ENABLE (1<<2) +# define R300_GB_STENCIL_AUTO_ENABLE (1<<4) +# define R300_GB_UNK31 (1<<31) + /* each of the following is 2 bits wide */ +#define R300_GB_TEX_REPLICATE 0 +#define R300_GB_TEX_ST 1 +#define R300_GB_TEX_STR 2 +# define R300_GB_TEX0_SOURCE_SHIFT 16 +# define R300_GB_TEX1_SOURCE_SHIFT 18 +# define R300_GB_TEX2_SOURCE_SHIFT 20 +# define R300_GB_TEX3_SOURCE_SHIFT 22 +# define R300_GB_TEX4_SOURCE_SHIFT 24 +# define R300_GB_TEX5_SOURCE_SHIFT 26 +# define R300_GB_TEX6_SOURCE_SHIFT 28 +# define R300_GB_TEX7_SOURCE_SHIFT 30 + +/* MSPOS - positions for multisample antialiasing (?) */ +#define R300_GB_MSPOS0 0x4010 + /* shifts - each of the fields is 4 bits */ +# define R300_GB_MSPOS0__MS_X0_SHIFT 0 +# define R300_GB_MSPOS0__MS_Y0_SHIFT 4 +# define R300_GB_MSPOS0__MS_X1_SHIFT 8 +# define R300_GB_MSPOS0__MS_Y1_SHIFT 12 +# define R300_GB_MSPOS0__MS_X2_SHIFT 16 +# define R300_GB_MSPOS0__MS_Y2_SHIFT 20 +# define R300_GB_MSPOS0__MSBD0_Y 24 +# define R300_GB_MSPOS0__MSBD0_X 28 + +#define R300_GB_MSPOS1 0x4014 +# define R300_GB_MSPOS1__MS_X3_SHIFT 0 +# define R300_GB_MSPOS1__MS_Y3_SHIFT 4 +# define R300_GB_MSPOS1__MS_X4_SHIFT 8 +# define R300_GB_MSPOS1__MS_Y4_SHIFT 12 +# define R300_GB_MSPOS1__MS_X5_SHIFT 16 +# define R300_GB_MSPOS1__MS_Y5_SHIFT 20 +# define R300_GB_MSPOS1__MSBD1 24 + + +#define R300_GB_TILE_CONFIG 0x4018 +# define R300_GB_TILE_ENABLE (1<<0) +# define R300_GB_TILE_PIPE_COUNT_RV300 0 +# define R300_GB_TILE_PIPE_COUNT_R300 (3<<1) +# define R300_GB_TILE_PIPE_COUNT_R420 (7<<1) +# define R300_GB_TILE_PIPE_COUNT_RV410 (3<<1) +# define R300_GB_TILE_SIZE_8 0 +# define R300_GB_TILE_SIZE_16 (1<<4) +# define R300_GB_TILE_SIZE_32 (2<<4) +# define R300_GB_SUPER_SIZE_1 (0<<6) +# define R300_GB_SUPER_SIZE_2 (1<<6) +# define R300_GB_SUPER_SIZE_4 (2<<6) +# define R300_GB_SUPER_SIZE_8 (3<<6) +# define R300_GB_SUPER_SIZE_16 (4<<6) +# define R300_GB_SUPER_SIZE_32 (5<<6) +# define R300_GB_SUPER_SIZE_64 (6<<6) +# define R300_GB_SUPER_SIZE_128 (7<<6) +# define R300_GB_SUPER_X_SHIFT 9 /* 3 bits wide */ +# define R300_GB_SUPER_Y_SHIFT 12 /* 3 bits wide */ +# define R300_GB_SUPER_TILE_A 0 +# define R300_GB_SUPER_TILE_B (1<<15) +# define R300_GB_SUBPIXEL_1_12 0 +# define R300_GB_SUBPIXEL_1_16 (1<<16) + +#define R300_GB_FIFO_SIZE 0x4024 + /* each of the following is 2 bits wide */ +#define R300_GB_FIFO_SIZE_32 0 +#define R300_GB_FIFO_SIZE_64 1 +#define R300_GB_FIFO_SIZE_128 2 +#define R300_GB_FIFO_SIZE_256 3 +# define R300_SC_IFIFO_SIZE_SHIFT 0 +# define R300_SC_TZFIFO_SIZE_SHIFT 2 +# define R300_SC_BFIFO_SIZE_SHIFT 4 + +# define R300_US_OFIFO_SIZE_SHIFT 12 +# define R300_US_WFIFO_SIZE_SHIFT 14 + /* the following use the same constants as above, but meaning is + is times 2 (i.e. instead of 32 words it means 64 */ +# define R300_RS_TFIFO_SIZE_SHIFT 6 +# define R300_RS_CFIFO_SIZE_SHIFT 8 +# define R300_US_RAM_SIZE_SHIFT 10 + /* watermarks, 3 bits wide */ +# define R300_RS_HIGHWATER_COL_SHIFT 16 +# define R300_RS_HIGHWATER_TEX_SHIFT 19 +# define R300_OFIFO_HIGHWATER_SHIFT 22 /* two bits only */ +# define R300_CUBE_FIFO_HIGHWATER_COL_SHIFT 24 + +#define R300_GB_SELECT 0x401C +# define R300_GB_FOG_SELECT_C0A 0 +# define R300_GB_FOG_SELECT_C1A 1 +# define R300_GB_FOG_SELECT_C2A 2 +# define R300_GB_FOG_SELECT_C3A 3 +# define R300_GB_FOG_SELECT_1_1_W 4 +# define R300_GB_FOG_SELECT_Z 5 +# define R300_GB_DEPTH_SELECT_Z 0 +# define R300_GB_DEPTH_SELECT_1_1_W (1<<3) +# define R300_GB_W_SELECT_1_W 0 +# define R300_GB_W_SELECT_1 (1<<4) + +#define R300_GB_AA_CONFIG 0x4020 +# define R300_AA_DISABLE 0x00 +# define R300_AA_ENABLE 0x01 +# define R300_AA_SUBSAMPLES_2 0 +# define R300_AA_SUBSAMPLES_3 (1<<1) +# define R300_AA_SUBSAMPLES_4 (2<<1) +# define R300_AA_SUBSAMPLES_6 (3<<1) + +/* gap */ + +/* Zero to flush caches. */ +#define R300_TX_INVALTAGS 0x4100 +#define R300_TX_FLUSH 0x0 + +/* The upper enable bits are guessed, based on fglrx reported limits. */ +#define R300_TX_ENABLE 0x4104 +# define R300_TX_ENABLE_0 (1 << 0) +# define R300_TX_ENABLE_1 (1 << 1) +# define R300_TX_ENABLE_2 (1 << 2) +# define R300_TX_ENABLE_3 (1 << 3) +# define R300_TX_ENABLE_4 (1 << 4) +# define R300_TX_ENABLE_5 (1 << 5) +# define R300_TX_ENABLE_6 (1 << 6) +# define R300_TX_ENABLE_7 (1 << 7) +# define R300_TX_ENABLE_8 (1 << 8) +# define R300_TX_ENABLE_9 (1 << 9) +# define R300_TX_ENABLE_10 (1 << 10) +# define R300_TX_ENABLE_11 (1 << 11) +# define R300_TX_ENABLE_12 (1 << 12) +# define R300_TX_ENABLE_13 (1 << 13) +# define R300_TX_ENABLE_14 (1 << 14) +# define R300_TX_ENABLE_15 (1 << 15) + +/* The pointsize is given in multiples of 6. The pointsize can be + * enormous: Clear() renders a single point that fills the entire + * framebuffer. + */ +#define R300_RE_POINTSIZE 0x421C +# define R300_POINTSIZE_Y_SHIFT 0 +# define R300_POINTSIZE_Y_MASK (0xFFFF << 0) /* GUESS */ +# define R300_POINTSIZE_X_SHIFT 16 +# define R300_POINTSIZE_X_MASK (0xFFFF << 16) /* GUESS */ +# define R300_POINTSIZE_MAX (R300_POINTSIZE_Y_MASK / 6) + +/* The line width is given in multiples of 6. + * In default mode lines are classified as vertical lines. + * HO: horizontal + * VE: vertical or horizontal + * HO & VE: no classification + */ +#define R300_RE_LINE_CNT 0x4234 +# define R300_LINESIZE_SHIFT 0 +# define R300_LINESIZE_MASK (0xFFFF << 0) /* GUESS */ +# define R300_LINESIZE_MAX (R300_LINESIZE_MASK / 6) +# define R300_LINE_CNT_HO (1 << 16) +# define R300_LINE_CNT_VE (1 << 17) + +/* Some sort of scale or clamp value for texcoordless textures. */ +#define R300_RE_UNK4238 0x4238 + +/* Something shade related */ +#define R300_RE_SHADE 0x4274 + +#define R300_RE_SHADE_MODEL 0x4278 +# define R300_RE_SHADE_MODEL_SMOOTH 0x3aaaa +# define R300_RE_SHADE_MODEL_FLAT 0x39595 + +/* Dangerous */ +#define R300_RE_POLYGON_MODE 0x4288 +# define R300_PM_ENABLED (1 << 0) +# define R300_PM_FRONT_POINT (0 << 0) +# define R300_PM_BACK_POINT (0 << 0) +# define R300_PM_FRONT_LINE (1 << 4) +# define R300_PM_FRONT_FILL (1 << 5) +# define R300_PM_BACK_LINE (1 << 7) +# define R300_PM_BACK_FILL (1 << 8) + +/* Fog parameters */ +#define R300_RE_FOG_SCALE 0x4294 +#define R300_RE_FOG_START 0x4298 + +/* Not sure why there are duplicate of factor and constant values. + * My best guess so far is that there are separate zbiases for test and write. + * Ordering might be wrong. + * Some of the tests indicate that fgl has a fallback implementation of zbias + * via pixel shaders. + */ +#define R300_RE_ZBIAS_CNTL 0x42A0 /* GUESS */ +#define R300_RE_ZBIAS_T_FACTOR 0x42A4 +#define R300_RE_ZBIAS_T_CONSTANT 0x42A8 +#define R300_RE_ZBIAS_W_FACTOR 0x42AC +#define R300_RE_ZBIAS_W_CONSTANT 0x42B0 + +/* This register needs to be set to (1<<1) for RV350 to correctly + * perform depth test (see --vb-triangles in r300_demo) + * Don't know about other chips. - Vladimir + * This is set to 3 when GL_POLYGON_OFFSET_FILL is on. + * My guess is that there are two bits for each zbias primitive + * (FILL, LINE, POINT). + * One to enable depth test and one for depth write. + * Yet this doesn't explain why depth writes work ... + */ +#define R300_RE_OCCLUSION_CNTL 0x42B4 +# define R300_OCCLUSION_ON (1<<1) + +#define R300_RE_CULL_CNTL 0x42B8 +# define R300_CULL_FRONT (1 << 0) +# define R300_CULL_BACK (1 << 1) +# define R300_FRONT_FACE_CCW (0 << 2) +# define R300_FRONT_FACE_CW (1 << 2) + + +/* BEGIN: Rasterization / Interpolators - many guesses */ + +/* 0_UNKNOWN_18 has always been set except for clear operations. + * TC_CNT is the number of incoming texture coordinate sets (i.e. it depends + * on the vertex program, *not* the fragment program) + */ +#define R300_RS_CNTL_0 0x4300 +# define R300_RS_CNTL_TC_CNT_SHIFT 2 +# define R300_RS_CNTL_TC_CNT_MASK (7 << 2) + /* number of color interpolators used */ +# define R300_RS_CNTL_CI_CNT_SHIFT 7 +# define R300_RS_CNTL_0_UNKNOWN_18 (1 << 18) + /* Guess: RS_CNTL_1 holds the index of the highest used RS_ROUTE_n + register. */ +#define R300_RS_CNTL_1 0x4304 + +/* gap */ + +/* Only used for texture coordinates. + * Use the source field to route texture coordinate input from the + * vertex program to the desired interpolator. Note that the source + * field is relative to the outputs the vertex program *actually* + * writes. If a vertex program only writes texcoord[1], this will + * be source index 0. + * Set INTERP_USED on all interpolators that produce data used by + * the fragment program. INTERP_USED looks like a swizzling mask, + * but I haven't seen it used that way. + * + * Note: The _UNKNOWN constants are always set in their respective + * register. I don't know if this is necessary. + */ +#define R300_RS_INTERP_0 0x4310 +#define R300_RS_INTERP_1 0x4314 +# define R300_RS_INTERP_1_UNKNOWN 0x40 +#define R300_RS_INTERP_2 0x4318 +# define R300_RS_INTERP_2_UNKNOWN 0x80 +#define R300_RS_INTERP_3 0x431C +# define R300_RS_INTERP_3_UNKNOWN 0xC0 +#define R300_RS_INTERP_4 0x4320 +#define R300_RS_INTERP_5 0x4324 +#define R300_RS_INTERP_6 0x4328 +#define R300_RS_INTERP_7 0x432C +# define R300_RS_INTERP_SRC_SHIFT 2 +# define R300_RS_INTERP_SRC_MASK (7 << 2) +# define R300_RS_INTERP_USED 0x00D10000 + +/* These DWORDs control how vertex data is routed into fragment program + * registers, after interpolators. + */ +#define R300_RS_ROUTE_0 0x4330 +#define R300_RS_ROUTE_1 0x4334 +#define R300_RS_ROUTE_2 0x4338 +#define R300_RS_ROUTE_3 0x433C /* GUESS */ +#define R300_RS_ROUTE_4 0x4340 /* GUESS */ +#define R300_RS_ROUTE_5 0x4344 /* GUESS */ +#define R300_RS_ROUTE_6 0x4348 /* GUESS */ +#define R300_RS_ROUTE_7 0x434C /* GUESS */ +# define R300_RS_ROUTE_SOURCE_INTERP_0 0 +# define R300_RS_ROUTE_SOURCE_INTERP_1 1 +# define R300_RS_ROUTE_SOURCE_INTERP_2 2 +# define R300_RS_ROUTE_SOURCE_INTERP_3 3 +# define R300_RS_ROUTE_SOURCE_INTERP_4 4 +# define R300_RS_ROUTE_SOURCE_INTERP_5 5 /* GUESS */ +# define R300_RS_ROUTE_SOURCE_INTERP_6 6 /* GUESS */ +# define R300_RS_ROUTE_SOURCE_INTERP_7 7 /* GUESS */ +# define R300_RS_ROUTE_ENABLE (1 << 3) /* GUESS */ +# define R300_RS_ROUTE_DEST_SHIFT 6 +# define R300_RS_ROUTE_DEST_MASK (31 << 6) /* GUESS */ + +/* Special handling for color: When the fragment program uses color, + * the ROUTE_0_COLOR bit is set and ROUTE_0_COLOR_DEST contains the + * color register index. + * + * Apperently you may set the R300_RS_ROUTE_0_COLOR bit, but not provide any + * R300_RS_ROUTE_0_COLOR_DEST value; this setup is used for clearing the state. + * See r300_ioctl.c:r300EmitClearState. I'm not sure if this setup is strictly + * correct or not. - Oliver. + */ +# define R300_RS_ROUTE_0_COLOR (1 << 14) +# define R300_RS_ROUTE_0_COLOR_DEST_SHIFT 17 +# define R300_RS_ROUTE_0_COLOR_DEST_MASK (31 << 17) /* GUESS */ +/* As above, but for secondary color */ +# define R300_RS_ROUTE_1_COLOR1 (1 << 14) +# define R300_RS_ROUTE_1_COLOR1_DEST_SHIFT 17 +# define R300_RS_ROUTE_1_COLOR1_DEST_MASK (31 << 17) +# define R300_RS_ROUTE_1_UNKNOWN11 (1 << 11) +/* END: Rasterization / Interpolators - many guesses */ + +/* Hierarchical Z Enable */ +#define R300_SC_HYPERZ 0x43a4 +# define R300_SC_HYPERZ_DISABLE (0 << 0) +# define R300_SC_HYPERZ_ENABLE (1 << 0) +# define R300_SC_HYPERZ_MIN (0 << 1) +# define R300_SC_HYPERZ_MAX (1 << 1) +# define R300_SC_HYPERZ_ADJ_256 (0 << 2) +# define R300_SC_HYPERZ_ADJ_128 (1 << 2) +# define R300_SC_HYPERZ_ADJ_64 (2 << 2) +# define R300_SC_HYPERZ_ADJ_32 (3 << 2) +# define R300_SC_HYPERZ_ADJ_16 (4 << 2) +# define R300_SC_HYPERZ_ADJ_8 (5 << 2) +# define R300_SC_HYPERZ_ADJ_4 (6 << 2) +# define R300_SC_HYPERZ_ADJ_2 (7 << 2) +# define R300_SC_HYPERZ_HZ_Z0MIN_NO (0 << 5) +# define R300_SC_HYPERZ_HZ_Z0MIN (1 << 5) +# define R300_SC_HYPERZ_HZ_Z0MAX_NO (0 << 6) +# define R300_SC_HYPERZ_HZ_Z0MAX (1 << 6) + +#define R300_SC_EDGERULE 0x43a8 + +/* BEGIN: Scissors and cliprects */ + +/* There are four clipping rectangles. Their corner coordinates are inclusive. + * Every pixel is assigned a number from 0 and 15 by setting bits 0-3 depending + * on whether the pixel is inside cliprects 0-3, respectively. For example, + * if a pixel is inside cliprects 0 and 1, but outside 2 and 3, it is assigned + * the number 3 (binary 0011). + * Iff the bit corresponding to the pixel's number in RE_CLIPRECT_CNTL is set, + * the pixel is rasterized. + * + * In addition to this, there is a scissors rectangle. Only pixels inside the + * scissors rectangle are drawn. (coordinates are inclusive) + * + * For some reason, the top-left corner of the framebuffer is at (1440, 1440) + * for the purpose of clipping and scissors. + */ +#define R300_RE_CLIPRECT_TL_0 0x43B0 +#define R300_RE_CLIPRECT_BR_0 0x43B4 +#define R300_RE_CLIPRECT_TL_1 0x43B8 +#define R300_RE_CLIPRECT_BR_1 0x43BC +#define R300_RE_CLIPRECT_TL_2 0x43C0 +#define R300_RE_CLIPRECT_BR_2 0x43C4 +#define R300_RE_CLIPRECT_TL_3 0x43C8 +#define R300_RE_CLIPRECT_BR_3 0x43CC +# define R300_CLIPRECT_OFFSET 1440 +# define R300_CLIPRECT_MASK 0x1FFF +# define R300_CLIPRECT_X_SHIFT 0 +# define R300_CLIPRECT_X_MASK (0x1FFF << 0) +# define R300_CLIPRECT_Y_SHIFT 13 +# define R300_CLIPRECT_Y_MASK (0x1FFF << 13) +#define R300_RE_CLIPRECT_CNTL 0x43D0 +# define R300_CLIP_OUT (1 << 0) +# define R300_CLIP_0 (1 << 1) +# define R300_CLIP_1 (1 << 2) +# define R300_CLIP_10 (1 << 3) +# define R300_CLIP_2 (1 << 4) +# define R300_CLIP_20 (1 << 5) +# define R300_CLIP_21 (1 << 6) +# define R300_CLIP_210 (1 << 7) +# define R300_CLIP_3 (1 << 8) +# define R300_CLIP_30 (1 << 9) +# define R300_CLIP_31 (1 << 10) +# define R300_CLIP_310 (1 << 11) +# define R300_CLIP_32 (1 << 12) +# define R300_CLIP_320 (1 << 13) +# define R300_CLIP_321 (1 << 14) +# define R300_CLIP_3210 (1 << 15) + +/* gap */ + +#define R300_RE_SCISSORS_TL 0x43E0 +#define R300_RE_SCISSORS_BR 0x43E4 +# define R300_SCISSORS_OFFSET 1440 +# define R300_SCISSORS_X_SHIFT 0 +# define R300_SCISSORS_X_MASK (0x1FFF << 0) +# define R300_SCISSORS_Y_SHIFT 13 +# define R300_SCISSORS_Y_MASK (0x1FFF << 13) +/* END: Scissors and cliprects */ + +/* BEGIN: Texture specification */ + +/* + * The texture specification dwords are grouped by meaning and not by texture + * unit. This means that e.g. the offset for texture image unit N is found in + * register TX_OFFSET_0 + (4*N) + */ +#define R300_TX_FILTER_0 0x4400 +# define R300_TX_REPEAT 0 +# define R300_TX_MIRRORED 1 +# define R300_TX_CLAMP 4 +# define R300_TX_CLAMP_TO_EDGE 2 +# define R300_TX_CLAMP_TO_BORDER 6 +# define R300_TX_WRAP_S_SHIFT 0 +# define R300_TX_WRAP_S_MASK (7 << 0) +# define R300_TX_WRAP_T_SHIFT 3 +# define R300_TX_WRAP_T_MASK (7 << 3) +# define R300_TX_WRAP_Q_SHIFT 6 +# define R300_TX_WRAP_Q_MASK (7 << 6) +# define R300_TX_MAG_FILTER_NEAREST (1 << 9) +# define R300_TX_MAG_FILTER_LINEAR (2 << 9) +# define R300_TX_MAG_FILTER_MASK (3 << 9) +# define R300_TX_MIN_FILTER_NEAREST (1 << 11) +# define R300_TX_MIN_FILTER_LINEAR (2 << 11) +# define R300_TX_MIN_FILTER_NEAREST_MIP_NEAREST (5 << 11) +# define R300_TX_MIN_FILTER_NEAREST_MIP_LINEAR (9 << 11) +# define R300_TX_MIN_FILTER_LINEAR_MIP_NEAREST (6 << 11) +# define R300_TX_MIN_FILTER_LINEAR_MIP_LINEAR (10 << 11) + +/* NOTE: NEAREST doesn't seem to exist. + * Im not seting MAG_FILTER_MASK and (3 << 11) on for all + * anisotropy modes because that would void selected mag filter + */ +# define R300_TX_MIN_FILTER_ANISO_NEAREST (0 << 13) +# define R300_TX_MIN_FILTER_ANISO_LINEAR (0 << 13) +# define R300_TX_MIN_FILTER_ANISO_NEAREST_MIP_NEAREST (1 << 13) +# define R300_TX_MIN_FILTER_ANISO_NEAREST_MIP_LINEAR (2 << 13) +# define R300_TX_MIN_FILTER_MASK ( (15 << 11) | (3 << 13) ) +# define R300_TX_MAX_ANISO_1_TO_1 (0 << 21) +# define R300_TX_MAX_ANISO_2_TO_1 (2 << 21) +# define R300_TX_MAX_ANISO_4_TO_1 (4 << 21) +# define R300_TX_MAX_ANISO_8_TO_1 (6 << 21) +# define R300_TX_MAX_ANISO_16_TO_1 (8 << 21) +# define R300_TX_MAX_ANISO_MASK (14 << 21) + +#define R300_TX_FILTER1_0 0x4440 +# define R300_CHROMA_KEY_MODE_DISABLE 0 +# define R300_CHROMA_KEY_FORCE 1 +# define R300_CHROMA_KEY_BLEND 2 +# define R300_MC_ROUND_NORMAL (0<<2) +# define R300_MC_ROUND_MPEG4 (1<<2) +# define R300_LOD_BIAS_MASK 0x1fff +# define R300_EDGE_ANISO_EDGE_DIAG (0<<13) +# define R300_EDGE_ANISO_EDGE_ONLY (1<<13) +# define R300_MC_COORD_TRUNCATE_DISABLE (0<<14) +# define R300_MC_COORD_TRUNCATE_MPEG (1<<14) +# define R300_TX_TRI_PERF_0_8 (0<<15) +# define R300_TX_TRI_PERF_1_8 (1<<15) +# define R300_TX_TRI_PERF_1_4 (2<<15) +# define R300_TX_TRI_PERF_3_8 (3<<15) +# define R300_ANISO_THRESHOLD_MASK (7<<17) + +#define R300_TX_SIZE_0 0x4480 +# define R300_TX_WIDTHMASK_SHIFT 0 +# define R300_TX_WIDTHMASK_MASK (2047 << 0) +# define R300_TX_HEIGHTMASK_SHIFT 11 +# define R300_TX_HEIGHTMASK_MASK (2047 << 11) +# define R300_TX_UNK23 (1 << 23) +# define R300_TX_MAX_MIP_LEVEL_SHIFT 26 +# define R300_TX_MAX_MIP_LEVEL_MASK (0xf << 26) +# define R300_TX_SIZE_PROJECTED (1<<30) +# define R300_TX_SIZE_TXPITCH_EN (1<<31) +#define R300_TX_FORMAT_0 0x44C0 + /* The interpretation of the format word by Wladimir van der Laan */ + /* The X, Y, Z and W refer to the layout of the components. + They are given meanings as R, G, B and Alpha by the swizzle + specification */ +# define R300_TX_FORMAT_X8 0x0 +# define R300_TX_FORMAT_X16 0x1 +# define R300_TX_FORMAT_Y4X4 0x2 +# define R300_TX_FORMAT_Y8X8 0x3 +# define R300_TX_FORMAT_Y16X16 0x4 +# define R300_TX_FORMAT_Z3Y3X2 0x5 +# define R300_TX_FORMAT_Z5Y6X5 0x6 +# define R300_TX_FORMAT_Z6Y5X5 0x7 +# define R300_TX_FORMAT_Z11Y11X10 0x8 +# define R300_TX_FORMAT_Z10Y11X11 0x9 +# define R300_TX_FORMAT_W4Z4Y4X4 0xA +# define R300_TX_FORMAT_W1Z5Y5X5 0xB +# define R300_TX_FORMAT_W8Z8Y8X8 0xC +# define R300_TX_FORMAT_W2Z10Y10X10 0xD +# define R300_TX_FORMAT_W16Z16Y16X16 0xE +# define R300_TX_FORMAT_DXT1 0xF +# define R300_TX_FORMAT_DXT3 0x10 +# define R300_TX_FORMAT_DXT5 0x11 +# define R300_TX_FORMAT_D3DMFT_CxV8U8 0x12 /* no swizzle */ +# define R300_TX_FORMAT_A8R8G8B8 0x13 /* no swizzle */ +# define R300_TX_FORMAT_B8G8_B8G8 0x14 /* no swizzle */ +# define R300_TX_FORMAT_G8R8_G8B8 0x15 /* no swizzle */ + /* 0x16 - some 16 bit green format.. ?? */ +# define R300_TX_FORMAT_UNK25 (1 << 25) /* no swizzle */ +# define R300_TX_FORMAT_CUBIC_MAP (1 << 26) + + /* gap */ + /* Floating point formats */ + /* Note - hardware supports both 16 and 32 bit floating point */ +# define R300_TX_FORMAT_FL_I16 0x18 +# define R300_TX_FORMAT_FL_I16A16 0x19 +# define R300_TX_FORMAT_FL_R16G16B16A16 0x1A +# define R300_TX_FORMAT_FL_I32 0x1B +# define R300_TX_FORMAT_FL_I32A32 0x1C +# define R300_TX_FORMAT_FL_R32G32B32A32 0x1D +# define R300_TX_FORMAT_ATI2N 0x1F + /* alpha modes, convenience mostly */ + /* if you have alpha, pick constant appropriate to the + number of channels (1 for I8, 2 for I8A8, 4 for R8G8B8A8, etc */ +# define R300_TX_FORMAT_ALPHA_1CH 0x000 +# define R300_TX_FORMAT_ALPHA_2CH 0x200 +# define R300_TX_FORMAT_ALPHA_4CH 0x600 +# define R300_TX_FORMAT_ALPHA_NONE 0xA00 + /* Swizzling */ + /* constants */ +# define R300_TX_FORMAT_X 0 +# define R300_TX_FORMAT_Y 1 +# define R300_TX_FORMAT_Z 2 +# define R300_TX_FORMAT_W 3 +# define R300_TX_FORMAT_ZERO 4 +# define R300_TX_FORMAT_ONE 5 + /* 2.0*Z, everything above 1.0 is set to 0.0 */ +# define R300_TX_FORMAT_CUT_Z 6 + /* 2.0*W, everything above 1.0 is set to 0.0 */ +# define R300_TX_FORMAT_CUT_W 7 + +# define R300_TX_FORMAT_B_SHIFT 18 +# define R300_TX_FORMAT_G_SHIFT 15 +# define R300_TX_FORMAT_R_SHIFT 12 +# define R300_TX_FORMAT_A_SHIFT 9 + /* Convenience macro to take care of layout and swizzling */ +# define R300_EASY_TX_FORMAT(B, G, R, A, FMT) ( \ + ((R300_TX_FORMAT_##B)< 0.5, return ARG0, else return ARG1 + * - CMP: If ARG2 < 0, return ARG1, else return ARG0 + * - FLR: use FRC+MAD + * - XPD: use MAD+MAD + * - SGE, SLT: use MAD+CMP + * - RSQ: use ABS modifier for argument + * - Use OUTC_REPL_ALPHA to write results of an alpha-only operation + * (e.g. RCP) into color register + * - apparently, there's no quick DST operation + * - fglrx set FPI2_UNKNOWN_31 on a "MAD fragment.color, tmp0, tmp1, tmp2" + * - fglrx set FPI2_UNKNOWN_31 on a "MAX r2, r1, c0" + * - fglrx once set FPI0_UNKNOWN_31 on a "FRC r1, r1" + * + * Operand selection + * First stage selects three sources from the available registers and + * constant parameters. This is defined in INSTR1 (color) and INSTR3 (alpha). + * fglrx sorts the three source fields: Registers before constants, + * lower indices before higher indices; I do not know whether this is + * necessary. + * + * fglrx fills unused sources with "read constant 0" + * According to specs, you cannot select more than two different constants. + * + * Second stage selects the operands from the sources. This is defined in + * INSTR0 (color) and INSTR2 (alpha). You can also select the special constants + * zero and one. + * Swizzling and negation happens in this stage, as well. + * + * Important: Color and alpha seem to be mostly separate, i.e. their sources + * selection appears to be fully independent (the register storage is probably + * physically split into a color and an alpha section). + * However (because of the apparent physical split), there is some interaction + * WRT swizzling. If, for example, you want to load an R component into an + * Alpha operand, this R component is taken from a *color* source, not from + * an alpha source. The corresponding register doesn't even have to appear in + * the alpha sources list. (I hope this all makes sense to you) + * + * Destination selection + * The destination register index is in FPI1 (color) and FPI3 (alpha) + * together with enable bits. + * There are separate enable bits for writing into temporary registers + * (DSTC_REG_* /DSTA_REG) and and program output registers (DSTC_OUTPUT_* + * /DSTA_OUTPUT). You can write to both at once, or not write at all (the + * same index must be used for both). + * + * Note: There is a special form for LRP + * - Argument order is the same as in ARB_fragment_program. + * - Operation is MAD + * - ARG1 is set to ARGC_SRC1C_LRP/ARGC_SRC1A_LRP + * - Set FPI0/FPI2_SPECIAL_LRP + * Arbitrary LRP (including support for swizzling) requires vanilla MAD+MAD + */ +#define R300_PFS_INSTR1_0 0x46C0 +# define R300_FPI1_SRC0C_SHIFT 0 +# define R300_FPI1_SRC0C_MASK (31 << 0) +# define R300_FPI1_SRC0C_CONST (1 << 5) +# define R300_FPI1_SRC1C_SHIFT 6 +# define R300_FPI1_SRC1C_MASK (31 << 6) +# define R300_FPI1_SRC1C_CONST (1 << 11) +# define R300_FPI1_SRC2C_SHIFT 12 +# define R300_FPI1_SRC2C_MASK (31 << 12) +# define R300_FPI1_SRC2C_CONST (1 << 17) +# define R300_FPI1_SRC_MASK 0x0003ffff +# define R300_FPI1_DSTC_SHIFT 18 +# define R300_FPI1_DSTC_MASK (31 << 18) +# define R300_FPI1_DSTC_REG_MASK_SHIFT 23 +# define R300_FPI1_DSTC_REG_X (1 << 23) +# define R300_FPI1_DSTC_REG_Y (1 << 24) +# define R300_FPI1_DSTC_REG_Z (1 << 25) +# define R300_FPI1_DSTC_OUTPUT_MASK_SHIFT 26 +# define R300_FPI1_DSTC_OUTPUT_X (1 << 26) +# define R300_FPI1_DSTC_OUTPUT_Y (1 << 27) +# define R300_FPI1_DSTC_OUTPUT_Z (1 << 28) + +#define R300_PFS_INSTR3_0 0x47C0 +# define R300_FPI3_SRC0A_SHIFT 0 +# define R300_FPI3_SRC0A_MASK (31 << 0) +# define R300_FPI3_SRC0A_CONST (1 << 5) +# define R300_FPI3_SRC1A_SHIFT 6 +# define R300_FPI3_SRC1A_MASK (31 << 6) +# define R300_FPI3_SRC1A_CONST (1 << 11) +# define R300_FPI3_SRC2A_SHIFT 12 +# define R300_FPI3_SRC2A_MASK (31 << 12) +# define R300_FPI3_SRC2A_CONST (1 << 17) +# define R300_FPI3_SRC_MASK 0x0003ffff +# define R300_FPI3_DSTA_SHIFT 18 +# define R300_FPI3_DSTA_MASK (31 << 18) +# define R300_FPI3_DSTA_REG (1 << 23) +# define R300_FPI3_DSTA_OUTPUT (1 << 24) +# define R300_FPI3_DSTA_DEPTH (1 << 27) + +#define R300_PFS_INSTR0_0 0x48C0 +# define R300_FPI0_ARGC_SRC0C_XYZ 0 +# define R300_FPI0_ARGC_SRC0C_XXX 1 +# define R300_FPI0_ARGC_SRC0C_YYY 2 +# define R300_FPI0_ARGC_SRC0C_ZZZ 3 +# define R300_FPI0_ARGC_SRC1C_XYZ 4 +# define R300_FPI0_ARGC_SRC1C_XXX 5 +# define R300_FPI0_ARGC_SRC1C_YYY 6 +# define R300_FPI0_ARGC_SRC1C_ZZZ 7 +# define R300_FPI0_ARGC_SRC2C_XYZ 8 +# define R300_FPI0_ARGC_SRC2C_XXX 9 +# define R300_FPI0_ARGC_SRC2C_YYY 10 +# define R300_FPI0_ARGC_SRC2C_ZZZ 11 +# define R300_FPI0_ARGC_SRC0A 12 +# define R300_FPI0_ARGC_SRC1A 13 +# define R300_FPI0_ARGC_SRC2A 14 +# define R300_FPI0_ARGC_SRC1C_LRP 15 +# define R300_FPI0_ARGC_ZERO 20 +# define R300_FPI0_ARGC_ONE 21 + /* GUESS */ +# define R300_FPI0_ARGC_HALF 22 +# define R300_FPI0_ARGC_SRC0C_YZX 23 +# define R300_FPI0_ARGC_SRC1C_YZX 24 +# define R300_FPI0_ARGC_SRC2C_YZX 25 +# define R300_FPI0_ARGC_SRC0C_ZXY 26 +# define R300_FPI0_ARGC_SRC1C_ZXY 27 +# define R300_FPI0_ARGC_SRC2C_ZXY 28 +# define R300_FPI0_ARGC_SRC0CA_WZY 29 +# define R300_FPI0_ARGC_SRC1CA_WZY 30 +# define R300_FPI0_ARGC_SRC2CA_WZY 31 + +# define R300_FPI0_ARG0C_SHIFT 0 +# define R300_FPI0_ARG0C_MASK (31 << 0) +# define R300_FPI0_ARG0C_NEG (1 << 5) +# define R300_FPI0_ARG0C_ABS (1 << 6) +# define R300_FPI0_ARG1C_SHIFT 7 +# define R300_FPI0_ARG1C_MASK (31 << 7) +# define R300_FPI0_ARG1C_NEG (1 << 12) +# define R300_FPI0_ARG1C_ABS (1 << 13) +# define R300_FPI0_ARG2C_SHIFT 14 +# define R300_FPI0_ARG2C_MASK (31 << 14) +# define R300_FPI0_ARG2C_NEG (1 << 19) +# define R300_FPI0_ARG2C_ABS (1 << 20) +# define R300_FPI0_SPECIAL_LRP (1 << 21) +# define R300_FPI0_OUTC_MAD (0 << 23) +# define R300_FPI0_OUTC_DP3 (1 << 23) +# define R300_FPI0_OUTC_DP4 (2 << 23) +# define R300_FPI0_OUTC_MIN (4 << 23) +# define R300_FPI0_OUTC_MAX (5 << 23) +# define R300_FPI0_OUTC_CMPH (7 << 23) +# define R300_FPI0_OUTC_CMP (8 << 23) +# define R300_FPI0_OUTC_FRC (9 << 23) +# define R300_FPI0_OUTC_REPL_ALPHA (10 << 23) +# define R300_FPI0_OUTC_SAT (1 << 30) +# define R300_FPI0_INSERT_NOP (1 << 31) + +#define R300_PFS_INSTR2_0 0x49C0 +# define R300_FPI2_ARGA_SRC0C_X 0 +# define R300_FPI2_ARGA_SRC0C_Y 1 +# define R300_FPI2_ARGA_SRC0C_Z 2 +# define R300_FPI2_ARGA_SRC1C_X 3 +# define R300_FPI2_ARGA_SRC1C_Y 4 +# define R300_FPI2_ARGA_SRC1C_Z 5 +# define R300_FPI2_ARGA_SRC2C_X 6 +# define R300_FPI2_ARGA_SRC2C_Y 7 +# define R300_FPI2_ARGA_SRC2C_Z 8 +# define R300_FPI2_ARGA_SRC0A 9 +# define R300_FPI2_ARGA_SRC1A 10 +# define R300_FPI2_ARGA_SRC2A 11 +# define R300_FPI2_ARGA_SRC1A_LRP 15 +# define R300_FPI2_ARGA_ZERO 16 +# define R300_FPI2_ARGA_ONE 17 + /* GUESS */ +# define R300_FPI2_ARGA_HALF 18 +# define R300_FPI2_ARG0A_SHIFT 0 +# define R300_FPI2_ARG0A_MASK (31 << 0) +# define R300_FPI2_ARG0A_NEG (1 << 5) + /* GUESS */ +# define R300_FPI2_ARG0A_ABS (1 << 6) +# define R300_FPI2_ARG1A_SHIFT 7 +# define R300_FPI2_ARG1A_MASK (31 << 7) +# define R300_FPI2_ARG1A_NEG (1 << 12) + /* GUESS */ +# define R300_FPI2_ARG1A_ABS (1 << 13) +# define R300_FPI2_ARG2A_SHIFT 14 +# define R300_FPI2_ARG2A_MASK (31 << 14) +# define R300_FPI2_ARG2A_NEG (1 << 19) + /* GUESS */ +# define R300_FPI2_ARG2A_ABS (1 << 20) +# define R300_FPI2_SPECIAL_LRP (1 << 21) +# define R300_FPI2_OUTA_MAD (0 << 23) +# define R300_FPI2_OUTA_DP4 (1 << 23) +# define R300_FPI2_OUTA_MIN (2 << 23) +# define R300_FPI2_OUTA_MAX (3 << 23) +# define R300_FPI2_OUTA_CMP (6 << 23) +# define R300_FPI2_OUTA_FRC (7 << 23) +# define R300_FPI2_OUTA_EX2 (8 << 23) +# define R300_FPI2_OUTA_LG2 (9 << 23) +# define R300_FPI2_OUTA_RCP (10 << 23) +# define R300_FPI2_OUTA_RSQ (11 << 23) +# define R300_FPI2_OUTA_SAT (1 << 30) +# define R300_FPI2_UNKNOWN_31 (1 << 31) +/* END: Fragment program instruction set */ + +/* Fog state and color */ +#define R300_RE_FOG_STATE 0x4BC0 +# define R300_FOG_ENABLE (1 << 0) +# define R300_FOG_MODE_LINEAR (0 << 1) +# define R300_FOG_MODE_EXP (1 << 1) +# define R300_FOG_MODE_EXP2 (2 << 1) +# define R300_FOG_MODE_MASK (3 << 1) +#define R300_FOG_COLOR_R 0x4BC8 +#define R300_FOG_COLOR_G 0x4BCC +#define R300_FOG_COLOR_B 0x4BD0 + +#define R300_PP_ALPHA_TEST 0x4BD4 +# define R300_REF_ALPHA_MASK 0x000000ff +# define R300_ALPHA_TEST_FAIL (0 << 8) +# define R300_ALPHA_TEST_LESS (1 << 8) +# define R300_ALPHA_TEST_LEQUAL (3 << 8) +# define R300_ALPHA_TEST_EQUAL (2 << 8) +# define R300_ALPHA_TEST_GEQUAL (6 << 8) +# define R300_ALPHA_TEST_GREATER (4 << 8) +# define R300_ALPHA_TEST_NEQUAL (5 << 8) +# define R300_ALPHA_TEST_PASS (7 << 8) +# define R300_ALPHA_TEST_OP_MASK (7 << 8) +# define R300_ALPHA_TEST_ENABLE (1 << 11) + +/* gap */ + +/* Fragment program parameters in 7.16 floating point */ +#define R300_PFS_PARAM_0_X 0x4C00 +#define R300_PFS_PARAM_0_Y 0x4C04 +#define R300_PFS_PARAM_0_Z 0x4C08 +#define R300_PFS_PARAM_0_W 0x4C0C +/* GUESS: PARAM_31 is last, based on native limits reported by fglrx */ +#define R300_PFS_PARAM_31_X 0x4DF0 +#define R300_PFS_PARAM_31_Y 0x4DF4 +#define R300_PFS_PARAM_31_Z 0x4DF8 +#define R300_PFS_PARAM_31_W 0x4DFC + +/* Notes: + * - AFAIK fglrx always sets BLEND_UNKNOWN when blending is used in + * the application + * - AFAIK fglrx always sets BLEND_NO_SEPARATE when CBLEND and ABLEND + * are set to the same + * function (both registers are always set up completely in any case) + * - Most blend flags are simply copied from R200 and not tested yet + */ +#define R300_RB3D_CBLEND 0x4E04 +#define R300_RB3D_ABLEND 0x4E08 +/* the following only appear in CBLEND */ +# define R300_BLEND_ENABLE (1 << 0) +# define R300_BLEND_UNKNOWN (3 << 1) +# define R300_BLEND_NO_SEPARATE (1 << 3) +/* the following are shared between CBLEND and ABLEND */ +# define R300_FCN_MASK (3 << 12) +# define R300_COMB_FCN_ADD_CLAMP (0 << 12) +# define R300_COMB_FCN_ADD_NOCLAMP (1 << 12) +# define R300_COMB_FCN_SUB_CLAMP (2 << 12) +# define R300_COMB_FCN_SUB_NOCLAMP (3 << 12) +# define R300_COMB_FCN_MIN (4 << 12) +# define R300_COMB_FCN_MAX (5 << 12) +# define R300_COMB_FCN_RSUB_CLAMP (6 << 12) +# define R300_COMB_FCN_RSUB_NOCLAMP (7 << 12) +# define R300_BLEND_GL_ZERO (32) +# define R300_BLEND_GL_ONE (33) +# define R300_BLEND_GL_SRC_COLOR (34) +# define R300_BLEND_GL_ONE_MINUS_SRC_COLOR (35) +# define R300_BLEND_GL_DST_COLOR (36) +# define R300_BLEND_GL_ONE_MINUS_DST_COLOR (37) +# define R300_BLEND_GL_SRC_ALPHA (38) +# define R300_BLEND_GL_ONE_MINUS_SRC_ALPHA (39) +# define R300_BLEND_GL_DST_ALPHA (40) +# define R300_BLEND_GL_ONE_MINUS_DST_ALPHA (41) +# define R300_BLEND_GL_SRC_ALPHA_SATURATE (42) +# define R300_BLEND_GL_CONST_COLOR (43) +# define R300_BLEND_GL_ONE_MINUS_CONST_COLOR (44) +# define R300_BLEND_GL_CONST_ALPHA (45) +# define R300_BLEND_GL_ONE_MINUS_CONST_ALPHA (46) +# define R300_BLEND_MASK (63) +# define R300_SRC_BLEND_SHIFT (16) +# define R300_DST_BLEND_SHIFT (24) +#define R300_RB3D_BLEND_COLOR 0x4E10 +#define R300_RB3D_COLORMASK 0x4E0C +# define R300_COLORMASK0_B (1<<0) +# define R300_COLORMASK0_G (1<<1) +# define R300_COLORMASK0_R (1<<2) +# define R300_COLORMASK0_A (1<<3) + +/* gap */ + +#define R300_RB3D_COLOROFFSET0 0x4E28 +# define R300_COLOROFFSET_MASK 0xFFFFFFF0 /* GUESS */ +#define R300_RB3D_COLOROFFSET1 0x4E2C /* GUESS */ +#define R300_RB3D_COLOROFFSET2 0x4E30 /* GUESS */ +#define R300_RB3D_COLOROFFSET3 0x4E34 /* GUESS */ + +/* gap */ + +/* Bit 16: Larger tiles + * Bit 17: 4x2 tiles + * Bit 18: Extremely weird tile like, but some pixels duplicated? + */ +#define R300_RB3D_COLORPITCH0 0x4E38 +# define R300_COLORPITCH_MASK 0x00001FF8 /* GUESS */ +# define R300_COLOR_TILE_ENABLE (1 << 16) /* GUESS */ +# define R300_COLOR_MICROTILE_ENABLE (1 << 17) /* GUESS */ +# define R300_COLOR_MICROTILE_SQUARE_ENABLE (2 << 17) +# define R300_COLOR_ENDIAN_NO_SWAP (0 << 18) /* GUESS */ +# define R300_COLOR_ENDIAN_WORD_SWAP (1 << 18) /* GUESS */ +# define R300_COLOR_ENDIAN_DWORD_SWAP (2 << 18) /* GUESS */ +# define R300_COLOR_FORMAT_RGB565 (2 << 22) +# define R300_COLOR_FORMAT_ARGB8888 (3 << 22) +#define R300_RB3D_COLORPITCH1 0x4E3C /* GUESS */ +#define R300_RB3D_COLORPITCH2 0x4E40 /* GUESS */ +#define R300_RB3D_COLORPITCH3 0x4E44 /* GUESS */ + +#define R300_RB3D_AARESOLVE_OFFSET 0x4E80 +#define R300_RB3D_AARESOLVE_PITCH 0x4E84 +#define R300_RB3D_AARESOLVE_CTL 0x4E88 +/* gap */ + +/* Guess by Vladimir. + * Set to 0A before 3D operations, set to 02 afterwards. + */ +/*#define R300_RB3D_DSTCACHE_CTLSTAT 0x4E4C*/ +# define R300_RB3D_DSTCACHE_UNKNOWN_02 0x00000002 +# define R300_RB3D_DSTCACHE_UNKNOWN_0A 0x0000000A + +/* gap */ +/* There seems to be no "write only" setting, so use Z-test = ALWAYS + * for this. + * Bit (1<<8) is the "test" bit. so plain write is 6 - vd + */ +#define R300_ZB_CNTL 0x4F00 +# define R300_STENCIL_ENABLE (1 << 0) +# define R300_Z_ENABLE (1 << 1) +# define R300_Z_WRITE_ENABLE (1 << 2) +# define R300_Z_SIGNED_COMPARE (1 << 3) +# define R300_STENCIL_FRONT_BACK (1 << 4) + +#define R300_ZB_ZSTENCILCNTL 0x4f04 + /* functions */ +# define R300_ZS_NEVER 0 +# define R300_ZS_LESS 1 +# define R300_ZS_LEQUAL 2 +# define R300_ZS_EQUAL 3 +# define R300_ZS_GEQUAL 4 +# define R300_ZS_GREATER 5 +# define R300_ZS_NOTEQUAL 6 +# define R300_ZS_ALWAYS 7 +# define R300_ZS_MASK 7 + /* operations */ +# define R300_ZS_KEEP 0 +# define R300_ZS_ZERO 1 +# define R300_ZS_REPLACE 2 +# define R300_ZS_INCR 3 +# define R300_ZS_DECR 4 +# define R300_ZS_INVERT 5 +# define R300_ZS_INCR_WRAP 6 +# define R300_ZS_DECR_WRAP 7 +# define R300_Z_FUNC_SHIFT 0 + /* front and back refer to operations done for front + and back faces, i.e. separate stencil function support */ +# define R300_S_FRONT_FUNC_SHIFT 3 +# define R300_S_FRONT_SFAIL_OP_SHIFT 6 +# define R300_S_FRONT_ZPASS_OP_SHIFT 9 +# define R300_S_FRONT_ZFAIL_OP_SHIFT 12 +# define R300_S_BACK_FUNC_SHIFT 15 +# define R300_S_BACK_SFAIL_OP_SHIFT 18 +# define R300_S_BACK_ZPASS_OP_SHIFT 21 +# define R300_S_BACK_ZFAIL_OP_SHIFT 24 + +#define R300_ZB_STENCILREFMASK 0x4f08 +# define R300_STENCILREF_SHIFT 0 +# define R300_STENCILREF_MASK 0x000000ff +# define R300_STENCILMASK_SHIFT 8 +# define R300_STENCILMASK_MASK 0x0000ff00 +# define R300_STENCILWRITEMASK_SHIFT 16 +# define R300_STENCILWRITEMASK_MASK 0x00ff0000 + +/* gap */ + +#define R300_ZB_FORMAT 0x4f10 +# define R300_DEPTHFORMAT_16BIT_INT_Z (0 << 0) +# define R300_DEPTHFORMAT_16BIT_13E3 (1 << 0) +# define R300_DEPTHFORMAT_24BIT_INT_Z_8BIT_STENCIL (2 << 0) +/* reserved up to (15 << 0) */ +# define R300_INVERT_13E3_LEADING_ONES (0 << 4) +# define R300_INVERT_13E3_LEADING_ZEROS (1 << 4) + +#define R300_ZB_ZTOP 0x4F14 +# define R300_ZTOP_DISABLE (0 << 0) +# define R300_ZTOP_ENABLE (1 << 0) + +/* gap */ + +#define R300_ZB_ZCACHE_CTLSTAT 0x4f18 +# define R300_ZB_ZCACHE_CTLSTAT_ZC_FLUSH_NO_EFFECT (0 << 0) +# define R300_ZB_ZCACHE_CTLSTAT_ZC_FLUSH_FLUSH_AND_FREE (1 << 0) +# define R300_ZB_ZCACHE_CTLSTAT_ZC_FREE_NO_EFFECT (0 << 1) +# define R300_ZB_ZCACHE_CTLSTAT_ZC_FREE_FREE (1 << 1) +# define R300_ZB_ZCACHE_CTLSTAT_ZC_BUSY_IDLE (0 << 31) +# define R300_ZB_ZCACHE_CTLSTAT_ZC_BUSY_BUSY (1 << 31) + +#define R300_ZB_BW_CNTL 0x4f1c +# define R300_HIZ_DISABLE (0 << 0) +# define R300_HIZ_ENABLE (1 << 0) +# define R300_HIZ_MIN (0 << 1) +# define R300_HIZ_MAX (1 << 1) +# define R300_FAST_FILL_DISABLE (0 << 2) +# define R300_FAST_FILL_ENABLE (1 << 2) +# define R300_RD_COMP_DISABLE (0 << 3) +# define R300_RD_COMP_ENABLE (1 << 3) +# define R300_WR_COMP_DISABLE (0 << 4) +# define R300_WR_COMP_ENABLE (1 << 4) +# define R300_ZB_CB_CLEAR_RMW (0 << 5) +# define R300_ZB_CB_CLEAR_CACHE_LINEAR (1 << 5) +# define R300_FORCE_COMPRESSED_STENCIL_VALUE_DISABLE (0 << 6) +# define R300_FORCE_COMPRESSED_STENCIL_VALUE_ENABLE (1 << 6) + +# define R500_ZEQUAL_OPTIMIZE_ENABLE (0 << 7) +# define R500_ZEQUAL_OPTIMIZE_DISABLE (1 << 7) +# define R500_SEQUAL_OPTIMIZE_ENABLE (0 << 8) +# define R500_SEQUAL_OPTIMIZE_DISABLE (1 << 8) + +# define R500_BMASK_ENABLE (0 << 10) +# define R500_BMASK_DISABLE (1 << 10) +# define R500_HIZ_EQUAL_REJECT_DISABLE (0 << 11) +# define R500_HIZ_EQUAL_REJECT_ENABLE (1 << 11) +# define R500_HIZ_FP_EXP_BITS_DISABLE (0 << 12) +# define R500_HIZ_FP_EXP_BITS_1 (1 << 12) +# define R500_HIZ_FP_EXP_BITS_2 (2 << 12) +# define R500_HIZ_FP_EXP_BITS_3 (3 << 12) +# define R500_HIZ_FP_EXP_BITS_4 (4 << 12) +# define R500_HIZ_FP_EXP_BITS_5 (5 << 12) +# define R500_HIZ_FP_INVERT_LEADING_ONES (0 << 15) +# define R500_HIZ_FP_INVERT_LEADING_ZEROS (1 << 15) +# define R500_TILE_OVERWRITE_RECOMPRESSION_ENABLE (0 << 16) +# define R500_TILE_OVERWRITE_RECOMPRESSION_DISABLE (1 << 16) +# define R500_CONTIGUOUS_6XAA_SAMPLES_ENABLE (0 << 17) +# define R500_CONTIGUOUS_6XAA_SAMPLES_DISABLE (1 << 17) +# define R500_PEQ_PACKING_DISABLE (0 << 18) +# define R500_PEQ_PACKING_ENABLE (1 << 18) +# define R500_COVERED_PTR_MASKING_DISABLE (0 << 18) +# define R500_COVERED_PTR_MASKING_ENABLE (1 << 18) + + +/* gap */ + +/* Z Buffer Address Offset. + * Bits 31 to 5 are used for aligned Z buffer address offset for macro tiles. + */ +#define R300_ZB_DEPTHOFFSET 0x4f20 + +/* Z Buffer Pitch and Endian Control */ +#define R300_ZB_DEPTHPITCH 0x4f24 +# define R300_DEPTHPITCH_MASK 0x00003FFC +# define R300_DEPTHMACROTILE_DISABLE (0 << 16) +# define R300_DEPTHMACROTILE_ENABLE (1 << 16) +# define R300_DEPTHMICROTILE_LINEAR (0 << 17) +# define R300_DEPTHMICROTILE_TILED (1 << 17) +# define R300_DEPTHMICROTILE_TILED_SQUARE (2 << 17) +# define R300_DEPTHENDIAN_NO_SWAP (0 << 18) +# define R300_DEPTHENDIAN_WORD_SWAP (1 << 18) +# define R300_DEPTHENDIAN_DWORD_SWAP (2 << 18) +# define R300_DEPTHENDIAN_HALF_DWORD_SWAP (3 << 18) + +/* Z Buffer Clear Value */ +#define R300_ZB_DEPTHCLEARVALUE 0x4f28 + +#define R300_ZB_ZMASK_OFFSET 0x4f30 +#define R300_ZB_ZMASK_PITCH 0x4f34 +#define R300_ZB_ZMASK_WRINDEX 0x4f38 +#define R300_ZB_ZMASK_DWORD 0x4f3c +#define R300_ZB_ZMASK_RDINDEX 0x4f40 + +/* Hierarchical Z Memory Offset */ +#define R300_ZB_HIZ_OFFSET 0x4f44 + +/* Hierarchical Z Write Index */ +#define R300_ZB_HIZ_WRINDEX 0x4f48 + +/* Hierarchical Z Data */ +#define R300_ZB_HIZ_DWORD 0x4f4c + +/* Hierarchical Z Read Index */ +#define R300_ZB_HIZ_RDINDEX 0x4f50 + +/* Hierarchical Z Pitch */ +#define R300_ZB_HIZ_PITCH 0x4f54 + +/* Z Buffer Z Pass Counter Data */ +#define R300_ZB_ZPASS_DATA 0x4f58 + +/* Z Buffer Z Pass Counter Address */ +#define R300_ZB_ZPASS_ADDR 0x4f5c + +/* Depth buffer X and Y coordinate offset */ +#define R300_ZB_DEPTHXY_OFFSET 0x4f60 +# define R300_DEPTHX_OFFSET_SHIFT 1 +# define R300_DEPTHX_OFFSET_MASK 0x000007FE +# define R300_DEPTHY_OFFSET_SHIFT 17 +# define R300_DEPTHY_OFFSET_MASK 0x07FE0000 + +/* Sets the fifo sizes */ +#define R500_ZB_FIFO_SIZE 0x4fd0 +# define R500_OP_FIFO_SIZE_FULL (0 << 0) +# define R500_OP_FIFO_SIZE_HALF (1 << 0) +# define R500_OP_FIFO_SIZE_QUATER (2 << 0) +# define R500_OP_FIFO_SIZE_EIGTHS (4 << 0) + +/* Stencil Reference Value and Mask for backfacing quads */ +/* R300_ZB_STENCILREFMASK handles front face */ +#define R500_ZB_STENCILREFMASK_BF 0x4fd4 +# define R500_STENCILREF_SHIFT 0 +# define R500_STENCILREF_MASK 0x000000ff +# define R500_STENCILMASK_SHIFT 8 +# define R500_STENCILMASK_MASK 0x0000ff00 +# define R500_STENCILWRITEMASK_SHIFT 16 +# define R500_STENCILWRITEMASK_MASK 0x00ff0000 + +/* BEGIN: Vertex program instruction set */ + +/* Every instruction is four dwords long: + * DWORD 0: output and opcode + * DWORD 1: first argument + * DWORD 2: second argument + * DWORD 3: third argument + * + * Notes: + * - ABS r, a is implemented as MAX r, a, -a + * - MOV is implemented as ADD to zero + * - XPD is implemented as MUL + MAD + * - FLR is implemented as FRC + ADD + * - apparently, fglrx tries to schedule instructions so that there is at + * least one instruction between the write to a temporary and the first + * read from said temporary; however, violations of this scheduling are + * allowed + * - register indices seem to be unrelated with OpenGL aliasing to + * conventional state + * - only one attribute and one parameter can be loaded at a time; however, + * the same attribute/parameter can be used for more than one argument + * - the second software argument for POW is the third hardware argument + * (no idea why) + * - MAD with only temporaries as input seems to use VPI_OUT_SELECT_MAD_2 + * + * There is some magic surrounding LIT: + * The single argument is replicated across all three inputs, but swizzled: + * First argument: xyzy + * Second argument: xyzx + * Third argument: xyzw + * Whenever the result is used later in the fragment program, fglrx forces + * x and w to be 1.0 in the input selection; I don't know whether this is + * strictly necessary + */ +#define R300_VPI_OUT_OP_DOT (1 << 0) +#define R300_VPI_OUT_OP_MUL (2 << 0) +#define R300_VPI_OUT_OP_ADD (3 << 0) +#define R300_VPI_OUT_OP_MAD (4 << 0) +#define R300_VPI_OUT_OP_DST (5 << 0) +#define R300_VPI_OUT_OP_FRC (6 << 0) +#define R300_VPI_OUT_OP_MAX (7 << 0) +#define R300_VPI_OUT_OP_MIN (8 << 0) +#define R300_VPI_OUT_OP_SGE (9 << 0) +#define R300_VPI_OUT_OP_SLT (10 << 0) + /* Used in GL_POINT_DISTANCE_ATTENUATION_ARB, vector(scalar, vector) */ +#define R300_VPI_OUT_OP_UNK12 (12 << 0) +#define R300_VPI_OUT_OP_ARL (13 << 0) +#define R300_VPI_OUT_OP_EXP (65 << 0) +#define R300_VPI_OUT_OP_LOG (66 << 0) + /* Used in fog computations, scalar(scalar) */ +#define R300_VPI_OUT_OP_UNK67 (67 << 0) +#define R300_VPI_OUT_OP_LIT (68 << 0) +#define R300_VPI_OUT_OP_POW (69 << 0) +#define R300_VPI_OUT_OP_RCP (70 << 0) +#define R300_VPI_OUT_OP_RSQ (72 << 0) + /* Used in GL_POINT_DISTANCE_ATTENUATION_ARB, scalar(scalar) */ +#define R300_VPI_OUT_OP_UNK73 (73 << 0) +#define R300_VPI_OUT_OP_EX2 (75 << 0) +#define R300_VPI_OUT_OP_LG2 (76 << 0) +#define R300_VPI_OUT_OP_MAD_2 (128 << 0) + /* all temps, vector(scalar, vector, vector) */ +#define R300_VPI_OUT_OP_UNK129 (129 << 0) + +#define R300_VPI_OUT_REG_CLASS_TEMPORARY (0 << 8) +#define R300_VPI_OUT_REG_CLASS_ADDR (1 << 8) +#define R300_VPI_OUT_REG_CLASS_RESULT (2 << 8) +#define R300_VPI_OUT_REG_CLASS_MASK (31 << 8) + +#define R300_VPI_OUT_REG_INDEX_SHIFT 13 + /* GUESS based on fglrx native limits */ +#define R300_VPI_OUT_REG_INDEX_MASK (31 << 13) + +#define R300_VPI_OUT_WRITE_X (1 << 20) +#define R300_VPI_OUT_WRITE_Y (1 << 21) +#define R300_VPI_OUT_WRITE_Z (1 << 22) +#define R300_VPI_OUT_WRITE_W (1 << 23) + +#define R300_VPI_IN_REG_CLASS_TEMPORARY (0 << 0) +#define R300_VPI_IN_REG_CLASS_ATTRIBUTE (1 << 0) +#define R300_VPI_IN_REG_CLASS_PARAMETER (2 << 0) +#define R300_VPI_IN_REG_CLASS_NONE (9 << 0) +#define R300_VPI_IN_REG_CLASS_MASK (31 << 0) + +#define R300_VPI_IN_REG_INDEX_SHIFT 5 + /* GUESS based on fglrx native limits */ +#define R300_VPI_IN_REG_INDEX_MASK (255 << 5) + +/* The R300 can select components from the input register arbitrarily. + * Use the following constants, shifted by the component shift you + * want to select + */ +#define R300_VPI_IN_SELECT_X 0 +#define R300_VPI_IN_SELECT_Y 1 +#define R300_VPI_IN_SELECT_Z 2 +#define R300_VPI_IN_SELECT_W 3 +#define R300_VPI_IN_SELECT_ZERO 4 +#define R300_VPI_IN_SELECT_ONE 5 +#define R300_VPI_IN_SELECT_MASK 7 + +#define R300_VPI_IN_X_SHIFT 13 +#define R300_VPI_IN_Y_SHIFT 16 +#define R300_VPI_IN_Z_SHIFT 19 +#define R300_VPI_IN_W_SHIFT 22 + +#define R300_VPI_IN_NEG_X (1 << 25) +#define R300_VPI_IN_NEG_Y (1 << 26) +#define R300_VPI_IN_NEG_Z (1 << 27) +#define R300_VPI_IN_NEG_W (1 << 28) +/* END: Vertex program instruction set */ + +/* BEGIN: Packet 3 commands */ + +/* A primitive emission dword. */ +#define R300_PRIM_TYPE_NONE (0 << 0) +#define R300_PRIM_TYPE_POINT (1 << 0) +#define R300_PRIM_TYPE_LINE (2 << 0) +#define R300_PRIM_TYPE_LINE_STRIP (3 << 0) +#define R300_PRIM_TYPE_TRI_LIST (4 << 0) +#define R300_PRIM_TYPE_TRI_FAN (5 << 0) +#define R300_PRIM_TYPE_TRI_STRIP (6 << 0) +#define R300_PRIM_TYPE_TRI_TYPE2 (7 << 0) +#define R300_PRIM_TYPE_RECT_LIST (8 << 0) +#define R300_PRIM_TYPE_3VRT_POINT_LIST (9 << 0) +#define R300_PRIM_TYPE_3VRT_LINE_LIST (10 << 0) + /* GUESS (based on r200) */ +#define R300_PRIM_TYPE_POINT_SPRITES (11 << 0) +#define R300_PRIM_TYPE_LINE_LOOP (12 << 0) +#define R300_PRIM_TYPE_QUADS (13 << 0) +#define R300_PRIM_TYPE_QUAD_STRIP (14 << 0) +#define R300_PRIM_TYPE_POLYGON (15 << 0) +#define R300_PRIM_TYPE_MASK 0xF +#define R300_PRIM_WALK_IND (1 << 4) +#define R300_PRIM_WALK_LIST (2 << 4) +#define R300_PRIM_WALK_RING (3 << 4) +#define R300_PRIM_WALK_MASK (3 << 4) + /* GUESS (based on r200) */ +#define R300_PRIM_COLOR_ORDER_BGRA (0 << 6) +#define R300_PRIM_COLOR_ORDER_RGBA (1 << 6) +#define R300_PRIM_NUM_VERTICES_SHIFT 16 +#define R300_PRIM_NUM_VERTICES_MASK 0xffff + +/* Draw a primitive from vertex data in arrays loaded via 3D_LOAD_VBPNTR. + * Two parameter dwords: + * 0. The first parameter appears to be always 0 + * 1. The second parameter is a standard primitive emission dword. + */ +#define R300_PACKET3_3D_DRAW_VBUF 0x00002800 + +/* Specify the full set of vertex arrays as (address, stride). + * The first parameter is the number of vertex arrays specified. + * The rest of the command is a variable length list of blocks, where + * each block is three dwords long and specifies two arrays. + * The first dword of a block is split into two words, the lower significant + * word refers to the first array, the more significant word to the second + * array in the block. + * The low byte of each word contains the size of an array entry in dwords, + * the high byte contains the stride of the array. + * The second dword of a block contains the pointer to the first array, + * the third dword of a block contains the pointer to the second array. + * Note that if the total number of arrays is odd, the third dword of + * the last block is omitted. + */ +#define R300_PACKET3_3D_LOAD_VBPNTR 0x00002F00 + +#define R300_PACKET3_INDX_BUFFER 0x00003300 +# define R300_EB_UNK1_SHIFT 24 +# define R300_EB_UNK1 (0x80<<24) +# define R300_EB_UNK2 0x0810 +#define R300_PACKET3_3D_DRAW_VBUF_2 0x00003400 +#define R300_PACKET3_3D_DRAW_INDX_2 0x00003600 + +/* END: Packet 3 commands */ + + +/* Color formats for 2d packets + */ +#define R300_CP_COLOR_FORMAT_CI8 2 +#define R300_CP_COLOR_FORMAT_ARGB1555 3 +#define R300_CP_COLOR_FORMAT_RGB565 4 +#define R300_CP_COLOR_FORMAT_ARGB8888 6 +#define R300_CP_COLOR_FORMAT_RGB332 7 +#define R300_CP_COLOR_FORMAT_RGB8 9 +#define R300_CP_COLOR_FORMAT_ARGB4444 15 + +/* + * CP type-3 packets + */ +#define R300_CP_CMD_BITBLT_MULTI 0xC0009B00 + +#define R500_VAP_INDEX_OFFSET 0x208c + +#define R500_GA_US_VECTOR_INDEX 0x4250 +#define R500_GA_US_VECTOR_DATA 0x4254 + +#define R500_RS_IP_0 0x4074 +#define R500_RS_INST_0 0x4320 + +#define R500_US_CONFIG 0x4600 + +#define R500_US_FC_CTRL 0x4624 +#define R500_US_CODE_ADDR 0x4630 + +#define R500_RB3D_COLOR_CLEAR_VALUE_AR 0x46c0 +#define R500_RB3D_CONSTANT_COLOR_AR 0x4ef8 + +#define R300_SU_REG_DEST 0x42c8 +#define RV530_FG_ZBREG_DEST 0x4be8 +#define R300_ZB_ZPASS_DATA 0x4f58 +#define R300_ZB_ZPASS_ADDR 0x4f5c + +#endif /* _R300_REG_H */ diff --git a/sys/dev/drm2/radeon/r300_reg_safe.h b/sys/dev/drm2/radeon/r300_reg_safe.h new file mode 100644 index 00000000000..0b5e1eb3b30 --- /dev/null +++ b/sys/dev/drm2/radeon/r300_reg_safe.h @@ -0,0 +1,45 @@ +#include +__FBSDID("$FreeBSD$"); + +static const unsigned r300_reg_safe_bm[159] = { + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0x17FF1FFF, 0xFFFFFFFC, 0xFFFFFFFF, 0xFF30FFBF, + 0xFFFFFFF8, 0xC3E6FFFF, 0xFFFFF6DF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFF03F, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFEFCE, 0xF00EBFFF, 0x007C0000, + 0xF0000078, 0xFF000009, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFF7FF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFC48, 0xFFFFFFFF, 0xFFFFFFFE, 0xFFFFFFFF, + 0x38FF8F50, 0xFFF88082, 0xF000000C, 0xFAE00BFF, + 0x0000FFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, + 0x00000000, 0x0000C100, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0xFFFF0000, 0xFFFFFFFF, 0xFF80FFFF, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x0003FC0B, 0xFFFFFCFF, 0xFFBFFB99, +}; diff --git a/sys/dev/drm2/radeon/r300d.h b/sys/dev/drm2/radeon/r300d.h new file mode 100644 index 00000000000..76511a91df4 --- /dev/null +++ b/sys/dev/drm2/radeon/r300d.h @@ -0,0 +1,357 @@ +/* + * Copyright 2008 Advanced Micro Devices, Inc. + * Copyright 2008 Red Hat Inc. + * Copyright 2009 Jerome Glisse. + * + * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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. + * + * Authors: Dave Airlie + * Alex Deucher + * Jerome Glisse + */ +#ifndef __R300D_H__ +#define __R300D_H__ + +#include +__FBSDID("$FreeBSD$"); + +#define CP_PACKET0 0x00000000 +#define PACKET0_BASE_INDEX_SHIFT 0 +#define PACKET0_BASE_INDEX_MASK (0x1ffff << 0) +#define PACKET0_COUNT_SHIFT 16 +#define PACKET0_COUNT_MASK (0x3fff << 16) +#define CP_PACKET1 0x40000000 +#define CP_PACKET2 0x80000000 +#define PACKET2_PAD_SHIFT 0 +#define PACKET2_PAD_MASK (0x3fffffff << 0) +#define CP_PACKET3 0xC0000000 +#define PACKET3_IT_OPCODE_SHIFT 8 +#define PACKET3_IT_OPCODE_MASK (0xff << 8) +#define PACKET3_COUNT_SHIFT 16 +#define PACKET3_COUNT_MASK (0x3fff << 16) +/* PACKET3 op code */ +#define PACKET3_NOP 0x10 +#define PACKET3_3D_DRAW_VBUF 0x28 +#define PACKET3_3D_DRAW_IMMD 0x29 +#define PACKET3_3D_DRAW_INDX 0x2A +#define PACKET3_3D_LOAD_VBPNTR 0x2F +#define PACKET3_3D_CLEAR_ZMASK 0x32 +#define PACKET3_INDX_BUFFER 0x33 +#define PACKET3_3D_DRAW_VBUF_2 0x34 +#define PACKET3_3D_DRAW_IMMD_2 0x35 +#define PACKET3_3D_DRAW_INDX_2 0x36 +#define PACKET3_3D_CLEAR_HIZ 0x37 +#define PACKET3_3D_CLEAR_CMASK 0x38 +#define PACKET3_BITBLT_MULTI 0x9B + +#define PACKET0(reg, n) (CP_PACKET0 | \ + REG_SET(PACKET0_BASE_INDEX, (reg) >> 2) | \ + REG_SET(PACKET0_COUNT, (n))) +#define PACKET2(v) (CP_PACKET2 | REG_SET(PACKET2_PAD, (v))) +#define PACKET3(op, n) (CP_PACKET3 | \ + REG_SET(PACKET3_IT_OPCODE, (op)) | \ + REG_SET(PACKET3_COUNT, (n))) + +#define PACKET_TYPE0 0 +#define PACKET_TYPE1 1 +#define PACKET_TYPE2 2 +#define PACKET_TYPE3 3 + +#define CP_PACKET_GET_TYPE(h) (((h) >> 30) & 3) +#define CP_PACKET_GET_COUNT(h) (((h) >> 16) & 0x3FFF) +#define CP_PACKET0_GET_REG(h) (((h) & 0x1FFF) << 2) +#define CP_PACKET0_GET_ONE_REG_WR(h) (((h) >> 15) & 1) +#define CP_PACKET3_GET_OPCODE(h) (((h) >> 8) & 0xFF) + +/* Registers */ +#define R_000148_MC_FB_LOCATION 0x000148 +#define S_000148_MC_FB_START(x) (((x) & 0xFFFF) << 0) +#define G_000148_MC_FB_START(x) (((x) >> 0) & 0xFFFF) +#define C_000148_MC_FB_START 0xFFFF0000 +#define S_000148_MC_FB_TOP(x) (((x) & 0xFFFF) << 16) +#define G_000148_MC_FB_TOP(x) (((x) >> 16) & 0xFFFF) +#define C_000148_MC_FB_TOP 0x0000FFFF +#define R_00014C_MC_AGP_LOCATION 0x00014C +#define S_00014C_MC_AGP_START(x) (((x) & 0xFFFF) << 0) +#define G_00014C_MC_AGP_START(x) (((x) >> 0) & 0xFFFF) +#define C_00014C_MC_AGP_START 0xFFFF0000 +#define S_00014C_MC_AGP_TOP(x) (((x) & 0xFFFF) << 16) +#define G_00014C_MC_AGP_TOP(x) (((x) >> 16) & 0xFFFF) +#define C_00014C_MC_AGP_TOP 0x0000FFFF +#define R_00015C_AGP_BASE_2 0x00015C +#define S_00015C_AGP_BASE_ADDR_2(x) (((x) & 0xF) << 0) +#define G_00015C_AGP_BASE_ADDR_2(x) (((x) >> 0) & 0xF) +#define C_00015C_AGP_BASE_ADDR_2 0xFFFFFFF0 +#define R_000170_AGP_BASE 0x000170 +#define S_000170_AGP_BASE_ADDR(x) (((x) & 0xFFFFFFFF) << 0) +#define G_000170_AGP_BASE_ADDR(x) (((x) >> 0) & 0xFFFFFFFF) +#define C_000170_AGP_BASE_ADDR 0x00000000 +#define R_0007C0_CP_STAT 0x0007C0 +#define S_0007C0_MRU_BUSY(x) (((x) & 0x1) << 0) +#define G_0007C0_MRU_BUSY(x) (((x) >> 0) & 0x1) +#define C_0007C0_MRU_BUSY 0xFFFFFFFE +#define S_0007C0_MWU_BUSY(x) (((x) & 0x1) << 1) +#define G_0007C0_MWU_BUSY(x) (((x) >> 1) & 0x1) +#define C_0007C0_MWU_BUSY 0xFFFFFFFD +#define S_0007C0_RSIU_BUSY(x) (((x) & 0x1) << 2) +#define G_0007C0_RSIU_BUSY(x) (((x) >> 2) & 0x1) +#define C_0007C0_RSIU_BUSY 0xFFFFFFFB +#define S_0007C0_RCIU_BUSY(x) (((x) & 0x1) << 3) +#define G_0007C0_RCIU_BUSY(x) (((x) >> 3) & 0x1) +#define C_0007C0_RCIU_BUSY 0xFFFFFFF7 +#define S_0007C0_CSF_PRIMARY_BUSY(x) (((x) & 0x1) << 9) +#define G_0007C0_CSF_PRIMARY_BUSY(x) (((x) >> 9) & 0x1) +#define C_0007C0_CSF_PRIMARY_BUSY 0xFFFFFDFF +#define S_0007C0_CSF_INDIRECT_BUSY(x) (((x) & 0x1) << 10) +#define G_0007C0_CSF_INDIRECT_BUSY(x) (((x) >> 10) & 0x1) +#define C_0007C0_CSF_INDIRECT_BUSY 0xFFFFFBFF +#define S_0007C0_CSQ_PRIMARY_BUSY(x) (((x) & 0x1) << 11) +#define G_0007C0_CSQ_PRIMARY_BUSY(x) (((x) >> 11) & 0x1) +#define C_0007C0_CSQ_PRIMARY_BUSY 0xFFFFF7FF +#define S_0007C0_CSQ_INDIRECT_BUSY(x) (((x) & 0x1) << 12) +#define G_0007C0_CSQ_INDIRECT_BUSY(x) (((x) >> 12) & 0x1) +#define C_0007C0_CSQ_INDIRECT_BUSY 0xFFFFEFFF +#define S_0007C0_CSI_BUSY(x) (((x) & 0x1) << 13) +#define G_0007C0_CSI_BUSY(x) (((x) >> 13) & 0x1) +#define C_0007C0_CSI_BUSY 0xFFFFDFFF +#define S_0007C0_CSF_INDIRECT2_BUSY(x) (((x) & 0x1) << 14) +#define G_0007C0_CSF_INDIRECT2_BUSY(x) (((x) >> 14) & 0x1) +#define C_0007C0_CSF_INDIRECT2_BUSY 0xFFFFBFFF +#define S_0007C0_CSQ_INDIRECT2_BUSY(x) (((x) & 0x1) << 15) +#define G_0007C0_CSQ_INDIRECT2_BUSY(x) (((x) >> 15) & 0x1) +#define C_0007C0_CSQ_INDIRECT2_BUSY 0xFFFF7FFF +#define S_0007C0_GUIDMA_BUSY(x) (((x) & 0x1) << 28) +#define G_0007C0_GUIDMA_BUSY(x) (((x) >> 28) & 0x1) +#define C_0007C0_GUIDMA_BUSY 0xEFFFFFFF +#define S_0007C0_VIDDMA_BUSY(x) (((x) & 0x1) << 29) +#define G_0007C0_VIDDMA_BUSY(x) (((x) >> 29) & 0x1) +#define C_0007C0_VIDDMA_BUSY 0xDFFFFFFF +#define S_0007C0_CMDSTRM_BUSY(x) (((x) & 0x1) << 30) +#define G_0007C0_CMDSTRM_BUSY(x) (((x) >> 30) & 0x1) +#define C_0007C0_CMDSTRM_BUSY 0xBFFFFFFF +#define S_0007C0_CP_BUSY(x) (((x) & 0x1) << 31) +#define G_0007C0_CP_BUSY(x) (((x) >> 31) & 0x1) +#define C_0007C0_CP_BUSY 0x7FFFFFFF +#define R_000E40_RBBM_STATUS 0x000E40 +#define S_000E40_CMDFIFO_AVAIL(x) (((x) & 0x7F) << 0) +#define G_000E40_CMDFIFO_AVAIL(x) (((x) >> 0) & 0x7F) +#define C_000E40_CMDFIFO_AVAIL 0xFFFFFF80 +#define S_000E40_HIRQ_ON_RBB(x) (((x) & 0x1) << 8) +#define G_000E40_HIRQ_ON_RBB(x) (((x) >> 8) & 0x1) +#define C_000E40_HIRQ_ON_RBB 0xFFFFFEFF +#define S_000E40_CPRQ_ON_RBB(x) (((x) & 0x1) << 9) +#define G_000E40_CPRQ_ON_RBB(x) (((x) >> 9) & 0x1) +#define C_000E40_CPRQ_ON_RBB 0xFFFFFDFF +#define S_000E40_CFRQ_ON_RBB(x) (((x) & 0x1) << 10) +#define G_000E40_CFRQ_ON_RBB(x) (((x) >> 10) & 0x1) +#define C_000E40_CFRQ_ON_RBB 0xFFFFFBFF +#define S_000E40_HIRQ_IN_RTBUF(x) (((x) & 0x1) << 11) +#define G_000E40_HIRQ_IN_RTBUF(x) (((x) >> 11) & 0x1) +#define C_000E40_HIRQ_IN_RTBUF 0xFFFFF7FF +#define S_000E40_CPRQ_IN_RTBUF(x) (((x) & 0x1) << 12) +#define G_000E40_CPRQ_IN_RTBUF(x) (((x) >> 12) & 0x1) +#define C_000E40_CPRQ_IN_RTBUF 0xFFFFEFFF +#define S_000E40_CFRQ_IN_RTBUF(x) (((x) & 0x1) << 13) +#define G_000E40_CFRQ_IN_RTBUF(x) (((x) >> 13) & 0x1) +#define C_000E40_CFRQ_IN_RTBUF 0xFFFFDFFF +#define S_000E40_CF_PIPE_BUSY(x) (((x) & 0x1) << 14) +#define G_000E40_CF_PIPE_BUSY(x) (((x) >> 14) & 0x1) +#define C_000E40_CF_PIPE_BUSY 0xFFFFBFFF +#define S_000E40_ENG_EV_BUSY(x) (((x) & 0x1) << 15) +#define G_000E40_ENG_EV_BUSY(x) (((x) >> 15) & 0x1) +#define C_000E40_ENG_EV_BUSY 0xFFFF7FFF +#define S_000E40_CP_CMDSTRM_BUSY(x) (((x) & 0x1) << 16) +#define G_000E40_CP_CMDSTRM_BUSY(x) (((x) >> 16) & 0x1) +#define C_000E40_CP_CMDSTRM_BUSY 0xFFFEFFFF +#define S_000E40_E2_BUSY(x) (((x) & 0x1) << 17) +#define G_000E40_E2_BUSY(x) (((x) >> 17) & 0x1) +#define C_000E40_E2_BUSY 0xFFFDFFFF +#define S_000E40_RB2D_BUSY(x) (((x) & 0x1) << 18) +#define G_000E40_RB2D_BUSY(x) (((x) >> 18) & 0x1) +#define C_000E40_RB2D_BUSY 0xFFFBFFFF +#define S_000E40_RB3D_BUSY(x) (((x) & 0x1) << 19) +#define G_000E40_RB3D_BUSY(x) (((x) >> 19) & 0x1) +#define C_000E40_RB3D_BUSY 0xFFF7FFFF +#define S_000E40_VAP_BUSY(x) (((x) & 0x1) << 20) +#define G_000E40_VAP_BUSY(x) (((x) >> 20) & 0x1) +#define C_000E40_VAP_BUSY 0xFFEFFFFF +#define S_000E40_RE_BUSY(x) (((x) & 0x1) << 21) +#define G_000E40_RE_BUSY(x) (((x) >> 21) & 0x1) +#define C_000E40_RE_BUSY 0xFFDFFFFF +#define S_000E40_TAM_BUSY(x) (((x) & 0x1) << 22) +#define G_000E40_TAM_BUSY(x) (((x) >> 22) & 0x1) +#define C_000E40_TAM_BUSY 0xFFBFFFFF +#define S_000E40_TDM_BUSY(x) (((x) & 0x1) << 23) +#define G_000E40_TDM_BUSY(x) (((x) >> 23) & 0x1) +#define C_000E40_TDM_BUSY 0xFF7FFFFF +#define S_000E40_PB_BUSY(x) (((x) & 0x1) << 24) +#define G_000E40_PB_BUSY(x) (((x) >> 24) & 0x1) +#define C_000E40_PB_BUSY 0xFEFFFFFF +#define S_000E40_TIM_BUSY(x) (((x) & 0x1) << 25) +#define G_000E40_TIM_BUSY(x) (((x) >> 25) & 0x1) +#define C_000E40_TIM_BUSY 0xFDFFFFFF +#define S_000E40_GA_BUSY(x) (((x) & 0x1) << 26) +#define G_000E40_GA_BUSY(x) (((x) >> 26) & 0x1) +#define C_000E40_GA_BUSY 0xFBFFFFFF +#define S_000E40_CBA2D_BUSY(x) (((x) & 0x1) << 27) +#define G_000E40_CBA2D_BUSY(x) (((x) >> 27) & 0x1) +#define C_000E40_CBA2D_BUSY 0xF7FFFFFF +#define S_000E40_GUI_ACTIVE(x) (((x) & 0x1) << 31) +#define G_000E40_GUI_ACTIVE(x) (((x) >> 31) & 0x1) +#define C_000E40_GUI_ACTIVE 0x7FFFFFFF +#define R_0000F0_RBBM_SOFT_RESET 0x0000F0 +#define S_0000F0_SOFT_RESET_CP(x) (((x) & 0x1) << 0) +#define G_0000F0_SOFT_RESET_CP(x) (((x) >> 0) & 0x1) +#define C_0000F0_SOFT_RESET_CP 0xFFFFFFFE +#define S_0000F0_SOFT_RESET_HI(x) (((x) & 0x1) << 1) +#define G_0000F0_SOFT_RESET_HI(x) (((x) >> 1) & 0x1) +#define C_0000F0_SOFT_RESET_HI 0xFFFFFFFD +#define S_0000F0_SOFT_RESET_VAP(x) (((x) & 0x1) << 2) +#define G_0000F0_SOFT_RESET_VAP(x) (((x) >> 2) & 0x1) +#define C_0000F0_SOFT_RESET_VAP 0xFFFFFFFB +#define S_0000F0_SOFT_RESET_RE(x) (((x) & 0x1) << 3) +#define G_0000F0_SOFT_RESET_RE(x) (((x) >> 3) & 0x1) +#define C_0000F0_SOFT_RESET_RE 0xFFFFFFF7 +#define S_0000F0_SOFT_RESET_PP(x) (((x) & 0x1) << 4) +#define G_0000F0_SOFT_RESET_PP(x) (((x) >> 4) & 0x1) +#define C_0000F0_SOFT_RESET_PP 0xFFFFFFEF +#define S_0000F0_SOFT_RESET_E2(x) (((x) & 0x1) << 5) +#define G_0000F0_SOFT_RESET_E2(x) (((x) >> 5) & 0x1) +#define C_0000F0_SOFT_RESET_E2 0xFFFFFFDF +#define S_0000F0_SOFT_RESET_RB(x) (((x) & 0x1) << 6) +#define G_0000F0_SOFT_RESET_RB(x) (((x) >> 6) & 0x1) +#define C_0000F0_SOFT_RESET_RB 0xFFFFFFBF +#define S_0000F0_SOFT_RESET_HDP(x) (((x) & 0x1) << 7) +#define G_0000F0_SOFT_RESET_HDP(x) (((x) >> 7) & 0x1) +#define C_0000F0_SOFT_RESET_HDP 0xFFFFFF7F +#define S_0000F0_SOFT_RESET_MC(x) (((x) & 0x1) << 8) +#define G_0000F0_SOFT_RESET_MC(x) (((x) >> 8) & 0x1) +#define C_0000F0_SOFT_RESET_MC 0xFFFFFEFF +#define S_0000F0_SOFT_RESET_AIC(x) (((x) & 0x1) << 9) +#define G_0000F0_SOFT_RESET_AIC(x) (((x) >> 9) & 0x1) +#define C_0000F0_SOFT_RESET_AIC 0xFFFFFDFF +#define S_0000F0_SOFT_RESET_VIP(x) (((x) & 0x1) << 10) +#define G_0000F0_SOFT_RESET_VIP(x) (((x) >> 10) & 0x1) +#define C_0000F0_SOFT_RESET_VIP 0xFFFFFBFF +#define S_0000F0_SOFT_RESET_DISP(x) (((x) & 0x1) << 11) +#define G_0000F0_SOFT_RESET_DISP(x) (((x) >> 11) & 0x1) +#define C_0000F0_SOFT_RESET_DISP 0xFFFFF7FF +#define S_0000F0_SOFT_RESET_CG(x) (((x) & 0x1) << 12) +#define G_0000F0_SOFT_RESET_CG(x) (((x) >> 12) & 0x1) +#define C_0000F0_SOFT_RESET_CG 0xFFFFEFFF +#define S_0000F0_SOFT_RESET_GA(x) (((x) & 0x1) << 13) +#define G_0000F0_SOFT_RESET_GA(x) (((x) >> 13) & 0x1) +#define C_0000F0_SOFT_RESET_GA 0xFFFFDFFF +#define S_0000F0_SOFT_RESET_IDCT(x) (((x) & 0x1) << 14) +#define G_0000F0_SOFT_RESET_IDCT(x) (((x) >> 14) & 0x1) +#define C_0000F0_SOFT_RESET_IDCT 0xFFFFBFFF + +#define R_00000D_SCLK_CNTL 0x00000D +#define S_00000D_SCLK_SRC_SEL(x) (((x) & 0x7) << 0) +#define G_00000D_SCLK_SRC_SEL(x) (((x) >> 0) & 0x7) +#define C_00000D_SCLK_SRC_SEL 0xFFFFFFF8 +#define S_00000D_CP_MAX_DYN_STOP_LAT(x) (((x) & 0x1) << 3) +#define G_00000D_CP_MAX_DYN_STOP_LAT(x) (((x) >> 3) & 0x1) +#define C_00000D_CP_MAX_DYN_STOP_LAT 0xFFFFFFF7 +#define S_00000D_HDP_MAX_DYN_STOP_LAT(x) (((x) & 0x1) << 4) +#define G_00000D_HDP_MAX_DYN_STOP_LAT(x) (((x) >> 4) & 0x1) +#define C_00000D_HDP_MAX_DYN_STOP_LAT 0xFFFFFFEF +#define S_00000D_TV_MAX_DYN_STOP_LAT(x) (((x) & 0x1) << 5) +#define G_00000D_TV_MAX_DYN_STOP_LAT(x) (((x) >> 5) & 0x1) +#define C_00000D_TV_MAX_DYN_STOP_LAT 0xFFFFFFDF +#define S_00000D_E2_MAX_DYN_STOP_LAT(x) (((x) & 0x1) << 6) +#define G_00000D_E2_MAX_DYN_STOP_LAT(x) (((x) >> 6) & 0x1) +#define C_00000D_E2_MAX_DYN_STOP_LAT 0xFFFFFFBF +#define S_00000D_SE_MAX_DYN_STOP_LAT(x) (((x) & 0x1) << 7) +#define G_00000D_SE_MAX_DYN_STOP_LAT(x) (((x) >> 7) & 0x1) +#define C_00000D_SE_MAX_DYN_STOP_LAT 0xFFFFFF7F +#define S_00000D_IDCT_MAX_DYN_STOP_LAT(x) (((x) & 0x1) << 8) +#define G_00000D_IDCT_MAX_DYN_STOP_LAT(x) (((x) >> 8) & 0x1) +#define C_00000D_IDCT_MAX_DYN_STOP_LAT 0xFFFFFEFF +#define S_00000D_VIP_MAX_DYN_STOP_LAT(x) (((x) & 0x1) << 9) +#define G_00000D_VIP_MAX_DYN_STOP_LAT(x) (((x) >> 9) & 0x1) +#define C_00000D_VIP_MAX_DYN_STOP_LAT 0xFFFFFDFF +#define S_00000D_RE_MAX_DYN_STOP_LAT(x) (((x) & 0x1) << 10) +#define G_00000D_RE_MAX_DYN_STOP_LAT(x) (((x) >> 10) & 0x1) +#define C_00000D_RE_MAX_DYN_STOP_LAT 0xFFFFFBFF +#define S_00000D_PB_MAX_DYN_STOP_LAT(x) (((x) & 0x1) << 11) +#define G_00000D_PB_MAX_DYN_STOP_LAT(x) (((x) >> 11) & 0x1) +#define C_00000D_PB_MAX_DYN_STOP_LAT 0xFFFFF7FF +#define S_00000D_TAM_MAX_DYN_STOP_LAT(x) (((x) & 0x1) << 12) +#define G_00000D_TAM_MAX_DYN_STOP_LAT(x) (((x) >> 12) & 0x1) +#define C_00000D_TAM_MAX_DYN_STOP_LAT 0xFFFFEFFF +#define S_00000D_TDM_MAX_DYN_STOP_LAT(x) (((x) & 0x1) << 13) +#define G_00000D_TDM_MAX_DYN_STOP_LAT(x) (((x) >> 13) & 0x1) +#define C_00000D_TDM_MAX_DYN_STOP_LAT 0xFFFFDFFF +#define S_00000D_RB_MAX_DYN_STOP_LAT(x) (((x) & 0x1) << 14) +#define G_00000D_RB_MAX_DYN_STOP_LAT(x) (((x) >> 14) & 0x1) +#define C_00000D_RB_MAX_DYN_STOP_LAT 0xFFFFBFFF +#define S_00000D_FORCE_DISP2(x) (((x) & 0x1) << 15) +#define G_00000D_FORCE_DISP2(x) (((x) >> 15) & 0x1) +#define C_00000D_FORCE_DISP2 0xFFFF7FFF +#define S_00000D_FORCE_CP(x) (((x) & 0x1) << 16) +#define G_00000D_FORCE_CP(x) (((x) >> 16) & 0x1) +#define C_00000D_FORCE_CP 0xFFFEFFFF +#define S_00000D_FORCE_HDP(x) (((x) & 0x1) << 17) +#define G_00000D_FORCE_HDP(x) (((x) >> 17) & 0x1) +#define C_00000D_FORCE_HDP 0xFFFDFFFF +#define S_00000D_FORCE_DISP1(x) (((x) & 0x1) << 18) +#define G_00000D_FORCE_DISP1(x) (((x) >> 18) & 0x1) +#define C_00000D_FORCE_DISP1 0xFFFBFFFF +#define S_00000D_FORCE_TOP(x) (((x) & 0x1) << 19) +#define G_00000D_FORCE_TOP(x) (((x) >> 19) & 0x1) +#define C_00000D_FORCE_TOP 0xFFF7FFFF +#define S_00000D_FORCE_E2(x) (((x) & 0x1) << 20) +#define G_00000D_FORCE_E2(x) (((x) >> 20) & 0x1) +#define C_00000D_FORCE_E2 0xFFEFFFFF +#define S_00000D_FORCE_SE(x) (((x) & 0x1) << 21) +#define G_00000D_FORCE_SE(x) (((x) >> 21) & 0x1) +#define C_00000D_FORCE_SE 0xFFDFFFFF +#define S_00000D_FORCE_IDCT(x) (((x) & 0x1) << 22) +#define G_00000D_FORCE_IDCT(x) (((x) >> 22) & 0x1) +#define C_00000D_FORCE_IDCT 0xFFBFFFFF +#define S_00000D_FORCE_VIP(x) (((x) & 0x1) << 23) +#define G_00000D_FORCE_VIP(x) (((x) >> 23) & 0x1) +#define C_00000D_FORCE_VIP 0xFF7FFFFF +#define S_00000D_FORCE_RE(x) (((x) & 0x1) << 24) +#define G_00000D_FORCE_RE(x) (((x) >> 24) & 0x1) +#define C_00000D_FORCE_RE 0xFEFFFFFF +#define S_00000D_FORCE_PB(x) (((x) & 0x1) << 25) +#define G_00000D_FORCE_PB(x) (((x) >> 25) & 0x1) +#define C_00000D_FORCE_PB 0xFDFFFFFF +#define S_00000D_FORCE_TAM(x) (((x) & 0x1) << 26) +#define G_00000D_FORCE_TAM(x) (((x) >> 26) & 0x1) +#define C_00000D_FORCE_TAM 0xFBFFFFFF +#define S_00000D_FORCE_TDM(x) (((x) & 0x1) << 27) +#define G_00000D_FORCE_TDM(x) (((x) >> 27) & 0x1) +#define C_00000D_FORCE_TDM 0xF7FFFFFF +#define S_00000D_FORCE_RB(x) (((x) & 0x1) << 28) +#define G_00000D_FORCE_RB(x) (((x) >> 28) & 0x1) +#define C_00000D_FORCE_RB 0xEFFFFFFF +#define S_00000D_FORCE_TV_SCLK(x) (((x) & 0x1) << 29) +#define G_00000D_FORCE_TV_SCLK(x) (((x) >> 29) & 0x1) +#define C_00000D_FORCE_TV_SCLK 0xDFFFFFFF +#define S_00000D_FORCE_SUBPIC(x) (((x) & 0x1) << 30) +#define G_00000D_FORCE_SUBPIC(x) (((x) >> 30) & 0x1) +#define C_00000D_FORCE_SUBPIC 0xBFFFFFFF +#define S_00000D_FORCE_OV0(x) (((x) & 0x1) << 31) +#define G_00000D_FORCE_OV0(x) (((x) >> 31) & 0x1) +#define C_00000D_FORCE_OV0 0x7FFFFFFF + +#endif diff --git a/sys/dev/drm2/radeon/r420.c b/sys/dev/drm2/radeon/r420.c new file mode 100644 index 00000000000..e422f9db6bc --- /dev/null +++ b/sys/dev/drm2/radeon/r420.c @@ -0,0 +1,491 @@ +/* + * Copyright 2008 Advanced Micro Devices, Inc. + * Copyright 2008 Red Hat Inc. + * Copyright 2009 Jerome Glisse. + * + * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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. + * + * Authors: Dave Airlie + * Alex Deucher + * Jerome Glisse + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include "radeon_reg.h" +#include "radeon.h" +#include "radeon_asic.h" +#include "atom.h" +#include "r100d.h" +#include "r420d.h" +#include "r420_reg_safe.h" + +void r420_pm_init_profile(struct radeon_device *rdev) +{ + /* default */ + rdev->pm.profiles[PM_PROFILE_DEFAULT_IDX].dpms_off_ps_idx = rdev->pm.default_power_state_index; + rdev->pm.profiles[PM_PROFILE_DEFAULT_IDX].dpms_on_ps_idx = rdev->pm.default_power_state_index; + rdev->pm.profiles[PM_PROFILE_DEFAULT_IDX].dpms_off_cm_idx = 0; + rdev->pm.profiles[PM_PROFILE_DEFAULT_IDX].dpms_on_cm_idx = 0; + /* low sh */ + rdev->pm.profiles[PM_PROFILE_LOW_SH_IDX].dpms_off_ps_idx = 0; + rdev->pm.profiles[PM_PROFILE_LOW_SH_IDX].dpms_on_ps_idx = 0; + rdev->pm.profiles[PM_PROFILE_LOW_SH_IDX].dpms_off_cm_idx = 0; + rdev->pm.profiles[PM_PROFILE_LOW_SH_IDX].dpms_on_cm_idx = 0; + /* mid sh */ + rdev->pm.profiles[PM_PROFILE_MID_SH_IDX].dpms_off_ps_idx = 0; + rdev->pm.profiles[PM_PROFILE_MID_SH_IDX].dpms_on_ps_idx = 1; + rdev->pm.profiles[PM_PROFILE_MID_SH_IDX].dpms_off_cm_idx = 0; + rdev->pm.profiles[PM_PROFILE_MID_SH_IDX].dpms_on_cm_idx = 0; + /* high sh */ + rdev->pm.profiles[PM_PROFILE_HIGH_SH_IDX].dpms_off_ps_idx = 0; + rdev->pm.profiles[PM_PROFILE_HIGH_SH_IDX].dpms_on_ps_idx = rdev->pm.default_power_state_index; + rdev->pm.profiles[PM_PROFILE_HIGH_SH_IDX].dpms_off_cm_idx = 0; + rdev->pm.profiles[PM_PROFILE_HIGH_SH_IDX].dpms_on_cm_idx = 0; + /* low mh */ + rdev->pm.profiles[PM_PROFILE_LOW_MH_IDX].dpms_off_ps_idx = 0; + rdev->pm.profiles[PM_PROFILE_LOW_MH_IDX].dpms_on_ps_idx = rdev->pm.default_power_state_index; + rdev->pm.profiles[PM_PROFILE_LOW_MH_IDX].dpms_off_cm_idx = 0; + rdev->pm.profiles[PM_PROFILE_LOW_MH_IDX].dpms_on_cm_idx = 0; + /* mid mh */ + rdev->pm.profiles[PM_PROFILE_MID_MH_IDX].dpms_off_ps_idx = 0; + rdev->pm.profiles[PM_PROFILE_MID_MH_IDX].dpms_on_ps_idx = rdev->pm.default_power_state_index; + rdev->pm.profiles[PM_PROFILE_MID_MH_IDX].dpms_off_cm_idx = 0; + rdev->pm.profiles[PM_PROFILE_MID_MH_IDX].dpms_on_cm_idx = 0; + /* high mh */ + rdev->pm.profiles[PM_PROFILE_HIGH_MH_IDX].dpms_off_ps_idx = 0; + rdev->pm.profiles[PM_PROFILE_HIGH_MH_IDX].dpms_on_ps_idx = rdev->pm.default_power_state_index; + rdev->pm.profiles[PM_PROFILE_HIGH_MH_IDX].dpms_off_cm_idx = 0; + rdev->pm.profiles[PM_PROFILE_HIGH_MH_IDX].dpms_on_cm_idx = 0; +} + +static void r420_set_reg_safe(struct radeon_device *rdev) +{ + rdev->config.r300.reg_safe_bm = r420_reg_safe_bm; + rdev->config.r300.reg_safe_bm_size = DRM_ARRAY_SIZE(r420_reg_safe_bm); +} + +void r420_pipes_init(struct radeon_device *rdev) +{ + unsigned tmp; + unsigned gb_pipe_select; + unsigned num_pipes; + + /* GA_ENHANCE workaround TCL deadlock issue */ + WREG32(R300_GA_ENHANCE, R300_GA_DEADLOCK_CNTL | R300_GA_FASTSYNC_CNTL | + (1 << 2) | (1 << 3)); + /* add idle wait as per freedesktop.org bug 24041 */ + if (r100_gui_wait_for_idle(rdev)) { + DRM_ERROR("Failed to wait GUI idle while " + "programming pipes. Bad things might happen.\n"); + } + /* get max number of pipes */ + gb_pipe_select = RREG32(R400_GB_PIPE_SELECT); + num_pipes = ((gb_pipe_select >> 12) & 3) + 1; + + /* SE chips have 1 pipe */ + if ((rdev->ddev->pci_device == 0x5e4c) || + (rdev->ddev->pci_device == 0x5e4f)) + num_pipes = 1; + + rdev->num_gb_pipes = num_pipes; + tmp = 0; + switch (num_pipes) { + default: + /* force to 1 pipe */ + num_pipes = 1; + case 1: + tmp = (0 << 1); + break; + case 2: + tmp = (3 << 1); + break; + case 3: + tmp = (6 << 1); + break; + case 4: + tmp = (7 << 1); + break; + } + WREG32(R500_SU_REG_DEST, (1 << num_pipes) - 1); + /* Sub pixel 1/12 so we can have 4K rendering according to doc */ + tmp |= R300_TILE_SIZE_16 | R300_ENABLE_TILING; + WREG32(R300_GB_TILE_CONFIG, tmp); + if (r100_gui_wait_for_idle(rdev)) { + DRM_ERROR("Failed to wait GUI idle while " + "programming pipes. Bad things might happen.\n"); + } + + tmp = RREG32(R300_DST_PIPE_CONFIG); + WREG32(R300_DST_PIPE_CONFIG, tmp | R300_PIPE_AUTO_CONFIG); + + WREG32(R300_RB2D_DSTCACHE_MODE, + RREG32(R300_RB2D_DSTCACHE_MODE) | + R300_DC_AUTOFLUSH_ENABLE | + R300_DC_DC_DISABLE_IGNORE_PE); + + if (r100_gui_wait_for_idle(rdev)) { + DRM_ERROR("Failed to wait GUI idle while " + "programming pipes. Bad things might happen.\n"); + } + + if (rdev->family == CHIP_RV530) { + tmp = RREG32(RV530_GB_PIPE_SELECT2); + if ((tmp & 3) == 3) + rdev->num_z_pipes = 2; + else + rdev->num_z_pipes = 1; + } else + rdev->num_z_pipes = 1; + + DRM_INFO("radeon: %d quad pipes, %d z pipes initialized.\n", + rdev->num_gb_pipes, rdev->num_z_pipes); +} + +u32 r420_mc_rreg(struct radeon_device *rdev, u32 reg) +{ + u32 r; + + WREG32(R_0001F8_MC_IND_INDEX, S_0001F8_MC_IND_ADDR(reg)); + r = RREG32(R_0001FC_MC_IND_DATA); + return r; +} + +void r420_mc_wreg(struct radeon_device *rdev, u32 reg, u32 v) +{ + WREG32(R_0001F8_MC_IND_INDEX, S_0001F8_MC_IND_ADDR(reg) | + S_0001F8_MC_IND_WR_EN(1)); + WREG32(R_0001FC_MC_IND_DATA, v); +} + +static void r420_debugfs(struct radeon_device *rdev) +{ + if (r100_debugfs_rbbm_init(rdev)) { + DRM_ERROR("Failed to register debugfs file for RBBM !\n"); + } + if (r420_debugfs_pipes_info_init(rdev)) { + DRM_ERROR("Failed to register debugfs file for pipes !\n"); + } +} + +static void r420_clock_resume(struct radeon_device *rdev) +{ + u32 sclk_cntl; + + if (radeon_dynclks != -1 && radeon_dynclks) + radeon_atom_set_clock_gating(rdev, 1); + sclk_cntl = RREG32_PLL(R_00000D_SCLK_CNTL); + sclk_cntl |= S_00000D_FORCE_CP(1) | S_00000D_FORCE_VIP(1); + if (rdev->family == CHIP_R420) + sclk_cntl |= S_00000D_FORCE_PX(1) | S_00000D_FORCE_TX(1); + WREG32_PLL(R_00000D_SCLK_CNTL, sclk_cntl); +} + +static void r420_cp_errata_init(struct radeon_device *rdev) +{ + struct radeon_ring *ring = &rdev->ring[RADEON_RING_TYPE_GFX_INDEX]; + + /* RV410 and R420 can lock up if CP DMA to host memory happens + * while the 2D engine is busy. + * + * The proper workaround is to queue a RESYNC at the beginning + * of the CP init, apparently. + */ + radeon_scratch_get(rdev, &rdev->config.r300.resync_scratch); + radeon_ring_lock(rdev, ring, 8); + radeon_ring_write(ring, PACKET0(R300_CP_RESYNC_ADDR, 1)); + radeon_ring_write(ring, rdev->config.r300.resync_scratch); + radeon_ring_write(ring, 0xDEADBEEF); + radeon_ring_unlock_commit(rdev, ring); +} + +static void r420_cp_errata_fini(struct radeon_device *rdev) +{ + struct radeon_ring *ring = &rdev->ring[RADEON_RING_TYPE_GFX_INDEX]; + + /* Catch the RESYNC we dispatched all the way back, + * at the very beginning of the CP init. + */ + radeon_ring_lock(rdev, ring, 8); + radeon_ring_write(ring, PACKET0(R300_RB3D_DSTCACHE_CTLSTAT, 0)); + radeon_ring_write(ring, R300_RB3D_DC_FINISH); + radeon_ring_unlock_commit(rdev, ring); + radeon_scratch_free(rdev, rdev->config.r300.resync_scratch); +} + +static int r420_startup(struct radeon_device *rdev) +{ + int r; + + /* set common regs */ + r100_set_common_regs(rdev); + /* program mc */ + r300_mc_program(rdev); + /* Resume clock */ + r420_clock_resume(rdev); + /* Initialize GART (initialize after TTM so we can allocate + * memory through TTM but finalize after TTM) */ + if (rdev->flags & RADEON_IS_PCIE) { + r = rv370_pcie_gart_enable(rdev); + if (r) + return r; + } + if (rdev->flags & RADEON_IS_PCI) { + r = r100_pci_gart_enable(rdev); + if (r) + return r; + } + r420_pipes_init(rdev); + + /* allocate wb buffer */ + r = radeon_wb_init(rdev); + if (r) + return r; + + r = radeon_fence_driver_start_ring(rdev, RADEON_RING_TYPE_GFX_INDEX); + if (r) { + dev_err(rdev->dev, "failed initializing CP fences (%d).\n", r); + return r; + } + + /* Enable IRQ */ + r100_irq_set(rdev); + rdev->config.r300.hdp_cntl = RREG32(RADEON_HOST_PATH_CNTL); + /* 1M ring buffer */ + r = r100_cp_init(rdev, 1024 * 1024); + if (r) { + dev_err(rdev->dev, "failed initializing CP (%d).\n", r); + return r; + } + r420_cp_errata_init(rdev); + + r = radeon_ib_pool_init(rdev); + if (r) { + dev_err(rdev->dev, "IB initialization failed (%d).\n", r); + return r; + } + + return 0; +} + +int r420_resume(struct radeon_device *rdev) +{ + int r; + + /* Make sur GART are not working */ + if (rdev->flags & RADEON_IS_PCIE) + rv370_pcie_gart_disable(rdev); + if (rdev->flags & RADEON_IS_PCI) + r100_pci_gart_disable(rdev); + /* Resume clock before doing reset */ + r420_clock_resume(rdev); + /* Reset gpu before posting otherwise ATOM will enter infinite loop */ + if (radeon_asic_reset(rdev)) { + dev_warn(rdev->dev, "GPU reset failed ! (0xE40=0x%08X, 0x7C0=0x%08X)\n", + RREG32(R_000E40_RBBM_STATUS), + RREG32(R_0007C0_CP_STAT)); + } + /* check if cards are posted or not */ + if (rdev->is_atom_bios) { + atom_asic_init(rdev->mode_info.atom_context); + } else { + radeon_combios_asic_init(rdev->ddev); + } + /* Resume clock after posting */ + r420_clock_resume(rdev); + /* Initialize surface registers */ + radeon_surface_init(rdev); + + rdev->accel_working = true; + r = r420_startup(rdev); + if (r) { + rdev->accel_working = false; + } + return r; +} + +int r420_suspend(struct radeon_device *rdev) +{ + r420_cp_errata_fini(rdev); + r100_cp_disable(rdev); + radeon_wb_disable(rdev); + r100_irq_disable(rdev); + if (rdev->flags & RADEON_IS_PCIE) + rv370_pcie_gart_disable(rdev); + if (rdev->flags & RADEON_IS_PCI) + r100_pci_gart_disable(rdev); + return 0; +} + +void r420_fini(struct radeon_device *rdev) +{ + r100_cp_fini(rdev); + radeon_wb_fini(rdev); + radeon_ib_pool_fini(rdev); + radeon_gem_fini(rdev); + if (rdev->flags & RADEON_IS_PCIE) + rv370_pcie_gart_fini(rdev); + if (rdev->flags & RADEON_IS_PCI) + r100_pci_gart_fini(rdev); + radeon_agp_fini(rdev); + radeon_irq_kms_fini(rdev); + radeon_fence_driver_fini(rdev); + radeon_bo_fini(rdev); + if (rdev->is_atom_bios) { + radeon_atombios_fini(rdev); + } else { + radeon_combios_fini(rdev); + } + free(rdev->bios, DRM_MEM_DRIVER); + rdev->bios = NULL; +} + +int r420_init(struct radeon_device *rdev) +{ + int r; + + /* Initialize scratch registers */ + radeon_scratch_init(rdev); + /* Initialize surface registers */ + radeon_surface_init(rdev); + /* TODO: disable VGA need to use VGA request */ + /* restore some register to sane defaults */ + r100_restore_sanity(rdev); + /* BIOS*/ + if (!radeon_get_bios(rdev)) { + if (ASIC_IS_AVIVO(rdev)) + return -EINVAL; + } + if (rdev->is_atom_bios) { + r = radeon_atombios_init(rdev); + if (r) { + return r; + } + } else { + r = radeon_combios_init(rdev); + if (r) { + return r; + } + } + /* Reset gpu before posting otherwise ATOM will enter infinite loop */ + if (radeon_asic_reset(rdev)) { + dev_warn(rdev->dev, + "GPU reset failed ! (0xE40=0x%08X, 0x7C0=0x%08X)\n", + RREG32(R_000E40_RBBM_STATUS), + RREG32(R_0007C0_CP_STAT)); + } + /* check if cards are posted or not */ + if (radeon_boot_test_post_card(rdev) == false) + return -EINVAL; + + /* Initialize clocks */ + radeon_get_clock_info(rdev->ddev); + /* initialize AGP */ + if (rdev->flags & RADEON_IS_AGP) { + r = radeon_agp_init(rdev); + if (r) { + radeon_agp_disable(rdev); + } + } + /* initialize memory controller */ + r300_mc_init(rdev); + r420_debugfs(rdev); + /* Fence driver */ + r = radeon_fence_driver_init(rdev); + if (r) { + return r; + } + r = radeon_irq_kms_init(rdev); + if (r) { + return r; + } + /* Memory manager */ + r = radeon_bo_init(rdev); + if (r) { + return r; + } + if (rdev->family == CHIP_R420) + r100_enable_bm(rdev); + + if (rdev->flags & RADEON_IS_PCIE) { + r = rv370_pcie_gart_init(rdev); + if (r) + return r; + } + if (rdev->flags & RADEON_IS_PCI) { + r = r100_pci_gart_init(rdev); + if (r) + return r; + } + r420_set_reg_safe(rdev); + + rdev->accel_working = true; + r = r420_startup(rdev); + if (r) { + /* Somethings want wront with the accel init stop accel */ + dev_err(rdev->dev, "Disabling GPU acceleration\n"); + r100_cp_fini(rdev); + radeon_wb_fini(rdev); + radeon_ib_pool_fini(rdev); + radeon_irq_kms_fini(rdev); + if (rdev->flags & RADEON_IS_PCIE) + rv370_pcie_gart_fini(rdev); + if (rdev->flags & RADEON_IS_PCI) + r100_pci_gart_fini(rdev); + radeon_agp_fini(rdev); + rdev->accel_working = false; + } + return 0; +} + +/* + * Debugfs info + */ +#if defined(CONFIG_DEBUG_FS) +static int r420_debugfs_pipes_info(struct seq_file *m, void *data) +{ + struct drm_info_node *node = (struct drm_info_node *) m->private; + struct drm_device *dev = node->minor->dev; + struct radeon_device *rdev = dev->dev_private; + uint32_t tmp; + + tmp = RREG32(R400_GB_PIPE_SELECT); + seq_printf(m, "GB_PIPE_SELECT 0x%08x\n", tmp); + tmp = RREG32(R300_GB_TILE_CONFIG); + seq_printf(m, "GB_TILE_CONFIG 0x%08x\n", tmp); + tmp = RREG32(R300_DST_PIPE_CONFIG); + seq_printf(m, "DST_PIPE_CONFIG 0x%08x\n", tmp); + return 0; +} + +static struct drm_info_list r420_pipes_info_list[] = { + {"r420_pipes_info", r420_debugfs_pipes_info, 0, NULL}, +}; +#endif + +int r420_debugfs_pipes_info_init(struct radeon_device *rdev) +{ +#if defined(CONFIG_DEBUG_FS) + return radeon_debugfs_add_files(rdev, r420_pipes_info_list, 1); +#else + return 0; +#endif +} diff --git a/sys/dev/drm2/radeon/r420_reg_safe.h b/sys/dev/drm2/radeon/r420_reg_safe.h new file mode 100644 index 00000000000..c929b0b848c --- /dev/null +++ b/sys/dev/drm2/radeon/r420_reg_safe.h @@ -0,0 +1,45 @@ +#include +__FBSDID("$FreeBSD$"); + +static const unsigned r420_reg_safe_bm[159] = { + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0x17FF1FFF, 0xFFFFFFFC, 0xFFFFFFFF, 0xFF30FFBF, + 0xFFFFFFF8, 0xC3E6FFFF, 0xFFFFF6DF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFF03F, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFEFCE, 0xF00EBFFF, 0x007C0000, + 0xF0000078, 0xFF000009, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFF7FF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFC48, 0xFFFFFFFF, 0xFFFFFFFE, 0xFFFFFFFF, + 0x38FF8F50, 0xFFF88082, 0xF000000C, 0xFAE00BFF, + 0x0000FFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, + 0x00000000, 0x00000100, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0xFF800000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x0003FC0B, 0xFFFFFCFF, 0xFFBFFB99, +}; diff --git a/sys/dev/drm2/radeon/r420d.h b/sys/dev/drm2/radeon/r420d.h new file mode 100644 index 00000000000..78ea0663eb3 --- /dev/null +++ b/sys/dev/drm2/radeon/r420d.h @@ -0,0 +1,252 @@ +/* + * Copyright 2008 Advanced Micro Devices, Inc. + * Copyright 2008 Red Hat Inc. + * Copyright 2009 Jerome Glisse. + * + * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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. + * + * Authors: Dave Airlie + * Alex Deucher + * Jerome Glisse + */ +#ifndef R420D_H +#define R420D_H + +#include +__FBSDID("$FreeBSD$"); + +#define R_0001F8_MC_IND_INDEX 0x0001F8 +#define S_0001F8_MC_IND_ADDR(x) (((x) & 0x7F) << 0) +#define G_0001F8_MC_IND_ADDR(x) (((x) >> 0) & 0x7F) +#define C_0001F8_MC_IND_ADDR 0xFFFFFF80 +#define S_0001F8_MC_IND_WR_EN(x) (((x) & 0x1) << 8) +#define G_0001F8_MC_IND_WR_EN(x) (((x) >> 8) & 0x1) +#define C_0001F8_MC_IND_WR_EN 0xFFFFFEFF +#define R_0001FC_MC_IND_DATA 0x0001FC +#define S_0001FC_MC_IND_DATA(x) (((x) & 0xFFFFFFFF) << 0) +#define G_0001FC_MC_IND_DATA(x) (((x) >> 0) & 0xFFFFFFFF) +#define C_0001FC_MC_IND_DATA 0x00000000 +#define R_0007C0_CP_STAT 0x0007C0 +#define S_0007C0_MRU_BUSY(x) (((x) & 0x1) << 0) +#define G_0007C0_MRU_BUSY(x) (((x) >> 0) & 0x1) +#define C_0007C0_MRU_BUSY 0xFFFFFFFE +#define S_0007C0_MWU_BUSY(x) (((x) & 0x1) << 1) +#define G_0007C0_MWU_BUSY(x) (((x) >> 1) & 0x1) +#define C_0007C0_MWU_BUSY 0xFFFFFFFD +#define S_0007C0_RSIU_BUSY(x) (((x) & 0x1) << 2) +#define G_0007C0_RSIU_BUSY(x) (((x) >> 2) & 0x1) +#define C_0007C0_RSIU_BUSY 0xFFFFFFFB +#define S_0007C0_RCIU_BUSY(x) (((x) & 0x1) << 3) +#define G_0007C0_RCIU_BUSY(x) (((x) >> 3) & 0x1) +#define C_0007C0_RCIU_BUSY 0xFFFFFFF7 +#define S_0007C0_CSF_PRIMARY_BUSY(x) (((x) & 0x1) << 9) +#define G_0007C0_CSF_PRIMARY_BUSY(x) (((x) >> 9) & 0x1) +#define C_0007C0_CSF_PRIMARY_BUSY 0xFFFFFDFF +#define S_0007C0_CSF_INDIRECT_BUSY(x) (((x) & 0x1) << 10) +#define G_0007C0_CSF_INDIRECT_BUSY(x) (((x) >> 10) & 0x1) +#define C_0007C0_CSF_INDIRECT_BUSY 0xFFFFFBFF +#define S_0007C0_CSQ_PRIMARY_BUSY(x) (((x) & 0x1) << 11) +#define G_0007C0_CSQ_PRIMARY_BUSY(x) (((x) >> 11) & 0x1) +#define C_0007C0_CSQ_PRIMARY_BUSY 0xFFFFF7FF +#define S_0007C0_CSQ_INDIRECT_BUSY(x) (((x) & 0x1) << 12) +#define G_0007C0_CSQ_INDIRECT_BUSY(x) (((x) >> 12) & 0x1) +#define C_0007C0_CSQ_INDIRECT_BUSY 0xFFFFEFFF +#define S_0007C0_CSI_BUSY(x) (((x) & 0x1) << 13) +#define G_0007C0_CSI_BUSY(x) (((x) >> 13) & 0x1) +#define C_0007C0_CSI_BUSY 0xFFFFDFFF +#define S_0007C0_CSF_INDIRECT2_BUSY(x) (((x) & 0x1) << 14) +#define G_0007C0_CSF_INDIRECT2_BUSY(x) (((x) >> 14) & 0x1) +#define C_0007C0_CSF_INDIRECT2_BUSY 0xFFFFBFFF +#define S_0007C0_CSQ_INDIRECT2_BUSY(x) (((x) & 0x1) << 15) +#define G_0007C0_CSQ_INDIRECT2_BUSY(x) (((x) >> 15) & 0x1) +#define C_0007C0_CSQ_INDIRECT2_BUSY 0xFFFF7FFF +#define S_0007C0_GUIDMA_BUSY(x) (((x) & 0x1) << 28) +#define G_0007C0_GUIDMA_BUSY(x) (((x) >> 28) & 0x1) +#define C_0007C0_GUIDMA_BUSY 0xEFFFFFFF +#define S_0007C0_VIDDMA_BUSY(x) (((x) & 0x1) << 29) +#define G_0007C0_VIDDMA_BUSY(x) (((x) >> 29) & 0x1) +#define C_0007C0_VIDDMA_BUSY 0xDFFFFFFF +#define S_0007C0_CMDSTRM_BUSY(x) (((x) & 0x1) << 30) +#define G_0007C0_CMDSTRM_BUSY(x) (((x) >> 30) & 0x1) +#define C_0007C0_CMDSTRM_BUSY 0xBFFFFFFF +#define S_0007C0_CP_BUSY(x) (((x) & 0x1) << 31) +#define G_0007C0_CP_BUSY(x) (((x) >> 31) & 0x1) +#define C_0007C0_CP_BUSY 0x7FFFFFFF +#define R_000E40_RBBM_STATUS 0x000E40 +#define S_000E40_CMDFIFO_AVAIL(x) (((x) & 0x7F) << 0) +#define G_000E40_CMDFIFO_AVAIL(x) (((x) >> 0) & 0x7F) +#define C_000E40_CMDFIFO_AVAIL 0xFFFFFF80 +#define S_000E40_HIRQ_ON_RBB(x) (((x) & 0x1) << 8) +#define G_000E40_HIRQ_ON_RBB(x) (((x) >> 8) & 0x1) +#define C_000E40_HIRQ_ON_RBB 0xFFFFFEFF +#define S_000E40_CPRQ_ON_RBB(x) (((x) & 0x1) << 9) +#define G_000E40_CPRQ_ON_RBB(x) (((x) >> 9) & 0x1) +#define C_000E40_CPRQ_ON_RBB 0xFFFFFDFF +#define S_000E40_CFRQ_ON_RBB(x) (((x) & 0x1) << 10) +#define G_000E40_CFRQ_ON_RBB(x) (((x) >> 10) & 0x1) +#define C_000E40_CFRQ_ON_RBB 0xFFFFFBFF +#define S_000E40_HIRQ_IN_RTBUF(x) (((x) & 0x1) << 11) +#define G_000E40_HIRQ_IN_RTBUF(x) (((x) >> 11) & 0x1) +#define C_000E40_HIRQ_IN_RTBUF 0xFFFFF7FF +#define S_000E40_CPRQ_IN_RTBUF(x) (((x) & 0x1) << 12) +#define G_000E40_CPRQ_IN_RTBUF(x) (((x) >> 12) & 0x1) +#define C_000E40_CPRQ_IN_RTBUF 0xFFFFEFFF +#define S_000E40_CFRQ_IN_RTBUF(x) (((x) & 0x1) << 13) +#define G_000E40_CFRQ_IN_RTBUF(x) (((x) >> 13) & 0x1) +#define C_000E40_CFRQ_IN_RTBUF 0xFFFFDFFF +#define S_000E40_CF_PIPE_BUSY(x) (((x) & 0x1) << 14) +#define G_000E40_CF_PIPE_BUSY(x) (((x) >> 14) & 0x1) +#define C_000E40_CF_PIPE_BUSY 0xFFFFBFFF +#define S_000E40_ENG_EV_BUSY(x) (((x) & 0x1) << 15) +#define G_000E40_ENG_EV_BUSY(x) (((x) >> 15) & 0x1) +#define C_000E40_ENG_EV_BUSY 0xFFFF7FFF +#define S_000E40_CP_CMDSTRM_BUSY(x) (((x) & 0x1) << 16) +#define G_000E40_CP_CMDSTRM_BUSY(x) (((x) >> 16) & 0x1) +#define C_000E40_CP_CMDSTRM_BUSY 0xFFFEFFFF +#define S_000E40_E2_BUSY(x) (((x) & 0x1) << 17) +#define G_000E40_E2_BUSY(x) (((x) >> 17) & 0x1) +#define C_000E40_E2_BUSY 0xFFFDFFFF +#define S_000E40_RB2D_BUSY(x) (((x) & 0x1) << 18) +#define G_000E40_RB2D_BUSY(x) (((x) >> 18) & 0x1) +#define C_000E40_RB2D_BUSY 0xFFFBFFFF +#define S_000E40_RB3D_BUSY(x) (((x) & 0x1) << 19) +#define G_000E40_RB3D_BUSY(x) (((x) >> 19) & 0x1) +#define C_000E40_RB3D_BUSY 0xFFF7FFFF +#define S_000E40_VAP_BUSY(x) (((x) & 0x1) << 20) +#define G_000E40_VAP_BUSY(x) (((x) >> 20) & 0x1) +#define C_000E40_VAP_BUSY 0xFFEFFFFF +#define S_000E40_RE_BUSY(x) (((x) & 0x1) << 21) +#define G_000E40_RE_BUSY(x) (((x) >> 21) & 0x1) +#define C_000E40_RE_BUSY 0xFFDFFFFF +#define S_000E40_TAM_BUSY(x) (((x) & 0x1) << 22) +#define G_000E40_TAM_BUSY(x) (((x) >> 22) & 0x1) +#define C_000E40_TAM_BUSY 0xFFBFFFFF +#define S_000E40_TDM_BUSY(x) (((x) & 0x1) << 23) +#define G_000E40_TDM_BUSY(x) (((x) >> 23) & 0x1) +#define C_000E40_TDM_BUSY 0xFF7FFFFF +#define S_000E40_PB_BUSY(x) (((x) & 0x1) << 24) +#define G_000E40_PB_BUSY(x) (((x) >> 24) & 0x1) +#define C_000E40_PB_BUSY 0xFEFFFFFF +#define S_000E40_TIM_BUSY(x) (((x) & 0x1) << 25) +#define G_000E40_TIM_BUSY(x) (((x) >> 25) & 0x1) +#define C_000E40_TIM_BUSY 0xFDFFFFFF +#define S_000E40_GA_BUSY(x) (((x) & 0x1) << 26) +#define G_000E40_GA_BUSY(x) (((x) >> 26) & 0x1) +#define C_000E40_GA_BUSY 0xFBFFFFFF +#define S_000E40_CBA2D_BUSY(x) (((x) & 0x1) << 27) +#define G_000E40_CBA2D_BUSY(x) (((x) >> 27) & 0x1) +#define C_000E40_CBA2D_BUSY 0xF7FFFFFF +#define S_000E40_GUI_ACTIVE(x) (((x) & 0x1) << 31) +#define G_000E40_GUI_ACTIVE(x) (((x) >> 31) & 0x1) +#define C_000E40_GUI_ACTIVE 0x7FFFFFFF + +/* CLK registers */ +#define R_00000D_SCLK_CNTL 0x00000D +#define S_00000D_SCLK_SRC_SEL(x) (((x) & 0x7) << 0) +#define G_00000D_SCLK_SRC_SEL(x) (((x) >> 0) & 0x7) +#define C_00000D_SCLK_SRC_SEL 0xFFFFFFF8 +#define S_00000D_CP_MAX_DYN_STOP_LAT(x) (((x) & 0x1) << 3) +#define G_00000D_CP_MAX_DYN_STOP_LAT(x) (((x) >> 3) & 0x1) +#define C_00000D_CP_MAX_DYN_STOP_LAT 0xFFFFFFF7 +#define S_00000D_HDP_MAX_DYN_STOP_LAT(x) (((x) & 0x1) << 4) +#define G_00000D_HDP_MAX_DYN_STOP_LAT(x) (((x) >> 4) & 0x1) +#define C_00000D_HDP_MAX_DYN_STOP_LAT 0xFFFFFFEF +#define S_00000D_TV_MAX_DYN_STOP_LAT(x) (((x) & 0x1) << 5) +#define G_00000D_TV_MAX_DYN_STOP_LAT(x) (((x) >> 5) & 0x1) +#define C_00000D_TV_MAX_DYN_STOP_LAT 0xFFFFFFDF +#define S_00000D_E2_MAX_DYN_STOP_LAT(x) (((x) & 0x1) << 6) +#define G_00000D_E2_MAX_DYN_STOP_LAT(x) (((x) >> 6) & 0x1) +#define C_00000D_E2_MAX_DYN_STOP_LAT 0xFFFFFFBF +#define S_00000D_SE_MAX_DYN_STOP_LAT(x) (((x) & 0x1) << 7) +#define G_00000D_SE_MAX_DYN_STOP_LAT(x) (((x) >> 7) & 0x1) +#define C_00000D_SE_MAX_DYN_STOP_LAT 0xFFFFFF7F +#define S_00000D_IDCT_MAX_DYN_STOP_LAT(x) (((x) & 0x1) << 8) +#define G_00000D_IDCT_MAX_DYN_STOP_LAT(x) (((x) >> 8) & 0x1) +#define C_00000D_IDCT_MAX_DYN_STOP_LAT 0xFFFFFEFF +#define S_00000D_VIP_MAX_DYN_STOP_LAT(x) (((x) & 0x1) << 9) +#define G_00000D_VIP_MAX_DYN_STOP_LAT(x) (((x) >> 9) & 0x1) +#define C_00000D_VIP_MAX_DYN_STOP_LAT 0xFFFFFDFF +#define S_00000D_RE_MAX_DYN_STOP_LAT(x) (((x) & 0x1) << 10) +#define G_00000D_RE_MAX_DYN_STOP_LAT(x) (((x) >> 10) & 0x1) +#define C_00000D_RE_MAX_DYN_STOP_LAT 0xFFFFFBFF +#define S_00000D_PB_MAX_DYN_STOP_LAT(x) (((x) & 0x1) << 11) +#define G_00000D_PB_MAX_DYN_STOP_LAT(x) (((x) >> 11) & 0x1) +#define C_00000D_PB_MAX_DYN_STOP_LAT 0xFFFFF7FF +#define S_00000D_TAM_MAX_DYN_STOP_LAT(x) (((x) & 0x1) << 12) +#define G_00000D_TAM_MAX_DYN_STOP_LAT(x) (((x) >> 12) & 0x1) +#define C_00000D_TAM_MAX_DYN_STOP_LAT 0xFFFFEFFF +#define S_00000D_TDM_MAX_DYN_STOP_LAT(x) (((x) & 0x1) << 13) +#define G_00000D_TDM_MAX_DYN_STOP_LAT(x) (((x) >> 13) & 0x1) +#define C_00000D_TDM_MAX_DYN_STOP_LAT 0xFFFFDFFF +#define S_00000D_RB_MAX_DYN_STOP_LAT(x) (((x) & 0x1) << 14) +#define G_00000D_RB_MAX_DYN_STOP_LAT(x) (((x) >> 14) & 0x1) +#define C_00000D_RB_MAX_DYN_STOP_LAT 0xFFFFBFFF +#define S_00000D_FORCE_DISP2(x) (((x) & 0x1) << 15) +#define G_00000D_FORCE_DISP2(x) (((x) >> 15) & 0x1) +#define C_00000D_FORCE_DISP2 0xFFFF7FFF +#define S_00000D_FORCE_CP(x) (((x) & 0x1) << 16) +#define G_00000D_FORCE_CP(x) (((x) >> 16) & 0x1) +#define C_00000D_FORCE_CP 0xFFFEFFFF +#define S_00000D_FORCE_HDP(x) (((x) & 0x1) << 17) +#define G_00000D_FORCE_HDP(x) (((x) >> 17) & 0x1) +#define C_00000D_FORCE_HDP 0xFFFDFFFF +#define S_00000D_FORCE_DISP1(x) (((x) & 0x1) << 18) +#define G_00000D_FORCE_DISP1(x) (((x) >> 18) & 0x1) +#define C_00000D_FORCE_DISP1 0xFFFBFFFF +#define S_00000D_FORCE_TOP(x) (((x) & 0x1) << 19) +#define G_00000D_FORCE_TOP(x) (((x) >> 19) & 0x1) +#define C_00000D_FORCE_TOP 0xFFF7FFFF +#define S_00000D_FORCE_E2(x) (((x) & 0x1) << 20) +#define G_00000D_FORCE_E2(x) (((x) >> 20) & 0x1) +#define C_00000D_FORCE_E2 0xFFEFFFFF +#define S_00000D_FORCE_VAP(x) (((x) & 0x1) << 21) +#define G_00000D_FORCE_VAP(x) (((x) >> 21) & 0x1) +#define C_00000D_FORCE_VAP 0xFFDFFFFF +#define S_00000D_FORCE_IDCT(x) (((x) & 0x1) << 22) +#define G_00000D_FORCE_IDCT(x) (((x) >> 22) & 0x1) +#define C_00000D_FORCE_IDCT 0xFFBFFFFF +#define S_00000D_FORCE_VIP(x) (((x) & 0x1) << 23) +#define G_00000D_FORCE_VIP(x) (((x) >> 23) & 0x1) +#define C_00000D_FORCE_VIP 0xFF7FFFFF +#define S_00000D_FORCE_RE(x) (((x) & 0x1) << 24) +#define G_00000D_FORCE_RE(x) (((x) >> 24) & 0x1) +#define C_00000D_FORCE_RE 0xFEFFFFFF +#define S_00000D_FORCE_SR(x) (((x) & 0x1) << 25) +#define G_00000D_FORCE_SR(x) (((x) >> 25) & 0x1) +#define C_00000D_FORCE_SR 0xFDFFFFFF +#define S_00000D_FORCE_PX(x) (((x) & 0x1) << 26) +#define G_00000D_FORCE_PX(x) (((x) >> 26) & 0x1) +#define C_00000D_FORCE_PX 0xFBFFFFFF +#define S_00000D_FORCE_TX(x) (((x) & 0x1) << 27) +#define G_00000D_FORCE_TX(x) (((x) >> 27) & 0x1) +#define C_00000D_FORCE_TX 0xF7FFFFFF +#define S_00000D_FORCE_US(x) (((x) & 0x1) << 28) +#define G_00000D_FORCE_US(x) (((x) >> 28) & 0x1) +#define C_00000D_FORCE_US 0xEFFFFFFF +#define S_00000D_FORCE_TV_SCLK(x) (((x) & 0x1) << 29) +#define G_00000D_FORCE_TV_SCLK(x) (((x) >> 29) & 0x1) +#define C_00000D_FORCE_TV_SCLK 0xDFFFFFFF +#define S_00000D_FORCE_SU(x) (((x) & 0x1) << 30) +#define G_00000D_FORCE_SU(x) (((x) >> 30) & 0x1) +#define C_00000D_FORCE_SU 0xBFFFFFFF +#define S_00000D_FORCE_OV0(x) (((x) & 0x1) << 31) +#define G_00000D_FORCE_OV0(x) (((x) >> 31) & 0x1) +#define C_00000D_FORCE_OV0 0x7FFFFFFF + +#endif diff --git a/sys/dev/drm2/radeon/r500_reg.h b/sys/dev/drm2/radeon/r500_reg.h new file mode 100644 index 00000000000..c56088b8652 --- /dev/null +++ b/sys/dev/drm2/radeon/r500_reg.h @@ -0,0 +1,804 @@ +/* + * Copyright 2008 Advanced Micro Devices, Inc. + * Copyright 2008 Red Hat Inc. + * Copyright 2009 Jerome Glisse. + * + * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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. + * + * Authors: Dave Airlie + * Alex Deucher + * Jerome Glisse + */ +#ifndef __R500_REG_H__ +#define __R500_REG_H__ + +#include +__FBSDID("$FreeBSD$"); + +/* pipe config regs */ +#define R300_GA_POLY_MODE 0x4288 +# define R300_FRONT_PTYPE_POINT (0 << 4) +# define R300_FRONT_PTYPE_LINE (1 << 4) +# define R300_FRONT_PTYPE_TRIANGE (2 << 4) +# define R300_BACK_PTYPE_POINT (0 << 7) +# define R300_BACK_PTYPE_LINE (1 << 7) +# define R300_BACK_PTYPE_TRIANGE (2 << 7) +#define R300_GA_ROUND_MODE 0x428c +# define R300_GEOMETRY_ROUND_TRUNC (0 << 0) +# define R300_GEOMETRY_ROUND_NEAREST (1 << 0) +# define R300_COLOR_ROUND_TRUNC (0 << 2) +# define R300_COLOR_ROUND_NEAREST (1 << 2) +#define R300_GB_MSPOS0 0x4010 +# define R300_MS_X0_SHIFT 0 +# define R300_MS_Y0_SHIFT 4 +# define R300_MS_X1_SHIFT 8 +# define R300_MS_Y1_SHIFT 12 +# define R300_MS_X2_SHIFT 16 +# define R300_MS_Y2_SHIFT 20 +# define R300_MSBD0_Y_SHIFT 24 +# define R300_MSBD0_X_SHIFT 28 +#define R300_GB_MSPOS1 0x4014 +# define R300_MS_X3_SHIFT 0 +# define R300_MS_Y3_SHIFT 4 +# define R300_MS_X4_SHIFT 8 +# define R300_MS_Y4_SHIFT 12 +# define R300_MS_X5_SHIFT 16 +# define R300_MS_Y5_SHIFT 20 +# define R300_MSBD1_SHIFT 24 + +#define R300_GA_ENHANCE 0x4274 +# define R300_GA_DEADLOCK_CNTL (1 << 0) +# define R300_GA_FASTSYNC_CNTL (1 << 1) +#define R300_RB3D_DSTCACHE_CTLSTAT 0x4e4c +# define R300_RB3D_DC_FLUSH (2 << 0) +# define R300_RB3D_DC_FREE (2 << 2) +# define R300_RB3D_DC_FINISH (1 << 4) +#define R300_RB3D_ZCACHE_CTLSTAT 0x4f18 +# define R300_ZC_FLUSH (1 << 0) +# define R300_ZC_FREE (1 << 1) +# define R300_ZC_FLUSH_ALL 0x3 +#define R400_GB_PIPE_SELECT 0x402c +#define R500_DYN_SCLK_PWMEM_PIPE 0x000d /* PLL */ +#define R500_SU_REG_DEST 0x42c8 +#define R300_GB_TILE_CONFIG 0x4018 +# define R300_ENABLE_TILING (1 << 0) +# define R300_PIPE_COUNT_RV350 (0 << 1) +# define R300_PIPE_COUNT_R300 (3 << 1) +# define R300_PIPE_COUNT_R420_3P (6 << 1) +# define R300_PIPE_COUNT_R420 (7 << 1) +# define R300_TILE_SIZE_8 (0 << 4) +# define R300_TILE_SIZE_16 (1 << 4) +# define R300_TILE_SIZE_32 (2 << 4) +# define R300_SUBPIXEL_1_12 (0 << 16) +# define R300_SUBPIXEL_1_16 (1 << 16) +#define R300_DST_PIPE_CONFIG 0x170c +# define R300_PIPE_AUTO_CONFIG (1 << 31) +#define R300_RB2D_DSTCACHE_MODE 0x3428 +# define R300_DC_AUTOFLUSH_ENABLE (1 << 8) +# define R300_DC_DC_DISABLE_IGNORE_PE (1 << 17) + +#define RADEON_CP_STAT 0x7C0 +#define RADEON_RBBM_CMDFIFO_ADDR 0xE70 +#define RADEON_RBBM_CMDFIFO_DATA 0xE74 +#define RADEON_ISYNC_CNTL 0x1724 +# define RADEON_ISYNC_ANY2D_IDLE3D (1 << 0) +# define RADEON_ISYNC_ANY3D_IDLE2D (1 << 1) +# define RADEON_ISYNC_TRIG2D_IDLE3D (1 << 2) +# define RADEON_ISYNC_TRIG3D_IDLE2D (1 << 3) +# define RADEON_ISYNC_WAIT_IDLEGUI (1 << 4) +# define RADEON_ISYNC_CPSCRATCH_IDLEGUI (1 << 5) + +#define RS480_NB_MC_INDEX 0x168 +# define RS480_NB_MC_IND_WR_EN (1 << 8) +#define RS480_NB_MC_DATA 0x16c + +/* + * RS690 + */ +#define RS690_MCCFG_FB_LOCATION 0x100 +#define RS690_MC_FB_START_MASK 0x0000FFFF +#define RS690_MC_FB_START_SHIFT 0 +#define RS690_MC_FB_TOP_MASK 0xFFFF0000 +#define RS690_MC_FB_TOP_SHIFT 16 +#define RS690_MCCFG_AGP_LOCATION 0x101 +#define RS690_MC_AGP_START_MASK 0x0000FFFF +#define RS690_MC_AGP_START_SHIFT 0 +#define RS690_MC_AGP_TOP_MASK 0xFFFF0000 +#define RS690_MC_AGP_TOP_SHIFT 16 +#define RS690_MCCFG_AGP_BASE 0x102 +#define RS690_MCCFG_AGP_BASE_2 0x103 +#define RS690_MC_INIT_MISC_LAT_TIMER 0x104 +#define RS690_HDP_FB_LOCATION 0x0134 +#define RS690_MC_INDEX 0x78 +# define RS690_MC_INDEX_MASK 0x1ff +# define RS690_MC_INDEX_WR_EN (1 << 9) +# define RS690_MC_INDEX_WR_ACK 0x7f +#define RS690_MC_NB_CNTL 0x0 +# define RS690_HIDE_MMCFG_BAR (1 << 3) +# define RS690_AGPMODE30 (1 << 4) +# define RS690_AGP30ENHANCED (1 << 5) +#define RS690_MC_DATA 0x7c +#define RS690_MC_STATUS 0x90 +#define RS690_MC_STATUS_IDLE (1 << 0) +#define RS480_AGP_BASE_2 0x0164 +#define RS480_MC_MISC_CNTL 0x18 +# define RS480_DISABLE_GTW (1 << 1) +# define RS480_GART_INDEX_REG_EN (1 << 12) +# define RS690_BLOCK_GFX_D3_EN (1 << 14) +#define RS480_GART_FEATURE_ID 0x2b +# define RS480_HANG_EN (1 << 11) +# define RS480_TLB_ENABLE (1 << 18) +# define RS480_P2P_ENABLE (1 << 19) +# define RS480_GTW_LAC_EN (1 << 25) +# define RS480_2LEVEL_GART (0 << 30) +# define RS480_1LEVEL_GART (1 << 30) +# define RS480_PDC_EN (1 << 31) +#define RS480_GART_BASE 0x2c +#define RS480_GART_CACHE_CNTRL 0x2e +# define RS480_GART_CACHE_INVALIDATE (1 << 0) /* wait for it to clear */ +#define RS480_AGP_ADDRESS_SPACE_SIZE 0x38 +# define RS480_GART_EN (1 << 0) +# define RS480_VA_SIZE_32MB (0 << 1) +# define RS480_VA_SIZE_64MB (1 << 1) +# define RS480_VA_SIZE_128MB (2 << 1) +# define RS480_VA_SIZE_256MB (3 << 1) +# define RS480_VA_SIZE_512MB (4 << 1) +# define RS480_VA_SIZE_1GB (5 << 1) +# define RS480_VA_SIZE_2GB (6 << 1) +#define RS480_AGP_MODE_CNTL 0x39 +# define RS480_POST_GART_Q_SIZE (1 << 18) +# define RS480_NONGART_SNOOP (1 << 19) +# define RS480_AGP_RD_BUF_SIZE (1 << 20) +# define RS480_REQ_TYPE_SNOOP_SHIFT 22 +# define RS480_REQ_TYPE_SNOOP_MASK 0x3 +# define RS480_REQ_TYPE_SNOOP_DIS (1 << 24) + +#define RS690_AIC_CTRL_SCRATCH 0x3A +# define RS690_DIS_OUT_OF_PCI_GART_ACCESS (1 << 1) + +/* + * RS600 + */ +#define RS600_MC_STATUS 0x0 +#define RS600_MC_STATUS_IDLE (1 << 0) +#define RS600_MC_INDEX 0x70 +# define RS600_MC_ADDR_MASK 0xffff +# define RS600_MC_IND_SEQ_RBS_0 (1 << 16) +# define RS600_MC_IND_SEQ_RBS_1 (1 << 17) +# define RS600_MC_IND_SEQ_RBS_2 (1 << 18) +# define RS600_MC_IND_SEQ_RBS_3 (1 << 19) +# define RS600_MC_IND_AIC_RBS (1 << 20) +# define RS600_MC_IND_CITF_ARB0 (1 << 21) +# define RS600_MC_IND_CITF_ARB1 (1 << 22) +# define RS600_MC_IND_WR_EN (1 << 23) +#define RS600_MC_DATA 0x74 +#define RS600_MC_STATUS 0x0 +# define RS600_MC_IDLE (1 << 1) +#define RS600_MC_FB_LOCATION 0x4 +#define RS600_MC_FB_START_MASK 0x0000FFFF +#define RS600_MC_FB_START_SHIFT 0 +#define RS600_MC_FB_TOP_MASK 0xFFFF0000 +#define RS600_MC_FB_TOP_SHIFT 16 +#define RS600_MC_AGP_LOCATION 0x5 +#define RS600_MC_AGP_START_MASK 0x0000FFFF +#define RS600_MC_AGP_START_SHIFT 0 +#define RS600_MC_AGP_TOP_MASK 0xFFFF0000 +#define RS600_MC_AGP_TOP_SHIFT 16 +#define RS600_MC_AGP_BASE 0x6 +#define RS600_MC_AGP_BASE_2 0x7 +#define RS600_MC_CNTL1 0x9 +# define RS600_ENABLE_PAGE_TABLES (1 << 26) +#define RS600_MC_PT0_CNTL 0x100 +# define RS600_ENABLE_PT (1 << 0) +# define RS600_EFFECTIVE_L2_CACHE_SIZE(x) ((x) << 15) +# define RS600_EFFECTIVE_L2_QUEUE_SIZE(x) ((x) << 21) +# define RS600_INVALIDATE_ALL_L1_TLBS (1 << 28) +# define RS600_INVALIDATE_L2_CACHE (1 << 29) +#define RS600_MC_PT0_CONTEXT0_CNTL 0x102 +# define RS600_ENABLE_PAGE_TABLE (1 << 0) +# define RS600_PAGE_TABLE_TYPE_FLAT (0 << 1) +#define RS600_MC_PT0_SYSTEM_APERTURE_LOW_ADDR 0x112 +#define RS600_MC_PT0_SYSTEM_APERTURE_HIGH_ADDR 0x114 +#define RS600_MC_PT0_CONTEXT0_DEFAULT_READ_ADDR 0x11c +#define RS600_MC_PT0_CONTEXT0_FLAT_BASE_ADDR 0x12c +#define RS600_MC_PT0_CONTEXT0_FLAT_START_ADDR 0x13c +#define RS600_MC_PT0_CONTEXT0_FLAT_END_ADDR 0x14c +#define RS600_MC_PT0_CLIENT0_CNTL 0x16c +# define RS600_ENABLE_TRANSLATION_MODE_OVERRIDE (1 << 0) +# define RS600_TRANSLATION_MODE_OVERRIDE (1 << 1) +# define RS600_SYSTEM_ACCESS_MODE_MASK (3 << 8) +# define RS600_SYSTEM_ACCESS_MODE_PA_ONLY (0 << 8) +# define RS600_SYSTEM_ACCESS_MODE_USE_SYS_MAP (1 << 8) +# define RS600_SYSTEM_ACCESS_MODE_IN_SYS (2 << 8) +# define RS600_SYSTEM_ACCESS_MODE_NOT_IN_SYS (3 << 8) +# define RS600_SYSTEM_APERTURE_UNMAPPED_ACCESS_PASSTHROUGH (0 << 10) +# define RS600_SYSTEM_APERTURE_UNMAPPED_ACCESS_DEFAULT_PAGE (1 << 10) +# define RS600_EFFECTIVE_L1_CACHE_SIZE(x) ((x) << 11) +# define RS600_ENABLE_FRAGMENT_PROCESSING (1 << 14) +# define RS600_EFFECTIVE_L1_QUEUE_SIZE(x) ((x) << 15) +# define RS600_INVALIDATE_L1_TLB (1 << 20) +/* rs600/rs690/rs740 */ +# define RS600_BUS_MASTER_DIS (1 << 14) +# define RS600_MSI_REARM (1 << 20) +/* see RS400_MSI_REARM in AIC_CNTL for rs480 */ + + + +#define RV515_MC_FB_LOCATION 0x01 +#define RV515_MC_FB_START_MASK 0x0000FFFF +#define RV515_MC_FB_START_SHIFT 0 +#define RV515_MC_FB_TOP_MASK 0xFFFF0000 +#define RV515_MC_FB_TOP_SHIFT 16 +#define RV515_MC_AGP_LOCATION 0x02 +#define RV515_MC_AGP_START_MASK 0x0000FFFF +#define RV515_MC_AGP_START_SHIFT 0 +#define RV515_MC_AGP_TOP_MASK 0xFFFF0000 +#define RV515_MC_AGP_TOP_SHIFT 16 +#define RV515_MC_AGP_BASE 0x03 +#define RV515_MC_AGP_BASE_2 0x04 + +#define R520_MC_FB_LOCATION 0x04 +#define R520_MC_FB_START_MASK 0x0000FFFF +#define R520_MC_FB_START_SHIFT 0 +#define R520_MC_FB_TOP_MASK 0xFFFF0000 +#define R520_MC_FB_TOP_SHIFT 16 +#define R520_MC_AGP_LOCATION 0x05 +#define R520_MC_AGP_START_MASK 0x0000FFFF +#define R520_MC_AGP_START_SHIFT 0 +#define R520_MC_AGP_TOP_MASK 0xFFFF0000 +#define R520_MC_AGP_TOP_SHIFT 16 +#define R520_MC_AGP_BASE 0x06 +#define R520_MC_AGP_BASE_2 0x07 + + +#define AVIVO_MC_INDEX 0x0070 +#define R520_MC_STATUS 0x00 +#define R520_MC_STATUS_IDLE (1<<1) +#define RV515_MC_STATUS 0x08 +#define RV515_MC_STATUS_IDLE (1<<4) +#define RV515_MC_INIT_MISC_LAT_TIMER 0x09 +#define AVIVO_MC_DATA 0x0074 + +#define R520_MC_IND_INDEX 0x70 +#define R520_MC_IND_WR_EN (1 << 24) +#define R520_MC_IND_DATA 0x74 + +#define RV515_MC_CNTL 0x5 +# define RV515_MEM_NUM_CHANNELS_MASK 0x3 +#define R520_MC_CNTL0 0x8 +# define R520_MEM_NUM_CHANNELS_MASK (0x3 << 24) +# define R520_MEM_NUM_CHANNELS_SHIFT 24 +# define R520_MC_CHANNEL_SIZE (1 << 23) + +#define AVIVO_CP_DYN_CNTL 0x000f /* PLL */ +# define AVIVO_CP_FORCEON (1 << 0) +#define AVIVO_E2_DYN_CNTL 0x0011 /* PLL */ +# define AVIVO_E2_FORCEON (1 << 0) +#define AVIVO_IDCT_DYN_CNTL 0x0013 /* PLL */ +# define AVIVO_IDCT_FORCEON (1 << 0) + +#define AVIVO_HDP_FB_LOCATION 0x134 + +#define AVIVO_VGA_RENDER_CONTROL 0x0300 +# define AVIVO_VGA_VSTATUS_CNTL_MASK (3 << 16) +#define AVIVO_D1VGA_CONTROL 0x0330 +# define AVIVO_DVGA_CONTROL_MODE_ENABLE (1<<0) +# define AVIVO_DVGA_CONTROL_TIMING_SELECT (1<<8) +# define AVIVO_DVGA_CONTROL_SYNC_POLARITY_SELECT (1<<9) +# define AVIVO_DVGA_CONTROL_OVERSCAN_TIMING_SELECT (1<<10) +# define AVIVO_DVGA_CONTROL_OVERSCAN_COLOR_EN (1<<16) +# define AVIVO_DVGA_CONTROL_ROTATE (1<<24) +#define AVIVO_D2VGA_CONTROL 0x0338 + +#define AVIVO_EXT1_PPLL_REF_DIV_SRC 0x400 +#define AVIVO_EXT1_PPLL_REF_DIV 0x404 +#define AVIVO_EXT1_PPLL_UPDATE_LOCK 0x408 +#define AVIVO_EXT1_PPLL_UPDATE_CNTL 0x40c + +#define AVIVO_EXT2_PPLL_REF_DIV_SRC 0x410 +#define AVIVO_EXT2_PPLL_REF_DIV 0x414 +#define AVIVO_EXT2_PPLL_UPDATE_LOCK 0x418 +#define AVIVO_EXT2_PPLL_UPDATE_CNTL 0x41c + +#define AVIVO_EXT1_PPLL_FB_DIV 0x430 +#define AVIVO_EXT2_PPLL_FB_DIV 0x434 + +#define AVIVO_EXT1_PPLL_POST_DIV_SRC 0x438 +#define AVIVO_EXT1_PPLL_POST_DIV 0x43c + +#define AVIVO_EXT2_PPLL_POST_DIV_SRC 0x440 +#define AVIVO_EXT2_PPLL_POST_DIV 0x444 + +#define AVIVO_EXT1_PPLL_CNTL 0x448 +#define AVIVO_EXT2_PPLL_CNTL 0x44c + +#define AVIVO_P1PLL_CNTL 0x450 +#define AVIVO_P2PLL_CNTL 0x454 +#define AVIVO_P1PLL_INT_SS_CNTL 0x458 +#define AVIVO_P2PLL_INT_SS_CNTL 0x45c +#define AVIVO_P1PLL_TMDSA_CNTL 0x460 +#define AVIVO_P2PLL_LVTMA_CNTL 0x464 + +#define AVIVO_PCLK_CRTC1_CNTL 0x480 +#define AVIVO_PCLK_CRTC2_CNTL 0x484 + +#define AVIVO_D1CRTC_H_TOTAL 0x6000 +#define AVIVO_D1CRTC_H_BLANK_START_END 0x6004 +#define AVIVO_D1CRTC_H_SYNC_A 0x6008 +#define AVIVO_D1CRTC_H_SYNC_A_CNTL 0x600c +#define AVIVO_D1CRTC_H_SYNC_B 0x6010 +#define AVIVO_D1CRTC_H_SYNC_B_CNTL 0x6014 + +#define AVIVO_D1CRTC_V_TOTAL 0x6020 +#define AVIVO_D1CRTC_V_BLANK_START_END 0x6024 +#define AVIVO_D1CRTC_V_SYNC_A 0x6028 +#define AVIVO_D1CRTC_V_SYNC_A_CNTL 0x602c +#define AVIVO_D1CRTC_V_SYNC_B 0x6030 +#define AVIVO_D1CRTC_V_SYNC_B_CNTL 0x6034 + +#define AVIVO_D1CRTC_CONTROL 0x6080 +# define AVIVO_CRTC_EN (1 << 0) +# define AVIVO_CRTC_DISP_READ_REQUEST_DISABLE (1 << 24) +#define AVIVO_D1CRTC_BLANK_CONTROL 0x6084 +#define AVIVO_D1CRTC_INTERLACE_CONTROL 0x6088 +#define AVIVO_D1CRTC_INTERLACE_STATUS 0x608c +#define AVIVO_D1CRTC_STATUS 0x609c +# define AVIVO_D1CRTC_V_BLANK (1 << 0) +#define AVIVO_D1CRTC_STATUS_POSITION 0x60a0 +#define AVIVO_D1CRTC_FRAME_COUNT 0x60a4 +#define AVIVO_D1CRTC_STEREO_CONTROL 0x60c4 + +#define AVIVO_D1MODE_MASTER_UPDATE_MODE 0x60e4 + +/* master controls */ +#define AVIVO_DC_CRTC_MASTER_EN 0x60f8 +#define AVIVO_DC_CRTC_TV_CONTROL 0x60fc + +#define AVIVO_D1GRPH_ENABLE 0x6100 +#define AVIVO_D1GRPH_CONTROL 0x6104 +# define AVIVO_D1GRPH_CONTROL_DEPTH_8BPP (0 << 0) +# define AVIVO_D1GRPH_CONTROL_DEPTH_16BPP (1 << 0) +# define AVIVO_D1GRPH_CONTROL_DEPTH_32BPP (2 << 0) +# define AVIVO_D1GRPH_CONTROL_DEPTH_64BPP (3 << 0) + +# define AVIVO_D1GRPH_CONTROL_8BPP_INDEXED (0 << 8) + +# define AVIVO_D1GRPH_CONTROL_16BPP_ARGB1555 (0 << 8) +# define AVIVO_D1GRPH_CONTROL_16BPP_RGB565 (1 << 8) +# define AVIVO_D1GRPH_CONTROL_16BPP_ARGB4444 (2 << 8) +# define AVIVO_D1GRPH_CONTROL_16BPP_AI88 (3 << 8) +# define AVIVO_D1GRPH_CONTROL_16BPP_MONO16 (4 << 8) + +# define AVIVO_D1GRPH_CONTROL_32BPP_ARGB8888 (0 << 8) +# define AVIVO_D1GRPH_CONTROL_32BPP_ARGB2101010 (1 << 8) +# define AVIVO_D1GRPH_CONTROL_32BPP_DIGITAL (2 << 8) +# define AVIVO_D1GRPH_CONTROL_32BPP_8B_ARGB2101010 (3 << 8) + + +# define AVIVO_D1GRPH_CONTROL_64BPP_ARGB16161616 (0 << 8) + +# define AVIVO_D1GRPH_SWAP_RB (1 << 16) +# define AVIVO_D1GRPH_TILED (1 << 20) +# define AVIVO_D1GRPH_MACRO_ADDRESS_MODE (1 << 21) + +# define R600_D1GRPH_ARRAY_MODE_LINEAR_GENERAL (0 << 20) +# define R600_D1GRPH_ARRAY_MODE_LINEAR_ALIGNED (1 << 20) +# define R600_D1GRPH_ARRAY_MODE_1D_TILED_THIN1 (2 << 20) +# define R600_D1GRPH_ARRAY_MODE_2D_TILED_THIN1 (4 << 20) + +/* The R7xx *_HIGH surface regs are backwards; the D1 regs are in the D2 + * block and vice versa. This applies to GRPH, CUR, etc. + */ +#define AVIVO_D1GRPH_LUT_SEL 0x6108 +#define AVIVO_D1GRPH_PRIMARY_SURFACE_ADDRESS 0x6110 +#define R700_D1GRPH_PRIMARY_SURFACE_ADDRESS_HIGH 0x6914 +#define R700_D2GRPH_PRIMARY_SURFACE_ADDRESS_HIGH 0x6114 +#define AVIVO_D1GRPH_SECONDARY_SURFACE_ADDRESS 0x6118 +#define R700_D1GRPH_SECONDARY_SURFACE_ADDRESS_HIGH 0x691c +#define R700_D2GRPH_SECONDARY_SURFACE_ADDRESS_HIGH 0x611c +#define AVIVO_D1GRPH_PITCH 0x6120 +#define AVIVO_D1GRPH_SURFACE_OFFSET_X 0x6124 +#define AVIVO_D1GRPH_SURFACE_OFFSET_Y 0x6128 +#define AVIVO_D1GRPH_X_START 0x612c +#define AVIVO_D1GRPH_Y_START 0x6130 +#define AVIVO_D1GRPH_X_END 0x6134 +#define AVIVO_D1GRPH_Y_END 0x6138 +#define AVIVO_D1GRPH_UPDATE 0x6144 +# define AVIVO_D1GRPH_SURFACE_UPDATE_PENDING (1 << 2) +# define AVIVO_D1GRPH_UPDATE_LOCK (1 << 16) +#define AVIVO_D1GRPH_FLIP_CONTROL 0x6148 +# define AVIVO_D1GRPH_SURFACE_UPDATE_H_RETRACE_EN (1 << 0) + +#define AVIVO_D1CUR_CONTROL 0x6400 +# define AVIVO_D1CURSOR_EN (1 << 0) +# define AVIVO_D1CURSOR_MODE_SHIFT 8 +# define AVIVO_D1CURSOR_MODE_MASK (3 << 8) +# define AVIVO_D1CURSOR_MODE_24BPP 2 +#define AVIVO_D1CUR_SURFACE_ADDRESS 0x6408 +#define R700_D1CUR_SURFACE_ADDRESS_HIGH 0x6c0c +#define R700_D2CUR_SURFACE_ADDRESS_HIGH 0x640c +#define AVIVO_D1CUR_SIZE 0x6410 +#define AVIVO_D1CUR_POSITION 0x6414 +#define AVIVO_D1CUR_HOT_SPOT 0x6418 +#define AVIVO_D1CUR_UPDATE 0x6424 +# define AVIVO_D1CURSOR_UPDATE_LOCK (1 << 16) + +#define AVIVO_DC_LUT_RW_SELECT 0x6480 +#define AVIVO_DC_LUT_RW_MODE 0x6484 +#define AVIVO_DC_LUT_RW_INDEX 0x6488 +#define AVIVO_DC_LUT_SEQ_COLOR 0x648c +#define AVIVO_DC_LUT_PWL_DATA 0x6490 +#define AVIVO_DC_LUT_30_COLOR 0x6494 +#define AVIVO_DC_LUT_READ_PIPE_SELECT 0x6498 +#define AVIVO_DC_LUT_WRITE_EN_MASK 0x649c +#define AVIVO_DC_LUT_AUTOFILL 0x64a0 + +#define AVIVO_DC_LUTA_CONTROL 0x64c0 +#define AVIVO_DC_LUTA_BLACK_OFFSET_BLUE 0x64c4 +#define AVIVO_DC_LUTA_BLACK_OFFSET_GREEN 0x64c8 +#define AVIVO_DC_LUTA_BLACK_OFFSET_RED 0x64cc +#define AVIVO_DC_LUTA_WHITE_OFFSET_BLUE 0x64d0 +#define AVIVO_DC_LUTA_WHITE_OFFSET_GREEN 0x64d4 +#define AVIVO_DC_LUTA_WHITE_OFFSET_RED 0x64d8 + +#define AVIVO_DC_LB_MEMORY_SPLIT 0x6520 +# define AVIVO_DC_LB_MEMORY_SPLIT_MASK 0x3 +# define AVIVO_DC_LB_MEMORY_SPLIT_SHIFT 0 +# define AVIVO_DC_LB_MEMORY_SPLIT_D1HALF_D2HALF 0 +# define AVIVO_DC_LB_MEMORY_SPLIT_D1_3Q_D2_1Q 1 +# define AVIVO_DC_LB_MEMORY_SPLIT_D1_ONLY 2 +# define AVIVO_DC_LB_MEMORY_SPLIT_D1_1Q_D2_3Q 3 +# define AVIVO_DC_LB_MEMORY_SPLIT_SHIFT_MODE (1 << 2) +# define AVIVO_DC_LB_DISP1_END_ADR_SHIFT 4 +# define AVIVO_DC_LB_DISP1_END_ADR_MASK 0x7ff + +#define AVIVO_D1MODE_DATA_FORMAT 0x6528 +# define AVIVO_D1MODE_INTERLEAVE_EN (1 << 0) +#define AVIVO_D1MODE_DESKTOP_HEIGHT 0x652C +#define AVIVO_D1MODE_VBLANK_STATUS 0x6534 +# define AVIVO_VBLANK_ACK (1 << 4) +#define AVIVO_D1MODE_VLINE_START_END 0x6538 +#define AVIVO_D1MODE_VLINE_STATUS 0x653c +# define AVIVO_D1MODE_VLINE_STAT (1 << 12) +#define AVIVO_DxMODE_INT_MASK 0x6540 +# define AVIVO_D1MODE_INT_MASK (1 << 0) +# define AVIVO_D2MODE_INT_MASK (1 << 8) +#define AVIVO_D1MODE_VIEWPORT_START 0x6580 +#define AVIVO_D1MODE_VIEWPORT_SIZE 0x6584 +#define AVIVO_D1MODE_EXT_OVERSCAN_LEFT_RIGHT 0x6588 +#define AVIVO_D1MODE_EXT_OVERSCAN_TOP_BOTTOM 0x658c + +#define AVIVO_D1SCL_SCALER_ENABLE 0x6590 +#define AVIVO_D1SCL_SCALER_TAP_CONTROL 0x6594 +#define AVIVO_D1SCL_UPDATE 0x65cc +# define AVIVO_D1SCL_UPDATE_LOCK (1 << 16) + +/* second crtc */ +#define AVIVO_D2CRTC_H_TOTAL 0x6800 +#define AVIVO_D2CRTC_H_BLANK_START_END 0x6804 +#define AVIVO_D2CRTC_H_SYNC_A 0x6808 +#define AVIVO_D2CRTC_H_SYNC_A_CNTL 0x680c +#define AVIVO_D2CRTC_H_SYNC_B 0x6810 +#define AVIVO_D2CRTC_H_SYNC_B_CNTL 0x6814 + +#define AVIVO_D2CRTC_V_TOTAL 0x6820 +#define AVIVO_D2CRTC_V_BLANK_START_END 0x6824 +#define AVIVO_D2CRTC_V_SYNC_A 0x6828 +#define AVIVO_D2CRTC_V_SYNC_A_CNTL 0x682c +#define AVIVO_D2CRTC_V_SYNC_B 0x6830 +#define AVIVO_D2CRTC_V_SYNC_B_CNTL 0x6834 + +#define AVIVO_D2CRTC_CONTROL 0x6880 +#define AVIVO_D2CRTC_BLANK_CONTROL 0x6884 +#define AVIVO_D2CRTC_INTERLACE_CONTROL 0x6888 +#define AVIVO_D2CRTC_INTERLACE_STATUS 0x688c +#define AVIVO_D2CRTC_STATUS_POSITION 0x68a0 +#define AVIVO_D2CRTC_FRAME_COUNT 0x68a4 +#define AVIVO_D2CRTC_STEREO_CONTROL 0x68c4 + +#define AVIVO_D2GRPH_ENABLE 0x6900 +#define AVIVO_D2GRPH_CONTROL 0x6904 +#define AVIVO_D2GRPH_LUT_SEL 0x6908 +#define AVIVO_D2GRPH_PRIMARY_SURFACE_ADDRESS 0x6910 +#define AVIVO_D2GRPH_SECONDARY_SURFACE_ADDRESS 0x6918 +#define AVIVO_D2GRPH_PITCH 0x6920 +#define AVIVO_D2GRPH_SURFACE_OFFSET_X 0x6924 +#define AVIVO_D2GRPH_SURFACE_OFFSET_Y 0x6928 +#define AVIVO_D2GRPH_X_START 0x692c +#define AVIVO_D2GRPH_Y_START 0x6930 +#define AVIVO_D2GRPH_X_END 0x6934 +#define AVIVO_D2GRPH_Y_END 0x6938 +#define AVIVO_D2GRPH_UPDATE 0x6944 +#define AVIVO_D2GRPH_FLIP_CONTROL 0x6948 + +#define AVIVO_D2CUR_CONTROL 0x6c00 +#define AVIVO_D2CUR_SURFACE_ADDRESS 0x6c08 +#define AVIVO_D2CUR_SIZE 0x6c10 +#define AVIVO_D2CUR_POSITION 0x6c14 + +#define AVIVO_D2MODE_VBLANK_STATUS 0x6d34 +#define AVIVO_D2MODE_VLINE_START_END 0x6d38 +#define AVIVO_D2MODE_VLINE_STATUS 0x6d3c +#define AVIVO_D2MODE_VIEWPORT_START 0x6d80 +#define AVIVO_D2MODE_VIEWPORT_SIZE 0x6d84 +#define AVIVO_D2MODE_EXT_OVERSCAN_LEFT_RIGHT 0x6d88 +#define AVIVO_D2MODE_EXT_OVERSCAN_TOP_BOTTOM 0x6d8c + +#define AVIVO_D2SCL_SCALER_ENABLE 0x6d90 +#define AVIVO_D2SCL_SCALER_TAP_CONTROL 0x6d94 + +#define AVIVO_DDIA_BIT_DEPTH_CONTROL 0x7214 + +#define AVIVO_DACA_ENABLE 0x7800 +# define AVIVO_DAC_ENABLE (1 << 0) +#define AVIVO_DACA_SOURCE_SELECT 0x7804 +# define AVIVO_DAC_SOURCE_CRTC1 (0 << 0) +# define AVIVO_DAC_SOURCE_CRTC2 (1 << 0) +# define AVIVO_DAC_SOURCE_TV (2 << 0) + +#define AVIVO_DACA_FORCE_OUTPUT_CNTL 0x783c +# define AVIVO_DACA_FORCE_OUTPUT_CNTL_FORCE_DATA_EN (1 << 0) +# define AVIVO_DACA_FORCE_OUTPUT_CNTL_DATA_SEL_SHIFT (8) +# define AVIVO_DACA_FORCE_OUTPUT_CNTL_DATA_SEL_BLUE (1 << 0) +# define AVIVO_DACA_FORCE_OUTPUT_CNTL_DATA_SEL_GREEN (1 << 1) +# define AVIVO_DACA_FORCE_OUTPUT_CNTL_DATA_SEL_RED (1 << 2) +# define AVIVO_DACA_FORCE_OUTPUT_CNTL_DATA_ON_BLANKB_ONLY (1 << 24) +#define AVIVO_DACA_POWERDOWN 0x7850 +# define AVIVO_DACA_POWERDOWN_POWERDOWN (1 << 0) +# define AVIVO_DACA_POWERDOWN_BLUE (1 << 8) +# define AVIVO_DACA_POWERDOWN_GREEN (1 << 16) +# define AVIVO_DACA_POWERDOWN_RED (1 << 24) + +#define AVIVO_DACB_ENABLE 0x7a00 +#define AVIVO_DACB_SOURCE_SELECT 0x7a04 +#define AVIVO_DACB_FORCE_OUTPUT_CNTL 0x7a3c +# define AVIVO_DACB_FORCE_OUTPUT_CNTL_FORCE_DATA_EN (1 << 0) +# define AVIVO_DACB_FORCE_OUTPUT_CNTL_DATA_SEL_SHIFT (8) +# define AVIVO_DACB_FORCE_OUTPUT_CNTL_DATA_SEL_BLUE (1 << 0) +# define AVIVO_DACB_FORCE_OUTPUT_CNTL_DATA_SEL_GREEN (1 << 1) +# define AVIVO_DACB_FORCE_OUTPUT_CNTL_DATA_SEL_RED (1 << 2) +# define AVIVO_DACB_FORCE_OUTPUT_CNTL_DATA_ON_BLANKB_ONLY (1 << 24) +#define AVIVO_DACB_POWERDOWN 0x7a50 +# define AVIVO_DACB_POWERDOWN_POWERDOWN (1 << 0) +# define AVIVO_DACB_POWERDOWN_BLUE (1 << 8) +# define AVIVO_DACB_POWERDOWN_GREEN (1 << 16) +# define AVIVO_DACB_POWERDOWN_RED + +#define AVIVO_TMDSA_CNTL 0x7880 +# define AVIVO_TMDSA_CNTL_ENABLE (1 << 0) +# define AVIVO_TMDSA_CNTL_HDMI_EN (1 << 2) +# define AVIVO_TMDSA_CNTL_HPD_MASK (1 << 4) +# define AVIVO_TMDSA_CNTL_HPD_SELECT (1 << 8) +# define AVIVO_TMDSA_CNTL_SYNC_PHASE (1 << 12) +# define AVIVO_TMDSA_CNTL_PIXEL_ENCODING (1 << 16) +# define AVIVO_TMDSA_CNTL_DUAL_LINK_ENABLE (1 << 24) +# define AVIVO_TMDSA_CNTL_SWAP (1 << 28) +#define AVIVO_TMDSA_SOURCE_SELECT 0x7884 +/* 78a8 appears to be some kind of (reasonably tolerant) clock? + * 78d0 definitely hits the transmitter, definitely clock. */ +/* MYSTERY1 This appears to control dithering? */ +#define AVIVO_TMDSA_BIT_DEPTH_CONTROL 0x7894 +# define AVIVO_TMDS_BIT_DEPTH_CONTROL_TRUNCATE_EN (1 << 0) +# define AVIVO_TMDS_BIT_DEPTH_CONTROL_TRUNCATE_DEPTH (1 << 4) +# define AVIVO_TMDS_BIT_DEPTH_CONTROL_SPATIAL_DITHER_EN (1 << 8) +# define AVIVO_TMDS_BIT_DEPTH_CONTROL_SPATIAL_DITHER_DEPTH (1 << 12) +# define AVIVO_TMDS_BIT_DEPTH_CONTROL_TEMPORAL_DITHER_EN (1 << 16) +# define AVIVO_TMDS_BIT_DEPTH_CONTROL_TEMPORAL_DITHER_DEPTH (1 << 20) +# define AVIVO_TMDS_BIT_DEPTH_CONTROL_TEMPORAL_LEVEL (1 << 24) +# define AVIVO_TMDS_BIT_DEPTH_CONTROL_TEMPORAL_DITHER_RESET (1 << 26) +#define AVIVO_TMDSA_DCBALANCER_CONTROL 0x78d0 +# define AVIVO_TMDSA_DCBALANCER_CONTROL_EN (1 << 0) +# define AVIVO_TMDSA_DCBALANCER_CONTROL_TEST_EN (1 << 8) +# define AVIVO_TMDSA_DCBALANCER_CONTROL_TEST_IN_SHIFT (16) +# define AVIVO_TMDSA_DCBALANCER_CONTROL_FORCE (1 << 24) +#define AVIVO_TMDSA_DATA_SYNCHRONIZATION 0x78d8 +# define AVIVO_TMDSA_DATA_SYNCHRONIZATION_DSYNSEL (1 << 0) +# define AVIVO_TMDSA_DATA_SYNCHRONIZATION_PFREQCHG (1 << 8) +#define AVIVO_TMDSA_CLOCK_ENABLE 0x7900 +#define AVIVO_TMDSA_TRANSMITTER_ENABLE 0x7904 +# define AVIVO_TMDSA_TRANSMITTER_ENABLE_TX0_ENABLE (1 << 0) +# define AVIVO_TMDSA_TRANSMITTER_ENABLE_LNKC0EN (1 << 1) +# define AVIVO_TMDSA_TRANSMITTER_ENABLE_LNKD00EN (1 << 2) +# define AVIVO_TMDSA_TRANSMITTER_ENABLE_LNKD01EN (1 << 3) +# define AVIVO_TMDSA_TRANSMITTER_ENABLE_LNKD02EN (1 << 4) +# define AVIVO_TMDSA_TRANSMITTER_ENABLE_TX1_ENABLE (1 << 8) +# define AVIVO_TMDSA_TRANSMITTER_ENABLE_LNKD10EN (1 << 10) +# define AVIVO_TMDSA_TRANSMITTER_ENABLE_LNKD11EN (1 << 11) +# define AVIVO_TMDSA_TRANSMITTER_ENABLE_LNKD12EN (1 << 12) +# define AVIVO_TMDSA_TRANSMITTER_ENABLE_TX_ENABLE_HPD_MASK (1 << 16) +# define AVIVO_TMDSA_TRANSMITTER_ENABLE_LNKCEN_HPD_MASK (1 << 17) +# define AVIVO_TMDSA_TRANSMITTER_ENABLE_LNKDEN_HPD_MASK (1 << 18) + +#define AVIVO_TMDSA_TRANSMITTER_CONTROL 0x7910 +# define AVIVO_TMDSA_TRANSMITTER_CONTROL_PLL_ENABLE (1 << 0) +# define AVIVO_TMDSA_TRANSMITTER_CONTROL_PLL_RESET (1 << 1) +# define AVIVO_TMDSA_TRANSMITTER_CONTROL_PLL_HPD_MASK_SHIFT (2) +# define AVIVO_TMDSA_TRANSMITTER_CONTROL_IDSCKSEL (1 << 4) +# define AVIVO_TMDSA_TRANSMITTER_CONTROL_BGSLEEP (1 << 5) +# define AVIVO_TMDSA_TRANSMITTER_CONTROL_PLL_PWRUP_SEQ_EN (1 << 6) +# define AVIVO_TMDSA_TRANSMITTER_CONTROL_TMCLK (1 << 8) +# define AVIVO_TMDSA_TRANSMITTER_CONTROL_TMCLK_FROM_PADS (1 << 13) +# define AVIVO_TMDSA_TRANSMITTER_CONTROL_TDCLK (1 << 14) +# define AVIVO_TMDSA_TRANSMITTER_CONTROL_TDCLK_FROM_PADS (1 << 15) +# define AVIVO_TMDSA_TRANSMITTER_CONTROL_CLK_PATTERN_SHIFT (16) +# define AVIVO_TMDSA_TRANSMITTER_CONTROL_BYPASS_PLL (1 << 28) +# define AVIVO_TMDSA_TRANSMITTER_CONTROL_USE_CLK_DATA (1 << 29) +# define AVIVO_TMDSA_TRANSMITTER_CONTROL_INPUT_TEST_CLK_SEL (1 << 31) + +#define AVIVO_LVTMA_CNTL 0x7a80 +# define AVIVO_LVTMA_CNTL_ENABLE (1 << 0) +# define AVIVO_LVTMA_CNTL_HDMI_EN (1 << 2) +# define AVIVO_LVTMA_CNTL_HPD_MASK (1 << 4) +# define AVIVO_LVTMA_CNTL_HPD_SELECT (1 << 8) +# define AVIVO_LVTMA_CNTL_SYNC_PHASE (1 << 12) +# define AVIVO_LVTMA_CNTL_PIXEL_ENCODING (1 << 16) +# define AVIVO_LVTMA_CNTL_DUAL_LINK_ENABLE (1 << 24) +# define AVIVO_LVTMA_CNTL_SWAP (1 << 28) +#define AVIVO_LVTMA_SOURCE_SELECT 0x7a84 +#define AVIVO_LVTMA_COLOR_FORMAT 0x7a88 +#define AVIVO_LVTMA_BIT_DEPTH_CONTROL 0x7a94 +# define AVIVO_LVTMA_BIT_DEPTH_CONTROL_TRUNCATE_EN (1 << 0) +# define AVIVO_LVTMA_BIT_DEPTH_CONTROL_TRUNCATE_DEPTH (1 << 4) +# define AVIVO_LVTMA_BIT_DEPTH_CONTROL_SPATIAL_DITHER_EN (1 << 8) +# define AVIVO_LVTMA_BIT_DEPTH_CONTROL_SPATIAL_DITHER_DEPTH (1 << 12) +# define AVIVO_LVTMA_BIT_DEPTH_CONTROL_TEMPORAL_DITHER_EN (1 << 16) +# define AVIVO_LVTMA_BIT_DEPTH_CONTROL_TEMPORAL_DITHER_DEPTH (1 << 20) +# define AVIVO_LVTMA_BIT_DEPTH_CONTROL_TEMPORAL_LEVEL (1 << 24) +# define AVIVO_LVTMA_BIT_DEPTH_CONTROL_TEMPORAL_DITHER_RESET (1 << 26) + + + +#define AVIVO_LVTMA_DCBALANCER_CONTROL 0x7ad0 +# define AVIVO_LVTMA_DCBALANCER_CONTROL_EN (1 << 0) +# define AVIVO_LVTMA_DCBALANCER_CONTROL_TEST_EN (1 << 8) +# define AVIVO_LVTMA_DCBALANCER_CONTROL_TEST_IN_SHIFT (16) +# define AVIVO_LVTMA_DCBALANCER_CONTROL_FORCE (1 << 24) + +#define AVIVO_LVTMA_DATA_SYNCHRONIZATION 0x78d8 +# define AVIVO_LVTMA_DATA_SYNCHRONIZATION_DSYNSEL (1 << 0) +# define AVIVO_LVTMA_DATA_SYNCHRONIZATION_PFREQCHG (1 << 8) +#define R500_LVTMA_CLOCK_ENABLE 0x7b00 +#define R600_LVTMA_CLOCK_ENABLE 0x7b04 + +#define R500_LVTMA_TRANSMITTER_ENABLE 0x7b04 +#define R600_LVTMA_TRANSMITTER_ENABLE 0x7b08 +# define AVIVO_LVTMA_TRANSMITTER_ENABLE_LNKC0EN (1 << 1) +# define AVIVO_LVTMA_TRANSMITTER_ENABLE_LNKD00EN (1 << 2) +# define AVIVO_LVTMA_TRANSMITTER_ENABLE_LNKD01EN (1 << 3) +# define AVIVO_LVTMA_TRANSMITTER_ENABLE_LNKD02EN (1 << 4) +# define AVIVO_LVTMA_TRANSMITTER_ENABLE_LNKD03EN (1 << 5) +# define AVIVO_LVTMA_TRANSMITTER_ENABLE_LNKC1EN (1 << 9) +# define AVIVO_LVTMA_TRANSMITTER_ENABLE_LNKD10EN (1 << 10) +# define AVIVO_LVTMA_TRANSMITTER_ENABLE_LNKD11EN (1 << 11) +# define AVIVO_LVTMA_TRANSMITTER_ENABLE_LNKD12EN (1 << 12) +# define AVIVO_LVTMA_TRANSMITTER_ENABLE_LNKCEN_HPD_MASK (1 << 17) +# define AVIVO_LVTMA_TRANSMITTER_ENABLE_LNKDEN_HPD_MASK (1 << 18) + +#define R500_LVTMA_TRANSMITTER_CONTROL 0x7b10 +#define R600_LVTMA_TRANSMITTER_CONTROL 0x7b14 +# define AVIVO_LVTMA_TRANSMITTER_CONTROL_PLL_ENABLE (1 << 0) +# define AVIVO_LVTMA_TRANSMITTER_CONTROL_PLL_RESET (1 << 1) +# define AVIVO_LVTMA_TRANSMITTER_CONTROL_PLL_HPD_MASK_SHIFT (2) +# define AVIVO_LVTMA_TRANSMITTER_CONTROL_IDSCKSEL (1 << 4) +# define AVIVO_LVTMA_TRANSMITTER_CONTROL_BGSLEEP (1 << 5) +# define AVIVO_LVTMA_TRANSMITTER_CONTROL_PLL_PWRUP_SEQ_EN (1 << 6) +# define AVIVO_LVTMA_TRANSMITTER_CONTROL_TMCLK (1 << 8) +# define AVIVO_LVTMA_TRANSMITTER_CONTROL_TMCLK_FROM_PADS (1 << 13) +# define AVIVO_LVTMA_TRANSMITTER_CONTROL_TDCLK (1 << 14) +# define AVIVO_LVTMA_TRANSMITTER_CONTROL_TDCLK_FROM_PADS (1 << 15) +# define AVIVO_LVTMA_TRANSMITTER_CONTROL_CLK_PATTERN_SHIFT (16) +# define AVIVO_LVTMA_TRANSMITTER_CONTROL_BYPASS_PLL (1 << 28) +# define AVIVO_LVTMA_TRANSMITTER_CONTROL_USE_CLK_DATA (1 << 29) +# define AVIVO_LVTMA_TRANSMITTER_CONTROL_INPUT_TEST_CLK_SEL (1 << 31) + +#define R500_LVTMA_PWRSEQ_CNTL 0x7af0 +#define R600_LVTMA_PWRSEQ_CNTL 0x7af4 +# define AVIVO_LVTMA_PWRSEQ_EN (1 << 0) +# define AVIVO_LVTMA_PWRSEQ_PLL_ENABLE_MASK (1 << 2) +# define AVIVO_LVTMA_PWRSEQ_PLL_RESET_MASK (1 << 3) +# define AVIVO_LVTMA_PWRSEQ_TARGET_STATE (1 << 4) +# define AVIVO_LVTMA_SYNCEN (1 << 8) +# define AVIVO_LVTMA_SYNCEN_OVRD (1 << 9) +# define AVIVO_LVTMA_SYNCEN_POL (1 << 10) +# define AVIVO_LVTMA_DIGON (1 << 16) +# define AVIVO_LVTMA_DIGON_OVRD (1 << 17) +# define AVIVO_LVTMA_DIGON_POL (1 << 18) +# define AVIVO_LVTMA_BLON (1 << 24) +# define AVIVO_LVTMA_BLON_OVRD (1 << 25) +# define AVIVO_LVTMA_BLON_POL (1 << 26) + +#define R500_LVTMA_PWRSEQ_STATE 0x7af4 +#define R600_LVTMA_PWRSEQ_STATE 0x7af8 +# define AVIVO_LVTMA_PWRSEQ_STATE_TARGET_STATE_R (1 << 0) +# define AVIVO_LVTMA_PWRSEQ_STATE_DIGON (1 << 1) +# define AVIVO_LVTMA_PWRSEQ_STATE_SYNCEN (1 << 2) +# define AVIVO_LVTMA_PWRSEQ_STATE_BLON (1 << 3) +# define AVIVO_LVTMA_PWRSEQ_STATE_DONE (1 << 4) +# define AVIVO_LVTMA_PWRSEQ_STATE_STATUS_SHIFT (8) + +#define AVIVO_LVDS_BACKLIGHT_CNTL 0x7af8 +# define AVIVO_LVDS_BACKLIGHT_CNTL_EN (1 << 0) +# define AVIVO_LVDS_BACKLIGHT_LEVEL_MASK 0x0000ff00 +# define AVIVO_LVDS_BACKLIGHT_LEVEL_SHIFT 8 + +#define AVIVO_DVOA_BIT_DEPTH_CONTROL 0x7988 + +#define AVIVO_DC_GPIO_HPD_A 0x7e94 +#define AVIVO_DC_GPIO_HPD_Y 0x7e9c + +#define AVIVO_DC_I2C_STATUS1 0x7d30 +# define AVIVO_DC_I2C_DONE (1 << 0) +# define AVIVO_DC_I2C_NACK (1 << 1) +# define AVIVO_DC_I2C_HALT (1 << 2) +# define AVIVO_DC_I2C_GO (1 << 3) +#define AVIVO_DC_I2C_RESET 0x7d34 +# define AVIVO_DC_I2C_SOFT_RESET (1 << 0) +# define AVIVO_DC_I2C_ABORT (1 << 8) +#define AVIVO_DC_I2C_CONTROL1 0x7d38 +# define AVIVO_DC_I2C_START (1 << 0) +# define AVIVO_DC_I2C_STOP (1 << 1) +# define AVIVO_DC_I2C_RECEIVE (1 << 2) +# define AVIVO_DC_I2C_EN (1 << 8) +# define AVIVO_DC_I2C_PIN_SELECT(x) ((x) << 16) +# define AVIVO_SEL_DDC1 0 +# define AVIVO_SEL_DDC2 1 +# define AVIVO_SEL_DDC3 2 +#define AVIVO_DC_I2C_CONTROL2 0x7d3c +# define AVIVO_DC_I2C_ADDR_COUNT(x) ((x) << 0) +# define AVIVO_DC_I2C_DATA_COUNT(x) ((x) << 8) +#define AVIVO_DC_I2C_CONTROL3 0x7d40 +# define AVIVO_DC_I2C_DATA_DRIVE_EN (1 << 0) +# define AVIVO_DC_I2C_DATA_DRIVE_SEL (1 << 1) +# define AVIVO_DC_I2C_CLK_DRIVE_EN (1 << 7) +# define AVIVO_DC_I2C_RD_INTRA_BYTE_DELAY(x) ((x) << 8) +# define AVIVO_DC_I2C_WR_INTRA_BYTE_DELAY(x) ((x) << 16) +# define AVIVO_DC_I2C_TIME_LIMIT(x) ((x) << 24) +#define AVIVO_DC_I2C_DATA 0x7d44 +#define AVIVO_DC_I2C_INTERRUPT_CONTROL 0x7d48 +# define AVIVO_DC_I2C_INTERRUPT_STATUS (1 << 0) +# define AVIVO_DC_I2C_INTERRUPT_AK (1 << 8) +# define AVIVO_DC_I2C_INTERRUPT_ENABLE (1 << 16) +#define AVIVO_DC_I2C_ARBITRATION 0x7d50 +# define AVIVO_DC_I2C_SW_WANTS_TO_USE_I2C (1 << 0) +# define AVIVO_DC_I2C_SW_CAN_USE_I2C (1 << 1) +# define AVIVO_DC_I2C_SW_DONE_USING_I2C (1 << 8) +# define AVIVO_DC_I2C_HW_NEEDS_I2C (1 << 9) +# define AVIVO_DC_I2C_ABORT_HDCP_I2C (1 << 16) +# define AVIVO_DC_I2C_HW_USING_I2C (1 << 17) + +#define AVIVO_DC_GPIO_DDC1_MASK 0x7e40 +#define AVIVO_DC_GPIO_DDC1_A 0x7e44 +#define AVIVO_DC_GPIO_DDC1_EN 0x7e48 +#define AVIVO_DC_GPIO_DDC1_Y 0x7e4c + +#define AVIVO_DC_GPIO_DDC2_MASK 0x7e50 +#define AVIVO_DC_GPIO_DDC2_A 0x7e54 +#define AVIVO_DC_GPIO_DDC2_EN 0x7e58 +#define AVIVO_DC_GPIO_DDC2_Y 0x7e5c + +#define AVIVO_DC_GPIO_DDC3_MASK 0x7e60 +#define AVIVO_DC_GPIO_DDC3_A 0x7e64 +#define AVIVO_DC_GPIO_DDC3_EN 0x7e68 +#define AVIVO_DC_GPIO_DDC3_Y 0x7e6c + +#define AVIVO_DISP_INTERRUPT_STATUS 0x7edc +# define AVIVO_D1_VBLANK_INTERRUPT (1 << 4) +# define AVIVO_D2_VBLANK_INTERRUPT (1 << 5) + +#endif diff --git a/sys/dev/drm2/radeon/r520.c b/sys/dev/drm2/radeon/r520.c new file mode 100644 index 00000000000..523c94aaf3b --- /dev/null +++ b/sys/dev/drm2/radeon/r520.c @@ -0,0 +1,330 @@ +/* + * Copyright 2008 Advanced Micro Devices, Inc. + * Copyright 2008 Red Hat Inc. + * Copyright 2009 Jerome Glisse. + * + * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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. + * + * Authors: Dave Airlie + * Alex Deucher + * Jerome Glisse + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include "radeon.h" +#include "radeon_asic.h" +#include "atom.h" +#include "r520d.h" + +/* This files gather functions specifics to: r520,rv530,rv560,rv570,r580 */ + +int r520_mc_wait_for_idle(struct radeon_device *rdev) +{ + unsigned i; + uint32_t tmp; + + for (i = 0; i < rdev->usec_timeout; i++) { + /* read MC_STATUS */ + tmp = RREG32_MC(R520_MC_STATUS); + if (tmp & R520_MC_STATUS_IDLE) { + return 0; + } + DRM_UDELAY(1); + } + return -1; +} + +static void r520_gpu_init(struct radeon_device *rdev) +{ + unsigned pipe_select_current, gb_pipe_select, tmp; + + rv515_vga_render_disable(rdev); + /* + * DST_PIPE_CONFIG 0x170C + * GB_TILE_CONFIG 0x4018 + * GB_FIFO_SIZE 0x4024 + * GB_PIPE_SELECT 0x402C + * GB_PIPE_SELECT2 0x4124 + * Z_PIPE_SHIFT 0 + * Z_PIPE_MASK 0x000000003 + * GB_FIFO_SIZE2 0x4128 + * SC_SFIFO_SIZE_SHIFT 0 + * SC_SFIFO_SIZE_MASK 0x000000003 + * SC_MFIFO_SIZE_SHIFT 2 + * SC_MFIFO_SIZE_MASK 0x00000000C + * FG_SFIFO_SIZE_SHIFT 4 + * FG_SFIFO_SIZE_MASK 0x000000030 + * ZB_MFIFO_SIZE_SHIFT 6 + * ZB_MFIFO_SIZE_MASK 0x0000000C0 + * GA_ENHANCE 0x4274 + * SU_REG_DEST 0x42C8 + */ + /* workaround for RV530 */ + if (rdev->family == CHIP_RV530) { + WREG32(0x4128, 0xFF); + } + r420_pipes_init(rdev); + gb_pipe_select = RREG32(R400_GB_PIPE_SELECT); + tmp = RREG32(R300_DST_PIPE_CONFIG); + pipe_select_current = (tmp >> 2) & 3; + tmp = (1 << pipe_select_current) | + (((gb_pipe_select >> 8) & 0xF) << 4); + WREG32_PLL(0x000D, tmp); + if (r520_mc_wait_for_idle(rdev)) { + DRM_ERROR("Failed to wait MC idle while " + "programming pipes. Bad things might happen.\n"); + } +} + +static void r520_vram_get_type(struct radeon_device *rdev) +{ + uint32_t tmp; + + rdev->mc.vram_width = 128; + rdev->mc.vram_is_ddr = true; + tmp = RREG32_MC(R520_MC_CNTL0); + switch ((tmp & R520_MEM_NUM_CHANNELS_MASK) >> R520_MEM_NUM_CHANNELS_SHIFT) { + case 0: + rdev->mc.vram_width = 32; + break; + case 1: + rdev->mc.vram_width = 64; + break; + case 2: + rdev->mc.vram_width = 128; + break; + case 3: + rdev->mc.vram_width = 256; + break; + default: + rdev->mc.vram_width = 128; + break; + } + if (tmp & R520_MC_CHANNEL_SIZE) + rdev->mc.vram_width *= 2; +} + +static void r520_mc_init(struct radeon_device *rdev) +{ + + r520_vram_get_type(rdev); + r100_vram_init_sizes(rdev); + radeon_vram_location(rdev, &rdev->mc, 0); + rdev->mc.gtt_base_align = 0; + if (!(rdev->flags & RADEON_IS_AGP)) + radeon_gtt_location(rdev, &rdev->mc); + radeon_update_bandwidth_info(rdev); +} + +static void r520_mc_program(struct radeon_device *rdev) +{ + struct rv515_mc_save save; + + /* Stops all mc clients */ + rv515_mc_stop(rdev, &save); + + /* Wait for mc idle */ + if (r520_mc_wait_for_idle(rdev)) + dev_warn(rdev->dev, "Wait MC idle timeout before updating MC.\n"); + /* Write VRAM size in case we are limiting it */ + WREG32(R_0000F8_CONFIG_MEMSIZE, rdev->mc.real_vram_size); + /* Program MC, should be a 32bits limited address space */ + WREG32_MC(R_000004_MC_FB_LOCATION, + S_000004_MC_FB_START(rdev->mc.vram_start >> 16) | + S_000004_MC_FB_TOP(rdev->mc.vram_end >> 16)); + WREG32(R_000134_HDP_FB_LOCATION, + S_000134_HDP_FB_START(rdev->mc.vram_start >> 16)); + if (rdev->flags & RADEON_IS_AGP) { + WREG32_MC(R_000005_MC_AGP_LOCATION, + S_000005_MC_AGP_START(rdev->mc.gtt_start >> 16) | + S_000005_MC_AGP_TOP(rdev->mc.gtt_end >> 16)); + WREG32_MC(R_000006_AGP_BASE, lower_32_bits(rdev->mc.agp_base)); + WREG32_MC(R_000007_AGP_BASE_2, + S_000007_AGP_BASE_ADDR_2(upper_32_bits(rdev->mc.agp_base))); + } else { + WREG32_MC(R_000005_MC_AGP_LOCATION, 0xFFFFFFFF); + WREG32_MC(R_000006_AGP_BASE, 0); + WREG32_MC(R_000007_AGP_BASE_2, 0); + } + + rv515_mc_resume(rdev, &save); +} + +static int r520_startup(struct radeon_device *rdev) +{ + int r; + + r520_mc_program(rdev); + /* Resume clock */ + rv515_clock_startup(rdev); + /* Initialize GPU configuration (# pipes, ...) */ + r520_gpu_init(rdev); + /* Initialize GART (initialize after TTM so we can allocate + * memory through TTM but finalize after TTM) */ + if (rdev->flags & RADEON_IS_PCIE) { + r = rv370_pcie_gart_enable(rdev); + if (r) + return r; + } + + /* allocate wb buffer */ + r = radeon_wb_init(rdev); + if (r) + return r; + + r = radeon_fence_driver_start_ring(rdev, RADEON_RING_TYPE_GFX_INDEX); + if (r) { + dev_err(rdev->dev, "failed initializing CP fences (%d).\n", r); + return r; + } + + /* Enable IRQ */ + rs600_irq_set(rdev); + rdev->config.r300.hdp_cntl = RREG32(RADEON_HOST_PATH_CNTL); + /* 1M ring buffer */ + r = r100_cp_init(rdev, 1024 * 1024); + if (r) { + dev_err(rdev->dev, "failed initializing CP (%d).\n", r); + return r; + } + + r = radeon_ib_pool_init(rdev); + if (r) { + dev_err(rdev->dev, "IB initialization failed (%d).\n", r); + return r; + } + + return 0; +} + +int r520_resume(struct radeon_device *rdev) +{ + int r; + + /* Make sur GART are not working */ + if (rdev->flags & RADEON_IS_PCIE) + rv370_pcie_gart_disable(rdev); + /* Resume clock before doing reset */ + rv515_clock_startup(rdev); + /* Reset gpu before posting otherwise ATOM will enter infinite loop */ + if (radeon_asic_reset(rdev)) { + dev_warn(rdev->dev, "GPU reset failed ! (0xE40=0x%08X, 0x7C0=0x%08X)\n", + RREG32(R_000E40_RBBM_STATUS), + RREG32(R_0007C0_CP_STAT)); + } + /* post */ + atom_asic_init(rdev->mode_info.atom_context); + /* Resume clock after posting */ + rv515_clock_startup(rdev); + /* Initialize surface registers */ + radeon_surface_init(rdev); + + rdev->accel_working = true; + r = r520_startup(rdev); + if (r) { + rdev->accel_working = false; + } + return r; +} + +int r520_init(struct radeon_device *rdev) +{ + int r; + + /* Initialize scratch registers */ + radeon_scratch_init(rdev); + /* Initialize surface registers */ + radeon_surface_init(rdev); + /* restore some register to sane defaults */ + r100_restore_sanity(rdev); + /* TODO: disable VGA need to use VGA request */ + /* BIOS*/ + if (!radeon_get_bios(rdev)) { + if (ASIC_IS_AVIVO(rdev)) + return -EINVAL; + } + if (rdev->is_atom_bios) { + r = radeon_atombios_init(rdev); + if (r) + return r; + } else { + dev_err(rdev->dev, "Expecting atombios for RV515 GPU\n"); + return -EINVAL; + } + /* Reset gpu before posting otherwise ATOM will enter infinite loop */ + if (radeon_asic_reset(rdev)) { + dev_warn(rdev->dev, + "GPU reset failed ! (0xE40=0x%08X, 0x7C0=0x%08X)\n", + RREG32(R_000E40_RBBM_STATUS), + RREG32(R_0007C0_CP_STAT)); + } + /* check if cards are posted or not */ + if (radeon_boot_test_post_card(rdev) == false) + return -EINVAL; + + if (!radeon_card_posted(rdev) && rdev->bios) { + DRM_INFO("GPU not posted. posting now...\n"); + atom_asic_init(rdev->mode_info.atom_context); + } + /* Initialize clocks */ + radeon_get_clock_info(rdev->ddev); + /* initialize AGP */ + if (rdev->flags & RADEON_IS_AGP) { + r = radeon_agp_init(rdev); + if (r) { + radeon_agp_disable(rdev); + } + } + /* initialize memory controller */ + r520_mc_init(rdev); + rv515_debugfs(rdev); + /* Fence driver */ + r = radeon_fence_driver_init(rdev); + if (r) + return r; + r = radeon_irq_kms_init(rdev); + if (r) + return r; + /* Memory manager */ + r = radeon_bo_init(rdev); + if (r) + return r; + r = rv370_pcie_gart_init(rdev); + if (r) + return r; + rv515_set_safe_registers(rdev); + + rdev->accel_working = true; + r = r520_startup(rdev); + if (r) { + /* Somethings want wront with the accel init stop accel */ + dev_err(rdev->dev, "Disabling GPU acceleration\n"); + r100_cp_fini(rdev); + radeon_wb_fini(rdev); + radeon_ib_pool_fini(rdev); + radeon_irq_kms_fini(rdev); + rv370_pcie_gart_fini(rdev); + radeon_agp_fini(rdev); + rdev->accel_working = false; + } + return 0; +} diff --git a/sys/dev/drm2/radeon/r520d.h b/sys/dev/drm2/radeon/r520d.h new file mode 100644 index 00000000000..6fbd54cbcd6 --- /dev/null +++ b/sys/dev/drm2/radeon/r520d.h @@ -0,0 +1,190 @@ +/* + * Copyright 2008 Advanced Micro Devices, Inc. + * Copyright 2008 Red Hat Inc. + * Copyright 2009 Jerome Glisse. + * + * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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. + * + * Authors: Dave Airlie + * Alex Deucher + * Jerome Glisse + */ +#ifndef __R520D_H__ +#define __R520D_H__ + +#include +__FBSDID("$FreeBSD$"); + +/* Registers */ +#define R_0000F8_CONFIG_MEMSIZE 0x0000F8 +#define S_0000F8_CONFIG_MEMSIZE(x) (((x) & 0xFFFFFFFF) << 0) +#define G_0000F8_CONFIG_MEMSIZE(x) (((x) >> 0) & 0xFFFFFFFF) +#define C_0000F8_CONFIG_MEMSIZE 0x00000000 +#define R_000134_HDP_FB_LOCATION 0x000134 +#define S_000134_HDP_FB_START(x) (((x) & 0xFFFF) << 0) +#define G_000134_HDP_FB_START(x) (((x) >> 0) & 0xFFFF) +#define C_000134_HDP_FB_START 0xFFFF0000 +#define R_0007C0_CP_STAT 0x0007C0 +#define S_0007C0_MRU_BUSY(x) (((x) & 0x1) << 0) +#define G_0007C0_MRU_BUSY(x) (((x) >> 0) & 0x1) +#define C_0007C0_MRU_BUSY 0xFFFFFFFE +#define S_0007C0_MWU_BUSY(x) (((x) & 0x1) << 1) +#define G_0007C0_MWU_BUSY(x) (((x) >> 1) & 0x1) +#define C_0007C0_MWU_BUSY 0xFFFFFFFD +#define S_0007C0_RSIU_BUSY(x) (((x) & 0x1) << 2) +#define G_0007C0_RSIU_BUSY(x) (((x) >> 2) & 0x1) +#define C_0007C0_RSIU_BUSY 0xFFFFFFFB +#define S_0007C0_RCIU_BUSY(x) (((x) & 0x1) << 3) +#define G_0007C0_RCIU_BUSY(x) (((x) >> 3) & 0x1) +#define C_0007C0_RCIU_BUSY 0xFFFFFFF7 +#define S_0007C0_CSF_PRIMARY_BUSY(x) (((x) & 0x1) << 9) +#define G_0007C0_CSF_PRIMARY_BUSY(x) (((x) >> 9) & 0x1) +#define C_0007C0_CSF_PRIMARY_BUSY 0xFFFFFDFF +#define S_0007C0_CSF_INDIRECT_BUSY(x) (((x) & 0x1) << 10) +#define G_0007C0_CSF_INDIRECT_BUSY(x) (((x) >> 10) & 0x1) +#define C_0007C0_CSF_INDIRECT_BUSY 0xFFFFFBFF +#define S_0007C0_CSQ_PRIMARY_BUSY(x) (((x) & 0x1) << 11) +#define G_0007C0_CSQ_PRIMARY_BUSY(x) (((x) >> 11) & 0x1) +#define C_0007C0_CSQ_PRIMARY_BUSY 0xFFFFF7FF +#define S_0007C0_CSQ_INDIRECT_BUSY(x) (((x) & 0x1) << 12) +#define G_0007C0_CSQ_INDIRECT_BUSY(x) (((x) >> 12) & 0x1) +#define C_0007C0_CSQ_INDIRECT_BUSY 0xFFFFEFFF +#define S_0007C0_CSI_BUSY(x) (((x) & 0x1) << 13) +#define G_0007C0_CSI_BUSY(x) (((x) >> 13) & 0x1) +#define C_0007C0_CSI_BUSY 0xFFFFDFFF +#define S_0007C0_CSF_INDIRECT2_BUSY(x) (((x) & 0x1) << 14) +#define G_0007C0_CSF_INDIRECT2_BUSY(x) (((x) >> 14) & 0x1) +#define C_0007C0_CSF_INDIRECT2_BUSY 0xFFFFBFFF +#define S_0007C0_CSQ_INDIRECT2_BUSY(x) (((x) & 0x1) << 15) +#define G_0007C0_CSQ_INDIRECT2_BUSY(x) (((x) >> 15) & 0x1) +#define C_0007C0_CSQ_INDIRECT2_BUSY 0xFFFF7FFF +#define S_0007C0_GUIDMA_BUSY(x) (((x) & 0x1) << 28) +#define G_0007C0_GUIDMA_BUSY(x) (((x) >> 28) & 0x1) +#define C_0007C0_GUIDMA_BUSY 0xEFFFFFFF +#define S_0007C0_VIDDMA_BUSY(x) (((x) & 0x1) << 29) +#define G_0007C0_VIDDMA_BUSY(x) (((x) >> 29) & 0x1) +#define C_0007C0_VIDDMA_BUSY 0xDFFFFFFF +#define S_0007C0_CMDSTRM_BUSY(x) (((x) & 0x1) << 30) +#define G_0007C0_CMDSTRM_BUSY(x) (((x) >> 30) & 0x1) +#define C_0007C0_CMDSTRM_BUSY 0xBFFFFFFF +#define S_0007C0_CP_BUSY(x) (((x) & 0x1) << 31) +#define G_0007C0_CP_BUSY(x) (((x) >> 31) & 0x1) +#define C_0007C0_CP_BUSY 0x7FFFFFFF +#define R_000E40_RBBM_STATUS 0x000E40 +#define S_000E40_CMDFIFO_AVAIL(x) (((x) & 0x7F) << 0) +#define G_000E40_CMDFIFO_AVAIL(x) (((x) >> 0) & 0x7F) +#define C_000E40_CMDFIFO_AVAIL 0xFFFFFF80 +#define S_000E40_HIRQ_ON_RBB(x) (((x) & 0x1) << 8) +#define G_000E40_HIRQ_ON_RBB(x) (((x) >> 8) & 0x1) +#define C_000E40_HIRQ_ON_RBB 0xFFFFFEFF +#define S_000E40_CPRQ_ON_RBB(x) (((x) & 0x1) << 9) +#define G_000E40_CPRQ_ON_RBB(x) (((x) >> 9) & 0x1) +#define C_000E40_CPRQ_ON_RBB 0xFFFFFDFF +#define S_000E40_CFRQ_ON_RBB(x) (((x) & 0x1) << 10) +#define G_000E40_CFRQ_ON_RBB(x) (((x) >> 10) & 0x1) +#define C_000E40_CFRQ_ON_RBB 0xFFFFFBFF +#define S_000E40_HIRQ_IN_RTBUF(x) (((x) & 0x1) << 11) +#define G_000E40_HIRQ_IN_RTBUF(x) (((x) >> 11) & 0x1) +#define C_000E40_HIRQ_IN_RTBUF 0xFFFFF7FF +#define S_000E40_CPRQ_IN_RTBUF(x) (((x) & 0x1) << 12) +#define G_000E40_CPRQ_IN_RTBUF(x) (((x) >> 12) & 0x1) +#define C_000E40_CPRQ_IN_RTBUF 0xFFFFEFFF +#define S_000E40_CFRQ_IN_RTBUF(x) (((x) & 0x1) << 13) +#define G_000E40_CFRQ_IN_RTBUF(x) (((x) >> 13) & 0x1) +#define C_000E40_CFRQ_IN_RTBUF 0xFFFFDFFF +#define S_000E40_CF_PIPE_BUSY(x) (((x) & 0x1) << 14) +#define G_000E40_CF_PIPE_BUSY(x) (((x) >> 14) & 0x1) +#define C_000E40_CF_PIPE_BUSY 0xFFFFBFFF +#define S_000E40_ENG_EV_BUSY(x) (((x) & 0x1) << 15) +#define G_000E40_ENG_EV_BUSY(x) (((x) >> 15) & 0x1) +#define C_000E40_ENG_EV_BUSY 0xFFFF7FFF +#define S_000E40_CP_CMDSTRM_BUSY(x) (((x) & 0x1) << 16) +#define G_000E40_CP_CMDSTRM_BUSY(x) (((x) >> 16) & 0x1) +#define C_000E40_CP_CMDSTRM_BUSY 0xFFFEFFFF +#define S_000E40_E2_BUSY(x) (((x) & 0x1) << 17) +#define G_000E40_E2_BUSY(x) (((x) >> 17) & 0x1) +#define C_000E40_E2_BUSY 0xFFFDFFFF +#define S_000E40_RB2D_BUSY(x) (((x) & 0x1) << 18) +#define G_000E40_RB2D_BUSY(x) (((x) >> 18) & 0x1) +#define C_000E40_RB2D_BUSY 0xFFFBFFFF +#define S_000E40_RB3D_BUSY(x) (((x) & 0x1) << 19) +#define G_000E40_RB3D_BUSY(x) (((x) >> 19) & 0x1) +#define C_000E40_RB3D_BUSY 0xFFF7FFFF +#define S_000E40_VAP_BUSY(x) (((x) & 0x1) << 20) +#define G_000E40_VAP_BUSY(x) (((x) >> 20) & 0x1) +#define C_000E40_VAP_BUSY 0xFFEFFFFF +#define S_000E40_RE_BUSY(x) (((x) & 0x1) << 21) +#define G_000E40_RE_BUSY(x) (((x) >> 21) & 0x1) +#define C_000E40_RE_BUSY 0xFFDFFFFF +#define S_000E40_TAM_BUSY(x) (((x) & 0x1) << 22) +#define G_000E40_TAM_BUSY(x) (((x) >> 22) & 0x1) +#define C_000E40_TAM_BUSY 0xFFBFFFFF +#define S_000E40_TDM_BUSY(x) (((x) & 0x1) << 23) +#define G_000E40_TDM_BUSY(x) (((x) >> 23) & 0x1) +#define C_000E40_TDM_BUSY 0xFF7FFFFF +#define S_000E40_PB_BUSY(x) (((x) & 0x1) << 24) +#define G_000E40_PB_BUSY(x) (((x) >> 24) & 0x1) +#define C_000E40_PB_BUSY 0xFEFFFFFF +#define S_000E40_TIM_BUSY(x) (((x) & 0x1) << 25) +#define G_000E40_TIM_BUSY(x) (((x) >> 25) & 0x1) +#define C_000E40_TIM_BUSY 0xFDFFFFFF +#define S_000E40_GA_BUSY(x) (((x) & 0x1) << 26) +#define G_000E40_GA_BUSY(x) (((x) >> 26) & 0x1) +#define C_000E40_GA_BUSY 0xFBFFFFFF +#define S_000E40_CBA2D_BUSY(x) (((x) & 0x1) << 27) +#define G_000E40_CBA2D_BUSY(x) (((x) >> 27) & 0x1) +#define C_000E40_CBA2D_BUSY 0xF7FFFFFF +#define S_000E40_RBBM_HIBUSY(x) (((x) & 0x1) << 28) +#define G_000E40_RBBM_HIBUSY(x) (((x) >> 28) & 0x1) +#define C_000E40_RBBM_HIBUSY 0xEFFFFFFF +#define S_000E40_SKID_CFBUSY(x) (((x) & 0x1) << 29) +#define G_000E40_SKID_CFBUSY(x) (((x) >> 29) & 0x1) +#define C_000E40_SKID_CFBUSY 0xDFFFFFFF +#define S_000E40_VAP_VF_BUSY(x) (((x) & 0x1) << 30) +#define G_000E40_VAP_VF_BUSY(x) (((x) >> 30) & 0x1) +#define C_000E40_VAP_VF_BUSY 0xBFFFFFFF +#define S_000E40_GUI_ACTIVE(x) (((x) & 0x1) << 31) +#define G_000E40_GUI_ACTIVE(x) (((x) >> 31) & 0x1) +#define C_000E40_GUI_ACTIVE 0x7FFFFFFF + + +#define R_000004_MC_FB_LOCATION 0x000004 +#define S_000004_MC_FB_START(x) (((x) & 0xFFFF) << 0) +#define G_000004_MC_FB_START(x) (((x) >> 0) & 0xFFFF) +#define C_000004_MC_FB_START 0xFFFF0000 +#define S_000004_MC_FB_TOP(x) (((x) & 0xFFFF) << 16) +#define G_000004_MC_FB_TOP(x) (((x) >> 16) & 0xFFFF) +#define C_000004_MC_FB_TOP 0x0000FFFF +#define R_000005_MC_AGP_LOCATION 0x000005 +#define S_000005_MC_AGP_START(x) (((x) & 0xFFFF) << 0) +#define G_000005_MC_AGP_START(x) (((x) >> 0) & 0xFFFF) +#define C_000005_MC_AGP_START 0xFFFF0000 +#define S_000005_MC_AGP_TOP(x) (((x) & 0xFFFF) << 16) +#define G_000005_MC_AGP_TOP(x) (((x) >> 16) & 0xFFFF) +#define C_000005_MC_AGP_TOP 0x0000FFFF +#define R_000006_AGP_BASE 0x000006 +#define S_000006_AGP_BASE_ADDR(x) (((x) & 0xFFFFFFFF) << 0) +#define G_000006_AGP_BASE_ADDR(x) (((x) >> 0) & 0xFFFFFFFF) +#define C_000006_AGP_BASE_ADDR 0x00000000 +#define R_000007_AGP_BASE_2 0x000007 +#define S_000007_AGP_BASE_ADDR_2(x) (((x) & 0xF) << 0) +#define G_000007_AGP_BASE_ADDR_2(x) (((x) >> 0) & 0xF) +#define C_000007_AGP_BASE_ADDR_2 0xFFFFFFF0 + +#endif diff --git a/sys/dev/drm2/radeon/r600.c b/sys/dev/drm2/radeon/r600.c new file mode 100644 index 00000000000..b9d41bcaed4 --- /dev/null +++ b/sys/dev/drm2/radeon/r600.c @@ -0,0 +1,4384 @@ +/* + * Copyright 2008 Advanced Micro Devices, Inc. + * Copyright 2008 Red Hat Inc. + * Copyright 2009 Jerome Glisse. + * + * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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. + * + * Authors: Dave Airlie + * Alex Deucher + * Jerome Glisse + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include "radeon.h" +#include "radeon_asic.h" +#include "radeon_mode.h" +#include "r600d.h" +#include "atom.h" +#include "avivod.h" + +#define PFP_UCODE_SIZE 576 +#define PM4_UCODE_SIZE 1792 +#define RLC_UCODE_SIZE 768 +#define R700_PFP_UCODE_SIZE 848 +#define R700_PM4_UCODE_SIZE 1360 +#define R700_RLC_UCODE_SIZE 1024 +#define EVERGREEN_PFP_UCODE_SIZE 1120 +#define EVERGREEN_PM4_UCODE_SIZE 1376 +#define EVERGREEN_RLC_UCODE_SIZE 768 +#define CAYMAN_RLC_UCODE_SIZE 1024 +#define ARUBA_RLC_UCODE_SIZE 1536 + +#ifdef DUMBBELL_WIP +/* Firmware Names */ +MODULE_FIRMWARE("radeon/R600_pfp.bin"); +MODULE_FIRMWARE("radeon/R600_me.bin"); +MODULE_FIRMWARE("radeon/RV610_pfp.bin"); +MODULE_FIRMWARE("radeon/RV610_me.bin"); +MODULE_FIRMWARE("radeon/RV630_pfp.bin"); +MODULE_FIRMWARE("radeon/RV630_me.bin"); +MODULE_FIRMWARE("radeon/RV620_pfp.bin"); +MODULE_FIRMWARE("radeon/RV620_me.bin"); +MODULE_FIRMWARE("radeon/RV635_pfp.bin"); +MODULE_FIRMWARE("radeon/RV635_me.bin"); +MODULE_FIRMWARE("radeon/RV670_pfp.bin"); +MODULE_FIRMWARE("radeon/RV670_me.bin"); +MODULE_FIRMWARE("radeon/RS780_pfp.bin"); +MODULE_FIRMWARE("radeon/RS780_me.bin"); +MODULE_FIRMWARE("radeon/RV770_pfp.bin"); +MODULE_FIRMWARE("radeon/RV770_me.bin"); +MODULE_FIRMWARE("radeon/RV730_pfp.bin"); +MODULE_FIRMWARE("radeon/RV730_me.bin"); +MODULE_FIRMWARE("radeon/RV710_pfp.bin"); +MODULE_FIRMWARE("radeon/RV710_me.bin"); +MODULE_FIRMWARE("radeon/R600_rlc.bin"); +MODULE_FIRMWARE("radeon/R700_rlc.bin"); +MODULE_FIRMWARE("radeon/CEDAR_pfp.bin"); +MODULE_FIRMWARE("radeon/CEDAR_me.bin"); +MODULE_FIRMWARE("radeon/CEDAR_rlc.bin"); +MODULE_FIRMWARE("radeon/REDWOOD_pfp.bin"); +MODULE_FIRMWARE("radeon/REDWOOD_me.bin"); +MODULE_FIRMWARE("radeon/REDWOOD_rlc.bin"); +MODULE_FIRMWARE("radeon/JUNIPER_pfp.bin"); +MODULE_FIRMWARE("radeon/JUNIPER_me.bin"); +MODULE_FIRMWARE("radeon/JUNIPER_rlc.bin"); +MODULE_FIRMWARE("radeon/CYPRESS_pfp.bin"); +MODULE_FIRMWARE("radeon/CYPRESS_me.bin"); +MODULE_FIRMWARE("radeon/CYPRESS_rlc.bin"); +MODULE_FIRMWARE("radeon/PALM_pfp.bin"); +MODULE_FIRMWARE("radeon/PALM_me.bin"); +MODULE_FIRMWARE("radeon/SUMO_rlc.bin"); +MODULE_FIRMWARE("radeon/SUMO_pfp.bin"); +MODULE_FIRMWARE("radeon/SUMO_me.bin"); +MODULE_FIRMWARE("radeon/SUMO2_pfp.bin"); +MODULE_FIRMWARE("radeon/SUMO2_me.bin"); +#endif /* DUMBBELL_WIP */ + +int r600_debugfs_mc_info_init(struct radeon_device *rdev); + +/* r600,rv610,rv630,rv620,rv635,rv670 */ +static void r600_gpu_init(struct radeon_device *rdev); +void r600_irq_disable(struct radeon_device *rdev); +static void r600_pcie_gen2_enable(struct radeon_device *rdev); + +/* get temperature in millidegrees */ +int rv6xx_get_temp(struct radeon_device *rdev) +{ + u32 temp = (RREG32(CG_THERMAL_STATUS) & ASIC_T_MASK) >> + ASIC_T_SHIFT; + int actual_temp = temp & 0xff; + + if (temp & 0x100) + actual_temp -= 256; + + return actual_temp * 1000; +} + +void r600_pm_get_dynpm_state(struct radeon_device *rdev) +{ + int i; + + rdev->pm.dynpm_can_upclock = true; + rdev->pm.dynpm_can_downclock = true; + + /* power state array is low to high, default is first */ + if ((rdev->flags & RADEON_IS_IGP) || (rdev->family == CHIP_R600)) { + int min_power_state_index = 0; + + if (rdev->pm.num_power_states > 2) + min_power_state_index = 1; + + switch (rdev->pm.dynpm_planned_action) { + case DYNPM_ACTION_MINIMUM: + rdev->pm.requested_power_state_index = min_power_state_index; + rdev->pm.requested_clock_mode_index = 0; + rdev->pm.dynpm_can_downclock = false; + break; + case DYNPM_ACTION_DOWNCLOCK: + if (rdev->pm.current_power_state_index == min_power_state_index) { + rdev->pm.requested_power_state_index = rdev->pm.current_power_state_index; + rdev->pm.dynpm_can_downclock = false; + } else { + if (rdev->pm.active_crtc_count > 1) { + for (i = 0; i < rdev->pm.num_power_states; i++) { + if (rdev->pm.power_state[i].flags & RADEON_PM_STATE_SINGLE_DISPLAY_ONLY) + continue; + else if (i >= rdev->pm.current_power_state_index) { + rdev->pm.requested_power_state_index = + rdev->pm.current_power_state_index; + break; + } else { + rdev->pm.requested_power_state_index = i; + break; + } + } + } else { + if (rdev->pm.current_power_state_index == 0) + rdev->pm.requested_power_state_index = + rdev->pm.num_power_states - 1; + else + rdev->pm.requested_power_state_index = + rdev->pm.current_power_state_index - 1; + } + } + rdev->pm.requested_clock_mode_index = 0; + /* don't use the power state if crtcs are active and no display flag is set */ + if ((rdev->pm.active_crtc_count > 0) && + (rdev->pm.power_state[rdev->pm.requested_power_state_index]. + clock_info[rdev->pm.requested_clock_mode_index].flags & + RADEON_PM_MODE_NO_DISPLAY)) { + rdev->pm.requested_power_state_index++; + } + break; + case DYNPM_ACTION_UPCLOCK: + if (rdev->pm.current_power_state_index == (rdev->pm.num_power_states - 1)) { + rdev->pm.requested_power_state_index = rdev->pm.current_power_state_index; + rdev->pm.dynpm_can_upclock = false; + } else { + if (rdev->pm.active_crtc_count > 1) { + for (i = (rdev->pm.num_power_states - 1); i >= 0; i--) { + if (rdev->pm.power_state[i].flags & RADEON_PM_STATE_SINGLE_DISPLAY_ONLY) + continue; + else if (i <= rdev->pm.current_power_state_index) { + rdev->pm.requested_power_state_index = + rdev->pm.current_power_state_index; + break; + } else { + rdev->pm.requested_power_state_index = i; + break; + } + } + } else + rdev->pm.requested_power_state_index = + rdev->pm.current_power_state_index + 1; + } + rdev->pm.requested_clock_mode_index = 0; + break; + case DYNPM_ACTION_DEFAULT: + rdev->pm.requested_power_state_index = rdev->pm.default_power_state_index; + rdev->pm.requested_clock_mode_index = 0; + rdev->pm.dynpm_can_upclock = false; + break; + case DYNPM_ACTION_NONE: + default: + DRM_ERROR("Requested mode for not defined action\n"); + return; + } + } else { + /* XXX select a power state based on AC/DC, single/dualhead, etc. */ + /* for now just select the first power state and switch between clock modes */ + /* power state array is low to high, default is first (0) */ + if (rdev->pm.active_crtc_count > 1) { + rdev->pm.requested_power_state_index = -1; + /* start at 1 as we don't want the default mode */ + for (i = 1; i < rdev->pm.num_power_states; i++) { + if (rdev->pm.power_state[i].flags & RADEON_PM_STATE_SINGLE_DISPLAY_ONLY) + continue; + else if ((rdev->pm.power_state[i].type == POWER_STATE_TYPE_PERFORMANCE) || + (rdev->pm.power_state[i].type == POWER_STATE_TYPE_BATTERY)) { + rdev->pm.requested_power_state_index = i; + break; + } + } + /* if nothing selected, grab the default state. */ + if (rdev->pm.requested_power_state_index == -1) + rdev->pm.requested_power_state_index = 0; + } else + rdev->pm.requested_power_state_index = 1; + + switch (rdev->pm.dynpm_planned_action) { + case DYNPM_ACTION_MINIMUM: + rdev->pm.requested_clock_mode_index = 0; + rdev->pm.dynpm_can_downclock = false; + break; + case DYNPM_ACTION_DOWNCLOCK: + if (rdev->pm.requested_power_state_index == rdev->pm.current_power_state_index) { + if (rdev->pm.current_clock_mode_index == 0) { + rdev->pm.requested_clock_mode_index = 0; + rdev->pm.dynpm_can_downclock = false; + } else + rdev->pm.requested_clock_mode_index = + rdev->pm.current_clock_mode_index - 1; + } else { + rdev->pm.requested_clock_mode_index = 0; + rdev->pm.dynpm_can_downclock = false; + } + /* don't use the power state if crtcs are active and no display flag is set */ + if ((rdev->pm.active_crtc_count > 0) && + (rdev->pm.power_state[rdev->pm.requested_power_state_index]. + clock_info[rdev->pm.requested_clock_mode_index].flags & + RADEON_PM_MODE_NO_DISPLAY)) { + rdev->pm.requested_clock_mode_index++; + } + break; + case DYNPM_ACTION_UPCLOCK: + if (rdev->pm.requested_power_state_index == rdev->pm.current_power_state_index) { + if (rdev->pm.current_clock_mode_index == + (rdev->pm.power_state[rdev->pm.requested_power_state_index].num_clock_modes - 1)) { + rdev->pm.requested_clock_mode_index = rdev->pm.current_clock_mode_index; + rdev->pm.dynpm_can_upclock = false; + } else + rdev->pm.requested_clock_mode_index = + rdev->pm.current_clock_mode_index + 1; + } else { + rdev->pm.requested_clock_mode_index = + rdev->pm.power_state[rdev->pm.requested_power_state_index].num_clock_modes - 1; + rdev->pm.dynpm_can_upclock = false; + } + break; + case DYNPM_ACTION_DEFAULT: + rdev->pm.requested_power_state_index = rdev->pm.default_power_state_index; + rdev->pm.requested_clock_mode_index = 0; + rdev->pm.dynpm_can_upclock = false; + break; + case DYNPM_ACTION_NONE: + default: + DRM_ERROR("Requested mode for not defined action\n"); + return; + } + } + + DRM_DEBUG_DRIVER("Requested: e: %d m: %d p: %d\n", + rdev->pm.power_state[rdev->pm.requested_power_state_index]. + clock_info[rdev->pm.requested_clock_mode_index].sclk, + rdev->pm.power_state[rdev->pm.requested_power_state_index]. + clock_info[rdev->pm.requested_clock_mode_index].mclk, + rdev->pm.power_state[rdev->pm.requested_power_state_index]. + pcie_lanes); +} + +void rs780_pm_init_profile(struct radeon_device *rdev) +{ + if (rdev->pm.num_power_states == 2) { + /* default */ + rdev->pm.profiles[PM_PROFILE_DEFAULT_IDX].dpms_off_ps_idx = rdev->pm.default_power_state_index; + rdev->pm.profiles[PM_PROFILE_DEFAULT_IDX].dpms_on_ps_idx = rdev->pm.default_power_state_index; + rdev->pm.profiles[PM_PROFILE_DEFAULT_IDX].dpms_off_cm_idx = 0; + rdev->pm.profiles[PM_PROFILE_DEFAULT_IDX].dpms_on_cm_idx = 0; + /* low sh */ + rdev->pm.profiles[PM_PROFILE_LOW_SH_IDX].dpms_off_ps_idx = 0; + rdev->pm.profiles[PM_PROFILE_LOW_SH_IDX].dpms_on_ps_idx = 0; + rdev->pm.profiles[PM_PROFILE_LOW_SH_IDX].dpms_off_cm_idx = 0; + rdev->pm.profiles[PM_PROFILE_LOW_SH_IDX].dpms_on_cm_idx = 0; + /* mid sh */ + rdev->pm.profiles[PM_PROFILE_MID_SH_IDX].dpms_off_ps_idx = 0; + rdev->pm.profiles[PM_PROFILE_MID_SH_IDX].dpms_on_ps_idx = 0; + rdev->pm.profiles[PM_PROFILE_MID_SH_IDX].dpms_off_cm_idx = 0; + rdev->pm.profiles[PM_PROFILE_MID_SH_IDX].dpms_on_cm_idx = 0; + /* high sh */ + rdev->pm.profiles[PM_PROFILE_HIGH_SH_IDX].dpms_off_ps_idx = 0; + rdev->pm.profiles[PM_PROFILE_HIGH_SH_IDX].dpms_on_ps_idx = 1; + rdev->pm.profiles[PM_PROFILE_HIGH_SH_IDX].dpms_off_cm_idx = 0; + rdev->pm.profiles[PM_PROFILE_HIGH_SH_IDX].dpms_on_cm_idx = 0; + /* low mh */ + rdev->pm.profiles[PM_PROFILE_LOW_MH_IDX].dpms_off_ps_idx = 0; + rdev->pm.profiles[PM_PROFILE_LOW_MH_IDX].dpms_on_ps_idx = 0; + rdev->pm.profiles[PM_PROFILE_LOW_MH_IDX].dpms_off_cm_idx = 0; + rdev->pm.profiles[PM_PROFILE_LOW_MH_IDX].dpms_on_cm_idx = 0; + /* mid mh */ + rdev->pm.profiles[PM_PROFILE_MID_MH_IDX].dpms_off_ps_idx = 0; + rdev->pm.profiles[PM_PROFILE_MID_MH_IDX].dpms_on_ps_idx = 0; + rdev->pm.profiles[PM_PROFILE_MID_MH_IDX].dpms_off_cm_idx = 0; + rdev->pm.profiles[PM_PROFILE_MID_MH_IDX].dpms_on_cm_idx = 0; + /* high mh */ + rdev->pm.profiles[PM_PROFILE_HIGH_MH_IDX].dpms_off_ps_idx = 0; + rdev->pm.profiles[PM_PROFILE_HIGH_MH_IDX].dpms_on_ps_idx = 1; + rdev->pm.profiles[PM_PROFILE_HIGH_MH_IDX].dpms_off_cm_idx = 0; + rdev->pm.profiles[PM_PROFILE_HIGH_MH_IDX].dpms_on_cm_idx = 0; + } else if (rdev->pm.num_power_states == 3) { + /* default */ + rdev->pm.profiles[PM_PROFILE_DEFAULT_IDX].dpms_off_ps_idx = rdev->pm.default_power_state_index; + rdev->pm.profiles[PM_PROFILE_DEFAULT_IDX].dpms_on_ps_idx = rdev->pm.default_power_state_index; + rdev->pm.profiles[PM_PROFILE_DEFAULT_IDX].dpms_off_cm_idx = 0; + rdev->pm.profiles[PM_PROFILE_DEFAULT_IDX].dpms_on_cm_idx = 0; + /* low sh */ + rdev->pm.profiles[PM_PROFILE_LOW_SH_IDX].dpms_off_ps_idx = 1; + rdev->pm.profiles[PM_PROFILE_LOW_SH_IDX].dpms_on_ps_idx = 1; + rdev->pm.profiles[PM_PROFILE_LOW_SH_IDX].dpms_off_cm_idx = 0; + rdev->pm.profiles[PM_PROFILE_LOW_SH_IDX].dpms_on_cm_idx = 0; + /* mid sh */ + rdev->pm.profiles[PM_PROFILE_MID_SH_IDX].dpms_off_ps_idx = 1; + rdev->pm.profiles[PM_PROFILE_MID_SH_IDX].dpms_on_ps_idx = 1; + rdev->pm.profiles[PM_PROFILE_MID_SH_IDX].dpms_off_cm_idx = 0; + rdev->pm.profiles[PM_PROFILE_MID_SH_IDX].dpms_on_cm_idx = 0; + /* high sh */ + rdev->pm.profiles[PM_PROFILE_HIGH_SH_IDX].dpms_off_ps_idx = 1; + rdev->pm.profiles[PM_PROFILE_HIGH_SH_IDX].dpms_on_ps_idx = 2; + rdev->pm.profiles[PM_PROFILE_HIGH_SH_IDX].dpms_off_cm_idx = 0; + rdev->pm.profiles[PM_PROFILE_HIGH_SH_IDX].dpms_on_cm_idx = 0; + /* low mh */ + rdev->pm.profiles[PM_PROFILE_LOW_MH_IDX].dpms_off_ps_idx = 1; + rdev->pm.profiles[PM_PROFILE_LOW_MH_IDX].dpms_on_ps_idx = 1; + rdev->pm.profiles[PM_PROFILE_LOW_MH_IDX].dpms_off_cm_idx = 0; + rdev->pm.profiles[PM_PROFILE_LOW_MH_IDX].dpms_on_cm_idx = 0; + /* mid mh */ + rdev->pm.profiles[PM_PROFILE_MID_MH_IDX].dpms_off_ps_idx = 1; + rdev->pm.profiles[PM_PROFILE_MID_MH_IDX].dpms_on_ps_idx = 1; + rdev->pm.profiles[PM_PROFILE_MID_MH_IDX].dpms_off_cm_idx = 0; + rdev->pm.profiles[PM_PROFILE_MID_MH_IDX].dpms_on_cm_idx = 0; + /* high mh */ + rdev->pm.profiles[PM_PROFILE_HIGH_MH_IDX].dpms_off_ps_idx = 1; + rdev->pm.profiles[PM_PROFILE_HIGH_MH_IDX].dpms_on_ps_idx = 2; + rdev->pm.profiles[PM_PROFILE_HIGH_MH_IDX].dpms_off_cm_idx = 0; + rdev->pm.profiles[PM_PROFILE_HIGH_MH_IDX].dpms_on_cm_idx = 0; + } else { + /* default */ + rdev->pm.profiles[PM_PROFILE_DEFAULT_IDX].dpms_off_ps_idx = rdev->pm.default_power_state_index; + rdev->pm.profiles[PM_PROFILE_DEFAULT_IDX].dpms_on_ps_idx = rdev->pm.default_power_state_index; + rdev->pm.profiles[PM_PROFILE_DEFAULT_IDX].dpms_off_cm_idx = 0; + rdev->pm.profiles[PM_PROFILE_DEFAULT_IDX].dpms_on_cm_idx = 0; + /* low sh */ + rdev->pm.profiles[PM_PROFILE_LOW_SH_IDX].dpms_off_ps_idx = 2; + rdev->pm.profiles[PM_PROFILE_LOW_SH_IDX].dpms_on_ps_idx = 2; + rdev->pm.profiles[PM_PROFILE_LOW_SH_IDX].dpms_off_cm_idx = 0; + rdev->pm.profiles[PM_PROFILE_LOW_SH_IDX].dpms_on_cm_idx = 0; + /* mid sh */ + rdev->pm.profiles[PM_PROFILE_MID_SH_IDX].dpms_off_ps_idx = 2; + rdev->pm.profiles[PM_PROFILE_MID_SH_IDX].dpms_on_ps_idx = 2; + rdev->pm.profiles[PM_PROFILE_MID_SH_IDX].dpms_off_cm_idx = 0; + rdev->pm.profiles[PM_PROFILE_MID_SH_IDX].dpms_on_cm_idx = 0; + /* high sh */ + rdev->pm.profiles[PM_PROFILE_HIGH_SH_IDX].dpms_off_ps_idx = 2; + rdev->pm.profiles[PM_PROFILE_HIGH_SH_IDX].dpms_on_ps_idx = 3; + rdev->pm.profiles[PM_PROFILE_HIGH_SH_IDX].dpms_off_cm_idx = 0; + rdev->pm.profiles[PM_PROFILE_HIGH_SH_IDX].dpms_on_cm_idx = 0; + /* low mh */ + rdev->pm.profiles[PM_PROFILE_LOW_MH_IDX].dpms_off_ps_idx = 2; + rdev->pm.profiles[PM_PROFILE_LOW_MH_IDX].dpms_on_ps_idx = 0; + rdev->pm.profiles[PM_PROFILE_LOW_MH_IDX].dpms_off_cm_idx = 0; + rdev->pm.profiles[PM_PROFILE_LOW_MH_IDX].dpms_on_cm_idx = 0; + /* mid mh */ + rdev->pm.profiles[PM_PROFILE_MID_MH_IDX].dpms_off_ps_idx = 2; + rdev->pm.profiles[PM_PROFILE_MID_MH_IDX].dpms_on_ps_idx = 0; + rdev->pm.profiles[PM_PROFILE_MID_MH_IDX].dpms_off_cm_idx = 0; + rdev->pm.profiles[PM_PROFILE_MID_MH_IDX].dpms_on_cm_idx = 0; + /* high mh */ + rdev->pm.profiles[PM_PROFILE_HIGH_MH_IDX].dpms_off_ps_idx = 2; + rdev->pm.profiles[PM_PROFILE_HIGH_MH_IDX].dpms_on_ps_idx = 3; + rdev->pm.profiles[PM_PROFILE_HIGH_MH_IDX].dpms_off_cm_idx = 0; + rdev->pm.profiles[PM_PROFILE_HIGH_MH_IDX].dpms_on_cm_idx = 0; + } +} + +void r600_pm_init_profile(struct radeon_device *rdev) +{ + int idx; + + if (rdev->family == CHIP_R600) { + /* XXX */ + /* default */ + rdev->pm.profiles[PM_PROFILE_DEFAULT_IDX].dpms_off_ps_idx = rdev->pm.default_power_state_index; + rdev->pm.profiles[PM_PROFILE_DEFAULT_IDX].dpms_on_ps_idx = rdev->pm.default_power_state_index; + rdev->pm.profiles[PM_PROFILE_DEFAULT_IDX].dpms_off_cm_idx = 0; + rdev->pm.profiles[PM_PROFILE_DEFAULT_IDX].dpms_on_cm_idx = 0; + /* low sh */ + rdev->pm.profiles[PM_PROFILE_LOW_SH_IDX].dpms_off_ps_idx = rdev->pm.default_power_state_index; + rdev->pm.profiles[PM_PROFILE_LOW_SH_IDX].dpms_on_ps_idx = rdev->pm.default_power_state_index; + rdev->pm.profiles[PM_PROFILE_LOW_SH_IDX].dpms_off_cm_idx = 0; + rdev->pm.profiles[PM_PROFILE_LOW_SH_IDX].dpms_on_cm_idx = 0; + /* mid sh */ + rdev->pm.profiles[PM_PROFILE_MID_SH_IDX].dpms_off_ps_idx = rdev->pm.default_power_state_index; + rdev->pm.profiles[PM_PROFILE_MID_SH_IDX].dpms_on_ps_idx = rdev->pm.default_power_state_index; + rdev->pm.profiles[PM_PROFILE_MID_SH_IDX].dpms_off_cm_idx = 0; + rdev->pm.profiles[PM_PROFILE_MID_SH_IDX].dpms_on_cm_idx = 0; + /* high sh */ + rdev->pm.profiles[PM_PROFILE_HIGH_SH_IDX].dpms_off_ps_idx = rdev->pm.default_power_state_index; + rdev->pm.profiles[PM_PROFILE_HIGH_SH_IDX].dpms_on_ps_idx = rdev->pm.default_power_state_index; + rdev->pm.profiles[PM_PROFILE_HIGH_SH_IDX].dpms_off_cm_idx = 0; + rdev->pm.profiles[PM_PROFILE_HIGH_SH_IDX].dpms_on_cm_idx = 0; + /* low mh */ + rdev->pm.profiles[PM_PROFILE_LOW_MH_IDX].dpms_off_ps_idx = rdev->pm.default_power_state_index; + rdev->pm.profiles[PM_PROFILE_LOW_MH_IDX].dpms_on_ps_idx = rdev->pm.default_power_state_index; + rdev->pm.profiles[PM_PROFILE_LOW_MH_IDX].dpms_off_cm_idx = 0; + rdev->pm.profiles[PM_PROFILE_LOW_MH_IDX].dpms_on_cm_idx = 0; + /* mid mh */ + rdev->pm.profiles[PM_PROFILE_MID_MH_IDX].dpms_off_ps_idx = rdev->pm.default_power_state_index; + rdev->pm.profiles[PM_PROFILE_MID_MH_IDX].dpms_on_ps_idx = rdev->pm.default_power_state_index; + rdev->pm.profiles[PM_PROFILE_MID_MH_IDX].dpms_off_cm_idx = 0; + rdev->pm.profiles[PM_PROFILE_MID_MH_IDX].dpms_on_cm_idx = 0; + /* high mh */ + rdev->pm.profiles[PM_PROFILE_HIGH_MH_IDX].dpms_off_ps_idx = rdev->pm.default_power_state_index; + rdev->pm.profiles[PM_PROFILE_HIGH_MH_IDX].dpms_on_ps_idx = rdev->pm.default_power_state_index; + rdev->pm.profiles[PM_PROFILE_HIGH_MH_IDX].dpms_off_cm_idx = 0; + rdev->pm.profiles[PM_PROFILE_HIGH_MH_IDX].dpms_on_cm_idx = 0; + } else { + if (rdev->pm.num_power_states < 4) { + /* default */ + rdev->pm.profiles[PM_PROFILE_DEFAULT_IDX].dpms_off_ps_idx = rdev->pm.default_power_state_index; + rdev->pm.profiles[PM_PROFILE_DEFAULT_IDX].dpms_on_ps_idx = rdev->pm.default_power_state_index; + rdev->pm.profiles[PM_PROFILE_DEFAULT_IDX].dpms_off_cm_idx = 0; + rdev->pm.profiles[PM_PROFILE_DEFAULT_IDX].dpms_on_cm_idx = 2; + /* low sh */ + rdev->pm.profiles[PM_PROFILE_LOW_SH_IDX].dpms_off_ps_idx = 1; + rdev->pm.profiles[PM_PROFILE_LOW_SH_IDX].dpms_on_ps_idx = 1; + rdev->pm.profiles[PM_PROFILE_LOW_SH_IDX].dpms_off_cm_idx = 0; + rdev->pm.profiles[PM_PROFILE_LOW_SH_IDX].dpms_on_cm_idx = 0; + /* mid sh */ + rdev->pm.profiles[PM_PROFILE_MID_SH_IDX].dpms_off_ps_idx = 1; + rdev->pm.profiles[PM_PROFILE_MID_SH_IDX].dpms_on_ps_idx = 1; + rdev->pm.profiles[PM_PROFILE_MID_SH_IDX].dpms_off_cm_idx = 0; + rdev->pm.profiles[PM_PROFILE_MID_SH_IDX].dpms_on_cm_idx = 1; + /* high sh */ + rdev->pm.profiles[PM_PROFILE_HIGH_SH_IDX].dpms_off_ps_idx = 1; + rdev->pm.profiles[PM_PROFILE_HIGH_SH_IDX].dpms_on_ps_idx = 1; + rdev->pm.profiles[PM_PROFILE_HIGH_SH_IDX].dpms_off_cm_idx = 0; + rdev->pm.profiles[PM_PROFILE_HIGH_SH_IDX].dpms_on_cm_idx = 2; + /* low mh */ + rdev->pm.profiles[PM_PROFILE_LOW_MH_IDX].dpms_off_ps_idx = 2; + rdev->pm.profiles[PM_PROFILE_LOW_MH_IDX].dpms_on_ps_idx = 2; + rdev->pm.profiles[PM_PROFILE_LOW_MH_IDX].dpms_off_cm_idx = 0; + rdev->pm.profiles[PM_PROFILE_LOW_MH_IDX].dpms_on_cm_idx = 0; + /* low mh */ + rdev->pm.profiles[PM_PROFILE_MID_MH_IDX].dpms_off_ps_idx = 2; + rdev->pm.profiles[PM_PROFILE_MID_MH_IDX].dpms_on_ps_idx = 2; + rdev->pm.profiles[PM_PROFILE_MID_MH_IDX].dpms_off_cm_idx = 0; + rdev->pm.profiles[PM_PROFILE_MID_MH_IDX].dpms_on_cm_idx = 1; + /* high mh */ + rdev->pm.profiles[PM_PROFILE_HIGH_MH_IDX].dpms_off_ps_idx = 2; + rdev->pm.profiles[PM_PROFILE_HIGH_MH_IDX].dpms_on_ps_idx = 2; + rdev->pm.profiles[PM_PROFILE_HIGH_MH_IDX].dpms_off_cm_idx = 0; + rdev->pm.profiles[PM_PROFILE_HIGH_MH_IDX].dpms_on_cm_idx = 2; + } else { + /* default */ + rdev->pm.profiles[PM_PROFILE_DEFAULT_IDX].dpms_off_ps_idx = rdev->pm.default_power_state_index; + rdev->pm.profiles[PM_PROFILE_DEFAULT_IDX].dpms_on_ps_idx = rdev->pm.default_power_state_index; + rdev->pm.profiles[PM_PROFILE_DEFAULT_IDX].dpms_off_cm_idx = 0; + rdev->pm.profiles[PM_PROFILE_DEFAULT_IDX].dpms_on_cm_idx = 2; + /* low sh */ + if (rdev->flags & RADEON_IS_MOBILITY) + idx = radeon_pm_get_type_index(rdev, POWER_STATE_TYPE_BATTERY, 0); + else + idx = radeon_pm_get_type_index(rdev, POWER_STATE_TYPE_PERFORMANCE, 0); + rdev->pm.profiles[PM_PROFILE_LOW_SH_IDX].dpms_off_ps_idx = idx; + rdev->pm.profiles[PM_PROFILE_LOW_SH_IDX].dpms_on_ps_idx = idx; + rdev->pm.profiles[PM_PROFILE_LOW_SH_IDX].dpms_off_cm_idx = 0; + rdev->pm.profiles[PM_PROFILE_LOW_SH_IDX].dpms_on_cm_idx = 0; + /* mid sh */ + rdev->pm.profiles[PM_PROFILE_MID_SH_IDX].dpms_off_ps_idx = idx; + rdev->pm.profiles[PM_PROFILE_MID_SH_IDX].dpms_on_ps_idx = idx; + rdev->pm.profiles[PM_PROFILE_MID_SH_IDX].dpms_off_cm_idx = 0; + rdev->pm.profiles[PM_PROFILE_MID_SH_IDX].dpms_on_cm_idx = 1; + /* high sh */ + idx = radeon_pm_get_type_index(rdev, POWER_STATE_TYPE_PERFORMANCE, 0); + rdev->pm.profiles[PM_PROFILE_HIGH_SH_IDX].dpms_off_ps_idx = idx; + rdev->pm.profiles[PM_PROFILE_HIGH_SH_IDX].dpms_on_ps_idx = idx; + rdev->pm.profiles[PM_PROFILE_HIGH_SH_IDX].dpms_off_cm_idx = 0; + rdev->pm.profiles[PM_PROFILE_HIGH_SH_IDX].dpms_on_cm_idx = 2; + /* low mh */ + if (rdev->flags & RADEON_IS_MOBILITY) + idx = radeon_pm_get_type_index(rdev, POWER_STATE_TYPE_BATTERY, 1); + else + idx = radeon_pm_get_type_index(rdev, POWER_STATE_TYPE_PERFORMANCE, 1); + rdev->pm.profiles[PM_PROFILE_LOW_MH_IDX].dpms_off_ps_idx = idx; + rdev->pm.profiles[PM_PROFILE_LOW_MH_IDX].dpms_on_ps_idx = idx; + rdev->pm.profiles[PM_PROFILE_LOW_MH_IDX].dpms_off_cm_idx = 0; + rdev->pm.profiles[PM_PROFILE_LOW_MH_IDX].dpms_on_cm_idx = 0; + /* mid mh */ + rdev->pm.profiles[PM_PROFILE_MID_MH_IDX].dpms_off_ps_idx = idx; + rdev->pm.profiles[PM_PROFILE_MID_MH_IDX].dpms_on_ps_idx = idx; + rdev->pm.profiles[PM_PROFILE_MID_MH_IDX].dpms_off_cm_idx = 0; + rdev->pm.profiles[PM_PROFILE_MID_MH_IDX].dpms_on_cm_idx = 1; + /* high mh */ + idx = radeon_pm_get_type_index(rdev, POWER_STATE_TYPE_PERFORMANCE, 1); + rdev->pm.profiles[PM_PROFILE_HIGH_MH_IDX].dpms_off_ps_idx = idx; + rdev->pm.profiles[PM_PROFILE_HIGH_MH_IDX].dpms_on_ps_idx = idx; + rdev->pm.profiles[PM_PROFILE_HIGH_MH_IDX].dpms_off_cm_idx = 0; + rdev->pm.profiles[PM_PROFILE_HIGH_MH_IDX].dpms_on_cm_idx = 2; + } + } +} + +void r600_pm_misc(struct radeon_device *rdev) +{ + int req_ps_idx = rdev->pm.requested_power_state_index; + int req_cm_idx = rdev->pm.requested_clock_mode_index; + struct radeon_power_state *ps = &rdev->pm.power_state[req_ps_idx]; + struct radeon_voltage *voltage = &ps->clock_info[req_cm_idx].voltage; + + if ((voltage->type == VOLTAGE_SW) && voltage->voltage) { + /* 0xff01 is a flag rather then an actual voltage */ + if (voltage->voltage == 0xff01) + return; + if (voltage->voltage != rdev->pm.current_vddc) { + radeon_atom_set_voltage(rdev, voltage->voltage, SET_VOLTAGE_TYPE_ASIC_VDDC); + rdev->pm.current_vddc = voltage->voltage; + DRM_DEBUG_DRIVER("Setting: v: %d\n", voltage->voltage); + } + } +} + +bool r600_gui_idle(struct radeon_device *rdev) +{ + if (RREG32(GRBM_STATUS) & GUI_ACTIVE) + return false; + else + return true; +} + +/* hpd for digital panel detect/disconnect */ +bool r600_hpd_sense(struct radeon_device *rdev, enum radeon_hpd_id hpd) +{ + bool connected = false; + + if (ASIC_IS_DCE3(rdev)) { + switch (hpd) { + case RADEON_HPD_1: + if (RREG32(DC_HPD1_INT_STATUS) & DC_HPDx_SENSE) + connected = true; + break; + case RADEON_HPD_2: + if (RREG32(DC_HPD2_INT_STATUS) & DC_HPDx_SENSE) + connected = true; + break; + case RADEON_HPD_3: + if (RREG32(DC_HPD3_INT_STATUS) & DC_HPDx_SENSE) + connected = true; + break; + case RADEON_HPD_4: + if (RREG32(DC_HPD4_INT_STATUS) & DC_HPDx_SENSE) + connected = true; + break; + /* DCE 3.2 */ + case RADEON_HPD_5: + if (RREG32(DC_HPD5_INT_STATUS) & DC_HPDx_SENSE) + connected = true; + break; + case RADEON_HPD_6: + if (RREG32(DC_HPD6_INT_STATUS) & DC_HPDx_SENSE) + connected = true; + break; + default: + break; + } + } else { + switch (hpd) { + case RADEON_HPD_1: + if (RREG32(DC_HOT_PLUG_DETECT1_INT_STATUS) & DC_HOT_PLUG_DETECTx_SENSE) + connected = true; + break; + case RADEON_HPD_2: + if (RREG32(DC_HOT_PLUG_DETECT2_INT_STATUS) & DC_HOT_PLUG_DETECTx_SENSE) + connected = true; + break; + case RADEON_HPD_3: + if (RREG32(DC_HOT_PLUG_DETECT3_INT_STATUS) & DC_HOT_PLUG_DETECTx_SENSE) + connected = true; + break; + default: + break; + } + } + return connected; +} + +void r600_hpd_set_polarity(struct radeon_device *rdev, + enum radeon_hpd_id hpd) +{ + u32 tmp; + bool connected = r600_hpd_sense(rdev, hpd); + + if (ASIC_IS_DCE3(rdev)) { + switch (hpd) { + case RADEON_HPD_1: + tmp = RREG32(DC_HPD1_INT_CONTROL); + if (connected) + tmp &= ~DC_HPDx_INT_POLARITY; + else + tmp |= DC_HPDx_INT_POLARITY; + WREG32(DC_HPD1_INT_CONTROL, tmp); + break; + case RADEON_HPD_2: + tmp = RREG32(DC_HPD2_INT_CONTROL); + if (connected) + tmp &= ~DC_HPDx_INT_POLARITY; + else + tmp |= DC_HPDx_INT_POLARITY; + WREG32(DC_HPD2_INT_CONTROL, tmp); + break; + case RADEON_HPD_3: + tmp = RREG32(DC_HPD3_INT_CONTROL); + if (connected) + tmp &= ~DC_HPDx_INT_POLARITY; + else + tmp |= DC_HPDx_INT_POLARITY; + WREG32(DC_HPD3_INT_CONTROL, tmp); + break; + case RADEON_HPD_4: + tmp = RREG32(DC_HPD4_INT_CONTROL); + if (connected) + tmp &= ~DC_HPDx_INT_POLARITY; + else + tmp |= DC_HPDx_INT_POLARITY; + WREG32(DC_HPD4_INT_CONTROL, tmp); + break; + case RADEON_HPD_5: + tmp = RREG32(DC_HPD5_INT_CONTROL); + if (connected) + tmp &= ~DC_HPDx_INT_POLARITY; + else + tmp |= DC_HPDx_INT_POLARITY; + WREG32(DC_HPD5_INT_CONTROL, tmp); + break; + /* DCE 3.2 */ + case RADEON_HPD_6: + tmp = RREG32(DC_HPD6_INT_CONTROL); + if (connected) + tmp &= ~DC_HPDx_INT_POLARITY; + else + tmp |= DC_HPDx_INT_POLARITY; + WREG32(DC_HPD6_INT_CONTROL, tmp); + break; + default: + break; + } + } else { + switch (hpd) { + case RADEON_HPD_1: + tmp = RREG32(DC_HOT_PLUG_DETECT1_INT_CONTROL); + if (connected) + tmp &= ~DC_HOT_PLUG_DETECTx_INT_POLARITY; + else + tmp |= DC_HOT_PLUG_DETECTx_INT_POLARITY; + WREG32(DC_HOT_PLUG_DETECT1_INT_CONTROL, tmp); + break; + case RADEON_HPD_2: + tmp = RREG32(DC_HOT_PLUG_DETECT2_INT_CONTROL); + if (connected) + tmp &= ~DC_HOT_PLUG_DETECTx_INT_POLARITY; + else + tmp |= DC_HOT_PLUG_DETECTx_INT_POLARITY; + WREG32(DC_HOT_PLUG_DETECT2_INT_CONTROL, tmp); + break; + case RADEON_HPD_3: + tmp = RREG32(DC_HOT_PLUG_DETECT3_INT_CONTROL); + if (connected) + tmp &= ~DC_HOT_PLUG_DETECTx_INT_POLARITY; + else + tmp |= DC_HOT_PLUG_DETECTx_INT_POLARITY; + WREG32(DC_HOT_PLUG_DETECT3_INT_CONTROL, tmp); + break; + default: + break; + } + } +} + +void r600_hpd_init(struct radeon_device *rdev) +{ + struct drm_device *dev = rdev->ddev; + struct drm_connector *connector; + unsigned enable = 0; + + list_for_each_entry(connector, &dev->mode_config.connector_list, head) { + struct radeon_connector *radeon_connector = to_radeon_connector(connector); + + if (connector->connector_type == DRM_MODE_CONNECTOR_eDP || + connector->connector_type == DRM_MODE_CONNECTOR_LVDS) { + /* don't try to enable hpd on eDP or LVDS avoid breaking the + * aux dp channel on imac and help (but not completely fix) + * https://bugzilla.redhat.com/show_bug.cgi?id=726143 + */ + continue; + } + if (ASIC_IS_DCE3(rdev)) { + u32 tmp = DC_HPDx_CONNECTION_TIMER(0x9c4) | DC_HPDx_RX_INT_TIMER(0xfa); + if (ASIC_IS_DCE32(rdev)) + tmp |= DC_HPDx_EN; + + switch (radeon_connector->hpd.hpd) { + case RADEON_HPD_1: + WREG32(DC_HPD1_CONTROL, tmp); + break; + case RADEON_HPD_2: + WREG32(DC_HPD2_CONTROL, tmp); + break; + case RADEON_HPD_3: + WREG32(DC_HPD3_CONTROL, tmp); + break; + case RADEON_HPD_4: + WREG32(DC_HPD4_CONTROL, tmp); + break; + /* DCE 3.2 */ + case RADEON_HPD_5: + WREG32(DC_HPD5_CONTROL, tmp); + break; + case RADEON_HPD_6: + WREG32(DC_HPD6_CONTROL, tmp); + break; + default: + break; + } + } else { + switch (radeon_connector->hpd.hpd) { + case RADEON_HPD_1: + WREG32(DC_HOT_PLUG_DETECT1_CONTROL, DC_HOT_PLUG_DETECTx_EN); + break; + case RADEON_HPD_2: + WREG32(DC_HOT_PLUG_DETECT2_CONTROL, DC_HOT_PLUG_DETECTx_EN); + break; + case RADEON_HPD_3: + WREG32(DC_HOT_PLUG_DETECT3_CONTROL, DC_HOT_PLUG_DETECTx_EN); + break; + default: + break; + } + } + enable |= 1 << radeon_connector->hpd.hpd; + radeon_hpd_set_polarity(rdev, radeon_connector->hpd.hpd); + } + radeon_irq_kms_enable_hpd(rdev, enable); +} + +void r600_hpd_fini(struct radeon_device *rdev) +{ + struct drm_device *dev = rdev->ddev; + struct drm_connector *connector; + unsigned disable = 0; + + list_for_each_entry(connector, &dev->mode_config.connector_list, head) { + struct radeon_connector *radeon_connector = to_radeon_connector(connector); + if (ASIC_IS_DCE3(rdev)) { + switch (radeon_connector->hpd.hpd) { + case RADEON_HPD_1: + WREG32(DC_HPD1_CONTROL, 0); + break; + case RADEON_HPD_2: + WREG32(DC_HPD2_CONTROL, 0); + break; + case RADEON_HPD_3: + WREG32(DC_HPD3_CONTROL, 0); + break; + case RADEON_HPD_4: + WREG32(DC_HPD4_CONTROL, 0); + break; + /* DCE 3.2 */ + case RADEON_HPD_5: + WREG32(DC_HPD5_CONTROL, 0); + break; + case RADEON_HPD_6: + WREG32(DC_HPD6_CONTROL, 0); + break; + default: + break; + } + } else { + switch (radeon_connector->hpd.hpd) { + case RADEON_HPD_1: + WREG32(DC_HOT_PLUG_DETECT1_CONTROL, 0); + break; + case RADEON_HPD_2: + WREG32(DC_HOT_PLUG_DETECT2_CONTROL, 0); + break; + case RADEON_HPD_3: + WREG32(DC_HOT_PLUG_DETECT3_CONTROL, 0); + break; + default: + break; + } + } + disable |= 1 << radeon_connector->hpd.hpd; + } + radeon_irq_kms_disable_hpd(rdev, disable); +} + +/* + * R600 PCIE GART + */ +void r600_pcie_gart_tlb_flush(struct radeon_device *rdev) +{ + unsigned i; + u32 tmp; + + /* flush hdp cache so updates hit vram */ + if ((rdev->family >= CHIP_RV770) && (rdev->family <= CHIP_RV740) && + !(rdev->flags & RADEON_IS_AGP)) { + volatile uint32_t *ptr = rdev->gart.ptr; + u32 tmp; + + /* r7xx hw bug. write to HDP_DEBUG1 followed by fb read + * rather than write to HDP_REG_COHERENCY_FLUSH_CNTL + * This seems to cause problems on some AGP cards. Just use the old + * method for them. + */ + WREG32(HDP_DEBUG1, 0); + tmp = *ptr; + } else + WREG32(R_005480_HDP_MEM_COHERENCY_FLUSH_CNTL, 0x1); + + WREG32(VM_CONTEXT0_INVALIDATION_LOW_ADDR, rdev->mc.gtt_start >> 12); + WREG32(VM_CONTEXT0_INVALIDATION_HIGH_ADDR, (rdev->mc.gtt_end - 1) >> 12); + WREG32(VM_CONTEXT0_REQUEST_RESPONSE, REQUEST_TYPE(1)); + for (i = 0; i < rdev->usec_timeout; i++) { + /* read MC_STATUS */ + tmp = RREG32(VM_CONTEXT0_REQUEST_RESPONSE); + tmp = (tmp & RESPONSE_TYPE_MASK) >> RESPONSE_TYPE_SHIFT; + if (tmp == 2) { + DRM_ERROR("[drm] r600 flush TLB failed\n"); + return; + } + if (tmp) { + return; + } + DRM_UDELAY(1); + } +} + +int r600_pcie_gart_init(struct radeon_device *rdev) +{ + int r; + + if (rdev->gart.robj) { + DRM_ERROR("R600 PCIE GART already initialized\n"); + return 0; + } + /* Initialize common gart structure */ + r = radeon_gart_init(rdev); + if (r) + return r; + rdev->gart.table_size = rdev->gart.num_gpu_pages * 8; + return radeon_gart_table_vram_alloc(rdev); +} + +static int r600_pcie_gart_enable(struct radeon_device *rdev) +{ + u32 tmp; + int r, i; + + if (rdev->gart.robj == NULL) { + dev_err(rdev->dev, "No VRAM object for PCIE GART.\n"); + return -EINVAL; + } + r = radeon_gart_table_vram_pin(rdev); + if (r) + return r; + radeon_gart_restore(rdev); + + /* Setup L2 cache */ + WREG32(VM_L2_CNTL, ENABLE_L2_CACHE | ENABLE_L2_FRAGMENT_PROCESSING | + ENABLE_L2_PTE_CACHE_LRU_UPDATE_BY_WRITE | + EFFECTIVE_L2_QUEUE_SIZE(7)); + WREG32(VM_L2_CNTL2, 0); + WREG32(VM_L2_CNTL3, BANK_SELECT_0(0) | BANK_SELECT_1(1)); + /* Setup TLB control */ + tmp = ENABLE_L1_TLB | ENABLE_L1_FRAGMENT_PROCESSING | + SYSTEM_ACCESS_MODE_NOT_IN_SYS | + EFFECTIVE_L1_TLB_SIZE(5) | EFFECTIVE_L1_QUEUE_SIZE(5) | + ENABLE_WAIT_L2_QUERY; + WREG32(MC_VM_L1_TLB_MCB_RD_SYS_CNTL, tmp); + WREG32(MC_VM_L1_TLB_MCB_WR_SYS_CNTL, tmp); + WREG32(MC_VM_L1_TLB_MCB_RD_HDP_CNTL, tmp | ENABLE_L1_STRICT_ORDERING); + WREG32(MC_VM_L1_TLB_MCB_WR_HDP_CNTL, tmp); + WREG32(MC_VM_L1_TLB_MCD_RD_A_CNTL, tmp); + WREG32(MC_VM_L1_TLB_MCD_WR_A_CNTL, tmp); + WREG32(MC_VM_L1_TLB_MCD_RD_B_CNTL, tmp); + WREG32(MC_VM_L1_TLB_MCD_WR_B_CNTL, tmp); + WREG32(MC_VM_L1_TLB_MCB_RD_GFX_CNTL, tmp); + WREG32(MC_VM_L1_TLB_MCB_WR_GFX_CNTL, tmp); + WREG32(MC_VM_L1_TLB_MCB_RD_PDMA_CNTL, tmp); + WREG32(MC_VM_L1_TLB_MCB_WR_PDMA_CNTL, tmp); + WREG32(MC_VM_L1_TLB_MCB_RD_SEM_CNTL, tmp | ENABLE_SEMAPHORE_MODE); + WREG32(MC_VM_L1_TLB_MCB_WR_SEM_CNTL, tmp | ENABLE_SEMAPHORE_MODE); + WREG32(VM_CONTEXT0_PAGE_TABLE_START_ADDR, rdev->mc.gtt_start >> 12); + WREG32(VM_CONTEXT0_PAGE_TABLE_END_ADDR, rdev->mc.gtt_end >> 12); + WREG32(VM_CONTEXT0_PAGE_TABLE_BASE_ADDR, rdev->gart.table_addr >> 12); + WREG32(VM_CONTEXT0_CNTL, ENABLE_CONTEXT | PAGE_TABLE_DEPTH(0) | + RANGE_PROTECTION_FAULT_ENABLE_DEFAULT); + WREG32(VM_CONTEXT0_PROTECTION_FAULT_DEFAULT_ADDR, + (u32)(rdev->dummy_page.addr >> 12)); + for (i = 1; i < 7; i++) + WREG32(VM_CONTEXT0_CNTL + (i * 4), 0); + + r600_pcie_gart_tlb_flush(rdev); + DRM_INFO("PCIE GART of %uM enabled (table at 0x%016llX).\n", + (unsigned)(rdev->mc.gtt_size >> 20), + (unsigned long long)rdev->gart.table_addr); + rdev->gart.ready = true; + return 0; +} + +static void r600_pcie_gart_disable(struct radeon_device *rdev) +{ + u32 tmp; + int i; + + /* Disable all tables */ + for (i = 0; i < 7; i++) + WREG32(VM_CONTEXT0_CNTL + (i * 4), 0); + + /* Disable L2 cache */ + WREG32(VM_L2_CNTL, ENABLE_L2_FRAGMENT_PROCESSING | + EFFECTIVE_L2_QUEUE_SIZE(7)); + WREG32(VM_L2_CNTL3, BANK_SELECT_0(0) | BANK_SELECT_1(1)); + /* Setup L1 TLB control */ + tmp = EFFECTIVE_L1_TLB_SIZE(5) | EFFECTIVE_L1_QUEUE_SIZE(5) | + ENABLE_WAIT_L2_QUERY; + WREG32(MC_VM_L1_TLB_MCD_RD_A_CNTL, tmp); + WREG32(MC_VM_L1_TLB_MCD_WR_A_CNTL, tmp); + WREG32(MC_VM_L1_TLB_MCD_RD_B_CNTL, tmp); + WREG32(MC_VM_L1_TLB_MCD_WR_B_CNTL, tmp); + WREG32(MC_VM_L1_TLB_MCB_RD_GFX_CNTL, tmp); + WREG32(MC_VM_L1_TLB_MCB_WR_GFX_CNTL, tmp); + WREG32(MC_VM_L1_TLB_MCB_RD_PDMA_CNTL, tmp); + WREG32(MC_VM_L1_TLB_MCB_WR_PDMA_CNTL, tmp); + WREG32(MC_VM_L1_TLB_MCB_RD_SEM_CNTL, tmp); + WREG32(MC_VM_L1_TLB_MCB_WR_SEM_CNTL, tmp); + WREG32(MC_VM_L1_TLB_MCB_RD_SYS_CNTL, tmp); + WREG32(MC_VM_L1_TLB_MCB_WR_SYS_CNTL, tmp); + WREG32(MC_VM_L1_TLB_MCB_RD_HDP_CNTL, tmp); + WREG32(MC_VM_L1_TLB_MCB_WR_HDP_CNTL, tmp); + radeon_gart_table_vram_unpin(rdev); +} + +static void r600_pcie_gart_fini(struct radeon_device *rdev) +{ + radeon_gart_fini(rdev); + r600_pcie_gart_disable(rdev); + radeon_gart_table_vram_free(rdev); +} + +static void r600_agp_enable(struct radeon_device *rdev) +{ + u32 tmp; + int i; + + /* Setup L2 cache */ + WREG32(VM_L2_CNTL, ENABLE_L2_CACHE | ENABLE_L2_FRAGMENT_PROCESSING | + ENABLE_L2_PTE_CACHE_LRU_UPDATE_BY_WRITE | + EFFECTIVE_L2_QUEUE_SIZE(7)); + WREG32(VM_L2_CNTL2, 0); + WREG32(VM_L2_CNTL3, BANK_SELECT_0(0) | BANK_SELECT_1(1)); + /* Setup TLB control */ + tmp = ENABLE_L1_TLB | ENABLE_L1_FRAGMENT_PROCESSING | + SYSTEM_ACCESS_MODE_NOT_IN_SYS | + EFFECTIVE_L1_TLB_SIZE(5) | EFFECTIVE_L1_QUEUE_SIZE(5) | + ENABLE_WAIT_L2_QUERY; + WREG32(MC_VM_L1_TLB_MCB_RD_SYS_CNTL, tmp); + WREG32(MC_VM_L1_TLB_MCB_WR_SYS_CNTL, tmp); + WREG32(MC_VM_L1_TLB_MCB_RD_HDP_CNTL, tmp | ENABLE_L1_STRICT_ORDERING); + WREG32(MC_VM_L1_TLB_MCB_WR_HDP_CNTL, tmp); + WREG32(MC_VM_L1_TLB_MCD_RD_A_CNTL, tmp); + WREG32(MC_VM_L1_TLB_MCD_WR_A_CNTL, tmp); + WREG32(MC_VM_L1_TLB_MCD_RD_B_CNTL, tmp); + WREG32(MC_VM_L1_TLB_MCD_WR_B_CNTL, tmp); + WREG32(MC_VM_L1_TLB_MCB_RD_GFX_CNTL, tmp); + WREG32(MC_VM_L1_TLB_MCB_WR_GFX_CNTL, tmp); + WREG32(MC_VM_L1_TLB_MCB_RD_PDMA_CNTL, tmp); + WREG32(MC_VM_L1_TLB_MCB_WR_PDMA_CNTL, tmp); + WREG32(MC_VM_L1_TLB_MCB_RD_SEM_CNTL, tmp | ENABLE_SEMAPHORE_MODE); + WREG32(MC_VM_L1_TLB_MCB_WR_SEM_CNTL, tmp | ENABLE_SEMAPHORE_MODE); + for (i = 0; i < 7; i++) + WREG32(VM_CONTEXT0_CNTL + (i * 4), 0); +} + +int r600_mc_wait_for_idle(struct radeon_device *rdev) +{ + unsigned i; + u32 tmp; + + for (i = 0; i < rdev->usec_timeout; i++) { + /* read MC_STATUS */ + tmp = RREG32(R_000E50_SRBM_STATUS) & 0x3F00; + if (!tmp) + return 0; + DRM_UDELAY(1); + } + return -1; +} + +static void r600_mc_program(struct radeon_device *rdev) +{ + struct rv515_mc_save save; + u32 tmp; + int i, j; + + /* Initialize HDP */ + for (i = 0, j = 0; i < 32; i++, j += 0x18) { + WREG32((0x2c14 + j), 0x00000000); + WREG32((0x2c18 + j), 0x00000000); + WREG32((0x2c1c + j), 0x00000000); + WREG32((0x2c20 + j), 0x00000000); + WREG32((0x2c24 + j), 0x00000000); + } + WREG32(HDP_REG_COHERENCY_FLUSH_CNTL, 0); + + rv515_mc_stop(rdev, &save); + if (r600_mc_wait_for_idle(rdev)) { + dev_warn(rdev->dev, "Wait for MC idle timedout !\n"); + } + /* Lockout access through VGA aperture (doesn't exist before R600) */ + WREG32(VGA_HDP_CONTROL, VGA_MEMORY_DISABLE); + /* Update configuration */ + if (rdev->flags & RADEON_IS_AGP) { + if (rdev->mc.vram_start < rdev->mc.gtt_start) { + /* VRAM before AGP */ + WREG32(MC_VM_SYSTEM_APERTURE_LOW_ADDR, + rdev->mc.vram_start >> 12); + WREG32(MC_VM_SYSTEM_APERTURE_HIGH_ADDR, + rdev->mc.gtt_end >> 12); + } else { + /* VRAM after AGP */ + WREG32(MC_VM_SYSTEM_APERTURE_LOW_ADDR, + rdev->mc.gtt_start >> 12); + WREG32(MC_VM_SYSTEM_APERTURE_HIGH_ADDR, + rdev->mc.vram_end >> 12); + } + } else { + WREG32(MC_VM_SYSTEM_APERTURE_LOW_ADDR, rdev->mc.vram_start >> 12); + WREG32(MC_VM_SYSTEM_APERTURE_HIGH_ADDR, rdev->mc.vram_end >> 12); + } + WREG32(MC_VM_SYSTEM_APERTURE_DEFAULT_ADDR, rdev->vram_scratch.gpu_addr >> 12); + tmp = ((rdev->mc.vram_end >> 24) & 0xFFFF) << 16; + tmp |= ((rdev->mc.vram_start >> 24) & 0xFFFF); + WREG32(MC_VM_FB_LOCATION, tmp); + WREG32(HDP_NONSURFACE_BASE, (rdev->mc.vram_start >> 8)); + WREG32(HDP_NONSURFACE_INFO, (2 << 7)); + WREG32(HDP_NONSURFACE_SIZE, 0x3FFFFFFF); + if (rdev->flags & RADEON_IS_AGP) { + WREG32(MC_VM_AGP_TOP, rdev->mc.gtt_end >> 22); + WREG32(MC_VM_AGP_BOT, rdev->mc.gtt_start >> 22); + WREG32(MC_VM_AGP_BASE, rdev->mc.agp_base >> 22); + } else { + WREG32(MC_VM_AGP_BASE, 0); + WREG32(MC_VM_AGP_TOP, 0x0FFFFFFF); + WREG32(MC_VM_AGP_BOT, 0x0FFFFFFF); + } + if (r600_mc_wait_for_idle(rdev)) { + dev_warn(rdev->dev, "Wait for MC idle timedout !\n"); + } + rv515_mc_resume(rdev, &save); + /* we need to own VRAM, so turn off the VGA renderer here + * to stop it overwriting our objects */ + rv515_vga_render_disable(rdev); +} + +/** + * r600_vram_gtt_location - try to find VRAM & GTT location + * @rdev: radeon device structure holding all necessary informations + * @mc: memory controller structure holding memory informations + * + * Function will place try to place VRAM at same place as in CPU (PCI) + * address space as some GPU seems to have issue when we reprogram at + * different address space. + * + * If there is not enough space to fit the unvisible VRAM after the + * aperture then we limit the VRAM size to the aperture. + * + * If we are using AGP then place VRAM adjacent to AGP aperture are we need + * them to be in one from GPU point of view so that we can program GPU to + * catch access outside them (weird GPU policy see ??). + * + * This function will never fails, worst case are limiting VRAM or GTT. + * + * Note: GTT start, end, size should be initialized before calling this + * function on AGP platform. + */ +static void r600_vram_gtt_location(struct radeon_device *rdev, struct radeon_mc *mc) +{ + u64 size_bf, size_af; + + if (mc->mc_vram_size > 0xE0000000) { + /* leave room for at least 512M GTT */ + dev_warn(rdev->dev, "limiting VRAM\n"); + mc->real_vram_size = 0xE0000000; + mc->mc_vram_size = 0xE0000000; + } + if (rdev->flags & RADEON_IS_AGP) { + size_bf = mc->gtt_start; + size_af = 0xFFFFFFFF - mc->gtt_end; + if (size_bf > size_af) { + if (mc->mc_vram_size > size_bf) { + dev_warn(rdev->dev, "limiting VRAM\n"); + mc->real_vram_size = size_bf; + mc->mc_vram_size = size_bf; + } + mc->vram_start = mc->gtt_start - mc->mc_vram_size; + } else { + if (mc->mc_vram_size > size_af) { + dev_warn(rdev->dev, "limiting VRAM\n"); + mc->real_vram_size = size_af; + mc->mc_vram_size = size_af; + } + mc->vram_start = mc->gtt_end + 1; + } + mc->vram_end = mc->vram_start + mc->mc_vram_size - 1; + dev_info(rdev->dev, "VRAM: %juM 0x%08jX - 0x%08jX (%juM used)\n", + (uintmax_t)mc->mc_vram_size >> 20, (uintmax_t)mc->vram_start, + (uintmax_t)mc->vram_end, (uintmax_t)mc->real_vram_size >> 20); + } else { + u64 base = 0; + if (rdev->flags & RADEON_IS_IGP) { + base = RREG32(MC_VM_FB_LOCATION) & 0xFFFF; + base <<= 24; + } + radeon_vram_location(rdev, &rdev->mc, base); + rdev->mc.gtt_base_align = 0; + radeon_gtt_location(rdev, mc); + } +} + +static int r600_mc_init(struct radeon_device *rdev) +{ + u32 tmp; + int chansize, numchan; + + /* Get VRAM informations */ + rdev->mc.vram_is_ddr = true; + tmp = RREG32(RAMCFG); + if (tmp & CHANSIZE_OVERRIDE) { + chansize = 16; + } else if (tmp & CHANSIZE_MASK) { + chansize = 64; + } else { + chansize = 32; + } + tmp = RREG32(CHMAP); + switch ((tmp & NOOFCHAN_MASK) >> NOOFCHAN_SHIFT) { + case 0: + default: + numchan = 1; + break; + case 1: + numchan = 2; + break; + case 2: + numchan = 4; + break; + case 3: + numchan = 8; + break; + } + rdev->mc.vram_width = numchan * chansize; + /* Could aper size report 0 ? */ + rdev->mc.aper_base = drm_get_resource_start(rdev->ddev, 0); + rdev->mc.aper_size = drm_get_resource_len(rdev->ddev, 0); + /* Setup GPU memory space */ + rdev->mc.mc_vram_size = RREG32(CONFIG_MEMSIZE); + rdev->mc.real_vram_size = RREG32(CONFIG_MEMSIZE); + rdev->mc.visible_vram_size = rdev->mc.aper_size; + r600_vram_gtt_location(rdev, &rdev->mc); + + if (rdev->flags & RADEON_IS_IGP) { + rs690_pm_info(rdev); + rdev->mc.igp_sideport_enabled = radeon_atombios_sideport_present(rdev); + } + radeon_update_bandwidth_info(rdev); + return 0; +} + +int r600_vram_scratch_init(struct radeon_device *rdev) +{ + int r; + void *vram_scratch_ptr_ptr; + + if (rdev->vram_scratch.robj == NULL) { + r = radeon_bo_create(rdev, RADEON_GPU_PAGE_SIZE, + PAGE_SIZE, true, RADEON_GEM_DOMAIN_VRAM, + NULL, &rdev->vram_scratch.robj); + if (r) { + return r; + } + } + + r = radeon_bo_reserve(rdev->vram_scratch.robj, false); + if (unlikely(r != 0)) { + radeon_bo_unref(&rdev->vram_scratch.robj); + return r; + } + r = radeon_bo_pin(rdev->vram_scratch.robj, + RADEON_GEM_DOMAIN_VRAM, &rdev->vram_scratch.gpu_addr); + if (r) { + radeon_bo_unreserve(rdev->vram_scratch.robj); + radeon_bo_unref(&rdev->vram_scratch.robj); + return r; + } + vram_scratch_ptr_ptr = &rdev->vram_scratch.ptr; + r = radeon_bo_kmap(rdev->vram_scratch.robj, + vram_scratch_ptr_ptr); + if (r) + radeon_bo_unpin(rdev->vram_scratch.robj); + radeon_bo_unreserve(rdev->vram_scratch.robj); + if (r) + radeon_bo_unref(&rdev->vram_scratch.robj); + + return r; +} + +void r600_vram_scratch_fini(struct radeon_device *rdev) +{ + int r; + + if (rdev->vram_scratch.robj == NULL) { + return; + } + r = radeon_bo_reserve(rdev->vram_scratch.robj, false); + if (likely(r == 0)) { + radeon_bo_kunmap(rdev->vram_scratch.robj); + radeon_bo_unpin(rdev->vram_scratch.robj); + radeon_bo_unreserve(rdev->vram_scratch.robj); + } + radeon_bo_unref(&rdev->vram_scratch.robj); +} + +/* We doesn't check that the GPU really needs a reset we simply do the + * reset, it's up to the caller to determine if the GPU needs one. We + * might add an helper function to check that. + */ +static void r600_gpu_soft_reset_gfx(struct radeon_device *rdev) +{ + u32 grbm_busy_mask = S_008010_VC_BUSY(1) | S_008010_VGT_BUSY_NO_DMA(1) | + S_008010_VGT_BUSY(1) | S_008010_TA03_BUSY(1) | + S_008010_TC_BUSY(1) | S_008010_SX_BUSY(1) | + S_008010_SH_BUSY(1) | S_008010_SPI03_BUSY(1) | + S_008010_SMX_BUSY(1) | S_008010_SC_BUSY(1) | + S_008010_PA_BUSY(1) | S_008010_DB03_BUSY(1) | + S_008010_CR_BUSY(1) | S_008010_CB03_BUSY(1) | + S_008010_GUI_ACTIVE(1); + u32 grbm2_busy_mask = S_008014_SPI0_BUSY(1) | S_008014_SPI1_BUSY(1) | + S_008014_SPI2_BUSY(1) | S_008014_SPI3_BUSY(1) | + S_008014_TA0_BUSY(1) | S_008014_TA1_BUSY(1) | + S_008014_TA2_BUSY(1) | S_008014_TA3_BUSY(1) | + S_008014_DB0_BUSY(1) | S_008014_DB1_BUSY(1) | + S_008014_DB2_BUSY(1) | S_008014_DB3_BUSY(1) | + S_008014_CB0_BUSY(1) | S_008014_CB1_BUSY(1) | + S_008014_CB2_BUSY(1) | S_008014_CB3_BUSY(1); + u32 tmp; + + if (!(RREG32(GRBM_STATUS) & GUI_ACTIVE)) + return; + + dev_info(rdev->dev, " R_008010_GRBM_STATUS = 0x%08X\n", + RREG32(R_008010_GRBM_STATUS)); + dev_info(rdev->dev, " R_008014_GRBM_STATUS2 = 0x%08X\n", + RREG32(R_008014_GRBM_STATUS2)); + dev_info(rdev->dev, " R_000E50_SRBM_STATUS = 0x%08X\n", + RREG32(R_000E50_SRBM_STATUS)); + dev_info(rdev->dev, " R_008674_CP_STALLED_STAT1 = 0x%08X\n", + RREG32(CP_STALLED_STAT1)); + dev_info(rdev->dev, " R_008678_CP_STALLED_STAT2 = 0x%08X\n", + RREG32(CP_STALLED_STAT2)); + dev_info(rdev->dev, " R_00867C_CP_BUSY_STAT = 0x%08X\n", + RREG32(CP_BUSY_STAT)); + dev_info(rdev->dev, " R_008680_CP_STAT = 0x%08X\n", + RREG32(CP_STAT)); + + /* Disable CP parsing/prefetching */ + WREG32(R_0086D8_CP_ME_CNTL, S_0086D8_CP_ME_HALT(1)); + + /* Check if any of the rendering block is busy and reset it */ + if ((RREG32(R_008010_GRBM_STATUS) & grbm_busy_mask) || + (RREG32(R_008014_GRBM_STATUS2) & grbm2_busy_mask)) { + tmp = S_008020_SOFT_RESET_CR(1) | + S_008020_SOFT_RESET_DB(1) | + S_008020_SOFT_RESET_CB(1) | + S_008020_SOFT_RESET_PA(1) | + S_008020_SOFT_RESET_SC(1) | + S_008020_SOFT_RESET_SMX(1) | + S_008020_SOFT_RESET_SPI(1) | + S_008020_SOFT_RESET_SX(1) | + S_008020_SOFT_RESET_SH(1) | + S_008020_SOFT_RESET_TC(1) | + S_008020_SOFT_RESET_TA(1) | + S_008020_SOFT_RESET_VC(1) | + S_008020_SOFT_RESET_VGT(1); + dev_info(rdev->dev, " R_008020_GRBM_SOFT_RESET=0x%08X\n", tmp); + WREG32(R_008020_GRBM_SOFT_RESET, tmp); + RREG32(R_008020_GRBM_SOFT_RESET); + DRM_MDELAY(15); + WREG32(R_008020_GRBM_SOFT_RESET, 0); + } + /* Reset CP (we always reset CP) */ + tmp = S_008020_SOFT_RESET_CP(1); + dev_info(rdev->dev, "R_008020_GRBM_SOFT_RESET=0x%08X\n", tmp); + WREG32(R_008020_GRBM_SOFT_RESET, tmp); + RREG32(R_008020_GRBM_SOFT_RESET); + DRM_MDELAY(15); + WREG32(R_008020_GRBM_SOFT_RESET, 0); + + dev_info(rdev->dev, " R_008010_GRBM_STATUS = 0x%08X\n", + RREG32(R_008010_GRBM_STATUS)); + dev_info(rdev->dev, " R_008014_GRBM_STATUS2 = 0x%08X\n", + RREG32(R_008014_GRBM_STATUS2)); + dev_info(rdev->dev, " R_000E50_SRBM_STATUS = 0x%08X\n", + RREG32(R_000E50_SRBM_STATUS)); + dev_info(rdev->dev, " R_008674_CP_STALLED_STAT1 = 0x%08X\n", + RREG32(CP_STALLED_STAT1)); + dev_info(rdev->dev, " R_008678_CP_STALLED_STAT2 = 0x%08X\n", + RREG32(CP_STALLED_STAT2)); + dev_info(rdev->dev, " R_00867C_CP_BUSY_STAT = 0x%08X\n", + RREG32(CP_BUSY_STAT)); + dev_info(rdev->dev, " R_008680_CP_STAT = 0x%08X\n", + RREG32(CP_STAT)); + +} + +static void r600_gpu_soft_reset_dma(struct radeon_device *rdev) +{ + u32 tmp; + + if (RREG32(DMA_STATUS_REG) & DMA_IDLE) + return; + + dev_info(rdev->dev, " R_00D034_DMA_STATUS_REG = 0x%08X\n", + RREG32(DMA_STATUS_REG)); + + /* Disable DMA */ + tmp = RREG32(DMA_RB_CNTL); + tmp &= ~DMA_RB_ENABLE; + WREG32(DMA_RB_CNTL, tmp); + + /* Reset dma */ + if (rdev->family >= CHIP_RV770) + WREG32(SRBM_SOFT_RESET, RV770_SOFT_RESET_DMA); + else + WREG32(SRBM_SOFT_RESET, SOFT_RESET_DMA); + RREG32(SRBM_SOFT_RESET); + DRM_UDELAY(50); + WREG32(SRBM_SOFT_RESET, 0); + + dev_info(rdev->dev, " R_00D034_DMA_STATUS_REG = 0x%08X\n", + RREG32(DMA_STATUS_REG)); +} + +static int r600_gpu_soft_reset(struct radeon_device *rdev, u32 reset_mask) +{ + struct rv515_mc_save save; + + if (!(RREG32(GRBM_STATUS) & GUI_ACTIVE)) + reset_mask &= ~(RADEON_RESET_GFX | RADEON_RESET_COMPUTE); + + if (RREG32(DMA_STATUS_REG) & DMA_IDLE) + reset_mask &= ~RADEON_RESET_DMA; + + if (reset_mask == 0) + return 0; + + dev_info(rdev->dev, "GPU softreset: 0x%08X\n", reset_mask); + + rv515_mc_stop(rdev, &save); + if (r600_mc_wait_for_idle(rdev)) { + dev_warn(rdev->dev, "Wait for MC idle timedout !\n"); + } + + if (reset_mask & (RADEON_RESET_GFX | RADEON_RESET_COMPUTE)) + r600_gpu_soft_reset_gfx(rdev); + + if (reset_mask & RADEON_RESET_DMA) + r600_gpu_soft_reset_dma(rdev); + + /* Wait a little for things to settle down */ + DRM_MDELAY(1); + + rv515_mc_resume(rdev, &save); + return 0; +} + +bool r600_gpu_is_lockup(struct radeon_device *rdev, struct radeon_ring *ring) +{ + u32 srbm_status; + u32 grbm_status; + u32 grbm_status2; + + srbm_status = RREG32(R_000E50_SRBM_STATUS); + grbm_status = RREG32(R_008010_GRBM_STATUS); + grbm_status2 = RREG32(R_008014_GRBM_STATUS2); + if (!G_008010_GUI_ACTIVE(grbm_status)) { + radeon_ring_lockup_update(ring); + return false; + } + /* force CP activities */ + radeon_ring_force_activity(rdev, ring); + return radeon_ring_test_lockup(rdev, ring); +} + +/** + * r600_dma_is_lockup - Check if the DMA engine is locked up + * + * @rdev: radeon_device pointer + * @ring: radeon_ring structure holding ring information + * + * Check if the async DMA engine is locked up (r6xx-evergreen). + * Returns true if the engine appears to be locked up, false if not. + */ +bool r600_dma_is_lockup(struct radeon_device *rdev, struct radeon_ring *ring) +{ + u32 dma_status_reg; + + dma_status_reg = RREG32(DMA_STATUS_REG); + if (dma_status_reg & DMA_IDLE) { + radeon_ring_lockup_update(ring); + return false; + } + /* force ring activities */ + radeon_ring_force_activity(rdev, ring); + return radeon_ring_test_lockup(rdev, ring); +} + +int r600_asic_reset(struct radeon_device *rdev) +{ + return r600_gpu_soft_reset(rdev, (RADEON_RESET_GFX | + RADEON_RESET_COMPUTE | + RADEON_RESET_DMA)); +} + +u32 r6xx_remap_render_backend(struct radeon_device *rdev, + u32 tiling_pipe_num, + u32 max_rb_num, + u32 total_max_rb_num, + u32 disabled_rb_mask) +{ + u32 rendering_pipe_num, rb_num_width, req_rb_num; + u32 pipe_rb_ratio, pipe_rb_remain, tmp; + u32 data = 0, mask = 1 << (max_rb_num - 1); + unsigned i, j; + + /* mask out the RBs that don't exist on that asic */ + tmp = disabled_rb_mask | ((0xff << max_rb_num) & 0xff); + /* make sure at least one RB is available */ + if ((tmp & 0xff) != 0xff) + disabled_rb_mask = tmp; + + rendering_pipe_num = 1 << tiling_pipe_num; + req_rb_num = total_max_rb_num - r600_count_pipe_bits(disabled_rb_mask); + KASSERT(rendering_pipe_num >= req_rb_num, ("rendering_pipe_num < req_rb_num")); + + pipe_rb_ratio = rendering_pipe_num / req_rb_num; + pipe_rb_remain = rendering_pipe_num - pipe_rb_ratio * req_rb_num; + + if (rdev->family <= CHIP_RV740) { + /* r6xx/r7xx */ + rb_num_width = 2; + } else { + /* eg+ */ + rb_num_width = 4; + } + + for (i = 0; i < max_rb_num; i++) { + if (!(mask & disabled_rb_mask)) { + for (j = 0; j < pipe_rb_ratio; j++) { + data <<= rb_num_width; + data |= max_rb_num - i - 1; + } + if (pipe_rb_remain) { + data <<= rb_num_width; + data |= max_rb_num - i - 1; + pipe_rb_remain--; + } + } + mask >>= 1; + } + + return data; +} + +int r600_count_pipe_bits(uint32_t val) +{ + return hweight32(val); +} + +static void r600_gpu_init(struct radeon_device *rdev) +{ + u32 tiling_config; + u32 ramcfg; + u32 cc_rb_backend_disable; + u32 cc_gc_shader_pipe_config; + u32 tmp; + int i, j; + u32 sq_config; + u32 sq_gpr_resource_mgmt_1 = 0; + u32 sq_gpr_resource_mgmt_2 = 0; + u32 sq_thread_resource_mgmt = 0; + u32 sq_stack_resource_mgmt_1 = 0; + u32 sq_stack_resource_mgmt_2 = 0; + u32 disabled_rb_mask; + + rdev->config.r600.tiling_group_size = 256; + switch (rdev->family) { + case CHIP_R600: + rdev->config.r600.max_pipes = 4; + rdev->config.r600.max_tile_pipes = 8; + rdev->config.r600.max_simds = 4; + rdev->config.r600.max_backends = 4; + rdev->config.r600.max_gprs = 256; + rdev->config.r600.max_threads = 192; + rdev->config.r600.max_stack_entries = 256; + rdev->config.r600.max_hw_contexts = 8; + rdev->config.r600.max_gs_threads = 16; + rdev->config.r600.sx_max_export_size = 128; + rdev->config.r600.sx_max_export_pos_size = 16; + rdev->config.r600.sx_max_export_smx_size = 128; + rdev->config.r600.sq_num_cf_insts = 2; + break; + case CHIP_RV630: + case CHIP_RV635: + rdev->config.r600.max_pipes = 2; + rdev->config.r600.max_tile_pipes = 2; + rdev->config.r600.max_simds = 3; + rdev->config.r600.max_backends = 1; + rdev->config.r600.max_gprs = 128; + rdev->config.r600.max_threads = 192; + rdev->config.r600.max_stack_entries = 128; + rdev->config.r600.max_hw_contexts = 8; + rdev->config.r600.max_gs_threads = 4; + rdev->config.r600.sx_max_export_size = 128; + rdev->config.r600.sx_max_export_pos_size = 16; + rdev->config.r600.sx_max_export_smx_size = 128; + rdev->config.r600.sq_num_cf_insts = 2; + break; + case CHIP_RV610: + case CHIP_RV620: + case CHIP_RS780: + case CHIP_RS880: + rdev->config.r600.max_pipes = 1; + rdev->config.r600.max_tile_pipes = 1; + rdev->config.r600.max_simds = 2; + rdev->config.r600.max_backends = 1; + rdev->config.r600.max_gprs = 128; + rdev->config.r600.max_threads = 192; + rdev->config.r600.max_stack_entries = 128; + rdev->config.r600.max_hw_contexts = 4; + rdev->config.r600.max_gs_threads = 4; + rdev->config.r600.sx_max_export_size = 128; + rdev->config.r600.sx_max_export_pos_size = 16; + rdev->config.r600.sx_max_export_smx_size = 128; + rdev->config.r600.sq_num_cf_insts = 1; + break; + case CHIP_RV670: + rdev->config.r600.max_pipes = 4; + rdev->config.r600.max_tile_pipes = 4; + rdev->config.r600.max_simds = 4; + rdev->config.r600.max_backends = 4; + rdev->config.r600.max_gprs = 192; + rdev->config.r600.max_threads = 192; + rdev->config.r600.max_stack_entries = 256; + rdev->config.r600.max_hw_contexts = 8; + rdev->config.r600.max_gs_threads = 16; + rdev->config.r600.sx_max_export_size = 128; + rdev->config.r600.sx_max_export_pos_size = 16; + rdev->config.r600.sx_max_export_smx_size = 128; + rdev->config.r600.sq_num_cf_insts = 2; + break; + default: + break; + } + + /* Initialize HDP */ + for (i = 0, j = 0; i < 32; i++, j += 0x18) { + WREG32((0x2c14 + j), 0x00000000); + WREG32((0x2c18 + j), 0x00000000); + WREG32((0x2c1c + j), 0x00000000); + WREG32((0x2c20 + j), 0x00000000); + WREG32((0x2c24 + j), 0x00000000); + } + + WREG32(GRBM_CNTL, GRBM_READ_TIMEOUT(0xff)); + + /* Setup tiling */ + tiling_config = 0; + ramcfg = RREG32(RAMCFG); + switch (rdev->config.r600.max_tile_pipes) { + case 1: + tiling_config |= PIPE_TILING(0); + break; + case 2: + tiling_config |= PIPE_TILING(1); + break; + case 4: + tiling_config |= PIPE_TILING(2); + break; + case 8: + tiling_config |= PIPE_TILING(3); + break; + default: + break; + } + rdev->config.r600.tiling_npipes = rdev->config.r600.max_tile_pipes; + rdev->config.r600.tiling_nbanks = 4 << ((ramcfg & NOOFBANK_MASK) >> NOOFBANK_SHIFT); + tiling_config |= BANK_TILING((ramcfg & NOOFBANK_MASK) >> NOOFBANK_SHIFT); + tiling_config |= GROUP_SIZE((ramcfg & BURSTLENGTH_MASK) >> BURSTLENGTH_SHIFT); + + tmp = (ramcfg & NOOFROWS_MASK) >> NOOFROWS_SHIFT; + if (tmp > 3) { + tiling_config |= ROW_TILING(3); + tiling_config |= SAMPLE_SPLIT(3); + } else { + tiling_config |= ROW_TILING(tmp); + tiling_config |= SAMPLE_SPLIT(tmp); + } + tiling_config |= BANK_SWAPS(1); + + cc_rb_backend_disable = RREG32(CC_RB_BACKEND_DISABLE) & 0x00ff0000; + tmp = R6XX_MAX_BACKENDS - + r600_count_pipe_bits((cc_rb_backend_disable >> 16) & R6XX_MAX_BACKENDS_MASK); + if (tmp < rdev->config.r600.max_backends) { + rdev->config.r600.max_backends = tmp; + } + + cc_gc_shader_pipe_config = RREG32(CC_GC_SHADER_PIPE_CONFIG) & 0x00ffff00; + tmp = R6XX_MAX_PIPES - + r600_count_pipe_bits((cc_gc_shader_pipe_config >> 8) & R6XX_MAX_PIPES_MASK); + if (tmp < rdev->config.r600.max_pipes) { + rdev->config.r600.max_pipes = tmp; + } + tmp = R6XX_MAX_SIMDS - + r600_count_pipe_bits((cc_gc_shader_pipe_config >> 16) & R6XX_MAX_SIMDS_MASK); + if (tmp < rdev->config.r600.max_simds) { + rdev->config.r600.max_simds = tmp; + } + + disabled_rb_mask = (RREG32(CC_RB_BACKEND_DISABLE) >> 16) & R6XX_MAX_BACKENDS_MASK; + tmp = (tiling_config & PIPE_TILING__MASK) >> PIPE_TILING__SHIFT; + tmp = r6xx_remap_render_backend(rdev, tmp, rdev->config.r600.max_backends, + R6XX_MAX_BACKENDS, disabled_rb_mask); + tiling_config |= tmp << 16; + rdev->config.r600.backend_map = tmp; + + rdev->config.r600.tile_config = tiling_config; + WREG32(GB_TILING_CONFIG, tiling_config); + WREG32(DCP_TILING_CONFIG, tiling_config & 0xffff); + WREG32(HDP_TILING_CONFIG, tiling_config & 0xffff); + WREG32(DMA_TILING_CONFIG, tiling_config & 0xffff); + + tmp = R6XX_MAX_PIPES - r600_count_pipe_bits((cc_gc_shader_pipe_config & INACTIVE_QD_PIPES_MASK) >> 8); + WREG32(VGT_OUT_DEALLOC_CNTL, (tmp * 4) & DEALLOC_DIST_MASK); + WREG32(VGT_VERTEX_REUSE_BLOCK_CNTL, ((tmp * 4) - 2) & VTX_REUSE_DEPTH_MASK); + + /* Setup some CP states */ + WREG32(CP_QUEUE_THRESHOLDS, (ROQ_IB1_START(0x16) | ROQ_IB2_START(0x2b))); + WREG32(CP_MEQ_THRESHOLDS, (MEQ_END(0x40) | ROQ_END(0x40))); + + WREG32(TA_CNTL_AUX, (DISABLE_CUBE_ANISO | SYNC_GRADIENT | + SYNC_WALKER | SYNC_ALIGNER)); + /* Setup various GPU states */ + if (rdev->family == CHIP_RV670) + WREG32(ARB_GDEC_RD_CNTL, 0x00000021); + + tmp = RREG32(SX_DEBUG_1); + tmp |= SMX_EVENT_RELEASE; + if ((rdev->family > CHIP_R600)) + tmp |= ENABLE_NEW_SMX_ADDRESS; + WREG32(SX_DEBUG_1, tmp); + + if (((rdev->family) == CHIP_R600) || + ((rdev->family) == CHIP_RV630) || + ((rdev->family) == CHIP_RV610) || + ((rdev->family) == CHIP_RV620) || + ((rdev->family) == CHIP_RS780) || + ((rdev->family) == CHIP_RS880)) { + WREG32(DB_DEBUG, PREZ_MUST_WAIT_FOR_POSTZ_DONE); + } else { + WREG32(DB_DEBUG, 0); + } + WREG32(DB_WATERMARKS, (DEPTH_FREE(4) | DEPTH_CACHELINE_FREE(16) | + DEPTH_FLUSH(16) | DEPTH_PENDING_FREE(4))); + + WREG32(PA_SC_MULTI_CHIP_CNTL, 0); + WREG32(VGT_NUM_INSTANCES, 0); + + WREG32(SPI_CONFIG_CNTL, GPR_WRITE_PRIORITY(0)); + WREG32(SPI_CONFIG_CNTL_1, VTX_DONE_DELAY(0)); + + tmp = RREG32(SQ_MS_FIFO_SIZES); + if (((rdev->family) == CHIP_RV610) || + ((rdev->family) == CHIP_RV620) || + ((rdev->family) == CHIP_RS780) || + ((rdev->family) == CHIP_RS880)) { + tmp = (CACHE_FIFO_SIZE(0xa) | + FETCH_FIFO_HIWATER(0xa) | + DONE_FIFO_HIWATER(0xe0) | + ALU_UPDATE_FIFO_HIWATER(0x8)); + } else if (((rdev->family) == CHIP_R600) || + ((rdev->family) == CHIP_RV630)) { + tmp &= ~DONE_FIFO_HIWATER(0xff); + tmp |= DONE_FIFO_HIWATER(0x4); + } + WREG32(SQ_MS_FIFO_SIZES, tmp); + + /* SQ_CONFIG, SQ_GPR_RESOURCE_MGMT, SQ_THREAD_RESOURCE_MGMT, SQ_STACK_RESOURCE_MGMT + * should be adjusted as needed by the 2D/3D drivers. This just sets default values + */ + sq_config = RREG32(SQ_CONFIG); + sq_config &= ~(PS_PRIO(3) | + VS_PRIO(3) | + GS_PRIO(3) | + ES_PRIO(3)); + sq_config |= (DX9_CONSTS | + VC_ENABLE | + PS_PRIO(0) | + VS_PRIO(1) | + GS_PRIO(2) | + ES_PRIO(3)); + + if ((rdev->family) == CHIP_R600) { + sq_gpr_resource_mgmt_1 = (NUM_PS_GPRS(124) | + NUM_VS_GPRS(124) | + NUM_CLAUSE_TEMP_GPRS(4)); + sq_gpr_resource_mgmt_2 = (NUM_GS_GPRS(0) | + NUM_ES_GPRS(0)); + sq_thread_resource_mgmt = (NUM_PS_THREADS(136) | + NUM_VS_THREADS(48) | + NUM_GS_THREADS(4) | + NUM_ES_THREADS(4)); + sq_stack_resource_mgmt_1 = (NUM_PS_STACK_ENTRIES(128) | + NUM_VS_STACK_ENTRIES(128)); + sq_stack_resource_mgmt_2 = (NUM_GS_STACK_ENTRIES(0) | + NUM_ES_STACK_ENTRIES(0)); + } else if (((rdev->family) == CHIP_RV610) || + ((rdev->family) == CHIP_RV620) || + ((rdev->family) == CHIP_RS780) || + ((rdev->family) == CHIP_RS880)) { + /* no vertex cache */ + sq_config &= ~VC_ENABLE; + + sq_gpr_resource_mgmt_1 = (NUM_PS_GPRS(44) | + NUM_VS_GPRS(44) | + NUM_CLAUSE_TEMP_GPRS(2)); + sq_gpr_resource_mgmt_2 = (NUM_GS_GPRS(17) | + NUM_ES_GPRS(17)); + sq_thread_resource_mgmt = (NUM_PS_THREADS(79) | + NUM_VS_THREADS(78) | + NUM_GS_THREADS(4) | + NUM_ES_THREADS(31)); + sq_stack_resource_mgmt_1 = (NUM_PS_STACK_ENTRIES(40) | + NUM_VS_STACK_ENTRIES(40)); + sq_stack_resource_mgmt_2 = (NUM_GS_STACK_ENTRIES(32) | + NUM_ES_STACK_ENTRIES(16)); + } else if (((rdev->family) == CHIP_RV630) || + ((rdev->family) == CHIP_RV635)) { + sq_gpr_resource_mgmt_1 = (NUM_PS_GPRS(44) | + NUM_VS_GPRS(44) | + NUM_CLAUSE_TEMP_GPRS(2)); + sq_gpr_resource_mgmt_2 = (NUM_GS_GPRS(18) | + NUM_ES_GPRS(18)); + sq_thread_resource_mgmt = (NUM_PS_THREADS(79) | + NUM_VS_THREADS(78) | + NUM_GS_THREADS(4) | + NUM_ES_THREADS(31)); + sq_stack_resource_mgmt_1 = (NUM_PS_STACK_ENTRIES(40) | + NUM_VS_STACK_ENTRIES(40)); + sq_stack_resource_mgmt_2 = (NUM_GS_STACK_ENTRIES(32) | + NUM_ES_STACK_ENTRIES(16)); + } else if ((rdev->family) == CHIP_RV670) { + sq_gpr_resource_mgmt_1 = (NUM_PS_GPRS(44) | + NUM_VS_GPRS(44) | + NUM_CLAUSE_TEMP_GPRS(2)); + sq_gpr_resource_mgmt_2 = (NUM_GS_GPRS(17) | + NUM_ES_GPRS(17)); + sq_thread_resource_mgmt = (NUM_PS_THREADS(79) | + NUM_VS_THREADS(78) | + NUM_GS_THREADS(4) | + NUM_ES_THREADS(31)); + sq_stack_resource_mgmt_1 = (NUM_PS_STACK_ENTRIES(64) | + NUM_VS_STACK_ENTRIES(64)); + sq_stack_resource_mgmt_2 = (NUM_GS_STACK_ENTRIES(64) | + NUM_ES_STACK_ENTRIES(64)); + } + + WREG32(SQ_CONFIG, sq_config); + WREG32(SQ_GPR_RESOURCE_MGMT_1, sq_gpr_resource_mgmt_1); + WREG32(SQ_GPR_RESOURCE_MGMT_2, sq_gpr_resource_mgmt_2); + WREG32(SQ_THREAD_RESOURCE_MGMT, sq_thread_resource_mgmt); + WREG32(SQ_STACK_RESOURCE_MGMT_1, sq_stack_resource_mgmt_1); + WREG32(SQ_STACK_RESOURCE_MGMT_2, sq_stack_resource_mgmt_2); + + if (((rdev->family) == CHIP_RV610) || + ((rdev->family) == CHIP_RV620) || + ((rdev->family) == CHIP_RS780) || + ((rdev->family) == CHIP_RS880)) { + WREG32(VGT_CACHE_INVALIDATION, CACHE_INVALIDATION(TC_ONLY)); + } else { + WREG32(VGT_CACHE_INVALIDATION, CACHE_INVALIDATION(VC_AND_TC)); + } + + /* More default values. 2D/3D driver should adjust as needed */ + WREG32(PA_SC_AA_SAMPLE_LOCS_2S, (S0_X(0xc) | S0_Y(0x4) | + S1_X(0x4) | S1_Y(0xc))); + WREG32(PA_SC_AA_SAMPLE_LOCS_4S, (S0_X(0xe) | S0_Y(0xe) | + S1_X(0x2) | S1_Y(0x2) | + S2_X(0xa) | S2_Y(0x6) | + S3_X(0x6) | S3_Y(0xa))); + WREG32(PA_SC_AA_SAMPLE_LOCS_8S_WD0, (S0_X(0xe) | S0_Y(0xb) | + S1_X(0x4) | S1_Y(0xc) | + S2_X(0x1) | S2_Y(0x6) | + S3_X(0xa) | S3_Y(0xe))); + WREG32(PA_SC_AA_SAMPLE_LOCS_8S_WD1, (S4_X(0x6) | S4_Y(0x1) | + S5_X(0x0) | S5_Y(0x0) | + S6_X(0xb) | S6_Y(0x4) | + S7_X(0x7) | S7_Y(0x8))); + + WREG32(VGT_STRMOUT_EN, 0); + tmp = rdev->config.r600.max_pipes * 16; + switch (rdev->family) { + case CHIP_RV610: + case CHIP_RV620: + case CHIP_RS780: + case CHIP_RS880: + tmp += 32; + break; + case CHIP_RV670: + tmp += 128; + break; + default: + break; + } + if (tmp > 256) { + tmp = 256; + } + WREG32(VGT_ES_PER_GS, 128); + WREG32(VGT_GS_PER_ES, tmp); + WREG32(VGT_GS_PER_VS, 2); + WREG32(VGT_GS_VERTEX_REUSE, 16); + + /* more default values. 2D/3D driver should adjust as needed */ + WREG32(PA_SC_LINE_STIPPLE_STATE, 0); + WREG32(VGT_STRMOUT_EN, 0); + WREG32(SX_MISC, 0); + WREG32(PA_SC_MODE_CNTL, 0); + WREG32(PA_SC_AA_CONFIG, 0); + WREG32(PA_SC_LINE_STIPPLE, 0); + WREG32(SPI_INPUT_Z, 0); + WREG32(SPI_PS_IN_CONTROL_0, NUM_INTERP(2)); + WREG32(CB_COLOR7_FRAG, 0); + + /* Clear render buffer base addresses */ + WREG32(CB_COLOR0_BASE, 0); + WREG32(CB_COLOR1_BASE, 0); + WREG32(CB_COLOR2_BASE, 0); + WREG32(CB_COLOR3_BASE, 0); + WREG32(CB_COLOR4_BASE, 0); + WREG32(CB_COLOR5_BASE, 0); + WREG32(CB_COLOR6_BASE, 0); + WREG32(CB_COLOR7_BASE, 0); + WREG32(CB_COLOR7_FRAG, 0); + + switch (rdev->family) { + case CHIP_RV610: + case CHIP_RV620: + case CHIP_RS780: + case CHIP_RS880: + tmp = TC_L2_SIZE(8); + break; + case CHIP_RV630: + case CHIP_RV635: + tmp = TC_L2_SIZE(4); + break; + case CHIP_R600: + tmp = TC_L2_SIZE(0) | L2_DISABLE_LATE_HIT; + break; + default: + tmp = TC_L2_SIZE(0); + break; + } + WREG32(TC_CNTL, tmp); + + tmp = RREG32(HDP_HOST_PATH_CNTL); + WREG32(HDP_HOST_PATH_CNTL, tmp); + + tmp = RREG32(ARB_POP); + tmp |= ENABLE_TC128; + WREG32(ARB_POP, tmp); + + WREG32(PA_SC_MULTI_CHIP_CNTL, 0); + WREG32(PA_CL_ENHANCE, (CLIP_VTX_REORDER_ENA | + NUM_CLIP_SEQ(3))); + WREG32(PA_SC_ENHANCE, FORCE_EOV_MAX_CLK_CNT(4095)); + WREG32(VC_ENHANCE, 0); +} + + +/* + * Indirect registers accessor + */ +u32 r600_pciep_rreg(struct radeon_device *rdev, u32 reg) +{ + u32 r; + + WREG32(PCIE_PORT_INDEX, ((reg) & 0xff)); + (void)RREG32(PCIE_PORT_INDEX); + r = RREG32(PCIE_PORT_DATA); + return r; +} + +void r600_pciep_wreg(struct radeon_device *rdev, u32 reg, u32 v) +{ + WREG32(PCIE_PORT_INDEX, ((reg) & 0xff)); + (void)RREG32(PCIE_PORT_INDEX); + WREG32(PCIE_PORT_DATA, (v)); + (void)RREG32(PCIE_PORT_DATA); +} + +/* + * CP & Ring + */ +void r600_cp_stop(struct radeon_device *rdev) +{ + radeon_ttm_set_active_vram_size(rdev, rdev->mc.visible_vram_size); + WREG32(R_0086D8_CP_ME_CNTL, S_0086D8_CP_ME_HALT(1)); + WREG32(SCRATCH_UMSK, 0); + rdev->ring[RADEON_RING_TYPE_GFX_INDEX].ready = false; +} + +int r600_init_microcode(struct radeon_device *rdev) +{ + const char *chip_name; + const char *rlc_chip_name; + size_t pfp_req_size, me_req_size, rlc_req_size; + char fw_name[30]; + int err; + + DRM_DEBUG("\n"); + + switch (rdev->family) { + case CHIP_R600: + chip_name = "R600"; + rlc_chip_name = "R600"; + break; + case CHIP_RV610: + chip_name = "RV610"; + rlc_chip_name = "R600"; + break; + case CHIP_RV630: + chip_name = "RV630"; + rlc_chip_name = "R600"; + break; + case CHIP_RV620: + chip_name = "RV620"; + rlc_chip_name = "R600"; + break; + case CHIP_RV635: + chip_name = "RV635"; + rlc_chip_name = "R600"; + break; + case CHIP_RV670: + chip_name = "RV670"; + rlc_chip_name = "R600"; + break; + case CHIP_RS780: + case CHIP_RS880: + chip_name = "RS780"; + rlc_chip_name = "R600"; + break; + case CHIP_RV770: + chip_name = "RV770"; + rlc_chip_name = "R700"; + break; + case CHIP_RV730: + case CHIP_RV740: + chip_name = "RV730"; + rlc_chip_name = "R700"; + break; + case CHIP_RV710: + chip_name = "RV710"; + rlc_chip_name = "R700"; + break; + case CHIP_CEDAR: + chip_name = "CEDAR"; + rlc_chip_name = "CEDAR"; + break; + case CHIP_REDWOOD: + chip_name = "REDWOOD"; + rlc_chip_name = "REDWOOD"; + break; + case CHIP_JUNIPER: + chip_name = "JUNIPER"; + rlc_chip_name = "JUNIPER"; + break; + case CHIP_CYPRESS: + case CHIP_HEMLOCK: + chip_name = "CYPRESS"; + rlc_chip_name = "CYPRESS"; + break; + case CHIP_PALM: + chip_name = "PALM"; + rlc_chip_name = "SUMO"; + break; + case CHIP_SUMO: + chip_name = "SUMO"; + rlc_chip_name = "SUMO"; + break; + case CHIP_SUMO2: + chip_name = "SUMO2"; + rlc_chip_name = "SUMO"; + break; + default: panic("%s: Unsupported family %d", __func__, rdev->family); + } + + if (rdev->family >= CHIP_CEDAR) { + pfp_req_size = EVERGREEN_PFP_UCODE_SIZE * 4; + me_req_size = EVERGREEN_PM4_UCODE_SIZE * 4; + rlc_req_size = EVERGREEN_RLC_UCODE_SIZE * 4; + } else if (rdev->family >= CHIP_RV770) { + pfp_req_size = R700_PFP_UCODE_SIZE * 4; + me_req_size = R700_PM4_UCODE_SIZE * 4; + rlc_req_size = R700_RLC_UCODE_SIZE * 4; + } else { + pfp_req_size = PFP_UCODE_SIZE * 4; + me_req_size = PM4_UCODE_SIZE * 12; + rlc_req_size = RLC_UCODE_SIZE * 4; + } + + DRM_INFO("Loading %s Microcode\n", chip_name); + err = 0; + + snprintf(fw_name, sizeof(fw_name), "radeonkmsfw_%s_pfp", chip_name); + rdev->pfp_fw = firmware_get(fw_name); + if (rdev->pfp_fw == NULL) { + err = -ENOENT; + goto out; + } + if (rdev->pfp_fw->datasize != pfp_req_size) { + DRM_ERROR( + "r600_cp: Bogus length %zu in firmware \"%s\"\n", + rdev->pfp_fw->datasize, fw_name); + err = -EINVAL; + goto out; + } + + snprintf(fw_name, sizeof(fw_name), "radeonkmsfw_%s_me", chip_name); + rdev->me_fw = firmware_get(fw_name); + if (rdev->me_fw == NULL) { + err = -ENOENT; + goto out; + } + if (rdev->me_fw->datasize != me_req_size) { + DRM_ERROR( + "r600_cp: Bogus length %zu in firmware \"%s\"\n", + rdev->me_fw->datasize, fw_name); + err = -EINVAL; + } + + snprintf(fw_name, sizeof(fw_name), "radeonkmsfw_%s_rlc", rlc_chip_name); + rdev->rlc_fw = firmware_get(fw_name); + if (rdev->rlc_fw == NULL) { + err = -ENOENT; + goto out; + } + if (rdev->rlc_fw->datasize != rlc_req_size) { + DRM_ERROR( + "r600_rlc: Bogus length %zu in firmware \"%s\"\n", + rdev->rlc_fw->datasize, fw_name); + err = -EINVAL; + } + +out: + if (err) { + if (err != -EINVAL) + DRM_ERROR( + "r600_cp: Failed to load firmware \"%s\"\n", + fw_name); + if (rdev->pfp_fw != NULL) { + firmware_put(rdev->pfp_fw, FIRMWARE_UNLOAD); + rdev->pfp_fw = NULL; + } + if (rdev->me_fw != NULL) { + firmware_put(rdev->me_fw, FIRMWARE_UNLOAD); + rdev->me_fw = NULL; + } + if (rdev->rlc_fw != NULL) { + firmware_put(rdev->rlc_fw, FIRMWARE_UNLOAD); + rdev->rlc_fw = NULL; + } + } + return err; +} + +/** + * r600_fini_microcode - drop the firmwares image references + * + * @rdev: radeon_device pointer + * + * Drop the pfp, me and rlc firmwares image references. + * Called at driver shutdown. + */ +void r600_fini_microcode(struct radeon_device *rdev) +{ + + if (rdev->pfp_fw != NULL) { + firmware_put(rdev->pfp_fw, FIRMWARE_UNLOAD); + rdev->pfp_fw = NULL; + } + + if (rdev->me_fw != NULL) { + firmware_put(rdev->me_fw, FIRMWARE_UNLOAD); + rdev->me_fw = NULL; + } + + if (rdev->rlc_fw != NULL) { + firmware_put(rdev->rlc_fw, FIRMWARE_UNLOAD); + rdev->rlc_fw = NULL; + } +} + +static int r600_cp_load_microcode(struct radeon_device *rdev) +{ + const __be32 *fw_data; + int i; + + if (!rdev->me_fw || !rdev->pfp_fw) + return -EINVAL; + + r600_cp_stop(rdev); + + WREG32(CP_RB_CNTL, +#ifdef __BIG_ENDIAN + BUF_SWAP_32BIT | +#endif + RB_NO_UPDATE | RB_BLKSZ(15) | RB_BUFSZ(3)); + + /* Reset cp */ + WREG32(GRBM_SOFT_RESET, SOFT_RESET_CP); + RREG32(GRBM_SOFT_RESET); + DRM_MDELAY(15); + WREG32(GRBM_SOFT_RESET, 0); + + WREG32(CP_ME_RAM_WADDR, 0); + + fw_data = (const __be32 *)rdev->me_fw->data; + WREG32(CP_ME_RAM_WADDR, 0); + for (i = 0; i < PM4_UCODE_SIZE * 3; i++) + WREG32(CP_ME_RAM_DATA, + be32_to_cpup(fw_data++)); + + fw_data = (const __be32 *)rdev->pfp_fw->data; + WREG32(CP_PFP_UCODE_ADDR, 0); + for (i = 0; i < PFP_UCODE_SIZE; i++) + WREG32(CP_PFP_UCODE_DATA, + be32_to_cpup(fw_data++)); + + WREG32(CP_PFP_UCODE_ADDR, 0); + WREG32(CP_ME_RAM_WADDR, 0); + WREG32(CP_ME_RAM_RADDR, 0); + return 0; +} + +int r600_cp_start(struct radeon_device *rdev) +{ + struct radeon_ring *ring = &rdev->ring[RADEON_RING_TYPE_GFX_INDEX]; + int r; + uint32_t cp_me; + + r = radeon_ring_lock(rdev, ring, 7); + if (r) { + DRM_ERROR("radeon: cp failed to lock ring (%d).\n", r); + return r; + } + radeon_ring_write(ring, PACKET3(PACKET3_ME_INITIALIZE, 5)); + radeon_ring_write(ring, 0x1); + if (rdev->family >= CHIP_RV770) { + radeon_ring_write(ring, 0x0); + radeon_ring_write(ring, rdev->config.rv770.max_hw_contexts - 1); + } else { + radeon_ring_write(ring, 0x3); + radeon_ring_write(ring, rdev->config.r600.max_hw_contexts - 1); + } + radeon_ring_write(ring, PACKET3_ME_INITIALIZE_DEVICE_ID(1)); + radeon_ring_write(ring, 0); + radeon_ring_write(ring, 0); + radeon_ring_unlock_commit(rdev, ring); + + cp_me = 0xff; + WREG32(R_0086D8_CP_ME_CNTL, cp_me); + return 0; +} + +int r600_cp_resume(struct radeon_device *rdev) +{ + struct radeon_ring *ring = &rdev->ring[RADEON_RING_TYPE_GFX_INDEX]; + u32 tmp; + u32 rb_bufsz; + int r; + + /* Reset cp */ + WREG32(GRBM_SOFT_RESET, SOFT_RESET_CP); + RREG32(GRBM_SOFT_RESET); + DRM_MDELAY(15); + WREG32(GRBM_SOFT_RESET, 0); + + /* Set ring buffer size */ + rb_bufsz = drm_order(ring->ring_size / 8); + tmp = (drm_order(RADEON_GPU_PAGE_SIZE/8) << 8) | rb_bufsz; +#ifdef __BIG_ENDIAN + tmp |= BUF_SWAP_32BIT; +#endif + WREG32(CP_RB_CNTL, tmp); + WREG32(CP_SEM_WAIT_TIMER, 0x0); + + /* Set the write pointer delay */ + WREG32(CP_RB_WPTR_DELAY, 0); + + /* Initialize the ring buffer's read and write pointers */ + WREG32(CP_RB_CNTL, tmp | RB_RPTR_WR_ENA); + WREG32(CP_RB_RPTR_WR, 0); + ring->wptr = 0; + WREG32(CP_RB_WPTR, ring->wptr); + + /* set the wb address whether it's enabled or not */ + WREG32(CP_RB_RPTR_ADDR, + ((rdev->wb.gpu_addr + RADEON_WB_CP_RPTR_OFFSET) & 0xFFFFFFFC)); + WREG32(CP_RB_RPTR_ADDR_HI, upper_32_bits(rdev->wb.gpu_addr + RADEON_WB_CP_RPTR_OFFSET) & 0xFF); + WREG32(SCRATCH_ADDR, ((rdev->wb.gpu_addr + RADEON_WB_SCRATCH_OFFSET) >> 8) & 0xFFFFFFFF); + + if (rdev->wb.enabled) + WREG32(SCRATCH_UMSK, 0xff); + else { + tmp |= RB_NO_UPDATE; + WREG32(SCRATCH_UMSK, 0); + } + + DRM_MDELAY(1); + WREG32(CP_RB_CNTL, tmp); + + WREG32(CP_RB_BASE, ring->gpu_addr >> 8); + WREG32(CP_DEBUG, (1 << 27) | (1 << 28)); + + ring->rptr = RREG32(CP_RB_RPTR); + + r600_cp_start(rdev); + ring->ready = true; + r = radeon_ring_test(rdev, RADEON_RING_TYPE_GFX_INDEX, ring); + if (r) { + ring->ready = false; + return r; + } + return 0; +} + +void r600_ring_init(struct radeon_device *rdev, struct radeon_ring *ring, unsigned ring_size) +{ + u32 rb_bufsz; + int r; + + /* Align ring size */ + rb_bufsz = drm_order(ring_size / 8); + ring_size = (1 << (rb_bufsz + 1)) * 4; + ring->ring_size = ring_size; + ring->align_mask = 16 - 1; + + if (radeon_ring_supports_scratch_reg(rdev, ring)) { + r = radeon_scratch_get(rdev, &ring->rptr_save_reg); + if (r) { + DRM_ERROR("failed to get scratch reg for rptr save (%d).\n", r); + ring->rptr_save_reg = 0; + } + } +} + +void r600_cp_fini(struct radeon_device *rdev) +{ + struct radeon_ring *ring = &rdev->ring[RADEON_RING_TYPE_GFX_INDEX]; + r600_cp_stop(rdev); + radeon_ring_fini(rdev, ring); + radeon_scratch_free(rdev, ring->rptr_save_reg); +} + +/* + * DMA + * Starting with R600, the GPU has an asynchronous + * DMA engine. The programming model is very similar + * to the 3D engine (ring buffer, IBs, etc.), but the + * DMA controller has it's own packet format that is + * different form the PM4 format used by the 3D engine. + * It supports copying data, writing embedded data, + * solid fills, and a number of other things. It also + * has support for tiling/detiling of buffers. + */ +/** + * r600_dma_stop - stop the async dma engine + * + * @rdev: radeon_device pointer + * + * Stop the async dma engine (r6xx-evergreen). + */ +void r600_dma_stop(struct radeon_device *rdev) +{ + u32 rb_cntl = RREG32(DMA_RB_CNTL); + + radeon_ttm_set_active_vram_size(rdev, rdev->mc.visible_vram_size); + + rb_cntl &= ~DMA_RB_ENABLE; + WREG32(DMA_RB_CNTL, rb_cntl); + + rdev->ring[R600_RING_TYPE_DMA_INDEX].ready = false; +} + +/** + * r600_dma_resume - setup and start the async dma engine + * + * @rdev: radeon_device pointer + * + * Set up the DMA ring buffer and enable it. (r6xx-evergreen). + * Returns 0 for success, error for failure. + */ +int r600_dma_resume(struct radeon_device *rdev) +{ + struct radeon_ring *ring = &rdev->ring[R600_RING_TYPE_DMA_INDEX]; + u32 rb_cntl, dma_cntl, ib_cntl; + u32 rb_bufsz; + int r; + + /* Reset dma */ + if (rdev->family >= CHIP_RV770) + WREG32(SRBM_SOFT_RESET, RV770_SOFT_RESET_DMA); + else + WREG32(SRBM_SOFT_RESET, SOFT_RESET_DMA); + RREG32(SRBM_SOFT_RESET); + DRM_UDELAY(50); + WREG32(SRBM_SOFT_RESET, 0); + + WREG32(DMA_SEM_INCOMPLETE_TIMER_CNTL, 0); + WREG32(DMA_SEM_WAIT_FAIL_TIMER_CNTL, 0); + + /* Set ring buffer size in dwords */ + rb_bufsz = drm_order(ring->ring_size / 4); + rb_cntl = rb_bufsz << 1; +#ifdef __BIG_ENDIAN + rb_cntl |= DMA_RB_SWAP_ENABLE | DMA_RPTR_WRITEBACK_SWAP_ENABLE; +#endif + WREG32(DMA_RB_CNTL, rb_cntl); + + /* Initialize the ring buffer's read and write pointers */ + WREG32(DMA_RB_RPTR, 0); + WREG32(DMA_RB_WPTR, 0); + + /* set the wb address whether it's enabled or not */ + WREG32(DMA_RB_RPTR_ADDR_HI, + upper_32_bits(rdev->wb.gpu_addr + R600_WB_DMA_RPTR_OFFSET) & 0xFF); + WREG32(DMA_RB_RPTR_ADDR_LO, + ((rdev->wb.gpu_addr + R600_WB_DMA_RPTR_OFFSET) & 0xFFFFFFFC)); + + if (rdev->wb.enabled) + rb_cntl |= DMA_RPTR_WRITEBACK_ENABLE; + + WREG32(DMA_RB_BASE, ring->gpu_addr >> 8); + + /* enable DMA IBs */ + ib_cntl = DMA_IB_ENABLE; +#ifdef __BIG_ENDIAN + ib_cntl |= DMA_IB_SWAP_ENABLE; +#endif + WREG32(DMA_IB_CNTL, ib_cntl); + + dma_cntl = RREG32(DMA_CNTL); + dma_cntl &= ~CTXEMPTY_INT_ENABLE; + WREG32(DMA_CNTL, dma_cntl); + + if (rdev->family >= CHIP_RV770) + WREG32(DMA_MODE, 1); + + ring->wptr = 0; + WREG32(DMA_RB_WPTR, ring->wptr << 2); + + ring->rptr = RREG32(DMA_RB_RPTR) >> 2; + + WREG32(DMA_RB_CNTL, rb_cntl | DMA_RB_ENABLE); + + ring->ready = true; + + r = radeon_ring_test(rdev, R600_RING_TYPE_DMA_INDEX, ring); + if (r) { + ring->ready = false; + return r; + } + + radeon_ttm_set_active_vram_size(rdev, rdev->mc.real_vram_size); + + return 0; +} + +/** + * r600_dma_fini - tear down the async dma engine + * + * @rdev: radeon_device pointer + * + * Stop the async dma engine and free the ring (r6xx-evergreen). + */ +void r600_dma_fini(struct radeon_device *rdev) +{ + r600_dma_stop(rdev); + radeon_ring_fini(rdev, &rdev->ring[R600_RING_TYPE_DMA_INDEX]); +} + +/* + * GPU scratch registers helpers function. + */ +void r600_scratch_init(struct radeon_device *rdev) +{ + int i; + + rdev->scratch.num_reg = 7; + rdev->scratch.reg_base = SCRATCH_REG0; + for (i = 0; i < rdev->scratch.num_reg; i++) { + rdev->scratch.free[i] = true; + rdev->scratch.reg[i] = rdev->scratch.reg_base + (i * 4); + } +} + +int r600_ring_test(struct radeon_device *rdev, struct radeon_ring *ring) +{ + uint32_t scratch; + uint32_t tmp = 0; + unsigned i; + int r; + + r = radeon_scratch_get(rdev, &scratch); + if (r) { + DRM_ERROR("radeon: cp failed to get scratch reg (%d).\n", r); + return r; + } + WREG32(scratch, 0xCAFEDEAD); + r = radeon_ring_lock(rdev, ring, 3); + if (r) { + DRM_ERROR("radeon: cp failed to lock ring %d (%d).\n", ring->idx, r); + radeon_scratch_free(rdev, scratch); + return r; + } + radeon_ring_write(ring, PACKET3(PACKET3_SET_CONFIG_REG, 1)); + radeon_ring_write(ring, ((scratch - PACKET3_SET_CONFIG_REG_OFFSET) >> 2)); + radeon_ring_write(ring, 0xDEADBEEF); + radeon_ring_unlock_commit(rdev, ring); + for (i = 0; i < rdev->usec_timeout; i++) { + tmp = RREG32(scratch); + if (tmp == 0xDEADBEEF) + break; + DRM_UDELAY(1); + } + if (i < rdev->usec_timeout) { + DRM_INFO("ring test on %d succeeded in %d usecs\n", ring->idx, i); + } else { + DRM_ERROR("radeon: ring %d test failed (scratch(0x%04X)=0x%08X)\n", + ring->idx, scratch, tmp); + r = -EINVAL; + } + radeon_scratch_free(rdev, scratch); + return r; +} + +/** + * r600_dma_ring_test - simple async dma engine test + * + * @rdev: radeon_device pointer + * @ring: radeon_ring structure holding ring information + * + * Test the DMA engine by writing using it to write an + * value to memory. (r6xx-SI). + * Returns 0 for success, error for failure. + */ +int r600_dma_ring_test(struct radeon_device *rdev, + struct radeon_ring *ring) +{ + unsigned i; + int r; + volatile uint32_t *ptr = rdev->vram_scratch.ptr; + u32 tmp; + + if (!ptr) { + DRM_ERROR("invalid vram scratch pointer\n"); + return -EINVAL; + } + + tmp = 0xCAFEDEAD; + *ptr = tmp; + + r = radeon_ring_lock(rdev, ring, 4); + if (r) { + DRM_ERROR("radeon: dma failed to lock ring %d (%d).\n", ring->idx, r); + return r; + } + radeon_ring_write(ring, DMA_PACKET(DMA_PACKET_WRITE, 0, 0, 1)); + radeon_ring_write(ring, rdev->vram_scratch.gpu_addr & 0xfffffffc); + radeon_ring_write(ring, upper_32_bits(rdev->vram_scratch.gpu_addr) & 0xff); + radeon_ring_write(ring, 0xDEADBEEF); + radeon_ring_unlock_commit(rdev, ring); + + for (i = 0; i < rdev->usec_timeout; i++) { + tmp = *ptr; + if (tmp == 0xDEADBEEF) + break; + DRM_UDELAY(1); + } + + if (i < rdev->usec_timeout) { + DRM_INFO("ring test on %d succeeded in %d usecs\n", ring->idx, i); + } else { + DRM_ERROR("radeon: ring %d test failed (0x%08X)\n", + ring->idx, tmp); + r = -EINVAL; + } + return r; +} + +/* + * CP fences/semaphores + */ + +void r600_fence_ring_emit(struct radeon_device *rdev, + struct radeon_fence *fence) +{ + struct radeon_ring *ring = &rdev->ring[fence->ring]; + + if (rdev->wb.use_event) { + u64 addr = rdev->fence_drv[fence->ring].gpu_addr; + /* flush read cache over gart */ + radeon_ring_write(ring, PACKET3(PACKET3_SURFACE_SYNC, 3)); + radeon_ring_write(ring, PACKET3_TC_ACTION_ENA | + PACKET3_VC_ACTION_ENA | + PACKET3_SH_ACTION_ENA); + radeon_ring_write(ring, 0xFFFFFFFF); + radeon_ring_write(ring, 0); + radeon_ring_write(ring, 10); /* poll interval */ + /* EVENT_WRITE_EOP - flush caches, send int */ + radeon_ring_write(ring, PACKET3(PACKET3_EVENT_WRITE_EOP, 4)); + radeon_ring_write(ring, EVENT_TYPE(CACHE_FLUSH_AND_INV_EVENT_TS) | EVENT_INDEX(5)); + radeon_ring_write(ring, addr & 0xffffffff); + radeon_ring_write(ring, (upper_32_bits(addr) & 0xff) | DATA_SEL(1) | INT_SEL(2)); + radeon_ring_write(ring, fence->seq); + radeon_ring_write(ring, 0); + } else { + /* flush read cache over gart */ + radeon_ring_write(ring, PACKET3(PACKET3_SURFACE_SYNC, 3)); + radeon_ring_write(ring, PACKET3_TC_ACTION_ENA | + PACKET3_VC_ACTION_ENA | + PACKET3_SH_ACTION_ENA); + radeon_ring_write(ring, 0xFFFFFFFF); + radeon_ring_write(ring, 0); + radeon_ring_write(ring, 10); /* poll interval */ + radeon_ring_write(ring, PACKET3(PACKET3_EVENT_WRITE, 0)); + radeon_ring_write(ring, EVENT_TYPE(CACHE_FLUSH_AND_INV_EVENT) | EVENT_INDEX(0)); + /* wait for 3D idle clean */ + radeon_ring_write(ring, PACKET3(PACKET3_SET_CONFIG_REG, 1)); + radeon_ring_write(ring, (WAIT_UNTIL - PACKET3_SET_CONFIG_REG_OFFSET) >> 2); + radeon_ring_write(ring, WAIT_3D_IDLE_bit | WAIT_3D_IDLECLEAN_bit); + /* Emit fence sequence & fire IRQ */ + radeon_ring_write(ring, PACKET3(PACKET3_SET_CONFIG_REG, 1)); + radeon_ring_write(ring, ((rdev->fence_drv[fence->ring].scratch_reg - PACKET3_SET_CONFIG_REG_OFFSET) >> 2)); + radeon_ring_write(ring, fence->seq); + /* CP_INTERRUPT packet 3 no longer exists, use packet 0 */ + radeon_ring_write(ring, PACKET0(CP_INT_STATUS, 0)); + radeon_ring_write(ring, RB_INT_STAT); + } +} + +void r600_semaphore_ring_emit(struct radeon_device *rdev, + struct radeon_ring *ring, + struct radeon_semaphore *semaphore, + bool emit_wait) +{ + uint64_t addr = semaphore->gpu_addr; + unsigned sel = emit_wait ? PACKET3_SEM_SEL_WAIT : PACKET3_SEM_SEL_SIGNAL; + + if (rdev->family < CHIP_CAYMAN) + sel |= PACKET3_SEM_WAIT_ON_SIGNAL; + + radeon_ring_write(ring, PACKET3(PACKET3_MEM_SEMAPHORE, 1)); + radeon_ring_write(ring, addr & 0xffffffff); + radeon_ring_write(ring, (upper_32_bits(addr) & 0xff) | sel); +} + +/* + * DMA fences/semaphores + */ + +/** + * r600_dma_fence_ring_emit - emit a fence on the DMA ring + * + * @rdev: radeon_device pointer + * @fence: radeon fence object + * + * Add a DMA fence packet to the ring to write + * the fence seq number and DMA trap packet to generate + * an interrupt if needed (r6xx-r7xx). + */ +void r600_dma_fence_ring_emit(struct radeon_device *rdev, + struct radeon_fence *fence) +{ + struct radeon_ring *ring = &rdev->ring[fence->ring]; + u64 addr = rdev->fence_drv[fence->ring].gpu_addr; + + /* write the fence */ + radeon_ring_write(ring, DMA_PACKET(DMA_PACKET_FENCE, 0, 0, 0)); + radeon_ring_write(ring, addr & 0xfffffffc); + radeon_ring_write(ring, (upper_32_bits(addr) & 0xff)); + radeon_ring_write(ring, lower_32_bits(fence->seq)); + /* generate an interrupt */ + radeon_ring_write(ring, DMA_PACKET(DMA_PACKET_TRAP, 0, 0, 0)); +} + +/** + * r600_dma_semaphore_ring_emit - emit a semaphore on the dma ring + * + * @rdev: radeon_device pointer + * @ring: radeon_ring structure holding ring information + * @semaphore: radeon semaphore object + * @emit_wait: wait or signal semaphore + * + * Add a DMA semaphore packet to the ring wait on or signal + * other rings (r6xx-SI). + */ +void r600_dma_semaphore_ring_emit(struct radeon_device *rdev, + struct radeon_ring *ring, + struct radeon_semaphore *semaphore, + bool emit_wait) +{ + u64 addr = semaphore->gpu_addr; + u32 s = emit_wait ? 0 : 1; + + radeon_ring_write(ring, DMA_PACKET(DMA_PACKET_SEMAPHORE, 0, s, 0)); + radeon_ring_write(ring, addr & 0xfffffffc); + radeon_ring_write(ring, upper_32_bits(addr) & 0xff); +} + +int r600_copy_blit(struct radeon_device *rdev, + uint64_t src_offset, + uint64_t dst_offset, + unsigned num_gpu_pages, + struct radeon_fence **fence) +{ + struct radeon_semaphore *sem = NULL; + struct radeon_sa_bo *vb = NULL; + int r; + + r = r600_blit_prepare_copy(rdev, num_gpu_pages, fence, &vb, &sem); + if (r) { + return r; + } + r600_kms_blit_copy(rdev, src_offset, dst_offset, num_gpu_pages, vb); + r600_blit_done_copy(rdev, fence, vb, sem); + return 0; +} + +/** + * r600_copy_dma - copy pages using the DMA engine + * + * @rdev: radeon_device pointer + * @src_offset: src GPU address + * @dst_offset: dst GPU address + * @num_gpu_pages: number of GPU pages to xfer + * @fence: radeon fence object + * + * Copy GPU paging using the DMA engine (r6xx). + * Used by the radeon ttm implementation to move pages if + * registered as the asic copy callback. + */ +int r600_copy_dma(struct radeon_device *rdev, + uint64_t src_offset, uint64_t dst_offset, + unsigned num_gpu_pages, + struct radeon_fence **fence) +{ + struct radeon_semaphore *sem = NULL; + int ring_index = rdev->asic->copy.dma_ring_index; + struct radeon_ring *ring = &rdev->ring[ring_index]; + u32 size_in_dw, cur_size_in_dw; + int i, num_loops; + int r = 0; + + r = radeon_semaphore_create(rdev, &sem); + if (r) { + DRM_ERROR("radeon: moving bo (%d).\n", r); + return r; + } + + size_in_dw = (num_gpu_pages << RADEON_GPU_PAGE_SHIFT) / 4; + num_loops = DIV_ROUND_UP(size_in_dw, 0xFFFE); + r = radeon_ring_lock(rdev, ring, num_loops * 4 + 8); + if (r) { + DRM_ERROR("radeon: moving bo (%d).\n", r); + radeon_semaphore_free(rdev, &sem, NULL); + return r; + } + + if (radeon_fence_need_sync(*fence, ring->idx)) { + radeon_semaphore_sync_rings(rdev, sem, (*fence)->ring, + ring->idx); + radeon_fence_note_sync(*fence, ring->idx); + } else { + radeon_semaphore_free(rdev, &sem, NULL); + } + + for (i = 0; i < num_loops; i++) { + cur_size_in_dw = size_in_dw; + if (cur_size_in_dw > 0xFFFE) + cur_size_in_dw = 0xFFFE; + size_in_dw -= cur_size_in_dw; + radeon_ring_write(ring, DMA_PACKET(DMA_PACKET_COPY, 0, 0, cur_size_in_dw)); + radeon_ring_write(ring, dst_offset & 0xfffffffc); + radeon_ring_write(ring, src_offset & 0xfffffffc); + radeon_ring_write(ring, (((upper_32_bits(dst_offset) & 0xff) << 16) | + (upper_32_bits(src_offset) & 0xff))); + src_offset += cur_size_in_dw * 4; + dst_offset += cur_size_in_dw * 4; + } + + r = radeon_fence_emit(rdev, fence, ring->idx); + if (r) { + radeon_ring_unlock_undo(rdev, ring); + return r; + } + + radeon_ring_unlock_commit(rdev, ring); + radeon_semaphore_free(rdev, &sem, *fence); + + return r; +} + +int r600_set_surface_reg(struct radeon_device *rdev, int reg, + uint32_t tiling_flags, uint32_t pitch, + uint32_t offset, uint32_t obj_size) +{ + /* FIXME: implement */ + return 0; +} + +void r600_clear_surface_reg(struct radeon_device *rdev, int reg) +{ + /* FIXME: implement */ +} + +static int r600_startup(struct radeon_device *rdev) +{ + struct radeon_ring *ring; + int r; + + /* enable pcie gen2 link */ + r600_pcie_gen2_enable(rdev); + + if (!rdev->me_fw || !rdev->pfp_fw || !rdev->rlc_fw) { + r = r600_init_microcode(rdev); + if (r) { + DRM_ERROR("Failed to load firmware!\n"); + return r; + } + } + + r = r600_vram_scratch_init(rdev); + if (r) + return r; + + r600_mc_program(rdev); + if (rdev->flags & RADEON_IS_AGP) { + r600_agp_enable(rdev); + } else { + r = r600_pcie_gart_enable(rdev); + if (r) + return r; + } + r600_gpu_init(rdev); + r = r600_blit_init(rdev); + if (r) { + r600_blit_fini(rdev); + rdev->asic->copy.copy = NULL; + dev_warn(rdev->dev, "failed blitter (%d) falling back to memcpy\n", r); + } + + /* allocate wb buffer */ + r = radeon_wb_init(rdev); + if (r) + return r; + + r = radeon_fence_driver_start_ring(rdev, RADEON_RING_TYPE_GFX_INDEX); + if (r) { + dev_err(rdev->dev, "failed initializing CP fences (%d).\n", r); + return r; + } + + r = radeon_fence_driver_start_ring(rdev, R600_RING_TYPE_DMA_INDEX); + if (r) { + dev_err(rdev->dev, "failed initializing DMA fences (%d).\n", r); + return r; + } + + /* Enable IRQ */ + r = r600_irq_init(rdev); + if (r) { + DRM_ERROR("radeon: IH init failed (%d).\n", r); + radeon_irq_kms_fini(rdev); + return r; + } + r600_irq_set(rdev); + + ring = &rdev->ring[RADEON_RING_TYPE_GFX_INDEX]; + r = radeon_ring_init(rdev, ring, ring->ring_size, RADEON_WB_CP_RPTR_OFFSET, + R600_CP_RB_RPTR, R600_CP_RB_WPTR, + 0, 0xfffff, RADEON_CP_PACKET2); + if (r) + return r; + + ring = &rdev->ring[R600_RING_TYPE_DMA_INDEX]; + r = radeon_ring_init(rdev, ring, ring->ring_size, R600_WB_DMA_RPTR_OFFSET, + DMA_RB_RPTR, DMA_RB_WPTR, + 2, 0x3fffc, DMA_PACKET(DMA_PACKET_NOP, 0, 0, 0)); + if (r) + return r; + + r = r600_cp_load_microcode(rdev); + if (r) + return r; + r = r600_cp_resume(rdev); + if (r) + return r; + + r = r600_dma_resume(rdev); + if (r) + return r; + + r = radeon_ib_pool_init(rdev); + if (r) { + dev_err(rdev->dev, "IB initialization failed (%d).\n", r); + return r; + } + + r = r600_audio_init(rdev); + if (r) { + DRM_ERROR("radeon: audio init failed\n"); + return r; + } + + return 0; +} + +void r600_vga_set_state(struct radeon_device *rdev, bool state) +{ + uint32_t temp; + + temp = RREG32(CONFIG_CNTL); + if (state == false) { + temp &= ~(1<<0); + temp |= (1<<1); + } else { + temp &= ~(1<<1); + } + WREG32(CONFIG_CNTL, temp); +} + +int r600_resume(struct radeon_device *rdev) +{ + int r; + + /* Do not reset GPU before posting, on r600 hw unlike on r500 hw, + * posting will perform necessary task to bring back GPU into good + * shape. + */ + /* post card */ + atom_asic_init(rdev->mode_info.atom_context); + + rdev->accel_working = true; + r = r600_startup(rdev); + if (r) { + DRM_ERROR("r600 startup failed on resume\n"); + rdev->accel_working = false; + return r; + } + + return r; +} + +int r600_suspend(struct radeon_device *rdev) +{ + r600_audio_fini(rdev); + r600_cp_stop(rdev); + r600_dma_stop(rdev); + r600_irq_suspend(rdev); + radeon_wb_disable(rdev); + r600_pcie_gart_disable(rdev); + + return 0; +} + +/* Plan is to move initialization in that function and use + * helper function so that radeon_device_init pretty much + * do nothing more than calling asic specific function. This + * should also allow to remove a bunch of callback function + * like vram_info. + */ +int r600_init(struct radeon_device *rdev) +{ + int r; + + if (r600_debugfs_mc_info_init(rdev)) { + DRM_ERROR("Failed to register debugfs file for mc !\n"); + } + /* Read BIOS */ + if (!radeon_get_bios(rdev)) { + if (ASIC_IS_AVIVO(rdev)) + return -EINVAL; + } + /* Must be an ATOMBIOS */ + if (!rdev->is_atom_bios) { + dev_err(rdev->dev, "Expecting atombios for R600 GPU\n"); + return -EINVAL; + } + r = radeon_atombios_init(rdev); + if (r) + return r; + /* Post card if necessary */ + if (!radeon_card_posted(rdev)) { + if (!rdev->bios) { + dev_err(rdev->dev, "Card not posted and no BIOS - ignoring\n"); + return -EINVAL; + } + DRM_INFO("GPU not posted. posting now...\n"); + atom_asic_init(rdev->mode_info.atom_context); + } + /* Initialize scratch registers */ + r600_scratch_init(rdev); + /* Initialize surface registers */ + radeon_surface_init(rdev); + /* Initialize clocks */ + radeon_get_clock_info(rdev->ddev); + /* Fence driver */ + r = radeon_fence_driver_init(rdev); + if (r) + return r; + if (rdev->flags & RADEON_IS_AGP) { + r = radeon_agp_init(rdev); + if (r) + radeon_agp_disable(rdev); + } + r = r600_mc_init(rdev); + if (r) + return r; + /* Memory manager */ + r = radeon_bo_init(rdev); + if (r) + return r; + + r = radeon_irq_kms_init(rdev); + if (r) + return r; + + rdev->ring[RADEON_RING_TYPE_GFX_INDEX].ring_obj = NULL; + r600_ring_init(rdev, &rdev->ring[RADEON_RING_TYPE_GFX_INDEX], 1024 * 1024); + + rdev->ring[R600_RING_TYPE_DMA_INDEX].ring_obj = NULL; + r600_ring_init(rdev, &rdev->ring[R600_RING_TYPE_DMA_INDEX], 64 * 1024); + + rdev->ih.ring_obj = NULL; + r600_ih_ring_init(rdev, 64 * 1024); + + r = r600_pcie_gart_init(rdev); + if (r) + return r; + + rdev->accel_working = true; + r = r600_startup(rdev); + if (r) { + dev_err(rdev->dev, "disabling GPU acceleration\n"); + r600_cp_fini(rdev); + r600_dma_fini(rdev); + r600_irq_fini(rdev); + radeon_wb_fini(rdev); + radeon_ib_pool_fini(rdev); + radeon_irq_kms_fini(rdev); + r600_pcie_gart_fini(rdev); + rdev->accel_working = false; + } + + return 0; +} + +void r600_fini(struct radeon_device *rdev) +{ + r600_audio_fini(rdev); + r600_blit_fini(rdev); + r600_cp_fini(rdev); + r600_dma_fini(rdev); + r600_irq_fini(rdev); + radeon_wb_fini(rdev); + radeon_ib_pool_fini(rdev); + radeon_irq_kms_fini(rdev); + r600_pcie_gart_fini(rdev); + r600_vram_scratch_fini(rdev); + radeon_agp_fini(rdev); + radeon_gem_fini(rdev); + radeon_fence_driver_fini(rdev); + radeon_bo_fini(rdev); + radeon_atombios_fini(rdev); + r600_fini_microcode(rdev); + free(rdev->bios, DRM_MEM_DRIVER); + rdev->bios = NULL; +} + + +/* + * CS stuff + */ +void r600_ring_ib_execute(struct radeon_device *rdev, struct radeon_ib *ib) +{ + struct radeon_ring *ring = &rdev->ring[ib->ring]; + u32 next_rptr; + + if (ring->rptr_save_reg) { + next_rptr = ring->wptr + 3 + 4; + radeon_ring_write(ring, PACKET3(PACKET3_SET_CONFIG_REG, 1)); + radeon_ring_write(ring, ((ring->rptr_save_reg - + PACKET3_SET_CONFIG_REG_OFFSET) >> 2)); + radeon_ring_write(ring, next_rptr); + } else if (rdev->wb.enabled) { + next_rptr = ring->wptr + 5 + 4; + radeon_ring_write(ring, PACKET3(PACKET3_MEM_WRITE, 3)); + radeon_ring_write(ring, ring->next_rptr_gpu_addr & 0xfffffffc); + radeon_ring_write(ring, (upper_32_bits(ring->next_rptr_gpu_addr) & 0xff) | (1 << 18)); + radeon_ring_write(ring, next_rptr); + radeon_ring_write(ring, 0); + } + + radeon_ring_write(ring, PACKET3(PACKET3_INDIRECT_BUFFER, 2)); + radeon_ring_write(ring, +#ifdef __BIG_ENDIAN + (2 << 0) | +#endif + (ib->gpu_addr & 0xFFFFFFFC)); + radeon_ring_write(ring, upper_32_bits(ib->gpu_addr) & 0xFF); + radeon_ring_write(ring, ib->length_dw); +} + +int r600_ib_test(struct radeon_device *rdev, struct radeon_ring *ring) +{ + struct radeon_ib ib; + uint32_t scratch; + uint32_t tmp = 0; + unsigned i; + int r; + + r = radeon_scratch_get(rdev, &scratch); + if (r) { + DRM_ERROR("radeon: failed to get scratch reg (%d).\n", r); + return r; + } + WREG32(scratch, 0xCAFEDEAD); + r = radeon_ib_get(rdev, ring->idx, &ib, NULL, 256); + if (r) { + DRM_ERROR("radeon: failed to get ib (%d).\n", r); + goto free_scratch; + } + ib.ptr[0] = PACKET3(PACKET3_SET_CONFIG_REG, 1); + ib.ptr[1] = ((scratch - PACKET3_SET_CONFIG_REG_OFFSET) >> 2); + ib.ptr[2] = 0xDEADBEEF; + ib.length_dw = 3; + r = radeon_ib_schedule(rdev, &ib, NULL); + if (r) { + DRM_ERROR("radeon: failed to schedule ib (%d).\n", r); + goto free_ib; + } + r = radeon_fence_wait(ib.fence, false); + if (r) { + DRM_ERROR("radeon: fence wait failed (%d).\n", r); + goto free_ib; + } + for (i = 0; i < rdev->usec_timeout; i++) { + tmp = RREG32(scratch); + if (tmp == 0xDEADBEEF) + break; + DRM_UDELAY(1); + } + if (i < rdev->usec_timeout) { + DRM_INFO("ib test on ring %d succeeded in %u usecs\n", ib.fence->ring, i); + } else { + DRM_ERROR("radeon: ib test failed (scratch(0x%04X)=0x%08X)\n", + scratch, tmp); + r = -EINVAL; + } +free_ib: + radeon_ib_free(rdev, &ib); +free_scratch: + radeon_scratch_free(rdev, scratch); + return r; +} + +/** + * r600_dma_ib_test - test an IB on the DMA engine + * + * @rdev: radeon_device pointer + * @ring: radeon_ring structure holding ring information + * + * Test a simple IB in the DMA ring (r6xx-SI). + * Returns 0 on success, error on failure. + */ +int r600_dma_ib_test(struct radeon_device *rdev, struct radeon_ring *ring) +{ + struct radeon_ib ib; + unsigned i; + int r; + volatile uint32_t *ptr = rdev->vram_scratch.ptr; + u32 tmp = 0; + + if (!ptr) { + DRM_ERROR("invalid vram scratch pointer\n"); + return -EINVAL; + } + + tmp = 0xCAFEDEAD; + *ptr = tmp; + + r = radeon_ib_get(rdev, ring->idx, &ib, NULL, 256); + if (r) { + DRM_ERROR("radeon: failed to get ib (%d).\n", r); + return r; + } + + ib.ptr[0] = DMA_PACKET(DMA_PACKET_WRITE, 0, 0, 1); + ib.ptr[1] = rdev->vram_scratch.gpu_addr & 0xfffffffc; + ib.ptr[2] = upper_32_bits(rdev->vram_scratch.gpu_addr) & 0xff; + ib.ptr[3] = 0xDEADBEEF; + ib.length_dw = 4; + + r = radeon_ib_schedule(rdev, &ib, NULL); + if (r) { + radeon_ib_free(rdev, &ib); + DRM_ERROR("radeon: failed to schedule ib (%d).\n", r); + return r; + } + r = radeon_fence_wait(ib.fence, false); + if (r) { + radeon_ib_free(rdev, &ib); + DRM_ERROR("radeon: fence wait failed (%d).\n", r); + return r; + } + for (i = 0; i < rdev->usec_timeout; i++) { + tmp = *ptr; + if (tmp == 0xDEADBEEF) + break; + DRM_UDELAY(1); + } + if (i < rdev->usec_timeout) { + DRM_INFO("ib test on ring %d succeeded in %u usecs\n", ib.fence->ring, i); + } else { + DRM_ERROR("radeon: ib test failed (0x%08X)\n", tmp); + r = -EINVAL; + } + radeon_ib_free(rdev, &ib); + return r; +} + +/** + * r600_dma_ring_ib_execute - Schedule an IB on the DMA engine + * + * @rdev: radeon_device pointer + * @ib: IB object to schedule + * + * Schedule an IB in the DMA ring (r6xx-r7xx). + */ +void r600_dma_ring_ib_execute(struct radeon_device *rdev, struct radeon_ib *ib) +{ + struct radeon_ring *ring = &rdev->ring[ib->ring]; + + if (rdev->wb.enabled) { + u32 next_rptr = ring->wptr + 4; + while ((next_rptr & 7) != 5) + next_rptr++; + next_rptr += 3; + radeon_ring_write(ring, DMA_PACKET(DMA_PACKET_WRITE, 0, 0, 1)); + radeon_ring_write(ring, ring->next_rptr_gpu_addr & 0xfffffffc); + radeon_ring_write(ring, upper_32_bits(ring->next_rptr_gpu_addr) & 0xff); + radeon_ring_write(ring, next_rptr); + } + + /* The indirect buffer packet must end on an 8 DW boundary in the DMA ring. + * Pad as necessary with NOPs. + */ + while ((ring->wptr & 7) != 5) + radeon_ring_write(ring, DMA_PACKET(DMA_PACKET_NOP, 0, 0, 0)); + radeon_ring_write(ring, DMA_PACKET(DMA_PACKET_INDIRECT_BUFFER, 0, 0, 0)); + radeon_ring_write(ring, (ib->gpu_addr & 0xFFFFFFE0)); + radeon_ring_write(ring, (ib->length_dw << 16) | (upper_32_bits(ib->gpu_addr) & 0xFF)); + +} + +/* + * Interrupts + * + * Interrupts use a ring buffer on r6xx/r7xx hardware. It works pretty + * the same as the CP ring buffer, but in reverse. Rather than the CPU + * writing to the ring and the GPU consuming, the GPU writes to the ring + * and host consumes. As the host irq handler processes interrupts, it + * increments the rptr. When the rptr catches up with the wptr, all the + * current interrupts have been processed. + */ + +void r600_ih_ring_init(struct radeon_device *rdev, unsigned ring_size) +{ + u32 rb_bufsz; + + /* Align ring size */ + rb_bufsz = drm_order(ring_size / 4); + ring_size = (1 << rb_bufsz) * 4; + rdev->ih.ring_size = ring_size; + rdev->ih.ptr_mask = rdev->ih.ring_size - 1; + rdev->ih.rptr = 0; +} + +int r600_ih_ring_alloc(struct radeon_device *rdev) +{ + int r; + void *ring_ptr; + + /* Allocate ring buffer */ + if (rdev->ih.ring_obj == NULL) { + r = radeon_bo_create(rdev, rdev->ih.ring_size, + PAGE_SIZE, true, + RADEON_GEM_DOMAIN_GTT, + NULL, &rdev->ih.ring_obj); + if (r) { + DRM_ERROR("radeon: failed to create ih ring buffer (%d).\n", r); + return r; + } + r = radeon_bo_reserve(rdev->ih.ring_obj, false); + if (unlikely(r != 0)) { + radeon_bo_unref(&rdev->ih.ring_obj); + return r; + } + r = radeon_bo_pin(rdev->ih.ring_obj, + RADEON_GEM_DOMAIN_GTT, + &rdev->ih.gpu_addr); + if (r) { + radeon_bo_unreserve(rdev->ih.ring_obj); + radeon_bo_unref(&rdev->ih.ring_obj); + DRM_ERROR("radeon: failed to pin ih ring buffer (%d).\n", r); + return r; + } + ring_ptr = &rdev->ih.ring; + r = radeon_bo_kmap(rdev->ih.ring_obj, + ring_ptr); + if (r) + radeon_bo_unpin(rdev->ih.ring_obj); + radeon_bo_unreserve(rdev->ih.ring_obj); + if (r) { + DRM_ERROR("radeon: failed to map ih ring buffer (%d).\n", r); + radeon_bo_unref(&rdev->ih.ring_obj); + return r; + } + } + return 0; +} + +void r600_ih_ring_fini(struct radeon_device *rdev) +{ + int r; + if (rdev->ih.ring_obj) { + r = radeon_bo_reserve(rdev->ih.ring_obj, false); + if (likely(r == 0)) { + radeon_bo_kunmap(rdev->ih.ring_obj); + radeon_bo_unpin(rdev->ih.ring_obj); + radeon_bo_unreserve(rdev->ih.ring_obj); + } + radeon_bo_unref(&rdev->ih.ring_obj); + rdev->ih.ring = NULL; + rdev->ih.ring_obj = NULL; + } +} + +void r600_rlc_stop(struct radeon_device *rdev) +{ + + if ((rdev->family >= CHIP_RV770) && + (rdev->family <= CHIP_RV740)) { + /* r7xx asics need to soft reset RLC before halting */ + WREG32(SRBM_SOFT_RESET, SOFT_RESET_RLC); + RREG32(SRBM_SOFT_RESET); + DRM_MDELAY(15); + WREG32(SRBM_SOFT_RESET, 0); + RREG32(SRBM_SOFT_RESET); + } + + WREG32(RLC_CNTL, 0); +} + +static void r600_rlc_start(struct radeon_device *rdev) +{ + WREG32(RLC_CNTL, RLC_ENABLE); +} + +static int r600_rlc_init(struct radeon_device *rdev) +{ + u32 i; + const __be32 *fw_data; + + if (!rdev->rlc_fw) + return -EINVAL; + + r600_rlc_stop(rdev); + + WREG32(RLC_HB_CNTL, 0); + + if (rdev->family == CHIP_ARUBA) { + WREG32(TN_RLC_SAVE_AND_RESTORE_BASE, rdev->rlc.save_restore_gpu_addr >> 8); + WREG32(TN_RLC_CLEAR_STATE_RESTORE_BASE, rdev->rlc.clear_state_gpu_addr >> 8); + } + if (rdev->family <= CHIP_CAYMAN) { + WREG32(RLC_HB_BASE, 0); + WREG32(RLC_HB_RPTR, 0); + WREG32(RLC_HB_WPTR, 0); + } + if (rdev->family <= CHIP_CAICOS) { + WREG32(RLC_HB_WPTR_LSB_ADDR, 0); + WREG32(RLC_HB_WPTR_MSB_ADDR, 0); + } + WREG32(RLC_MC_CNTL, 0); + WREG32(RLC_UCODE_CNTL, 0); + + fw_data = (const __be32 *)rdev->rlc_fw->data; + if (rdev->family >= CHIP_ARUBA) { + for (i = 0; i < ARUBA_RLC_UCODE_SIZE; i++) { + WREG32(RLC_UCODE_ADDR, i); + WREG32(RLC_UCODE_DATA, be32_to_cpup(fw_data++)); + } + } else if (rdev->family >= CHIP_CAYMAN) { + for (i = 0; i < CAYMAN_RLC_UCODE_SIZE; i++) { + WREG32(RLC_UCODE_ADDR, i); + WREG32(RLC_UCODE_DATA, be32_to_cpup(fw_data++)); + } + } else if (rdev->family >= CHIP_CEDAR) { + for (i = 0; i < EVERGREEN_RLC_UCODE_SIZE; i++) { + WREG32(RLC_UCODE_ADDR, i); + WREG32(RLC_UCODE_DATA, be32_to_cpup(fw_data++)); + } + } else if (rdev->family >= CHIP_RV770) { + for (i = 0; i < R700_RLC_UCODE_SIZE; i++) { + WREG32(RLC_UCODE_ADDR, i); + WREG32(RLC_UCODE_DATA, be32_to_cpup(fw_data++)); + } + } else { + for (i = 0; i < RLC_UCODE_SIZE; i++) { + WREG32(RLC_UCODE_ADDR, i); + WREG32(RLC_UCODE_DATA, be32_to_cpup(fw_data++)); + } + } + WREG32(RLC_UCODE_ADDR, 0); + + r600_rlc_start(rdev); + + return 0; +} + +static void r600_enable_interrupts(struct radeon_device *rdev) +{ + u32 ih_cntl = RREG32(IH_CNTL); + u32 ih_rb_cntl = RREG32(IH_RB_CNTL); + + ih_cntl |= ENABLE_INTR; + ih_rb_cntl |= IH_RB_ENABLE; + WREG32(IH_CNTL, ih_cntl); + WREG32(IH_RB_CNTL, ih_rb_cntl); + rdev->ih.enabled = true; +} + +void r600_disable_interrupts(struct radeon_device *rdev) +{ + u32 ih_rb_cntl = RREG32(IH_RB_CNTL); + u32 ih_cntl = RREG32(IH_CNTL); + + ih_rb_cntl &= ~IH_RB_ENABLE; + ih_cntl &= ~ENABLE_INTR; + WREG32(IH_RB_CNTL, ih_rb_cntl); + WREG32(IH_CNTL, ih_cntl); + /* set rptr, wptr to 0 */ + WREG32(IH_RB_RPTR, 0); + WREG32(IH_RB_WPTR, 0); + rdev->ih.enabled = false; + rdev->ih.rptr = 0; +} + +static void r600_disable_interrupt_state(struct radeon_device *rdev) +{ + u32 tmp; + + WREG32(CP_INT_CNTL, CNTX_BUSY_INT_ENABLE | CNTX_EMPTY_INT_ENABLE); + tmp = RREG32(DMA_CNTL) & ~TRAP_ENABLE; + WREG32(DMA_CNTL, tmp); + WREG32(GRBM_INT_CNTL, 0); + WREG32(DxMODE_INT_MASK, 0); + WREG32(D1GRPH_INTERRUPT_CONTROL, 0); + WREG32(D2GRPH_INTERRUPT_CONTROL, 0); + if (ASIC_IS_DCE3(rdev)) { + WREG32(DCE3_DACA_AUTODETECT_INT_CONTROL, 0); + WREG32(DCE3_DACB_AUTODETECT_INT_CONTROL, 0); + tmp = RREG32(DC_HPD1_INT_CONTROL) & DC_HPDx_INT_POLARITY; + WREG32(DC_HPD1_INT_CONTROL, tmp); + tmp = RREG32(DC_HPD2_INT_CONTROL) & DC_HPDx_INT_POLARITY; + WREG32(DC_HPD2_INT_CONTROL, tmp); + tmp = RREG32(DC_HPD3_INT_CONTROL) & DC_HPDx_INT_POLARITY; + WREG32(DC_HPD3_INT_CONTROL, tmp); + tmp = RREG32(DC_HPD4_INT_CONTROL) & DC_HPDx_INT_POLARITY; + WREG32(DC_HPD4_INT_CONTROL, tmp); + if (ASIC_IS_DCE32(rdev)) { + tmp = RREG32(DC_HPD5_INT_CONTROL) & DC_HPDx_INT_POLARITY; + WREG32(DC_HPD5_INT_CONTROL, tmp); + tmp = RREG32(DC_HPD6_INT_CONTROL) & DC_HPDx_INT_POLARITY; + WREG32(DC_HPD6_INT_CONTROL, tmp); + tmp = RREG32(AFMT_AUDIO_PACKET_CONTROL + DCE3_HDMI_OFFSET0) & ~HDMI0_AZ_FORMAT_WTRIG_MASK; + WREG32(AFMT_AUDIO_PACKET_CONTROL + DCE3_HDMI_OFFSET0, tmp); + tmp = RREG32(AFMT_AUDIO_PACKET_CONTROL + DCE3_HDMI_OFFSET1) & ~HDMI0_AZ_FORMAT_WTRIG_MASK; + WREG32(AFMT_AUDIO_PACKET_CONTROL + DCE3_HDMI_OFFSET1, tmp); + } else { + tmp = RREG32(HDMI0_AUDIO_PACKET_CONTROL) & ~HDMI0_AZ_FORMAT_WTRIG_MASK; + WREG32(HDMI0_AUDIO_PACKET_CONTROL, tmp); + tmp = RREG32(DCE3_HDMI1_AUDIO_PACKET_CONTROL) & ~HDMI0_AZ_FORMAT_WTRIG_MASK; + WREG32(DCE3_HDMI1_AUDIO_PACKET_CONTROL, tmp); + } + } else { + WREG32(DACA_AUTODETECT_INT_CONTROL, 0); + WREG32(DACB_AUTODETECT_INT_CONTROL, 0); + tmp = RREG32(DC_HOT_PLUG_DETECT1_INT_CONTROL) & DC_HOT_PLUG_DETECTx_INT_POLARITY; + WREG32(DC_HOT_PLUG_DETECT1_INT_CONTROL, tmp); + tmp = RREG32(DC_HOT_PLUG_DETECT2_INT_CONTROL) & DC_HOT_PLUG_DETECTx_INT_POLARITY; + WREG32(DC_HOT_PLUG_DETECT2_INT_CONTROL, tmp); + tmp = RREG32(DC_HOT_PLUG_DETECT3_INT_CONTROL) & DC_HOT_PLUG_DETECTx_INT_POLARITY; + WREG32(DC_HOT_PLUG_DETECT3_INT_CONTROL, tmp); + tmp = RREG32(HDMI0_AUDIO_PACKET_CONTROL) & ~HDMI0_AZ_FORMAT_WTRIG_MASK; + WREG32(HDMI0_AUDIO_PACKET_CONTROL, tmp); + tmp = RREG32(HDMI1_AUDIO_PACKET_CONTROL) & ~HDMI0_AZ_FORMAT_WTRIG_MASK; + WREG32(HDMI1_AUDIO_PACKET_CONTROL, tmp); + } +} + +int r600_irq_init(struct radeon_device *rdev) +{ + int ret = 0; + int rb_bufsz; + u32 interrupt_cntl, ih_cntl, ih_rb_cntl; + + /* allocate ring */ + ret = r600_ih_ring_alloc(rdev); + if (ret) + return ret; + + /* disable irqs */ + r600_disable_interrupts(rdev); + + /* init rlc */ + ret = r600_rlc_init(rdev); + if (ret) { + r600_ih_ring_fini(rdev); + return ret; + } + + /* setup interrupt control */ + /* set dummy read address to ring address */ + WREG32(INTERRUPT_CNTL2, rdev->ih.gpu_addr >> 8); + interrupt_cntl = RREG32(INTERRUPT_CNTL); + /* IH_DUMMY_RD_OVERRIDE=0 - dummy read disabled with msi, enabled without msi + * IH_DUMMY_RD_OVERRIDE=1 - dummy read controlled by IH_DUMMY_RD_EN + */ + interrupt_cntl &= ~IH_DUMMY_RD_OVERRIDE; + /* IH_REQ_NONSNOOP_EN=1 if ring is in non-cacheable memory, e.g., vram */ + interrupt_cntl &= ~IH_REQ_NONSNOOP_EN; + WREG32(INTERRUPT_CNTL, interrupt_cntl); + + WREG32(IH_RB_BASE, rdev->ih.gpu_addr >> 8); + rb_bufsz = drm_order(rdev->ih.ring_size / 4); + + ih_rb_cntl = (IH_WPTR_OVERFLOW_ENABLE | + IH_WPTR_OVERFLOW_CLEAR | + (rb_bufsz << 1)); + + if (rdev->wb.enabled) + ih_rb_cntl |= IH_WPTR_WRITEBACK_ENABLE; + + /* set the writeback address whether it's enabled or not */ + WREG32(IH_RB_WPTR_ADDR_LO, (rdev->wb.gpu_addr + R600_WB_IH_WPTR_OFFSET) & 0xFFFFFFFC); + WREG32(IH_RB_WPTR_ADDR_HI, upper_32_bits(rdev->wb.gpu_addr + R600_WB_IH_WPTR_OFFSET) & 0xFF); + + WREG32(IH_RB_CNTL, ih_rb_cntl); + + /* set rptr, wptr to 0 */ + WREG32(IH_RB_RPTR, 0); + WREG32(IH_RB_WPTR, 0); + + /* Default settings for IH_CNTL (disabled at first) */ + ih_cntl = MC_WRREQ_CREDIT(0x10) | MC_WR_CLEAN_CNT(0x10); + /* RPTR_REARM only works if msi's are enabled */ + if (rdev->msi_enabled) + ih_cntl |= RPTR_REARM; + WREG32(IH_CNTL, ih_cntl); + + /* force the active interrupt state to all disabled */ + if (rdev->family >= CHIP_CEDAR) + evergreen_disable_interrupt_state(rdev); + else + r600_disable_interrupt_state(rdev); + + /* at this point everything should be setup correctly to enable master */ + pci_enable_busmaster(rdev->dev); + + /* enable irqs */ + r600_enable_interrupts(rdev); + + return ret; +} + +void r600_irq_suspend(struct radeon_device *rdev) +{ + r600_irq_disable(rdev); + r600_rlc_stop(rdev); +} + +void r600_irq_fini(struct radeon_device *rdev) +{ + r600_irq_suspend(rdev); + r600_ih_ring_fini(rdev); +} + +int r600_irq_set(struct radeon_device *rdev) +{ + u32 cp_int_cntl = CNTX_BUSY_INT_ENABLE | CNTX_EMPTY_INT_ENABLE; + u32 mode_int = 0; + u32 hpd1, hpd2, hpd3, hpd4 = 0, hpd5 = 0, hpd6 = 0; + u32 grbm_int_cntl = 0; + u32 hdmi0, hdmi1; + u32 d1grph = 0, d2grph = 0; + u32 dma_cntl; + + if (!rdev->irq.installed) { + DRM_ERROR("Can't enable IRQ/MSI because no handler is installed\n"); + return -EINVAL; + } + /* don't enable anything if the ih is disabled */ + if (!rdev->ih.enabled) { + r600_disable_interrupts(rdev); + /* force the active interrupt state to all disabled */ + r600_disable_interrupt_state(rdev); + return 0; + } + + if (ASIC_IS_DCE3(rdev)) { + hpd1 = RREG32(DC_HPD1_INT_CONTROL) & ~DC_HPDx_INT_EN; + hpd2 = RREG32(DC_HPD2_INT_CONTROL) & ~DC_HPDx_INT_EN; + hpd3 = RREG32(DC_HPD3_INT_CONTROL) & ~DC_HPDx_INT_EN; + hpd4 = RREG32(DC_HPD4_INT_CONTROL) & ~DC_HPDx_INT_EN; + if (ASIC_IS_DCE32(rdev)) { + hpd5 = RREG32(DC_HPD5_INT_CONTROL) & ~DC_HPDx_INT_EN; + hpd6 = RREG32(DC_HPD6_INT_CONTROL) & ~DC_HPDx_INT_EN; + hdmi0 = RREG32(AFMT_AUDIO_PACKET_CONTROL + DCE3_HDMI_OFFSET0) & ~AFMT_AZ_FORMAT_WTRIG_MASK; + hdmi1 = RREG32(AFMT_AUDIO_PACKET_CONTROL + DCE3_HDMI_OFFSET1) & ~AFMT_AZ_FORMAT_WTRIG_MASK; + } else { + hdmi0 = RREG32(HDMI0_AUDIO_PACKET_CONTROL) & ~HDMI0_AZ_FORMAT_WTRIG_MASK; + hdmi1 = RREG32(DCE3_HDMI1_AUDIO_PACKET_CONTROL) & ~HDMI0_AZ_FORMAT_WTRIG_MASK; + } + } else { + hpd1 = RREG32(DC_HOT_PLUG_DETECT1_INT_CONTROL) & ~DC_HPDx_INT_EN; + hpd2 = RREG32(DC_HOT_PLUG_DETECT2_INT_CONTROL) & ~DC_HPDx_INT_EN; + hpd3 = RREG32(DC_HOT_PLUG_DETECT3_INT_CONTROL) & ~DC_HPDx_INT_EN; + hdmi0 = RREG32(HDMI0_AUDIO_PACKET_CONTROL) & ~HDMI0_AZ_FORMAT_WTRIG_MASK; + hdmi1 = RREG32(HDMI1_AUDIO_PACKET_CONTROL) & ~HDMI0_AZ_FORMAT_WTRIG_MASK; + } + dma_cntl = RREG32(DMA_CNTL) & ~TRAP_ENABLE; + + if (atomic_read(&rdev->irq.ring_int[RADEON_RING_TYPE_GFX_INDEX])) { + DRM_DEBUG("r600_irq_set: sw int\n"); + cp_int_cntl |= RB_INT_ENABLE; + cp_int_cntl |= TIME_STAMP_INT_ENABLE; + } + + if (atomic_read(&rdev->irq.ring_int[R600_RING_TYPE_DMA_INDEX])) { + DRM_DEBUG("r600_irq_set: sw int dma\n"); + dma_cntl |= TRAP_ENABLE; + } + + if (rdev->irq.crtc_vblank_int[0] || + atomic_read(&rdev->irq.pflip[0])) { + DRM_DEBUG("r600_irq_set: vblank 0\n"); + mode_int |= D1MODE_VBLANK_INT_MASK; + } + if (rdev->irq.crtc_vblank_int[1] || + atomic_read(&rdev->irq.pflip[1])) { + DRM_DEBUG("r600_irq_set: vblank 1\n"); + mode_int |= D2MODE_VBLANK_INT_MASK; + } + if (rdev->irq.hpd[0]) { + DRM_DEBUG("r600_irq_set: hpd 1\n"); + hpd1 |= DC_HPDx_INT_EN; + } + if (rdev->irq.hpd[1]) { + DRM_DEBUG("r600_irq_set: hpd 2\n"); + hpd2 |= DC_HPDx_INT_EN; + } + if (rdev->irq.hpd[2]) { + DRM_DEBUG("r600_irq_set: hpd 3\n"); + hpd3 |= DC_HPDx_INT_EN; + } + if (rdev->irq.hpd[3]) { + DRM_DEBUG("r600_irq_set: hpd 4\n"); + hpd4 |= DC_HPDx_INT_EN; + } + if (rdev->irq.hpd[4]) { + DRM_DEBUG("r600_irq_set: hpd 5\n"); + hpd5 |= DC_HPDx_INT_EN; + } + if (rdev->irq.hpd[5]) { + DRM_DEBUG("r600_irq_set: hpd 6\n"); + hpd6 |= DC_HPDx_INT_EN; + } + if (rdev->irq.afmt[0]) { + DRM_DEBUG("r600_irq_set: hdmi 0\n"); + hdmi0 |= HDMI0_AZ_FORMAT_WTRIG_MASK; + } + if (rdev->irq.afmt[1]) { + DRM_DEBUG("r600_irq_set: hdmi 0\n"); + hdmi1 |= HDMI0_AZ_FORMAT_WTRIG_MASK; + } + + WREG32(CP_INT_CNTL, cp_int_cntl); + WREG32(DMA_CNTL, dma_cntl); + WREG32(DxMODE_INT_MASK, mode_int); + WREG32(D1GRPH_INTERRUPT_CONTROL, d1grph); + WREG32(D2GRPH_INTERRUPT_CONTROL, d2grph); + WREG32(GRBM_INT_CNTL, grbm_int_cntl); + if (ASIC_IS_DCE3(rdev)) { + WREG32(DC_HPD1_INT_CONTROL, hpd1); + WREG32(DC_HPD2_INT_CONTROL, hpd2); + WREG32(DC_HPD3_INT_CONTROL, hpd3); + WREG32(DC_HPD4_INT_CONTROL, hpd4); + if (ASIC_IS_DCE32(rdev)) { + WREG32(DC_HPD5_INT_CONTROL, hpd5); + WREG32(DC_HPD6_INT_CONTROL, hpd6); + WREG32(AFMT_AUDIO_PACKET_CONTROL + DCE3_HDMI_OFFSET0, hdmi0); + WREG32(AFMT_AUDIO_PACKET_CONTROL + DCE3_HDMI_OFFSET1, hdmi1); + } else { + WREG32(HDMI0_AUDIO_PACKET_CONTROL, hdmi0); + WREG32(DCE3_HDMI1_AUDIO_PACKET_CONTROL, hdmi1); + } + } else { + WREG32(DC_HOT_PLUG_DETECT1_INT_CONTROL, hpd1); + WREG32(DC_HOT_PLUG_DETECT2_INT_CONTROL, hpd2); + WREG32(DC_HOT_PLUG_DETECT3_INT_CONTROL, hpd3); + WREG32(HDMI0_AUDIO_PACKET_CONTROL, hdmi0); + WREG32(HDMI1_AUDIO_PACKET_CONTROL, hdmi1); + } + + return 0; +} + +static void r600_irq_ack(struct radeon_device *rdev) +{ + u32 tmp; + + if (ASIC_IS_DCE3(rdev)) { + rdev->irq.stat_regs.r600.disp_int = RREG32(DCE3_DISP_INTERRUPT_STATUS); + rdev->irq.stat_regs.r600.disp_int_cont = RREG32(DCE3_DISP_INTERRUPT_STATUS_CONTINUE); + rdev->irq.stat_regs.r600.disp_int_cont2 = RREG32(DCE3_DISP_INTERRUPT_STATUS_CONTINUE2); + if (ASIC_IS_DCE32(rdev)) { + rdev->irq.stat_regs.r600.hdmi0_status = RREG32(AFMT_STATUS + DCE3_HDMI_OFFSET0); + rdev->irq.stat_regs.r600.hdmi1_status = RREG32(AFMT_STATUS + DCE3_HDMI_OFFSET1); + } else { + rdev->irq.stat_regs.r600.hdmi0_status = RREG32(HDMI0_STATUS); + rdev->irq.stat_regs.r600.hdmi1_status = RREG32(DCE3_HDMI1_STATUS); + } + } else { + rdev->irq.stat_regs.r600.disp_int = RREG32(DISP_INTERRUPT_STATUS); + rdev->irq.stat_regs.r600.disp_int_cont = RREG32(DISP_INTERRUPT_STATUS_CONTINUE); + rdev->irq.stat_regs.r600.disp_int_cont2 = 0; + rdev->irq.stat_regs.r600.hdmi0_status = RREG32(HDMI0_STATUS); + rdev->irq.stat_regs.r600.hdmi1_status = RREG32(HDMI1_STATUS); + } + rdev->irq.stat_regs.r600.d1grph_int = RREG32(D1GRPH_INTERRUPT_STATUS); + rdev->irq.stat_regs.r600.d2grph_int = RREG32(D2GRPH_INTERRUPT_STATUS); + + if (rdev->irq.stat_regs.r600.d1grph_int & DxGRPH_PFLIP_INT_OCCURRED) + WREG32(D1GRPH_INTERRUPT_STATUS, DxGRPH_PFLIP_INT_CLEAR); + if (rdev->irq.stat_regs.r600.d2grph_int & DxGRPH_PFLIP_INT_OCCURRED) + WREG32(D2GRPH_INTERRUPT_STATUS, DxGRPH_PFLIP_INT_CLEAR); + if (rdev->irq.stat_regs.r600.disp_int & LB_D1_VBLANK_INTERRUPT) + WREG32(D1MODE_VBLANK_STATUS, DxMODE_VBLANK_ACK); + if (rdev->irq.stat_regs.r600.disp_int & LB_D1_VLINE_INTERRUPT) + WREG32(D1MODE_VLINE_STATUS, DxMODE_VLINE_ACK); + if (rdev->irq.stat_regs.r600.disp_int & LB_D2_VBLANK_INTERRUPT) + WREG32(D2MODE_VBLANK_STATUS, DxMODE_VBLANK_ACK); + if (rdev->irq.stat_regs.r600.disp_int & LB_D2_VLINE_INTERRUPT) + WREG32(D2MODE_VLINE_STATUS, DxMODE_VLINE_ACK); + if (rdev->irq.stat_regs.r600.disp_int & DC_HPD1_INTERRUPT) { + if (ASIC_IS_DCE3(rdev)) { + tmp = RREG32(DC_HPD1_INT_CONTROL); + tmp |= DC_HPDx_INT_ACK; + WREG32(DC_HPD1_INT_CONTROL, tmp); + } else { + tmp = RREG32(DC_HOT_PLUG_DETECT1_INT_CONTROL); + tmp |= DC_HPDx_INT_ACK; + WREG32(DC_HOT_PLUG_DETECT1_INT_CONTROL, tmp); + } + } + if (rdev->irq.stat_regs.r600.disp_int & DC_HPD2_INTERRUPT) { + if (ASIC_IS_DCE3(rdev)) { + tmp = RREG32(DC_HPD2_INT_CONTROL); + tmp |= DC_HPDx_INT_ACK; + WREG32(DC_HPD2_INT_CONTROL, tmp); + } else { + tmp = RREG32(DC_HOT_PLUG_DETECT2_INT_CONTROL); + tmp |= DC_HPDx_INT_ACK; + WREG32(DC_HOT_PLUG_DETECT2_INT_CONTROL, tmp); + } + } + if (rdev->irq.stat_regs.r600.disp_int_cont & DC_HPD3_INTERRUPT) { + if (ASIC_IS_DCE3(rdev)) { + tmp = RREG32(DC_HPD3_INT_CONTROL); + tmp |= DC_HPDx_INT_ACK; + WREG32(DC_HPD3_INT_CONTROL, tmp); + } else { + tmp = RREG32(DC_HOT_PLUG_DETECT3_INT_CONTROL); + tmp |= DC_HPDx_INT_ACK; + WREG32(DC_HOT_PLUG_DETECT3_INT_CONTROL, tmp); + } + } + if (rdev->irq.stat_regs.r600.disp_int_cont & DC_HPD4_INTERRUPT) { + tmp = RREG32(DC_HPD4_INT_CONTROL); + tmp |= DC_HPDx_INT_ACK; + WREG32(DC_HPD4_INT_CONTROL, tmp); + } + if (ASIC_IS_DCE32(rdev)) { + if (rdev->irq.stat_regs.r600.disp_int_cont2 & DC_HPD5_INTERRUPT) { + tmp = RREG32(DC_HPD5_INT_CONTROL); + tmp |= DC_HPDx_INT_ACK; + WREG32(DC_HPD5_INT_CONTROL, tmp); + } + if (rdev->irq.stat_regs.r600.disp_int_cont2 & DC_HPD6_INTERRUPT) { + tmp = RREG32(DC_HPD5_INT_CONTROL); + tmp |= DC_HPDx_INT_ACK; + WREG32(DC_HPD6_INT_CONTROL, tmp); + } + if (rdev->irq.stat_regs.r600.hdmi0_status & AFMT_AZ_FORMAT_WTRIG) { + tmp = RREG32(AFMT_AUDIO_PACKET_CONTROL + DCE3_HDMI_OFFSET0); + tmp |= AFMT_AZ_FORMAT_WTRIG_ACK; + WREG32(AFMT_AUDIO_PACKET_CONTROL + DCE3_HDMI_OFFSET0, tmp); + } + if (rdev->irq.stat_regs.r600.hdmi1_status & AFMT_AZ_FORMAT_WTRIG) { + tmp = RREG32(AFMT_AUDIO_PACKET_CONTROL + DCE3_HDMI_OFFSET1); + tmp |= AFMT_AZ_FORMAT_WTRIG_ACK; + WREG32(AFMT_AUDIO_PACKET_CONTROL + DCE3_HDMI_OFFSET1, tmp); + } + } else { + if (rdev->irq.stat_regs.r600.hdmi0_status & HDMI0_AZ_FORMAT_WTRIG) { + tmp = RREG32(HDMI0_AUDIO_PACKET_CONTROL); + tmp |= HDMI0_AZ_FORMAT_WTRIG_ACK; + WREG32(HDMI0_AUDIO_PACKET_CONTROL, tmp); + } + if (rdev->irq.stat_regs.r600.hdmi1_status & HDMI0_AZ_FORMAT_WTRIG) { + if (ASIC_IS_DCE3(rdev)) { + tmp = RREG32(DCE3_HDMI1_AUDIO_PACKET_CONTROL); + tmp |= HDMI0_AZ_FORMAT_WTRIG_ACK; + WREG32(DCE3_HDMI1_AUDIO_PACKET_CONTROL, tmp); + } else { + tmp = RREG32(HDMI1_AUDIO_PACKET_CONTROL); + tmp |= HDMI0_AZ_FORMAT_WTRIG_ACK; + WREG32(HDMI1_AUDIO_PACKET_CONTROL, tmp); + } + } + } +} + +void r600_irq_disable(struct radeon_device *rdev) +{ + r600_disable_interrupts(rdev); + /* Wait and acknowledge irq */ + DRM_MDELAY(1); + r600_irq_ack(rdev); + r600_disable_interrupt_state(rdev); +} + +static u32 r600_get_ih_wptr(struct radeon_device *rdev) +{ + u32 wptr, tmp; + + if (rdev->wb.enabled) + wptr = le32_to_cpu(rdev->wb.wb[R600_WB_IH_WPTR_OFFSET/4]); + else + wptr = RREG32(IH_RB_WPTR); + + if (wptr & RB_OVERFLOW) { + /* When a ring buffer overflow happen start parsing interrupt + * from the last not overwritten vector (wptr + 16). Hopefully + * this should allow us to catchup. + */ + dev_warn(rdev->dev, "IH ring buffer overflow (0x%08X, %d, %d)\n", + wptr, rdev->ih.rptr, (wptr + 16) + rdev->ih.ptr_mask); + rdev->ih.rptr = (wptr + 16) & rdev->ih.ptr_mask; + tmp = RREG32(IH_RB_CNTL); + tmp |= IH_WPTR_OVERFLOW_CLEAR; + WREG32(IH_RB_CNTL, tmp); + } + return (wptr & rdev->ih.ptr_mask); +} + +/* r600 IV Ring + * Each IV ring entry is 128 bits: + * [7:0] - interrupt source id + * [31:8] - reserved + * [59:32] - interrupt source data + * [127:60] - reserved + * + * The basic interrupt vector entries + * are decoded as follows: + * src_id src_data description + * 1 0 D1 Vblank + * 1 1 D1 Vline + * 5 0 D2 Vblank + * 5 1 D2 Vline + * 19 0 FP Hot plug detection A + * 19 1 FP Hot plug detection B + * 19 2 DAC A auto-detection + * 19 3 DAC B auto-detection + * 21 4 HDMI block A + * 21 5 HDMI block B + * 176 - CP_INT RB + * 177 - CP_INT IB1 + * 178 - CP_INT IB2 + * 181 - EOP Interrupt + * 233 - GUI Idle + * + * Note, these are based on r600 and may need to be + * adjusted or added to on newer asics + */ + +irqreturn_t r600_irq_process(struct radeon_device *rdev) +{ + u32 wptr; + u32 rptr; + u32 src_id, src_data; + u32 ring_index; + bool queue_hotplug = false; + bool queue_hdmi = false; + + if (!rdev->ih.enabled || rdev->shutdown) + return IRQ_NONE; + + /* No MSIs, need a dummy read to flush PCI DMAs */ + if (!rdev->msi_enabled) + RREG32(IH_RB_WPTR); + + wptr = r600_get_ih_wptr(rdev); + +restart_ih: + /* is somebody else already processing irqs? */ + if (atomic_xchg(&rdev->ih.lock, 1)) + return IRQ_NONE; + + rptr = rdev->ih.rptr; + DRM_DEBUG("r600_irq_process start: rptr %d, wptr %d\n", rptr, wptr); + + /* Order reading of wptr vs. reading of IH ring data */ + rmb(); + + /* display interrupts */ + r600_irq_ack(rdev); + + while (rptr != wptr) { + /* wptr/rptr are in bytes! */ + ring_index = rptr / 4; + src_id = le32_to_cpu(rdev->ih.ring[ring_index]) & 0xff; + src_data = le32_to_cpu(rdev->ih.ring[ring_index + 1]) & 0xfffffff; + + switch (src_id) { + case 1: /* D1 vblank/vline */ + switch (src_data) { + case 0: /* D1 vblank */ + if (rdev->irq.stat_regs.r600.disp_int & LB_D1_VBLANK_INTERRUPT) { + if (rdev->irq.crtc_vblank_int[0]) { + drm_handle_vblank(rdev->ddev, 0); + rdev->pm.vblank_sync = true; + DRM_WAKEUP(&rdev->irq.vblank_queue); + } + if (atomic_read(&rdev->irq.pflip[0])) + radeon_crtc_handle_flip(rdev, 0); + rdev->irq.stat_regs.r600.disp_int &= ~LB_D1_VBLANK_INTERRUPT; + DRM_DEBUG("IH: D1 vblank\n"); + } + break; + case 1: /* D1 vline */ + if (rdev->irq.stat_regs.r600.disp_int & LB_D1_VLINE_INTERRUPT) { + rdev->irq.stat_regs.r600.disp_int &= ~LB_D1_VLINE_INTERRUPT; + DRM_DEBUG("IH: D1 vline\n"); + } + break; + default: + DRM_DEBUG("Unhandled interrupt: %d %d\n", src_id, src_data); + break; + } + break; + case 5: /* D2 vblank/vline */ + switch (src_data) { + case 0: /* D2 vblank */ + if (rdev->irq.stat_regs.r600.disp_int & LB_D2_VBLANK_INTERRUPT) { + if (rdev->irq.crtc_vblank_int[1]) { + drm_handle_vblank(rdev->ddev, 1); + rdev->pm.vblank_sync = true; + DRM_WAKEUP(&rdev->irq.vblank_queue); + } + if (atomic_read(&rdev->irq.pflip[1])) + radeon_crtc_handle_flip(rdev, 1); + rdev->irq.stat_regs.r600.disp_int &= ~LB_D2_VBLANK_INTERRUPT; + DRM_DEBUG("IH: D2 vblank\n"); + } + break; + case 1: /* D1 vline */ + if (rdev->irq.stat_regs.r600.disp_int & LB_D2_VLINE_INTERRUPT) { + rdev->irq.stat_regs.r600.disp_int &= ~LB_D2_VLINE_INTERRUPT; + DRM_DEBUG("IH: D2 vline\n"); + } + break; + default: + DRM_DEBUG("Unhandled interrupt: %d %d\n", src_id, src_data); + break; + } + break; + case 19: /* HPD/DAC hotplug */ + switch (src_data) { + case 0: + if (rdev->irq.stat_regs.r600.disp_int & DC_HPD1_INTERRUPT) { + rdev->irq.stat_regs.r600.disp_int &= ~DC_HPD1_INTERRUPT; + queue_hotplug = true; + DRM_DEBUG("IH: HPD1\n"); + } + break; + case 1: + if (rdev->irq.stat_regs.r600.disp_int & DC_HPD2_INTERRUPT) { + rdev->irq.stat_regs.r600.disp_int &= ~DC_HPD2_INTERRUPT; + queue_hotplug = true; + DRM_DEBUG("IH: HPD2\n"); + } + break; + case 4: + if (rdev->irq.stat_regs.r600.disp_int_cont & DC_HPD3_INTERRUPT) { + rdev->irq.stat_regs.r600.disp_int_cont &= ~DC_HPD3_INTERRUPT; + queue_hotplug = true; + DRM_DEBUG("IH: HPD3\n"); + } + break; + case 5: + if (rdev->irq.stat_regs.r600.disp_int_cont & DC_HPD4_INTERRUPT) { + rdev->irq.stat_regs.r600.disp_int_cont &= ~DC_HPD4_INTERRUPT; + queue_hotplug = true; + DRM_DEBUG("IH: HPD4\n"); + } + break; + case 10: + if (rdev->irq.stat_regs.r600.disp_int_cont2 & DC_HPD5_INTERRUPT) { + rdev->irq.stat_regs.r600.disp_int_cont2 &= ~DC_HPD5_INTERRUPT; + queue_hotplug = true; + DRM_DEBUG("IH: HPD5\n"); + } + break; + case 12: + if (rdev->irq.stat_regs.r600.disp_int_cont2 & DC_HPD6_INTERRUPT) { + rdev->irq.stat_regs.r600.disp_int_cont2 &= ~DC_HPD6_INTERRUPT; + queue_hotplug = true; + DRM_DEBUG("IH: HPD6\n"); + } + break; + default: + DRM_DEBUG("Unhandled interrupt: %d %d\n", src_id, src_data); + break; + } + break; + case 21: /* hdmi */ + switch (src_data) { + case 4: + if (rdev->irq.stat_regs.r600.hdmi0_status & HDMI0_AZ_FORMAT_WTRIG) { + rdev->irq.stat_regs.r600.hdmi0_status &= ~HDMI0_AZ_FORMAT_WTRIG; + queue_hdmi = true; + DRM_DEBUG("IH: HDMI0\n"); + } + break; + case 5: + if (rdev->irq.stat_regs.r600.hdmi1_status & HDMI0_AZ_FORMAT_WTRIG) { + rdev->irq.stat_regs.r600.hdmi1_status &= ~HDMI0_AZ_FORMAT_WTRIG; + queue_hdmi = true; + DRM_DEBUG("IH: HDMI1\n"); + } + break; + default: + DRM_ERROR("Unhandled interrupt: %d %d\n", src_id, src_data); + break; + } + break; + case 176: /* CP_INT in ring buffer */ + case 177: /* CP_INT in IB1 */ + case 178: /* CP_INT in IB2 */ + DRM_DEBUG("IH: CP int: 0x%08x\n", src_data); + radeon_fence_process(rdev, RADEON_RING_TYPE_GFX_INDEX); + break; + case 181: /* CP EOP event */ + DRM_DEBUG("IH: CP EOP\n"); + radeon_fence_process(rdev, RADEON_RING_TYPE_GFX_INDEX); + break; + case 224: /* DMA trap event */ + DRM_DEBUG("IH: DMA trap\n"); + radeon_fence_process(rdev, R600_RING_TYPE_DMA_INDEX); + break; + case 233: /* GUI IDLE */ + DRM_DEBUG("IH: GUI idle\n"); + break; + default: + DRM_DEBUG("Unhandled interrupt: %d %d\n", src_id, src_data); + break; + } + + /* wptr/rptr are in bytes! */ + rptr += 16; + rptr &= rdev->ih.ptr_mask; + } + if (queue_hotplug) + taskqueue_enqueue(rdev->tq, &rdev->hotplug_work); + if (queue_hdmi) + taskqueue_enqueue(rdev->tq, &rdev->audio_work); + rdev->ih.rptr = rptr; + WREG32(IH_RB_RPTR, rdev->ih.rptr); + atomic_set(&rdev->ih.lock, 0); + + /* make sure wptr hasn't changed while processing */ + wptr = r600_get_ih_wptr(rdev); + if (wptr != rptr) + goto restart_ih; + + return IRQ_HANDLED; +} + +/* + * Debugfs info + */ +#if defined(CONFIG_DEBUG_FS) + +static int r600_debugfs_mc_info(struct seq_file *m, void *data) +{ + struct drm_info_node *node = (struct drm_info_node *) m->private; + struct drm_device *dev = node->minor->dev; + struct radeon_device *rdev = dev->dev_private; + + DREG32_SYS(m, rdev, R_000E50_SRBM_STATUS); + DREG32_SYS(m, rdev, VM_L2_STATUS); + return 0; +} + +static struct drm_info_list r600_mc_info_list[] = { + {"r600_mc_info", r600_debugfs_mc_info, 0, NULL}, +}; +#endif + +int r600_debugfs_mc_info_init(struct radeon_device *rdev) +{ +#if defined(CONFIG_DEBUG_FS) + return radeon_debugfs_add_files(rdev, r600_mc_info_list, ARRAY_SIZE(r600_mc_info_list)); +#else + return 0; +#endif +} + +/** + * r600_ioctl_wait_idle - flush host path cache on wait idle ioctl + * rdev: radeon device structure + * bo: buffer object struct which userspace is waiting for idle + * + * Some R6XX/R7XX doesn't seems to take into account HDP flush performed + * through ring buffer, this leads to corruption in rendering, see + * http://bugzilla.kernel.org/show_bug.cgi?id=15186 to avoid this we + * directly perform HDP flush by writing register through MMIO. + */ +void r600_ioctl_wait_idle(struct radeon_device *rdev, struct radeon_bo *bo) +{ + /* r7xx hw bug. write to HDP_DEBUG1 followed by fb read + * rather than write to HDP_REG_COHERENCY_FLUSH_CNTL. + * This seems to cause problems on some AGP cards. Just use the old + * method for them. + */ + if ((rdev->family >= CHIP_RV770) && (rdev->family <= CHIP_RV740) && + rdev->vram_scratch.ptr && !(rdev->flags & RADEON_IS_AGP)) { + volatile uint32_t *ptr = rdev->vram_scratch.ptr; + u32 tmp; + + WREG32(HDP_DEBUG1, 0); + tmp = *ptr; + } else + WREG32(R_005480_HDP_MEM_COHERENCY_FLUSH_CNTL, 0x1); +} + +void r600_set_pcie_lanes(struct radeon_device *rdev, int lanes) +{ + u32 link_width_cntl, mask, target_reg; + + if (rdev->flags & RADEON_IS_IGP) + return; + + if (!(rdev->flags & RADEON_IS_PCIE)) + return; + + /* x2 cards have a special sequence */ + if (ASIC_IS_X2(rdev)) + return; + + /* FIXME wait for idle */ + + switch (lanes) { + case 0: + mask = RADEON_PCIE_LC_LINK_WIDTH_X0; + break; + case 1: + mask = RADEON_PCIE_LC_LINK_WIDTH_X1; + break; + case 2: + mask = RADEON_PCIE_LC_LINK_WIDTH_X2; + break; + case 4: + mask = RADEON_PCIE_LC_LINK_WIDTH_X4; + break; + case 8: + mask = RADEON_PCIE_LC_LINK_WIDTH_X8; + break; + case 12: + mask = RADEON_PCIE_LC_LINK_WIDTH_X12; + break; + case 16: + default: + mask = RADEON_PCIE_LC_LINK_WIDTH_X16; + break; + } + + link_width_cntl = RREG32_PCIE_P(RADEON_PCIE_LC_LINK_WIDTH_CNTL); + + if ((link_width_cntl & RADEON_PCIE_LC_LINK_WIDTH_RD_MASK) == + (mask << RADEON_PCIE_LC_LINK_WIDTH_RD_SHIFT)) + return; + + if (link_width_cntl & R600_PCIE_LC_UPCONFIGURE_DIS) + return; + + link_width_cntl &= ~(RADEON_PCIE_LC_LINK_WIDTH_MASK | + RADEON_PCIE_LC_RECONFIG_NOW | + R600_PCIE_LC_RENEGOTIATE_EN | + R600_PCIE_LC_RECONFIG_ARC_MISSING_ESCAPE); + link_width_cntl |= mask; + + WREG32_PCIE_P(RADEON_PCIE_LC_LINK_WIDTH_CNTL, link_width_cntl); + + /* some northbridges can renegotiate the link rather than requiring + * a complete re-config. + * e.g., AMD 780/790 northbridges (pci ids: 0x5956, 0x5957, 0x5958, etc.) + */ + if (link_width_cntl & R600_PCIE_LC_RENEGOTIATION_SUPPORT) + link_width_cntl |= R600_PCIE_LC_RENEGOTIATE_EN | R600_PCIE_LC_UPCONFIGURE_SUPPORT; + else + link_width_cntl |= R600_PCIE_LC_RECONFIG_ARC_MISSING_ESCAPE; + + WREG32_PCIE_P(RADEON_PCIE_LC_LINK_WIDTH_CNTL, (link_width_cntl | + RADEON_PCIE_LC_RECONFIG_NOW)); + + if (rdev->family >= CHIP_RV770) + target_reg = R700_TARGET_AND_CURRENT_PROFILE_INDEX; + else + target_reg = R600_TARGET_AND_CURRENT_PROFILE_INDEX; + + /* wait for lane set to complete */ + link_width_cntl = RREG32(target_reg); + while (link_width_cntl == 0xffffffff) + link_width_cntl = RREG32(target_reg); + +} + +int r600_get_pcie_lanes(struct radeon_device *rdev) +{ + u32 link_width_cntl; + + if (rdev->flags & RADEON_IS_IGP) + return 0; + + if (!(rdev->flags & RADEON_IS_PCIE)) + return 0; + + /* x2 cards have a special sequence */ + if (ASIC_IS_X2(rdev)) + return 0; + + /* FIXME wait for idle */ + + link_width_cntl = RREG32_PCIE_P(RADEON_PCIE_LC_LINK_WIDTH_CNTL); + + switch ((link_width_cntl & RADEON_PCIE_LC_LINK_WIDTH_RD_MASK) >> RADEON_PCIE_LC_LINK_WIDTH_RD_SHIFT) { + case RADEON_PCIE_LC_LINK_WIDTH_X0: + return 0; + case RADEON_PCIE_LC_LINK_WIDTH_X1: + return 1; + case RADEON_PCIE_LC_LINK_WIDTH_X2: + return 2; + case RADEON_PCIE_LC_LINK_WIDTH_X4: + return 4; + case RADEON_PCIE_LC_LINK_WIDTH_X8: + return 8; + case RADEON_PCIE_LC_LINK_WIDTH_X16: + default: + return 16; + } +} + +static void r600_pcie_gen2_enable(struct radeon_device *rdev) +{ + u32 link_width_cntl, lanes, speed_cntl, training_cntl, tmp; + u16 link_cntl2; + u32 mask; + int ret; + + if (radeon_pcie_gen2 == 0) + return; + + if (rdev->flags & RADEON_IS_IGP) + return; + + if (!(rdev->flags & RADEON_IS_PCIE)) + return; + + /* x2 cards have a special sequence */ + if (ASIC_IS_X2(rdev)) + return; + + /* only RV6xx+ chips are supported */ + if (rdev->family <= CHIP_R600) + return; + + ret = drm_pcie_get_speed_cap_mask(rdev->ddev, &mask); + if (ret != 0) + return; + + if (!(mask & DRM_PCIE_SPEED_50)) + return; + + speed_cntl = RREG32_PCIE_P(PCIE_LC_SPEED_CNTL); + if (speed_cntl & LC_CURRENT_DATA_RATE) { + DRM_INFO("PCIE gen 2 link speeds already enabled\n"); + return; + } + + DRM_INFO("enabling PCIE gen 2 link speeds, disable with radeon.pcie_gen2=0\n"); + + /* 55 nm r6xx asics */ + if ((rdev->family == CHIP_RV670) || + (rdev->family == CHIP_RV620) || + (rdev->family == CHIP_RV635)) { + /* advertise upconfig capability */ + link_width_cntl = RREG32_PCIE_P(PCIE_LC_LINK_WIDTH_CNTL); + link_width_cntl &= ~LC_UPCONFIGURE_DIS; + WREG32_PCIE_P(PCIE_LC_LINK_WIDTH_CNTL, link_width_cntl); + link_width_cntl = RREG32_PCIE_P(PCIE_LC_LINK_WIDTH_CNTL); + if (link_width_cntl & LC_RENEGOTIATION_SUPPORT) { + lanes = (link_width_cntl & LC_LINK_WIDTH_RD_MASK) >> LC_LINK_WIDTH_RD_SHIFT; + link_width_cntl &= ~(LC_LINK_WIDTH_MASK | + LC_RECONFIG_ARC_MISSING_ESCAPE); + link_width_cntl |= lanes | LC_RECONFIG_NOW | LC_RENEGOTIATE_EN; + WREG32_PCIE_P(PCIE_LC_LINK_WIDTH_CNTL, link_width_cntl); + } else { + link_width_cntl |= LC_UPCONFIGURE_DIS; + WREG32_PCIE_P(PCIE_LC_LINK_WIDTH_CNTL, link_width_cntl); + } + } + + speed_cntl = RREG32_PCIE_P(PCIE_LC_SPEED_CNTL); + if ((speed_cntl & LC_OTHER_SIDE_EVER_SENT_GEN2) && + (speed_cntl & LC_OTHER_SIDE_SUPPORTS_GEN2)) { + + /* 55 nm r6xx asics */ + if ((rdev->family == CHIP_RV670) || + (rdev->family == CHIP_RV620) || + (rdev->family == CHIP_RV635)) { + WREG32(MM_CFGREGS_CNTL, 0x8); + link_cntl2 = RREG32(0x4088); + WREG32(MM_CFGREGS_CNTL, 0); + /* not supported yet */ + if (link_cntl2 & SELECTABLE_DEEMPHASIS) + return; + } + + speed_cntl &= ~LC_SPEED_CHANGE_ATTEMPTS_ALLOWED_MASK; + speed_cntl |= (0x3 << LC_SPEED_CHANGE_ATTEMPTS_ALLOWED_SHIFT); + speed_cntl &= ~LC_VOLTAGE_TIMER_SEL_MASK; + speed_cntl &= ~LC_FORCE_DIS_HW_SPEED_CHANGE; + speed_cntl |= LC_FORCE_EN_HW_SPEED_CHANGE; + WREG32_PCIE_P(PCIE_LC_SPEED_CNTL, speed_cntl); + + tmp = RREG32(0x541c); + WREG32(0x541c, tmp | 0x8); + WREG32(MM_CFGREGS_CNTL, MM_WR_TO_CFG_EN); + link_cntl2 = RREG16(0x4088); + link_cntl2 &= ~TARGET_LINK_SPEED_MASK; + link_cntl2 |= 0x2; + WREG16(0x4088, link_cntl2); + WREG32(MM_CFGREGS_CNTL, 0); + + if ((rdev->family == CHIP_RV670) || + (rdev->family == CHIP_RV620) || + (rdev->family == CHIP_RV635)) { + training_cntl = RREG32_PCIE_P(PCIE_LC_TRAINING_CNTL); + training_cntl &= ~LC_POINT_7_PLUS_EN; + WREG32_PCIE_P(PCIE_LC_TRAINING_CNTL, training_cntl); + } else { + speed_cntl = RREG32_PCIE_P(PCIE_LC_SPEED_CNTL); + speed_cntl &= ~LC_TARGET_LINK_SPEED_OVERRIDE_EN; + WREG32_PCIE_P(PCIE_LC_SPEED_CNTL, speed_cntl); + } + + speed_cntl = RREG32_PCIE_P(PCIE_LC_SPEED_CNTL); + speed_cntl |= LC_GEN2_EN_STRAP; + WREG32_PCIE_P(PCIE_LC_SPEED_CNTL, speed_cntl); + + } else { + link_width_cntl = RREG32_PCIE_P(PCIE_LC_LINK_WIDTH_CNTL); + /* XXX: only disable it if gen1 bridge vendor == 0x111d or 0x1106 */ + if (1) + link_width_cntl |= LC_UPCONFIGURE_DIS; + else + link_width_cntl &= ~LC_UPCONFIGURE_DIS; + WREG32_PCIE_P(PCIE_LC_LINK_WIDTH_CNTL, link_width_cntl); + } +} + +/** + * r600_get_gpu_clock - return GPU clock counter snapshot + * + * @rdev: radeon_device pointer + * + * Fetches a GPU clock counter snapshot (R6xx-cayman). + * Returns the 64 bit clock counter snapshot. + */ +uint64_t r600_get_gpu_clock(struct radeon_device *rdev) +{ + uint64_t clock; + + sx_xlock(&rdev->gpu_clock_mutex); + WREG32(RLC_CAPTURE_GPU_CLOCK_COUNT, 1); + clock = (uint64_t)RREG32(RLC_GPU_CLOCK_COUNT_LSB) | + ((uint64_t)RREG32(RLC_GPU_CLOCK_COUNT_MSB) << 32ULL); + sx_xunlock(&rdev->gpu_clock_mutex); + return clock; +} diff --git a/sys/dev/drm2/radeon/r600_audio.c b/sys/dev/drm2/radeon/r600_audio.c new file mode 100644 index 00000000000..bf14b6095fa --- /dev/null +++ b/sys/dev/drm2/radeon/r600_audio.c @@ -0,0 +1,258 @@ +/* + * Copyright 2008 Advanced Micro Devices, Inc. + * Copyright 2008 Red Hat Inc. + * Copyright 2009 Christian König. + * + * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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. + * + * Authors: Christian König + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include "radeon.h" +#include "radeon_reg.h" +#include "radeon_asic.h" +#include "atom.h" + +/* + * check if enc_priv stores radeon_encoder_atom_dig + */ +static bool radeon_dig_encoder(struct drm_encoder *encoder) +{ + struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); + switch (radeon_encoder->encoder_id) { + case ENCODER_OBJECT_ID_INTERNAL_LVDS: + case ENCODER_OBJECT_ID_INTERNAL_TMDS1: + case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_TMDS1: + case ENCODER_OBJECT_ID_INTERNAL_LVTM1: + case ENCODER_OBJECT_ID_INTERNAL_DVO1: + case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DVO1: + case ENCODER_OBJECT_ID_INTERNAL_DDI: + case ENCODER_OBJECT_ID_INTERNAL_UNIPHY: + case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA: + case ENCODER_OBJECT_ID_INTERNAL_UNIPHY1: + case ENCODER_OBJECT_ID_INTERNAL_UNIPHY2: + return true; + } + return false; +} + +/* + * check if the chipset is supported + */ +static int r600_audio_chipset_supported(struct radeon_device *rdev) +{ + return (rdev->family >= CHIP_R600 && !ASIC_IS_DCE6(rdev)) + || rdev->family == CHIP_RS600 + || rdev->family == CHIP_RS690 + || rdev->family == CHIP_RS740; +} + +struct r600_audio r600_audio_status(struct radeon_device *rdev) +{ + struct r600_audio status; + uint32_t value; + + value = RREG32(R600_AUDIO_RATE_BPS_CHANNEL); + + /* number of channels */ + status.channels = (value & 0x7) + 1; + + /* bits per sample */ + switch ((value & 0xF0) >> 4) { + case 0x0: + status.bits_per_sample = 8; + break; + case 0x1: + status.bits_per_sample = 16; + break; + case 0x2: + status.bits_per_sample = 20; + break; + case 0x3: + status.bits_per_sample = 24; + break; + case 0x4: + status.bits_per_sample = 32; + break; + default: + dev_err(rdev->dev, "Unknown bits per sample 0x%x, using 16\n", + (int)value); + status.bits_per_sample = 16; + } + + /* current sampling rate in HZ */ + if (value & 0x4000) + status.rate = 44100; + else + status.rate = 48000; + status.rate *= ((value >> 11) & 0x7) + 1; + status.rate /= ((value >> 8) & 0x7) + 1; + + value = RREG32(R600_AUDIO_STATUS_BITS); + + /* iec 60958 status bits */ + status.status_bits = value & 0xff; + + /* iec 60958 category code */ + status.category_code = (value >> 8) & 0xff; + + return status; +} + +/* + * update all hdmi interfaces with current audio parameters + */ +void r600_audio_update_hdmi(void *arg, int pending) +{ + struct radeon_device *rdev = arg; + struct drm_device *dev = rdev->ddev; + struct r600_audio audio_status = r600_audio_status(rdev); + struct drm_encoder *encoder; + bool changed = false; + + if (rdev->audio_status.channels != audio_status.channels || + rdev->audio_status.rate != audio_status.rate || + rdev->audio_status.bits_per_sample != audio_status.bits_per_sample || + rdev->audio_status.status_bits != audio_status.status_bits || + rdev->audio_status.category_code != audio_status.category_code) { + rdev->audio_status = audio_status; + changed = true; + } + + list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { + if (!radeon_dig_encoder(encoder)) + continue; + if (changed || r600_hdmi_buffer_status_changed(encoder)) + r600_hdmi_update_audio_settings(encoder); + } +} + +/* + * turn on/off audio engine + */ +static void r600_audio_engine_enable(struct radeon_device *rdev, bool enable) +{ + u32 value = 0; + DRM_INFO("%s audio support\n", enable ? "Enabling" : "Disabling"); + if (ASIC_IS_DCE4(rdev)) { + if (enable) { + value |= 0x81000000; /* Required to enable audio */ + value |= 0x0e1000f0; /* fglrx sets that too */ + } + WREG32(EVERGREEN_AUDIO_ENABLE, value); + } else { + WREG32_P(R600_AUDIO_ENABLE, + enable ? 0x81000000 : 0x0, ~0x81000000); + } + rdev->audio_enabled = enable; +} + +/* + * initialize the audio vars + */ +int r600_audio_init(struct radeon_device *rdev) +{ + if (!radeon_audio || !r600_audio_chipset_supported(rdev)) + return 0; + + r600_audio_engine_enable(rdev, true); + + rdev->audio_status.channels = -1; + rdev->audio_status.rate = -1; + rdev->audio_status.bits_per_sample = -1; + rdev->audio_status.status_bits = 0; + rdev->audio_status.category_code = 0; + + return 0; +} + +/* + * atach the audio codec to the clock source of the encoder + */ +void r600_audio_set_clock(struct drm_encoder *encoder, int clock) +{ + struct drm_device *dev = encoder->dev; + struct radeon_device *rdev = dev->dev_private; + struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); + struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv; + struct radeon_crtc *radeon_crtc = to_radeon_crtc(encoder->crtc); + int base_rate = 48000; + + switch (radeon_encoder->encoder_id) { + case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_TMDS1: + case ENCODER_OBJECT_ID_INTERNAL_LVTM1: + WREG32_P(R600_AUDIO_TIMING, 0, ~0x301); + break; + case ENCODER_OBJECT_ID_INTERNAL_UNIPHY: + case ENCODER_OBJECT_ID_INTERNAL_UNIPHY1: + case ENCODER_OBJECT_ID_INTERNAL_UNIPHY2: + case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA: + WREG32_P(R600_AUDIO_TIMING, 0x100, ~0x301); + break; + default: + dev_err(rdev->dev, "Unsupported encoder type 0x%02X\n", + radeon_encoder->encoder_id); + return; + } + + if (ASIC_IS_DCE4(rdev)) { + /* TODO: other PLLs? */ + WREG32(EVERGREEN_AUDIO_PLL1_MUL, base_rate * 10); + WREG32(EVERGREEN_AUDIO_PLL1_DIV, clock * 10); + WREG32(EVERGREEN_AUDIO_PLL1_UNK, 0x00000071); + + /* Select DTO source */ + WREG32(0x5ac, radeon_crtc->crtc_id); + } else { + switch (dig->dig_encoder) { + case 0: + WREG32(R600_AUDIO_PLL1_MUL, base_rate * 50); + WREG32(R600_AUDIO_PLL1_DIV, clock * 100); + WREG32(R600_AUDIO_CLK_SRCSEL, 0); + break; + + case 1: + WREG32(R600_AUDIO_PLL2_MUL, base_rate * 50); + WREG32(R600_AUDIO_PLL2_DIV, clock * 100); + WREG32(R600_AUDIO_CLK_SRCSEL, 1); + break; + default: + dev_err(rdev->dev, + "Unsupported DIG on encoder 0x%02X\n", + radeon_encoder->encoder_id); + return; + } + } +} + +/* + * release the audio timer + * TODO: How to do this correctly on SMP systems? + */ +void r600_audio_fini(struct radeon_device *rdev) +{ + if (!rdev->audio_enabled) + return; + + r600_audio_engine_enable(rdev, false); +} diff --git a/sys/dev/drm2/radeon/r600_blit.c b/sys/dev/drm2/radeon/r600_blit.c new file mode 100644 index 00000000000..f9658d49ed2 --- /dev/null +++ b/sys/dev/drm2/radeon/r600_blit.c @@ -0,0 +1,876 @@ +/* + * Copyright 2009 Advanced Micro Devices, Inc. + * + * 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 (including the next + * paragraph) 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 COPYRIGHT HOLDER(S) AND/OR ITS SUPPLIERS 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. + * + * Authors: + * Alex Deucher + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include "radeon_drv.h" + +#include "r600_blit_shaders.h" + +#define DI_PT_RECTLIST 0x11 +#define DI_INDEX_SIZE_16_BIT 0x0 +#define DI_SRC_SEL_AUTO_INDEX 0x2 + +#define FMT_8 0x1 +#define FMT_5_6_5 0x8 +#define FMT_8_8_8_8 0x1a +#define COLOR_8 0x1 +#define COLOR_5_6_5 0x8 +#define COLOR_8_8_8_8 0x1a + +static void +set_render_target(drm_radeon_private_t *dev_priv, int format, int w, int h, u64 gpu_addr) +{ + u32 cb_color_info; + int pitch, slice; + RING_LOCALS; + DRM_DEBUG("\n"); + + h = roundup2(h, 8); + if (h < 8) + h = 8; + + cb_color_info = ((format << 2) | (1 << 27)); + pitch = (w / 8) - 1; + slice = ((w * h) / 64) - 1; + + if (((dev_priv->flags & RADEON_FAMILY_MASK) > CHIP_R600) && + ((dev_priv->flags & RADEON_FAMILY_MASK) < CHIP_RV770)) { + BEGIN_RING(21 + 2); + OUT_RING(CP_PACKET3(R600_IT_SET_CONTEXT_REG, 1)); + OUT_RING((R600_CB_COLOR0_BASE - R600_SET_CONTEXT_REG_OFFSET) >> 2); + OUT_RING(gpu_addr >> 8); + OUT_RING(CP_PACKET3(R600_IT_SURFACE_BASE_UPDATE, 0)); + OUT_RING(2 << 0); + } else { + BEGIN_RING(21); + OUT_RING(CP_PACKET3(R600_IT_SET_CONTEXT_REG, 1)); + OUT_RING((R600_CB_COLOR0_BASE - R600_SET_CONTEXT_REG_OFFSET) >> 2); + OUT_RING(gpu_addr >> 8); + } + + OUT_RING(CP_PACKET3(R600_IT_SET_CONTEXT_REG, 1)); + OUT_RING((R600_CB_COLOR0_SIZE - R600_SET_CONTEXT_REG_OFFSET) >> 2); + OUT_RING((pitch << 0) | (slice << 10)); + + OUT_RING(CP_PACKET3(R600_IT_SET_CONTEXT_REG, 1)); + OUT_RING((R600_CB_COLOR0_VIEW - R600_SET_CONTEXT_REG_OFFSET) >> 2); + OUT_RING(0); + + OUT_RING(CP_PACKET3(R600_IT_SET_CONTEXT_REG, 1)); + OUT_RING((R600_CB_COLOR0_INFO - R600_SET_CONTEXT_REG_OFFSET) >> 2); + OUT_RING(cb_color_info); + + OUT_RING(CP_PACKET3(R600_IT_SET_CONTEXT_REG, 1)); + OUT_RING((R600_CB_COLOR0_TILE - R600_SET_CONTEXT_REG_OFFSET) >> 2); + OUT_RING(0); + + OUT_RING(CP_PACKET3(R600_IT_SET_CONTEXT_REG, 1)); + OUT_RING((R600_CB_COLOR0_FRAG - R600_SET_CONTEXT_REG_OFFSET) >> 2); + OUT_RING(0); + + OUT_RING(CP_PACKET3(R600_IT_SET_CONTEXT_REG, 1)); + OUT_RING((R600_CB_COLOR0_MASK - R600_SET_CONTEXT_REG_OFFSET) >> 2); + OUT_RING(0); + + ADVANCE_RING(); +} + +static void +cp_set_surface_sync(drm_radeon_private_t *dev_priv, + u32 sync_type, u32 size, u64 mc_addr) +{ + u32 cp_coher_size; + RING_LOCALS; + DRM_DEBUG("\n"); + + if (size == 0xffffffff) + cp_coher_size = 0xffffffff; + else + cp_coher_size = ((size + 255) >> 8); + + BEGIN_RING(5); + OUT_RING(CP_PACKET3(R600_IT_SURFACE_SYNC, 3)); + OUT_RING(sync_type); + OUT_RING(cp_coher_size); + OUT_RING((mc_addr >> 8)); + OUT_RING(10); /* poll interval */ + ADVANCE_RING(); +} + +static void +set_shaders(struct drm_device *dev) +{ + drm_radeon_private_t *dev_priv = dev->dev_private; + u64 gpu_addr; + int i; + u32 *vs, *ps; + uint32_t sq_pgm_resources; + RING_LOCALS; + DRM_DEBUG("\n"); + + /* load shaders */ + vs = (u32 *) ((char *)dev->agp_buffer_map->handle + dev_priv->blit_vb->offset); + ps = (u32 *) ((char *)dev->agp_buffer_map->handle + dev_priv->blit_vb->offset + 256); + + for (i = 0; i < r6xx_vs_size; i++) + vs[i] = cpu_to_le32(r6xx_vs[i]); + for (i = 0; i < r6xx_ps_size; i++) + ps[i] = cpu_to_le32(r6xx_ps[i]); + + dev_priv->blit_vb->used = 512; + + gpu_addr = dev_priv->gart_buffers_offset + dev_priv->blit_vb->offset; + + /* setup shader regs */ + sq_pgm_resources = (1 << 0); + + BEGIN_RING(9 + 12); + /* VS */ + OUT_RING(CP_PACKET3(R600_IT_SET_CONTEXT_REG, 1)); + OUT_RING((R600_SQ_PGM_START_VS - R600_SET_CONTEXT_REG_OFFSET) >> 2); + OUT_RING(gpu_addr >> 8); + + OUT_RING(CP_PACKET3(R600_IT_SET_CONTEXT_REG, 1)); + OUT_RING((R600_SQ_PGM_RESOURCES_VS - R600_SET_CONTEXT_REG_OFFSET) >> 2); + OUT_RING(sq_pgm_resources); + + OUT_RING(CP_PACKET3(R600_IT_SET_CONTEXT_REG, 1)); + OUT_RING((R600_SQ_PGM_CF_OFFSET_VS - R600_SET_CONTEXT_REG_OFFSET) >> 2); + OUT_RING(0); + + /* PS */ + OUT_RING(CP_PACKET3(R600_IT_SET_CONTEXT_REG, 1)); + OUT_RING((R600_SQ_PGM_START_PS - R600_SET_CONTEXT_REG_OFFSET) >> 2); + OUT_RING((gpu_addr + 256) >> 8); + + OUT_RING(CP_PACKET3(R600_IT_SET_CONTEXT_REG, 1)); + OUT_RING((R600_SQ_PGM_RESOURCES_PS - R600_SET_CONTEXT_REG_OFFSET) >> 2); + OUT_RING(sq_pgm_resources | (1 << 28)); + + OUT_RING(CP_PACKET3(R600_IT_SET_CONTEXT_REG, 1)); + OUT_RING((R600_SQ_PGM_EXPORTS_PS - R600_SET_CONTEXT_REG_OFFSET) >> 2); + OUT_RING(2); + + OUT_RING(CP_PACKET3(R600_IT_SET_CONTEXT_REG, 1)); + OUT_RING((R600_SQ_PGM_CF_OFFSET_PS - R600_SET_CONTEXT_REG_OFFSET) >> 2); + OUT_RING(0); + ADVANCE_RING(); + + cp_set_surface_sync(dev_priv, + R600_SH_ACTION_ENA, 512, gpu_addr); +} + +static void +set_vtx_resource(drm_radeon_private_t *dev_priv, u64 gpu_addr) +{ + uint32_t sq_vtx_constant_word2; + RING_LOCALS; + DRM_DEBUG("\n"); + + sq_vtx_constant_word2 = (((gpu_addr >> 32) & 0xff) | (16 << 8)); +#ifdef __BIG_ENDIAN + sq_vtx_constant_word2 |= (2 << 30); +#endif + + BEGIN_RING(9); + OUT_RING(CP_PACKET3(R600_IT_SET_RESOURCE, 7)); + OUT_RING(0x460); + OUT_RING(gpu_addr & 0xffffffff); + OUT_RING(48 - 1); + OUT_RING(sq_vtx_constant_word2); + OUT_RING(1 << 0); + OUT_RING(0); + OUT_RING(0); + OUT_RING(R600_SQ_TEX_VTX_VALID_BUFFER << 30); + ADVANCE_RING(); + + if (((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RV610) || + ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RV620) || + ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RS780) || + ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RS880) || + ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RV710)) + cp_set_surface_sync(dev_priv, + R600_TC_ACTION_ENA, 48, gpu_addr); + else + cp_set_surface_sync(dev_priv, + R600_VC_ACTION_ENA, 48, gpu_addr); +} + +static void +set_tex_resource(drm_radeon_private_t *dev_priv, + int format, int w, int h, int pitch, u64 gpu_addr) +{ + uint32_t sq_tex_resource_word0, sq_tex_resource_word1, sq_tex_resource_word4; + RING_LOCALS; + DRM_DEBUG("\n"); + + if (h < 1) + h = 1; + + sq_tex_resource_word0 = (1 << 0); + sq_tex_resource_word0 |= ((((pitch >> 3) - 1) << 8) | + ((w - 1) << 19)); + + sq_tex_resource_word1 = (format << 26); + sq_tex_resource_word1 |= ((h - 1) << 0); + + sq_tex_resource_word4 = ((1 << 14) | + (0 << 16) | + (1 << 19) | + (2 << 22) | + (3 << 25)); + + BEGIN_RING(9); + OUT_RING(CP_PACKET3(R600_IT_SET_RESOURCE, 7)); + OUT_RING(0); + OUT_RING(sq_tex_resource_word0); + OUT_RING(sq_tex_resource_word1); + OUT_RING(gpu_addr >> 8); + OUT_RING(gpu_addr >> 8); + OUT_RING(sq_tex_resource_word4); + OUT_RING(0); + OUT_RING(R600_SQ_TEX_VTX_VALID_TEXTURE << 30); + ADVANCE_RING(); + +} + +static void +set_scissors(drm_radeon_private_t *dev_priv, int x1, int y1, int x2, int y2) +{ + RING_LOCALS; + DRM_DEBUG("\n"); + + BEGIN_RING(12); + OUT_RING(CP_PACKET3(R600_IT_SET_CONTEXT_REG, 2)); + OUT_RING((R600_PA_SC_SCREEN_SCISSOR_TL - R600_SET_CONTEXT_REG_OFFSET) >> 2); + OUT_RING((x1 << 0) | (y1 << 16)); + OUT_RING((x2 << 0) | (y2 << 16)); + + OUT_RING(CP_PACKET3(R600_IT_SET_CONTEXT_REG, 2)); + OUT_RING((R600_PA_SC_GENERIC_SCISSOR_TL - R600_SET_CONTEXT_REG_OFFSET) >> 2); + OUT_RING((x1 << 0) | (y1 << 16) | (1 << 31)); + OUT_RING((x2 << 0) | (y2 << 16)); + + OUT_RING(CP_PACKET3(R600_IT_SET_CONTEXT_REG, 2)); + OUT_RING((R600_PA_SC_WINDOW_SCISSOR_TL - R600_SET_CONTEXT_REG_OFFSET) >> 2); + OUT_RING((x1 << 0) | (y1 << 16) | (1 << 31)); + OUT_RING((x2 << 0) | (y2 << 16)); + ADVANCE_RING(); +} + +static void +draw_auto(drm_radeon_private_t *dev_priv) +{ + RING_LOCALS; + DRM_DEBUG("\n"); + + BEGIN_RING(10); + OUT_RING(CP_PACKET3(R600_IT_SET_CONFIG_REG, 1)); + OUT_RING((R600_VGT_PRIMITIVE_TYPE - R600_SET_CONFIG_REG_OFFSET) >> 2); + OUT_RING(DI_PT_RECTLIST); + + OUT_RING(CP_PACKET3(R600_IT_INDEX_TYPE, 0)); +#ifdef __BIG_ENDIAN + OUT_RING((2 << 2) | DI_INDEX_SIZE_16_BIT); +#else + OUT_RING(DI_INDEX_SIZE_16_BIT); +#endif + + OUT_RING(CP_PACKET3(R600_IT_NUM_INSTANCES, 0)); + OUT_RING(1); + + OUT_RING(CP_PACKET3(R600_IT_DRAW_INDEX_AUTO, 1)); + OUT_RING(3); + OUT_RING(DI_SRC_SEL_AUTO_INDEX); + + ADVANCE_RING(); + COMMIT_RING(); +} + +static void +set_default_state(drm_radeon_private_t *dev_priv) +{ + int i; + u32 sq_config, sq_gpr_resource_mgmt_1, sq_gpr_resource_mgmt_2; + u32 sq_thread_resource_mgmt, sq_stack_resource_mgmt_1, sq_stack_resource_mgmt_2; + int num_ps_gprs, num_vs_gprs, num_temp_gprs, num_gs_gprs, num_es_gprs; + int num_ps_threads, num_vs_threads, num_gs_threads, num_es_threads; + int num_ps_stack_entries, num_vs_stack_entries, num_gs_stack_entries, num_es_stack_entries; + RING_LOCALS; + + switch ((dev_priv->flags & RADEON_FAMILY_MASK)) { + case CHIP_R600: + num_ps_gprs = 192; + num_vs_gprs = 56; + num_temp_gprs = 4; + num_gs_gprs = 0; + num_es_gprs = 0; + num_ps_threads = 136; + num_vs_threads = 48; + num_gs_threads = 4; + num_es_threads = 4; + num_ps_stack_entries = 128; + num_vs_stack_entries = 128; + num_gs_stack_entries = 0; + num_es_stack_entries = 0; + break; + case CHIP_RV630: + case CHIP_RV635: + num_ps_gprs = 84; + num_vs_gprs = 36; + num_temp_gprs = 4; + num_gs_gprs = 0; + num_es_gprs = 0; + num_ps_threads = 144; + num_vs_threads = 40; + num_gs_threads = 4; + num_es_threads = 4; + num_ps_stack_entries = 40; + num_vs_stack_entries = 40; + num_gs_stack_entries = 32; + num_es_stack_entries = 16; + break; + case CHIP_RV610: + case CHIP_RV620: + case CHIP_RS780: + case CHIP_RS880: + default: + num_ps_gprs = 84; + num_vs_gprs = 36; + num_temp_gprs = 4; + num_gs_gprs = 0; + num_es_gprs = 0; + num_ps_threads = 136; + num_vs_threads = 48; + num_gs_threads = 4; + num_es_threads = 4; + num_ps_stack_entries = 40; + num_vs_stack_entries = 40; + num_gs_stack_entries = 32; + num_es_stack_entries = 16; + break; + case CHIP_RV670: + num_ps_gprs = 144; + num_vs_gprs = 40; + num_temp_gprs = 4; + num_gs_gprs = 0; + num_es_gprs = 0; + num_ps_threads = 136; + num_vs_threads = 48; + num_gs_threads = 4; + num_es_threads = 4; + num_ps_stack_entries = 40; + num_vs_stack_entries = 40; + num_gs_stack_entries = 32; + num_es_stack_entries = 16; + break; + case CHIP_RV770: + num_ps_gprs = 192; + num_vs_gprs = 56; + num_temp_gprs = 4; + num_gs_gprs = 0; + num_es_gprs = 0; + num_ps_threads = 188; + num_vs_threads = 60; + num_gs_threads = 0; + num_es_threads = 0; + num_ps_stack_entries = 256; + num_vs_stack_entries = 256; + num_gs_stack_entries = 0; + num_es_stack_entries = 0; + break; + case CHIP_RV730: + case CHIP_RV740: + num_ps_gprs = 84; + num_vs_gprs = 36; + num_temp_gprs = 4; + num_gs_gprs = 0; + num_es_gprs = 0; + num_ps_threads = 188; + num_vs_threads = 60; + num_gs_threads = 0; + num_es_threads = 0; + num_ps_stack_entries = 128; + num_vs_stack_entries = 128; + num_gs_stack_entries = 0; + num_es_stack_entries = 0; + break; + case CHIP_RV710: + num_ps_gprs = 192; + num_vs_gprs = 56; + num_temp_gprs = 4; + num_gs_gprs = 0; + num_es_gprs = 0; + num_ps_threads = 144; + num_vs_threads = 48; + num_gs_threads = 0; + num_es_threads = 0; + num_ps_stack_entries = 128; + num_vs_stack_entries = 128; + num_gs_stack_entries = 0; + num_es_stack_entries = 0; + break; + } + + if (((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RV610) || + ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RV620) || + ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RS780) || + ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RS880) || + ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RV710)) + sq_config = 0; + else + sq_config = R600_VC_ENABLE; + + sq_config |= (R600_DX9_CONSTS | + R600_ALU_INST_PREFER_VECTOR | + R600_PS_PRIO(0) | + R600_VS_PRIO(1) | + R600_GS_PRIO(2) | + R600_ES_PRIO(3)); + + sq_gpr_resource_mgmt_1 = (R600_NUM_PS_GPRS(num_ps_gprs) | + R600_NUM_VS_GPRS(num_vs_gprs) | + R600_NUM_CLAUSE_TEMP_GPRS(num_temp_gprs)); + sq_gpr_resource_mgmt_2 = (R600_NUM_GS_GPRS(num_gs_gprs) | + R600_NUM_ES_GPRS(num_es_gprs)); + sq_thread_resource_mgmt = (R600_NUM_PS_THREADS(num_ps_threads) | + R600_NUM_VS_THREADS(num_vs_threads) | + R600_NUM_GS_THREADS(num_gs_threads) | + R600_NUM_ES_THREADS(num_es_threads)); + sq_stack_resource_mgmt_1 = (R600_NUM_PS_STACK_ENTRIES(num_ps_stack_entries) | + R600_NUM_VS_STACK_ENTRIES(num_vs_stack_entries)); + sq_stack_resource_mgmt_2 = (R600_NUM_GS_STACK_ENTRIES(num_gs_stack_entries) | + R600_NUM_ES_STACK_ENTRIES(num_es_stack_entries)); + + if ((dev_priv->flags & RADEON_FAMILY_MASK) >= CHIP_RV770) { + BEGIN_RING(r7xx_default_size + 10); + for (i = 0; i < r7xx_default_size; i++) + OUT_RING(r7xx_default_state[i]); + } else { + BEGIN_RING(r6xx_default_size + 10); + for (i = 0; i < r6xx_default_size; i++) + OUT_RING(r6xx_default_state[i]); + } + OUT_RING(CP_PACKET3(R600_IT_EVENT_WRITE, 0)); + OUT_RING(R600_CACHE_FLUSH_AND_INV_EVENT); + /* SQ config */ + OUT_RING(CP_PACKET3(R600_IT_SET_CONFIG_REG, 6)); + OUT_RING((R600_SQ_CONFIG - R600_SET_CONFIG_REG_OFFSET) >> 2); + OUT_RING(sq_config); + OUT_RING(sq_gpr_resource_mgmt_1); + OUT_RING(sq_gpr_resource_mgmt_2); + OUT_RING(sq_thread_resource_mgmt); + OUT_RING(sq_stack_resource_mgmt_1); + OUT_RING(sq_stack_resource_mgmt_2); + ADVANCE_RING(); +} + +/* 23 bits of float fractional data */ +#define I2F_FRAC_BITS 23 +#define I2F_MASK ((1 << I2F_FRAC_BITS) - 1) + +/* + * Converts unsigned integer into 32-bit IEEE floating point representation. + * Will be exact from 0 to 2^24. Above that, we round towards zero + * as the fractional bits will not fit in a float. (It would be better to + * round towards even as the fpu does, but that is slower.) + */ +__pure uint32_t int2float(uint32_t x) +{ + uint32_t msb, exponent, fraction; + + /* Zero is special */ + if (!x) return 0; + + /* Get location of the most significant bit */ + msb = fls(x); + + /* + * Use a rotate instead of a shift because that works both leftwards + * and rightwards due to the mod(32) behaviour. This means we don't + * need to check to see if we are above 2^24 or not. + */ + fraction = ror32(x, (msb - I2F_FRAC_BITS) & 0x1f) & I2F_MASK; + exponent = (127 + msb) << I2F_FRAC_BITS; + + return fraction + exponent; +} + +static int r600_nomm_get_vb(struct drm_device *dev) +{ + drm_radeon_private_t *dev_priv = dev->dev_private; + dev_priv->blit_vb = radeon_freelist_get(dev); + if (!dev_priv->blit_vb) { + DRM_ERROR("Unable to allocate vertex buffer for blit\n"); + return -EAGAIN; + } + return 0; +} + +static void r600_nomm_put_vb(struct drm_device *dev) +{ + drm_radeon_private_t *dev_priv = dev->dev_private; + + dev_priv->blit_vb->used = 0; + radeon_cp_discard_buffer(dev, dev_priv->blit_vb->file_priv->masterp, dev_priv->blit_vb); +} + +static void *r600_nomm_get_vb_ptr(struct drm_device *dev) +{ + drm_radeon_private_t *dev_priv = dev->dev_private; + return (((char *)dev->agp_buffer_map->handle + + dev_priv->blit_vb->offset + dev_priv->blit_vb->used)); +} + +int +r600_prepare_blit_copy(struct drm_device *dev, struct drm_file *file_priv) +{ + drm_radeon_private_t *dev_priv = dev->dev_private; + int ret; + DRM_DEBUG("\n"); + + ret = r600_nomm_get_vb(dev); + if (ret) + return ret; + + dev_priv->blit_vb->file_priv = file_priv; + + set_default_state(dev_priv); + set_shaders(dev); + + return 0; +} + + +void +r600_done_blit_copy(struct drm_device *dev) +{ + drm_radeon_private_t *dev_priv = dev->dev_private; + RING_LOCALS; + DRM_DEBUG("\n"); + + BEGIN_RING(5); + OUT_RING(CP_PACKET3(R600_IT_EVENT_WRITE, 0)); + OUT_RING(R600_CACHE_FLUSH_AND_INV_EVENT); + /* wait for 3D idle clean */ + OUT_RING(CP_PACKET3(R600_IT_SET_CONFIG_REG, 1)); + OUT_RING((R600_WAIT_UNTIL - R600_SET_CONFIG_REG_OFFSET) >> 2); + OUT_RING(RADEON_WAIT_3D_IDLE | RADEON_WAIT_3D_IDLECLEAN); + + ADVANCE_RING(); + COMMIT_RING(); + + r600_nomm_put_vb(dev); +} + +void +r600_blit_copy(struct drm_device *dev, + uint64_t src_gpu_addr, uint64_t dst_gpu_addr, + int size_bytes) +{ + drm_radeon_private_t *dev_priv = dev->dev_private; + int max_bytes; + u64 vb_addr; + u32 *vb; + + vb = r600_nomm_get_vb_ptr(dev); + + if ((size_bytes & 3) || (src_gpu_addr & 3) || (dst_gpu_addr & 3)) { + max_bytes = 8192; + + while (size_bytes) { + int cur_size = size_bytes; + int src_x = src_gpu_addr & 255; + int dst_x = dst_gpu_addr & 255; + int h = 1; + src_gpu_addr = src_gpu_addr & ~255; + dst_gpu_addr = dst_gpu_addr & ~255; + + if (!src_x && !dst_x) { + h = (cur_size / max_bytes); + if (h > 8192) + h = 8192; + if (h == 0) + h = 1; + else + cur_size = max_bytes; + } else { + if (cur_size > max_bytes) + cur_size = max_bytes; + if (cur_size > (max_bytes - dst_x)) + cur_size = (max_bytes - dst_x); + if (cur_size > (max_bytes - src_x)) + cur_size = (max_bytes - src_x); + } + + if ((dev_priv->blit_vb->used + 48) > dev_priv->blit_vb->total) { + + r600_nomm_put_vb(dev); + r600_nomm_get_vb(dev); + if (!dev_priv->blit_vb) + return; + set_shaders(dev); + vb = r600_nomm_get_vb_ptr(dev); + } + + vb[0] = int2float(dst_x); + vb[1] = 0; + vb[2] = int2float(src_x); + vb[3] = 0; + + vb[4] = int2float(dst_x); + vb[5] = int2float(h); + vb[6] = int2float(src_x); + vb[7] = int2float(h); + + vb[8] = int2float(dst_x + cur_size); + vb[9] = int2float(h); + vb[10] = int2float(src_x + cur_size); + vb[11] = int2float(h); + + /* src */ + set_tex_resource(dev_priv, FMT_8, + src_x + cur_size, h, src_x + cur_size, + src_gpu_addr); + + cp_set_surface_sync(dev_priv, + R600_TC_ACTION_ENA, (src_x + cur_size * h), src_gpu_addr); + + /* dst */ + set_render_target(dev_priv, COLOR_8, + dst_x + cur_size, h, + dst_gpu_addr); + + /* scissors */ + set_scissors(dev_priv, dst_x, 0, dst_x + cur_size, h); + + /* Vertex buffer setup */ + vb_addr = dev_priv->gart_buffers_offset + + dev_priv->blit_vb->offset + + dev_priv->blit_vb->used; + set_vtx_resource(dev_priv, vb_addr); + + /* draw */ + draw_auto(dev_priv); + + cp_set_surface_sync(dev_priv, + R600_CB_ACTION_ENA | R600_CB0_DEST_BASE_ENA, + cur_size * h, dst_gpu_addr); + + vb += 12; + dev_priv->blit_vb->used += 12 * 4; + + src_gpu_addr += cur_size * h; + dst_gpu_addr += cur_size * h; + size_bytes -= cur_size * h; + } + } else { + max_bytes = 8192 * 4; + + while (size_bytes) { + int cur_size = size_bytes; + int src_x = (src_gpu_addr & 255); + int dst_x = (dst_gpu_addr & 255); + int h = 1; + src_gpu_addr = src_gpu_addr & ~255; + dst_gpu_addr = dst_gpu_addr & ~255; + + if (!src_x && !dst_x) { + h = (cur_size / max_bytes); + if (h > 8192) + h = 8192; + if (h == 0) + h = 1; + else + cur_size = max_bytes; + } else { + if (cur_size > max_bytes) + cur_size = max_bytes; + if (cur_size > (max_bytes - dst_x)) + cur_size = (max_bytes - dst_x); + if (cur_size > (max_bytes - src_x)) + cur_size = (max_bytes - src_x); + } + + if ((dev_priv->blit_vb->used + 48) > dev_priv->blit_vb->total) { + r600_nomm_put_vb(dev); + r600_nomm_get_vb(dev); + if (!dev_priv->blit_vb) + return; + + set_shaders(dev); + vb = r600_nomm_get_vb_ptr(dev); + } + + vb[0] = int2float(dst_x / 4); + vb[1] = 0; + vb[2] = int2float(src_x / 4); + vb[3] = 0; + + vb[4] = int2float(dst_x / 4); + vb[5] = int2float(h); + vb[6] = int2float(src_x / 4); + vb[7] = int2float(h); + + vb[8] = int2float((dst_x + cur_size) / 4); + vb[9] = int2float(h); + vb[10] = int2float((src_x + cur_size) / 4); + vb[11] = int2float(h); + + /* src */ + set_tex_resource(dev_priv, FMT_8_8_8_8, + (src_x + cur_size) / 4, + h, (src_x + cur_size) / 4, + src_gpu_addr); + + cp_set_surface_sync(dev_priv, + R600_TC_ACTION_ENA, (src_x + cur_size * h), src_gpu_addr); + + /* dst */ + set_render_target(dev_priv, COLOR_8_8_8_8, + (dst_x + cur_size) / 4, h, + dst_gpu_addr); + + /* scissors */ + set_scissors(dev_priv, (dst_x / 4), 0, (dst_x + cur_size / 4), h); + + /* Vertex buffer setup */ + vb_addr = dev_priv->gart_buffers_offset + + dev_priv->blit_vb->offset + + dev_priv->blit_vb->used; + set_vtx_resource(dev_priv, vb_addr); + + /* draw */ + draw_auto(dev_priv); + + cp_set_surface_sync(dev_priv, + R600_CB_ACTION_ENA | R600_CB0_DEST_BASE_ENA, + cur_size * h, dst_gpu_addr); + + vb += 12; + dev_priv->blit_vb->used += 12 * 4; + + src_gpu_addr += cur_size * h; + dst_gpu_addr += cur_size * h; + size_bytes -= cur_size * h; + } + } +} + +void +r600_blit_swap(struct drm_device *dev, + uint64_t src_gpu_addr, uint64_t dst_gpu_addr, + int sx, int sy, int dx, int dy, + int w, int h, int src_pitch, int dst_pitch, int cpp) +{ + drm_radeon_private_t *dev_priv = dev->dev_private; + int cb_format, tex_format; + int sx2, sy2, dx2, dy2; + u64 vb_addr; + u32 *vb; + + if ((dev_priv->blit_vb->used + 48) > dev_priv->blit_vb->total) { + + r600_nomm_put_vb(dev); + r600_nomm_get_vb(dev); + if (!dev_priv->blit_vb) + return; + + set_shaders(dev); + } + vb = r600_nomm_get_vb_ptr(dev); + + sx2 = sx + w; + sy2 = sy + h; + dx2 = dx + w; + dy2 = dy + h; + + vb[0] = int2float(dx); + vb[1] = int2float(dy); + vb[2] = int2float(sx); + vb[3] = int2float(sy); + + vb[4] = int2float(dx); + vb[5] = int2float(dy2); + vb[6] = int2float(sx); + vb[7] = int2float(sy2); + + vb[8] = int2float(dx2); + vb[9] = int2float(dy2); + vb[10] = int2float(sx2); + vb[11] = int2float(sy2); + + switch(cpp) { + case 4: + cb_format = COLOR_8_8_8_8; + tex_format = FMT_8_8_8_8; + break; + case 2: + cb_format = COLOR_5_6_5; + tex_format = FMT_5_6_5; + break; + default: + cb_format = COLOR_8; + tex_format = FMT_8; + break; + } + + /* src */ + set_tex_resource(dev_priv, tex_format, + src_pitch / cpp, + sy2, src_pitch / cpp, + src_gpu_addr); + + cp_set_surface_sync(dev_priv, + R600_TC_ACTION_ENA, src_pitch * sy2, src_gpu_addr); + + /* dst */ + set_render_target(dev_priv, cb_format, + dst_pitch / cpp, dy2, + dst_gpu_addr); + + /* scissors */ + set_scissors(dev_priv, dx, dy, dx2, dy2); + + /* Vertex buffer setup */ + vb_addr = dev_priv->gart_buffers_offset + + dev_priv->blit_vb->offset + + dev_priv->blit_vb->used; + set_vtx_resource(dev_priv, vb_addr); + + /* draw */ + draw_auto(dev_priv); + + cp_set_surface_sync(dev_priv, + R600_CB_ACTION_ENA | R600_CB0_DEST_BASE_ENA, + dst_pitch * dy2, dst_gpu_addr); + + dev_priv->blit_vb->used += 12 * 4; +} diff --git a/sys/dev/drm2/radeon/r600_blit_kms.c b/sys/dev/drm2/radeon/r600_blit_kms.c new file mode 100644 index 00000000000..e2ace6936a9 --- /dev/null +++ b/sys/dev/drm2/radeon/r600_blit_kms.c @@ -0,0 +1,758 @@ +/* + * Copyright 2009 Advanced Micro Devices, Inc. + * Copyright 2009 Red Hat Inc. + * + * 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 (including the next + * paragraph) 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 COPYRIGHT HOLDER(S) AND/OR ITS SUPPLIERS 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 +__FBSDID("$FreeBSD$"); + +#include +#include +#include "radeon.h" +#include "radeon_asic.h" + +#include "r600d.h" +#include "r600_blit_shaders.h" +#include "radeon_blit_common.h" + +/* emits 21 on rv770+, 23 on r600 */ +static void +set_render_target(struct radeon_device *rdev, int format, + int w, int h, u64 gpu_addr) +{ + struct radeon_ring *ring = &rdev->ring[RADEON_RING_TYPE_GFX_INDEX]; + u32 cb_color_info; + int pitch, slice; + + h = roundup2(h, 8); + if (h < 8) + h = 8; + + cb_color_info = CB_FORMAT(format) | + CB_SOURCE_FORMAT(CB_SF_EXPORT_NORM) | + CB_ARRAY_MODE(ARRAY_1D_TILED_THIN1); + pitch = (w / 8) - 1; + slice = ((w * h) / 64) - 1; + + radeon_ring_write(ring, PACKET3(PACKET3_SET_CONTEXT_REG, 1)); + radeon_ring_write(ring, (CB_COLOR0_BASE - PACKET3_SET_CONTEXT_REG_OFFSET) >> 2); + radeon_ring_write(ring, gpu_addr >> 8); + + if (rdev->family > CHIP_R600 && rdev->family < CHIP_RV770) { + radeon_ring_write(ring, PACKET3(PACKET3_SURFACE_BASE_UPDATE, 0)); + radeon_ring_write(ring, 2 << 0); + } + + radeon_ring_write(ring, PACKET3(PACKET3_SET_CONTEXT_REG, 1)); + radeon_ring_write(ring, (CB_COLOR0_SIZE - PACKET3_SET_CONTEXT_REG_OFFSET) >> 2); + radeon_ring_write(ring, (pitch << 0) | (slice << 10)); + + radeon_ring_write(ring, PACKET3(PACKET3_SET_CONTEXT_REG, 1)); + radeon_ring_write(ring, (CB_COLOR0_VIEW - PACKET3_SET_CONTEXT_REG_OFFSET) >> 2); + radeon_ring_write(ring, 0); + + radeon_ring_write(ring, PACKET3(PACKET3_SET_CONTEXT_REG, 1)); + radeon_ring_write(ring, (CB_COLOR0_INFO - PACKET3_SET_CONTEXT_REG_OFFSET) >> 2); + radeon_ring_write(ring, cb_color_info); + + radeon_ring_write(ring, PACKET3(PACKET3_SET_CONTEXT_REG, 1)); + radeon_ring_write(ring, (CB_COLOR0_TILE - PACKET3_SET_CONTEXT_REG_OFFSET) >> 2); + radeon_ring_write(ring, 0); + + radeon_ring_write(ring, PACKET3(PACKET3_SET_CONTEXT_REG, 1)); + radeon_ring_write(ring, (CB_COLOR0_FRAG - PACKET3_SET_CONTEXT_REG_OFFSET) >> 2); + radeon_ring_write(ring, 0); + + radeon_ring_write(ring, PACKET3(PACKET3_SET_CONTEXT_REG, 1)); + radeon_ring_write(ring, (CB_COLOR0_MASK - PACKET3_SET_CONTEXT_REG_OFFSET) >> 2); + radeon_ring_write(ring, 0); +} + +/* emits 5dw */ +static void +cp_set_surface_sync(struct radeon_device *rdev, + u32 sync_type, u32 size, + u64 mc_addr) +{ + struct radeon_ring *ring = &rdev->ring[RADEON_RING_TYPE_GFX_INDEX]; + u32 cp_coher_size; + + if (size == 0xffffffff) + cp_coher_size = 0xffffffff; + else + cp_coher_size = ((size + 255) >> 8); + + radeon_ring_write(ring, PACKET3(PACKET3_SURFACE_SYNC, 3)); + radeon_ring_write(ring, sync_type); + radeon_ring_write(ring, cp_coher_size); + radeon_ring_write(ring, mc_addr >> 8); + radeon_ring_write(ring, 10); /* poll interval */ +} + +/* emits 21dw + 1 surface sync = 26dw */ +static void +set_shaders(struct radeon_device *rdev) +{ + struct radeon_ring *ring = &rdev->ring[RADEON_RING_TYPE_GFX_INDEX]; + u64 gpu_addr; + u32 sq_pgm_resources; + + /* setup shader regs */ + sq_pgm_resources = (1 << 0); + + /* VS */ + gpu_addr = rdev->r600_blit.shader_gpu_addr + rdev->r600_blit.vs_offset; + radeon_ring_write(ring, PACKET3(PACKET3_SET_CONTEXT_REG, 1)); + radeon_ring_write(ring, (SQ_PGM_START_VS - PACKET3_SET_CONTEXT_REG_OFFSET) >> 2); + radeon_ring_write(ring, gpu_addr >> 8); + + radeon_ring_write(ring, PACKET3(PACKET3_SET_CONTEXT_REG, 1)); + radeon_ring_write(ring, (SQ_PGM_RESOURCES_VS - PACKET3_SET_CONTEXT_REG_OFFSET) >> 2); + radeon_ring_write(ring, sq_pgm_resources); + + radeon_ring_write(ring, PACKET3(PACKET3_SET_CONTEXT_REG, 1)); + radeon_ring_write(ring, (SQ_PGM_CF_OFFSET_VS - PACKET3_SET_CONTEXT_REG_OFFSET) >> 2); + radeon_ring_write(ring, 0); + + /* PS */ + gpu_addr = rdev->r600_blit.shader_gpu_addr + rdev->r600_blit.ps_offset; + radeon_ring_write(ring, PACKET3(PACKET3_SET_CONTEXT_REG, 1)); + radeon_ring_write(ring, (SQ_PGM_START_PS - PACKET3_SET_CONTEXT_REG_OFFSET) >> 2); + radeon_ring_write(ring, gpu_addr >> 8); + + radeon_ring_write(ring, PACKET3(PACKET3_SET_CONTEXT_REG, 1)); + radeon_ring_write(ring, (SQ_PGM_RESOURCES_PS - PACKET3_SET_CONTEXT_REG_OFFSET) >> 2); + radeon_ring_write(ring, sq_pgm_resources | (1 << 28)); + + radeon_ring_write(ring, PACKET3(PACKET3_SET_CONTEXT_REG, 1)); + radeon_ring_write(ring, (SQ_PGM_EXPORTS_PS - PACKET3_SET_CONTEXT_REG_OFFSET) >> 2); + radeon_ring_write(ring, 2); + + radeon_ring_write(ring, PACKET3(PACKET3_SET_CONTEXT_REG, 1)); + radeon_ring_write(ring, (SQ_PGM_CF_OFFSET_PS - PACKET3_SET_CONTEXT_REG_OFFSET) >> 2); + radeon_ring_write(ring, 0); + + gpu_addr = rdev->r600_blit.shader_gpu_addr + rdev->r600_blit.vs_offset; + cp_set_surface_sync(rdev, PACKET3_SH_ACTION_ENA, 512, gpu_addr); +} + +/* emits 9 + 1 sync (5) = 14*/ +static void +set_vtx_resource(struct radeon_device *rdev, u64 gpu_addr) +{ + struct radeon_ring *ring = &rdev->ring[RADEON_RING_TYPE_GFX_INDEX]; + u32 sq_vtx_constant_word2; + + sq_vtx_constant_word2 = SQ_VTXC_BASE_ADDR_HI(upper_32_bits(gpu_addr) & 0xff) | + SQ_VTXC_STRIDE(16); +#ifdef __BIG_ENDIAN + sq_vtx_constant_word2 |= SQ_VTXC_ENDIAN_SWAP(SQ_ENDIAN_8IN32); +#endif + + radeon_ring_write(ring, PACKET3(PACKET3_SET_RESOURCE, 7)); + radeon_ring_write(ring, 0x460); + radeon_ring_write(ring, gpu_addr & 0xffffffff); + radeon_ring_write(ring, 48 - 1); + radeon_ring_write(ring, sq_vtx_constant_word2); + radeon_ring_write(ring, 1 << 0); + radeon_ring_write(ring, 0); + radeon_ring_write(ring, 0); + radeon_ring_write(ring, SQ_TEX_VTX_VALID_BUFFER << 30); + + if ((rdev->family == CHIP_RV610) || + (rdev->family == CHIP_RV620) || + (rdev->family == CHIP_RS780) || + (rdev->family == CHIP_RS880) || + (rdev->family == CHIP_RV710)) + cp_set_surface_sync(rdev, + PACKET3_TC_ACTION_ENA, 48, gpu_addr); + else + cp_set_surface_sync(rdev, + PACKET3_VC_ACTION_ENA, 48, gpu_addr); +} + +/* emits 9 */ +static void +set_tex_resource(struct radeon_device *rdev, + int format, int w, int h, int pitch, + u64 gpu_addr, u32 size) +{ + struct radeon_ring *ring = &rdev->ring[RADEON_RING_TYPE_GFX_INDEX]; + uint32_t sq_tex_resource_word0, sq_tex_resource_word1, sq_tex_resource_word4; + + if (h < 1) + h = 1; + + sq_tex_resource_word0 = S_038000_DIM(V_038000_SQ_TEX_DIM_2D) | + S_038000_TILE_MODE(V_038000_ARRAY_1D_TILED_THIN1); + sq_tex_resource_word0 |= S_038000_PITCH((pitch >> 3) - 1) | + S_038000_TEX_WIDTH(w - 1); + + sq_tex_resource_word1 = S_038004_DATA_FORMAT(format); + sq_tex_resource_word1 |= S_038004_TEX_HEIGHT(h - 1); + + sq_tex_resource_word4 = S_038010_REQUEST_SIZE(1) | + S_038010_DST_SEL_X(SQ_SEL_X) | + S_038010_DST_SEL_Y(SQ_SEL_Y) | + S_038010_DST_SEL_Z(SQ_SEL_Z) | + S_038010_DST_SEL_W(SQ_SEL_W); + + cp_set_surface_sync(rdev, + PACKET3_TC_ACTION_ENA, size, gpu_addr); + + radeon_ring_write(ring, PACKET3(PACKET3_SET_RESOURCE, 7)); + radeon_ring_write(ring, 0); + radeon_ring_write(ring, sq_tex_resource_word0); + radeon_ring_write(ring, sq_tex_resource_word1); + radeon_ring_write(ring, gpu_addr >> 8); + radeon_ring_write(ring, gpu_addr >> 8); + radeon_ring_write(ring, sq_tex_resource_word4); + radeon_ring_write(ring, 0); + radeon_ring_write(ring, SQ_TEX_VTX_VALID_TEXTURE << 30); +} + +/* emits 12 */ +static void +set_scissors(struct radeon_device *rdev, int x1, int y1, + int x2, int y2) +{ + struct radeon_ring *ring = &rdev->ring[RADEON_RING_TYPE_GFX_INDEX]; + radeon_ring_write(ring, PACKET3(PACKET3_SET_CONTEXT_REG, 2)); + radeon_ring_write(ring, (PA_SC_SCREEN_SCISSOR_TL - PACKET3_SET_CONTEXT_REG_OFFSET) >> 2); + radeon_ring_write(ring, (x1 << 0) | (y1 << 16)); + radeon_ring_write(ring, (x2 << 0) | (y2 << 16)); + + radeon_ring_write(ring, PACKET3(PACKET3_SET_CONTEXT_REG, 2)); + radeon_ring_write(ring, (PA_SC_GENERIC_SCISSOR_TL - PACKET3_SET_CONTEXT_REG_OFFSET) >> 2); + radeon_ring_write(ring, (x1 << 0) | (y1 << 16) | (1 << 31)); + radeon_ring_write(ring, (x2 << 0) | (y2 << 16)); + + radeon_ring_write(ring, PACKET3(PACKET3_SET_CONTEXT_REG, 2)); + radeon_ring_write(ring, (PA_SC_WINDOW_SCISSOR_TL - PACKET3_SET_CONTEXT_REG_OFFSET) >> 2); + radeon_ring_write(ring, (x1 << 0) | (y1 << 16) | (1 << 31)); + radeon_ring_write(ring, (x2 << 0) | (y2 << 16)); +} + +/* emits 10 */ +static void +draw_auto(struct radeon_device *rdev) +{ + struct radeon_ring *ring = &rdev->ring[RADEON_RING_TYPE_GFX_INDEX]; + radeon_ring_write(ring, PACKET3(PACKET3_SET_CONFIG_REG, 1)); + radeon_ring_write(ring, (VGT_PRIMITIVE_TYPE - PACKET3_SET_CONFIG_REG_OFFSET) >> 2); + radeon_ring_write(ring, DI_PT_RECTLIST); + + radeon_ring_write(ring, PACKET3(PACKET3_INDEX_TYPE, 0)); + radeon_ring_write(ring, +#ifdef __BIG_ENDIAN + (2 << 2) | +#endif + DI_INDEX_SIZE_16_BIT); + + radeon_ring_write(ring, PACKET3(PACKET3_NUM_INSTANCES, 0)); + radeon_ring_write(ring, 1); + + radeon_ring_write(ring, PACKET3(PACKET3_DRAW_INDEX_AUTO, 1)); + radeon_ring_write(ring, 3); + radeon_ring_write(ring, DI_SRC_SEL_AUTO_INDEX); + +} + +/* emits 14 */ +static void +set_default_state(struct radeon_device *rdev) +{ + struct radeon_ring *ring = &rdev->ring[RADEON_RING_TYPE_GFX_INDEX]; + u32 sq_config, sq_gpr_resource_mgmt_1, sq_gpr_resource_mgmt_2; + u32 sq_thread_resource_mgmt, sq_stack_resource_mgmt_1, sq_stack_resource_mgmt_2; + int num_ps_gprs, num_vs_gprs, num_temp_gprs, num_gs_gprs, num_es_gprs; + int num_ps_threads, num_vs_threads, num_gs_threads, num_es_threads; + int num_ps_stack_entries, num_vs_stack_entries, num_gs_stack_entries, num_es_stack_entries; + u64 gpu_addr; + int dwords; + + switch (rdev->family) { + case CHIP_R600: + num_ps_gprs = 192; + num_vs_gprs = 56; + num_temp_gprs = 4; + num_gs_gprs = 0; + num_es_gprs = 0; + num_ps_threads = 136; + num_vs_threads = 48; + num_gs_threads = 4; + num_es_threads = 4; + num_ps_stack_entries = 128; + num_vs_stack_entries = 128; + num_gs_stack_entries = 0; + num_es_stack_entries = 0; + break; + case CHIP_RV630: + case CHIP_RV635: + num_ps_gprs = 84; + num_vs_gprs = 36; + num_temp_gprs = 4; + num_gs_gprs = 0; + num_es_gprs = 0; + num_ps_threads = 144; + num_vs_threads = 40; + num_gs_threads = 4; + num_es_threads = 4; + num_ps_stack_entries = 40; + num_vs_stack_entries = 40; + num_gs_stack_entries = 32; + num_es_stack_entries = 16; + break; + case CHIP_RV610: + case CHIP_RV620: + case CHIP_RS780: + case CHIP_RS880: + default: + num_ps_gprs = 84; + num_vs_gprs = 36; + num_temp_gprs = 4; + num_gs_gprs = 0; + num_es_gprs = 0; + num_ps_threads = 136; + num_vs_threads = 48; + num_gs_threads = 4; + num_es_threads = 4; + num_ps_stack_entries = 40; + num_vs_stack_entries = 40; + num_gs_stack_entries = 32; + num_es_stack_entries = 16; + break; + case CHIP_RV670: + num_ps_gprs = 144; + num_vs_gprs = 40; + num_temp_gprs = 4; + num_gs_gprs = 0; + num_es_gprs = 0; + num_ps_threads = 136; + num_vs_threads = 48; + num_gs_threads = 4; + num_es_threads = 4; + num_ps_stack_entries = 40; + num_vs_stack_entries = 40; + num_gs_stack_entries = 32; + num_es_stack_entries = 16; + break; + case CHIP_RV770: + num_ps_gprs = 192; + num_vs_gprs = 56; + num_temp_gprs = 4; + num_gs_gprs = 0; + num_es_gprs = 0; + num_ps_threads = 188; + num_vs_threads = 60; + num_gs_threads = 0; + num_es_threads = 0; + num_ps_stack_entries = 256; + num_vs_stack_entries = 256; + num_gs_stack_entries = 0; + num_es_stack_entries = 0; + break; + case CHIP_RV730: + case CHIP_RV740: + num_ps_gprs = 84; + num_vs_gprs = 36; + num_temp_gprs = 4; + num_gs_gprs = 0; + num_es_gprs = 0; + num_ps_threads = 188; + num_vs_threads = 60; + num_gs_threads = 0; + num_es_threads = 0; + num_ps_stack_entries = 128; + num_vs_stack_entries = 128; + num_gs_stack_entries = 0; + num_es_stack_entries = 0; + break; + case CHIP_RV710: + num_ps_gprs = 192; + num_vs_gprs = 56; + num_temp_gprs = 4; + num_gs_gprs = 0; + num_es_gprs = 0; + num_ps_threads = 144; + num_vs_threads = 48; + num_gs_threads = 0; + num_es_threads = 0; + num_ps_stack_entries = 128; + num_vs_stack_entries = 128; + num_gs_stack_entries = 0; + num_es_stack_entries = 0; + break; + } + + if ((rdev->family == CHIP_RV610) || + (rdev->family == CHIP_RV620) || + (rdev->family == CHIP_RS780) || + (rdev->family == CHIP_RS880) || + (rdev->family == CHIP_RV710)) + sq_config = 0; + else + sq_config = VC_ENABLE; + + sq_config |= (DX9_CONSTS | + ALU_INST_PREFER_VECTOR | + PS_PRIO(0) | + VS_PRIO(1) | + GS_PRIO(2) | + ES_PRIO(3)); + + sq_gpr_resource_mgmt_1 = (NUM_PS_GPRS(num_ps_gprs) | + NUM_VS_GPRS(num_vs_gprs) | + NUM_CLAUSE_TEMP_GPRS(num_temp_gprs)); + sq_gpr_resource_mgmt_2 = (NUM_GS_GPRS(num_gs_gprs) | + NUM_ES_GPRS(num_es_gprs)); + sq_thread_resource_mgmt = (NUM_PS_THREADS(num_ps_threads) | + NUM_VS_THREADS(num_vs_threads) | + NUM_GS_THREADS(num_gs_threads) | + NUM_ES_THREADS(num_es_threads)); + sq_stack_resource_mgmt_1 = (NUM_PS_STACK_ENTRIES(num_ps_stack_entries) | + NUM_VS_STACK_ENTRIES(num_vs_stack_entries)); + sq_stack_resource_mgmt_2 = (NUM_GS_STACK_ENTRIES(num_gs_stack_entries) | + NUM_ES_STACK_ENTRIES(num_es_stack_entries)); + + /* emit an IB pointing at default state */ + dwords = roundup2(rdev->r600_blit.state_len, 0x10); + gpu_addr = rdev->r600_blit.shader_gpu_addr + rdev->r600_blit.state_offset; + radeon_ring_write(ring, PACKET3(PACKET3_INDIRECT_BUFFER, 2)); + radeon_ring_write(ring, +#ifdef __BIG_ENDIAN + (2 << 0) | +#endif + (gpu_addr & 0xFFFFFFFC)); + radeon_ring_write(ring, upper_32_bits(gpu_addr) & 0xFF); + radeon_ring_write(ring, dwords); + + /* SQ config */ + radeon_ring_write(ring, PACKET3(PACKET3_SET_CONFIG_REG, 6)); + radeon_ring_write(ring, (SQ_CONFIG - PACKET3_SET_CONFIG_REG_OFFSET) >> 2); + radeon_ring_write(ring, sq_config); + radeon_ring_write(ring, sq_gpr_resource_mgmt_1); + radeon_ring_write(ring, sq_gpr_resource_mgmt_2); + radeon_ring_write(ring, sq_thread_resource_mgmt); + radeon_ring_write(ring, sq_stack_resource_mgmt_1); + radeon_ring_write(ring, sq_stack_resource_mgmt_2); +} + +int r600_blit_init(struct radeon_device *rdev) +{ + u32 obj_size; + int i, r, dwords; + void *ptr; + u32 packet2s[16]; + int num_packet2s = 0; + + rdev->r600_blit.primitives.set_render_target = set_render_target; + rdev->r600_blit.primitives.cp_set_surface_sync = cp_set_surface_sync; + rdev->r600_blit.primitives.set_shaders = set_shaders; + rdev->r600_blit.primitives.set_vtx_resource = set_vtx_resource; + rdev->r600_blit.primitives.set_tex_resource = set_tex_resource; + rdev->r600_blit.primitives.set_scissors = set_scissors; + rdev->r600_blit.primitives.draw_auto = draw_auto; + rdev->r600_blit.primitives.set_default_state = set_default_state; + + rdev->r600_blit.ring_size_common = 8; /* sync semaphore */ + rdev->r600_blit.ring_size_common += 40; /* shaders + def state */ + rdev->r600_blit.ring_size_common += 5; /* done copy */ + rdev->r600_blit.ring_size_common += 16; /* fence emit for done copy */ + + rdev->r600_blit.ring_size_per_loop = 76; + /* set_render_target emits 2 extra dwords on rv6xx */ + if (rdev->family > CHIP_R600 && rdev->family < CHIP_RV770) + rdev->r600_blit.ring_size_per_loop += 2; + + rdev->r600_blit.max_dim = 8192; + + rdev->r600_blit.state_offset = 0; + + if (rdev->family >= CHIP_RV770) + rdev->r600_blit.state_len = r7xx_default_size; + else + rdev->r600_blit.state_len = r6xx_default_size; + + dwords = rdev->r600_blit.state_len; + while (dwords & 0xf) { + packet2s[num_packet2s++] = cpu_to_le32(PACKET2(0)); + dwords++; + } + + obj_size = dwords * 4; + obj_size = roundup2(obj_size, 256); + + rdev->r600_blit.vs_offset = obj_size; + obj_size += r6xx_vs_size * 4; + obj_size = roundup2(obj_size, 256); + + rdev->r600_blit.ps_offset = obj_size; + obj_size += r6xx_ps_size * 4; + obj_size = roundup2(obj_size, 256); + + /* pin copy shader into vram if not already initialized */ + if (rdev->r600_blit.shader_obj == NULL) { + r = radeon_bo_create(rdev, obj_size, PAGE_SIZE, true, + RADEON_GEM_DOMAIN_VRAM, + NULL, &rdev->r600_blit.shader_obj); + if (r) { + DRM_ERROR("r600 failed to allocate shader\n"); + return r; + } + + r = radeon_bo_reserve(rdev->r600_blit.shader_obj, false); + if (unlikely(r != 0)) + return r; + r = radeon_bo_pin(rdev->r600_blit.shader_obj, RADEON_GEM_DOMAIN_VRAM, + &rdev->r600_blit.shader_gpu_addr); + radeon_bo_unreserve(rdev->r600_blit.shader_obj); + if (r) { + dev_err(rdev->dev, "(%d) pin blit object failed\n", r); + return r; + } + } + + DRM_DEBUG("r6xx blit allocated bo %08x vs %08x ps %08x\n", + obj_size, + rdev->r600_blit.vs_offset, rdev->r600_blit.ps_offset); + + r = radeon_bo_reserve(rdev->r600_blit.shader_obj, false); + if (unlikely(r != 0)) + return r; + r = radeon_bo_kmap(rdev->r600_blit.shader_obj, &ptr); + if (r) { + DRM_ERROR("failed to map blit object %d\n", r); + return r; + } + if (rdev->family >= CHIP_RV770) + memcpy_toio((char *)ptr + rdev->r600_blit.state_offset, + r7xx_default_state, rdev->r600_blit.state_len * 4); + else + memcpy_toio((char *)ptr + rdev->r600_blit.state_offset, + r6xx_default_state, rdev->r600_blit.state_len * 4); + if (num_packet2s) + memcpy_toio((char *)ptr + rdev->r600_blit.state_offset + (rdev->r600_blit.state_len * 4), + packet2s, num_packet2s * 4); + for (i = 0; i < r6xx_vs_size; i++) + *(u32 *)((unsigned long)ptr + rdev->r600_blit.vs_offset + i * 4) = cpu_to_le32(r6xx_vs[i]); + for (i = 0; i < r6xx_ps_size; i++) + *(u32 *)((unsigned long)ptr + rdev->r600_blit.ps_offset + i * 4) = cpu_to_le32(r6xx_ps[i]); + radeon_bo_kunmap(rdev->r600_blit.shader_obj); + radeon_bo_unreserve(rdev->r600_blit.shader_obj); + + radeon_ttm_set_active_vram_size(rdev, rdev->mc.real_vram_size); + return 0; +} + +void r600_blit_fini(struct radeon_device *rdev) +{ + int r; + + radeon_ttm_set_active_vram_size(rdev, rdev->mc.visible_vram_size); + if (rdev->r600_blit.shader_obj == NULL) + return; + /* If we can't reserve the bo, unref should be enough to destroy + * it when it becomes idle. + */ + r = radeon_bo_reserve(rdev->r600_blit.shader_obj, false); + if (!r) { + radeon_bo_unpin(rdev->r600_blit.shader_obj); + radeon_bo_unreserve(rdev->r600_blit.shader_obj); + } + radeon_bo_unref(&rdev->r600_blit.shader_obj); +} + +static unsigned r600_blit_create_rect(unsigned num_gpu_pages, + int *width, int *height, int max_dim) +{ + unsigned max_pages; + unsigned pages = num_gpu_pages; + int w, h; + + if (num_gpu_pages == 0) { + /* not supposed to be called with no pages, but just in case */ + h = 0; + w = 0; + pages = 0; + DRM_ERROR("%s: called with no pages", __func__); + } else { + int rect_order = 2; + h = RECT_UNIT_H; + while (num_gpu_pages / rect_order) { + h *= 2; + rect_order *= 4; + if (h >= max_dim) { + h = max_dim; + break; + } + } + max_pages = (max_dim * h) / (RECT_UNIT_W * RECT_UNIT_H); + if (pages > max_pages) + pages = max_pages; + w = (pages * RECT_UNIT_W * RECT_UNIT_H) / h; + w = (w / RECT_UNIT_W) * RECT_UNIT_W; + pages = (w * h) / (RECT_UNIT_W * RECT_UNIT_H); + KASSERT(pages != 0, ("r600_blit_create_rect: pages == 0")); + } + + + DRM_DEBUG("blit_rectangle: h=%d, w=%d, pages=%d\n", h, w, pages); + + /* return width and height only of the caller wants it */ + if (height) + *height = h; + if (width) + *width = w; + + return pages; +} + + +int r600_blit_prepare_copy(struct radeon_device *rdev, unsigned num_gpu_pages, + struct radeon_fence **fence, struct radeon_sa_bo **vb, + struct radeon_semaphore **sem) +{ + struct radeon_ring *ring = &rdev->ring[RADEON_RING_TYPE_GFX_INDEX]; + int r; + int ring_size; + int num_loops = 0; + int dwords_per_loop = rdev->r600_blit.ring_size_per_loop; + + /* num loops */ + while (num_gpu_pages) { + num_gpu_pages -= + r600_blit_create_rect(num_gpu_pages, NULL, NULL, + rdev->r600_blit.max_dim); + num_loops++; + } + + /* 48 bytes for vertex per loop */ + r = radeon_sa_bo_new(rdev, &rdev->ring_tmp_bo, vb, + (num_loops*48)+256, 256, true); + if (r) { + return r; + } + + r = radeon_semaphore_create(rdev, sem); + if (r) { + radeon_sa_bo_free(rdev, vb, NULL); + return r; + } + + /* calculate number of loops correctly */ + ring_size = num_loops * dwords_per_loop; + ring_size += rdev->r600_blit.ring_size_common; + r = radeon_ring_lock(rdev, ring, ring_size); + if (r) { + radeon_sa_bo_free(rdev, vb, NULL); + radeon_semaphore_free(rdev, sem, NULL); + return r; + } + + if (radeon_fence_need_sync(*fence, RADEON_RING_TYPE_GFX_INDEX)) { + radeon_semaphore_sync_rings(rdev, *sem, (*fence)->ring, + RADEON_RING_TYPE_GFX_INDEX); + radeon_fence_note_sync(*fence, RADEON_RING_TYPE_GFX_INDEX); + } else { + radeon_semaphore_free(rdev, sem, NULL); + } + + rdev->r600_blit.primitives.set_default_state(rdev); + rdev->r600_blit.primitives.set_shaders(rdev); + return 0; +} + +void r600_blit_done_copy(struct radeon_device *rdev, struct radeon_fence **fence, + struct radeon_sa_bo *vb, struct radeon_semaphore *sem) +{ + struct radeon_ring *ring = &rdev->ring[RADEON_RING_TYPE_GFX_INDEX]; + int r; + + r = radeon_fence_emit(rdev, fence, RADEON_RING_TYPE_GFX_INDEX); + if (r) { + radeon_ring_unlock_undo(rdev, ring); + return; + } + + radeon_ring_unlock_commit(rdev, ring); + radeon_sa_bo_free(rdev, &vb, *fence); + radeon_semaphore_free(rdev, &sem, *fence); +} + +void r600_kms_blit_copy(struct radeon_device *rdev, + u64 src_gpu_addr, u64 dst_gpu_addr, + unsigned num_gpu_pages, + struct radeon_sa_bo *vb) +{ + u64 vb_gpu_addr; + u32 *vb_cpu_addr; + + DRM_DEBUG("emitting copy %16jx %16jx %d\n", + (uintmax_t)src_gpu_addr, (uintmax_t)dst_gpu_addr, num_gpu_pages); + vb_cpu_addr = (u32 *)radeon_sa_bo_cpu_addr(vb); + vb_gpu_addr = radeon_sa_bo_gpu_addr(vb); + + while (num_gpu_pages) { + int w, h; + unsigned size_in_bytes; + unsigned pages_per_loop = + r600_blit_create_rect(num_gpu_pages, &w, &h, + rdev->r600_blit.max_dim); + + size_in_bytes = pages_per_loop * RADEON_GPU_PAGE_SIZE; + DRM_DEBUG("rectangle w=%d h=%d\n", w, h); + + vb_cpu_addr[0] = 0; + vb_cpu_addr[1] = 0; + vb_cpu_addr[2] = 0; + vb_cpu_addr[3] = 0; + + vb_cpu_addr[4] = 0; + vb_cpu_addr[5] = int2float(h); + vb_cpu_addr[6] = 0; + vb_cpu_addr[7] = int2float(h); + + vb_cpu_addr[8] = int2float(w); + vb_cpu_addr[9] = int2float(h); + vb_cpu_addr[10] = int2float(w); + vb_cpu_addr[11] = int2float(h); + + rdev->r600_blit.primitives.set_tex_resource(rdev, FMT_8_8_8_8, + w, h, w, src_gpu_addr, size_in_bytes); + rdev->r600_blit.primitives.set_render_target(rdev, COLOR_8_8_8_8, + w, h, dst_gpu_addr); + rdev->r600_blit.primitives.set_scissors(rdev, 0, 0, w, h); + rdev->r600_blit.primitives.set_vtx_resource(rdev, vb_gpu_addr); + rdev->r600_blit.primitives.draw_auto(rdev); + rdev->r600_blit.primitives.cp_set_surface_sync(rdev, + PACKET3_CB_ACTION_ENA | PACKET3_CB0_DEST_BASE_ENA, + size_in_bytes, dst_gpu_addr); + + vb_cpu_addr += 12; + vb_gpu_addr += 4*12; + src_gpu_addr += size_in_bytes; + dst_gpu_addr += size_in_bytes; + num_gpu_pages -= pages_per_loop; + } +} diff --git a/sys/dev/drm2/radeon/r600_blit_shaders.c b/sys/dev/drm2/radeon/r600_blit_shaders.c new file mode 100644 index 00000000000..1095a2c1213 --- /dev/null +++ b/sys/dev/drm2/radeon/r600_blit_shaders.c @@ -0,0 +1,720 @@ +/* + * Copyright 2009 Advanced Micro Devices, Inc. + * + * 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 (including the next + * paragraph) 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 COPYRIGHT HOLDER(S) AND/OR ITS SUPPLIERS 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. + * + * Authors: + * Alex Deucher + */ + +#include +__FBSDID("$FreeBSD$"); + +#include + +/* + * R6xx+ cards need to use the 3D engine to blit data which requires + * quite a bit of hw state setup. Rather than pull the whole 3D driver + * (which normally generates the 3D state) into the DRM, we opt to use + * statically generated state tables. The regsiter state and shaders + * were hand generated to support blitting functionality. See the 3D + * driver or documentation for descriptions of the registers and + * shader instructions. + */ + +const u32 r6xx_default_state[] = +{ + 0xc0002400, /* START_3D_CMDBUF */ + 0x00000000, + + 0xc0012800, /* CONTEXT_CONTROL */ + 0x80000000, + 0x80000000, + + 0xc0016800, + 0x00000010, + 0x00008000, /* WAIT_UNTIL */ + + 0xc0016800, + 0x00000542, + 0x07000003, /* TA_CNTL_AUX */ + + 0xc0016800, + 0x000005c5, + 0x00000000, /* VC_ENHANCE */ + + 0xc0016800, + 0x00000363, + 0x00000000, /* SQ_DYN_GPR_CNTL_PS_FLUSH_REQ */ + + 0xc0016800, + 0x0000060c, + 0x82000000, /* DB_DEBUG */ + + 0xc0016800, + 0x0000060e, + 0x01020204, /* DB_WATERMARKS */ + + 0xc0026f00, + 0x00000000, + 0x00000000, /* SQ_VTX_BASE_VTX_LOC */ + 0x00000000, /* SQ_VTX_START_INST_LOC */ + + 0xc0096900, + 0x0000022a, + 0x00000000, /* SQ_ESGS_RING_ITEMSIZE */ + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + + 0xc0016900, + 0x00000004, + 0x00000000, /* DB_DEPTH_INFO */ + + 0xc0026900, + 0x0000000a, + 0x00000000, /* DB_STENCIL_CLEAR */ + 0x00000000, /* DB_DEPTH_CLEAR */ + + 0xc0016900, + 0x00000200, + 0x00000000, /* DB_DEPTH_CONTROL */ + + 0xc0026900, + 0x00000343, + 0x00000060, /* DB_RENDER_CONTROL */ + 0x00000040, /* DB_RENDER_OVERRIDE */ + + 0xc0016900, + 0x00000351, + 0x0000aa00, /* DB_ALPHA_TO_MASK */ + + 0xc00f6900, + 0x00000100, + 0x00000800, /* VGT_MAX_VTX_INDX */ + 0x00000000, /* VGT_MIN_VTX_INDX */ + 0x00000000, /* VGT_INDX_OFFSET */ + 0x00000000, /* VGT_MULTI_PRIM_IB_RESET_INDX */ + 0x00000000, /* SX_ALPHA_TEST_CONTROL */ + 0x00000000, /* CB_BLEND_RED */ + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, /* CB_FOG_RED */ + 0x00000000, + 0x00000000, + 0x00000000, /* DB_STENCILREFMASK */ + 0x00000000, /* DB_STENCILREFMASK_BF */ + 0x00000000, /* SX_ALPHA_REF */ + + 0xc0046900, + 0x0000030c, + 0x01000000, /* CB_CLRCMP_CNTL */ + 0x00000000, + 0x00000000, + 0x00000000, + + 0xc0046900, + 0x00000048, + 0x3f800000, /* CB_CLEAR_RED */ + 0x00000000, + 0x3f800000, + 0x3f800000, + + 0xc0016900, + 0x00000080, + 0x00000000, /* PA_SC_WINDOW_OFFSET */ + + 0xc00a6900, + 0x00000083, + 0x0000ffff, /* PA_SC_CLIP_RECT_RULE */ + 0x00000000, /* PA_SC_CLIPRECT_0_TL */ + 0x20002000, + 0x00000000, + 0x20002000, + 0x00000000, + 0x20002000, + 0x00000000, + 0x20002000, + 0x00000000, /* PA_SC_EDGERULE */ + + 0xc0406900, + 0x00000094, + 0x80000000, /* PA_SC_VPORT_SCISSOR_0_TL */ + 0x20002000, /* PA_SC_VPORT_SCISSOR_0_BR */ + 0x80000000, /* PA_SC_VPORT_SCISSOR_1_TL */ + 0x20002000, + 0x80000000, + 0x20002000, + 0x80000000, + 0x20002000, + 0x80000000, + 0x20002000, + 0x80000000, + 0x20002000, + 0x80000000, + 0x20002000, + 0x80000000, + 0x20002000, + 0x80000000, + 0x20002000, + 0x80000000, + 0x20002000, + 0x80000000, + 0x20002000, + 0x80000000, + 0x20002000, + 0x80000000, + 0x20002000, + 0x80000000, + 0x20002000, + 0x80000000, + 0x20002000, + 0x80000000, + 0x20002000, + 0x00000000, /* PA_SC_VPORT_ZMIN_0 */ + 0x3f800000, + 0x00000000, + 0x3f800000, + 0x00000000, + 0x3f800000, + 0x00000000, + 0x3f800000, + 0x00000000, + 0x3f800000, + 0x00000000, + 0x3f800000, + 0x00000000, + 0x3f800000, + 0x00000000, + 0x3f800000, + 0x00000000, + 0x3f800000, + 0x00000000, + 0x3f800000, + 0x00000000, + 0x3f800000, + 0x00000000, + 0x3f800000, + 0x00000000, + 0x3f800000, + 0x00000000, + 0x3f800000, + 0x00000000, + 0x3f800000, + 0x00000000, + 0x3f800000, + + 0xc0026900, + 0x00000292, + 0x00000000, /* PA_SC_MPASS_PS_CNTL */ + 0x00004010, /* PA_SC_MODE_CNTL */ + + 0xc0096900, + 0x00000300, + 0x00000000, /* PA_SC_LINE_CNTL */ + 0x00000000, /* PA_SC_AA_CONFIG */ + 0x0000002d, /* PA_SU_VTX_CNTL */ + 0x3f800000, /* PA_CL_GB_VERT_CLIP_ADJ */ + 0x3f800000, + 0x3f800000, + 0x3f800000, + 0x00000000, /* PA_SC_SAMPLE_LOCS_MCTX */ + 0x00000000, + + 0xc0016900, + 0x00000312, + 0xffffffff, /* PA_SC_AA_MASK */ + + 0xc0066900, + 0x0000037e, + 0x00000000, /* PA_SU_POLY_OFFSET_DB_FMT_CNTL */ + 0x00000000, /* PA_SU_POLY_OFFSET_CLAMP */ + 0x00000000, /* PA_SU_POLY_OFFSET_FRONT_SCALE */ + 0x00000000, /* PA_SU_POLY_OFFSET_FRONT_OFFSET */ + 0x00000000, /* PA_SU_POLY_OFFSET_BACK_SCALE */ + 0x00000000, /* PA_SU_POLY_OFFSET_BACK_OFFSET */ + + 0xc0046900, + 0x000001b6, + 0x00000000, /* SPI_INPUT_Z */ + 0x00000000, /* SPI_FOG_CNTL */ + 0x00000000, /* SPI_FOG_FUNC_SCALE */ + 0x00000000, /* SPI_FOG_FUNC_BIAS */ + + 0xc0016900, + 0x00000225, + 0x00000000, /* SQ_PGM_START_FS */ + + 0xc0016900, + 0x00000229, + 0x00000000, /* SQ_PGM_RESOURCES_FS */ + + 0xc0016900, + 0x00000237, + 0x00000000, /* SQ_PGM_CF_OFFSET_FS */ + + 0xc0026900, + 0x000002a8, + 0x00000000, /* VGT_INSTANCE_STEP_RATE_0 */ + 0x00000000, /* VGT_INSTANCE_STEP_RATE_1 */ + + 0xc0116900, + 0x00000280, + 0x00000000, /* PA_SU_POINT_SIZE */ + 0x00000000, /* PA_SU_POINT_MINMAX */ + 0x00000008, /* PA_SU_LINE_CNTL */ + 0x00000000, /* PA_SC_LINE_STIPPLE */ + 0x00000000, /* VGT_OUTPUT_PATH_CNTL */ + 0x00000000, /* VGT_HOS_CNTL */ + 0x00000000, /* VGT_HOS_MAX_TESS_LEVEL */ + 0x00000000, /* VGT_HOS_MIN_TESS_LEVEL */ + 0x00000000, /* VGT_HOS_REUSE_DEPTH */ + 0x00000000, /* VGT_GROUP_PRIM_TYPE */ + 0x00000000, /* VGT_GROUP_FIRST_DECR */ + 0x00000000, /* VGT_GROUP_DECR */ + 0x00000000, /* VGT_GROUP_VECT_0_CNTL */ + 0x00000000, /* VGT_GROUP_VECT_1_CNTL */ + 0x00000000, /* VGT_GROUP_VECT_0_FMT_CNTL */ + 0x00000000, /* VGT_GROUP_VECT_1_FMT_CNTL */ + 0x00000000, /* VGT_GS_MODE */ + + 0xc0016900, + 0x000002a1, + 0x00000000, /* VGT_PRIMITIVEID_EN */ + + 0xc0016900, + 0x000002a5, + 0x00000000, /* VGT_MULTI_PRIM_ID_RESET_EN */ + + 0xc0036900, + 0x000002ac, + 0x00000000, /* VGT_STRMOUT_EN */ + 0x00000000, /* VGT_REUSE_OFF */ + 0x00000000, /* VGT_VTX_CNT_EN */ + + 0xc0016900, + 0x000000d4, + 0x00000000, /* SX_MISC */ + + 0xc0016900, + 0x000002c8, + 0x00000000, /* VGT_STRMOUT_BUFFER_EN */ + + 0xc0076900, + 0x00000202, + 0x00cc0000, /* CB_COLOR_CONTROL */ + 0x00000210, /* DB_SHADER_CNTL */ + 0x00010000, /* PA_CL_CLIP_CNTL */ + 0x00000244, /* PA_SU_SC_MODE_CNTL */ + 0x00000100, /* PA_CL_VTE_CNTL */ + 0x00000000, /* PA_CL_VS_OUT_CNTL */ + 0x00000000, /* PA_CL_NANINF_CNTL */ + + 0xc0026900, + 0x0000008e, + 0x0000000f, /* CB_TARGET_MASK */ + 0x0000000f, /* CB_SHADER_MASK */ + + 0xc0016900, + 0x000001e8, + 0x00000001, /* CB_SHADER_CONTROL */ + + 0xc0016900, + 0x00000185, + 0x00000000, /* SPI_VS_OUT_ID_0 */ + + 0xc0016900, + 0x00000191, + 0x00000b00, /* SPI_PS_INPUT_CNTL_0 */ + + 0xc0056900, + 0x000001b1, + 0x00000000, /* SPI_VS_OUT_CONFIG */ + 0x00000000, /* SPI_THREAD_GROUPING */ + 0x00000001, /* SPI_PS_IN_CONTROL_0 */ + 0x00000000, /* SPI_PS_IN_CONTROL_1 */ + 0x00000000, /* SPI_INTERP_CONTROL_0 */ + + 0xc0036e00, /* SET_SAMPLER */ + 0x00000000, + 0x00000012, + 0x00000000, + 0x00000000, +}; + +const u32 r7xx_default_state[] = +{ + 0xc0012800, /* CONTEXT_CONTROL */ + 0x80000000, + 0x80000000, + + 0xc0016800, + 0x00000010, + 0x00008000, /* WAIT_UNTIL */ + + 0xc0016800, + 0x00000542, + 0x07000002, /* TA_CNTL_AUX */ + + 0xc0016800, + 0x000005c5, + 0x00000000, /* VC_ENHANCE */ + + 0xc0016800, + 0x00000363, + 0x00004000, /* SQ_DYN_GPR_CNTL_PS_FLUSH_REQ */ + + 0xc0016800, + 0x0000060c, + 0x00000000, /* DB_DEBUG */ + + 0xc0016800, + 0x0000060e, + 0x00420204, /* DB_WATERMARKS */ + + 0xc0026f00, + 0x00000000, + 0x00000000, /* SQ_VTX_BASE_VTX_LOC */ + 0x00000000, /* SQ_VTX_START_INST_LOC */ + + 0xc0096900, + 0x0000022a, + 0x00000000, /* SQ_ESGS_RING_ITEMSIZE */ + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + + 0xc0016900, + 0x00000004, + 0x00000000, /* DB_DEPTH_INFO */ + + 0xc0026900, + 0x0000000a, + 0x00000000, /* DB_STENCIL_CLEAR */ + 0x00000000, /* DB_DEPTH_CLEAR */ + + 0xc0016900, + 0x00000200, + 0x00000000, /* DB_DEPTH_CONTROL */ + + 0xc0026900, + 0x00000343, + 0x00000060, /* DB_RENDER_CONTROL */ + 0x00000000, /* DB_RENDER_OVERRIDE */ + + 0xc0016900, + 0x00000351, + 0x0000aa00, /* DB_ALPHA_TO_MASK */ + + 0xc0096900, + 0x00000100, + 0x00000800, /* VGT_MAX_VTX_INDX */ + 0x00000000, /* VGT_MIN_VTX_INDX */ + 0x00000000, /* VGT_INDX_OFFSET */ + 0x00000000, /* VGT_MULTI_PRIM_IB_RESET_INDX */ + 0x00000000, /* SX_ALPHA_TEST_CONTROL */ + 0x00000000, /* CB_BLEND_RED */ + 0x00000000, + 0x00000000, + 0x00000000, + + 0xc0036900, + 0x0000010c, + 0x00000000, /* DB_STENCILREFMASK */ + 0x00000000, /* DB_STENCILREFMASK_BF */ + 0x00000000, /* SX_ALPHA_REF */ + + 0xc0046900, + 0x0000030c, /* CB_CLRCMP_CNTL */ + 0x01000000, + 0x00000000, + 0x00000000, + 0x00000000, + + 0xc0016900, + 0x00000080, + 0x00000000, /* PA_SC_WINDOW_OFFSET */ + + 0xc00a6900, + 0x00000083, + 0x0000ffff, /* PA_SC_CLIP_RECT_RULE */ + 0x00000000, /* PA_SC_CLIPRECT_0_TL */ + 0x20002000, + 0x00000000, + 0x20002000, + 0x00000000, + 0x20002000, + 0x00000000, + 0x20002000, + 0xaaaaaaaa, /* PA_SC_EDGERULE */ + + 0xc0406900, + 0x00000094, + 0x80000000, /* PA_SC_VPORT_SCISSOR_0_TL */ + 0x20002000, /* PA_SC_VPORT_SCISSOR_0_BR */ + 0x80000000, /* PA_SC_VPORT_SCISSOR_1_TL */ + 0x20002000, + 0x80000000, + 0x20002000, + 0x80000000, + 0x20002000, + 0x80000000, + 0x20002000, + 0x80000000, + 0x20002000, + 0x80000000, + 0x20002000, + 0x80000000, + 0x20002000, + 0x80000000, + 0x20002000, + 0x80000000, + 0x20002000, + 0x80000000, + 0x20002000, + 0x80000000, + 0x20002000, + 0x80000000, + 0x20002000, + 0x80000000, + 0x20002000, + 0x80000000, + 0x20002000, + 0x80000000, + 0x20002000, + 0x00000000, /* PA_SC_VPORT_ZMIN_0 */ + 0x3f800000, + 0x00000000, + 0x3f800000, + 0x00000000, + 0x3f800000, + 0x00000000, + 0x3f800000, + 0x00000000, + 0x3f800000, + 0x00000000, + 0x3f800000, + 0x00000000, + 0x3f800000, + 0x00000000, + 0x3f800000, + 0x00000000, + 0x3f800000, + 0x00000000, + 0x3f800000, + 0x00000000, + 0x3f800000, + 0x00000000, + 0x3f800000, + 0x00000000, + 0x3f800000, + 0x00000000, + 0x3f800000, + 0x00000000, + 0x3f800000, + 0x00000000, + 0x3f800000, + + 0xc0026900, + 0x00000292, + 0x00000000, /* PA_SC_MPASS_PS_CNTL */ + 0x00514000, /* PA_SC_MODE_CNTL */ + + 0xc0096900, + 0x00000300, + 0x00000000, /* PA_SC_LINE_CNTL */ + 0x00000000, /* PA_SC_AA_CONFIG */ + 0x0000002d, /* PA_SU_VTX_CNTL */ + 0x3f800000, /* PA_CL_GB_VERT_CLIP_ADJ */ + 0x3f800000, + 0x3f800000, + 0x3f800000, + 0x00000000, /* PA_SC_SAMPLE_LOCS_MCTX */ + 0x00000000, + + 0xc0016900, + 0x00000312, + 0xffffffff, /* PA_SC_AA_MASK */ + + 0xc0066900, + 0x0000037e, + 0x00000000, /* PA_SU_POLY_OFFSET_DB_FMT_CNTL */ + 0x00000000, /* PA_SU_POLY_OFFSET_CLAMP */ + 0x00000000, /* PA_SU_POLY_OFFSET_FRONT_SCALE */ + 0x00000000, /* PA_SU_POLY_OFFSET_FRONT_OFFSET */ + 0x00000000, /* PA_SU_POLY_OFFSET_BACK_SCALE */ + 0x00000000, /* PA_SU_POLY_OFFSET_BACK_OFFSET */ + + 0xc0046900, + 0x000001b6, + 0x00000000, /* SPI_INPUT_Z */ + 0x00000000, /* SPI_FOG_CNTL */ + 0x00000000, /* SPI_FOG_FUNC_SCALE */ + 0x00000000, /* SPI_FOG_FUNC_BIAS */ + + 0xc0016900, + 0x00000225, + 0x00000000, /* SQ_PGM_START_FS */ + + 0xc0016900, + 0x00000229, + 0x00000000, /* SQ_PGM_RESOURCES_FS */ + + 0xc0016900, + 0x00000237, + 0x00000000, /* SQ_PGM_CF_OFFSET_FS */ + + 0xc0026900, + 0x000002a8, + 0x00000000, /* VGT_INSTANCE_STEP_RATE_0 */ + 0x00000000, /* VGT_INSTANCE_STEP_RATE_1 */ + + 0xc0116900, + 0x00000280, + 0x00000000, /* PA_SU_POINT_SIZE */ + 0x00000000, /* PA_SU_POINT_MINMAX */ + 0x00000008, /* PA_SU_LINE_CNTL */ + 0x00000000, /* PA_SC_LINE_STIPPLE */ + 0x00000000, /* VGT_OUTPUT_PATH_CNTL */ + 0x00000000, /* VGT_HOS_CNTL */ + 0x00000000, /* VGT_HOS_MAX_TESS_LEVEL */ + 0x00000000, /* VGT_HOS_MIN_TESS_LEVEL */ + 0x00000000, /* VGT_HOS_REUSE_DEPTH */ + 0x00000000, /* VGT_GROUP_PRIM_TYPE */ + 0x00000000, /* VGT_GROUP_FIRST_DECR */ + 0x00000000, /* VGT_GROUP_DECR */ + 0x00000000, /* VGT_GROUP_VECT_0_CNTL */ + 0x00000000, /* VGT_GROUP_VECT_1_CNTL */ + 0x00000000, /* VGT_GROUP_VECT_0_FMT_CNTL */ + 0x00000000, /* VGT_GROUP_VECT_1_FMT_CNTL */ + 0x00000000, /* VGT_GS_MODE */ + + 0xc0016900, + 0x000002a1, + 0x00000000, /* VGT_PRIMITIVEID_EN */ + + 0xc0016900, + 0x000002a5, + 0x00000000, /* VGT_MULTI_PRIM_ID_RESET_EN */ + + 0xc0036900, + 0x000002ac, + 0x00000000, /* VGT_STRMOUT_EN */ + 0x00000000, /* VGT_REUSE_OFF */ + 0x00000000, /* VGT_VTX_CNT_EN */ + + 0xc0016900, + 0x000000d4, + 0x00000000, /* SX_MISC */ + + 0xc0016900, + 0x000002c8, + 0x00000000, /* VGT_STRMOUT_BUFFER_EN */ + + 0xc0076900, + 0x00000202, + 0x00cc0000, /* CB_COLOR_CONTROL */ + 0x00000210, /* DB_SHADER_CNTL */ + 0x00010000, /* PA_CL_CLIP_CNTL */ + 0x00000244, /* PA_SU_SC_MODE_CNTL */ + 0x00000100, /* PA_CL_VTE_CNTL */ + 0x00000000, /* PA_CL_VS_OUT_CNTL */ + 0x00000000, /* PA_CL_NANINF_CNTL */ + + 0xc0026900, + 0x0000008e, + 0x0000000f, /* CB_TARGET_MASK */ + 0x0000000f, /* CB_SHADER_MASK */ + + 0xc0016900, + 0x000001e8, + 0x00000001, /* CB_SHADER_CONTROL */ + + 0xc0016900, + 0x00000185, + 0x00000000, /* SPI_VS_OUT_ID_0 */ + + 0xc0016900, + 0x00000191, + 0x00000b00, /* SPI_PS_INPUT_CNTL_0 */ + + 0xc0056900, + 0x000001b1, + 0x00000000, /* SPI_VS_OUT_CONFIG */ + 0x00000001, /* SPI_THREAD_GROUPING */ + 0x00000001, /* SPI_PS_IN_CONTROL_0 */ + 0x00000000, /* SPI_PS_IN_CONTROL_1 */ + 0x00000000, /* SPI_INTERP_CONTROL_0 */ + + 0xc0036e00, /* SET_SAMPLER */ + 0x00000000, + 0x00000012, + 0x00000000, + 0x00000000, +}; + +/* same for r6xx/r7xx */ +const u32 r6xx_vs[] = +{ + 0x00000004, + 0x81000000, + 0x0000203c, + 0x94000b08, + 0x00004000, + 0x14200b1a, + 0x00000000, + 0x00000000, + 0x3c000000, + 0x68cd1000, +#ifdef __BIG_ENDIAN + 0x000a0000, +#else + 0x00080000, +#endif + 0x00000000, +}; + +const u32 r6xx_ps[] = +{ + 0x00000002, + 0x80800000, + 0x00000000, + 0x94200688, + 0x00000010, + 0x000d1000, + 0xb0800000, + 0x00000000, +}; + +const u32 r6xx_ps_size = DRM_ARRAY_SIZE(r6xx_ps); +const u32 r6xx_vs_size = DRM_ARRAY_SIZE(r6xx_vs); +const u32 r6xx_default_size = DRM_ARRAY_SIZE(r6xx_default_state); +const u32 r7xx_default_size = DRM_ARRAY_SIZE(r7xx_default_state); diff --git a/sys/dev/drm2/radeon/r600_blit_shaders.h b/sys/dev/drm2/radeon/r600_blit_shaders.h new file mode 100644 index 00000000000..591b756d2b3 --- /dev/null +++ b/sys/dev/drm2/radeon/r600_blit_shaders.h @@ -0,0 +1,42 @@ +/* + * Copyright 2009 Advanced Micro Devices, Inc. + * Copyright 2009 Red Hat Inc. + * + * 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 (including the next + * paragraph) 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 COPYRIGHT HOLDER(S) AND/OR ITS SUPPLIERS 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 R600_BLIT_SHADERS_H +#define R600_BLIT_SHADERS_H + +#include +__FBSDID("$FreeBSD$"); + +extern const u32 r6xx_ps[]; +extern const u32 r6xx_vs[]; +extern const u32 r7xx_default_state[]; +extern const u32 r6xx_default_state[]; + + +extern const u32 r6xx_ps_size, r6xx_vs_size; +extern const u32 r6xx_default_size, r7xx_default_size; + +__pure uint32_t int2float(uint32_t x); +#endif diff --git a/sys/dev/drm2/radeon/r600_cp.c b/sys/dev/drm2/radeon/r600_cp.c new file mode 100644 index 00000000000..2907bdb2fbe --- /dev/null +++ b/sys/dev/drm2/radeon/r600_cp.c @@ -0,0 +1,2645 @@ +/* + * Copyright 2008-2009 Advanced Micro Devices, Inc. + * Copyright 2008 Red Hat Inc. + * + * 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 (including the next + * paragraph) 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 COPYRIGHT HOLDER(S) AND/OR ITS SUPPLIERS 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. + * + * Authors: + * Dave Airlie + * Alex Deucher + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include + +#include +#include +#include "radeon_drv.h" +#include "r600_cp.h" + +#define PFP_UCODE_SIZE 576 +#define PM4_UCODE_SIZE 1792 +#define R700_PFP_UCODE_SIZE 848 +#define R700_PM4_UCODE_SIZE 1360 + +# define ATI_PCIGART_PAGE_SIZE 4096 /**< PCI GART page size */ +# define ATI_PCIGART_PAGE_MASK (~(ATI_PCIGART_PAGE_SIZE-1)) + +#define R600_PTE_VALID (1 << 0) +#define R600_PTE_SYSTEM (1 << 1) +#define R600_PTE_SNOOPED (1 << 2) +#define R600_PTE_READABLE (1 << 5) +#define R600_PTE_WRITEABLE (1 << 6) + +/* MAX values used for gfx init */ +#define R6XX_MAX_SH_GPRS 256 +#define R6XX_MAX_TEMP_GPRS 16 +#define R6XX_MAX_SH_THREADS 256 +#define R6XX_MAX_SH_STACK_ENTRIES 4096 +#define R6XX_MAX_BACKENDS 8 +#define R6XX_MAX_BACKENDS_MASK 0xff +#define R6XX_MAX_SIMDS 8 +#define R6XX_MAX_SIMDS_MASK 0xff +#define R6XX_MAX_PIPES 8 +#define R6XX_MAX_PIPES_MASK 0xff + +#define R7XX_MAX_SH_GPRS 256 +#define R7XX_MAX_TEMP_GPRS 16 +#define R7XX_MAX_SH_THREADS 256 +#define R7XX_MAX_SH_STACK_ENTRIES 4096 +#define R7XX_MAX_BACKENDS 8 +#define R7XX_MAX_BACKENDS_MASK 0xff +#define R7XX_MAX_SIMDS 16 +#define R7XX_MAX_SIMDS_MASK 0xffff +#define R7XX_MAX_PIPES 8 +#define R7XX_MAX_PIPES_MASK 0xff + +static int r600_do_wait_for_fifo(drm_radeon_private_t *dev_priv, int entries) +{ + int i; + + dev_priv->stats.boxes |= RADEON_BOX_WAIT_IDLE; + + for (i = 0; i < dev_priv->usec_timeout; i++) { + int slots; + if ((dev_priv->flags & RADEON_FAMILY_MASK) >= CHIP_RV770) + slots = (RADEON_READ(R600_GRBM_STATUS) + & R700_CMDFIFO_AVAIL_MASK); + else + slots = (RADEON_READ(R600_GRBM_STATUS) + & R600_CMDFIFO_AVAIL_MASK); + if (slots >= entries) + return 0; + DRM_UDELAY(1); + } + DRM_INFO("wait for fifo failed status : 0x%08X 0x%08X\n", + RADEON_READ(R600_GRBM_STATUS), + RADEON_READ(R600_GRBM_STATUS2)); + + return -EBUSY; +} + +static int r600_do_wait_for_idle(drm_radeon_private_t *dev_priv) +{ + int i, ret; + + dev_priv->stats.boxes |= RADEON_BOX_WAIT_IDLE; + + if ((dev_priv->flags & RADEON_FAMILY_MASK) >= CHIP_RV770) + ret = r600_do_wait_for_fifo(dev_priv, 8); + else + ret = r600_do_wait_for_fifo(dev_priv, 16); + if (ret) + return ret; + for (i = 0; i < dev_priv->usec_timeout; i++) { + if (!(RADEON_READ(R600_GRBM_STATUS) & R600_GUI_ACTIVE)) + return 0; + DRM_UDELAY(1); + } + DRM_INFO("wait idle failed status : 0x%08X 0x%08X\n", + RADEON_READ(R600_GRBM_STATUS), + RADEON_READ(R600_GRBM_STATUS2)); + + return -EBUSY; +} + +void r600_page_table_cleanup(struct drm_device *dev, struct drm_ati_pcigart_info *gart_info) +{ + struct drm_sg_mem *entry = dev->sg; +#ifdef __linux__ + int max_pages; + int pages; + int i; +#endif + + if (!entry) + return; + + if (gart_info->bus_addr) { +#ifdef __linux__ + max_pages = (gart_info->table_size / sizeof(u64)); + pages = (entry->pages <= max_pages) + ? entry->pages : max_pages; + + for (i = 0; i < pages; i++) { + if (!entry->busaddr[i]) + break; + pci_unmap_page(dev->pdev, entry->busaddr[i], + PAGE_SIZE, PCI_DMA_BIDIRECTIONAL); + } +#endif + if (gart_info->gart_table_location == DRM_ATI_GART_MAIN) + gart_info->bus_addr = 0; + } +} + +/* R600 has page table setup */ +int r600_page_table_init(struct drm_device *dev) +{ + drm_radeon_private_t *dev_priv = dev->dev_private; + struct drm_ati_pcigart_info *gart_info = &dev_priv->gart_info; + struct drm_local_map *map = &gart_info->mapping; + struct drm_sg_mem *entry = dev->sg; + int ret = 0; + int i, j; + int pages; + u64 page_base; + dma_addr_t entry_addr; + int max_ati_pages, max_real_pages, gart_idx; + + /* okay page table is available - lets rock */ + max_ati_pages = (gart_info->table_size / sizeof(u64)); + max_real_pages = max_ati_pages / (PAGE_SIZE / ATI_PCIGART_PAGE_SIZE); + + pages = (entry->pages <= max_real_pages) ? + entry->pages : max_real_pages; + + memset_io((void __iomem *)map->handle, 0, max_ati_pages * sizeof(u64)); + + gart_idx = 0; + for (i = 0; i < pages; i++) { +#ifdef __linux__ + entry->busaddr[i] = pci_map_page(dev->pdev, + entry->pagelist[i], 0, + PAGE_SIZE, + PCI_DMA_BIDIRECTIONAL); + if (pci_dma_mapping_error(dev->pdev, entry->busaddr[i])) { + DRM_ERROR("unable to map PCIGART pages!\n"); + r600_page_table_cleanup(dev, gart_info); + goto done; + } +#endif + entry_addr = entry->busaddr[i]; + for (j = 0; j < (PAGE_SIZE / ATI_PCIGART_PAGE_SIZE); j++) { + page_base = (u64) entry_addr & ATI_PCIGART_PAGE_MASK; + page_base |= R600_PTE_VALID | R600_PTE_SYSTEM | R600_PTE_SNOOPED; + page_base |= R600_PTE_READABLE | R600_PTE_WRITEABLE; + + DRM_WRITE64(map, gart_idx * sizeof(u64), page_base); + + gart_idx++; + + if ((i % 128) == 0) + DRM_DEBUG("page entry %d: 0x%016llx\n", + i, (unsigned long long)page_base); + entry_addr += ATI_PCIGART_PAGE_SIZE; + } + } + ret = 1; +#ifdef __linux__ +done: +#endif + return ret; +} + +static void r600_vm_flush_gart_range(struct drm_device *dev) +{ + drm_radeon_private_t *dev_priv = dev->dev_private; + u32 resp, countdown = 1000; + RADEON_WRITE(R600_VM_CONTEXT0_INVALIDATION_LOW_ADDR, dev_priv->gart_vm_start >> 12); + RADEON_WRITE(R600_VM_CONTEXT0_INVALIDATION_HIGH_ADDR, (dev_priv->gart_vm_start + dev_priv->gart_size - 1) >> 12); + RADEON_WRITE(R600_VM_CONTEXT0_REQUEST_RESPONSE, 2); + + do { + resp = RADEON_READ(R600_VM_CONTEXT0_REQUEST_RESPONSE); + countdown--; + DRM_UDELAY(1); + } while (((resp & 0xf0) == 0) && countdown); +} + +static void r600_vm_init(struct drm_device *dev) +{ + drm_radeon_private_t *dev_priv = dev->dev_private; + /* initialise the VM to use the page table we constructed up there */ + u32 vm_c0, i; + u32 mc_rd_a; + u32 vm_l2_cntl, vm_l2_cntl3; + /* okay set up the PCIE aperture type thingo */ + RADEON_WRITE(R600_MC_VM_SYSTEM_APERTURE_LOW_ADDR, dev_priv->gart_vm_start >> 12); + RADEON_WRITE(R600_MC_VM_SYSTEM_APERTURE_HIGH_ADDR, (dev_priv->gart_vm_start + dev_priv->gart_size - 1) >> 12); + RADEON_WRITE(R600_MC_VM_SYSTEM_APERTURE_DEFAULT_ADDR, 0); + + /* setup MC RD a */ + mc_rd_a = R600_MCD_L1_TLB | R600_MCD_L1_FRAG_PROC | R600_MCD_SYSTEM_ACCESS_MODE_IN_SYS | + R600_MCD_SYSTEM_APERTURE_UNMAPPED_ACCESS_PASS_THRU | R600_MCD_EFFECTIVE_L1_TLB_SIZE(5) | + R600_MCD_EFFECTIVE_L1_QUEUE_SIZE(5) | R600_MCD_WAIT_L2_QUERY; + + RADEON_WRITE(R600_MCD_RD_A_CNTL, mc_rd_a); + RADEON_WRITE(R600_MCD_RD_B_CNTL, mc_rd_a); + + RADEON_WRITE(R600_MCD_WR_A_CNTL, mc_rd_a); + RADEON_WRITE(R600_MCD_WR_B_CNTL, mc_rd_a); + + RADEON_WRITE(R600_MCD_RD_GFX_CNTL, mc_rd_a); + RADEON_WRITE(R600_MCD_WR_GFX_CNTL, mc_rd_a); + + RADEON_WRITE(R600_MCD_RD_SYS_CNTL, mc_rd_a); + RADEON_WRITE(R600_MCD_WR_SYS_CNTL, mc_rd_a); + + RADEON_WRITE(R600_MCD_RD_HDP_CNTL, mc_rd_a | R600_MCD_L1_STRICT_ORDERING); + RADEON_WRITE(R600_MCD_WR_HDP_CNTL, mc_rd_a /*| R600_MCD_L1_STRICT_ORDERING*/); + + RADEON_WRITE(R600_MCD_RD_PDMA_CNTL, mc_rd_a); + RADEON_WRITE(R600_MCD_WR_PDMA_CNTL, mc_rd_a); + + RADEON_WRITE(R600_MCD_RD_SEM_CNTL, mc_rd_a | R600_MCD_SEMAPHORE_MODE); + RADEON_WRITE(R600_MCD_WR_SEM_CNTL, mc_rd_a); + + vm_l2_cntl = R600_VM_L2_CACHE_EN | R600_VM_L2_FRAG_PROC | R600_VM_ENABLE_PTE_CACHE_LRU_W; + vm_l2_cntl |= R600_VM_L2_CNTL_QUEUE_SIZE(7); + RADEON_WRITE(R600_VM_L2_CNTL, vm_l2_cntl); + + RADEON_WRITE(R600_VM_L2_CNTL2, 0); + vm_l2_cntl3 = (R600_VM_L2_CNTL3_BANK_SELECT_0(0) | + R600_VM_L2_CNTL3_BANK_SELECT_1(1) | + R600_VM_L2_CNTL3_CACHE_UPDATE_MODE(2)); + RADEON_WRITE(R600_VM_L2_CNTL3, vm_l2_cntl3); + + vm_c0 = R600_VM_ENABLE_CONTEXT | R600_VM_PAGE_TABLE_DEPTH_FLAT; + + RADEON_WRITE(R600_VM_CONTEXT0_CNTL, vm_c0); + + vm_c0 &= ~R600_VM_ENABLE_CONTEXT; + + /* disable all other contexts */ + for (i = 1; i < 8; i++) + RADEON_WRITE(R600_VM_CONTEXT0_CNTL + (i * 4), vm_c0); + + RADEON_WRITE(R600_VM_CONTEXT0_PAGE_TABLE_BASE_ADDR, dev_priv->gart_info.bus_addr >> 12); + RADEON_WRITE(R600_VM_CONTEXT0_PAGE_TABLE_START_ADDR, dev_priv->gart_vm_start >> 12); + RADEON_WRITE(R600_VM_CONTEXT0_PAGE_TABLE_END_ADDR, (dev_priv->gart_vm_start + dev_priv->gart_size - 1) >> 12); + + r600_vm_flush_gart_range(dev); +} + +static int r600_cp_init_microcode(drm_radeon_private_t *dev_priv) +{ + const char *chip_name; + size_t pfp_req_size, me_req_size; + char fw_name[30]; + int err; + + switch (dev_priv->flags & RADEON_FAMILY_MASK) { + case CHIP_R600: chip_name = "R600"; break; + case CHIP_RV610: chip_name = "RV610"; break; + case CHIP_RV630: chip_name = "RV630"; break; + case CHIP_RV620: chip_name = "RV620"; break; + case CHIP_RV635: chip_name = "RV635"; break; + case CHIP_RV670: chip_name = "RV670"; break; + case CHIP_RS780: + case CHIP_RS880: chip_name = "RS780"; break; + case CHIP_RV770: chip_name = "RV770"; break; + case CHIP_RV730: + case CHIP_RV740: chip_name = "RV730"; break; + case CHIP_RV710: chip_name = "RV710"; break; + default: panic("%s: Unsupported family %d", __func__, dev_priv->flags & RADEON_FAMILY_MASK); + } + + if ((dev_priv->flags & RADEON_FAMILY_MASK) >= CHIP_RV770) { + pfp_req_size = R700_PFP_UCODE_SIZE * 4; + me_req_size = R700_PM4_UCODE_SIZE * 4; + } else { + pfp_req_size = PFP_UCODE_SIZE * 4; + me_req_size = PM4_UCODE_SIZE * 12; + } + + DRM_INFO("Loading %s CP Microcode\n", chip_name); + err = 0; + + snprintf(fw_name, sizeof(fw_name), "radeonkmsfw_%s_pfp", chip_name); + dev_priv->pfp_fw = firmware_get(fw_name); + if (dev_priv->pfp_fw == NULL) { + err = -ENOENT; + goto out; + } + if (dev_priv->pfp_fw->datasize != pfp_req_size) { + DRM_ERROR( + "r600_cp: Bogus length %zu in firmware \"%s\"\n", + dev_priv->pfp_fw->datasize, fw_name); + err = -EINVAL; + goto out; + } + + snprintf(fw_name, sizeof(fw_name), "radeonkmsfw_%s_me", chip_name); + dev_priv->me_fw = firmware_get(fw_name); + if (dev_priv->me_fw == NULL) { + err = -ENOENT; + goto out; + } + if (dev_priv->me_fw->datasize != me_req_size) { + DRM_ERROR( + "r600_cp: Bogus length %zu in firmware \"%s\"\n", + dev_priv->me_fw->datasize, fw_name); + err = -EINVAL; + } +out: + if (err) { + if (err != -EINVAL) + DRM_ERROR( + "r600_cp: Failed to load firmware \"%s\"\n", + fw_name); + if (dev_priv->pfp_fw != NULL) { + firmware_put(dev_priv->pfp_fw, FIRMWARE_UNLOAD); + dev_priv->pfp_fw = NULL; + } + if (dev_priv->me_fw != NULL) { + firmware_put(dev_priv->me_fw, FIRMWARE_UNLOAD); + dev_priv->me_fw = NULL; + } + } + return err; +} + +static void r600_cp_load_microcode(drm_radeon_private_t *dev_priv) +{ + const __be32 *fw_data; + int i; + + if (!dev_priv->me_fw || !dev_priv->pfp_fw) + return; + + r600_do_cp_stop(dev_priv); + + RADEON_WRITE(R600_CP_RB_CNTL, +#ifdef __BIG_ENDIAN + R600_BUF_SWAP_32BIT | +#endif + R600_RB_NO_UPDATE | + R600_RB_BLKSZ(15) | + R600_RB_BUFSZ(3)); + + RADEON_WRITE(R600_GRBM_SOFT_RESET, R600_SOFT_RESET_CP); + RADEON_READ(R600_GRBM_SOFT_RESET); + DRM_MDELAY(15); + RADEON_WRITE(R600_GRBM_SOFT_RESET, 0); + + fw_data = (const __be32 *)dev_priv->me_fw->data; + RADEON_WRITE(R600_CP_ME_RAM_WADDR, 0); + for (i = 0; i < PM4_UCODE_SIZE * 3; i++) + RADEON_WRITE(R600_CP_ME_RAM_DATA, + be32_to_cpup(fw_data++)); + + fw_data = (const __be32 *)dev_priv->pfp_fw->data; + RADEON_WRITE(R600_CP_PFP_UCODE_ADDR, 0); + for (i = 0; i < PFP_UCODE_SIZE; i++) + RADEON_WRITE(R600_CP_PFP_UCODE_DATA, + be32_to_cpup(fw_data++)); + + RADEON_WRITE(R600_CP_PFP_UCODE_ADDR, 0); + RADEON_WRITE(R600_CP_ME_RAM_WADDR, 0); + RADEON_WRITE(R600_CP_ME_RAM_RADDR, 0); + +} + +static void r700_vm_init(struct drm_device *dev) +{ + drm_radeon_private_t *dev_priv = dev->dev_private; + /* initialise the VM to use the page table we constructed up there */ + u32 vm_c0, i; + u32 mc_vm_md_l1; + u32 vm_l2_cntl, vm_l2_cntl3; + /* okay set up the PCIE aperture type thingo */ + RADEON_WRITE(R700_MC_VM_SYSTEM_APERTURE_LOW_ADDR, dev_priv->gart_vm_start >> 12); + RADEON_WRITE(R700_MC_VM_SYSTEM_APERTURE_HIGH_ADDR, (dev_priv->gart_vm_start + dev_priv->gart_size - 1) >> 12); + RADEON_WRITE(R700_MC_VM_SYSTEM_APERTURE_DEFAULT_ADDR, 0); + + mc_vm_md_l1 = R700_ENABLE_L1_TLB | + R700_ENABLE_L1_FRAGMENT_PROCESSING | + R700_SYSTEM_ACCESS_MODE_IN_SYS | + R700_SYSTEM_APERTURE_UNMAPPED_ACCESS_PASS_THRU | + R700_EFFECTIVE_L1_TLB_SIZE(5) | + R700_EFFECTIVE_L1_QUEUE_SIZE(5); + + RADEON_WRITE(R700_MC_VM_MD_L1_TLB0_CNTL, mc_vm_md_l1); + RADEON_WRITE(R700_MC_VM_MD_L1_TLB1_CNTL, mc_vm_md_l1); + RADEON_WRITE(R700_MC_VM_MD_L1_TLB2_CNTL, mc_vm_md_l1); + RADEON_WRITE(R700_MC_VM_MB_L1_TLB0_CNTL, mc_vm_md_l1); + RADEON_WRITE(R700_MC_VM_MB_L1_TLB1_CNTL, mc_vm_md_l1); + RADEON_WRITE(R700_MC_VM_MB_L1_TLB2_CNTL, mc_vm_md_l1); + RADEON_WRITE(R700_MC_VM_MB_L1_TLB3_CNTL, mc_vm_md_l1); + + vm_l2_cntl = R600_VM_L2_CACHE_EN | R600_VM_L2_FRAG_PROC | R600_VM_ENABLE_PTE_CACHE_LRU_W; + vm_l2_cntl |= R700_VM_L2_CNTL_QUEUE_SIZE(7); + RADEON_WRITE(R600_VM_L2_CNTL, vm_l2_cntl); + + RADEON_WRITE(R600_VM_L2_CNTL2, 0); + vm_l2_cntl3 = R700_VM_L2_CNTL3_BANK_SELECT(0) | R700_VM_L2_CNTL3_CACHE_UPDATE_MODE(2); + RADEON_WRITE(R600_VM_L2_CNTL3, vm_l2_cntl3); + + vm_c0 = R600_VM_ENABLE_CONTEXT | R600_VM_PAGE_TABLE_DEPTH_FLAT; + + RADEON_WRITE(R600_VM_CONTEXT0_CNTL, vm_c0); + + vm_c0 &= ~R600_VM_ENABLE_CONTEXT; + + /* disable all other contexts */ + for (i = 1; i < 8; i++) + RADEON_WRITE(R600_VM_CONTEXT0_CNTL + (i * 4), vm_c0); + + RADEON_WRITE(R700_VM_CONTEXT0_PAGE_TABLE_BASE_ADDR, dev_priv->gart_info.bus_addr >> 12); + RADEON_WRITE(R700_VM_CONTEXT0_PAGE_TABLE_START_ADDR, dev_priv->gart_vm_start >> 12); + RADEON_WRITE(R700_VM_CONTEXT0_PAGE_TABLE_END_ADDR, (dev_priv->gart_vm_start + dev_priv->gart_size - 1) >> 12); + + r600_vm_flush_gart_range(dev); +} + +static void r700_cp_load_microcode(drm_radeon_private_t *dev_priv) +{ + const __be32 *fw_data; + int i; + + if (!dev_priv->me_fw || !dev_priv->pfp_fw) + return; + + r600_do_cp_stop(dev_priv); + + RADEON_WRITE(R600_CP_RB_CNTL, +#ifdef __BIG_ENDIAN + R600_BUF_SWAP_32BIT | +#endif + R600_RB_NO_UPDATE | + R600_RB_BLKSZ(15) | + R600_RB_BUFSZ(3)); + + RADEON_WRITE(R600_GRBM_SOFT_RESET, R600_SOFT_RESET_CP); + RADEON_READ(R600_GRBM_SOFT_RESET); + DRM_MDELAY(15); + RADEON_WRITE(R600_GRBM_SOFT_RESET, 0); + + fw_data = (const __be32 *)dev_priv->pfp_fw->data; + RADEON_WRITE(R600_CP_PFP_UCODE_ADDR, 0); + for (i = 0; i < R700_PFP_UCODE_SIZE; i++) + RADEON_WRITE(R600_CP_PFP_UCODE_DATA, be32_to_cpup(fw_data++)); + RADEON_WRITE(R600_CP_PFP_UCODE_ADDR, 0); + + fw_data = (const __be32 *)dev_priv->me_fw->data; + RADEON_WRITE(R600_CP_ME_RAM_WADDR, 0); + for (i = 0; i < R700_PM4_UCODE_SIZE; i++) + RADEON_WRITE(R600_CP_ME_RAM_DATA, be32_to_cpup(fw_data++)); + RADEON_WRITE(R600_CP_ME_RAM_WADDR, 0); + + RADEON_WRITE(R600_CP_PFP_UCODE_ADDR, 0); + RADEON_WRITE(R600_CP_ME_RAM_WADDR, 0); + RADEON_WRITE(R600_CP_ME_RAM_RADDR, 0); + +} + +static void r600_test_writeback(drm_radeon_private_t *dev_priv) +{ + u32 tmp; + + /* Start with assuming that writeback doesn't work */ + dev_priv->writeback_works = 0; + + /* Writeback doesn't seem to work everywhere, test it here and possibly + * enable it if it appears to work + */ + radeon_write_ring_rptr(dev_priv, R600_SCRATCHOFF(1), 0); + + RADEON_WRITE(R600_SCRATCH_REG1, 0xdeadbeef); + + for (tmp = 0; tmp < dev_priv->usec_timeout; tmp++) { + u32 val; + + val = radeon_read_ring_rptr(dev_priv, R600_SCRATCHOFF(1)); + if (val == 0xdeadbeef) + break; + DRM_UDELAY(1); + } + + if (tmp < dev_priv->usec_timeout) { + dev_priv->writeback_works = 1; + DRM_INFO("writeback test succeeded in %d usecs\n", tmp); + } else { + dev_priv->writeback_works = 0; + DRM_INFO("writeback test failed\n"); + } + if (radeon_no_wb == 1) { + dev_priv->writeback_works = 0; + DRM_INFO("writeback forced off\n"); + } + + if (!dev_priv->writeback_works) { + /* Disable writeback to avoid unnecessary bus master transfer */ + RADEON_WRITE(R600_CP_RB_CNTL, +#ifdef __BIG_ENDIAN + R600_BUF_SWAP_32BIT | +#endif + RADEON_READ(R600_CP_RB_CNTL) | + R600_RB_NO_UPDATE); + RADEON_WRITE(R600_SCRATCH_UMSK, 0); + } +} + +int r600_do_engine_reset(struct drm_device *dev) +{ + drm_radeon_private_t *dev_priv = dev->dev_private; + u32 cp_ptr, cp_me_cntl, cp_rb_cntl; + + DRM_INFO("Resetting GPU\n"); + + cp_ptr = RADEON_READ(R600_CP_RB_WPTR); + cp_me_cntl = RADEON_READ(R600_CP_ME_CNTL); + RADEON_WRITE(R600_CP_ME_CNTL, R600_CP_ME_HALT); + + RADEON_WRITE(R600_GRBM_SOFT_RESET, 0x7fff); + RADEON_READ(R600_GRBM_SOFT_RESET); + DRM_UDELAY(50); + RADEON_WRITE(R600_GRBM_SOFT_RESET, 0); + RADEON_READ(R600_GRBM_SOFT_RESET); + + RADEON_WRITE(R600_CP_RB_WPTR_DELAY, 0); + cp_rb_cntl = RADEON_READ(R600_CP_RB_CNTL); + RADEON_WRITE(R600_CP_RB_CNTL, +#ifdef __BIG_ENDIAN + R600_BUF_SWAP_32BIT | +#endif + R600_RB_RPTR_WR_ENA); + + RADEON_WRITE(R600_CP_RB_RPTR_WR, cp_ptr); + RADEON_WRITE(R600_CP_RB_WPTR, cp_ptr); + RADEON_WRITE(R600_CP_RB_CNTL, cp_rb_cntl); + RADEON_WRITE(R600_CP_ME_CNTL, cp_me_cntl); + + /* Reset the CP ring */ + r600_do_cp_reset(dev_priv); + + /* The CP is no longer running after an engine reset */ + dev_priv->cp_running = 0; + + /* Reset any pending vertex, indirect buffers */ + radeon_freelist_reset(dev); + + return 0; + +} + +static u32 r600_get_tile_pipe_to_backend_map(u32 num_tile_pipes, + u32 num_backends, + u32 backend_disable_mask) +{ + u32 backend_map = 0; + u32 enabled_backends_mask; + u32 enabled_backends_count; + u32 cur_pipe; + u32 swizzle_pipe[R6XX_MAX_PIPES]; + u32 cur_backend; + u32 i; + + if (num_tile_pipes > R6XX_MAX_PIPES) + num_tile_pipes = R6XX_MAX_PIPES; + if (num_tile_pipes < 1) + num_tile_pipes = 1; + if (num_backends > R6XX_MAX_BACKENDS) + num_backends = R6XX_MAX_BACKENDS; + if (num_backends < 1) + num_backends = 1; + + enabled_backends_mask = 0; + enabled_backends_count = 0; + for (i = 0; i < R6XX_MAX_BACKENDS; ++i) { + if (((backend_disable_mask >> i) & 1) == 0) { + enabled_backends_mask |= (1 << i); + ++enabled_backends_count; + } + if (enabled_backends_count == num_backends) + break; + } + + if (enabled_backends_count == 0) { + enabled_backends_mask = 1; + enabled_backends_count = 1; + } + + if (enabled_backends_count != num_backends) + num_backends = enabled_backends_count; + + memset((uint8_t *)&swizzle_pipe[0], 0, sizeof(u32) * R6XX_MAX_PIPES); + switch (num_tile_pipes) { + case 1: + swizzle_pipe[0] = 0; + break; + case 2: + swizzle_pipe[0] = 0; + swizzle_pipe[1] = 1; + break; + case 3: + swizzle_pipe[0] = 0; + swizzle_pipe[1] = 1; + swizzle_pipe[2] = 2; + break; + case 4: + swizzle_pipe[0] = 0; + swizzle_pipe[1] = 1; + swizzle_pipe[2] = 2; + swizzle_pipe[3] = 3; + break; + case 5: + swizzle_pipe[0] = 0; + swizzle_pipe[1] = 1; + swizzle_pipe[2] = 2; + swizzle_pipe[3] = 3; + swizzle_pipe[4] = 4; + break; + case 6: + swizzle_pipe[0] = 0; + swizzle_pipe[1] = 2; + swizzle_pipe[2] = 4; + swizzle_pipe[3] = 5; + swizzle_pipe[4] = 1; + swizzle_pipe[5] = 3; + break; + case 7: + swizzle_pipe[0] = 0; + swizzle_pipe[1] = 2; + swizzle_pipe[2] = 4; + swizzle_pipe[3] = 6; + swizzle_pipe[4] = 1; + swizzle_pipe[5] = 3; + swizzle_pipe[6] = 5; + break; + case 8: + swizzle_pipe[0] = 0; + swizzle_pipe[1] = 2; + swizzle_pipe[2] = 4; + swizzle_pipe[3] = 6; + swizzle_pipe[4] = 1; + swizzle_pipe[5] = 3; + swizzle_pipe[6] = 5; + swizzle_pipe[7] = 7; + break; + } + + cur_backend = 0; + for (cur_pipe = 0; cur_pipe < num_tile_pipes; ++cur_pipe) { + while (((1 << cur_backend) & enabled_backends_mask) == 0) + cur_backend = (cur_backend + 1) % R6XX_MAX_BACKENDS; + + backend_map |= (u32)(((cur_backend & 3) << (swizzle_pipe[cur_pipe] * 2))); + + cur_backend = (cur_backend + 1) % R6XX_MAX_BACKENDS; + } + + return backend_map; +} + +static int r600_count_pipe_bits(uint32_t val) +{ + return hweight32(val); +} + +static void r600_gfx_init(struct drm_device *dev, + drm_radeon_private_t *dev_priv) +{ + int i, j, num_qd_pipes; + u32 sx_debug_1; + u32 tc_cntl; + u32 arb_pop; + u32 num_gs_verts_per_thread; + u32 vgt_gs_per_es; + u32 gs_prim_buffer_depth = 0; + u32 sq_ms_fifo_sizes; + u32 sq_config; + u32 sq_gpr_resource_mgmt_1 = 0; + u32 sq_gpr_resource_mgmt_2 = 0; + u32 sq_thread_resource_mgmt = 0; + u32 sq_stack_resource_mgmt_1 = 0; + u32 sq_stack_resource_mgmt_2 = 0; + u32 hdp_host_path_cntl; + u32 backend_map; + u32 gb_tiling_config = 0; + u32 cc_rb_backend_disable; + u32 cc_gc_shader_pipe_config; + u32 ramcfg; + + /* setup chip specs */ + switch (dev_priv->flags & RADEON_FAMILY_MASK) { + case CHIP_R600: + dev_priv->r600_max_pipes = 4; + dev_priv->r600_max_tile_pipes = 8; + dev_priv->r600_max_simds = 4; + dev_priv->r600_max_backends = 4; + dev_priv->r600_max_gprs = 256; + dev_priv->r600_max_threads = 192; + dev_priv->r600_max_stack_entries = 256; + dev_priv->r600_max_hw_contexts = 8; + dev_priv->r600_max_gs_threads = 16; + dev_priv->r600_sx_max_export_size = 128; + dev_priv->r600_sx_max_export_pos_size = 16; + dev_priv->r600_sx_max_export_smx_size = 128; + dev_priv->r600_sq_num_cf_insts = 2; + break; + case CHIP_RV630: + case CHIP_RV635: + dev_priv->r600_max_pipes = 2; + dev_priv->r600_max_tile_pipes = 2; + dev_priv->r600_max_simds = 3; + dev_priv->r600_max_backends = 1; + dev_priv->r600_max_gprs = 128; + dev_priv->r600_max_threads = 192; + dev_priv->r600_max_stack_entries = 128; + dev_priv->r600_max_hw_contexts = 8; + dev_priv->r600_max_gs_threads = 4; + dev_priv->r600_sx_max_export_size = 128; + dev_priv->r600_sx_max_export_pos_size = 16; + dev_priv->r600_sx_max_export_smx_size = 128; + dev_priv->r600_sq_num_cf_insts = 2; + break; + case CHIP_RV610: + case CHIP_RS780: + case CHIP_RS880: + case CHIP_RV620: + dev_priv->r600_max_pipes = 1; + dev_priv->r600_max_tile_pipes = 1; + dev_priv->r600_max_simds = 2; + dev_priv->r600_max_backends = 1; + dev_priv->r600_max_gprs = 128; + dev_priv->r600_max_threads = 192; + dev_priv->r600_max_stack_entries = 128; + dev_priv->r600_max_hw_contexts = 4; + dev_priv->r600_max_gs_threads = 4; + dev_priv->r600_sx_max_export_size = 128; + dev_priv->r600_sx_max_export_pos_size = 16; + dev_priv->r600_sx_max_export_smx_size = 128; + dev_priv->r600_sq_num_cf_insts = 1; + break; + case CHIP_RV670: + dev_priv->r600_max_pipes = 4; + dev_priv->r600_max_tile_pipes = 4; + dev_priv->r600_max_simds = 4; + dev_priv->r600_max_backends = 4; + dev_priv->r600_max_gprs = 192; + dev_priv->r600_max_threads = 192; + dev_priv->r600_max_stack_entries = 256; + dev_priv->r600_max_hw_contexts = 8; + dev_priv->r600_max_gs_threads = 16; + dev_priv->r600_sx_max_export_size = 128; + dev_priv->r600_sx_max_export_pos_size = 16; + dev_priv->r600_sx_max_export_smx_size = 128; + dev_priv->r600_sq_num_cf_insts = 2; + break; + default: + break; + } + + /* Initialize HDP */ + j = 0; + for (i = 0; i < 32; i++) { + RADEON_WRITE((0x2c14 + j), 0x00000000); + RADEON_WRITE((0x2c18 + j), 0x00000000); + RADEON_WRITE((0x2c1c + j), 0x00000000); + RADEON_WRITE((0x2c20 + j), 0x00000000); + RADEON_WRITE((0x2c24 + j), 0x00000000); + j += 0x18; + } + + RADEON_WRITE(R600_GRBM_CNTL, R600_GRBM_READ_TIMEOUT(0xff)); + + /* setup tiling, simd, pipe config */ + ramcfg = RADEON_READ(R600_RAMCFG); + + switch (dev_priv->r600_max_tile_pipes) { + case 1: + gb_tiling_config |= R600_PIPE_TILING(0); + break; + case 2: + gb_tiling_config |= R600_PIPE_TILING(1); + break; + case 4: + gb_tiling_config |= R600_PIPE_TILING(2); + break; + case 8: + gb_tiling_config |= R600_PIPE_TILING(3); + break; + default: + break; + } + + gb_tiling_config |= R600_BANK_TILING((ramcfg >> R600_NOOFBANK_SHIFT) & R600_NOOFBANK_MASK); + + gb_tiling_config |= R600_GROUP_SIZE(0); + + if (((ramcfg >> R600_NOOFROWS_SHIFT) & R600_NOOFROWS_MASK) > 3) { + gb_tiling_config |= R600_ROW_TILING(3); + gb_tiling_config |= R600_SAMPLE_SPLIT(3); + } else { + gb_tiling_config |= + R600_ROW_TILING(((ramcfg >> R600_NOOFROWS_SHIFT) & R600_NOOFROWS_MASK)); + gb_tiling_config |= + R600_SAMPLE_SPLIT(((ramcfg >> R600_NOOFROWS_SHIFT) & R600_NOOFROWS_MASK)); + } + + gb_tiling_config |= R600_BANK_SWAPS(1); + + cc_rb_backend_disable = RADEON_READ(R600_CC_RB_BACKEND_DISABLE) & 0x00ff0000; + cc_rb_backend_disable |= + R600_BACKEND_DISABLE((R6XX_MAX_BACKENDS_MASK << dev_priv->r600_max_backends) & R6XX_MAX_BACKENDS_MASK); + + cc_gc_shader_pipe_config = RADEON_READ(R600_CC_GC_SHADER_PIPE_CONFIG) & 0xffffff00; + cc_gc_shader_pipe_config |= + R600_INACTIVE_QD_PIPES((R6XX_MAX_PIPES_MASK << dev_priv->r600_max_pipes) & R6XX_MAX_PIPES_MASK); + cc_gc_shader_pipe_config |= + R600_INACTIVE_SIMDS((R6XX_MAX_SIMDS_MASK << dev_priv->r600_max_simds) & R6XX_MAX_SIMDS_MASK); + + backend_map = r600_get_tile_pipe_to_backend_map(dev_priv->r600_max_tile_pipes, + (R6XX_MAX_BACKENDS - + r600_count_pipe_bits((cc_rb_backend_disable & + R6XX_MAX_BACKENDS_MASK) >> 16)), + (cc_rb_backend_disable >> 16)); + gb_tiling_config |= R600_BACKEND_MAP(backend_map); + + RADEON_WRITE(R600_GB_TILING_CONFIG, gb_tiling_config); + RADEON_WRITE(R600_DCP_TILING_CONFIG, (gb_tiling_config & 0xffff)); + RADEON_WRITE(R600_HDP_TILING_CONFIG, (gb_tiling_config & 0xffff)); + if (gb_tiling_config & 0xc0) { + dev_priv->r600_group_size = 512; + } else { + dev_priv->r600_group_size = 256; + } + dev_priv->r600_npipes = 1 << ((gb_tiling_config >> 1) & 0x7); + if (gb_tiling_config & 0x30) { + dev_priv->r600_nbanks = 8; + } else { + dev_priv->r600_nbanks = 4; + } + + RADEON_WRITE(R600_CC_RB_BACKEND_DISABLE, cc_rb_backend_disable); + RADEON_WRITE(R600_CC_GC_SHADER_PIPE_CONFIG, cc_gc_shader_pipe_config); + RADEON_WRITE(R600_GC_USER_SHADER_PIPE_CONFIG, cc_gc_shader_pipe_config); + + num_qd_pipes = + R6XX_MAX_PIPES - r600_count_pipe_bits((cc_gc_shader_pipe_config & R600_INACTIVE_QD_PIPES_MASK) >> 8); + RADEON_WRITE(R600_VGT_OUT_DEALLOC_CNTL, (num_qd_pipes * 4) & R600_DEALLOC_DIST_MASK); + RADEON_WRITE(R600_VGT_VERTEX_REUSE_BLOCK_CNTL, ((num_qd_pipes * 4) - 2) & R600_VTX_REUSE_DEPTH_MASK); + + /* set HW defaults for 3D engine */ + RADEON_WRITE(R600_CP_QUEUE_THRESHOLDS, (R600_ROQ_IB1_START(0x16) | + R600_ROQ_IB2_START(0x2b))); + + RADEON_WRITE(R600_CP_MEQ_THRESHOLDS, (R600_MEQ_END(0x40) | + R600_ROQ_END(0x40))); + + RADEON_WRITE(R600_TA_CNTL_AUX, (R600_DISABLE_CUBE_ANISO | + R600_SYNC_GRADIENT | + R600_SYNC_WALKER | + R600_SYNC_ALIGNER)); + + if ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RV670) + RADEON_WRITE(R600_ARB_GDEC_RD_CNTL, 0x00000021); + + sx_debug_1 = RADEON_READ(R600_SX_DEBUG_1); + sx_debug_1 |= R600_SMX_EVENT_RELEASE; + if (((dev_priv->flags & RADEON_FAMILY_MASK) > CHIP_R600)) + sx_debug_1 |= R600_ENABLE_NEW_SMX_ADDRESS; + RADEON_WRITE(R600_SX_DEBUG_1, sx_debug_1); + + if (((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_R600) || + ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RV630) || + ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RV610) || + ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RV620) || + ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RS780) || + ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RS880)) + RADEON_WRITE(R600_DB_DEBUG, R600_PREZ_MUST_WAIT_FOR_POSTZ_DONE); + else + RADEON_WRITE(R600_DB_DEBUG, 0); + + RADEON_WRITE(R600_DB_WATERMARKS, (R600_DEPTH_FREE(4) | + R600_DEPTH_FLUSH(16) | + R600_DEPTH_PENDING_FREE(4) | + R600_DEPTH_CACHELINE_FREE(16))); + RADEON_WRITE(R600_PA_SC_MULTI_CHIP_CNTL, 0); + RADEON_WRITE(R600_VGT_NUM_INSTANCES, 0); + + RADEON_WRITE(R600_SPI_CONFIG_CNTL, R600_GPR_WRITE_PRIORITY(0)); + RADEON_WRITE(R600_SPI_CONFIG_CNTL_1, R600_VTX_DONE_DELAY(0)); + + sq_ms_fifo_sizes = RADEON_READ(R600_SQ_MS_FIFO_SIZES); + if (((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RV610) || + ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RV620) || + ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RS780) || + ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RS880)) { + sq_ms_fifo_sizes = (R600_CACHE_FIFO_SIZE(0xa) | + R600_FETCH_FIFO_HIWATER(0xa) | + R600_DONE_FIFO_HIWATER(0xe0) | + R600_ALU_UPDATE_FIFO_HIWATER(0x8)); + } else if (((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_R600) || + ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RV630)) { + sq_ms_fifo_sizes &= ~R600_DONE_FIFO_HIWATER(0xff); + sq_ms_fifo_sizes |= R600_DONE_FIFO_HIWATER(0x4); + } + RADEON_WRITE(R600_SQ_MS_FIFO_SIZES, sq_ms_fifo_sizes); + + /* SQ_CONFIG, SQ_GPR_RESOURCE_MGMT, SQ_THREAD_RESOURCE_MGMT, SQ_STACK_RESOURCE_MGMT + * should be adjusted as needed by the 2D/3D drivers. This just sets default values + */ + sq_config = RADEON_READ(R600_SQ_CONFIG); + sq_config &= ~(R600_PS_PRIO(3) | + R600_VS_PRIO(3) | + R600_GS_PRIO(3) | + R600_ES_PRIO(3)); + sq_config |= (R600_DX9_CONSTS | + R600_VC_ENABLE | + R600_PS_PRIO(0) | + R600_VS_PRIO(1) | + R600_GS_PRIO(2) | + R600_ES_PRIO(3)); + + if ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_R600) { + sq_gpr_resource_mgmt_1 = (R600_NUM_PS_GPRS(124) | + R600_NUM_VS_GPRS(124) | + R600_NUM_CLAUSE_TEMP_GPRS(4)); + sq_gpr_resource_mgmt_2 = (R600_NUM_GS_GPRS(0) | + R600_NUM_ES_GPRS(0)); + sq_thread_resource_mgmt = (R600_NUM_PS_THREADS(136) | + R600_NUM_VS_THREADS(48) | + R600_NUM_GS_THREADS(4) | + R600_NUM_ES_THREADS(4)); + sq_stack_resource_mgmt_1 = (R600_NUM_PS_STACK_ENTRIES(128) | + R600_NUM_VS_STACK_ENTRIES(128)); + sq_stack_resource_mgmt_2 = (R600_NUM_GS_STACK_ENTRIES(0) | + R600_NUM_ES_STACK_ENTRIES(0)); + } else if (((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RV610) || + ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RV620) || + ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RS780) || + ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RS880)) { + /* no vertex cache */ + sq_config &= ~R600_VC_ENABLE; + + sq_gpr_resource_mgmt_1 = (R600_NUM_PS_GPRS(44) | + R600_NUM_VS_GPRS(44) | + R600_NUM_CLAUSE_TEMP_GPRS(2)); + sq_gpr_resource_mgmt_2 = (R600_NUM_GS_GPRS(17) | + R600_NUM_ES_GPRS(17)); + sq_thread_resource_mgmt = (R600_NUM_PS_THREADS(79) | + R600_NUM_VS_THREADS(78) | + R600_NUM_GS_THREADS(4) | + R600_NUM_ES_THREADS(31)); + sq_stack_resource_mgmt_1 = (R600_NUM_PS_STACK_ENTRIES(40) | + R600_NUM_VS_STACK_ENTRIES(40)); + sq_stack_resource_mgmt_2 = (R600_NUM_GS_STACK_ENTRIES(32) | + R600_NUM_ES_STACK_ENTRIES(16)); + } else if (((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RV630) || + ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RV635)) { + sq_gpr_resource_mgmt_1 = (R600_NUM_PS_GPRS(44) | + R600_NUM_VS_GPRS(44) | + R600_NUM_CLAUSE_TEMP_GPRS(2)); + sq_gpr_resource_mgmt_2 = (R600_NUM_GS_GPRS(18) | + R600_NUM_ES_GPRS(18)); + sq_thread_resource_mgmt = (R600_NUM_PS_THREADS(79) | + R600_NUM_VS_THREADS(78) | + R600_NUM_GS_THREADS(4) | + R600_NUM_ES_THREADS(31)); + sq_stack_resource_mgmt_1 = (R600_NUM_PS_STACK_ENTRIES(40) | + R600_NUM_VS_STACK_ENTRIES(40)); + sq_stack_resource_mgmt_2 = (R600_NUM_GS_STACK_ENTRIES(32) | + R600_NUM_ES_STACK_ENTRIES(16)); + } else if ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RV670) { + sq_gpr_resource_mgmt_1 = (R600_NUM_PS_GPRS(44) | + R600_NUM_VS_GPRS(44) | + R600_NUM_CLAUSE_TEMP_GPRS(2)); + sq_gpr_resource_mgmt_2 = (R600_NUM_GS_GPRS(17) | + R600_NUM_ES_GPRS(17)); + sq_thread_resource_mgmt = (R600_NUM_PS_THREADS(79) | + R600_NUM_VS_THREADS(78) | + R600_NUM_GS_THREADS(4) | + R600_NUM_ES_THREADS(31)); + sq_stack_resource_mgmt_1 = (R600_NUM_PS_STACK_ENTRIES(64) | + R600_NUM_VS_STACK_ENTRIES(64)); + sq_stack_resource_mgmt_2 = (R600_NUM_GS_STACK_ENTRIES(64) | + R600_NUM_ES_STACK_ENTRIES(64)); + } + + RADEON_WRITE(R600_SQ_CONFIG, sq_config); + RADEON_WRITE(R600_SQ_GPR_RESOURCE_MGMT_1, sq_gpr_resource_mgmt_1); + RADEON_WRITE(R600_SQ_GPR_RESOURCE_MGMT_2, sq_gpr_resource_mgmt_2); + RADEON_WRITE(R600_SQ_THREAD_RESOURCE_MGMT, sq_thread_resource_mgmt); + RADEON_WRITE(R600_SQ_STACK_RESOURCE_MGMT_1, sq_stack_resource_mgmt_1); + RADEON_WRITE(R600_SQ_STACK_RESOURCE_MGMT_2, sq_stack_resource_mgmt_2); + + if (((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RV610) || + ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RV620) || + ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RS780) || + ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RS880)) + RADEON_WRITE(R600_VGT_CACHE_INVALIDATION, R600_CACHE_INVALIDATION(R600_TC_ONLY)); + else + RADEON_WRITE(R600_VGT_CACHE_INVALIDATION, R600_CACHE_INVALIDATION(R600_VC_AND_TC)); + + RADEON_WRITE(R600_PA_SC_AA_SAMPLE_LOCS_2S, (R600_S0_X(0xc) | + R600_S0_Y(0x4) | + R600_S1_X(0x4) | + R600_S1_Y(0xc))); + RADEON_WRITE(R600_PA_SC_AA_SAMPLE_LOCS_4S, (R600_S0_X(0xe) | + R600_S0_Y(0xe) | + R600_S1_X(0x2) | + R600_S1_Y(0x2) | + R600_S2_X(0xa) | + R600_S2_Y(0x6) | + R600_S3_X(0x6) | + R600_S3_Y(0xa))); + RADEON_WRITE(R600_PA_SC_AA_SAMPLE_LOCS_8S_WD0, (R600_S0_X(0xe) | + R600_S0_Y(0xb) | + R600_S1_X(0x4) | + R600_S1_Y(0xc) | + R600_S2_X(0x1) | + R600_S2_Y(0x6) | + R600_S3_X(0xa) | + R600_S3_Y(0xe))); + RADEON_WRITE(R600_PA_SC_AA_SAMPLE_LOCS_8S_WD1, (R600_S4_X(0x6) | + R600_S4_Y(0x1) | + R600_S5_X(0x0) | + R600_S5_Y(0x0) | + R600_S6_X(0xb) | + R600_S6_Y(0x4) | + R600_S7_X(0x7) | + R600_S7_Y(0x8))); + + + switch (dev_priv->flags & RADEON_FAMILY_MASK) { + case CHIP_R600: + case CHIP_RV630: + case CHIP_RV635: + gs_prim_buffer_depth = 0; + break; + case CHIP_RV610: + case CHIP_RS780: + case CHIP_RS880: + case CHIP_RV620: + gs_prim_buffer_depth = 32; + break; + case CHIP_RV670: + gs_prim_buffer_depth = 128; + break; + default: + break; + } + + num_gs_verts_per_thread = dev_priv->r600_max_pipes * 16; + vgt_gs_per_es = gs_prim_buffer_depth + num_gs_verts_per_thread; + /* Max value for this is 256 */ + if (vgt_gs_per_es > 256) + vgt_gs_per_es = 256; + + RADEON_WRITE(R600_VGT_ES_PER_GS, 128); + RADEON_WRITE(R600_VGT_GS_PER_ES, vgt_gs_per_es); + RADEON_WRITE(R600_VGT_GS_PER_VS, 2); + RADEON_WRITE(R600_VGT_GS_VERTEX_REUSE, 16); + + /* more default values. 2D/3D driver should adjust as needed */ + RADEON_WRITE(R600_PA_SC_LINE_STIPPLE_STATE, 0); + RADEON_WRITE(R600_VGT_STRMOUT_EN, 0); + RADEON_WRITE(R600_SX_MISC, 0); + RADEON_WRITE(R600_PA_SC_MODE_CNTL, 0); + RADEON_WRITE(R600_PA_SC_AA_CONFIG, 0); + RADEON_WRITE(R600_PA_SC_LINE_STIPPLE, 0); + RADEON_WRITE(R600_SPI_INPUT_Z, 0); + RADEON_WRITE(R600_SPI_PS_IN_CONTROL_0, R600_NUM_INTERP(2)); + RADEON_WRITE(R600_CB_COLOR7_FRAG, 0); + + /* clear render buffer base addresses */ + RADEON_WRITE(R600_CB_COLOR0_BASE, 0); + RADEON_WRITE(R600_CB_COLOR1_BASE, 0); + RADEON_WRITE(R600_CB_COLOR2_BASE, 0); + RADEON_WRITE(R600_CB_COLOR3_BASE, 0); + RADEON_WRITE(R600_CB_COLOR4_BASE, 0); + RADEON_WRITE(R600_CB_COLOR5_BASE, 0); + RADEON_WRITE(R600_CB_COLOR6_BASE, 0); + RADEON_WRITE(R600_CB_COLOR7_BASE, 0); + + switch (dev_priv->flags & RADEON_FAMILY_MASK) { + case CHIP_RV610: + case CHIP_RS780: + case CHIP_RS880: + case CHIP_RV620: + tc_cntl = R600_TC_L2_SIZE(8); + break; + case CHIP_RV630: + case CHIP_RV635: + tc_cntl = R600_TC_L2_SIZE(4); + break; + case CHIP_R600: + tc_cntl = R600_TC_L2_SIZE(0) | R600_L2_DISABLE_LATE_HIT; + break; + default: + tc_cntl = R600_TC_L2_SIZE(0); + break; + } + + RADEON_WRITE(R600_TC_CNTL, tc_cntl); + + hdp_host_path_cntl = RADEON_READ(R600_HDP_HOST_PATH_CNTL); + RADEON_WRITE(R600_HDP_HOST_PATH_CNTL, hdp_host_path_cntl); + + arb_pop = RADEON_READ(R600_ARB_POP); + arb_pop |= R600_ENABLE_TC128; + RADEON_WRITE(R600_ARB_POP, arb_pop); + + RADEON_WRITE(R600_PA_SC_MULTI_CHIP_CNTL, 0); + RADEON_WRITE(R600_PA_CL_ENHANCE, (R600_CLIP_VTX_REORDER_ENA | + R600_NUM_CLIP_SEQ(3))); + RADEON_WRITE(R600_PA_SC_ENHANCE, R600_FORCE_EOV_MAX_CLK_CNT(4095)); + +} + +static u32 r700_get_tile_pipe_to_backend_map(drm_radeon_private_t *dev_priv, + u32 num_tile_pipes, + u32 num_backends, + u32 backend_disable_mask) +{ + u32 backend_map = 0; + u32 enabled_backends_mask; + u32 enabled_backends_count; + u32 cur_pipe; + u32 swizzle_pipe[R7XX_MAX_PIPES]; + u32 cur_backend; + u32 i; + bool force_no_swizzle; + + if (num_tile_pipes > R7XX_MAX_PIPES) + num_tile_pipes = R7XX_MAX_PIPES; + if (num_tile_pipes < 1) + num_tile_pipes = 1; + if (num_backends > R7XX_MAX_BACKENDS) + num_backends = R7XX_MAX_BACKENDS; + if (num_backends < 1) + num_backends = 1; + + enabled_backends_mask = 0; + enabled_backends_count = 0; + for (i = 0; i < R7XX_MAX_BACKENDS; ++i) { + if (((backend_disable_mask >> i) & 1) == 0) { + enabled_backends_mask |= (1 << i); + ++enabled_backends_count; + } + if (enabled_backends_count == num_backends) + break; + } + + if (enabled_backends_count == 0) { + enabled_backends_mask = 1; + enabled_backends_count = 1; + } + + if (enabled_backends_count != num_backends) + num_backends = enabled_backends_count; + + switch (dev_priv->flags & RADEON_FAMILY_MASK) { + case CHIP_RV770: + case CHIP_RV730: + force_no_swizzle = false; + break; + case CHIP_RV710: + case CHIP_RV740: + default: + force_no_swizzle = true; + break; + } + + memset((uint8_t *)&swizzle_pipe[0], 0, sizeof(u32) * R7XX_MAX_PIPES); + switch (num_tile_pipes) { + case 1: + swizzle_pipe[0] = 0; + break; + case 2: + swizzle_pipe[0] = 0; + swizzle_pipe[1] = 1; + break; + case 3: + if (force_no_swizzle) { + swizzle_pipe[0] = 0; + swizzle_pipe[1] = 1; + swizzle_pipe[2] = 2; + } else { + swizzle_pipe[0] = 0; + swizzle_pipe[1] = 2; + swizzle_pipe[2] = 1; + } + break; + case 4: + if (force_no_swizzle) { + swizzle_pipe[0] = 0; + swizzle_pipe[1] = 1; + swizzle_pipe[2] = 2; + swizzle_pipe[3] = 3; + } else { + swizzle_pipe[0] = 0; + swizzle_pipe[1] = 2; + swizzle_pipe[2] = 3; + swizzle_pipe[3] = 1; + } + break; + case 5: + if (force_no_swizzle) { + swizzle_pipe[0] = 0; + swizzle_pipe[1] = 1; + swizzle_pipe[2] = 2; + swizzle_pipe[3] = 3; + swizzle_pipe[4] = 4; + } else { + swizzle_pipe[0] = 0; + swizzle_pipe[1] = 2; + swizzle_pipe[2] = 4; + swizzle_pipe[3] = 1; + swizzle_pipe[4] = 3; + } + break; + case 6: + if (force_no_swizzle) { + swizzle_pipe[0] = 0; + swizzle_pipe[1] = 1; + swizzle_pipe[2] = 2; + swizzle_pipe[3] = 3; + swizzle_pipe[4] = 4; + swizzle_pipe[5] = 5; + } else { + swizzle_pipe[0] = 0; + swizzle_pipe[1] = 2; + swizzle_pipe[2] = 4; + swizzle_pipe[3] = 5; + swizzle_pipe[4] = 3; + swizzle_pipe[5] = 1; + } + break; + case 7: + if (force_no_swizzle) { + swizzle_pipe[0] = 0; + swizzle_pipe[1] = 1; + swizzle_pipe[2] = 2; + swizzle_pipe[3] = 3; + swizzle_pipe[4] = 4; + swizzle_pipe[5] = 5; + swizzle_pipe[6] = 6; + } else { + swizzle_pipe[0] = 0; + swizzle_pipe[1] = 2; + swizzle_pipe[2] = 4; + swizzle_pipe[3] = 6; + swizzle_pipe[4] = 3; + swizzle_pipe[5] = 1; + swizzle_pipe[6] = 5; + } + break; + case 8: + if (force_no_swizzle) { + swizzle_pipe[0] = 0; + swizzle_pipe[1] = 1; + swizzle_pipe[2] = 2; + swizzle_pipe[3] = 3; + swizzle_pipe[4] = 4; + swizzle_pipe[5] = 5; + swizzle_pipe[6] = 6; + swizzle_pipe[7] = 7; + } else { + swizzle_pipe[0] = 0; + swizzle_pipe[1] = 2; + swizzle_pipe[2] = 4; + swizzle_pipe[3] = 6; + swizzle_pipe[4] = 3; + swizzle_pipe[5] = 1; + swizzle_pipe[6] = 7; + swizzle_pipe[7] = 5; + } + break; + } + + cur_backend = 0; + for (cur_pipe = 0; cur_pipe < num_tile_pipes; ++cur_pipe) { + while (((1 << cur_backend) & enabled_backends_mask) == 0) + cur_backend = (cur_backend + 1) % R7XX_MAX_BACKENDS; + + backend_map |= (u32)(((cur_backend & 3) << (swizzle_pipe[cur_pipe] * 2))); + + cur_backend = (cur_backend + 1) % R7XX_MAX_BACKENDS; + } + + return backend_map; +} + +static void r700_gfx_init(struct drm_device *dev, + drm_radeon_private_t *dev_priv) +{ + int i, j, num_qd_pipes; + u32 ta_aux_cntl; + u32 sx_debug_1; + u32 smx_dc_ctl0; + u32 db_debug3; + u32 num_gs_verts_per_thread; + u32 vgt_gs_per_es; + u32 gs_prim_buffer_depth = 0; + u32 sq_ms_fifo_sizes; + u32 sq_config; + u32 sq_thread_resource_mgmt; + u32 hdp_host_path_cntl; + u32 sq_dyn_gpr_size_simd_ab_0; + u32 backend_map; + u32 gb_tiling_config = 0; + u32 cc_rb_backend_disable; + u32 cc_gc_shader_pipe_config; + u32 mc_arb_ramcfg; + u32 db_debug4; + + /* setup chip specs */ + switch (dev_priv->flags & RADEON_FAMILY_MASK) { + case CHIP_RV770: + dev_priv->r600_max_pipes = 4; + dev_priv->r600_max_tile_pipes = 8; + dev_priv->r600_max_simds = 10; + dev_priv->r600_max_backends = 4; + dev_priv->r600_max_gprs = 256; + dev_priv->r600_max_threads = 248; + dev_priv->r600_max_stack_entries = 512; + dev_priv->r600_max_hw_contexts = 8; + dev_priv->r600_max_gs_threads = 16 * 2; + dev_priv->r600_sx_max_export_size = 128; + dev_priv->r600_sx_max_export_pos_size = 16; + dev_priv->r600_sx_max_export_smx_size = 112; + dev_priv->r600_sq_num_cf_insts = 2; + + dev_priv->r700_sx_num_of_sets = 7; + dev_priv->r700_sc_prim_fifo_size = 0xF9; + dev_priv->r700_sc_hiz_tile_fifo_size = 0x30; + dev_priv->r700_sc_earlyz_tile_fifo_fize = 0x130; + break; + case CHIP_RV730: + dev_priv->r600_max_pipes = 2; + dev_priv->r600_max_tile_pipes = 4; + dev_priv->r600_max_simds = 8; + dev_priv->r600_max_backends = 2; + dev_priv->r600_max_gprs = 128; + dev_priv->r600_max_threads = 248; + dev_priv->r600_max_stack_entries = 256; + dev_priv->r600_max_hw_contexts = 8; + dev_priv->r600_max_gs_threads = 16 * 2; + dev_priv->r600_sx_max_export_size = 256; + dev_priv->r600_sx_max_export_pos_size = 32; + dev_priv->r600_sx_max_export_smx_size = 224; + dev_priv->r600_sq_num_cf_insts = 2; + + dev_priv->r700_sx_num_of_sets = 7; + dev_priv->r700_sc_prim_fifo_size = 0xf9; + dev_priv->r700_sc_hiz_tile_fifo_size = 0x30; + dev_priv->r700_sc_earlyz_tile_fifo_fize = 0x130; + if (dev_priv->r600_sx_max_export_pos_size > 16) { + dev_priv->r600_sx_max_export_pos_size -= 16; + dev_priv->r600_sx_max_export_smx_size += 16; + } + break; + case CHIP_RV710: + dev_priv->r600_max_pipes = 2; + dev_priv->r600_max_tile_pipes = 2; + dev_priv->r600_max_simds = 2; + dev_priv->r600_max_backends = 1; + dev_priv->r600_max_gprs = 256; + dev_priv->r600_max_threads = 192; + dev_priv->r600_max_stack_entries = 256; + dev_priv->r600_max_hw_contexts = 4; + dev_priv->r600_max_gs_threads = 8 * 2; + dev_priv->r600_sx_max_export_size = 128; + dev_priv->r600_sx_max_export_pos_size = 16; + dev_priv->r600_sx_max_export_smx_size = 112; + dev_priv->r600_sq_num_cf_insts = 1; + + dev_priv->r700_sx_num_of_sets = 7; + dev_priv->r700_sc_prim_fifo_size = 0x40; + dev_priv->r700_sc_hiz_tile_fifo_size = 0x30; + dev_priv->r700_sc_earlyz_tile_fifo_fize = 0x130; + break; + case CHIP_RV740: + dev_priv->r600_max_pipes = 4; + dev_priv->r600_max_tile_pipes = 4; + dev_priv->r600_max_simds = 8; + dev_priv->r600_max_backends = 4; + dev_priv->r600_max_gprs = 256; + dev_priv->r600_max_threads = 248; + dev_priv->r600_max_stack_entries = 512; + dev_priv->r600_max_hw_contexts = 8; + dev_priv->r600_max_gs_threads = 16 * 2; + dev_priv->r600_sx_max_export_size = 256; + dev_priv->r600_sx_max_export_pos_size = 32; + dev_priv->r600_sx_max_export_smx_size = 224; + dev_priv->r600_sq_num_cf_insts = 2; + + dev_priv->r700_sx_num_of_sets = 7; + dev_priv->r700_sc_prim_fifo_size = 0x100; + dev_priv->r700_sc_hiz_tile_fifo_size = 0x30; + dev_priv->r700_sc_earlyz_tile_fifo_fize = 0x130; + + if (dev_priv->r600_sx_max_export_pos_size > 16) { + dev_priv->r600_sx_max_export_pos_size -= 16; + dev_priv->r600_sx_max_export_smx_size += 16; + } + break; + default: + break; + } + + /* Initialize HDP */ + j = 0; + for (i = 0; i < 32; i++) { + RADEON_WRITE((0x2c14 + j), 0x00000000); + RADEON_WRITE((0x2c18 + j), 0x00000000); + RADEON_WRITE((0x2c1c + j), 0x00000000); + RADEON_WRITE((0x2c20 + j), 0x00000000); + RADEON_WRITE((0x2c24 + j), 0x00000000); + j += 0x18; + } + + RADEON_WRITE(R600_GRBM_CNTL, R600_GRBM_READ_TIMEOUT(0xff)); + + /* setup tiling, simd, pipe config */ + mc_arb_ramcfg = RADEON_READ(R700_MC_ARB_RAMCFG); + + switch (dev_priv->r600_max_tile_pipes) { + case 1: + gb_tiling_config |= R600_PIPE_TILING(0); + break; + case 2: + gb_tiling_config |= R600_PIPE_TILING(1); + break; + case 4: + gb_tiling_config |= R600_PIPE_TILING(2); + break; + case 8: + gb_tiling_config |= R600_PIPE_TILING(3); + break; + default: + break; + } + + if ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RV770) + gb_tiling_config |= R600_BANK_TILING(1); + else + gb_tiling_config |= R600_BANK_TILING((mc_arb_ramcfg >> R700_NOOFBANK_SHIFT) & R700_NOOFBANK_MASK); + + gb_tiling_config |= R600_GROUP_SIZE(0); + + if (((mc_arb_ramcfg >> R700_NOOFROWS_SHIFT) & R700_NOOFROWS_MASK) > 3) { + gb_tiling_config |= R600_ROW_TILING(3); + gb_tiling_config |= R600_SAMPLE_SPLIT(3); + } else { + gb_tiling_config |= + R600_ROW_TILING(((mc_arb_ramcfg >> R700_NOOFROWS_SHIFT) & R700_NOOFROWS_MASK)); + gb_tiling_config |= + R600_SAMPLE_SPLIT(((mc_arb_ramcfg >> R700_NOOFROWS_SHIFT) & R700_NOOFROWS_MASK)); + } + + gb_tiling_config |= R600_BANK_SWAPS(1); + + cc_rb_backend_disable = RADEON_READ(R600_CC_RB_BACKEND_DISABLE) & 0x00ff0000; + cc_rb_backend_disable |= + R600_BACKEND_DISABLE((R7XX_MAX_BACKENDS_MASK << dev_priv->r600_max_backends) & R7XX_MAX_BACKENDS_MASK); + + cc_gc_shader_pipe_config = RADEON_READ(R600_CC_GC_SHADER_PIPE_CONFIG) & 0xffffff00; + cc_gc_shader_pipe_config |= + R600_INACTIVE_QD_PIPES((R7XX_MAX_PIPES_MASK << dev_priv->r600_max_pipes) & R7XX_MAX_PIPES_MASK); + cc_gc_shader_pipe_config |= + R600_INACTIVE_SIMDS((R7XX_MAX_SIMDS_MASK << dev_priv->r600_max_simds) & R7XX_MAX_SIMDS_MASK); + + if ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RV740) + backend_map = 0x28; + else + backend_map = r700_get_tile_pipe_to_backend_map(dev_priv, + dev_priv->r600_max_tile_pipes, + (R7XX_MAX_BACKENDS - + r600_count_pipe_bits((cc_rb_backend_disable & + R7XX_MAX_BACKENDS_MASK) >> 16)), + (cc_rb_backend_disable >> 16)); + gb_tiling_config |= R600_BACKEND_MAP(backend_map); + + RADEON_WRITE(R600_GB_TILING_CONFIG, gb_tiling_config); + RADEON_WRITE(R600_DCP_TILING_CONFIG, (gb_tiling_config & 0xffff)); + RADEON_WRITE(R600_HDP_TILING_CONFIG, (gb_tiling_config & 0xffff)); + if (gb_tiling_config & 0xc0) { + dev_priv->r600_group_size = 512; + } else { + dev_priv->r600_group_size = 256; + } + dev_priv->r600_npipes = 1 << ((gb_tiling_config >> 1) & 0x7); + if (gb_tiling_config & 0x30) { + dev_priv->r600_nbanks = 8; + } else { + dev_priv->r600_nbanks = 4; + } + + RADEON_WRITE(R600_CC_RB_BACKEND_DISABLE, cc_rb_backend_disable); + RADEON_WRITE(R600_CC_GC_SHADER_PIPE_CONFIG, cc_gc_shader_pipe_config); + RADEON_WRITE(R600_GC_USER_SHADER_PIPE_CONFIG, cc_gc_shader_pipe_config); + + RADEON_WRITE(R700_CC_SYS_RB_BACKEND_DISABLE, cc_rb_backend_disable); + RADEON_WRITE(R700_CGTS_SYS_TCC_DISABLE, 0); + RADEON_WRITE(R700_CGTS_TCC_DISABLE, 0); + RADEON_WRITE(R700_CGTS_USER_SYS_TCC_DISABLE, 0); + RADEON_WRITE(R700_CGTS_USER_TCC_DISABLE, 0); + + num_qd_pipes = + R7XX_MAX_PIPES - r600_count_pipe_bits((cc_gc_shader_pipe_config & R600_INACTIVE_QD_PIPES_MASK) >> 8); + RADEON_WRITE(R600_VGT_OUT_DEALLOC_CNTL, (num_qd_pipes * 4) & R600_DEALLOC_DIST_MASK); + RADEON_WRITE(R600_VGT_VERTEX_REUSE_BLOCK_CNTL, ((num_qd_pipes * 4) - 2) & R600_VTX_REUSE_DEPTH_MASK); + + /* set HW defaults for 3D engine */ + RADEON_WRITE(R600_CP_QUEUE_THRESHOLDS, (R600_ROQ_IB1_START(0x16) | + R600_ROQ_IB2_START(0x2b))); + + RADEON_WRITE(R600_CP_MEQ_THRESHOLDS, R700_STQ_SPLIT(0x30)); + + ta_aux_cntl = RADEON_READ(R600_TA_CNTL_AUX); + RADEON_WRITE(R600_TA_CNTL_AUX, ta_aux_cntl | R600_DISABLE_CUBE_ANISO); + + sx_debug_1 = RADEON_READ(R700_SX_DEBUG_1); + sx_debug_1 |= R700_ENABLE_NEW_SMX_ADDRESS; + RADEON_WRITE(R700_SX_DEBUG_1, sx_debug_1); + + smx_dc_ctl0 = RADEON_READ(R600_SMX_DC_CTL0); + smx_dc_ctl0 &= ~R700_CACHE_DEPTH(0x1ff); + smx_dc_ctl0 |= R700_CACHE_DEPTH((dev_priv->r700_sx_num_of_sets * 64) - 1); + RADEON_WRITE(R600_SMX_DC_CTL0, smx_dc_ctl0); + + if ((dev_priv->flags & RADEON_FAMILY_MASK) != CHIP_RV740) + RADEON_WRITE(R700_SMX_EVENT_CTL, (R700_ES_FLUSH_CTL(4) | + R700_GS_FLUSH_CTL(4) | + R700_ACK_FLUSH_CTL(3) | + R700_SYNC_FLUSH_CTL)); + + db_debug3 = RADEON_READ(R700_DB_DEBUG3); + db_debug3 &= ~R700_DB_CLK_OFF_DELAY(0x1f); + switch (dev_priv->flags & RADEON_FAMILY_MASK) { + case CHIP_RV770: + case CHIP_RV740: + db_debug3 |= R700_DB_CLK_OFF_DELAY(0x1f); + break; + case CHIP_RV710: + case CHIP_RV730: + default: + db_debug3 |= R700_DB_CLK_OFF_DELAY(2); + break; + } + RADEON_WRITE(R700_DB_DEBUG3, db_debug3); + + if ((dev_priv->flags & RADEON_FAMILY_MASK) != CHIP_RV770) { + db_debug4 = RADEON_READ(RV700_DB_DEBUG4); + db_debug4 |= RV700_DISABLE_TILE_COVERED_FOR_PS_ITER; + RADEON_WRITE(RV700_DB_DEBUG4, db_debug4); + } + + RADEON_WRITE(R600_SX_EXPORT_BUFFER_SIZES, (R600_COLOR_BUFFER_SIZE((dev_priv->r600_sx_max_export_size / 4) - 1) | + R600_POSITION_BUFFER_SIZE((dev_priv->r600_sx_max_export_pos_size / 4) - 1) | + R600_SMX_BUFFER_SIZE((dev_priv->r600_sx_max_export_smx_size / 4) - 1))); + + RADEON_WRITE(R700_PA_SC_FIFO_SIZE_R7XX, (R700_SC_PRIM_FIFO_SIZE(dev_priv->r700_sc_prim_fifo_size) | + R700_SC_HIZ_TILE_FIFO_SIZE(dev_priv->r700_sc_hiz_tile_fifo_size) | + R700_SC_EARLYZ_TILE_FIFO_SIZE(dev_priv->r700_sc_earlyz_tile_fifo_fize))); + + RADEON_WRITE(R600_PA_SC_MULTI_CHIP_CNTL, 0); + + RADEON_WRITE(R600_VGT_NUM_INSTANCES, 1); + + RADEON_WRITE(R600_SPI_CONFIG_CNTL, R600_GPR_WRITE_PRIORITY(0)); + + RADEON_WRITE(R600_SPI_CONFIG_CNTL_1, R600_VTX_DONE_DELAY(4)); + + RADEON_WRITE(R600_CP_PERFMON_CNTL, 0); + + sq_ms_fifo_sizes = (R600_CACHE_FIFO_SIZE(16 * dev_priv->r600_sq_num_cf_insts) | + R600_DONE_FIFO_HIWATER(0xe0) | + R600_ALU_UPDATE_FIFO_HIWATER(0x8)); + switch (dev_priv->flags & RADEON_FAMILY_MASK) { + case CHIP_RV770: + case CHIP_RV730: + case CHIP_RV710: + sq_ms_fifo_sizes |= R600_FETCH_FIFO_HIWATER(0x1); + break; + case CHIP_RV740: + default: + sq_ms_fifo_sizes |= R600_FETCH_FIFO_HIWATER(0x4); + break; + } + RADEON_WRITE(R600_SQ_MS_FIFO_SIZES, sq_ms_fifo_sizes); + + /* SQ_CONFIG, SQ_GPR_RESOURCE_MGMT, SQ_THREAD_RESOURCE_MGMT, SQ_STACK_RESOURCE_MGMT + * should be adjusted as needed by the 2D/3D drivers. This just sets default values + */ + sq_config = RADEON_READ(R600_SQ_CONFIG); + sq_config &= ~(R600_PS_PRIO(3) | + R600_VS_PRIO(3) | + R600_GS_PRIO(3) | + R600_ES_PRIO(3)); + sq_config |= (R600_DX9_CONSTS | + R600_VC_ENABLE | + R600_EXPORT_SRC_C | + R600_PS_PRIO(0) | + R600_VS_PRIO(1) | + R600_GS_PRIO(2) | + R600_ES_PRIO(3)); + if ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RV710) + /* no vertex cache */ + sq_config &= ~R600_VC_ENABLE; + + RADEON_WRITE(R600_SQ_CONFIG, sq_config); + + RADEON_WRITE(R600_SQ_GPR_RESOURCE_MGMT_1, (R600_NUM_PS_GPRS((dev_priv->r600_max_gprs * 24)/64) | + R600_NUM_VS_GPRS((dev_priv->r600_max_gprs * 24)/64) | + R600_NUM_CLAUSE_TEMP_GPRS(((dev_priv->r600_max_gprs * 24)/64)/2))); + + RADEON_WRITE(R600_SQ_GPR_RESOURCE_MGMT_2, (R600_NUM_GS_GPRS((dev_priv->r600_max_gprs * 7)/64) | + R600_NUM_ES_GPRS((dev_priv->r600_max_gprs * 7)/64))); + + sq_thread_resource_mgmt = (R600_NUM_PS_THREADS((dev_priv->r600_max_threads * 4)/8) | + R600_NUM_VS_THREADS((dev_priv->r600_max_threads * 2)/8) | + R600_NUM_ES_THREADS((dev_priv->r600_max_threads * 1)/8)); + if (((dev_priv->r600_max_threads * 1) / 8) > dev_priv->r600_max_gs_threads) + sq_thread_resource_mgmt |= R600_NUM_GS_THREADS(dev_priv->r600_max_gs_threads); + else + sq_thread_resource_mgmt |= R600_NUM_GS_THREADS((dev_priv->r600_max_gs_threads * 1)/8); + RADEON_WRITE(R600_SQ_THREAD_RESOURCE_MGMT, sq_thread_resource_mgmt); + + RADEON_WRITE(R600_SQ_STACK_RESOURCE_MGMT_1, (R600_NUM_PS_STACK_ENTRIES((dev_priv->r600_max_stack_entries * 1)/4) | + R600_NUM_VS_STACK_ENTRIES((dev_priv->r600_max_stack_entries * 1)/4))); + + RADEON_WRITE(R600_SQ_STACK_RESOURCE_MGMT_2, (R600_NUM_GS_STACK_ENTRIES((dev_priv->r600_max_stack_entries * 1)/4) | + R600_NUM_ES_STACK_ENTRIES((dev_priv->r600_max_stack_entries * 1)/4))); + + sq_dyn_gpr_size_simd_ab_0 = (R700_SIMDA_RING0((dev_priv->r600_max_gprs * 38)/64) | + R700_SIMDA_RING1((dev_priv->r600_max_gprs * 38)/64) | + R700_SIMDB_RING0((dev_priv->r600_max_gprs * 38)/64) | + R700_SIMDB_RING1((dev_priv->r600_max_gprs * 38)/64)); + + RADEON_WRITE(R700_SQ_DYN_GPR_SIZE_SIMD_AB_0, sq_dyn_gpr_size_simd_ab_0); + RADEON_WRITE(R700_SQ_DYN_GPR_SIZE_SIMD_AB_1, sq_dyn_gpr_size_simd_ab_0); + RADEON_WRITE(R700_SQ_DYN_GPR_SIZE_SIMD_AB_2, sq_dyn_gpr_size_simd_ab_0); + RADEON_WRITE(R700_SQ_DYN_GPR_SIZE_SIMD_AB_3, sq_dyn_gpr_size_simd_ab_0); + RADEON_WRITE(R700_SQ_DYN_GPR_SIZE_SIMD_AB_4, sq_dyn_gpr_size_simd_ab_0); + RADEON_WRITE(R700_SQ_DYN_GPR_SIZE_SIMD_AB_5, sq_dyn_gpr_size_simd_ab_0); + RADEON_WRITE(R700_SQ_DYN_GPR_SIZE_SIMD_AB_6, sq_dyn_gpr_size_simd_ab_0); + RADEON_WRITE(R700_SQ_DYN_GPR_SIZE_SIMD_AB_7, sq_dyn_gpr_size_simd_ab_0); + + RADEON_WRITE(R700_PA_SC_FORCE_EOV_MAX_CNTS, (R700_FORCE_EOV_MAX_CLK_CNT(4095) | + R700_FORCE_EOV_MAX_REZ_CNT(255))); + + if ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RV710) + RADEON_WRITE(R600_VGT_CACHE_INVALIDATION, (R600_CACHE_INVALIDATION(R600_TC_ONLY) | + R700_AUTO_INVLD_EN(R700_ES_AND_GS_AUTO))); + else + RADEON_WRITE(R600_VGT_CACHE_INVALIDATION, (R600_CACHE_INVALIDATION(R600_VC_AND_TC) | + R700_AUTO_INVLD_EN(R700_ES_AND_GS_AUTO))); + + switch (dev_priv->flags & RADEON_FAMILY_MASK) { + case CHIP_RV770: + case CHIP_RV730: + case CHIP_RV740: + gs_prim_buffer_depth = 384; + break; + case CHIP_RV710: + gs_prim_buffer_depth = 128; + break; + default: + break; + } + + num_gs_verts_per_thread = dev_priv->r600_max_pipes * 16; + vgt_gs_per_es = gs_prim_buffer_depth + num_gs_verts_per_thread; + /* Max value for this is 256 */ + if (vgt_gs_per_es > 256) + vgt_gs_per_es = 256; + + RADEON_WRITE(R600_VGT_ES_PER_GS, 128); + RADEON_WRITE(R600_VGT_GS_PER_ES, vgt_gs_per_es); + RADEON_WRITE(R600_VGT_GS_PER_VS, 2); + + /* more default values. 2D/3D driver should adjust as needed */ + RADEON_WRITE(R600_VGT_GS_VERTEX_REUSE, 16); + RADEON_WRITE(R600_PA_SC_LINE_STIPPLE_STATE, 0); + RADEON_WRITE(R600_VGT_STRMOUT_EN, 0); + RADEON_WRITE(R600_SX_MISC, 0); + RADEON_WRITE(R600_PA_SC_MODE_CNTL, 0); + RADEON_WRITE(R700_PA_SC_EDGERULE, 0xaaaaaaaa); + RADEON_WRITE(R600_PA_SC_AA_CONFIG, 0); + RADEON_WRITE(R600_PA_SC_CLIPRECT_RULE, 0xffff); + RADEON_WRITE(R600_PA_SC_LINE_STIPPLE, 0); + RADEON_WRITE(R600_SPI_INPUT_Z, 0); + RADEON_WRITE(R600_SPI_PS_IN_CONTROL_0, R600_NUM_INTERP(2)); + RADEON_WRITE(R600_CB_COLOR7_FRAG, 0); + + /* clear render buffer base addresses */ + RADEON_WRITE(R600_CB_COLOR0_BASE, 0); + RADEON_WRITE(R600_CB_COLOR1_BASE, 0); + RADEON_WRITE(R600_CB_COLOR2_BASE, 0); + RADEON_WRITE(R600_CB_COLOR3_BASE, 0); + RADEON_WRITE(R600_CB_COLOR4_BASE, 0); + RADEON_WRITE(R600_CB_COLOR5_BASE, 0); + RADEON_WRITE(R600_CB_COLOR6_BASE, 0); + RADEON_WRITE(R600_CB_COLOR7_BASE, 0); + + RADEON_WRITE(R700_TCP_CNTL, 0); + + hdp_host_path_cntl = RADEON_READ(R600_HDP_HOST_PATH_CNTL); + RADEON_WRITE(R600_HDP_HOST_PATH_CNTL, hdp_host_path_cntl); + + RADEON_WRITE(R600_PA_SC_MULTI_CHIP_CNTL, 0); + + RADEON_WRITE(R600_PA_CL_ENHANCE, (R600_CLIP_VTX_REORDER_ENA | + R600_NUM_CLIP_SEQ(3))); + +} + +static void r600_cp_init_ring_buffer(struct drm_device *dev, + drm_radeon_private_t *dev_priv, + struct drm_file *file_priv) +{ + struct drm_radeon_master_private *master_priv; + u32 ring_start; + u64 rptr_addr; + + if (((dev_priv->flags & RADEON_FAMILY_MASK) >= CHIP_RV770)) + r700_gfx_init(dev, dev_priv); + else + r600_gfx_init(dev, dev_priv); + + RADEON_WRITE(R600_GRBM_SOFT_RESET, R600_SOFT_RESET_CP); + RADEON_READ(R600_GRBM_SOFT_RESET); + DRM_MDELAY(15); + RADEON_WRITE(R600_GRBM_SOFT_RESET, 0); + + + /* Set ring buffer size */ +#ifdef __BIG_ENDIAN + RADEON_WRITE(R600_CP_RB_CNTL, + R600_BUF_SWAP_32BIT | + R600_RB_NO_UPDATE | + (dev_priv->ring.rptr_update_l2qw << 8) | + dev_priv->ring.size_l2qw); +#else + RADEON_WRITE(R600_CP_RB_CNTL, + RADEON_RB_NO_UPDATE | + (dev_priv->ring.rptr_update_l2qw << 8) | + dev_priv->ring.size_l2qw); +#endif + + RADEON_WRITE(R600_CP_SEM_WAIT_TIMER, 0x0); + + /* Set the write pointer delay */ + RADEON_WRITE(R600_CP_RB_WPTR_DELAY, 0); + +#ifdef __BIG_ENDIAN + RADEON_WRITE(R600_CP_RB_CNTL, + R600_BUF_SWAP_32BIT | + R600_RB_NO_UPDATE | + R600_RB_RPTR_WR_ENA | + (dev_priv->ring.rptr_update_l2qw << 8) | + dev_priv->ring.size_l2qw); +#else + RADEON_WRITE(R600_CP_RB_CNTL, + R600_RB_NO_UPDATE | + R600_RB_RPTR_WR_ENA | + (dev_priv->ring.rptr_update_l2qw << 8) | + dev_priv->ring.size_l2qw); +#endif + + /* Initialize the ring buffer's read and write pointers */ + RADEON_WRITE(R600_CP_RB_RPTR_WR, 0); + RADEON_WRITE(R600_CP_RB_WPTR, 0); + SET_RING_HEAD(dev_priv, 0); + dev_priv->ring.tail = 0; + +#if __OS_HAS_AGP + if (dev_priv->flags & RADEON_IS_AGP) { + rptr_addr = dev_priv->ring_rptr->offset + - dev->agp->base + + dev_priv->gart_vm_start; + } else +#endif + { + rptr_addr = dev_priv->ring_rptr->offset + - ((unsigned long) dev->sg->vaddr) + + dev_priv->gart_vm_start; + } + RADEON_WRITE(R600_CP_RB_RPTR_ADDR, (rptr_addr & 0xfffffffc)); + RADEON_WRITE(R600_CP_RB_RPTR_ADDR_HI, upper_32_bits(rptr_addr)); + +#ifdef __BIG_ENDIAN + RADEON_WRITE(R600_CP_RB_CNTL, + RADEON_BUF_SWAP_32BIT | + (dev_priv->ring.rptr_update_l2qw << 8) | + dev_priv->ring.size_l2qw); +#else + RADEON_WRITE(R600_CP_RB_CNTL, + (dev_priv->ring.rptr_update_l2qw << 8) | + dev_priv->ring.size_l2qw); +#endif + +#if __OS_HAS_AGP + if (dev_priv->flags & RADEON_IS_AGP) { + /* XXX */ + radeon_write_agp_base(dev_priv, dev->agp->base); + + /* XXX */ + radeon_write_agp_location(dev_priv, + (((dev_priv->gart_vm_start - 1 + + dev_priv->gart_size) & 0xffff0000) | + (dev_priv->gart_vm_start >> 16))); + + ring_start = (dev_priv->cp_ring->offset + - dev->agp->base + + dev_priv->gart_vm_start); + } else +#endif + ring_start = (dev_priv->cp_ring->offset + - (unsigned long)dev->sg->vaddr> + + dev_priv->gart_vm_start); + + RADEON_WRITE(R600_CP_RB_BASE, ring_start >> 8); + + RADEON_WRITE(R600_CP_ME_CNTL, 0xff); + + RADEON_WRITE(R600_CP_DEBUG, (1 << 27) | (1 << 28)); + + /* Initialize the scratch register pointer. This will cause + * the scratch register values to be written out to memory + * whenever they are updated. + * + * We simply put this behind the ring read pointer, this works + * with PCI GART as well as (whatever kind of) AGP GART + */ + { + u64 scratch_addr; + + scratch_addr = RADEON_READ(R600_CP_RB_RPTR_ADDR) & 0xFFFFFFFC; + scratch_addr |= ((u64)RADEON_READ(R600_CP_RB_RPTR_ADDR_HI)) << 32; + scratch_addr += R600_SCRATCH_REG_OFFSET; + scratch_addr >>= 8; + scratch_addr &= 0xffffffff; + + RADEON_WRITE(R600_SCRATCH_ADDR, (uint32_t)scratch_addr); + } + + RADEON_WRITE(R600_SCRATCH_UMSK, 0x7); + + /* Turn on bus mastering */ + radeon_enable_bm(dev_priv); + + radeon_write_ring_rptr(dev_priv, R600_SCRATCHOFF(0), 0); + RADEON_WRITE(R600_LAST_FRAME_REG, 0); + + radeon_write_ring_rptr(dev_priv, R600_SCRATCHOFF(1), 0); + RADEON_WRITE(R600_LAST_DISPATCH_REG, 0); + + radeon_write_ring_rptr(dev_priv, R600_SCRATCHOFF(2), 0); + RADEON_WRITE(R600_LAST_CLEAR_REG, 0); + + /* reset sarea copies of these */ + master_priv = file_priv->masterp->driver_priv; + if (master_priv->sarea_priv) { + master_priv->sarea_priv->last_frame = 0; + master_priv->sarea_priv->last_dispatch = 0; + master_priv->sarea_priv->last_clear = 0; + } + + r600_do_wait_for_idle(dev_priv); + +} + +int r600_do_cleanup_cp(struct drm_device *dev) +{ + drm_radeon_private_t *dev_priv = dev->dev_private; + DRM_DEBUG("\n"); + + /* Make sure interrupts are disabled here because the uninstall ioctl + * may not have been called from userspace and after dev_private + * is freed, it's too late. + */ + if (dev->irq_enabled) + drm_irq_uninstall(dev); + +#if __OS_HAS_AGP + if (dev_priv->flags & RADEON_IS_AGP) { + if (dev_priv->cp_ring != NULL) { + drm_core_ioremapfree(dev_priv->cp_ring, dev); + dev_priv->cp_ring = NULL; + } + if (dev_priv->ring_rptr != NULL) { + drm_core_ioremapfree(dev_priv->ring_rptr, dev); + dev_priv->ring_rptr = NULL; + } + if (dev->agp_buffer_map != NULL) { + drm_core_ioremapfree(dev->agp_buffer_map, dev); + dev->agp_buffer_map = NULL; + } + } else +#endif + { + + if (dev_priv->gart_info.bus_addr) + r600_page_table_cleanup(dev, &dev_priv->gart_info); + + if (dev_priv->gart_info.gart_table_location == DRM_ATI_GART_FB) { + drm_core_ioremapfree(&dev_priv->gart_info.mapping, dev); + dev_priv->gart_info.addr = NULL; + } + } + /* only clear to the start of flags */ + memset(dev_priv, 0, offsetof(drm_radeon_private_t, flags)); + + return 0; +} + +int r600_do_init_cp(struct drm_device *dev, drm_radeon_init_t *init, + struct drm_file *file_priv) +{ + drm_radeon_private_t *dev_priv = dev->dev_private; + struct drm_radeon_master_private *master_priv = file_priv->masterp->driver_priv; + + DRM_DEBUG("\n"); + + sx_init(&dev_priv->cs_mutex, "drm__radeon_private__cs_mutex"); + r600_cs_legacy_init(); + /* if we require new memory map but we don't have it fail */ + if ((dev_priv->flags & RADEON_NEW_MEMMAP) && !dev_priv->new_memmap) { + DRM_ERROR("Cannot initialise DRM on this card\nThis card requires a new X.org DDX for 3D\n"); + r600_do_cleanup_cp(dev); + return -EINVAL; + } + + if (init->is_pci && (dev_priv->flags & RADEON_IS_AGP)) { + DRM_DEBUG("Forcing AGP card to PCI mode\n"); + dev_priv->flags &= ~RADEON_IS_AGP; + /* The writeback test succeeds, but when writeback is enabled, + * the ring buffer read ptr update fails after first 128 bytes. + */ + radeon_no_wb = 1; + } else if (!(dev_priv->flags & (RADEON_IS_AGP | RADEON_IS_PCI | RADEON_IS_PCIE)) + && !init->is_pci) { + DRM_DEBUG("Restoring AGP flag\n"); + dev_priv->flags |= RADEON_IS_AGP; + } + + dev_priv->usec_timeout = init->usec_timeout; + if (dev_priv->usec_timeout < 1 || + dev_priv->usec_timeout > RADEON_MAX_USEC_TIMEOUT) { + DRM_DEBUG("TIMEOUT problem!\n"); + r600_do_cleanup_cp(dev); + return -EINVAL; + } + + /* Enable vblank on CRTC1 for older X servers + */ + dev_priv->vblank_crtc = DRM_RADEON_VBLANK_CRTC1; + dev_priv->do_boxes = 0; + dev_priv->cp_mode = init->cp_mode; + + /* We don't support anything other than bus-mastering ring mode, + * but the ring can be in either AGP or PCI space for the ring + * read pointer. + */ + if ((init->cp_mode != RADEON_CSQ_PRIBM_INDDIS) && + (init->cp_mode != RADEON_CSQ_PRIBM_INDBM)) { + DRM_DEBUG("BAD cp_mode (%x)!\n", init->cp_mode); + r600_do_cleanup_cp(dev); + return -EINVAL; + } + + switch (init->fb_bpp) { + case 16: + dev_priv->color_fmt = RADEON_COLOR_FORMAT_RGB565; + break; + case 32: + default: + dev_priv->color_fmt = RADEON_COLOR_FORMAT_ARGB8888; + break; + } + dev_priv->front_offset = init->front_offset; + dev_priv->front_pitch = init->front_pitch; + dev_priv->back_offset = init->back_offset; + dev_priv->back_pitch = init->back_pitch; + + dev_priv->ring_offset = init->ring_offset; + dev_priv->ring_rptr_offset = init->ring_rptr_offset; + dev_priv->buffers_offset = init->buffers_offset; + dev_priv->gart_textures_offset = init->gart_textures_offset; + + master_priv->sarea = drm_getsarea(dev); + if (!master_priv->sarea) { + DRM_ERROR("could not find sarea!\n"); + r600_do_cleanup_cp(dev); + return -EINVAL; + } + + dev_priv->cp_ring = drm_core_findmap(dev, init->ring_offset); + if (!dev_priv->cp_ring) { + DRM_ERROR("could not find cp ring region!\n"); + r600_do_cleanup_cp(dev); + return -EINVAL; + } + dev_priv->ring_rptr = drm_core_findmap(dev, init->ring_rptr_offset); + if (!dev_priv->ring_rptr) { + DRM_ERROR("could not find ring read pointer!\n"); + r600_do_cleanup_cp(dev); + return -EINVAL; + } + dev->agp_buffer_token = init->buffers_offset; + dev->agp_buffer_map = drm_core_findmap(dev, init->buffers_offset); + if (!dev->agp_buffer_map) { + DRM_ERROR("could not find dma buffer region!\n"); + r600_do_cleanup_cp(dev); + return -EINVAL; + } + + if (init->gart_textures_offset) { + dev_priv->gart_textures = + drm_core_findmap(dev, init->gart_textures_offset); + if (!dev_priv->gart_textures) { + DRM_ERROR("could not find GART texture region!\n"); + r600_do_cleanup_cp(dev); + return -EINVAL; + } + } + +#if __OS_HAS_AGP + /* XXX */ + if (dev_priv->flags & RADEON_IS_AGP) { + drm_core_ioremap_wc(dev_priv->cp_ring, dev); + drm_core_ioremap_wc(dev_priv->ring_rptr, dev); + drm_core_ioremap_wc(dev->agp_buffer_map, dev); + if (!dev_priv->cp_ring->handle || + !dev_priv->ring_rptr->handle || + !dev->agp_buffer_map->handle) { + DRM_ERROR("could not find ioremap agp regions!\n"); + r600_do_cleanup_cp(dev); + return -EINVAL; + } + } else +#endif + { + dev_priv->cp_ring->handle = (void *)(unsigned long)dev_priv->cp_ring->offset; + dev_priv->ring_rptr->handle = + (void *)(unsigned long)dev_priv->ring_rptr->offset; + dev->agp_buffer_map->handle = + (void *)(unsigned long)dev->agp_buffer_map->offset; + + DRM_DEBUG("dev_priv->cp_ring->handle %p\n", + dev_priv->cp_ring->handle); + DRM_DEBUG("dev_priv->ring_rptr->handle %p\n", + dev_priv->ring_rptr->handle); + DRM_DEBUG("dev->agp_buffer_map->handle %p\n", + dev->agp_buffer_map->handle); + } + + dev_priv->fb_location = (radeon_read_fb_location(dev_priv) & 0xffff) << 24; + dev_priv->fb_size = + (((radeon_read_fb_location(dev_priv) & 0xffff0000u) << 8) + 0x1000000) + - dev_priv->fb_location; + + dev_priv->front_pitch_offset = (((dev_priv->front_pitch / 64) << 22) | + ((dev_priv->front_offset + + dev_priv->fb_location) >> 10)); + + dev_priv->back_pitch_offset = (((dev_priv->back_pitch / 64) << 22) | + ((dev_priv->back_offset + + dev_priv->fb_location) >> 10)); + + dev_priv->depth_pitch_offset = (((dev_priv->depth_pitch / 64) << 22) | + ((dev_priv->depth_offset + + dev_priv->fb_location) >> 10)); + + dev_priv->gart_size = init->gart_size; + + /* New let's set the memory map ... */ + if (dev_priv->new_memmap) { + u32 base = 0; + + DRM_INFO("Setting GART location based on new memory map\n"); + + /* If using AGP, try to locate the AGP aperture at the same + * location in the card and on the bus, though we have to + * align it down. + */ +#if __OS_HAS_AGP + /* XXX */ + if (dev_priv->flags & RADEON_IS_AGP) { + base = dev->agp->base; + /* Check if valid */ + if ((base + dev_priv->gart_size - 1) >= dev_priv->fb_location && + base < (dev_priv->fb_location + dev_priv->fb_size - 1)) { + DRM_INFO("Can't use AGP base @0x%08lx, won't fit\n", + dev->agp->base); + base = 0; + } + } +#endif + /* If not or if AGP is at 0 (Macs), try to put it elsewhere */ + if (base == 0) { + base = dev_priv->fb_location + dev_priv->fb_size; + if (base < dev_priv->fb_location || + ((base + dev_priv->gart_size) & 0xfffffffful) < base) + base = dev_priv->fb_location + - dev_priv->gart_size; + } + dev_priv->gart_vm_start = base & 0xffc00000u; + if (dev_priv->gart_vm_start != base) + DRM_INFO("GART aligned down from 0x%08x to 0x%08x\n", + base, dev_priv->gart_vm_start); + } + +#if __OS_HAS_AGP + /* XXX */ + if (dev_priv->flags & RADEON_IS_AGP) + dev_priv->gart_buffers_offset = (dev->agp_buffer_map->offset + - dev->agp->base + + dev_priv->gart_vm_start); + else +#endif + dev_priv->gart_buffers_offset = (dev->agp_buffer_map->offset + - (unsigned long)dev->sg->vaddr + + dev_priv->gart_vm_start); + + DRM_DEBUG("fb 0x%08x size %d\n", + (unsigned int) dev_priv->fb_location, + (unsigned int) dev_priv->fb_size); + DRM_DEBUG("dev_priv->gart_size %d\n", dev_priv->gart_size); + DRM_DEBUG("dev_priv->gart_vm_start 0x%08x\n", + (unsigned int) dev_priv->gart_vm_start); + DRM_DEBUG("dev_priv->gart_buffers_offset 0x%08lx\n", + dev_priv->gart_buffers_offset); + + dev_priv->ring.start = (u32 *) dev_priv->cp_ring->handle; + dev_priv->ring.end = ((u32 *) dev_priv->cp_ring->handle + + init->ring_size / sizeof(u32)); + dev_priv->ring.size = init->ring_size; + dev_priv->ring.size_l2qw = drm_order(init->ring_size / 8); + + dev_priv->ring.rptr_update = /* init->rptr_update */ 4096; + dev_priv->ring.rptr_update_l2qw = drm_order(/* init->rptr_update */ 4096 / 8); + + dev_priv->ring.fetch_size = /* init->fetch_size */ 32; + dev_priv->ring.fetch_size_l2ow = drm_order(/* init->fetch_size */ 32 / 16); + + dev_priv->ring.tail_mask = (dev_priv->ring.size / sizeof(u32)) - 1; + + dev_priv->ring.high_mark = RADEON_RING_HIGH_MARK; + +#if __OS_HAS_AGP + if (dev_priv->flags & RADEON_IS_AGP) { + /* XXX turn off pcie gart */ + } else +#endif + { + dev_priv->gart_info.table_mask = DMA_BIT_MASK(32); + /* if we have an offset set from userspace */ + if (!dev_priv->pcigart_offset_set) { + DRM_ERROR("Need gart offset from userspace\n"); + r600_do_cleanup_cp(dev); + return -EINVAL; + } + + DRM_DEBUG("Using gart offset 0x%08lx\n", dev_priv->pcigart_offset); + + dev_priv->gart_info.bus_addr = + dev_priv->pcigart_offset + dev_priv->fb_location; + dev_priv->gart_info.mapping.offset = + dev_priv->pcigart_offset + dev_priv->fb_aper_offset; + dev_priv->gart_info.mapping.size = + dev_priv->gart_info.table_size; + + drm_core_ioremap_wc(&dev_priv->gart_info.mapping, dev); + if (!dev_priv->gart_info.mapping.handle) { + DRM_ERROR("ioremap failed.\n"); + r600_do_cleanup_cp(dev); + return -EINVAL; + } + + dev_priv->gart_info.addr = + dev_priv->gart_info.mapping.handle; + + DRM_DEBUG("Setting phys_pci_gart to %p %08lX\n", + dev_priv->gart_info.addr, + dev_priv->pcigart_offset); + + if (!r600_page_table_init(dev)) { + DRM_ERROR("Failed to init GART table\n"); + r600_do_cleanup_cp(dev); + return -EINVAL; + } + + if (((dev_priv->flags & RADEON_FAMILY_MASK) >= CHIP_RV770)) + r700_vm_init(dev); + else + r600_vm_init(dev); + } + + if (!dev_priv->me_fw || !dev_priv->pfp_fw) { + int err = r600_cp_init_microcode(dev_priv); + if (err) { + DRM_ERROR("Failed to load firmware!\n"); + r600_do_cleanup_cp(dev); + return err; + } + } + if (((dev_priv->flags & RADEON_FAMILY_MASK) >= CHIP_RV770)) + r700_cp_load_microcode(dev_priv); + else + r600_cp_load_microcode(dev_priv); + + r600_cp_init_ring_buffer(dev, dev_priv, file_priv); + + dev_priv->last_buf = 0; + + r600_do_engine_reset(dev); + r600_test_writeback(dev_priv); + + return 0; +} + +int r600_do_resume_cp(struct drm_device *dev, struct drm_file *file_priv) +{ + drm_radeon_private_t *dev_priv = dev->dev_private; + + DRM_DEBUG("\n"); + if (((dev_priv->flags & RADEON_FAMILY_MASK) >= CHIP_RV770)) { + r700_vm_init(dev); + r700_cp_load_microcode(dev_priv); + } else { + r600_vm_init(dev); + r600_cp_load_microcode(dev_priv); + } + r600_cp_init_ring_buffer(dev, dev_priv, file_priv); + r600_do_engine_reset(dev); + + return 0; +} + +/* Wait for the CP to go idle. + */ +int r600_do_cp_idle(drm_radeon_private_t *dev_priv) +{ + RING_LOCALS; + DRM_DEBUG("\n"); + + BEGIN_RING(5); + OUT_RING(CP_PACKET3(R600_IT_EVENT_WRITE, 0)); + OUT_RING(R600_CACHE_FLUSH_AND_INV_EVENT); + /* wait for 3D idle clean */ + OUT_RING(CP_PACKET3(R600_IT_SET_CONFIG_REG, 1)); + OUT_RING((R600_WAIT_UNTIL - R600_SET_CONFIG_REG_OFFSET) >> 2); + OUT_RING(RADEON_WAIT_3D_IDLE | RADEON_WAIT_3D_IDLECLEAN); + + ADVANCE_RING(); + COMMIT_RING(); + + return r600_do_wait_for_idle(dev_priv); +} + +/* Start the Command Processor. + */ +void r600_do_cp_start(drm_radeon_private_t *dev_priv) +{ + u32 cp_me; + RING_LOCALS; + DRM_DEBUG("\n"); + + BEGIN_RING(7); + OUT_RING(CP_PACKET3(R600_IT_ME_INITIALIZE, 5)); + OUT_RING(0x00000001); + if (((dev_priv->flags & RADEON_FAMILY_MASK) < CHIP_RV770)) + OUT_RING(0x00000003); + else + OUT_RING(0x00000000); + OUT_RING((dev_priv->r600_max_hw_contexts - 1)); + OUT_RING(R600_ME_INITIALIZE_DEVICE_ID(1)); + OUT_RING(0x00000000); + OUT_RING(0x00000000); + ADVANCE_RING(); + COMMIT_RING(); + + /* set the mux and reset the halt bit */ + cp_me = 0xff; + RADEON_WRITE(R600_CP_ME_CNTL, cp_me); + + dev_priv->cp_running = 1; + +} + +void r600_do_cp_reset(drm_radeon_private_t *dev_priv) +{ + u32 cur_read_ptr; + DRM_DEBUG("\n"); + + cur_read_ptr = RADEON_READ(R600_CP_RB_RPTR); + RADEON_WRITE(R600_CP_RB_WPTR, cur_read_ptr); + SET_RING_HEAD(dev_priv, cur_read_ptr); + dev_priv->ring.tail = cur_read_ptr; +} + +void r600_do_cp_stop(drm_radeon_private_t *dev_priv) +{ + uint32_t cp_me; + + DRM_DEBUG("\n"); + + cp_me = 0xff | R600_CP_ME_HALT; + + RADEON_WRITE(R600_CP_ME_CNTL, cp_me); + + dev_priv->cp_running = 0; +} + +int r600_cp_dispatch_indirect(struct drm_device *dev, + struct drm_buf *buf, int start, int end) +{ + drm_radeon_private_t *dev_priv = dev->dev_private; + RING_LOCALS; + + if (start != end) { + unsigned long offset = (dev_priv->gart_buffers_offset + + buf->offset + start); + int dwords = (end - start + 3) / sizeof(u32); + + DRM_DEBUG("dwords:%d\n", dwords); + DRM_DEBUG("offset 0x%lx\n", offset); + + + /* Indirect buffer data must be a multiple of 16 dwords. + * pad the data with a Type-2 CP packet. + */ + while (dwords & 0xf) { + u32 *data = (u32 *) + ((char *)dev->agp_buffer_map->handle + + buf->offset + start); + data[dwords++] = RADEON_CP_PACKET2; + } + + /* Fire off the indirect buffer */ + BEGIN_RING(4); + OUT_RING(CP_PACKET3(R600_IT_INDIRECT_BUFFER, 2)); + OUT_RING((offset & 0xfffffffc)); + OUT_RING((upper_32_bits(offset) & 0xff)); + OUT_RING(dwords); + ADVANCE_RING(); + } + + return 0; +} + +void r600_cp_dispatch_swap(struct drm_device *dev, struct drm_file *file_priv) +{ + drm_radeon_private_t *dev_priv = dev->dev_private; + struct drm_master *master = file_priv->masterp; + struct drm_radeon_master_private *master_priv = master->driver_priv; + drm_radeon_sarea_t *sarea_priv = master_priv->sarea_priv; + int nbox = sarea_priv->nbox; + struct drm_clip_rect *pbox = sarea_priv->boxes; + int i, cpp, src_pitch, dst_pitch; + uint64_t src, dst; + RING_LOCALS; + DRM_DEBUG("\n"); + + if (dev_priv->color_fmt == RADEON_COLOR_FORMAT_ARGB8888) + cpp = 4; + else + cpp = 2; + + if (sarea_priv->pfCurrentPage == 0) { + src_pitch = dev_priv->back_pitch; + dst_pitch = dev_priv->front_pitch; + src = dev_priv->back_offset + dev_priv->fb_location; + dst = dev_priv->front_offset + dev_priv->fb_location; + } else { + src_pitch = dev_priv->front_pitch; + dst_pitch = dev_priv->back_pitch; + src = dev_priv->front_offset + dev_priv->fb_location; + dst = dev_priv->back_offset + dev_priv->fb_location; + } + + if (r600_prepare_blit_copy(dev, file_priv)) { + DRM_ERROR("unable to allocate vertex buffer for swap buffer\n"); + return; + } + for (i = 0; i < nbox; i++) { + int x = pbox[i].x1; + int y = pbox[i].y1; + int w = pbox[i].x2 - x; + int h = pbox[i].y2 - y; + + DRM_DEBUG("%d,%d-%d,%d\n", x, y, w, h); + + r600_blit_swap(dev, + src, dst, + x, y, x, y, w, h, + src_pitch, dst_pitch, cpp); + } + r600_done_blit_copy(dev); + + /* Increment the frame counter. The client-side 3D driver must + * throttle the framerate by waiting for this value before + * performing the swapbuffer ioctl. + */ + sarea_priv->last_frame++; + + BEGIN_RING(3); + R600_FRAME_AGE(sarea_priv->last_frame); + ADVANCE_RING(); +} + +int r600_cp_dispatch_texture(struct drm_device *dev, + struct drm_file *file_priv, + drm_radeon_texture_t *tex, + drm_radeon_tex_image_t *image) +{ + drm_radeon_private_t *dev_priv = dev->dev_private; + struct drm_buf *buf; + u32 *buffer; + const u8 __user *data; + int size, pass_size; + u64 src_offset, dst_offset; + + if (!radeon_check_offset(dev_priv, tex->offset)) { + DRM_ERROR("Invalid destination offset\n"); + return -EINVAL; + } + + /* this might fail for zero-sized uploads - are those illegal? */ + if (!radeon_check_offset(dev_priv, tex->offset + tex->height * tex->pitch - 1)) { + DRM_ERROR("Invalid final destination offset\n"); + return -EINVAL; + } + + size = tex->height * tex->pitch; + + if (size == 0) + return 0; + + dst_offset = tex->offset; + + if (r600_prepare_blit_copy(dev, file_priv)) { + DRM_ERROR("unable to allocate vertex buffer for swap buffer\n"); + return -EAGAIN; + } + do { + data = (const u8 __user *)image->data; + pass_size = size; + + buf = radeon_freelist_get(dev); + if (!buf) { + DRM_DEBUG("EAGAIN\n"); + if (DRM_COPY_TO_USER(tex->image, image, sizeof(*image))) + return -EFAULT; + return -EAGAIN; + } + + if (pass_size > buf->total) + pass_size = buf->total; + + /* Dispatch the indirect buffer. + */ + buffer = + (u32 *) ((char *)dev->agp_buffer_map->handle + buf->offset); + + if (DRM_COPY_FROM_USER(buffer, data, pass_size)) { + DRM_ERROR("EFAULT on pad, %d bytes\n", pass_size); + return -EFAULT; + } + + buf->file_priv = file_priv; + buf->used = pass_size; + src_offset = dev_priv->gart_buffers_offset + buf->offset; + + r600_blit_copy(dev, src_offset, dst_offset, pass_size); + + radeon_cp_discard_buffer(dev, file_priv->masterp, buf); + + /* Update the input parameters for next time */ + image->data = (const u8 __user *)image->data + pass_size; + dst_offset += pass_size; + size -= pass_size; + } while (size > 0); + r600_done_blit_copy(dev); + + return 0; +} + +/* + * Legacy cs ioctl + */ +static u32 radeon_cs_id_get(struct drm_radeon_private *radeon) +{ + /* FIXME: check if wrap affect last reported wrap & sequence */ + radeon->cs_id_scnt = (radeon->cs_id_scnt + 1) & 0x00FFFFFF; + if (!radeon->cs_id_scnt) { + /* increment wrap counter */ + radeon->cs_id_wcnt += 0x01000000; + /* valid sequence counter start at 1 */ + radeon->cs_id_scnt = 1; + } + return (radeon->cs_id_scnt | radeon->cs_id_wcnt); +} + +static void r600_cs_id_emit(drm_radeon_private_t *dev_priv, u32 *id) +{ + RING_LOCALS; + + *id = radeon_cs_id_get(dev_priv); + + /* SCRATCH 2 */ + BEGIN_RING(3); + R600_CLEAR_AGE(*id); + ADVANCE_RING(); + COMMIT_RING(); +} + +static int r600_ib_get(struct drm_device *dev, + struct drm_file *fpriv, + struct drm_buf **buffer) +{ + struct drm_buf *buf; + + *buffer = NULL; + buf = radeon_freelist_get(dev); + if (!buf) { + return -EBUSY; + } + buf->file_priv = fpriv; + *buffer = buf; + return 0; +} + +static void r600_ib_free(struct drm_device *dev, struct drm_buf *buf, + struct drm_file *fpriv, int l, int r) +{ + drm_radeon_private_t *dev_priv = dev->dev_private; + + if (buf) { + if (!r) + r600_cp_dispatch_indirect(dev, buf, 0, l * 4); + radeon_cp_discard_buffer(dev, fpriv->masterp, buf); + COMMIT_RING(); + } +} + +int r600_cs_legacy_ioctl(struct drm_device *dev, void *data, struct drm_file *fpriv) +{ + struct drm_radeon_private *dev_priv = dev->dev_private; + struct drm_radeon_cs *cs = data; + struct drm_buf *buf; + unsigned family; + int l, r = 0; + u32 *ib, cs_id = 0; + + if (dev_priv == NULL) { + DRM_ERROR("called with no initialization\n"); + return -EINVAL; + } + family = dev_priv->flags & RADEON_FAMILY_MASK; + if (family < CHIP_R600) { + DRM_ERROR("cs ioctl valid only for R6XX & R7XX in legacy mode\n"); + return -EINVAL; + } + sx_xlock(&dev_priv->cs_mutex); + /* get ib */ + l = 0; + r = r600_ib_get(dev, fpriv, &buf); + if (r) { + DRM_ERROR("ib_get failed\n"); + goto out; + } + ib = (u32 *)((uintptr_t)dev->agp_buffer_map->handle + buf->offset); + /* now parse command stream */ + r = r600_cs_legacy(dev, data, fpriv, family, ib, &l); + if (r) { + goto out; + } + +out: + r600_ib_free(dev, buf, fpriv, l, r); + /* emit cs id sequence */ + r600_cs_id_emit(dev_priv, &cs_id); + cs->cs_id = cs_id; + sx_xunlock(&dev_priv->cs_mutex); + return r; +} + +void r600_cs_legacy_get_tiling_conf(struct drm_device *dev, u32 *npipes, u32 *nbanks, u32 *group_size) +{ + struct drm_radeon_private *dev_priv = dev->dev_private; + + *npipes = dev_priv->r600_npipes; + *nbanks = dev_priv->r600_nbanks; + *group_size = dev_priv->r600_group_size; +} diff --git a/sys/dev/drm2/radeon/r600_cp.h b/sys/dev/drm2/radeon/r600_cp.h new file mode 100644 index 00000000000..b1611ba3e29 --- /dev/null +++ b/sys/dev/drm2/radeon/r600_cp.h @@ -0,0 +1,15 @@ + +#include +__FBSDID("$FreeBSD$"); + +#ifndef __R600_CP_H__ +#define __R600_CP_H__ + +void r600_cs_legacy_get_tiling_conf(struct drm_device *dev, + u32 *npipes, u32 *nbanks, u32 *group_size); + +int r600_cs_legacy(struct drm_device *dev, void *data, struct drm_file *filp, + unsigned family, u32 *ib, int *l); +void r600_cs_legacy_init(void); + +#endif /* !defined(__R600_CP_H__) */ diff --git a/sys/dev/drm2/radeon/r600_cs.c b/sys/dev/drm2/radeon/r600_cs.c new file mode 100644 index 00000000000..6ba46143266 --- /dev/null +++ b/sys/dev/drm2/radeon/r600_cs.c @@ -0,0 +1,2761 @@ +/* + * Copyright 2008 Advanced Micro Devices, Inc. + * Copyright 2008 Red Hat Inc. + * Copyright 2009 Jerome Glisse. + * + * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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. + * + * Authors: Dave Airlie + * Alex Deucher + * Jerome Glisse + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include "radeon.h" +#include "radeon_asic.h" +#include "r600d.h" +#include "r600_reg_safe.h" +#include "r600_cp.h" +#include "r600_cs.h" + +static int r600_cs_packet_next_reloc_mm(struct radeon_cs_parser *p, + struct radeon_cs_reloc **cs_reloc); +static int r600_cs_packet_next_reloc_nomm(struct radeon_cs_parser *p, + struct radeon_cs_reloc **cs_reloc); +typedef int (*next_reloc_t)(struct radeon_cs_parser*, struct radeon_cs_reloc**); +static next_reloc_t r600_cs_packet_next_reloc = &r600_cs_packet_next_reloc_mm; + + +struct r600_cs_track { + /* configuration we miror so that we use same code btw kms/ums */ + u32 group_size; + u32 nbanks; + u32 npipes; + /* value we track */ + u32 sq_config; + u32 log_nsamples; + u32 nsamples; + u32 cb_color_base_last[8]; + struct radeon_bo *cb_color_bo[8]; + u64 cb_color_bo_mc[8]; + u64 cb_color_bo_offset[8]; + struct radeon_bo *cb_color_frag_bo[8]; + u64 cb_color_frag_offset[8]; + struct radeon_bo *cb_color_tile_bo[8]; + u64 cb_color_tile_offset[8]; + u32 cb_color_mask[8]; + u32 cb_color_info[8]; + u32 cb_color_view[8]; + u32 cb_color_size_idx[8]; /* unused */ + u32 cb_target_mask; + u32 cb_shader_mask; /* unused */ + bool is_resolve; + u32 cb_color_size[8]; + u32 vgt_strmout_en; + u32 vgt_strmout_buffer_en; + struct radeon_bo *vgt_strmout_bo[4]; + u64 vgt_strmout_bo_mc[4]; /* unused */ + u32 vgt_strmout_bo_offset[4]; + u32 vgt_strmout_size[4]; + u32 db_depth_control; + u32 db_depth_info; + u32 db_depth_size_idx; + u32 db_depth_view; + u32 db_depth_size; + u32 db_offset; + struct radeon_bo *db_bo; + u64 db_bo_mc; + bool sx_misc_kill_all_prims; + bool cb_dirty; + bool db_dirty; + bool streamout_dirty; + struct radeon_bo *htile_bo; + u64 htile_offset; + u32 htile_surface; +}; + +#define FMT_8_BIT(fmt, vc) [fmt] = { 1, 1, 1, vc, CHIP_R600 } +#define FMT_16_BIT(fmt, vc) [fmt] = { 1, 1, 2, vc, CHIP_R600 } +#define FMT_24_BIT(fmt) [fmt] = { 1, 1, 4, 0, CHIP_R600 } +#define FMT_32_BIT(fmt, vc) [fmt] = { 1, 1, 4, vc, CHIP_R600 } +#define FMT_48_BIT(fmt) [fmt] = { 1, 1, 8, 0, CHIP_R600 } +#define FMT_64_BIT(fmt, vc) [fmt] = { 1, 1, 8, vc, CHIP_R600 } +#define FMT_96_BIT(fmt) [fmt] = { 1, 1, 12, 0, CHIP_R600 } +#define FMT_128_BIT(fmt, vc) [fmt] = { 1, 1, 16,vc, CHIP_R600 } + +struct gpu_formats { + unsigned blockwidth; + unsigned blockheight; + unsigned blocksize; + unsigned valid_color; + enum radeon_family min_family; +}; + +static const struct gpu_formats color_formats_table[] = { + /* 8 bit */ + FMT_8_BIT(V_038004_COLOR_8, 1), + FMT_8_BIT(V_038004_COLOR_4_4, 1), + FMT_8_BIT(V_038004_COLOR_3_3_2, 1), + FMT_8_BIT(V_038004_FMT_1, 0), + + /* 16-bit */ + FMT_16_BIT(V_038004_COLOR_16, 1), + FMT_16_BIT(V_038004_COLOR_16_FLOAT, 1), + FMT_16_BIT(V_038004_COLOR_8_8, 1), + FMT_16_BIT(V_038004_COLOR_5_6_5, 1), + FMT_16_BIT(V_038004_COLOR_6_5_5, 1), + FMT_16_BIT(V_038004_COLOR_1_5_5_5, 1), + FMT_16_BIT(V_038004_COLOR_4_4_4_4, 1), + FMT_16_BIT(V_038004_COLOR_5_5_5_1, 1), + + /* 24-bit */ + FMT_24_BIT(V_038004_FMT_8_8_8), + + /* 32-bit */ + FMT_32_BIT(V_038004_COLOR_32, 1), + FMT_32_BIT(V_038004_COLOR_32_FLOAT, 1), + FMT_32_BIT(V_038004_COLOR_16_16, 1), + FMT_32_BIT(V_038004_COLOR_16_16_FLOAT, 1), + FMT_32_BIT(V_038004_COLOR_8_24, 1), + FMT_32_BIT(V_038004_COLOR_8_24_FLOAT, 1), + FMT_32_BIT(V_038004_COLOR_24_8, 1), + FMT_32_BIT(V_038004_COLOR_24_8_FLOAT, 1), + FMT_32_BIT(V_038004_COLOR_10_11_11, 1), + FMT_32_BIT(V_038004_COLOR_10_11_11_FLOAT, 1), + FMT_32_BIT(V_038004_COLOR_11_11_10, 1), + FMT_32_BIT(V_038004_COLOR_11_11_10_FLOAT, 1), + FMT_32_BIT(V_038004_COLOR_2_10_10_10, 1), + FMT_32_BIT(V_038004_COLOR_8_8_8_8, 1), + FMT_32_BIT(V_038004_COLOR_10_10_10_2, 1), + FMT_32_BIT(V_038004_FMT_5_9_9_9_SHAREDEXP, 0), + FMT_32_BIT(V_038004_FMT_32_AS_8, 0), + FMT_32_BIT(V_038004_FMT_32_AS_8_8, 0), + + /* 48-bit */ + FMT_48_BIT(V_038004_FMT_16_16_16), + FMT_48_BIT(V_038004_FMT_16_16_16_FLOAT), + + /* 64-bit */ + FMT_64_BIT(V_038004_COLOR_X24_8_32_FLOAT, 1), + FMT_64_BIT(V_038004_COLOR_32_32, 1), + FMT_64_BIT(V_038004_COLOR_32_32_FLOAT, 1), + FMT_64_BIT(V_038004_COLOR_16_16_16_16, 1), + FMT_64_BIT(V_038004_COLOR_16_16_16_16_FLOAT, 1), + + FMT_96_BIT(V_038004_FMT_32_32_32), + FMT_96_BIT(V_038004_FMT_32_32_32_FLOAT), + + /* 128-bit */ + FMT_128_BIT(V_038004_COLOR_32_32_32_32, 1), + FMT_128_BIT(V_038004_COLOR_32_32_32_32_FLOAT, 1), + + [V_038004_FMT_GB_GR] = { 2, 1, 4, 0 }, + [V_038004_FMT_BG_RG] = { 2, 1, 4, 0 }, + + /* block compressed formats */ + [V_038004_FMT_BC1] = { 4, 4, 8, 0 }, + [V_038004_FMT_BC2] = { 4, 4, 16, 0 }, + [V_038004_FMT_BC3] = { 4, 4, 16, 0 }, + [V_038004_FMT_BC4] = { 4, 4, 8, 0 }, + [V_038004_FMT_BC5] = { 4, 4, 16, 0}, + [V_038004_FMT_BC6] = { 4, 4, 16, 0, CHIP_CEDAR}, /* Evergreen-only */ + [V_038004_FMT_BC7] = { 4, 4, 16, 0, CHIP_CEDAR}, /* Evergreen-only */ + + /* The other Evergreen formats */ + [V_038004_FMT_32_AS_32_32_32_32] = { 1, 1, 4, 0, CHIP_CEDAR}, +}; + +bool r600_fmt_is_valid_color(u32 format) +{ + if (format >= DRM_ARRAY_SIZE(color_formats_table)) + return false; + + if (color_formats_table[format].valid_color) + return true; + + return false; +} + +bool r600_fmt_is_valid_texture(u32 format, enum radeon_family family) +{ + if (format >= DRM_ARRAY_SIZE(color_formats_table)) + return false; + + if (family < color_formats_table[format].min_family) + return false; + + if (color_formats_table[format].blockwidth > 0) + return true; + + return false; +} + +int r600_fmt_get_blocksize(u32 format) +{ + if (format >= DRM_ARRAY_SIZE(color_formats_table)) + return 0; + + return color_formats_table[format].blocksize; +} + +int r600_fmt_get_nblocksx(u32 format, u32 w) +{ + unsigned bw; + + if (format >= DRM_ARRAY_SIZE(color_formats_table)) + return 0; + + bw = color_formats_table[format].blockwidth; + if (bw == 0) + return 0; + + return (w + bw - 1) / bw; +} + +int r600_fmt_get_nblocksy(u32 format, u32 h) +{ + unsigned bh; + + if (format >= DRM_ARRAY_SIZE(color_formats_table)) + return 0; + + bh = color_formats_table[format].blockheight; + if (bh == 0) + return 0; + + return (h + bh - 1) / bh; +} + +struct array_mode_checker { + int array_mode; + u32 group_size; + u32 nbanks; + u32 npipes; + u32 nsamples; + u32 blocksize; +}; + +/* returns alignment in pixels for pitch/height/depth and bytes for base */ +static int r600_get_array_mode_alignment(struct array_mode_checker *values, + u32 *pitch_align, + u32 *height_align, + u32 *depth_align, + u64 *base_align) +{ + u32 tile_width = 8; + u32 tile_height = 8; + u32 macro_tile_width = values->nbanks; + u32 macro_tile_height = values->npipes; + u32 tile_bytes = tile_width * tile_height * values->blocksize * values->nsamples; + u32 macro_tile_bytes = macro_tile_width * macro_tile_height * tile_bytes; + + switch (values->array_mode) { + case ARRAY_LINEAR_GENERAL: + /* technically tile_width/_height for pitch/height */ + *pitch_align = 1; /* tile_width */ + *height_align = 1; /* tile_height */ + *depth_align = 1; + *base_align = 1; + break; + case ARRAY_LINEAR_ALIGNED: + *pitch_align = max((u32)64, (u32)(values->group_size / values->blocksize)); + *height_align = 1; + *depth_align = 1; + *base_align = values->group_size; + break; + case ARRAY_1D_TILED_THIN1: + *pitch_align = max((u32)tile_width, + (u32)(values->group_size / + (tile_height * values->blocksize * values->nsamples))); + *height_align = tile_height; + *depth_align = 1; + *base_align = values->group_size; + break; + case ARRAY_2D_TILED_THIN1: + *pitch_align = max((u32)macro_tile_width * tile_width, + (u32)((values->group_size * values->nbanks) / + (values->blocksize * values->nsamples * tile_width))); + *height_align = macro_tile_height * tile_height; + *depth_align = 1; + *base_align = max(macro_tile_bytes, + (*pitch_align) * values->blocksize * (*height_align) * values->nsamples); + break; + default: + return -EINVAL; + } + + return 0; +} + +static void r600_cs_track_init(struct r600_cs_track *track) +{ + int i; + + /* assume DX9 mode */ + track->sq_config = DX9_CONSTS; + for (i = 0; i < 8; i++) { + track->cb_color_base_last[i] = 0; + track->cb_color_size[i] = 0; + track->cb_color_size_idx[i] = 0; + track->cb_color_info[i] = 0; + track->cb_color_view[i] = 0xFFFFFFFF; + track->cb_color_bo[i] = NULL; + track->cb_color_bo_offset[i] = 0xFFFFFFFF; + track->cb_color_bo_mc[i] = 0xFFFFFFFF; + track->cb_color_frag_bo[i] = NULL; + track->cb_color_frag_offset[i] = 0xFFFFFFFF; + track->cb_color_tile_bo[i] = NULL; + track->cb_color_tile_offset[i] = 0xFFFFFFFF; + track->cb_color_mask[i] = 0xFFFFFFFF; + } + track->is_resolve = false; + track->nsamples = 16; + track->log_nsamples = 4; + track->cb_target_mask = 0xFFFFFFFF; + track->cb_shader_mask = 0xFFFFFFFF; + track->cb_dirty = true; + track->db_bo = NULL; + track->db_bo_mc = 0xFFFFFFFF; + /* assume the biggest format and that htile is enabled */ + track->db_depth_info = 7 | (1 << 25); + track->db_depth_view = 0xFFFFC000; + track->db_depth_size = 0xFFFFFFFF; + track->db_depth_size_idx = 0; + track->db_depth_control = 0xFFFFFFFF; + track->db_dirty = true; + track->htile_bo = NULL; + track->htile_offset = 0xFFFFFFFF; + track->htile_surface = 0; + + for (i = 0; i < 4; i++) { + track->vgt_strmout_size[i] = 0; + track->vgt_strmout_bo[i] = NULL; + track->vgt_strmout_bo_offset[i] = 0xFFFFFFFF; + track->vgt_strmout_bo_mc[i] = 0xFFFFFFFF; + } + track->streamout_dirty = true; + track->sx_misc_kill_all_prims = false; +} + +static int r600_cs_track_validate_cb(struct radeon_cs_parser *p, int i) +{ + struct r600_cs_track *track = p->track; + u32 slice_tile_max, size, tmp; + u32 height, height_align, pitch, pitch_align, depth_align; + u64 base_offset, base_align; + struct array_mode_checker array_check; + volatile u32 *ib = p->ib.ptr; + unsigned array_mode; + u32 format; + /* When resolve is used, the second colorbuffer has always 1 sample. */ + unsigned nsamples = track->is_resolve && i == 1 ? 1 : track->nsamples; + + size = radeon_bo_size(track->cb_color_bo[i]) - track->cb_color_bo_offset[i]; + format = G_0280A0_FORMAT(track->cb_color_info[i]); + if (!r600_fmt_is_valid_color(format)) { + dev_warn(p->dev, "%s:%d cb invalid format %d for %d (0x%08X)\n", + __func__, __LINE__, format, + i, track->cb_color_info[i]); + return -EINVAL; + } + /* pitch in pixels */ + pitch = (G_028060_PITCH_TILE_MAX(track->cb_color_size[i]) + 1) * 8; + slice_tile_max = G_028060_SLICE_TILE_MAX(track->cb_color_size[i]) + 1; + slice_tile_max *= 64; + height = slice_tile_max / pitch; + if (height > 8192) + height = 8192; + array_mode = G_0280A0_ARRAY_MODE(track->cb_color_info[i]); + + base_offset = track->cb_color_bo_mc[i] + track->cb_color_bo_offset[i]; + array_check.array_mode = array_mode; + array_check.group_size = track->group_size; + array_check.nbanks = track->nbanks; + array_check.npipes = track->npipes; + array_check.nsamples = nsamples; + array_check.blocksize = r600_fmt_get_blocksize(format); + if (r600_get_array_mode_alignment(&array_check, + &pitch_align, &height_align, &depth_align, &base_align)) { + dev_warn(p->dev, "%s invalid tiling %d for %d (0x%08X)\n", __func__, + G_0280A0_ARRAY_MODE(track->cb_color_info[i]), i, + track->cb_color_info[i]); + return -EINVAL; + } + switch (array_mode) { + case V_0280A0_ARRAY_LINEAR_GENERAL: + break; + case V_0280A0_ARRAY_LINEAR_ALIGNED: + break; + case V_0280A0_ARRAY_1D_TILED_THIN1: + /* avoid breaking userspace */ + if (height > 7) + height &= ~0x7; + break; + case V_0280A0_ARRAY_2D_TILED_THIN1: + break; + default: + dev_warn(p->dev, "%s invalid tiling %d for %d (0x%08X)\n", __func__, + G_0280A0_ARRAY_MODE(track->cb_color_info[i]), i, + track->cb_color_info[i]); + return -EINVAL; + } + + if (!IS_ALIGNED(pitch, pitch_align)) { + dev_warn(p->dev, "%s:%d cb pitch (%d, 0x%x, %d) invalid\n", + __func__, __LINE__, pitch, pitch_align, array_mode); + return -EINVAL; + } + if (!IS_ALIGNED(height, height_align)) { + dev_warn(p->dev, "%s:%d cb height (%d, 0x%x, %d) invalid\n", + __func__, __LINE__, height, height_align, array_mode); + return -EINVAL; + } + if (!IS_ALIGNED(base_offset, base_align)) { + dev_warn(p->dev, "%s offset[%d] 0x%jx 0x%jx, %d not aligned\n", __func__, i, + (uintmax_t)base_offset, (uintmax_t)base_align, array_mode); + return -EINVAL; + } + + /* check offset */ + tmp = r600_fmt_get_nblocksy(format, height) * r600_fmt_get_nblocksx(format, pitch) * + r600_fmt_get_blocksize(format) * nsamples; + switch (array_mode) { + default: + case V_0280A0_ARRAY_LINEAR_GENERAL: + case V_0280A0_ARRAY_LINEAR_ALIGNED: + tmp += track->cb_color_view[i] & 0xFF; + break; + case V_0280A0_ARRAY_1D_TILED_THIN1: + case V_0280A0_ARRAY_2D_TILED_THIN1: + tmp += G_028080_SLICE_MAX(track->cb_color_view[i]) * tmp; + break; + } + if ((tmp + track->cb_color_bo_offset[i]) > radeon_bo_size(track->cb_color_bo[i])) { + if (array_mode == V_0280A0_ARRAY_LINEAR_GENERAL) { + /* the initial DDX does bad things with the CB size occasionally */ + /* it rounds up height too far for slice tile max but the BO is smaller */ + /* r600c,g also seem to flush at bad times in some apps resulting in + * bogus values here. So for linear just allow anything to avoid breaking + * broken userspace. + */ + } else { + dev_warn(p->dev, "%s offset[%d] %d %ju %d %lu too big (%d %d) (%d %d %d)\n", + __func__, i, array_mode, + (uintmax_t)track->cb_color_bo_offset[i], tmp, + radeon_bo_size(track->cb_color_bo[i]), + pitch, height, r600_fmt_get_nblocksx(format, pitch), + r600_fmt_get_nblocksy(format, height), + r600_fmt_get_blocksize(format)); + return -EINVAL; + } + } + /* limit max tile */ + tmp = (height * pitch) >> 6; + if (tmp < slice_tile_max) + slice_tile_max = tmp; + tmp = S_028060_PITCH_TILE_MAX((pitch / 8) - 1) | + S_028060_SLICE_TILE_MAX(slice_tile_max - 1); + ib[track->cb_color_size_idx[i]] = tmp; + + /* FMASK/CMASK */ + switch (G_0280A0_TILE_MODE(track->cb_color_info[i])) { + case V_0280A0_TILE_DISABLE: + break; + case V_0280A0_FRAG_ENABLE: + if (track->nsamples > 1) { + uint32_t tile_max = G_028100_FMASK_TILE_MAX(track->cb_color_mask[i]); + /* the tile size is 8x8, but the size is in units of bits. + * for bytes, do just * 8. */ + uint32_t bytes = track->nsamples * track->log_nsamples * 8 * (tile_max + 1); + + if (bytes + track->cb_color_frag_offset[i] > + radeon_bo_size(track->cb_color_frag_bo[i])) { + dev_warn(p->dev, "%s FMASK_TILE_MAX too large " + "(tile_max=%u, bytes=%u, offset=%ju, bo_size=%lu)\n", + __func__, tile_max, bytes, + (uintmax_t)track->cb_color_frag_offset[i], + radeon_bo_size(track->cb_color_frag_bo[i])); + return -EINVAL; + } + } + /* fall through */ + case V_0280A0_CLEAR_ENABLE: + { + uint32_t block_max = G_028100_CMASK_BLOCK_MAX(track->cb_color_mask[i]); + /* One block = 128x128 pixels, one 8x8 tile has 4 bits.. + * (128*128) / (8*8) / 2 = 128 bytes per block. */ + uint32_t bytes = (block_max + 1) * 128; + + if (bytes + track->cb_color_tile_offset[i] > + radeon_bo_size(track->cb_color_tile_bo[i])) { + dev_warn(p->dev, "%s CMASK_BLOCK_MAX too large " + "(block_max=%u, bytes=%u, offset=%ju, bo_size=%lu)\n", + __func__, block_max, bytes, + (uintmax_t)track->cb_color_tile_offset[i], + radeon_bo_size(track->cb_color_tile_bo[i])); + return -EINVAL; + } + break; + } + default: + dev_warn(p->dev, "%s invalid tile mode\n", __func__); + return -EINVAL; + } + return 0; +} + +static int r600_cs_track_validate_db(struct radeon_cs_parser *p) +{ + struct r600_cs_track *track = p->track; + u32 nviews, bpe, ntiles, size, slice_tile_max, tmp; + u32 height_align, pitch_align, depth_align; + u32 pitch = 8192; + u32 height = 8192; + u64 base_offset, base_align; + struct array_mode_checker array_check; + int array_mode; + volatile u32 *ib = p->ib.ptr; + + + if (track->db_bo == NULL) { + dev_warn(p->dev, "z/stencil with no depth buffer\n"); + return -EINVAL; + } + switch (G_028010_FORMAT(track->db_depth_info)) { + case V_028010_DEPTH_16: + bpe = 2; + break; + case V_028010_DEPTH_X8_24: + case V_028010_DEPTH_8_24: + case V_028010_DEPTH_X8_24_FLOAT: + case V_028010_DEPTH_8_24_FLOAT: + case V_028010_DEPTH_32_FLOAT: + bpe = 4; + break; + case V_028010_DEPTH_X24_8_32_FLOAT: + bpe = 8; + break; + default: + dev_warn(p->dev, "z/stencil with invalid format %d\n", G_028010_FORMAT(track->db_depth_info)); + return -EINVAL; + } + if ((track->db_depth_size & 0xFFFFFC00) == 0xFFFFFC00) { + if (!track->db_depth_size_idx) { + dev_warn(p->dev, "z/stencil buffer size not set\n"); + return -EINVAL; + } + tmp = radeon_bo_size(track->db_bo) - track->db_offset; + tmp = (tmp / bpe) >> 6; + if (!tmp) { + dev_warn(p->dev, "z/stencil buffer too small (0x%08X %d %d %ld)\n", + track->db_depth_size, bpe, track->db_offset, + radeon_bo_size(track->db_bo)); + return -EINVAL; + } + ib[track->db_depth_size_idx] = S_028000_SLICE_TILE_MAX(tmp - 1) | (track->db_depth_size & 0x3FF); + } else { + size = radeon_bo_size(track->db_bo); + /* pitch in pixels */ + pitch = (G_028000_PITCH_TILE_MAX(track->db_depth_size) + 1) * 8; + slice_tile_max = G_028000_SLICE_TILE_MAX(track->db_depth_size) + 1; + slice_tile_max *= 64; + height = slice_tile_max / pitch; + if (height > 8192) + height = 8192; + base_offset = track->db_bo_mc + track->db_offset; + array_mode = G_028010_ARRAY_MODE(track->db_depth_info); + array_check.array_mode = array_mode; + array_check.group_size = track->group_size; + array_check.nbanks = track->nbanks; + array_check.npipes = track->npipes; + array_check.nsamples = track->nsamples; + array_check.blocksize = bpe; + if (r600_get_array_mode_alignment(&array_check, + &pitch_align, &height_align, &depth_align, &base_align)) { + dev_warn(p->dev, "%s invalid tiling %d (0x%08X)\n", __func__, + G_028010_ARRAY_MODE(track->db_depth_info), + track->db_depth_info); + return -EINVAL; + } + switch (array_mode) { + case V_028010_ARRAY_1D_TILED_THIN1: + /* don't break userspace */ + height &= ~0x7; + break; + case V_028010_ARRAY_2D_TILED_THIN1: + break; + default: + dev_warn(p->dev, "%s invalid tiling %d (0x%08X)\n", __func__, + G_028010_ARRAY_MODE(track->db_depth_info), + track->db_depth_info); + return -EINVAL; + } + + if (!IS_ALIGNED(pitch, pitch_align)) { + dev_warn(p->dev, "%s:%d db pitch (%d, 0x%x, %d) invalid\n", + __func__, __LINE__, pitch, pitch_align, array_mode); + return -EINVAL; + } + if (!IS_ALIGNED(height, height_align)) { + dev_warn(p->dev, "%s:%d db height (%d, 0x%x, %d) invalid\n", + __func__, __LINE__, height, height_align, array_mode); + return -EINVAL; + } + if (!IS_ALIGNED(base_offset, base_align)) { + dev_warn(p->dev, "%s offset 0x%jx, 0x%jx, %d not aligned\n", __func__, + (uintmax_t)base_offset, (uintmax_t)base_align, array_mode); + return -EINVAL; + } + + ntiles = G_028000_SLICE_TILE_MAX(track->db_depth_size) + 1; + nviews = G_028004_SLICE_MAX(track->db_depth_view) + 1; + tmp = ntiles * bpe * 64 * nviews * track->nsamples; + if ((tmp + track->db_offset) > radeon_bo_size(track->db_bo)) { + dev_warn(p->dev, "z/stencil buffer (%d) too small (0x%08X %d %d %d -> %u have %lu)\n", + array_mode, + track->db_depth_size, ntiles, nviews, bpe, tmp + track->db_offset, + radeon_bo_size(track->db_bo)); + return -EINVAL; + } + } + + /* hyperz */ + if (G_028010_TILE_SURFACE_ENABLE(track->db_depth_info)) { + unsigned long size; + unsigned nbx, nby; + + if (track->htile_bo == NULL) { + dev_warn(p->dev, "%s:%d htile enabled without htile surface 0x%08x\n", + __func__, __LINE__, track->db_depth_info); + return -EINVAL; + } + if ((track->db_depth_size & 0xFFFFFC00) == 0xFFFFFC00) { + dev_warn(p->dev, "%s:%d htile can't be enabled with bogus db_depth_size 0x%08x\n", + __func__, __LINE__, track->db_depth_size); + return -EINVAL; + } + + nbx = pitch; + nby = height; + if (G_028D24_LINEAR(track->htile_surface)) { + /* nbx must be 16 htiles aligned == 16 * 8 pixel aligned */ + nbx = roundup2(nbx, 16 * 8); + /* nby is npipes htiles aligned == npipes * 8 pixel aligned */ + nby = roundup(nby, track->npipes * 8); + } else { + /* always assume 8x8 htile */ + /* align is htile align * 8, htile align vary according to + * number of pipe and tile width and nby + */ + switch (track->npipes) { + case 8: + /* HTILE_WIDTH = 8 & HTILE_HEIGHT = 8*/ + nbx = roundup2(nbx, 64 * 8); + nby = roundup2(nby, 64 * 8); + break; + case 4: + /* HTILE_WIDTH = 8 & HTILE_HEIGHT = 8*/ + nbx = roundup2(nbx, 64 * 8); + nby = roundup2(nby, 32 * 8); + break; + case 2: + /* HTILE_WIDTH = 8 & HTILE_HEIGHT = 8*/ + nbx = roundup2(nbx, 32 * 8); + nby = roundup2(nby, 32 * 8); + break; + case 1: + /* HTILE_WIDTH = 8 & HTILE_HEIGHT = 8*/ + nbx = roundup2(nbx, 32 * 8); + nby = roundup2(nby, 16 * 8); + break; + default: + dev_warn(p->dev, "%s:%d invalid num pipes %d\n", + __func__, __LINE__, track->npipes); + return -EINVAL; + } + } + /* compute number of htile */ + nbx = nbx >> 3; + nby = nby >> 3; + /* size must be aligned on npipes * 2K boundary */ + size = roundup(nbx * nby * 4, track->npipes * (2 << 10)); + size += track->htile_offset; + + if (size > radeon_bo_size(track->htile_bo)) { + dev_warn(p->dev, "%s:%d htile surface too small %ld for %ld (%d %d)\n", + __func__, __LINE__, radeon_bo_size(track->htile_bo), + size, nbx, nby); + return -EINVAL; + } + } + + track->db_dirty = false; + return 0; +} + +static int r600_cs_track_check(struct radeon_cs_parser *p) +{ + struct r600_cs_track *track = p->track; + u32 tmp; + int r, i; + + /* on legacy kernel we don't perform advanced check */ + if (p->rdev == NULL) + return 0; + + /* check streamout */ + if (track->streamout_dirty && track->vgt_strmout_en) { + for (i = 0; i < 4; i++) { + if (track->vgt_strmout_buffer_en & (1 << i)) { + if (track->vgt_strmout_bo[i]) { + u64 offset = (u64)track->vgt_strmout_bo_offset[i] + + (u64)track->vgt_strmout_size[i]; + if (offset > radeon_bo_size(track->vgt_strmout_bo[i])) { + DRM_ERROR("streamout %d bo too small: 0x%jx, 0x%lx\n", + i, (uintmax_t)offset, + radeon_bo_size(track->vgt_strmout_bo[i])); + return -EINVAL; + } + } else { + dev_warn(p->dev, "No buffer for streamout %d\n", i); + return -EINVAL; + } + } + } + track->streamout_dirty = false; + } + + if (track->sx_misc_kill_all_prims) + return 0; + + /* check that we have a cb for each enabled target, we don't check + * shader_mask because it seems mesa isn't always setting it :( + */ + if (track->cb_dirty) { + tmp = track->cb_target_mask; + + /* We must check both colorbuffers for RESOLVE. */ + if (track->is_resolve) { + tmp |= 0xff; + } + + for (i = 0; i < 8; i++) { + if ((tmp >> (i * 4)) & 0xF) { + /* at least one component is enabled */ + if (track->cb_color_bo[i] == NULL) { + dev_warn(p->dev, "%s:%d mask 0x%08X | 0x%08X no cb for %d\n", + __func__, __LINE__, track->cb_target_mask, track->cb_shader_mask, i); + return -EINVAL; + } + /* perform rewrite of CB_COLOR[0-7]_SIZE */ + r = r600_cs_track_validate_cb(p, i); + if (r) + return r; + } + } + track->cb_dirty = false; + } + + /* Check depth buffer */ + if (track->db_dirty && + G_028010_FORMAT(track->db_depth_info) != V_028010_DEPTH_INVALID && + (G_028800_STENCIL_ENABLE(track->db_depth_control) || + G_028800_Z_ENABLE(track->db_depth_control))) { + r = r600_cs_track_validate_db(p); + if (r) + return r; + } + + return 0; +} + +/** + * r600_cs_packet_parse() - parse cp packet and point ib index to next packet + * @parser: parser structure holding parsing context. + * @pkt: where to store packet informations + * + * Assume that chunk_ib_index is properly set. Will return -EINVAL + * if packet is bigger than remaining ib size. or if packets is unknown. + **/ +static int r600_cs_packet_parse(struct radeon_cs_parser *p, + struct radeon_cs_packet *pkt, + unsigned idx) +{ + struct radeon_cs_chunk *ib_chunk = &p->chunks[p->chunk_ib_idx]; + uint32_t header; + + if (idx >= ib_chunk->length_dw) { + DRM_ERROR("Can not parse packet at %d after CS end %d !\n", + idx, ib_chunk->length_dw); + return -EINVAL; + } + header = radeon_get_ib_value(p, idx); + pkt->idx = idx; + pkt->type = CP_PACKET_GET_TYPE(header); + pkt->count = CP_PACKET_GET_COUNT(header); + pkt->one_reg_wr = 0; + switch (pkt->type) { + case PACKET_TYPE0: + pkt->reg = CP_PACKET0_GET_REG(header); + break; + case PACKET_TYPE3: + pkt->opcode = CP_PACKET3_GET_OPCODE(header); + break; + case PACKET_TYPE2: + pkt->count = -1; + break; + default: + DRM_ERROR("Unknown packet type %d at %d !\n", pkt->type, idx); + return -EINVAL; + } + if ((pkt->count + 1 + pkt->idx) >= ib_chunk->length_dw) { + DRM_ERROR("Packet (%d:%d:%d) end after CS buffer (%d) !\n", + pkt->idx, pkt->type, pkt->count, ib_chunk->length_dw); + return -EINVAL; + } + return 0; +} + +/** + * r600_cs_packet_next_reloc_mm() - parse next packet which should be reloc packet3 + * @parser: parser structure holding parsing context. + * @data: pointer to relocation data + * @offset_start: starting offset + * @offset_mask: offset mask (to align start offset on) + * @reloc: reloc informations + * + * Check next packet is relocation packet3, do bo validation and compute + * GPU offset using the provided start. + **/ +static int r600_cs_packet_next_reloc_mm(struct radeon_cs_parser *p, + struct radeon_cs_reloc **cs_reloc) +{ + struct radeon_cs_chunk *relocs_chunk; + struct radeon_cs_packet p3reloc; + unsigned idx; + int r; + + if (p->chunk_relocs_idx == -1) { + DRM_ERROR("No relocation chunk !\n"); + return -EINVAL; + } + *cs_reloc = NULL; + relocs_chunk = &p->chunks[p->chunk_relocs_idx]; + r = r600_cs_packet_parse(p, &p3reloc, p->idx); + if (r) { + return r; + } + p->idx += p3reloc.count + 2; + if (p3reloc.type != PACKET_TYPE3 || p3reloc.opcode != PACKET3_NOP) { + DRM_ERROR("No packet3 for relocation for packet at %d.\n", + p3reloc.idx); + return -EINVAL; + } + idx = radeon_get_ib_value(p, p3reloc.idx + 1); + if (idx >= relocs_chunk->length_dw) { + DRM_ERROR("Relocs at %d after relocations chunk end %d !\n", + idx, relocs_chunk->length_dw); + return -EINVAL; + } + /* FIXME: we assume reloc size is 4 dwords */ + *cs_reloc = p->relocs_ptr[(idx / 4)]; + return 0; +} + +/** + * r600_cs_packet_next_reloc_nomm() - parse next packet which should be reloc packet3 + * @parser: parser structure holding parsing context. + * @data: pointer to relocation data + * @offset_start: starting offset + * @offset_mask: offset mask (to align start offset on) + * @reloc: reloc informations + * + * Check next packet is relocation packet3, do bo validation and compute + * GPU offset using the provided start. + **/ +static int r600_cs_packet_next_reloc_nomm(struct radeon_cs_parser *p, + struct radeon_cs_reloc **cs_reloc) +{ + struct radeon_cs_chunk *relocs_chunk; + struct radeon_cs_packet p3reloc; + unsigned idx; + int r; + + if (p->chunk_relocs_idx == -1) { + DRM_ERROR("No relocation chunk !\n"); + return -EINVAL; + } + *cs_reloc = NULL; + relocs_chunk = &p->chunks[p->chunk_relocs_idx]; + r = r600_cs_packet_parse(p, &p3reloc, p->idx); + if (r) { + return r; + } + p->idx += p3reloc.count + 2; + if (p3reloc.type != PACKET_TYPE3 || p3reloc.opcode != PACKET3_NOP) { + DRM_ERROR("No packet3 for relocation for packet at %d.\n", + p3reloc.idx); + return -EINVAL; + } + idx = radeon_get_ib_value(p, p3reloc.idx + 1); + if (idx >= relocs_chunk->length_dw) { + DRM_ERROR("Relocs at %d after relocations chunk end %d !\n", + idx, relocs_chunk->length_dw); + return -EINVAL; + } + *cs_reloc = p->relocs; + (*cs_reloc)->lobj.gpu_offset = (u64)relocs_chunk->kdata[idx + 3] << 32; + (*cs_reloc)->lobj.gpu_offset |= relocs_chunk->kdata[idx + 0]; + return 0; +} + +/** + * r600_cs_packet_next_is_pkt3_nop() - test if next packet is packet3 nop for reloc + * @parser: parser structure holding parsing context. + * + * Check next packet is relocation packet3, do bo validation and compute + * GPU offset using the provided start. + **/ +static int r600_cs_packet_next_is_pkt3_nop(struct radeon_cs_parser *p) +{ + struct radeon_cs_packet p3reloc; + int r; + + r = r600_cs_packet_parse(p, &p3reloc, p->idx); + if (r) { + return 0; + } + if (p3reloc.type != PACKET_TYPE3 || p3reloc.opcode != PACKET3_NOP) { + return 0; + } + return 1; +} + +/** + * r600_cs_packet_next_vline() - parse userspace VLINE packet + * @parser: parser structure holding parsing context. + * + * Userspace sends a special sequence for VLINE waits. + * PACKET0 - VLINE_START_END + value + * PACKET3 - WAIT_REG_MEM poll vline status reg + * RELOC (P3) - crtc_id in reloc. + * + * This function parses this and relocates the VLINE START END + * and WAIT_REG_MEM packets to the correct crtc. + * It also detects a switched off crtc and nulls out the + * wait in that case. + */ +static int r600_cs_packet_parse_vline(struct radeon_cs_parser *p) +{ + struct drm_mode_object *obj; + struct drm_crtc *crtc; + struct radeon_crtc *radeon_crtc; + struct radeon_cs_packet p3reloc, wait_reg_mem; + int crtc_id; + int r; + uint32_t header, h_idx, reg, wait_reg_mem_info; + volatile uint32_t *ib; + + ib = p->ib.ptr; + + /* parse the WAIT_REG_MEM */ + r = r600_cs_packet_parse(p, &wait_reg_mem, p->idx); + if (r) + return r; + + /* check its a WAIT_REG_MEM */ + if (wait_reg_mem.type != PACKET_TYPE3 || + wait_reg_mem.opcode != PACKET3_WAIT_REG_MEM) { + DRM_ERROR("vline wait missing WAIT_REG_MEM segment\n"); + return -EINVAL; + } + + wait_reg_mem_info = radeon_get_ib_value(p, wait_reg_mem.idx + 1); + /* bit 4 is reg (0) or mem (1) */ + if (wait_reg_mem_info & 0x10) { + DRM_ERROR("vline WAIT_REG_MEM waiting on MEM rather than REG\n"); + return -EINVAL; + } + /* waiting for value to be equal */ + if ((wait_reg_mem_info & 0x7) != 0x3) { + DRM_ERROR("vline WAIT_REG_MEM function not equal\n"); + return -EINVAL; + } + if ((radeon_get_ib_value(p, wait_reg_mem.idx + 2) << 2) != AVIVO_D1MODE_VLINE_STATUS) { + DRM_ERROR("vline WAIT_REG_MEM bad reg\n"); + return -EINVAL; + } + + if (radeon_get_ib_value(p, wait_reg_mem.idx + 5) != AVIVO_D1MODE_VLINE_STAT) { + DRM_ERROR("vline WAIT_REG_MEM bad bit mask\n"); + return -EINVAL; + } + + /* jump over the NOP */ + r = r600_cs_packet_parse(p, &p3reloc, p->idx + wait_reg_mem.count + 2); + if (r) + return r; + + h_idx = p->idx - 2; + p->idx += wait_reg_mem.count + 2; + p->idx += p3reloc.count + 2; + + header = radeon_get_ib_value(p, h_idx); + crtc_id = radeon_get_ib_value(p, h_idx + 2 + 7 + 1); + reg = CP_PACKET0_GET_REG(header); + + obj = drm_mode_object_find(p->rdev->ddev, crtc_id, DRM_MODE_OBJECT_CRTC); + if (!obj) { + DRM_ERROR("cannot find crtc %d\n", crtc_id); + return -EINVAL; + } + crtc = obj_to_crtc(obj); + radeon_crtc = to_radeon_crtc(crtc); + crtc_id = radeon_crtc->crtc_id; + + if (!crtc->enabled) { + /* if the CRTC isn't enabled - we need to nop out the WAIT_REG_MEM */ + ib[h_idx + 2] = PACKET2(0); + ib[h_idx + 3] = PACKET2(0); + ib[h_idx + 4] = PACKET2(0); + ib[h_idx + 5] = PACKET2(0); + ib[h_idx + 6] = PACKET2(0); + ib[h_idx + 7] = PACKET2(0); + ib[h_idx + 8] = PACKET2(0); + } else if (crtc_id == 1) { + switch (reg) { + case AVIVO_D1MODE_VLINE_START_END: + header &= ~R600_CP_PACKET0_REG_MASK; + header |= AVIVO_D2MODE_VLINE_START_END >> 2; + break; + default: + DRM_ERROR("unknown crtc reloc\n"); + return -EINVAL; + } + ib[h_idx] = header; + ib[h_idx + 4] = AVIVO_D2MODE_VLINE_STATUS >> 2; + } + + return 0; +} + +static int r600_packet0_check(struct radeon_cs_parser *p, + struct radeon_cs_packet *pkt, + unsigned idx, unsigned reg) +{ + int r; + + switch (reg) { + case AVIVO_D1MODE_VLINE_START_END: + r = r600_cs_packet_parse_vline(p); + if (r) { + DRM_ERROR("No reloc for ib[%d]=0x%04X\n", + idx, reg); + return r; + } + break; + default: + DRM_ERROR("Forbidden register 0x%04X in cs at %d\n", + reg, idx); + return -EINVAL; + } + return 0; +} + +static int r600_cs_parse_packet0(struct radeon_cs_parser *p, + struct radeon_cs_packet *pkt) +{ + unsigned reg, i; + unsigned idx; + int r; + + idx = pkt->idx + 1; + reg = pkt->reg; + for (i = 0; i <= pkt->count; i++, idx++, reg += 4) { + r = r600_packet0_check(p, pkt, idx, reg); + if (r) { + return r; + } + } + return 0; +} + +/** + * r600_cs_check_reg() - check if register is authorized or not + * @parser: parser structure holding parsing context + * @reg: register we are testing + * @idx: index into the cs buffer + * + * This function will test against r600_reg_safe_bm and return 0 + * if register is safe. If register is not flag as safe this function + * will test it against a list of register needind special handling. + */ +static int r600_cs_check_reg(struct radeon_cs_parser *p, u32 reg, u32 idx) +{ + struct r600_cs_track *track = (struct r600_cs_track *)p->track; + struct radeon_cs_reloc *reloc; + u32 m, i, tmp, *ib; + int r; + + i = (reg >> 7); + if (i >= DRM_ARRAY_SIZE(r600_reg_safe_bm)) { + dev_warn(p->dev, "forbidden register 0x%08x at %d\n", reg, idx); + return -EINVAL; + } + m = 1 << ((reg >> 2) & 31); + if (!(r600_reg_safe_bm[i] & m)) + return 0; + ib = p->ib.ptr; + switch (reg) { + /* force following reg to 0 in an attempt to disable out buffer + * which will need us to better understand how it works to perform + * security check on it (Jerome) + */ + case R_0288A8_SQ_ESGS_RING_ITEMSIZE: + case R_008C44_SQ_ESGS_RING_SIZE: + case R_0288B0_SQ_ESTMP_RING_ITEMSIZE: + case R_008C54_SQ_ESTMP_RING_SIZE: + case R_0288C0_SQ_FBUF_RING_ITEMSIZE: + case R_008C74_SQ_FBUF_RING_SIZE: + case R_0288B4_SQ_GSTMP_RING_ITEMSIZE: + case R_008C5C_SQ_GSTMP_RING_SIZE: + case R_0288AC_SQ_GSVS_RING_ITEMSIZE: + case R_008C4C_SQ_GSVS_RING_SIZE: + case R_0288BC_SQ_PSTMP_RING_ITEMSIZE: + case R_008C6C_SQ_PSTMP_RING_SIZE: + case R_0288C4_SQ_REDUC_RING_ITEMSIZE: + case R_008C7C_SQ_REDUC_RING_SIZE: + case R_0288B8_SQ_VSTMP_RING_ITEMSIZE: + case R_008C64_SQ_VSTMP_RING_SIZE: + case R_0288C8_SQ_GS_VERT_ITEMSIZE: + /* get value to populate the IB don't remove */ + tmp =radeon_get_ib_value(p, idx); + ib[idx] = 0; + break; + case SQ_CONFIG: + track->sq_config = radeon_get_ib_value(p, idx); + break; + case R_028800_DB_DEPTH_CONTROL: + track->db_depth_control = radeon_get_ib_value(p, idx); + track->db_dirty = true; + break; + case R_028010_DB_DEPTH_INFO: + if (!(p->cs_flags & RADEON_CS_KEEP_TILING_FLAGS) && + r600_cs_packet_next_is_pkt3_nop(p)) { + r = r600_cs_packet_next_reloc(p, &reloc); + if (r) { + dev_warn(p->dev, "bad SET_CONTEXT_REG " + "0x%04X\n", reg); + return -EINVAL; + } + track->db_depth_info = radeon_get_ib_value(p, idx); + ib[idx] &= C_028010_ARRAY_MODE; + track->db_depth_info &= C_028010_ARRAY_MODE; + if (reloc->lobj.tiling_flags & RADEON_TILING_MACRO) { + ib[idx] |= S_028010_ARRAY_MODE(V_028010_ARRAY_2D_TILED_THIN1); + track->db_depth_info |= S_028010_ARRAY_MODE(V_028010_ARRAY_2D_TILED_THIN1); + } else { + ib[idx] |= S_028010_ARRAY_MODE(V_028010_ARRAY_1D_TILED_THIN1); + track->db_depth_info |= S_028010_ARRAY_MODE(V_028010_ARRAY_1D_TILED_THIN1); + } + } else { + track->db_depth_info = radeon_get_ib_value(p, idx); + } + track->db_dirty = true; + break; + case R_028004_DB_DEPTH_VIEW: + track->db_depth_view = radeon_get_ib_value(p, idx); + track->db_dirty = true; + break; + case R_028000_DB_DEPTH_SIZE: + track->db_depth_size = radeon_get_ib_value(p, idx); + track->db_depth_size_idx = idx; + track->db_dirty = true; + break; + case R_028AB0_VGT_STRMOUT_EN: + track->vgt_strmout_en = radeon_get_ib_value(p, idx); + track->streamout_dirty = true; + break; + case R_028B20_VGT_STRMOUT_BUFFER_EN: + track->vgt_strmout_buffer_en = radeon_get_ib_value(p, idx); + track->streamout_dirty = true; + break; + case VGT_STRMOUT_BUFFER_BASE_0: + case VGT_STRMOUT_BUFFER_BASE_1: + case VGT_STRMOUT_BUFFER_BASE_2: + case VGT_STRMOUT_BUFFER_BASE_3: + r = r600_cs_packet_next_reloc(p, &reloc); + if (r) { + dev_warn(p->dev, "bad SET_CONTEXT_REG " + "0x%04X\n", reg); + return -EINVAL; + } + tmp = (reg - VGT_STRMOUT_BUFFER_BASE_0) / 16; + track->vgt_strmout_bo_offset[tmp] = radeon_get_ib_value(p, idx) << 8; + ib[idx] += (u32)((reloc->lobj.gpu_offset >> 8) & 0xffffffff); + track->vgt_strmout_bo[tmp] = reloc->robj; + track->vgt_strmout_bo_mc[tmp] = reloc->lobj.gpu_offset; + track->streamout_dirty = true; + break; + case VGT_STRMOUT_BUFFER_SIZE_0: + case VGT_STRMOUT_BUFFER_SIZE_1: + case VGT_STRMOUT_BUFFER_SIZE_2: + case VGT_STRMOUT_BUFFER_SIZE_3: + tmp = (reg - VGT_STRMOUT_BUFFER_SIZE_0) / 16; + /* size in register is DWs, convert to bytes */ + track->vgt_strmout_size[tmp] = radeon_get_ib_value(p, idx) * 4; + track->streamout_dirty = true; + break; + case CP_COHER_BASE: + r = r600_cs_packet_next_reloc(p, &reloc); + if (r) { + dev_warn(p->dev, "missing reloc for CP_COHER_BASE " + "0x%04X\n", reg); + return -EINVAL; + } + ib[idx] += (u32)((reloc->lobj.gpu_offset >> 8) & 0xffffffff); + break; + case R_028238_CB_TARGET_MASK: + track->cb_target_mask = radeon_get_ib_value(p, idx); + track->cb_dirty = true; + break; + case R_02823C_CB_SHADER_MASK: + track->cb_shader_mask = radeon_get_ib_value(p, idx); + break; + case R_028C04_PA_SC_AA_CONFIG: + tmp = G_028C04_MSAA_NUM_SAMPLES(radeon_get_ib_value(p, idx)); + track->log_nsamples = tmp; + track->nsamples = 1 << tmp; + track->cb_dirty = true; + break; + case R_028808_CB_COLOR_CONTROL: + tmp = G_028808_SPECIAL_OP(radeon_get_ib_value(p, idx)); + track->is_resolve = tmp == V_028808_SPECIAL_RESOLVE_BOX; + track->cb_dirty = true; + break; + case R_0280A0_CB_COLOR0_INFO: + case R_0280A4_CB_COLOR1_INFO: + case R_0280A8_CB_COLOR2_INFO: + case R_0280AC_CB_COLOR3_INFO: + case R_0280B0_CB_COLOR4_INFO: + case R_0280B4_CB_COLOR5_INFO: + case R_0280B8_CB_COLOR6_INFO: + case R_0280BC_CB_COLOR7_INFO: + if (!(p->cs_flags & RADEON_CS_KEEP_TILING_FLAGS) && + r600_cs_packet_next_is_pkt3_nop(p)) { + r = r600_cs_packet_next_reloc(p, &reloc); + if (r) { + dev_err(p->dev, "bad SET_CONTEXT_REG 0x%04X\n", reg); + return -EINVAL; + } + tmp = (reg - R_0280A0_CB_COLOR0_INFO) / 4; + track->cb_color_info[tmp] = radeon_get_ib_value(p, idx); + if (reloc->lobj.tiling_flags & RADEON_TILING_MACRO) { + ib[idx] |= S_0280A0_ARRAY_MODE(V_0280A0_ARRAY_2D_TILED_THIN1); + track->cb_color_info[tmp] |= S_0280A0_ARRAY_MODE(V_0280A0_ARRAY_2D_TILED_THIN1); + } else if (reloc->lobj.tiling_flags & RADEON_TILING_MICRO) { + ib[idx] |= S_0280A0_ARRAY_MODE(V_0280A0_ARRAY_1D_TILED_THIN1); + track->cb_color_info[tmp] |= S_0280A0_ARRAY_MODE(V_0280A0_ARRAY_1D_TILED_THIN1); + } + } else { + tmp = (reg - R_0280A0_CB_COLOR0_INFO) / 4; + track->cb_color_info[tmp] = radeon_get_ib_value(p, idx); + } + track->cb_dirty = true; + break; + case R_028080_CB_COLOR0_VIEW: + case R_028084_CB_COLOR1_VIEW: + case R_028088_CB_COLOR2_VIEW: + case R_02808C_CB_COLOR3_VIEW: + case R_028090_CB_COLOR4_VIEW: + case R_028094_CB_COLOR5_VIEW: + case R_028098_CB_COLOR6_VIEW: + case R_02809C_CB_COLOR7_VIEW: + tmp = (reg - R_028080_CB_COLOR0_VIEW) / 4; + track->cb_color_view[tmp] = radeon_get_ib_value(p, idx); + track->cb_dirty = true; + break; + case R_028060_CB_COLOR0_SIZE: + case R_028064_CB_COLOR1_SIZE: + case R_028068_CB_COLOR2_SIZE: + case R_02806C_CB_COLOR3_SIZE: + case R_028070_CB_COLOR4_SIZE: + case R_028074_CB_COLOR5_SIZE: + case R_028078_CB_COLOR6_SIZE: + case R_02807C_CB_COLOR7_SIZE: + tmp = (reg - R_028060_CB_COLOR0_SIZE) / 4; + track->cb_color_size[tmp] = radeon_get_ib_value(p, idx); + track->cb_color_size_idx[tmp] = idx; + track->cb_dirty = true; + break; + /* This register were added late, there is userspace + * which does provide relocation for those but set + * 0 offset. In order to avoid breaking old userspace + * we detect this and set address to point to last + * CB_COLOR0_BASE, note that if userspace doesn't set + * CB_COLOR0_BASE before this register we will report + * error. Old userspace always set CB_COLOR0_BASE + * before any of this. + */ + case R_0280E0_CB_COLOR0_FRAG: + case R_0280E4_CB_COLOR1_FRAG: + case R_0280E8_CB_COLOR2_FRAG: + case R_0280EC_CB_COLOR3_FRAG: + case R_0280F0_CB_COLOR4_FRAG: + case R_0280F4_CB_COLOR5_FRAG: + case R_0280F8_CB_COLOR6_FRAG: + case R_0280FC_CB_COLOR7_FRAG: + tmp = (reg - R_0280E0_CB_COLOR0_FRAG) / 4; + if (!r600_cs_packet_next_is_pkt3_nop(p)) { + if (!track->cb_color_base_last[tmp]) { + dev_err(p->dev, "Broken old userspace ? no cb_color0_base supplied before trying to write 0x%08X\n", reg); + return -EINVAL; + } + track->cb_color_frag_bo[tmp] = track->cb_color_bo[tmp]; + track->cb_color_frag_offset[tmp] = track->cb_color_bo_offset[tmp]; + ib[idx] = track->cb_color_base_last[tmp]; + } else { + r = r600_cs_packet_next_reloc(p, &reloc); + if (r) { + dev_err(p->dev, "bad SET_CONTEXT_REG 0x%04X\n", reg); + return -EINVAL; + } + track->cb_color_frag_bo[tmp] = reloc->robj; + track->cb_color_frag_offset[tmp] = (u64)ib[idx] << 8; + ib[idx] += (u32)((reloc->lobj.gpu_offset >> 8) & 0xffffffff); + } + if (G_0280A0_TILE_MODE(track->cb_color_info[tmp])) { + track->cb_dirty = true; + } + break; + case R_0280C0_CB_COLOR0_TILE: + case R_0280C4_CB_COLOR1_TILE: + case R_0280C8_CB_COLOR2_TILE: + case R_0280CC_CB_COLOR3_TILE: + case R_0280D0_CB_COLOR4_TILE: + case R_0280D4_CB_COLOR5_TILE: + case R_0280D8_CB_COLOR6_TILE: + case R_0280DC_CB_COLOR7_TILE: + tmp = (reg - R_0280C0_CB_COLOR0_TILE) / 4; + if (!r600_cs_packet_next_is_pkt3_nop(p)) { + if (!track->cb_color_base_last[tmp]) { + dev_err(p->dev, "Broken old userspace ? no cb_color0_base supplied before trying to write 0x%08X\n", reg); + return -EINVAL; + } + track->cb_color_tile_bo[tmp] = track->cb_color_bo[tmp]; + track->cb_color_tile_offset[tmp] = track->cb_color_bo_offset[tmp]; + ib[idx] = track->cb_color_base_last[tmp]; + } else { + r = r600_cs_packet_next_reloc(p, &reloc); + if (r) { + dev_err(p->dev, "bad SET_CONTEXT_REG 0x%04X\n", reg); + return -EINVAL; + } + track->cb_color_tile_bo[tmp] = reloc->robj; + track->cb_color_tile_offset[tmp] = (u64)ib[idx] << 8; + ib[idx] += (u32)((reloc->lobj.gpu_offset >> 8) & 0xffffffff); + } + if (G_0280A0_TILE_MODE(track->cb_color_info[tmp])) { + track->cb_dirty = true; + } + break; + case R_028100_CB_COLOR0_MASK: + case R_028104_CB_COLOR1_MASK: + case R_028108_CB_COLOR2_MASK: + case R_02810C_CB_COLOR3_MASK: + case R_028110_CB_COLOR4_MASK: + case R_028114_CB_COLOR5_MASK: + case R_028118_CB_COLOR6_MASK: + case R_02811C_CB_COLOR7_MASK: + tmp = (reg - R_028100_CB_COLOR0_MASK) / 4; + track->cb_color_mask[tmp] = radeon_get_ib_value(p, idx); + if (G_0280A0_TILE_MODE(track->cb_color_info[tmp])) { + track->cb_dirty = true; + } + break; + case CB_COLOR0_BASE: + case CB_COLOR1_BASE: + case CB_COLOR2_BASE: + case CB_COLOR3_BASE: + case CB_COLOR4_BASE: + case CB_COLOR5_BASE: + case CB_COLOR6_BASE: + case CB_COLOR7_BASE: + r = r600_cs_packet_next_reloc(p, &reloc); + if (r) { + dev_warn(p->dev, "bad SET_CONTEXT_REG " + "0x%04X\n", reg); + return -EINVAL; + } + tmp = (reg - CB_COLOR0_BASE) / 4; + track->cb_color_bo_offset[tmp] = radeon_get_ib_value(p, idx) << 8; + ib[idx] += (u32)((reloc->lobj.gpu_offset >> 8) & 0xffffffff); + track->cb_color_base_last[tmp] = ib[idx]; + track->cb_color_bo[tmp] = reloc->robj; + track->cb_color_bo_mc[tmp] = reloc->lobj.gpu_offset; + track->cb_dirty = true; + break; + case DB_DEPTH_BASE: + r = r600_cs_packet_next_reloc(p, &reloc); + if (r) { + dev_warn(p->dev, "bad SET_CONTEXT_REG " + "0x%04X\n", reg); + return -EINVAL; + } + track->db_offset = radeon_get_ib_value(p, idx) << 8; + ib[idx] += (u32)((reloc->lobj.gpu_offset >> 8) & 0xffffffff); + track->db_bo = reloc->robj; + track->db_bo_mc = reloc->lobj.gpu_offset; + track->db_dirty = true; + break; + case DB_HTILE_DATA_BASE: + r = r600_cs_packet_next_reloc(p, &reloc); + if (r) { + dev_warn(p->dev, "bad SET_CONTEXT_REG " + "0x%04X\n", reg); + return -EINVAL; + } + track->htile_offset = radeon_get_ib_value(p, idx) << 8; + ib[idx] += (u32)((reloc->lobj.gpu_offset >> 8) & 0xffffffff); + track->htile_bo = reloc->robj; + track->db_dirty = true; + break; + case DB_HTILE_SURFACE: + track->htile_surface = radeon_get_ib_value(p, idx); + /* force 8x8 htile width and height */ + ib[idx] |= 3; + track->db_dirty = true; + break; + case SQ_PGM_START_FS: + case SQ_PGM_START_ES: + case SQ_PGM_START_VS: + case SQ_PGM_START_GS: + case SQ_PGM_START_PS: + case SQ_ALU_CONST_CACHE_GS_0: + case SQ_ALU_CONST_CACHE_GS_1: + case SQ_ALU_CONST_CACHE_GS_2: + case SQ_ALU_CONST_CACHE_GS_3: + case SQ_ALU_CONST_CACHE_GS_4: + case SQ_ALU_CONST_CACHE_GS_5: + case SQ_ALU_CONST_CACHE_GS_6: + case SQ_ALU_CONST_CACHE_GS_7: + case SQ_ALU_CONST_CACHE_GS_8: + case SQ_ALU_CONST_CACHE_GS_9: + case SQ_ALU_CONST_CACHE_GS_10: + case SQ_ALU_CONST_CACHE_GS_11: + case SQ_ALU_CONST_CACHE_GS_12: + case SQ_ALU_CONST_CACHE_GS_13: + case SQ_ALU_CONST_CACHE_GS_14: + case SQ_ALU_CONST_CACHE_GS_15: + case SQ_ALU_CONST_CACHE_PS_0: + case SQ_ALU_CONST_CACHE_PS_1: + case SQ_ALU_CONST_CACHE_PS_2: + case SQ_ALU_CONST_CACHE_PS_3: + case SQ_ALU_CONST_CACHE_PS_4: + case SQ_ALU_CONST_CACHE_PS_5: + case SQ_ALU_CONST_CACHE_PS_6: + case SQ_ALU_CONST_CACHE_PS_7: + case SQ_ALU_CONST_CACHE_PS_8: + case SQ_ALU_CONST_CACHE_PS_9: + case SQ_ALU_CONST_CACHE_PS_10: + case SQ_ALU_CONST_CACHE_PS_11: + case SQ_ALU_CONST_CACHE_PS_12: + case SQ_ALU_CONST_CACHE_PS_13: + case SQ_ALU_CONST_CACHE_PS_14: + case SQ_ALU_CONST_CACHE_PS_15: + case SQ_ALU_CONST_CACHE_VS_0: + case SQ_ALU_CONST_CACHE_VS_1: + case SQ_ALU_CONST_CACHE_VS_2: + case SQ_ALU_CONST_CACHE_VS_3: + case SQ_ALU_CONST_CACHE_VS_4: + case SQ_ALU_CONST_CACHE_VS_5: + case SQ_ALU_CONST_CACHE_VS_6: + case SQ_ALU_CONST_CACHE_VS_7: + case SQ_ALU_CONST_CACHE_VS_8: + case SQ_ALU_CONST_CACHE_VS_9: + case SQ_ALU_CONST_CACHE_VS_10: + case SQ_ALU_CONST_CACHE_VS_11: + case SQ_ALU_CONST_CACHE_VS_12: + case SQ_ALU_CONST_CACHE_VS_13: + case SQ_ALU_CONST_CACHE_VS_14: + case SQ_ALU_CONST_CACHE_VS_15: + r = r600_cs_packet_next_reloc(p, &reloc); + if (r) { + dev_warn(p->dev, "bad SET_CONTEXT_REG " + "0x%04X\n", reg); + return -EINVAL; + } + ib[idx] += (u32)((reloc->lobj.gpu_offset >> 8) & 0xffffffff); + break; + case SX_MEMORY_EXPORT_BASE: + r = r600_cs_packet_next_reloc(p, &reloc); + if (r) { + dev_warn(p->dev, "bad SET_CONFIG_REG " + "0x%04X\n", reg); + return -EINVAL; + } + ib[idx] += (u32)((reloc->lobj.gpu_offset >> 8) & 0xffffffff); + break; + case SX_MISC: + track->sx_misc_kill_all_prims = (radeon_get_ib_value(p, idx) & 0x1) != 0; + break; + default: + dev_warn(p->dev, "forbidden register 0x%08x at %d\n", reg, idx); + return -EINVAL; + } + return 0; +} + +unsigned r600_mip_minify(unsigned size, unsigned level) +{ + unsigned val; + + val = max(1U, size >> level); + if (level > 0) + val = roundup_pow_of_two(val); + return val; +} + +static void r600_texture_size(unsigned nfaces, unsigned blevel, unsigned llevel, + unsigned w0, unsigned h0, unsigned d0, unsigned nsamples, unsigned format, + unsigned block_align, unsigned height_align, unsigned base_align, + unsigned *l0_size, unsigned *mipmap_size) +{ + unsigned offset, i, level; + unsigned width, height, depth, size; + unsigned blocksize; + unsigned nbx, nby; + unsigned nlevels = llevel - blevel + 1; + + *l0_size = -1; + blocksize = r600_fmt_get_blocksize(format); + + w0 = r600_mip_minify(w0, 0); + h0 = r600_mip_minify(h0, 0); + d0 = r600_mip_minify(d0, 0); + for(i = 0, offset = 0, level = blevel; i < nlevels; i++, level++) { + width = r600_mip_minify(w0, i); + nbx = r600_fmt_get_nblocksx(format, width); + + nbx = roundup(nbx, block_align); + + height = r600_mip_minify(h0, i); + nby = r600_fmt_get_nblocksy(format, height); + nby = roundup(nby, height_align); + + depth = r600_mip_minify(d0, i); + + size = nbx * nby * blocksize * nsamples; + if (nfaces) + size *= nfaces; + else + size *= depth; + + if (i == 0) + *l0_size = size; + + if (i == 0 || i == 1) + offset = roundup(offset, base_align); + + offset += size; + } + *mipmap_size = offset; + if (llevel == 0) + *mipmap_size = *l0_size; + if (!blevel) + *mipmap_size -= *l0_size; +} + +/** + * r600_check_texture_resource() - check if register is authorized or not + * @p: parser structure holding parsing context + * @idx: index into the cs buffer + * @texture: texture's bo structure + * @mipmap: mipmap's bo structure + * + * This function will check that the resource has valid field and that + * the texture and mipmap bo object are big enough to cover this resource. + */ +static int r600_check_texture_resource(struct radeon_cs_parser *p, u32 idx, + struct radeon_bo *texture, + struct radeon_bo *mipmap, + u64 base_offset, + u64 mip_offset, + u32 tiling_flags) +{ + struct r600_cs_track *track = p->track; + u32 dim, nfaces, llevel, blevel, w0, h0, d0; + u32 word0, word1, l0_size, mipmap_size, word2, word3, word4, word5; + u32 height_align, pitch, pitch_align, depth_align; + u32 barray, larray; + u64 base_align; + struct array_mode_checker array_check; + u32 format; + bool is_array; + + /* on legacy kernel we don't perform advanced check */ + if (p->rdev == NULL) + return 0; + + /* convert to bytes */ + base_offset <<= 8; + mip_offset <<= 8; + + word0 = radeon_get_ib_value(p, idx + 0); + if (!(p->cs_flags & RADEON_CS_KEEP_TILING_FLAGS)) { + if (tiling_flags & RADEON_TILING_MACRO) + word0 |= S_038000_TILE_MODE(V_038000_ARRAY_2D_TILED_THIN1); + else if (tiling_flags & RADEON_TILING_MICRO) + word0 |= S_038000_TILE_MODE(V_038000_ARRAY_1D_TILED_THIN1); + } + word1 = radeon_get_ib_value(p, idx + 1); + word2 = radeon_get_ib_value(p, idx + 2) << 8; + word3 = radeon_get_ib_value(p, idx + 3) << 8; + word4 = radeon_get_ib_value(p, idx + 4); + word5 = radeon_get_ib_value(p, idx + 5); + dim = G_038000_DIM(word0); + w0 = G_038000_TEX_WIDTH(word0) + 1; + pitch = (G_038000_PITCH(word0) + 1) * 8; + h0 = G_038004_TEX_HEIGHT(word1) + 1; + d0 = G_038004_TEX_DEPTH(word1); + format = G_038004_DATA_FORMAT(word1); + blevel = G_038010_BASE_LEVEL(word4); + llevel = G_038014_LAST_LEVEL(word5); + /* pitch in texels */ + array_check.array_mode = G_038000_TILE_MODE(word0); + array_check.group_size = track->group_size; + array_check.nbanks = track->nbanks; + array_check.npipes = track->npipes; + array_check.nsamples = 1; + array_check.blocksize = r600_fmt_get_blocksize(format); + nfaces = 1; + is_array = false; + switch (dim) { + case V_038000_SQ_TEX_DIM_1D: + case V_038000_SQ_TEX_DIM_2D: + case V_038000_SQ_TEX_DIM_3D: + break; + case V_038000_SQ_TEX_DIM_CUBEMAP: + if (p->family >= CHIP_RV770) + nfaces = 8; + else + nfaces = 6; + break; + case V_038000_SQ_TEX_DIM_1D_ARRAY: + case V_038000_SQ_TEX_DIM_2D_ARRAY: + is_array = true; + break; + case V_038000_SQ_TEX_DIM_2D_ARRAY_MSAA: + is_array = true; + /* fall through */ + case V_038000_SQ_TEX_DIM_2D_MSAA: + array_check.nsamples = 1 << llevel; + llevel = 0; + break; + default: + dev_warn(p->dev, "this kernel doesn't support %d texture dim\n", G_038000_DIM(word0)); + return -EINVAL; + } + if (!r600_fmt_is_valid_texture(format, p->family)) { + dev_warn(p->dev, "%s:%d texture invalid format %d\n", + __func__, __LINE__, format); + return -EINVAL; + } + + if (r600_get_array_mode_alignment(&array_check, + &pitch_align, &height_align, &depth_align, &base_align)) { + dev_warn(p->dev, "%s:%d tex array mode (%d) invalid\n", + __func__, __LINE__, G_038000_TILE_MODE(word0)); + return -EINVAL; + } + + /* XXX check height as well... */ + + if (!IS_ALIGNED(pitch, pitch_align)) { + dev_warn(p->dev, "%s:%d tex pitch (%d, 0x%x, %d) invalid\n", + __func__, __LINE__, pitch, pitch_align, G_038000_TILE_MODE(word0)); + return -EINVAL; + } + if (!IS_ALIGNED(base_offset, base_align)) { + dev_warn(p->dev, "%s:%d tex base offset (0x%jx, 0x%jx, %d) invalid\n", + __func__, __LINE__, (uintmax_t)base_offset, (uintmax_t)base_align, G_038000_TILE_MODE(word0)); + return -EINVAL; + } + if (!IS_ALIGNED(mip_offset, base_align)) { + dev_warn(p->dev, "%s:%d tex mip offset (0x%jx, 0x%jx, %d) invalid\n", + __func__, __LINE__, (uintmax_t)mip_offset, (uintmax_t)base_align, G_038000_TILE_MODE(word0)); + return -EINVAL; + } + + if (blevel > llevel) { + dev_warn(p->dev, "texture blevel %d > llevel %d\n", + blevel, llevel); + } + if (is_array) { + barray = G_038014_BASE_ARRAY(word5); + larray = G_038014_LAST_ARRAY(word5); + + nfaces = larray - barray + 1; + } + r600_texture_size(nfaces, blevel, llevel, w0, h0, d0, array_check.nsamples, format, + pitch_align, height_align, base_align, + &l0_size, &mipmap_size); + /* using get ib will give us the offset into the texture bo */ + if ((l0_size + word2) > radeon_bo_size(texture)) { + dev_warn(p->dev, "texture bo too small ((%d %d) (%d %d) %d %d %d -> %d have %ld)\n", + w0, h0, pitch_align, height_align, + array_check.array_mode, format, word2, + l0_size, radeon_bo_size(texture)); + dev_warn(p->dev, "alignments %d %d %d %jd\n", pitch, pitch_align, height_align, (uintmax_t)base_align); + return -EINVAL; + } + /* using get ib will give us the offset into the mipmap bo */ + if ((mipmap_size + word3) > radeon_bo_size(mipmap)) { + /*dev_warn(p->dev, "mipmap bo too small (%d %d %d %d %d %d -> %d have %ld)\n", + w0, h0, format, blevel, nlevels, word3, mipmap_size, radeon_bo_size(texture));*/ + } + return 0; +} + +static bool r600_is_safe_reg(struct radeon_cs_parser *p, u32 reg, u32 idx) +{ + u32 m, i; + + i = (reg >> 7); + if (i >= DRM_ARRAY_SIZE(r600_reg_safe_bm)) { + dev_warn(p->dev, "forbidden register 0x%08x at %d\n", reg, idx); + return false; + } + m = 1 << ((reg >> 2) & 31); + if (!(r600_reg_safe_bm[i] & m)) + return true; + dev_warn(p->dev, "forbidden register 0x%08x at %d\n", reg, idx); + return false; +} + +static int r600_packet3_check(struct radeon_cs_parser *p, + struct radeon_cs_packet *pkt) +{ + struct radeon_cs_reloc *reloc; + struct r600_cs_track *track; + volatile u32 *ib; + unsigned idx; + unsigned i; + unsigned start_reg, end_reg, reg; + int r; + u32 idx_value; + + track = (struct r600_cs_track *)p->track; + ib = p->ib.ptr; + idx = pkt->idx + 1; + idx_value = radeon_get_ib_value(p, idx); + + switch (pkt->opcode) { + case PACKET3_SET_PREDICATION: + { + int pred_op; + int tmp; + uint64_t offset; + + if (pkt->count != 1) { + DRM_ERROR("bad SET PREDICATION\n"); + return -EINVAL; + } + + tmp = radeon_get_ib_value(p, idx + 1); + pred_op = (tmp >> 16) & 0x7; + + /* for the clear predicate operation */ + if (pred_op == 0) + return 0; + + if (pred_op > 2) { + DRM_ERROR("bad SET PREDICATION operation %d\n", pred_op); + return -EINVAL; + } + + r = r600_cs_packet_next_reloc(p, &reloc); + if (r) { + DRM_ERROR("bad SET PREDICATION\n"); + return -EINVAL; + } + + offset = reloc->lobj.gpu_offset + + (idx_value & 0xfffffff0) + + ((u64)(tmp & 0xff) << 32); + + ib[idx + 0] = offset; + ib[idx + 1] = (tmp & 0xffffff00) | (upper_32_bits(offset) & 0xff); + } + break; + + case PACKET3_START_3D_CMDBUF: + if (p->family >= CHIP_RV770 || pkt->count) { + DRM_ERROR("bad START_3D\n"); + return -EINVAL; + } + break; + case PACKET3_CONTEXT_CONTROL: + if (pkt->count != 1) { + DRM_ERROR("bad CONTEXT_CONTROL\n"); + return -EINVAL; + } + break; + case PACKET3_INDEX_TYPE: + case PACKET3_NUM_INSTANCES: + if (pkt->count) { + DRM_ERROR("bad INDEX_TYPE/NUM_INSTANCES\n"); + return -EINVAL; + } + break; + case PACKET3_DRAW_INDEX: + { + uint64_t offset; + if (pkt->count != 3) { + DRM_ERROR("bad DRAW_INDEX\n"); + return -EINVAL; + } + r = r600_cs_packet_next_reloc(p, &reloc); + if (r) { + DRM_ERROR("bad DRAW_INDEX\n"); + return -EINVAL; + } + + offset = reloc->lobj.gpu_offset + + idx_value + + ((u64)(radeon_get_ib_value(p, idx+1) & 0xff) << 32); + + ib[idx+0] = offset; + ib[idx+1] = upper_32_bits(offset) & 0xff; + + r = r600_cs_track_check(p); + if (r) { + dev_warn(p->dev, "%s:%d invalid cmd stream\n", __func__, __LINE__); + return r; + } + break; + } + case PACKET3_DRAW_INDEX_AUTO: + if (pkt->count != 1) { + DRM_ERROR("bad DRAW_INDEX_AUTO\n"); + return -EINVAL; + } + r = r600_cs_track_check(p); + if (r) { + dev_warn(p->dev, "%s:%d invalid cmd stream %d\n", __func__, __LINE__, idx); + return r; + } + break; + case PACKET3_DRAW_INDEX_IMMD_BE: + case PACKET3_DRAW_INDEX_IMMD: + if (pkt->count < 2) { + DRM_ERROR("bad DRAW_INDEX_IMMD\n"); + return -EINVAL; + } + r = r600_cs_track_check(p); + if (r) { + dev_warn(p->dev, "%s:%d invalid cmd stream\n", __func__, __LINE__); + return r; + } + break; + case PACKET3_WAIT_REG_MEM: + if (pkt->count != 5) { + DRM_ERROR("bad WAIT_REG_MEM\n"); + return -EINVAL; + } + /* bit 4 is reg (0) or mem (1) */ + if (idx_value & 0x10) { + uint64_t offset; + + r = r600_cs_packet_next_reloc(p, &reloc); + if (r) { + DRM_ERROR("bad WAIT_REG_MEM\n"); + return -EINVAL; + } + + offset = reloc->lobj.gpu_offset + + (radeon_get_ib_value(p, idx+1) & 0xfffffff0) + + ((u64)(radeon_get_ib_value(p, idx+2) & 0xff) << 32); + + ib[idx+1] = (ib[idx+1] & 0x3) | (offset & 0xfffffff0); + ib[idx+2] = upper_32_bits(offset) & 0xff; + } + break; + case PACKET3_CP_DMA: + { + u32 command, size; + u64 offset, tmp; + if (pkt->count != 4) { + DRM_ERROR("bad CP DMA\n"); + return -EINVAL; + } + command = radeon_get_ib_value(p, idx+4); + size = command & 0x1fffff; + if (command & PACKET3_CP_DMA_CMD_SAS) { + /* src address space is register */ + DRM_ERROR("CP DMA SAS not supported\n"); + return -EINVAL; + } else { + if (command & PACKET3_CP_DMA_CMD_SAIC) { + DRM_ERROR("CP DMA SAIC only supported for registers\n"); + return -EINVAL; + } + /* src address space is memory */ + r = r600_cs_packet_next_reloc(p, &reloc); + if (r) { + DRM_ERROR("bad CP DMA SRC\n"); + return -EINVAL; + } + + tmp = radeon_get_ib_value(p, idx) + + ((u64)(radeon_get_ib_value(p, idx+1) & 0xff) << 32); + + offset = reloc->lobj.gpu_offset + tmp; + + if ((tmp + size) > radeon_bo_size(reloc->robj)) { + dev_warn(p->dev, "CP DMA src buffer too small (%ju %lu)\n", + (uintmax_t)tmp + size, radeon_bo_size(reloc->robj)); + return -EINVAL; + } + + ib[idx] = offset; + ib[idx+1] = (ib[idx+1] & 0xffffff00) | (upper_32_bits(offset) & 0xff); + } + if (command & PACKET3_CP_DMA_CMD_DAS) { + /* dst address space is register */ + DRM_ERROR("CP DMA DAS not supported\n"); + return -EINVAL; + } else { + /* dst address space is memory */ + if (command & PACKET3_CP_DMA_CMD_DAIC) { + DRM_ERROR("CP DMA DAIC only supported for registers\n"); + return -EINVAL; + } + r = r600_cs_packet_next_reloc(p, &reloc); + if (r) { + DRM_ERROR("bad CP DMA DST\n"); + return -EINVAL; + } + + tmp = radeon_get_ib_value(p, idx+2) + + ((u64)(radeon_get_ib_value(p, idx+3) & 0xff) << 32); + + offset = reloc->lobj.gpu_offset + tmp; + + if ((tmp + size) > radeon_bo_size(reloc->robj)) { + dev_warn(p->dev, "CP DMA dst buffer too small (%ju %lu)\n", + (uintmax_t)tmp + size, radeon_bo_size(reloc->robj)); + return -EINVAL; + } + + ib[idx+2] = offset; + ib[idx+3] = upper_32_bits(offset) & 0xff; + } + break; + } + case PACKET3_SURFACE_SYNC: + if (pkt->count != 3) { + DRM_ERROR("bad SURFACE_SYNC\n"); + return -EINVAL; + } + /* 0xffffffff/0x0 is flush all cache flag */ + if (radeon_get_ib_value(p, idx + 1) != 0xffffffff || + radeon_get_ib_value(p, idx + 2) != 0) { + r = r600_cs_packet_next_reloc(p, &reloc); + if (r) { + DRM_ERROR("bad SURFACE_SYNC\n"); + return -EINVAL; + } + ib[idx+2] += (u32)((reloc->lobj.gpu_offset >> 8) & 0xffffffff); + } + break; + case PACKET3_EVENT_WRITE: + if (pkt->count != 2 && pkt->count != 0) { + DRM_ERROR("bad EVENT_WRITE\n"); + return -EINVAL; + } + if (pkt->count) { + uint64_t offset; + + r = r600_cs_packet_next_reloc(p, &reloc); + if (r) { + DRM_ERROR("bad EVENT_WRITE\n"); + return -EINVAL; + } + offset = reloc->lobj.gpu_offset + + (radeon_get_ib_value(p, idx+1) & 0xfffffff8) + + ((u64)(radeon_get_ib_value(p, idx+2) & 0xff) << 32); + + ib[idx+1] = offset & 0xfffffff8; + ib[idx+2] = upper_32_bits(offset) & 0xff; + } + break; + case PACKET3_EVENT_WRITE_EOP: + { + uint64_t offset; + + if (pkt->count != 4) { + DRM_ERROR("bad EVENT_WRITE_EOP\n"); + return -EINVAL; + } + r = r600_cs_packet_next_reloc(p, &reloc); + if (r) { + DRM_ERROR("bad EVENT_WRITE\n"); + return -EINVAL; + } + + offset = reloc->lobj.gpu_offset + + (radeon_get_ib_value(p, idx+1) & 0xfffffffc) + + ((u64)(radeon_get_ib_value(p, idx+2) & 0xff) << 32); + + ib[idx+1] = offset & 0xfffffffc; + ib[idx+2] = (ib[idx+2] & 0xffffff00) | (upper_32_bits(offset) & 0xff); + break; + } + case PACKET3_SET_CONFIG_REG: + start_reg = (idx_value << 2) + PACKET3_SET_CONFIG_REG_OFFSET; + end_reg = 4 * pkt->count + start_reg - 4; + if ((start_reg < PACKET3_SET_CONFIG_REG_OFFSET) || + (start_reg >= PACKET3_SET_CONFIG_REG_END) || + (end_reg >= PACKET3_SET_CONFIG_REG_END)) { + DRM_ERROR("bad PACKET3_SET_CONFIG_REG\n"); + return -EINVAL; + } + for (i = 0; i < pkt->count; i++) { + reg = start_reg + (4 * i); + r = r600_cs_check_reg(p, reg, idx+1+i); + if (r) + return r; + } + break; + case PACKET3_SET_CONTEXT_REG: + start_reg = (idx_value << 2) + PACKET3_SET_CONTEXT_REG_OFFSET; + end_reg = 4 * pkt->count + start_reg - 4; + if ((start_reg < PACKET3_SET_CONTEXT_REG_OFFSET) || + (start_reg >= PACKET3_SET_CONTEXT_REG_END) || + (end_reg >= PACKET3_SET_CONTEXT_REG_END)) { + DRM_ERROR("bad PACKET3_SET_CONTEXT_REG\n"); + return -EINVAL; + } + for (i = 0; i < pkt->count; i++) { + reg = start_reg + (4 * i); + r = r600_cs_check_reg(p, reg, idx+1+i); + if (r) + return r; + } + break; + case PACKET3_SET_RESOURCE: + if (pkt->count % 7) { + DRM_ERROR("bad SET_RESOURCE\n"); + return -EINVAL; + } + start_reg = (idx_value << 2) + PACKET3_SET_RESOURCE_OFFSET; + end_reg = 4 * pkt->count + start_reg - 4; + if ((start_reg < PACKET3_SET_RESOURCE_OFFSET) || + (start_reg >= PACKET3_SET_RESOURCE_END) || + (end_reg >= PACKET3_SET_RESOURCE_END)) { + DRM_ERROR("bad SET_RESOURCE\n"); + return -EINVAL; + } + for (i = 0; i < (pkt->count / 7); i++) { + struct radeon_bo *texture, *mipmap; + u32 size, offset, base_offset, mip_offset; + + switch (G__SQ_VTX_CONSTANT_TYPE(radeon_get_ib_value(p, idx+(i*7)+6+1))) { + case SQ_TEX_VTX_VALID_TEXTURE: + /* tex base */ + r = r600_cs_packet_next_reloc(p, &reloc); + if (r) { + DRM_ERROR("bad SET_RESOURCE\n"); + return -EINVAL; + } + base_offset = (u32)((reloc->lobj.gpu_offset >> 8) & 0xffffffff); + if (!(p->cs_flags & RADEON_CS_KEEP_TILING_FLAGS)) { + if (reloc->lobj.tiling_flags & RADEON_TILING_MACRO) + ib[idx+1+(i*7)+0] |= S_038000_TILE_MODE(V_038000_ARRAY_2D_TILED_THIN1); + else if (reloc->lobj.tiling_flags & RADEON_TILING_MICRO) + ib[idx+1+(i*7)+0] |= S_038000_TILE_MODE(V_038000_ARRAY_1D_TILED_THIN1); + } + texture = reloc->robj; + /* tex mip base */ + r = r600_cs_packet_next_reloc(p, &reloc); + if (r) { + DRM_ERROR("bad SET_RESOURCE\n"); + return -EINVAL; + } + mip_offset = (u32)((reloc->lobj.gpu_offset >> 8) & 0xffffffff); + mipmap = reloc->robj; + r = r600_check_texture_resource(p, idx+(i*7)+1, + texture, mipmap, + base_offset + radeon_get_ib_value(p, idx+1+(i*7)+2), + mip_offset + radeon_get_ib_value(p, idx+1+(i*7)+3), + reloc->lobj.tiling_flags); + if (r) + return r; + ib[idx+1+(i*7)+2] += base_offset; + ib[idx+1+(i*7)+3] += mip_offset; + break; + case SQ_TEX_VTX_VALID_BUFFER: + { + uint64_t offset64; + /* vtx base */ + r = r600_cs_packet_next_reloc(p, &reloc); + if (r) { + DRM_ERROR("bad SET_RESOURCE\n"); + return -EINVAL; + } + offset = radeon_get_ib_value(p, idx+1+(i*7)+0); + size = radeon_get_ib_value(p, idx+1+(i*7)+1) + 1; + if (p->rdev && (size + offset) > radeon_bo_size(reloc->robj)) { + /* force size to size of the buffer */ + dev_warn(p->dev, "vbo resource seems too big (%d) for the bo (%ld)\n", + size + offset, radeon_bo_size(reloc->robj)); + ib[idx+1+(i*7)+1] = radeon_bo_size(reloc->robj) - offset; + } + + offset64 = reloc->lobj.gpu_offset + offset; + ib[idx+1+(i*8)+0] = offset64; + ib[idx+1+(i*8)+2] = (ib[idx+1+(i*8)+2] & 0xffffff00) | + (upper_32_bits(offset64) & 0xff); + break; + } + case SQ_TEX_VTX_INVALID_TEXTURE: + case SQ_TEX_VTX_INVALID_BUFFER: + default: + DRM_ERROR("bad SET_RESOURCE\n"); + return -EINVAL; + } + } + break; + case PACKET3_SET_ALU_CONST: + if (track->sq_config & DX9_CONSTS) { + start_reg = (idx_value << 2) + PACKET3_SET_ALU_CONST_OFFSET; + end_reg = 4 * pkt->count + start_reg - 4; + if ((start_reg < PACKET3_SET_ALU_CONST_OFFSET) || + (start_reg >= PACKET3_SET_ALU_CONST_END) || + (end_reg >= PACKET3_SET_ALU_CONST_END)) { + DRM_ERROR("bad SET_ALU_CONST\n"); + return -EINVAL; + } + } + break; + case PACKET3_SET_BOOL_CONST: + start_reg = (idx_value << 2) + PACKET3_SET_BOOL_CONST_OFFSET; + end_reg = 4 * pkt->count + start_reg - 4; + if ((start_reg < PACKET3_SET_BOOL_CONST_OFFSET) || + (start_reg >= PACKET3_SET_BOOL_CONST_END) || + (end_reg >= PACKET3_SET_BOOL_CONST_END)) { + DRM_ERROR("bad SET_BOOL_CONST\n"); + return -EINVAL; + } + break; + case PACKET3_SET_LOOP_CONST: + start_reg = (idx_value << 2) + PACKET3_SET_LOOP_CONST_OFFSET; + end_reg = 4 * pkt->count + start_reg - 4; + if ((start_reg < PACKET3_SET_LOOP_CONST_OFFSET) || + (start_reg >= PACKET3_SET_LOOP_CONST_END) || + (end_reg >= PACKET3_SET_LOOP_CONST_END)) { + DRM_ERROR("bad SET_LOOP_CONST\n"); + return -EINVAL; + } + break; + case PACKET3_SET_CTL_CONST: + start_reg = (idx_value << 2) + PACKET3_SET_CTL_CONST_OFFSET; + end_reg = 4 * pkt->count + start_reg - 4; + if ((start_reg < PACKET3_SET_CTL_CONST_OFFSET) || + (start_reg >= PACKET3_SET_CTL_CONST_END) || + (end_reg >= PACKET3_SET_CTL_CONST_END)) { + DRM_ERROR("bad SET_CTL_CONST\n"); + return -EINVAL; + } + break; + case PACKET3_SET_SAMPLER: + if (pkt->count % 3) { + DRM_ERROR("bad SET_SAMPLER\n"); + return -EINVAL; + } + start_reg = (idx_value << 2) + PACKET3_SET_SAMPLER_OFFSET; + end_reg = 4 * pkt->count + start_reg - 4; + if ((start_reg < PACKET3_SET_SAMPLER_OFFSET) || + (start_reg >= PACKET3_SET_SAMPLER_END) || + (end_reg >= PACKET3_SET_SAMPLER_END)) { + DRM_ERROR("bad SET_SAMPLER\n"); + return -EINVAL; + } + break; + case PACKET3_STRMOUT_BASE_UPDATE: + /* RS780 and RS880 also need this */ + if (p->family < CHIP_RS780) { + DRM_ERROR("STRMOUT_BASE_UPDATE only supported on 7xx\n"); + return -EINVAL; + } + if (pkt->count != 1) { + DRM_ERROR("bad STRMOUT_BASE_UPDATE packet count\n"); + return -EINVAL; + } + if (idx_value > 3) { + DRM_ERROR("bad STRMOUT_BASE_UPDATE index\n"); + return -EINVAL; + } + { + u64 offset; + + r = r600_cs_packet_next_reloc(p, &reloc); + if (r) { + DRM_ERROR("bad STRMOUT_BASE_UPDATE reloc\n"); + return -EINVAL; + } + + if (reloc->robj != track->vgt_strmout_bo[idx_value]) { + DRM_ERROR("bad STRMOUT_BASE_UPDATE, bo does not match\n"); + return -EINVAL; + } + + offset = radeon_get_ib_value(p, idx+1) << 8; + if (offset != track->vgt_strmout_bo_offset[idx_value]) { + DRM_ERROR("bad STRMOUT_BASE_UPDATE, bo offset does not match: 0x%jx, 0x%x\n", + (uintmax_t)offset, track->vgt_strmout_bo_offset[idx_value]); + return -EINVAL; + } + + if ((offset + 4) > radeon_bo_size(reloc->robj)) { + DRM_ERROR("bad STRMOUT_BASE_UPDATE bo too small: 0x%jx, 0x%lx\n", + (uintmax_t)offset + 4, radeon_bo_size(reloc->robj)); + return -EINVAL; + } + ib[idx+1] += (u32)((reloc->lobj.gpu_offset >> 8) & 0xffffffff); + } + break; + case PACKET3_SURFACE_BASE_UPDATE: + if (p->family >= CHIP_RV770 || p->family == CHIP_R600) { + DRM_ERROR("bad SURFACE_BASE_UPDATE\n"); + return -EINVAL; + } + if (pkt->count) { + DRM_ERROR("bad SURFACE_BASE_UPDATE\n"); + return -EINVAL; + } + break; + case PACKET3_STRMOUT_BUFFER_UPDATE: + if (pkt->count != 4) { + DRM_ERROR("bad STRMOUT_BUFFER_UPDATE (invalid count)\n"); + return -EINVAL; + } + /* Updating memory at DST_ADDRESS. */ + if (idx_value & 0x1) { + u64 offset; + r = r600_cs_packet_next_reloc(p, &reloc); + if (r) { + DRM_ERROR("bad STRMOUT_BUFFER_UPDATE (missing dst reloc)\n"); + return -EINVAL; + } + offset = radeon_get_ib_value(p, idx+1); + offset += ((u64)(radeon_get_ib_value(p, idx+2) & 0xff)) << 32; + if ((offset + 4) > radeon_bo_size(reloc->robj)) { + DRM_ERROR("bad STRMOUT_BUFFER_UPDATE dst bo too small: 0x%jx, 0x%lx\n", + (uintmax_t)offset + 4, radeon_bo_size(reloc->robj)); + return -EINVAL; + } + offset += reloc->lobj.gpu_offset; + ib[idx+1] = offset; + ib[idx+2] = upper_32_bits(offset) & 0xff; + } + /* Reading data from SRC_ADDRESS. */ + if (((idx_value >> 1) & 0x3) == 2) { + u64 offset; + r = r600_cs_packet_next_reloc(p, &reloc); + if (r) { + DRM_ERROR("bad STRMOUT_BUFFER_UPDATE (missing src reloc)\n"); + return -EINVAL; + } + offset = radeon_get_ib_value(p, idx+3); + offset += ((u64)(radeon_get_ib_value(p, idx+4) & 0xff)) << 32; + if ((offset + 4) > radeon_bo_size(reloc->robj)) { + DRM_ERROR("bad STRMOUT_BUFFER_UPDATE src bo too small: 0x%jx, 0x%lx\n", + (uintmax_t)offset + 4, radeon_bo_size(reloc->robj)); + return -EINVAL; + } + offset += reloc->lobj.gpu_offset; + ib[idx+3] = offset; + ib[idx+4] = upper_32_bits(offset) & 0xff; + } + break; + case PACKET3_MEM_WRITE: + { + u64 offset; + + if (pkt->count != 3) { + DRM_ERROR("bad MEM_WRITE (invalid count)\n"); + return -EINVAL; + } + r = r600_cs_packet_next_reloc(p, &reloc); + if (r) { + DRM_ERROR("bad MEM_WRITE (missing reloc)\n"); + return -EINVAL; + } + offset = radeon_get_ib_value(p, idx+0); + offset += ((u64)(radeon_get_ib_value(p, idx+1) & 0xff)) << 32UL; + if (offset & 0x7) { + DRM_ERROR("bad MEM_WRITE (address not qwords aligned)\n"); + return -EINVAL; + } + if ((offset + 8) > radeon_bo_size(reloc->robj)) { + DRM_ERROR("bad MEM_WRITE bo too small: 0x%jx, 0x%lx\n", + (uintmax_t)offset + 8, radeon_bo_size(reloc->robj)); + return -EINVAL; + } + offset += reloc->lobj.gpu_offset; + ib[idx+0] = offset; + ib[idx+1] = upper_32_bits(offset) & 0xff; + break; + } + case PACKET3_COPY_DW: + if (pkt->count != 4) { + DRM_ERROR("bad COPY_DW (invalid count)\n"); + return -EINVAL; + } + if (idx_value & 0x1) { + u64 offset; + /* SRC is memory. */ + r = r600_cs_packet_next_reloc(p, &reloc); + if (r) { + DRM_ERROR("bad COPY_DW (missing src reloc)\n"); + return -EINVAL; + } + offset = radeon_get_ib_value(p, idx+1); + offset += ((u64)(radeon_get_ib_value(p, idx+2) & 0xff)) << 32; + if ((offset + 4) > radeon_bo_size(reloc->robj)) { + DRM_ERROR("bad COPY_DW src bo too small: 0x%jx, 0x%lx\n", + (uintmax_t)offset + 4, radeon_bo_size(reloc->robj)); + return -EINVAL; + } + offset += reloc->lobj.gpu_offset; + ib[idx+1] = offset; + ib[idx+2] = upper_32_bits(offset) & 0xff; + } else { + /* SRC is a reg. */ + reg = radeon_get_ib_value(p, idx+1) << 2; + if (!r600_is_safe_reg(p, reg, idx+1)) + return -EINVAL; + } + if (idx_value & 0x2) { + u64 offset; + /* DST is memory. */ + r = r600_cs_packet_next_reloc(p, &reloc); + if (r) { + DRM_ERROR("bad COPY_DW (missing dst reloc)\n"); + return -EINVAL; + } + offset = radeon_get_ib_value(p, idx+3); + offset += ((u64)(radeon_get_ib_value(p, idx+4) & 0xff)) << 32; + if ((offset + 4) > radeon_bo_size(reloc->robj)) { + DRM_ERROR("bad COPY_DW dst bo too small: 0x%jx, 0x%lx\n", + (uintmax_t)offset + 4, radeon_bo_size(reloc->robj)); + return -EINVAL; + } + offset += reloc->lobj.gpu_offset; + ib[idx+3] = offset; + ib[idx+4] = upper_32_bits(offset) & 0xff; + } else { + /* DST is a reg. */ + reg = radeon_get_ib_value(p, idx+3) << 2; + if (!r600_is_safe_reg(p, reg, idx+3)) + return -EINVAL; + } + break; + case PACKET3_NOP: + break; + default: + DRM_ERROR("Packet3 opcode %x not supported\n", pkt->opcode); + return -EINVAL; + } + return 0; +} + +int r600_cs_parse(struct radeon_cs_parser *p) +{ + struct radeon_cs_packet pkt; + struct r600_cs_track *track; + int r; + + if (p->track == NULL) { + /* initialize tracker, we are in kms */ + track = malloc(sizeof(*track), + DRM_MEM_DRIVER, M_ZERO | M_WAITOK); + if (track == NULL) + return -ENOMEM; + r600_cs_track_init(track); + if (p->rdev->family < CHIP_RV770) { + track->npipes = p->rdev->config.r600.tiling_npipes; + track->nbanks = p->rdev->config.r600.tiling_nbanks; + track->group_size = p->rdev->config.r600.tiling_group_size; + } else if (p->rdev->family <= CHIP_RV740) { + track->npipes = p->rdev->config.rv770.tiling_npipes; + track->nbanks = p->rdev->config.rv770.tiling_nbanks; + track->group_size = p->rdev->config.rv770.tiling_group_size; + } + p->track = track; + } + do { + r = r600_cs_packet_parse(p, &pkt, p->idx); + if (r) { + free(p->track, DRM_MEM_DRIVER); + p->track = NULL; + return r; + } + p->idx += pkt.count + 2; + switch (pkt.type) { + case PACKET_TYPE0: + r = r600_cs_parse_packet0(p, &pkt); + break; + case PACKET_TYPE2: + break; + case PACKET_TYPE3: + r = r600_packet3_check(p, &pkt); + break; + default: + DRM_ERROR("Unknown packet type %d !\n", pkt.type); + free(p->track, DRM_MEM_DRIVER); + p->track = NULL; + return -EINVAL; + } + if (r) { + free(p->track, DRM_MEM_DRIVER); + p->track = NULL; + return r; + } + } while (p->idx < p->chunks[p->chunk_ib_idx].length_dw); +#if 0 + for (r = 0; r < p->ib.length_dw; r++) { + DRM_INFO("%05d 0x%08X\n", r, p->ib.ptr[r]); + DRM_MDELAY(1); + } +#endif + free(p->track, DRM_MEM_DRIVER); + p->track = NULL; + return 0; +} + +static int r600_cs_parser_relocs_legacy(struct radeon_cs_parser *p) +{ + if (p->chunk_relocs_idx == -1) { + return 0; + } + p->relocs = malloc(sizeof(struct radeon_cs_reloc), + DRM_MEM_DRIVER, M_ZERO | M_WAITOK); + if (p->relocs == NULL) { + return -ENOMEM; + } + return 0; +} + +/** + * cs_parser_fini() - clean parser states + * @parser: parser structure holding parsing context. + * @error: error number + * + * If error is set than unvalidate buffer, otherwise just free memory + * used by parsing context. + **/ +static void r600_cs_parser_fini(struct radeon_cs_parser *parser, int error) +{ + unsigned i; + + free(parser->relocs, DRM_MEM_DRIVER); + for (i = 0; i < parser->nchunks; i++) { + free(parser->chunks[i].kdata, DRM_MEM_DRIVER); + if (parser->rdev && (parser->rdev->flags & RADEON_IS_AGP)) { + free(parser->chunks[i].kpage[0], DRM_MEM_DRIVER); + free(parser->chunks[i].kpage[1], DRM_MEM_DRIVER); + } + } + free(parser->chunks, DRM_MEM_DRIVER); + free(parser->chunks_array, DRM_MEM_DRIVER); + free(parser->track, DRM_MEM_DRIVER); +} + +int r600_cs_legacy(struct drm_device *dev, void *data, struct drm_file *filp, + unsigned family, u32 *ib, int *l) +{ + struct radeon_cs_parser parser; + struct radeon_cs_chunk *ib_chunk; + struct r600_cs_track *track; + int r; + + /* initialize tracker */ + track = malloc(sizeof(*track), DRM_MEM_DRIVER, M_ZERO | M_WAITOK); + if (track == NULL) + return -ENOMEM; + r600_cs_track_init(track); + r600_cs_legacy_get_tiling_conf(dev, &track->npipes, &track->nbanks, &track->group_size); + /* initialize parser */ + memset(&parser, 0, sizeof(struct radeon_cs_parser)); + parser.filp = filp; + parser.dev = dev->device; + parser.rdev = NULL; + parser.family = family; + parser.track = track; + parser.ib.ptr = ib; + r = radeon_cs_parser_init(&parser, data); + if (r) { + DRM_ERROR("Failed to initialize parser !\n"); + r600_cs_parser_fini(&parser, r); + return r; + } + r = r600_cs_parser_relocs_legacy(&parser); + if (r) { + DRM_ERROR("Failed to parse relocation !\n"); + r600_cs_parser_fini(&parser, r); + return r; + } + /* Copy the packet into the IB, the parser will read from the + * input memory (cached) and write to the IB (which can be + * uncached). */ + ib_chunk = &parser.chunks[parser.chunk_ib_idx]; + parser.ib.length_dw = ib_chunk->length_dw; + *l = parser.ib.length_dw; + r = r600_cs_parse(&parser); + if (r) { + DRM_ERROR("Invalid command stream !\n"); + r600_cs_parser_fini(&parser, r); + return r; + } + r = radeon_cs_finish_pages(&parser); + if (r) { + DRM_ERROR("Invalid command stream !\n"); + r600_cs_parser_fini(&parser, r); + return r; + } + r600_cs_parser_fini(&parser, r); + return r; +} + +void r600_cs_legacy_init(void) +{ + r600_cs_packet_next_reloc = &r600_cs_packet_next_reloc_nomm; +} + +/* + * DMA + */ +/** + * r600_dma_cs_next_reloc() - parse next reloc + * @p: parser structure holding parsing context. + * @cs_reloc: reloc informations + * + * Return the next reloc, do bo validation and compute + * GPU offset using the provided start. + **/ +int r600_dma_cs_next_reloc(struct radeon_cs_parser *p, + struct radeon_cs_reloc **cs_reloc) +{ + struct radeon_cs_chunk *relocs_chunk; + unsigned idx; + + *cs_reloc = NULL; + if (p->chunk_relocs_idx == -1) { + DRM_ERROR("No relocation chunk !\n"); + return -EINVAL; + } + relocs_chunk = &p->chunks[p->chunk_relocs_idx]; + idx = p->dma_reloc_idx; + if (idx >= p->nrelocs) { + DRM_ERROR("Relocs at %d after relocations chunk end %d !\n", + idx, p->nrelocs); + return -EINVAL; + } + *cs_reloc = p->relocs_ptr[idx]; + p->dma_reloc_idx++; + return 0; +} + +#define GET_DMA_CMD(h) (((h) & 0xf0000000) >> 28) +#define GET_DMA_COUNT(h) ((h) & 0x0000ffff) +#define GET_DMA_T(h) (((h) & 0x00800000) >> 23) + +/** + * r600_dma_cs_parse() - parse the DMA IB + * @p: parser structure holding parsing context. + * + * Parses the DMA IB from the CS ioctl and updates + * the GPU addresses based on the reloc information and + * checks for errors. (R6xx-R7xx) + * Returns 0 for success and an error on failure. + **/ +int r600_dma_cs_parse(struct radeon_cs_parser *p) +{ + struct radeon_cs_chunk *ib_chunk = &p->chunks[p->chunk_ib_idx]; + struct radeon_cs_reloc *src_reloc, *dst_reloc; + u32 header, cmd, count, tiled; + volatile u32 *ib = p->ib.ptr; + u32 idx, idx_value; + u64 src_offset, dst_offset; + int r; + + do { + if (p->idx >= ib_chunk->length_dw) { + DRM_ERROR("Can not parse packet at %d after CS end %d !\n", + p->idx, ib_chunk->length_dw); + return -EINVAL; + } + idx = p->idx; + header = radeon_get_ib_value(p, idx); + cmd = GET_DMA_CMD(header); + count = GET_DMA_COUNT(header); + tiled = GET_DMA_T(header); + + switch (cmd) { + case DMA_PACKET_WRITE: + r = r600_dma_cs_next_reloc(p, &dst_reloc); + if (r) { + DRM_ERROR("bad DMA_PACKET_WRITE\n"); + return -EINVAL; + } + if (tiled) { + dst_offset = radeon_get_ib_value(p, idx+1); + dst_offset <<= 8; + + ib[idx+1] += (u32)(dst_reloc->lobj.gpu_offset >> 8); + p->idx += count + 5; + } else { + dst_offset = radeon_get_ib_value(p, idx+1); + dst_offset |= ((u64)(radeon_get_ib_value(p, idx+2) & 0xff)) << 32; + + ib[idx+1] += (u32)(dst_reloc->lobj.gpu_offset & 0xfffffffc); + ib[idx+2] += upper_32_bits(dst_reloc->lobj.gpu_offset) & 0xff; + p->idx += count + 3; + } + if ((dst_offset + (count * 4)) > radeon_bo_size(dst_reloc->robj)) { + dev_warn(p->dev, "DMA write buffer too small (%ju %lu)\n", + (uintmax_t)dst_offset + (count * 4), radeon_bo_size(dst_reloc->robj)); + return -EINVAL; + } + break; + case DMA_PACKET_COPY: + r = r600_dma_cs_next_reloc(p, &src_reloc); + if (r) { + DRM_ERROR("bad DMA_PACKET_COPY\n"); + return -EINVAL; + } + r = r600_dma_cs_next_reloc(p, &dst_reloc); + if (r) { + DRM_ERROR("bad DMA_PACKET_COPY\n"); + return -EINVAL; + } + if (tiled) { + idx_value = radeon_get_ib_value(p, idx + 2); + /* detile bit */ + if (idx_value & (1 << 31)) { + /* tiled src, linear dst */ + src_offset = radeon_get_ib_value(p, idx+1); + src_offset <<= 8; + ib[idx+1] += (u32)(src_reloc->lobj.gpu_offset >> 8); + + dst_offset = radeon_get_ib_value(p, idx+5); + dst_offset |= ((u64)(radeon_get_ib_value(p, idx+6) & 0xff)) << 32; + ib[idx+5] += (u32)(dst_reloc->lobj.gpu_offset & 0xfffffffc); + ib[idx+6] += upper_32_bits(dst_reloc->lobj.gpu_offset) & 0xff; + } else { + /* linear src, tiled dst */ + src_offset = radeon_get_ib_value(p, idx+5); + src_offset |= ((u64)(radeon_get_ib_value(p, idx+6) & 0xff)) << 32; + ib[idx+5] += (u32)(src_reloc->lobj.gpu_offset & 0xfffffffc); + ib[idx+6] += upper_32_bits(src_reloc->lobj.gpu_offset) & 0xff; + + dst_offset = radeon_get_ib_value(p, idx+1); + dst_offset <<= 8; + ib[idx+1] += (u32)(dst_reloc->lobj.gpu_offset >> 8); + } + p->idx += 7; + } else { + if (p->family >= CHIP_RV770) { + src_offset = radeon_get_ib_value(p, idx+2); + src_offset |= ((u64)(radeon_get_ib_value(p, idx+4) & 0xff)) << 32; + dst_offset = radeon_get_ib_value(p, idx+1); + dst_offset |= ((u64)(radeon_get_ib_value(p, idx+3) & 0xff)) << 32; + + ib[idx+1] += (u32)(dst_reloc->lobj.gpu_offset & 0xfffffffc); + ib[idx+2] += (u32)(src_reloc->lobj.gpu_offset & 0xfffffffc); + ib[idx+3] += upper_32_bits(dst_reloc->lobj.gpu_offset) & 0xff; + ib[idx+4] += upper_32_bits(src_reloc->lobj.gpu_offset) & 0xff; + p->idx += 5; + } else { + src_offset = radeon_get_ib_value(p, idx+2); + src_offset |= ((u64)(radeon_get_ib_value(p, idx+3) & 0xff)) << 32; + dst_offset = radeon_get_ib_value(p, idx+1); + dst_offset |= ((u64)(radeon_get_ib_value(p, idx+3) & 0xff0000)) << 16; + + ib[idx+1] += (u32)(dst_reloc->lobj.gpu_offset & 0xfffffffc); + ib[idx+2] += (u32)(src_reloc->lobj.gpu_offset & 0xfffffffc); + ib[idx+3] += upper_32_bits(src_reloc->lobj.gpu_offset) & 0xff; + ib[idx+3] += (upper_32_bits(dst_reloc->lobj.gpu_offset) & 0xff) << 16; + p->idx += 4; + } + } + if ((src_offset + (count * 4)) > radeon_bo_size(src_reloc->robj)) { + dev_warn(p->dev, "DMA copy src buffer too small (%ju %lu)\n", + (uintmax_t)src_offset + (count * 4), radeon_bo_size(src_reloc->robj)); + return -EINVAL; + } + if ((dst_offset + (count * 4)) > radeon_bo_size(dst_reloc->robj)) { + dev_warn(p->dev, "DMA write dst buffer too small (%ju %lu)\n", + (uintmax_t)dst_offset + (count * 4), radeon_bo_size(dst_reloc->robj)); + return -EINVAL; + } + break; + case DMA_PACKET_CONSTANT_FILL: + if (p->family < CHIP_RV770) { + DRM_ERROR("Constant Fill is 7xx only !\n"); + return -EINVAL; + } + r = r600_dma_cs_next_reloc(p, &dst_reloc); + if (r) { + DRM_ERROR("bad DMA_PACKET_WRITE\n"); + return -EINVAL; + } + dst_offset = radeon_get_ib_value(p, idx+1); + dst_offset |= ((u64)(radeon_get_ib_value(p, idx+3) & 0x00ff0000)) << 16; + if ((dst_offset + (count * 4)) > radeon_bo_size(dst_reloc->robj)) { + dev_warn(p->dev, "DMA constant fill buffer too small (%ju %lu)\n", + (uintmax_t)dst_offset + (count * 4), radeon_bo_size(dst_reloc->robj)); + return -EINVAL; + } + ib[idx+1] += (u32)(dst_reloc->lobj.gpu_offset & 0xfffffffc); + ib[idx+3] += (upper_32_bits(dst_reloc->lobj.gpu_offset) << 16) & 0x00ff0000; + p->idx += 4; + break; + case DMA_PACKET_NOP: + p->idx += 1; + break; + default: + DRM_ERROR("Unknown packet type %d at %d !\n", cmd, idx); + return -EINVAL; + } + } while (p->idx < p->chunks[p->chunk_ib_idx].length_dw); +#if 0 + for (r = 0; r < p->ib->length_dw; r++) { + DRM_INFO("%05d 0x%08X\n", r, p->ib.ptr[r]); + DRM_MDELAY(1); + } +#endif + return 0; +} diff --git a/sys/dev/drm2/radeon/r600_cs.h b/sys/dev/drm2/radeon/r600_cs.h new file mode 100644 index 00000000000..9db545e4c70 --- /dev/null +++ b/sys/dev/drm2/radeon/r600_cs.h @@ -0,0 +1,11 @@ + +#include +__FBSDID("$FreeBSD$"); + +#ifndef __R600_CS_H__ +#define __R600_CS_H__ + +int r600_dma_cs_next_reloc(struct radeon_cs_parser *p, + struct radeon_cs_reloc **cs_reloc); + +#endif /* !defined(__R600_CS_H__) */ diff --git a/sys/dev/drm2/radeon/r600_hdmi.c b/sys/dev/drm2/radeon/r600_hdmi.c new file mode 100644 index 00000000000..46a72fbad0f --- /dev/null +++ b/sys/dev/drm2/radeon/r600_hdmi.c @@ -0,0 +1,589 @@ +/* + * Copyright 2008 Advanced Micro Devices, Inc. + * Copyright 2008 Red Hat Inc. + * Copyright 2009 Christian König. + * + * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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. + * + * Authors: Christian König + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include "radeon.h" +#include "radeon_asic.h" +#include "r600d.h" +#include "atom.h" + +/* + * HDMI color format + */ +enum r600_hdmi_color_format { + RGB = 0, + YCC_422 = 1, + YCC_444 = 2 +}; + +/* + * IEC60958 status bits + */ +enum r600_hdmi_iec_status_bits { + AUDIO_STATUS_DIG_ENABLE = 0x01, + AUDIO_STATUS_V = 0x02, + AUDIO_STATUS_VCFG = 0x04, + AUDIO_STATUS_EMPHASIS = 0x08, + AUDIO_STATUS_COPYRIGHT = 0x10, + AUDIO_STATUS_NONAUDIO = 0x20, + AUDIO_STATUS_PROFESSIONAL = 0x40, + AUDIO_STATUS_LEVEL = 0x80 +}; + +static const struct radeon_hdmi_acr r600_hdmi_predefined_acr[] = { + /* 32kHz 44.1kHz 48kHz */ + /* Clock N CTS N CTS N CTS */ + { 25174, 4576, 28125, 7007, 31250, 6864, 28125 }, /* 25,20/1.001 MHz */ + { 25200, 4096, 25200, 6272, 28000, 6144, 25200 }, /* 25.20 MHz */ + { 27000, 4096, 27000, 6272, 30000, 6144, 27000 }, /* 27.00 MHz */ + { 27027, 4096, 27027, 6272, 30030, 6144, 27027 }, /* 27.00*1.001 MHz */ + { 54000, 4096, 54000, 6272, 60000, 6144, 54000 }, /* 54.00 MHz */ + { 54054, 4096, 54054, 6272, 60060, 6144, 54054 }, /* 54.00*1.001 MHz */ + { 74175, 11648, 210937, 17836, 234375, 11648, 140625 }, /* 74.25/1.001 MHz */ + { 74250, 4096, 74250, 6272, 82500, 6144, 74250 }, /* 74.25 MHz */ + { 148351, 11648, 421875, 8918, 234375, 5824, 140625 }, /* 148.50/1.001 MHz */ + { 148500, 4096, 148500, 6272, 165000, 6144, 148500 }, /* 148.50 MHz */ + { 0, 4096, 0, 6272, 0, 6144, 0 } /* Other */ +}; + +/* + * calculate CTS value if it's not found in the table + */ +static void r600_hdmi_calc_cts(uint32_t clock, int *CTS, int N, int freq) +{ + if (*CTS == 0) + *CTS = clock * N / (128 * freq) * 1000; + DRM_DEBUG("Using ACR timing N=%d CTS=%d for frequency %d\n", + N, *CTS, freq); +} + +struct radeon_hdmi_acr r600_hdmi_acr(uint32_t clock) +{ + struct radeon_hdmi_acr res; + u8 i; + + for (i = 0; r600_hdmi_predefined_acr[i].clock != clock && + r600_hdmi_predefined_acr[i].clock != 0; i++) + ; + res = r600_hdmi_predefined_acr[i]; + + /* In case some CTS are missing */ + r600_hdmi_calc_cts(clock, &res.cts_32khz, res.n_32khz, 32000); + r600_hdmi_calc_cts(clock, &res.cts_44_1khz, res.n_44_1khz, 44100); + r600_hdmi_calc_cts(clock, &res.cts_48khz, res.n_48khz, 48000); + + return res; +} + +/* + * update the N and CTS parameters for a given pixel clock rate + */ +static void r600_hdmi_update_ACR(struct drm_encoder *encoder, uint32_t clock) +{ + struct drm_device *dev = encoder->dev; + struct radeon_device *rdev = dev->dev_private; + struct radeon_hdmi_acr acr = r600_hdmi_acr(clock); + struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); + struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv; + uint32_t offset = dig->afmt->offset; + + WREG32(HDMI0_ACR_32_0 + offset, HDMI0_ACR_CTS_32(acr.cts_32khz)); + WREG32(HDMI0_ACR_32_1 + offset, acr.n_32khz); + + WREG32(HDMI0_ACR_44_0 + offset, HDMI0_ACR_CTS_44(acr.cts_44_1khz)); + WREG32(HDMI0_ACR_44_1 + offset, acr.n_44_1khz); + + WREG32(HDMI0_ACR_48_0 + offset, HDMI0_ACR_CTS_48(acr.cts_48khz)); + WREG32(HDMI0_ACR_48_1 + offset, acr.n_48khz); +} + +/* + * calculate the crc for a given info frame + */ +static void r600_hdmi_infoframe_checksum(uint8_t packetType, + uint8_t versionNumber, + uint8_t length, + uint8_t *frame) +{ + int i; + frame[0] = packetType + versionNumber + length; + for (i = 1; i <= length; i++) + frame[0] += frame[i]; + frame[0] = 0x100 - frame[0]; +} + +/* + * build a HDMI Video Info Frame + */ +static void r600_hdmi_videoinfoframe( + struct drm_encoder *encoder, + enum r600_hdmi_color_format color_format, + int active_information_present, + uint8_t active_format_aspect_ratio, + uint8_t scan_information, + uint8_t colorimetry, + uint8_t ex_colorimetry, + uint8_t quantization, + int ITC, + uint8_t picture_aspect_ratio, + uint8_t video_format_identification, + uint8_t pixel_repetition, + uint8_t non_uniform_picture_scaling, + uint8_t bar_info_data_valid, + uint16_t top_bar, + uint16_t bottom_bar, + uint16_t left_bar, + uint16_t right_bar +) +{ + struct drm_device *dev = encoder->dev; + struct radeon_device *rdev = dev->dev_private; + struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); + struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv; + uint32_t offset = dig->afmt->offset; + + uint8_t frame[14]; + + frame[0x0] = 0; + frame[0x1] = + (scan_information & 0x3) | + ((bar_info_data_valid & 0x3) << 2) | + ((active_information_present & 0x1) << 4) | + ((color_format & 0x3) << 5); + frame[0x2] = + (active_format_aspect_ratio & 0xF) | + ((picture_aspect_ratio & 0x3) << 4) | + ((colorimetry & 0x3) << 6); + frame[0x3] = + (non_uniform_picture_scaling & 0x3) | + ((quantization & 0x3) << 2) | + ((ex_colorimetry & 0x7) << 4) | + ((ITC & 0x1) << 7); + frame[0x4] = (video_format_identification & 0x7F); + frame[0x5] = (pixel_repetition & 0xF); + frame[0x6] = (top_bar & 0xFF); + frame[0x7] = (top_bar >> 8); + frame[0x8] = (bottom_bar & 0xFF); + frame[0x9] = (bottom_bar >> 8); + frame[0xA] = (left_bar & 0xFF); + frame[0xB] = (left_bar >> 8); + frame[0xC] = (right_bar & 0xFF); + frame[0xD] = (right_bar >> 8); + + r600_hdmi_infoframe_checksum(0x82, 0x02, 0x0D, frame); + /* Our header values (type, version, length) should be alright, Intel + * is using the same. Checksum function also seems to be OK, it works + * fine for audio infoframe. However calculated value is always lower + * by 2 in comparison to fglrx. It breaks displaying anything in case + * of TVs that strictly check the checksum. Hack it manually here to + * workaround this issue. */ + frame[0x0] += 2; + + WREG32(HDMI0_AVI_INFO0 + offset, + frame[0x0] | (frame[0x1] << 8) | (frame[0x2] << 16) | (frame[0x3] << 24)); + WREG32(HDMI0_AVI_INFO1 + offset, + frame[0x4] | (frame[0x5] << 8) | (frame[0x6] << 16) | (frame[0x7] << 24)); + WREG32(HDMI0_AVI_INFO2 + offset, + frame[0x8] | (frame[0x9] << 8) | (frame[0xA] << 16) | (frame[0xB] << 24)); + WREG32(HDMI0_AVI_INFO3 + offset, + frame[0xC] | (frame[0xD] << 8)); +} + +/* + * build a Audio Info Frame + */ +static void r600_hdmi_audioinfoframe( + struct drm_encoder *encoder, + uint8_t channel_count, + uint8_t coding_type, + uint8_t sample_size, + uint8_t sample_frequency, + uint8_t format, + uint8_t channel_allocation, + uint8_t level_shift, + int downmix_inhibit +) +{ + struct drm_device *dev = encoder->dev; + struct radeon_device *rdev = dev->dev_private; + struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); + struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv; + uint32_t offset = dig->afmt->offset; + + uint8_t frame[11]; + + frame[0x0] = 0; + frame[0x1] = (channel_count & 0x7) | ((coding_type & 0xF) << 4); + frame[0x2] = (sample_size & 0x3) | ((sample_frequency & 0x7) << 2); + frame[0x3] = format; + frame[0x4] = channel_allocation; + frame[0x5] = ((level_shift & 0xF) << 3) | ((downmix_inhibit & 0x1) << 7); + frame[0x6] = 0; + frame[0x7] = 0; + frame[0x8] = 0; + frame[0x9] = 0; + frame[0xA] = 0; + + r600_hdmi_infoframe_checksum(0x84, 0x01, 0x0A, frame); + + WREG32(HDMI0_AUDIO_INFO0 + offset, + frame[0x0] | (frame[0x1] << 8) | (frame[0x2] << 16) | (frame[0x3] << 24)); + WREG32(HDMI0_AUDIO_INFO1 + offset, + frame[0x4] | (frame[0x5] << 8) | (frame[0x6] << 16) | (frame[0x8] << 24)); +} + +/* + * test if audio buffer is filled enough to start playing + */ +static bool r600_hdmi_is_audio_buffer_filled(struct drm_encoder *encoder) +{ + struct drm_device *dev = encoder->dev; + struct radeon_device *rdev = dev->dev_private; + struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); + struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv; + uint32_t offset = dig->afmt->offset; + + return (RREG32(HDMI0_STATUS + offset) & 0x10) != 0; +} + +/* + * have buffer status changed since last call? + */ +int r600_hdmi_buffer_status_changed(struct drm_encoder *encoder) +{ + struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); + struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv; + int status, result; + + if (!dig->afmt || !dig->afmt->enabled) + return 0; + + status = r600_hdmi_is_audio_buffer_filled(encoder); + result = dig->afmt->last_buffer_filled_status != status; + dig->afmt->last_buffer_filled_status = status; + + return result; +} + +/* + * write the audio workaround status to the hardware + */ +static void r600_hdmi_audio_workaround(struct drm_encoder *encoder) +{ + struct drm_device *dev = encoder->dev; + struct radeon_device *rdev = dev->dev_private; + struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); + struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv; + uint32_t offset = dig->afmt->offset; + bool hdmi_audio_workaround = false; /* FIXME */ + u32 value; + + if (!hdmi_audio_workaround || + r600_hdmi_is_audio_buffer_filled(encoder)) + value = 0; /* disable workaround */ + else + value = HDMI0_AUDIO_TEST_EN; /* enable workaround */ + WREG32_P(HDMI0_AUDIO_PACKET_CONTROL + offset, + value, ~HDMI0_AUDIO_TEST_EN); +} + + +/* + * update the info frames with the data from the current display mode + */ +void r600_hdmi_setmode(struct drm_encoder *encoder, struct drm_display_mode *mode) +{ + struct drm_device *dev = encoder->dev; + struct radeon_device *rdev = dev->dev_private; + struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); + struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv; + uint32_t offset; + + /* Silent, r600_hdmi_enable will raise WARN for us */ + if (!dig->afmt->enabled) + return; + offset = dig->afmt->offset; + + r600_audio_set_clock(encoder, mode->clock); + + WREG32(HDMI0_VBI_PACKET_CONTROL + offset, + HDMI0_NULL_SEND); /* send null packets when required */ + + WREG32(HDMI0_AUDIO_CRC_CONTROL + offset, 0x1000); + + if (ASIC_IS_DCE32(rdev)) { + WREG32(HDMI0_AUDIO_PACKET_CONTROL + offset, + HDMI0_AUDIO_DELAY_EN(1) | /* default audio delay */ + HDMI0_AUDIO_PACKETS_PER_LINE(3)); /* should be suffient for all audio modes and small enough for all hblanks */ + WREG32(AFMT_AUDIO_PACKET_CONTROL + offset, + AFMT_AUDIO_SAMPLE_SEND | /* send audio packets */ + AFMT_60958_CS_UPDATE); /* allow 60958 channel status fields to be updated */ + } else { + WREG32(HDMI0_AUDIO_PACKET_CONTROL + offset, + HDMI0_AUDIO_SAMPLE_SEND | /* send audio packets */ + HDMI0_AUDIO_DELAY_EN(1) | /* default audio delay */ + HDMI0_AUDIO_PACKETS_PER_LINE(3) | /* should be suffient for all audio modes and small enough for all hblanks */ + HDMI0_60958_CS_UPDATE); /* allow 60958 channel status fields to be updated */ + } + + WREG32(HDMI0_ACR_PACKET_CONTROL + offset, + HDMI0_ACR_AUTO_SEND | /* allow hw to sent ACR packets when required */ + HDMI0_ACR_SOURCE); /* select SW CTS value */ + + WREG32(HDMI0_VBI_PACKET_CONTROL + offset, + HDMI0_NULL_SEND | /* send null packets when required */ + HDMI0_GC_SEND | /* send general control packets */ + HDMI0_GC_CONT); /* send general control packets every frame */ + + /* TODO: HDMI0_AUDIO_INFO_UPDATE */ + WREG32(HDMI0_INFOFRAME_CONTROL0 + offset, + HDMI0_AVI_INFO_SEND | /* enable AVI info frames */ + HDMI0_AVI_INFO_CONT | /* send AVI info frames every frame/field */ + HDMI0_AUDIO_INFO_SEND | /* enable audio info frames (frames won't be set until audio is enabled) */ + HDMI0_AUDIO_INFO_CONT); /* send audio info frames every frame/field */ + + WREG32(HDMI0_INFOFRAME_CONTROL1 + offset, + HDMI0_AVI_INFO_LINE(2) | /* anything other than 0 */ + HDMI0_AUDIO_INFO_LINE(2)); /* anything other than 0 */ + + WREG32(HDMI0_GC + offset, 0); /* unset HDMI0_GC_AVMUTE */ + + r600_hdmi_videoinfoframe(encoder, RGB, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); + + r600_hdmi_update_ACR(encoder, mode->clock); + + /* it's unknown what these bits do excatly, but it's indeed quite useful for debugging */ + WREG32(HDMI0_RAMP_CONTROL0 + offset, 0x00FFFFFF); + WREG32(HDMI0_RAMP_CONTROL1 + offset, 0x007FFFFF); + WREG32(HDMI0_RAMP_CONTROL2 + offset, 0x00000001); + WREG32(HDMI0_RAMP_CONTROL3 + offset, 0x00000001); + + r600_hdmi_audio_workaround(encoder); +} + +/* + * update settings with current parameters from audio engine + */ +void r600_hdmi_update_audio_settings(struct drm_encoder *encoder) +{ + struct drm_device *dev = encoder->dev; + struct radeon_device *rdev = dev->dev_private; + struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); + struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv; + struct r600_audio audio = r600_audio_status(rdev); + uint32_t offset; + uint32_t iec; + + if (!dig->afmt || !dig->afmt->enabled) + return; + offset = dig->afmt->offset; + + DRM_DEBUG("%s with %d channels, %d Hz sampling rate, %d bits per sample,\n", + r600_hdmi_is_audio_buffer_filled(encoder) ? "playing" : "stopped", + audio.channels, audio.rate, audio.bits_per_sample); + DRM_DEBUG("0x%02X IEC60958 status bits and 0x%02X category code\n", + (int)audio.status_bits, (int)audio.category_code); + + iec = 0; + if (audio.status_bits & AUDIO_STATUS_PROFESSIONAL) + iec |= 1 << 0; + if (audio.status_bits & AUDIO_STATUS_NONAUDIO) + iec |= 1 << 1; + if (audio.status_bits & AUDIO_STATUS_COPYRIGHT) + iec |= 1 << 2; + if (audio.status_bits & AUDIO_STATUS_EMPHASIS) + iec |= 1 << 3; + + iec |= HDMI0_60958_CS_CATEGORY_CODE(audio.category_code); + + switch (audio.rate) { + case 32000: + iec |= HDMI0_60958_CS_SAMPLING_FREQUENCY(0x3); + break; + case 44100: + iec |= HDMI0_60958_CS_SAMPLING_FREQUENCY(0x0); + break; + case 48000: + iec |= HDMI0_60958_CS_SAMPLING_FREQUENCY(0x2); + break; + case 88200: + iec |= HDMI0_60958_CS_SAMPLING_FREQUENCY(0x8); + break; + case 96000: + iec |= HDMI0_60958_CS_SAMPLING_FREQUENCY(0xa); + break; + case 176400: + iec |= HDMI0_60958_CS_SAMPLING_FREQUENCY(0xc); + break; + case 192000: + iec |= HDMI0_60958_CS_SAMPLING_FREQUENCY(0xe); + break; + } + + WREG32(HDMI0_60958_0 + offset, iec); + + iec = 0; + switch (audio.bits_per_sample) { + case 16: + iec |= HDMI0_60958_CS_WORD_LENGTH(0x2); + break; + case 20: + iec |= HDMI0_60958_CS_WORD_LENGTH(0x3); + break; + case 24: + iec |= HDMI0_60958_CS_WORD_LENGTH(0xb); + break; + } + if (audio.status_bits & AUDIO_STATUS_V) + iec |= 0x5 << 16; + WREG32_P(HDMI0_60958_1 + offset, iec, ~0x5000f); + + r600_hdmi_audioinfoframe(encoder, audio.channels - 1, 0, 0, 0, 0, 0, 0, + 0); + + r600_hdmi_audio_workaround(encoder); +} + +/* + * enable the HDMI engine + */ +void r600_hdmi_enable(struct drm_encoder *encoder) +{ + struct drm_device *dev = encoder->dev; + struct radeon_device *rdev = dev->dev_private; + struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); + struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv; + uint32_t offset; + u32 hdmi; + + if (ASIC_IS_DCE6(rdev)) + return; + + /* Silent, r600_hdmi_enable will raise WARN for us */ + if (dig->afmt->enabled) + return; + offset = dig->afmt->offset; + + /* Older chipsets require setting HDMI and routing manually */ + if (rdev->family >= CHIP_R600 && !ASIC_IS_DCE3(rdev)) { + hdmi = HDMI0_ERROR_ACK | HDMI0_ENABLE; + switch (radeon_encoder->encoder_id) { + case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_TMDS1: + WREG32_P(AVIVO_TMDSA_CNTL, AVIVO_TMDSA_CNTL_HDMI_EN, + ~AVIVO_TMDSA_CNTL_HDMI_EN); + hdmi |= HDMI0_STREAM(HDMI0_STREAM_TMDSA); + break; + case ENCODER_OBJECT_ID_INTERNAL_LVTM1: + WREG32_P(AVIVO_LVTMA_CNTL, AVIVO_LVTMA_CNTL_HDMI_EN, + ~AVIVO_LVTMA_CNTL_HDMI_EN); + hdmi |= HDMI0_STREAM(HDMI0_STREAM_LVTMA); + break; + case ENCODER_OBJECT_ID_INTERNAL_DDI: + WREG32_P(DDIA_CNTL, DDIA_HDMI_EN, ~DDIA_HDMI_EN); + hdmi |= HDMI0_STREAM(HDMI0_STREAM_DDIA); + break; + case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DVO1: + hdmi |= HDMI0_STREAM(HDMI0_STREAM_DVOA); + break; + default: + dev_err(rdev->dev, "Invalid encoder for HDMI: 0x%X\n", + radeon_encoder->encoder_id); + break; + } + WREG32(HDMI0_CONTROL + offset, hdmi); + } + + if (rdev->irq.installed) { + /* if irq is available use it */ + radeon_irq_kms_enable_afmt(rdev, dig->afmt->id); + } + + dig->afmt->enabled = true; + + DRM_DEBUG("Enabling HDMI interface @ 0x%04X for encoder 0x%x\n", + offset, radeon_encoder->encoder_id); +} + +/* + * disable the HDMI engine + */ +void r600_hdmi_disable(struct drm_encoder *encoder) +{ + struct drm_device *dev = encoder->dev; + struct radeon_device *rdev = dev->dev_private; + struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); + struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv; + uint32_t offset; + + if (ASIC_IS_DCE6(rdev)) + return; + + /* Called for ATOM_ENCODER_MODE_HDMI only */ + if (!dig || !dig->afmt) { + DRM_ERROR("%s: !dig || !dig->afmt", __func__); + return; + } + if (!dig->afmt->enabled) + return; + offset = dig->afmt->offset; + + DRM_DEBUG("Disabling HDMI interface @ 0x%04X for encoder 0x%x\n", + offset, radeon_encoder->encoder_id); + + /* disable irq */ + radeon_irq_kms_disable_afmt(rdev, dig->afmt->id); + + /* Older chipsets not handled by AtomBIOS */ + if (rdev->family >= CHIP_R600 && !ASIC_IS_DCE3(rdev)) { + switch (radeon_encoder->encoder_id) { + case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_TMDS1: + WREG32_P(AVIVO_TMDSA_CNTL, 0, + ~AVIVO_TMDSA_CNTL_HDMI_EN); + break; + case ENCODER_OBJECT_ID_INTERNAL_LVTM1: + WREG32_P(AVIVO_LVTMA_CNTL, 0, + ~AVIVO_LVTMA_CNTL_HDMI_EN); + break; + case ENCODER_OBJECT_ID_INTERNAL_DDI: + WREG32_P(DDIA_CNTL, 0, ~DDIA_HDMI_EN); + break; + case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DVO1: + break; + default: + dev_err(rdev->dev, "Invalid encoder for HDMI: 0x%X\n", + radeon_encoder->encoder_id); + break; + } + WREG32(HDMI0_CONTROL + offset, HDMI0_ERROR_ACK); + } + + dig->afmt->enabled = false; +} diff --git a/sys/dev/drm2/radeon/r600_reg.h b/sys/dev/drm2/radeon/r600_reg.h new file mode 100644 index 00000000000..e0578cc3e75 --- /dev/null +++ b/sys/dev/drm2/radeon/r600_reg.h @@ -0,0 +1,177 @@ +/* + * Copyright 2008 Advanced Micro Devices, Inc. + * Copyright 2008 Red Hat Inc. + * Copyright 2009 Jerome Glisse. + * + * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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. + * + * Authors: Dave Airlie + * Alex Deucher + * Jerome Glisse + */ +#ifndef __R600_REG_H__ +#define __R600_REG_H__ + +#include +__FBSDID("$FreeBSD$"); + +#define R600_PCIE_PORT_INDEX 0x0038 +#define R600_PCIE_PORT_DATA 0x003c + +#define R600_MC_VM_FB_LOCATION 0x2180 +#define R600_MC_FB_BASE_MASK 0x0000FFFF +#define R600_MC_FB_BASE_SHIFT 0 +#define R600_MC_FB_TOP_MASK 0xFFFF0000 +#define R600_MC_FB_TOP_SHIFT 16 +#define R600_MC_VM_AGP_TOP 0x2184 +#define R600_MC_AGP_TOP_MASK 0x0003FFFF +#define R600_MC_AGP_TOP_SHIFT 0 +#define R600_MC_VM_AGP_BOT 0x2188 +#define R600_MC_AGP_BOT_MASK 0x0003FFFF +#define R600_MC_AGP_BOT_SHIFT 0 +#define R600_MC_VM_AGP_BASE 0x218c +#define R600_MC_VM_SYSTEM_APERTURE_LOW_ADDR 0x2190 +#define R600_LOGICAL_PAGE_NUMBER_MASK 0x000FFFFF +#define R600_LOGICAL_PAGE_NUMBER_SHIFT 0 +#define R600_MC_VM_SYSTEM_APERTURE_HIGH_ADDR 0x2194 +#define R600_MC_VM_SYSTEM_APERTURE_DEFAULT_ADDR 0x2198 + +#define R700_MC_VM_FB_LOCATION 0x2024 +#define R700_MC_FB_BASE_MASK 0x0000FFFF +#define R700_MC_FB_BASE_SHIFT 0 +#define R700_MC_FB_TOP_MASK 0xFFFF0000 +#define R700_MC_FB_TOP_SHIFT 16 +#define R700_MC_VM_AGP_TOP 0x2028 +#define R700_MC_AGP_TOP_MASK 0x0003FFFF +#define R700_MC_AGP_TOP_SHIFT 0 +#define R700_MC_VM_AGP_BOT 0x202c +#define R700_MC_AGP_BOT_MASK 0x0003FFFF +#define R700_MC_AGP_BOT_SHIFT 0 +#define R700_MC_VM_AGP_BASE 0x2030 +#define R700_MC_VM_SYSTEM_APERTURE_LOW_ADDR 0x2034 +#define R700_LOGICAL_PAGE_NUMBER_MASK 0x000FFFFF +#define R700_LOGICAL_PAGE_NUMBER_SHIFT 0 +#define R700_MC_VM_SYSTEM_APERTURE_HIGH_ADDR 0x2038 +#define R700_MC_VM_SYSTEM_APERTURE_DEFAULT_ADDR 0x203c + +#define R600_RAMCFG 0x2408 +# define R600_CHANSIZE (1 << 7) +# define R600_CHANSIZE_OVERRIDE (1 << 10) + + +#define R600_GENERAL_PWRMGT 0x618 +# define R600_OPEN_DRAIN_PADS (1 << 11) + +#define R600_LOWER_GPIO_ENABLE 0x710 +#define R600_CTXSW_VID_LOWER_GPIO_CNTL 0x718 +#define R600_HIGH_VID_LOWER_GPIO_CNTL 0x71c +#define R600_MEDIUM_VID_LOWER_GPIO_CNTL 0x720 +#define R600_LOW_VID_LOWER_GPIO_CNTL 0x724 + +#define R600_D1GRPH_SWAP_CONTROL 0x610C +# define R600_D1GRPH_SWAP_ENDIAN_NONE (0 << 0) +# define R600_D1GRPH_SWAP_ENDIAN_16BIT (1 << 0) +# define R600_D1GRPH_SWAP_ENDIAN_32BIT (2 << 0) +# define R600_D1GRPH_SWAP_ENDIAN_64BIT (3 << 0) + +#define R600_HDP_NONSURFACE_BASE 0x2c04 + +#define R600_BUS_CNTL 0x5420 +# define R600_BIOS_ROM_DIS (1 << 1) +#define R600_CONFIG_CNTL 0x5424 +#define R600_CONFIG_MEMSIZE 0x5428 +#define R600_CONFIG_F0_BASE 0x542C +#define R600_CONFIG_APER_SIZE 0x5430 + +#define R600_BIF_FB_EN 0x5490 +#define R600_FB_READ_EN (1 << 0) +#define R600_FB_WRITE_EN (1 << 1) + +#define R600_CITF_CNTL 0x200c +#define R600_BLACKOUT_MASK 0x00000003 + +#define R700_MC_CITF_CNTL 0x25c0 + +#define R600_ROM_CNTL 0x1600 +# define R600_SCK_OVERWRITE (1 << 1) +# define R600_SCK_PRESCALE_CRYSTAL_CLK_SHIFT 28 +# define R600_SCK_PRESCALE_CRYSTAL_CLK_MASK (0xf << 28) + +#define R600_CG_SPLL_FUNC_CNTL 0x600 +# define R600_SPLL_BYPASS_EN (1 << 3) +#define R600_CG_SPLL_STATUS 0x60c +# define R600_SPLL_CHG_STATUS (1 << 1) + +#define R600_BIOS_0_SCRATCH 0x1724 +#define R600_BIOS_1_SCRATCH 0x1728 +#define R600_BIOS_2_SCRATCH 0x172c +#define R600_BIOS_3_SCRATCH 0x1730 +#define R600_BIOS_4_SCRATCH 0x1734 +#define R600_BIOS_5_SCRATCH 0x1738 +#define R600_BIOS_6_SCRATCH 0x173c +#define R600_BIOS_7_SCRATCH 0x1740 + +/* Audio, these regs were reverse enginered, + * so the chance is high that the naming is wrong + * R6xx+ ??? */ + +/* Audio clocks */ +#define R600_AUDIO_PLL1_MUL 0x0514 +#define R600_AUDIO_PLL1_DIV 0x0518 +#define R600_AUDIO_PLL2_MUL 0x0524 +#define R600_AUDIO_PLL2_DIV 0x0528 +#define R600_AUDIO_CLK_SRCSEL 0x0534 + +/* Audio general */ +#define R600_AUDIO_ENABLE 0x7300 +#define R600_AUDIO_TIMING 0x7344 + +/* Audio params */ +#define R600_AUDIO_VENDOR_ID 0x7380 +#define R600_AUDIO_REVISION_ID 0x7384 +#define R600_AUDIO_ROOT_NODE_COUNT 0x7388 +#define R600_AUDIO_NID1_NODE_COUNT 0x738c +#define R600_AUDIO_NID1_TYPE 0x7390 +#define R600_AUDIO_SUPPORTED_SIZE_RATE 0x7394 +#define R600_AUDIO_SUPPORTED_CODEC 0x7398 +#define R600_AUDIO_SUPPORTED_POWER_STATES 0x739c +#define R600_AUDIO_NID2_CAPS 0x73a0 +#define R600_AUDIO_NID3_CAPS 0x73a4 +#define R600_AUDIO_NID3_PIN_CAPS 0x73a8 + +/* Audio conn list */ +#define R600_AUDIO_CONN_LIST_LEN 0x73ac +#define R600_AUDIO_CONN_LIST 0x73b0 + +/* Audio verbs */ +#define R600_AUDIO_RATE_BPS_CHANNEL 0x73c0 +#define R600_AUDIO_PLAYING 0x73c4 +#define R600_AUDIO_IMPLEMENTATION_ID 0x73c8 +#define R600_AUDIO_CONFIG_DEFAULT 0x73cc +#define R600_AUDIO_PIN_SENSE 0x73d0 +#define R600_AUDIO_PIN_WIDGET_CNTL 0x73d4 +#define R600_AUDIO_STATUS_BITS 0x73d8 + +#define DCE2_HDMI_OFFSET0 (0x7400 - 0x7400) +#define DCE2_HDMI_OFFSET1 (0x7700 - 0x7400) +/* DCE3.2 second instance starts at 0x7800 */ +#define DCE3_HDMI_OFFSET0 (0x7400 - 0x7400) +#define DCE3_HDMI_OFFSET1 (0x7800 - 0x7400) + +#endif diff --git a/sys/dev/drm2/radeon/r600_reg_safe.h b/sys/dev/drm2/radeon/r600_reg_safe.h new file mode 100644 index 00000000000..003a40ded4d --- /dev/null +++ b/sys/dev/drm2/radeon/r600_reg_safe.h @@ -0,0 +1,493 @@ +#include +__FBSDID("$FreeBSD$"); + +static const unsigned r600_reg_safe_bm[1952] = { + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFEFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFEF, 0xFFFFFFFF, 0xCFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFBD1EFFF, 0xCF3FFFFF, 0xFFFFFFFF, + 0xFFFFFFDF, 0xFFFFFFFF, 0xFFF0FEEF, 0xEFFFFFFF, + 0xFFFFFFC1, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFF7, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFDF, 0xFFFFFFFF, 0xFFFF7FFE, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0x00000000, 0xFFFFFFF0, 0xFFFFFFFB, 0xFFFFFFFF, + 0xFFFFFFFD, 0xFFFFFFFF, 0xFFFFFFDE, 0xFFFFFFFF, + 0xFFFFAFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0x00000000, 0x00000000, 0xFFFFFF00, 0xFFFFFFFF, + 0x00000000, 0x00000000, 0xFFFFFF00, 0xFFFFFFFF, + 0x00000000, 0x00000000, 0xFFFFFF00, 0xFFFFFFFF, + 0xFFFC0000, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFC3FF, 0xFFFFFFFF, 0x0000F0FF, 0x00000000, + 0x000CE000, 0x00000000, 0xFFD00000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0xFFFF8000, + 0x0001801F, 0xFC000000, 0xFFFFFFFF, 0xFFFFFE00, + 0x7BCFFE05, 0xFE07FDEF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xF7E20000, 0xDDDF9CDD, 0xFFFFE3FD, 0xFFFFFFFF, + 0xFFFB0E02, 0xFFFFFFFF, 0xFFFDC3E7, 0x3FFFFFFF, + 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xDFFFFFFF, +}; diff --git a/sys/dev/drm2/radeon/r600d.h b/sys/dev/drm2/radeon/r600d.h new file mode 100644 index 00000000000..41cc7c6dd89 --- /dev/null +++ b/sys/dev/drm2/radeon/r600d.h @@ -0,0 +1,1932 @@ +/* + * Copyright 2009 Advanced Micro Devices, Inc. + * Copyright 2009 Red Hat Inc. + * + * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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. + * + * Authors: Dave Airlie + * Alex Deucher + * Jerome Glisse + */ +#ifndef R600D_H +#define R600D_H + +#include +__FBSDID("$FreeBSD$"); + +#define CP_PACKET2 0x80000000 +#define PACKET2_PAD_SHIFT 0 +#define PACKET2_PAD_MASK (0x3fffffff << 0) + +#define PACKET2(v) (CP_PACKET2 | REG_SET(PACKET2_PAD, (v))) + +#define R6XX_MAX_SH_GPRS 256 +#define R6XX_MAX_TEMP_GPRS 16 +#define R6XX_MAX_SH_THREADS 256 +#define R6XX_MAX_SH_STACK_ENTRIES 4096 +#define R6XX_MAX_BACKENDS 8 +#define R6XX_MAX_BACKENDS_MASK 0xff +#define R6XX_MAX_SIMDS 8 +#define R6XX_MAX_SIMDS_MASK 0xff +#define R6XX_MAX_PIPES 8 +#define R6XX_MAX_PIPES_MASK 0xff + +/* PTE flags */ +#define PTE_VALID (1 << 0) +#define PTE_SYSTEM (1 << 1) +#define PTE_SNOOPED (1 << 2) +#define PTE_READABLE (1 << 5) +#define PTE_WRITEABLE (1 << 6) + +/* tiling bits */ +#define ARRAY_LINEAR_GENERAL 0x00000000 +#define ARRAY_LINEAR_ALIGNED 0x00000001 +#define ARRAY_1D_TILED_THIN1 0x00000002 +#define ARRAY_2D_TILED_THIN1 0x00000004 + +/* Registers */ +#define ARB_POP 0x2418 +#define ENABLE_TC128 (1 << 30) +#define ARB_GDEC_RD_CNTL 0x246C + +#define CC_GC_SHADER_PIPE_CONFIG 0x8950 +#define CC_RB_BACKEND_DISABLE 0x98F4 +#define BACKEND_DISABLE(x) ((x) << 16) + +#define R_028808_CB_COLOR_CONTROL 0x28808 +#define S_028808_SPECIAL_OP(x) (((x) & 0x7) << 4) +#define G_028808_SPECIAL_OP(x) (((x) >> 4) & 0x7) +#define C_028808_SPECIAL_OP 0xFFFFFF8F +#define V_028808_SPECIAL_NORMAL 0x00 +#define V_028808_SPECIAL_DISABLE 0x01 +#define V_028808_SPECIAL_RESOLVE_BOX 0x07 + +#define CB_COLOR0_BASE 0x28040 +#define CB_COLOR1_BASE 0x28044 +#define CB_COLOR2_BASE 0x28048 +#define CB_COLOR3_BASE 0x2804C +#define CB_COLOR4_BASE 0x28050 +#define CB_COLOR5_BASE 0x28054 +#define CB_COLOR6_BASE 0x28058 +#define CB_COLOR7_BASE 0x2805C +#define CB_COLOR7_FRAG 0x280FC + +#define CB_COLOR0_SIZE 0x28060 +#define CB_COLOR0_VIEW 0x28080 +#define R_028080_CB_COLOR0_VIEW 0x028080 +#define S_028080_SLICE_START(x) (((x) & 0x7FF) << 0) +#define G_028080_SLICE_START(x) (((x) >> 0) & 0x7FF) +#define C_028080_SLICE_START 0xFFFFF800 +#define S_028080_SLICE_MAX(x) (((x) & 0x7FF) << 13) +#define G_028080_SLICE_MAX(x) (((x) >> 13) & 0x7FF) +#define C_028080_SLICE_MAX 0xFF001FFF +#define R_028084_CB_COLOR1_VIEW 0x028084 +#define R_028088_CB_COLOR2_VIEW 0x028088 +#define R_02808C_CB_COLOR3_VIEW 0x02808C +#define R_028090_CB_COLOR4_VIEW 0x028090 +#define R_028094_CB_COLOR5_VIEW 0x028094 +#define R_028098_CB_COLOR6_VIEW 0x028098 +#define R_02809C_CB_COLOR7_VIEW 0x02809C +#define R_028100_CB_COLOR0_MASK 0x028100 +#define S_028100_CMASK_BLOCK_MAX(x) (((x) & 0xFFF) << 0) +#define G_028100_CMASK_BLOCK_MAX(x) (((x) >> 0) & 0xFFF) +#define C_028100_CMASK_BLOCK_MAX 0xFFFFF000 +#define S_028100_FMASK_TILE_MAX(x) (((x) & 0xFFFFF) << 12) +#define G_028100_FMASK_TILE_MAX(x) (((x) >> 12) & 0xFFFFF) +#define C_028100_FMASK_TILE_MAX 0x00000FFF +#define R_028104_CB_COLOR1_MASK 0x028104 +#define R_028108_CB_COLOR2_MASK 0x028108 +#define R_02810C_CB_COLOR3_MASK 0x02810C +#define R_028110_CB_COLOR4_MASK 0x028110 +#define R_028114_CB_COLOR5_MASK 0x028114 +#define R_028118_CB_COLOR6_MASK 0x028118 +#define R_02811C_CB_COLOR7_MASK 0x02811C +#define CB_COLOR0_INFO 0x280a0 +# define CB_FORMAT(x) ((x) << 2) +# define CB_ARRAY_MODE(x) ((x) << 8) +# define CB_SOURCE_FORMAT(x) ((x) << 27) +# define CB_SF_EXPORT_FULL 0 +# define CB_SF_EXPORT_NORM 1 +#define CB_COLOR0_TILE 0x280c0 +#define CB_COLOR0_FRAG 0x280e0 +#define CB_COLOR0_MASK 0x28100 + +#define SQ_ALU_CONST_CACHE_PS_0 0x28940 +#define SQ_ALU_CONST_CACHE_PS_1 0x28944 +#define SQ_ALU_CONST_CACHE_PS_2 0x28948 +#define SQ_ALU_CONST_CACHE_PS_3 0x2894c +#define SQ_ALU_CONST_CACHE_PS_4 0x28950 +#define SQ_ALU_CONST_CACHE_PS_5 0x28954 +#define SQ_ALU_CONST_CACHE_PS_6 0x28958 +#define SQ_ALU_CONST_CACHE_PS_7 0x2895c +#define SQ_ALU_CONST_CACHE_PS_8 0x28960 +#define SQ_ALU_CONST_CACHE_PS_9 0x28964 +#define SQ_ALU_CONST_CACHE_PS_10 0x28968 +#define SQ_ALU_CONST_CACHE_PS_11 0x2896c +#define SQ_ALU_CONST_CACHE_PS_12 0x28970 +#define SQ_ALU_CONST_CACHE_PS_13 0x28974 +#define SQ_ALU_CONST_CACHE_PS_14 0x28978 +#define SQ_ALU_CONST_CACHE_PS_15 0x2897c +#define SQ_ALU_CONST_CACHE_VS_0 0x28980 +#define SQ_ALU_CONST_CACHE_VS_1 0x28984 +#define SQ_ALU_CONST_CACHE_VS_2 0x28988 +#define SQ_ALU_CONST_CACHE_VS_3 0x2898c +#define SQ_ALU_CONST_CACHE_VS_4 0x28990 +#define SQ_ALU_CONST_CACHE_VS_5 0x28994 +#define SQ_ALU_CONST_CACHE_VS_6 0x28998 +#define SQ_ALU_CONST_CACHE_VS_7 0x2899c +#define SQ_ALU_CONST_CACHE_VS_8 0x289a0 +#define SQ_ALU_CONST_CACHE_VS_9 0x289a4 +#define SQ_ALU_CONST_CACHE_VS_10 0x289a8 +#define SQ_ALU_CONST_CACHE_VS_11 0x289ac +#define SQ_ALU_CONST_CACHE_VS_12 0x289b0 +#define SQ_ALU_CONST_CACHE_VS_13 0x289b4 +#define SQ_ALU_CONST_CACHE_VS_14 0x289b8 +#define SQ_ALU_CONST_CACHE_VS_15 0x289bc +#define SQ_ALU_CONST_CACHE_GS_0 0x289c0 +#define SQ_ALU_CONST_CACHE_GS_1 0x289c4 +#define SQ_ALU_CONST_CACHE_GS_2 0x289c8 +#define SQ_ALU_CONST_CACHE_GS_3 0x289cc +#define SQ_ALU_CONST_CACHE_GS_4 0x289d0 +#define SQ_ALU_CONST_CACHE_GS_5 0x289d4 +#define SQ_ALU_CONST_CACHE_GS_6 0x289d8 +#define SQ_ALU_CONST_CACHE_GS_7 0x289dc +#define SQ_ALU_CONST_CACHE_GS_8 0x289e0 +#define SQ_ALU_CONST_CACHE_GS_9 0x289e4 +#define SQ_ALU_CONST_CACHE_GS_10 0x289e8 +#define SQ_ALU_CONST_CACHE_GS_11 0x289ec +#define SQ_ALU_CONST_CACHE_GS_12 0x289f0 +#define SQ_ALU_CONST_CACHE_GS_13 0x289f4 +#define SQ_ALU_CONST_CACHE_GS_14 0x289f8 +#define SQ_ALU_CONST_CACHE_GS_15 0x289fc + +#define CONFIG_MEMSIZE 0x5428 +#define CONFIG_CNTL 0x5424 +#define CP_STALLED_STAT1 0x8674 +#define CP_STALLED_STAT2 0x8678 +#define CP_BUSY_STAT 0x867C +#define CP_STAT 0x8680 +#define CP_COHER_BASE 0x85F8 +#define CP_DEBUG 0xC1FC +#define R_0086D8_CP_ME_CNTL 0x86D8 +#define S_0086D8_CP_ME_HALT(x) (((x) & 1)<<28) +#define C_0086D8_CP_ME_HALT(x) ((x) & 0xEFFFFFFF) +#define CP_ME_RAM_DATA 0xC160 +#define CP_ME_RAM_RADDR 0xC158 +#define CP_ME_RAM_WADDR 0xC15C +#define CP_MEQ_THRESHOLDS 0x8764 +#define MEQ_END(x) ((x) << 16) +#define ROQ_END(x) ((x) << 24) +#define CP_PERFMON_CNTL 0x87FC +#define CP_PFP_UCODE_ADDR 0xC150 +#define CP_PFP_UCODE_DATA 0xC154 +#define CP_QUEUE_THRESHOLDS 0x8760 +#define ROQ_IB1_START(x) ((x) << 0) +#define ROQ_IB2_START(x) ((x) << 8) +#define CP_RB_BASE 0xC100 +#define CP_RB_CNTL 0xC104 +#define RB_BUFSZ(x) ((x) << 0) +#define RB_BLKSZ(x) ((x) << 8) +#define RB_NO_UPDATE (1 << 27) +#define RB_RPTR_WR_ENA (1 << 31) +#define BUF_SWAP_32BIT (2 << 16) +#define CP_RB_RPTR 0x8700 +#define CP_RB_RPTR_ADDR 0xC10C +#define RB_RPTR_SWAP(x) ((x) << 0) +#define CP_RB_RPTR_ADDR_HI 0xC110 +#define CP_RB_RPTR_WR 0xC108 +#define CP_RB_WPTR 0xC114 +#define CP_RB_WPTR_ADDR 0xC118 +#define CP_RB_WPTR_ADDR_HI 0xC11C +#define CP_RB_WPTR_DELAY 0x8704 +#define CP_ROQ_IB1_STAT 0x8784 +#define CP_ROQ_IB2_STAT 0x8788 +#define CP_SEM_WAIT_TIMER 0x85BC + +#define DB_DEBUG 0x9830 +#define PREZ_MUST_WAIT_FOR_POSTZ_DONE (1 << 31) +#define DB_DEPTH_BASE 0x2800C +#define DB_HTILE_DATA_BASE 0x28014 +#define DB_HTILE_SURFACE 0x28D24 +#define S_028D24_HTILE_WIDTH(x) (((x) & 0x1) << 0) +#define G_028D24_HTILE_WIDTH(x) (((x) >> 0) & 0x1) +#define C_028D24_HTILE_WIDTH 0xFFFFFFFE +#define S_028D24_HTILE_HEIGHT(x) (((x) & 0x1) << 1) +#define G_028D24_HTILE_HEIGHT(x) (((x) >> 1) & 0x1) +#define C_028D24_HTILE_HEIGHT 0xFFFFFFFD +#define G_028D24_LINEAR(x) (((x) >> 2) & 0x1) +#define DB_WATERMARKS 0x9838 +#define DEPTH_FREE(x) ((x) << 0) +#define DEPTH_FLUSH(x) ((x) << 5) +#define DEPTH_PENDING_FREE(x) ((x) << 15) +#define DEPTH_CACHELINE_FREE(x) ((x) << 20) + +#define DCP_TILING_CONFIG 0x6CA0 +#define PIPE_TILING(x) ((x) << 1) +#define BANK_TILING(x) ((x) << 4) +#define GROUP_SIZE(x) ((x) << 6) +#define ROW_TILING(x) ((x) << 8) +#define BANK_SWAPS(x) ((x) << 11) +#define SAMPLE_SPLIT(x) ((x) << 14) +#define BACKEND_MAP(x) ((x) << 16) + +#define GB_TILING_CONFIG 0x98F0 +#define PIPE_TILING__SHIFT 1 +#define PIPE_TILING__MASK 0x0000000e + +#define GC_USER_SHADER_PIPE_CONFIG 0x8954 +#define INACTIVE_QD_PIPES(x) ((x) << 8) +#define INACTIVE_QD_PIPES_MASK 0x0000FF00 +#define INACTIVE_SIMDS(x) ((x) << 16) +#define INACTIVE_SIMDS_MASK 0x00FF0000 + +#define SQ_CONFIG 0x8c00 +# define VC_ENABLE (1 << 0) +# define EXPORT_SRC_C (1 << 1) +# define DX9_CONSTS (1 << 2) +# define ALU_INST_PREFER_VECTOR (1 << 3) +# define DX10_CLAMP (1 << 4) +# define CLAUSE_SEQ_PRIO(x) ((x) << 8) +# define PS_PRIO(x) ((x) << 24) +# define VS_PRIO(x) ((x) << 26) +# define GS_PRIO(x) ((x) << 28) +# define ES_PRIO(x) ((x) << 30) +#define SQ_GPR_RESOURCE_MGMT_1 0x8c04 +# define NUM_PS_GPRS(x) ((x) << 0) +# define NUM_VS_GPRS(x) ((x) << 16) +# define NUM_CLAUSE_TEMP_GPRS(x) ((x) << 28) +#define SQ_GPR_RESOURCE_MGMT_2 0x8c08 +# define NUM_GS_GPRS(x) ((x) << 0) +# define NUM_ES_GPRS(x) ((x) << 16) +#define SQ_THREAD_RESOURCE_MGMT 0x8c0c +# define NUM_PS_THREADS(x) ((x) << 0) +# define NUM_VS_THREADS(x) ((x) << 8) +# define NUM_GS_THREADS(x) ((x) << 16) +# define NUM_ES_THREADS(x) ((x) << 24) +#define SQ_STACK_RESOURCE_MGMT_1 0x8c10 +# define NUM_PS_STACK_ENTRIES(x) ((x) << 0) +# define NUM_VS_STACK_ENTRIES(x) ((x) << 16) +#define SQ_STACK_RESOURCE_MGMT_2 0x8c14 +# define NUM_GS_STACK_ENTRIES(x) ((x) << 0) +# define NUM_ES_STACK_ENTRIES(x) ((x) << 16) +#define SQ_ESGS_RING_BASE 0x8c40 +#define SQ_GSVS_RING_BASE 0x8c48 +#define SQ_ESTMP_RING_BASE 0x8c50 +#define SQ_GSTMP_RING_BASE 0x8c58 +#define SQ_VSTMP_RING_BASE 0x8c60 +#define SQ_PSTMP_RING_BASE 0x8c68 +#define SQ_FBUF_RING_BASE 0x8c70 +#define SQ_REDUC_RING_BASE 0x8c78 + +#define GRBM_CNTL 0x8000 +# define GRBM_READ_TIMEOUT(x) ((x) << 0) +#define GRBM_STATUS 0x8010 +#define CMDFIFO_AVAIL_MASK 0x0000001F +#define GUI_ACTIVE (1<<31) +#define GRBM_STATUS2 0x8014 +#define GRBM_SOFT_RESET 0x8020 +#define SOFT_RESET_CP (1<<0) + +#define CG_THERMAL_STATUS 0x7F4 +#define ASIC_T(x) ((x) << 0) +#define ASIC_T_MASK 0x1FF +#define ASIC_T_SHIFT 0 + +#define HDP_HOST_PATH_CNTL 0x2C00 +#define HDP_NONSURFACE_BASE 0x2C04 +#define HDP_NONSURFACE_INFO 0x2C08 +#define HDP_NONSURFACE_SIZE 0x2C0C +#define HDP_REG_COHERENCY_FLUSH_CNTL 0x54A0 +#define HDP_TILING_CONFIG 0x2F3C +#define HDP_DEBUG1 0x2F34 + +#define MC_VM_AGP_TOP 0x2184 +#define MC_VM_AGP_BOT 0x2188 +#define MC_VM_AGP_BASE 0x218C +#define MC_VM_FB_LOCATION 0x2180 +#define MC_VM_L1_TLB_MCD_RD_A_CNTL 0x219C +#define ENABLE_L1_TLB (1 << 0) +#define ENABLE_L1_FRAGMENT_PROCESSING (1 << 1) +#define ENABLE_L1_STRICT_ORDERING (1 << 2) +#define SYSTEM_ACCESS_MODE_MASK 0x000000C0 +#define SYSTEM_ACCESS_MODE_SHIFT 6 +#define SYSTEM_ACCESS_MODE_PA_ONLY (0 << 6) +#define SYSTEM_ACCESS_MODE_USE_SYS_MAP (1 << 6) +#define SYSTEM_ACCESS_MODE_IN_SYS (2 << 6) +#define SYSTEM_ACCESS_MODE_NOT_IN_SYS (3 << 6) +#define SYSTEM_APERTURE_UNMAPPED_ACCESS_PASS_THRU (0 << 8) +#define SYSTEM_APERTURE_UNMAPPED_ACCESS_DEFAULT_PAGE (1 << 8) +#define ENABLE_SEMAPHORE_MODE (1 << 10) +#define ENABLE_WAIT_L2_QUERY (1 << 11) +#define EFFECTIVE_L1_TLB_SIZE(x) (((x) & 7) << 12) +#define EFFECTIVE_L1_TLB_SIZE_MASK 0x00007000 +#define EFFECTIVE_L1_TLB_SIZE_SHIFT 12 +#define EFFECTIVE_L1_QUEUE_SIZE(x) (((x) & 7) << 15) +#define EFFECTIVE_L1_QUEUE_SIZE_MASK 0x00038000 +#define EFFECTIVE_L1_QUEUE_SIZE_SHIFT 15 +#define MC_VM_L1_TLB_MCD_RD_B_CNTL 0x21A0 +#define MC_VM_L1_TLB_MCB_RD_GFX_CNTL 0x21FC +#define MC_VM_L1_TLB_MCB_RD_HDP_CNTL 0x2204 +#define MC_VM_L1_TLB_MCB_RD_PDMA_CNTL 0x2208 +#define MC_VM_L1_TLB_MCB_RD_SEM_CNTL 0x220C +#define MC_VM_L1_TLB_MCB_RD_SYS_CNTL 0x2200 +#define MC_VM_L1_TLB_MCD_WR_A_CNTL 0x21A4 +#define MC_VM_L1_TLB_MCD_WR_B_CNTL 0x21A8 +#define MC_VM_L1_TLB_MCB_WR_GFX_CNTL 0x2210 +#define MC_VM_L1_TLB_MCB_WR_HDP_CNTL 0x2218 +#define MC_VM_L1_TLB_MCB_WR_PDMA_CNTL 0x221C +#define MC_VM_L1_TLB_MCB_WR_SEM_CNTL 0x2220 +#define MC_VM_L1_TLB_MCB_WR_SYS_CNTL 0x2214 +#define MC_VM_SYSTEM_APERTURE_LOW_ADDR 0x2190 +#define LOGICAL_PAGE_NUMBER_MASK 0x000FFFFF +#define LOGICAL_PAGE_NUMBER_SHIFT 0 +#define MC_VM_SYSTEM_APERTURE_HIGH_ADDR 0x2194 +#define MC_VM_SYSTEM_APERTURE_DEFAULT_ADDR 0x2198 + +#define PA_CL_ENHANCE 0x8A14 +#define CLIP_VTX_REORDER_ENA (1 << 0) +#define NUM_CLIP_SEQ(x) ((x) << 1) +#define PA_SC_AA_CONFIG 0x28C04 +#define PA_SC_AA_SAMPLE_LOCS_2S 0x8B40 +#define PA_SC_AA_SAMPLE_LOCS_4S 0x8B44 +#define PA_SC_AA_SAMPLE_LOCS_8S_WD0 0x8B48 +#define PA_SC_AA_SAMPLE_LOCS_8S_WD1 0x8B4C +#define S0_X(x) ((x) << 0) +#define S0_Y(x) ((x) << 4) +#define S1_X(x) ((x) << 8) +#define S1_Y(x) ((x) << 12) +#define S2_X(x) ((x) << 16) +#define S2_Y(x) ((x) << 20) +#define S3_X(x) ((x) << 24) +#define S3_Y(x) ((x) << 28) +#define S4_X(x) ((x) << 0) +#define S4_Y(x) ((x) << 4) +#define S5_X(x) ((x) << 8) +#define S5_Y(x) ((x) << 12) +#define S6_X(x) ((x) << 16) +#define S6_Y(x) ((x) << 20) +#define S7_X(x) ((x) << 24) +#define S7_Y(x) ((x) << 28) +#define PA_SC_CLIPRECT_RULE 0x2820c +#define PA_SC_ENHANCE 0x8BF0 +#define FORCE_EOV_MAX_CLK_CNT(x) ((x) << 0) +#define FORCE_EOV_MAX_TILE_CNT(x) ((x) << 12) +#define PA_SC_LINE_STIPPLE 0x28A0C +#define PA_SC_LINE_STIPPLE_STATE 0x8B10 +#define PA_SC_MODE_CNTL 0x28A4C +#define PA_SC_MULTI_CHIP_CNTL 0x8B20 + +#define PA_SC_SCREEN_SCISSOR_TL 0x28030 +#define PA_SC_GENERIC_SCISSOR_TL 0x28240 +#define PA_SC_WINDOW_SCISSOR_TL 0x28204 + +#define PCIE_PORT_INDEX 0x0038 +#define PCIE_PORT_DATA 0x003C + +#define CHMAP 0x2004 +#define NOOFCHAN_SHIFT 12 +#define NOOFCHAN_MASK 0x00003000 + +#define RAMCFG 0x2408 +#define NOOFBANK_SHIFT 0 +#define NOOFBANK_MASK 0x00000001 +#define NOOFRANK_SHIFT 1 +#define NOOFRANK_MASK 0x00000002 +#define NOOFROWS_SHIFT 2 +#define NOOFROWS_MASK 0x0000001C +#define NOOFCOLS_SHIFT 5 +#define NOOFCOLS_MASK 0x00000060 +#define CHANSIZE_SHIFT 7 +#define CHANSIZE_MASK 0x00000080 +#define BURSTLENGTH_SHIFT 8 +#define BURSTLENGTH_MASK 0x00000100 +#define CHANSIZE_OVERRIDE (1 << 10) + +#define SCRATCH_REG0 0x8500 +#define SCRATCH_REG1 0x8504 +#define SCRATCH_REG2 0x8508 +#define SCRATCH_REG3 0x850C +#define SCRATCH_REG4 0x8510 +#define SCRATCH_REG5 0x8514 +#define SCRATCH_REG6 0x8518 +#define SCRATCH_REG7 0x851C +#define SCRATCH_UMSK 0x8540 +#define SCRATCH_ADDR 0x8544 + +#define SPI_CONFIG_CNTL 0x9100 +#define GPR_WRITE_PRIORITY(x) ((x) << 0) +#define DISABLE_INTERP_1 (1 << 5) +#define SPI_CONFIG_CNTL_1 0x913C +#define VTX_DONE_DELAY(x) ((x) << 0) +#define INTERP_ONE_PRIM_PER_ROW (1 << 4) +#define SPI_INPUT_Z 0x286D8 +#define SPI_PS_IN_CONTROL_0 0x286CC +#define NUM_INTERP(x) ((x)<<0) +#define POSITION_ENA (1<<8) +#define POSITION_CENTROID (1<<9) +#define POSITION_ADDR(x) ((x)<<10) +#define PARAM_GEN(x) ((x)<<15) +#define PARAM_GEN_ADDR(x) ((x)<<19) +#define BARYC_SAMPLE_CNTL(x) ((x)<<26) +#define PERSP_GRADIENT_ENA (1<<28) +#define LINEAR_GRADIENT_ENA (1<<29) +#define POSITION_SAMPLE (1<<30) +#define BARYC_AT_SAMPLE_ENA (1<<31) +#define SPI_PS_IN_CONTROL_1 0x286D0 +#define GEN_INDEX_PIX (1<<0) +#define GEN_INDEX_PIX_ADDR(x) ((x)<<1) +#define FRONT_FACE_ENA (1<<8) +#define FRONT_FACE_CHAN(x) ((x)<<9) +#define FRONT_FACE_ALL_BITS (1<<11) +#define FRONT_FACE_ADDR(x) ((x)<<12) +#define FOG_ADDR(x) ((x)<<17) +#define FIXED_PT_POSITION_ENA (1<<24) +#define FIXED_PT_POSITION_ADDR(x) ((x)<<25) + +#define SQ_MS_FIFO_SIZES 0x8CF0 +#define CACHE_FIFO_SIZE(x) ((x) << 0) +#define FETCH_FIFO_HIWATER(x) ((x) << 8) +#define DONE_FIFO_HIWATER(x) ((x) << 16) +#define ALU_UPDATE_FIFO_HIWATER(x) ((x) << 24) +#define SQ_PGM_START_ES 0x28880 +#define SQ_PGM_START_FS 0x28894 +#define SQ_PGM_START_GS 0x2886C +#define SQ_PGM_START_PS 0x28840 +#define SQ_PGM_RESOURCES_PS 0x28850 +#define SQ_PGM_EXPORTS_PS 0x28854 +#define SQ_PGM_CF_OFFSET_PS 0x288cc +#define SQ_PGM_START_VS 0x28858 +#define SQ_PGM_RESOURCES_VS 0x28868 +#define SQ_PGM_CF_OFFSET_VS 0x288d0 + +#define SQ_VTX_CONSTANT_WORD0_0 0x30000 +#define SQ_VTX_CONSTANT_WORD1_0 0x30004 +#define SQ_VTX_CONSTANT_WORD2_0 0x30008 +# define SQ_VTXC_BASE_ADDR_HI(x) ((x) << 0) +# define SQ_VTXC_STRIDE(x) ((x) << 8) +# define SQ_VTXC_ENDIAN_SWAP(x) ((x) << 30) +# define SQ_ENDIAN_NONE 0 +# define SQ_ENDIAN_8IN16 1 +# define SQ_ENDIAN_8IN32 2 +#define SQ_VTX_CONSTANT_WORD3_0 0x3000c +#define SQ_VTX_CONSTANT_WORD6_0 0x38018 +#define S__SQ_VTX_CONSTANT_TYPE(x) (((x) & 3) << 30) +#define G__SQ_VTX_CONSTANT_TYPE(x) (((x) >> 30) & 3) +#define SQ_TEX_VTX_INVALID_TEXTURE 0x0 +#define SQ_TEX_VTX_INVALID_BUFFER 0x1 +#define SQ_TEX_VTX_VALID_TEXTURE 0x2 +#define SQ_TEX_VTX_VALID_BUFFER 0x3 + + +#define SX_MISC 0x28350 +#define SX_MEMORY_EXPORT_BASE 0x9010 +#define SX_DEBUG_1 0x9054 +#define SMX_EVENT_RELEASE (1 << 0) +#define ENABLE_NEW_SMX_ADDRESS (1 << 16) + +#define TA_CNTL_AUX 0x9508 +#define DISABLE_CUBE_WRAP (1 << 0) +#define DISABLE_CUBE_ANISO (1 << 1) +#define SYNC_GRADIENT (1 << 24) +#define SYNC_WALKER (1 << 25) +#define SYNC_ALIGNER (1 << 26) +#define BILINEAR_PRECISION_6_BIT (0 << 31) +#define BILINEAR_PRECISION_8_BIT (1 << 31) + +#define TC_CNTL 0x9608 +#define TC_L2_SIZE(x) ((x)<<5) +#define L2_DISABLE_LATE_HIT (1<<9) + +#define VC_ENHANCE 0x9714 + +#define VGT_CACHE_INVALIDATION 0x88C4 +#define CACHE_INVALIDATION(x) ((x)<<0) +#define VC_ONLY 0 +#define TC_ONLY 1 +#define VC_AND_TC 2 +#define VGT_DMA_BASE 0x287E8 +#define VGT_DMA_BASE_HI 0x287E4 +#define VGT_ES_PER_GS 0x88CC +#define VGT_GS_PER_ES 0x88C8 +#define VGT_GS_PER_VS 0x88E8 +#define VGT_GS_VERTEX_REUSE 0x88D4 +#define VGT_PRIMITIVE_TYPE 0x8958 +#define VGT_NUM_INSTANCES 0x8974 +#define VGT_OUT_DEALLOC_CNTL 0x28C5C +#define DEALLOC_DIST_MASK 0x0000007F +#define VGT_STRMOUT_BASE_OFFSET_0 0x28B10 +#define VGT_STRMOUT_BASE_OFFSET_1 0x28B14 +#define VGT_STRMOUT_BASE_OFFSET_2 0x28B18 +#define VGT_STRMOUT_BASE_OFFSET_3 0x28B1c +#define VGT_STRMOUT_BASE_OFFSET_HI_0 0x28B44 +#define VGT_STRMOUT_BASE_OFFSET_HI_1 0x28B48 +#define VGT_STRMOUT_BASE_OFFSET_HI_2 0x28B4c +#define VGT_STRMOUT_BASE_OFFSET_HI_3 0x28B50 +#define VGT_STRMOUT_BUFFER_BASE_0 0x28AD8 +#define VGT_STRMOUT_BUFFER_BASE_1 0x28AE8 +#define VGT_STRMOUT_BUFFER_BASE_2 0x28AF8 +#define VGT_STRMOUT_BUFFER_BASE_3 0x28B08 +#define VGT_STRMOUT_BUFFER_OFFSET_0 0x28ADC +#define VGT_STRMOUT_BUFFER_OFFSET_1 0x28AEC +#define VGT_STRMOUT_BUFFER_OFFSET_2 0x28AFC +#define VGT_STRMOUT_BUFFER_OFFSET_3 0x28B0C +#define VGT_STRMOUT_BUFFER_SIZE_0 0x28AD0 +#define VGT_STRMOUT_BUFFER_SIZE_1 0x28AE0 +#define VGT_STRMOUT_BUFFER_SIZE_2 0x28AF0 +#define VGT_STRMOUT_BUFFER_SIZE_3 0x28B00 + +#define VGT_STRMOUT_EN 0x28AB0 +#define VGT_VERTEX_REUSE_BLOCK_CNTL 0x28C58 +#define VTX_REUSE_DEPTH_MASK 0x000000FF +#define VGT_EVENT_INITIATOR 0x28a90 +# define CACHE_FLUSH_AND_INV_EVENT_TS (0x14 << 0) +# define CACHE_FLUSH_AND_INV_EVENT (0x16 << 0) + +#define VM_CONTEXT0_CNTL 0x1410 +#define ENABLE_CONTEXT (1 << 0) +#define PAGE_TABLE_DEPTH(x) (((x) & 3) << 1) +#define RANGE_PROTECTION_FAULT_ENABLE_DEFAULT (1 << 4) +#define VM_CONTEXT0_INVALIDATION_LOW_ADDR 0x1490 +#define VM_CONTEXT0_INVALIDATION_HIGH_ADDR 0x14B0 +#define VM_CONTEXT0_PAGE_TABLE_BASE_ADDR 0x1574 +#define VM_CONTEXT0_PAGE_TABLE_START_ADDR 0x1594 +#define VM_CONTEXT0_PAGE_TABLE_END_ADDR 0x15B4 +#define VM_CONTEXT0_PROTECTION_FAULT_DEFAULT_ADDR 0x1554 +#define VM_CONTEXT0_REQUEST_RESPONSE 0x1470 +#define REQUEST_TYPE(x) (((x) & 0xf) << 0) +#define RESPONSE_TYPE_MASK 0x000000F0 +#define RESPONSE_TYPE_SHIFT 4 +#define VM_L2_CNTL 0x1400 +#define ENABLE_L2_CACHE (1 << 0) +#define ENABLE_L2_FRAGMENT_PROCESSING (1 << 1) +#define ENABLE_L2_PTE_CACHE_LRU_UPDATE_BY_WRITE (1 << 9) +#define EFFECTIVE_L2_QUEUE_SIZE(x) (((x) & 7) << 13) +#define VM_L2_CNTL2 0x1404 +#define INVALIDATE_ALL_L1_TLBS (1 << 0) +#define INVALIDATE_L2_CACHE (1 << 1) +#define VM_L2_CNTL3 0x1408 +#define BANK_SELECT_0(x) (((x) & 0x1f) << 0) +#define BANK_SELECT_1(x) (((x) & 0x1f) << 5) +#define L2_CACHE_UPDATE_MODE(x) (((x) & 3) << 10) +#define VM_L2_STATUS 0x140C +#define L2_BUSY (1 << 0) + +#define WAIT_UNTIL 0x8040 +#define WAIT_2D_IDLE_bit (1 << 14) +#define WAIT_3D_IDLE_bit (1 << 15) +#define WAIT_2D_IDLECLEAN_bit (1 << 16) +#define WAIT_3D_IDLECLEAN_bit (1 << 17) + +/* async DMA */ +#define DMA_TILING_CONFIG 0x3ec4 +#define DMA_CONFIG 0x3e4c + +#define DMA_RB_CNTL 0xd000 +# define DMA_RB_ENABLE (1 << 0) +# define DMA_RB_SIZE(x) ((x) << 1) /* log2 */ +# define DMA_RB_SWAP_ENABLE (1 << 9) /* 8IN32 */ +# define DMA_RPTR_WRITEBACK_ENABLE (1 << 12) +# define DMA_RPTR_WRITEBACK_SWAP_ENABLE (1 << 13) /* 8IN32 */ +# define DMA_RPTR_WRITEBACK_TIMER(x) ((x) << 16) /* log2 */ +#define DMA_RB_BASE 0xd004 +#define DMA_RB_RPTR 0xd008 +#define DMA_RB_WPTR 0xd00c + +#define DMA_RB_RPTR_ADDR_HI 0xd01c +#define DMA_RB_RPTR_ADDR_LO 0xd020 + +#define DMA_IB_CNTL 0xd024 +# define DMA_IB_ENABLE (1 << 0) +# define DMA_IB_SWAP_ENABLE (1 << 4) +#define DMA_IB_RPTR 0xd028 +#define DMA_CNTL 0xd02c +# define TRAP_ENABLE (1 << 0) +# define SEM_INCOMPLETE_INT_ENABLE (1 << 1) +# define SEM_WAIT_INT_ENABLE (1 << 2) +# define DATA_SWAP_ENABLE (1 << 3) +# define FENCE_SWAP_ENABLE (1 << 4) +# define CTXEMPTY_INT_ENABLE (1 << 28) +#define DMA_STATUS_REG 0xd034 +# define DMA_IDLE (1 << 0) +#define DMA_SEM_INCOMPLETE_TIMER_CNTL 0xd044 +#define DMA_SEM_WAIT_FAIL_TIMER_CNTL 0xd048 +#define DMA_MODE 0xd0bc + +/* async DMA packets */ +#define DMA_PACKET(cmd, t, s, n) ((((cmd) & 0xF) << 28) | \ + (((t) & 0x1) << 23) | \ + (((s) & 0x1) << 22) | \ + (((n) & 0xFFFF) << 0)) +/* async DMA Packet types */ +#define DMA_PACKET_WRITE 0x2 +#define DMA_PACKET_COPY 0x3 +#define DMA_PACKET_INDIRECT_BUFFER 0x4 +#define DMA_PACKET_SEMAPHORE 0x5 +#define DMA_PACKET_FENCE 0x6 +#define DMA_PACKET_TRAP 0x7 +#define DMA_PACKET_CONSTANT_FILL 0xd /* 7xx only */ +#define DMA_PACKET_NOP 0xf + +#define IH_RB_CNTL 0x3e00 +# define IH_RB_ENABLE (1 << 0) +# define IH_RB_SIZE(x) ((x) << 1) /* log2 */ +# define IH_RB_FULL_DRAIN_ENABLE (1 << 6) +# define IH_WPTR_WRITEBACK_ENABLE (1 << 8) +# define IH_WPTR_WRITEBACK_TIMER(x) ((x) << 9) /* log2 */ +# define IH_WPTR_OVERFLOW_ENABLE (1 << 16) +# define IH_WPTR_OVERFLOW_CLEAR (1 << 31) +#define IH_RB_BASE 0x3e04 +#define IH_RB_RPTR 0x3e08 +#define IH_RB_WPTR 0x3e0c +# define RB_OVERFLOW (1 << 0) +# define WPTR_OFFSET_MASK 0x3fffc +#define IH_RB_WPTR_ADDR_HI 0x3e10 +#define IH_RB_WPTR_ADDR_LO 0x3e14 +#define IH_CNTL 0x3e18 +# define ENABLE_INTR (1 << 0) +# define IH_MC_SWAP(x) ((x) << 1) +# define IH_MC_SWAP_NONE 0 +# define IH_MC_SWAP_16BIT 1 +# define IH_MC_SWAP_32BIT 2 +# define IH_MC_SWAP_64BIT 3 +# define RPTR_REARM (1 << 4) +# define MC_WRREQ_CREDIT(x) ((x) << 15) +# define MC_WR_CLEAN_CNT(x) ((x) << 20) + +#define RLC_CNTL 0x3f00 +# define RLC_ENABLE (1 << 0) +#define RLC_HB_BASE 0x3f10 +#define RLC_HB_CNTL 0x3f0c +#define RLC_HB_RPTR 0x3f20 +#define RLC_HB_WPTR 0x3f1c +#define RLC_HB_WPTR_LSB_ADDR 0x3f14 +#define RLC_HB_WPTR_MSB_ADDR 0x3f18 +#define RLC_GPU_CLOCK_COUNT_LSB 0x3f38 +#define RLC_GPU_CLOCK_COUNT_MSB 0x3f3c +#define RLC_CAPTURE_GPU_CLOCK_COUNT 0x3f40 +#define RLC_MC_CNTL 0x3f44 +#define RLC_UCODE_CNTL 0x3f48 +#define RLC_UCODE_ADDR 0x3f2c +#define RLC_UCODE_DATA 0x3f30 + +/* new for TN */ +#define TN_RLC_SAVE_AND_RESTORE_BASE 0x3f10 +#define TN_RLC_CLEAR_STATE_RESTORE_BASE 0x3f20 + +#define SRBM_SOFT_RESET 0xe60 +# define SOFT_RESET_DMA (1 << 12) +# define SOFT_RESET_RLC (1 << 13) +# define RV770_SOFT_RESET_DMA (1 << 20) + +#define CP_INT_CNTL 0xc124 +# define CNTX_BUSY_INT_ENABLE (1 << 19) +# define CNTX_EMPTY_INT_ENABLE (1 << 20) +# define SCRATCH_INT_ENABLE (1 << 25) +# define TIME_STAMP_INT_ENABLE (1 << 26) +# define IB2_INT_ENABLE (1 << 29) +# define IB1_INT_ENABLE (1 << 30) +# define RB_INT_ENABLE (1 << 31) +#define CP_INT_STATUS 0xc128 +# define SCRATCH_INT_STAT (1 << 25) +# define TIME_STAMP_INT_STAT (1 << 26) +# define IB2_INT_STAT (1 << 29) +# define IB1_INT_STAT (1 << 30) +# define RB_INT_STAT (1 << 31) + +#define GRBM_INT_CNTL 0x8060 +# define RDERR_INT_ENABLE (1 << 0) +# define WAIT_COUNT_TIMEOUT_INT_ENABLE (1 << 1) +# define GUI_IDLE_INT_ENABLE (1 << 19) + +#define INTERRUPT_CNTL 0x5468 +# define IH_DUMMY_RD_OVERRIDE (1 << 0) +# define IH_DUMMY_RD_EN (1 << 1) +# define IH_REQ_NONSNOOP_EN (1 << 3) +# define GEN_IH_INT_EN (1 << 8) +#define INTERRUPT_CNTL2 0x546c + +#define D1MODE_VBLANK_STATUS 0x6534 +#define D2MODE_VBLANK_STATUS 0x6d34 +# define DxMODE_VBLANK_OCCURRED (1 << 0) +# define DxMODE_VBLANK_ACK (1 << 4) +# define DxMODE_VBLANK_STAT (1 << 12) +# define DxMODE_VBLANK_INTERRUPT (1 << 16) +# define DxMODE_VBLANK_INTERRUPT_TYPE (1 << 17) +#define D1MODE_VLINE_STATUS 0x653c +#define D2MODE_VLINE_STATUS 0x6d3c +# define DxMODE_VLINE_OCCURRED (1 << 0) +# define DxMODE_VLINE_ACK (1 << 4) +# define DxMODE_VLINE_STAT (1 << 12) +# define DxMODE_VLINE_INTERRUPT (1 << 16) +# define DxMODE_VLINE_INTERRUPT_TYPE (1 << 17) +#define DxMODE_INT_MASK 0x6540 +# define D1MODE_VBLANK_INT_MASK (1 << 0) +# define D1MODE_VLINE_INT_MASK (1 << 4) +# define D2MODE_VBLANK_INT_MASK (1 << 8) +# define D2MODE_VLINE_INT_MASK (1 << 12) +#define DCE3_DISP_INTERRUPT_STATUS 0x7ddc +# define DC_HPD1_INTERRUPT (1 << 18) +# define DC_HPD2_INTERRUPT (1 << 19) +#define DISP_INTERRUPT_STATUS 0x7edc +# define LB_D1_VLINE_INTERRUPT (1 << 2) +# define LB_D2_VLINE_INTERRUPT (1 << 3) +# define LB_D1_VBLANK_INTERRUPT (1 << 4) +# define LB_D2_VBLANK_INTERRUPT (1 << 5) +# define DACA_AUTODETECT_INTERRUPT (1 << 16) +# define DACB_AUTODETECT_INTERRUPT (1 << 17) +# define DC_HOT_PLUG_DETECT1_INTERRUPT (1 << 18) +# define DC_HOT_PLUG_DETECT2_INTERRUPT (1 << 19) +# define DC_I2C_SW_DONE_INTERRUPT (1 << 20) +# define DC_I2C_HW_DONE_INTERRUPT (1 << 21) +#define DISP_INTERRUPT_STATUS_CONTINUE 0x7ee8 +#define DCE3_DISP_INTERRUPT_STATUS_CONTINUE 0x7de8 +# define DC_HPD4_INTERRUPT (1 << 14) +# define DC_HPD4_RX_INTERRUPT (1 << 15) +# define DC_HPD3_INTERRUPT (1 << 28) +# define DC_HPD1_RX_INTERRUPT (1 << 29) +# define DC_HPD2_RX_INTERRUPT (1 << 30) +#define DCE3_DISP_INTERRUPT_STATUS_CONTINUE2 0x7dec +# define DC_HPD3_RX_INTERRUPT (1 << 0) +# define DIGA_DP_VID_STREAM_DISABLE_INTERRUPT (1 << 1) +# define DIGA_DP_STEER_FIFO_OVERFLOW_INTERRUPT (1 << 2) +# define DIGB_DP_VID_STREAM_DISABLE_INTERRUPT (1 << 3) +# define DIGB_DP_STEER_FIFO_OVERFLOW_INTERRUPT (1 << 4) +# define AUX1_SW_DONE_INTERRUPT (1 << 5) +# define AUX1_LS_DONE_INTERRUPT (1 << 6) +# define AUX2_SW_DONE_INTERRUPT (1 << 7) +# define AUX2_LS_DONE_INTERRUPT (1 << 8) +# define AUX3_SW_DONE_INTERRUPT (1 << 9) +# define AUX3_LS_DONE_INTERRUPT (1 << 10) +# define AUX4_SW_DONE_INTERRUPT (1 << 11) +# define AUX4_LS_DONE_INTERRUPT (1 << 12) +# define DIGA_DP_FAST_TRAINING_COMPLETE_INTERRUPT (1 << 13) +# define DIGB_DP_FAST_TRAINING_COMPLETE_INTERRUPT (1 << 14) +/* DCE 3.2 */ +# define AUX5_SW_DONE_INTERRUPT (1 << 15) +# define AUX5_LS_DONE_INTERRUPT (1 << 16) +# define AUX6_SW_DONE_INTERRUPT (1 << 17) +# define AUX6_LS_DONE_INTERRUPT (1 << 18) +# define DC_HPD5_INTERRUPT (1 << 19) +# define DC_HPD5_RX_INTERRUPT (1 << 20) +# define DC_HPD6_INTERRUPT (1 << 21) +# define DC_HPD6_RX_INTERRUPT (1 << 22) + +#define DACA_AUTO_DETECT_CONTROL 0x7828 +#define DACB_AUTO_DETECT_CONTROL 0x7a28 +#define DCE3_DACA_AUTO_DETECT_CONTROL 0x7028 +#define DCE3_DACB_AUTO_DETECT_CONTROL 0x7128 +# define DACx_AUTODETECT_MODE(x) ((x) << 0) +# define DACx_AUTODETECT_MODE_NONE 0 +# define DACx_AUTODETECT_MODE_CONNECT 1 +# define DACx_AUTODETECT_MODE_DISCONNECT 2 +# define DACx_AUTODETECT_FRAME_TIME_COUNTER(x) ((x) << 8) +/* bit 18 = R/C, 17 = G/Y, 16 = B/Comp */ +# define DACx_AUTODETECT_CHECK_MASK(x) ((x) << 16) + +#define DCE3_DACA_AUTODETECT_INT_CONTROL 0x7038 +#define DCE3_DACB_AUTODETECT_INT_CONTROL 0x7138 +#define DACA_AUTODETECT_INT_CONTROL 0x7838 +#define DACB_AUTODETECT_INT_CONTROL 0x7a38 +# define DACx_AUTODETECT_ACK (1 << 0) +# define DACx_AUTODETECT_INT_ENABLE (1 << 16) + +#define DC_HOT_PLUG_DETECT1_CONTROL 0x7d00 +#define DC_HOT_PLUG_DETECT2_CONTROL 0x7d10 +#define DC_HOT_PLUG_DETECT3_CONTROL 0x7d24 +# define DC_HOT_PLUG_DETECTx_EN (1 << 0) + +#define DC_HOT_PLUG_DETECT1_INT_STATUS 0x7d04 +#define DC_HOT_PLUG_DETECT2_INT_STATUS 0x7d14 +#define DC_HOT_PLUG_DETECT3_INT_STATUS 0x7d28 +# define DC_HOT_PLUG_DETECTx_INT_STATUS (1 << 0) +# define DC_HOT_PLUG_DETECTx_SENSE (1 << 1) + +/* DCE 3.0 */ +#define DC_HPD1_INT_STATUS 0x7d00 +#define DC_HPD2_INT_STATUS 0x7d0c +#define DC_HPD3_INT_STATUS 0x7d18 +#define DC_HPD4_INT_STATUS 0x7d24 +/* DCE 3.2 */ +#define DC_HPD5_INT_STATUS 0x7dc0 +#define DC_HPD6_INT_STATUS 0x7df4 +# define DC_HPDx_INT_STATUS (1 << 0) +# define DC_HPDx_SENSE (1 << 1) +# define DC_HPDx_RX_INT_STATUS (1 << 8) + +#define DC_HOT_PLUG_DETECT1_INT_CONTROL 0x7d08 +#define DC_HOT_PLUG_DETECT2_INT_CONTROL 0x7d18 +#define DC_HOT_PLUG_DETECT3_INT_CONTROL 0x7d2c +# define DC_HOT_PLUG_DETECTx_INT_ACK (1 << 0) +# define DC_HOT_PLUG_DETECTx_INT_POLARITY (1 << 8) +# define DC_HOT_PLUG_DETECTx_INT_EN (1 << 16) +/* DCE 3.0 */ +#define DC_HPD1_INT_CONTROL 0x7d04 +#define DC_HPD2_INT_CONTROL 0x7d10 +#define DC_HPD3_INT_CONTROL 0x7d1c +#define DC_HPD4_INT_CONTROL 0x7d28 +/* DCE 3.2 */ +#define DC_HPD5_INT_CONTROL 0x7dc4 +#define DC_HPD6_INT_CONTROL 0x7df8 +# define DC_HPDx_INT_ACK (1 << 0) +# define DC_HPDx_INT_POLARITY (1 << 8) +# define DC_HPDx_INT_EN (1 << 16) +# define DC_HPDx_RX_INT_ACK (1 << 20) +# define DC_HPDx_RX_INT_EN (1 << 24) + +/* DCE 3.0 */ +#define DC_HPD1_CONTROL 0x7d08 +#define DC_HPD2_CONTROL 0x7d14 +#define DC_HPD3_CONTROL 0x7d20 +#define DC_HPD4_CONTROL 0x7d2c +/* DCE 3.2 */ +#define DC_HPD5_CONTROL 0x7dc8 +#define DC_HPD6_CONTROL 0x7dfc +# define DC_HPDx_CONNECTION_TIMER(x) ((x) << 0) +# define DC_HPDx_RX_INT_TIMER(x) ((x) << 16) +/* DCE 3.2 */ +# define DC_HPDx_EN (1 << 28) + +#define D1GRPH_INTERRUPT_STATUS 0x6158 +#define D2GRPH_INTERRUPT_STATUS 0x6958 +# define DxGRPH_PFLIP_INT_OCCURRED (1 << 0) +# define DxGRPH_PFLIP_INT_CLEAR (1 << 8) +#define D1GRPH_INTERRUPT_CONTROL 0x615c +#define D2GRPH_INTERRUPT_CONTROL 0x695c +# define DxGRPH_PFLIP_INT_MASK (1 << 0) +# define DxGRPH_PFLIP_INT_TYPE (1 << 8) + +/* PCIE link stuff */ +#define PCIE_LC_TRAINING_CNTL 0xa1 /* PCIE_P */ +# define LC_POINT_7_PLUS_EN (1 << 6) +#define PCIE_LC_LINK_WIDTH_CNTL 0xa2 /* PCIE_P */ +# define LC_LINK_WIDTH_SHIFT 0 +# define LC_LINK_WIDTH_MASK 0x7 +# define LC_LINK_WIDTH_X0 0 +# define LC_LINK_WIDTH_X1 1 +# define LC_LINK_WIDTH_X2 2 +# define LC_LINK_WIDTH_X4 3 +# define LC_LINK_WIDTH_X8 4 +# define LC_LINK_WIDTH_X16 6 +# define LC_LINK_WIDTH_RD_SHIFT 4 +# define LC_LINK_WIDTH_RD_MASK 0x70 +# define LC_RECONFIG_ARC_MISSING_ESCAPE (1 << 7) +# define LC_RECONFIG_NOW (1 << 8) +# define LC_RENEGOTIATION_SUPPORT (1 << 9) +# define LC_RENEGOTIATE_EN (1 << 10) +# define LC_SHORT_RECONFIG_EN (1 << 11) +# define LC_UPCONFIGURE_SUPPORT (1 << 12) +# define LC_UPCONFIGURE_DIS (1 << 13) +#define PCIE_LC_SPEED_CNTL 0xa4 /* PCIE_P */ +# define LC_GEN2_EN_STRAP (1 << 0) +# define LC_TARGET_LINK_SPEED_OVERRIDE_EN (1 << 1) +# define LC_FORCE_EN_HW_SPEED_CHANGE (1 << 5) +# define LC_FORCE_DIS_HW_SPEED_CHANGE (1 << 6) +# define LC_SPEED_CHANGE_ATTEMPTS_ALLOWED_MASK (0x3 << 8) +# define LC_SPEED_CHANGE_ATTEMPTS_ALLOWED_SHIFT 3 +# define LC_CURRENT_DATA_RATE (1 << 11) +# define LC_VOLTAGE_TIMER_SEL_MASK (0xf << 14) +# define LC_CLR_FAILED_SPD_CHANGE_CNT (1 << 21) +# define LC_OTHER_SIDE_EVER_SENT_GEN2 (1 << 23) +# define LC_OTHER_SIDE_SUPPORTS_GEN2 (1 << 24) +#define MM_CFGREGS_CNTL 0x544c +# define MM_WR_TO_CFG_EN (1 << 3) +#define LINK_CNTL2 0x88 /* F0 */ +# define TARGET_LINK_SPEED_MASK (0xf << 0) +# define SELECTABLE_DEEMPHASIS (1 << 6) + +/* Audio clocks */ +#define DCCG_AUDIO_DTO0_PHASE 0x0514 +#define DCCG_AUDIO_DTO0_MODULE 0x0518 +#define DCCG_AUDIO_DTO0_LOAD 0x051c +# define DTO_LOAD (1 << 31) +#define DCCG_AUDIO_DTO0_CNTL 0x0520 + +#define DCCG_AUDIO_DTO1_PHASE 0x0524 +#define DCCG_AUDIO_DTO1_MODULE 0x0528 +#define DCCG_AUDIO_DTO1_LOAD 0x052c +#define DCCG_AUDIO_DTO1_CNTL 0x0530 + +#define DCCG_AUDIO_DTO_SELECT 0x0534 + +/* digital blocks */ +#define TMDSA_CNTL 0x7880 +# define TMDSA_HDMI_EN (1 << 2) +#define LVTMA_CNTL 0x7a80 +# define LVTMA_HDMI_EN (1 << 2) +#define DDIA_CNTL 0x7200 +# define DDIA_HDMI_EN (1 << 2) +#define DIG0_CNTL 0x75a0 +# define DIG_MODE(x) (((x) & 7) << 8) +# define DIG_MODE_DP 0 +# define DIG_MODE_LVDS 1 +# define DIG_MODE_TMDS_DVI 2 +# define DIG_MODE_TMDS_HDMI 3 +# define DIG_MODE_SDVO 4 +#define DIG1_CNTL 0x79a0 + +/* rs6xx/rs740 and r6xx share the same HDMI blocks, however, rs6xx has only one + * instance of the blocks while r6xx has 2. DCE 3.0 cards are slightly + * different due to the new DIG blocks, but also have 2 instances. + * DCE 3.0 HDMI blocks are part of each DIG encoder. + */ + +/* rs6xx/rs740/r6xx/dce3 */ +#define HDMI0_CONTROL 0x7400 +/* rs6xx/rs740/r6xx */ +# define HDMI0_ENABLE (1 << 0) +# define HDMI0_STREAM(x) (((x) & 3) << 2) +# define HDMI0_STREAM_TMDSA 0 +# define HDMI0_STREAM_LVTMA 1 +# define HDMI0_STREAM_DVOA 2 +# define HDMI0_STREAM_DDIA 3 +/* rs6xx/r6xx/dce3 */ +# define HDMI0_ERROR_ACK (1 << 8) +# define HDMI0_ERROR_MASK (1 << 9) +#define HDMI0_STATUS 0x7404 +# define HDMI0_ACTIVE_AVMUTE (1 << 0) +# define HDMI0_AUDIO_ENABLE (1 << 4) +# define HDMI0_AZ_FORMAT_WTRIG (1 << 28) +# define HDMI0_AZ_FORMAT_WTRIG_INT (1 << 29) +#define HDMI0_AUDIO_PACKET_CONTROL 0x7408 +# define HDMI0_AUDIO_SAMPLE_SEND (1 << 0) +# define HDMI0_AUDIO_DELAY_EN(x) (((x) & 3) << 4) +# define HDMI0_AUDIO_SEND_MAX_PACKETS (1 << 8) +# define HDMI0_AUDIO_TEST_EN (1 << 12) +# define HDMI0_AUDIO_PACKETS_PER_LINE(x) (((x) & 0x1f) << 16) +# define HDMI0_AUDIO_CHANNEL_SWAP (1 << 24) +# define HDMI0_60958_CS_UPDATE (1 << 26) +# define HDMI0_AZ_FORMAT_WTRIG_MASK (1 << 28) +# define HDMI0_AZ_FORMAT_WTRIG_ACK (1 << 29) +#define HDMI0_AUDIO_CRC_CONTROL 0x740c +# define HDMI0_AUDIO_CRC_EN (1 << 0) +#define HDMI0_VBI_PACKET_CONTROL 0x7410 +# define HDMI0_NULL_SEND (1 << 0) +# define HDMI0_GC_SEND (1 << 4) +# define HDMI0_GC_CONT (1 << 5) /* 0 - once; 1 - every frame */ +#define HDMI0_INFOFRAME_CONTROL0 0x7414 +# define HDMI0_AVI_INFO_SEND (1 << 0) +# define HDMI0_AVI_INFO_CONT (1 << 1) +# define HDMI0_AUDIO_INFO_SEND (1 << 4) +# define HDMI0_AUDIO_INFO_CONT (1 << 5) +# define HDMI0_AUDIO_INFO_SOURCE (1 << 6) /* 0 - sound block; 1 - hmdi regs */ +# define HDMI0_AUDIO_INFO_UPDATE (1 << 7) +# define HDMI0_MPEG_INFO_SEND (1 << 8) +# define HDMI0_MPEG_INFO_CONT (1 << 9) +# define HDMI0_MPEG_INFO_UPDATE (1 << 10) +#define HDMI0_INFOFRAME_CONTROL1 0x7418 +# define HDMI0_AVI_INFO_LINE(x) (((x) & 0x3f) << 0) +# define HDMI0_AUDIO_INFO_LINE(x) (((x) & 0x3f) << 8) +# define HDMI0_MPEG_INFO_LINE(x) (((x) & 0x3f) << 16) +#define HDMI0_GENERIC_PACKET_CONTROL 0x741c +# define HDMI0_GENERIC0_SEND (1 << 0) +# define HDMI0_GENERIC0_CONT (1 << 1) +# define HDMI0_GENERIC0_UPDATE (1 << 2) +# define HDMI0_GENERIC1_SEND (1 << 4) +# define HDMI0_GENERIC1_CONT (1 << 5) +# define HDMI0_GENERIC0_LINE(x) (((x) & 0x3f) << 16) +# define HDMI0_GENERIC1_LINE(x) (((x) & 0x3f) << 24) +#define HDMI0_GC 0x7428 +# define HDMI0_GC_AVMUTE (1 << 0) +#define HDMI0_AVI_INFO0 0x7454 +# define HDMI0_AVI_INFO_CHECKSUM(x) (((x) & 0xff) << 0) +# define HDMI0_AVI_INFO_S(x) (((x) & 3) << 8) +# define HDMI0_AVI_INFO_B(x) (((x) & 3) << 10) +# define HDMI0_AVI_INFO_A(x) (((x) & 1) << 12) +# define HDMI0_AVI_INFO_Y(x) (((x) & 3) << 13) +# define HDMI0_AVI_INFO_Y_RGB 0 +# define HDMI0_AVI_INFO_Y_YCBCR422 1 +# define HDMI0_AVI_INFO_Y_YCBCR444 2 +# define HDMI0_AVI_INFO_Y_A_B_S(x) (((x) & 0xff) << 8) +# define HDMI0_AVI_INFO_R(x) (((x) & 0xf) << 16) +# define HDMI0_AVI_INFO_M(x) (((x) & 0x3) << 20) +# define HDMI0_AVI_INFO_C(x) (((x) & 0x3) << 22) +# define HDMI0_AVI_INFO_C_M_R(x) (((x) & 0xff) << 16) +# define HDMI0_AVI_INFO_SC(x) (((x) & 0x3) << 24) +# define HDMI0_AVI_INFO_ITC_EC_Q_SC(x) (((x) & 0xff) << 24) +#define HDMI0_AVI_INFO1 0x7458 +# define HDMI0_AVI_INFO_VIC(x) (((x) & 0x7f) << 0) /* don't use avi infoframe v1 */ +# define HDMI0_AVI_INFO_PR(x) (((x) & 0xf) << 8) /* don't use avi infoframe v1 */ +# define HDMI0_AVI_INFO_TOP(x) (((x) & 0xffff) << 16) +#define HDMI0_AVI_INFO2 0x745c +# define HDMI0_AVI_INFO_BOTTOM(x) (((x) & 0xffff) << 0) +# define HDMI0_AVI_INFO_LEFT(x) (((x) & 0xffff) << 16) +#define HDMI0_AVI_INFO3 0x7460 +# define HDMI0_AVI_INFO_RIGHT(x) (((x) & 0xffff) << 0) +# define HDMI0_AVI_INFO_VERSION(x) (((x) & 3) << 24) +#define HDMI0_MPEG_INFO0 0x7464 +# define HDMI0_MPEG_INFO_CHECKSUM(x) (((x) & 0xff) << 0) +# define HDMI0_MPEG_INFO_MB0(x) (((x) & 0xff) << 8) +# define HDMI0_MPEG_INFO_MB1(x) (((x) & 0xff) << 16) +# define HDMI0_MPEG_INFO_MB2(x) (((x) & 0xff) << 24) +#define HDMI0_MPEG_INFO1 0x7468 +# define HDMI0_MPEG_INFO_MB3(x) (((x) & 0xff) << 0) +# define HDMI0_MPEG_INFO_MF(x) (((x) & 3) << 8) +# define HDMI0_MPEG_INFO_FR(x) (((x) & 1) << 12) +#define HDMI0_GENERIC0_HDR 0x746c +#define HDMI0_GENERIC0_0 0x7470 +#define HDMI0_GENERIC0_1 0x7474 +#define HDMI0_GENERIC0_2 0x7478 +#define HDMI0_GENERIC0_3 0x747c +#define HDMI0_GENERIC0_4 0x7480 +#define HDMI0_GENERIC0_5 0x7484 +#define HDMI0_GENERIC0_6 0x7488 +#define HDMI0_GENERIC1_HDR 0x748c +#define HDMI0_GENERIC1_0 0x7490 +#define HDMI0_GENERIC1_1 0x7494 +#define HDMI0_GENERIC1_2 0x7498 +#define HDMI0_GENERIC1_3 0x749c +#define HDMI0_GENERIC1_4 0x74a0 +#define HDMI0_GENERIC1_5 0x74a4 +#define HDMI0_GENERIC1_6 0x74a8 +#define HDMI0_ACR_32_0 0x74ac +# define HDMI0_ACR_CTS_32(x) (((x) & 0xfffff) << 12) +#define HDMI0_ACR_32_1 0x74b0 +# define HDMI0_ACR_N_32(x) (((x) & 0xfffff) << 0) +#define HDMI0_ACR_44_0 0x74b4 +# define HDMI0_ACR_CTS_44(x) (((x) & 0xfffff) << 12) +#define HDMI0_ACR_44_1 0x74b8 +# define HDMI0_ACR_N_44(x) (((x) & 0xfffff) << 0) +#define HDMI0_ACR_48_0 0x74bc +# define HDMI0_ACR_CTS_48(x) (((x) & 0xfffff) << 12) +#define HDMI0_ACR_48_1 0x74c0 +# define HDMI0_ACR_N_48(x) (((x) & 0xfffff) << 0) +#define HDMI0_ACR_STATUS_0 0x74c4 +#define HDMI0_ACR_STATUS_1 0x74c8 +#define HDMI0_AUDIO_INFO0 0x74cc +# define HDMI0_AUDIO_INFO_CHECKSUM(x) (((x) & 0xff) << 0) +# define HDMI0_AUDIO_INFO_CC(x) (((x) & 7) << 8) +#define HDMI0_AUDIO_INFO1 0x74d0 +# define HDMI0_AUDIO_INFO_CA(x) (((x) & 0xff) << 0) +# define HDMI0_AUDIO_INFO_LSV(x) (((x) & 0xf) << 11) +# define HDMI0_AUDIO_INFO_DM_INH(x) (((x) & 1) << 15) +# define HDMI0_AUDIO_INFO_DM_INH_LSV(x) (((x) & 0xff) << 8) +#define HDMI0_60958_0 0x74d4 +# define HDMI0_60958_CS_A(x) (((x) & 1) << 0) +# define HDMI0_60958_CS_B(x) (((x) & 1) << 1) +# define HDMI0_60958_CS_C(x) (((x) & 1) << 2) +# define HDMI0_60958_CS_D(x) (((x) & 3) << 3) +# define HDMI0_60958_CS_MODE(x) (((x) & 3) << 6) +# define HDMI0_60958_CS_CATEGORY_CODE(x) (((x) & 0xff) << 8) +# define HDMI0_60958_CS_SOURCE_NUMBER(x) (((x) & 0xf) << 16) +# define HDMI0_60958_CS_CHANNEL_NUMBER_L(x) (((x) & 0xf) << 20) +# define HDMI0_60958_CS_SAMPLING_FREQUENCY(x) (((x) & 0xf) << 24) +# define HDMI0_60958_CS_CLOCK_ACCURACY(x) (((x) & 3) << 28) +#define HDMI0_60958_1 0x74d8 +# define HDMI0_60958_CS_WORD_LENGTH(x) (((x) & 0xf) << 0) +# define HDMI0_60958_CS_ORIGINAL_SAMPLING_FREQUENCY(x) (((x) & 0xf) << 4) +# define HDMI0_60958_CS_VALID_L(x) (((x) & 1) << 16) +# define HDMI0_60958_CS_VALID_R(x) (((x) & 1) << 18) +# define HDMI0_60958_CS_CHANNEL_NUMBER_R(x) (((x) & 0xf) << 20) +#define HDMI0_ACR_PACKET_CONTROL 0x74dc +# define HDMI0_ACR_SEND (1 << 0) +# define HDMI0_ACR_CONT (1 << 1) +# define HDMI0_ACR_SELECT(x) (((x) & 3) << 4) +# define HDMI0_ACR_HW 0 +# define HDMI0_ACR_32 1 +# define HDMI0_ACR_44 2 +# define HDMI0_ACR_48 3 +# define HDMI0_ACR_SOURCE (1 << 8) /* 0 - hw; 1 - cts value */ +# define HDMI0_ACR_AUTO_SEND (1 << 12) +#define HDMI0_RAMP_CONTROL0 0x74e0 +# define HDMI0_RAMP_MAX_COUNT(x) (((x) & 0xffffff) << 0) +#define HDMI0_RAMP_CONTROL1 0x74e4 +# define HDMI0_RAMP_MIN_COUNT(x) (((x) & 0xffffff) << 0) +#define HDMI0_RAMP_CONTROL2 0x74e8 +# define HDMI0_RAMP_INC_COUNT(x) (((x) & 0xffffff) << 0) +#define HDMI0_RAMP_CONTROL3 0x74ec +# define HDMI0_RAMP_DEC_COUNT(x) (((x) & 0xffffff) << 0) +/* HDMI0_60958_2 is r7xx only */ +#define HDMI0_60958_2 0x74f0 +# define HDMI0_60958_CS_CHANNEL_NUMBER_2(x) (((x) & 0xf) << 0) +# define HDMI0_60958_CS_CHANNEL_NUMBER_3(x) (((x) & 0xf) << 4) +# define HDMI0_60958_CS_CHANNEL_NUMBER_4(x) (((x) & 0xf) << 8) +# define HDMI0_60958_CS_CHANNEL_NUMBER_5(x) (((x) & 0xf) << 12) +# define HDMI0_60958_CS_CHANNEL_NUMBER_6(x) (((x) & 0xf) << 16) +# define HDMI0_60958_CS_CHANNEL_NUMBER_7(x) (((x) & 0xf) << 20) +/* r6xx only; second instance starts at 0x7700 */ +#define HDMI1_CONTROL 0x7700 +#define HDMI1_STATUS 0x7704 +#define HDMI1_AUDIO_PACKET_CONTROL 0x7708 +/* DCE3; second instance starts at 0x7800 NOT 0x7700 */ +#define DCE3_HDMI1_CONTROL 0x7800 +#define DCE3_HDMI1_STATUS 0x7804 +#define DCE3_HDMI1_AUDIO_PACKET_CONTROL 0x7808 +/* DCE3.2 (for interrupts) */ +#define AFMT_STATUS 0x7600 +# define AFMT_AUDIO_ENABLE (1 << 4) +# define AFMT_AZ_FORMAT_WTRIG (1 << 28) +# define AFMT_AZ_FORMAT_WTRIG_INT (1 << 29) +# define AFMT_AZ_AUDIO_ENABLE_CHG (1 << 30) +#define AFMT_AUDIO_PACKET_CONTROL 0x7604 +# define AFMT_AUDIO_SAMPLE_SEND (1 << 0) +# define AFMT_AUDIO_TEST_EN (1 << 12) +# define AFMT_AUDIO_CHANNEL_SWAP (1 << 24) +# define AFMT_60958_CS_UPDATE (1 << 26) +# define AFMT_AZ_AUDIO_ENABLE_CHG_MASK (1 << 27) +# define AFMT_AZ_FORMAT_WTRIG_MASK (1 << 28) +# define AFMT_AZ_FORMAT_WTRIG_ACK (1 << 29) +# define AFMT_AZ_AUDIO_ENABLE_CHG_ACK (1 << 30) + +/* + * PM4 + */ +#define PACKET_TYPE0 0 +#define PACKET_TYPE1 1 +#define PACKET_TYPE2 2 +#define PACKET_TYPE3 3 + +#define CP_PACKET_GET_TYPE(h) (((h) >> 30) & 3) +#define CP_PACKET_GET_COUNT(h) (((h) >> 16) & 0x3FFF) +#define CP_PACKET0_GET_REG(h) (((h) & 0xFFFF) << 2) +#define CP_PACKET3_GET_OPCODE(h) (((h) >> 8) & 0xFF) +#define PACKET0(reg, n) ((PACKET_TYPE0 << 30) | \ + (((reg) >> 2) & 0xFFFF) | \ + ((n) & 0x3FFF) << 16) +#define PACKET3(op, n) ((PACKET_TYPE3 << 30) | \ + (((op) & 0xFF) << 8) | \ + ((n) & 0x3FFF) << 16) + +/* Packet 3 types */ +#define PACKET3_NOP 0x10 +#define PACKET3_INDIRECT_BUFFER_END 0x17 +#define PACKET3_SET_PREDICATION 0x20 +#define PACKET3_REG_RMW 0x21 +#define PACKET3_COND_EXEC 0x22 +#define PACKET3_PRED_EXEC 0x23 +#define PACKET3_START_3D_CMDBUF 0x24 +#define PACKET3_DRAW_INDEX_2 0x27 +#define PACKET3_CONTEXT_CONTROL 0x28 +#define PACKET3_DRAW_INDEX_IMMD_BE 0x29 +#define PACKET3_INDEX_TYPE 0x2A +#define PACKET3_DRAW_INDEX 0x2B +#define PACKET3_DRAW_INDEX_AUTO 0x2D +#define PACKET3_DRAW_INDEX_IMMD 0x2E +#define PACKET3_NUM_INSTANCES 0x2F +#define PACKET3_STRMOUT_BUFFER_UPDATE 0x34 +#define PACKET3_INDIRECT_BUFFER_MP 0x38 +#define PACKET3_MEM_SEMAPHORE 0x39 +# define PACKET3_SEM_WAIT_ON_SIGNAL (0x1 << 12) +# define PACKET3_SEM_SEL_SIGNAL (0x6 << 29) +# define PACKET3_SEM_SEL_WAIT (0x7 << 29) +#define PACKET3_MPEG_INDEX 0x3A +#define PACKET3_COPY_DW 0x3B +#define PACKET3_WAIT_REG_MEM 0x3C +#define PACKET3_MEM_WRITE 0x3D +#define PACKET3_INDIRECT_BUFFER 0x32 +#define PACKET3_CP_DMA 0x41 +/* 1. header + * 2. SRC_ADDR_LO [31:0] + * 3. CP_SYNC [31] | SRC_ADDR_HI [7:0] + * 4. DST_ADDR_LO [31:0] + * 5. DST_ADDR_HI [7:0] + * 6. COMMAND [29:22] | BYTE_COUNT [20:0] + */ +# define PACKET3_CP_DMA_CP_SYNC (1 << 31) +/* COMMAND */ +# define PACKET3_CP_DMA_CMD_SRC_SWAP(x) ((x) << 23) + /* 0 - none + * 1 - 8 in 16 + * 2 - 8 in 32 + * 3 - 8 in 64 + */ +# define PACKET3_CP_DMA_CMD_DST_SWAP(x) ((x) << 24) + /* 0 - none + * 1 - 8 in 16 + * 2 - 8 in 32 + * 3 - 8 in 64 + */ +# define PACKET3_CP_DMA_CMD_SAS (1 << 26) + /* 0 - memory + * 1 - register + */ +# define PACKET3_CP_DMA_CMD_DAS (1 << 27) + /* 0 - memory + * 1 - register + */ +# define PACKET3_CP_DMA_CMD_SAIC (1 << 28) +# define PACKET3_CP_DMA_CMD_DAIC (1 << 29) +#define PACKET3_SURFACE_SYNC 0x43 +# define PACKET3_CB0_DEST_BASE_ENA (1 << 6) +# define PACKET3_TC_ACTION_ENA (1 << 23) +# define PACKET3_VC_ACTION_ENA (1 << 24) +# define PACKET3_CB_ACTION_ENA (1 << 25) +# define PACKET3_DB_ACTION_ENA (1 << 26) +# define PACKET3_SH_ACTION_ENA (1 << 27) +# define PACKET3_SMX_ACTION_ENA (1 << 28) +#define PACKET3_ME_INITIALIZE 0x44 +#define PACKET3_ME_INITIALIZE_DEVICE_ID(x) ((x) << 16) +#define PACKET3_COND_WRITE 0x45 +#define PACKET3_EVENT_WRITE 0x46 +#define EVENT_TYPE(x) ((x) << 0) +#define EVENT_INDEX(x) ((x) << 8) + /* 0 - any non-TS event + * 1 - ZPASS_DONE + * 2 - SAMPLE_PIPELINESTAT + * 3 - SAMPLE_STREAMOUTSTAT* + * 4 - *S_PARTIAL_FLUSH + * 5 - TS events + */ +#define PACKET3_EVENT_WRITE_EOP 0x47 +#define DATA_SEL(x) ((x) << 29) + /* 0 - discard + * 1 - send low 32bit data + * 2 - send 64bit data + * 3 - send 64bit counter value + */ +#define INT_SEL(x) ((x) << 24) + /* 0 - none + * 1 - interrupt only (DATA_SEL = 0) + * 2 - interrupt when data write is confirmed + */ +#define PACKET3_ONE_REG_WRITE 0x57 +#define PACKET3_SET_CONFIG_REG 0x68 +#define PACKET3_SET_CONFIG_REG_OFFSET 0x00008000 +#define PACKET3_SET_CONFIG_REG_END 0x0000ac00 +#define PACKET3_SET_CONTEXT_REG 0x69 +#define PACKET3_SET_CONTEXT_REG_OFFSET 0x00028000 +#define PACKET3_SET_CONTEXT_REG_END 0x00029000 +#define PACKET3_SET_ALU_CONST 0x6A +#define PACKET3_SET_ALU_CONST_OFFSET 0x00030000 +#define PACKET3_SET_ALU_CONST_END 0x00032000 +#define PACKET3_SET_BOOL_CONST 0x6B +#define PACKET3_SET_BOOL_CONST_OFFSET 0x0003e380 +#define PACKET3_SET_BOOL_CONST_END 0x00040000 +#define PACKET3_SET_LOOP_CONST 0x6C +#define PACKET3_SET_LOOP_CONST_OFFSET 0x0003e200 +#define PACKET3_SET_LOOP_CONST_END 0x0003e380 +#define PACKET3_SET_RESOURCE 0x6D +#define PACKET3_SET_RESOURCE_OFFSET 0x00038000 +#define PACKET3_SET_RESOURCE_END 0x0003c000 +#define PACKET3_SET_SAMPLER 0x6E +#define PACKET3_SET_SAMPLER_OFFSET 0x0003c000 +#define PACKET3_SET_SAMPLER_END 0x0003cff0 +#define PACKET3_SET_CTL_CONST 0x6F +#define PACKET3_SET_CTL_CONST_OFFSET 0x0003cff0 +#define PACKET3_SET_CTL_CONST_END 0x0003e200 +#define PACKET3_STRMOUT_BASE_UPDATE 0x72 /* r7xx */ +#define PACKET3_SURFACE_BASE_UPDATE 0x73 + + +#define R_008020_GRBM_SOFT_RESET 0x8020 +#define S_008020_SOFT_RESET_CP(x) (((x) & 1) << 0) +#define S_008020_SOFT_RESET_CB(x) (((x) & 1) << 1) +#define S_008020_SOFT_RESET_CR(x) (((x) & 1) << 2) +#define S_008020_SOFT_RESET_DB(x) (((x) & 1) << 3) +#define S_008020_SOFT_RESET_PA(x) (((x) & 1) << 5) +#define S_008020_SOFT_RESET_SC(x) (((x) & 1) << 6) +#define S_008020_SOFT_RESET_SMX(x) (((x) & 1) << 7) +#define S_008020_SOFT_RESET_SPI(x) (((x) & 1) << 8) +#define S_008020_SOFT_RESET_SH(x) (((x) & 1) << 9) +#define S_008020_SOFT_RESET_SX(x) (((x) & 1) << 10) +#define S_008020_SOFT_RESET_TC(x) (((x) & 1) << 11) +#define S_008020_SOFT_RESET_TA(x) (((x) & 1) << 12) +#define S_008020_SOFT_RESET_VC(x) (((x) & 1) << 13) +#define S_008020_SOFT_RESET_VGT(x) (((x) & 1) << 14) +#define R_008010_GRBM_STATUS 0x8010 +#define S_008010_CMDFIFO_AVAIL(x) (((x) & 0x1F) << 0) +#define S_008010_CP_RQ_PENDING(x) (((x) & 1) << 6) +#define S_008010_CF_RQ_PENDING(x) (((x) & 1) << 7) +#define S_008010_PF_RQ_PENDING(x) (((x) & 1) << 8) +#define S_008010_GRBM_EE_BUSY(x) (((x) & 1) << 10) +#define S_008010_VC_BUSY(x) (((x) & 1) << 11) +#define S_008010_DB03_CLEAN(x) (((x) & 1) << 12) +#define S_008010_CB03_CLEAN(x) (((x) & 1) << 13) +#define S_008010_VGT_BUSY_NO_DMA(x) (((x) & 1) << 16) +#define S_008010_VGT_BUSY(x) (((x) & 1) << 17) +#define S_008010_TA03_BUSY(x) (((x) & 1) << 18) +#define S_008010_TC_BUSY(x) (((x) & 1) << 19) +#define S_008010_SX_BUSY(x) (((x) & 1) << 20) +#define S_008010_SH_BUSY(x) (((x) & 1) << 21) +#define S_008010_SPI03_BUSY(x) (((x) & 1) << 22) +#define S_008010_SMX_BUSY(x) (((x) & 1) << 23) +#define S_008010_SC_BUSY(x) (((x) & 1) << 24) +#define S_008010_PA_BUSY(x) (((x) & 1) << 25) +#define S_008010_DB03_BUSY(x) (((x) & 1) << 26) +#define S_008010_CR_BUSY(x) (((x) & 1) << 27) +#define S_008010_CP_COHERENCY_BUSY(x) (((x) & 1) << 28) +#define S_008010_CP_BUSY(x) (((x) & 1) << 29) +#define S_008010_CB03_BUSY(x) (((x) & 1) << 30) +#define S_008010_GUI_ACTIVE(x) (((x) & 1) << 31) +#define G_008010_CMDFIFO_AVAIL(x) (((x) >> 0) & 0x1F) +#define G_008010_CP_RQ_PENDING(x) (((x) >> 6) & 1) +#define G_008010_CF_RQ_PENDING(x) (((x) >> 7) & 1) +#define G_008010_PF_RQ_PENDING(x) (((x) >> 8) & 1) +#define G_008010_GRBM_EE_BUSY(x) (((x) >> 10) & 1) +#define G_008010_VC_BUSY(x) (((x) >> 11) & 1) +#define G_008010_DB03_CLEAN(x) (((x) >> 12) & 1) +#define G_008010_CB03_CLEAN(x) (((x) >> 13) & 1) +#define G_008010_VGT_BUSY_NO_DMA(x) (((x) >> 16) & 1) +#define G_008010_VGT_BUSY(x) (((x) >> 17) & 1) +#define G_008010_TA03_BUSY(x) (((x) >> 18) & 1) +#define G_008010_TC_BUSY(x) (((x) >> 19) & 1) +#define G_008010_SX_BUSY(x) (((x) >> 20) & 1) +#define G_008010_SH_BUSY(x) (((x) >> 21) & 1) +#define G_008010_SPI03_BUSY(x) (((x) >> 22) & 1) +#define G_008010_SMX_BUSY(x) (((x) >> 23) & 1) +#define G_008010_SC_BUSY(x) (((x) >> 24) & 1) +#define G_008010_PA_BUSY(x) (((x) >> 25) & 1) +#define G_008010_DB03_BUSY(x) (((x) >> 26) & 1) +#define G_008010_CR_BUSY(x) (((x) >> 27) & 1) +#define G_008010_CP_COHERENCY_BUSY(x) (((x) >> 28) & 1) +#define G_008010_CP_BUSY(x) (((x) >> 29) & 1) +#define G_008010_CB03_BUSY(x) (((x) >> 30) & 1) +#define G_008010_GUI_ACTIVE(x) (((x) >> 31) & 1) +#define R_008014_GRBM_STATUS2 0x8014 +#define S_008014_CR_CLEAN(x) (((x) & 1) << 0) +#define S_008014_SMX_CLEAN(x) (((x) & 1) << 1) +#define S_008014_SPI0_BUSY(x) (((x) & 1) << 8) +#define S_008014_SPI1_BUSY(x) (((x) & 1) << 9) +#define S_008014_SPI2_BUSY(x) (((x) & 1) << 10) +#define S_008014_SPI3_BUSY(x) (((x) & 1) << 11) +#define S_008014_TA0_BUSY(x) (((x) & 1) << 12) +#define S_008014_TA1_BUSY(x) (((x) & 1) << 13) +#define S_008014_TA2_BUSY(x) (((x) & 1) << 14) +#define S_008014_TA3_BUSY(x) (((x) & 1) << 15) +#define S_008014_DB0_BUSY(x) (((x) & 1) << 16) +#define S_008014_DB1_BUSY(x) (((x) & 1) << 17) +#define S_008014_DB2_BUSY(x) (((x) & 1) << 18) +#define S_008014_DB3_BUSY(x) (((x) & 1) << 19) +#define S_008014_CB0_BUSY(x) (((x) & 1) << 20) +#define S_008014_CB1_BUSY(x) (((x) & 1) << 21) +#define S_008014_CB2_BUSY(x) (((x) & 1) << 22) +#define S_008014_CB3_BUSY(x) (((x) & 1) << 23) +#define G_008014_CR_CLEAN(x) (((x) >> 0) & 1) +#define G_008014_SMX_CLEAN(x) (((x) >> 1) & 1) +#define G_008014_SPI0_BUSY(x) (((x) >> 8) & 1) +#define G_008014_SPI1_BUSY(x) (((x) >> 9) & 1) +#define G_008014_SPI2_BUSY(x) (((x) >> 10) & 1) +#define G_008014_SPI3_BUSY(x) (((x) >> 11) & 1) +#define G_008014_TA0_BUSY(x) (((x) >> 12) & 1) +#define G_008014_TA1_BUSY(x) (((x) >> 13) & 1) +#define G_008014_TA2_BUSY(x) (((x) >> 14) & 1) +#define G_008014_TA3_BUSY(x) (((x) >> 15) & 1) +#define G_008014_DB0_BUSY(x) (((x) >> 16) & 1) +#define G_008014_DB1_BUSY(x) (((x) >> 17) & 1) +#define G_008014_DB2_BUSY(x) (((x) >> 18) & 1) +#define G_008014_DB3_BUSY(x) (((x) >> 19) & 1) +#define G_008014_CB0_BUSY(x) (((x) >> 20) & 1) +#define G_008014_CB1_BUSY(x) (((x) >> 21) & 1) +#define G_008014_CB2_BUSY(x) (((x) >> 22) & 1) +#define G_008014_CB3_BUSY(x) (((x) >> 23) & 1) +#define R_000E50_SRBM_STATUS 0x0E50 +#define G_000E50_RLC_RQ_PENDING(x) (((x) >> 3) & 1) +#define G_000E50_RCU_RQ_PENDING(x) (((x) >> 4) & 1) +#define G_000E50_GRBM_RQ_PENDING(x) (((x) >> 5) & 1) +#define G_000E50_HI_RQ_PENDING(x) (((x) >> 6) & 1) +#define G_000E50_IO_EXTERN_SIGNAL(x) (((x) >> 7) & 1) +#define G_000E50_VMC_BUSY(x) (((x) >> 8) & 1) +#define G_000E50_MCB_BUSY(x) (((x) >> 9) & 1) +#define G_000E50_MCDZ_BUSY(x) (((x) >> 10) & 1) +#define G_000E50_MCDY_BUSY(x) (((x) >> 11) & 1) +#define G_000E50_MCDX_BUSY(x) (((x) >> 12) & 1) +#define G_000E50_MCDW_BUSY(x) (((x) >> 13) & 1) +#define G_000E50_SEM_BUSY(x) (((x) >> 14) & 1) +#define G_000E50_RLC_BUSY(x) (((x) >> 15) & 1) +#define G_000E50_BIF_BUSY(x) (((x) >> 29) & 1) +#define R_000E60_SRBM_SOFT_RESET 0x0E60 +#define S_000E60_SOFT_RESET_BIF(x) (((x) & 1) << 1) +#define S_000E60_SOFT_RESET_CG(x) (((x) & 1) << 2) +#define S_000E60_SOFT_RESET_CMC(x) (((x) & 1) << 3) +#define S_000E60_SOFT_RESET_CSC(x) (((x) & 1) << 4) +#define S_000E60_SOFT_RESET_DC(x) (((x) & 1) << 5) +#define S_000E60_SOFT_RESET_GRBM(x) (((x) & 1) << 8) +#define S_000E60_SOFT_RESET_HDP(x) (((x) & 1) << 9) +#define S_000E60_SOFT_RESET_IH(x) (((x) & 1) << 10) +#define S_000E60_SOFT_RESET_MC(x) (((x) & 1) << 11) +#define S_000E60_SOFT_RESET_RLC(x) (((x) & 1) << 13) +#define S_000E60_SOFT_RESET_ROM(x) (((x) & 1) << 14) +#define S_000E60_SOFT_RESET_SEM(x) (((x) & 1) << 15) +#define S_000E60_SOFT_RESET_TSC(x) (((x) & 1) << 16) +#define S_000E60_SOFT_RESET_VMC(x) (((x) & 1) << 17) + +#define R_005480_HDP_MEM_COHERENCY_FLUSH_CNTL 0x5480 + +#define R_028C04_PA_SC_AA_CONFIG 0x028C04 +#define S_028C04_MSAA_NUM_SAMPLES(x) (((x) & 0x3) << 0) +#define G_028C04_MSAA_NUM_SAMPLES(x) (((x) >> 0) & 0x3) +#define C_028C04_MSAA_NUM_SAMPLES 0xFFFFFFFC +#define S_028C04_AA_MASK_CENTROID_DTMN(x) (((x) & 0x1) << 4) +#define G_028C04_AA_MASK_CENTROID_DTMN(x) (((x) >> 4) & 0x1) +#define C_028C04_AA_MASK_CENTROID_DTMN 0xFFFFFFEF +#define S_028C04_MAX_SAMPLE_DIST(x) (((x) & 0xF) << 13) +#define G_028C04_MAX_SAMPLE_DIST(x) (((x) >> 13) & 0xF) +#define C_028C04_MAX_SAMPLE_DIST 0xFFFE1FFF +#define R_0280E0_CB_COLOR0_FRAG 0x0280E0 +#define S_0280E0_BASE_256B(x) (((x) & 0xFFFFFFFF) << 0) +#define G_0280E0_BASE_256B(x) (((x) >> 0) & 0xFFFFFFFF) +#define C_0280E0_BASE_256B 0x00000000 +#define R_0280E4_CB_COLOR1_FRAG 0x0280E4 +#define R_0280E8_CB_COLOR2_FRAG 0x0280E8 +#define R_0280EC_CB_COLOR3_FRAG 0x0280EC +#define R_0280F0_CB_COLOR4_FRAG 0x0280F0 +#define R_0280F4_CB_COLOR5_FRAG 0x0280F4 +#define R_0280F8_CB_COLOR6_FRAG 0x0280F8 +#define R_0280FC_CB_COLOR7_FRAG 0x0280FC +#define R_0280C0_CB_COLOR0_TILE 0x0280C0 +#define S_0280C0_BASE_256B(x) (((x) & 0xFFFFFFFF) << 0) +#define G_0280C0_BASE_256B(x) (((x) >> 0) & 0xFFFFFFFF) +#define C_0280C0_BASE_256B 0x00000000 +#define R_0280C4_CB_COLOR1_TILE 0x0280C4 +#define R_0280C8_CB_COLOR2_TILE 0x0280C8 +#define R_0280CC_CB_COLOR3_TILE 0x0280CC +#define R_0280D0_CB_COLOR4_TILE 0x0280D0 +#define R_0280D4_CB_COLOR5_TILE 0x0280D4 +#define R_0280D8_CB_COLOR6_TILE 0x0280D8 +#define R_0280DC_CB_COLOR7_TILE 0x0280DC +#define R_0280A0_CB_COLOR0_INFO 0x0280A0 +#define S_0280A0_ENDIAN(x) (((x) & 0x3) << 0) +#define G_0280A0_ENDIAN(x) (((x) >> 0) & 0x3) +#define C_0280A0_ENDIAN 0xFFFFFFFC +#define S_0280A0_FORMAT(x) (((x) & 0x3F) << 2) +#define G_0280A0_FORMAT(x) (((x) >> 2) & 0x3F) +#define C_0280A0_FORMAT 0xFFFFFF03 +#define V_0280A0_COLOR_INVALID 0x00000000 +#define V_0280A0_COLOR_8 0x00000001 +#define V_0280A0_COLOR_4_4 0x00000002 +#define V_0280A0_COLOR_3_3_2 0x00000003 +#define V_0280A0_COLOR_16 0x00000005 +#define V_0280A0_COLOR_16_FLOAT 0x00000006 +#define V_0280A0_COLOR_8_8 0x00000007 +#define V_0280A0_COLOR_5_6_5 0x00000008 +#define V_0280A0_COLOR_6_5_5 0x00000009 +#define V_0280A0_COLOR_1_5_5_5 0x0000000A +#define V_0280A0_COLOR_4_4_4_4 0x0000000B +#define V_0280A0_COLOR_5_5_5_1 0x0000000C +#define V_0280A0_COLOR_32 0x0000000D +#define V_0280A0_COLOR_32_FLOAT 0x0000000E +#define V_0280A0_COLOR_16_16 0x0000000F +#define V_0280A0_COLOR_16_16_FLOAT 0x00000010 +#define V_0280A0_COLOR_8_24 0x00000011 +#define V_0280A0_COLOR_8_24_FLOAT 0x00000012 +#define V_0280A0_COLOR_24_8 0x00000013 +#define V_0280A0_COLOR_24_8_FLOAT 0x00000014 +#define V_0280A0_COLOR_10_11_11 0x00000015 +#define V_0280A0_COLOR_10_11_11_FLOAT 0x00000016 +#define V_0280A0_COLOR_11_11_10 0x00000017 +#define V_0280A0_COLOR_11_11_10_FLOAT 0x00000018 +#define V_0280A0_COLOR_2_10_10_10 0x00000019 +#define V_0280A0_COLOR_8_8_8_8 0x0000001A +#define V_0280A0_COLOR_10_10_10_2 0x0000001B +#define V_0280A0_COLOR_X24_8_32_FLOAT 0x0000001C +#define V_0280A0_COLOR_32_32 0x0000001D +#define V_0280A0_COLOR_32_32_FLOAT 0x0000001E +#define V_0280A0_COLOR_16_16_16_16 0x0000001F +#define V_0280A0_COLOR_16_16_16_16_FLOAT 0x00000020 +#define V_0280A0_COLOR_32_32_32_32 0x00000022 +#define V_0280A0_COLOR_32_32_32_32_FLOAT 0x00000023 +#define S_0280A0_ARRAY_MODE(x) (((x) & 0xF) << 8) +#define G_0280A0_ARRAY_MODE(x) (((x) >> 8) & 0xF) +#define C_0280A0_ARRAY_MODE 0xFFFFF0FF +#define V_0280A0_ARRAY_LINEAR_GENERAL 0x00000000 +#define V_0280A0_ARRAY_LINEAR_ALIGNED 0x00000001 +#define V_0280A0_ARRAY_1D_TILED_THIN1 0x00000002 +#define V_0280A0_ARRAY_2D_TILED_THIN1 0x00000004 +#define S_0280A0_NUMBER_TYPE(x) (((x) & 0x7) << 12) +#define G_0280A0_NUMBER_TYPE(x) (((x) >> 12) & 0x7) +#define C_0280A0_NUMBER_TYPE 0xFFFF8FFF +#define S_0280A0_READ_SIZE(x) (((x) & 0x1) << 15) +#define G_0280A0_READ_SIZE(x) (((x) >> 15) & 0x1) +#define C_0280A0_READ_SIZE 0xFFFF7FFF +#define S_0280A0_COMP_SWAP(x) (((x) & 0x3) << 16) +#define G_0280A0_COMP_SWAP(x) (((x) >> 16) & 0x3) +#define C_0280A0_COMP_SWAP 0xFFFCFFFF +#define S_0280A0_TILE_MODE(x) (((x) & 0x3) << 18) +#define G_0280A0_TILE_MODE(x) (((x) >> 18) & 0x3) +#define C_0280A0_TILE_MODE 0xFFF3FFFF +#define V_0280A0_TILE_DISABLE 0 +#define V_0280A0_CLEAR_ENABLE 1 +#define V_0280A0_FRAG_ENABLE 2 +#define S_0280A0_BLEND_CLAMP(x) (((x) & 0x1) << 20) +#define G_0280A0_BLEND_CLAMP(x) (((x) >> 20) & 0x1) +#define C_0280A0_BLEND_CLAMP 0xFFEFFFFF +#define S_0280A0_CLEAR_COLOR(x) (((x) & 0x1) << 21) +#define G_0280A0_CLEAR_COLOR(x) (((x) >> 21) & 0x1) +#define C_0280A0_CLEAR_COLOR 0xFFDFFFFF +#define S_0280A0_BLEND_BYPASS(x) (((x) & 0x1) << 22) +#define G_0280A0_BLEND_BYPASS(x) (((x) >> 22) & 0x1) +#define C_0280A0_BLEND_BYPASS 0xFFBFFFFF +#define S_0280A0_BLEND_FLOAT32(x) (((x) & 0x1) << 23) +#define G_0280A0_BLEND_FLOAT32(x) (((x) >> 23) & 0x1) +#define C_0280A0_BLEND_FLOAT32 0xFF7FFFFF +#define S_0280A0_SIMPLE_FLOAT(x) (((x) & 0x1) << 24) +#define G_0280A0_SIMPLE_FLOAT(x) (((x) >> 24) & 0x1) +#define C_0280A0_SIMPLE_FLOAT 0xFEFFFFFF +#define S_0280A0_ROUND_MODE(x) (((x) & 0x1) << 25) +#define G_0280A0_ROUND_MODE(x) (((x) >> 25) & 0x1) +#define C_0280A0_ROUND_MODE 0xFDFFFFFF +#define S_0280A0_TILE_COMPACT(x) (((x) & 0x1) << 26) +#define G_0280A0_TILE_COMPACT(x) (((x) >> 26) & 0x1) +#define C_0280A0_TILE_COMPACT 0xFBFFFFFF +#define S_0280A0_SOURCE_FORMAT(x) (((x) & 0x1) << 27) +#define G_0280A0_SOURCE_FORMAT(x) (((x) >> 27) & 0x1) +#define C_0280A0_SOURCE_FORMAT 0xF7FFFFFF +#define R_0280A4_CB_COLOR1_INFO 0x0280A4 +#define R_0280A8_CB_COLOR2_INFO 0x0280A8 +#define R_0280AC_CB_COLOR3_INFO 0x0280AC +#define R_0280B0_CB_COLOR4_INFO 0x0280B0 +#define R_0280B4_CB_COLOR5_INFO 0x0280B4 +#define R_0280B8_CB_COLOR6_INFO 0x0280B8 +#define R_0280BC_CB_COLOR7_INFO 0x0280BC +#define R_028060_CB_COLOR0_SIZE 0x028060 +#define S_028060_PITCH_TILE_MAX(x) (((x) & 0x3FF) << 0) +#define G_028060_PITCH_TILE_MAX(x) (((x) >> 0) & 0x3FF) +#define C_028060_PITCH_TILE_MAX 0xFFFFFC00 +#define S_028060_SLICE_TILE_MAX(x) (((x) & 0xFFFFF) << 10) +#define G_028060_SLICE_TILE_MAX(x) (((x) >> 10) & 0xFFFFF) +#define C_028060_SLICE_TILE_MAX 0xC00003FF +#define R_028064_CB_COLOR1_SIZE 0x028064 +#define R_028068_CB_COLOR2_SIZE 0x028068 +#define R_02806C_CB_COLOR3_SIZE 0x02806C +#define R_028070_CB_COLOR4_SIZE 0x028070 +#define R_028074_CB_COLOR5_SIZE 0x028074 +#define R_028078_CB_COLOR6_SIZE 0x028078 +#define R_02807C_CB_COLOR7_SIZE 0x02807C +#define R_028238_CB_TARGET_MASK 0x028238 +#define S_028238_TARGET0_ENABLE(x) (((x) & 0xF) << 0) +#define G_028238_TARGET0_ENABLE(x) (((x) >> 0) & 0xF) +#define C_028238_TARGET0_ENABLE 0xFFFFFFF0 +#define S_028238_TARGET1_ENABLE(x) (((x) & 0xF) << 4) +#define G_028238_TARGET1_ENABLE(x) (((x) >> 4) & 0xF) +#define C_028238_TARGET1_ENABLE 0xFFFFFF0F +#define S_028238_TARGET2_ENABLE(x) (((x) & 0xF) << 8) +#define G_028238_TARGET2_ENABLE(x) (((x) >> 8) & 0xF) +#define C_028238_TARGET2_ENABLE 0xFFFFF0FF +#define S_028238_TARGET3_ENABLE(x) (((x) & 0xF) << 12) +#define G_028238_TARGET3_ENABLE(x) (((x) >> 12) & 0xF) +#define C_028238_TARGET3_ENABLE 0xFFFF0FFF +#define S_028238_TARGET4_ENABLE(x) (((x) & 0xF) << 16) +#define G_028238_TARGET4_ENABLE(x) (((x) >> 16) & 0xF) +#define C_028238_TARGET4_ENABLE 0xFFF0FFFF +#define S_028238_TARGET5_ENABLE(x) (((x) & 0xF) << 20) +#define G_028238_TARGET5_ENABLE(x) (((x) >> 20) & 0xF) +#define C_028238_TARGET5_ENABLE 0xFF0FFFFF +#define S_028238_TARGET6_ENABLE(x) (((x) & 0xF) << 24) +#define G_028238_TARGET6_ENABLE(x) (((x) >> 24) & 0xF) +#define C_028238_TARGET6_ENABLE 0xF0FFFFFF +#define S_028238_TARGET7_ENABLE(x) (((x) & 0xF) << 28) +#define G_028238_TARGET7_ENABLE(x) (((x) >> 28) & 0xF) +#define C_028238_TARGET7_ENABLE 0x0FFFFFFF +#define R_02823C_CB_SHADER_MASK 0x02823C +#define S_02823C_OUTPUT0_ENABLE(x) (((x) & 0xF) << 0) +#define G_02823C_OUTPUT0_ENABLE(x) (((x) >> 0) & 0xF) +#define C_02823C_OUTPUT0_ENABLE 0xFFFFFFF0 +#define S_02823C_OUTPUT1_ENABLE(x) (((x) & 0xF) << 4) +#define G_02823C_OUTPUT1_ENABLE(x) (((x) >> 4) & 0xF) +#define C_02823C_OUTPUT1_ENABLE 0xFFFFFF0F +#define S_02823C_OUTPUT2_ENABLE(x) (((x) & 0xF) << 8) +#define G_02823C_OUTPUT2_ENABLE(x) (((x) >> 8) & 0xF) +#define C_02823C_OUTPUT2_ENABLE 0xFFFFF0FF +#define S_02823C_OUTPUT3_ENABLE(x) (((x) & 0xF) << 12) +#define G_02823C_OUTPUT3_ENABLE(x) (((x) >> 12) & 0xF) +#define C_02823C_OUTPUT3_ENABLE 0xFFFF0FFF +#define S_02823C_OUTPUT4_ENABLE(x) (((x) & 0xF) << 16) +#define G_02823C_OUTPUT4_ENABLE(x) (((x) >> 16) & 0xF) +#define C_02823C_OUTPUT4_ENABLE 0xFFF0FFFF +#define S_02823C_OUTPUT5_ENABLE(x) (((x) & 0xF) << 20) +#define G_02823C_OUTPUT5_ENABLE(x) (((x) >> 20) & 0xF) +#define C_02823C_OUTPUT5_ENABLE 0xFF0FFFFF +#define S_02823C_OUTPUT6_ENABLE(x) (((x) & 0xF) << 24) +#define G_02823C_OUTPUT6_ENABLE(x) (((x) >> 24) & 0xF) +#define C_02823C_OUTPUT6_ENABLE 0xF0FFFFFF +#define S_02823C_OUTPUT7_ENABLE(x) (((x) & 0xF) << 28) +#define G_02823C_OUTPUT7_ENABLE(x) (((x) >> 28) & 0xF) +#define C_02823C_OUTPUT7_ENABLE 0x0FFFFFFF +#define R_028AB0_VGT_STRMOUT_EN 0x028AB0 +#define S_028AB0_STREAMOUT(x) (((x) & 0x1) << 0) +#define G_028AB0_STREAMOUT(x) (((x) >> 0) & 0x1) +#define C_028AB0_STREAMOUT 0xFFFFFFFE +#define R_028B20_VGT_STRMOUT_BUFFER_EN 0x028B20 +#define S_028B20_BUFFER_0_EN(x) (((x) & 0x1) << 0) +#define G_028B20_BUFFER_0_EN(x) (((x) >> 0) & 0x1) +#define C_028B20_BUFFER_0_EN 0xFFFFFFFE +#define S_028B20_BUFFER_1_EN(x) (((x) & 0x1) << 1) +#define G_028B20_BUFFER_1_EN(x) (((x) >> 1) & 0x1) +#define C_028B20_BUFFER_1_EN 0xFFFFFFFD +#define S_028B20_BUFFER_2_EN(x) (((x) & 0x1) << 2) +#define G_028B20_BUFFER_2_EN(x) (((x) >> 2) & 0x1) +#define C_028B20_BUFFER_2_EN 0xFFFFFFFB +#define S_028B20_BUFFER_3_EN(x) (((x) & 0x1) << 3) +#define G_028B20_BUFFER_3_EN(x) (((x) >> 3) & 0x1) +#define C_028B20_BUFFER_3_EN 0xFFFFFFF7 +#define S_028B20_SIZE(x) (((x) & 0xFFFFFFFF) << 0) +#define G_028B20_SIZE(x) (((x) >> 0) & 0xFFFFFFFF) +#define C_028B20_SIZE 0x00000000 +#define R_038000_SQ_TEX_RESOURCE_WORD0_0 0x038000 +#define S_038000_DIM(x) (((x) & 0x7) << 0) +#define G_038000_DIM(x) (((x) >> 0) & 0x7) +#define C_038000_DIM 0xFFFFFFF8 +#define V_038000_SQ_TEX_DIM_1D 0x00000000 +#define V_038000_SQ_TEX_DIM_2D 0x00000001 +#define V_038000_SQ_TEX_DIM_3D 0x00000002 +#define V_038000_SQ_TEX_DIM_CUBEMAP 0x00000003 +#define V_038000_SQ_TEX_DIM_1D_ARRAY 0x00000004 +#define V_038000_SQ_TEX_DIM_2D_ARRAY 0x00000005 +#define V_038000_SQ_TEX_DIM_2D_MSAA 0x00000006 +#define V_038000_SQ_TEX_DIM_2D_ARRAY_MSAA 0x00000007 +#define S_038000_TILE_MODE(x) (((x) & 0xF) << 3) +#define G_038000_TILE_MODE(x) (((x) >> 3) & 0xF) +#define C_038000_TILE_MODE 0xFFFFFF87 +#define V_038000_ARRAY_LINEAR_GENERAL 0x00000000 +#define V_038000_ARRAY_LINEAR_ALIGNED 0x00000001 +#define V_038000_ARRAY_1D_TILED_THIN1 0x00000002 +#define V_038000_ARRAY_2D_TILED_THIN1 0x00000004 +#define S_038000_TILE_TYPE(x) (((x) & 0x1) << 7) +#define G_038000_TILE_TYPE(x) (((x) >> 7) & 0x1) +#define C_038000_TILE_TYPE 0xFFFFFF7F +#define S_038000_PITCH(x) (((x) & 0x7FF) << 8) +#define G_038000_PITCH(x) (((x) >> 8) & 0x7FF) +#define C_038000_PITCH 0xFFF800FF +#define S_038000_TEX_WIDTH(x) (((x) & 0x1FFF) << 19) +#define G_038000_TEX_WIDTH(x) (((x) >> 19) & 0x1FFF) +#define C_038000_TEX_WIDTH 0x0007FFFF +#define R_038004_SQ_TEX_RESOURCE_WORD1_0 0x038004 +#define S_038004_TEX_HEIGHT(x) (((x) & 0x1FFF) << 0) +#define G_038004_TEX_HEIGHT(x) (((x) >> 0) & 0x1FFF) +#define C_038004_TEX_HEIGHT 0xFFFFE000 +#define S_038004_TEX_DEPTH(x) (((x) & 0x1FFF) << 13) +#define G_038004_TEX_DEPTH(x) (((x) >> 13) & 0x1FFF) +#define C_038004_TEX_DEPTH 0xFC001FFF +#define S_038004_DATA_FORMAT(x) (((x) & 0x3F) << 26) +#define G_038004_DATA_FORMAT(x) (((x) >> 26) & 0x3F) +#define C_038004_DATA_FORMAT 0x03FFFFFF +#define V_038004_COLOR_INVALID 0x00000000 +#define V_038004_COLOR_8 0x00000001 +#define V_038004_COLOR_4_4 0x00000002 +#define V_038004_COLOR_3_3_2 0x00000003 +#define V_038004_COLOR_16 0x00000005 +#define V_038004_COLOR_16_FLOAT 0x00000006 +#define V_038004_COLOR_8_8 0x00000007 +#define V_038004_COLOR_5_6_5 0x00000008 +#define V_038004_COLOR_6_5_5 0x00000009 +#define V_038004_COLOR_1_5_5_5 0x0000000A +#define V_038004_COLOR_4_4_4_4 0x0000000B +#define V_038004_COLOR_5_5_5_1 0x0000000C +#define V_038004_COLOR_32 0x0000000D +#define V_038004_COLOR_32_FLOAT 0x0000000E +#define V_038004_COLOR_16_16 0x0000000F +#define V_038004_COLOR_16_16_FLOAT 0x00000010 +#define V_038004_COLOR_8_24 0x00000011 +#define V_038004_COLOR_8_24_FLOAT 0x00000012 +#define V_038004_COLOR_24_8 0x00000013 +#define V_038004_COLOR_24_8_FLOAT 0x00000014 +#define V_038004_COLOR_10_11_11 0x00000015 +#define V_038004_COLOR_10_11_11_FLOAT 0x00000016 +#define V_038004_COLOR_11_11_10 0x00000017 +#define V_038004_COLOR_11_11_10_FLOAT 0x00000018 +#define V_038004_COLOR_2_10_10_10 0x00000019 +#define V_038004_COLOR_8_8_8_8 0x0000001A +#define V_038004_COLOR_10_10_10_2 0x0000001B +#define V_038004_COLOR_X24_8_32_FLOAT 0x0000001C +#define V_038004_COLOR_32_32 0x0000001D +#define V_038004_COLOR_32_32_FLOAT 0x0000001E +#define V_038004_COLOR_16_16_16_16 0x0000001F +#define V_038004_COLOR_16_16_16_16_FLOAT 0x00000020 +#define V_038004_COLOR_32_32_32_32 0x00000022 +#define V_038004_COLOR_32_32_32_32_FLOAT 0x00000023 +#define V_038004_FMT_1 0x00000025 +#define V_038004_FMT_GB_GR 0x00000027 +#define V_038004_FMT_BG_RG 0x00000028 +#define V_038004_FMT_32_AS_8 0x00000029 +#define V_038004_FMT_32_AS_8_8 0x0000002A +#define V_038004_FMT_5_9_9_9_SHAREDEXP 0x0000002B +#define V_038004_FMT_8_8_8 0x0000002C +#define V_038004_FMT_16_16_16 0x0000002D +#define V_038004_FMT_16_16_16_FLOAT 0x0000002E +#define V_038004_FMT_32_32_32 0x0000002F +#define V_038004_FMT_32_32_32_FLOAT 0x00000030 +#define V_038004_FMT_BC1 0x00000031 +#define V_038004_FMT_BC2 0x00000032 +#define V_038004_FMT_BC3 0x00000033 +#define V_038004_FMT_BC4 0x00000034 +#define V_038004_FMT_BC5 0x00000035 +#define V_038004_FMT_BC6 0x00000036 +#define V_038004_FMT_BC7 0x00000037 +#define V_038004_FMT_32_AS_32_32_32_32 0x00000038 +#define R_038010_SQ_TEX_RESOURCE_WORD4_0 0x038010 +#define S_038010_FORMAT_COMP_X(x) (((x) & 0x3) << 0) +#define G_038010_FORMAT_COMP_X(x) (((x) >> 0) & 0x3) +#define C_038010_FORMAT_COMP_X 0xFFFFFFFC +#define S_038010_FORMAT_COMP_Y(x) (((x) & 0x3) << 2) +#define G_038010_FORMAT_COMP_Y(x) (((x) >> 2) & 0x3) +#define C_038010_FORMAT_COMP_Y 0xFFFFFFF3 +#define S_038010_FORMAT_COMP_Z(x) (((x) & 0x3) << 4) +#define G_038010_FORMAT_COMP_Z(x) (((x) >> 4) & 0x3) +#define C_038010_FORMAT_COMP_Z 0xFFFFFFCF +#define S_038010_FORMAT_COMP_W(x) (((x) & 0x3) << 6) +#define G_038010_FORMAT_COMP_W(x) (((x) >> 6) & 0x3) +#define C_038010_FORMAT_COMP_W 0xFFFFFF3F +#define S_038010_NUM_FORMAT_ALL(x) (((x) & 0x3) << 8) +#define G_038010_NUM_FORMAT_ALL(x) (((x) >> 8) & 0x3) +#define C_038010_NUM_FORMAT_ALL 0xFFFFFCFF +#define S_038010_SRF_MODE_ALL(x) (((x) & 0x1) << 10) +#define G_038010_SRF_MODE_ALL(x) (((x) >> 10) & 0x1) +#define C_038010_SRF_MODE_ALL 0xFFFFFBFF +#define S_038010_FORCE_DEGAMMA(x) (((x) & 0x1) << 11) +#define G_038010_FORCE_DEGAMMA(x) (((x) >> 11) & 0x1) +#define C_038010_FORCE_DEGAMMA 0xFFFFF7FF +#define S_038010_ENDIAN_SWAP(x) (((x) & 0x3) << 12) +#define G_038010_ENDIAN_SWAP(x) (((x) >> 12) & 0x3) +#define C_038010_ENDIAN_SWAP 0xFFFFCFFF +#define S_038010_REQUEST_SIZE(x) (((x) & 0x3) << 14) +#define G_038010_REQUEST_SIZE(x) (((x) >> 14) & 0x3) +#define C_038010_REQUEST_SIZE 0xFFFF3FFF +#define S_038010_DST_SEL_X(x) (((x) & 0x7) << 16) +#define G_038010_DST_SEL_X(x) (((x) >> 16) & 0x7) +#define C_038010_DST_SEL_X 0xFFF8FFFF +#define S_038010_DST_SEL_Y(x) (((x) & 0x7) << 19) +#define G_038010_DST_SEL_Y(x) (((x) >> 19) & 0x7) +#define C_038010_DST_SEL_Y 0xFFC7FFFF +#define S_038010_DST_SEL_Z(x) (((x) & 0x7) << 22) +#define G_038010_DST_SEL_Z(x) (((x) >> 22) & 0x7) +#define C_038010_DST_SEL_Z 0xFE3FFFFF +#define S_038010_DST_SEL_W(x) (((x) & 0x7) << 25) +#define G_038010_DST_SEL_W(x) (((x) >> 25) & 0x7) +#define C_038010_DST_SEL_W 0xF1FFFFFF +# define SQ_SEL_X 0 +# define SQ_SEL_Y 1 +# define SQ_SEL_Z 2 +# define SQ_SEL_W 3 +# define SQ_SEL_0 4 +# define SQ_SEL_1 5 +#define S_038010_BASE_LEVEL(x) (((x) & 0xF) << 28) +#define G_038010_BASE_LEVEL(x) (((x) >> 28) & 0xF) +#define C_038010_BASE_LEVEL 0x0FFFFFFF +#define R_038014_SQ_TEX_RESOURCE_WORD5_0 0x038014 +#define S_038014_LAST_LEVEL(x) (((x) & 0xF) << 0) +#define G_038014_LAST_LEVEL(x) (((x) >> 0) & 0xF) +#define C_038014_LAST_LEVEL 0xFFFFFFF0 +#define S_038014_BASE_ARRAY(x) (((x) & 0x1FFF) << 4) +#define G_038014_BASE_ARRAY(x) (((x) >> 4) & 0x1FFF) +#define C_038014_BASE_ARRAY 0xFFFE000F +#define S_038014_LAST_ARRAY(x) (((x) & 0x1FFF) << 17) +#define G_038014_LAST_ARRAY(x) (((x) >> 17) & 0x1FFF) +#define C_038014_LAST_ARRAY 0xC001FFFF +#define R_0288A8_SQ_ESGS_RING_ITEMSIZE 0x0288A8 +#define S_0288A8_ITEMSIZE(x) (((x) & 0x7FFF) << 0) +#define G_0288A8_ITEMSIZE(x) (((x) >> 0) & 0x7FFF) +#define C_0288A8_ITEMSIZE 0xFFFF8000 +#define R_008C44_SQ_ESGS_RING_SIZE 0x008C44 +#define S_008C44_MEM_SIZE(x) (((x) & 0xFFFFFFFF) << 0) +#define G_008C44_MEM_SIZE(x) (((x) >> 0) & 0xFFFFFFFF) +#define C_008C44_MEM_SIZE 0x00000000 +#define R_0288B0_SQ_ESTMP_RING_ITEMSIZE 0x0288B0 +#define S_0288B0_ITEMSIZE(x) (((x) & 0x7FFF) << 0) +#define G_0288B0_ITEMSIZE(x) (((x) >> 0) & 0x7FFF) +#define C_0288B0_ITEMSIZE 0xFFFF8000 +#define R_008C54_SQ_ESTMP_RING_SIZE 0x008C54 +#define S_008C54_MEM_SIZE(x) (((x) & 0xFFFFFFFF) << 0) +#define G_008C54_MEM_SIZE(x) (((x) >> 0) & 0xFFFFFFFF) +#define C_008C54_MEM_SIZE 0x00000000 +#define R_0288C0_SQ_FBUF_RING_ITEMSIZE 0x0288C0 +#define S_0288C0_ITEMSIZE(x) (((x) & 0x7FFF) << 0) +#define G_0288C0_ITEMSIZE(x) (((x) >> 0) & 0x7FFF) +#define C_0288C0_ITEMSIZE 0xFFFF8000 +#define R_008C74_SQ_FBUF_RING_SIZE 0x008C74 +#define S_008C74_MEM_SIZE(x) (((x) & 0xFFFFFFFF) << 0) +#define G_008C74_MEM_SIZE(x) (((x) >> 0) & 0xFFFFFFFF) +#define C_008C74_MEM_SIZE 0x00000000 +#define R_0288B4_SQ_GSTMP_RING_ITEMSIZE 0x0288B4 +#define S_0288B4_ITEMSIZE(x) (((x) & 0x7FFF) << 0) +#define G_0288B4_ITEMSIZE(x) (((x) >> 0) & 0x7FFF) +#define C_0288B4_ITEMSIZE 0xFFFF8000 +#define R_008C5C_SQ_GSTMP_RING_SIZE 0x008C5C +#define S_008C5C_MEM_SIZE(x) (((x) & 0xFFFFFFFF) << 0) +#define G_008C5C_MEM_SIZE(x) (((x) >> 0) & 0xFFFFFFFF) +#define C_008C5C_MEM_SIZE 0x00000000 +#define R_0288AC_SQ_GSVS_RING_ITEMSIZE 0x0288AC +#define S_0288AC_ITEMSIZE(x) (((x) & 0x7FFF) << 0) +#define G_0288AC_ITEMSIZE(x) (((x) >> 0) & 0x7FFF) +#define C_0288AC_ITEMSIZE 0xFFFF8000 +#define R_008C4C_SQ_GSVS_RING_SIZE 0x008C4C +#define S_008C4C_MEM_SIZE(x) (((x) & 0xFFFFFFFF) << 0) +#define G_008C4C_MEM_SIZE(x) (((x) >> 0) & 0xFFFFFFFF) +#define C_008C4C_MEM_SIZE 0x00000000 +#define R_0288BC_SQ_PSTMP_RING_ITEMSIZE 0x0288BC +#define S_0288BC_ITEMSIZE(x) (((x) & 0x7FFF) << 0) +#define G_0288BC_ITEMSIZE(x) (((x) >> 0) & 0x7FFF) +#define C_0288BC_ITEMSIZE 0xFFFF8000 +#define R_008C6C_SQ_PSTMP_RING_SIZE 0x008C6C +#define S_008C6C_MEM_SIZE(x) (((x) & 0xFFFFFFFF) << 0) +#define G_008C6C_MEM_SIZE(x) (((x) >> 0) & 0xFFFFFFFF) +#define C_008C6C_MEM_SIZE 0x00000000 +#define R_0288C4_SQ_REDUC_RING_ITEMSIZE 0x0288C4 +#define S_0288C4_ITEMSIZE(x) (((x) & 0x7FFF) << 0) +#define G_0288C4_ITEMSIZE(x) (((x) >> 0) & 0x7FFF) +#define C_0288C4_ITEMSIZE 0xFFFF8000 +#define R_008C7C_SQ_REDUC_RING_SIZE 0x008C7C +#define S_008C7C_MEM_SIZE(x) (((x) & 0xFFFFFFFF) << 0) +#define G_008C7C_MEM_SIZE(x) (((x) >> 0) & 0xFFFFFFFF) +#define C_008C7C_MEM_SIZE 0x00000000 +#define R_0288B8_SQ_VSTMP_RING_ITEMSIZE 0x0288B8 +#define S_0288B8_ITEMSIZE(x) (((x) & 0x7FFF) << 0) +#define G_0288B8_ITEMSIZE(x) (((x) >> 0) & 0x7FFF) +#define C_0288B8_ITEMSIZE 0xFFFF8000 +#define R_008C64_SQ_VSTMP_RING_SIZE 0x008C64 +#define S_008C64_MEM_SIZE(x) (((x) & 0xFFFFFFFF) << 0) +#define G_008C64_MEM_SIZE(x) (((x) >> 0) & 0xFFFFFFFF) +#define C_008C64_MEM_SIZE 0x00000000 +#define R_0288C8_SQ_GS_VERT_ITEMSIZE 0x0288C8 +#define S_0288C8_ITEMSIZE(x) (((x) & 0x7FFF) << 0) +#define G_0288C8_ITEMSIZE(x) (((x) >> 0) & 0x7FFF) +#define C_0288C8_ITEMSIZE 0xFFFF8000 +#define R_028010_DB_DEPTH_INFO 0x028010 +#define S_028010_FORMAT(x) (((x) & 0x7) << 0) +#define G_028010_FORMAT(x) (((x) >> 0) & 0x7) +#define C_028010_FORMAT 0xFFFFFFF8 +#define V_028010_DEPTH_INVALID 0x00000000 +#define V_028010_DEPTH_16 0x00000001 +#define V_028010_DEPTH_X8_24 0x00000002 +#define V_028010_DEPTH_8_24 0x00000003 +#define V_028010_DEPTH_X8_24_FLOAT 0x00000004 +#define V_028010_DEPTH_8_24_FLOAT 0x00000005 +#define V_028010_DEPTH_32_FLOAT 0x00000006 +#define V_028010_DEPTH_X24_8_32_FLOAT 0x00000007 +#define S_028010_READ_SIZE(x) (((x) & 0x1) << 3) +#define G_028010_READ_SIZE(x) (((x) >> 3) & 0x1) +#define C_028010_READ_SIZE 0xFFFFFFF7 +#define S_028010_ARRAY_MODE(x) (((x) & 0xF) << 15) +#define G_028010_ARRAY_MODE(x) (((x) >> 15) & 0xF) +#define C_028010_ARRAY_MODE 0xFFF87FFF +#define V_028010_ARRAY_1D_TILED_THIN1 0x00000002 +#define V_028010_ARRAY_2D_TILED_THIN1 0x00000004 +#define S_028010_TILE_SURFACE_ENABLE(x) (((x) & 0x1) << 25) +#define G_028010_TILE_SURFACE_ENABLE(x) (((x) >> 25) & 0x1) +#define C_028010_TILE_SURFACE_ENABLE 0xFDFFFFFF +#define S_028010_TILE_COMPACT(x) (((x) & 0x1) << 26) +#define G_028010_TILE_COMPACT(x) (((x) >> 26) & 0x1) +#define C_028010_TILE_COMPACT 0xFBFFFFFF +#define S_028010_ZRANGE_PRECISION(x) (((x) & 0x1) << 31) +#define G_028010_ZRANGE_PRECISION(x) (((x) >> 31) & 0x1) +#define C_028010_ZRANGE_PRECISION 0x7FFFFFFF +#define R_028000_DB_DEPTH_SIZE 0x028000 +#define S_028000_PITCH_TILE_MAX(x) (((x) & 0x3FF) << 0) +#define G_028000_PITCH_TILE_MAX(x) (((x) >> 0) & 0x3FF) +#define C_028000_PITCH_TILE_MAX 0xFFFFFC00 +#define S_028000_SLICE_TILE_MAX(x) (((x) & 0xFFFFF) << 10) +#define G_028000_SLICE_TILE_MAX(x) (((x) >> 10) & 0xFFFFF) +#define C_028000_SLICE_TILE_MAX 0xC00003FF +#define R_028004_DB_DEPTH_VIEW 0x028004 +#define S_028004_SLICE_START(x) (((x) & 0x7FF) << 0) +#define G_028004_SLICE_START(x) (((x) >> 0) & 0x7FF) +#define C_028004_SLICE_START 0xFFFFF800 +#define S_028004_SLICE_MAX(x) (((x) & 0x7FF) << 13) +#define G_028004_SLICE_MAX(x) (((x) >> 13) & 0x7FF) +#define C_028004_SLICE_MAX 0xFF001FFF +#define R_028800_DB_DEPTH_CONTROL 0x028800 +#define S_028800_STENCIL_ENABLE(x) (((x) & 0x1) << 0) +#define G_028800_STENCIL_ENABLE(x) (((x) >> 0) & 0x1) +#define C_028800_STENCIL_ENABLE 0xFFFFFFFE +#define S_028800_Z_ENABLE(x) (((x) & 0x1) << 1) +#define G_028800_Z_ENABLE(x) (((x) >> 1) & 0x1) +#define C_028800_Z_ENABLE 0xFFFFFFFD +#define S_028800_Z_WRITE_ENABLE(x) (((x) & 0x1) << 2) +#define G_028800_Z_WRITE_ENABLE(x) (((x) >> 2) & 0x1) +#define C_028800_Z_WRITE_ENABLE 0xFFFFFFFB +#define S_028800_ZFUNC(x) (((x) & 0x7) << 4) +#define G_028800_ZFUNC(x) (((x) >> 4) & 0x7) +#define C_028800_ZFUNC 0xFFFFFF8F +#define S_028800_BACKFACE_ENABLE(x) (((x) & 0x1) << 7) +#define G_028800_BACKFACE_ENABLE(x) (((x) >> 7) & 0x1) +#define C_028800_BACKFACE_ENABLE 0xFFFFFF7F +#define S_028800_STENCILFUNC(x) (((x) & 0x7) << 8) +#define G_028800_STENCILFUNC(x) (((x) >> 8) & 0x7) +#define C_028800_STENCILFUNC 0xFFFFF8FF +#define S_028800_STENCILFAIL(x) (((x) & 0x7) << 11) +#define G_028800_STENCILFAIL(x) (((x) >> 11) & 0x7) +#define C_028800_STENCILFAIL 0xFFFFC7FF +#define S_028800_STENCILZPASS(x) (((x) & 0x7) << 14) +#define G_028800_STENCILZPASS(x) (((x) >> 14) & 0x7) +#define C_028800_STENCILZPASS 0xFFFE3FFF +#define S_028800_STENCILZFAIL(x) (((x) & 0x7) << 17) +#define G_028800_STENCILZFAIL(x) (((x) >> 17) & 0x7) +#define C_028800_STENCILZFAIL 0xFFF1FFFF +#define S_028800_STENCILFUNC_BF(x) (((x) & 0x7) << 20) +#define G_028800_STENCILFUNC_BF(x) (((x) >> 20) & 0x7) +#define C_028800_STENCILFUNC_BF 0xFF8FFFFF +#define S_028800_STENCILFAIL_BF(x) (((x) & 0x7) << 23) +#define G_028800_STENCILFAIL_BF(x) (((x) >> 23) & 0x7) +#define C_028800_STENCILFAIL_BF 0xFC7FFFFF +#define S_028800_STENCILZPASS_BF(x) (((x) & 0x7) << 26) +#define G_028800_STENCILZPASS_BF(x) (((x) >> 26) & 0x7) +#define C_028800_STENCILZPASS_BF 0xE3FFFFFF +#define S_028800_STENCILZFAIL_BF(x) (((x) & 0x7) << 29) +#define G_028800_STENCILZFAIL_BF(x) (((x) >> 29) & 0x7) +#define C_028800_STENCILZFAIL_BF 0x1FFFFFFF + +#endif diff --git a/sys/dev/drm2/radeon/radeon.h b/sys/dev/drm2/radeon/radeon.h new file mode 100644 index 00000000000..88a5c5a37f6 --- /dev/null +++ b/sys/dev/drm2/radeon/radeon.h @@ -0,0 +1,2050 @@ +/* + * Copyright 2008 Advanced Micro Devices, Inc. + * Copyright 2008 Red Hat Inc. + * Copyright 2009 Jerome Glisse. + * + * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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. + * + * Authors: Dave Airlie + * Alex Deucher + * Jerome Glisse + */ + +#include +__FBSDID("$FreeBSD$"); + +#ifndef __RADEON_H__ +#define __RADEON_H__ + +/* TODO: Here are things that needs to be done : + * - surface allocator & initializer : (bit like scratch reg) should + * initialize HDP_ stuff on RS600, R600, R700 hw, well anythings + * related to surface + * - WB : write back stuff (do it bit like scratch reg things) + * - Vblank : look at Jesse's rework and what we should do + * - r600/r700: gart & cp + * - cs : clean cs ioctl use bitmap & things like that. + * - power management stuff + * - Barrier in gart code + * - Unmappabled vram ? + * - TESTING, TESTING, TESTING + */ + +/* Initialization path: + * We expect that acceleration initialization might fail for various + * reasons even thought we work hard to make it works on most + * configurations. In order to still have a working userspace in such + * situation the init path must succeed up to the memory controller + * initialization point. Failure before this point are considered as + * fatal error. Here is the init callchain : + * radeon_device_init perform common structure, mutex initialization + * asic_init setup the GPU memory layout and perform all + * one time initialization (failure in this + * function are considered fatal) + * asic_startup setup the GPU acceleration, in order to + * follow guideline the first thing this + * function should do is setting the GPU + * memory controller (only MC setup failure + * are considered as fatal) + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +#include "radeon_family.h" +#include "radeon_mode.h" +#include "radeon_reg.h" + +/* + * Modules parameters. + */ +extern int radeon_no_wb; +extern int radeon_modeset; +extern int radeon_dynclks; +extern int radeon_r4xx_atom; +extern int radeon_agpmode; +extern int radeon_vram_limit; +extern int radeon_gart_size; +extern int radeon_benchmarking; +extern int radeon_testing; +extern int radeon_connector_table; +extern int radeon_tv; +extern int radeon_audio; +extern int radeon_disp_priority; +extern int radeon_hw_i2c; +extern int radeon_pcie_gen2; +extern int radeon_msi; +extern int radeon_lockup_timeout; + +/* + * Copy from radeon_drv.h so we don't have to include both and have conflicting + * symbol; + */ +#define RADEON_MAX_USEC_TIMEOUT 100000 /* 100 ms */ +#define RADEON_FENCE_JIFFIES_TIMEOUT (DRM_HZ / 2) +/* RADEON_IB_POOL_SIZE must be a power of 2 */ +#define RADEON_IB_POOL_SIZE 16 +#define RADEON_DEBUGFS_MAX_COMPONENTS 32 +#define RADEONFB_CONN_LIMIT 4 +#define RADEON_BIOS_NUM_SCRATCH 8 + +/* max number of rings */ +#define RADEON_NUM_RINGS 5 + +/* fence seq are set to this number when signaled */ +#define RADEON_FENCE_SIGNALED_SEQ 0LL + +/* internal ring indices */ +/* r1xx+ has gfx CP ring */ +#define RADEON_RING_TYPE_GFX_INDEX 0 + +/* cayman has 2 compute CP rings */ +#define CAYMAN_RING_TYPE_CP1_INDEX 1 +#define CAYMAN_RING_TYPE_CP2_INDEX 2 + +/* R600+ has an async dma ring */ +#define R600_RING_TYPE_DMA_INDEX 3 +/* cayman add a second async dma ring */ +#define CAYMAN_RING_TYPE_DMA1_INDEX 4 + +/* hardcode those limit for now */ +#define RADEON_VA_IB_OFFSET (1 << 20) +#define RADEON_VA_RESERVED_SIZE (8 << 20) +#define RADEON_IB_VM_MAX_SIZE (64 << 10) + +/* reset flags */ +#define RADEON_RESET_GFX (1 << 0) +#define RADEON_RESET_COMPUTE (1 << 1) +#define RADEON_RESET_DMA (1 << 2) + +/* + * Errata workarounds. + */ +enum radeon_pll_errata { + CHIP_ERRATA_R300_CG = 0x00000001, + CHIP_ERRATA_PLL_DUMMYREADS = 0x00000002, + CHIP_ERRATA_PLL_DELAY = 0x00000004 +}; + + +struct radeon_device; + + +/* + * BIOS. + */ +bool radeon_get_bios(struct radeon_device *rdev); + +/* + * Dummy page + */ +struct radeon_dummy_page { + drm_dma_handle_t *dmah; + dma_addr_t addr; +}; +int radeon_dummy_page_init(struct radeon_device *rdev); +void radeon_dummy_page_fini(struct radeon_device *rdev); + + +/* + * Clocks + */ +struct radeon_clock { + struct radeon_pll p1pll; + struct radeon_pll p2pll; + struct radeon_pll dcpll; + struct radeon_pll spll; + struct radeon_pll mpll; + /* 10 Khz units */ + uint32_t default_mclk; + uint32_t default_sclk; + uint32_t default_dispclk; + uint32_t dp_extclk; + uint32_t max_pixel_clock; +}; + +/* + * Power management + */ +int radeon_pm_init(struct radeon_device *rdev); +void radeon_pm_fini(struct radeon_device *rdev); +void radeon_pm_compute_clocks(struct radeon_device *rdev); +void radeon_pm_suspend(struct radeon_device *rdev); +void radeon_pm_resume(struct radeon_device *rdev); +void radeon_combios_get_power_modes(struct radeon_device *rdev); +void radeon_atombios_get_power_modes(struct radeon_device *rdev); +void radeon_atom_set_voltage(struct radeon_device *rdev, u16 voltage_level, u8 voltage_type); +void rs690_pm_info(struct radeon_device *rdev); +extern int rv6xx_get_temp(struct radeon_device *rdev); +extern int rv770_get_temp(struct radeon_device *rdev); +extern int evergreen_get_temp(struct radeon_device *rdev); +extern int sumo_get_temp(struct radeon_device *rdev); +extern int si_get_temp(struct radeon_device *rdev); +extern void evergreen_tiling_fields(unsigned tiling_flags, unsigned *bankw, + unsigned *bankh, unsigned *mtaspect, + unsigned *tile_split); + +/* + * Fences. + */ +struct radeon_fence_driver { + uint32_t scratch_reg; + uint64_t gpu_addr; + volatile uint32_t *cpu_addr; + /* sync_seq is protected by ring emission lock */ + uint64_t sync_seq[RADEON_NUM_RINGS]; + atomic64_t last_seq; + unsigned long last_activity; + bool initialized; +}; + +struct radeon_fence { + struct radeon_device *rdev; + unsigned int kref; + /* protected by radeon_fence.lock */ + uint64_t seq; + /* RB, DMA, etc. */ + unsigned ring; +}; + +int radeon_fence_driver_start_ring(struct radeon_device *rdev, int ring); +int radeon_fence_driver_init(struct radeon_device *rdev); +void radeon_fence_driver_fini(struct radeon_device *rdev); +void radeon_fence_driver_force_completion(struct radeon_device *rdev); +int radeon_fence_emit(struct radeon_device *rdev, struct radeon_fence **fence, int ring); +void radeon_fence_process(struct radeon_device *rdev, int ring); +bool radeon_fence_signaled(struct radeon_fence *fence); +int radeon_fence_wait(struct radeon_fence *fence, bool interruptible); +int radeon_fence_wait_next_locked(struct radeon_device *rdev, int ring); +int radeon_fence_wait_empty_locked(struct radeon_device *rdev, int ring); +int radeon_fence_wait_any(struct radeon_device *rdev, + struct radeon_fence **fences, + bool intr); +struct radeon_fence *radeon_fence_ref(struct radeon_fence *fence); +void radeon_fence_unref(struct radeon_fence **fence); +unsigned radeon_fence_count_emitted(struct radeon_device *rdev, int ring); +bool radeon_fence_need_sync(struct radeon_fence *fence, int ring); +void radeon_fence_note_sync(struct radeon_fence *fence, int ring); +static inline struct radeon_fence *radeon_fence_later(struct radeon_fence *a, + struct radeon_fence *b) +{ + if (!a) { + return b; + } + + if (!b) { + return a; + } + + KASSERT(a->ring == b->ring, ("\"a\" and \"b\" belongs to different rings")); + + if (a->seq > b->seq) { + return a; + } else { + return b; + } +} + +static inline bool radeon_fence_is_earlier(struct radeon_fence *a, + struct radeon_fence *b) +{ + if (!a) { + return false; + } + + if (!b) { + return true; + } + + KASSERT(a->ring == b->ring, ("\"a\" and \"b\" belongs to different rings")); + + return a->seq < b->seq; +} + +/* + * Tiling registers + */ +struct radeon_surface_reg { + struct radeon_bo *bo; +}; + +#define RADEON_GEM_MAX_SURFACES 8 + +/* + * TTM. + */ +struct radeon_mman { + struct ttm_bo_global_ref bo_global_ref; + struct drm_global_reference mem_global_ref; + struct ttm_bo_device bdev; + bool mem_global_referenced; + bool initialized; +}; + +/* bo virtual address in a specific vm */ +struct radeon_bo_va { + /* protected by bo being reserved */ + struct list_head bo_list; + uint64_t soffset; + uint64_t eoffset; + uint32_t flags; + bool valid; + unsigned ref_count; + + /* protected by vm mutex */ + struct list_head vm_list; + + /* constant after initialization */ + struct radeon_vm *vm; + struct radeon_bo *bo; +}; + +struct radeon_bo { + /* Protected by gem.mutex */ + struct list_head list; + /* Protected by tbo.reserved */ + u32 placements[3]; + struct ttm_placement placement; + struct ttm_buffer_object tbo; + struct ttm_bo_kmap_obj kmap; + unsigned pin_count; + void *kptr; + u32 tiling_flags; + u32 pitch; + int surface_reg; + /* list of all virtual address to which this bo + * is associated to + */ + struct list_head va; + /* Constant after initialization */ + struct radeon_device *rdev; + struct drm_gem_object gem_base; + + struct ttm_bo_kmap_obj dma_buf_vmap; + int vmapping_count; +}; +#define gem_to_radeon_bo(gobj) container_of((gobj), struct radeon_bo, gem_base) + +struct radeon_bo_list { + struct ttm_validate_buffer tv; + struct radeon_bo *bo; + uint64_t gpu_offset; + unsigned rdomain; + unsigned wdomain; + u32 tiling_flags; +}; + +/* sub-allocation manager, it has to be protected by another lock. + * By conception this is an helper for other part of the driver + * like the indirect buffer or semaphore, which both have their + * locking. + * + * Principe is simple, we keep a list of sub allocation in offset + * order (first entry has offset == 0, last entry has the highest + * offset). + * + * When allocating new object we first check if there is room at + * the end total_size - (last_object_offset + last_object_size) >= + * alloc_size. If so we allocate new object there. + * + * When there is not enough room at the end, we start waiting for + * each sub object until we reach object_offset+object_size >= + * alloc_size, this object then become the sub object we return. + * + * Alignment can't be bigger than page size. + * + * Hole are not considered for allocation to keep things simple. + * Assumption is that there won't be hole (all object on same + * alignment). + */ +struct radeon_sa_manager { + struct cv wq; + struct sx wq_lock; + struct radeon_bo *bo; + struct list_head *hole; + struct list_head flist[RADEON_NUM_RINGS]; + struct list_head olist; + unsigned size; + uint64_t gpu_addr; + void *cpu_ptr; + uint32_t domain; +}; + +struct radeon_sa_bo; + +/* sub-allocation buffer */ +struct radeon_sa_bo { + struct list_head olist; + struct list_head flist; + struct radeon_sa_manager *manager; + unsigned soffset; + unsigned eoffset; + struct radeon_fence *fence; +}; + +/* + * GEM objects. + */ +struct radeon_gem { + struct sx mutex; + struct list_head objects; +}; + +int radeon_gem_init(struct radeon_device *rdev); +void radeon_gem_fini(struct radeon_device *rdev); +int radeon_gem_object_create(struct radeon_device *rdev, int size, + int alignment, int initial_domain, + bool discardable, bool kernel, + struct drm_gem_object **obj); + +int radeon_mode_dumb_create(struct drm_file *file_priv, + struct drm_device *dev, + struct drm_mode_create_dumb *args); +int radeon_mode_dumb_mmap(struct drm_file *filp, + struct drm_device *dev, + uint32_t handle, uint64_t *offset_p); +int radeon_mode_dumb_destroy(struct drm_file *file_priv, + struct drm_device *dev, + uint32_t handle); + +/* + * Semaphores. + */ +/* everything here is constant */ +struct radeon_semaphore { + struct radeon_sa_bo *sa_bo; + signed waiters; + uint64_t gpu_addr; +}; + +int radeon_semaphore_create(struct radeon_device *rdev, + struct radeon_semaphore **semaphore); +void radeon_semaphore_emit_signal(struct radeon_device *rdev, int ring, + struct radeon_semaphore *semaphore); +void radeon_semaphore_emit_wait(struct radeon_device *rdev, int ring, + struct radeon_semaphore *semaphore); +int radeon_semaphore_sync_rings(struct radeon_device *rdev, + struct radeon_semaphore *semaphore, + int signaler, int waiter); +void radeon_semaphore_free(struct radeon_device *rdev, + struct radeon_semaphore **semaphore, + struct radeon_fence *fence); + +/* + * GART structures, functions & helpers + */ +struct radeon_mc; + +#define RADEON_GPU_PAGE_SIZE 4096 +#define RADEON_GPU_PAGE_MASK (RADEON_GPU_PAGE_SIZE - 1) +#define RADEON_GPU_PAGE_SHIFT 12 +#define RADEON_GPU_PAGE_ALIGN(a) (((a) + RADEON_GPU_PAGE_MASK) & ~RADEON_GPU_PAGE_MASK) + +struct radeon_gart { + drm_dma_handle_t *dmah; + dma_addr_t table_addr; + struct radeon_bo *robj; + void *ptr; + unsigned num_gpu_pages; + unsigned num_cpu_pages; + unsigned table_size; + vm_page_t *pages; + dma_addr_t *pages_addr; + bool ready; +}; + +int radeon_gart_table_ram_alloc(struct radeon_device *rdev); +void radeon_gart_table_ram_free(struct radeon_device *rdev); +int radeon_gart_table_vram_alloc(struct radeon_device *rdev); +void radeon_gart_table_vram_free(struct radeon_device *rdev); +int radeon_gart_table_vram_pin(struct radeon_device *rdev); +void radeon_gart_table_vram_unpin(struct radeon_device *rdev); +int radeon_gart_init(struct radeon_device *rdev); +void radeon_gart_fini(struct radeon_device *rdev); +void radeon_gart_unbind(struct radeon_device *rdev, unsigned offset, + int pages); +int radeon_gart_bind(struct radeon_device *rdev, unsigned offset, + int pages, vm_page_t *pagelist, + dma_addr_t *dma_addr); +void radeon_gart_restore(struct radeon_device *rdev); + + +/* + * GPU MC structures, functions & helpers + */ +struct radeon_mc { + resource_size_t aper_size; + resource_size_t aper_base; + resource_size_t agp_base; + /* for some chips with <= 32MB we need to lie + * about vram size near mc fb location */ + u64 mc_vram_size; + u64 visible_vram_size; + u64 gtt_size; + u64 gtt_start; + u64 gtt_end; + u64 vram_start; + u64 vram_end; + unsigned vram_width; + u64 real_vram_size; + int vram_mtrr; + bool vram_is_ddr; + bool igp_sideport_enabled; + u64 gtt_base_align; +}; + +bool radeon_combios_sideport_present(struct radeon_device *rdev); +bool radeon_atombios_sideport_present(struct radeon_device *rdev); + +/* + * GPU scratch registers structures, functions & helpers + */ +struct radeon_scratch { + unsigned num_reg; + uint32_t reg_base; + bool free[32]; + uint32_t reg[32]; +}; + +int radeon_scratch_get(struct radeon_device *rdev, uint32_t *reg); +void radeon_scratch_free(struct radeon_device *rdev, uint32_t reg); + + +/* + * IRQS. + */ + +struct radeon_unpin_work { + struct task work; + struct radeon_device *rdev; + int crtc_id; + struct radeon_fence *fence; + struct drm_pending_vblank_event *event; + struct radeon_bo *old_rbo; + u64 new_crtc_base; +}; + +struct r500_irq_stat_regs { + u32 disp_int; + u32 hdmi0_status; +}; + +struct r600_irq_stat_regs { + u32 disp_int; + u32 disp_int_cont; + u32 disp_int_cont2; + u32 d1grph_int; + u32 d2grph_int; + u32 hdmi0_status; + u32 hdmi1_status; +}; + +struct evergreen_irq_stat_regs { + u32 disp_int; + u32 disp_int_cont; + u32 disp_int_cont2; + u32 disp_int_cont3; + u32 disp_int_cont4; + u32 disp_int_cont5; + u32 d1grph_int; + u32 d2grph_int; + u32 d3grph_int; + u32 d4grph_int; + u32 d5grph_int; + u32 d6grph_int; + u32 afmt_status1; + u32 afmt_status2; + u32 afmt_status3; + u32 afmt_status4; + u32 afmt_status5; + u32 afmt_status6; +}; + +union radeon_irq_stat_regs { + struct r500_irq_stat_regs r500; + struct r600_irq_stat_regs r600; + struct evergreen_irq_stat_regs evergreen; +}; + +#define RADEON_MAX_HPD_PINS 6 +#define RADEON_MAX_CRTCS 6 +#define RADEON_MAX_AFMT_BLOCKS 6 + +struct radeon_irq { + bool installed; + struct mtx lock; + atomic_t ring_int[RADEON_NUM_RINGS]; + bool crtc_vblank_int[RADEON_MAX_CRTCS]; + atomic_t pflip[RADEON_MAX_CRTCS]; + wait_queue_head_t vblank_queue; + bool hpd[RADEON_MAX_HPD_PINS]; + bool afmt[RADEON_MAX_AFMT_BLOCKS]; + union radeon_irq_stat_regs stat_regs; +}; + +int radeon_irq_kms_init(struct radeon_device *rdev); +void radeon_irq_kms_fini(struct radeon_device *rdev); +void radeon_irq_kms_sw_irq_get(struct radeon_device *rdev, int ring); +void radeon_irq_kms_sw_irq_put(struct radeon_device *rdev, int ring); +void radeon_irq_kms_pflip_irq_get(struct radeon_device *rdev, int crtc); +void radeon_irq_kms_pflip_irq_put(struct radeon_device *rdev, int crtc); +void radeon_irq_kms_enable_afmt(struct radeon_device *rdev, int block); +void radeon_irq_kms_disable_afmt(struct radeon_device *rdev, int block); +void radeon_irq_kms_enable_hpd(struct radeon_device *rdev, unsigned hpd_mask); +void radeon_irq_kms_disable_hpd(struct radeon_device *rdev, unsigned hpd_mask); + +/* + * CP & rings. + */ + +struct radeon_ib { + struct radeon_sa_bo *sa_bo; + uint32_t length_dw; + uint64_t gpu_addr; + uint32_t *ptr; + int ring; + struct radeon_fence *fence; + struct radeon_vm *vm; + bool is_const_ib; + struct radeon_fence *sync_to[RADEON_NUM_RINGS]; + struct radeon_semaphore *semaphore; +}; + +struct radeon_ring { + struct radeon_bo *ring_obj; + volatile uint32_t *ring; + unsigned rptr; + unsigned rptr_offs; + unsigned rptr_reg; + unsigned rptr_save_reg; + u64 next_rptr_gpu_addr; + volatile u32 *next_rptr_cpu_addr; + unsigned wptr; + unsigned wptr_old; + unsigned wptr_reg; + unsigned ring_size; + unsigned ring_free_dw; + int count_dw; + unsigned long last_activity; + unsigned last_rptr; + uint64_t gpu_addr; + uint32_t align_mask; + uint32_t ptr_mask; + bool ready; + u32 ptr_reg_shift; + u32 ptr_reg_mask; + u32 nop; + u32 idx; + u64 last_semaphore_signal_addr; + u64 last_semaphore_wait_addr; +}; + +/* + * VM + */ + +/* maximum number of VMIDs */ +#define RADEON_NUM_VM 16 + +/* defines number of bits in page table versus page directory, + * a page is 4KB so we have 12 bits offset, 9 bits in the page + * table and the remaining 19 bits are in the page directory */ +#define RADEON_VM_BLOCK_SIZE 9 + +/* number of entries in page table */ +#define RADEON_VM_PTE_COUNT (1 << RADEON_VM_BLOCK_SIZE) + +struct radeon_vm { + struct list_head list; + struct list_head va; + unsigned id; + + /* contains the page directory */ + struct radeon_sa_bo *page_directory; + uint64_t pd_gpu_addr; + + /* array of page tables, one for each page directory entry */ + struct radeon_sa_bo **page_tables; + + struct sx mutex; + /* last fence for cs using this vm */ + struct radeon_fence *fence; + /* last flush or NULL if we still need to flush */ + struct radeon_fence *last_flush; +}; + +struct radeon_vm_manager { + struct sx lock; + struct list_head lru_vm; + struct radeon_fence *active[RADEON_NUM_VM]; + struct radeon_sa_manager sa_manager; + uint32_t max_pfn; + /* number of VMIDs */ + unsigned nvm; + /* vram base address for page table entry */ + u64 vram_base_offset; + /* is vm enabled? */ + bool enabled; +}; + +/* + * file private structure + */ +struct radeon_fpriv { + struct radeon_vm vm; +}; + +/* + * R6xx+ IH ring + */ +struct r600_ih { + struct radeon_bo *ring_obj; + volatile uint32_t *ring; + unsigned rptr; + unsigned ring_size; + uint64_t gpu_addr; + uint32_t ptr_mask; + atomic_t lock; + bool enabled; +}; + +struct r600_blit_cp_primitives { + void (*set_render_target)(struct radeon_device *rdev, int format, + int w, int h, u64 gpu_addr); + void (*cp_set_surface_sync)(struct radeon_device *rdev, + u32 sync_type, u32 size, + u64 mc_addr); + void (*set_shaders)(struct radeon_device *rdev); + void (*set_vtx_resource)(struct radeon_device *rdev, u64 gpu_addr); + void (*set_tex_resource)(struct radeon_device *rdev, + int format, int w, int h, int pitch, + u64 gpu_addr, u32 size); + void (*set_scissors)(struct radeon_device *rdev, int x1, int y1, + int x2, int y2); + void (*draw_auto)(struct radeon_device *rdev); + void (*set_default_state)(struct radeon_device *rdev); +}; + +struct r600_blit { + struct radeon_bo *shader_obj; + struct r600_blit_cp_primitives primitives; + int max_dim; + int ring_size_common; + int ring_size_per_loop; + u64 shader_gpu_addr; + u32 vs_offset, ps_offset; + u32 state_offset; + u32 state_len; +}; + +/* + * SI RLC stuff + */ +struct si_rlc { + /* for power gating */ + struct radeon_bo *save_restore_obj; + uint64_t save_restore_gpu_addr; + /* for clear state */ + struct radeon_bo *clear_state_obj; + uint64_t clear_state_gpu_addr; +}; + +int radeon_ib_get(struct radeon_device *rdev, int ring, + struct radeon_ib *ib, struct radeon_vm *vm, + unsigned size); +void radeon_ib_free(struct radeon_device *rdev, struct radeon_ib *ib); +int radeon_ib_schedule(struct radeon_device *rdev, struct radeon_ib *ib, + struct radeon_ib *const_ib); +int radeon_ib_pool_init(struct radeon_device *rdev); +void radeon_ib_pool_fini(struct radeon_device *rdev); +int radeon_ib_ring_tests(struct radeon_device *rdev); +/* Ring access between begin & end cannot sleep */ +bool radeon_ring_supports_scratch_reg(struct radeon_device *rdev, + struct radeon_ring *ring); +void radeon_ring_free_size(struct radeon_device *rdev, struct radeon_ring *cp); +int radeon_ring_alloc(struct radeon_device *rdev, struct radeon_ring *cp, unsigned ndw); +int radeon_ring_lock(struct radeon_device *rdev, struct radeon_ring *cp, unsigned ndw); +void radeon_ring_commit(struct radeon_device *rdev, struct radeon_ring *cp); +void radeon_ring_unlock_commit(struct radeon_device *rdev, struct radeon_ring *cp); +void radeon_ring_undo(struct radeon_ring *ring); +void radeon_ring_unlock_undo(struct radeon_device *rdev, struct radeon_ring *cp); +int radeon_ring_test(struct radeon_device *rdev, struct radeon_ring *cp); +void radeon_ring_force_activity(struct radeon_device *rdev, struct radeon_ring *ring); +void radeon_ring_lockup_update(struct radeon_ring *ring); +bool radeon_ring_test_lockup(struct radeon_device *rdev, struct radeon_ring *ring); +unsigned radeon_ring_backup(struct radeon_device *rdev, struct radeon_ring *ring, + uint32_t **data); +int radeon_ring_restore(struct radeon_device *rdev, struct radeon_ring *ring, + unsigned size, uint32_t *data); +int radeon_ring_init(struct radeon_device *rdev, struct radeon_ring *cp, unsigned ring_size, + unsigned rptr_offs, unsigned rptr_reg, unsigned wptr_reg, + u32 ptr_reg_shift, u32 ptr_reg_mask, u32 nop); +void radeon_ring_fini(struct radeon_device *rdev, struct radeon_ring *cp); + + +/* r600 async dma */ +void r600_dma_stop(struct radeon_device *rdev); +int r600_dma_resume(struct radeon_device *rdev); +void r600_dma_fini(struct radeon_device *rdev); + +void cayman_dma_stop(struct radeon_device *rdev); +int cayman_dma_resume(struct radeon_device *rdev); +void cayman_dma_fini(struct radeon_device *rdev); + +/* + * CS. + */ +struct radeon_cs_reloc { + struct drm_gem_object *gobj; + struct radeon_bo *robj; + struct radeon_bo_list lobj; + uint32_t handle; + uint32_t flags; +}; + +struct radeon_cs_chunk { + uint32_t chunk_id; + uint32_t length_dw; + int kpage_idx[2]; + uint32_t *kpage[2]; + uint32_t *kdata; + void __user *user_ptr; + int last_copied_page; + int last_page_index; +}; + +struct radeon_cs_parser { + device_t dev; + struct radeon_device *rdev; + struct drm_file *filp; + /* chunks */ + unsigned nchunks; + struct radeon_cs_chunk *chunks; + uint64_t *chunks_array; + /* IB */ + unsigned idx; + /* relocations */ + unsigned nrelocs; + struct radeon_cs_reloc *relocs; + struct radeon_cs_reloc **relocs_ptr; + struct list_head validated; + unsigned dma_reloc_idx; + /* indices of various chunks */ + int chunk_ib_idx; + int chunk_relocs_idx; + int chunk_flags_idx; + int chunk_const_ib_idx; + struct radeon_ib ib; + struct radeon_ib const_ib; + void *track; + unsigned family; + int parser_error; + u32 cs_flags; + u32 ring; + s32 priority; +}; + +extern int radeon_cs_finish_pages(struct radeon_cs_parser *p); +extern u32 radeon_get_ib_value(struct radeon_cs_parser *p, int idx); + +struct radeon_cs_packet { + unsigned idx; + unsigned type; + unsigned reg; + unsigned opcode; + int count; + unsigned one_reg_wr; +}; + +typedef int (*radeon_packet0_check_t)(struct radeon_cs_parser *p, + struct radeon_cs_packet *pkt, + unsigned idx, unsigned reg); +typedef int (*radeon_packet3_check_t)(struct radeon_cs_parser *p, + struct radeon_cs_packet *pkt); + + +/* + * AGP + */ +int radeon_agp_init(struct radeon_device *rdev); +void radeon_agp_resume(struct radeon_device *rdev); +void radeon_agp_suspend(struct radeon_device *rdev); +void radeon_agp_fini(struct radeon_device *rdev); + + +/* + * Writeback + */ +struct radeon_wb { + struct radeon_bo *wb_obj; + volatile uint32_t *wb; + uint64_t gpu_addr; + bool enabled; + bool use_event; +}; + +#define RADEON_WB_SCRATCH_OFFSET 0 +#define RADEON_WB_RING0_NEXT_RPTR 256 +#define RADEON_WB_CP_RPTR_OFFSET 1024 +#define RADEON_WB_CP1_RPTR_OFFSET 1280 +#define RADEON_WB_CP2_RPTR_OFFSET 1536 +#define R600_WB_DMA_RPTR_OFFSET 1792 +#define R600_WB_IH_WPTR_OFFSET 2048 +#define CAYMAN_WB_DMA1_RPTR_OFFSET 2304 +#define R600_WB_EVENT_OFFSET 3072 + +/** + * struct radeon_pm - power management datas + * @max_bandwidth: maximum bandwidth the gpu has (MByte/s) + * @igp_sideport_mclk: sideport memory clock Mhz (rs690,rs740,rs780,rs880) + * @igp_system_mclk: system clock Mhz (rs690,rs740,rs780,rs880) + * @igp_ht_link_clk: ht link clock Mhz (rs690,rs740,rs780,rs880) + * @igp_ht_link_width: ht link width in bits (rs690,rs740,rs780,rs880) + * @k8_bandwidth: k8 bandwidth the gpu has (MByte/s) (IGP) + * @sideport_bandwidth: sideport bandwidth the gpu has (MByte/s) (IGP) + * @ht_bandwidth: ht bandwidth the gpu has (MByte/s) (IGP) + * @core_bandwidth: core GPU bandwidth the gpu has (MByte/s) (IGP) + * @sclk: GPU clock Mhz (core bandwidth depends of this clock) + * @needed_bandwidth: current bandwidth needs + * + * It keeps track of various data needed to take powermanagement decision. + * Bandwidth need is used to determine minimun clock of the GPU and memory. + * Equation between gpu/memory clock and available bandwidth is hw dependent + * (type of memory, bus size, efficiency, ...) + */ + +enum radeon_pm_method { + PM_METHOD_PROFILE, + PM_METHOD_DYNPM, +}; + +enum radeon_dynpm_state { + DYNPM_STATE_DISABLED, + DYNPM_STATE_MINIMUM, + DYNPM_STATE_PAUSED, + DYNPM_STATE_ACTIVE, + DYNPM_STATE_SUSPENDED, +}; +enum radeon_dynpm_action { + DYNPM_ACTION_NONE, + DYNPM_ACTION_MINIMUM, + DYNPM_ACTION_DOWNCLOCK, + DYNPM_ACTION_UPCLOCK, + DYNPM_ACTION_DEFAULT +}; + +enum radeon_voltage_type { + VOLTAGE_NONE = 0, + VOLTAGE_GPIO, + VOLTAGE_VDDC, + VOLTAGE_SW +}; + +enum radeon_pm_state_type { + POWER_STATE_TYPE_DEFAULT, + POWER_STATE_TYPE_POWERSAVE, + POWER_STATE_TYPE_BATTERY, + POWER_STATE_TYPE_BALANCED, + POWER_STATE_TYPE_PERFORMANCE, +}; + +enum radeon_pm_profile_type { + PM_PROFILE_DEFAULT, + PM_PROFILE_AUTO, + PM_PROFILE_LOW, + PM_PROFILE_MID, + PM_PROFILE_HIGH, +}; + +#define PM_PROFILE_DEFAULT_IDX 0 +#define PM_PROFILE_LOW_SH_IDX 1 +#define PM_PROFILE_MID_SH_IDX 2 +#define PM_PROFILE_HIGH_SH_IDX 3 +#define PM_PROFILE_LOW_MH_IDX 4 +#define PM_PROFILE_MID_MH_IDX 5 +#define PM_PROFILE_HIGH_MH_IDX 6 +#define PM_PROFILE_MAX 7 + +struct radeon_pm_profile { + int dpms_off_ps_idx; + int dpms_on_ps_idx; + int dpms_off_cm_idx; + int dpms_on_cm_idx; +}; + +enum radeon_int_thermal_type { + THERMAL_TYPE_NONE, + THERMAL_TYPE_RV6XX, + THERMAL_TYPE_RV770, + THERMAL_TYPE_EVERGREEN, + THERMAL_TYPE_SUMO, + THERMAL_TYPE_NI, + THERMAL_TYPE_SI, +}; + +struct radeon_voltage { + enum radeon_voltage_type type; + /* gpio voltage */ + struct radeon_gpio_rec gpio; + u32 delay; /* delay in usec from voltage drop to sclk change */ + bool active_high; /* voltage drop is active when bit is high */ + /* VDDC voltage */ + u8 vddc_id; /* index into vddc voltage table */ + u8 vddci_id; /* index into vddci voltage table */ + bool vddci_enabled; + /* r6xx+ sw */ + u16 voltage; + /* evergreen+ vddci */ + u16 vddci; +}; + +/* clock mode flags */ +#define RADEON_PM_MODE_NO_DISPLAY (1 << 0) + +struct radeon_pm_clock_info { + /* memory clock */ + u32 mclk; + /* engine clock */ + u32 sclk; + /* voltage info */ + struct radeon_voltage voltage; + /* standardized clock flags */ + u32 flags; +}; + +/* state flags */ +#define RADEON_PM_STATE_SINGLE_DISPLAY_ONLY (1 << 0) + +struct radeon_power_state { + enum radeon_pm_state_type type; + struct radeon_pm_clock_info *clock_info; + /* number of valid clock modes in this power state */ + int num_clock_modes; + struct radeon_pm_clock_info *default_clock_mode; + /* standardized state flags */ + u32 flags; + u32 misc; /* vbios specific flags */ + u32 misc2; /* vbios specific flags */ + int pcie_lanes; /* pcie lanes */ +}; + +/* + * Some modes are overclocked by very low value, accept them + */ +#define RADEON_MODE_OVERCLOCK_MARGIN 500 /* 5 MHz */ + +struct radeon_pm { + struct sx mutex; + /* write locked while reprogramming mclk */ + struct sx mclk_lock; + u32 active_crtcs; + int active_crtc_count; + int req_vblank; + bool vblank_sync; + fixed20_12 max_bandwidth; + fixed20_12 igp_sideport_mclk; + fixed20_12 igp_system_mclk; + fixed20_12 igp_ht_link_clk; + fixed20_12 igp_ht_link_width; + fixed20_12 k8_bandwidth; + fixed20_12 sideport_bandwidth; + fixed20_12 ht_bandwidth; + fixed20_12 core_bandwidth; + fixed20_12 sclk; + fixed20_12 mclk; + fixed20_12 needed_bandwidth; + struct radeon_power_state *power_state; + /* number of valid power states */ + int num_power_states; + int current_power_state_index; + int current_clock_mode_index; + int requested_power_state_index; + int requested_clock_mode_index; + int default_power_state_index; + u32 current_sclk; + u32 current_mclk; + u16 current_vddc; + u16 current_vddci; + u32 default_sclk; + u32 default_mclk; + u16 default_vddc; + u16 default_vddci; + struct radeon_i2c_chan *i2c_bus; + /* selected pm method */ + enum radeon_pm_method pm_method; + /* dynpm power management */ +#ifdef DUMBBELL_WIP + struct delayed_work dynpm_idle_work; +#endif /* DUMBBELL_WIP */ + enum radeon_dynpm_state dynpm_state; + enum radeon_dynpm_action dynpm_planned_action; + unsigned long dynpm_action_timeout; + bool dynpm_can_upclock; + bool dynpm_can_downclock; + /* profile-based power management */ + enum radeon_pm_profile_type profile; + int profile_index; + struct radeon_pm_profile profiles[PM_PROFILE_MAX]; + /* internal thermal controller on rv6xx+ */ + enum radeon_int_thermal_type int_thermal_type; +#ifdef DUMBBELL_WIP + struct device *int_hwmon_dev; +#endif /* DUMBBELL_WIP */ +}; + +int radeon_pm_get_type_index(struct radeon_device *rdev, + enum radeon_pm_state_type ps_type, + int instance); + +struct r600_audio { + int channels; + int rate; + int bits_per_sample; + u8 status_bits; + u8 category_code; +}; + +/* + * Benchmarking + */ +void radeon_benchmark(struct radeon_device *rdev, int test_number); + + +/* + * Testing + */ +void radeon_test_moves(struct radeon_device *rdev); +void radeon_test_ring_sync(struct radeon_device *rdev, + struct radeon_ring *cpA, + struct radeon_ring *cpB); +void radeon_test_syncing(struct radeon_device *rdev); + + +/* + * Debugfs + */ +struct radeon_debugfs { + struct drm_info_list *files; + unsigned num_files; +}; + +int radeon_debugfs_add_files(struct radeon_device *rdev, + struct drm_info_list *files, + unsigned nfiles); +int radeon_debugfs_fence_init(struct radeon_device *rdev); + + +/* + * ASIC specific functions. + */ +struct radeon_asic { + int (*init)(struct radeon_device *rdev); + void (*fini)(struct radeon_device *rdev); + int (*resume)(struct radeon_device *rdev); + int (*suspend)(struct radeon_device *rdev); + void (*vga_set_state)(struct radeon_device *rdev, bool state); + int (*asic_reset)(struct radeon_device *rdev); + /* ioctl hw specific callback. Some hw might want to perform special + * operation on specific ioctl. For instance on wait idle some hw + * might want to perform and HDP flush through MMIO as it seems that + * some R6XX/R7XX hw doesn't take HDP flush into account if programmed + * through ring. + */ + void (*ioctl_wait_idle)(struct radeon_device *rdev, struct radeon_bo *bo); + /* check if 3D engine is idle */ + bool (*gui_idle)(struct radeon_device *rdev); + /* wait for mc_idle */ + int (*mc_wait_for_idle)(struct radeon_device *rdev); + /* gart */ + struct { + void (*tlb_flush)(struct radeon_device *rdev); + int (*set_page)(struct radeon_device *rdev, int i, uint64_t addr); + } gart; + struct { + int (*init)(struct radeon_device *rdev); + void (*fini)(struct radeon_device *rdev); + + u32 pt_ring_index; + void (*set_page)(struct radeon_device *rdev, uint64_t pe, + uint64_t addr, unsigned count, + uint32_t incr, uint32_t flags); + } vm; + /* ring specific callbacks */ + struct { + void (*ib_execute)(struct radeon_device *rdev, struct radeon_ib *ib); + int (*ib_parse)(struct radeon_device *rdev, struct radeon_ib *ib); + void (*emit_fence)(struct radeon_device *rdev, struct radeon_fence *fence); + void (*emit_semaphore)(struct radeon_device *rdev, struct radeon_ring *cp, + struct radeon_semaphore *semaphore, bool emit_wait); + int (*cs_parse)(struct radeon_cs_parser *p); + void (*ring_start)(struct radeon_device *rdev, struct radeon_ring *cp); + int (*ring_test)(struct radeon_device *rdev, struct radeon_ring *cp); + int (*ib_test)(struct radeon_device *rdev, struct radeon_ring *cp); + bool (*is_lockup)(struct radeon_device *rdev, struct radeon_ring *cp); + void (*vm_flush)(struct radeon_device *rdev, int ridx, struct radeon_vm *vm); + } ring[RADEON_NUM_RINGS]; + /* irqs */ + struct { + int (*set)(struct radeon_device *rdev); + irqreturn_t (*process)(struct radeon_device *rdev); + } irq; + /* displays */ + struct { + /* display watermarks */ + void (*bandwidth_update)(struct radeon_device *rdev); + /* get frame count */ + u32 (*get_vblank_counter)(struct radeon_device *rdev, int crtc); + /* wait for vblank */ + void (*wait_for_vblank)(struct radeon_device *rdev, int crtc); + /* set backlight level */ + void (*set_backlight_level)(struct radeon_encoder *radeon_encoder, u8 level); + /* get backlight level */ + u8 (*get_backlight_level)(struct radeon_encoder *radeon_encoder); + } display; + /* copy functions for bo handling */ + struct { + int (*blit)(struct radeon_device *rdev, + uint64_t src_offset, + uint64_t dst_offset, + unsigned num_gpu_pages, + struct radeon_fence **fence); + u32 blit_ring_index; + int (*dma)(struct radeon_device *rdev, + uint64_t src_offset, + uint64_t dst_offset, + unsigned num_gpu_pages, + struct radeon_fence **fence); + u32 dma_ring_index; + /* method used for bo copy */ + int (*copy)(struct radeon_device *rdev, + uint64_t src_offset, + uint64_t dst_offset, + unsigned num_gpu_pages, + struct radeon_fence **fence); + /* ring used for bo copies */ + u32 copy_ring_index; + } copy; + /* surfaces */ + struct { + int (*set_reg)(struct radeon_device *rdev, int reg, + uint32_t tiling_flags, uint32_t pitch, + uint32_t offset, uint32_t obj_size); + void (*clear_reg)(struct radeon_device *rdev, int reg); + } surface; + /* hotplug detect */ + struct { + void (*init)(struct radeon_device *rdev); + void (*fini)(struct radeon_device *rdev); + bool (*sense)(struct radeon_device *rdev, enum radeon_hpd_id hpd); + void (*set_polarity)(struct radeon_device *rdev, enum radeon_hpd_id hpd); + } hpd; + /* power management */ + struct { + void (*misc)(struct radeon_device *rdev); + void (*prepare)(struct radeon_device *rdev); + void (*finish)(struct radeon_device *rdev); + void (*init_profile)(struct radeon_device *rdev); + void (*get_dynpm_state)(struct radeon_device *rdev); + uint32_t (*get_engine_clock)(struct radeon_device *rdev); + void (*set_engine_clock)(struct radeon_device *rdev, uint32_t eng_clock); + uint32_t (*get_memory_clock)(struct radeon_device *rdev); + void (*set_memory_clock)(struct radeon_device *rdev, uint32_t mem_clock); + int (*get_pcie_lanes)(struct radeon_device *rdev); + void (*set_pcie_lanes)(struct radeon_device *rdev, int lanes); + void (*set_clock_gating)(struct radeon_device *rdev, int enable); + } pm; + /* pageflipping */ + struct { + void (*pre_page_flip)(struct radeon_device *rdev, int crtc); + u32 (*page_flip)(struct radeon_device *rdev, int crtc, u64 crtc_base); + void (*post_page_flip)(struct radeon_device *rdev, int crtc); + } pflip; +}; + +/* + * Asic structures + */ +struct r100_asic { + const unsigned *reg_safe_bm; + unsigned reg_safe_bm_size; + u32 hdp_cntl; +}; + +struct r300_asic { + const unsigned *reg_safe_bm; + unsigned reg_safe_bm_size; + u32 resync_scratch; + u32 hdp_cntl; +}; + +struct r600_asic { + unsigned max_pipes; + unsigned max_tile_pipes; + unsigned max_simds; + unsigned max_backends; + unsigned max_gprs; + unsigned max_threads; + unsigned max_stack_entries; + unsigned max_hw_contexts; + unsigned max_gs_threads; + unsigned sx_max_export_size; + unsigned sx_max_export_pos_size; + unsigned sx_max_export_smx_size; + unsigned sq_num_cf_insts; + unsigned tiling_nbanks; + unsigned tiling_npipes; + unsigned tiling_group_size; + unsigned tile_config; + unsigned backend_map; +}; + +struct rv770_asic { + unsigned max_pipes; + unsigned max_tile_pipes; + unsigned max_simds; + unsigned max_backends; + unsigned max_gprs; + unsigned max_threads; + unsigned max_stack_entries; + unsigned max_hw_contexts; + unsigned max_gs_threads; + unsigned sx_max_export_size; + unsigned sx_max_export_pos_size; + unsigned sx_max_export_smx_size; + unsigned sq_num_cf_insts; + unsigned sx_num_of_sets; + unsigned sc_prim_fifo_size; + unsigned sc_hiz_tile_fifo_size; + unsigned sc_earlyz_tile_fifo_fize; + unsigned tiling_nbanks; + unsigned tiling_npipes; + unsigned tiling_group_size; + unsigned tile_config; + unsigned backend_map; +}; + +struct evergreen_asic { + unsigned num_ses; + unsigned max_pipes; + unsigned max_tile_pipes; + unsigned max_simds; + unsigned max_backends; + unsigned max_gprs; + unsigned max_threads; + unsigned max_stack_entries; + unsigned max_hw_contexts; + unsigned max_gs_threads; + unsigned sx_max_export_size; + unsigned sx_max_export_pos_size; + unsigned sx_max_export_smx_size; + unsigned sq_num_cf_insts; + unsigned sx_num_of_sets; + unsigned sc_prim_fifo_size; + unsigned sc_hiz_tile_fifo_size; + unsigned sc_earlyz_tile_fifo_size; + unsigned tiling_nbanks; + unsigned tiling_npipes; + unsigned tiling_group_size; + unsigned tile_config; + unsigned backend_map; +}; + +struct cayman_asic { + unsigned max_shader_engines; + unsigned max_pipes_per_simd; + unsigned max_tile_pipes; + unsigned max_simds_per_se; + unsigned max_backends_per_se; + unsigned max_texture_channel_caches; + unsigned max_gprs; + unsigned max_threads; + unsigned max_gs_threads; + unsigned max_stack_entries; + unsigned sx_num_of_sets; + unsigned sx_max_export_size; + unsigned sx_max_export_pos_size; + unsigned sx_max_export_smx_size; + unsigned max_hw_contexts; + unsigned sq_num_cf_insts; + unsigned sc_prim_fifo_size; + unsigned sc_hiz_tile_fifo_size; + unsigned sc_earlyz_tile_fifo_size; + + unsigned num_shader_engines; + unsigned num_shader_pipes_per_simd; + unsigned num_tile_pipes; + unsigned num_simds_per_se; + unsigned num_backends_per_se; + unsigned backend_disable_mask_per_asic; + unsigned backend_map; + unsigned num_texture_channel_caches; + unsigned mem_max_burst_length_bytes; + unsigned mem_row_size_in_kb; + unsigned shader_engine_tile_size; + unsigned num_gpus; + unsigned multi_gpu_tile_size; + + unsigned tile_config; +}; + +struct si_asic { + unsigned max_shader_engines; + unsigned max_tile_pipes; + unsigned max_cu_per_sh; + unsigned max_sh_per_se; + unsigned max_backends_per_se; + unsigned max_texture_channel_caches; + unsigned max_gprs; + unsigned max_gs_threads; + unsigned max_hw_contexts; + unsigned sc_prim_fifo_size_frontend; + unsigned sc_prim_fifo_size_backend; + unsigned sc_hiz_tile_fifo_size; + unsigned sc_earlyz_tile_fifo_size; + + unsigned num_tile_pipes; + unsigned num_backends_per_se; + unsigned backend_disable_mask_per_asic; + unsigned backend_map; + unsigned num_texture_channel_caches; + unsigned mem_max_burst_length_bytes; + unsigned mem_row_size_in_kb; + unsigned shader_engine_tile_size; + unsigned num_gpus; + unsigned multi_gpu_tile_size; + + unsigned tile_config; +}; + +union radeon_asic_config { + struct r300_asic r300; + struct r100_asic r100; + struct r600_asic r600; + struct rv770_asic rv770; + struct evergreen_asic evergreen; + struct cayman_asic cayman; + struct si_asic si; +}; + +/* + * asic initizalization from radeon_asic.c + */ +int radeon_asic_init(struct radeon_device *rdev); + + +/* + * IOCTL. + */ +int radeon_gem_info_ioctl(struct drm_device *dev, void *data, + struct drm_file *filp); +int radeon_gem_create_ioctl(struct drm_device *dev, void *data, + struct drm_file *filp); +int radeon_gem_pin_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv); +int radeon_gem_unpin_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv); +int radeon_gem_pwrite_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv); +int radeon_gem_pread_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv); +int radeon_gem_set_domain_ioctl(struct drm_device *dev, void *data, + struct drm_file *filp); +int radeon_gem_mmap_ioctl(struct drm_device *dev, void *data, + struct drm_file *filp); +int radeon_gem_busy_ioctl(struct drm_device *dev, void *data, + struct drm_file *filp); +int radeon_gem_wait_idle_ioctl(struct drm_device *dev, void *data, + struct drm_file *filp); +int radeon_gem_va_ioctl(struct drm_device *dev, void *data, + struct drm_file *filp); +int radeon_cs_ioctl(struct drm_device *dev, void *data, struct drm_file *filp); +int radeon_gem_set_tiling_ioctl(struct drm_device *dev, void *data, + struct drm_file *filp); +int radeon_gem_get_tiling_ioctl(struct drm_device *dev, void *data, + struct drm_file *filp); + +/* VRAM scratch page for HDP bug, default vram page */ +struct r600_vram_scratch { + struct radeon_bo *robj; + volatile uint32_t *ptr; + u64 gpu_addr; +}; + +/* + * ACPI + */ +struct radeon_atif_notification_cfg { + bool enabled; + int command_code; +}; + +struct radeon_atif_notifications { + bool display_switch; + bool expansion_mode_change; + bool thermal_state; + bool forced_power_state; + bool system_power_state; + bool display_conf_change; + bool px_gfx_switch; + bool brightness_change; + bool dgpu_display_event; +}; + +struct radeon_atif_functions { + bool system_params; + bool sbios_requests; + bool select_active_disp; + bool lid_state; + bool get_tv_standard; + bool set_tv_standard; + bool get_panel_expansion_mode; + bool set_panel_expansion_mode; + bool temperature_change; + bool graphics_device_types; +}; + +struct radeon_atif { + struct radeon_atif_notifications notifications; + struct radeon_atif_functions functions; + struct radeon_atif_notification_cfg notification_cfg; + struct radeon_encoder *encoder_for_bl; +}; + +struct radeon_atcs_functions { + bool get_ext_state; + bool pcie_perf_req; + bool pcie_dev_rdy; + bool pcie_bus_width; +}; + +struct radeon_atcs { + struct radeon_atcs_functions functions; +}; + +/* + * Core structure, functions and helpers. + */ +typedef uint32_t (*radeon_rreg_t)(struct radeon_device*, uint32_t); +typedef void (*radeon_wreg_t)(struct radeon_device*, uint32_t, uint32_t); + +struct radeon_device { + device_t dev; + struct drm_device *ddev; + struct sx exclusive_lock; + /* ASIC */ + union radeon_asic_config config; + enum radeon_family family; + unsigned long flags; + int usec_timeout; + enum radeon_pll_errata pll_errata; + int num_gb_pipes; + int num_z_pipes; + int disp_priority; + /* BIOS */ + uint8_t *bios; + bool is_atom_bios; + uint16_t bios_header_start; + struct radeon_bo *stollen_vga_memory; + /* Register mmio */ + resource_size_t rmmio_base; + resource_size_t rmmio_size; + /* protects concurrent MM_INDEX/DATA based register access */ + struct mtx mmio_idx_lock; + int rmmio_rid; + struct resource *rmmio; + radeon_rreg_t mc_rreg; + radeon_wreg_t mc_wreg; + radeon_rreg_t pll_rreg; + radeon_wreg_t pll_wreg; + uint32_t pcie_reg_mask; + radeon_rreg_t pciep_rreg; + radeon_wreg_t pciep_wreg; + /* io port */ + int rio_rid; + struct resource *rio_mem; + resource_size_t rio_mem_size; + struct radeon_clock clock; + struct radeon_mc mc; + struct radeon_gart gart; + struct radeon_mode_info mode_info; + struct radeon_scratch scratch; + struct radeon_mman mman; + struct radeon_fence_driver fence_drv[RADEON_NUM_RINGS]; + struct cv fence_queue; + struct mtx fence_queue_mtx; + struct sx ring_lock; + struct radeon_ring ring[RADEON_NUM_RINGS]; + bool ib_pool_ready; + struct radeon_sa_manager ring_tmp_bo; + struct radeon_irq irq; + struct radeon_asic *asic; + struct radeon_gem gem; + struct radeon_pm pm; + uint32_t bios_scratch[RADEON_BIOS_NUM_SCRATCH]; + struct radeon_wb wb; + struct radeon_dummy_page dummy_page; + bool shutdown; + bool suspend; + bool need_dma32; + bool accel_working; + bool fictitious_range_registered; + struct radeon_surface_reg surface_regs[RADEON_GEM_MAX_SURFACES]; + const struct firmware *me_fw; /* all family ME firmware */ + const struct firmware *pfp_fw; /* r6/700 PFP firmware */ + const struct firmware *rlc_fw; /* r6/700 RLC firmware */ + const struct firmware *mc_fw; /* NI MC firmware */ + const struct firmware *ce_fw; /* SI CE firmware */ + struct r600_blit r600_blit; + struct r600_vram_scratch vram_scratch; + int msi_enabled; /* msi enabled */ + struct r600_ih ih; /* r6/700 interrupt ring */ + struct si_rlc rlc; + struct taskqueue *tq; + struct task hotplug_work; + struct task audio_work; + int num_crtc; /* number of crtcs */ + struct sx dc_hw_i2c_mutex; /* display controller hw i2c mutex */ + bool audio_enabled; + struct r600_audio audio_status; /* audio stuff */ + struct { + ACPI_HANDLE handle; + ACPI_NOTIFY_HANDLER notifier_call; + } acpi; + /* only one userspace can use Hyperz features or CMASK at a time */ + struct drm_file *hyperz_filp; + struct drm_file *cmask_filp; + /* i2c buses */ + struct radeon_i2c_chan *i2c_bus[RADEON_MAX_I2C_BUS]; + /* debugfs */ + struct radeon_debugfs debugfs[RADEON_DEBUGFS_MAX_COMPONENTS]; + unsigned debugfs_count; + /* virtual memory */ + struct radeon_vm_manager vm_manager; + struct sx gpu_clock_mutex; + /* ACPI interface */ + struct radeon_atif atif; + struct radeon_atcs atcs; +}; + +int radeon_device_init(struct radeon_device *rdev, + struct drm_device *ddev, + uint32_t flags); +void radeon_device_fini(struct radeon_device *rdev); +int radeon_gpu_wait_for_idle(struct radeon_device *rdev); + +uint32_t r100_mm_rreg(struct radeon_device *rdev, uint32_t reg, + bool always_indirect); +void r100_mm_wreg(struct radeon_device *rdev, uint32_t reg, uint32_t v, + bool always_indirect); +u32 r100_io_rreg(struct radeon_device *rdev, u32 reg); +void r100_io_wreg(struct radeon_device *rdev, u32 reg, u32 v); + +/* + * Cast helper + */ +#define to_radeon_fence(p) ((struct radeon_fence *)(p)) + +/* + * Registers read & write functions. + */ +#define RREG8(reg) bus_read_1((rdev->rmmio), (reg)) +#define WREG8(reg, v) bus_write_1((rdev->rmmio), (reg), v) +#define RREG16(reg) bus_read_2((rdev->rmmio), (reg)) +#define WREG16(reg, v) bus_write_2((rdev->rmmio), (reg), v) +#define RREG32(reg) r100_mm_rreg(rdev, (reg), false) +#define RREG32_IDX(reg) r100_mm_rreg(rdev, (reg), true) +#define DREG32(reg) DRM_INFO("REGISTER: " #reg " : 0x%08X\n", r100_mm_rreg(rdev, (reg))) +#define WREG32(reg, v) r100_mm_wreg(rdev, (reg), (v), false) +#define WREG32_IDX(reg, v) r100_mm_wreg(rdev, (reg), (v), true) +#define REG_SET(FIELD, v) (((v) << FIELD##_SHIFT) & FIELD##_MASK) +#define REG_GET(FIELD, v) (((v) << FIELD##_SHIFT) & FIELD##_MASK) +#define RREG32_PLL(reg) rdev->pll_rreg(rdev, (reg)) +#define WREG32_PLL(reg, v) rdev->pll_wreg(rdev, (reg), (v)) +#define RREG32_MC(reg) rdev->mc_rreg(rdev, (reg)) +#define WREG32_MC(reg, v) rdev->mc_wreg(rdev, (reg), (v)) +#define RREG32_PCIE(reg) rv370_pcie_rreg(rdev, (reg)) +#define WREG32_PCIE(reg, v) rv370_pcie_wreg(rdev, (reg), (v)) +#define RREG32_PCIE_P(reg) rdev->pciep_rreg(rdev, (reg)) +#define WREG32_PCIE_P(reg, v) rdev->pciep_wreg(rdev, (reg), (v)) +#define WREG32_P(reg, val, mask) \ + do { \ + uint32_t tmp_ = RREG32(reg); \ + tmp_ &= (mask); \ + tmp_ |= ((val) & ~(mask)); \ + WREG32(reg, tmp_); \ + } while (0) +#define WREG32_PLL_P(reg, val, mask) \ + do { \ + uint32_t tmp_ = RREG32_PLL(reg); \ + tmp_ &= (mask); \ + tmp_ |= ((val) & ~(mask)); \ + WREG32_PLL(reg, tmp_); \ + } while (0) +#define DREG32_SYS(sqf, rdev, reg) seq_printf((sqf), #reg " : 0x%08X\n", r100_mm_rreg((rdev), (reg), false)) +#define RREG32_IO(reg) r100_io_rreg(rdev, (reg)) +#define WREG32_IO(reg, v) r100_io_wreg(rdev, (reg), (v)) + +/* + * Indirect registers accessor + */ +static inline uint32_t rv370_pcie_rreg(struct radeon_device *rdev, uint32_t reg) +{ + uint32_t r; + + WREG32(RADEON_PCIE_INDEX, ((reg) & rdev->pcie_reg_mask)); + r = RREG32(RADEON_PCIE_DATA); + return r; +} + +static inline void rv370_pcie_wreg(struct radeon_device *rdev, uint32_t reg, uint32_t v) +{ + WREG32(RADEON_PCIE_INDEX, ((reg) & rdev->pcie_reg_mask)); + WREG32(RADEON_PCIE_DATA, (v)); +} + +void r100_pll_errata_after_index(struct radeon_device *rdev); + + +/* + * ASICs helpers. + */ +#define ASIC_IS_RN50(rdev) ((rdev->ddev->pci_device == 0x515e) || \ + (rdev->ddev->pci_device == 0x5969)) +#define ASIC_IS_RV100(rdev) ((rdev->family == CHIP_RV100) || \ + (rdev->family == CHIP_RV200) || \ + (rdev->family == CHIP_RS100) || \ + (rdev->family == CHIP_RS200) || \ + (rdev->family == CHIP_RV250) || \ + (rdev->family == CHIP_RV280) || \ + (rdev->family == CHIP_RS300)) +#define ASIC_IS_R300(rdev) ((rdev->family == CHIP_R300) || \ + (rdev->family == CHIP_RV350) || \ + (rdev->family == CHIP_R350) || \ + (rdev->family == CHIP_RV380) || \ + (rdev->family == CHIP_R420) || \ + (rdev->family == CHIP_R423) || \ + (rdev->family == CHIP_RV410) || \ + (rdev->family == CHIP_RS400) || \ + (rdev->family == CHIP_RS480)) +#define ASIC_IS_X2(rdev) ((rdev->ddev->pci_device == 0x9441) || \ + (rdev->ddev->pci_device == 0x9443) || \ + (rdev->ddev->pci_device == 0x944B) || \ + (rdev->ddev->pci_device == 0x9506) || \ + (rdev->ddev->pci_device == 0x9509) || \ + (rdev->ddev->pci_device == 0x950F) || \ + (rdev->ddev->pci_device == 0x689C) || \ + (rdev->ddev->pci_device == 0x689D)) +#define ASIC_IS_AVIVO(rdev) ((rdev->family >= CHIP_RS600)) +#define ASIC_IS_DCE2(rdev) ((rdev->family == CHIP_RS600) || \ + (rdev->family == CHIP_RS690) || \ + (rdev->family == CHIP_RS740) || \ + (rdev->family >= CHIP_R600)) +#define ASIC_IS_DCE3(rdev) ((rdev->family >= CHIP_RV620)) +#define ASIC_IS_DCE32(rdev) ((rdev->family >= CHIP_RV730)) +#define ASIC_IS_DCE4(rdev) ((rdev->family >= CHIP_CEDAR)) +#define ASIC_IS_DCE41(rdev) ((rdev->family >= CHIP_PALM) && \ + (rdev->flags & RADEON_IS_IGP)) +#define ASIC_IS_DCE5(rdev) ((rdev->family >= CHIP_BARTS)) +#define ASIC_IS_DCE6(rdev) ((rdev->family >= CHIP_ARUBA)) +#define ASIC_IS_DCE61(rdev) ((rdev->family >= CHIP_ARUBA) && \ + (rdev->flags & RADEON_IS_IGP)) + +/* + * BIOS helpers. + */ +#define RBIOS8(i) (rdev->bios[i]) +#define RBIOS16(i) (RBIOS8(i) | (RBIOS8((i)+1) << 8)) +#define RBIOS32(i) ((RBIOS16(i)) | (RBIOS16((i)+2) << 16)) + +int radeon_combios_init(struct radeon_device *rdev); +void radeon_combios_fini(struct radeon_device *rdev); +int radeon_atombios_init(struct radeon_device *rdev); +void radeon_atombios_fini(struct radeon_device *rdev); + + +/* + * RING helpers. + */ +#if !defined(DRM_DEBUG_CODE) || DRM_DEBUG_CODE == 0 +static inline void radeon_ring_write(struct radeon_ring *ring, uint32_t v) +{ + ring->ring[ring->wptr++] = v; + ring->wptr &= ring->ptr_mask; + ring->count_dw--; + ring->ring_free_dw--; +} +#else +/* With debugging this is just too big to inline */ +void radeon_ring_write(struct radeon_ring *ring, uint32_t v); +#endif + +/* + * ASICs macro. + */ +#define radeon_init(rdev) (rdev)->asic->init((rdev)) +#define radeon_fini(rdev) (rdev)->asic->fini((rdev)) +#define radeon_resume(rdev) (rdev)->asic->resume((rdev)) +#define radeon_suspend(rdev) (rdev)->asic->suspend((rdev)) +#define radeon_cs_parse(rdev, r, p) (rdev)->asic->ring[(r)].cs_parse((p)) +#define radeon_vga_set_state(rdev, state) (rdev)->asic->vga_set_state((rdev), (state)) +#define radeon_asic_reset(rdev) (rdev)->asic->asic_reset((rdev)) +#define radeon_gart_tlb_flush(rdev) (rdev)->asic->gart.tlb_flush((rdev)) +#define radeon_gart_set_page(rdev, i, p) (rdev)->asic->gart.set_page((rdev), (i), (p)) +#define radeon_asic_vm_init(rdev) (rdev)->asic->vm.init((rdev)) +#define radeon_asic_vm_fini(rdev) (rdev)->asic->vm.fini((rdev)) +#define radeon_asic_vm_set_page(rdev, pe, addr, count, incr, flags) ((rdev)->asic->vm.set_page((rdev), (pe), (addr), (count), (incr), (flags))) +#define radeon_ring_start(rdev, r, cp) (rdev)->asic->ring[(r)].ring_start((rdev), (cp)) +#define radeon_ring_test(rdev, r, cp) (rdev)->asic->ring[(r)].ring_test((rdev), (cp)) +#define radeon_ib_test(rdev, r, cp) (rdev)->asic->ring[(r)].ib_test((rdev), (cp)) +#define radeon_ring_ib_execute(rdev, r, ib) (rdev)->asic->ring[(r)].ib_execute((rdev), (ib)) +#define radeon_ring_ib_parse(rdev, r, ib) (rdev)->asic->ring[(r)].ib_parse((rdev), (ib)) +#define radeon_ring_is_lockup(rdev, r, cp) (rdev)->asic->ring[(r)].is_lockup((rdev), (cp)) +#define radeon_ring_vm_flush(rdev, r, vm) (rdev)->asic->ring[(r)].vm_flush((rdev), (r), (vm)) +#define radeon_irq_set(rdev) (rdev)->asic->irq.set((rdev)) +#define radeon_irq_process(rdev) (rdev)->asic->irq.process((rdev)) +#define radeon_get_vblank_counter(rdev, crtc) (rdev)->asic->display.get_vblank_counter((rdev), (crtc)) +#define radeon_set_backlight_level(rdev, e, l) (rdev)->asic->display.set_backlight_level((e), (l)) +#define radeon_get_backlight_level(rdev, e) (rdev)->asic->display.get_backlight_level((e)) +#define radeon_fence_ring_emit(rdev, r, fence) (rdev)->asic->ring[(r)].emit_fence((rdev), (fence)) +#define radeon_semaphore_ring_emit(rdev, r, cp, semaphore, emit_wait) (rdev)->asic->ring[(r)].emit_semaphore((rdev), (cp), (semaphore), (emit_wait)) +#define radeon_copy_blit(rdev, s, d, np, f) (rdev)->asic->copy.blit((rdev), (s), (d), (np), (f)) +#define radeon_copy_dma(rdev, s, d, np, f) (rdev)->asic->copy.dma((rdev), (s), (d), (np), (f)) +#define radeon_copy(rdev, s, d, np, f) (rdev)->asic->copy.copy((rdev), (s), (d), (np), (f)) +#define radeon_copy_blit_ring_index(rdev) (rdev)->asic->copy.blit_ring_index +#define radeon_copy_dma_ring_index(rdev) (rdev)->asic->copy.dma_ring_index +#define radeon_copy_ring_index(rdev) (rdev)->asic->copy.copy_ring_index +#define radeon_get_engine_clock(rdev) (rdev)->asic->pm.get_engine_clock((rdev)) +#define radeon_set_engine_clock(rdev, e) (rdev)->asic->pm.set_engine_clock((rdev), (e)) +#define radeon_get_memory_clock(rdev) (rdev)->asic->pm.get_memory_clock((rdev)) +#define radeon_set_memory_clock(rdev, e) (rdev)->asic->pm.set_memory_clock((rdev), (e)) +#define radeon_get_pcie_lanes(rdev) (rdev)->asic->pm.get_pcie_lanes((rdev)) +#define radeon_set_pcie_lanes(rdev, l) (rdev)->asic->pm.set_pcie_lanes((rdev), (l)) +#define radeon_set_clock_gating(rdev, e) (rdev)->asic->pm.set_clock_gating((rdev), (e)) +#define radeon_set_surface_reg(rdev, r, f, p, o, s) ((rdev)->asic->surface.set_reg((rdev), (r), (f), (p), (o), (s))) +#define radeon_clear_surface_reg(rdev, r) ((rdev)->asic->surface.clear_reg((rdev), (r))) +#define radeon_bandwidth_update(rdev) (rdev)->asic->display.bandwidth_update((rdev)) +#define radeon_hpd_init(rdev) (rdev)->asic->hpd.init((rdev)) +#define radeon_hpd_fini(rdev) (rdev)->asic->hpd.fini((rdev)) +#define radeon_hpd_sense(rdev, h) (rdev)->asic->hpd.sense((rdev), (h)) +#define radeon_hpd_set_polarity(rdev, h) (rdev)->asic->hpd.set_polarity((rdev), (h)) +#define radeon_gui_idle(rdev) (rdev)->asic->gui_idle((rdev)) +#define radeon_pm_misc(rdev) (rdev)->asic->pm.misc((rdev)) +#define radeon_pm_prepare(rdev) (rdev)->asic->pm.prepare((rdev)) +#define radeon_pm_finish(rdev) (rdev)->asic->pm.finish((rdev)) +#define radeon_pm_init_profile(rdev) (rdev)->asic->pm.init_profile((rdev)) +#define radeon_pm_get_dynpm_state(rdev) (rdev)->asic->pm.get_dynpm_state((rdev)) +#define radeon_pre_page_flip(rdev, crtc) (rdev)->asic->pflip.pre_page_flip((rdev), (crtc)) +#define radeon_page_flip(rdev, crtc, base) (rdev)->asic->pflip.page_flip((rdev), (crtc), (base)) +#define radeon_post_page_flip(rdev, crtc) (rdev)->asic->pflip.post_page_flip((rdev), (crtc)) +#define radeon_wait_for_vblank(rdev, crtc) (rdev)->asic->display.wait_for_vblank((rdev), (crtc)) +#define radeon_mc_wait_for_idle(rdev) (rdev)->asic->mc_wait_for_idle((rdev)) + +/* Common functions */ +/* AGP */ +extern int radeon_gpu_reset(struct radeon_device *rdev); +extern void radeon_agp_disable(struct radeon_device *rdev); +extern int radeon_modeset_init(struct radeon_device *rdev); +extern void radeon_modeset_fini(struct radeon_device *rdev); +extern bool radeon_card_posted(struct radeon_device *rdev); +extern void radeon_update_bandwidth_info(struct radeon_device *rdev); +extern void radeon_update_display_priority(struct radeon_device *rdev); +extern bool radeon_boot_test_post_card(struct radeon_device *rdev); +extern void radeon_scratch_init(struct radeon_device *rdev); +extern void radeon_wb_fini(struct radeon_device *rdev); +extern int radeon_wb_init(struct radeon_device *rdev); +extern void radeon_wb_disable(struct radeon_device *rdev); +extern void radeon_surface_init(struct radeon_device *rdev); +extern int radeon_cs_parser_init(struct radeon_cs_parser *p, void *data); +extern void radeon_ttm_placement_from_domain(struct radeon_bo *rbo, u32 domain); +extern bool radeon_ttm_bo_is_radeon_bo(struct ttm_buffer_object *bo); +extern void radeon_vram_location(struct radeon_device *rdev, struct radeon_mc *mc, u64 base); +extern void radeon_gtt_location(struct radeon_device *rdev, struct radeon_mc *mc); +extern int radeon_resume_kms(struct drm_device *dev); +extern int radeon_suspend_kms(struct drm_device *dev); +extern void radeon_ttm_set_active_vram_size(struct radeon_device *rdev, u64 size); + +/* + * vm + */ +int radeon_vm_manager_init(struct radeon_device *rdev); +void radeon_vm_manager_fini(struct radeon_device *rdev); +void radeon_vm_init(struct radeon_device *rdev, struct radeon_vm *vm); +void radeon_vm_fini(struct radeon_device *rdev, struct radeon_vm *vm); +int radeon_vm_alloc_pt(struct radeon_device *rdev, struct radeon_vm *vm); +void radeon_vm_add_to_lru(struct radeon_device *rdev, struct radeon_vm *vm); +struct radeon_fence *radeon_vm_grab_id(struct radeon_device *rdev, + struct radeon_vm *vm, int ring); +void radeon_vm_fence(struct radeon_device *rdev, + struct radeon_vm *vm, + struct radeon_fence *fence); +uint64_t radeon_vm_map_gart(struct radeon_device *rdev, uint64_t addr); +int radeon_vm_bo_update_pte(struct radeon_device *rdev, + struct radeon_vm *vm, + struct radeon_bo *bo, + struct ttm_mem_reg *mem); +void radeon_vm_bo_invalidate(struct radeon_device *rdev, + struct radeon_bo *bo); +struct radeon_bo_va *radeon_vm_bo_find(struct radeon_vm *vm, + struct radeon_bo *bo); +struct radeon_bo_va *radeon_vm_bo_add(struct radeon_device *rdev, + struct radeon_vm *vm, + struct radeon_bo *bo); +int radeon_vm_bo_set_addr(struct radeon_device *rdev, + struct radeon_bo_va *bo_va, + uint64_t offset, + uint32_t flags); +int radeon_vm_bo_rmv(struct radeon_device *rdev, + struct radeon_bo_va *bo_va); + +/* audio */ +void r600_audio_update_hdmi(void *arg, int pending); + +/* + * R600 vram scratch functions + */ +int r600_vram_scratch_init(struct radeon_device *rdev); +void r600_vram_scratch_fini(struct radeon_device *rdev); + +/* + * r600 cs checking helper + */ +unsigned r600_mip_minify(unsigned size, unsigned level); +bool r600_fmt_is_valid_color(u32 format); +bool r600_fmt_is_valid_texture(u32 format, enum radeon_family family); +int r600_fmt_get_blocksize(u32 format); +int r600_fmt_get_nblocksx(u32 format, u32 w); +int r600_fmt_get_nblocksy(u32 format, u32 h); + +/* + * r600 functions used by radeon_encoder.c + */ +struct radeon_hdmi_acr { + u32 clock; + + int n_32khz; + int cts_32khz; + + int n_44_1khz; + int cts_44_1khz; + + int n_48khz; + int cts_48khz; + +}; + +extern struct radeon_hdmi_acr r600_hdmi_acr(uint32_t clock); + +extern void r600_hdmi_enable(struct drm_encoder *encoder); +extern void r600_hdmi_disable(struct drm_encoder *encoder); +extern void r600_hdmi_setmode(struct drm_encoder *encoder, struct drm_display_mode *mode); +extern u32 r6xx_remap_render_backend(struct radeon_device *rdev, + u32 tiling_pipe_num, + u32 max_rb_num, + u32 total_max_rb_num, + u32 enabled_rb_mask); + +/* + * evergreen functions used by radeon_encoder.c + */ + +extern void evergreen_hdmi_setmode(struct drm_encoder *encoder, struct drm_display_mode *mode); + +extern int ni_init_microcode(struct radeon_device *rdev); +extern int ni_mc_load_microcode(struct radeon_device *rdev); +extern void ni_fini_microcode(struct radeon_device *rdev); + +/* radeon_acpi.c */ +extern int radeon_acpi_init(struct radeon_device *rdev); +extern void radeon_acpi_fini(struct radeon_device *rdev); + +/* Prototypes added by @dumbbell. */ + +/* atombios_encoders.c */ +void radeon_atom_backlight_init(struct radeon_encoder *radeon_encoder, + struct drm_connector *drm_connector); +void radeon_add_atom_encoder(struct drm_device *dev, uint32_t encoder_enum, + uint32_t supported_device, u16 caps); + +/* radeon_atombios.c */ +bool radeon_atom_get_tv_timings(struct radeon_device *rdev, int index, + struct drm_display_mode *mode); + +/* radeon_combios.c */ +void radeon_combios_connected_scratch_regs(struct drm_connector *connector, + struct drm_encoder *encoder, bool connected); + +/* radeon_connectors.c */ +void radeon_atombios_connected_scratch_regs(struct drm_connector *connector, + struct drm_encoder *encoder, bool connected); +void radeon_add_legacy_connector(struct drm_device *dev, + uint32_t connector_id, + uint32_t supported_device, + int connector_type, + struct radeon_i2c_bus_rec *i2c_bus, + uint16_t connector_object_id, + struct radeon_hpd *hpd); +void radeon_add_atom_connector(struct drm_device *dev, + uint32_t connector_id, + uint32_t supported_device, + int connector_type, + struct radeon_i2c_bus_rec *i2c_bus, + uint32_t igp_lane_info, + uint16_t connector_object_id, + struct radeon_hpd *hpd, + struct radeon_router *router); + +/* radeon_encoders.c */ +uint32_t radeon_get_encoder_enum(struct drm_device *dev, + uint32_t supported_device, uint8_t dac); +void radeon_link_encoder_connector(struct drm_device *dev); + +/* radeon_legacy_encoders.c */ +void radeon_add_legacy_encoder(struct drm_device *dev, + uint32_t encoder_enum, uint32_t supported_device); +void radeon_legacy_backlight_init(struct radeon_encoder *radeon_encoder, + struct drm_connector *drm_connector); + +/* radeon_pm.c */ +void radeon_pm_acpi_event_handler(struct radeon_device *rdev); + +/* radeon_ttm.c */ +int radeon_ttm_init(struct radeon_device *rdev); +void radeon_ttm_fini(struct radeon_device *rdev); + +/* r600.c */ +int r600_ih_ring_alloc(struct radeon_device *rdev); +void r600_ih_ring_fini(struct radeon_device *rdev); + +#include "radeon_object.h" + +#endif diff --git a/sys/dev/drm2/radeon/radeon_acpi.c b/sys/dev/drm2/radeon/radeon_acpi.c new file mode 100644 index 00000000000..176e160ffb0 --- /dev/null +++ b/sys/dev/drm2/radeon/radeon_acpi.c @@ -0,0 +1,639 @@ +/* + * Copyright 2012 Advanced Micro Devices, Inc. + * + * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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 +__FBSDID("$FreeBSD$"); + +#include +#include +#include "radeon.h" +#include "radeon_acpi.h" +#include "atom.h" + +#define ACPI_AC_CLASS "ac_adapter" + +struct atif_verify_interface { + u16 size; /* structure size in bytes (includes size field) */ + u16 version; /* version */ + u32 notification_mask; /* supported notifications mask */ + u32 function_bits; /* supported functions bit vector */ +} __packed; + +struct atif_system_params { + u16 size; /* structure size in bytes (includes size field) */ + u32 valid_mask; /* valid flags mask */ + u32 flags; /* flags */ + u8 command_code; /* notify command code */ +} __packed; + +struct atif_sbios_requests { + u16 size; /* structure size in bytes (includes size field) */ + u32 pending; /* pending sbios requests */ + u8 panel_exp_mode; /* panel expansion mode */ + u8 thermal_gfx; /* thermal state: target gfx controller */ + u8 thermal_state; /* thermal state: state id (0: exit state, non-0: state) */ + u8 forced_power_gfx; /* forced power state: target gfx controller */ + u8 forced_power_state; /* forced power state: state id */ + u8 system_power_src; /* system power source */ + u8 backlight_level; /* panel backlight level (0-255) */ +} __packed; + +#define ATIF_NOTIFY_MASK 0x3 +#define ATIF_NOTIFY_NONE 0 +#define ATIF_NOTIFY_81 1 +#define ATIF_NOTIFY_N 2 + +struct atcs_verify_interface { + u16 size; /* structure size in bytes (includes size field) */ + u16 version; /* version */ + u32 function_bits; /* supported functions bit vector */ +} __packed; + +/* Call the ATIF method + */ +/** + * radeon_atif_call - call an ATIF method + * + * @handle: acpi handle + * @function: the ATIF function to execute + * @params: ATIF function params + * + * Executes the requested ATIF function (all asics). + * Returns a pointer to the acpi output buffer. + */ +static ACPI_OBJECT *radeon_atif_call(ACPI_HANDLE handle, int function, + ACPI_BUFFER *params) +{ + ACPI_STATUS status; + ACPI_OBJECT atif_arg_elements[2]; + ACPI_OBJECT_LIST atif_arg; + ACPI_BUFFER buffer = { ACPI_ALLOCATE_BUFFER, NULL }; + + atif_arg.Count = 2; + atif_arg.Pointer = &atif_arg_elements[0]; + + atif_arg_elements[0].Type = ACPI_TYPE_INTEGER; + atif_arg_elements[0].Integer.Value = function; + + if (params) { + atif_arg_elements[1].Type = ACPI_TYPE_BUFFER; + atif_arg_elements[1].Buffer.Length = params->Length; + atif_arg_elements[1].Buffer.Pointer = params->Pointer; + } else { + /* We need a second fake parameter */ + atif_arg_elements[1].Type = ACPI_TYPE_INTEGER; + atif_arg_elements[1].Integer.Value = 0; + } + + status = AcpiEvaluateObject(handle, "ATIF", &atif_arg, &buffer); + + /* Fail only if calling the method fails and ATIF is supported */ + if (ACPI_FAILURE(status) && status != AE_NOT_FOUND) { + DRM_DEBUG_DRIVER("failed to evaluate ATIF got %s\n", + AcpiFormatException(status)); + AcpiOsFree(buffer.Pointer); + return NULL; + } + + return buffer.Pointer; +} + +/** + * radeon_atif_parse_notification - parse supported notifications + * + * @n: supported notifications struct + * @mask: supported notifications mask from ATIF + * + * Use the supported notifications mask from ATIF function + * ATIF_FUNCTION_VERIFY_INTERFACE to determine what notifications + * are supported (all asics). + */ +static void radeon_atif_parse_notification(struct radeon_atif_notifications *n, u32 mask) +{ + n->display_switch = mask & ATIF_DISPLAY_SWITCH_REQUEST_SUPPORTED; + n->expansion_mode_change = mask & ATIF_EXPANSION_MODE_CHANGE_REQUEST_SUPPORTED; + n->thermal_state = mask & ATIF_THERMAL_STATE_CHANGE_REQUEST_SUPPORTED; + n->forced_power_state = mask & ATIF_FORCED_POWER_STATE_CHANGE_REQUEST_SUPPORTED; + n->system_power_state = mask & ATIF_SYSTEM_POWER_SOURCE_CHANGE_REQUEST_SUPPORTED; + n->display_conf_change = mask & ATIF_DISPLAY_CONF_CHANGE_REQUEST_SUPPORTED; + n->px_gfx_switch = mask & ATIF_PX_GFX_SWITCH_REQUEST_SUPPORTED; + n->brightness_change = mask & ATIF_PANEL_BRIGHTNESS_CHANGE_REQUEST_SUPPORTED; + n->dgpu_display_event = mask & ATIF_DGPU_DISPLAY_EVENT_SUPPORTED; +} + +/** + * radeon_atif_parse_functions - parse supported functions + * + * @f: supported functions struct + * @mask: supported functions mask from ATIF + * + * Use the supported functions mask from ATIF function + * ATIF_FUNCTION_VERIFY_INTERFACE to determine what functions + * are supported (all asics). + */ +static void radeon_atif_parse_functions(struct radeon_atif_functions *f, u32 mask) +{ + f->system_params = mask & ATIF_GET_SYSTEM_PARAMETERS_SUPPORTED; + f->sbios_requests = mask & ATIF_GET_SYSTEM_BIOS_REQUESTS_SUPPORTED; + f->select_active_disp = mask & ATIF_SELECT_ACTIVE_DISPLAYS_SUPPORTED; + f->lid_state = mask & ATIF_GET_LID_STATE_SUPPORTED; + f->get_tv_standard = mask & ATIF_GET_TV_STANDARD_FROM_CMOS_SUPPORTED; + f->set_tv_standard = mask & ATIF_SET_TV_STANDARD_IN_CMOS_SUPPORTED; + f->get_panel_expansion_mode = mask & ATIF_GET_PANEL_EXPANSION_MODE_FROM_CMOS_SUPPORTED; + f->set_panel_expansion_mode = mask & ATIF_SET_PANEL_EXPANSION_MODE_IN_CMOS_SUPPORTED; + f->temperature_change = mask & ATIF_TEMPERATURE_CHANGE_NOTIFICATION_SUPPORTED; + f->graphics_device_types = mask & ATIF_GET_GRAPHICS_DEVICE_TYPES_SUPPORTED; +} + +/** + * radeon_atif_verify_interface - verify ATIF + * + * @handle: acpi handle + * @atif: radeon atif struct + * + * Execute the ATIF_FUNCTION_VERIFY_INTERFACE ATIF function + * to initialize ATIF and determine what features are supported + * (all asics). + * returns 0 on success, error on failure. + */ +static int radeon_atif_verify_interface(ACPI_HANDLE handle, + struct radeon_atif *atif) +{ + ACPI_OBJECT *info; + struct atif_verify_interface output; + size_t size; + int err = 0; + + info = radeon_atif_call(handle, ATIF_FUNCTION_VERIFY_INTERFACE, NULL); + if (!info) + return -EIO; + + memset(&output, 0, sizeof(output)); + + size = *(u16 *) info->Buffer.Pointer; + if (size < 12) { + DRM_INFO("ATIF buffer is too small: %zu\n", size); + err = -EINVAL; + goto out; + } + size = min(sizeof(output), size); + + memcpy(&output, info->Buffer.Pointer, size); + + /* TODO: check version? */ + DRM_DEBUG_DRIVER("ATIF version %u\n", output.version); + + radeon_atif_parse_notification(&atif->notifications, output.notification_mask); + radeon_atif_parse_functions(&atif->functions, output.function_bits); + +out: + AcpiOsFree(info); + return err; +} + +/** + * radeon_atif_get_notification_params - determine notify configuration + * + * @handle: acpi handle + * @n: atif notification configuration struct + * + * Execute the ATIF_FUNCTION_GET_SYSTEM_PARAMETERS ATIF function + * to determine if a notifier is used and if so which one + * (all asics). This is either Notify(VGA, 0x81) or Notify(VGA, n) + * where n is specified in the result if a notifier is used. + * Returns 0 on success, error on failure. + */ +static int radeon_atif_get_notification_params(ACPI_HANDLE handle, + struct radeon_atif_notification_cfg *n) +{ + ACPI_OBJECT *info; + struct atif_system_params params; + size_t size; + int err = 0; + + info = radeon_atif_call(handle, ATIF_FUNCTION_GET_SYSTEM_PARAMETERS, NULL); + if (!info) { + err = -EIO; + goto out; + } + + size = *(u16 *) info->Buffer.Pointer; + if (size < 10) { + err = -EINVAL; + goto out; + } + + memset(¶ms, 0, sizeof(params)); + size = min(sizeof(params), size); + memcpy(¶ms, info->Buffer.Pointer, size); + + DRM_DEBUG_DRIVER("SYSTEM_PARAMS: mask = %#x, flags = %#x\n", + params.flags, params.valid_mask); + params.flags = params.flags & params.valid_mask; + + if ((params.flags & ATIF_NOTIFY_MASK) == ATIF_NOTIFY_NONE) { + n->enabled = false; + n->command_code = 0; + } else if ((params.flags & ATIF_NOTIFY_MASK) == ATIF_NOTIFY_81) { + n->enabled = true; + n->command_code = 0x81; + } else { + if (size < 11) { + err = -EINVAL; + goto out; + } + n->enabled = true; + n->command_code = params.command_code; + } + +out: + DRM_DEBUG_DRIVER("Notification %s, command code = %#x\n", + (n->enabled ? "enabled" : "disabled"), + n->command_code); + AcpiOsFree(info); + return err; +} + +/** + * radeon_atif_get_sbios_requests - get requested sbios event + * + * @handle: acpi handle + * @req: atif sbios request struct + * + * Execute the ATIF_FUNCTION_GET_SYSTEM_BIOS_REQUESTS ATIF function + * to determine what requests the sbios is making to the driver + * (all asics). + * Returns 0 on success, error on failure. + */ +static int radeon_atif_get_sbios_requests(ACPI_HANDLE handle, + struct atif_sbios_requests *req) +{ + ACPI_OBJECT *info; + size_t size; + int count = 0; + + info = radeon_atif_call(handle, ATIF_FUNCTION_GET_SYSTEM_BIOS_REQUESTS, NULL); + if (!info) + return -EIO; + + size = *(u16 *)info->Buffer.Pointer; + if (size < 0xd) { + count = -EINVAL; + goto out; + } + memset(req, 0, sizeof(*req)); + + size = min(sizeof(*req), size); + memcpy(req, info->Buffer.Pointer, size); + DRM_DEBUG_DRIVER("SBIOS pending requests: %#x\n", req->pending); + + count = hweight32(req->pending); + +out: + AcpiOsFree(info); + return count; +} + +/** + * radeon_atif_handler - handle ATIF notify requests + * + * @rdev: radeon_device pointer + * @event: atif sbios request struct + * + * Checks the acpi event and if it matches an atif event, + * handles it. + * Returns NOTIFY code + */ +void radeon_atif_handler(struct radeon_device *rdev, + UINT32 type) +{ + struct radeon_atif *atif = &rdev->atif; + struct atif_sbios_requests req; + ACPI_HANDLE handle; + int count; + + DRM_DEBUG_DRIVER("event, type = %#x\n", + type); + + if (!atif->notification_cfg.enabled || + type != atif->notification_cfg.command_code) + /* Not our event */ + return; + + /* Check pending SBIOS requests */ + handle = rdev->acpi.handle; + count = radeon_atif_get_sbios_requests(handle, &req); + + if (count <= 0) + return; + + DRM_DEBUG_DRIVER("ATIF: %d pending SBIOS requests\n", count); + + if (req.pending & ATIF_PANEL_BRIGHTNESS_CHANGE_REQUEST) { + struct radeon_encoder *enc = atif->encoder_for_bl; + + if (enc) { + DRM_DEBUG_DRIVER("Changing brightness to %d\n", + req.backlight_level); + + radeon_set_backlight_level(rdev, enc, req.backlight_level); + +#ifdef DUMBBELL_WIP + if (rdev->is_atom_bios) { + struct radeon_encoder_atom_dig *dig = enc->enc_priv; + backlight_force_update(dig->bl_dev, + BACKLIGHT_UPDATE_HOTKEY); + } else { + struct radeon_encoder_lvds *dig = enc->enc_priv; + backlight_force_update(dig->bl_dev, + BACKLIGHT_UPDATE_HOTKEY); + } +#endif /* DUMBBELL_WIP */ + } + } + /* TODO: check other events */ + + /* We've handled the event, stop the notifier chain. The ACPI interface + * overloads ACPI_VIDEO_NOTIFY_PROBE, we don't want to send that to + * userspace if the event was generated only to signal a SBIOS + * request. + */ +} + +/* Call the ATCS method + */ +/** + * radeon_atcs_call - call an ATCS method + * + * @handle: acpi handle + * @function: the ATCS function to execute + * @params: ATCS function params + * + * Executes the requested ATCS function (all asics). + * Returns a pointer to the acpi output buffer. + */ +static union acpi_object *radeon_atcs_call(ACPI_HANDLE handle, int function, + ACPI_BUFFER *params) +{ + ACPI_STATUS status; + ACPI_OBJECT atcs_arg_elements[2]; + ACPI_OBJECT_LIST atcs_arg; + ACPI_BUFFER buffer = { ACPI_ALLOCATE_BUFFER, NULL }; + + atcs_arg.Count = 2; + atcs_arg.Pointer = &atcs_arg_elements[0]; + + atcs_arg_elements[0].Type = ACPI_TYPE_INTEGER; + atcs_arg_elements[0].Integer.Value = function; + + if (params) { + atcs_arg_elements[1].Type = ACPI_TYPE_BUFFER; + atcs_arg_elements[1].Buffer.Length = params->Length; + atcs_arg_elements[1].Buffer.Pointer = params->Pointer; + } else { + /* We need a second fake parameter */ + atcs_arg_elements[1].Type = ACPI_TYPE_INTEGER; + atcs_arg_elements[1].Integer.Value = 0; + } + + status = AcpiEvaluateObject(handle, "ATCS", &atcs_arg, &buffer); + + /* Fail only if calling the method fails and ATIF is supported */ + if (ACPI_FAILURE(status) && status != AE_NOT_FOUND) { + DRM_DEBUG_DRIVER("failed to evaluate ATCS got %s\n", + AcpiFormatException(status)); + AcpiOsFree(buffer.Pointer); + return NULL; + } + + return buffer.Pointer; +} + +/** + * radeon_atcs_parse_functions - parse supported functions + * + * @f: supported functions struct + * @mask: supported functions mask from ATCS + * + * Use the supported functions mask from ATCS function + * ATCS_FUNCTION_VERIFY_INTERFACE to determine what functions + * are supported (all asics). + */ +static void radeon_atcs_parse_functions(struct radeon_atcs_functions *f, u32 mask) +{ + f->get_ext_state = mask & ATCS_GET_EXTERNAL_STATE_SUPPORTED; + f->pcie_perf_req = mask & ATCS_PCIE_PERFORMANCE_REQUEST_SUPPORTED; + f->pcie_dev_rdy = mask & ATCS_PCIE_DEVICE_READY_NOTIFICATION_SUPPORTED; + f->pcie_bus_width = mask & ATCS_SET_PCIE_BUS_WIDTH_SUPPORTED; +} + +/** + * radeon_atcs_verify_interface - verify ATCS + * + * @handle: acpi handle + * @atcs: radeon atcs struct + * + * Execute the ATCS_FUNCTION_VERIFY_INTERFACE ATCS function + * to initialize ATCS and determine what features are supported + * (all asics). + * returns 0 on success, error on failure. + */ +static int radeon_atcs_verify_interface(ACPI_HANDLE handle, + struct radeon_atcs *atcs) +{ + ACPI_OBJECT *info; + struct atcs_verify_interface output; + size_t size; + int err = 0; + + info = radeon_atcs_call(handle, ATCS_FUNCTION_VERIFY_INTERFACE, NULL); + if (!info) + return -EIO; + + memset(&output, 0, sizeof(output)); + + size = *(u16 *) info->Buffer.Pointer; + if (size < 8) { + DRM_INFO("ATCS buffer is too small: %zu\n", size); + err = -EINVAL; + goto out; + } + size = min(sizeof(output), size); + + memcpy(&output, info->Buffer.Pointer, size); + + /* TODO: check version? */ + DRM_DEBUG_DRIVER("ATCS version %u\n", output.version); + + radeon_atcs_parse_functions(&atcs->functions, output.function_bits); + +out: + AcpiOsFree(info); + return err; +} + +/** + * radeon_acpi_event - handle notify events + * + * @nb: notifier block + * @val: val + * @data: acpi event + * + * Calls relevant radeon functions in response to various + * acpi events. + * Returns NOTIFY code + */ +static void radeon_acpi_event(ACPI_HANDLE handle, UINT32 type, + void *context) +{ + struct radeon_device *rdev = (struct radeon_device *)context; + +#ifdef DUMBBELL_WIP + if (strcmp(entry->device_class, ACPI_AC_CLASS) == 0) { + if (power_supply_is_system_supplied() > 0) + DRM_DEBUG_DRIVER("pm: AC\n"); + else + DRM_DEBUG_DRIVER("pm: DC\n"); + + radeon_pm_acpi_event_handler(rdev); + } +#endif /* DUMBBELL_WIP */ + + /* Check for pending SBIOS requests */ + radeon_atif_handler(rdev, type); +} + +/* Call all ACPI methods here */ +/** + * radeon_acpi_init - init driver acpi support + * + * @rdev: radeon_device pointer + * + * Verifies the AMD ACPI interfaces and registers with the acpi + * notifier chain (all asics). + * Returns 0 on success, error on failure. + */ +int radeon_acpi_init(struct radeon_device *rdev) +{ + ACPI_HANDLE handle; + struct radeon_atif *atif = &rdev->atif; + struct radeon_atcs *atcs = &rdev->atcs; + int ret; + + /* Get the device handle */ + handle = acpi_get_handle(rdev->dev); + + /* No need to proceed if we're sure that ATIF is not supported */ + if (!ASIC_IS_AVIVO(rdev) || !rdev->bios || !handle) + return 0; + + /* Call the ATCS method */ + ret = radeon_atcs_verify_interface(handle, atcs); + if (ret) { + DRM_DEBUG_DRIVER("Call to ATCS verify_interface failed: %d\n", ret); + } + + /* Call the ATIF method */ + ret = radeon_atif_verify_interface(handle, atif); + if (ret) { + DRM_DEBUG_DRIVER("Call to ATIF verify_interface failed: %d\n", ret); + goto out; + } + + if (atif->notifications.brightness_change) { + struct drm_encoder *tmp; + struct radeon_encoder *target = NULL; + + /* Find the encoder controlling the brightness */ + list_for_each_entry(tmp, &rdev->ddev->mode_config.encoder_list, + head) { + struct radeon_encoder *enc = to_radeon_encoder(tmp); + + if ((enc->devices & (ATOM_DEVICE_LCD_SUPPORT)) && + enc->enc_priv) { + if (rdev->is_atom_bios) { + struct radeon_encoder_atom_dig *dig = enc->enc_priv; + if (dig->bl_dev) { + target = enc; + break; + } + } else { + struct radeon_encoder_lvds *dig = enc->enc_priv; + if (dig->bl_dev) { + target = enc; + break; + } + } + } + } + + atif->encoder_for_bl = target; + if (!target) { + /* Brightness change notification is enabled, but we + * didn't find a backlight controller, this should + * never happen. + */ + DRM_ERROR("Cannot find a backlight controller\n"); + } + } + + if (atif->functions.sbios_requests && !atif->functions.system_params) { + /* XXX check this workraround, if sbios request function is + * present we have to see how it's configured in the system + * params + */ + atif->functions.system_params = true; + } + + if (atif->functions.system_params) { + ret = radeon_atif_get_notification_params(handle, + &atif->notification_cfg); + if (ret) { + DRM_DEBUG_DRIVER("Call to GET_SYSTEM_PARAMS failed: %d\n", + ret); + /* Disable notification */ + atif->notification_cfg.enabled = false; + } + } + +out: + rdev->acpi.handle = handle; + rdev->acpi.notifier_call = radeon_acpi_event; + AcpiInstallNotifyHandler(handle, ACPI_DEVICE_NOTIFY, + rdev->acpi.notifier_call, rdev); + + return ret; +} + +/** + * radeon_acpi_fini - tear down driver acpi support + * + * @rdev: radeon_device pointer + * + * Unregisters with the acpi notifier chain (all asics). + */ +void radeon_acpi_fini(struct radeon_device *rdev) +{ + AcpiRemoveNotifyHandler(rdev->acpi.handle, ACPI_DEVICE_NOTIFY, + rdev->acpi.notifier_call); +} diff --git a/sys/dev/drm2/radeon/radeon_acpi.h b/sys/dev/drm2/radeon/radeon_acpi.h new file mode 100644 index 00000000000..300be6c882f --- /dev/null +++ b/sys/dev/drm2/radeon/radeon_acpi.h @@ -0,0 +1,446 @@ +/* + * Copyright 2012 Advanced Micro Devices, Inc. + * + * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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 +__FBSDID("$FreeBSD$"); + +#ifndef RADEON_ACPI_H +#define RADEON_ACPI_H + +struct radeon_device; + +void radeon_atif_handler(struct radeon_device *rdev, UINT32 type); + +/* AMD hw uses four ACPI control methods: + * 1. ATIF + * ARG0: (ACPI_INTEGER) function code + * ARG1: (ACPI_BUFFER) parameter buffer, 256 bytes + * OUTPUT: (ACPI_BUFFER) output buffer, 256 bytes + * ATIF provides an entry point for the gfx driver to interact with the sbios. + * The AMD ACPI notification mechanism uses Notify (VGA, 0x81) or a custom + * notification. Which notification is used as indicated by the ATIF Control + * Method GET_SYSTEM_PARAMETERS. When the driver receives Notify (VGA, 0x81) or + * a custom notification it invokes ATIF Control Method GET_SYSTEM_BIOS_REQUESTS + * to identify pending System BIOS requests and associated parameters. For + * example, if one of the pending requests is DISPLAY_SWITCH_REQUEST, the driver + * will perform display device detection and invoke ATIF Control Method + * SELECT_ACTIVE_DISPLAYS. + * + * 2. ATPX + * ARG0: (ACPI_INTEGER) function code + * ARG1: (ACPI_BUFFER) parameter buffer, 256 bytes + * OUTPUT: (ACPI_BUFFER) output buffer, 256 bytes + * ATPX methods are used on PowerXpress systems to handle mux switching and + * discrete GPU power control. + * + * 3. ATRM + * ARG0: (ACPI_INTEGER) offset of vbios rom data + * ARG1: (ACPI_BUFFER) size of the buffer to fill (up to 4K). + * OUTPUT: (ACPI_BUFFER) output buffer + * ATRM provides an interfacess to access the discrete GPU vbios image on + * PowerXpress systems with multiple GPUs. + * + * 4. ATCS + * ARG0: (ACPI_INTEGER) function code + * ARG1: (ACPI_BUFFER) parameter buffer, 256 bytes + * OUTPUT: (ACPI_BUFFER) output buffer, 256 bytes + * ATCS provides an interface to AMD chipset specific functionality. + * + */ +/* ATIF */ +#define ATIF_FUNCTION_VERIFY_INTERFACE 0x0 +/* ARG0: ATIF_FUNCTION_VERIFY_INTERFACE + * ARG1: none + * OUTPUT: + * WORD - structure size in bytes (includes size field) + * WORD - version + * DWORD - supported notifications mask + * DWORD - supported functions bit vector + */ +/* Notifications mask */ +# define ATIF_DISPLAY_SWITCH_REQUEST_SUPPORTED (1 << 0) +# define ATIF_EXPANSION_MODE_CHANGE_REQUEST_SUPPORTED (1 << 1) +# define ATIF_THERMAL_STATE_CHANGE_REQUEST_SUPPORTED (1 << 2) +# define ATIF_FORCED_POWER_STATE_CHANGE_REQUEST_SUPPORTED (1 << 3) +# define ATIF_SYSTEM_POWER_SOURCE_CHANGE_REQUEST_SUPPORTED (1 << 4) +# define ATIF_DISPLAY_CONF_CHANGE_REQUEST_SUPPORTED (1 << 5) +# define ATIF_PX_GFX_SWITCH_REQUEST_SUPPORTED (1 << 6) +# define ATIF_PANEL_BRIGHTNESS_CHANGE_REQUEST_SUPPORTED (1 << 7) +# define ATIF_DGPU_DISPLAY_EVENT_SUPPORTED (1 << 8) +/* supported functions vector */ +# define ATIF_GET_SYSTEM_PARAMETERS_SUPPORTED (1 << 0) +# define ATIF_GET_SYSTEM_BIOS_REQUESTS_SUPPORTED (1 << 1) +# define ATIF_SELECT_ACTIVE_DISPLAYS_SUPPORTED (1 << 2) +# define ATIF_GET_LID_STATE_SUPPORTED (1 << 3) +# define ATIF_GET_TV_STANDARD_FROM_CMOS_SUPPORTED (1 << 4) +# define ATIF_SET_TV_STANDARD_IN_CMOS_SUPPORTED (1 << 5) +# define ATIF_GET_PANEL_EXPANSION_MODE_FROM_CMOS_SUPPORTED (1 << 6) +# define ATIF_SET_PANEL_EXPANSION_MODE_IN_CMOS_SUPPORTED (1 << 7) +# define ATIF_TEMPERATURE_CHANGE_NOTIFICATION_SUPPORTED (1 << 12) +# define ATIF_GET_GRAPHICS_DEVICE_TYPES_SUPPORTED (1 << 14) +#define ATIF_FUNCTION_GET_SYSTEM_PARAMETERS 0x1 +/* ARG0: ATIF_FUNCTION_GET_SYSTEM_PARAMETERS + * ARG1: none + * OUTPUT: + * WORD - structure size in bytes (includes size field) + * DWORD - valid flags mask + * DWORD - flags + * + * OR + * + * WORD - structure size in bytes (includes size field) + * DWORD - valid flags mask + * DWORD - flags + * BYTE - notify command code + * + * flags + * bits 1:0: + * 0 - Notify(VGA, 0x81) is not used for notification + * 1 - Notify(VGA, 0x81) is used for notification + * 2 - Notify(VGA, n) is used for notification where + * n (0xd0-0xd9) is specified in notify command code. + * bit 2: + * 1 - lid changes not reported though int10 + */ +#define ATIF_FUNCTION_GET_SYSTEM_BIOS_REQUESTS 0x2 +/* ARG0: ATIF_FUNCTION_GET_SYSTEM_BIOS_REQUESTS + * ARG1: none + * OUTPUT: + * WORD - structure size in bytes (includes size field) + * DWORD - pending sbios requests + * BYTE - panel expansion mode + * BYTE - thermal state: target gfx controller + * BYTE - thermal state: state id (0: exit state, non-0: state) + * BYTE - forced power state: target gfx controller + * BYTE - forced power state: state id + * BYTE - system power source + * BYTE - panel backlight level (0-255) + */ +/* pending sbios requests */ +# define ATIF_DISPLAY_SWITCH_REQUEST (1 << 0) +# define ATIF_EXPANSION_MODE_CHANGE_REQUEST (1 << 1) +# define ATIF_THERMAL_STATE_CHANGE_REQUEST (1 << 2) +# define ATIF_FORCED_POWER_STATE_CHANGE_REQUEST (1 << 3) +# define ATIF_SYSTEM_POWER_SOURCE_CHANGE_REQUEST (1 << 4) +# define ATIF_DISPLAY_CONF_CHANGE_REQUEST (1 << 5) +# define ATIF_PX_GFX_SWITCH_REQUEST (1 << 6) +# define ATIF_PANEL_BRIGHTNESS_CHANGE_REQUEST (1 << 7) +# define ATIF_DGPU_DISPLAY_EVENT (1 << 8) +/* panel expansion mode */ +# define ATIF_PANEL_EXPANSION_DISABLE 0 +# define ATIF_PANEL_EXPANSION_FULL 1 +# define ATIF_PANEL_EXPANSION_ASPECT 2 +/* target gfx controller */ +# define ATIF_TARGET_GFX_SINGLE 0 +# define ATIF_TARGET_GFX_PX_IGPU 1 +# define ATIF_TARGET_GFX_PX_DGPU 2 +/* system power source */ +# define ATIF_POWER_SOURCE_AC 1 +# define ATIF_POWER_SOURCE_DC 2 +# define ATIF_POWER_SOURCE_RESTRICTED_AC_1 3 +# define ATIF_POWER_SOURCE_RESTRICTED_AC_2 4 +#define ATIF_FUNCTION_SELECT_ACTIVE_DISPLAYS 0x3 +/* ARG0: ATIF_FUNCTION_SELECT_ACTIVE_DISPLAYS + * ARG1: + * WORD - structure size in bytes (includes size field) + * WORD - selected displays + * WORD - connected displays + * OUTPUT: + * WORD - structure size in bytes (includes size field) + * WORD - selected displays + */ +# define ATIF_LCD1 (1 << 0) +# define ATIF_CRT1 (1 << 1) +# define ATIF_TV (1 << 2) +# define ATIF_DFP1 (1 << 3) +# define ATIF_CRT2 (1 << 4) +# define ATIF_LCD2 (1 << 5) +# define ATIF_DFP2 (1 << 7) +# define ATIF_CV (1 << 8) +# define ATIF_DFP3 (1 << 9) +# define ATIF_DFP4 (1 << 10) +# define ATIF_DFP5 (1 << 11) +# define ATIF_DFP6 (1 << 12) +#define ATIF_FUNCTION_GET_LID_STATE 0x4 +/* ARG0: ATIF_FUNCTION_GET_LID_STATE + * ARG1: none + * OUTPUT: + * WORD - structure size in bytes (includes size field) + * BYTE - lid state (0: open, 1: closed) + * + * GET_LID_STATE only works at boot and resume, for general lid + * status, use the kernel provided status + */ +#define ATIF_FUNCTION_GET_TV_STANDARD_FROM_CMOS 0x5 +/* ARG0: ATIF_FUNCTION_GET_TV_STANDARD_FROM_CMOS + * ARG1: none + * OUTPUT: + * WORD - structure size in bytes (includes size field) + * BYTE - 0 + * BYTE - TV standard + */ +# define ATIF_TV_STD_NTSC 0 +# define ATIF_TV_STD_PAL 1 +# define ATIF_TV_STD_PALM 2 +# define ATIF_TV_STD_PAL60 3 +# define ATIF_TV_STD_NTSCJ 4 +# define ATIF_TV_STD_PALCN 5 +# define ATIF_TV_STD_PALN 6 +# define ATIF_TV_STD_SCART_RGB 9 +#define ATIF_FUNCTION_SET_TV_STANDARD_IN_CMOS 0x6 +/* ARG0: ATIF_FUNCTION_SET_TV_STANDARD_IN_CMOS + * ARG1: + * WORD - structure size in bytes (includes size field) + * BYTE - 0 + * BYTE - TV standard + * OUTPUT: none + */ +#define ATIF_FUNCTION_GET_PANEL_EXPANSION_MODE_FROM_CMOS 0x7 +/* ARG0: ATIF_FUNCTION_GET_PANEL_EXPANSION_MODE_FROM_CMOS + * ARG1: none + * OUTPUT: + * WORD - structure size in bytes (includes size field) + * BYTE - panel expansion mode + */ +#define ATIF_FUNCTION_SET_PANEL_EXPANSION_MODE_IN_CMOS 0x8 +/* ARG0: ATIF_FUNCTION_SET_PANEL_EXPANSION_MODE_IN_CMOS + * ARG1: + * WORD - structure size in bytes (includes size field) + * BYTE - panel expansion mode + * OUTPUT: none + */ +#define ATIF_FUNCTION_TEMPERATURE_CHANGE_NOTIFICATION 0xD +/* ARG0: ATIF_FUNCTION_TEMPERATURE_CHANGE_NOTIFICATION + * ARG1: + * WORD - structure size in bytes (includes size field) + * WORD - gfx controller id + * BYTE - current temperature (degress Celsius) + * OUTPUT: none + */ +#define ATIF_FUNCTION_GET_GRAPHICS_DEVICE_TYPES 0xF +/* ARG0: ATIF_FUNCTION_GET_GRAPHICS_DEVICE_TYPES + * ARG1: none + * OUTPUT: + * WORD - number of gfx devices + * WORD - device structure size in bytes (excludes device size field) + * DWORD - flags \ + * WORD - bus number } repeated structure + * WORD - device number / + */ +/* flags */ +# define ATIF_PX_REMOVABLE_GRAPHICS_DEVICE (1 << 0) +# define ATIF_XGP_PORT (1 << 1) +# define ATIF_VGA_ENABLED_GRAPHICS_DEVICE (1 << 2) +# define ATIF_XGP_PORT_IN_DOCK (1 << 3) + +/* ATPX */ +#define ATPX_FUNCTION_VERIFY_INTERFACE 0x0 +/* ARG0: ATPX_FUNCTION_VERIFY_INTERFACE + * ARG1: none + * OUTPUT: + * WORD - structure size in bytes (includes size field) + * WORD - version + * DWORD - supported functions bit vector + */ +/* supported functions vector */ +# define ATPX_GET_PX_PARAMETERS_SUPPORTED (1 << 0) +# define ATPX_POWER_CONTROL_SUPPORTED (1 << 1) +# define ATPX_DISPLAY_MUX_CONTROL_SUPPORTED (1 << 2) +# define ATPX_I2C_MUX_CONTROL_SUPPORTED (1 << 3) +# define ATPX_GRAPHICS_DEVICE_SWITCH_START_NOTIFICATION_SUPPORTED (1 << 4) +# define ATPX_GRAPHICS_DEVICE_SWITCH_END_NOTIFICATION_SUPPORTED (1 << 5) +# define ATPX_GET_DISPLAY_CONNECTORS_MAPPING_SUPPORTED (1 << 7) +# define ATPX_GET_DISPLAY_DETECTION_PORTS_SUPPORTED (1 << 8) +#define ATPX_FUNCTION_GET_PX_PARAMETERS 0x1 +/* ARG0: ATPX_FUNCTION_GET_PX_PARAMETERS + * ARG1: none + * OUTPUT: + * WORD - structure size in bytes (includes size field) + * DWORD - valid flags mask + * DWORD - flags + */ +/* flags */ +# define ATPX_LVDS_I2C_AVAILABLE_TO_BOTH_GPUS (1 << 0) +# define ATPX_CRT1_I2C_AVAILABLE_TO_BOTH_GPUS (1 << 1) +# define ATPX_DVI1_I2C_AVAILABLE_TO_BOTH_GPUS (1 << 2) +# define ATPX_CRT1_RGB_SIGNAL_MUXED (1 << 3) +# define ATPX_TV_SIGNAL_MUXED (1 << 4) +# define ATPX_DFP_SIGNAL_MUXED (1 << 5) +# define ATPX_SEPARATE_MUX_FOR_I2C (1 << 6) +# define ATPX_DYNAMIC_PX_SUPPORTED (1 << 7) +# define ATPX_ACF_NOT_SUPPORTED (1 << 8) +# define ATPX_FIXED_NOT_SUPPORTED (1 << 9) +# define ATPX_DYNAMIC_DGPU_POWER_OFF_SUPPORTED (1 << 10) +# define ATPX_DGPU_REQ_POWER_FOR_DISPLAYS (1 << 11) +#define ATPX_FUNCTION_POWER_CONTROL 0x2 +/* ARG0: ATPX_FUNCTION_POWER_CONTROL + * ARG1: + * WORD - structure size in bytes (includes size field) + * BYTE - dGPU power state (0: power off, 1: power on) + * OUTPUT: none + */ +#define ATPX_FUNCTION_DISPLAY_MUX_CONTROL 0x3 +/* ARG0: ATPX_FUNCTION_DISPLAY_MUX_CONTROL + * ARG1: + * WORD - structure size in bytes (includes size field) + * WORD - display mux control (0: iGPU, 1: dGPU) + * OUTPUT: none + */ +# define ATPX_INTEGRATED_GPU 0 +# define ATPX_DISCRETE_GPU 1 +#define ATPX_FUNCTION_I2C_MUX_CONTROL 0x4 +/* ARG0: ATPX_FUNCTION_I2C_MUX_CONTROL + * ARG1: + * WORD - structure size in bytes (includes size field) + * WORD - i2c/aux/hpd mux control (0: iGPU, 1: dGPU) + * OUTPUT: none + */ +#define ATPX_FUNCTION_GRAPHICS_DEVICE_SWITCH_START_NOTIFICATION 0x5 +/* ARG0: ATPX_FUNCTION_GRAPHICS_DEVICE_SWITCH_START_NOTIFICATION + * ARG1: + * WORD - structure size in bytes (includes size field) + * WORD - target gpu (0: iGPU, 1: dGPU) + * OUTPUT: none + */ +#define ATPX_FUNCTION_GRAPHICS_DEVICE_SWITCH_END_NOTIFICATION 0x6 +/* ARG0: ATPX_FUNCTION_GRAPHICS_DEVICE_SWITCH_END_NOTIFICATION + * ARG1: + * WORD - structure size in bytes (includes size field) + * WORD - target gpu (0: iGPU, 1: dGPU) + * OUTPUT: none + */ +#define ATPX_FUNCTION_GET_DISPLAY_CONNECTORS_MAPPING 0x8 +/* ARG0: ATPX_FUNCTION_GET_DISPLAY_CONNECTORS_MAPPING + * ARG1: none + * OUTPUT: + * WORD - number of display connectors + * WORD - connector structure size in bytes (excludes connector size field) + * BYTE - flags \ + * BYTE - ATIF display vector bit position } repeated + * BYTE - adapter id (0: iGPU, 1-n: dGPU ordered by pcie bus number) } structure + * WORD - connector ACPI id / + */ +/* flags */ +# define ATPX_DISPLAY_OUTPUT_SUPPORTED_BY_ADAPTER_ID_DEVICE (1 << 0) +# define ATPX_DISPLAY_HPD_SUPPORTED_BY_ADAPTER_ID_DEVICE (1 << 1) +# define ATPX_DISPLAY_I2C_SUPPORTED_BY_ADAPTER_ID_DEVICE (1 << 2) +#define ATPX_FUNCTION_GET_DISPLAY_DETECTION_PORTS 0x9 +/* ARG0: ATPX_FUNCTION_GET_DISPLAY_DETECTION_PORTS + * ARG1: none + * OUTPUT: + * WORD - number of HPD/DDC ports + * WORD - port structure size in bytes (excludes port size field) + * BYTE - ATIF display vector bit position \ + * BYTE - hpd id } reapeated structure + * BYTE - ddc id / + * + * available on A+A systems only + */ +/* hpd id */ +# define ATPX_HPD_NONE 0 +# define ATPX_HPD1 1 +# define ATPX_HPD2 2 +# define ATPX_HPD3 3 +# define ATPX_HPD4 4 +# define ATPX_HPD5 5 +# define ATPX_HPD6 6 +/* ddc id */ +# define ATPX_DDC_NONE 0 +# define ATPX_DDC1 1 +# define ATPX_DDC2 2 +# define ATPX_DDC3 3 +# define ATPX_DDC4 4 +# define ATPX_DDC5 5 +# define ATPX_DDC6 6 +# define ATPX_DDC7 7 +# define ATPX_DDC8 8 + +/* ATCS */ +#define ATCS_FUNCTION_VERIFY_INTERFACE 0x0 +/* ARG0: ATCS_FUNCTION_VERIFY_INTERFACE + * ARG1: none + * OUTPUT: + * WORD - structure size in bytes (includes size field) + * WORD - version + * DWORD - supported functions bit vector + */ +/* supported functions vector */ +# define ATCS_GET_EXTERNAL_STATE_SUPPORTED (1 << 0) +# define ATCS_PCIE_PERFORMANCE_REQUEST_SUPPORTED (1 << 1) +# define ATCS_PCIE_DEVICE_READY_NOTIFICATION_SUPPORTED (1 << 2) +# define ATCS_SET_PCIE_BUS_WIDTH_SUPPORTED (1 << 3) +#define ATCS_FUNCTION_GET_EXTERNAL_STATE 0x1 +/* ARG0: ATCS_FUNCTION_GET_EXTERNAL_STATE + * ARG1: none + * OUTPUT: + * WORD - structure size in bytes (includes size field) + * DWORD - valid flags mask + * DWORD - flags (0: undocked, 1: docked) + */ +/* flags */ +# define ATCS_DOCKED (1 << 0) +#define ATCS_FUNCTION_PCIE_PERFORMANCE_REQUEST 0x2 +/* ARG0: ATCS_FUNCTION_PCIE_PERFORMANCE_REQUEST + * ARG1: + * WORD - structure size in bytes (includes size field) + * WORD - client id (bit 2-0: func num, 7-3: dev num, 15-8: bus num) + * WORD - valid flags mask + * WORD - flags + * BYTE - request type + * BYTE - performance request + * OUTPUT: + * WORD - structure size in bytes (includes size field) + * BYTE - return value + */ +/* flags */ +# define ATCS_ADVERTISE_CAPS (1 << 0) +# define ATCS_WAIT_FOR_COMPLETION (1 << 1) +/* request type */ +# define ATCS_PCIE_LINK_SPEED 1 +/* performance request */ +# define ATCS_REMOVE 0 +# define ATCS_FORCE_LOW_POWER 1 +# define ATCS_PERF_LEVEL_1 2 /* PCIE Gen 1 */ +# define ATCS_PERF_LEVEL_2 3 /* PCIE Gen 2 */ +# define ATCS_PERF_LEVEL_3 4 /* PCIE Gen 3 */ +/* return value */ +# define ATCS_REQUEST_REFUSED 1 +# define ATCS_REQUEST_COMPLETE 2 +# define ATCS_REQUEST_IN_PROGRESS 3 +#define ATCS_FUNCTION_PCIE_DEVICE_READY_NOTIFICATION 0x3 +/* ARG0: ATCS_FUNCTION_PCIE_DEVICE_READY_NOTIFICATION + * ARG1: none + * OUTPUT: none + */ +#define ATCS_FUNCTION_SET_PCIE_BUS_WIDTH 0x4 +/* ARG0: ATCS_FUNCTION_SET_PCIE_BUS_WIDTH + * ARG1: + * WORD - structure size in bytes (includes size field) + * WORD - client id (bit 2-0: func num, 7-3: dev num, 15-8: bus num) + * BYTE - number of active lanes + * OUTPUT: + * WORD - structure size in bytes (includes size field) + * BYTE - number of active lanes + */ + +#endif diff --git a/sys/dev/drm2/radeon/radeon_agp.c b/sys/dev/drm2/radeon/radeon_agp.c new file mode 100644 index 00000000000..dec4045065c --- /dev/null +++ b/sys/dev/drm2/radeon/radeon_agp.c @@ -0,0 +1,289 @@ +/* + * Copyright 2008 Red Hat Inc. + * Copyright 2009 Jerome Glisse. + * + * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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. + * + * Authors: + * Dave Airlie + * Jerome Glisse + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include "radeon.h" +#include + +#if __OS_HAS_AGP + +struct radeon_agpmode_quirk { + u32 hostbridge_vendor; + u32 hostbridge_device; + u32 chip_vendor; + u32 chip_device; + u32 subsys_vendor; + u32 subsys_device; + u32 default_mode; +}; + +static struct radeon_agpmode_quirk radeon_agpmode_quirk_list[] = { + /* Intel E7505 Memory Controller Hub / RV350 AR [Radeon 9600XT] Needs AGPMode 4 (deb #515326) */ + { PCI_VENDOR_ID_INTEL, 0x2550, PCI_VENDOR_ID_ATI, 0x4152, 0x1458, 0x4038, 4}, + /* Intel 82865G/PE/P DRAM Controller/Host-Hub / Mobility 9800 Needs AGPMode 4 (deb #462590) */ + { PCI_VENDOR_ID_INTEL, 0x2570, PCI_VENDOR_ID_ATI, 0x4a4e, PCI_VENDOR_ID_DELL, 0x5106, 4}, + /* Intel 82865G/PE/P DRAM Controller/Host-Hub / RV280 [Radeon 9200 SE] Needs AGPMode 4 (lp #300304) */ + { PCI_VENDOR_ID_INTEL, 0x2570, PCI_VENDOR_ID_ATI, 0x5964, + 0x148c, 0x2073, 4}, + /* Intel 82855PM Processor to I/O Controller / Mobility M6 LY Needs AGPMode 1 (deb #467235) */ + { PCI_VENDOR_ID_INTEL, 0x3340, PCI_VENDOR_ID_ATI, 0x4c59, + PCI_VENDOR_ID_IBM, 0x052f, 1}, + /* Intel 82855PM host bridge / Mobility 9600 M10 RV350 Needs AGPMode 1 (lp #195051) */ + { PCI_VENDOR_ID_INTEL, 0x3340, PCI_VENDOR_ID_ATI, 0x4e50, + PCI_VENDOR_ID_IBM, 0x0550, 1}, + /* Intel 82855PM host bridge / Mobility M7 needs AGPMode 1 */ + { PCI_VENDOR_ID_INTEL, 0x3340, PCI_VENDOR_ID_ATI, 0x4c57, + PCI_VENDOR_ID_IBM, 0x0530, 1}, + /* Intel 82855PM host bridge / FireGL Mobility T2 RV350 Needs AGPMode 2 (fdo #20647) */ + { PCI_VENDOR_ID_INTEL, 0x3340, PCI_VENDOR_ID_ATI, 0x4e54, + PCI_VENDOR_ID_IBM, 0x054f, 2}, + /* Intel 82855PM host bridge / Mobility M9+ / VaioPCG-V505DX Needs AGPMode 2 (fdo #17928) */ + { PCI_VENDOR_ID_INTEL, 0x3340, PCI_VENDOR_ID_ATI, 0x5c61, + PCI_VENDOR_ID_SONY, 0x816b, 2}, + /* Intel 82855PM Processor to I/O Controller / Mobility M9+ Needs AGPMode 8 (phoronix forum) */ + { PCI_VENDOR_ID_INTEL, 0x3340, PCI_VENDOR_ID_ATI, 0x5c61, + PCI_VENDOR_ID_SONY, 0x8195, 8}, + /* Intel 82830 830 Chipset Host Bridge / Mobility M6 LY Needs AGPMode 2 (fdo #17360)*/ + { PCI_VENDOR_ID_INTEL, 0x3575, PCI_VENDOR_ID_ATI, 0x4c59, + PCI_VENDOR_ID_DELL, 0x00e3, 2}, + /* Intel 82852/82855 host bridge / Mobility FireGL 9000 RV250 Needs AGPMode 1 (lp #296617) */ + { PCI_VENDOR_ID_INTEL, 0x3580, PCI_VENDOR_ID_ATI, 0x4c66, + PCI_VENDOR_ID_DELL, 0x0149, 1}, + /* Intel 82855PM host bridge / Mobility FireGL 9000 RV250 Needs AGPMode 1 for suspend/resume */ + { PCI_VENDOR_ID_INTEL, 0x3340, PCI_VENDOR_ID_ATI, 0x4c66, + PCI_VENDOR_ID_IBM, 0x0531, 1}, + /* Intel 82852/82855 host bridge / Mobility 9600 M10 RV350 Needs AGPMode 1 (deb #467460) */ + { PCI_VENDOR_ID_INTEL, 0x3580, PCI_VENDOR_ID_ATI, 0x4e50, + 0x1025, 0x0061, 1}, + /* Intel 82852/82855 host bridge / Mobility 9600 M10 RV350 Needs AGPMode 1 (lp #203007) */ + { PCI_VENDOR_ID_INTEL, 0x3580, PCI_VENDOR_ID_ATI, 0x4e50, + 0x1025, 0x0064, 1}, + /* Intel 82852/82855 host bridge / Mobility 9600 M10 RV350 Needs AGPMode 1 (lp #141551) */ + { PCI_VENDOR_ID_INTEL, 0x3580, PCI_VENDOR_ID_ATI, 0x4e50, + PCI_VENDOR_ID_ASUSTEK, 0x1942, 1}, + /* Intel 82852/82855 host bridge / Mobility 9600/9700 Needs AGPMode 1 (deb #510208) */ + { PCI_VENDOR_ID_INTEL, 0x3580, PCI_VENDOR_ID_ATI, 0x4e50, + 0x10cf, 0x127f, 1}, + /* ASRock K7VT4A+ AGP 8x / ATI Radeon 9250 AGP Needs AGPMode 4 (lp #133192) */ + { 0x1849, 0x3189, PCI_VENDOR_ID_ATI, 0x5960, + 0x1787, 0x5960, 4}, + /* VIA K8M800 Host Bridge / RV280 [Radeon 9200 PRO] Needs AGPMode 4 (fdo #12544) */ + { PCI_VENDOR_ID_VIA, 0x0204, PCI_VENDOR_ID_ATI, 0x5960, + 0x17af, 0x2020, 4}, + /* VIA KT880 Host Bridge / RV350 [Radeon 9550] Needs AGPMode 4 (fdo #19981) */ + { PCI_VENDOR_ID_VIA, 0x0269, PCI_VENDOR_ID_ATI, 0x4153, + PCI_VENDOR_ID_ASUSTEK, 0x003c, 4}, + /* VIA VT8363 Host Bridge / R200 QL [Radeon 8500] Needs AGPMode 2 (lp #141551) */ + { PCI_VENDOR_ID_VIA, 0x0305, PCI_VENDOR_ID_ATI, 0x514c, + PCI_VENDOR_ID_ATI, 0x013a, 2}, + /* VIA VT82C693A Host Bridge / RV280 [Radeon 9200 PRO] Needs AGPMode 2 (deb #515512) */ + { PCI_VENDOR_ID_VIA, 0x0691, PCI_VENDOR_ID_ATI, 0x5960, + PCI_VENDOR_ID_ASUSTEK, 0x004c, 2}, + /* VIA VT82C693A Host Bridge / RV280 [Radeon 9200 PRO] Needs AGPMode 2 */ + { PCI_VENDOR_ID_VIA, 0x0691, PCI_VENDOR_ID_ATI, 0x5960, + PCI_VENDOR_ID_ASUSTEK, 0x0054, 2}, + /* VIA VT8377 Host Bridge / R200 QM [Radeon 9100] Needs AGPMode 4 (deb #461144) */ + { PCI_VENDOR_ID_VIA, 0x3189, PCI_VENDOR_ID_ATI, 0x514d, + 0x174b, 0x7149, 4}, + /* VIA VT8377 Host Bridge / RV280 [Radeon 9200 PRO] Needs AGPMode 4 (lp #312693) */ + { PCI_VENDOR_ID_VIA, 0x3189, PCI_VENDOR_ID_ATI, 0x5960, + 0x1462, 0x0380, 4}, + /* VIA VT8377 Host Bridge / RV280 Needs AGPMode 4 (ati ML) */ + { PCI_VENDOR_ID_VIA, 0x3189, PCI_VENDOR_ID_ATI, 0x5964, + 0x148c, 0x2073, 4}, + /* ATI Host Bridge / RV280 [M9+] Needs AGPMode 1 (phoronix forum) */ + { PCI_VENDOR_ID_ATI, 0xcbb2, PCI_VENDOR_ID_ATI, 0x5c61, + PCI_VENDOR_ID_SONY, 0x8175, 1}, + /* HP Host Bridge / R300 [FireGL X1] Needs AGPMode 2 (fdo #7770) */ + { PCI_VENDOR_ID_HP, 0x122e, PCI_VENDOR_ID_ATI, 0x4e47, + PCI_VENDOR_ID_ATI, 0x0152, 2}, + { 0, 0, 0, 0, 0, 0, 0 }, +}; +#endif + +int radeon_agp_init(struct radeon_device *rdev) +{ +#if __OS_HAS_AGP + struct radeon_agpmode_quirk *p = radeon_agpmode_quirk_list; + struct drm_agp_mode mode; + struct drm_agp_info info; + uint32_t agp_status; + int default_mode; + bool is_v3; + int ret; + + /* Acquire AGP. */ + ret = drm_agp_acquire(rdev->ddev); + if (ret) { + DRM_ERROR("Unable to acquire AGP: %d\n", ret); + return ret; + } + + ret = drm_agp_info(rdev->ddev, &info); + if (ret) { + drm_agp_release(rdev->ddev); + DRM_ERROR("Unable to get AGP info: %d\n", ret); + return ret; + } + + if (rdev->ddev->agp->info.ai_aperture_size < 32) { + drm_agp_release(rdev->ddev); + dev_warn(rdev->dev, "AGP aperture too small (%zuM) " + "need at least 32M, disabling AGP\n", + rdev->ddev->agp->info.ai_aperture_size); + return -EINVAL; + } + + mode.mode = info.mode; + /* chips with the agp to pcie bridge don't have the AGP_STATUS register + * Just use the whatever mode the host sets up. + */ + if (rdev->family <= CHIP_RV350) + agp_status = (RREG32(RADEON_AGP_STATUS) | RADEON_AGPv3_MODE) & mode.mode; + else + agp_status = mode.mode; + is_v3 = !!(agp_status & RADEON_AGPv3_MODE); + + if (is_v3) { + default_mode = (agp_status & RADEON_AGPv3_8X_MODE) ? 8 : 4; + } else { + if (agp_status & RADEON_AGP_4X_MODE) { + default_mode = 4; + } else if (agp_status & RADEON_AGP_2X_MODE) { + default_mode = 2; + } else { + default_mode = 1; + } + } + + /* Apply AGPMode Quirks */ + while (p && p->chip_device != 0) { + if (info.id_vendor == p->hostbridge_vendor && + info.id_device == p->hostbridge_device && + rdev->ddev->pci_vendor == p->chip_vendor && + rdev->ddev->pci_device == p->chip_device && + rdev->ddev->pci_subvendor == p->subsys_vendor && + rdev->ddev->pci_subdevice == p->subsys_device) { + default_mode = p->default_mode; + } + ++p; + } + + if (radeon_agpmode > 0) { + if ((radeon_agpmode < (is_v3 ? 4 : 1)) || + (radeon_agpmode > (is_v3 ? 8 : 4)) || + (radeon_agpmode & (radeon_agpmode - 1))) { + DRM_ERROR("Illegal AGP Mode: %d (valid %s), leaving at %d\n", + radeon_agpmode, is_v3 ? "4, 8" : "1, 2, 4", + default_mode); + radeon_agpmode = default_mode; + } else { + DRM_INFO("AGP mode requested: %d\n", radeon_agpmode); + } + } else { + radeon_agpmode = default_mode; + } + + mode.mode &= ~RADEON_AGP_MODE_MASK; + if (is_v3) { + switch (radeon_agpmode) { + case 8: + mode.mode |= RADEON_AGPv3_8X_MODE; + break; + case 4: + default: + mode.mode |= RADEON_AGPv3_4X_MODE; + break; + } + } else { + switch (radeon_agpmode) { + case 4: + mode.mode |= RADEON_AGP_4X_MODE; + break; + case 2: + mode.mode |= RADEON_AGP_2X_MODE; + break; + case 1: + default: + mode.mode |= RADEON_AGP_1X_MODE; + break; + } + } + + mode.mode &= ~RADEON_AGP_FW_MODE; /* disable fw */ + ret = drm_agp_enable(rdev->ddev, mode); + if (ret) { + DRM_ERROR("Unable to enable AGP (mode = 0x%lx)\n", mode.mode); + drm_agp_release(rdev->ddev); + return ret; + } + + rdev->mc.agp_base = rdev->ddev->agp->info.ai_aperture_base; + rdev->mc.gtt_size = rdev->ddev->agp->info.ai_aperture_size << 20; + rdev->mc.gtt_start = rdev->mc.agp_base; + rdev->mc.gtt_end = rdev->mc.gtt_start + rdev->mc.gtt_size - 1; + dev_info(rdev->dev, "GTT: %juM 0x%08jX - 0x%08jX\n", + (uintmax_t)rdev->mc.gtt_size >> 20, (uintmax_t)rdev->mc.gtt_start, (uintmax_t)rdev->mc.gtt_end); + + /* workaround some hw issues */ + if (rdev->family < CHIP_R200) { + WREG32(RADEON_AGP_CNTL, RREG32(RADEON_AGP_CNTL) | 0x000e0000); + } + return 0; +#else + return 0; +#endif +} + +void radeon_agp_resume(struct radeon_device *rdev) +{ +#if __OS_HAS_AGP + int r; + if (rdev->flags & RADEON_IS_AGP) { + r = radeon_agp_init(rdev); + if (r) + dev_warn(rdev->dev, "radeon AGP reinit failed\n"); + } +#endif +} + +void radeon_agp_fini(struct radeon_device *rdev) +{ +#if __OS_HAS_AGP + if (rdev->ddev->agp && rdev->ddev->agp->acquired) { + drm_agp_release(rdev->ddev); + } +#endif +} + +void radeon_agp_suspend(struct radeon_device *rdev) +{ + radeon_agp_fini(rdev); +} diff --git a/sys/dev/drm2/radeon/radeon_asic.c b/sys/dev/drm2/radeon/radeon_asic.c new file mode 100644 index 00000000000..374380538ba --- /dev/null +++ b/sys/dev/drm2/radeon/radeon_asic.c @@ -0,0 +1,1961 @@ +/* + * Copyright 2008 Advanced Micro Devices, Inc. + * Copyright 2008 Red Hat Inc. + * Copyright 2009 Jerome Glisse. + * + * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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. + * + * Authors: Dave Airlie + * Alex Deucher + * Jerome Glisse + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include "radeon_reg.h" +#include "radeon.h" +#include "radeon_asic.h" +#include "atom.h" + +/* + * Registers accessors functions. + */ +/** + * radeon_invalid_rreg - dummy reg read function + * + * @rdev: radeon device pointer + * @reg: offset of register + * + * Dummy register read function. Used for register blocks + * that certain asics don't have (all asics). + * Returns the value in the register. + */ +static uint32_t radeon_invalid_rreg(struct radeon_device *rdev, uint32_t reg) +{ + panic("Invalid callback to read register 0x%04X\n", reg); + return 0; +} + +/** + * radeon_invalid_wreg - dummy reg write function + * + * @rdev: radeon device pointer + * @reg: offset of register + * @v: value to write to the register + * + * Dummy register read function. Used for register blocks + * that certain asics don't have (all asics). + */ +static void radeon_invalid_wreg(struct radeon_device *rdev, uint32_t reg, uint32_t v) +{ + panic("Invalid callback to write register 0x%04X with 0x%08X\n", + reg, v); +} + +/** + * radeon_register_accessor_init - sets up the register accessor callbacks + * + * @rdev: radeon device pointer + * + * Sets up the register accessor callbacks for various register + * apertures. Not all asics have all apertures (all asics). + */ +static void radeon_register_accessor_init(struct radeon_device *rdev) +{ + rdev->mc_rreg = &radeon_invalid_rreg; + rdev->mc_wreg = &radeon_invalid_wreg; + rdev->pll_rreg = &radeon_invalid_rreg; + rdev->pll_wreg = &radeon_invalid_wreg; + rdev->pciep_rreg = &radeon_invalid_rreg; + rdev->pciep_wreg = &radeon_invalid_wreg; + + /* Don't change order as we are overridding accessor. */ + if (rdev->family < CHIP_RV515) { + rdev->pcie_reg_mask = 0xff; + } else { + rdev->pcie_reg_mask = 0x7ff; + } + /* FIXME: not sure here */ + if (rdev->family <= CHIP_R580) { + rdev->pll_rreg = &r100_pll_rreg; + rdev->pll_wreg = &r100_pll_wreg; + } + if (rdev->family >= CHIP_R420) { + rdev->mc_rreg = &r420_mc_rreg; + rdev->mc_wreg = &r420_mc_wreg; + } + if (rdev->family >= CHIP_RV515) { + rdev->mc_rreg = &rv515_mc_rreg; + rdev->mc_wreg = &rv515_mc_wreg; + } + if (rdev->family == CHIP_RS400 || rdev->family == CHIP_RS480) { + rdev->mc_rreg = &rs400_mc_rreg; + rdev->mc_wreg = &rs400_mc_wreg; + } + if (rdev->family == CHIP_RS690 || rdev->family == CHIP_RS740) { + rdev->mc_rreg = &rs690_mc_rreg; + rdev->mc_wreg = &rs690_mc_wreg; + } + if (rdev->family == CHIP_RS600) { + rdev->mc_rreg = &rs600_mc_rreg; + rdev->mc_wreg = &rs600_mc_wreg; + } + if (rdev->family >= CHIP_R600) { + rdev->pciep_rreg = &r600_pciep_rreg; + rdev->pciep_wreg = &r600_pciep_wreg; + } +} + + +/* helper to disable agp */ +/** + * radeon_agp_disable - AGP disable helper function + * + * @rdev: radeon device pointer + * + * Removes AGP flags and changes the gart callbacks on AGP + * cards when using the internal gart rather than AGP (all asics). + */ +void radeon_agp_disable(struct radeon_device *rdev) +{ + rdev->flags &= ~RADEON_IS_AGP; + if (rdev->family >= CHIP_R600) { + DRM_INFO("Forcing AGP to PCIE mode\n"); + rdev->flags |= RADEON_IS_PCIE; + } else if (rdev->family >= CHIP_RV515 || + rdev->family == CHIP_RV380 || + rdev->family == CHIP_RV410 || + rdev->family == CHIP_R423) { + DRM_INFO("Forcing AGP to PCIE mode\n"); + rdev->flags |= RADEON_IS_PCIE; + rdev->asic->gart.tlb_flush = &rv370_pcie_gart_tlb_flush; + rdev->asic->gart.set_page = &rv370_pcie_gart_set_page; + } else { + DRM_INFO("Forcing AGP to PCI mode\n"); + rdev->flags |= RADEON_IS_PCI; + rdev->asic->gart.tlb_flush = &r100_pci_gart_tlb_flush; + rdev->asic->gart.set_page = &r100_pci_gart_set_page; + } + rdev->mc.gtt_size = radeon_gart_size * 1024 * 1024; +} + +/* + * ASIC + */ +static struct radeon_asic r100_asic = { + .init = &r100_init, + .fini = &r100_fini, + .suspend = &r100_suspend, + .resume = &r100_resume, + .vga_set_state = &r100_vga_set_state, + .asic_reset = &r100_asic_reset, + .ioctl_wait_idle = NULL, + .gui_idle = &r100_gui_idle, + .mc_wait_for_idle = &r100_mc_wait_for_idle, + .gart = { + .tlb_flush = &r100_pci_gart_tlb_flush, + .set_page = &r100_pci_gart_set_page, + }, + .ring = { + [RADEON_RING_TYPE_GFX_INDEX] = { + .ib_execute = &r100_ring_ib_execute, + .emit_fence = &r100_fence_ring_emit, + .emit_semaphore = &r100_semaphore_ring_emit, + .cs_parse = &r100_cs_parse, + .ring_start = &r100_ring_start, + .ring_test = &r100_ring_test, + .ib_test = &r100_ib_test, + .is_lockup = &r100_gpu_is_lockup, + } + }, + .irq = { + .set = &r100_irq_set, + .process = &r100_irq_process, + }, + .display = { + .bandwidth_update = &r100_bandwidth_update, + .get_vblank_counter = &r100_get_vblank_counter, + .wait_for_vblank = &r100_wait_for_vblank, + .set_backlight_level = &radeon_legacy_set_backlight_level, + .get_backlight_level = &radeon_legacy_get_backlight_level, + }, + .copy = { + .blit = &r100_copy_blit, + .blit_ring_index = RADEON_RING_TYPE_GFX_INDEX, + .dma = NULL, + .dma_ring_index = RADEON_RING_TYPE_GFX_INDEX, + .copy = &r100_copy_blit, + .copy_ring_index = RADEON_RING_TYPE_GFX_INDEX, + }, + .surface = { + .set_reg = r100_set_surface_reg, + .clear_reg = r100_clear_surface_reg, + }, + .hpd = { + .init = &r100_hpd_init, + .fini = &r100_hpd_fini, + .sense = &r100_hpd_sense, + .set_polarity = &r100_hpd_set_polarity, + }, + .pm = { + .misc = &r100_pm_misc, + .prepare = &r100_pm_prepare, + .finish = &r100_pm_finish, + .init_profile = &r100_pm_init_profile, + .get_dynpm_state = &r100_pm_get_dynpm_state, + .get_engine_clock = &radeon_legacy_get_engine_clock, + .set_engine_clock = &radeon_legacy_set_engine_clock, + .get_memory_clock = &radeon_legacy_get_memory_clock, + .set_memory_clock = NULL, + .get_pcie_lanes = NULL, + .set_pcie_lanes = NULL, + .set_clock_gating = &radeon_legacy_set_clock_gating, + }, + .pflip = { + .pre_page_flip = &r100_pre_page_flip, + .page_flip = &r100_page_flip, + .post_page_flip = &r100_post_page_flip, + }, +}; + +static struct radeon_asic r200_asic = { + .init = &r100_init, + .fini = &r100_fini, + .suspend = &r100_suspend, + .resume = &r100_resume, + .vga_set_state = &r100_vga_set_state, + .asic_reset = &r100_asic_reset, + .ioctl_wait_idle = NULL, + .gui_idle = &r100_gui_idle, + .mc_wait_for_idle = &r100_mc_wait_for_idle, + .gart = { + .tlb_flush = &r100_pci_gart_tlb_flush, + .set_page = &r100_pci_gart_set_page, + }, + .ring = { + [RADEON_RING_TYPE_GFX_INDEX] = { + .ib_execute = &r100_ring_ib_execute, + .emit_fence = &r100_fence_ring_emit, + .emit_semaphore = &r100_semaphore_ring_emit, + .cs_parse = &r100_cs_parse, + .ring_start = &r100_ring_start, + .ring_test = &r100_ring_test, + .ib_test = &r100_ib_test, + .is_lockup = &r100_gpu_is_lockup, + } + }, + .irq = { + .set = &r100_irq_set, + .process = &r100_irq_process, + }, + .display = { + .bandwidth_update = &r100_bandwidth_update, + .get_vblank_counter = &r100_get_vblank_counter, + .wait_for_vblank = &r100_wait_for_vblank, + .set_backlight_level = &radeon_legacy_set_backlight_level, + .get_backlight_level = &radeon_legacy_get_backlight_level, + }, + .copy = { + .blit = &r100_copy_blit, + .blit_ring_index = RADEON_RING_TYPE_GFX_INDEX, + .dma = &r200_copy_dma, + .dma_ring_index = RADEON_RING_TYPE_GFX_INDEX, + .copy = &r100_copy_blit, + .copy_ring_index = RADEON_RING_TYPE_GFX_INDEX, + }, + .surface = { + .set_reg = r100_set_surface_reg, + .clear_reg = r100_clear_surface_reg, + }, + .hpd = { + .init = &r100_hpd_init, + .fini = &r100_hpd_fini, + .sense = &r100_hpd_sense, + .set_polarity = &r100_hpd_set_polarity, + }, + .pm = { + .misc = &r100_pm_misc, + .prepare = &r100_pm_prepare, + .finish = &r100_pm_finish, + .init_profile = &r100_pm_init_profile, + .get_dynpm_state = &r100_pm_get_dynpm_state, + .get_engine_clock = &radeon_legacy_get_engine_clock, + .set_engine_clock = &radeon_legacy_set_engine_clock, + .get_memory_clock = &radeon_legacy_get_memory_clock, + .set_memory_clock = NULL, + .get_pcie_lanes = NULL, + .set_pcie_lanes = NULL, + .set_clock_gating = &radeon_legacy_set_clock_gating, + }, + .pflip = { + .pre_page_flip = &r100_pre_page_flip, + .page_flip = &r100_page_flip, + .post_page_flip = &r100_post_page_flip, + }, +}; + +static struct radeon_asic r300_asic = { + .init = &r300_init, + .fini = &r300_fini, + .suspend = &r300_suspend, + .resume = &r300_resume, + .vga_set_state = &r100_vga_set_state, + .asic_reset = &r300_asic_reset, + .ioctl_wait_idle = NULL, + .gui_idle = &r100_gui_idle, + .mc_wait_for_idle = &r300_mc_wait_for_idle, + .gart = { + .tlb_flush = &r100_pci_gart_tlb_flush, + .set_page = &r100_pci_gart_set_page, + }, + .ring = { + [RADEON_RING_TYPE_GFX_INDEX] = { + .ib_execute = &r100_ring_ib_execute, + .emit_fence = &r300_fence_ring_emit, + .emit_semaphore = &r100_semaphore_ring_emit, + .cs_parse = &r300_cs_parse, + .ring_start = &r300_ring_start, + .ring_test = &r100_ring_test, + .ib_test = &r100_ib_test, + .is_lockup = &r100_gpu_is_lockup, + } + }, + .irq = { + .set = &r100_irq_set, + .process = &r100_irq_process, + }, + .display = { + .bandwidth_update = &r100_bandwidth_update, + .get_vblank_counter = &r100_get_vblank_counter, + .wait_for_vblank = &r100_wait_for_vblank, + .set_backlight_level = &radeon_legacy_set_backlight_level, + .get_backlight_level = &radeon_legacy_get_backlight_level, + }, + .copy = { + .blit = &r100_copy_blit, + .blit_ring_index = RADEON_RING_TYPE_GFX_INDEX, + .dma = &r200_copy_dma, + .dma_ring_index = RADEON_RING_TYPE_GFX_INDEX, + .copy = &r100_copy_blit, + .copy_ring_index = RADEON_RING_TYPE_GFX_INDEX, + }, + .surface = { + .set_reg = r100_set_surface_reg, + .clear_reg = r100_clear_surface_reg, + }, + .hpd = { + .init = &r100_hpd_init, + .fini = &r100_hpd_fini, + .sense = &r100_hpd_sense, + .set_polarity = &r100_hpd_set_polarity, + }, + .pm = { + .misc = &r100_pm_misc, + .prepare = &r100_pm_prepare, + .finish = &r100_pm_finish, + .init_profile = &r100_pm_init_profile, + .get_dynpm_state = &r100_pm_get_dynpm_state, + .get_engine_clock = &radeon_legacy_get_engine_clock, + .set_engine_clock = &radeon_legacy_set_engine_clock, + .get_memory_clock = &radeon_legacy_get_memory_clock, + .set_memory_clock = NULL, + .get_pcie_lanes = &rv370_get_pcie_lanes, + .set_pcie_lanes = &rv370_set_pcie_lanes, + .set_clock_gating = &radeon_legacy_set_clock_gating, + }, + .pflip = { + .pre_page_flip = &r100_pre_page_flip, + .page_flip = &r100_page_flip, + .post_page_flip = &r100_post_page_flip, + }, +}; + +static struct radeon_asic r300_asic_pcie = { + .init = &r300_init, + .fini = &r300_fini, + .suspend = &r300_suspend, + .resume = &r300_resume, + .vga_set_state = &r100_vga_set_state, + .asic_reset = &r300_asic_reset, + .ioctl_wait_idle = NULL, + .gui_idle = &r100_gui_idle, + .mc_wait_for_idle = &r300_mc_wait_for_idle, + .gart = { + .tlb_flush = &rv370_pcie_gart_tlb_flush, + .set_page = &rv370_pcie_gart_set_page, + }, + .ring = { + [RADEON_RING_TYPE_GFX_INDEX] = { + .ib_execute = &r100_ring_ib_execute, + .emit_fence = &r300_fence_ring_emit, + .emit_semaphore = &r100_semaphore_ring_emit, + .cs_parse = &r300_cs_parse, + .ring_start = &r300_ring_start, + .ring_test = &r100_ring_test, + .ib_test = &r100_ib_test, + .is_lockup = &r100_gpu_is_lockup, + } + }, + .irq = { + .set = &r100_irq_set, + .process = &r100_irq_process, + }, + .display = { + .bandwidth_update = &r100_bandwidth_update, + .get_vblank_counter = &r100_get_vblank_counter, + .wait_for_vblank = &r100_wait_for_vblank, + .set_backlight_level = &radeon_legacy_set_backlight_level, + .get_backlight_level = &radeon_legacy_get_backlight_level, + }, + .copy = { + .blit = &r100_copy_blit, + .blit_ring_index = RADEON_RING_TYPE_GFX_INDEX, + .dma = &r200_copy_dma, + .dma_ring_index = RADEON_RING_TYPE_GFX_INDEX, + .copy = &r100_copy_blit, + .copy_ring_index = RADEON_RING_TYPE_GFX_INDEX, + }, + .surface = { + .set_reg = r100_set_surface_reg, + .clear_reg = r100_clear_surface_reg, + }, + .hpd = { + .init = &r100_hpd_init, + .fini = &r100_hpd_fini, + .sense = &r100_hpd_sense, + .set_polarity = &r100_hpd_set_polarity, + }, + .pm = { + .misc = &r100_pm_misc, + .prepare = &r100_pm_prepare, + .finish = &r100_pm_finish, + .init_profile = &r100_pm_init_profile, + .get_dynpm_state = &r100_pm_get_dynpm_state, + .get_engine_clock = &radeon_legacy_get_engine_clock, + .set_engine_clock = &radeon_legacy_set_engine_clock, + .get_memory_clock = &radeon_legacy_get_memory_clock, + .set_memory_clock = NULL, + .get_pcie_lanes = &rv370_get_pcie_lanes, + .set_pcie_lanes = &rv370_set_pcie_lanes, + .set_clock_gating = &radeon_legacy_set_clock_gating, + }, + .pflip = { + .pre_page_flip = &r100_pre_page_flip, + .page_flip = &r100_page_flip, + .post_page_flip = &r100_post_page_flip, + }, +}; + +static struct radeon_asic r420_asic = { + .init = &r420_init, + .fini = &r420_fini, + .suspend = &r420_suspend, + .resume = &r420_resume, + .vga_set_state = &r100_vga_set_state, + .asic_reset = &r300_asic_reset, + .ioctl_wait_idle = NULL, + .gui_idle = &r100_gui_idle, + .mc_wait_for_idle = &r300_mc_wait_for_idle, + .gart = { + .tlb_flush = &rv370_pcie_gart_tlb_flush, + .set_page = &rv370_pcie_gart_set_page, + }, + .ring = { + [RADEON_RING_TYPE_GFX_INDEX] = { + .ib_execute = &r100_ring_ib_execute, + .emit_fence = &r300_fence_ring_emit, + .emit_semaphore = &r100_semaphore_ring_emit, + .cs_parse = &r300_cs_parse, + .ring_start = &r300_ring_start, + .ring_test = &r100_ring_test, + .ib_test = &r100_ib_test, + .is_lockup = &r100_gpu_is_lockup, + } + }, + .irq = { + .set = &r100_irq_set, + .process = &r100_irq_process, + }, + .display = { + .bandwidth_update = &r100_bandwidth_update, + .get_vblank_counter = &r100_get_vblank_counter, + .wait_for_vblank = &r100_wait_for_vblank, + .set_backlight_level = &atombios_set_backlight_level, + .get_backlight_level = &atombios_get_backlight_level, + }, + .copy = { + .blit = &r100_copy_blit, + .blit_ring_index = RADEON_RING_TYPE_GFX_INDEX, + .dma = &r200_copy_dma, + .dma_ring_index = RADEON_RING_TYPE_GFX_INDEX, + .copy = &r100_copy_blit, + .copy_ring_index = RADEON_RING_TYPE_GFX_INDEX, + }, + .surface = { + .set_reg = r100_set_surface_reg, + .clear_reg = r100_clear_surface_reg, + }, + .hpd = { + .init = &r100_hpd_init, + .fini = &r100_hpd_fini, + .sense = &r100_hpd_sense, + .set_polarity = &r100_hpd_set_polarity, + }, + .pm = { + .misc = &r100_pm_misc, + .prepare = &r100_pm_prepare, + .finish = &r100_pm_finish, + .init_profile = &r420_pm_init_profile, + .get_dynpm_state = &r100_pm_get_dynpm_state, + .get_engine_clock = &radeon_atom_get_engine_clock, + .set_engine_clock = &radeon_atom_set_engine_clock, + .get_memory_clock = &radeon_atom_get_memory_clock, + .set_memory_clock = &radeon_atom_set_memory_clock, + .get_pcie_lanes = &rv370_get_pcie_lanes, + .set_pcie_lanes = &rv370_set_pcie_lanes, + .set_clock_gating = &radeon_atom_set_clock_gating, + }, + .pflip = { + .pre_page_flip = &r100_pre_page_flip, + .page_flip = &r100_page_flip, + .post_page_flip = &r100_post_page_flip, + }, +}; + +static struct radeon_asic rs400_asic = { + .init = &rs400_init, + .fini = &rs400_fini, + .suspend = &rs400_suspend, + .resume = &rs400_resume, + .vga_set_state = &r100_vga_set_state, + .asic_reset = &r300_asic_reset, + .ioctl_wait_idle = NULL, + .gui_idle = &r100_gui_idle, + .mc_wait_for_idle = &rs400_mc_wait_for_idle, + .gart = { + .tlb_flush = &rs400_gart_tlb_flush, + .set_page = &rs400_gart_set_page, + }, + .ring = { + [RADEON_RING_TYPE_GFX_INDEX] = { + .ib_execute = &r100_ring_ib_execute, + .emit_fence = &r300_fence_ring_emit, + .emit_semaphore = &r100_semaphore_ring_emit, + .cs_parse = &r300_cs_parse, + .ring_start = &r300_ring_start, + .ring_test = &r100_ring_test, + .ib_test = &r100_ib_test, + .is_lockup = &r100_gpu_is_lockup, + } + }, + .irq = { + .set = &r100_irq_set, + .process = &r100_irq_process, + }, + .display = { + .bandwidth_update = &r100_bandwidth_update, + .get_vblank_counter = &r100_get_vblank_counter, + .wait_for_vblank = &r100_wait_for_vblank, + .set_backlight_level = &radeon_legacy_set_backlight_level, + .get_backlight_level = &radeon_legacy_get_backlight_level, + }, + .copy = { + .blit = &r100_copy_blit, + .blit_ring_index = RADEON_RING_TYPE_GFX_INDEX, + .dma = &r200_copy_dma, + .dma_ring_index = RADEON_RING_TYPE_GFX_INDEX, + .copy = &r100_copy_blit, + .copy_ring_index = RADEON_RING_TYPE_GFX_INDEX, + }, + .surface = { + .set_reg = r100_set_surface_reg, + .clear_reg = r100_clear_surface_reg, + }, + .hpd = { + .init = &r100_hpd_init, + .fini = &r100_hpd_fini, + .sense = &r100_hpd_sense, + .set_polarity = &r100_hpd_set_polarity, + }, + .pm = { + .misc = &r100_pm_misc, + .prepare = &r100_pm_prepare, + .finish = &r100_pm_finish, + .init_profile = &r100_pm_init_profile, + .get_dynpm_state = &r100_pm_get_dynpm_state, + .get_engine_clock = &radeon_legacy_get_engine_clock, + .set_engine_clock = &radeon_legacy_set_engine_clock, + .get_memory_clock = &radeon_legacy_get_memory_clock, + .set_memory_clock = NULL, + .get_pcie_lanes = NULL, + .set_pcie_lanes = NULL, + .set_clock_gating = &radeon_legacy_set_clock_gating, + }, + .pflip = { + .pre_page_flip = &r100_pre_page_flip, + .page_flip = &r100_page_flip, + .post_page_flip = &r100_post_page_flip, + }, +}; + +static struct radeon_asic rs600_asic = { + .init = &rs600_init, + .fini = &rs600_fini, + .suspend = &rs600_suspend, + .resume = &rs600_resume, + .vga_set_state = &r100_vga_set_state, + .asic_reset = &rs600_asic_reset, + .ioctl_wait_idle = NULL, + .gui_idle = &r100_gui_idle, + .mc_wait_for_idle = &rs600_mc_wait_for_idle, + .gart = { + .tlb_flush = &rs600_gart_tlb_flush, + .set_page = &rs600_gart_set_page, + }, + .ring = { + [RADEON_RING_TYPE_GFX_INDEX] = { + .ib_execute = &r100_ring_ib_execute, + .emit_fence = &r300_fence_ring_emit, + .emit_semaphore = &r100_semaphore_ring_emit, + .cs_parse = &r300_cs_parse, + .ring_start = &r300_ring_start, + .ring_test = &r100_ring_test, + .ib_test = &r100_ib_test, + .is_lockup = &r100_gpu_is_lockup, + } + }, + .irq = { + .set = &rs600_irq_set, + .process = &rs600_irq_process, + }, + .display = { + .bandwidth_update = &rs600_bandwidth_update, + .get_vblank_counter = &rs600_get_vblank_counter, + .wait_for_vblank = &avivo_wait_for_vblank, + .set_backlight_level = &atombios_set_backlight_level, + .get_backlight_level = &atombios_get_backlight_level, + }, + .copy = { + .blit = &r100_copy_blit, + .blit_ring_index = RADEON_RING_TYPE_GFX_INDEX, + .dma = &r200_copy_dma, + .dma_ring_index = RADEON_RING_TYPE_GFX_INDEX, + .copy = &r100_copy_blit, + .copy_ring_index = RADEON_RING_TYPE_GFX_INDEX, + }, + .surface = { + .set_reg = r100_set_surface_reg, + .clear_reg = r100_clear_surface_reg, + }, + .hpd = { + .init = &rs600_hpd_init, + .fini = &rs600_hpd_fini, + .sense = &rs600_hpd_sense, + .set_polarity = &rs600_hpd_set_polarity, + }, + .pm = { + .misc = &rs600_pm_misc, + .prepare = &rs600_pm_prepare, + .finish = &rs600_pm_finish, + .init_profile = &r420_pm_init_profile, + .get_dynpm_state = &r100_pm_get_dynpm_state, + .get_engine_clock = &radeon_atom_get_engine_clock, + .set_engine_clock = &radeon_atom_set_engine_clock, + .get_memory_clock = &radeon_atom_get_memory_clock, + .set_memory_clock = &radeon_atom_set_memory_clock, + .get_pcie_lanes = NULL, + .set_pcie_lanes = NULL, + .set_clock_gating = &radeon_atom_set_clock_gating, + }, + .pflip = { + .pre_page_flip = &rs600_pre_page_flip, + .page_flip = &rs600_page_flip, + .post_page_flip = &rs600_post_page_flip, + }, +}; + +static struct radeon_asic rs690_asic = { + .init = &rs690_init, + .fini = &rs690_fini, + .suspend = &rs690_suspend, + .resume = &rs690_resume, + .vga_set_state = &r100_vga_set_state, + .asic_reset = &rs600_asic_reset, + .ioctl_wait_idle = NULL, + .gui_idle = &r100_gui_idle, + .mc_wait_for_idle = &rs690_mc_wait_for_idle, + .gart = { + .tlb_flush = &rs400_gart_tlb_flush, + .set_page = &rs400_gart_set_page, + }, + .ring = { + [RADEON_RING_TYPE_GFX_INDEX] = { + .ib_execute = &r100_ring_ib_execute, + .emit_fence = &r300_fence_ring_emit, + .emit_semaphore = &r100_semaphore_ring_emit, + .cs_parse = &r300_cs_parse, + .ring_start = &r300_ring_start, + .ring_test = &r100_ring_test, + .ib_test = &r100_ib_test, + .is_lockup = &r100_gpu_is_lockup, + } + }, + .irq = { + .set = &rs600_irq_set, + .process = &rs600_irq_process, + }, + .display = { + .get_vblank_counter = &rs600_get_vblank_counter, + .bandwidth_update = &rs690_bandwidth_update, + .wait_for_vblank = &avivo_wait_for_vblank, + .set_backlight_level = &atombios_set_backlight_level, + .get_backlight_level = &atombios_get_backlight_level, + }, + .copy = { + .blit = &r100_copy_blit, + .blit_ring_index = RADEON_RING_TYPE_GFX_INDEX, + .dma = &r200_copy_dma, + .dma_ring_index = RADEON_RING_TYPE_GFX_INDEX, + .copy = &r200_copy_dma, + .copy_ring_index = RADEON_RING_TYPE_GFX_INDEX, + }, + .surface = { + .set_reg = r100_set_surface_reg, + .clear_reg = r100_clear_surface_reg, + }, + .hpd = { + .init = &rs600_hpd_init, + .fini = &rs600_hpd_fini, + .sense = &rs600_hpd_sense, + .set_polarity = &rs600_hpd_set_polarity, + }, + .pm = { + .misc = &rs600_pm_misc, + .prepare = &rs600_pm_prepare, + .finish = &rs600_pm_finish, + .init_profile = &r420_pm_init_profile, + .get_dynpm_state = &r100_pm_get_dynpm_state, + .get_engine_clock = &radeon_atom_get_engine_clock, + .set_engine_clock = &radeon_atom_set_engine_clock, + .get_memory_clock = &radeon_atom_get_memory_clock, + .set_memory_clock = &radeon_atom_set_memory_clock, + .get_pcie_lanes = NULL, + .set_pcie_lanes = NULL, + .set_clock_gating = &radeon_atom_set_clock_gating, + }, + .pflip = { + .pre_page_flip = &rs600_pre_page_flip, + .page_flip = &rs600_page_flip, + .post_page_flip = &rs600_post_page_flip, + }, +}; + +static struct radeon_asic rv515_asic = { + .init = &rv515_init, + .fini = &rv515_fini, + .suspend = &rv515_suspend, + .resume = &rv515_resume, + .vga_set_state = &r100_vga_set_state, + .asic_reset = &rs600_asic_reset, + .ioctl_wait_idle = NULL, + .gui_idle = &r100_gui_idle, + .mc_wait_for_idle = &rv515_mc_wait_for_idle, + .gart = { + .tlb_flush = &rv370_pcie_gart_tlb_flush, + .set_page = &rv370_pcie_gart_set_page, + }, + .ring = { + [RADEON_RING_TYPE_GFX_INDEX] = { + .ib_execute = &r100_ring_ib_execute, + .emit_fence = &r300_fence_ring_emit, + .emit_semaphore = &r100_semaphore_ring_emit, + .cs_parse = &r300_cs_parse, + .ring_start = &rv515_ring_start, + .ring_test = &r100_ring_test, + .ib_test = &r100_ib_test, + .is_lockup = &r100_gpu_is_lockup, + } + }, + .irq = { + .set = &rs600_irq_set, + .process = &rs600_irq_process, + }, + .display = { + .get_vblank_counter = &rs600_get_vblank_counter, + .bandwidth_update = &rv515_bandwidth_update, + .wait_for_vblank = &avivo_wait_for_vblank, + .set_backlight_level = &atombios_set_backlight_level, + .get_backlight_level = &atombios_get_backlight_level, + }, + .copy = { + .blit = &r100_copy_blit, + .blit_ring_index = RADEON_RING_TYPE_GFX_INDEX, + .dma = &r200_copy_dma, + .dma_ring_index = RADEON_RING_TYPE_GFX_INDEX, + .copy = &r100_copy_blit, + .copy_ring_index = RADEON_RING_TYPE_GFX_INDEX, + }, + .surface = { + .set_reg = r100_set_surface_reg, + .clear_reg = r100_clear_surface_reg, + }, + .hpd = { + .init = &rs600_hpd_init, + .fini = &rs600_hpd_fini, + .sense = &rs600_hpd_sense, + .set_polarity = &rs600_hpd_set_polarity, + }, + .pm = { + .misc = &rs600_pm_misc, + .prepare = &rs600_pm_prepare, + .finish = &rs600_pm_finish, + .init_profile = &r420_pm_init_profile, + .get_dynpm_state = &r100_pm_get_dynpm_state, + .get_engine_clock = &radeon_atom_get_engine_clock, + .set_engine_clock = &radeon_atom_set_engine_clock, + .get_memory_clock = &radeon_atom_get_memory_clock, + .set_memory_clock = &radeon_atom_set_memory_clock, + .get_pcie_lanes = &rv370_get_pcie_lanes, + .set_pcie_lanes = &rv370_set_pcie_lanes, + .set_clock_gating = &radeon_atom_set_clock_gating, + }, + .pflip = { + .pre_page_flip = &rs600_pre_page_flip, + .page_flip = &rs600_page_flip, + .post_page_flip = &rs600_post_page_flip, + }, +}; + +static struct radeon_asic r520_asic = { + .init = &r520_init, + .fini = &rv515_fini, + .suspend = &rv515_suspend, + .resume = &r520_resume, + .vga_set_state = &r100_vga_set_state, + .asic_reset = &rs600_asic_reset, + .ioctl_wait_idle = NULL, + .gui_idle = &r100_gui_idle, + .mc_wait_for_idle = &r520_mc_wait_for_idle, + .gart = { + .tlb_flush = &rv370_pcie_gart_tlb_flush, + .set_page = &rv370_pcie_gart_set_page, + }, + .ring = { + [RADEON_RING_TYPE_GFX_INDEX] = { + .ib_execute = &r100_ring_ib_execute, + .emit_fence = &r300_fence_ring_emit, + .emit_semaphore = &r100_semaphore_ring_emit, + .cs_parse = &r300_cs_parse, + .ring_start = &rv515_ring_start, + .ring_test = &r100_ring_test, + .ib_test = &r100_ib_test, + .is_lockup = &r100_gpu_is_lockup, + } + }, + .irq = { + .set = &rs600_irq_set, + .process = &rs600_irq_process, + }, + .display = { + .bandwidth_update = &rv515_bandwidth_update, + .get_vblank_counter = &rs600_get_vblank_counter, + .wait_for_vblank = &avivo_wait_for_vblank, + .set_backlight_level = &atombios_set_backlight_level, + .get_backlight_level = &atombios_get_backlight_level, + }, + .copy = { + .blit = &r100_copy_blit, + .blit_ring_index = RADEON_RING_TYPE_GFX_INDEX, + .dma = &r200_copy_dma, + .dma_ring_index = RADEON_RING_TYPE_GFX_INDEX, + .copy = &r100_copy_blit, + .copy_ring_index = RADEON_RING_TYPE_GFX_INDEX, + }, + .surface = { + .set_reg = r100_set_surface_reg, + .clear_reg = r100_clear_surface_reg, + }, + .hpd = { + .init = &rs600_hpd_init, + .fini = &rs600_hpd_fini, + .sense = &rs600_hpd_sense, + .set_polarity = &rs600_hpd_set_polarity, + }, + .pm = { + .misc = &rs600_pm_misc, + .prepare = &rs600_pm_prepare, + .finish = &rs600_pm_finish, + .init_profile = &r420_pm_init_profile, + .get_dynpm_state = &r100_pm_get_dynpm_state, + .get_engine_clock = &radeon_atom_get_engine_clock, + .set_engine_clock = &radeon_atom_set_engine_clock, + .get_memory_clock = &radeon_atom_get_memory_clock, + .set_memory_clock = &radeon_atom_set_memory_clock, + .get_pcie_lanes = &rv370_get_pcie_lanes, + .set_pcie_lanes = &rv370_set_pcie_lanes, + .set_clock_gating = &radeon_atom_set_clock_gating, + }, + .pflip = { + .pre_page_flip = &rs600_pre_page_flip, + .page_flip = &rs600_page_flip, + .post_page_flip = &rs600_post_page_flip, + }, +}; + +static struct radeon_asic r600_asic = { + .init = &r600_init, + .fini = &r600_fini, + .suspend = &r600_suspend, + .resume = &r600_resume, + .vga_set_state = &r600_vga_set_state, + .asic_reset = &r600_asic_reset, + .ioctl_wait_idle = r600_ioctl_wait_idle, + .gui_idle = &r600_gui_idle, + .mc_wait_for_idle = &r600_mc_wait_for_idle, + .gart = { + .tlb_flush = &r600_pcie_gart_tlb_flush, + .set_page = &rs600_gart_set_page, + }, + .ring = { + [RADEON_RING_TYPE_GFX_INDEX] = { + .ib_execute = &r600_ring_ib_execute, + .emit_fence = &r600_fence_ring_emit, + .emit_semaphore = &r600_semaphore_ring_emit, + .cs_parse = &r600_cs_parse, + .ring_test = &r600_ring_test, + .ib_test = &r600_ib_test, + .is_lockup = &r600_gpu_is_lockup, + }, + [R600_RING_TYPE_DMA_INDEX] = { + .ib_execute = &r600_dma_ring_ib_execute, + .emit_fence = &r600_dma_fence_ring_emit, + .emit_semaphore = &r600_dma_semaphore_ring_emit, + .cs_parse = &r600_dma_cs_parse, + .ring_test = &r600_dma_ring_test, + .ib_test = &r600_dma_ib_test, + .is_lockup = &r600_dma_is_lockup, + } + }, + .irq = { + .set = &r600_irq_set, + .process = &r600_irq_process, + }, + .display = { + .bandwidth_update = &rv515_bandwidth_update, + .get_vblank_counter = &rs600_get_vblank_counter, + .wait_for_vblank = &avivo_wait_for_vblank, + .set_backlight_level = &atombios_set_backlight_level, + .get_backlight_level = &atombios_get_backlight_level, + }, + .copy = { + .blit = &r600_copy_blit, + .blit_ring_index = RADEON_RING_TYPE_GFX_INDEX, + .dma = &r600_copy_dma, + .dma_ring_index = R600_RING_TYPE_DMA_INDEX, + .copy = &r600_copy_dma, + .copy_ring_index = R600_RING_TYPE_DMA_INDEX, + }, + .surface = { + .set_reg = r600_set_surface_reg, + .clear_reg = r600_clear_surface_reg, + }, + .hpd = { + .init = &r600_hpd_init, + .fini = &r600_hpd_fini, + .sense = &r600_hpd_sense, + .set_polarity = &r600_hpd_set_polarity, + }, + .pm = { + .misc = &r600_pm_misc, + .prepare = &rs600_pm_prepare, + .finish = &rs600_pm_finish, + .init_profile = &r600_pm_init_profile, + .get_dynpm_state = &r600_pm_get_dynpm_state, + .get_engine_clock = &radeon_atom_get_engine_clock, + .set_engine_clock = &radeon_atom_set_engine_clock, + .get_memory_clock = &radeon_atom_get_memory_clock, + .set_memory_clock = &radeon_atom_set_memory_clock, + .get_pcie_lanes = &r600_get_pcie_lanes, + .set_pcie_lanes = &r600_set_pcie_lanes, + .set_clock_gating = NULL, + }, + .pflip = { + .pre_page_flip = &rs600_pre_page_flip, + .page_flip = &rs600_page_flip, + .post_page_flip = &rs600_post_page_flip, + }, +}; + +static struct radeon_asic rs780_asic = { + .init = &r600_init, + .fini = &r600_fini, + .suspend = &r600_suspend, + .resume = &r600_resume, + .vga_set_state = &r600_vga_set_state, + .asic_reset = &r600_asic_reset, + .ioctl_wait_idle = r600_ioctl_wait_idle, + .gui_idle = &r600_gui_idle, + .mc_wait_for_idle = &r600_mc_wait_for_idle, + .gart = { + .tlb_flush = &r600_pcie_gart_tlb_flush, + .set_page = &rs600_gart_set_page, + }, + .ring = { + [RADEON_RING_TYPE_GFX_INDEX] = { + .ib_execute = &r600_ring_ib_execute, + .emit_fence = &r600_fence_ring_emit, + .emit_semaphore = &r600_semaphore_ring_emit, + .cs_parse = &r600_cs_parse, + .ring_test = &r600_ring_test, + .ib_test = &r600_ib_test, + .is_lockup = &r600_gpu_is_lockup, + }, + [R600_RING_TYPE_DMA_INDEX] = { + .ib_execute = &r600_dma_ring_ib_execute, + .emit_fence = &r600_dma_fence_ring_emit, + .emit_semaphore = &r600_dma_semaphore_ring_emit, + .cs_parse = &r600_dma_cs_parse, + .ring_test = &r600_dma_ring_test, + .ib_test = &r600_dma_ib_test, + .is_lockup = &r600_dma_is_lockup, + } + }, + .irq = { + .set = &r600_irq_set, + .process = &r600_irq_process, + }, + .display = { + .bandwidth_update = &rs690_bandwidth_update, + .get_vblank_counter = &rs600_get_vblank_counter, + .wait_for_vblank = &avivo_wait_for_vblank, + .set_backlight_level = &atombios_set_backlight_level, + .get_backlight_level = &atombios_get_backlight_level, + }, + .copy = { + .blit = &r600_copy_blit, + .blit_ring_index = RADEON_RING_TYPE_GFX_INDEX, + .dma = &r600_copy_dma, + .dma_ring_index = R600_RING_TYPE_DMA_INDEX, + .copy = &r600_copy_dma, + .copy_ring_index = R600_RING_TYPE_DMA_INDEX, + }, + .surface = { + .set_reg = r600_set_surface_reg, + .clear_reg = r600_clear_surface_reg, + }, + .hpd = { + .init = &r600_hpd_init, + .fini = &r600_hpd_fini, + .sense = &r600_hpd_sense, + .set_polarity = &r600_hpd_set_polarity, + }, + .pm = { + .misc = &r600_pm_misc, + .prepare = &rs600_pm_prepare, + .finish = &rs600_pm_finish, + .init_profile = &rs780_pm_init_profile, + .get_dynpm_state = &r600_pm_get_dynpm_state, + .get_engine_clock = &radeon_atom_get_engine_clock, + .set_engine_clock = &radeon_atom_set_engine_clock, + .get_memory_clock = NULL, + .set_memory_clock = NULL, + .get_pcie_lanes = NULL, + .set_pcie_lanes = NULL, + .set_clock_gating = NULL, + }, + .pflip = { + .pre_page_flip = &rs600_pre_page_flip, + .page_flip = &rs600_page_flip, + .post_page_flip = &rs600_post_page_flip, + }, +}; + +static struct radeon_asic rv770_asic = { + .init = &rv770_init, + .fini = &rv770_fini, + .suspend = &rv770_suspend, + .resume = &rv770_resume, + .asic_reset = &r600_asic_reset, + .vga_set_state = &r600_vga_set_state, + .ioctl_wait_idle = r600_ioctl_wait_idle, + .gui_idle = &r600_gui_idle, + .mc_wait_for_idle = &r600_mc_wait_for_idle, + .gart = { + .tlb_flush = &r600_pcie_gart_tlb_flush, + .set_page = &rs600_gart_set_page, + }, + .ring = { + [RADEON_RING_TYPE_GFX_INDEX] = { + .ib_execute = &r600_ring_ib_execute, + .emit_fence = &r600_fence_ring_emit, + .emit_semaphore = &r600_semaphore_ring_emit, + .cs_parse = &r600_cs_parse, + .ring_test = &r600_ring_test, + .ib_test = &r600_ib_test, + .is_lockup = &r600_gpu_is_lockup, + }, + [R600_RING_TYPE_DMA_INDEX] = { + .ib_execute = &r600_dma_ring_ib_execute, + .emit_fence = &r600_dma_fence_ring_emit, + .emit_semaphore = &r600_dma_semaphore_ring_emit, + .cs_parse = &r600_dma_cs_parse, + .ring_test = &r600_dma_ring_test, + .ib_test = &r600_dma_ib_test, + .is_lockup = &r600_dma_is_lockup, + } + }, + .irq = { + .set = &r600_irq_set, + .process = &r600_irq_process, + }, + .display = { + .bandwidth_update = &rv515_bandwidth_update, + .get_vblank_counter = &rs600_get_vblank_counter, + .wait_for_vblank = &avivo_wait_for_vblank, + .set_backlight_level = &atombios_set_backlight_level, + .get_backlight_level = &atombios_get_backlight_level, + }, + .copy = { + .blit = &r600_copy_blit, + .blit_ring_index = RADEON_RING_TYPE_GFX_INDEX, + .dma = &rv770_copy_dma, + .dma_ring_index = R600_RING_TYPE_DMA_INDEX, + .copy = &rv770_copy_dma, + .copy_ring_index = R600_RING_TYPE_DMA_INDEX, + }, + .surface = { + .set_reg = r600_set_surface_reg, + .clear_reg = r600_clear_surface_reg, + }, + .hpd = { + .init = &r600_hpd_init, + .fini = &r600_hpd_fini, + .sense = &r600_hpd_sense, + .set_polarity = &r600_hpd_set_polarity, + }, + .pm = { + .misc = &rv770_pm_misc, + .prepare = &rs600_pm_prepare, + .finish = &rs600_pm_finish, + .init_profile = &r600_pm_init_profile, + .get_dynpm_state = &r600_pm_get_dynpm_state, + .get_engine_clock = &radeon_atom_get_engine_clock, + .set_engine_clock = &radeon_atom_set_engine_clock, + .get_memory_clock = &radeon_atom_get_memory_clock, + .set_memory_clock = &radeon_atom_set_memory_clock, + .get_pcie_lanes = &r600_get_pcie_lanes, + .set_pcie_lanes = &r600_set_pcie_lanes, + .set_clock_gating = &radeon_atom_set_clock_gating, + }, + .pflip = { + .pre_page_flip = &rs600_pre_page_flip, + .page_flip = &rv770_page_flip, + .post_page_flip = &rs600_post_page_flip, + }, +}; + +static struct radeon_asic evergreen_asic = { + .init = &evergreen_init, + .fini = &evergreen_fini, + .suspend = &evergreen_suspend, + .resume = &evergreen_resume, + .asic_reset = &evergreen_asic_reset, + .vga_set_state = &r600_vga_set_state, + .ioctl_wait_idle = r600_ioctl_wait_idle, + .gui_idle = &r600_gui_idle, + .mc_wait_for_idle = &evergreen_mc_wait_for_idle, + .gart = { + .tlb_flush = &evergreen_pcie_gart_tlb_flush, + .set_page = &rs600_gart_set_page, + }, + .ring = { + [RADEON_RING_TYPE_GFX_INDEX] = { + .ib_execute = &evergreen_ring_ib_execute, + .emit_fence = &r600_fence_ring_emit, + .emit_semaphore = &r600_semaphore_ring_emit, + .cs_parse = &evergreen_cs_parse, + .ring_test = &r600_ring_test, + .ib_test = &r600_ib_test, + .is_lockup = &evergreen_gpu_is_lockup, + }, + [R600_RING_TYPE_DMA_INDEX] = { + .ib_execute = &evergreen_dma_ring_ib_execute, + .emit_fence = &evergreen_dma_fence_ring_emit, + .emit_semaphore = &r600_dma_semaphore_ring_emit, + .cs_parse = &evergreen_dma_cs_parse, + .ring_test = &r600_dma_ring_test, + .ib_test = &r600_dma_ib_test, + .is_lockup = &r600_dma_is_lockup, + } + }, + .irq = { + .set = &evergreen_irq_set, + .process = &evergreen_irq_process, + }, + .display = { + .bandwidth_update = &evergreen_bandwidth_update, + .get_vblank_counter = &evergreen_get_vblank_counter, + .wait_for_vblank = &dce4_wait_for_vblank, + .set_backlight_level = &atombios_set_backlight_level, + .get_backlight_level = &atombios_get_backlight_level, + }, + .copy = { + .blit = &r600_copy_blit, + .blit_ring_index = RADEON_RING_TYPE_GFX_INDEX, + .dma = &evergreen_copy_dma, + .dma_ring_index = R600_RING_TYPE_DMA_INDEX, + .copy = &evergreen_copy_dma, + .copy_ring_index = R600_RING_TYPE_DMA_INDEX, + }, + .surface = { + .set_reg = r600_set_surface_reg, + .clear_reg = r600_clear_surface_reg, + }, + .hpd = { + .init = &evergreen_hpd_init, + .fini = &evergreen_hpd_fini, + .sense = &evergreen_hpd_sense, + .set_polarity = &evergreen_hpd_set_polarity, + }, + .pm = { + .misc = &evergreen_pm_misc, + .prepare = &evergreen_pm_prepare, + .finish = &evergreen_pm_finish, + .init_profile = &r600_pm_init_profile, + .get_dynpm_state = &r600_pm_get_dynpm_state, + .get_engine_clock = &radeon_atom_get_engine_clock, + .set_engine_clock = &radeon_atom_set_engine_clock, + .get_memory_clock = &radeon_atom_get_memory_clock, + .set_memory_clock = &radeon_atom_set_memory_clock, + .get_pcie_lanes = &r600_get_pcie_lanes, + .set_pcie_lanes = &r600_set_pcie_lanes, + .set_clock_gating = NULL, + }, + .pflip = { + .pre_page_flip = &evergreen_pre_page_flip, + .page_flip = &evergreen_page_flip, + .post_page_flip = &evergreen_post_page_flip, + }, +}; + +static struct radeon_asic sumo_asic = { + .init = &evergreen_init, + .fini = &evergreen_fini, + .suspend = &evergreen_suspend, + .resume = &evergreen_resume, + .asic_reset = &evergreen_asic_reset, + .vga_set_state = &r600_vga_set_state, + .ioctl_wait_idle = r600_ioctl_wait_idle, + .gui_idle = &r600_gui_idle, + .mc_wait_for_idle = &evergreen_mc_wait_for_idle, + .gart = { + .tlb_flush = &evergreen_pcie_gart_tlb_flush, + .set_page = &rs600_gart_set_page, + }, + .ring = { + [RADEON_RING_TYPE_GFX_INDEX] = { + .ib_execute = &evergreen_ring_ib_execute, + .emit_fence = &r600_fence_ring_emit, + .emit_semaphore = &r600_semaphore_ring_emit, + .cs_parse = &evergreen_cs_parse, + .ring_test = &r600_ring_test, + .ib_test = &r600_ib_test, + .is_lockup = &evergreen_gpu_is_lockup, + }, + [R600_RING_TYPE_DMA_INDEX] = { + .ib_execute = &evergreen_dma_ring_ib_execute, + .emit_fence = &evergreen_dma_fence_ring_emit, + .emit_semaphore = &r600_dma_semaphore_ring_emit, + .cs_parse = &evergreen_dma_cs_parse, + .ring_test = &r600_dma_ring_test, + .ib_test = &r600_dma_ib_test, + .is_lockup = &r600_dma_is_lockup, + } + }, + .irq = { + .set = &evergreen_irq_set, + .process = &evergreen_irq_process, + }, + .display = { + .bandwidth_update = &evergreen_bandwidth_update, + .get_vblank_counter = &evergreen_get_vblank_counter, + .wait_for_vblank = &dce4_wait_for_vblank, + .set_backlight_level = &atombios_set_backlight_level, + .get_backlight_level = &atombios_get_backlight_level, + }, + .copy = { + .blit = &r600_copy_blit, + .blit_ring_index = RADEON_RING_TYPE_GFX_INDEX, + .dma = &evergreen_copy_dma, + .dma_ring_index = R600_RING_TYPE_DMA_INDEX, + .copy = &evergreen_copy_dma, + .copy_ring_index = R600_RING_TYPE_DMA_INDEX, + }, + .surface = { + .set_reg = r600_set_surface_reg, + .clear_reg = r600_clear_surface_reg, + }, + .hpd = { + .init = &evergreen_hpd_init, + .fini = &evergreen_hpd_fini, + .sense = &evergreen_hpd_sense, + .set_polarity = &evergreen_hpd_set_polarity, + }, + .pm = { + .misc = &evergreen_pm_misc, + .prepare = &evergreen_pm_prepare, + .finish = &evergreen_pm_finish, + .init_profile = &sumo_pm_init_profile, + .get_dynpm_state = &r600_pm_get_dynpm_state, + .get_engine_clock = &radeon_atom_get_engine_clock, + .set_engine_clock = &radeon_atom_set_engine_clock, + .get_memory_clock = NULL, + .set_memory_clock = NULL, + .get_pcie_lanes = NULL, + .set_pcie_lanes = NULL, + .set_clock_gating = NULL, + }, + .pflip = { + .pre_page_flip = &evergreen_pre_page_flip, + .page_flip = &evergreen_page_flip, + .post_page_flip = &evergreen_post_page_flip, + }, +}; + +static struct radeon_asic btc_asic = { + .init = &evergreen_init, + .fini = &evergreen_fini, + .suspend = &evergreen_suspend, + .resume = &evergreen_resume, + .asic_reset = &evergreen_asic_reset, + .vga_set_state = &r600_vga_set_state, + .ioctl_wait_idle = r600_ioctl_wait_idle, + .gui_idle = &r600_gui_idle, + .mc_wait_for_idle = &evergreen_mc_wait_for_idle, + .gart = { + .tlb_flush = &evergreen_pcie_gart_tlb_flush, + .set_page = &rs600_gart_set_page, + }, + .ring = { + [RADEON_RING_TYPE_GFX_INDEX] = { + .ib_execute = &evergreen_ring_ib_execute, + .emit_fence = &r600_fence_ring_emit, + .emit_semaphore = &r600_semaphore_ring_emit, + .cs_parse = &evergreen_cs_parse, + .ring_test = &r600_ring_test, + .ib_test = &r600_ib_test, + .is_lockup = &evergreen_gpu_is_lockup, + }, + [R600_RING_TYPE_DMA_INDEX] = { + .ib_execute = &evergreen_dma_ring_ib_execute, + .emit_fence = &evergreen_dma_fence_ring_emit, + .emit_semaphore = &r600_dma_semaphore_ring_emit, + .cs_parse = &evergreen_dma_cs_parse, + .ring_test = &r600_dma_ring_test, + .ib_test = &r600_dma_ib_test, + .is_lockup = &r600_dma_is_lockup, + } + }, + .irq = { + .set = &evergreen_irq_set, + .process = &evergreen_irq_process, + }, + .display = { + .bandwidth_update = &evergreen_bandwidth_update, + .get_vblank_counter = &evergreen_get_vblank_counter, + .wait_for_vblank = &dce4_wait_for_vblank, + .set_backlight_level = &atombios_set_backlight_level, + .get_backlight_level = &atombios_get_backlight_level, + }, + .copy = { + .blit = &r600_copy_blit, + .blit_ring_index = RADEON_RING_TYPE_GFX_INDEX, + .dma = &evergreen_copy_dma, + .dma_ring_index = R600_RING_TYPE_DMA_INDEX, + .copy = &evergreen_copy_dma, + .copy_ring_index = R600_RING_TYPE_DMA_INDEX, + }, + .surface = { + .set_reg = r600_set_surface_reg, + .clear_reg = r600_clear_surface_reg, + }, + .hpd = { + .init = &evergreen_hpd_init, + .fini = &evergreen_hpd_fini, + .sense = &evergreen_hpd_sense, + .set_polarity = &evergreen_hpd_set_polarity, + }, + .pm = { + .misc = &evergreen_pm_misc, + .prepare = &evergreen_pm_prepare, + .finish = &evergreen_pm_finish, + .init_profile = &btc_pm_init_profile, + .get_dynpm_state = &r600_pm_get_dynpm_state, + .get_engine_clock = &radeon_atom_get_engine_clock, + .set_engine_clock = &radeon_atom_set_engine_clock, + .get_memory_clock = &radeon_atom_get_memory_clock, + .set_memory_clock = &radeon_atom_set_memory_clock, + .get_pcie_lanes = NULL, + .set_pcie_lanes = NULL, + .set_clock_gating = NULL, + }, + .pflip = { + .pre_page_flip = &evergreen_pre_page_flip, + .page_flip = &evergreen_page_flip, + .post_page_flip = &evergreen_post_page_flip, + }, +}; + +static struct radeon_asic cayman_asic = { + .init = &cayman_init, + .fini = &cayman_fini, + .suspend = &cayman_suspend, + .resume = &cayman_resume, + .asic_reset = &cayman_asic_reset, + .vga_set_state = &r600_vga_set_state, + .ioctl_wait_idle = r600_ioctl_wait_idle, + .gui_idle = &r600_gui_idle, + .mc_wait_for_idle = &evergreen_mc_wait_for_idle, + .gart = { + .tlb_flush = &cayman_pcie_gart_tlb_flush, + .set_page = &rs600_gart_set_page, + }, + .vm = { + .init = &cayman_vm_init, + .fini = &cayman_vm_fini, + .pt_ring_index = RADEON_RING_TYPE_GFX_INDEX, + .set_page = &cayman_vm_set_page, + }, + .ring = { + [RADEON_RING_TYPE_GFX_INDEX] = { + .ib_execute = &cayman_ring_ib_execute, + .ib_parse = &evergreen_ib_parse, + .emit_fence = &cayman_fence_ring_emit, + .emit_semaphore = &r600_semaphore_ring_emit, + .cs_parse = &evergreen_cs_parse, + .ring_test = &r600_ring_test, + .ib_test = &r600_ib_test, + .is_lockup = &evergreen_gpu_is_lockup, + .vm_flush = &cayman_vm_flush, + }, + [CAYMAN_RING_TYPE_CP1_INDEX] = { + .ib_execute = &cayman_ring_ib_execute, + .ib_parse = &evergreen_ib_parse, + .emit_fence = &cayman_fence_ring_emit, + .emit_semaphore = &r600_semaphore_ring_emit, + .cs_parse = &evergreen_cs_parse, + .ring_test = &r600_ring_test, + .ib_test = &r600_ib_test, + .is_lockup = &evergreen_gpu_is_lockup, + .vm_flush = &cayman_vm_flush, + }, + [CAYMAN_RING_TYPE_CP2_INDEX] = { + .ib_execute = &cayman_ring_ib_execute, + .ib_parse = &evergreen_ib_parse, + .emit_fence = &cayman_fence_ring_emit, + .emit_semaphore = &r600_semaphore_ring_emit, + .cs_parse = &evergreen_cs_parse, + .ring_test = &r600_ring_test, + .ib_test = &r600_ib_test, + .is_lockup = &evergreen_gpu_is_lockup, + .vm_flush = &cayman_vm_flush, + }, + [R600_RING_TYPE_DMA_INDEX] = { + .ib_execute = &cayman_dma_ring_ib_execute, + .ib_parse = &evergreen_dma_ib_parse, + .emit_fence = &evergreen_dma_fence_ring_emit, + .emit_semaphore = &r600_dma_semaphore_ring_emit, + .cs_parse = &evergreen_dma_cs_parse, + .ring_test = &r600_dma_ring_test, + .ib_test = &r600_dma_ib_test, + .is_lockup = &cayman_dma_is_lockup, + .vm_flush = &cayman_dma_vm_flush, + }, + [CAYMAN_RING_TYPE_DMA1_INDEX] = { + .ib_execute = &cayman_dma_ring_ib_execute, + .ib_parse = &evergreen_dma_ib_parse, + .emit_fence = &evergreen_dma_fence_ring_emit, + .emit_semaphore = &r600_dma_semaphore_ring_emit, + .cs_parse = &evergreen_dma_cs_parse, + .ring_test = &r600_dma_ring_test, + .ib_test = &r600_dma_ib_test, + .is_lockup = &cayman_dma_is_lockup, + .vm_flush = &cayman_dma_vm_flush, + } + }, + .irq = { + .set = &evergreen_irq_set, + .process = &evergreen_irq_process, + }, + .display = { + .bandwidth_update = &evergreen_bandwidth_update, + .get_vblank_counter = &evergreen_get_vblank_counter, + .wait_for_vblank = &dce4_wait_for_vblank, + .set_backlight_level = &atombios_set_backlight_level, + .get_backlight_level = &atombios_get_backlight_level, + }, + .copy = { + .blit = &r600_copy_blit, + .blit_ring_index = RADEON_RING_TYPE_GFX_INDEX, + .dma = &evergreen_copy_dma, + .dma_ring_index = R600_RING_TYPE_DMA_INDEX, + .copy = &evergreen_copy_dma, + .copy_ring_index = R600_RING_TYPE_DMA_INDEX, + }, + .surface = { + .set_reg = r600_set_surface_reg, + .clear_reg = r600_clear_surface_reg, + }, + .hpd = { + .init = &evergreen_hpd_init, + .fini = &evergreen_hpd_fini, + .sense = &evergreen_hpd_sense, + .set_polarity = &evergreen_hpd_set_polarity, + }, + .pm = { + .misc = &evergreen_pm_misc, + .prepare = &evergreen_pm_prepare, + .finish = &evergreen_pm_finish, + .init_profile = &btc_pm_init_profile, + .get_dynpm_state = &r600_pm_get_dynpm_state, + .get_engine_clock = &radeon_atom_get_engine_clock, + .set_engine_clock = &radeon_atom_set_engine_clock, + .get_memory_clock = &radeon_atom_get_memory_clock, + .set_memory_clock = &radeon_atom_set_memory_clock, + .get_pcie_lanes = NULL, + .set_pcie_lanes = NULL, + .set_clock_gating = NULL, + }, + .pflip = { + .pre_page_flip = &evergreen_pre_page_flip, + .page_flip = &evergreen_page_flip, + .post_page_flip = &evergreen_post_page_flip, + }, +}; + +static struct radeon_asic trinity_asic = { + .init = &cayman_init, + .fini = &cayman_fini, + .suspend = &cayman_suspend, + .resume = &cayman_resume, + .asic_reset = &cayman_asic_reset, + .vga_set_state = &r600_vga_set_state, + .ioctl_wait_idle = r600_ioctl_wait_idle, + .gui_idle = &r600_gui_idle, + .mc_wait_for_idle = &evergreen_mc_wait_for_idle, + .gart = { + .tlb_flush = &cayman_pcie_gart_tlb_flush, + .set_page = &rs600_gart_set_page, + }, + .vm = { + .init = &cayman_vm_init, + .fini = &cayman_vm_fini, + .pt_ring_index = RADEON_RING_TYPE_GFX_INDEX, + .set_page = &cayman_vm_set_page, + }, + .ring = { + [RADEON_RING_TYPE_GFX_INDEX] = { + .ib_execute = &cayman_ring_ib_execute, + .ib_parse = &evergreen_ib_parse, + .emit_fence = &cayman_fence_ring_emit, + .emit_semaphore = &r600_semaphore_ring_emit, + .cs_parse = &evergreen_cs_parse, + .ring_test = &r600_ring_test, + .ib_test = &r600_ib_test, + .is_lockup = &evergreen_gpu_is_lockup, + .vm_flush = &cayman_vm_flush, + }, + [CAYMAN_RING_TYPE_CP1_INDEX] = { + .ib_execute = &cayman_ring_ib_execute, + .ib_parse = &evergreen_ib_parse, + .emit_fence = &cayman_fence_ring_emit, + .emit_semaphore = &r600_semaphore_ring_emit, + .cs_parse = &evergreen_cs_parse, + .ring_test = &r600_ring_test, + .ib_test = &r600_ib_test, + .is_lockup = &evergreen_gpu_is_lockup, + .vm_flush = &cayman_vm_flush, + }, + [CAYMAN_RING_TYPE_CP2_INDEX] = { + .ib_execute = &cayman_ring_ib_execute, + .ib_parse = &evergreen_ib_parse, + .emit_fence = &cayman_fence_ring_emit, + .emit_semaphore = &r600_semaphore_ring_emit, + .cs_parse = &evergreen_cs_parse, + .ring_test = &r600_ring_test, + .ib_test = &r600_ib_test, + .is_lockup = &evergreen_gpu_is_lockup, + .vm_flush = &cayman_vm_flush, + }, + [R600_RING_TYPE_DMA_INDEX] = { + .ib_execute = &cayman_dma_ring_ib_execute, + .ib_parse = &evergreen_dma_ib_parse, + .emit_fence = &evergreen_dma_fence_ring_emit, + .emit_semaphore = &r600_dma_semaphore_ring_emit, + .cs_parse = &evergreen_dma_cs_parse, + .ring_test = &r600_dma_ring_test, + .ib_test = &r600_dma_ib_test, + .is_lockup = &cayman_dma_is_lockup, + .vm_flush = &cayman_dma_vm_flush, + }, + [CAYMAN_RING_TYPE_DMA1_INDEX] = { + .ib_execute = &cayman_dma_ring_ib_execute, + .ib_parse = &evergreen_dma_ib_parse, + .emit_fence = &evergreen_dma_fence_ring_emit, + .emit_semaphore = &r600_dma_semaphore_ring_emit, + .cs_parse = &evergreen_dma_cs_parse, + .ring_test = &r600_dma_ring_test, + .ib_test = &r600_dma_ib_test, + .is_lockup = &cayman_dma_is_lockup, + .vm_flush = &cayman_dma_vm_flush, + } + }, + .irq = { + .set = &evergreen_irq_set, + .process = &evergreen_irq_process, + }, + .display = { + .bandwidth_update = &dce6_bandwidth_update, + .get_vblank_counter = &evergreen_get_vblank_counter, + .wait_for_vblank = &dce4_wait_for_vblank, + .set_backlight_level = &atombios_set_backlight_level, + .get_backlight_level = &atombios_get_backlight_level, + }, + .copy = { + .blit = &r600_copy_blit, + .blit_ring_index = RADEON_RING_TYPE_GFX_INDEX, + .dma = &evergreen_copy_dma, + .dma_ring_index = R600_RING_TYPE_DMA_INDEX, + .copy = &evergreen_copy_dma, + .copy_ring_index = R600_RING_TYPE_DMA_INDEX, + }, + .surface = { + .set_reg = r600_set_surface_reg, + .clear_reg = r600_clear_surface_reg, + }, + .hpd = { + .init = &evergreen_hpd_init, + .fini = &evergreen_hpd_fini, + .sense = &evergreen_hpd_sense, + .set_polarity = &evergreen_hpd_set_polarity, + }, + .pm = { + .misc = &evergreen_pm_misc, + .prepare = &evergreen_pm_prepare, + .finish = &evergreen_pm_finish, + .init_profile = &sumo_pm_init_profile, + .get_dynpm_state = &r600_pm_get_dynpm_state, + .get_engine_clock = &radeon_atom_get_engine_clock, + .set_engine_clock = &radeon_atom_set_engine_clock, + .get_memory_clock = NULL, + .set_memory_clock = NULL, + .get_pcie_lanes = NULL, + .set_pcie_lanes = NULL, + .set_clock_gating = NULL, + }, + .pflip = { + .pre_page_flip = &evergreen_pre_page_flip, + .page_flip = &evergreen_page_flip, + .post_page_flip = &evergreen_post_page_flip, + }, +}; + +static struct radeon_asic si_asic = { + .init = &si_init, + .fini = &si_fini, + .suspend = &si_suspend, + .resume = &si_resume, + .asic_reset = &si_asic_reset, + .vga_set_state = &r600_vga_set_state, + .ioctl_wait_idle = r600_ioctl_wait_idle, + .gui_idle = &r600_gui_idle, + .mc_wait_for_idle = &evergreen_mc_wait_for_idle, + .gart = { + .tlb_flush = &si_pcie_gart_tlb_flush, + .set_page = &rs600_gart_set_page, + }, + .vm = { + .init = &si_vm_init, + .fini = &si_vm_fini, + .pt_ring_index = RADEON_RING_TYPE_GFX_INDEX, + .set_page = &si_vm_set_page, + }, + .ring = { + [RADEON_RING_TYPE_GFX_INDEX] = { + .ib_execute = &si_ring_ib_execute, + .ib_parse = &si_ib_parse, + .emit_fence = &si_fence_ring_emit, + .emit_semaphore = &r600_semaphore_ring_emit, + .cs_parse = NULL, + .ring_test = &r600_ring_test, + .ib_test = &r600_ib_test, + .is_lockup = &si_gpu_is_lockup, + .vm_flush = &si_vm_flush, + }, + [CAYMAN_RING_TYPE_CP1_INDEX] = { + .ib_execute = &si_ring_ib_execute, + .ib_parse = &si_ib_parse, + .emit_fence = &si_fence_ring_emit, + .emit_semaphore = &r600_semaphore_ring_emit, + .cs_parse = NULL, + .ring_test = &r600_ring_test, + .ib_test = &r600_ib_test, + .is_lockup = &si_gpu_is_lockup, + .vm_flush = &si_vm_flush, + }, + [CAYMAN_RING_TYPE_CP2_INDEX] = { + .ib_execute = &si_ring_ib_execute, + .ib_parse = &si_ib_parse, + .emit_fence = &si_fence_ring_emit, + .emit_semaphore = &r600_semaphore_ring_emit, + .cs_parse = NULL, + .ring_test = &r600_ring_test, + .ib_test = &r600_ib_test, + .is_lockup = &si_gpu_is_lockup, + .vm_flush = &si_vm_flush, + }, + [R600_RING_TYPE_DMA_INDEX] = { + .ib_execute = &cayman_dma_ring_ib_execute, + .ib_parse = &evergreen_dma_ib_parse, + .emit_fence = &evergreen_dma_fence_ring_emit, + .emit_semaphore = &r600_dma_semaphore_ring_emit, + .cs_parse = NULL, + .ring_test = &r600_dma_ring_test, + .ib_test = &r600_dma_ib_test, + .is_lockup = &cayman_dma_is_lockup, + .vm_flush = &si_dma_vm_flush, + }, + [CAYMAN_RING_TYPE_DMA1_INDEX] = { + .ib_execute = &cayman_dma_ring_ib_execute, + .ib_parse = &evergreen_dma_ib_parse, + .emit_fence = &evergreen_dma_fence_ring_emit, + .emit_semaphore = &r600_dma_semaphore_ring_emit, + .cs_parse = NULL, + .ring_test = &r600_dma_ring_test, + .ib_test = &r600_dma_ib_test, + .is_lockup = &cayman_dma_is_lockup, + .vm_flush = &si_dma_vm_flush, + } + }, + .irq = { + .set = &si_irq_set, + .process = &si_irq_process, + }, + .display = { + .bandwidth_update = &dce6_bandwidth_update, + .get_vblank_counter = &evergreen_get_vblank_counter, + .wait_for_vblank = &dce4_wait_for_vblank, + .set_backlight_level = &atombios_set_backlight_level, + .get_backlight_level = &atombios_get_backlight_level, + }, + .copy = { + .blit = NULL, + .blit_ring_index = RADEON_RING_TYPE_GFX_INDEX, + .dma = &si_copy_dma, + .dma_ring_index = R600_RING_TYPE_DMA_INDEX, + .copy = &si_copy_dma, + .copy_ring_index = R600_RING_TYPE_DMA_INDEX, + }, + .surface = { + .set_reg = r600_set_surface_reg, + .clear_reg = r600_clear_surface_reg, + }, + .hpd = { + .init = &evergreen_hpd_init, + .fini = &evergreen_hpd_fini, + .sense = &evergreen_hpd_sense, + .set_polarity = &evergreen_hpd_set_polarity, + }, + .pm = { + .misc = &evergreen_pm_misc, + .prepare = &evergreen_pm_prepare, + .finish = &evergreen_pm_finish, + .init_profile = &sumo_pm_init_profile, + .get_dynpm_state = &r600_pm_get_dynpm_state, + .get_engine_clock = &radeon_atom_get_engine_clock, + .set_engine_clock = &radeon_atom_set_engine_clock, + .get_memory_clock = &radeon_atom_get_memory_clock, + .set_memory_clock = &radeon_atom_set_memory_clock, + .get_pcie_lanes = NULL, + .set_pcie_lanes = NULL, + .set_clock_gating = NULL, + }, + .pflip = { + .pre_page_flip = &evergreen_pre_page_flip, + .page_flip = &evergreen_page_flip, + .post_page_flip = &evergreen_post_page_flip, + }, +}; + +/** + * radeon_asic_init - register asic specific callbacks + * + * @rdev: radeon device pointer + * + * Registers the appropriate asic specific callbacks for each + * chip family. Also sets other asics specific info like the number + * of crtcs and the register aperture accessors (all asics). + * Returns 0 for success. + */ +int radeon_asic_init(struct radeon_device *rdev) +{ + radeon_register_accessor_init(rdev); + + /* set the number of crtcs */ + if (rdev->flags & RADEON_SINGLE_CRTC) + rdev->num_crtc = 1; + else + rdev->num_crtc = 2; + + switch (rdev->family) { + case CHIP_R100: + case CHIP_RV100: + case CHIP_RS100: + case CHIP_RV200: + case CHIP_RS200: + rdev->asic = &r100_asic; + break; + case CHIP_R200: + case CHIP_RV250: + case CHIP_RS300: + case CHIP_RV280: + rdev->asic = &r200_asic; + break; + case CHIP_R300: + case CHIP_R350: + case CHIP_RV350: + case CHIP_RV380: + if (rdev->flags & RADEON_IS_PCIE) + rdev->asic = &r300_asic_pcie; + else + rdev->asic = &r300_asic; + break; + case CHIP_R420: + case CHIP_R423: + case CHIP_RV410: + rdev->asic = &r420_asic; + /* handle macs */ + if (rdev->bios == NULL) { + rdev->asic->pm.get_engine_clock = &radeon_legacy_get_engine_clock; + rdev->asic->pm.set_engine_clock = &radeon_legacy_set_engine_clock; + rdev->asic->pm.get_memory_clock = &radeon_legacy_get_memory_clock; + rdev->asic->pm.set_memory_clock = NULL; + rdev->asic->display.set_backlight_level = &radeon_legacy_set_backlight_level; + } + break; + case CHIP_RS400: + case CHIP_RS480: + rdev->asic = &rs400_asic; + break; + case CHIP_RS600: + rdev->asic = &rs600_asic; + break; + case CHIP_RS690: + case CHIP_RS740: + rdev->asic = &rs690_asic; + break; + case CHIP_RV515: + rdev->asic = &rv515_asic; + break; + case CHIP_R520: + case CHIP_RV530: + case CHIP_RV560: + case CHIP_RV570: + case CHIP_R580: + rdev->asic = &r520_asic; + break; + case CHIP_R600: + case CHIP_RV610: + case CHIP_RV630: + case CHIP_RV620: + case CHIP_RV635: + case CHIP_RV670: + rdev->asic = &r600_asic; + break; + case CHIP_RS780: + case CHIP_RS880: + rdev->asic = &rs780_asic; + break; + case CHIP_RV770: + case CHIP_RV730: + case CHIP_RV710: + case CHIP_RV740: + rdev->asic = &rv770_asic; + break; + case CHIP_CEDAR: + case CHIP_REDWOOD: + case CHIP_JUNIPER: + case CHIP_CYPRESS: + case CHIP_HEMLOCK: + /* set num crtcs */ + if (rdev->family == CHIP_CEDAR) + rdev->num_crtc = 4; + else + rdev->num_crtc = 6; + rdev->asic = &evergreen_asic; + break; + case CHIP_PALM: + case CHIP_SUMO: + case CHIP_SUMO2: + rdev->asic = &sumo_asic; + break; + case CHIP_BARTS: + case CHIP_TURKS: + case CHIP_CAICOS: + /* set num crtcs */ + if (rdev->family == CHIP_CAICOS) + rdev->num_crtc = 4; + else + rdev->num_crtc = 6; + rdev->asic = &btc_asic; + break; + case CHIP_CAYMAN: + rdev->asic = &cayman_asic; + /* set num crtcs */ + rdev->num_crtc = 6; + break; + case CHIP_ARUBA: + rdev->asic = &trinity_asic; + /* set num crtcs */ + rdev->num_crtc = 4; + break; + case CHIP_TAHITI: + case CHIP_PITCAIRN: + case CHIP_VERDE: + rdev->asic = &si_asic; + /* set num crtcs */ + rdev->num_crtc = 6; + break; + default: + /* FIXME: not supported yet */ + return -EINVAL; + } + + if (rdev->flags & RADEON_IS_IGP) { + rdev->asic->pm.get_memory_clock = NULL; + rdev->asic->pm.set_memory_clock = NULL; + } + + return 0; +} + diff --git a/sys/dev/drm2/radeon/radeon_asic.h b/sys/dev/drm2/radeon/radeon_asic.h new file mode 100644 index 00000000000..f3b85f887ff --- /dev/null +++ b/sys/dev/drm2/radeon/radeon_asic.h @@ -0,0 +1,532 @@ +/* + * Copyright 2008 Advanced Micro Devices, Inc. + * Copyright 2008 Red Hat Inc. + * Copyright 2009 Jerome Glisse. + * + * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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. + * + * Authors: Dave Airlie + * Alex Deucher + * Jerome Glisse + */ + +#include +__FBSDID("$FreeBSD$"); + +#ifndef __RADEON_ASIC_H__ +#define __RADEON_ASIC_H__ + +/* + * common functions + */ +uint32_t radeon_legacy_get_engine_clock(struct radeon_device *rdev); +void radeon_legacy_set_engine_clock(struct radeon_device *rdev, uint32_t eng_clock); +uint32_t radeon_legacy_get_memory_clock(struct radeon_device *rdev); +void radeon_legacy_set_clock_gating(struct radeon_device *rdev, int enable); + +uint32_t radeon_atom_get_engine_clock(struct radeon_device *rdev); +void radeon_atom_set_engine_clock(struct radeon_device *rdev, uint32_t eng_clock); +uint32_t radeon_atom_get_memory_clock(struct radeon_device *rdev); +void radeon_atom_set_memory_clock(struct radeon_device *rdev, uint32_t mem_clock); +void radeon_atom_set_clock_gating(struct radeon_device *rdev, int enable); + +void atombios_set_backlight_level(struct radeon_encoder *radeon_encoder, u8 level); +u8 atombios_get_backlight_level(struct radeon_encoder *radeon_encoder); +void radeon_legacy_set_backlight_level(struct radeon_encoder *radeon_encoder, u8 level); +u8 radeon_legacy_get_backlight_level(struct radeon_encoder *radeon_encoder); + + +/* + * r100,rv100,rs100,rv200,rs200 + */ +struct r100_mc_save { + u32 GENMO_WT; + u32 CRTC_EXT_CNTL; + u32 CRTC_GEN_CNTL; + u32 CRTC2_GEN_CNTL; + u32 CUR_OFFSET; + u32 CUR2_OFFSET; +}; +int r100_init(struct radeon_device *rdev); +void r100_fini(struct radeon_device *rdev); +int r100_suspend(struct radeon_device *rdev); +int r100_resume(struct radeon_device *rdev); +void r100_vga_set_state(struct radeon_device *rdev, bool state); +bool r100_gpu_is_lockup(struct radeon_device *rdev, struct radeon_ring *cp); +int r100_asic_reset(struct radeon_device *rdev); +u32 r100_get_vblank_counter(struct radeon_device *rdev, int crtc); +void r100_pci_gart_tlb_flush(struct radeon_device *rdev); +int r100_pci_gart_set_page(struct radeon_device *rdev, int i, uint64_t addr); +void r100_ring_start(struct radeon_device *rdev, struct radeon_ring *ring); +int r100_irq_set(struct radeon_device *rdev); +irqreturn_t r100_irq_process(struct radeon_device *rdev); +void r100_fence_ring_emit(struct radeon_device *rdev, + struct radeon_fence *fence); +void r100_semaphore_ring_emit(struct radeon_device *rdev, + struct radeon_ring *cp, + struct radeon_semaphore *semaphore, + bool emit_wait); +int r100_cs_parse(struct radeon_cs_parser *p); +void r100_pll_wreg(struct radeon_device *rdev, uint32_t reg, uint32_t v); +uint32_t r100_pll_rreg(struct radeon_device *rdev, uint32_t reg); +int r100_copy_blit(struct radeon_device *rdev, + uint64_t src_offset, + uint64_t dst_offset, + unsigned num_gpu_pages, + struct radeon_fence **fence); +int r100_set_surface_reg(struct radeon_device *rdev, int reg, + uint32_t tiling_flags, uint32_t pitch, + uint32_t offset, uint32_t obj_size); +void r100_clear_surface_reg(struct radeon_device *rdev, int reg); +void r100_bandwidth_update(struct radeon_device *rdev); +void r100_ring_ib_execute(struct radeon_device *rdev, struct radeon_ib *ib); +int r100_ring_test(struct radeon_device *rdev, struct radeon_ring *cp); +void r100_hpd_init(struct radeon_device *rdev); +void r100_hpd_fini(struct radeon_device *rdev); +bool r100_hpd_sense(struct radeon_device *rdev, enum radeon_hpd_id hpd); +void r100_hpd_set_polarity(struct radeon_device *rdev, + enum radeon_hpd_id hpd); +int r100_debugfs_rbbm_init(struct radeon_device *rdev); +int r100_debugfs_cp_init(struct radeon_device *rdev); +void r100_cp_disable(struct radeon_device *rdev); +int r100_cp_init(struct radeon_device *rdev, unsigned ring_size); +void r100_cp_fini(struct radeon_device *rdev); +int r100_pci_gart_init(struct radeon_device *rdev); +void r100_pci_gart_fini(struct radeon_device *rdev); +int r100_pci_gart_enable(struct radeon_device *rdev); +void r100_pci_gart_disable(struct radeon_device *rdev); +int r100_debugfs_mc_info_init(struct radeon_device *rdev); +int r100_gui_wait_for_idle(struct radeon_device *rdev); +int r100_ib_test(struct radeon_device *rdev, struct radeon_ring *ring); +void r100_irq_disable(struct radeon_device *rdev); +void r100_mc_stop(struct radeon_device *rdev, struct r100_mc_save *save); +void r100_mc_resume(struct radeon_device *rdev, struct r100_mc_save *save); +void r100_vram_init_sizes(struct radeon_device *rdev); +int r100_cp_reset(struct radeon_device *rdev); +void r100_vga_render_disable(struct radeon_device *rdev); +void r100_restore_sanity(struct radeon_device *rdev); +int r100_cs_track_check_pkt3_indx_buffer(struct radeon_cs_parser *p, + struct radeon_cs_packet *pkt, + struct radeon_bo *robj); +int r100_cs_parse_packet0(struct radeon_cs_parser *p, + struct radeon_cs_packet *pkt, + const unsigned *auth, unsigned n, + radeon_packet0_check_t check); +int r100_cs_packet_parse(struct radeon_cs_parser *p, + struct radeon_cs_packet *pkt, + unsigned idx); +void r100_enable_bm(struct radeon_device *rdev); +void r100_set_common_regs(struct radeon_device *rdev); +void r100_bm_disable(struct radeon_device *rdev); +extern bool r100_gui_idle(struct radeon_device *rdev); +extern void r100_pm_misc(struct radeon_device *rdev); +extern void r100_pm_prepare(struct radeon_device *rdev); +extern void r100_pm_finish(struct radeon_device *rdev); +extern void r100_pm_init_profile(struct radeon_device *rdev); +extern void r100_pm_get_dynpm_state(struct radeon_device *rdev); +extern void r100_pre_page_flip(struct radeon_device *rdev, int crtc); +extern u32 r100_page_flip(struct radeon_device *rdev, int crtc, u64 crtc_base); +extern void r100_post_page_flip(struct radeon_device *rdev, int crtc); +extern void r100_wait_for_vblank(struct radeon_device *rdev, int crtc); +extern int r100_mc_wait_for_idle(struct radeon_device *rdev); + +/* + * r200,rv250,rs300,rv280 + */ +extern int r200_copy_dma(struct radeon_device *rdev, + uint64_t src_offset, + uint64_t dst_offset, + unsigned num_gpu_pages, + struct radeon_fence **fence); +void r200_set_safe_registers(struct radeon_device *rdev); + +/* + * r300,r350,rv350,rv380 + */ +extern int r300_init(struct radeon_device *rdev); +extern void r300_fini(struct radeon_device *rdev); +extern int r300_suspend(struct radeon_device *rdev); +extern int r300_resume(struct radeon_device *rdev); +extern int r300_asic_reset(struct radeon_device *rdev); +extern void r300_ring_start(struct radeon_device *rdev, struct radeon_ring *ring); +extern void r300_fence_ring_emit(struct radeon_device *rdev, + struct radeon_fence *fence); +extern int r300_cs_parse(struct radeon_cs_parser *p); +extern void rv370_pcie_gart_tlb_flush(struct radeon_device *rdev); +extern int rv370_pcie_gart_set_page(struct radeon_device *rdev, int i, uint64_t addr); +extern void rv370_set_pcie_lanes(struct radeon_device *rdev, int lanes); +extern int rv370_get_pcie_lanes(struct radeon_device *rdev); +extern void r300_set_reg_safe(struct radeon_device *rdev); +extern void r300_mc_program(struct radeon_device *rdev); +extern void r300_mc_init(struct radeon_device *rdev); +extern void r300_clock_startup(struct radeon_device *rdev); +extern int r300_mc_wait_for_idle(struct radeon_device *rdev); +extern int rv370_pcie_gart_init(struct radeon_device *rdev); +extern void rv370_pcie_gart_fini(struct radeon_device *rdev); +extern int rv370_pcie_gart_enable(struct radeon_device *rdev); +extern void rv370_pcie_gart_disable(struct radeon_device *rdev); + +/* + * r420,r423,rv410 + */ +extern int r420_init(struct radeon_device *rdev); +extern void r420_fini(struct radeon_device *rdev); +extern int r420_suspend(struct radeon_device *rdev); +extern int r420_resume(struct radeon_device *rdev); +extern void r420_pm_init_profile(struct radeon_device *rdev); +extern u32 r420_mc_rreg(struct radeon_device *rdev, u32 reg); +extern void r420_mc_wreg(struct radeon_device *rdev, u32 reg, u32 v); +extern int r420_debugfs_pipes_info_init(struct radeon_device *rdev); +extern void r420_pipes_init(struct radeon_device *rdev); + +/* + * rs400,rs480 + */ +extern int rs400_init(struct radeon_device *rdev); +extern void rs400_fini(struct radeon_device *rdev); +extern int rs400_suspend(struct radeon_device *rdev); +extern int rs400_resume(struct radeon_device *rdev); +void rs400_gart_tlb_flush(struct radeon_device *rdev); +int rs400_gart_set_page(struct radeon_device *rdev, int i, uint64_t addr); +uint32_t rs400_mc_rreg(struct radeon_device *rdev, uint32_t reg); +void rs400_mc_wreg(struct radeon_device *rdev, uint32_t reg, uint32_t v); +int rs400_gart_init(struct radeon_device *rdev); +int rs400_gart_enable(struct radeon_device *rdev); +void rs400_gart_adjust_size(struct radeon_device *rdev); +void rs400_gart_disable(struct radeon_device *rdev); +void rs400_gart_fini(struct radeon_device *rdev); +extern int rs400_mc_wait_for_idle(struct radeon_device *rdev); + +/* + * rs600. + */ +extern int rs600_asic_reset(struct radeon_device *rdev); +extern int rs600_init(struct radeon_device *rdev); +extern void rs600_fini(struct radeon_device *rdev); +extern int rs600_suspend(struct radeon_device *rdev); +extern int rs600_resume(struct radeon_device *rdev); +int rs600_irq_set(struct radeon_device *rdev); +irqreturn_t rs600_irq_process(struct radeon_device *rdev); +void rs600_irq_disable(struct radeon_device *rdev); +u32 rs600_get_vblank_counter(struct radeon_device *rdev, int crtc); +void rs600_gart_tlb_flush(struct radeon_device *rdev); +int rs600_gart_set_page(struct radeon_device *rdev, int i, uint64_t addr); +uint32_t rs600_mc_rreg(struct radeon_device *rdev, uint32_t reg); +void rs600_mc_wreg(struct radeon_device *rdev, uint32_t reg, uint32_t v); +void rs600_bandwidth_update(struct radeon_device *rdev); +void rs600_hpd_init(struct radeon_device *rdev); +void rs600_hpd_fini(struct radeon_device *rdev); +bool rs600_hpd_sense(struct radeon_device *rdev, enum radeon_hpd_id hpd); +void rs600_hpd_set_polarity(struct radeon_device *rdev, + enum radeon_hpd_id hpd); +extern void rs600_pm_misc(struct radeon_device *rdev); +extern void rs600_pm_prepare(struct radeon_device *rdev); +extern void rs600_pm_finish(struct radeon_device *rdev); +extern void rs600_pre_page_flip(struct radeon_device *rdev, int crtc); +extern u32 rs600_page_flip(struct radeon_device *rdev, int crtc, u64 crtc_base); +extern void rs600_post_page_flip(struct radeon_device *rdev, int crtc); +void rs600_set_safe_registers(struct radeon_device *rdev); +extern void avivo_wait_for_vblank(struct radeon_device *rdev, int crtc); +extern int rs600_mc_wait_for_idle(struct radeon_device *rdev); + +/* + * rs690,rs740 + */ +int rs690_init(struct radeon_device *rdev); +void rs690_fini(struct radeon_device *rdev); +int rs690_resume(struct radeon_device *rdev); +int rs690_suspend(struct radeon_device *rdev); +uint32_t rs690_mc_rreg(struct radeon_device *rdev, uint32_t reg); +void rs690_mc_wreg(struct radeon_device *rdev, uint32_t reg, uint32_t v); +void rs690_bandwidth_update(struct radeon_device *rdev); +void rs690_line_buffer_adjust(struct radeon_device *rdev, + struct drm_display_mode *mode1, + struct drm_display_mode *mode2); +extern int rs690_mc_wait_for_idle(struct radeon_device *rdev); + +/* + * rv515 + */ +struct rv515_mc_save { + u32 vga_render_control; + u32 vga_hdp_control; + bool crtc_enabled[2]; +}; + +int rv515_init(struct radeon_device *rdev); +void rv515_fini(struct radeon_device *rdev); +uint32_t rv515_mc_rreg(struct radeon_device *rdev, uint32_t reg); +void rv515_mc_wreg(struct radeon_device *rdev, uint32_t reg, uint32_t v); +void rv515_ring_start(struct radeon_device *rdev, struct radeon_ring *ring); +void rv515_bandwidth_update(struct radeon_device *rdev); +int rv515_resume(struct radeon_device *rdev); +int rv515_suspend(struct radeon_device *rdev); +void rv515_bandwidth_avivo_update(struct radeon_device *rdev); +void rv515_vga_render_disable(struct radeon_device *rdev); +void rv515_set_safe_registers(struct radeon_device *rdev); +void rv515_mc_stop(struct radeon_device *rdev, struct rv515_mc_save *save); +void rv515_mc_resume(struct radeon_device *rdev, struct rv515_mc_save *save); +void rv515_clock_startup(struct radeon_device *rdev); +void rv515_debugfs(struct radeon_device *rdev); +int rv515_mc_wait_for_idle(struct radeon_device *rdev); + +/* + * r520,rv530,rv560,rv570,r580 + */ +int r520_init(struct radeon_device *rdev); +int r520_resume(struct radeon_device *rdev); +int r520_mc_wait_for_idle(struct radeon_device *rdev); + +/* + * r600,rv610,rv630,rv620,rv635,rv670,rs780,rs880 + */ +int r600_init(struct radeon_device *rdev); +void r600_fini(struct radeon_device *rdev); +int r600_suspend(struct radeon_device *rdev); +int r600_resume(struct radeon_device *rdev); +void r600_vga_set_state(struct radeon_device *rdev, bool state); +int r600_wb_init(struct radeon_device *rdev); +void r600_wb_fini(struct radeon_device *rdev); +void r600_pcie_gart_tlb_flush(struct radeon_device *rdev); +uint32_t r600_pciep_rreg(struct radeon_device *rdev, uint32_t reg); +void r600_pciep_wreg(struct radeon_device *rdev, uint32_t reg, uint32_t v); +int r600_cs_parse(struct radeon_cs_parser *p); +int r600_dma_cs_parse(struct radeon_cs_parser *p); +void r600_fence_ring_emit(struct radeon_device *rdev, + struct radeon_fence *fence); +void r600_semaphore_ring_emit(struct radeon_device *rdev, + struct radeon_ring *cp, + struct radeon_semaphore *semaphore, + bool emit_wait); +void r600_dma_fence_ring_emit(struct radeon_device *rdev, + struct radeon_fence *fence); +void r600_dma_semaphore_ring_emit(struct radeon_device *rdev, + struct radeon_ring *ring, + struct radeon_semaphore *semaphore, + bool emit_wait); +void r600_dma_ring_ib_execute(struct radeon_device *rdev, struct radeon_ib *ib); +bool r600_dma_is_lockup(struct radeon_device *rdev, struct radeon_ring *ring); +bool r600_gpu_is_lockup(struct radeon_device *rdev, struct radeon_ring *cp); +int r600_asic_reset(struct radeon_device *rdev); +int r600_set_surface_reg(struct radeon_device *rdev, int reg, + uint32_t tiling_flags, uint32_t pitch, + uint32_t offset, uint32_t obj_size); +void r600_clear_surface_reg(struct radeon_device *rdev, int reg); +int r600_ib_test(struct radeon_device *rdev, struct radeon_ring *ring); +int r600_dma_ib_test(struct radeon_device *rdev, struct radeon_ring *ring); +void r600_ring_ib_execute(struct radeon_device *rdev, struct radeon_ib *ib); +int r600_ring_test(struct radeon_device *rdev, struct radeon_ring *cp); +int r600_dma_ring_test(struct radeon_device *rdev, struct radeon_ring *cp); +int r600_copy_blit(struct radeon_device *rdev, + uint64_t src_offset, uint64_t dst_offset, + unsigned num_gpu_pages, struct radeon_fence **fence); +int r600_copy_dma(struct radeon_device *rdev, + uint64_t src_offset, uint64_t dst_offset, + unsigned num_gpu_pages, struct radeon_fence **fence); +void r600_hpd_init(struct radeon_device *rdev); +void r600_hpd_fini(struct radeon_device *rdev); +bool r600_hpd_sense(struct radeon_device *rdev, enum radeon_hpd_id hpd); +void r600_hpd_set_polarity(struct radeon_device *rdev, + enum radeon_hpd_id hpd); +extern void r600_ioctl_wait_idle(struct radeon_device *rdev, struct radeon_bo *bo); +extern bool r600_gui_idle(struct radeon_device *rdev); +extern void r600_pm_misc(struct radeon_device *rdev); +extern void r600_pm_init_profile(struct radeon_device *rdev); +extern void rs780_pm_init_profile(struct radeon_device *rdev); +extern void r600_pm_get_dynpm_state(struct radeon_device *rdev); +extern void r600_set_pcie_lanes(struct radeon_device *rdev, int lanes); +extern int r600_get_pcie_lanes(struct radeon_device *rdev); +bool r600_card_posted(struct radeon_device *rdev); +void r600_cp_stop(struct radeon_device *rdev); +int r600_cp_start(struct radeon_device *rdev); +void r600_ring_init(struct radeon_device *rdev, struct radeon_ring *cp, unsigned ring_size); +int r600_cp_resume(struct radeon_device *rdev); +void r600_cp_fini(struct radeon_device *rdev); +int r600_count_pipe_bits(uint32_t val); +int r600_mc_wait_for_idle(struct radeon_device *rdev); +int r600_pcie_gart_init(struct radeon_device *rdev); +void r600_scratch_init(struct radeon_device *rdev); +int r600_blit_init(struct radeon_device *rdev); +void r600_blit_fini(struct radeon_device *rdev); +int r600_init_microcode(struct radeon_device *rdev); +void r600_fini_microcode(struct radeon_device *rdev); +/* r600 irq */ +irqreturn_t r600_irq_process(struct radeon_device *rdev); +int r600_irq_init(struct radeon_device *rdev); +void r600_irq_fini(struct radeon_device *rdev); +void r600_ih_ring_init(struct radeon_device *rdev, unsigned ring_size); +int r600_irq_set(struct radeon_device *rdev); +void r600_irq_suspend(struct radeon_device *rdev); +void r600_disable_interrupts(struct radeon_device *rdev); +void r600_rlc_stop(struct radeon_device *rdev); +/* r600 audio */ +int r600_audio_init(struct radeon_device *rdev); +void r600_audio_set_clock(struct drm_encoder *encoder, int clock); +struct r600_audio r600_audio_status(struct radeon_device *rdev); +void r600_audio_fini(struct radeon_device *rdev); +int r600_hdmi_buffer_status_changed(struct drm_encoder *encoder); +void r600_hdmi_update_audio_settings(struct drm_encoder *encoder); +/* r600 blit */ +int r600_blit_prepare_copy(struct radeon_device *rdev, unsigned num_gpu_pages, + struct radeon_fence **fence, struct radeon_sa_bo **vb, + struct radeon_semaphore **sem); +void r600_blit_done_copy(struct radeon_device *rdev, struct radeon_fence **fence, + struct radeon_sa_bo *vb, struct radeon_semaphore *sem); +void r600_kms_blit_copy(struct radeon_device *rdev, + u64 src_gpu_addr, u64 dst_gpu_addr, + unsigned num_gpu_pages, + struct radeon_sa_bo *vb); +uint64_t r600_get_gpu_clock(struct radeon_device *rdev); + +/* + * rv770,rv730,rv710,rv740 + */ +int rv770_init(struct radeon_device *rdev); +void rv770_fini(struct radeon_device *rdev); +int rv770_suspend(struct radeon_device *rdev); +int rv770_resume(struct radeon_device *rdev); +void rv770_pm_misc(struct radeon_device *rdev); +u32 rv770_page_flip(struct radeon_device *rdev, int crtc, u64 crtc_base); +void r700_vram_gtt_location(struct radeon_device *rdev, struct radeon_mc *mc); +void r700_cp_stop(struct radeon_device *rdev); +void r700_cp_fini(struct radeon_device *rdev); +int rv770_copy_dma(struct radeon_device *rdev, + uint64_t src_offset, uint64_t dst_offset, + unsigned num_gpu_pages, + struct radeon_fence **fence); + +/* + * evergreen + */ +struct evergreen_mc_save { + u32 vga_render_control; + u32 vga_hdp_control; + bool crtc_enabled[RADEON_MAX_CRTCS]; +}; + +void evergreen_pcie_gart_tlb_flush(struct radeon_device *rdev); +int evergreen_init(struct radeon_device *rdev); +void evergreen_fini(struct radeon_device *rdev); +int evergreen_suspend(struct radeon_device *rdev); +int evergreen_resume(struct radeon_device *rdev); +bool evergreen_gpu_is_lockup(struct radeon_device *rdev, struct radeon_ring *cp); +int evergreen_asic_reset(struct radeon_device *rdev); +void evergreen_bandwidth_update(struct radeon_device *rdev); +void evergreen_ring_ib_execute(struct radeon_device *rdev, struct radeon_ib *ib); +void evergreen_hpd_init(struct radeon_device *rdev); +void evergreen_hpd_fini(struct radeon_device *rdev); +bool evergreen_hpd_sense(struct radeon_device *rdev, enum radeon_hpd_id hpd); +void evergreen_hpd_set_polarity(struct radeon_device *rdev, + enum radeon_hpd_id hpd); +u32 evergreen_get_vblank_counter(struct radeon_device *rdev, int crtc); +int evergreen_irq_set(struct radeon_device *rdev); +irqreturn_t evergreen_irq_process(struct radeon_device *rdev); +extern int evergreen_cs_parse(struct radeon_cs_parser *p); +extern int evergreen_dma_cs_parse(struct radeon_cs_parser *p); +extern void evergreen_pm_misc(struct radeon_device *rdev); +extern void evergreen_pm_prepare(struct radeon_device *rdev); +extern void evergreen_pm_finish(struct radeon_device *rdev); +extern void sumo_pm_init_profile(struct radeon_device *rdev); +extern void btc_pm_init_profile(struct radeon_device *rdev); +extern void evergreen_pre_page_flip(struct radeon_device *rdev, int crtc); +extern u32 evergreen_page_flip(struct radeon_device *rdev, int crtc, u64 crtc_base); +extern void evergreen_post_page_flip(struct radeon_device *rdev, int crtc); +extern void dce4_wait_for_vblank(struct radeon_device *rdev, int crtc); +void evergreen_disable_interrupt_state(struct radeon_device *rdev); +int evergreen_blit_init(struct radeon_device *rdev); +int evergreen_mc_wait_for_idle(struct radeon_device *rdev); +void evergreen_dma_fence_ring_emit(struct radeon_device *rdev, + struct radeon_fence *fence); +void evergreen_dma_ring_ib_execute(struct radeon_device *rdev, + struct radeon_ib *ib); +int evergreen_copy_dma(struct radeon_device *rdev, + uint64_t src_offset, uint64_t dst_offset, + unsigned num_gpu_pages, + struct radeon_fence **fence); +void evergreen_fix_pci_max_read_req_size(struct radeon_device *rdev); +u32 evergreen_get_number_of_dram_channels(struct radeon_device *rdev); +void evergreen_mc_stop(struct radeon_device *rdev, struct evergreen_mc_save *save); +void evergreen_mc_resume(struct radeon_device *rdev, struct evergreen_mc_save *save); +void evergreen_mc_program(struct radeon_device *rdev); +int evergreen_mc_init(struct radeon_device *rdev); +void evergreen_irq_suspend(struct radeon_device *rdev); + +/* + * cayman + */ +void cayman_fence_ring_emit(struct radeon_device *rdev, + struct radeon_fence *fence); +void cayman_pcie_gart_tlb_flush(struct radeon_device *rdev); +int cayman_init(struct radeon_device *rdev); +void cayman_fini(struct radeon_device *rdev); +int cayman_suspend(struct radeon_device *rdev); +int cayman_resume(struct radeon_device *rdev); +int cayman_asic_reset(struct radeon_device *rdev); +void cayman_ring_ib_execute(struct radeon_device *rdev, struct radeon_ib *ib); +int cayman_vm_init(struct radeon_device *rdev); +void cayman_vm_fini(struct radeon_device *rdev); +void cayman_vm_flush(struct radeon_device *rdev, int ridx, struct radeon_vm *vm); +uint32_t cayman_vm_page_flags(struct radeon_device *rdev, uint32_t flags); +void cayman_vm_set_page(struct radeon_device *rdev, uint64_t pe, + uint64_t addr, unsigned count, + uint32_t incr, uint32_t flags); +int evergreen_ib_parse(struct radeon_device *rdev, struct radeon_ib *ib); +int evergreen_dma_ib_parse(struct radeon_device *rdev, struct radeon_ib *ib); +void cayman_dma_ring_ib_execute(struct radeon_device *rdev, + struct radeon_ib *ib); +bool cayman_dma_is_lockup(struct radeon_device *rdev, struct radeon_ring *ring); +void cayman_dma_vm_flush(struct radeon_device *rdev, int ridx, struct radeon_vm *vm); +extern void cayman_cp_int_cntl_setup(struct radeon_device *rdev, + int ring, u32 cp_int_cntl); + +/* DCE6 - SI */ +void dce6_bandwidth_update(struct radeon_device *rdev); + +/* + * si + */ +void si_fence_ring_emit(struct radeon_device *rdev, + struct radeon_fence *fence); +void si_pcie_gart_tlb_flush(struct radeon_device *rdev); +int si_init(struct radeon_device *rdev); +void si_fini(struct radeon_device *rdev); +int si_suspend(struct radeon_device *rdev); +int si_resume(struct radeon_device *rdev); +bool si_gpu_is_lockup(struct radeon_device *rdev, struct radeon_ring *cp); +int si_asic_reset(struct radeon_device *rdev); +void si_ring_ib_execute(struct radeon_device *rdev, struct radeon_ib *ib); +int si_irq_set(struct radeon_device *rdev); +irqreturn_t si_irq_process(struct radeon_device *rdev); +int si_vm_init(struct radeon_device *rdev); +void si_vm_fini(struct radeon_device *rdev); +void si_vm_set_page(struct radeon_device *rdev, uint64_t pe, + uint64_t addr, unsigned count, + uint32_t incr, uint32_t flags); +void si_vm_flush(struct radeon_device *rdev, int ridx, struct radeon_vm *vm); +int si_ib_parse(struct radeon_device *rdev, struct radeon_ib *ib); +uint64_t si_get_gpu_clock(struct radeon_device *rdev); +int si_copy_dma(struct radeon_device *rdev, + uint64_t src_offset, uint64_t dst_offset, + unsigned num_gpu_pages, + struct radeon_fence **fence); +void si_dma_vm_flush(struct radeon_device *rdev, int ridx, struct radeon_vm *vm); +void si_rlc_fini(struct radeon_device *rdev); +int si_rlc_init(struct radeon_device *rdev); + +#endif diff --git a/sys/dev/drm2/radeon/radeon_atombios.c b/sys/dev/drm2/radeon/radeon_atombios.c new file mode 100644 index 00000000000..309f1477154 --- /dev/null +++ b/sys/dev/drm2/radeon/radeon_atombios.c @@ -0,0 +1,3190 @@ +/* + * Copyright 2007-8 Advanced Micro Devices, Inc. + * Copyright 2008 Red Hat Inc. + * + * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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. + * + * Authors: Dave Airlie + * Alex Deucher + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include "radeon.h" +#include "radeon_asic.h" /* Declares several prototypes; clang is pleased. */ + +#include "atom.h" +#include "atom-bits.h" + +/* local */ +static int radeon_atom_get_max_vddc(struct radeon_device *rdev, u8 voltage_type, + u16 voltage_id, u16 *voltage); + +union atom_supported_devices { + struct _ATOM_SUPPORTED_DEVICES_INFO info; + struct _ATOM_SUPPORTED_DEVICES_INFO_2 info_2; + struct _ATOM_SUPPORTED_DEVICES_INFO_2d1 info_2d1; +}; + +static void radeon_lookup_i2c_gpio_quirks(struct radeon_device *rdev, + ATOM_GPIO_I2C_ASSIGMENT *gpio, + u8 index) +{ + /* r4xx mask is technically not used by the hw, so patch in the legacy mask bits */ + if ((rdev->family == CHIP_R420) || + (rdev->family == CHIP_R423) || + (rdev->family == CHIP_RV410)) { + if ((le16_to_cpu(gpio->usClkMaskRegisterIndex) == 0x0018) || + (le16_to_cpu(gpio->usClkMaskRegisterIndex) == 0x0019) || + (le16_to_cpu(gpio->usClkMaskRegisterIndex) == 0x001a)) { + gpio->ucClkMaskShift = 0x19; + gpio->ucDataMaskShift = 0x18; + } + } + + /* some evergreen boards have bad data for this entry */ + if (ASIC_IS_DCE4(rdev)) { + if ((index == 7) && + (le16_to_cpu(gpio->usClkMaskRegisterIndex) == 0x1936) && + (gpio->sucI2cId.ucAccess == 0)) { + gpio->sucI2cId.ucAccess = 0x97; + gpio->ucDataMaskShift = 8; + gpio->ucDataEnShift = 8; + gpio->ucDataY_Shift = 8; + gpio->ucDataA_Shift = 8; + } + } + + /* some DCE3 boards have bad data for this entry */ + if (ASIC_IS_DCE3(rdev)) { + if ((index == 4) && + (le16_to_cpu(gpio->usClkMaskRegisterIndex) == 0x1fda) && + (gpio->sucI2cId.ucAccess == 0x94)) + gpio->sucI2cId.ucAccess = 0x14; + } +} + +static struct radeon_i2c_bus_rec radeon_get_bus_rec_for_i2c_gpio(ATOM_GPIO_I2C_ASSIGMENT *gpio) +{ + struct radeon_i2c_bus_rec i2c; + + memset(&i2c, 0, sizeof(struct radeon_i2c_bus_rec)); + + i2c.mask_clk_reg = le16_to_cpu(gpio->usClkMaskRegisterIndex) * 4; + i2c.mask_data_reg = le16_to_cpu(gpio->usDataMaskRegisterIndex) * 4; + i2c.en_clk_reg = le16_to_cpu(gpio->usClkEnRegisterIndex) * 4; + i2c.en_data_reg = le16_to_cpu(gpio->usDataEnRegisterIndex) * 4; + i2c.y_clk_reg = le16_to_cpu(gpio->usClkY_RegisterIndex) * 4; + i2c.y_data_reg = le16_to_cpu(gpio->usDataY_RegisterIndex) * 4; + i2c.a_clk_reg = le16_to_cpu(gpio->usClkA_RegisterIndex) * 4; + i2c.a_data_reg = le16_to_cpu(gpio->usDataA_RegisterIndex) * 4; + i2c.mask_clk_mask = (1 << gpio->ucClkMaskShift); + i2c.mask_data_mask = (1 << gpio->ucDataMaskShift); + i2c.en_clk_mask = (1 << gpio->ucClkEnShift); + i2c.en_data_mask = (1 << gpio->ucDataEnShift); + i2c.y_clk_mask = (1 << gpio->ucClkY_Shift); + i2c.y_data_mask = (1 << gpio->ucDataY_Shift); + i2c.a_clk_mask = (1 << gpio->ucClkA_Shift); + i2c.a_data_mask = (1 << gpio->ucDataA_Shift); + + if (gpio->sucI2cId.sbfAccess.bfHW_Capable) + i2c.hw_capable = true; + else + i2c.hw_capable = false; + + if (gpio->sucI2cId.ucAccess == 0xa0) + i2c.mm_i2c = true; + else + i2c.mm_i2c = false; + + i2c.i2c_id = gpio->sucI2cId.ucAccess; + + if (i2c.mask_clk_reg) + i2c.valid = true; + else + i2c.valid = false; + + return i2c; +} + +static struct radeon_i2c_bus_rec radeon_lookup_i2c_gpio(struct radeon_device *rdev, + uint8_t id) +{ + struct atom_context *ctx = rdev->mode_info.atom_context; + ATOM_GPIO_I2C_ASSIGMENT *gpio; + struct radeon_i2c_bus_rec i2c; + int index = GetIndexIntoMasterTable(DATA, GPIO_I2C_Info); + struct _ATOM_GPIO_I2C_INFO *i2c_info; + uint16_t data_offset, size; + int i, num_indices; + + memset(&i2c, 0, sizeof(struct radeon_i2c_bus_rec)); + i2c.valid = false; + + if (atom_parse_data_header(ctx, index, &size, NULL, NULL, &data_offset)) { + i2c_info = (struct _ATOM_GPIO_I2C_INFO *)((char *)ctx->bios + data_offset); + + num_indices = (size - sizeof(ATOM_COMMON_TABLE_HEADER)) / + sizeof(ATOM_GPIO_I2C_ASSIGMENT); + + for (i = 0; i < num_indices; i++) { + gpio = &i2c_info->asGPIO_Info[i]; + + radeon_lookup_i2c_gpio_quirks(rdev, gpio, i); + + if (gpio->sucI2cId.ucAccess == id) { + i2c = radeon_get_bus_rec_for_i2c_gpio(gpio); + break; + } + } + } + + return i2c; +} + +void radeon_atombios_i2c_init(struct radeon_device *rdev) +{ + struct atom_context *ctx = rdev->mode_info.atom_context; + ATOM_GPIO_I2C_ASSIGMENT *gpio; + struct radeon_i2c_bus_rec i2c; + int index = GetIndexIntoMasterTable(DATA, GPIO_I2C_Info); + struct _ATOM_GPIO_I2C_INFO *i2c_info; + uint16_t data_offset, size; + int i, num_indices; + char stmp[32]; + + if (atom_parse_data_header(ctx, index, &size, NULL, NULL, &data_offset)) { + i2c_info = (struct _ATOM_GPIO_I2C_INFO *)((char *)ctx->bios + data_offset); + + num_indices = (size - sizeof(ATOM_COMMON_TABLE_HEADER)) / + sizeof(ATOM_GPIO_I2C_ASSIGMENT); + + for (i = 0; i < num_indices; i++) { + gpio = &i2c_info->asGPIO_Info[i]; + + radeon_lookup_i2c_gpio_quirks(rdev, gpio, i); + + i2c = radeon_get_bus_rec_for_i2c_gpio(gpio); + + if (i2c.valid) { + sprintf(stmp, "0x%x", i2c.i2c_id); + rdev->i2c_bus[i] = radeon_i2c_create(rdev->ddev, &i2c, stmp); + } + } + } +} + +static struct radeon_gpio_rec radeon_lookup_gpio(struct radeon_device *rdev, + u8 id) +{ + struct atom_context *ctx = rdev->mode_info.atom_context; + struct radeon_gpio_rec gpio; + int index = GetIndexIntoMasterTable(DATA, GPIO_Pin_LUT); + struct _ATOM_GPIO_PIN_LUT *gpio_info; + ATOM_GPIO_PIN_ASSIGNMENT *pin; + u16 data_offset, size; + int i, num_indices; + + memset(&gpio, 0, sizeof(struct radeon_gpio_rec)); + gpio.valid = false; + + if (atom_parse_data_header(ctx, index, &size, NULL, NULL, &data_offset)) { + gpio_info = (struct _ATOM_GPIO_PIN_LUT *)((char *)ctx->bios + data_offset); + + num_indices = (size - sizeof(ATOM_COMMON_TABLE_HEADER)) / + sizeof(ATOM_GPIO_PIN_ASSIGNMENT); + + for (i = 0; i < num_indices; i++) { + pin = &gpio_info->asGPIO_Pin[i]; + if (id == pin->ucGPIO_ID) { + gpio.id = pin->ucGPIO_ID; + gpio.reg = le16_to_cpu(pin->usGpioPin_AIndex) * 4; + gpio.mask = (1 << pin->ucGpioPinBitShift); + gpio.valid = true; + break; + } + } + } + + return gpio; +} + +static struct radeon_hpd radeon_atom_get_hpd_info_from_gpio(struct radeon_device *rdev, + struct radeon_gpio_rec *gpio) +{ + struct radeon_hpd hpd; + u32 reg; + + memset(&hpd, 0, sizeof(struct radeon_hpd)); + + if (ASIC_IS_DCE6(rdev)) + reg = SI_DC_GPIO_HPD_A; + else if (ASIC_IS_DCE4(rdev)) + reg = EVERGREEN_DC_GPIO_HPD_A; + else + reg = AVIVO_DC_GPIO_HPD_A; + + hpd.gpio = *gpio; + if (gpio->reg == reg) { + switch(gpio->mask) { + case (1 << 0): + hpd.hpd = RADEON_HPD_1; + break; + case (1 << 8): + hpd.hpd = RADEON_HPD_2; + break; + case (1 << 16): + hpd.hpd = RADEON_HPD_3; + break; + case (1 << 24): + hpd.hpd = RADEON_HPD_4; + break; + case (1 << 26): + hpd.hpd = RADEON_HPD_5; + break; + case (1 << 28): + hpd.hpd = RADEON_HPD_6; + break; + default: + hpd.hpd = RADEON_HPD_NONE; + break; + } + } else + hpd.hpd = RADEON_HPD_NONE; + return hpd; +} + +static bool radeon_atom_apply_quirks(struct drm_device *dev, + uint32_t supported_device, + int *connector_type, + struct radeon_i2c_bus_rec *i2c_bus, + uint16_t *line_mux, + struct radeon_hpd *hpd) +{ + + /* Asus M2A-VM HDMI board lists the DVI port as HDMI */ + if ((dev->pci_device == 0x791e) && + (dev->pci_subvendor == 0x1043) && + (dev->pci_subdevice == 0x826d)) { + if ((*connector_type == DRM_MODE_CONNECTOR_HDMIA) && + (supported_device == ATOM_DEVICE_DFP3_SUPPORT)) + *connector_type = DRM_MODE_CONNECTOR_DVID; + } + + /* Asrock RS600 board lists the DVI port as HDMI */ + if ((dev->pci_device == 0x7941) && + (dev->pci_subvendor == 0x1849) && + (dev->pci_subdevice == 0x7941)) { + if ((*connector_type == DRM_MODE_CONNECTOR_HDMIA) && + (supported_device == ATOM_DEVICE_DFP3_SUPPORT)) + *connector_type = DRM_MODE_CONNECTOR_DVID; + } + + /* MSI K9A2GM V2/V3 board has no HDMI or DVI */ + if ((dev->pci_device == 0x796e) && + (dev->pci_subvendor == 0x1462) && + (dev->pci_subdevice == 0x7302)) { + if ((supported_device == ATOM_DEVICE_DFP2_SUPPORT) || + (supported_device == ATOM_DEVICE_DFP3_SUPPORT)) + return false; + } + + /* a-bit f-i90hd - ciaranm on #radeonhd - this board has no DVI */ + if ((dev->pci_device == 0x7941) && + (dev->pci_subvendor == 0x147b) && + (dev->pci_subdevice == 0x2412)) { + if (*connector_type == DRM_MODE_CONNECTOR_DVII) + return false; + } + + /* Falcon NW laptop lists vga ddc line for LVDS */ + if ((dev->pci_device == 0x5653) && + (dev->pci_subvendor == 0x1462) && + (dev->pci_subdevice == 0x0291)) { + if (*connector_type == DRM_MODE_CONNECTOR_LVDS) { + i2c_bus->valid = false; + *line_mux = 53; + } + } + + /* HIS X1300 is DVI+VGA, not DVI+DVI */ + if ((dev->pci_device == 0x7146) && + (dev->pci_subvendor == 0x17af) && + (dev->pci_subdevice == 0x2058)) { + if (supported_device == ATOM_DEVICE_DFP1_SUPPORT) + return false; + } + + /* Gigabyte X1300 is DVI+VGA, not DVI+DVI */ + if ((dev->pci_device == 0x7142) && + (dev->pci_subvendor == 0x1458) && + (dev->pci_subdevice == 0x2134)) { + if (supported_device == ATOM_DEVICE_DFP1_SUPPORT) + return false; + } + + + /* Funky macbooks */ + if ((dev->pci_device == 0x71C5) && + (dev->pci_subvendor == 0x106b) && + (dev->pci_subdevice == 0x0080)) { + if ((supported_device == ATOM_DEVICE_CRT1_SUPPORT) || + (supported_device == ATOM_DEVICE_DFP2_SUPPORT)) + return false; + if (supported_device == ATOM_DEVICE_CRT2_SUPPORT) + *line_mux = 0x90; + } + + /* mac rv630, rv730, others */ + if ((supported_device == ATOM_DEVICE_TV1_SUPPORT) && + (*connector_type == DRM_MODE_CONNECTOR_DVII)) { + *connector_type = DRM_MODE_CONNECTOR_9PinDIN; + *line_mux = CONNECTOR_7PIN_DIN_ENUM_ID1; + } + + /* ASUS HD 3600 XT board lists the DVI port as HDMI */ + if ((dev->pci_device == 0x9598) && + (dev->pci_subvendor == 0x1043) && + (dev->pci_subdevice == 0x01da)) { + if (*connector_type == DRM_MODE_CONNECTOR_HDMIA) { + *connector_type = DRM_MODE_CONNECTOR_DVII; + } + } + + /* ASUS HD 3600 board lists the DVI port as HDMI */ + if ((dev->pci_device == 0x9598) && + (dev->pci_subvendor == 0x1043) && + (dev->pci_subdevice == 0x01e4)) { + if (*connector_type == DRM_MODE_CONNECTOR_HDMIA) { + *connector_type = DRM_MODE_CONNECTOR_DVII; + } + } + + /* ASUS HD 3450 board lists the DVI port as HDMI */ + if ((dev->pci_device == 0x95C5) && + (dev->pci_subvendor == 0x1043) && + (dev->pci_subdevice == 0x01e2)) { + if (*connector_type == DRM_MODE_CONNECTOR_HDMIA) { + *connector_type = DRM_MODE_CONNECTOR_DVII; + } + } + + /* some BIOSes seem to report DAC on HDMI - usually this is a board with + * HDMI + VGA reporting as HDMI + */ + if (*connector_type == DRM_MODE_CONNECTOR_HDMIA) { + if (supported_device & (ATOM_DEVICE_CRT_SUPPORT)) { + *connector_type = DRM_MODE_CONNECTOR_VGA; + *line_mux = 0; + } + } + + /* Acer laptop (Acer TravelMate 5730/5730G) has an HDMI port + * on the laptop and a DVI port on the docking station and + * both share the same encoder, hpd pin, and ddc line. + * So while the bios table is technically correct, + * we drop the DVI port here since xrandr has no concept of + * encoders and will try and drive both connectors + * with different crtcs which isn't possible on the hardware + * side and leaves no crtcs for LVDS or VGA. + */ + if (((dev->pci_device == 0x95c4) || (dev->pci_device == 0x9591)) && + (dev->pci_subvendor == 0x1025) && + (dev->pci_subdevice == 0x013c)) { + if ((*connector_type == DRM_MODE_CONNECTOR_DVII) && + (supported_device == ATOM_DEVICE_DFP1_SUPPORT)) { + /* actually it's a DVI-D port not DVI-I */ + *connector_type = DRM_MODE_CONNECTOR_DVID; + return false; + } + } + + /* XFX Pine Group device rv730 reports no VGA DDC lines + * even though they are wired up to record 0x93 + */ + if ((dev->pci_device == 0x9498) && + (dev->pci_subvendor == 0x1682) && + (dev->pci_subdevice == 0x2452) && + (i2c_bus->valid == false) && + !(supported_device & (ATOM_DEVICE_TV_SUPPORT | ATOM_DEVICE_CV_SUPPORT))) { + struct radeon_device *rdev = dev->dev_private; + *i2c_bus = radeon_lookup_i2c_gpio(rdev, 0x93); + } + + /* Fujitsu D3003-S2 board lists DVI-I as DVI-D and VGA */ + if (((dev->pci_device == 0x9802) || (dev->pci_device == 0x9806)) && + (dev->pci_subvendor == 0x1734) && + (dev->pci_subdevice == 0x11bd)) { + if (*connector_type == DRM_MODE_CONNECTOR_VGA) { + *connector_type = DRM_MODE_CONNECTOR_DVII; + *line_mux = 0x3103; + } else if (*connector_type == DRM_MODE_CONNECTOR_DVID) { + *connector_type = DRM_MODE_CONNECTOR_DVII; + } + } + + + return true; +} + +const int supported_devices_connector_convert[] = { + DRM_MODE_CONNECTOR_Unknown, + DRM_MODE_CONNECTOR_VGA, + DRM_MODE_CONNECTOR_DVII, + DRM_MODE_CONNECTOR_DVID, + DRM_MODE_CONNECTOR_DVIA, + DRM_MODE_CONNECTOR_SVIDEO, + DRM_MODE_CONNECTOR_Composite, + DRM_MODE_CONNECTOR_LVDS, + DRM_MODE_CONNECTOR_Unknown, + DRM_MODE_CONNECTOR_Unknown, + DRM_MODE_CONNECTOR_HDMIA, + DRM_MODE_CONNECTOR_HDMIB, + DRM_MODE_CONNECTOR_Unknown, + DRM_MODE_CONNECTOR_Unknown, + DRM_MODE_CONNECTOR_9PinDIN, + DRM_MODE_CONNECTOR_DisplayPort +}; + +const uint16_t supported_devices_connector_object_id_convert[] = { + CONNECTOR_OBJECT_ID_NONE, + CONNECTOR_OBJECT_ID_VGA, + CONNECTOR_OBJECT_ID_DUAL_LINK_DVI_I, /* not all boards support DL */ + CONNECTOR_OBJECT_ID_DUAL_LINK_DVI_D, /* not all boards support DL */ + CONNECTOR_OBJECT_ID_VGA, /* technically DVI-A */ + CONNECTOR_OBJECT_ID_COMPOSITE, + CONNECTOR_OBJECT_ID_SVIDEO, + CONNECTOR_OBJECT_ID_LVDS, + CONNECTOR_OBJECT_ID_9PIN_DIN, + CONNECTOR_OBJECT_ID_9PIN_DIN, + CONNECTOR_OBJECT_ID_DISPLAYPORT, + CONNECTOR_OBJECT_ID_HDMI_TYPE_A, + CONNECTOR_OBJECT_ID_HDMI_TYPE_B, + CONNECTOR_OBJECT_ID_SVIDEO +}; + +const int object_connector_convert[] = { + DRM_MODE_CONNECTOR_Unknown, + DRM_MODE_CONNECTOR_DVII, + DRM_MODE_CONNECTOR_DVII, + DRM_MODE_CONNECTOR_DVID, + DRM_MODE_CONNECTOR_DVID, + DRM_MODE_CONNECTOR_VGA, + DRM_MODE_CONNECTOR_Composite, + DRM_MODE_CONNECTOR_SVIDEO, + DRM_MODE_CONNECTOR_Unknown, + DRM_MODE_CONNECTOR_Unknown, + DRM_MODE_CONNECTOR_9PinDIN, + DRM_MODE_CONNECTOR_Unknown, + DRM_MODE_CONNECTOR_HDMIA, + DRM_MODE_CONNECTOR_HDMIB, + DRM_MODE_CONNECTOR_LVDS, + DRM_MODE_CONNECTOR_9PinDIN, + DRM_MODE_CONNECTOR_Unknown, + DRM_MODE_CONNECTOR_Unknown, + DRM_MODE_CONNECTOR_Unknown, + DRM_MODE_CONNECTOR_DisplayPort, + DRM_MODE_CONNECTOR_eDP, + DRM_MODE_CONNECTOR_Unknown +}; + +bool radeon_get_atom_connector_info_from_object_table(struct drm_device *dev) +{ + struct radeon_device *rdev = dev->dev_private; + struct radeon_mode_info *mode_info = &rdev->mode_info; + struct atom_context *ctx = mode_info->atom_context; + int index = GetIndexIntoMasterTable(DATA, Object_Header); + u16 size, data_offset; + u8 frev, crev; + ATOM_CONNECTOR_OBJECT_TABLE *con_obj; + ATOM_ENCODER_OBJECT_TABLE *enc_obj; + ATOM_OBJECT_TABLE *router_obj; + ATOM_DISPLAY_OBJECT_PATH_TABLE *path_obj; + ATOM_OBJECT_HEADER *obj_header; + int i, j, k, path_size, device_support; + int connector_type; + u16 igp_lane_info, conn_id, connector_object_id; + struct radeon_i2c_bus_rec ddc_bus; + struct radeon_router router; + struct radeon_gpio_rec gpio; + struct radeon_hpd hpd; + + if (!atom_parse_data_header(ctx, index, &size, &frev, &crev, &data_offset)) + return false; + + if (crev < 2) + return false; + + obj_header = (ATOM_OBJECT_HEADER *) ((char *)ctx->bios + data_offset); + path_obj = (ATOM_DISPLAY_OBJECT_PATH_TABLE *) + ((char *)ctx->bios + data_offset + + le16_to_cpu(obj_header->usDisplayPathTableOffset)); + con_obj = (ATOM_CONNECTOR_OBJECT_TABLE *) + ((char *)ctx->bios + data_offset + + le16_to_cpu(obj_header->usConnectorObjectTableOffset)); + enc_obj = (ATOM_ENCODER_OBJECT_TABLE *) + ((char *)ctx->bios + data_offset + + le16_to_cpu(obj_header->usEncoderObjectTableOffset)); + router_obj = (ATOM_OBJECT_TABLE *) + ((char *)ctx->bios + data_offset + + le16_to_cpu(obj_header->usRouterObjectTableOffset)); + device_support = le16_to_cpu(obj_header->usDeviceSupport); + + path_size = 0; + for (i = 0; i < path_obj->ucNumOfDispPath; i++) { + uint8_t *addr = (uint8_t *) path_obj->asDispPath; + ATOM_DISPLAY_OBJECT_PATH *path; + addr += path_size; + path = (ATOM_DISPLAY_OBJECT_PATH *) addr; + path_size += le16_to_cpu(path->usSize); + + if (device_support & le16_to_cpu(path->usDeviceTag)) { + uint8_t con_obj_id, con_obj_num, con_obj_type; + + con_obj_id = + (le16_to_cpu(path->usConnObjectId) & OBJECT_ID_MASK) + >> OBJECT_ID_SHIFT; + con_obj_num = + (le16_to_cpu(path->usConnObjectId) & ENUM_ID_MASK) + >> ENUM_ID_SHIFT; + con_obj_type = + (le16_to_cpu(path->usConnObjectId) & + OBJECT_TYPE_MASK) >> OBJECT_TYPE_SHIFT; + + /* TODO CV support */ + if (le16_to_cpu(path->usDeviceTag) == + ATOM_DEVICE_CV_SUPPORT) + continue; + + /* IGP chips */ + if ((rdev->flags & RADEON_IS_IGP) && + (con_obj_id == + CONNECTOR_OBJECT_ID_PCIE_CONNECTOR)) { + uint16_t igp_offset = 0; + ATOM_INTEGRATED_SYSTEM_INFO_V2 *igp_obj; + + index = + GetIndexIntoMasterTable(DATA, + IntegratedSystemInfo); + + if (atom_parse_data_header(ctx, index, &size, &frev, + &crev, &igp_offset)) { + + if (crev >= 2) { + igp_obj = + (ATOM_INTEGRATED_SYSTEM_INFO_V2 + *) ((char *)ctx->bios + igp_offset); + + if (igp_obj) { + uint32_t slot_config, ct; + + if (con_obj_num == 1) + slot_config = + igp_obj-> + ulDDISlot1Config; + else + slot_config = + igp_obj-> + ulDDISlot2Config; + + ct = (slot_config >> 16) & 0xff; + connector_type = + object_connector_convert + [ct]; + connector_object_id = ct; + igp_lane_info = + slot_config & 0xffff; + } else + continue; + } else + continue; + } else { + igp_lane_info = 0; + connector_type = + object_connector_convert[con_obj_id]; + connector_object_id = con_obj_id; + } + } else { + igp_lane_info = 0; + connector_type = + object_connector_convert[con_obj_id]; + connector_object_id = con_obj_id; + } + + if (connector_type == DRM_MODE_CONNECTOR_Unknown) + continue; + + router.ddc_valid = false; + router.cd_valid = false; + for (j = 0; j < ((le16_to_cpu(path->usSize) - 8) / 2); j++) { + uint8_t grph_obj_id, grph_obj_num, grph_obj_type; + + grph_obj_id = + (le16_to_cpu(path->usGraphicObjIds[j]) & + OBJECT_ID_MASK) >> OBJECT_ID_SHIFT; + grph_obj_num = + (le16_to_cpu(path->usGraphicObjIds[j]) & + ENUM_ID_MASK) >> ENUM_ID_SHIFT; + grph_obj_type = + (le16_to_cpu(path->usGraphicObjIds[j]) & + OBJECT_TYPE_MASK) >> OBJECT_TYPE_SHIFT; + + if (grph_obj_type == GRAPH_OBJECT_TYPE_ENCODER) { + for (k = 0; k < enc_obj->ucNumberOfObjects; k++) { + u16 encoder_obj = le16_to_cpu(enc_obj->asObjects[k].usObjectID); + if (le16_to_cpu(path->usGraphicObjIds[j]) == encoder_obj) { + ATOM_COMMON_RECORD_HEADER *record = (ATOM_COMMON_RECORD_HEADER *) + ((char *)ctx->bios + data_offset + + le16_to_cpu(enc_obj->asObjects[k].usRecordOffset)); + ATOM_ENCODER_CAP_RECORD *cap_record; + u16 caps = 0; + + while (record->ucRecordSize > 0 && + record->ucRecordType > 0 && + record->ucRecordType <= ATOM_MAX_OBJECT_RECORD_NUMBER) { + switch (record->ucRecordType) { + case ATOM_ENCODER_CAP_RECORD_TYPE: + cap_record =(ATOM_ENCODER_CAP_RECORD *) + record; + caps = le16_to_cpu(cap_record->usEncoderCap); + break; + } + record = (ATOM_COMMON_RECORD_HEADER *) + ((char *)record + record->ucRecordSize); + } + radeon_add_atom_encoder(dev, + encoder_obj, + le16_to_cpu + (path-> + usDeviceTag), + caps); + } + } + } else if (grph_obj_type == GRAPH_OBJECT_TYPE_ROUTER) { + for (k = 0; k < router_obj->ucNumberOfObjects; k++) { + u16 router_obj_id = le16_to_cpu(router_obj->asObjects[k].usObjectID); + if (le16_to_cpu(path->usGraphicObjIds[j]) == router_obj_id) { + ATOM_COMMON_RECORD_HEADER *record = (ATOM_COMMON_RECORD_HEADER *) + ((char *)ctx->bios + data_offset + + le16_to_cpu(router_obj->asObjects[k].usRecordOffset)); + ATOM_I2C_RECORD *i2c_record; + ATOM_I2C_ID_CONFIG_ACCESS *i2c_config; + ATOM_ROUTER_DDC_PATH_SELECT_RECORD *ddc_path; + ATOM_ROUTER_DATA_CLOCK_PATH_SELECT_RECORD *cd_path; + ATOM_SRC_DST_TABLE_FOR_ONE_OBJECT *router_src_dst_table = + (ATOM_SRC_DST_TABLE_FOR_ONE_OBJECT *) + ((char *)ctx->bios + data_offset + + le16_to_cpu(router_obj->asObjects[k].usSrcDstTableOffset)); + int enum_id; + + router.router_id = router_obj_id; + for (enum_id = 0; enum_id < router_src_dst_table->ucNumberOfDst; + enum_id++) { + if (le16_to_cpu(path->usConnObjectId) == + le16_to_cpu(router_src_dst_table->usDstObjectID[enum_id])) + break; + } + + while (record->ucRecordSize > 0 && + record->ucRecordType > 0 && + record->ucRecordType <= ATOM_MAX_OBJECT_RECORD_NUMBER) { + switch (record->ucRecordType) { + case ATOM_I2C_RECORD_TYPE: + i2c_record = + (ATOM_I2C_RECORD *) + record; + i2c_config = + (ATOM_I2C_ID_CONFIG_ACCESS *) + &i2c_record->sucI2cId; + router.i2c_info = + radeon_lookup_i2c_gpio(rdev, + i2c_config-> + ucAccess); + router.i2c_addr = i2c_record->ucI2CAddr >> 1; + break; + case ATOM_ROUTER_DDC_PATH_SELECT_RECORD_TYPE: + ddc_path = (ATOM_ROUTER_DDC_PATH_SELECT_RECORD *) + record; + router.ddc_valid = true; + router.ddc_mux_type = ddc_path->ucMuxType; + router.ddc_mux_control_pin = ddc_path->ucMuxControlPin; + router.ddc_mux_state = ddc_path->ucMuxState[enum_id]; + break; + case ATOM_ROUTER_DATA_CLOCK_PATH_SELECT_RECORD_TYPE: + cd_path = (ATOM_ROUTER_DATA_CLOCK_PATH_SELECT_RECORD *) + record; + router.cd_valid = true; + router.cd_mux_type = cd_path->ucMuxType; + router.cd_mux_control_pin = cd_path->ucMuxControlPin; + router.cd_mux_state = cd_path->ucMuxState[enum_id]; + break; + } + record = (ATOM_COMMON_RECORD_HEADER *) + ((char *)record + record->ucRecordSize); + } + } + } + } + } + + /* look up gpio for ddc, hpd */ + ddc_bus.valid = false; + hpd.hpd = RADEON_HPD_NONE; + if ((le16_to_cpu(path->usDeviceTag) & + (ATOM_DEVICE_TV_SUPPORT | ATOM_DEVICE_CV_SUPPORT)) == 0) { + for (j = 0; j < con_obj->ucNumberOfObjects; j++) { + if (le16_to_cpu(path->usConnObjectId) == + le16_to_cpu(con_obj->asObjects[j]. + usObjectID)) { + ATOM_COMMON_RECORD_HEADER + *record = + (ATOM_COMMON_RECORD_HEADER + *) + ((char *)ctx->bios + data_offset + + le16_to_cpu(con_obj-> + asObjects[j]. + usRecordOffset)); + ATOM_I2C_RECORD *i2c_record; + ATOM_HPD_INT_RECORD *hpd_record; + ATOM_I2C_ID_CONFIG_ACCESS *i2c_config; + + while (record->ucRecordSize > 0 && + record->ucRecordType > 0 && + record->ucRecordType <= ATOM_MAX_OBJECT_RECORD_NUMBER) { + switch (record->ucRecordType) { + case ATOM_I2C_RECORD_TYPE: + i2c_record = + (ATOM_I2C_RECORD *) + record; + i2c_config = + (ATOM_I2C_ID_CONFIG_ACCESS *) + &i2c_record->sucI2cId; + ddc_bus = radeon_lookup_i2c_gpio(rdev, + i2c_config-> + ucAccess); + break; + case ATOM_HPD_INT_RECORD_TYPE: + hpd_record = + (ATOM_HPD_INT_RECORD *) + record; + gpio = radeon_lookup_gpio(rdev, + hpd_record->ucHPDIntGPIOID); + hpd = radeon_atom_get_hpd_info_from_gpio(rdev, &gpio); + hpd.plugged_state = hpd_record->ucPlugged_PinState; + break; + } + record = + (ATOM_COMMON_RECORD_HEADER + *) ((char *)record + + + record-> + ucRecordSize); + } + break; + } + } + } + + /* needed for aux chan transactions */ + ddc_bus.hpd = hpd.hpd; + + conn_id = le16_to_cpu(path->usConnObjectId); + + if (!radeon_atom_apply_quirks + (dev, le16_to_cpu(path->usDeviceTag), &connector_type, + &ddc_bus, &conn_id, &hpd)) + continue; + + radeon_add_atom_connector(dev, + conn_id, + le16_to_cpu(path-> + usDeviceTag), + connector_type, &ddc_bus, + igp_lane_info, + connector_object_id, + &hpd, + &router); + + } + } + + radeon_link_encoder_connector(dev); + + return true; +} + +static uint16_t atombios_get_connector_object_id(struct drm_device *dev, + int connector_type, + uint16_t devices) +{ + struct radeon_device *rdev = dev->dev_private; + + if (rdev->flags & RADEON_IS_IGP) { + return supported_devices_connector_object_id_convert + [connector_type]; + } else if (((connector_type == DRM_MODE_CONNECTOR_DVII) || + (connector_type == DRM_MODE_CONNECTOR_DVID)) && + (devices & ATOM_DEVICE_DFP2_SUPPORT)) { + struct radeon_mode_info *mode_info = &rdev->mode_info; + struct atom_context *ctx = mode_info->atom_context; + int index = GetIndexIntoMasterTable(DATA, XTMDS_Info); + uint16_t size, data_offset; + uint8_t frev, crev; + ATOM_XTMDS_INFO *xtmds; + + if (atom_parse_data_header(ctx, index, &size, &frev, &crev, &data_offset)) { + xtmds = (ATOM_XTMDS_INFO *)((char *)ctx->bios + data_offset); + + if (xtmds->ucSupportedLink & ATOM_XTMDS_SUPPORTED_DUALLINK) { + if (connector_type == DRM_MODE_CONNECTOR_DVII) + return CONNECTOR_OBJECT_ID_DUAL_LINK_DVI_I; + else + return CONNECTOR_OBJECT_ID_DUAL_LINK_DVI_D; + } else { + if (connector_type == DRM_MODE_CONNECTOR_DVII) + return CONNECTOR_OBJECT_ID_SINGLE_LINK_DVI_I; + else + return CONNECTOR_OBJECT_ID_SINGLE_LINK_DVI_D; + } + } else + return supported_devices_connector_object_id_convert + [connector_type]; + } else { + return supported_devices_connector_object_id_convert + [connector_type]; + } +} + +struct bios_connector { + bool valid; + uint16_t line_mux; + uint16_t devices; + int connector_type; + struct radeon_i2c_bus_rec ddc_bus; + struct radeon_hpd hpd; +}; + +bool radeon_get_atom_connector_info_from_supported_devices_table(struct + drm_device + *dev) +{ + struct radeon_device *rdev = dev->dev_private; + struct radeon_mode_info *mode_info = &rdev->mode_info; + struct atom_context *ctx = mode_info->atom_context; + int index = GetIndexIntoMasterTable(DATA, SupportedDevicesInfo); + uint16_t size, data_offset; + uint8_t frev, crev; + uint16_t device_support; + uint8_t dac; + union atom_supported_devices *supported_devices; + int i, j, max_device; + struct bios_connector *bios_connectors; + size_t bc_size = sizeof(*bios_connectors) * ATOM_MAX_SUPPORTED_DEVICE; + struct radeon_router router; + + router.ddc_valid = false; + router.cd_valid = false; + + bios_connectors = malloc(bc_size, DRM_MEM_DRIVER, M_WAITOK | M_ZERO); + if (!bios_connectors) + return false; + + if (!atom_parse_data_header(ctx, index, &size, &frev, &crev, + &data_offset)) { + free(bios_connectors, DRM_MEM_DRIVER); + return false; + } + + supported_devices = + (union atom_supported_devices *)((char *)ctx->bios + data_offset); + + device_support = le16_to_cpu(supported_devices->info.usDeviceSupport); + + if (frev > 1) + max_device = ATOM_MAX_SUPPORTED_DEVICE; + else + max_device = ATOM_MAX_SUPPORTED_DEVICE_INFO; + + for (i = 0; i < max_device; i++) { + ATOM_CONNECTOR_INFO_I2C ci = + supported_devices->info.asConnInfo[i]; + + bios_connectors[i].valid = false; + + if (!(device_support & (1 << i))) { + continue; + } + + if (i == ATOM_DEVICE_CV_INDEX) { + DRM_DEBUG_KMS("Skipping Component Video\n"); + continue; + } + + bios_connectors[i].connector_type = + supported_devices_connector_convert[ci.sucConnectorInfo. + sbfAccess. + bfConnectorType]; + + if (bios_connectors[i].connector_type == + DRM_MODE_CONNECTOR_Unknown) + continue; + + dac = ci.sucConnectorInfo.sbfAccess.bfAssociatedDAC; + + bios_connectors[i].line_mux = + ci.sucI2cId.ucAccess; + + /* give tv unique connector ids */ + if (i == ATOM_DEVICE_TV1_INDEX) { + bios_connectors[i].ddc_bus.valid = false; + bios_connectors[i].line_mux = 50; + } else if (i == ATOM_DEVICE_TV2_INDEX) { + bios_connectors[i].ddc_bus.valid = false; + bios_connectors[i].line_mux = 51; + } else if (i == ATOM_DEVICE_CV_INDEX) { + bios_connectors[i].ddc_bus.valid = false; + bios_connectors[i].line_mux = 52; + } else + bios_connectors[i].ddc_bus = + radeon_lookup_i2c_gpio(rdev, + bios_connectors[i].line_mux); + + if ((crev > 1) && (frev > 1)) { + u8 isb = supported_devices->info_2d1.asIntSrcInfo[i].ucIntSrcBitmap; + switch (isb) { + case 0x4: + bios_connectors[i].hpd.hpd = RADEON_HPD_1; + break; + case 0xa: + bios_connectors[i].hpd.hpd = RADEON_HPD_2; + break; + default: + bios_connectors[i].hpd.hpd = RADEON_HPD_NONE; + break; + } + } else { + if (i == ATOM_DEVICE_DFP1_INDEX) + bios_connectors[i].hpd.hpd = RADEON_HPD_1; + else if (i == ATOM_DEVICE_DFP2_INDEX) + bios_connectors[i].hpd.hpd = RADEON_HPD_2; + else + bios_connectors[i].hpd.hpd = RADEON_HPD_NONE; + } + + /* Always set the connector type to VGA for CRT1/CRT2. if they are + * shared with a DVI port, we'll pick up the DVI connector when we + * merge the outputs. Some bioses incorrectly list VGA ports as DVI. + */ + if (i == ATOM_DEVICE_CRT1_INDEX || i == ATOM_DEVICE_CRT2_INDEX) + bios_connectors[i].connector_type = + DRM_MODE_CONNECTOR_VGA; + + if (!radeon_atom_apply_quirks + (dev, (1 << i), &bios_connectors[i].connector_type, + &bios_connectors[i].ddc_bus, &bios_connectors[i].line_mux, + &bios_connectors[i].hpd)) + continue; + + bios_connectors[i].valid = true; + bios_connectors[i].devices = (1 << i); + + if (ASIC_IS_AVIVO(rdev) || radeon_r4xx_atom) + radeon_add_atom_encoder(dev, + radeon_get_encoder_enum(dev, + (1 << i), + dac), + (1 << i), + 0); + else + radeon_add_legacy_encoder(dev, + radeon_get_encoder_enum(dev, + (1 << i), + dac), + (1 << i)); + } + + /* combine shared connectors */ + for (i = 0; i < max_device; i++) { + if (bios_connectors[i].valid) { + for (j = 0; j < max_device; j++) { + if (bios_connectors[j].valid && (i != j)) { + if (bios_connectors[i].line_mux == + bios_connectors[j].line_mux) { + /* make sure not to combine LVDS */ + if (bios_connectors[i].devices & (ATOM_DEVICE_LCD_SUPPORT)) { + bios_connectors[i].line_mux = 53; + bios_connectors[i].ddc_bus.valid = false; + continue; + } + if (bios_connectors[j].devices & (ATOM_DEVICE_LCD_SUPPORT)) { + bios_connectors[j].line_mux = 53; + bios_connectors[j].ddc_bus.valid = false; + continue; + } + /* combine analog and digital for DVI-I */ + if (((bios_connectors[i].devices & (ATOM_DEVICE_DFP_SUPPORT)) && + (bios_connectors[j].devices & (ATOM_DEVICE_CRT_SUPPORT))) || + ((bios_connectors[j].devices & (ATOM_DEVICE_DFP_SUPPORT)) && + (bios_connectors[i].devices & (ATOM_DEVICE_CRT_SUPPORT)))) { + bios_connectors[i].devices |= + bios_connectors[j].devices; + bios_connectors[i].connector_type = + DRM_MODE_CONNECTOR_DVII; + if (bios_connectors[j].devices & (ATOM_DEVICE_DFP_SUPPORT)) + bios_connectors[i].hpd = + bios_connectors[j].hpd; + bios_connectors[j].valid = false; + } + } + } + } + } + } + + /* add the connectors */ + for (i = 0; i < max_device; i++) { + if (bios_connectors[i].valid) { + uint16_t connector_object_id = + atombios_get_connector_object_id(dev, + bios_connectors[i].connector_type, + bios_connectors[i].devices); + radeon_add_atom_connector(dev, + bios_connectors[i].line_mux, + bios_connectors[i].devices, + bios_connectors[i]. + connector_type, + &bios_connectors[i].ddc_bus, + 0, + connector_object_id, + &bios_connectors[i].hpd, + &router); + } + } + + radeon_link_encoder_connector(dev); + + free(bios_connectors, DRM_MEM_DRIVER); + return true; +} + +union firmware_info { + ATOM_FIRMWARE_INFO info; + ATOM_FIRMWARE_INFO_V1_2 info_12; + ATOM_FIRMWARE_INFO_V1_3 info_13; + ATOM_FIRMWARE_INFO_V1_4 info_14; + ATOM_FIRMWARE_INFO_V2_1 info_21; + ATOM_FIRMWARE_INFO_V2_2 info_22; +}; + +bool radeon_atom_get_clock_info(struct drm_device *dev) +{ + struct radeon_device *rdev = dev->dev_private; + struct radeon_mode_info *mode_info = &rdev->mode_info; + int index = GetIndexIntoMasterTable(DATA, FirmwareInfo); + union firmware_info *firmware_info; + uint8_t frev, crev; + struct radeon_pll *p1pll = &rdev->clock.p1pll; + struct radeon_pll *p2pll = &rdev->clock.p2pll; + struct radeon_pll *dcpll = &rdev->clock.dcpll; + struct radeon_pll *spll = &rdev->clock.spll; + struct radeon_pll *mpll = &rdev->clock.mpll; + uint16_t data_offset; + + if (atom_parse_data_header(mode_info->atom_context, index, NULL, + &frev, &crev, &data_offset)) { + firmware_info = + (union firmware_info *)((char *)mode_info->atom_context->bios + + data_offset); + /* pixel clocks */ + p1pll->reference_freq = + le16_to_cpu(firmware_info->info.usReferenceClock); + p1pll->reference_div = 0; + + if (crev < 2) + p1pll->pll_out_min = + le16_to_cpu(firmware_info->info.usMinPixelClockPLL_Output); + else + p1pll->pll_out_min = + le32_to_cpu(firmware_info->info_12.ulMinPixelClockPLL_Output); + p1pll->pll_out_max = + le32_to_cpu(firmware_info->info.ulMaxPixelClockPLL_Output); + + if (crev >= 4) { + p1pll->lcd_pll_out_min = + le16_to_cpu(firmware_info->info_14.usLcdMinPixelClockPLL_Output) * 100; + if (p1pll->lcd_pll_out_min == 0) + p1pll->lcd_pll_out_min = p1pll->pll_out_min; + p1pll->lcd_pll_out_max = + le16_to_cpu(firmware_info->info_14.usLcdMaxPixelClockPLL_Output) * 100; + if (p1pll->lcd_pll_out_max == 0) + p1pll->lcd_pll_out_max = p1pll->pll_out_max; + } else { + p1pll->lcd_pll_out_min = p1pll->pll_out_min; + p1pll->lcd_pll_out_max = p1pll->pll_out_max; + } + + if (p1pll->pll_out_min == 0) { + if (ASIC_IS_AVIVO(rdev)) + p1pll->pll_out_min = 64800; + else + p1pll->pll_out_min = 20000; + } + + p1pll->pll_in_min = + le16_to_cpu(firmware_info->info.usMinPixelClockPLL_Input); + p1pll->pll_in_max = + le16_to_cpu(firmware_info->info.usMaxPixelClockPLL_Input); + + *p2pll = *p1pll; + + /* system clock */ + if (ASIC_IS_DCE4(rdev)) + spll->reference_freq = + le16_to_cpu(firmware_info->info_21.usCoreReferenceClock); + else + spll->reference_freq = + le16_to_cpu(firmware_info->info.usReferenceClock); + spll->reference_div = 0; + + spll->pll_out_min = + le16_to_cpu(firmware_info->info.usMinEngineClockPLL_Output); + spll->pll_out_max = + le32_to_cpu(firmware_info->info.ulMaxEngineClockPLL_Output); + + /* ??? */ + if (spll->pll_out_min == 0) { + if (ASIC_IS_AVIVO(rdev)) + spll->pll_out_min = 64800; + else + spll->pll_out_min = 20000; + } + + spll->pll_in_min = + le16_to_cpu(firmware_info->info.usMinEngineClockPLL_Input); + spll->pll_in_max = + le16_to_cpu(firmware_info->info.usMaxEngineClockPLL_Input); + + /* memory clock */ + if (ASIC_IS_DCE4(rdev)) + mpll->reference_freq = + le16_to_cpu(firmware_info->info_21.usMemoryReferenceClock); + else + mpll->reference_freq = + le16_to_cpu(firmware_info->info.usReferenceClock); + mpll->reference_div = 0; + + mpll->pll_out_min = + le16_to_cpu(firmware_info->info.usMinMemoryClockPLL_Output); + mpll->pll_out_max = + le32_to_cpu(firmware_info->info.ulMaxMemoryClockPLL_Output); + + /* ??? */ + if (mpll->pll_out_min == 0) { + if (ASIC_IS_AVIVO(rdev)) + mpll->pll_out_min = 64800; + else + mpll->pll_out_min = 20000; + } + + mpll->pll_in_min = + le16_to_cpu(firmware_info->info.usMinMemoryClockPLL_Input); + mpll->pll_in_max = + le16_to_cpu(firmware_info->info.usMaxMemoryClockPLL_Input); + + rdev->clock.default_sclk = + le32_to_cpu(firmware_info->info.ulDefaultEngineClock); + rdev->clock.default_mclk = + le32_to_cpu(firmware_info->info.ulDefaultMemoryClock); + + if (ASIC_IS_DCE4(rdev)) { + rdev->clock.default_dispclk = + le32_to_cpu(firmware_info->info_21.ulDefaultDispEngineClkFreq); + if (rdev->clock.default_dispclk == 0) { + if (ASIC_IS_DCE5(rdev)) + rdev->clock.default_dispclk = 54000; /* 540 Mhz */ + else + rdev->clock.default_dispclk = 60000; /* 600 Mhz */ + } + rdev->clock.dp_extclk = + le16_to_cpu(firmware_info->info_21.usUniphyDPModeExtClkFreq); + } + *dcpll = *p1pll; + + rdev->clock.max_pixel_clock = le16_to_cpu(firmware_info->info.usMaxPixelClock); + if (rdev->clock.max_pixel_clock == 0) + rdev->clock.max_pixel_clock = 40000; + + /* not technically a clock, but... */ + rdev->mode_info.firmware_flags = + le16_to_cpu(firmware_info->info.usFirmwareCapability.susAccess); + + return true; + } + + return false; +} + +union igp_info { + struct _ATOM_INTEGRATED_SYSTEM_INFO info; + struct _ATOM_INTEGRATED_SYSTEM_INFO_V2 info_2; + struct _ATOM_INTEGRATED_SYSTEM_INFO_V6 info_6; + struct _ATOM_INTEGRATED_SYSTEM_INFO_V1_7 info_7; +}; + +bool radeon_atombios_sideport_present(struct radeon_device *rdev) +{ + struct radeon_mode_info *mode_info = &rdev->mode_info; + int index = GetIndexIntoMasterTable(DATA, IntegratedSystemInfo); + union igp_info *igp_info; + u8 frev, crev; + u16 data_offset; + + /* sideport is AMD only */ + if (rdev->family == CHIP_RS600) + return false; + + if (atom_parse_data_header(mode_info->atom_context, index, NULL, + &frev, &crev, &data_offset)) { + igp_info = (union igp_info *)((char *)mode_info->atom_context->bios + + data_offset); + switch (crev) { + case 1: + if (le32_to_cpu(igp_info->info.ulBootUpMemoryClock)) + return true; + break; + case 2: + if (le32_to_cpu(igp_info->info_2.ulBootUpSidePortClock)) + return true; + break; + default: + DRM_ERROR("Unsupported IGP table: %d %d\n", frev, crev); + break; + } + } + return false; +} + +bool radeon_atombios_get_tmds_info(struct radeon_encoder *encoder, + struct radeon_encoder_int_tmds *tmds) +{ + struct drm_device *dev = encoder->base.dev; + struct radeon_device *rdev = dev->dev_private; + struct radeon_mode_info *mode_info = &rdev->mode_info; + int index = GetIndexIntoMasterTable(DATA, TMDS_Info); + uint16_t data_offset; + struct _ATOM_TMDS_INFO *tmds_info; + uint8_t frev, crev; + uint16_t maxfreq; + int i; + + if (atom_parse_data_header(mode_info->atom_context, index, NULL, + &frev, &crev, &data_offset)) { + tmds_info = + (struct _ATOM_TMDS_INFO *)((char *)mode_info->atom_context->bios + + data_offset); + + maxfreq = le16_to_cpu(tmds_info->usMaxFrequency); + for (i = 0; i < 4; i++) { + tmds->tmds_pll[i].freq = + le16_to_cpu(tmds_info->asMiscInfo[i].usFrequency); + tmds->tmds_pll[i].value = + tmds_info->asMiscInfo[i].ucPLL_ChargePump & 0x3f; + tmds->tmds_pll[i].value |= + (tmds_info->asMiscInfo[i]. + ucPLL_VCO_Gain & 0x3f) << 6; + tmds->tmds_pll[i].value |= + (tmds_info->asMiscInfo[i]. + ucPLL_DutyCycle & 0xf) << 12; + tmds->tmds_pll[i].value |= + (tmds_info->asMiscInfo[i]. + ucPLL_VoltageSwing & 0xf) << 16; + + DRM_DEBUG_KMS("TMDS PLL From ATOMBIOS %u %x\n", + tmds->tmds_pll[i].freq, + tmds->tmds_pll[i].value); + + if (maxfreq == tmds->tmds_pll[i].freq) { + tmds->tmds_pll[i].freq = 0xffffffff; + break; + } + } + return true; + } + return false; +} + +bool radeon_atombios_get_ppll_ss_info(struct radeon_device *rdev, + struct radeon_atom_ss *ss, + int id) +{ + struct radeon_mode_info *mode_info = &rdev->mode_info; + int index = GetIndexIntoMasterTable(DATA, PPLL_SS_Info); + uint16_t data_offset, size; + struct _ATOM_SPREAD_SPECTRUM_INFO *ss_info; + uint8_t frev, crev; + int i, num_indices; + + memset(ss, 0, sizeof(struct radeon_atom_ss)); + if (atom_parse_data_header(mode_info->atom_context, index, &size, + &frev, &crev, &data_offset)) { + ss_info = + (struct _ATOM_SPREAD_SPECTRUM_INFO *)((char *)mode_info->atom_context->bios + data_offset); + + num_indices = (size - sizeof(ATOM_COMMON_TABLE_HEADER)) / + sizeof(ATOM_SPREAD_SPECTRUM_ASSIGNMENT); + + for (i = 0; i < num_indices; i++) { + if (ss_info->asSS_Info[i].ucSS_Id == id) { + ss->percentage = + le16_to_cpu(ss_info->asSS_Info[i].usSpreadSpectrumPercentage); + ss->type = ss_info->asSS_Info[i].ucSpreadSpectrumType; + ss->step = ss_info->asSS_Info[i].ucSS_Step; + ss->delay = ss_info->asSS_Info[i].ucSS_Delay; + ss->range = ss_info->asSS_Info[i].ucSS_Range; + ss->refdiv = ss_info->asSS_Info[i].ucRecommendedRef_Div; + return true; + } + } + } + return false; +} + +static void radeon_atombios_get_igp_ss_overrides(struct radeon_device *rdev, + struct radeon_atom_ss *ss, + int id) +{ + struct radeon_mode_info *mode_info = &rdev->mode_info; + int index = GetIndexIntoMasterTable(DATA, IntegratedSystemInfo); + u16 data_offset, size; + union igp_info *igp_info; + u8 frev, crev; + u16 percentage = 0, rate = 0; + + /* get any igp specific overrides */ + if (atom_parse_data_header(mode_info->atom_context, index, &size, + &frev, &crev, &data_offset)) { + igp_info = (union igp_info *) + ((char *)mode_info->atom_context->bios + data_offset); + switch (crev) { + case 6: + switch (id) { + case ASIC_INTERNAL_SS_ON_TMDS: + percentage = le16_to_cpu(igp_info->info_6.usDVISSPercentage); + rate = le16_to_cpu(igp_info->info_6.usDVISSpreadRateIn10Hz); + break; + case ASIC_INTERNAL_SS_ON_HDMI: + percentage = le16_to_cpu(igp_info->info_6.usHDMISSPercentage); + rate = le16_to_cpu(igp_info->info_6.usHDMISSpreadRateIn10Hz); + break; + case ASIC_INTERNAL_SS_ON_LVDS: + percentage = le16_to_cpu(igp_info->info_6.usLvdsSSPercentage); + rate = le16_to_cpu(igp_info->info_6.usLvdsSSpreadRateIn10Hz); + break; + } + break; + case 7: + switch (id) { + case ASIC_INTERNAL_SS_ON_TMDS: + percentage = le16_to_cpu(igp_info->info_7.usDVISSPercentage); + rate = le16_to_cpu(igp_info->info_7.usDVISSpreadRateIn10Hz); + break; + case ASIC_INTERNAL_SS_ON_HDMI: + percentage = le16_to_cpu(igp_info->info_7.usHDMISSPercentage); + rate = le16_to_cpu(igp_info->info_7.usHDMISSpreadRateIn10Hz); + break; + case ASIC_INTERNAL_SS_ON_LVDS: + percentage = le16_to_cpu(igp_info->info_7.usLvdsSSPercentage); + rate = le16_to_cpu(igp_info->info_7.usLvdsSSpreadRateIn10Hz); + break; + } + break; + default: + DRM_ERROR("Unsupported IGP table: %d %d\n", frev, crev); + break; + } + if (percentage) + ss->percentage = percentage; + if (rate) + ss->rate = rate; + } +} + +union asic_ss_info { + struct _ATOM_ASIC_INTERNAL_SS_INFO info; + struct _ATOM_ASIC_INTERNAL_SS_INFO_V2 info_2; + struct _ATOM_ASIC_INTERNAL_SS_INFO_V3 info_3; +}; + +bool radeon_atombios_get_asic_ss_info(struct radeon_device *rdev, + struct radeon_atom_ss *ss, + int id, u32 clock) +{ + struct radeon_mode_info *mode_info = &rdev->mode_info; + int index = GetIndexIntoMasterTable(DATA, ASIC_InternalSS_Info); + uint16_t data_offset, size; + union asic_ss_info *ss_info; + uint8_t frev, crev; + int i, num_indices; + + memset(ss, 0, sizeof(struct radeon_atom_ss)); + if (atom_parse_data_header(mode_info->atom_context, index, &size, + &frev, &crev, &data_offset)) { + + ss_info = + (union asic_ss_info *)((char *)mode_info->atom_context->bios + data_offset); + + switch (frev) { + case 1: + num_indices = (size - sizeof(ATOM_COMMON_TABLE_HEADER)) / + sizeof(ATOM_ASIC_SS_ASSIGNMENT); + + for (i = 0; i < num_indices; i++) { + if ((ss_info->info.asSpreadSpectrum[i].ucClockIndication == id) && + (clock <= le32_to_cpu(ss_info->info.asSpreadSpectrum[i].ulTargetClockRange))) { + ss->percentage = + le16_to_cpu(ss_info->info.asSpreadSpectrum[i].usSpreadSpectrumPercentage); + ss->type = ss_info->info.asSpreadSpectrum[i].ucSpreadSpectrumMode; + ss->rate = le16_to_cpu(ss_info->info.asSpreadSpectrum[i].usSpreadRateInKhz); + return true; + } + } + break; + case 2: + num_indices = (size - sizeof(ATOM_COMMON_TABLE_HEADER)) / + sizeof(ATOM_ASIC_SS_ASSIGNMENT_V2); + for (i = 0; i < num_indices; i++) { + if ((ss_info->info_2.asSpreadSpectrum[i].ucClockIndication == id) && + (clock <= le32_to_cpu(ss_info->info_2.asSpreadSpectrum[i].ulTargetClockRange))) { + ss->percentage = + le16_to_cpu(ss_info->info_2.asSpreadSpectrum[i].usSpreadSpectrumPercentage); + ss->type = ss_info->info_2.asSpreadSpectrum[i].ucSpreadSpectrumMode; + ss->rate = le16_to_cpu(ss_info->info_2.asSpreadSpectrum[i].usSpreadRateIn10Hz); + return true; + } + } + break; + case 3: + num_indices = (size - sizeof(ATOM_COMMON_TABLE_HEADER)) / + sizeof(ATOM_ASIC_SS_ASSIGNMENT_V3); + for (i = 0; i < num_indices; i++) { + if ((ss_info->info_3.asSpreadSpectrum[i].ucClockIndication == id) && + (clock <= le32_to_cpu(ss_info->info_3.asSpreadSpectrum[i].ulTargetClockRange))) { + ss->percentage = + le16_to_cpu(ss_info->info_3.asSpreadSpectrum[i].usSpreadSpectrumPercentage); + ss->type = ss_info->info_3.asSpreadSpectrum[i].ucSpreadSpectrumMode; + ss->rate = le16_to_cpu(ss_info->info_3.asSpreadSpectrum[i].usSpreadRateIn10Hz); + if (rdev->flags & RADEON_IS_IGP) + radeon_atombios_get_igp_ss_overrides(rdev, ss, id); + return true; + } + } + break; + default: + DRM_ERROR("Unsupported ASIC_InternalSS_Info table: %d %d\n", frev, crev); + break; + } + + } + return false; +} + +union lvds_info { + struct _ATOM_LVDS_INFO info; + struct _ATOM_LVDS_INFO_V12 info_12; +}; + +struct radeon_encoder_atom_dig *radeon_atombios_get_lvds_info(struct + radeon_encoder + *encoder) +{ + struct drm_device *dev = encoder->base.dev; + struct radeon_device *rdev = dev->dev_private; + struct radeon_mode_info *mode_info = &rdev->mode_info; + int index = GetIndexIntoMasterTable(DATA, LVDS_Info); + uint16_t data_offset, misc; + union lvds_info *lvds_info; + uint8_t frev, crev; + struct radeon_encoder_atom_dig *lvds = NULL; + int encoder_enum = (encoder->encoder_enum & ENUM_ID_MASK) >> ENUM_ID_SHIFT; + + if (atom_parse_data_header(mode_info->atom_context, index, NULL, + &frev, &crev, &data_offset)) { + lvds_info = + (union lvds_info *)((char *)mode_info->atom_context->bios + data_offset); + lvds = + malloc(sizeof(struct radeon_encoder_atom_dig), + DRM_MEM_DRIVER, M_WAITOK | M_ZERO); + + if (!lvds) + return NULL; + + lvds->native_mode.clock = + le16_to_cpu(lvds_info->info.sLCDTiming.usPixClk) * 10; + lvds->native_mode.hdisplay = + le16_to_cpu(lvds_info->info.sLCDTiming.usHActive); + lvds->native_mode.vdisplay = + le16_to_cpu(lvds_info->info.sLCDTiming.usVActive); + lvds->native_mode.htotal = lvds->native_mode.hdisplay + + le16_to_cpu(lvds_info->info.sLCDTiming.usHBlanking_Time); + lvds->native_mode.hsync_start = lvds->native_mode.hdisplay + + le16_to_cpu(lvds_info->info.sLCDTiming.usHSyncOffset); + lvds->native_mode.hsync_end = lvds->native_mode.hsync_start + + le16_to_cpu(lvds_info->info.sLCDTiming.usHSyncWidth); + lvds->native_mode.vtotal = lvds->native_mode.vdisplay + + le16_to_cpu(lvds_info->info.sLCDTiming.usVBlanking_Time); + lvds->native_mode.vsync_start = lvds->native_mode.vdisplay + + le16_to_cpu(lvds_info->info.sLCDTiming.usVSyncOffset); + lvds->native_mode.vsync_end = lvds->native_mode.vsync_start + + le16_to_cpu(lvds_info->info.sLCDTiming.usVSyncWidth); + lvds->panel_pwr_delay = + le16_to_cpu(lvds_info->info.usOffDelayInMs); + lvds->lcd_misc = lvds_info->info.ucLVDS_Misc; + + misc = le16_to_cpu(lvds_info->info.sLCDTiming.susModeMiscInfo.usAccess); + if (misc & ATOM_VSYNC_POLARITY) + lvds->native_mode.flags |= DRM_MODE_FLAG_NVSYNC; + if (misc & ATOM_HSYNC_POLARITY) + lvds->native_mode.flags |= DRM_MODE_FLAG_NHSYNC; + if (misc & ATOM_COMPOSITESYNC) + lvds->native_mode.flags |= DRM_MODE_FLAG_CSYNC; + if (misc & ATOM_INTERLACE) + lvds->native_mode.flags |= DRM_MODE_FLAG_INTERLACE; + if (misc & ATOM_DOUBLE_CLOCK_MODE) + lvds->native_mode.flags |= DRM_MODE_FLAG_DBLSCAN; + + lvds->native_mode.width_mm = le16_to_cpu(lvds_info->info.sLCDTiming.usImageHSize); + lvds->native_mode.height_mm = le16_to_cpu(lvds_info->info.sLCDTiming.usImageVSize); + + /* set crtc values */ + drm_mode_set_crtcinfo(&lvds->native_mode, CRTC_INTERLACE_HALVE_V); + + lvds->lcd_ss_id = lvds_info->info.ucSS_Id; + + encoder->native_mode = lvds->native_mode; + + if (encoder_enum == 2) + lvds->linkb = true; + else + lvds->linkb = false; + + /* parse the lcd record table */ + if (le16_to_cpu(lvds_info->info.usModePatchTableOffset)) { + ATOM_FAKE_EDID_PATCH_RECORD *fake_edid_record; + ATOM_PANEL_RESOLUTION_PATCH_RECORD *panel_res_record; + bool bad_record = false; + u8 *record; + + if ((frev == 1) && (crev < 2)) + /* absolute */ + record = (u8 *)((char *)mode_info->atom_context->bios + + le16_to_cpu(lvds_info->info.usModePatchTableOffset)); + else + /* relative */ + record = (u8 *)((char *)mode_info->atom_context->bios + + data_offset + + le16_to_cpu(lvds_info->info.usModePatchTableOffset)); + while (*record != ATOM_RECORD_END_TYPE) { + switch (*record) { + case LCD_MODE_PATCH_RECORD_MODE_TYPE: + record += sizeof(ATOM_PATCH_RECORD_MODE); + break; + case LCD_RTS_RECORD_TYPE: + record += sizeof(ATOM_LCD_RTS_RECORD); + break; + case LCD_CAP_RECORD_TYPE: + record += sizeof(ATOM_LCD_MODE_CONTROL_CAP); + break; + case LCD_FAKE_EDID_PATCH_RECORD_TYPE: + fake_edid_record = (ATOM_FAKE_EDID_PATCH_RECORD *)record; + if (fake_edid_record->ucFakeEDIDLength) { + struct edid *edid; + int edid_size = + max((int)EDID_LENGTH, (int)fake_edid_record->ucFakeEDIDLength); + edid = malloc(edid_size, DRM_MEM_KMS, M_WAITOK); + if (edid) { + memcpy((u8 *)edid, (u8 *)&fake_edid_record->ucFakeEDIDString[0], + fake_edid_record->ucFakeEDIDLength); + + if (drm_edid_is_valid(edid)) { + rdev->mode_info.bios_hardcoded_edid = edid; + rdev->mode_info.bios_hardcoded_edid_size = edid_size; + } else + free(edid, DRM_MEM_KMS); + } + } + record += sizeof(ATOM_FAKE_EDID_PATCH_RECORD); + break; + case LCD_PANEL_RESOLUTION_RECORD_TYPE: + panel_res_record = (ATOM_PANEL_RESOLUTION_PATCH_RECORD *)record; + lvds->native_mode.width_mm = panel_res_record->usHSize; + lvds->native_mode.height_mm = panel_res_record->usVSize; + record += sizeof(ATOM_PANEL_RESOLUTION_PATCH_RECORD); + break; + default: + DRM_ERROR("Bad LCD record %d\n", *record); + bad_record = true; + break; + } + if (bad_record) + break; + } + } + } + return lvds; +} + +struct radeon_encoder_primary_dac * +radeon_atombios_get_primary_dac_info(struct radeon_encoder *encoder) +{ + struct drm_device *dev = encoder->base.dev; + struct radeon_device *rdev = dev->dev_private; + struct radeon_mode_info *mode_info = &rdev->mode_info; + int index = GetIndexIntoMasterTable(DATA, CompassionateData); + uint16_t data_offset; + struct _COMPASSIONATE_DATA *dac_info; + uint8_t frev, crev; + uint8_t bg, dac; + struct radeon_encoder_primary_dac *p_dac = NULL; + + if (atom_parse_data_header(mode_info->atom_context, index, NULL, + &frev, &crev, &data_offset)) { + dac_info = (struct _COMPASSIONATE_DATA *) + ((char *)mode_info->atom_context->bios + data_offset); + + p_dac = malloc(sizeof(struct radeon_encoder_primary_dac), + DRM_MEM_DRIVER, M_WAITOK | M_ZERO); + + if (!p_dac) + return NULL; + + bg = dac_info->ucDAC1_BG_Adjustment; + dac = dac_info->ucDAC1_DAC_Adjustment; + p_dac->ps2_pdac_adj = (bg << 8) | (dac); + + } + return p_dac; +} + +bool radeon_atom_get_tv_timings(struct radeon_device *rdev, int index, + struct drm_display_mode *mode) +{ + struct radeon_mode_info *mode_info = &rdev->mode_info; + ATOM_ANALOG_TV_INFO *tv_info; + ATOM_ANALOG_TV_INFO_V1_2 *tv_info_v1_2; + ATOM_DTD_FORMAT *dtd_timings; + int data_index = GetIndexIntoMasterTable(DATA, AnalogTV_Info); + u8 frev, crev; + u16 data_offset, misc; + + if (!atom_parse_data_header(mode_info->atom_context, data_index, NULL, + &frev, &crev, &data_offset)) + return false; + + switch (crev) { + case 1: + tv_info = (ATOM_ANALOG_TV_INFO *)((char *)mode_info->atom_context->bios + data_offset); + if (index >= MAX_SUPPORTED_TV_TIMING) + return false; + + mode->crtc_htotal = le16_to_cpu(tv_info->aModeTimings[index].usCRTC_H_Total); + mode->crtc_hdisplay = le16_to_cpu(tv_info->aModeTimings[index].usCRTC_H_Disp); + mode->crtc_hsync_start = le16_to_cpu(tv_info->aModeTimings[index].usCRTC_H_SyncStart); + mode->crtc_hsync_end = le16_to_cpu(tv_info->aModeTimings[index].usCRTC_H_SyncStart) + + le16_to_cpu(tv_info->aModeTimings[index].usCRTC_H_SyncWidth); + + mode->crtc_vtotal = le16_to_cpu(tv_info->aModeTimings[index].usCRTC_V_Total); + mode->crtc_vdisplay = le16_to_cpu(tv_info->aModeTimings[index].usCRTC_V_Disp); + mode->crtc_vsync_start = le16_to_cpu(tv_info->aModeTimings[index].usCRTC_V_SyncStart); + mode->crtc_vsync_end = le16_to_cpu(tv_info->aModeTimings[index].usCRTC_V_SyncStart) + + le16_to_cpu(tv_info->aModeTimings[index].usCRTC_V_SyncWidth); + + mode->flags = 0; + misc = le16_to_cpu(tv_info->aModeTimings[index].susModeMiscInfo.usAccess); + if (misc & ATOM_VSYNC_POLARITY) + mode->flags |= DRM_MODE_FLAG_NVSYNC; + if (misc & ATOM_HSYNC_POLARITY) + mode->flags |= DRM_MODE_FLAG_NHSYNC; + if (misc & ATOM_COMPOSITESYNC) + mode->flags |= DRM_MODE_FLAG_CSYNC; + if (misc & ATOM_INTERLACE) + mode->flags |= DRM_MODE_FLAG_INTERLACE; + if (misc & ATOM_DOUBLE_CLOCK_MODE) + mode->flags |= DRM_MODE_FLAG_DBLSCAN; + + mode->clock = le16_to_cpu(tv_info->aModeTimings[index].usPixelClock) * 10; + + if (index == 1) { + /* PAL timings appear to have wrong values for totals */ + mode->crtc_htotal -= 1; + mode->crtc_vtotal -= 1; + } + break; + case 2: + tv_info_v1_2 = (ATOM_ANALOG_TV_INFO_V1_2 *)((char *)mode_info->atom_context->bios + data_offset); + if (index >= MAX_SUPPORTED_TV_TIMING_V1_2) + return false; + + dtd_timings = &tv_info_v1_2->aModeTimings[index]; + mode->crtc_htotal = le16_to_cpu(dtd_timings->usHActive) + + le16_to_cpu(dtd_timings->usHBlanking_Time); + mode->crtc_hdisplay = le16_to_cpu(dtd_timings->usHActive); + mode->crtc_hsync_start = le16_to_cpu(dtd_timings->usHActive) + + le16_to_cpu(dtd_timings->usHSyncOffset); + mode->crtc_hsync_end = mode->crtc_hsync_start + + le16_to_cpu(dtd_timings->usHSyncWidth); + + mode->crtc_vtotal = le16_to_cpu(dtd_timings->usVActive) + + le16_to_cpu(dtd_timings->usVBlanking_Time); + mode->crtc_vdisplay = le16_to_cpu(dtd_timings->usVActive); + mode->crtc_vsync_start = le16_to_cpu(dtd_timings->usVActive) + + le16_to_cpu(dtd_timings->usVSyncOffset); + mode->crtc_vsync_end = mode->crtc_vsync_start + + le16_to_cpu(dtd_timings->usVSyncWidth); + + mode->flags = 0; + misc = le16_to_cpu(dtd_timings->susModeMiscInfo.usAccess); + if (misc & ATOM_VSYNC_POLARITY) + mode->flags |= DRM_MODE_FLAG_NVSYNC; + if (misc & ATOM_HSYNC_POLARITY) + mode->flags |= DRM_MODE_FLAG_NHSYNC; + if (misc & ATOM_COMPOSITESYNC) + mode->flags |= DRM_MODE_FLAG_CSYNC; + if (misc & ATOM_INTERLACE) + mode->flags |= DRM_MODE_FLAG_INTERLACE; + if (misc & ATOM_DOUBLE_CLOCK_MODE) + mode->flags |= DRM_MODE_FLAG_DBLSCAN; + + mode->clock = le16_to_cpu(dtd_timings->usPixClk) * 10; + break; + } + return true; +} + +enum radeon_tv_std +radeon_atombios_get_tv_info(struct radeon_device *rdev) +{ + struct radeon_mode_info *mode_info = &rdev->mode_info; + int index = GetIndexIntoMasterTable(DATA, AnalogTV_Info); + uint16_t data_offset; + uint8_t frev, crev; + struct _ATOM_ANALOG_TV_INFO *tv_info; + enum radeon_tv_std tv_std = TV_STD_NTSC; + + if (atom_parse_data_header(mode_info->atom_context, index, NULL, + &frev, &crev, &data_offset)) { + + tv_info = (struct _ATOM_ANALOG_TV_INFO *) + ((char *)mode_info->atom_context->bios + data_offset); + + switch (tv_info->ucTV_BootUpDefaultStandard) { + case ATOM_TV_NTSC: + tv_std = TV_STD_NTSC; + DRM_DEBUG_KMS("Default TV standard: NTSC\n"); + break; + case ATOM_TV_NTSCJ: + tv_std = TV_STD_NTSC_J; + DRM_DEBUG_KMS("Default TV standard: NTSC-J\n"); + break; + case ATOM_TV_PAL: + tv_std = TV_STD_PAL; + DRM_DEBUG_KMS("Default TV standard: PAL\n"); + break; + case ATOM_TV_PALM: + tv_std = TV_STD_PAL_M; + DRM_DEBUG_KMS("Default TV standard: PAL-M\n"); + break; + case ATOM_TV_PALN: + tv_std = TV_STD_PAL_N; + DRM_DEBUG_KMS("Default TV standard: PAL-N\n"); + break; + case ATOM_TV_PALCN: + tv_std = TV_STD_PAL_CN; + DRM_DEBUG_KMS("Default TV standard: PAL-CN\n"); + break; + case ATOM_TV_PAL60: + tv_std = TV_STD_PAL_60; + DRM_DEBUG_KMS("Default TV standard: PAL-60\n"); + break; + case ATOM_TV_SECAM: + tv_std = TV_STD_SECAM; + DRM_DEBUG_KMS("Default TV standard: SECAM\n"); + break; + default: + tv_std = TV_STD_NTSC; + DRM_DEBUG_KMS("Unknown TV standard; defaulting to NTSC\n"); + break; + } + } + return tv_std; +} + +struct radeon_encoder_tv_dac * +radeon_atombios_get_tv_dac_info(struct radeon_encoder *encoder) +{ + struct drm_device *dev = encoder->base.dev; + struct radeon_device *rdev = dev->dev_private; + struct radeon_mode_info *mode_info = &rdev->mode_info; + int index = GetIndexIntoMasterTable(DATA, CompassionateData); + uint16_t data_offset; + struct _COMPASSIONATE_DATA *dac_info; + uint8_t frev, crev; + uint8_t bg, dac; + struct radeon_encoder_tv_dac *tv_dac = NULL; + + if (atom_parse_data_header(mode_info->atom_context, index, NULL, + &frev, &crev, &data_offset)) { + + dac_info = (struct _COMPASSIONATE_DATA *) + ((char *)mode_info->atom_context->bios + data_offset); + + tv_dac = malloc(sizeof(struct radeon_encoder_tv_dac), + DRM_MEM_DRIVER, M_WAITOK | M_ZERO); + + if (!tv_dac) + return NULL; + + bg = dac_info->ucDAC2_CRT2_BG_Adjustment; + dac = dac_info->ucDAC2_CRT2_DAC_Adjustment; + tv_dac->ps2_tvdac_adj = (bg << 16) | (dac << 20); + + bg = dac_info->ucDAC2_PAL_BG_Adjustment; + dac = dac_info->ucDAC2_PAL_DAC_Adjustment; + tv_dac->pal_tvdac_adj = (bg << 16) | (dac << 20); + + bg = dac_info->ucDAC2_NTSC_BG_Adjustment; + dac = dac_info->ucDAC2_NTSC_DAC_Adjustment; + tv_dac->ntsc_tvdac_adj = (bg << 16) | (dac << 20); + + tv_dac->tv_std = radeon_atombios_get_tv_info(rdev); + } + return tv_dac; +} + +static const char *thermal_controller_names[] = { + "NONE", + "lm63", + "adm1032", + "adm1030", + "max6649", + "lm64", + "f75375", + "asc7xxx", +}; + +static const char *pp_lib_thermal_controller_names[] = { + "NONE", + "lm63", + "adm1032", + "adm1030", + "max6649", + "lm64", + "f75375", + "RV6xx", + "RV770", + "adt7473", + "NONE", + "External GPIO", + "Evergreen", + "emc2103", + "Sumo", + "Northern Islands", + "Southern Islands", + "lm96163", +}; + +union power_info { + struct _ATOM_POWERPLAY_INFO info; + struct _ATOM_POWERPLAY_INFO_V2 info_2; + struct _ATOM_POWERPLAY_INFO_V3 info_3; + struct _ATOM_PPLIB_POWERPLAYTABLE pplib; + struct _ATOM_PPLIB_POWERPLAYTABLE2 pplib2; + struct _ATOM_PPLIB_POWERPLAYTABLE3 pplib3; +}; + +union pplib_clock_info { + struct _ATOM_PPLIB_R600_CLOCK_INFO r600; + struct _ATOM_PPLIB_RS780_CLOCK_INFO rs780; + struct _ATOM_PPLIB_EVERGREEN_CLOCK_INFO evergreen; + struct _ATOM_PPLIB_SUMO_CLOCK_INFO sumo; + struct _ATOM_PPLIB_SI_CLOCK_INFO si; +}; + +union pplib_power_state { + struct _ATOM_PPLIB_STATE v1; + struct _ATOM_PPLIB_STATE_V2 v2; +}; + +static void radeon_atombios_parse_misc_flags_1_3(struct radeon_device *rdev, + int state_index, + u32 misc, u32 misc2) +{ + rdev->pm.power_state[state_index].misc = misc; + rdev->pm.power_state[state_index].misc2 = misc2; + /* order matters! */ + if (misc & ATOM_PM_MISCINFO_POWER_SAVING_MODE) + rdev->pm.power_state[state_index].type = + POWER_STATE_TYPE_POWERSAVE; + if (misc & ATOM_PM_MISCINFO_DEFAULT_DC_STATE_ENTRY_TRUE) + rdev->pm.power_state[state_index].type = + POWER_STATE_TYPE_BATTERY; + if (misc & ATOM_PM_MISCINFO_DEFAULT_LOW_DC_STATE_ENTRY_TRUE) + rdev->pm.power_state[state_index].type = + POWER_STATE_TYPE_BATTERY; + if (misc & ATOM_PM_MISCINFO_LOAD_BALANCE_EN) + rdev->pm.power_state[state_index].type = + POWER_STATE_TYPE_BALANCED; + if (misc & ATOM_PM_MISCINFO_3D_ACCELERATION_EN) { + rdev->pm.power_state[state_index].type = + POWER_STATE_TYPE_PERFORMANCE; + rdev->pm.power_state[state_index].flags &= + ~RADEON_PM_STATE_SINGLE_DISPLAY_ONLY; + } + if (misc2 & ATOM_PM_MISCINFO2_SYSTEM_AC_LITE_MODE) + rdev->pm.power_state[state_index].type = + POWER_STATE_TYPE_BALANCED; + if (misc & ATOM_PM_MISCINFO_DRIVER_DEFAULT_MODE) { + rdev->pm.power_state[state_index].type = + POWER_STATE_TYPE_DEFAULT; + rdev->pm.default_power_state_index = state_index; + rdev->pm.power_state[state_index].default_clock_mode = + &rdev->pm.power_state[state_index].clock_info[0]; + } else if (state_index == 0) { + rdev->pm.power_state[state_index].clock_info[0].flags |= + RADEON_PM_MODE_NO_DISPLAY; + } +} + +static int radeon_atombios_parse_power_table_1_3(struct radeon_device *rdev) +{ + struct radeon_mode_info *mode_info = &rdev->mode_info; + u32 misc, misc2 = 0; + int num_modes = 0, i; + int state_index = 0; + struct radeon_i2c_bus_rec i2c_bus; + union power_info *power_info; + int index = GetIndexIntoMasterTable(DATA, PowerPlayInfo); + u16 data_offset; + u8 frev, crev; + + if (!atom_parse_data_header(mode_info->atom_context, index, NULL, + &frev, &crev, &data_offset)) + return state_index; + power_info = (union power_info *)((char *)mode_info->atom_context->bios + data_offset); + + /* add the i2c bus for thermal/fan chip */ + if ((power_info->info.ucOverdriveThermalController > 0) && + (power_info->info.ucOverdriveThermalController < DRM_ARRAY_SIZE(thermal_controller_names))) { + DRM_INFO("Possible %s thermal controller at 0x%02x\n", + thermal_controller_names[power_info->info.ucOverdriveThermalController], + power_info->info.ucOverdriveControllerAddress >> 1); + i2c_bus = radeon_lookup_i2c_gpio(rdev, power_info->info.ucOverdriveI2cLine); + rdev->pm.i2c_bus = radeon_i2c_lookup(rdev, &i2c_bus); +#ifdef DUMBBELL_WIP + if (rdev->pm.i2c_bus) { + struct i2c_board_info info = { }; + const char *name = thermal_controller_names[power_info->info. + ucOverdriveThermalController]; + info.addr = power_info->info.ucOverdriveControllerAddress >> 1; + strlcpy(info.type, name, sizeof(info.type)); + i2c_new_device(&rdev->pm.i2c_bus->adapter, &info); + } +#endif /* DUMBBELL_WIP */ + } + num_modes = power_info->info.ucNumOfPowerModeEntries; + if (num_modes > ATOM_MAX_NUMBEROF_POWER_BLOCK) + num_modes = ATOM_MAX_NUMBEROF_POWER_BLOCK; + rdev->pm.power_state = malloc(sizeof(struct radeon_power_state) * num_modes, + DRM_MEM_DRIVER, M_WAITOK | M_ZERO); + if (!rdev->pm.power_state) + return state_index; + /* last mode is usually default, array is low to high */ + for (i = 0; i < num_modes; i++) { + rdev->pm.power_state[state_index].clock_info = + malloc(sizeof(struct radeon_pm_clock_info) * 1, + DRM_MEM_DRIVER, M_WAITOK | M_ZERO); + if (!rdev->pm.power_state[state_index].clock_info) + return state_index; + rdev->pm.power_state[state_index].num_clock_modes = 1; + rdev->pm.power_state[state_index].clock_info[0].voltage.type = VOLTAGE_NONE; + switch (frev) { + case 1: + rdev->pm.power_state[state_index].clock_info[0].mclk = + le16_to_cpu(power_info->info.asPowerPlayInfo[i].usMemoryClock); + rdev->pm.power_state[state_index].clock_info[0].sclk = + le16_to_cpu(power_info->info.asPowerPlayInfo[i].usEngineClock); + /* skip invalid modes */ + if ((rdev->pm.power_state[state_index].clock_info[0].mclk == 0) || + (rdev->pm.power_state[state_index].clock_info[0].sclk == 0)) + continue; + rdev->pm.power_state[state_index].pcie_lanes = + power_info->info.asPowerPlayInfo[i].ucNumPciELanes; + misc = le32_to_cpu(power_info->info.asPowerPlayInfo[i].ulMiscInfo); + if ((misc & ATOM_PM_MISCINFO_VOLTAGE_DROP_SUPPORT) || + (misc & ATOM_PM_MISCINFO_VOLTAGE_DROP_ACTIVE_HIGH)) { + rdev->pm.power_state[state_index].clock_info[0].voltage.type = + VOLTAGE_GPIO; + rdev->pm.power_state[state_index].clock_info[0].voltage.gpio = + radeon_lookup_gpio(rdev, + power_info->info.asPowerPlayInfo[i].ucVoltageDropIndex); + if (misc & ATOM_PM_MISCINFO_VOLTAGE_DROP_ACTIVE_HIGH) + rdev->pm.power_state[state_index].clock_info[0].voltage.active_high = + true; + else + rdev->pm.power_state[state_index].clock_info[0].voltage.active_high = + false; + } else if (misc & ATOM_PM_MISCINFO_PROGRAM_VOLTAGE) { + rdev->pm.power_state[state_index].clock_info[0].voltage.type = + VOLTAGE_VDDC; + rdev->pm.power_state[state_index].clock_info[0].voltage.vddc_id = + power_info->info.asPowerPlayInfo[i].ucVoltageDropIndex; + } + rdev->pm.power_state[state_index].flags = RADEON_PM_STATE_SINGLE_DISPLAY_ONLY; + radeon_atombios_parse_misc_flags_1_3(rdev, state_index, misc, 0); + state_index++; + break; + case 2: + rdev->pm.power_state[state_index].clock_info[0].mclk = + le32_to_cpu(power_info->info_2.asPowerPlayInfo[i].ulMemoryClock); + rdev->pm.power_state[state_index].clock_info[0].sclk = + le32_to_cpu(power_info->info_2.asPowerPlayInfo[i].ulEngineClock); + /* skip invalid modes */ + if ((rdev->pm.power_state[state_index].clock_info[0].mclk == 0) || + (rdev->pm.power_state[state_index].clock_info[0].sclk == 0)) + continue; + rdev->pm.power_state[state_index].pcie_lanes = + power_info->info_2.asPowerPlayInfo[i].ucNumPciELanes; + misc = le32_to_cpu(power_info->info_2.asPowerPlayInfo[i].ulMiscInfo); + misc2 = le32_to_cpu(power_info->info_2.asPowerPlayInfo[i].ulMiscInfo2); + if ((misc & ATOM_PM_MISCINFO_VOLTAGE_DROP_SUPPORT) || + (misc & ATOM_PM_MISCINFO_VOLTAGE_DROP_ACTIVE_HIGH)) { + rdev->pm.power_state[state_index].clock_info[0].voltage.type = + VOLTAGE_GPIO; + rdev->pm.power_state[state_index].clock_info[0].voltage.gpio = + radeon_lookup_gpio(rdev, + power_info->info_2.asPowerPlayInfo[i].ucVoltageDropIndex); + if (misc & ATOM_PM_MISCINFO_VOLTAGE_DROP_ACTIVE_HIGH) + rdev->pm.power_state[state_index].clock_info[0].voltage.active_high = + true; + else + rdev->pm.power_state[state_index].clock_info[0].voltage.active_high = + false; + } else if (misc & ATOM_PM_MISCINFO_PROGRAM_VOLTAGE) { + rdev->pm.power_state[state_index].clock_info[0].voltage.type = + VOLTAGE_VDDC; + rdev->pm.power_state[state_index].clock_info[0].voltage.vddc_id = + power_info->info_2.asPowerPlayInfo[i].ucVoltageDropIndex; + } + rdev->pm.power_state[state_index].flags = RADEON_PM_STATE_SINGLE_DISPLAY_ONLY; + radeon_atombios_parse_misc_flags_1_3(rdev, state_index, misc, misc2); + state_index++; + break; + case 3: + rdev->pm.power_state[state_index].clock_info[0].mclk = + le32_to_cpu(power_info->info_3.asPowerPlayInfo[i].ulMemoryClock); + rdev->pm.power_state[state_index].clock_info[0].sclk = + le32_to_cpu(power_info->info_3.asPowerPlayInfo[i].ulEngineClock); + /* skip invalid modes */ + if ((rdev->pm.power_state[state_index].clock_info[0].mclk == 0) || + (rdev->pm.power_state[state_index].clock_info[0].sclk == 0)) + continue; + rdev->pm.power_state[state_index].pcie_lanes = + power_info->info_3.asPowerPlayInfo[i].ucNumPciELanes; + misc = le32_to_cpu(power_info->info_3.asPowerPlayInfo[i].ulMiscInfo); + misc2 = le32_to_cpu(power_info->info_3.asPowerPlayInfo[i].ulMiscInfo2); + if ((misc & ATOM_PM_MISCINFO_VOLTAGE_DROP_SUPPORT) || + (misc & ATOM_PM_MISCINFO_VOLTAGE_DROP_ACTIVE_HIGH)) { + rdev->pm.power_state[state_index].clock_info[0].voltage.type = + VOLTAGE_GPIO; + rdev->pm.power_state[state_index].clock_info[0].voltage.gpio = + radeon_lookup_gpio(rdev, + power_info->info_3.asPowerPlayInfo[i].ucVoltageDropIndex); + if (misc & ATOM_PM_MISCINFO_VOLTAGE_DROP_ACTIVE_HIGH) + rdev->pm.power_state[state_index].clock_info[0].voltage.active_high = + true; + else + rdev->pm.power_state[state_index].clock_info[0].voltage.active_high = + false; + } else if (misc & ATOM_PM_MISCINFO_PROGRAM_VOLTAGE) { + rdev->pm.power_state[state_index].clock_info[0].voltage.type = + VOLTAGE_VDDC; + rdev->pm.power_state[state_index].clock_info[0].voltage.vddc_id = + power_info->info_3.asPowerPlayInfo[i].ucVoltageDropIndex; + if (misc2 & ATOM_PM_MISCINFO2_VDDCI_DYNAMIC_VOLTAGE_EN) { + rdev->pm.power_state[state_index].clock_info[0].voltage.vddci_enabled = + true; + rdev->pm.power_state[state_index].clock_info[0].voltage.vddci_id = + power_info->info_3.asPowerPlayInfo[i].ucVDDCI_VoltageDropIndex; + } + } + rdev->pm.power_state[state_index].flags = RADEON_PM_STATE_SINGLE_DISPLAY_ONLY; + radeon_atombios_parse_misc_flags_1_3(rdev, state_index, misc, misc2); + state_index++; + break; + } + } + /* last mode is usually default */ + if (rdev->pm.default_power_state_index == -1) { + rdev->pm.power_state[state_index - 1].type = + POWER_STATE_TYPE_DEFAULT; + rdev->pm.default_power_state_index = state_index - 1; + rdev->pm.power_state[state_index - 1].default_clock_mode = + &rdev->pm.power_state[state_index - 1].clock_info[0]; + rdev->pm.power_state[state_index].flags &= + ~RADEON_PM_STATE_SINGLE_DISPLAY_ONLY; + rdev->pm.power_state[state_index].misc = 0; + rdev->pm.power_state[state_index].misc2 = 0; + } + return state_index; +} + +static void radeon_atombios_add_pplib_thermal_controller(struct radeon_device *rdev, + ATOM_PPLIB_THERMALCONTROLLER *controller) +{ + struct radeon_i2c_bus_rec i2c_bus; + + /* add the i2c bus for thermal/fan chip */ + if (controller->ucType > 0) { + if (controller->ucType == ATOM_PP_THERMALCONTROLLER_RV6xx) { + DRM_INFO("Internal thermal controller %s fan control\n", + (controller->ucFanParameters & + ATOM_PP_FANPARAMETERS_NOFAN) ? "without" : "with"); + rdev->pm.int_thermal_type = THERMAL_TYPE_RV6XX; + } else if (controller->ucType == ATOM_PP_THERMALCONTROLLER_RV770) { + DRM_INFO("Internal thermal controller %s fan control\n", + (controller->ucFanParameters & + ATOM_PP_FANPARAMETERS_NOFAN) ? "without" : "with"); + rdev->pm.int_thermal_type = THERMAL_TYPE_RV770; + } else if (controller->ucType == ATOM_PP_THERMALCONTROLLER_EVERGREEN) { + DRM_INFO("Internal thermal controller %s fan control\n", + (controller->ucFanParameters & + ATOM_PP_FANPARAMETERS_NOFAN) ? "without" : "with"); + rdev->pm.int_thermal_type = THERMAL_TYPE_EVERGREEN; + } else if (controller->ucType == ATOM_PP_THERMALCONTROLLER_SUMO) { + DRM_INFO("Internal thermal controller %s fan control\n", + (controller->ucFanParameters & + ATOM_PP_FANPARAMETERS_NOFAN) ? "without" : "with"); + rdev->pm.int_thermal_type = THERMAL_TYPE_SUMO; + } else if (controller->ucType == ATOM_PP_THERMALCONTROLLER_NISLANDS) { + DRM_INFO("Internal thermal controller %s fan control\n", + (controller->ucFanParameters & + ATOM_PP_FANPARAMETERS_NOFAN) ? "without" : "with"); + rdev->pm.int_thermal_type = THERMAL_TYPE_NI; + } else if (controller->ucType == ATOM_PP_THERMALCONTROLLER_SISLANDS) { + DRM_INFO("Internal thermal controller %s fan control\n", + (controller->ucFanParameters & + ATOM_PP_FANPARAMETERS_NOFAN) ? "without" : "with"); + rdev->pm.int_thermal_type = THERMAL_TYPE_SI; + } else if ((controller->ucType == + ATOM_PP_THERMALCONTROLLER_EXTERNAL_GPIO) || + (controller->ucType == + ATOM_PP_THERMALCONTROLLER_ADT7473_WITH_INTERNAL) || + (controller->ucType == + ATOM_PP_THERMALCONTROLLER_EMC2103_WITH_INTERNAL)) { + DRM_INFO("Special thermal controller config\n"); + } else if (controller->ucType < DRM_ARRAY_SIZE(pp_lib_thermal_controller_names)) { + DRM_INFO("Possible %s thermal controller at 0x%02x %s fan control\n", + pp_lib_thermal_controller_names[controller->ucType], + controller->ucI2cAddress >> 1, + (controller->ucFanParameters & + ATOM_PP_FANPARAMETERS_NOFAN) ? "without" : "with"); + i2c_bus = radeon_lookup_i2c_gpio(rdev, controller->ucI2cLine); + rdev->pm.i2c_bus = radeon_i2c_lookup(rdev, &i2c_bus); +#ifdef DUMBBELL_WIP + if (rdev->pm.i2c_bus) { + struct i2c_board_info info = { }; + const char *name = pp_lib_thermal_controller_names[controller->ucType]; + info.addr = controller->ucI2cAddress >> 1; + strlcpy(info.type, name, sizeof(info.type)); + i2c_new_device(&rdev->pm.i2c_bus->adapter, &info); + } +#endif /* DUMBBELL_WIP */ + } else { + DRM_INFO("Unknown thermal controller type %d at 0x%02x %s fan control\n", + controller->ucType, + controller->ucI2cAddress >> 1, + (controller->ucFanParameters & + ATOM_PP_FANPARAMETERS_NOFAN) ? "without" : "with"); + } + } +} + +static void radeon_atombios_get_default_voltages(struct radeon_device *rdev, + u16 *vddc, u16 *vddci) +{ + struct radeon_mode_info *mode_info = &rdev->mode_info; + int index = GetIndexIntoMasterTable(DATA, FirmwareInfo); + u8 frev, crev; + u16 data_offset; + union firmware_info *firmware_info; + + *vddc = 0; + *vddci = 0; + + if (atom_parse_data_header(mode_info->atom_context, index, NULL, + &frev, &crev, &data_offset)) { + firmware_info = + (union firmware_info *)((char *)mode_info->atom_context->bios + + data_offset); + *vddc = le16_to_cpu(firmware_info->info_14.usBootUpVDDCVoltage); + if ((frev == 2) && (crev >= 2)) + *vddci = le16_to_cpu(firmware_info->info_22.usBootUpVDDCIVoltage); + } +} + +static void radeon_atombios_parse_pplib_non_clock_info(struct radeon_device *rdev, + int state_index, int mode_index, + struct _ATOM_PPLIB_NONCLOCK_INFO *non_clock_info) +{ + int j; + u32 misc = le32_to_cpu(non_clock_info->ulCapsAndSettings); + u32 misc2 = le16_to_cpu(non_clock_info->usClassification); + u16 vddc, vddci; + + radeon_atombios_get_default_voltages(rdev, &vddc, &vddci); + + rdev->pm.power_state[state_index].misc = misc; + rdev->pm.power_state[state_index].misc2 = misc2; + rdev->pm.power_state[state_index].pcie_lanes = + ((misc & ATOM_PPLIB_PCIE_LINK_WIDTH_MASK) >> + ATOM_PPLIB_PCIE_LINK_WIDTH_SHIFT) + 1; + switch (misc2 & ATOM_PPLIB_CLASSIFICATION_UI_MASK) { + case ATOM_PPLIB_CLASSIFICATION_UI_BATTERY: + rdev->pm.power_state[state_index].type = + POWER_STATE_TYPE_BATTERY; + break; + case ATOM_PPLIB_CLASSIFICATION_UI_BALANCED: + rdev->pm.power_state[state_index].type = + POWER_STATE_TYPE_BALANCED; + break; + case ATOM_PPLIB_CLASSIFICATION_UI_PERFORMANCE: + rdev->pm.power_state[state_index].type = + POWER_STATE_TYPE_PERFORMANCE; + break; + case ATOM_PPLIB_CLASSIFICATION_UI_NONE: + if (misc2 & ATOM_PPLIB_CLASSIFICATION_3DPERFORMANCE) + rdev->pm.power_state[state_index].type = + POWER_STATE_TYPE_PERFORMANCE; + break; + } + rdev->pm.power_state[state_index].flags = 0; + if (misc & ATOM_PPLIB_SINGLE_DISPLAY_ONLY) + rdev->pm.power_state[state_index].flags |= + RADEON_PM_STATE_SINGLE_DISPLAY_ONLY; + if (misc2 & ATOM_PPLIB_CLASSIFICATION_BOOT) { + rdev->pm.power_state[state_index].type = + POWER_STATE_TYPE_DEFAULT; + rdev->pm.default_power_state_index = state_index; + rdev->pm.power_state[state_index].default_clock_mode = + &rdev->pm.power_state[state_index].clock_info[mode_index - 1]; + if (ASIC_IS_DCE5(rdev) && !(rdev->flags & RADEON_IS_IGP)) { + /* NI chips post without MC ucode, so default clocks are strobe mode only */ + rdev->pm.default_sclk = rdev->pm.power_state[state_index].clock_info[0].sclk; + rdev->pm.default_mclk = rdev->pm.power_state[state_index].clock_info[0].mclk; + rdev->pm.default_vddc = rdev->pm.power_state[state_index].clock_info[0].voltage.voltage; + rdev->pm.default_vddci = rdev->pm.power_state[state_index].clock_info[0].voltage.vddci; + } else { + /* patch the table values with the default slck/mclk from firmware info */ + for (j = 0; j < mode_index; j++) { + rdev->pm.power_state[state_index].clock_info[j].mclk = + rdev->clock.default_mclk; + rdev->pm.power_state[state_index].clock_info[j].sclk = + rdev->clock.default_sclk; + if (vddc) + rdev->pm.power_state[state_index].clock_info[j].voltage.voltage = + vddc; + } + } + } +} + +static bool radeon_atombios_parse_pplib_clock_info(struct radeon_device *rdev, + int state_index, int mode_index, + union pplib_clock_info *clock_info) +{ + u32 sclk, mclk; + u16 vddc; + + if (rdev->flags & RADEON_IS_IGP) { + if (rdev->family >= CHIP_PALM) { + sclk = le16_to_cpu(clock_info->sumo.usEngineClockLow); + sclk |= clock_info->sumo.ucEngineClockHigh << 16; + rdev->pm.power_state[state_index].clock_info[mode_index].sclk = sclk; + } else { + sclk = le16_to_cpu(clock_info->rs780.usLowEngineClockLow); + sclk |= clock_info->rs780.ucLowEngineClockHigh << 16; + rdev->pm.power_state[state_index].clock_info[mode_index].sclk = sclk; + } + } else if (ASIC_IS_DCE6(rdev)) { + sclk = le16_to_cpu(clock_info->si.usEngineClockLow); + sclk |= clock_info->si.ucEngineClockHigh << 16; + mclk = le16_to_cpu(clock_info->si.usMemoryClockLow); + mclk |= clock_info->si.ucMemoryClockHigh << 16; + rdev->pm.power_state[state_index].clock_info[mode_index].mclk = mclk; + rdev->pm.power_state[state_index].clock_info[mode_index].sclk = sclk; + rdev->pm.power_state[state_index].clock_info[mode_index].voltage.type = + VOLTAGE_SW; + rdev->pm.power_state[state_index].clock_info[mode_index].voltage.voltage = + le16_to_cpu(clock_info->si.usVDDC); + rdev->pm.power_state[state_index].clock_info[mode_index].voltage.vddci = + le16_to_cpu(clock_info->si.usVDDCI); + } else if (ASIC_IS_DCE4(rdev)) { + sclk = le16_to_cpu(clock_info->evergreen.usEngineClockLow); + sclk |= clock_info->evergreen.ucEngineClockHigh << 16; + mclk = le16_to_cpu(clock_info->evergreen.usMemoryClockLow); + mclk |= clock_info->evergreen.ucMemoryClockHigh << 16; + rdev->pm.power_state[state_index].clock_info[mode_index].mclk = mclk; + rdev->pm.power_state[state_index].clock_info[mode_index].sclk = sclk; + rdev->pm.power_state[state_index].clock_info[mode_index].voltage.type = + VOLTAGE_SW; + rdev->pm.power_state[state_index].clock_info[mode_index].voltage.voltage = + le16_to_cpu(clock_info->evergreen.usVDDC); + rdev->pm.power_state[state_index].clock_info[mode_index].voltage.vddci = + le16_to_cpu(clock_info->evergreen.usVDDCI); + } else { + sclk = le16_to_cpu(clock_info->r600.usEngineClockLow); + sclk |= clock_info->r600.ucEngineClockHigh << 16; + mclk = le16_to_cpu(clock_info->r600.usMemoryClockLow); + mclk |= clock_info->r600.ucMemoryClockHigh << 16; + rdev->pm.power_state[state_index].clock_info[mode_index].mclk = mclk; + rdev->pm.power_state[state_index].clock_info[mode_index].sclk = sclk; + rdev->pm.power_state[state_index].clock_info[mode_index].voltage.type = + VOLTAGE_SW; + rdev->pm.power_state[state_index].clock_info[mode_index].voltage.voltage = + le16_to_cpu(clock_info->r600.usVDDC); + } + + /* patch up vddc if necessary */ + switch (rdev->pm.power_state[state_index].clock_info[mode_index].voltage.voltage) { + case ATOM_VIRTUAL_VOLTAGE_ID0: + case ATOM_VIRTUAL_VOLTAGE_ID1: + case ATOM_VIRTUAL_VOLTAGE_ID2: + case ATOM_VIRTUAL_VOLTAGE_ID3: + if (radeon_atom_get_max_vddc(rdev, VOLTAGE_TYPE_VDDC, + rdev->pm.power_state[state_index].clock_info[mode_index].voltage.voltage, + &vddc) == 0) + rdev->pm.power_state[state_index].clock_info[mode_index].voltage.voltage = vddc; + break; + default: + break; + } + + if (rdev->flags & RADEON_IS_IGP) { + /* skip invalid modes */ + if (rdev->pm.power_state[state_index].clock_info[mode_index].sclk == 0) + return false; + } else { + /* skip invalid modes */ + if ((rdev->pm.power_state[state_index].clock_info[mode_index].mclk == 0) || + (rdev->pm.power_state[state_index].clock_info[mode_index].sclk == 0)) + return false; + } + return true; +} + +static int radeon_atombios_parse_power_table_4_5(struct radeon_device *rdev) +{ + struct radeon_mode_info *mode_info = &rdev->mode_info; + struct _ATOM_PPLIB_NONCLOCK_INFO *non_clock_info; + union pplib_power_state *power_state; + int i, j; + int state_index = 0, mode_index = 0; + union pplib_clock_info *clock_info; + bool valid; + union power_info *power_info; + int index = GetIndexIntoMasterTable(DATA, PowerPlayInfo); + u16 data_offset; + u8 frev, crev; + + if (!atom_parse_data_header(mode_info->atom_context, index, NULL, + &frev, &crev, &data_offset)) + return state_index; + power_info = (union power_info *)((char *)mode_info->atom_context->bios + data_offset); + + radeon_atombios_add_pplib_thermal_controller(rdev, &power_info->pplib.sThermalController); + rdev->pm.power_state = malloc(sizeof(struct radeon_power_state) * + power_info->pplib.ucNumStates, + DRM_MEM_DRIVER, M_WAITOK | M_ZERO); + if (!rdev->pm.power_state) + return state_index; + /* first mode is usually default, followed by low to high */ + for (i = 0; i < power_info->pplib.ucNumStates; i++) { + mode_index = 0; + power_state = (union pplib_power_state *) + ((char *)mode_info->atom_context->bios + data_offset + + le16_to_cpu(power_info->pplib.usStateArrayOffset) + + i * power_info->pplib.ucStateEntrySize); + non_clock_info = (struct _ATOM_PPLIB_NONCLOCK_INFO *) + ((char *)mode_info->atom_context->bios + data_offset + + le16_to_cpu(power_info->pplib.usNonClockInfoArrayOffset) + + (power_state->v1.ucNonClockStateIndex * + power_info->pplib.ucNonClockSize)); + rdev->pm.power_state[i].clock_info = malloc(sizeof(struct radeon_pm_clock_info) * + ((power_info->pplib.ucStateEntrySize - 1) ? + (power_info->pplib.ucStateEntrySize - 1) : 1), + DRM_MEM_DRIVER, M_WAITOK | M_ZERO); + if (!rdev->pm.power_state[i].clock_info) + return state_index; + if (power_info->pplib.ucStateEntrySize - 1) { + for (j = 0; j < (power_info->pplib.ucStateEntrySize - 1); j++) { + clock_info = (union pplib_clock_info *) + ((char *)mode_info->atom_context->bios + data_offset + + le16_to_cpu(power_info->pplib.usClockInfoArrayOffset) + + (power_state->v1.ucClockStateIndices[j] * + power_info->pplib.ucClockInfoSize)); + valid = radeon_atombios_parse_pplib_clock_info(rdev, + state_index, mode_index, + clock_info); + if (valid) + mode_index++; + } + } else { + rdev->pm.power_state[state_index].clock_info[0].mclk = + rdev->clock.default_mclk; + rdev->pm.power_state[state_index].clock_info[0].sclk = + rdev->clock.default_sclk; + mode_index++; + } + rdev->pm.power_state[state_index].num_clock_modes = mode_index; + if (mode_index) { + radeon_atombios_parse_pplib_non_clock_info(rdev, state_index, mode_index, + non_clock_info); + state_index++; + } + } + /* if multiple clock modes, mark the lowest as no display */ + for (i = 0; i < state_index; i++) { + if (rdev->pm.power_state[i].num_clock_modes > 1) + rdev->pm.power_state[i].clock_info[0].flags |= + RADEON_PM_MODE_NO_DISPLAY; + } + /* first mode is usually default */ + if (rdev->pm.default_power_state_index == -1) { + rdev->pm.power_state[0].type = + POWER_STATE_TYPE_DEFAULT; + rdev->pm.default_power_state_index = 0; + rdev->pm.power_state[0].default_clock_mode = + &rdev->pm.power_state[0].clock_info[0]; + } + return state_index; +} + +static int radeon_atombios_parse_power_table_6(struct radeon_device *rdev) +{ + struct radeon_mode_info *mode_info = &rdev->mode_info; + struct _ATOM_PPLIB_NONCLOCK_INFO *non_clock_info; + union pplib_power_state *power_state; + int i, j, non_clock_array_index, clock_array_index; + int state_index = 0, mode_index = 0; + union pplib_clock_info *clock_info; + struct _StateArray *state_array; + struct _ClockInfoArray *clock_info_array; + struct _NonClockInfoArray *non_clock_info_array; + bool valid; + union power_info *power_info; + int index = GetIndexIntoMasterTable(DATA, PowerPlayInfo); + u16 data_offset; + u8 frev, crev; + + if (!atom_parse_data_header(mode_info->atom_context, index, NULL, + &frev, &crev, &data_offset)) + return state_index; + power_info = (union power_info *)((char *)mode_info->atom_context->bios + data_offset); + + radeon_atombios_add_pplib_thermal_controller(rdev, &power_info->pplib.sThermalController); + state_array = (struct _StateArray *) + ((char *)mode_info->atom_context->bios + data_offset + + le16_to_cpu(power_info->pplib.usStateArrayOffset)); + clock_info_array = (struct _ClockInfoArray *) + ((char *)mode_info->atom_context->bios + data_offset + + le16_to_cpu(power_info->pplib.usClockInfoArrayOffset)); + non_clock_info_array = (struct _NonClockInfoArray *) + ((char *)mode_info->atom_context->bios + data_offset + + le16_to_cpu(power_info->pplib.usNonClockInfoArrayOffset)); + rdev->pm.power_state = malloc(sizeof(struct radeon_power_state) * + state_array->ucNumEntries, + DRM_MEM_DRIVER, M_WAITOK | M_ZERO); + if (!rdev->pm.power_state) + return state_index; + for (i = 0; i < state_array->ucNumEntries; i++) { + mode_index = 0; + power_state = (union pplib_power_state *)&state_array->states[i]; + /* XXX this might be an inagua bug... */ + non_clock_array_index = i; /* power_state->v2.nonClockInfoIndex */ + non_clock_info = (struct _ATOM_PPLIB_NONCLOCK_INFO *) + &non_clock_info_array->nonClockInfo[non_clock_array_index]; + rdev->pm.power_state[i].clock_info = malloc(sizeof(struct radeon_pm_clock_info) * + (power_state->v2.ucNumDPMLevels ? + power_state->v2.ucNumDPMLevels : 1), + DRM_MEM_DRIVER, M_WAITOK | M_ZERO); + if (!rdev->pm.power_state[i].clock_info) + return state_index; + if (power_state->v2.ucNumDPMLevels) { + for (j = 0; j < power_state->v2.ucNumDPMLevels; j++) { + clock_array_index = power_state->v2.clockInfoIndex[j]; + /* XXX this might be an inagua bug... */ + if (clock_array_index >= clock_info_array->ucNumEntries) + continue; + clock_info = (union pplib_clock_info *) + &clock_info_array->clockInfo[clock_array_index * clock_info_array->ucEntrySize]; + valid = radeon_atombios_parse_pplib_clock_info(rdev, + state_index, mode_index, + clock_info); + if (valid) + mode_index++; + } + } else { + rdev->pm.power_state[state_index].clock_info[0].mclk = + rdev->clock.default_mclk; + rdev->pm.power_state[state_index].clock_info[0].sclk = + rdev->clock.default_sclk; + mode_index++; + } + rdev->pm.power_state[state_index].num_clock_modes = mode_index; + if (mode_index) { + radeon_atombios_parse_pplib_non_clock_info(rdev, state_index, mode_index, + non_clock_info); + state_index++; + } + } + /* if multiple clock modes, mark the lowest as no display */ + for (i = 0; i < state_index; i++) { + if (rdev->pm.power_state[i].num_clock_modes > 1) + rdev->pm.power_state[i].clock_info[0].flags |= + RADEON_PM_MODE_NO_DISPLAY; + } + /* first mode is usually default */ + if (rdev->pm.default_power_state_index == -1) { + rdev->pm.power_state[0].type = + POWER_STATE_TYPE_DEFAULT; + rdev->pm.default_power_state_index = 0; + rdev->pm.power_state[0].default_clock_mode = + &rdev->pm.power_state[0].clock_info[0]; + } + return state_index; +} + +void radeon_atombios_get_power_modes(struct radeon_device *rdev) +{ + struct radeon_mode_info *mode_info = &rdev->mode_info; + int index = GetIndexIntoMasterTable(DATA, PowerPlayInfo); + u16 data_offset; + u8 frev, crev; + int state_index = 0; + + rdev->pm.default_power_state_index = -1; + + if (atom_parse_data_header(mode_info->atom_context, index, NULL, + &frev, &crev, &data_offset)) { + switch (frev) { + case 1: + case 2: + case 3: + state_index = radeon_atombios_parse_power_table_1_3(rdev); + break; + case 4: + case 5: + state_index = radeon_atombios_parse_power_table_4_5(rdev); + break; + case 6: + state_index = radeon_atombios_parse_power_table_6(rdev); + break; + default: + break; + } + } else { + rdev->pm.power_state = malloc(sizeof(struct radeon_power_state), + DRM_MEM_DRIVER, M_WAITOK | M_ZERO); + if (rdev->pm.power_state) { + rdev->pm.power_state[0].clock_info = + malloc(sizeof(struct radeon_pm_clock_info) * 1, + DRM_MEM_DRIVER, M_WAITOK | M_ZERO); + if (rdev->pm.power_state[0].clock_info) { + /* add the default mode */ + rdev->pm.power_state[state_index].type = + POWER_STATE_TYPE_DEFAULT; + rdev->pm.power_state[state_index].num_clock_modes = 1; + rdev->pm.power_state[state_index].clock_info[0].mclk = rdev->clock.default_mclk; + rdev->pm.power_state[state_index].clock_info[0].sclk = rdev->clock.default_sclk; + rdev->pm.power_state[state_index].default_clock_mode = + &rdev->pm.power_state[state_index].clock_info[0]; + rdev->pm.power_state[state_index].clock_info[0].voltage.type = VOLTAGE_NONE; + rdev->pm.power_state[state_index].pcie_lanes = 16; + rdev->pm.default_power_state_index = state_index; + rdev->pm.power_state[state_index].flags = 0; + state_index++; + } + } + } + + rdev->pm.num_power_states = state_index; + + rdev->pm.current_power_state_index = rdev->pm.default_power_state_index; + rdev->pm.current_clock_mode_index = 0; + if (rdev->pm.default_power_state_index >= 0) + rdev->pm.current_vddc = + rdev->pm.power_state[rdev->pm.default_power_state_index].clock_info[0].voltage.voltage; + else + rdev->pm.current_vddc = 0; +} + +void radeon_atom_set_clock_gating(struct radeon_device *rdev, int enable) +{ + DYNAMIC_CLOCK_GATING_PS_ALLOCATION args; + int index = GetIndexIntoMasterTable(COMMAND, DynamicClockGating); + + args.ucEnable = enable; + + atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args); +} + +uint32_t radeon_atom_get_engine_clock(struct radeon_device *rdev) +{ + GET_ENGINE_CLOCK_PS_ALLOCATION args; + int index = GetIndexIntoMasterTable(COMMAND, GetEngineClock); + + atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args); + return le32_to_cpu(args.ulReturnEngineClock); +} + +uint32_t radeon_atom_get_memory_clock(struct radeon_device *rdev) +{ + GET_MEMORY_CLOCK_PS_ALLOCATION args; + int index = GetIndexIntoMasterTable(COMMAND, GetMemoryClock); + + atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args); + return le32_to_cpu(args.ulReturnMemoryClock); +} + +void radeon_atom_set_engine_clock(struct radeon_device *rdev, + uint32_t eng_clock) +{ + SET_ENGINE_CLOCK_PS_ALLOCATION args; + int index = GetIndexIntoMasterTable(COMMAND, SetEngineClock); + + args.ulTargetEngineClock = cpu_to_le32(eng_clock); /* 10 khz */ + + atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args); +} + +void radeon_atom_set_memory_clock(struct radeon_device *rdev, + uint32_t mem_clock) +{ + SET_MEMORY_CLOCK_PS_ALLOCATION args; + int index = GetIndexIntoMasterTable(COMMAND, SetMemoryClock); + + if (rdev->flags & RADEON_IS_IGP) + return; + + args.ulTargetMemoryClock = cpu_to_le32(mem_clock); /* 10 khz */ + + atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args); +} + +union set_voltage { + struct _SET_VOLTAGE_PS_ALLOCATION alloc; + struct _SET_VOLTAGE_PARAMETERS v1; + struct _SET_VOLTAGE_PARAMETERS_V2 v2; + struct _SET_VOLTAGE_PARAMETERS_V1_3 v3; +}; + +void radeon_atom_set_voltage(struct radeon_device *rdev, u16 voltage_level, u8 voltage_type) +{ + union set_voltage args; + int index = GetIndexIntoMasterTable(COMMAND, SetVoltage); + u8 frev, crev, volt_index = voltage_level; + + if (!atom_parse_cmd_header(rdev->mode_info.atom_context, index, &frev, &crev)) + return; + + /* 0xff01 is a flag rather then an actual voltage */ + if (voltage_level == 0xff01) + return; + + switch (crev) { + case 1: + args.v1.ucVoltageType = voltage_type; + args.v1.ucVoltageMode = SET_ASIC_VOLTAGE_MODE_ALL_SOURCE; + args.v1.ucVoltageIndex = volt_index; + break; + case 2: + args.v2.ucVoltageType = voltage_type; + args.v2.ucVoltageMode = SET_ASIC_VOLTAGE_MODE_SET_VOLTAGE; + args.v2.usVoltageLevel = cpu_to_le16(voltage_level); + break; + case 3: + args.v3.ucVoltageType = voltage_type; + args.v3.ucVoltageMode = ATOM_SET_VOLTAGE; + args.v3.usVoltageLevel = cpu_to_le16(voltage_level); + break; + default: + DRM_ERROR("Unknown table version %d, %d\n", frev, crev); + return; + } + + atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args); +} + +static int radeon_atom_get_max_vddc(struct radeon_device *rdev, u8 voltage_type, + u16 voltage_id, u16 *voltage) +{ + union set_voltage args; + int index = GetIndexIntoMasterTable(COMMAND, SetVoltage); + u8 frev, crev; + + if (!atom_parse_cmd_header(rdev->mode_info.atom_context, index, &frev, &crev)) + return -EINVAL; + + switch (crev) { + case 1: + return -EINVAL; + case 2: + args.v2.ucVoltageType = SET_VOLTAGE_GET_MAX_VOLTAGE; + args.v2.ucVoltageMode = 0; + args.v2.usVoltageLevel = 0; + + atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args); + + *voltage = le16_to_cpu(args.v2.usVoltageLevel); + break; + case 3: + args.v3.ucVoltageType = voltage_type; + args.v3.ucVoltageMode = ATOM_GET_VOLTAGE_LEVEL; + args.v3.usVoltageLevel = cpu_to_le16(voltage_id); + + atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args); + + *voltage = le16_to_cpu(args.v3.usVoltageLevel); + break; + default: + DRM_ERROR("Unknown table version %d, %d\n", frev, crev); + return -EINVAL; + } + + return 0; +} + +void radeon_atom_initialize_bios_scratch_regs(struct drm_device *dev) +{ + struct radeon_device *rdev = dev->dev_private; + uint32_t bios_2_scratch, bios_6_scratch; + + if (rdev->family >= CHIP_R600) { + bios_2_scratch = RREG32(R600_BIOS_2_SCRATCH); + bios_6_scratch = RREG32(R600_BIOS_6_SCRATCH); + } else { + bios_2_scratch = RREG32(RADEON_BIOS_2_SCRATCH); + bios_6_scratch = RREG32(RADEON_BIOS_6_SCRATCH); + } + + /* let the bios control the backlight */ + bios_2_scratch &= ~ATOM_S2_VRI_BRIGHT_ENABLE; + + /* tell the bios not to handle mode switching */ + bios_6_scratch |= ATOM_S6_ACC_BLOCK_DISPLAY_SWITCH; + + if (rdev->family >= CHIP_R600) { + WREG32(R600_BIOS_2_SCRATCH, bios_2_scratch); + WREG32(R600_BIOS_6_SCRATCH, bios_6_scratch); + } else { + WREG32(RADEON_BIOS_2_SCRATCH, bios_2_scratch); + WREG32(RADEON_BIOS_6_SCRATCH, bios_6_scratch); + } + +} + +void radeon_save_bios_scratch_regs(struct radeon_device *rdev) +{ + uint32_t scratch_reg; + int i; + + if (rdev->family >= CHIP_R600) + scratch_reg = R600_BIOS_0_SCRATCH; + else + scratch_reg = RADEON_BIOS_0_SCRATCH; + + for (i = 0; i < RADEON_BIOS_NUM_SCRATCH; i++) + rdev->bios_scratch[i] = RREG32(scratch_reg + (i * 4)); +} + +void radeon_restore_bios_scratch_regs(struct radeon_device *rdev) +{ + uint32_t scratch_reg; + int i; + + if (rdev->family >= CHIP_R600) + scratch_reg = R600_BIOS_0_SCRATCH; + else + scratch_reg = RADEON_BIOS_0_SCRATCH; + + for (i = 0; i < RADEON_BIOS_NUM_SCRATCH; i++) + WREG32(scratch_reg + (i * 4), rdev->bios_scratch[i]); +} + +void radeon_atom_output_lock(struct drm_encoder *encoder, bool lock) +{ + struct drm_device *dev = encoder->dev; + struct radeon_device *rdev = dev->dev_private; + uint32_t bios_6_scratch; + + if (rdev->family >= CHIP_R600) + bios_6_scratch = RREG32(R600_BIOS_6_SCRATCH); + else + bios_6_scratch = RREG32(RADEON_BIOS_6_SCRATCH); + + if (lock) { + bios_6_scratch |= ATOM_S6_CRITICAL_STATE; + bios_6_scratch &= ~ATOM_S6_ACC_MODE; + } else { + bios_6_scratch &= ~ATOM_S6_CRITICAL_STATE; + bios_6_scratch |= ATOM_S6_ACC_MODE; + } + + if (rdev->family >= CHIP_R600) + WREG32(R600_BIOS_6_SCRATCH, bios_6_scratch); + else + WREG32(RADEON_BIOS_6_SCRATCH, bios_6_scratch); +} + +/* at some point we may want to break this out into individual functions */ +void +radeon_atombios_connected_scratch_regs(struct drm_connector *connector, + struct drm_encoder *encoder, + bool connected) +{ + struct drm_device *dev = connector->dev; + struct radeon_device *rdev = dev->dev_private; + struct radeon_connector *radeon_connector = + to_radeon_connector(connector); + struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); + uint32_t bios_0_scratch, bios_3_scratch, bios_6_scratch; + + if (rdev->family >= CHIP_R600) { + bios_0_scratch = RREG32(R600_BIOS_0_SCRATCH); + bios_3_scratch = RREG32(R600_BIOS_3_SCRATCH); + bios_6_scratch = RREG32(R600_BIOS_6_SCRATCH); + } else { + bios_0_scratch = RREG32(RADEON_BIOS_0_SCRATCH); + bios_3_scratch = RREG32(RADEON_BIOS_3_SCRATCH); + bios_6_scratch = RREG32(RADEON_BIOS_6_SCRATCH); + } + + if ((radeon_encoder->devices & ATOM_DEVICE_TV1_SUPPORT) && + (radeon_connector->devices & ATOM_DEVICE_TV1_SUPPORT)) { + if (connected) { + DRM_DEBUG_KMS("TV1 connected\n"); + bios_3_scratch |= ATOM_S3_TV1_ACTIVE; + bios_6_scratch |= ATOM_S6_ACC_REQ_TV1; + } else { + DRM_DEBUG_KMS("TV1 disconnected\n"); + bios_0_scratch &= ~ATOM_S0_TV1_MASK; + bios_3_scratch &= ~ATOM_S3_TV1_ACTIVE; + bios_6_scratch &= ~ATOM_S6_ACC_REQ_TV1; + } + } + if ((radeon_encoder->devices & ATOM_DEVICE_CV_SUPPORT) && + (radeon_connector->devices & ATOM_DEVICE_CV_SUPPORT)) { + if (connected) { + DRM_DEBUG_KMS("CV connected\n"); + bios_3_scratch |= ATOM_S3_CV_ACTIVE; + bios_6_scratch |= ATOM_S6_ACC_REQ_CV; + } else { + DRM_DEBUG_KMS("CV disconnected\n"); + bios_0_scratch &= ~ATOM_S0_CV_MASK; + bios_3_scratch &= ~ATOM_S3_CV_ACTIVE; + bios_6_scratch &= ~ATOM_S6_ACC_REQ_CV; + } + } + if ((radeon_encoder->devices & ATOM_DEVICE_LCD1_SUPPORT) && + (radeon_connector->devices & ATOM_DEVICE_LCD1_SUPPORT)) { + if (connected) { + DRM_DEBUG_KMS("LCD1 connected\n"); + bios_0_scratch |= ATOM_S0_LCD1; + bios_3_scratch |= ATOM_S3_LCD1_ACTIVE; + bios_6_scratch |= ATOM_S6_ACC_REQ_LCD1; + } else { + DRM_DEBUG_KMS("LCD1 disconnected\n"); + bios_0_scratch &= ~ATOM_S0_LCD1; + bios_3_scratch &= ~ATOM_S3_LCD1_ACTIVE; + bios_6_scratch &= ~ATOM_S6_ACC_REQ_LCD1; + } + } + if ((radeon_encoder->devices & ATOM_DEVICE_CRT1_SUPPORT) && + (radeon_connector->devices & ATOM_DEVICE_CRT1_SUPPORT)) { + if (connected) { + DRM_DEBUG_KMS("CRT1 connected\n"); + bios_0_scratch |= ATOM_S0_CRT1_COLOR; + bios_3_scratch |= ATOM_S3_CRT1_ACTIVE; + bios_6_scratch |= ATOM_S6_ACC_REQ_CRT1; + } else { + DRM_DEBUG_KMS("CRT1 disconnected\n"); + bios_0_scratch &= ~ATOM_S0_CRT1_MASK; + bios_3_scratch &= ~ATOM_S3_CRT1_ACTIVE; + bios_6_scratch &= ~ATOM_S6_ACC_REQ_CRT1; + } + } + if ((radeon_encoder->devices & ATOM_DEVICE_CRT2_SUPPORT) && + (radeon_connector->devices & ATOM_DEVICE_CRT2_SUPPORT)) { + if (connected) { + DRM_DEBUG_KMS("CRT2 connected\n"); + bios_0_scratch |= ATOM_S0_CRT2_COLOR; + bios_3_scratch |= ATOM_S3_CRT2_ACTIVE; + bios_6_scratch |= ATOM_S6_ACC_REQ_CRT2; + } else { + DRM_DEBUG_KMS("CRT2 disconnected\n"); + bios_0_scratch &= ~ATOM_S0_CRT2_MASK; + bios_3_scratch &= ~ATOM_S3_CRT2_ACTIVE; + bios_6_scratch &= ~ATOM_S6_ACC_REQ_CRT2; + } + } + if ((radeon_encoder->devices & ATOM_DEVICE_DFP1_SUPPORT) && + (radeon_connector->devices & ATOM_DEVICE_DFP1_SUPPORT)) { + if (connected) { + DRM_DEBUG_KMS("DFP1 connected\n"); + bios_0_scratch |= ATOM_S0_DFP1; + bios_3_scratch |= ATOM_S3_DFP1_ACTIVE; + bios_6_scratch |= ATOM_S6_ACC_REQ_DFP1; + } else { + DRM_DEBUG_KMS("DFP1 disconnected\n"); + bios_0_scratch &= ~ATOM_S0_DFP1; + bios_3_scratch &= ~ATOM_S3_DFP1_ACTIVE; + bios_6_scratch &= ~ATOM_S6_ACC_REQ_DFP1; + } + } + if ((radeon_encoder->devices & ATOM_DEVICE_DFP2_SUPPORT) && + (radeon_connector->devices & ATOM_DEVICE_DFP2_SUPPORT)) { + if (connected) { + DRM_DEBUG_KMS("DFP2 connected\n"); + bios_0_scratch |= ATOM_S0_DFP2; + bios_3_scratch |= ATOM_S3_DFP2_ACTIVE; + bios_6_scratch |= ATOM_S6_ACC_REQ_DFP2; + } else { + DRM_DEBUG_KMS("DFP2 disconnected\n"); + bios_0_scratch &= ~ATOM_S0_DFP2; + bios_3_scratch &= ~ATOM_S3_DFP2_ACTIVE; + bios_6_scratch &= ~ATOM_S6_ACC_REQ_DFP2; + } + } + if ((radeon_encoder->devices & ATOM_DEVICE_DFP3_SUPPORT) && + (radeon_connector->devices & ATOM_DEVICE_DFP3_SUPPORT)) { + if (connected) { + DRM_DEBUG_KMS("DFP3 connected\n"); + bios_0_scratch |= ATOM_S0_DFP3; + bios_3_scratch |= ATOM_S3_DFP3_ACTIVE; + bios_6_scratch |= ATOM_S6_ACC_REQ_DFP3; + } else { + DRM_DEBUG_KMS("DFP3 disconnected\n"); + bios_0_scratch &= ~ATOM_S0_DFP3; + bios_3_scratch &= ~ATOM_S3_DFP3_ACTIVE; + bios_6_scratch &= ~ATOM_S6_ACC_REQ_DFP3; + } + } + if ((radeon_encoder->devices & ATOM_DEVICE_DFP4_SUPPORT) && + (radeon_connector->devices & ATOM_DEVICE_DFP4_SUPPORT)) { + if (connected) { + DRM_DEBUG_KMS("DFP4 connected\n"); + bios_0_scratch |= ATOM_S0_DFP4; + bios_3_scratch |= ATOM_S3_DFP4_ACTIVE; + bios_6_scratch |= ATOM_S6_ACC_REQ_DFP4; + } else { + DRM_DEBUG_KMS("DFP4 disconnected\n"); + bios_0_scratch &= ~ATOM_S0_DFP4; + bios_3_scratch &= ~ATOM_S3_DFP4_ACTIVE; + bios_6_scratch &= ~ATOM_S6_ACC_REQ_DFP4; + } + } + if ((radeon_encoder->devices & ATOM_DEVICE_DFP5_SUPPORT) && + (radeon_connector->devices & ATOM_DEVICE_DFP5_SUPPORT)) { + if (connected) { + DRM_DEBUG_KMS("DFP5 connected\n"); + bios_0_scratch |= ATOM_S0_DFP5; + bios_3_scratch |= ATOM_S3_DFP5_ACTIVE; + bios_6_scratch |= ATOM_S6_ACC_REQ_DFP5; + } else { + DRM_DEBUG_KMS("DFP5 disconnected\n"); + bios_0_scratch &= ~ATOM_S0_DFP5; + bios_3_scratch &= ~ATOM_S3_DFP5_ACTIVE; + bios_6_scratch &= ~ATOM_S6_ACC_REQ_DFP5; + } + } + if ((radeon_encoder->devices & ATOM_DEVICE_DFP6_SUPPORT) && + (radeon_connector->devices & ATOM_DEVICE_DFP6_SUPPORT)) { + if (connected) { + DRM_DEBUG_KMS("DFP6 connected\n"); + bios_0_scratch |= ATOM_S0_DFP6; + bios_3_scratch |= ATOM_S3_DFP6_ACTIVE; + bios_6_scratch |= ATOM_S6_ACC_REQ_DFP6; + } else { + DRM_DEBUG_KMS("DFP6 disconnected\n"); + bios_0_scratch &= ~ATOM_S0_DFP6; + bios_3_scratch &= ~ATOM_S3_DFP6_ACTIVE; + bios_6_scratch &= ~ATOM_S6_ACC_REQ_DFP6; + } + } + + if (rdev->family >= CHIP_R600) { + WREG32(R600_BIOS_0_SCRATCH, bios_0_scratch); + WREG32(R600_BIOS_3_SCRATCH, bios_3_scratch); + WREG32(R600_BIOS_6_SCRATCH, bios_6_scratch); + } else { + WREG32(RADEON_BIOS_0_SCRATCH, bios_0_scratch); + WREG32(RADEON_BIOS_3_SCRATCH, bios_3_scratch); + WREG32(RADEON_BIOS_6_SCRATCH, bios_6_scratch); + } +} + +void +radeon_atombios_encoder_crtc_scratch_regs(struct drm_encoder *encoder, int crtc) +{ + struct drm_device *dev = encoder->dev; + struct radeon_device *rdev = dev->dev_private; + struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); + uint32_t bios_3_scratch; + + if (ASIC_IS_DCE4(rdev)) + return; + + if (rdev->family >= CHIP_R600) + bios_3_scratch = RREG32(R600_BIOS_3_SCRATCH); + else + bios_3_scratch = RREG32(RADEON_BIOS_3_SCRATCH); + + if (radeon_encoder->devices & ATOM_DEVICE_TV1_SUPPORT) { + bios_3_scratch &= ~ATOM_S3_TV1_CRTC_ACTIVE; + bios_3_scratch |= (crtc << 18); + } + if (radeon_encoder->devices & ATOM_DEVICE_CV_SUPPORT) { + bios_3_scratch &= ~ATOM_S3_CV_CRTC_ACTIVE; + bios_3_scratch |= (crtc << 24); + } + if (radeon_encoder->devices & ATOM_DEVICE_CRT1_SUPPORT) { + bios_3_scratch &= ~ATOM_S3_CRT1_CRTC_ACTIVE; + bios_3_scratch |= (crtc << 16); + } + if (radeon_encoder->devices & ATOM_DEVICE_CRT2_SUPPORT) { + bios_3_scratch &= ~ATOM_S3_CRT2_CRTC_ACTIVE; + bios_3_scratch |= (crtc << 20); + } + if (radeon_encoder->devices & ATOM_DEVICE_LCD1_SUPPORT) { + bios_3_scratch &= ~ATOM_S3_LCD1_CRTC_ACTIVE; + bios_3_scratch |= (crtc << 17); + } + if (radeon_encoder->devices & ATOM_DEVICE_DFP1_SUPPORT) { + bios_3_scratch &= ~ATOM_S3_DFP1_CRTC_ACTIVE; + bios_3_scratch |= (crtc << 19); + } + if (radeon_encoder->devices & ATOM_DEVICE_DFP2_SUPPORT) { + bios_3_scratch &= ~ATOM_S3_DFP2_CRTC_ACTIVE; + bios_3_scratch |= (crtc << 23); + } + if (radeon_encoder->devices & ATOM_DEVICE_DFP3_SUPPORT) { + bios_3_scratch &= ~ATOM_S3_DFP3_CRTC_ACTIVE; + bios_3_scratch |= (crtc << 25); + } + + if (rdev->family >= CHIP_R600) + WREG32(R600_BIOS_3_SCRATCH, bios_3_scratch); + else + WREG32(RADEON_BIOS_3_SCRATCH, bios_3_scratch); +} + +void +radeon_atombios_encoder_dpms_scratch_regs(struct drm_encoder *encoder, bool on) +{ + struct drm_device *dev = encoder->dev; + struct radeon_device *rdev = dev->dev_private; + struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); + uint32_t bios_2_scratch; + + if (ASIC_IS_DCE4(rdev)) + return; + + if (rdev->family >= CHIP_R600) + bios_2_scratch = RREG32(R600_BIOS_2_SCRATCH); + else + bios_2_scratch = RREG32(RADEON_BIOS_2_SCRATCH); + + if (radeon_encoder->devices & ATOM_DEVICE_TV1_SUPPORT) { + if (on) + bios_2_scratch &= ~ATOM_S2_TV1_DPMS_STATE; + else + bios_2_scratch |= ATOM_S2_TV1_DPMS_STATE; + } + if (radeon_encoder->devices & ATOM_DEVICE_CV_SUPPORT) { + if (on) + bios_2_scratch &= ~ATOM_S2_CV_DPMS_STATE; + else + bios_2_scratch |= ATOM_S2_CV_DPMS_STATE; + } + if (radeon_encoder->devices & ATOM_DEVICE_CRT1_SUPPORT) { + if (on) + bios_2_scratch &= ~ATOM_S2_CRT1_DPMS_STATE; + else + bios_2_scratch |= ATOM_S2_CRT1_DPMS_STATE; + } + if (radeon_encoder->devices & ATOM_DEVICE_CRT2_SUPPORT) { + if (on) + bios_2_scratch &= ~ATOM_S2_CRT2_DPMS_STATE; + else + bios_2_scratch |= ATOM_S2_CRT2_DPMS_STATE; + } + if (radeon_encoder->devices & ATOM_DEVICE_LCD1_SUPPORT) { + if (on) + bios_2_scratch &= ~ATOM_S2_LCD1_DPMS_STATE; + else + bios_2_scratch |= ATOM_S2_LCD1_DPMS_STATE; + } + if (radeon_encoder->devices & ATOM_DEVICE_DFP1_SUPPORT) { + if (on) + bios_2_scratch &= ~ATOM_S2_DFP1_DPMS_STATE; + else + bios_2_scratch |= ATOM_S2_DFP1_DPMS_STATE; + } + if (radeon_encoder->devices & ATOM_DEVICE_DFP2_SUPPORT) { + if (on) + bios_2_scratch &= ~ATOM_S2_DFP2_DPMS_STATE; + else + bios_2_scratch |= ATOM_S2_DFP2_DPMS_STATE; + } + if (radeon_encoder->devices & ATOM_DEVICE_DFP3_SUPPORT) { + if (on) + bios_2_scratch &= ~ATOM_S2_DFP3_DPMS_STATE; + else + bios_2_scratch |= ATOM_S2_DFP3_DPMS_STATE; + } + if (radeon_encoder->devices & ATOM_DEVICE_DFP4_SUPPORT) { + if (on) + bios_2_scratch &= ~ATOM_S2_DFP4_DPMS_STATE; + else + bios_2_scratch |= ATOM_S2_DFP4_DPMS_STATE; + } + if (radeon_encoder->devices & ATOM_DEVICE_DFP5_SUPPORT) { + if (on) + bios_2_scratch &= ~ATOM_S2_DFP5_DPMS_STATE; + else + bios_2_scratch |= ATOM_S2_DFP5_DPMS_STATE; + } + + if (rdev->family >= CHIP_R600) + WREG32(R600_BIOS_2_SCRATCH, bios_2_scratch); + else + WREG32(RADEON_BIOS_2_SCRATCH, bios_2_scratch); +} diff --git a/sys/dev/drm2/radeon/radeon_atpx_handler.c b/sys/dev/drm2/radeon/radeon_atpx_handler.c new file mode 100644 index 00000000000..4cfd7434cde --- /dev/null +++ b/sys/dev/drm2/radeon/radeon_atpx_handler.c @@ -0,0 +1,506 @@ +/* + * Copyright (c) 2010 Red Hat Inc. + * Author : Dave Airlie + * + * Licensed under GPLv2 + * + * ATPX support for both Intel/ATI + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include + +#include +#include + +#include +#include +#include "radeon_acpi.h" +#include "radeon_drv.h" + +#ifdef DUMBBELL_WIP +struct radeon_atpx_functions { + bool px_params; + bool power_cntl; + bool disp_mux_cntl; + bool i2c_mux_cntl; + bool switch_start; + bool switch_end; + bool disp_connectors_mapping; + bool disp_detetion_ports; +}; + +struct radeon_atpx { + ACPI_HANDLE handle; + struct radeon_atpx_functions functions; +}; + +static struct radeon_atpx_priv { + bool atpx_detected; + /* handle for device - and atpx */ + ACPI_HANDLE dhandle; + struct radeon_atpx atpx; +} radeon_atpx_priv; + +struct atpx_verify_interface { + u16 size; /* structure size in bytes (includes size field) */ + u16 version; /* version */ + u32 function_bits; /* supported functions bit vector */ +} __packed; + +struct atpx_power_control { + u16 size; + u8 dgpu_state; +} __packed; + +struct atpx_mux { + u16 size; + u16 mux; +} __packed; + +/** + * radeon_atpx_call - call an ATPX method + * + * @handle: acpi handle + * @function: the ATPX function to execute + * @params: ATPX function params + * + * Executes the requested ATPX function (all asics). + * Returns a pointer to the acpi output buffer. + */ +static ACPI_OBJECT *radeon_atpx_call(ACPI_HANDLE handle, int function, + ACPI_BUFFER *params) +{ + ACPI_STATUS status; + ACPI_OBJECT atpx_arg_elements[2]; + ACPI_OBJECT_LIST atpx_arg; + ACPI_BUFFER buffer = { ACPI_ALLOCATE_BUFFER, NULL }; + + atpx_arg.Count = 2; + atpx_arg.Pointer = &atpx_arg_elements[0]; + + atpx_arg_elements[0].Type = ACPI_TYPE_INTEGER; + atpx_arg_elements[0].Integer.Value = function; + + if (params) { + atpx_arg_elements[1].Type = ACPI_TYPE_BUFFER; + atpx_arg_elements[1].Buffer.Length = params->Length; + atpx_arg_elements[1].Buffer.Pointer = params->Pointer; + } else { + /* We need a second fake parameter */ + atpx_arg_elements[1].Type = ACPI_TYPE_INTEGER; + atpx_arg_elements[1].Integer.Value = 0; + } + + status = AcpiEvaluateObject(handle, NULL, &atpx_arg, &buffer); + + /* Fail only if calling the method fails and ATPX is supported */ + if (ACPI_FAILURE(status) && status != AE_NOT_FOUND) { + DRM_ERROR("failed to evaluate ATPX got %s\n", + AcpiFormatException(status)); + AcpiOsFree(buffer.Pointer); + return NULL; + } + + return buffer.Pointer; +} + +/** + * radeon_atpx_parse_functions - parse supported functions + * + * @f: supported functions struct + * @mask: supported functions mask from ATPX + * + * Use the supported functions mask from ATPX function + * ATPX_FUNCTION_VERIFY_INTERFACE to determine what functions + * are supported (all asics). + */ +static void radeon_atpx_parse_functions(struct radeon_atpx_functions *f, u32 mask) +{ + f->px_params = mask & ATPX_GET_PX_PARAMETERS_SUPPORTED; + f->power_cntl = mask & ATPX_POWER_CONTROL_SUPPORTED; + f->disp_mux_cntl = mask & ATPX_DISPLAY_MUX_CONTROL_SUPPORTED; + f->i2c_mux_cntl = mask & ATPX_I2C_MUX_CONTROL_SUPPORTED; + f->switch_start = mask & ATPX_GRAPHICS_DEVICE_SWITCH_START_NOTIFICATION_SUPPORTED; + f->switch_end = mask & ATPX_GRAPHICS_DEVICE_SWITCH_END_NOTIFICATION_SUPPORTED; + f->disp_connectors_mapping = mask & ATPX_GET_DISPLAY_CONNECTORS_MAPPING_SUPPORTED; + f->disp_detetion_ports = mask & ATPX_GET_DISPLAY_DETECTION_PORTS_SUPPORTED; +} + +/** + * radeon_atpx_verify_interface - verify ATPX + * + * @handle: acpi handle + * @atpx: radeon atpx struct + * + * Execute the ATPX_FUNCTION_VERIFY_INTERFACE ATPX function + * to initialize ATPX and determine what features are supported + * (all asics). + * returns 0 on success, error on failure. + */ +static int radeon_atpx_verify_interface(struct radeon_atpx *atpx) +{ + ACPI_OBJECT *info; + struct atpx_verify_interface output; + size_t size; + int err = 0; + + info = radeon_atpx_call(atpx->handle, ATPX_FUNCTION_VERIFY_INTERFACE, NULL); + if (!info) + return -EIO; + + memset(&output, 0, sizeof(output)); + + size = *(u16 *) info->Buffer.Pointer; + if (size < 8) { + DRM_ERROR("ATPX buffer is too small: %zu\n", size); + err = -EINVAL; + goto out; + } + size = min(sizeof(output), size); + + memcpy(&output, info->Buffer.Pointer, size); + + /* TODO: check version? */ + DRM_INFO("ATPX version %u\n", output.version); + + radeon_atpx_parse_functions(&atpx->functions, output.function_bits); + +out: + AcpiOsFree(info); + return err; +} + +/** + * radeon_atpx_set_discrete_state - power up/down discrete GPU + * + * @atpx: atpx info struct + * @state: discrete GPU state (0 = power down, 1 = power up) + * + * Execute the ATPX_FUNCTION_POWER_CONTROL ATPX function to + * power down/up the discrete GPU (all asics). + * Returns 0 on success, error on failure. + */ +static int radeon_atpx_set_discrete_state(struct radeon_atpx *atpx, u8 state) +{ + ACPI_BUFFER params; + ACPI_OBJECT *info; + struct atpx_power_control input; + + if (atpx->functions.power_cntl) { + input.size = 3; + input.dgpu_state = state; + params.Length = input.size; + params.Pointer = &input; + info = radeon_atpx_call(atpx->handle, + ATPX_FUNCTION_POWER_CONTROL, + ¶ms); + if (!info) + return -EIO; + AcpiOsFree(info); + } + return 0; +} + +/** + * radeon_atpx_switch_disp_mux - switch display mux + * + * @atpx: atpx info struct + * @mux_id: mux state (0 = integrated GPU, 1 = discrete GPU) + * + * Execute the ATPX_FUNCTION_DISPLAY_MUX_CONTROL ATPX function to + * switch the display mux between the discrete GPU and integrated GPU + * (all asics). + * Returns 0 on success, error on failure. + */ +static int radeon_atpx_switch_disp_mux(struct radeon_atpx *atpx, u16 mux_id) +{ + ACPI_BUFFER params; + ACPI_OBJECT *info; + struct atpx_mux input; + + if (atpx->functions.disp_mux_cntl) { + input.size = 4; + input.mux = mux_id; + params.Length = input.size; + params.Pointer = &input; + info = radeon_atpx_call(atpx->handle, + ATPX_FUNCTION_DISPLAY_MUX_CONTROL, + ¶ms); + if (!info) + return -EIO; + AcpiOsFree(info); + } + return 0; +} + +/** + * radeon_atpx_switch_i2c_mux - switch i2c/hpd mux + * + * @atpx: atpx info struct + * @mux_id: mux state (0 = integrated GPU, 1 = discrete GPU) + * + * Execute the ATPX_FUNCTION_I2C_MUX_CONTROL ATPX function to + * switch the i2c/hpd mux between the discrete GPU and integrated GPU + * (all asics). + * Returns 0 on success, error on failure. + */ +static int radeon_atpx_switch_i2c_mux(struct radeon_atpx *atpx, u16 mux_id) +{ + ACPI_BUFFER params; + ACPI_OBJECT *info; + struct atpx_mux input; + + if (atpx->functions.i2c_mux_cntl) { + input.size = 4; + input.mux = mux_id; + params.Length = input.size; + params.Pointer = &input; + info = radeon_atpx_call(atpx->handle, + ATPX_FUNCTION_I2C_MUX_CONTROL, + ¶ms); + if (!info) + return -EIO; + AcpiOsFree(info); + } + return 0; +} + +/** + * radeon_atpx_switch_start - notify the sbios of a GPU switch + * + * @atpx: atpx info struct + * @mux_id: mux state (0 = integrated GPU, 1 = discrete GPU) + * + * Execute the ATPX_FUNCTION_GRAPHICS_DEVICE_SWITCH_START_NOTIFICATION ATPX + * function to notify the sbios that a switch between the discrete GPU and + * integrated GPU has begun (all asics). + * Returns 0 on success, error on failure. + */ +static int radeon_atpx_switch_start(struct radeon_atpx *atpx, u16 mux_id) +{ + ACPI_BUFFER params; + ACPI_OBJECT *info; + struct atpx_mux input; + + if (atpx->functions.switch_start) { + input.size = 4; + input.mux = mux_id; + params.Length = input.size; + params.Pointer = &input; + info = radeon_atpx_call(atpx->handle, + ATPX_FUNCTION_GRAPHICS_DEVICE_SWITCH_START_NOTIFICATION, + ¶ms); + if (!info) + return -EIO; + AcpiOsFree(info); + } + return 0; +} + +/** + * radeon_atpx_switch_end - notify the sbios of a GPU switch + * + * @atpx: atpx info struct + * @mux_id: mux state (0 = integrated GPU, 1 = discrete GPU) + * + * Execute the ATPX_FUNCTION_GRAPHICS_DEVICE_SWITCH_END_NOTIFICATION ATPX + * function to notify the sbios that a switch between the discrete GPU and + * integrated GPU has ended (all asics). + * Returns 0 on success, error on failure. + */ +static int radeon_atpx_switch_end(struct radeon_atpx *atpx, u16 mux_id) +{ + ACPI_BUFFER params; + ACPI_OBJECT *info; + struct atpx_mux input; + + if (atpx->functions.switch_end) { + input.size = 4; + input.mux = mux_id; + params.Length = input.size; + params.Pointer = &input; + info = radeon_atpx_call(atpx->handle, + ATPX_FUNCTION_GRAPHICS_DEVICE_SWITCH_END_NOTIFICATION, + ¶ms); + if (!info) + return -EIO; + AcpiOsFree(info); + } + return 0; +} + +/** + * radeon_atpx_switchto - switch to the requested GPU + * + * @id: GPU to switch to + * + * Execute the necessary ATPX functions to switch between the discrete GPU and + * integrated GPU (all asics). + * Returns 0 on success, error on failure. + */ +static int radeon_atpx_switchto(enum vga_switcheroo_client_id id) +{ + u16 gpu_id; + + if (id == VGA_SWITCHEROO_IGD) + gpu_id = ATPX_INTEGRATED_GPU; + else + gpu_id = ATPX_DISCRETE_GPU; + + radeon_atpx_switch_start(&radeon_atpx_priv.atpx, gpu_id); + radeon_atpx_switch_disp_mux(&radeon_atpx_priv.atpx, gpu_id); + radeon_atpx_switch_i2c_mux(&radeon_atpx_priv.atpx, gpu_id); + radeon_atpx_switch_end(&radeon_atpx_priv.atpx, gpu_id); + + return 0; +} + +/** + * radeon_atpx_power_state - power down/up the requested GPU + * + * @id: GPU to power down/up + * @state: requested power state (0 = off, 1 = on) + * + * Execute the necessary ATPX function to power down/up the discrete GPU + * (all asics). + * Returns 0 on success, error on failure. + */ +static int radeon_atpx_power_state(enum vga_switcheroo_client_id id, + enum vga_switcheroo_state state) +{ + /* on w500 ACPI can't change intel gpu state */ + if (id == VGA_SWITCHEROO_IGD) + return 0; + + radeon_atpx_set_discrete_state(&radeon_atpx_priv.atpx, state); + return 0; +} + +/** + * radeon_atpx_pci_probe_handle - look up the ATPX handle + * + * @pdev: pci device + * + * Look up the ATPX handles (all asics). + * Returns true if the handles are found, false if not. + */ +static bool radeon_atpx_pci_probe_handle(struct pci_dev *pdev) +{ + ACPI_HANDLE dhandle, atpx_handle; + ACPI_STATUS status; + + dhandle = DEVICE_ACPI_HANDLE(&pdev->dev); + if (!dhandle) + return false; + + status = AcpiGetHandle(dhandle, "ATPX", &atpx_handle); + if (ACPI_FAILURE(status)) + return false; + + radeon_atpx_priv.dhandle = dhandle; + radeon_atpx_priv.atpx.handle = atpx_handle; + return true; +} + +/** + * radeon_atpx_init - verify the ATPX interface + * + * Verify the ATPX interface (all asics). + * Returns 0 on success, error on failure. + */ +static int radeon_atpx_init(void) +{ + /* set up the ATPX handle */ + return radeon_atpx_verify_interface(&radeon_atpx_priv.atpx); +} + +/** + * radeon_atpx_get_client_id - get the client id + * + * @pdev: pci device + * + * look up whether we are the integrated or discrete GPU (all asics). + * Returns the client id. + */ +static int radeon_atpx_get_client_id(struct pci_dev *pdev) +{ + if (radeon_atpx_priv.dhandle == DEVICE_ACPI_HANDLE(&pdev->dev)) + return VGA_SWITCHEROO_IGD; + else + return VGA_SWITCHEROO_DIS; +} + +static struct vga_switcheroo_handler radeon_atpx_handler = { + .switchto = radeon_atpx_switchto, + .power_state = radeon_atpx_power_state, + .init = radeon_atpx_init, + .get_client_id = radeon_atpx_get_client_id, +}; + +/** + * radeon_atpx_detect - detect whether we have PX + * + * Check if we have a PX system (all asics). + * Returns true if we have a PX system, false if not. + */ +static bool radeon_atpx_detect(void) +{ + char acpi_method_name[255] = { 0 }; + ACPI_BUFFER buffer = {sizeof(acpi_method_name), acpi_method_name}; + struct pci_dev *pdev = NULL; + bool has_atpx = false; + int vga_count = 0; + + while ((pdev = pci_get_class(PCI_CLASS_DISPLAY_VGA << 8, pdev)) != NULL) { + vga_count++; + + has_atpx |= (radeon_atpx_pci_probe_handle(pdev) == true); + } + + if (has_atpx && vga_count == 2) { + AcpiGetName(radeon_atpx_priv.atpx.handle, ACPI_FULL_PATHNAME, &buffer); + DRM_INFO("VGA switcheroo: detected switching method %s handle\n", + acpi_method_name); + radeon_atpx_priv.atpx_detected = true; + return true; + } + return false; +} +#endif /* DUMBBELL_WIP */ + +/** + * radeon_register_atpx_handler - register with vga_switcheroo + * + * Register the PX callbacks with vga_switcheroo (all asics). + */ +void radeon_register_atpx_handler(void) +{ +#ifdef DUMBBELL_WIP + bool r; + + /* detect if we have any ATPX + 2 VGA in the system */ + r = radeon_atpx_detect(); + if (!r) + return; + + vga_switcheroo_register_handler(&radeon_atpx_handler); +#endif /* DUMBBELL_WIP */ +} + +/** + * radeon_unregister_atpx_handler - unregister with vga_switcheroo + * + * Unregister the PX callbacks with vga_switcheroo (all asics). + */ +void radeon_unregister_atpx_handler(void) +{ +#ifdef DUMBBELL_WIP + vga_switcheroo_unregister_handler(); +#endif /* DUMBBELL_WIP */ +} diff --git a/sys/dev/drm2/radeon/radeon_benchmark.c b/sys/dev/drm2/radeon/radeon_benchmark.c new file mode 100644 index 00000000000..ce39748ecff --- /dev/null +++ b/sys/dev/drm2/radeon/radeon_benchmark.c @@ -0,0 +1,255 @@ +/* + * Copyright 2009 Jerome Glisse. + * + * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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. + * + * Authors: Jerome Glisse + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include "radeon_reg.h" +#include "radeon.h" + +#define RADEON_BENCHMARK_COPY_BLIT 1 +#define RADEON_BENCHMARK_COPY_DMA 0 + +#define RADEON_BENCHMARK_ITERATIONS 1024 +#define RADEON_BENCHMARK_COMMON_MODES_N 17 + +static int radeon_benchmark_do_move(struct radeon_device *rdev, unsigned size, + uint64_t saddr, uint64_t daddr, + int flag, int n) +{ + unsigned long start_jiffies; + unsigned long end_jiffies; + struct radeon_fence *fence = NULL; + int i, r; + + start_jiffies = jiffies; + for (i = 0; i < n; i++) { + switch (flag) { + case RADEON_BENCHMARK_COPY_DMA: + r = radeon_copy_dma(rdev, saddr, daddr, + size / RADEON_GPU_PAGE_SIZE, + &fence); + break; + case RADEON_BENCHMARK_COPY_BLIT: + r = radeon_copy_blit(rdev, saddr, daddr, + size / RADEON_GPU_PAGE_SIZE, + &fence); + break; + default: + DRM_ERROR("Unknown copy method\n"); + r = -EINVAL; + } + if (r) + goto exit_do_move; + r = radeon_fence_wait(fence, false); + if (r) + goto exit_do_move; + radeon_fence_unref(&fence); + } + end_jiffies = jiffies; + r = jiffies_to_msecs(end_jiffies - start_jiffies); + +exit_do_move: + if (fence) + radeon_fence_unref(&fence); + return r; +} + + +static void radeon_benchmark_log_results(int n, unsigned size, + unsigned int time, + unsigned sdomain, unsigned ddomain, + char *kind) +{ + unsigned int throughput = (n * (size >> 10)) / time; + DRM_INFO("radeon: %s %u bo moves of %u kB from" + " %d to %d in %u ms, throughput: %u Mb/s or %u MB/s\n", + kind, n, size >> 10, sdomain, ddomain, time, + throughput * 8, throughput); +} + +static void radeon_benchmark_move(struct radeon_device *rdev, unsigned size, + unsigned sdomain, unsigned ddomain) +{ + struct radeon_bo *dobj = NULL; + struct radeon_bo *sobj = NULL; + uint64_t saddr, daddr; + int r, n; + int time; + + n = RADEON_BENCHMARK_ITERATIONS; + r = radeon_bo_create(rdev, size, PAGE_SIZE, true, sdomain, NULL, &sobj); + if (r) { + goto out_cleanup; + } + r = radeon_bo_reserve(sobj, false); + if (unlikely(r != 0)) + goto out_cleanup; + r = radeon_bo_pin(sobj, sdomain, &saddr); + radeon_bo_unreserve(sobj); + if (r) { + goto out_cleanup; + } + r = radeon_bo_create(rdev, size, PAGE_SIZE, true, ddomain, NULL, &dobj); + if (r) { + goto out_cleanup; + } + r = radeon_bo_reserve(dobj, false); + if (unlikely(r != 0)) + goto out_cleanup; + r = radeon_bo_pin(dobj, ddomain, &daddr); + radeon_bo_unreserve(dobj); + if (r) { + goto out_cleanup; + } + + /* r100 doesn't have dma engine so skip the test */ + /* also, VRAM-to-VRAM test doesn't make much sense for DMA */ + /* skip it as well if domains are the same */ + if ((rdev->asic->copy.dma) && (sdomain != ddomain)) { + time = radeon_benchmark_do_move(rdev, size, saddr, daddr, + RADEON_BENCHMARK_COPY_DMA, n); + if (time < 0) + goto out_cleanup; + if (time > 0) + radeon_benchmark_log_results(n, size, time, + sdomain, ddomain, "dma"); + } + + time = radeon_benchmark_do_move(rdev, size, saddr, daddr, + RADEON_BENCHMARK_COPY_BLIT, n); + if (time < 0) + goto out_cleanup; + if (time > 0) + radeon_benchmark_log_results(n, size, time, + sdomain, ddomain, "blit"); + +out_cleanup: + if (sobj) { + r = radeon_bo_reserve(sobj, false); + if (likely(r == 0)) { + radeon_bo_unpin(sobj); + radeon_bo_unreserve(sobj); + } + radeon_bo_unref(&sobj); + } + if (dobj) { + r = radeon_bo_reserve(dobj, false); + if (likely(r == 0)) { + radeon_bo_unpin(dobj); + radeon_bo_unreserve(dobj); + } + radeon_bo_unref(&dobj); + } + + if (r) { + DRM_ERROR("Error while benchmarking BO move.\n"); + } +} + +void radeon_benchmark(struct radeon_device *rdev, int test_number) +{ + int i; + int common_modes[RADEON_BENCHMARK_COMMON_MODES_N] = { + 640 * 480 * 4, + 720 * 480 * 4, + 800 * 600 * 4, + 848 * 480 * 4, + 1024 * 768 * 4, + 1152 * 768 * 4, + 1280 * 720 * 4, + 1280 * 800 * 4, + 1280 * 854 * 4, + 1280 * 960 * 4, + 1280 * 1024 * 4, + 1440 * 900 * 4, + 1400 * 1050 * 4, + 1680 * 1050 * 4, + 1600 * 1200 * 4, + 1920 * 1080 * 4, + 1920 * 1200 * 4 + }; + + switch (test_number) { + case 1: + /* simple test, VRAM to GTT and GTT to VRAM */ + radeon_benchmark_move(rdev, 1024*1024, RADEON_GEM_DOMAIN_GTT, + RADEON_GEM_DOMAIN_VRAM); + radeon_benchmark_move(rdev, 1024*1024, RADEON_GEM_DOMAIN_VRAM, + RADEON_GEM_DOMAIN_GTT); + break; + case 2: + /* simple test, VRAM to VRAM */ + radeon_benchmark_move(rdev, 1024*1024, RADEON_GEM_DOMAIN_VRAM, + RADEON_GEM_DOMAIN_VRAM); + break; + case 3: + /* GTT to VRAM, buffer size sweep, powers of 2 */ + for (i = 1; i <= 16384; i <<= 1) + radeon_benchmark_move(rdev, i * RADEON_GPU_PAGE_SIZE, + RADEON_GEM_DOMAIN_GTT, + RADEON_GEM_DOMAIN_VRAM); + break; + case 4: + /* VRAM to GTT, buffer size sweep, powers of 2 */ + for (i = 1; i <= 16384; i <<= 1) + radeon_benchmark_move(rdev, i * RADEON_GPU_PAGE_SIZE, + RADEON_GEM_DOMAIN_VRAM, + RADEON_GEM_DOMAIN_GTT); + break; + case 5: + /* VRAM to VRAM, buffer size sweep, powers of 2 */ + for (i = 1; i <= 16384; i <<= 1) + radeon_benchmark_move(rdev, i * RADEON_GPU_PAGE_SIZE, + RADEON_GEM_DOMAIN_VRAM, + RADEON_GEM_DOMAIN_VRAM); + break; + case 6: + /* GTT to VRAM, buffer size sweep, common modes */ + for (i = 0; i < RADEON_BENCHMARK_COMMON_MODES_N; i++) + radeon_benchmark_move(rdev, common_modes[i], + RADEON_GEM_DOMAIN_GTT, + RADEON_GEM_DOMAIN_VRAM); + break; + case 7: + /* VRAM to GTT, buffer size sweep, common modes */ + for (i = 0; i < RADEON_BENCHMARK_COMMON_MODES_N; i++) + radeon_benchmark_move(rdev, common_modes[i], + RADEON_GEM_DOMAIN_VRAM, + RADEON_GEM_DOMAIN_GTT); + break; + case 8: + /* VRAM to VRAM, buffer size sweep, common modes */ + for (i = 0; i < RADEON_BENCHMARK_COMMON_MODES_N; i++) + radeon_benchmark_move(rdev, common_modes[i], + RADEON_GEM_DOMAIN_VRAM, + RADEON_GEM_DOMAIN_VRAM); + break; + + default: + DRM_ERROR("Unknown benchmark\n"); + } +} diff --git a/sys/dev/drm2/radeon/radeon_bios.c b/sys/dev/drm2/radeon/radeon_bios.c new file mode 100644 index 00000000000..ee48268469e --- /dev/null +++ b/sys/dev/drm2/radeon/radeon_bios.c @@ -0,0 +1,731 @@ +/* + * Copyright 2008 Advanced Micro Devices, Inc. + * Copyright 2008 Red Hat Inc. + * Copyright 2009 Jerome Glisse. + * + * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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. + * + * Authors: Dave Airlie + * Alex Deucher + * Jerome Glisse + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include "radeon_reg.h" +#include "radeon.h" +#include "atom.h" + +/* + * BIOS. + */ + +/* If you boot an IGP board with a discrete card as the primary, + * the IGP rom is not accessible via the rom bar as the IGP rom is + * part of the system bios. On boot, the system bios puts a + * copy of the igp rom at the start of vram if a discrete card is + * present. + */ +static bool igp_read_bios_from_vram(struct radeon_device *rdev) +{ + drm_local_map_t bios_map; + uint8_t __iomem *bios; + resource_size_t vram_base; + resource_size_t size = 256 * 1024; /* ??? */ + + DRM_INFO("%s: ===> Try IGP's VRAM...\n", __func__); + + if (!(rdev->flags & RADEON_IS_IGP)) + if (!radeon_card_posted(rdev)) { + DRM_INFO("%s: not POSTed discrete card detected, skipping this method...\n", + __func__); + return false; + } + + rdev->bios = NULL; + vram_base = drm_get_resource_start(rdev->ddev, 0); + DRM_INFO("%s: VRAM base address: 0x%jx\n", __func__, (uintmax_t)vram_base); + + bios_map.offset = vram_base; + bios_map.size = size; + bios_map.type = 0; + bios_map.flags = 0; + bios_map.mtrr = 0; + drm_core_ioremap(&bios_map, rdev->ddev); + if (bios_map.virtual == NULL) { + DRM_INFO("%s: failed to ioremap\n", __func__); + return false; + } + bios = bios_map.virtual; + size = bios_map.size; + DRM_INFO("%s: Map address: %p (%ju bytes)\n", __func__, bios, (uintmax_t)size); + + if (size == 0 || bios[0] != 0x55 || bios[1] != 0xaa) { + if (size == 0) { + DRM_INFO("%s: Incorrect BIOS size\n", __func__); + } else { + DRM_INFO("%s: Incorrect BIOS signature: 0x%02X%02X\n", + __func__, bios[0], bios[1]); + } + drm_core_ioremapfree(&bios_map, rdev->ddev); + return false; + } + rdev->bios = malloc(size, DRM_MEM_DRIVER, M_WAITOK); + if (rdev->bios == NULL) { + drm_core_ioremapfree(&bios_map, rdev->ddev); + return false; + } + memcpy_fromio(rdev->bios, bios, size); + drm_core_ioremapfree(&bios_map, rdev->ddev); + return true; +} + +static bool radeon_read_bios(struct radeon_device *rdev) +{ + device_t vga_dev; + uint8_t __iomem *bios; + size_t size; + + DRM_INFO("%s: ===> Try PCI Expansion ROM...\n", __func__); + + vga_dev = device_get_parent(rdev->dev); + rdev->bios = NULL; + /* XXX: some cards may return 0 for rom size? ddx has a workaround */ + bios = vga_pci_map_bios(vga_dev, &size); + if (!bios) { + return false; + } + DRM_INFO("%s: Map address: %p (%zu bytes)\n", __func__, bios, size); + + if (size == 0 || bios[0] != 0x55 || bios[1] != 0xaa) { + if (size == 0) { + DRM_INFO("%s: Incorrect BIOS size\n", __func__); + } else { + DRM_INFO("%s: Incorrect BIOS signature: 0x%02X%02X\n", + __func__, bios[0], bios[1]); + } + vga_pci_unmap_bios(vga_dev, bios); + return false; + } + rdev->bios = malloc(size, DRM_MEM_DRIVER, M_WAITOK); + memcpy(rdev->bios, bios, size); + vga_pci_unmap_bios(vga_dev, bios); + return true; +} + +/* ATRM is used to get the BIOS on the discrete cards in + * dual-gpu systems. + */ +/* retrieve the ROM in 4k blocks */ +#define ATRM_BIOS_PAGE 4096 +/** + * radeon_atrm_call - fetch a chunk of the vbios + * + * @atrm_handle: acpi ATRM handle + * @bios: vbios image pointer + * @offset: offset of vbios image data to fetch + * @len: length of vbios image data to fetch + * + * Executes ATRM to fetch a chunk of the discrete + * vbios image on PX systems (all asics). + * Returns the length of the buffer fetched. + */ +static int radeon_atrm_call(ACPI_HANDLE atrm_handle, uint8_t *bios, + int offset, int len) +{ + ACPI_STATUS status; + ACPI_OBJECT atrm_arg_elements[2], *obj; + ACPI_OBJECT_LIST atrm_arg; + ACPI_BUFFER buffer = { ACPI_ALLOCATE_BUFFER, NULL}; + + atrm_arg.Count = 2; + atrm_arg.Pointer = &atrm_arg_elements[0]; + + atrm_arg_elements[0].Type = ACPI_TYPE_INTEGER; + atrm_arg_elements[0].Integer.Value = offset; + + atrm_arg_elements[1].Type = ACPI_TYPE_INTEGER; + atrm_arg_elements[1].Integer.Value = len; + + status = AcpiEvaluateObject(atrm_handle, NULL, &atrm_arg, &buffer); + if (ACPI_FAILURE(status)) { + DRM_ERROR("failed to evaluate ATRM got %s\n", AcpiFormatException(status)); + return -ENODEV; + } + + obj = (ACPI_OBJECT *)buffer.Pointer; + memcpy(bios+offset, obj->Buffer.Pointer, obj->Buffer.Length); + len = obj->Buffer.Length; + AcpiOsFree(buffer.Pointer); + return len; +} + +static bool radeon_atrm_get_bios(struct radeon_device *rdev) +{ + int ret; + int size = 256 * 1024; + int i; + device_t dev; + ACPI_HANDLE dhandle, atrm_handle; + ACPI_STATUS status; + bool found = false; + + DRM_INFO("%s: ===> Try ATRM...\n", __func__); + + /* ATRM is for the discrete card only */ + if (rdev->flags & RADEON_IS_IGP) { + DRM_INFO("%s: IGP card detected, skipping this method...\n", + __func__); + return false; + } + +#ifdef DUMBBELL_WIP + while ((pdev = pci_get_class(PCI_CLASS_DISPLAY_VGA << 8, pdev)) != NULL) { +#endif /* DUMBBELL_WIP */ + if ((dev = pci_find_class(PCIC_DISPLAY, PCIS_DISPLAY_VGA)) != NULL) { + DRM_INFO("%s: pci_find_class() found: %d:%d:%d:%d, vendor=%04x, device=%04x\n", + __func__, + pci_get_domain(dev), + pci_get_bus(dev), + pci_get_slot(dev), + pci_get_function(dev), + pci_get_vendor(dev), + pci_get_device(dev)); + DRM_INFO("%s: Get ACPI device handle\n", __func__); + dhandle = acpi_get_handle(dev); +#ifdef DUMBBELL_WIP + if (!dhandle) + continue; +#endif /* DUMBBELL_WIP */ + if (!dhandle) + return false; + + DRM_INFO("%s: Get ACPI handle for \"ATRM\"\n", __func__); + status = AcpiGetHandle(dhandle, "ATRM", &atrm_handle); + if (!ACPI_FAILURE(status)) { + found = true; +#ifdef DUMBBELL_WIP + break; +#endif /* DUMBBELL_WIP */ + } else { + DRM_INFO("%s: Failed to get \"ATRM\" handle: %s\n", + __func__, AcpiFormatException(status)); + } + } + + if (!found) + return false; + + rdev->bios = malloc(size, DRM_MEM_DRIVER, M_WAITOK); + if (!rdev->bios) { + DRM_ERROR("Unable to allocate bios\n"); + return false; + } + + for (i = 0; i < size / ATRM_BIOS_PAGE; i++) { + DRM_INFO("%s: Call radeon_atrm_call()\n", __func__); + ret = radeon_atrm_call(atrm_handle, + rdev->bios, + (i * ATRM_BIOS_PAGE), + ATRM_BIOS_PAGE); + if (ret < ATRM_BIOS_PAGE) + break; + } + + if (i == 0 || rdev->bios[0] != 0x55 || rdev->bios[1] != 0xaa) { + if (i == 0) { + DRM_INFO("%s: Incorrect BIOS size\n", __func__); + } else { + DRM_INFO("%s: Incorrect BIOS signature: 0x%02X%02X\n", + __func__, rdev->bios[0], rdev->bios[1]); + } + free(rdev->bios, DRM_MEM_DRIVER); + return false; + } + return true; +} + +static bool ni_read_disabled_bios(struct radeon_device *rdev) +{ + u32 bus_cntl; + u32 d1vga_control; + u32 d2vga_control; + u32 vga_render_control; + u32 rom_cntl; + bool r; + + DRM_INFO("%s: ===> Try disabled BIOS (ni)...\n", __func__); + + bus_cntl = RREG32(R600_BUS_CNTL); + d1vga_control = RREG32(AVIVO_D1VGA_CONTROL); + d2vga_control = RREG32(AVIVO_D2VGA_CONTROL); + vga_render_control = RREG32(AVIVO_VGA_RENDER_CONTROL); + rom_cntl = RREG32(R600_ROM_CNTL); + + /* enable the rom */ + WREG32(R600_BUS_CNTL, (bus_cntl & ~R600_BIOS_ROM_DIS)); + /* Disable VGA mode */ + WREG32(AVIVO_D1VGA_CONTROL, + (d1vga_control & ~(AVIVO_DVGA_CONTROL_MODE_ENABLE | + AVIVO_DVGA_CONTROL_TIMING_SELECT))); + WREG32(AVIVO_D2VGA_CONTROL, + (d2vga_control & ~(AVIVO_DVGA_CONTROL_MODE_ENABLE | + AVIVO_DVGA_CONTROL_TIMING_SELECT))); + WREG32(AVIVO_VGA_RENDER_CONTROL, + (vga_render_control & ~AVIVO_VGA_VSTATUS_CNTL_MASK)); + WREG32(R600_ROM_CNTL, rom_cntl | R600_SCK_OVERWRITE); + + r = radeon_read_bios(rdev); + + /* restore regs */ + WREG32(R600_BUS_CNTL, bus_cntl); + WREG32(AVIVO_D1VGA_CONTROL, d1vga_control); + WREG32(AVIVO_D2VGA_CONTROL, d2vga_control); + WREG32(AVIVO_VGA_RENDER_CONTROL, vga_render_control); + WREG32(R600_ROM_CNTL, rom_cntl); + return r; +} + +static bool r700_read_disabled_bios(struct radeon_device *rdev) +{ + uint32_t viph_control; + uint32_t bus_cntl; + uint32_t d1vga_control; + uint32_t d2vga_control; + uint32_t vga_render_control; + uint32_t rom_cntl; + uint32_t cg_spll_func_cntl = 0; + uint32_t cg_spll_status; + bool r; + + DRM_INFO("%s: ===> Try disabled BIOS (r700)...\n", __func__); + + viph_control = RREG32(RADEON_VIPH_CONTROL); + bus_cntl = RREG32(R600_BUS_CNTL); + d1vga_control = RREG32(AVIVO_D1VGA_CONTROL); + d2vga_control = RREG32(AVIVO_D2VGA_CONTROL); + vga_render_control = RREG32(AVIVO_VGA_RENDER_CONTROL); + rom_cntl = RREG32(R600_ROM_CNTL); + + /* disable VIP */ + WREG32(RADEON_VIPH_CONTROL, (viph_control & ~RADEON_VIPH_EN)); + /* enable the rom */ + WREG32(R600_BUS_CNTL, (bus_cntl & ~R600_BIOS_ROM_DIS)); + /* Disable VGA mode */ + WREG32(AVIVO_D1VGA_CONTROL, + (d1vga_control & ~(AVIVO_DVGA_CONTROL_MODE_ENABLE | + AVIVO_DVGA_CONTROL_TIMING_SELECT))); + WREG32(AVIVO_D2VGA_CONTROL, + (d2vga_control & ~(AVIVO_DVGA_CONTROL_MODE_ENABLE | + AVIVO_DVGA_CONTROL_TIMING_SELECT))); + WREG32(AVIVO_VGA_RENDER_CONTROL, + (vga_render_control & ~AVIVO_VGA_VSTATUS_CNTL_MASK)); + + if (rdev->family == CHIP_RV730) { + cg_spll_func_cntl = RREG32(R600_CG_SPLL_FUNC_CNTL); + + /* enable bypass mode */ + WREG32(R600_CG_SPLL_FUNC_CNTL, (cg_spll_func_cntl | + R600_SPLL_BYPASS_EN)); + + /* wait for SPLL_CHG_STATUS to change to 1 */ + cg_spll_status = 0; + while (!(cg_spll_status & R600_SPLL_CHG_STATUS)) + cg_spll_status = RREG32(R600_CG_SPLL_STATUS); + + WREG32(R600_ROM_CNTL, (rom_cntl & ~R600_SCK_OVERWRITE)); + } else + WREG32(R600_ROM_CNTL, (rom_cntl | R600_SCK_OVERWRITE)); + + r = radeon_read_bios(rdev); + + /* restore regs */ + if (rdev->family == CHIP_RV730) { + WREG32(R600_CG_SPLL_FUNC_CNTL, cg_spll_func_cntl); + + /* wait for SPLL_CHG_STATUS to change to 1 */ + cg_spll_status = 0; + while (!(cg_spll_status & R600_SPLL_CHG_STATUS)) + cg_spll_status = RREG32(R600_CG_SPLL_STATUS); + } + WREG32(RADEON_VIPH_CONTROL, viph_control); + WREG32(R600_BUS_CNTL, bus_cntl); + WREG32(AVIVO_D1VGA_CONTROL, d1vga_control); + WREG32(AVIVO_D2VGA_CONTROL, d2vga_control); + WREG32(AVIVO_VGA_RENDER_CONTROL, vga_render_control); + WREG32(R600_ROM_CNTL, rom_cntl); + return r; +} + +static bool r600_read_disabled_bios(struct radeon_device *rdev) +{ + uint32_t viph_control; + uint32_t bus_cntl; + uint32_t d1vga_control; + uint32_t d2vga_control; + uint32_t vga_render_control; + uint32_t rom_cntl; + uint32_t general_pwrmgt; + uint32_t low_vid_lower_gpio_cntl; + uint32_t medium_vid_lower_gpio_cntl; + uint32_t high_vid_lower_gpio_cntl; + uint32_t ctxsw_vid_lower_gpio_cntl; + uint32_t lower_gpio_enable; + bool r; + + DRM_INFO("%s: ===> Try disabled BIOS (r600)...\n", __func__); + + viph_control = RREG32(RADEON_VIPH_CONTROL); + bus_cntl = RREG32(R600_BUS_CNTL); + d1vga_control = RREG32(AVIVO_D1VGA_CONTROL); + d2vga_control = RREG32(AVIVO_D2VGA_CONTROL); + vga_render_control = RREG32(AVIVO_VGA_RENDER_CONTROL); + rom_cntl = RREG32(R600_ROM_CNTL); + general_pwrmgt = RREG32(R600_GENERAL_PWRMGT); + low_vid_lower_gpio_cntl = RREG32(R600_LOW_VID_LOWER_GPIO_CNTL); + medium_vid_lower_gpio_cntl = RREG32(R600_MEDIUM_VID_LOWER_GPIO_CNTL); + high_vid_lower_gpio_cntl = RREG32(R600_HIGH_VID_LOWER_GPIO_CNTL); + ctxsw_vid_lower_gpio_cntl = RREG32(R600_CTXSW_VID_LOWER_GPIO_CNTL); + lower_gpio_enable = RREG32(R600_LOWER_GPIO_ENABLE); + + /* disable VIP */ + WREG32(RADEON_VIPH_CONTROL, (viph_control & ~RADEON_VIPH_EN)); + /* enable the rom */ + WREG32(R600_BUS_CNTL, (bus_cntl & ~R600_BIOS_ROM_DIS)); + /* Disable VGA mode */ + WREG32(AVIVO_D1VGA_CONTROL, + (d1vga_control & ~(AVIVO_DVGA_CONTROL_MODE_ENABLE | + AVIVO_DVGA_CONTROL_TIMING_SELECT))); + WREG32(AVIVO_D2VGA_CONTROL, + (d2vga_control & ~(AVIVO_DVGA_CONTROL_MODE_ENABLE | + AVIVO_DVGA_CONTROL_TIMING_SELECT))); + WREG32(AVIVO_VGA_RENDER_CONTROL, + (vga_render_control & ~AVIVO_VGA_VSTATUS_CNTL_MASK)); + + WREG32(R600_ROM_CNTL, + ((rom_cntl & ~R600_SCK_PRESCALE_CRYSTAL_CLK_MASK) | + (1 << R600_SCK_PRESCALE_CRYSTAL_CLK_SHIFT) | + R600_SCK_OVERWRITE)); + + WREG32(R600_GENERAL_PWRMGT, (general_pwrmgt & ~R600_OPEN_DRAIN_PADS)); + WREG32(R600_LOW_VID_LOWER_GPIO_CNTL, + (low_vid_lower_gpio_cntl & ~0x400)); + WREG32(R600_MEDIUM_VID_LOWER_GPIO_CNTL, + (medium_vid_lower_gpio_cntl & ~0x400)); + WREG32(R600_HIGH_VID_LOWER_GPIO_CNTL, + (high_vid_lower_gpio_cntl & ~0x400)); + WREG32(R600_CTXSW_VID_LOWER_GPIO_CNTL, + (ctxsw_vid_lower_gpio_cntl & ~0x400)); + WREG32(R600_LOWER_GPIO_ENABLE, (lower_gpio_enable | 0x400)); + + r = radeon_read_bios(rdev); + + /* restore regs */ + WREG32(RADEON_VIPH_CONTROL, viph_control); + WREG32(R600_BUS_CNTL, bus_cntl); + WREG32(AVIVO_D1VGA_CONTROL, d1vga_control); + WREG32(AVIVO_D2VGA_CONTROL, d2vga_control); + WREG32(AVIVO_VGA_RENDER_CONTROL, vga_render_control); + WREG32(R600_ROM_CNTL, rom_cntl); + WREG32(R600_GENERAL_PWRMGT, general_pwrmgt); + WREG32(R600_LOW_VID_LOWER_GPIO_CNTL, low_vid_lower_gpio_cntl); + WREG32(R600_MEDIUM_VID_LOWER_GPIO_CNTL, medium_vid_lower_gpio_cntl); + WREG32(R600_HIGH_VID_LOWER_GPIO_CNTL, high_vid_lower_gpio_cntl); + WREG32(R600_CTXSW_VID_LOWER_GPIO_CNTL, ctxsw_vid_lower_gpio_cntl); + WREG32(R600_LOWER_GPIO_ENABLE, lower_gpio_enable); + return r; +} + +static bool avivo_read_disabled_bios(struct radeon_device *rdev) +{ + uint32_t seprom_cntl1; + uint32_t viph_control; + uint32_t bus_cntl; + uint32_t d1vga_control; + uint32_t d2vga_control; + uint32_t vga_render_control; + uint32_t gpiopad_a; + uint32_t gpiopad_en; + uint32_t gpiopad_mask; + bool r; + + DRM_INFO("%s: ===> Try disabled BIOS (avivo)...\n", __func__); + + seprom_cntl1 = RREG32(RADEON_SEPROM_CNTL1); + viph_control = RREG32(RADEON_VIPH_CONTROL); + bus_cntl = RREG32(RV370_BUS_CNTL); + d1vga_control = RREG32(AVIVO_D1VGA_CONTROL); + d2vga_control = RREG32(AVIVO_D2VGA_CONTROL); + vga_render_control = RREG32(AVIVO_VGA_RENDER_CONTROL); + gpiopad_a = RREG32(RADEON_GPIOPAD_A); + gpiopad_en = RREG32(RADEON_GPIOPAD_EN); + gpiopad_mask = RREG32(RADEON_GPIOPAD_MASK); + + WREG32(RADEON_SEPROM_CNTL1, + ((seprom_cntl1 & ~RADEON_SCK_PRESCALE_MASK) | + (0xc << RADEON_SCK_PRESCALE_SHIFT))); + WREG32(RADEON_GPIOPAD_A, 0); + WREG32(RADEON_GPIOPAD_EN, 0); + WREG32(RADEON_GPIOPAD_MASK, 0); + + /* disable VIP */ + WREG32(RADEON_VIPH_CONTROL, (viph_control & ~RADEON_VIPH_EN)); + + /* enable the rom */ + WREG32(RV370_BUS_CNTL, (bus_cntl & ~RV370_BUS_BIOS_DIS_ROM)); + + /* Disable VGA mode */ + WREG32(AVIVO_D1VGA_CONTROL, + (d1vga_control & ~(AVIVO_DVGA_CONTROL_MODE_ENABLE | + AVIVO_DVGA_CONTROL_TIMING_SELECT))); + WREG32(AVIVO_D2VGA_CONTROL, + (d2vga_control & ~(AVIVO_DVGA_CONTROL_MODE_ENABLE | + AVIVO_DVGA_CONTROL_TIMING_SELECT))); + WREG32(AVIVO_VGA_RENDER_CONTROL, + (vga_render_control & ~AVIVO_VGA_VSTATUS_CNTL_MASK)); + + r = radeon_read_bios(rdev); + + /* restore regs */ + WREG32(RADEON_SEPROM_CNTL1, seprom_cntl1); + WREG32(RADEON_VIPH_CONTROL, viph_control); + WREG32(RV370_BUS_CNTL, bus_cntl); + WREG32(AVIVO_D1VGA_CONTROL, d1vga_control); + WREG32(AVIVO_D2VGA_CONTROL, d2vga_control); + WREG32(AVIVO_VGA_RENDER_CONTROL, vga_render_control); + WREG32(RADEON_GPIOPAD_A, gpiopad_a); + WREG32(RADEON_GPIOPAD_EN, gpiopad_en); + WREG32(RADEON_GPIOPAD_MASK, gpiopad_mask); + return r; +} + +static bool legacy_read_disabled_bios(struct radeon_device *rdev) +{ + uint32_t seprom_cntl1; + uint32_t viph_control; + uint32_t bus_cntl; + uint32_t crtc_gen_cntl; + uint32_t crtc2_gen_cntl; + uint32_t crtc_ext_cntl; + uint32_t fp2_gen_cntl; + bool r; + + DRM_INFO("%s: ===> Try disabled BIOS (legacy)...\n", __func__); + + seprom_cntl1 = RREG32(RADEON_SEPROM_CNTL1); + viph_control = RREG32(RADEON_VIPH_CONTROL); + if (rdev->flags & RADEON_IS_PCIE) + bus_cntl = RREG32(RV370_BUS_CNTL); + else + bus_cntl = RREG32(RADEON_BUS_CNTL); + crtc_gen_cntl = RREG32(RADEON_CRTC_GEN_CNTL); + crtc2_gen_cntl = 0; + crtc_ext_cntl = RREG32(RADEON_CRTC_EXT_CNTL); + fp2_gen_cntl = 0; + +#define PCI_DEVICE_ID_ATI_RADEON_QY 0x5159 + + if (rdev->ddev->pci_device == PCI_DEVICE_ID_ATI_RADEON_QY) { + fp2_gen_cntl = RREG32(RADEON_FP2_GEN_CNTL); + } + + if (!(rdev->flags & RADEON_SINGLE_CRTC)) { + crtc2_gen_cntl = RREG32(RADEON_CRTC2_GEN_CNTL); + } + + WREG32(RADEON_SEPROM_CNTL1, + ((seprom_cntl1 & ~RADEON_SCK_PRESCALE_MASK) | + (0xc << RADEON_SCK_PRESCALE_SHIFT))); + + /* disable VIP */ + WREG32(RADEON_VIPH_CONTROL, (viph_control & ~RADEON_VIPH_EN)); + + /* enable the rom */ + if (rdev->flags & RADEON_IS_PCIE) + WREG32(RV370_BUS_CNTL, (bus_cntl & ~RV370_BUS_BIOS_DIS_ROM)); + else + WREG32(RADEON_BUS_CNTL, (bus_cntl & ~RADEON_BUS_BIOS_DIS_ROM)); + + /* Turn off mem requests and CRTC for both controllers */ + WREG32(RADEON_CRTC_GEN_CNTL, + ((crtc_gen_cntl & ~RADEON_CRTC_EN) | + (RADEON_CRTC_DISP_REQ_EN_B | + RADEON_CRTC_EXT_DISP_EN))); + if (!(rdev->flags & RADEON_SINGLE_CRTC)) { + WREG32(RADEON_CRTC2_GEN_CNTL, + ((crtc2_gen_cntl & ~RADEON_CRTC2_EN) | + RADEON_CRTC2_DISP_REQ_EN_B)); + } + /* Turn off CRTC */ + WREG32(RADEON_CRTC_EXT_CNTL, + ((crtc_ext_cntl & ~RADEON_CRTC_CRT_ON) | + (RADEON_CRTC_SYNC_TRISTAT | + RADEON_CRTC_DISPLAY_DIS))); + + if (rdev->ddev->pci_device == PCI_DEVICE_ID_ATI_RADEON_QY) { + WREG32(RADEON_FP2_GEN_CNTL, (fp2_gen_cntl & ~RADEON_FP2_ON)); + } + + r = radeon_read_bios(rdev); + + /* restore regs */ + WREG32(RADEON_SEPROM_CNTL1, seprom_cntl1); + WREG32(RADEON_VIPH_CONTROL, viph_control); + if (rdev->flags & RADEON_IS_PCIE) + WREG32(RV370_BUS_CNTL, bus_cntl); + else + WREG32(RADEON_BUS_CNTL, bus_cntl); + WREG32(RADEON_CRTC_GEN_CNTL, crtc_gen_cntl); + if (!(rdev->flags & RADEON_SINGLE_CRTC)) { + WREG32(RADEON_CRTC2_GEN_CNTL, crtc2_gen_cntl); + } + WREG32(RADEON_CRTC_EXT_CNTL, crtc_ext_cntl); + if (rdev->ddev->pci_device == PCI_DEVICE_ID_ATI_RADEON_QY) { + WREG32(RADEON_FP2_GEN_CNTL, fp2_gen_cntl); + } + return r; +} + +static bool radeon_read_disabled_bios(struct radeon_device *rdev) +{ + if (rdev->flags & RADEON_IS_IGP) + return igp_read_bios_from_vram(rdev); + else if (rdev->family >= CHIP_BARTS) + return ni_read_disabled_bios(rdev); + else if (rdev->family >= CHIP_RV770) + return r700_read_disabled_bios(rdev); + else if (rdev->family >= CHIP_R600) + return r600_read_disabled_bios(rdev); + else if (rdev->family >= CHIP_RS600) + return avivo_read_disabled_bios(rdev); + else + return legacy_read_disabled_bios(rdev); +} + +static bool radeon_acpi_vfct_bios(struct radeon_device *rdev) +{ + bool ret = false; + ACPI_TABLE_HEADER *hdr; + ACPI_SIZE tbl_size; + UEFI_ACPI_VFCT *vfct; + GOP_VBIOS_CONTENT *vbios; + VFCT_IMAGE_HEADER *vhdr; + ACPI_STATUS status; + + DRM_INFO("%s: ===> Try VFCT...\n", __func__); + + DRM_INFO("%s: Get \"VFCT\" ACPI table\n", __func__); + status = AcpiGetTable("VFCT", 1, &hdr); + if (!ACPI_SUCCESS(status)) { + DRM_INFO("%s: Failed to get \"VFCT\" table: %s\n", + __func__, AcpiFormatException(status)); + return false; + } + tbl_size = hdr->Length; + if (tbl_size < sizeof(UEFI_ACPI_VFCT)) { + DRM_ERROR("ACPI VFCT table present but broken (too short #1)\n"); + goto out_unmap; + } + + vfct = (UEFI_ACPI_VFCT *)hdr; + if (vfct->VBIOSImageOffset + sizeof(VFCT_IMAGE_HEADER) > tbl_size) { + DRM_ERROR("ACPI VFCT table present but broken (too short #2)\n"); + goto out_unmap; + } + + vbios = (GOP_VBIOS_CONTENT *)((char *)hdr + vfct->VBIOSImageOffset); + vhdr = &vbios->VbiosHeader; + DRM_INFO("ACPI VFCT contains a BIOS for %02x:%02x.%d %04x:%04x, size %d\n", + vhdr->PCIBus, vhdr->PCIDevice, vhdr->PCIFunction, + vhdr->VendorID, vhdr->DeviceID, vhdr->ImageLength); + + if (vhdr->PCIBus != rdev->ddev->pci_bus || + vhdr->PCIDevice != rdev->ddev->pci_slot || + vhdr->PCIFunction != rdev->ddev->pci_func || + vhdr->VendorID != rdev->ddev->pci_vendor || + vhdr->DeviceID != rdev->ddev->pci_device) { + DRM_INFO("ACPI VFCT table is not for this card\n"); + goto out_unmap; + }; + + if (vfct->VBIOSImageOffset + sizeof(VFCT_IMAGE_HEADER) + vhdr->ImageLength > tbl_size) { + DRM_ERROR("ACPI VFCT image truncated\n"); + goto out_unmap; + } + + rdev->bios = malloc(vhdr->ImageLength, DRM_MEM_DRIVER, M_WAITOK); + memcpy(rdev->bios, &vbios->VbiosContent, vhdr->ImageLength); + ret = !!rdev->bios; + +out_unmap: + return ret; +} + +bool radeon_get_bios(struct radeon_device *rdev) +{ + bool r; + uint16_t tmp; + + r = radeon_atrm_get_bios(rdev); + if (r == false) + r = radeon_acpi_vfct_bios(rdev); + if (r == false) + r = igp_read_bios_from_vram(rdev); + if (r == false) + r = radeon_read_bios(rdev); + if (r == false) { + r = radeon_read_disabled_bios(rdev); + } + if (r == false || rdev->bios == NULL) { + DRM_ERROR("Unable to locate a BIOS ROM\n"); + rdev->bios = NULL; + return false; + } + if (rdev->bios[0] != 0x55 || rdev->bios[1] != 0xaa) { + DRM_ERROR("BIOS signature incorrect %x %x\n", rdev->bios[0], rdev->bios[1]); + goto free_bios; + } + + tmp = RBIOS16(0x18); + if (RBIOS8(tmp + 0x14) != 0x0) { + DRM_INFO("Not an x86 BIOS ROM, not using.\n"); + goto free_bios; + } + + rdev->bios_header_start = RBIOS16(0x48); + if (!rdev->bios_header_start) { + goto free_bios; + } + tmp = rdev->bios_header_start + 4; + if (!memcmp(rdev->bios + tmp, "ATOM", 4) || + !memcmp(rdev->bios + tmp, "MOTA", 4)) { + rdev->is_atom_bios = true; + } else { + rdev->is_atom_bios = false; + } + + DRM_DEBUG("%sBIOS detected\n", rdev->is_atom_bios ? "ATOM" : "COM"); + return true; +free_bios: + free(rdev->bios, DRM_MEM_DRIVER); + rdev->bios = NULL; + return false; +} diff --git a/sys/dev/drm2/radeon/radeon_blit_common.h b/sys/dev/drm2/radeon/radeon_blit_common.h new file mode 100644 index 00000000000..a8f6827cf79 --- /dev/null +++ b/sys/dev/drm2/radeon/radeon_blit_common.h @@ -0,0 +1,47 @@ +/* + * Copyright 2009 Advanced Micro Devices, Inc. + * Copyright 2009 Red Hat Inc. + * Copyright 2012 Alcatel-Lucent, Inc. + * + * 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 (including the next + * paragraph) 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 COPYRIGHT HOLDER(S) AND/OR ITS SUPPLIERS 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 __RADEON_BLIT_COMMON_H__ + +#include +__FBSDID("$FreeBSD$"); + +#define DI_PT_RECTLIST 0x11 +#define DI_INDEX_SIZE_16_BIT 0x0 +#define DI_SRC_SEL_AUTO_INDEX 0x2 + +#define FMT_8 0x1 +#define FMT_5_6_5 0x8 +#define FMT_8_8_8_8 0x1a +#define COLOR_8 0x1 +#define COLOR_5_6_5 0x8 +#define COLOR_8_8_8_8 0x1a + +#define RECT_UNIT_H 32 +#define RECT_UNIT_W (RADEON_GPU_PAGE_SIZE / 4 / RECT_UNIT_H) + +#define __RADEON_BLIT_COMMON_H__ +#endif diff --git a/sys/dev/drm2/radeon/radeon_clocks.c b/sys/dev/drm2/radeon/radeon_clocks.c new file mode 100644 index 00000000000..9ecd18adb94 --- /dev/null +++ b/sys/dev/drm2/radeon/radeon_clocks.c @@ -0,0 +1,917 @@ +/* + * Copyright 2008 Advanced Micro Devices, Inc. + * Copyright 2008 Red Hat Inc. + * Copyright 2009 Jerome Glisse. + * + * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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. + * + * Authors: Dave Airlie + * Alex Deucher + * Jerome Glisse + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include "radeon_reg.h" +#include "radeon.h" +#include "radeon_asic.h" +#include "atom.h" + +/* 10 khz */ +uint32_t radeon_legacy_get_engine_clock(struct radeon_device *rdev) +{ + struct radeon_pll *spll = &rdev->clock.spll; + uint32_t fb_div, ref_div, post_div, sclk; + + fb_div = RREG32_PLL(RADEON_M_SPLL_REF_FB_DIV); + fb_div = (fb_div >> RADEON_SPLL_FB_DIV_SHIFT) & RADEON_SPLL_FB_DIV_MASK; + fb_div <<= 1; + fb_div *= spll->reference_freq; + + ref_div = + RREG32_PLL(RADEON_M_SPLL_REF_FB_DIV) & RADEON_M_SPLL_REF_DIV_MASK; + + if (ref_div == 0) + return 0; + + sclk = fb_div / ref_div; + + post_div = RREG32_PLL(RADEON_SCLK_CNTL) & RADEON_SCLK_SRC_SEL_MASK; + if (post_div == 2) + sclk >>= 1; + else if (post_div == 3) + sclk >>= 2; + else if (post_div == 4) + sclk >>= 3; + + return sclk; +} + +/* 10 khz */ +uint32_t radeon_legacy_get_memory_clock(struct radeon_device *rdev) +{ + struct radeon_pll *mpll = &rdev->clock.mpll; + uint32_t fb_div, ref_div, post_div, mclk; + + fb_div = RREG32_PLL(RADEON_M_SPLL_REF_FB_DIV); + fb_div = (fb_div >> RADEON_MPLL_FB_DIV_SHIFT) & RADEON_MPLL_FB_DIV_MASK; + fb_div <<= 1; + fb_div *= mpll->reference_freq; + + ref_div = + RREG32_PLL(RADEON_M_SPLL_REF_FB_DIV) & RADEON_M_SPLL_REF_DIV_MASK; + + if (ref_div == 0) + return 0; + + mclk = fb_div / ref_div; + + post_div = RREG32_PLL(RADEON_MCLK_CNTL) & 0x7; + if (post_div == 2) + mclk >>= 1; + else if (post_div == 3) + mclk >>= 2; + else if (post_div == 4) + mclk >>= 3; + + return mclk; +} + +#ifdef CONFIG_OF +/* + * Read XTAL (ref clock), SCLK and MCLK from Open Firmware device + * tree. Hopefully, ATI OF driver is kind enough to fill these + */ +static bool radeon_read_clocks_OF(struct drm_device *dev) +{ + struct radeon_device *rdev = dev->dev_private; + struct device_node *dp = rdev->pdev->dev.of_node; + const u32 *val; + struct radeon_pll *p1pll = &rdev->clock.p1pll; + struct radeon_pll *p2pll = &rdev->clock.p2pll; + struct radeon_pll *spll = &rdev->clock.spll; + struct radeon_pll *mpll = &rdev->clock.mpll; + + if (dp == NULL) + return false; + val = of_get_property(dp, "ATY,RefCLK", NULL); + if (!val || !*val) { + printk(KERN_WARNING "radeonfb: No ATY,RefCLK property !\n"); + return false; + } + p1pll->reference_freq = p2pll->reference_freq = (*val) / 10; + p1pll->reference_div = RREG32_PLL(RADEON_PPLL_REF_DIV) & 0x3ff; + if (p1pll->reference_div < 2) + p1pll->reference_div = 12; + p2pll->reference_div = p1pll->reference_div; + + /* These aren't in the device-tree */ + if (rdev->family >= CHIP_R420) { + p1pll->pll_in_min = 100; + p1pll->pll_in_max = 1350; + p1pll->pll_out_min = 20000; + p1pll->pll_out_max = 50000; + p2pll->pll_in_min = 100; + p2pll->pll_in_max = 1350; + p2pll->pll_out_min = 20000; + p2pll->pll_out_max = 50000; + } else { + p1pll->pll_in_min = 40; + p1pll->pll_in_max = 500; + p1pll->pll_out_min = 12500; + p1pll->pll_out_max = 35000; + p2pll->pll_in_min = 40; + p2pll->pll_in_max = 500; + p2pll->pll_out_min = 12500; + p2pll->pll_out_max = 35000; + } + /* not sure what the max should be in all cases */ + rdev->clock.max_pixel_clock = 35000; + + spll->reference_freq = mpll->reference_freq = p1pll->reference_freq; + spll->reference_div = mpll->reference_div = + RREG32_PLL(RADEON_M_SPLL_REF_FB_DIV) & + RADEON_M_SPLL_REF_DIV_MASK; + + val = of_get_property(dp, "ATY,SCLK", NULL); + if (val && *val) + rdev->clock.default_sclk = (*val) / 10; + else + rdev->clock.default_sclk = + radeon_legacy_get_engine_clock(rdev); + + val = of_get_property(dp, "ATY,MCLK", NULL); + if (val && *val) + rdev->clock.default_mclk = (*val) / 10; + else + rdev->clock.default_mclk = + radeon_legacy_get_memory_clock(rdev); + + DRM_INFO("Using device-tree clock info\n"); + + return true; +} +#else +static bool radeon_read_clocks_OF(struct drm_device *dev) +{ + return false; +} +#endif /* CONFIG_OF */ + +void radeon_get_clock_info(struct drm_device *dev) +{ + struct radeon_device *rdev = dev->dev_private; + struct radeon_pll *p1pll = &rdev->clock.p1pll; + struct radeon_pll *p2pll = &rdev->clock.p2pll; + struct radeon_pll *dcpll = &rdev->clock.dcpll; + struct radeon_pll *spll = &rdev->clock.spll; + struct radeon_pll *mpll = &rdev->clock.mpll; + int ret; + + if (rdev->is_atom_bios) + ret = radeon_atom_get_clock_info(dev); + else + ret = radeon_combios_get_clock_info(dev); + if (!ret) + ret = radeon_read_clocks_OF(dev); + + if (ret) { + if (p1pll->reference_div < 2) { + if (!ASIC_IS_AVIVO(rdev)) { + u32 tmp = RREG32_PLL(RADEON_PPLL_REF_DIV); + if (ASIC_IS_R300(rdev)) + p1pll->reference_div = + (tmp & R300_PPLL_REF_DIV_ACC_MASK) >> R300_PPLL_REF_DIV_ACC_SHIFT; + else + p1pll->reference_div = tmp & RADEON_PPLL_REF_DIV_MASK; + if (p1pll->reference_div < 2) + p1pll->reference_div = 12; + } else + p1pll->reference_div = 12; + } + if (p2pll->reference_div < 2) + p2pll->reference_div = 12; + if (rdev->family < CHIP_RS600) { + if (spll->reference_div < 2) + spll->reference_div = + RREG32_PLL(RADEON_M_SPLL_REF_FB_DIV) & + RADEON_M_SPLL_REF_DIV_MASK; + } + if (mpll->reference_div < 2) + mpll->reference_div = spll->reference_div; + } else { + if (ASIC_IS_AVIVO(rdev)) { + /* TODO FALLBACK */ + } else { + DRM_INFO("Using generic clock info\n"); + + /* may need to be per card */ + rdev->clock.max_pixel_clock = 35000; + + if (rdev->flags & RADEON_IS_IGP) { + p1pll->reference_freq = 1432; + p2pll->reference_freq = 1432; + spll->reference_freq = 1432; + mpll->reference_freq = 1432; + } else { + p1pll->reference_freq = 2700; + p2pll->reference_freq = 2700; + spll->reference_freq = 2700; + mpll->reference_freq = 2700; + } + p1pll->reference_div = + RREG32_PLL(RADEON_PPLL_REF_DIV) & 0x3ff; + if (p1pll->reference_div < 2) + p1pll->reference_div = 12; + p2pll->reference_div = p1pll->reference_div; + + if (rdev->family >= CHIP_R420) { + p1pll->pll_in_min = 100; + p1pll->pll_in_max = 1350; + p1pll->pll_out_min = 20000; + p1pll->pll_out_max = 50000; + p2pll->pll_in_min = 100; + p2pll->pll_in_max = 1350; + p2pll->pll_out_min = 20000; + p2pll->pll_out_max = 50000; + } else { + p1pll->pll_in_min = 40; + p1pll->pll_in_max = 500; + p1pll->pll_out_min = 12500; + p1pll->pll_out_max = 35000; + p2pll->pll_in_min = 40; + p2pll->pll_in_max = 500; + p2pll->pll_out_min = 12500; + p2pll->pll_out_max = 35000; + } + + spll->reference_div = + RREG32_PLL(RADEON_M_SPLL_REF_FB_DIV) & + RADEON_M_SPLL_REF_DIV_MASK; + mpll->reference_div = spll->reference_div; + rdev->clock.default_sclk = + radeon_legacy_get_engine_clock(rdev); + rdev->clock.default_mclk = + radeon_legacy_get_memory_clock(rdev); + } + } + + /* pixel clocks */ + if (ASIC_IS_AVIVO(rdev)) { + p1pll->min_post_div = 2; + p1pll->max_post_div = 0x7f; + p1pll->min_frac_feedback_div = 0; + p1pll->max_frac_feedback_div = 9; + p2pll->min_post_div = 2; + p2pll->max_post_div = 0x7f; + p2pll->min_frac_feedback_div = 0; + p2pll->max_frac_feedback_div = 9; + } else { + p1pll->min_post_div = 1; + p1pll->max_post_div = 16; + p1pll->min_frac_feedback_div = 0; + p1pll->max_frac_feedback_div = 0; + p2pll->min_post_div = 1; + p2pll->max_post_div = 12; + p2pll->min_frac_feedback_div = 0; + p2pll->max_frac_feedback_div = 0; + } + + /* dcpll is DCE4 only */ + dcpll->min_post_div = 2; + dcpll->max_post_div = 0x7f; + dcpll->min_frac_feedback_div = 0; + dcpll->max_frac_feedback_div = 9; + dcpll->min_ref_div = 2; + dcpll->max_ref_div = 0x3ff; + dcpll->min_feedback_div = 4; + dcpll->max_feedback_div = 0xfff; + dcpll->best_vco = 0; + + p1pll->min_ref_div = 2; + p1pll->max_ref_div = 0x3ff; + p1pll->min_feedback_div = 4; + p1pll->max_feedback_div = 0x7ff; + p1pll->best_vco = 0; + + p2pll->min_ref_div = 2; + p2pll->max_ref_div = 0x3ff; + p2pll->min_feedback_div = 4; + p2pll->max_feedback_div = 0x7ff; + p2pll->best_vco = 0; + + /* system clock */ + spll->min_post_div = 1; + spll->max_post_div = 1; + spll->min_ref_div = 2; + spll->max_ref_div = 0xff; + spll->min_feedback_div = 4; + spll->max_feedback_div = 0xff; + spll->best_vco = 0; + + /* memory clock */ + mpll->min_post_div = 1; + mpll->max_post_div = 1; + mpll->min_ref_div = 2; + mpll->max_ref_div = 0xff; + mpll->min_feedback_div = 4; + mpll->max_feedback_div = 0xff; + mpll->best_vco = 0; + + if (!rdev->clock.default_sclk) + rdev->clock.default_sclk = radeon_get_engine_clock(rdev); + if ((!rdev->clock.default_mclk) && rdev->asic->pm.get_memory_clock) + rdev->clock.default_mclk = radeon_get_memory_clock(rdev); + + rdev->pm.current_sclk = rdev->clock.default_sclk; + rdev->pm.current_mclk = rdev->clock.default_mclk; + +} + +/* 10 khz */ +static uint32_t calc_eng_mem_clock(struct radeon_device *rdev, + uint32_t req_clock, + int *fb_div, int *post_div) +{ + struct radeon_pll *spll = &rdev->clock.spll; + int ref_div = spll->reference_div; + + if (!ref_div) + ref_div = + RREG32_PLL(RADEON_M_SPLL_REF_FB_DIV) & + RADEON_M_SPLL_REF_DIV_MASK; + + if (req_clock < 15000) { + *post_div = 8; + req_clock *= 8; + } else if (req_clock < 30000) { + *post_div = 4; + req_clock *= 4; + } else if (req_clock < 60000) { + *post_div = 2; + req_clock *= 2; + } else + *post_div = 1; + + req_clock *= ref_div; + req_clock += spll->reference_freq; + req_clock /= (2 * spll->reference_freq); + + *fb_div = req_clock & 0xff; + + req_clock = (req_clock & 0xffff) << 1; + req_clock *= spll->reference_freq; + req_clock /= ref_div; + req_clock /= *post_div; + + return req_clock; +} + +/* 10 khz */ +void radeon_legacy_set_engine_clock(struct radeon_device *rdev, + uint32_t eng_clock) +{ + uint32_t tmp; + int fb_div, post_div; + + /* XXX: wait for idle */ + + eng_clock = calc_eng_mem_clock(rdev, eng_clock, &fb_div, &post_div); + + tmp = RREG32_PLL(RADEON_CLK_PIN_CNTL); + tmp &= ~RADEON_DONT_USE_XTALIN; + WREG32_PLL(RADEON_CLK_PIN_CNTL, tmp); + + tmp = RREG32_PLL(RADEON_SCLK_CNTL); + tmp &= ~RADEON_SCLK_SRC_SEL_MASK; + WREG32_PLL(RADEON_SCLK_CNTL, tmp); + + DRM_UDELAY(10); + + tmp = RREG32_PLL(RADEON_SPLL_CNTL); + tmp |= RADEON_SPLL_SLEEP; + WREG32_PLL(RADEON_SPLL_CNTL, tmp); + + DRM_UDELAY(2); + + tmp = RREG32_PLL(RADEON_SPLL_CNTL); + tmp |= RADEON_SPLL_RESET; + WREG32_PLL(RADEON_SPLL_CNTL, tmp); + + DRM_UDELAY(200); + + tmp = RREG32_PLL(RADEON_M_SPLL_REF_FB_DIV); + tmp &= ~(RADEON_SPLL_FB_DIV_MASK << RADEON_SPLL_FB_DIV_SHIFT); + tmp |= (fb_div & RADEON_SPLL_FB_DIV_MASK) << RADEON_SPLL_FB_DIV_SHIFT; + WREG32_PLL(RADEON_M_SPLL_REF_FB_DIV, tmp); + + /* XXX: verify on different asics */ + tmp = RREG32_PLL(RADEON_SPLL_CNTL); + tmp &= ~RADEON_SPLL_PVG_MASK; + if ((eng_clock * post_div) >= 90000) + tmp |= (0x7 << RADEON_SPLL_PVG_SHIFT); + else + tmp |= (0x4 << RADEON_SPLL_PVG_SHIFT); + WREG32_PLL(RADEON_SPLL_CNTL, tmp); + + tmp = RREG32_PLL(RADEON_SPLL_CNTL); + tmp &= ~RADEON_SPLL_SLEEP; + WREG32_PLL(RADEON_SPLL_CNTL, tmp); + + DRM_UDELAY(2); + + tmp = RREG32_PLL(RADEON_SPLL_CNTL); + tmp &= ~RADEON_SPLL_RESET; + WREG32_PLL(RADEON_SPLL_CNTL, tmp); + + DRM_UDELAY(200); + + tmp = RREG32_PLL(RADEON_SCLK_CNTL); + tmp &= ~RADEON_SCLK_SRC_SEL_MASK; + switch (post_div) { + case 1: + default: + tmp |= 1; + break; + case 2: + tmp |= 2; + break; + case 4: + tmp |= 3; + break; + case 8: + tmp |= 4; + break; + } + WREG32_PLL(RADEON_SCLK_CNTL, tmp); + + DRM_UDELAY(20); + + tmp = RREG32_PLL(RADEON_CLK_PIN_CNTL); + tmp |= RADEON_DONT_USE_XTALIN; + WREG32_PLL(RADEON_CLK_PIN_CNTL, tmp); + + DRM_UDELAY(10); +} + +void radeon_legacy_set_clock_gating(struct radeon_device *rdev, int enable) +{ + uint32_t tmp; + + if (enable) { + if (rdev->flags & RADEON_SINGLE_CRTC) { + tmp = RREG32_PLL(RADEON_SCLK_CNTL); + if ((RREG32(RADEON_CONFIG_CNTL) & + RADEON_CFG_ATI_REV_ID_MASK) > + RADEON_CFG_ATI_REV_A13) { + tmp &= + ~(RADEON_SCLK_FORCE_CP | + RADEON_SCLK_FORCE_RB); + } + tmp &= + ~(RADEON_SCLK_FORCE_HDP | RADEON_SCLK_FORCE_DISP1 | + RADEON_SCLK_FORCE_TOP | RADEON_SCLK_FORCE_SE | + RADEON_SCLK_FORCE_IDCT | RADEON_SCLK_FORCE_RE | + RADEON_SCLK_FORCE_PB | RADEON_SCLK_FORCE_TAM | + RADEON_SCLK_FORCE_TDM); + WREG32_PLL(RADEON_SCLK_CNTL, tmp); + } else if (ASIC_IS_R300(rdev)) { + if ((rdev->family == CHIP_RS400) || + (rdev->family == CHIP_RS480)) { + tmp = RREG32_PLL(RADEON_SCLK_CNTL); + tmp &= + ~(RADEON_SCLK_FORCE_DISP2 | + RADEON_SCLK_FORCE_CP | + RADEON_SCLK_FORCE_HDP | + RADEON_SCLK_FORCE_DISP1 | + RADEON_SCLK_FORCE_TOP | + RADEON_SCLK_FORCE_E2 | R300_SCLK_FORCE_VAP + | RADEON_SCLK_FORCE_IDCT | + RADEON_SCLK_FORCE_VIP | R300_SCLK_FORCE_SR + | R300_SCLK_FORCE_PX | R300_SCLK_FORCE_TX + | R300_SCLK_FORCE_US | + RADEON_SCLK_FORCE_TV_SCLK | + R300_SCLK_FORCE_SU | + RADEON_SCLK_FORCE_OV0); + tmp |= RADEON_DYN_STOP_LAT_MASK; + tmp |= + RADEON_SCLK_FORCE_TOP | + RADEON_SCLK_FORCE_VIP; + WREG32_PLL(RADEON_SCLK_CNTL, tmp); + + tmp = RREG32_PLL(RADEON_SCLK_MORE_CNTL); + tmp &= ~RADEON_SCLK_MORE_FORCEON; + tmp |= RADEON_SCLK_MORE_MAX_DYN_STOP_LAT; + WREG32_PLL(RADEON_SCLK_MORE_CNTL, tmp); + + tmp = RREG32_PLL(RADEON_VCLK_ECP_CNTL); + tmp |= (RADEON_PIXCLK_ALWAYS_ONb | + RADEON_PIXCLK_DAC_ALWAYS_ONb); + WREG32_PLL(RADEON_VCLK_ECP_CNTL, tmp); + + tmp = RREG32_PLL(RADEON_PIXCLKS_CNTL); + tmp |= (RADEON_PIX2CLK_ALWAYS_ONb | + RADEON_PIX2CLK_DAC_ALWAYS_ONb | + RADEON_DISP_TVOUT_PIXCLK_TV_ALWAYS_ONb | + R300_DVOCLK_ALWAYS_ONb | + RADEON_PIXCLK_BLEND_ALWAYS_ONb | + RADEON_PIXCLK_GV_ALWAYS_ONb | + R300_PIXCLK_DVO_ALWAYS_ONb | + RADEON_PIXCLK_LVDS_ALWAYS_ONb | + RADEON_PIXCLK_TMDS_ALWAYS_ONb | + R300_PIXCLK_TRANS_ALWAYS_ONb | + R300_PIXCLK_TVO_ALWAYS_ONb | + R300_P2G2CLK_ALWAYS_ONb | + R300_P2G2CLK_DAC_ALWAYS_ONb); + WREG32_PLL(RADEON_PIXCLKS_CNTL, tmp); + } else if (rdev->family >= CHIP_RV350) { + tmp = RREG32_PLL(R300_SCLK_CNTL2); + tmp &= ~(R300_SCLK_FORCE_TCL | + R300_SCLK_FORCE_GA | + R300_SCLK_FORCE_CBA); + tmp |= (R300_SCLK_TCL_MAX_DYN_STOP_LAT | + R300_SCLK_GA_MAX_DYN_STOP_LAT | + R300_SCLK_CBA_MAX_DYN_STOP_LAT); + WREG32_PLL(R300_SCLK_CNTL2, tmp); + + tmp = RREG32_PLL(RADEON_SCLK_CNTL); + tmp &= + ~(RADEON_SCLK_FORCE_DISP2 | + RADEON_SCLK_FORCE_CP | + RADEON_SCLK_FORCE_HDP | + RADEON_SCLK_FORCE_DISP1 | + RADEON_SCLK_FORCE_TOP | + RADEON_SCLK_FORCE_E2 | R300_SCLK_FORCE_VAP + | RADEON_SCLK_FORCE_IDCT | + RADEON_SCLK_FORCE_VIP | R300_SCLK_FORCE_SR + | R300_SCLK_FORCE_PX | R300_SCLK_FORCE_TX + | R300_SCLK_FORCE_US | + RADEON_SCLK_FORCE_TV_SCLK | + R300_SCLK_FORCE_SU | + RADEON_SCLK_FORCE_OV0); + tmp |= RADEON_DYN_STOP_LAT_MASK; + WREG32_PLL(RADEON_SCLK_CNTL, tmp); + + tmp = RREG32_PLL(RADEON_SCLK_MORE_CNTL); + tmp &= ~RADEON_SCLK_MORE_FORCEON; + tmp |= RADEON_SCLK_MORE_MAX_DYN_STOP_LAT; + WREG32_PLL(RADEON_SCLK_MORE_CNTL, tmp); + + tmp = RREG32_PLL(RADEON_VCLK_ECP_CNTL); + tmp |= (RADEON_PIXCLK_ALWAYS_ONb | + RADEON_PIXCLK_DAC_ALWAYS_ONb); + WREG32_PLL(RADEON_VCLK_ECP_CNTL, tmp); + + tmp = RREG32_PLL(RADEON_PIXCLKS_CNTL); + tmp |= (RADEON_PIX2CLK_ALWAYS_ONb | + RADEON_PIX2CLK_DAC_ALWAYS_ONb | + RADEON_DISP_TVOUT_PIXCLK_TV_ALWAYS_ONb | + R300_DVOCLK_ALWAYS_ONb | + RADEON_PIXCLK_BLEND_ALWAYS_ONb | + RADEON_PIXCLK_GV_ALWAYS_ONb | + R300_PIXCLK_DVO_ALWAYS_ONb | + RADEON_PIXCLK_LVDS_ALWAYS_ONb | + RADEON_PIXCLK_TMDS_ALWAYS_ONb | + R300_PIXCLK_TRANS_ALWAYS_ONb | + R300_PIXCLK_TVO_ALWAYS_ONb | + R300_P2G2CLK_ALWAYS_ONb | + R300_P2G2CLK_DAC_ALWAYS_ONb); + WREG32_PLL(RADEON_PIXCLKS_CNTL, tmp); + + tmp = RREG32_PLL(RADEON_MCLK_MISC); + tmp |= (RADEON_MC_MCLK_DYN_ENABLE | + RADEON_IO_MCLK_DYN_ENABLE); + WREG32_PLL(RADEON_MCLK_MISC, tmp); + + tmp = RREG32_PLL(RADEON_MCLK_CNTL); + tmp |= (RADEON_FORCEON_MCLKA | + RADEON_FORCEON_MCLKB); + + tmp &= ~(RADEON_FORCEON_YCLKA | + RADEON_FORCEON_YCLKB | + RADEON_FORCEON_MC); + + /* Some releases of vbios have set DISABLE_MC_MCLKA + and DISABLE_MC_MCLKB bits in the vbios table. Setting these + bits will cause H/W hang when reading video memory with dynamic clocking + enabled. */ + if ((tmp & R300_DISABLE_MC_MCLKA) && + (tmp & R300_DISABLE_MC_MCLKB)) { + /* If both bits are set, then check the active channels */ + tmp = RREG32_PLL(RADEON_MCLK_CNTL); + if (rdev->mc.vram_width == 64) { + if (RREG32(RADEON_MEM_CNTL) & + R300_MEM_USE_CD_CH_ONLY) + tmp &= + ~R300_DISABLE_MC_MCLKB; + else + tmp &= + ~R300_DISABLE_MC_MCLKA; + } else { + tmp &= ~(R300_DISABLE_MC_MCLKA | + R300_DISABLE_MC_MCLKB); + } + } + + WREG32_PLL(RADEON_MCLK_CNTL, tmp); + } else { + tmp = RREG32_PLL(RADEON_SCLK_CNTL); + tmp &= ~(R300_SCLK_FORCE_VAP); + tmp |= RADEON_SCLK_FORCE_CP; + WREG32_PLL(RADEON_SCLK_CNTL, tmp); + DRM_MDELAY(15); + + tmp = RREG32_PLL(R300_SCLK_CNTL2); + tmp &= ~(R300_SCLK_FORCE_TCL | + R300_SCLK_FORCE_GA | + R300_SCLK_FORCE_CBA); + WREG32_PLL(R300_SCLK_CNTL2, tmp); + } + } else { + tmp = RREG32_PLL(RADEON_CLK_PWRMGT_CNTL); + + tmp &= ~(RADEON_ACTIVE_HILO_LAT_MASK | + RADEON_DISP_DYN_STOP_LAT_MASK | + RADEON_DYN_STOP_MODE_MASK); + + tmp |= (RADEON_ENGIN_DYNCLK_MODE | + (0x01 << RADEON_ACTIVE_HILO_LAT_SHIFT)); + WREG32_PLL(RADEON_CLK_PWRMGT_CNTL, tmp); + DRM_MDELAY(15); + + tmp = RREG32_PLL(RADEON_CLK_PIN_CNTL); + tmp |= RADEON_SCLK_DYN_START_CNTL; + WREG32_PLL(RADEON_CLK_PIN_CNTL, tmp); + DRM_MDELAY(15); + + /* When DRI is enabled, setting DYN_STOP_LAT to zero can cause some R200 + to lockup randomly, leave them as set by BIOS. + */ + tmp = RREG32_PLL(RADEON_SCLK_CNTL); + /*tmp &= RADEON_SCLK_SRC_SEL_MASK; */ + tmp &= ~RADEON_SCLK_FORCEON_MASK; + + /*RAGE_6::A11 A12 A12N1 A13, RV250::A11 A12, R300 */ + if (((rdev->family == CHIP_RV250) && + ((RREG32(RADEON_CONFIG_CNTL) & + RADEON_CFG_ATI_REV_ID_MASK) < + RADEON_CFG_ATI_REV_A13)) + || ((rdev->family == CHIP_RV100) + && + ((RREG32(RADEON_CONFIG_CNTL) & + RADEON_CFG_ATI_REV_ID_MASK) <= + RADEON_CFG_ATI_REV_A13))) { + tmp |= RADEON_SCLK_FORCE_CP; + tmp |= RADEON_SCLK_FORCE_VIP; + } + + WREG32_PLL(RADEON_SCLK_CNTL, tmp); + + if ((rdev->family == CHIP_RV200) || + (rdev->family == CHIP_RV250) || + (rdev->family == CHIP_RV280)) { + tmp = RREG32_PLL(RADEON_SCLK_MORE_CNTL); + tmp &= ~RADEON_SCLK_MORE_FORCEON; + + /* RV200::A11 A12 RV250::A11 A12 */ + if (((rdev->family == CHIP_RV200) || + (rdev->family == CHIP_RV250)) && + ((RREG32(RADEON_CONFIG_CNTL) & + RADEON_CFG_ATI_REV_ID_MASK) < + RADEON_CFG_ATI_REV_A13)) { + tmp |= RADEON_SCLK_MORE_FORCEON; + } + WREG32_PLL(RADEON_SCLK_MORE_CNTL, tmp); + DRM_MDELAY(15); + } + + /* RV200::A11 A12, RV250::A11 A12 */ + if (((rdev->family == CHIP_RV200) || + (rdev->family == CHIP_RV250)) && + ((RREG32(RADEON_CONFIG_CNTL) & + RADEON_CFG_ATI_REV_ID_MASK) < + RADEON_CFG_ATI_REV_A13)) { + tmp = RREG32_PLL(RADEON_PLL_PWRMGT_CNTL); + tmp |= RADEON_TCL_BYPASS_DISABLE; + WREG32_PLL(RADEON_PLL_PWRMGT_CNTL, tmp); + } + DRM_MDELAY(15); + + /*enable dynamic mode for display clocks (PIXCLK and PIX2CLK) */ + tmp = RREG32_PLL(RADEON_PIXCLKS_CNTL); + tmp |= (RADEON_PIX2CLK_ALWAYS_ONb | + RADEON_PIX2CLK_DAC_ALWAYS_ONb | + RADEON_PIXCLK_BLEND_ALWAYS_ONb | + RADEON_PIXCLK_GV_ALWAYS_ONb | + RADEON_PIXCLK_DIG_TMDS_ALWAYS_ONb | + RADEON_PIXCLK_LVDS_ALWAYS_ONb | + RADEON_PIXCLK_TMDS_ALWAYS_ONb); + + WREG32_PLL(RADEON_PIXCLKS_CNTL, tmp); + DRM_MDELAY(15); + + tmp = RREG32_PLL(RADEON_VCLK_ECP_CNTL); + tmp |= (RADEON_PIXCLK_ALWAYS_ONb | + RADEON_PIXCLK_DAC_ALWAYS_ONb); + + WREG32_PLL(RADEON_VCLK_ECP_CNTL, tmp); + DRM_MDELAY(15); + } + } else { + /* Turn everything OFF (ForceON to everything) */ + if (rdev->flags & RADEON_SINGLE_CRTC) { + tmp = RREG32_PLL(RADEON_SCLK_CNTL); + tmp |= (RADEON_SCLK_FORCE_CP | RADEON_SCLK_FORCE_HDP | + RADEON_SCLK_FORCE_DISP1 | RADEON_SCLK_FORCE_TOP + | RADEON_SCLK_FORCE_E2 | RADEON_SCLK_FORCE_SE | + RADEON_SCLK_FORCE_IDCT | RADEON_SCLK_FORCE_VIP | + RADEON_SCLK_FORCE_RE | RADEON_SCLK_FORCE_PB | + RADEON_SCLK_FORCE_TAM | RADEON_SCLK_FORCE_TDM | + RADEON_SCLK_FORCE_RB); + WREG32_PLL(RADEON_SCLK_CNTL, tmp); + } else if ((rdev->family == CHIP_RS400) || + (rdev->family == CHIP_RS480)) { + tmp = RREG32_PLL(RADEON_SCLK_CNTL); + tmp |= (RADEON_SCLK_FORCE_DISP2 | RADEON_SCLK_FORCE_CP | + RADEON_SCLK_FORCE_HDP | RADEON_SCLK_FORCE_DISP1 + | RADEON_SCLK_FORCE_TOP | RADEON_SCLK_FORCE_E2 | + R300_SCLK_FORCE_VAP | RADEON_SCLK_FORCE_IDCT | + RADEON_SCLK_FORCE_VIP | R300_SCLK_FORCE_SR | + R300_SCLK_FORCE_PX | R300_SCLK_FORCE_TX | + R300_SCLK_FORCE_US | RADEON_SCLK_FORCE_TV_SCLK | + R300_SCLK_FORCE_SU | RADEON_SCLK_FORCE_OV0); + WREG32_PLL(RADEON_SCLK_CNTL, tmp); + + tmp = RREG32_PLL(RADEON_SCLK_MORE_CNTL); + tmp |= RADEON_SCLK_MORE_FORCEON; + WREG32_PLL(RADEON_SCLK_MORE_CNTL, tmp); + + tmp = RREG32_PLL(RADEON_VCLK_ECP_CNTL); + tmp &= ~(RADEON_PIXCLK_ALWAYS_ONb | + RADEON_PIXCLK_DAC_ALWAYS_ONb | + R300_DISP_DAC_PIXCLK_DAC_BLANK_OFF); + WREG32_PLL(RADEON_VCLK_ECP_CNTL, tmp); + + tmp = RREG32_PLL(RADEON_PIXCLKS_CNTL); + tmp &= ~(RADEON_PIX2CLK_ALWAYS_ONb | + RADEON_PIX2CLK_DAC_ALWAYS_ONb | + RADEON_DISP_TVOUT_PIXCLK_TV_ALWAYS_ONb | + R300_DVOCLK_ALWAYS_ONb | + RADEON_PIXCLK_BLEND_ALWAYS_ONb | + RADEON_PIXCLK_GV_ALWAYS_ONb | + R300_PIXCLK_DVO_ALWAYS_ONb | + RADEON_PIXCLK_LVDS_ALWAYS_ONb | + RADEON_PIXCLK_TMDS_ALWAYS_ONb | + R300_PIXCLK_TRANS_ALWAYS_ONb | + R300_PIXCLK_TVO_ALWAYS_ONb | + R300_P2G2CLK_ALWAYS_ONb | + R300_P2G2CLK_DAC_ALWAYS_ONb | + R300_DISP_DAC_PIXCLK_DAC2_BLANK_OFF); + WREG32_PLL(RADEON_PIXCLKS_CNTL, tmp); + } else if (rdev->family >= CHIP_RV350) { + /* for RV350/M10, no delays are required. */ + tmp = RREG32_PLL(R300_SCLK_CNTL2); + tmp |= (R300_SCLK_FORCE_TCL | + R300_SCLK_FORCE_GA | R300_SCLK_FORCE_CBA); + WREG32_PLL(R300_SCLK_CNTL2, tmp); + + tmp = RREG32_PLL(RADEON_SCLK_CNTL); + tmp |= (RADEON_SCLK_FORCE_DISP2 | RADEON_SCLK_FORCE_CP | + RADEON_SCLK_FORCE_HDP | RADEON_SCLK_FORCE_DISP1 + | RADEON_SCLK_FORCE_TOP | RADEON_SCLK_FORCE_E2 | + R300_SCLK_FORCE_VAP | RADEON_SCLK_FORCE_IDCT | + RADEON_SCLK_FORCE_VIP | R300_SCLK_FORCE_SR | + R300_SCLK_FORCE_PX | R300_SCLK_FORCE_TX | + R300_SCLK_FORCE_US | RADEON_SCLK_FORCE_TV_SCLK | + R300_SCLK_FORCE_SU | RADEON_SCLK_FORCE_OV0); + WREG32_PLL(RADEON_SCLK_CNTL, tmp); + + tmp = RREG32_PLL(RADEON_SCLK_MORE_CNTL); + tmp |= RADEON_SCLK_MORE_FORCEON; + WREG32_PLL(RADEON_SCLK_MORE_CNTL, tmp); + + tmp = RREG32_PLL(RADEON_MCLK_CNTL); + tmp |= (RADEON_FORCEON_MCLKA | + RADEON_FORCEON_MCLKB | + RADEON_FORCEON_YCLKA | + RADEON_FORCEON_YCLKB | RADEON_FORCEON_MC); + WREG32_PLL(RADEON_MCLK_CNTL, tmp); + + tmp = RREG32_PLL(RADEON_VCLK_ECP_CNTL); + tmp &= ~(RADEON_PIXCLK_ALWAYS_ONb | + RADEON_PIXCLK_DAC_ALWAYS_ONb | + R300_DISP_DAC_PIXCLK_DAC_BLANK_OFF); + WREG32_PLL(RADEON_VCLK_ECP_CNTL, tmp); + + tmp = RREG32_PLL(RADEON_PIXCLKS_CNTL); + tmp &= ~(RADEON_PIX2CLK_ALWAYS_ONb | + RADEON_PIX2CLK_DAC_ALWAYS_ONb | + RADEON_DISP_TVOUT_PIXCLK_TV_ALWAYS_ONb | + R300_DVOCLK_ALWAYS_ONb | + RADEON_PIXCLK_BLEND_ALWAYS_ONb | + RADEON_PIXCLK_GV_ALWAYS_ONb | + R300_PIXCLK_DVO_ALWAYS_ONb | + RADEON_PIXCLK_LVDS_ALWAYS_ONb | + RADEON_PIXCLK_TMDS_ALWAYS_ONb | + R300_PIXCLK_TRANS_ALWAYS_ONb | + R300_PIXCLK_TVO_ALWAYS_ONb | + R300_P2G2CLK_ALWAYS_ONb | + R300_P2G2CLK_DAC_ALWAYS_ONb | + R300_DISP_DAC_PIXCLK_DAC2_BLANK_OFF); + WREG32_PLL(RADEON_PIXCLKS_CNTL, tmp); + } else { + tmp = RREG32_PLL(RADEON_SCLK_CNTL); + tmp |= (RADEON_SCLK_FORCE_CP | RADEON_SCLK_FORCE_E2); + tmp |= RADEON_SCLK_FORCE_SE; + + if (rdev->flags & RADEON_SINGLE_CRTC) { + tmp |= (RADEON_SCLK_FORCE_RB | + RADEON_SCLK_FORCE_TDM | + RADEON_SCLK_FORCE_TAM | + RADEON_SCLK_FORCE_PB | + RADEON_SCLK_FORCE_RE | + RADEON_SCLK_FORCE_VIP | + RADEON_SCLK_FORCE_IDCT | + RADEON_SCLK_FORCE_TOP | + RADEON_SCLK_FORCE_DISP1 | + RADEON_SCLK_FORCE_DISP2 | + RADEON_SCLK_FORCE_HDP); + } else if ((rdev->family == CHIP_R300) || + (rdev->family == CHIP_R350)) { + tmp |= (RADEON_SCLK_FORCE_HDP | + RADEON_SCLK_FORCE_DISP1 | + RADEON_SCLK_FORCE_DISP2 | + RADEON_SCLK_FORCE_TOP | + RADEON_SCLK_FORCE_IDCT | + RADEON_SCLK_FORCE_VIP); + } + WREG32_PLL(RADEON_SCLK_CNTL, tmp); + + DRM_MDELAY(16); + + if ((rdev->family == CHIP_R300) || + (rdev->family == CHIP_R350)) { + tmp = RREG32_PLL(R300_SCLK_CNTL2); + tmp |= (R300_SCLK_FORCE_TCL | + R300_SCLK_FORCE_GA | + R300_SCLK_FORCE_CBA); + WREG32_PLL(R300_SCLK_CNTL2, tmp); + DRM_MDELAY(16); + } + + if (rdev->flags & RADEON_IS_IGP) { + tmp = RREG32_PLL(RADEON_MCLK_CNTL); + tmp &= ~(RADEON_FORCEON_MCLKA | + RADEON_FORCEON_YCLKA); + WREG32_PLL(RADEON_MCLK_CNTL, tmp); + DRM_MDELAY(16); + } + + if ((rdev->family == CHIP_RV200) || + (rdev->family == CHIP_RV250) || + (rdev->family == CHIP_RV280)) { + tmp = RREG32_PLL(RADEON_SCLK_MORE_CNTL); + tmp |= RADEON_SCLK_MORE_FORCEON; + WREG32_PLL(RADEON_SCLK_MORE_CNTL, tmp); + DRM_MDELAY(16); + } + + tmp = RREG32_PLL(RADEON_PIXCLKS_CNTL); + tmp &= ~(RADEON_PIX2CLK_ALWAYS_ONb | + RADEON_PIX2CLK_DAC_ALWAYS_ONb | + RADEON_PIXCLK_BLEND_ALWAYS_ONb | + RADEON_PIXCLK_GV_ALWAYS_ONb | + RADEON_PIXCLK_DIG_TMDS_ALWAYS_ONb | + RADEON_PIXCLK_LVDS_ALWAYS_ONb | + RADEON_PIXCLK_TMDS_ALWAYS_ONb); + + WREG32_PLL(RADEON_PIXCLKS_CNTL, tmp); + DRM_MDELAY(16); + + tmp = RREG32_PLL(RADEON_VCLK_ECP_CNTL); + tmp &= ~(RADEON_PIXCLK_ALWAYS_ONb | + RADEON_PIXCLK_DAC_ALWAYS_ONb); + WREG32_PLL(RADEON_VCLK_ECP_CNTL, tmp); + } + } +} + diff --git a/sys/dev/drm2/radeon/radeon_combios.c b/sys/dev/drm2/radeon/radeon_combios.c new file mode 100644 index 00000000000..caa8a003589 --- /dev/null +++ b/sys/dev/drm2/radeon/radeon_combios.c @@ -0,0 +1,3661 @@ +/* + * Copyright 2004 ATI Technologies Inc., Markham, Ontario + * Copyright 2007-8 Advanced Micro Devices, Inc. + * Copyright 2008 Red Hat Inc. + * + * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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. + * + * Authors: Dave Airlie + * Alex Deucher + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include "radeon.h" +#include "atom.h" + +#ifdef CONFIG_PPC_PMAC +/* not sure which of these are needed */ +#include +#include +#include +#include +#endif /* CONFIG_PPC_PMAC */ + +/* old legacy ATI BIOS routines */ + +/* COMBIOS table offsets */ +enum radeon_combios_table_offset { + /* absolute offset tables */ + COMBIOS_ASIC_INIT_1_TABLE, + COMBIOS_BIOS_SUPPORT_TABLE, + COMBIOS_DAC_PROGRAMMING_TABLE, + COMBIOS_MAX_COLOR_DEPTH_TABLE, + COMBIOS_CRTC_INFO_TABLE, + COMBIOS_PLL_INFO_TABLE, + COMBIOS_TV_INFO_TABLE, + COMBIOS_DFP_INFO_TABLE, + COMBIOS_HW_CONFIG_INFO_TABLE, + COMBIOS_MULTIMEDIA_INFO_TABLE, + COMBIOS_TV_STD_PATCH_TABLE, + COMBIOS_LCD_INFO_TABLE, + COMBIOS_MOBILE_INFO_TABLE, + COMBIOS_PLL_INIT_TABLE, + COMBIOS_MEM_CONFIG_TABLE, + COMBIOS_SAVE_MASK_TABLE, + COMBIOS_HARDCODED_EDID_TABLE, + COMBIOS_ASIC_INIT_2_TABLE, + COMBIOS_CONNECTOR_INFO_TABLE, + COMBIOS_DYN_CLK_1_TABLE, + COMBIOS_RESERVED_MEM_TABLE, + COMBIOS_EXT_TMDS_INFO_TABLE, + COMBIOS_MEM_CLK_INFO_TABLE, + COMBIOS_EXT_DAC_INFO_TABLE, + COMBIOS_MISC_INFO_TABLE, + COMBIOS_CRT_INFO_TABLE, + COMBIOS_INTEGRATED_SYSTEM_INFO_TABLE, + COMBIOS_COMPONENT_VIDEO_INFO_TABLE, + COMBIOS_FAN_SPEED_INFO_TABLE, + COMBIOS_OVERDRIVE_INFO_TABLE, + COMBIOS_OEM_INFO_TABLE, + COMBIOS_DYN_CLK_2_TABLE, + COMBIOS_POWER_CONNECTOR_INFO_TABLE, + COMBIOS_I2C_INFO_TABLE, + /* relative offset tables */ + COMBIOS_ASIC_INIT_3_TABLE, /* offset from misc info */ + COMBIOS_ASIC_INIT_4_TABLE, /* offset from misc info */ + COMBIOS_DETECTED_MEM_TABLE, /* offset from misc info */ + COMBIOS_ASIC_INIT_5_TABLE, /* offset from misc info */ + COMBIOS_RAM_RESET_TABLE, /* offset from mem config */ + COMBIOS_POWERPLAY_INFO_TABLE, /* offset from mobile info */ + COMBIOS_GPIO_INFO_TABLE, /* offset from mobile info */ + COMBIOS_LCD_DDC_INFO_TABLE, /* offset from mobile info */ + COMBIOS_TMDS_POWER_TABLE, /* offset from mobile info */ + COMBIOS_TMDS_POWER_ON_TABLE, /* offset from tmds power */ + COMBIOS_TMDS_POWER_OFF_TABLE, /* offset from tmds power */ +}; + +enum radeon_combios_ddc { + DDC_NONE_DETECTED, + DDC_MONID, + DDC_DVI, + DDC_VGA, + DDC_CRT2, + DDC_LCD, + DDC_GPIO, +}; + +enum radeon_combios_connector { + CONNECTOR_NONE_LEGACY, + CONNECTOR_PROPRIETARY_LEGACY, + CONNECTOR_CRT_LEGACY, + CONNECTOR_DVI_I_LEGACY, + CONNECTOR_DVI_D_LEGACY, + CONNECTOR_CTV_LEGACY, + CONNECTOR_STV_LEGACY, + CONNECTOR_UNSUPPORTED_LEGACY +}; + +const int legacy_connector_convert[] = { + DRM_MODE_CONNECTOR_Unknown, + DRM_MODE_CONNECTOR_DVID, + DRM_MODE_CONNECTOR_VGA, + DRM_MODE_CONNECTOR_DVII, + DRM_MODE_CONNECTOR_DVID, + DRM_MODE_CONNECTOR_Composite, + DRM_MODE_CONNECTOR_SVIDEO, + DRM_MODE_CONNECTOR_Unknown, +}; + +static uint16_t combios_get_table_offset(struct drm_device *dev, + enum radeon_combios_table_offset table) +{ + struct radeon_device *rdev = dev->dev_private; + int rev; + uint16_t offset = 0, check_offset; + + if (!rdev->bios) + return 0; + + switch (table) { + /* absolute offset tables */ + case COMBIOS_ASIC_INIT_1_TABLE: + check_offset = RBIOS16(rdev->bios_header_start + 0xc); + if (check_offset) + offset = check_offset; + break; + case COMBIOS_BIOS_SUPPORT_TABLE: + check_offset = RBIOS16(rdev->bios_header_start + 0x14); + if (check_offset) + offset = check_offset; + break; + case COMBIOS_DAC_PROGRAMMING_TABLE: + check_offset = RBIOS16(rdev->bios_header_start + 0x2a); + if (check_offset) + offset = check_offset; + break; + case COMBIOS_MAX_COLOR_DEPTH_TABLE: + check_offset = RBIOS16(rdev->bios_header_start + 0x2c); + if (check_offset) + offset = check_offset; + break; + case COMBIOS_CRTC_INFO_TABLE: + check_offset = RBIOS16(rdev->bios_header_start + 0x2e); + if (check_offset) + offset = check_offset; + break; + case COMBIOS_PLL_INFO_TABLE: + check_offset = RBIOS16(rdev->bios_header_start + 0x30); + if (check_offset) + offset = check_offset; + break; + case COMBIOS_TV_INFO_TABLE: + check_offset = RBIOS16(rdev->bios_header_start + 0x32); + if (check_offset) + offset = check_offset; + break; + case COMBIOS_DFP_INFO_TABLE: + check_offset = RBIOS16(rdev->bios_header_start + 0x34); + if (check_offset) + offset = check_offset; + break; + case COMBIOS_HW_CONFIG_INFO_TABLE: + check_offset = RBIOS16(rdev->bios_header_start + 0x36); + if (check_offset) + offset = check_offset; + break; + case COMBIOS_MULTIMEDIA_INFO_TABLE: + check_offset = RBIOS16(rdev->bios_header_start + 0x38); + if (check_offset) + offset = check_offset; + break; + case COMBIOS_TV_STD_PATCH_TABLE: + check_offset = RBIOS16(rdev->bios_header_start + 0x3e); + if (check_offset) + offset = check_offset; + break; + case COMBIOS_LCD_INFO_TABLE: + check_offset = RBIOS16(rdev->bios_header_start + 0x40); + if (check_offset) + offset = check_offset; + break; + case COMBIOS_MOBILE_INFO_TABLE: + check_offset = RBIOS16(rdev->bios_header_start + 0x42); + if (check_offset) + offset = check_offset; + break; + case COMBIOS_PLL_INIT_TABLE: + check_offset = RBIOS16(rdev->bios_header_start + 0x46); + if (check_offset) + offset = check_offset; + break; + case COMBIOS_MEM_CONFIG_TABLE: + check_offset = RBIOS16(rdev->bios_header_start + 0x48); + if (check_offset) + offset = check_offset; + break; + case COMBIOS_SAVE_MASK_TABLE: + check_offset = RBIOS16(rdev->bios_header_start + 0x4a); + if (check_offset) + offset = check_offset; + break; + case COMBIOS_HARDCODED_EDID_TABLE: + check_offset = RBIOS16(rdev->bios_header_start + 0x4c); + if (check_offset) + offset = check_offset; + break; + case COMBIOS_ASIC_INIT_2_TABLE: + check_offset = RBIOS16(rdev->bios_header_start + 0x4e); + if (check_offset) + offset = check_offset; + break; + case COMBIOS_CONNECTOR_INFO_TABLE: + check_offset = RBIOS16(rdev->bios_header_start + 0x50); + if (check_offset) + offset = check_offset; + break; + case COMBIOS_DYN_CLK_1_TABLE: + check_offset = RBIOS16(rdev->bios_header_start + 0x52); + if (check_offset) + offset = check_offset; + break; + case COMBIOS_RESERVED_MEM_TABLE: + check_offset = RBIOS16(rdev->bios_header_start + 0x54); + if (check_offset) + offset = check_offset; + break; + case COMBIOS_EXT_TMDS_INFO_TABLE: + check_offset = RBIOS16(rdev->bios_header_start + 0x58); + if (check_offset) + offset = check_offset; + break; + case COMBIOS_MEM_CLK_INFO_TABLE: + check_offset = RBIOS16(rdev->bios_header_start + 0x5a); + if (check_offset) + offset = check_offset; + break; + case COMBIOS_EXT_DAC_INFO_TABLE: + check_offset = RBIOS16(rdev->bios_header_start + 0x5c); + if (check_offset) + offset = check_offset; + break; + case COMBIOS_MISC_INFO_TABLE: + check_offset = RBIOS16(rdev->bios_header_start + 0x5e); + if (check_offset) + offset = check_offset; + break; + case COMBIOS_CRT_INFO_TABLE: + check_offset = RBIOS16(rdev->bios_header_start + 0x60); + if (check_offset) + offset = check_offset; + break; + case COMBIOS_INTEGRATED_SYSTEM_INFO_TABLE: + check_offset = RBIOS16(rdev->bios_header_start + 0x62); + if (check_offset) + offset = check_offset; + break; + case COMBIOS_COMPONENT_VIDEO_INFO_TABLE: + check_offset = RBIOS16(rdev->bios_header_start + 0x64); + if (check_offset) + offset = check_offset; + break; + case COMBIOS_FAN_SPEED_INFO_TABLE: + check_offset = RBIOS16(rdev->bios_header_start + 0x66); + if (check_offset) + offset = check_offset; + break; + case COMBIOS_OVERDRIVE_INFO_TABLE: + check_offset = RBIOS16(rdev->bios_header_start + 0x68); + if (check_offset) + offset = check_offset; + break; + case COMBIOS_OEM_INFO_TABLE: + check_offset = RBIOS16(rdev->bios_header_start + 0x6a); + if (check_offset) + offset = check_offset; + break; + case COMBIOS_DYN_CLK_2_TABLE: + check_offset = RBIOS16(rdev->bios_header_start + 0x6c); + if (check_offset) + offset = check_offset; + break; + case COMBIOS_POWER_CONNECTOR_INFO_TABLE: + check_offset = RBIOS16(rdev->bios_header_start + 0x6e); + if (check_offset) + offset = check_offset; + break; + case COMBIOS_I2C_INFO_TABLE: + check_offset = RBIOS16(rdev->bios_header_start + 0x70); + if (check_offset) + offset = check_offset; + break; + /* relative offset tables */ + case COMBIOS_ASIC_INIT_3_TABLE: /* offset from misc info */ + check_offset = + combios_get_table_offset(dev, COMBIOS_MISC_INFO_TABLE); + if (check_offset) { + rev = RBIOS8(check_offset); + if (rev > 0) { + check_offset = RBIOS16(check_offset + 0x3); + if (check_offset) + offset = check_offset; + } + } + break; + case COMBIOS_ASIC_INIT_4_TABLE: /* offset from misc info */ + check_offset = + combios_get_table_offset(dev, COMBIOS_MISC_INFO_TABLE); + if (check_offset) { + rev = RBIOS8(check_offset); + if (rev > 0) { + check_offset = RBIOS16(check_offset + 0x5); + if (check_offset) + offset = check_offset; + } + } + break; + case COMBIOS_DETECTED_MEM_TABLE: /* offset from misc info */ + check_offset = + combios_get_table_offset(dev, COMBIOS_MISC_INFO_TABLE); + if (check_offset) { + rev = RBIOS8(check_offset); + if (rev > 0) { + check_offset = RBIOS16(check_offset + 0x7); + if (check_offset) + offset = check_offset; + } + } + break; + case COMBIOS_ASIC_INIT_5_TABLE: /* offset from misc info */ + check_offset = + combios_get_table_offset(dev, COMBIOS_MISC_INFO_TABLE); + if (check_offset) { + rev = RBIOS8(check_offset); + if (rev == 2) { + check_offset = RBIOS16(check_offset + 0x9); + if (check_offset) + offset = check_offset; + } + } + break; + case COMBIOS_RAM_RESET_TABLE: /* offset from mem config */ + check_offset = + combios_get_table_offset(dev, COMBIOS_MEM_CONFIG_TABLE); + if (check_offset) { + while (RBIOS8(check_offset++)); + check_offset += 2; + if (check_offset) + offset = check_offset; + } + break; + case COMBIOS_POWERPLAY_INFO_TABLE: /* offset from mobile info */ + check_offset = + combios_get_table_offset(dev, COMBIOS_MOBILE_INFO_TABLE); + if (check_offset) { + check_offset = RBIOS16(check_offset + 0x11); + if (check_offset) + offset = check_offset; + } + break; + case COMBIOS_GPIO_INFO_TABLE: /* offset from mobile info */ + check_offset = + combios_get_table_offset(dev, COMBIOS_MOBILE_INFO_TABLE); + if (check_offset) { + check_offset = RBIOS16(check_offset + 0x13); + if (check_offset) + offset = check_offset; + } + break; + case COMBIOS_LCD_DDC_INFO_TABLE: /* offset from mobile info */ + check_offset = + combios_get_table_offset(dev, COMBIOS_MOBILE_INFO_TABLE); + if (check_offset) { + check_offset = RBIOS16(check_offset + 0x15); + if (check_offset) + offset = check_offset; + } + break; + case COMBIOS_TMDS_POWER_TABLE: /* offset from mobile info */ + check_offset = + combios_get_table_offset(dev, COMBIOS_MOBILE_INFO_TABLE); + if (check_offset) { + check_offset = RBIOS16(check_offset + 0x17); + if (check_offset) + offset = check_offset; + } + break; + case COMBIOS_TMDS_POWER_ON_TABLE: /* offset from tmds power */ + check_offset = + combios_get_table_offset(dev, COMBIOS_TMDS_POWER_TABLE); + if (check_offset) { + check_offset = RBIOS16(check_offset + 0x2); + if (check_offset) + offset = check_offset; + } + break; + case COMBIOS_TMDS_POWER_OFF_TABLE: /* offset from tmds power */ + check_offset = + combios_get_table_offset(dev, COMBIOS_TMDS_POWER_TABLE); + if (check_offset) { + check_offset = RBIOS16(check_offset + 0x4); + if (check_offset) + offset = check_offset; + } + break; + default: + break; + } + + return offset; + +} + +bool radeon_combios_check_hardcoded_edid(struct radeon_device *rdev) +{ + int edid_info, size; + struct edid *edid; + unsigned char *raw; + edid_info = combios_get_table_offset(rdev->ddev, COMBIOS_HARDCODED_EDID_TABLE); + if (!edid_info) + return false; + + raw = rdev->bios + edid_info; + size = EDID_LENGTH * (raw[0x7e] + 1); + edid = malloc(size, DRM_MEM_KMS, M_WAITOK); + if (edid == NULL) + return false; + + memcpy((unsigned char *)edid, raw, size); + + if (!drm_edid_is_valid(edid)) { + free(edid, DRM_MEM_KMS); + return false; + } + + rdev->mode_info.bios_hardcoded_edid = edid; + rdev->mode_info.bios_hardcoded_edid_size = size; + return true; +} + +/* this is used for atom LCDs as well */ +struct edid * +radeon_bios_get_hardcoded_edid(struct radeon_device *rdev) +{ + struct edid *edid; + + if (rdev->mode_info.bios_hardcoded_edid) { + edid = malloc(rdev->mode_info.bios_hardcoded_edid_size, + DRM_MEM_KMS, M_WAITOK); + if (edid) { + memcpy((unsigned char *)edid, + (unsigned char *)rdev->mode_info.bios_hardcoded_edid, + rdev->mode_info.bios_hardcoded_edid_size); + return edid; + } + } + return NULL; +} + +static struct radeon_i2c_bus_rec combios_setup_i2c_bus(struct radeon_device *rdev, + enum radeon_combios_ddc ddc, + u32 clk_mask, + u32 data_mask) +{ + struct radeon_i2c_bus_rec i2c; + int ddc_line = 0; + + /* ddc id = mask reg + * DDC_NONE_DETECTED = none + * DDC_DVI = RADEON_GPIO_DVI_DDC + * DDC_VGA = RADEON_GPIO_VGA_DDC + * DDC_LCD = RADEON_GPIOPAD_MASK + * DDC_GPIO = RADEON_MDGPIO_MASK + * r1xx + * DDC_MONID = RADEON_GPIO_MONID + * DDC_CRT2 = RADEON_GPIO_CRT2_DDC + * r200 + * DDC_MONID = RADEON_GPIO_MONID + * DDC_CRT2 = RADEON_GPIO_DVI_DDC + * r300/r350 + * DDC_MONID = RADEON_GPIO_DVI_DDC + * DDC_CRT2 = RADEON_GPIO_DVI_DDC + * rv2xx/rv3xx + * DDC_MONID = RADEON_GPIO_MONID + * DDC_CRT2 = RADEON_GPIO_MONID + * rs3xx/rs4xx + * DDC_MONID = RADEON_GPIOPAD_MASK + * DDC_CRT2 = RADEON_GPIO_MONID + */ + switch (ddc) { + case DDC_NONE_DETECTED: + default: + ddc_line = 0; + break; + case DDC_DVI: + ddc_line = RADEON_GPIO_DVI_DDC; + break; + case DDC_VGA: + ddc_line = RADEON_GPIO_VGA_DDC; + break; + case DDC_LCD: + ddc_line = RADEON_GPIOPAD_MASK; + break; + case DDC_GPIO: + ddc_line = RADEON_MDGPIO_MASK; + break; + case DDC_MONID: + if (rdev->family == CHIP_RS300 || + rdev->family == CHIP_RS400 || + rdev->family == CHIP_RS480) + ddc_line = RADEON_GPIOPAD_MASK; + else if (rdev->family == CHIP_R300 || + rdev->family == CHIP_R350) { + ddc_line = RADEON_GPIO_DVI_DDC; + ddc = DDC_DVI; + } else + ddc_line = RADEON_GPIO_MONID; + break; + case DDC_CRT2: + if (rdev->family == CHIP_R200 || + rdev->family == CHIP_R300 || + rdev->family == CHIP_R350) { + ddc_line = RADEON_GPIO_DVI_DDC; + ddc = DDC_DVI; + } else if (rdev->family == CHIP_RS300 || + rdev->family == CHIP_RS400 || + rdev->family == CHIP_RS480) + ddc_line = RADEON_GPIO_MONID; + else if (rdev->family >= CHIP_RV350) { + ddc_line = RADEON_GPIO_MONID; + ddc = DDC_MONID; + } else + ddc_line = RADEON_GPIO_CRT2_DDC; + break; + } + + if (ddc_line == RADEON_GPIOPAD_MASK) { + i2c.mask_clk_reg = RADEON_GPIOPAD_MASK; + i2c.mask_data_reg = RADEON_GPIOPAD_MASK; + i2c.a_clk_reg = RADEON_GPIOPAD_A; + i2c.a_data_reg = RADEON_GPIOPAD_A; + i2c.en_clk_reg = RADEON_GPIOPAD_EN; + i2c.en_data_reg = RADEON_GPIOPAD_EN; + i2c.y_clk_reg = RADEON_GPIOPAD_Y; + i2c.y_data_reg = RADEON_GPIOPAD_Y; + } else if (ddc_line == RADEON_MDGPIO_MASK) { + i2c.mask_clk_reg = RADEON_MDGPIO_MASK; + i2c.mask_data_reg = RADEON_MDGPIO_MASK; + i2c.a_clk_reg = RADEON_MDGPIO_A; + i2c.a_data_reg = RADEON_MDGPIO_A; + i2c.en_clk_reg = RADEON_MDGPIO_EN; + i2c.en_data_reg = RADEON_MDGPIO_EN; + i2c.y_clk_reg = RADEON_MDGPIO_Y; + i2c.y_data_reg = RADEON_MDGPIO_Y; + } else { + i2c.mask_clk_reg = ddc_line; + i2c.mask_data_reg = ddc_line; + i2c.a_clk_reg = ddc_line; + i2c.a_data_reg = ddc_line; + i2c.en_clk_reg = ddc_line; + i2c.en_data_reg = ddc_line; + i2c.y_clk_reg = ddc_line; + i2c.y_data_reg = ddc_line; + } + + if (clk_mask && data_mask) { + /* system specific masks */ + i2c.mask_clk_mask = clk_mask; + i2c.mask_data_mask = data_mask; + i2c.a_clk_mask = clk_mask; + i2c.a_data_mask = data_mask; + i2c.en_clk_mask = clk_mask; + i2c.en_data_mask = data_mask; + i2c.y_clk_mask = clk_mask; + i2c.y_data_mask = data_mask; + } else if ((ddc_line == RADEON_GPIOPAD_MASK) || + (ddc_line == RADEON_MDGPIO_MASK)) { + /* default gpiopad masks */ + i2c.mask_clk_mask = (0x20 << 8); + i2c.mask_data_mask = 0x80; + i2c.a_clk_mask = (0x20 << 8); + i2c.a_data_mask = 0x80; + i2c.en_clk_mask = (0x20 << 8); + i2c.en_data_mask = 0x80; + i2c.y_clk_mask = (0x20 << 8); + i2c.y_data_mask = 0x80; + } else { + /* default masks for ddc pads */ + i2c.mask_clk_mask = RADEON_GPIO_MASK_1; + i2c.mask_data_mask = RADEON_GPIO_MASK_0; + i2c.a_clk_mask = RADEON_GPIO_A_1; + i2c.a_data_mask = RADEON_GPIO_A_0; + i2c.en_clk_mask = RADEON_GPIO_EN_1; + i2c.en_data_mask = RADEON_GPIO_EN_0; + i2c.y_clk_mask = RADEON_GPIO_Y_1; + i2c.y_data_mask = RADEON_GPIO_Y_0; + } + + switch (rdev->family) { + case CHIP_R100: + case CHIP_RV100: + case CHIP_RS100: + case CHIP_RV200: + case CHIP_RS200: + case CHIP_RS300: + switch (ddc_line) { + case RADEON_GPIO_DVI_DDC: + i2c.hw_capable = true; + break; + default: + i2c.hw_capable = false; + break; + } + break; + case CHIP_R200: + switch (ddc_line) { + case RADEON_GPIO_DVI_DDC: + case RADEON_GPIO_MONID: + i2c.hw_capable = true; + break; + default: + i2c.hw_capable = false; + break; + } + break; + case CHIP_RV250: + case CHIP_RV280: + switch (ddc_line) { + case RADEON_GPIO_VGA_DDC: + case RADEON_GPIO_DVI_DDC: + case RADEON_GPIO_CRT2_DDC: + i2c.hw_capable = true; + break; + default: + i2c.hw_capable = false; + break; + } + break; + case CHIP_R300: + case CHIP_R350: + switch (ddc_line) { + case RADEON_GPIO_VGA_DDC: + case RADEON_GPIO_DVI_DDC: + i2c.hw_capable = true; + break; + default: + i2c.hw_capable = false; + break; + } + break; + case CHIP_RV350: + case CHIP_RV380: + case CHIP_RS400: + case CHIP_RS480: + switch (ddc_line) { + case RADEON_GPIO_VGA_DDC: + case RADEON_GPIO_DVI_DDC: + i2c.hw_capable = true; + break; + case RADEON_GPIO_MONID: + /* hw i2c on RADEON_GPIO_MONID doesn't seem to work + * reliably on some pre-r4xx hardware; not sure why. + */ + i2c.hw_capable = false; + break; + default: + i2c.hw_capable = false; + break; + } + break; + default: + i2c.hw_capable = false; + break; + } + i2c.mm_i2c = false; + + i2c.i2c_id = ddc; + i2c.hpd = RADEON_HPD_NONE; + + if (ddc_line) + i2c.valid = true; + else + i2c.valid = false; + + return i2c; +} + +static struct radeon_i2c_bus_rec radeon_combios_get_i2c_info_from_table(struct radeon_device *rdev) +{ + struct drm_device *dev = rdev->ddev; + struct radeon_i2c_bus_rec i2c; + u16 offset; + u8 id, blocks, clk, data; + int i; + + i2c.valid = false; + + offset = combios_get_table_offset(dev, COMBIOS_I2C_INFO_TABLE); + if (offset) { + blocks = RBIOS8(offset + 2); + for (i = 0; i < blocks; i++) { + id = RBIOS8(offset + 3 + (i * 5) + 0); + if (id == 136) { + clk = RBIOS8(offset + 3 + (i * 5) + 3); + data = RBIOS8(offset + 3 + (i * 5) + 4); + /* gpiopad */ + i2c = combios_setup_i2c_bus(rdev, DDC_MONID, + (1 << clk), (1 << data)); + break; + } + } + } + return i2c; +} + +void radeon_combios_i2c_init(struct radeon_device *rdev) +{ + struct drm_device *dev = rdev->ddev; + struct radeon_i2c_bus_rec i2c; + + /* actual hw pads + * r1xx/rs2xx/rs3xx + * 0x60, 0x64, 0x68, 0x6c, gpiopads, mm + * r200 + * 0x60, 0x64, 0x68, mm + * r300/r350 + * 0x60, 0x64, mm + * rv2xx/rv3xx/rs4xx + * 0x60, 0x64, 0x68, gpiopads, mm + */ + + /* 0x60 */ + i2c = combios_setup_i2c_bus(rdev, DDC_DVI, 0, 0); + rdev->i2c_bus[0] = radeon_i2c_create(dev, &i2c, "DVI_DDC"); + /* 0x64 */ + i2c = combios_setup_i2c_bus(rdev, DDC_VGA, 0, 0); + rdev->i2c_bus[1] = radeon_i2c_create(dev, &i2c, "VGA_DDC"); + + /* mm i2c */ + i2c.valid = true; + i2c.hw_capable = true; + i2c.mm_i2c = true; + i2c.i2c_id = 0xa0; + rdev->i2c_bus[2] = radeon_i2c_create(dev, &i2c, "MM_I2C"); + + if (rdev->family == CHIP_R300 || + rdev->family == CHIP_R350) { + /* only 2 sw i2c pads */ + } else if (rdev->family == CHIP_RS300 || + rdev->family == CHIP_RS400 || + rdev->family == CHIP_RS480) { + /* 0x68 */ + i2c = combios_setup_i2c_bus(rdev, DDC_CRT2, 0, 0); + rdev->i2c_bus[3] = radeon_i2c_create(dev, &i2c, "MONID"); + + /* gpiopad */ + i2c = radeon_combios_get_i2c_info_from_table(rdev); + if (i2c.valid) + rdev->i2c_bus[4] = radeon_i2c_create(dev, &i2c, "GPIOPAD_MASK"); + } else if ((rdev->family == CHIP_R200) || + (rdev->family >= CHIP_R300)) { + /* 0x68 */ + i2c = combios_setup_i2c_bus(rdev, DDC_MONID, 0, 0); + rdev->i2c_bus[3] = radeon_i2c_create(dev, &i2c, "MONID"); + } else { + /* 0x68 */ + i2c = combios_setup_i2c_bus(rdev, DDC_MONID, 0, 0); + rdev->i2c_bus[3] = radeon_i2c_create(dev, &i2c, "MONID"); + /* 0x6c */ + i2c = combios_setup_i2c_bus(rdev, DDC_CRT2, 0, 0); + rdev->i2c_bus[4] = radeon_i2c_create(dev, &i2c, "CRT2_DDC"); + } +} + +bool radeon_combios_get_clock_info(struct drm_device *dev) +{ + struct radeon_device *rdev = dev->dev_private; + uint16_t pll_info; + struct radeon_pll *p1pll = &rdev->clock.p1pll; + struct radeon_pll *p2pll = &rdev->clock.p2pll; + struct radeon_pll *spll = &rdev->clock.spll; + struct radeon_pll *mpll = &rdev->clock.mpll; + int8_t rev; + uint16_t sclk, mclk; + + pll_info = combios_get_table_offset(dev, COMBIOS_PLL_INFO_TABLE); + if (pll_info) { + rev = RBIOS8(pll_info); + + /* pixel clocks */ + p1pll->reference_freq = RBIOS16(pll_info + 0xe); + p1pll->reference_div = RBIOS16(pll_info + 0x10); + p1pll->pll_out_min = RBIOS32(pll_info + 0x12); + p1pll->pll_out_max = RBIOS32(pll_info + 0x16); + p1pll->lcd_pll_out_min = p1pll->pll_out_min; + p1pll->lcd_pll_out_max = p1pll->pll_out_max; + + if (rev > 9) { + p1pll->pll_in_min = RBIOS32(pll_info + 0x36); + p1pll->pll_in_max = RBIOS32(pll_info + 0x3a); + } else { + p1pll->pll_in_min = 40; + p1pll->pll_in_max = 500; + } + *p2pll = *p1pll; + + /* system clock */ + spll->reference_freq = RBIOS16(pll_info + 0x1a); + spll->reference_div = RBIOS16(pll_info + 0x1c); + spll->pll_out_min = RBIOS32(pll_info + 0x1e); + spll->pll_out_max = RBIOS32(pll_info + 0x22); + + if (rev > 10) { + spll->pll_in_min = RBIOS32(pll_info + 0x48); + spll->pll_in_max = RBIOS32(pll_info + 0x4c); + } else { + /* ??? */ + spll->pll_in_min = 40; + spll->pll_in_max = 500; + } + + /* memory clock */ + mpll->reference_freq = RBIOS16(pll_info + 0x26); + mpll->reference_div = RBIOS16(pll_info + 0x28); + mpll->pll_out_min = RBIOS32(pll_info + 0x2a); + mpll->pll_out_max = RBIOS32(pll_info + 0x2e); + + if (rev > 10) { + mpll->pll_in_min = RBIOS32(pll_info + 0x5a); + mpll->pll_in_max = RBIOS32(pll_info + 0x5e); + } else { + /* ??? */ + mpll->pll_in_min = 40; + mpll->pll_in_max = 500; + } + + /* default sclk/mclk */ + sclk = RBIOS16(pll_info + 0xa); + mclk = RBIOS16(pll_info + 0x8); + if (sclk == 0) + sclk = 200 * 100; + if (mclk == 0) + mclk = 200 * 100; + + rdev->clock.default_sclk = sclk; + rdev->clock.default_mclk = mclk; + + if (RBIOS32(pll_info + 0x16)) + rdev->clock.max_pixel_clock = RBIOS32(pll_info + 0x16); + else + rdev->clock.max_pixel_clock = 35000; /* might need something asic specific */ + + return true; + } + return false; +} + +bool radeon_combios_sideport_present(struct radeon_device *rdev) +{ + struct drm_device *dev = rdev->ddev; + u16 igp_info; + + /* sideport is AMD only */ + if (rdev->family == CHIP_RS400) + return false; + + igp_info = combios_get_table_offset(dev, COMBIOS_INTEGRATED_SYSTEM_INFO_TABLE); + + if (igp_info) { + if (RBIOS16(igp_info + 0x4)) + return true; + } + return false; +} + +static const uint32_t default_primarydac_adj[CHIP_LAST] = { + 0x00000808, /* r100 */ + 0x00000808, /* rv100 */ + 0x00000808, /* rs100 */ + 0x00000808, /* rv200 */ + 0x00000808, /* rs200 */ + 0x00000808, /* r200 */ + 0x00000808, /* rv250 */ + 0x00000000, /* rs300 */ + 0x00000808, /* rv280 */ + 0x00000808, /* r300 */ + 0x00000808, /* r350 */ + 0x00000808, /* rv350 */ + 0x00000808, /* rv380 */ + 0x00000808, /* r420 */ + 0x00000808, /* r423 */ + 0x00000808, /* rv410 */ + 0x00000000, /* rs400 */ + 0x00000000, /* rs480 */ +}; + +static void radeon_legacy_get_primary_dac_info_from_table(struct radeon_device *rdev, + struct radeon_encoder_primary_dac *p_dac) +{ + p_dac->ps2_pdac_adj = default_primarydac_adj[rdev->family]; + return; +} + +struct radeon_encoder_primary_dac *radeon_combios_get_primary_dac_info(struct + radeon_encoder + *encoder) +{ + struct drm_device *dev = encoder->base.dev; + struct radeon_device *rdev = dev->dev_private; + uint16_t dac_info; + uint8_t rev, bg, dac; + struct radeon_encoder_primary_dac *p_dac = NULL; + int found = 0; + + p_dac = malloc(sizeof(struct radeon_encoder_primary_dac), + DRM_MEM_DRIVER, M_WAITOK | M_ZERO); + + if (!p_dac) + return NULL; + + /* check CRT table */ + dac_info = combios_get_table_offset(dev, COMBIOS_CRT_INFO_TABLE); + if (dac_info) { + rev = RBIOS8(dac_info) & 0x3; + if (rev < 2) { + bg = RBIOS8(dac_info + 0x2) & 0xf; + dac = (RBIOS8(dac_info + 0x2) >> 4) & 0xf; + p_dac->ps2_pdac_adj = (bg << 8) | (dac); + } else { + bg = RBIOS8(dac_info + 0x2) & 0xf; + dac = RBIOS8(dac_info + 0x3) & 0xf; + p_dac->ps2_pdac_adj = (bg << 8) | (dac); + } + /* if the values are all zeros, use the table */ + if (p_dac->ps2_pdac_adj) + found = 1; + } + + if (!found) /* fallback to defaults */ + radeon_legacy_get_primary_dac_info_from_table(rdev, p_dac); + + return p_dac; +} + +enum radeon_tv_std +radeon_combios_get_tv_info(struct radeon_device *rdev) +{ + struct drm_device *dev = rdev->ddev; + uint16_t tv_info; + enum radeon_tv_std tv_std = TV_STD_NTSC; + + tv_info = combios_get_table_offset(dev, COMBIOS_TV_INFO_TABLE); + if (tv_info) { + if (RBIOS8(tv_info + 6) == 'T') { + switch (RBIOS8(tv_info + 7) & 0xf) { + case 1: + tv_std = TV_STD_NTSC; + DRM_DEBUG_KMS("Default TV standard: NTSC\n"); + break; + case 2: + tv_std = TV_STD_PAL; + DRM_DEBUG_KMS("Default TV standard: PAL\n"); + break; + case 3: + tv_std = TV_STD_PAL_M; + DRM_DEBUG_KMS("Default TV standard: PAL-M\n"); + break; + case 4: + tv_std = TV_STD_PAL_60; + DRM_DEBUG_KMS("Default TV standard: PAL-60\n"); + break; + case 5: + tv_std = TV_STD_NTSC_J; + DRM_DEBUG_KMS("Default TV standard: NTSC-J\n"); + break; + case 6: + tv_std = TV_STD_SCART_PAL; + DRM_DEBUG_KMS("Default TV standard: SCART-PAL\n"); + break; + default: + tv_std = TV_STD_NTSC; + DRM_DEBUG_KMS + ("Unknown TV standard; defaulting to NTSC\n"); + break; + } + + switch ((RBIOS8(tv_info + 9) >> 2) & 0x3) { + case 0: + DRM_DEBUG_KMS("29.498928713 MHz TV ref clk\n"); + break; + case 1: + DRM_DEBUG_KMS("28.636360000 MHz TV ref clk\n"); + break; + case 2: + DRM_DEBUG_KMS("14.318180000 MHz TV ref clk\n"); + break; + case 3: + DRM_DEBUG_KMS("27.000000000 MHz TV ref clk\n"); + break; + default: + break; + } + } + } + return tv_std; +} + +static const uint32_t default_tvdac_adj[CHIP_LAST] = { + 0x00000000, /* r100 */ + 0x00280000, /* rv100 */ + 0x00000000, /* rs100 */ + 0x00880000, /* rv200 */ + 0x00000000, /* rs200 */ + 0x00000000, /* r200 */ + 0x00770000, /* rv250 */ + 0x00290000, /* rs300 */ + 0x00560000, /* rv280 */ + 0x00780000, /* r300 */ + 0x00770000, /* r350 */ + 0x00780000, /* rv350 */ + 0x00780000, /* rv380 */ + 0x01080000, /* r420 */ + 0x01080000, /* r423 */ + 0x01080000, /* rv410 */ + 0x00780000, /* rs400 */ + 0x00780000, /* rs480 */ +}; + +static void radeon_legacy_get_tv_dac_info_from_table(struct radeon_device *rdev, + struct radeon_encoder_tv_dac *tv_dac) +{ + tv_dac->ps2_tvdac_adj = default_tvdac_adj[rdev->family]; + if ((rdev->flags & RADEON_IS_MOBILITY) && (rdev->family == CHIP_RV250)) + tv_dac->ps2_tvdac_adj = 0x00880000; + tv_dac->pal_tvdac_adj = tv_dac->ps2_tvdac_adj; + tv_dac->ntsc_tvdac_adj = tv_dac->ps2_tvdac_adj; + return; +} + +struct radeon_encoder_tv_dac *radeon_combios_get_tv_dac_info(struct + radeon_encoder + *encoder) +{ + struct drm_device *dev = encoder->base.dev; + struct radeon_device *rdev = dev->dev_private; + uint16_t dac_info; + uint8_t rev, bg, dac; + struct radeon_encoder_tv_dac *tv_dac = NULL; + int found = 0; + + tv_dac = malloc(sizeof(struct radeon_encoder_tv_dac), + DRM_MEM_DRIVER, M_WAITOK | M_ZERO); + if (!tv_dac) + return NULL; + + /* first check TV table */ + dac_info = combios_get_table_offset(dev, COMBIOS_TV_INFO_TABLE); + if (dac_info) { + rev = RBIOS8(dac_info + 0x3); + if (rev > 4) { + bg = RBIOS8(dac_info + 0xc) & 0xf; + dac = RBIOS8(dac_info + 0xd) & 0xf; + tv_dac->ps2_tvdac_adj = (bg << 16) | (dac << 20); + + bg = RBIOS8(dac_info + 0xe) & 0xf; + dac = RBIOS8(dac_info + 0xf) & 0xf; + tv_dac->pal_tvdac_adj = (bg << 16) | (dac << 20); + + bg = RBIOS8(dac_info + 0x10) & 0xf; + dac = RBIOS8(dac_info + 0x11) & 0xf; + tv_dac->ntsc_tvdac_adj = (bg << 16) | (dac << 20); + /* if the values are all zeros, use the table */ + if (tv_dac->ps2_tvdac_adj) + found = 1; + } else if (rev > 1) { + bg = RBIOS8(dac_info + 0xc) & 0xf; + dac = (RBIOS8(dac_info + 0xc) >> 4) & 0xf; + tv_dac->ps2_tvdac_adj = (bg << 16) | (dac << 20); + + bg = RBIOS8(dac_info + 0xd) & 0xf; + dac = (RBIOS8(dac_info + 0xd) >> 4) & 0xf; + tv_dac->pal_tvdac_adj = (bg << 16) | (dac << 20); + + bg = RBIOS8(dac_info + 0xe) & 0xf; + dac = (RBIOS8(dac_info + 0xe) >> 4) & 0xf; + tv_dac->ntsc_tvdac_adj = (bg << 16) | (dac << 20); + /* if the values are all zeros, use the table */ + if (tv_dac->ps2_tvdac_adj) + found = 1; + } + tv_dac->tv_std = radeon_combios_get_tv_info(rdev); + } + if (!found) { + /* then check CRT table */ + dac_info = + combios_get_table_offset(dev, COMBIOS_CRT_INFO_TABLE); + if (dac_info) { + rev = RBIOS8(dac_info) & 0x3; + if (rev < 2) { + bg = RBIOS8(dac_info + 0x3) & 0xf; + dac = (RBIOS8(dac_info + 0x3) >> 4) & 0xf; + tv_dac->ps2_tvdac_adj = + (bg << 16) | (dac << 20); + tv_dac->pal_tvdac_adj = tv_dac->ps2_tvdac_adj; + tv_dac->ntsc_tvdac_adj = tv_dac->ps2_tvdac_adj; + /* if the values are all zeros, use the table */ + if (tv_dac->ps2_tvdac_adj) + found = 1; + } else { + bg = RBIOS8(dac_info + 0x4) & 0xf; + dac = RBIOS8(dac_info + 0x5) & 0xf; + tv_dac->ps2_tvdac_adj = + (bg << 16) | (dac << 20); + tv_dac->pal_tvdac_adj = tv_dac->ps2_tvdac_adj; + tv_dac->ntsc_tvdac_adj = tv_dac->ps2_tvdac_adj; + /* if the values are all zeros, use the table */ + if (tv_dac->ps2_tvdac_adj) + found = 1; + } + } else { + DRM_INFO("No TV DAC info found in BIOS\n"); + } + } + + if (!found) /* fallback to defaults */ + radeon_legacy_get_tv_dac_info_from_table(rdev, tv_dac); + + return tv_dac; +} + +static struct radeon_encoder_lvds *radeon_legacy_get_lvds_info_from_regs(struct + radeon_device + *rdev) +{ + struct radeon_encoder_lvds *lvds = NULL; + uint32_t fp_vert_stretch, fp_horz_stretch; + uint32_t ppll_div_sel, ppll_val; + uint32_t lvds_ss_gen_cntl = RREG32(RADEON_LVDS_SS_GEN_CNTL); + + lvds = malloc(sizeof(struct radeon_encoder_lvds), + DRM_MEM_DRIVER, M_WAITOK | M_ZERO); + + if (!lvds) + return NULL; + + fp_vert_stretch = RREG32(RADEON_FP_VERT_STRETCH); + fp_horz_stretch = RREG32(RADEON_FP_HORZ_STRETCH); + + /* These should be fail-safe defaults, fingers crossed */ + lvds->panel_pwr_delay = 200; + lvds->panel_vcc_delay = 2000; + + lvds->lvds_gen_cntl = RREG32(RADEON_LVDS_GEN_CNTL); + lvds->panel_digon_delay = (lvds_ss_gen_cntl >> RADEON_LVDS_PWRSEQ_DELAY1_SHIFT) & 0xf; + lvds->panel_blon_delay = (lvds_ss_gen_cntl >> RADEON_LVDS_PWRSEQ_DELAY2_SHIFT) & 0xf; + + if (fp_vert_stretch & RADEON_VERT_STRETCH_ENABLE) + lvds->native_mode.vdisplay = + ((fp_vert_stretch & RADEON_VERT_PANEL_SIZE) >> + RADEON_VERT_PANEL_SHIFT) + 1; + else + lvds->native_mode.vdisplay = + (RREG32(RADEON_CRTC_V_TOTAL_DISP) >> 16) + 1; + + if (fp_horz_stretch & RADEON_HORZ_STRETCH_ENABLE) + lvds->native_mode.hdisplay = + (((fp_horz_stretch & RADEON_HORZ_PANEL_SIZE) >> + RADEON_HORZ_PANEL_SHIFT) + 1) * 8; + else + lvds->native_mode.hdisplay = + ((RREG32(RADEON_CRTC_H_TOTAL_DISP) >> 16) + 1) * 8; + + if ((lvds->native_mode.hdisplay < 640) || + (lvds->native_mode.vdisplay < 480)) { + lvds->native_mode.hdisplay = 640; + lvds->native_mode.vdisplay = 480; + } + + ppll_div_sel = RREG8(RADEON_CLOCK_CNTL_INDEX + 1) & 0x3; + ppll_val = RREG32_PLL(RADEON_PPLL_DIV_0 + ppll_div_sel); + if ((ppll_val & 0x000707ff) == 0x1bb) + lvds->use_bios_dividers = false; + else { + lvds->panel_ref_divider = + RREG32_PLL(RADEON_PPLL_REF_DIV) & 0x3ff; + lvds->panel_post_divider = (ppll_val >> 16) & 0x7; + lvds->panel_fb_divider = ppll_val & 0x7ff; + + if ((lvds->panel_ref_divider != 0) && + (lvds->panel_fb_divider > 3)) + lvds->use_bios_dividers = true; + } + lvds->panel_vcc_delay = 200; + + DRM_INFO("Panel info derived from registers\n"); + DRM_INFO("Panel Size %dx%d\n", lvds->native_mode.hdisplay, + lvds->native_mode.vdisplay); + + return lvds; +} + +struct radeon_encoder_lvds *radeon_combios_get_lvds_info(struct radeon_encoder + *encoder) +{ + struct drm_device *dev = encoder->base.dev; + struct radeon_device *rdev = dev->dev_private; + uint16_t lcd_info; + uint32_t panel_setup; + char stmp[30]; + int tmp, i; + struct radeon_encoder_lvds *lvds = NULL; + + lcd_info = combios_get_table_offset(dev, COMBIOS_LCD_INFO_TABLE); + + if (lcd_info) { + lvds = malloc(sizeof(struct radeon_encoder_lvds), + DRM_MEM_DRIVER, M_WAITOK | M_ZERO); + + if (!lvds) + return NULL; + + for (i = 0; i < 24; i++) + stmp[i] = RBIOS8(lcd_info + i + 1); + stmp[24] = 0; + + DRM_INFO("Panel ID String: %s\n", stmp); + + lvds->native_mode.hdisplay = RBIOS16(lcd_info + 0x19); + lvds->native_mode.vdisplay = RBIOS16(lcd_info + 0x1b); + + DRM_INFO("Panel Size %dx%d\n", lvds->native_mode.hdisplay, + lvds->native_mode.vdisplay); + + lvds->panel_vcc_delay = RBIOS16(lcd_info + 0x2c); + lvds->panel_vcc_delay = min_t(u16, lvds->panel_vcc_delay, 2000); + + lvds->panel_pwr_delay = RBIOS8(lcd_info + 0x24); + lvds->panel_digon_delay = RBIOS16(lcd_info + 0x38) & 0xf; + lvds->panel_blon_delay = (RBIOS16(lcd_info + 0x38) >> 4) & 0xf; + + lvds->panel_ref_divider = RBIOS16(lcd_info + 0x2e); + lvds->panel_post_divider = RBIOS8(lcd_info + 0x30); + lvds->panel_fb_divider = RBIOS16(lcd_info + 0x31); + if ((lvds->panel_ref_divider != 0) && + (lvds->panel_fb_divider > 3)) + lvds->use_bios_dividers = true; + + panel_setup = RBIOS32(lcd_info + 0x39); + lvds->lvds_gen_cntl = 0xff00; + if (panel_setup & 0x1) + lvds->lvds_gen_cntl |= RADEON_LVDS_PANEL_FORMAT; + + if ((panel_setup >> 4) & 0x1) + lvds->lvds_gen_cntl |= RADEON_LVDS_PANEL_TYPE; + + switch ((panel_setup >> 8) & 0x7) { + case 0: + lvds->lvds_gen_cntl |= RADEON_LVDS_NO_FM; + break; + case 1: + lvds->lvds_gen_cntl |= RADEON_LVDS_2_GREY; + break; + case 2: + lvds->lvds_gen_cntl |= RADEON_LVDS_4_GREY; + break; + default: + break; + } + + if ((panel_setup >> 16) & 0x1) + lvds->lvds_gen_cntl |= RADEON_LVDS_FP_POL_LOW; + + if ((panel_setup >> 17) & 0x1) + lvds->lvds_gen_cntl |= RADEON_LVDS_LP_POL_LOW; + + if ((panel_setup >> 18) & 0x1) + lvds->lvds_gen_cntl |= RADEON_LVDS_DTM_POL_LOW; + + if ((panel_setup >> 23) & 0x1) + lvds->lvds_gen_cntl |= RADEON_LVDS_BL_CLK_SEL; + + lvds->lvds_gen_cntl |= (panel_setup & 0xf0000000); + + for (i = 0; i < 32; i++) { + tmp = RBIOS16(lcd_info + 64 + i * 2); + if (tmp == 0) + break; + + if ((RBIOS16(tmp) == lvds->native_mode.hdisplay) && + (RBIOS16(tmp + 2) == lvds->native_mode.vdisplay)) { + lvds->native_mode.htotal = lvds->native_mode.hdisplay + + (RBIOS16(tmp + 17) - RBIOS16(tmp + 19)) * 8; + lvds->native_mode.hsync_start = lvds->native_mode.hdisplay + + (RBIOS16(tmp + 21) - RBIOS16(tmp + 19) - 1) * 8; + lvds->native_mode.hsync_end = lvds->native_mode.hsync_start + + (RBIOS8(tmp + 23) * 8); + + lvds->native_mode.vtotal = lvds->native_mode.vdisplay + + (RBIOS16(tmp + 24) - RBIOS16(tmp + 26)); + lvds->native_mode.vsync_start = lvds->native_mode.vdisplay + + ((RBIOS16(tmp + 28) & 0x7ff) - RBIOS16(tmp + 26)); + lvds->native_mode.vsync_end = lvds->native_mode.vsync_start + + ((RBIOS16(tmp + 28) & 0xf800) >> 11); + + lvds->native_mode.clock = RBIOS16(tmp + 9) * 10; + lvds->native_mode.flags = 0; + /* set crtc values */ + drm_mode_set_crtcinfo(&lvds->native_mode, CRTC_INTERLACE_HALVE_V); + + } + } + } else { + DRM_INFO("No panel info found in BIOS\n"); + lvds = radeon_legacy_get_lvds_info_from_regs(rdev); + } + + if (lvds) + encoder->native_mode = lvds->native_mode; + return lvds; +} + +static const struct radeon_tmds_pll default_tmds_pll[CHIP_LAST][4] = { + {{12000, 0xa1b}, {0xffffffff, 0xa3f}, {0, 0}, {0, 0}}, /* CHIP_R100 */ + {{12000, 0xa1b}, {0xffffffff, 0xa3f}, {0, 0}, {0, 0}}, /* CHIP_RV100 */ + {{0, 0}, {0, 0}, {0, 0}, {0, 0}}, /* CHIP_RS100 */ + {{15000, 0xa1b}, {0xffffffff, 0xa3f}, {0, 0}, {0, 0}}, /* CHIP_RV200 */ + {{12000, 0xa1b}, {0xffffffff, 0xa3f}, {0, 0}, {0, 0}}, /* CHIP_RS200 */ + {{15000, 0xa1b}, {0xffffffff, 0xa3f}, {0, 0}, {0, 0}}, /* CHIP_R200 */ + {{15500, 0x81b}, {0xffffffff, 0x83f}, {0, 0}, {0, 0}}, /* CHIP_RV250 */ + {{0, 0}, {0, 0}, {0, 0}, {0, 0}}, /* CHIP_RS300 */ + {{13000, 0x400f4}, {15000, 0x400f7}, {0xffffffff, 0x40111}, {0, 0}}, /* CHIP_RV280 */ + {{0xffffffff, 0xb01cb}, {0, 0}, {0, 0}, {0, 0}}, /* CHIP_R300 */ + {{0xffffffff, 0xb01cb}, {0, 0}, {0, 0}, {0, 0}}, /* CHIP_R350 */ + {{15000, 0xb0155}, {0xffffffff, 0xb01cb}, {0, 0}, {0, 0}}, /* CHIP_RV350 */ + {{15000, 0xb0155}, {0xffffffff, 0xb01cb}, {0, 0}, {0, 0}}, /* CHIP_RV380 */ + {{0xffffffff, 0xb01cb}, {0, 0}, {0, 0}, {0, 0}}, /* CHIP_R420 */ + {{0xffffffff, 0xb01cb}, {0, 0}, {0, 0}, {0, 0}}, /* CHIP_R423 */ + {{0xffffffff, 0xb01cb}, {0, 0}, {0, 0}, {0, 0}}, /* CHIP_RV410 */ + { {0, 0}, {0, 0}, {0, 0}, {0, 0} }, /* CHIP_RS400 */ + { {0, 0}, {0, 0}, {0, 0}, {0, 0} }, /* CHIP_RS480 */ +}; + +bool radeon_legacy_get_tmds_info_from_table(struct radeon_encoder *encoder, + struct radeon_encoder_int_tmds *tmds) +{ + struct drm_device *dev = encoder->base.dev; + struct radeon_device *rdev = dev->dev_private; + int i; + + for (i = 0; i < 4; i++) { + tmds->tmds_pll[i].value = + default_tmds_pll[rdev->family][i].value; + tmds->tmds_pll[i].freq = default_tmds_pll[rdev->family][i].freq; + } + + return true; +} + +bool radeon_legacy_get_tmds_info_from_combios(struct radeon_encoder *encoder, + struct radeon_encoder_int_tmds *tmds) +{ + struct drm_device *dev = encoder->base.dev; + struct radeon_device *rdev = dev->dev_private; + uint16_t tmds_info; + int i, n; + uint8_t ver; + + tmds_info = combios_get_table_offset(dev, COMBIOS_DFP_INFO_TABLE); + + if (tmds_info) { + ver = RBIOS8(tmds_info); + DRM_DEBUG_KMS("DFP table revision: %d\n", ver); + if (ver == 3) { + n = RBIOS8(tmds_info + 5) + 1; + if (n > 4) + n = 4; + for (i = 0; i < n; i++) { + tmds->tmds_pll[i].value = + RBIOS32(tmds_info + i * 10 + 0x08); + tmds->tmds_pll[i].freq = + RBIOS16(tmds_info + i * 10 + 0x10); + DRM_DEBUG_KMS("TMDS PLL From COMBIOS %u %x\n", + tmds->tmds_pll[i].freq, + tmds->tmds_pll[i].value); + } + } else if (ver == 4) { + int stride = 0; + n = RBIOS8(tmds_info + 5) + 1; + if (n > 4) + n = 4; + for (i = 0; i < n; i++) { + tmds->tmds_pll[i].value = + RBIOS32(tmds_info + stride + 0x08); + tmds->tmds_pll[i].freq = + RBIOS16(tmds_info + stride + 0x10); + if (i == 0) + stride += 10; + else + stride += 6; + DRM_DEBUG_KMS("TMDS PLL From COMBIOS %u %x\n", + tmds->tmds_pll[i].freq, + tmds->tmds_pll[i].value); + } + } + } else { + DRM_INFO("No TMDS info found in BIOS\n"); + return false; + } + return true; +} + +bool radeon_legacy_get_ext_tmds_info_from_table(struct radeon_encoder *encoder, + struct radeon_encoder_ext_tmds *tmds) +{ + struct drm_device *dev = encoder->base.dev; + struct radeon_device *rdev = dev->dev_private; + struct radeon_i2c_bus_rec i2c_bus; + + /* default for macs */ + i2c_bus = combios_setup_i2c_bus(rdev, DDC_MONID, 0, 0); + tmds->i2c_bus = radeon_i2c_lookup(rdev, &i2c_bus); + + /* XXX some macs have duallink chips */ + switch (rdev->mode_info.connector_table) { + case CT_POWERBOOK_EXTERNAL: + case CT_MINI_EXTERNAL: + default: + tmds->dvo_chip = DVO_SIL164; + tmds->slave_addr = 0x70 >> 1; /* 7 bit addressing */ + break; + } + + return true; +} + +bool radeon_legacy_get_ext_tmds_info_from_combios(struct radeon_encoder *encoder, + struct radeon_encoder_ext_tmds *tmds) +{ + struct drm_device *dev = encoder->base.dev; + struct radeon_device *rdev = dev->dev_private; + uint16_t offset; + uint8_t ver; + enum radeon_combios_ddc gpio; + struct radeon_i2c_bus_rec i2c_bus; + + tmds->i2c_bus = NULL; + if (rdev->flags & RADEON_IS_IGP) { + i2c_bus = combios_setup_i2c_bus(rdev, DDC_MONID, 0, 0); + tmds->i2c_bus = radeon_i2c_lookup(rdev, &i2c_bus); + tmds->dvo_chip = DVO_SIL164; + tmds->slave_addr = 0x70 >> 1; /* 7 bit addressing */ + } else { + offset = combios_get_table_offset(dev, COMBIOS_EXT_TMDS_INFO_TABLE); + if (offset) { + ver = RBIOS8(offset); + DRM_DEBUG_KMS("External TMDS Table revision: %d\n", ver); + tmds->slave_addr = RBIOS8(offset + 4 + 2); + tmds->slave_addr >>= 1; /* 7 bit addressing */ + gpio = RBIOS8(offset + 4 + 3); + if (gpio == DDC_LCD) { + /* MM i2c */ + i2c_bus.valid = true; + i2c_bus.hw_capable = true; + i2c_bus.mm_i2c = true; + i2c_bus.i2c_id = 0xa0; + } else + i2c_bus = combios_setup_i2c_bus(rdev, gpio, 0, 0); + tmds->i2c_bus = radeon_i2c_lookup(rdev, &i2c_bus); + } + } + + if (!tmds->i2c_bus) { + DRM_INFO("No valid Ext TMDS info found in BIOS\n"); + return false; + } + + return true; +} + +bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev) +{ + struct radeon_device *rdev = dev->dev_private; + struct radeon_i2c_bus_rec ddc_i2c; + struct radeon_hpd hpd; + + rdev->mode_info.connector_table = radeon_connector_table; + if (rdev->mode_info.connector_table == CT_NONE) { +#ifdef CONFIG_PPC_PMAC + if (of_machine_is_compatible("PowerBook3,3")) { + /* powerbook with VGA */ + rdev->mode_info.connector_table = CT_POWERBOOK_VGA; + } else if (of_machine_is_compatible("PowerBook3,4") || + of_machine_is_compatible("PowerBook3,5")) { + /* powerbook with internal tmds */ + rdev->mode_info.connector_table = CT_POWERBOOK_INTERNAL; + } else if (of_machine_is_compatible("PowerBook5,1") || + of_machine_is_compatible("PowerBook5,2") || + of_machine_is_compatible("PowerBook5,3") || + of_machine_is_compatible("PowerBook5,4") || + of_machine_is_compatible("PowerBook5,5")) { + /* powerbook with external single link tmds (sil164) */ + rdev->mode_info.connector_table = CT_POWERBOOK_EXTERNAL; + } else if (of_machine_is_compatible("PowerBook5,6")) { + /* powerbook with external dual or single link tmds */ + rdev->mode_info.connector_table = CT_POWERBOOK_EXTERNAL; + } else if (of_machine_is_compatible("PowerBook5,7") || + of_machine_is_compatible("PowerBook5,8") || + of_machine_is_compatible("PowerBook5,9")) { + /* PowerBook6,2 ? */ + /* powerbook with external dual link tmds (sil1178?) */ + rdev->mode_info.connector_table = CT_POWERBOOK_EXTERNAL; + } else if (of_machine_is_compatible("PowerBook4,1") || + of_machine_is_compatible("PowerBook4,2") || + of_machine_is_compatible("PowerBook4,3") || + of_machine_is_compatible("PowerBook6,3") || + of_machine_is_compatible("PowerBook6,5") || + of_machine_is_compatible("PowerBook6,7")) { + /* ibook */ + rdev->mode_info.connector_table = CT_IBOOK; + } else if (of_machine_is_compatible("PowerMac3,5")) { + /* PowerMac G4 Silver radeon 7500 */ + rdev->mode_info.connector_table = CT_MAC_G4_SILVER; + } else if (of_machine_is_compatible("PowerMac4,4")) { + /* emac */ + rdev->mode_info.connector_table = CT_EMAC; + } else if (of_machine_is_compatible("PowerMac10,1")) { + /* mini with internal tmds */ + rdev->mode_info.connector_table = CT_MINI_INTERNAL; + } else if (of_machine_is_compatible("PowerMac10,2")) { + /* mini with external tmds */ + rdev->mode_info.connector_table = CT_MINI_EXTERNAL; + } else if (of_machine_is_compatible("PowerMac12,1")) { + /* PowerMac8,1 ? */ + /* imac g5 isight */ + rdev->mode_info.connector_table = CT_IMAC_G5_ISIGHT; + } else if ((dev->pci_device == 0x4a48) && + (dev->pci_subvendor == 0x1002) && + (dev->pci_subdevice == 0x4a48)) { + /* Mac X800 */ + rdev->mode_info.connector_table = CT_MAC_X800; + } else if ((of_machine_is_compatible("PowerMac7,2") || + of_machine_is_compatible("PowerMac7,3")) && + (dev->pci_device == 0x4150) && + (dev->pci_subvendor == 0x1002) && + (dev->pci_subdevice == 0x4150)) { + /* Mac G5 tower 9600 */ + rdev->mode_info.connector_table = CT_MAC_G5_9600; + } else if ((dev->pci_device == 0x4c66) && + (dev->pci_subvendor == 0x1002) && + (dev->pci_subdevice == 0x4c66)) { + /* SAM440ep RV250 embedded board */ + rdev->mode_info.connector_table = CT_SAM440EP; + } else +#endif /* CONFIG_PPC_PMAC */ +#ifdef CONFIG_PPC64 + if (ASIC_IS_RN50(rdev)) + rdev->mode_info.connector_table = CT_RN50_POWER; + else +#endif + rdev->mode_info.connector_table = CT_GENERIC; + } + + switch (rdev->mode_info.connector_table) { + case CT_GENERIC: + DRM_INFO("Connector Table: %d (generic)\n", + rdev->mode_info.connector_table); + /* these are the most common settings */ + if (rdev->flags & RADEON_SINGLE_CRTC) { + /* VGA - primary dac */ + ddc_i2c = combios_setup_i2c_bus(rdev, DDC_VGA, 0, 0); + hpd.hpd = RADEON_HPD_NONE; + radeon_add_legacy_encoder(dev, + radeon_get_encoder_enum(dev, + ATOM_DEVICE_CRT1_SUPPORT, + 1), + ATOM_DEVICE_CRT1_SUPPORT); + radeon_add_legacy_connector(dev, 0, + ATOM_DEVICE_CRT1_SUPPORT, + DRM_MODE_CONNECTOR_VGA, + &ddc_i2c, + CONNECTOR_OBJECT_ID_VGA, + &hpd); + } else if (rdev->flags & RADEON_IS_MOBILITY) { + /* LVDS */ + ddc_i2c = combios_setup_i2c_bus(rdev, DDC_NONE_DETECTED, 0, 0); + hpd.hpd = RADEON_HPD_NONE; + radeon_add_legacy_encoder(dev, + radeon_get_encoder_enum(dev, + ATOM_DEVICE_LCD1_SUPPORT, + 0), + ATOM_DEVICE_LCD1_SUPPORT); + radeon_add_legacy_connector(dev, 0, + ATOM_DEVICE_LCD1_SUPPORT, + DRM_MODE_CONNECTOR_LVDS, + &ddc_i2c, + CONNECTOR_OBJECT_ID_LVDS, + &hpd); + + /* VGA - primary dac */ + ddc_i2c = combios_setup_i2c_bus(rdev, DDC_VGA, 0, 0); + hpd.hpd = RADEON_HPD_NONE; + radeon_add_legacy_encoder(dev, + radeon_get_encoder_enum(dev, + ATOM_DEVICE_CRT1_SUPPORT, + 1), + ATOM_DEVICE_CRT1_SUPPORT); + radeon_add_legacy_connector(dev, 1, + ATOM_DEVICE_CRT1_SUPPORT, + DRM_MODE_CONNECTOR_VGA, + &ddc_i2c, + CONNECTOR_OBJECT_ID_VGA, + &hpd); + } else { + /* DVI-I - tv dac, int tmds */ + ddc_i2c = combios_setup_i2c_bus(rdev, DDC_DVI, 0, 0); + hpd.hpd = RADEON_HPD_1; + radeon_add_legacy_encoder(dev, + radeon_get_encoder_enum(dev, + ATOM_DEVICE_DFP1_SUPPORT, + 0), + ATOM_DEVICE_DFP1_SUPPORT); + radeon_add_legacy_encoder(dev, + radeon_get_encoder_enum(dev, + ATOM_DEVICE_CRT2_SUPPORT, + 2), + ATOM_DEVICE_CRT2_SUPPORT); + radeon_add_legacy_connector(dev, 0, + ATOM_DEVICE_DFP1_SUPPORT | + ATOM_DEVICE_CRT2_SUPPORT, + DRM_MODE_CONNECTOR_DVII, + &ddc_i2c, + CONNECTOR_OBJECT_ID_SINGLE_LINK_DVI_I, + &hpd); + + /* VGA - primary dac */ + ddc_i2c = combios_setup_i2c_bus(rdev, DDC_VGA, 0, 0); + hpd.hpd = RADEON_HPD_NONE; + radeon_add_legacy_encoder(dev, + radeon_get_encoder_enum(dev, + ATOM_DEVICE_CRT1_SUPPORT, + 1), + ATOM_DEVICE_CRT1_SUPPORT); + radeon_add_legacy_connector(dev, 1, + ATOM_DEVICE_CRT1_SUPPORT, + DRM_MODE_CONNECTOR_VGA, + &ddc_i2c, + CONNECTOR_OBJECT_ID_VGA, + &hpd); + } + + if (rdev->family != CHIP_R100 && rdev->family != CHIP_R200) { + /* TV - tv dac */ + ddc_i2c.valid = false; + hpd.hpd = RADEON_HPD_NONE; + radeon_add_legacy_encoder(dev, + radeon_get_encoder_enum(dev, + ATOM_DEVICE_TV1_SUPPORT, + 2), + ATOM_DEVICE_TV1_SUPPORT); + radeon_add_legacy_connector(dev, 2, + ATOM_DEVICE_TV1_SUPPORT, + DRM_MODE_CONNECTOR_SVIDEO, + &ddc_i2c, + CONNECTOR_OBJECT_ID_SVIDEO, + &hpd); + } + break; + case CT_IBOOK: + DRM_INFO("Connector Table: %d (ibook)\n", + rdev->mode_info.connector_table); + /* LVDS */ + ddc_i2c = combios_setup_i2c_bus(rdev, DDC_DVI, 0, 0); + hpd.hpd = RADEON_HPD_NONE; + radeon_add_legacy_encoder(dev, + radeon_get_encoder_enum(dev, + ATOM_DEVICE_LCD1_SUPPORT, + 0), + ATOM_DEVICE_LCD1_SUPPORT); + radeon_add_legacy_connector(dev, 0, ATOM_DEVICE_LCD1_SUPPORT, + DRM_MODE_CONNECTOR_LVDS, &ddc_i2c, + CONNECTOR_OBJECT_ID_LVDS, + &hpd); + /* VGA - TV DAC */ + ddc_i2c = combios_setup_i2c_bus(rdev, DDC_VGA, 0, 0); + hpd.hpd = RADEON_HPD_NONE; + radeon_add_legacy_encoder(dev, + radeon_get_encoder_enum(dev, + ATOM_DEVICE_CRT2_SUPPORT, + 2), + ATOM_DEVICE_CRT2_SUPPORT); + radeon_add_legacy_connector(dev, 1, ATOM_DEVICE_CRT2_SUPPORT, + DRM_MODE_CONNECTOR_VGA, &ddc_i2c, + CONNECTOR_OBJECT_ID_VGA, + &hpd); + /* TV - TV DAC */ + ddc_i2c.valid = false; + hpd.hpd = RADEON_HPD_NONE; + radeon_add_legacy_encoder(dev, + radeon_get_encoder_enum(dev, + ATOM_DEVICE_TV1_SUPPORT, + 2), + ATOM_DEVICE_TV1_SUPPORT); + radeon_add_legacy_connector(dev, 2, ATOM_DEVICE_TV1_SUPPORT, + DRM_MODE_CONNECTOR_SVIDEO, + &ddc_i2c, + CONNECTOR_OBJECT_ID_SVIDEO, + &hpd); + break; + case CT_POWERBOOK_EXTERNAL: + DRM_INFO("Connector Table: %d (powerbook external tmds)\n", + rdev->mode_info.connector_table); + /* LVDS */ + ddc_i2c = combios_setup_i2c_bus(rdev, DDC_DVI, 0, 0); + hpd.hpd = RADEON_HPD_NONE; + radeon_add_legacy_encoder(dev, + radeon_get_encoder_enum(dev, + ATOM_DEVICE_LCD1_SUPPORT, + 0), + ATOM_DEVICE_LCD1_SUPPORT); + radeon_add_legacy_connector(dev, 0, ATOM_DEVICE_LCD1_SUPPORT, + DRM_MODE_CONNECTOR_LVDS, &ddc_i2c, + CONNECTOR_OBJECT_ID_LVDS, + &hpd); + /* DVI-I - primary dac, ext tmds */ + ddc_i2c = combios_setup_i2c_bus(rdev, DDC_VGA, 0, 0); + hpd.hpd = RADEON_HPD_2; /* ??? */ + radeon_add_legacy_encoder(dev, + radeon_get_encoder_enum(dev, + ATOM_DEVICE_DFP2_SUPPORT, + 0), + ATOM_DEVICE_DFP2_SUPPORT); + radeon_add_legacy_encoder(dev, + radeon_get_encoder_enum(dev, + ATOM_DEVICE_CRT1_SUPPORT, + 1), + ATOM_DEVICE_CRT1_SUPPORT); + /* XXX some are SL */ + radeon_add_legacy_connector(dev, 1, + ATOM_DEVICE_DFP2_SUPPORT | + ATOM_DEVICE_CRT1_SUPPORT, + DRM_MODE_CONNECTOR_DVII, &ddc_i2c, + CONNECTOR_OBJECT_ID_DUAL_LINK_DVI_I, + &hpd); + /* TV - TV DAC */ + ddc_i2c.valid = false; + hpd.hpd = RADEON_HPD_NONE; + radeon_add_legacy_encoder(dev, + radeon_get_encoder_enum(dev, + ATOM_DEVICE_TV1_SUPPORT, + 2), + ATOM_DEVICE_TV1_SUPPORT); + radeon_add_legacy_connector(dev, 2, ATOM_DEVICE_TV1_SUPPORT, + DRM_MODE_CONNECTOR_SVIDEO, + &ddc_i2c, + CONNECTOR_OBJECT_ID_SVIDEO, + &hpd); + break; + case CT_POWERBOOK_INTERNAL: + DRM_INFO("Connector Table: %d (powerbook internal tmds)\n", + rdev->mode_info.connector_table); + /* LVDS */ + ddc_i2c = combios_setup_i2c_bus(rdev, DDC_DVI, 0, 0); + hpd.hpd = RADEON_HPD_NONE; + radeon_add_legacy_encoder(dev, + radeon_get_encoder_enum(dev, + ATOM_DEVICE_LCD1_SUPPORT, + 0), + ATOM_DEVICE_LCD1_SUPPORT); + radeon_add_legacy_connector(dev, 0, ATOM_DEVICE_LCD1_SUPPORT, + DRM_MODE_CONNECTOR_LVDS, &ddc_i2c, + CONNECTOR_OBJECT_ID_LVDS, + &hpd); + /* DVI-I - primary dac, int tmds */ + ddc_i2c = combios_setup_i2c_bus(rdev, DDC_VGA, 0, 0); + hpd.hpd = RADEON_HPD_1; /* ??? */ + radeon_add_legacy_encoder(dev, + radeon_get_encoder_enum(dev, + ATOM_DEVICE_DFP1_SUPPORT, + 0), + ATOM_DEVICE_DFP1_SUPPORT); + radeon_add_legacy_encoder(dev, + radeon_get_encoder_enum(dev, + ATOM_DEVICE_CRT1_SUPPORT, + 1), + ATOM_DEVICE_CRT1_SUPPORT); + radeon_add_legacy_connector(dev, 1, + ATOM_DEVICE_DFP1_SUPPORT | + ATOM_DEVICE_CRT1_SUPPORT, + DRM_MODE_CONNECTOR_DVII, &ddc_i2c, + CONNECTOR_OBJECT_ID_SINGLE_LINK_DVI_I, + &hpd); + /* TV - TV DAC */ + ddc_i2c.valid = false; + hpd.hpd = RADEON_HPD_NONE; + radeon_add_legacy_encoder(dev, + radeon_get_encoder_enum(dev, + ATOM_DEVICE_TV1_SUPPORT, + 2), + ATOM_DEVICE_TV1_SUPPORT); + radeon_add_legacy_connector(dev, 2, ATOM_DEVICE_TV1_SUPPORT, + DRM_MODE_CONNECTOR_SVIDEO, + &ddc_i2c, + CONNECTOR_OBJECT_ID_SVIDEO, + &hpd); + break; + case CT_POWERBOOK_VGA: + DRM_INFO("Connector Table: %d (powerbook vga)\n", + rdev->mode_info.connector_table); + /* LVDS */ + ddc_i2c = combios_setup_i2c_bus(rdev, DDC_DVI, 0, 0); + hpd.hpd = RADEON_HPD_NONE; + radeon_add_legacy_encoder(dev, + radeon_get_encoder_enum(dev, + ATOM_DEVICE_LCD1_SUPPORT, + 0), + ATOM_DEVICE_LCD1_SUPPORT); + radeon_add_legacy_connector(dev, 0, ATOM_DEVICE_LCD1_SUPPORT, + DRM_MODE_CONNECTOR_LVDS, &ddc_i2c, + CONNECTOR_OBJECT_ID_LVDS, + &hpd); + /* VGA - primary dac */ + ddc_i2c = combios_setup_i2c_bus(rdev, DDC_VGA, 0, 0); + hpd.hpd = RADEON_HPD_NONE; + radeon_add_legacy_encoder(dev, + radeon_get_encoder_enum(dev, + ATOM_DEVICE_CRT1_SUPPORT, + 1), + ATOM_DEVICE_CRT1_SUPPORT); + radeon_add_legacy_connector(dev, 1, ATOM_DEVICE_CRT1_SUPPORT, + DRM_MODE_CONNECTOR_VGA, &ddc_i2c, + CONNECTOR_OBJECT_ID_VGA, + &hpd); + /* TV - TV DAC */ + ddc_i2c.valid = false; + hpd.hpd = RADEON_HPD_NONE; + radeon_add_legacy_encoder(dev, + radeon_get_encoder_enum(dev, + ATOM_DEVICE_TV1_SUPPORT, + 2), + ATOM_DEVICE_TV1_SUPPORT); + radeon_add_legacy_connector(dev, 2, ATOM_DEVICE_TV1_SUPPORT, + DRM_MODE_CONNECTOR_SVIDEO, + &ddc_i2c, + CONNECTOR_OBJECT_ID_SVIDEO, + &hpd); + break; + case CT_MINI_EXTERNAL: + DRM_INFO("Connector Table: %d (mini external tmds)\n", + rdev->mode_info.connector_table); + /* DVI-I - tv dac, ext tmds */ + ddc_i2c = combios_setup_i2c_bus(rdev, DDC_CRT2, 0, 0); + hpd.hpd = RADEON_HPD_2; /* ??? */ + radeon_add_legacy_encoder(dev, + radeon_get_encoder_enum(dev, + ATOM_DEVICE_DFP2_SUPPORT, + 0), + ATOM_DEVICE_DFP2_SUPPORT); + radeon_add_legacy_encoder(dev, + radeon_get_encoder_enum(dev, + ATOM_DEVICE_CRT2_SUPPORT, + 2), + ATOM_DEVICE_CRT2_SUPPORT); + /* XXX are any DL? */ + radeon_add_legacy_connector(dev, 0, + ATOM_DEVICE_DFP2_SUPPORT | + ATOM_DEVICE_CRT2_SUPPORT, + DRM_MODE_CONNECTOR_DVII, &ddc_i2c, + CONNECTOR_OBJECT_ID_SINGLE_LINK_DVI_I, + &hpd); + /* TV - TV DAC */ + ddc_i2c.valid = false; + hpd.hpd = RADEON_HPD_NONE; + radeon_add_legacy_encoder(dev, + radeon_get_encoder_enum(dev, + ATOM_DEVICE_TV1_SUPPORT, + 2), + ATOM_DEVICE_TV1_SUPPORT); + radeon_add_legacy_connector(dev, 1, ATOM_DEVICE_TV1_SUPPORT, + DRM_MODE_CONNECTOR_SVIDEO, + &ddc_i2c, + CONNECTOR_OBJECT_ID_SVIDEO, + &hpd); + break; + case CT_MINI_INTERNAL: + DRM_INFO("Connector Table: %d (mini internal tmds)\n", + rdev->mode_info.connector_table); + /* DVI-I - tv dac, int tmds */ + ddc_i2c = combios_setup_i2c_bus(rdev, DDC_CRT2, 0, 0); + hpd.hpd = RADEON_HPD_1; /* ??? */ + radeon_add_legacy_encoder(dev, + radeon_get_encoder_enum(dev, + ATOM_DEVICE_DFP1_SUPPORT, + 0), + ATOM_DEVICE_DFP1_SUPPORT); + radeon_add_legacy_encoder(dev, + radeon_get_encoder_enum(dev, + ATOM_DEVICE_CRT2_SUPPORT, + 2), + ATOM_DEVICE_CRT2_SUPPORT); + radeon_add_legacy_connector(dev, 0, + ATOM_DEVICE_DFP1_SUPPORT | + ATOM_DEVICE_CRT2_SUPPORT, + DRM_MODE_CONNECTOR_DVII, &ddc_i2c, + CONNECTOR_OBJECT_ID_SINGLE_LINK_DVI_I, + &hpd); + /* TV - TV DAC */ + ddc_i2c.valid = false; + hpd.hpd = RADEON_HPD_NONE; + radeon_add_legacy_encoder(dev, + radeon_get_encoder_enum(dev, + ATOM_DEVICE_TV1_SUPPORT, + 2), + ATOM_DEVICE_TV1_SUPPORT); + radeon_add_legacy_connector(dev, 1, ATOM_DEVICE_TV1_SUPPORT, + DRM_MODE_CONNECTOR_SVIDEO, + &ddc_i2c, + CONNECTOR_OBJECT_ID_SVIDEO, + &hpd); + break; + case CT_IMAC_G5_ISIGHT: + DRM_INFO("Connector Table: %d (imac g5 isight)\n", + rdev->mode_info.connector_table); + /* DVI-D - int tmds */ + ddc_i2c = combios_setup_i2c_bus(rdev, DDC_MONID, 0, 0); + hpd.hpd = RADEON_HPD_1; /* ??? */ + radeon_add_legacy_encoder(dev, + radeon_get_encoder_enum(dev, + ATOM_DEVICE_DFP1_SUPPORT, + 0), + ATOM_DEVICE_DFP1_SUPPORT); + radeon_add_legacy_connector(dev, 0, ATOM_DEVICE_DFP1_SUPPORT, + DRM_MODE_CONNECTOR_DVID, &ddc_i2c, + CONNECTOR_OBJECT_ID_SINGLE_LINK_DVI_D, + &hpd); + /* VGA - tv dac */ + ddc_i2c = combios_setup_i2c_bus(rdev, DDC_DVI, 0, 0); + hpd.hpd = RADEON_HPD_NONE; + radeon_add_legacy_encoder(dev, + radeon_get_encoder_enum(dev, + ATOM_DEVICE_CRT2_SUPPORT, + 2), + ATOM_DEVICE_CRT2_SUPPORT); + radeon_add_legacy_connector(dev, 1, ATOM_DEVICE_CRT2_SUPPORT, + DRM_MODE_CONNECTOR_VGA, &ddc_i2c, + CONNECTOR_OBJECT_ID_VGA, + &hpd); + /* TV - TV DAC */ + ddc_i2c.valid = false; + hpd.hpd = RADEON_HPD_NONE; + radeon_add_legacy_encoder(dev, + radeon_get_encoder_enum(dev, + ATOM_DEVICE_TV1_SUPPORT, + 2), + ATOM_DEVICE_TV1_SUPPORT); + radeon_add_legacy_connector(dev, 2, ATOM_DEVICE_TV1_SUPPORT, + DRM_MODE_CONNECTOR_SVIDEO, + &ddc_i2c, + CONNECTOR_OBJECT_ID_SVIDEO, + &hpd); + break; + case CT_EMAC: + DRM_INFO("Connector Table: %d (emac)\n", + rdev->mode_info.connector_table); + /* VGA - primary dac */ + ddc_i2c = combios_setup_i2c_bus(rdev, DDC_VGA, 0, 0); + hpd.hpd = RADEON_HPD_NONE; + radeon_add_legacy_encoder(dev, + radeon_get_encoder_enum(dev, + ATOM_DEVICE_CRT1_SUPPORT, + 1), + ATOM_DEVICE_CRT1_SUPPORT); + radeon_add_legacy_connector(dev, 0, ATOM_DEVICE_CRT1_SUPPORT, + DRM_MODE_CONNECTOR_VGA, &ddc_i2c, + CONNECTOR_OBJECT_ID_VGA, + &hpd); + /* VGA - tv dac */ + ddc_i2c = combios_setup_i2c_bus(rdev, DDC_CRT2, 0, 0); + hpd.hpd = RADEON_HPD_NONE; + radeon_add_legacy_encoder(dev, + radeon_get_encoder_enum(dev, + ATOM_DEVICE_CRT2_SUPPORT, + 2), + ATOM_DEVICE_CRT2_SUPPORT); + radeon_add_legacy_connector(dev, 1, ATOM_DEVICE_CRT2_SUPPORT, + DRM_MODE_CONNECTOR_VGA, &ddc_i2c, + CONNECTOR_OBJECT_ID_VGA, + &hpd); + /* TV - TV DAC */ + ddc_i2c.valid = false; + hpd.hpd = RADEON_HPD_NONE; + radeon_add_legacy_encoder(dev, + radeon_get_encoder_enum(dev, + ATOM_DEVICE_TV1_SUPPORT, + 2), + ATOM_DEVICE_TV1_SUPPORT); + radeon_add_legacy_connector(dev, 2, ATOM_DEVICE_TV1_SUPPORT, + DRM_MODE_CONNECTOR_SVIDEO, + &ddc_i2c, + CONNECTOR_OBJECT_ID_SVIDEO, + &hpd); + break; + case CT_RN50_POWER: + DRM_INFO("Connector Table: %d (rn50-power)\n", + rdev->mode_info.connector_table); + /* VGA - primary dac */ + ddc_i2c = combios_setup_i2c_bus(rdev, DDC_VGA, 0, 0); + hpd.hpd = RADEON_HPD_NONE; + radeon_add_legacy_encoder(dev, + radeon_get_encoder_enum(dev, + ATOM_DEVICE_CRT1_SUPPORT, + 1), + ATOM_DEVICE_CRT1_SUPPORT); + radeon_add_legacy_connector(dev, 0, ATOM_DEVICE_CRT1_SUPPORT, + DRM_MODE_CONNECTOR_VGA, &ddc_i2c, + CONNECTOR_OBJECT_ID_VGA, + &hpd); + ddc_i2c = combios_setup_i2c_bus(rdev, DDC_CRT2, 0, 0); + hpd.hpd = RADEON_HPD_NONE; + radeon_add_legacy_encoder(dev, + radeon_get_encoder_enum(dev, + ATOM_DEVICE_CRT2_SUPPORT, + 2), + ATOM_DEVICE_CRT2_SUPPORT); + radeon_add_legacy_connector(dev, 1, ATOM_DEVICE_CRT2_SUPPORT, + DRM_MODE_CONNECTOR_VGA, &ddc_i2c, + CONNECTOR_OBJECT_ID_VGA, + &hpd); + break; + case CT_MAC_X800: + DRM_INFO("Connector Table: %d (mac x800)\n", + rdev->mode_info.connector_table); + /* DVI - primary dac, internal tmds */ + ddc_i2c = combios_setup_i2c_bus(rdev, DDC_DVI, 0, 0); + hpd.hpd = RADEON_HPD_1; /* ??? */ + radeon_add_legacy_encoder(dev, + radeon_get_encoder_enum(dev, + ATOM_DEVICE_DFP1_SUPPORT, + 0), + ATOM_DEVICE_DFP1_SUPPORT); + radeon_add_legacy_encoder(dev, + radeon_get_encoder_enum(dev, + ATOM_DEVICE_CRT1_SUPPORT, + 1), + ATOM_DEVICE_CRT1_SUPPORT); + radeon_add_legacy_connector(dev, 0, + ATOM_DEVICE_DFP1_SUPPORT | + ATOM_DEVICE_CRT1_SUPPORT, + DRM_MODE_CONNECTOR_DVII, &ddc_i2c, + CONNECTOR_OBJECT_ID_SINGLE_LINK_DVI_I, + &hpd); + /* DVI - tv dac, dvo */ + ddc_i2c = combios_setup_i2c_bus(rdev, DDC_MONID, 0, 0); + hpd.hpd = RADEON_HPD_2; /* ??? */ + radeon_add_legacy_encoder(dev, + radeon_get_encoder_enum(dev, + ATOM_DEVICE_DFP2_SUPPORT, + 0), + ATOM_DEVICE_DFP2_SUPPORT); + radeon_add_legacy_encoder(dev, + radeon_get_encoder_enum(dev, + ATOM_DEVICE_CRT2_SUPPORT, + 2), + ATOM_DEVICE_CRT2_SUPPORT); + radeon_add_legacy_connector(dev, 1, + ATOM_DEVICE_DFP2_SUPPORT | + ATOM_DEVICE_CRT2_SUPPORT, + DRM_MODE_CONNECTOR_DVII, &ddc_i2c, + CONNECTOR_OBJECT_ID_DUAL_LINK_DVI_I, + &hpd); + break; + case CT_MAC_G5_9600: + DRM_INFO("Connector Table: %d (mac g5 9600)\n", + rdev->mode_info.connector_table); + /* DVI - tv dac, dvo */ + ddc_i2c = combios_setup_i2c_bus(rdev, DDC_DVI, 0, 0); + hpd.hpd = RADEON_HPD_1; /* ??? */ + radeon_add_legacy_encoder(dev, + radeon_get_encoder_enum(dev, + ATOM_DEVICE_DFP2_SUPPORT, + 0), + ATOM_DEVICE_DFP2_SUPPORT); + radeon_add_legacy_encoder(dev, + radeon_get_encoder_enum(dev, + ATOM_DEVICE_CRT2_SUPPORT, + 2), + ATOM_DEVICE_CRT2_SUPPORT); + radeon_add_legacy_connector(dev, 0, + ATOM_DEVICE_DFP2_SUPPORT | + ATOM_DEVICE_CRT2_SUPPORT, + DRM_MODE_CONNECTOR_DVII, &ddc_i2c, + CONNECTOR_OBJECT_ID_SINGLE_LINK_DVI_I, + &hpd); + /* ADC - primary dac, internal tmds */ + ddc_i2c = combios_setup_i2c_bus(rdev, DDC_VGA, 0, 0); + hpd.hpd = RADEON_HPD_2; /* ??? */ + radeon_add_legacy_encoder(dev, + radeon_get_encoder_enum(dev, + ATOM_DEVICE_DFP1_SUPPORT, + 0), + ATOM_DEVICE_DFP1_SUPPORT); + radeon_add_legacy_encoder(dev, + radeon_get_encoder_enum(dev, + ATOM_DEVICE_CRT1_SUPPORT, + 1), + ATOM_DEVICE_CRT1_SUPPORT); + radeon_add_legacy_connector(dev, 1, + ATOM_DEVICE_DFP1_SUPPORT | + ATOM_DEVICE_CRT1_SUPPORT, + DRM_MODE_CONNECTOR_DVII, &ddc_i2c, + CONNECTOR_OBJECT_ID_SINGLE_LINK_DVI_I, + &hpd); + /* TV - TV DAC */ + ddc_i2c.valid = false; + hpd.hpd = RADEON_HPD_NONE; + radeon_add_legacy_encoder(dev, + radeon_get_encoder_enum(dev, + ATOM_DEVICE_TV1_SUPPORT, + 2), + ATOM_DEVICE_TV1_SUPPORT); + radeon_add_legacy_connector(dev, 2, ATOM_DEVICE_TV1_SUPPORT, + DRM_MODE_CONNECTOR_SVIDEO, + &ddc_i2c, + CONNECTOR_OBJECT_ID_SVIDEO, + &hpd); + break; + case CT_SAM440EP: + DRM_INFO("Connector Table: %d (SAM440ep embedded board)\n", + rdev->mode_info.connector_table); + /* LVDS */ + ddc_i2c = combios_setup_i2c_bus(rdev, DDC_NONE_DETECTED, 0, 0); + hpd.hpd = RADEON_HPD_NONE; + radeon_add_legacy_encoder(dev, + radeon_get_encoder_enum(dev, + ATOM_DEVICE_LCD1_SUPPORT, + 0), + ATOM_DEVICE_LCD1_SUPPORT); + radeon_add_legacy_connector(dev, 0, ATOM_DEVICE_LCD1_SUPPORT, + DRM_MODE_CONNECTOR_LVDS, &ddc_i2c, + CONNECTOR_OBJECT_ID_LVDS, + &hpd); + /* DVI-I - secondary dac, int tmds */ + ddc_i2c = combios_setup_i2c_bus(rdev, DDC_DVI, 0, 0); + hpd.hpd = RADEON_HPD_1; /* ??? */ + radeon_add_legacy_encoder(dev, + radeon_get_encoder_enum(dev, + ATOM_DEVICE_DFP1_SUPPORT, + 0), + ATOM_DEVICE_DFP1_SUPPORT); + radeon_add_legacy_encoder(dev, + radeon_get_encoder_enum(dev, + ATOM_DEVICE_CRT2_SUPPORT, + 2), + ATOM_DEVICE_CRT2_SUPPORT); + radeon_add_legacy_connector(dev, 1, + ATOM_DEVICE_DFP1_SUPPORT | + ATOM_DEVICE_CRT2_SUPPORT, + DRM_MODE_CONNECTOR_DVII, &ddc_i2c, + CONNECTOR_OBJECT_ID_SINGLE_LINK_DVI_I, + &hpd); + /* VGA - primary dac */ + ddc_i2c = combios_setup_i2c_bus(rdev, DDC_VGA, 0, 0); + hpd.hpd = RADEON_HPD_NONE; + radeon_add_legacy_encoder(dev, + radeon_get_encoder_enum(dev, + ATOM_DEVICE_CRT1_SUPPORT, + 1), + ATOM_DEVICE_CRT1_SUPPORT); + radeon_add_legacy_connector(dev, 2, + ATOM_DEVICE_CRT1_SUPPORT, + DRM_MODE_CONNECTOR_VGA, &ddc_i2c, + CONNECTOR_OBJECT_ID_VGA, + &hpd); + /* TV - TV DAC */ + ddc_i2c.valid = false; + hpd.hpd = RADEON_HPD_NONE; + radeon_add_legacy_encoder(dev, + radeon_get_encoder_enum(dev, + ATOM_DEVICE_TV1_SUPPORT, + 2), + ATOM_DEVICE_TV1_SUPPORT); + radeon_add_legacy_connector(dev, 3, ATOM_DEVICE_TV1_SUPPORT, + DRM_MODE_CONNECTOR_SVIDEO, + &ddc_i2c, + CONNECTOR_OBJECT_ID_SVIDEO, + &hpd); + break; + case CT_MAC_G4_SILVER: + DRM_INFO("Connector Table: %d (mac g4 silver)\n", + rdev->mode_info.connector_table); + /* DVI-I - tv dac, int tmds */ + ddc_i2c = combios_setup_i2c_bus(rdev, DDC_DVI, 0, 0); + hpd.hpd = RADEON_HPD_1; /* ??? */ + radeon_add_legacy_encoder(dev, + radeon_get_encoder_enum(dev, + ATOM_DEVICE_DFP1_SUPPORT, + 0), + ATOM_DEVICE_DFP1_SUPPORT); + radeon_add_legacy_encoder(dev, + radeon_get_encoder_enum(dev, + ATOM_DEVICE_CRT2_SUPPORT, + 2), + ATOM_DEVICE_CRT2_SUPPORT); + radeon_add_legacy_connector(dev, 0, + ATOM_DEVICE_DFP1_SUPPORT | + ATOM_DEVICE_CRT2_SUPPORT, + DRM_MODE_CONNECTOR_DVII, &ddc_i2c, + CONNECTOR_OBJECT_ID_SINGLE_LINK_DVI_I, + &hpd); + /* VGA - primary dac */ + ddc_i2c = combios_setup_i2c_bus(rdev, DDC_VGA, 0, 0); + hpd.hpd = RADEON_HPD_NONE; + radeon_add_legacy_encoder(dev, + radeon_get_encoder_enum(dev, + ATOM_DEVICE_CRT1_SUPPORT, + 1), + ATOM_DEVICE_CRT1_SUPPORT); + radeon_add_legacy_connector(dev, 1, ATOM_DEVICE_CRT1_SUPPORT, + DRM_MODE_CONNECTOR_VGA, &ddc_i2c, + CONNECTOR_OBJECT_ID_VGA, + &hpd); + /* TV - TV DAC */ + ddc_i2c.valid = false; + hpd.hpd = RADEON_HPD_NONE; + radeon_add_legacy_encoder(dev, + radeon_get_encoder_enum(dev, + ATOM_DEVICE_TV1_SUPPORT, + 2), + ATOM_DEVICE_TV1_SUPPORT); + radeon_add_legacy_connector(dev, 2, ATOM_DEVICE_TV1_SUPPORT, + DRM_MODE_CONNECTOR_SVIDEO, + &ddc_i2c, + CONNECTOR_OBJECT_ID_SVIDEO, + &hpd); + break; + default: + DRM_INFO("Connector table: %d (invalid)\n", + rdev->mode_info.connector_table); + return false; + } + + radeon_link_encoder_connector(dev); + + return true; +} + +static bool radeon_apply_legacy_quirks(struct drm_device *dev, + int bios_index, + enum radeon_combios_connector + *legacy_connector, + struct radeon_i2c_bus_rec *ddc_i2c, + struct radeon_hpd *hpd) +{ + + /* Certain IBM chipset RN50s have a BIOS reporting two VGAs, + one with VGA DDC and one with CRT2 DDC. - kill the CRT2 DDC one */ + if (dev->pci_device == 0x515e && + dev->pci_subvendor == 0x1014) { + if (*legacy_connector == CONNECTOR_CRT_LEGACY && + ddc_i2c->mask_clk_reg == RADEON_GPIO_CRT2_DDC) + return false; + } + + /* X300 card with extra non-existent DVI port */ + if (dev->pci_device == 0x5B60 && + dev->pci_subvendor == 0x17af && + dev->pci_subdevice == 0x201e && bios_index == 2) { + if (*legacy_connector == CONNECTOR_DVI_I_LEGACY) + return false; + } + + return true; +} + +static bool radeon_apply_legacy_tv_quirks(struct drm_device *dev) +{ + /* Acer 5102 has non-existent TV port */ + if (dev->pci_device == 0x5975 && + dev->pci_subvendor == 0x1025 && + dev->pci_subdevice == 0x009f) + return false; + + /* HP dc5750 has non-existent TV port */ + if (dev->pci_device == 0x5974 && + dev->pci_subvendor == 0x103c && + dev->pci_subdevice == 0x280a) + return false; + + /* MSI S270 has non-existent TV port */ + if (dev->pci_device == 0x5955 && + dev->pci_subvendor == 0x1462 && + dev->pci_subdevice == 0x0131) + return false; + + return true; +} + +static uint16_t combios_check_dl_dvi(struct drm_device *dev, int is_dvi_d) +{ + struct radeon_device *rdev = dev->dev_private; + uint32_t ext_tmds_info; + + if (rdev->flags & RADEON_IS_IGP) { + if (is_dvi_d) + return CONNECTOR_OBJECT_ID_SINGLE_LINK_DVI_D; + else + return CONNECTOR_OBJECT_ID_SINGLE_LINK_DVI_I; + } + ext_tmds_info = combios_get_table_offset(dev, COMBIOS_EXT_TMDS_INFO_TABLE); + if (ext_tmds_info) { + uint8_t rev = RBIOS8(ext_tmds_info); + uint8_t flags = RBIOS8(ext_tmds_info + 4 + 5); + if (rev >= 3) { + if (is_dvi_d) + return CONNECTOR_OBJECT_ID_DUAL_LINK_DVI_D; + else + return CONNECTOR_OBJECT_ID_DUAL_LINK_DVI_I; + } else { + if (flags & 1) { + if (is_dvi_d) + return CONNECTOR_OBJECT_ID_DUAL_LINK_DVI_D; + else + return CONNECTOR_OBJECT_ID_DUAL_LINK_DVI_I; + } + } + } + if (is_dvi_d) + return CONNECTOR_OBJECT_ID_SINGLE_LINK_DVI_D; + else + return CONNECTOR_OBJECT_ID_SINGLE_LINK_DVI_I; +} + +bool radeon_get_legacy_connector_info_from_bios(struct drm_device *dev) +{ + struct radeon_device *rdev = dev->dev_private; + uint32_t conn_info, entry, devices; + uint16_t tmp, connector_object_id; + enum radeon_combios_ddc ddc_type; + enum radeon_combios_connector connector; + int i = 0; + struct radeon_i2c_bus_rec ddc_i2c; + struct radeon_hpd hpd; + + conn_info = combios_get_table_offset(dev, COMBIOS_CONNECTOR_INFO_TABLE); + if (conn_info) { + for (i = 0; i < 4; i++) { + entry = conn_info + 2 + i * 2; + + if (!RBIOS16(entry)) + break; + + tmp = RBIOS16(entry); + + connector = (tmp >> 12) & 0xf; + + ddc_type = (tmp >> 8) & 0xf; + if (ddc_type == 5) + ddc_i2c = radeon_combios_get_i2c_info_from_table(rdev); + else + ddc_i2c = combios_setup_i2c_bus(rdev, ddc_type, 0, 0); + + switch (connector) { + case CONNECTOR_PROPRIETARY_LEGACY: + case CONNECTOR_DVI_I_LEGACY: + case CONNECTOR_DVI_D_LEGACY: + if ((tmp >> 4) & 0x1) + hpd.hpd = RADEON_HPD_2; + else + hpd.hpd = RADEON_HPD_1; + break; + default: + hpd.hpd = RADEON_HPD_NONE; + break; + } + + if (!radeon_apply_legacy_quirks(dev, i, &connector, + &ddc_i2c, &hpd)) + continue; + + switch (connector) { + case CONNECTOR_PROPRIETARY_LEGACY: + if ((tmp >> 4) & 0x1) + devices = ATOM_DEVICE_DFP2_SUPPORT; + else + devices = ATOM_DEVICE_DFP1_SUPPORT; + radeon_add_legacy_encoder(dev, + radeon_get_encoder_enum + (dev, devices, 0), + devices); + radeon_add_legacy_connector(dev, i, devices, + legacy_connector_convert + [connector], + &ddc_i2c, + CONNECTOR_OBJECT_ID_SINGLE_LINK_DVI_D, + &hpd); + break; + case CONNECTOR_CRT_LEGACY: + if (tmp & 0x1) { + devices = ATOM_DEVICE_CRT2_SUPPORT; + radeon_add_legacy_encoder(dev, + radeon_get_encoder_enum + (dev, + ATOM_DEVICE_CRT2_SUPPORT, + 2), + ATOM_DEVICE_CRT2_SUPPORT); + } else { + devices = ATOM_DEVICE_CRT1_SUPPORT; + radeon_add_legacy_encoder(dev, + radeon_get_encoder_enum + (dev, + ATOM_DEVICE_CRT1_SUPPORT, + 1), + ATOM_DEVICE_CRT1_SUPPORT); + } + radeon_add_legacy_connector(dev, + i, + devices, + legacy_connector_convert + [connector], + &ddc_i2c, + CONNECTOR_OBJECT_ID_VGA, + &hpd); + break; + case CONNECTOR_DVI_I_LEGACY: + devices = 0; + if (tmp & 0x1) { + devices |= ATOM_DEVICE_CRT2_SUPPORT; + radeon_add_legacy_encoder(dev, + radeon_get_encoder_enum + (dev, + ATOM_DEVICE_CRT2_SUPPORT, + 2), + ATOM_DEVICE_CRT2_SUPPORT); + } else { + devices |= ATOM_DEVICE_CRT1_SUPPORT; + radeon_add_legacy_encoder(dev, + radeon_get_encoder_enum + (dev, + ATOM_DEVICE_CRT1_SUPPORT, + 1), + ATOM_DEVICE_CRT1_SUPPORT); + } + /* RV100 board with external TDMS bit mis-set. + * Actually uses internal TMDS, clear the bit. + */ + if (dev->pci_device == 0x5159 && + dev->pci_subvendor == 0x1014 && + dev->pci_subdevice == 0x029A) { + tmp &= ~(1 << 4); + } + if ((tmp >> 4) & 0x1) { + devices |= ATOM_DEVICE_DFP2_SUPPORT; + radeon_add_legacy_encoder(dev, + radeon_get_encoder_enum + (dev, + ATOM_DEVICE_DFP2_SUPPORT, + 0), + ATOM_DEVICE_DFP2_SUPPORT); + connector_object_id = combios_check_dl_dvi(dev, 0); + } else { + devices |= ATOM_DEVICE_DFP1_SUPPORT; + radeon_add_legacy_encoder(dev, + radeon_get_encoder_enum + (dev, + ATOM_DEVICE_DFP1_SUPPORT, + 0), + ATOM_DEVICE_DFP1_SUPPORT); + connector_object_id = CONNECTOR_OBJECT_ID_SINGLE_LINK_DVI_I; + } + radeon_add_legacy_connector(dev, + i, + devices, + legacy_connector_convert + [connector], + &ddc_i2c, + connector_object_id, + &hpd); + break; + case CONNECTOR_DVI_D_LEGACY: + if ((tmp >> 4) & 0x1) { + devices = ATOM_DEVICE_DFP2_SUPPORT; + connector_object_id = combios_check_dl_dvi(dev, 1); + } else { + devices = ATOM_DEVICE_DFP1_SUPPORT; + connector_object_id = CONNECTOR_OBJECT_ID_SINGLE_LINK_DVI_I; + } + radeon_add_legacy_encoder(dev, + radeon_get_encoder_enum + (dev, devices, 0), + devices); + radeon_add_legacy_connector(dev, i, devices, + legacy_connector_convert + [connector], + &ddc_i2c, + connector_object_id, + &hpd); + break; + case CONNECTOR_CTV_LEGACY: + case CONNECTOR_STV_LEGACY: + radeon_add_legacy_encoder(dev, + radeon_get_encoder_enum + (dev, + ATOM_DEVICE_TV1_SUPPORT, + 2), + ATOM_DEVICE_TV1_SUPPORT); + radeon_add_legacy_connector(dev, i, + ATOM_DEVICE_TV1_SUPPORT, + legacy_connector_convert + [connector], + &ddc_i2c, + CONNECTOR_OBJECT_ID_SVIDEO, + &hpd); + break; + default: + DRM_ERROR("Unknown connector type: %d\n", + connector); + continue; + } + + } + } else { + uint16_t tmds_info = + combios_get_table_offset(dev, COMBIOS_DFP_INFO_TABLE); + if (tmds_info) { + DRM_DEBUG_KMS("Found DFP table, assuming DVI connector\n"); + + radeon_add_legacy_encoder(dev, + radeon_get_encoder_enum(dev, + ATOM_DEVICE_CRT1_SUPPORT, + 1), + ATOM_DEVICE_CRT1_SUPPORT); + radeon_add_legacy_encoder(dev, + radeon_get_encoder_enum(dev, + ATOM_DEVICE_DFP1_SUPPORT, + 0), + ATOM_DEVICE_DFP1_SUPPORT); + + ddc_i2c = combios_setup_i2c_bus(rdev, DDC_DVI, 0, 0); + hpd.hpd = RADEON_HPD_1; + radeon_add_legacy_connector(dev, + 0, + ATOM_DEVICE_CRT1_SUPPORT | + ATOM_DEVICE_DFP1_SUPPORT, + DRM_MODE_CONNECTOR_DVII, + &ddc_i2c, + CONNECTOR_OBJECT_ID_SINGLE_LINK_DVI_I, + &hpd); + } else { + uint16_t crt_info = + combios_get_table_offset(dev, COMBIOS_CRT_INFO_TABLE); + DRM_DEBUG_KMS("Found CRT table, assuming VGA connector\n"); + if (crt_info) { + radeon_add_legacy_encoder(dev, + radeon_get_encoder_enum(dev, + ATOM_DEVICE_CRT1_SUPPORT, + 1), + ATOM_DEVICE_CRT1_SUPPORT); + ddc_i2c = combios_setup_i2c_bus(rdev, DDC_VGA, 0, 0); + hpd.hpd = RADEON_HPD_NONE; + radeon_add_legacy_connector(dev, + 0, + ATOM_DEVICE_CRT1_SUPPORT, + DRM_MODE_CONNECTOR_VGA, + &ddc_i2c, + CONNECTOR_OBJECT_ID_VGA, + &hpd); + } else { + DRM_DEBUG_KMS("No connector info found\n"); + return false; + } + } + } + + if (rdev->flags & RADEON_IS_MOBILITY || rdev->flags & RADEON_IS_IGP) { + uint16_t lcd_info = + combios_get_table_offset(dev, COMBIOS_LCD_INFO_TABLE); + if (lcd_info) { + uint16_t lcd_ddc_info = + combios_get_table_offset(dev, + COMBIOS_LCD_DDC_INFO_TABLE); + + radeon_add_legacy_encoder(dev, + radeon_get_encoder_enum(dev, + ATOM_DEVICE_LCD1_SUPPORT, + 0), + ATOM_DEVICE_LCD1_SUPPORT); + + if (lcd_ddc_info) { + ddc_type = RBIOS8(lcd_ddc_info + 2); + switch (ddc_type) { + case DDC_LCD: + ddc_i2c = + combios_setup_i2c_bus(rdev, + DDC_LCD, + RBIOS32(lcd_ddc_info + 3), + RBIOS32(lcd_ddc_info + 7)); + radeon_i2c_add(rdev, &ddc_i2c, "LCD"); + break; + case DDC_GPIO: + ddc_i2c = + combios_setup_i2c_bus(rdev, + DDC_GPIO, + RBIOS32(lcd_ddc_info + 3), + RBIOS32(lcd_ddc_info + 7)); + radeon_i2c_add(rdev, &ddc_i2c, "LCD"); + break; + default: + ddc_i2c = + combios_setup_i2c_bus(rdev, ddc_type, 0, 0); + break; + } + DRM_DEBUG_KMS("LCD DDC Info Table found!\n"); + } else + ddc_i2c.valid = false; + + hpd.hpd = RADEON_HPD_NONE; + radeon_add_legacy_connector(dev, + 5, + ATOM_DEVICE_LCD1_SUPPORT, + DRM_MODE_CONNECTOR_LVDS, + &ddc_i2c, + CONNECTOR_OBJECT_ID_LVDS, + &hpd); + } + } + + /* check TV table */ + if (rdev->family != CHIP_R100 && rdev->family != CHIP_R200) { + uint32_t tv_info = + combios_get_table_offset(dev, COMBIOS_TV_INFO_TABLE); + if (tv_info) { + if (RBIOS8(tv_info + 6) == 'T') { + if (radeon_apply_legacy_tv_quirks(dev)) { + hpd.hpd = RADEON_HPD_NONE; + ddc_i2c.valid = false; + radeon_add_legacy_encoder(dev, + radeon_get_encoder_enum + (dev, + ATOM_DEVICE_TV1_SUPPORT, + 2), + ATOM_DEVICE_TV1_SUPPORT); + radeon_add_legacy_connector(dev, 6, + ATOM_DEVICE_TV1_SUPPORT, + DRM_MODE_CONNECTOR_SVIDEO, + &ddc_i2c, + CONNECTOR_OBJECT_ID_SVIDEO, + &hpd); + } + } + } + } + + radeon_link_encoder_connector(dev); + + return true; +} + +static const char *thermal_controller_names[] = { + "NONE", + "lm63", + "adm1032", +}; + +void radeon_combios_get_power_modes(struct radeon_device *rdev) +{ + struct drm_device *dev = rdev->ddev; + u16 offset, misc, misc2 = 0; + u8 rev, blocks, tmp; + int state_index = 0; + struct radeon_i2c_bus_rec i2c_bus; + + rdev->pm.default_power_state_index = -1; + + /* allocate 2 power states */ + rdev->pm.power_state = malloc(sizeof(struct radeon_power_state) * 2, + DRM_MEM_DRIVER, M_WAITOK | M_ZERO); + if (rdev->pm.power_state) { + /* allocate 1 clock mode per state */ + rdev->pm.power_state[0].clock_info = + malloc(sizeof(struct radeon_pm_clock_info) * 1, + DRM_MEM_DRIVER, M_WAITOK | M_ZERO); + rdev->pm.power_state[1].clock_info = + malloc(sizeof(struct radeon_pm_clock_info) * 1, + DRM_MEM_DRIVER, M_WAITOK | M_ZERO); + if (!rdev->pm.power_state[0].clock_info || + !rdev->pm.power_state[1].clock_info) + goto pm_failed; + } else + goto pm_failed; + + /* check for a thermal chip */ + offset = combios_get_table_offset(dev, COMBIOS_OVERDRIVE_INFO_TABLE); + if (offset) { + u8 thermal_controller = 0, gpio = 0, i2c_addr = 0, clk_bit = 0, data_bit = 0; + + rev = RBIOS8(offset); + + if (rev == 0) { + thermal_controller = RBIOS8(offset + 3); + gpio = RBIOS8(offset + 4) & 0x3f; + i2c_addr = RBIOS8(offset + 5); + } else if (rev == 1) { + thermal_controller = RBIOS8(offset + 4); + gpio = RBIOS8(offset + 5) & 0x3f; + i2c_addr = RBIOS8(offset + 6); + } else if (rev == 2) { + thermal_controller = RBIOS8(offset + 4); + gpio = RBIOS8(offset + 5) & 0x3f; + i2c_addr = RBIOS8(offset + 6); + clk_bit = RBIOS8(offset + 0xa); + data_bit = RBIOS8(offset + 0xb); + } + if ((thermal_controller > 0) && (thermal_controller < 3)) { + DRM_INFO("Possible %s thermal controller at 0x%02x\n", + thermal_controller_names[thermal_controller], + i2c_addr >> 1); + if (gpio == DDC_LCD) { + /* MM i2c */ + i2c_bus.valid = true; + i2c_bus.hw_capable = true; + i2c_bus.mm_i2c = true; + i2c_bus.i2c_id = 0xa0; + } else if (gpio == DDC_GPIO) + i2c_bus = combios_setup_i2c_bus(rdev, gpio, 1 << clk_bit, 1 << data_bit); + else + i2c_bus = combios_setup_i2c_bus(rdev, gpio, 0, 0); + rdev->pm.i2c_bus = radeon_i2c_lookup(rdev, &i2c_bus); + if (rdev->pm.i2c_bus) { +#ifdef DUMBBELL_WIP + struct i2c_board_info info = { }; + const char *name = thermal_controller_names[thermal_controller]; + info.addr = i2c_addr >> 1; + strlcpy(info.type, name, sizeof(info.type)); + i2c_new_device(&rdev->pm.i2c_bus->adapter, &info); +#endif /* DUMBBELL_WIP */ + } + } + } else { + /* boards with a thermal chip, but no overdrive table */ + + /* Asus 9600xt has an f75375 on the monid bus */ + if ((dev->pci_device == 0x4152) && + (dev->pci_subvendor == 0x1043) && + (dev->pci_subdevice == 0xc002)) { + i2c_bus = combios_setup_i2c_bus(rdev, DDC_MONID, 0, 0); + rdev->pm.i2c_bus = radeon_i2c_lookup(rdev, &i2c_bus); + if (rdev->pm.i2c_bus) { +#ifdef DUMBBELL_WIP + struct i2c_board_info info = { }; + const char *name = "f75375"; + info.addr = 0x28; + strlcpy(info.type, name, sizeof(info.type)); + i2c_new_device(&rdev->pm.i2c_bus->adapter, &info); + DRM_INFO("Possible %s thermal controller at 0x%02x\n", + name, info.addr); +#endif /* DUMBBELL_WIP */ + } + } + } + + if (rdev->flags & RADEON_IS_MOBILITY) { + offset = combios_get_table_offset(dev, COMBIOS_POWERPLAY_INFO_TABLE); + if (offset) { + rev = RBIOS8(offset); + blocks = RBIOS8(offset + 0x2); + /* power mode 0 tends to be the only valid one */ + rdev->pm.power_state[state_index].num_clock_modes = 1; + rdev->pm.power_state[state_index].clock_info[0].mclk = RBIOS32(offset + 0x5 + 0x2); + rdev->pm.power_state[state_index].clock_info[0].sclk = RBIOS32(offset + 0x5 + 0x6); + if ((rdev->pm.power_state[state_index].clock_info[0].mclk == 0) || + (rdev->pm.power_state[state_index].clock_info[0].sclk == 0)) + goto default_mode; + rdev->pm.power_state[state_index].type = + POWER_STATE_TYPE_BATTERY; + misc = RBIOS16(offset + 0x5 + 0x0); + if (rev > 4) + misc2 = RBIOS16(offset + 0x5 + 0xe); + rdev->pm.power_state[state_index].misc = misc; + rdev->pm.power_state[state_index].misc2 = misc2; + if (misc & 0x4) { + rdev->pm.power_state[state_index].clock_info[0].voltage.type = VOLTAGE_GPIO; + if (misc & 0x8) + rdev->pm.power_state[state_index].clock_info[0].voltage.active_high = + true; + else + rdev->pm.power_state[state_index].clock_info[0].voltage.active_high = + false; + rdev->pm.power_state[state_index].clock_info[0].voltage.gpio.valid = true; + if (rev < 6) { + rdev->pm.power_state[state_index].clock_info[0].voltage.gpio.reg = + RBIOS16(offset + 0x5 + 0xb) * 4; + tmp = RBIOS8(offset + 0x5 + 0xd); + rdev->pm.power_state[state_index].clock_info[0].voltage.gpio.mask = (1 << tmp); + } else { + u8 entries = RBIOS8(offset + 0x5 + 0xb); + u16 voltage_table_offset = RBIOS16(offset + 0x5 + 0xc); + if (entries && voltage_table_offset) { + rdev->pm.power_state[state_index].clock_info[0].voltage.gpio.reg = + RBIOS16(voltage_table_offset) * 4; + tmp = RBIOS8(voltage_table_offset + 0x2); + rdev->pm.power_state[state_index].clock_info[0].voltage.gpio.mask = (1 << tmp); + } else + rdev->pm.power_state[state_index].clock_info[0].voltage.gpio.valid = false; + } + switch ((misc2 & 0x700) >> 8) { + case 0: + default: + rdev->pm.power_state[state_index].clock_info[0].voltage.delay = 0; + break; + case 1: + rdev->pm.power_state[state_index].clock_info[0].voltage.delay = 33; + break; + case 2: + rdev->pm.power_state[state_index].clock_info[0].voltage.delay = 66; + break; + case 3: + rdev->pm.power_state[state_index].clock_info[0].voltage.delay = 99; + break; + case 4: + rdev->pm.power_state[state_index].clock_info[0].voltage.delay = 132; + break; + } + } else + rdev->pm.power_state[state_index].clock_info[0].voltage.type = VOLTAGE_NONE; + if (rev > 6) + rdev->pm.power_state[state_index].pcie_lanes = + RBIOS8(offset + 0x5 + 0x10); + rdev->pm.power_state[state_index].flags = RADEON_PM_STATE_SINGLE_DISPLAY_ONLY; + state_index++; + } else { + /* XXX figure out some good default low power mode for mobility cards w/out power tables */ + } + } else { + /* XXX figure out some good default low power mode for desktop cards */ + } + +default_mode: + /* add the default mode */ + rdev->pm.power_state[state_index].type = + POWER_STATE_TYPE_DEFAULT; + rdev->pm.power_state[state_index].num_clock_modes = 1; + rdev->pm.power_state[state_index].clock_info[0].mclk = rdev->clock.default_mclk; + rdev->pm.power_state[state_index].clock_info[0].sclk = rdev->clock.default_sclk; + rdev->pm.power_state[state_index].default_clock_mode = &rdev->pm.power_state[state_index].clock_info[0]; + if ((state_index > 0) && + (rdev->pm.power_state[0].clock_info[0].voltage.type == VOLTAGE_GPIO)) + rdev->pm.power_state[state_index].clock_info[0].voltage = + rdev->pm.power_state[0].clock_info[0].voltage; + else + rdev->pm.power_state[state_index].clock_info[0].voltage.type = VOLTAGE_NONE; + rdev->pm.power_state[state_index].pcie_lanes = 16; + rdev->pm.power_state[state_index].flags = 0; + rdev->pm.default_power_state_index = state_index; + rdev->pm.num_power_states = state_index + 1; + + rdev->pm.current_power_state_index = rdev->pm.default_power_state_index; + rdev->pm.current_clock_mode_index = 0; + return; + +pm_failed: + rdev->pm.default_power_state_index = state_index; + rdev->pm.num_power_states = 0; + + rdev->pm.current_power_state_index = rdev->pm.default_power_state_index; + rdev->pm.current_clock_mode_index = 0; +} + +void radeon_external_tmds_setup(struct drm_encoder *encoder) +{ + struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); + struct radeon_encoder_ext_tmds *tmds = radeon_encoder->enc_priv; + + if (!tmds) + return; + + switch (tmds->dvo_chip) { + case DVO_SIL164: + /* sil 164 */ + radeon_i2c_put_byte(tmds->i2c_bus, + tmds->slave_addr, + 0x08, 0x30); + radeon_i2c_put_byte(tmds->i2c_bus, + tmds->slave_addr, + 0x09, 0x00); + radeon_i2c_put_byte(tmds->i2c_bus, + tmds->slave_addr, + 0x0a, 0x90); + radeon_i2c_put_byte(tmds->i2c_bus, + tmds->slave_addr, + 0x0c, 0x89); + radeon_i2c_put_byte(tmds->i2c_bus, + tmds->slave_addr, + 0x08, 0x3b); + break; + case DVO_SIL1178: + /* sil 1178 - untested */ + /* + * 0x0f, 0x44 + * 0x0f, 0x4c + * 0x0e, 0x01 + * 0x0a, 0x80 + * 0x09, 0x30 + * 0x0c, 0xc9 + * 0x0d, 0x70 + * 0x08, 0x32 + * 0x08, 0x33 + */ + break; + default: + break; + } + +} + +bool radeon_combios_external_tmds_setup(struct drm_encoder *encoder) +{ + struct drm_device *dev = encoder->dev; + struct radeon_device *rdev = dev->dev_private; + struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); + uint16_t offset; + uint8_t blocks, slave_addr, rev; + uint32_t index, id; + uint32_t reg, val, and_mask, or_mask; + struct radeon_encoder_ext_tmds *tmds = radeon_encoder->enc_priv; + + if (!tmds) + return false; + + if (rdev->flags & RADEON_IS_IGP) { + offset = combios_get_table_offset(dev, COMBIOS_TMDS_POWER_ON_TABLE); + rev = RBIOS8(offset); + if (offset) { + rev = RBIOS8(offset); + if (rev > 1) { + blocks = RBIOS8(offset + 3); + index = offset + 4; + while (blocks > 0) { + id = RBIOS16(index); + index += 2; + switch (id >> 13) { + case 0: + reg = (id & 0x1fff) * 4; + val = RBIOS32(index); + index += 4; + WREG32(reg, val); + break; + case 2: + reg = (id & 0x1fff) * 4; + and_mask = RBIOS32(index); + index += 4; + or_mask = RBIOS32(index); + index += 4; + val = RREG32(reg); + val = (val & and_mask) | or_mask; + WREG32(reg, val); + break; + case 3: + val = RBIOS16(index); + index += 2; + DRM_UDELAY(val); + break; + case 4: + val = RBIOS16(index); + index += 2; + DRM_MDELAY(val); + break; + case 6: + slave_addr = id & 0xff; + slave_addr >>= 1; /* 7 bit addressing */ + index++; + reg = RBIOS8(index); + index++; + val = RBIOS8(index); + index++; + radeon_i2c_put_byte(tmds->i2c_bus, + slave_addr, + reg, val); + break; + default: + DRM_ERROR("Unknown id %d\n", id >> 13); + break; + } + blocks--; + } + return true; + } + } + } else { + offset = combios_get_table_offset(dev, COMBIOS_EXT_TMDS_INFO_TABLE); + if (offset) { + index = offset + 10; + id = RBIOS16(index); + while (id != 0xffff) { + index += 2; + switch (id >> 13) { + case 0: + reg = (id & 0x1fff) * 4; + val = RBIOS32(index); + WREG32(reg, val); + break; + case 2: + reg = (id & 0x1fff) * 4; + and_mask = RBIOS32(index); + index += 4; + or_mask = RBIOS32(index); + index += 4; + val = RREG32(reg); + val = (val & and_mask) | or_mask; + WREG32(reg, val); + break; + case 4: + val = RBIOS16(index); + index += 2; + DRM_UDELAY(val); + break; + case 5: + reg = id & 0x1fff; + and_mask = RBIOS32(index); + index += 4; + or_mask = RBIOS32(index); + index += 4; + val = RREG32_PLL(reg); + val = (val & and_mask) | or_mask; + WREG32_PLL(reg, val); + break; + case 6: + reg = id & 0x1fff; + val = RBIOS8(index); + index += 1; + radeon_i2c_put_byte(tmds->i2c_bus, + tmds->slave_addr, + reg, val); + break; + default: + DRM_ERROR("Unknown id %d\n", id >> 13); + break; + } + id = RBIOS16(index); + } + return true; + } + } + return false; +} + +static void combios_parse_mmio_table(struct drm_device *dev, uint16_t offset) +{ + struct radeon_device *rdev = dev->dev_private; + + if (offset) { + while (RBIOS16(offset)) { + uint16_t cmd = ((RBIOS16(offset) & 0xe000) >> 13); + uint32_t addr = (RBIOS16(offset) & 0x1fff); + uint32_t val, and_mask, or_mask; + uint32_t tmp; + + offset += 2; + switch (cmd) { + case 0: + val = RBIOS32(offset); + offset += 4; + WREG32(addr, val); + break; + case 1: + val = RBIOS32(offset); + offset += 4; + WREG32(addr, val); + break; + case 2: + and_mask = RBIOS32(offset); + offset += 4; + or_mask = RBIOS32(offset); + offset += 4; + tmp = RREG32(addr); + tmp &= and_mask; + tmp |= or_mask; + WREG32(addr, tmp); + break; + case 3: + and_mask = RBIOS32(offset); + offset += 4; + or_mask = RBIOS32(offset); + offset += 4; + tmp = RREG32(addr); + tmp &= and_mask; + tmp |= or_mask; + WREG32(addr, tmp); + break; + case 4: + val = RBIOS16(offset); + offset += 2; + DRM_UDELAY(val); + break; + case 5: + val = RBIOS16(offset); + offset += 2; + switch (addr) { + case 8: + while (val--) { + if (! + (RREG32_PLL + (RADEON_CLK_PWRMGT_CNTL) & + RADEON_MC_BUSY)) + break; + } + break; + case 9: + while (val--) { + if ((RREG32(RADEON_MC_STATUS) & + RADEON_MC_IDLE)) + break; + } + break; + default: + break; + } + break; + default: + break; + } + } + } +} + +static void combios_parse_pll_table(struct drm_device *dev, uint16_t offset) +{ + struct radeon_device *rdev = dev->dev_private; + + if (offset) { + while (RBIOS8(offset)) { + uint8_t cmd = ((RBIOS8(offset) & 0xc0) >> 6); + uint8_t addr = (RBIOS8(offset) & 0x3f); + uint32_t val, shift, tmp; + uint32_t and_mask, or_mask; + + offset++; + switch (cmd) { + case 0: + val = RBIOS32(offset); + offset += 4; + WREG32_PLL(addr, val); + break; + case 1: + shift = RBIOS8(offset) * 8; + offset++; + and_mask = RBIOS8(offset) << shift; + and_mask |= ~(0xff << shift); + offset++; + or_mask = RBIOS8(offset) << shift; + offset++; + tmp = RREG32_PLL(addr); + tmp &= and_mask; + tmp |= or_mask; + WREG32_PLL(addr, tmp); + break; + case 2: + case 3: + tmp = 1000; + switch (addr) { + case 1: + DRM_UDELAY(150); + break; + case 2: + DRM_MDELAY(1); + break; + case 3: + while (tmp--) { + if (! + (RREG32_PLL + (RADEON_CLK_PWRMGT_CNTL) & + RADEON_MC_BUSY)) + break; + } + break; + case 4: + while (tmp--) { + if (RREG32_PLL + (RADEON_CLK_PWRMGT_CNTL) & + RADEON_DLL_READY) + break; + } + break; + case 5: + tmp = + RREG32_PLL(RADEON_CLK_PWRMGT_CNTL); + if (tmp & RADEON_CG_NO1_DEBUG_0) { +#if 0 + uint32_t mclk_cntl = + RREG32_PLL + (RADEON_MCLK_CNTL); + mclk_cntl &= 0xffff0000; + /*mclk_cntl |= 0x00001111;*//* ??? */ + WREG32_PLL(RADEON_MCLK_CNTL, + mclk_cntl); + DRM_MDELAY(10); +#endif + WREG32_PLL + (RADEON_CLK_PWRMGT_CNTL, + tmp & + ~RADEON_CG_NO1_DEBUG_0); + DRM_MDELAY(10); + } + break; + default: + break; + } + break; + default: + break; + } + } + } +} + +static void combios_parse_ram_reset_table(struct drm_device *dev, + uint16_t offset) +{ + struct radeon_device *rdev = dev->dev_private; + uint32_t tmp; + + if (offset) { + uint8_t val = RBIOS8(offset); + while (val != 0xff) { + offset++; + + if (val == 0x0f) { + uint32_t channel_complete_mask; + + if (ASIC_IS_R300(rdev)) + channel_complete_mask = + R300_MEM_PWRUP_COMPLETE; + else + channel_complete_mask = + RADEON_MEM_PWRUP_COMPLETE; + tmp = 20000; + while (tmp--) { + if ((RREG32(RADEON_MEM_STR_CNTL) & + channel_complete_mask) == + channel_complete_mask) + break; + } + } else { + uint32_t or_mask = RBIOS16(offset); + offset += 2; + + tmp = RREG32(RADEON_MEM_SDRAM_MODE_REG); + tmp &= RADEON_SDRAM_MODE_MASK; + tmp |= or_mask; + WREG32(RADEON_MEM_SDRAM_MODE_REG, tmp); + + or_mask = val << 24; + tmp = RREG32(RADEON_MEM_SDRAM_MODE_REG); + tmp &= RADEON_B3MEM_RESET_MASK; + tmp |= or_mask; + WREG32(RADEON_MEM_SDRAM_MODE_REG, tmp); + } + val = RBIOS8(offset); + } + } +} + +static uint32_t combios_detect_ram(struct drm_device *dev, int ram, + int mem_addr_mapping) +{ + struct radeon_device *rdev = dev->dev_private; + uint32_t mem_cntl; + uint32_t mem_size; + uint32_t addr = 0; + + mem_cntl = RREG32(RADEON_MEM_CNTL); + if (mem_cntl & RV100_HALF_MODE) + ram /= 2; + mem_size = ram; + mem_cntl &= ~(0xff << 8); + mem_cntl |= (mem_addr_mapping & 0xff) << 8; + WREG32(RADEON_MEM_CNTL, mem_cntl); + RREG32(RADEON_MEM_CNTL); + + /* sdram reset ? */ + + /* something like this???? */ + while (ram--) { + addr = ram * 1024 * 1024; + /* write to each page */ + WREG32_IDX((addr) | RADEON_MM_APER, 0xdeadbeef); + /* read back and verify */ + if (RREG32_IDX((addr) | RADEON_MM_APER) != 0xdeadbeef) + return 0; + } + + return mem_size; +} + +static void combios_write_ram_size(struct drm_device *dev) +{ + struct radeon_device *rdev = dev->dev_private; + uint8_t rev; + uint16_t offset; + uint32_t mem_size = 0; + uint32_t mem_cntl = 0; + + /* should do something smarter here I guess... */ + if (rdev->flags & RADEON_IS_IGP) + return; + + /* first check detected mem table */ + offset = combios_get_table_offset(dev, COMBIOS_DETECTED_MEM_TABLE); + if (offset) { + rev = RBIOS8(offset); + if (rev < 3) { + mem_cntl = RBIOS32(offset + 1); + mem_size = RBIOS16(offset + 5); + if ((rdev->family < CHIP_R200) && + !ASIC_IS_RN50(rdev)) + WREG32(RADEON_MEM_CNTL, mem_cntl); + } + } + + if (!mem_size) { + offset = + combios_get_table_offset(dev, COMBIOS_MEM_CONFIG_TABLE); + if (offset) { + rev = RBIOS8(offset - 1); + if (rev < 1) { + if ((rdev->family < CHIP_R200) + && !ASIC_IS_RN50(rdev)) { + int ram = 0; + int mem_addr_mapping = 0; + + while (RBIOS8(offset)) { + ram = RBIOS8(offset); + mem_addr_mapping = + RBIOS8(offset + 1); + if (mem_addr_mapping != 0x25) + ram *= 2; + mem_size = + combios_detect_ram(dev, ram, + mem_addr_mapping); + if (mem_size) + break; + offset += 2; + } + } else + mem_size = RBIOS8(offset); + } else { + mem_size = RBIOS8(offset); + mem_size *= 2; /* convert to MB */ + } + } + } + + mem_size *= (1024 * 1024); /* convert to bytes */ + WREG32(RADEON_CONFIG_MEMSIZE, mem_size); +} + +void radeon_combios_asic_init(struct drm_device *dev) +{ + struct radeon_device *rdev = dev->dev_private; + uint16_t table; + + /* port hardcoded mac stuff from radeonfb */ + if (rdev->bios == NULL) + return; + + /* ASIC INIT 1 */ + table = combios_get_table_offset(dev, COMBIOS_ASIC_INIT_1_TABLE); + if (table) + combios_parse_mmio_table(dev, table); + + /* PLL INIT */ + table = combios_get_table_offset(dev, COMBIOS_PLL_INIT_TABLE); + if (table) + combios_parse_pll_table(dev, table); + + /* ASIC INIT 2 */ + table = combios_get_table_offset(dev, COMBIOS_ASIC_INIT_2_TABLE); + if (table) + combios_parse_mmio_table(dev, table); + + if (!(rdev->flags & RADEON_IS_IGP)) { + /* ASIC INIT 4 */ + table = + combios_get_table_offset(dev, COMBIOS_ASIC_INIT_4_TABLE); + if (table) + combios_parse_mmio_table(dev, table); + + /* RAM RESET */ + table = combios_get_table_offset(dev, COMBIOS_RAM_RESET_TABLE); + if (table) + combios_parse_ram_reset_table(dev, table); + + /* ASIC INIT 3 */ + table = + combios_get_table_offset(dev, COMBIOS_ASIC_INIT_3_TABLE); + if (table) + combios_parse_mmio_table(dev, table); + + /* write CONFIG_MEMSIZE */ + combios_write_ram_size(dev); + } + + /* quirk for rs4xx HP nx6125 laptop to make it resume + * - it hangs on resume inside the dynclk 1 table. + */ + if (rdev->family == CHIP_RS480 && + dev->pci_subvendor == 0x103c && + dev->pci_subdevice == 0x308b) + return; + + /* quirk for rs4xx HP dv5000 laptop to make it resume + * - it hangs on resume inside the dynclk 1 table. + */ + if (rdev->family == CHIP_RS480 && + dev->pci_subvendor == 0x103c && + dev->pci_subdevice == 0x30a4) + return; + + /* quirk for rs4xx Compaq Presario V5245EU laptop to make it resume + * - it hangs on resume inside the dynclk 1 table. + */ + if (rdev->family == CHIP_RS480 && + dev->pci_subvendor == 0x103c && + dev->pci_subdevice == 0x30ae) + return; + + /* DYN CLK 1 */ + table = combios_get_table_offset(dev, COMBIOS_DYN_CLK_1_TABLE); + if (table) + combios_parse_pll_table(dev, table); + +} + +void radeon_combios_initialize_bios_scratch_regs(struct drm_device *dev) +{ + struct radeon_device *rdev = dev->dev_private; + uint32_t bios_0_scratch, bios_6_scratch, bios_7_scratch; + + bios_0_scratch = RREG32(RADEON_BIOS_0_SCRATCH); + bios_6_scratch = RREG32(RADEON_BIOS_6_SCRATCH); + bios_7_scratch = RREG32(RADEON_BIOS_7_SCRATCH); + + /* let the bios control the backlight */ + bios_0_scratch &= ~RADEON_DRIVER_BRIGHTNESS_EN; + + /* tell the bios not to handle mode switching */ + bios_6_scratch |= (RADEON_DISPLAY_SWITCHING_DIS | + RADEON_ACC_MODE_CHANGE); + + /* tell the bios a driver is loaded */ + bios_7_scratch |= RADEON_DRV_LOADED; + + WREG32(RADEON_BIOS_0_SCRATCH, bios_0_scratch); + WREG32(RADEON_BIOS_6_SCRATCH, bios_6_scratch); + WREG32(RADEON_BIOS_7_SCRATCH, bios_7_scratch); +} + +void radeon_combios_output_lock(struct drm_encoder *encoder, bool lock) +{ + struct drm_device *dev = encoder->dev; + struct radeon_device *rdev = dev->dev_private; + uint32_t bios_6_scratch; + + bios_6_scratch = RREG32(RADEON_BIOS_6_SCRATCH); + + if (lock) + bios_6_scratch |= RADEON_DRIVER_CRITICAL; + else + bios_6_scratch &= ~RADEON_DRIVER_CRITICAL; + + WREG32(RADEON_BIOS_6_SCRATCH, bios_6_scratch); +} + +void +radeon_combios_connected_scratch_regs(struct drm_connector *connector, + struct drm_encoder *encoder, + bool connected) +{ + struct drm_device *dev = connector->dev; + struct radeon_device *rdev = dev->dev_private; + struct radeon_connector *radeon_connector = + to_radeon_connector(connector); + struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); + uint32_t bios_4_scratch = RREG32(RADEON_BIOS_4_SCRATCH); + uint32_t bios_5_scratch = RREG32(RADEON_BIOS_5_SCRATCH); + + if ((radeon_encoder->devices & ATOM_DEVICE_TV1_SUPPORT) && + (radeon_connector->devices & ATOM_DEVICE_TV1_SUPPORT)) { + if (connected) { + DRM_DEBUG_KMS("TV1 connected\n"); + /* fix me */ + bios_4_scratch |= RADEON_TV1_ATTACHED_SVIDEO; + /*save->bios_4_scratch |= RADEON_TV1_ATTACHED_COMP; */ + bios_5_scratch |= RADEON_TV1_ON; + bios_5_scratch |= RADEON_ACC_REQ_TV1; + } else { + DRM_DEBUG_KMS("TV1 disconnected\n"); + bios_4_scratch &= ~RADEON_TV1_ATTACHED_MASK; + bios_5_scratch &= ~RADEON_TV1_ON; + bios_5_scratch &= ~RADEON_ACC_REQ_TV1; + } + } + if ((radeon_encoder->devices & ATOM_DEVICE_LCD1_SUPPORT) && + (radeon_connector->devices & ATOM_DEVICE_LCD1_SUPPORT)) { + if (connected) { + DRM_DEBUG_KMS("LCD1 connected\n"); + bios_4_scratch |= RADEON_LCD1_ATTACHED; + bios_5_scratch |= RADEON_LCD1_ON; + bios_5_scratch |= RADEON_ACC_REQ_LCD1; + } else { + DRM_DEBUG_KMS("LCD1 disconnected\n"); + bios_4_scratch &= ~RADEON_LCD1_ATTACHED; + bios_5_scratch &= ~RADEON_LCD1_ON; + bios_5_scratch &= ~RADEON_ACC_REQ_LCD1; + } + } + if ((radeon_encoder->devices & ATOM_DEVICE_CRT1_SUPPORT) && + (radeon_connector->devices & ATOM_DEVICE_CRT1_SUPPORT)) { + if (connected) { + DRM_DEBUG_KMS("CRT1 connected\n"); + bios_4_scratch |= RADEON_CRT1_ATTACHED_COLOR; + bios_5_scratch |= RADEON_CRT1_ON; + bios_5_scratch |= RADEON_ACC_REQ_CRT1; + } else { + DRM_DEBUG_KMS("CRT1 disconnected\n"); + bios_4_scratch &= ~RADEON_CRT1_ATTACHED_MASK; + bios_5_scratch &= ~RADEON_CRT1_ON; + bios_5_scratch &= ~RADEON_ACC_REQ_CRT1; + } + } + if ((radeon_encoder->devices & ATOM_DEVICE_CRT2_SUPPORT) && + (radeon_connector->devices & ATOM_DEVICE_CRT2_SUPPORT)) { + if (connected) { + DRM_DEBUG_KMS("CRT2 connected\n"); + bios_4_scratch |= RADEON_CRT2_ATTACHED_COLOR; + bios_5_scratch |= RADEON_CRT2_ON; + bios_5_scratch |= RADEON_ACC_REQ_CRT2; + } else { + DRM_DEBUG_KMS("CRT2 disconnected\n"); + bios_4_scratch &= ~RADEON_CRT2_ATTACHED_MASK; + bios_5_scratch &= ~RADEON_CRT2_ON; + bios_5_scratch &= ~RADEON_ACC_REQ_CRT2; + } + } + if ((radeon_encoder->devices & ATOM_DEVICE_DFP1_SUPPORT) && + (radeon_connector->devices & ATOM_DEVICE_DFP1_SUPPORT)) { + if (connected) { + DRM_DEBUG_KMS("DFP1 connected\n"); + bios_4_scratch |= RADEON_DFP1_ATTACHED; + bios_5_scratch |= RADEON_DFP1_ON; + bios_5_scratch |= RADEON_ACC_REQ_DFP1; + } else { + DRM_DEBUG_KMS("DFP1 disconnected\n"); + bios_4_scratch &= ~RADEON_DFP1_ATTACHED; + bios_5_scratch &= ~RADEON_DFP1_ON; + bios_5_scratch &= ~RADEON_ACC_REQ_DFP1; + } + } + if ((radeon_encoder->devices & ATOM_DEVICE_DFP2_SUPPORT) && + (radeon_connector->devices & ATOM_DEVICE_DFP2_SUPPORT)) { + if (connected) { + DRM_DEBUG_KMS("DFP2 connected\n"); + bios_4_scratch |= RADEON_DFP2_ATTACHED; + bios_5_scratch |= RADEON_DFP2_ON; + bios_5_scratch |= RADEON_ACC_REQ_DFP2; + } else { + DRM_DEBUG_KMS("DFP2 disconnected\n"); + bios_4_scratch &= ~RADEON_DFP2_ATTACHED; + bios_5_scratch &= ~RADEON_DFP2_ON; + bios_5_scratch &= ~RADEON_ACC_REQ_DFP2; + } + } + WREG32(RADEON_BIOS_4_SCRATCH, bios_4_scratch); + WREG32(RADEON_BIOS_5_SCRATCH, bios_5_scratch); +} + +void +radeon_combios_encoder_crtc_scratch_regs(struct drm_encoder *encoder, int crtc) +{ + struct drm_device *dev = encoder->dev; + struct radeon_device *rdev = dev->dev_private; + struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); + uint32_t bios_5_scratch = RREG32(RADEON_BIOS_5_SCRATCH); + + if (radeon_encoder->devices & ATOM_DEVICE_TV1_SUPPORT) { + bios_5_scratch &= ~RADEON_TV1_CRTC_MASK; + bios_5_scratch |= (crtc << RADEON_TV1_CRTC_SHIFT); + } + if (radeon_encoder->devices & ATOM_DEVICE_CRT1_SUPPORT) { + bios_5_scratch &= ~RADEON_CRT1_CRTC_MASK; + bios_5_scratch |= (crtc << RADEON_CRT1_CRTC_SHIFT); + } + if (radeon_encoder->devices & ATOM_DEVICE_CRT2_SUPPORT) { + bios_5_scratch &= ~RADEON_CRT2_CRTC_MASK; + bios_5_scratch |= (crtc << RADEON_CRT2_CRTC_SHIFT); + } + if (radeon_encoder->devices & ATOM_DEVICE_LCD1_SUPPORT) { + bios_5_scratch &= ~RADEON_LCD1_CRTC_MASK; + bios_5_scratch |= (crtc << RADEON_LCD1_CRTC_SHIFT); + } + if (radeon_encoder->devices & ATOM_DEVICE_DFP1_SUPPORT) { + bios_5_scratch &= ~RADEON_DFP1_CRTC_MASK; + bios_5_scratch |= (crtc << RADEON_DFP1_CRTC_SHIFT); + } + if (radeon_encoder->devices & ATOM_DEVICE_DFP2_SUPPORT) { + bios_5_scratch &= ~RADEON_DFP2_CRTC_MASK; + bios_5_scratch |= (crtc << RADEON_DFP2_CRTC_SHIFT); + } + WREG32(RADEON_BIOS_5_SCRATCH, bios_5_scratch); +} + +void +radeon_combios_encoder_dpms_scratch_regs(struct drm_encoder *encoder, bool on) +{ + struct drm_device *dev = encoder->dev; + struct radeon_device *rdev = dev->dev_private; + struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); + uint32_t bios_6_scratch = RREG32(RADEON_BIOS_6_SCRATCH); + + if (radeon_encoder->devices & (ATOM_DEVICE_TV_SUPPORT)) { + if (on) + bios_6_scratch |= RADEON_TV_DPMS_ON; + else + bios_6_scratch &= ~RADEON_TV_DPMS_ON; + } + if (radeon_encoder->devices & (ATOM_DEVICE_CRT_SUPPORT)) { + if (on) + bios_6_scratch |= RADEON_CRT_DPMS_ON; + else + bios_6_scratch &= ~RADEON_CRT_DPMS_ON; + } + if (radeon_encoder->devices & (ATOM_DEVICE_LCD_SUPPORT)) { + if (on) + bios_6_scratch |= RADEON_LCD_DPMS_ON; + else + bios_6_scratch &= ~RADEON_LCD_DPMS_ON; + } + if (radeon_encoder->devices & (ATOM_DEVICE_DFP_SUPPORT)) { + if (on) + bios_6_scratch |= RADEON_DFP_DPMS_ON; + else + bios_6_scratch &= ~RADEON_DFP_DPMS_ON; + } + WREG32(RADEON_BIOS_6_SCRATCH, bios_6_scratch); +} diff --git a/sys/dev/drm2/radeon/radeon_connectors.c b/sys/dev/drm2/radeon/radeon_connectors.c new file mode 100644 index 00000000000..57d7c269028 --- /dev/null +++ b/sys/dev/drm2/radeon/radeon_connectors.c @@ -0,0 +1,2040 @@ +/* + * Copyright 2007-8 Advanced Micro Devices, Inc. + * Copyright 2008 Red Hat Inc. + * + * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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. + * + * Authors: Dave Airlie + * Alex Deucher + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include "radeon.h" +#include "atom.h" + +void radeon_connector_hotplug(struct drm_connector *connector) +{ + struct drm_device *dev = connector->dev; + struct radeon_device *rdev = dev->dev_private; + struct radeon_connector *radeon_connector = to_radeon_connector(connector); + + /* bail if the connector does not have hpd pin, e.g., + * VGA, TV, etc. + */ + if (radeon_connector->hpd.hpd == RADEON_HPD_NONE) + return; + + radeon_hpd_set_polarity(rdev, radeon_connector->hpd.hpd); + + /* if the connector is already off, don't turn it back on */ + if (connector->dpms != DRM_MODE_DPMS_ON) + return; + + /* just deal with DP (not eDP) here. */ + if (connector->connector_type == DRM_MODE_CONNECTOR_DisplayPort) { + struct radeon_connector_atom_dig *dig_connector = + radeon_connector->con_priv; + + /* if existing sink type was not DP no need to retrain */ + if (dig_connector->dp_sink_type != CONNECTOR_OBJECT_ID_DISPLAYPORT) + return; + + /* first get sink type as it may be reset after (un)plug */ + dig_connector->dp_sink_type = radeon_dp_getsinktype(radeon_connector); + /* don't do anything if sink is not display port, i.e., + * passive dp->(dvi|hdmi) adaptor + */ + if (dig_connector->dp_sink_type == CONNECTOR_OBJECT_ID_DISPLAYPORT) { + int saved_dpms = connector->dpms; + /* Only turn off the display if it's physically disconnected */ + if (!radeon_hpd_sense(rdev, radeon_connector->hpd.hpd)) { + drm_helper_connector_dpms(connector, DRM_MODE_DPMS_OFF); + } else if (radeon_dp_needs_link_train(radeon_connector)) { + /* set it to OFF so that drm_helper_connector_dpms() + * won't return immediately since the current state + * is ON at this point. + */ + connector->dpms = DRM_MODE_DPMS_OFF; + drm_helper_connector_dpms(connector, DRM_MODE_DPMS_ON); + } + connector->dpms = saved_dpms; + } + } +} + +static void radeon_property_change_mode(struct drm_encoder *encoder) +{ + struct drm_crtc *crtc = encoder->crtc; + + if (crtc && crtc->enabled) { + drm_crtc_helper_set_mode(crtc, &crtc->mode, + crtc->x, crtc->y, crtc->fb); + } +} + +int radeon_get_monitor_bpc(struct drm_connector *connector) +{ + struct drm_device *dev = connector->dev; + struct radeon_device *rdev = dev->dev_private; + struct radeon_connector *radeon_connector = to_radeon_connector(connector); + struct radeon_connector_atom_dig *dig_connector; + int bpc = 8; + + switch (connector->connector_type) { + case DRM_MODE_CONNECTOR_DVII: + case DRM_MODE_CONNECTOR_HDMIB: + if (radeon_connector->use_digital) { + if (drm_detect_hdmi_monitor(radeon_connector->edid)) { + if (connector->display_info.bpc) + bpc = connector->display_info.bpc; + } + } + break; + case DRM_MODE_CONNECTOR_DVID: + case DRM_MODE_CONNECTOR_HDMIA: + if (drm_detect_hdmi_monitor(radeon_connector->edid)) { + if (connector->display_info.bpc) + bpc = connector->display_info.bpc; + } + break; + case DRM_MODE_CONNECTOR_DisplayPort: + dig_connector = radeon_connector->con_priv; + if ((dig_connector->dp_sink_type == CONNECTOR_OBJECT_ID_DISPLAYPORT) || + (dig_connector->dp_sink_type == CONNECTOR_OBJECT_ID_eDP) || + drm_detect_hdmi_monitor(radeon_connector->edid)) { + if (connector->display_info.bpc) + bpc = connector->display_info.bpc; + } + break; + case DRM_MODE_CONNECTOR_eDP: + case DRM_MODE_CONNECTOR_LVDS: + if (connector->display_info.bpc) + bpc = connector->display_info.bpc; + else if (ASIC_IS_DCE41(rdev) || ASIC_IS_DCE5(rdev)) { + struct drm_connector_helper_funcs *connector_funcs = + connector->helper_private; + struct drm_encoder *encoder = connector_funcs->best_encoder(connector); + struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); + struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv; + + if (dig->lcd_misc & ATOM_PANEL_MISC_V13_6BIT_PER_COLOR) + bpc = 6; + else if (dig->lcd_misc & ATOM_PANEL_MISC_V13_8BIT_PER_COLOR) + bpc = 8; + } + break; + } + return bpc; +} + +static void +radeon_connector_update_scratch_regs(struct drm_connector *connector, enum drm_connector_status status) +{ + struct drm_device *dev = connector->dev; + struct radeon_device *rdev = dev->dev_private; + struct drm_encoder *best_encoder = NULL; + struct drm_encoder *encoder = NULL; + struct drm_connector_helper_funcs *connector_funcs = connector->helper_private; + struct drm_mode_object *obj; + bool connected; + int i; + + best_encoder = connector_funcs->best_encoder(connector); + + for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) { + if (connector->encoder_ids[i] == 0) + break; + + obj = drm_mode_object_find(connector->dev, + connector->encoder_ids[i], + DRM_MODE_OBJECT_ENCODER); + if (!obj) + continue; + + encoder = obj_to_encoder(obj); + + if ((encoder == best_encoder) && (status == connector_status_connected)) + connected = true; + else + connected = false; + + if (rdev->is_atom_bios) + radeon_atombios_connected_scratch_regs(connector, encoder, connected); + else + radeon_combios_connected_scratch_regs(connector, encoder, connected); + + } +} + +static struct drm_encoder *radeon_find_encoder(struct drm_connector *connector, int encoder_type) +{ + struct drm_mode_object *obj; + struct drm_encoder *encoder; + int i; + + for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) { + if (connector->encoder_ids[i] == 0) + break; + + obj = drm_mode_object_find(connector->dev, connector->encoder_ids[i], DRM_MODE_OBJECT_ENCODER); + if (!obj) + continue; + + encoder = obj_to_encoder(obj); + if (encoder->encoder_type == encoder_type) + return encoder; + } + return NULL; +} + +static struct drm_encoder *radeon_best_single_encoder(struct drm_connector *connector) +{ + int enc_id = connector->encoder_ids[0]; + struct drm_mode_object *obj; + struct drm_encoder *encoder; + + /* pick the encoder ids */ + if (enc_id) { + obj = drm_mode_object_find(connector->dev, enc_id, DRM_MODE_OBJECT_ENCODER); + if (!obj) + return NULL; + encoder = obj_to_encoder(obj); + return encoder; + } + return NULL; +} + +/* + * radeon_connector_analog_encoder_conflict_solve + * - search for other connectors sharing this encoder + * if priority is true, then set them disconnected if this is connected + * if priority is false, set us disconnected if they are connected + */ +static enum drm_connector_status +radeon_connector_analog_encoder_conflict_solve(struct drm_connector *connector, + struct drm_encoder *encoder, + enum drm_connector_status current_status, + bool priority) +{ + struct drm_device *dev = connector->dev; + struct drm_connector *conflict; + struct radeon_connector *radeon_conflict; + int i; + + list_for_each_entry(conflict, &dev->mode_config.connector_list, head) { + if (conflict == connector) + continue; + + radeon_conflict = to_radeon_connector(conflict); + for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) { + if (conflict->encoder_ids[i] == 0) + break; + + /* if the IDs match */ + if (conflict->encoder_ids[i] == encoder->base.id) { + if (conflict->status != connector_status_connected) + continue; + + if (radeon_conflict->use_digital) + continue; + + if (priority == true) { + DRM_DEBUG_KMS("1: conflicting encoders switching off %s\n", drm_get_connector_name(conflict)); + DRM_DEBUG_KMS("in favor of %s\n", drm_get_connector_name(connector)); + conflict->status = connector_status_disconnected; + radeon_connector_update_scratch_regs(conflict, connector_status_disconnected); + } else { + DRM_DEBUG_KMS("2: conflicting encoders switching off %s\n", drm_get_connector_name(connector)); + DRM_DEBUG_KMS("in favor of %s\n", drm_get_connector_name(conflict)); + current_status = connector_status_disconnected; + } + break; + } + } + } + return current_status; + +} + +static struct drm_display_mode *radeon_fp_native_mode(struct drm_encoder *encoder) +{ + struct drm_device *dev = encoder->dev; + struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); + struct drm_display_mode *mode = NULL; + struct drm_display_mode *native_mode = &radeon_encoder->native_mode; + + if (native_mode->hdisplay != 0 && + native_mode->vdisplay != 0 && + native_mode->clock != 0) { + mode = drm_mode_duplicate(dev, native_mode); + mode->type = DRM_MODE_TYPE_PREFERRED | DRM_MODE_TYPE_DRIVER; + drm_mode_set_name(mode); + + DRM_DEBUG_KMS("Adding native panel mode %s\n", mode->name); + } else if (native_mode->hdisplay != 0 && + native_mode->vdisplay != 0) { + /* mac laptops without an edid */ + /* Note that this is not necessarily the exact panel mode, + * but an approximation based on the cvt formula. For these + * systems we should ideally read the mode info out of the + * registers or add a mode table, but this works and is much + * simpler. + */ + mode = drm_cvt_mode(dev, native_mode->hdisplay, native_mode->vdisplay, 60, true, false, false); + mode->type = DRM_MODE_TYPE_PREFERRED | DRM_MODE_TYPE_DRIVER; + DRM_DEBUG_KMS("Adding cvt approximation of native panel mode %s\n", mode->name); + } + return mode; +} + +static void radeon_add_common_modes(struct drm_encoder *encoder, struct drm_connector *connector) +{ + struct drm_device *dev = encoder->dev; + struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); + struct drm_display_mode *mode = NULL; + struct drm_display_mode *native_mode = &radeon_encoder->native_mode; + int i; + struct mode_size { + int w; + int h; + } common_modes[17] = { + { 640, 480}, + { 720, 480}, + { 800, 600}, + { 848, 480}, + {1024, 768}, + {1152, 768}, + {1280, 720}, + {1280, 800}, + {1280, 854}, + {1280, 960}, + {1280, 1024}, + {1440, 900}, + {1400, 1050}, + {1680, 1050}, + {1600, 1200}, + {1920, 1080}, + {1920, 1200} + }; + + for (i = 0; i < 17; i++) { + if (radeon_encoder->devices & (ATOM_DEVICE_TV_SUPPORT)) { + if (common_modes[i].w > 1024 || + common_modes[i].h > 768) + continue; + } + if (radeon_encoder->devices & (ATOM_DEVICE_LCD_SUPPORT)) { + if (common_modes[i].w > native_mode->hdisplay || + common_modes[i].h > native_mode->vdisplay || + (common_modes[i].w == native_mode->hdisplay && + common_modes[i].h == native_mode->vdisplay)) + continue; + } + if (common_modes[i].w < 320 || common_modes[i].h < 200) + continue; + + mode = drm_cvt_mode(dev, common_modes[i].w, common_modes[i].h, 60, false, false, false); + drm_mode_probed_add(connector, mode); + } +} + +static int radeon_connector_set_property(struct drm_connector *connector, struct drm_property *property, + uint64_t val) +{ + struct drm_device *dev = connector->dev; + struct radeon_device *rdev = dev->dev_private; + struct drm_encoder *encoder; + struct radeon_encoder *radeon_encoder; + + if (property == rdev->mode_info.coherent_mode_property) { + struct radeon_encoder_atom_dig *dig; + bool new_coherent_mode; + + /* need to find digital encoder on connector */ + encoder = radeon_find_encoder(connector, DRM_MODE_ENCODER_TMDS); + if (!encoder) + return 0; + + radeon_encoder = to_radeon_encoder(encoder); + + if (!radeon_encoder->enc_priv) + return 0; + + dig = radeon_encoder->enc_priv; + new_coherent_mode = val ? true : false; + if (dig->coherent_mode != new_coherent_mode) { + dig->coherent_mode = new_coherent_mode; + radeon_property_change_mode(&radeon_encoder->base); + } + } + + if (property == rdev->mode_info.underscan_property) { + /* need to find digital encoder on connector */ + encoder = radeon_find_encoder(connector, DRM_MODE_ENCODER_TMDS); + if (!encoder) + return 0; + + radeon_encoder = to_radeon_encoder(encoder); + + if (radeon_encoder->underscan_type != val) { + radeon_encoder->underscan_type = val; + radeon_property_change_mode(&radeon_encoder->base); + } + } + + if (property == rdev->mode_info.underscan_hborder_property) { + /* need to find digital encoder on connector */ + encoder = radeon_find_encoder(connector, DRM_MODE_ENCODER_TMDS); + if (!encoder) + return 0; + + radeon_encoder = to_radeon_encoder(encoder); + + if (radeon_encoder->underscan_hborder != val) { + radeon_encoder->underscan_hborder = val; + radeon_property_change_mode(&radeon_encoder->base); + } + } + + if (property == rdev->mode_info.underscan_vborder_property) { + /* need to find digital encoder on connector */ + encoder = radeon_find_encoder(connector, DRM_MODE_ENCODER_TMDS); + if (!encoder) + return 0; + + radeon_encoder = to_radeon_encoder(encoder); + + if (radeon_encoder->underscan_vborder != val) { + radeon_encoder->underscan_vborder = val; + radeon_property_change_mode(&radeon_encoder->base); + } + } + + if (property == rdev->mode_info.tv_std_property) { + encoder = radeon_find_encoder(connector, DRM_MODE_ENCODER_TVDAC); + if (!encoder) { + encoder = radeon_find_encoder(connector, DRM_MODE_ENCODER_DAC); + } + + if (!encoder) + return 0; + + radeon_encoder = to_radeon_encoder(encoder); + if (!radeon_encoder->enc_priv) + return 0; + if (ASIC_IS_AVIVO(rdev) || radeon_r4xx_atom) { + struct radeon_encoder_atom_dac *dac_int; + dac_int = radeon_encoder->enc_priv; + dac_int->tv_std = val; + } else { + struct radeon_encoder_tv_dac *dac_int; + dac_int = radeon_encoder->enc_priv; + dac_int->tv_std = val; + } + radeon_property_change_mode(&radeon_encoder->base); + } + + if (property == rdev->mode_info.load_detect_property) { + struct radeon_connector *radeon_connector = + to_radeon_connector(connector); + + if (val == 0) + radeon_connector->dac_load_detect = false; + else + radeon_connector->dac_load_detect = true; + } + + if (property == rdev->mode_info.tmds_pll_property) { + struct radeon_encoder_int_tmds *tmds = NULL; + bool ret = false; + /* need to find digital encoder on connector */ + encoder = radeon_find_encoder(connector, DRM_MODE_ENCODER_TMDS); + if (!encoder) + return 0; + + radeon_encoder = to_radeon_encoder(encoder); + + tmds = radeon_encoder->enc_priv; + if (!tmds) + return 0; + + if (val == 0) { + if (rdev->is_atom_bios) + ret = radeon_atombios_get_tmds_info(radeon_encoder, tmds); + else + ret = radeon_legacy_get_tmds_info_from_combios(radeon_encoder, tmds); + } + if (val == 1 || ret == false) { + radeon_legacy_get_tmds_info_from_table(radeon_encoder, tmds); + } + radeon_property_change_mode(&radeon_encoder->base); + } + + return 0; +} + +static void radeon_fixup_lvds_native_mode(struct drm_encoder *encoder, + struct drm_connector *connector) +{ + struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); + struct drm_display_mode *native_mode = &radeon_encoder->native_mode; + struct drm_display_mode *t, *mode; + + /* If the EDID preferred mode doesn't match the native mode, use it */ + list_for_each_entry_safe(mode, t, &connector->probed_modes, head) { + if (mode->type & DRM_MODE_TYPE_PREFERRED) { + if (mode->hdisplay != native_mode->hdisplay || + mode->vdisplay != native_mode->vdisplay) + memcpy(native_mode, mode, sizeof(*mode)); + } + } + + /* Try to get native mode details from EDID if necessary */ + if (!native_mode->clock) { + list_for_each_entry_safe(mode, t, &connector->probed_modes, head) { + if (mode->hdisplay == native_mode->hdisplay && + mode->vdisplay == native_mode->vdisplay) { + *native_mode = *mode; + drm_mode_set_crtcinfo(native_mode, CRTC_INTERLACE_HALVE_V); + DRM_DEBUG_KMS("Determined LVDS native mode details from EDID\n"); + break; + } + } + } + + if (!native_mode->clock) { + DRM_DEBUG_KMS("No LVDS native mode details, disabling RMX\n"); + radeon_encoder->rmx_type = RMX_OFF; + } +} + +static int radeon_lvds_get_modes(struct drm_connector *connector) +{ + struct radeon_connector *radeon_connector = to_radeon_connector(connector); + struct drm_encoder *encoder; + int ret = 0; + struct drm_display_mode *mode; + + if (radeon_connector->ddc_bus) { + ret = radeon_ddc_get_modes(radeon_connector); + if (ret > 0) { + encoder = radeon_best_single_encoder(connector); + if (encoder) { + radeon_fixup_lvds_native_mode(encoder, connector); + /* add scaled modes */ + radeon_add_common_modes(encoder, connector); + } + return ret; + } + } + + encoder = radeon_best_single_encoder(connector); + if (!encoder) + return 0; + + /* we have no EDID modes */ + mode = radeon_fp_native_mode(encoder); + if (mode) { + ret = 1; + drm_mode_probed_add(connector, mode); + /* add the width/height from vbios tables if available */ + connector->display_info.width_mm = mode->width_mm; + connector->display_info.height_mm = mode->height_mm; + /* add scaled modes */ + radeon_add_common_modes(encoder, connector); + } + + return ret; +} + +static int radeon_lvds_mode_valid(struct drm_connector *connector, + struct drm_display_mode *mode) +{ + struct drm_encoder *encoder = radeon_best_single_encoder(connector); + + if ((mode->hdisplay < 320) || (mode->vdisplay < 240)) + return MODE_PANEL; + + if (encoder) { + struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); + struct drm_display_mode *native_mode = &radeon_encoder->native_mode; + + /* AVIVO hardware supports downscaling modes larger than the panel + * to the panel size, but I'm not sure this is desirable. + */ + if ((mode->hdisplay > native_mode->hdisplay) || + (mode->vdisplay > native_mode->vdisplay)) + return MODE_PANEL; + + /* if scaling is disabled, block non-native modes */ + if (radeon_encoder->rmx_type == RMX_OFF) { + if ((mode->hdisplay != native_mode->hdisplay) || + (mode->vdisplay != native_mode->vdisplay)) + return MODE_PANEL; + } + } + + return MODE_OK; +} + +static enum drm_connector_status +radeon_lvds_detect(struct drm_connector *connector, bool force) +{ + struct radeon_connector *radeon_connector = to_radeon_connector(connector); + struct drm_encoder *encoder = radeon_best_single_encoder(connector); + enum drm_connector_status ret = connector_status_disconnected; + + if (encoder) { + struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); + struct drm_display_mode *native_mode = &radeon_encoder->native_mode; + + /* check if panel is valid */ + if (native_mode->hdisplay >= 320 && native_mode->vdisplay >= 240) + ret = connector_status_connected; + + } + + /* check for edid as well */ + if (radeon_connector->edid) + ret = connector_status_connected; + else { + if (radeon_connector->ddc_bus) { + radeon_connector->edid = drm_get_edid(&radeon_connector->base, + radeon_connector->ddc_bus->adapter); + if (radeon_connector->edid) + ret = connector_status_connected; + } + } + /* check acpi lid status ??? */ + + radeon_connector_update_scratch_regs(connector, ret); + return ret; +} + +static void radeon_connector_destroy(struct drm_connector *connector) +{ + struct radeon_connector *radeon_connector = to_radeon_connector(connector); + + if (radeon_connector->edid) + free(radeon_connector->edid, DRM_MEM_KMS); + free(radeon_connector->con_priv, DRM_MEM_DRIVER); +#ifdef DUMBBELL_WIP + drm_sysfs_connector_remove(connector); +#endif /* DUMBBELL_WIP */ + drm_connector_cleanup(connector); + free(connector, DRM_MEM_DRIVER); +} + +static int radeon_lvds_set_property(struct drm_connector *connector, + struct drm_property *property, + uint64_t value) +{ + struct drm_device *dev = connector->dev; + struct radeon_encoder *radeon_encoder; + enum radeon_rmx_type rmx_type; + + DRM_DEBUG_KMS("\n"); + if (property != dev->mode_config.scaling_mode_property) + return 0; + + if (connector->encoder) + radeon_encoder = to_radeon_encoder(connector->encoder); + else { + struct drm_connector_helper_funcs *connector_funcs = connector->helper_private; + radeon_encoder = to_radeon_encoder(connector_funcs->best_encoder(connector)); + } + + switch (value) { + case DRM_MODE_SCALE_NONE: rmx_type = RMX_OFF; break; + case DRM_MODE_SCALE_CENTER: rmx_type = RMX_CENTER; break; + case DRM_MODE_SCALE_ASPECT: rmx_type = RMX_ASPECT; break; + default: + case DRM_MODE_SCALE_FULLSCREEN: rmx_type = RMX_FULL; break; + } + if (radeon_encoder->rmx_type == rmx_type) + return 0; + + radeon_encoder->rmx_type = rmx_type; + + radeon_property_change_mode(&radeon_encoder->base); + return 0; +} + + +static const struct drm_connector_helper_funcs radeon_lvds_connector_helper_funcs = { + .get_modes = radeon_lvds_get_modes, + .mode_valid = radeon_lvds_mode_valid, + .best_encoder = radeon_best_single_encoder, +}; + +static const struct drm_connector_funcs radeon_lvds_connector_funcs = { + .dpms = drm_helper_connector_dpms, + .detect = radeon_lvds_detect, + .fill_modes = drm_helper_probe_single_connector_modes, + .destroy = radeon_connector_destroy, + .set_property = radeon_lvds_set_property, +}; + +static int radeon_vga_get_modes(struct drm_connector *connector) +{ + struct radeon_connector *radeon_connector = to_radeon_connector(connector); + int ret; + + ret = radeon_ddc_get_modes(radeon_connector); + + return ret; +} + +static int radeon_vga_mode_valid(struct drm_connector *connector, + struct drm_display_mode *mode) +{ + struct drm_device *dev = connector->dev; + struct radeon_device *rdev = dev->dev_private; + + /* XXX check mode bandwidth */ + + if ((mode->clock / 10) > rdev->clock.max_pixel_clock) + return MODE_CLOCK_HIGH; + + return MODE_OK; +} + +static enum drm_connector_status +radeon_vga_detect(struct drm_connector *connector, bool force) +{ + struct drm_device *dev = connector->dev; + struct radeon_device *rdev = dev->dev_private; + struct radeon_connector *radeon_connector = to_radeon_connector(connector); + struct drm_encoder *encoder; + struct drm_encoder_helper_funcs *encoder_funcs; + bool dret = false; + enum drm_connector_status ret = connector_status_disconnected; + + encoder = radeon_best_single_encoder(connector); + if (!encoder) + ret = connector_status_disconnected; + + if (radeon_connector->ddc_bus) + dret = radeon_ddc_probe(radeon_connector, false); + if (dret) { + radeon_connector->detected_by_load = false; + if (radeon_connector->edid) { + free(radeon_connector->edid, DRM_MEM_KMS); + radeon_connector->edid = NULL; + } + radeon_connector->edid = drm_get_edid(&radeon_connector->base, radeon_connector->ddc_bus->adapter); + + if (!radeon_connector->edid) { + DRM_ERROR("%s: probed a monitor but no|invalid EDID\n", + drm_get_connector_name(connector)); + ret = connector_status_connected; + } else { + radeon_connector->use_digital = !!(radeon_connector->edid->input & DRM_EDID_INPUT_DIGITAL); + + /* some oems have boards with separate digital and analog connectors + * with a shared ddc line (often vga + hdmi) + */ + if (radeon_connector->use_digital && radeon_connector->shared_ddc) { + free(radeon_connector->edid, DRM_MEM_KMS); + radeon_connector->edid = NULL; + ret = connector_status_disconnected; + } else + ret = connector_status_connected; + } + } else { + + /* if we aren't forcing don't do destructive polling */ + if (!force) { + /* only return the previous status if we last + * detected a monitor via load. + */ + if (radeon_connector->detected_by_load) + return connector->status; + else + return ret; + } + + if (radeon_connector->dac_load_detect && encoder) { + encoder_funcs = encoder->helper_private; + ret = encoder_funcs->detect(encoder, connector); + if (ret != connector_status_disconnected) + radeon_connector->detected_by_load = true; + } + } + + if (ret == connector_status_connected) + ret = radeon_connector_analog_encoder_conflict_solve(connector, encoder, ret, true); + + /* RN50 and some RV100 asics in servers often have a hardcoded EDID in the + * vbios to deal with KVMs. If we have one and are not able to detect a monitor + * by other means, assume the CRT is connected and use that EDID. + */ + if ((!rdev->is_atom_bios) && + (ret == connector_status_disconnected) && + rdev->mode_info.bios_hardcoded_edid_size) { + ret = connector_status_connected; + } + + radeon_connector_update_scratch_regs(connector, ret); + return ret; +} + +static const struct drm_connector_helper_funcs radeon_vga_connector_helper_funcs = { + .get_modes = radeon_vga_get_modes, + .mode_valid = radeon_vga_mode_valid, + .best_encoder = radeon_best_single_encoder, +}; + +static const struct drm_connector_funcs radeon_vga_connector_funcs = { + .dpms = drm_helper_connector_dpms, + .detect = radeon_vga_detect, + .fill_modes = drm_helper_probe_single_connector_modes, + .destroy = radeon_connector_destroy, + .set_property = radeon_connector_set_property, +}; + +static int radeon_tv_get_modes(struct drm_connector *connector) +{ + struct drm_device *dev = connector->dev; + struct radeon_device *rdev = dev->dev_private; + struct drm_display_mode *tv_mode; + struct drm_encoder *encoder; + + encoder = radeon_best_single_encoder(connector); + if (!encoder) + return 0; + + /* avivo chips can scale any mode */ + if (rdev->family >= CHIP_RS600) + /* add scaled modes */ + radeon_add_common_modes(encoder, connector); + else { + /* only 800x600 is supported right now on pre-avivo chips */ + tv_mode = drm_cvt_mode(dev, 800, 600, 60, false, false, false); + tv_mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED; + drm_mode_probed_add(connector, tv_mode); + } + return 1; +} + +static int radeon_tv_mode_valid(struct drm_connector *connector, + struct drm_display_mode *mode) +{ + if ((mode->hdisplay > 1024) || (mode->vdisplay > 768)) + return MODE_CLOCK_RANGE; + return MODE_OK; +} + +static enum drm_connector_status +radeon_tv_detect(struct drm_connector *connector, bool force) +{ + struct drm_encoder *encoder; + struct drm_encoder_helper_funcs *encoder_funcs; + struct radeon_connector *radeon_connector = to_radeon_connector(connector); + enum drm_connector_status ret = connector_status_disconnected; + + if (!radeon_connector->dac_load_detect) + return ret; + + encoder = radeon_best_single_encoder(connector); + if (!encoder) + ret = connector_status_disconnected; + else { + encoder_funcs = encoder->helper_private; + ret = encoder_funcs->detect(encoder, connector); + } + if (ret == connector_status_connected) + ret = radeon_connector_analog_encoder_conflict_solve(connector, encoder, ret, false); + radeon_connector_update_scratch_regs(connector, ret); + return ret; +} + +static const struct drm_connector_helper_funcs radeon_tv_connector_helper_funcs = { + .get_modes = radeon_tv_get_modes, + .mode_valid = radeon_tv_mode_valid, + .best_encoder = radeon_best_single_encoder, +}; + +static const struct drm_connector_funcs radeon_tv_connector_funcs = { + .dpms = drm_helper_connector_dpms, + .detect = radeon_tv_detect, + .fill_modes = drm_helper_probe_single_connector_modes, + .destroy = radeon_connector_destroy, + .set_property = radeon_connector_set_property, +}; + +static int radeon_dvi_get_modes(struct drm_connector *connector) +{ + struct radeon_connector *radeon_connector = to_radeon_connector(connector); + int ret; + + ret = radeon_ddc_get_modes(radeon_connector); + return ret; +} + +static bool radeon_check_hpd_status_unchanged(struct drm_connector *connector) +{ + struct drm_device *dev = connector->dev; + struct radeon_device *rdev = dev->dev_private; + struct radeon_connector *radeon_connector = to_radeon_connector(connector); + enum drm_connector_status status; + + /* We only trust HPD on R600 and newer ASICS. */ + if (rdev->family >= CHIP_R600 + && radeon_connector->hpd.hpd != RADEON_HPD_NONE) { + if (radeon_hpd_sense(rdev, radeon_connector->hpd.hpd)) + status = connector_status_connected; + else + status = connector_status_disconnected; + if (connector->status == status) + return true; + } + + return false; +} + +/* + * DVI is complicated + * Do a DDC probe, if DDC probe passes, get the full EDID so + * we can do analog/digital monitor detection at this point. + * If the monitor is an analog monitor or we got no DDC, + * we need to find the DAC encoder object for this connector. + * If we got no DDC, we do load detection on the DAC encoder object. + * If we got analog DDC or load detection passes on the DAC encoder + * we have to check if this analog encoder is shared with anyone else (TV) + * if its shared we have to set the other connector to disconnected. + */ +static enum drm_connector_status +radeon_dvi_detect(struct drm_connector *connector, bool force) +{ + struct drm_device *dev = connector->dev; + struct radeon_device *rdev = dev->dev_private; + struct radeon_connector *radeon_connector = to_radeon_connector(connector); + struct drm_encoder *encoder = NULL; + struct drm_encoder_helper_funcs *encoder_funcs; + struct drm_mode_object *obj; + int i; + enum drm_connector_status ret = connector_status_disconnected; + bool dret = false, broken_edid = false; + + if (!force && radeon_check_hpd_status_unchanged(connector)) + return connector->status; + + if (radeon_connector->ddc_bus) + dret = radeon_ddc_probe(radeon_connector, false); + if (dret) { + radeon_connector->detected_by_load = false; + if (radeon_connector->edid) { + free(radeon_connector->edid, DRM_MEM_KMS); + radeon_connector->edid = NULL; + } + radeon_connector->edid = drm_get_edid(&radeon_connector->base, radeon_connector->ddc_bus->adapter); + + if (!radeon_connector->edid) { + DRM_ERROR("%s: probed a monitor but no|invalid EDID\n", + drm_get_connector_name(connector)); + /* rs690 seems to have a problem with connectors not existing and always + * return a block of 0's. If we see this just stop polling on this output */ + if ((rdev->family == CHIP_RS690 || rdev->family == CHIP_RS740) && radeon_connector->base.null_edid_counter) { + ret = connector_status_disconnected; + DRM_ERROR("%s: detected RS690 floating bus bug, stopping ddc detect\n", drm_get_connector_name(connector)); + radeon_connector->ddc_bus = NULL; + } else { + ret = connector_status_connected; + broken_edid = true; /* defer use_digital to later */ + } + } else { + radeon_connector->use_digital = !!(radeon_connector->edid->input & DRM_EDID_INPUT_DIGITAL); + + /* some oems have boards with separate digital and analog connectors + * with a shared ddc line (often vga + hdmi) + */ + if ((!radeon_connector->use_digital) && radeon_connector->shared_ddc) { + free(radeon_connector->edid, DRM_MEM_KMS); + radeon_connector->edid = NULL; + ret = connector_status_disconnected; + } else + ret = connector_status_connected; + + /* This gets complicated. We have boards with VGA + HDMI with a + * shared DDC line and we have boards with DVI-D + HDMI with a shared + * DDC line. The latter is more complex because with DVI<->HDMI adapters + * you don't really know what's connected to which port as both are digital. + */ + if (radeon_connector->shared_ddc && (ret == connector_status_connected)) { + struct drm_connector *list_connector; + struct radeon_connector *list_radeon_connector; + list_for_each_entry(list_connector, &dev->mode_config.connector_list, head) { + if (connector == list_connector) + continue; + list_radeon_connector = to_radeon_connector(list_connector); + if (list_radeon_connector->shared_ddc && + (list_radeon_connector->ddc_bus->rec.i2c_id == + radeon_connector->ddc_bus->rec.i2c_id)) { + /* cases where both connectors are digital */ + if (list_connector->connector_type != DRM_MODE_CONNECTOR_VGA) { + /* hpd is our only option in this case */ + if (!radeon_hpd_sense(rdev, radeon_connector->hpd.hpd)) { + free(radeon_connector->edid, DRM_MEM_KMS); + radeon_connector->edid = NULL; + ret = connector_status_disconnected; + } + } + } + } + } + } + } + + if ((ret == connector_status_connected) && (radeon_connector->use_digital == true)) + goto out; + + /* DVI-D and HDMI-A are digital only */ + if ((connector->connector_type == DRM_MODE_CONNECTOR_DVID) || + (connector->connector_type == DRM_MODE_CONNECTOR_HDMIA)) + goto out; + + /* if we aren't forcing don't do destructive polling */ + if (!force) { + /* only return the previous status if we last + * detected a monitor via load. + */ + if (radeon_connector->detected_by_load) + ret = connector->status; + goto out; + } + + /* find analog encoder */ + if (radeon_connector->dac_load_detect) { + for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) { + if (connector->encoder_ids[i] == 0) + break; + + obj = drm_mode_object_find(connector->dev, + connector->encoder_ids[i], + DRM_MODE_OBJECT_ENCODER); + if (!obj) + continue; + + encoder = obj_to_encoder(obj); + + if (encoder->encoder_type != DRM_MODE_ENCODER_DAC && + encoder->encoder_type != DRM_MODE_ENCODER_TVDAC) + continue; + + encoder_funcs = encoder->helper_private; + if (encoder_funcs->detect) { + if (!broken_edid) { + if (ret != connector_status_connected) { + /* deal with analog monitors without DDC */ + ret = encoder_funcs->detect(encoder, connector); + if (ret == connector_status_connected) { + radeon_connector->use_digital = false; + } + if (ret != connector_status_disconnected) + radeon_connector->detected_by_load = true; + } + } else { + enum drm_connector_status lret; + /* assume digital unless load detected otherwise */ + radeon_connector->use_digital = true; + lret = encoder_funcs->detect(encoder, connector); + DRM_DEBUG_KMS("load_detect %x returned: %x\n",encoder->encoder_type,lret); + if (lret == connector_status_connected) + radeon_connector->use_digital = false; + } + break; + } + } + } + + if ((ret == connector_status_connected) && (radeon_connector->use_digital == false) && + encoder) { + ret = radeon_connector_analog_encoder_conflict_solve(connector, encoder, ret, true); + } + + /* RN50 and some RV100 asics in servers often have a hardcoded EDID in the + * vbios to deal with KVMs. If we have one and are not able to detect a monitor + * by other means, assume the DFP is connected and use that EDID. In most + * cases the DVI port is actually a virtual KVM port connected to the service + * processor. + */ +out: + if ((!rdev->is_atom_bios) && + (ret == connector_status_disconnected) && + rdev->mode_info.bios_hardcoded_edid_size) { + radeon_connector->use_digital = true; + ret = connector_status_connected; + } + + /* updated in get modes as well since we need to know if it's analog or digital */ + radeon_connector_update_scratch_regs(connector, ret); + return ret; +} + +/* okay need to be smart in here about which encoder to pick */ +static struct drm_encoder *radeon_dvi_encoder(struct drm_connector *connector) +{ + int enc_id = connector->encoder_ids[0]; + struct radeon_connector *radeon_connector = to_radeon_connector(connector); + struct drm_mode_object *obj; + struct drm_encoder *encoder; + int i; + for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) { + if (connector->encoder_ids[i] == 0) + break; + + obj = drm_mode_object_find(connector->dev, connector->encoder_ids[i], DRM_MODE_OBJECT_ENCODER); + if (!obj) + continue; + + encoder = obj_to_encoder(obj); + + if (radeon_connector->use_digital == true) { + if (encoder->encoder_type == DRM_MODE_ENCODER_TMDS) + return encoder; + } else { + if (encoder->encoder_type == DRM_MODE_ENCODER_DAC || + encoder->encoder_type == DRM_MODE_ENCODER_TVDAC) + return encoder; + } + } + + /* see if we have a default encoder TODO */ + + /* then check use digitial */ + /* pick the first one */ + if (enc_id) { + obj = drm_mode_object_find(connector->dev, enc_id, DRM_MODE_OBJECT_ENCODER); + if (!obj) + return NULL; + encoder = obj_to_encoder(obj); + return encoder; + } + return NULL; +} + +static void radeon_dvi_force(struct drm_connector *connector) +{ + struct radeon_connector *radeon_connector = to_radeon_connector(connector); + if (connector->force == DRM_FORCE_ON) + radeon_connector->use_digital = false; + if (connector->force == DRM_FORCE_ON_DIGITAL) + radeon_connector->use_digital = true; +} + +static int radeon_dvi_mode_valid(struct drm_connector *connector, + struct drm_display_mode *mode) +{ + struct drm_device *dev = connector->dev; + struct radeon_device *rdev = dev->dev_private; + struct radeon_connector *radeon_connector = to_radeon_connector(connector); + + /* XXX check mode bandwidth */ + + /* clocks over 135 MHz have heat issues with DVI on RV100 */ + if (radeon_connector->use_digital && + (rdev->family == CHIP_RV100) && + (mode->clock > 135000)) + return MODE_CLOCK_HIGH; + + if (radeon_connector->use_digital && (mode->clock > 165000)) { + if ((radeon_connector->connector_object_id == CONNECTOR_OBJECT_ID_DUAL_LINK_DVI_I) || + (radeon_connector->connector_object_id == CONNECTOR_OBJECT_ID_DUAL_LINK_DVI_D) || + (radeon_connector->connector_object_id == CONNECTOR_OBJECT_ID_HDMI_TYPE_B)) + return MODE_OK; + else if (radeon_connector->connector_object_id == CONNECTOR_OBJECT_ID_HDMI_TYPE_A) { + if (ASIC_IS_DCE6(rdev)) { + /* HDMI 1.3+ supports max clock of 340 Mhz */ + if (mode->clock > 340000) + return MODE_CLOCK_HIGH; + else + return MODE_OK; + } else + return MODE_CLOCK_HIGH; + } else + return MODE_CLOCK_HIGH; + } + + /* check against the max pixel clock */ + if ((mode->clock / 10) > rdev->clock.max_pixel_clock) + return MODE_CLOCK_HIGH; + + return MODE_OK; +} + +static const struct drm_connector_helper_funcs radeon_dvi_connector_helper_funcs = { + .get_modes = radeon_dvi_get_modes, + .mode_valid = radeon_dvi_mode_valid, + .best_encoder = radeon_dvi_encoder, +}; + +static const struct drm_connector_funcs radeon_dvi_connector_funcs = { + .dpms = drm_helper_connector_dpms, + .detect = radeon_dvi_detect, + .fill_modes = drm_helper_probe_single_connector_modes, + .set_property = radeon_connector_set_property, + .destroy = radeon_connector_destroy, + .force = radeon_dvi_force, +}; + +static void radeon_dp_connector_destroy(struct drm_connector *connector) +{ + struct radeon_connector *radeon_connector = to_radeon_connector(connector); + struct radeon_connector_atom_dig *radeon_dig_connector = radeon_connector->con_priv; + + if (radeon_connector->edid) + free(radeon_connector->edid, DRM_MEM_KMS); + if (radeon_dig_connector->dp_i2c_bus) + radeon_i2c_destroy(radeon_dig_connector->dp_i2c_bus); + free(radeon_connector->con_priv, DRM_MEM_DRIVER); +#ifdef DUMBBELL_WIP + drm_sysfs_connector_remove(connector); +#endif /* DUMBBELL_WIP */ + drm_connector_cleanup(connector); + free(connector, DRM_MEM_DRIVER); +} + +static int radeon_dp_get_modes(struct drm_connector *connector) +{ + struct radeon_connector *radeon_connector = to_radeon_connector(connector); + struct radeon_connector_atom_dig *radeon_dig_connector = radeon_connector->con_priv; + struct drm_encoder *encoder = radeon_best_single_encoder(connector); + int ret; + + if ((connector->connector_type == DRM_MODE_CONNECTOR_eDP) || + (connector->connector_type == DRM_MODE_CONNECTOR_LVDS)) { + struct drm_display_mode *mode; + + if (connector->connector_type == DRM_MODE_CONNECTOR_eDP) { + if (!radeon_dig_connector->edp_on) + atombios_set_edp_panel_power(connector, + ATOM_TRANSMITTER_ACTION_POWER_ON); + ret = radeon_ddc_get_modes(radeon_connector); + if (!radeon_dig_connector->edp_on) + atombios_set_edp_panel_power(connector, + ATOM_TRANSMITTER_ACTION_POWER_OFF); + } else { + /* need to setup ddc on the bridge */ + if (radeon_connector_encoder_get_dp_bridge_encoder_id(connector) != + ENCODER_OBJECT_ID_NONE) { + if (encoder) + radeon_atom_ext_encoder_setup_ddc(encoder); + } + ret = radeon_ddc_get_modes(radeon_connector); + } + + if (ret > 0) { + if (encoder) { + radeon_fixup_lvds_native_mode(encoder, connector); + /* add scaled modes */ + radeon_add_common_modes(encoder, connector); + } + return ret; + } + + if (!encoder) + return 0; + + /* we have no EDID modes */ + mode = radeon_fp_native_mode(encoder); + if (mode) { + ret = 1; + drm_mode_probed_add(connector, mode); + /* add the width/height from vbios tables if available */ + connector->display_info.width_mm = mode->width_mm; + connector->display_info.height_mm = mode->height_mm; + /* add scaled modes */ + radeon_add_common_modes(encoder, connector); + } + } else { + /* need to setup ddc on the bridge */ + if (radeon_connector_encoder_get_dp_bridge_encoder_id(connector) != + ENCODER_OBJECT_ID_NONE) { + if (encoder) + radeon_atom_ext_encoder_setup_ddc(encoder); + } + ret = radeon_ddc_get_modes(radeon_connector); + } + + return ret; +} + +u16 radeon_connector_encoder_get_dp_bridge_encoder_id(struct drm_connector *connector) +{ + struct drm_mode_object *obj; + struct drm_encoder *encoder; + struct radeon_encoder *radeon_encoder; + int i; + + for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) { + if (connector->encoder_ids[i] == 0) + break; + + obj = drm_mode_object_find(connector->dev, connector->encoder_ids[i], DRM_MODE_OBJECT_ENCODER); + if (!obj) + continue; + + encoder = obj_to_encoder(obj); + radeon_encoder = to_radeon_encoder(encoder); + + switch (radeon_encoder->encoder_id) { + case ENCODER_OBJECT_ID_TRAVIS: + case ENCODER_OBJECT_ID_NUTMEG: + return radeon_encoder->encoder_id; + default: + break; + } + } + + return ENCODER_OBJECT_ID_NONE; +} + +bool radeon_connector_encoder_is_hbr2(struct drm_connector *connector) +{ + struct drm_mode_object *obj; + struct drm_encoder *encoder; + struct radeon_encoder *radeon_encoder; + int i; + bool found = false; + + for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) { + if (connector->encoder_ids[i] == 0) + break; + + obj = drm_mode_object_find(connector->dev, connector->encoder_ids[i], DRM_MODE_OBJECT_ENCODER); + if (!obj) + continue; + + encoder = obj_to_encoder(obj); + radeon_encoder = to_radeon_encoder(encoder); + if (radeon_encoder->caps & ATOM_ENCODER_CAP_RECORD_HBR2) + found = true; + } + + return found; +} + +bool radeon_connector_is_dp12_capable(struct drm_connector *connector) +{ + struct drm_device *dev = connector->dev; + struct radeon_device *rdev = dev->dev_private; + + if (ASIC_IS_DCE5(rdev) && + (rdev->clock.dp_extclk >= 53900) && + radeon_connector_encoder_is_hbr2(connector)) { + return true; + } + + return false; +} + +static enum drm_connector_status +radeon_dp_detect(struct drm_connector *connector, bool force) +{ + struct drm_device *dev = connector->dev; + struct radeon_device *rdev = dev->dev_private; + struct radeon_connector *radeon_connector = to_radeon_connector(connector); + enum drm_connector_status ret = connector_status_disconnected; + struct radeon_connector_atom_dig *radeon_dig_connector = radeon_connector->con_priv; + struct drm_encoder *encoder = radeon_best_single_encoder(connector); + + if (!force && radeon_check_hpd_status_unchanged(connector)) + return connector->status; + + if (radeon_connector->edid) { + free(radeon_connector->edid, DRM_MEM_KMS); + radeon_connector->edid = NULL; + } + + if ((connector->connector_type == DRM_MODE_CONNECTOR_eDP) || + (connector->connector_type == DRM_MODE_CONNECTOR_LVDS)) { + if (encoder) { + struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); + struct drm_display_mode *native_mode = &radeon_encoder->native_mode; + + /* check if panel is valid */ + if (native_mode->hdisplay >= 320 && native_mode->vdisplay >= 240) + ret = connector_status_connected; + } + /* eDP is always DP */ + radeon_dig_connector->dp_sink_type = CONNECTOR_OBJECT_ID_DISPLAYPORT; + if (!radeon_dig_connector->edp_on) + atombios_set_edp_panel_power(connector, + ATOM_TRANSMITTER_ACTION_POWER_ON); + if (radeon_dp_getdpcd(radeon_connector)) + ret = connector_status_connected; + if (!radeon_dig_connector->edp_on) + atombios_set_edp_panel_power(connector, + ATOM_TRANSMITTER_ACTION_POWER_OFF); + } else if (radeon_connector_encoder_get_dp_bridge_encoder_id(connector) != + ENCODER_OBJECT_ID_NONE) { + /* DP bridges are always DP */ + radeon_dig_connector->dp_sink_type = CONNECTOR_OBJECT_ID_DISPLAYPORT; + /* get the DPCD from the bridge */ + radeon_dp_getdpcd(radeon_connector); + + if (encoder) { + /* setup ddc on the bridge */ + radeon_atom_ext_encoder_setup_ddc(encoder); + /* bridge chips are always aux */ + if (radeon_ddc_probe(radeon_connector, true)) /* try DDC */ + ret = connector_status_connected; + else if (radeon_connector->dac_load_detect) { /* try load detection */ + struct drm_encoder_helper_funcs *encoder_funcs = encoder->helper_private; + ret = encoder_funcs->detect(encoder, connector); + } + } + } else { + radeon_dig_connector->dp_sink_type = radeon_dp_getsinktype(radeon_connector); + if (radeon_hpd_sense(rdev, radeon_connector->hpd.hpd)) { + ret = connector_status_connected; + if (radeon_dig_connector->dp_sink_type == CONNECTOR_OBJECT_ID_DISPLAYPORT) + radeon_dp_getdpcd(radeon_connector); + } else { + if (radeon_dig_connector->dp_sink_type == CONNECTOR_OBJECT_ID_DISPLAYPORT) { + if (radeon_dp_getdpcd(radeon_connector)) + ret = connector_status_connected; + } else { + /* try non-aux ddc (DP to DVI/HMDI/etc. adapter) */ + if (radeon_ddc_probe(radeon_connector, false)) + ret = connector_status_connected; + } + } + } + + radeon_connector_update_scratch_regs(connector, ret); + return ret; +} + +static int radeon_dp_mode_valid(struct drm_connector *connector, + struct drm_display_mode *mode) +{ + struct radeon_connector *radeon_connector = to_radeon_connector(connector); + struct radeon_connector_atom_dig *radeon_dig_connector = radeon_connector->con_priv; + + /* XXX check mode bandwidth */ + + if ((connector->connector_type == DRM_MODE_CONNECTOR_eDP) || + (connector->connector_type == DRM_MODE_CONNECTOR_LVDS)) { + struct drm_encoder *encoder = radeon_best_single_encoder(connector); + + if ((mode->hdisplay < 320) || (mode->vdisplay < 240)) + return MODE_PANEL; + + if (encoder) { + struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); + struct drm_display_mode *native_mode = &radeon_encoder->native_mode; + + /* AVIVO hardware supports downscaling modes larger than the panel + * to the panel size, but I'm not sure this is desirable. + */ + if ((mode->hdisplay > native_mode->hdisplay) || + (mode->vdisplay > native_mode->vdisplay)) + return MODE_PANEL; + + /* if scaling is disabled, block non-native modes */ + if (radeon_encoder->rmx_type == RMX_OFF) { + if ((mode->hdisplay != native_mode->hdisplay) || + (mode->vdisplay != native_mode->vdisplay)) + return MODE_PANEL; + } + } + return MODE_OK; + } else { + if ((radeon_dig_connector->dp_sink_type == CONNECTOR_OBJECT_ID_DISPLAYPORT) || + (radeon_dig_connector->dp_sink_type == CONNECTOR_OBJECT_ID_eDP)) + return radeon_dp_mode_valid_helper(connector, mode); + else + return MODE_OK; + } +} + +static const struct drm_connector_helper_funcs radeon_dp_connector_helper_funcs = { + .get_modes = radeon_dp_get_modes, + .mode_valid = radeon_dp_mode_valid, + .best_encoder = radeon_dvi_encoder, +}; + +static const struct drm_connector_funcs radeon_dp_connector_funcs = { + .dpms = drm_helper_connector_dpms, + .detect = radeon_dp_detect, + .fill_modes = drm_helper_probe_single_connector_modes, + .set_property = radeon_connector_set_property, + .destroy = radeon_dp_connector_destroy, + .force = radeon_dvi_force, +}; + +void +radeon_add_atom_connector(struct drm_device *dev, + uint32_t connector_id, + uint32_t supported_device, + int connector_type, + struct radeon_i2c_bus_rec *i2c_bus, + uint32_t igp_lane_info, + uint16_t connector_object_id, + struct radeon_hpd *hpd, + struct radeon_router *router) +{ + struct radeon_device *rdev = dev->dev_private; + struct drm_connector *connector; + struct radeon_connector *radeon_connector; + struct radeon_connector_atom_dig *radeon_dig_connector; + struct drm_encoder *encoder; + struct radeon_encoder *radeon_encoder; + uint32_t subpixel_order = SubPixelNone; + bool shared_ddc = false; + bool is_dp_bridge = false; + + if (connector_type == DRM_MODE_CONNECTOR_Unknown) + return; + + /* if the user selected tv=0 don't try and add the connector */ + if (((connector_type == DRM_MODE_CONNECTOR_SVIDEO) || + (connector_type == DRM_MODE_CONNECTOR_Composite) || + (connector_type == DRM_MODE_CONNECTOR_9PinDIN)) && + (radeon_tv == 0)) + return; + + /* see if we already added it */ + list_for_each_entry(connector, &dev->mode_config.connector_list, head) { + radeon_connector = to_radeon_connector(connector); + if (radeon_connector->connector_id == connector_id) { + radeon_connector->devices |= supported_device; + return; + } + if (radeon_connector->ddc_bus && i2c_bus->valid) { + if (radeon_connector->ddc_bus->rec.i2c_id == i2c_bus->i2c_id) { + radeon_connector->shared_ddc = true; + shared_ddc = true; + } + if (radeon_connector->router_bus && router->ddc_valid && + (radeon_connector->router.router_id == router->router_id)) { + radeon_connector->shared_ddc = false; + shared_ddc = false; + } + } + } + + /* check if it's a dp bridge */ + list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { + radeon_encoder = to_radeon_encoder(encoder); + if (radeon_encoder->devices & supported_device) { + switch (radeon_encoder->encoder_id) { + case ENCODER_OBJECT_ID_TRAVIS: + case ENCODER_OBJECT_ID_NUTMEG: + is_dp_bridge = true; + break; + default: + break; + } + } + } + + radeon_connector = malloc(sizeof(struct radeon_connector), + DRM_MEM_DRIVER, M_ZERO | M_WAITOK); + if (!radeon_connector) + return; + + connector = &radeon_connector->base; + + radeon_connector->connector_id = connector_id; + radeon_connector->devices = supported_device; + radeon_connector->shared_ddc = shared_ddc; + radeon_connector->connector_object_id = connector_object_id; + radeon_connector->hpd = *hpd; + + radeon_connector->router = *router; + if (router->ddc_valid || router->cd_valid) { + radeon_connector->router_bus = radeon_i2c_lookup(rdev, &router->i2c_info); + if (!radeon_connector->router_bus) + DRM_ERROR("Failed to assign router i2c bus! Check dmesg for i2c errors.\n"); + } + + if (is_dp_bridge) { + radeon_dig_connector = malloc( + sizeof(struct radeon_connector_atom_dig), + DRM_MEM_DRIVER, M_ZERO | M_WAITOK); + if (!radeon_dig_connector) + goto failed; + radeon_dig_connector->igp_lane_info = igp_lane_info; + radeon_connector->con_priv = radeon_dig_connector; + drm_connector_init(dev, &radeon_connector->base, &radeon_dp_connector_funcs, connector_type); + drm_connector_helper_add(&radeon_connector->base, &radeon_dp_connector_helper_funcs); + if (i2c_bus->valid) { + /* add DP i2c bus */ + if (connector_type == DRM_MODE_CONNECTOR_eDP) + radeon_dig_connector->dp_i2c_bus = radeon_i2c_create_dp(dev, i2c_bus, "eDP-auxch"); + else + radeon_dig_connector->dp_i2c_bus = radeon_i2c_create_dp(dev, i2c_bus, "DP-auxch"); + if (!radeon_dig_connector->dp_i2c_bus) + DRM_ERROR("DP: Failed to assign dp ddc bus! Check dmesg for i2c errors.\n"); + radeon_connector->ddc_bus = radeon_i2c_lookup(rdev, i2c_bus); + if (!radeon_connector->ddc_bus) + DRM_ERROR("DP: Failed to assign ddc bus! Check dmesg for i2c errors.\n"); + } + switch (connector_type) { + case DRM_MODE_CONNECTOR_VGA: + case DRM_MODE_CONNECTOR_DVIA: + default: + connector->interlace_allowed = true; + connector->doublescan_allowed = true; + radeon_connector->dac_load_detect = true; + drm_connector_attach_property(&radeon_connector->base, + rdev->mode_info.load_detect_property, + 1); + break; + case DRM_MODE_CONNECTOR_DVII: + case DRM_MODE_CONNECTOR_DVID: + case DRM_MODE_CONNECTOR_HDMIA: + case DRM_MODE_CONNECTOR_HDMIB: + case DRM_MODE_CONNECTOR_DisplayPort: + drm_connector_attach_property(&radeon_connector->base, + rdev->mode_info.underscan_property, + UNDERSCAN_OFF); + drm_connector_attach_property(&radeon_connector->base, + rdev->mode_info.underscan_hborder_property, + 0); + drm_connector_attach_property(&radeon_connector->base, + rdev->mode_info.underscan_vborder_property, + 0); + subpixel_order = SubPixelHorizontalRGB; + connector->interlace_allowed = true; + if (connector_type == DRM_MODE_CONNECTOR_HDMIB) + connector->doublescan_allowed = true; + else + connector->doublescan_allowed = false; + if (connector_type == DRM_MODE_CONNECTOR_DVII) { + radeon_connector->dac_load_detect = true; + drm_connector_attach_property(&radeon_connector->base, + rdev->mode_info.load_detect_property, + 1); + } + break; + case DRM_MODE_CONNECTOR_LVDS: + case DRM_MODE_CONNECTOR_eDP: + drm_connector_attach_property(&radeon_connector->base, + dev->mode_config.scaling_mode_property, + DRM_MODE_SCALE_FULLSCREEN); + subpixel_order = SubPixelHorizontalRGB; + connector->interlace_allowed = false; + connector->doublescan_allowed = false; + break; + } + } else { + switch (connector_type) { + case DRM_MODE_CONNECTOR_VGA: + drm_connector_init(dev, &radeon_connector->base, &radeon_vga_connector_funcs, connector_type); + drm_connector_helper_add(&radeon_connector->base, &radeon_vga_connector_helper_funcs); + if (i2c_bus->valid) { + radeon_connector->ddc_bus = radeon_i2c_lookup(rdev, i2c_bus); + if (!radeon_connector->ddc_bus) + DRM_ERROR("VGA: Failed to assign ddc bus! Check dmesg for i2c errors.\n"); + } + radeon_connector->dac_load_detect = true; + drm_connector_attach_property(&radeon_connector->base, + rdev->mode_info.load_detect_property, + 1); + /* no HPD on analog connectors */ + radeon_connector->hpd.hpd = RADEON_HPD_NONE; + connector->polled = DRM_CONNECTOR_POLL_CONNECT; + connector->interlace_allowed = true; + connector->doublescan_allowed = true; + break; + case DRM_MODE_CONNECTOR_DVIA: + drm_connector_init(dev, &radeon_connector->base, &radeon_vga_connector_funcs, connector_type); + drm_connector_helper_add(&radeon_connector->base, &radeon_vga_connector_helper_funcs); + if (i2c_bus->valid) { + radeon_connector->ddc_bus = radeon_i2c_lookup(rdev, i2c_bus); + if (!radeon_connector->ddc_bus) + DRM_ERROR("DVIA: Failed to assign ddc bus! Check dmesg for i2c errors.\n"); + } + radeon_connector->dac_load_detect = true; + drm_connector_attach_property(&radeon_connector->base, + rdev->mode_info.load_detect_property, + 1); + /* no HPD on analog connectors */ + radeon_connector->hpd.hpd = RADEON_HPD_NONE; + connector->interlace_allowed = true; + connector->doublescan_allowed = true; + break; + case DRM_MODE_CONNECTOR_DVII: + case DRM_MODE_CONNECTOR_DVID: + radeon_dig_connector = malloc( + sizeof(struct radeon_connector_atom_dig), + DRM_MEM_DRIVER, M_ZERO | M_WAITOK); + if (!radeon_dig_connector) + goto failed; + radeon_dig_connector->igp_lane_info = igp_lane_info; + radeon_connector->con_priv = radeon_dig_connector; + drm_connector_init(dev, &radeon_connector->base, &radeon_dvi_connector_funcs, connector_type); + drm_connector_helper_add(&radeon_connector->base, &radeon_dvi_connector_helper_funcs); + if (i2c_bus->valid) { + radeon_connector->ddc_bus = radeon_i2c_lookup(rdev, i2c_bus); + if (!radeon_connector->ddc_bus) + DRM_ERROR("DVI: Failed to assign ddc bus! Check dmesg for i2c errors.\n"); + } + subpixel_order = SubPixelHorizontalRGB; + drm_connector_attach_property(&radeon_connector->base, + rdev->mode_info.coherent_mode_property, + 1); + if (ASIC_IS_AVIVO(rdev)) { + drm_connector_attach_property(&radeon_connector->base, + rdev->mode_info.underscan_property, + UNDERSCAN_OFF); + drm_connector_attach_property(&radeon_connector->base, + rdev->mode_info.underscan_hborder_property, + 0); + drm_connector_attach_property(&radeon_connector->base, + rdev->mode_info.underscan_vborder_property, + 0); + } + if (connector_type == DRM_MODE_CONNECTOR_DVII) { + radeon_connector->dac_load_detect = true; + drm_connector_attach_property(&radeon_connector->base, + rdev->mode_info.load_detect_property, + 1); + } + connector->interlace_allowed = true; + if (connector_type == DRM_MODE_CONNECTOR_DVII) + connector->doublescan_allowed = true; + else + connector->doublescan_allowed = false; + break; + case DRM_MODE_CONNECTOR_HDMIA: + case DRM_MODE_CONNECTOR_HDMIB: + radeon_dig_connector = malloc( + sizeof(struct radeon_connector_atom_dig), + DRM_MEM_DRIVER, M_ZERO | M_WAITOK); + if (!radeon_dig_connector) + goto failed; + radeon_dig_connector->igp_lane_info = igp_lane_info; + radeon_connector->con_priv = radeon_dig_connector; + drm_connector_init(dev, &radeon_connector->base, &radeon_dvi_connector_funcs, connector_type); + drm_connector_helper_add(&radeon_connector->base, &radeon_dvi_connector_helper_funcs); + if (i2c_bus->valid) { + radeon_connector->ddc_bus = radeon_i2c_lookup(rdev, i2c_bus); + if (!radeon_connector->ddc_bus) + DRM_ERROR("HDMI: Failed to assign ddc bus! Check dmesg for i2c errors.\n"); + } + drm_connector_attach_property(&radeon_connector->base, + rdev->mode_info.coherent_mode_property, + 1); + if (ASIC_IS_AVIVO(rdev)) { + drm_connector_attach_property(&radeon_connector->base, + rdev->mode_info.underscan_property, + UNDERSCAN_OFF); + drm_connector_attach_property(&radeon_connector->base, + rdev->mode_info.underscan_hborder_property, + 0); + drm_connector_attach_property(&radeon_connector->base, + rdev->mode_info.underscan_vborder_property, + 0); + } + subpixel_order = SubPixelHorizontalRGB; + connector->interlace_allowed = true; + if (connector_type == DRM_MODE_CONNECTOR_HDMIB) + connector->doublescan_allowed = true; + else + connector->doublescan_allowed = false; + break; + case DRM_MODE_CONNECTOR_DisplayPort: + radeon_dig_connector = malloc( + sizeof(struct radeon_connector_atom_dig), + DRM_MEM_DRIVER, M_ZERO | M_WAITOK); + if (!radeon_dig_connector) + goto failed; + radeon_dig_connector->igp_lane_info = igp_lane_info; + radeon_connector->con_priv = radeon_dig_connector; + drm_connector_init(dev, &radeon_connector->base, &radeon_dp_connector_funcs, connector_type); + drm_connector_helper_add(&radeon_connector->base, &radeon_dp_connector_helper_funcs); + if (i2c_bus->valid) { + /* add DP i2c bus */ + radeon_dig_connector->dp_i2c_bus = radeon_i2c_create_dp(dev, i2c_bus, "DP-auxch"); + if (!radeon_dig_connector->dp_i2c_bus) + DRM_ERROR("DP: Failed to assign dp ddc bus! Check dmesg for i2c errors.\n"); + radeon_connector->ddc_bus = radeon_i2c_lookup(rdev, i2c_bus); + if (!radeon_connector->ddc_bus) + DRM_ERROR("DP: Failed to assign ddc bus! Check dmesg for i2c errors.\n"); + } + subpixel_order = SubPixelHorizontalRGB; + drm_connector_attach_property(&radeon_connector->base, + rdev->mode_info.coherent_mode_property, + 1); + if (ASIC_IS_AVIVO(rdev)) { + drm_connector_attach_property(&radeon_connector->base, + rdev->mode_info.underscan_property, + UNDERSCAN_OFF); + drm_connector_attach_property(&radeon_connector->base, + rdev->mode_info.underscan_hborder_property, + 0); + drm_connector_attach_property(&radeon_connector->base, + rdev->mode_info.underscan_vborder_property, + 0); + } + connector->interlace_allowed = true; + /* in theory with a DP to VGA converter... */ + connector->doublescan_allowed = false; + break; + case DRM_MODE_CONNECTOR_eDP: + radeon_dig_connector = malloc( + sizeof(struct radeon_connector_atom_dig), + DRM_MEM_DRIVER, M_ZERO | M_WAITOK); + if (!radeon_dig_connector) + goto failed; + radeon_dig_connector->igp_lane_info = igp_lane_info; + radeon_connector->con_priv = radeon_dig_connector; + drm_connector_init(dev, &radeon_connector->base, &radeon_dp_connector_funcs, connector_type); + drm_connector_helper_add(&radeon_connector->base, &radeon_dp_connector_helper_funcs); + if (i2c_bus->valid) { + /* add DP i2c bus */ + radeon_dig_connector->dp_i2c_bus = radeon_i2c_create_dp(dev, i2c_bus, "eDP-auxch"); + if (!radeon_dig_connector->dp_i2c_bus) + DRM_ERROR("DP: Failed to assign dp ddc bus! Check dmesg for i2c errors.\n"); + radeon_connector->ddc_bus = radeon_i2c_lookup(rdev, i2c_bus); + if (!radeon_connector->ddc_bus) + DRM_ERROR("DP: Failed to assign ddc bus! Check dmesg for i2c errors.\n"); + } + drm_connector_attach_property(&radeon_connector->base, + dev->mode_config.scaling_mode_property, + DRM_MODE_SCALE_FULLSCREEN); + subpixel_order = SubPixelHorizontalRGB; + connector->interlace_allowed = false; + connector->doublescan_allowed = false; + break; + case DRM_MODE_CONNECTOR_SVIDEO: + case DRM_MODE_CONNECTOR_Composite: + case DRM_MODE_CONNECTOR_9PinDIN: + drm_connector_init(dev, &radeon_connector->base, &radeon_tv_connector_funcs, connector_type); + drm_connector_helper_add(&radeon_connector->base, &radeon_tv_connector_helper_funcs); + radeon_connector->dac_load_detect = true; + drm_connector_attach_property(&radeon_connector->base, + rdev->mode_info.load_detect_property, + 1); + drm_connector_attach_property(&radeon_connector->base, + rdev->mode_info.tv_std_property, + radeon_atombios_get_tv_info(rdev)); + /* no HPD on analog connectors */ + radeon_connector->hpd.hpd = RADEON_HPD_NONE; + connector->interlace_allowed = false; + connector->doublescan_allowed = false; + break; + case DRM_MODE_CONNECTOR_LVDS: + radeon_dig_connector = malloc( + sizeof(struct radeon_connector_atom_dig), + DRM_MEM_DRIVER, M_ZERO | M_WAITOK); + if (!radeon_dig_connector) + goto failed; + radeon_dig_connector->igp_lane_info = igp_lane_info; + radeon_connector->con_priv = radeon_dig_connector; + drm_connector_init(dev, &radeon_connector->base, &radeon_lvds_connector_funcs, connector_type); + drm_connector_helper_add(&radeon_connector->base, &radeon_lvds_connector_helper_funcs); + if (i2c_bus->valid) { + radeon_connector->ddc_bus = radeon_i2c_lookup(rdev, i2c_bus); + if (!radeon_connector->ddc_bus) + DRM_ERROR("LVDS: Failed to assign ddc bus! Check dmesg for i2c errors.\n"); + } + drm_connector_attach_property(&radeon_connector->base, + dev->mode_config.scaling_mode_property, + DRM_MODE_SCALE_FULLSCREEN); + subpixel_order = SubPixelHorizontalRGB; + connector->interlace_allowed = false; + connector->doublescan_allowed = false; + break; + } + } + + if (radeon_connector->hpd.hpd == RADEON_HPD_NONE) { + if (i2c_bus->valid) + connector->polled = DRM_CONNECTOR_POLL_CONNECT; + } else + connector->polled = DRM_CONNECTOR_POLL_HPD; + + connector->display_info.subpixel_order = subpixel_order; +#ifdef DUMBBELL_WIP + drm_sysfs_connector_add(connector); +#endif /* DUMBBELL_WIP */ + return; + +failed: + drm_connector_cleanup(connector); + free(connector, DRM_MEM_DRIVER); +} + +void +radeon_add_legacy_connector(struct drm_device *dev, + uint32_t connector_id, + uint32_t supported_device, + int connector_type, + struct radeon_i2c_bus_rec *i2c_bus, + uint16_t connector_object_id, + struct radeon_hpd *hpd) +{ + struct radeon_device *rdev = dev->dev_private; + struct drm_connector *connector; + struct radeon_connector *radeon_connector; + uint32_t subpixel_order = SubPixelNone; + + if (connector_type == DRM_MODE_CONNECTOR_Unknown) + return; + + /* if the user selected tv=0 don't try and add the connector */ + if (((connector_type == DRM_MODE_CONNECTOR_SVIDEO) || + (connector_type == DRM_MODE_CONNECTOR_Composite) || + (connector_type == DRM_MODE_CONNECTOR_9PinDIN)) && + (radeon_tv == 0)) + return; + + /* see if we already added it */ + list_for_each_entry(connector, &dev->mode_config.connector_list, head) { + radeon_connector = to_radeon_connector(connector); + if (radeon_connector->connector_id == connector_id) { + radeon_connector->devices |= supported_device; + return; + } + } + + radeon_connector = malloc(sizeof(struct radeon_connector), + DRM_MEM_DRIVER, M_ZERO | M_WAITOK); + if (!radeon_connector) + return; + + connector = &radeon_connector->base; + + radeon_connector->connector_id = connector_id; + radeon_connector->devices = supported_device; + radeon_connector->connector_object_id = connector_object_id; + radeon_connector->hpd = *hpd; + + switch (connector_type) { + case DRM_MODE_CONNECTOR_VGA: + drm_connector_init(dev, &radeon_connector->base, &radeon_vga_connector_funcs, connector_type); + drm_connector_helper_add(&radeon_connector->base, &radeon_vga_connector_helper_funcs); + if (i2c_bus->valid) { + radeon_connector->ddc_bus = radeon_i2c_lookup(rdev, i2c_bus); + if (!radeon_connector->ddc_bus) + DRM_ERROR("VGA: Failed to assign ddc bus! Check dmesg for i2c errors.\n"); + } + radeon_connector->dac_load_detect = true; + drm_connector_attach_property(&radeon_connector->base, + rdev->mode_info.load_detect_property, + 1); + /* no HPD on analog connectors */ + radeon_connector->hpd.hpd = RADEON_HPD_NONE; + connector->polled = DRM_CONNECTOR_POLL_CONNECT; + connector->interlace_allowed = true; + connector->doublescan_allowed = true; + break; + case DRM_MODE_CONNECTOR_DVIA: + drm_connector_init(dev, &radeon_connector->base, &radeon_vga_connector_funcs, connector_type); + drm_connector_helper_add(&radeon_connector->base, &radeon_vga_connector_helper_funcs); + if (i2c_bus->valid) { + radeon_connector->ddc_bus = radeon_i2c_lookup(rdev, i2c_bus); + if (!radeon_connector->ddc_bus) + DRM_ERROR("DVIA: Failed to assign ddc bus! Check dmesg for i2c errors.\n"); + } + radeon_connector->dac_load_detect = true; + drm_connector_attach_property(&radeon_connector->base, + rdev->mode_info.load_detect_property, + 1); + /* no HPD on analog connectors */ + radeon_connector->hpd.hpd = RADEON_HPD_NONE; + connector->interlace_allowed = true; + connector->doublescan_allowed = true; + break; + case DRM_MODE_CONNECTOR_DVII: + case DRM_MODE_CONNECTOR_DVID: + drm_connector_init(dev, &radeon_connector->base, &radeon_dvi_connector_funcs, connector_type); + drm_connector_helper_add(&radeon_connector->base, &radeon_dvi_connector_helper_funcs); + if (i2c_bus->valid) { + radeon_connector->ddc_bus = radeon_i2c_lookup(rdev, i2c_bus); + if (!radeon_connector->ddc_bus) + DRM_ERROR("DVI: Failed to assign ddc bus! Check dmesg for i2c errors.\n"); + } + if (connector_type == DRM_MODE_CONNECTOR_DVII) { + radeon_connector->dac_load_detect = true; + drm_connector_attach_property(&radeon_connector->base, + rdev->mode_info.load_detect_property, + 1); + } + subpixel_order = SubPixelHorizontalRGB; + connector->interlace_allowed = true; + if (connector_type == DRM_MODE_CONNECTOR_DVII) + connector->doublescan_allowed = true; + else + connector->doublescan_allowed = false; + break; + case DRM_MODE_CONNECTOR_SVIDEO: + case DRM_MODE_CONNECTOR_Composite: + case DRM_MODE_CONNECTOR_9PinDIN: + drm_connector_init(dev, &radeon_connector->base, &radeon_tv_connector_funcs, connector_type); + drm_connector_helper_add(&radeon_connector->base, &radeon_tv_connector_helper_funcs); + radeon_connector->dac_load_detect = true; + /* RS400,RC410,RS480 chipset seems to report a lot + * of false positive on load detect, we haven't yet + * found a way to make load detect reliable on those + * chipset, thus just disable it for TV. + */ + if (rdev->family == CHIP_RS400 || rdev->family == CHIP_RS480) + radeon_connector->dac_load_detect = false; + drm_connector_attach_property(&radeon_connector->base, + rdev->mode_info.load_detect_property, + radeon_connector->dac_load_detect); + drm_connector_attach_property(&radeon_connector->base, + rdev->mode_info.tv_std_property, + radeon_combios_get_tv_info(rdev)); + /* no HPD on analog connectors */ + radeon_connector->hpd.hpd = RADEON_HPD_NONE; + connector->interlace_allowed = false; + connector->doublescan_allowed = false; + break; + case DRM_MODE_CONNECTOR_LVDS: + drm_connector_init(dev, &radeon_connector->base, &radeon_lvds_connector_funcs, connector_type); + drm_connector_helper_add(&radeon_connector->base, &radeon_lvds_connector_helper_funcs); + if (i2c_bus->valid) { + radeon_connector->ddc_bus = radeon_i2c_lookup(rdev, i2c_bus); + if (!radeon_connector->ddc_bus) + DRM_ERROR("LVDS: Failed to assign ddc bus! Check dmesg for i2c errors.\n"); + } + drm_connector_attach_property(&radeon_connector->base, + dev->mode_config.scaling_mode_property, + DRM_MODE_SCALE_FULLSCREEN); + subpixel_order = SubPixelHorizontalRGB; + connector->interlace_allowed = false; + connector->doublescan_allowed = false; + break; + } + + if (radeon_connector->hpd.hpd == RADEON_HPD_NONE) { + if (i2c_bus->valid) + connector->polled = DRM_CONNECTOR_POLL_CONNECT; + } else + connector->polled = DRM_CONNECTOR_POLL_HPD; + connector->display_info.subpixel_order = subpixel_order; +#ifdef DUMBBELL_WIP + drm_sysfs_connector_add(connector); +#endif /* DUMBBELL_WIP */ +} diff --git a/sys/dev/drm2/radeon/radeon_cp.c b/sys/dev/drm2/radeon/radeon_cp.c new file mode 100644 index 00000000000..4a036336cdb --- /dev/null +++ b/sys/dev/drm2/radeon/radeon_cp.c @@ -0,0 +1,2245 @@ +/* radeon_cp.c -- CP support for Radeon -*- linux-c -*- */ +/* + * Copyright 2000 Precision Insight, Inc., Cedar Park, Texas. + * Copyright 2000 VA Linux Systems, Inc., Fremont, California. + * Copyright 2007 Advanced Micro Devices, Inc. + * All Rights Reserved. + * + * 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 (including the next + * paragraph) 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 + * PRECISION INSIGHT AND/OR ITS SUPPLIERS 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. + * + * Authors: + * Kevin E. Martin + * Gareth Hughes + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include + +#include +#include +#include "radeon_drv.h" +#include "r300_reg.h" + +#define RADEON_FIFO_DEBUG 0 + +/* Firmware Names */ +#define FIRMWARE_R100 "radeonkmsfw_R100_cp" +#define FIRMWARE_R200 "radeonkmsfw_R200_cp" +#define FIRMWARE_R300 "radeonkmsfw_R300_cp" +#define FIRMWARE_R420 "radeonkmsfw_R420_cp" +#define FIRMWARE_RS690 "radeonkmsfw_RS690_cp" +#define FIRMWARE_RS600 "radeonkmsfw_RS600_cp" +#define FIRMWARE_R520 "radeonkmsfw_R520_cp" + +static int radeon_do_cleanup_cp(struct drm_device * dev); +static void radeon_do_cp_start(drm_radeon_private_t * dev_priv); + +u32 radeon_read_ring_rptr(drm_radeon_private_t *dev_priv, u32 off) +{ + u32 val; + + if (dev_priv->flags & RADEON_IS_AGP) { + val = DRM_READ32(dev_priv->ring_rptr, off); + } else { + val = *(((volatile u32 *) + dev_priv->ring_rptr->handle) + + (off / sizeof(u32))); + val = le32_to_cpu(val); + } + return val; +} + +u32 radeon_get_ring_head(drm_radeon_private_t *dev_priv) +{ + if (dev_priv->writeback_works) + return radeon_read_ring_rptr(dev_priv, 0); + else { + if ((dev_priv->flags & RADEON_FAMILY_MASK) >= CHIP_R600) + return RADEON_READ(R600_CP_RB_RPTR); + else + return RADEON_READ(RADEON_CP_RB_RPTR); + } +} + +void radeon_write_ring_rptr(drm_radeon_private_t *dev_priv, u32 off, u32 val) +{ + if (dev_priv->flags & RADEON_IS_AGP) + DRM_WRITE32(dev_priv->ring_rptr, off, val); + else + *(((volatile u32 *) dev_priv->ring_rptr->handle) + + (off / sizeof(u32))) = cpu_to_le32(val); +} + +void radeon_set_ring_head(drm_radeon_private_t *dev_priv, u32 val) +{ + radeon_write_ring_rptr(dev_priv, 0, val); +} + +u32 radeon_get_scratch(drm_radeon_private_t *dev_priv, int index) +{ + if (dev_priv->writeback_works) { + if ((dev_priv->flags & RADEON_FAMILY_MASK) >= CHIP_R600) + return radeon_read_ring_rptr(dev_priv, + R600_SCRATCHOFF(index)); + else + return radeon_read_ring_rptr(dev_priv, + RADEON_SCRATCHOFF(index)); + } else { + if ((dev_priv->flags & RADEON_FAMILY_MASK) >= CHIP_R600) + return RADEON_READ(R600_SCRATCH_REG0 + 4*index); + else + return RADEON_READ(RADEON_SCRATCH_REG0 + 4*index); + } +} + +static u32 R500_READ_MCIND(drm_radeon_private_t *dev_priv, int addr) +{ + u32 ret; + RADEON_WRITE(R520_MC_IND_INDEX, 0x7f0000 | (addr & 0xff)); + ret = RADEON_READ(R520_MC_IND_DATA); + RADEON_WRITE(R520_MC_IND_INDEX, 0); + return ret; +} + +static u32 RS480_READ_MCIND(drm_radeon_private_t *dev_priv, int addr) +{ + u32 ret; + RADEON_WRITE(RS480_NB_MC_INDEX, addr & 0xff); + ret = RADEON_READ(RS480_NB_MC_DATA); + RADEON_WRITE(RS480_NB_MC_INDEX, 0xff); + return ret; +} + +static u32 RS690_READ_MCIND(drm_radeon_private_t *dev_priv, int addr) +{ + u32 ret; + RADEON_WRITE(RS690_MC_INDEX, (addr & RS690_MC_INDEX_MASK)); + ret = RADEON_READ(RS690_MC_DATA); + RADEON_WRITE(RS690_MC_INDEX, RS690_MC_INDEX_MASK); + return ret; +} + +static u32 RS600_READ_MCIND(drm_radeon_private_t *dev_priv, int addr) +{ + u32 ret; + RADEON_WRITE(RS600_MC_INDEX, ((addr & RS600_MC_ADDR_MASK) | + RS600_MC_IND_CITF_ARB0)); + ret = RADEON_READ(RS600_MC_DATA); + return ret; +} + +static u32 IGP_READ_MCIND(drm_radeon_private_t *dev_priv, int addr) +{ + if (((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RS690) || + ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RS740)) + return RS690_READ_MCIND(dev_priv, addr); + else if ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RS600) + return RS600_READ_MCIND(dev_priv, addr); + else + return RS480_READ_MCIND(dev_priv, addr); +} + +u32 radeon_read_fb_location(drm_radeon_private_t *dev_priv) +{ + + if ((dev_priv->flags & RADEON_FAMILY_MASK) >= CHIP_RV770) + return RADEON_READ(R700_MC_VM_FB_LOCATION); + else if ((dev_priv->flags & RADEON_FAMILY_MASK) >= CHIP_R600) + return RADEON_READ(R600_MC_VM_FB_LOCATION); + else if ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RV515) + return R500_READ_MCIND(dev_priv, RV515_MC_FB_LOCATION); + else if (((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RS690) || + ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RS740)) + return RS690_READ_MCIND(dev_priv, RS690_MC_FB_LOCATION); + else if ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RS600) + return RS600_READ_MCIND(dev_priv, RS600_MC_FB_LOCATION); + else if ((dev_priv->flags & RADEON_FAMILY_MASK) > CHIP_RV515) + return R500_READ_MCIND(dev_priv, R520_MC_FB_LOCATION); + else + return RADEON_READ(RADEON_MC_FB_LOCATION); +} + +static void radeon_write_fb_location(drm_radeon_private_t *dev_priv, u32 fb_loc) +{ + if ((dev_priv->flags & RADEON_FAMILY_MASK) >= CHIP_RV770) + RADEON_WRITE(R700_MC_VM_FB_LOCATION, fb_loc); + else if ((dev_priv->flags & RADEON_FAMILY_MASK) >= CHIP_R600) + RADEON_WRITE(R600_MC_VM_FB_LOCATION, fb_loc); + else if ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RV515) + R500_WRITE_MCIND(RV515_MC_FB_LOCATION, fb_loc); + else if (((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RS690) || + ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RS740)) + RS690_WRITE_MCIND(RS690_MC_FB_LOCATION, fb_loc); + else if ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RS600) + RS600_WRITE_MCIND(RS600_MC_FB_LOCATION, fb_loc); + else if ((dev_priv->flags & RADEON_FAMILY_MASK) > CHIP_RV515) + R500_WRITE_MCIND(R520_MC_FB_LOCATION, fb_loc); + else + RADEON_WRITE(RADEON_MC_FB_LOCATION, fb_loc); +} + +void radeon_write_agp_location(drm_radeon_private_t *dev_priv, u32 agp_loc) +{ + /*R6xx/R7xx: AGP_TOP and BOT are actually 18 bits each */ + if ((dev_priv->flags & RADEON_FAMILY_MASK) >= CHIP_RV770) { + RADEON_WRITE(R700_MC_VM_AGP_BOT, agp_loc & 0xffff); /* FIX ME */ + RADEON_WRITE(R700_MC_VM_AGP_TOP, (agp_loc >> 16) & 0xffff); + } else if ((dev_priv->flags & RADEON_FAMILY_MASK) >= CHIP_R600) { + RADEON_WRITE(R600_MC_VM_AGP_BOT, agp_loc & 0xffff); /* FIX ME */ + RADEON_WRITE(R600_MC_VM_AGP_TOP, (agp_loc >> 16) & 0xffff); + } else if ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RV515) + R500_WRITE_MCIND(RV515_MC_AGP_LOCATION, agp_loc); + else if (((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RS690) || + ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RS740)) + RS690_WRITE_MCIND(RS690_MC_AGP_LOCATION, agp_loc); + else if ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RS600) + RS600_WRITE_MCIND(RS600_MC_AGP_LOCATION, agp_loc); + else if ((dev_priv->flags & RADEON_FAMILY_MASK) > CHIP_RV515) + R500_WRITE_MCIND(R520_MC_AGP_LOCATION, agp_loc); + else + RADEON_WRITE(RADEON_MC_AGP_LOCATION, agp_loc); +} + +void radeon_write_agp_base(drm_radeon_private_t *dev_priv, u64 agp_base) +{ + u32 agp_base_hi = upper_32_bits(agp_base); + u32 agp_base_lo = agp_base & 0xffffffff; + u32 r6xx_agp_base = (agp_base >> 22) & 0x3ffff; + + /* R6xx/R7xx must be aligned to a 4MB boundary */ + if ((dev_priv->flags & RADEON_FAMILY_MASK) >= CHIP_RV770) + RADEON_WRITE(R700_MC_VM_AGP_BASE, r6xx_agp_base); + else if ((dev_priv->flags & RADEON_FAMILY_MASK) >= CHIP_R600) + RADEON_WRITE(R600_MC_VM_AGP_BASE, r6xx_agp_base); + else if ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RV515) { + R500_WRITE_MCIND(RV515_MC_AGP_BASE, agp_base_lo); + R500_WRITE_MCIND(RV515_MC_AGP_BASE_2, agp_base_hi); + } else if (((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RS690) || + ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RS740)) { + RS690_WRITE_MCIND(RS690_MC_AGP_BASE, agp_base_lo); + RS690_WRITE_MCIND(RS690_MC_AGP_BASE_2, agp_base_hi); + } else if ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RS600) { + RS600_WRITE_MCIND(RS600_AGP_BASE, agp_base_lo); + RS600_WRITE_MCIND(RS600_AGP_BASE_2, agp_base_hi); + } else if ((dev_priv->flags & RADEON_FAMILY_MASK) > CHIP_RV515) { + R500_WRITE_MCIND(R520_MC_AGP_BASE, agp_base_lo); + R500_WRITE_MCIND(R520_MC_AGP_BASE_2, agp_base_hi); + } else if (((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RS400) || + ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RS480)) { + RADEON_WRITE(RADEON_AGP_BASE, agp_base_lo); + RADEON_WRITE(RS480_AGP_BASE_2, agp_base_hi); + } else { + RADEON_WRITE(RADEON_AGP_BASE, agp_base_lo); + if ((dev_priv->flags & RADEON_FAMILY_MASK) >= CHIP_R200) + RADEON_WRITE(RADEON_AGP_BASE_2, agp_base_hi); + } +} + +void radeon_enable_bm(struct drm_radeon_private *dev_priv) +{ + u32 tmp; + /* Turn on bus mastering */ + if (((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RS690) || + ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RS740)) { + /* rs600/rs690/rs740 */ + tmp = RADEON_READ(RADEON_BUS_CNTL) & ~RS600_BUS_MASTER_DIS; + RADEON_WRITE(RADEON_BUS_CNTL, tmp); + } else if (((dev_priv->flags & RADEON_FAMILY_MASK) <= CHIP_RV350) || + ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_R420) || + ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RS400) || + ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RS480)) { + /* r1xx, r2xx, r300, r(v)350, r420/r481, rs400/rs480 */ + tmp = RADEON_READ(RADEON_BUS_CNTL) & ~RADEON_BUS_MASTER_DIS; + RADEON_WRITE(RADEON_BUS_CNTL, tmp); + } /* PCIE cards appears to not need this */ +} + +static int RADEON_READ_PLL(struct drm_device * dev, int addr) +{ + drm_radeon_private_t *dev_priv = dev->dev_private; + + RADEON_WRITE8(RADEON_CLOCK_CNTL_INDEX, addr & 0x1f); + return RADEON_READ(RADEON_CLOCK_CNTL_DATA); +} + +static u32 RADEON_READ_PCIE(drm_radeon_private_t *dev_priv, int addr) +{ + RADEON_WRITE8(RADEON_PCIE_INDEX, addr & 0xff); + return RADEON_READ(RADEON_PCIE_DATA); +} + +#if RADEON_FIFO_DEBUG +static void radeon_status(drm_radeon_private_t * dev_priv) +{ + printk("%s:\n", __func__); + printk("RBBM_STATUS = 0x%08x\n", + (unsigned int)RADEON_READ(RADEON_RBBM_STATUS)); + printk("CP_RB_RTPR = 0x%08x\n", + (unsigned int)RADEON_READ(RADEON_CP_RB_RPTR)); + printk("CP_RB_WTPR = 0x%08x\n", + (unsigned int)RADEON_READ(RADEON_CP_RB_WPTR)); + printk("AIC_CNTL = 0x%08x\n", + (unsigned int)RADEON_READ(RADEON_AIC_CNTL)); + printk("AIC_STAT = 0x%08x\n", + (unsigned int)RADEON_READ(RADEON_AIC_STAT)); + printk("AIC_PT_BASE = 0x%08x\n", + (unsigned int)RADEON_READ(RADEON_AIC_PT_BASE)); + printk("TLB_ADDR = 0x%08x\n", + (unsigned int)RADEON_READ(RADEON_AIC_TLB_ADDR)); + printk("TLB_DATA = 0x%08x\n", + (unsigned int)RADEON_READ(RADEON_AIC_TLB_DATA)); +} +#endif + +/* ================================================================ + * Engine, FIFO control + */ + +static int radeon_do_pixcache_flush(drm_radeon_private_t * dev_priv) +{ + u32 tmp; + int i; + + dev_priv->stats.boxes |= RADEON_BOX_WAIT_IDLE; + + if ((dev_priv->flags & RADEON_FAMILY_MASK) <= CHIP_RV280) { + tmp = RADEON_READ(RADEON_RB3D_DSTCACHE_CTLSTAT); + tmp |= RADEON_RB3D_DC_FLUSH_ALL; + RADEON_WRITE(RADEON_RB3D_DSTCACHE_CTLSTAT, tmp); + + for (i = 0; i < dev_priv->usec_timeout; i++) { + if (!(RADEON_READ(RADEON_RB3D_DSTCACHE_CTLSTAT) + & RADEON_RB3D_DC_BUSY)) { + return 0; + } + DRM_UDELAY(1); + } + } else { + /* don't flush or purge cache here or lockup */ + return 0; + } + +#if RADEON_FIFO_DEBUG + DRM_ERROR("failed!\n"); + radeon_status(dev_priv); +#endif + return -EBUSY; +} + +static int radeon_do_wait_for_fifo(drm_radeon_private_t * dev_priv, int entries) +{ + int i; + + dev_priv->stats.boxes |= RADEON_BOX_WAIT_IDLE; + + for (i = 0; i < dev_priv->usec_timeout; i++) { + int slots = (RADEON_READ(RADEON_RBBM_STATUS) + & RADEON_RBBM_FIFOCNT_MASK); + if (slots >= entries) + return 0; + DRM_UDELAY(1); + } + DRM_DEBUG("wait for fifo failed status : 0x%08X 0x%08X\n", + RADEON_READ(RADEON_RBBM_STATUS), + RADEON_READ(R300_VAP_CNTL_STATUS)); + +#if RADEON_FIFO_DEBUG + DRM_ERROR("failed!\n"); + radeon_status(dev_priv); +#endif + return -EBUSY; +} + +static int radeon_do_wait_for_idle(drm_radeon_private_t * dev_priv) +{ + int i, ret; + + dev_priv->stats.boxes |= RADEON_BOX_WAIT_IDLE; + + ret = radeon_do_wait_for_fifo(dev_priv, 64); + if (ret) + return ret; + + for (i = 0; i < dev_priv->usec_timeout; i++) { + if (!(RADEON_READ(RADEON_RBBM_STATUS) + & RADEON_RBBM_ACTIVE)) { + radeon_do_pixcache_flush(dev_priv); + return 0; + } + DRM_UDELAY(1); + } + DRM_DEBUG("wait idle failed status : 0x%08X 0x%08X\n", + RADEON_READ(RADEON_RBBM_STATUS), + RADEON_READ(R300_VAP_CNTL_STATUS)); + +#if RADEON_FIFO_DEBUG + DRM_ERROR("failed!\n"); + radeon_status(dev_priv); +#endif + return -EBUSY; +} + +static void radeon_init_pipes(struct drm_device *dev) +{ + drm_radeon_private_t *dev_priv = dev->dev_private; + uint32_t gb_tile_config, gb_pipe_sel = 0; + + if ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RV530) { + uint32_t z_pipe_sel = RADEON_READ(RV530_GB_PIPE_SELECT2); + if ((z_pipe_sel & 3) == 3) + dev_priv->num_z_pipes = 2; + else + dev_priv->num_z_pipes = 1; + } else + dev_priv->num_z_pipes = 1; + + /* RS4xx/RS6xx/R4xx/R5xx */ + if ((dev_priv->flags & RADEON_FAMILY_MASK) >= CHIP_R420) { + gb_pipe_sel = RADEON_READ(R400_GB_PIPE_SELECT); + dev_priv->num_gb_pipes = ((gb_pipe_sel >> 12) & 0x3) + 1; + /* SE cards have 1 pipe */ + if ((dev->pci_device == 0x5e4c) || + (dev->pci_device == 0x5e4f)) + dev_priv->num_gb_pipes = 1; + } else { + /* R3xx */ + if (((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_R300 && + dev->pci_device != 0x4144) || + ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_R350 && + dev->pci_device != 0x4148)) { + dev_priv->num_gb_pipes = 2; + } else { + /* RV3xx/R300 AD/R350 AH */ + dev_priv->num_gb_pipes = 1; + } + } + DRM_INFO("Num pipes: %d\n", dev_priv->num_gb_pipes); + + gb_tile_config = (R300_ENABLE_TILING | R300_TILE_SIZE_16 /*| R300_SUBPIXEL_1_16*/); + + switch (dev_priv->num_gb_pipes) { + case 2: gb_tile_config |= R300_PIPE_COUNT_R300; break; + case 3: gb_tile_config |= R300_PIPE_COUNT_R420_3P; break; + case 4: gb_tile_config |= R300_PIPE_COUNT_R420; break; + default: + case 1: gb_tile_config |= R300_PIPE_COUNT_RV350; break; + } + + if ((dev_priv->flags & RADEON_FAMILY_MASK) >= CHIP_RV515) { + RADEON_WRITE_PLL(R500_DYN_SCLK_PWMEM_PIPE, (1 | ((gb_pipe_sel >> 8) & 0xf) << 4)); + RADEON_WRITE(R300_SU_REG_DEST, ((1 << dev_priv->num_gb_pipes) - 1)); + } + RADEON_WRITE(R300_GB_TILE_CONFIG, gb_tile_config); + radeon_do_wait_for_idle(dev_priv); + RADEON_WRITE(R300_DST_PIPE_CONFIG, RADEON_READ(R300_DST_PIPE_CONFIG) | R300_PIPE_AUTO_CONFIG); + RADEON_WRITE(R300_RB2D_DSTCACHE_MODE, (RADEON_READ(R300_RB2D_DSTCACHE_MODE) | + R300_DC_AUTOFLUSH_ENABLE | + R300_DC_DC_DISABLE_IGNORE_PE)); + + +} + +/* ================================================================ + * CP control, initialization + */ + +/* Load the microcode for the CP */ +static int radeon_cp_init_microcode(drm_radeon_private_t *dev_priv) +{ + const char *fw_name = NULL; + int err; + + DRM_DEBUG("\n"); + + if (((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_R100) || + ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RV100) || + ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RV200) || + ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RS100) || + ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RS200)) { + DRM_INFO("Loading R100 Microcode\n"); + fw_name = FIRMWARE_R100; + } else if (((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_R200) || + ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RV250) || + ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RV280) || + ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RS300)) { + DRM_INFO("Loading R200 Microcode\n"); + fw_name = FIRMWARE_R200; + } else if (((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_R300) || + ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_R350) || + ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RV350) || + ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RV380) || + ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RS400) || + ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RS480)) { + DRM_INFO("Loading R300 Microcode\n"); + fw_name = FIRMWARE_R300; + } else if (((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_R420) || + ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_R423) || + ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RV410)) { + DRM_INFO("Loading R400 Microcode\n"); + fw_name = FIRMWARE_R420; + } else if (((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RS690) || + ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RS740)) { + DRM_INFO("Loading RS690/RS740 Microcode\n"); + fw_name = FIRMWARE_RS690; + } else if ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RS600) { + DRM_INFO("Loading RS600 Microcode\n"); + fw_name = FIRMWARE_RS600; + } else if (((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RV515) || + ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_R520) || + ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RV530) || + ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_R580) || + ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RV560) || + ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RV570)) { + DRM_INFO("Loading R500 Microcode\n"); + fw_name = FIRMWARE_R520; + } + + err = 0; + + dev_priv->me_fw = firmware_get(fw_name); + if (dev_priv->me_fw == NULL) { + err = -ENOENT; + DRM_ERROR("radeon_cp: Failed to load firmware \"%s\"\n", + fw_name); + } else if (dev_priv->me_fw->datasize % 8) { + DRM_ERROR( + "radeon_cp: Bogus length %zu in firmware \"%s\"\n", + dev_priv->me_fw->datasize, fw_name); + err = -EINVAL; + firmware_put(dev_priv->me_fw, FIRMWARE_UNLOAD); + dev_priv->me_fw = NULL; + } + return err; +} + +static void radeon_cp_load_microcode(drm_radeon_private_t *dev_priv) +{ + const __be32 *fw_data; + int i, size; + + radeon_do_wait_for_idle(dev_priv); + + if (dev_priv->me_fw) { + size = dev_priv->me_fw->datasize / 4; + fw_data = (const __be32 *)dev_priv->me_fw->data; + RADEON_WRITE(RADEON_CP_ME_RAM_ADDR, 0); + for (i = 0; i < size; i += 2) { + RADEON_WRITE(RADEON_CP_ME_RAM_DATAH, + be32_to_cpup(&fw_data[i])); + RADEON_WRITE(RADEON_CP_ME_RAM_DATAL, + be32_to_cpup(&fw_data[i + 1])); + } + } +} + +/* Flush any pending commands to the CP. This should only be used just + * prior to a wait for idle, as it informs the engine that the command + * stream is ending. + */ +static void radeon_do_cp_flush(drm_radeon_private_t * dev_priv) +{ + DRM_DEBUG("\n"); +#if 0 + u32 tmp; + + tmp = RADEON_READ(RADEON_CP_RB_WPTR) | (1 << 31); + RADEON_WRITE(RADEON_CP_RB_WPTR, tmp); +#endif +} + +/* Wait for the CP to go idle. + */ +int radeon_do_cp_idle(drm_radeon_private_t * dev_priv) +{ + RING_LOCALS; + DRM_DEBUG("\n"); + + BEGIN_RING(6); + + RADEON_PURGE_CACHE(); + RADEON_PURGE_ZCACHE(); + RADEON_WAIT_UNTIL_IDLE(); + + ADVANCE_RING(); + COMMIT_RING(); + + return radeon_do_wait_for_idle(dev_priv); +} + +/* Start the Command Processor. + */ +static void radeon_do_cp_start(drm_radeon_private_t * dev_priv) +{ + RING_LOCALS; + DRM_DEBUG("\n"); + + radeon_do_wait_for_idle(dev_priv); + + RADEON_WRITE(RADEON_CP_CSQ_CNTL, dev_priv->cp_mode); + + dev_priv->cp_running = 1; + + /* on r420, any DMA from CP to system memory while 2D is active + * can cause a hang. workaround is to queue a CP RESYNC token + */ + if ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_R420) { + BEGIN_RING(3); + OUT_RING(CP_PACKET0(R300_CP_RESYNC_ADDR, 1)); + OUT_RING(5); /* scratch reg 5 */ + OUT_RING(0xdeadbeef); + ADVANCE_RING(); + COMMIT_RING(); + } + + BEGIN_RING(8); + /* isync can only be written through cp on r5xx write it here */ + OUT_RING(CP_PACKET0(RADEON_ISYNC_CNTL, 0)); + OUT_RING(RADEON_ISYNC_ANY2D_IDLE3D | + RADEON_ISYNC_ANY3D_IDLE2D | + RADEON_ISYNC_WAIT_IDLEGUI | + RADEON_ISYNC_CPSCRATCH_IDLEGUI); + RADEON_PURGE_CACHE(); + RADEON_PURGE_ZCACHE(); + RADEON_WAIT_UNTIL_IDLE(); + ADVANCE_RING(); + COMMIT_RING(); + + dev_priv->track_flush |= RADEON_FLUSH_EMITED | RADEON_PURGE_EMITED; +} + +/* Reset the Command Processor. This will not flush any pending + * commands, so you must wait for the CP command stream to complete + * before calling this routine. + */ +static void radeon_do_cp_reset(drm_radeon_private_t * dev_priv) +{ + u32 cur_read_ptr; + DRM_DEBUG("\n"); + + cur_read_ptr = RADEON_READ(RADEON_CP_RB_RPTR); + RADEON_WRITE(RADEON_CP_RB_WPTR, cur_read_ptr); + SET_RING_HEAD(dev_priv, cur_read_ptr); + dev_priv->ring.tail = cur_read_ptr; +} + +/* Stop the Command Processor. This will not flush any pending + * commands, so you must flush the command stream and wait for the CP + * to go idle before calling this routine. + */ +static void radeon_do_cp_stop(drm_radeon_private_t * dev_priv) +{ + RING_LOCALS; + DRM_DEBUG("\n"); + + /* finish the pending CP_RESYNC token */ + if ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_R420) { + BEGIN_RING(2); + OUT_RING(CP_PACKET0(R300_RB3D_DSTCACHE_CTLSTAT, 0)); + OUT_RING(R300_RB3D_DC_FINISH); + ADVANCE_RING(); + COMMIT_RING(); + radeon_do_wait_for_idle(dev_priv); + } + + RADEON_WRITE(RADEON_CP_CSQ_CNTL, RADEON_CSQ_PRIDIS_INDDIS); + + dev_priv->cp_running = 0; +} + +/* Reset the engine. This will stop the CP if it is running. + */ +static int radeon_do_engine_reset(struct drm_device * dev) +{ + drm_radeon_private_t *dev_priv = dev->dev_private; + u32 clock_cntl_index = 0, mclk_cntl = 0, rbbm_soft_reset; + DRM_DEBUG("\n"); + + radeon_do_pixcache_flush(dev_priv); + + if ((dev_priv->flags & RADEON_FAMILY_MASK) <= CHIP_RV410) { + /* may need something similar for newer chips */ + clock_cntl_index = RADEON_READ(RADEON_CLOCK_CNTL_INDEX); + mclk_cntl = RADEON_READ_PLL(dev, RADEON_MCLK_CNTL); + + RADEON_WRITE_PLL(RADEON_MCLK_CNTL, (mclk_cntl | + RADEON_FORCEON_MCLKA | + RADEON_FORCEON_MCLKB | + RADEON_FORCEON_YCLKA | + RADEON_FORCEON_YCLKB | + RADEON_FORCEON_MC | + RADEON_FORCEON_AIC)); + } + + rbbm_soft_reset = RADEON_READ(RADEON_RBBM_SOFT_RESET); + + RADEON_WRITE(RADEON_RBBM_SOFT_RESET, (rbbm_soft_reset | + RADEON_SOFT_RESET_CP | + RADEON_SOFT_RESET_HI | + RADEON_SOFT_RESET_SE | + RADEON_SOFT_RESET_RE | + RADEON_SOFT_RESET_PP | + RADEON_SOFT_RESET_E2 | + RADEON_SOFT_RESET_RB)); + RADEON_READ(RADEON_RBBM_SOFT_RESET); + RADEON_WRITE(RADEON_RBBM_SOFT_RESET, (rbbm_soft_reset & + ~(RADEON_SOFT_RESET_CP | + RADEON_SOFT_RESET_HI | + RADEON_SOFT_RESET_SE | + RADEON_SOFT_RESET_RE | + RADEON_SOFT_RESET_PP | + RADEON_SOFT_RESET_E2 | + RADEON_SOFT_RESET_RB))); + RADEON_READ(RADEON_RBBM_SOFT_RESET); + + if ((dev_priv->flags & RADEON_FAMILY_MASK) <= CHIP_RV410) { + RADEON_WRITE_PLL(RADEON_MCLK_CNTL, mclk_cntl); + RADEON_WRITE(RADEON_CLOCK_CNTL_INDEX, clock_cntl_index); + RADEON_WRITE(RADEON_RBBM_SOFT_RESET, rbbm_soft_reset); + } + + /* setup the raster pipes */ + if ((dev_priv->flags & RADEON_FAMILY_MASK) >= CHIP_R300) + radeon_init_pipes(dev); + + /* Reset the CP ring */ + radeon_do_cp_reset(dev_priv); + + /* The CP is no longer running after an engine reset */ + dev_priv->cp_running = 0; + + /* Reset any pending vertex, indirect buffers */ + radeon_freelist_reset(dev); + + return 0; +} + +static void radeon_cp_init_ring_buffer(struct drm_device * dev, + drm_radeon_private_t *dev_priv, + struct drm_file *file_priv) +{ + struct drm_radeon_master_private *master_priv; + u32 ring_start, cur_read_ptr; + + /* Initialize the memory controller. With new memory map, the fb location + * is not changed, it should have been properly initialized already. Part + * of the problem is that the code below is bogus, assuming the GART is + * always appended to the fb which is not necessarily the case + */ + if (!dev_priv->new_memmap) + radeon_write_fb_location(dev_priv, + ((dev_priv->gart_vm_start - 1) & 0xffff0000) + | (dev_priv->fb_location >> 16)); + +#if __OS_HAS_AGP + if (dev_priv->flags & RADEON_IS_AGP) { + radeon_write_agp_base(dev_priv, dev->agp->base); + + radeon_write_agp_location(dev_priv, + (((dev_priv->gart_vm_start - 1 + + dev_priv->gart_size) & 0xffff0000) | + (dev_priv->gart_vm_start >> 16))); + + ring_start = (dev_priv->cp_ring->offset + - dev->agp->base + + dev_priv->gart_vm_start); + } else +#endif + ring_start = (dev_priv->cp_ring->offset + - (unsigned long)dev->sg->vaddr + + dev_priv->gart_vm_start); + + RADEON_WRITE(RADEON_CP_RB_BASE, ring_start); + + /* Set the write pointer delay */ + RADEON_WRITE(RADEON_CP_RB_WPTR_DELAY, 0); + + /* Initialize the ring buffer's read and write pointers */ + cur_read_ptr = RADEON_READ(RADEON_CP_RB_RPTR); + RADEON_WRITE(RADEON_CP_RB_WPTR, cur_read_ptr); + SET_RING_HEAD(dev_priv, cur_read_ptr); + dev_priv->ring.tail = cur_read_ptr; + +#if __OS_HAS_AGP + if (dev_priv->flags & RADEON_IS_AGP) { + RADEON_WRITE(RADEON_CP_RB_RPTR_ADDR, + dev_priv->ring_rptr->offset + - dev->agp->base + dev_priv->gart_vm_start); + } else +#endif + { + RADEON_WRITE(RADEON_CP_RB_RPTR_ADDR, + dev_priv->ring_rptr->offset + - ((unsigned long) dev->sg->vaddr) + + dev_priv->gart_vm_start); + } + + /* Set ring buffer size */ +#ifdef __BIG_ENDIAN + RADEON_WRITE(RADEON_CP_RB_CNTL, + RADEON_BUF_SWAP_32BIT | + (dev_priv->ring.fetch_size_l2ow << 18) | + (dev_priv->ring.rptr_update_l2qw << 8) | + dev_priv->ring.size_l2qw); +#else + RADEON_WRITE(RADEON_CP_RB_CNTL, + (dev_priv->ring.fetch_size_l2ow << 18) | + (dev_priv->ring.rptr_update_l2qw << 8) | + dev_priv->ring.size_l2qw); +#endif + + + /* Initialize the scratch register pointer. This will cause + * the scratch register values to be written out to memory + * whenever they are updated. + * + * We simply put this behind the ring read pointer, this works + * with PCI GART as well as (whatever kind of) AGP GART + */ + RADEON_WRITE(RADEON_SCRATCH_ADDR, RADEON_READ(RADEON_CP_RB_RPTR_ADDR) + + RADEON_SCRATCH_REG_OFFSET); + + RADEON_WRITE(RADEON_SCRATCH_UMSK, 0x7); + + radeon_enable_bm(dev_priv); + + radeon_write_ring_rptr(dev_priv, RADEON_SCRATCHOFF(0), 0); + RADEON_WRITE(RADEON_LAST_FRAME_REG, 0); + + radeon_write_ring_rptr(dev_priv, RADEON_SCRATCHOFF(1), 0); + RADEON_WRITE(RADEON_LAST_DISPATCH_REG, 0); + + radeon_write_ring_rptr(dev_priv, RADEON_SCRATCHOFF(2), 0); + RADEON_WRITE(RADEON_LAST_CLEAR_REG, 0); + + /* reset sarea copies of these */ + master_priv = file_priv->masterp->driver_priv; + if (master_priv->sarea_priv) { + master_priv->sarea_priv->last_frame = 0; + master_priv->sarea_priv->last_dispatch = 0; + master_priv->sarea_priv->last_clear = 0; + } + + radeon_do_wait_for_idle(dev_priv); + + /* Sync everything up */ + RADEON_WRITE(RADEON_ISYNC_CNTL, + (RADEON_ISYNC_ANY2D_IDLE3D | + RADEON_ISYNC_ANY3D_IDLE2D | + RADEON_ISYNC_WAIT_IDLEGUI | + RADEON_ISYNC_CPSCRATCH_IDLEGUI)); + +} + +static void radeon_test_writeback(drm_radeon_private_t * dev_priv) +{ + u32 tmp; + + /* Start with assuming that writeback doesn't work */ + dev_priv->writeback_works = 0; + + /* Writeback doesn't seem to work everywhere, test it here and possibly + * enable it if it appears to work + */ + radeon_write_ring_rptr(dev_priv, RADEON_SCRATCHOFF(1), 0); + + RADEON_WRITE(RADEON_SCRATCH_REG1, 0xdeadbeef); + + for (tmp = 0; tmp < dev_priv->usec_timeout; tmp++) { + u32 val; + + val = radeon_read_ring_rptr(dev_priv, RADEON_SCRATCHOFF(1)); + if (val == 0xdeadbeef) + break; + DRM_UDELAY(1); + } + + if (tmp < dev_priv->usec_timeout) { + dev_priv->writeback_works = 1; + DRM_INFO("writeback test succeeded in %d usecs\n", tmp); + } else { + dev_priv->writeback_works = 0; + DRM_INFO("writeback test failed\n"); + } + if (radeon_no_wb == 1) { + dev_priv->writeback_works = 0; + DRM_INFO("writeback forced off\n"); + } + + if (!dev_priv->writeback_works) { + /* Disable writeback to avoid unnecessary bus master transfer */ + RADEON_WRITE(RADEON_CP_RB_CNTL, RADEON_READ(RADEON_CP_RB_CNTL) | + RADEON_RB_NO_UPDATE); + RADEON_WRITE(RADEON_SCRATCH_UMSK, 0); + } +} + +/* Enable or disable IGP GART on the chip */ +static void radeon_set_igpgart(drm_radeon_private_t * dev_priv, int on) +{ + u32 temp; + + if (on) { + DRM_DEBUG("programming igp gart %08X %08lX %08X\n", + dev_priv->gart_vm_start, + (long)dev_priv->gart_info.bus_addr, + dev_priv->gart_size); + + temp = IGP_READ_MCIND(dev_priv, RS480_MC_MISC_CNTL); + if (((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RS690) || + ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RS740)) + IGP_WRITE_MCIND(RS480_MC_MISC_CNTL, (RS480_GART_INDEX_REG_EN | + RS690_BLOCK_GFX_D3_EN)); + else + IGP_WRITE_MCIND(RS480_MC_MISC_CNTL, RS480_GART_INDEX_REG_EN); + + IGP_WRITE_MCIND(RS480_AGP_ADDRESS_SPACE_SIZE, (RS480_GART_EN | + RS480_VA_SIZE_32MB)); + + temp = IGP_READ_MCIND(dev_priv, RS480_GART_FEATURE_ID); + IGP_WRITE_MCIND(RS480_GART_FEATURE_ID, (RS480_HANG_EN | + RS480_TLB_ENABLE | + RS480_GTW_LAC_EN | + RS480_1LEVEL_GART)); + + temp = dev_priv->gart_info.bus_addr & 0xfffff000; + temp |= (upper_32_bits(dev_priv->gart_info.bus_addr) & 0xff) << 4; + IGP_WRITE_MCIND(RS480_GART_BASE, temp); + + temp = IGP_READ_MCIND(dev_priv, RS480_AGP_MODE_CNTL); + IGP_WRITE_MCIND(RS480_AGP_MODE_CNTL, ((1 << RS480_REQ_TYPE_SNOOP_SHIFT) | + RS480_REQ_TYPE_SNOOP_DIS)); + + radeon_write_agp_base(dev_priv, dev_priv->gart_vm_start); + + dev_priv->gart_size = 32*1024*1024; + temp = (((dev_priv->gart_vm_start - 1 + dev_priv->gart_size) & + 0xffff0000) | (dev_priv->gart_vm_start >> 16)); + + radeon_write_agp_location(dev_priv, temp); + + temp = IGP_READ_MCIND(dev_priv, RS480_AGP_ADDRESS_SPACE_SIZE); + IGP_WRITE_MCIND(RS480_AGP_ADDRESS_SPACE_SIZE, (RS480_GART_EN | + RS480_VA_SIZE_32MB)); + + do { + temp = IGP_READ_MCIND(dev_priv, RS480_GART_CACHE_CNTRL); + if ((temp & RS480_GART_CACHE_INVALIDATE) == 0) + break; + DRM_UDELAY(1); + } while (1); + + IGP_WRITE_MCIND(RS480_GART_CACHE_CNTRL, + RS480_GART_CACHE_INVALIDATE); + + do { + temp = IGP_READ_MCIND(dev_priv, RS480_GART_CACHE_CNTRL); + if ((temp & RS480_GART_CACHE_INVALIDATE) == 0) + break; + DRM_UDELAY(1); + } while (1); + + IGP_WRITE_MCIND(RS480_GART_CACHE_CNTRL, 0); + } else { + IGP_WRITE_MCIND(RS480_AGP_ADDRESS_SPACE_SIZE, 0); + } +} + +/* Enable or disable IGP GART on the chip */ +static void rs600_set_igpgart(drm_radeon_private_t *dev_priv, int on) +{ + u32 temp; + int i; + + if (on) { + DRM_DEBUG("programming igp gart %08X %08lX %08X\n", + dev_priv->gart_vm_start, + (long)dev_priv->gart_info.bus_addr, + dev_priv->gart_size); + + IGP_WRITE_MCIND(RS600_MC_PT0_CNTL, (RS600_EFFECTIVE_L2_CACHE_SIZE(6) | + RS600_EFFECTIVE_L2_QUEUE_SIZE(6))); + + for (i = 0; i < 19; i++) + IGP_WRITE_MCIND(RS600_MC_PT0_CLIENT0_CNTL + i, + (RS600_ENABLE_TRANSLATION_MODE_OVERRIDE | + RS600_SYSTEM_ACCESS_MODE_IN_SYS | + RS600_SYSTEM_APERTURE_UNMAPPED_ACCESS_PASSTHROUGH | + RS600_EFFECTIVE_L1_CACHE_SIZE(3) | + RS600_ENABLE_FRAGMENT_PROCESSING | + RS600_EFFECTIVE_L1_QUEUE_SIZE(3))); + + IGP_WRITE_MCIND(RS600_MC_PT0_CONTEXT0_CNTL, (RS600_ENABLE_PAGE_TABLE | + RS600_PAGE_TABLE_TYPE_FLAT)); + + /* disable all other contexts */ + for (i = 1; i < 8; i++) + IGP_WRITE_MCIND(RS600_MC_PT0_CONTEXT0_CNTL + i, 0); + + /* setup the page table aperture */ + IGP_WRITE_MCIND(RS600_MC_PT0_CONTEXT0_FLAT_BASE_ADDR, + dev_priv->gart_info.bus_addr); + IGP_WRITE_MCIND(RS600_MC_PT0_CONTEXT0_FLAT_START_ADDR, + dev_priv->gart_vm_start); + IGP_WRITE_MCIND(RS600_MC_PT0_CONTEXT0_FLAT_END_ADDR, + (dev_priv->gart_vm_start + dev_priv->gart_size - 1)); + IGP_WRITE_MCIND(RS600_MC_PT0_CONTEXT0_DEFAULT_READ_ADDR, 0); + + /* setup the system aperture */ + IGP_WRITE_MCIND(RS600_MC_PT0_SYSTEM_APERTURE_LOW_ADDR, + dev_priv->gart_vm_start); + IGP_WRITE_MCIND(RS600_MC_PT0_SYSTEM_APERTURE_HIGH_ADDR, + (dev_priv->gart_vm_start + dev_priv->gart_size - 1)); + + /* enable page tables */ + temp = IGP_READ_MCIND(dev_priv, RS600_MC_PT0_CNTL); + IGP_WRITE_MCIND(RS600_MC_PT0_CNTL, (temp | RS600_ENABLE_PT)); + + temp = IGP_READ_MCIND(dev_priv, RS600_MC_CNTL1); + IGP_WRITE_MCIND(RS600_MC_CNTL1, (temp | RS600_ENABLE_PAGE_TABLES)); + + /* invalidate the cache */ + temp = IGP_READ_MCIND(dev_priv, RS600_MC_PT0_CNTL); + + temp &= ~(RS600_INVALIDATE_ALL_L1_TLBS | RS600_INVALIDATE_L2_CACHE); + IGP_WRITE_MCIND(RS600_MC_PT0_CNTL, temp); + temp = IGP_READ_MCIND(dev_priv, RS600_MC_PT0_CNTL); + + temp |= RS600_INVALIDATE_ALL_L1_TLBS | RS600_INVALIDATE_L2_CACHE; + IGP_WRITE_MCIND(RS600_MC_PT0_CNTL, temp); + temp = IGP_READ_MCIND(dev_priv, RS600_MC_PT0_CNTL); + + temp &= ~(RS600_INVALIDATE_ALL_L1_TLBS | RS600_INVALIDATE_L2_CACHE); + IGP_WRITE_MCIND(RS600_MC_PT0_CNTL, temp); + temp = IGP_READ_MCIND(dev_priv, RS600_MC_PT0_CNTL); + + } else { + IGP_WRITE_MCIND(RS600_MC_PT0_CNTL, 0); + temp = IGP_READ_MCIND(dev_priv, RS600_MC_CNTL1); + temp &= ~RS600_ENABLE_PAGE_TABLES; + IGP_WRITE_MCIND(RS600_MC_CNTL1, temp); + } +} + +static void radeon_set_pciegart(drm_radeon_private_t * dev_priv, int on) +{ + u32 tmp = RADEON_READ_PCIE(dev_priv, RADEON_PCIE_TX_GART_CNTL); + if (on) { + + DRM_DEBUG("programming pcie %08X %08lX %08X\n", + dev_priv->gart_vm_start, + (long)dev_priv->gart_info.bus_addr, + dev_priv->gart_size); + RADEON_WRITE_PCIE(RADEON_PCIE_TX_DISCARD_RD_ADDR_LO, + dev_priv->gart_vm_start); + RADEON_WRITE_PCIE(RADEON_PCIE_TX_GART_BASE, + dev_priv->gart_info.bus_addr); + RADEON_WRITE_PCIE(RADEON_PCIE_TX_GART_START_LO, + dev_priv->gart_vm_start); + RADEON_WRITE_PCIE(RADEON_PCIE_TX_GART_END_LO, + dev_priv->gart_vm_start + + dev_priv->gart_size - 1); + + radeon_write_agp_location(dev_priv, 0xffffffc0); /* ?? */ + + RADEON_WRITE_PCIE(RADEON_PCIE_TX_GART_CNTL, + RADEON_PCIE_TX_GART_EN); + } else { + RADEON_WRITE_PCIE(RADEON_PCIE_TX_GART_CNTL, + tmp & ~RADEON_PCIE_TX_GART_EN); + } +} + +/* Enable or disable PCI GART on the chip */ +static void radeon_set_pcigart(drm_radeon_private_t * dev_priv, int on) +{ + u32 tmp; + + if (((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RS690) || + ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RS740) || + (dev_priv->flags & RADEON_IS_IGPGART)) { + radeon_set_igpgart(dev_priv, on); + return; + } + + if ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RS600) { + rs600_set_igpgart(dev_priv, on); + return; + } + + if (dev_priv->flags & RADEON_IS_PCIE) { + radeon_set_pciegart(dev_priv, on); + return; + } + + tmp = RADEON_READ(RADEON_AIC_CNTL); + + if (on) { + RADEON_WRITE(RADEON_AIC_CNTL, + tmp | RADEON_PCIGART_TRANSLATE_EN); + + /* set PCI GART page-table base address + */ + RADEON_WRITE(RADEON_AIC_PT_BASE, dev_priv->gart_info.bus_addr); + + /* set address range for PCI address translate + */ + RADEON_WRITE(RADEON_AIC_LO_ADDR, dev_priv->gart_vm_start); + RADEON_WRITE(RADEON_AIC_HI_ADDR, dev_priv->gart_vm_start + + dev_priv->gart_size - 1); + + /* Turn off AGP aperture -- is this required for PCI GART? + */ + radeon_write_agp_location(dev_priv, 0xffffffc0); + RADEON_WRITE(RADEON_AGP_COMMAND, 0); /* clear AGP_COMMAND */ + } else { + RADEON_WRITE(RADEON_AIC_CNTL, + tmp & ~RADEON_PCIGART_TRANSLATE_EN); + } +} + +static int radeon_setup_pcigart_surface(drm_radeon_private_t *dev_priv) +{ + struct drm_ati_pcigart_info *gart_info = &dev_priv->gart_info; + struct radeon_virt_surface *vp; + int i; + + for (i = 0; i < RADEON_MAX_SURFACES * 2; i++) { + if (!dev_priv->virt_surfaces[i].file_priv || + dev_priv->virt_surfaces[i].file_priv == PCIGART_FILE_PRIV) + break; + } + if (i >= 2 * RADEON_MAX_SURFACES) + return -ENOMEM; + vp = &dev_priv->virt_surfaces[i]; + + for (i = 0; i < RADEON_MAX_SURFACES; i++) { + struct radeon_surface *sp = &dev_priv->surfaces[i]; + if (sp->refcount) + continue; + + vp->surface_index = i; + vp->lower = gart_info->bus_addr; + vp->upper = vp->lower + gart_info->table_size; + vp->flags = 0; + vp->file_priv = PCIGART_FILE_PRIV; + + sp->refcount = 1; + sp->lower = vp->lower; + sp->upper = vp->upper; + sp->flags = 0; + + RADEON_WRITE(RADEON_SURFACE0_INFO + 16 * i, sp->flags); + RADEON_WRITE(RADEON_SURFACE0_LOWER_BOUND + 16 * i, sp->lower); + RADEON_WRITE(RADEON_SURFACE0_UPPER_BOUND + 16 * i, sp->upper); + return 0; + } + + return -ENOMEM; +} + +static int radeon_do_init_cp(struct drm_device *dev, drm_radeon_init_t *init, + struct drm_file *file_priv) +{ + drm_radeon_private_t *dev_priv = dev->dev_private; + struct drm_radeon_master_private *master_priv = file_priv->masterp->driver_priv; + + DRM_DEBUG("\n"); + + /* if we require new memory map but we don't have it fail */ + if ((dev_priv->flags & RADEON_NEW_MEMMAP) && !dev_priv->new_memmap) { + DRM_ERROR("Cannot initialise DRM on this card\nThis card requires a new X.org DDX for 3D\n"); + radeon_do_cleanup_cp(dev); + return -EINVAL; + } + + if (init->is_pci && (dev_priv->flags & RADEON_IS_AGP)) { + DRM_DEBUG("Forcing AGP card to PCI mode\n"); + dev_priv->flags &= ~RADEON_IS_AGP; + } else if (!(dev_priv->flags & (RADEON_IS_AGP | RADEON_IS_PCI | RADEON_IS_PCIE)) + && !init->is_pci) { + DRM_DEBUG("Restoring AGP flag\n"); + dev_priv->flags |= RADEON_IS_AGP; + } + + if ((!(dev_priv->flags & RADEON_IS_AGP)) && !dev->sg) { + DRM_ERROR("PCI GART memory not allocated!\n"); + radeon_do_cleanup_cp(dev); + return -EINVAL; + } + + dev_priv->usec_timeout = init->usec_timeout; + if (dev_priv->usec_timeout < 1 || + dev_priv->usec_timeout > RADEON_MAX_USEC_TIMEOUT) { + DRM_DEBUG("TIMEOUT problem!\n"); + radeon_do_cleanup_cp(dev); + return -EINVAL; + } + + /* Enable vblank on CRTC1 for older X servers + */ + dev_priv->vblank_crtc = DRM_RADEON_VBLANK_CRTC1; + + switch(init->func) { + case RADEON_INIT_R200_CP: + dev_priv->microcode_version = UCODE_R200; + break; + case RADEON_INIT_R300_CP: + dev_priv->microcode_version = UCODE_R300; + break; + default: + dev_priv->microcode_version = UCODE_R100; + } + + dev_priv->do_boxes = 0; + dev_priv->cp_mode = init->cp_mode; + + /* We don't support anything other than bus-mastering ring mode, + * but the ring can be in either AGP or PCI space for the ring + * read pointer. + */ + if ((init->cp_mode != RADEON_CSQ_PRIBM_INDDIS) && + (init->cp_mode != RADEON_CSQ_PRIBM_INDBM)) { + DRM_DEBUG("BAD cp_mode (%x)!\n", init->cp_mode); + radeon_do_cleanup_cp(dev); + return -EINVAL; + } + + switch (init->fb_bpp) { + case 16: + dev_priv->color_fmt = RADEON_COLOR_FORMAT_RGB565; + break; + case 32: + default: + dev_priv->color_fmt = RADEON_COLOR_FORMAT_ARGB8888; + break; + } + dev_priv->front_offset = init->front_offset; + dev_priv->front_pitch = init->front_pitch; + dev_priv->back_offset = init->back_offset; + dev_priv->back_pitch = init->back_pitch; + + switch (init->depth_bpp) { + case 16: + dev_priv->depth_fmt = RADEON_DEPTH_FORMAT_16BIT_INT_Z; + break; + case 32: + default: + dev_priv->depth_fmt = RADEON_DEPTH_FORMAT_24BIT_INT_Z; + break; + } + dev_priv->depth_offset = init->depth_offset; + dev_priv->depth_pitch = init->depth_pitch; + + /* Hardware state for depth clears. Remove this if/when we no + * longer clear the depth buffer with a 3D rectangle. Hard-code + * all values to prevent unwanted 3D state from slipping through + * and screwing with the clear operation. + */ + dev_priv->depth_clear.rb3d_cntl = (RADEON_PLANE_MASK_ENABLE | + (dev_priv->color_fmt << 10) | + (dev_priv->microcode_version == + UCODE_R100 ? RADEON_ZBLOCK16 : 0)); + + dev_priv->depth_clear.rb3d_zstencilcntl = + (dev_priv->depth_fmt | + RADEON_Z_TEST_ALWAYS | + RADEON_STENCIL_TEST_ALWAYS | + RADEON_STENCIL_S_FAIL_REPLACE | + RADEON_STENCIL_ZPASS_REPLACE | + RADEON_STENCIL_ZFAIL_REPLACE | RADEON_Z_WRITE_ENABLE); + + dev_priv->depth_clear.se_cntl = (RADEON_FFACE_CULL_CW | + RADEON_BFACE_SOLID | + RADEON_FFACE_SOLID | + RADEON_FLAT_SHADE_VTX_LAST | + RADEON_DIFFUSE_SHADE_FLAT | + RADEON_ALPHA_SHADE_FLAT | + RADEON_SPECULAR_SHADE_FLAT | + RADEON_FOG_SHADE_FLAT | + RADEON_VTX_PIX_CENTER_OGL | + RADEON_ROUND_MODE_TRUNC | + RADEON_ROUND_PREC_8TH_PIX); + + + dev_priv->ring_offset = init->ring_offset; + dev_priv->ring_rptr_offset = init->ring_rptr_offset; + dev_priv->buffers_offset = init->buffers_offset; + dev_priv->gart_textures_offset = init->gart_textures_offset; + + master_priv->sarea = drm_getsarea(dev); + if (!master_priv->sarea) { + DRM_ERROR("could not find sarea!\n"); + radeon_do_cleanup_cp(dev); + return -EINVAL; + } + + dev_priv->cp_ring = drm_core_findmap(dev, init->ring_offset); + if (!dev_priv->cp_ring) { + DRM_ERROR("could not find cp ring region!\n"); + radeon_do_cleanup_cp(dev); + return -EINVAL; + } + dev_priv->ring_rptr = drm_core_findmap(dev, init->ring_rptr_offset); + if (!dev_priv->ring_rptr) { + DRM_ERROR("could not find ring read pointer!\n"); + radeon_do_cleanup_cp(dev); + return -EINVAL; + } + dev->agp_buffer_token = init->buffers_offset; + dev->agp_buffer_map = drm_core_findmap(dev, init->buffers_offset); + if (!dev->agp_buffer_map) { + DRM_ERROR("could not find dma buffer region!\n"); + radeon_do_cleanup_cp(dev); + return -EINVAL; + } + + if (init->gart_textures_offset) { + dev_priv->gart_textures = + drm_core_findmap(dev, init->gart_textures_offset); + if (!dev_priv->gart_textures) { + DRM_ERROR("could not find GART texture region!\n"); + radeon_do_cleanup_cp(dev); + return -EINVAL; + } + } + +#if __OS_HAS_AGP + if (dev_priv->flags & RADEON_IS_AGP) { + drm_core_ioremap_wc(dev_priv->cp_ring, dev); + drm_core_ioremap_wc(dev_priv->ring_rptr, dev); + drm_core_ioremap_wc(dev->agp_buffer_map, dev); + if (!dev_priv->cp_ring->handle || + !dev_priv->ring_rptr->handle || + !dev->agp_buffer_map->handle) { + DRM_ERROR("could not find ioremap agp regions!\n"); + radeon_do_cleanup_cp(dev); + return -EINVAL; + } + } else +#endif + { + dev_priv->cp_ring->handle = + (void *)(unsigned long)dev_priv->cp_ring->offset; + dev_priv->ring_rptr->handle = + (void *)(unsigned long)dev_priv->ring_rptr->offset; + dev->agp_buffer_map->handle = + (void *)(unsigned long)dev->agp_buffer_map->offset; + + DRM_DEBUG("dev_priv->cp_ring->handle %p\n", + dev_priv->cp_ring->handle); + DRM_DEBUG("dev_priv->ring_rptr->handle %p\n", + dev_priv->ring_rptr->handle); + DRM_DEBUG("dev->agp_buffer_map->handle %p\n", + dev->agp_buffer_map->handle); + } + + dev_priv->fb_location = (radeon_read_fb_location(dev_priv) & 0xffff) << 16; + dev_priv->fb_size = + ((radeon_read_fb_location(dev_priv) & 0xffff0000u) + 0x10000) + - dev_priv->fb_location; + + dev_priv->front_pitch_offset = (((dev_priv->front_pitch / 64) << 22) | + ((dev_priv->front_offset + + dev_priv->fb_location) >> 10)); + + dev_priv->back_pitch_offset = (((dev_priv->back_pitch / 64) << 22) | + ((dev_priv->back_offset + + dev_priv->fb_location) >> 10)); + + dev_priv->depth_pitch_offset = (((dev_priv->depth_pitch / 64) << 22) | + ((dev_priv->depth_offset + + dev_priv->fb_location) >> 10)); + + dev_priv->gart_size = init->gart_size; + + /* New let's set the memory map ... */ + if (dev_priv->new_memmap) { + u32 base = 0; + + DRM_INFO("Setting GART location based on new memory map\n"); + + /* If using AGP, try to locate the AGP aperture at the same + * location in the card and on the bus, though we have to + * align it down. + */ +#if __OS_HAS_AGP + if (dev_priv->flags & RADEON_IS_AGP) { + base = dev->agp->base; + /* Check if valid */ + if ((base + dev_priv->gart_size - 1) >= dev_priv->fb_location && + base < (dev_priv->fb_location + dev_priv->fb_size - 1)) { + DRM_INFO("Can't use AGP base @0x%08lx, won't fit\n", + dev->agp->base); + base = 0; + } + } +#endif + /* If not or if AGP is at 0 (Macs), try to put it elsewhere */ + if (base == 0) { + base = dev_priv->fb_location + dev_priv->fb_size; + if (base < dev_priv->fb_location || + ((base + dev_priv->gart_size) & 0xfffffffful) < base) + base = dev_priv->fb_location + - dev_priv->gart_size; + } + dev_priv->gart_vm_start = base & 0xffc00000u; + if (dev_priv->gart_vm_start != base) + DRM_INFO("GART aligned down from 0x%08x to 0x%08x\n", + base, dev_priv->gart_vm_start); + } else { + DRM_INFO("Setting GART location based on old memory map\n"); + dev_priv->gart_vm_start = dev_priv->fb_location + + RADEON_READ(RADEON_CONFIG_APER_SIZE); + } + +#if __OS_HAS_AGP + if (dev_priv->flags & RADEON_IS_AGP) + dev_priv->gart_buffers_offset = (dev->agp_buffer_map->offset + - dev->agp->base + + dev_priv->gart_vm_start); + else +#endif + dev_priv->gart_buffers_offset = (dev->agp_buffer_map->offset + - (unsigned long)dev->sg->vaddr + + dev_priv->gart_vm_start); + + DRM_DEBUG("dev_priv->gart_size %d\n", dev_priv->gart_size); + DRM_DEBUG("dev_priv->gart_vm_start 0x%x\n", dev_priv->gart_vm_start); + DRM_DEBUG("dev_priv->gart_buffers_offset 0x%lx\n", + dev_priv->gart_buffers_offset); + + dev_priv->ring.start = (u32 *) dev_priv->cp_ring->handle; + dev_priv->ring.end = ((u32 *) dev_priv->cp_ring->handle + + init->ring_size / sizeof(u32)); + dev_priv->ring.size = init->ring_size; + dev_priv->ring.size_l2qw = drm_order(init->ring_size / 8); + + dev_priv->ring.rptr_update = /* init->rptr_update */ 4096; + dev_priv->ring.rptr_update_l2qw = drm_order( /* init->rptr_update */ 4096 / 8); + + dev_priv->ring.fetch_size = /* init->fetch_size */ 32; + dev_priv->ring.fetch_size_l2ow = drm_order( /* init->fetch_size */ 32 / 16); + dev_priv->ring.tail_mask = (dev_priv->ring.size / sizeof(u32)) - 1; + + dev_priv->ring.high_mark = RADEON_RING_HIGH_MARK; + +#if __OS_HAS_AGP + if (dev_priv->flags & RADEON_IS_AGP) { + /* Turn off PCI GART */ + radeon_set_pcigart(dev_priv, 0); + } else +#endif + { + u32 sctrl; + int ret; + + dev_priv->gart_info.table_mask = DMA_BIT_MASK(32); + /* if we have an offset set from userspace */ + if (dev_priv->pcigart_offset_set) { + dev_priv->gart_info.bus_addr = + (resource_size_t)dev_priv->pcigart_offset + dev_priv->fb_location; + dev_priv->gart_info.mapping.offset = + dev_priv->pcigart_offset + dev_priv->fb_aper_offset; + dev_priv->gart_info.mapping.size = + dev_priv->gart_info.table_size; + + drm_core_ioremap_wc(&dev_priv->gart_info.mapping, dev); + dev_priv->gart_info.addr = + dev_priv->gart_info.mapping.handle; + + if (dev_priv->flags & RADEON_IS_PCIE) + dev_priv->gart_info.gart_reg_if = DRM_ATI_GART_PCIE; + else + dev_priv->gart_info.gart_reg_if = DRM_ATI_GART_PCI; + dev_priv->gart_info.gart_table_location = + DRM_ATI_GART_FB; + + DRM_DEBUG("Setting phys_pci_gart to %p %08lX\n", + dev_priv->gart_info.addr, + dev_priv->pcigart_offset); + } else { + if (dev_priv->flags & RADEON_IS_IGPGART) + dev_priv->gart_info.gart_reg_if = DRM_ATI_GART_IGP; + else + dev_priv->gart_info.gart_reg_if = DRM_ATI_GART_PCI; + dev_priv->gart_info.gart_table_location = + DRM_ATI_GART_MAIN; + dev_priv->gart_info.addr = NULL; + dev_priv->gart_info.bus_addr = 0; + if (dev_priv->flags & RADEON_IS_PCIE) { + DRM_ERROR + ("Cannot use PCI Express without GART in FB memory\n"); + radeon_do_cleanup_cp(dev); + return -EINVAL; + } + } + + sctrl = RADEON_READ(RADEON_SURFACE_CNTL); + RADEON_WRITE(RADEON_SURFACE_CNTL, 0); + if ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RS600) + ret = r600_page_table_init(dev); + else + ret = drm_ati_pcigart_init(dev, &dev_priv->gart_info); + RADEON_WRITE(RADEON_SURFACE_CNTL, sctrl); + + if (!ret) { + DRM_ERROR("failed to init PCI GART!\n"); + radeon_do_cleanup_cp(dev); + return -ENOMEM; + } + + ret = radeon_setup_pcigart_surface(dev_priv); + if (ret) { + DRM_ERROR("failed to setup GART surface!\n"); + if ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RS600) + r600_page_table_cleanup(dev, &dev_priv->gart_info); + else + drm_ati_pcigart_cleanup(dev, &dev_priv->gart_info); + radeon_do_cleanup_cp(dev); + return ret; + } + + /* Turn on PCI GART */ + radeon_set_pcigart(dev_priv, 1); + } + + if (!dev_priv->me_fw) { + int err = radeon_cp_init_microcode(dev_priv); + if (err) { + DRM_ERROR("Failed to load firmware!\n"); + radeon_do_cleanup_cp(dev); + return err; + } + } + radeon_cp_load_microcode(dev_priv); + radeon_cp_init_ring_buffer(dev, dev_priv, file_priv); + + dev_priv->last_buf = 0; + + radeon_do_engine_reset(dev); + radeon_test_writeback(dev_priv); + + return 0; +} + +static int radeon_do_cleanup_cp(struct drm_device * dev) +{ + drm_radeon_private_t *dev_priv = dev->dev_private; + DRM_DEBUG("\n"); + + /* Make sure interrupts are disabled here because the uninstall ioctl + * may not have been called from userspace and after dev_private + * is freed, it's too late. + */ + if (dev->irq_enabled) + drm_irq_uninstall(dev); + +#if __OS_HAS_AGP + if (dev_priv->flags & RADEON_IS_AGP) { + if (dev_priv->cp_ring != NULL) { + drm_core_ioremapfree(dev_priv->cp_ring, dev); + dev_priv->cp_ring = NULL; + } + if (dev_priv->ring_rptr != NULL) { + drm_core_ioremapfree(dev_priv->ring_rptr, dev); + dev_priv->ring_rptr = NULL; + } + if (dev->agp_buffer_map != NULL) { + drm_core_ioremapfree(dev->agp_buffer_map, dev); + dev->agp_buffer_map = NULL; + } + } else +#endif + { + + if (dev_priv->gart_info.bus_addr) { + /* Turn off PCI GART */ + radeon_set_pcigart(dev_priv, 0); + if ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RS600) + r600_page_table_cleanup(dev, &dev_priv->gart_info); + else { + if (!drm_ati_pcigart_cleanup(dev, &dev_priv->gart_info)) + DRM_ERROR("failed to cleanup PCI GART!\n"); + } + } + + if (dev_priv->gart_info.gart_table_location == DRM_ATI_GART_FB) + { + drm_core_ioremapfree(&dev_priv->gart_info.mapping, dev); + dev_priv->gart_info.addr = NULL; + } + } + /* only clear to the start of flags */ + memset(dev_priv, 0, offsetof(drm_radeon_private_t, flags)); + + return 0; +} + +/* This code will reinit the Radeon CP hardware after a resume from disc. + * AFAIK, it would be very difficult to pickle the state at suspend time, so + * here we make sure that all Radeon hardware initialisation is re-done without + * affecting running applications. + * + * Charl P. Botha + */ +static int radeon_do_resume_cp(struct drm_device *dev, struct drm_file *file_priv) +{ + drm_radeon_private_t *dev_priv = dev->dev_private; + + if (!dev_priv) { + DRM_ERROR("Called with no initialization\n"); + return -EINVAL; + } + + DRM_DEBUG("Starting radeon_do_resume_cp()\n"); + +#if __OS_HAS_AGP + if (dev_priv->flags & RADEON_IS_AGP) { + /* Turn off PCI GART */ + radeon_set_pcigart(dev_priv, 0); + } else +#endif + { + /* Turn on PCI GART */ + radeon_set_pcigart(dev_priv, 1); + } + + radeon_cp_load_microcode(dev_priv); + radeon_cp_init_ring_buffer(dev, dev_priv, file_priv); + + dev_priv->have_z_offset = 0; + radeon_do_engine_reset(dev); + radeon_irq_set_state(dev, RADEON_SW_INT_ENABLE, 1); + + DRM_DEBUG("radeon_do_resume_cp() complete\n"); + + return 0; +} + +int radeon_cp_init(struct drm_device *dev, void *data, struct drm_file *file_priv) +{ + drm_radeon_private_t *dev_priv = dev->dev_private; + drm_radeon_init_t *init = data; + + LOCK_TEST_WITH_RETURN(dev, file_priv); + + if (init->func == RADEON_INIT_R300_CP) + r300_init_reg_flags(dev); + + switch (init->func) { + case RADEON_INIT_CP: + case RADEON_INIT_R200_CP: + case RADEON_INIT_R300_CP: + return radeon_do_init_cp(dev, init, file_priv); + case RADEON_INIT_R600_CP: + return r600_do_init_cp(dev, init, file_priv); + break; + case RADEON_CLEANUP_CP: + if ((dev_priv->flags & RADEON_FAMILY_MASK) >= CHIP_R600) + return r600_do_cleanup_cp(dev); + else + return radeon_do_cleanup_cp(dev); + } + + return -EINVAL; +} + +int radeon_cp_start(struct drm_device *dev, void *data, struct drm_file *file_priv) +{ + drm_radeon_private_t *dev_priv = dev->dev_private; + DRM_DEBUG("\n"); + + LOCK_TEST_WITH_RETURN(dev, file_priv); + + if (dev_priv->cp_running) { + DRM_DEBUG("while CP running\n"); + return 0; + } + if (dev_priv->cp_mode == RADEON_CSQ_PRIDIS_INDDIS) { + DRM_DEBUG("called with bogus CP mode (%d)\n", + dev_priv->cp_mode); + return 0; + } + + if ((dev_priv->flags & RADEON_FAMILY_MASK) >= CHIP_R600) + r600_do_cp_start(dev_priv); + else + radeon_do_cp_start(dev_priv); + + return 0; +} + +/* Stop the CP. The engine must have been idled before calling this + * routine. + */ +int radeon_cp_stop(struct drm_device *dev, void *data, struct drm_file *file_priv) +{ + drm_radeon_private_t *dev_priv = dev->dev_private; + drm_radeon_cp_stop_t *stop = data; + int ret; + DRM_DEBUG("\n"); + + LOCK_TEST_WITH_RETURN(dev, file_priv); + + if (!dev_priv->cp_running) + return 0; + + /* Flush any pending CP commands. This ensures any outstanding + * commands are exectuted by the engine before we turn it off. + */ + if (stop->flush) { + radeon_do_cp_flush(dev_priv); + } + + /* If we fail to make the engine go idle, we return an error + * code so that the DRM ioctl wrapper can try again. + */ + if (stop->idle) { + if ((dev_priv->flags & RADEON_FAMILY_MASK) >= CHIP_R600) + ret = r600_do_cp_idle(dev_priv); + else + ret = radeon_do_cp_idle(dev_priv); + if (ret) + return ret; + } + + /* Finally, we can turn off the CP. If the engine isn't idle, + * we will get some dropped triangles as they won't be fully + * rendered before the CP is shut down. + */ + if ((dev_priv->flags & RADEON_FAMILY_MASK) >= CHIP_R600) + r600_do_cp_stop(dev_priv); + else + radeon_do_cp_stop(dev_priv); + + /* Reset the engine */ + if ((dev_priv->flags & RADEON_FAMILY_MASK) >= CHIP_R600) + r600_do_engine_reset(dev); + else + radeon_do_engine_reset(dev); + + return 0; +} + +void radeon_do_release(struct drm_device * dev) +{ + drm_radeon_private_t *dev_priv = dev->dev_private; + int i, ret; + + if (dev_priv) { + if (dev_priv->cp_running) { + /* Stop the cp */ + if ((dev_priv->flags & RADEON_FAMILY_MASK) >= CHIP_R600) { + while ((ret = r600_do_cp_idle(dev_priv)) != 0) { + DRM_DEBUG("radeon_do_cp_idle %d\n", ret); +#ifdef __linux__ + schedule(); +#else + tsleep(&ret, PZERO, "rdnrel", 1); +#endif + } + } else { + while ((ret = radeon_do_cp_idle(dev_priv)) != 0) { + DRM_DEBUG("radeon_do_cp_idle %d\n", ret); +#ifdef __linux__ + schedule(); +#else + tsleep(&ret, PZERO, "rdnrel", 1); +#endif + } + } + if ((dev_priv->flags & RADEON_FAMILY_MASK) >= CHIP_R600) { + r600_do_cp_stop(dev_priv); + r600_do_engine_reset(dev); + } else { + radeon_do_cp_stop(dev_priv); + radeon_do_engine_reset(dev); + } + } + + if ((dev_priv->flags & RADEON_FAMILY_MASK) < CHIP_R600) { + /* Disable *all* interrupts */ + if (dev_priv->mmio) /* remove this after permanent addmaps */ + RADEON_WRITE(RADEON_GEN_INT_CNTL, 0); + + if (dev_priv->mmio) { /* remove all surfaces */ + for (i = 0; i < RADEON_MAX_SURFACES; i++) { + RADEON_WRITE(RADEON_SURFACE0_INFO + 16 * i, 0); + RADEON_WRITE(RADEON_SURFACE0_LOWER_BOUND + + 16 * i, 0); + RADEON_WRITE(RADEON_SURFACE0_UPPER_BOUND + + 16 * i, 0); + } + } + } + + /* Free memory heap structures */ + radeon_mem_takedown(&(dev_priv->gart_heap)); + radeon_mem_takedown(&(dev_priv->fb_heap)); + + /* deallocate kernel resources */ + if ((dev_priv->flags & RADEON_FAMILY_MASK) >= CHIP_R600) + r600_do_cleanup_cp(dev); + else + radeon_do_cleanup_cp(dev); + if (dev_priv->me_fw != NULL) { + firmware_put(dev_priv->me_fw, FIRMWARE_UNLOAD); + dev_priv->me_fw = NULL; + } + if (dev_priv->pfp_fw != NULL) { + firmware_put(dev_priv->pfp_fw, FIRMWARE_UNLOAD); + dev_priv->pfp_fw = NULL; + } + } +} + +/* Just reset the CP ring. Called as part of an X Server engine reset. + */ +int radeon_cp_reset(struct drm_device *dev, void *data, struct drm_file *file_priv) +{ + drm_radeon_private_t *dev_priv = dev->dev_private; + DRM_DEBUG("\n"); + + LOCK_TEST_WITH_RETURN(dev, file_priv); + + if (!dev_priv) { + DRM_DEBUG("called before init done\n"); + return -EINVAL; + } + + if ((dev_priv->flags & RADEON_FAMILY_MASK) >= CHIP_R600) + r600_do_cp_reset(dev_priv); + else + radeon_do_cp_reset(dev_priv); + + /* The CP is no longer running after an engine reset */ + dev_priv->cp_running = 0; + + return 0; +} + +int radeon_cp_idle(struct drm_device *dev, void *data, struct drm_file *file_priv) +{ + drm_radeon_private_t *dev_priv = dev->dev_private; + DRM_DEBUG("\n"); + + LOCK_TEST_WITH_RETURN(dev, file_priv); + + if ((dev_priv->flags & RADEON_FAMILY_MASK) >= CHIP_R600) + return r600_do_cp_idle(dev_priv); + else + return radeon_do_cp_idle(dev_priv); +} + +/* Added by Charl P. Botha to call radeon_do_resume_cp(). + */ +int radeon_cp_resume(struct drm_device *dev, void *data, struct drm_file *file_priv) +{ + drm_radeon_private_t *dev_priv = dev->dev_private; + DRM_DEBUG("\n"); + + if ((dev_priv->flags & RADEON_FAMILY_MASK) >= CHIP_R600) + return r600_do_resume_cp(dev, file_priv); + else + return radeon_do_resume_cp(dev, file_priv); +} + +int radeon_engine_reset(struct drm_device *dev, void *data, struct drm_file *file_priv) +{ + drm_radeon_private_t *dev_priv = dev->dev_private; + DRM_DEBUG("\n"); + + LOCK_TEST_WITH_RETURN(dev, file_priv); + + if ((dev_priv->flags & RADEON_FAMILY_MASK) >= CHIP_R600) + return r600_do_engine_reset(dev); + else + return radeon_do_engine_reset(dev); +} + +/* ================================================================ + * Fullscreen mode + */ + +/* KW: Deprecated to say the least: + */ +int radeon_fullscreen(struct drm_device *dev, void *data, struct drm_file *file_priv) +{ + return 0; +} + +/* ================================================================ + * Freelist management + */ + +/* Original comment: FIXME: ROTATE_BUFS is a hack to cycle through + * bufs until freelist code is used. Note this hides a problem with + * the scratch register * (used to keep track of last buffer + * completed) being written to before * the last buffer has actually + * completed rendering. + * + * KW: It's also a good way to find free buffers quickly. + * + * KW: Ideally this loop wouldn't exist, and freelist_get wouldn't + * sleep. However, bugs in older versions of radeon_accel.c mean that + * we essentially have to do this, else old clients will break. + * + * However, it does leave open a potential deadlock where all the + * buffers are held by other clients, which can't release them because + * they can't get the lock. + */ + +struct drm_buf *radeon_freelist_get(struct drm_device * dev) +{ + struct drm_device_dma *dma = dev->dma; + drm_radeon_private_t *dev_priv = dev->dev_private; + drm_radeon_buf_priv_t *buf_priv; + struct drm_buf *buf; + int i, t; + int start; + + if (++dev_priv->last_buf >= dma->buf_count) + dev_priv->last_buf = 0; + + start = dev_priv->last_buf; + + for (t = 0; t < dev_priv->usec_timeout; t++) { + u32 done_age = GET_SCRATCH(dev_priv, 1); + DRM_DEBUG("done_age = %d\n", done_age); + for (i = 0; i < dma->buf_count; i++) { + buf = dma->buflist[start]; + buf_priv = buf->dev_private; + if (buf->file_priv == NULL || (buf->pending && + buf_priv->age <= + done_age)) { + dev_priv->stats.requested_bufs++; + buf->pending = 0; + return buf; + } + if (++start >= dma->buf_count) + start = 0; + } + + if (t) { + DRM_UDELAY(1); + dev_priv->stats.freelist_loops++; + } + } + + return NULL; +} + +void radeon_freelist_reset(struct drm_device * dev) +{ + struct drm_device_dma *dma = dev->dma; + drm_radeon_private_t *dev_priv = dev->dev_private; + int i; + + dev_priv->last_buf = 0; + for (i = 0; i < dma->buf_count; i++) { + struct drm_buf *buf = dma->buflist[i]; + drm_radeon_buf_priv_t *buf_priv = buf->dev_private; + buf_priv->age = 0; + } +} + +/* ================================================================ + * CP command submission + */ + +int radeon_wait_ring(drm_radeon_private_t * dev_priv, int n) +{ + drm_radeon_ring_buffer_t *ring = &dev_priv->ring; + int i; + u32 last_head = GET_RING_HEAD(dev_priv); + + for (i = 0; i < dev_priv->usec_timeout; i++) { + u32 head = GET_RING_HEAD(dev_priv); + + ring->space = (head - ring->tail) * sizeof(u32); + if (ring->space <= 0) + ring->space += ring->size; + if (ring->space > n) + return 0; + + dev_priv->stats.boxes |= RADEON_BOX_WAIT_IDLE; + + if (head != last_head) + i = 0; + last_head = head; + + DRM_UDELAY(1); + } + + /* FIXME: This return value is ignored in the BEGIN_RING macro! */ +#if RADEON_FIFO_DEBUG + radeon_status(dev_priv); + DRM_ERROR("failed!\n"); +#endif + return -EBUSY; +} + +static int radeon_cp_get_buffers(struct drm_device *dev, + struct drm_file *file_priv, + struct drm_dma * d) +{ + int i; + struct drm_buf *buf; + + for (i = d->granted_count; i < d->request_count; i++) { + buf = radeon_freelist_get(dev); + if (!buf) + return -EBUSY; /* NOTE: broken client */ + + buf->file_priv = file_priv; + + if (DRM_COPY_TO_USER(&d->request_indices[i], &buf->idx, + sizeof(buf->idx))) + return -EFAULT; + if (DRM_COPY_TO_USER(&d->request_sizes[i], &buf->total, + sizeof(buf->total))) + return -EFAULT; + + d->granted_count++; + } + return 0; +} + +int radeon_cp_buffers(struct drm_device *dev, void *data, struct drm_file *file_priv) +{ + struct drm_device_dma *dma = dev->dma; + int ret = 0; + struct drm_dma *d = data; + + LOCK_TEST_WITH_RETURN(dev, file_priv); + + /* Please don't send us buffers. + */ + if (d->send_count != 0) { + DRM_ERROR("Process %d trying to send %d buffers via drmDMA\n", + DRM_CURRENTPID, d->send_count); + return -EINVAL; + } + + /* We'll send you buffers. + */ + if (d->request_count < 0 || d->request_count > dma->buf_count) { + DRM_ERROR("Process %d trying to get %d buffers (of %d max)\n", + DRM_CURRENTPID, d->request_count, dma->buf_count); + return -EINVAL; + } + + d->granted_count = 0; + + if (d->request_count) { + ret = radeon_cp_get_buffers(dev, file_priv, d); + } + + return ret; +} + +int radeon_driver_load(struct drm_device *dev, unsigned long flags) +{ + drm_radeon_private_t *dev_priv; + int ret = 0; + + dev_priv = malloc(sizeof(drm_radeon_private_t), + DRM_MEM_DRIVER, M_ZERO | M_WAITOK); + if (dev_priv == NULL) + return -ENOMEM; + + dev->dev_private = (void *)dev_priv; + dev_priv->flags = flags; + + switch (flags & RADEON_FAMILY_MASK) { + case CHIP_R100: + case CHIP_RV200: + case CHIP_R200: + case CHIP_R300: + case CHIP_R350: + case CHIP_R420: + case CHIP_R423: + case CHIP_RV410: + case CHIP_RV515: + case CHIP_R520: + case CHIP_RV570: + case CHIP_R580: + dev_priv->flags |= RADEON_HAS_HIERZ; + break; + default: + /* all other chips have no hierarchical z buffer */ + break; + } + + pci_enable_busmaster(dev->device); + + if (drm_device_is_agp(dev)) + dev_priv->flags |= RADEON_IS_AGP; + else if (drm_device_is_pcie(dev)) + dev_priv->flags |= RADEON_IS_PCIE; + else + dev_priv->flags |= RADEON_IS_PCI; + + ret = drm_addmap(dev, drm_get_resource_start(dev, 2), + drm_get_resource_len(dev, 2), _DRM_REGISTERS, + _DRM_READ_ONLY | _DRM_DRIVER, &dev_priv->mmio); + if (ret != 0) + return ret; + + ret = drm_vblank_init(dev, 2); + if (ret) { + radeon_driver_unload(dev); + return ret; + } + + DRM_DEBUG("%s card detected\n", + ((dev_priv->flags & RADEON_IS_AGP) ? "AGP" : (((dev_priv->flags & RADEON_IS_PCIE) ? "PCIE" : "PCI")))); + return ret; +} + +int radeon_master_create(struct drm_device *dev, struct drm_master *master) +{ + struct drm_radeon_master_private *master_priv; + unsigned long sareapage; + int ret; + + master_priv = malloc(sizeof(*master_priv), + DRM_MEM_DRIVER, M_ZERO | M_WAITOK); + if (!master_priv) + return -ENOMEM; + + /* prebuild the SAREA */ + sareapage = max_t(unsigned long, SAREA_MAX, PAGE_SIZE); + ret = drm_addmap(dev, 0, sareapage, _DRM_SHM, _DRM_CONTAINS_LOCK, + &master_priv->sarea); + if (ret) { + DRM_ERROR("SAREA setup failed\n"); + free(master_priv, DRM_MEM_DRIVER); + return ret; + } + master_priv->sarea_priv = (drm_radeon_sarea_t *)((char *)master_priv->sarea->handle) + + sizeof(struct drm_sarea); + master_priv->sarea_priv->pfCurrentPage = 0; + + master->driver_priv = master_priv; + return 0; +} + +void radeon_master_destroy(struct drm_device *dev, struct drm_master *master) +{ + struct drm_radeon_master_private *master_priv = master->driver_priv; + + if (!master_priv) + return; + + if (master_priv->sarea_priv && + master_priv->sarea_priv->pfCurrentPage != 0) + radeon_cp_dispatch_flip(dev, master); + + master_priv->sarea_priv = NULL; + if (master_priv->sarea) +#ifdef __linux__ + drm_rmmap_locked(dev, master_priv->sarea); +#else + drm_rmmap(dev, master_priv->sarea); +#endif + + free(master_priv, DRM_MEM_DRIVER); + + master->driver_priv = NULL; +} + +/* Create mappings for registers and framebuffer so userland doesn't necessarily + * have to find them. + */ +int radeon_driver_firstopen(struct drm_device *dev) +{ + int ret; + drm_local_map_t *map; + drm_radeon_private_t *dev_priv = dev->dev_private; + + dev_priv->gart_info.table_size = RADEON_PCIGART_TABLE_SIZE; + + dev_priv->fb_aper_offset = drm_get_resource_start(dev, 0); + ret = drm_addmap(dev, dev_priv->fb_aper_offset, + drm_get_resource_len(dev, 0), _DRM_FRAME_BUFFER, + _DRM_WRITE_COMBINING, &map); + if (ret != 0) + return ret; + + return 0; +} + +int radeon_driver_unload(struct drm_device *dev) +{ + drm_radeon_private_t *dev_priv = dev->dev_private; + + DRM_DEBUG("\n"); + + drm_rmmap(dev, dev_priv->mmio); + + free(dev_priv, DRM_MEM_DRIVER); + + dev->dev_private = NULL; + return 0; +} + +void radeon_commit_ring(drm_radeon_private_t *dev_priv) +{ + int i; + u32 *ring; + int tail_aligned; + + /* check if the ring is padded out to 16-dword alignment */ + + tail_aligned = dev_priv->ring.tail & (RADEON_RING_ALIGN-1); + if (tail_aligned) { + int num_p2 = RADEON_RING_ALIGN - tail_aligned; + + ring = dev_priv->ring.start; + /* pad with some CP_PACKET2 */ + for (i = 0; i < num_p2; i++) + ring[dev_priv->ring.tail + i] = CP_PACKET2(); + + dev_priv->ring.tail += i; + + dev_priv->ring.space -= num_p2 * sizeof(u32); + } + + dev_priv->ring.tail &= dev_priv->ring.tail_mask; + + DRM_MEMORYBARRIER(); + GET_RING_HEAD( dev_priv ); + + if ((dev_priv->flags & RADEON_FAMILY_MASK) >= CHIP_R600) { + RADEON_WRITE(R600_CP_RB_WPTR, dev_priv->ring.tail); + /* read from PCI bus to ensure correct posting */ + RADEON_READ(R600_CP_RB_RPTR); + } else { + RADEON_WRITE(RADEON_CP_RB_WPTR, dev_priv->ring.tail); + /* read from PCI bus to ensure correct posting */ + RADEON_READ(RADEON_CP_RB_RPTR); + } +} diff --git a/sys/dev/drm2/radeon/radeon_cs.c b/sys/dev/drm2/radeon/radeon_cs.c new file mode 100644 index 00000000000..ee299fa7206 --- /dev/null +++ b/sys/dev/drm2/radeon/radeon_cs.c @@ -0,0 +1,658 @@ +/* + * Copyright 2008 Jerome Glisse. + * All Rights Reserved. + * + * 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 (including the next + * paragraph) 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 + * PRECISION INSIGHT AND/OR ITS SUPPLIERS 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. + * + * Authors: + * Jerome Glisse + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include "radeon_reg.h" +#include "radeon.h" + +void r100_cs_dump_packet(struct radeon_cs_parser *p, + struct radeon_cs_packet *pkt); + +static int radeon_cs_parser_relocs(struct radeon_cs_parser *p) +{ + struct drm_device *ddev = p->rdev->ddev; + struct radeon_cs_chunk *chunk; + unsigned i, j; + bool duplicate; + + if (p->chunk_relocs_idx == -1) { + return 0; + } + chunk = &p->chunks[p->chunk_relocs_idx]; + p->dma_reloc_idx = 0; + /* FIXME: we assume that each relocs use 4 dwords */ + p->nrelocs = chunk->length_dw / 4; + p->relocs_ptr = malloc(p->nrelocs * sizeof(void *), + DRM_MEM_DRIVER, M_ZERO | M_WAITOK); + if (p->relocs_ptr == NULL) { + return -ENOMEM; + } + p->relocs = malloc(p->nrelocs * sizeof(struct radeon_cs_reloc), + DRM_MEM_DRIVER, M_ZERO | M_WAITOK); + if (p->relocs == NULL) { + return -ENOMEM; + } + for (i = 0; i < p->nrelocs; i++) { + struct drm_radeon_cs_reloc *r; + + duplicate = false; + r = (struct drm_radeon_cs_reloc *)&chunk->kdata[i*4]; + for (j = 0; j < i; j++) { + if (r->handle == p->relocs[j].handle) { + p->relocs_ptr[i] = &p->relocs[j]; + duplicate = true; + break; + } + } + if (!duplicate) { + p->relocs[i].gobj = drm_gem_object_lookup(ddev, + p->filp, + r->handle); + if (p->relocs[i].gobj == NULL) { + DRM_ERROR("gem object lookup failed 0x%x\n", + r->handle); + return -ENOENT; + } + p->relocs_ptr[i] = &p->relocs[i]; + p->relocs[i].robj = gem_to_radeon_bo(p->relocs[i].gobj); + p->relocs[i].lobj.bo = p->relocs[i].robj; + p->relocs[i].lobj.wdomain = r->write_domain; + p->relocs[i].lobj.rdomain = r->read_domains; + p->relocs[i].lobj.tv.bo = &p->relocs[i].robj->tbo; + p->relocs[i].handle = r->handle; + p->relocs[i].flags = r->flags; + radeon_bo_list_add_object(&p->relocs[i].lobj, + &p->validated); + + } else + p->relocs[i].handle = 0; + } + return radeon_bo_list_validate(&p->validated); +} + +static int radeon_cs_get_ring(struct radeon_cs_parser *p, u32 ring, s32 priority) +{ + p->priority = priority; + + switch (ring) { + default: + DRM_ERROR("unknown ring id: %d\n", ring); + return -EINVAL; + case RADEON_CS_RING_GFX: + p->ring = RADEON_RING_TYPE_GFX_INDEX; + break; + case RADEON_CS_RING_COMPUTE: + if (p->rdev->family >= CHIP_TAHITI) { + if (p->priority > 0) + p->ring = CAYMAN_RING_TYPE_CP1_INDEX; + else + p->ring = CAYMAN_RING_TYPE_CP2_INDEX; + } else + p->ring = RADEON_RING_TYPE_GFX_INDEX; + break; + case RADEON_CS_RING_DMA: + if (p->rdev->family >= CHIP_CAYMAN) { + if (p->priority > 0) + p->ring = R600_RING_TYPE_DMA_INDEX; + else + p->ring = CAYMAN_RING_TYPE_DMA1_INDEX; + } else if (p->rdev->family >= CHIP_R600) { + p->ring = R600_RING_TYPE_DMA_INDEX; + } else { + return -EINVAL; + } + break; + } + return 0; +} + +static void radeon_cs_sync_to(struct radeon_cs_parser *p, + struct radeon_fence *fence) +{ + struct radeon_fence *other; + + if (!fence) + return; + + other = p->ib.sync_to[fence->ring]; + p->ib.sync_to[fence->ring] = radeon_fence_later(fence, other); +} + +static void radeon_cs_sync_rings(struct radeon_cs_parser *p) +{ + int i; + + for (i = 0; i < p->nrelocs; i++) { + if (!p->relocs[i].robj) + continue; + + radeon_cs_sync_to(p, p->relocs[i].robj->tbo.sync_obj); + } +} + +/* XXX: note that this is called from the legacy UMS CS ioctl as well */ +int radeon_cs_parser_init(struct radeon_cs_parser *p, void *data) +{ + struct drm_radeon_cs *cs = data; + uint64_t *chunk_array_ptr; + unsigned size, i; + u32 ring = RADEON_CS_RING_GFX; + s32 priority = 0; + + if (!cs->num_chunks) { + return 0; + } + /* get chunks */ + INIT_LIST_HEAD(&p->validated); + p->idx = 0; + p->ib.sa_bo = NULL; + p->ib.semaphore = NULL; + p->const_ib.sa_bo = NULL; + p->const_ib.semaphore = NULL; + p->chunk_ib_idx = -1; + p->chunk_relocs_idx = -1; + p->chunk_flags_idx = -1; + p->chunk_const_ib_idx = -1; + p->chunks_array = malloc(cs->num_chunks * sizeof(uint64_t), + DRM_MEM_DRIVER, M_ZERO | M_WAITOK); + if (p->chunks_array == NULL) { + return -ENOMEM; + } + chunk_array_ptr = (uint64_t *)(unsigned long)(cs->chunks); + if (DRM_COPY_FROM_USER(p->chunks_array, chunk_array_ptr, + sizeof(uint64_t)*cs->num_chunks)) { + return -EFAULT; + } + p->cs_flags = 0; + p->nchunks = cs->num_chunks; + p->chunks = malloc(p->nchunks * sizeof(struct radeon_cs_chunk), + DRM_MEM_DRIVER, M_ZERO | M_WAITOK); + if (p->chunks == NULL) { + return -ENOMEM; + } + for (i = 0; i < p->nchunks; i++) { + struct drm_radeon_cs_chunk __user **chunk_ptr = NULL; + struct drm_radeon_cs_chunk user_chunk; + uint32_t __user *cdata; + + chunk_ptr = (void __user*)(unsigned long)p->chunks_array[i]; + if (DRM_COPY_FROM_USER(&user_chunk, chunk_ptr, + sizeof(struct drm_radeon_cs_chunk))) { + return -EFAULT; + } + p->chunks[i].length_dw = user_chunk.length_dw; + p->chunks[i].kdata = NULL; + p->chunks[i].chunk_id = user_chunk.chunk_id; + + if (p->chunks[i].chunk_id == RADEON_CHUNK_ID_RELOCS) { + p->chunk_relocs_idx = i; + } + if (p->chunks[i].chunk_id == RADEON_CHUNK_ID_IB) { + p->chunk_ib_idx = i; + /* zero length IB isn't useful */ + if (p->chunks[i].length_dw == 0) + return -EINVAL; + } + if (p->chunks[i].chunk_id == RADEON_CHUNK_ID_CONST_IB) { + p->chunk_const_ib_idx = i; + /* zero length CONST IB isn't useful */ + if (p->chunks[i].length_dw == 0) + return -EINVAL; + } + if (p->chunks[i].chunk_id == RADEON_CHUNK_ID_FLAGS) { + p->chunk_flags_idx = i; + /* zero length flags aren't useful */ + if (p->chunks[i].length_dw == 0) + return -EINVAL; + } + + p->chunks[i].length_dw = user_chunk.length_dw; + p->chunks[i].user_ptr = (void __user *)(unsigned long)user_chunk.chunk_data; + + cdata = (uint32_t *)(unsigned long)user_chunk.chunk_data; + if ((p->chunks[i].chunk_id == RADEON_CHUNK_ID_RELOCS) || + (p->chunks[i].chunk_id == RADEON_CHUNK_ID_FLAGS)) { + size = p->chunks[i].length_dw * sizeof(uint32_t); + p->chunks[i].kdata = malloc(size, DRM_MEM_DRIVER, M_WAITOK); + if (p->chunks[i].kdata == NULL) { + return -ENOMEM; + } + if (DRM_COPY_FROM_USER(p->chunks[i].kdata, + p->chunks[i].user_ptr, size)) { + return -EFAULT; + } + if (p->chunks[i].chunk_id == RADEON_CHUNK_ID_FLAGS) { + p->cs_flags = p->chunks[i].kdata[0]; + if (p->chunks[i].length_dw > 1) + ring = p->chunks[i].kdata[1]; + if (p->chunks[i].length_dw > 2) + priority = (s32)p->chunks[i].kdata[2]; + } + } + } + + /* these are KMS only */ + if (p->rdev) { + if ((p->cs_flags & RADEON_CS_USE_VM) && + !p->rdev->vm_manager.enabled) { + DRM_ERROR("VM not active on asic!\n"); + return -EINVAL; + } + + /* we only support VM on SI+ */ + if ((p->rdev->family >= CHIP_TAHITI) && + ((p->cs_flags & RADEON_CS_USE_VM) == 0)) { + DRM_ERROR("VM required on SI+!\n"); + return -EINVAL; + } + + if (radeon_cs_get_ring(p, ring, priority)) + return -EINVAL; + } + + /* deal with non-vm */ + if ((p->chunk_ib_idx != -1) && + ((p->cs_flags & RADEON_CS_USE_VM) == 0) && + (p->chunks[p->chunk_ib_idx].chunk_id == RADEON_CHUNK_ID_IB)) { + if (p->chunks[p->chunk_ib_idx].length_dw > (16 * 1024)) { + DRM_ERROR("cs IB too big: %d\n", + p->chunks[p->chunk_ib_idx].length_dw); + return -EINVAL; + } + if (p->rdev && (p->rdev->flags & RADEON_IS_AGP)) { + p->chunks[p->chunk_ib_idx].kpage[0] = malloc(PAGE_SIZE, DRM_MEM_DRIVER, M_WAITOK); + p->chunks[p->chunk_ib_idx].kpage[1] = malloc(PAGE_SIZE, DRM_MEM_DRIVER, M_WAITOK); + if (p->chunks[p->chunk_ib_idx].kpage[0] == NULL || + p->chunks[p->chunk_ib_idx].kpage[1] == NULL) { + free(p->chunks[p->chunk_ib_idx].kpage[0], DRM_MEM_DRIVER); + free(p->chunks[p->chunk_ib_idx].kpage[1], DRM_MEM_DRIVER); + p->chunks[p->chunk_ib_idx].kpage[0] = NULL; + p->chunks[p->chunk_ib_idx].kpage[1] = NULL; + return -ENOMEM; + } + } + p->chunks[p->chunk_ib_idx].kpage_idx[0] = -1; + p->chunks[p->chunk_ib_idx].kpage_idx[1] = -1; + p->chunks[p->chunk_ib_idx].last_copied_page = -1; + p->chunks[p->chunk_ib_idx].last_page_index = + ((p->chunks[p->chunk_ib_idx].length_dw * 4) - 1) / PAGE_SIZE; + } + + return 0; +} + +/** + * cs_parser_fini() - clean parser states + * @parser: parser structure holding parsing context. + * @error: error number + * + * If error is set than unvalidate buffer, otherwise just free memory + * used by parsing context. + **/ +static void radeon_cs_parser_fini(struct radeon_cs_parser *parser, int error) +{ + unsigned i; + + if (!error) { + ttm_eu_fence_buffer_objects(&parser->validated, + parser->ib.fence); + } else { + ttm_eu_backoff_reservation(&parser->validated); + } + + if (parser->relocs != NULL) { + for (i = 0; i < parser->nrelocs; i++) { + if (parser->relocs[i].gobj) + drm_gem_object_unreference_unlocked(parser->relocs[i].gobj); + } + } + free(parser->track, DRM_MEM_DRIVER); + free(parser->relocs, DRM_MEM_DRIVER); + free(parser->relocs_ptr, DRM_MEM_DRIVER); + for (i = 0; i < parser->nchunks; i++) { + free(parser->chunks[i].kdata, DRM_MEM_DRIVER); + if ((parser->rdev->flags & RADEON_IS_AGP)) { + free(parser->chunks[i].kpage[0], DRM_MEM_DRIVER); + free(parser->chunks[i].kpage[1], DRM_MEM_DRIVER); + } + } + free(parser->chunks, DRM_MEM_DRIVER); + free(parser->chunks_array, DRM_MEM_DRIVER); + radeon_ib_free(parser->rdev, &parser->ib); + radeon_ib_free(parser->rdev, &parser->const_ib); +} + +static int radeon_cs_ib_chunk(struct radeon_device *rdev, + struct radeon_cs_parser *parser) +{ + struct radeon_cs_chunk *ib_chunk; + int r; + + if (parser->chunk_ib_idx == -1) + return 0; + + if (parser->cs_flags & RADEON_CS_USE_VM) + return 0; + + ib_chunk = &parser->chunks[parser->chunk_ib_idx]; + /* Copy the packet into the IB, the parser will read from the + * input memory (cached) and write to the IB (which can be + * uncached). + */ + r = radeon_ib_get(rdev, parser->ring, &parser->ib, + NULL, ib_chunk->length_dw * 4); + if (r) { + DRM_ERROR("Failed to get ib !\n"); + return r; + } + parser->ib.length_dw = ib_chunk->length_dw; + r = radeon_cs_parse(rdev, parser->ring, parser); + if (r || parser->parser_error) { + DRM_ERROR("Invalid command stream !\n"); + return r; + } + r = radeon_cs_finish_pages(parser); + if (r) { + DRM_ERROR("Invalid command stream !\n"); + return r; + } + radeon_cs_sync_rings(parser); + r = radeon_ib_schedule(rdev, &parser->ib, NULL); + if (r) { + DRM_ERROR("Failed to schedule IB !\n"); + } + return r; +} + +static int radeon_bo_vm_update_pte(struct radeon_cs_parser *parser, + struct radeon_vm *vm) +{ + struct radeon_device *rdev = parser->rdev; + struct radeon_bo_list *lobj; + struct radeon_bo *bo; + int r; + + r = radeon_vm_bo_update_pte(rdev, vm, rdev->ring_tmp_bo.bo, &rdev->ring_tmp_bo.bo->tbo.mem); + if (r) { + return r; + } + list_for_each_entry(lobj, &parser->validated, tv.head) { + bo = lobj->bo; + r = radeon_vm_bo_update_pte(parser->rdev, vm, bo, &bo->tbo.mem); + if (r) { + return r; + } + } + return 0; +} + +static int radeon_cs_ib_vm_chunk(struct radeon_device *rdev, + struct radeon_cs_parser *parser) +{ + struct radeon_cs_chunk *ib_chunk; + struct radeon_fpriv *fpriv = parser->filp->driver_priv; + struct radeon_vm *vm = &fpriv->vm; + int r; + + if (parser->chunk_ib_idx == -1) + return 0; + if ((parser->cs_flags & RADEON_CS_USE_VM) == 0) + return 0; + + if ((rdev->family >= CHIP_TAHITI) && + (parser->chunk_const_ib_idx != -1)) { + ib_chunk = &parser->chunks[parser->chunk_const_ib_idx]; + if (ib_chunk->length_dw > RADEON_IB_VM_MAX_SIZE) { + DRM_ERROR("cs IB CONST too big: %d\n", ib_chunk->length_dw); + return -EINVAL; + } + r = radeon_ib_get(rdev, parser->ring, &parser->const_ib, + vm, ib_chunk->length_dw * 4); + if (r) { + DRM_ERROR("Failed to get const ib !\n"); + return r; + } + parser->const_ib.is_const_ib = true; + parser->const_ib.length_dw = ib_chunk->length_dw; + /* Copy the packet into the IB */ + if (DRM_COPY_FROM_USER(parser->const_ib.ptr, ib_chunk->user_ptr, + ib_chunk->length_dw * 4)) { + return -EFAULT; + } + r = radeon_ring_ib_parse(rdev, parser->ring, &parser->const_ib); + if (r) { + return r; + } + } + + ib_chunk = &parser->chunks[parser->chunk_ib_idx]; + if (ib_chunk->length_dw > RADEON_IB_VM_MAX_SIZE) { + DRM_ERROR("cs IB too big: %d\n", ib_chunk->length_dw); + return -EINVAL; + } + r = radeon_ib_get(rdev, parser->ring, &parser->ib, + vm, ib_chunk->length_dw * 4); + if (r) { + DRM_ERROR("Failed to get ib !\n"); + return r; + } + parser->ib.length_dw = ib_chunk->length_dw; + /* Copy the packet into the IB */ + if (DRM_COPY_FROM_USER(parser->ib.ptr, ib_chunk->user_ptr, + ib_chunk->length_dw * 4)) { + return -EFAULT; + } + r = radeon_ring_ib_parse(rdev, parser->ring, &parser->ib); + if (r) { + return r; + } + + sx_xlock(&rdev->vm_manager.lock); + sx_xlock(&vm->mutex); + r = radeon_vm_alloc_pt(rdev, vm); + if (r) { + goto out; + } + r = radeon_bo_vm_update_pte(parser, vm); + if (r) { + goto out; + } + radeon_cs_sync_rings(parser); + radeon_cs_sync_to(parser, vm->fence); + radeon_cs_sync_to(parser, radeon_vm_grab_id(rdev, vm, parser->ring)); + + if ((rdev->family >= CHIP_TAHITI) && + (parser->chunk_const_ib_idx != -1)) { + r = radeon_ib_schedule(rdev, &parser->ib, &parser->const_ib); + } else { + r = radeon_ib_schedule(rdev, &parser->ib, NULL); + } + + if (!r) { + radeon_vm_fence(rdev, vm, parser->ib.fence); + } + +out: + radeon_vm_add_to_lru(rdev, vm); + sx_xunlock(&vm->mutex); + sx_xunlock(&rdev->vm_manager.lock); + return r; +} + +static int radeon_cs_handle_lockup(struct radeon_device *rdev, int r) +{ + if (r == -EDEADLK) { + r = radeon_gpu_reset(rdev); + if (!r) + r = -EAGAIN; + } + return r; +} + +int radeon_cs_ioctl(struct drm_device *dev, void *data, struct drm_file *filp) +{ + struct radeon_device *rdev = dev->dev_private; + struct radeon_cs_parser parser; + int r; + + sx_slock(&rdev->exclusive_lock); + if (!rdev->accel_working) { + sx_sunlock(&rdev->exclusive_lock); + return -EBUSY; + } + /* initialize parser */ + memset(&parser, 0, sizeof(struct radeon_cs_parser)); + parser.filp = filp; + parser.rdev = rdev; + parser.dev = rdev->dev; + parser.family = rdev->family; + r = radeon_cs_parser_init(&parser, data); + if (r) { + DRM_ERROR("Failed to initialize parser !\n"); + radeon_cs_parser_fini(&parser, r); + sx_sunlock(&rdev->exclusive_lock); + r = radeon_cs_handle_lockup(rdev, r); + return r; + } + r = radeon_cs_parser_relocs(&parser); + if (r) { + if (r != -ERESTARTSYS) + DRM_ERROR("Failed to parse relocation %d!\n", r); + radeon_cs_parser_fini(&parser, r); + sx_sunlock(&rdev->exclusive_lock); + r = radeon_cs_handle_lockup(rdev, r); + return r; + } + r = radeon_cs_ib_chunk(rdev, &parser); + if (r) { + goto out; + } + r = radeon_cs_ib_vm_chunk(rdev, &parser); + if (r) { + goto out; + } +out: + radeon_cs_parser_fini(&parser, r); + sx_sunlock(&rdev->exclusive_lock); + r = radeon_cs_handle_lockup(rdev, r); + return r; +} + +int radeon_cs_finish_pages(struct radeon_cs_parser *p) +{ + struct radeon_cs_chunk *ibc = &p->chunks[p->chunk_ib_idx]; + int i; + int size = PAGE_SIZE; + + for (i = ibc->last_copied_page + 1; i <= ibc->last_page_index; i++) { + if (i == ibc->last_page_index) { + size = (ibc->length_dw * 4) % PAGE_SIZE; + if (size == 0) + size = PAGE_SIZE; + } + + if (DRM_COPY_FROM_USER(p->ib.ptr + (i * (PAGE_SIZE/4)), + (char *)ibc->user_ptr + (i * PAGE_SIZE), + size)) + return -EFAULT; + } + return 0; +} + +static int radeon_cs_update_pages(struct radeon_cs_parser *p, int pg_idx) +{ + int new_page; + struct radeon_cs_chunk *ibc = &p->chunks[p->chunk_ib_idx]; + int i; + int size = PAGE_SIZE; + bool copy1 = (p->rdev && (p->rdev->flags & RADEON_IS_AGP)) ? + false : true; + + for (i = ibc->last_copied_page + 1; i < pg_idx; i++) { + if (DRM_COPY_FROM_USER(p->ib.ptr + (i * (PAGE_SIZE/4)), + (char *)ibc->user_ptr + (i * PAGE_SIZE), + PAGE_SIZE)) { + p->parser_error = -EFAULT; + return 0; + } + } + + if (pg_idx == ibc->last_page_index) { + size = (ibc->length_dw * 4) % PAGE_SIZE; + if (size == 0) + size = PAGE_SIZE; + } + + new_page = ibc->kpage_idx[0] < ibc->kpage_idx[1] ? 0 : 1; + if (copy1) + ibc->kpage[new_page] = p->ib.ptr + (pg_idx * (PAGE_SIZE / 4)); + + if (DRM_COPY_FROM_USER(ibc->kpage[new_page], + (char *)ibc->user_ptr + (pg_idx * PAGE_SIZE), + size)) { + p->parser_error = -EFAULT; + return 0; + } + + /* copy to IB for non single case */ + if (!copy1) + memcpy((void *)(p->ib.ptr+(pg_idx*(PAGE_SIZE/4))), ibc->kpage[new_page], size); + + ibc->last_copied_page = pg_idx; + ibc->kpage_idx[new_page] = pg_idx; + + return new_page; +} + +u32 radeon_get_ib_value(struct radeon_cs_parser *p, int idx) +{ + struct radeon_cs_chunk *ibc = &p->chunks[p->chunk_ib_idx]; + u32 pg_idx, pg_offset; + u32 idx_value = 0; + int new_page; + + pg_idx = (idx * 4) / PAGE_SIZE; + pg_offset = (idx * 4) % PAGE_SIZE; + + if (ibc->kpage_idx[0] == pg_idx) + return ibc->kpage[0][pg_offset/4]; + if (ibc->kpage_idx[1] == pg_idx) + return ibc->kpage[1][pg_offset/4]; + + new_page = radeon_cs_update_pages(p, pg_idx); + if (new_page < 0) { + p->parser_error = new_page; + return 0; + } + + idx_value = ibc->kpage[new_page][pg_offset/4]; + return idx_value; +} diff --git a/sys/dev/drm2/radeon/radeon_cursor.c b/sys/dev/drm2/radeon/radeon_cursor.c new file mode 100644 index 00000000000..0b4e73e1dac --- /dev/null +++ b/sys/dev/drm2/radeon/radeon_cursor.c @@ -0,0 +1,316 @@ +/* + * Copyright 2007-8 Advanced Micro Devices, Inc. + * Copyright 2008 Red Hat Inc. + * + * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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. + * + * Authors: Dave Airlie + * Alex Deucher + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include "radeon.h" + +#define CURSOR_WIDTH 64 +#define CURSOR_HEIGHT 64 + +static void radeon_lock_cursor(struct drm_crtc *crtc, bool lock) +{ + struct radeon_device *rdev = crtc->dev->dev_private; + struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); + uint32_t cur_lock; + + if (ASIC_IS_DCE4(rdev)) { + cur_lock = RREG32(EVERGREEN_CUR_UPDATE + radeon_crtc->crtc_offset); + if (lock) + cur_lock |= EVERGREEN_CURSOR_UPDATE_LOCK; + else + cur_lock &= ~EVERGREEN_CURSOR_UPDATE_LOCK; + WREG32(EVERGREEN_CUR_UPDATE + radeon_crtc->crtc_offset, cur_lock); + } else if (ASIC_IS_AVIVO(rdev)) { + cur_lock = RREG32(AVIVO_D1CUR_UPDATE + radeon_crtc->crtc_offset); + if (lock) + cur_lock |= AVIVO_D1CURSOR_UPDATE_LOCK; + else + cur_lock &= ~AVIVO_D1CURSOR_UPDATE_LOCK; + WREG32(AVIVO_D1CUR_UPDATE + radeon_crtc->crtc_offset, cur_lock); + } else { + cur_lock = RREG32(RADEON_CUR_OFFSET + radeon_crtc->crtc_offset); + if (lock) + cur_lock |= RADEON_CUR_LOCK; + else + cur_lock &= ~RADEON_CUR_LOCK; + WREG32(RADEON_CUR_OFFSET + radeon_crtc->crtc_offset, cur_lock); + } +} + +static void radeon_hide_cursor(struct drm_crtc *crtc) +{ + struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); + struct radeon_device *rdev = crtc->dev->dev_private; + + if (ASIC_IS_DCE4(rdev)) { + WREG32_IDX(EVERGREEN_CUR_CONTROL + radeon_crtc->crtc_offset, + EVERGREEN_CURSOR_MODE(EVERGREEN_CURSOR_24_8_PRE_MULT) | + EVERGREEN_CURSOR_URGENT_CONTROL(EVERGREEN_CURSOR_URGENT_1_2)); + } else if (ASIC_IS_AVIVO(rdev)) { + WREG32_IDX(AVIVO_D1CUR_CONTROL + radeon_crtc->crtc_offset, + (AVIVO_D1CURSOR_MODE_24BPP << AVIVO_D1CURSOR_MODE_SHIFT)); + } else { + u32 reg; + switch (radeon_crtc->crtc_id) { + case 0: + reg = RADEON_CRTC_GEN_CNTL; + break; + case 1: + reg = RADEON_CRTC2_GEN_CNTL; + break; + default: + return; + } + WREG32_IDX(reg, RREG32_IDX(reg) & ~RADEON_CRTC_CUR_EN); + } +} + +static void radeon_show_cursor(struct drm_crtc *crtc) +{ + struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); + struct radeon_device *rdev = crtc->dev->dev_private; + + if (ASIC_IS_DCE4(rdev)) { + WREG32(RADEON_MM_INDEX, EVERGREEN_CUR_CONTROL + radeon_crtc->crtc_offset); + WREG32(RADEON_MM_DATA, EVERGREEN_CURSOR_EN | + EVERGREEN_CURSOR_MODE(EVERGREEN_CURSOR_24_8_PRE_MULT) | + EVERGREEN_CURSOR_URGENT_CONTROL(EVERGREEN_CURSOR_URGENT_1_2)); + } else if (ASIC_IS_AVIVO(rdev)) { + WREG32(RADEON_MM_INDEX, AVIVO_D1CUR_CONTROL + radeon_crtc->crtc_offset); + WREG32(RADEON_MM_DATA, AVIVO_D1CURSOR_EN | + (AVIVO_D1CURSOR_MODE_24BPP << AVIVO_D1CURSOR_MODE_SHIFT)); + } else { + switch (radeon_crtc->crtc_id) { + case 0: + WREG32(RADEON_MM_INDEX, RADEON_CRTC_GEN_CNTL); + break; + case 1: + WREG32(RADEON_MM_INDEX, RADEON_CRTC2_GEN_CNTL); + break; + default: + return; + } + + WREG32_P(RADEON_MM_DATA, (RADEON_CRTC_CUR_EN | + (RADEON_CRTC_CUR_MODE_24BPP << RADEON_CRTC_CUR_MODE_SHIFT)), + ~(RADEON_CRTC_CUR_EN | RADEON_CRTC_CUR_MODE_MASK)); + } +} + +static void radeon_set_cursor(struct drm_crtc *crtc, struct drm_gem_object *obj, + uint64_t gpu_addr) +{ + struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); + struct radeon_device *rdev = crtc->dev->dev_private; + + if (ASIC_IS_DCE4(rdev)) { + WREG32(EVERGREEN_CUR_SURFACE_ADDRESS_HIGH + radeon_crtc->crtc_offset, + upper_32_bits(gpu_addr)); + WREG32(EVERGREEN_CUR_SURFACE_ADDRESS + radeon_crtc->crtc_offset, + gpu_addr & 0xffffffff); + } else if (ASIC_IS_AVIVO(rdev)) { + if (rdev->family >= CHIP_RV770) { + if (radeon_crtc->crtc_id) + WREG32(R700_D2CUR_SURFACE_ADDRESS_HIGH, upper_32_bits(gpu_addr)); + else + WREG32(R700_D1CUR_SURFACE_ADDRESS_HIGH, upper_32_bits(gpu_addr)); + } + WREG32(AVIVO_D1CUR_SURFACE_ADDRESS + radeon_crtc->crtc_offset, + gpu_addr & 0xffffffff); + } else { + radeon_crtc->legacy_cursor_offset = gpu_addr - radeon_crtc->legacy_display_base_addr; + /* offset is from DISP(2)_BASE_ADDRESS */ + WREG32(RADEON_CUR_OFFSET + radeon_crtc->crtc_offset, radeon_crtc->legacy_cursor_offset); + } +} + +int radeon_crtc_cursor_set(struct drm_crtc *crtc, + struct drm_file *file_priv, + uint32_t handle, + uint32_t width, + uint32_t height) +{ + struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); + struct radeon_device *rdev = crtc->dev->dev_private; + struct drm_gem_object *obj; + struct radeon_bo *robj; + uint64_t gpu_addr; + int ret; + + if (!handle) { + /* turn off cursor */ + radeon_hide_cursor(crtc); + obj = NULL; + goto unpin; + } + + if ((width > CURSOR_WIDTH) || (height > CURSOR_HEIGHT)) { + DRM_ERROR("bad cursor width or height %d x %d\n", width, height); + return -EINVAL; + } + + obj = drm_gem_object_lookup(crtc->dev, file_priv, handle); + if (!obj) { + DRM_ERROR("Cannot find cursor object %x for crtc %d\n", handle, radeon_crtc->crtc_id); + return -ENOENT; + } + + robj = gem_to_radeon_bo(obj); + ret = radeon_bo_reserve(robj, false); + if (unlikely(ret != 0)) + goto fail; + /* Only 27 bit offset for legacy cursor */ + ret = radeon_bo_pin_restricted(robj, RADEON_GEM_DOMAIN_VRAM, + ASIC_IS_AVIVO(rdev) ? 0 : 1 << 27, + &gpu_addr); + radeon_bo_unreserve(robj); + if (ret) + goto fail; + + radeon_crtc->cursor_width = width; + radeon_crtc->cursor_height = height; + + radeon_lock_cursor(crtc, true); + radeon_set_cursor(crtc, obj, gpu_addr); + radeon_show_cursor(crtc); + radeon_lock_cursor(crtc, false); + +unpin: + if (radeon_crtc->cursor_bo) { + robj = gem_to_radeon_bo(radeon_crtc->cursor_bo); + ret = radeon_bo_reserve(robj, false); + if (likely(ret == 0)) { + radeon_bo_unpin(robj); + radeon_bo_unreserve(robj); + } + drm_gem_object_unreference_unlocked(radeon_crtc->cursor_bo); + } + + radeon_crtc->cursor_bo = obj; + return 0; +fail: + drm_gem_object_unreference_unlocked(obj); + + return ret; +} + +int radeon_crtc_cursor_move(struct drm_crtc *crtc, + int x, int y) +{ + struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); + struct radeon_device *rdev = crtc->dev->dev_private; + int xorigin = 0, yorigin = 0; + int w = radeon_crtc->cursor_width; + + if (ASIC_IS_AVIVO(rdev)) { + /* avivo cursor are offset into the total surface */ + x += crtc->x; + y += crtc->y; + } + DRM_DEBUG("x %d y %d c->x %d c->y %d\n", x, y, crtc->x, crtc->y); + + if (x < 0) { + xorigin = min(-x, CURSOR_WIDTH - 1); + x = 0; + } + if (y < 0) { + yorigin = min(-y, CURSOR_HEIGHT - 1); + y = 0; + } + + /* fixed on DCE6 and newer */ + if (ASIC_IS_AVIVO(rdev) && !ASIC_IS_DCE6(rdev)) { + int i = 0; + struct drm_crtc *crtc_p; + + /* avivo cursor image can't end on 128 pixel boundary or + * go past the end of the frame if both crtcs are enabled + */ + list_for_each_entry(crtc_p, &crtc->dev->mode_config.crtc_list, head) { + if (crtc_p->enabled) + i++; + } + if (i > 1) { + int cursor_end, frame_end; + + cursor_end = x - xorigin + w; + frame_end = crtc->x + crtc->mode.crtc_hdisplay; + if (cursor_end >= frame_end) { + w = w - (cursor_end - frame_end); + if (!(frame_end & 0x7f)) + w--; + } else { + if (!(cursor_end & 0x7f)) + w--; + } + if (w <= 0) { + w = 1; + cursor_end = x - xorigin + w; + if (!(cursor_end & 0x7f)) { + x--; + if (x < 0) { + DRM_ERROR("%s: x(%d) < 0", __func__, x); + } + } + } + } + } + + radeon_lock_cursor(crtc, true); + if (ASIC_IS_DCE4(rdev)) { + WREG32(EVERGREEN_CUR_POSITION + radeon_crtc->crtc_offset, (x << 16) | y); + WREG32(EVERGREEN_CUR_HOT_SPOT + radeon_crtc->crtc_offset, (xorigin << 16) | yorigin); + WREG32(EVERGREEN_CUR_SIZE + radeon_crtc->crtc_offset, + ((w - 1) << 16) | (radeon_crtc->cursor_height - 1)); + } else if (ASIC_IS_AVIVO(rdev)) { + WREG32(AVIVO_D1CUR_POSITION + radeon_crtc->crtc_offset, (x << 16) | y); + WREG32(AVIVO_D1CUR_HOT_SPOT + radeon_crtc->crtc_offset, (xorigin << 16) | yorigin); + WREG32(AVIVO_D1CUR_SIZE + radeon_crtc->crtc_offset, + ((w - 1) << 16) | (radeon_crtc->cursor_height - 1)); + } else { + if (crtc->mode.flags & DRM_MODE_FLAG_DBLSCAN) + y *= 2; + + WREG32(RADEON_CUR_HORZ_VERT_OFF + radeon_crtc->crtc_offset, + (RADEON_CUR_LOCK + | (xorigin << 16) + | yorigin)); + WREG32(RADEON_CUR_HORZ_VERT_POSN + radeon_crtc->crtc_offset, + (RADEON_CUR_LOCK + | (x << 16) + | y)); + /* offset is from DISP(2)_BASE_ADDRESS */ + WREG32(RADEON_CUR_OFFSET + radeon_crtc->crtc_offset, (radeon_crtc->legacy_cursor_offset + + (yorigin * 256))); + } + radeon_lock_cursor(crtc, false); + + return 0; +} diff --git a/sys/dev/drm2/radeon/radeon_device.c b/sys/dev/drm2/radeon/radeon_device.c new file mode 100644 index 00000000000..676b38aad94 --- /dev/null +++ b/sys/dev/drm2/radeon/radeon_device.c @@ -0,0 +1,1551 @@ +/* + * Copyright 2008 Advanced Micro Devices, Inc. + * Copyright 2008 Red Hat Inc. + * Copyright 2009 Jerome Glisse. + * + * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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. + * + * Authors: Dave Airlie + * Alex Deucher + * Jerome Glisse + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include "radeon_reg.h" +#include "radeon.h" +#include "atom.h" + +static const char radeon_family_name[][16] = { + "R100", + "RV100", + "RS100", + "RV200", + "RS200", + "R200", + "RV250", + "RS300", + "RV280", + "R300", + "R350", + "RV350", + "RV380", + "R420", + "R423", + "RV410", + "RS400", + "RS480", + "RS600", + "RS690", + "RS740", + "RV515", + "R520", + "RV530", + "RV560", + "RV570", + "R580", + "R600", + "RV610", + "RV630", + "RV670", + "RV620", + "RV635", + "RS780", + "RS880", + "RV770", + "RV730", + "RV710", + "RV740", + "CEDAR", + "REDWOOD", + "JUNIPER", + "CYPRESS", + "HEMLOCK", + "PALM", + "SUMO", + "SUMO2", + "BARTS", + "TURKS", + "CAICOS", + "CAYMAN", + "ARUBA", + "TAHITI", + "PITCAIRN", + "VERDE", + "LAST", +}; + +/** + * radeon_surface_init - Clear GPU surface registers. + * + * @rdev: radeon_device pointer + * + * Clear GPU surface registers (r1xx-r5xx). + */ +void radeon_surface_init(struct radeon_device *rdev) +{ + /* FIXME: check this out */ + if (rdev->family < CHIP_R600) { + int i; + + for (i = 0; i < RADEON_GEM_MAX_SURFACES; i++) { + if (rdev->surface_regs[i].bo) + radeon_bo_get_surface_reg(rdev->surface_regs[i].bo); + else + radeon_clear_surface_reg(rdev, i); + } + /* enable surfaces */ + WREG32(RADEON_SURFACE_CNTL, 0); + } +} + +/* + * GPU scratch registers helpers function. + */ +/** + * radeon_scratch_init - Init scratch register driver information. + * + * @rdev: radeon_device pointer + * + * Init CP scratch register driver information (r1xx-r5xx) + */ +void radeon_scratch_init(struct radeon_device *rdev) +{ + int i; + + /* FIXME: check this out */ + if (rdev->family < CHIP_R300) { + rdev->scratch.num_reg = 5; + } else { + rdev->scratch.num_reg = 7; + } + rdev->scratch.reg_base = RADEON_SCRATCH_REG0; + for (i = 0; i < rdev->scratch.num_reg; i++) { + rdev->scratch.free[i] = true; + rdev->scratch.reg[i] = rdev->scratch.reg_base + (i * 4); + } +} + +/** + * radeon_scratch_get - Allocate a scratch register + * + * @rdev: radeon_device pointer + * @reg: scratch register mmio offset + * + * Allocate a CP scratch register for use by the driver (all asics). + * Returns 0 on success or -EINVAL on failure. + */ +int radeon_scratch_get(struct radeon_device *rdev, uint32_t *reg) +{ + int i; + + for (i = 0; i < rdev->scratch.num_reg; i++) { + if (rdev->scratch.free[i]) { + rdev->scratch.free[i] = false; + *reg = rdev->scratch.reg[i]; + return 0; + } + } + return -EINVAL; +} + +/** + * radeon_scratch_free - Free a scratch register + * + * @rdev: radeon_device pointer + * @reg: scratch register mmio offset + * + * Free a CP scratch register allocated for use by the driver (all asics) + */ +void radeon_scratch_free(struct radeon_device *rdev, uint32_t reg) +{ + int i; + + for (i = 0; i < rdev->scratch.num_reg; i++) { + if (rdev->scratch.reg[i] == reg) { + rdev->scratch.free[i] = true; + return; + } + } +} + +/* + * radeon_wb_*() + * Writeback is the the method by which the the GPU updates special pages + * in memory with the status of certain GPU events (fences, ring pointers, + * etc.). + */ + +/** + * radeon_wb_disable - Disable Writeback + * + * @rdev: radeon_device pointer + * + * Disables Writeback (all asics). Used for suspend. + */ +void radeon_wb_disable(struct radeon_device *rdev) +{ + int r; + + if (rdev->wb.wb_obj) { + r = radeon_bo_reserve(rdev->wb.wb_obj, false); + if (unlikely(r != 0)) + return; + radeon_bo_kunmap(rdev->wb.wb_obj); + radeon_bo_unpin(rdev->wb.wb_obj); + radeon_bo_unreserve(rdev->wb.wb_obj); + } + rdev->wb.enabled = false; +} + +/** + * radeon_wb_fini - Disable Writeback and free memory + * + * @rdev: radeon_device pointer + * + * Disables Writeback and frees the Writeback memory (all asics). + * Used at driver shutdown. + */ +void radeon_wb_fini(struct radeon_device *rdev) +{ + radeon_wb_disable(rdev); + if (rdev->wb.wb_obj) { + radeon_bo_unref(&rdev->wb.wb_obj); + rdev->wb.wb = NULL; + rdev->wb.wb_obj = NULL; + } +} + +/** + * radeon_wb_init- Init Writeback driver info and allocate memory + * + * @rdev: radeon_device pointer + * + * Disables Writeback and frees the Writeback memory (all asics). + * Used at driver startup. + * Returns 0 on success or an -error on failure. + */ +int radeon_wb_init(struct radeon_device *rdev) +{ + int r; + void *wb_ptr; + + if (rdev->wb.wb_obj == NULL) { + r = radeon_bo_create(rdev, RADEON_GPU_PAGE_SIZE, PAGE_SIZE, true, + RADEON_GEM_DOMAIN_GTT, NULL, &rdev->wb.wb_obj); + if (r) { + dev_warn(rdev->dev, "(%d) create WB bo failed\n", r); + return r; + } + } + r = radeon_bo_reserve(rdev->wb.wb_obj, false); + if (unlikely(r != 0)) { + radeon_wb_fini(rdev); + return r; + } + r = radeon_bo_pin(rdev->wb.wb_obj, RADEON_GEM_DOMAIN_GTT, + &rdev->wb.gpu_addr); + if (r) { + radeon_bo_unreserve(rdev->wb.wb_obj); + dev_warn(rdev->dev, "(%d) pin WB bo failed\n", r); + radeon_wb_fini(rdev); + return r; + } + wb_ptr = &rdev->wb.wb; + r = radeon_bo_kmap(rdev->wb.wb_obj, wb_ptr); + radeon_bo_unreserve(rdev->wb.wb_obj); + if (r) { + dev_warn(rdev->dev, "(%d) map WB bo failed\n", r); + radeon_wb_fini(rdev); + return r; + } + + /* clear wb memory */ + memset(*(void **)wb_ptr, 0, RADEON_GPU_PAGE_SIZE); + /* disable event_write fences */ + rdev->wb.use_event = false; + /* disabled via module param */ + if (radeon_no_wb == 1) { + rdev->wb.enabled = false; + } else { + if (rdev->flags & RADEON_IS_AGP) { + /* often unreliable on AGP */ + rdev->wb.enabled = false; + } else if (rdev->family < CHIP_R300) { + /* often unreliable on pre-r300 */ + rdev->wb.enabled = false; + } else { + rdev->wb.enabled = true; + /* event_write fences are only available on r600+ */ + if (rdev->family >= CHIP_R600) { + rdev->wb.use_event = true; + } + } + } + /* always use writeback/events on NI, APUs */ + if (rdev->family >= CHIP_PALM) { + rdev->wb.enabled = true; + rdev->wb.use_event = true; + } + + dev_info(rdev->dev, "WB %sabled\n", rdev->wb.enabled ? "en" : "dis"); + + return 0; +} + +/** + * radeon_vram_location - try to find VRAM location + * @rdev: radeon device structure holding all necessary informations + * @mc: memory controller structure holding memory informations + * @base: base address at which to put VRAM + * + * Function will place try to place VRAM at base address provided + * as parameter (which is so far either PCI aperture address or + * for IGP TOM base address). + * + * If there is not enough space to fit the unvisible VRAM in the 32bits + * address space then we limit the VRAM size to the aperture. + * + * If we are using AGP and if the AGP aperture doesn't allow us to have + * room for all the VRAM than we restrict the VRAM to the PCI aperture + * size and print a warning. + * + * This function will never fails, worst case are limiting VRAM. + * + * Note: GTT start, end, size should be initialized before calling this + * function on AGP platform. + * + * Note: We don't explicitly enforce VRAM start to be aligned on VRAM size, + * this shouldn't be a problem as we are using the PCI aperture as a reference. + * Otherwise this would be needed for rv280, all r3xx, and all r4xx, but + * not IGP. + * + * Note: we use mc_vram_size as on some board we need to program the mc to + * cover the whole aperture even if VRAM size is inferior to aperture size + * Novell bug 204882 + along with lots of ubuntu ones + * + * Note: when limiting vram it's safe to overwritte real_vram_size because + * we are not in case where real_vram_size is inferior to mc_vram_size (ie + * note afected by bogus hw of Novell bug 204882 + along with lots of ubuntu + * ones) + * + * Note: IGP TOM addr should be the same as the aperture addr, we don't + * explicitly check for that thought. + * + * FIXME: when reducing VRAM size align new size on power of 2. + */ +void radeon_vram_location(struct radeon_device *rdev, struct radeon_mc *mc, u64 base) +{ + uint64_t limit = (uint64_t)radeon_vram_limit << 20; + + mc->vram_start = base; + if (mc->mc_vram_size > (0xFFFFFFFF - base + 1)) { + dev_warn(rdev->dev, "limiting VRAM to PCI aperture size\n"); + mc->real_vram_size = mc->aper_size; + mc->mc_vram_size = mc->aper_size; + } + mc->vram_end = mc->vram_start + mc->mc_vram_size - 1; + if (rdev->flags & RADEON_IS_AGP && mc->vram_end > mc->gtt_start && mc->vram_start <= mc->gtt_end) { + dev_warn(rdev->dev, "limiting VRAM to PCI aperture size\n"); + mc->real_vram_size = mc->aper_size; + mc->mc_vram_size = mc->aper_size; + } + mc->vram_end = mc->vram_start + mc->mc_vram_size - 1; + if (limit && limit < mc->real_vram_size) + mc->real_vram_size = limit; + dev_info(rdev->dev, "VRAM: %juM 0x%016jX - 0x%016jX (%juM used)\n", + (uintmax_t)mc->mc_vram_size >> 20, (uintmax_t)mc->vram_start, + (uintmax_t)mc->vram_end, (uintmax_t)mc->real_vram_size >> 20); +} + +/** + * radeon_gtt_location - try to find GTT location + * @rdev: radeon device structure holding all necessary informations + * @mc: memory controller structure holding memory informations + * + * Function will place try to place GTT before or after VRAM. + * + * If GTT size is bigger than space left then we ajust GTT size. + * Thus function will never fails. + * + * FIXME: when reducing GTT size align new size on power of 2. + */ +void radeon_gtt_location(struct radeon_device *rdev, struct radeon_mc *mc) +{ + u64 size_af, size_bf; + + size_af = ((0xFFFFFFFF - mc->vram_end) + mc->gtt_base_align) & ~mc->gtt_base_align; + size_bf = mc->vram_start & ~mc->gtt_base_align; + if (size_bf > size_af) { + if (mc->gtt_size > size_bf) { + dev_warn(rdev->dev, "limiting GTT\n"); + mc->gtt_size = size_bf; + } + mc->gtt_start = (mc->vram_start & ~mc->gtt_base_align) - mc->gtt_size; + } else { + if (mc->gtt_size > size_af) { + dev_warn(rdev->dev, "limiting GTT\n"); + mc->gtt_size = size_af; + } + mc->gtt_start = (mc->vram_end + 1 + mc->gtt_base_align) & ~mc->gtt_base_align; + } + mc->gtt_end = mc->gtt_start + mc->gtt_size - 1; + dev_info(rdev->dev, "GTT: %juM 0x%016jX - 0x%016jX\n", + (uintmax_t)mc->gtt_size >> 20, (uintmax_t)mc->gtt_start, (uintmax_t)mc->gtt_end); +} + +/* + * GPU helpers function. + */ +/** + * radeon_card_posted - check if the hw has already been initialized + * + * @rdev: radeon_device pointer + * + * Check if the asic has been initialized (all asics). + * Used at driver startup. + * Returns true if initialized or false if not. + */ +bool radeon_card_posted(struct radeon_device *rdev) +{ + uint32_t reg; + +#ifdef DUMBBELL_WIP + if (efi_enabled(EFI_BOOT) && + rdev->dev->pci_subvendor == PCI_VENDOR_ID_APPLE) + return false; +#endif /* DUMBBELL_WIP */ + + /* first check CRTCs */ + if (ASIC_IS_DCE41(rdev)) { + reg = RREG32(EVERGREEN_CRTC_CONTROL + EVERGREEN_CRTC0_REGISTER_OFFSET) | + RREG32(EVERGREEN_CRTC_CONTROL + EVERGREEN_CRTC1_REGISTER_OFFSET); + if (reg & EVERGREEN_CRTC_MASTER_EN) + return true; + } else if (ASIC_IS_DCE4(rdev)) { + reg = RREG32(EVERGREEN_CRTC_CONTROL + EVERGREEN_CRTC0_REGISTER_OFFSET) | + RREG32(EVERGREEN_CRTC_CONTROL + EVERGREEN_CRTC1_REGISTER_OFFSET) | + RREG32(EVERGREEN_CRTC_CONTROL + EVERGREEN_CRTC2_REGISTER_OFFSET) | + RREG32(EVERGREEN_CRTC_CONTROL + EVERGREEN_CRTC3_REGISTER_OFFSET) | + RREG32(EVERGREEN_CRTC_CONTROL + EVERGREEN_CRTC4_REGISTER_OFFSET) | + RREG32(EVERGREEN_CRTC_CONTROL + EVERGREEN_CRTC5_REGISTER_OFFSET); + if (reg & EVERGREEN_CRTC_MASTER_EN) + return true; + } else if (ASIC_IS_AVIVO(rdev)) { + reg = RREG32(AVIVO_D1CRTC_CONTROL) | + RREG32(AVIVO_D2CRTC_CONTROL); + if (reg & AVIVO_CRTC_EN) { + return true; + } + } else { + reg = RREG32(RADEON_CRTC_GEN_CNTL) | + RREG32(RADEON_CRTC2_GEN_CNTL); + if (reg & RADEON_CRTC_EN) { + return true; + } + } + + /* then check MEM_SIZE, in case the crtcs are off */ + if (rdev->family >= CHIP_R600) + reg = RREG32(R600_CONFIG_MEMSIZE); + else + reg = RREG32(RADEON_CONFIG_MEMSIZE); + + if (reg) + return true; + + return false; + +} + +/** + * radeon_update_bandwidth_info - update display bandwidth params + * + * @rdev: radeon_device pointer + * + * Used when sclk/mclk are switched or display modes are set. + * params are used to calculate display watermarks (all asics) + */ +void radeon_update_bandwidth_info(struct radeon_device *rdev) +{ + fixed20_12 a; + u32 sclk = rdev->pm.current_sclk; + u32 mclk = rdev->pm.current_mclk; + + /* sclk/mclk in Mhz */ + a.full = dfixed_const(100); + rdev->pm.sclk.full = dfixed_const(sclk); + rdev->pm.sclk.full = dfixed_div(rdev->pm.sclk, a); + rdev->pm.mclk.full = dfixed_const(mclk); + rdev->pm.mclk.full = dfixed_div(rdev->pm.mclk, a); + + if (rdev->flags & RADEON_IS_IGP) { + a.full = dfixed_const(16); + /* core_bandwidth = sclk(Mhz) * 16 */ + rdev->pm.core_bandwidth.full = dfixed_div(rdev->pm.sclk, a); + } +} + +/** + * radeon_boot_test_post_card - check and possibly initialize the hw + * + * @rdev: radeon_device pointer + * + * Check if the asic is initialized and if not, attempt to initialize + * it (all asics). + * Returns true if initialized or false if not. + */ +bool radeon_boot_test_post_card(struct radeon_device *rdev) +{ + if (radeon_card_posted(rdev)) + return true; + + if (rdev->bios) { + DRM_INFO("GPU not posted. posting now...\n"); + if (rdev->is_atom_bios) + atom_asic_init(rdev->mode_info.atom_context); + else + radeon_combios_asic_init(rdev->ddev); + return true; + } else { + dev_err(rdev->dev, "Card not posted and no BIOS - ignoring\n"); + return false; + } +} + +/** + * radeon_dummy_page_init - init dummy page used by the driver + * + * @rdev: radeon_device pointer + * + * Allocate the dummy page used by the driver (all asics). + * This dummy page is used by the driver as a filler for gart entries + * when pages are taken out of the GART + * Returns 0 on sucess, -ENOMEM on failure. + */ +int radeon_dummy_page_init(struct radeon_device *rdev) +{ + if (rdev->dummy_page.dmah) + return 0; + rdev->dummy_page.dmah = drm_pci_alloc(rdev->ddev, + PAGE_SIZE, PAGE_SIZE, ~0); + if (rdev->dummy_page.dmah == NULL) + return -ENOMEM; + rdev->dummy_page.addr = (dma_addr_t)rdev->dummy_page.dmah->vaddr; + return 0; +} + +/** + * radeon_dummy_page_fini - free dummy page used by the driver + * + * @rdev: radeon_device pointer + * + * Frees the dummy page used by the driver (all asics). + */ +void radeon_dummy_page_fini(struct radeon_device *rdev) +{ + if (rdev->dummy_page.dmah == NULL) + return; + drm_pci_free(rdev->ddev, rdev->dummy_page.dmah); + rdev->dummy_page.dmah = NULL; + rdev->dummy_page.addr = 0; +} + + +/* ATOM accessor methods */ +/* + * ATOM is an interpreted byte code stored in tables in the vbios. The + * driver registers callbacks to access registers and the interpreter + * in the driver parses the tables and executes then to program specific + * actions (set display modes, asic init, etc.). See radeon_atombios.c, + * atombios.h, and atom.c + */ + +/** + * cail_pll_read - read PLL register + * + * @info: atom card_info pointer + * @reg: PLL register offset + * + * Provides a PLL register accessor for the atom interpreter (r4xx+). + * Returns the value of the PLL register. + */ +static uint32_t cail_pll_read(struct card_info *info, uint32_t reg) +{ + struct radeon_device *rdev = info->dev->dev_private; + uint32_t r; + + r = rdev->pll_rreg(rdev, reg); + return r; +} + +/** + * cail_pll_write - write PLL register + * + * @info: atom card_info pointer + * @reg: PLL register offset + * @val: value to write to the pll register + * + * Provides a PLL register accessor for the atom interpreter (r4xx+). + */ +static void cail_pll_write(struct card_info *info, uint32_t reg, uint32_t val) +{ + struct radeon_device *rdev = info->dev->dev_private; + + rdev->pll_wreg(rdev, reg, val); +} + +/** + * cail_mc_read - read MC (Memory Controller) register + * + * @info: atom card_info pointer + * @reg: MC register offset + * + * Provides an MC register accessor for the atom interpreter (r4xx+). + * Returns the value of the MC register. + */ +static uint32_t cail_mc_read(struct card_info *info, uint32_t reg) +{ + struct radeon_device *rdev = info->dev->dev_private; + uint32_t r; + + r = rdev->mc_rreg(rdev, reg); + return r; +} + +/** + * cail_mc_write - write MC (Memory Controller) register + * + * @info: atom card_info pointer + * @reg: MC register offset + * @val: value to write to the pll register + * + * Provides a MC register accessor for the atom interpreter (r4xx+). + */ +static void cail_mc_write(struct card_info *info, uint32_t reg, uint32_t val) +{ + struct radeon_device *rdev = info->dev->dev_private; + + rdev->mc_wreg(rdev, reg, val); +} + +/** + * cail_reg_write - write MMIO register + * + * @info: atom card_info pointer + * @reg: MMIO register offset + * @val: value to write to the pll register + * + * Provides a MMIO register accessor for the atom interpreter (r4xx+). + */ +static void cail_reg_write(struct card_info *info, uint32_t reg, uint32_t val) +{ + struct radeon_device *rdev = info->dev->dev_private; + + WREG32(reg*4, val); +} + +/** + * cail_reg_read - read MMIO register + * + * @info: atom card_info pointer + * @reg: MMIO register offset + * + * Provides an MMIO register accessor for the atom interpreter (r4xx+). + * Returns the value of the MMIO register. + */ +static uint32_t cail_reg_read(struct card_info *info, uint32_t reg) +{ + struct radeon_device *rdev = info->dev->dev_private; + uint32_t r; + + r = RREG32(reg*4); + return r; +} + +/** + * cail_ioreg_write - write IO register + * + * @info: atom card_info pointer + * @reg: IO register offset + * @val: value to write to the pll register + * + * Provides a IO register accessor for the atom interpreter (r4xx+). + */ +static void cail_ioreg_write(struct card_info *info, uint32_t reg, uint32_t val) +{ + struct radeon_device *rdev = info->dev->dev_private; + + WREG32_IO(reg*4, val); +} + +/** + * cail_ioreg_read - read IO register + * + * @info: atom card_info pointer + * @reg: IO register offset + * + * Provides an IO register accessor for the atom interpreter (r4xx+). + * Returns the value of the IO register. + */ +static uint32_t cail_ioreg_read(struct card_info *info, uint32_t reg) +{ + struct radeon_device *rdev = info->dev->dev_private; + uint32_t r; + + r = RREG32_IO(reg*4); + return r; +} + +/** + * radeon_atombios_init - init the driver info and callbacks for atombios + * + * @rdev: radeon_device pointer + * + * Initializes the driver info and register access callbacks for the + * ATOM interpreter (r4xx+). + * Returns 0 on sucess, -ENOMEM on failure. + * Called at driver startup. + */ +int radeon_atombios_init(struct radeon_device *rdev) +{ + struct card_info *atom_card_info = + malloc(sizeof(struct card_info), + DRM_MEM_DRIVER, M_ZERO | M_WAITOK); + + if (!atom_card_info) + return -ENOMEM; + + rdev->mode_info.atom_card_info = atom_card_info; + atom_card_info->dev = rdev->ddev; + atom_card_info->reg_read = cail_reg_read; + atom_card_info->reg_write = cail_reg_write; + /* needed for iio ops */ + if (rdev->rio_mem) { + atom_card_info->ioreg_read = cail_ioreg_read; + atom_card_info->ioreg_write = cail_ioreg_write; + } else { + DRM_ERROR("Unable to find PCI I/O BAR; using MMIO for ATOM IIO\n"); + atom_card_info->ioreg_read = cail_reg_read; + atom_card_info->ioreg_write = cail_reg_write; + } + atom_card_info->mc_read = cail_mc_read; + atom_card_info->mc_write = cail_mc_write; + atom_card_info->pll_read = cail_pll_read; + atom_card_info->pll_write = cail_pll_write; + + rdev->mode_info.atom_context = atom_parse(atom_card_info, rdev->bios); + sx_init(&rdev->mode_info.atom_context->mutex, + "drm__radeon_device__mode_info__atom_context__mutex"); + radeon_atom_initialize_bios_scratch_regs(rdev->ddev); + atom_allocate_fb_scratch(rdev->mode_info.atom_context); + return 0; +} + +/** + * radeon_atombios_fini - free the driver info and callbacks for atombios + * + * @rdev: radeon_device pointer + * + * Frees the driver info and register access callbacks for the ATOM + * interpreter (r4xx+). + * Called at driver shutdown. + */ +void radeon_atombios_fini(struct radeon_device *rdev) +{ + if (rdev->mode_info.atom_context) { + free(rdev->mode_info.atom_context->scratch, DRM_MEM_DRIVER); + atom_destroy(rdev->mode_info.atom_context); + } + free(rdev->mode_info.atom_card_info, DRM_MEM_DRIVER); +} + +/* COMBIOS */ +/* + * COMBIOS is the bios format prior to ATOM. It provides + * command tables similar to ATOM, but doesn't have a unified + * parser. See radeon_combios.c + */ + +/** + * radeon_combios_init - init the driver info for combios + * + * @rdev: radeon_device pointer + * + * Initializes the driver info for combios (r1xx-r3xx). + * Returns 0 on sucess. + * Called at driver startup. + */ +int radeon_combios_init(struct radeon_device *rdev) +{ + radeon_combios_initialize_bios_scratch_regs(rdev->ddev); + return 0; +} + +/** + * radeon_combios_fini - free the driver info for combios + * + * @rdev: radeon_device pointer + * + * Frees the driver info for combios (r1xx-r3xx). + * Called at driver shutdown. + */ +void radeon_combios_fini(struct radeon_device *rdev) +{ +} + +#ifdef DUMBBELL_WIP +/* if we get transitioned to only one device, take VGA back */ +/** + * radeon_vga_set_decode - enable/disable vga decode + * + * @cookie: radeon_device pointer + * @state: enable/disable vga decode + * + * Enable/disable vga decode (all asics). + * Returns VGA resource flags. + */ +static unsigned int radeon_vga_set_decode(void *cookie, bool state) +{ + struct radeon_device *rdev = cookie; + radeon_vga_set_state(rdev, state); + if (state) + return VGA_RSRC_LEGACY_IO | VGA_RSRC_LEGACY_MEM | + VGA_RSRC_NORMAL_IO | VGA_RSRC_NORMAL_MEM; + else + return VGA_RSRC_NORMAL_IO | VGA_RSRC_NORMAL_MEM; +} +#endif /* DUMBBELL_WIP */ + +/** + * radeon_check_pot_argument - check that argument is a power of two + * + * @arg: value to check + * + * Validates that a certain argument is a power of two (all asics). + * Returns true if argument is valid. + */ +static bool radeon_check_pot_argument(int arg) +{ + return (arg & (arg - 1)) == 0; +} + +/** + * radeon_check_arguments - validate module params + * + * @rdev: radeon_device pointer + * + * Validates certain module parameters and updates + * the associated values used by the driver (all asics). + */ +static void radeon_check_arguments(struct radeon_device *rdev) +{ + /* vramlimit must be a power of two */ + if (!radeon_check_pot_argument(radeon_vram_limit)) { + dev_warn(rdev->dev, "vram limit (%d) must be a power of 2\n", + radeon_vram_limit); + radeon_vram_limit = 0; + } + + /* gtt size must be power of two and greater or equal to 32M */ + if (radeon_gart_size < 32) { + dev_warn(rdev->dev, "gart size (%d) too small forcing to 512M\n", + radeon_gart_size); + radeon_gart_size = 512; + + } else if (!radeon_check_pot_argument(radeon_gart_size)) { + dev_warn(rdev->dev, "gart size (%d) must be a power of 2\n", + radeon_gart_size); + radeon_gart_size = 512; + } + rdev->mc.gtt_size = (uint64_t)radeon_gart_size << 20; + + /* AGP mode can only be -1, 1, 2, 4, 8 */ + switch (radeon_agpmode) { + case -1: + case 0: + case 1: + case 2: + case 4: + case 8: + break; + default: + dev_warn(rdev->dev, "invalid AGP mode %d (valid mode: " + "-1, 0, 1, 2, 4, 8)\n", radeon_agpmode); + radeon_agpmode = 0; + break; + } +} + +/** + * radeon_switcheroo_quirk_long_wakeup - return true if longer d3 delay is + * needed for waking up. + * + * @pdev: pci dev pointer + */ +#ifdef DUMBBELL_WIP +static bool radeon_switcheroo_quirk_long_wakeup(struct pci_dev *pdev) +{ + + /* 6600m in a macbook pro */ + if (pdev->subsystem_vendor == PCI_VENDOR_ID_APPLE && + pdev->subsystem_device == 0x00e2) { + printk(KERN_INFO "radeon: quirking longer d3 wakeup delay\n"); + return true; + } + + return false; +} +#endif /* DUMBBELL_WIP */ + +/** + * radeon_switcheroo_set_state - set switcheroo state + * + * @pdev: pci dev pointer + * @state: vga switcheroo state + * + * Callback for the switcheroo driver. Suspends or resumes the + * the asics before or after it is powered up using ACPI methods. + */ +#ifdef DUMBBELL_WIP +static void radeon_switcheroo_set_state(struct pci_dev *pdev, enum vga_switcheroo_state state) +{ + struct drm_device *dev = pci_get_drvdata(pdev); + pm_message_t pmm = { .event = PM_EVENT_SUSPEND }; + if (state == VGA_SWITCHEROO_ON) { + unsigned d3_delay = dev->pdev->d3_delay; + + printk(KERN_INFO "radeon: switched on\n"); + /* don't suspend or resume card normally */ + dev->switch_power_state = DRM_SWITCH_POWER_CHANGING; + + if (d3_delay < 20 && radeon_switcheroo_quirk_long_wakeup(pdev)) + dev->pdev->d3_delay = 20; + + radeon_resume_kms(dev); + + dev->pdev->d3_delay = d3_delay; + + dev->switch_power_state = DRM_SWITCH_POWER_ON; + drm_kms_helper_poll_enable(dev); + } else { + printk(KERN_INFO "radeon: switched off\n"); + drm_kms_helper_poll_disable(dev); + dev->switch_power_state = DRM_SWITCH_POWER_CHANGING; + radeon_suspend_kms(dev, pmm); + dev->switch_power_state = DRM_SWITCH_POWER_OFF; + } +} +#endif /* DUMBBELL_WIP */ + +/** + * radeon_switcheroo_can_switch - see if switcheroo state can change + * + * @pdev: pci dev pointer + * + * Callback for the switcheroo driver. Check of the switcheroo + * state can be changed. + * Returns true if the state can be changed, false if not. + */ +#ifdef DUMBBELL_WIP +static bool radeon_switcheroo_can_switch(struct pci_dev *pdev) +{ + struct drm_device *dev = pci_get_drvdata(pdev); + bool can_switch; + + spin_lock(&dev->count_lock); + can_switch = (dev->open_count == 0); + spin_unlock(&dev->count_lock); + return can_switch; +} + +static const struct vga_switcheroo_client_ops radeon_switcheroo_ops = { + .set_gpu_state = radeon_switcheroo_set_state, + .reprobe = NULL, + .can_switch = radeon_switcheroo_can_switch, +}; +#endif /* DUMBBELL_WIP */ + +/** + * radeon_device_init - initialize the driver + * + * @rdev: radeon_device pointer + * @pdev: drm dev pointer + * @flags: driver flags + * + * Initializes the driver info and hw (all asics). + * Returns 0 for success or an error on failure. + * Called at driver startup. + */ +int radeon_device_init(struct radeon_device *rdev, + struct drm_device *ddev, + uint32_t flags) +{ + int r, i; + int dma_bits; + + rdev->shutdown = false; + rdev->dev = ddev->device; + rdev->ddev = ddev; + rdev->flags = flags; + rdev->family = flags & RADEON_FAMILY_MASK; + rdev->is_atom_bios = false; + rdev->usec_timeout = RADEON_MAX_USEC_TIMEOUT; + rdev->mc.gtt_size = radeon_gart_size * 1024 * 1024; + rdev->accel_working = false; + rdev->fictitious_range_registered = false; + /* set up ring ids */ + for (i = 0; i < RADEON_NUM_RINGS; i++) { + rdev->ring[i].idx = i; + } + + DRM_INFO("initializing kernel modesetting (%s 0x%04X:0x%04X 0x%04X:0x%04X).\n", + radeon_family_name[rdev->family], ddev->pci_vendor, ddev->pci_device, + ddev->pci_subvendor, ddev->pci_subdevice); + + /* mutex initialization are all done here so we + * can recall function without having locking issues */ + sx_init(&rdev->ring_lock, "drm__radeon_device__ring_lock"); + sx_init(&rdev->dc_hw_i2c_mutex, "drm__radeon_device__dc_hw_i2c_mutex"); + atomic_set(&rdev->ih.lock, 0); + sx_init(&rdev->gem.mutex, "drm__radeon_device__gem__mutex"); + sx_init(&rdev->pm.mutex, "drm__radeon_device__pm__mutex"); + sx_init(&rdev->gpu_clock_mutex, "drm__radeon_device__gpu_clock_mutex"); + sx_init(&rdev->pm.mclk_lock, "drm__radeon_device__pm__mclk_lock"); + sx_init(&rdev->exclusive_lock, "drm__radeon_device__exclusive_lock"); + DRM_INIT_WAITQUEUE(&rdev->irq.vblank_queue); + r = radeon_gem_init(rdev); + if (r) + return r; + /* initialize vm here */ + sx_init(&rdev->vm_manager.lock, "drm__radeon_device__vm_manager__lock"); + /* Adjust VM size here. + * Currently set to 4GB ((1 << 20) 4k pages). + * Max GPUVM size for cayman and SI is 40 bits. + */ + rdev->vm_manager.max_pfn = 1 << 20; + INIT_LIST_HEAD(&rdev->vm_manager.lru_vm); + + /* Set asic functions */ + r = radeon_asic_init(rdev); + if (r) + return r; + radeon_check_arguments(rdev); + + /* all of the newer IGP chips have an internal gart + * However some rs4xx report as AGP, so remove that here. + */ + if ((rdev->family >= CHIP_RS400) && + (rdev->flags & RADEON_IS_IGP)) { + rdev->flags &= ~RADEON_IS_AGP; + } + + if (rdev->flags & RADEON_IS_AGP && radeon_agpmode == -1) { + radeon_agp_disable(rdev); + } + + /* set DMA mask + need_dma32 flags. + * PCIE - can handle 40-bits. + * IGP - can handle 40-bits + * AGP - generally dma32 is safest + * PCI - dma32 for legacy pci gart, 40 bits on newer asics + */ + rdev->need_dma32 = false; + if (rdev->flags & RADEON_IS_AGP) + rdev->need_dma32 = true; + if ((rdev->flags & RADEON_IS_PCI) && + (rdev->family <= CHIP_RS740)) + rdev->need_dma32 = true; + + dma_bits = rdev->need_dma32 ? 32 : 40; +#ifdef DUMBBELL_WIP + r = pci_set_dma_mask(rdev->pdev, DMA_BIT_MASK(dma_bits)); + if (r) { + rdev->need_dma32 = true; + dma_bits = 32; + printk(KERN_WARNING "radeon: No suitable DMA available.\n"); + } + r = pci_set_consistent_dma_mask(rdev->pdev, DMA_BIT_MASK(dma_bits)); + if (r) { + pci_set_consistent_dma_mask(rdev->pdev, DMA_BIT_MASK(32)); + printk(KERN_WARNING "radeon: No coherent DMA available.\n"); + } +#endif /* DUMBBELL_WIP */ + + /* Registers mapping */ + /* TODO: block userspace mapping of io register */ + DRM_SPININIT(&rdev->mmio_idx_lock, "drm__radeon_device__mmio_idx_lock"); + rdev->rmmio_rid = PCIR_BAR(2); + rdev->rmmio = bus_alloc_resource_any(rdev->dev, SYS_RES_MEMORY, + &rdev->rmmio_rid, RF_ACTIVE | RF_SHAREABLE); + if (rdev->rmmio == NULL) { + return -ENOMEM; + } + rdev->rmmio_base = rman_get_start(rdev->rmmio); + rdev->rmmio_size = rman_get_size(rdev->rmmio); + DRM_INFO("register mmio base: 0x%08X\n", (uint32_t)rdev->rmmio_base); + DRM_INFO("register mmio size: %u\n", (unsigned)rdev->rmmio_size); + + /* io port mapping */ + for (i = 0; i < DRM_MAX_PCI_RESOURCE; i++) { + uint32_t data; + + data = pci_read_config(rdev->dev, PCIR_BAR(i), 4); + if (PCI_BAR_IO(data)) { + rdev->rio_rid = PCIR_BAR(i); + rdev->rio_mem = bus_alloc_resource_any(rdev->dev, + SYS_RES_IOPORT, &rdev->rio_rid, + RF_ACTIVE | RF_SHAREABLE); + break; + } + } + if (rdev->rio_mem == NULL) + DRM_ERROR("Unable to find PCI I/O BAR\n"); + + rdev->tq = taskqueue_create("radeonkms", M_WAITOK, + taskqueue_thread_enqueue, &rdev->tq); + taskqueue_start_threads(&rdev->tq, 1, PWAIT, "radeon taskq"); + +#ifdef DUMBBELL_WIP + /* if we have > 1 VGA cards, then disable the radeon VGA resources */ + /* this will fail for cards that aren't VGA class devices, just + * ignore it */ + vga_client_register(rdev->pdev, rdev, NULL, radeon_vga_set_decode); + vga_switcheroo_register_client(rdev->pdev, &radeon_switcheroo_ops); +#endif /* DUMBBELL_WIP */ + + r = radeon_init(rdev); + if (r) + return r; + + r = radeon_ib_ring_tests(rdev); + if (r) + DRM_ERROR("ib ring test failed (%d).\n", r); + + if (rdev->flags & RADEON_IS_AGP && !rdev->accel_working) { + /* Acceleration not working on AGP card try again + * with fallback to PCI or PCIE GART + */ + radeon_asic_reset(rdev); + radeon_fini(rdev); + radeon_agp_disable(rdev); + r = radeon_init(rdev); + if (r) + return r; + } + + DRM_INFO("%s: Taking over the fictitious range 0x%jx-0x%jx\n", + __func__, (uintmax_t)rdev->mc.aper_base, + (uintmax_t)rdev->mc.aper_base + rdev->mc.visible_vram_size); + r = vm_phys_fictitious_reg_range( + rdev->mc.aper_base, + rdev->mc.aper_base + rdev->mc.visible_vram_size, + VM_MEMATTR_WRITE_COMBINING); + if (r != 0) { + DRM_ERROR("Failed to register fictitious range " + "0x%jx-0x%jx (%d).\n", (uintmax_t)rdev->mc.aper_base, + (uintmax_t)rdev->mc.aper_base + rdev->mc.visible_vram_size, r); + return (-r); + } + rdev->fictitious_range_registered = true; + + if ((radeon_testing & 1)) { + radeon_test_moves(rdev); + } + if ((radeon_testing & 2)) { + radeon_test_syncing(rdev); + } + if (radeon_benchmarking) { + radeon_benchmark(rdev, radeon_benchmarking); + } + return 0; +} + +#ifdef DUMBBELL_WIP +static void radeon_debugfs_remove_files(struct radeon_device *rdev); +#endif /* DUMBBELL_WIP */ + +/** + * radeon_device_fini - tear down the driver + * + * @rdev: radeon_device pointer + * + * Tear down the driver info (all asics). + * Called at driver shutdown. + */ +void radeon_device_fini(struct radeon_device *rdev) +{ + DRM_INFO("radeon: finishing device.\n"); + rdev->shutdown = true; + /* evict vram memory */ + radeon_bo_evict_vram(rdev); + + if (rdev->fictitious_range_registered) { + vm_phys_fictitious_unreg_range( + rdev->mc.aper_base, + rdev->mc.aper_base + rdev->mc.visible_vram_size); + } + + radeon_fini(rdev); +#ifdef DUMBBELL_WIP + vga_switcheroo_unregister_client(rdev->pdev); + vga_client_register(rdev->pdev, NULL, NULL, NULL); +#endif /* DUMBBELL_WIP */ + + if (rdev->tq != NULL) { + taskqueue_free(rdev->tq); + rdev->tq = NULL; + } + + if (rdev->rio_mem) + bus_release_resource(rdev->dev, SYS_RES_IOPORT, rdev->rio_rid, + rdev->rio_mem); + rdev->rio_mem = NULL; + bus_release_resource(rdev->dev, SYS_RES_MEMORY, rdev->rmmio_rid, + rdev->rmmio); + rdev->rmmio = NULL; +#ifdef DUMBBELL_WIP + radeon_debugfs_remove_files(rdev); +#endif /* DUMBBELL_WIP */ +} + + +/* + * Suspend & resume. + */ +/** + * radeon_suspend_kms - initiate device suspend + * + * @pdev: drm dev pointer + * @state: suspend state + * + * Puts the hw in the suspend state (all asics). + * Returns 0 for success or an error on failure. + * Called at driver suspend. + */ +int radeon_suspend_kms(struct drm_device *dev) +{ + struct radeon_device *rdev; + struct drm_crtc *crtc; + struct drm_connector *connector; + int i, r; + bool force_completion = false; + + if (dev == NULL || dev->dev_private == NULL) { + return -ENODEV; + } +#ifdef DUMBBELL_WIP + if (state.event == PM_EVENT_PRETHAW) { + return 0; + } +#endif /* DUMBBELL_WIP */ + rdev = dev->dev_private; + + if (dev->switch_power_state == DRM_SWITCH_POWER_OFF) + return 0; + + drm_kms_helper_poll_disable(dev); + + /* turn off display hw */ + list_for_each_entry(connector, &dev->mode_config.connector_list, head) { + drm_helper_connector_dpms(connector, DRM_MODE_DPMS_OFF); + } + + /* unpin the front buffers */ + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { + struct radeon_framebuffer *rfb = to_radeon_framebuffer(crtc->fb); + struct radeon_bo *robj; + + if (rfb == NULL || rfb->obj == NULL) { + continue; + } + robj = gem_to_radeon_bo(rfb->obj); + /* don't unpin kernel fb objects */ + if (!radeon_fbdev_robj_is_fb(rdev, robj)) { + r = radeon_bo_reserve(robj, false); + if (r == 0) { + radeon_bo_unpin(robj); + radeon_bo_unreserve(robj); + } + } + } + /* evict vram memory */ + radeon_bo_evict_vram(rdev); + + sx_xlock(&rdev->ring_lock); + /* wait for gpu to finish processing current batch */ + for (i = 0; i < RADEON_NUM_RINGS; i++) { + r = radeon_fence_wait_empty_locked(rdev, i); + if (r) { + /* delay GPU reset to resume */ + force_completion = true; + } + } + if (force_completion) { + radeon_fence_driver_force_completion(rdev); + } + sx_xunlock(&rdev->ring_lock); + + radeon_save_bios_scratch_regs(rdev); + + radeon_pm_suspend(rdev); + radeon_suspend(rdev); + radeon_hpd_fini(rdev); + /* evict remaining vram memory */ + radeon_bo_evict_vram(rdev); + + radeon_agp_suspend(rdev); + + pci_save_state(device_get_parent(rdev->dev)); +#ifdef DUMBBELL_WIP + if (state.event == PM_EVENT_SUSPEND) { + /* Shut down the device */ + pci_disable_device(dev->pdev); +#endif /* DUMBBELL_WIP */ + pci_set_powerstate(dev->device, PCI_POWERSTATE_D3); +#ifdef DUMBBELL_WIP + } + console_lock(); +#endif /* DUMBBELL_WIP */ + radeon_fbdev_set_suspend(rdev, 1); +#ifdef DUMBBELL_WIP + console_unlock(); +#endif /* DUMBBELL_WIP */ + return 0; +} + +/** + * radeon_resume_kms - initiate device resume + * + * @pdev: drm dev pointer + * + * Bring the hw back to operating state (all asics). + * Returns 0 for success or an error on failure. + * Called at driver resume. + */ +int radeon_resume_kms(struct drm_device *dev) +{ + struct drm_connector *connector; + struct radeon_device *rdev = dev->dev_private; + int r; + + if (dev->switch_power_state == DRM_SWITCH_POWER_OFF) + return 0; + +#ifdef DUMBBELL_WIP + console_lock(); +#endif /* DUMBBELL_WIP */ + pci_set_powerstate(dev->device, PCI_POWERSTATE_D0); + pci_restore_state(device_get_parent(rdev->dev)); +#ifdef DUMBBELL_WIP + if (pci_enable_device(dev->pdev)) { + console_unlock(); + return -1; + } +#endif /* DUMBBELL_WIP */ + /* resume AGP if in use */ + radeon_agp_resume(rdev); + radeon_resume(rdev); + + r = radeon_ib_ring_tests(rdev); + if (r) + DRM_ERROR("ib ring test failed (%d).\n", r); + + radeon_pm_resume(rdev); + radeon_restore_bios_scratch_regs(rdev); + + radeon_fbdev_set_suspend(rdev, 0); +#ifdef DUMBBELL_WIP + console_unlock(); +#endif /* DUMBBELL_WIP */ + + /* init dig PHYs, disp eng pll */ + if (rdev->is_atom_bios) { + radeon_atom_encoder_init(rdev); + radeon_atom_disp_eng_pll_init(rdev); + /* turn on the BL */ + if (rdev->mode_info.bl_encoder) { + u8 bl_level = radeon_get_backlight_level(rdev, + rdev->mode_info.bl_encoder); + radeon_set_backlight_level(rdev, rdev->mode_info.bl_encoder, + bl_level); + } + } + /* reset hpd state */ + radeon_hpd_init(rdev); + /* blat the mode back in */ + drm_helper_resume_force_mode(dev); + /* turn on display hw */ + list_for_each_entry(connector, &dev->mode_config.connector_list, head) { + drm_helper_connector_dpms(connector, DRM_MODE_DPMS_ON); + } + + drm_kms_helper_poll_enable(dev); + return 0; +} + +/** + * radeon_gpu_reset - reset the asic + * + * @rdev: radeon device pointer + * + * Attempt the reset the GPU if it has hung (all asics). + * Returns 0 for success or an error on failure. + */ +int radeon_gpu_reset(struct radeon_device *rdev) +{ + unsigned ring_sizes[RADEON_NUM_RINGS]; + uint32_t *ring_data[RADEON_NUM_RINGS]; + + bool saved = false; + + int i, r; + int resched; + + sx_xlock(&rdev->exclusive_lock); + radeon_save_bios_scratch_regs(rdev); + /* block TTM */ + resched = ttm_bo_lock_delayed_workqueue(&rdev->mman.bdev); + radeon_suspend(rdev); + + for (i = 0; i < RADEON_NUM_RINGS; ++i) { + ring_sizes[i] = radeon_ring_backup(rdev, &rdev->ring[i], + &ring_data[i]); + if (ring_sizes[i]) { + saved = true; + dev_info(rdev->dev, "Saved %d dwords of commands " + "on ring %d.\n", ring_sizes[i], i); + } + } + +retry: + r = radeon_asic_reset(rdev); + if (!r) { + dev_info(rdev->dev, "GPU reset succeeded, trying to resume\n"); + radeon_resume(rdev); + } + + radeon_restore_bios_scratch_regs(rdev); + + if (!r) { + for (i = 0; i < RADEON_NUM_RINGS; ++i) { + radeon_ring_restore(rdev, &rdev->ring[i], + ring_sizes[i], ring_data[i]); + ring_sizes[i] = 0; + ring_data[i] = NULL; + } + + r = radeon_ib_ring_tests(rdev); + if (r) { + dev_err(rdev->dev, "ib ring test failed (%d).\n", r); + if (saved) { + saved = false; + radeon_suspend(rdev); + goto retry; + } + } + } else { + radeon_fence_driver_force_completion(rdev); + for (i = 0; i < RADEON_NUM_RINGS; ++i) { + free(ring_data[i], DRM_MEM_DRIVER); + } + } + + drm_helper_resume_force_mode(rdev->ddev); + + ttm_bo_unlock_delayed_workqueue(&rdev->mman.bdev, resched); + if (r) { + /* bad news, how to tell it to userspace ? */ + dev_info(rdev->dev, "GPU reset failed\n"); + } + + sx_xunlock(&rdev->exclusive_lock); + return r; +} + + +/* + * Debugfs + */ +#ifdef DUMBBELL_WIP +int radeon_debugfs_add_files(struct radeon_device *rdev, + struct drm_info_list *files, + unsigned nfiles) +{ + unsigned i; + + for (i = 0; i < rdev->debugfs_count; i++) { + if (rdev->debugfs[i].files == files) { + /* Already registered */ + return 0; + } + } + + i = rdev->debugfs_count + 1; + if (i > RADEON_DEBUGFS_MAX_COMPONENTS) { + DRM_ERROR("Reached maximum number of debugfs components.\n"); + DRM_ERROR("Report so we increase " + "RADEON_DEBUGFS_MAX_COMPONENTS.\n"); + return -EINVAL; + } + rdev->debugfs[rdev->debugfs_count].files = files; + rdev->debugfs[rdev->debugfs_count].num_files = nfiles; + rdev->debugfs_count = i; +#if defined(CONFIG_DEBUG_FS) + drm_debugfs_create_files(files, nfiles, + rdev->ddev->control->debugfs_root, + rdev->ddev->control); + drm_debugfs_create_files(files, nfiles, + rdev->ddev->primary->debugfs_root, + rdev->ddev->primary); +#endif + return 0; +} + +static void radeon_debugfs_remove_files(struct radeon_device *rdev) +{ +#if defined(CONFIG_DEBUG_FS) + unsigned i; + + for (i = 0; i < rdev->debugfs_count; i++) { + drm_debugfs_remove_files(rdev->debugfs[i].files, + rdev->debugfs[i].num_files, + rdev->ddev->control); + drm_debugfs_remove_files(rdev->debugfs[i].files, + rdev->debugfs[i].num_files, + rdev->ddev->primary); + } +#endif +} + +#if defined(CONFIG_DEBUG_FS) +int radeon_debugfs_init(struct drm_minor *minor) +{ + return 0; +} + +void radeon_debugfs_cleanup(struct drm_minor *minor) +{ +} +#endif /* DUMBBELL_WIP */ +#endif diff --git a/sys/dev/drm2/radeon/radeon_display.c b/sys/dev/drm2/radeon/radeon_display.c new file mode 100644 index 00000000000..45c3b0cef68 --- /dev/null +++ b/sys/dev/drm2/radeon/radeon_display.c @@ -0,0 +1,1691 @@ +/* + * Copyright 2007-8 Advanced Micro Devices, Inc. + * Copyright 2008 Red Hat Inc. + * + * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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. + * + * Authors: Dave Airlie + * Alex Deucher + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include "radeon.h" + +#include "atom.h" + +#include +#include + +static void avivo_crtc_load_lut(struct drm_crtc *crtc) +{ + struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); + struct drm_device *dev = crtc->dev; + struct radeon_device *rdev = dev->dev_private; + int i; + + DRM_DEBUG_KMS("%d\n", radeon_crtc->crtc_id); + WREG32(AVIVO_DC_LUTA_CONTROL + radeon_crtc->crtc_offset, 0); + + WREG32(AVIVO_DC_LUTA_BLACK_OFFSET_BLUE + radeon_crtc->crtc_offset, 0); + WREG32(AVIVO_DC_LUTA_BLACK_OFFSET_GREEN + radeon_crtc->crtc_offset, 0); + WREG32(AVIVO_DC_LUTA_BLACK_OFFSET_RED + radeon_crtc->crtc_offset, 0); + + WREG32(AVIVO_DC_LUTA_WHITE_OFFSET_BLUE + radeon_crtc->crtc_offset, 0xffff); + WREG32(AVIVO_DC_LUTA_WHITE_OFFSET_GREEN + radeon_crtc->crtc_offset, 0xffff); + WREG32(AVIVO_DC_LUTA_WHITE_OFFSET_RED + radeon_crtc->crtc_offset, 0xffff); + + WREG32(AVIVO_DC_LUT_RW_SELECT, radeon_crtc->crtc_id); + WREG32(AVIVO_DC_LUT_RW_MODE, 0); + WREG32(AVIVO_DC_LUT_WRITE_EN_MASK, 0x0000003f); + + WREG8(AVIVO_DC_LUT_RW_INDEX, 0); + for (i = 0; i < 256; i++) { + WREG32(AVIVO_DC_LUT_30_COLOR, + (radeon_crtc->lut_r[i] << 20) | + (radeon_crtc->lut_g[i] << 10) | + (radeon_crtc->lut_b[i] << 0)); + } + + WREG32(AVIVO_D1GRPH_LUT_SEL + radeon_crtc->crtc_offset, radeon_crtc->crtc_id); +} + +static void dce4_crtc_load_lut(struct drm_crtc *crtc) +{ + struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); + struct drm_device *dev = crtc->dev; + struct radeon_device *rdev = dev->dev_private; + int i; + + DRM_DEBUG_KMS("%d\n", radeon_crtc->crtc_id); + WREG32(EVERGREEN_DC_LUT_CONTROL + radeon_crtc->crtc_offset, 0); + + WREG32(EVERGREEN_DC_LUT_BLACK_OFFSET_BLUE + radeon_crtc->crtc_offset, 0); + WREG32(EVERGREEN_DC_LUT_BLACK_OFFSET_GREEN + radeon_crtc->crtc_offset, 0); + WREG32(EVERGREEN_DC_LUT_BLACK_OFFSET_RED + radeon_crtc->crtc_offset, 0); + + WREG32(EVERGREEN_DC_LUT_WHITE_OFFSET_BLUE + radeon_crtc->crtc_offset, 0xffff); + WREG32(EVERGREEN_DC_LUT_WHITE_OFFSET_GREEN + radeon_crtc->crtc_offset, 0xffff); + WREG32(EVERGREEN_DC_LUT_WHITE_OFFSET_RED + radeon_crtc->crtc_offset, 0xffff); + + WREG32(EVERGREEN_DC_LUT_RW_MODE + radeon_crtc->crtc_offset, 0); + WREG32(EVERGREEN_DC_LUT_WRITE_EN_MASK + radeon_crtc->crtc_offset, 0x00000007); + + WREG32(EVERGREEN_DC_LUT_RW_INDEX + radeon_crtc->crtc_offset, 0); + for (i = 0; i < 256; i++) { + WREG32(EVERGREEN_DC_LUT_30_COLOR + radeon_crtc->crtc_offset, + (radeon_crtc->lut_r[i] << 20) | + (radeon_crtc->lut_g[i] << 10) | + (radeon_crtc->lut_b[i] << 0)); + } +} + +static void dce5_crtc_load_lut(struct drm_crtc *crtc) +{ + struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); + struct drm_device *dev = crtc->dev; + struct radeon_device *rdev = dev->dev_private; + int i; + + DRM_DEBUG_KMS("%d\n", radeon_crtc->crtc_id); + + WREG32(NI_INPUT_CSC_CONTROL + radeon_crtc->crtc_offset, + (NI_INPUT_CSC_GRPH_MODE(NI_INPUT_CSC_BYPASS) | + NI_INPUT_CSC_OVL_MODE(NI_INPUT_CSC_BYPASS))); + WREG32(NI_PRESCALE_GRPH_CONTROL + radeon_crtc->crtc_offset, + NI_GRPH_PRESCALE_BYPASS); + WREG32(NI_PRESCALE_OVL_CONTROL + radeon_crtc->crtc_offset, + NI_OVL_PRESCALE_BYPASS); + WREG32(NI_INPUT_GAMMA_CONTROL + radeon_crtc->crtc_offset, + (NI_GRPH_INPUT_GAMMA_MODE(NI_INPUT_GAMMA_USE_LUT) | + NI_OVL_INPUT_GAMMA_MODE(NI_INPUT_GAMMA_USE_LUT))); + + WREG32(EVERGREEN_DC_LUT_CONTROL + radeon_crtc->crtc_offset, 0); + + WREG32(EVERGREEN_DC_LUT_BLACK_OFFSET_BLUE + radeon_crtc->crtc_offset, 0); + WREG32(EVERGREEN_DC_LUT_BLACK_OFFSET_GREEN + radeon_crtc->crtc_offset, 0); + WREG32(EVERGREEN_DC_LUT_BLACK_OFFSET_RED + radeon_crtc->crtc_offset, 0); + + WREG32(EVERGREEN_DC_LUT_WHITE_OFFSET_BLUE + radeon_crtc->crtc_offset, 0xffff); + WREG32(EVERGREEN_DC_LUT_WHITE_OFFSET_GREEN + radeon_crtc->crtc_offset, 0xffff); + WREG32(EVERGREEN_DC_LUT_WHITE_OFFSET_RED + radeon_crtc->crtc_offset, 0xffff); + + WREG32(EVERGREEN_DC_LUT_RW_MODE + radeon_crtc->crtc_offset, 0); + WREG32(EVERGREEN_DC_LUT_WRITE_EN_MASK + radeon_crtc->crtc_offset, 0x00000007); + + WREG32(EVERGREEN_DC_LUT_RW_INDEX + radeon_crtc->crtc_offset, 0); + for (i = 0; i < 256; i++) { + WREG32(EVERGREEN_DC_LUT_30_COLOR + radeon_crtc->crtc_offset, + (radeon_crtc->lut_r[i] << 20) | + (radeon_crtc->lut_g[i] << 10) | + (radeon_crtc->lut_b[i] << 0)); + } + + WREG32(NI_DEGAMMA_CONTROL + radeon_crtc->crtc_offset, + (NI_GRPH_DEGAMMA_MODE(NI_DEGAMMA_BYPASS) | + NI_OVL_DEGAMMA_MODE(NI_DEGAMMA_BYPASS) | + NI_ICON_DEGAMMA_MODE(NI_DEGAMMA_BYPASS) | + NI_CURSOR_DEGAMMA_MODE(NI_DEGAMMA_BYPASS))); + WREG32(NI_GAMUT_REMAP_CONTROL + radeon_crtc->crtc_offset, + (NI_GRPH_GAMUT_REMAP_MODE(NI_GAMUT_REMAP_BYPASS) | + NI_OVL_GAMUT_REMAP_MODE(NI_GAMUT_REMAP_BYPASS))); + WREG32(NI_REGAMMA_CONTROL + radeon_crtc->crtc_offset, + (NI_GRPH_REGAMMA_MODE(NI_REGAMMA_BYPASS) | + NI_OVL_REGAMMA_MODE(NI_REGAMMA_BYPASS))); + WREG32(NI_OUTPUT_CSC_CONTROL + radeon_crtc->crtc_offset, + (NI_OUTPUT_CSC_GRPH_MODE(NI_OUTPUT_CSC_BYPASS) | + NI_OUTPUT_CSC_OVL_MODE(NI_OUTPUT_CSC_BYPASS))); + /* XXX match this to the depth of the crtc fmt block, move to modeset? */ + WREG32(0x6940 + radeon_crtc->crtc_offset, 0); + +} + +static void legacy_crtc_load_lut(struct drm_crtc *crtc) +{ + struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); + struct drm_device *dev = crtc->dev; + struct radeon_device *rdev = dev->dev_private; + int i; + uint32_t dac2_cntl; + + dac2_cntl = RREG32(RADEON_DAC_CNTL2); + if (radeon_crtc->crtc_id == 0) + dac2_cntl &= (uint32_t)~RADEON_DAC2_PALETTE_ACC_CTL; + else + dac2_cntl |= RADEON_DAC2_PALETTE_ACC_CTL; + WREG32(RADEON_DAC_CNTL2, dac2_cntl); + + WREG8(RADEON_PALETTE_INDEX, 0); + for (i = 0; i < 256; i++) { + WREG32(RADEON_PALETTE_30_DATA, + (radeon_crtc->lut_r[i] << 20) | + (radeon_crtc->lut_g[i] << 10) | + (radeon_crtc->lut_b[i] << 0)); + } +} + +void radeon_crtc_load_lut(struct drm_crtc *crtc) +{ + struct drm_device *dev = crtc->dev; + struct radeon_device *rdev = dev->dev_private; + + if (!crtc->enabled) + return; + + if (ASIC_IS_DCE5(rdev)) + dce5_crtc_load_lut(crtc); + else if (ASIC_IS_DCE4(rdev)) + dce4_crtc_load_lut(crtc); + else if (ASIC_IS_AVIVO(rdev)) + avivo_crtc_load_lut(crtc); + else + legacy_crtc_load_lut(crtc); +} + +/** Sets the color ramps on behalf of fbcon */ +void radeon_crtc_fb_gamma_set(struct drm_crtc *crtc, u16 red, u16 green, + u16 blue, int regno) +{ + struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); + + radeon_crtc->lut_r[regno] = red >> 6; + radeon_crtc->lut_g[regno] = green >> 6; + radeon_crtc->lut_b[regno] = blue >> 6; +} + +/** Gets the color ramps on behalf of fbcon */ +void radeon_crtc_fb_gamma_get(struct drm_crtc *crtc, u16 *red, u16 *green, + u16 *blue, int regno) +{ + struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); + + *red = radeon_crtc->lut_r[regno] << 6; + *green = radeon_crtc->lut_g[regno] << 6; + *blue = radeon_crtc->lut_b[regno] << 6; +} + +static void radeon_crtc_gamma_set(struct drm_crtc *crtc, u16 *red, u16 *green, + u16 *blue, uint32_t start, uint32_t size) +{ + struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); + int end = (start + size > 256) ? 256 : start + size, i; + + /* userspace palettes are always correct as is */ + for (i = start; i < end; i++) { + radeon_crtc->lut_r[i] = red[i] >> 6; + radeon_crtc->lut_g[i] = green[i] >> 6; + radeon_crtc->lut_b[i] = blue[i] >> 6; + } + radeon_crtc_load_lut(crtc); +} + +static void radeon_crtc_destroy(struct drm_crtc *crtc) +{ + struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); + + drm_crtc_cleanup(crtc); + free(radeon_crtc, DRM_MEM_DRIVER); +} + +/* + * Handle unpin events outside the interrupt handler proper. + */ +static void radeon_unpin_work_func(void *arg, int pending) +{ + struct radeon_unpin_work *work = arg; + int r; + + /* unpin of the old buffer */ + r = radeon_bo_reserve(work->old_rbo, false); + if (likely(r == 0)) { + r = radeon_bo_unpin(work->old_rbo); + if (unlikely(r != 0)) { + DRM_ERROR("failed to unpin buffer after flip\n"); + } + radeon_bo_unreserve(work->old_rbo); + } else + DRM_ERROR("failed to reserve buffer after flip\n"); + + drm_gem_object_unreference_unlocked(&work->old_rbo->gem_base); + free(work, DRM_MEM_DRIVER); +} + +void radeon_crtc_handle_flip(struct radeon_device *rdev, int crtc_id) +{ + struct radeon_crtc *radeon_crtc = rdev->mode_info.crtcs[crtc_id]; + struct radeon_unpin_work *work; + struct drm_pending_vblank_event *e; + struct timeval now; + unsigned long flags; + u32 update_pending; + int vpos, hpos; + + DRM_SPINLOCK_IRQSAVE(&rdev->ddev->event_lock, flags); + work = radeon_crtc->unpin_work; + if (work == NULL || + (work->fence && !radeon_fence_signaled(work->fence))) { + DRM_SPINUNLOCK_IRQRESTORE(&rdev->ddev->event_lock, flags); + return; + } + /* New pageflip, or just completion of a previous one? */ + if (!radeon_crtc->deferred_flip_completion) { + /* do the flip (mmio) */ + update_pending = radeon_page_flip(rdev, crtc_id, work->new_crtc_base); + } else { + /* This is just a completion of a flip queued in crtc + * at last invocation. Make sure we go directly to + * completion routine. + */ + update_pending = 0; + radeon_crtc->deferred_flip_completion = 0; + } + + /* Has the pageflip already completed in crtc, or is it certain + * to complete in this vblank? + */ + if (update_pending && + (DRM_SCANOUTPOS_VALID & radeon_get_crtc_scanoutpos(rdev->ddev, crtc_id, + &vpos, &hpos)) && + ((vpos >= (99 * rdev->mode_info.crtcs[crtc_id]->base.hwmode.crtc_vdisplay)/100) || + (vpos < 0 && !ASIC_IS_AVIVO(rdev)))) { + /* crtc didn't flip in this target vblank interval, + * but flip is pending in crtc. Based on the current + * scanout position we know that the current frame is + * (nearly) complete and the flip will (likely) + * complete before the start of the next frame. + */ + update_pending = 0; + } + if (update_pending) { + /* crtc didn't flip in this target vblank interval, + * but flip is pending in crtc. It will complete it + * in next vblank interval, so complete the flip at + * next vblank irq. + */ + radeon_crtc->deferred_flip_completion = 1; + DRM_SPINUNLOCK_IRQRESTORE(&rdev->ddev->event_lock, flags); + return; + } + + /* Pageflip (will be) certainly completed in this vblank. Clean up. */ + radeon_crtc->unpin_work = NULL; + + /* wakeup userspace */ + if (work->event) { + e = work->event; + e->event.sequence = drm_vblank_count_and_time(rdev->ddev, crtc_id, &now); + e->event.tv_sec = now.tv_sec; + e->event.tv_usec = now.tv_usec; + list_add_tail(&e->base.link, &e->base.file_priv->event_list); +#ifdef DUMBBELL_WIP + wake_up_interruptible(&e->base.file_priv->event_wait); +#endif /* DUMBBELL_WIP */ + } + DRM_SPINUNLOCK_IRQRESTORE(&rdev->ddev->event_lock, flags); + + drm_vblank_put(rdev->ddev, radeon_crtc->crtc_id); + radeon_fence_unref(&work->fence); + radeon_post_page_flip(work->rdev, work->crtc_id); + taskqueue_enqueue(rdev->tq, &work->work); +} + +static int radeon_crtc_page_flip(struct drm_crtc *crtc, + struct drm_framebuffer *fb, + struct drm_pending_vblank_event *event) +{ + struct drm_device *dev = crtc->dev; + struct radeon_device *rdev = dev->dev_private; + struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); + struct radeon_framebuffer *old_radeon_fb; + struct radeon_framebuffer *new_radeon_fb; + struct drm_gem_object *obj; + struct radeon_bo *rbo; + struct radeon_unpin_work *work; + unsigned long flags; + u32 tiling_flags, pitch_pixels; + u64 base; + int r; + + work = malloc(sizeof *work, DRM_MEM_DRIVER, M_WAITOK | M_ZERO); + if (work == NULL) + return -ENOMEM; + + work->event = event; + work->rdev = rdev; + work->crtc_id = radeon_crtc->crtc_id; + old_radeon_fb = to_radeon_framebuffer(crtc->fb); + new_radeon_fb = to_radeon_framebuffer(fb); + /* schedule unpin of the old buffer */ + obj = old_radeon_fb->obj; + /* take a reference to the old object */ + drm_gem_object_reference(obj); + rbo = gem_to_radeon_bo(obj); + work->old_rbo = rbo; + obj = new_radeon_fb->obj; + rbo = gem_to_radeon_bo(obj); + + mtx_lock(&rbo->tbo.bdev->fence_lock); + if (rbo->tbo.sync_obj) + work->fence = radeon_fence_ref(rbo->tbo.sync_obj); + mtx_unlock(&rbo->tbo.bdev->fence_lock); + + TASK_INIT(&work->work, 0, radeon_unpin_work_func, work); + + /* We borrow the event spin lock for protecting unpin_work */ + DRM_SPINLOCK_IRQSAVE(&dev->event_lock, flags); + if (radeon_crtc->unpin_work) { + DRM_DEBUG_DRIVER("flip queue: crtc already busy\n"); + r = -EBUSY; + goto unlock_free; + } + radeon_crtc->unpin_work = work; + radeon_crtc->deferred_flip_completion = 0; + DRM_SPINUNLOCK_IRQRESTORE(&dev->event_lock, flags); + + /* pin the new buffer */ + DRM_DEBUG_DRIVER("flip-ioctl() cur_fbo = %p, cur_bbo = %p\n", + work->old_rbo, rbo); + + r = radeon_bo_reserve(rbo, false); + if (unlikely(r != 0)) { + DRM_ERROR("failed to reserve new rbo buffer before flip\n"); + goto pflip_cleanup; + } + /* Only 27 bit offset for legacy CRTC */ + r = radeon_bo_pin_restricted(rbo, RADEON_GEM_DOMAIN_VRAM, + ASIC_IS_AVIVO(rdev) ? 0 : 1 << 27, &base); + if (unlikely(r != 0)) { + radeon_bo_unreserve(rbo); + r = -EINVAL; + DRM_ERROR("failed to pin new rbo buffer before flip\n"); + goto pflip_cleanup; + } + radeon_bo_get_tiling_flags(rbo, &tiling_flags, NULL); + radeon_bo_unreserve(rbo); + + if (!ASIC_IS_AVIVO(rdev)) { + /* crtc offset is from display base addr not FB location */ + base -= radeon_crtc->legacy_display_base_addr; + pitch_pixels = fb->pitches[0] / (fb->bits_per_pixel / 8); + + if (tiling_flags & RADEON_TILING_MACRO) { + if (ASIC_IS_R300(rdev)) { + base &= ~0x7ff; + } else { + int byteshift = fb->bits_per_pixel >> 4; + int tile_addr = (((crtc->y >> 3) * pitch_pixels + crtc->x) >> (8 - byteshift)) << 11; + base += tile_addr + ((crtc->x << byteshift) % 256) + ((crtc->y % 8) << 8); + } + } else { + int offset = crtc->y * pitch_pixels + crtc->x; + switch (fb->bits_per_pixel) { + case 8: + default: + offset *= 1; + break; + case 15: + case 16: + offset *= 2; + break; + case 24: + offset *= 3; + break; + case 32: + offset *= 4; + break; + } + base += offset; + } + base &= ~7; + } + + DRM_SPINLOCK_IRQSAVE(&dev->event_lock, flags); + work->new_crtc_base = base; + DRM_SPINUNLOCK_IRQRESTORE(&dev->event_lock, flags); + + /* update crtc fb */ + crtc->fb = fb; + + r = drm_vblank_get(dev, radeon_crtc->crtc_id); + if (r) { + DRM_ERROR("failed to get vblank before flip\n"); + goto pflip_cleanup1; + } + + /* set the proper interrupt */ + radeon_pre_page_flip(rdev, radeon_crtc->crtc_id); + + return 0; + +pflip_cleanup1: + if (unlikely(radeon_bo_reserve(rbo, false) != 0)) { + DRM_ERROR("failed to reserve new rbo in error path\n"); + goto pflip_cleanup; + } + if (unlikely(radeon_bo_unpin(rbo) != 0)) { + DRM_ERROR("failed to unpin new rbo in error path\n"); + } + radeon_bo_unreserve(rbo); + +pflip_cleanup: + DRM_SPINLOCK_IRQSAVE(&dev->event_lock, flags); + radeon_crtc->unpin_work = NULL; +unlock_free: + DRM_SPINUNLOCK_IRQRESTORE(&dev->event_lock, flags); + drm_gem_object_unreference_unlocked(old_radeon_fb->obj); + radeon_fence_unref(&work->fence); + free(work, DRM_MEM_DRIVER); + + return r; +} + +static const struct drm_crtc_funcs radeon_crtc_funcs = { + .cursor_set = radeon_crtc_cursor_set, + .cursor_move = radeon_crtc_cursor_move, + .gamma_set = radeon_crtc_gamma_set, + .set_config = drm_crtc_helper_set_config, + .destroy = radeon_crtc_destroy, + .page_flip = radeon_crtc_page_flip, +}; + +static void radeon_crtc_init(struct drm_device *dev, int index) +{ + struct radeon_device *rdev = dev->dev_private; + struct radeon_crtc *radeon_crtc; + int i; + + radeon_crtc = malloc(sizeof(struct radeon_crtc) + (RADEONFB_CONN_LIMIT * sizeof(struct drm_connector *)), DRM_MEM_DRIVER, M_WAITOK | M_ZERO); + if (radeon_crtc == NULL) + return; + + drm_crtc_init(dev, &radeon_crtc->base, &radeon_crtc_funcs); + + drm_mode_crtc_set_gamma_size(&radeon_crtc->base, 256); + radeon_crtc->crtc_id = index; + rdev->mode_info.crtcs[index] = radeon_crtc; + +#if 0 + radeon_crtc->mode_set.crtc = &radeon_crtc->base; + radeon_crtc->mode_set.connectors = (struct drm_connector **)(radeon_crtc + 1); + radeon_crtc->mode_set.num_connectors = 0; +#endif + + for (i = 0; i < 256; i++) { + radeon_crtc->lut_r[i] = i << 2; + radeon_crtc->lut_g[i] = i << 2; + radeon_crtc->lut_b[i] = i << 2; + } + + if (rdev->is_atom_bios && (ASIC_IS_AVIVO(rdev) || radeon_r4xx_atom)) + radeon_atombios_init_crtc(dev, radeon_crtc); + else + radeon_legacy_init_crtc(dev, radeon_crtc); +} + +static const char *encoder_names[37] = { + "NONE", + "INTERNAL_LVDS", + "INTERNAL_TMDS1", + "INTERNAL_TMDS2", + "INTERNAL_DAC1", + "INTERNAL_DAC2", + "INTERNAL_SDVOA", + "INTERNAL_SDVOB", + "SI170B", + "CH7303", + "CH7301", + "INTERNAL_DVO1", + "EXTERNAL_SDVOA", + "EXTERNAL_SDVOB", + "TITFP513", + "INTERNAL_LVTM1", + "VT1623", + "HDMI_SI1930", + "HDMI_INTERNAL", + "INTERNAL_KLDSCP_TMDS1", + "INTERNAL_KLDSCP_DVO1", + "INTERNAL_KLDSCP_DAC1", + "INTERNAL_KLDSCP_DAC2", + "SI178", + "MVPU_FPGA", + "INTERNAL_DDI", + "VT1625", + "HDMI_SI1932", + "DP_AN9801", + "DP_DP501", + "INTERNAL_UNIPHY", + "INTERNAL_KLDSCP_LVTMA", + "INTERNAL_UNIPHY1", + "INTERNAL_UNIPHY2", + "NUTMEG", + "TRAVIS", + "INTERNAL_VCE" +}; + +static const char *hpd_names[6] = { + "HPD1", + "HPD2", + "HPD3", + "HPD4", + "HPD5", + "HPD6", +}; + +static void radeon_print_display_setup(struct drm_device *dev) +{ + struct drm_connector *connector; + struct radeon_connector *radeon_connector; + struct drm_encoder *encoder; + struct radeon_encoder *radeon_encoder; + uint32_t devices; + int i = 0; + + DRM_INFO("Radeon Display Connectors\n"); + list_for_each_entry(connector, &dev->mode_config.connector_list, head) { + radeon_connector = to_radeon_connector(connector); + DRM_INFO("Connector %d:\n", i); + DRM_INFO(" %s\n", drm_get_connector_name(connector)); + if (radeon_connector->hpd.hpd != RADEON_HPD_NONE) + DRM_INFO(" %s\n", hpd_names[radeon_connector->hpd.hpd]); + if (radeon_connector->ddc_bus) { + DRM_INFO(" DDC: 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x\n", + radeon_connector->ddc_bus->rec.mask_clk_reg, + radeon_connector->ddc_bus->rec.mask_data_reg, + radeon_connector->ddc_bus->rec.a_clk_reg, + radeon_connector->ddc_bus->rec.a_data_reg, + radeon_connector->ddc_bus->rec.en_clk_reg, + radeon_connector->ddc_bus->rec.en_data_reg, + radeon_connector->ddc_bus->rec.y_clk_reg, + radeon_connector->ddc_bus->rec.y_data_reg); + if (radeon_connector->router.ddc_valid) + DRM_INFO(" DDC Router 0x%x/0x%x\n", + radeon_connector->router.ddc_mux_control_pin, + radeon_connector->router.ddc_mux_state); + if (radeon_connector->router.cd_valid) + DRM_INFO(" Clock/Data Router 0x%x/0x%x\n", + radeon_connector->router.cd_mux_control_pin, + radeon_connector->router.cd_mux_state); + } else { + if (connector->connector_type == DRM_MODE_CONNECTOR_VGA || + connector->connector_type == DRM_MODE_CONNECTOR_DVII || + connector->connector_type == DRM_MODE_CONNECTOR_DVID || + connector->connector_type == DRM_MODE_CONNECTOR_DVIA || + connector->connector_type == DRM_MODE_CONNECTOR_HDMIA || + connector->connector_type == DRM_MODE_CONNECTOR_HDMIB) + DRM_INFO(" DDC: no ddc bus - possible BIOS bug - please report to xorg-driver-ati@lists.x.org\n"); + } + DRM_INFO(" Encoders:\n"); + list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { + radeon_encoder = to_radeon_encoder(encoder); + devices = radeon_encoder->devices & radeon_connector->devices; + if (devices) { + if (devices & ATOM_DEVICE_CRT1_SUPPORT) + DRM_INFO(" CRT1: %s\n", encoder_names[radeon_encoder->encoder_id]); + if (devices & ATOM_DEVICE_CRT2_SUPPORT) + DRM_INFO(" CRT2: %s\n", encoder_names[radeon_encoder->encoder_id]); + if (devices & ATOM_DEVICE_LCD1_SUPPORT) + DRM_INFO(" LCD1: %s\n", encoder_names[radeon_encoder->encoder_id]); + if (devices & ATOM_DEVICE_DFP1_SUPPORT) + DRM_INFO(" DFP1: %s\n", encoder_names[radeon_encoder->encoder_id]); + if (devices & ATOM_DEVICE_DFP2_SUPPORT) + DRM_INFO(" DFP2: %s\n", encoder_names[radeon_encoder->encoder_id]); + if (devices & ATOM_DEVICE_DFP3_SUPPORT) + DRM_INFO(" DFP3: %s\n", encoder_names[radeon_encoder->encoder_id]); + if (devices & ATOM_DEVICE_DFP4_SUPPORT) + DRM_INFO(" DFP4: %s\n", encoder_names[radeon_encoder->encoder_id]); + if (devices & ATOM_DEVICE_DFP5_SUPPORT) + DRM_INFO(" DFP5: %s\n", encoder_names[radeon_encoder->encoder_id]); + if (devices & ATOM_DEVICE_DFP6_SUPPORT) + DRM_INFO(" DFP6: %s\n", encoder_names[radeon_encoder->encoder_id]); + if (devices & ATOM_DEVICE_TV1_SUPPORT) + DRM_INFO(" TV1: %s\n", encoder_names[radeon_encoder->encoder_id]); + if (devices & ATOM_DEVICE_CV_SUPPORT) + DRM_INFO(" CV: %s\n", encoder_names[radeon_encoder->encoder_id]); + } + } + i++; + } +} + +static bool radeon_setup_enc_conn(struct drm_device *dev) +{ + struct radeon_device *rdev = dev->dev_private; + bool ret = false; + + if (rdev->bios) { + if (rdev->is_atom_bios) { + ret = radeon_get_atom_connector_info_from_supported_devices_table(dev); + if (ret == false) + ret = radeon_get_atom_connector_info_from_object_table(dev); + } else { + ret = radeon_get_legacy_connector_info_from_bios(dev); + if (ret == false) + ret = radeon_get_legacy_connector_info_from_table(dev); + } + } else { + if (!ASIC_IS_AVIVO(rdev)) + ret = radeon_get_legacy_connector_info_from_table(dev); + } + if (ret) { + radeon_setup_encoder_clones(dev); + radeon_print_display_setup(dev); + } + + return ret; +} + +int radeon_ddc_get_modes(struct radeon_connector *radeon_connector) +{ + struct drm_device *dev = radeon_connector->base.dev; + struct radeon_device *rdev = dev->dev_private; + int ret = 0; + + /* on hw with routers, select right port */ + if (radeon_connector->router.ddc_valid) + radeon_router_select_ddc_port(radeon_connector); + + if (radeon_connector_encoder_get_dp_bridge_encoder_id(&radeon_connector->base) != + ENCODER_OBJECT_ID_NONE) { + struct radeon_connector_atom_dig *dig = radeon_connector->con_priv; + + if (dig->dp_i2c_bus) + radeon_connector->edid = drm_get_edid(&radeon_connector->base, + dig->dp_i2c_bus->adapter); + } else if ((radeon_connector->base.connector_type == DRM_MODE_CONNECTOR_DisplayPort) || + (radeon_connector->base.connector_type == DRM_MODE_CONNECTOR_eDP)) { + struct radeon_connector_atom_dig *dig = radeon_connector->con_priv; + + if ((dig->dp_sink_type == CONNECTOR_OBJECT_ID_DISPLAYPORT || + dig->dp_sink_type == CONNECTOR_OBJECT_ID_eDP) && dig->dp_i2c_bus) + radeon_connector->edid = drm_get_edid(&radeon_connector->base, + dig->dp_i2c_bus->adapter); + else if (radeon_connector->ddc_bus && !radeon_connector->edid) + radeon_connector->edid = drm_get_edid(&radeon_connector->base, + radeon_connector->ddc_bus->adapter); + } else { + if (radeon_connector->ddc_bus && !radeon_connector->edid) + radeon_connector->edid = drm_get_edid(&radeon_connector->base, + radeon_connector->ddc_bus->adapter); + } + + if (!radeon_connector->edid) { + if (rdev->is_atom_bios) { + /* some laptops provide a hardcoded edid in rom for LCDs */ + if (((radeon_connector->base.connector_type == DRM_MODE_CONNECTOR_LVDS) || + (radeon_connector->base.connector_type == DRM_MODE_CONNECTOR_eDP))) + radeon_connector->edid = radeon_bios_get_hardcoded_edid(rdev); + } else + /* some servers provide a hardcoded edid in rom for KVMs */ + radeon_connector->edid = radeon_bios_get_hardcoded_edid(rdev); + } + if (radeon_connector->edid) { + drm_mode_connector_update_edid_property(&radeon_connector->base, radeon_connector->edid); + ret = drm_add_edid_modes(&radeon_connector->base, radeon_connector->edid); + return ret; + } + drm_mode_connector_update_edid_property(&radeon_connector->base, NULL); + return 0; +} + +/* avivo */ +static void avivo_get_fb_div(struct radeon_pll *pll, + u32 target_clock, + u32 post_div, + u32 ref_div, + u32 *fb_div, + u32 *frac_fb_div) +{ + u32 tmp = post_div * ref_div; + + tmp *= target_clock; + *fb_div = tmp / pll->reference_freq; + *frac_fb_div = tmp % pll->reference_freq; + + if (*fb_div > pll->max_feedback_div) + *fb_div = pll->max_feedback_div; + else if (*fb_div < pll->min_feedback_div) + *fb_div = pll->min_feedback_div; +} + +static u32 avivo_get_post_div(struct radeon_pll *pll, + u32 target_clock) +{ + u32 vco, post_div, tmp; + + if (pll->flags & RADEON_PLL_USE_POST_DIV) + return pll->post_div; + + if (pll->flags & RADEON_PLL_PREFER_MINM_OVER_MAXP) { + if (pll->flags & RADEON_PLL_IS_LCD) + vco = pll->lcd_pll_out_min; + else + vco = pll->pll_out_min; + } else { + if (pll->flags & RADEON_PLL_IS_LCD) + vco = pll->lcd_pll_out_max; + else + vco = pll->pll_out_max; + } + + post_div = vco / target_clock; + tmp = vco % target_clock; + + if (pll->flags & RADEON_PLL_PREFER_MINM_OVER_MAXP) { + if (tmp) + post_div++; + } else { + if (!tmp) + post_div--; + } + + if (post_div > pll->max_post_div) + post_div = pll->max_post_div; + else if (post_div < pll->min_post_div) + post_div = pll->min_post_div; + + return post_div; +} + +#define MAX_TOLERANCE 10 + +void radeon_compute_pll_avivo(struct radeon_pll *pll, + u32 freq, + u32 *dot_clock_p, + u32 *fb_div_p, + u32 *frac_fb_div_p, + u32 *ref_div_p, + u32 *post_div_p) +{ + u32 target_clock = freq / 10; + u32 post_div = avivo_get_post_div(pll, target_clock); + u32 ref_div = pll->min_ref_div; + u32 fb_div = 0, frac_fb_div = 0, tmp; + + if (pll->flags & RADEON_PLL_USE_REF_DIV) + ref_div = pll->reference_div; + + if (pll->flags & RADEON_PLL_USE_FRAC_FB_DIV) { + avivo_get_fb_div(pll, target_clock, post_div, ref_div, &fb_div, &frac_fb_div); + frac_fb_div = (100 * frac_fb_div) / pll->reference_freq; + if (frac_fb_div >= 5) { + frac_fb_div -= 5; + frac_fb_div = frac_fb_div / 10; + frac_fb_div++; + } + if (frac_fb_div >= 10) { + fb_div++; + frac_fb_div = 0; + } + } else { + while (ref_div <= pll->max_ref_div) { + avivo_get_fb_div(pll, target_clock, post_div, ref_div, + &fb_div, &frac_fb_div); + if (frac_fb_div >= (pll->reference_freq / 2)) + fb_div++; + frac_fb_div = 0; + tmp = (pll->reference_freq * fb_div) / (post_div * ref_div); + tmp = (tmp * 10000) / target_clock; + + if (tmp > (10000 + MAX_TOLERANCE)) + ref_div++; + else if (tmp >= (10000 - MAX_TOLERANCE)) + break; + else + ref_div++; + } + } + + *dot_clock_p = ((pll->reference_freq * fb_div * 10) + (pll->reference_freq * frac_fb_div)) / + (ref_div * post_div * 10); + *fb_div_p = fb_div; + *frac_fb_div_p = frac_fb_div; + *ref_div_p = ref_div; + *post_div_p = post_div; + DRM_DEBUG_KMS("%d, pll dividers - fb: %d.%d ref: %d, post %d\n", + *dot_clock_p, fb_div, frac_fb_div, ref_div, post_div); +} + +/* pre-avivo */ +static inline uint32_t radeon_div(uint64_t n, uint32_t d) +{ + uint64_t mod; + + n += d / 2; + + mod = do_div(n, d); + return n; +} + +void radeon_compute_pll_legacy(struct radeon_pll *pll, + uint64_t freq, + uint32_t *dot_clock_p, + uint32_t *fb_div_p, + uint32_t *frac_fb_div_p, + uint32_t *ref_div_p, + uint32_t *post_div_p) +{ + uint32_t min_ref_div = pll->min_ref_div; + uint32_t max_ref_div = pll->max_ref_div; + uint32_t min_post_div = pll->min_post_div; + uint32_t max_post_div = pll->max_post_div; + uint32_t min_fractional_feed_div = 0; + uint32_t max_fractional_feed_div = 0; + uint32_t best_vco = pll->best_vco; + uint32_t best_post_div = 1; + uint32_t best_ref_div = 1; + uint32_t best_feedback_div = 1; + uint32_t best_frac_feedback_div = 0; + uint32_t best_freq = -1; + uint32_t best_error = 0xffffffff; + uint32_t best_vco_diff = 1; + uint32_t post_div; + u32 pll_out_min, pll_out_max; + + DRM_DEBUG_KMS("PLL freq %ju %u %u\n", (uintmax_t)freq, pll->min_ref_div, pll->max_ref_div); + freq = freq * 1000; + + if (pll->flags & RADEON_PLL_IS_LCD) { + pll_out_min = pll->lcd_pll_out_min; + pll_out_max = pll->lcd_pll_out_max; + } else { + pll_out_min = pll->pll_out_min; + pll_out_max = pll->pll_out_max; + } + + if (pll_out_min > 64800) + pll_out_min = 64800; + + if (pll->flags & RADEON_PLL_USE_REF_DIV) + min_ref_div = max_ref_div = pll->reference_div; + else { + while (min_ref_div < max_ref_div-1) { + uint32_t mid = (min_ref_div + max_ref_div) / 2; + uint32_t pll_in = pll->reference_freq / mid; + if (pll_in < pll->pll_in_min) + max_ref_div = mid; + else if (pll_in > pll->pll_in_max) + min_ref_div = mid; + else + break; + } + } + + if (pll->flags & RADEON_PLL_USE_POST_DIV) + min_post_div = max_post_div = pll->post_div; + + if (pll->flags & RADEON_PLL_USE_FRAC_FB_DIV) { + min_fractional_feed_div = pll->min_frac_feedback_div; + max_fractional_feed_div = pll->max_frac_feedback_div; + } + + for (post_div = max_post_div; post_div >= min_post_div; --post_div) { + uint32_t ref_div; + + if ((pll->flags & RADEON_PLL_NO_ODD_POST_DIV) && (post_div & 1)) + continue; + + /* legacy radeons only have a few post_divs */ + if (pll->flags & RADEON_PLL_LEGACY) { + if ((post_div == 5) || + (post_div == 7) || + (post_div == 9) || + (post_div == 10) || + (post_div == 11) || + (post_div == 13) || + (post_div == 14) || + (post_div == 15)) + continue; + } + + for (ref_div = min_ref_div; ref_div <= max_ref_div; ++ref_div) { + uint32_t feedback_div, current_freq = 0, error, vco_diff; + uint32_t pll_in = pll->reference_freq / ref_div; + uint32_t min_feed_div = pll->min_feedback_div; + uint32_t max_feed_div = pll->max_feedback_div + 1; + + if (pll_in < pll->pll_in_min || pll_in > pll->pll_in_max) + continue; + + while (min_feed_div < max_feed_div) { + uint32_t vco; + uint32_t min_frac_feed_div = min_fractional_feed_div; + uint32_t max_frac_feed_div = max_fractional_feed_div + 1; + uint32_t frac_feedback_div; + uint64_t tmp; + + feedback_div = (min_feed_div + max_feed_div) / 2; + + tmp = (uint64_t)pll->reference_freq * feedback_div; + vco = radeon_div(tmp, ref_div); + + if (vco < pll_out_min) { + min_feed_div = feedback_div + 1; + continue; + } else if (vco > pll_out_max) { + max_feed_div = feedback_div; + continue; + } + + while (min_frac_feed_div < max_frac_feed_div) { + frac_feedback_div = (min_frac_feed_div + max_frac_feed_div) / 2; + tmp = (uint64_t)pll->reference_freq * 10000 * feedback_div; + tmp += (uint64_t)pll->reference_freq * 1000 * frac_feedback_div; + current_freq = radeon_div(tmp, ref_div * post_div); + + if (pll->flags & RADEON_PLL_PREFER_CLOSEST_LOWER) { + if (freq < current_freq) + error = 0xffffffff; + else + error = freq - current_freq; + } else + error = abs(current_freq - freq); + vco_diff = abs(vco - best_vco); + + if ((best_vco == 0 && error < best_error) || + (best_vco != 0 && + ((best_error > 100 && error < best_error - 100) || + (abs(error - best_error) < 100 && vco_diff < best_vco_diff)))) { + best_post_div = post_div; + best_ref_div = ref_div; + best_feedback_div = feedback_div; + best_frac_feedback_div = frac_feedback_div; + best_freq = current_freq; + best_error = error; + best_vco_diff = vco_diff; + } else if (current_freq == freq) { + if (best_freq == -1) { + best_post_div = post_div; + best_ref_div = ref_div; + best_feedback_div = feedback_div; + best_frac_feedback_div = frac_feedback_div; + best_freq = current_freq; + best_error = error; + best_vco_diff = vco_diff; + } else if (((pll->flags & RADEON_PLL_PREFER_LOW_REF_DIV) && (ref_div < best_ref_div)) || + ((pll->flags & RADEON_PLL_PREFER_HIGH_REF_DIV) && (ref_div > best_ref_div)) || + ((pll->flags & RADEON_PLL_PREFER_LOW_FB_DIV) && (feedback_div < best_feedback_div)) || + ((pll->flags & RADEON_PLL_PREFER_HIGH_FB_DIV) && (feedback_div > best_feedback_div)) || + ((pll->flags & RADEON_PLL_PREFER_LOW_POST_DIV) && (post_div < best_post_div)) || + ((pll->flags & RADEON_PLL_PREFER_HIGH_POST_DIV) && (post_div > best_post_div))) { + best_post_div = post_div; + best_ref_div = ref_div; + best_feedback_div = feedback_div; + best_frac_feedback_div = frac_feedback_div; + best_freq = current_freq; + best_error = error; + best_vco_diff = vco_diff; + } + } + if (current_freq < freq) + min_frac_feed_div = frac_feedback_div + 1; + else + max_frac_feed_div = frac_feedback_div; + } + if (current_freq < freq) + min_feed_div = feedback_div + 1; + else + max_feed_div = feedback_div; + } + } + } + + *dot_clock_p = best_freq / 10000; + *fb_div_p = best_feedback_div; + *frac_fb_div_p = best_frac_feedback_div; + *ref_div_p = best_ref_div; + *post_div_p = best_post_div; + DRM_DEBUG_KMS("%lld %d, pll dividers - fb: %d.%d ref: %d, post %d\n", + (long long)freq, + best_freq / 1000, best_feedback_div, best_frac_feedback_div, + best_ref_div, best_post_div); + +} + +static void radeon_user_framebuffer_destroy(struct drm_framebuffer *fb) +{ + struct radeon_framebuffer *radeon_fb = to_radeon_framebuffer(fb); + + if (radeon_fb->obj) { + drm_gem_object_unreference_unlocked(radeon_fb->obj); + } + drm_framebuffer_cleanup(fb); + free(radeon_fb, DRM_MEM_DRIVER); +} + +static int radeon_user_framebuffer_create_handle(struct drm_framebuffer *fb, + struct drm_file *file_priv, + unsigned int *handle) +{ + struct radeon_framebuffer *radeon_fb = to_radeon_framebuffer(fb); + + return drm_gem_handle_create(file_priv, radeon_fb->obj, handle); +} + +static const struct drm_framebuffer_funcs radeon_fb_funcs = { + .destroy = radeon_user_framebuffer_destroy, + .create_handle = radeon_user_framebuffer_create_handle, +}; + +int +radeon_framebuffer_init(struct drm_device *dev, + struct radeon_framebuffer *rfb, + struct drm_mode_fb_cmd2 *mode_cmd, + struct drm_gem_object *obj) +{ + int ret; + rfb->obj = obj; + ret = drm_framebuffer_init(dev, &rfb->base, &radeon_fb_funcs); + if (ret) { + rfb->obj = NULL; + return ret; + } + drm_helper_mode_fill_fb_struct(&rfb->base, mode_cmd); + return 0; +} + +static int +radeon_user_framebuffer_create(struct drm_device *dev, + struct drm_file *file_priv, + struct drm_mode_fb_cmd2 *mode_cmd, + struct drm_framebuffer **res) +{ + struct drm_gem_object *obj; + struct radeon_framebuffer *radeon_fb; + int ret; + + obj = drm_gem_object_lookup(dev, file_priv, mode_cmd->handles[0]); + if (obj == NULL) { + dev_err(dev->device, "No GEM object associated to handle 0x%08X, " + "can't create framebuffer\n", mode_cmd->handles[0]); + return -ENOENT; + } + + radeon_fb = malloc(sizeof(*radeon_fb), DRM_MEM_DRIVER, M_WAITOK | M_ZERO); + if (radeon_fb == NULL) { + drm_gem_object_unreference_unlocked(obj); + return (-ENOMEM); + } + + ret = radeon_framebuffer_init(dev, radeon_fb, mode_cmd, obj); + if (ret) { + free(radeon_fb, DRM_MEM_DRIVER); + drm_gem_object_unreference_unlocked(obj); + return ret; + } + + *res = &radeon_fb->base; + return 0; +} + +static void radeon_output_poll_changed(struct drm_device *dev) +{ + struct radeon_device *rdev = dev->dev_private; + radeon_fb_output_poll_changed(rdev); +} + +static const struct drm_mode_config_funcs radeon_mode_funcs = { + .fb_create = radeon_user_framebuffer_create, + .output_poll_changed = radeon_output_poll_changed +}; + +static struct drm_prop_enum_list radeon_tmds_pll_enum_list[] = +{ { 0, "driver" }, + { 1, "bios" }, +}; + +static struct drm_prop_enum_list radeon_tv_std_enum_list[] = +{ { TV_STD_NTSC, "ntsc" }, + { TV_STD_PAL, "pal" }, + { TV_STD_PAL_M, "pal-m" }, + { TV_STD_PAL_60, "pal-60" }, + { TV_STD_NTSC_J, "ntsc-j" }, + { TV_STD_SCART_PAL, "scart-pal" }, + { TV_STD_PAL_CN, "pal-cn" }, + { TV_STD_SECAM, "secam" }, +}; + +static struct drm_prop_enum_list radeon_underscan_enum_list[] = +{ { UNDERSCAN_OFF, "off" }, + { UNDERSCAN_ON, "on" }, + { UNDERSCAN_AUTO, "auto" }, +}; + +static int radeon_modeset_create_props(struct radeon_device *rdev) +{ + int sz; + + if (rdev->is_atom_bios) { + rdev->mode_info.coherent_mode_property = + drm_property_create_range(rdev->ddev, 0 , "coherent", 0, 1); + if (!rdev->mode_info.coherent_mode_property) + return -ENOMEM; + } + + if (!ASIC_IS_AVIVO(rdev)) { + sz = DRM_ARRAY_SIZE(radeon_tmds_pll_enum_list); + rdev->mode_info.tmds_pll_property = + drm_property_create_enum(rdev->ddev, 0, + "tmds_pll", + radeon_tmds_pll_enum_list, sz); + } + + rdev->mode_info.load_detect_property = + drm_property_create_range(rdev->ddev, 0, "load detection", 0, 1); + if (!rdev->mode_info.load_detect_property) + return -ENOMEM; + + drm_mode_create_scaling_mode_property(rdev->ddev); + + sz = DRM_ARRAY_SIZE(radeon_tv_std_enum_list); + rdev->mode_info.tv_std_property = + drm_property_create_enum(rdev->ddev, 0, + "tv standard", + radeon_tv_std_enum_list, sz); + + sz = DRM_ARRAY_SIZE(radeon_underscan_enum_list); + rdev->mode_info.underscan_property = + drm_property_create_enum(rdev->ddev, 0, + "underscan", + radeon_underscan_enum_list, sz); + + rdev->mode_info.underscan_hborder_property = + drm_property_create_range(rdev->ddev, 0, + "underscan hborder", 0, 128); + if (!rdev->mode_info.underscan_hborder_property) + return -ENOMEM; + + rdev->mode_info.underscan_vborder_property = + drm_property_create_range(rdev->ddev, 0, + "underscan vborder", 0, 128); + if (!rdev->mode_info.underscan_vborder_property) + return -ENOMEM; + + return 0; +} + +void radeon_update_display_priority(struct radeon_device *rdev) +{ + /* adjustment options for the display watermarks */ + if ((radeon_disp_priority == 0) || (radeon_disp_priority > 2)) { + /* set display priority to high for r3xx, rv515 chips + * this avoids flickering due to underflow to the + * display controllers during heavy acceleration. + * Don't force high on rs4xx igp chips as it seems to + * affect the sound card. See kernel bug 15982. + */ + if ((ASIC_IS_R300(rdev) || (rdev->family == CHIP_RV515)) && + !(rdev->flags & RADEON_IS_IGP)) + rdev->disp_priority = 2; + else + rdev->disp_priority = 0; + } else + rdev->disp_priority = radeon_disp_priority; + +} + +/* + * Allocate hdmi structs and determine register offsets + */ +static void radeon_afmt_init(struct radeon_device *rdev) +{ + int i; + + for (i = 0; i < RADEON_MAX_AFMT_BLOCKS; i++) + rdev->mode_info.afmt[i] = NULL; + + if (ASIC_IS_DCE6(rdev)) { + /* todo */ + } else if (ASIC_IS_DCE4(rdev)) { + /* DCE4/5 has 6 audio blocks tied to DIG encoders */ + /* DCE4.1 has 2 audio blocks tied to DIG encoders */ + rdev->mode_info.afmt[0] = malloc(sizeof(struct radeon_afmt), + DRM_MEM_DRIVER, M_WAITOK | M_ZERO); + if (rdev->mode_info.afmt[0]) { + rdev->mode_info.afmt[0]->offset = EVERGREEN_CRTC0_REGISTER_OFFSET; + rdev->mode_info.afmt[0]->id = 0; + } + rdev->mode_info.afmt[1] = malloc(sizeof(struct radeon_afmt), + DRM_MEM_DRIVER, M_WAITOK | M_ZERO); + if (rdev->mode_info.afmt[1]) { + rdev->mode_info.afmt[1]->offset = EVERGREEN_CRTC1_REGISTER_OFFSET; + rdev->mode_info.afmt[1]->id = 1; + } + if (!ASIC_IS_DCE41(rdev)) { + rdev->mode_info.afmt[2] = malloc(sizeof(struct radeon_afmt), + DRM_MEM_DRIVER, M_WAITOK | M_ZERO); + if (rdev->mode_info.afmt[2]) { + rdev->mode_info.afmt[2]->offset = EVERGREEN_CRTC2_REGISTER_OFFSET; + rdev->mode_info.afmt[2]->id = 2; + } + rdev->mode_info.afmt[3] = malloc(sizeof(struct radeon_afmt), + DRM_MEM_DRIVER, M_WAITOK | M_ZERO); + if (rdev->mode_info.afmt[3]) { + rdev->mode_info.afmt[3]->offset = EVERGREEN_CRTC3_REGISTER_OFFSET; + rdev->mode_info.afmt[3]->id = 3; + } + rdev->mode_info.afmt[4] = malloc(sizeof(struct radeon_afmt), + DRM_MEM_DRIVER, M_WAITOK | M_ZERO); + if (rdev->mode_info.afmt[4]) { + rdev->mode_info.afmt[4]->offset = EVERGREEN_CRTC4_REGISTER_OFFSET; + rdev->mode_info.afmt[4]->id = 4; + } + rdev->mode_info.afmt[5] = malloc(sizeof(struct radeon_afmt), + DRM_MEM_DRIVER, M_WAITOK | M_ZERO); + if (rdev->mode_info.afmt[5]) { + rdev->mode_info.afmt[5]->offset = EVERGREEN_CRTC5_REGISTER_OFFSET; + rdev->mode_info.afmt[5]->id = 5; + } + } + } else if (ASIC_IS_DCE3(rdev)) { + /* DCE3.x has 2 audio blocks tied to DIG encoders */ + rdev->mode_info.afmt[0] = malloc(sizeof(struct radeon_afmt), + DRM_MEM_DRIVER, M_WAITOK | M_ZERO); + if (rdev->mode_info.afmt[0]) { + rdev->mode_info.afmt[0]->offset = DCE3_HDMI_OFFSET0; + rdev->mode_info.afmt[0]->id = 0; + } + rdev->mode_info.afmt[1] = malloc(sizeof(struct radeon_afmt), + DRM_MEM_DRIVER, M_WAITOK | M_ZERO); + if (rdev->mode_info.afmt[1]) { + rdev->mode_info.afmt[1]->offset = DCE3_HDMI_OFFSET1; + rdev->mode_info.afmt[1]->id = 1; + } + } else if (ASIC_IS_DCE2(rdev)) { + /* DCE2 has at least 1 routable audio block */ + rdev->mode_info.afmt[0] = malloc(sizeof(struct radeon_afmt), + DRM_MEM_DRIVER, M_WAITOK | M_ZERO); + if (rdev->mode_info.afmt[0]) { + rdev->mode_info.afmt[0]->offset = DCE2_HDMI_OFFSET0; + rdev->mode_info.afmt[0]->id = 0; + } + /* r6xx has 2 routable audio blocks */ + if (rdev->family >= CHIP_R600) { + rdev->mode_info.afmt[1] = malloc(sizeof(struct radeon_afmt), + DRM_MEM_DRIVER, M_WAITOK | M_ZERO); + if (rdev->mode_info.afmt[1]) { + rdev->mode_info.afmt[1]->offset = DCE2_HDMI_OFFSET1; + rdev->mode_info.afmt[1]->id = 1; + } + } + } +} + +static void radeon_afmt_fini(struct radeon_device *rdev) +{ + int i; + + for (i = 0; i < RADEON_MAX_AFMT_BLOCKS; i++) { + free(rdev->mode_info.afmt[i], DRM_MEM_DRIVER); + rdev->mode_info.afmt[i] = NULL; + } +} + +int radeon_modeset_init(struct radeon_device *rdev) +{ + int i; + int ret; + + drm_mode_config_init(rdev->ddev); + rdev->mode_info.mode_config_initialized = true; + + rdev->ddev->mode_config.funcs = &radeon_mode_funcs; + + if (ASIC_IS_DCE5(rdev)) { + rdev->ddev->mode_config.max_width = 16384; + rdev->ddev->mode_config.max_height = 16384; + } else if (ASIC_IS_AVIVO(rdev)) { + rdev->ddev->mode_config.max_width = 8192; + rdev->ddev->mode_config.max_height = 8192; + } else { + rdev->ddev->mode_config.max_width = 4096; + rdev->ddev->mode_config.max_height = 4096; + } + + rdev->ddev->mode_config.preferred_depth = 24; + rdev->ddev->mode_config.prefer_shadow = 1; + + rdev->ddev->mode_config.fb_base = rdev->mc.aper_base; + + ret = radeon_modeset_create_props(rdev); + if (ret) { + return ret; + } + + /* init i2c buses */ + radeon_i2c_init(rdev); + + /* check combios for a valid hardcoded EDID - Sun servers */ + if (!rdev->is_atom_bios) { + /* check for hardcoded EDID in BIOS */ + radeon_combios_check_hardcoded_edid(rdev); + } + + /* allocate crtcs */ + for (i = 0; i < rdev->num_crtc; i++) { + radeon_crtc_init(rdev->ddev, i); + } + + /* okay we should have all the bios connectors */ + ret = radeon_setup_enc_conn(rdev->ddev); + if (!ret) { + return ret; + } + + /* init dig PHYs, disp eng pll */ + if (rdev->is_atom_bios) { + radeon_atom_encoder_init(rdev); + radeon_atom_disp_eng_pll_init(rdev); + } + + /* initialize hpd */ + radeon_hpd_init(rdev); + + /* setup afmt */ + radeon_afmt_init(rdev); + + /* Initialize power management */ + radeon_pm_init(rdev); + + radeon_fbdev_init(rdev); + drm_kms_helper_poll_init(rdev->ddev); + + return 0; +} + +void radeon_modeset_fini(struct radeon_device *rdev) +{ + radeon_fbdev_fini(rdev); + free(rdev->mode_info.bios_hardcoded_edid, DRM_MEM_KMS); + radeon_pm_fini(rdev); + + if (rdev->mode_info.mode_config_initialized) { + radeon_afmt_fini(rdev); + drm_kms_helper_poll_fini(rdev->ddev); + radeon_hpd_fini(rdev); + DRM_UNLOCK(rdev->ddev); /* Work around lock recursion. dumbbell@ */ + drm_mode_config_cleanup(rdev->ddev); + DRM_LOCK(rdev->ddev); + rdev->mode_info.mode_config_initialized = false; + } + /* free i2c buses */ + radeon_i2c_fini(rdev); +} + +static bool is_hdtv_mode(const struct drm_display_mode *mode) +{ + /* try and guess if this is a tv or a monitor */ + if ((mode->vdisplay == 480 && mode->hdisplay == 720) || /* 480p */ + (mode->vdisplay == 576) || /* 576p */ + (mode->vdisplay == 720) || /* 720p */ + (mode->vdisplay == 1080)) /* 1080p */ + return true; + else + return false; +} + +bool radeon_crtc_scaling_mode_fixup(struct drm_crtc *crtc, + const struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + struct drm_device *dev = crtc->dev; + struct radeon_device *rdev = dev->dev_private; + struct drm_encoder *encoder; + struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); + struct radeon_encoder *radeon_encoder; + struct drm_connector *connector; + struct radeon_connector *radeon_connector; + bool first = true; + u32 src_v = 1, dst_v = 1; + u32 src_h = 1, dst_h = 1; + + radeon_crtc->h_border = 0; + radeon_crtc->v_border = 0; + + list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { + if (encoder->crtc != crtc) + continue; + radeon_encoder = to_radeon_encoder(encoder); + connector = radeon_get_connector_for_encoder(encoder); + radeon_connector = to_radeon_connector(connector); + + if (first) { + /* set scaling */ + if (radeon_encoder->rmx_type == RMX_OFF) + radeon_crtc->rmx_type = RMX_OFF; + else if (mode->hdisplay < radeon_encoder->native_mode.hdisplay || + mode->vdisplay < radeon_encoder->native_mode.vdisplay) + radeon_crtc->rmx_type = radeon_encoder->rmx_type; + else + radeon_crtc->rmx_type = RMX_OFF; + /* copy native mode */ + memcpy(&radeon_crtc->native_mode, + &radeon_encoder->native_mode, + sizeof(struct drm_display_mode)); + src_v = crtc->mode.vdisplay; + dst_v = radeon_crtc->native_mode.vdisplay; + src_h = crtc->mode.hdisplay; + dst_h = radeon_crtc->native_mode.hdisplay; + + /* fix up for overscan on hdmi */ + if (ASIC_IS_AVIVO(rdev) && + (!(mode->flags & DRM_MODE_FLAG_INTERLACE)) && + ((radeon_encoder->underscan_type == UNDERSCAN_ON) || + ((radeon_encoder->underscan_type == UNDERSCAN_AUTO) && + drm_detect_hdmi_monitor(radeon_connector->edid) && + is_hdtv_mode(mode)))) { + if (radeon_encoder->underscan_hborder != 0) + radeon_crtc->h_border = radeon_encoder->underscan_hborder; + else + radeon_crtc->h_border = (mode->hdisplay >> 5) + 16; + if (radeon_encoder->underscan_vborder != 0) + radeon_crtc->v_border = radeon_encoder->underscan_vborder; + else + radeon_crtc->v_border = (mode->vdisplay >> 5) + 16; + radeon_crtc->rmx_type = RMX_FULL; + src_v = crtc->mode.vdisplay; + dst_v = crtc->mode.vdisplay - (radeon_crtc->v_border * 2); + src_h = crtc->mode.hdisplay; + dst_h = crtc->mode.hdisplay - (radeon_crtc->h_border * 2); + } + first = false; + } else { + if (radeon_crtc->rmx_type != radeon_encoder->rmx_type) { + /* WARNING: Right now this can't happen but + * in the future we need to check that scaling + * are consistent across different encoder + * (ie all encoder can work with the same + * scaling). + */ + DRM_ERROR("Scaling not consistent across encoder.\n"); + return false; + } + } + } + if (radeon_crtc->rmx_type != RMX_OFF) { + fixed20_12 a, b; + a.full = dfixed_const(src_v); + b.full = dfixed_const(dst_v); + radeon_crtc->vsc.full = dfixed_div(a, b); + a.full = dfixed_const(src_h); + b.full = dfixed_const(dst_h); + radeon_crtc->hsc.full = dfixed_div(a, b); + } else { + radeon_crtc->vsc.full = dfixed_const(1); + radeon_crtc->hsc.full = dfixed_const(1); + } + return true; +} + +/* + * Retrieve current video scanout position of crtc on a given gpu. + * + * \param dev Device to query. + * \param crtc Crtc to query. + * \param *vpos Location where vertical scanout position should be stored. + * \param *hpos Location where horizontal scanout position should go. + * + * Returns vpos as a positive number while in active scanout area. + * Returns vpos as a negative number inside vblank, counting the number + * of scanlines to go until end of vblank, e.g., -1 means "one scanline + * until start of active scanout / end of vblank." + * + * \return Flags, or'ed together as follows: + * + * DRM_SCANOUTPOS_VALID = Query successful. + * DRM_SCANOUTPOS_INVBL = Inside vblank. + * DRM_SCANOUTPOS_ACCURATE = Returned position is accurate. A lack of + * this flag means that returned position may be offset by a constant but + * unknown small number of scanlines wrt. real scanout position. + * + */ +int radeon_get_crtc_scanoutpos(struct drm_device *dev, int crtc, int *vpos, int *hpos) +{ + u32 stat_crtc = 0, vbl = 0, position = 0; + int vbl_start, vbl_end, vtotal, ret = 0; + bool in_vbl = true; + + struct radeon_device *rdev = dev->dev_private; + + if (ASIC_IS_DCE4(rdev)) { + if (crtc == 0) { + vbl = RREG32(EVERGREEN_CRTC_V_BLANK_START_END + + EVERGREEN_CRTC0_REGISTER_OFFSET); + position = RREG32(EVERGREEN_CRTC_STATUS_POSITION + + EVERGREEN_CRTC0_REGISTER_OFFSET); + ret |= DRM_SCANOUTPOS_VALID; + } + if (crtc == 1) { + vbl = RREG32(EVERGREEN_CRTC_V_BLANK_START_END + + EVERGREEN_CRTC1_REGISTER_OFFSET); + position = RREG32(EVERGREEN_CRTC_STATUS_POSITION + + EVERGREEN_CRTC1_REGISTER_OFFSET); + ret |= DRM_SCANOUTPOS_VALID; + } + if (crtc == 2) { + vbl = RREG32(EVERGREEN_CRTC_V_BLANK_START_END + + EVERGREEN_CRTC2_REGISTER_OFFSET); + position = RREG32(EVERGREEN_CRTC_STATUS_POSITION + + EVERGREEN_CRTC2_REGISTER_OFFSET); + ret |= DRM_SCANOUTPOS_VALID; + } + if (crtc == 3) { + vbl = RREG32(EVERGREEN_CRTC_V_BLANK_START_END + + EVERGREEN_CRTC3_REGISTER_OFFSET); + position = RREG32(EVERGREEN_CRTC_STATUS_POSITION + + EVERGREEN_CRTC3_REGISTER_OFFSET); + ret |= DRM_SCANOUTPOS_VALID; + } + if (crtc == 4) { + vbl = RREG32(EVERGREEN_CRTC_V_BLANK_START_END + + EVERGREEN_CRTC4_REGISTER_OFFSET); + position = RREG32(EVERGREEN_CRTC_STATUS_POSITION + + EVERGREEN_CRTC4_REGISTER_OFFSET); + ret |= DRM_SCANOUTPOS_VALID; + } + if (crtc == 5) { + vbl = RREG32(EVERGREEN_CRTC_V_BLANK_START_END + + EVERGREEN_CRTC5_REGISTER_OFFSET); + position = RREG32(EVERGREEN_CRTC_STATUS_POSITION + + EVERGREEN_CRTC5_REGISTER_OFFSET); + ret |= DRM_SCANOUTPOS_VALID; + } + } else if (ASIC_IS_AVIVO(rdev)) { + if (crtc == 0) { + vbl = RREG32(AVIVO_D1CRTC_V_BLANK_START_END); + position = RREG32(AVIVO_D1CRTC_STATUS_POSITION); + ret |= DRM_SCANOUTPOS_VALID; + } + if (crtc == 1) { + vbl = RREG32(AVIVO_D2CRTC_V_BLANK_START_END); + position = RREG32(AVIVO_D2CRTC_STATUS_POSITION); + ret |= DRM_SCANOUTPOS_VALID; + } + } else { + /* Pre-AVIVO: Different encoding of scanout pos and vblank interval. */ + if (crtc == 0) { + /* Assume vbl_end == 0, get vbl_start from + * upper 16 bits. + */ + vbl = (RREG32(RADEON_CRTC_V_TOTAL_DISP) & + RADEON_CRTC_V_DISP) >> RADEON_CRTC_V_DISP_SHIFT; + /* Only retrieve vpos from upper 16 bits, set hpos == 0. */ + position = (RREG32(RADEON_CRTC_VLINE_CRNT_VLINE) >> 16) & RADEON_CRTC_V_TOTAL; + stat_crtc = RREG32(RADEON_CRTC_STATUS); + if (!(stat_crtc & 1)) + in_vbl = false; + + ret |= DRM_SCANOUTPOS_VALID; + } + if (crtc == 1) { + vbl = (RREG32(RADEON_CRTC2_V_TOTAL_DISP) & + RADEON_CRTC_V_DISP) >> RADEON_CRTC_V_DISP_SHIFT; + position = (RREG32(RADEON_CRTC2_VLINE_CRNT_VLINE) >> 16) & RADEON_CRTC_V_TOTAL; + stat_crtc = RREG32(RADEON_CRTC2_STATUS); + if (!(stat_crtc & 1)) + in_vbl = false; + + ret |= DRM_SCANOUTPOS_VALID; + } + } + + /* Decode into vertical and horizontal scanout position. */ + *vpos = position & 0x1fff; + *hpos = (position >> 16) & 0x1fff; + + /* Valid vblank area boundaries from gpu retrieved? */ + if (vbl > 0) { + /* Yes: Decode. */ + ret |= DRM_SCANOUTPOS_ACCURATE; + vbl_start = vbl & 0x1fff; + vbl_end = (vbl >> 16) & 0x1fff; + } + else { + /* No: Fake something reasonable which gives at least ok results. */ + vbl_start = rdev->mode_info.crtcs[crtc]->base.hwmode.crtc_vdisplay; + vbl_end = 0; + } + + /* Test scanout position against vblank region. */ + if ((*vpos < vbl_start) && (*vpos >= vbl_end)) + in_vbl = false; + + /* Check if inside vblank area and apply corrective offsets: + * vpos will then be >=0 in video scanout area, but negative + * within vblank area, counting down the number of lines until + * start of scanout. + */ + + /* Inside "upper part" of vblank area? Apply corrective offset if so: */ + if (in_vbl && (*vpos >= vbl_start)) { + vtotal = rdev->mode_info.crtcs[crtc]->base.hwmode.crtc_vtotal; + *vpos = *vpos - vtotal; + } + + /* Correct for shifted end of vbl at vbl_end. */ + *vpos = *vpos - vbl_end; + + /* In vblank? */ + if (in_vbl) + ret |= DRM_SCANOUTPOS_INVBL; + + return ret; +} diff --git a/sys/dev/drm2/radeon/radeon_drm.h b/sys/dev/drm2/radeon/radeon_drm.h new file mode 100644 index 00000000000..e84500c118f --- /dev/null +++ b/sys/dev/drm2/radeon/radeon_drm.h @@ -0,0 +1,985 @@ +/* radeon_drm.h -- Public header for the radeon driver -*- linux-c -*- + * + * Copyright 2000 Precision Insight, Inc., Cedar Park, Texas. + * Copyright 2000 VA Linux Systems, Inc., Fremont, California. + * Copyright 2002 Tungsten Graphics, Inc., Cedar Park, Texas. + * All rights reserved. + * + * 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 (including the next + * paragraph) 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 + * PRECISION INSIGHT AND/OR ITS SUPPLIERS 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. + * + * Authors: + * Kevin E. Martin + * Gareth Hughes + * Keith Whitwell + */ + +#include +__FBSDID("$FreeBSD$"); + +#ifndef __RADEON_DRM_H__ +#define __RADEON_DRM_H__ + +#include + +/* WARNING: If you change any of these defines, make sure to change the + * defines in the X server file (radeon_sarea.h) + */ +#ifndef __RADEON_SAREA_DEFINES__ +#define __RADEON_SAREA_DEFINES__ + +/* Old style state flags, required for sarea interface (1.1 and 1.2 + * clears) and 1.2 drm_vertex2 ioctl. + */ +#define RADEON_UPLOAD_CONTEXT 0x00000001 +#define RADEON_UPLOAD_VERTFMT 0x00000002 +#define RADEON_UPLOAD_LINE 0x00000004 +#define RADEON_UPLOAD_BUMPMAP 0x00000008 +#define RADEON_UPLOAD_MASKS 0x00000010 +#define RADEON_UPLOAD_VIEWPORT 0x00000020 +#define RADEON_UPLOAD_SETUP 0x00000040 +#define RADEON_UPLOAD_TCL 0x00000080 +#define RADEON_UPLOAD_MISC 0x00000100 +#define RADEON_UPLOAD_TEX0 0x00000200 +#define RADEON_UPLOAD_TEX1 0x00000400 +#define RADEON_UPLOAD_TEX2 0x00000800 +#define RADEON_UPLOAD_TEX0IMAGES 0x00001000 +#define RADEON_UPLOAD_TEX1IMAGES 0x00002000 +#define RADEON_UPLOAD_TEX2IMAGES 0x00004000 +#define RADEON_UPLOAD_CLIPRECTS 0x00008000 /* handled client-side */ +#define RADEON_REQUIRE_QUIESCENCE 0x00010000 +#define RADEON_UPLOAD_ZBIAS 0x00020000 /* version 1.2 and newer */ +#define RADEON_UPLOAD_ALL 0x003effff +#define RADEON_UPLOAD_CONTEXT_ALL 0x003e01ff + +/* New style per-packet identifiers for use in cmd_buffer ioctl with + * the RADEON_EMIT_PACKET command. Comments relate new packets to old + * state bits and the packet size: + */ +#define RADEON_EMIT_PP_MISC 0 /* context/7 */ +#define RADEON_EMIT_PP_CNTL 1 /* context/3 */ +#define RADEON_EMIT_RB3D_COLORPITCH 2 /* context/1 */ +#define RADEON_EMIT_RE_LINE_PATTERN 3 /* line/2 */ +#define RADEON_EMIT_SE_LINE_WIDTH 4 /* line/1 */ +#define RADEON_EMIT_PP_LUM_MATRIX 5 /* bumpmap/1 */ +#define RADEON_EMIT_PP_ROT_MATRIX_0 6 /* bumpmap/2 */ +#define RADEON_EMIT_RB3D_STENCILREFMASK 7 /* masks/3 */ +#define RADEON_EMIT_SE_VPORT_XSCALE 8 /* viewport/6 */ +#define RADEON_EMIT_SE_CNTL 9 /* setup/2 */ +#define RADEON_EMIT_SE_CNTL_STATUS 10 /* setup/1 */ +#define RADEON_EMIT_RE_MISC 11 /* misc/1 */ +#define RADEON_EMIT_PP_TXFILTER_0 12 /* tex0/6 */ +#define RADEON_EMIT_PP_BORDER_COLOR_0 13 /* tex0/1 */ +#define RADEON_EMIT_PP_TXFILTER_1 14 /* tex1/6 */ +#define RADEON_EMIT_PP_BORDER_COLOR_1 15 /* tex1/1 */ +#define RADEON_EMIT_PP_TXFILTER_2 16 /* tex2/6 */ +#define RADEON_EMIT_PP_BORDER_COLOR_2 17 /* tex2/1 */ +#define RADEON_EMIT_SE_ZBIAS_FACTOR 18 /* zbias/2 */ +#define RADEON_EMIT_SE_TCL_OUTPUT_VTX_FMT 19 /* tcl/11 */ +#define RADEON_EMIT_SE_TCL_MATERIAL_EMMISSIVE_RED 20 /* material/17 */ +#define R200_EMIT_PP_TXCBLEND_0 21 /* tex0/4 */ +#define R200_EMIT_PP_TXCBLEND_1 22 /* tex1/4 */ +#define R200_EMIT_PP_TXCBLEND_2 23 /* tex2/4 */ +#define R200_EMIT_PP_TXCBLEND_3 24 /* tex3/4 */ +#define R200_EMIT_PP_TXCBLEND_4 25 /* tex4/4 */ +#define R200_EMIT_PP_TXCBLEND_5 26 /* tex5/4 */ +#define R200_EMIT_PP_TXCBLEND_6 27 /* /4 */ +#define R200_EMIT_PP_TXCBLEND_7 28 /* /4 */ +#define R200_EMIT_TCL_LIGHT_MODEL_CTL_0 29 /* tcl/7 */ +#define R200_EMIT_TFACTOR_0 30 /* tf/7 */ +#define R200_EMIT_VTX_FMT_0 31 /* vtx/5 */ +#define R200_EMIT_VAP_CTL 32 /* vap/1 */ +#define R200_EMIT_MATRIX_SELECT_0 33 /* msl/5 */ +#define R200_EMIT_TEX_PROC_CTL_2 34 /* tcg/5 */ +#define R200_EMIT_TCL_UCP_VERT_BLEND_CTL 35 /* tcl/1 */ +#define R200_EMIT_PP_TXFILTER_0 36 /* tex0/6 */ +#define R200_EMIT_PP_TXFILTER_1 37 /* tex1/6 */ +#define R200_EMIT_PP_TXFILTER_2 38 /* tex2/6 */ +#define R200_EMIT_PP_TXFILTER_3 39 /* tex3/6 */ +#define R200_EMIT_PP_TXFILTER_4 40 /* tex4/6 */ +#define R200_EMIT_PP_TXFILTER_5 41 /* tex5/6 */ +#define R200_EMIT_PP_TXOFFSET_0 42 /* tex0/1 */ +#define R200_EMIT_PP_TXOFFSET_1 43 /* tex1/1 */ +#define R200_EMIT_PP_TXOFFSET_2 44 /* tex2/1 */ +#define R200_EMIT_PP_TXOFFSET_3 45 /* tex3/1 */ +#define R200_EMIT_PP_TXOFFSET_4 46 /* tex4/1 */ +#define R200_EMIT_PP_TXOFFSET_5 47 /* tex5/1 */ +#define R200_EMIT_VTE_CNTL 48 /* vte/1 */ +#define R200_EMIT_OUTPUT_VTX_COMP_SEL 49 /* vtx/1 */ +#define R200_EMIT_PP_TAM_DEBUG3 50 /* tam/1 */ +#define R200_EMIT_PP_CNTL_X 51 /* cst/1 */ +#define R200_EMIT_RB3D_DEPTHXY_OFFSET 52 /* cst/1 */ +#define R200_EMIT_RE_AUX_SCISSOR_CNTL 53 /* cst/1 */ +#define R200_EMIT_RE_SCISSOR_TL_0 54 /* cst/2 */ +#define R200_EMIT_RE_SCISSOR_TL_1 55 /* cst/2 */ +#define R200_EMIT_RE_SCISSOR_TL_2 56 /* cst/2 */ +#define R200_EMIT_SE_VAP_CNTL_STATUS 57 /* cst/1 */ +#define R200_EMIT_SE_VTX_STATE_CNTL 58 /* cst/1 */ +#define R200_EMIT_RE_POINTSIZE 59 /* cst/1 */ +#define R200_EMIT_TCL_INPUT_VTX_VECTOR_ADDR_0 60 /* cst/4 */ +#define R200_EMIT_PP_CUBIC_FACES_0 61 +#define R200_EMIT_PP_CUBIC_OFFSETS_0 62 +#define R200_EMIT_PP_CUBIC_FACES_1 63 +#define R200_EMIT_PP_CUBIC_OFFSETS_1 64 +#define R200_EMIT_PP_CUBIC_FACES_2 65 +#define R200_EMIT_PP_CUBIC_OFFSETS_2 66 +#define R200_EMIT_PP_CUBIC_FACES_3 67 +#define R200_EMIT_PP_CUBIC_OFFSETS_3 68 +#define R200_EMIT_PP_CUBIC_FACES_4 69 +#define R200_EMIT_PP_CUBIC_OFFSETS_4 70 +#define R200_EMIT_PP_CUBIC_FACES_5 71 +#define R200_EMIT_PP_CUBIC_OFFSETS_5 72 +#define RADEON_EMIT_PP_TEX_SIZE_0 73 +#define RADEON_EMIT_PP_TEX_SIZE_1 74 +#define RADEON_EMIT_PP_TEX_SIZE_2 75 +#define R200_EMIT_RB3D_BLENDCOLOR 76 +#define R200_EMIT_TCL_POINT_SPRITE_CNTL 77 +#define RADEON_EMIT_PP_CUBIC_FACES_0 78 +#define RADEON_EMIT_PP_CUBIC_OFFSETS_T0 79 +#define RADEON_EMIT_PP_CUBIC_FACES_1 80 +#define RADEON_EMIT_PP_CUBIC_OFFSETS_T1 81 +#define RADEON_EMIT_PP_CUBIC_FACES_2 82 +#define RADEON_EMIT_PP_CUBIC_OFFSETS_T2 83 +#define R200_EMIT_PP_TRI_PERF_CNTL 84 +#define R200_EMIT_PP_AFS_0 85 +#define R200_EMIT_PP_AFS_1 86 +#define R200_EMIT_ATF_TFACTOR 87 +#define R200_EMIT_PP_TXCTLALL_0 88 +#define R200_EMIT_PP_TXCTLALL_1 89 +#define R200_EMIT_PP_TXCTLALL_2 90 +#define R200_EMIT_PP_TXCTLALL_3 91 +#define R200_EMIT_PP_TXCTLALL_4 92 +#define R200_EMIT_PP_TXCTLALL_5 93 +#define R200_EMIT_VAP_PVS_CNTL 94 +#define RADEON_MAX_STATE_PACKETS 95 + +/* Commands understood by cmd_buffer ioctl. More can be added but + * obviously these can't be removed or changed: + */ +#define RADEON_CMD_PACKET 1 /* emit one of the register packets above */ +#define RADEON_CMD_SCALARS 2 /* emit scalar data */ +#define RADEON_CMD_VECTORS 3 /* emit vector data */ +#define RADEON_CMD_DMA_DISCARD 4 /* discard current dma buf */ +#define RADEON_CMD_PACKET3 5 /* emit hw packet */ +#define RADEON_CMD_PACKET3_CLIP 6 /* emit hw packet wrapped in cliprects */ +#define RADEON_CMD_SCALARS2 7 /* r200 stopgap */ +#define RADEON_CMD_WAIT 8 /* emit hw wait commands -- note: + * doesn't make the cpu wait, just + * the graphics hardware */ +#define RADEON_CMD_VECLINEAR 9 /* another r200 stopgap */ + +typedef union { + int i; + struct { + unsigned char cmd_type, pad0, pad1, pad2; + } header; + struct { + unsigned char cmd_type, packet_id, pad0, pad1; + } packet; + struct { + unsigned char cmd_type, offset, stride, count; + } scalars; + struct { + unsigned char cmd_type, offset, stride, count; + } vectors; + struct { + unsigned char cmd_type, addr_lo, addr_hi, count; + } veclinear; + struct { + unsigned char cmd_type, buf_idx, pad0, pad1; + } dma; + struct { + unsigned char cmd_type, flags, pad0, pad1; + } wait; +} drm_radeon_cmd_header_t; + +#define RADEON_WAIT_2D 0x1 +#define RADEON_WAIT_3D 0x2 + +/* Allowed parameters for R300_CMD_PACKET3 + */ +#define R300_CMD_PACKET3_CLEAR 0 +#define R300_CMD_PACKET3_RAW 1 + +/* Commands understood by cmd_buffer ioctl for R300. + * The interface has not been stabilized, so some of these may be removed + * and eventually reordered before stabilization. + */ +#define R300_CMD_PACKET0 1 +#define R300_CMD_VPU 2 /* emit vertex program upload */ +#define R300_CMD_PACKET3 3 /* emit a packet3 */ +#define R300_CMD_END3D 4 /* emit sequence ending 3d rendering */ +#define R300_CMD_CP_DELAY 5 +#define R300_CMD_DMA_DISCARD 6 +#define R300_CMD_WAIT 7 +# define R300_WAIT_2D 0x1 +# define R300_WAIT_3D 0x2 +/* these two defines are DOING IT WRONG - however + * we have userspace which relies on using these. + * The wait interface is backwards compat new + * code should use the NEW_WAIT defines below + * THESE ARE NOT BIT FIELDS + */ +# define R300_WAIT_2D_CLEAN 0x3 +# define R300_WAIT_3D_CLEAN 0x4 + +# define R300_NEW_WAIT_2D_3D 0x3 +# define R300_NEW_WAIT_2D_2D_CLEAN 0x4 +# define R300_NEW_WAIT_3D_3D_CLEAN 0x6 +# define R300_NEW_WAIT_2D_2D_CLEAN_3D_3D_CLEAN 0x8 + +#define R300_CMD_SCRATCH 8 +#define R300_CMD_R500FP 9 + +typedef union { + unsigned int u; + struct { + unsigned char cmd_type, pad0, pad1, pad2; + } header; + struct { + unsigned char cmd_type, count, reglo, reghi; + } packet0; + struct { + unsigned char cmd_type, count, adrlo, adrhi; + } vpu; + struct { + unsigned char cmd_type, packet, pad0, pad1; + } packet3; + struct { + unsigned char cmd_type, packet; + unsigned short count; /* amount of packet2 to emit */ + } delay; + struct { + unsigned char cmd_type, buf_idx, pad0, pad1; + } dma; + struct { + unsigned char cmd_type, flags, pad0, pad1; + } wait; + struct { + unsigned char cmd_type, reg, n_bufs, flags; + } scratch; + struct { + unsigned char cmd_type, count, adrlo, adrhi_flags; + } r500fp; +} drm_r300_cmd_header_t; + +#define RADEON_FRONT 0x1 +#define RADEON_BACK 0x2 +#define RADEON_DEPTH 0x4 +#define RADEON_STENCIL 0x8 +#define RADEON_CLEAR_FASTZ 0x80000000 +#define RADEON_USE_HIERZ 0x40000000 +#define RADEON_USE_COMP_ZBUF 0x20000000 + +#define R500FP_CONSTANT_TYPE (1 << 1) +#define R500FP_CONSTANT_CLAMP (1 << 2) + +/* Primitive types + */ +#define RADEON_POINTS 0x1 +#define RADEON_LINES 0x2 +#define RADEON_LINE_STRIP 0x3 +#define RADEON_TRIANGLES 0x4 +#define RADEON_TRIANGLE_FAN 0x5 +#define RADEON_TRIANGLE_STRIP 0x6 + +/* Vertex/indirect buffer size + */ +#define RADEON_BUFFER_SIZE 65536 + +/* Byte offsets for indirect buffer data + */ +#define RADEON_INDEX_PRIM_OFFSET 20 + +#define RADEON_SCRATCH_REG_OFFSET 32 + +#define R600_SCRATCH_REG_OFFSET 256 + +#define RADEON_NR_SAREA_CLIPRECTS 12 + +/* There are 2 heaps (local/GART). Each region within a heap is a + * minimum of 64k, and there are at most 64 of them per heap. + */ +#define RADEON_LOCAL_TEX_HEAP 0 +#define RADEON_GART_TEX_HEAP 1 +#define RADEON_NR_TEX_HEAPS 2 +#define RADEON_NR_TEX_REGIONS 64 +#define RADEON_LOG_TEX_GRANULARITY 16 + +#define RADEON_MAX_TEXTURE_LEVELS 12 +#define RADEON_MAX_TEXTURE_UNITS 3 + +#define RADEON_MAX_SURFACES 8 + +/* Blits have strict offset rules. All blit offset must be aligned on + * a 1K-byte boundary. + */ +#define RADEON_OFFSET_SHIFT 10 +#define RADEON_OFFSET_ALIGN (1 << RADEON_OFFSET_SHIFT) +#define RADEON_OFFSET_MASK (RADEON_OFFSET_ALIGN - 1) + +#endif /* __RADEON_SAREA_DEFINES__ */ + +typedef struct { + unsigned int red; + unsigned int green; + unsigned int blue; + unsigned int alpha; +} radeon_color_regs_t; + +typedef struct { + /* Context state */ + unsigned int pp_misc; /* 0x1c14 */ + unsigned int pp_fog_color; + unsigned int re_solid_color; + unsigned int rb3d_blendcntl; + unsigned int rb3d_depthoffset; + unsigned int rb3d_depthpitch; + unsigned int rb3d_zstencilcntl; + + unsigned int pp_cntl; /* 0x1c38 */ + unsigned int rb3d_cntl; + unsigned int rb3d_coloroffset; + unsigned int re_width_height; + unsigned int rb3d_colorpitch; + unsigned int se_cntl; + + /* Vertex format state */ + unsigned int se_coord_fmt; /* 0x1c50 */ + + /* Line state */ + unsigned int re_line_pattern; /* 0x1cd0 */ + unsigned int re_line_state; + + unsigned int se_line_width; /* 0x1db8 */ + + /* Bumpmap state */ + unsigned int pp_lum_matrix; /* 0x1d00 */ + + unsigned int pp_rot_matrix_0; /* 0x1d58 */ + unsigned int pp_rot_matrix_1; + + /* Mask state */ + unsigned int rb3d_stencilrefmask; /* 0x1d7c */ + unsigned int rb3d_ropcntl; + unsigned int rb3d_planemask; + + /* Viewport state */ + unsigned int se_vport_xscale; /* 0x1d98 */ + unsigned int se_vport_xoffset; + unsigned int se_vport_yscale; + unsigned int se_vport_yoffset; + unsigned int se_vport_zscale; + unsigned int se_vport_zoffset; + + /* Setup state */ + unsigned int se_cntl_status; /* 0x2140 */ + + /* Misc state */ + unsigned int re_top_left; /* 0x26c0 */ + unsigned int re_misc; +} drm_radeon_context_regs_t; + +typedef struct { + /* Zbias state */ + unsigned int se_zbias_factor; /* 0x1dac */ + unsigned int se_zbias_constant; +} drm_radeon_context2_regs_t; + +/* Setup registers for each texture unit + */ +typedef struct { + unsigned int pp_txfilter; + unsigned int pp_txformat; + unsigned int pp_txoffset; + unsigned int pp_txcblend; + unsigned int pp_txablend; + unsigned int pp_tfactor; + unsigned int pp_border_color; +} drm_radeon_texture_regs_t; + +typedef struct { + unsigned int start; + unsigned int finish; + unsigned int prim:8; + unsigned int stateidx:8; + unsigned int numverts:16; /* overloaded as offset/64 for elt prims */ + unsigned int vc_format; /* vertex format */ +} drm_radeon_prim_t; + +typedef struct { + drm_radeon_context_regs_t context; + drm_radeon_texture_regs_t tex[RADEON_MAX_TEXTURE_UNITS]; + drm_radeon_context2_regs_t context2; + unsigned int dirty; +} drm_radeon_state_t; + +typedef struct { + /* The channel for communication of state information to the + * kernel on firing a vertex buffer with either of the + * obsoleted vertex/index ioctls. + */ + drm_radeon_context_regs_t context_state; + drm_radeon_texture_regs_t tex_state[RADEON_MAX_TEXTURE_UNITS]; + unsigned int dirty; + unsigned int vertsize; + unsigned int vc_format; + + /* The current cliprects, or a subset thereof. + */ + struct drm_clip_rect boxes[RADEON_NR_SAREA_CLIPRECTS]; + unsigned int nbox; + + /* Counters for client-side throttling of rendering clients. + */ + unsigned int last_frame; + unsigned int last_dispatch; + unsigned int last_clear; + + struct drm_tex_region tex_list[RADEON_NR_TEX_HEAPS][RADEON_NR_TEX_REGIONS + + 1]; + unsigned int tex_age[RADEON_NR_TEX_HEAPS]; + int ctx_owner; + int pfState; /* number of 3d windows (0,1,2ormore) */ + int pfCurrentPage; /* which buffer is being displayed? */ + int crtc2_base; /* CRTC2 frame offset */ + int tiling_enabled; /* set by drm, read by 2d + 3d clients */ +} drm_radeon_sarea_t; + +/* WARNING: If you change any of these defines, make sure to change the + * defines in the Xserver file (xf86drmRadeon.h) + * + * KW: actually it's illegal to change any of this (backwards compatibility). + */ + +/* Radeon specific ioctls + * The device specific ioctl range is 0x40 to 0x79. + */ +#define DRM_RADEON_CP_INIT 0x00 +#define DRM_RADEON_CP_START 0x01 +#define DRM_RADEON_CP_STOP 0x02 +#define DRM_RADEON_CP_RESET 0x03 +#define DRM_RADEON_CP_IDLE 0x04 +#define DRM_RADEON_RESET 0x05 +#define DRM_RADEON_FULLSCREEN 0x06 +#define DRM_RADEON_SWAP 0x07 +#define DRM_RADEON_CLEAR 0x08 +#define DRM_RADEON_VERTEX 0x09 +#define DRM_RADEON_INDICES 0x0A +#define DRM_RADEON_NOT_USED +#define DRM_RADEON_STIPPLE 0x0C +#define DRM_RADEON_INDIRECT 0x0D +#define DRM_RADEON_TEXTURE 0x0E +#define DRM_RADEON_VERTEX2 0x0F +#define DRM_RADEON_CMDBUF 0x10 +#define DRM_RADEON_GETPARAM 0x11 +#define DRM_RADEON_FLIP 0x12 +#define DRM_RADEON_ALLOC 0x13 +#define DRM_RADEON_FREE 0x14 +#define DRM_RADEON_INIT_HEAP 0x15 +#define DRM_RADEON_IRQ_EMIT 0x16 +#define DRM_RADEON_IRQ_WAIT 0x17 +#define DRM_RADEON_CP_RESUME 0x18 +#define DRM_RADEON_SETPARAM 0x19 +#define DRM_RADEON_SURF_ALLOC 0x1a +#define DRM_RADEON_SURF_FREE 0x1b +/* KMS ioctl */ +#define DRM_RADEON_GEM_INFO 0x1c +#define DRM_RADEON_GEM_CREATE 0x1d +#define DRM_RADEON_GEM_MMAP 0x1e +#define DRM_RADEON_GEM_PREAD 0x21 +#define DRM_RADEON_GEM_PWRITE 0x22 +#define DRM_RADEON_GEM_SET_DOMAIN 0x23 +#define DRM_RADEON_GEM_WAIT_IDLE 0x24 +#define DRM_RADEON_CS 0x26 +#define DRM_RADEON_INFO 0x27 +#define DRM_RADEON_GEM_SET_TILING 0x28 +#define DRM_RADEON_GEM_GET_TILING 0x29 +#define DRM_RADEON_GEM_BUSY 0x2a +#define DRM_RADEON_GEM_VA 0x2b + +#define DRM_IOCTL_RADEON_CP_INIT DRM_IOW( DRM_COMMAND_BASE + DRM_RADEON_CP_INIT, drm_radeon_init_t) +#define DRM_IOCTL_RADEON_CP_START DRM_IO( DRM_COMMAND_BASE + DRM_RADEON_CP_START) +#define DRM_IOCTL_RADEON_CP_STOP DRM_IOW( DRM_COMMAND_BASE + DRM_RADEON_CP_STOP, drm_radeon_cp_stop_t) +#define DRM_IOCTL_RADEON_CP_RESET DRM_IO( DRM_COMMAND_BASE + DRM_RADEON_CP_RESET) +#define DRM_IOCTL_RADEON_CP_IDLE DRM_IO( DRM_COMMAND_BASE + DRM_RADEON_CP_IDLE) +#define DRM_IOCTL_RADEON_RESET DRM_IO( DRM_COMMAND_BASE + DRM_RADEON_RESET) +#define DRM_IOCTL_RADEON_FULLSCREEN DRM_IOW( DRM_COMMAND_BASE + DRM_RADEON_FULLSCREEN, drm_radeon_fullscreen_t) +#define DRM_IOCTL_RADEON_SWAP DRM_IO( DRM_COMMAND_BASE + DRM_RADEON_SWAP) +#define DRM_IOCTL_RADEON_CLEAR DRM_IOW( DRM_COMMAND_BASE + DRM_RADEON_CLEAR, drm_radeon_clear_t) +#define DRM_IOCTL_RADEON_VERTEX DRM_IOW( DRM_COMMAND_BASE + DRM_RADEON_VERTEX, drm_radeon_vertex_t) +#define DRM_IOCTL_RADEON_INDICES DRM_IOW( DRM_COMMAND_BASE + DRM_RADEON_INDICES, drm_radeon_indices_t) +#define DRM_IOCTL_RADEON_STIPPLE DRM_IOW( DRM_COMMAND_BASE + DRM_RADEON_STIPPLE, drm_radeon_stipple_t) +#define DRM_IOCTL_RADEON_INDIRECT DRM_IOWR(DRM_COMMAND_BASE + DRM_RADEON_INDIRECT, drm_radeon_indirect_t) +#define DRM_IOCTL_RADEON_TEXTURE DRM_IOWR(DRM_COMMAND_BASE + DRM_RADEON_TEXTURE, drm_radeon_texture_t) +#define DRM_IOCTL_RADEON_VERTEX2 DRM_IOW( DRM_COMMAND_BASE + DRM_RADEON_VERTEX2, drm_radeon_vertex2_t) +#define DRM_IOCTL_RADEON_CMDBUF DRM_IOW( DRM_COMMAND_BASE + DRM_RADEON_CMDBUF, drm_radeon_cmd_buffer_t) +#define DRM_IOCTL_RADEON_GETPARAM DRM_IOWR(DRM_COMMAND_BASE + DRM_RADEON_GETPARAM, drm_radeon_getparam_t) +#define DRM_IOCTL_RADEON_FLIP DRM_IO( DRM_COMMAND_BASE + DRM_RADEON_FLIP) +#define DRM_IOCTL_RADEON_ALLOC DRM_IOWR(DRM_COMMAND_BASE + DRM_RADEON_ALLOC, drm_radeon_mem_alloc_t) +#define DRM_IOCTL_RADEON_FREE DRM_IOW( DRM_COMMAND_BASE + DRM_RADEON_FREE, drm_radeon_mem_free_t) +#define DRM_IOCTL_RADEON_INIT_HEAP DRM_IOW( DRM_COMMAND_BASE + DRM_RADEON_INIT_HEAP, drm_radeon_mem_init_heap_t) +#define DRM_IOCTL_RADEON_IRQ_EMIT DRM_IOWR(DRM_COMMAND_BASE + DRM_RADEON_IRQ_EMIT, drm_radeon_irq_emit_t) +#define DRM_IOCTL_RADEON_IRQ_WAIT DRM_IOW( DRM_COMMAND_BASE + DRM_RADEON_IRQ_WAIT, drm_radeon_irq_wait_t) +#define DRM_IOCTL_RADEON_CP_RESUME DRM_IO( DRM_COMMAND_BASE + DRM_RADEON_CP_RESUME) +#define DRM_IOCTL_RADEON_SETPARAM DRM_IOW( DRM_COMMAND_BASE + DRM_RADEON_SETPARAM, drm_radeon_setparam_t) +#define DRM_IOCTL_RADEON_SURF_ALLOC DRM_IOW( DRM_COMMAND_BASE + DRM_RADEON_SURF_ALLOC, drm_radeon_surface_alloc_t) +#define DRM_IOCTL_RADEON_SURF_FREE DRM_IOW( DRM_COMMAND_BASE + DRM_RADEON_SURF_FREE, drm_radeon_surface_free_t) +/* KMS */ +#define DRM_IOCTL_RADEON_GEM_INFO DRM_IOWR(DRM_COMMAND_BASE + DRM_RADEON_GEM_INFO, struct drm_radeon_gem_info) +#define DRM_IOCTL_RADEON_GEM_CREATE DRM_IOWR(DRM_COMMAND_BASE + DRM_RADEON_GEM_CREATE, struct drm_radeon_gem_create) +#define DRM_IOCTL_RADEON_GEM_MMAP DRM_IOWR(DRM_COMMAND_BASE + DRM_RADEON_GEM_MMAP, struct drm_radeon_gem_mmap) +#define DRM_IOCTL_RADEON_GEM_PREAD DRM_IOWR(DRM_COMMAND_BASE + DRM_RADEON_GEM_PREAD, struct drm_radeon_gem_pread) +#define DRM_IOCTL_RADEON_GEM_PWRITE DRM_IOWR(DRM_COMMAND_BASE + DRM_RADEON_GEM_PWRITE, struct drm_radeon_gem_pwrite) +#define DRM_IOCTL_RADEON_GEM_SET_DOMAIN DRM_IOWR(DRM_COMMAND_BASE + DRM_RADEON_GEM_SET_DOMAIN, struct drm_radeon_gem_set_domain) +#define DRM_IOCTL_RADEON_GEM_WAIT_IDLE DRM_IOW(DRM_COMMAND_BASE + DRM_RADEON_GEM_WAIT_IDLE, struct drm_radeon_gem_wait_idle) +#define DRM_IOCTL_RADEON_CS DRM_IOWR(DRM_COMMAND_BASE + DRM_RADEON_CS, struct drm_radeon_cs) +#define DRM_IOCTL_RADEON_INFO DRM_IOWR(DRM_COMMAND_BASE + DRM_RADEON_INFO, struct drm_radeon_info) +#define DRM_IOCTL_RADEON_GEM_SET_TILING DRM_IOWR(DRM_COMMAND_BASE + DRM_RADEON_GEM_SET_TILING, struct drm_radeon_gem_set_tiling) +#define DRM_IOCTL_RADEON_GEM_GET_TILING DRM_IOWR(DRM_COMMAND_BASE + DRM_RADEON_GEM_GET_TILING, struct drm_radeon_gem_get_tiling) +#define DRM_IOCTL_RADEON_GEM_BUSY DRM_IOWR(DRM_COMMAND_BASE + DRM_RADEON_GEM_BUSY, struct drm_radeon_gem_busy) +#define DRM_IOCTL_RADEON_GEM_VA DRM_IOWR(DRM_COMMAND_BASE + DRM_RADEON_GEM_VA, struct drm_radeon_gem_va) + +typedef struct drm_radeon_init { + enum { + RADEON_INIT_CP = 0x01, + RADEON_CLEANUP_CP = 0x02, + RADEON_INIT_R200_CP = 0x03, + RADEON_INIT_R300_CP = 0x04, + RADEON_INIT_R600_CP = 0x05 + } func; + unsigned long sarea_priv_offset; + int is_pci; + int cp_mode; + int gart_size; + int ring_size; + int usec_timeout; + + unsigned int fb_bpp; + unsigned int front_offset, front_pitch; + unsigned int back_offset, back_pitch; + unsigned int depth_bpp; + unsigned int depth_offset, depth_pitch; + + unsigned long fb_offset; + unsigned long mmio_offset; + unsigned long ring_offset; + unsigned long ring_rptr_offset; + unsigned long buffers_offset; + unsigned long gart_textures_offset; +} drm_radeon_init_t; + +typedef struct drm_radeon_cp_stop { + int flush; + int idle; +} drm_radeon_cp_stop_t; + +typedef struct drm_radeon_fullscreen { + enum { + RADEON_INIT_FULLSCREEN = 0x01, + RADEON_CLEANUP_FULLSCREEN = 0x02 + } func; +} drm_radeon_fullscreen_t; + +#define CLEAR_X1 0 +#define CLEAR_Y1 1 +#define CLEAR_X2 2 +#define CLEAR_Y2 3 +#define CLEAR_DEPTH 4 + +typedef union drm_radeon_clear_rect { + float f[5]; + unsigned int ui[5]; +} drm_radeon_clear_rect_t; + +typedef struct drm_radeon_clear { + unsigned int flags; + unsigned int clear_color; + unsigned int clear_depth; + unsigned int color_mask; + unsigned int depth_mask; /* misnamed field: should be stencil */ + drm_radeon_clear_rect_t __user *depth_boxes; +} drm_radeon_clear_t; + +typedef struct drm_radeon_vertex { + int prim; + int idx; /* Index of vertex buffer */ + int count; /* Number of vertices in buffer */ + int discard; /* Client finished with buffer? */ +} drm_radeon_vertex_t; + +typedef struct drm_radeon_indices { + int prim; + int idx; + int start; + int end; + int discard; /* Client finished with buffer? */ +} drm_radeon_indices_t; + +/* v1.2 - obsoletes drm_radeon_vertex and drm_radeon_indices + * - allows multiple primitives and state changes in a single ioctl + * - supports driver change to emit native primitives + */ +typedef struct drm_radeon_vertex2 { + int idx; /* Index of vertex buffer */ + int discard; /* Client finished with buffer? */ + int nr_states; + drm_radeon_state_t __user *state; + int nr_prims; + drm_radeon_prim_t __user *prim; +} drm_radeon_vertex2_t; + +/* v1.3 - obsoletes drm_radeon_vertex2 + * - allows arbitrarily large cliprect list + * - allows updating of tcl packet, vector and scalar state + * - allows memory-efficient description of state updates + * - allows state to be emitted without a primitive + * (for clears, ctx switches) + * - allows more than one dma buffer to be referenced per ioctl + * - supports tcl driver + * - may be extended in future versions with new cmd types, packets + */ +typedef struct drm_radeon_cmd_buffer { + int bufsz; + char __user *buf; + int nbox; + struct drm_clip_rect __user *boxes; +} drm_radeon_cmd_buffer_t; + +typedef struct drm_radeon_tex_image { + unsigned int x, y; /* Blit coordinates */ + unsigned int width, height; + const void __user *data; +} drm_radeon_tex_image_t; + +typedef struct drm_radeon_texture { + unsigned int offset; + int pitch; + int format; + int width; /* Texture image coordinates */ + int height; + drm_radeon_tex_image_t __user *image; +} drm_radeon_texture_t; + +typedef struct drm_radeon_stipple { + unsigned int __user *mask; +} drm_radeon_stipple_t; + +typedef struct drm_radeon_indirect { + int idx; + int start; + int end; + int discard; +} drm_radeon_indirect_t; + +/* enum for card type parameters */ +#define RADEON_CARD_PCI 0 +#define RADEON_CARD_AGP 1 +#define RADEON_CARD_PCIE 2 + +/* 1.3: An ioctl to get parameters that aren't available to the 3d + * client any other way. + */ +#define RADEON_PARAM_GART_BUFFER_OFFSET 1 /* card offset of 1st GART buffer */ +#define RADEON_PARAM_LAST_FRAME 2 +#define RADEON_PARAM_LAST_DISPATCH 3 +#define RADEON_PARAM_LAST_CLEAR 4 +/* Added with DRM version 1.6. */ +#define RADEON_PARAM_IRQ_NR 5 +#define RADEON_PARAM_GART_BASE 6 /* card offset of GART base */ +/* Added with DRM version 1.8. */ +#define RADEON_PARAM_REGISTER_HANDLE 7 /* for drmMap() */ +#define RADEON_PARAM_STATUS_HANDLE 8 +#define RADEON_PARAM_SAREA_HANDLE 9 +#define RADEON_PARAM_GART_TEX_HANDLE 10 +#define RADEON_PARAM_SCRATCH_OFFSET 11 +#define RADEON_PARAM_CARD_TYPE 12 +#define RADEON_PARAM_VBLANK_CRTC 13 /* VBLANK CRTC */ +#define RADEON_PARAM_FB_LOCATION 14 /* FB location */ +#define RADEON_PARAM_NUM_GB_PIPES 15 /* num GB pipes */ +#define RADEON_PARAM_DEVICE_ID 16 +#define RADEON_PARAM_NUM_Z_PIPES 17 /* num Z pipes */ + +typedef struct drm_radeon_getparam { + int param; + void __user *value; +} drm_radeon_getparam_t; + +/* 1.6: Set up a memory manager for regions of shared memory: + */ +#define RADEON_MEM_REGION_GART 1 +#define RADEON_MEM_REGION_FB 2 + +typedef struct drm_radeon_mem_alloc { + int region; + int alignment; + int size; + int __user *region_offset; /* offset from start of fb or GART */ +} drm_radeon_mem_alloc_t; + +typedef struct drm_radeon_mem_free { + int region; + int region_offset; +} drm_radeon_mem_free_t; + +typedef struct drm_radeon_mem_init_heap { + int region; + int size; + int start; +} drm_radeon_mem_init_heap_t; + +/* 1.6: Userspace can request & wait on irq's: + */ +typedef struct drm_radeon_irq_emit { + int __user *irq_seq; +} drm_radeon_irq_emit_t; + +typedef struct drm_radeon_irq_wait { + int irq_seq; +} drm_radeon_irq_wait_t; + +/* 1.10: Clients tell the DRM where they think the framebuffer is located in + * the card's address space, via a new generic ioctl to set parameters + */ + +typedef struct drm_radeon_setparam { + unsigned int param; + int64_t value; +} drm_radeon_setparam_t; + +#define RADEON_SETPARAM_FB_LOCATION 1 /* determined framebuffer location */ +#define RADEON_SETPARAM_SWITCH_TILING 2 /* enable/disable color tiling */ +#define RADEON_SETPARAM_PCIGART_LOCATION 3 /* PCI Gart Location */ +#define RADEON_SETPARAM_NEW_MEMMAP 4 /* Use new memory map */ +#define RADEON_SETPARAM_PCIGART_TABLE_SIZE 5 /* PCI GART Table Size */ +#define RADEON_SETPARAM_VBLANK_CRTC 6 /* VBLANK CRTC */ +/* 1.14: Clients can allocate/free a surface + */ +typedef struct drm_radeon_surface_alloc { + unsigned int address; + unsigned int size; + unsigned int flags; +} drm_radeon_surface_alloc_t; + +typedef struct drm_radeon_surface_free { + unsigned int address; +} drm_radeon_surface_free_t; + +#define DRM_RADEON_VBLANK_CRTC1 1 +#define DRM_RADEON_VBLANK_CRTC2 2 + +/* + * Kernel modesetting world below. + */ +#define RADEON_GEM_DOMAIN_CPU 0x1 +#define RADEON_GEM_DOMAIN_GTT 0x2 +#define RADEON_GEM_DOMAIN_VRAM 0x4 + +struct drm_radeon_gem_info { + uint64_t gart_size; + uint64_t vram_size; + uint64_t vram_visible; +}; + +#define RADEON_GEM_NO_BACKING_STORE 1 + +struct drm_radeon_gem_create { + uint64_t size; + uint64_t alignment; + uint32_t handle; + uint32_t initial_domain; + uint32_t flags; +}; + +#define RADEON_TILING_MACRO 0x1 +#define RADEON_TILING_MICRO 0x2 +#define RADEON_TILING_SWAP_16BIT 0x4 +#define RADEON_TILING_SWAP_32BIT 0x8 +/* this object requires a surface when mapped - i.e. front buffer */ +#define RADEON_TILING_SURFACE 0x10 +#define RADEON_TILING_MICRO_SQUARE 0x20 +#define RADEON_TILING_EG_BANKW_SHIFT 8 +#define RADEON_TILING_EG_BANKW_MASK 0xf +#define RADEON_TILING_EG_BANKH_SHIFT 12 +#define RADEON_TILING_EG_BANKH_MASK 0xf +#define RADEON_TILING_EG_MACRO_TILE_ASPECT_SHIFT 16 +#define RADEON_TILING_EG_MACRO_TILE_ASPECT_MASK 0xf +#define RADEON_TILING_EG_TILE_SPLIT_SHIFT 24 +#define RADEON_TILING_EG_TILE_SPLIT_MASK 0xf +#define RADEON_TILING_EG_STENCIL_TILE_SPLIT_SHIFT 28 +#define RADEON_TILING_EG_STENCIL_TILE_SPLIT_MASK 0xf + +struct drm_radeon_gem_set_tiling { + uint32_t handle; + uint32_t tiling_flags; + uint32_t pitch; +}; + +struct drm_radeon_gem_get_tiling { + uint32_t handle; + uint32_t tiling_flags; + uint32_t pitch; +}; + +struct drm_radeon_gem_mmap { + uint32_t handle; + uint32_t pad; + uint64_t offset; + uint64_t size; + uint64_t addr_ptr; +}; + +struct drm_radeon_gem_set_domain { + uint32_t handle; + uint32_t read_domains; + uint32_t write_domain; +}; + +struct drm_radeon_gem_wait_idle { + uint32_t handle; + uint32_t pad; +}; + +struct drm_radeon_gem_busy { + uint32_t handle; + uint32_t domain; +}; + +struct drm_radeon_gem_pread { + /** Handle for the object being read. */ + uint32_t handle; + uint32_t pad; + /** Offset into the object to read from */ + uint64_t offset; + /** Length of data to read */ + uint64_t size; + /** Pointer to write the data into. */ + /* void *, but pointers are not 32/64 compatible */ + uint64_t data_ptr; +}; + +struct drm_radeon_gem_pwrite { + /** Handle for the object being written to. */ + uint32_t handle; + uint32_t pad; + /** Offset into the object to write to */ + uint64_t offset; + /** Length of data to write */ + uint64_t size; + /** Pointer to read the data from. */ + /* void *, but pointers are not 32/64 compatible */ + uint64_t data_ptr; +}; + +#define RADEON_VA_MAP 1 +#define RADEON_VA_UNMAP 2 + +#define RADEON_VA_RESULT_OK 0 +#define RADEON_VA_RESULT_ERROR 1 +#define RADEON_VA_RESULT_VA_EXIST 2 + +#define RADEON_VM_PAGE_VALID (1 << 0) +#define RADEON_VM_PAGE_READABLE (1 << 1) +#define RADEON_VM_PAGE_WRITEABLE (1 << 2) +#define RADEON_VM_PAGE_SYSTEM (1 << 3) +#define RADEON_VM_PAGE_SNOOPED (1 << 4) + +struct drm_radeon_gem_va { + uint32_t handle; + uint32_t operation; + uint32_t vm_id; + uint32_t flags; + uint64_t offset; +}; + +#define RADEON_CHUNK_ID_RELOCS 0x01 +#define RADEON_CHUNK_ID_IB 0x02 +#define RADEON_CHUNK_ID_FLAGS 0x03 +#define RADEON_CHUNK_ID_CONST_IB 0x04 + +/* The first dword of RADEON_CHUNK_ID_FLAGS is a uint32 of these flags: */ +#define RADEON_CS_KEEP_TILING_FLAGS 0x01 +#define RADEON_CS_USE_VM 0x02 +#define RADEON_CS_END_OF_FRAME 0x04 /* a hint from userspace which CS is the last one */ +/* The second dword of RADEON_CHUNK_ID_FLAGS is a uint32 that sets the ring type */ +#define RADEON_CS_RING_GFX 0 +#define RADEON_CS_RING_COMPUTE 1 +#define RADEON_CS_RING_DMA 2 +/* The third dword of RADEON_CHUNK_ID_FLAGS is a sint32 that sets the priority */ +/* 0 = normal, + = higher priority, - = lower priority */ + +struct drm_radeon_cs_chunk { + uint32_t chunk_id; + uint32_t length_dw; + uint64_t chunk_data; +}; + +/* drm_radeon_cs_reloc.flags */ + +struct drm_radeon_cs_reloc { + uint32_t handle; + uint32_t read_domains; + uint32_t write_domain; + uint32_t flags; +}; + +struct drm_radeon_cs { + uint32_t num_chunks; + uint32_t cs_id; + /* this points to uint64_t * which point to cs chunks */ + uint64_t chunks; + /* updates to the limits after this CS ioctl */ + uint64_t gart_limit; + uint64_t vram_limit; +}; + +#define RADEON_INFO_DEVICE_ID 0x00 +#define RADEON_INFO_NUM_GB_PIPES 0x01 +#define RADEON_INFO_NUM_Z_PIPES 0x02 +#define RADEON_INFO_ACCEL_WORKING 0x03 +#define RADEON_INFO_CRTC_FROM_ID 0x04 +#define RADEON_INFO_ACCEL_WORKING2 0x05 +#define RADEON_INFO_TILING_CONFIG 0x06 +#define RADEON_INFO_WANT_HYPERZ 0x07 +#define RADEON_INFO_WANT_CMASK 0x08 /* get access to CMASK on r300 */ +#define RADEON_INFO_CLOCK_CRYSTAL_FREQ 0x09 /* clock crystal frequency */ +#define RADEON_INFO_NUM_BACKENDS 0x0a /* DB/backends for r600+ - need for OQ */ +#define RADEON_INFO_NUM_TILE_PIPES 0x0b /* tile pipes for r600+ */ +#define RADEON_INFO_FUSION_GART_WORKING 0x0c /* fusion writes to GTT were broken before this */ +#define RADEON_INFO_BACKEND_MAP 0x0d /* pipe to backend map, needed by mesa */ +/* virtual address start, va < start are reserved by the kernel */ +#define RADEON_INFO_VA_START 0x0e +/* maximum size of ib using the virtual memory cs */ +#define RADEON_INFO_IB_VM_MAX_SIZE 0x0f +/* max pipes - needed for compute shaders */ +#define RADEON_INFO_MAX_PIPES 0x10 +/* timestamp for GL_ARB_timer_query (OpenGL), returns the current GPU clock */ +#define RADEON_INFO_TIMESTAMP 0x11 +/* max shader engines (SE) - needed for geometry shaders, etc. */ +#define RADEON_INFO_MAX_SE 0x12 +/* max SH per SE */ +#define RADEON_INFO_MAX_SH_PER_SE 0x13 + +struct drm_radeon_info { + uint32_t request; + uint32_t pad; + uint64_t value; +}; + +#endif diff --git a/sys/dev/drm2/radeon/radeon_drv.c b/sys/dev/drm2/radeon/radeon_drv.c new file mode 100644 index 00000000000..2ef5426f9cc --- /dev/null +++ b/sys/dev/drm2/radeon/radeon_drv.c @@ -0,0 +1,514 @@ +/** + * \file radeon_drv.c + * ATI Radeon driver + * + * \author Gareth Hughes + */ + +/* + * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. + * All Rights Reserved. + * + * 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 (including the next + * paragraph) 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 + * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS 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 +__FBSDID("$FreeBSD$"); + +#include +#include +#include "radeon_drv.h" +#include "radeon_gem.h" +#include "radeon_kms.h" +#include "radeon_irq_kms.h" + +#include + + +/* + * KMS wrapper. + * - 2.0.0 - initial interface + * - 2.1.0 - add square tiling interface + * - 2.2.0 - add r6xx/r7xx const buffer support + * - 2.3.0 - add MSPOS + 3D texture + r500 VAP regs + * - 2.4.0 - add crtc id query + * - 2.5.0 - add get accel 2 to work around ddx breakage for evergreen + * - 2.6.0 - add tiling config query (r6xx+), add initial HiZ support (r300->r500) + * 2.7.0 - fixups for r600 2D tiling support. (no external ABI change), add eg dyn gpr regs + * 2.8.0 - pageflip support, r500 US_FORMAT regs. r500 ARGB2101010 colorbuf, r300->r500 CMASK, clock crystal query + * 2.9.0 - r600 tiling (s3tc,rgtc) working, SET_PREDICATION packet 3 on r600 + eg, backend query + * 2.10.0 - fusion 2D tiling + * 2.11.0 - backend map, initial compute support for the CS checker + * 2.12.0 - RADEON_CS_KEEP_TILING_FLAGS + * 2.13.0 - virtual memory support, streamout + * 2.14.0 - add evergreen tiling informations + * 2.15.0 - add max_pipes query + * 2.16.0 - fix evergreen 2D tiled surface calculation + * 2.17.0 - add STRMOUT_BASE_UPDATE for r7xx + * 2.18.0 - r600-eg: allow "invalid" DB formats + * 2.19.0 - r600-eg: MSAA textures + * 2.20.0 - r600-si: RADEON_INFO_TIMESTAMP query + * 2.21.0 - r600-r700: FMASK and CMASK + * 2.22.0 - r600 only: RESOLVE_BOX allowed + * 2.23.0 - allow STRMOUT_BASE_UPDATE on RS780 and RS880 + * 2.24.0 - eg only: allow MIP_ADDRESS=0 for MSAA textures + * 2.25.0 - eg+: new info request for num SE and num SH + * 2.26.0 - r600-eg: fix htile size computation + * 2.27.0 - r600-SI: Add CS ioctl support for async DMA + * 2.28.0 - r600-eg: Add MEM_WRITE packet support + * 2.29.0 - R500 FP16 color clear registers + */ +#define KMS_DRIVER_MAJOR 2 +#define KMS_DRIVER_MINOR 29 +#define KMS_DRIVER_PATCHLEVEL 0 +int radeon_suspend_kms(struct drm_device *dev); +int radeon_resume_kms(struct drm_device *dev); +extern int radeon_get_crtc_scanoutpos(struct drm_device *dev, int crtc, + int *vpos, int *hpos); +extern struct drm_ioctl_desc radeon_ioctls_kms[]; +extern int radeon_max_kms_ioctl; +#ifdef DUMBBELL_WIP +int radeon_mmap(struct file *filp, struct vm_area_struct *vma); +#endif /* DUMBBELL_WIP */ +int radeon_mode_dumb_mmap(struct drm_file *filp, + struct drm_device *dev, + uint32_t handle, uint64_t *offset_p); +int radeon_mode_dumb_create(struct drm_file *file_priv, + struct drm_device *dev, + struct drm_mode_create_dumb *args); +int radeon_mode_dumb_destroy(struct drm_file *file_priv, + struct drm_device *dev, + uint32_t handle); +struct dma_buf *radeon_gem_prime_export(struct drm_device *dev, + struct drm_gem_object *obj, + int flags); +struct drm_gem_object *radeon_gem_prime_import(struct drm_device *dev, + struct dma_buf *dma_buf); + +#if defined(CONFIG_DEBUG_FS) +int radeon_debugfs_init(struct drm_minor *minor); +void radeon_debugfs_cleanup(struct drm_minor *minor); +#endif + + +int radeon_no_wb; +int radeon_modeset = 1; +int radeon_dynclks = -1; +int radeon_r4xx_atom = 0; +int radeon_agpmode = 0; +int radeon_vram_limit = 0; +int radeon_gart_size = 512; /* default gart size */ +int radeon_benchmarking = 0; +int radeon_testing = 0; +int radeon_connector_table = 0; +int radeon_tv = 1; +int radeon_audio = 0; +int radeon_disp_priority = 0; +int radeon_hw_i2c = 0; +int radeon_pcie_gen2 = -1; +int radeon_msi = -1; +int radeon_lockup_timeout = 10000; + +#ifdef DUMBBELL_WIP +MODULE_PARM_DESC(no_wb, "Disable AGP writeback for scratch registers"); +module_param_named(no_wb, radeon_no_wb, int, 0444); + +MODULE_PARM_DESC(modeset, "Disable/Enable modesetting"); +module_param_named(modeset, radeon_modeset, int, 0400); + +MODULE_PARM_DESC(dynclks, "Disable/Enable dynamic clocks"); +module_param_named(dynclks, radeon_dynclks, int, 0444); + +MODULE_PARM_DESC(r4xx_atom, "Enable ATOMBIOS modesetting for R4xx"); +module_param_named(r4xx_atom, radeon_r4xx_atom, int, 0444); + +MODULE_PARM_DESC(vramlimit, "Restrict VRAM for testing"); +module_param_named(vramlimit, radeon_vram_limit, int, 0600); + +MODULE_PARM_DESC(agpmode, "AGP Mode (-1 == PCI)"); +module_param_named(agpmode, radeon_agpmode, int, 0444); + +MODULE_PARM_DESC(gartsize, "Size of PCIE/IGP gart to setup in megabytes (32, 64, etc)"); +module_param_named(gartsize, radeon_gart_size, int, 0600); + +MODULE_PARM_DESC(benchmark, "Run benchmark"); +module_param_named(benchmark, radeon_benchmarking, int, 0444); + +MODULE_PARM_DESC(test, "Run tests"); +module_param_named(test, radeon_testing, int, 0444); + +MODULE_PARM_DESC(connector_table, "Force connector table"); +module_param_named(connector_table, radeon_connector_table, int, 0444); + +MODULE_PARM_DESC(tv, "TV enable (0 = disable)"); +module_param_named(tv, radeon_tv, int, 0444); + +MODULE_PARM_DESC(audio, "Audio enable (1 = enable)"); +module_param_named(audio, radeon_audio, int, 0444); + +MODULE_PARM_DESC(disp_priority, "Display Priority (0 = auto, 1 = normal, 2 = high)"); +module_param_named(disp_priority, radeon_disp_priority, int, 0444); + +MODULE_PARM_DESC(hw_i2c, "hw i2c engine enable (0 = disable)"); +module_param_named(hw_i2c, radeon_hw_i2c, int, 0444); + +MODULE_PARM_DESC(pcie_gen2, "PCIE Gen2 mode (-1 = auto, 0 = disable, 1 = enable)"); +module_param_named(pcie_gen2, radeon_pcie_gen2, int, 0444); + +MODULE_PARM_DESC(msi, "MSI support (1 = enable, 0 = disable, -1 = auto)"); +module_param_named(msi, radeon_msi, int, 0444); + +MODULE_PARM_DESC(lockup_timeout, "GPU lockup timeout in ms (defaul 10000 = 10 seconds, 0 = disable)"); +module_param_named(lockup_timeout, radeon_lockup_timeout, int, 0444); + +static int radeon_suspend(struct drm_device *dev, pm_message_t state) +{ + drm_radeon_private_t *dev_priv = dev->dev_private; + + if ((dev_priv->flags & RADEON_FAMILY_MASK) >= CHIP_R600) + return 0; + + /* Disable *all* interrupts */ + if ((dev_priv->flags & RADEON_FAMILY_MASK) >= CHIP_RS600) + RADEON_WRITE(R500_DxMODE_INT_MASK, 0); + RADEON_WRITE(RADEON_GEN_INT_CNTL, 0); + return 0; +} + +static int radeon_resume(struct drm_device *dev) +{ + drm_radeon_private_t *dev_priv = dev->dev_private; + + if ((dev_priv->flags & RADEON_FAMILY_MASK) >= CHIP_R600) + return 0; + + /* Restore interrupt registers */ + if ((dev_priv->flags & RADEON_FAMILY_MASK) >= CHIP_RS600) + RADEON_WRITE(R500_DxMODE_INT_MASK, dev_priv->r500_disp_irq_reg); + RADEON_WRITE(RADEON_GEN_INT_CNTL, dev_priv->irq_enable_reg); + return 0; +} +#endif /* DUMBBELL_WIP */ + +static drm_pci_id_list_t pciidlist[] = { + radeon_PCI_IDS +}; + +#ifdef DUMBBELL_WIP +static const struct file_operations radeon_driver_old_fops = { + .owner = THIS_MODULE, + .open = drm_open, + .release = drm_release, + .unlocked_ioctl = drm_ioctl, + .mmap = drm_mmap, + .poll = drm_poll, + .fasync = drm_fasync, + .read = drm_read, +#ifdef CONFIG_COMPAT + .compat_ioctl = radeon_compat_ioctl, +#endif + .llseek = noop_llseek, +}; + +static struct drm_driver driver_old = { + .driver_features = + DRIVER_USE_AGP | DRIVER_USE_MTRR | DRIVER_PCI_DMA | DRIVER_SG | + DRIVER_HAVE_IRQ | DRIVER_HAVE_DMA | DRIVER_IRQ_SHARED, + .dev_priv_size = sizeof(drm_radeon_buf_priv_t), + .load = radeon_driver_load, + .firstopen = radeon_driver_firstopen, + .open = radeon_driver_open, + .preclose = radeon_driver_preclose, + .postclose = radeon_driver_postclose, + .lastclose = radeon_driver_lastclose, + .unload = radeon_driver_unload, +#ifdef DUMBBELL_WIP + .suspend = radeon_suspend, + .resume = radeon_resume, +#endif /* DUMBBELL_WIP */ + .get_vblank_counter = radeon_get_vblank_counter, + .enable_vblank = radeon_enable_vblank, + .disable_vblank = radeon_disable_vblank, + .master_create = radeon_master_create, + .master_destroy = radeon_master_destroy, + .irq_preinstall = radeon_driver_irq_preinstall, + .irq_postinstall = radeon_driver_irq_postinstall, + .irq_uninstall = radeon_driver_irq_uninstall, + .irq_handler = radeon_driver_irq_handler, + .ioctls = radeon_ioctls, + .dma_ioctl = radeon_cp_buffers, + .fops = &radeon_driver_old_fops, + .name = DRIVER_NAME, + .desc = DRIVER_DESC, + .date = DRIVER_DATE, + .major = DRIVER_MAJOR, + .minor = DRIVER_MINOR, + .patchlevel = DRIVER_PATCHLEVEL, +}; +#endif /* DUMBBELL_WIP */ + +static struct drm_driver_info kms_driver; + +#ifdef DUMBBELL_WIP +static int radeon_kick_out_firmware_fb(struct pci_dev *pdev) +{ + struct apertures_struct *ap; + bool primary = false; + + ap = alloc_apertures(1); + if (!ap) + return -ENOMEM; + + ap->ranges[0].base = pci_resource_start(pdev, 0); + ap->ranges[0].size = pci_resource_len(pdev, 0); + +#ifdef CONFIG_X86 + primary = pdev->resource[PCI_ROM_RESOURCE].flags & IORESOURCE_ROM_SHADOW; +#endif + remove_conflicting_framebuffers(ap, "radeondrmfb", primary); + kfree(ap); + + return 0; +} + +static int radeon_pci_probe(struct pci_dev *pdev, + const struct pci_device_id *ent) +{ + int ret; + + /* Get rid of things like offb */ + ret = radeon_kick_out_firmware_fb(pdev); + if (ret) + return ret; + + return drm_get_pci_dev(pdev, ent, &kms_driver); +} + +static void +radeon_pci_remove(struct pci_dev *pdev) +{ + struct drm_device *dev = pci_get_drvdata(pdev); + + drm_put_dev(dev); +} + +static int +radeon_pci_suspend(struct pci_dev *pdev, pm_message_t state) +{ + struct drm_device *dev = pci_get_drvdata(pdev); + return radeon_suspend_kms(dev, state); +} + +static int +radeon_pci_resume(struct pci_dev *pdev) +{ + struct drm_device *dev = pci_get_drvdata(pdev); + return radeon_resume_kms(dev); +} + +static const struct file_operations radeon_driver_kms_fops = { + .owner = THIS_MODULE, + .open = drm_open, + .release = drm_release, + .unlocked_ioctl = drm_ioctl, + .mmap = radeon_mmap, + .poll = drm_poll, + .fasync = drm_fasync, + .read = drm_read, +#ifdef CONFIG_COMPAT + .compat_ioctl = radeon_kms_compat_ioctl, +#endif +}; +#endif /* DUMBBELL_WIP */ + +static struct drm_driver_info kms_driver = { + .driver_features = + DRIVER_USE_AGP | DRIVER_USE_MTRR | DRIVER_PCI_DMA | DRIVER_SG | + DRIVER_HAVE_IRQ | DRIVER_HAVE_DMA | DRIVER_IRQ_SHARED | DRIVER_GEM | + DRIVER_PRIME /* | DRIVE_MODESET */, +#ifdef DUMBBELL_WIP + .dev_priv_size = 0, +#endif /* DUMBBELL_WIP */ + .load = radeon_driver_load_kms, + .use_msi = radeon_msi_ok, + .firstopen = radeon_driver_firstopen_kms, + .open = radeon_driver_open_kms, + .preclose = radeon_driver_preclose_kms, + .postclose = radeon_driver_postclose_kms, + .lastclose = radeon_driver_lastclose_kms, + .unload = radeon_driver_unload_kms, +#ifdef DUMBBELL_WIP + .suspend = radeon_suspend_kms, + .resume = radeon_resume_kms, +#endif /* DUMBBELL_WIP */ + .get_vblank_counter = radeon_get_vblank_counter_kms, + .enable_vblank = radeon_enable_vblank_kms, + .disable_vblank = radeon_disable_vblank_kms, + .get_vblank_timestamp = radeon_get_vblank_timestamp_kms, + .get_scanout_position = radeon_get_crtc_scanoutpos, + .irq_preinstall = radeon_driver_irq_preinstall_kms, + .irq_postinstall = radeon_driver_irq_postinstall_kms, + .irq_uninstall = radeon_driver_irq_uninstall_kms, + .irq_handler = radeon_driver_irq_handler_kms, + .ioctls = radeon_ioctls_kms, + .gem_init_object = radeon_gem_object_init, + .gem_free_object = radeon_gem_object_free, + .gem_open_object = radeon_gem_object_open, + .gem_close_object = radeon_gem_object_close, + .dma_ioctl = radeon_dma_ioctl_kms, + .dumb_create = radeon_mode_dumb_create, + .dumb_map_offset = radeon_mode_dumb_mmap, + .dumb_destroy = radeon_mode_dumb_destroy, +#ifdef DUMBBELL_WIP + .fops = &radeon_driver_kms_fops, +#endif /* DUMBBELL_WIP */ + +#ifdef DUMBBELL_WIP + .prime_handle_to_fd = drm_gem_prime_handle_to_fd, + .prime_fd_to_handle = drm_gem_prime_fd_to_handle, + .gem_prime_export = radeon_gem_prime_export, + .gem_prime_import = radeon_gem_prime_import, +#endif /* DUMBBELL_WIP */ + + .name = DRIVER_NAME, + .desc = DRIVER_DESC, + .date = DRIVER_DATE, + .major = KMS_DRIVER_MAJOR, + .minor = KMS_DRIVER_MINOR, + .patchlevel = KMS_DRIVER_PATCHLEVEL, +}; + +#ifdef DUMBBELL_WIP +static int __init radeon_init(void) +{ + driver = &driver_old; + pdriver = &radeon_pci_driver; + driver->num_ioctls = radeon_max_ioctl; +#ifdef CONFIG_VGA_CONSOLE + if (vgacon_text_force() && radeon_modeset == -1) { + DRM_INFO("VGACON disable radeon kernel modesetting.\n"); + driver = &driver_old; + pdriver = &radeon_pci_driver; + driver->driver_features &= ~DRIVER_MODESET; + radeon_modeset = 0; + } +#endif + /* if enabled by default */ + if (radeon_modeset == -1) { +#ifdef CONFIG_DRM_RADEON_KMS + DRM_INFO("radeon defaulting to kernel modesetting.\n"); + radeon_modeset = 1; +#else + DRM_INFO("radeon defaulting to userspace modesetting.\n"); + radeon_modeset = 0; +#endif + } + if (radeon_modeset == 1) { + DRM_INFO("radeon kernel modesetting enabled.\n"); + driver = &kms_driver; + pdriver = &radeon_kms_pci_driver; + driver->driver_features |= DRIVER_MODESET; + driver->num_ioctls = radeon_max_kms_ioctl; + radeon_register_atpx_handler(); + } + /* if the vga console setting is enabled still + * let modprobe override it */ + return drm_pci_init(driver, pdriver); +} + +static void __exit radeon_exit(void) +{ + drm_pci_exit(driver, pdriver); + radeon_unregister_atpx_handler(); +} +#endif /* DUMBBELL_WIP */ + +/* =================================================================== */ + +static int +radeon_probe(device_t kdev) +{ + + return drm_probe(kdev, pciidlist); +} + +static int +radeon_attach(device_t kdev) +{ + struct drm_device *dev; + + dev = device_get_softc(kdev); + if (radeon_modeset == 1) { + kms_driver.driver_features |= DRIVER_MODESET; + kms_driver.max_ioctl = radeon_max_kms_ioctl; + radeon_register_atpx_handler(); + } + dev->driver = &kms_driver; + return (drm_attach(kdev, pciidlist)); +} + +static int +radeon_suspend(device_t kdev) +{ + struct drm_device *dev; + int ret; + + dev = device_get_softc(kdev); + ret = radeon_suspend_kms(dev); + + return (-ret); +} + +static int +radeon_resume(device_t kdev) +{ + struct drm_device *dev; + int ret; + + dev = device_get_softc(kdev); + ret = radeon_resume_kms(dev); + + return (-ret); +} + +static device_method_t radeon_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, radeon_probe), + DEVMETHOD(device_attach, radeon_attach), + DEVMETHOD(device_suspend, radeon_suspend), + DEVMETHOD(device_resume, radeon_resume), + DEVMETHOD(device_detach, drm_detach), + DEVMETHOD_END +}; + +static driver_t radeon_driver = { + "drmn", + radeon_methods, + sizeof(struct drm_device) +}; + +extern devclass_t drm_devclass; +DRIVER_MODULE_ORDERED(radeonkms, vgapci, radeon_driver, drm_devclass, + NULL, NULL, SI_ORDER_ANY); +MODULE_DEPEND(radeonkms, drmn, 1, 1, 1); +MODULE_DEPEND(radeonkms, agp, 1, 1, 1); +MODULE_DEPEND(radeonkms, iicbus, 1, 1, 1); +MODULE_DEPEND(radeonkms, iic, 1, 1, 1); +MODULE_DEPEND(radeonkms, iicbb, 1, 1, 1); diff --git a/sys/dev/drm2/radeon/radeon_drv.h b/sys/dev/drm2/radeon/radeon_drv.h new file mode 100644 index 00000000000..f33ea189f6e --- /dev/null +++ b/sys/dev/drm2/radeon/radeon_drv.h @@ -0,0 +1,2165 @@ +/* radeon_drv.h -- Private header for radeon driver -*- linux-c -*- + * + * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. + * Copyright 2000 VA Linux Systems, Inc., Fremont, California. + * All rights reserved. + * + * 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 (including the next + * paragraph) 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 + * PRECISION INSIGHT AND/OR ITS SUPPLIERS 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. + * + * Authors: + * Kevin E. Martin + * Gareth Hughes + */ + +#include +__FBSDID("$FreeBSD$"); + +#ifndef __RADEON_DRV_H__ +#define __RADEON_DRV_H__ + +#include "radeon_family.h" + +/* General customization: + */ + +#define DRIVER_AUTHOR "Gareth Hughes, Keith Whitwell, others." + +#define DRIVER_NAME "radeon" +#define DRIVER_DESC "ATI Radeon" +#define DRIVER_DATE "20080528" + +/* Interface history: + * + * 1.1 - ?? + * 1.2 - Add vertex2 ioctl (keith) + * - Add stencil capability to clear ioctl (gareth, keith) + * - Increase MAX_TEXTURE_LEVELS (brian) + * 1.3 - Add cmdbuf ioctl (keith) + * - Add support for new radeon packets (keith) + * - Add getparam ioctl (keith) + * - Add flip-buffers ioctl, deprecate fullscreen foo (keith). + * 1.4 - Add scratch registers to get_param ioctl. + * 1.5 - Add r200 packets to cmdbuf ioctl + * - Add r200 function to init ioctl + * - Add 'scalar2' instruction to cmdbuf + * 1.6 - Add static GART memory manager + * Add irq handler (won't be turned on unless X server knows to) + * Add irq ioctls and irq_active getparam. + * Add wait command for cmdbuf ioctl + * Add GART offset query for getparam + * 1.7 - Add support for cube map registers: R200_PP_CUBIC_FACES_[0..5] + * and R200_PP_CUBIC_OFFSET_F1_[0..5]. + * Added packets R200_EMIT_PP_CUBIC_FACES_[0..5] and + * R200_EMIT_PP_CUBIC_OFFSETS_[0..5]. (brian) + * 1.8 - Remove need to call cleanup ioctls on last client exit (keith) + * Add 'GET' queries for starting additional clients on different VT's. + * 1.9 - Add DRM_IOCTL_RADEON_CP_RESUME ioctl. + * Add texture rectangle support for r100. + * 1.10- Add SETPARAM ioctl; first parameter to set is FB_LOCATION, which + * clients use to tell the DRM where they think the framebuffer is + * located in the card's address space + * 1.11- Add packet R200_EMIT_RB3D_BLENDCOLOR to support GL_EXT_blend_color + * and GL_EXT_blend_[func|equation]_separate on r200 + * 1.12- Add R300 CP microcode support - this just loads the CP on r300 + * (No 3D support yet - just microcode loading). + * 1.13- Add packet R200_EMIT_TCL_POINT_SPRITE_CNTL for ARB_point_parameters + * - Add hyperz support, add hyperz flags to clear ioctl. + * 1.14- Add support for color tiling + * - Add R100/R200 surface allocation/free support + * 1.15- Add support for texture micro tiling + * - Add support for r100 cube maps + * 1.16- Add R200_EMIT_PP_TRI_PERF_CNTL packet to support brilinear + * texture filtering on r200 + * 1.17- Add initial support for R300 (3D). + * 1.18- Add support for GL_ATI_fragment_shader, new packets + * R200_EMIT_PP_AFS_0/1, R200_EMIT_PP_TXCTLALL_0-5 (replaces + * R200_EMIT_PP_TXFILTER_0-5, 2 more regs) and R200_EMIT_ATF_TFACTOR + * (replaces R200_EMIT_TFACTOR_0 (8 consts instead of 6) + * 1.19- Add support for gart table in FB memory and PCIE r300 + * 1.20- Add support for r300 texrect + * 1.21- Add support for card type getparam + * 1.22- Add support for texture cache flushes (R300_TX_CNTL) + * 1.23- Add new radeon memory map work from benh + * 1.24- Add general-purpose packet for manipulating scratch registers (r300) + * 1.25- Add support for r200 vertex programs (R200_EMIT_VAP_PVS_CNTL, + * new packet type) + * 1.26- Add support for variable size PCI(E) gart aperture + * 1.27- Add support for IGP GART + * 1.28- Add support for VBL on CRTC2 + * 1.29- R500 3D cmd buffer support + * 1.30- Add support for occlusion queries + * 1.31- Add support for num Z pipes from GET_PARAM + * 1.32- fixes for rv740 setup + * 1.33- Add r6xx/r7xx const buffer support + */ +#define DRIVER_MAJOR 1 +#define DRIVER_MINOR 33 +#define DRIVER_PATCHLEVEL 0 + +enum radeon_cp_microcode_version { + UCODE_R100, + UCODE_R200, + UCODE_R300, +}; + +typedef struct drm_radeon_freelist { + unsigned int age; + struct drm_buf *buf; + struct drm_radeon_freelist *next; + struct drm_radeon_freelist *prev; +} drm_radeon_freelist_t; + +typedef struct drm_radeon_ring_buffer { + u32 *start; + u32 *end; + int size; + int size_l2qw; + + int rptr_update; /* Double Words */ + int rptr_update_l2qw; /* log2 Quad Words */ + + int fetch_size; /* Double Words */ + int fetch_size_l2ow; /* log2 Oct Words */ + + u32 tail; + u32 tail_mask; + int space; + + int high_mark; +} drm_radeon_ring_buffer_t; + +typedef struct drm_radeon_depth_clear_t { + u32 rb3d_cntl; + u32 rb3d_zstencilcntl; + u32 se_cntl; +} drm_radeon_depth_clear_t; + +struct drm_radeon_driver_file_fields { + int64_t radeon_fb_delta; +}; + +struct mem_block { + struct mem_block *next; + struct mem_block *prev; + int start; + int size; + struct drm_file *file_priv; /* NULL: free, -1: heap, other: real files */ +}; + +struct radeon_surface { + int refcount; + u32 lower; + u32 upper; + u32 flags; +}; + +struct radeon_virt_surface { + int surface_index; + u32 lower; + u32 upper; + u32 flags; + struct drm_file *file_priv; +#define PCIGART_FILE_PRIV ((void *) -1L) +}; + +#define RADEON_FLUSH_EMITED (1 << 0) +#define RADEON_PURGE_EMITED (1 << 1) + +struct drm_radeon_master_private { + drm_local_map_t *sarea; + drm_radeon_sarea_t *sarea_priv; +}; + +typedef struct drm_radeon_private { + drm_radeon_ring_buffer_t ring; + + u32 fb_location; + u32 fb_size; + int new_memmap; + + int gart_size; + u32 gart_vm_start; + unsigned long gart_buffers_offset; + + int cp_mode; + int cp_running; + + drm_radeon_freelist_t *head; + drm_radeon_freelist_t *tail; + int last_buf; + int writeback_works; + + int usec_timeout; + + int microcode_version; + + struct { + u32 boxes; + int freelist_timeouts; + int freelist_loops; + int requested_bufs; + int last_frame_reads; + int last_clear_reads; + int clears; + int texture_uploads; + } stats; + + int do_boxes; + int page_flipping; + + u32 color_fmt; + unsigned int front_offset; + unsigned int front_pitch; + unsigned int back_offset; + unsigned int back_pitch; + + u32 depth_fmt; + unsigned int depth_offset; + unsigned int depth_pitch; + + u32 front_pitch_offset; + u32 back_pitch_offset; + u32 depth_pitch_offset; + + drm_radeon_depth_clear_t depth_clear; + + unsigned long ring_offset; + unsigned long ring_rptr_offset; + unsigned long buffers_offset; + unsigned long gart_textures_offset; + + drm_local_map_t *sarea; + drm_local_map_t *cp_ring; + drm_local_map_t *ring_rptr; + drm_local_map_t *gart_textures; + + struct mem_block *gart_heap; + struct mem_block *fb_heap; + + /* SW interrupt */ + wait_queue_head_t swi_queue; + atomic_t swi_emitted; + int vblank_crtc; + uint32_t irq_enable_reg; + uint32_t r500_disp_irq_reg; + + struct radeon_surface surfaces[RADEON_MAX_SURFACES]; + struct radeon_virt_surface virt_surfaces[2 * RADEON_MAX_SURFACES]; + + unsigned long pcigart_offset; + unsigned int pcigart_offset_set; + struct drm_ati_pcigart_info gart_info; + + u32 scratch_ages[5]; + + int have_z_offset; + + /* starting from here on, data is preserved across an open */ + uint32_t flags; /* see radeon_chip_flags */ + resource_size_t fb_aper_offset; + + int num_gb_pipes; + int num_z_pipes; + int track_flush; + drm_local_map_t *mmio; + + /* r6xx/r7xx pipe/shader config */ + int r600_max_pipes; + int r600_max_tile_pipes; + int r600_max_simds; + int r600_max_backends; + int r600_max_gprs; + int r600_max_threads; + int r600_max_stack_entries; + int r600_max_hw_contexts; + int r600_max_gs_threads; + int r600_sx_max_export_size; + int r600_sx_max_export_pos_size; + int r600_sx_max_export_smx_size; + int r600_sq_num_cf_insts; + int r700_sx_num_of_sets; + int r700_sc_prim_fifo_size; + int r700_sc_hiz_tile_fifo_size; + int r700_sc_earlyz_tile_fifo_fize; + int r600_group_size; + int r600_npipes; + int r600_nbanks; + + struct sx cs_mutex; + u32 cs_id_scnt; + u32 cs_id_wcnt; + /* r6xx/r7xx drm blit vertex buffer */ + struct drm_buf *blit_vb; + + /* firmware */ + const struct firmware *me_fw, *pfp_fw; +} drm_radeon_private_t; + +typedef struct drm_radeon_buf_priv { + u32 age; +} drm_radeon_buf_priv_t; + +struct drm_buffer; + +typedef struct drm_radeon_kcmd_buffer { + int bufsz; + struct drm_buffer *buffer; + int nbox; + struct drm_clip_rect __user *boxes; +} drm_radeon_kcmd_buffer_t; + +extern int radeon_no_wb; +extern struct drm_ioctl_desc radeon_ioctls[]; +extern int radeon_max_ioctl; + +extern u32 radeon_get_ring_head(drm_radeon_private_t *dev_priv); +extern void radeon_set_ring_head(drm_radeon_private_t *dev_priv, u32 val); + +#define GET_RING_HEAD(dev_priv) radeon_get_ring_head(dev_priv) +#define SET_RING_HEAD(dev_priv, val) radeon_set_ring_head(dev_priv, val) + +/* Check whether the given hardware address is inside the framebuffer or the + * GART area. + */ +static __inline__ int radeon_check_offset(drm_radeon_private_t *dev_priv, + u64 off) +{ + u32 fb_start = dev_priv->fb_location; + u32 fb_end = fb_start + dev_priv->fb_size - 1; + u32 gart_start = dev_priv->gart_vm_start; + u32 gart_end = gart_start + dev_priv->gart_size - 1; + + return ((off >= fb_start && off <= fb_end) || + (off >= gart_start && off <= gart_end)); +} + +/* radeon_state.c */ +extern void radeon_cp_discard_buffer(struct drm_device *dev, struct drm_master *master, struct drm_buf *buf); + + /* radeon_cp.c */ +extern int radeon_cp_init(struct drm_device *dev, void *data, struct drm_file *file_priv); +extern int radeon_cp_start(struct drm_device *dev, void *data, struct drm_file *file_priv); +extern int radeon_cp_stop(struct drm_device *dev, void *data, struct drm_file *file_priv); +extern int radeon_cp_reset(struct drm_device *dev, void *data, struct drm_file *file_priv); +extern int radeon_cp_idle(struct drm_device *dev, void *data, struct drm_file *file_priv); +extern int radeon_cp_resume(struct drm_device *dev, void *data, struct drm_file *file_priv); +extern int radeon_engine_reset(struct drm_device *dev, void *data, struct drm_file *file_priv); +extern int radeon_fullscreen(struct drm_device *dev, void *data, struct drm_file *file_priv); +extern int radeon_cp_buffers(struct drm_device *dev, void *data, struct drm_file *file_priv); +extern u32 radeon_read_fb_location(drm_radeon_private_t *dev_priv); +extern void radeon_write_agp_location(drm_radeon_private_t *dev_priv, u32 agp_loc); +extern void radeon_write_agp_base(drm_radeon_private_t *dev_priv, u64 agp_base); + +extern void radeon_freelist_reset(struct drm_device * dev); +extern struct drm_buf *radeon_freelist_get(struct drm_device * dev); + +extern int radeon_wait_ring(drm_radeon_private_t * dev_priv, int n); + +extern int radeon_do_cp_idle(drm_radeon_private_t * dev_priv); + +extern int radeon_driver_preinit(struct drm_device *dev, unsigned long flags); +extern int radeon_presetup(struct drm_device *dev); +extern int radeon_driver_postcleanup(struct drm_device *dev); + +extern int radeon_mem_alloc(struct drm_device *dev, void *data, struct drm_file *file_priv); +extern int radeon_mem_free(struct drm_device *dev, void *data, struct drm_file *file_priv); +extern int radeon_mem_init_heap(struct drm_device *dev, void *data, struct drm_file *file_priv); +extern void radeon_mem_takedown(struct mem_block **heap); +extern void radeon_mem_release(struct drm_file *file_priv, + struct mem_block *heap); + +extern void radeon_enable_bm(struct drm_radeon_private *dev_priv); +extern u32 radeon_read_ring_rptr(drm_radeon_private_t *dev_priv, u32 off); +extern void radeon_write_ring_rptr(drm_radeon_private_t *dev_priv, u32 off, u32 val); + + /* radeon_irq.c */ +extern void radeon_irq_set_state(struct drm_device *dev, u32 mask, int state); +extern int radeon_irq_emit(struct drm_device *dev, void *data, struct drm_file *file_priv); +extern int radeon_irq_wait(struct drm_device *dev, void *data, struct drm_file *file_priv); + +extern void radeon_do_release(struct drm_device * dev); +extern u32 radeon_get_vblank_counter(struct drm_device *dev, int crtc); +extern int radeon_enable_vblank(struct drm_device *dev, int crtc); +extern void radeon_disable_vblank(struct drm_device *dev, int crtc); +extern irqreturn_t radeon_driver_irq_handler(DRM_IRQ_ARGS); +extern void radeon_driver_irq_preinstall(struct drm_device * dev); +extern int radeon_driver_irq_postinstall(struct drm_device *dev); +extern void radeon_driver_irq_uninstall(struct drm_device * dev); +extern void radeon_enable_interrupt(struct drm_device *dev); +extern int radeon_vblank_crtc_get(struct drm_device *dev); +extern int radeon_vblank_crtc_set(struct drm_device *dev, int64_t value); + +extern int radeon_driver_load(struct drm_device *dev, unsigned long flags); +extern int radeon_driver_unload(struct drm_device *dev); +extern int radeon_driver_firstopen(struct drm_device *dev); +extern void radeon_driver_preclose(struct drm_device *dev, + struct drm_file *file_priv); +extern void radeon_driver_postclose(struct drm_device *dev, + struct drm_file *file_priv); +extern void radeon_driver_lastclose(struct drm_device * dev); +extern int radeon_driver_open(struct drm_device *dev, + struct drm_file *file_priv); +extern long radeon_compat_ioctl(struct file *filp, unsigned int cmd, + unsigned long arg); +extern long radeon_kms_compat_ioctl(struct file *filp, unsigned int cmd, + unsigned long arg); + +extern int radeon_master_create(struct drm_device *dev, struct drm_master *master); +extern void radeon_master_destroy(struct drm_device *dev, struct drm_master *master); +extern void radeon_cp_dispatch_flip(struct drm_device *dev, struct drm_master *master); +/* r300_cmdbuf.c */ +extern void r300_init_reg_flags(struct drm_device *dev); + +extern int r300_do_cp_cmdbuf(struct drm_device *dev, + struct drm_file *file_priv, + drm_radeon_kcmd_buffer_t *cmdbuf); + +/* r600_cp.c */ +extern int r600_do_engine_reset(struct drm_device *dev); +extern int r600_do_cleanup_cp(struct drm_device *dev); +extern int r600_do_init_cp(struct drm_device *dev, drm_radeon_init_t *init, + struct drm_file *file_priv); +extern int r600_do_resume_cp(struct drm_device *dev, struct drm_file *file_priv); +extern int r600_do_cp_idle(drm_radeon_private_t *dev_priv); +extern void r600_do_cp_start(drm_radeon_private_t *dev_priv); +extern void r600_do_cp_reset(drm_radeon_private_t *dev_priv); +extern void r600_do_cp_stop(drm_radeon_private_t *dev_priv); +extern int r600_cp_dispatch_indirect(struct drm_device *dev, + struct drm_buf *buf, int start, int end); +extern int r600_page_table_init(struct drm_device *dev); +extern void r600_page_table_cleanup(struct drm_device *dev, struct drm_ati_pcigart_info *gart_info); +extern int r600_cs_legacy_ioctl(struct drm_device *dev, void *data, struct drm_file *fpriv); +extern void r600_cp_dispatch_swap(struct drm_device *dev, struct drm_file *file_priv); +extern int r600_cp_dispatch_texture(struct drm_device *dev, + struct drm_file *file_priv, + drm_radeon_texture_t *tex, + drm_radeon_tex_image_t *image); +/* r600_blit.c */ +extern int r600_prepare_blit_copy(struct drm_device *dev, struct drm_file *file_priv); +extern void r600_done_blit_copy(struct drm_device *dev); +extern void r600_blit_copy(struct drm_device *dev, + uint64_t src_gpu_addr, uint64_t dst_gpu_addr, + int size_bytes); +extern void r600_blit_swap(struct drm_device *dev, + uint64_t src_gpu_addr, uint64_t dst_gpu_addr, + int sx, int sy, int dx, int dy, + int w, int h, int src_pitch, int dst_pitch, int cpp); + +/* atpx handler */ +void radeon_register_atpx_handler(void); +void radeon_unregister_atpx_handler(void); + +/* Flags for stats.boxes + */ +#define RADEON_BOX_DMA_IDLE 0x1 +#define RADEON_BOX_RING_FULL 0x2 +#define RADEON_BOX_FLIP 0x4 +#define RADEON_BOX_WAIT_IDLE 0x8 +#define RADEON_BOX_TEXTURE_LOAD 0x10 + +/* Register definitions, register access macros and drmAddMap constants + * for Radeon kernel driver. + */ +#define RADEON_MM_INDEX 0x0000 +#define RADEON_MM_DATA 0x0004 + +#define RADEON_AGP_COMMAND 0x0f60 +#define RADEON_AGP_COMMAND_PCI_CONFIG 0x0060 /* offset in PCI config */ +# define RADEON_AGP_ENABLE (1<<8) +#define RADEON_AUX_SCISSOR_CNTL 0x26f0 +# define RADEON_EXCLUSIVE_SCISSOR_0 (1 << 24) +# define RADEON_EXCLUSIVE_SCISSOR_1 (1 << 25) +# define RADEON_EXCLUSIVE_SCISSOR_2 (1 << 26) +# define RADEON_SCISSOR_0_ENABLE (1 << 28) +# define RADEON_SCISSOR_1_ENABLE (1 << 29) +# define RADEON_SCISSOR_2_ENABLE (1 << 30) + +/* + * PCIE radeons (rv370/rv380, rv410, r423/r430/r480, r5xx) + * don't have an explicit bus mastering disable bit. It's handled + * by the PCI D-states. PMI_BM_DIS disables D-state bus master + * handling, not bus mastering itself. + */ +#define RADEON_BUS_CNTL 0x0030 +/* r1xx, r2xx, r300, r(v)350, r420/r481, rs400/rs480 */ +# define RADEON_BUS_MASTER_DIS (1 << 6) +/* rs600/rs690/rs740 */ +# define RS600_BUS_MASTER_DIS (1 << 14) +# define RS600_MSI_REARM (1 << 20) +/* see RS400_MSI_REARM in AIC_CNTL for rs480 */ + +#define RADEON_BUS_CNTL1 0x0034 +# define RADEON_PMI_BM_DIS (1 << 2) +# define RADEON_PMI_INT_DIS (1 << 3) + +#define RV370_BUS_CNTL 0x004c +# define RV370_PMI_BM_DIS (1 << 5) +# define RV370_PMI_INT_DIS (1 << 6) + +#define RADEON_MSI_REARM_EN 0x0160 +/* rv370/rv380, rv410, r423/r430/r480, r5xx */ +# define RV370_MSI_REARM_EN (1 << 0) + +#define RADEON_CLOCK_CNTL_DATA 0x000c +# define RADEON_PLL_WR_EN (1 << 7) +#define RADEON_CLOCK_CNTL_INDEX 0x0008 +#define RADEON_CONFIG_APER_SIZE 0x0108 +#define RADEON_CONFIG_MEMSIZE 0x00f8 +#define RADEON_CRTC_OFFSET 0x0224 +#define RADEON_CRTC_OFFSET_CNTL 0x0228 +# define RADEON_CRTC_TILE_EN (1 << 15) +# define RADEON_CRTC_OFFSET_FLIP_CNTL (1 << 16) +#define RADEON_CRTC2_OFFSET 0x0324 +#define RADEON_CRTC2_OFFSET_CNTL 0x0328 + +#define RADEON_PCIE_INDEX 0x0030 +#define RADEON_PCIE_DATA 0x0034 +#define RADEON_PCIE_TX_GART_CNTL 0x10 +# define RADEON_PCIE_TX_GART_EN (1 << 0) +# define RADEON_PCIE_TX_GART_UNMAPPED_ACCESS_PASS_THRU (0 << 1) +# define RADEON_PCIE_TX_GART_UNMAPPED_ACCESS_CLAMP_LO (1 << 1) +# define RADEON_PCIE_TX_GART_UNMAPPED_ACCESS_DISCARD (3 << 1) +# define RADEON_PCIE_TX_GART_MODE_32_128_CACHE (0 << 3) +# define RADEON_PCIE_TX_GART_MODE_8_4_128_CACHE (1 << 3) +# define RADEON_PCIE_TX_GART_CHK_RW_VALID_EN (1 << 5) +# define RADEON_PCIE_TX_GART_INVALIDATE_TLB (1 << 8) +#define RADEON_PCIE_TX_DISCARD_RD_ADDR_LO 0x11 +#define RADEON_PCIE_TX_DISCARD_RD_ADDR_HI 0x12 +#define RADEON_PCIE_TX_GART_BASE 0x13 +#define RADEON_PCIE_TX_GART_START_LO 0x14 +#define RADEON_PCIE_TX_GART_START_HI 0x15 +#define RADEON_PCIE_TX_GART_END_LO 0x16 +#define RADEON_PCIE_TX_GART_END_HI 0x17 + +#define RS480_NB_MC_INDEX 0x168 +# define RS480_NB_MC_IND_WR_EN (1 << 8) +#define RS480_NB_MC_DATA 0x16c + +#define RS690_MC_INDEX 0x78 +# define RS690_MC_INDEX_MASK 0x1ff +# define RS690_MC_INDEX_WR_EN (1 << 9) +# define RS690_MC_INDEX_WR_ACK 0x7f +#define RS690_MC_DATA 0x7c + +/* MC indirect registers */ +#define RS480_MC_MISC_CNTL 0x18 +# define RS480_DISABLE_GTW (1 << 1) +/* switch between MCIND GART and MM GART registers. 0 = mmgart, 1 = mcind gart */ +# define RS480_GART_INDEX_REG_EN (1 << 12) +# define RS690_BLOCK_GFX_D3_EN (1 << 14) +#define RS480_K8_FB_LOCATION 0x1e +#define RS480_GART_FEATURE_ID 0x2b +# define RS480_HANG_EN (1 << 11) +# define RS480_TLB_ENABLE (1 << 18) +# define RS480_P2P_ENABLE (1 << 19) +# define RS480_GTW_LAC_EN (1 << 25) +# define RS480_2LEVEL_GART (0 << 30) +# define RS480_1LEVEL_GART (1 << 30) +# define RS480_PDC_EN (1 << 31) +#define RS480_GART_BASE 0x2c +#define RS480_GART_CACHE_CNTRL 0x2e +# define RS480_GART_CACHE_INVALIDATE (1 << 0) /* wait for it to clear */ +#define RS480_AGP_ADDRESS_SPACE_SIZE 0x38 +# define RS480_GART_EN (1 << 0) +# define RS480_VA_SIZE_32MB (0 << 1) +# define RS480_VA_SIZE_64MB (1 << 1) +# define RS480_VA_SIZE_128MB (2 << 1) +# define RS480_VA_SIZE_256MB (3 << 1) +# define RS480_VA_SIZE_512MB (4 << 1) +# define RS480_VA_SIZE_1GB (5 << 1) +# define RS480_VA_SIZE_2GB (6 << 1) +#define RS480_AGP_MODE_CNTL 0x39 +# define RS480_POST_GART_Q_SIZE (1 << 18) +# define RS480_NONGART_SNOOP (1 << 19) +# define RS480_AGP_RD_BUF_SIZE (1 << 20) +# define RS480_REQ_TYPE_SNOOP_SHIFT 22 +# define RS480_REQ_TYPE_SNOOP_MASK 0x3 +# define RS480_REQ_TYPE_SNOOP_DIS (1 << 24) +#define RS480_MC_MISC_UMA_CNTL 0x5f +#define RS480_MC_MCLK_CNTL 0x7a +#define RS480_MC_UMA_DUALCH_CNTL 0x86 + +#define RS690_MC_FB_LOCATION 0x100 +#define RS690_MC_AGP_LOCATION 0x101 +#define RS690_MC_AGP_BASE 0x102 +#define RS690_MC_AGP_BASE_2 0x103 + +#define RS600_MC_INDEX 0x70 +# define RS600_MC_ADDR_MASK 0xffff +# define RS600_MC_IND_SEQ_RBS_0 (1 << 16) +# define RS600_MC_IND_SEQ_RBS_1 (1 << 17) +# define RS600_MC_IND_SEQ_RBS_2 (1 << 18) +# define RS600_MC_IND_SEQ_RBS_3 (1 << 19) +# define RS600_MC_IND_AIC_RBS (1 << 20) +# define RS600_MC_IND_CITF_ARB0 (1 << 21) +# define RS600_MC_IND_CITF_ARB1 (1 << 22) +# define RS600_MC_IND_WR_EN (1 << 23) +#define RS600_MC_DATA 0x74 + +#define RS600_MC_STATUS 0x0 +# define RS600_MC_IDLE (1 << 1) +#define RS600_MC_FB_LOCATION 0x4 +#define RS600_MC_AGP_LOCATION 0x5 +#define RS600_AGP_BASE 0x6 +#define RS600_AGP_BASE_2 0x7 +#define RS600_MC_CNTL1 0x9 +# define RS600_ENABLE_PAGE_TABLES (1 << 26) +#define RS600_MC_PT0_CNTL 0x100 +# define RS600_ENABLE_PT (1 << 0) +# define RS600_EFFECTIVE_L2_CACHE_SIZE(x) ((x) << 15) +# define RS600_EFFECTIVE_L2_QUEUE_SIZE(x) ((x) << 21) +# define RS600_INVALIDATE_ALL_L1_TLBS (1 << 28) +# define RS600_INVALIDATE_L2_CACHE (1 << 29) +#define RS600_MC_PT0_CONTEXT0_CNTL 0x102 +# define RS600_ENABLE_PAGE_TABLE (1 << 0) +# define RS600_PAGE_TABLE_TYPE_FLAT (0 << 1) +#define RS600_MC_PT0_SYSTEM_APERTURE_LOW_ADDR 0x112 +#define RS600_MC_PT0_SYSTEM_APERTURE_HIGH_ADDR 0x114 +#define RS600_MC_PT0_CONTEXT0_DEFAULT_READ_ADDR 0x11c +#define RS600_MC_PT0_CONTEXT0_FLAT_BASE_ADDR 0x12c +#define RS600_MC_PT0_CONTEXT0_FLAT_START_ADDR 0x13c +#define RS600_MC_PT0_CONTEXT0_FLAT_END_ADDR 0x14c +#define RS600_MC_PT0_CLIENT0_CNTL 0x16c +# define RS600_ENABLE_TRANSLATION_MODE_OVERRIDE (1 << 0) +# define RS600_TRANSLATION_MODE_OVERRIDE (1 << 1) +# define RS600_SYSTEM_ACCESS_MODE_MASK (3 << 8) +# define RS600_SYSTEM_ACCESS_MODE_PA_ONLY (0 << 8) +# define RS600_SYSTEM_ACCESS_MODE_USE_SYS_MAP (1 << 8) +# define RS600_SYSTEM_ACCESS_MODE_IN_SYS (2 << 8) +# define RS600_SYSTEM_ACCESS_MODE_NOT_IN_SYS (3 << 8) +# define RS600_SYSTEM_APERTURE_UNMAPPED_ACCESS_PASSTHROUGH (0 << 10) +# define RS600_SYSTEM_APERTURE_UNMAPPED_ACCESS_DEFAULT_PAGE (1 << 10) +# define RS600_EFFECTIVE_L1_CACHE_SIZE(x) ((x) << 11) +# define RS600_ENABLE_FRAGMENT_PROCESSING (1 << 14) +# define RS600_EFFECTIVE_L1_QUEUE_SIZE(x) ((x) << 15) +# define RS600_INVALIDATE_L1_TLB (1 << 20) + +#define R520_MC_IND_INDEX 0x70 +#define R520_MC_IND_WR_EN (1 << 24) +#define R520_MC_IND_DATA 0x74 + +#define RV515_MC_FB_LOCATION 0x01 +#define RV515_MC_AGP_LOCATION 0x02 +#define RV515_MC_AGP_BASE 0x03 +#define RV515_MC_AGP_BASE_2 0x04 + +#define R520_MC_FB_LOCATION 0x04 +#define R520_MC_AGP_LOCATION 0x05 +#define R520_MC_AGP_BASE 0x06 +#define R520_MC_AGP_BASE_2 0x07 + +#define RADEON_MPP_TB_CONFIG 0x01c0 +#define RADEON_MEM_CNTL 0x0140 +#define RADEON_MEM_SDRAM_MODE_REG 0x0158 +#define RADEON_AGP_BASE_2 0x015c /* r200+ only */ +#define RS480_AGP_BASE_2 0x0164 +#define RADEON_AGP_BASE 0x0170 + +/* pipe config regs */ +#define R400_GB_PIPE_SELECT 0x402c +#define RV530_GB_PIPE_SELECT2 0x4124 +#define R500_DYN_SCLK_PWMEM_PIPE 0x000d /* PLL */ +#define R300_GB_TILE_CONFIG 0x4018 +# define R300_ENABLE_TILING (1 << 0) +# define R300_PIPE_COUNT_RV350 (0 << 1) +# define R300_PIPE_COUNT_R300 (3 << 1) +# define R300_PIPE_COUNT_R420_3P (6 << 1) +# define R300_PIPE_COUNT_R420 (7 << 1) +# define R300_TILE_SIZE_8 (0 << 4) +# define R300_TILE_SIZE_16 (1 << 4) +# define R300_TILE_SIZE_32 (2 << 4) +# define R300_SUBPIXEL_1_12 (0 << 16) +# define R300_SUBPIXEL_1_16 (1 << 16) +#define R300_DST_PIPE_CONFIG 0x170c +# define R300_PIPE_AUTO_CONFIG (1 << 31) +#define R300_RB2D_DSTCACHE_MODE 0x3428 +# define R300_DC_AUTOFLUSH_ENABLE (1 << 8) +# define R300_DC_DC_DISABLE_IGNORE_PE (1 << 17) + +#define RADEON_RB3D_COLOROFFSET 0x1c40 +#define RADEON_RB3D_COLORPITCH 0x1c48 + +#define RADEON_SRC_X_Y 0x1590 + +#define RADEON_DP_GUI_MASTER_CNTL 0x146c +# define RADEON_GMC_SRC_PITCH_OFFSET_CNTL (1 << 0) +# define RADEON_GMC_DST_PITCH_OFFSET_CNTL (1 << 1) +# define RADEON_GMC_BRUSH_SOLID_COLOR (13 << 4) +# define RADEON_GMC_BRUSH_NONE (15 << 4) +# define RADEON_GMC_DST_16BPP (4 << 8) +# define RADEON_GMC_DST_24BPP (5 << 8) +# define RADEON_GMC_DST_32BPP (6 << 8) +# define RADEON_GMC_DST_DATATYPE_SHIFT 8 +# define RADEON_GMC_SRC_DATATYPE_COLOR (3 << 12) +# define RADEON_DP_SRC_SOURCE_MEMORY (2 << 24) +# define RADEON_DP_SRC_SOURCE_HOST_DATA (3 << 24) +# define RADEON_GMC_CLR_CMP_CNTL_DIS (1 << 28) +# define RADEON_GMC_WR_MSK_DIS (1 << 30) +# define RADEON_ROP3_S 0x00cc0000 +# define RADEON_ROP3_P 0x00f00000 +#define RADEON_DP_WRITE_MASK 0x16cc +#define RADEON_SRC_PITCH_OFFSET 0x1428 +#define RADEON_DST_PITCH_OFFSET 0x142c +#define RADEON_DST_PITCH_OFFSET_C 0x1c80 +# define RADEON_DST_TILE_LINEAR (0 << 30) +# define RADEON_DST_TILE_MACRO (1 << 30) +# define RADEON_DST_TILE_MICRO (2 << 30) +# define RADEON_DST_TILE_BOTH (3 << 30) + +#define RADEON_SCRATCH_REG0 0x15e0 +#define RADEON_SCRATCH_REG1 0x15e4 +#define RADEON_SCRATCH_REG2 0x15e8 +#define RADEON_SCRATCH_REG3 0x15ec +#define RADEON_SCRATCH_REG4 0x15f0 +#define RADEON_SCRATCH_REG5 0x15f4 +#define RADEON_SCRATCH_UMSK 0x0770 +#define RADEON_SCRATCH_ADDR 0x0774 + +#define RADEON_SCRATCHOFF( x ) (RADEON_SCRATCH_REG_OFFSET + 4*(x)) + +extern u32 radeon_get_scratch(drm_radeon_private_t *dev_priv, int index); + +#define GET_SCRATCH(dev_priv, x) radeon_get_scratch(dev_priv, x) + +#define R600_SCRATCH_REG0 0x8500 +#define R600_SCRATCH_REG1 0x8504 +#define R600_SCRATCH_REG2 0x8508 +#define R600_SCRATCH_REG3 0x850c +#define R600_SCRATCH_REG4 0x8510 +#define R600_SCRATCH_REG5 0x8514 +#define R600_SCRATCH_REG6 0x8518 +#define R600_SCRATCH_REG7 0x851c +#define R600_SCRATCH_UMSK 0x8540 +#define R600_SCRATCH_ADDR 0x8544 + +#define R600_SCRATCHOFF(x) (R600_SCRATCH_REG_OFFSET + 4*(x)) + +#define RADEON_GEN_INT_CNTL 0x0040 +# define RADEON_CRTC_VBLANK_MASK (1 << 0) +# define RADEON_CRTC2_VBLANK_MASK (1 << 9) +# define RADEON_GUI_IDLE_INT_ENABLE (1 << 19) +# define RADEON_SW_INT_ENABLE (1 << 25) + +#define RADEON_GEN_INT_STATUS 0x0044 +# define RADEON_CRTC_VBLANK_STAT (1 << 0) +# define RADEON_CRTC_VBLANK_STAT_ACK (1 << 0) +# define RADEON_CRTC2_VBLANK_STAT (1 << 9) +# define RADEON_CRTC2_VBLANK_STAT_ACK (1 << 9) +# define RADEON_GUI_IDLE_INT_TEST_ACK (1 << 19) +# define RADEON_SW_INT_TEST (1 << 25) +# define RADEON_SW_INT_TEST_ACK (1 << 25) +# define RADEON_SW_INT_FIRE (1 << 26) +# define R500_DISPLAY_INT_STATUS (1 << 0) + +#define RADEON_HOST_PATH_CNTL 0x0130 +# define RADEON_HDP_SOFT_RESET (1 << 26) +# define RADEON_HDP_WC_TIMEOUT_MASK (7 << 28) +# define RADEON_HDP_WC_TIMEOUT_28BCLK (7 << 28) + +#define RADEON_ISYNC_CNTL 0x1724 +# define RADEON_ISYNC_ANY2D_IDLE3D (1 << 0) +# define RADEON_ISYNC_ANY3D_IDLE2D (1 << 1) +# define RADEON_ISYNC_TRIG2D_IDLE3D (1 << 2) +# define RADEON_ISYNC_TRIG3D_IDLE2D (1 << 3) +# define RADEON_ISYNC_WAIT_IDLEGUI (1 << 4) +# define RADEON_ISYNC_CPSCRATCH_IDLEGUI (1 << 5) + +#define RADEON_RBBM_GUICNTL 0x172c +# define RADEON_HOST_DATA_SWAP_NONE (0 << 0) +# define RADEON_HOST_DATA_SWAP_16BIT (1 << 0) +# define RADEON_HOST_DATA_SWAP_32BIT (2 << 0) +# define RADEON_HOST_DATA_SWAP_HDW (3 << 0) + +#define RADEON_MC_AGP_LOCATION 0x014c +#define RADEON_MC_FB_LOCATION 0x0148 +#define RADEON_MCLK_CNTL 0x0012 +# define RADEON_FORCEON_MCLKA (1 << 16) +# define RADEON_FORCEON_MCLKB (1 << 17) +# define RADEON_FORCEON_YCLKA (1 << 18) +# define RADEON_FORCEON_YCLKB (1 << 19) +# define RADEON_FORCEON_MC (1 << 20) +# define RADEON_FORCEON_AIC (1 << 21) + +#define RADEON_PP_BORDER_COLOR_0 0x1d40 +#define RADEON_PP_BORDER_COLOR_1 0x1d44 +#define RADEON_PP_BORDER_COLOR_2 0x1d48 +#define RADEON_PP_CNTL 0x1c38 +# define RADEON_SCISSOR_ENABLE (1 << 1) +#define RADEON_PP_LUM_MATRIX 0x1d00 +#define RADEON_PP_MISC 0x1c14 +#define RADEON_PP_ROT_MATRIX_0 0x1d58 +#define RADEON_PP_TXFILTER_0 0x1c54 +#define RADEON_PP_TXOFFSET_0 0x1c5c +#define RADEON_PP_TXFILTER_1 0x1c6c +#define RADEON_PP_TXFILTER_2 0x1c84 + +#define R300_RB2D_DSTCACHE_CTLSTAT 0x342c /* use R300_DSTCACHE_CTLSTAT */ +#define R300_DSTCACHE_CTLSTAT 0x1714 +# define R300_RB2D_DC_FLUSH (3 << 0) +# define R300_RB2D_DC_FREE (3 << 2) +# define R300_RB2D_DC_FLUSH_ALL 0xf +# define R300_RB2D_DC_BUSY (1 << 31) +#define RADEON_RB3D_CNTL 0x1c3c +# define RADEON_ALPHA_BLEND_ENABLE (1 << 0) +# define RADEON_PLANE_MASK_ENABLE (1 << 1) +# define RADEON_DITHER_ENABLE (1 << 2) +# define RADEON_ROUND_ENABLE (1 << 3) +# define RADEON_SCALE_DITHER_ENABLE (1 << 4) +# define RADEON_DITHER_INIT (1 << 5) +# define RADEON_ROP_ENABLE (1 << 6) +# define RADEON_STENCIL_ENABLE (1 << 7) +# define RADEON_Z_ENABLE (1 << 8) +# define RADEON_ZBLOCK16 (1 << 15) +#define RADEON_RB3D_DEPTHOFFSET 0x1c24 +#define RADEON_RB3D_DEPTHCLEARVALUE 0x3230 +#define RADEON_RB3D_DEPTHPITCH 0x1c28 +#define RADEON_RB3D_PLANEMASK 0x1d84 +#define RADEON_RB3D_STENCILREFMASK 0x1d7c +#define RADEON_RB3D_ZCACHE_MODE 0x3250 +#define RADEON_RB3D_ZCACHE_CTLSTAT 0x3254 +# define RADEON_RB3D_ZC_FLUSH (1 << 0) +# define RADEON_RB3D_ZC_FREE (1 << 2) +# define RADEON_RB3D_ZC_FLUSH_ALL 0x5 +# define RADEON_RB3D_ZC_BUSY (1 << 31) +#define R300_ZB_ZCACHE_CTLSTAT 0x4f18 +# define R300_ZC_FLUSH (1 << 0) +# define R300_ZC_FREE (1 << 1) +# define R300_ZC_BUSY (1 << 31) +#define RADEON_RB3D_DSTCACHE_CTLSTAT 0x325c +# define RADEON_RB3D_DC_FLUSH (3 << 0) +# define RADEON_RB3D_DC_FREE (3 << 2) +# define RADEON_RB3D_DC_FLUSH_ALL 0xf +# define RADEON_RB3D_DC_BUSY (1 << 31) +#define R300_RB3D_DSTCACHE_CTLSTAT 0x4e4c +# define R300_RB3D_DC_FLUSH (2 << 0) +# define R300_RB3D_DC_FREE (2 << 2) +# define R300_RB3D_DC_FINISH (1 << 4) +#define RADEON_RB3D_ZSTENCILCNTL 0x1c2c +# define RADEON_Z_TEST_MASK (7 << 4) +# define RADEON_Z_TEST_ALWAYS (7 << 4) +# define RADEON_Z_HIERARCHY_ENABLE (1 << 8) +# define RADEON_STENCIL_TEST_ALWAYS (7 << 12) +# define RADEON_STENCIL_S_FAIL_REPLACE (2 << 16) +# define RADEON_STENCIL_ZPASS_REPLACE (2 << 20) +# define RADEON_STENCIL_ZFAIL_REPLACE (2 << 24) +# define RADEON_Z_COMPRESSION_ENABLE (1 << 28) +# define RADEON_FORCE_Z_DIRTY (1 << 29) +# define RADEON_Z_WRITE_ENABLE (1 << 30) +# define RADEON_Z_DECOMPRESSION_ENABLE (1 << 31) +#define RADEON_RBBM_SOFT_RESET 0x00f0 +# define RADEON_SOFT_RESET_CP (1 << 0) +# define RADEON_SOFT_RESET_HI (1 << 1) +# define RADEON_SOFT_RESET_SE (1 << 2) +# define RADEON_SOFT_RESET_RE (1 << 3) +# define RADEON_SOFT_RESET_PP (1 << 4) +# define RADEON_SOFT_RESET_E2 (1 << 5) +# define RADEON_SOFT_RESET_RB (1 << 6) +# define RADEON_SOFT_RESET_HDP (1 << 7) +/* + * 6:0 Available slots in the FIFO + * 8 Host Interface active + * 9 CP request active + * 10 FIFO request active + * 11 Host Interface retry active + * 12 CP retry active + * 13 FIFO retry active + * 14 FIFO pipeline busy + * 15 Event engine busy + * 16 CP command stream busy + * 17 2D engine busy + * 18 2D portion of render backend busy + * 20 3D setup engine busy + * 26 GA engine busy + * 27 CBA 2D engine busy + * 31 2D engine busy or 3D engine busy or FIFO not empty or CP busy or + * command stream queue not empty or Ring Buffer not empty + */ +#define RADEON_RBBM_STATUS 0x0e40 +/* Same as the previous RADEON_RBBM_STATUS; this is a mirror of that register. */ +/* #define RADEON_RBBM_STATUS 0x1740 */ +/* bits 6:0 are dword slots available in the cmd fifo */ +# define RADEON_RBBM_FIFOCNT_MASK 0x007f +# define RADEON_HIRQ_ON_RBB (1 << 8) +# define RADEON_CPRQ_ON_RBB (1 << 9) +# define RADEON_CFRQ_ON_RBB (1 << 10) +# define RADEON_HIRQ_IN_RTBUF (1 << 11) +# define RADEON_CPRQ_IN_RTBUF (1 << 12) +# define RADEON_CFRQ_IN_RTBUF (1 << 13) +# define RADEON_PIPE_BUSY (1 << 14) +# define RADEON_ENG_EV_BUSY (1 << 15) +# define RADEON_CP_CMDSTRM_BUSY (1 << 16) +# define RADEON_E2_BUSY (1 << 17) +# define RADEON_RB2D_BUSY (1 << 18) +# define RADEON_RB3D_BUSY (1 << 19) /* not used on r300 */ +# define RADEON_VAP_BUSY (1 << 20) +# define RADEON_RE_BUSY (1 << 21) /* not used on r300 */ +# define RADEON_TAM_BUSY (1 << 22) /* not used on r300 */ +# define RADEON_TDM_BUSY (1 << 23) /* not used on r300 */ +# define RADEON_PB_BUSY (1 << 24) /* not used on r300 */ +# define RADEON_TIM_BUSY (1 << 25) /* not used on r300 */ +# define RADEON_GA_BUSY (1 << 26) +# define RADEON_CBA2D_BUSY (1 << 27) +# define RADEON_RBBM_ACTIVE (1 << 31) +#define RADEON_RE_LINE_PATTERN 0x1cd0 +#define RADEON_RE_MISC 0x26c4 +#define RADEON_RE_TOP_LEFT 0x26c0 +#define RADEON_RE_WIDTH_HEIGHT 0x1c44 +#define RADEON_RE_STIPPLE_ADDR 0x1cc8 +#define RADEON_RE_STIPPLE_DATA 0x1ccc + +#define RADEON_SCISSOR_TL_0 0x1cd8 +#define RADEON_SCISSOR_BR_0 0x1cdc +#define RADEON_SCISSOR_TL_1 0x1ce0 +#define RADEON_SCISSOR_BR_1 0x1ce4 +#define RADEON_SCISSOR_TL_2 0x1ce8 +#define RADEON_SCISSOR_BR_2 0x1cec +#define RADEON_SE_COORD_FMT 0x1c50 +#define RADEON_SE_CNTL 0x1c4c +# define RADEON_FFACE_CULL_CW (0 << 0) +# define RADEON_BFACE_SOLID (3 << 1) +# define RADEON_FFACE_SOLID (3 << 3) +# define RADEON_FLAT_SHADE_VTX_LAST (3 << 6) +# define RADEON_DIFFUSE_SHADE_FLAT (1 << 8) +# define RADEON_DIFFUSE_SHADE_GOURAUD (2 << 8) +# define RADEON_ALPHA_SHADE_FLAT (1 << 10) +# define RADEON_ALPHA_SHADE_GOURAUD (2 << 10) +# define RADEON_SPECULAR_SHADE_FLAT (1 << 12) +# define RADEON_SPECULAR_SHADE_GOURAUD (2 << 12) +# define RADEON_FOG_SHADE_FLAT (1 << 14) +# define RADEON_FOG_SHADE_GOURAUD (2 << 14) +# define RADEON_VPORT_XY_XFORM_ENABLE (1 << 24) +# define RADEON_VPORT_Z_XFORM_ENABLE (1 << 25) +# define RADEON_VTX_PIX_CENTER_OGL (1 << 27) +# define RADEON_ROUND_MODE_TRUNC (0 << 28) +# define RADEON_ROUND_PREC_8TH_PIX (1 << 30) +#define RADEON_SE_CNTL_STATUS 0x2140 +#define RADEON_SE_LINE_WIDTH 0x1db8 +#define RADEON_SE_VPORT_XSCALE 0x1d98 +#define RADEON_SE_ZBIAS_FACTOR 0x1db0 +#define RADEON_SE_TCL_MATERIAL_EMMISSIVE_RED 0x2210 +#define RADEON_SE_TCL_OUTPUT_VTX_FMT 0x2254 +#define RADEON_SE_TCL_VECTOR_INDX_REG 0x2200 +# define RADEON_VEC_INDX_OCTWORD_STRIDE_SHIFT 16 +# define RADEON_VEC_INDX_DWORD_COUNT_SHIFT 28 +#define RADEON_SE_TCL_VECTOR_DATA_REG 0x2204 +#define RADEON_SE_TCL_SCALAR_INDX_REG 0x2208 +# define RADEON_SCAL_INDX_DWORD_STRIDE_SHIFT 16 +#define RADEON_SE_TCL_SCALAR_DATA_REG 0x220C +#define RADEON_SURFACE_ACCESS_FLAGS 0x0bf8 +#define RADEON_SURFACE_ACCESS_CLR 0x0bfc +#define RADEON_SURFACE_CNTL 0x0b00 +# define RADEON_SURF_TRANSLATION_DIS (1 << 8) +# define RADEON_NONSURF_AP0_SWP_MASK (3 << 20) +# define RADEON_NONSURF_AP0_SWP_LITTLE (0 << 20) +# define RADEON_NONSURF_AP0_SWP_BIG16 (1 << 20) +# define RADEON_NONSURF_AP0_SWP_BIG32 (2 << 20) +# define RADEON_NONSURF_AP1_SWP_MASK (3 << 22) +# define RADEON_NONSURF_AP1_SWP_LITTLE (0 << 22) +# define RADEON_NONSURF_AP1_SWP_BIG16 (1 << 22) +# define RADEON_NONSURF_AP1_SWP_BIG32 (2 << 22) +#define RADEON_SURFACE0_INFO 0x0b0c +# define RADEON_SURF_PITCHSEL_MASK (0x1ff << 0) +# define RADEON_SURF_TILE_MODE_MASK (3 << 16) +# define RADEON_SURF_TILE_MODE_MACRO (0 << 16) +# define RADEON_SURF_TILE_MODE_MICRO (1 << 16) +# define RADEON_SURF_TILE_MODE_32BIT_Z (2 << 16) +# define RADEON_SURF_TILE_MODE_16BIT_Z (3 << 16) +#define RADEON_SURFACE0_LOWER_BOUND 0x0b04 +#define RADEON_SURFACE0_UPPER_BOUND 0x0b08 +# define RADEON_SURF_ADDRESS_FIXED_MASK (0x3ff << 0) +#define RADEON_SURFACE1_INFO 0x0b1c +#define RADEON_SURFACE1_LOWER_BOUND 0x0b14 +#define RADEON_SURFACE1_UPPER_BOUND 0x0b18 +#define RADEON_SURFACE2_INFO 0x0b2c +#define RADEON_SURFACE2_LOWER_BOUND 0x0b24 +#define RADEON_SURFACE2_UPPER_BOUND 0x0b28 +#define RADEON_SURFACE3_INFO 0x0b3c +#define RADEON_SURFACE3_LOWER_BOUND 0x0b34 +#define RADEON_SURFACE3_UPPER_BOUND 0x0b38 +#define RADEON_SURFACE4_INFO 0x0b4c +#define RADEON_SURFACE4_LOWER_BOUND 0x0b44 +#define RADEON_SURFACE4_UPPER_BOUND 0x0b48 +#define RADEON_SURFACE5_INFO 0x0b5c +#define RADEON_SURFACE5_LOWER_BOUND 0x0b54 +#define RADEON_SURFACE5_UPPER_BOUND 0x0b58 +#define RADEON_SURFACE6_INFO 0x0b6c +#define RADEON_SURFACE6_LOWER_BOUND 0x0b64 +#define RADEON_SURFACE6_UPPER_BOUND 0x0b68 +#define RADEON_SURFACE7_INFO 0x0b7c +#define RADEON_SURFACE7_LOWER_BOUND 0x0b74 +#define RADEON_SURFACE7_UPPER_BOUND 0x0b78 +#define RADEON_SW_SEMAPHORE 0x013c + +#define RADEON_WAIT_UNTIL 0x1720 +# define RADEON_WAIT_CRTC_PFLIP (1 << 0) +# define RADEON_WAIT_2D_IDLE (1 << 14) +# define RADEON_WAIT_3D_IDLE (1 << 15) +# define RADEON_WAIT_2D_IDLECLEAN (1 << 16) +# define RADEON_WAIT_3D_IDLECLEAN (1 << 17) +# define RADEON_WAIT_HOST_IDLECLEAN (1 << 18) + +#define RADEON_RB3D_ZMASKOFFSET 0x3234 +#define RADEON_RB3D_ZSTENCILCNTL 0x1c2c +# define RADEON_DEPTH_FORMAT_16BIT_INT_Z (0 << 0) +# define RADEON_DEPTH_FORMAT_24BIT_INT_Z (2 << 0) + +/* CP registers */ +#define RADEON_CP_ME_RAM_ADDR 0x07d4 +#define RADEON_CP_ME_RAM_RADDR 0x07d8 +#define RADEON_CP_ME_RAM_DATAH 0x07dc +#define RADEON_CP_ME_RAM_DATAL 0x07e0 + +#define RADEON_CP_RB_BASE 0x0700 +#define RADEON_CP_RB_CNTL 0x0704 +# define RADEON_BUF_SWAP_32BIT (2 << 16) +# define RADEON_RB_NO_UPDATE (1 << 27) +# define RADEON_RB_RPTR_WR_ENA (1 << 31) +#define RADEON_CP_RB_RPTR_ADDR 0x070c +#define RADEON_CP_RB_RPTR 0x0710 +#define RADEON_CP_RB_WPTR 0x0714 + +#define RADEON_CP_RB_WPTR_DELAY 0x0718 +# define RADEON_PRE_WRITE_TIMER_SHIFT 0 +# define RADEON_PRE_WRITE_LIMIT_SHIFT 23 + +#define RADEON_CP_IB_BASE 0x0738 + +#define RADEON_CP_CSQ_CNTL 0x0740 +# define RADEON_CSQ_CNT_PRIMARY_MASK (0xff << 0) +# define RADEON_CSQ_PRIDIS_INDDIS (0 << 28) +# define RADEON_CSQ_PRIPIO_INDDIS (1 << 28) +# define RADEON_CSQ_PRIBM_INDDIS (2 << 28) +# define RADEON_CSQ_PRIPIO_INDBM (3 << 28) +# define RADEON_CSQ_PRIBM_INDBM (4 << 28) +# define RADEON_CSQ_PRIPIO_INDPIO (15 << 28) + +#define R300_CP_RESYNC_ADDR 0x0778 +#define R300_CP_RESYNC_DATA 0x077c + +#define RADEON_AIC_CNTL 0x01d0 +# define RADEON_PCIGART_TRANSLATE_EN (1 << 0) +# define RS400_MSI_REARM (1 << 3) +#define RADEON_AIC_STAT 0x01d4 +#define RADEON_AIC_PT_BASE 0x01d8 +#define RADEON_AIC_LO_ADDR 0x01dc +#define RADEON_AIC_HI_ADDR 0x01e0 +#define RADEON_AIC_TLB_ADDR 0x01e4 +#define RADEON_AIC_TLB_DATA 0x01e8 + +/* CP command packets */ +#define RADEON_CP_PACKET0 0x00000000 +# define RADEON_ONE_REG_WR (1 << 15) +#define RADEON_CP_PACKET1 0x40000000 +#define RADEON_CP_PACKET2 0x80000000 +#define RADEON_CP_PACKET3 0xC0000000 +# define RADEON_CP_NOP 0x00001000 +# define RADEON_CP_NEXT_CHAR 0x00001900 +# define RADEON_CP_PLY_NEXTSCAN 0x00001D00 +# define RADEON_CP_SET_SCISSORS 0x00001E00 + /* GEN_INDX_PRIM is unsupported starting with R300 */ +# define RADEON_3D_RNDR_GEN_INDX_PRIM 0x00002300 +# define RADEON_WAIT_FOR_IDLE 0x00002600 +# define RADEON_3D_DRAW_VBUF 0x00002800 +# define RADEON_3D_DRAW_IMMD 0x00002900 +# define RADEON_3D_DRAW_INDX 0x00002A00 +# define RADEON_CP_LOAD_PALETTE 0x00002C00 +# define RADEON_3D_LOAD_VBPNTR 0x00002F00 +# define RADEON_MPEG_IDCT_MACROBLOCK 0x00003000 +# define RADEON_MPEG_IDCT_MACROBLOCK_REV 0x00003100 +# define RADEON_3D_CLEAR_ZMASK 0x00003200 +# define RADEON_CP_INDX_BUFFER 0x00003300 +# define RADEON_CP_3D_DRAW_VBUF_2 0x00003400 +# define RADEON_CP_3D_DRAW_IMMD_2 0x00003500 +# define RADEON_CP_3D_DRAW_INDX_2 0x00003600 +# define RADEON_3D_CLEAR_HIZ 0x00003700 +# define RADEON_CP_3D_CLEAR_CMASK 0x00003802 +# define RADEON_CNTL_HOSTDATA_BLT 0x00009400 +# define RADEON_CNTL_PAINT_MULTI 0x00009A00 +# define RADEON_CNTL_BITBLT_MULTI 0x00009B00 +# define RADEON_CNTL_SET_SCISSORS 0xC0001E00 + +# define R600_IT_INDIRECT_BUFFER_END 0x00001700 +# define R600_IT_SET_PREDICATION 0x00002000 +# define R600_IT_REG_RMW 0x00002100 +# define R600_IT_COND_EXEC 0x00002200 +# define R600_IT_PRED_EXEC 0x00002300 +# define R600_IT_START_3D_CMDBUF 0x00002400 +# define R600_IT_DRAW_INDEX_2 0x00002700 +# define R600_IT_CONTEXT_CONTROL 0x00002800 +# define R600_IT_DRAW_INDEX_IMMD_BE 0x00002900 +# define R600_IT_INDEX_TYPE 0x00002A00 +# define R600_IT_DRAW_INDEX 0x00002B00 +# define R600_IT_DRAW_INDEX_AUTO 0x00002D00 +# define R600_IT_DRAW_INDEX_IMMD 0x00002E00 +# define R600_IT_NUM_INSTANCES 0x00002F00 +# define R600_IT_STRMOUT_BUFFER_UPDATE 0x00003400 +# define R600_IT_INDIRECT_BUFFER_MP 0x00003800 +# define R600_IT_MEM_SEMAPHORE 0x00003900 +# define R600_IT_MPEG_INDEX 0x00003A00 +# define R600_IT_WAIT_REG_MEM 0x00003C00 +# define R600_IT_MEM_WRITE 0x00003D00 +# define R600_IT_INDIRECT_BUFFER 0x00003200 +# define R600_IT_SURFACE_SYNC 0x00004300 +# define R600_CB0_DEST_BASE_ENA (1 << 6) +# define R600_TC_ACTION_ENA (1 << 23) +# define R600_VC_ACTION_ENA (1 << 24) +# define R600_CB_ACTION_ENA (1 << 25) +# define R600_DB_ACTION_ENA (1 << 26) +# define R600_SH_ACTION_ENA (1 << 27) +# define R600_SMX_ACTION_ENA (1 << 28) +# define R600_IT_ME_INITIALIZE 0x00004400 +# define R600_ME_INITIALIZE_DEVICE_ID(x) ((x) << 16) +# define R600_IT_COND_WRITE 0x00004500 +# define R600_IT_EVENT_WRITE 0x00004600 +# define R600_IT_EVENT_WRITE_EOP 0x00004700 +# define R600_IT_ONE_REG_WRITE 0x00005700 +# define R600_IT_SET_CONFIG_REG 0x00006800 +# define R600_SET_CONFIG_REG_OFFSET 0x00008000 +# define R600_SET_CONFIG_REG_END 0x0000ac00 +# define R600_IT_SET_CONTEXT_REG 0x00006900 +# define R600_SET_CONTEXT_REG_OFFSET 0x00028000 +# define R600_SET_CONTEXT_REG_END 0x00029000 +# define R600_IT_SET_ALU_CONST 0x00006A00 +# define R600_SET_ALU_CONST_OFFSET 0x00030000 +# define R600_SET_ALU_CONST_END 0x00032000 +# define R600_IT_SET_BOOL_CONST 0x00006B00 +# define R600_SET_BOOL_CONST_OFFSET 0x0003e380 +# define R600_SET_BOOL_CONST_END 0x00040000 +# define R600_IT_SET_LOOP_CONST 0x00006C00 +# define R600_SET_LOOP_CONST_OFFSET 0x0003e200 +# define R600_SET_LOOP_CONST_END 0x0003e380 +# define R600_IT_SET_RESOURCE 0x00006D00 +# define R600_SET_RESOURCE_OFFSET 0x00038000 +# define R600_SET_RESOURCE_END 0x0003c000 +# define R600_SQ_TEX_VTX_INVALID_TEXTURE 0x0 +# define R600_SQ_TEX_VTX_INVALID_BUFFER 0x1 +# define R600_SQ_TEX_VTX_VALID_TEXTURE 0x2 +# define R600_SQ_TEX_VTX_VALID_BUFFER 0x3 +# define R600_IT_SET_SAMPLER 0x00006E00 +# define R600_SET_SAMPLER_OFFSET 0x0003c000 +# define R600_SET_SAMPLER_END 0x0003cff0 +# define R600_IT_SET_CTL_CONST 0x00006F00 +# define R600_SET_CTL_CONST_OFFSET 0x0003cff0 +# define R600_SET_CTL_CONST_END 0x0003e200 +# define R600_IT_SURFACE_BASE_UPDATE 0x00007300 + +#define RADEON_CP_PACKET_MASK 0xC0000000 +#define RADEON_CP_PACKET_COUNT_MASK 0x3fff0000 +#define RADEON_CP_PACKET0_REG_MASK 0x000007ff +#define RADEON_CP_PACKET1_REG0_MASK 0x000007ff +#define RADEON_CP_PACKET1_REG1_MASK 0x003ff800 + +#define RADEON_VTX_Z_PRESENT (1 << 31) +#define RADEON_VTX_PKCOLOR_PRESENT (1 << 3) + +#define RADEON_PRIM_TYPE_NONE (0 << 0) +#define RADEON_PRIM_TYPE_POINT (1 << 0) +#define RADEON_PRIM_TYPE_LINE (2 << 0) +#define RADEON_PRIM_TYPE_LINE_STRIP (3 << 0) +#define RADEON_PRIM_TYPE_TRI_LIST (4 << 0) +#define RADEON_PRIM_TYPE_TRI_FAN (5 << 0) +#define RADEON_PRIM_TYPE_TRI_STRIP (6 << 0) +#define RADEON_PRIM_TYPE_TRI_TYPE2 (7 << 0) +#define RADEON_PRIM_TYPE_RECT_LIST (8 << 0) +#define RADEON_PRIM_TYPE_3VRT_POINT_LIST (9 << 0) +#define RADEON_PRIM_TYPE_3VRT_LINE_LIST (10 << 0) +#define RADEON_PRIM_TYPE_MASK 0xf +#define RADEON_PRIM_WALK_IND (1 << 4) +#define RADEON_PRIM_WALK_LIST (2 << 4) +#define RADEON_PRIM_WALK_RING (3 << 4) +#define RADEON_COLOR_ORDER_BGRA (0 << 6) +#define RADEON_COLOR_ORDER_RGBA (1 << 6) +#define RADEON_MAOS_ENABLE (1 << 7) +#define RADEON_VTX_FMT_R128_MODE (0 << 8) +#define RADEON_VTX_FMT_RADEON_MODE (1 << 8) +#define RADEON_NUM_VERTICES_SHIFT 16 + +#define RADEON_COLOR_FORMAT_CI8 2 +#define RADEON_COLOR_FORMAT_ARGB1555 3 +#define RADEON_COLOR_FORMAT_RGB565 4 +#define RADEON_COLOR_FORMAT_ARGB8888 6 +#define RADEON_COLOR_FORMAT_RGB332 7 +#define RADEON_COLOR_FORMAT_RGB8 9 +#define RADEON_COLOR_FORMAT_ARGB4444 15 + +#define RADEON_TXFORMAT_I8 0 +#define RADEON_TXFORMAT_AI88 1 +#define RADEON_TXFORMAT_RGB332 2 +#define RADEON_TXFORMAT_ARGB1555 3 +#define RADEON_TXFORMAT_RGB565 4 +#define RADEON_TXFORMAT_ARGB4444 5 +#define RADEON_TXFORMAT_ARGB8888 6 +#define RADEON_TXFORMAT_RGBA8888 7 +#define RADEON_TXFORMAT_Y8 8 +#define RADEON_TXFORMAT_VYUY422 10 +#define RADEON_TXFORMAT_YVYU422 11 +#define RADEON_TXFORMAT_DXT1 12 +#define RADEON_TXFORMAT_DXT23 14 +#define RADEON_TXFORMAT_DXT45 15 + +#define R200_PP_TXCBLEND_0 0x2f00 +#define R200_PP_TXCBLEND_1 0x2f10 +#define R200_PP_TXCBLEND_2 0x2f20 +#define R200_PP_TXCBLEND_3 0x2f30 +#define R200_PP_TXCBLEND_4 0x2f40 +#define R200_PP_TXCBLEND_5 0x2f50 +#define R200_PP_TXCBLEND_6 0x2f60 +#define R200_PP_TXCBLEND_7 0x2f70 +#define R200_SE_TCL_LIGHT_MODEL_CTL_0 0x2268 +#define R200_PP_TFACTOR_0 0x2ee0 +#define R200_SE_VTX_FMT_0 0x2088 +#define R200_SE_VAP_CNTL 0x2080 +#define R200_SE_TCL_MATRIX_SEL_0 0x2230 +#define R200_SE_TCL_TEX_PROC_CTL_2 0x22a8 +#define R200_SE_TCL_UCP_VERT_BLEND_CTL 0x22c0 +#define R200_PP_TXFILTER_5 0x2ca0 +#define R200_PP_TXFILTER_4 0x2c80 +#define R200_PP_TXFILTER_3 0x2c60 +#define R200_PP_TXFILTER_2 0x2c40 +#define R200_PP_TXFILTER_1 0x2c20 +#define R200_PP_TXFILTER_0 0x2c00 +#define R200_PP_TXOFFSET_5 0x2d78 +#define R200_PP_TXOFFSET_4 0x2d60 +#define R200_PP_TXOFFSET_3 0x2d48 +#define R200_PP_TXOFFSET_2 0x2d30 +#define R200_PP_TXOFFSET_1 0x2d18 +#define R200_PP_TXOFFSET_0 0x2d00 + +#define R200_PP_CUBIC_FACES_0 0x2c18 +#define R200_PP_CUBIC_FACES_1 0x2c38 +#define R200_PP_CUBIC_FACES_2 0x2c58 +#define R200_PP_CUBIC_FACES_3 0x2c78 +#define R200_PP_CUBIC_FACES_4 0x2c98 +#define R200_PP_CUBIC_FACES_5 0x2cb8 +#define R200_PP_CUBIC_OFFSET_F1_0 0x2d04 +#define R200_PP_CUBIC_OFFSET_F2_0 0x2d08 +#define R200_PP_CUBIC_OFFSET_F3_0 0x2d0c +#define R200_PP_CUBIC_OFFSET_F4_0 0x2d10 +#define R200_PP_CUBIC_OFFSET_F5_0 0x2d14 +#define R200_PP_CUBIC_OFFSET_F1_1 0x2d1c +#define R200_PP_CUBIC_OFFSET_F2_1 0x2d20 +#define R200_PP_CUBIC_OFFSET_F3_1 0x2d24 +#define R200_PP_CUBIC_OFFSET_F4_1 0x2d28 +#define R200_PP_CUBIC_OFFSET_F5_1 0x2d2c +#define R200_PP_CUBIC_OFFSET_F1_2 0x2d34 +#define R200_PP_CUBIC_OFFSET_F2_2 0x2d38 +#define R200_PP_CUBIC_OFFSET_F3_2 0x2d3c +#define R200_PP_CUBIC_OFFSET_F4_2 0x2d40 +#define R200_PP_CUBIC_OFFSET_F5_2 0x2d44 +#define R200_PP_CUBIC_OFFSET_F1_3 0x2d4c +#define R200_PP_CUBIC_OFFSET_F2_3 0x2d50 +#define R200_PP_CUBIC_OFFSET_F3_3 0x2d54 +#define R200_PP_CUBIC_OFFSET_F4_3 0x2d58 +#define R200_PP_CUBIC_OFFSET_F5_3 0x2d5c +#define R200_PP_CUBIC_OFFSET_F1_4 0x2d64 +#define R200_PP_CUBIC_OFFSET_F2_4 0x2d68 +#define R200_PP_CUBIC_OFFSET_F3_4 0x2d6c +#define R200_PP_CUBIC_OFFSET_F4_4 0x2d70 +#define R200_PP_CUBIC_OFFSET_F5_4 0x2d74 +#define R200_PP_CUBIC_OFFSET_F1_5 0x2d7c +#define R200_PP_CUBIC_OFFSET_F2_5 0x2d80 +#define R200_PP_CUBIC_OFFSET_F3_5 0x2d84 +#define R200_PP_CUBIC_OFFSET_F4_5 0x2d88 +#define R200_PP_CUBIC_OFFSET_F5_5 0x2d8c + +#define R200_RE_AUX_SCISSOR_CNTL 0x26f0 +#define R200_SE_VTE_CNTL 0x20b0 +#define R200_SE_TCL_OUTPUT_VTX_COMP_SEL 0x2250 +#define R200_PP_TAM_DEBUG3 0x2d9c +#define R200_PP_CNTL_X 0x2cc4 +#define R200_SE_VAP_CNTL_STATUS 0x2140 +#define R200_RE_SCISSOR_TL_0 0x1cd8 +#define R200_RE_SCISSOR_TL_1 0x1ce0 +#define R200_RE_SCISSOR_TL_2 0x1ce8 +#define R200_RB3D_DEPTHXY_OFFSET 0x1d60 +#define R200_RE_AUX_SCISSOR_CNTL 0x26f0 +#define R200_SE_VTX_STATE_CNTL 0x2180 +#define R200_RE_POINTSIZE 0x2648 +#define R200_SE_TCL_INPUT_VTX_VECTOR_ADDR_0 0x2254 + +#define RADEON_PP_TEX_SIZE_0 0x1d04 /* NPOT */ +#define RADEON_PP_TEX_SIZE_1 0x1d0c +#define RADEON_PP_TEX_SIZE_2 0x1d14 + +#define RADEON_PP_CUBIC_FACES_0 0x1d24 +#define RADEON_PP_CUBIC_FACES_1 0x1d28 +#define RADEON_PP_CUBIC_FACES_2 0x1d2c +#define RADEON_PP_CUBIC_OFFSET_T0_0 0x1dd0 /* bits [31:5] */ +#define RADEON_PP_CUBIC_OFFSET_T1_0 0x1e00 +#define RADEON_PP_CUBIC_OFFSET_T2_0 0x1e14 + +#define RADEON_SE_TCL_STATE_FLUSH 0x2284 + +#define SE_VAP_CNTL__TCL_ENA_MASK 0x00000001 +#define SE_VAP_CNTL__FORCE_W_TO_ONE_MASK 0x00010000 +#define SE_VAP_CNTL__VF_MAX_VTX_NUM__SHIFT 0x00000012 +#define SE_VTE_CNTL__VTX_XY_FMT_MASK 0x00000100 +#define SE_VTE_CNTL__VTX_Z_FMT_MASK 0x00000200 +#define SE_VTX_FMT_0__VTX_Z0_PRESENT_MASK 0x00000001 +#define SE_VTX_FMT_0__VTX_W0_PRESENT_MASK 0x00000002 +#define SE_VTX_FMT_0__VTX_COLOR_0_FMT__SHIFT 0x0000000b +#define R200_3D_DRAW_IMMD_2 0xC0003500 +#define R200_SE_VTX_FMT_1 0x208c +#define R200_RE_CNTL 0x1c50 + +#define R200_RB3D_BLENDCOLOR 0x3218 + +#define R200_SE_TCL_POINT_SPRITE_CNTL 0x22c4 + +#define R200_PP_TRI_PERF 0x2cf8 + +#define R200_PP_AFS_0 0x2f80 +#define R200_PP_AFS_1 0x2f00 /* same as txcblend_0 */ + +#define R200_VAP_PVS_CNTL_1 0x22D0 + +#define RADEON_CRTC_CRNT_FRAME 0x0214 +#define RADEON_CRTC2_CRNT_FRAME 0x0314 + +#define R500_D1CRTC_STATUS 0x609c +#define R500_D2CRTC_STATUS 0x689c +#define R500_CRTC_V_BLANK (1<<0) + +#define R500_D1CRTC_FRAME_COUNT 0x60a4 +#define R500_D2CRTC_FRAME_COUNT 0x68a4 + +#define R500_D1MODE_V_COUNTER 0x6530 +#define R500_D2MODE_V_COUNTER 0x6d30 + +#define R500_D1MODE_VBLANK_STATUS 0x6534 +#define R500_D2MODE_VBLANK_STATUS 0x6d34 +#define R500_VBLANK_OCCURED (1<<0) +#define R500_VBLANK_ACK (1<<4) +#define R500_VBLANK_STAT (1<<12) +#define R500_VBLANK_INT (1<<16) + +#define R500_DxMODE_INT_MASK 0x6540 +#define R500_D1MODE_INT_MASK (1<<0) +#define R500_D2MODE_INT_MASK (1<<8) + +#define R500_DISP_INTERRUPT_STATUS 0x7edc +#define R500_D1_VBLANK_INTERRUPT (1 << 4) +#define R500_D2_VBLANK_INTERRUPT (1 << 5) + +/* R6xx/R7xx registers */ +#define R600_MC_VM_FB_LOCATION 0x2180 +#define R600_MC_VM_AGP_TOP 0x2184 +#define R600_MC_VM_AGP_BOT 0x2188 +#define R600_MC_VM_AGP_BASE 0x218c +#define R600_MC_VM_SYSTEM_APERTURE_LOW_ADDR 0x2190 +#define R600_MC_VM_SYSTEM_APERTURE_HIGH_ADDR 0x2194 +#define R600_MC_VM_SYSTEM_APERTURE_DEFAULT_ADDR 0x2198 + +#define R700_MC_VM_FB_LOCATION 0x2024 +#define R700_MC_VM_AGP_TOP 0x2028 +#define R700_MC_VM_AGP_BOT 0x202c +#define R700_MC_VM_AGP_BASE 0x2030 +#define R700_MC_VM_SYSTEM_APERTURE_LOW_ADDR 0x2034 +#define R700_MC_VM_SYSTEM_APERTURE_HIGH_ADDR 0x2038 +#define R700_MC_VM_SYSTEM_APERTURE_DEFAULT_ADDR 0x203c + +#define R600_MCD_RD_A_CNTL 0x219c +#define R600_MCD_RD_B_CNTL 0x21a0 + +#define R600_MCD_WR_A_CNTL 0x21a4 +#define R600_MCD_WR_B_CNTL 0x21a8 + +#define R600_MCD_RD_SYS_CNTL 0x2200 +#define R600_MCD_WR_SYS_CNTL 0x2214 + +#define R600_MCD_RD_GFX_CNTL 0x21fc +#define R600_MCD_RD_HDP_CNTL 0x2204 +#define R600_MCD_RD_PDMA_CNTL 0x2208 +#define R600_MCD_RD_SEM_CNTL 0x220c +#define R600_MCD_WR_GFX_CNTL 0x2210 +#define R600_MCD_WR_HDP_CNTL 0x2218 +#define R600_MCD_WR_PDMA_CNTL 0x221c +#define R600_MCD_WR_SEM_CNTL 0x2220 + +# define R600_MCD_L1_TLB (1 << 0) +# define R600_MCD_L1_FRAG_PROC (1 << 1) +# define R600_MCD_L1_STRICT_ORDERING (1 << 2) + +# define R600_MCD_SYSTEM_ACCESS_MODE_MASK (3 << 6) +# define R600_MCD_SYSTEM_ACCESS_MODE_PA_ONLY (0 << 6) +# define R600_MCD_SYSTEM_ACCESS_MODE_USE_SYS_MAP (1 << 6) +# define R600_MCD_SYSTEM_ACCESS_MODE_IN_SYS (2 << 6) +# define R600_MCD_SYSTEM_ACCESS_MODE_NOT_IN_SYS (3 << 6) + +# define R600_MCD_SYSTEM_APERTURE_UNMAPPED_ACCESS_PASS_THRU (0 << 8) +# define R600_MCD_SYSTEM_APERTURE_UNMAPPED_ACCESS_DEFAULT_PAGE (1 << 8) + +# define R600_MCD_SEMAPHORE_MODE (1 << 10) +# define R600_MCD_WAIT_L2_QUERY (1 << 11) +# define R600_MCD_EFFECTIVE_L1_TLB_SIZE(x) ((x) << 12) +# define R600_MCD_EFFECTIVE_L1_QUEUE_SIZE(x) ((x) << 15) + +#define R700_MC_VM_MD_L1_TLB0_CNTL 0x2654 +#define R700_MC_VM_MD_L1_TLB1_CNTL 0x2658 +#define R700_MC_VM_MD_L1_TLB2_CNTL 0x265c + +#define R700_MC_VM_MB_L1_TLB0_CNTL 0x2234 +#define R700_MC_VM_MB_L1_TLB1_CNTL 0x2238 +#define R700_MC_VM_MB_L1_TLB2_CNTL 0x223c +#define R700_MC_VM_MB_L1_TLB3_CNTL 0x2240 + +# define R700_ENABLE_L1_TLB (1 << 0) +# define R700_ENABLE_L1_FRAGMENT_PROCESSING (1 << 1) +# define R700_SYSTEM_ACCESS_MODE_IN_SYS (2 << 3) +# define R700_SYSTEM_APERTURE_UNMAPPED_ACCESS_PASS_THRU (0 << 5) +# define R700_EFFECTIVE_L1_TLB_SIZE(x) ((x) << 15) +# define R700_EFFECTIVE_L1_QUEUE_SIZE(x) ((x) << 18) + +#define R700_MC_ARB_RAMCFG 0x2760 +# define R700_NOOFBANK_SHIFT 0 +# define R700_NOOFBANK_MASK 0x3 +# define R700_NOOFRANK_SHIFT 2 +# define R700_NOOFRANK_MASK 0x1 +# define R700_NOOFROWS_SHIFT 3 +# define R700_NOOFROWS_MASK 0x7 +# define R700_NOOFCOLS_SHIFT 6 +# define R700_NOOFCOLS_MASK 0x3 +# define R700_CHANSIZE_SHIFT 8 +# define R700_CHANSIZE_MASK 0x1 +# define R700_BURSTLENGTH_SHIFT 9 +# define R700_BURSTLENGTH_MASK 0x1 +#define R600_RAMCFG 0x2408 +# define R600_NOOFBANK_SHIFT 0 +# define R600_NOOFBANK_MASK 0x1 +# define R600_NOOFRANK_SHIFT 1 +# define R600_NOOFRANK_MASK 0x1 +# define R600_NOOFROWS_SHIFT 2 +# define R600_NOOFROWS_MASK 0x7 +# define R600_NOOFCOLS_SHIFT 5 +# define R600_NOOFCOLS_MASK 0x3 +# define R600_CHANSIZE_SHIFT 7 +# define R600_CHANSIZE_MASK 0x1 +# define R600_BURSTLENGTH_SHIFT 8 +# define R600_BURSTLENGTH_MASK 0x1 + +#define R600_VM_L2_CNTL 0x1400 +# define R600_VM_L2_CACHE_EN (1 << 0) +# define R600_VM_L2_FRAG_PROC (1 << 1) +# define R600_VM_ENABLE_PTE_CACHE_LRU_W (1 << 9) +# define R600_VM_L2_CNTL_QUEUE_SIZE(x) ((x) << 13) +# define R700_VM_L2_CNTL_QUEUE_SIZE(x) ((x) << 14) + +#define R600_VM_L2_CNTL2 0x1404 +# define R600_VM_L2_CNTL2_INVALIDATE_ALL_L1_TLBS (1 << 0) +# define R600_VM_L2_CNTL2_INVALIDATE_L2_CACHE (1 << 1) +#define R600_VM_L2_CNTL3 0x1408 +# define R600_VM_L2_CNTL3_BANK_SELECT_0(x) ((x) << 0) +# define R600_VM_L2_CNTL3_BANK_SELECT_1(x) ((x) << 5) +# define R600_VM_L2_CNTL3_CACHE_UPDATE_MODE(x) ((x) << 10) +# define R700_VM_L2_CNTL3_BANK_SELECT(x) ((x) << 0) +# define R700_VM_L2_CNTL3_CACHE_UPDATE_MODE(x) ((x) << 6) + +#define R600_VM_L2_STATUS 0x140c + +#define R600_VM_CONTEXT0_CNTL 0x1410 +# define R600_VM_ENABLE_CONTEXT (1 << 0) +# define R600_VM_PAGE_TABLE_DEPTH_FLAT (0 << 1) + +#define R600_VM_CONTEXT0_CNTL2 0x1430 +#define R600_VM_CONTEXT0_REQUEST_RESPONSE 0x1470 +#define R600_VM_CONTEXT0_INVALIDATION_LOW_ADDR 0x1490 +#define R600_VM_CONTEXT0_INVALIDATION_HIGH_ADDR 0x14b0 +#define R600_VM_CONTEXT0_PAGE_TABLE_BASE_ADDR 0x1574 +#define R600_VM_CONTEXT0_PAGE_TABLE_START_ADDR 0x1594 +#define R600_VM_CONTEXT0_PAGE_TABLE_END_ADDR 0x15b4 + +#define R700_VM_CONTEXT0_PAGE_TABLE_BASE_ADDR 0x153c +#define R700_VM_CONTEXT0_PAGE_TABLE_START_ADDR 0x155c +#define R700_VM_CONTEXT0_PAGE_TABLE_END_ADDR 0x157c + +#define R600_HDP_HOST_PATH_CNTL 0x2c00 + +#define R600_GRBM_CNTL 0x8000 +# define R600_GRBM_READ_TIMEOUT(x) ((x) << 0) + +#define R600_GRBM_STATUS 0x8010 +# define R600_CMDFIFO_AVAIL_MASK 0x1f +# define R700_CMDFIFO_AVAIL_MASK 0xf +# define R600_GUI_ACTIVE (1 << 31) +#define R600_GRBM_STATUS2 0x8014 +#define R600_GRBM_SOFT_RESET 0x8020 +# define R600_SOFT_RESET_CP (1 << 0) +#define R600_WAIT_UNTIL 0x8040 + +#define R600_CP_SEM_WAIT_TIMER 0x85bc +#define R600_CP_ME_CNTL 0x86d8 +# define R600_CP_ME_HALT (1 << 28) +#define R600_CP_QUEUE_THRESHOLDS 0x8760 +# define R600_ROQ_IB1_START(x) ((x) << 0) +# define R600_ROQ_IB2_START(x) ((x) << 8) +#define R600_CP_MEQ_THRESHOLDS 0x8764 +# define R700_STQ_SPLIT(x) ((x) << 0) +# define R600_MEQ_END(x) ((x) << 16) +# define R600_ROQ_END(x) ((x) << 24) +#define R600_CP_PERFMON_CNTL 0x87fc +#define R600_CP_RB_BASE 0xc100 +#define R600_CP_RB_CNTL 0xc104 +# define R600_RB_BUFSZ(x) ((x) << 0) +# define R600_RB_BLKSZ(x) ((x) << 8) +# define R600_BUF_SWAP_32BIT (2 << 16) +# define R600_RB_NO_UPDATE (1 << 27) +# define R600_RB_RPTR_WR_ENA (1 << 31) +#define R600_CP_RB_RPTR_WR 0xc108 +#define R600_CP_RB_RPTR_ADDR 0xc10c +#define R600_CP_RB_RPTR_ADDR_HI 0xc110 +#define R600_CP_RB_WPTR 0xc114 +#define R600_CP_RB_WPTR_ADDR 0xc118 +#define R600_CP_RB_WPTR_ADDR_HI 0xc11c +#define R600_CP_RB_RPTR 0x8700 +#define R600_CP_RB_WPTR_DELAY 0x8704 +#define R600_CP_PFP_UCODE_ADDR 0xc150 +#define R600_CP_PFP_UCODE_DATA 0xc154 +#define R600_CP_ME_RAM_RADDR 0xc158 +#define R600_CP_ME_RAM_WADDR 0xc15c +#define R600_CP_ME_RAM_DATA 0xc160 +#define R600_CP_DEBUG 0xc1fc + +#define R600_PA_CL_ENHANCE 0x8a14 +# define R600_CLIP_VTX_REORDER_ENA (1 << 0) +# define R600_NUM_CLIP_SEQ(x) ((x) << 1) +#define R600_PA_SC_LINE_STIPPLE_STATE 0x8b10 +#define R600_PA_SC_MULTI_CHIP_CNTL 0x8b20 +#define R700_PA_SC_FORCE_EOV_MAX_CNTS 0x8b24 +# define R700_FORCE_EOV_MAX_CLK_CNT(x) ((x) << 0) +# define R700_FORCE_EOV_MAX_REZ_CNT(x) ((x) << 16) +#define R600_PA_SC_AA_SAMPLE_LOCS_2S 0x8b40 +#define R600_PA_SC_AA_SAMPLE_LOCS_4S 0x8b44 +#define R600_PA_SC_AA_SAMPLE_LOCS_8S_WD0 0x8b48 +#define R600_PA_SC_AA_SAMPLE_LOCS_8S_WD1 0x8b4c +# define R600_S0_X(x) ((x) << 0) +# define R600_S0_Y(x) ((x) << 4) +# define R600_S1_X(x) ((x) << 8) +# define R600_S1_Y(x) ((x) << 12) +# define R600_S2_X(x) ((x) << 16) +# define R600_S2_Y(x) ((x) << 20) +# define R600_S3_X(x) ((x) << 24) +# define R600_S3_Y(x) ((x) << 28) +# define R600_S4_X(x) ((x) << 0) +# define R600_S4_Y(x) ((x) << 4) +# define R600_S5_X(x) ((x) << 8) +# define R600_S5_Y(x) ((x) << 12) +# define R600_S6_X(x) ((x) << 16) +# define R600_S6_Y(x) ((x) << 20) +# define R600_S7_X(x) ((x) << 24) +# define R600_S7_Y(x) ((x) << 28) +#define R600_PA_SC_FIFO_SIZE 0x8bd0 +# define R600_SC_PRIM_FIFO_SIZE(x) ((x) << 0) +# define R600_SC_HIZ_TILE_FIFO_SIZE(x) ((x) << 8) +# define R600_SC_EARLYZ_TILE_FIFO_SIZE(x) ((x) << 16) +#define R700_PA_SC_FIFO_SIZE_R7XX 0x8bcc +# define R700_SC_PRIM_FIFO_SIZE(x) ((x) << 0) +# define R700_SC_HIZ_TILE_FIFO_SIZE(x) ((x) << 12) +# define R700_SC_EARLYZ_TILE_FIFO_SIZE(x) ((x) << 20) +#define R600_PA_SC_ENHANCE 0x8bf0 +# define R600_FORCE_EOV_MAX_CLK_CNT(x) ((x) << 0) +# define R600_FORCE_EOV_MAX_TILE_CNT(x) ((x) << 12) +#define R600_PA_SC_CLIPRECT_RULE 0x2820c +#define R700_PA_SC_EDGERULE 0x28230 +#define R600_PA_SC_LINE_STIPPLE 0x28a0c +#define R600_PA_SC_MODE_CNTL 0x28a4c +#define R600_PA_SC_AA_CONFIG 0x28c04 + +#define R600_SX_EXPORT_BUFFER_SIZES 0x900c +# define R600_COLOR_BUFFER_SIZE(x) ((x) << 0) +# define R600_POSITION_BUFFER_SIZE(x) ((x) << 8) +# define R600_SMX_BUFFER_SIZE(x) ((x) << 16) +#define R600_SX_DEBUG_1 0x9054 +# define R600_SMX_EVENT_RELEASE (1 << 0) +# define R600_ENABLE_NEW_SMX_ADDRESS (1 << 16) +#define R700_SX_DEBUG_1 0x9058 +# define R700_ENABLE_NEW_SMX_ADDRESS (1 << 16) +#define R600_SX_MISC 0x28350 + +#define R600_DB_DEBUG 0x9830 +# define R600_PREZ_MUST_WAIT_FOR_POSTZ_DONE (1 << 31) +#define R600_DB_WATERMARKS 0x9838 +# define R600_DEPTH_FREE(x) ((x) << 0) +# define R600_DEPTH_FLUSH(x) ((x) << 5) +# define R600_DEPTH_PENDING_FREE(x) ((x) << 15) +# define R600_DEPTH_CACHELINE_FREE(x) ((x) << 20) +#define R700_DB_DEBUG3 0x98b0 +# define R700_DB_CLK_OFF_DELAY(x) ((x) << 11) +#define RV700_DB_DEBUG4 0x9b8c +# define RV700_DISABLE_TILE_COVERED_FOR_PS_ITER (1 << 6) + +#define R600_VGT_CACHE_INVALIDATION 0x88c4 +# define R600_CACHE_INVALIDATION(x) ((x) << 0) +# define R600_VC_ONLY 0 +# define R600_TC_ONLY 1 +# define R600_VC_AND_TC 2 +# define R700_AUTO_INVLD_EN(x) ((x) << 6) +# define R700_NO_AUTO 0 +# define R700_ES_AUTO 1 +# define R700_GS_AUTO 2 +# define R700_ES_AND_GS_AUTO 3 +#define R600_VGT_GS_PER_ES 0x88c8 +#define R600_VGT_ES_PER_GS 0x88cc +#define R600_VGT_GS_PER_VS 0x88e8 +#define R600_VGT_GS_VERTEX_REUSE 0x88d4 +#define R600_VGT_NUM_INSTANCES 0x8974 +#define R600_VGT_STRMOUT_EN 0x28ab0 +#define R600_VGT_EVENT_INITIATOR 0x28a90 +# define R600_CACHE_FLUSH_AND_INV_EVENT (0x16 << 0) +#define R600_VGT_VERTEX_REUSE_BLOCK_CNTL 0x28c58 +# define R600_VTX_REUSE_DEPTH_MASK 0xff +#define R600_VGT_OUT_DEALLOC_CNTL 0x28c5c +# define R600_DEALLOC_DIST_MASK 0x7f + +#define R600_CB_COLOR0_BASE 0x28040 +#define R600_CB_COLOR1_BASE 0x28044 +#define R600_CB_COLOR2_BASE 0x28048 +#define R600_CB_COLOR3_BASE 0x2804c +#define R600_CB_COLOR4_BASE 0x28050 +#define R600_CB_COLOR5_BASE 0x28054 +#define R600_CB_COLOR6_BASE 0x28058 +#define R600_CB_COLOR7_BASE 0x2805c +#define R600_CB_COLOR7_FRAG 0x280fc + +#define R600_CB_COLOR0_SIZE 0x28060 +#define R600_CB_COLOR0_VIEW 0x28080 +#define R600_CB_COLOR0_INFO 0x280a0 +#define R600_CB_COLOR0_TILE 0x280c0 +#define R600_CB_COLOR0_FRAG 0x280e0 +#define R600_CB_COLOR0_MASK 0x28100 + +#define AVIVO_D1MODE_VLINE_START_END 0x6538 +#define AVIVO_D2MODE_VLINE_START_END 0x6d38 +#define R600_CP_COHER_BASE 0x85f8 +#define R600_DB_DEPTH_BASE 0x2800c +#define R600_SQ_PGM_START_FS 0x28894 +#define R600_SQ_PGM_START_ES 0x28880 +#define R600_SQ_PGM_START_VS 0x28858 +#define R600_SQ_PGM_RESOURCES_VS 0x28868 +#define R600_SQ_PGM_CF_OFFSET_VS 0x288d0 +#define R600_SQ_PGM_START_GS 0x2886c +#define R600_SQ_PGM_START_PS 0x28840 +#define R600_SQ_PGM_RESOURCES_PS 0x28850 +#define R600_SQ_PGM_EXPORTS_PS 0x28854 +#define R600_SQ_PGM_CF_OFFSET_PS 0x288cc +#define R600_VGT_DMA_BASE 0x287e8 +#define R600_VGT_DMA_BASE_HI 0x287e4 +#define R600_VGT_STRMOUT_BASE_OFFSET_0 0x28b10 +#define R600_VGT_STRMOUT_BASE_OFFSET_1 0x28b14 +#define R600_VGT_STRMOUT_BASE_OFFSET_2 0x28b18 +#define R600_VGT_STRMOUT_BASE_OFFSET_3 0x28b1c +#define R600_VGT_STRMOUT_BASE_OFFSET_HI_0 0x28b44 +#define R600_VGT_STRMOUT_BASE_OFFSET_HI_1 0x28b48 +#define R600_VGT_STRMOUT_BASE_OFFSET_HI_2 0x28b4c +#define R600_VGT_STRMOUT_BASE_OFFSET_HI_3 0x28b50 +#define R600_VGT_STRMOUT_BUFFER_BASE_0 0x28ad8 +#define R600_VGT_STRMOUT_BUFFER_BASE_1 0x28ae8 +#define R600_VGT_STRMOUT_BUFFER_BASE_2 0x28af8 +#define R600_VGT_STRMOUT_BUFFER_BASE_3 0x28b08 +#define R600_VGT_STRMOUT_BUFFER_OFFSET_0 0x28adc +#define R600_VGT_STRMOUT_BUFFER_OFFSET_1 0x28aec +#define R600_VGT_STRMOUT_BUFFER_OFFSET_2 0x28afc +#define R600_VGT_STRMOUT_BUFFER_OFFSET_3 0x28b0c + +#define R600_VGT_PRIMITIVE_TYPE 0x8958 + +#define R600_PA_SC_SCREEN_SCISSOR_TL 0x28030 +#define R600_PA_SC_GENERIC_SCISSOR_TL 0x28240 +#define R600_PA_SC_WINDOW_SCISSOR_TL 0x28204 + +#define R600_TC_CNTL 0x9608 +# define R600_TC_L2_SIZE(x) ((x) << 5) +# define R600_L2_DISABLE_LATE_HIT (1 << 9) + +#define R600_ARB_POP 0x2418 +# define R600_ENABLE_TC128 (1 << 30) +#define R600_ARB_GDEC_RD_CNTL 0x246c + +#define R600_TA_CNTL_AUX 0x9508 +# define R600_DISABLE_CUBE_WRAP (1 << 0) +# define R600_DISABLE_CUBE_ANISO (1 << 1) +# define R700_GETLOD_SELECT(x) ((x) << 2) +# define R600_SYNC_GRADIENT (1 << 24) +# define R600_SYNC_WALKER (1 << 25) +# define R600_SYNC_ALIGNER (1 << 26) +# define R600_BILINEAR_PRECISION_6_BIT (0 << 31) +# define R600_BILINEAR_PRECISION_8_BIT (1 << 31) + +#define R700_TCP_CNTL 0x9610 + +#define R600_SMX_DC_CTL0 0xa020 +# define R700_USE_HASH_FUNCTION (1 << 0) +# define R700_CACHE_DEPTH(x) ((x) << 1) +# define R700_FLUSH_ALL_ON_EVENT (1 << 10) +# define R700_STALL_ON_EVENT (1 << 11) +#define R700_SMX_EVENT_CTL 0xa02c +# define R700_ES_FLUSH_CTL(x) ((x) << 0) +# define R700_GS_FLUSH_CTL(x) ((x) << 3) +# define R700_ACK_FLUSH_CTL(x) ((x) << 6) +# define R700_SYNC_FLUSH_CTL (1 << 8) + +#define R600_SQ_CONFIG 0x8c00 +# define R600_VC_ENABLE (1 << 0) +# define R600_EXPORT_SRC_C (1 << 1) +# define R600_DX9_CONSTS (1 << 2) +# define R600_ALU_INST_PREFER_VECTOR (1 << 3) +# define R600_DX10_CLAMP (1 << 4) +# define R600_CLAUSE_SEQ_PRIO(x) ((x) << 8) +# define R600_PS_PRIO(x) ((x) << 24) +# define R600_VS_PRIO(x) ((x) << 26) +# define R600_GS_PRIO(x) ((x) << 28) +# define R600_ES_PRIO(x) ((x) << 30) +#define R600_SQ_GPR_RESOURCE_MGMT_1 0x8c04 +# define R600_NUM_PS_GPRS(x) ((x) << 0) +# define R600_NUM_VS_GPRS(x) ((x) << 16) +# define R700_DYN_GPR_ENABLE (1 << 27) +# define R600_NUM_CLAUSE_TEMP_GPRS(x) ((x) << 28) +#define R600_SQ_GPR_RESOURCE_MGMT_2 0x8c08 +# define R600_NUM_GS_GPRS(x) ((x) << 0) +# define R600_NUM_ES_GPRS(x) ((x) << 16) +#define R600_SQ_THREAD_RESOURCE_MGMT 0x8c0c +# define R600_NUM_PS_THREADS(x) ((x) << 0) +# define R600_NUM_VS_THREADS(x) ((x) << 8) +# define R600_NUM_GS_THREADS(x) ((x) << 16) +# define R600_NUM_ES_THREADS(x) ((x) << 24) +#define R600_SQ_STACK_RESOURCE_MGMT_1 0x8c10 +# define R600_NUM_PS_STACK_ENTRIES(x) ((x) << 0) +# define R600_NUM_VS_STACK_ENTRIES(x) ((x) << 16) +#define R600_SQ_STACK_RESOURCE_MGMT_2 0x8c14 +# define R600_NUM_GS_STACK_ENTRIES(x) ((x) << 0) +# define R600_NUM_ES_STACK_ENTRIES(x) ((x) << 16) +#define R600_SQ_MS_FIFO_SIZES 0x8cf0 +# define R600_CACHE_FIFO_SIZE(x) ((x) << 0) +# define R600_FETCH_FIFO_HIWATER(x) ((x) << 8) +# define R600_DONE_FIFO_HIWATER(x) ((x) << 16) +# define R600_ALU_UPDATE_FIFO_HIWATER(x) ((x) << 24) +#define R700_SQ_DYN_GPR_SIZE_SIMD_AB_0 0x8db0 +# define R700_SIMDA_RING0(x) ((x) << 0) +# define R700_SIMDA_RING1(x) ((x) << 8) +# define R700_SIMDB_RING0(x) ((x) << 16) +# define R700_SIMDB_RING1(x) ((x) << 24) +#define R700_SQ_DYN_GPR_SIZE_SIMD_AB_1 0x8db4 +#define R700_SQ_DYN_GPR_SIZE_SIMD_AB_2 0x8db8 +#define R700_SQ_DYN_GPR_SIZE_SIMD_AB_3 0x8dbc +#define R700_SQ_DYN_GPR_SIZE_SIMD_AB_4 0x8dc0 +#define R700_SQ_DYN_GPR_SIZE_SIMD_AB_5 0x8dc4 +#define R700_SQ_DYN_GPR_SIZE_SIMD_AB_6 0x8dc8 +#define R700_SQ_DYN_GPR_SIZE_SIMD_AB_7 0x8dcc + +#define R600_SPI_PS_IN_CONTROL_0 0x286cc +# define R600_NUM_INTERP(x) ((x) << 0) +# define R600_POSITION_ENA (1 << 8) +# define R600_POSITION_CENTROID (1 << 9) +# define R600_POSITION_ADDR(x) ((x) << 10) +# define R600_PARAM_GEN(x) ((x) << 15) +# define R600_PARAM_GEN_ADDR(x) ((x) << 19) +# define R600_BARYC_SAMPLE_CNTL(x) ((x) << 26) +# define R600_PERSP_GRADIENT_ENA (1 << 28) +# define R600_LINEAR_GRADIENT_ENA (1 << 29) +# define R600_POSITION_SAMPLE (1 << 30) +# define R600_BARYC_AT_SAMPLE_ENA (1 << 31) +#define R600_SPI_PS_IN_CONTROL_1 0x286d0 +# define R600_GEN_INDEX_PIX (1 << 0) +# define R600_GEN_INDEX_PIX_ADDR(x) ((x) << 1) +# define R600_FRONT_FACE_ENA (1 << 8) +# define R600_FRONT_FACE_CHAN(x) ((x) << 9) +# define R600_FRONT_FACE_ALL_BITS (1 << 11) +# define R600_FRONT_FACE_ADDR(x) ((x) << 12) +# define R600_FOG_ADDR(x) ((x) << 17) +# define R600_FIXED_PT_POSITION_ENA (1 << 24) +# define R600_FIXED_PT_POSITION_ADDR(x) ((x) << 25) +# define R700_POSITION_ULC (1 << 30) +#define R600_SPI_INPUT_Z 0x286d8 + +#define R600_SPI_CONFIG_CNTL 0x9100 +# define R600_GPR_WRITE_PRIORITY(x) ((x) << 0) +# define R600_DISABLE_INTERP_1 (1 << 5) +#define R600_SPI_CONFIG_CNTL_1 0x913c +# define R600_VTX_DONE_DELAY(x) ((x) << 0) +# define R600_INTERP_ONE_PRIM_PER_ROW (1 << 4) + +#define R600_GB_TILING_CONFIG 0x98f0 +# define R600_PIPE_TILING(x) ((x) << 1) +# define R600_BANK_TILING(x) ((x) << 4) +# define R600_GROUP_SIZE(x) ((x) << 6) +# define R600_ROW_TILING(x) ((x) << 8) +# define R600_BANK_SWAPS(x) ((x) << 11) +# define R600_SAMPLE_SPLIT(x) ((x) << 14) +# define R600_BACKEND_MAP(x) ((x) << 16) +#define R600_DCP_TILING_CONFIG 0x6ca0 +#define R600_HDP_TILING_CONFIG 0x2f3c + +#define R600_CC_RB_BACKEND_DISABLE 0x98f4 +#define R700_CC_SYS_RB_BACKEND_DISABLE 0x3f88 +# define R600_BACKEND_DISABLE(x) ((x) << 16) + +#define R600_CC_GC_SHADER_PIPE_CONFIG 0x8950 +#define R600_GC_USER_SHADER_PIPE_CONFIG 0x8954 +# define R600_INACTIVE_QD_PIPES(x) ((x) << 8) +# define R600_INACTIVE_QD_PIPES_MASK (0xff << 8) +# define R600_INACTIVE_SIMDS(x) ((x) << 16) +# define R600_INACTIVE_SIMDS_MASK (0xff << 16) + +#define R700_CGTS_SYS_TCC_DISABLE 0x3f90 +#define R700_CGTS_USER_SYS_TCC_DISABLE 0x3f94 +#define R700_CGTS_TCC_DISABLE 0x9148 +#define R700_CGTS_USER_TCC_DISABLE 0x914c + +/* Constants */ +#define RADEON_MAX_USEC_TIMEOUT 100000 /* 100 ms */ + +#define RADEON_LAST_FRAME_REG RADEON_SCRATCH_REG0 +#define RADEON_LAST_DISPATCH_REG RADEON_SCRATCH_REG1 +#define RADEON_LAST_CLEAR_REG RADEON_SCRATCH_REG2 +#define RADEON_LAST_SWI_REG RADEON_SCRATCH_REG3 +#define RADEON_LAST_DISPATCH 1 + +#define R600_LAST_FRAME_REG R600_SCRATCH_REG0 +#define R600_LAST_DISPATCH_REG R600_SCRATCH_REG1 +#define R600_LAST_CLEAR_REG R600_SCRATCH_REG2 +#define R600_LAST_SWI_REG R600_SCRATCH_REG3 + +#define RADEON_MAX_VB_AGE 0x7fffffff +#define RADEON_MAX_VB_VERTS (0xffff) + +#define RADEON_RING_HIGH_MARK 128 + +#define RADEON_PCIGART_TABLE_SIZE (32*1024) + +#define RADEON_READ(reg) DRM_READ32( dev_priv->mmio, (reg) ) +#define RADEON_WRITE(reg, val) \ +do { \ + if (reg < 0x10000) { \ + DRM_WRITE32(dev_priv->mmio, (reg), (val)); \ + } else { \ + DRM_WRITE32(dev_priv->mmio, RADEON_MM_INDEX, (reg)); \ + DRM_WRITE32(dev_priv->mmio, RADEON_MM_DATA, (val)); \ + } \ +} while (0) +#define RADEON_READ8(reg) DRM_READ8( dev_priv->mmio, (reg) ) +#define RADEON_WRITE8(reg,val) DRM_WRITE8( dev_priv->mmio, (reg), (val) ) + +#define RADEON_WRITE_PLL(addr, val) \ +do { \ + RADEON_WRITE8(RADEON_CLOCK_CNTL_INDEX, \ + ((addr) & 0x1f) | RADEON_PLL_WR_EN ); \ + RADEON_WRITE(RADEON_CLOCK_CNTL_DATA, (val)); \ +} while (0) + +#define RADEON_WRITE_PCIE(addr, val) \ +do { \ + RADEON_WRITE8(RADEON_PCIE_INDEX, \ + ((addr) & 0xff)); \ + RADEON_WRITE(RADEON_PCIE_DATA, (val)); \ +} while (0) + +#define R500_WRITE_MCIND(addr, val) \ +do { \ + RADEON_WRITE(R520_MC_IND_INDEX, 0xff0000 | ((addr) & 0xff)); \ + RADEON_WRITE(R520_MC_IND_DATA, (val)); \ + RADEON_WRITE(R520_MC_IND_INDEX, 0); \ +} while (0) + +#define RS480_WRITE_MCIND(addr, val) \ +do { \ + RADEON_WRITE(RS480_NB_MC_INDEX, \ + ((addr) & 0xff) | RS480_NB_MC_IND_WR_EN); \ + RADEON_WRITE(RS480_NB_MC_DATA, (val)); \ + RADEON_WRITE(RS480_NB_MC_INDEX, 0xff); \ +} while (0) + +#define RS690_WRITE_MCIND(addr, val) \ +do { \ + RADEON_WRITE(RS690_MC_INDEX, RS690_MC_INDEX_WR_EN | ((addr) & RS690_MC_INDEX_MASK)); \ + RADEON_WRITE(RS690_MC_DATA, val); \ + RADEON_WRITE(RS690_MC_INDEX, RS690_MC_INDEX_WR_ACK); \ +} while (0) + +#define RS600_WRITE_MCIND(addr, val) \ +do { \ + RADEON_WRITE(RS600_MC_INDEX, RS600_MC_IND_WR_EN | RS600_MC_IND_CITF_ARB0 | ((addr) & RS600_MC_ADDR_MASK)); \ + RADEON_WRITE(RS600_MC_DATA, val); \ +} while (0) + +#define IGP_WRITE_MCIND(addr, val) \ +do { \ + if (((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RS690) || \ + ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RS740)) \ + RS690_WRITE_MCIND(addr, val); \ + else if ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RS600) \ + RS600_WRITE_MCIND(addr, val); \ + else \ + RS480_WRITE_MCIND(addr, val); \ +} while (0) + +#define CP_PACKET0( reg, n ) \ + (RADEON_CP_PACKET0 | ((n) << 16) | ((reg) >> 2)) +#define CP_PACKET0_TABLE( reg, n ) \ + (RADEON_CP_PACKET0 | RADEON_ONE_REG_WR | ((n) << 16) | ((reg) >> 2)) +#define CP_PACKET1( reg0, reg1 ) \ + (RADEON_CP_PACKET1 | (((reg1) >> 2) << 15) | ((reg0) >> 2)) +#define CP_PACKET2() \ + (RADEON_CP_PACKET2) +#define CP_PACKET3( pkt, n ) \ + (RADEON_CP_PACKET3 | (pkt) | ((n) << 16)) + +/* ================================================================ + * Engine control helper macros + */ + +#define RADEON_WAIT_UNTIL_2D_IDLE() do { \ + OUT_RING( CP_PACKET0( RADEON_WAIT_UNTIL, 0 ) ); \ + OUT_RING( (RADEON_WAIT_2D_IDLECLEAN | \ + RADEON_WAIT_HOST_IDLECLEAN) ); \ +} while (0) + +#define RADEON_WAIT_UNTIL_3D_IDLE() do { \ + OUT_RING( CP_PACKET0( RADEON_WAIT_UNTIL, 0 ) ); \ + OUT_RING( (RADEON_WAIT_3D_IDLECLEAN | \ + RADEON_WAIT_HOST_IDLECLEAN) ); \ +} while (0) + +#define RADEON_WAIT_UNTIL_IDLE() do { \ + OUT_RING( CP_PACKET0( RADEON_WAIT_UNTIL, 0 ) ); \ + OUT_RING( (RADEON_WAIT_2D_IDLECLEAN | \ + RADEON_WAIT_3D_IDLECLEAN | \ + RADEON_WAIT_HOST_IDLECLEAN) ); \ +} while (0) + +#define RADEON_WAIT_UNTIL_PAGE_FLIPPED() do { \ + OUT_RING( CP_PACKET0( RADEON_WAIT_UNTIL, 0 ) ); \ + OUT_RING( RADEON_WAIT_CRTC_PFLIP ); \ +} while (0) + +#define RADEON_FLUSH_CACHE() do { \ + if ((dev_priv->flags & RADEON_FAMILY_MASK) <= CHIP_RV280) { \ + OUT_RING(CP_PACKET0(RADEON_RB3D_DSTCACHE_CTLSTAT, 0)); \ + OUT_RING(RADEON_RB3D_DC_FLUSH); \ + } else { \ + OUT_RING(CP_PACKET0(R300_RB3D_DSTCACHE_CTLSTAT, 0)); \ + OUT_RING(R300_RB3D_DC_FLUSH); \ + } \ +} while (0) + +#define RADEON_PURGE_CACHE() do { \ + if ((dev_priv->flags & RADEON_FAMILY_MASK) <= CHIP_RV280) { \ + OUT_RING(CP_PACKET0(RADEON_RB3D_DSTCACHE_CTLSTAT, 0)); \ + OUT_RING(RADEON_RB3D_DC_FLUSH | RADEON_RB3D_DC_FREE); \ + } else { \ + OUT_RING(CP_PACKET0(R300_RB3D_DSTCACHE_CTLSTAT, 0)); \ + OUT_RING(R300_RB3D_DC_FLUSH | R300_RB3D_DC_FREE); \ + } \ +} while (0) + +#define RADEON_FLUSH_ZCACHE() do { \ + if ((dev_priv->flags & RADEON_FAMILY_MASK) <= CHIP_RV280) { \ + OUT_RING(CP_PACKET0(RADEON_RB3D_ZCACHE_CTLSTAT, 0)); \ + OUT_RING(RADEON_RB3D_ZC_FLUSH); \ + } else { \ + OUT_RING(CP_PACKET0(R300_ZB_ZCACHE_CTLSTAT, 0)); \ + OUT_RING(R300_ZC_FLUSH); \ + } \ +} while (0) + +#define RADEON_PURGE_ZCACHE() do { \ + if ((dev_priv->flags & RADEON_FAMILY_MASK) <= CHIP_RV280) { \ + OUT_RING(CP_PACKET0(RADEON_RB3D_ZCACHE_CTLSTAT, 0)); \ + OUT_RING(RADEON_RB3D_ZC_FLUSH | RADEON_RB3D_ZC_FREE); \ + } else { \ + OUT_RING(CP_PACKET0(R300_ZB_ZCACHE_CTLSTAT, 0)); \ + OUT_RING(R300_ZC_FLUSH | R300_ZC_FREE); \ + } \ +} while (0) + +/* ================================================================ + * Misc helper macros + */ + +/* Perfbox functionality only. + */ +#define RING_SPACE_TEST_WITH_RETURN( dev_priv ) \ +do { \ + if (!(dev_priv->stats.boxes & RADEON_BOX_DMA_IDLE)) { \ + u32 head = GET_RING_HEAD( dev_priv ); \ + if (head == dev_priv->ring.tail) \ + dev_priv->stats.boxes |= RADEON_BOX_DMA_IDLE; \ + } \ +} while (0) + +#define VB_AGE_TEST_WITH_RETURN( dev_priv ) \ +do { \ + struct drm_radeon_master_private *master_priv = file_priv->masterp->driver_priv;\ + drm_radeon_sarea_t *sarea_priv = master_priv->sarea_priv; \ + if ( sarea_priv->last_dispatch >= RADEON_MAX_VB_AGE ) { \ + int __ret; \ + if ((dev_priv->flags & RADEON_FAMILY_MASK) >= CHIP_R600) \ + __ret = r600_do_cp_idle(dev_priv); \ + else \ + __ret = radeon_do_cp_idle(dev_priv); \ + if ( __ret ) return __ret; \ + sarea_priv->last_dispatch = 0; \ + radeon_freelist_reset( dev ); \ + } \ +} while (0) + +#define RADEON_DISPATCH_AGE( age ) do { \ + OUT_RING( CP_PACKET0( RADEON_LAST_DISPATCH_REG, 0 ) ); \ + OUT_RING( age ); \ +} while (0) + +#define RADEON_FRAME_AGE( age ) do { \ + OUT_RING( CP_PACKET0( RADEON_LAST_FRAME_REG, 0 ) ); \ + OUT_RING( age ); \ +} while (0) + +#define RADEON_CLEAR_AGE( age ) do { \ + OUT_RING( CP_PACKET0( RADEON_LAST_CLEAR_REG, 0 ) ); \ + OUT_RING( age ); \ +} while (0) + +#define R600_DISPATCH_AGE(age) do { \ + OUT_RING(CP_PACKET3(R600_IT_SET_CONFIG_REG, 1)); \ + OUT_RING((R600_LAST_DISPATCH_REG - R600_SET_CONFIG_REG_OFFSET) >> 2); \ + OUT_RING(age); \ +} while (0) + +#define R600_FRAME_AGE(age) do { \ + OUT_RING(CP_PACKET3(R600_IT_SET_CONFIG_REG, 1)); \ + OUT_RING((R600_LAST_FRAME_REG - R600_SET_CONFIG_REG_OFFSET) >> 2); \ + OUT_RING(age); \ +} while (0) + +#define R600_CLEAR_AGE(age) do { \ + OUT_RING(CP_PACKET3(R600_IT_SET_CONFIG_REG, 1)); \ + OUT_RING((R600_LAST_CLEAR_REG - R600_SET_CONFIG_REG_OFFSET) >> 2); \ + OUT_RING(age); \ +} while (0) + +/* ================================================================ + * Ring control + */ + +#define RADEON_VERBOSE 0 + +#define RING_LOCALS int write, _nr, _align_nr; unsigned int mask; u32 *ring; + +#define RADEON_RING_ALIGN 16 + +#define BEGIN_RING( n ) do { \ + if ( RADEON_VERBOSE ) { \ + DRM_INFO( "BEGIN_RING( %d )\n", (n)); \ + } \ + _align_nr = RADEON_RING_ALIGN - ((dev_priv->ring.tail + n) & (RADEON_RING_ALIGN-1)); \ + _align_nr += n; \ + if (dev_priv->ring.space <= (_align_nr * sizeof(u32))) { \ + COMMIT_RING(); \ + radeon_wait_ring( dev_priv, _align_nr * sizeof(u32)); \ + } \ + _nr = n; dev_priv->ring.space -= (n) * sizeof(u32); \ + ring = dev_priv->ring.start; \ + write = dev_priv->ring.tail; \ + mask = dev_priv->ring.tail_mask; \ +} while (0) + +#define ADVANCE_RING() do { \ + if ( RADEON_VERBOSE ) { \ + DRM_INFO( "ADVANCE_RING() wr=0x%06x tail=0x%06x\n", \ + write, dev_priv->ring.tail ); \ + } \ + if (((dev_priv->ring.tail + _nr) & mask) != write) { \ + DRM_ERROR( \ + "ADVANCE_RING(): mismatch: nr: %x write: %x line: %d\n", \ + ((dev_priv->ring.tail + _nr) & mask), \ + write, __LINE__); \ + } else \ + dev_priv->ring.tail = write; \ +} while (0) + +extern void radeon_commit_ring(drm_radeon_private_t *dev_priv); + +#define COMMIT_RING() do { \ + radeon_commit_ring(dev_priv); \ + } while(0) + +#define OUT_RING( x ) do { \ + if ( RADEON_VERBOSE ) { \ + DRM_INFO( " OUT_RING( 0x%08x ) at 0x%x\n", \ + (unsigned int)(x), write ); \ + } \ + ring[write++] = (x); \ + write &= mask; \ +} while (0) + +#define OUT_RING_REG( reg, val ) do { \ + OUT_RING( CP_PACKET0( reg, 0 ) ); \ + OUT_RING( val ); \ +} while (0) + +#define OUT_RING_TABLE( tab, sz ) do { \ + int _size = (sz); \ + int *_tab = (int *)(tab); \ + \ + if (write + _size > mask) { \ + int _i = (mask+1) - write; \ + _size -= _i; \ + while (_i > 0 ) { \ + *(int *)(ring + write) = *_tab++; \ + write++; \ + _i--; \ + } \ + write = 0; \ + _tab += _i; \ + } \ + while (_size > 0) { \ + *(ring + write) = *_tab++; \ + write++; \ + _size--; \ + } \ + write &= mask; \ +} while (0) + +/** + * Copy given number of dwords from drm buffer to the ring buffer. + */ +#define OUT_RING_DRM_BUFFER(buf, sz) do { \ + int _size = (sz) * 4; \ + struct drm_buffer *_buf = (buf); \ + int _part_size; \ + while (_size > 0) { \ + _part_size = _size; \ + \ + if (write + _part_size/4 > mask) \ + _part_size = ((mask + 1) - write)*4; \ + \ + if (drm_buffer_index(_buf) + _part_size > PAGE_SIZE) \ + _part_size = PAGE_SIZE - drm_buffer_index(_buf);\ + \ + \ + \ + memcpy(ring + write, &_buf->data[drm_buffer_page(_buf)] \ + [drm_buffer_index(_buf)], _part_size); \ + \ + _size -= _part_size; \ + write = (write + _part_size/4) & mask; \ + drm_buffer_advance(_buf, _part_size); \ + } \ +} while (0) + + +#endif /* __RADEON_DRV_H__ */ diff --git a/sys/dev/drm2/radeon/radeon_encoders.c b/sys/dev/drm2/radeon/radeon_encoders.c new file mode 100644 index 00000000000..be7ad3498cf --- /dev/null +++ b/sys/dev/drm2/radeon/radeon_encoders.c @@ -0,0 +1,381 @@ +/* + * Copyright 2007-8 Advanced Micro Devices, Inc. + * Copyright 2008 Red Hat Inc. + * + * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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. + * + * Authors: Dave Airlie + * Alex Deucher + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include "radeon.h" +#include "atom.h" + + +static uint32_t radeon_encoder_clones(struct drm_encoder *encoder) +{ + struct drm_device *dev = encoder->dev; + struct radeon_device *rdev = dev->dev_private; + struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); + struct drm_encoder *clone_encoder; + uint32_t index_mask = 0; + int count; + + /* DIG routing gets problematic */ + if (rdev->family >= CHIP_R600) + return index_mask; + /* LVDS/TV are too wacky */ + if (radeon_encoder->devices & ATOM_DEVICE_LCD_SUPPORT) + return index_mask; + /* DVO requires 2x ppll clocks depending on tmds chip */ + if (radeon_encoder->devices & ATOM_DEVICE_DFP2_SUPPORT) + return index_mask; + + count = -1; + list_for_each_entry(clone_encoder, &dev->mode_config.encoder_list, head) { + struct radeon_encoder *radeon_clone = to_radeon_encoder(clone_encoder); + count++; + + if (clone_encoder == encoder) + continue; + if (radeon_clone->devices & (ATOM_DEVICE_LCD_SUPPORT)) + continue; + if (radeon_clone->devices & ATOM_DEVICE_DFP2_SUPPORT) + continue; + else + index_mask |= (1 << count); + } + return index_mask; +} + +void radeon_setup_encoder_clones(struct drm_device *dev) +{ + struct drm_encoder *encoder; + + list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { + encoder->possible_clones = radeon_encoder_clones(encoder); + } +} + +uint32_t +radeon_get_encoder_enum(struct drm_device *dev, uint32_t supported_device, uint8_t dac) +{ + struct radeon_device *rdev = dev->dev_private; + uint32_t ret = 0; + + switch (supported_device) { + case ATOM_DEVICE_CRT1_SUPPORT: + case ATOM_DEVICE_TV1_SUPPORT: + case ATOM_DEVICE_TV2_SUPPORT: + case ATOM_DEVICE_CRT2_SUPPORT: + case ATOM_DEVICE_CV_SUPPORT: + switch (dac) { + case 1: /* dac a */ + if ((rdev->family == CHIP_RS300) || + (rdev->family == CHIP_RS400) || + (rdev->family == CHIP_RS480)) + ret = ENCODER_INTERNAL_DAC2_ENUM_ID1; + else if (ASIC_IS_AVIVO(rdev)) + ret = ENCODER_INTERNAL_KLDSCP_DAC1_ENUM_ID1; + else + ret = ENCODER_INTERNAL_DAC1_ENUM_ID1; + break; + case 2: /* dac b */ + if (ASIC_IS_AVIVO(rdev)) + ret = ENCODER_INTERNAL_KLDSCP_DAC2_ENUM_ID1; + else { + /*if (rdev->family == CHIP_R200) + ret = ENCODER_INTERNAL_DVO1_ENUM_ID1; + else*/ + ret = ENCODER_INTERNAL_DAC2_ENUM_ID1; + } + break; + case 3: /* external dac */ + if (ASIC_IS_AVIVO(rdev)) + ret = ENCODER_INTERNAL_KLDSCP_DVO1_ENUM_ID1; + else + ret = ENCODER_INTERNAL_DVO1_ENUM_ID1; + break; + } + break; + case ATOM_DEVICE_LCD1_SUPPORT: + if (ASIC_IS_AVIVO(rdev)) + ret = ENCODER_INTERNAL_LVTM1_ENUM_ID1; + else + ret = ENCODER_INTERNAL_LVDS_ENUM_ID1; + break; + case ATOM_DEVICE_DFP1_SUPPORT: + if ((rdev->family == CHIP_RS300) || + (rdev->family == CHIP_RS400) || + (rdev->family == CHIP_RS480)) + ret = ENCODER_INTERNAL_DVO1_ENUM_ID1; + else if (ASIC_IS_AVIVO(rdev)) + ret = ENCODER_INTERNAL_KLDSCP_TMDS1_ENUM_ID1; + else + ret = ENCODER_INTERNAL_TMDS1_ENUM_ID1; + break; + case ATOM_DEVICE_LCD2_SUPPORT: + case ATOM_DEVICE_DFP2_SUPPORT: + if ((rdev->family == CHIP_RS600) || + (rdev->family == CHIP_RS690) || + (rdev->family == CHIP_RS740)) + ret = ENCODER_INTERNAL_DDI_ENUM_ID1; + else if (ASIC_IS_AVIVO(rdev)) + ret = ENCODER_INTERNAL_KLDSCP_DVO1_ENUM_ID1; + else + ret = ENCODER_INTERNAL_DVO1_ENUM_ID1; + break; + case ATOM_DEVICE_DFP3_SUPPORT: + ret = ENCODER_INTERNAL_LVTM1_ENUM_ID1; + break; + } + + return ret; +} + +void +radeon_link_encoder_connector(struct drm_device *dev) +{ + struct radeon_device *rdev = dev->dev_private; + struct drm_connector *connector; + struct radeon_connector *radeon_connector; + struct drm_encoder *encoder; + struct radeon_encoder *radeon_encoder; + + /* walk the list and link encoders to connectors */ + list_for_each_entry(connector, &dev->mode_config.connector_list, head) { + radeon_connector = to_radeon_connector(connector); + list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { + radeon_encoder = to_radeon_encoder(encoder); + if (radeon_encoder->devices & radeon_connector->devices) { + drm_mode_connector_attach_encoder(connector, encoder); + if (radeon_encoder->devices & (ATOM_DEVICE_LCD_SUPPORT)) { + if (rdev->is_atom_bios) + radeon_atom_backlight_init(radeon_encoder, connector); + else + radeon_legacy_backlight_init(radeon_encoder, connector); + rdev->mode_info.bl_encoder = radeon_encoder; + } + } + } + } +} + +void radeon_encoder_set_active_device(struct drm_encoder *encoder) +{ + struct drm_device *dev = encoder->dev; + struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); + struct drm_connector *connector; + + list_for_each_entry(connector, &dev->mode_config.connector_list, head) { + if (connector->encoder == encoder) { + struct radeon_connector *radeon_connector = to_radeon_connector(connector); + radeon_encoder->active_device = radeon_encoder->devices & radeon_connector->devices; + DRM_DEBUG_KMS("setting active device to %08x from %08x %08x for encoder %d\n", + radeon_encoder->active_device, radeon_encoder->devices, + radeon_connector->devices, encoder->encoder_type); + } + } +} + +struct drm_connector * +radeon_get_connector_for_encoder(struct drm_encoder *encoder) +{ + struct drm_device *dev = encoder->dev; + struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); + struct drm_connector *connector; + struct radeon_connector *radeon_connector; + + list_for_each_entry(connector, &dev->mode_config.connector_list, head) { + radeon_connector = to_radeon_connector(connector); + if (radeon_encoder->active_device & radeon_connector->devices) + return connector; + } + return NULL; +} + +struct drm_connector * +radeon_get_connector_for_encoder_init(struct drm_encoder *encoder) +{ + struct drm_device *dev = encoder->dev; + struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); + struct drm_connector *connector; + struct radeon_connector *radeon_connector; + + list_for_each_entry(connector, &dev->mode_config.connector_list, head) { + radeon_connector = to_radeon_connector(connector); + if (radeon_encoder->devices & radeon_connector->devices) + return connector; + } + return NULL; +} + +struct drm_encoder *radeon_get_external_encoder(struct drm_encoder *encoder) +{ + struct drm_device *dev = encoder->dev; + struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); + struct drm_encoder *other_encoder; + struct radeon_encoder *other_radeon_encoder; + + if (radeon_encoder->is_ext_encoder) + return NULL; + + list_for_each_entry(other_encoder, &dev->mode_config.encoder_list, head) { + if (other_encoder == encoder) + continue; + other_radeon_encoder = to_radeon_encoder(other_encoder); + if (other_radeon_encoder->is_ext_encoder && + (radeon_encoder->devices & other_radeon_encoder->devices)) + return other_encoder; + } + return NULL; +} + +u16 radeon_encoder_get_dp_bridge_encoder_id(struct drm_encoder *encoder) +{ + struct drm_encoder *other_encoder = radeon_get_external_encoder(encoder); + + if (other_encoder) { + struct radeon_encoder *radeon_encoder = to_radeon_encoder(other_encoder); + + switch (radeon_encoder->encoder_id) { + case ENCODER_OBJECT_ID_TRAVIS: + case ENCODER_OBJECT_ID_NUTMEG: + return radeon_encoder->encoder_id; + default: + return ENCODER_OBJECT_ID_NONE; + } + } + return ENCODER_OBJECT_ID_NONE; +} + +void radeon_panel_mode_fixup(struct drm_encoder *encoder, + struct drm_display_mode *adjusted_mode) +{ + struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); + struct drm_device *dev = encoder->dev; + struct radeon_device *rdev = dev->dev_private; + struct drm_display_mode *native_mode = &radeon_encoder->native_mode; + unsigned hblank = native_mode->htotal - native_mode->hdisplay; + unsigned vblank = native_mode->vtotal - native_mode->vdisplay; + unsigned hover = native_mode->hsync_start - native_mode->hdisplay; + unsigned vover = native_mode->vsync_start - native_mode->vdisplay; + unsigned hsync_width = native_mode->hsync_end - native_mode->hsync_start; + unsigned vsync_width = native_mode->vsync_end - native_mode->vsync_start; + + adjusted_mode->clock = native_mode->clock; + adjusted_mode->flags = native_mode->flags; + + if (ASIC_IS_AVIVO(rdev)) { + adjusted_mode->hdisplay = native_mode->hdisplay; + adjusted_mode->vdisplay = native_mode->vdisplay; + } + + adjusted_mode->htotal = native_mode->hdisplay + hblank; + adjusted_mode->hsync_start = native_mode->hdisplay + hover; + adjusted_mode->hsync_end = adjusted_mode->hsync_start + hsync_width; + + adjusted_mode->vtotal = native_mode->vdisplay + vblank; + adjusted_mode->vsync_start = native_mode->vdisplay + vover; + adjusted_mode->vsync_end = adjusted_mode->vsync_start + vsync_width; + + drm_mode_set_crtcinfo(adjusted_mode, CRTC_INTERLACE_HALVE_V); + + if (ASIC_IS_AVIVO(rdev)) { + adjusted_mode->crtc_hdisplay = native_mode->hdisplay; + adjusted_mode->crtc_vdisplay = native_mode->vdisplay; + } + + adjusted_mode->crtc_htotal = adjusted_mode->crtc_hdisplay + hblank; + adjusted_mode->crtc_hsync_start = adjusted_mode->crtc_hdisplay + hover; + adjusted_mode->crtc_hsync_end = adjusted_mode->crtc_hsync_start + hsync_width; + + adjusted_mode->crtc_vtotal = adjusted_mode->crtc_vdisplay + vblank; + adjusted_mode->crtc_vsync_start = adjusted_mode->crtc_vdisplay + vover; + adjusted_mode->crtc_vsync_end = adjusted_mode->crtc_vsync_start + vsync_width; + +} + +bool radeon_dig_monitor_is_duallink(struct drm_encoder *encoder, + u32 pixel_clock) +{ + struct drm_device *dev = encoder->dev; + struct radeon_device *rdev = dev->dev_private; + struct drm_connector *connector; + struct radeon_connector *radeon_connector; + struct radeon_connector_atom_dig *dig_connector; + + connector = radeon_get_connector_for_encoder(encoder); + /* if we don't have an active device yet, just use one of + * the connectors tied to the encoder. + */ + if (!connector) + connector = radeon_get_connector_for_encoder_init(encoder); + radeon_connector = to_radeon_connector(connector); + + switch (connector->connector_type) { + case DRM_MODE_CONNECTOR_DVII: + case DRM_MODE_CONNECTOR_HDMIB: + if (radeon_connector->use_digital) { + /* HDMI 1.3 supports up to 340 Mhz over single link */ + if (ASIC_IS_DCE6(rdev) && drm_detect_hdmi_monitor(radeon_connector->edid)) { + if (pixel_clock > 340000) + return true; + else + return false; + } else { + if (pixel_clock > 165000) + return true; + else + return false; + } + } else + return false; + case DRM_MODE_CONNECTOR_DVID: + case DRM_MODE_CONNECTOR_HDMIA: + case DRM_MODE_CONNECTOR_DisplayPort: + dig_connector = radeon_connector->con_priv; + if ((dig_connector->dp_sink_type == CONNECTOR_OBJECT_ID_DISPLAYPORT) || + (dig_connector->dp_sink_type == CONNECTOR_OBJECT_ID_eDP)) + return false; + else { + /* HDMI 1.3 supports up to 340 Mhz over single link */ + if (ASIC_IS_DCE6(rdev) && drm_detect_hdmi_monitor(radeon_connector->edid)) { + if (pixel_clock > 340000) + return true; + else + return false; + } else { + if (pixel_clock > 165000) + return true; + else + return false; + } + } + default: + return false; + } +} + diff --git a/sys/dev/drm2/radeon/radeon_family.h b/sys/dev/drm2/radeon/radeon_family.h new file mode 100644 index 00000000000..7ac4937d133 --- /dev/null +++ b/sys/dev/drm2/radeon/radeon_family.h @@ -0,0 +1,117 @@ +/* + * Copyright 2008 Advanced Micro Devices, Inc. + * Copyright 2008 Red Hat Inc. + * Copyright 2009 Jerome Glisse. + * + * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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. + * + * Authors: Dave Airlie + * Alex Deucher + * Jerome Glisse + */ + +#include +__FBSDID("$FreeBSD$"); + +/* this file defines the CHIP_ and family flags used in the pciids, + * its is common between kms and non-kms because duplicating it and + * changing one place is fail. + */ +#ifndef RADEON_FAMILY_H +#define RADEON_FAMILY_H +/* + * Radeon chip families + */ +enum radeon_family { + CHIP_R100 = 0, + CHIP_RV100, + CHIP_RS100, + CHIP_RV200, + CHIP_RS200, + CHIP_R200, + CHIP_RV250, + CHIP_RS300, + CHIP_RV280, + CHIP_R300, + CHIP_R350, + CHIP_RV350, + CHIP_RV380, + CHIP_R420, + CHIP_R423, + CHIP_RV410, + CHIP_RS400, + CHIP_RS480, + CHIP_RS600, + CHIP_RS690, + CHIP_RS740, + CHIP_RV515, + CHIP_R520, + CHIP_RV530, + CHIP_RV560, + CHIP_RV570, + CHIP_R580, + CHIP_R600, + CHIP_RV610, + CHIP_RV630, + CHIP_RV670, + CHIP_RV620, + CHIP_RV635, + CHIP_RS780, + CHIP_RS880, + CHIP_RV770, + CHIP_RV730, + CHIP_RV710, + CHIP_RV740, + CHIP_CEDAR, + CHIP_REDWOOD, + CHIP_JUNIPER, + CHIP_CYPRESS, + CHIP_HEMLOCK, + CHIP_PALM, + CHIP_SUMO, + CHIP_SUMO2, + CHIP_BARTS, + CHIP_TURKS, + CHIP_CAICOS, + CHIP_CAYMAN, + CHIP_ARUBA, + CHIP_TAHITI, + CHIP_PITCAIRN, + CHIP_VERDE, + CHIP_LAST, +}; + +/* + * Chip flags + */ +enum radeon_chip_flags { + RADEON_FAMILY_MASK = 0x0000ffffUL, + RADEON_FLAGS_MASK = 0xffff0000UL, + RADEON_IS_MOBILITY = 0x00010000UL, + RADEON_IS_IGP = 0x00020000UL, + RADEON_SINGLE_CRTC = 0x00040000UL, + RADEON_IS_AGP = 0x00080000UL, + RADEON_HAS_HIERZ = 0x00100000UL, + RADEON_IS_PCIE = 0x00200000UL, + RADEON_NEW_MEMMAP = 0x00400000UL, + RADEON_IS_PCI = 0x00800000UL, + RADEON_IS_IGPGART = 0x01000000UL, +}; + +#endif diff --git a/sys/dev/drm2/radeon/radeon_fb.c b/sys/dev/drm2/radeon/radeon_fb.c new file mode 100644 index 00000000000..fc247e2bbc6 --- /dev/null +++ b/sys/dev/drm2/radeon/radeon_fb.c @@ -0,0 +1,433 @@ +/* + * Copyright © 2007 David Airlie + * + * 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 (including the next + * paragraph) 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. + * + * Authors: + * David Airlie + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include "radeon.h" + +#include + +/* object hierarchy - + this contains a helper + a radeon fb + the helper contains a pointer to radeon framebuffer baseclass. +*/ +struct radeon_fbdev { + struct drm_fb_helper helper; + struct radeon_framebuffer rfb; + struct list_head fbdev_list; + struct radeon_device *rdev; +}; + +#ifdef DUMBBELL_WIP +static struct fb_ops radeonfb_ops = { + .owner = THIS_MODULE, + .fb_check_var = drm_fb_helper_check_var, + .fb_set_par = drm_fb_helper_set_par, + .fb_fillrect = cfb_fillrect, + .fb_copyarea = cfb_copyarea, + .fb_imageblit = cfb_imageblit, + .fb_pan_display = drm_fb_helper_pan_display, + .fb_blank = drm_fb_helper_blank, + .fb_setcmap = drm_fb_helper_setcmap, + .fb_debug_enter = drm_fb_helper_debug_enter, + .fb_debug_leave = drm_fb_helper_debug_leave, +}; +#endif /* DUMBBELL_WIP */ + + +int radeon_align_pitch(struct radeon_device *rdev, int width, int bpp, bool tiled) +{ + int aligned = width; + int align_large = (ASIC_IS_AVIVO(rdev)) || tiled; + int pitch_mask = 0; + + switch (bpp / 8) { + case 1: + pitch_mask = align_large ? 255 : 127; + break; + case 2: + pitch_mask = align_large ? 127 : 31; + break; + case 3: + case 4: + pitch_mask = align_large ? 63 : 15; + break; + } + + aligned += pitch_mask; + aligned &= ~pitch_mask; + return aligned; +} + +static void radeonfb_destroy_pinned_object(struct drm_gem_object *gobj) +{ + struct radeon_bo *rbo = gem_to_radeon_bo(gobj); + int ret; + + ret = radeon_bo_reserve(rbo, false); + if (likely(ret == 0)) { + radeon_bo_kunmap(rbo); + radeon_bo_unpin(rbo); + radeon_bo_unreserve(rbo); + } + drm_gem_object_unreference_unlocked(gobj); +} + +static int radeonfb_create_pinned_object(struct radeon_fbdev *rfbdev, + struct drm_mode_fb_cmd2 *mode_cmd, + struct drm_gem_object **gobj_p) +{ + struct radeon_device *rdev = rfbdev->rdev; + struct drm_gem_object *gobj = NULL; + struct radeon_bo *rbo = NULL; + bool fb_tiled = false; /* useful for testing */ + u32 tiling_flags = 0; + int ret; + int aligned_size, size; + int height = mode_cmd->height; + u32 bpp, depth; + + drm_fb_get_bpp_depth(mode_cmd->pixel_format, &depth, &bpp); + + /* need to align pitch with crtc limits */ + mode_cmd->pitches[0] = radeon_align_pitch(rdev, mode_cmd->width, bpp, + fb_tiled) * ((bpp + 1) / 8); + + if (rdev->family >= CHIP_R600) + height = roundup2(mode_cmd->height, 8); + size = mode_cmd->pitches[0] * height; + aligned_size = roundup2(size, PAGE_SIZE); + ret = radeon_gem_object_create(rdev, aligned_size, 0, + RADEON_GEM_DOMAIN_VRAM, + false, true, + &gobj); + if (ret) { + DRM_ERROR("failed to allocate framebuffer (%d)\n", + aligned_size); + return -ENOMEM; + } + rbo = gem_to_radeon_bo(gobj); + + if (fb_tiled) + tiling_flags = RADEON_TILING_MACRO; + +#ifdef __BIG_ENDIAN + switch (bpp) { + case 32: + tiling_flags |= RADEON_TILING_SWAP_32BIT; + break; + case 16: + tiling_flags |= RADEON_TILING_SWAP_16BIT; + default: + break; + } +#endif + + if (tiling_flags) { + ret = radeon_bo_set_tiling_flags(rbo, + tiling_flags | RADEON_TILING_SURFACE, + mode_cmd->pitches[0]); + if (ret) + dev_err(rdev->dev, "FB failed to set tiling flags\n"); + } + + + ret = radeon_bo_reserve(rbo, false); + if (unlikely(ret != 0)) + goto out_unref; + /* Only 27 bit offset for legacy CRTC */ + ret = radeon_bo_pin_restricted(rbo, RADEON_GEM_DOMAIN_VRAM, + ASIC_IS_AVIVO(rdev) ? 0 : 1 << 27, + NULL); + if (ret) { + radeon_bo_unreserve(rbo); + goto out_unref; + } + if (fb_tiled) + radeon_bo_check_tiling(rbo, 0, 0); + ret = radeon_bo_kmap(rbo, NULL); + radeon_bo_unreserve(rbo); + if (ret) { + goto out_unref; + } + + *gobj_p = gobj; + return 0; +out_unref: + radeonfb_destroy_pinned_object(gobj); + *gobj_p = NULL; + return ret; +} + +static int radeonfb_create(struct radeon_fbdev *rfbdev, + struct drm_fb_helper_surface_size *sizes) +{ + struct radeon_device *rdev = rfbdev->rdev; +#ifdef DUMBBELL_WIP + struct fb_info *info; +#endif /* DUMBBELL_WIP */ + struct drm_framebuffer *fb = NULL; + struct drm_mode_fb_cmd2 mode_cmd; + struct drm_gem_object *gobj = NULL; + struct radeon_bo *rbo = NULL; +#ifdef DUMBBELL_WIP + device_t device = rdev->dev; +#endif /* DUMBBELL_WIP */ + int ret; +#ifdef DUMBBELL_WIP + unsigned long tmp; +#endif /* DUMBBELL_WIP */ + + mode_cmd.width = sizes->surface_width; + mode_cmd.height = sizes->surface_height; + + /* avivo can't scanout real 24bpp */ + if ((sizes->surface_bpp == 24) && ASIC_IS_AVIVO(rdev)) + sizes->surface_bpp = 32; + + mode_cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp, + sizes->surface_depth); + + ret = radeonfb_create_pinned_object(rfbdev, &mode_cmd, &gobj); + if (ret) { + DRM_ERROR("failed to create fbcon object %d\n", ret); + return ret; + } + + rbo = gem_to_radeon_bo(gobj); + +#ifdef DUMBBELL_WIP + /* okay we have an object now allocate the framebuffer */ + info = framebuffer_alloc(0, device); + if (info == NULL) { + ret = -ENOMEM; + goto out_unref; + } + + info->par = rfbdev; +#endif /* DUMBBELL_WIP */ + + ret = radeon_framebuffer_init(rdev->ddev, &rfbdev->rfb, &mode_cmd, gobj); + if (ret) { + DRM_ERROR("failed to initalise framebuffer %d\n", ret); + goto out_unref; + } + + fb = &rfbdev->rfb.base; + + /* setup helper */ + rfbdev->helper.fb = fb; +#ifdef DUMBBELL_WIP + rfbdev->helper.fbdev = info; + + memset_io(rbo->kptr, 0x0, radeon_bo_size(rbo)); + + strcpy(info->fix.id, "radeondrmfb"); + + drm_fb_helper_fill_fix(info, fb->pitches[0], fb->depth); + + info->flags = FBINFO_DEFAULT | FBINFO_CAN_FORCE_OUTPUT; + info->fbops = &radeonfb_ops; + + tmp = radeon_bo_gpu_offset(rbo) - rdev->mc.vram_start; + info->fix.smem_start = rdev->mc.aper_base + tmp; + info->fix.smem_len = radeon_bo_size(rbo); + info->screen_base = rbo->kptr; + info->screen_size = radeon_bo_size(rbo); + + drm_fb_helper_fill_var(info, &rfbdev->helper, sizes->fb_width, sizes->fb_height); + + /* setup aperture base/size for vesafb takeover */ + info->apertures = alloc_apertures(1); + if (!info->apertures) { + ret = -ENOMEM; + goto out_unref; + } + info->apertures->ranges[0].base = rdev->ddev->mode_config.fb_base; + info->apertures->ranges[0].size = rdev->mc.aper_size; + + /* Use default scratch pixmap (info->pixmap.flags = FB_PIXMAP_SYSTEM) */ + + if (info->screen_base == NULL) { + ret = -ENOSPC; + goto out_unref; + } + + ret = fb_alloc_cmap(&info->cmap, 256, 0); + if (ret) { + ret = -ENOMEM; + goto out_unref; + } + + DRM_INFO("fb mappable at 0x%lX\n", info->fix.smem_start); + DRM_INFO("vram apper at 0x%lX\n", (unsigned long)rdev->mc.aper_base); + DRM_INFO("size %lu\n", (unsigned long)radeon_bo_size(rbo)); + DRM_INFO("fb depth is %d\n", fb->depth); + DRM_INFO(" pitch is %d\n", fb->pitches[0]); + + vga_switcheroo_client_fb_set(rdev->ddev->pdev, info); +#endif /* DUMBBELL_WIP */ + return 0; + +out_unref: + if (rbo) { + + } + if (fb && ret) { + drm_gem_object_unreference(gobj); + drm_framebuffer_cleanup(fb); + free(fb, DRM_MEM_DRIVER); /* XXX malloc'd in radeon_user_framebuffer_create? */ + } + return ret; +} + +static int radeon_fb_find_or_create_single(struct drm_fb_helper *helper, + struct drm_fb_helper_surface_size *sizes) +{ + struct radeon_fbdev *rfbdev = (struct radeon_fbdev *)helper; + int new_fb = 0; + int ret; + + if (!helper->fb) { + ret = radeonfb_create(rfbdev, sizes); + if (ret) + return ret; + new_fb = 1; + } + return new_fb; +} + +void radeon_fb_output_poll_changed(struct radeon_device *rdev) +{ + drm_fb_helper_hotplug_event(&rdev->mode_info.rfbdev->helper); +} + +static int radeon_fbdev_destroy(struct drm_device *dev, struct radeon_fbdev *rfbdev) +{ +#ifdef DUMBBELL_WIP + struct fb_info *info; +#endif /* DUMBBELL_WIP */ + struct radeon_framebuffer *rfb = &rfbdev->rfb; + +#ifdef DUMBBELL_WIP + if (rfbdev->helper.fbdev) { + info = rfbdev->helper.fbdev; + + unregister_framebuffer(info); + if (info->cmap.len) + fb_dealloc_cmap(&info->cmap); + framebuffer_release(info); + } +#endif /* DUMBBELL_WIP */ + + if (rfb->obj) { + DRM_UNLOCK(dev); /* Work around lock recursion. dumbbell@ */ + radeonfb_destroy_pinned_object(rfb->obj); + DRM_LOCK(dev); + rfb->obj = NULL; + } + drm_fb_helper_fini(&rfbdev->helper); + drm_framebuffer_cleanup(&rfb->base); + + return 0; +} + +static struct drm_fb_helper_funcs radeon_fb_helper_funcs = { + .gamma_set = radeon_crtc_fb_gamma_set, + .gamma_get = radeon_crtc_fb_gamma_get, + .fb_probe = radeon_fb_find_or_create_single, +}; + +int radeon_fbdev_init(struct radeon_device *rdev) +{ + struct radeon_fbdev *rfbdev; + int bpp_sel = 32; + int ret; + + /* select 8 bpp console on RN50 or 16MB cards */ + if (ASIC_IS_RN50(rdev) || rdev->mc.real_vram_size <= (32*1024*1024)) + bpp_sel = 8; + + rfbdev = malloc(sizeof(struct radeon_fbdev), + DRM_MEM_DRIVER, M_WAITOK | M_ZERO); + if (!rfbdev) + return -ENOMEM; + + rfbdev->rdev = rdev; + rdev->mode_info.rfbdev = rfbdev; + rfbdev->helper.funcs = &radeon_fb_helper_funcs; + + ret = drm_fb_helper_init(rdev->ddev, &rfbdev->helper, + rdev->num_crtc, + RADEONFB_CONN_LIMIT); + if (ret) { + free(rfbdev, DRM_MEM_DRIVER); + return ret; + } + + drm_fb_helper_single_add_all_connectors(&rfbdev->helper); + drm_fb_helper_initial_config(&rfbdev->helper, bpp_sel); + return 0; +} + +void radeon_fbdev_fini(struct radeon_device *rdev) +{ + if (!rdev->mode_info.rfbdev) + return; + + radeon_fbdev_destroy(rdev->ddev, rdev->mode_info.rfbdev); + free(rdev->mode_info.rfbdev, DRM_MEM_DRIVER); + rdev->mode_info.rfbdev = NULL; +} + +void radeon_fbdev_set_suspend(struct radeon_device *rdev, int state) +{ +#ifdef DUMBBELL_WIP + fb_set_suspend(rdev->mode_info.rfbdev->helper.fbdev, state); +#endif /* DUMBBELL_WIP */ +} + +int radeon_fbdev_total_size(struct radeon_device *rdev) +{ + struct radeon_bo *robj; + int size = 0; + + robj = gem_to_radeon_bo(rdev->mode_info.rfbdev->rfb.obj); + size += radeon_bo_size(robj); + return size; +} + +bool radeon_fbdev_robj_is_fb(struct radeon_device *rdev, struct radeon_bo *robj) +{ + if (robj == gem_to_radeon_bo(rdev->mode_info.rfbdev->rfb.obj)) + return true; + return false; +} diff --git a/sys/dev/drm2/radeon/radeon_fence.c b/sys/dev/drm2/radeon/radeon_fence.c new file mode 100644 index 00000000000..07b1a64b53b --- /dev/null +++ b/sys/dev/drm2/radeon/radeon_fence.c @@ -0,0 +1,983 @@ +/* + * Copyright 2009 Jerome Glisse. + * All Rights Reserved. + * + * 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, sub license, 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 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 NON-INFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS 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. + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + */ +/* + * Authors: + * Jerome Glisse + * Dave Airlie + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include "radeon_reg.h" +#include "radeon.h" +#ifdef DUMBBELL_WIP +#include "radeon_trace.h" +#endif /* DUMBBELL_WIP */ + +/* + * Fences + * Fences mark an event in the GPUs pipeline and are used + * for GPU/CPU synchronization. When the fence is written, + * it is expected that all buffers associated with that fence + * are no longer in use by the associated ring on the GPU and + * that the the relevant GPU caches have been flushed. Whether + * we use a scratch register or memory location depends on the asic + * and whether writeback is enabled. + */ + +/** + * radeon_fence_write - write a fence value + * + * @rdev: radeon_device pointer + * @seq: sequence number to write + * @ring: ring index the fence is associated with + * + * Writes a fence value to memory or a scratch register (all asics). + */ +static void radeon_fence_write(struct radeon_device *rdev, u32 seq, int ring) +{ + struct radeon_fence_driver *drv = &rdev->fence_drv[ring]; + if (likely(rdev->wb.enabled || !drv->scratch_reg)) { + *drv->cpu_addr = cpu_to_le32(seq); + } else { + WREG32(drv->scratch_reg, seq); + } +} + +/** + * radeon_fence_read - read a fence value + * + * @rdev: radeon_device pointer + * @ring: ring index the fence is associated with + * + * Reads a fence value from memory or a scratch register (all asics). + * Returns the value of the fence read from memory or register. + */ +static u32 radeon_fence_read(struct radeon_device *rdev, int ring) +{ + struct radeon_fence_driver *drv = &rdev->fence_drv[ring]; + u32 seq = 0; + + if (likely(rdev->wb.enabled || !drv->scratch_reg)) { + seq = le32_to_cpu(*drv->cpu_addr); + } else { + seq = RREG32(drv->scratch_reg); + } + return seq; +} + +/** + * radeon_fence_emit - emit a fence on the requested ring + * + * @rdev: radeon_device pointer + * @fence: radeon fence object + * @ring: ring index the fence is associated with + * + * Emits a fence command on the requested ring (all asics). + * Returns 0 on success, -ENOMEM on failure. + */ +int radeon_fence_emit(struct radeon_device *rdev, + struct radeon_fence **fence, + int ring) +{ + /* we are protected by the ring emission mutex */ + *fence = malloc(sizeof(struct radeon_fence), DRM_MEM_DRIVER, M_WAITOK); + if ((*fence) == NULL) { + return -ENOMEM; + } + refcount_init(&((*fence)->kref), 1); + (*fence)->rdev = rdev; + (*fence)->seq = ++rdev->fence_drv[ring].sync_seq[ring]; + (*fence)->ring = ring; + radeon_fence_ring_emit(rdev, ring, *fence); + CTR2(KTR_DRM, "radeon fence: emit (ring=%d, seq=%d)", ring, (*fence)->seq); + return 0; +} + +/** + * radeon_fence_process - process a fence + * + * @rdev: radeon_device pointer + * @ring: ring index the fence is associated with + * + * Checks the current fence value and wakes the fence queue + * if the sequence number has increased (all asics). + */ +void radeon_fence_process(struct radeon_device *rdev, int ring) +{ + uint64_t seq, last_seq, last_emitted; + unsigned count_loop = 0; + bool wake = false; + + /* Note there is a scenario here for an infinite loop but it's + * very unlikely to happen. For it to happen, the current polling + * process need to be interrupted by another process and another + * process needs to update the last_seq btw the atomic read and + * xchg of the current process. + * + * More over for this to go in infinite loop there need to be + * continuously new fence signaled ie radeon_fence_read needs + * to return a different value each time for both the currently + * polling process and the other process that xchg the last_seq + * btw atomic read and xchg of the current process. And the + * value the other process set as last seq must be higher than + * the seq value we just read. Which means that current process + * need to be interrupted after radeon_fence_read and before + * atomic xchg. + * + * To be even more safe we count the number of time we loop and + * we bail after 10 loop just accepting the fact that we might + * have temporarly set the last_seq not to the true real last + * seq but to an older one. + */ + last_seq = atomic_load_acq_64(&rdev->fence_drv[ring].last_seq); + do { + last_emitted = rdev->fence_drv[ring].sync_seq[ring]; + seq = radeon_fence_read(rdev, ring); + seq |= last_seq & 0xffffffff00000000LL; + if (seq < last_seq) { + seq &= 0xffffffff; + seq |= last_emitted & 0xffffffff00000000LL; + } + + if (seq <= last_seq || seq > last_emitted) { + break; + } + /* If we loop over we don't want to return without + * checking if a fence is signaled as it means that the + * seq we just read is different from the previous on. + */ + wake = true; + last_seq = seq; + if ((count_loop++) > 10) { + /* We looped over too many time leave with the + * fact that we might have set an older fence + * seq then the current real last seq as signaled + * by the hw. + */ + break; + } + } while (atomic64_xchg(&rdev->fence_drv[ring].last_seq, seq) > seq); + + if (wake) { + rdev->fence_drv[ring].last_activity = jiffies; + cv_broadcast(&rdev->fence_queue); + } +} + +/** + * radeon_fence_destroy - destroy a fence + * + * @kref: fence kref + * + * Frees the fence object (all asics). + */ +static void radeon_fence_destroy(struct radeon_fence *fence) +{ + + free(fence, DRM_MEM_DRIVER); +} + +/** + * radeon_fence_seq_signaled - check if a fence sequeuce number has signaled + * + * @rdev: radeon device pointer + * @seq: sequence number + * @ring: ring index the fence is associated with + * + * Check if the last singled fence sequnce number is >= the requested + * sequence number (all asics). + * Returns true if the fence has signaled (current fence value + * is >= requested value) or false if it has not (current fence + * value is < the requested value. Helper function for + * radeon_fence_signaled(). + */ +static bool radeon_fence_seq_signaled(struct radeon_device *rdev, + u64 seq, unsigned ring) +{ + if (atomic_load_acq_64(&rdev->fence_drv[ring].last_seq) >= seq) { + return true; + } + /* poll new last sequence at least once */ + radeon_fence_process(rdev, ring); + if (atomic_load_acq_64(&rdev->fence_drv[ring].last_seq) >= seq) { + return true; + } + return false; +} + +/** + * radeon_fence_signaled - check if a fence has signaled + * + * @fence: radeon fence object + * + * Check if the requested fence has signaled (all asics). + * Returns true if the fence has signaled or false if it has not. + */ +bool radeon_fence_signaled(struct radeon_fence *fence) +{ + if (!fence) { + return true; + } + if (fence->seq == RADEON_FENCE_SIGNALED_SEQ) { + return true; + } + if (radeon_fence_seq_signaled(fence->rdev, fence->seq, fence->ring)) { + fence->seq = RADEON_FENCE_SIGNALED_SEQ; + return true; + } + return false; +} + +/** + * radeon_fence_wait_seq - wait for a specific sequence number + * + * @rdev: radeon device pointer + * @target_seq: sequence number we want to wait for + * @ring: ring index the fence is associated with + * @intr: use interruptable sleep + * @lock_ring: whether the ring should be locked or not + * + * Wait for the requested sequence number to be written (all asics). + * @intr selects whether to use interruptable (true) or non-interruptable + * (false) sleep when waiting for the sequence number. Helper function + * for radeon_fence_wait(), et al. + * Returns 0 if the sequence number has passed, error for all other cases. + * -EDEADLK is returned when a GPU lockup has been detected and the ring is + * marked as not ready so no further jobs get scheduled until a successful + * reset. + */ +static int radeon_fence_wait_seq(struct radeon_device *rdev, u64 target_seq, + unsigned ring, bool intr, bool lock_ring) +{ + unsigned long timeout, last_activity; + uint64_t seq; + unsigned i; + bool signaled, fence_queue_locked; + int r; + + while (target_seq > atomic_load_acq_64(&rdev->fence_drv[ring].last_seq)) { + if (!rdev->ring[ring].ready) { + return -EBUSY; + } + + timeout = jiffies - RADEON_FENCE_JIFFIES_TIMEOUT; + if (time_after(rdev->fence_drv[ring].last_activity, timeout)) { + /* the normal case, timeout is somewhere before last_activity */ + timeout = rdev->fence_drv[ring].last_activity - timeout; + } else { + /* either jiffies wrapped around, or no fence was signaled in the last 500ms + * anyway we will just wait for the minimum amount and then check for a lockup + */ + timeout = 1; + } + seq = atomic_load_acq_64(&rdev->fence_drv[ring].last_seq); + /* Save current last activity valuee, used to check for GPU lockups */ + last_activity = rdev->fence_drv[ring].last_activity; + + CTR2(KTR_DRM, "radeon fence: wait begin (ring=%d, seq=%d)", + ring, seq); + + radeon_irq_kms_sw_irq_get(rdev, ring); + fence_queue_locked = false; + r = 0; + while (!(signaled = radeon_fence_seq_signaled(rdev, + target_seq, ring))) { + if (!fence_queue_locked) { + mtx_lock(&rdev->fence_queue_mtx); + fence_queue_locked = true; + } + if (intr) { + r = cv_timedwait_sig(&rdev->fence_queue, + &rdev->fence_queue_mtx, + timeout); + } else { + r = cv_timedwait(&rdev->fence_queue, + &rdev->fence_queue_mtx, + timeout); + } + if (r != 0) { + if (r == EWOULDBLOCK) { + signaled = + radeon_fence_seq_signaled( + rdev, target_seq, ring); + } + break; + } + } + if (fence_queue_locked) { + mtx_unlock(&rdev->fence_queue_mtx); + } + radeon_irq_kms_sw_irq_put(rdev, ring); + if (unlikely(r == EINTR || r == ERESTART)) { + return -r; + } + CTR2(KTR_DRM, "radeon fence: wait end (ring=%d, seq=%d)", + ring, seq); + + if (unlikely(!signaled)) { +#ifndef __FreeBSD__ + /* we were interrupted for some reason and fence + * isn't signaled yet, resume waiting */ + if (r) { + continue; + } +#endif + + /* check if sequence value has changed since last_activity */ + if (seq != atomic_load_acq_64(&rdev->fence_drv[ring].last_seq)) { + continue; + } + + if (lock_ring) { + sx_xlock(&rdev->ring_lock); + } + + /* test if somebody else has already decided that this is a lockup */ + if (last_activity != rdev->fence_drv[ring].last_activity) { + if (lock_ring) { + sx_xunlock(&rdev->ring_lock); + } + continue; + } + + if (radeon_ring_is_lockup(rdev, ring, &rdev->ring[ring])) { + /* good news we believe it's a lockup */ + dev_warn(rdev->dev, "GPU lockup (waiting for 0x%016jx last fence id 0x%016jx)\n", + (uintmax_t)target_seq, (uintmax_t)seq); + + /* change last activity so nobody else think there is a lockup */ + for (i = 0; i < RADEON_NUM_RINGS; ++i) { + rdev->fence_drv[i].last_activity = jiffies; + } + + /* mark the ring as not ready any more */ + rdev->ring[ring].ready = false; + if (lock_ring) { + sx_xunlock(&rdev->ring_lock); + } + return -EDEADLK; + } + + if (lock_ring) { + sx_xunlock(&rdev->ring_lock); + } + } + } + return 0; +} + +/** + * radeon_fence_wait - wait for a fence to signal + * + * @fence: radeon fence object + * @intr: use interruptable sleep + * + * Wait for the requested fence to signal (all asics). + * @intr selects whether to use interruptable (true) or non-interruptable + * (false) sleep when waiting for the fence. + * Returns 0 if the fence has passed, error for all other cases. + */ +int radeon_fence_wait(struct radeon_fence *fence, bool intr) +{ + int r; + + if (fence == NULL) { + DRM_ERROR("Querying an invalid fence : %p !\n", fence); + return -EINVAL; + } + + r = radeon_fence_wait_seq(fence->rdev, fence->seq, + fence->ring, intr, true); + if (r) { + return r; + } + fence->seq = RADEON_FENCE_SIGNALED_SEQ; + return 0; +} + +static bool radeon_fence_any_seq_signaled(struct radeon_device *rdev, u64 *seq) +{ + unsigned i; + + for (i = 0; i < RADEON_NUM_RINGS; ++i) { + if (seq[i] && radeon_fence_seq_signaled(rdev, seq[i], i)) { + return true; + } + } + return false; +} + +/** + * radeon_fence_wait_any_seq - wait for a sequence number on any ring + * + * @rdev: radeon device pointer + * @target_seq: sequence number(s) we want to wait for + * @intr: use interruptable sleep + * + * Wait for the requested sequence number(s) to be written by any ring + * (all asics). Sequnce number array is indexed by ring id. + * @intr selects whether to use interruptable (true) or non-interruptable + * (false) sleep when waiting for the sequence number. Helper function + * for radeon_fence_wait_any(), et al. + * Returns 0 if the sequence number has passed, error for all other cases. + */ +static int radeon_fence_wait_any_seq(struct radeon_device *rdev, + u64 *target_seq, bool intr) +{ + unsigned long timeout, last_activity, tmp; + unsigned i, ring = RADEON_NUM_RINGS; + bool signaled, fence_queue_locked; + int r; + + for (i = 0, last_activity = 0; i < RADEON_NUM_RINGS; ++i) { + if (!target_seq[i]) { + continue; + } + + /* use the most recent one as indicator */ + if (time_after(rdev->fence_drv[i].last_activity, last_activity)) { + last_activity = rdev->fence_drv[i].last_activity; + } + + /* For lockup detection just pick the lowest ring we are + * actively waiting for + */ + if (i < ring) { + ring = i; + } + } + + /* nothing to wait for ? */ + if (ring == RADEON_NUM_RINGS) { + return -ENOENT; + } + + while (!radeon_fence_any_seq_signaled(rdev, target_seq)) { + timeout = jiffies - RADEON_FENCE_JIFFIES_TIMEOUT; + if (time_after(last_activity, timeout)) { + /* the normal case, timeout is somewhere before last_activity */ + timeout = last_activity - timeout; + } else { + /* either jiffies wrapped around, or no fence was signaled in the last 500ms + * anyway we will just wait for the minimum amount and then check for a lockup + */ + timeout = 1; + } + + CTR2(KTR_DRM, "radeon fence: wait begin (ring=%d, target_seq=%d)", + ring, target_seq[ring]); + for (i = 0; i < RADEON_NUM_RINGS; ++i) { + if (target_seq[i]) { + radeon_irq_kms_sw_irq_get(rdev, i); + } + } + fence_queue_locked = false; + r = 0; + while (!(signaled = radeon_fence_any_seq_signaled(rdev, + target_seq))) { + if (!fence_queue_locked) { + mtx_lock(&rdev->fence_queue_mtx); + fence_queue_locked = true; + } + if (intr) { + r = cv_timedwait_sig(&rdev->fence_queue, + &rdev->fence_queue_mtx, + timeout); + } else { + r = cv_timedwait(&rdev->fence_queue, + &rdev->fence_queue_mtx, + timeout); + } + if (r != 0) { + if (r == EWOULDBLOCK) { + signaled = + radeon_fence_any_seq_signaled( + rdev, target_seq); + } + break; + } + } + if (fence_queue_locked) { + mtx_unlock(&rdev->fence_queue_mtx); + } + for (i = 0; i < RADEON_NUM_RINGS; ++i) { + if (target_seq[i]) { + radeon_irq_kms_sw_irq_put(rdev, i); + } + } + if (unlikely(r == EINTR || r == ERESTART)) { + return -r; + } + CTR2(KTR_DRM, "radeon fence: wait end (ring=%d, target_seq=%d)", + ring, target_seq[ring]); + + if (unlikely(!signaled)) { +#ifndef __FreeBSD__ + /* we were interrupted for some reason and fence + * isn't signaled yet, resume waiting */ + if (r) { + continue; + } +#endif + + sx_xlock(&rdev->ring_lock); + for (i = 0, tmp = 0; i < RADEON_NUM_RINGS; ++i) { + if (time_after(rdev->fence_drv[i].last_activity, tmp)) { + tmp = rdev->fence_drv[i].last_activity; + } + } + /* test if somebody else has already decided that this is a lockup */ + if (last_activity != tmp) { + last_activity = tmp; + sx_xunlock(&rdev->ring_lock); + continue; + } + + if (radeon_ring_is_lockup(rdev, ring, &rdev->ring[ring])) { + /* good news we believe it's a lockup */ + dev_warn(rdev->dev, "GPU lockup (waiting for 0x%016jx)\n", + (uintmax_t)target_seq[ring]); + + /* change last activity so nobody else think there is a lockup */ + for (i = 0; i < RADEON_NUM_RINGS; ++i) { + rdev->fence_drv[i].last_activity = jiffies; + } + + /* mark the ring as not ready any more */ + rdev->ring[ring].ready = false; + sx_xunlock(&rdev->ring_lock); + return -EDEADLK; + } + sx_xunlock(&rdev->ring_lock); + } + } + return 0; +} + +/** + * radeon_fence_wait_any - wait for a fence to signal on any ring + * + * @rdev: radeon device pointer + * @fences: radeon fence object(s) + * @intr: use interruptable sleep + * + * Wait for any requested fence to signal (all asics). Fence + * array is indexed by ring id. @intr selects whether to use + * interruptable (true) or non-interruptable (false) sleep when + * waiting for the fences. Used by the suballocator. + * Returns 0 if any fence has passed, error for all other cases. + */ +int radeon_fence_wait_any(struct radeon_device *rdev, + struct radeon_fence **fences, + bool intr) +{ + uint64_t seq[RADEON_NUM_RINGS]; + unsigned i; + int r; + + for (i = 0; i < RADEON_NUM_RINGS; ++i) { + seq[i] = 0; + + if (!fences[i]) { + continue; + } + + if (fences[i]->seq == RADEON_FENCE_SIGNALED_SEQ) { + /* something was allready signaled */ + return 0; + } + + seq[i] = fences[i]->seq; + } + + r = radeon_fence_wait_any_seq(rdev, seq, intr); + if (r) { + return r; + } + return 0; +} + +/** + * radeon_fence_wait_next_locked - wait for the next fence to signal + * + * @rdev: radeon device pointer + * @ring: ring index the fence is associated with + * + * Wait for the next fence on the requested ring to signal (all asics). + * Returns 0 if the next fence has passed, error for all other cases. + * Caller must hold ring lock. + */ +int radeon_fence_wait_next_locked(struct radeon_device *rdev, int ring) +{ + uint64_t seq; + + seq = atomic_load_acq_64(&rdev->fence_drv[ring].last_seq) + 1ULL; + if (seq >= rdev->fence_drv[ring].sync_seq[ring]) { + /* nothing to wait for, last_seq is + already the last emited fence */ + return -ENOENT; + } + return radeon_fence_wait_seq(rdev, seq, ring, false, false); +} + +/** + * radeon_fence_wait_empty_locked - wait for all fences to signal + * + * @rdev: radeon device pointer + * @ring: ring index the fence is associated with + * + * Wait for all fences on the requested ring to signal (all asics). + * Returns 0 if the fences have passed, error for all other cases. + * Caller must hold ring lock. + */ +int radeon_fence_wait_empty_locked(struct radeon_device *rdev, int ring) +{ + uint64_t seq = rdev->fence_drv[ring].sync_seq[ring]; + int r; + + r = radeon_fence_wait_seq(rdev, seq, ring, false, false); + if (r) { + if (r == -EDEADLK) { + return -EDEADLK; + } + dev_err(rdev->dev, "error waiting for ring[%d] to become idle (%d)\n", + ring, r); + } + return 0; +} + +/** + * radeon_fence_ref - take a ref on a fence + * + * @fence: radeon fence object + * + * Take a reference on a fence (all asics). + * Returns the fence. + */ +struct radeon_fence *radeon_fence_ref(struct radeon_fence *fence) +{ + refcount_acquire(&fence->kref); + return fence; +} + +/** + * radeon_fence_unref - remove a ref on a fence + * + * @fence: radeon fence object + * + * Remove a reference on a fence (all asics). + */ +void radeon_fence_unref(struct radeon_fence **fence) +{ + struct radeon_fence *tmp = *fence; + + *fence = NULL; + if (tmp) { + if (refcount_release(&tmp->kref)) { + radeon_fence_destroy(tmp); + } + } +} + +/** + * radeon_fence_count_emitted - get the count of emitted fences + * + * @rdev: radeon device pointer + * @ring: ring index the fence is associated with + * + * Get the number of fences emitted on the requested ring (all asics). + * Returns the number of emitted fences on the ring. Used by the + * dynpm code to ring track activity. + */ +unsigned radeon_fence_count_emitted(struct radeon_device *rdev, int ring) +{ + uint64_t emitted; + + /* We are not protected by ring lock when reading the last sequence + * but it's ok to report slightly wrong fence count here. + */ + radeon_fence_process(rdev, ring); + emitted = rdev->fence_drv[ring].sync_seq[ring] + - atomic_load_acq_64(&rdev->fence_drv[ring].last_seq); + /* to avoid 32bits warp around */ + if (emitted > 0x10000000) { + emitted = 0x10000000; + } + return (unsigned)emitted; +} + +/** + * radeon_fence_need_sync - do we need a semaphore + * + * @fence: radeon fence object + * @dst_ring: which ring to check against + * + * Check if the fence needs to be synced against another ring + * (all asics). If so, we need to emit a semaphore. + * Returns true if we need to sync with another ring, false if + * not. + */ +bool radeon_fence_need_sync(struct radeon_fence *fence, int dst_ring) +{ + struct radeon_fence_driver *fdrv; + + if (!fence) { + return false; + } + + if (fence->ring == dst_ring) { + return false; + } + + /* we are protected by the ring mutex */ + fdrv = &fence->rdev->fence_drv[dst_ring]; + if (fence->seq <= fdrv->sync_seq[fence->ring]) { + return false; + } + + return true; +} + +/** + * radeon_fence_note_sync - record the sync point + * + * @fence: radeon fence object + * @dst_ring: which ring to check against + * + * Note the sequence number at which point the fence will + * be synced with the requested ring (all asics). + */ +void radeon_fence_note_sync(struct radeon_fence *fence, int dst_ring) +{ + struct radeon_fence_driver *dst, *src; + unsigned i; + + if (!fence) { + return; + } + + if (fence->ring == dst_ring) { + return; + } + + /* we are protected by the ring mutex */ + src = &fence->rdev->fence_drv[fence->ring]; + dst = &fence->rdev->fence_drv[dst_ring]; + for (i = 0; i < RADEON_NUM_RINGS; ++i) { + if (i == dst_ring) { + continue; + } + dst->sync_seq[i] = max(dst->sync_seq[i], src->sync_seq[i]); + } +} + +/** + * radeon_fence_driver_start_ring - make the fence driver + * ready for use on the requested ring. + * + * @rdev: radeon device pointer + * @ring: ring index to start the fence driver on + * + * Make the fence driver ready for processing (all asics). + * Not all asics have all rings, so each asic will only + * start the fence driver on the rings it has. + * Returns 0 for success, errors for failure. + */ +int radeon_fence_driver_start_ring(struct radeon_device *rdev, int ring) +{ + uint64_t index; + int r; + + radeon_scratch_free(rdev, rdev->fence_drv[ring].scratch_reg); + if (rdev->wb.use_event || !radeon_ring_supports_scratch_reg(rdev, &rdev->ring[ring])) { + rdev->fence_drv[ring].scratch_reg = 0; + index = R600_WB_EVENT_OFFSET + ring * 4; + } else { + r = radeon_scratch_get(rdev, &rdev->fence_drv[ring].scratch_reg); + if (r) { + dev_err(rdev->dev, "fence failed to get scratch register\n"); + return r; + } + index = RADEON_WB_SCRATCH_OFFSET + + rdev->fence_drv[ring].scratch_reg - + rdev->scratch.reg_base; + } + rdev->fence_drv[ring].cpu_addr = &rdev->wb.wb[index/4]; + rdev->fence_drv[ring].gpu_addr = rdev->wb.gpu_addr + index; + radeon_fence_write(rdev, atomic_load_acq_64(&rdev->fence_drv[ring].last_seq), ring); + rdev->fence_drv[ring].initialized = true; + dev_info(rdev->dev, "fence driver on ring %d use gpu addr 0x%016jx and cpu addr 0x%p\n", + ring, (uintmax_t)rdev->fence_drv[ring].gpu_addr, rdev->fence_drv[ring].cpu_addr); + return 0; +} + +/** + * radeon_fence_driver_init_ring - init the fence driver + * for the requested ring. + * + * @rdev: radeon device pointer + * @ring: ring index to start the fence driver on + * + * Init the fence driver for the requested ring (all asics). + * Helper function for radeon_fence_driver_init(). + */ +static void radeon_fence_driver_init_ring(struct radeon_device *rdev, int ring) +{ + int i; + + rdev->fence_drv[ring].scratch_reg = -1; + rdev->fence_drv[ring].cpu_addr = NULL; + rdev->fence_drv[ring].gpu_addr = 0; + for (i = 0; i < RADEON_NUM_RINGS; ++i) + rdev->fence_drv[ring].sync_seq[i] = 0; + atomic_store_rel_64(&rdev->fence_drv[ring].last_seq, 0); + rdev->fence_drv[ring].last_activity = jiffies; + rdev->fence_drv[ring].initialized = false; +} + +/** + * radeon_fence_driver_init - init the fence driver + * for all possible rings. + * + * @rdev: radeon device pointer + * + * Init the fence driver for all possible rings (all asics). + * Not all asics have all rings, so each asic will only + * start the fence driver on the rings it has using + * radeon_fence_driver_start_ring(). + * Returns 0 for success. + */ +int radeon_fence_driver_init(struct radeon_device *rdev) +{ + int ring; + + mtx_init(&rdev->fence_queue_mtx, + "drm__radeon_device__fence_queue_mtx", NULL, MTX_DEF); + cv_init(&rdev->fence_queue, "drm__radeon_device__fence_queue"); + for (ring = 0; ring < RADEON_NUM_RINGS; ring++) { + radeon_fence_driver_init_ring(rdev, ring); + } + if (radeon_debugfs_fence_init(rdev)) { + dev_err(rdev->dev, "fence debugfs file creation failed\n"); + } + return 0; +} + +/** + * radeon_fence_driver_fini - tear down the fence driver + * for all possible rings. + * + * @rdev: radeon device pointer + * + * Tear down the fence driver for all possible rings (all asics). + */ +void radeon_fence_driver_fini(struct radeon_device *rdev) +{ + int ring, r; + + sx_xlock(&rdev->ring_lock); + for (ring = 0; ring < RADEON_NUM_RINGS; ring++) { + if (!rdev->fence_drv[ring].initialized) + continue; + r = radeon_fence_wait_empty_locked(rdev, ring); + if (r) { + /* no need to trigger GPU reset as we are unloading */ + radeon_fence_driver_force_completion(rdev); + } + cv_broadcast(&rdev->fence_queue); + radeon_scratch_free(rdev, rdev->fence_drv[ring].scratch_reg); + rdev->fence_drv[ring].initialized = false; + cv_destroy(&rdev->fence_queue); + } + sx_xunlock(&rdev->ring_lock); +} + +/** + * radeon_fence_driver_force_completion - force all fence waiter to complete + * + * @rdev: radeon device pointer + * + * In case of GPU reset failure make sure no process keep waiting on fence + * that will never complete. + */ +void radeon_fence_driver_force_completion(struct radeon_device *rdev) +{ + int ring; + + for (ring = 0; ring < RADEON_NUM_RINGS; ring++) { + if (!rdev->fence_drv[ring].initialized) + continue; + radeon_fence_write(rdev, rdev->fence_drv[ring].sync_seq[ring], ring); + } +} + + +/* + * Fence debugfs + */ +#if defined(CONFIG_DEBUG_FS) +static int radeon_debugfs_fence_info(struct seq_file *m, void *data) +{ + struct drm_info_node *node = (struct drm_info_node *)m->private; + struct drm_device *dev = node->minor->dev; + struct radeon_device *rdev = dev->dev_private; + int i, j; + + for (i = 0; i < RADEON_NUM_RINGS; ++i) { + if (!rdev->fence_drv[i].initialized) + continue; + + seq_printf(m, "--- ring %d ---\n", i); + seq_printf(m, "Last signaled fence 0x%016llx\n", + (unsigned long long)atomic_load_acq_64(&rdev->fence_drv[i].last_seq)); + seq_printf(m, "Last emitted 0x%016llx\n", + rdev->fence_drv[i].sync_seq[i]); + + for (j = 0; j < RADEON_NUM_RINGS; ++j) { + if (i != j && rdev->fence_drv[j].initialized) + seq_printf(m, "Last sync to ring %d 0x%016llx\n", + j, rdev->fence_drv[i].sync_seq[j]); + } + } + return 0; +} + +static struct drm_info_list radeon_debugfs_fence_list[] = { + {"radeon_fence_info", &radeon_debugfs_fence_info, 0, NULL}, +}; +#endif + +int radeon_debugfs_fence_init(struct radeon_device *rdev) +{ +#if defined(CONFIG_DEBUG_FS) + return radeon_debugfs_add_files(rdev, radeon_debugfs_fence_list, 1); +#else + return 0; +#endif +} diff --git a/sys/dev/drm2/radeon/radeon_gart.c b/sys/dev/drm2/radeon/radeon_gart.c new file mode 100644 index 00000000000..36b4452b14b --- /dev/null +++ b/sys/dev/drm2/radeon/radeon_gart.c @@ -0,0 +1,1304 @@ +/* + * Copyright 2008 Advanced Micro Devices, Inc. + * Copyright 2008 Red Hat Inc. + * Copyright 2009 Jerome Glisse. + * + * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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. + * + * Authors: Dave Airlie + * Alex Deucher + * Jerome Glisse + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include "radeon.h" +#include "radeon_reg.h" + +/* + * GART + * The GART (Graphics Aperture Remapping Table) is an aperture + * in the GPU's address space. System pages can be mapped into + * the aperture and look like contiguous pages from the GPU's + * perspective. A page table maps the pages in the aperture + * to the actual backing pages in system memory. + * + * Radeon GPUs support both an internal GART, as described above, + * and AGP. AGP works similarly, but the GART table is configured + * and maintained by the northbridge rather than the driver. + * Radeon hw has a separate AGP aperture that is programmed to + * point to the AGP aperture provided by the northbridge and the + * requests are passed through to the northbridge aperture. + * Both AGP and internal GART can be used at the same time, however + * that is not currently supported by the driver. + * + * This file handles the common internal GART management. + */ + +/* + * Common GART table functions. + */ +/** + * radeon_gart_table_ram_alloc - allocate system ram for gart page table + * + * @rdev: radeon_device pointer + * + * Allocate system memory for GART page table + * (r1xx-r3xx, non-pcie r4xx, rs400). These asics require the + * gart table to be in system memory. + * Returns 0 for success, -ENOMEM for failure. + */ +int radeon_gart_table_ram_alloc(struct radeon_device *rdev) +{ + drm_dma_handle_t *dmah; + + dmah = drm_pci_alloc(rdev->ddev, rdev->gart.table_size, + PAGE_SIZE, 0xFFFFFFFFUL); + if (dmah == NULL) { + return -ENOMEM; + } + rdev->gart.dmah = dmah; + rdev->gart.ptr = dmah->vaddr; +#if defined(__i386) || defined(__amd64) + if (rdev->family == CHIP_RS400 || rdev->family == CHIP_RS480 || + rdev->family == CHIP_RS690 || rdev->family == CHIP_RS740) { + pmap_change_attr((vm_offset_t)rdev->gart.ptr, + rdev->gart.table_size >> PAGE_SHIFT, PAT_UNCACHED); + } +#endif + rdev->gart.table_addr = dmah->busaddr; + memset((void *)rdev->gart.ptr, 0, rdev->gart.table_size); + return 0; +} + +/** + * radeon_gart_table_ram_free - free system ram for gart page table + * + * @rdev: radeon_device pointer + * + * Free system memory for GART page table + * (r1xx-r3xx, non-pcie r4xx, rs400). These asics require the + * gart table to be in system memory. + */ +void radeon_gart_table_ram_free(struct radeon_device *rdev) +{ + if (rdev->gart.ptr == NULL) { + return; + } +#if defined(__i386) || defined(__amd64) + if (rdev->family == CHIP_RS400 || rdev->family == CHIP_RS480 || + rdev->family == CHIP_RS690 || rdev->family == CHIP_RS740) { + pmap_change_attr((vm_offset_t)rdev->gart.ptr, + rdev->gart.table_size >> PAGE_SHIFT, PAT_WRITE_COMBINING); + } +#endif + drm_pci_free(rdev->ddev, rdev->gart.dmah); + rdev->gart.dmah = NULL; + rdev->gart.ptr = NULL; + rdev->gart.table_addr = 0; +} + +/** + * radeon_gart_table_vram_alloc - allocate vram for gart page table + * + * @rdev: radeon_device pointer + * + * Allocate video memory for GART page table + * (pcie r4xx, r5xx+). These asics require the + * gart table to be in video memory. + * Returns 0 for success, error for failure. + */ +int radeon_gart_table_vram_alloc(struct radeon_device *rdev) +{ + int r; + + if (rdev->gart.robj == NULL) { + r = radeon_bo_create(rdev, rdev->gart.table_size, + PAGE_SIZE, true, RADEON_GEM_DOMAIN_VRAM, + NULL, &rdev->gart.robj); + if (r) { + return r; + } + } + return 0; +} + +/** + * radeon_gart_table_vram_pin - pin gart page table in vram + * + * @rdev: radeon_device pointer + * + * Pin the GART page table in vram so it will not be moved + * by the memory manager (pcie r4xx, r5xx+). These asics require the + * gart table to be in video memory. + * Returns 0 for success, error for failure. + */ +int radeon_gart_table_vram_pin(struct radeon_device *rdev) +{ + uint64_t gpu_addr; + int r; + + r = radeon_bo_reserve(rdev->gart.robj, false); + if (unlikely(r != 0)) + return r; + r = radeon_bo_pin(rdev->gart.robj, + RADEON_GEM_DOMAIN_VRAM, &gpu_addr); + if (r) { + radeon_bo_unreserve(rdev->gart.robj); + return r; + } + r = radeon_bo_kmap(rdev->gart.robj, &rdev->gart.ptr); + if (r) + radeon_bo_unpin(rdev->gart.robj); + radeon_bo_unreserve(rdev->gart.robj); + rdev->gart.table_addr = gpu_addr; + return r; +} + +/** + * radeon_gart_table_vram_unpin - unpin gart page table in vram + * + * @rdev: radeon_device pointer + * + * Unpin the GART page table in vram (pcie r4xx, r5xx+). + * These asics require the gart table to be in video memory. + */ +void radeon_gart_table_vram_unpin(struct radeon_device *rdev) +{ + int r; + + if (rdev->gart.robj == NULL) { + return; + } + r = radeon_bo_reserve(rdev->gart.robj, false); + if (likely(r == 0)) { + radeon_bo_kunmap(rdev->gart.robj); + radeon_bo_unpin(rdev->gart.robj); + radeon_bo_unreserve(rdev->gart.robj); + rdev->gart.ptr = NULL; + } +} + +/** + * radeon_gart_table_vram_free - free gart page table vram + * + * @rdev: radeon_device pointer + * + * Free the video memory used for the GART page table + * (pcie r4xx, r5xx+). These asics require the gart table to + * be in video memory. + */ +void radeon_gart_table_vram_free(struct radeon_device *rdev) +{ + if (rdev->gart.robj == NULL) { + return; + } + radeon_gart_table_vram_unpin(rdev); + radeon_bo_unref(&rdev->gart.robj); +} + +/* + * Common gart functions. + */ +/** + * radeon_gart_unbind - unbind pages from the gart page table + * + * @rdev: radeon_device pointer + * @offset: offset into the GPU's gart aperture + * @pages: number of pages to unbind + * + * Unbinds the requested pages from the gart page table and + * replaces them with the dummy page (all asics). + */ +void radeon_gart_unbind(struct radeon_device *rdev, unsigned offset, + int pages) +{ + unsigned t; + unsigned p; + int i, j; + u64 page_base; + + if (!rdev->gart.ready) { + DRM_ERROR("trying to unbind memory from uninitialized GART !\n"); + return; + } + t = offset / RADEON_GPU_PAGE_SIZE; + p = t / (PAGE_SIZE / RADEON_GPU_PAGE_SIZE); + for (i = 0; i < pages; i++, p++) { + if (rdev->gart.pages[p]) { + rdev->gart.pages[p] = NULL; + rdev->gart.pages_addr[p] = rdev->dummy_page.addr; + page_base = rdev->gart.pages_addr[p]; + for (j = 0; j < (PAGE_SIZE / RADEON_GPU_PAGE_SIZE); j++, t++) { + if (rdev->gart.ptr) { + radeon_gart_set_page(rdev, t, page_base); + } + page_base += RADEON_GPU_PAGE_SIZE; + } + } + } + mb(); + radeon_gart_tlb_flush(rdev); +} + +/** + * radeon_gart_bind - bind pages into the gart page table + * + * @rdev: radeon_device pointer + * @offset: offset into the GPU's gart aperture + * @pages: number of pages to bind + * @pagelist: pages to bind + * @dma_addr: DMA addresses of pages + * + * Binds the requested pages to the gart page table + * (all asics). + * Returns 0 for success, -EINVAL for failure. + */ +int radeon_gart_bind(struct radeon_device *rdev, unsigned offset, + int pages, vm_page_t *pagelist, dma_addr_t *dma_addr) +{ + unsigned t; + unsigned p; + uint64_t page_base; + int i, j; + + if (!rdev->gart.ready) { + DRM_ERROR("trying to bind memory to uninitialized GART !\n"); + return -EINVAL; + } + t = offset / RADEON_GPU_PAGE_SIZE; + p = t / (PAGE_SIZE / RADEON_GPU_PAGE_SIZE); + + for (i = 0; i < pages; i++, p++) { + rdev->gart.pages_addr[p] = dma_addr[i]; + rdev->gart.pages[p] = pagelist[i]; + if (rdev->gart.ptr) { + page_base = rdev->gart.pages_addr[p]; + for (j = 0; j < (PAGE_SIZE / RADEON_GPU_PAGE_SIZE); j++, t++) { + radeon_gart_set_page(rdev, t, page_base); + page_base += RADEON_GPU_PAGE_SIZE; + } + } + } + mb(); + radeon_gart_tlb_flush(rdev); + return 0; +} + +/** + * radeon_gart_restore - bind all pages in the gart page table + * + * @rdev: radeon_device pointer + * + * Binds all pages in the gart page table (all asics). + * Used to rebuild the gart table on device startup or resume. + */ +void radeon_gart_restore(struct radeon_device *rdev) +{ + int i, j, t; + u64 page_base; + + if (!rdev->gart.ptr) { + return; + } + for (i = 0, t = 0; i < rdev->gart.num_cpu_pages; i++) { + page_base = rdev->gart.pages_addr[i]; + for (j = 0; j < (PAGE_SIZE / RADEON_GPU_PAGE_SIZE); j++, t++) { + radeon_gart_set_page(rdev, t, page_base); + page_base += RADEON_GPU_PAGE_SIZE; + } + } + mb(); + radeon_gart_tlb_flush(rdev); +} + +/** + * radeon_gart_init - init the driver info for managing the gart + * + * @rdev: radeon_device pointer + * + * Allocate the dummy page and init the gart driver info (all asics). + * Returns 0 for success, error for failure. + */ +int radeon_gart_init(struct radeon_device *rdev) +{ + int r, i; + + if (rdev->gart.pages) { + return 0; + } + /* We need PAGE_SIZE >= RADEON_GPU_PAGE_SIZE */ + if (PAGE_SIZE < RADEON_GPU_PAGE_SIZE) { + DRM_ERROR("Page size is smaller than GPU page size!\n"); + return -EINVAL; + } + r = radeon_dummy_page_init(rdev); + if (r) + return r; + /* Compute table size */ + rdev->gart.num_cpu_pages = rdev->mc.gtt_size / PAGE_SIZE; + rdev->gart.num_gpu_pages = rdev->mc.gtt_size / RADEON_GPU_PAGE_SIZE; + DRM_INFO("GART: num cpu pages %u, num gpu pages %u\n", + rdev->gart.num_cpu_pages, rdev->gart.num_gpu_pages); + /* Allocate pages table */ + rdev->gart.pages = malloc(sizeof(void *) * rdev->gart.num_cpu_pages, + DRM_MEM_DRIVER, M_ZERO | M_WAITOK); + if (rdev->gart.pages == NULL) { + radeon_gart_fini(rdev); + return -ENOMEM; + } + rdev->gart.pages_addr = malloc(sizeof(dma_addr_t) * + rdev->gart.num_cpu_pages, + DRM_MEM_DRIVER, M_ZERO | M_WAITOK); + if (rdev->gart.pages_addr == NULL) { + radeon_gart_fini(rdev); + return -ENOMEM; + } + /* set GART entry to point to the dummy page by default */ + for (i = 0; i < rdev->gart.num_cpu_pages; i++) { + rdev->gart.pages_addr[i] = rdev->dummy_page.addr; + } + return 0; +} + +/** + * radeon_gart_fini - tear down the driver info for managing the gart + * + * @rdev: radeon_device pointer + * + * Tear down the gart driver info and free the dummy page (all asics). + */ +void radeon_gart_fini(struct radeon_device *rdev) +{ + if (rdev->gart.pages && rdev->gart.pages_addr && rdev->gart.ready) { + /* unbind pages */ + radeon_gart_unbind(rdev, 0, rdev->gart.num_cpu_pages); + } + rdev->gart.ready = false; + free(rdev->gart.pages, DRM_MEM_DRIVER); + free(rdev->gart.pages_addr, DRM_MEM_DRIVER); + rdev->gart.pages = NULL; + rdev->gart.pages_addr = NULL; + + radeon_dummy_page_fini(rdev); +} + +/* + * GPUVM + * GPUVM is similar to the legacy gart on older asics, however + * rather than there being a single global gart table + * for the entire GPU, there are multiple VM page tables active + * at any given time. The VM page tables can contain a mix + * vram pages and system memory pages and system memory pages + * can be mapped as snooped (cached system pages) or unsnooped + * (uncached system pages). + * Each VM has an ID associated with it and there is a page table + * associated with each VMID. When execting a command buffer, + * the kernel tells the the ring what VMID to use for that command + * buffer. VMIDs are allocated dynamically as commands are submitted. + * The userspace drivers maintain their own address space and the kernel + * sets up their pages tables accordingly when they submit their + * command buffers and a VMID is assigned. + * Cayman/Trinity support up to 8 active VMs at any given time; + * SI supports 16. + */ + +/* + * vm helpers + * + * TODO bind a default page at vm initialization for default address + */ + +/** + * radeon_vm_num_pde - return the number of page directory entries + * + * @rdev: radeon_device pointer + * + * Calculate the number of page directory entries (cayman+). + */ +static unsigned radeon_vm_num_pdes(struct radeon_device *rdev) +{ + return rdev->vm_manager.max_pfn >> RADEON_VM_BLOCK_SIZE; +} + +/** + * radeon_vm_directory_size - returns the size of the page directory in bytes + * + * @rdev: radeon_device pointer + * + * Calculate the size of the page directory in bytes (cayman+). + */ +static unsigned radeon_vm_directory_size(struct radeon_device *rdev) +{ + return RADEON_GPU_PAGE_ALIGN(radeon_vm_num_pdes(rdev) * 8); +} + +/** + * radeon_vm_manager_init - init the vm manager + * + * @rdev: radeon_device pointer + * + * Init the vm manager (cayman+). + * Returns 0 for success, error for failure. + */ +int radeon_vm_manager_init(struct radeon_device *rdev) +{ + struct radeon_vm *vm; + struct radeon_bo_va *bo_va; + int r; + unsigned size; + + if (!rdev->vm_manager.enabled) { + /* allocate enough for 2 full VM pts */ + size = radeon_vm_directory_size(rdev); + size += rdev->vm_manager.max_pfn * 8; + size *= 2; + r = radeon_sa_bo_manager_init(rdev, &rdev->vm_manager.sa_manager, + RADEON_GPU_PAGE_ALIGN(size), + RADEON_GEM_DOMAIN_VRAM); + if (r) { + dev_err(rdev->dev, "failed to allocate vm bo (%dKB)\n", + (rdev->vm_manager.max_pfn * 8) >> 10); + return r; + } + + r = radeon_asic_vm_init(rdev); + if (r) + return r; + + rdev->vm_manager.enabled = true; + + r = radeon_sa_bo_manager_start(rdev, &rdev->vm_manager.sa_manager); + if (r) + return r; + } + + /* restore page table */ + list_for_each_entry(vm, &rdev->vm_manager.lru_vm, list) { + if (vm->page_directory == NULL) + continue; + + list_for_each_entry(bo_va, &vm->va, vm_list) { + bo_va->valid = false; + } + } + return 0; +} + +/** + * radeon_vm_free_pt - free the page table for a specific vm + * + * @rdev: radeon_device pointer + * @vm: vm to unbind + * + * Free the page table of a specific vm (cayman+). + * + * Global and local mutex must be lock! + */ +static void radeon_vm_free_pt(struct radeon_device *rdev, + struct radeon_vm *vm) +{ + struct radeon_bo_va *bo_va; + int i; + + if (!vm->page_directory) + return; + + list_del_init(&vm->list); + radeon_sa_bo_free(rdev, &vm->page_directory, vm->fence); + + list_for_each_entry(bo_va, &vm->va, vm_list) { + bo_va->valid = false; + } + + if (vm->page_tables == NULL) + return; + + for (i = 0; i < radeon_vm_num_pdes(rdev); i++) + radeon_sa_bo_free(rdev, &vm->page_tables[i], vm->fence); + + free(vm->page_tables, DRM_MEM_DRIVER); +} + +/** + * radeon_vm_manager_fini - tear down the vm manager + * + * @rdev: radeon_device pointer + * + * Tear down the VM manager (cayman+). + */ +void radeon_vm_manager_fini(struct radeon_device *rdev) +{ + struct radeon_vm *vm, *tmp; + int i; + + if (!rdev->vm_manager.enabled) + return; + + sx_xlock(&rdev->vm_manager.lock); + /* free all allocated page tables */ + list_for_each_entry_safe(vm, tmp, &rdev->vm_manager.lru_vm, list) { + sx_xlock(&vm->mutex); + radeon_vm_free_pt(rdev, vm); + sx_xunlock(&vm->mutex); + } + for (i = 0; i < RADEON_NUM_VM; ++i) { + radeon_fence_unref(&rdev->vm_manager.active[i]); + } + radeon_asic_vm_fini(rdev); + sx_xunlock(&rdev->vm_manager.lock); + + radeon_sa_bo_manager_suspend(rdev, &rdev->vm_manager.sa_manager); + radeon_sa_bo_manager_fini(rdev, &rdev->vm_manager.sa_manager); + rdev->vm_manager.enabled = false; +} + +/** + * radeon_vm_evict - evict page table to make room for new one + * + * @rdev: radeon_device pointer + * @vm: VM we want to allocate something for + * + * Evict a VM from the lru, making sure that it isn't @vm. (cayman+). + * Returns 0 for success, -ENOMEM for failure. + * + * Global and local mutex must be locked! + */ +static int radeon_vm_evict(struct radeon_device *rdev, struct radeon_vm *vm) +{ + struct radeon_vm *vm_evict; + + if (list_empty(&rdev->vm_manager.lru_vm)) + return -ENOMEM; + + vm_evict = list_first_entry(&rdev->vm_manager.lru_vm, + struct radeon_vm, list); + if (vm_evict == vm) + return -ENOMEM; + + sx_xlock(&vm_evict->mutex); + radeon_vm_free_pt(rdev, vm_evict); + sx_xunlock(&vm_evict->mutex); + return 0; +} + +/** + * radeon_vm_alloc_pt - allocates a page table for a VM + * + * @rdev: radeon_device pointer + * @vm: vm to bind + * + * Allocate a page table for the requested vm (cayman+). + * Returns 0 for success, error for failure. + * + * Global and local mutex must be locked! + */ +int radeon_vm_alloc_pt(struct radeon_device *rdev, struct radeon_vm *vm) +{ + unsigned pd_size, pts_size; + u64 *pd_addr; + int r; + + if (vm == NULL) { + return -EINVAL; + } + + if (vm->page_directory != NULL) { + return 0; + } + +retry: + pd_size = RADEON_GPU_PAGE_ALIGN(radeon_vm_directory_size(rdev)); + r = radeon_sa_bo_new(rdev, &rdev->vm_manager.sa_manager, + &vm->page_directory, pd_size, + RADEON_GPU_PAGE_SIZE, false); + if (r == -ENOMEM) { + r = radeon_vm_evict(rdev, vm); + if (r) + return r; + goto retry; + + } else if (r) { + return r; + } + + vm->pd_gpu_addr = radeon_sa_bo_gpu_addr(vm->page_directory); + + /* Initially clear the page directory */ + pd_addr = radeon_sa_bo_cpu_addr(vm->page_directory); + memset(pd_addr, 0, pd_size); + + pts_size = radeon_vm_num_pdes(rdev) * sizeof(struct radeon_sa_bo *); + vm->page_tables = malloc(pts_size, DRM_MEM_DRIVER, M_ZERO | M_WAITOK); + + if (vm->page_tables == NULL) { + DRM_ERROR("Cannot allocate memory for page table array\n"); + radeon_sa_bo_free(rdev, &vm->page_directory, vm->fence); + return -ENOMEM; + } + + return 0; +} + +/** + * radeon_vm_add_to_lru - add VMs page table to LRU list + * + * @rdev: radeon_device pointer + * @vm: vm to add to LRU + * + * Add the allocated page table to the LRU list (cayman+). + * + * Global mutex must be locked! + */ +void radeon_vm_add_to_lru(struct radeon_device *rdev, struct radeon_vm *vm) +{ + list_del_init(&vm->list); + list_add_tail(&vm->list, &rdev->vm_manager.lru_vm); +} + +/** + * radeon_vm_grab_id - allocate the next free VMID + * + * @rdev: radeon_device pointer + * @vm: vm to allocate id for + * @ring: ring we want to submit job to + * + * Allocate an id for the vm (cayman+). + * Returns the fence we need to sync to (if any). + * + * Global and local mutex must be locked! + */ +struct radeon_fence *radeon_vm_grab_id(struct radeon_device *rdev, + struct radeon_vm *vm, int ring) +{ + struct radeon_fence *best[RADEON_NUM_RINGS] = {}; + unsigned choices[2] = {}; + unsigned i; + + /* check if the id is still valid */ + if (vm->fence && vm->fence == rdev->vm_manager.active[vm->id]) + return NULL; + + /* we definately need to flush */ + radeon_fence_unref(&vm->last_flush); + + /* skip over VMID 0, since it is the system VM */ + for (i = 1; i < rdev->vm_manager.nvm; ++i) { + struct radeon_fence *fence = rdev->vm_manager.active[i]; + + if (fence == NULL) { + /* found a free one */ + vm->id = i; + return NULL; + } + + if (radeon_fence_is_earlier(fence, best[fence->ring])) { + best[fence->ring] = fence; + choices[fence->ring == ring ? 0 : 1] = i; + } + } + + for (i = 0; i < 2; ++i) { + if (choices[i]) { + vm->id = choices[i]; + return rdev->vm_manager.active[choices[i]]; + } + } + + /* should never happen */ + panic("%s: failed to allocate next VMID", __func__); + return NULL; +} + +/** + * radeon_vm_fence - remember fence for vm + * + * @rdev: radeon_device pointer + * @vm: vm we want to fence + * @fence: fence to remember + * + * Fence the vm (cayman+). + * Set the fence used to protect page table and id. + * + * Global and local mutex must be locked! + */ +void radeon_vm_fence(struct radeon_device *rdev, + struct radeon_vm *vm, + struct radeon_fence *fence) +{ + radeon_fence_unref(&rdev->vm_manager.active[vm->id]); + rdev->vm_manager.active[vm->id] = radeon_fence_ref(fence); + + radeon_fence_unref(&vm->fence); + vm->fence = radeon_fence_ref(fence); +} + +/** + * radeon_vm_bo_find - find the bo_va for a specific vm & bo + * + * @vm: requested vm + * @bo: requested buffer object + * + * Find @bo inside the requested vm (cayman+). + * Search inside the @bos vm list for the requested vm + * Returns the found bo_va or NULL if none is found + * + * Object has to be reserved! + */ +struct radeon_bo_va *radeon_vm_bo_find(struct radeon_vm *vm, + struct radeon_bo *bo) +{ + struct radeon_bo_va *bo_va; + + list_for_each_entry(bo_va, &bo->va, bo_list) { + if (bo_va->vm == vm) { + return bo_va; + } + } + return NULL; +} + +/** + * radeon_vm_bo_add - add a bo to a specific vm + * + * @rdev: radeon_device pointer + * @vm: requested vm + * @bo: radeon buffer object + * + * Add @bo into the requested vm (cayman+). + * Add @bo to the list of bos associated with the vm + * Returns newly added bo_va or NULL for failure + * + * Object has to be reserved! + */ +struct radeon_bo_va *radeon_vm_bo_add(struct radeon_device *rdev, + struct radeon_vm *vm, + struct radeon_bo *bo) +{ + struct radeon_bo_va *bo_va; + + bo_va = malloc(sizeof(struct radeon_bo_va), + DRM_MEM_DRIVER, M_ZERO | M_WAITOK); + if (bo_va == NULL) { + return NULL; + } + bo_va->vm = vm; + bo_va->bo = bo; + bo_va->soffset = 0; + bo_va->eoffset = 0; + bo_va->flags = 0; + bo_va->valid = false; + bo_va->ref_count = 1; + INIT_LIST_HEAD(&bo_va->bo_list); + INIT_LIST_HEAD(&bo_va->vm_list); + + sx_xlock(&vm->mutex); + list_add(&bo_va->vm_list, &vm->va); + list_add_tail(&bo_va->bo_list, &bo->va); + sx_xunlock(&vm->mutex); + + return bo_va; +} + +/** + * radeon_vm_bo_set_addr - set bos virtual address inside a vm + * + * @rdev: radeon_device pointer + * @bo_va: bo_va to store the address + * @soffset: requested offset of the buffer in the VM address space + * @flags: attributes of pages (read/write/valid/etc.) + * + * Set offset of @bo_va (cayman+). + * Validate and set the offset requested within the vm address space. + * Returns 0 for success, error for failure. + * + * Object has to be reserved! + */ +int radeon_vm_bo_set_addr(struct radeon_device *rdev, + struct radeon_bo_va *bo_va, + uint64_t soffset, + uint32_t flags) +{ + uint64_t size = radeon_bo_size(bo_va->bo); + uint64_t eoffset, last_offset = 0; + struct radeon_vm *vm = bo_va->vm; + struct radeon_bo_va *tmp; + struct list_head *head; + unsigned last_pfn; + + if (soffset) { + /* make sure object fit at this offset */ + eoffset = soffset + size; + if (soffset >= eoffset) { + return -EINVAL; + } + + last_pfn = eoffset / RADEON_GPU_PAGE_SIZE; + if (last_pfn > rdev->vm_manager.max_pfn) { + dev_err(rdev->dev, "va above limit (0x%08X > 0x%08X)\n", + last_pfn, rdev->vm_manager.max_pfn); + return -EINVAL; + } + + } else { + eoffset = last_pfn = 0; + } + + sx_xlock(&vm->mutex); + head = &vm->va; + last_offset = 0; + list_for_each_entry(tmp, &vm->va, vm_list) { + if (bo_va == tmp) { + /* skip over currently modified bo */ + continue; + } + + if (soffset >= last_offset && eoffset <= tmp->soffset) { + /* bo can be added before this one */ + break; + } + if (eoffset > tmp->soffset && soffset < tmp->eoffset) { + /* bo and tmp overlap, invalid offset */ + dev_err(rdev->dev, "bo %p va 0x%08X conflict with (bo %p 0x%08X 0x%08X)\n", + bo_va->bo, (unsigned)bo_va->soffset, tmp->bo, + (unsigned)tmp->soffset, (unsigned)tmp->eoffset); + sx_xunlock(&vm->mutex); + return -EINVAL; + } + last_offset = tmp->eoffset; + head = &tmp->vm_list; + } + + bo_va->soffset = soffset; + bo_va->eoffset = eoffset; + bo_va->flags = flags; + bo_va->valid = false; + list_move(&bo_va->vm_list, head); + + sx_xunlock(&vm->mutex); + return 0; +} + +/** + * radeon_vm_map_gart - get the physical address of a gart page + * + * @rdev: radeon_device pointer + * @addr: the unmapped addr + * + * Look up the physical address of the page that the pte resolves + * to (cayman+). + * Returns the physical address of the page. + */ +uint64_t radeon_vm_map_gart(struct radeon_device *rdev, uint64_t addr) +{ + uint64_t result; + + /* page table offset */ + result = rdev->gart.pages_addr[addr >> PAGE_SHIFT]; + + /* in case cpu page size != gpu page size*/ + result |= addr & (~PAGE_MASK); + + return result; +} + +/** + * radeon_vm_update_pdes - make sure that page directory is valid + * + * @rdev: radeon_device pointer + * @vm: requested vm + * @start: start of GPU address range + * @end: end of GPU address range + * + * Allocates new page tables if necessary + * and updates the page directory (cayman+). + * Returns 0 for success, error for failure. + * + * Global and local mutex must be locked! + */ +static int radeon_vm_update_pdes(struct radeon_device *rdev, + struct radeon_vm *vm, + uint64_t start, uint64_t end) +{ + static const uint32_t incr = RADEON_VM_PTE_COUNT * 8; + + uint64_t last_pde = ~0, last_pt = ~0; + unsigned count = 0; + uint64_t pt_idx; + int r; + + start = (start / RADEON_GPU_PAGE_SIZE) >> RADEON_VM_BLOCK_SIZE; + end = (end / RADEON_GPU_PAGE_SIZE) >> RADEON_VM_BLOCK_SIZE; + + /* walk over the address space and update the page directory */ + for (pt_idx = start; pt_idx <= end; ++pt_idx) { + uint64_t pde, pt; + + if (vm->page_tables[pt_idx]) + continue; + +retry: + r = radeon_sa_bo_new(rdev, &rdev->vm_manager.sa_manager, + &vm->page_tables[pt_idx], + RADEON_VM_PTE_COUNT * 8, + RADEON_GPU_PAGE_SIZE, false); + + if (r == -ENOMEM) { + r = radeon_vm_evict(rdev, vm); + if (r) + return r; + goto retry; + } else if (r) { + return r; + } + + pde = vm->pd_gpu_addr + pt_idx * 8; + + pt = radeon_sa_bo_gpu_addr(vm->page_tables[pt_idx]); + + if (((last_pde + 8 * count) != pde) || + ((last_pt + incr * count) != pt)) { + + if (count) { + radeon_asic_vm_set_page(rdev, last_pde, + last_pt, count, incr, + RADEON_VM_PAGE_VALID); + } + + count = 1; + last_pde = pde; + last_pt = pt; + } else { + ++count; + } + } + + if (count) { + radeon_asic_vm_set_page(rdev, last_pde, last_pt, count, + incr, RADEON_VM_PAGE_VALID); + + } + + return 0; +} + +/** + * radeon_vm_update_ptes - make sure that page tables are valid + * + * @rdev: radeon_device pointer + * @vm: requested vm + * @start: start of GPU address range + * @end: end of GPU address range + * @dst: destination address to map to + * @flags: mapping flags + * + * Update the page tables in the range @start - @end (cayman+). + * + * Global and local mutex must be locked! + */ +static void radeon_vm_update_ptes(struct radeon_device *rdev, + struct radeon_vm *vm, + uint64_t start, uint64_t end, + uint64_t dst, uint32_t flags) +{ + static const uint64_t mask = RADEON_VM_PTE_COUNT - 1; + + uint64_t last_pte = ~0, last_dst = ~0; + unsigned count = 0; + uint64_t addr; + + start = start / RADEON_GPU_PAGE_SIZE; + end = end / RADEON_GPU_PAGE_SIZE; + + /* walk over the address space and update the page tables */ + for (addr = start; addr < end; ) { + uint64_t pt_idx = addr >> RADEON_VM_BLOCK_SIZE; + unsigned nptes; + uint64_t pte; + + if ((addr & ~mask) == (end & ~mask)) + nptes = end - addr; + else + nptes = RADEON_VM_PTE_COUNT - (addr & mask); + + pte = radeon_sa_bo_gpu_addr(vm->page_tables[pt_idx]); + pte += (addr & mask) * 8; + + if ((last_pte + 8 * count) != pte) { + + if (count) { + radeon_asic_vm_set_page(rdev, last_pte, + last_dst, count, + RADEON_GPU_PAGE_SIZE, + flags); + } + + count = nptes; + last_pte = pte; + last_dst = dst; + } else { + count += nptes; + } + + addr += nptes; + dst += nptes * RADEON_GPU_PAGE_SIZE; + } + + if (count) { + radeon_asic_vm_set_page(rdev, last_pte, last_dst, count, + RADEON_GPU_PAGE_SIZE, flags); + } +} + +/** + * radeon_vm_bo_update_pte - map a bo into the vm page table + * + * @rdev: radeon_device pointer + * @vm: requested vm + * @bo: radeon buffer object + * @mem: ttm mem + * + * Fill in the page table entries for @bo (cayman+). + * Returns 0 for success, -EINVAL for failure. + * + * Object have to be reserved & global and local mutex must be locked! + */ +int radeon_vm_bo_update_pte(struct radeon_device *rdev, + struct radeon_vm *vm, + struct radeon_bo *bo, + struct ttm_mem_reg *mem) +{ + unsigned ridx = rdev->asic->vm.pt_ring_index; + struct radeon_ring *ring = &rdev->ring[ridx]; + struct radeon_semaphore *sem = NULL; + struct radeon_bo_va *bo_va; + unsigned nptes, npdes, ndw; + uint64_t addr; + int r; + + /* nothing to do if vm isn't bound */ + if (vm->page_directory == NULL) + return 0; + + bo_va = radeon_vm_bo_find(vm, bo); + if (bo_va == NULL) { + dev_err(rdev->dev, "bo %p not in vm %p\n", bo, vm); + return -EINVAL; + } + + if (!bo_va->soffset) { + dev_err(rdev->dev, "bo %p don't has a mapping in vm %p\n", + bo, vm); + return -EINVAL; + } + + if ((bo_va->valid && mem) || (!bo_va->valid && mem == NULL)) + return 0; + + bo_va->flags &= ~RADEON_VM_PAGE_VALID; + bo_va->flags &= ~RADEON_VM_PAGE_SYSTEM; + if (mem) { + addr = mem->start << PAGE_SHIFT; + if (mem->mem_type != TTM_PL_SYSTEM) { + bo_va->flags |= RADEON_VM_PAGE_VALID; + bo_va->valid = true; + } + if (mem->mem_type == TTM_PL_TT) { + bo_va->flags |= RADEON_VM_PAGE_SYSTEM; + } else { + addr += rdev->vm_manager.vram_base_offset; + } + } else { + addr = 0; + bo_va->valid = false; + } + + if (vm->fence && radeon_fence_signaled(vm->fence)) { + radeon_fence_unref(&vm->fence); + } + + if (vm->fence && vm->fence->ring != ridx) { + r = radeon_semaphore_create(rdev, &sem); + if (r) { + return r; + } + } + + nptes = radeon_bo_ngpu_pages(bo); + + /* assume two extra pdes in case the mapping overlaps the borders */ + npdes = (nptes >> RADEON_VM_BLOCK_SIZE) + 2; + + /* estimate number of dw needed */ + /* semaphore, fence and padding */ + ndw = 32; + + if (RADEON_VM_BLOCK_SIZE > 11) + /* reserve space for one header for every 2k dwords */ + ndw += (nptes >> 11) * 4; + else + /* reserve space for one header for + every (1 << BLOCK_SIZE) entries */ + ndw += (nptes >> RADEON_VM_BLOCK_SIZE) * 4; + + /* reserve space for pte addresses */ + ndw += nptes * 2; + + /* reserve space for one header for every 2k dwords */ + ndw += (npdes >> 11) * 4; + + /* reserve space for pde addresses */ + ndw += npdes * 2; + + r = radeon_ring_lock(rdev, ring, ndw); + if (r) { + return r; + } + + if (sem && radeon_fence_need_sync(vm->fence, ridx)) { + radeon_semaphore_sync_rings(rdev, sem, vm->fence->ring, ridx); + radeon_fence_note_sync(vm->fence, ridx); + } + + r = radeon_vm_update_pdes(rdev, vm, bo_va->soffset, bo_va->eoffset); + if (r) { + radeon_ring_unlock_undo(rdev, ring); + return r; + } + + radeon_vm_update_ptes(rdev, vm, bo_va->soffset, bo_va->eoffset, + addr, bo_va->flags); + + radeon_fence_unref(&vm->fence); + r = radeon_fence_emit(rdev, &vm->fence, ridx); + if (r) { + radeon_ring_unlock_undo(rdev, ring); + return r; + } + radeon_ring_unlock_commit(rdev, ring); + radeon_semaphore_free(rdev, &sem, vm->fence); + radeon_fence_unref(&vm->last_flush); + + return 0; +} + +/** + * radeon_vm_bo_rmv - remove a bo to a specific vm + * + * @rdev: radeon_device pointer + * @bo_va: requested bo_va + * + * Remove @bo_va->bo from the requested vm (cayman+). + * Remove @bo_va->bo from the list of bos associated with the bo_va->vm and + * remove the ptes for @bo_va in the page table. + * Returns 0 for success. + * + * Object have to be reserved! + */ +int radeon_vm_bo_rmv(struct radeon_device *rdev, + struct radeon_bo_va *bo_va) +{ + int r; + + sx_xlock(&rdev->vm_manager.lock); + sx_xlock(&bo_va->vm->mutex); + r = radeon_vm_bo_update_pte(rdev, bo_va->vm, bo_va->bo, NULL); + sx_xunlock(&rdev->vm_manager.lock); + list_del(&bo_va->vm_list); + sx_xunlock(&bo_va->vm->mutex); + list_del(&bo_va->bo_list); + + free(bo_va, DRM_MEM_DRIVER); + return r; +} + +/** + * radeon_vm_bo_invalidate - mark the bo as invalid + * + * @rdev: radeon_device pointer + * @vm: requested vm + * @bo: radeon buffer object + * + * Mark @bo as invalid (cayman+). + */ +void radeon_vm_bo_invalidate(struct radeon_device *rdev, + struct radeon_bo *bo) +{ + struct radeon_bo_va *bo_va; + + list_for_each_entry(bo_va, &bo->va, bo_list) { + bo_va->valid = false; + } +} + +/** + * radeon_vm_init - initialize a vm instance + * + * @rdev: radeon_device pointer + * @vm: requested vm + * + * Init @vm fields (cayman+). + */ +void radeon_vm_init(struct radeon_device *rdev, struct radeon_vm *vm) +{ + vm->id = 0; + vm->fence = NULL; + sx_init(&vm->mutex, "drm__radeon_vm__mutex"); + INIT_LIST_HEAD(&vm->list); + INIT_LIST_HEAD(&vm->va); +} + +/** + * radeon_vm_fini - tear down a vm instance + * + * @rdev: radeon_device pointer + * @vm: requested vm + * + * Tear down @vm (cayman+). + * Unbind the VM and remove all bos from the vm bo list + */ +void radeon_vm_fini(struct radeon_device *rdev, struct radeon_vm *vm) +{ + struct radeon_bo_va *bo_va, *tmp; + int r; + + sx_xlock(&rdev->vm_manager.lock); + sx_xlock(&vm->mutex); + radeon_vm_free_pt(rdev, vm); + sx_xunlock(&rdev->vm_manager.lock); + + if (!list_empty(&vm->va)) { + dev_err(rdev->dev, "still active bo inside vm\n"); + } + list_for_each_entry_safe(bo_va, tmp, &vm->va, vm_list) { + list_del_init(&bo_va->vm_list); + r = radeon_bo_reserve(bo_va->bo, false); + if (!r) { + list_del_init(&bo_va->bo_list); + radeon_bo_unreserve(bo_va->bo); + free(bo_va, DRM_MEM_DRIVER); + } + } + radeon_fence_unref(&vm->fence); + radeon_fence_unref(&vm->last_flush); + sx_xunlock(&vm->mutex); +} diff --git a/sys/dev/drm2/radeon/radeon_gem.c b/sys/dev/drm2/radeon/radeon_gem.c new file mode 100644 index 00000000000..390298edfd4 --- /dev/null +++ b/sys/dev/drm2/radeon/radeon_gem.c @@ -0,0 +1,585 @@ +/* + * Copyright 2008 Advanced Micro Devices, Inc. + * Copyright 2008 Red Hat Inc. + * Copyright 2009 Jerome Glisse. + * + * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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. + * + * Authors: Dave Airlie + * Alex Deucher + * Jerome Glisse + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include "radeon.h" +#include "radeon_gem.h" + +int radeon_gem_object_init(struct drm_gem_object *obj) +{ + panic("radeon_gem_object_init() must not be called"); + + return 0; +} + +void radeon_gem_object_free(struct drm_gem_object *gobj) +{ + struct radeon_bo *robj = gem_to_radeon_bo(gobj); + + if (robj) { +#ifdef DUMBBELL_WIP + if (robj->gem_base.import_attach) + drm_prime_gem_destroy(&robj->gem_base, robj->tbo.sg); +#endif /* DUMBBELL_WIP */ + radeon_bo_unref(&robj); + } +} + +int radeon_gem_object_create(struct radeon_device *rdev, int size, + int alignment, int initial_domain, + bool discardable, bool kernel, + struct drm_gem_object **obj) +{ + struct radeon_bo *robj; + unsigned long max_size; + int r; + + *obj = NULL; + /* At least align on page size */ + if (alignment < PAGE_SIZE) { + alignment = PAGE_SIZE; + } + + /* maximun bo size is the minimun btw visible vram and gtt size */ + max_size = min(rdev->mc.visible_vram_size, rdev->mc.gtt_size); + if (size > max_size) { + DRM_ERROR("%s:%d alloc size %dMb bigger than %ldMb limit\n", + __func__, __LINE__, size >> 20, max_size >> 20); + return -ENOMEM; + } + +retry: + r = radeon_bo_create(rdev, size, alignment, kernel, initial_domain, NULL, &robj); + if (r) { + if (r != -ERESTART) { + if (initial_domain == RADEON_GEM_DOMAIN_VRAM) { + initial_domain |= RADEON_GEM_DOMAIN_GTT; + goto retry; + } + DRM_ERROR("Failed to allocate GEM object (%d, %d, %u, %d)\n", + size, initial_domain, alignment, r); + } + return r; + } + *obj = &robj->gem_base; + + sx_xlock(&rdev->gem.mutex); + list_add_tail(&robj->list, &rdev->gem.objects); + sx_xunlock(&rdev->gem.mutex); + + return 0; +} + +static int radeon_gem_set_domain(struct drm_gem_object *gobj, + uint32_t rdomain, uint32_t wdomain) +{ + struct radeon_bo *robj; + uint32_t domain; + int r; + + /* FIXME: reeimplement */ + robj = gem_to_radeon_bo(gobj); + /* work out where to validate the buffer to */ + domain = wdomain; + if (!domain) { + domain = rdomain; + } + if (!domain) { + /* Do nothings */ + DRM_ERROR("Set domain without domain !\n"); + return 0; + } + if (domain == RADEON_GEM_DOMAIN_CPU) { + /* Asking for cpu access wait for object idle */ + r = radeon_bo_wait(robj, NULL, false); + if (r) { + DRM_ERROR("Failed to wait for object !\n"); + return r; + } + } + return 0; +} + +int radeon_gem_init(struct radeon_device *rdev) +{ + INIT_LIST_HEAD(&rdev->gem.objects); + return 0; +} + +void radeon_gem_fini(struct radeon_device *rdev) +{ + radeon_bo_force_delete(rdev); +} + +/* + * Call from drm_gem_handle_create which appear in both new and open ioctl + * case. + */ +int radeon_gem_object_open(struct drm_gem_object *obj, struct drm_file *file_priv) +{ + struct radeon_bo *rbo = gem_to_radeon_bo(obj); + struct radeon_device *rdev = rbo->rdev; + struct radeon_fpriv *fpriv = file_priv->driver_priv; + struct radeon_vm *vm = &fpriv->vm; + struct radeon_bo_va *bo_va; + int r; + + if (rdev->family < CHIP_CAYMAN) { + return 0; + } + + r = radeon_bo_reserve(rbo, false); + if (r) { + return r; + } + + bo_va = radeon_vm_bo_find(vm, rbo); + if (!bo_va) { + bo_va = radeon_vm_bo_add(rdev, vm, rbo); + } else { + ++bo_va->ref_count; + } + radeon_bo_unreserve(rbo); + + return 0; +} + +void radeon_gem_object_close(struct drm_gem_object *obj, + struct drm_file *file_priv) +{ + struct radeon_bo *rbo = gem_to_radeon_bo(obj); + struct radeon_device *rdev = rbo->rdev; + struct radeon_fpriv *fpriv = file_priv->driver_priv; + struct radeon_vm *vm = &fpriv->vm; + struct radeon_bo_va *bo_va; + int r; + + if (rdev->family < CHIP_CAYMAN) { + return; + } + + r = radeon_bo_reserve(rbo, true); + if (r) { + dev_err(rdev->dev, "leaking bo va because " + "we fail to reserve bo (%d)\n", r); + return; + } + bo_va = radeon_vm_bo_find(vm, rbo); + if (bo_va) { + if (--bo_va->ref_count == 0) { + radeon_vm_bo_rmv(rdev, bo_va); + } + } + radeon_bo_unreserve(rbo); +} + +static int radeon_gem_handle_lockup(struct radeon_device *rdev, int r) +{ + if (r == -EDEADLK) { + r = radeon_gpu_reset(rdev); + if (!r) + r = -EAGAIN; + } + return r; +} + +/* + * GEM ioctls. + */ +int radeon_gem_info_ioctl(struct drm_device *dev, void *data, + struct drm_file *filp) +{ + struct radeon_device *rdev = dev->dev_private; + struct drm_radeon_gem_info *args = data; + struct ttm_mem_type_manager *man; + unsigned i; + + man = &rdev->mman.bdev.man[TTM_PL_VRAM]; + + args->vram_size = rdev->mc.real_vram_size; + args->vram_visible = (u64)man->size << PAGE_SHIFT; + if (rdev->stollen_vga_memory) + args->vram_visible -= radeon_bo_size(rdev->stollen_vga_memory); + args->vram_visible -= radeon_fbdev_total_size(rdev); + args->gart_size = rdev->mc.gtt_size - 4096 - RADEON_IB_POOL_SIZE*64*1024; + for(i = 0; i < RADEON_NUM_RINGS; ++i) + args->gart_size -= rdev->ring[i].ring_size; + return 0; +} + +int radeon_gem_pread_ioctl(struct drm_device *dev, void *data, + struct drm_file *filp) +{ + /* TODO: implement */ + DRM_ERROR("unimplemented %s\n", __func__); + return -ENOSYS; +} + +int radeon_gem_pwrite_ioctl(struct drm_device *dev, void *data, + struct drm_file *filp) +{ + /* TODO: implement */ + DRM_ERROR("unimplemented %s\n", __func__); + return -ENOSYS; +} + +int radeon_gem_create_ioctl(struct drm_device *dev, void *data, + struct drm_file *filp) +{ + struct radeon_device *rdev = dev->dev_private; + struct drm_radeon_gem_create *args = data; + struct drm_gem_object *gobj; + uint32_t handle; + int r; + + sx_slock(&rdev->exclusive_lock); + /* create a gem object to contain this object in */ + args->size = roundup(args->size, PAGE_SIZE); + r = radeon_gem_object_create(rdev, args->size, args->alignment, + args->initial_domain, false, + false, &gobj); + if (r) { + sx_sunlock(&rdev->exclusive_lock); + r = radeon_gem_handle_lockup(rdev, r); + return r; + } + handle = 0; + r = drm_gem_handle_create(filp, gobj, &handle); + /* drop reference from allocate - handle holds it now */ + drm_gem_object_unreference_unlocked(gobj); + if (r) { + sx_sunlock(&rdev->exclusive_lock); + r = radeon_gem_handle_lockup(rdev, r); + return r; + } + args->handle = handle; + sx_sunlock(&rdev->exclusive_lock); + return 0; +} + +int radeon_gem_set_domain_ioctl(struct drm_device *dev, void *data, + struct drm_file *filp) +{ + /* transition the BO to a domain - + * just validate the BO into a certain domain */ + struct radeon_device *rdev = dev->dev_private; + struct drm_radeon_gem_set_domain *args = data; + struct drm_gem_object *gobj; + struct radeon_bo *robj; + int r; + + /* for now if someone requests domain CPU - + * just make sure the buffer is finished with */ + sx_slock(&rdev->exclusive_lock); + + /* just do a BO wait for now */ + gobj = drm_gem_object_lookup(dev, filp, args->handle); + if (gobj == NULL) { + sx_sunlock(&rdev->exclusive_lock); + return -ENOENT; + } + robj = gem_to_radeon_bo(gobj); + + r = radeon_gem_set_domain(gobj, args->read_domains, args->write_domain); + + drm_gem_object_unreference_unlocked(gobj); + sx_sunlock(&rdev->exclusive_lock); + r = radeon_gem_handle_lockup(robj->rdev, r); + return r; +} + +int radeon_mode_dumb_mmap(struct drm_file *filp, + struct drm_device *dev, + uint32_t handle, uint64_t *offset_p) +{ + struct drm_gem_object *gobj; + struct radeon_bo *robj; + + gobj = drm_gem_object_lookup(dev, filp, handle); + if (gobj == NULL) { + return -ENOENT; + } + robj = gem_to_radeon_bo(gobj); + *offset_p = radeon_bo_mmap_offset(robj); + drm_gem_object_unreference_unlocked(gobj); + return 0; +} + +int radeon_gem_mmap_ioctl(struct drm_device *dev, void *data, + struct drm_file *filp) +{ + struct drm_radeon_gem_mmap *args = data; + + return radeon_mode_dumb_mmap(filp, dev, args->handle, &args->addr_ptr); +} + +int radeon_gem_busy_ioctl(struct drm_device *dev, void *data, + struct drm_file *filp) +{ + struct radeon_device *rdev = dev->dev_private; + struct drm_radeon_gem_busy *args = data; + struct drm_gem_object *gobj; + struct radeon_bo *robj; + int r; + uint32_t cur_placement = 0; + + gobj = drm_gem_object_lookup(dev, filp, args->handle); + if (gobj == NULL) { + return -ENOENT; + } + robj = gem_to_radeon_bo(gobj); + r = radeon_bo_wait(robj, &cur_placement, true); + switch (cur_placement) { + case TTM_PL_VRAM: + args->domain = RADEON_GEM_DOMAIN_VRAM; + break; + case TTM_PL_TT: + args->domain = RADEON_GEM_DOMAIN_GTT; + break; + case TTM_PL_SYSTEM: + args->domain = RADEON_GEM_DOMAIN_CPU; + default: + break; + } + drm_gem_object_unreference_unlocked(gobj); + r = radeon_gem_handle_lockup(rdev, r); + return r; +} + +int radeon_gem_wait_idle_ioctl(struct drm_device *dev, void *data, + struct drm_file *filp) +{ + struct radeon_device *rdev = dev->dev_private; + struct drm_radeon_gem_wait_idle *args = data; + struct drm_gem_object *gobj; + struct radeon_bo *robj; + int r; + + gobj = drm_gem_object_lookup(dev, filp, args->handle); + if (gobj == NULL) { + return -ENOENT; + } + robj = gem_to_radeon_bo(gobj); + r = radeon_bo_wait(robj, NULL, false); + /* callback hw specific functions if any */ + if (rdev->asic->ioctl_wait_idle) + robj->rdev->asic->ioctl_wait_idle(rdev, robj); + drm_gem_object_unreference_unlocked(gobj); + r = radeon_gem_handle_lockup(rdev, r); + return r; +} + +int radeon_gem_set_tiling_ioctl(struct drm_device *dev, void *data, + struct drm_file *filp) +{ + struct drm_radeon_gem_set_tiling *args = data; + struct drm_gem_object *gobj; + struct radeon_bo *robj; + int r = 0; + + DRM_DEBUG("%d \n", args->handle); + gobj = drm_gem_object_lookup(dev, filp, args->handle); + if (gobj == NULL) + return -ENOENT; + robj = gem_to_radeon_bo(gobj); + r = radeon_bo_set_tiling_flags(robj, args->tiling_flags, args->pitch); + drm_gem_object_unreference_unlocked(gobj); + return r; +} + +int radeon_gem_get_tiling_ioctl(struct drm_device *dev, void *data, + struct drm_file *filp) +{ + struct drm_radeon_gem_get_tiling *args = data; + struct drm_gem_object *gobj; + struct radeon_bo *rbo; + int r = 0; + + DRM_DEBUG("\n"); + gobj = drm_gem_object_lookup(dev, filp, args->handle); + if (gobj == NULL) + return -ENOENT; + rbo = gem_to_radeon_bo(gobj); + r = radeon_bo_reserve(rbo, false); + if (unlikely(r != 0)) + goto out; + radeon_bo_get_tiling_flags(rbo, &args->tiling_flags, &args->pitch); + radeon_bo_unreserve(rbo); +out: + drm_gem_object_unreference_unlocked(gobj); + return r; +} + +int radeon_gem_va_ioctl(struct drm_device *dev, void *data, + struct drm_file *filp) +{ + struct drm_radeon_gem_va *args = data; + struct drm_gem_object *gobj; + struct radeon_device *rdev = dev->dev_private; + struct radeon_fpriv *fpriv = filp->driver_priv; + struct radeon_bo *rbo; + struct radeon_bo_va *bo_va; + u32 invalid_flags; + int r = 0; + + if (!rdev->vm_manager.enabled) { + args->operation = RADEON_VA_RESULT_ERROR; + return -ENOTTY; + } + + /* !! DONT REMOVE !! + * We don't support vm_id yet, to be sure we don't have have broken + * userspace, reject anyone trying to use non 0 value thus moving + * forward we can use those fields without breaking existant userspace + */ + if (args->vm_id) { + args->operation = RADEON_VA_RESULT_ERROR; + return -EINVAL; + } + + if (args->offset < RADEON_VA_RESERVED_SIZE) { + dev_err(dev->device, + "offset 0x%lX is in reserved area 0x%X\n", + (unsigned long)args->offset, + RADEON_VA_RESERVED_SIZE); + args->operation = RADEON_VA_RESULT_ERROR; + return -EINVAL; + } + + /* don't remove, we need to enforce userspace to set the snooped flag + * otherwise we will endup with broken userspace and we won't be able + * to enable this feature without adding new interface + */ + invalid_flags = RADEON_VM_PAGE_VALID | RADEON_VM_PAGE_SYSTEM; + if ((args->flags & invalid_flags)) { + dev_err(dev->device, "invalid flags 0x%08X vs 0x%08X\n", + args->flags, invalid_flags); + args->operation = RADEON_VA_RESULT_ERROR; + return -EINVAL; + } + if (!(args->flags & RADEON_VM_PAGE_SNOOPED)) { + dev_err(dev->device, "only supported snooped mapping for now\n"); + args->operation = RADEON_VA_RESULT_ERROR; + return -EINVAL; + } + + switch (args->operation) { + case RADEON_VA_MAP: + case RADEON_VA_UNMAP: + break; + default: + dev_err(dev->device, "unsupported operation %d\n", + args->operation); + args->operation = RADEON_VA_RESULT_ERROR; + return -EINVAL; + } + + gobj = drm_gem_object_lookup(dev, filp, args->handle); + if (gobj == NULL) { + args->operation = RADEON_VA_RESULT_ERROR; + return -ENOENT; + } + rbo = gem_to_radeon_bo(gobj); + r = radeon_bo_reserve(rbo, false); + if (r) { + args->operation = RADEON_VA_RESULT_ERROR; + drm_gem_object_unreference_unlocked(gobj); + return r; + } + bo_va = radeon_vm_bo_find(&fpriv->vm, rbo); + if (!bo_va) { + args->operation = RADEON_VA_RESULT_ERROR; + drm_gem_object_unreference_unlocked(gobj); + return -ENOENT; + } + + switch (args->operation) { + case RADEON_VA_MAP: + if (bo_va->soffset) { + args->operation = RADEON_VA_RESULT_VA_EXIST; + args->offset = bo_va->soffset; + goto out; + } + r = radeon_vm_bo_set_addr(rdev, bo_va, args->offset, args->flags); + break; + case RADEON_VA_UNMAP: + r = radeon_vm_bo_set_addr(rdev, bo_va, 0, 0); + break; + default: + break; + } + args->operation = RADEON_VA_RESULT_OK; + if (r) { + args->operation = RADEON_VA_RESULT_ERROR; + } +out: + radeon_bo_unreserve(rbo); + drm_gem_object_unreference_unlocked(gobj); + return r; +} + +int radeon_mode_dumb_create(struct drm_file *file_priv, + struct drm_device *dev, + struct drm_mode_create_dumb *args) +{ + struct radeon_device *rdev = dev->dev_private; + struct drm_gem_object *gobj; + uint32_t handle; + int r; + + args->pitch = radeon_align_pitch(rdev, args->width, args->bpp, 0) * ((args->bpp + 1) / 8); + args->size = args->pitch * args->height; + args->size = roundup2(args->size, PAGE_SIZE); + + r = radeon_gem_object_create(rdev, args->size, 0, + RADEON_GEM_DOMAIN_VRAM, + false, ttm_bo_type_device, + &gobj); + if (r) + return -ENOMEM; + + r = drm_gem_handle_create(file_priv, gobj, &handle); + /* drop reference from allocate - handle holds it now */ + drm_gem_object_unreference_unlocked(gobj); + if (r) { + return r; + } + args->handle = handle; + return 0; +} + +int radeon_mode_dumb_destroy(struct drm_file *file_priv, + struct drm_device *dev, + uint32_t handle) +{ + return drm_gem_handle_delete(file_priv, handle); +} diff --git a/sys/dev/drm2/radeon/radeon_gem.h b/sys/dev/drm2/radeon/radeon_gem.h new file mode 100644 index 00000000000..c7edd503f21 --- /dev/null +++ b/sys/dev/drm2/radeon/radeon_gem.h @@ -0,0 +1,17 @@ + +#include +__FBSDID("$FreeBSD$"); + +#ifndef __RADEON_GEM_H__ +#define __RADEON_GEM_H__ + +#include + +int radeon_gem_object_init(struct drm_gem_object *obj); +void radeon_gem_object_free(struct drm_gem_object *obj); +int radeon_gem_object_open(struct drm_gem_object *obj, + struct drm_file *file_priv); +void radeon_gem_object_close(struct drm_gem_object *obj, + struct drm_file *file_priv); + +#endif /* !defined(__RADEON_GEM_H__) */ diff --git a/sys/dev/drm2/radeon/radeon_i2c.c b/sys/dev/drm2/radeon/radeon_i2c.c new file mode 100644 index 00000000000..917286a14c1 --- /dev/null +++ b/sys/dev/drm2/radeon/radeon_i2c.c @@ -0,0 +1,1384 @@ +/* + * Copyright 2007-8 Advanced Micro Devices, Inc. + * Copyright 2008 Red Hat Inc. + * + * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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. + * + * Authors: Dave Airlie + * Alex Deucher + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include "radeon.h" +#include "atom.h" +#include "iicbus_if.h" +#include "iicbb_if.h" + +/** + * radeon_ddc_probe + * + */ +bool radeon_ddc_probe(struct radeon_connector *radeon_connector, bool use_aux) +{ + u8 out = 0x0; + u8 buf[8]; + int ret; + struct iic_msg msgs[] = { + { + .slave = DDC_ADDR << 1, + .flags = 0, + .len = 1, + .buf = &out, + }, + { + .slave = DDC_ADDR << 1, + .flags = IIC_M_RD, + .len = 8, + .buf = buf, + } + }; + + /* on hw with routers, select right port */ + if (radeon_connector->router.ddc_valid) + radeon_router_select_ddc_port(radeon_connector); + + if (use_aux) { + struct radeon_connector_atom_dig *dig = radeon_connector->con_priv; + ret = iicbus_transfer(dig->dp_i2c_bus->adapter, msgs, 2); + } else { + ret = iicbus_transfer(radeon_connector->ddc_bus->adapter, msgs, 2); + } + + if (ret != 0) + /* Couldn't find an accessible DDC on this connector */ + return false; + /* Probe also for valid EDID header + * EDID header starts with: + * 0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00. + * Only the first 6 bytes must be valid as + * drm_edid_block_valid() can fix the last 2 bytes */ + if (drm_edid_header_is_valid(buf) < 6) { + /* Couldn't find an accessible EDID on this + * connector */ + return false; + } + return true; +} + +/* bit banging i2c */ + +static int radeon_iicbb_pre_xfer(device_t dev) +{ + struct radeon_i2c_chan *i2c = device_get_softc(dev); + struct radeon_device *rdev = i2c->dev->dev_private; + struct radeon_i2c_bus_rec *rec = &i2c->rec; + uint32_t temp; + + /* RV410 appears to have a bug where the hw i2c in reset + * holds the i2c port in a bad state - switch hw i2c away before + * doing DDC - do this for all r200s/r300s/r400s for safety sake + */ + if (rec->hw_capable) { + if ((rdev->family >= CHIP_R200) && !ASIC_IS_AVIVO(rdev)) { + u32 reg; + + if (rdev->family >= CHIP_RV350) + reg = RADEON_GPIO_MONID; + else if ((rdev->family == CHIP_R300) || + (rdev->family == CHIP_R350)) + reg = RADEON_GPIO_DVI_DDC; + else + reg = RADEON_GPIO_CRT2_DDC; + + sx_xlock(&rdev->dc_hw_i2c_mutex); + if (rec->a_clk_reg == reg) { + WREG32(RADEON_DVI_I2C_CNTL_0, (RADEON_I2C_SOFT_RST | + R200_DVI_I2C_PIN_SEL(R200_SEL_DDC1))); + } else { + WREG32(RADEON_DVI_I2C_CNTL_0, (RADEON_I2C_SOFT_RST | + R200_DVI_I2C_PIN_SEL(R200_SEL_DDC3))); + } + sx_xunlock(&rdev->dc_hw_i2c_mutex); + } + } + + /* switch the pads to ddc mode */ + if (ASIC_IS_DCE3(rdev) && rec->hw_capable) { + temp = RREG32(rec->mask_clk_reg); + temp &= ~(1 << 16); + WREG32(rec->mask_clk_reg, temp); + } + + /* clear the output pin values */ + temp = RREG32(rec->a_clk_reg) & ~rec->a_clk_mask; + WREG32(rec->a_clk_reg, temp); + + temp = RREG32(rec->a_data_reg) & ~rec->a_data_mask; + WREG32(rec->a_data_reg, temp); + + /* set the pins to input */ + temp = RREG32(rec->en_clk_reg) & ~rec->en_clk_mask; + WREG32(rec->en_clk_reg, temp); + + temp = RREG32(rec->en_data_reg) & ~rec->en_data_mask; + WREG32(rec->en_data_reg, temp); + + /* mask the gpio pins for software use */ + temp = RREG32(rec->mask_clk_reg) | rec->mask_clk_mask; + WREG32(rec->mask_clk_reg, temp); + temp = RREG32(rec->mask_clk_reg); + + temp = RREG32(rec->mask_data_reg) | rec->mask_data_mask; + WREG32(rec->mask_data_reg, temp); + temp = RREG32(rec->mask_data_reg); + + return 0; +} + +static void radeon_iicbb_post_xfer(device_t dev) +{ + struct radeon_i2c_chan *i2c = device_get_softc(dev); + struct radeon_device *rdev = i2c->dev->dev_private; + struct radeon_i2c_bus_rec *rec = &i2c->rec; + uint32_t temp; + + /* unmask the gpio pins for software use */ + temp = RREG32(rec->mask_clk_reg) & ~rec->mask_clk_mask; + WREG32(rec->mask_clk_reg, temp); + temp = RREG32(rec->mask_clk_reg); + + temp = RREG32(rec->mask_data_reg) & ~rec->mask_data_mask; + WREG32(rec->mask_data_reg, temp); + temp = RREG32(rec->mask_data_reg); +} + +static int radeon_iicbb_get_clock(device_t dev) +{ + struct radeon_i2c_chan *i2c = device_get_softc(dev); + struct radeon_device *rdev = i2c->dev->dev_private; + struct radeon_i2c_bus_rec *rec = &i2c->rec; + uint32_t val; + + /* read the value off the pin */ + val = RREG32(rec->y_clk_reg); + val &= rec->y_clk_mask; + + return (val != 0); +} + + +static int radeon_iicbb_get_data(device_t dev) +{ + struct radeon_i2c_chan *i2c = device_get_softc(dev); + struct radeon_device *rdev = i2c->dev->dev_private; + struct radeon_i2c_bus_rec *rec = &i2c->rec; + uint32_t val; + + /* read the value off the pin */ + val = RREG32(rec->y_data_reg); + val &= rec->y_data_mask; + + return (val != 0); +} + +static void radeon_iicbb_set_clock(device_t dev, int clock) +{ + struct radeon_i2c_chan *i2c = device_get_softc(dev); + struct radeon_device *rdev = i2c->dev->dev_private; + struct radeon_i2c_bus_rec *rec = &i2c->rec; + uint32_t val; + + /* set pin direction */ + val = RREG32(rec->en_clk_reg) & ~rec->en_clk_mask; + val |= clock ? 0 : rec->en_clk_mask; + WREG32(rec->en_clk_reg, val); +} + +static void radeon_iicbb_set_data(device_t dev, int data) +{ + struct radeon_i2c_chan *i2c = device_get_softc(dev); + struct radeon_device *rdev = i2c->dev->dev_private; + struct radeon_i2c_bus_rec *rec = &i2c->rec; + uint32_t val; + + /* set pin direction */ + val = RREG32(rec->en_data_reg) & ~rec->en_data_mask; + val |= data ? 0 : rec->en_data_mask; + WREG32(rec->en_data_reg, val); +} + +static int +radeon_iicbb_probe(device_t dev) +{ + + return (BUS_PROBE_DEFAULT); +} + +static int +radeon_iicbb_attach(device_t dev) +{ + struct radeon_i2c_chan *i2c; + device_t iic_dev; + + i2c = device_get_softc(dev); + device_set_desc(dev, i2c->name); + + /* add generic bit-banging code */ + iic_dev = device_add_child(dev, "iicbb", -1); + if (iic_dev == NULL) + return (ENXIO); + device_quiet(iic_dev); + + /* attach and probe added child */ + bus_generic_attach(dev); + + return (0); +} + +static int +radeon_iicbb_detach(device_t dev) +{ + + /* detach bit-banding code. */ + bus_generic_detach(dev); + + /* delete bit-banding code. */ + device_delete_children(dev); + return (0); +} + +static int +radeon_iicbb_reset(device_t dev, u_char speed, u_char addr, u_char *oldaddr) +{ + + /* Not sure what to do here. */ + return 0; +} + +static device_method_t radeon_iicbb_methods[] = { + DEVMETHOD(device_probe, radeon_iicbb_probe), + DEVMETHOD(device_attach, radeon_iicbb_attach), + DEVMETHOD(device_detach, radeon_iicbb_detach), + + DEVMETHOD(bus_add_child, bus_generic_add_child), + DEVMETHOD(bus_print_child, bus_generic_print_child), + + DEVMETHOD(iicbb_reset, radeon_iicbb_reset), + DEVMETHOD(iicbb_pre_xfer, radeon_iicbb_pre_xfer), + DEVMETHOD(iicbb_post_xfer, radeon_iicbb_post_xfer), + DEVMETHOD(iicbb_setsda, radeon_iicbb_set_data), + DEVMETHOD(iicbb_setscl, radeon_iicbb_set_clock), + DEVMETHOD(iicbb_getsda, radeon_iicbb_get_data), + DEVMETHOD(iicbb_getscl, radeon_iicbb_get_clock), + DEVMETHOD_END +}; + +static driver_t radeon_iicbb_driver = { + "radeon_iicbb", + radeon_iicbb_methods, + 0 /* softc will be allocated by parent */ +}; +static devclass_t radeon_iicbb_devclass; +DRIVER_MODULE_ORDERED(radeon_iicbb, drmn, radeon_iicbb_driver, + radeon_iicbb_devclass, 0, 0, SI_ORDER_FIRST); +DRIVER_MODULE(iicbb, radeon_iicbb, iicbb_driver, iicbb_devclass, 0, 0); + +/* hw i2c */ + +static u32 radeon_get_i2c_prescale(struct radeon_device *rdev) +{ + u32 sclk = rdev->pm.current_sclk; + u32 prescale = 0; + u32 nm; + u8 n, m, loop; + int i2c_clock; + + switch (rdev->family) { + case CHIP_R100: + case CHIP_RV100: + case CHIP_RS100: + case CHIP_RV200: + case CHIP_RS200: + case CHIP_R200: + case CHIP_RV250: + case CHIP_RS300: + case CHIP_RV280: + case CHIP_R300: + case CHIP_R350: + case CHIP_RV350: + i2c_clock = 60; + nm = (sclk * 10) / (i2c_clock * 4); + for (loop = 1; loop < 255; loop++) { + if ((nm / loop) < loop) + break; + } + n = loop - 1; + m = loop - 2; + prescale = m | (n << 8); + break; + case CHIP_RV380: + case CHIP_RS400: + case CHIP_RS480: + case CHIP_R420: + case CHIP_R423: + case CHIP_RV410: + prescale = (((sclk * 10)/(4 * 128 * 100) + 1) << 8) + 128; + break; + case CHIP_RS600: + case CHIP_RS690: + case CHIP_RS740: + /* todo */ + break; + case CHIP_RV515: + case CHIP_R520: + case CHIP_RV530: + case CHIP_RV560: + case CHIP_RV570: + case CHIP_R580: + i2c_clock = 50; + if (rdev->family == CHIP_R520) + prescale = (127 << 8) + ((sclk * 10) / (4 * 127 * i2c_clock)); + else + prescale = (((sclk * 10)/(4 * 128 * 100) + 1) << 8) + 128; + break; + case CHIP_R600: + case CHIP_RV610: + case CHIP_RV630: + case CHIP_RV670: + /* todo */ + break; + case CHIP_RV620: + case CHIP_RV635: + case CHIP_RS780: + case CHIP_RS880: + case CHIP_RV770: + case CHIP_RV730: + case CHIP_RV710: + case CHIP_RV740: + /* todo */ + break; + case CHIP_CEDAR: + case CHIP_REDWOOD: + case CHIP_JUNIPER: + case CHIP_CYPRESS: + case CHIP_HEMLOCK: + /* todo */ + break; + default: + DRM_ERROR("i2c: unhandled radeon chip\n"); + break; + } + return prescale; +} + + +/* hw i2c engine for r1xx-4xx hardware + * hw can buffer up to 15 bytes + */ +static int r100_hw_i2c_xfer(struct radeon_i2c_chan *i2c, + struct iic_msg *msgs, int num) +{ + struct radeon_device *rdev = i2c->dev->dev_private; + struct radeon_i2c_bus_rec *rec = &i2c->rec; + struct iic_msg *p; + int i, j, k, ret = 0; + u32 prescale; + u32 i2c_cntl_0, i2c_cntl_1, i2c_data; + u32 tmp, reg; + + sx_xlock(&rdev->dc_hw_i2c_mutex); + /* take the pm lock since we need a constant sclk */ + sx_xlock(&rdev->pm.mutex); + + prescale = radeon_get_i2c_prescale(rdev); + + reg = ((prescale << RADEON_I2C_PRESCALE_SHIFT) | + RADEON_I2C_DRIVE_EN | + RADEON_I2C_START | + RADEON_I2C_STOP | + RADEON_I2C_GO); + + if (rdev->is_atom_bios) { + tmp = RREG32(RADEON_BIOS_6_SCRATCH); + WREG32(RADEON_BIOS_6_SCRATCH, tmp | ATOM_S6_HW_I2C_BUSY_STATE); + } + + if (rec->mm_i2c) { + i2c_cntl_0 = RADEON_I2C_CNTL_0; + i2c_cntl_1 = RADEON_I2C_CNTL_1; + i2c_data = RADEON_I2C_DATA; + } else { + i2c_cntl_0 = RADEON_DVI_I2C_CNTL_0; + i2c_cntl_1 = RADEON_DVI_I2C_CNTL_1; + i2c_data = RADEON_DVI_I2C_DATA; + + switch (rdev->family) { + case CHIP_R100: + case CHIP_RV100: + case CHIP_RS100: + case CHIP_RV200: + case CHIP_RS200: + case CHIP_RS300: + switch (rec->mask_clk_reg) { + case RADEON_GPIO_DVI_DDC: + /* no gpio select bit */ + break; + default: + DRM_ERROR("gpio not supported with hw i2c\n"); + ret = EINVAL; + goto done; + } + break; + case CHIP_R200: + /* only bit 4 on r200 */ + switch (rec->mask_clk_reg) { + case RADEON_GPIO_DVI_DDC: + reg |= R200_DVI_I2C_PIN_SEL(R200_SEL_DDC1); + break; + case RADEON_GPIO_MONID: + reg |= R200_DVI_I2C_PIN_SEL(R200_SEL_DDC3); + break; + default: + DRM_ERROR("gpio not supported with hw i2c\n"); + ret = EINVAL; + goto done; + } + break; + case CHIP_RV250: + case CHIP_RV280: + /* bits 3 and 4 */ + switch (rec->mask_clk_reg) { + case RADEON_GPIO_DVI_DDC: + reg |= R200_DVI_I2C_PIN_SEL(R200_SEL_DDC1); + break; + case RADEON_GPIO_VGA_DDC: + reg |= R200_DVI_I2C_PIN_SEL(R200_SEL_DDC2); + break; + case RADEON_GPIO_CRT2_DDC: + reg |= R200_DVI_I2C_PIN_SEL(R200_SEL_DDC3); + break; + default: + DRM_ERROR("gpio not supported with hw i2c\n"); + ret = EINVAL; + goto done; + } + break; + case CHIP_R300: + case CHIP_R350: + /* only bit 4 on r300/r350 */ + switch (rec->mask_clk_reg) { + case RADEON_GPIO_VGA_DDC: + reg |= R200_DVI_I2C_PIN_SEL(R200_SEL_DDC1); + break; + case RADEON_GPIO_DVI_DDC: + reg |= R200_DVI_I2C_PIN_SEL(R200_SEL_DDC3); + break; + default: + DRM_ERROR("gpio not supported with hw i2c\n"); + ret = EINVAL; + goto done; + } + break; + case CHIP_RV350: + case CHIP_RV380: + case CHIP_R420: + case CHIP_R423: + case CHIP_RV410: + case CHIP_RS400: + case CHIP_RS480: + /* bits 3 and 4 */ + switch (rec->mask_clk_reg) { + case RADEON_GPIO_VGA_DDC: + reg |= R200_DVI_I2C_PIN_SEL(R200_SEL_DDC1); + break; + case RADEON_GPIO_DVI_DDC: + reg |= R200_DVI_I2C_PIN_SEL(R200_SEL_DDC2); + break; + case RADEON_GPIO_MONID: + reg |= R200_DVI_I2C_PIN_SEL(R200_SEL_DDC3); + break; + default: + DRM_ERROR("gpio not supported with hw i2c\n"); + ret = EINVAL; + goto done; + } + break; + default: + DRM_ERROR("unsupported asic\n"); + ret = EINVAL; + goto done; + break; + } + } + + /* check for bus probe */ + p = &msgs[0]; + if ((num == 1) && (p->len == 0)) { + WREG32(i2c_cntl_0, (RADEON_I2C_DONE | + RADEON_I2C_NACK | + RADEON_I2C_HALT | + RADEON_I2C_SOFT_RST)); + WREG32(i2c_data, (p->slave << 1) & 0xff); + WREG32(i2c_data, 0); + WREG32(i2c_cntl_1, ((1 << RADEON_I2C_DATA_COUNT_SHIFT) | + (1 << RADEON_I2C_ADDR_COUNT_SHIFT) | + RADEON_I2C_EN | + (48 << RADEON_I2C_TIME_LIMIT_SHIFT))); + WREG32(i2c_cntl_0, reg); + for (k = 0; k < 32; k++) { + DRM_UDELAY(10); + tmp = RREG32(i2c_cntl_0); + if (tmp & RADEON_I2C_GO) + continue; + tmp = RREG32(i2c_cntl_0); + if (tmp & RADEON_I2C_DONE) + break; + else { + DRM_DEBUG("i2c write error 0x%08x\n", tmp); + WREG32(i2c_cntl_0, tmp | RADEON_I2C_ABORT); + ret = EIO; + goto done; + } + } + goto done; + } + + for (i = 0; i < num; i++) { + p = &msgs[i]; + for (j = 0; j < p->len; j++) { + if (p->flags & IIC_M_RD) { + WREG32(i2c_cntl_0, (RADEON_I2C_DONE | + RADEON_I2C_NACK | + RADEON_I2C_HALT | + RADEON_I2C_SOFT_RST)); + WREG32(i2c_data, ((p->slave << 1) & 0xff) | 0x1); + WREG32(i2c_cntl_1, ((1 << RADEON_I2C_DATA_COUNT_SHIFT) | + (1 << RADEON_I2C_ADDR_COUNT_SHIFT) | + RADEON_I2C_EN | + (48 << RADEON_I2C_TIME_LIMIT_SHIFT))); + WREG32(i2c_cntl_0, reg | RADEON_I2C_RECEIVE); + for (k = 0; k < 32; k++) { + DRM_UDELAY(10); + tmp = RREG32(i2c_cntl_0); + if (tmp & RADEON_I2C_GO) + continue; + tmp = RREG32(i2c_cntl_0); + if (tmp & RADEON_I2C_DONE) + break; + else { + DRM_DEBUG("i2c read error 0x%08x\n", tmp); + WREG32(i2c_cntl_0, tmp | RADEON_I2C_ABORT); + ret = EIO; + goto done; + } + } + p->buf[j] = RREG32(i2c_data) & 0xff; + } else { + WREG32(i2c_cntl_0, (RADEON_I2C_DONE | + RADEON_I2C_NACK | + RADEON_I2C_HALT | + RADEON_I2C_SOFT_RST)); + WREG32(i2c_data, (p->slave << 1) & 0xff); + WREG32(i2c_data, p->buf[j]); + WREG32(i2c_cntl_1, ((1 << RADEON_I2C_DATA_COUNT_SHIFT) | + (1 << RADEON_I2C_ADDR_COUNT_SHIFT) | + RADEON_I2C_EN | + (48 << RADEON_I2C_TIME_LIMIT_SHIFT))); + WREG32(i2c_cntl_0, reg); + for (k = 0; k < 32; k++) { + DRM_UDELAY(10); + tmp = RREG32(i2c_cntl_0); + if (tmp & RADEON_I2C_GO) + continue; + tmp = RREG32(i2c_cntl_0); + if (tmp & RADEON_I2C_DONE) + break; + else { + DRM_DEBUG("i2c write error 0x%08x\n", tmp); + WREG32(i2c_cntl_0, tmp | RADEON_I2C_ABORT); + ret = EIO; + goto done; + } + } + } + } + } + +done: + WREG32(i2c_cntl_0, 0); + WREG32(i2c_cntl_1, 0); + WREG32(i2c_cntl_0, (RADEON_I2C_DONE | + RADEON_I2C_NACK | + RADEON_I2C_HALT | + RADEON_I2C_SOFT_RST)); + + if (rdev->is_atom_bios) { + tmp = RREG32(RADEON_BIOS_6_SCRATCH); + tmp &= ~ATOM_S6_HW_I2C_BUSY_STATE; + WREG32(RADEON_BIOS_6_SCRATCH, tmp); + } + + sx_xunlock(&rdev->pm.mutex); + sx_xunlock(&rdev->dc_hw_i2c_mutex); + + return ret; +} + +/* hw i2c engine for r5xx hardware + * hw can buffer up to 15 bytes + */ +static int r500_hw_i2c_xfer(struct radeon_i2c_chan *i2c, + struct iic_msg *msgs, int num) +{ + struct radeon_device *rdev = i2c->dev->dev_private; + struct radeon_i2c_bus_rec *rec = &i2c->rec; + struct iic_msg *p; + int i, j, remaining, current_count, buffer_offset, ret = 0; + u32 prescale; + u32 tmp, reg; + u32 saved1, saved2; + + sx_xlock(&rdev->dc_hw_i2c_mutex); + /* take the pm lock since we need a constant sclk */ + sx_xlock(&rdev->pm.mutex); + + prescale = radeon_get_i2c_prescale(rdev); + + /* clear gpio mask bits */ + tmp = RREG32(rec->mask_clk_reg); + tmp &= ~rec->mask_clk_mask; + WREG32(rec->mask_clk_reg, tmp); + tmp = RREG32(rec->mask_clk_reg); + + tmp = RREG32(rec->mask_data_reg); + tmp &= ~rec->mask_data_mask; + WREG32(rec->mask_data_reg, tmp); + tmp = RREG32(rec->mask_data_reg); + + /* clear pin values */ + tmp = RREG32(rec->a_clk_reg); + tmp &= ~rec->a_clk_mask; + WREG32(rec->a_clk_reg, tmp); + tmp = RREG32(rec->a_clk_reg); + + tmp = RREG32(rec->a_data_reg); + tmp &= ~rec->a_data_mask; + WREG32(rec->a_data_reg, tmp); + tmp = RREG32(rec->a_data_reg); + + /* set the pins to input */ + tmp = RREG32(rec->en_clk_reg); + tmp &= ~rec->en_clk_mask; + WREG32(rec->en_clk_reg, tmp); + tmp = RREG32(rec->en_clk_reg); + + tmp = RREG32(rec->en_data_reg); + tmp &= ~rec->en_data_mask; + WREG32(rec->en_data_reg, tmp); + tmp = RREG32(rec->en_data_reg); + + /* */ + tmp = RREG32(RADEON_BIOS_6_SCRATCH); + WREG32(RADEON_BIOS_6_SCRATCH, tmp | ATOM_S6_HW_I2C_BUSY_STATE); + saved1 = RREG32(AVIVO_DC_I2C_CONTROL1); + saved2 = RREG32(0x494); + WREG32(0x494, saved2 | 0x1); + + WREG32(AVIVO_DC_I2C_ARBITRATION, AVIVO_DC_I2C_SW_WANTS_TO_USE_I2C); + for (i = 0; i < 50; i++) { + DRM_UDELAY(1); + if (RREG32(AVIVO_DC_I2C_ARBITRATION) & AVIVO_DC_I2C_SW_CAN_USE_I2C) + break; + } + if (i == 50) { + DRM_ERROR("failed to get i2c bus\n"); + ret = EBUSY; + goto done; + } + + reg = AVIVO_DC_I2C_START | AVIVO_DC_I2C_STOP | AVIVO_DC_I2C_EN; + switch (rec->mask_clk_reg) { + case AVIVO_DC_GPIO_DDC1_MASK: + reg |= AVIVO_DC_I2C_PIN_SELECT(AVIVO_SEL_DDC1); + break; + case AVIVO_DC_GPIO_DDC2_MASK: + reg |= AVIVO_DC_I2C_PIN_SELECT(AVIVO_SEL_DDC2); + break; + case AVIVO_DC_GPIO_DDC3_MASK: + reg |= AVIVO_DC_I2C_PIN_SELECT(AVIVO_SEL_DDC3); + break; + default: + DRM_ERROR("gpio not supported with hw i2c\n"); + ret = EINVAL; + goto done; + } + + /* check for bus probe */ + p = &msgs[0]; + if ((num == 1) && (p->len == 0)) { + WREG32(AVIVO_DC_I2C_STATUS1, (AVIVO_DC_I2C_DONE | + AVIVO_DC_I2C_NACK | + AVIVO_DC_I2C_HALT)); + WREG32(AVIVO_DC_I2C_RESET, AVIVO_DC_I2C_SOFT_RESET); + DRM_UDELAY(1); + WREG32(AVIVO_DC_I2C_RESET, 0); + + WREG32(AVIVO_DC_I2C_DATA, (p->slave << 1) & 0xff); + WREG32(AVIVO_DC_I2C_DATA, 0); + + WREG32(AVIVO_DC_I2C_CONTROL3, AVIVO_DC_I2C_TIME_LIMIT(48)); + WREG32(AVIVO_DC_I2C_CONTROL2, (AVIVO_DC_I2C_ADDR_COUNT(1) | + AVIVO_DC_I2C_DATA_COUNT(1) | + (prescale << 16))); + WREG32(AVIVO_DC_I2C_CONTROL1, reg); + WREG32(AVIVO_DC_I2C_STATUS1, AVIVO_DC_I2C_GO); + for (j = 0; j < 200; j++) { + DRM_UDELAY(50); + tmp = RREG32(AVIVO_DC_I2C_STATUS1); + if (tmp & AVIVO_DC_I2C_GO) + continue; + tmp = RREG32(AVIVO_DC_I2C_STATUS1); + if (tmp & AVIVO_DC_I2C_DONE) + break; + else { + DRM_DEBUG("i2c write error 0x%08x\n", tmp); + WREG32(AVIVO_DC_I2C_RESET, AVIVO_DC_I2C_ABORT); + ret = EIO; + goto done; + } + } + goto done; + } + + for (i = 0; i < num; i++) { + p = &msgs[i]; + remaining = p->len; + buffer_offset = 0; + if (p->flags & IIC_M_RD) { + while (remaining) { + if (remaining > 15) + current_count = 15; + else + current_count = remaining; + WREG32(AVIVO_DC_I2C_STATUS1, (AVIVO_DC_I2C_DONE | + AVIVO_DC_I2C_NACK | + AVIVO_DC_I2C_HALT)); + WREG32(AVIVO_DC_I2C_RESET, AVIVO_DC_I2C_SOFT_RESET); + DRM_UDELAY(1); + WREG32(AVIVO_DC_I2C_RESET, 0); + + WREG32(AVIVO_DC_I2C_DATA, ((p->slave << 1) & 0xff) | 0x1); + WREG32(AVIVO_DC_I2C_CONTROL3, AVIVO_DC_I2C_TIME_LIMIT(48)); + WREG32(AVIVO_DC_I2C_CONTROL2, (AVIVO_DC_I2C_ADDR_COUNT(1) | + AVIVO_DC_I2C_DATA_COUNT(current_count) | + (prescale << 16))); + WREG32(AVIVO_DC_I2C_CONTROL1, reg | AVIVO_DC_I2C_RECEIVE); + WREG32(AVIVO_DC_I2C_STATUS1, AVIVO_DC_I2C_GO); + for (j = 0; j < 200; j++) { + DRM_UDELAY(50); + tmp = RREG32(AVIVO_DC_I2C_STATUS1); + if (tmp & AVIVO_DC_I2C_GO) + continue; + tmp = RREG32(AVIVO_DC_I2C_STATUS1); + if (tmp & AVIVO_DC_I2C_DONE) + break; + else { + DRM_DEBUG("i2c read error 0x%08x\n", tmp); + WREG32(AVIVO_DC_I2C_RESET, AVIVO_DC_I2C_ABORT); + ret = EIO; + goto done; + } + } + for (j = 0; j < current_count; j++) + p->buf[buffer_offset + j] = RREG32(AVIVO_DC_I2C_DATA) & 0xff; + remaining -= current_count; + buffer_offset += current_count; + } + } else { + while (remaining) { + if (remaining > 15) + current_count = 15; + else + current_count = remaining; + WREG32(AVIVO_DC_I2C_STATUS1, (AVIVO_DC_I2C_DONE | + AVIVO_DC_I2C_NACK | + AVIVO_DC_I2C_HALT)); + WREG32(AVIVO_DC_I2C_RESET, AVIVO_DC_I2C_SOFT_RESET); + DRM_UDELAY(1); + WREG32(AVIVO_DC_I2C_RESET, 0); + + WREG32(AVIVO_DC_I2C_DATA, (p->slave << 1) & 0xff); + for (j = 0; j < current_count; j++) + WREG32(AVIVO_DC_I2C_DATA, p->buf[buffer_offset + j]); + + WREG32(AVIVO_DC_I2C_CONTROL3, AVIVO_DC_I2C_TIME_LIMIT(48)); + WREG32(AVIVO_DC_I2C_CONTROL2, (AVIVO_DC_I2C_ADDR_COUNT(1) | + AVIVO_DC_I2C_DATA_COUNT(current_count) | + (prescale << 16))); + WREG32(AVIVO_DC_I2C_CONTROL1, reg); + WREG32(AVIVO_DC_I2C_STATUS1, AVIVO_DC_I2C_GO); + for (j = 0; j < 200; j++) { + DRM_UDELAY(50); + tmp = RREG32(AVIVO_DC_I2C_STATUS1); + if (tmp & AVIVO_DC_I2C_GO) + continue; + tmp = RREG32(AVIVO_DC_I2C_STATUS1); + if (tmp & AVIVO_DC_I2C_DONE) + break; + else { + DRM_DEBUG("i2c write error 0x%08x\n", tmp); + WREG32(AVIVO_DC_I2C_RESET, AVIVO_DC_I2C_ABORT); + ret = EIO; + goto done; + } + } + remaining -= current_count; + buffer_offset += current_count; + } + } + } + +done: + WREG32(AVIVO_DC_I2C_STATUS1, (AVIVO_DC_I2C_DONE | + AVIVO_DC_I2C_NACK | + AVIVO_DC_I2C_HALT)); + WREG32(AVIVO_DC_I2C_RESET, AVIVO_DC_I2C_SOFT_RESET); + DRM_UDELAY(1); + WREG32(AVIVO_DC_I2C_RESET, 0); + + WREG32(AVIVO_DC_I2C_ARBITRATION, AVIVO_DC_I2C_SW_DONE_USING_I2C); + WREG32(AVIVO_DC_I2C_CONTROL1, saved1); + WREG32(0x494, saved2); + tmp = RREG32(RADEON_BIOS_6_SCRATCH); + tmp &= ~ATOM_S6_HW_I2C_BUSY_STATE; + WREG32(RADEON_BIOS_6_SCRATCH, tmp); + + sx_xunlock(&rdev->pm.mutex); + sx_xunlock(&rdev->dc_hw_i2c_mutex); + + return ret; +} + +static int radeon_hw_i2c_xfer(device_t dev, + struct iic_msg *msgs, uint32_t num) +{ + struct radeon_i2c_chan *i2c = device_get_softc(dev); + struct radeon_device *rdev = i2c->dev->dev_private; + struct radeon_i2c_bus_rec *rec = &i2c->rec; + int ret = 0; + + switch (rdev->family) { + case CHIP_R100: + case CHIP_RV100: + case CHIP_RS100: + case CHIP_RV200: + case CHIP_RS200: + case CHIP_R200: + case CHIP_RV250: + case CHIP_RS300: + case CHIP_RV280: + case CHIP_R300: + case CHIP_R350: + case CHIP_RV350: + case CHIP_RV380: + case CHIP_R420: + case CHIP_R423: + case CHIP_RV410: + case CHIP_RS400: + case CHIP_RS480: + ret = r100_hw_i2c_xfer(i2c, msgs, num); + break; + case CHIP_RS600: + case CHIP_RS690: + case CHIP_RS740: + /* XXX fill in hw i2c implementation */ + break; + case CHIP_RV515: + case CHIP_R520: + case CHIP_RV530: + case CHIP_RV560: + case CHIP_RV570: + case CHIP_R580: + if (rec->mm_i2c) + ret = r100_hw_i2c_xfer(i2c, msgs, num); + else + ret = r500_hw_i2c_xfer(i2c, msgs, num); + break; + case CHIP_R600: + case CHIP_RV610: + case CHIP_RV630: + case CHIP_RV670: + /* XXX fill in hw i2c implementation */ + break; + case CHIP_RV620: + case CHIP_RV635: + case CHIP_RS780: + case CHIP_RS880: + case CHIP_RV770: + case CHIP_RV730: + case CHIP_RV710: + case CHIP_RV740: + /* XXX fill in hw i2c implementation */ + break; + case CHIP_CEDAR: + case CHIP_REDWOOD: + case CHIP_JUNIPER: + case CHIP_CYPRESS: + case CHIP_HEMLOCK: + /* XXX fill in hw i2c implementation */ + break; + default: + DRM_ERROR("i2c: unhandled radeon chip\n"); + ret = EIO; + break; + } + + return ret; +} + +static int +radeon_hw_i2c_probe(device_t dev) +{ + + return (BUS_PROBE_SPECIFIC); +} + +static int +radeon_hw_i2c_attach(device_t dev) +{ + struct radeon_i2c_chan *i2c; + device_t iic_dev; + + i2c = device_get_softc(dev); + device_set_desc(dev, i2c->name); + + /* add generic bit-banging code */ + iic_dev = device_add_child(dev, "iicbus", -1); + if (iic_dev == NULL) + return (ENXIO); + device_quiet(iic_dev); + + /* attach and probe added child */ + bus_generic_attach(dev); + + return (0); +} + +static int +radeon_hw_i2c_detach(device_t dev) +{ + + /* detach bit-banding code. */ + bus_generic_detach(dev); + + /* delete bit-banding code. */ + device_delete_children(dev); + return (0); +} + +static int +radeon_hw_i2c_reset(device_t dev, u_char speed, u_char addr, u_char *oldaddr) +{ + + /* Not sure what to do here. */ + return 0; +} + + +static device_method_t radeon_hw_i2c_methods[] = { + DEVMETHOD(device_probe, radeon_hw_i2c_probe), + DEVMETHOD(device_attach, radeon_hw_i2c_attach), + DEVMETHOD(device_detach, radeon_hw_i2c_detach), + DEVMETHOD(iicbus_reset, radeon_hw_i2c_reset), + DEVMETHOD(iicbus_transfer, radeon_hw_i2c_xfer), + DEVMETHOD_END +}; + +static driver_t radeon_hw_i2c_driver = { + "radeon_hw_i2c", + radeon_hw_i2c_methods, + 0 /* softc will be allocated by parent */ +}; + +static devclass_t radeon_hw_i2c_devclass; +DRIVER_MODULE_ORDERED(radeon_hw_i2c, drm, radeon_hw_i2c_driver, + radeon_hw_i2c_devclass, 0, 0, SI_ORDER_FIRST); +DRIVER_MODULE(iicbus, radeon_hw_i2c, iicbus_driver, iicbus_devclass, 0, 0); + +struct radeon_i2c_chan *radeon_i2c_create(struct drm_device *dev, + struct radeon_i2c_bus_rec *rec, + const char *name) +{ + struct radeon_device *rdev = dev->dev_private; + struct radeon_i2c_chan *i2c; + device_t iicbus_dev; + int ret; + + /* don't add the mm_i2c bus unless hw_i2c is enabled */ + if (rec->mm_i2c && (radeon_hw_i2c == 0)) + return NULL; + + i2c = malloc(sizeof(struct radeon_i2c_chan), + DRM_MEM_DRIVER, M_ZERO | M_WAITOK); + if (i2c == NULL) + return NULL; + + /* + * Grab Giant before messing with newbus devices, just in case + * we do not hold it already. + */ + mtx_lock(&Giant); + + i2c->rec = *rec; + i2c->dev = dev; + if (rec->mm_i2c || + (rec->hw_capable && + radeon_hw_i2c && + ((rdev->family <= CHIP_RS480) || + ((rdev->family >= CHIP_RV515) && (rdev->family <= CHIP_R580))))) { + /* set the radeon hw i2c adapter */ + snprintf(i2c->name, sizeof(i2c->name), + "Radeon i2c hw bus %s", name); + iicbus_dev = device_add_child(dev->device, "radeon_hw_i2c", -1); + if (iicbus_dev == NULL) { + DRM_ERROR("Failed to create bridge for hw i2c %s\n", + name); + goto out_free; + } + device_quiet(iicbus_dev); + device_set_softc(iicbus_dev, i2c); + + ret = device_probe_and_attach(iicbus_dev); + if (ret != 0) { + DRM_ERROR("Attach failed for bridge for hw i2c %s\n", + name); + device_delete_child(dev->device, iicbus_dev); + goto out_free; + } + + i2c->adapter = device_find_child(iicbus_dev, "iicbus", -1); + if (i2c->adapter == NULL) { + DRM_ERROR("hw i2c bridge doesn't have iicbus child\n"); + device_delete_child(dev->device, iicbus_dev); + goto out_free; + } + } else if (rec->hw_capable && + radeon_hw_i2c && + ASIC_IS_DCE3(rdev)) { + /* hw i2c using atom */ + snprintf(i2c->name, sizeof(i2c->name), + "Radeon i2c hw bus %s", name); + iicbus_dev = device_add_child(dev->device, "radeon_atom_hw_i2c", -1); + if (iicbus_dev == NULL) { + DRM_ERROR("Failed to create bridge for hw i2c %s\n", + name); + goto out_free; + } + device_quiet(iicbus_dev); + device_set_softc(iicbus_dev, i2c); + + ret = device_probe_and_attach(iicbus_dev); + if (ret != 0) { + DRM_ERROR("Attach failed for bridge for hw i2c %s\n", + name); + device_delete_child(dev->device, iicbus_dev); + goto out_free; + } + + i2c->adapter = device_find_child(iicbus_dev, "iicbus", -1); + if (i2c->adapter == NULL) { + DRM_ERROR("hw i2c bridge doesn't have iicbus child\n"); + device_delete_child(dev->device, iicbus_dev); + goto out_free; + } + } else { + device_t iicbb_dev; + + /* set the radeon bit adapter */ + snprintf(i2c->name, sizeof(i2c->name), + "Radeon i2c bit bus %s", name); + iicbus_dev = device_add_child(dev->device, "radeon_iicbb", -1); + if (iicbus_dev == NULL) { + DRM_ERROR("Failed to create bridge for bb i2c %s\n", + name); + goto out_free; + } + device_quiet(iicbus_dev); + device_set_softc(iicbus_dev, i2c); + + ret = device_probe_and_attach(iicbus_dev); + if (ret != 0) { + DRM_ERROR("Attach failed for bridge for bb i2c %s\n", + name); + device_delete_child(dev->device, iicbus_dev); + goto out_free; + } + + iicbb_dev = device_find_child(iicbus_dev, "iicbb", -1); + if (iicbb_dev == NULL) { + DRM_ERROR("bb i2c bridge doesn't have iicbb child\n"); + device_delete_child(dev->device, iicbus_dev); + goto out_free; + } + + i2c->adapter = device_find_child(iicbb_dev, "iicbus", -1); + if (i2c->adapter == NULL) { + DRM_ERROR( + "bbbus bridge doesn't have iicbus grandchild\n"); + device_delete_child(dev->device, iicbus_dev); + goto out_free; + } + } + + i2c->iic_bus = iicbus_dev; + + mtx_unlock(&Giant); + + return i2c; +out_free: + mtx_unlock(&Giant); + free(i2c, DRM_MEM_DRIVER); + return NULL; + +} + +struct radeon_i2c_chan *radeon_i2c_create_dp(struct drm_device *dev, + struct radeon_i2c_bus_rec *rec, + const char *name) +{ + struct radeon_i2c_chan *i2c; + int ret; + + i2c = malloc(sizeof(struct radeon_i2c_chan), + DRM_MEM_DRIVER, M_ZERO | M_WAITOK); + if (i2c == NULL) + return NULL; + + i2c->rec = *rec; + i2c->dev = dev; + snprintf(i2c->name, sizeof(i2c->name), + "Radeon aux bus %s", name); + ret = iic_dp_aux_add_bus(dev->device, i2c->name, + radeon_dp_i2c_aux_ch, i2c, &i2c->iic_bus, + &i2c->adapter); + if (ret) { + DRM_INFO("Failed to register i2c %s\n", name); + goto out_free; + } + + return i2c; +out_free: + free(i2c, DRM_MEM_DRIVER); + return NULL; + +} + +void radeon_i2c_destroy(struct radeon_i2c_chan *i2c) +{ + if (!i2c) + return; + if (i2c->iic_bus != NULL) { + int ret; + + mtx_lock(&Giant); + ret = device_delete_child(i2c->dev->device, i2c->iic_bus); + mtx_unlock(&Giant); + KASSERT(ret == 0, ("unable to detach iic bus %s: %d", + i2c->name, ret)); + } + free(i2c, DRM_MEM_DRIVER); +} + +/* Add the default buses */ +void radeon_i2c_init(struct radeon_device *rdev) +{ + if (rdev->is_atom_bios) + radeon_atombios_i2c_init(rdev); + else + radeon_combios_i2c_init(rdev); +} + +/* remove all the buses */ +void radeon_i2c_fini(struct radeon_device *rdev) +{ + int i; + + for (i = 0; i < RADEON_MAX_I2C_BUS; i++) { + if (rdev->i2c_bus[i]) { + radeon_i2c_destroy(rdev->i2c_bus[i]); + rdev->i2c_bus[i] = NULL; + } + } +} + +/* Add additional buses */ +void radeon_i2c_add(struct radeon_device *rdev, + struct radeon_i2c_bus_rec *rec, + const char *name) +{ + struct drm_device *dev = rdev->ddev; + int i; + + for (i = 0; i < RADEON_MAX_I2C_BUS; i++) { + if (!rdev->i2c_bus[i]) { + rdev->i2c_bus[i] = radeon_i2c_create(dev, rec, name); + return; + } + } +} + +/* looks up bus based on id */ +struct radeon_i2c_chan *radeon_i2c_lookup(struct radeon_device *rdev, + struct radeon_i2c_bus_rec *i2c_bus) +{ + int i; + + for (i = 0; i < RADEON_MAX_I2C_BUS; i++) { + if (rdev->i2c_bus[i] && + (rdev->i2c_bus[i]->rec.i2c_id == i2c_bus->i2c_id)) { + return rdev->i2c_bus[i]; + } + } + return NULL; +} + +struct drm_encoder *radeon_best_encoder(struct drm_connector *connector) +{ + return NULL; +} + +void radeon_i2c_get_byte(struct radeon_i2c_chan *i2c_bus, + u8 slave_addr, + u8 addr, + u8 *val) +{ + u8 out_buf[2]; + u8 in_buf[2]; + struct iic_msg msgs[] = { + { + .slave = slave_addr << 1, + .flags = 0, + .len = 1, + .buf = out_buf, + }, + { + .slave = slave_addr << 1, + .flags = IIC_M_RD, + .len = 1, + .buf = in_buf, + } + }; + + out_buf[0] = addr; + out_buf[1] = 0; + + if (iicbus_transfer(i2c_bus->adapter, msgs, 2) == 0) { + *val = in_buf[0]; + DRM_DEBUG("val = 0x%02x\n", *val); + } else { + DRM_DEBUG("i2c 0x%02x 0x%02x read failed\n", + addr, *val); + } +} + +void radeon_i2c_put_byte(struct radeon_i2c_chan *i2c_bus, + u8 slave_addr, + u8 addr, + u8 val) +{ + uint8_t out_buf[2]; + struct iic_msg msg = { + .slave = slave_addr << 1, + .flags = 0, + .len = 2, + .buf = out_buf, + }; + + out_buf[0] = addr; + out_buf[1] = val; + + if (iicbus_transfer(i2c_bus->adapter, &msg, 1) != 0) + DRM_DEBUG("i2c 0x%02x 0x%02x write failed\n", + addr, val); +} + +/* ddc router switching */ +void radeon_router_select_ddc_port(struct radeon_connector *radeon_connector) +{ + u8 val; + + if (!radeon_connector->router.ddc_valid) + return; + + if (!radeon_connector->router_bus) + return; + + radeon_i2c_get_byte(radeon_connector->router_bus, + radeon_connector->router.i2c_addr, + 0x3, &val); + val &= ~radeon_connector->router.ddc_mux_control_pin; + radeon_i2c_put_byte(radeon_connector->router_bus, + radeon_connector->router.i2c_addr, + 0x3, val); + radeon_i2c_get_byte(radeon_connector->router_bus, + radeon_connector->router.i2c_addr, + 0x1, &val); + val &= ~radeon_connector->router.ddc_mux_control_pin; + val |= radeon_connector->router.ddc_mux_state; + radeon_i2c_put_byte(radeon_connector->router_bus, + radeon_connector->router.i2c_addr, + 0x1, val); +} + +/* clock/data router switching */ +void radeon_router_select_cd_port(struct radeon_connector *radeon_connector) +{ + u8 val; + + if (!radeon_connector->router.cd_valid) + return; + + if (!radeon_connector->router_bus) + return; + + radeon_i2c_get_byte(radeon_connector->router_bus, + radeon_connector->router.i2c_addr, + 0x3, &val); + val &= ~radeon_connector->router.cd_mux_control_pin; + radeon_i2c_put_byte(radeon_connector->router_bus, + radeon_connector->router.i2c_addr, + 0x3, val); + radeon_i2c_get_byte(radeon_connector->router_bus, + radeon_connector->router.i2c_addr, + 0x1, &val); + val &= ~radeon_connector->router.cd_mux_control_pin; + val |= radeon_connector->router.cd_mux_state; + radeon_i2c_put_byte(radeon_connector->router_bus, + radeon_connector->router.i2c_addr, + 0x1, val); +} + diff --git a/sys/dev/drm2/radeon/radeon_ioc32.c b/sys/dev/drm2/radeon/radeon_ioc32.c new file mode 100644 index 00000000000..361d48cb3f4 --- /dev/null +++ b/sys/dev/drm2/radeon/radeon_ioc32.c @@ -0,0 +1,428 @@ +/** + * \file radeon_ioc32.c + * + * 32-bit ioctl compatibility routines for the Radeon DRM. + * + * \author Paul Mackerras + * + * Copyright (C) Paul Mackerras 2005 + * All Rights Reserved. + * + * 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 (including the next + * paragraph) 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 AUTHOR 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 +__FBSDID("$FreeBSD$"); + +#include + +#include +#include +#include "radeon_drv.h" + +typedef struct drm_radeon_init32 { + int func; + u32 sarea_priv_offset; + int is_pci; + int cp_mode; + int gart_size; + int ring_size; + int usec_timeout; + + unsigned int fb_bpp; + unsigned int front_offset, front_pitch; + unsigned int back_offset, back_pitch; + unsigned int depth_bpp; + unsigned int depth_offset, depth_pitch; + + u32 fb_offset; + u32 mmio_offset; + u32 ring_offset; + u32 ring_rptr_offset; + u32 buffers_offset; + u32 gart_textures_offset; +} drm_radeon_init32_t; + +static int compat_radeon_cp_init(struct file *file, unsigned int cmd, + unsigned long arg) +{ + drm_radeon_init32_t init32; + drm_radeon_init_t __user *init; + + if (copy_from_user(&init32, (void __user *)arg, sizeof(init32))) + return -EFAULT; + + init = compat_alloc_user_space(sizeof(*init)); + if (!access_ok(VERIFY_WRITE, init, sizeof(*init)) + || __put_user(init32.func, &init->func) + || __put_user(init32.sarea_priv_offset, &init->sarea_priv_offset) + || __put_user(init32.is_pci, &init->is_pci) + || __put_user(init32.cp_mode, &init->cp_mode) + || __put_user(init32.gart_size, &init->gart_size) + || __put_user(init32.ring_size, &init->ring_size) + || __put_user(init32.usec_timeout, &init->usec_timeout) + || __put_user(init32.fb_bpp, &init->fb_bpp) + || __put_user(init32.front_offset, &init->front_offset) + || __put_user(init32.front_pitch, &init->front_pitch) + || __put_user(init32.back_offset, &init->back_offset) + || __put_user(init32.back_pitch, &init->back_pitch) + || __put_user(init32.depth_bpp, &init->depth_bpp) + || __put_user(init32.depth_offset, &init->depth_offset) + || __put_user(init32.depth_pitch, &init->depth_pitch) + || __put_user(init32.fb_offset, &init->fb_offset) + || __put_user(init32.mmio_offset, &init->mmio_offset) + || __put_user(init32.ring_offset, &init->ring_offset) + || __put_user(init32.ring_rptr_offset, &init->ring_rptr_offset) + || __put_user(init32.buffers_offset, &init->buffers_offset) + || __put_user(init32.gart_textures_offset, + &init->gart_textures_offset)) + return -EFAULT; + + return drm_ioctl(file, DRM_IOCTL_RADEON_CP_INIT, (unsigned long)init); +} + +typedef struct drm_radeon_clear32 { + unsigned int flags; + unsigned int clear_color; + unsigned int clear_depth; + unsigned int color_mask; + unsigned int depth_mask; /* misnamed field: should be stencil */ + u32 depth_boxes; +} drm_radeon_clear32_t; + +static int compat_radeon_cp_clear(struct file *file, unsigned int cmd, + unsigned long arg) +{ + drm_radeon_clear32_t clr32; + drm_radeon_clear_t __user *clr; + + if (copy_from_user(&clr32, (void __user *)arg, sizeof(clr32))) + return -EFAULT; + + clr = compat_alloc_user_space(sizeof(*clr)); + if (!access_ok(VERIFY_WRITE, clr, sizeof(*clr)) + || __put_user(clr32.flags, &clr->flags) + || __put_user(clr32.clear_color, &clr->clear_color) + || __put_user(clr32.clear_depth, &clr->clear_depth) + || __put_user(clr32.color_mask, &clr->color_mask) + || __put_user(clr32.depth_mask, &clr->depth_mask) + || __put_user((void __user *)(unsigned long)clr32.depth_boxes, + &clr->depth_boxes)) + return -EFAULT; + + return drm_ioctl(file, DRM_IOCTL_RADEON_CLEAR, (unsigned long)clr); +} + +typedef struct drm_radeon_stipple32 { + u32 mask; +} drm_radeon_stipple32_t; + +static int compat_radeon_cp_stipple(struct file *file, unsigned int cmd, + unsigned long arg) +{ + drm_radeon_stipple32_t __user *argp = (void __user *)arg; + drm_radeon_stipple_t __user *request; + u32 mask; + + if (get_user(mask, &argp->mask)) + return -EFAULT; + + request = compat_alloc_user_space(sizeof(*request)); + if (!access_ok(VERIFY_WRITE, request, sizeof(*request)) + || __put_user((unsigned int __user *)(unsigned long)mask, + &request->mask)) + return -EFAULT; + + return drm_ioctl(file, DRM_IOCTL_RADEON_STIPPLE, (unsigned long)request); +} + +typedef struct drm_radeon_tex_image32 { + unsigned int x, y; /* Blit coordinates */ + unsigned int width, height; + u32 data; +} drm_radeon_tex_image32_t; + +typedef struct drm_radeon_texture32 { + unsigned int offset; + int pitch; + int format; + int width; /* Texture image coordinates */ + int height; + u32 image; +} drm_radeon_texture32_t; + +static int compat_radeon_cp_texture(struct file *file, unsigned int cmd, + unsigned long arg) +{ + drm_radeon_texture32_t req32; + drm_radeon_texture_t __user *request; + drm_radeon_tex_image32_t img32; + drm_radeon_tex_image_t __user *image; + + if (copy_from_user(&req32, (void __user *)arg, sizeof(req32))) + return -EFAULT; + if (req32.image == 0) + return -EINVAL; + if (copy_from_user(&img32, (void __user *)(unsigned long)req32.image, + sizeof(img32))) + return -EFAULT; + + request = compat_alloc_user_space(sizeof(*request) + sizeof(*image)); + if (!access_ok(VERIFY_WRITE, request, + sizeof(*request) + sizeof(*image))) + return -EFAULT; + image = (drm_radeon_tex_image_t __user *) (request + 1); + + if (__put_user(req32.offset, &request->offset) + || __put_user(req32.pitch, &request->pitch) + || __put_user(req32.format, &request->format) + || __put_user(req32.width, &request->width) + || __put_user(req32.height, &request->height) + || __put_user(image, &request->image) + || __put_user(img32.x, &image->x) + || __put_user(img32.y, &image->y) + || __put_user(img32.width, &image->width) + || __put_user(img32.height, &image->height) + || __put_user((const void __user *)(unsigned long)img32.data, + &image->data)) + return -EFAULT; + + return drm_ioctl(file, DRM_IOCTL_RADEON_TEXTURE, (unsigned long)request); +} + +typedef struct drm_radeon_vertex2_32 { + int idx; /* Index of vertex buffer */ + int discard; /* Client finished with buffer? */ + int nr_states; + u32 state; + int nr_prims; + u32 prim; +} drm_radeon_vertex2_32_t; + +static int compat_radeon_cp_vertex2(struct file *file, unsigned int cmd, + unsigned long arg) +{ + drm_radeon_vertex2_32_t req32; + drm_radeon_vertex2_t __user *request; + + if (copy_from_user(&req32, (void __user *)arg, sizeof(req32))) + return -EFAULT; + + request = compat_alloc_user_space(sizeof(*request)); + if (!access_ok(VERIFY_WRITE, request, sizeof(*request)) + || __put_user(req32.idx, &request->idx) + || __put_user(req32.discard, &request->discard) + || __put_user(req32.nr_states, &request->nr_states) + || __put_user((void __user *)(unsigned long)req32.state, + &request->state) + || __put_user(req32.nr_prims, &request->nr_prims) + || __put_user((void __user *)(unsigned long)req32.prim, + &request->prim)) + return -EFAULT; + + return drm_ioctl(file, DRM_IOCTL_RADEON_VERTEX2, (unsigned long)request); +} + +typedef struct drm_radeon_cmd_buffer32 { + int bufsz; + u32 buf; + int nbox; + u32 boxes; +} drm_radeon_cmd_buffer32_t; + +static int compat_radeon_cp_cmdbuf(struct file *file, unsigned int cmd, + unsigned long arg) +{ + drm_radeon_cmd_buffer32_t req32; + drm_radeon_cmd_buffer_t __user *request; + + if (copy_from_user(&req32, (void __user *)arg, sizeof(req32))) + return -EFAULT; + + request = compat_alloc_user_space(sizeof(*request)); + if (!access_ok(VERIFY_WRITE, request, sizeof(*request)) + || __put_user(req32.bufsz, &request->bufsz) + || __put_user((void __user *)(unsigned long)req32.buf, + &request->buf) + || __put_user(req32.nbox, &request->nbox) + || __put_user((void __user *)(unsigned long)req32.boxes, + &request->boxes)) + return -EFAULT; + + return drm_ioctl(file, DRM_IOCTL_RADEON_CMDBUF, (unsigned long)request); +} + +typedef struct drm_radeon_getparam32 { + int param; + u32 value; +} drm_radeon_getparam32_t; + +static int compat_radeon_cp_getparam(struct file *file, unsigned int cmd, + unsigned long arg) +{ + drm_radeon_getparam32_t req32; + drm_radeon_getparam_t __user *request; + + if (copy_from_user(&req32, (void __user *)arg, sizeof(req32))) + return -EFAULT; + + request = compat_alloc_user_space(sizeof(*request)); + if (!access_ok(VERIFY_WRITE, request, sizeof(*request)) + || __put_user(req32.param, &request->param) + || __put_user((void __user *)(unsigned long)req32.value, + &request->value)) + return -EFAULT; + + return drm_ioctl(file, DRM_IOCTL_RADEON_GETPARAM, (unsigned long)request); +} + +typedef struct drm_radeon_mem_alloc32 { + int region; + int alignment; + int size; + u32 region_offset; /* offset from start of fb or GART */ +} drm_radeon_mem_alloc32_t; + +static int compat_radeon_mem_alloc(struct file *file, unsigned int cmd, + unsigned long arg) +{ + drm_radeon_mem_alloc32_t req32; + drm_radeon_mem_alloc_t __user *request; + + if (copy_from_user(&req32, (void __user *)arg, sizeof(req32))) + return -EFAULT; + + request = compat_alloc_user_space(sizeof(*request)); + if (!access_ok(VERIFY_WRITE, request, sizeof(*request)) + || __put_user(req32.region, &request->region) + || __put_user(req32.alignment, &request->alignment) + || __put_user(req32.size, &request->size) + || __put_user((int __user *)(unsigned long)req32.region_offset, + &request->region_offset)) + return -EFAULT; + + return drm_ioctl(file, DRM_IOCTL_RADEON_ALLOC, (unsigned long)request); +} + +typedef struct drm_radeon_irq_emit32 { + u32 irq_seq; +} drm_radeon_irq_emit32_t; + +static int compat_radeon_irq_emit(struct file *file, unsigned int cmd, + unsigned long arg) +{ + drm_radeon_irq_emit32_t req32; + drm_radeon_irq_emit_t __user *request; + + if (copy_from_user(&req32, (void __user *)arg, sizeof(req32))) + return -EFAULT; + + request = compat_alloc_user_space(sizeof(*request)); + if (!access_ok(VERIFY_WRITE, request, sizeof(*request)) + || __put_user((int __user *)(unsigned long)req32.irq_seq, + &request->irq_seq)) + return -EFAULT; + + return drm_ioctl(file, DRM_IOCTL_RADEON_IRQ_EMIT, (unsigned long)request); +} + +/* The two 64-bit arches where alignof(u64)==4 in 32-bit code */ +#if defined (CONFIG_X86_64) || defined(CONFIG_IA64) +typedef struct drm_radeon_setparam32 { + int param; + u64 value; +} __attribute__((packed)) drm_radeon_setparam32_t; + +static int compat_radeon_cp_setparam(struct file *file, unsigned int cmd, + unsigned long arg) +{ + drm_radeon_setparam32_t req32; + drm_radeon_setparam_t __user *request; + + if (copy_from_user(&req32, (void __user *) arg, sizeof(req32))) + return -EFAULT; + + request = compat_alloc_user_space(sizeof(*request)); + if (!access_ok(VERIFY_WRITE, request, sizeof(*request)) + || __put_user(req32.param, &request->param) + || __put_user((void __user *)(unsigned long)req32.value, + &request->value)) + return -EFAULT; + + return drm_ioctl(file, DRM_IOCTL_RADEON_SETPARAM, (unsigned long) request); +} +#else +#define compat_radeon_cp_setparam NULL +#endif /* X86_64 || IA64 */ + +static drm_ioctl_compat_t *radeon_compat_ioctls[] = { + [DRM_RADEON_CP_INIT] = compat_radeon_cp_init, + [DRM_RADEON_CLEAR] = compat_radeon_cp_clear, + [DRM_RADEON_STIPPLE] = compat_radeon_cp_stipple, + [DRM_RADEON_TEXTURE] = compat_radeon_cp_texture, + [DRM_RADEON_VERTEX2] = compat_radeon_cp_vertex2, + [DRM_RADEON_CMDBUF] = compat_radeon_cp_cmdbuf, + [DRM_RADEON_GETPARAM] = compat_radeon_cp_getparam, + [DRM_RADEON_SETPARAM] = compat_radeon_cp_setparam, + [DRM_RADEON_ALLOC] = compat_radeon_mem_alloc, + [DRM_RADEON_IRQ_EMIT] = compat_radeon_irq_emit, +}; + +/** + * Called whenever a 32-bit process running under a 64-bit kernel + * performs an ioctl on /dev/dri/card. + * + * \param filp file pointer. + * \param cmd command. + * \param arg user argument. + * \return zero on success or negative number on failure. + */ +long radeon_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) +{ + unsigned int nr = DRM_IOCTL_NR(cmd); + drm_ioctl_compat_t *fn = NULL; + int ret; + + if (nr < DRM_COMMAND_BASE) + return drm_compat_ioctl(filp, cmd, arg); + + if (nr < DRM_COMMAND_BASE + DRM_ARRAY_SIZE(radeon_compat_ioctls)) + fn = radeon_compat_ioctls[nr - DRM_COMMAND_BASE]; + + if (fn != NULL) + ret = (*fn) (filp, cmd, arg); + else + ret = drm_ioctl(filp, cmd, arg); + + return ret; +} + +long radeon_kms_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) +{ + unsigned int nr = DRM_IOCTL_NR(cmd); + int ret; + + if (nr < DRM_COMMAND_BASE) + return drm_compat_ioctl(filp, cmd, arg); + + ret = drm_ioctl(filp, cmd, arg); + + return ret; +} diff --git a/sys/dev/drm2/radeon/radeon_irq.c b/sys/dev/drm2/radeon/radeon_irq.c new file mode 100644 index 00000000000..11e66e197b5 --- /dev/null +++ b/sys/dev/drm2/radeon/radeon_irq.c @@ -0,0 +1,403 @@ +/* radeon_irq.c -- IRQ handling for radeon -*- linux-c -*- */ +/* + * Copyright (C) The Weather Channel, Inc. 2002. All Rights Reserved. + * + * The Weather Channel (TM) funded Tungsten Graphics to develop the + * initial release of the Radeon 8500 driver under the XFree86 license. + * This notice must be preserved. + * + * 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 (including the next + * paragraph) 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 + * PRECISION INSIGHT AND/OR ITS SUPPLIERS 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. + * + * Authors: + * Keith Whitwell + * Michel D�zer + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include "radeon_drv.h" + +void radeon_irq_set_state(struct drm_device *dev, u32 mask, int state) +{ + drm_radeon_private_t *dev_priv = dev->dev_private; + + if (state) + dev_priv->irq_enable_reg |= mask; + else + dev_priv->irq_enable_reg &= ~mask; + + if (dev->irq_enabled) + RADEON_WRITE(RADEON_GEN_INT_CNTL, dev_priv->irq_enable_reg); +} + +static void r500_vbl_irq_set_state(struct drm_device *dev, u32 mask, int state) +{ + drm_radeon_private_t *dev_priv = dev->dev_private; + + if (state) + dev_priv->r500_disp_irq_reg |= mask; + else + dev_priv->r500_disp_irq_reg &= ~mask; + + if (dev->irq_enabled) + RADEON_WRITE(R500_DxMODE_INT_MASK, dev_priv->r500_disp_irq_reg); +} + +int radeon_enable_vblank(struct drm_device *dev, int crtc) +{ + drm_radeon_private_t *dev_priv = dev->dev_private; + + if ((dev_priv->flags & RADEON_FAMILY_MASK) >= CHIP_RS600) { + switch (crtc) { + case 0: + r500_vbl_irq_set_state(dev, R500_D1MODE_INT_MASK, 1); + break; + case 1: + r500_vbl_irq_set_state(dev, R500_D2MODE_INT_MASK, 1); + break; + default: + DRM_ERROR("tried to enable vblank on non-existent crtc %d\n", + crtc); + return -EINVAL; + } + } else { + switch (crtc) { + case 0: + radeon_irq_set_state(dev, RADEON_CRTC_VBLANK_MASK, 1); + break; + case 1: + radeon_irq_set_state(dev, RADEON_CRTC2_VBLANK_MASK, 1); + break; + default: + DRM_ERROR("tried to enable vblank on non-existent crtc %d\n", + crtc); + return -EINVAL; + } + } + + return 0; +} + +void radeon_disable_vblank(struct drm_device *dev, int crtc) +{ + drm_radeon_private_t *dev_priv = dev->dev_private; + + if ((dev_priv->flags & RADEON_FAMILY_MASK) >= CHIP_RS600) { + switch (crtc) { + case 0: + r500_vbl_irq_set_state(dev, R500_D1MODE_INT_MASK, 0); + break; + case 1: + r500_vbl_irq_set_state(dev, R500_D2MODE_INT_MASK, 0); + break; + default: + DRM_ERROR("tried to enable vblank on non-existent crtc %d\n", + crtc); + break; + } + } else { + switch (crtc) { + case 0: + radeon_irq_set_state(dev, RADEON_CRTC_VBLANK_MASK, 0); + break; + case 1: + radeon_irq_set_state(dev, RADEON_CRTC2_VBLANK_MASK, 0); + break; + default: + DRM_ERROR("tried to enable vblank on non-existent crtc %d\n", + crtc); + break; + } + } +} + +static u32 radeon_acknowledge_irqs(drm_radeon_private_t *dev_priv, u32 *r500_disp_int) +{ + u32 irqs = RADEON_READ(RADEON_GEN_INT_STATUS); + u32 irq_mask = RADEON_SW_INT_TEST; + + *r500_disp_int = 0; + if ((dev_priv->flags & RADEON_FAMILY_MASK) >= CHIP_RS600) { + /* vbl interrupts in a different place */ + + if (irqs & R500_DISPLAY_INT_STATUS) { + /* if a display interrupt */ + u32 disp_irq; + + disp_irq = RADEON_READ(R500_DISP_INTERRUPT_STATUS); + + *r500_disp_int = disp_irq; + if (disp_irq & R500_D1_VBLANK_INTERRUPT) + RADEON_WRITE(R500_D1MODE_VBLANK_STATUS, R500_VBLANK_ACK); + if (disp_irq & R500_D2_VBLANK_INTERRUPT) + RADEON_WRITE(R500_D2MODE_VBLANK_STATUS, R500_VBLANK_ACK); + } + irq_mask |= R500_DISPLAY_INT_STATUS; + } else + irq_mask |= RADEON_CRTC_VBLANK_STAT | RADEON_CRTC2_VBLANK_STAT; + + irqs &= irq_mask; + + if (irqs) + RADEON_WRITE(RADEON_GEN_INT_STATUS, irqs); + + return irqs; +} + +/* Interrupts - Used for device synchronization and flushing in the + * following circumstances: + * + * - Exclusive FB access with hw idle: + * - Wait for GUI Idle (?) interrupt, then do normal flush. + * + * - Frame throttling, NV_fence: + * - Drop marker irq's into command stream ahead of time. + * - Wait on irq's with lock *not held* + * - Check each for termination condition + * + * - Internally in cp_getbuffer, etc: + * - as above, but wait with lock held??? + * + * NOTE: These functions are misleadingly named -- the irq's aren't + * tied to dma at all, this is just a hangover from dri prehistory. + */ + +irqreturn_t radeon_driver_irq_handler(DRM_IRQ_ARGS) +{ + struct drm_device *dev = (struct drm_device *) arg; + drm_radeon_private_t *dev_priv = + (drm_radeon_private_t *) dev->dev_private; + u32 stat; + u32 r500_disp_int; + + if ((dev_priv->flags & RADEON_FAMILY_MASK) >= CHIP_R600) + return IRQ_NONE; + + /* Only consider the bits we're interested in - others could be used + * outside the DRM + */ + stat = radeon_acknowledge_irqs(dev_priv, &r500_disp_int); + if (!stat) + return IRQ_NONE; + + stat &= dev_priv->irq_enable_reg; + + /* SW interrupt */ + if (stat & RADEON_SW_INT_TEST) + DRM_WAKEUP(&dev_priv->swi_queue); + + /* VBLANK interrupt */ + if ((dev_priv->flags & RADEON_FAMILY_MASK) >= CHIP_RS600) { + if (r500_disp_int & R500_D1_VBLANK_INTERRUPT) + drm_handle_vblank(dev, 0); + if (r500_disp_int & R500_D2_VBLANK_INTERRUPT) + drm_handle_vblank(dev, 1); + } else { + if (stat & RADEON_CRTC_VBLANK_STAT) + drm_handle_vblank(dev, 0); + if (stat & RADEON_CRTC2_VBLANK_STAT) + drm_handle_vblank(dev, 1); + } + return IRQ_HANDLED; +} + +static int radeon_emit_irq(struct drm_device * dev) +{ + drm_radeon_private_t *dev_priv = dev->dev_private; + unsigned int ret; + RING_LOCALS; + + atomic_inc(&dev_priv->swi_emitted); + ret = atomic_read(&dev_priv->swi_emitted); + + BEGIN_RING(4); + OUT_RING_REG(RADEON_LAST_SWI_REG, ret); + OUT_RING_REG(RADEON_GEN_INT_STATUS, RADEON_SW_INT_FIRE); + ADVANCE_RING(); + COMMIT_RING(); + + return ret; +} + +static int radeon_wait_irq(struct drm_device * dev, int swi_nr) +{ + drm_radeon_private_t *dev_priv = + (drm_radeon_private_t *) dev->dev_private; + int ret = 0; + + if (RADEON_READ(RADEON_LAST_SWI_REG) >= swi_nr) + return 0; + + dev_priv->stats.boxes |= RADEON_BOX_WAIT_IDLE; + + DRM_WAIT_ON(ret, dev_priv->swi_queue, 3 * DRM_HZ, + RADEON_READ(RADEON_LAST_SWI_REG) >= swi_nr); + + return ret; +} + +u32 radeon_get_vblank_counter(struct drm_device *dev, int crtc) +{ + drm_radeon_private_t *dev_priv = dev->dev_private; + + if (!dev_priv) { + DRM_ERROR("called with no initialization\n"); + return -EINVAL; + } + + if (crtc < 0 || crtc > 1) { + DRM_ERROR("Invalid crtc %d\n", crtc); + return -EINVAL; + } + + if ((dev_priv->flags & RADEON_FAMILY_MASK) >= CHIP_RS600) { + if (crtc == 0) + return RADEON_READ(R500_D1CRTC_FRAME_COUNT); + else + return RADEON_READ(R500_D2CRTC_FRAME_COUNT); + } else { + if (crtc == 0) + return RADEON_READ(RADEON_CRTC_CRNT_FRAME); + else + return RADEON_READ(RADEON_CRTC2_CRNT_FRAME); + } +} + +/* Needs the lock as it touches the ring. + */ +int radeon_irq_emit(struct drm_device *dev, void *data, struct drm_file *file_priv) +{ + drm_radeon_private_t *dev_priv = dev->dev_private; + drm_radeon_irq_emit_t *emit = data; + int result; + + if (!dev_priv) { + DRM_ERROR("called with no initialization\n"); + return -EINVAL; + } + + if ((dev_priv->flags & RADEON_FAMILY_MASK) >= CHIP_R600) + return -EINVAL; + + LOCK_TEST_WITH_RETURN(dev, file_priv); + + result = radeon_emit_irq(dev); + + if (DRM_COPY_TO_USER(emit->irq_seq, &result, sizeof(int))) { + DRM_ERROR("copy_to_user\n"); + return -EFAULT; + } + + return 0; +} + +/* Doesn't need the hardware lock. + */ +int radeon_irq_wait(struct drm_device *dev, void *data, struct drm_file *file_priv) +{ + drm_radeon_private_t *dev_priv = dev->dev_private; + drm_radeon_irq_wait_t *irqwait = data; + + if (!dev_priv) { + DRM_ERROR("called with no initialization\n"); + return -EINVAL; + } + + if ((dev_priv->flags & RADEON_FAMILY_MASK) >= CHIP_R600) + return -EINVAL; + + return radeon_wait_irq(dev, irqwait->irq_seq); +} + +/* drm_dma.h hooks +*/ +void radeon_driver_irq_preinstall(struct drm_device * dev) +{ + drm_radeon_private_t *dev_priv = + (drm_radeon_private_t *) dev->dev_private; + u32 dummy; + + if ((dev_priv->flags & RADEON_FAMILY_MASK) >= CHIP_R600) + return; + + /* Disable *all* interrupts */ + if ((dev_priv->flags & RADEON_FAMILY_MASK) >= CHIP_RS600) + RADEON_WRITE(R500_DxMODE_INT_MASK, 0); + RADEON_WRITE(RADEON_GEN_INT_CNTL, 0); + + /* Clear bits if they're already high */ + radeon_acknowledge_irqs(dev_priv, &dummy); +} + +int radeon_driver_irq_postinstall(struct drm_device *dev) +{ + drm_radeon_private_t *dev_priv = + (drm_radeon_private_t *) dev->dev_private; + + atomic_set(&dev_priv->swi_emitted, 0); + DRM_INIT_WAITQUEUE(&dev_priv->swi_queue); + + dev->max_vblank_count = 0x001fffff; + + if ((dev_priv->flags & RADEON_FAMILY_MASK) >= CHIP_R600) + return 0; + + radeon_irq_set_state(dev, RADEON_SW_INT_ENABLE, 1); + + return 0; +} + +void radeon_driver_irq_uninstall(struct drm_device * dev) +{ + drm_radeon_private_t *dev_priv = + (drm_radeon_private_t *) dev->dev_private; + if (!dev_priv) + return; + + if ((dev_priv->flags & RADEON_FAMILY_MASK) >= CHIP_R600) + return; + + if ((dev_priv->flags & RADEON_FAMILY_MASK) >= CHIP_RS600) + RADEON_WRITE(R500_DxMODE_INT_MASK, 0); + /* Disable *all* interrupts */ + RADEON_WRITE(RADEON_GEN_INT_CNTL, 0); +} + + +int radeon_vblank_crtc_get(struct drm_device *dev) +{ + drm_radeon_private_t *dev_priv = (drm_radeon_private_t *) dev->dev_private; + + return dev_priv->vblank_crtc; +} + +int radeon_vblank_crtc_set(struct drm_device *dev, int64_t value) +{ + drm_radeon_private_t *dev_priv = (drm_radeon_private_t *) dev->dev_private; + if (value & ~(DRM_RADEON_VBLANK_CRTC1 | DRM_RADEON_VBLANK_CRTC2)) { + DRM_ERROR("called with invalid crtc 0x%x\n", (unsigned int)value); + return -EINVAL; + } + dev_priv->vblank_crtc = (unsigned int)value; + return 0; +} diff --git a/sys/dev/drm2/radeon/radeon_irq_kms.c b/sys/dev/drm2/radeon/radeon_irq_kms.c new file mode 100644 index 00000000000..a679ed192e0 --- /dev/null +++ b/sys/dev/drm2/radeon/radeon_irq_kms.c @@ -0,0 +1,468 @@ +/* + * Copyright 2008 Advanced Micro Devices, Inc. + * Copyright 2008 Red Hat Inc. + * Copyright 2009 Jerome Glisse. + * + * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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. + * + * Authors: Dave Airlie + * Alex Deucher + * Jerome Glisse + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include "radeon_reg.h" +#include "radeon_irq_kms.h" +#include "radeon.h" +#include "atom.h" + +#define RADEON_WAIT_IDLE_TIMEOUT 200 + +/** + * radeon_driver_irq_handler_kms - irq handler for KMS + * + * @DRM_IRQ_ARGS: args + * + * This is the irq handler for the radeon KMS driver (all asics). + * radeon_irq_process is a macro that points to the per-asic + * irq handler callback. + */ +irqreturn_t radeon_driver_irq_handler_kms(DRM_IRQ_ARGS) +{ + struct drm_device *dev = (struct drm_device *) arg; + struct radeon_device *rdev = dev->dev_private; + + return radeon_irq_process(rdev); +} + +/* + * Handle hotplug events outside the interrupt handler proper. + */ +/** + * radeon_hotplug_work_func - display hotplug work handler + * + * @work: work struct + * + * This is the hot plug event work handler (all asics). + * The work gets scheduled from the irq handler if there + * was a hot plug interrupt. It walks the connector table + * and calls the hotplug handler for each one, then sends + * a drm hotplug event to alert userspace. + */ +static void radeon_hotplug_work_func(void *arg, int pending) +{ + struct radeon_device *rdev = arg; + struct drm_device *dev = rdev->ddev; + struct drm_mode_config *mode_config = &dev->mode_config; + struct drm_connector *connector; + + if (mode_config->num_connector) { + list_for_each_entry(connector, &mode_config->connector_list, head) + radeon_connector_hotplug(connector); + } + /* Just fire off a uevent and let userspace tell us what to do */ + drm_helper_hpd_irq_event(dev); +} + +/** + * radeon_driver_irq_preinstall_kms - drm irq preinstall callback + * + * @dev: drm dev pointer + * + * Gets the hw ready to enable irqs (all asics). + * This function disables all interrupt sources on the GPU. + */ +void radeon_driver_irq_preinstall_kms(struct drm_device *dev) +{ + struct radeon_device *rdev = dev->dev_private; + unsigned long irqflags; + unsigned i; + + DRM_SPINLOCK_IRQSAVE(&rdev->irq.lock, irqflags); + /* Disable *all* interrupts */ + for (i = 0; i < RADEON_NUM_RINGS; i++) + atomic_set(&rdev->irq.ring_int[i], 0); + for (i = 0; i < RADEON_MAX_HPD_PINS; i++) + rdev->irq.hpd[i] = false; + for (i = 0; i < RADEON_MAX_CRTCS; i++) { + rdev->irq.crtc_vblank_int[i] = false; + atomic_set(&rdev->irq.pflip[i], 0); + rdev->irq.afmt[i] = false; + } + radeon_irq_set(rdev); + DRM_SPINUNLOCK_IRQRESTORE(&rdev->irq.lock, irqflags); + /* Clear bits */ + radeon_irq_process(rdev); +} + +/** + * radeon_driver_irq_postinstall_kms - drm irq preinstall callback + * + * @dev: drm dev pointer + * + * Handles stuff to be done after enabling irqs (all asics). + * Returns 0 on success. + */ +int radeon_driver_irq_postinstall_kms(struct drm_device *dev) +{ + dev->max_vblank_count = 0x001fffff; + return 0; +} + +/** + * radeon_driver_irq_uninstall_kms - drm irq uninstall callback + * + * @dev: drm dev pointer + * + * This function disables all interrupt sources on the GPU (all asics). + */ +void radeon_driver_irq_uninstall_kms(struct drm_device *dev) +{ + struct radeon_device *rdev = dev->dev_private; + unsigned long irqflags; + unsigned i; + + if (rdev == NULL) { + return; + } + DRM_SPINLOCK_IRQSAVE(&rdev->irq.lock, irqflags); + /* Disable *all* interrupts */ + for (i = 0; i < RADEON_NUM_RINGS; i++) + atomic_set(&rdev->irq.ring_int[i], 0); + for (i = 0; i < RADEON_MAX_HPD_PINS; i++) + rdev->irq.hpd[i] = false; + for (i = 0; i < RADEON_MAX_CRTCS; i++) { + rdev->irq.crtc_vblank_int[i] = false; + atomic_set(&rdev->irq.pflip[i], 0); + rdev->irq.afmt[i] = false; + } + radeon_irq_set(rdev); + DRM_SPINUNLOCK_IRQRESTORE(&rdev->irq.lock, irqflags); +} + +/** + * radeon_msi_ok - asic specific msi checks + * + * @rdev: radeon device pointer + * + * Handles asic specific MSI checks to determine if + * MSIs should be enabled on a particular chip (all asics). + * Returns true if MSIs should be enabled, false if MSIs + * should not be enabled. + */ +int radeon_msi_ok(struct drm_device *dev, unsigned long flags) +{ + int family; + + family = flags & RADEON_FAMILY_MASK; + + /* RV370/RV380 was first asic with MSI support */ + if (family < CHIP_RV380) + return false; + + /* MSIs don't work on AGP */ + if (drm_device_is_agp(dev)) + return false; + + /* force MSI on */ + if (radeon_msi == 1) + return true; + else if (radeon_msi == 0) + return false; + + /* Quirks */ + /* HP RS690 only seems to work with MSIs. */ + if ((dev->pci_device == 0x791f) && + (dev->pci_subvendor == 0x103c) && + (dev->pci_subdevice == 0x30c2)) + return true; + + /* Dell RS690 only seems to work with MSIs. */ + if ((dev->pci_device == 0x791f) && + (dev->pci_subvendor == 0x1028) && + (dev->pci_subdevice == 0x01fc)) + return true; + + /* Dell RS690 only seems to work with MSIs. */ + if ((dev->pci_device == 0x791f) && + (dev->pci_subvendor == 0x1028) && + (dev->pci_subdevice == 0x01fd)) + return true; + + /* Gateway RS690 only seems to work with MSIs. */ + if ((dev->pci_device == 0x791f) && + (dev->pci_subvendor == 0x107b) && + (dev->pci_subdevice == 0x0185)) + return true; + + /* try and enable MSIs by default on all RS690s */ + if (family == CHIP_RS690) + return true; + + /* RV515 seems to have MSI issues where it loses + * MSI rearms occasionally. This leads to lockups and freezes. + * disable it by default. + */ + if (family == CHIP_RV515) + return false; + if (flags & RADEON_IS_IGP) { + /* APUs work fine with MSIs */ + if (family >= CHIP_PALM) + return true; + /* lots of IGPs have problems with MSIs */ + return false; + } + + return true; +} + +/** + * radeon_irq_kms_init - init driver interrupt info + * + * @rdev: radeon device pointer + * + * Sets up the work irq handlers, vblank init, MSIs, etc. (all asics). + * Returns 0 for success, error for failure. + */ +int radeon_irq_kms_init(struct radeon_device *rdev) +{ + int r = 0; + + TASK_INIT(&rdev->hotplug_work, 0, radeon_hotplug_work_func, rdev); + TASK_INIT(&rdev->audio_work, 0, r600_audio_update_hdmi, rdev); + + DRM_SPININIT(&rdev->irq.lock, "drm__radeon_device__irq__lock"); + r = drm_vblank_init(rdev->ddev, rdev->num_crtc); + if (r) { + return r; + } + /* enable msi */ + rdev->msi_enabled = rdev->ddev->msi_enabled; + + rdev->irq.installed = true; + DRM_UNLOCK(rdev->ddev); + r = drm_irq_install(rdev->ddev); + DRM_LOCK(rdev->ddev); + if (r) { + rdev->irq.installed = false; + return r; + } + DRM_INFO("radeon: irq initialized.\n"); + return 0; +} + +/** + * radeon_irq_kms_fini - tear down driver interrrupt info + * + * @rdev: radeon device pointer + * + * Tears down the work irq handlers, vblank handlers, MSIs, etc. (all asics). + */ +void radeon_irq_kms_fini(struct radeon_device *rdev) +{ + drm_vblank_cleanup(rdev->ddev); + if (rdev->irq.installed) { + drm_irq_uninstall(rdev->ddev); + rdev->irq.installed = false; + } + taskqueue_drain(rdev->tq, &rdev->hotplug_work); +} + +/** + * radeon_irq_kms_sw_irq_get - enable software interrupt + * + * @rdev: radeon device pointer + * @ring: ring whose interrupt you want to enable + * + * Enables the software interrupt for a specific ring (all asics). + * The software interrupt is generally used to signal a fence on + * a particular ring. + */ +void radeon_irq_kms_sw_irq_get(struct radeon_device *rdev, int ring) +{ + unsigned long irqflags; + + if (!rdev->ddev->irq_enabled) + return; + + if (atomic_inc_return(&rdev->irq.ring_int[ring]) == 1) { + DRM_SPINLOCK_IRQSAVE(&rdev->irq.lock, irqflags); + radeon_irq_set(rdev); + DRM_SPINUNLOCK_IRQRESTORE(&rdev->irq.lock, irqflags); + } +} + +/** + * radeon_irq_kms_sw_irq_put - disable software interrupt + * + * @rdev: radeon device pointer + * @ring: ring whose interrupt you want to disable + * + * Disables the software interrupt for a specific ring (all asics). + * The software interrupt is generally used to signal a fence on + * a particular ring. + */ +void radeon_irq_kms_sw_irq_put(struct radeon_device *rdev, int ring) +{ + unsigned long irqflags; + + if (!rdev->ddev->irq_enabled) + return; + + if (atomic_dec_and_test(&rdev->irq.ring_int[ring])) { + DRM_SPINLOCK_IRQSAVE(&rdev->irq.lock, irqflags); + radeon_irq_set(rdev); + DRM_SPINUNLOCK_IRQRESTORE(&rdev->irq.lock, irqflags); + } +} + +/** + * radeon_irq_kms_pflip_irq_get - enable pageflip interrupt + * + * @rdev: radeon device pointer + * @crtc: crtc whose interrupt you want to enable + * + * Enables the pageflip interrupt for a specific crtc (all asics). + * For pageflips we use the vblank interrupt source. + */ +void radeon_irq_kms_pflip_irq_get(struct radeon_device *rdev, int crtc) +{ + unsigned long irqflags; + + if (crtc < 0 || crtc >= rdev->num_crtc) + return; + + if (!rdev->ddev->irq_enabled) + return; + + if (atomic_inc_return(&rdev->irq.pflip[crtc]) == 1) { + DRM_SPINLOCK_IRQSAVE(&rdev->irq.lock, irqflags); + radeon_irq_set(rdev); + DRM_SPINUNLOCK_IRQRESTORE(&rdev->irq.lock, irqflags); + } +} + +/** + * radeon_irq_kms_pflip_irq_put - disable pageflip interrupt + * + * @rdev: radeon device pointer + * @crtc: crtc whose interrupt you want to disable + * + * Disables the pageflip interrupt for a specific crtc (all asics). + * For pageflips we use the vblank interrupt source. + */ +void radeon_irq_kms_pflip_irq_put(struct radeon_device *rdev, int crtc) +{ + unsigned long irqflags; + + if (crtc < 0 || crtc >= rdev->num_crtc) + return; + + if (!rdev->ddev->irq_enabled) + return; + + if (atomic_dec_and_test(&rdev->irq.pflip[crtc])) { + DRM_SPINLOCK_IRQSAVE(&rdev->irq.lock, irqflags); + radeon_irq_set(rdev); + DRM_SPINUNLOCK_IRQRESTORE(&rdev->irq.lock, irqflags); + } +} + +/** + * radeon_irq_kms_enable_afmt - enable audio format change interrupt + * + * @rdev: radeon device pointer + * @block: afmt block whose interrupt you want to enable + * + * Enables the afmt change interrupt for a specific afmt block (all asics). + */ +void radeon_irq_kms_enable_afmt(struct radeon_device *rdev, int block) +{ + unsigned long irqflags; + + DRM_SPINLOCK_IRQSAVE(&rdev->irq.lock, irqflags); + rdev->irq.afmt[block] = true; + radeon_irq_set(rdev); + DRM_SPINUNLOCK_IRQRESTORE(&rdev->irq.lock, irqflags); + +} + +/** + * radeon_irq_kms_disable_afmt - disable audio format change interrupt + * + * @rdev: radeon device pointer + * @block: afmt block whose interrupt you want to disable + * + * Disables the afmt change interrupt for a specific afmt block (all asics). + */ +void radeon_irq_kms_disable_afmt(struct radeon_device *rdev, int block) +{ + unsigned long irqflags; + + DRM_SPINLOCK_IRQSAVE(&rdev->irq.lock, irqflags); + rdev->irq.afmt[block] = false; + radeon_irq_set(rdev); + DRM_SPINUNLOCK_IRQRESTORE(&rdev->irq.lock, irqflags); +} + +/** + * radeon_irq_kms_enable_hpd - enable hotplug detect interrupt + * + * @rdev: radeon device pointer + * @hpd_mask: mask of hpd pins you want to enable. + * + * Enables the hotplug detect interrupt for a specific hpd pin (all asics). + */ +void radeon_irq_kms_enable_hpd(struct radeon_device *rdev, unsigned hpd_mask) +{ + unsigned long irqflags; + int i; + + DRM_SPINLOCK_IRQSAVE(&rdev->irq.lock, irqflags); + for (i = 0; i < RADEON_MAX_HPD_PINS; ++i) + rdev->irq.hpd[i] |= !!(hpd_mask & (1 << i)); + radeon_irq_set(rdev); + DRM_SPINUNLOCK_IRQRESTORE(&rdev->irq.lock, irqflags); +} + +/** + * radeon_irq_kms_disable_hpd - disable hotplug detect interrupt + * + * @rdev: radeon device pointer + * @hpd_mask: mask of hpd pins you want to disable. + * + * Disables the hotplug detect interrupt for a specific hpd pin (all asics). + */ +void radeon_irq_kms_disable_hpd(struct radeon_device *rdev, unsigned hpd_mask) +{ + unsigned long irqflags; + int i; + + DRM_SPINLOCK_IRQSAVE(&rdev->irq.lock, irqflags); + for (i = 0; i < RADEON_MAX_HPD_PINS; ++i) + rdev->irq.hpd[i] &= !(hpd_mask & (1 << i)); + radeon_irq_set(rdev); + DRM_SPINUNLOCK_IRQRESTORE(&rdev->irq.lock, irqflags); +} + diff --git a/sys/dev/drm2/radeon/radeon_irq_kms.h b/sys/dev/drm2/radeon/radeon_irq_kms.h new file mode 100644 index 00000000000..6bfb9889779 --- /dev/null +++ b/sys/dev/drm2/radeon/radeon_irq_kms.h @@ -0,0 +1,15 @@ + +#include +__FBSDID("$FreeBSD$"); + +#ifndef __RADEON_IRQ_KMS_H__ +#define __RADEON_IRQ_KMS_H__ + +irqreturn_t radeon_driver_irq_handler_kms(DRM_IRQ_ARGS); +void radeon_driver_irq_preinstall_kms(struct drm_device *dev); +int radeon_driver_irq_postinstall_kms(struct drm_device *dev); +void radeon_driver_irq_uninstall_kms(struct drm_device *dev); + +int radeon_msi_ok(struct drm_device *dev, unsigned long flags); + +#endif /* !defined(__RADEON_IRQ_KMS_H__) */ diff --git a/sys/dev/drm2/radeon/radeon_kms.c b/sys/dev/drm2/radeon/radeon_kms.c new file mode 100644 index 00000000000..8507d016e58 --- /dev/null +++ b/sys/dev/drm2/radeon/radeon_kms.c @@ -0,0 +1,727 @@ +/* + * Copyright 2008 Advanced Micro Devices, Inc. + * Copyright 2008 Red Hat Inc. + * Copyright 2009 Jerome Glisse. + * + * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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. + * + * Authors: Dave Airlie + * Alex Deucher + * Jerome Glisse + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include "radeon.h" +#include +#include "radeon_asic.h" +#include "radeon_kms.h" + +/** + * radeon_driver_unload_kms - Main unload function for KMS. + * + * @dev: drm dev pointer + * + * This is the main unload function for KMS (all asics). + * It calls radeon_modeset_fini() to tear down the + * displays, and radeon_device_fini() to tear down + * the rest of the device (CP, writeback, etc.). + * Returns 0 on success. + */ +int radeon_driver_unload_kms(struct drm_device *dev) +{ + struct radeon_device *rdev = dev->dev_private; + + if (rdev == NULL) + return 0; + radeon_acpi_fini(rdev); + radeon_modeset_fini(rdev); + radeon_device_fini(rdev); + free(rdev, DRM_MEM_DRIVER); + dev->dev_private = NULL; + return 0; +} + +/** + * radeon_driver_load_kms - Main load function for KMS. + * + * @dev: drm dev pointer + * @flags: device flags + * + * This is the main load function for KMS (all asics). + * It calls radeon_device_init() to set up the non-display + * parts of the chip (asic init, CP, writeback, etc.), and + * radeon_modeset_init() to set up the display parts + * (crtcs, encoders, hotplug detect, etc.). + * Returns 0 on success, error on failure. + */ +int radeon_driver_load_kms(struct drm_device *dev, unsigned long flags) +{ + struct radeon_device *rdev; + int r, acpi_status; + + rdev = malloc(sizeof(struct radeon_device), DRM_MEM_DRIVER, M_ZERO | M_WAITOK); + if (rdev == NULL) { + return -ENOMEM; + } + dev->dev_private = (void *)rdev; + + /* update BUS flag */ + if (drm_device_is_agp(dev)) { + DRM_INFO("RADEON_IS_AGP\n"); + flags |= RADEON_IS_AGP; + } else if (drm_device_is_pcie(dev)) { + DRM_INFO("RADEON_IS_PCIE\n"); + flags |= RADEON_IS_PCIE; + } else { + DRM_INFO("RADEON_IS_PCI\n"); + flags |= RADEON_IS_PCI; + } + + /* radeon_device_init should report only fatal error + * like memory allocation failure or iomapping failure, + * or memory manager initialization failure, it must + * properly initialize the GPU MC controller and permit + * VRAM allocation + */ + r = radeon_device_init(rdev, dev, flags); + if (r) { + dev_err(dev->device, "Fatal error during GPU init\n"); + goto out; + } + + /* Again modeset_init should fail only on fatal error + * otherwise it should provide enough functionalities + * for shadowfb to run + */ + r = radeon_modeset_init(rdev); + if (r) + dev_err(dev->device, "Fatal error during modeset init\n"); + + /* Call ACPI methods: require modeset init + * but failure is not fatal + */ + if (!r) { + acpi_status = radeon_acpi_init(rdev); + if (acpi_status) + dev_dbg(dev->device, + "Error during ACPI methods call\n"); + } + +out: + if (r) + radeon_driver_unload_kms(dev); + return r; +} + +/** + * radeon_set_filp_rights - Set filp right. + * + * @dev: drm dev pointer + * @owner: drm file + * @applier: drm file + * @value: value + * + * Sets the filp rights for the device (all asics). + */ +static void radeon_set_filp_rights(struct drm_device *dev, + struct drm_file **owner, + struct drm_file *applier, + uint32_t *value) +{ + DRM_LOCK(dev); + if (*value == 1) { + /* wants rights */ + if (!*owner) + *owner = applier; + } else if (*value == 0) { + /* revokes rights */ + if (*owner == applier) + *owner = NULL; + } + *value = *owner == applier ? 1 : 0; + DRM_UNLOCK(dev); +} + +/* + * Userspace get information ioctl + */ +/** + * radeon_info_ioctl - answer a device specific request. + * + * @rdev: radeon device pointer + * @data: request object + * @filp: drm filp + * + * This function is used to pass device specific parameters to the userspace + * drivers. Examples include: pci device id, pipeline parms, tiling params, + * etc. (all asics). + * Returns 0 on success, -EINVAL on failure. + */ +static int radeon_info_ioctl(struct drm_device *dev, void *data, struct drm_file *filp) +{ + struct radeon_device *rdev = dev->dev_private; + struct drm_radeon_info *info = data; + struct radeon_mode_info *minfo = &rdev->mode_info; + uint32_t value, *value_ptr; + uint64_t value64, *value_ptr64; + struct drm_crtc *crtc; + int i, found; + + /* TIMESTAMP is a 64-bit value, needs special handling. */ + if (info->request == RADEON_INFO_TIMESTAMP) { + if (rdev->family >= CHIP_R600) { + value_ptr64 = (uint64_t*)((unsigned long)info->value); + if (rdev->family >= CHIP_TAHITI) { + value64 = si_get_gpu_clock(rdev); + } else { + value64 = r600_get_gpu_clock(rdev); + } + + if (DRM_COPY_TO_USER(value_ptr64, &value64, sizeof(value64))) { + DRM_ERROR("copy_to_user %s:%u\n", __func__, __LINE__); + return -EFAULT; + } + return 0; + } else { + DRM_DEBUG_KMS("timestamp is r6xx+ only!\n"); + return -EINVAL; + } + } + + value_ptr = (uint32_t *)((unsigned long)info->value); + if (DRM_COPY_FROM_USER(&value, value_ptr, sizeof(value))) { + DRM_ERROR("copy_from_user %s:%u\n", __func__, __LINE__); + return -EFAULT; + } + + switch (info->request) { + case RADEON_INFO_DEVICE_ID: + value = dev->pci_device; + break; + case RADEON_INFO_NUM_GB_PIPES: + value = rdev->num_gb_pipes; + break; + case RADEON_INFO_NUM_Z_PIPES: + value = rdev->num_z_pipes; + break; + case RADEON_INFO_ACCEL_WORKING: + /* xf86-video-ati 6.13.0 relies on this being false for evergreen */ + if ((rdev->family >= CHIP_CEDAR) && (rdev->family <= CHIP_HEMLOCK)) + value = false; + else + value = rdev->accel_working; + break; + case RADEON_INFO_CRTC_FROM_ID: + for (i = 0, found = 0; i < rdev->num_crtc; i++) { + crtc = (struct drm_crtc *)minfo->crtcs[i]; + if (crtc && crtc->base.id == value) { + struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); + value = radeon_crtc->crtc_id; + found = 1; + break; + } + } + if (!found) { + DRM_DEBUG_KMS("unknown crtc id %d\n", value); + return -EINVAL; + } + break; + case RADEON_INFO_ACCEL_WORKING2: + value = rdev->accel_working; + break; + case RADEON_INFO_TILING_CONFIG: + if (rdev->family >= CHIP_TAHITI) + value = rdev->config.si.tile_config; + else if (rdev->family >= CHIP_CAYMAN) + value = rdev->config.cayman.tile_config; + else if (rdev->family >= CHIP_CEDAR) + value = rdev->config.evergreen.tile_config; + else if (rdev->family >= CHIP_RV770) + value = rdev->config.rv770.tile_config; + else if (rdev->family >= CHIP_R600) + value = rdev->config.r600.tile_config; + else { + DRM_DEBUG_KMS("tiling config is r6xx+ only!\n"); + return -EINVAL; + } + break; + case RADEON_INFO_WANT_HYPERZ: + /* The "value" here is both an input and output parameter. + * If the input value is 1, filp requests hyper-z access. + * If the input value is 0, filp revokes its hyper-z access. + * + * When returning, the value is 1 if filp owns hyper-z access, + * 0 otherwise. */ + if (value >= 2) { + DRM_DEBUG_KMS("WANT_HYPERZ: invalid value %d\n", value); + return -EINVAL; + } + radeon_set_filp_rights(dev, &rdev->hyperz_filp, filp, &value); + break; + case RADEON_INFO_WANT_CMASK: + /* The same logic as Hyper-Z. */ + if (value >= 2) { + DRM_DEBUG_KMS("WANT_CMASK: invalid value %d\n", value); + return -EINVAL; + } + radeon_set_filp_rights(dev, &rdev->cmask_filp, filp, &value); + break; + case RADEON_INFO_CLOCK_CRYSTAL_FREQ: + /* return clock value in KHz */ + value = rdev->clock.spll.reference_freq * 10; + break; + case RADEON_INFO_NUM_BACKENDS: + if (rdev->family >= CHIP_TAHITI) + value = rdev->config.si.max_backends_per_se * + rdev->config.si.max_shader_engines; + else if (rdev->family >= CHIP_CAYMAN) + value = rdev->config.cayman.max_backends_per_se * + rdev->config.cayman.max_shader_engines; + else if (rdev->family >= CHIP_CEDAR) + value = rdev->config.evergreen.max_backends; + else if (rdev->family >= CHIP_RV770) + value = rdev->config.rv770.max_backends; + else if (rdev->family >= CHIP_R600) + value = rdev->config.r600.max_backends; + else { + return -EINVAL; + } + break; + case RADEON_INFO_NUM_TILE_PIPES: + if (rdev->family >= CHIP_TAHITI) + value = rdev->config.si.max_tile_pipes; + else if (rdev->family >= CHIP_CAYMAN) + value = rdev->config.cayman.max_tile_pipes; + else if (rdev->family >= CHIP_CEDAR) + value = rdev->config.evergreen.max_tile_pipes; + else if (rdev->family >= CHIP_RV770) + value = rdev->config.rv770.max_tile_pipes; + else if (rdev->family >= CHIP_R600) + value = rdev->config.r600.max_tile_pipes; + else { + return -EINVAL; + } + break; + case RADEON_INFO_FUSION_GART_WORKING: + value = 1; + break; + case RADEON_INFO_BACKEND_MAP: + if (rdev->family >= CHIP_TAHITI) + value = rdev->config.si.backend_map; + else if (rdev->family >= CHIP_CAYMAN) + value = rdev->config.cayman.backend_map; + else if (rdev->family >= CHIP_CEDAR) + value = rdev->config.evergreen.backend_map; + else if (rdev->family >= CHIP_RV770) + value = rdev->config.rv770.backend_map; + else if (rdev->family >= CHIP_R600) + value = rdev->config.r600.backend_map; + else { + return -EINVAL; + } + break; + case RADEON_INFO_VA_START: + /* this is where we report if vm is supported or not */ + if (rdev->family < CHIP_CAYMAN) + return -EINVAL; + value = RADEON_VA_RESERVED_SIZE; + break; + case RADEON_INFO_IB_VM_MAX_SIZE: + /* this is where we report if vm is supported or not */ + if (rdev->family < CHIP_CAYMAN) + return -EINVAL; + value = RADEON_IB_VM_MAX_SIZE; + break; + case RADEON_INFO_MAX_PIPES: + if (rdev->family >= CHIP_TAHITI) + value = rdev->config.si.max_cu_per_sh; + else if (rdev->family >= CHIP_CAYMAN) + value = rdev->config.cayman.max_pipes_per_simd; + else if (rdev->family >= CHIP_CEDAR) + value = rdev->config.evergreen.max_pipes; + else if (rdev->family >= CHIP_RV770) + value = rdev->config.rv770.max_pipes; + else if (rdev->family >= CHIP_R600) + value = rdev->config.r600.max_pipes; + else { + return -EINVAL; + } + break; + case RADEON_INFO_MAX_SE: + if (rdev->family >= CHIP_TAHITI) + value = rdev->config.si.max_shader_engines; + else if (rdev->family >= CHIP_CAYMAN) + value = rdev->config.cayman.max_shader_engines; + else if (rdev->family >= CHIP_CEDAR) + value = rdev->config.evergreen.num_ses; + else + value = 1; + break; + case RADEON_INFO_MAX_SH_PER_SE: + if (rdev->family >= CHIP_TAHITI) + value = rdev->config.si.max_sh_per_se; + else + return -EINVAL; + break; + default: + DRM_DEBUG_KMS("Invalid request %d\n", info->request); + return -EINVAL; + } + if (DRM_COPY_TO_USER(value_ptr, &value, sizeof(uint32_t))) { + DRM_ERROR("copy_to_user %s:%u\n", __func__, __LINE__); + return -EFAULT; + } + return 0; +} + + +/* + * Outdated mess for old drm with Xorg being in charge (void function now). + */ +/** + * radeon_driver_firstopen_kms - drm callback for first open + * + * @dev: drm dev pointer + * + * Nothing to be done for KMS (all asics). + * Returns 0 on success. + */ +int radeon_driver_firstopen_kms(struct drm_device *dev) +{ + return 0; +} + +/** + * radeon_driver_firstopen_kms - drm callback for last close + * + * @dev: drm dev pointer + * + * Switch vga switcheroo state after last close (all asics). + */ +void radeon_driver_lastclose_kms(struct drm_device *dev) +{ +#ifdef DUMBBELL_WIP + vga_switcheroo_process_delayed_switch(); +#endif /* DUMBBELL_WIP */ +} + +/** + * radeon_driver_open_kms - drm callback for open + * + * @dev: drm dev pointer + * @file_priv: drm file + * + * On device open, init vm on cayman+ (all asics). + * Returns 0 on success, error on failure. + */ +int radeon_driver_open_kms(struct drm_device *dev, struct drm_file *file_priv) +{ + struct radeon_device *rdev = dev->dev_private; + + file_priv->driver_priv = NULL; + + /* new gpu have virtual address space support */ + if (rdev->family >= CHIP_CAYMAN) { + struct radeon_fpriv *fpriv; + struct radeon_bo_va *bo_va; + int r; + + fpriv = malloc(sizeof(*fpriv), DRM_MEM_DRIVER, M_ZERO | M_WAITOK); + if (unlikely(!fpriv)) { + return -ENOMEM; + } + + radeon_vm_init(rdev, &fpriv->vm); + + /* map the ib pool buffer read only into + * virtual address space */ + bo_va = radeon_vm_bo_add(rdev, &fpriv->vm, + rdev->ring_tmp_bo.bo); + r = radeon_vm_bo_set_addr(rdev, bo_va, RADEON_VA_IB_OFFSET, + RADEON_VM_PAGE_READABLE | + RADEON_VM_PAGE_SNOOPED); + if (r) { + radeon_vm_fini(rdev, &fpriv->vm); + free(fpriv, DRM_MEM_DRIVER); + return r; + } + + file_priv->driver_priv = fpriv; + } + return 0; +} + +/** + * radeon_driver_postclose_kms - drm callback for post close + * + * @dev: drm dev pointer + * @file_priv: drm file + * + * On device post close, tear down vm on cayman+ (all asics). + */ +void radeon_driver_postclose_kms(struct drm_device *dev, + struct drm_file *file_priv) +{ + struct radeon_device *rdev = dev->dev_private; + + /* new gpu have virtual address space support */ + if (rdev->family >= CHIP_CAYMAN && file_priv->driver_priv) { + struct radeon_fpriv *fpriv = file_priv->driver_priv; + struct radeon_bo_va *bo_va; + int r; + + r = radeon_bo_reserve(rdev->ring_tmp_bo.bo, false); + if (!r) { + bo_va = radeon_vm_bo_find(&fpriv->vm, + rdev->ring_tmp_bo.bo); + if (bo_va) + radeon_vm_bo_rmv(rdev, bo_va); + radeon_bo_unreserve(rdev->ring_tmp_bo.bo); + } + + radeon_vm_fini(rdev, &fpriv->vm); + free(fpriv, DRM_MEM_DRIVER); + file_priv->driver_priv = NULL; + } +} + +/** + * radeon_driver_preclose_kms - drm callback for pre close + * + * @dev: drm dev pointer + * @file_priv: drm file + * + * On device pre close, tear down hyperz and cmask filps on r1xx-r5xx + * (all asics). + */ +void radeon_driver_preclose_kms(struct drm_device *dev, + struct drm_file *file_priv) +{ + struct radeon_device *rdev = dev->dev_private; + if (rdev->hyperz_filp == file_priv) + rdev->hyperz_filp = NULL; + if (rdev->cmask_filp == file_priv) + rdev->cmask_filp = NULL; +} + +/* + * VBlank related functions. + */ +/** + * radeon_get_vblank_counter_kms - get frame count + * + * @dev: drm dev pointer + * @crtc: crtc to get the frame count from + * + * Gets the frame count on the requested crtc (all asics). + * Returns frame count on success, -EINVAL on failure. + */ +u32 radeon_get_vblank_counter_kms(struct drm_device *dev, int crtc) +{ + struct radeon_device *rdev = dev->dev_private; + + if (crtc < 0 || crtc >= rdev->num_crtc) { + DRM_ERROR("Invalid crtc %d\n", crtc); + return -EINVAL; + } + + return radeon_get_vblank_counter(rdev, crtc); +} + +/** + * radeon_enable_vblank_kms - enable vblank interrupt + * + * @dev: drm dev pointer + * @crtc: crtc to enable vblank interrupt for + * + * Enable the interrupt on the requested crtc (all asics). + * Returns 0 on success, -EINVAL on failure. + */ +int radeon_enable_vblank_kms(struct drm_device *dev, int crtc) +{ + struct radeon_device *rdev = dev->dev_private; + int r; + + if (crtc < 0 || crtc >= rdev->num_crtc) { + DRM_ERROR("Invalid crtc %d\n", crtc); + return -EINVAL; + } + + mtx_lock(&rdev->irq.lock); + rdev->irq.crtc_vblank_int[crtc] = true; + r = radeon_irq_set(rdev); + mtx_unlock(&rdev->irq.lock); + return r; +} + +/** + * radeon_disable_vblank_kms - disable vblank interrupt + * + * @dev: drm dev pointer + * @crtc: crtc to disable vblank interrupt for + * + * Disable the interrupt on the requested crtc (all asics). + */ +void radeon_disable_vblank_kms(struct drm_device *dev, int crtc) +{ + struct radeon_device *rdev = dev->dev_private; + + if (crtc < 0 || crtc >= rdev->num_crtc) { + DRM_ERROR("Invalid crtc %d\n", crtc); + return; + } + + mtx_lock(&rdev->irq.lock); + rdev->irq.crtc_vblank_int[crtc] = false; + radeon_irq_set(rdev); + mtx_unlock(&rdev->irq.lock); +} + +/** + * radeon_get_vblank_timestamp_kms - get vblank timestamp + * + * @dev: drm dev pointer + * @crtc: crtc to get the timestamp for + * @max_error: max error + * @vblank_time: time value + * @flags: flags passed to the driver + * + * Gets the timestamp on the requested crtc based on the + * scanout position. (all asics). + * Returns postive status flags on success, negative error on failure. + */ +int radeon_get_vblank_timestamp_kms(struct drm_device *dev, int crtc, + int *max_error, + struct timeval *vblank_time, + unsigned flags) +{ + struct drm_crtc *drmcrtc; + struct radeon_device *rdev = dev->dev_private; + + if (crtc < 0 || crtc >= dev->num_crtcs) { + DRM_ERROR("Invalid crtc %d\n", crtc); + return -EINVAL; + } + + /* Get associated drm_crtc: */ + drmcrtc = &rdev->mode_info.crtcs[crtc]->base; + + /* Helper routine in DRM core does all the work: */ + return drm_calc_vbltimestamp_from_scanoutpos(dev, crtc, max_error, + vblank_time, flags, + drmcrtc); +} + +/* + * IOCTL. + */ +int radeon_dma_ioctl_kms(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + /* Not valid in KMS. */ + return -EINVAL; +} + +#define KMS_INVALID_IOCTL(name) \ +static int \ +name(struct drm_device *dev, void *data, struct drm_file *file_priv) \ +{ \ + DRM_ERROR("invalid ioctl with kms %s\n", __func__); \ + return -EINVAL; \ +} + +/* + * All these ioctls are invalid in kms world. + */ +KMS_INVALID_IOCTL(radeon_cp_init_kms) +KMS_INVALID_IOCTL(radeon_cp_start_kms) +KMS_INVALID_IOCTL(radeon_cp_stop_kms) +KMS_INVALID_IOCTL(radeon_cp_reset_kms) +KMS_INVALID_IOCTL(radeon_cp_idle_kms) +KMS_INVALID_IOCTL(radeon_cp_resume_kms) +KMS_INVALID_IOCTL(radeon_engine_reset_kms) +KMS_INVALID_IOCTL(radeon_fullscreen_kms) +KMS_INVALID_IOCTL(radeon_cp_swap_kms) +KMS_INVALID_IOCTL(radeon_cp_clear_kms) +KMS_INVALID_IOCTL(radeon_cp_vertex_kms) +KMS_INVALID_IOCTL(radeon_cp_indices_kms) +KMS_INVALID_IOCTL(radeon_cp_texture_kms) +KMS_INVALID_IOCTL(radeon_cp_stipple_kms) +KMS_INVALID_IOCTL(radeon_cp_indirect_kms) +KMS_INVALID_IOCTL(radeon_cp_vertex2_kms) +KMS_INVALID_IOCTL(radeon_cp_cmdbuf_kms) +KMS_INVALID_IOCTL(radeon_cp_getparam_kms) +KMS_INVALID_IOCTL(radeon_cp_flip_kms) +KMS_INVALID_IOCTL(radeon_mem_alloc_kms) +KMS_INVALID_IOCTL(radeon_mem_free_kms) +KMS_INVALID_IOCTL(radeon_mem_init_heap_kms) +KMS_INVALID_IOCTL(radeon_irq_emit_kms) +KMS_INVALID_IOCTL(radeon_irq_wait_kms) +KMS_INVALID_IOCTL(radeon_cp_setparam_kms) +KMS_INVALID_IOCTL(radeon_surface_alloc_kms) +KMS_INVALID_IOCTL(radeon_surface_free_kms) + + +struct drm_ioctl_desc radeon_ioctls_kms[] = { + DRM_IOCTL_DEF_DRV(RADEON_CP_INIT, radeon_cp_init_kms, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), + DRM_IOCTL_DEF_DRV(RADEON_CP_START, radeon_cp_start_kms, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), + DRM_IOCTL_DEF_DRV(RADEON_CP_STOP, radeon_cp_stop_kms, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), + DRM_IOCTL_DEF_DRV(RADEON_CP_RESET, radeon_cp_reset_kms, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), + DRM_IOCTL_DEF_DRV(RADEON_CP_IDLE, radeon_cp_idle_kms, DRM_AUTH), + DRM_IOCTL_DEF_DRV(RADEON_CP_RESUME, radeon_cp_resume_kms, DRM_AUTH), + DRM_IOCTL_DEF_DRV(RADEON_RESET, radeon_engine_reset_kms, DRM_AUTH), + DRM_IOCTL_DEF_DRV(RADEON_FULLSCREEN, radeon_fullscreen_kms, DRM_AUTH), + DRM_IOCTL_DEF_DRV(RADEON_SWAP, radeon_cp_swap_kms, DRM_AUTH), + DRM_IOCTL_DEF_DRV(RADEON_CLEAR, radeon_cp_clear_kms, DRM_AUTH), + DRM_IOCTL_DEF_DRV(RADEON_VERTEX, radeon_cp_vertex_kms, DRM_AUTH), + DRM_IOCTL_DEF_DRV(RADEON_INDICES, radeon_cp_indices_kms, DRM_AUTH), + DRM_IOCTL_DEF_DRV(RADEON_TEXTURE, radeon_cp_texture_kms, DRM_AUTH), + DRM_IOCTL_DEF_DRV(RADEON_STIPPLE, radeon_cp_stipple_kms, DRM_AUTH), + DRM_IOCTL_DEF_DRV(RADEON_INDIRECT, radeon_cp_indirect_kms, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), + DRM_IOCTL_DEF_DRV(RADEON_VERTEX2, radeon_cp_vertex2_kms, DRM_AUTH), + DRM_IOCTL_DEF_DRV(RADEON_CMDBUF, radeon_cp_cmdbuf_kms, DRM_AUTH), + DRM_IOCTL_DEF_DRV(RADEON_GETPARAM, radeon_cp_getparam_kms, DRM_AUTH), + DRM_IOCTL_DEF_DRV(RADEON_FLIP, radeon_cp_flip_kms, DRM_AUTH), + DRM_IOCTL_DEF_DRV(RADEON_ALLOC, radeon_mem_alloc_kms, DRM_AUTH), + DRM_IOCTL_DEF_DRV(RADEON_FREE, radeon_mem_free_kms, DRM_AUTH), + DRM_IOCTL_DEF_DRV(RADEON_INIT_HEAP, radeon_mem_init_heap_kms, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), + DRM_IOCTL_DEF_DRV(RADEON_IRQ_EMIT, radeon_irq_emit_kms, DRM_AUTH), + DRM_IOCTL_DEF_DRV(RADEON_IRQ_WAIT, radeon_irq_wait_kms, DRM_AUTH), + DRM_IOCTL_DEF_DRV(RADEON_SETPARAM, radeon_cp_setparam_kms, DRM_AUTH), + DRM_IOCTL_DEF_DRV(RADEON_SURF_ALLOC, radeon_surface_alloc_kms, DRM_AUTH), + DRM_IOCTL_DEF_DRV(RADEON_SURF_FREE, radeon_surface_free_kms, DRM_AUTH), + /* KMS */ + DRM_IOCTL_DEF_DRV(RADEON_GEM_INFO, radeon_gem_info_ioctl, DRM_AUTH|DRM_UNLOCKED), + DRM_IOCTL_DEF_DRV(RADEON_GEM_CREATE, radeon_gem_create_ioctl, DRM_AUTH|DRM_UNLOCKED), + DRM_IOCTL_DEF_DRV(RADEON_GEM_MMAP, radeon_gem_mmap_ioctl, DRM_AUTH|DRM_UNLOCKED), + DRM_IOCTL_DEF_DRV(RADEON_GEM_SET_DOMAIN, radeon_gem_set_domain_ioctl, DRM_AUTH|DRM_UNLOCKED), + DRM_IOCTL_DEF_DRV(RADEON_GEM_PREAD, radeon_gem_pread_ioctl, DRM_AUTH|DRM_UNLOCKED), + DRM_IOCTL_DEF_DRV(RADEON_GEM_PWRITE, radeon_gem_pwrite_ioctl, DRM_AUTH|DRM_UNLOCKED), + DRM_IOCTL_DEF_DRV(RADEON_GEM_WAIT_IDLE, radeon_gem_wait_idle_ioctl, DRM_AUTH|DRM_UNLOCKED), + DRM_IOCTL_DEF_DRV(RADEON_CS, radeon_cs_ioctl, DRM_AUTH|DRM_UNLOCKED), + DRM_IOCTL_DEF_DRV(RADEON_INFO, radeon_info_ioctl, DRM_AUTH|DRM_UNLOCKED), + DRM_IOCTL_DEF_DRV(RADEON_GEM_SET_TILING, radeon_gem_set_tiling_ioctl, DRM_AUTH|DRM_UNLOCKED), + DRM_IOCTL_DEF_DRV(RADEON_GEM_GET_TILING, radeon_gem_get_tiling_ioctl, DRM_AUTH|DRM_UNLOCKED), + DRM_IOCTL_DEF_DRV(RADEON_GEM_BUSY, radeon_gem_busy_ioctl, DRM_AUTH|DRM_UNLOCKED), + DRM_IOCTL_DEF_DRV(RADEON_GEM_VA, radeon_gem_va_ioctl, DRM_AUTH|DRM_UNLOCKED), +}; +int radeon_max_kms_ioctl = DRM_ARRAY_SIZE(radeon_ioctls_kms); diff --git a/sys/dev/drm2/radeon/radeon_kms.h b/sys/dev/drm2/radeon/radeon_kms.h new file mode 100644 index 00000000000..ff3bbbccdd3 --- /dev/null +++ b/sys/dev/drm2/radeon/radeon_kms.h @@ -0,0 +1,30 @@ + +#include +__FBSDID("$FreeBSD$"); + +#ifndef __RADEON_KMS_H__ +#define __RADEON_KMS_H__ + +#include + +int radeon_driver_load_kms(struct drm_device *dev, unsigned long flags); +int radeon_driver_unload_kms(struct drm_device *dev); + +int radeon_driver_firstopen_kms(struct drm_device *dev); +void radeon_driver_lastclose_kms(struct drm_device *dev); +int radeon_driver_open_kms(struct drm_device *dev, struct drm_file *file_priv); +void radeon_driver_postclose_kms(struct drm_device *dev, + struct drm_file *file_priv); +void radeon_driver_preclose_kms(struct drm_device *dev, + struct drm_file *file_priv); +u32 radeon_get_vblank_counter_kms(struct drm_device *dev, int crtc); +int radeon_enable_vblank_kms(struct drm_device *dev, int crtc); +void radeon_disable_vblank_kms(struct drm_device *dev, int crtc); +int radeon_get_vblank_timestamp_kms(struct drm_device *dev, int crtc, + int *max_error, + struct timeval *vblank_time, + unsigned flags); +int radeon_dma_ioctl_kms(struct drm_device *dev, void *data, + struct drm_file *file_priv); + +#endif /* !defined(__RADEON_KMS_H__) */ diff --git a/sys/dev/drm2/radeon/radeon_legacy_crtc.c b/sys/dev/drm2/radeon/radeon_legacy_crtc.c new file mode 100644 index 00000000000..54f755a6097 --- /dev/null +++ b/sys/dev/drm2/radeon/radeon_legacy_crtc.c @@ -0,0 +1,1085 @@ +/* + * Copyright 2007-8 Advanced Micro Devices, Inc. + * Copyright 2008 Red Hat Inc. + * + * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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. + * + * Authors: Dave Airlie + * Alex Deucher + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include "radeon.h" +#include "atom.h" + +static void radeon_overscan_setup(struct drm_crtc *crtc, + struct drm_display_mode *mode) +{ + struct drm_device *dev = crtc->dev; + struct radeon_device *rdev = dev->dev_private; + struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); + + WREG32(RADEON_OVR_CLR + radeon_crtc->crtc_offset, 0); + WREG32(RADEON_OVR_WID_LEFT_RIGHT + radeon_crtc->crtc_offset, 0); + WREG32(RADEON_OVR_WID_TOP_BOTTOM + radeon_crtc->crtc_offset, 0); +} + +static void radeon_legacy_rmx_mode_set(struct drm_crtc *crtc, + struct drm_display_mode *mode) +{ + struct drm_device *dev = crtc->dev; + struct radeon_device *rdev = dev->dev_private; + struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); + int xres = mode->hdisplay; + int yres = mode->vdisplay; + bool hscale = true, vscale = true; + int hsync_wid; + int vsync_wid; + int hsync_start; + int blank_width; + u32 scale, inc, crtc_more_cntl; + u32 fp_horz_stretch, fp_vert_stretch, fp_horz_vert_active; + u32 fp_h_sync_strt_wid, fp_crtc_h_total_disp; + u32 fp_v_sync_strt_wid, fp_crtc_v_total_disp; + struct drm_display_mode *native_mode = &radeon_crtc->native_mode; + + fp_vert_stretch = RREG32(RADEON_FP_VERT_STRETCH) & + (RADEON_VERT_STRETCH_RESERVED | + RADEON_VERT_AUTO_RATIO_INC); + fp_horz_stretch = RREG32(RADEON_FP_HORZ_STRETCH) & + (RADEON_HORZ_FP_LOOP_STRETCH | + RADEON_HORZ_AUTO_RATIO_INC); + + crtc_more_cntl = 0; + if ((rdev->family == CHIP_RS100) || + (rdev->family == CHIP_RS200)) { + /* This is to workaround the asic bug for RMX, some versions + of BIOS dosen't have this register initialized correctly. */ + crtc_more_cntl |= RADEON_CRTC_H_CUTOFF_ACTIVE_EN; + } + + + fp_crtc_h_total_disp = ((((mode->crtc_htotal / 8) - 1) & 0x3ff) + | ((((mode->crtc_hdisplay / 8) - 1) & 0x1ff) << 16)); + + hsync_wid = (mode->crtc_hsync_end - mode->crtc_hsync_start) / 8; + if (!hsync_wid) + hsync_wid = 1; + hsync_start = mode->crtc_hsync_start - 8; + + fp_h_sync_strt_wid = ((hsync_start & 0x1fff) + | ((hsync_wid & 0x3f) << 16) + | ((mode->flags & DRM_MODE_FLAG_NHSYNC) + ? RADEON_CRTC_H_SYNC_POL + : 0)); + + fp_crtc_v_total_disp = (((mode->crtc_vtotal - 1) & 0xffff) + | ((mode->crtc_vdisplay - 1) << 16)); + + vsync_wid = mode->crtc_vsync_end - mode->crtc_vsync_start; + if (!vsync_wid) + vsync_wid = 1; + + fp_v_sync_strt_wid = (((mode->crtc_vsync_start - 1) & 0xfff) + | ((vsync_wid & 0x1f) << 16) + | ((mode->flags & DRM_MODE_FLAG_NVSYNC) + ? RADEON_CRTC_V_SYNC_POL + : 0)); + + fp_horz_vert_active = 0; + + if (native_mode->hdisplay == 0 || + native_mode->vdisplay == 0) { + hscale = false; + vscale = false; + } else { + if (xres > native_mode->hdisplay) + xres = native_mode->hdisplay; + if (yres > native_mode->vdisplay) + yres = native_mode->vdisplay; + + if (xres == native_mode->hdisplay) + hscale = false; + if (yres == native_mode->vdisplay) + vscale = false; + } + + switch (radeon_crtc->rmx_type) { + case RMX_FULL: + case RMX_ASPECT: + if (!hscale) + fp_horz_stretch |= ((xres/8-1) << 16); + else { + inc = (fp_horz_stretch & RADEON_HORZ_AUTO_RATIO_INC) ? 1 : 0; + scale = ((xres + inc) * RADEON_HORZ_STRETCH_RATIO_MAX) + / native_mode->hdisplay + 1; + fp_horz_stretch |= (((scale) & RADEON_HORZ_STRETCH_RATIO_MASK) | + RADEON_HORZ_STRETCH_BLEND | + RADEON_HORZ_STRETCH_ENABLE | + ((native_mode->hdisplay/8-1) << 16)); + } + + if (!vscale) + fp_vert_stretch |= ((yres-1) << 12); + else { + inc = (fp_vert_stretch & RADEON_VERT_AUTO_RATIO_INC) ? 1 : 0; + scale = ((yres + inc) * RADEON_VERT_STRETCH_RATIO_MAX) + / native_mode->vdisplay + 1; + fp_vert_stretch |= (((scale) & RADEON_VERT_STRETCH_RATIO_MASK) | + RADEON_VERT_STRETCH_ENABLE | + RADEON_VERT_STRETCH_BLEND | + ((native_mode->vdisplay-1) << 12)); + } + break; + case RMX_CENTER: + fp_horz_stretch |= ((xres/8-1) << 16); + fp_vert_stretch |= ((yres-1) << 12); + + crtc_more_cntl |= (RADEON_CRTC_AUTO_HORZ_CENTER_EN | + RADEON_CRTC_AUTO_VERT_CENTER_EN); + + blank_width = (mode->crtc_hblank_end - mode->crtc_hblank_start) / 8; + if (blank_width > 110) + blank_width = 110; + + fp_crtc_h_total_disp = (((blank_width) & 0x3ff) + | ((((mode->crtc_hdisplay / 8) - 1) & 0x1ff) << 16)); + + hsync_wid = (mode->crtc_hsync_end - mode->crtc_hsync_start) / 8; + if (!hsync_wid) + hsync_wid = 1; + + fp_h_sync_strt_wid = ((((mode->crtc_hsync_start - mode->crtc_hblank_start) / 8) & 0x1fff) + | ((hsync_wid & 0x3f) << 16) + | ((mode->flags & DRM_MODE_FLAG_NHSYNC) + ? RADEON_CRTC_H_SYNC_POL + : 0)); + + fp_crtc_v_total_disp = (((mode->crtc_vblank_end - mode->crtc_vblank_start) & 0xffff) + | ((mode->crtc_vdisplay - 1) << 16)); + + vsync_wid = mode->crtc_vsync_end - mode->crtc_vsync_start; + if (!vsync_wid) + vsync_wid = 1; + + fp_v_sync_strt_wid = ((((mode->crtc_vsync_start - mode->crtc_vblank_start) & 0xfff) + | ((vsync_wid & 0x1f) << 16) + | ((mode->flags & DRM_MODE_FLAG_NVSYNC) + ? RADEON_CRTC_V_SYNC_POL + : 0))); + + fp_horz_vert_active = (((native_mode->vdisplay) & 0xfff) | + (((native_mode->hdisplay / 8) & 0x1ff) << 16)); + break; + case RMX_OFF: + default: + fp_horz_stretch |= ((xres/8-1) << 16); + fp_vert_stretch |= ((yres-1) << 12); + break; + } + + WREG32(RADEON_FP_HORZ_STRETCH, fp_horz_stretch); + WREG32(RADEON_FP_VERT_STRETCH, fp_vert_stretch); + WREG32(RADEON_CRTC_MORE_CNTL, crtc_more_cntl); + WREG32(RADEON_FP_HORZ_VERT_ACTIVE, fp_horz_vert_active); + WREG32(RADEON_FP_H_SYNC_STRT_WID, fp_h_sync_strt_wid); + WREG32(RADEON_FP_V_SYNC_STRT_WID, fp_v_sync_strt_wid); + WREG32(RADEON_FP_CRTC_H_TOTAL_DISP, fp_crtc_h_total_disp); + WREG32(RADEON_FP_CRTC_V_TOTAL_DISP, fp_crtc_v_total_disp); +} + +static void radeon_pll_wait_for_read_update_complete(struct drm_device *dev) +{ + struct radeon_device *rdev = dev->dev_private; + int i = 0; + + /* FIXME: Certain revisions of R300 can't recover here. Not sure of + the cause yet, but this workaround will mask the problem for now. + Other chips usually will pass at the very first test, so the + workaround shouldn't have any effect on them. */ + for (i = 0; + (i < 10000 && + RREG32_PLL(RADEON_PPLL_REF_DIV) & RADEON_PPLL_ATOMIC_UPDATE_R); + i++); +} + +static void radeon_pll_write_update(struct drm_device *dev) +{ + struct radeon_device *rdev = dev->dev_private; + + while (RREG32_PLL(RADEON_PPLL_REF_DIV) & RADEON_PPLL_ATOMIC_UPDATE_R); + + WREG32_PLL_P(RADEON_PPLL_REF_DIV, + RADEON_PPLL_ATOMIC_UPDATE_W, + ~(RADEON_PPLL_ATOMIC_UPDATE_W)); +} + +static void radeon_pll2_wait_for_read_update_complete(struct drm_device *dev) +{ + struct radeon_device *rdev = dev->dev_private; + int i = 0; + + + /* FIXME: Certain revisions of R300 can't recover here. Not sure of + the cause yet, but this workaround will mask the problem for now. + Other chips usually will pass at the very first test, so the + workaround shouldn't have any effect on them. */ + for (i = 0; + (i < 10000 && + RREG32_PLL(RADEON_P2PLL_REF_DIV) & RADEON_P2PLL_ATOMIC_UPDATE_R); + i++); +} + +static void radeon_pll2_write_update(struct drm_device *dev) +{ + struct radeon_device *rdev = dev->dev_private; + + while (RREG32_PLL(RADEON_P2PLL_REF_DIV) & RADEON_P2PLL_ATOMIC_UPDATE_R); + + WREG32_PLL_P(RADEON_P2PLL_REF_DIV, + RADEON_P2PLL_ATOMIC_UPDATE_W, + ~(RADEON_P2PLL_ATOMIC_UPDATE_W)); +} + +static uint8_t radeon_compute_pll_gain(uint16_t ref_freq, uint16_t ref_div, + uint16_t fb_div) +{ + unsigned int vcoFreq; + + if (!ref_div) + return 1; + + vcoFreq = ((unsigned)ref_freq * fb_div) / ref_div; + + /* + * This is horribly crude: the VCO frequency range is divided into + * 3 parts, each part having a fixed PLL gain value. + */ + if (vcoFreq >= 30000) + /* + * [300..max] MHz : 7 + */ + return 7; + else if (vcoFreq >= 18000) + /* + * [180..300) MHz : 4 + */ + return 4; + else + /* + * [0..180) MHz : 1 + */ + return 1; +} + +static void radeon_crtc_dpms(struct drm_crtc *crtc, int mode) +{ + struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); + struct drm_device *dev = crtc->dev; + struct radeon_device *rdev = dev->dev_private; + uint32_t crtc_ext_cntl = 0; + uint32_t mask; + + if (radeon_crtc->crtc_id) + mask = (RADEON_CRTC2_DISP_DIS | + RADEON_CRTC2_VSYNC_DIS | + RADEON_CRTC2_HSYNC_DIS | + RADEON_CRTC2_DISP_REQ_EN_B); + else + mask = (RADEON_CRTC_DISPLAY_DIS | + RADEON_CRTC_VSYNC_DIS | + RADEON_CRTC_HSYNC_DIS); + + /* + * On all dual CRTC GPUs this bit controls the CRTC of the primary DAC. + * Therefore it is set in the DAC DMPS function. + * This is different for GPU's with a single CRTC but a primary and a + * TV DAC: here it controls the single CRTC no matter where it is + * routed. Therefore we set it here. + */ + if (rdev->flags & RADEON_SINGLE_CRTC) + crtc_ext_cntl = RADEON_CRTC_CRT_ON; + + switch (mode) { + case DRM_MODE_DPMS_ON: + radeon_crtc->enabled = true; + /* adjust pm to dpms changes BEFORE enabling crtcs */ + radeon_pm_compute_clocks(rdev); + if (radeon_crtc->crtc_id) + WREG32_P(RADEON_CRTC2_GEN_CNTL, RADEON_CRTC2_EN, ~(RADEON_CRTC2_EN | mask)); + else { + WREG32_P(RADEON_CRTC_GEN_CNTL, RADEON_CRTC_EN, ~(RADEON_CRTC_EN | + RADEON_CRTC_DISP_REQ_EN_B)); + WREG32_P(RADEON_CRTC_EXT_CNTL, crtc_ext_cntl, ~(mask | crtc_ext_cntl)); + } + drm_vblank_post_modeset(dev, radeon_crtc->crtc_id); + radeon_crtc_load_lut(crtc); + break; + case DRM_MODE_DPMS_STANDBY: + case DRM_MODE_DPMS_SUSPEND: + case DRM_MODE_DPMS_OFF: + drm_vblank_pre_modeset(dev, radeon_crtc->crtc_id); + if (radeon_crtc->crtc_id) + WREG32_P(RADEON_CRTC2_GEN_CNTL, mask, ~(RADEON_CRTC2_EN | mask)); + else { + WREG32_P(RADEON_CRTC_GEN_CNTL, RADEON_CRTC_DISP_REQ_EN_B, ~(RADEON_CRTC_EN | + RADEON_CRTC_DISP_REQ_EN_B)); + WREG32_P(RADEON_CRTC_EXT_CNTL, mask, ~(mask | crtc_ext_cntl)); + } + radeon_crtc->enabled = false; + /* adjust pm to dpms changes AFTER disabling crtcs */ + radeon_pm_compute_clocks(rdev); + break; + } +} + +int radeon_crtc_set_base(struct drm_crtc *crtc, int x, int y, + struct drm_framebuffer *old_fb) +{ + return radeon_crtc_do_set_base(crtc, old_fb, x, y, 0); +} + +int radeon_crtc_set_base_atomic(struct drm_crtc *crtc, + struct drm_framebuffer *fb, + int x, int y, enum mode_set_atomic state) +{ + return radeon_crtc_do_set_base(crtc, fb, x, y, 1); +} + +int radeon_crtc_do_set_base(struct drm_crtc *crtc, + struct drm_framebuffer *fb, + int x, int y, int atomic) +{ + struct drm_device *dev = crtc->dev; + struct radeon_device *rdev = dev->dev_private; + struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); + struct radeon_framebuffer *radeon_fb; + struct drm_framebuffer *target_fb; + struct drm_gem_object *obj; + struct radeon_bo *rbo; + uint64_t base; + uint32_t crtc_offset, crtc_offset_cntl, crtc_tile_x0_y0 = 0; + uint32_t crtc_pitch, pitch_pixels; + uint32_t tiling_flags; + int format; + uint32_t gen_cntl_reg, gen_cntl_val; + int r; + + DRM_DEBUG_KMS("\n"); + /* no fb bound */ + if (!atomic && !crtc->fb) { + DRM_DEBUG_KMS("No FB bound\n"); + return 0; + } + + if (atomic) { + radeon_fb = to_radeon_framebuffer(fb); + target_fb = fb; + } + else { + radeon_fb = to_radeon_framebuffer(crtc->fb); + target_fb = crtc->fb; + } + + switch (target_fb->bits_per_pixel) { + case 8: + format = 2; + break; + case 15: /* 555 */ + format = 3; + break; + case 16: /* 565 */ + format = 4; + break; + case 24: /* RGB */ + format = 5; + break; + case 32: /* xRGB */ + format = 6; + break; + default: + return false; + } + + /* Pin framebuffer & get tilling informations */ + obj = radeon_fb->obj; + rbo = gem_to_radeon_bo(obj); + r = radeon_bo_reserve(rbo, false); + if (unlikely(r != 0)) + return r; + /* Only 27 bit offset for legacy CRTC */ + r = radeon_bo_pin_restricted(rbo, RADEON_GEM_DOMAIN_VRAM, 1 << 27, + &base); + if (unlikely(r != 0)) { + radeon_bo_unreserve(rbo); + return -EINVAL; + } + radeon_bo_get_tiling_flags(rbo, &tiling_flags, NULL); + radeon_bo_unreserve(rbo); + if (tiling_flags & RADEON_TILING_MICRO) + DRM_ERROR("trying to scanout microtiled buffer\n"); + + /* if scanout was in GTT this really wouldn't work */ + /* crtc offset is from display base addr not FB location */ + radeon_crtc->legacy_display_base_addr = rdev->mc.vram_start; + + base -= radeon_crtc->legacy_display_base_addr; + + crtc_offset_cntl = 0; + + pitch_pixels = target_fb->pitches[0] / (target_fb->bits_per_pixel / 8); + crtc_pitch = (((pitch_pixels * target_fb->bits_per_pixel) + + ((target_fb->bits_per_pixel * 8) - 1)) / + (target_fb->bits_per_pixel * 8)); + crtc_pitch |= crtc_pitch << 16; + + crtc_offset_cntl |= RADEON_CRTC_GUI_TRIG_OFFSET_LEFT_EN; + if (tiling_flags & RADEON_TILING_MACRO) { + if (ASIC_IS_R300(rdev)) + crtc_offset_cntl |= (R300_CRTC_X_Y_MODE_EN | + R300_CRTC_MICRO_TILE_BUFFER_DIS | + R300_CRTC_MACRO_TILE_EN); + else + crtc_offset_cntl |= RADEON_CRTC_TILE_EN; + } else { + if (ASIC_IS_R300(rdev)) + crtc_offset_cntl &= ~(R300_CRTC_X_Y_MODE_EN | + R300_CRTC_MICRO_TILE_BUFFER_DIS | + R300_CRTC_MACRO_TILE_EN); + else + crtc_offset_cntl &= ~RADEON_CRTC_TILE_EN; + } + + if (tiling_flags & RADEON_TILING_MACRO) { + if (ASIC_IS_R300(rdev)) { + crtc_tile_x0_y0 = x | (y << 16); + base &= ~0x7ff; + } else { + int byteshift = target_fb->bits_per_pixel >> 4; + int tile_addr = (((y >> 3) * pitch_pixels + x) >> (8 - byteshift)) << 11; + base += tile_addr + ((x << byteshift) % 256) + ((y % 8) << 8); + crtc_offset_cntl |= (y % 16); + } + } else { + int offset = y * pitch_pixels + x; + switch (target_fb->bits_per_pixel) { + case 8: + offset *= 1; + break; + case 15: + case 16: + offset *= 2; + break; + case 24: + offset *= 3; + break; + case 32: + offset *= 4; + break; + default: + return false; + } + base += offset; + } + + base &= ~7; + + if (radeon_crtc->crtc_id == 1) + gen_cntl_reg = RADEON_CRTC2_GEN_CNTL; + else + gen_cntl_reg = RADEON_CRTC_GEN_CNTL; + + gen_cntl_val = RREG32(gen_cntl_reg); + gen_cntl_val &= ~(0xf << 8); + gen_cntl_val |= (format << 8); + gen_cntl_val &= ~RADEON_CRTC_VSTAT_MODE_MASK; + WREG32(gen_cntl_reg, gen_cntl_val); + + crtc_offset = (u32)base; + + WREG32(RADEON_DISPLAY_BASE_ADDR + radeon_crtc->crtc_offset, radeon_crtc->legacy_display_base_addr); + + if (ASIC_IS_R300(rdev)) { + if (radeon_crtc->crtc_id) + WREG32(R300_CRTC2_TILE_X0_Y0, crtc_tile_x0_y0); + else + WREG32(R300_CRTC_TILE_X0_Y0, crtc_tile_x0_y0); + } + WREG32(RADEON_CRTC_OFFSET_CNTL + radeon_crtc->crtc_offset, crtc_offset_cntl); + WREG32(RADEON_CRTC_OFFSET + radeon_crtc->crtc_offset, crtc_offset); + WREG32(RADEON_CRTC_PITCH + radeon_crtc->crtc_offset, crtc_pitch); + + if (!atomic && fb && fb != crtc->fb) { + radeon_fb = to_radeon_framebuffer(fb); + rbo = gem_to_radeon_bo(radeon_fb->obj); + r = radeon_bo_reserve(rbo, false); + if (unlikely(r != 0)) + return r; + radeon_bo_unpin(rbo); + radeon_bo_unreserve(rbo); + } + + /* Bytes per pixel may have changed */ + radeon_bandwidth_update(rdev); + + return 0; +} + +static bool radeon_set_crtc_timing(struct drm_crtc *crtc, struct drm_display_mode *mode) +{ + struct drm_device *dev = crtc->dev; + struct radeon_device *rdev = dev->dev_private; + struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); + struct drm_encoder *encoder; + int format; + int hsync_start; + int hsync_wid; + int vsync_wid; + uint32_t crtc_h_total_disp; + uint32_t crtc_h_sync_strt_wid; + uint32_t crtc_v_total_disp; + uint32_t crtc_v_sync_strt_wid; + bool is_tv = false; + + DRM_DEBUG_KMS("\n"); + list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { + if (encoder->crtc == crtc) { + struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); + if (radeon_encoder->active_device & ATOM_DEVICE_TV_SUPPORT) { + is_tv = true; + DRM_INFO("crtc %d is connected to a TV\n", radeon_crtc->crtc_id); + break; + } + } + } + + switch (crtc->fb->bits_per_pixel) { + case 8: + format = 2; + break; + case 15: /* 555 */ + format = 3; + break; + case 16: /* 565 */ + format = 4; + break; + case 24: /* RGB */ + format = 5; + break; + case 32: /* xRGB */ + format = 6; + break; + default: + return false; + } + + crtc_h_total_disp = ((((mode->crtc_htotal / 8) - 1) & 0x3ff) + | ((((mode->crtc_hdisplay / 8) - 1) & 0x1ff) << 16)); + + hsync_wid = (mode->crtc_hsync_end - mode->crtc_hsync_start) / 8; + if (!hsync_wid) + hsync_wid = 1; + hsync_start = mode->crtc_hsync_start - 8; + + crtc_h_sync_strt_wid = ((hsync_start & 0x1fff) + | ((hsync_wid & 0x3f) << 16) + | ((mode->flags & DRM_MODE_FLAG_NHSYNC) + ? RADEON_CRTC_H_SYNC_POL + : 0)); + + /* This works for double scan mode. */ + crtc_v_total_disp = (((mode->crtc_vtotal - 1) & 0xffff) + | ((mode->crtc_vdisplay - 1) << 16)); + + vsync_wid = mode->crtc_vsync_end - mode->crtc_vsync_start; + if (!vsync_wid) + vsync_wid = 1; + + crtc_v_sync_strt_wid = (((mode->crtc_vsync_start - 1) & 0xfff) + | ((vsync_wid & 0x1f) << 16) + | ((mode->flags & DRM_MODE_FLAG_NVSYNC) + ? RADEON_CRTC_V_SYNC_POL + : 0)); + + if (radeon_crtc->crtc_id) { + uint32_t crtc2_gen_cntl; + uint32_t disp2_merge_cntl; + + /* if TV DAC is enabled for another crtc and keep it enabled */ + crtc2_gen_cntl = RREG32(RADEON_CRTC2_GEN_CNTL) & 0x00718080; + crtc2_gen_cntl |= ((format << 8) + | RADEON_CRTC2_VSYNC_DIS + | RADEON_CRTC2_HSYNC_DIS + | RADEON_CRTC2_DISP_DIS + | RADEON_CRTC2_DISP_REQ_EN_B + | ((mode->flags & DRM_MODE_FLAG_DBLSCAN) + ? RADEON_CRTC2_DBL_SCAN_EN + : 0) + | ((mode->flags & DRM_MODE_FLAG_CSYNC) + ? RADEON_CRTC2_CSYNC_EN + : 0) + | ((mode->flags & DRM_MODE_FLAG_INTERLACE) + ? RADEON_CRTC2_INTERLACE_EN + : 0)); + + /* rs4xx chips seem to like to have the crtc enabled when the timing is set */ + if ((rdev->family == CHIP_RS400) || (rdev->family == CHIP_RS480)) + crtc2_gen_cntl |= RADEON_CRTC2_EN; + + disp2_merge_cntl = RREG32(RADEON_DISP2_MERGE_CNTL); + disp2_merge_cntl &= ~RADEON_DISP2_RGB_OFFSET_EN; + + WREG32(RADEON_DISP2_MERGE_CNTL, disp2_merge_cntl); + WREG32(RADEON_CRTC2_GEN_CNTL, crtc2_gen_cntl); + + WREG32(RADEON_FP_H2_SYNC_STRT_WID, crtc_h_sync_strt_wid); + WREG32(RADEON_FP_V2_SYNC_STRT_WID, crtc_v_sync_strt_wid); + } else { + uint32_t crtc_gen_cntl; + uint32_t crtc_ext_cntl; + uint32_t disp_merge_cntl; + + crtc_gen_cntl = RREG32(RADEON_CRTC_GEN_CNTL) & 0x00718000; + crtc_gen_cntl |= (RADEON_CRTC_EXT_DISP_EN + | (format << 8) + | RADEON_CRTC_DISP_REQ_EN_B + | ((mode->flags & DRM_MODE_FLAG_DBLSCAN) + ? RADEON_CRTC_DBL_SCAN_EN + : 0) + | ((mode->flags & DRM_MODE_FLAG_CSYNC) + ? RADEON_CRTC_CSYNC_EN + : 0) + | ((mode->flags & DRM_MODE_FLAG_INTERLACE) + ? RADEON_CRTC_INTERLACE_EN + : 0)); + + /* rs4xx chips seem to like to have the crtc enabled when the timing is set */ + if ((rdev->family == CHIP_RS400) || (rdev->family == CHIP_RS480)) + crtc_gen_cntl |= RADEON_CRTC_EN; + + crtc_ext_cntl = RREG32(RADEON_CRTC_EXT_CNTL); + crtc_ext_cntl |= (RADEON_XCRT_CNT_EN | + RADEON_CRTC_VSYNC_DIS | + RADEON_CRTC_HSYNC_DIS | + RADEON_CRTC_DISPLAY_DIS); + + disp_merge_cntl = RREG32(RADEON_DISP_MERGE_CNTL); + disp_merge_cntl &= ~RADEON_DISP_RGB_OFFSET_EN; + + WREG32(RADEON_DISP_MERGE_CNTL, disp_merge_cntl); + WREG32(RADEON_CRTC_GEN_CNTL, crtc_gen_cntl); + WREG32(RADEON_CRTC_EXT_CNTL, crtc_ext_cntl); + } + + if (is_tv) + radeon_legacy_tv_adjust_crtc_reg(encoder, &crtc_h_total_disp, + &crtc_h_sync_strt_wid, &crtc_v_total_disp, + &crtc_v_sync_strt_wid); + + WREG32(RADEON_CRTC_H_TOTAL_DISP + radeon_crtc->crtc_offset, crtc_h_total_disp); + WREG32(RADEON_CRTC_H_SYNC_STRT_WID + radeon_crtc->crtc_offset, crtc_h_sync_strt_wid); + WREG32(RADEON_CRTC_V_TOTAL_DISP + radeon_crtc->crtc_offset, crtc_v_total_disp); + WREG32(RADEON_CRTC_V_SYNC_STRT_WID + radeon_crtc->crtc_offset, crtc_v_sync_strt_wid); + + return true; +} + +static void radeon_set_pll(struct drm_crtc *crtc, struct drm_display_mode *mode) +{ + struct drm_device *dev = crtc->dev; + struct radeon_device *rdev = dev->dev_private; + struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); + struct drm_encoder *encoder; + uint32_t feedback_div = 0; + uint32_t frac_fb_div = 0; + uint32_t reference_div = 0; + uint32_t post_divider = 0; + uint32_t freq = 0; + uint8_t pll_gain; + bool use_bios_divs = false; + /* PLL registers */ + uint32_t pll_ref_div = 0; + uint32_t pll_fb_post_div = 0; + uint32_t htotal_cntl = 0; + bool is_tv = false; + struct radeon_pll *pll; + + struct { + int divider; + int bitvalue; + } *post_div, post_divs[] = { + /* From RAGE 128 VR/RAGE 128 GL Register + * Reference Manual (Technical Reference + * Manual P/N RRG-G04100-C Rev. 0.04), page + * 3-17 (PLL_DIV_[3:0]). + */ + { 1, 0 }, /* VCLK_SRC */ + { 2, 1 }, /* VCLK_SRC/2 */ + { 4, 2 }, /* VCLK_SRC/4 */ + { 8, 3 }, /* VCLK_SRC/8 */ + { 3, 4 }, /* VCLK_SRC/3 */ + { 16, 5 }, /* VCLK_SRC/16 */ + { 6, 6 }, /* VCLK_SRC/6 */ + { 12, 7 }, /* VCLK_SRC/12 */ + { 0, 0 } + }; + + if (radeon_crtc->crtc_id) + pll = &rdev->clock.p2pll; + else + pll = &rdev->clock.p1pll; + + pll->flags = RADEON_PLL_LEGACY; + + if (mode->clock > 200000) /* range limits??? */ + pll->flags |= RADEON_PLL_PREFER_HIGH_FB_DIV; + else + pll->flags |= RADEON_PLL_PREFER_LOW_REF_DIV; + + list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { + if (encoder->crtc == crtc) { + struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); + + if (radeon_encoder->active_device & ATOM_DEVICE_TV_SUPPORT) { + is_tv = true; + break; + } + + if (encoder->encoder_type != DRM_MODE_ENCODER_DAC) + pll->flags |= RADEON_PLL_NO_ODD_POST_DIV; + if (encoder->encoder_type == DRM_MODE_ENCODER_LVDS) { + if (!rdev->is_atom_bios) { + struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); + struct radeon_encoder_lvds *lvds = (struct radeon_encoder_lvds *)radeon_encoder->enc_priv; + if (lvds) { + if (lvds->use_bios_dividers) { + pll_ref_div = lvds->panel_ref_divider; + pll_fb_post_div = (lvds->panel_fb_divider | + (lvds->panel_post_divider << 16)); + htotal_cntl = 0; + use_bios_divs = true; + } + } + } + pll->flags |= RADEON_PLL_USE_REF_DIV; + } + } + } + + DRM_DEBUG_KMS("\n"); + + if (!use_bios_divs) { + radeon_compute_pll_legacy(pll, mode->clock, + &freq, &feedback_div, &frac_fb_div, + &reference_div, &post_divider); + + for (post_div = &post_divs[0]; post_div->divider; ++post_div) { + if (post_div->divider == post_divider) + break; + } + + if (!post_div->divider) + post_div = &post_divs[0]; + + DRM_DEBUG_KMS("dc=%u, fd=%d, rd=%d, pd=%d\n", + (unsigned)freq, + feedback_div, + reference_div, + post_divider); + + pll_ref_div = reference_div; +#if defined(__powerpc__) && (0) /* TODO */ + /* apparently programming this otherwise causes a hang??? */ + if (info->MacModel == RADEON_MAC_IBOOK) + pll_fb_post_div = 0x000600ad; + else +#endif + pll_fb_post_div = (feedback_div | (post_div->bitvalue << 16)); + + htotal_cntl = mode->htotal & 0x7; + + } + + pll_gain = radeon_compute_pll_gain(pll->reference_freq, + pll_ref_div & 0x3ff, + pll_fb_post_div & 0x7ff); + + if (radeon_crtc->crtc_id) { + uint32_t pixclks_cntl = ((RREG32_PLL(RADEON_PIXCLKS_CNTL) & + ~(RADEON_PIX2CLK_SRC_SEL_MASK)) | + RADEON_PIX2CLK_SRC_SEL_P2PLLCLK); + + if (is_tv) { + radeon_legacy_tv_adjust_pll2(encoder, &htotal_cntl, + &pll_ref_div, &pll_fb_post_div, + &pixclks_cntl); + } + + WREG32_PLL_P(RADEON_PIXCLKS_CNTL, + RADEON_PIX2CLK_SRC_SEL_CPUCLK, + ~(RADEON_PIX2CLK_SRC_SEL_MASK)); + + WREG32_PLL_P(RADEON_P2PLL_CNTL, + RADEON_P2PLL_RESET + | RADEON_P2PLL_ATOMIC_UPDATE_EN + | ((uint32_t)pll_gain << RADEON_P2PLL_PVG_SHIFT), + ~(RADEON_P2PLL_RESET + | RADEON_P2PLL_ATOMIC_UPDATE_EN + | RADEON_P2PLL_PVG_MASK)); + + WREG32_PLL_P(RADEON_P2PLL_REF_DIV, + pll_ref_div, + ~RADEON_P2PLL_REF_DIV_MASK); + + WREG32_PLL_P(RADEON_P2PLL_DIV_0, + pll_fb_post_div, + ~RADEON_P2PLL_FB0_DIV_MASK); + + WREG32_PLL_P(RADEON_P2PLL_DIV_0, + pll_fb_post_div, + ~RADEON_P2PLL_POST0_DIV_MASK); + + radeon_pll2_write_update(dev); + radeon_pll2_wait_for_read_update_complete(dev); + + WREG32_PLL(RADEON_HTOTAL2_CNTL, htotal_cntl); + + WREG32_PLL_P(RADEON_P2PLL_CNTL, + 0, + ~(RADEON_P2PLL_RESET + | RADEON_P2PLL_SLEEP + | RADEON_P2PLL_ATOMIC_UPDATE_EN)); + + DRM_DEBUG_KMS("Wrote2: 0x%08x 0x%08x 0x%08x (0x%08x)\n", + (unsigned)pll_ref_div, + (unsigned)pll_fb_post_div, + (unsigned)htotal_cntl, + RREG32_PLL(RADEON_P2PLL_CNTL)); + DRM_DEBUG_KMS("Wrote2: rd=%u, fd=%u, pd=%u\n", + (unsigned)pll_ref_div & RADEON_P2PLL_REF_DIV_MASK, + (unsigned)pll_fb_post_div & RADEON_P2PLL_FB0_DIV_MASK, + (unsigned)((pll_fb_post_div & + RADEON_P2PLL_POST0_DIV_MASK) >> 16)); + + DRM_MDELAY(50); /* Let the clock to lock */ + + WREG32_PLL_P(RADEON_PIXCLKS_CNTL, + RADEON_PIX2CLK_SRC_SEL_P2PLLCLK, + ~(RADEON_PIX2CLK_SRC_SEL_MASK)); + + WREG32_PLL(RADEON_PIXCLKS_CNTL, pixclks_cntl); + } else { + uint32_t pixclks_cntl; + + + if (is_tv) { + pixclks_cntl = RREG32_PLL(RADEON_PIXCLKS_CNTL); + radeon_legacy_tv_adjust_pll1(encoder, &htotal_cntl, &pll_ref_div, + &pll_fb_post_div, &pixclks_cntl); + } + + if (rdev->flags & RADEON_IS_MOBILITY) { + /* A temporal workaround for the occasional blanking on certain laptop panels. + This appears to related to the PLL divider registers (fail to lock?). + It occurs even when all dividers are the same with their old settings. + In this case we really don't need to fiddle with PLL registers. + By doing this we can avoid the blanking problem with some panels. + */ + if ((pll_ref_div == (RREG32_PLL(RADEON_PPLL_REF_DIV) & RADEON_PPLL_REF_DIV_MASK)) && + (pll_fb_post_div == (RREG32_PLL(RADEON_PPLL_DIV_3) & + (RADEON_PPLL_POST3_DIV_MASK | RADEON_PPLL_FB3_DIV_MASK)))) { + WREG32_P(RADEON_CLOCK_CNTL_INDEX, + RADEON_PLL_DIV_SEL, + ~(RADEON_PLL_DIV_SEL)); + r100_pll_errata_after_index(rdev); + return; + } + } + + WREG32_PLL_P(RADEON_VCLK_ECP_CNTL, + RADEON_VCLK_SRC_SEL_CPUCLK, + ~(RADEON_VCLK_SRC_SEL_MASK)); + WREG32_PLL_P(RADEON_PPLL_CNTL, + RADEON_PPLL_RESET + | RADEON_PPLL_ATOMIC_UPDATE_EN + | RADEON_PPLL_VGA_ATOMIC_UPDATE_EN + | ((uint32_t)pll_gain << RADEON_PPLL_PVG_SHIFT), + ~(RADEON_PPLL_RESET + | RADEON_PPLL_ATOMIC_UPDATE_EN + | RADEON_PPLL_VGA_ATOMIC_UPDATE_EN + | RADEON_PPLL_PVG_MASK)); + + WREG32_P(RADEON_CLOCK_CNTL_INDEX, + RADEON_PLL_DIV_SEL, + ~(RADEON_PLL_DIV_SEL)); + r100_pll_errata_after_index(rdev); + + if (ASIC_IS_R300(rdev) || + (rdev->family == CHIP_RS300) || + (rdev->family == CHIP_RS400) || + (rdev->family == CHIP_RS480)) { + if (pll_ref_div & R300_PPLL_REF_DIV_ACC_MASK) { + /* When restoring console mode, use saved PPLL_REF_DIV + * setting. + */ + WREG32_PLL_P(RADEON_PPLL_REF_DIV, + pll_ref_div, + 0); + } else { + /* R300 uses ref_div_acc field as real ref divider */ + WREG32_PLL_P(RADEON_PPLL_REF_DIV, + (pll_ref_div << R300_PPLL_REF_DIV_ACC_SHIFT), + ~R300_PPLL_REF_DIV_ACC_MASK); + } + } else + WREG32_PLL_P(RADEON_PPLL_REF_DIV, + pll_ref_div, + ~RADEON_PPLL_REF_DIV_MASK); + + WREG32_PLL_P(RADEON_PPLL_DIV_3, + pll_fb_post_div, + ~RADEON_PPLL_FB3_DIV_MASK); + + WREG32_PLL_P(RADEON_PPLL_DIV_3, + pll_fb_post_div, + ~RADEON_PPLL_POST3_DIV_MASK); + + radeon_pll_write_update(dev); + radeon_pll_wait_for_read_update_complete(dev); + + WREG32_PLL(RADEON_HTOTAL_CNTL, htotal_cntl); + + WREG32_PLL_P(RADEON_PPLL_CNTL, + 0, + ~(RADEON_PPLL_RESET + | RADEON_PPLL_SLEEP + | RADEON_PPLL_ATOMIC_UPDATE_EN + | RADEON_PPLL_VGA_ATOMIC_UPDATE_EN)); + + DRM_DEBUG_KMS("Wrote: 0x%08x 0x%08x 0x%08x (0x%08x)\n", + pll_ref_div, + pll_fb_post_div, + (unsigned)htotal_cntl, + RREG32_PLL(RADEON_PPLL_CNTL)); + DRM_DEBUG_KMS("Wrote: rd=%d, fd=%d, pd=%d\n", + pll_ref_div & RADEON_PPLL_REF_DIV_MASK, + pll_fb_post_div & RADEON_PPLL_FB3_DIV_MASK, + (pll_fb_post_div & RADEON_PPLL_POST3_DIV_MASK) >> 16); + + DRM_MDELAY(50); /* Let the clock to lock */ + + WREG32_PLL_P(RADEON_VCLK_ECP_CNTL, + RADEON_VCLK_SRC_SEL_PPLLCLK, + ~(RADEON_VCLK_SRC_SEL_MASK)); + + if (is_tv) + WREG32_PLL(RADEON_PIXCLKS_CNTL, pixclks_cntl); + } +} + +static bool radeon_crtc_mode_fixup(struct drm_crtc *crtc, + const struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + if (!radeon_crtc_scaling_mode_fixup(crtc, mode, adjusted_mode)) + return false; + return true; +} + +static int radeon_crtc_mode_set(struct drm_crtc *crtc, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode, + int x, int y, struct drm_framebuffer *old_fb) +{ + struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); + + /* TODO TV */ + radeon_crtc_set_base(crtc, x, y, old_fb); + radeon_set_crtc_timing(crtc, adjusted_mode); + radeon_set_pll(crtc, adjusted_mode); + radeon_overscan_setup(crtc, adjusted_mode); + if (radeon_crtc->crtc_id == 0) { + radeon_legacy_rmx_mode_set(crtc, adjusted_mode); + } else { + if (radeon_crtc->rmx_type != RMX_OFF) { + /* FIXME: only first crtc has rmx what should we + * do ? + */ + DRM_ERROR("Mode need scaling but only first crtc can do that.\n"); + } + } + return 0; +} + +static void radeon_crtc_prepare(struct drm_crtc *crtc) +{ + struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); + struct drm_device *dev = crtc->dev; + struct drm_crtc *crtci; + + radeon_crtc->in_mode_set = true; + /* + * The hardware wedges sometimes if you reconfigure one CRTC + * whilst another is running (see fdo bug #24611). + */ + list_for_each_entry(crtci, &dev->mode_config.crtc_list, head) + radeon_crtc_dpms(crtci, DRM_MODE_DPMS_OFF); +} + +static void radeon_crtc_commit(struct drm_crtc *crtc) +{ + struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); + struct drm_device *dev = crtc->dev; + struct drm_crtc *crtci; + + /* + * Reenable the CRTCs that should be running. + */ + list_for_each_entry(crtci, &dev->mode_config.crtc_list, head) { + if (crtci->enabled) + radeon_crtc_dpms(crtci, DRM_MODE_DPMS_ON); + } + radeon_crtc->in_mode_set = false; +} + +static const struct drm_crtc_helper_funcs legacy_helper_funcs = { + .dpms = radeon_crtc_dpms, + .mode_fixup = radeon_crtc_mode_fixup, + .mode_set = radeon_crtc_mode_set, + .mode_set_base = radeon_crtc_set_base, + .mode_set_base_atomic = radeon_crtc_set_base_atomic, + .prepare = radeon_crtc_prepare, + .commit = radeon_crtc_commit, + .load_lut = radeon_crtc_load_lut, +}; + + +void radeon_legacy_init_crtc(struct drm_device *dev, + struct radeon_crtc *radeon_crtc) +{ + if (radeon_crtc->crtc_id == 1) + radeon_crtc->crtc_offset = RADEON_CRTC2_H_TOTAL_DISP - RADEON_CRTC_H_TOTAL_DISP; + drm_crtc_helper_add(&radeon_crtc->base, &legacy_helper_funcs); +} diff --git a/sys/dev/drm2/radeon/radeon_legacy_encoders.c b/sys/dev/drm2/radeon/radeon_legacy_encoders.c new file mode 100644 index 00000000000..4a1ae163c9d --- /dev/null +++ b/sys/dev/drm2/radeon/radeon_legacy_encoders.c @@ -0,0 +1,1817 @@ +/* + * Copyright 2007-8 Advanced Micro Devices, Inc. + * Copyright 2008 Red Hat Inc. + * + * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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. + * + * Authors: Dave Airlie + * Alex Deucher + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include "radeon.h" +#include "radeon_asic.h" +#include "atom.h" + +static void radeon_legacy_encoder_disable(struct drm_encoder *encoder) +{ + struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); + struct drm_encoder_helper_funcs *encoder_funcs; + + encoder_funcs = encoder->helper_private; + encoder_funcs->dpms(encoder, DRM_MODE_DPMS_OFF); + radeon_encoder->active_device = 0; +} + +static void radeon_legacy_lvds_update(struct drm_encoder *encoder, int mode) +{ + struct drm_device *dev = encoder->dev; + struct radeon_device *rdev = dev->dev_private; + struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); + uint32_t lvds_gen_cntl, lvds_pll_cntl, pixclks_cntl, disp_pwr_man; + int panel_pwr_delay = 2000; + bool is_mac = false; + uint8_t backlight_level; + DRM_DEBUG_KMS("\n"); + + lvds_gen_cntl = RREG32(RADEON_LVDS_GEN_CNTL); + backlight_level = (lvds_gen_cntl >> RADEON_LVDS_BL_MOD_LEVEL_SHIFT) & 0xff; + + if (radeon_encoder->enc_priv) { + if (rdev->is_atom_bios) { + struct radeon_encoder_atom_dig *lvds = radeon_encoder->enc_priv; + panel_pwr_delay = lvds->panel_pwr_delay; + if (lvds->bl_dev) + backlight_level = lvds->backlight_level; + } else { + struct radeon_encoder_lvds *lvds = radeon_encoder->enc_priv; + panel_pwr_delay = lvds->panel_pwr_delay; + if (lvds->bl_dev) + backlight_level = lvds->backlight_level; + } + } + + /* macs (and possibly some x86 oem systems?) wire up LVDS strangely + * Taken from radeonfb. + */ + if ((rdev->mode_info.connector_table == CT_IBOOK) || + (rdev->mode_info.connector_table == CT_POWERBOOK_EXTERNAL) || + (rdev->mode_info.connector_table == CT_POWERBOOK_INTERNAL) || + (rdev->mode_info.connector_table == CT_POWERBOOK_VGA)) + is_mac = true; + + switch (mode) { + case DRM_MODE_DPMS_ON: + disp_pwr_man = RREG32(RADEON_DISP_PWR_MAN); + disp_pwr_man |= RADEON_AUTO_PWRUP_EN; + WREG32(RADEON_DISP_PWR_MAN, disp_pwr_man); + lvds_pll_cntl = RREG32(RADEON_LVDS_PLL_CNTL); + lvds_pll_cntl |= RADEON_LVDS_PLL_EN; + WREG32(RADEON_LVDS_PLL_CNTL, lvds_pll_cntl); + DRM_MDELAY(1); + + lvds_pll_cntl = RREG32(RADEON_LVDS_PLL_CNTL); + lvds_pll_cntl &= ~RADEON_LVDS_PLL_RESET; + WREG32(RADEON_LVDS_PLL_CNTL, lvds_pll_cntl); + + lvds_gen_cntl &= ~(RADEON_LVDS_DISPLAY_DIS | + RADEON_LVDS_BL_MOD_LEVEL_MASK); + lvds_gen_cntl |= (RADEON_LVDS_ON | RADEON_LVDS_EN | + RADEON_LVDS_DIGON | RADEON_LVDS_BLON | + (backlight_level << RADEON_LVDS_BL_MOD_LEVEL_SHIFT)); + if (is_mac) + lvds_gen_cntl |= RADEON_LVDS_BL_MOD_EN; + DRM_MDELAY(panel_pwr_delay); + WREG32(RADEON_LVDS_GEN_CNTL, lvds_gen_cntl); + break; + case DRM_MODE_DPMS_STANDBY: + case DRM_MODE_DPMS_SUSPEND: + case DRM_MODE_DPMS_OFF: + pixclks_cntl = RREG32_PLL(RADEON_PIXCLKS_CNTL); + WREG32_PLL_P(RADEON_PIXCLKS_CNTL, 0, ~RADEON_PIXCLK_LVDS_ALWAYS_ONb); + lvds_gen_cntl |= RADEON_LVDS_DISPLAY_DIS; + if (is_mac) { + lvds_gen_cntl &= ~RADEON_LVDS_BL_MOD_EN; + WREG32(RADEON_LVDS_GEN_CNTL, lvds_gen_cntl); + lvds_gen_cntl &= ~(RADEON_LVDS_ON | RADEON_LVDS_EN); + } else { + WREG32(RADEON_LVDS_GEN_CNTL, lvds_gen_cntl); + lvds_gen_cntl &= ~(RADEON_LVDS_ON | RADEON_LVDS_BLON | RADEON_LVDS_EN | RADEON_LVDS_DIGON); + } + DRM_MDELAY(panel_pwr_delay); + WREG32(RADEON_LVDS_GEN_CNTL, lvds_gen_cntl); + WREG32_PLL(RADEON_PIXCLKS_CNTL, pixclks_cntl); + DRM_MDELAY(panel_pwr_delay); + break; + } + + if (rdev->is_atom_bios) + radeon_atombios_encoder_dpms_scratch_regs(encoder, (mode == DRM_MODE_DPMS_ON) ? true : false); + else + radeon_combios_encoder_dpms_scratch_regs(encoder, (mode == DRM_MODE_DPMS_ON) ? true : false); + +} + +static void radeon_legacy_lvds_dpms(struct drm_encoder *encoder, int mode) +{ + struct radeon_device *rdev = encoder->dev->dev_private; + struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); + DRM_DEBUG("\n"); + + if (radeon_encoder->enc_priv) { + if (rdev->is_atom_bios) { + struct radeon_encoder_atom_dig *lvds = radeon_encoder->enc_priv; + lvds->dpms_mode = mode; + } else { + struct radeon_encoder_lvds *lvds = radeon_encoder->enc_priv; + lvds->dpms_mode = mode; + } + } + + radeon_legacy_lvds_update(encoder, mode); +} + +static void radeon_legacy_lvds_prepare(struct drm_encoder *encoder) +{ + struct radeon_device *rdev = encoder->dev->dev_private; + + if (rdev->is_atom_bios) + radeon_atom_output_lock(encoder, true); + else + radeon_combios_output_lock(encoder, true); + radeon_legacy_lvds_dpms(encoder, DRM_MODE_DPMS_OFF); +} + +static void radeon_legacy_lvds_commit(struct drm_encoder *encoder) +{ + struct radeon_device *rdev = encoder->dev->dev_private; + + radeon_legacy_lvds_dpms(encoder, DRM_MODE_DPMS_ON); + if (rdev->is_atom_bios) + radeon_atom_output_lock(encoder, false); + else + radeon_combios_output_lock(encoder, false); +} + +static void radeon_legacy_lvds_mode_set(struct drm_encoder *encoder, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + struct drm_device *dev = encoder->dev; + struct radeon_device *rdev = dev->dev_private; + struct radeon_crtc *radeon_crtc = to_radeon_crtc(encoder->crtc); + struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); + uint32_t lvds_pll_cntl, lvds_gen_cntl, lvds_ss_gen_cntl; + + DRM_DEBUG_KMS("\n"); + + lvds_pll_cntl = RREG32(RADEON_LVDS_PLL_CNTL); + lvds_pll_cntl &= ~RADEON_LVDS_PLL_EN; + + lvds_ss_gen_cntl = RREG32(RADEON_LVDS_SS_GEN_CNTL); + if (rdev->is_atom_bios) { + /* LVDS_GEN_CNTL parameters are computed in LVDSEncoderControl + * need to call that on resume to set up the reg properly. + */ + radeon_encoder->pixel_clock = adjusted_mode->clock; + atombios_digital_setup(encoder, PANEL_ENCODER_ACTION_ENABLE); + lvds_gen_cntl = RREG32(RADEON_LVDS_GEN_CNTL); + } else { + struct radeon_encoder_lvds *lvds = (struct radeon_encoder_lvds *)radeon_encoder->enc_priv; + if (lvds) { + DRM_DEBUG_KMS("bios LVDS_GEN_CNTL: 0x%x\n", lvds->lvds_gen_cntl); + lvds_gen_cntl = lvds->lvds_gen_cntl; + lvds_ss_gen_cntl &= ~((0xf << RADEON_LVDS_PWRSEQ_DELAY1_SHIFT) | + (0xf << RADEON_LVDS_PWRSEQ_DELAY2_SHIFT)); + lvds_ss_gen_cntl |= ((lvds->panel_digon_delay << RADEON_LVDS_PWRSEQ_DELAY1_SHIFT) | + (lvds->panel_blon_delay << RADEON_LVDS_PWRSEQ_DELAY2_SHIFT)); + } else + lvds_gen_cntl = RREG32(RADEON_LVDS_GEN_CNTL); + } + lvds_gen_cntl |= RADEON_LVDS_DISPLAY_DIS; + lvds_gen_cntl &= ~(RADEON_LVDS_ON | + RADEON_LVDS_BLON | + RADEON_LVDS_EN | + RADEON_LVDS_RST_FM); + + if (ASIC_IS_R300(rdev)) + lvds_pll_cntl &= ~(R300_LVDS_SRC_SEL_MASK); + + if (radeon_crtc->crtc_id == 0) { + if (ASIC_IS_R300(rdev)) { + if (radeon_encoder->rmx_type != RMX_OFF) + lvds_pll_cntl |= R300_LVDS_SRC_SEL_RMX; + } else + lvds_gen_cntl &= ~RADEON_LVDS_SEL_CRTC2; + } else { + if (ASIC_IS_R300(rdev)) + lvds_pll_cntl |= R300_LVDS_SRC_SEL_CRTC2; + else + lvds_gen_cntl |= RADEON_LVDS_SEL_CRTC2; + } + + WREG32(RADEON_LVDS_GEN_CNTL, lvds_gen_cntl); + WREG32(RADEON_LVDS_PLL_CNTL, lvds_pll_cntl); + WREG32(RADEON_LVDS_SS_GEN_CNTL, lvds_ss_gen_cntl); + + if (rdev->family == CHIP_RV410) + WREG32(RADEON_CLOCK_CNTL_INDEX, 0); + + if (rdev->is_atom_bios) + radeon_atombios_encoder_crtc_scratch_regs(encoder, radeon_crtc->crtc_id); + else + radeon_combios_encoder_crtc_scratch_regs(encoder, radeon_crtc->crtc_id); +} + +static bool radeon_legacy_mode_fixup(struct drm_encoder *encoder, + const struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); + + /* set the active encoder to connector routing */ + radeon_encoder_set_active_device(encoder); + drm_mode_set_crtcinfo(adjusted_mode, 0); + + /* get the native mode for LVDS */ + if (radeon_encoder->active_device & (ATOM_DEVICE_LCD_SUPPORT)) + radeon_panel_mode_fixup(encoder, adjusted_mode); + + return true; +} + +static const struct drm_encoder_helper_funcs radeon_legacy_lvds_helper_funcs = { + .dpms = radeon_legacy_lvds_dpms, + .mode_fixup = radeon_legacy_mode_fixup, + .prepare = radeon_legacy_lvds_prepare, + .mode_set = radeon_legacy_lvds_mode_set, + .commit = radeon_legacy_lvds_commit, + .disable = radeon_legacy_encoder_disable, +}; + +u8 +radeon_legacy_get_backlight_level(struct radeon_encoder *radeon_encoder) +{ + struct drm_device *dev = radeon_encoder->base.dev; + struct radeon_device *rdev = dev->dev_private; + u8 backlight_level; + + backlight_level = (RREG32(RADEON_LVDS_GEN_CNTL) >> + RADEON_LVDS_BL_MOD_LEVEL_SHIFT) & 0xff; + + return backlight_level; +} + +void +radeon_legacy_set_backlight_level(struct radeon_encoder *radeon_encoder, u8 level) +{ + struct drm_device *dev = radeon_encoder->base.dev; + struct radeon_device *rdev = dev->dev_private; + int dpms_mode = DRM_MODE_DPMS_ON; + + if (radeon_encoder->enc_priv) { + if (rdev->is_atom_bios) { + struct radeon_encoder_atom_dig *lvds = radeon_encoder->enc_priv; + if (lvds->backlight_level > 0) + dpms_mode = lvds->dpms_mode; + else + dpms_mode = DRM_MODE_DPMS_OFF; + lvds->backlight_level = level; + } else { + struct radeon_encoder_lvds *lvds = radeon_encoder->enc_priv; + if (lvds->backlight_level > 0) + dpms_mode = lvds->dpms_mode; + else + dpms_mode = DRM_MODE_DPMS_OFF; + lvds->backlight_level = level; + } + } + + radeon_legacy_lvds_update(&radeon_encoder->base, dpms_mode); +} + +#if defined(CONFIG_BACKLIGHT_CLASS_DEVICE) || defined(CONFIG_BACKLIGHT_CLASS_DEVICE_MODULE) + +static uint8_t radeon_legacy_lvds_level(struct backlight_device *bd) +{ + struct radeon_backlight_privdata *pdata = bl_get_data(bd); + uint8_t level; + + /* Convert brightness to hardware level */ + if (bd->props.brightness < 0) + level = 0; + else if (bd->props.brightness > RADEON_MAX_BL_LEVEL) + level = RADEON_MAX_BL_LEVEL; + else + level = bd->props.brightness; + + if (pdata->negative) + level = RADEON_MAX_BL_LEVEL - level; + + return level; +} + +static int radeon_legacy_backlight_update_status(struct backlight_device *bd) +{ + struct radeon_backlight_privdata *pdata = bl_get_data(bd); + struct radeon_encoder *radeon_encoder = pdata->encoder; + + radeon_legacy_set_backlight_level(radeon_encoder, + radeon_legacy_lvds_level(bd)); + + return 0; +} + +static int radeon_legacy_backlight_get_brightness(struct backlight_device *bd) +{ + struct radeon_backlight_privdata *pdata = bl_get_data(bd); + struct radeon_encoder *radeon_encoder = pdata->encoder; + struct drm_device *dev = radeon_encoder->base.dev; + struct radeon_device *rdev = dev->dev_private; + uint8_t backlight_level; + + backlight_level = (RREG32(RADEON_LVDS_GEN_CNTL) >> + RADEON_LVDS_BL_MOD_LEVEL_SHIFT) & 0xff; + + return pdata->negative ? RADEON_MAX_BL_LEVEL - backlight_level : backlight_level; +} + +static const struct backlight_ops radeon_backlight_ops = { + .get_brightness = radeon_legacy_backlight_get_brightness, + .update_status = radeon_legacy_backlight_update_status, +}; + +void radeon_legacy_backlight_init(struct radeon_encoder *radeon_encoder, + struct drm_connector *drm_connector) +{ + struct drm_device *dev = radeon_encoder->base.dev; + struct radeon_device *rdev = dev->dev_private; + struct backlight_device *bd; + struct backlight_properties props; + struct radeon_backlight_privdata *pdata; + uint8_t backlight_level; + char bl_name[16]; + + if (!radeon_encoder->enc_priv) + return; + +#ifdef CONFIG_PMAC_BACKLIGHT + if (!pmac_has_backlight_type("ati") && + !pmac_has_backlight_type("mnca")) + return; +#endif + + pdata = malloc(sizeof(struct radeon_backlight_privdata), + DRM_MEM_DRIVER, M_WAITOK); + if (!pdata) { + DRM_ERROR("Memory allocation failed\n"); + goto error; + } + + memset(&props, 0, sizeof(props)); + props.max_brightness = RADEON_MAX_BL_LEVEL; + props.type = BACKLIGHT_RAW; + snprintf(bl_name, sizeof(bl_name), + "radeon_bl%d", dev->primary->index); + bd = backlight_device_register(bl_name, &drm_connector->kdev, + pdata, &radeon_backlight_ops, &props); + if (IS_ERR(bd)) { + DRM_ERROR("Backlight registration failed\n"); + goto error; + } + + pdata->encoder = radeon_encoder; + + backlight_level = (RREG32(RADEON_LVDS_GEN_CNTL) >> + RADEON_LVDS_BL_MOD_LEVEL_SHIFT) & 0xff; + + /* First, try to detect backlight level sense based on the assumption + * that firmware set it up at full brightness + */ + if (backlight_level == 0) + pdata->negative = true; + else if (backlight_level == 0xff) + pdata->negative = false; + else { + /* XXX hack... maybe some day we can figure out in what direction + * backlight should work on a given panel? + */ + pdata->negative = (rdev->family != CHIP_RV200 && + rdev->family != CHIP_RV250 && + rdev->family != CHIP_RV280 && + rdev->family != CHIP_RV350); + +#ifdef CONFIG_PMAC_BACKLIGHT + pdata->negative = (pdata->negative || + of_machine_is_compatible("PowerBook4,3") || + of_machine_is_compatible("PowerBook6,3") || + of_machine_is_compatible("PowerBook6,5")); +#endif + } + + if (rdev->is_atom_bios) { + struct radeon_encoder_atom_dig *lvds = radeon_encoder->enc_priv; + lvds->bl_dev = bd; + } else { + struct radeon_encoder_lvds *lvds = radeon_encoder->enc_priv; + lvds->bl_dev = bd; + } + + bd->props.brightness = radeon_legacy_backlight_get_brightness(bd); + bd->props.power = FB_BLANK_UNBLANK; + backlight_update_status(bd); + + DRM_INFO("radeon legacy LVDS backlight initialized\n"); + + return; + +error: + free(pdata, DRM_MEM_DRIVER); + return; +} + +static void radeon_legacy_backlight_exit(struct radeon_encoder *radeon_encoder) +{ + struct drm_device *dev = radeon_encoder->base.dev; + struct radeon_device *rdev = dev->dev_private; + struct backlight_device *bd = NULL; + + if (!radeon_encoder->enc_priv) + return; + + if (rdev->is_atom_bios) { + struct radeon_encoder_atom_dig *lvds = radeon_encoder->enc_priv; + bd = lvds->bl_dev; + lvds->bl_dev = NULL; + } else { + struct radeon_encoder_lvds *lvds = radeon_encoder->enc_priv; + bd = lvds->bl_dev; + lvds->bl_dev = NULL; + } + + if (bd) { + struct radeon_backlight_privdata *pdata; + + pdata = bl_get_data(bd); + backlight_device_unregister(bd); + free(pdata, DRM_MEM_DRIVER); + + DRM_INFO("radeon legacy LVDS backlight unloaded\n"); + } +} + +#else /* !CONFIG_BACKLIGHT_CLASS_DEVICE */ + +void radeon_legacy_backlight_init(struct radeon_encoder *encoder, + struct drm_connector *drm_connector) +{ +} + +static void radeon_legacy_backlight_exit(struct radeon_encoder *encoder) +{ +} + +#endif + + +static void radeon_lvds_enc_destroy(struct drm_encoder *encoder) +{ + struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); + + if (radeon_encoder->enc_priv) { + radeon_legacy_backlight_exit(radeon_encoder); + free(radeon_encoder->enc_priv, DRM_MEM_DRIVER); + } + drm_encoder_cleanup(encoder); + free(radeon_encoder, DRM_MEM_DRIVER); +} + +static const struct drm_encoder_funcs radeon_legacy_lvds_enc_funcs = { + .destroy = radeon_lvds_enc_destroy, +}; + +static void radeon_legacy_primary_dac_dpms(struct drm_encoder *encoder, int mode) +{ + struct drm_device *dev = encoder->dev; + struct radeon_device *rdev = dev->dev_private; + uint32_t crtc_ext_cntl = RREG32(RADEON_CRTC_EXT_CNTL); + uint32_t dac_cntl = RREG32(RADEON_DAC_CNTL); + uint32_t dac_macro_cntl = RREG32(RADEON_DAC_MACRO_CNTL); + + DRM_DEBUG_KMS("\n"); + + switch (mode) { + case DRM_MODE_DPMS_ON: + crtc_ext_cntl |= RADEON_CRTC_CRT_ON; + dac_cntl &= ~RADEON_DAC_PDWN; + dac_macro_cntl &= ~(RADEON_DAC_PDWN_R | + RADEON_DAC_PDWN_G | + RADEON_DAC_PDWN_B); + break; + case DRM_MODE_DPMS_STANDBY: + case DRM_MODE_DPMS_SUSPEND: + case DRM_MODE_DPMS_OFF: + crtc_ext_cntl &= ~RADEON_CRTC_CRT_ON; + dac_cntl |= RADEON_DAC_PDWN; + dac_macro_cntl |= (RADEON_DAC_PDWN_R | + RADEON_DAC_PDWN_G | + RADEON_DAC_PDWN_B); + break; + } + + /* handled in radeon_crtc_dpms() */ + if (!(rdev->flags & RADEON_SINGLE_CRTC)) + WREG32(RADEON_CRTC_EXT_CNTL, crtc_ext_cntl); + WREG32(RADEON_DAC_CNTL, dac_cntl); + WREG32(RADEON_DAC_MACRO_CNTL, dac_macro_cntl); + + if (rdev->is_atom_bios) + radeon_atombios_encoder_dpms_scratch_regs(encoder, (mode == DRM_MODE_DPMS_ON) ? true : false); + else + radeon_combios_encoder_dpms_scratch_regs(encoder, (mode == DRM_MODE_DPMS_ON) ? true : false); + +} + +static void radeon_legacy_primary_dac_prepare(struct drm_encoder *encoder) +{ + struct radeon_device *rdev = encoder->dev->dev_private; + + if (rdev->is_atom_bios) + radeon_atom_output_lock(encoder, true); + else + radeon_combios_output_lock(encoder, true); + radeon_legacy_primary_dac_dpms(encoder, DRM_MODE_DPMS_OFF); +} + +static void radeon_legacy_primary_dac_commit(struct drm_encoder *encoder) +{ + struct radeon_device *rdev = encoder->dev->dev_private; + + radeon_legacy_primary_dac_dpms(encoder, DRM_MODE_DPMS_ON); + + if (rdev->is_atom_bios) + radeon_atom_output_lock(encoder, false); + else + radeon_combios_output_lock(encoder, false); +} + +static void radeon_legacy_primary_dac_mode_set(struct drm_encoder *encoder, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + struct drm_device *dev = encoder->dev; + struct radeon_device *rdev = dev->dev_private; + struct radeon_crtc *radeon_crtc = to_radeon_crtc(encoder->crtc); + struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); + uint32_t disp_output_cntl, dac_cntl, dac2_cntl, dac_macro_cntl; + + DRM_DEBUG_KMS("\n"); + + if (radeon_crtc->crtc_id == 0) { + if (rdev->family == CHIP_R200 || ASIC_IS_R300(rdev)) { + disp_output_cntl = RREG32(RADEON_DISP_OUTPUT_CNTL) & + ~(RADEON_DISP_DAC_SOURCE_MASK); + WREG32(RADEON_DISP_OUTPUT_CNTL, disp_output_cntl); + } else { + dac2_cntl = RREG32(RADEON_DAC_CNTL2) & ~(RADEON_DAC2_DAC_CLK_SEL); + WREG32(RADEON_DAC_CNTL2, dac2_cntl); + } + } else { + if (rdev->family == CHIP_R200 || ASIC_IS_R300(rdev)) { + disp_output_cntl = RREG32(RADEON_DISP_OUTPUT_CNTL) & + ~(RADEON_DISP_DAC_SOURCE_MASK); + disp_output_cntl |= RADEON_DISP_DAC_SOURCE_CRTC2; + WREG32(RADEON_DISP_OUTPUT_CNTL, disp_output_cntl); + } else { + dac2_cntl = RREG32(RADEON_DAC_CNTL2) | RADEON_DAC2_DAC_CLK_SEL; + WREG32(RADEON_DAC_CNTL2, dac2_cntl); + } + } + + dac_cntl = (RADEON_DAC_MASK_ALL | + RADEON_DAC_VGA_ADR_EN | + /* TODO 6-bits */ + RADEON_DAC_8BIT_EN); + + WREG32_P(RADEON_DAC_CNTL, + dac_cntl, + RADEON_DAC_RANGE_CNTL | + RADEON_DAC_BLANKING); + + if (radeon_encoder->enc_priv) { + struct radeon_encoder_primary_dac *p_dac = (struct radeon_encoder_primary_dac *)radeon_encoder->enc_priv; + dac_macro_cntl = p_dac->ps2_pdac_adj; + } else + dac_macro_cntl = RREG32(RADEON_DAC_MACRO_CNTL); + dac_macro_cntl |= RADEON_DAC_PDWN_R | RADEON_DAC_PDWN_G | RADEON_DAC_PDWN_B; + WREG32(RADEON_DAC_MACRO_CNTL, dac_macro_cntl); + + if (rdev->is_atom_bios) + radeon_atombios_encoder_crtc_scratch_regs(encoder, radeon_crtc->crtc_id); + else + radeon_combios_encoder_crtc_scratch_regs(encoder, radeon_crtc->crtc_id); +} + +static enum drm_connector_status radeon_legacy_primary_dac_detect(struct drm_encoder *encoder, + struct drm_connector *connector) +{ + struct drm_device *dev = encoder->dev; + struct radeon_device *rdev = dev->dev_private; + uint32_t vclk_ecp_cntl, crtc_ext_cntl; + uint32_t dac_ext_cntl, dac_cntl, dac_macro_cntl, tmp; + enum drm_connector_status found = connector_status_disconnected; + bool color = true; + + /* just don't bother on RN50 those chip are often connected to remoting + * console hw and often we get failure to load detect those. So to make + * everyone happy report the encoder as always connected. + */ + if (ASIC_IS_RN50(rdev)) { + return connector_status_connected; + } + + /* save the regs we need */ + vclk_ecp_cntl = RREG32_PLL(RADEON_VCLK_ECP_CNTL); + crtc_ext_cntl = RREG32(RADEON_CRTC_EXT_CNTL); + dac_ext_cntl = RREG32(RADEON_DAC_EXT_CNTL); + dac_cntl = RREG32(RADEON_DAC_CNTL); + dac_macro_cntl = RREG32(RADEON_DAC_MACRO_CNTL); + + tmp = vclk_ecp_cntl & + ~(RADEON_PIXCLK_ALWAYS_ONb | RADEON_PIXCLK_DAC_ALWAYS_ONb); + WREG32_PLL(RADEON_VCLK_ECP_CNTL, tmp); + + tmp = crtc_ext_cntl | RADEON_CRTC_CRT_ON; + WREG32(RADEON_CRTC_EXT_CNTL, tmp); + + tmp = RADEON_DAC_FORCE_BLANK_OFF_EN | + RADEON_DAC_FORCE_DATA_EN; + + if (color) + tmp |= RADEON_DAC_FORCE_DATA_SEL_RGB; + else + tmp |= RADEON_DAC_FORCE_DATA_SEL_G; + + if (ASIC_IS_R300(rdev)) + tmp |= (0x1b6 << RADEON_DAC_FORCE_DATA_SHIFT); + else if (ASIC_IS_RV100(rdev)) + tmp |= (0x1ac << RADEON_DAC_FORCE_DATA_SHIFT); + else + tmp |= (0x180 << RADEON_DAC_FORCE_DATA_SHIFT); + + WREG32(RADEON_DAC_EXT_CNTL, tmp); + + tmp = dac_cntl & ~(RADEON_DAC_RANGE_CNTL_MASK | RADEON_DAC_PDWN); + tmp |= RADEON_DAC_RANGE_CNTL_PS2 | RADEON_DAC_CMP_EN; + WREG32(RADEON_DAC_CNTL, tmp); + + tmp = dac_macro_cntl; + tmp &= ~(RADEON_DAC_PDWN_R | + RADEON_DAC_PDWN_G | + RADEON_DAC_PDWN_B); + + WREG32(RADEON_DAC_MACRO_CNTL, tmp); + + DRM_MDELAY(2); + + if (RREG32(RADEON_DAC_CNTL) & RADEON_DAC_CMP_OUTPUT) + found = connector_status_connected; + + /* restore the regs we used */ + WREG32(RADEON_DAC_CNTL, dac_cntl); + WREG32(RADEON_DAC_MACRO_CNTL, dac_macro_cntl); + WREG32(RADEON_DAC_EXT_CNTL, dac_ext_cntl); + WREG32(RADEON_CRTC_EXT_CNTL, crtc_ext_cntl); + WREG32_PLL(RADEON_VCLK_ECP_CNTL, vclk_ecp_cntl); + + return found; +} + +static const struct drm_encoder_helper_funcs radeon_legacy_primary_dac_helper_funcs = { + .dpms = radeon_legacy_primary_dac_dpms, + .mode_fixup = radeon_legacy_mode_fixup, + .prepare = radeon_legacy_primary_dac_prepare, + .mode_set = radeon_legacy_primary_dac_mode_set, + .commit = radeon_legacy_primary_dac_commit, + .detect = radeon_legacy_primary_dac_detect, + .disable = radeon_legacy_encoder_disable, +}; + + +static const struct drm_encoder_funcs radeon_legacy_primary_dac_enc_funcs = { + .destroy = radeon_enc_destroy, +}; + +static void radeon_legacy_tmds_int_dpms(struct drm_encoder *encoder, int mode) +{ + struct drm_device *dev = encoder->dev; + struct radeon_device *rdev = dev->dev_private; + uint32_t fp_gen_cntl = RREG32(RADEON_FP_GEN_CNTL); + DRM_DEBUG_KMS("\n"); + + switch (mode) { + case DRM_MODE_DPMS_ON: + fp_gen_cntl |= (RADEON_FP_FPON | RADEON_FP_TMDS_EN); + break; + case DRM_MODE_DPMS_STANDBY: + case DRM_MODE_DPMS_SUSPEND: + case DRM_MODE_DPMS_OFF: + fp_gen_cntl &= ~(RADEON_FP_FPON | RADEON_FP_TMDS_EN); + break; + } + + WREG32(RADEON_FP_GEN_CNTL, fp_gen_cntl); + + if (rdev->is_atom_bios) + radeon_atombios_encoder_dpms_scratch_regs(encoder, (mode == DRM_MODE_DPMS_ON) ? true : false); + else + radeon_combios_encoder_dpms_scratch_regs(encoder, (mode == DRM_MODE_DPMS_ON) ? true : false); + +} + +static void radeon_legacy_tmds_int_prepare(struct drm_encoder *encoder) +{ + struct radeon_device *rdev = encoder->dev->dev_private; + + if (rdev->is_atom_bios) + radeon_atom_output_lock(encoder, true); + else + radeon_combios_output_lock(encoder, true); + radeon_legacy_tmds_int_dpms(encoder, DRM_MODE_DPMS_OFF); +} + +static void radeon_legacy_tmds_int_commit(struct drm_encoder *encoder) +{ + struct radeon_device *rdev = encoder->dev->dev_private; + + radeon_legacy_tmds_int_dpms(encoder, DRM_MODE_DPMS_ON); + + if (rdev->is_atom_bios) + radeon_atom_output_lock(encoder, true); + else + radeon_combios_output_lock(encoder, true); +} + +static void radeon_legacy_tmds_int_mode_set(struct drm_encoder *encoder, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + struct drm_device *dev = encoder->dev; + struct radeon_device *rdev = dev->dev_private; + struct radeon_crtc *radeon_crtc = to_radeon_crtc(encoder->crtc); + struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); + uint32_t tmp, tmds_pll_cntl, tmds_transmitter_cntl, fp_gen_cntl; + int i; + + DRM_DEBUG_KMS("\n"); + + tmp = tmds_pll_cntl = RREG32(RADEON_TMDS_PLL_CNTL); + tmp &= 0xfffff; + if (rdev->family == CHIP_RV280) { + /* bit 22 of TMDS_PLL_CNTL is read-back inverted */ + tmp ^= (1 << 22); + tmds_pll_cntl ^= (1 << 22); + } + + if (radeon_encoder->enc_priv) { + struct radeon_encoder_int_tmds *tmds = (struct radeon_encoder_int_tmds *)radeon_encoder->enc_priv; + + for (i = 0; i < 4; i++) { + if (tmds->tmds_pll[i].freq == 0) + break; + if ((uint32_t)(mode->clock / 10) < tmds->tmds_pll[i].freq) { + tmp = tmds->tmds_pll[i].value ; + break; + } + } + } + + if (ASIC_IS_R300(rdev) || (rdev->family == CHIP_RV280)) { + if (tmp & 0xfff00000) + tmds_pll_cntl = tmp; + else { + tmds_pll_cntl &= 0xfff00000; + tmds_pll_cntl |= tmp; + } + } else + tmds_pll_cntl = tmp; + + tmds_transmitter_cntl = RREG32(RADEON_TMDS_TRANSMITTER_CNTL) & + ~(RADEON_TMDS_TRANSMITTER_PLLRST); + + if (rdev->family == CHIP_R200 || + rdev->family == CHIP_R100 || + ASIC_IS_R300(rdev)) + tmds_transmitter_cntl &= ~(RADEON_TMDS_TRANSMITTER_PLLEN); + else /* RV chips got this bit reversed */ + tmds_transmitter_cntl |= RADEON_TMDS_TRANSMITTER_PLLEN; + + fp_gen_cntl = (RREG32(RADEON_FP_GEN_CNTL) | + (RADEON_FP_CRTC_DONT_SHADOW_VPAR | + RADEON_FP_CRTC_DONT_SHADOW_HEND)); + + fp_gen_cntl &= ~(RADEON_FP_FPON | RADEON_FP_TMDS_EN); + + fp_gen_cntl &= ~(RADEON_FP_RMX_HVSYNC_CONTROL_EN | + RADEON_FP_DFP_SYNC_SEL | + RADEON_FP_CRT_SYNC_SEL | + RADEON_FP_CRTC_LOCK_8DOT | + RADEON_FP_USE_SHADOW_EN | + RADEON_FP_CRTC_USE_SHADOW_VEND | + RADEON_FP_CRT_SYNC_ALT); + + if (1) /* FIXME rgbBits == 8 */ + fp_gen_cntl |= RADEON_FP_PANEL_FORMAT; /* 24 bit format */ + else + fp_gen_cntl &= ~RADEON_FP_PANEL_FORMAT;/* 18 bit format */ + + if (radeon_crtc->crtc_id == 0) { + if (ASIC_IS_R300(rdev) || rdev->family == CHIP_R200) { + fp_gen_cntl &= ~R200_FP_SOURCE_SEL_MASK; + if (radeon_encoder->rmx_type != RMX_OFF) + fp_gen_cntl |= R200_FP_SOURCE_SEL_RMX; + else + fp_gen_cntl |= R200_FP_SOURCE_SEL_CRTC1; + } else + fp_gen_cntl &= ~RADEON_FP_SEL_CRTC2; + } else { + if (ASIC_IS_R300(rdev) || rdev->family == CHIP_R200) { + fp_gen_cntl &= ~R200_FP_SOURCE_SEL_MASK; + fp_gen_cntl |= R200_FP_SOURCE_SEL_CRTC2; + } else + fp_gen_cntl |= RADEON_FP_SEL_CRTC2; + } + + WREG32(RADEON_TMDS_PLL_CNTL, tmds_pll_cntl); + WREG32(RADEON_TMDS_TRANSMITTER_CNTL, tmds_transmitter_cntl); + WREG32(RADEON_FP_GEN_CNTL, fp_gen_cntl); + + if (rdev->is_atom_bios) + radeon_atombios_encoder_crtc_scratch_regs(encoder, radeon_crtc->crtc_id); + else + radeon_combios_encoder_crtc_scratch_regs(encoder, radeon_crtc->crtc_id); +} + +static const struct drm_encoder_helper_funcs radeon_legacy_tmds_int_helper_funcs = { + .dpms = radeon_legacy_tmds_int_dpms, + .mode_fixup = radeon_legacy_mode_fixup, + .prepare = radeon_legacy_tmds_int_prepare, + .mode_set = radeon_legacy_tmds_int_mode_set, + .commit = radeon_legacy_tmds_int_commit, + .disable = radeon_legacy_encoder_disable, +}; + + +static const struct drm_encoder_funcs radeon_legacy_tmds_int_enc_funcs = { + .destroy = radeon_enc_destroy, +}; + +static void radeon_legacy_tmds_ext_dpms(struct drm_encoder *encoder, int mode) +{ + struct drm_device *dev = encoder->dev; + struct radeon_device *rdev = dev->dev_private; + uint32_t fp2_gen_cntl = RREG32(RADEON_FP2_GEN_CNTL); + DRM_DEBUG_KMS("\n"); + + switch (mode) { + case DRM_MODE_DPMS_ON: + fp2_gen_cntl &= ~RADEON_FP2_BLANK_EN; + fp2_gen_cntl |= (RADEON_FP2_ON | RADEON_FP2_DVO_EN); + break; + case DRM_MODE_DPMS_STANDBY: + case DRM_MODE_DPMS_SUSPEND: + case DRM_MODE_DPMS_OFF: + fp2_gen_cntl |= RADEON_FP2_BLANK_EN; + fp2_gen_cntl &= ~(RADEON_FP2_ON | RADEON_FP2_DVO_EN); + break; + } + + WREG32(RADEON_FP2_GEN_CNTL, fp2_gen_cntl); + + if (rdev->is_atom_bios) + radeon_atombios_encoder_dpms_scratch_regs(encoder, (mode == DRM_MODE_DPMS_ON) ? true : false); + else + radeon_combios_encoder_dpms_scratch_regs(encoder, (mode == DRM_MODE_DPMS_ON) ? true : false); + +} + +static void radeon_legacy_tmds_ext_prepare(struct drm_encoder *encoder) +{ + struct radeon_device *rdev = encoder->dev->dev_private; + + if (rdev->is_atom_bios) + radeon_atom_output_lock(encoder, true); + else + radeon_combios_output_lock(encoder, true); + radeon_legacy_tmds_ext_dpms(encoder, DRM_MODE_DPMS_OFF); +} + +static void radeon_legacy_tmds_ext_commit(struct drm_encoder *encoder) +{ + struct radeon_device *rdev = encoder->dev->dev_private; + radeon_legacy_tmds_ext_dpms(encoder, DRM_MODE_DPMS_ON); + + if (rdev->is_atom_bios) + radeon_atom_output_lock(encoder, false); + else + radeon_combios_output_lock(encoder, false); +} + +static void radeon_legacy_tmds_ext_mode_set(struct drm_encoder *encoder, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + struct drm_device *dev = encoder->dev; + struct radeon_device *rdev = dev->dev_private; + struct radeon_crtc *radeon_crtc = to_radeon_crtc(encoder->crtc); + struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); + uint32_t fp2_gen_cntl; + + DRM_DEBUG_KMS("\n"); + + if (rdev->is_atom_bios) { + radeon_encoder->pixel_clock = adjusted_mode->clock; + atombios_dvo_setup(encoder, ATOM_ENABLE); + fp2_gen_cntl = RREG32(RADEON_FP2_GEN_CNTL); + } else { + fp2_gen_cntl = RREG32(RADEON_FP2_GEN_CNTL); + + if (1) /* FIXME rgbBits == 8 */ + fp2_gen_cntl |= RADEON_FP2_PANEL_FORMAT; /* 24 bit format, */ + else + fp2_gen_cntl &= ~RADEON_FP2_PANEL_FORMAT;/* 18 bit format, */ + + fp2_gen_cntl &= ~(RADEON_FP2_ON | + RADEON_FP2_DVO_EN | + RADEON_FP2_DVO_RATE_SEL_SDR); + + /* XXX: these are oem specific */ + if (ASIC_IS_R300(rdev)) { + if ((dev->pci_device == 0x4850) && + (dev->pci_subvendor == 0x1028) && + (dev->pci_subdevice == 0x2001)) /* Dell Inspiron 8600 */ + fp2_gen_cntl |= R300_FP2_DVO_CLOCK_MODE_SINGLE; + else + fp2_gen_cntl |= RADEON_FP2_PAD_FLOP_EN | R300_FP2_DVO_CLOCK_MODE_SINGLE; + + /*if (mode->clock > 165000) + fp2_gen_cntl |= R300_FP2_DVO_DUAL_CHANNEL_EN;*/ + } + if (!radeon_combios_external_tmds_setup(encoder)) + radeon_external_tmds_setup(encoder); + } + + if (radeon_crtc->crtc_id == 0) { + if ((rdev->family == CHIP_R200) || ASIC_IS_R300(rdev)) { + fp2_gen_cntl &= ~R200_FP2_SOURCE_SEL_MASK; + if (radeon_encoder->rmx_type != RMX_OFF) + fp2_gen_cntl |= R200_FP2_SOURCE_SEL_RMX; + else + fp2_gen_cntl |= R200_FP2_SOURCE_SEL_CRTC1; + } else + fp2_gen_cntl &= ~RADEON_FP2_SRC_SEL_CRTC2; + } else { + if ((rdev->family == CHIP_R200) || ASIC_IS_R300(rdev)) { + fp2_gen_cntl &= ~R200_FP2_SOURCE_SEL_MASK; + fp2_gen_cntl |= R200_FP2_SOURCE_SEL_CRTC2; + } else + fp2_gen_cntl |= RADEON_FP2_SRC_SEL_CRTC2; + } + + WREG32(RADEON_FP2_GEN_CNTL, fp2_gen_cntl); + + if (rdev->is_atom_bios) + radeon_atombios_encoder_crtc_scratch_regs(encoder, radeon_crtc->crtc_id); + else + radeon_combios_encoder_crtc_scratch_regs(encoder, radeon_crtc->crtc_id); +} + +static void radeon_ext_tmds_enc_destroy(struct drm_encoder *encoder) +{ + struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); + /* don't destroy the i2c bus record here, this will be done in radeon_i2c_fini */ + free(radeon_encoder->enc_priv, DRM_MEM_DRIVER); + drm_encoder_cleanup(encoder); + free(radeon_encoder, DRM_MEM_DRIVER); +} + +static const struct drm_encoder_helper_funcs radeon_legacy_tmds_ext_helper_funcs = { + .dpms = radeon_legacy_tmds_ext_dpms, + .mode_fixup = radeon_legacy_mode_fixup, + .prepare = radeon_legacy_tmds_ext_prepare, + .mode_set = radeon_legacy_tmds_ext_mode_set, + .commit = radeon_legacy_tmds_ext_commit, + .disable = radeon_legacy_encoder_disable, +}; + + +static const struct drm_encoder_funcs radeon_legacy_tmds_ext_enc_funcs = { + .destroy = radeon_ext_tmds_enc_destroy, +}; + +static void radeon_legacy_tv_dac_dpms(struct drm_encoder *encoder, int mode) +{ + struct drm_device *dev = encoder->dev; + struct radeon_device *rdev = dev->dev_private; + struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); + uint32_t fp2_gen_cntl = 0, crtc2_gen_cntl = 0, tv_dac_cntl = 0; + uint32_t tv_master_cntl = 0; + bool is_tv; + DRM_DEBUG_KMS("\n"); + + is_tv = radeon_encoder->active_device & ATOM_DEVICE_TV_SUPPORT ? true : false; + + if (rdev->family == CHIP_R200) + fp2_gen_cntl = RREG32(RADEON_FP2_GEN_CNTL); + else { + if (is_tv) + tv_master_cntl = RREG32(RADEON_TV_MASTER_CNTL); + else + crtc2_gen_cntl = RREG32(RADEON_CRTC2_GEN_CNTL); + tv_dac_cntl = RREG32(RADEON_TV_DAC_CNTL); + } + + switch (mode) { + case DRM_MODE_DPMS_ON: + if (rdev->family == CHIP_R200) { + fp2_gen_cntl |= (RADEON_FP2_ON | RADEON_FP2_DVO_EN); + } else { + if (is_tv) + tv_master_cntl |= RADEON_TV_ON; + else + crtc2_gen_cntl |= RADEON_CRTC2_CRT2_ON; + + if (rdev->family == CHIP_R420 || + rdev->family == CHIP_R423 || + rdev->family == CHIP_RV410) + tv_dac_cntl &= ~(R420_TV_DAC_RDACPD | + R420_TV_DAC_GDACPD | + R420_TV_DAC_BDACPD | + RADEON_TV_DAC_BGSLEEP); + else + tv_dac_cntl &= ~(RADEON_TV_DAC_RDACPD | + RADEON_TV_DAC_GDACPD | + RADEON_TV_DAC_BDACPD | + RADEON_TV_DAC_BGSLEEP); + } + break; + case DRM_MODE_DPMS_STANDBY: + case DRM_MODE_DPMS_SUSPEND: + case DRM_MODE_DPMS_OFF: + if (rdev->family == CHIP_R200) + fp2_gen_cntl &= ~(RADEON_FP2_ON | RADEON_FP2_DVO_EN); + else { + if (is_tv) + tv_master_cntl &= ~RADEON_TV_ON; + else + crtc2_gen_cntl &= ~RADEON_CRTC2_CRT2_ON; + + if (rdev->family == CHIP_R420 || + rdev->family == CHIP_R423 || + rdev->family == CHIP_RV410) + tv_dac_cntl |= (R420_TV_DAC_RDACPD | + R420_TV_DAC_GDACPD | + R420_TV_DAC_BDACPD | + RADEON_TV_DAC_BGSLEEP); + else + tv_dac_cntl |= (RADEON_TV_DAC_RDACPD | + RADEON_TV_DAC_GDACPD | + RADEON_TV_DAC_BDACPD | + RADEON_TV_DAC_BGSLEEP); + } + break; + } + + if (rdev->family == CHIP_R200) { + WREG32(RADEON_FP2_GEN_CNTL, fp2_gen_cntl); + } else { + if (is_tv) + WREG32(RADEON_TV_MASTER_CNTL, tv_master_cntl); + /* handled in radeon_crtc_dpms() */ + else if (!(rdev->flags & RADEON_SINGLE_CRTC)) + WREG32(RADEON_CRTC2_GEN_CNTL, crtc2_gen_cntl); + WREG32(RADEON_TV_DAC_CNTL, tv_dac_cntl); + } + + if (rdev->is_atom_bios) + radeon_atombios_encoder_dpms_scratch_regs(encoder, (mode == DRM_MODE_DPMS_ON) ? true : false); + else + radeon_combios_encoder_dpms_scratch_regs(encoder, (mode == DRM_MODE_DPMS_ON) ? true : false); + +} + +static void radeon_legacy_tv_dac_prepare(struct drm_encoder *encoder) +{ + struct radeon_device *rdev = encoder->dev->dev_private; + + if (rdev->is_atom_bios) + radeon_atom_output_lock(encoder, true); + else + radeon_combios_output_lock(encoder, true); + radeon_legacy_tv_dac_dpms(encoder, DRM_MODE_DPMS_OFF); +} + +static void radeon_legacy_tv_dac_commit(struct drm_encoder *encoder) +{ + struct radeon_device *rdev = encoder->dev->dev_private; + + radeon_legacy_tv_dac_dpms(encoder, DRM_MODE_DPMS_ON); + + if (rdev->is_atom_bios) + radeon_atom_output_lock(encoder, true); + else + radeon_combios_output_lock(encoder, true); +} + +static void radeon_legacy_tv_dac_mode_set(struct drm_encoder *encoder, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + struct drm_device *dev = encoder->dev; + struct radeon_device *rdev = dev->dev_private; + struct radeon_crtc *radeon_crtc = to_radeon_crtc(encoder->crtc); + struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); + struct radeon_encoder_tv_dac *tv_dac = radeon_encoder->enc_priv; + uint32_t tv_dac_cntl, gpiopad_a = 0, dac2_cntl, disp_output_cntl = 0; + uint32_t disp_hw_debug = 0, fp2_gen_cntl = 0, disp_tv_out_cntl = 0; + bool is_tv = false; + + DRM_DEBUG_KMS("\n"); + + is_tv = radeon_encoder->active_device & ATOM_DEVICE_TV_SUPPORT ? true : false; + + if (rdev->family != CHIP_R200) { + tv_dac_cntl = RREG32(RADEON_TV_DAC_CNTL); + if (rdev->family == CHIP_R420 || + rdev->family == CHIP_R423 || + rdev->family == CHIP_RV410) { + tv_dac_cntl &= ~(RADEON_TV_DAC_STD_MASK | + RADEON_TV_DAC_BGADJ_MASK | + R420_TV_DAC_DACADJ_MASK | + R420_TV_DAC_RDACPD | + R420_TV_DAC_GDACPD | + R420_TV_DAC_BDACPD | + R420_TV_DAC_TVENABLE); + } else { + tv_dac_cntl &= ~(RADEON_TV_DAC_STD_MASK | + RADEON_TV_DAC_BGADJ_MASK | + RADEON_TV_DAC_DACADJ_MASK | + RADEON_TV_DAC_RDACPD | + RADEON_TV_DAC_GDACPD | + RADEON_TV_DAC_BDACPD); + } + + tv_dac_cntl |= RADEON_TV_DAC_NBLANK | RADEON_TV_DAC_NHOLD; + + if (is_tv) { + if (tv_dac->tv_std == TV_STD_NTSC || + tv_dac->tv_std == TV_STD_NTSC_J || + tv_dac->tv_std == TV_STD_PAL_M || + tv_dac->tv_std == TV_STD_PAL_60) + tv_dac_cntl |= tv_dac->ntsc_tvdac_adj; + else + tv_dac_cntl |= tv_dac->pal_tvdac_adj; + + if (tv_dac->tv_std == TV_STD_NTSC || + tv_dac->tv_std == TV_STD_NTSC_J) + tv_dac_cntl |= RADEON_TV_DAC_STD_NTSC; + else + tv_dac_cntl |= RADEON_TV_DAC_STD_PAL; + } else + tv_dac_cntl |= (RADEON_TV_DAC_STD_PS2 | + tv_dac->ps2_tvdac_adj); + + WREG32(RADEON_TV_DAC_CNTL, tv_dac_cntl); + } + + if (ASIC_IS_R300(rdev)) { + gpiopad_a = RREG32(RADEON_GPIOPAD_A) | 1; + disp_output_cntl = RREG32(RADEON_DISP_OUTPUT_CNTL); + } else if (rdev->family != CHIP_R200) + disp_hw_debug = RREG32(RADEON_DISP_HW_DEBUG); + else if (rdev->family == CHIP_R200) + fp2_gen_cntl = RREG32(RADEON_FP2_GEN_CNTL); + + if (rdev->family >= CHIP_R200) + disp_tv_out_cntl = RREG32(RADEON_DISP_TV_OUT_CNTL); + + if (is_tv) { + uint32_t dac_cntl; + + dac_cntl = RREG32(RADEON_DAC_CNTL); + dac_cntl &= ~RADEON_DAC_TVO_EN; + WREG32(RADEON_DAC_CNTL, dac_cntl); + + if (ASIC_IS_R300(rdev)) + gpiopad_a = RREG32(RADEON_GPIOPAD_A) & ~1; + + dac2_cntl = RREG32(RADEON_DAC_CNTL2) & ~RADEON_DAC2_DAC2_CLK_SEL; + if (radeon_crtc->crtc_id == 0) { + if (ASIC_IS_R300(rdev)) { + disp_output_cntl &= ~RADEON_DISP_TVDAC_SOURCE_MASK; + disp_output_cntl |= (RADEON_DISP_TVDAC_SOURCE_CRTC | + RADEON_DISP_TV_SOURCE_CRTC); + } + if (rdev->family >= CHIP_R200) { + disp_tv_out_cntl &= ~RADEON_DISP_TV_PATH_SRC_CRTC2; + } else { + disp_hw_debug |= RADEON_CRT2_DISP1_SEL; + } + } else { + if (ASIC_IS_R300(rdev)) { + disp_output_cntl &= ~RADEON_DISP_TVDAC_SOURCE_MASK; + disp_output_cntl |= RADEON_DISP_TV_SOURCE_CRTC; + } + if (rdev->family >= CHIP_R200) { + disp_tv_out_cntl |= RADEON_DISP_TV_PATH_SRC_CRTC2; + } else { + disp_hw_debug &= ~RADEON_CRT2_DISP1_SEL; + } + } + WREG32(RADEON_DAC_CNTL2, dac2_cntl); + } else { + + dac2_cntl = RREG32(RADEON_DAC_CNTL2) | RADEON_DAC2_DAC2_CLK_SEL; + + if (radeon_crtc->crtc_id == 0) { + if (ASIC_IS_R300(rdev)) { + disp_output_cntl &= ~RADEON_DISP_TVDAC_SOURCE_MASK; + disp_output_cntl |= RADEON_DISP_TVDAC_SOURCE_CRTC; + } else if (rdev->family == CHIP_R200) { + fp2_gen_cntl &= ~(R200_FP2_SOURCE_SEL_MASK | + RADEON_FP2_DVO_RATE_SEL_SDR); + } else + disp_hw_debug |= RADEON_CRT2_DISP1_SEL; + } else { + if (ASIC_IS_R300(rdev)) { + disp_output_cntl &= ~RADEON_DISP_TVDAC_SOURCE_MASK; + disp_output_cntl |= RADEON_DISP_TVDAC_SOURCE_CRTC2; + } else if (rdev->family == CHIP_R200) { + fp2_gen_cntl &= ~(R200_FP2_SOURCE_SEL_MASK | + RADEON_FP2_DVO_RATE_SEL_SDR); + fp2_gen_cntl |= R200_FP2_SOURCE_SEL_CRTC2; + } else + disp_hw_debug &= ~RADEON_CRT2_DISP1_SEL; + } + WREG32(RADEON_DAC_CNTL2, dac2_cntl); + } + + if (ASIC_IS_R300(rdev)) { + WREG32_P(RADEON_GPIOPAD_A, gpiopad_a, ~1); + WREG32(RADEON_DISP_OUTPUT_CNTL, disp_output_cntl); + } else if (rdev->family != CHIP_R200) + WREG32(RADEON_DISP_HW_DEBUG, disp_hw_debug); + else if (rdev->family == CHIP_R200) + WREG32(RADEON_FP2_GEN_CNTL, fp2_gen_cntl); + + if (rdev->family >= CHIP_R200) + WREG32(RADEON_DISP_TV_OUT_CNTL, disp_tv_out_cntl); + + if (is_tv) + radeon_legacy_tv_mode_set(encoder, mode, adjusted_mode); + + if (rdev->is_atom_bios) + radeon_atombios_encoder_crtc_scratch_regs(encoder, radeon_crtc->crtc_id); + else + radeon_combios_encoder_crtc_scratch_regs(encoder, radeon_crtc->crtc_id); + +} + +static bool r300_legacy_tv_detect(struct drm_encoder *encoder, + struct drm_connector *connector) +{ + struct drm_device *dev = encoder->dev; + struct radeon_device *rdev = dev->dev_private; + uint32_t crtc2_gen_cntl, tv_dac_cntl, dac_cntl2, dac_ext_cntl; + uint32_t disp_output_cntl, gpiopad_a, tmp; + bool found = false; + + /* save regs needed */ + gpiopad_a = RREG32(RADEON_GPIOPAD_A); + dac_cntl2 = RREG32(RADEON_DAC_CNTL2); + crtc2_gen_cntl = RREG32(RADEON_CRTC2_GEN_CNTL); + dac_ext_cntl = RREG32(RADEON_DAC_EXT_CNTL); + tv_dac_cntl = RREG32(RADEON_TV_DAC_CNTL); + disp_output_cntl = RREG32(RADEON_DISP_OUTPUT_CNTL); + + WREG32_P(RADEON_GPIOPAD_A, 0, ~1); + + WREG32(RADEON_DAC_CNTL2, RADEON_DAC2_DAC2_CLK_SEL); + + WREG32(RADEON_CRTC2_GEN_CNTL, + RADEON_CRTC2_CRT2_ON | RADEON_CRTC2_VSYNC_TRISTAT); + + tmp = disp_output_cntl & ~RADEON_DISP_TVDAC_SOURCE_MASK; + tmp |= RADEON_DISP_TVDAC_SOURCE_CRTC2; + WREG32(RADEON_DISP_OUTPUT_CNTL, tmp); + + WREG32(RADEON_DAC_EXT_CNTL, + RADEON_DAC2_FORCE_BLANK_OFF_EN | + RADEON_DAC2_FORCE_DATA_EN | + RADEON_DAC_FORCE_DATA_SEL_RGB | + (0xec << RADEON_DAC_FORCE_DATA_SHIFT)); + + WREG32(RADEON_TV_DAC_CNTL, + RADEON_TV_DAC_STD_NTSC | + (8 << RADEON_TV_DAC_BGADJ_SHIFT) | + (6 << RADEON_TV_DAC_DACADJ_SHIFT)); + + RREG32(RADEON_TV_DAC_CNTL); + DRM_MDELAY(4); + + WREG32(RADEON_TV_DAC_CNTL, + RADEON_TV_DAC_NBLANK | + RADEON_TV_DAC_NHOLD | + RADEON_TV_MONITOR_DETECT_EN | + RADEON_TV_DAC_STD_NTSC | + (8 << RADEON_TV_DAC_BGADJ_SHIFT) | + (6 << RADEON_TV_DAC_DACADJ_SHIFT)); + + RREG32(RADEON_TV_DAC_CNTL); + DRM_MDELAY(6); + + tmp = RREG32(RADEON_TV_DAC_CNTL); + if ((tmp & RADEON_TV_DAC_GDACDET) != 0) { + found = true; + DRM_DEBUG_KMS("S-video TV connection detected\n"); + } else if ((tmp & RADEON_TV_DAC_BDACDET) != 0) { + found = true; + DRM_DEBUG_KMS("Composite TV connection detected\n"); + } + + WREG32(RADEON_TV_DAC_CNTL, tv_dac_cntl); + WREG32(RADEON_DAC_EXT_CNTL, dac_ext_cntl); + WREG32(RADEON_CRTC2_GEN_CNTL, crtc2_gen_cntl); + WREG32(RADEON_DISP_OUTPUT_CNTL, disp_output_cntl); + WREG32(RADEON_DAC_CNTL2, dac_cntl2); + WREG32_P(RADEON_GPIOPAD_A, gpiopad_a, ~1); + return found; +} + +static bool radeon_legacy_tv_detect(struct drm_encoder *encoder, + struct drm_connector *connector) +{ + struct drm_device *dev = encoder->dev; + struct radeon_device *rdev = dev->dev_private; + uint32_t tv_dac_cntl, dac_cntl2; + uint32_t config_cntl, tv_pre_dac_mux_cntl, tv_master_cntl, tmp; + bool found = false; + + if (ASIC_IS_R300(rdev)) + return r300_legacy_tv_detect(encoder, connector); + + dac_cntl2 = RREG32(RADEON_DAC_CNTL2); + tv_master_cntl = RREG32(RADEON_TV_MASTER_CNTL); + tv_dac_cntl = RREG32(RADEON_TV_DAC_CNTL); + config_cntl = RREG32(RADEON_CONFIG_CNTL); + tv_pre_dac_mux_cntl = RREG32(RADEON_TV_PRE_DAC_MUX_CNTL); + + tmp = dac_cntl2 & ~RADEON_DAC2_DAC2_CLK_SEL; + WREG32(RADEON_DAC_CNTL2, tmp); + + tmp = tv_master_cntl | RADEON_TV_ON; + tmp &= ~(RADEON_TV_ASYNC_RST | + RADEON_RESTART_PHASE_FIX | + RADEON_CRT_FIFO_CE_EN | + RADEON_TV_FIFO_CE_EN | + RADEON_RE_SYNC_NOW_SEL_MASK); + tmp |= RADEON_TV_FIFO_ASYNC_RST | RADEON_CRT_ASYNC_RST; + WREG32(RADEON_TV_MASTER_CNTL, tmp); + + tmp = RADEON_TV_DAC_NBLANK | RADEON_TV_DAC_NHOLD | + RADEON_TV_MONITOR_DETECT_EN | RADEON_TV_DAC_STD_NTSC | + (8 << RADEON_TV_DAC_BGADJ_SHIFT); + + if (config_cntl & RADEON_CFG_ATI_REV_ID_MASK) + tmp |= (4 << RADEON_TV_DAC_DACADJ_SHIFT); + else + tmp |= (8 << RADEON_TV_DAC_DACADJ_SHIFT); + WREG32(RADEON_TV_DAC_CNTL, tmp); + + tmp = RADEON_C_GRN_EN | RADEON_CMP_BLU_EN | + RADEON_RED_MX_FORCE_DAC_DATA | + RADEON_GRN_MX_FORCE_DAC_DATA | + RADEON_BLU_MX_FORCE_DAC_DATA | + (0x109 << RADEON_TV_FORCE_DAC_DATA_SHIFT); + WREG32(RADEON_TV_PRE_DAC_MUX_CNTL, tmp); + + DRM_MDELAY(3); + tmp = RREG32(RADEON_TV_DAC_CNTL); + if (tmp & RADEON_TV_DAC_GDACDET) { + found = true; + DRM_DEBUG_KMS("S-video TV connection detected\n"); + } else if ((tmp & RADEON_TV_DAC_BDACDET) != 0) { + found = true; + DRM_DEBUG_KMS("Composite TV connection detected\n"); + } + + WREG32(RADEON_TV_PRE_DAC_MUX_CNTL, tv_pre_dac_mux_cntl); + WREG32(RADEON_TV_DAC_CNTL, tv_dac_cntl); + WREG32(RADEON_TV_MASTER_CNTL, tv_master_cntl); + WREG32(RADEON_DAC_CNTL2, dac_cntl2); + return found; +} + +static bool radeon_legacy_ext_dac_detect(struct drm_encoder *encoder, + struct drm_connector *connector) +{ + struct drm_device *dev = encoder->dev; + struct radeon_device *rdev = dev->dev_private; + uint32_t gpio_monid, fp2_gen_cntl, disp_output_cntl, crtc2_gen_cntl; + uint32_t disp_lin_trans_grph_a, disp_lin_trans_grph_b, disp_lin_trans_grph_c; + uint32_t disp_lin_trans_grph_d, disp_lin_trans_grph_e, disp_lin_trans_grph_f; + uint32_t tmp, crtc2_h_total_disp, crtc2_v_total_disp; + uint32_t crtc2_h_sync_strt_wid, crtc2_v_sync_strt_wid; + bool found = false; + int i; + + /* save the regs we need */ + gpio_monid = RREG32(RADEON_GPIO_MONID); + fp2_gen_cntl = RREG32(RADEON_FP2_GEN_CNTL); + disp_output_cntl = RREG32(RADEON_DISP_OUTPUT_CNTL); + crtc2_gen_cntl = RREG32(RADEON_CRTC2_GEN_CNTL); + disp_lin_trans_grph_a = RREG32(RADEON_DISP_LIN_TRANS_GRPH_A); + disp_lin_trans_grph_b = RREG32(RADEON_DISP_LIN_TRANS_GRPH_B); + disp_lin_trans_grph_c = RREG32(RADEON_DISP_LIN_TRANS_GRPH_C); + disp_lin_trans_grph_d = RREG32(RADEON_DISP_LIN_TRANS_GRPH_D); + disp_lin_trans_grph_e = RREG32(RADEON_DISP_LIN_TRANS_GRPH_E); + disp_lin_trans_grph_f = RREG32(RADEON_DISP_LIN_TRANS_GRPH_F); + crtc2_h_total_disp = RREG32(RADEON_CRTC2_H_TOTAL_DISP); + crtc2_v_total_disp = RREG32(RADEON_CRTC2_V_TOTAL_DISP); + crtc2_h_sync_strt_wid = RREG32(RADEON_CRTC2_H_SYNC_STRT_WID); + crtc2_v_sync_strt_wid = RREG32(RADEON_CRTC2_V_SYNC_STRT_WID); + + tmp = RREG32(RADEON_GPIO_MONID); + tmp &= ~RADEON_GPIO_A_0; + WREG32(RADEON_GPIO_MONID, tmp); + + WREG32(RADEON_FP2_GEN_CNTL, (RADEON_FP2_ON | + RADEON_FP2_PANEL_FORMAT | + R200_FP2_SOURCE_SEL_TRANS_UNIT | + RADEON_FP2_DVO_EN | + R200_FP2_DVO_RATE_SEL_SDR)); + + WREG32(RADEON_DISP_OUTPUT_CNTL, (RADEON_DISP_DAC_SOURCE_RMX | + RADEON_DISP_TRANS_MATRIX_GRAPHICS)); + + WREG32(RADEON_CRTC2_GEN_CNTL, (RADEON_CRTC2_EN | + RADEON_CRTC2_DISP_REQ_EN_B)); + + WREG32(RADEON_DISP_LIN_TRANS_GRPH_A, 0x00000000); + WREG32(RADEON_DISP_LIN_TRANS_GRPH_B, 0x000003f0); + WREG32(RADEON_DISP_LIN_TRANS_GRPH_C, 0x00000000); + WREG32(RADEON_DISP_LIN_TRANS_GRPH_D, 0x000003f0); + WREG32(RADEON_DISP_LIN_TRANS_GRPH_E, 0x00000000); + WREG32(RADEON_DISP_LIN_TRANS_GRPH_F, 0x000003f0); + + WREG32(RADEON_CRTC2_H_TOTAL_DISP, 0x01000008); + WREG32(RADEON_CRTC2_H_SYNC_STRT_WID, 0x00000800); + WREG32(RADEON_CRTC2_V_TOTAL_DISP, 0x00080001); + WREG32(RADEON_CRTC2_V_SYNC_STRT_WID, 0x00000080); + + for (i = 0; i < 200; i++) { + tmp = RREG32(RADEON_GPIO_MONID); + if (tmp & RADEON_GPIO_Y_0) + found = true; + + if (found) + break; + + DRM_MDELAY(1); + if (!drm_can_sleep()) + DRM_MDELAY(1); + else + DRM_MSLEEP(1); + } + + /* restore the regs we used */ + WREG32(RADEON_DISP_LIN_TRANS_GRPH_A, disp_lin_trans_grph_a); + WREG32(RADEON_DISP_LIN_TRANS_GRPH_B, disp_lin_trans_grph_b); + WREG32(RADEON_DISP_LIN_TRANS_GRPH_C, disp_lin_trans_grph_c); + WREG32(RADEON_DISP_LIN_TRANS_GRPH_D, disp_lin_trans_grph_d); + WREG32(RADEON_DISP_LIN_TRANS_GRPH_E, disp_lin_trans_grph_e); + WREG32(RADEON_DISP_LIN_TRANS_GRPH_F, disp_lin_trans_grph_f); + WREG32(RADEON_CRTC2_H_TOTAL_DISP, crtc2_h_total_disp); + WREG32(RADEON_CRTC2_V_TOTAL_DISP, crtc2_v_total_disp); + WREG32(RADEON_CRTC2_H_SYNC_STRT_WID, crtc2_h_sync_strt_wid); + WREG32(RADEON_CRTC2_V_SYNC_STRT_WID, crtc2_v_sync_strt_wid); + WREG32(RADEON_CRTC2_GEN_CNTL, crtc2_gen_cntl); + WREG32(RADEON_DISP_OUTPUT_CNTL, disp_output_cntl); + WREG32(RADEON_FP2_GEN_CNTL, fp2_gen_cntl); + WREG32(RADEON_GPIO_MONID, gpio_monid); + + return found; +} + +static enum drm_connector_status radeon_legacy_tv_dac_detect(struct drm_encoder *encoder, + struct drm_connector *connector) +{ + struct drm_device *dev = encoder->dev; + struct radeon_device *rdev = dev->dev_private; + uint32_t crtc2_gen_cntl = 0, tv_dac_cntl, dac_cntl2, dac_ext_cntl; + uint32_t gpiopad_a = 0, pixclks_cntl, tmp; + uint32_t disp_output_cntl = 0, disp_hw_debug = 0, crtc_ext_cntl = 0; + enum drm_connector_status found = connector_status_disconnected; + struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); + struct radeon_encoder_tv_dac *tv_dac = radeon_encoder->enc_priv; + bool color = true; + struct drm_crtc *crtc; + + /* find out if crtc2 is in use or if this encoder is using it */ + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { + struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); + if ((radeon_crtc->crtc_id == 1) && crtc->enabled) { + if (encoder->crtc != crtc) { + return connector_status_disconnected; + } + } + } + + if (connector->connector_type == DRM_MODE_CONNECTOR_SVIDEO || + connector->connector_type == DRM_MODE_CONNECTOR_Composite || + connector->connector_type == DRM_MODE_CONNECTOR_9PinDIN) { + bool tv_detect; + + if (radeon_encoder->active_device && !(radeon_encoder->active_device & ATOM_DEVICE_TV_SUPPORT)) + return connector_status_disconnected; + + tv_detect = radeon_legacy_tv_detect(encoder, connector); + if (tv_detect && tv_dac) + found = connector_status_connected; + return found; + } + + /* don't probe if the encoder is being used for something else not CRT related */ + if (radeon_encoder->active_device && !(radeon_encoder->active_device & ATOM_DEVICE_CRT_SUPPORT)) { + DRM_INFO("not detecting due to %08x\n", radeon_encoder->active_device); + return connector_status_disconnected; + } + + /* R200 uses an external DAC for secondary DAC */ + if (rdev->family == CHIP_R200) { + if (radeon_legacy_ext_dac_detect(encoder, connector)) + found = connector_status_connected; + return found; + } + + /* save the regs we need */ + pixclks_cntl = RREG32_PLL(RADEON_PIXCLKS_CNTL); + + if (rdev->flags & RADEON_SINGLE_CRTC) { + crtc_ext_cntl = RREG32(RADEON_CRTC_EXT_CNTL); + } else { + if (ASIC_IS_R300(rdev)) { + gpiopad_a = RREG32(RADEON_GPIOPAD_A); + disp_output_cntl = RREG32(RADEON_DISP_OUTPUT_CNTL); + } else { + disp_hw_debug = RREG32(RADEON_DISP_HW_DEBUG); + } + crtc2_gen_cntl = RREG32(RADEON_CRTC2_GEN_CNTL); + } + tv_dac_cntl = RREG32(RADEON_TV_DAC_CNTL); + dac_ext_cntl = RREG32(RADEON_DAC_EXT_CNTL); + dac_cntl2 = RREG32(RADEON_DAC_CNTL2); + + tmp = pixclks_cntl & ~(RADEON_PIX2CLK_ALWAYS_ONb + | RADEON_PIX2CLK_DAC_ALWAYS_ONb); + WREG32_PLL(RADEON_PIXCLKS_CNTL, tmp); + + if (rdev->flags & RADEON_SINGLE_CRTC) { + tmp = crtc_ext_cntl | RADEON_CRTC_CRT_ON; + WREG32(RADEON_CRTC_EXT_CNTL, tmp); + } else { + tmp = crtc2_gen_cntl & ~RADEON_CRTC2_PIX_WIDTH_MASK; + tmp |= RADEON_CRTC2_CRT2_ON | + (2 << RADEON_CRTC2_PIX_WIDTH_SHIFT); + WREG32(RADEON_CRTC2_GEN_CNTL, tmp); + + if (ASIC_IS_R300(rdev)) { + WREG32_P(RADEON_GPIOPAD_A, 1, ~1); + tmp = disp_output_cntl & ~RADEON_DISP_TVDAC_SOURCE_MASK; + tmp |= RADEON_DISP_TVDAC_SOURCE_CRTC2; + WREG32(RADEON_DISP_OUTPUT_CNTL, tmp); + } else { + tmp = disp_hw_debug & ~RADEON_CRT2_DISP1_SEL; + WREG32(RADEON_DISP_HW_DEBUG, tmp); + } + } + + tmp = RADEON_TV_DAC_NBLANK | + RADEON_TV_DAC_NHOLD | + RADEON_TV_MONITOR_DETECT_EN | + RADEON_TV_DAC_STD_PS2; + + WREG32(RADEON_TV_DAC_CNTL, tmp); + + tmp = RADEON_DAC2_FORCE_BLANK_OFF_EN | + RADEON_DAC2_FORCE_DATA_EN; + + if (color) + tmp |= RADEON_DAC_FORCE_DATA_SEL_RGB; + else + tmp |= RADEON_DAC_FORCE_DATA_SEL_G; + + if (ASIC_IS_R300(rdev)) + tmp |= (0x1b6 << RADEON_DAC_FORCE_DATA_SHIFT); + else + tmp |= (0x180 << RADEON_DAC_FORCE_DATA_SHIFT); + + WREG32(RADEON_DAC_EXT_CNTL, tmp); + + tmp = dac_cntl2 | RADEON_DAC2_DAC2_CLK_SEL | RADEON_DAC2_CMP_EN; + WREG32(RADEON_DAC_CNTL2, tmp); + + DRM_MDELAY(10); + + if (ASIC_IS_R300(rdev)) { + if (RREG32(RADEON_DAC_CNTL2) & RADEON_DAC2_CMP_OUT_B) + found = connector_status_connected; + } else { + if (RREG32(RADEON_DAC_CNTL2) & RADEON_DAC2_CMP_OUTPUT) + found = connector_status_connected; + } + + /* restore regs we used */ + WREG32(RADEON_DAC_CNTL2, dac_cntl2); + WREG32(RADEON_DAC_EXT_CNTL, dac_ext_cntl); + WREG32(RADEON_TV_DAC_CNTL, tv_dac_cntl); + + if (rdev->flags & RADEON_SINGLE_CRTC) { + WREG32(RADEON_CRTC_EXT_CNTL, crtc_ext_cntl); + } else { + WREG32(RADEON_CRTC2_GEN_CNTL, crtc2_gen_cntl); + if (ASIC_IS_R300(rdev)) { + WREG32(RADEON_DISP_OUTPUT_CNTL, disp_output_cntl); + WREG32_P(RADEON_GPIOPAD_A, gpiopad_a, ~1); + } else { + WREG32(RADEON_DISP_HW_DEBUG, disp_hw_debug); + } + } + + WREG32_PLL(RADEON_PIXCLKS_CNTL, pixclks_cntl); + + return found; + +} + +static const struct drm_encoder_helper_funcs radeon_legacy_tv_dac_helper_funcs = { + .dpms = radeon_legacy_tv_dac_dpms, + .mode_fixup = radeon_legacy_mode_fixup, + .prepare = radeon_legacy_tv_dac_prepare, + .mode_set = radeon_legacy_tv_dac_mode_set, + .commit = radeon_legacy_tv_dac_commit, + .detect = radeon_legacy_tv_dac_detect, + .disable = radeon_legacy_encoder_disable, +}; + + +static const struct drm_encoder_funcs radeon_legacy_tv_dac_enc_funcs = { + .destroy = radeon_enc_destroy, +}; + + +static struct radeon_encoder_int_tmds *radeon_legacy_get_tmds_info(struct radeon_encoder *encoder) +{ + struct drm_device *dev = encoder->base.dev; + struct radeon_device *rdev = dev->dev_private; + struct radeon_encoder_int_tmds *tmds = NULL; + bool ret; + + tmds = malloc(sizeof(struct radeon_encoder_int_tmds), + DRM_MEM_DRIVER, M_ZERO | M_WAITOK); + + if (!tmds) + return NULL; + + if (rdev->is_atom_bios) + ret = radeon_atombios_get_tmds_info(encoder, tmds); + else + ret = radeon_legacy_get_tmds_info_from_combios(encoder, tmds); + + if (ret == false) + radeon_legacy_get_tmds_info_from_table(encoder, tmds); + + return tmds; +} + +static struct radeon_encoder_ext_tmds *radeon_legacy_get_ext_tmds_info(struct radeon_encoder *encoder) +{ + struct drm_device *dev = encoder->base.dev; + struct radeon_device *rdev = dev->dev_private; + struct radeon_encoder_ext_tmds *tmds = NULL; + bool ret; + + if (rdev->is_atom_bios) + return NULL; + + tmds = malloc(sizeof(struct radeon_encoder_ext_tmds), + DRM_MEM_DRIVER, M_ZERO | M_WAITOK); + + if (!tmds) + return NULL; + + ret = radeon_legacy_get_ext_tmds_info_from_combios(encoder, tmds); + + if (ret == false) + radeon_legacy_get_ext_tmds_info_from_table(encoder, tmds); + + return tmds; +} + +void +radeon_add_legacy_encoder(struct drm_device *dev, uint32_t encoder_enum, uint32_t supported_device) +{ + struct radeon_device *rdev = dev->dev_private; + struct drm_encoder *encoder; + struct radeon_encoder *radeon_encoder; + + /* see if we already added it */ + list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { + radeon_encoder = to_radeon_encoder(encoder); + if (radeon_encoder->encoder_enum == encoder_enum) { + radeon_encoder->devices |= supported_device; + return; + } + + } + + /* add a new one */ + radeon_encoder = malloc(sizeof(struct radeon_encoder), + DRM_MEM_DRIVER, M_ZERO | M_WAITOK); + if (!radeon_encoder) + return; + + encoder = &radeon_encoder->base; + if (rdev->flags & RADEON_SINGLE_CRTC) + encoder->possible_crtcs = 0x1; + else + encoder->possible_crtcs = 0x3; + + radeon_encoder->enc_priv = NULL; + + radeon_encoder->encoder_enum = encoder_enum; + radeon_encoder->encoder_id = (encoder_enum & OBJECT_ID_MASK) >> OBJECT_ID_SHIFT; + radeon_encoder->devices = supported_device; + radeon_encoder->rmx_type = RMX_OFF; + + switch (radeon_encoder->encoder_id) { + case ENCODER_OBJECT_ID_INTERNAL_LVDS: + encoder->possible_crtcs = 0x1; + drm_encoder_init(dev, encoder, &radeon_legacy_lvds_enc_funcs, DRM_MODE_ENCODER_LVDS); + drm_encoder_helper_add(encoder, &radeon_legacy_lvds_helper_funcs); + if (rdev->is_atom_bios) + radeon_encoder->enc_priv = radeon_atombios_get_lvds_info(radeon_encoder); + else + radeon_encoder->enc_priv = radeon_combios_get_lvds_info(radeon_encoder); + radeon_encoder->rmx_type = RMX_FULL; + break; + case ENCODER_OBJECT_ID_INTERNAL_TMDS1: + drm_encoder_init(dev, encoder, &radeon_legacy_tmds_int_enc_funcs, DRM_MODE_ENCODER_TMDS); + drm_encoder_helper_add(encoder, &radeon_legacy_tmds_int_helper_funcs); + radeon_encoder->enc_priv = radeon_legacy_get_tmds_info(radeon_encoder); + break; + case ENCODER_OBJECT_ID_INTERNAL_DAC1: + drm_encoder_init(dev, encoder, &radeon_legacy_primary_dac_enc_funcs, DRM_MODE_ENCODER_DAC); + drm_encoder_helper_add(encoder, &radeon_legacy_primary_dac_helper_funcs); + if (rdev->is_atom_bios) + radeon_encoder->enc_priv = radeon_atombios_get_primary_dac_info(radeon_encoder); + else + radeon_encoder->enc_priv = radeon_combios_get_primary_dac_info(radeon_encoder); + break; + case ENCODER_OBJECT_ID_INTERNAL_DAC2: + drm_encoder_init(dev, encoder, &radeon_legacy_tv_dac_enc_funcs, DRM_MODE_ENCODER_TVDAC); + drm_encoder_helper_add(encoder, &radeon_legacy_tv_dac_helper_funcs); + if (rdev->is_atom_bios) + radeon_encoder->enc_priv = radeon_atombios_get_tv_dac_info(radeon_encoder); + else + radeon_encoder->enc_priv = radeon_combios_get_tv_dac_info(radeon_encoder); + break; + case ENCODER_OBJECT_ID_INTERNAL_DVO1: + drm_encoder_init(dev, encoder, &radeon_legacy_tmds_ext_enc_funcs, DRM_MODE_ENCODER_TMDS); + drm_encoder_helper_add(encoder, &radeon_legacy_tmds_ext_helper_funcs); + if (!rdev->is_atom_bios) + radeon_encoder->enc_priv = radeon_legacy_get_ext_tmds_info(radeon_encoder); + break; + } +} diff --git a/sys/dev/drm2/radeon/radeon_legacy_tv.c b/sys/dev/drm2/radeon/radeon_legacy_tv.c new file mode 100644 index 00000000000..5311a48a56e --- /dev/null +++ b/sys/dev/drm2/radeon/radeon_legacy_tv.c @@ -0,0 +1,926 @@ +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include "radeon.h" + +/* + * Integrated TV out support based on the GATOS code by + * Federico Ulivi + */ + + +/* + * Limits of h/v positions (hPos & vPos) + */ +#define MAX_H_POSITION 5 /* Range: [-5..5], negative is on the left, 0 is default, positive is on the right */ +#define MAX_V_POSITION 5 /* Range: [-5..5], negative is up, 0 is default, positive is down */ + +/* + * Unit for hPos (in TV clock periods) + */ +#define H_POS_UNIT 10 + +/* + * Indexes in h. code timing table for horizontal line position adjustment + */ +#define H_TABLE_POS1 6 +#define H_TABLE_POS2 8 + +/* + * Limits of hor. size (hSize) + */ +#define MAX_H_SIZE 5 /* Range: [-5..5], negative is smaller, positive is larger */ + +/* tv standard constants */ +#define NTSC_TV_CLOCK_T 233 +#define NTSC_TV_VFTOTAL 1 +#define NTSC_TV_LINES_PER_FRAME 525 +#define NTSC_TV_ZERO_H_SIZE 479166 +#define NTSC_TV_H_SIZE_UNIT 9478 + +#define PAL_TV_CLOCK_T 188 +#define PAL_TV_VFTOTAL 3 +#define PAL_TV_LINES_PER_FRAME 625 +#define PAL_TV_ZERO_H_SIZE 473200 +#define PAL_TV_H_SIZE_UNIT 9360 + +/* tv pll setting for 27 mhz ref clk */ +#define NTSC_TV_PLL_M_27 22 +#define NTSC_TV_PLL_N_27 175 +#define NTSC_TV_PLL_P_27 5 + +#define PAL_TV_PLL_M_27 113 +#define PAL_TV_PLL_N_27 668 +#define PAL_TV_PLL_P_27 3 + +/* tv pll setting for 14 mhz ref clk */ +#define NTSC_TV_PLL_M_14 33 +#define NTSC_TV_PLL_N_14 693 +#define NTSC_TV_PLL_P_14 7 + +#define PAL_TV_PLL_M_14 19 +#define PAL_TV_PLL_N_14 353 +#define PAL_TV_PLL_P_14 5 + +#define VERT_LEAD_IN_LINES 2 +#define FRAC_BITS 0xe +#define FRAC_MASK 0x3fff + +struct radeon_tv_mode_constants { + uint16_t hor_resolution; + uint16_t ver_resolution; + enum radeon_tv_std standard; + uint16_t hor_total; + uint16_t ver_total; + uint16_t hor_start; + uint16_t hor_syncstart; + uint16_t ver_syncstart; + unsigned def_restart; + uint16_t crtcPLL_N; + uint8_t crtcPLL_M; + uint8_t crtcPLL_post_div; + unsigned pix_to_tv; +}; + +static const uint16_t hor_timing_NTSC[MAX_H_CODE_TIMING_LEN] = { + 0x0007, + 0x003f, + 0x0263, + 0x0a24, + 0x2a6b, + 0x0a36, + 0x126d, /* H_TABLE_POS1 */ + 0x1bfe, + 0x1a8f, /* H_TABLE_POS2 */ + 0x1ec7, + 0x3863, + 0x1bfe, + 0x1bfe, + 0x1a2a, + 0x1e95, + 0x0e31, + 0x201b, + 0 +}; + +static const uint16_t vert_timing_NTSC[MAX_V_CODE_TIMING_LEN] = { + 0x2001, + 0x200d, + 0x1006, + 0x0c06, + 0x1006, + 0x1818, + 0x21e3, + 0x1006, + 0x0c06, + 0x1006, + 0x1817, + 0x21d4, + 0x0002, + 0 +}; + +static const uint16_t hor_timing_PAL[MAX_H_CODE_TIMING_LEN] = { + 0x0007, + 0x0058, + 0x027c, + 0x0a31, + 0x2a77, + 0x0a95, + 0x124f, /* H_TABLE_POS1 */ + 0x1bfe, + 0x1b22, /* H_TABLE_POS2 */ + 0x1ef9, + 0x387c, + 0x1bfe, + 0x1bfe, + 0x1b31, + 0x1eb5, + 0x0e43, + 0x201b, + 0 +}; + +static const uint16_t vert_timing_PAL[MAX_V_CODE_TIMING_LEN] = { + 0x2001, + 0x200c, + 0x1005, + 0x0c05, + 0x1005, + 0x1401, + 0x1821, + 0x2240, + 0x1005, + 0x0c05, + 0x1005, + 0x1401, + 0x1822, + 0x2230, + 0x0002, + 0 +}; + +/********************************************************************** + * + * availableModes + * + * Table of all allowed modes for tv output + * + **********************************************************************/ +static const struct radeon_tv_mode_constants available_tv_modes[] = { + { /* NTSC timing for 27 Mhz ref clk */ + 800, /* horResolution */ + 600, /* verResolution */ + TV_STD_NTSC, /* standard */ + 990, /* horTotal */ + 740, /* verTotal */ + 813, /* horStart */ + 824, /* horSyncStart */ + 632, /* verSyncStart */ + 625592, /* defRestart */ + 592, /* crtcPLL_N */ + 91, /* crtcPLL_M */ + 4, /* crtcPLL_postDiv */ + 1022, /* pixToTV */ + }, + { /* PAL timing for 27 Mhz ref clk */ + 800, /* horResolution */ + 600, /* verResolution */ + TV_STD_PAL, /* standard */ + 1144, /* horTotal */ + 706, /* verTotal */ + 812, /* horStart */ + 824, /* horSyncStart */ + 669, /* verSyncStart */ + 696700, /* defRestart */ + 1382, /* crtcPLL_N */ + 231, /* crtcPLL_M */ + 4, /* crtcPLL_postDiv */ + 759, /* pixToTV */ + }, + { /* NTSC timing for 14 Mhz ref clk */ + 800, /* horResolution */ + 600, /* verResolution */ + TV_STD_NTSC, /* standard */ + 1018, /* horTotal */ + 727, /* verTotal */ + 813, /* horStart */ + 840, /* horSyncStart */ + 633, /* verSyncStart */ + 630627, /* defRestart */ + 347, /* crtcPLL_N */ + 14, /* crtcPLL_M */ + 8, /* crtcPLL_postDiv */ + 1022, /* pixToTV */ + }, + { /* PAL timing for 14 Mhz ref clk */ + 800, /* horResolution */ + 600, /* verResolution */ + TV_STD_PAL, /* standard */ + 1131, /* horTotal */ + 742, /* verTotal */ + 813, /* horStart */ + 840, /* horSyncStart */ + 633, /* verSyncStart */ + 708369, /* defRestart */ + 211, /* crtcPLL_N */ + 9, /* crtcPLL_M */ + 8, /* crtcPLL_postDiv */ + 759, /* pixToTV */ + }, +}; + +#define N_AVAILABLE_MODES DRM_ARRAY_SIZE(available_tv_modes) + +static const struct radeon_tv_mode_constants *radeon_legacy_tv_get_std_mode(struct radeon_encoder *radeon_encoder, + uint16_t *pll_ref_freq) +{ + struct drm_device *dev = radeon_encoder->base.dev; + struct radeon_device *rdev = dev->dev_private; + struct radeon_crtc *radeon_crtc; + struct radeon_encoder_tv_dac *tv_dac = radeon_encoder->enc_priv; + const struct radeon_tv_mode_constants *const_ptr; + struct radeon_pll *pll; + + radeon_crtc = to_radeon_crtc(radeon_encoder->base.crtc); + if (radeon_crtc->crtc_id == 1) + pll = &rdev->clock.p2pll; + else + pll = &rdev->clock.p1pll; + + if (pll_ref_freq) + *pll_ref_freq = pll->reference_freq; + + if (tv_dac->tv_std == TV_STD_NTSC || + tv_dac->tv_std == TV_STD_NTSC_J || + tv_dac->tv_std == TV_STD_PAL_M) { + if (pll->reference_freq == 2700) + const_ptr = &available_tv_modes[0]; + else + const_ptr = &available_tv_modes[2]; + } else { + if (pll->reference_freq == 2700) + const_ptr = &available_tv_modes[1]; + else + const_ptr = &available_tv_modes[3]; + } + return const_ptr; +} + +static long YCOEF_value[5] = { 2, 2, 0, 4, 0 }; +static long YCOEF_EN_value[5] = { 1, 1, 0, 1, 0 }; +static long SLOPE_value[5] = { 1, 2, 2, 4, 8 }; +static long SLOPE_limit[5] = { 6, 5, 4, 3, 2 }; + +static void radeon_wait_pll_lock(struct drm_encoder *encoder, unsigned n_tests, + unsigned n_wait_loops, unsigned cnt_threshold) +{ + struct drm_device *dev = encoder->dev; + struct radeon_device *rdev = dev->dev_private; + uint32_t save_pll_test; + unsigned int i, j; + + WREG32(RADEON_TEST_DEBUG_MUX, (RREG32(RADEON_TEST_DEBUG_MUX) & 0xffff60ff) | 0x100); + save_pll_test = RREG32_PLL(RADEON_PLL_TEST_CNTL); + WREG32_PLL(RADEON_PLL_TEST_CNTL, save_pll_test & ~RADEON_PLL_MASK_READ_B); + + WREG8(RADEON_CLOCK_CNTL_INDEX, RADEON_PLL_TEST_CNTL); + for (i = 0; i < n_tests; i++) { + WREG8(RADEON_CLOCK_CNTL_DATA + 3, 0); + for (j = 0; j < n_wait_loops; j++) + if (RREG8(RADEON_CLOCK_CNTL_DATA + 3) >= cnt_threshold) + break; + } + WREG32_PLL(RADEON_PLL_TEST_CNTL, save_pll_test); + WREG32(RADEON_TEST_DEBUG_MUX, RREG32(RADEON_TEST_DEBUG_MUX) & 0xffffe0ff); +} + + +static void radeon_legacy_tv_write_fifo(struct radeon_encoder *radeon_encoder, + uint16_t addr, uint32_t value) +{ + struct drm_device *dev = radeon_encoder->base.dev; + struct radeon_device *rdev = dev->dev_private; + uint32_t tmp; + int i = 0; + + WREG32(RADEON_TV_HOST_WRITE_DATA, value); + + WREG32(RADEON_TV_HOST_RD_WT_CNTL, addr); + WREG32(RADEON_TV_HOST_RD_WT_CNTL, addr | RADEON_HOST_FIFO_WT); + + do { + tmp = RREG32(RADEON_TV_HOST_RD_WT_CNTL); + if ((tmp & RADEON_HOST_FIFO_WT_ACK) == 0) + break; + i++; + } while (i < 10000); + WREG32(RADEON_TV_HOST_RD_WT_CNTL, 0); +} + +#if 0 /* included for completeness */ +static uint32_t radeon_legacy_tv_read_fifo(struct radeon_encoder *radeon_encoder, uint16_t addr) +{ + struct drm_device *dev = radeon_encoder->base.dev; + struct radeon_device *rdev = dev->dev_private; + uint32_t tmp; + int i = 0; + + WREG32(RADEON_TV_HOST_RD_WT_CNTL, addr); + WREG32(RADEON_TV_HOST_RD_WT_CNTL, addr | RADEON_HOST_FIFO_RD); + + do { + tmp = RREG32(RADEON_TV_HOST_RD_WT_CNTL); + if ((tmp & RADEON_HOST_FIFO_RD_ACK) == 0) + break; + i++; + } while (i < 10000); + WREG32(RADEON_TV_HOST_RD_WT_CNTL, 0); + return RREG32(RADEON_TV_HOST_READ_DATA); +} +#endif + +static uint16_t radeon_get_htiming_tables_addr(uint32_t tv_uv_adr) +{ + uint16_t h_table; + + switch ((tv_uv_adr & RADEON_HCODE_TABLE_SEL_MASK) >> RADEON_HCODE_TABLE_SEL_SHIFT) { + case 0: + h_table = RADEON_TV_MAX_FIFO_ADDR_INTERNAL; + break; + case 1: + h_table = ((tv_uv_adr & RADEON_TABLE1_BOT_ADR_MASK) >> RADEON_TABLE1_BOT_ADR_SHIFT) * 2; + break; + case 2: + h_table = ((tv_uv_adr & RADEON_TABLE3_TOP_ADR_MASK) >> RADEON_TABLE3_TOP_ADR_SHIFT) * 2; + break; + default: + h_table = 0; + break; + } + return h_table; +} + +static uint16_t radeon_get_vtiming_tables_addr(uint32_t tv_uv_adr) +{ + uint16_t v_table; + + switch ((tv_uv_adr & RADEON_VCODE_TABLE_SEL_MASK) >> RADEON_VCODE_TABLE_SEL_SHIFT) { + case 0: + v_table = ((tv_uv_adr & RADEON_MAX_UV_ADR_MASK) >> RADEON_MAX_UV_ADR_SHIFT) * 2 + 1; + break; + case 1: + v_table = ((tv_uv_adr & RADEON_TABLE1_BOT_ADR_MASK) >> RADEON_TABLE1_BOT_ADR_SHIFT) * 2 + 1; + break; + case 2: + v_table = ((tv_uv_adr & RADEON_TABLE3_TOP_ADR_MASK) >> RADEON_TABLE3_TOP_ADR_SHIFT) * 2 + 1; + break; + default: + v_table = 0; + break; + } + return v_table; +} + +static void radeon_restore_tv_timing_tables(struct radeon_encoder *radeon_encoder) +{ + struct drm_device *dev = radeon_encoder->base.dev; + struct radeon_device *rdev = dev->dev_private; + struct radeon_encoder_tv_dac *tv_dac = radeon_encoder->enc_priv; + uint16_t h_table, v_table; + uint32_t tmp; + int i; + + WREG32(RADEON_TV_UV_ADR, tv_dac->tv.tv_uv_adr); + h_table = radeon_get_htiming_tables_addr(tv_dac->tv.tv_uv_adr); + v_table = radeon_get_vtiming_tables_addr(tv_dac->tv.tv_uv_adr); + + for (i = 0; i < MAX_H_CODE_TIMING_LEN; i += 2, h_table--) { + tmp = ((uint32_t)tv_dac->tv.h_code_timing[i] << 14) | ((uint32_t)tv_dac->tv.h_code_timing[i+1]); + radeon_legacy_tv_write_fifo(radeon_encoder, h_table, tmp); + if (tv_dac->tv.h_code_timing[i] == 0 || tv_dac->tv.h_code_timing[i + 1] == 0) + break; + } + for (i = 0; i < MAX_V_CODE_TIMING_LEN; i += 2, v_table++) { + tmp = ((uint32_t)tv_dac->tv.v_code_timing[i+1] << 14) | ((uint32_t)tv_dac->tv.v_code_timing[i]); + radeon_legacy_tv_write_fifo(radeon_encoder, v_table, tmp); + if (tv_dac->tv.v_code_timing[i] == 0 || tv_dac->tv.v_code_timing[i + 1] == 0) + break; + } +} + +static void radeon_legacy_write_tv_restarts(struct radeon_encoder *radeon_encoder) +{ + struct drm_device *dev = radeon_encoder->base.dev; + struct radeon_device *rdev = dev->dev_private; + struct radeon_encoder_tv_dac *tv_dac = radeon_encoder->enc_priv; + WREG32(RADEON_TV_FRESTART, tv_dac->tv.frestart); + WREG32(RADEON_TV_HRESTART, tv_dac->tv.hrestart); + WREG32(RADEON_TV_VRESTART, tv_dac->tv.vrestart); +} + +static bool radeon_legacy_tv_init_restarts(struct drm_encoder *encoder) +{ + struct drm_device *dev = encoder->dev; + struct radeon_device *rdev = dev->dev_private; + struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); + struct radeon_encoder_tv_dac *tv_dac = radeon_encoder->enc_priv; + struct radeon_crtc *radeon_crtc; + int restart; + unsigned int h_total, v_total, f_total; + int v_offset, h_offset; + u16 p1, p2, h_inc; + bool h_changed; + const struct radeon_tv_mode_constants *const_ptr; + struct radeon_pll *pll; + + radeon_crtc = to_radeon_crtc(radeon_encoder->base.crtc); + if (radeon_crtc->crtc_id == 1) + pll = &rdev->clock.p2pll; + else + pll = &rdev->clock.p1pll; + + const_ptr = radeon_legacy_tv_get_std_mode(radeon_encoder, NULL); + if (!const_ptr) + return false; + + h_total = const_ptr->hor_total; + v_total = const_ptr->ver_total; + + if (tv_dac->tv_std == TV_STD_NTSC || + tv_dac->tv_std == TV_STD_NTSC_J || + tv_dac->tv_std == TV_STD_PAL_M || + tv_dac->tv_std == TV_STD_PAL_60) + f_total = NTSC_TV_VFTOTAL + 1; + else + f_total = PAL_TV_VFTOTAL + 1; + + /* adjust positions 1&2 in hor. cod timing table */ + h_offset = tv_dac->h_pos * H_POS_UNIT; + + if (tv_dac->tv_std == TV_STD_NTSC || + tv_dac->tv_std == TV_STD_NTSC_J || + tv_dac->tv_std == TV_STD_PAL_M) { + h_offset -= 50; + p1 = hor_timing_NTSC[H_TABLE_POS1]; + p2 = hor_timing_NTSC[H_TABLE_POS2]; + } else { + p1 = hor_timing_PAL[H_TABLE_POS1]; + p2 = hor_timing_PAL[H_TABLE_POS2]; + } + + p1 = (u16)((int)p1 + h_offset); + p2 = (u16)((int)p2 - h_offset); + + h_changed = (p1 != tv_dac->tv.h_code_timing[H_TABLE_POS1] || + p2 != tv_dac->tv.h_code_timing[H_TABLE_POS2]); + + tv_dac->tv.h_code_timing[H_TABLE_POS1] = p1; + tv_dac->tv.h_code_timing[H_TABLE_POS2] = p2; + + /* Convert hOffset from n. of TV clock periods to n. of CRTC clock periods (CRTC pixels) */ + h_offset = (h_offset * (int)(const_ptr->pix_to_tv)) / 1000; + + /* adjust restart */ + restart = const_ptr->def_restart; + + /* + * convert v_pos TV lines to n. of CRTC pixels + */ + if (tv_dac->tv_std == TV_STD_NTSC || + tv_dac->tv_std == TV_STD_NTSC_J || + tv_dac->tv_std == TV_STD_PAL_M || + tv_dac->tv_std == TV_STD_PAL_60) + v_offset = ((int)(v_total * h_total) * 2 * tv_dac->v_pos) / (int)(NTSC_TV_LINES_PER_FRAME); + else + v_offset = ((int)(v_total * h_total) * 2 * tv_dac->v_pos) / (int)(PAL_TV_LINES_PER_FRAME); + + restart -= v_offset + h_offset; + + DRM_DEBUG_KMS("compute_restarts: def = %u h = %d v = %d, p1 = %04x, p2 = %04x, restart = %d\n", + const_ptr->def_restart, tv_dac->h_pos, tv_dac->v_pos, p1, p2, restart); + + tv_dac->tv.hrestart = restart % h_total; + restart /= h_total; + tv_dac->tv.vrestart = restart % v_total; + restart /= v_total; + tv_dac->tv.frestart = restart % f_total; + + DRM_DEBUG_KMS("compute_restart: F/H/V=%u,%u,%u\n", + (unsigned)tv_dac->tv.frestart, + (unsigned)tv_dac->tv.vrestart, + (unsigned)tv_dac->tv.hrestart); + + /* compute h_inc from hsize */ + if (tv_dac->tv_std == TV_STD_NTSC || + tv_dac->tv_std == TV_STD_NTSC_J || + tv_dac->tv_std == TV_STD_PAL_M) + h_inc = (u16)((int)(const_ptr->hor_resolution * 4096 * NTSC_TV_CLOCK_T) / + (tv_dac->h_size * (int)(NTSC_TV_H_SIZE_UNIT) + (int)(NTSC_TV_ZERO_H_SIZE))); + else + h_inc = (u16)((int)(const_ptr->hor_resolution * 4096 * PAL_TV_CLOCK_T) / + (tv_dac->h_size * (int)(PAL_TV_H_SIZE_UNIT) + (int)(PAL_TV_ZERO_H_SIZE))); + + tv_dac->tv.timing_cntl = (tv_dac->tv.timing_cntl & ~RADEON_H_INC_MASK) | + ((u32)h_inc << RADEON_H_INC_SHIFT); + + DRM_DEBUG_KMS("compute_restart: h_size = %d h_inc = %d\n", tv_dac->h_size, h_inc); + + return h_changed; +} + +void radeon_legacy_tv_mode_set(struct drm_encoder *encoder, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + struct drm_device *dev = encoder->dev; + struct radeon_device *rdev = dev->dev_private; + struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); + struct radeon_encoder_tv_dac *tv_dac = radeon_encoder->enc_priv; + const struct radeon_tv_mode_constants *const_ptr; + struct radeon_crtc *radeon_crtc; + int i; + uint16_t pll_ref_freq; + uint32_t vert_space, flicker_removal, tmp; + uint32_t tv_master_cntl, tv_rgb_cntl, tv_dac_cntl; + uint32_t tv_modulator_cntl1, tv_modulator_cntl2; + uint32_t tv_vscaler_cntl1, tv_vscaler_cntl2; + uint32_t tv_pll_cntl, tv_pll_cntl1, tv_ftotal; + uint32_t tv_y_fall_cntl, tv_y_rise_cntl, tv_y_saw_tooth_cntl; + uint32_t m, n, p; + const uint16_t *hor_timing; + const uint16_t *vert_timing; + + const_ptr = radeon_legacy_tv_get_std_mode(radeon_encoder, &pll_ref_freq); + if (!const_ptr) + return; + + radeon_crtc = to_radeon_crtc(encoder->crtc); + + tv_master_cntl = (RADEON_VIN_ASYNC_RST | + RADEON_CRT_FIFO_CE_EN | + RADEON_TV_FIFO_CE_EN | + RADEON_TV_ON); + + if (!ASIC_IS_R300(rdev)) + tv_master_cntl |= RADEON_TVCLK_ALWAYS_ONb; + + if (tv_dac->tv_std == TV_STD_NTSC || + tv_dac->tv_std == TV_STD_NTSC_J) + tv_master_cntl |= RADEON_RESTART_PHASE_FIX; + + tv_modulator_cntl1 = (RADEON_SLEW_RATE_LIMIT | + RADEON_SYNC_TIP_LEVEL | + RADEON_YFLT_EN | + RADEON_UVFLT_EN | + (6 << RADEON_CY_FILT_BLEND_SHIFT)); + + if (tv_dac->tv_std == TV_STD_NTSC || + tv_dac->tv_std == TV_STD_NTSC_J) { + tv_modulator_cntl1 |= (0x46 << RADEON_SET_UP_LEVEL_SHIFT) | + (0x3b << RADEON_BLANK_LEVEL_SHIFT); + tv_modulator_cntl2 = (-111 & RADEON_TV_U_BURST_LEVEL_MASK) | + ((0 & RADEON_TV_V_BURST_LEVEL_MASK) << RADEON_TV_V_BURST_LEVEL_SHIFT); + } else if (tv_dac->tv_std == TV_STD_SCART_PAL) { + tv_modulator_cntl1 |= RADEON_ALT_PHASE_EN; + tv_modulator_cntl2 = (0 & RADEON_TV_U_BURST_LEVEL_MASK) | + ((0 & RADEON_TV_V_BURST_LEVEL_MASK) << RADEON_TV_V_BURST_LEVEL_SHIFT); + } else { + tv_modulator_cntl1 |= RADEON_ALT_PHASE_EN | + (0x3b << RADEON_SET_UP_LEVEL_SHIFT) | + (0x3b << RADEON_BLANK_LEVEL_SHIFT); + tv_modulator_cntl2 = (-78 & RADEON_TV_U_BURST_LEVEL_MASK) | + ((62 & RADEON_TV_V_BURST_LEVEL_MASK) << RADEON_TV_V_BURST_LEVEL_SHIFT); + } + + + tv_rgb_cntl = (RADEON_RGB_DITHER_EN + | RADEON_TVOUT_SCALE_EN + | (0x0b << RADEON_UVRAM_READ_MARGIN_SHIFT) + | (0x07 << RADEON_FIFORAM_FFMACRO_READ_MARGIN_SHIFT) + | RADEON_RGB_ATTEN_SEL(0x3) + | RADEON_RGB_ATTEN_VAL(0xc)); + + if (radeon_crtc->crtc_id == 1) + tv_rgb_cntl |= RADEON_RGB_SRC_SEL_CRTC2; + else { + if (radeon_crtc->rmx_type != RMX_OFF) + tv_rgb_cntl |= RADEON_RGB_SRC_SEL_RMX; + else + tv_rgb_cntl |= RADEON_RGB_SRC_SEL_CRTC1; + } + + if (tv_dac->tv_std == TV_STD_NTSC || + tv_dac->tv_std == TV_STD_NTSC_J || + tv_dac->tv_std == TV_STD_PAL_M || + tv_dac->tv_std == TV_STD_PAL_60) + vert_space = const_ptr->ver_total * 2 * 10000 / NTSC_TV_LINES_PER_FRAME; + else + vert_space = const_ptr->ver_total * 2 * 10000 / PAL_TV_LINES_PER_FRAME; + + tmp = RREG32(RADEON_TV_VSCALER_CNTL1); + tmp &= 0xe3ff0000; + tmp |= (vert_space * (1 << FRAC_BITS) / 10000); + tv_vscaler_cntl1 = tmp; + + if (pll_ref_freq == 2700) + tv_vscaler_cntl1 |= RADEON_RESTART_FIELD; + + if (const_ptr->hor_resolution == 1024) + tv_vscaler_cntl1 |= (4 << RADEON_Y_DEL_W_SIG_SHIFT); + else + tv_vscaler_cntl1 |= (2 << RADEON_Y_DEL_W_SIG_SHIFT); + + /* scale up for int divide */ + tmp = const_ptr->ver_total * 2 * 1000; + if (tv_dac->tv_std == TV_STD_NTSC || + tv_dac->tv_std == TV_STD_NTSC_J || + tv_dac->tv_std == TV_STD_PAL_M || + tv_dac->tv_std == TV_STD_PAL_60) { + tmp /= NTSC_TV_LINES_PER_FRAME; + } else { + tmp /= PAL_TV_LINES_PER_FRAME; + } + flicker_removal = (tmp + 500) / 1000; + + if (flicker_removal < 3) + flicker_removal = 3; + for (i = 0; i < DRM_ARRAY_SIZE(SLOPE_limit); ++i) { + if (flicker_removal == SLOPE_limit[i]) + break; + } + + tv_y_saw_tooth_cntl = (vert_space * SLOPE_value[i] * (1 << (FRAC_BITS - 1)) + + 5001) / 10000 / 8 | ((SLOPE_value[i] * + (1 << (FRAC_BITS - 1)) / 8) << 16); + tv_y_fall_cntl = + (YCOEF_EN_value[i] << 17) | ((YCOEF_value[i] * (1 << 8) / 8) << 24) | + RADEON_Y_FALL_PING_PONG | (272 * SLOPE_value[i] / 8) * (1 << (FRAC_BITS - 1)) / + 1024; + tv_y_rise_cntl = RADEON_Y_RISE_PING_PONG| + (flicker_removal * 1024 - 272) * SLOPE_value[i] / 8 * (1 << (FRAC_BITS - 1)) / 1024; + + tv_vscaler_cntl2 = RREG32(RADEON_TV_VSCALER_CNTL2) & 0x00fffff0; + tv_vscaler_cntl2 |= (0x10 << 24) | + RADEON_DITHER_MODE | + RADEON_Y_OUTPUT_DITHER_EN | + RADEON_UV_OUTPUT_DITHER_EN | + RADEON_UV_TO_BUF_DITHER_EN; + + tmp = (tv_vscaler_cntl1 >> RADEON_UV_INC_SHIFT) & RADEON_UV_INC_MASK; + tmp = ((16384 * 256 * 10) / tmp + 5) / 10; + tmp = (tmp << RADEON_UV_OUTPUT_POST_SCALE_SHIFT) | 0x000b0000; + tv_dac->tv.timing_cntl = tmp; + + if (tv_dac->tv_std == TV_STD_NTSC || + tv_dac->tv_std == TV_STD_NTSC_J || + tv_dac->tv_std == TV_STD_PAL_M || + tv_dac->tv_std == TV_STD_PAL_60) + tv_dac_cntl = tv_dac->ntsc_tvdac_adj; + else + tv_dac_cntl = tv_dac->pal_tvdac_adj; + + tv_dac_cntl |= RADEON_TV_DAC_NBLANK | RADEON_TV_DAC_NHOLD; + + if (tv_dac->tv_std == TV_STD_NTSC || + tv_dac->tv_std == TV_STD_NTSC_J) + tv_dac_cntl |= RADEON_TV_DAC_STD_NTSC; + else + tv_dac_cntl |= RADEON_TV_DAC_STD_PAL; + + if (tv_dac->tv_std == TV_STD_NTSC || + tv_dac->tv_std == TV_STD_NTSC_J) { + if (pll_ref_freq == 2700) { + m = NTSC_TV_PLL_M_27; + n = NTSC_TV_PLL_N_27; + p = NTSC_TV_PLL_P_27; + } else { + m = NTSC_TV_PLL_M_14; + n = NTSC_TV_PLL_N_14; + p = NTSC_TV_PLL_P_14; + } + } else { + if (pll_ref_freq == 2700) { + m = PAL_TV_PLL_M_27; + n = PAL_TV_PLL_N_27; + p = PAL_TV_PLL_P_27; + } else { + m = PAL_TV_PLL_M_14; + n = PAL_TV_PLL_N_14; + p = PAL_TV_PLL_P_14; + } + } + + tv_pll_cntl = (m & RADEON_TV_M0LO_MASK) | + (((m >> 8) & RADEON_TV_M0HI_MASK) << RADEON_TV_M0HI_SHIFT) | + ((n & RADEON_TV_N0LO_MASK) << RADEON_TV_N0LO_SHIFT) | + (((n >> 9) & RADEON_TV_N0HI_MASK) << RADEON_TV_N0HI_SHIFT) | + ((p & RADEON_TV_P_MASK) << RADEON_TV_P_SHIFT); + + tv_pll_cntl1 = (((4 & RADEON_TVPCP_MASK) << RADEON_TVPCP_SHIFT) | + ((4 & RADEON_TVPVG_MASK) << RADEON_TVPVG_SHIFT) | + ((1 & RADEON_TVPDC_MASK) << RADEON_TVPDC_SHIFT) | + RADEON_TVCLK_SRC_SEL_TVPLL | + RADEON_TVPLL_TEST_DIS); + + tv_dac->tv.tv_uv_adr = 0xc8; + + if (tv_dac->tv_std == TV_STD_NTSC || + tv_dac->tv_std == TV_STD_NTSC_J || + tv_dac->tv_std == TV_STD_PAL_M || + tv_dac->tv_std == TV_STD_PAL_60) { + tv_ftotal = NTSC_TV_VFTOTAL; + hor_timing = hor_timing_NTSC; + vert_timing = vert_timing_NTSC; + } else { + hor_timing = hor_timing_PAL; + vert_timing = vert_timing_PAL; + tv_ftotal = PAL_TV_VFTOTAL; + } + + for (i = 0; i < MAX_H_CODE_TIMING_LEN; i++) { + if ((tv_dac->tv.h_code_timing[i] = hor_timing[i]) == 0) + break; + } + + for (i = 0; i < MAX_V_CODE_TIMING_LEN; i++) { + if ((tv_dac->tv.v_code_timing[i] = vert_timing[i]) == 0) + break; + } + + radeon_legacy_tv_init_restarts(encoder); + + /* play with DAC_CNTL */ + /* play with GPIOPAD_A */ + /* DISP_OUTPUT_CNTL */ + /* use reference freq */ + + /* program the TV registers */ + WREG32(RADEON_TV_MASTER_CNTL, (tv_master_cntl | RADEON_TV_ASYNC_RST | + RADEON_CRT_ASYNC_RST | RADEON_TV_FIFO_ASYNC_RST)); + + tmp = RREG32(RADEON_TV_DAC_CNTL); + tmp &= ~RADEON_TV_DAC_NBLANK; + tmp |= RADEON_TV_DAC_BGSLEEP | + RADEON_TV_DAC_RDACPD | + RADEON_TV_DAC_GDACPD | + RADEON_TV_DAC_BDACPD; + WREG32(RADEON_TV_DAC_CNTL, tmp); + + /* TV PLL */ + WREG32_PLL_P(RADEON_TV_PLL_CNTL1, 0, ~RADEON_TVCLK_SRC_SEL_TVPLL); + WREG32_PLL(RADEON_TV_PLL_CNTL, tv_pll_cntl); + WREG32_PLL_P(RADEON_TV_PLL_CNTL1, RADEON_TVPLL_RESET, ~RADEON_TVPLL_RESET); + + radeon_wait_pll_lock(encoder, 200, 800, 135); + + WREG32_PLL_P(RADEON_TV_PLL_CNTL1, 0, ~RADEON_TVPLL_RESET); + + radeon_wait_pll_lock(encoder, 300, 160, 27); + radeon_wait_pll_lock(encoder, 200, 800, 135); + + WREG32_PLL_P(RADEON_TV_PLL_CNTL1, 0, ~0xf); + WREG32_PLL_P(RADEON_TV_PLL_CNTL1, RADEON_TVCLK_SRC_SEL_TVPLL, ~RADEON_TVCLK_SRC_SEL_TVPLL); + + WREG32_PLL_P(RADEON_TV_PLL_CNTL1, (1 << RADEON_TVPDC_SHIFT), ~RADEON_TVPDC_MASK); + WREG32_PLL_P(RADEON_TV_PLL_CNTL1, 0, ~RADEON_TVPLL_SLEEP); + + /* TV HV */ + WREG32(RADEON_TV_RGB_CNTL, tv_rgb_cntl); + WREG32(RADEON_TV_HTOTAL, const_ptr->hor_total - 1); + WREG32(RADEON_TV_HDISP, const_ptr->hor_resolution - 1); + WREG32(RADEON_TV_HSTART, const_ptr->hor_start); + + WREG32(RADEON_TV_VTOTAL, const_ptr->ver_total - 1); + WREG32(RADEON_TV_VDISP, const_ptr->ver_resolution - 1); + WREG32(RADEON_TV_FTOTAL, tv_ftotal); + WREG32(RADEON_TV_VSCALER_CNTL1, tv_vscaler_cntl1); + WREG32(RADEON_TV_VSCALER_CNTL2, tv_vscaler_cntl2); + + WREG32(RADEON_TV_Y_FALL_CNTL, tv_y_fall_cntl); + WREG32(RADEON_TV_Y_RISE_CNTL, tv_y_rise_cntl); + WREG32(RADEON_TV_Y_SAW_TOOTH_CNTL, tv_y_saw_tooth_cntl); + + WREG32(RADEON_TV_MASTER_CNTL, (tv_master_cntl | RADEON_TV_ASYNC_RST | + RADEON_CRT_ASYNC_RST)); + + /* TV restarts */ + radeon_legacy_write_tv_restarts(radeon_encoder); + + /* tv timings */ + radeon_restore_tv_timing_tables(radeon_encoder); + + WREG32(RADEON_TV_MASTER_CNTL, (tv_master_cntl | RADEON_TV_ASYNC_RST)); + + /* tv std */ + WREG32(RADEON_TV_SYNC_CNTL, (RADEON_SYNC_PUB | RADEON_TV_SYNC_IO_DRIVE)); + WREG32(RADEON_TV_TIMING_CNTL, tv_dac->tv.timing_cntl); + WREG32(RADEON_TV_MODULATOR_CNTL1, tv_modulator_cntl1); + WREG32(RADEON_TV_MODULATOR_CNTL2, tv_modulator_cntl2); + WREG32(RADEON_TV_PRE_DAC_MUX_CNTL, (RADEON_Y_RED_EN | + RADEON_C_GRN_EN | + RADEON_CMP_BLU_EN | + RADEON_DAC_DITHER_EN)); + + WREG32(RADEON_TV_CRC_CNTL, 0); + + WREG32(RADEON_TV_MASTER_CNTL, tv_master_cntl); + + WREG32(RADEON_TV_GAIN_LIMIT_SETTINGS, ((0x17f << RADEON_UV_GAIN_LIMIT_SHIFT) | + (0x5ff << RADEON_Y_GAIN_LIMIT_SHIFT))); + WREG32(RADEON_TV_LINEAR_GAIN_SETTINGS, ((0x100 << RADEON_UV_GAIN_SHIFT) | + (0x100 << RADEON_Y_GAIN_SHIFT))); + + WREG32(RADEON_TV_DAC_CNTL, tv_dac_cntl); + +} + +void radeon_legacy_tv_adjust_crtc_reg(struct drm_encoder *encoder, + uint32_t *h_total_disp, uint32_t *h_sync_strt_wid, + uint32_t *v_total_disp, uint32_t *v_sync_strt_wid) +{ + struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); + const struct radeon_tv_mode_constants *const_ptr; + uint32_t tmp; + + const_ptr = radeon_legacy_tv_get_std_mode(radeon_encoder, NULL); + if (!const_ptr) + return; + + *h_total_disp = (((const_ptr->hor_resolution / 8) - 1) << RADEON_CRTC_H_DISP_SHIFT) | + (((const_ptr->hor_total / 8) - 1) << RADEON_CRTC_H_TOTAL_SHIFT); + + tmp = *h_sync_strt_wid; + tmp &= ~(RADEON_CRTC_H_SYNC_STRT_PIX | RADEON_CRTC_H_SYNC_STRT_CHAR); + tmp |= (((const_ptr->hor_syncstart / 8) - 1) << RADEON_CRTC_H_SYNC_STRT_CHAR_SHIFT) | + (const_ptr->hor_syncstart & 7); + *h_sync_strt_wid = tmp; + + *v_total_disp = ((const_ptr->ver_resolution - 1) << RADEON_CRTC_V_DISP_SHIFT) | + ((const_ptr->ver_total - 1) << RADEON_CRTC_V_TOTAL_SHIFT); + + tmp = *v_sync_strt_wid; + tmp &= ~RADEON_CRTC_V_SYNC_STRT; + tmp |= ((const_ptr->ver_syncstart - 1) << RADEON_CRTC_V_SYNC_STRT_SHIFT); + *v_sync_strt_wid = tmp; +} + +static int get_post_div(int value) +{ + int post_div; + switch (value) { + case 1: post_div = 0; break; + case 2: post_div = 1; break; + case 3: post_div = 4; break; + case 4: post_div = 2; break; + case 6: post_div = 6; break; + case 8: post_div = 3; break; + case 12: post_div = 7; break; + case 16: + default: post_div = 5; break; + } + return post_div; +} + +void radeon_legacy_tv_adjust_pll1(struct drm_encoder *encoder, + uint32_t *htotal_cntl, uint32_t *ppll_ref_div, + uint32_t *ppll_div_3, uint32_t *pixclks_cntl) +{ + struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); + const struct radeon_tv_mode_constants *const_ptr; + + const_ptr = radeon_legacy_tv_get_std_mode(radeon_encoder, NULL); + if (!const_ptr) + return; + + *htotal_cntl = (const_ptr->hor_total & 0x7) | RADEON_HTOT_CNTL_VGA_EN; + + *ppll_ref_div = const_ptr->crtcPLL_M; + + *ppll_div_3 = (const_ptr->crtcPLL_N & 0x7ff) | (get_post_div(const_ptr->crtcPLL_post_div) << 16); + *pixclks_cntl &= ~(RADEON_PIX2CLK_SRC_SEL_MASK | RADEON_PIXCLK_TV_SRC_SEL); + *pixclks_cntl |= RADEON_PIX2CLK_SRC_SEL_P2PLLCLK; +} + +void radeon_legacy_tv_adjust_pll2(struct drm_encoder *encoder, + uint32_t *htotal2_cntl, uint32_t *p2pll_ref_div, + uint32_t *p2pll_div_0, uint32_t *pixclks_cntl) +{ + struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); + const struct radeon_tv_mode_constants *const_ptr; + + const_ptr = radeon_legacy_tv_get_std_mode(radeon_encoder, NULL); + if (!const_ptr) + return; + + *htotal2_cntl = (const_ptr->hor_total & 0x7); + + *p2pll_ref_div = const_ptr->crtcPLL_M; + + *p2pll_div_0 = (const_ptr->crtcPLL_N & 0x7ff) | (get_post_div(const_ptr->crtcPLL_post_div) << 16); + *pixclks_cntl &= ~RADEON_PIX2CLK_SRC_SEL_MASK; + *pixclks_cntl |= RADEON_PIX2CLK_SRC_SEL_P2PLLCLK | RADEON_PIXCLK_TV_SRC_SEL; +} + diff --git a/sys/dev/drm2/radeon/radeon_mem.c b/sys/dev/drm2/radeon/radeon_mem.c new file mode 100644 index 00000000000..9ff87911373 --- /dev/null +++ b/sys/dev/drm2/radeon/radeon_mem.c @@ -0,0 +1,304 @@ +/* radeon_mem.c -- Simple GART/fb memory manager for radeon -*- linux-c -*- */ +/* + * Copyright (C) The Weather Channel, Inc. 2002. All Rights Reserved. + * + * The Weather Channel (TM) funded Tungsten Graphics to develop the + * initial release of the Radeon 8500 driver under the XFree86 license. + * This notice must be preserved. + * + * 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 (including the next + * paragraph) 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 + * PRECISION INSIGHT AND/OR ITS SUPPLIERS 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. + * + * Authors: + * Keith Whitwell + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include "radeon_drv.h" + +/* Very simple allocator for GART memory, working on a static range + * already mapped into each client's address space. + */ + +static struct mem_block *split_block(struct mem_block *p, int start, int size, + struct drm_file *file_priv) +{ + /* Maybe cut off the start of an existing block */ + if (start > p->start) { + struct mem_block *newblock = malloc(sizeof(*newblock), + DRM_MEM_DRIVER, M_WAITOK); + if (!newblock) + goto out; + newblock->start = start; + newblock->size = p->size - (start - p->start); + newblock->file_priv = NULL; + newblock->next = p->next; + newblock->prev = p; + p->next->prev = newblock; + p->next = newblock; + p->size -= newblock->size; + p = newblock; + } + + /* Maybe cut off the end of an existing block */ + if (size < p->size) { + struct mem_block *newblock = malloc(sizeof(*newblock), + DRM_MEM_DRIVER, M_WAITOK); + if (!newblock) + goto out; + newblock->start = start + size; + newblock->size = p->size - size; + newblock->file_priv = NULL; + newblock->next = p->next; + newblock->prev = p; + p->next->prev = newblock; + p->next = newblock; + p->size = size; + } + + out: + /* Our block is in the middle */ + p->file_priv = file_priv; + return p; +} + +static struct mem_block *alloc_block(struct mem_block *heap, int size, + int align2, struct drm_file *file_priv) +{ + struct mem_block *p; + int mask = (1 << align2) - 1; + + list_for_each(p, heap) { + int start = (p->start + mask) & ~mask; + if (p->file_priv == NULL && start + size <= p->start + p->size) + return split_block(p, start, size, file_priv); + } + + return NULL; +} + +static struct mem_block *find_block(struct mem_block *heap, int start) +{ + struct mem_block *p; + + list_for_each(p, heap) + if (p->start == start) + return p; + + return NULL; +} + +static void free_block(struct mem_block *p) +{ + p->file_priv = NULL; + + /* Assumes a single contiguous range. Needs a special file_priv in + * 'heap' to stop it being subsumed. + */ + if (p->next->file_priv == NULL) { + struct mem_block *q = p->next; + p->size += q->size; + p->next = q->next; + p->next->prev = p; + free(q, DRM_MEM_DRIVER); + } + + if (p->prev->file_priv == NULL) { + struct mem_block *q = p->prev; + q->size += p->size; + q->next = p->next; + q->next->prev = q; + free(p, DRM_MEM_DRIVER); + } +} + +/* Initialize. How to check for an uninitialized heap? + */ +static int init_heap(struct mem_block **heap, int start, int size) +{ + struct mem_block *blocks = malloc(sizeof(*blocks), + DRM_MEM_DRIVER, M_WAITOK); + + if (!blocks) + return -ENOMEM; + + *heap = malloc(sizeof(**heap), DRM_MEM_DRIVER, M_ZERO | M_WAITOK); + if (!*heap) { + free(blocks, DRM_MEM_DRIVER); + return -ENOMEM; + } + + blocks->start = start; + blocks->size = size; + blocks->file_priv = NULL; + blocks->next = blocks->prev = *heap; + + (*heap)->file_priv = (struct drm_file *) - 1; + (*heap)->next = (*heap)->prev = blocks; + return 0; +} + +/* Free all blocks associated with the releasing file. + */ +void radeon_mem_release(struct drm_file *file_priv, struct mem_block *heap) +{ + struct mem_block *p; + + if (!heap || !heap->next) + return; + + list_for_each(p, heap) { + if (p->file_priv == file_priv) + p->file_priv = NULL; + } + + /* Assumes a single contiguous range. Needs a special file_priv in + * 'heap' to stop it being subsumed. + */ + list_for_each(p, heap) { + while (p->file_priv == NULL && p->next->file_priv == NULL) { + struct mem_block *q = p->next; + p->size += q->size; + p->next = q->next; + p->next->prev = p; + free(q, DRM_MEM_DRIVER); + } + } +} + +/* Shutdown. + */ +void radeon_mem_takedown(struct mem_block **heap) +{ + struct mem_block *p; + + if (!*heap) + return; + + for (p = (*heap)->next; p != *heap;) { + struct mem_block *q = p; + p = p->next; + free(q, DRM_MEM_DRIVER); + } + + free(*heap, DRM_MEM_DRIVER); + *heap = NULL; +} + +/* IOCTL HANDLERS */ + +static struct mem_block **get_heap(drm_radeon_private_t * dev_priv, int region) +{ + switch (region) { + case RADEON_MEM_REGION_GART: + return &dev_priv->gart_heap; + case RADEON_MEM_REGION_FB: + return &dev_priv->fb_heap; + default: + return NULL; + } +} + +int radeon_mem_alloc(struct drm_device *dev, void *data, struct drm_file *file_priv) +{ + drm_radeon_private_t *dev_priv = dev->dev_private; + drm_radeon_mem_alloc_t *alloc = data; + struct mem_block *block, **heap; + + if (!dev_priv) { + DRM_ERROR("called with no initialization\n"); + return -EINVAL; + } + + heap = get_heap(dev_priv, alloc->region); + if (!heap || !*heap) + return -EFAULT; + + /* Make things easier on ourselves: all allocations at least + * 4k aligned. + */ + if (alloc->alignment < 12) + alloc->alignment = 12; + + block = alloc_block(*heap, alloc->size, alloc->alignment, file_priv); + + if (!block) + return -ENOMEM; + + if (DRM_COPY_TO_USER(alloc->region_offset, &block->start, + sizeof(int))) { + DRM_ERROR("copy_to_user\n"); + return -EFAULT; + } + + return 0; +} + +int radeon_mem_free(struct drm_device *dev, void *data, struct drm_file *file_priv) +{ + drm_radeon_private_t *dev_priv = dev->dev_private; + drm_radeon_mem_free_t *memfree = data; + struct mem_block *block, **heap; + + if (!dev_priv) { + DRM_ERROR("called with no initialization\n"); + return -EINVAL; + } + + heap = get_heap(dev_priv, memfree->region); + if (!heap || !*heap) + return -EFAULT; + + block = find_block(*heap, memfree->region_offset); + if (!block) + return -EFAULT; + + if (block->file_priv != file_priv) + return -EPERM; + + free_block(block); + return 0; +} + +int radeon_mem_init_heap(struct drm_device *dev, void *data, struct drm_file *file_priv) +{ + drm_radeon_private_t *dev_priv = dev->dev_private; + drm_radeon_mem_init_heap_t *initheap = data; + struct mem_block **heap; + + if (!dev_priv) { + DRM_ERROR("called with no initialization\n"); + return -EINVAL; + } + + heap = get_heap(dev_priv, initheap->region); + if (!heap) + return -EFAULT; + + if (*heap) { + DRM_ERROR("heap already initialized?"); + return -EFAULT; + } + + return init_heap(heap, initheap->start, initheap->size); +} diff --git a/sys/dev/drm2/radeon/radeon_mode.h b/sys/dev/drm2/radeon/radeon_mode.h new file mode 100644 index 00000000000..ac39bc3ee82 --- /dev/null +++ b/sys/dev/drm2/radeon/radeon_mode.h @@ -0,0 +1,738 @@ +/* + * Copyright 2000 ATI Technologies Inc., Markham, Ontario, and + * VA Linux Systems Inc., Fremont, California. + * Copyright 2008 Red Hat Inc. + * + * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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. + * + * Original Authors: + * Kevin E. Martin, Rickard E. Faith, Alan Hourihane + * + * Kernel port Author: Dave Airlie + */ + +#include +__FBSDID("$FreeBSD$"); + +#ifndef RADEON_MODE_H +#define RADEON_MODE_H + +#include +#include +#include +#include +#include + +struct radeon_bo; +struct radeon_device; + +#define to_radeon_crtc(x) container_of(x, struct radeon_crtc, base) +#define to_radeon_connector(x) container_of(x, struct radeon_connector, base) +#define to_radeon_encoder(x) container_of(x, struct radeon_encoder, base) +#define to_radeon_framebuffer(x) container_of(x, struct radeon_framebuffer, base) + +enum radeon_rmx_type { + RMX_OFF, + RMX_FULL, + RMX_CENTER, + RMX_ASPECT +}; + +enum radeon_tv_std { + TV_STD_NTSC, + TV_STD_PAL, + TV_STD_PAL_M, + TV_STD_PAL_60, + TV_STD_NTSC_J, + TV_STD_SCART_PAL, + TV_STD_SECAM, + TV_STD_PAL_CN, + TV_STD_PAL_N, +}; + +enum radeon_underscan_type { + UNDERSCAN_OFF, + UNDERSCAN_ON, + UNDERSCAN_AUTO, +}; + +enum radeon_hpd_id { + RADEON_HPD_1 = 0, + RADEON_HPD_2, + RADEON_HPD_3, + RADEON_HPD_4, + RADEON_HPD_5, + RADEON_HPD_6, + RADEON_HPD_NONE = 0xff, +}; + +#define RADEON_MAX_I2C_BUS 16 + +/* radeon gpio-based i2c + * 1. "mask" reg and bits + * grabs the gpio pins for software use + * 0=not held 1=held + * 2. "a" reg and bits + * output pin value + * 0=low 1=high + * 3. "en" reg and bits + * sets the pin direction + * 0=input 1=output + * 4. "y" reg and bits + * input pin value + * 0=low 1=high + */ +struct radeon_i2c_bus_rec { + bool valid; + /* id used by atom */ + uint8_t i2c_id; + /* id used by atom */ + enum radeon_hpd_id hpd; + /* can be used with hw i2c engine */ + bool hw_capable; + /* uses multi-media i2c engine */ + bool mm_i2c; + /* regs and bits */ + uint32_t mask_clk_reg; + uint32_t mask_data_reg; + uint32_t a_clk_reg; + uint32_t a_data_reg; + uint32_t en_clk_reg; + uint32_t en_data_reg; + uint32_t y_clk_reg; + uint32_t y_data_reg; + uint32_t mask_clk_mask; + uint32_t mask_data_mask; + uint32_t a_clk_mask; + uint32_t a_data_mask; + uint32_t en_clk_mask; + uint32_t en_data_mask; + uint32_t y_clk_mask; + uint32_t y_data_mask; +}; + +struct radeon_tmds_pll { + uint32_t freq; + uint32_t value; +}; + +#define RADEON_MAX_BIOS_CONNECTOR 16 + +/* pll flags */ +#define RADEON_PLL_USE_BIOS_DIVS (1 << 0) +#define RADEON_PLL_NO_ODD_POST_DIV (1 << 1) +#define RADEON_PLL_USE_REF_DIV (1 << 2) +#define RADEON_PLL_LEGACY (1 << 3) +#define RADEON_PLL_PREFER_LOW_REF_DIV (1 << 4) +#define RADEON_PLL_PREFER_HIGH_REF_DIV (1 << 5) +#define RADEON_PLL_PREFER_LOW_FB_DIV (1 << 6) +#define RADEON_PLL_PREFER_HIGH_FB_DIV (1 << 7) +#define RADEON_PLL_PREFER_LOW_POST_DIV (1 << 8) +#define RADEON_PLL_PREFER_HIGH_POST_DIV (1 << 9) +#define RADEON_PLL_USE_FRAC_FB_DIV (1 << 10) +#define RADEON_PLL_PREFER_CLOSEST_LOWER (1 << 11) +#define RADEON_PLL_USE_POST_DIV (1 << 12) +#define RADEON_PLL_IS_LCD (1 << 13) +#define RADEON_PLL_PREFER_MINM_OVER_MAXP (1 << 14) + +struct radeon_pll { + /* reference frequency */ + uint32_t reference_freq; + + /* fixed dividers */ + uint32_t reference_div; + uint32_t post_div; + + /* pll in/out limits */ + uint32_t pll_in_min; + uint32_t pll_in_max; + uint32_t pll_out_min; + uint32_t pll_out_max; + uint32_t lcd_pll_out_min; + uint32_t lcd_pll_out_max; + uint32_t best_vco; + + /* divider limits */ + uint32_t min_ref_div; + uint32_t max_ref_div; + uint32_t min_post_div; + uint32_t max_post_div; + uint32_t min_feedback_div; + uint32_t max_feedback_div; + uint32_t min_frac_feedback_div; + uint32_t max_frac_feedback_div; + + /* flags for the current clock */ + uint32_t flags; + + /* pll id */ + uint32_t id; +}; + +struct radeon_i2c_chan { + device_t adapter; + device_t iic_bus; + struct drm_device *dev; + struct radeon_i2c_bus_rec rec; + char name[48]; +}; + +/* mostly for macs, but really any system without connector tables */ +enum radeon_connector_table { + CT_NONE = 0, + CT_GENERIC, + CT_IBOOK, + CT_POWERBOOK_EXTERNAL, + CT_POWERBOOK_INTERNAL, + CT_POWERBOOK_VGA, + CT_MINI_EXTERNAL, + CT_MINI_INTERNAL, + CT_IMAC_G5_ISIGHT, + CT_EMAC, + CT_RN50_POWER, + CT_MAC_X800, + CT_MAC_G5_9600, + CT_SAM440EP, + CT_MAC_G4_SILVER +}; + +enum radeon_dvo_chip { + DVO_SIL164, + DVO_SIL1178, +}; + +struct radeon_fbdev; + +struct radeon_afmt { + bool enabled; + int offset; + bool last_buffer_filled_status; + int id; +}; + +struct radeon_mode_info { + struct atom_context *atom_context; + struct card_info *atom_card_info; + enum radeon_connector_table connector_table; + bool mode_config_initialized; + struct radeon_crtc *crtcs[6]; + struct radeon_afmt *afmt[6]; + /* DVI-I properties */ + struct drm_property *coherent_mode_property; + /* DAC enable load detect */ + struct drm_property *load_detect_property; + /* TV standard */ + struct drm_property *tv_std_property; + /* legacy TMDS PLL detect */ + struct drm_property *tmds_pll_property; + /* underscan */ + struct drm_property *underscan_property; + struct drm_property *underscan_hborder_property; + struct drm_property *underscan_vborder_property; + /* hardcoded DFP edid from BIOS */ + struct edid *bios_hardcoded_edid; + int bios_hardcoded_edid_size; + + /* pointer to fbdev info structure */ + struct radeon_fbdev *rfbdev; + /* firmware flags */ + u16 firmware_flags; + /* pointer to backlight encoder */ + struct radeon_encoder *bl_encoder; +}; + +#define RADEON_MAX_BL_LEVEL 0xFF + +#if defined(CONFIG_BACKLIGHT_CLASS_DEVICE) || defined(CONFIG_BACKLIGHT_CLASS_DEVICE_MODULE) + +struct radeon_backlight_privdata { + struct radeon_encoder *encoder; + uint8_t negative; +}; + +#endif + +#define MAX_H_CODE_TIMING_LEN 32 +#define MAX_V_CODE_TIMING_LEN 32 + +/* need to store these as reading + back code tables is excessive */ +struct radeon_tv_regs { + uint32_t tv_uv_adr; + uint32_t timing_cntl; + uint32_t hrestart; + uint32_t vrestart; + uint32_t frestart; + uint16_t h_code_timing[MAX_H_CODE_TIMING_LEN]; + uint16_t v_code_timing[MAX_V_CODE_TIMING_LEN]; +}; + +struct radeon_atom_ss { + uint16_t percentage; + uint8_t type; + uint16_t step; + uint8_t delay; + uint8_t range; + uint8_t refdiv; + /* asic_ss */ + uint16_t rate; + uint16_t amount; +}; + +struct radeon_crtc { + struct drm_crtc base; + int crtc_id; + u16 lut_r[256], lut_g[256], lut_b[256]; + bool enabled; + bool can_tile; + bool in_mode_set; + uint32_t crtc_offset; + struct drm_gem_object *cursor_bo; + uint64_t cursor_addr; + int cursor_width; + int cursor_height; + uint32_t legacy_display_base_addr; + uint32_t legacy_cursor_offset; + enum radeon_rmx_type rmx_type; + u8 h_border; + u8 v_border; + fixed20_12 vsc; + fixed20_12 hsc; + struct drm_display_mode native_mode; + int pll_id; + /* page flipping */ + struct radeon_unpin_work *unpin_work; + int deferred_flip_completion; + /* pll sharing */ + struct radeon_atom_ss ss; + bool ss_enabled; + u32 adjusted_clock; + int bpc; + u32 pll_reference_div; + u32 pll_post_div; + u32 pll_flags; + struct drm_encoder *encoder; + struct drm_connector *connector; +}; + +struct radeon_encoder_primary_dac { + /* legacy primary dac */ + uint32_t ps2_pdac_adj; +}; + +struct radeon_encoder_lvds { + /* legacy lvds */ + uint16_t panel_vcc_delay; + uint8_t panel_pwr_delay; + uint8_t panel_digon_delay; + uint8_t panel_blon_delay; + uint16_t panel_ref_divider; + uint8_t panel_post_divider; + uint16_t panel_fb_divider; + bool use_bios_dividers; + uint32_t lvds_gen_cntl; + /* panel mode */ + struct drm_display_mode native_mode; + struct backlight_device *bl_dev; + int dpms_mode; + uint8_t backlight_level; +}; + +struct radeon_encoder_tv_dac { + /* legacy tv dac */ + uint32_t ps2_tvdac_adj; + uint32_t ntsc_tvdac_adj; + uint32_t pal_tvdac_adj; + + int h_pos; + int v_pos; + int h_size; + int supported_tv_stds; + bool tv_on; + enum radeon_tv_std tv_std; + struct radeon_tv_regs tv; +}; + +struct radeon_encoder_int_tmds { + /* legacy int tmds */ + struct radeon_tmds_pll tmds_pll[4]; +}; + +struct radeon_encoder_ext_tmds { + /* tmds over dvo */ + struct radeon_i2c_chan *i2c_bus; + uint8_t slave_addr; + enum radeon_dvo_chip dvo_chip; +}; + +/* spread spectrum */ +struct radeon_encoder_atom_dig { + bool linkb; + /* atom dig */ + bool coherent_mode; + int dig_encoder; /* -1 disabled, 0 DIGA, 1 DIGB, etc. */ + /* atom lvds/edp */ + uint32_t lcd_misc; + uint16_t panel_pwr_delay; + uint32_t lcd_ss_id; + /* panel mode */ + struct drm_display_mode native_mode; + struct backlight_device *bl_dev; + int dpms_mode; + uint8_t backlight_level; + int panel_mode; + struct radeon_afmt *afmt; +}; + +struct radeon_encoder_atom_dac { + enum radeon_tv_std tv_std; +}; + +struct radeon_encoder { + struct drm_encoder base; + uint32_t encoder_enum; + uint32_t encoder_id; + uint32_t devices; + uint32_t active_device; + uint32_t flags; + uint32_t pixel_clock; + enum radeon_rmx_type rmx_type; + enum radeon_underscan_type underscan_type; + uint32_t underscan_hborder; + uint32_t underscan_vborder; + struct drm_display_mode native_mode; + void *enc_priv; + int audio_polling_active; + bool is_ext_encoder; + u16 caps; +}; + +struct radeon_connector_atom_dig { + uint32_t igp_lane_info; + /* displayport */ + struct radeon_i2c_chan *dp_i2c_bus; + u8 dpcd[DP_RECEIVER_CAP_SIZE]; + u8 dp_sink_type; + int dp_clock; + int dp_lane_count; + bool edp_on; +}; + +struct radeon_gpio_rec { + bool valid; + u8 id; + u32 reg; + u32 mask; +}; + +struct radeon_hpd { + enum radeon_hpd_id hpd; + u8 plugged_state; + struct radeon_gpio_rec gpio; +}; + +struct radeon_router { + u32 router_id; + struct radeon_i2c_bus_rec i2c_info; + u8 i2c_addr; + /* i2c mux */ + bool ddc_valid; + u8 ddc_mux_type; + u8 ddc_mux_control_pin; + u8 ddc_mux_state; + /* clock/data mux */ + bool cd_valid; + u8 cd_mux_type; + u8 cd_mux_control_pin; + u8 cd_mux_state; +}; + +struct radeon_connector { + struct drm_connector base; + uint32_t connector_id; + uint32_t devices; + struct radeon_i2c_chan *ddc_bus; + /* some systems have an hdmi and vga port with a shared ddc line */ + bool shared_ddc; + bool use_digital; + /* we need to mind the EDID between detect + and get modes due to analog/digital/tvencoder */ + struct edid *edid; + void *con_priv; + bool dac_load_detect; + bool detected_by_load; /* if the connection status was determined by load */ + uint16_t connector_object_id; + struct radeon_hpd hpd; + struct radeon_router router; + struct radeon_i2c_chan *router_bus; +}; + +struct radeon_framebuffer { + struct drm_framebuffer base; + struct drm_gem_object *obj; +}; + +#define ENCODER_MODE_IS_DP(em) (((em) == ATOM_ENCODER_MODE_DP) || \ + ((em) == ATOM_ENCODER_MODE_DP_MST)) + +extern enum radeon_tv_std +radeon_combios_get_tv_info(struct radeon_device *rdev); +extern enum radeon_tv_std +radeon_atombios_get_tv_info(struct radeon_device *rdev); + +extern struct drm_connector * +radeon_get_connector_for_encoder(struct drm_encoder *encoder); +extern struct drm_connector * +radeon_get_connector_for_encoder_init(struct drm_encoder *encoder); +extern bool radeon_dig_monitor_is_duallink(struct drm_encoder *encoder, + u32 pixel_clock); + +extern u16 radeon_encoder_get_dp_bridge_encoder_id(struct drm_encoder *encoder); +extern u16 radeon_connector_encoder_get_dp_bridge_encoder_id(struct drm_connector *connector); +extern bool radeon_connector_encoder_is_hbr2(struct drm_connector *connector); +extern bool radeon_connector_is_dp12_capable(struct drm_connector *connector); +extern int radeon_get_monitor_bpc(struct drm_connector *connector); + +extern void radeon_connector_hotplug(struct drm_connector *connector); +extern int radeon_dp_mode_valid_helper(struct drm_connector *connector, + struct drm_display_mode *mode); +extern void radeon_dp_set_link_config(struct drm_connector *connector, + const struct drm_display_mode *mode); +extern void radeon_dp_link_train(struct drm_encoder *encoder, + struct drm_connector *connector); +extern bool radeon_dp_needs_link_train(struct radeon_connector *radeon_connector); +extern u8 radeon_dp_getsinktype(struct radeon_connector *radeon_connector); +extern bool radeon_dp_getdpcd(struct radeon_connector *radeon_connector); +extern int radeon_dp_get_panel_mode(struct drm_encoder *encoder, + struct drm_connector *connector); +extern void atombios_dig_encoder_setup(struct drm_encoder *encoder, int action, int panel_mode); +extern void radeon_atom_encoder_init(struct radeon_device *rdev); +extern void radeon_atom_disp_eng_pll_init(struct radeon_device *rdev); +extern void atombios_dig_transmitter_setup(struct drm_encoder *encoder, + int action, uint8_t lane_num, + uint8_t lane_set); +extern void radeon_atom_ext_encoder_setup_ddc(struct drm_encoder *encoder); +extern struct drm_encoder *radeon_get_external_encoder(struct drm_encoder *encoder); +extern int radeon_dp_i2c_aux_ch(device_t dev, int mode, + u8 write_byte, u8 *read_byte); + +extern void radeon_i2c_init(struct radeon_device *rdev); +extern void radeon_i2c_fini(struct radeon_device *rdev); +extern void radeon_combios_i2c_init(struct radeon_device *rdev); +extern void radeon_atombios_i2c_init(struct radeon_device *rdev); +extern void radeon_i2c_add(struct radeon_device *rdev, + struct radeon_i2c_bus_rec *rec, + const char *name); +extern struct radeon_i2c_chan *radeon_i2c_lookup(struct radeon_device *rdev, + struct radeon_i2c_bus_rec *i2c_bus); +extern struct radeon_i2c_chan *radeon_i2c_create_dp(struct drm_device *dev, + struct radeon_i2c_bus_rec *rec, + const char *name); +extern struct radeon_i2c_chan *radeon_i2c_create(struct drm_device *dev, + struct radeon_i2c_bus_rec *rec, + const char *name); +extern void radeon_i2c_destroy(struct radeon_i2c_chan *i2c); +extern void radeon_i2c_get_byte(struct radeon_i2c_chan *i2c_bus, + u8 slave_addr, + u8 addr, + u8 *val); +extern void radeon_i2c_put_byte(struct radeon_i2c_chan *i2c, + u8 slave_addr, + u8 addr, + u8 val); +extern void radeon_router_select_ddc_port(struct radeon_connector *radeon_connector); +extern void radeon_router_select_cd_port(struct radeon_connector *radeon_connector); +extern bool radeon_ddc_probe(struct radeon_connector *radeon_connector, bool use_aux); +extern int radeon_ddc_get_modes(struct radeon_connector *radeon_connector); + +extern struct drm_encoder *radeon_best_encoder(struct drm_connector *connector); + +extern bool radeon_atombios_get_ppll_ss_info(struct radeon_device *rdev, + struct radeon_atom_ss *ss, + int id); +extern bool radeon_atombios_get_asic_ss_info(struct radeon_device *rdev, + struct radeon_atom_ss *ss, + int id, u32 clock); + +extern void radeon_compute_pll_legacy(struct radeon_pll *pll, + uint64_t freq, + uint32_t *dot_clock_p, + uint32_t *fb_div_p, + uint32_t *frac_fb_div_p, + uint32_t *ref_div_p, + uint32_t *post_div_p); + +extern void radeon_compute_pll_avivo(struct radeon_pll *pll, + u32 freq, + u32 *dot_clock_p, + u32 *fb_div_p, + u32 *frac_fb_div_p, + u32 *ref_div_p, + u32 *post_div_p); + +extern void radeon_setup_encoder_clones(struct drm_device *dev); + +struct drm_encoder *radeon_encoder_legacy_lvds_add(struct drm_device *dev, int bios_index); +struct drm_encoder *radeon_encoder_legacy_primary_dac_add(struct drm_device *dev, int bios_index, int with_tv); +struct drm_encoder *radeon_encoder_legacy_tv_dac_add(struct drm_device *dev, int bios_index, int with_tv); +struct drm_encoder *radeon_encoder_legacy_tmds_int_add(struct drm_device *dev, int bios_index); +struct drm_encoder *radeon_encoder_legacy_tmds_ext_add(struct drm_device *dev, int bios_index); +extern void atombios_dvo_setup(struct drm_encoder *encoder, int action); +extern void atombios_digital_setup(struct drm_encoder *encoder, int action); +extern int atombios_get_encoder_mode(struct drm_encoder *encoder); +extern bool atombios_set_edp_panel_power(struct drm_connector *connector, int action); +extern void radeon_encoder_set_active_device(struct drm_encoder *encoder); + +extern void radeon_crtc_load_lut(struct drm_crtc *crtc); +extern int atombios_crtc_set_base(struct drm_crtc *crtc, int x, int y, + struct drm_framebuffer *old_fb); +extern int atombios_crtc_set_base_atomic(struct drm_crtc *crtc, + struct drm_framebuffer *fb, + int x, int y, + enum mode_set_atomic state); +extern int atombios_crtc_mode_set(struct drm_crtc *crtc, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode, + int x, int y, + struct drm_framebuffer *old_fb); +extern void atombios_crtc_dpms(struct drm_crtc *crtc, int mode); + +extern int radeon_crtc_set_base(struct drm_crtc *crtc, int x, int y, + struct drm_framebuffer *old_fb); +extern int radeon_crtc_set_base_atomic(struct drm_crtc *crtc, + struct drm_framebuffer *fb, + int x, int y, + enum mode_set_atomic state); +extern int radeon_crtc_do_set_base(struct drm_crtc *crtc, + struct drm_framebuffer *fb, + int x, int y, int atomic); +extern int radeon_crtc_cursor_set(struct drm_crtc *crtc, + struct drm_file *file_priv, + uint32_t handle, + uint32_t width, + uint32_t height); +extern int radeon_crtc_cursor_move(struct drm_crtc *crtc, + int x, int y); + +extern int radeon_get_crtc_scanoutpos(struct drm_device *dev, int crtc, + int *vpos, int *hpos); + +extern bool radeon_combios_check_hardcoded_edid(struct radeon_device *rdev); +extern struct edid * +radeon_bios_get_hardcoded_edid(struct radeon_device *rdev); +extern bool radeon_atom_get_clock_info(struct drm_device *dev); +extern bool radeon_combios_get_clock_info(struct drm_device *dev); +extern struct radeon_encoder_atom_dig * +radeon_atombios_get_lvds_info(struct radeon_encoder *encoder); +extern bool radeon_atombios_get_tmds_info(struct radeon_encoder *encoder, + struct radeon_encoder_int_tmds *tmds); +extern bool radeon_legacy_get_tmds_info_from_combios(struct radeon_encoder *encoder, + struct radeon_encoder_int_tmds *tmds); +extern bool radeon_legacy_get_tmds_info_from_table(struct radeon_encoder *encoder, + struct radeon_encoder_int_tmds *tmds); +extern bool radeon_legacy_get_ext_tmds_info_from_combios(struct radeon_encoder *encoder, + struct radeon_encoder_ext_tmds *tmds); +extern bool radeon_legacy_get_ext_tmds_info_from_table(struct radeon_encoder *encoder, + struct radeon_encoder_ext_tmds *tmds); +extern struct radeon_encoder_primary_dac * +radeon_atombios_get_primary_dac_info(struct radeon_encoder *encoder); +extern struct radeon_encoder_tv_dac * +radeon_atombios_get_tv_dac_info(struct radeon_encoder *encoder); +extern struct radeon_encoder_lvds * +radeon_combios_get_lvds_info(struct radeon_encoder *encoder); +extern void radeon_combios_get_ext_tmds_info(struct radeon_encoder *encoder); +extern struct radeon_encoder_tv_dac * +radeon_combios_get_tv_dac_info(struct radeon_encoder *encoder); +extern struct radeon_encoder_primary_dac * +radeon_combios_get_primary_dac_info(struct radeon_encoder *encoder); +extern bool radeon_combios_external_tmds_setup(struct drm_encoder *encoder); +extern void radeon_external_tmds_setup(struct drm_encoder *encoder); +extern void radeon_combios_output_lock(struct drm_encoder *encoder, bool lock); +extern void radeon_combios_initialize_bios_scratch_regs(struct drm_device *dev); +extern void radeon_atom_output_lock(struct drm_encoder *encoder, bool lock); +extern void radeon_atom_initialize_bios_scratch_regs(struct drm_device *dev); +extern void radeon_save_bios_scratch_regs(struct radeon_device *rdev); +extern void radeon_restore_bios_scratch_regs(struct radeon_device *rdev); +extern void +radeon_atombios_encoder_crtc_scratch_regs(struct drm_encoder *encoder, int crtc); +extern void +radeon_atombios_encoder_dpms_scratch_regs(struct drm_encoder *encoder, bool on); +extern void +radeon_combios_encoder_crtc_scratch_regs(struct drm_encoder *encoder, int crtc); +extern void +radeon_combios_encoder_dpms_scratch_regs(struct drm_encoder *encoder, bool on); +extern void radeon_crtc_fb_gamma_set(struct drm_crtc *crtc, u16 red, u16 green, + u16 blue, int regno); +extern void radeon_crtc_fb_gamma_get(struct drm_crtc *crtc, u16 *red, u16 *green, + u16 *blue, int regno); +int radeon_framebuffer_init(struct drm_device *dev, + struct radeon_framebuffer *rfb, + struct drm_mode_fb_cmd2 *mode_cmd, + struct drm_gem_object *obj); + +int radeonfb_remove(struct drm_device *dev, struct drm_framebuffer *fb); +bool radeon_get_legacy_connector_info_from_bios(struct drm_device *dev); +bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev); +void radeon_atombios_init_crtc(struct drm_device *dev, + struct radeon_crtc *radeon_crtc); +void radeon_legacy_init_crtc(struct drm_device *dev, + struct radeon_crtc *radeon_crtc); + +void radeon_get_clock_info(struct drm_device *dev); + +extern bool radeon_get_atom_connector_info_from_object_table(struct drm_device *dev); +extern bool radeon_get_atom_connector_info_from_supported_devices_table(struct drm_device *dev); + +void radeon_enc_destroy(struct drm_encoder *encoder); +void radeon_copy_fb(struct drm_device *dev, struct drm_gem_object *dst_obj); +void radeon_combios_asic_init(struct drm_device *dev); +bool radeon_crtc_scaling_mode_fixup(struct drm_crtc *crtc, + const struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode); +void radeon_panel_mode_fixup(struct drm_encoder *encoder, + struct drm_display_mode *adjusted_mode); +void atom_rv515_force_tv_scaler(struct radeon_device *rdev, struct radeon_crtc *radeon_crtc); + +/* legacy tv */ +void radeon_legacy_tv_adjust_crtc_reg(struct drm_encoder *encoder, + uint32_t *h_total_disp, uint32_t *h_sync_strt_wid, + uint32_t *v_total_disp, uint32_t *v_sync_strt_wid); +void radeon_legacy_tv_adjust_pll1(struct drm_encoder *encoder, + uint32_t *htotal_cntl, uint32_t *ppll_ref_div, + uint32_t *ppll_div_3, uint32_t *pixclks_cntl); +void radeon_legacy_tv_adjust_pll2(struct drm_encoder *encoder, + uint32_t *htotal2_cntl, uint32_t *p2pll_ref_div, + uint32_t *p2pll_div_0, uint32_t *pixclks_cntl); +void radeon_legacy_tv_mode_set(struct drm_encoder *encoder, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode); + +/* fbdev layer */ +int radeon_fbdev_init(struct radeon_device *rdev); +void radeon_fbdev_fini(struct radeon_device *rdev); +void radeon_fbdev_set_suspend(struct radeon_device *rdev, int state); +int radeon_fbdev_total_size(struct radeon_device *rdev); +bool radeon_fbdev_robj_is_fb(struct radeon_device *rdev, struct radeon_bo *robj); + +void radeon_fb_output_poll_changed(struct radeon_device *rdev); + +void radeon_crtc_handle_flip(struct radeon_device *rdev, int crtc_id); + +int radeon_align_pitch(struct radeon_device *rdev, int width, int bpp, bool tiled); +#endif diff --git a/sys/dev/drm2/radeon/radeon_object.c b/sys/dev/drm2/radeon/radeon_object.c new file mode 100644 index 00000000000..30d3d81c82a --- /dev/null +++ b/sys/dev/drm2/radeon/radeon_object.c @@ -0,0 +1,651 @@ +/* + * Copyright 2009 Jerome Glisse. + * All Rights Reserved. + * + * 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, sub license, 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 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 NON-INFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS 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. + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + */ +/* + * Authors: + * Jerome Glisse + * Thomas Hellstrom + * Dave Airlie + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include "radeon.h" +#ifdef DUMBBELL_WIP +#include "radeon_trace.h" +#endif /* DUMBBELL_WIP */ + + +static void radeon_bo_clear_surface_reg(struct radeon_bo *bo); + +/* + * To exclude mutual BO access we rely on bo_reserve exclusion, as all + * function are calling it. + */ + +static void radeon_bo_clear_va(struct radeon_bo *bo) +{ + struct radeon_bo_va *bo_va, *tmp; + + list_for_each_entry_safe(bo_va, tmp, &bo->va, bo_list) { + /* remove from all vm address space */ + radeon_vm_bo_rmv(bo->rdev, bo_va); + } +} + +static void radeon_ttm_bo_destroy(struct ttm_buffer_object *tbo) +{ + struct radeon_bo *bo; + + bo = container_of(tbo, struct radeon_bo, tbo); + sx_xlock(&bo->rdev->gem.mutex); + list_del_init(&bo->list); + sx_xunlock(&bo->rdev->gem.mutex); + radeon_bo_clear_surface_reg(bo); + radeon_bo_clear_va(bo); + drm_gem_object_release(&bo->gem_base); + free(bo, DRM_MEM_DRIVER); +} + +bool radeon_ttm_bo_is_radeon_bo(struct ttm_buffer_object *bo) +{ + if (bo->destroy == &radeon_ttm_bo_destroy) + return true; + return false; +} + +void radeon_ttm_placement_from_domain(struct radeon_bo *rbo, u32 domain) +{ + u32 c = 0; + + rbo->placement.fpfn = 0; + rbo->placement.lpfn = 0; + rbo->placement.placement = rbo->placements; + rbo->placement.busy_placement = rbo->placements; + if (domain & RADEON_GEM_DOMAIN_VRAM) + rbo->placements[c++] = TTM_PL_FLAG_WC | TTM_PL_FLAG_UNCACHED | + TTM_PL_FLAG_VRAM; + if (domain & RADEON_GEM_DOMAIN_GTT) { + if (rbo->rdev->flags & RADEON_IS_AGP) { + rbo->placements[c++] = TTM_PL_FLAG_WC | TTM_PL_FLAG_TT; + } else { + rbo->placements[c++] = TTM_PL_FLAG_CACHED | TTM_PL_FLAG_TT; + } + } + if (domain & RADEON_GEM_DOMAIN_CPU) { + if (rbo->rdev->flags & RADEON_IS_AGP) { + rbo->placements[c++] = TTM_PL_FLAG_WC | TTM_PL_FLAG_SYSTEM; + } else { + rbo->placements[c++] = TTM_PL_FLAG_CACHED | TTM_PL_FLAG_SYSTEM; + } + } + if (!c) + rbo->placements[c++] = TTM_PL_MASK_CACHING | TTM_PL_FLAG_SYSTEM; + rbo->placement.num_placement = c; + rbo->placement.num_busy_placement = c; +} + +int radeon_bo_create(struct radeon_device *rdev, + unsigned long size, int byte_align, bool kernel, u32 domain, + struct sg_table *sg, struct radeon_bo **bo_ptr) +{ + struct radeon_bo *bo; + enum ttm_bo_type type; + unsigned long page_align = roundup2(byte_align, PAGE_SIZE) >> PAGE_SHIFT; + size_t acc_size; + int r; + + size = roundup2(size, PAGE_SIZE); + + if (kernel) { + type = ttm_bo_type_kernel; + } else if (sg) { + type = ttm_bo_type_sg; + } else { + type = ttm_bo_type_device; + } + *bo_ptr = NULL; + + acc_size = ttm_bo_dma_acc_size(&rdev->mman.bdev, size, + sizeof(struct radeon_bo)); + + bo = malloc(sizeof(struct radeon_bo), + DRM_MEM_DRIVER, M_ZERO | M_WAITOK); + if (bo == NULL) + return -ENOMEM; + r = drm_gem_object_init(rdev->ddev, &bo->gem_base, size); + if (unlikely(r)) { + free(bo, DRM_MEM_DRIVER); + return r; + } + bo->rdev = rdev; + bo->gem_base.driver_private = NULL; + bo->surface_reg = -1; + INIT_LIST_HEAD(&bo->list); + INIT_LIST_HEAD(&bo->va); + radeon_ttm_placement_from_domain(bo, domain); + /* Kernel allocation are uninterruptible */ + sx_slock(&rdev->pm.mclk_lock); + r = ttm_bo_init(&rdev->mman.bdev, &bo->tbo, size, type, + &bo->placement, page_align, !kernel, NULL, + acc_size, sg, &radeon_ttm_bo_destroy); + sx_sunlock(&rdev->pm.mclk_lock); + if (unlikely(r != 0)) { + return r; + } + *bo_ptr = bo; + +#ifdef DUMBBELL_WIP + trace_radeon_bo_create(bo); +#endif /* DUMBBELL_WIP */ + + return 0; +} + +int radeon_bo_kmap(struct radeon_bo *bo, void **ptr) +{ + bool is_iomem; + int r; + + if (bo->kptr) { + if (ptr) { + *ptr = bo->kptr; + } + return 0; + } + r = ttm_bo_kmap(&bo->tbo, 0, bo->tbo.num_pages, &bo->kmap); + if (r) { + return r; + } + bo->kptr = ttm_kmap_obj_virtual(&bo->kmap, &is_iomem); + if (ptr) { + *ptr = bo->kptr; + } + radeon_bo_check_tiling(bo, 0, 0); + return 0; +} + +void radeon_bo_kunmap(struct radeon_bo *bo) +{ + if (bo->kptr == NULL) + return; + bo->kptr = NULL; + radeon_bo_check_tiling(bo, 0, 0); + ttm_bo_kunmap(&bo->kmap); +} + +void radeon_bo_unref(struct radeon_bo **bo) +{ + struct ttm_buffer_object *tbo; + struct radeon_device *rdev; + + if ((*bo) == NULL) + return; + rdev = (*bo)->rdev; + tbo = &((*bo)->tbo); + sx_slock(&rdev->pm.mclk_lock); + ttm_bo_unref(&tbo); + sx_sunlock(&rdev->pm.mclk_lock); + if (tbo == NULL) + *bo = NULL; +} + +int radeon_bo_pin_restricted(struct radeon_bo *bo, u32 domain, u64 max_offset, + u64 *gpu_addr) +{ + int r, i; + + if (bo->pin_count) { + bo->pin_count++; + if (gpu_addr) + *gpu_addr = radeon_bo_gpu_offset(bo); + + if (max_offset != 0) { + u64 domain_start; + + if (domain == RADEON_GEM_DOMAIN_VRAM) + domain_start = bo->rdev->mc.vram_start; + else + domain_start = bo->rdev->mc.gtt_start; + if (max_offset < (radeon_bo_gpu_offset(bo) - domain_start)) { + DRM_ERROR("radeon_bo_pin_restricted: " + "max_offset(%ju) < " + "(radeon_bo_gpu_offset(%ju) - " + "domain_start(%ju)", + (uintmax_t)max_offset, (uintmax_t)radeon_bo_gpu_offset(bo), + (uintmax_t)domain_start); + } + } + + return 0; + } + radeon_ttm_placement_from_domain(bo, domain); + if (domain == RADEON_GEM_DOMAIN_VRAM) { + /* force to pin into visible video ram */ + bo->placement.lpfn = bo->rdev->mc.visible_vram_size >> PAGE_SHIFT; + } + if (max_offset) { + u64 lpfn = max_offset >> PAGE_SHIFT; + + if (!bo->placement.lpfn) + bo->placement.lpfn = bo->rdev->mc.gtt_size >> PAGE_SHIFT; + + if (lpfn < bo->placement.lpfn) + bo->placement.lpfn = lpfn; + } + for (i = 0; i < bo->placement.num_placement; i++) + bo->placements[i] |= TTM_PL_FLAG_NO_EVICT; + r = ttm_bo_validate(&bo->tbo, &bo->placement, false, false); + if (likely(r == 0)) { + bo->pin_count = 1; + if (gpu_addr != NULL) + *gpu_addr = radeon_bo_gpu_offset(bo); + } + if (unlikely(r != 0)) + dev_err(bo->rdev->dev, "%p pin failed\n", bo); + return r; +} + +int radeon_bo_pin(struct radeon_bo *bo, u32 domain, u64 *gpu_addr) +{ + return radeon_bo_pin_restricted(bo, domain, 0, gpu_addr); +} + +int radeon_bo_unpin(struct radeon_bo *bo) +{ + int r, i; + + if (!bo->pin_count) { + dev_warn(bo->rdev->dev, "%p unpin not necessary\n", bo); + return 0; + } + bo->pin_count--; + if (bo->pin_count) + return 0; + for (i = 0; i < bo->placement.num_placement; i++) + bo->placements[i] &= ~TTM_PL_FLAG_NO_EVICT; + r = ttm_bo_validate(&bo->tbo, &bo->placement, false, false); + if (unlikely(r != 0)) + dev_err(bo->rdev->dev, "%p validate failed for unpin\n", bo); + return r; +} + +int radeon_bo_evict_vram(struct radeon_device *rdev) +{ + /* late 2.6.33 fix IGP hibernate - we need pm ops to do this correct */ + if (0 && (rdev->flags & RADEON_IS_IGP)) { + if (rdev->mc.igp_sideport_enabled == false) + /* Useless to evict on IGP chips */ + return 0; + } + return ttm_bo_evict_mm(&rdev->mman.bdev, TTM_PL_VRAM); +} + +void radeon_bo_force_delete(struct radeon_device *rdev) +{ + struct radeon_bo *bo, *n; + + if (list_empty(&rdev->gem.objects)) { + return; + } + dev_err(rdev->dev, "Userspace still has active objects !\n"); + list_for_each_entry_safe(bo, n, &rdev->gem.objects, list) { + dev_err(rdev->dev, "%p %p %lu %lu force free\n", + &bo->gem_base, bo, (unsigned long)bo->gem_base.size, + *((unsigned long *)&bo->gem_base.refcount)); + sx_xlock(&bo->rdev->gem.mutex); + list_del_init(&bo->list); + sx_xunlock(&bo->rdev->gem.mutex); + /* this should unref the ttm bo */ + drm_gem_object_unreference(&bo->gem_base); + } +} + +int radeon_bo_init(struct radeon_device *rdev) +{ + /* Add an MTRR for the VRAM */ + rdev->mc.vram_mtrr = drm_mtrr_add(rdev->mc.aper_base, rdev->mc.aper_size, + DRM_MTRR_WC); + DRM_INFO("Detected VRAM RAM=%juM, BAR=%juM\n", + (uintmax_t)rdev->mc.mc_vram_size >> 20, + (uintmax_t)rdev->mc.aper_size >> 20); + DRM_INFO("RAM width %dbits %cDR\n", + rdev->mc.vram_width, rdev->mc.vram_is_ddr ? 'D' : 'S'); + return radeon_ttm_init(rdev); +} + +void radeon_bo_fini(struct radeon_device *rdev) +{ + radeon_ttm_fini(rdev); +} + +void radeon_bo_list_add_object(struct radeon_bo_list *lobj, + struct list_head *head) +{ + if (lobj->wdomain) { + list_add(&lobj->tv.head, head); + } else { + list_add_tail(&lobj->tv.head, head); + } +} + +int radeon_bo_list_validate(struct list_head *head) +{ + struct radeon_bo_list *lobj; + struct radeon_bo *bo; + u32 domain; + int r; + + r = ttm_eu_reserve_buffers(head); + if (unlikely(r != 0)) { + return r; + } + list_for_each_entry(lobj, head, tv.head) { + bo = lobj->bo; + if (!bo->pin_count) { + domain = lobj->wdomain ? lobj->wdomain : lobj->rdomain; + + retry: + radeon_ttm_placement_from_domain(bo, domain); + r = ttm_bo_validate(&bo->tbo, &bo->placement, + true, false); + if (unlikely(r)) { + if (r != -ERESTARTSYS && domain == RADEON_GEM_DOMAIN_VRAM) { + domain |= RADEON_GEM_DOMAIN_GTT; + goto retry; + } + return r; + } + } + lobj->gpu_offset = radeon_bo_gpu_offset(bo); + lobj->tiling_flags = bo->tiling_flags; + } + return 0; +} + +#ifdef DUMBBELL_WIP +int radeon_bo_fbdev_mmap(struct radeon_bo *bo, + struct vm_area_struct *vma) +{ + return ttm_fbdev_mmap(vma, &bo->tbo); +} +#endif /* DUMBBELL_WIP */ + +int radeon_bo_get_surface_reg(struct radeon_bo *bo) +{ + struct radeon_device *rdev = bo->rdev; + struct radeon_surface_reg *reg; + struct radeon_bo *old_object; + int steal; + int i; + + KASSERT(radeon_bo_is_reserved(bo), + ("radeon_bo_get_surface_reg: radeon_bo is not reserved")); + + if (!bo->tiling_flags) + return 0; + + if (bo->surface_reg >= 0) { + reg = &rdev->surface_regs[bo->surface_reg]; + i = bo->surface_reg; + goto out; + } + + steal = -1; + for (i = 0; i < RADEON_GEM_MAX_SURFACES; i++) { + + reg = &rdev->surface_regs[i]; + if (!reg->bo) + break; + + old_object = reg->bo; + if (old_object->pin_count == 0) + steal = i; + } + + /* if we are all out */ + if (i == RADEON_GEM_MAX_SURFACES) { + if (steal == -1) + return -ENOMEM; + /* find someone with a surface reg and nuke their BO */ + reg = &rdev->surface_regs[steal]; + old_object = reg->bo; + /* blow away the mapping */ + DRM_DEBUG("stealing surface reg %d from %p\n", steal, old_object); + ttm_bo_unmap_virtual(&old_object->tbo); + old_object->surface_reg = -1; + i = steal; + } + + bo->surface_reg = i; + reg->bo = bo; + +out: + radeon_set_surface_reg(rdev, i, bo->tiling_flags, bo->pitch, + bo->tbo.mem.start << PAGE_SHIFT, + bo->tbo.num_pages << PAGE_SHIFT); + return 0; +} + +static void radeon_bo_clear_surface_reg(struct radeon_bo *bo) +{ + struct radeon_device *rdev = bo->rdev; + struct radeon_surface_reg *reg; + + if (bo->surface_reg == -1) + return; + + reg = &rdev->surface_regs[bo->surface_reg]; + radeon_clear_surface_reg(rdev, bo->surface_reg); + + reg->bo = NULL; + bo->surface_reg = -1; +} + +int radeon_bo_set_tiling_flags(struct radeon_bo *bo, + uint32_t tiling_flags, uint32_t pitch) +{ + struct radeon_device *rdev = bo->rdev; + int r; + + if (rdev->family >= CHIP_CEDAR) { + unsigned bankw, bankh, mtaspect, tilesplit, stilesplit; + + bankw = (tiling_flags >> RADEON_TILING_EG_BANKW_SHIFT) & RADEON_TILING_EG_BANKW_MASK; + bankh = (tiling_flags >> RADEON_TILING_EG_BANKH_SHIFT) & RADEON_TILING_EG_BANKH_MASK; + mtaspect = (tiling_flags >> RADEON_TILING_EG_MACRO_TILE_ASPECT_SHIFT) & RADEON_TILING_EG_MACRO_TILE_ASPECT_MASK; + tilesplit = (tiling_flags >> RADEON_TILING_EG_TILE_SPLIT_SHIFT) & RADEON_TILING_EG_TILE_SPLIT_MASK; + stilesplit = (tiling_flags >> RADEON_TILING_EG_STENCIL_TILE_SPLIT_SHIFT) & RADEON_TILING_EG_STENCIL_TILE_SPLIT_MASK; + switch (bankw) { + case 0: + case 1: + case 2: + case 4: + case 8: + break; + default: + return -EINVAL; + } + switch (bankh) { + case 0: + case 1: + case 2: + case 4: + case 8: + break; + default: + return -EINVAL; + } + switch (mtaspect) { + case 0: + case 1: + case 2: + case 4: + case 8: + break; + default: + return -EINVAL; + } + if (tilesplit > 6) { + return -EINVAL; + } + if (stilesplit > 6) { + return -EINVAL; + } + } + r = radeon_bo_reserve(bo, false); + if (unlikely(r != 0)) + return r; + bo->tiling_flags = tiling_flags; + bo->pitch = pitch; + radeon_bo_unreserve(bo); + return 0; +} + +void radeon_bo_get_tiling_flags(struct radeon_bo *bo, + uint32_t *tiling_flags, + uint32_t *pitch) +{ + KASSERT(radeon_bo_is_reserved(bo), + ("radeon_bo_get_tiling_flags: radeon_bo is not reserved")); + if (tiling_flags) + *tiling_flags = bo->tiling_flags; + if (pitch) + *pitch = bo->pitch; +} + +int radeon_bo_check_tiling(struct radeon_bo *bo, bool has_moved, + bool force_drop) +{ + KASSERT((radeon_bo_is_reserved(bo) || force_drop), + ("radeon_bo_check_tiling: radeon_bo is not reserved && !force_drop")); + + if (!(bo->tiling_flags & RADEON_TILING_SURFACE)) + return 0; + + if (force_drop) { + radeon_bo_clear_surface_reg(bo); + return 0; + } + + if (bo->tbo.mem.mem_type != TTM_PL_VRAM) { + if (!has_moved) + return 0; + + if (bo->surface_reg >= 0) + radeon_bo_clear_surface_reg(bo); + return 0; + } + + if ((bo->surface_reg >= 0) && !has_moved) + return 0; + + return radeon_bo_get_surface_reg(bo); +} + +void radeon_bo_move_notify(struct ttm_buffer_object *bo, + struct ttm_mem_reg *mem) +{ + struct radeon_bo *rbo; + if (!radeon_ttm_bo_is_radeon_bo(bo)) + return; + rbo = container_of(bo, struct radeon_bo, tbo); + radeon_bo_check_tiling(rbo, 0, 1); + radeon_vm_bo_invalidate(rbo->rdev, rbo); +} + +int radeon_bo_fault_reserve_notify(struct ttm_buffer_object *bo) +{ + struct radeon_device *rdev; + struct radeon_bo *rbo; + unsigned long offset, size; + int r; + + if (!radeon_ttm_bo_is_radeon_bo(bo)) + return 0; + rbo = container_of(bo, struct radeon_bo, tbo); + radeon_bo_check_tiling(rbo, 0, 0); + rdev = rbo->rdev; + if (bo->mem.mem_type == TTM_PL_VRAM) { + size = bo->mem.num_pages << PAGE_SHIFT; + offset = bo->mem.start << PAGE_SHIFT; + if ((offset + size) > rdev->mc.visible_vram_size) { + /* hurrah the memory is not visible ! */ + radeon_ttm_placement_from_domain(rbo, RADEON_GEM_DOMAIN_VRAM); + rbo->placement.lpfn = rdev->mc.visible_vram_size >> PAGE_SHIFT; + r = ttm_bo_validate(bo, &rbo->placement, false, false); + if (unlikely(r != 0)) + return r; + offset = bo->mem.start << PAGE_SHIFT; + /* this should not happen */ + if ((offset + size) > rdev->mc.visible_vram_size) + return -EINVAL; + } + } + return 0; +} + +int radeon_bo_wait(struct radeon_bo *bo, u32 *mem_type, bool no_wait) +{ + int r; + + r = ttm_bo_reserve(&bo->tbo, true, no_wait, false, 0); + if (unlikely(r != 0)) + return r; + mtx_lock(&bo->tbo.bdev->fence_lock); + if (mem_type) + *mem_type = bo->tbo.mem.mem_type; + if (bo->tbo.sync_obj) + r = ttm_bo_wait(&bo->tbo, true, true, no_wait); + mtx_unlock(&bo->tbo.bdev->fence_lock); + ttm_bo_unreserve(&bo->tbo); + return r; +} + + +/** + * radeon_bo_reserve - reserve bo + * @bo: bo structure + * @no_intr: don't return -ERESTARTSYS on pending signal + * + * Returns: + * -ERESTARTSYS: A wait for the buffer to become unreserved was interrupted by + * a signal. Release all buffer reservations and return to user-space. + */ +int radeon_bo_reserve(struct radeon_bo *bo, bool no_intr) +{ + int r; + + r = ttm_bo_reserve(&bo->tbo, !no_intr, false, false, 0); + if (unlikely(r != 0)) { + if (r != -ERESTARTSYS) + dev_err(bo->rdev->dev, "%p reserve failed\n", bo); + return r; + } + return 0; +} diff --git a/sys/dev/drm2/radeon/radeon_object.h b/sys/dev/drm2/radeon/radeon_object.h new file mode 100644 index 00000000000..efb7149b867 --- /dev/null +++ b/sys/dev/drm2/radeon/radeon_object.h @@ -0,0 +1,193 @@ +/* + * Copyright 2008 Advanced Micro Devices, Inc. + * Copyright 2008 Red Hat Inc. + * Copyright 2009 Jerome Glisse. + * + * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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. + * + * Authors: Dave Airlie + * Alex Deucher + * Jerome Glisse + */ + +#include +__FBSDID("$FreeBSD$"); + +#ifndef __RADEON_OBJECT_H__ +#define __RADEON_OBJECT_H__ + +#include +#include "radeon.h" + +/* + * Undefine max_offset (defined in vm/vm_map.h), because it conflicts + * with an argument of the function radeon_bo_pin_restricted(). + */ +#undef max_offset + +/** + * radeon_mem_type_to_domain - return domain corresponding to mem_type + * @mem_type: ttm memory type + * + * Returns corresponding domain of the ttm mem_type + */ +static inline unsigned radeon_mem_type_to_domain(u32 mem_type) +{ + switch (mem_type) { + case TTM_PL_VRAM: + return RADEON_GEM_DOMAIN_VRAM; + case TTM_PL_TT: + return RADEON_GEM_DOMAIN_GTT; + case TTM_PL_SYSTEM: + return RADEON_GEM_DOMAIN_CPU; + default: + break; + } + return 0; +} + +int radeon_bo_reserve(struct radeon_bo *bo, bool no_intr); + +static inline void radeon_bo_unreserve(struct radeon_bo *bo) +{ + ttm_bo_unreserve(&bo->tbo); +} + +/** + * radeon_bo_gpu_offset - return GPU offset of bo + * @bo: radeon object for which we query the offset + * + * Returns current GPU offset of the object. + * + * Note: object should either be pinned or reserved when calling this + * function, it might be useful to add check for this for debugging. + */ +static inline u64 radeon_bo_gpu_offset(struct radeon_bo *bo) +{ + return bo->tbo.offset; +} + +static inline unsigned long radeon_bo_size(struct radeon_bo *bo) +{ + return bo->tbo.num_pages << PAGE_SHIFT; +} + +static inline bool radeon_bo_is_reserved(struct radeon_bo *bo) +{ + return ttm_bo_is_reserved(&bo->tbo); +} + +static inline unsigned radeon_bo_ngpu_pages(struct radeon_bo *bo) +{ + return (bo->tbo.num_pages << PAGE_SHIFT) / RADEON_GPU_PAGE_SIZE; +} + +static inline unsigned radeon_bo_gpu_page_alignment(struct radeon_bo *bo) +{ + return (bo->tbo.mem.page_alignment << PAGE_SHIFT) / RADEON_GPU_PAGE_SIZE; +} + +/** + * radeon_bo_mmap_offset - return mmap offset of bo + * @bo: radeon object for which we query the offset + * + * Returns mmap offset of the object. + * + * Note: addr_space_offset is constant after ttm bo init thus isn't protected + * by any lock. + */ +static inline u64 radeon_bo_mmap_offset(struct radeon_bo *bo) +{ + return bo->tbo.addr_space_offset; +} + +extern int radeon_bo_wait(struct radeon_bo *bo, u32 *mem_type, + bool no_wait); + +extern int radeon_bo_create(struct radeon_device *rdev, + unsigned long size, int byte_align, + bool kernel, u32 domain, + struct sg_table *sg, + struct radeon_bo **bo_ptr); +extern int radeon_bo_kmap(struct radeon_bo *bo, void **ptr); +extern void radeon_bo_kunmap(struct radeon_bo *bo); +extern void radeon_bo_unref(struct radeon_bo **bo); +extern int radeon_bo_pin(struct radeon_bo *bo, u32 domain, u64 *gpu_addr); +extern int radeon_bo_pin_restricted(struct radeon_bo *bo, u32 domain, + u64 max_offset, u64 *gpu_addr); +extern int radeon_bo_unpin(struct radeon_bo *bo); +extern int radeon_bo_evict_vram(struct radeon_device *rdev); +extern void radeon_bo_force_delete(struct radeon_device *rdev); +extern int radeon_bo_init(struct radeon_device *rdev); +extern void radeon_bo_fini(struct radeon_device *rdev); +extern void radeon_bo_list_add_object(struct radeon_bo_list *lobj, + struct list_head *head); +extern int radeon_bo_list_validate(struct list_head *head); +#ifdef DUMBBELL_WIP +extern int radeon_bo_fbdev_mmap(struct radeon_bo *bo, + struct vm_area_struct *vma); +#endif /* DUMBBELL_WIP */ +extern int radeon_bo_set_tiling_flags(struct radeon_bo *bo, + u32 tiling_flags, u32 pitch); +extern void radeon_bo_get_tiling_flags(struct radeon_bo *bo, + u32 *tiling_flags, u32 *pitch); +extern int radeon_bo_check_tiling(struct radeon_bo *bo, bool has_moved, + bool force_drop); +extern void radeon_bo_move_notify(struct ttm_buffer_object *bo, + struct ttm_mem_reg *mem); +extern int radeon_bo_fault_reserve_notify(struct ttm_buffer_object *bo); +extern int radeon_bo_get_surface_reg(struct radeon_bo *bo); + +/* + * sub allocation + */ + +static inline uint64_t radeon_sa_bo_gpu_addr(struct radeon_sa_bo *sa_bo) +{ + return sa_bo->manager->gpu_addr + sa_bo->soffset; +} + +static inline void * radeon_sa_bo_cpu_addr(struct radeon_sa_bo *sa_bo) +{ + return (char *)sa_bo->manager->cpu_ptr + sa_bo->soffset; +} + +extern int radeon_sa_bo_manager_init(struct radeon_device *rdev, + struct radeon_sa_manager *sa_manager, + unsigned size, u32 domain); +extern void radeon_sa_bo_manager_fini(struct radeon_device *rdev, + struct radeon_sa_manager *sa_manager); +extern int radeon_sa_bo_manager_start(struct radeon_device *rdev, + struct radeon_sa_manager *sa_manager); +extern int radeon_sa_bo_manager_suspend(struct radeon_device *rdev, + struct radeon_sa_manager *sa_manager); +extern int radeon_sa_bo_new(struct radeon_device *rdev, + struct radeon_sa_manager *sa_manager, + struct radeon_sa_bo **sa_bo, + unsigned size, unsigned align, bool block); +extern void radeon_sa_bo_free(struct radeon_device *rdev, + struct radeon_sa_bo **sa_bo, + struct radeon_fence *fence); +#if defined(CONFIG_DEBUG_FS) +extern void radeon_sa_bo_dump_debug_info(struct radeon_sa_manager *sa_manager, + struct seq_file *m); +#endif + + +#endif diff --git a/sys/dev/drm2/radeon/radeon_pm.c b/sys/dev/drm2/radeon/radeon_pm.c new file mode 100644 index 00000000000..47042dbd8e2 --- /dev/null +++ b/sys/dev/drm2/radeon/radeon_pm.c @@ -0,0 +1,918 @@ +/* + * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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. + * + * Authors: Rafał Miłecki + * Alex Deucher + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include "radeon.h" +#include "avivod.h" +#include "atom.h" + +#define RADEON_IDLE_LOOP_MS 100 +#define RADEON_RECLOCK_DELAY_MS 200 +#define RADEON_WAIT_VBLANK_TIMEOUT 200 + +static const char *radeon_pm_state_type_name[5] = { + "", + "Powersave", + "Battery", + "Balanced", + "Performance", +}; + +#ifdef DUMBBELL_WIP +static void radeon_dynpm_idle_work_handler(struct work_struct *work); +#endif /* DUMBBELL_WIP */ +static int radeon_debugfs_pm_init(struct radeon_device *rdev); +static bool radeon_pm_in_vbl(struct radeon_device *rdev); +static bool radeon_pm_debug_check_in_vbl(struct radeon_device *rdev, bool finish); +static void radeon_pm_update_profile(struct radeon_device *rdev); +static void radeon_pm_set_clocks(struct radeon_device *rdev); + +int radeon_pm_get_type_index(struct radeon_device *rdev, + enum radeon_pm_state_type ps_type, + int instance) +{ + int i; + int found_instance = -1; + + for (i = 0; i < rdev->pm.num_power_states; i++) { + if (rdev->pm.power_state[i].type == ps_type) { + found_instance++; + if (found_instance == instance) + return i; + } + } + /* return default if no match */ + return rdev->pm.default_power_state_index; +} + +void radeon_pm_acpi_event_handler(struct radeon_device *rdev) +{ + if (rdev->pm.pm_method == PM_METHOD_PROFILE) { + if (rdev->pm.profile == PM_PROFILE_AUTO) { + sx_xlock(&rdev->pm.mutex); + radeon_pm_update_profile(rdev); + radeon_pm_set_clocks(rdev); + sx_xunlock(&rdev->pm.mutex); + } + } +} + +static void radeon_pm_update_profile(struct radeon_device *rdev) +{ + switch (rdev->pm.profile) { + case PM_PROFILE_DEFAULT: + rdev->pm.profile_index = PM_PROFILE_DEFAULT_IDX; + break; + case PM_PROFILE_AUTO: +#ifdef DUMBBELL_WIP + if (power_supply_is_system_supplied() > 0) { + if (rdev->pm.active_crtc_count > 1) + rdev->pm.profile_index = PM_PROFILE_HIGH_MH_IDX; + else + rdev->pm.profile_index = PM_PROFILE_HIGH_SH_IDX; + } else { + if (rdev->pm.active_crtc_count > 1) + rdev->pm.profile_index = PM_PROFILE_MID_MH_IDX; + else + rdev->pm.profile_index = PM_PROFILE_MID_SH_IDX; + } +#endif /* DUMBBELL_WIP */ + break; + case PM_PROFILE_LOW: + if (rdev->pm.active_crtc_count > 1) + rdev->pm.profile_index = PM_PROFILE_LOW_MH_IDX; + else + rdev->pm.profile_index = PM_PROFILE_LOW_SH_IDX; + break; + case PM_PROFILE_MID: + if (rdev->pm.active_crtc_count > 1) + rdev->pm.profile_index = PM_PROFILE_MID_MH_IDX; + else + rdev->pm.profile_index = PM_PROFILE_MID_SH_IDX; + break; + case PM_PROFILE_HIGH: + if (rdev->pm.active_crtc_count > 1) + rdev->pm.profile_index = PM_PROFILE_HIGH_MH_IDX; + else + rdev->pm.profile_index = PM_PROFILE_HIGH_SH_IDX; + break; + } + + if (rdev->pm.active_crtc_count == 0) { + rdev->pm.requested_power_state_index = + rdev->pm.profiles[rdev->pm.profile_index].dpms_off_ps_idx; + rdev->pm.requested_clock_mode_index = + rdev->pm.profiles[rdev->pm.profile_index].dpms_off_cm_idx; + } else { + rdev->pm.requested_power_state_index = + rdev->pm.profiles[rdev->pm.profile_index].dpms_on_ps_idx; + rdev->pm.requested_clock_mode_index = + rdev->pm.profiles[rdev->pm.profile_index].dpms_on_cm_idx; + } +} + +static void radeon_unmap_vram_bos(struct radeon_device *rdev) +{ + struct radeon_bo *bo, *n; + + if (list_empty(&rdev->gem.objects)) + return; + + list_for_each_entry_safe(bo, n, &rdev->gem.objects, list) { + if (bo->tbo.mem.mem_type == TTM_PL_VRAM) + ttm_bo_unmap_virtual(&bo->tbo); + } +} + +static void radeon_sync_with_vblank(struct radeon_device *rdev) +{ + if (rdev->pm.active_crtcs) { + rdev->pm.vblank_sync = false; +#ifdef DUMBBELL_WIP + wait_event_timeout( + rdev->irq.vblank_queue, rdev->pm.vblank_sync, + msecs_to_jiffies(RADEON_WAIT_VBLANK_TIMEOUT)); +#endif /* DUMBBELL_WIP */ + } +} + +static void radeon_set_power_state(struct radeon_device *rdev) +{ + u32 sclk, mclk; + bool misc_after = false; + + if ((rdev->pm.requested_clock_mode_index == rdev->pm.current_clock_mode_index) && + (rdev->pm.requested_power_state_index == rdev->pm.current_power_state_index)) + return; + + if (radeon_gui_idle(rdev)) { + sclk = rdev->pm.power_state[rdev->pm.requested_power_state_index]. + clock_info[rdev->pm.requested_clock_mode_index].sclk; + if (sclk > rdev->pm.default_sclk) + sclk = rdev->pm.default_sclk; + + /* starting with BTC, there is one state that is used for both + * MH and SH. Difference is that we always use the high clock index for + * mclk. + */ + if ((rdev->pm.pm_method == PM_METHOD_PROFILE) && + (rdev->family >= CHIP_BARTS) && + rdev->pm.active_crtc_count && + ((rdev->pm.profile_index == PM_PROFILE_MID_MH_IDX) || + (rdev->pm.profile_index == PM_PROFILE_LOW_MH_IDX))) + mclk = rdev->pm.power_state[rdev->pm.requested_power_state_index]. + clock_info[rdev->pm.profiles[PM_PROFILE_HIGH_MH_IDX].dpms_on_cm_idx].mclk; + else + mclk = rdev->pm.power_state[rdev->pm.requested_power_state_index]. + clock_info[rdev->pm.requested_clock_mode_index].mclk; + + if (mclk > rdev->pm.default_mclk) + mclk = rdev->pm.default_mclk; + + /* upvolt before raising clocks, downvolt after lowering clocks */ + if (sclk < rdev->pm.current_sclk) + misc_after = true; + + radeon_sync_with_vblank(rdev); + + if (rdev->pm.pm_method == PM_METHOD_DYNPM) { + if (!radeon_pm_in_vbl(rdev)) + return; + } + + radeon_pm_prepare(rdev); + + if (!misc_after) + /* voltage, pcie lanes, etc.*/ + radeon_pm_misc(rdev); + + /* set engine clock */ + if (sclk != rdev->pm.current_sclk) { + radeon_pm_debug_check_in_vbl(rdev, false); + radeon_set_engine_clock(rdev, sclk); + radeon_pm_debug_check_in_vbl(rdev, true); + rdev->pm.current_sclk = sclk; + DRM_DEBUG_DRIVER("Setting: e: %d\n", sclk); + } + + /* set memory clock */ + if (rdev->asic->pm.set_memory_clock && (mclk != rdev->pm.current_mclk)) { + radeon_pm_debug_check_in_vbl(rdev, false); + radeon_set_memory_clock(rdev, mclk); + radeon_pm_debug_check_in_vbl(rdev, true); + rdev->pm.current_mclk = mclk; + DRM_DEBUG_DRIVER("Setting: m: %d\n", mclk); + } + + if (misc_after) + /* voltage, pcie lanes, etc.*/ + radeon_pm_misc(rdev); + + radeon_pm_finish(rdev); + + rdev->pm.current_power_state_index = rdev->pm.requested_power_state_index; + rdev->pm.current_clock_mode_index = rdev->pm.requested_clock_mode_index; + } else + DRM_DEBUG_DRIVER("pm: GUI not idle!!!\n"); +} + +static void radeon_pm_set_clocks(struct radeon_device *rdev) +{ + int i, r; + + /* no need to take locks, etc. if nothing's going to change */ + if ((rdev->pm.requested_clock_mode_index == rdev->pm.current_clock_mode_index) && + (rdev->pm.requested_power_state_index == rdev->pm.current_power_state_index)) + return; + + DRM_LOCK(rdev->ddev); + sx_xlock(&rdev->pm.mclk_lock); + sx_xlock(&rdev->ring_lock); + + /* wait for the rings to drain */ + for (i = 0; i < RADEON_NUM_RINGS; i++) { + struct radeon_ring *ring = &rdev->ring[i]; + if (!ring->ready) { + continue; + } + r = radeon_fence_wait_empty_locked(rdev, i); + if (r) { + /* needs a GPU reset dont reset here */ + sx_xunlock(&rdev->ring_lock); + sx_xunlock(&rdev->pm.mclk_lock); + DRM_UNLOCK(rdev->ddev); + return; + } + } + + radeon_unmap_vram_bos(rdev); + + if (rdev->irq.installed) { + for (i = 0; i < rdev->num_crtc; i++) { + if (rdev->pm.active_crtcs & (1 << i)) { + rdev->pm.req_vblank |= (1 << i); + drm_vblank_get(rdev->ddev, i); + } + } + } + + radeon_set_power_state(rdev); + + if (rdev->irq.installed) { + for (i = 0; i < rdev->num_crtc; i++) { + if (rdev->pm.req_vblank & (1 << i)) { + rdev->pm.req_vblank &= ~(1 << i); + drm_vblank_put(rdev->ddev, i); + } + } + } + + /* update display watermarks based on new power state */ + radeon_update_bandwidth_info(rdev); + if (rdev->pm.active_crtc_count) + radeon_bandwidth_update(rdev); + + rdev->pm.dynpm_planned_action = DYNPM_ACTION_NONE; + + sx_xunlock(&rdev->ring_lock); + sx_xunlock(&rdev->pm.mclk_lock); + DRM_UNLOCK(rdev->ddev); +} + +static void radeon_pm_print_states(struct radeon_device *rdev) +{ + int i, j; + struct radeon_power_state *power_state; + struct radeon_pm_clock_info *clock_info; + + DRM_DEBUG_DRIVER("%d Power State(s)\n", rdev->pm.num_power_states); + for (i = 0; i < rdev->pm.num_power_states; i++) { + power_state = &rdev->pm.power_state[i]; + DRM_DEBUG_DRIVER("State %d: %s\n", i, + radeon_pm_state_type_name[power_state->type]); + if (i == rdev->pm.default_power_state_index) + DRM_DEBUG_DRIVER("\tDefault"); + if ((rdev->flags & RADEON_IS_PCIE) && !(rdev->flags & RADEON_IS_IGP)) + DRM_DEBUG_DRIVER("\t%d PCIE Lanes\n", power_state->pcie_lanes); + if (power_state->flags & RADEON_PM_STATE_SINGLE_DISPLAY_ONLY) + DRM_DEBUG_DRIVER("\tSingle display only\n"); + DRM_DEBUG_DRIVER("\t%d Clock Mode(s)\n", power_state->num_clock_modes); + for (j = 0; j < power_state->num_clock_modes; j++) { + clock_info = &(power_state->clock_info[j]); + if (rdev->flags & RADEON_IS_IGP) + DRM_DEBUG_DRIVER("\t\t%d e: %d\n", + j, + clock_info->sclk * 10); + else + DRM_DEBUG_DRIVER("\t\t%d e: %d\tm: %d\tv: %d\n", + j, + clock_info->sclk * 10, + clock_info->mclk * 10, + clock_info->voltage.voltage); + } + } +} + +#ifdef DUMBBELL_WIP +static ssize_t radeon_get_pm_profile(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct drm_device *ddev = pci_get_drvdata(to_pci_dev(dev)); + struct radeon_device *rdev = ddev->dev_private; + int cp = rdev->pm.profile; + + return snprintf(buf, PAGE_SIZE, "%s\n", + (cp == PM_PROFILE_AUTO) ? "auto" : + (cp == PM_PROFILE_LOW) ? "low" : + (cp == PM_PROFILE_MID) ? "mid" : + (cp == PM_PROFILE_HIGH) ? "high" : "default"); +} + +static ssize_t radeon_set_pm_profile(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + struct drm_device *ddev = pci_get_drvdata(to_pci_dev(dev)); + struct radeon_device *rdev = ddev->dev_private; + + sx_xlock(&rdev->pm.mutex); + if (rdev->pm.pm_method == PM_METHOD_PROFILE) { + if (strncmp("default", buf, strlen("default")) == 0) + rdev->pm.profile = PM_PROFILE_DEFAULT; + else if (strncmp("auto", buf, strlen("auto")) == 0) + rdev->pm.profile = PM_PROFILE_AUTO; + else if (strncmp("low", buf, strlen("low")) == 0) + rdev->pm.profile = PM_PROFILE_LOW; + else if (strncmp("mid", buf, strlen("mid")) == 0) + rdev->pm.profile = PM_PROFILE_MID; + else if (strncmp("high", buf, strlen("high")) == 0) + rdev->pm.profile = PM_PROFILE_HIGH; + else { + count = -EINVAL; + goto fail; + } + radeon_pm_update_profile(rdev); + radeon_pm_set_clocks(rdev); + } else + count = -EINVAL; + +fail: + sx_xunlock(&rdev->pm.mutex); + + return count; +} + +static ssize_t radeon_get_pm_method(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct drm_device *ddev = pci_get_drvdata(to_pci_dev(dev)); + struct radeon_device *rdev = ddev->dev_private; + int pm = rdev->pm.pm_method; + + return snprintf(buf, PAGE_SIZE, "%s\n", + (pm == PM_METHOD_DYNPM) ? "dynpm" : "profile"); +} + +static ssize_t radeon_set_pm_method(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + struct drm_device *ddev = pci_get_drvdata(to_pci_dev(dev)); + struct radeon_device *rdev = ddev->dev_private; + + + if (strncmp("dynpm", buf, strlen("dynpm")) == 0) { + sx_xlock(&rdev->pm.mutex); + rdev->pm.pm_method = PM_METHOD_DYNPM; + rdev->pm.dynpm_state = DYNPM_STATE_PAUSED; + rdev->pm.dynpm_planned_action = DYNPM_ACTION_DEFAULT; + sx_xunlock(&rdev->pm.mutex); + } else if (strncmp("profile", buf, strlen("profile")) == 0) { + sx_xlock(&rdev->pm.mutex); + /* disable dynpm */ + rdev->pm.dynpm_state = DYNPM_STATE_DISABLED; + rdev->pm.dynpm_planned_action = DYNPM_ACTION_NONE; + rdev->pm.pm_method = PM_METHOD_PROFILE; + sx_xunlock(&rdev->pm.mutex); +#ifdef DUMBBELL_WIP + cancel_delayed_work_sync(&rdev->pm.dynpm_idle_work); +#endif /* DUMBBELL_WIP */ + } else { + count = -EINVAL; + goto fail; + } + radeon_pm_compute_clocks(rdev); +fail: + return count; +} + +static DEVICE_ATTR(power_profile, S_IRUGO | S_IWUSR, radeon_get_pm_profile, radeon_set_pm_profile); +static DEVICE_ATTR(power_method, S_IRUGO | S_IWUSR, radeon_get_pm_method, radeon_set_pm_method); + +static ssize_t radeon_hwmon_show_temp(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct drm_device *ddev = pci_get_drvdata(to_pci_dev(dev)); + struct radeon_device *rdev = ddev->dev_private; + int temp; + + switch (rdev->pm.int_thermal_type) { + case THERMAL_TYPE_RV6XX: + temp = rv6xx_get_temp(rdev); + break; + case THERMAL_TYPE_RV770: + temp = rv770_get_temp(rdev); + break; + case THERMAL_TYPE_EVERGREEN: + case THERMAL_TYPE_NI: + temp = evergreen_get_temp(rdev); + break; + case THERMAL_TYPE_SUMO: + temp = sumo_get_temp(rdev); + break; + case THERMAL_TYPE_SI: + temp = si_get_temp(rdev); + break; + default: + temp = 0; + break; + } + + return snprintf(buf, PAGE_SIZE, "%d\n", temp); +} + +static ssize_t radeon_hwmon_show_name(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return sprintf(buf, "radeon\n"); +} + +static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, radeon_hwmon_show_temp, NULL, 0); +static SENSOR_DEVICE_ATTR(name, S_IRUGO, radeon_hwmon_show_name, NULL, 0); + +static struct attribute *hwmon_attributes[] = { + &sensor_dev_attr_temp1_input.dev_attr.attr, + &sensor_dev_attr_name.dev_attr.attr, + NULL +}; + +static const struct attribute_group hwmon_attrgroup = { + .attrs = hwmon_attributes, +}; +#endif /* DUMBBELL_WIP */ + +static int radeon_hwmon_init(struct radeon_device *rdev) +{ + int err = 0; + +#ifdef DUMBBELL_WIP + rdev->pm.int_hwmon_dev = NULL; +#endif /* DUMBBELL_WIP */ + + switch (rdev->pm.int_thermal_type) { + case THERMAL_TYPE_RV6XX: + case THERMAL_TYPE_RV770: + case THERMAL_TYPE_EVERGREEN: + case THERMAL_TYPE_NI: + case THERMAL_TYPE_SUMO: + case THERMAL_TYPE_SI: + /* No support for TN yet */ + if (rdev->family == CHIP_ARUBA) + return err; +#ifdef DUMBBELL_WIP + rdev->pm.int_hwmon_dev = hwmon_device_register(rdev->dev); + if (IS_ERR(rdev->pm.int_hwmon_dev)) { + err = PTR_ERR(rdev->pm.int_hwmon_dev); + dev_err(rdev->dev, + "Unable to register hwmon device: %d\n", err); + break; + } + dev_set_drvdata(rdev->pm.int_hwmon_dev, rdev->ddev); + err = sysfs_create_group(&rdev->pm.int_hwmon_dev->kobj, + &hwmon_attrgroup); + if (err) { + dev_err(rdev->dev, + "Unable to create hwmon sysfs file: %d\n", err); + hwmon_device_unregister(rdev->dev); + } +#endif /* DUMBBELL_WIP */ + break; + default: + break; + } + + return err; +} + +static void radeon_hwmon_fini(struct radeon_device *rdev) +{ +#ifdef DUMBBELL_WIP + if (rdev->pm.int_hwmon_dev) { + sysfs_remove_group(&rdev->pm.int_hwmon_dev->kobj, &hwmon_attrgroup); + hwmon_device_unregister(rdev->pm.int_hwmon_dev); + } +#endif /* DUMBBELL_WIP */ +} + +void radeon_pm_suspend(struct radeon_device *rdev) +{ + sx_xlock(&rdev->pm.mutex); + if (rdev->pm.pm_method == PM_METHOD_DYNPM) { + if (rdev->pm.dynpm_state == DYNPM_STATE_ACTIVE) + rdev->pm.dynpm_state = DYNPM_STATE_SUSPENDED; + } + sx_xunlock(&rdev->pm.mutex); + +#ifdef DUMBBELL_WIP + cancel_delayed_work_sync(&rdev->pm.dynpm_idle_work); +#endif /* DUMBBELL_WIP */ +} + +void radeon_pm_resume(struct radeon_device *rdev) +{ + /* set up the default clocks if the MC ucode is loaded */ + if ((rdev->family >= CHIP_BARTS) && + (rdev->family <= CHIP_CAYMAN) && + rdev->mc_fw) { + if (rdev->pm.default_vddc) + radeon_atom_set_voltage(rdev, rdev->pm.default_vddc, + SET_VOLTAGE_TYPE_ASIC_VDDC); + if (rdev->pm.default_vddci) + radeon_atom_set_voltage(rdev, rdev->pm.default_vddci, + SET_VOLTAGE_TYPE_ASIC_VDDCI); + if (rdev->pm.default_sclk) + radeon_set_engine_clock(rdev, rdev->pm.default_sclk); + if (rdev->pm.default_mclk) + radeon_set_memory_clock(rdev, rdev->pm.default_mclk); + } + /* asic init will reset the default power state */ + sx_xlock(&rdev->pm.mutex); + rdev->pm.current_power_state_index = rdev->pm.default_power_state_index; + rdev->pm.current_clock_mode_index = 0; + rdev->pm.current_sclk = rdev->pm.default_sclk; + rdev->pm.current_mclk = rdev->pm.default_mclk; + rdev->pm.current_vddc = rdev->pm.power_state[rdev->pm.default_power_state_index].clock_info[0].voltage.voltage; + rdev->pm.current_vddci = rdev->pm.power_state[rdev->pm.default_power_state_index].clock_info[0].voltage.vddci; + if (rdev->pm.pm_method == PM_METHOD_DYNPM + && rdev->pm.dynpm_state == DYNPM_STATE_SUSPENDED) { + rdev->pm.dynpm_state = DYNPM_STATE_ACTIVE; +#ifdef DUMBBELL_WIP + schedule_delayed_work(&rdev->pm.dynpm_idle_work, + msecs_to_jiffies(RADEON_IDLE_LOOP_MS)); +#endif /* DUMBBELL_WIP */ + } + sx_xunlock(&rdev->pm.mutex); + radeon_pm_compute_clocks(rdev); +} + +int radeon_pm_init(struct radeon_device *rdev) +{ + int ret; + + /* default to profile method */ + rdev->pm.pm_method = PM_METHOD_PROFILE; + rdev->pm.profile = PM_PROFILE_DEFAULT; + rdev->pm.dynpm_state = DYNPM_STATE_DISABLED; + rdev->pm.dynpm_planned_action = DYNPM_ACTION_NONE; + rdev->pm.dynpm_can_upclock = true; + rdev->pm.dynpm_can_downclock = true; + rdev->pm.default_sclk = rdev->clock.default_sclk; + rdev->pm.default_mclk = rdev->clock.default_mclk; + rdev->pm.current_sclk = rdev->clock.default_sclk; + rdev->pm.current_mclk = rdev->clock.default_mclk; + rdev->pm.int_thermal_type = THERMAL_TYPE_NONE; + + if (rdev->bios) { + if (rdev->is_atom_bios) + radeon_atombios_get_power_modes(rdev); + else + radeon_combios_get_power_modes(rdev); + radeon_pm_print_states(rdev); + radeon_pm_init_profile(rdev); + /* set up the default clocks if the MC ucode is loaded */ + if ((rdev->family >= CHIP_BARTS) && + (rdev->family <= CHIP_CAYMAN) && + rdev->mc_fw) { + if (rdev->pm.default_vddc) + radeon_atom_set_voltage(rdev, rdev->pm.default_vddc, + SET_VOLTAGE_TYPE_ASIC_VDDC); + if (rdev->pm.default_vddci) + radeon_atom_set_voltage(rdev, rdev->pm.default_vddci, + SET_VOLTAGE_TYPE_ASIC_VDDCI); + if (rdev->pm.default_sclk) + radeon_set_engine_clock(rdev, rdev->pm.default_sclk); + if (rdev->pm.default_mclk) + radeon_set_memory_clock(rdev, rdev->pm.default_mclk); + } + } + + /* set up the internal thermal sensor if applicable */ + ret = radeon_hwmon_init(rdev); + if (ret) + return ret; + +#ifdef DUMBBELL_WIP + INIT_DELAYED_WORK(&rdev->pm.dynpm_idle_work, radeon_dynpm_idle_work_handler); +#endif /* DUMBBELL_WIP */ + + if (rdev->pm.num_power_states > 1) { + /* where's the best place to put these? */ +#ifdef DUMBBELL_WIP + ret = device_create_file(rdev->dev, &dev_attr_power_profile); +#endif /* DUMBBELL_WIP */ + if (ret) + DRM_ERROR("failed to create device file for power profile\n"); +#ifdef DUMBBELL_WIP + ret = device_create_file(rdev->dev, &dev_attr_power_method); +#endif /* DUMBBELL_WIP */ + if (ret) + DRM_ERROR("failed to create device file for power method\n"); + + if (radeon_debugfs_pm_init(rdev)) { + DRM_ERROR("Failed to register debugfs file for PM!\n"); + } + + DRM_INFO("radeon: power management initialized\n"); + } + + return 0; +} + +void radeon_pm_fini(struct radeon_device *rdev) +{ + if (rdev->pm.num_power_states > 1) { + DRM_UNLOCK(rdev->ddev); /* Work around LOR. */ + sx_xlock(&rdev->pm.mutex); + if (rdev->pm.pm_method == PM_METHOD_PROFILE) { + rdev->pm.profile = PM_PROFILE_DEFAULT; + radeon_pm_update_profile(rdev); + radeon_pm_set_clocks(rdev); + } else if (rdev->pm.pm_method == PM_METHOD_DYNPM) { + /* reset default clocks */ + rdev->pm.dynpm_state = DYNPM_STATE_DISABLED; + rdev->pm.dynpm_planned_action = DYNPM_ACTION_DEFAULT; + radeon_pm_set_clocks(rdev); + } + sx_xunlock(&rdev->pm.mutex); + DRM_LOCK(rdev->ddev); + +#ifdef DUMBBELL_WIP + cancel_delayed_work_sync(&rdev->pm.dynpm_idle_work); + + device_remove_file(rdev->dev, &dev_attr_power_profile); + device_remove_file(rdev->dev, &dev_attr_power_method); +#endif /* DUMBBELL_WIP */ + } + + if (rdev->pm.power_state) { + int i; + for (i = 0; i < rdev->pm.num_power_states; ++i) { + free(rdev->pm.power_state[i].clock_info, DRM_MEM_DRIVER); + } + free(rdev->pm.power_state, DRM_MEM_DRIVER); + rdev->pm.power_state = NULL; + rdev->pm.num_power_states = 0; + } + + radeon_hwmon_fini(rdev); +} + +void radeon_pm_compute_clocks(struct radeon_device *rdev) +{ + struct drm_device *ddev = rdev->ddev; + struct drm_crtc *crtc; + struct radeon_crtc *radeon_crtc; + + if (rdev->pm.num_power_states < 2) + return; + + sx_xlock(&rdev->pm.mutex); + + rdev->pm.active_crtcs = 0; + rdev->pm.active_crtc_count = 0; + list_for_each_entry(crtc, + &ddev->mode_config.crtc_list, head) { + radeon_crtc = to_radeon_crtc(crtc); + if (radeon_crtc->enabled) { + rdev->pm.active_crtcs |= (1 << radeon_crtc->crtc_id); + rdev->pm.active_crtc_count++; + } + } + + if (rdev->pm.pm_method == PM_METHOD_PROFILE) { + radeon_pm_update_profile(rdev); + radeon_pm_set_clocks(rdev); + } else if (rdev->pm.pm_method == PM_METHOD_DYNPM) { + if (rdev->pm.dynpm_state != DYNPM_STATE_DISABLED) { + if (rdev->pm.active_crtc_count > 1) { + if (rdev->pm.dynpm_state == DYNPM_STATE_ACTIVE) { +#ifdef DUMBBELL_WIP + cancel_delayed_work(&rdev->pm.dynpm_idle_work); +#endif /* DUMBBELL_WIP */ + + rdev->pm.dynpm_state = DYNPM_STATE_PAUSED; + rdev->pm.dynpm_planned_action = DYNPM_ACTION_DEFAULT; + radeon_pm_get_dynpm_state(rdev); + radeon_pm_set_clocks(rdev); + + DRM_DEBUG_DRIVER("radeon: dynamic power management deactivated\n"); + } + } else if (rdev->pm.active_crtc_count == 1) { + /* TODO: Increase clocks if needed for current mode */ + + if (rdev->pm.dynpm_state == DYNPM_STATE_MINIMUM) { + rdev->pm.dynpm_state = DYNPM_STATE_ACTIVE; + rdev->pm.dynpm_planned_action = DYNPM_ACTION_UPCLOCK; + radeon_pm_get_dynpm_state(rdev); + radeon_pm_set_clocks(rdev); + +#ifdef DUMBBELL_WIP + schedule_delayed_work(&rdev->pm.dynpm_idle_work, + msecs_to_jiffies(RADEON_IDLE_LOOP_MS)); +#endif /* DUMBBELL_WIP */ + } else if (rdev->pm.dynpm_state == DYNPM_STATE_PAUSED) { + rdev->pm.dynpm_state = DYNPM_STATE_ACTIVE; +#ifdef DUMBBELL_WIP + schedule_delayed_work(&rdev->pm.dynpm_idle_work, + msecs_to_jiffies(RADEON_IDLE_LOOP_MS)); +#endif /* DUMBBELL_WIP */ + DRM_DEBUG_DRIVER("radeon: dynamic power management activated\n"); + } + } else { /* count == 0 */ + if (rdev->pm.dynpm_state != DYNPM_STATE_MINIMUM) { +#ifdef DUMBBELL_WIP + cancel_delayed_work(&rdev->pm.dynpm_idle_work); +#endif /* DUMBBELL_WIP */ + + rdev->pm.dynpm_state = DYNPM_STATE_MINIMUM; + rdev->pm.dynpm_planned_action = DYNPM_ACTION_MINIMUM; + radeon_pm_get_dynpm_state(rdev); + radeon_pm_set_clocks(rdev); + } + } + } + } + + sx_xunlock(&rdev->pm.mutex); +} + +static bool radeon_pm_in_vbl(struct radeon_device *rdev) +{ + int crtc, vpos, hpos, vbl_status; + bool in_vbl = true; + + /* Iterate over all active crtc's. All crtc's must be in vblank, + * otherwise return in_vbl == false. + */ + for (crtc = 0; (crtc < rdev->num_crtc) && in_vbl; crtc++) { + if (rdev->pm.active_crtcs & (1 << crtc)) { + vbl_status = radeon_get_crtc_scanoutpos(rdev->ddev, crtc, &vpos, &hpos); + if ((vbl_status & DRM_SCANOUTPOS_VALID) && + !(vbl_status & DRM_SCANOUTPOS_INVBL)) + in_vbl = false; + } + } + + return in_vbl; +} + +static bool radeon_pm_debug_check_in_vbl(struct radeon_device *rdev, bool finish) +{ + u32 stat_crtc = 0; + bool in_vbl = radeon_pm_in_vbl(rdev); + + if (in_vbl == false) + DRM_DEBUG_DRIVER("not in vbl for pm change %08x at %s\n", stat_crtc, + finish ? "exit" : "entry"); + return in_vbl; +} + +#ifdef DUMBBELL_WIP +static void radeon_dynpm_idle_work_handler(struct work_struct *work) +{ + struct radeon_device *rdev; + int resched; + rdev = container_of(work, struct radeon_device, + pm.dynpm_idle_work.work); + + resched = ttm_bo_lock_delayed_workqueue(&rdev->mman.bdev); + sx_xlock(&rdev->pm.mutex); + if (rdev->pm.dynpm_state == DYNPM_STATE_ACTIVE) { + int not_processed = 0; + int i; + + for (i = 0; i < RADEON_NUM_RINGS; ++i) { + struct radeon_ring *ring = &rdev->ring[i]; + + if (ring->ready) { + not_processed += radeon_fence_count_emitted(rdev, i); + if (not_processed >= 3) + break; + } + } + + if (not_processed >= 3) { /* should upclock */ + if (rdev->pm.dynpm_planned_action == DYNPM_ACTION_DOWNCLOCK) { + rdev->pm.dynpm_planned_action = DYNPM_ACTION_NONE; + } else if (rdev->pm.dynpm_planned_action == DYNPM_ACTION_NONE && + rdev->pm.dynpm_can_upclock) { + rdev->pm.dynpm_planned_action = + DYNPM_ACTION_UPCLOCK; + rdev->pm.dynpm_action_timeout = jiffies + + msecs_to_jiffies(RADEON_RECLOCK_DELAY_MS); + } + } else if (not_processed == 0) { /* should downclock */ + if (rdev->pm.dynpm_planned_action == DYNPM_ACTION_UPCLOCK) { + rdev->pm.dynpm_planned_action = DYNPM_ACTION_NONE; + } else if (rdev->pm.dynpm_planned_action == DYNPM_ACTION_NONE && + rdev->pm.dynpm_can_downclock) { + rdev->pm.dynpm_planned_action = + DYNPM_ACTION_DOWNCLOCK; + rdev->pm.dynpm_action_timeout = jiffies + + msecs_to_jiffies(RADEON_RECLOCK_DELAY_MS); + } + } + + /* Note, radeon_pm_set_clocks is called with static_switch set + * to false since we want to wait for vbl to avoid flicker. + */ + if (rdev->pm.dynpm_planned_action != DYNPM_ACTION_NONE && + jiffies > rdev->pm.dynpm_action_timeout) { + radeon_pm_get_dynpm_state(rdev); + radeon_pm_set_clocks(rdev); + } + + schedule_delayed_work(&rdev->pm.dynpm_idle_work, + msecs_to_jiffies(RADEON_IDLE_LOOP_MS)); + } + sx_xunlock(&rdev->pm.mutex); + ttm_bo_unlock_delayed_workqueue(&rdev->mman.bdev, resched); +} +#endif /* DUMBBELL_WIP */ + +/* + * Debugfs info + */ +#if defined(CONFIG_DEBUG_FS) + +static int radeon_debugfs_pm_info(struct seq_file *m, void *data) +{ + struct drm_info_node *node = (struct drm_info_node *) m->private; + struct drm_device *dev = node->minor->dev; + struct radeon_device *rdev = dev->dev_private; + + seq_printf(m, "default engine clock: %u0 kHz\n", rdev->pm.default_sclk); + seq_printf(m, "current engine clock: %u0 kHz\n", radeon_get_engine_clock(rdev)); + seq_printf(m, "default memory clock: %u0 kHz\n", rdev->pm.default_mclk); + if (rdev->asic->pm.get_memory_clock) + seq_printf(m, "current memory clock: %u0 kHz\n", radeon_get_memory_clock(rdev)); + if (rdev->pm.current_vddc) + seq_printf(m, "voltage: %u mV\n", rdev->pm.current_vddc); + if (rdev->asic->pm.get_pcie_lanes) + seq_printf(m, "PCIE lanes: %d\n", radeon_get_pcie_lanes(rdev)); + + return 0; +} + +static struct drm_info_list radeon_pm_info_list[] = { + {"radeon_pm_info", radeon_debugfs_pm_info, 0, NULL}, +}; +#endif + +static int radeon_debugfs_pm_init(struct radeon_device *rdev) +{ +#if defined(CONFIG_DEBUG_FS) + return radeon_debugfs_add_files(rdev, radeon_pm_info_list, ARRAY_SIZE(radeon_pm_info_list)); +#else + return 0; +#endif +} diff --git a/sys/dev/drm2/radeon/radeon_prime.c b/sys/dev/drm2/radeon/radeon_prime.c new file mode 100644 index 00000000000..c0c7a548b0e --- /dev/null +++ b/sys/dev/drm2/radeon/radeon_prime.c @@ -0,0 +1,230 @@ +/* + * Copyright 2012 Advanced Micro Devices, Inc. + * + * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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. + * + * based on nouveau_prime.c + * + * Authors: Alex Deucher + */ + +#include +__FBSDID("$FreeBSD$"); + +#include + +#include "radeon.h" +#include + +#include + +static struct sg_table *radeon_gem_map_dma_buf(struct dma_buf_attachment *attachment, + enum dma_data_direction dir) +{ + struct radeon_bo *bo = attachment->dmabuf->priv; + struct drm_device *dev = bo->rdev->ddev; + int npages = bo->tbo.num_pages; + struct sg_table *sg; + int nents; + + mutex_lock(&dev->struct_mutex); + sg = drm_prime_pages_to_sg(bo->tbo.ttm->pages, npages); + nents = dma_map_sg(attachment->dev, sg->sgl, sg->nents, dir); + mutex_unlock(&dev->struct_mutex); + return sg; +} + +static void radeon_gem_unmap_dma_buf(struct dma_buf_attachment *attachment, + struct sg_table *sg, enum dma_data_direction dir) +{ + dma_unmap_sg(attachment->dev, sg->sgl, sg->nents, dir); + sg_free_table(sg); + kfree(sg); +} + +static void radeon_gem_dmabuf_release(struct dma_buf *dma_buf) +{ + struct radeon_bo *bo = dma_buf->priv; + + if (bo->gem_base.export_dma_buf == dma_buf) { + DRM_ERROR("unreference dmabuf %p\n", &bo->gem_base); + bo->gem_base.export_dma_buf = NULL; + drm_gem_object_unreference_unlocked(&bo->gem_base); + } +} + +static void *radeon_gem_kmap_atomic(struct dma_buf *dma_buf, unsigned long page_num) +{ + return NULL; +} + +static void radeon_gem_kunmap_atomic(struct dma_buf *dma_buf, unsigned long page_num, void *addr) +{ + +} +static void *radeon_gem_kmap(struct dma_buf *dma_buf, unsigned long page_num) +{ + return NULL; +} + +static void radeon_gem_kunmap(struct dma_buf *dma_buf, unsigned long page_num, void *addr) +{ + +} + +static int radeon_gem_prime_mmap(struct dma_buf *dma_buf, struct vm_area_struct *vma) +{ + return -EINVAL; +} + +static void *radeon_gem_prime_vmap(struct dma_buf *dma_buf) +{ + struct radeon_bo *bo = dma_buf->priv; + struct drm_device *dev = bo->rdev->ddev; + int ret; + + mutex_lock(&dev->struct_mutex); + if (bo->vmapping_count) { + bo->vmapping_count++; + goto out_unlock; + } + + ret = ttm_bo_kmap(&bo->tbo, 0, bo->tbo.num_pages, + &bo->dma_buf_vmap); + if (ret) { + mutex_unlock(&dev->struct_mutex); + return ERR_PTR(ret); + } + bo->vmapping_count = 1; +out_unlock: + mutex_unlock(&dev->struct_mutex); + return bo->dma_buf_vmap.virtual; +} + +static void radeon_gem_prime_vunmap(struct dma_buf *dma_buf, void *vaddr) +{ + struct radeon_bo *bo = dma_buf->priv; + struct drm_device *dev = bo->rdev->ddev; + + mutex_lock(&dev->struct_mutex); + bo->vmapping_count--; + if (bo->vmapping_count == 0) { + ttm_bo_kunmap(&bo->dma_buf_vmap); + } + mutex_unlock(&dev->struct_mutex); +} +const static struct dma_buf_ops radeon_dmabuf_ops = { + .map_dma_buf = radeon_gem_map_dma_buf, + .unmap_dma_buf = radeon_gem_unmap_dma_buf, + .release = radeon_gem_dmabuf_release, + .kmap = radeon_gem_kmap, + .kmap_atomic = radeon_gem_kmap_atomic, + .kunmap = radeon_gem_kunmap, + .kunmap_atomic = radeon_gem_kunmap_atomic, + .mmap = radeon_gem_prime_mmap, + .vmap = radeon_gem_prime_vmap, + .vunmap = radeon_gem_prime_vunmap, +}; + +static int radeon_prime_create(struct drm_device *dev, + size_t size, + struct sg_table *sg, + struct radeon_bo **pbo) +{ + struct radeon_device *rdev = dev->dev_private; + struct radeon_bo *bo; + int ret; + + ret = radeon_bo_create(rdev, size, PAGE_SIZE, false, + RADEON_GEM_DOMAIN_GTT, sg, pbo); + if (ret) + return ret; + bo = *pbo; + bo->gem_base.driver_private = bo; + + mutex_lock(&rdev->gem.mutex); + list_add_tail(&bo->list, &rdev->gem.objects); + mutex_unlock(&rdev->gem.mutex); + + return 0; +} + +struct dma_buf *radeon_gem_prime_export(struct drm_device *dev, + struct drm_gem_object *obj, + int flags) +{ + struct radeon_bo *bo = gem_to_radeon_bo(obj); + int ret = 0; + + ret = radeon_bo_reserve(bo, false); + if (unlikely(ret != 0)) + return ERR_PTR(ret); + + /* pin buffer into GTT */ + ret = radeon_bo_pin(bo, RADEON_GEM_DOMAIN_GTT, NULL); + if (ret) { + radeon_bo_unreserve(bo); + return ERR_PTR(ret); + } + radeon_bo_unreserve(bo); + return dma_buf_export(bo, &radeon_dmabuf_ops, obj->size, flags); +} + +struct drm_gem_object *radeon_gem_prime_import(struct drm_device *dev, + struct dma_buf *dma_buf) +{ + struct dma_buf_attachment *attach; + struct sg_table *sg; + struct radeon_bo *bo; + int ret; + + if (dma_buf->ops == &radeon_dmabuf_ops) { + bo = dma_buf->priv; + if (bo->gem_base.dev == dev) { + drm_gem_object_reference(&bo->gem_base); + dma_buf_put(dma_buf); + return &bo->gem_base; + } + } + + /* need to attach */ + attach = dma_buf_attach(dma_buf, dev->dev); + if (IS_ERR(attach)) + return ERR_CAST(attach); + + sg = dma_buf_map_attachment(attach, DMA_BIDIRECTIONAL); + if (IS_ERR(sg)) { + ret = PTR_ERR(sg); + goto fail_detach; + } + + ret = radeon_prime_create(dev, dma_buf->size, sg, &bo); + if (ret) + goto fail_unmap; + + bo->gem_base.import_attach = attach; + + return &bo->gem_base; + +fail_unmap: + dma_buf_unmap_attachment(attach, sg, DMA_BIDIRECTIONAL); +fail_detach: + dma_buf_detach(dma_buf, attach); + return ERR_PTR(ret); +} diff --git a/sys/dev/drm2/radeon/radeon_reg.h b/sys/dev/drm2/radeon/radeon_reg.h new file mode 100644 index 00000000000..8697e0f27e7 --- /dev/null +++ b/sys/dev/drm2/radeon/radeon_reg.h @@ -0,0 +1,3713 @@ +/* + * Copyright 2000 ATI Technologies Inc., Markham, Ontario, and + * VA Linux Systems Inc., Fremont, California. + * + * All Rights Reserved. + * + * 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 on 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 (including the + * next paragraph) 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 + * NON-INFRINGEMENT. IN NO EVENT SHALL ATI, VA LINUX SYSTEMS AND/OR + * THEIR SUPPLIERS 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. + */ + +/* + * Authors: + * Kevin E. Martin + * Rickard E. Faith + * Alan Hourihane + * + * References: + * + * !!!! FIXME !!!! + * RAGE 128 VR/ RAGE 128 GL Register Reference Manual (Technical + * Reference Manual P/N RRG-G04100-C Rev. 0.04), ATI Technologies: April + * 1999. + * + * !!!! FIXME !!!! + * RAGE 128 Software Development Manual (Technical Reference Manual P/N + * SDK-G04000 Rev. 0.01), ATI Technologies: June 1999. + * + */ + +/* !!!! FIXME !!!! NOTE: THIS FILE HAS BEEN CONVERTED FROM r128_reg.h + * AND CONTAINS REGISTERS AND REGISTER DEFINITIONS THAT ARE NOT CORRECT + * ON THE RADEON. A FULL AUDIT OF THIS CODE IS NEEDED! */ + +#include +__FBSDID("$FreeBSD$"); + +#ifndef _RADEON_REG_H_ +#define _RADEON_REG_H_ + +#include "r300_reg.h" +#include "r500_reg.h" +#include "r600_reg.h" +#include "evergreen_reg.h" +#include "ni_reg.h" +#include "si_reg.h" + +#define RADEON_MC_AGP_LOCATION 0x014c +#define RADEON_MC_AGP_START_MASK 0x0000FFFF +#define RADEON_MC_AGP_START_SHIFT 0 +#define RADEON_MC_AGP_TOP_MASK 0xFFFF0000 +#define RADEON_MC_AGP_TOP_SHIFT 16 +#define RADEON_MC_FB_LOCATION 0x0148 +#define RADEON_MC_FB_START_MASK 0x0000FFFF +#define RADEON_MC_FB_START_SHIFT 0 +#define RADEON_MC_FB_TOP_MASK 0xFFFF0000 +#define RADEON_MC_FB_TOP_SHIFT 16 +#define RADEON_AGP_BASE_2 0x015c /* r200+ only */ +#define RADEON_AGP_BASE 0x0170 + +#define ATI_DATATYPE_VQ 0 +#define ATI_DATATYPE_CI4 1 +#define ATI_DATATYPE_CI8 2 +#define ATI_DATATYPE_ARGB1555 3 +#define ATI_DATATYPE_RGB565 4 +#define ATI_DATATYPE_RGB888 5 +#define ATI_DATATYPE_ARGB8888 6 +#define ATI_DATATYPE_RGB332 7 +#define ATI_DATATYPE_Y8 8 +#define ATI_DATATYPE_RGB8 9 +#define ATI_DATATYPE_CI16 10 +#define ATI_DATATYPE_VYUY_422 11 +#define ATI_DATATYPE_YVYU_422 12 +#define ATI_DATATYPE_AYUV_444 14 +#define ATI_DATATYPE_ARGB4444 15 + + /* Registers for 2D/Video/Overlay */ +#define RADEON_ADAPTER_ID 0x0f2c /* PCI */ +#define RADEON_AGP_BASE 0x0170 +#define RADEON_AGP_CNTL 0x0174 +# define RADEON_AGP_APER_SIZE_256MB (0x00 << 0) +# define RADEON_AGP_APER_SIZE_128MB (0x20 << 0) +# define RADEON_AGP_APER_SIZE_64MB (0x30 << 0) +# define RADEON_AGP_APER_SIZE_32MB (0x38 << 0) +# define RADEON_AGP_APER_SIZE_16MB (0x3c << 0) +# define RADEON_AGP_APER_SIZE_8MB (0x3e << 0) +# define RADEON_AGP_APER_SIZE_4MB (0x3f << 0) +# define RADEON_AGP_APER_SIZE_MASK (0x3f << 0) +#define RADEON_STATUS_PCI_CONFIG 0x06 +# define RADEON_CAP_LIST 0x100000 +#define RADEON_CAPABILITIES_PTR_PCI_CONFIG 0x34 /* offset in PCI config*/ +# define RADEON_CAP_PTR_MASK 0xfc /* mask off reserved bits of CAP_PTR */ +# define RADEON_CAP_ID_NULL 0x00 /* End of capability list */ +# define RADEON_CAP_ID_AGP 0x02 /* AGP capability ID */ +# define RADEON_CAP_ID_EXP 0x10 /* PCI Express */ +#define RADEON_AGP_COMMAND 0x0f60 /* PCI */ +#define RADEON_AGP_COMMAND_PCI_CONFIG 0x0060 /* offset in PCI config*/ +# define RADEON_AGP_ENABLE (1<<8) +#define RADEON_AGP_PLL_CNTL 0x000b /* PLL */ +#define RADEON_AGP_STATUS 0x0f5c /* PCI */ +# define RADEON_AGP_1X_MODE 0x01 +# define RADEON_AGP_2X_MODE 0x02 +# define RADEON_AGP_4X_MODE 0x04 +# define RADEON_AGP_FW_MODE 0x10 +# define RADEON_AGP_MODE_MASK 0x17 +# define RADEON_AGPv3_MODE 0x08 +# define RADEON_AGPv3_4X_MODE 0x01 +# define RADEON_AGPv3_8X_MODE 0x02 +#define RADEON_ATTRDR 0x03c1 /* VGA */ +#define RADEON_ATTRDW 0x03c0 /* VGA */ +#define RADEON_ATTRX 0x03c0 /* VGA */ +#define RADEON_AUX_SC_CNTL 0x1660 +# define RADEON_AUX1_SC_EN (1 << 0) +# define RADEON_AUX1_SC_MODE_OR (0 << 1) +# define RADEON_AUX1_SC_MODE_NAND (1 << 1) +# define RADEON_AUX2_SC_EN (1 << 2) +# define RADEON_AUX2_SC_MODE_OR (0 << 3) +# define RADEON_AUX2_SC_MODE_NAND (1 << 3) +# define RADEON_AUX3_SC_EN (1 << 4) +# define RADEON_AUX3_SC_MODE_OR (0 << 5) +# define RADEON_AUX3_SC_MODE_NAND (1 << 5) +#define RADEON_AUX1_SC_BOTTOM 0x1670 +#define RADEON_AUX1_SC_LEFT 0x1664 +#define RADEON_AUX1_SC_RIGHT 0x1668 +#define RADEON_AUX1_SC_TOP 0x166c +#define RADEON_AUX2_SC_BOTTOM 0x1680 +#define RADEON_AUX2_SC_LEFT 0x1674 +#define RADEON_AUX2_SC_RIGHT 0x1678 +#define RADEON_AUX2_SC_TOP 0x167c +#define RADEON_AUX3_SC_BOTTOM 0x1690 +#define RADEON_AUX3_SC_LEFT 0x1684 +#define RADEON_AUX3_SC_RIGHT 0x1688 +#define RADEON_AUX3_SC_TOP 0x168c +#define RADEON_AUX_WINDOW_HORZ_CNTL 0x02d8 +#define RADEON_AUX_WINDOW_VERT_CNTL 0x02dc + +#define RADEON_BASE_CODE 0x0f0b +#define RADEON_BIOS_0_SCRATCH 0x0010 +# define RADEON_FP_PANEL_SCALABLE (1 << 16) +# define RADEON_FP_PANEL_SCALE_EN (1 << 17) +# define RADEON_FP_CHIP_SCALE_EN (1 << 18) +# define RADEON_DRIVER_BRIGHTNESS_EN (1 << 26) +# define RADEON_DISPLAY_ROT_MASK (3 << 28) +# define RADEON_DISPLAY_ROT_00 (0 << 28) +# define RADEON_DISPLAY_ROT_90 (1 << 28) +# define RADEON_DISPLAY_ROT_180 (2 << 28) +# define RADEON_DISPLAY_ROT_270 (3 << 28) +#define RADEON_BIOS_1_SCRATCH 0x0014 +#define RADEON_BIOS_2_SCRATCH 0x0018 +#define RADEON_BIOS_3_SCRATCH 0x001c +#define RADEON_BIOS_4_SCRATCH 0x0020 +# define RADEON_CRT1_ATTACHED_MASK (3 << 0) +# define RADEON_CRT1_ATTACHED_MONO (1 << 0) +# define RADEON_CRT1_ATTACHED_COLOR (2 << 0) +# define RADEON_LCD1_ATTACHED (1 << 2) +# define RADEON_DFP1_ATTACHED (1 << 3) +# define RADEON_TV1_ATTACHED_MASK (3 << 4) +# define RADEON_TV1_ATTACHED_COMP (1 << 4) +# define RADEON_TV1_ATTACHED_SVIDEO (2 << 4) +# define RADEON_CRT2_ATTACHED_MASK (3 << 8) +# define RADEON_CRT2_ATTACHED_MONO (1 << 8) +# define RADEON_CRT2_ATTACHED_COLOR (2 << 8) +# define RADEON_DFP2_ATTACHED (1 << 11) +#define RADEON_BIOS_5_SCRATCH 0x0024 +# define RADEON_LCD1_ON (1 << 0) +# define RADEON_CRT1_ON (1 << 1) +# define RADEON_TV1_ON (1 << 2) +# define RADEON_DFP1_ON (1 << 3) +# define RADEON_CRT2_ON (1 << 5) +# define RADEON_CV1_ON (1 << 6) +# define RADEON_DFP2_ON (1 << 7) +# define RADEON_LCD1_CRTC_MASK (1 << 8) +# define RADEON_LCD1_CRTC_SHIFT 8 +# define RADEON_CRT1_CRTC_MASK (1 << 9) +# define RADEON_CRT1_CRTC_SHIFT 9 +# define RADEON_TV1_CRTC_MASK (1 << 10) +# define RADEON_TV1_CRTC_SHIFT 10 +# define RADEON_DFP1_CRTC_MASK (1 << 11) +# define RADEON_DFP1_CRTC_SHIFT 11 +# define RADEON_CRT2_CRTC_MASK (1 << 12) +# define RADEON_CRT2_CRTC_SHIFT 12 +# define RADEON_CV1_CRTC_MASK (1 << 13) +# define RADEON_CV1_CRTC_SHIFT 13 +# define RADEON_DFP2_CRTC_MASK (1 << 14) +# define RADEON_DFP2_CRTC_SHIFT 14 +# define RADEON_ACC_REQ_LCD1 (1 << 16) +# define RADEON_ACC_REQ_CRT1 (1 << 17) +# define RADEON_ACC_REQ_TV1 (1 << 18) +# define RADEON_ACC_REQ_DFP1 (1 << 19) +# define RADEON_ACC_REQ_CRT2 (1 << 21) +# define RADEON_ACC_REQ_TV2 (1 << 22) +# define RADEON_ACC_REQ_DFP2 (1 << 23) +#define RADEON_BIOS_6_SCRATCH 0x0028 +# define RADEON_ACC_MODE_CHANGE (1 << 2) +# define RADEON_EXT_DESKTOP_MODE (1 << 3) +# define RADEON_LCD_DPMS_ON (1 << 20) +# define RADEON_CRT_DPMS_ON (1 << 21) +# define RADEON_TV_DPMS_ON (1 << 22) +# define RADEON_DFP_DPMS_ON (1 << 23) +# define RADEON_DPMS_MASK (3 << 24) +# define RADEON_DPMS_ON (0 << 24) +# define RADEON_DPMS_STANDBY (1 << 24) +# define RADEON_DPMS_SUSPEND (2 << 24) +# define RADEON_DPMS_OFF (3 << 24) +# define RADEON_SCREEN_BLANKING (1 << 26) +# define RADEON_DRIVER_CRITICAL (1 << 27) +# define RADEON_DISPLAY_SWITCHING_DIS (1 << 30) +#define RADEON_BIOS_7_SCRATCH 0x002c +# define RADEON_SYS_HOTKEY (1 << 10) +# define RADEON_DRV_LOADED (1 << 12) +#define RADEON_BIOS_ROM 0x0f30 /* PCI */ +#define RADEON_BIST 0x0f0f /* PCI */ +#define RADEON_BRUSH_DATA0 0x1480 +#define RADEON_BRUSH_DATA1 0x1484 +#define RADEON_BRUSH_DATA10 0x14a8 +#define RADEON_BRUSH_DATA11 0x14ac +#define RADEON_BRUSH_DATA12 0x14b0 +#define RADEON_BRUSH_DATA13 0x14b4 +#define RADEON_BRUSH_DATA14 0x14b8 +#define RADEON_BRUSH_DATA15 0x14bc +#define RADEON_BRUSH_DATA16 0x14c0 +#define RADEON_BRUSH_DATA17 0x14c4 +#define RADEON_BRUSH_DATA18 0x14c8 +#define RADEON_BRUSH_DATA19 0x14cc +#define RADEON_BRUSH_DATA2 0x1488 +#define RADEON_BRUSH_DATA20 0x14d0 +#define RADEON_BRUSH_DATA21 0x14d4 +#define RADEON_BRUSH_DATA22 0x14d8 +#define RADEON_BRUSH_DATA23 0x14dc +#define RADEON_BRUSH_DATA24 0x14e0 +#define RADEON_BRUSH_DATA25 0x14e4 +#define RADEON_BRUSH_DATA26 0x14e8 +#define RADEON_BRUSH_DATA27 0x14ec +#define RADEON_BRUSH_DATA28 0x14f0 +#define RADEON_BRUSH_DATA29 0x14f4 +#define RADEON_BRUSH_DATA3 0x148c +#define RADEON_BRUSH_DATA30 0x14f8 +#define RADEON_BRUSH_DATA31 0x14fc +#define RADEON_BRUSH_DATA32 0x1500 +#define RADEON_BRUSH_DATA33 0x1504 +#define RADEON_BRUSH_DATA34 0x1508 +#define RADEON_BRUSH_DATA35 0x150c +#define RADEON_BRUSH_DATA36 0x1510 +#define RADEON_BRUSH_DATA37 0x1514 +#define RADEON_BRUSH_DATA38 0x1518 +#define RADEON_BRUSH_DATA39 0x151c +#define RADEON_BRUSH_DATA4 0x1490 +#define RADEON_BRUSH_DATA40 0x1520 +#define RADEON_BRUSH_DATA41 0x1524 +#define RADEON_BRUSH_DATA42 0x1528 +#define RADEON_BRUSH_DATA43 0x152c +#define RADEON_BRUSH_DATA44 0x1530 +#define RADEON_BRUSH_DATA45 0x1534 +#define RADEON_BRUSH_DATA46 0x1538 +#define RADEON_BRUSH_DATA47 0x153c +#define RADEON_BRUSH_DATA48 0x1540 +#define RADEON_BRUSH_DATA49 0x1544 +#define RADEON_BRUSH_DATA5 0x1494 +#define RADEON_BRUSH_DATA50 0x1548 +#define RADEON_BRUSH_DATA51 0x154c +#define RADEON_BRUSH_DATA52 0x1550 +#define RADEON_BRUSH_DATA53 0x1554 +#define RADEON_BRUSH_DATA54 0x1558 +#define RADEON_BRUSH_DATA55 0x155c +#define RADEON_BRUSH_DATA56 0x1560 +#define RADEON_BRUSH_DATA57 0x1564 +#define RADEON_BRUSH_DATA58 0x1568 +#define RADEON_BRUSH_DATA59 0x156c +#define RADEON_BRUSH_DATA6 0x1498 +#define RADEON_BRUSH_DATA60 0x1570 +#define RADEON_BRUSH_DATA61 0x1574 +#define RADEON_BRUSH_DATA62 0x1578 +#define RADEON_BRUSH_DATA63 0x157c +#define RADEON_BRUSH_DATA7 0x149c +#define RADEON_BRUSH_DATA8 0x14a0 +#define RADEON_BRUSH_DATA9 0x14a4 +#define RADEON_BRUSH_SCALE 0x1470 +#define RADEON_BRUSH_Y_X 0x1474 +#define RADEON_BUS_CNTL 0x0030 +# define RADEON_BUS_MASTER_DIS (1 << 6) +# define RADEON_BUS_BIOS_DIS_ROM (1 << 12) +# define RS600_BUS_MASTER_DIS (1 << 14) +# define RS600_MSI_REARM (1 << 20) /* rs600/rs690/rs740 */ +# define RADEON_BUS_RD_DISCARD_EN (1 << 24) +# define RADEON_BUS_RD_ABORT_EN (1 << 25) +# define RADEON_BUS_MSTR_DISCONNECT_EN (1 << 28) +# define RADEON_BUS_WRT_BURST (1 << 29) +# define RADEON_BUS_READ_BURST (1 << 30) +#define RADEON_BUS_CNTL1 0x0034 +# define RADEON_BUS_WAIT_ON_LOCK_EN (1 << 4) +#define RV370_BUS_CNTL 0x004c +# define RV370_BUS_BIOS_DIS_ROM (1 << 2) +/* rv370/rv380, rv410, r423/r430/r480, r5xx */ +#define RADEON_MSI_REARM_EN 0x0160 +# define RV370_MSI_REARM_EN (1 << 0) + +/* #define RADEON_PCIE_INDEX 0x0030 */ +/* #define RADEON_PCIE_DATA 0x0034 */ +#define RADEON_PCIE_LC_LINK_WIDTH_CNTL 0xa2 /* PCIE */ +# define RADEON_PCIE_LC_LINK_WIDTH_SHIFT 0 +# define RADEON_PCIE_LC_LINK_WIDTH_MASK 0x7 +# define RADEON_PCIE_LC_LINK_WIDTH_X0 0 +# define RADEON_PCIE_LC_LINK_WIDTH_X1 1 +# define RADEON_PCIE_LC_LINK_WIDTH_X2 2 +# define RADEON_PCIE_LC_LINK_WIDTH_X4 3 +# define RADEON_PCIE_LC_LINK_WIDTH_X8 4 +# define RADEON_PCIE_LC_LINK_WIDTH_X12 5 +# define RADEON_PCIE_LC_LINK_WIDTH_X16 6 +# define RADEON_PCIE_LC_LINK_WIDTH_RD_SHIFT 4 +# define RADEON_PCIE_LC_LINK_WIDTH_RD_MASK 0x70 +# define RADEON_PCIE_LC_RECONFIG_NOW (1 << 8) +# define RADEON_PCIE_LC_RECONFIG_LATER (1 << 9) +# define RADEON_PCIE_LC_SHORT_RECONFIG_EN (1 << 10) +# define R600_PCIE_LC_RECONFIG_ARC_MISSING_ESCAPE (1 << 7) +# define R600_PCIE_LC_RENEGOTIATION_SUPPORT (1 << 9) +# define R600_PCIE_LC_RENEGOTIATE_EN (1 << 10) +# define R600_PCIE_LC_SHORT_RECONFIG_EN (1 << 11) +# define R600_PCIE_LC_UPCONFIGURE_SUPPORT (1 << 12) +# define R600_PCIE_LC_UPCONFIGURE_DIS (1 << 13) + +#define R600_TARGET_AND_CURRENT_PROFILE_INDEX 0x70c +#define R700_TARGET_AND_CURRENT_PROFILE_INDEX 0x66c + +#define RADEON_CACHE_CNTL 0x1724 +#define RADEON_CACHE_LINE 0x0f0c /* PCI */ +#define RADEON_CAPABILITIES_ID 0x0f50 /* PCI */ +#define RADEON_CAPABILITIES_PTR 0x0f34 /* PCI */ +#define RADEON_CLK_PIN_CNTL 0x0001 /* PLL */ +# define RADEON_DONT_USE_XTALIN (1 << 4) +# define RADEON_SCLK_DYN_START_CNTL (1 << 15) +#define RADEON_CLOCK_CNTL_DATA 0x000c +#define RADEON_CLOCK_CNTL_INDEX 0x0008 +# define RADEON_PLL_WR_EN (1 << 7) +# define RADEON_PLL_DIV_SEL (3 << 8) +# define RADEON_PLL2_DIV_SEL_MASK (~(3 << 8)) +#define RADEON_CLK_PWRMGT_CNTL 0x0014 +# define RADEON_ENGIN_DYNCLK_MODE (1 << 12) +# define RADEON_ACTIVE_HILO_LAT_MASK (3 << 13) +# define RADEON_ACTIVE_HILO_LAT_SHIFT 13 +# define RADEON_DISP_DYN_STOP_LAT_MASK (1 << 12) +# define RADEON_MC_BUSY (1 << 16) +# define RADEON_DLL_READY (1 << 19) +# define RADEON_CG_NO1_DEBUG_0 (1 << 24) +# define RADEON_CG_NO1_DEBUG_MASK (0x1f << 24) +# define RADEON_DYN_STOP_MODE_MASK (7 << 21) +# define RADEON_TVPLL_PWRMGT_OFF (1 << 30) +# define RADEON_TVCLK_TURNOFF (1 << 31) +#define RADEON_PLL_PWRMGT_CNTL 0x0015 /* PLL */ +# define RADEON_PM_MODE_SEL (1 << 13) +# define RADEON_TCL_BYPASS_DISABLE (1 << 20) +#define RADEON_CLR_CMP_CLR_3D 0x1a24 +#define RADEON_CLR_CMP_CLR_DST 0x15c8 +#define RADEON_CLR_CMP_CLR_SRC 0x15c4 +#define RADEON_CLR_CMP_CNTL 0x15c0 +# define RADEON_SRC_CMP_EQ_COLOR (4 << 0) +# define RADEON_SRC_CMP_NEQ_COLOR (5 << 0) +# define RADEON_CLR_CMP_SRC_SOURCE (1 << 24) +#define RADEON_CLR_CMP_MASK 0x15cc +# define RADEON_CLR_CMP_MSK 0xffffffff +#define RADEON_CLR_CMP_MASK_3D 0x1A28 +#define RADEON_COMMAND 0x0f04 /* PCI */ +#define RADEON_COMPOSITE_SHADOW_ID 0x1a0c +#define RADEON_CONFIG_APER_0_BASE 0x0100 +#define RADEON_CONFIG_APER_1_BASE 0x0104 +#define RADEON_CONFIG_APER_SIZE 0x0108 +#define RADEON_CONFIG_BONDS 0x00e8 +#define RADEON_CONFIG_CNTL 0x00e0 +# define RADEON_CFG_VGA_RAM_EN (1 << 8) +# define RADEON_CFG_VGA_IO_DIS (1 << 9) +# define RADEON_CFG_ATI_REV_A11 (0 << 16) +# define RADEON_CFG_ATI_REV_A12 (1 << 16) +# define RADEON_CFG_ATI_REV_A13 (2 << 16) +# define RADEON_CFG_ATI_REV_ID_MASK (0xf << 16) +#define RADEON_CONFIG_MEMSIZE 0x00f8 +#define RADEON_CONFIG_MEMSIZE_EMBEDDED 0x0114 +#define RADEON_CONFIG_REG_1_BASE 0x010c +#define RADEON_CONFIG_REG_APER_SIZE 0x0110 +#define RADEON_CONFIG_XSTRAP 0x00e4 +#define RADEON_CONSTANT_COLOR_C 0x1d34 +# define RADEON_CONSTANT_COLOR_MASK 0x00ffffff +# define RADEON_CONSTANT_COLOR_ONE 0x00ffffff +# define RADEON_CONSTANT_COLOR_ZERO 0x00000000 +#define RADEON_CRC_CMDFIFO_ADDR 0x0740 +#define RADEON_CRC_CMDFIFO_DOUT 0x0744 +#define RADEON_GRPH_BUFFER_CNTL 0x02f0 +# define RADEON_GRPH_START_REQ_MASK (0x7f) +# define RADEON_GRPH_START_REQ_SHIFT 0 +# define RADEON_GRPH_STOP_REQ_MASK (0x7f<<8) +# define RADEON_GRPH_STOP_REQ_SHIFT 8 +# define RADEON_GRPH_CRITICAL_POINT_MASK (0x7f<<16) +# define RADEON_GRPH_CRITICAL_POINT_SHIFT 16 +# define RADEON_GRPH_CRITICAL_CNTL (1<<28) +# define RADEON_GRPH_BUFFER_SIZE (1<<29) +# define RADEON_GRPH_CRITICAL_AT_SOF (1<<30) +# define RADEON_GRPH_STOP_CNTL (1<<31) +#define RADEON_GRPH2_BUFFER_CNTL 0x03f0 +# define RADEON_GRPH2_START_REQ_MASK (0x7f) +# define RADEON_GRPH2_START_REQ_SHIFT 0 +# define RADEON_GRPH2_STOP_REQ_MASK (0x7f<<8) +# define RADEON_GRPH2_STOP_REQ_SHIFT 8 +# define RADEON_GRPH2_CRITICAL_POINT_MASK (0x7f<<16) +# define RADEON_GRPH2_CRITICAL_POINT_SHIFT 16 +# define RADEON_GRPH2_CRITICAL_CNTL (1<<28) +# define RADEON_GRPH2_BUFFER_SIZE (1<<29) +# define RADEON_GRPH2_CRITICAL_AT_SOF (1<<30) +# define RADEON_GRPH2_STOP_CNTL (1<<31) +#define RADEON_CRTC_CRNT_FRAME 0x0214 +#define RADEON_CRTC_EXT_CNTL 0x0054 +# define RADEON_CRTC_VGA_XOVERSCAN (1 << 0) +# define RADEON_VGA_ATI_LINEAR (1 << 3) +# define RADEON_XCRT_CNT_EN (1 << 6) +# define RADEON_CRTC_HSYNC_DIS (1 << 8) +# define RADEON_CRTC_VSYNC_DIS (1 << 9) +# define RADEON_CRTC_DISPLAY_DIS (1 << 10) +# define RADEON_CRTC_SYNC_TRISTAT (1 << 11) +# define RADEON_CRTC_CRT_ON (1 << 15) +#define RADEON_CRTC_EXT_CNTL_DPMS_BYTE 0x0055 +# define RADEON_CRTC_HSYNC_DIS_BYTE (1 << 0) +# define RADEON_CRTC_VSYNC_DIS_BYTE (1 << 1) +# define RADEON_CRTC_DISPLAY_DIS_BYTE (1 << 2) +#define RADEON_CRTC_GEN_CNTL 0x0050 +# define RADEON_CRTC_DBL_SCAN_EN (1 << 0) +# define RADEON_CRTC_INTERLACE_EN (1 << 1) +# define RADEON_CRTC_CSYNC_EN (1 << 4) +# define RADEON_CRTC_ICON_EN (1 << 15) +# define RADEON_CRTC_CUR_EN (1 << 16) +# define RADEON_CRTC_VSTAT_MODE_MASK (3 << 17) +# define RADEON_CRTC_CUR_MODE_MASK (7 << 20) +# define RADEON_CRTC_CUR_MODE_SHIFT 20 +# define RADEON_CRTC_CUR_MODE_MONO 0 +# define RADEON_CRTC_CUR_MODE_24BPP 2 +# define RADEON_CRTC_EXT_DISP_EN (1 << 24) +# define RADEON_CRTC_EN (1 << 25) +# define RADEON_CRTC_DISP_REQ_EN_B (1 << 26) +#define RADEON_CRTC2_GEN_CNTL 0x03f8 +# define RADEON_CRTC2_DBL_SCAN_EN (1 << 0) +# define RADEON_CRTC2_INTERLACE_EN (1 << 1) +# define RADEON_CRTC2_SYNC_TRISTAT (1 << 4) +# define RADEON_CRTC2_HSYNC_TRISTAT (1 << 5) +# define RADEON_CRTC2_VSYNC_TRISTAT (1 << 6) +# define RADEON_CRTC2_CRT2_ON (1 << 7) +# define RADEON_CRTC2_PIX_WIDTH_SHIFT 8 +# define RADEON_CRTC2_PIX_WIDTH_MASK (0xf << 8) +# define RADEON_CRTC2_ICON_EN (1 << 15) +# define RADEON_CRTC2_CUR_EN (1 << 16) +# define RADEON_CRTC2_CUR_MODE_MASK (7 << 20) +# define RADEON_CRTC2_DISP_DIS (1 << 23) +# define RADEON_CRTC2_EN (1 << 25) +# define RADEON_CRTC2_DISP_REQ_EN_B (1 << 26) +# define RADEON_CRTC2_CSYNC_EN (1 << 27) +# define RADEON_CRTC2_HSYNC_DIS (1 << 28) +# define RADEON_CRTC2_VSYNC_DIS (1 << 29) +#define RADEON_CRTC_MORE_CNTL 0x27c +# define RADEON_CRTC_AUTO_HORZ_CENTER_EN (1<<2) +# define RADEON_CRTC_AUTO_VERT_CENTER_EN (1<<3) +# define RADEON_CRTC_H_CUTOFF_ACTIVE_EN (1<<4) +# define RADEON_CRTC_V_CUTOFF_ACTIVE_EN (1<<5) +#define RADEON_CRTC_GUI_TRIG_VLINE 0x0218 +#define RADEON_CRTC_H_SYNC_STRT_WID 0x0204 +# define RADEON_CRTC_H_SYNC_STRT_PIX (0x07 << 0) +# define RADEON_CRTC_H_SYNC_STRT_CHAR (0x3ff << 3) +# define RADEON_CRTC_H_SYNC_STRT_CHAR_SHIFT 3 +# define RADEON_CRTC_H_SYNC_WID (0x3f << 16) +# define RADEON_CRTC_H_SYNC_WID_SHIFT 16 +# define RADEON_CRTC_H_SYNC_POL (1 << 23) +#define RADEON_CRTC2_H_SYNC_STRT_WID 0x0304 +# define RADEON_CRTC2_H_SYNC_STRT_PIX (0x07 << 0) +# define RADEON_CRTC2_H_SYNC_STRT_CHAR (0x3ff << 3) +# define RADEON_CRTC2_H_SYNC_STRT_CHAR_SHIFT 3 +# define RADEON_CRTC2_H_SYNC_WID (0x3f << 16) +# define RADEON_CRTC2_H_SYNC_WID_SHIFT 16 +# define RADEON_CRTC2_H_SYNC_POL (1 << 23) +#define RADEON_CRTC_H_TOTAL_DISP 0x0200 +# define RADEON_CRTC_H_TOTAL (0x03ff << 0) +# define RADEON_CRTC_H_TOTAL_SHIFT 0 +# define RADEON_CRTC_H_DISP (0x01ff << 16) +# define RADEON_CRTC_H_DISP_SHIFT 16 +#define RADEON_CRTC2_H_TOTAL_DISP 0x0300 +# define RADEON_CRTC2_H_TOTAL (0x03ff << 0) +# define RADEON_CRTC2_H_TOTAL_SHIFT 0 +# define RADEON_CRTC2_H_DISP (0x01ff << 16) +# define RADEON_CRTC2_H_DISP_SHIFT 16 + +#define RADEON_CRTC_OFFSET_RIGHT 0x0220 +#define RADEON_CRTC_OFFSET 0x0224 +# define RADEON_CRTC_OFFSET__GUI_TRIG_OFFSET (1<<30) +# define RADEON_CRTC_OFFSET__OFFSET_LOCK (1<<31) + +#define RADEON_CRTC2_OFFSET 0x0324 +# define RADEON_CRTC2_OFFSET__GUI_TRIG_OFFSET (1<<30) +# define RADEON_CRTC2_OFFSET__OFFSET_LOCK (1<<31) +#define RADEON_CRTC_OFFSET_CNTL 0x0228 +# define RADEON_CRTC_TILE_LINE_SHIFT 0 +# define RADEON_CRTC_TILE_LINE_RIGHT_SHIFT 4 +# define R300_CRTC_X_Y_MODE_EN_RIGHT (1 << 6) +# define R300_CRTC_MICRO_TILE_BUFFER_RIGHT_MASK (3 << 7) +# define R300_CRTC_MICRO_TILE_BUFFER_RIGHT_AUTO (0 << 7) +# define R300_CRTC_MICRO_TILE_BUFFER_RIGHT_SINGLE (1 << 7) +# define R300_CRTC_MICRO_TILE_BUFFER_RIGHT_DOUBLE (2 << 7) +# define R300_CRTC_MICRO_TILE_BUFFER_RIGHT_DIS (3 << 7) +# define R300_CRTC_X_Y_MODE_EN (1 << 9) +# define R300_CRTC_MICRO_TILE_BUFFER_MASK (3 << 10) +# define R300_CRTC_MICRO_TILE_BUFFER_AUTO (0 << 10) +# define R300_CRTC_MICRO_TILE_BUFFER_SINGLE (1 << 10) +# define R300_CRTC_MICRO_TILE_BUFFER_DOUBLE (2 << 10) +# define R300_CRTC_MICRO_TILE_BUFFER_DIS (3 << 10) +# define R300_CRTC_MICRO_TILE_EN_RIGHT (1 << 12) +# define R300_CRTC_MICRO_TILE_EN (1 << 13) +# define R300_CRTC_MACRO_TILE_EN_RIGHT (1 << 14) +# define R300_CRTC_MACRO_TILE_EN (1 << 15) +# define RADEON_CRTC_TILE_EN_RIGHT (1 << 14) +# define RADEON_CRTC_TILE_EN (1 << 15) +# define RADEON_CRTC_OFFSET_FLIP_CNTL (1 << 16) +# define RADEON_CRTC_STEREO_OFFSET_EN (1 << 17) +# define RADEON_CRTC_GUI_TRIG_OFFSET_LEFT_EN (1 << 28) +# define RADEON_CRTC_GUI_TRIG_OFFSET_RIGHT_EN (1 << 29) + +#define R300_CRTC_TILE_X0_Y0 0x0350 +#define R300_CRTC2_TILE_X0_Y0 0x0358 + +#define RADEON_CRTC2_OFFSET_CNTL 0x0328 +# define RADEON_CRTC2_OFFSET_FLIP_CNTL (1 << 16) +# define RADEON_CRTC2_TILE_EN (1 << 15) +#define RADEON_CRTC_PITCH 0x022c +# define RADEON_CRTC_PITCH__SHIFT 0 +# define RADEON_CRTC_PITCH__RIGHT_SHIFT 16 + +#define RADEON_CRTC2_PITCH 0x032c +#define RADEON_CRTC_STATUS 0x005c +# define RADEON_CRTC_VBLANK_CUR (1 << 0) +# define RADEON_CRTC_VBLANK_SAVE (1 << 1) +# define RADEON_CRTC_VBLANK_SAVE_CLEAR (1 << 1) +#define RADEON_CRTC2_STATUS 0x03fc +# define RADEON_CRTC2_VBLANK_CUR (1 << 0) +# define RADEON_CRTC2_VBLANK_SAVE (1 << 1) +# define RADEON_CRTC2_VBLANK_SAVE_CLEAR (1 << 1) +#define RADEON_CRTC_V_SYNC_STRT_WID 0x020c +# define RADEON_CRTC_V_SYNC_STRT (0x7ff << 0) +# define RADEON_CRTC_V_SYNC_STRT_SHIFT 0 +# define RADEON_CRTC_V_SYNC_WID (0x1f << 16) +# define RADEON_CRTC_V_SYNC_WID_SHIFT 16 +# define RADEON_CRTC_V_SYNC_POL (1 << 23) +#define RADEON_CRTC2_V_SYNC_STRT_WID 0x030c +# define RADEON_CRTC2_V_SYNC_STRT (0x7ff << 0) +# define RADEON_CRTC2_V_SYNC_STRT_SHIFT 0 +# define RADEON_CRTC2_V_SYNC_WID (0x1f << 16) +# define RADEON_CRTC2_V_SYNC_WID_SHIFT 16 +# define RADEON_CRTC2_V_SYNC_POL (1 << 23) +#define RADEON_CRTC_V_TOTAL_DISP 0x0208 +# define RADEON_CRTC_V_TOTAL (0x07ff << 0) +# define RADEON_CRTC_V_TOTAL_SHIFT 0 +# define RADEON_CRTC_V_DISP (0x07ff << 16) +# define RADEON_CRTC_V_DISP_SHIFT 16 +#define RADEON_CRTC2_V_TOTAL_DISP 0x0308 +# define RADEON_CRTC2_V_TOTAL (0x07ff << 0) +# define RADEON_CRTC2_V_TOTAL_SHIFT 0 +# define RADEON_CRTC2_V_DISP (0x07ff << 16) +# define RADEON_CRTC2_V_DISP_SHIFT 16 +#define RADEON_CRTC_VLINE_CRNT_VLINE 0x0210 +# define RADEON_CRTC_CRNT_VLINE_MASK (0x7ff << 16) +#define RADEON_CRTC2_CRNT_FRAME 0x0314 +#define RADEON_CRTC2_GUI_TRIG_VLINE 0x0318 +#define RADEON_CRTC2_VLINE_CRNT_VLINE 0x0310 +#define RADEON_CRTC8_DATA 0x03d5 /* VGA, 0x3b5 */ +#define RADEON_CRTC8_IDX 0x03d4 /* VGA, 0x3b4 */ +#define RADEON_CUR_CLR0 0x026c +#define RADEON_CUR_CLR1 0x0270 +#define RADEON_CUR_HORZ_VERT_OFF 0x0268 +#define RADEON_CUR_HORZ_VERT_POSN 0x0264 +#define RADEON_CUR_OFFSET 0x0260 +# define RADEON_CUR_LOCK (1 << 31) +#define RADEON_CUR2_CLR0 0x036c +#define RADEON_CUR2_CLR1 0x0370 +#define RADEON_CUR2_HORZ_VERT_OFF 0x0368 +#define RADEON_CUR2_HORZ_VERT_POSN 0x0364 +#define RADEON_CUR2_OFFSET 0x0360 +# define RADEON_CUR2_LOCK (1 << 31) + +#define RADEON_DAC_CNTL 0x0058 +# define RADEON_DAC_RANGE_CNTL (3 << 0) +# define RADEON_DAC_RANGE_CNTL_PS2 (2 << 0) +# define RADEON_DAC_RANGE_CNTL_MASK 0x03 +# define RADEON_DAC_BLANKING (1 << 2) +# define RADEON_DAC_CMP_EN (1 << 3) +# define RADEON_DAC_CMP_OUTPUT (1 << 7) +# define RADEON_DAC_8BIT_EN (1 << 8) +# define RADEON_DAC_TVO_EN (1 << 10) +# define RADEON_DAC_VGA_ADR_EN (1 << 13) +# define RADEON_DAC_PDWN (1 << 15) +# define RADEON_DAC_MASK_ALL (0xff << 24) +#define RADEON_DAC_CNTL2 0x007c +# define RADEON_DAC2_TV_CLK_SEL (0 << 1) +# define RADEON_DAC2_DAC_CLK_SEL (1 << 0) +# define RADEON_DAC2_DAC2_CLK_SEL (1 << 1) +# define RADEON_DAC2_PALETTE_ACC_CTL (1 << 5) +# define RADEON_DAC2_CMP_EN (1 << 7) +# define RADEON_DAC2_CMP_OUT_R (1 << 8) +# define RADEON_DAC2_CMP_OUT_G (1 << 9) +# define RADEON_DAC2_CMP_OUT_B (1 << 10) +# define RADEON_DAC2_CMP_OUTPUT (1 << 11) +#define RADEON_DAC_EXT_CNTL 0x0280 +# define RADEON_DAC2_FORCE_BLANK_OFF_EN (1 << 0) +# define RADEON_DAC2_FORCE_DATA_EN (1 << 1) +# define RADEON_DAC_FORCE_BLANK_OFF_EN (1 << 4) +# define RADEON_DAC_FORCE_DATA_EN (1 << 5) +# define RADEON_DAC_FORCE_DATA_SEL_MASK (3 << 6) +# define RADEON_DAC_FORCE_DATA_SEL_R (0 << 6) +# define RADEON_DAC_FORCE_DATA_SEL_G (1 << 6) +# define RADEON_DAC_FORCE_DATA_SEL_B (2 << 6) +# define RADEON_DAC_FORCE_DATA_SEL_RGB (3 << 6) +# define RADEON_DAC_FORCE_DATA_MASK 0x0003ff00 +# define RADEON_DAC_FORCE_DATA_SHIFT 8 +#define RADEON_DAC_MACRO_CNTL 0x0d04 +# define RADEON_DAC_PDWN_R (1 << 16) +# define RADEON_DAC_PDWN_G (1 << 17) +# define RADEON_DAC_PDWN_B (1 << 18) +#define RADEON_DISP_PWR_MAN 0x0d08 +# define RADEON_DISP_PWR_MAN_D3_CRTC_EN (1 << 0) +# define RADEON_DISP_PWR_MAN_D3_CRTC2_EN (1 << 4) +# define RADEON_DISP_PWR_MAN_DPMS_ON (0 << 8) +# define RADEON_DISP_PWR_MAN_DPMS_STANDBY (1 << 8) +# define RADEON_DISP_PWR_MAN_DPMS_SUSPEND (2 << 8) +# define RADEON_DISP_PWR_MAN_DPMS_OFF (3 << 8) +# define RADEON_DISP_D3_RST (1 << 16) +# define RADEON_DISP_D3_REG_RST (1 << 17) +# define RADEON_DISP_D3_GRPH_RST (1 << 18) +# define RADEON_DISP_D3_SUBPIC_RST (1 << 19) +# define RADEON_DISP_D3_OV0_RST (1 << 20) +# define RADEON_DISP_D1D2_GRPH_RST (1 << 21) +# define RADEON_DISP_D1D2_SUBPIC_RST (1 << 22) +# define RADEON_DISP_D1D2_OV0_RST (1 << 23) +# define RADEON_DIG_TMDS_ENABLE_RST (1 << 24) +# define RADEON_TV_ENABLE_RST (1 << 25) +# define RADEON_AUTO_PWRUP_EN (1 << 26) +#define RADEON_TV_DAC_CNTL 0x088c +# define RADEON_TV_DAC_NBLANK (1 << 0) +# define RADEON_TV_DAC_NHOLD (1 << 1) +# define RADEON_TV_DAC_PEDESTAL (1 << 2) +# define RADEON_TV_MONITOR_DETECT_EN (1 << 4) +# define RADEON_TV_DAC_CMPOUT (1 << 5) +# define RADEON_TV_DAC_STD_MASK (3 << 8) +# define RADEON_TV_DAC_STD_PAL (0 << 8) +# define RADEON_TV_DAC_STD_NTSC (1 << 8) +# define RADEON_TV_DAC_STD_PS2 (2 << 8) +# define RADEON_TV_DAC_STD_RS343 (3 << 8) +# define RADEON_TV_DAC_BGSLEEP (1 << 6) +# define RADEON_TV_DAC_BGADJ_MASK (0xf << 16) +# define RADEON_TV_DAC_BGADJ_SHIFT 16 +# define RADEON_TV_DAC_DACADJ_MASK (0xf << 20) +# define RADEON_TV_DAC_DACADJ_SHIFT 20 +# define RADEON_TV_DAC_RDACPD (1 << 24) +# define RADEON_TV_DAC_GDACPD (1 << 25) +# define RADEON_TV_DAC_BDACPD (1 << 26) +# define RADEON_TV_DAC_RDACDET (1 << 29) +# define RADEON_TV_DAC_GDACDET (1 << 30) +# define RADEON_TV_DAC_BDACDET (1 << 31) +# define R420_TV_DAC_DACADJ_MASK (0x1f << 20) +# define R420_TV_DAC_RDACPD (1 << 25) +# define R420_TV_DAC_GDACPD (1 << 26) +# define R420_TV_DAC_BDACPD (1 << 27) +# define R420_TV_DAC_TVENABLE (1 << 28) +#define RADEON_DISP_HW_DEBUG 0x0d14 +# define RADEON_CRT2_DISP1_SEL (1 << 5) +#define RADEON_DISP_OUTPUT_CNTL 0x0d64 +# define RADEON_DISP_DAC_SOURCE_MASK 0x03 +# define RADEON_DISP_DAC2_SOURCE_MASK 0x0c +# define RADEON_DISP_DAC_SOURCE_CRTC2 0x01 +# define RADEON_DISP_DAC_SOURCE_RMX 0x02 +# define RADEON_DISP_DAC_SOURCE_LTU 0x03 +# define RADEON_DISP_DAC2_SOURCE_CRTC2 0x04 +# define RADEON_DISP_TVDAC_SOURCE_MASK (0x03 << 2) +# define RADEON_DISP_TVDAC_SOURCE_CRTC 0x0 +# define RADEON_DISP_TVDAC_SOURCE_CRTC2 (0x01 << 2) +# define RADEON_DISP_TVDAC_SOURCE_RMX (0x02 << 2) +# define RADEON_DISP_TVDAC_SOURCE_LTU (0x03 << 2) +# define RADEON_DISP_TRANS_MATRIX_MASK (0x03 << 4) +# define RADEON_DISP_TRANS_MATRIX_ALPHA_MSB (0x00 << 4) +# define RADEON_DISP_TRANS_MATRIX_GRAPHICS (0x01 << 4) +# define RADEON_DISP_TRANS_MATRIX_VIDEO (0x02 << 4) +# define RADEON_DISP_TV_SOURCE_CRTC (1 << 16) /* crtc1 or crtc2 */ +# define RADEON_DISP_TV_SOURCE_LTU (0 << 16) /* linear transform unit */ +#define RADEON_DISP_TV_OUT_CNTL 0x0d6c +# define RADEON_DISP_TV_PATH_SRC_CRTC2 (1 << 16) +# define RADEON_DISP_TV_PATH_SRC_CRTC1 (0 << 16) +#define RADEON_DAC_CRC_SIG 0x02cc +#define RADEON_DAC_DATA 0x03c9 /* VGA */ +#define RADEON_DAC_MASK 0x03c6 /* VGA */ +#define RADEON_DAC_R_INDEX 0x03c7 /* VGA */ +#define RADEON_DAC_W_INDEX 0x03c8 /* VGA */ +#define RADEON_DDA_CONFIG 0x02e0 +#define RADEON_DDA_ON_OFF 0x02e4 +#define RADEON_DEFAULT_OFFSET 0x16e0 +#define RADEON_DEFAULT_PITCH 0x16e4 +#define RADEON_DEFAULT_SC_BOTTOM_RIGHT 0x16e8 +# define RADEON_DEFAULT_SC_RIGHT_MAX (0x1fff << 0) +# define RADEON_DEFAULT_SC_BOTTOM_MAX (0x1fff << 16) +#define RADEON_DESTINATION_3D_CLR_CMP_VAL 0x1820 +#define RADEON_DESTINATION_3D_CLR_CMP_MSK 0x1824 +#define RADEON_DEVICE_ID 0x0f02 /* PCI */ +#define RADEON_DISP_MISC_CNTL 0x0d00 +# define RADEON_SOFT_RESET_GRPH_PP (1 << 0) +#define RADEON_DISP_MERGE_CNTL 0x0d60 +# define RADEON_DISP_ALPHA_MODE_MASK 0x03 +# define RADEON_DISP_ALPHA_MODE_KEY 0 +# define RADEON_DISP_ALPHA_MODE_PER_PIXEL 1 +# define RADEON_DISP_ALPHA_MODE_GLOBAL 2 +# define RADEON_DISP_RGB_OFFSET_EN (1 << 8) +# define RADEON_DISP_GRPH_ALPHA_MASK (0xff << 16) +# define RADEON_DISP_OV0_ALPHA_MASK (0xff << 24) +# define RADEON_DISP_LIN_TRANS_BYPASS (0x01 << 9) +#define RADEON_DISP2_MERGE_CNTL 0x0d68 +# define RADEON_DISP2_RGB_OFFSET_EN (1 << 8) +#define RADEON_DISP_LIN_TRANS_GRPH_A 0x0d80 +#define RADEON_DISP_LIN_TRANS_GRPH_B 0x0d84 +#define RADEON_DISP_LIN_TRANS_GRPH_C 0x0d88 +#define RADEON_DISP_LIN_TRANS_GRPH_D 0x0d8c +#define RADEON_DISP_LIN_TRANS_GRPH_E 0x0d90 +#define RADEON_DISP_LIN_TRANS_GRPH_F 0x0d98 +#define RADEON_DP_BRUSH_BKGD_CLR 0x1478 +#define RADEON_DP_BRUSH_FRGD_CLR 0x147c +#define RADEON_DP_CNTL 0x16c0 +# define RADEON_DST_X_LEFT_TO_RIGHT (1 << 0) +# define RADEON_DST_Y_TOP_TO_BOTTOM (1 << 1) +# define RADEON_DP_DST_TILE_LINEAR (0 << 3) +# define RADEON_DP_DST_TILE_MACRO (1 << 3) +# define RADEON_DP_DST_TILE_MICRO (2 << 3) +# define RADEON_DP_DST_TILE_BOTH (3 << 3) +#define RADEON_DP_CNTL_XDIR_YDIR_YMAJOR 0x16d0 +# define RADEON_DST_Y_MAJOR (1 << 2) +# define RADEON_DST_Y_DIR_TOP_TO_BOTTOM (1 << 15) +# define RADEON_DST_X_DIR_LEFT_TO_RIGHT (1 << 31) +#define RADEON_DP_DATATYPE 0x16c4 +# define RADEON_HOST_BIG_ENDIAN_EN (1 << 29) +#define RADEON_DP_GUI_MASTER_CNTL 0x146c +# define RADEON_GMC_SRC_PITCH_OFFSET_CNTL (1 << 0) +# define RADEON_GMC_DST_PITCH_OFFSET_CNTL (1 << 1) +# define RADEON_GMC_SRC_CLIPPING (1 << 2) +# define RADEON_GMC_DST_CLIPPING (1 << 3) +# define RADEON_GMC_BRUSH_DATATYPE_MASK (0x0f << 4) +# define RADEON_GMC_BRUSH_8X8_MONO_FG_BG (0 << 4) +# define RADEON_GMC_BRUSH_8X8_MONO_FG_LA (1 << 4) +# define RADEON_GMC_BRUSH_1X8_MONO_FG_BG (4 << 4) +# define RADEON_GMC_BRUSH_1X8_MONO_FG_LA (5 << 4) +# define RADEON_GMC_BRUSH_32x1_MONO_FG_BG (6 << 4) +# define RADEON_GMC_BRUSH_32x1_MONO_FG_LA (7 << 4) +# define RADEON_GMC_BRUSH_32x32_MONO_FG_BG (8 << 4) +# define RADEON_GMC_BRUSH_32x32_MONO_FG_LA (9 << 4) +# define RADEON_GMC_BRUSH_8x8_COLOR (10 << 4) +# define RADEON_GMC_BRUSH_1X8_COLOR (12 << 4) +# define RADEON_GMC_BRUSH_SOLID_COLOR (13 << 4) +# define RADEON_GMC_BRUSH_NONE (15 << 4) +# define RADEON_GMC_DST_8BPP_CI (2 << 8) +# define RADEON_GMC_DST_15BPP (3 << 8) +# define RADEON_GMC_DST_16BPP (4 << 8) +# define RADEON_GMC_DST_24BPP (5 << 8) +# define RADEON_GMC_DST_32BPP (6 << 8) +# define RADEON_GMC_DST_8BPP_RGB (7 << 8) +# define RADEON_GMC_DST_Y8 (8 << 8) +# define RADEON_GMC_DST_RGB8 (9 << 8) +# define RADEON_GMC_DST_VYUY (11 << 8) +# define RADEON_GMC_DST_YVYU (12 << 8) +# define RADEON_GMC_DST_AYUV444 (14 << 8) +# define RADEON_GMC_DST_ARGB4444 (15 << 8) +# define RADEON_GMC_DST_DATATYPE_MASK (0x0f << 8) +# define RADEON_GMC_DST_DATATYPE_SHIFT 8 +# define RADEON_GMC_SRC_DATATYPE_MASK (3 << 12) +# define RADEON_GMC_SRC_DATATYPE_MONO_FG_BG (0 << 12) +# define RADEON_GMC_SRC_DATATYPE_MONO_FG_LA (1 << 12) +# define RADEON_GMC_SRC_DATATYPE_COLOR (3 << 12) +# define RADEON_GMC_BYTE_PIX_ORDER (1 << 14) +# define RADEON_GMC_BYTE_MSB_TO_LSB (0 << 14) +# define RADEON_GMC_BYTE_LSB_TO_MSB (1 << 14) +# define RADEON_GMC_CONVERSION_TEMP (1 << 15) +# define RADEON_GMC_CONVERSION_TEMP_6500 (0 << 15) +# define RADEON_GMC_CONVERSION_TEMP_9300 (1 << 15) +# define RADEON_GMC_ROP3_MASK (0xff << 16) +# define RADEON_DP_SRC_SOURCE_MASK (7 << 24) +# define RADEON_DP_SRC_SOURCE_MEMORY (2 << 24) +# define RADEON_DP_SRC_SOURCE_HOST_DATA (3 << 24) +# define RADEON_GMC_3D_FCN_EN (1 << 27) +# define RADEON_GMC_CLR_CMP_CNTL_DIS (1 << 28) +# define RADEON_GMC_AUX_CLIP_DIS (1 << 29) +# define RADEON_GMC_WR_MSK_DIS (1 << 30) +# define RADEON_GMC_LD_BRUSH_Y_X (1 << 31) +# define RADEON_ROP3_ZERO 0x00000000 +# define RADEON_ROP3_DSa 0x00880000 +# define RADEON_ROP3_SDna 0x00440000 +# define RADEON_ROP3_S 0x00cc0000 +# define RADEON_ROP3_DSna 0x00220000 +# define RADEON_ROP3_D 0x00aa0000 +# define RADEON_ROP3_DSx 0x00660000 +# define RADEON_ROP3_DSo 0x00ee0000 +# define RADEON_ROP3_DSon 0x00110000 +# define RADEON_ROP3_DSxn 0x00990000 +# define RADEON_ROP3_Dn 0x00550000 +# define RADEON_ROP3_SDno 0x00dd0000 +# define RADEON_ROP3_Sn 0x00330000 +# define RADEON_ROP3_DSno 0x00bb0000 +# define RADEON_ROP3_DSan 0x00770000 +# define RADEON_ROP3_ONE 0x00ff0000 +# define RADEON_ROP3_DPa 0x00a00000 +# define RADEON_ROP3_PDna 0x00500000 +# define RADEON_ROP3_P 0x00f00000 +# define RADEON_ROP3_DPna 0x000a0000 +# define RADEON_ROP3_D 0x00aa0000 +# define RADEON_ROP3_DPx 0x005a0000 +# define RADEON_ROP3_DPo 0x00fa0000 +# define RADEON_ROP3_DPon 0x00050000 +# define RADEON_ROP3_PDxn 0x00a50000 +# define RADEON_ROP3_PDno 0x00f50000 +# define RADEON_ROP3_Pn 0x000f0000 +# define RADEON_ROP3_DPno 0x00af0000 +# define RADEON_ROP3_DPan 0x005f0000 +#define RADEON_DP_GUI_MASTER_CNTL_C 0x1c84 +#define RADEON_DP_MIX 0x16c8 +#define RADEON_DP_SRC_BKGD_CLR 0x15dc +#define RADEON_DP_SRC_FRGD_CLR 0x15d8 +#define RADEON_DP_WRITE_MASK 0x16cc +#define RADEON_DST_BRES_DEC 0x1630 +#define RADEON_DST_BRES_ERR 0x1628 +#define RADEON_DST_BRES_INC 0x162c +#define RADEON_DST_BRES_LNTH 0x1634 +#define RADEON_DST_BRES_LNTH_SUB 0x1638 +#define RADEON_DST_HEIGHT 0x1410 +#define RADEON_DST_HEIGHT_WIDTH 0x143c +#define RADEON_DST_HEIGHT_WIDTH_8 0x158c +#define RADEON_DST_HEIGHT_WIDTH_BW 0x15b4 +#define RADEON_DST_HEIGHT_Y 0x15a0 +#define RADEON_DST_LINE_START 0x1600 +#define RADEON_DST_LINE_END 0x1604 +#define RADEON_DST_LINE_PATCOUNT 0x1608 +# define RADEON_BRES_CNTL_SHIFT 8 +#define RADEON_DST_OFFSET 0x1404 +#define RADEON_DST_PITCH 0x1408 +#define RADEON_DST_PITCH_OFFSET 0x142c +#define RADEON_DST_PITCH_OFFSET_C 0x1c80 +# define RADEON_PITCH_SHIFT 21 +# define RADEON_DST_TILE_LINEAR (0 << 30) +# define RADEON_DST_TILE_MACRO (1 << 30) +# define RADEON_DST_TILE_MICRO (2 << 30) +# define RADEON_DST_TILE_BOTH (3 << 30) +#define RADEON_DST_WIDTH 0x140c +#define RADEON_DST_WIDTH_HEIGHT 0x1598 +#define RADEON_DST_WIDTH_X 0x1588 +#define RADEON_DST_WIDTH_X_INCY 0x159c +#define RADEON_DST_X 0x141c +#define RADEON_DST_X_SUB 0x15a4 +#define RADEON_DST_X_Y 0x1594 +#define RADEON_DST_Y 0x1420 +#define RADEON_DST_Y_SUB 0x15a8 +#define RADEON_DST_Y_X 0x1438 + +#define RADEON_FCP_CNTL 0x0910 +# define RADEON_FCP0_SRC_PCICLK 0 +# define RADEON_FCP0_SRC_PCLK 1 +# define RADEON_FCP0_SRC_PCLKb 2 +# define RADEON_FCP0_SRC_HREF 3 +# define RADEON_FCP0_SRC_GND 4 +# define RADEON_FCP0_SRC_HREFb 5 +#define RADEON_FLUSH_1 0x1704 +#define RADEON_FLUSH_2 0x1708 +#define RADEON_FLUSH_3 0x170c +#define RADEON_FLUSH_4 0x1710 +#define RADEON_FLUSH_5 0x1714 +#define RADEON_FLUSH_6 0x1718 +#define RADEON_FLUSH_7 0x171c +#define RADEON_FOG_3D_TABLE_START 0x1810 +#define RADEON_FOG_3D_TABLE_END 0x1814 +#define RADEON_FOG_3D_TABLE_DENSITY 0x181c +#define RADEON_FOG_TABLE_INDEX 0x1a14 +#define RADEON_FOG_TABLE_DATA 0x1a18 +#define RADEON_FP_CRTC_H_TOTAL_DISP 0x0250 +#define RADEON_FP_CRTC_V_TOTAL_DISP 0x0254 +# define RADEON_FP_CRTC_H_TOTAL_MASK 0x000003ff +# define RADEON_FP_CRTC_H_DISP_MASK 0x01ff0000 +# define RADEON_FP_CRTC_V_TOTAL_MASK 0x00000fff +# define RADEON_FP_CRTC_V_DISP_MASK 0x0fff0000 +# define RADEON_FP_H_SYNC_STRT_CHAR_MASK 0x00001ff8 +# define RADEON_FP_H_SYNC_WID_MASK 0x003f0000 +# define RADEON_FP_V_SYNC_STRT_MASK 0x00000fff +# define RADEON_FP_V_SYNC_WID_MASK 0x001f0000 +# define RADEON_FP_CRTC_H_TOTAL_SHIFT 0x00000000 +# define RADEON_FP_CRTC_H_DISP_SHIFT 0x00000010 +# define RADEON_FP_CRTC_V_TOTAL_SHIFT 0x00000000 +# define RADEON_FP_CRTC_V_DISP_SHIFT 0x00000010 +# define RADEON_FP_H_SYNC_STRT_CHAR_SHIFT 0x00000003 +# define RADEON_FP_H_SYNC_WID_SHIFT 0x00000010 +# define RADEON_FP_V_SYNC_STRT_SHIFT 0x00000000 +# define RADEON_FP_V_SYNC_WID_SHIFT 0x00000010 +#define RADEON_FP_GEN_CNTL 0x0284 +# define RADEON_FP_FPON (1 << 0) +# define RADEON_FP_BLANK_EN (1 << 1) +# define RADEON_FP_TMDS_EN (1 << 2) +# define RADEON_FP_PANEL_FORMAT (1 << 3) +# define RADEON_FP_EN_TMDS (1 << 7) +# define RADEON_FP_DETECT_SENSE (1 << 8) +# define RADEON_FP_DETECT_INT_POL (1 << 9) +# define R200_FP_SOURCE_SEL_MASK (3 << 10) +# define R200_FP_SOURCE_SEL_CRTC1 (0 << 10) +# define R200_FP_SOURCE_SEL_CRTC2 (1 << 10) +# define R200_FP_SOURCE_SEL_RMX (2 << 10) +# define R200_FP_SOURCE_SEL_TRANS (3 << 10) +# define RADEON_FP_SEL_CRTC1 (0 << 13) +# define RADEON_FP_SEL_CRTC2 (1 << 13) +# define R300_HPD_SEL(x) ((x) << 13) +# define RADEON_FP_CRTC_DONT_SHADOW_HPAR (1 << 15) +# define RADEON_FP_CRTC_DONT_SHADOW_VPAR (1 << 16) +# define RADEON_FP_CRTC_DONT_SHADOW_HEND (1 << 17) +# define RADEON_FP_CRTC_USE_SHADOW_VEND (1 << 18) +# define RADEON_FP_RMX_HVSYNC_CONTROL_EN (1 << 20) +# define RADEON_FP_DFP_SYNC_SEL (1 << 21) +# define RADEON_FP_CRTC_LOCK_8DOT (1 << 22) +# define RADEON_FP_CRT_SYNC_SEL (1 << 23) +# define RADEON_FP_USE_SHADOW_EN (1 << 24) +# define RADEON_FP_CRT_SYNC_ALT (1 << 26) +#define RADEON_FP2_GEN_CNTL 0x0288 +# define RADEON_FP2_BLANK_EN (1 << 1) +# define RADEON_FP2_ON (1 << 2) +# define RADEON_FP2_PANEL_FORMAT (1 << 3) +# define RADEON_FP2_DETECT_SENSE (1 << 8) +# define RADEON_FP2_DETECT_INT_POL (1 << 9) +# define R200_FP2_SOURCE_SEL_MASK (3 << 10) +# define R200_FP2_SOURCE_SEL_CRTC1 (0 << 10) +# define R200_FP2_SOURCE_SEL_CRTC2 (1 << 10) +# define R200_FP2_SOURCE_SEL_RMX (2 << 10) +# define R200_FP2_SOURCE_SEL_TRANS_UNIT (3 << 10) +# define RADEON_FP2_SRC_SEL_MASK (3 << 13) +# define RADEON_FP2_SRC_SEL_CRTC2 (1 << 13) +# define RADEON_FP2_FP_POL (1 << 16) +# define RADEON_FP2_LP_POL (1 << 17) +# define RADEON_FP2_SCK_POL (1 << 18) +# define RADEON_FP2_LCD_CNTL_MASK (7 << 19) +# define RADEON_FP2_PAD_FLOP_EN (1 << 22) +# define RADEON_FP2_CRC_EN (1 << 23) +# define RADEON_FP2_CRC_READ_EN (1 << 24) +# define RADEON_FP2_DVO_EN (1 << 25) +# define RADEON_FP2_DVO_RATE_SEL_SDR (1 << 26) +# define R200_FP2_DVO_RATE_SEL_SDR (1 << 27) +# define R300_FP2_DVO_CLOCK_MODE_SINGLE (1 << 28) +# define R300_FP2_DVO_DUAL_CHANNEL_EN (1 << 29) +#define RADEON_FP_H_SYNC_STRT_WID 0x02c4 +#define RADEON_FP_H2_SYNC_STRT_WID 0x03c4 +#define RADEON_FP_HORZ_STRETCH 0x028c +#define RADEON_FP_HORZ2_STRETCH 0x038c +# define RADEON_HORZ_STRETCH_RATIO_MASK 0xffff +# define RADEON_HORZ_STRETCH_RATIO_MAX 4096 +# define RADEON_HORZ_PANEL_SIZE (0x1ff << 16) +# define RADEON_HORZ_PANEL_SHIFT 16 +# define RADEON_HORZ_STRETCH_PIXREP (0 << 25) +# define RADEON_HORZ_STRETCH_BLEND (1 << 26) +# define RADEON_HORZ_STRETCH_ENABLE (1 << 25) +# define RADEON_HORZ_AUTO_RATIO (1 << 27) +# define RADEON_HORZ_FP_LOOP_STRETCH (0x7 << 28) +# define RADEON_HORZ_AUTO_RATIO_INC (1 << 31) +#define RADEON_FP_HORZ_VERT_ACTIVE 0x0278 +#define RADEON_FP_V_SYNC_STRT_WID 0x02c8 +#define RADEON_FP_VERT_STRETCH 0x0290 +#define RADEON_FP_V2_SYNC_STRT_WID 0x03c8 +#define RADEON_FP_VERT2_STRETCH 0x0390 +# define RADEON_VERT_PANEL_SIZE (0xfff << 12) +# define RADEON_VERT_PANEL_SHIFT 12 +# define RADEON_VERT_STRETCH_RATIO_MASK 0xfff +# define RADEON_VERT_STRETCH_RATIO_SHIFT 0 +# define RADEON_VERT_STRETCH_RATIO_MAX 4096 +# define RADEON_VERT_STRETCH_ENABLE (1 << 25) +# define RADEON_VERT_STRETCH_LINEREP (0 << 26) +# define RADEON_VERT_STRETCH_BLEND (1 << 26) +# define RADEON_VERT_AUTO_RATIO_EN (1 << 27) +# define RADEON_VERT_AUTO_RATIO_INC (1 << 31) +# define RADEON_VERT_STRETCH_RESERVED 0x71000000 +#define RS400_FP_2ND_GEN_CNTL 0x0384 +# define RS400_FP_2ND_ON (1 << 0) +# define RS400_FP_2ND_BLANK_EN (1 << 1) +# define RS400_TMDS_2ND_EN (1 << 2) +# define RS400_PANEL_FORMAT_2ND (1 << 3) +# define RS400_FP_2ND_EN_TMDS (1 << 7) +# define RS400_FP_2ND_DETECT_SENSE (1 << 8) +# define RS400_FP_2ND_SOURCE_SEL_MASK (3 << 10) +# define RS400_FP_2ND_SOURCE_SEL_CRTC1 (0 << 10) +# define RS400_FP_2ND_SOURCE_SEL_CRTC2 (1 << 10) +# define RS400_FP_2ND_SOURCE_SEL_RMX (2 << 10) +# define RS400_FP_2ND_DETECT_EN (1 << 12) +# define RS400_HPD_2ND_SEL (1 << 13) +#define RS400_FP2_2_GEN_CNTL 0x0388 +# define RS400_FP2_2_BLANK_EN (1 << 1) +# define RS400_FP2_2_ON (1 << 2) +# define RS400_FP2_2_PANEL_FORMAT (1 << 3) +# define RS400_FP2_2_DETECT_SENSE (1 << 8) +# define RS400_FP2_2_SOURCE_SEL_MASK (3 << 10) +# define RS400_FP2_2_SOURCE_SEL_CRTC1 (0 << 10) +# define RS400_FP2_2_SOURCE_SEL_CRTC2 (1 << 10) +# define RS400_FP2_2_SOURCE_SEL_RMX (2 << 10) +# define RS400_FP2_2_DVO2_EN (1 << 25) +#define RS400_TMDS2_CNTL 0x0394 +#define RS400_TMDS2_TRANSMITTER_CNTL 0x03a4 +# define RS400_TMDS2_PLLEN (1 << 0) +# define RS400_TMDS2_PLLRST (1 << 1) + +#define RADEON_GEN_INT_CNTL 0x0040 +# define RADEON_CRTC_VBLANK_MASK (1 << 0) +# define RADEON_FP_DETECT_MASK (1 << 4) +# define RADEON_CRTC2_VBLANK_MASK (1 << 9) +# define RADEON_FP2_DETECT_MASK (1 << 10) +# define RADEON_GUI_IDLE_MASK (1 << 19) +# define RADEON_SW_INT_ENABLE (1 << 25) +#define RADEON_GEN_INT_STATUS 0x0044 +# define AVIVO_DISPLAY_INT_STATUS (1 << 0) +# define RADEON_CRTC_VBLANK_STAT (1 << 0) +# define RADEON_CRTC_VBLANK_STAT_ACK (1 << 0) +# define RADEON_FP_DETECT_STAT (1 << 4) +# define RADEON_FP_DETECT_STAT_ACK (1 << 4) +# define RADEON_CRTC2_VBLANK_STAT (1 << 9) +# define RADEON_CRTC2_VBLANK_STAT_ACK (1 << 9) +# define RADEON_FP2_DETECT_STAT (1 << 10) +# define RADEON_FP2_DETECT_STAT_ACK (1 << 10) +# define RADEON_GUI_IDLE_STAT (1 << 19) +# define RADEON_GUI_IDLE_STAT_ACK (1 << 19) +# define RADEON_SW_INT_FIRE (1 << 26) +# define RADEON_SW_INT_TEST (1 << 25) +# define RADEON_SW_INT_TEST_ACK (1 << 25) +#define RADEON_GENENB 0x03c3 /* VGA */ +#define RADEON_GENFC_RD 0x03ca /* VGA */ +#define RADEON_GENFC_WT 0x03da /* VGA, 0x03ba */ +#define RADEON_GENMO_RD 0x03cc /* VGA */ +#define RADEON_GENMO_WT 0x03c2 /* VGA */ +#define RADEON_GENS0 0x03c2 /* VGA */ +#define RADEON_GENS1 0x03da /* VGA, 0x03ba */ +#define RADEON_GPIO_MONID 0x0068 /* DDC interface via I2C */ /* DDC3 */ +#define RADEON_GPIO_MONIDB 0x006c +#define RADEON_GPIO_CRT2_DDC 0x006c +#define RADEON_GPIO_DVI_DDC 0x0064 /* DDC2 */ +#define RADEON_GPIO_VGA_DDC 0x0060 /* DDC1 */ +# define RADEON_GPIO_A_0 (1 << 0) +# define RADEON_GPIO_A_1 (1 << 1) +# define RADEON_GPIO_Y_0 (1 << 8) +# define RADEON_GPIO_Y_1 (1 << 9) +# define RADEON_GPIO_Y_SHIFT_0 8 +# define RADEON_GPIO_Y_SHIFT_1 9 +# define RADEON_GPIO_EN_0 (1 << 16) +# define RADEON_GPIO_EN_1 (1 << 17) +# define RADEON_GPIO_MASK_0 (1 << 24) /*??*/ +# define RADEON_GPIO_MASK_1 (1 << 25) /*??*/ +#define RADEON_GRPH8_DATA 0x03cf /* VGA */ +#define RADEON_GRPH8_IDX 0x03ce /* VGA */ +#define RADEON_GUI_SCRATCH_REG0 0x15e0 +#define RADEON_GUI_SCRATCH_REG1 0x15e4 +#define RADEON_GUI_SCRATCH_REG2 0x15e8 +#define RADEON_GUI_SCRATCH_REG3 0x15ec +#define RADEON_GUI_SCRATCH_REG4 0x15f0 +#define RADEON_GUI_SCRATCH_REG5 0x15f4 + +#define RADEON_HEADER 0x0f0e /* PCI */ +#define RADEON_HOST_DATA0 0x17c0 +#define RADEON_HOST_DATA1 0x17c4 +#define RADEON_HOST_DATA2 0x17c8 +#define RADEON_HOST_DATA3 0x17cc +#define RADEON_HOST_DATA4 0x17d0 +#define RADEON_HOST_DATA5 0x17d4 +#define RADEON_HOST_DATA6 0x17d8 +#define RADEON_HOST_DATA7 0x17dc +#define RADEON_HOST_DATA_LAST 0x17e0 +#define RADEON_HOST_PATH_CNTL 0x0130 +# define RADEON_HP_LIN_RD_CACHE_DIS (1 << 24) +# define RADEON_HDP_READ_BUFFER_INVALIDATE (1 << 27) +# define RADEON_HDP_SOFT_RESET (1 << 26) +# define RADEON_HDP_APER_CNTL (1 << 23) +#define RADEON_HTOTAL_CNTL 0x0009 /* PLL */ +# define RADEON_HTOT_CNTL_VGA_EN (1 << 28) +#define RADEON_HTOTAL2_CNTL 0x002e /* PLL */ + + /* Multimedia I2C bus */ +#define RADEON_I2C_CNTL_0 0x0090 +# define RADEON_I2C_DONE (1 << 0) +# define RADEON_I2C_NACK (1 << 1) +# define RADEON_I2C_HALT (1 << 2) +# define RADEON_I2C_SOFT_RST (1 << 5) +# define RADEON_I2C_DRIVE_EN (1 << 6) +# define RADEON_I2C_DRIVE_SEL (1 << 7) +# define RADEON_I2C_START (1 << 8) +# define RADEON_I2C_STOP (1 << 9) +# define RADEON_I2C_RECEIVE (1 << 10) +# define RADEON_I2C_ABORT (1 << 11) +# define RADEON_I2C_GO (1 << 12) +# define RADEON_I2C_PRESCALE_SHIFT 16 +#define RADEON_I2C_CNTL_1 0x0094 +# define RADEON_I2C_DATA_COUNT_SHIFT 0 +# define RADEON_I2C_ADDR_COUNT_SHIFT 4 +# define RADEON_I2C_INTRA_BYTE_DELAY_SHIFT 8 +# define RADEON_I2C_SEL (1 << 16) +# define RADEON_I2C_EN (1 << 17) +# define RADEON_I2C_TIME_LIMIT_SHIFT 24 +#define RADEON_I2C_DATA 0x0098 + +#define RADEON_DVI_I2C_CNTL_0 0x02e0 +# define R200_DVI_I2C_PIN_SEL(x) ((x) << 3) +# define R200_SEL_DDC1 0 /* depends on asic */ +# define R200_SEL_DDC2 1 /* depends on asic */ +# define R200_SEL_DDC3 2 /* depends on asic */ +# define RADEON_SW_WANTS_TO_USE_DVI_I2C (1 << 13) +# define RADEON_SW_CAN_USE_DVI_I2C (1 << 13) +# define RADEON_SW_DONE_USING_DVI_I2C (1 << 14) +# define RADEON_HW_NEEDS_DVI_I2C (1 << 14) +# define RADEON_ABORT_HW_DVI_I2C (1 << 15) +# define RADEON_HW_USING_DVI_I2C (1 << 15) +#define RADEON_DVI_I2C_CNTL_1 0x02e4 +#define RADEON_DVI_I2C_DATA 0x02e8 + +#define RADEON_INTERRUPT_LINE 0x0f3c /* PCI */ +#define RADEON_INTERRUPT_PIN 0x0f3d /* PCI */ +#define RADEON_IO_BASE 0x0f14 /* PCI */ + +#define RADEON_LATENCY 0x0f0d /* PCI */ +#define RADEON_LEAD_BRES_DEC 0x1608 +#define RADEON_LEAD_BRES_LNTH 0x161c +#define RADEON_LEAD_BRES_LNTH_SUB 0x1624 +#define RADEON_LVDS_GEN_CNTL 0x02d0 +# define RADEON_LVDS_ON (1 << 0) +# define RADEON_LVDS_DISPLAY_DIS (1 << 1) +# define RADEON_LVDS_PANEL_TYPE (1 << 2) +# define RADEON_LVDS_PANEL_FORMAT (1 << 3) +# define RADEON_LVDS_NO_FM (0 << 4) +# define RADEON_LVDS_2_GREY (1 << 4) +# define RADEON_LVDS_4_GREY (2 << 4) +# define RADEON_LVDS_RST_FM (1 << 6) +# define RADEON_LVDS_EN (1 << 7) +# define RADEON_LVDS_BL_MOD_LEVEL_SHIFT 8 +# define RADEON_LVDS_BL_MOD_LEVEL_MASK (0xff << 8) +# define RADEON_LVDS_BL_MOD_EN (1 << 16) +# define RADEON_LVDS_BL_CLK_SEL (1 << 17) +# define RADEON_LVDS_DIGON (1 << 18) +# define RADEON_LVDS_BLON (1 << 19) +# define RADEON_LVDS_FP_POL_LOW (1 << 20) +# define RADEON_LVDS_LP_POL_LOW (1 << 21) +# define RADEON_LVDS_DTM_POL_LOW (1 << 22) +# define RADEON_LVDS_SEL_CRTC2 (1 << 23) +# define RADEON_LVDS_FPDI_EN (1 << 27) +# define RADEON_LVDS_HSYNC_DELAY_SHIFT 28 +#define RADEON_LVDS_PLL_CNTL 0x02d4 +# define RADEON_HSYNC_DELAY_SHIFT 28 +# define RADEON_HSYNC_DELAY_MASK (0xf << 28) +# define RADEON_LVDS_PLL_EN (1 << 16) +# define RADEON_LVDS_PLL_RESET (1 << 17) +# define R300_LVDS_SRC_SEL_MASK (3 << 18) +# define R300_LVDS_SRC_SEL_CRTC1 (0 << 18) +# define R300_LVDS_SRC_SEL_CRTC2 (1 << 18) +# define R300_LVDS_SRC_SEL_RMX (2 << 18) +#define RADEON_LVDS_SS_GEN_CNTL 0x02ec +# define RADEON_LVDS_PWRSEQ_DELAY1_SHIFT 16 +# define RADEON_LVDS_PWRSEQ_DELAY2_SHIFT 20 + +#define RADEON_MAX_LATENCY 0x0f3f /* PCI */ +#define RADEON_DISPLAY_BASE_ADDR 0x23c +#define RADEON_DISPLAY2_BASE_ADDR 0x33c +#define RADEON_OV0_BASE_ADDR 0x43c +#define RADEON_NB_TOM 0x15c +#define R300_MC_INIT_MISC_LAT_TIMER 0x180 +# define R300_MC_DISP0R_INIT_LAT_SHIFT 8 +# define R300_MC_DISP0R_INIT_LAT_MASK 0xf +# define R300_MC_DISP1R_INIT_LAT_SHIFT 12 +# define R300_MC_DISP1R_INIT_LAT_MASK 0xf +#define RADEON_MCLK_CNTL 0x0012 /* PLL */ +# define RADEON_MCLKA_SRC_SEL_MASK 0x7 +# define RADEON_FORCEON_MCLKA (1 << 16) +# define RADEON_FORCEON_MCLKB (1 << 17) +# define RADEON_FORCEON_YCLKA (1 << 18) +# define RADEON_FORCEON_YCLKB (1 << 19) +# define RADEON_FORCEON_MC (1 << 20) +# define RADEON_FORCEON_AIC (1 << 21) +# define R300_DISABLE_MC_MCLKA (1 << 21) +# define R300_DISABLE_MC_MCLKB (1 << 21) +#define RADEON_MCLK_MISC 0x001f /* PLL */ +# define RADEON_MC_MCLK_MAX_DYN_STOP_LAT (1 << 12) +# define RADEON_IO_MCLK_MAX_DYN_STOP_LAT (1 << 13) +# define RADEON_MC_MCLK_DYN_ENABLE (1 << 14) +# define RADEON_IO_MCLK_DYN_ENABLE (1 << 15) + +#define RADEON_GPIOPAD_MASK 0x0198 +#define RADEON_GPIOPAD_A 0x019c +#define RADEON_GPIOPAD_EN 0x01a0 +#define RADEON_GPIOPAD_Y 0x01a4 +#define RADEON_MDGPIO_MASK 0x01a8 +#define RADEON_MDGPIO_A 0x01ac +#define RADEON_MDGPIO_EN 0x01b0 +#define RADEON_MDGPIO_Y 0x01b4 + +#define RADEON_MEM_ADDR_CONFIG 0x0148 +#define RADEON_MEM_BASE 0x0f10 /* PCI */ +#define RADEON_MEM_CNTL 0x0140 +# define RADEON_MEM_NUM_CHANNELS_MASK 0x01 +# define RADEON_MEM_USE_B_CH_ONLY (1 << 1) +# define RV100_HALF_MODE (1 << 3) +# define R300_MEM_NUM_CHANNELS_MASK 0x03 +# define R300_MEM_USE_CD_CH_ONLY (1 << 2) +#define RADEON_MEM_TIMING_CNTL 0x0144 /* EXT_MEM_CNTL */ +#define RADEON_MEM_INIT_LAT_TIMER 0x0154 +#define RADEON_MEM_INTF_CNTL 0x014c +#define RADEON_MEM_SDRAM_MODE_REG 0x0158 +# define RADEON_SDRAM_MODE_MASK 0xffff0000 +# define RADEON_B3MEM_RESET_MASK 0x6fffffff +# define RADEON_MEM_CFG_TYPE_DDR (1 << 30) +#define RADEON_MEM_STR_CNTL 0x0150 +# define RADEON_MEM_PWRUP_COMPL_A (1 << 0) +# define RADEON_MEM_PWRUP_COMPL_B (1 << 1) +# define R300_MEM_PWRUP_COMPL_C (1 << 2) +# define R300_MEM_PWRUP_COMPL_D (1 << 3) +# define RADEON_MEM_PWRUP_COMPLETE 0x03 +# define R300_MEM_PWRUP_COMPLETE 0x0f +#define RADEON_MC_STATUS 0x0150 +# define RADEON_MC_IDLE (1 << 2) +# define R300_MC_IDLE (1 << 4) +#define RADEON_MEM_VGA_RP_SEL 0x003c +#define RADEON_MEM_VGA_WP_SEL 0x0038 +#define RADEON_MIN_GRANT 0x0f3e /* PCI */ +#define RADEON_MM_DATA 0x0004 +#define RADEON_MM_INDEX 0x0000 +# define RADEON_MM_APER (1 << 31) +#define RADEON_MPLL_CNTL 0x000e /* PLL */ +#define RADEON_MPP_TB_CONFIG 0x01c0 /* ? */ +#define RADEON_MPP_GP_CONFIG 0x01c8 /* ? */ +#define RADEON_SEPROM_CNTL1 0x01c0 +# define RADEON_SCK_PRESCALE_SHIFT 24 +# define RADEON_SCK_PRESCALE_MASK (0xff << 24) +#define R300_MC_IND_INDEX 0x01f8 +# define R300_MC_IND_ADDR_MASK 0x3f +# define R300_MC_IND_WR_EN (1 << 8) +#define R300_MC_IND_DATA 0x01fc +#define R300_MC_READ_CNTL_AB 0x017c +# define R300_MEM_RBS_POSITION_A_MASK 0x03 +#define R300_MC_READ_CNTL_CD_mcind 0x24 +# define R300_MEM_RBS_POSITION_C_MASK 0x03 + +#define RADEON_N_VIF_COUNT 0x0248 + +#define RADEON_OV0_AUTO_FLIP_CNTL 0x0470 +# define RADEON_OV0_AUTO_FLIP_CNTL_SOFT_BUF_NUM 0x00000007 +# define RADEON_OV0_AUTO_FLIP_CNTL_SOFT_REPEAT_FIELD 0x00000008 +# define RADEON_OV0_AUTO_FLIP_CNTL_SOFT_BUF_ODD 0x00000010 +# define RADEON_OV0_AUTO_FLIP_CNTL_IGNORE_REPEAT_FIELD 0x00000020 +# define RADEON_OV0_AUTO_FLIP_CNTL_SOFT_EOF_TOGGLE 0x00000040 +# define RADEON_OV0_AUTO_FLIP_CNTL_VID_PORT_SELECT 0x00000300 +# define RADEON_OV0_AUTO_FLIP_CNTL_P1_FIRST_LINE_EVEN 0x00010000 +# define RADEON_OV0_AUTO_FLIP_CNTL_SHIFT_EVEN_DOWN 0x00040000 +# define RADEON_OV0_AUTO_FLIP_CNTL_SHIFT_ODD_DOWN 0x00080000 +# define RADEON_OV0_AUTO_FLIP_CNTL_FIELD_POL_SOURCE 0x00800000 + +#define RADEON_OV0_COLOUR_CNTL 0x04E0 +#define RADEON_OV0_DEINTERLACE_PATTERN 0x0474 +#define RADEON_OV0_EXCLUSIVE_HORZ 0x0408 +# define RADEON_EXCL_HORZ_START_MASK 0x000000ff +# define RADEON_EXCL_HORZ_END_MASK 0x0000ff00 +# define RADEON_EXCL_HORZ_BACK_PORCH_MASK 0x00ff0000 +# define RADEON_EXCL_HORZ_EXCLUSIVE_EN 0x80000000 +#define RADEON_OV0_EXCLUSIVE_VERT 0x040C +# define RADEON_EXCL_VERT_START_MASK 0x000003ff +# define RADEON_EXCL_VERT_END_MASK 0x03ff0000 +#define RADEON_OV0_FILTER_CNTL 0x04A0 +# define RADEON_FILTER_PROGRAMMABLE_COEF 0x0 +# define RADEON_FILTER_HC_COEF_HORZ_Y 0x1 +# define RADEON_FILTER_HC_COEF_HORZ_UV 0x2 +# define RADEON_FILTER_HC_COEF_VERT_Y 0x4 +# define RADEON_FILTER_HC_COEF_VERT_UV 0x8 +# define RADEON_FILTER_HARDCODED_COEF 0xf +# define RADEON_FILTER_COEF_MASK 0xf + +#define RADEON_OV0_FOUR_TAP_COEF_0 0x04B0 +#define RADEON_OV0_FOUR_TAP_COEF_1 0x04B4 +#define RADEON_OV0_FOUR_TAP_COEF_2 0x04B8 +#define RADEON_OV0_FOUR_TAP_COEF_3 0x04BC +#define RADEON_OV0_FOUR_TAP_COEF_4 0x04C0 +#define RADEON_OV0_FLAG_CNTL 0x04DC +#define RADEON_OV0_GAMMA_000_00F 0x0d40 +#define RADEON_OV0_GAMMA_010_01F 0x0d44 +#define RADEON_OV0_GAMMA_020_03F 0x0d48 +#define RADEON_OV0_GAMMA_040_07F 0x0d4c +#define RADEON_OV0_GAMMA_080_0BF 0x0e00 +#define RADEON_OV0_GAMMA_0C0_0FF 0x0e04 +#define RADEON_OV0_GAMMA_100_13F 0x0e08 +#define RADEON_OV0_GAMMA_140_17F 0x0e0c +#define RADEON_OV0_GAMMA_180_1BF 0x0e10 +#define RADEON_OV0_GAMMA_1C0_1FF 0x0e14 +#define RADEON_OV0_GAMMA_200_23F 0x0e18 +#define RADEON_OV0_GAMMA_240_27F 0x0e1c +#define RADEON_OV0_GAMMA_280_2BF 0x0e20 +#define RADEON_OV0_GAMMA_2C0_2FF 0x0e24 +#define RADEON_OV0_GAMMA_300_33F 0x0e28 +#define RADEON_OV0_GAMMA_340_37F 0x0e2c +#define RADEON_OV0_GAMMA_380_3BF 0x0d50 +#define RADEON_OV0_GAMMA_3C0_3FF 0x0d54 +#define RADEON_OV0_GRAPHICS_KEY_CLR_LOW 0x04EC +#define RADEON_OV0_GRAPHICS_KEY_CLR_HIGH 0x04F0 +#define RADEON_OV0_H_INC 0x0480 +#define RADEON_OV0_KEY_CNTL 0x04F4 +# define RADEON_VIDEO_KEY_FN_MASK 0x00000003L +# define RADEON_VIDEO_KEY_FN_FALSE 0x00000000L +# define RADEON_VIDEO_KEY_FN_TRUE 0x00000001L +# define RADEON_VIDEO_KEY_FN_EQ 0x00000002L +# define RADEON_VIDEO_KEY_FN_NE 0x00000003L +# define RADEON_GRAPHIC_KEY_FN_MASK 0x00000030L +# define RADEON_GRAPHIC_KEY_FN_FALSE 0x00000000L +# define RADEON_GRAPHIC_KEY_FN_TRUE 0x00000010L +# define RADEON_GRAPHIC_KEY_FN_EQ 0x00000020L +# define RADEON_GRAPHIC_KEY_FN_NE 0x00000030L +# define RADEON_CMP_MIX_MASK 0x00000100L +# define RADEON_CMP_MIX_OR 0x00000000L +# define RADEON_CMP_MIX_AND 0x00000100L +#define RADEON_OV0_LIN_TRANS_A 0x0d20 +#define RADEON_OV0_LIN_TRANS_B 0x0d24 +#define RADEON_OV0_LIN_TRANS_C 0x0d28 +#define RADEON_OV0_LIN_TRANS_D 0x0d2c +#define RADEON_OV0_LIN_TRANS_E 0x0d30 +#define RADEON_OV0_LIN_TRANS_F 0x0d34 +#define RADEON_OV0_P1_BLANK_LINES_AT_TOP 0x0430 +# define RADEON_P1_BLNK_LN_AT_TOP_M1_MASK 0x00000fffL +# define RADEON_P1_ACTIVE_LINES_M1 0x0fff0000L +#define RADEON_OV0_P1_H_ACCUM_INIT 0x0488 +#define RADEON_OV0_P1_V_ACCUM_INIT 0x0428 +# define RADEON_OV0_P1_MAX_LN_IN_PER_LN_OUT 0x00000003L +# define RADEON_OV0_P1_V_ACCUM_INIT_MASK 0x01ff8000L +#define RADEON_OV0_P1_X_START_END 0x0494 +#define RADEON_OV0_P2_X_START_END 0x0498 +#define RADEON_OV0_P23_BLANK_LINES_AT_TOP 0x0434 +# define RADEON_P23_BLNK_LN_AT_TOP_M1_MASK 0x000007ffL +# define RADEON_P23_ACTIVE_LINES_M1 0x07ff0000L +#define RADEON_OV0_P23_H_ACCUM_INIT 0x048C +#define RADEON_OV0_P23_V_ACCUM_INIT 0x042C +#define RADEON_OV0_P3_X_START_END 0x049C +#define RADEON_OV0_REG_LOAD_CNTL 0x0410 +# define RADEON_REG_LD_CTL_LOCK 0x00000001L +# define RADEON_REG_LD_CTL_VBLANK_DURING_LOCK 0x00000002L +# define RADEON_REG_LD_CTL_STALL_GUI_UNTIL_FLIP 0x00000004L +# define RADEON_REG_LD_CTL_LOCK_READBACK 0x00000008L +# define RADEON_REG_LD_CTL_FLIP_READBACK 0x00000010L +#define RADEON_OV0_SCALE_CNTL 0x0420 +# define RADEON_SCALER_HORZ_PICK_NEAREST 0x00000004L +# define RADEON_SCALER_VERT_PICK_NEAREST 0x00000008L +# define RADEON_SCALER_SIGNED_UV 0x00000010L +# define RADEON_SCALER_GAMMA_SEL_MASK 0x00000060L +# define RADEON_SCALER_GAMMA_SEL_BRIGHT 0x00000000L +# define RADEON_SCALER_GAMMA_SEL_G22 0x00000020L +# define RADEON_SCALER_GAMMA_SEL_G18 0x00000040L +# define RADEON_SCALER_GAMMA_SEL_G14 0x00000060L +# define RADEON_SCALER_COMCORE_SHIFT_UP_ONE 0x00000080L +# define RADEON_SCALER_SURFAC_FORMAT 0x00000f00L +# define RADEON_SCALER_SOURCE_15BPP 0x00000300L +# define RADEON_SCALER_SOURCE_16BPP 0x00000400L +# define RADEON_SCALER_SOURCE_32BPP 0x00000600L +# define RADEON_SCALER_SOURCE_YUV9 0x00000900L +# define RADEON_SCALER_SOURCE_YUV12 0x00000A00L +# define RADEON_SCALER_SOURCE_VYUY422 0x00000B00L +# define RADEON_SCALER_SOURCE_YVYU422 0x00000C00L +# define RADEON_SCALER_ADAPTIVE_DEINT 0x00001000L +# define RADEON_SCALER_TEMPORAL_DEINT 0x00002000L +# define RADEON_SCALER_CRTC_SEL 0x00004000L +# define RADEON_SCALER_SMART_SWITCH 0x00008000L +# define RADEON_SCALER_BURST_PER_PLANE 0x007F0000L +# define RADEON_SCALER_DOUBLE_BUFFER 0x01000000L +# define RADEON_SCALER_DIS_LIMIT 0x08000000L +# define RADEON_SCALER_LIN_TRANS_BYPASS 0x10000000L +# define RADEON_SCALER_INT_EMU 0x20000000L +# define RADEON_SCALER_ENABLE 0x40000000L +# define RADEON_SCALER_SOFT_RESET 0x80000000L +#define RADEON_OV0_STEP_BY 0x0484 +#define RADEON_OV0_TEST 0x04F8 +#define RADEON_OV0_V_INC 0x0424 +#define RADEON_OV0_VID_BUF_PITCH0_VALUE 0x0460 +#define RADEON_OV0_VID_BUF_PITCH1_VALUE 0x0464 +#define RADEON_OV0_VID_BUF0_BASE_ADRS 0x0440 +# define RADEON_VIF_BUF0_PITCH_SEL 0x00000001L +# define RADEON_VIF_BUF0_TILE_ADRS 0x00000002L +# define RADEON_VIF_BUF0_BASE_ADRS_MASK 0x03fffff0L +# define RADEON_VIF_BUF0_1ST_LINE_LSBS_MASK 0x48000000L +#define RADEON_OV0_VID_BUF1_BASE_ADRS 0x0444 +# define RADEON_VIF_BUF1_PITCH_SEL 0x00000001L +# define RADEON_VIF_BUF1_TILE_ADRS 0x00000002L +# define RADEON_VIF_BUF1_BASE_ADRS_MASK 0x03fffff0L +# define RADEON_VIF_BUF1_1ST_LINE_LSBS_MASK 0x48000000L +#define RADEON_OV0_VID_BUF2_BASE_ADRS 0x0448 +# define RADEON_VIF_BUF2_PITCH_SEL 0x00000001L +# define RADEON_VIF_BUF2_TILE_ADRS 0x00000002L +# define RADEON_VIF_BUF2_BASE_ADRS_MASK 0x03fffff0L +# define RADEON_VIF_BUF2_1ST_LINE_LSBS_MASK 0x48000000L +#define RADEON_OV0_VID_BUF3_BASE_ADRS 0x044C +#define RADEON_OV0_VID_BUF4_BASE_ADRS 0x0450 +#define RADEON_OV0_VID_BUF5_BASE_ADRS 0x0454 +#define RADEON_OV0_VIDEO_KEY_CLR_HIGH 0x04E8 +#define RADEON_OV0_VIDEO_KEY_CLR_LOW 0x04E4 +#define RADEON_OV0_Y_X_START 0x0400 +#define RADEON_OV0_Y_X_END 0x0404 +#define RADEON_OV1_Y_X_START 0x0600 +#define RADEON_OV1_Y_X_END 0x0604 +#define RADEON_OVR_CLR 0x0230 +#define RADEON_OVR_WID_LEFT_RIGHT 0x0234 +#define RADEON_OVR_WID_TOP_BOTTOM 0x0238 +#define RADEON_OVR2_CLR 0x0330 +#define RADEON_OVR2_WID_LEFT_RIGHT 0x0334 +#define RADEON_OVR2_WID_TOP_BOTTOM 0x0338 + +/* first capture unit */ + +#define RADEON_CAP0_BUF0_OFFSET 0x0920 +#define RADEON_CAP0_BUF1_OFFSET 0x0924 +#define RADEON_CAP0_BUF0_EVEN_OFFSET 0x0928 +#define RADEON_CAP0_BUF1_EVEN_OFFSET 0x092C + +#define RADEON_CAP0_BUF_PITCH 0x0930 +#define RADEON_CAP0_V_WINDOW 0x0934 +#define RADEON_CAP0_H_WINDOW 0x0938 +#define RADEON_CAP0_VBI0_OFFSET 0x093C +#define RADEON_CAP0_VBI1_OFFSET 0x0940 +#define RADEON_CAP0_VBI_V_WINDOW 0x0944 +#define RADEON_CAP0_VBI_H_WINDOW 0x0948 +#define RADEON_CAP0_PORT_MODE_CNTL 0x094C +#define RADEON_CAP0_TRIG_CNTL 0x0950 +#define RADEON_CAP0_DEBUG 0x0954 +#define RADEON_CAP0_CONFIG 0x0958 +# define RADEON_CAP0_CONFIG_CONTINUOS 0x00000001 +# define RADEON_CAP0_CONFIG_START_FIELD_EVEN 0x00000002 +# define RADEON_CAP0_CONFIG_START_BUF_GET 0x00000004 +# define RADEON_CAP0_CONFIG_START_BUF_SET 0x00000008 +# define RADEON_CAP0_CONFIG_BUF_TYPE_ALT 0x00000010 +# define RADEON_CAP0_CONFIG_BUF_TYPE_FRAME 0x00000020 +# define RADEON_CAP0_CONFIG_ONESHOT_MODE_FRAME 0x00000040 +# define RADEON_CAP0_CONFIG_BUF_MODE_DOUBLE 0x00000080 +# define RADEON_CAP0_CONFIG_BUF_MODE_TRIPLE 0x00000100 +# define RADEON_CAP0_CONFIG_MIRROR_EN 0x00000200 +# define RADEON_CAP0_CONFIG_ONESHOT_MIRROR_EN 0x00000400 +# define RADEON_CAP0_CONFIG_VIDEO_SIGNED_UV 0x00000800 +# define RADEON_CAP0_CONFIG_ANC_DECODE_EN 0x00001000 +# define RADEON_CAP0_CONFIG_VBI_EN 0x00002000 +# define RADEON_CAP0_CONFIG_SOFT_PULL_DOWN_EN 0x00004000 +# define RADEON_CAP0_CONFIG_VIP_EXTEND_FLAG_EN 0x00008000 +# define RADEON_CAP0_CONFIG_FAKE_FIELD_EN 0x00010000 +# define RADEON_CAP0_CONFIG_ODD_ONE_MORE_LINE 0x00020000 +# define RADEON_CAP0_CONFIG_EVEN_ONE_MORE_LINE 0x00040000 +# define RADEON_CAP0_CONFIG_HORZ_DIVIDE_2 0x00080000 +# define RADEON_CAP0_CONFIG_HORZ_DIVIDE_4 0x00100000 +# define RADEON_CAP0_CONFIG_VERT_DIVIDE_2 0x00200000 +# define RADEON_CAP0_CONFIG_VERT_DIVIDE_4 0x00400000 +# define RADEON_CAP0_CONFIG_FORMAT_BROOKTREE 0x00000000 +# define RADEON_CAP0_CONFIG_FORMAT_CCIR656 0x00800000 +# define RADEON_CAP0_CONFIG_FORMAT_ZV 0x01000000 +# define RADEON_CAP0_CONFIG_FORMAT_VIP 0x01800000 +# define RADEON_CAP0_CONFIG_FORMAT_TRANSPORT 0x02000000 +# define RADEON_CAP0_CONFIG_HORZ_DECIMATOR 0x04000000 +# define RADEON_CAP0_CONFIG_VIDEO_IN_YVYU422 0x00000000 +# define RADEON_CAP0_CONFIG_VIDEO_IN_VYUY422 0x20000000 +# define RADEON_CAP0_CONFIG_VBI_DIVIDE_2 0x40000000 +# define RADEON_CAP0_CONFIG_VBI_DIVIDE_4 0x80000000 +#define RADEON_CAP0_ANC_ODD_OFFSET 0x095C +#define RADEON_CAP0_ANC_EVEN_OFFSET 0x0960 +#define RADEON_CAP0_ANC_H_WINDOW 0x0964 +#define RADEON_CAP0_VIDEO_SYNC_TEST 0x0968 +#define RADEON_CAP0_ONESHOT_BUF_OFFSET 0x096C +#define RADEON_CAP0_BUF_STATUS 0x0970 +/* #define RADEON_CAP0_DWNSC_XRATIO 0x0978 */ +/* #define RADEON_CAP0_XSHARPNESS 0x097C */ +#define RADEON_CAP0_VBI2_OFFSET 0x0980 +#define RADEON_CAP0_VBI3_OFFSET 0x0984 +#define RADEON_CAP0_ANC2_OFFSET 0x0988 +#define RADEON_CAP0_ANC3_OFFSET 0x098C +#define RADEON_VID_BUFFER_CONTROL 0x0900 + +/* second capture unit */ + +#define RADEON_CAP1_BUF0_OFFSET 0x0990 +#define RADEON_CAP1_BUF1_OFFSET 0x0994 +#define RADEON_CAP1_BUF0_EVEN_OFFSET 0x0998 +#define RADEON_CAP1_BUF1_EVEN_OFFSET 0x099C + +#define RADEON_CAP1_BUF_PITCH 0x09A0 +#define RADEON_CAP1_V_WINDOW 0x09A4 +#define RADEON_CAP1_H_WINDOW 0x09A8 +#define RADEON_CAP1_VBI_ODD_OFFSET 0x09AC +#define RADEON_CAP1_VBI_EVEN_OFFSET 0x09B0 +#define RADEON_CAP1_VBI_V_WINDOW 0x09B4 +#define RADEON_CAP1_VBI_H_WINDOW 0x09B8 +#define RADEON_CAP1_PORT_MODE_CNTL 0x09BC +#define RADEON_CAP1_TRIG_CNTL 0x09C0 +#define RADEON_CAP1_DEBUG 0x09C4 +#define RADEON_CAP1_CONFIG 0x09C8 +#define RADEON_CAP1_ANC_ODD_OFFSET 0x09CC +#define RADEON_CAP1_ANC_EVEN_OFFSET 0x09D0 +#define RADEON_CAP1_ANC_H_WINDOW 0x09D4 +#define RADEON_CAP1_VIDEO_SYNC_TEST 0x09D8 +#define RADEON_CAP1_ONESHOT_BUF_OFFSET 0x09DC +#define RADEON_CAP1_BUF_STATUS 0x09E0 +#define RADEON_CAP1_DWNSC_XRATIO 0x09E8 +#define RADEON_CAP1_XSHARPNESS 0x09EC + +/* misc multimedia registers */ + +#define RADEON_IDCT_RUNS 0x1F80 +#define RADEON_IDCT_LEVELS 0x1F84 +#define RADEON_IDCT_CONTROL 0x1FBC +#define RADEON_IDCT_AUTH_CONTROL 0x1F88 +#define RADEON_IDCT_AUTH 0x1F8C + +#define RADEON_P2PLL_CNTL 0x002a /* P2PLL */ +# define RADEON_P2PLL_RESET (1 << 0) +# define RADEON_P2PLL_SLEEP (1 << 1) +# define RADEON_P2PLL_PVG_MASK (7 << 11) +# define RADEON_P2PLL_PVG_SHIFT 11 +# define RADEON_P2PLL_ATOMIC_UPDATE_EN (1 << 16) +# define RADEON_P2PLL_VGA_ATOMIC_UPDATE_EN (1 << 17) +# define RADEON_P2PLL_ATOMIC_UPDATE_VSYNC (1 << 18) +#define RADEON_P2PLL_DIV_0 0x002c +# define RADEON_P2PLL_FB0_DIV_MASK 0x07ff +# define RADEON_P2PLL_POST0_DIV_MASK 0x00070000 +#define RADEON_P2PLL_REF_DIV 0x002B /* PLL */ +# define RADEON_P2PLL_REF_DIV_MASK 0x03ff +# define RADEON_P2PLL_ATOMIC_UPDATE_R (1 << 15) /* same as _W */ +# define RADEON_P2PLL_ATOMIC_UPDATE_W (1 << 15) /* same as _R */ +# define R300_PPLL_REF_DIV_ACC_MASK (0x3ff << 18) +# define R300_PPLL_REF_DIV_ACC_SHIFT 18 +#define RADEON_PALETTE_DATA 0x00b4 +#define RADEON_PALETTE_30_DATA 0x00b8 +#define RADEON_PALETTE_INDEX 0x00b0 +#define RADEON_PCI_GART_PAGE 0x017c +#define RADEON_PIXCLKS_CNTL 0x002d +# define RADEON_PIX2CLK_SRC_SEL_MASK 0x03 +# define RADEON_PIX2CLK_SRC_SEL_CPUCLK 0x00 +# define RADEON_PIX2CLK_SRC_SEL_PSCANCLK 0x01 +# define RADEON_PIX2CLK_SRC_SEL_BYTECLK 0x02 +# define RADEON_PIX2CLK_SRC_SEL_P2PLLCLK 0x03 +# define RADEON_PIX2CLK_ALWAYS_ONb (1<<6) +# define RADEON_PIX2CLK_DAC_ALWAYS_ONb (1<<7) +# define RADEON_PIXCLK_TV_SRC_SEL (1 << 8) +# define RADEON_DISP_TVOUT_PIXCLK_TV_ALWAYS_ONb (1 << 9) +# define R300_DVOCLK_ALWAYS_ONb (1 << 10) +# define RADEON_PIXCLK_BLEND_ALWAYS_ONb (1 << 11) +# define RADEON_PIXCLK_GV_ALWAYS_ONb (1 << 12) +# define RADEON_PIXCLK_DIG_TMDS_ALWAYS_ONb (1 << 13) +# define R300_PIXCLK_DVO_ALWAYS_ONb (1 << 13) +# define RADEON_PIXCLK_LVDS_ALWAYS_ONb (1 << 14) +# define RADEON_PIXCLK_TMDS_ALWAYS_ONb (1 << 15) +# define R300_PIXCLK_TRANS_ALWAYS_ONb (1 << 16) +# define R300_PIXCLK_TVO_ALWAYS_ONb (1 << 17) +# define R300_P2G2CLK_ALWAYS_ONb (1 << 18) +# define R300_P2G2CLK_DAC_ALWAYS_ONb (1 << 19) +# define R300_DISP_DAC_PIXCLK_DAC2_BLANK_OFF (1 << 23) +#define RADEON_PLANE_3D_MASK_C 0x1d44 +#define RADEON_PLL_TEST_CNTL 0x0013 /* PLL */ +# define RADEON_PLL_MASK_READ_B (1 << 9) +#define RADEON_PMI_CAP_ID 0x0f5c /* PCI */ +#define RADEON_PMI_DATA 0x0f63 /* PCI */ +#define RADEON_PMI_NXT_CAP_PTR 0x0f5d /* PCI */ +#define RADEON_PMI_PMC_REG 0x0f5e /* PCI */ +#define RADEON_PMI_PMCSR_REG 0x0f60 /* PCI */ +#define RADEON_PMI_REGISTER 0x0f5c /* PCI */ +#define RADEON_PPLL_CNTL 0x0002 /* PLL */ +# define RADEON_PPLL_RESET (1 << 0) +# define RADEON_PPLL_SLEEP (1 << 1) +# define RADEON_PPLL_PVG_MASK (7 << 11) +# define RADEON_PPLL_PVG_SHIFT 11 +# define RADEON_PPLL_ATOMIC_UPDATE_EN (1 << 16) +# define RADEON_PPLL_VGA_ATOMIC_UPDATE_EN (1 << 17) +# define RADEON_PPLL_ATOMIC_UPDATE_VSYNC (1 << 18) +#define RADEON_PPLL_DIV_0 0x0004 /* PLL */ +#define RADEON_PPLL_DIV_1 0x0005 /* PLL */ +#define RADEON_PPLL_DIV_2 0x0006 /* PLL */ +#define RADEON_PPLL_DIV_3 0x0007 /* PLL */ +# define RADEON_PPLL_FB3_DIV_MASK 0x07ff +# define RADEON_PPLL_POST3_DIV_MASK 0x00070000 +#define RADEON_PPLL_REF_DIV 0x0003 /* PLL */ +# define RADEON_PPLL_REF_DIV_MASK 0x03ff +# define RADEON_PPLL_ATOMIC_UPDATE_R (1 << 15) /* same as _W */ +# define RADEON_PPLL_ATOMIC_UPDATE_W (1 << 15) /* same as _R */ +#define RADEON_PWR_MNGMT_CNTL_STATUS 0x0f60 /* PCI */ + +#define RADEON_RBBM_GUICNTL 0x172c +# define RADEON_HOST_DATA_SWAP_NONE (0 << 0) +# define RADEON_HOST_DATA_SWAP_16BIT (1 << 0) +# define RADEON_HOST_DATA_SWAP_32BIT (2 << 0) +# define RADEON_HOST_DATA_SWAP_HDW (3 << 0) +#define RADEON_RBBM_SOFT_RESET 0x00f0 +# define RADEON_SOFT_RESET_CP (1 << 0) +# define RADEON_SOFT_RESET_HI (1 << 1) +# define RADEON_SOFT_RESET_SE (1 << 2) +# define RADEON_SOFT_RESET_RE (1 << 3) +# define RADEON_SOFT_RESET_PP (1 << 4) +# define RADEON_SOFT_RESET_E2 (1 << 5) +# define RADEON_SOFT_RESET_RB (1 << 6) +# define RADEON_SOFT_RESET_HDP (1 << 7) +#define RADEON_RBBM_STATUS 0x0e40 +# define RADEON_RBBM_FIFOCNT_MASK 0x007f +# define RADEON_RBBM_ACTIVE (1 << 31) +#define RADEON_RB2D_DSTCACHE_CTLSTAT 0x342c +# define RADEON_RB2D_DC_FLUSH (3 << 0) +# define RADEON_RB2D_DC_FREE (3 << 2) +# define RADEON_RB2D_DC_FLUSH_ALL 0xf +# define RADEON_RB2D_DC_BUSY (1 << 31) +#define RADEON_RB2D_DSTCACHE_MODE 0x3428 +#define RADEON_DSTCACHE_CTLSTAT 0x1714 + +#define RADEON_RB3D_ZCACHE_MODE 0x3250 +#define RADEON_RB3D_ZCACHE_CTLSTAT 0x3254 +# define RADEON_RB3D_ZC_FLUSH_ALL 0x5 +#define RADEON_RB3D_DSTCACHE_MODE 0x3258 +# define RADEON_RB3D_DC_CACHE_ENABLE (0) +# define RADEON_RB3D_DC_2D_CACHE_DISABLE (1) +# define RADEON_RB3D_DC_3D_CACHE_DISABLE (2) +# define RADEON_RB3D_DC_CACHE_DISABLE (3) +# define RADEON_RB3D_DC_2D_CACHE_LINESIZE_128 (1 << 2) +# define RADEON_RB3D_DC_3D_CACHE_LINESIZE_128 (2 << 2) +# define RADEON_RB3D_DC_2D_CACHE_AUTOFLUSH (1 << 8) +# define RADEON_RB3D_DC_3D_CACHE_AUTOFLUSH (2 << 8) +# define R200_RB3D_DC_2D_CACHE_AUTOFREE (1 << 10) +# define R200_RB3D_DC_3D_CACHE_AUTOFREE (2 << 10) +# define RADEON_RB3D_DC_FORCE_RMW (1 << 16) +# define RADEON_RB3D_DC_DISABLE_RI_FILL (1 << 24) +# define RADEON_RB3D_DC_DISABLE_RI_READ (1 << 25) + +#define RADEON_RB3D_DSTCACHE_CTLSTAT 0x325C +# define RADEON_RB3D_DC_FLUSH (3 << 0) +# define RADEON_RB3D_DC_FREE (3 << 2) +# define RADEON_RB3D_DC_FLUSH_ALL 0xf +# define RADEON_RB3D_DC_BUSY (1 << 31) + +#define RADEON_REG_BASE 0x0f18 /* PCI */ +#define RADEON_REGPROG_INF 0x0f09 /* PCI */ +#define RADEON_REVISION_ID 0x0f08 /* PCI */ + +#define RADEON_SC_BOTTOM 0x164c +#define RADEON_SC_BOTTOM_RIGHT 0x16f0 +#define RADEON_SC_BOTTOM_RIGHT_C 0x1c8c +#define RADEON_SC_LEFT 0x1640 +#define RADEON_SC_RIGHT 0x1644 +#define RADEON_SC_TOP 0x1648 +#define RADEON_SC_TOP_LEFT 0x16ec +#define RADEON_SC_TOP_LEFT_C 0x1c88 +# define RADEON_SC_SIGN_MASK_LO 0x8000 +# define RADEON_SC_SIGN_MASK_HI 0x80000000 +#define RADEON_M_SPLL_REF_FB_DIV 0x000a /* PLL */ +# define RADEON_M_SPLL_REF_DIV_SHIFT 0 +# define RADEON_M_SPLL_REF_DIV_MASK 0xff +# define RADEON_MPLL_FB_DIV_SHIFT 8 +# define RADEON_MPLL_FB_DIV_MASK 0xff +# define RADEON_SPLL_FB_DIV_SHIFT 16 +# define RADEON_SPLL_FB_DIV_MASK 0xff +#define RADEON_SPLL_CNTL 0x000c /* PLL */ +# define RADEON_SPLL_SLEEP (1 << 0) +# define RADEON_SPLL_RESET (1 << 1) +# define RADEON_SPLL_PCP_MASK 0x7 +# define RADEON_SPLL_PCP_SHIFT 8 +# define RADEON_SPLL_PVG_MASK 0x7 +# define RADEON_SPLL_PVG_SHIFT 11 +# define RADEON_SPLL_PDC_MASK 0x3 +# define RADEON_SPLL_PDC_SHIFT 14 +#define RADEON_SCLK_CNTL 0x000d /* PLL */ +# define RADEON_SCLK_SRC_SEL_MASK 0x0007 +# define RADEON_DYN_STOP_LAT_MASK 0x00007ff8 +# define RADEON_CP_MAX_DYN_STOP_LAT 0x0008 +# define RADEON_SCLK_FORCEON_MASK 0xffff8000 +# define RADEON_SCLK_FORCE_DISP2 (1<<15) +# define RADEON_SCLK_FORCE_CP (1<<16) +# define RADEON_SCLK_FORCE_HDP (1<<17) +# define RADEON_SCLK_FORCE_DISP1 (1<<18) +# define RADEON_SCLK_FORCE_TOP (1<<19) +# define RADEON_SCLK_FORCE_E2 (1<<20) +# define RADEON_SCLK_FORCE_SE (1<<21) +# define RADEON_SCLK_FORCE_IDCT (1<<22) +# define RADEON_SCLK_FORCE_VIP (1<<23) +# define RADEON_SCLK_FORCE_RE (1<<24) +# define RADEON_SCLK_FORCE_PB (1<<25) +# define RADEON_SCLK_FORCE_TAM (1<<26) +# define RADEON_SCLK_FORCE_TDM (1<<27) +# define RADEON_SCLK_FORCE_RB (1<<28) +# define RADEON_SCLK_FORCE_TV_SCLK (1<<29) +# define RADEON_SCLK_FORCE_SUBPIC (1<<30) +# define RADEON_SCLK_FORCE_OV0 (1<<31) +# define R300_SCLK_FORCE_VAP (1<<21) +# define R300_SCLK_FORCE_SR (1<<25) +# define R300_SCLK_FORCE_PX (1<<26) +# define R300_SCLK_FORCE_TX (1<<27) +# define R300_SCLK_FORCE_US (1<<28) +# define R300_SCLK_FORCE_SU (1<<30) +#define R300_SCLK_CNTL2 0x1e /* PLL */ +# define R300_SCLK_TCL_MAX_DYN_STOP_LAT (1<<10) +# define R300_SCLK_GA_MAX_DYN_STOP_LAT (1<<11) +# define R300_SCLK_CBA_MAX_DYN_STOP_LAT (1<<12) +# define R300_SCLK_FORCE_TCL (1<<13) +# define R300_SCLK_FORCE_CBA (1<<14) +# define R300_SCLK_FORCE_GA (1<<15) +#define RADEON_SCLK_MORE_CNTL 0x0035 /* PLL */ +# define RADEON_SCLK_MORE_MAX_DYN_STOP_LAT 0x0007 +# define RADEON_SCLK_MORE_FORCEON 0x0700 +#define RADEON_SDRAM_MODE_REG 0x0158 +#define RADEON_SEQ8_DATA 0x03c5 /* VGA */ +#define RADEON_SEQ8_IDX 0x03c4 /* VGA */ +#define RADEON_SNAPSHOT_F_COUNT 0x0244 +#define RADEON_SNAPSHOT_VH_COUNTS 0x0240 +#define RADEON_SNAPSHOT_VIF_COUNT 0x024c +#define RADEON_SRC_OFFSET 0x15ac +#define RADEON_SRC_PITCH 0x15b0 +#define RADEON_SRC_PITCH_OFFSET 0x1428 +#define RADEON_SRC_SC_BOTTOM 0x165c +#define RADEON_SRC_SC_BOTTOM_RIGHT 0x16f4 +#define RADEON_SRC_SC_RIGHT 0x1654 +#define RADEON_SRC_X 0x1414 +#define RADEON_SRC_X_Y 0x1590 +#define RADEON_SRC_Y 0x1418 +#define RADEON_SRC_Y_X 0x1434 +#define RADEON_STATUS 0x0f06 /* PCI */ +#define RADEON_SUBPIC_CNTL 0x0540 /* ? */ +#define RADEON_SUB_CLASS 0x0f0a /* PCI */ +#define RADEON_SURFACE_CNTL 0x0b00 +# define RADEON_SURF_TRANSLATION_DIS (1 << 8) +# define RADEON_NONSURF_AP0_SWP_16BPP (1 << 20) +# define RADEON_NONSURF_AP0_SWP_32BPP (1 << 21) +# define RADEON_NONSURF_AP1_SWP_16BPP (1 << 22) +# define RADEON_NONSURF_AP1_SWP_32BPP (1 << 23) +#define RADEON_SURFACE0_INFO 0x0b0c +# define RADEON_SURF_TILE_COLOR_MACRO (0 << 16) +# define RADEON_SURF_TILE_COLOR_BOTH (1 << 16) +# define RADEON_SURF_TILE_DEPTH_32BPP (2 << 16) +# define RADEON_SURF_TILE_DEPTH_16BPP (3 << 16) +# define R200_SURF_TILE_NONE (0 << 16) +# define R200_SURF_TILE_COLOR_MACRO (1 << 16) +# define R200_SURF_TILE_COLOR_MICRO (2 << 16) +# define R200_SURF_TILE_COLOR_BOTH (3 << 16) +# define R200_SURF_TILE_DEPTH_32BPP (4 << 16) +# define R200_SURF_TILE_DEPTH_16BPP (5 << 16) +# define R300_SURF_TILE_NONE (0 << 16) +# define R300_SURF_TILE_COLOR_MACRO (1 << 16) +# define R300_SURF_TILE_DEPTH_32BPP (2 << 16) +# define RADEON_SURF_AP0_SWP_16BPP (1 << 20) +# define RADEON_SURF_AP0_SWP_32BPP (1 << 21) +# define RADEON_SURF_AP1_SWP_16BPP (1 << 22) +# define RADEON_SURF_AP1_SWP_32BPP (1 << 23) +#define RADEON_SURFACE0_LOWER_BOUND 0x0b04 +#define RADEON_SURFACE0_UPPER_BOUND 0x0b08 +#define RADEON_SURFACE1_INFO 0x0b1c +#define RADEON_SURFACE1_LOWER_BOUND 0x0b14 +#define RADEON_SURFACE1_UPPER_BOUND 0x0b18 +#define RADEON_SURFACE2_INFO 0x0b2c +#define RADEON_SURFACE2_LOWER_BOUND 0x0b24 +#define RADEON_SURFACE2_UPPER_BOUND 0x0b28 +#define RADEON_SURFACE3_INFO 0x0b3c +#define RADEON_SURFACE3_LOWER_BOUND 0x0b34 +#define RADEON_SURFACE3_UPPER_BOUND 0x0b38 +#define RADEON_SURFACE4_INFO 0x0b4c +#define RADEON_SURFACE4_LOWER_BOUND 0x0b44 +#define RADEON_SURFACE4_UPPER_BOUND 0x0b48 +#define RADEON_SURFACE5_INFO 0x0b5c +#define RADEON_SURFACE5_LOWER_BOUND 0x0b54 +#define RADEON_SURFACE5_UPPER_BOUND 0x0b58 +#define RADEON_SURFACE6_INFO 0x0b6c +#define RADEON_SURFACE6_LOWER_BOUND 0x0b64 +#define RADEON_SURFACE6_UPPER_BOUND 0x0b68 +#define RADEON_SURFACE7_INFO 0x0b7c +#define RADEON_SURFACE7_LOWER_BOUND 0x0b74 +#define RADEON_SURFACE7_UPPER_BOUND 0x0b78 +#define RADEON_SW_SEMAPHORE 0x013c + +#define RADEON_TEST_DEBUG_CNTL 0x0120 +#define RADEON_TEST_DEBUG_CNTL__TEST_DEBUG_OUT_EN 0x00000001 + +#define RADEON_TEST_DEBUG_MUX 0x0124 +#define RADEON_TEST_DEBUG_OUT 0x012c +#define RADEON_TMDS_PLL_CNTL 0x02a8 +#define RADEON_TMDS_TRANSMITTER_CNTL 0x02a4 +# define RADEON_TMDS_TRANSMITTER_PLLEN 1 +# define RADEON_TMDS_TRANSMITTER_PLLRST 2 +#define RADEON_TRAIL_BRES_DEC 0x1614 +#define RADEON_TRAIL_BRES_ERR 0x160c +#define RADEON_TRAIL_BRES_INC 0x1610 +#define RADEON_TRAIL_X 0x1618 +#define RADEON_TRAIL_X_SUB 0x1620 + +#define RADEON_VCLK_ECP_CNTL 0x0008 /* PLL */ +# define RADEON_VCLK_SRC_SEL_MASK 0x03 +# define RADEON_VCLK_SRC_SEL_CPUCLK 0x00 +# define RADEON_VCLK_SRC_SEL_PSCANCLK 0x01 +# define RADEON_VCLK_SRC_SEL_BYTECLK 0x02 +# define RADEON_VCLK_SRC_SEL_PPLLCLK 0x03 +# define RADEON_PIXCLK_ALWAYS_ONb (1<<6) +# define RADEON_PIXCLK_DAC_ALWAYS_ONb (1<<7) +# define R300_DISP_DAC_PIXCLK_DAC_BLANK_OFF (1<<23) + +#define RADEON_VENDOR_ID 0x0f00 /* PCI */ +#define RADEON_VGA_DDA_CONFIG 0x02e8 +#define RADEON_VGA_DDA_ON_OFF 0x02ec +#define RADEON_VID_BUFFER_CONTROL 0x0900 +#define RADEON_VIDEOMUX_CNTL 0x0190 + +/* VIP bus */ +#define RADEON_VIPH_CH0_DATA 0x0c00 +#define RADEON_VIPH_CH1_DATA 0x0c04 +#define RADEON_VIPH_CH2_DATA 0x0c08 +#define RADEON_VIPH_CH3_DATA 0x0c0c +#define RADEON_VIPH_CH0_ADDR 0x0c10 +#define RADEON_VIPH_CH1_ADDR 0x0c14 +#define RADEON_VIPH_CH2_ADDR 0x0c18 +#define RADEON_VIPH_CH3_ADDR 0x0c1c +#define RADEON_VIPH_CH0_SBCNT 0x0c20 +#define RADEON_VIPH_CH1_SBCNT 0x0c24 +#define RADEON_VIPH_CH2_SBCNT 0x0c28 +#define RADEON_VIPH_CH3_SBCNT 0x0c2c +#define RADEON_VIPH_CH0_ABCNT 0x0c30 +#define RADEON_VIPH_CH1_ABCNT 0x0c34 +#define RADEON_VIPH_CH2_ABCNT 0x0c38 +#define RADEON_VIPH_CH3_ABCNT 0x0c3c +#define RADEON_VIPH_CONTROL 0x0c40 +# define RADEON_VIP_BUSY 0 +# define RADEON_VIP_IDLE 1 +# define RADEON_VIP_RESET 2 +# define RADEON_VIPH_EN (1 << 21) +#define RADEON_VIPH_DV_LAT 0x0c44 +#define RADEON_VIPH_BM_CHUNK 0x0c48 +#define RADEON_VIPH_DV_INT 0x0c4c +#define RADEON_VIPH_TIMEOUT_STAT 0x0c50 +#define RADEON_VIPH_TIMEOUT_STAT__VIPH_REG_STAT 0x00000010 +#define RADEON_VIPH_TIMEOUT_STAT__VIPH_REG_AK 0x00000010 +#define RADEON_VIPH_TIMEOUT_STAT__VIPH_REGR_DIS 0x01000000 + +#define RADEON_VIPH_REG_DATA 0x0084 +#define RADEON_VIPH_REG_ADDR 0x0080 + + +#define RADEON_WAIT_UNTIL 0x1720 +# define RADEON_WAIT_CRTC_PFLIP (1 << 0) +# define RADEON_WAIT_RE_CRTC_VLINE (1 << 1) +# define RADEON_WAIT_FE_CRTC_VLINE (1 << 2) +# define RADEON_WAIT_CRTC_VLINE (1 << 3) +# define RADEON_WAIT_DMA_VID_IDLE (1 << 8) +# define RADEON_WAIT_DMA_GUI_IDLE (1 << 9) +# define RADEON_WAIT_CMDFIFO (1 << 10) /* wait for CMDFIFO_ENTRIES */ +# define RADEON_WAIT_OV0_FLIP (1 << 11) +# define RADEON_WAIT_AGP_FLUSH (1 << 13) +# define RADEON_WAIT_2D_IDLE (1 << 14) +# define RADEON_WAIT_3D_IDLE (1 << 15) +# define RADEON_WAIT_2D_IDLECLEAN (1 << 16) +# define RADEON_WAIT_3D_IDLECLEAN (1 << 17) +# define RADEON_WAIT_HOST_IDLECLEAN (1 << 18) +# define RADEON_CMDFIFO_ENTRIES_SHIFT 10 +# define RADEON_CMDFIFO_ENTRIES_MASK 0x7f +# define RADEON_WAIT_VAP_IDLE (1 << 28) +# define RADEON_WAIT_BOTH_CRTC_PFLIP (1 << 30) +# define RADEON_ENG_DISPLAY_SELECT_CRTC0 (0 << 31) +# define RADEON_ENG_DISPLAY_SELECT_CRTC1 (1 << 31) + +#define RADEON_X_MPLL_REF_FB_DIV 0x000a /* PLL */ +#define RADEON_XCLK_CNTL 0x000d /* PLL */ +#define RADEON_XDLL_CNTL 0x000c /* PLL */ +#define RADEON_XPLL_CNTL 0x000b /* PLL */ + + + + /* Registers for 3D/TCL */ +#define RADEON_PP_BORDER_COLOR_0 0x1d40 +#define RADEON_PP_BORDER_COLOR_1 0x1d44 +#define RADEON_PP_BORDER_COLOR_2 0x1d48 +#define RADEON_PP_CNTL 0x1c38 +# define RADEON_STIPPLE_ENABLE (1 << 0) +# define RADEON_SCISSOR_ENABLE (1 << 1) +# define RADEON_PATTERN_ENABLE (1 << 2) +# define RADEON_SHADOW_ENABLE (1 << 3) +# define RADEON_TEX_ENABLE_MASK (0xf << 4) +# define RADEON_TEX_0_ENABLE (1 << 4) +# define RADEON_TEX_1_ENABLE (1 << 5) +# define RADEON_TEX_2_ENABLE (1 << 6) +# define RADEON_TEX_3_ENABLE (1 << 7) +# define RADEON_TEX_BLEND_ENABLE_MASK (0xf << 12) +# define RADEON_TEX_BLEND_0_ENABLE (1 << 12) +# define RADEON_TEX_BLEND_1_ENABLE (1 << 13) +# define RADEON_TEX_BLEND_2_ENABLE (1 << 14) +# define RADEON_TEX_BLEND_3_ENABLE (1 << 15) +# define RADEON_PLANAR_YUV_ENABLE (1 << 20) +# define RADEON_SPECULAR_ENABLE (1 << 21) +# define RADEON_FOG_ENABLE (1 << 22) +# define RADEON_ALPHA_TEST_ENABLE (1 << 23) +# define RADEON_ANTI_ALIAS_NONE (0 << 24) +# define RADEON_ANTI_ALIAS_LINE (1 << 24) +# define RADEON_ANTI_ALIAS_POLY (2 << 24) +# define RADEON_ANTI_ALIAS_LINE_POLY (3 << 24) +# define RADEON_BUMP_MAP_ENABLE (1 << 26) +# define RADEON_BUMPED_MAP_T0 (0 << 27) +# define RADEON_BUMPED_MAP_T1 (1 << 27) +# define RADEON_BUMPED_MAP_T2 (2 << 27) +# define RADEON_TEX_3D_ENABLE_0 (1 << 29) +# define RADEON_TEX_3D_ENABLE_1 (1 << 30) +# define RADEON_MC_ENABLE (1 << 31) +#define RADEON_PP_FOG_COLOR 0x1c18 +# define RADEON_FOG_COLOR_MASK 0x00ffffff +# define RADEON_FOG_VERTEX (0 << 24) +# define RADEON_FOG_TABLE (1 << 24) +# define RADEON_FOG_USE_DEPTH (0 << 25) +# define RADEON_FOG_USE_DIFFUSE_ALPHA (2 << 25) +# define RADEON_FOG_USE_SPEC_ALPHA (3 << 25) +#define RADEON_PP_LUM_MATRIX 0x1d00 +#define RADEON_PP_MISC 0x1c14 +# define RADEON_REF_ALPHA_MASK 0x000000ff +# define RADEON_ALPHA_TEST_FAIL (0 << 8) +# define RADEON_ALPHA_TEST_LESS (1 << 8) +# define RADEON_ALPHA_TEST_LEQUAL (2 << 8) +# define RADEON_ALPHA_TEST_EQUAL (3 << 8) +# define RADEON_ALPHA_TEST_GEQUAL (4 << 8) +# define RADEON_ALPHA_TEST_GREATER (5 << 8) +# define RADEON_ALPHA_TEST_NEQUAL (6 << 8) +# define RADEON_ALPHA_TEST_PASS (7 << 8) +# define RADEON_ALPHA_TEST_OP_MASK (7 << 8) +# define RADEON_CHROMA_FUNC_FAIL (0 << 16) +# define RADEON_CHROMA_FUNC_PASS (1 << 16) +# define RADEON_CHROMA_FUNC_NEQUAL (2 << 16) +# define RADEON_CHROMA_FUNC_EQUAL (3 << 16) +# define RADEON_CHROMA_KEY_NEAREST (0 << 18) +# define RADEON_CHROMA_KEY_ZERO (1 << 18) +# define RADEON_SHADOW_ID_AUTO_INC (1 << 20) +# define RADEON_SHADOW_FUNC_EQUAL (0 << 21) +# define RADEON_SHADOW_FUNC_NEQUAL (1 << 21) +# define RADEON_SHADOW_PASS_1 (0 << 22) +# define RADEON_SHADOW_PASS_2 (1 << 22) +# define RADEON_RIGHT_HAND_CUBE_D3D (0 << 24) +# define RADEON_RIGHT_HAND_CUBE_OGL (1 << 24) +#define RADEON_PP_ROT_MATRIX_0 0x1d58 +#define RADEON_PP_ROT_MATRIX_1 0x1d5c +#define RADEON_PP_TXFILTER_0 0x1c54 +#define RADEON_PP_TXFILTER_1 0x1c6c +#define RADEON_PP_TXFILTER_2 0x1c84 +# define RADEON_MAG_FILTER_NEAREST (0 << 0) +# define RADEON_MAG_FILTER_LINEAR (1 << 0) +# define RADEON_MAG_FILTER_MASK (1 << 0) +# define RADEON_MIN_FILTER_NEAREST (0 << 1) +# define RADEON_MIN_FILTER_LINEAR (1 << 1) +# define RADEON_MIN_FILTER_NEAREST_MIP_NEAREST (2 << 1) +# define RADEON_MIN_FILTER_NEAREST_MIP_LINEAR (3 << 1) +# define RADEON_MIN_FILTER_LINEAR_MIP_NEAREST (6 << 1) +# define RADEON_MIN_FILTER_LINEAR_MIP_LINEAR (7 << 1) +# define RADEON_MIN_FILTER_ANISO_NEAREST (8 << 1) +# define RADEON_MIN_FILTER_ANISO_LINEAR (9 << 1) +# define RADEON_MIN_FILTER_ANISO_NEAREST_MIP_NEAREST (10 << 1) +# define RADEON_MIN_FILTER_ANISO_NEAREST_MIP_LINEAR (11 << 1) +# define RADEON_MIN_FILTER_MASK (15 << 1) +# define RADEON_MAX_ANISO_1_TO_1 (0 << 5) +# define RADEON_MAX_ANISO_2_TO_1 (1 << 5) +# define RADEON_MAX_ANISO_4_TO_1 (2 << 5) +# define RADEON_MAX_ANISO_8_TO_1 (3 << 5) +# define RADEON_MAX_ANISO_16_TO_1 (4 << 5) +# define RADEON_MAX_ANISO_MASK (7 << 5) +# define RADEON_LOD_BIAS_MASK (0xff << 8) +# define RADEON_LOD_BIAS_SHIFT 8 +# define RADEON_MAX_MIP_LEVEL_MASK (0x0f << 16) +# define RADEON_MAX_MIP_LEVEL_SHIFT 16 +# define RADEON_YUV_TO_RGB (1 << 20) +# define RADEON_YUV_TEMPERATURE_COOL (0 << 21) +# define RADEON_YUV_TEMPERATURE_HOT (1 << 21) +# define RADEON_YUV_TEMPERATURE_MASK (1 << 21) +# define RADEON_WRAPEN_S (1 << 22) +# define RADEON_CLAMP_S_WRAP (0 << 23) +# define RADEON_CLAMP_S_MIRROR (1 << 23) +# define RADEON_CLAMP_S_CLAMP_LAST (2 << 23) +# define RADEON_CLAMP_S_MIRROR_CLAMP_LAST (3 << 23) +# define RADEON_CLAMP_S_CLAMP_BORDER (4 << 23) +# define RADEON_CLAMP_S_MIRROR_CLAMP_BORDER (5 << 23) +# define RADEON_CLAMP_S_CLAMP_GL (6 << 23) +# define RADEON_CLAMP_S_MIRROR_CLAMP_GL (7 << 23) +# define RADEON_CLAMP_S_MASK (7 << 23) +# define RADEON_WRAPEN_T (1 << 26) +# define RADEON_CLAMP_T_WRAP (0 << 27) +# define RADEON_CLAMP_T_MIRROR (1 << 27) +# define RADEON_CLAMP_T_CLAMP_LAST (2 << 27) +# define RADEON_CLAMP_T_MIRROR_CLAMP_LAST (3 << 27) +# define RADEON_CLAMP_T_CLAMP_BORDER (4 << 27) +# define RADEON_CLAMP_T_MIRROR_CLAMP_BORDER (5 << 27) +# define RADEON_CLAMP_T_CLAMP_GL (6 << 27) +# define RADEON_CLAMP_T_MIRROR_CLAMP_GL (7 << 27) +# define RADEON_CLAMP_T_MASK (7 << 27) +# define RADEON_BORDER_MODE_OGL (0 << 31) +# define RADEON_BORDER_MODE_D3D (1 << 31) +#define RADEON_PP_TXFORMAT_0 0x1c58 +#define RADEON_PP_TXFORMAT_1 0x1c70 +#define RADEON_PP_TXFORMAT_2 0x1c88 +# define RADEON_TXFORMAT_I8 (0 << 0) +# define RADEON_TXFORMAT_AI88 (1 << 0) +# define RADEON_TXFORMAT_RGB332 (2 << 0) +# define RADEON_TXFORMAT_ARGB1555 (3 << 0) +# define RADEON_TXFORMAT_RGB565 (4 << 0) +# define RADEON_TXFORMAT_ARGB4444 (5 << 0) +# define RADEON_TXFORMAT_ARGB8888 (6 << 0) +# define RADEON_TXFORMAT_RGBA8888 (7 << 0) +# define RADEON_TXFORMAT_Y8 (8 << 0) +# define RADEON_TXFORMAT_VYUY422 (10 << 0) +# define RADEON_TXFORMAT_YVYU422 (11 << 0) +# define RADEON_TXFORMAT_DXT1 (12 << 0) +# define RADEON_TXFORMAT_DXT23 (14 << 0) +# define RADEON_TXFORMAT_DXT45 (15 << 0) +# define RADEON_TXFORMAT_SHADOW16 (16 << 0) +# define RADEON_TXFORMAT_SHADOW32 (17 << 0) +# define RADEON_TXFORMAT_DUDV88 (18 << 0) +# define RADEON_TXFORMAT_LDUDV655 (19 << 0) +# define RADEON_TXFORMAT_LDUDUV8888 (20 << 0) +# define RADEON_TXFORMAT_FORMAT_MASK (31 << 0) +# define RADEON_TXFORMAT_FORMAT_SHIFT 0 +# define RADEON_TXFORMAT_APPLE_YUV_MODE (1 << 5) +# define RADEON_TXFORMAT_ALPHA_IN_MAP (1 << 6) +# define RADEON_TXFORMAT_NON_POWER2 (1 << 7) +# define RADEON_TXFORMAT_WIDTH_MASK (15 << 8) +# define RADEON_TXFORMAT_WIDTH_SHIFT 8 +# define RADEON_TXFORMAT_HEIGHT_MASK (15 << 12) +# define RADEON_TXFORMAT_HEIGHT_SHIFT 12 +# define RADEON_TXFORMAT_F5_WIDTH_MASK (15 << 16) +# define RADEON_TXFORMAT_F5_WIDTH_SHIFT 16 +# define RADEON_TXFORMAT_F5_HEIGHT_MASK (15 << 20) +# define RADEON_TXFORMAT_F5_HEIGHT_SHIFT 20 +# define RADEON_TXFORMAT_ST_ROUTE_STQ0 (0 << 24) +# define RADEON_TXFORMAT_ST_ROUTE_MASK (3 << 24) +# define RADEON_TXFORMAT_ST_ROUTE_STQ1 (1 << 24) +# define RADEON_TXFORMAT_ST_ROUTE_STQ2 (2 << 24) +# define RADEON_TXFORMAT_ENDIAN_NO_SWAP (0 << 26) +# define RADEON_TXFORMAT_ENDIAN_16BPP_SWAP (1 << 26) +# define RADEON_TXFORMAT_ENDIAN_32BPP_SWAP (2 << 26) +# define RADEON_TXFORMAT_ENDIAN_HALFDW_SWAP (3 << 26) +# define RADEON_TXFORMAT_ALPHA_MASK_ENABLE (1 << 28) +# define RADEON_TXFORMAT_CHROMA_KEY_ENABLE (1 << 29) +# define RADEON_TXFORMAT_CUBIC_MAP_ENABLE (1 << 30) +# define RADEON_TXFORMAT_PERSPECTIVE_ENABLE (1 << 31) +#define RADEON_PP_CUBIC_FACES_0 0x1d24 +#define RADEON_PP_CUBIC_FACES_1 0x1d28 +#define RADEON_PP_CUBIC_FACES_2 0x1d2c +# define RADEON_FACE_WIDTH_1_SHIFT 0 +# define RADEON_FACE_HEIGHT_1_SHIFT 4 +# define RADEON_FACE_WIDTH_1_MASK (0xf << 0) +# define RADEON_FACE_HEIGHT_1_MASK (0xf << 4) +# define RADEON_FACE_WIDTH_2_SHIFT 8 +# define RADEON_FACE_HEIGHT_2_SHIFT 12 +# define RADEON_FACE_WIDTH_2_MASK (0xf << 8) +# define RADEON_FACE_HEIGHT_2_MASK (0xf << 12) +# define RADEON_FACE_WIDTH_3_SHIFT 16 +# define RADEON_FACE_HEIGHT_3_SHIFT 20 +# define RADEON_FACE_WIDTH_3_MASK (0xf << 16) +# define RADEON_FACE_HEIGHT_3_MASK (0xf << 20) +# define RADEON_FACE_WIDTH_4_SHIFT 24 +# define RADEON_FACE_HEIGHT_4_SHIFT 28 +# define RADEON_FACE_WIDTH_4_MASK (0xf << 24) +# define RADEON_FACE_HEIGHT_4_MASK (0xf << 28) + +#define RADEON_PP_TXOFFSET_0 0x1c5c +#define RADEON_PP_TXOFFSET_1 0x1c74 +#define RADEON_PP_TXOFFSET_2 0x1c8c +# define RADEON_TXO_ENDIAN_NO_SWAP (0 << 0) +# define RADEON_TXO_ENDIAN_BYTE_SWAP (1 << 0) +# define RADEON_TXO_ENDIAN_WORD_SWAP (2 << 0) +# define RADEON_TXO_ENDIAN_HALFDW_SWAP (3 << 0) +# define RADEON_TXO_MACRO_LINEAR (0 << 2) +# define RADEON_TXO_MACRO_TILE (1 << 2) +# define RADEON_TXO_MICRO_LINEAR (0 << 3) +# define RADEON_TXO_MICRO_TILE_X2 (1 << 3) +# define RADEON_TXO_MICRO_TILE_OPT (2 << 3) +# define RADEON_TXO_OFFSET_MASK 0xffffffe0 +# define RADEON_TXO_OFFSET_SHIFT 5 + +#define RADEON_PP_CUBIC_OFFSET_T0_0 0x1dd0 /* bits [31:5] */ +#define RADEON_PP_CUBIC_OFFSET_T0_1 0x1dd4 +#define RADEON_PP_CUBIC_OFFSET_T0_2 0x1dd8 +#define RADEON_PP_CUBIC_OFFSET_T0_3 0x1ddc +#define RADEON_PP_CUBIC_OFFSET_T0_4 0x1de0 +#define RADEON_PP_CUBIC_OFFSET_T1_0 0x1e00 +#define RADEON_PP_CUBIC_OFFSET_T1_1 0x1e04 +#define RADEON_PP_CUBIC_OFFSET_T1_2 0x1e08 +#define RADEON_PP_CUBIC_OFFSET_T1_3 0x1e0c +#define RADEON_PP_CUBIC_OFFSET_T1_4 0x1e10 +#define RADEON_PP_CUBIC_OFFSET_T2_0 0x1e14 +#define RADEON_PP_CUBIC_OFFSET_T2_1 0x1e18 +#define RADEON_PP_CUBIC_OFFSET_T2_2 0x1e1c +#define RADEON_PP_CUBIC_OFFSET_T2_3 0x1e20 +#define RADEON_PP_CUBIC_OFFSET_T2_4 0x1e24 + +#define RADEON_PP_TEX_SIZE_0 0x1d04 /* NPOT */ +#define RADEON_PP_TEX_SIZE_1 0x1d0c +#define RADEON_PP_TEX_SIZE_2 0x1d14 +# define RADEON_TEX_USIZE_MASK (0x7ff << 0) +# define RADEON_TEX_USIZE_SHIFT 0 +# define RADEON_TEX_VSIZE_MASK (0x7ff << 16) +# define RADEON_TEX_VSIZE_SHIFT 16 +# define RADEON_SIGNED_RGB_MASK (1 << 30) +# define RADEON_SIGNED_RGB_SHIFT 30 +# define RADEON_SIGNED_ALPHA_MASK (1 << 31) +# define RADEON_SIGNED_ALPHA_SHIFT 31 +#define RADEON_PP_TEX_PITCH_0 0x1d08 /* NPOT */ +#define RADEON_PP_TEX_PITCH_1 0x1d10 /* NPOT */ +#define RADEON_PP_TEX_PITCH_2 0x1d18 /* NPOT */ +/* note: bits 13-5: 32 byte aligned stride of texture map */ + +#define RADEON_PP_TXCBLEND_0 0x1c60 +#define RADEON_PP_TXCBLEND_1 0x1c78 +#define RADEON_PP_TXCBLEND_2 0x1c90 +# define RADEON_COLOR_ARG_A_SHIFT 0 +# define RADEON_COLOR_ARG_A_MASK (0x1f << 0) +# define RADEON_COLOR_ARG_A_ZERO (0 << 0) +# define RADEON_COLOR_ARG_A_CURRENT_COLOR (2 << 0) +# define RADEON_COLOR_ARG_A_CURRENT_ALPHA (3 << 0) +# define RADEON_COLOR_ARG_A_DIFFUSE_COLOR (4 << 0) +# define RADEON_COLOR_ARG_A_DIFFUSE_ALPHA (5 << 0) +# define RADEON_COLOR_ARG_A_SPECULAR_COLOR (6 << 0) +# define RADEON_COLOR_ARG_A_SPECULAR_ALPHA (7 << 0) +# define RADEON_COLOR_ARG_A_TFACTOR_COLOR (8 << 0) +# define RADEON_COLOR_ARG_A_TFACTOR_ALPHA (9 << 0) +# define RADEON_COLOR_ARG_A_T0_COLOR (10 << 0) +# define RADEON_COLOR_ARG_A_T0_ALPHA (11 << 0) +# define RADEON_COLOR_ARG_A_T1_COLOR (12 << 0) +# define RADEON_COLOR_ARG_A_T1_ALPHA (13 << 0) +# define RADEON_COLOR_ARG_A_T2_COLOR (14 << 0) +# define RADEON_COLOR_ARG_A_T2_ALPHA (15 << 0) +# define RADEON_COLOR_ARG_A_T3_COLOR (16 << 0) +# define RADEON_COLOR_ARG_A_T3_ALPHA (17 << 0) +# define RADEON_COLOR_ARG_B_SHIFT 5 +# define RADEON_COLOR_ARG_B_MASK (0x1f << 5) +# define RADEON_COLOR_ARG_B_ZERO (0 << 5) +# define RADEON_COLOR_ARG_B_CURRENT_COLOR (2 << 5) +# define RADEON_COLOR_ARG_B_CURRENT_ALPHA (3 << 5) +# define RADEON_COLOR_ARG_B_DIFFUSE_COLOR (4 << 5) +# define RADEON_COLOR_ARG_B_DIFFUSE_ALPHA (5 << 5) +# define RADEON_COLOR_ARG_B_SPECULAR_COLOR (6 << 5) +# define RADEON_COLOR_ARG_B_SPECULAR_ALPHA (7 << 5) +# define RADEON_COLOR_ARG_B_TFACTOR_COLOR (8 << 5) +# define RADEON_COLOR_ARG_B_TFACTOR_ALPHA (9 << 5) +# define RADEON_COLOR_ARG_B_T0_COLOR (10 << 5) +# define RADEON_COLOR_ARG_B_T0_ALPHA (11 << 5) +# define RADEON_COLOR_ARG_B_T1_COLOR (12 << 5) +# define RADEON_COLOR_ARG_B_T1_ALPHA (13 << 5) +# define RADEON_COLOR_ARG_B_T2_COLOR (14 << 5) +# define RADEON_COLOR_ARG_B_T2_ALPHA (15 << 5) +# define RADEON_COLOR_ARG_B_T3_COLOR (16 << 5) +# define RADEON_COLOR_ARG_B_T3_ALPHA (17 << 5) +# define RADEON_COLOR_ARG_C_SHIFT 10 +# define RADEON_COLOR_ARG_C_MASK (0x1f << 10) +# define RADEON_COLOR_ARG_C_ZERO (0 << 10) +# define RADEON_COLOR_ARG_C_CURRENT_COLOR (2 << 10) +# define RADEON_COLOR_ARG_C_CURRENT_ALPHA (3 << 10) +# define RADEON_COLOR_ARG_C_DIFFUSE_COLOR (4 << 10) +# define RADEON_COLOR_ARG_C_DIFFUSE_ALPHA (5 << 10) +# define RADEON_COLOR_ARG_C_SPECULAR_COLOR (6 << 10) +# define RADEON_COLOR_ARG_C_SPECULAR_ALPHA (7 << 10) +# define RADEON_COLOR_ARG_C_TFACTOR_COLOR (8 << 10) +# define RADEON_COLOR_ARG_C_TFACTOR_ALPHA (9 << 10) +# define RADEON_COLOR_ARG_C_T0_COLOR (10 << 10) +# define RADEON_COLOR_ARG_C_T0_ALPHA (11 << 10) +# define RADEON_COLOR_ARG_C_T1_COLOR (12 << 10) +# define RADEON_COLOR_ARG_C_T1_ALPHA (13 << 10) +# define RADEON_COLOR_ARG_C_T2_COLOR (14 << 10) +# define RADEON_COLOR_ARG_C_T2_ALPHA (15 << 10) +# define RADEON_COLOR_ARG_C_T3_COLOR (16 << 10) +# define RADEON_COLOR_ARG_C_T3_ALPHA (17 << 10) +# define RADEON_COMP_ARG_A (1 << 15) +# define RADEON_COMP_ARG_A_SHIFT 15 +# define RADEON_COMP_ARG_B (1 << 16) +# define RADEON_COMP_ARG_B_SHIFT 16 +# define RADEON_COMP_ARG_C (1 << 17) +# define RADEON_COMP_ARG_C_SHIFT 17 +# define RADEON_BLEND_CTL_MASK (7 << 18) +# define RADEON_BLEND_CTL_ADD (0 << 18) +# define RADEON_BLEND_CTL_SUBTRACT (1 << 18) +# define RADEON_BLEND_CTL_ADDSIGNED (2 << 18) +# define RADEON_BLEND_CTL_BLEND (3 << 18) +# define RADEON_BLEND_CTL_DOT3 (4 << 18) +# define RADEON_SCALE_SHIFT 21 +# define RADEON_SCALE_MASK (3 << 21) +# define RADEON_SCALE_1X (0 << 21) +# define RADEON_SCALE_2X (1 << 21) +# define RADEON_SCALE_4X (2 << 21) +# define RADEON_CLAMP_TX (1 << 23) +# define RADEON_T0_EQ_TCUR (1 << 24) +# define RADEON_T1_EQ_TCUR (1 << 25) +# define RADEON_T2_EQ_TCUR (1 << 26) +# define RADEON_T3_EQ_TCUR (1 << 27) +# define RADEON_COLOR_ARG_MASK 0x1f +# define RADEON_COMP_ARG_SHIFT 15 +#define RADEON_PP_TXABLEND_0 0x1c64 +#define RADEON_PP_TXABLEND_1 0x1c7c +#define RADEON_PP_TXABLEND_2 0x1c94 +# define RADEON_ALPHA_ARG_A_SHIFT 0 +# define RADEON_ALPHA_ARG_A_MASK (0xf << 0) +# define RADEON_ALPHA_ARG_A_ZERO (0 << 0) +# define RADEON_ALPHA_ARG_A_CURRENT_ALPHA (1 << 0) +# define RADEON_ALPHA_ARG_A_DIFFUSE_ALPHA (2 << 0) +# define RADEON_ALPHA_ARG_A_SPECULAR_ALPHA (3 << 0) +# define RADEON_ALPHA_ARG_A_TFACTOR_ALPHA (4 << 0) +# define RADEON_ALPHA_ARG_A_T0_ALPHA (5 << 0) +# define RADEON_ALPHA_ARG_A_T1_ALPHA (6 << 0) +# define RADEON_ALPHA_ARG_A_T2_ALPHA (7 << 0) +# define RADEON_ALPHA_ARG_A_T3_ALPHA (8 << 0) +# define RADEON_ALPHA_ARG_B_SHIFT 4 +# define RADEON_ALPHA_ARG_B_MASK (0xf << 4) +# define RADEON_ALPHA_ARG_B_ZERO (0 << 4) +# define RADEON_ALPHA_ARG_B_CURRENT_ALPHA (1 << 4) +# define RADEON_ALPHA_ARG_B_DIFFUSE_ALPHA (2 << 4) +# define RADEON_ALPHA_ARG_B_SPECULAR_ALPHA (3 << 4) +# define RADEON_ALPHA_ARG_B_TFACTOR_ALPHA (4 << 4) +# define RADEON_ALPHA_ARG_B_T0_ALPHA (5 << 4) +# define RADEON_ALPHA_ARG_B_T1_ALPHA (6 << 4) +# define RADEON_ALPHA_ARG_B_T2_ALPHA (7 << 4) +# define RADEON_ALPHA_ARG_B_T3_ALPHA (8 << 4) +# define RADEON_ALPHA_ARG_C_SHIFT 8 +# define RADEON_ALPHA_ARG_C_MASK (0xf << 8) +# define RADEON_ALPHA_ARG_C_ZERO (0 << 8) +# define RADEON_ALPHA_ARG_C_CURRENT_ALPHA (1 << 8) +# define RADEON_ALPHA_ARG_C_DIFFUSE_ALPHA (2 << 8) +# define RADEON_ALPHA_ARG_C_SPECULAR_ALPHA (3 << 8) +# define RADEON_ALPHA_ARG_C_TFACTOR_ALPHA (4 << 8) +# define RADEON_ALPHA_ARG_C_T0_ALPHA (5 << 8) +# define RADEON_ALPHA_ARG_C_T1_ALPHA (6 << 8) +# define RADEON_ALPHA_ARG_C_T2_ALPHA (7 << 8) +# define RADEON_ALPHA_ARG_C_T3_ALPHA (8 << 8) +# define RADEON_DOT_ALPHA_DONT_REPLICATE (1 << 9) +# define RADEON_ALPHA_ARG_MASK 0xf + +#define RADEON_PP_TFACTOR_0 0x1c68 +#define RADEON_PP_TFACTOR_1 0x1c80 +#define RADEON_PP_TFACTOR_2 0x1c98 + +#define RADEON_RB3D_BLENDCNTL 0x1c20 +# define RADEON_COMB_FCN_MASK (3 << 12) +# define RADEON_COMB_FCN_ADD_CLAMP (0 << 12) +# define RADEON_COMB_FCN_ADD_NOCLAMP (1 << 12) +# define RADEON_COMB_FCN_SUB_CLAMP (2 << 12) +# define RADEON_COMB_FCN_SUB_NOCLAMP (3 << 12) +# define RADEON_SRC_BLEND_GL_ZERO (32 << 16) +# define RADEON_SRC_BLEND_GL_ONE (33 << 16) +# define RADEON_SRC_BLEND_GL_SRC_COLOR (34 << 16) +# define RADEON_SRC_BLEND_GL_ONE_MINUS_SRC_COLOR (35 << 16) +# define RADEON_SRC_BLEND_GL_DST_COLOR (36 << 16) +# define RADEON_SRC_BLEND_GL_ONE_MINUS_DST_COLOR (37 << 16) +# define RADEON_SRC_BLEND_GL_SRC_ALPHA (38 << 16) +# define RADEON_SRC_BLEND_GL_ONE_MINUS_SRC_ALPHA (39 << 16) +# define RADEON_SRC_BLEND_GL_DST_ALPHA (40 << 16) +# define RADEON_SRC_BLEND_GL_ONE_MINUS_DST_ALPHA (41 << 16) +# define RADEON_SRC_BLEND_GL_SRC_ALPHA_SATURATE (42 << 16) +# define RADEON_SRC_BLEND_MASK (63 << 16) +# define RADEON_DST_BLEND_GL_ZERO (32 << 24) +# define RADEON_DST_BLEND_GL_ONE (33 << 24) +# define RADEON_DST_BLEND_GL_SRC_COLOR (34 << 24) +# define RADEON_DST_BLEND_GL_ONE_MINUS_SRC_COLOR (35 << 24) +# define RADEON_DST_BLEND_GL_DST_COLOR (36 << 24) +# define RADEON_DST_BLEND_GL_ONE_MINUS_DST_COLOR (37 << 24) +# define RADEON_DST_BLEND_GL_SRC_ALPHA (38 << 24) +# define RADEON_DST_BLEND_GL_ONE_MINUS_SRC_ALPHA (39 << 24) +# define RADEON_DST_BLEND_GL_DST_ALPHA (40 << 24) +# define RADEON_DST_BLEND_GL_ONE_MINUS_DST_ALPHA (41 << 24) +# define RADEON_DST_BLEND_MASK (63 << 24) +#define RADEON_RB3D_CNTL 0x1c3c +# define RADEON_ALPHA_BLEND_ENABLE (1 << 0) +# define RADEON_PLANE_MASK_ENABLE (1 << 1) +# define RADEON_DITHER_ENABLE (1 << 2) +# define RADEON_ROUND_ENABLE (1 << 3) +# define RADEON_SCALE_DITHER_ENABLE (1 << 4) +# define RADEON_DITHER_INIT (1 << 5) +# define RADEON_ROP_ENABLE (1 << 6) +# define RADEON_STENCIL_ENABLE (1 << 7) +# define RADEON_Z_ENABLE (1 << 8) +# define RADEON_DEPTHXY_OFFSET_ENABLE (1 << 9) +# define RADEON_RB3D_COLOR_FORMAT_SHIFT 10 + +# define RADEON_COLOR_FORMAT_ARGB1555 3 +# define RADEON_COLOR_FORMAT_RGB565 4 +# define RADEON_COLOR_FORMAT_ARGB8888 6 +# define RADEON_COLOR_FORMAT_RGB332 7 +# define RADEON_COLOR_FORMAT_Y8 8 +# define RADEON_COLOR_FORMAT_RGB8 9 +# define RADEON_COLOR_FORMAT_YUV422_VYUY 11 +# define RADEON_COLOR_FORMAT_YUV422_YVYU 12 +# define RADEON_COLOR_FORMAT_aYUV444 14 +# define RADEON_COLOR_FORMAT_ARGB4444 15 + +# define RADEON_CLRCMP_FLIP_ENABLE (1 << 14) +#define RADEON_RB3D_COLOROFFSET 0x1c40 +# define RADEON_COLOROFFSET_MASK 0xfffffff0 +#define RADEON_RB3D_COLORPITCH 0x1c48 +# define RADEON_COLORPITCH_MASK 0x000001ff8 +# define RADEON_COLOR_TILE_ENABLE (1 << 16) +# define RADEON_COLOR_MICROTILE_ENABLE (1 << 17) +# define RADEON_COLOR_ENDIAN_NO_SWAP (0 << 18) +# define RADEON_COLOR_ENDIAN_WORD_SWAP (1 << 18) +# define RADEON_COLOR_ENDIAN_DWORD_SWAP (2 << 18) +#define RADEON_RB3D_DEPTHOFFSET 0x1c24 +#define RADEON_RB3D_DEPTHPITCH 0x1c28 +# define RADEON_DEPTHPITCH_MASK 0x00001ff8 +# define RADEON_DEPTH_ENDIAN_NO_SWAP (0 << 18) +# define RADEON_DEPTH_ENDIAN_WORD_SWAP (1 << 18) +# define RADEON_DEPTH_ENDIAN_DWORD_SWAP (2 << 18) +#define RADEON_RB3D_PLANEMASK 0x1d84 +#define RADEON_RB3D_ROPCNTL 0x1d80 +# define RADEON_ROP_MASK (15 << 8) +# define RADEON_ROP_CLEAR (0 << 8) +# define RADEON_ROP_NOR (1 << 8) +# define RADEON_ROP_AND_INVERTED (2 << 8) +# define RADEON_ROP_COPY_INVERTED (3 << 8) +# define RADEON_ROP_AND_REVERSE (4 << 8) +# define RADEON_ROP_INVERT (5 << 8) +# define RADEON_ROP_XOR (6 << 8) +# define RADEON_ROP_NAND (7 << 8) +# define RADEON_ROP_AND (8 << 8) +# define RADEON_ROP_EQUIV (9 << 8) +# define RADEON_ROP_NOOP (10 << 8) +# define RADEON_ROP_OR_INVERTED (11 << 8) +# define RADEON_ROP_COPY (12 << 8) +# define RADEON_ROP_OR_REVERSE (13 << 8) +# define RADEON_ROP_OR (14 << 8) +# define RADEON_ROP_SET (15 << 8) +#define RADEON_RB3D_STENCILREFMASK 0x1d7c +# define RADEON_STENCIL_REF_SHIFT 0 +# define RADEON_STENCIL_REF_MASK (0xff << 0) +# define RADEON_STENCIL_MASK_SHIFT 16 +# define RADEON_STENCIL_VALUE_MASK (0xff << 16) +# define RADEON_STENCIL_WRITEMASK_SHIFT 24 +# define RADEON_STENCIL_WRITE_MASK (0xff << 24) +#define RADEON_RB3D_ZSTENCILCNTL 0x1c2c +# define RADEON_DEPTH_FORMAT_MASK (0xf << 0) +# define RADEON_DEPTH_FORMAT_16BIT_INT_Z (0 << 0) +# define RADEON_DEPTH_FORMAT_24BIT_INT_Z (2 << 0) +# define RADEON_DEPTH_FORMAT_24BIT_FLOAT_Z (3 << 0) +# define RADEON_DEPTH_FORMAT_32BIT_INT_Z (4 << 0) +# define RADEON_DEPTH_FORMAT_32BIT_FLOAT_Z (5 << 0) +# define RADEON_DEPTH_FORMAT_16BIT_FLOAT_W (7 << 0) +# define RADEON_DEPTH_FORMAT_24BIT_FLOAT_W (9 << 0) +# define RADEON_DEPTH_FORMAT_32BIT_FLOAT_W (11 << 0) +# define RADEON_Z_TEST_NEVER (0 << 4) +# define RADEON_Z_TEST_LESS (1 << 4) +# define RADEON_Z_TEST_LEQUAL (2 << 4) +# define RADEON_Z_TEST_EQUAL (3 << 4) +# define RADEON_Z_TEST_GEQUAL (4 << 4) +# define RADEON_Z_TEST_GREATER (5 << 4) +# define RADEON_Z_TEST_NEQUAL (6 << 4) +# define RADEON_Z_TEST_ALWAYS (7 << 4) +# define RADEON_Z_TEST_MASK (7 << 4) +# define RADEON_STENCIL_TEST_NEVER (0 << 12) +# define RADEON_STENCIL_TEST_LESS (1 << 12) +# define RADEON_STENCIL_TEST_LEQUAL (2 << 12) +# define RADEON_STENCIL_TEST_EQUAL (3 << 12) +# define RADEON_STENCIL_TEST_GEQUAL (4 << 12) +# define RADEON_STENCIL_TEST_GREATER (5 << 12) +# define RADEON_STENCIL_TEST_NEQUAL (6 << 12) +# define RADEON_STENCIL_TEST_ALWAYS (7 << 12) +# define RADEON_STENCIL_TEST_MASK (0x7 << 12) +# define RADEON_STENCIL_FAIL_KEEP (0 << 16) +# define RADEON_STENCIL_FAIL_ZERO (1 << 16) +# define RADEON_STENCIL_FAIL_REPLACE (2 << 16) +# define RADEON_STENCIL_FAIL_INC (3 << 16) +# define RADEON_STENCIL_FAIL_DEC (4 << 16) +# define RADEON_STENCIL_FAIL_INVERT (5 << 16) +# define RADEON_STENCIL_FAIL_MASK (0x7 << 16) +# define RADEON_STENCIL_ZPASS_KEEP (0 << 20) +# define RADEON_STENCIL_ZPASS_ZERO (1 << 20) +# define RADEON_STENCIL_ZPASS_REPLACE (2 << 20) +# define RADEON_STENCIL_ZPASS_INC (3 << 20) +# define RADEON_STENCIL_ZPASS_DEC (4 << 20) +# define RADEON_STENCIL_ZPASS_INVERT (5 << 20) +# define RADEON_STENCIL_ZPASS_MASK (0x7 << 20) +# define RADEON_STENCIL_ZFAIL_KEEP (0 << 24) +# define RADEON_STENCIL_ZFAIL_ZERO (1 << 24) +# define RADEON_STENCIL_ZFAIL_REPLACE (2 << 24) +# define RADEON_STENCIL_ZFAIL_INC (3 << 24) +# define RADEON_STENCIL_ZFAIL_DEC (4 << 24) +# define RADEON_STENCIL_ZFAIL_INVERT (5 << 24) +# define RADEON_STENCIL_ZFAIL_MASK (0x7 << 24) +# define RADEON_Z_COMPRESSION_ENABLE (1 << 28) +# define RADEON_FORCE_Z_DIRTY (1 << 29) +# define RADEON_Z_WRITE_ENABLE (1 << 30) +#define RADEON_RE_LINE_PATTERN 0x1cd0 +# define RADEON_LINE_PATTERN_MASK 0x0000ffff +# define RADEON_LINE_REPEAT_COUNT_SHIFT 16 +# define RADEON_LINE_PATTERN_START_SHIFT 24 +# define RADEON_LINE_PATTERN_LITTLE_BIT_ORDER (0 << 28) +# define RADEON_LINE_PATTERN_BIG_BIT_ORDER (1 << 28) +# define RADEON_LINE_PATTERN_AUTO_RESET (1 << 29) +#define RADEON_RE_LINE_STATE 0x1cd4 +# define RADEON_LINE_CURRENT_PTR_SHIFT 0 +# define RADEON_LINE_CURRENT_COUNT_SHIFT 8 +#define RADEON_RE_MISC 0x26c4 +# define RADEON_STIPPLE_COORD_MASK 0x1f +# define RADEON_STIPPLE_X_OFFSET_SHIFT 0 +# define RADEON_STIPPLE_X_OFFSET_MASK (0x1f << 0) +# define RADEON_STIPPLE_Y_OFFSET_SHIFT 8 +# define RADEON_STIPPLE_Y_OFFSET_MASK (0x1f << 8) +# define RADEON_STIPPLE_LITTLE_BIT_ORDER (0 << 16) +# define RADEON_STIPPLE_BIG_BIT_ORDER (1 << 16) +#define RADEON_RE_SOLID_COLOR 0x1c1c +#define RADEON_RE_TOP_LEFT 0x26c0 +# define RADEON_RE_LEFT_SHIFT 0 +# define RADEON_RE_TOP_SHIFT 16 +#define RADEON_RE_WIDTH_HEIGHT 0x1c44 +# define RADEON_RE_WIDTH_SHIFT 0 +# define RADEON_RE_HEIGHT_SHIFT 16 + +#define RADEON_RB3D_ZPASS_DATA 0x3290 +#define RADEON_RB3D_ZPASS_ADDR 0x3294 + +#define RADEON_SE_CNTL 0x1c4c +# define RADEON_FFACE_CULL_CW (0 << 0) +# define RADEON_FFACE_CULL_CCW (1 << 0) +# define RADEON_FFACE_CULL_DIR_MASK (1 << 0) +# define RADEON_BFACE_CULL (0 << 1) +# define RADEON_BFACE_SOLID (3 << 1) +# define RADEON_FFACE_CULL (0 << 3) +# define RADEON_FFACE_SOLID (3 << 3) +# define RADEON_FFACE_CULL_MASK (3 << 3) +# define RADEON_BADVTX_CULL_DISABLE (1 << 5) +# define RADEON_FLAT_SHADE_VTX_0 (0 << 6) +# define RADEON_FLAT_SHADE_VTX_1 (1 << 6) +# define RADEON_FLAT_SHADE_VTX_2 (2 << 6) +# define RADEON_FLAT_SHADE_VTX_LAST (3 << 6) +# define RADEON_DIFFUSE_SHADE_SOLID (0 << 8) +# define RADEON_DIFFUSE_SHADE_FLAT (1 << 8) +# define RADEON_DIFFUSE_SHADE_GOURAUD (2 << 8) +# define RADEON_DIFFUSE_SHADE_MASK (3 << 8) +# define RADEON_ALPHA_SHADE_SOLID (0 << 10) +# define RADEON_ALPHA_SHADE_FLAT (1 << 10) +# define RADEON_ALPHA_SHADE_GOURAUD (2 << 10) +# define RADEON_ALPHA_SHADE_MASK (3 << 10) +# define RADEON_SPECULAR_SHADE_SOLID (0 << 12) +# define RADEON_SPECULAR_SHADE_FLAT (1 << 12) +# define RADEON_SPECULAR_SHADE_GOURAUD (2 << 12) +# define RADEON_SPECULAR_SHADE_MASK (3 << 12) +# define RADEON_FOG_SHADE_SOLID (0 << 14) +# define RADEON_FOG_SHADE_FLAT (1 << 14) +# define RADEON_FOG_SHADE_GOURAUD (2 << 14) +# define RADEON_FOG_SHADE_MASK (3 << 14) +# define RADEON_ZBIAS_ENABLE_POINT (1 << 16) +# define RADEON_ZBIAS_ENABLE_LINE (1 << 17) +# define RADEON_ZBIAS_ENABLE_TRI (1 << 18) +# define RADEON_WIDELINE_ENABLE (1 << 20) +# define RADEON_VPORT_XY_XFORM_ENABLE (1 << 24) +# define RADEON_VPORT_Z_XFORM_ENABLE (1 << 25) +# define RADEON_VTX_PIX_CENTER_D3D (0 << 27) +# define RADEON_VTX_PIX_CENTER_OGL (1 << 27) +# define RADEON_ROUND_MODE_TRUNC (0 << 28) +# define RADEON_ROUND_MODE_ROUND (1 << 28) +# define RADEON_ROUND_MODE_ROUND_EVEN (2 << 28) +# define RADEON_ROUND_MODE_ROUND_ODD (3 << 28) +# define RADEON_ROUND_PREC_16TH_PIX (0 << 30) +# define RADEON_ROUND_PREC_8TH_PIX (1 << 30) +# define RADEON_ROUND_PREC_4TH_PIX (2 << 30) +# define RADEON_ROUND_PREC_HALF_PIX (3 << 30) +#define R200_RE_CNTL 0x1c50 +# define R200_STIPPLE_ENABLE 0x1 +# define R200_SCISSOR_ENABLE 0x2 +# define R200_PATTERN_ENABLE 0x4 +# define R200_PERSPECTIVE_ENABLE 0x8 +# define R200_POINT_SMOOTH 0x20 +# define R200_VTX_STQ0_D3D 0x00010000 +# define R200_VTX_STQ1_D3D 0x00040000 +# define R200_VTX_STQ2_D3D 0x00100000 +# define R200_VTX_STQ3_D3D 0x00400000 +# define R200_VTX_STQ4_D3D 0x01000000 +# define R200_VTX_STQ5_D3D 0x04000000 +#define RADEON_SE_CNTL_STATUS 0x2140 +# define RADEON_VC_NO_SWAP (0 << 0) +# define RADEON_VC_16BIT_SWAP (1 << 0) +# define RADEON_VC_32BIT_SWAP (2 << 0) +# define RADEON_VC_HALF_DWORD_SWAP (3 << 0) +# define RADEON_TCL_BYPASS (1 << 8) +#define RADEON_SE_COORD_FMT 0x1c50 +# define RADEON_VTX_XY_PRE_MULT_1_OVER_W0 (1 << 0) +# define RADEON_VTX_Z_PRE_MULT_1_OVER_W0 (1 << 1) +# define RADEON_VTX_ST0_NONPARAMETRIC (1 << 8) +# define RADEON_VTX_ST1_NONPARAMETRIC (1 << 9) +# define RADEON_VTX_ST2_NONPARAMETRIC (1 << 10) +# define RADEON_VTX_ST3_NONPARAMETRIC (1 << 11) +# define RADEON_VTX_W0_NORMALIZE (1 << 12) +# define RADEON_VTX_W0_IS_NOT_1_OVER_W0 (1 << 16) +# define RADEON_VTX_ST0_PRE_MULT_1_OVER_W0 (1 << 17) +# define RADEON_VTX_ST1_PRE_MULT_1_OVER_W0 (1 << 19) +# define RADEON_VTX_ST2_PRE_MULT_1_OVER_W0 (1 << 21) +# define RADEON_VTX_ST3_PRE_MULT_1_OVER_W0 (1 << 23) +# define RADEON_TEX1_W_ROUTING_USE_W0 (0 << 26) +# define RADEON_TEX1_W_ROUTING_USE_Q1 (1 << 26) +#define RADEON_SE_LINE_WIDTH 0x1db8 +#define RADEON_SE_TCL_LIGHT_MODEL_CTL 0x226c +# define RADEON_LIGHTING_ENABLE (1 << 0) +# define RADEON_LIGHT_IN_MODELSPACE (1 << 1) +# define RADEON_LOCAL_VIEWER (1 << 2) +# define RADEON_NORMALIZE_NORMALS (1 << 3) +# define RADEON_RESCALE_NORMALS (1 << 4) +# define RADEON_SPECULAR_LIGHTS (1 << 5) +# define RADEON_DIFFUSE_SPECULAR_COMBINE (1 << 6) +# define RADEON_LIGHT_ALPHA (1 << 7) +# define RADEON_LOCAL_LIGHT_VEC_GL (1 << 8) +# define RADEON_LIGHT_NO_NORMAL_AMBIENT_ONLY (1 << 9) +# define RADEON_LM_SOURCE_STATE_PREMULT 0 +# define RADEON_LM_SOURCE_STATE_MULT 1 +# define RADEON_LM_SOURCE_VERTEX_DIFFUSE 2 +# define RADEON_LM_SOURCE_VERTEX_SPECULAR 3 +# define RADEON_EMISSIVE_SOURCE_SHIFT 16 +# define RADEON_AMBIENT_SOURCE_SHIFT 18 +# define RADEON_DIFFUSE_SOURCE_SHIFT 20 +# define RADEON_SPECULAR_SOURCE_SHIFT 22 +#define RADEON_SE_TCL_MATERIAL_AMBIENT_RED 0x2220 +#define RADEON_SE_TCL_MATERIAL_AMBIENT_GREEN 0x2224 +#define RADEON_SE_TCL_MATERIAL_AMBIENT_BLUE 0x2228 +#define RADEON_SE_TCL_MATERIAL_AMBIENT_ALPHA 0x222c +#define RADEON_SE_TCL_MATERIAL_DIFFUSE_RED 0x2230 +#define RADEON_SE_TCL_MATERIAL_DIFFUSE_GREEN 0x2234 +#define RADEON_SE_TCL_MATERIAL_DIFFUSE_BLUE 0x2238 +#define RADEON_SE_TCL_MATERIAL_DIFFUSE_ALPHA 0x223c +#define RADEON_SE_TCL_MATERIAL_EMMISSIVE_RED 0x2210 +#define RADEON_SE_TCL_MATERIAL_EMMISSIVE_GREEN 0x2214 +#define RADEON_SE_TCL_MATERIAL_EMMISSIVE_BLUE 0x2218 +#define RADEON_SE_TCL_MATERIAL_EMMISSIVE_ALPHA 0x221c +#define RADEON_SE_TCL_MATERIAL_SPECULAR_RED 0x2240 +#define RADEON_SE_TCL_MATERIAL_SPECULAR_GREEN 0x2244 +#define RADEON_SE_TCL_MATERIAL_SPECULAR_BLUE 0x2248 +#define RADEON_SE_TCL_MATERIAL_SPECULAR_ALPHA 0x224c +#define RADEON_SE_TCL_MATRIX_SELECT_0 0x225c +# define RADEON_MODELVIEW_0_SHIFT 0 +# define RADEON_MODELVIEW_1_SHIFT 4 +# define RADEON_MODELVIEW_2_SHIFT 8 +# define RADEON_MODELVIEW_3_SHIFT 12 +# define RADEON_IT_MODELVIEW_0_SHIFT 16 +# define RADEON_IT_MODELVIEW_1_SHIFT 20 +# define RADEON_IT_MODELVIEW_2_SHIFT 24 +# define RADEON_IT_MODELVIEW_3_SHIFT 28 +#define RADEON_SE_TCL_MATRIX_SELECT_1 0x2260 +# define RADEON_MODELPROJECT_0_SHIFT 0 +# define RADEON_MODELPROJECT_1_SHIFT 4 +# define RADEON_MODELPROJECT_2_SHIFT 8 +# define RADEON_MODELPROJECT_3_SHIFT 12 +# define RADEON_TEXMAT_0_SHIFT 16 +# define RADEON_TEXMAT_1_SHIFT 20 +# define RADEON_TEXMAT_2_SHIFT 24 +# define RADEON_TEXMAT_3_SHIFT 28 + + +#define RADEON_SE_TCL_OUTPUT_VTX_FMT 0x2254 +# define RADEON_TCL_VTX_W0 (1 << 0) +# define RADEON_TCL_VTX_FP_DIFFUSE (1 << 1) +# define RADEON_TCL_VTX_FP_ALPHA (1 << 2) +# define RADEON_TCL_VTX_PK_DIFFUSE (1 << 3) +# define RADEON_TCL_VTX_FP_SPEC (1 << 4) +# define RADEON_TCL_VTX_FP_FOG (1 << 5) +# define RADEON_TCL_VTX_PK_SPEC (1 << 6) +# define RADEON_TCL_VTX_ST0 (1 << 7) +# define RADEON_TCL_VTX_ST1 (1 << 8) +# define RADEON_TCL_VTX_Q1 (1 << 9) +# define RADEON_TCL_VTX_ST2 (1 << 10) +# define RADEON_TCL_VTX_Q2 (1 << 11) +# define RADEON_TCL_VTX_ST3 (1 << 12) +# define RADEON_TCL_VTX_Q3 (1 << 13) +# define RADEON_TCL_VTX_Q0 (1 << 14) +# define RADEON_TCL_VTX_WEIGHT_COUNT_SHIFT 15 +# define RADEON_TCL_VTX_NORM0 (1 << 18) +# define RADEON_TCL_VTX_XY1 (1 << 27) +# define RADEON_TCL_VTX_Z1 (1 << 28) +# define RADEON_TCL_VTX_W1 (1 << 29) +# define RADEON_TCL_VTX_NORM1 (1 << 30) +# define RADEON_TCL_VTX_Z0 (1 << 31) + +#define RADEON_SE_TCL_OUTPUT_VTX_SEL 0x2258 +# define RADEON_TCL_COMPUTE_XYZW (1 << 0) +# define RADEON_TCL_COMPUTE_DIFFUSE (1 << 1) +# define RADEON_TCL_COMPUTE_SPECULAR (1 << 2) +# define RADEON_TCL_FORCE_NAN_IF_COLOR_NAN (1 << 3) +# define RADEON_TCL_FORCE_INORDER_PROC (1 << 4) +# define RADEON_TCL_TEX_INPUT_TEX_0 0 +# define RADEON_TCL_TEX_INPUT_TEX_1 1 +# define RADEON_TCL_TEX_INPUT_TEX_2 2 +# define RADEON_TCL_TEX_INPUT_TEX_3 3 +# define RADEON_TCL_TEX_COMPUTED_TEX_0 8 +# define RADEON_TCL_TEX_COMPUTED_TEX_1 9 +# define RADEON_TCL_TEX_COMPUTED_TEX_2 10 +# define RADEON_TCL_TEX_COMPUTED_TEX_3 11 +# define RADEON_TCL_TEX_0_OUTPUT_SHIFT 16 +# define RADEON_TCL_TEX_1_OUTPUT_SHIFT 20 +# define RADEON_TCL_TEX_2_OUTPUT_SHIFT 24 +# define RADEON_TCL_TEX_3_OUTPUT_SHIFT 28 + +#define RADEON_SE_TCL_PER_LIGHT_CTL_0 0x2270 +# define RADEON_LIGHT_0_ENABLE (1 << 0) +# define RADEON_LIGHT_0_ENABLE_AMBIENT (1 << 1) +# define RADEON_LIGHT_0_ENABLE_SPECULAR (1 << 2) +# define RADEON_LIGHT_0_IS_LOCAL (1 << 3) +# define RADEON_LIGHT_0_IS_SPOT (1 << 4) +# define RADEON_LIGHT_0_DUAL_CONE (1 << 5) +# define RADEON_LIGHT_0_ENABLE_RANGE_ATTEN (1 << 6) +# define RADEON_LIGHT_0_CONSTANT_RANGE_ATTEN (1 << 7) +# define RADEON_LIGHT_0_SHIFT 0 +# define RADEON_LIGHT_1_ENABLE (1 << 16) +# define RADEON_LIGHT_1_ENABLE_AMBIENT (1 << 17) +# define RADEON_LIGHT_1_ENABLE_SPECULAR (1 << 18) +# define RADEON_LIGHT_1_IS_LOCAL (1 << 19) +# define RADEON_LIGHT_1_IS_SPOT (1 << 20) +# define RADEON_LIGHT_1_DUAL_CONE (1 << 21) +# define RADEON_LIGHT_1_ENABLE_RANGE_ATTEN (1 << 22) +# define RADEON_LIGHT_1_CONSTANT_RANGE_ATTEN (1 << 23) +# define RADEON_LIGHT_1_SHIFT 16 +#define RADEON_SE_TCL_PER_LIGHT_CTL_1 0x2274 +# define RADEON_LIGHT_2_SHIFT 0 +# define RADEON_LIGHT_3_SHIFT 16 +#define RADEON_SE_TCL_PER_LIGHT_CTL_2 0x2278 +# define RADEON_LIGHT_4_SHIFT 0 +# define RADEON_LIGHT_5_SHIFT 16 +#define RADEON_SE_TCL_PER_LIGHT_CTL_3 0x227c +# define RADEON_LIGHT_6_SHIFT 0 +# define RADEON_LIGHT_7_SHIFT 16 + +#define RADEON_SE_TCL_SHININESS 0x2250 + +#define RADEON_SE_TCL_TEXTURE_PROC_CTL 0x2268 +# define RADEON_TEXGEN_TEXMAT_0_ENABLE (1 << 0) +# define RADEON_TEXGEN_TEXMAT_1_ENABLE (1 << 1) +# define RADEON_TEXGEN_TEXMAT_2_ENABLE (1 << 2) +# define RADEON_TEXGEN_TEXMAT_3_ENABLE (1 << 3) +# define RADEON_TEXMAT_0_ENABLE (1 << 4) +# define RADEON_TEXMAT_1_ENABLE (1 << 5) +# define RADEON_TEXMAT_2_ENABLE (1 << 6) +# define RADEON_TEXMAT_3_ENABLE (1 << 7) +# define RADEON_TEXGEN_INPUT_MASK 0xf +# define RADEON_TEXGEN_INPUT_TEXCOORD_0 0 +# define RADEON_TEXGEN_INPUT_TEXCOORD_1 1 +# define RADEON_TEXGEN_INPUT_TEXCOORD_2 2 +# define RADEON_TEXGEN_INPUT_TEXCOORD_3 3 +# define RADEON_TEXGEN_INPUT_OBJ 4 +# define RADEON_TEXGEN_INPUT_EYE 5 +# define RADEON_TEXGEN_INPUT_EYE_NORMAL 6 +# define RADEON_TEXGEN_INPUT_EYE_REFLECT 7 +# define RADEON_TEXGEN_INPUT_EYE_NORMALIZED 8 +# define RADEON_TEXGEN_0_INPUT_SHIFT 16 +# define RADEON_TEXGEN_1_INPUT_SHIFT 20 +# define RADEON_TEXGEN_2_INPUT_SHIFT 24 +# define RADEON_TEXGEN_3_INPUT_SHIFT 28 + +#define RADEON_SE_TCL_UCP_VERT_BLEND_CTL 0x2264 +# define RADEON_UCP_IN_CLIP_SPACE (1 << 0) +# define RADEON_UCP_IN_MODEL_SPACE (1 << 1) +# define RADEON_UCP_ENABLE_0 (1 << 2) +# define RADEON_UCP_ENABLE_1 (1 << 3) +# define RADEON_UCP_ENABLE_2 (1 << 4) +# define RADEON_UCP_ENABLE_3 (1 << 5) +# define RADEON_UCP_ENABLE_4 (1 << 6) +# define RADEON_UCP_ENABLE_5 (1 << 7) +# define RADEON_TCL_FOG_MASK (3 << 8) +# define RADEON_TCL_FOG_DISABLE (0 << 8) +# define RADEON_TCL_FOG_EXP (1 << 8) +# define RADEON_TCL_FOG_EXP2 (2 << 8) +# define RADEON_TCL_FOG_LINEAR (3 << 8) +# define RADEON_RNG_BASED_FOG (1 << 10) +# define RADEON_LIGHT_TWOSIDE (1 << 11) +# define RADEON_BLEND_OP_COUNT_MASK (7 << 12) +# define RADEON_BLEND_OP_COUNT_SHIFT 12 +# define RADEON_POSITION_BLEND_OP_ENABLE (1 << 16) +# define RADEON_NORMAL_BLEND_OP_ENABLE (1 << 17) +# define RADEON_VERTEX_BLEND_SRC_0_PRIMARY (1 << 18) +# define RADEON_VERTEX_BLEND_SRC_0_SECONDARY (1 << 18) +# define RADEON_VERTEX_BLEND_SRC_1_PRIMARY (1 << 19) +# define RADEON_VERTEX_BLEND_SRC_1_SECONDARY (1 << 19) +# define RADEON_VERTEX_BLEND_SRC_2_PRIMARY (1 << 20) +# define RADEON_VERTEX_BLEND_SRC_2_SECONDARY (1 << 20) +# define RADEON_VERTEX_BLEND_SRC_3_PRIMARY (1 << 21) +# define RADEON_VERTEX_BLEND_SRC_3_SECONDARY (1 << 21) +# define RADEON_VERTEX_BLEND_WGT_MINUS_ONE (1 << 22) +# define RADEON_CULL_FRONT_IS_CW (0 << 28) +# define RADEON_CULL_FRONT_IS_CCW (1 << 28) +# define RADEON_CULL_FRONT (1 << 29) +# define RADEON_CULL_BACK (1 << 30) +# define RADEON_FORCE_W_TO_ONE (1 << 31) + +#define RADEON_SE_VPORT_XSCALE 0x1d98 +#define RADEON_SE_VPORT_XOFFSET 0x1d9c +#define RADEON_SE_VPORT_YSCALE 0x1da0 +#define RADEON_SE_VPORT_YOFFSET 0x1da4 +#define RADEON_SE_VPORT_ZSCALE 0x1da8 +#define RADEON_SE_VPORT_ZOFFSET 0x1dac +#define RADEON_SE_ZBIAS_FACTOR 0x1db0 +#define RADEON_SE_ZBIAS_CONSTANT 0x1db4 + +#define RADEON_SE_VTX_FMT 0x2080 +# define RADEON_SE_VTX_FMT_XY 0x00000000 +# define RADEON_SE_VTX_FMT_W0 0x00000001 +# define RADEON_SE_VTX_FMT_FPCOLOR 0x00000002 +# define RADEON_SE_VTX_FMT_FPALPHA 0x00000004 +# define RADEON_SE_VTX_FMT_PKCOLOR 0x00000008 +# define RADEON_SE_VTX_FMT_FPSPEC 0x00000010 +# define RADEON_SE_VTX_FMT_FPFOG 0x00000020 +# define RADEON_SE_VTX_FMT_PKSPEC 0x00000040 +# define RADEON_SE_VTX_FMT_ST0 0x00000080 +# define RADEON_SE_VTX_FMT_ST1 0x00000100 +# define RADEON_SE_VTX_FMT_Q1 0x00000200 +# define RADEON_SE_VTX_FMT_ST2 0x00000400 +# define RADEON_SE_VTX_FMT_Q2 0x00000800 +# define RADEON_SE_VTX_FMT_ST3 0x00001000 +# define RADEON_SE_VTX_FMT_Q3 0x00002000 +# define RADEON_SE_VTX_FMT_Q0 0x00004000 +# define RADEON_SE_VTX_FMT_BLND_WEIGHT_CNT_MASK 0x00038000 +# define RADEON_SE_VTX_FMT_N0 0x00040000 +# define RADEON_SE_VTX_FMT_XY1 0x08000000 +# define RADEON_SE_VTX_FMT_Z1 0x10000000 +# define RADEON_SE_VTX_FMT_W1 0x20000000 +# define RADEON_SE_VTX_FMT_N1 0x40000000 +# define RADEON_SE_VTX_FMT_Z 0x80000000 + +#define RADEON_SE_VF_CNTL 0x2084 +# define RADEON_VF_PRIM_TYPE_POINT_LIST 1 +# define RADEON_VF_PRIM_TYPE_LINE_LIST 2 +# define RADEON_VF_PRIM_TYPE_LINE_STRIP 3 +# define RADEON_VF_PRIM_TYPE_TRIANGLE_LIST 4 +# define RADEON_VF_PRIM_TYPE_TRIANGLE_FAN 5 +# define RADEON_VF_PRIM_TYPE_TRIANGLE_STRIP 6 +# define RADEON_VF_PRIM_TYPE_TRIANGLE_FLAG 7 +# define RADEON_VF_PRIM_TYPE_RECTANGLE_LIST 8 +# define RADEON_VF_PRIM_TYPE_POINT_LIST_3 9 +# define RADEON_VF_PRIM_TYPE_LINE_LIST_3 10 +# define RADEON_VF_PRIM_TYPE_SPIRIT_LIST 11 +# define RADEON_VF_PRIM_TYPE_LINE_LOOP 12 +# define RADEON_VF_PRIM_TYPE_QUAD_LIST 13 +# define RADEON_VF_PRIM_TYPE_QUAD_STRIP 14 +# define RADEON_VF_PRIM_TYPE_POLYGON 15 +# define RADEON_VF_PRIM_WALK_STATE (0<<4) +# define RADEON_VF_PRIM_WALK_INDEX (1<<4) +# define RADEON_VF_PRIM_WALK_LIST (2<<4) +# define RADEON_VF_PRIM_WALK_DATA (3<<4) +# define RADEON_VF_COLOR_ORDER_RGBA (1<<6) +# define RADEON_VF_RADEON_MODE (1<<8) +# define RADEON_VF_TCL_OUTPUT_CTL_ENA (1<<9) +# define RADEON_VF_PROG_STREAM_ENA (1<<10) +# define RADEON_VF_INDEX_SIZE_SHIFT 11 +# define RADEON_VF_NUM_VERTICES_SHIFT 16 + +#define RADEON_SE_PORT_DATA0 0x2000 + +#define R200_SE_VAP_CNTL 0x2080 +# define R200_VAP_TCL_ENABLE 0x00000001 +# define R200_VAP_SINGLE_BUF_STATE_ENABLE 0x00000010 +# define R200_VAP_FORCE_W_TO_ONE 0x00010000 +# define R200_VAP_D3D_TEX_DEFAULT 0x00020000 +# define R200_VAP_VF_MAX_VTX_NUM__SHIFT 18 +# define R200_VAP_VF_MAX_VTX_NUM (9 << 18) +# define R200_VAP_DX_CLIP_SPACE_DEF 0x00400000 +#define R200_VF_MAX_VTX_INDX 0x210c +#define R200_VF_MIN_VTX_INDX 0x2110 +#define R200_SE_VTE_CNTL 0x20b0 +# define R200_VPORT_X_SCALE_ENA 0x00000001 +# define R200_VPORT_X_OFFSET_ENA 0x00000002 +# define R200_VPORT_Y_SCALE_ENA 0x00000004 +# define R200_VPORT_Y_OFFSET_ENA 0x00000008 +# define R200_VPORT_Z_SCALE_ENA 0x00000010 +# define R200_VPORT_Z_OFFSET_ENA 0x00000020 +# define R200_VTX_XY_FMT 0x00000100 +# define R200_VTX_Z_FMT 0x00000200 +# define R200_VTX_W0_FMT 0x00000400 +# define R200_VTX_W0_NORMALIZE 0x00000800 +# define R200_VTX_ST_DENORMALIZED 0x00001000 +#define R200_SE_VAP_CNTL_STATUS 0x2140 +# define R200_VC_NO_SWAP (0 << 0) +# define R200_VC_16BIT_SWAP (1 << 0) +# define R200_VC_32BIT_SWAP (2 << 0) +#define R200_PP_TXFILTER_0 0x2c00 +#define R200_PP_TXFILTER_1 0x2c20 +#define R200_PP_TXFILTER_2 0x2c40 +#define R200_PP_TXFILTER_3 0x2c60 +#define R200_PP_TXFILTER_4 0x2c80 +#define R200_PP_TXFILTER_5 0x2ca0 +# define R200_MAG_FILTER_NEAREST (0 << 0) +# define R200_MAG_FILTER_LINEAR (1 << 0) +# define R200_MAG_FILTER_MASK (1 << 0) +# define R200_MIN_FILTER_NEAREST (0 << 1) +# define R200_MIN_FILTER_LINEAR (1 << 1) +# define R200_MIN_FILTER_NEAREST_MIP_NEAREST (2 << 1) +# define R200_MIN_FILTER_NEAREST_MIP_LINEAR (3 << 1) +# define R200_MIN_FILTER_LINEAR_MIP_NEAREST (6 << 1) +# define R200_MIN_FILTER_LINEAR_MIP_LINEAR (7 << 1) +# define R200_MIN_FILTER_ANISO_NEAREST (8 << 1) +# define R200_MIN_FILTER_ANISO_LINEAR (9 << 1) +# define R200_MIN_FILTER_ANISO_NEAREST_MIP_NEAREST (10 << 1) +# define R200_MIN_FILTER_ANISO_NEAREST_MIP_LINEAR (11 << 1) +# define R200_MIN_FILTER_MASK (15 << 1) +# define R200_MAX_ANISO_1_TO_1 (0 << 5) +# define R200_MAX_ANISO_2_TO_1 (1 << 5) +# define R200_MAX_ANISO_4_TO_1 (2 << 5) +# define R200_MAX_ANISO_8_TO_1 (3 << 5) +# define R200_MAX_ANISO_16_TO_1 (4 << 5) +# define R200_MAX_ANISO_MASK (7 << 5) +# define R200_MAX_MIP_LEVEL_MASK (0x0f << 16) +# define R200_MAX_MIP_LEVEL_SHIFT 16 +# define R200_YUV_TO_RGB (1 << 20) +# define R200_YUV_TEMPERATURE_COOL (0 << 21) +# define R200_YUV_TEMPERATURE_HOT (1 << 21) +# define R200_YUV_TEMPERATURE_MASK (1 << 21) +# define R200_WRAPEN_S (1 << 22) +# define R200_CLAMP_S_WRAP (0 << 23) +# define R200_CLAMP_S_MIRROR (1 << 23) +# define R200_CLAMP_S_CLAMP_LAST (2 << 23) +# define R200_CLAMP_S_MIRROR_CLAMP_LAST (3 << 23) +# define R200_CLAMP_S_CLAMP_BORDER (4 << 23) +# define R200_CLAMP_S_MIRROR_CLAMP_BORDER (5 << 23) +# define R200_CLAMP_S_CLAMP_GL (6 << 23) +# define R200_CLAMP_S_MIRROR_CLAMP_GL (7 << 23) +# define R200_CLAMP_S_MASK (7 << 23) +# define R200_WRAPEN_T (1 << 26) +# define R200_CLAMP_T_WRAP (0 << 27) +# define R200_CLAMP_T_MIRROR (1 << 27) +# define R200_CLAMP_T_CLAMP_LAST (2 << 27) +# define R200_CLAMP_T_MIRROR_CLAMP_LAST (3 << 27) +# define R200_CLAMP_T_CLAMP_BORDER (4 << 27) +# define R200_CLAMP_T_MIRROR_CLAMP_BORDER (5 << 27) +# define R200_CLAMP_T_CLAMP_GL (6 << 27) +# define R200_CLAMP_T_MIRROR_CLAMP_GL (7 << 27) +# define R200_CLAMP_T_MASK (7 << 27) +# define R200_KILL_LT_ZERO (1 << 30) +# define R200_BORDER_MODE_OGL (0 << 31) +# define R200_BORDER_MODE_D3D (1 << 31) +#define R200_PP_TXFORMAT_0 0x2c04 +#define R200_PP_TXFORMAT_1 0x2c24 +#define R200_PP_TXFORMAT_2 0x2c44 +#define R200_PP_TXFORMAT_3 0x2c64 +#define R200_PP_TXFORMAT_4 0x2c84 +#define R200_PP_TXFORMAT_5 0x2ca4 +# define R200_TXFORMAT_I8 (0 << 0) +# define R200_TXFORMAT_AI88 (1 << 0) +# define R200_TXFORMAT_RGB332 (2 << 0) +# define R200_TXFORMAT_ARGB1555 (3 << 0) +# define R200_TXFORMAT_RGB565 (4 << 0) +# define R200_TXFORMAT_ARGB4444 (5 << 0) +# define R200_TXFORMAT_ARGB8888 (6 << 0) +# define R200_TXFORMAT_RGBA8888 (7 << 0) +# define R200_TXFORMAT_Y8 (8 << 0) +# define R200_TXFORMAT_AVYU4444 (9 << 0) +# define R200_TXFORMAT_VYUY422 (10 << 0) +# define R200_TXFORMAT_YVYU422 (11 << 0) +# define R200_TXFORMAT_DXT1 (12 << 0) +# define R200_TXFORMAT_DXT23 (14 << 0) +# define R200_TXFORMAT_DXT45 (15 << 0) +# define R200_TXFORMAT_DVDU88 (18 << 0) +# define R200_TXFORMAT_LDVDU655 (19 << 0) +# define R200_TXFORMAT_LDVDU8888 (20 << 0) +# define R200_TXFORMAT_GR1616 (21 << 0) +# define R200_TXFORMAT_ABGR8888 (22 << 0) +# define R200_TXFORMAT_BGR111110 (23 << 0) +# define R200_TXFORMAT_FORMAT_MASK (31 << 0) +# define R200_TXFORMAT_FORMAT_SHIFT 0 +# define R200_TXFORMAT_ALPHA_IN_MAP (1 << 6) +# define R200_TXFORMAT_NON_POWER2 (1 << 7) +# define R200_TXFORMAT_WIDTH_MASK (15 << 8) +# define R200_TXFORMAT_WIDTH_SHIFT 8 +# define R200_TXFORMAT_HEIGHT_MASK (15 << 12) +# define R200_TXFORMAT_HEIGHT_SHIFT 12 +# define R200_TXFORMAT_F5_WIDTH_MASK (15 << 16) /* cube face 5 */ +# define R200_TXFORMAT_F5_WIDTH_SHIFT 16 +# define R200_TXFORMAT_F5_HEIGHT_MASK (15 << 20) +# define R200_TXFORMAT_F5_HEIGHT_SHIFT 20 +# define R200_TXFORMAT_ST_ROUTE_STQ0 (0 << 24) +# define R200_TXFORMAT_ST_ROUTE_STQ1 (1 << 24) +# define R200_TXFORMAT_ST_ROUTE_STQ2 (2 << 24) +# define R200_TXFORMAT_ST_ROUTE_STQ3 (3 << 24) +# define R200_TXFORMAT_ST_ROUTE_STQ4 (4 << 24) +# define R200_TXFORMAT_ST_ROUTE_STQ5 (5 << 24) +# define R200_TXFORMAT_ST_ROUTE_MASK (7 << 24) +# define R200_TXFORMAT_ST_ROUTE_SHIFT 24 +# define R200_TXFORMAT_LOOKUP_DISABLE (1 << 27) +# define R200_TXFORMAT_ALPHA_MASK_ENABLE (1 << 28) +# define R200_TXFORMAT_CHROMA_KEY_ENABLE (1 << 29) +# define R200_TXFORMAT_CUBIC_MAP_ENABLE (1 << 30) +#define R200_PP_TXFORMAT_X_0 0x2c08 +#define R200_PP_TXFORMAT_X_1 0x2c28 +#define R200_PP_TXFORMAT_X_2 0x2c48 +#define R200_PP_TXFORMAT_X_3 0x2c68 +#define R200_PP_TXFORMAT_X_4 0x2c88 +#define R200_PP_TXFORMAT_X_5 0x2ca8 + +#define R200_PP_TXSIZE_0 0x2c0c /* NPOT only */ +#define R200_PP_TXSIZE_1 0x2c2c /* NPOT only */ +#define R200_PP_TXSIZE_2 0x2c4c /* NPOT only */ +#define R200_PP_TXSIZE_3 0x2c6c /* NPOT only */ +#define R200_PP_TXSIZE_4 0x2c8c /* NPOT only */ +#define R200_PP_TXSIZE_5 0x2cac /* NPOT only */ + +#define R200_PP_TXPITCH_0 0x2c10 /* NPOT only */ +#define R200_PP_TXPITCH_1 0x2c30 /* NPOT only */ +#define R200_PP_TXPITCH_2 0x2c50 /* NPOT only */ +#define R200_PP_TXPITCH_3 0x2c70 /* NPOT only */ +#define R200_PP_TXPITCH_4 0x2c90 /* NPOT only */ +#define R200_PP_TXPITCH_5 0x2cb0 /* NPOT only */ + +#define R200_PP_CUBIC_FACES_0 0x2c18 +#define R200_PP_CUBIC_FACES_1 0x2c38 +#define R200_PP_CUBIC_FACES_2 0x2c58 +#define R200_PP_CUBIC_FACES_3 0x2c78 +#define R200_PP_CUBIC_FACES_4 0x2c98 +#define R200_PP_CUBIC_FACES_5 0x2cb8 + +#define R200_PP_TXOFFSET_0 0x2d00 +# define R200_TXO_ENDIAN_NO_SWAP (0 << 0) +# define R200_TXO_ENDIAN_BYTE_SWAP (1 << 0) +# define R200_TXO_ENDIAN_WORD_SWAP (2 << 0) +# define R200_TXO_ENDIAN_HALFDW_SWAP (3 << 0) +# define R200_TXO_MACRO_LINEAR (0 << 2) +# define R200_TXO_MACRO_TILE (1 << 2) +# define R200_TXO_MICRO_LINEAR (0 << 3) +# define R200_TXO_MICRO_TILE (1 << 3) +# define R200_TXO_OFFSET_MASK 0xffffffe0 +# define R200_TXO_OFFSET_SHIFT 5 +#define R200_PP_CUBIC_OFFSET_F1_0 0x2d04 +#define R200_PP_CUBIC_OFFSET_F2_0 0x2d08 +#define R200_PP_CUBIC_OFFSET_F3_0 0x2d0c +#define R200_PP_CUBIC_OFFSET_F4_0 0x2d10 +#define R200_PP_CUBIC_OFFSET_F5_0 0x2d14 + +#define R200_PP_TXOFFSET_1 0x2d18 +#define R200_PP_CUBIC_OFFSET_F1_1 0x2d1c +#define R200_PP_CUBIC_OFFSET_F2_1 0x2d20 +#define R200_PP_CUBIC_OFFSET_F3_1 0x2d24 +#define R200_PP_CUBIC_OFFSET_F4_1 0x2d28 +#define R200_PP_CUBIC_OFFSET_F5_1 0x2d2c + +#define R200_PP_TXOFFSET_2 0x2d30 +#define R200_PP_CUBIC_OFFSET_F1_2 0x2d34 +#define R200_PP_CUBIC_OFFSET_F2_2 0x2d38 +#define R200_PP_CUBIC_OFFSET_F3_2 0x2d3c +#define R200_PP_CUBIC_OFFSET_F4_2 0x2d40 +#define R200_PP_CUBIC_OFFSET_F5_2 0x2d44 + +#define R200_PP_TXOFFSET_3 0x2d48 +#define R200_PP_CUBIC_OFFSET_F1_3 0x2d4c +#define R200_PP_CUBIC_OFFSET_F2_3 0x2d50 +#define R200_PP_CUBIC_OFFSET_F3_3 0x2d54 +#define R200_PP_CUBIC_OFFSET_F4_3 0x2d58 +#define R200_PP_CUBIC_OFFSET_F5_3 0x2d5c +#define R200_PP_TXOFFSET_4 0x2d60 +#define R200_PP_CUBIC_OFFSET_F1_4 0x2d64 +#define R200_PP_CUBIC_OFFSET_F2_4 0x2d68 +#define R200_PP_CUBIC_OFFSET_F3_4 0x2d6c +#define R200_PP_CUBIC_OFFSET_F4_4 0x2d70 +#define R200_PP_CUBIC_OFFSET_F5_4 0x2d74 +#define R200_PP_TXOFFSET_5 0x2d78 +#define R200_PP_CUBIC_OFFSET_F1_5 0x2d7c +#define R200_PP_CUBIC_OFFSET_F2_5 0x2d80 +#define R200_PP_CUBIC_OFFSET_F3_5 0x2d84 +#define R200_PP_CUBIC_OFFSET_F4_5 0x2d88 +#define R200_PP_CUBIC_OFFSET_F5_5 0x2d8c + +#define R200_PP_TFACTOR_0 0x2ee0 +#define R200_PP_TFACTOR_1 0x2ee4 +#define R200_PP_TFACTOR_2 0x2ee8 +#define R200_PP_TFACTOR_3 0x2eec +#define R200_PP_TFACTOR_4 0x2ef0 +#define R200_PP_TFACTOR_5 0x2ef4 + +#define R200_PP_TXCBLEND_0 0x2f00 +# define R200_TXC_ARG_A_ZERO (0) +# define R200_TXC_ARG_A_CURRENT_COLOR (2) +# define R200_TXC_ARG_A_CURRENT_ALPHA (3) +# define R200_TXC_ARG_A_DIFFUSE_COLOR (4) +# define R200_TXC_ARG_A_DIFFUSE_ALPHA (5) +# define R200_TXC_ARG_A_SPECULAR_COLOR (6) +# define R200_TXC_ARG_A_SPECULAR_ALPHA (7) +# define R200_TXC_ARG_A_TFACTOR_COLOR (8) +# define R200_TXC_ARG_A_TFACTOR_ALPHA (9) +# define R200_TXC_ARG_A_R0_COLOR (10) +# define R200_TXC_ARG_A_R0_ALPHA (11) +# define R200_TXC_ARG_A_R1_COLOR (12) +# define R200_TXC_ARG_A_R1_ALPHA (13) +# define R200_TXC_ARG_A_R2_COLOR (14) +# define R200_TXC_ARG_A_R2_ALPHA (15) +# define R200_TXC_ARG_A_R3_COLOR (16) +# define R200_TXC_ARG_A_R3_ALPHA (17) +# define R200_TXC_ARG_A_R4_COLOR (18) +# define R200_TXC_ARG_A_R4_ALPHA (19) +# define R200_TXC_ARG_A_R5_COLOR (20) +# define R200_TXC_ARG_A_R5_ALPHA (21) +# define R200_TXC_ARG_A_TFACTOR1_COLOR (26) +# define R200_TXC_ARG_A_TFACTOR1_ALPHA (27) +# define R200_TXC_ARG_A_MASK (31 << 0) +# define R200_TXC_ARG_A_SHIFT 0 +# define R200_TXC_ARG_B_ZERO (0 << 5) +# define R200_TXC_ARG_B_CURRENT_COLOR (2 << 5) +# define R200_TXC_ARG_B_CURRENT_ALPHA (3 << 5) +# define R200_TXC_ARG_B_DIFFUSE_COLOR (4 << 5) +# define R200_TXC_ARG_B_DIFFUSE_ALPHA (5 << 5) +# define R200_TXC_ARG_B_SPECULAR_COLOR (6 << 5) +# define R200_TXC_ARG_B_SPECULAR_ALPHA (7 << 5) +# define R200_TXC_ARG_B_TFACTOR_COLOR (8 << 5) +# define R200_TXC_ARG_B_TFACTOR_ALPHA (9 << 5) +# define R200_TXC_ARG_B_R0_COLOR (10 << 5) +# define R200_TXC_ARG_B_R0_ALPHA (11 << 5) +# define R200_TXC_ARG_B_R1_COLOR (12 << 5) +# define R200_TXC_ARG_B_R1_ALPHA (13 << 5) +# define R200_TXC_ARG_B_R2_COLOR (14 << 5) +# define R200_TXC_ARG_B_R2_ALPHA (15 << 5) +# define R200_TXC_ARG_B_R3_COLOR (16 << 5) +# define R200_TXC_ARG_B_R3_ALPHA (17 << 5) +# define R200_TXC_ARG_B_R4_COLOR (18 << 5) +# define R200_TXC_ARG_B_R4_ALPHA (19 << 5) +# define R200_TXC_ARG_B_R5_COLOR (20 << 5) +# define R200_TXC_ARG_B_R5_ALPHA (21 << 5) +# define R200_TXC_ARG_B_TFACTOR1_COLOR (26 << 5) +# define R200_TXC_ARG_B_TFACTOR1_ALPHA (27 << 5) +# define R200_TXC_ARG_B_MASK (31 << 5) +# define R200_TXC_ARG_B_SHIFT 5 +# define R200_TXC_ARG_C_ZERO (0 << 10) +# define R200_TXC_ARG_C_CURRENT_COLOR (2 << 10) +# define R200_TXC_ARG_C_CURRENT_ALPHA (3 << 10) +# define R200_TXC_ARG_C_DIFFUSE_COLOR (4 << 10) +# define R200_TXC_ARG_C_DIFFUSE_ALPHA (5 << 10) +# define R200_TXC_ARG_C_SPECULAR_COLOR (6 << 10) +# define R200_TXC_ARG_C_SPECULAR_ALPHA (7 << 10) +# define R200_TXC_ARG_C_TFACTOR_COLOR (8 << 10) +# define R200_TXC_ARG_C_TFACTOR_ALPHA (9 << 10) +# define R200_TXC_ARG_C_R0_COLOR (10 << 10) +# define R200_TXC_ARG_C_R0_ALPHA (11 << 10) +# define R200_TXC_ARG_C_R1_COLOR (12 << 10) +# define R200_TXC_ARG_C_R1_ALPHA (13 << 10) +# define R200_TXC_ARG_C_R2_COLOR (14 << 10) +# define R200_TXC_ARG_C_R2_ALPHA (15 << 10) +# define R200_TXC_ARG_C_R3_COLOR (16 << 10) +# define R200_TXC_ARG_C_R3_ALPHA (17 << 10) +# define R200_TXC_ARG_C_R4_COLOR (18 << 10) +# define R200_TXC_ARG_C_R4_ALPHA (19 << 10) +# define R200_TXC_ARG_C_R5_COLOR (20 << 10) +# define R200_TXC_ARG_C_R5_ALPHA (21 << 10) +# define R200_TXC_ARG_C_TFACTOR1_COLOR (26 << 10) +# define R200_TXC_ARG_C_TFACTOR1_ALPHA (27 << 10) +# define R200_TXC_ARG_C_MASK (31 << 10) +# define R200_TXC_ARG_C_SHIFT 10 +# define R200_TXC_COMP_ARG_A (1 << 16) +# define R200_TXC_COMP_ARG_A_SHIFT (16) +# define R200_TXC_BIAS_ARG_A (1 << 17) +# define R200_TXC_SCALE_ARG_A (1 << 18) +# define R200_TXC_NEG_ARG_A (1 << 19) +# define R200_TXC_COMP_ARG_B (1 << 20) +# define R200_TXC_COMP_ARG_B_SHIFT (20) +# define R200_TXC_BIAS_ARG_B (1 << 21) +# define R200_TXC_SCALE_ARG_B (1 << 22) +# define R200_TXC_NEG_ARG_B (1 << 23) +# define R200_TXC_COMP_ARG_C (1 << 24) +# define R200_TXC_COMP_ARG_C_SHIFT (24) +# define R200_TXC_BIAS_ARG_C (1 << 25) +# define R200_TXC_SCALE_ARG_C (1 << 26) +# define R200_TXC_NEG_ARG_C (1 << 27) +# define R200_TXC_OP_MADD (0 << 28) +# define R200_TXC_OP_CND0 (2 << 28) +# define R200_TXC_OP_LERP (3 << 28) +# define R200_TXC_OP_DOT3 (4 << 28) +# define R200_TXC_OP_DOT4 (5 << 28) +# define R200_TXC_OP_CONDITIONAL (6 << 28) +# define R200_TXC_OP_DOT2_ADD (7 << 28) +# define R200_TXC_OP_MASK (7 << 28) +#define R200_PP_TXCBLEND2_0 0x2f04 +# define R200_TXC_TFACTOR_SEL_SHIFT 0 +# define R200_TXC_TFACTOR_SEL_MASK 0x7 +# define R200_TXC_TFACTOR1_SEL_SHIFT 4 +# define R200_TXC_TFACTOR1_SEL_MASK (0x7 << 4) +# define R200_TXC_SCALE_SHIFT 8 +# define R200_TXC_SCALE_MASK (7 << 8) +# define R200_TXC_SCALE_1X (0 << 8) +# define R200_TXC_SCALE_2X (1 << 8) +# define R200_TXC_SCALE_4X (2 << 8) +# define R200_TXC_SCALE_8X (3 << 8) +# define R200_TXC_SCALE_INV2 (5 << 8) +# define R200_TXC_SCALE_INV4 (6 << 8) +# define R200_TXC_SCALE_INV8 (7 << 8) +# define R200_TXC_CLAMP_SHIFT 12 +# define R200_TXC_CLAMP_MASK (3 << 12) +# define R200_TXC_CLAMP_WRAP (0 << 12) +# define R200_TXC_CLAMP_0_1 (1 << 12) +# define R200_TXC_CLAMP_8_8 (2 << 12) +# define R200_TXC_OUTPUT_REG_MASK (7 << 16) +# define R200_TXC_OUTPUT_REG_NONE (0 << 16) +# define R200_TXC_OUTPUT_REG_R0 (1 << 16) +# define R200_TXC_OUTPUT_REG_R1 (2 << 16) +# define R200_TXC_OUTPUT_REG_R2 (3 << 16) +# define R200_TXC_OUTPUT_REG_R3 (4 << 16) +# define R200_TXC_OUTPUT_REG_R4 (5 << 16) +# define R200_TXC_OUTPUT_REG_R5 (6 << 16) +# define R200_TXC_OUTPUT_MASK_MASK (7 << 20) +# define R200_TXC_OUTPUT_MASK_RGB (0 << 20) +# define R200_TXC_OUTPUT_MASK_RG (1 << 20) +# define R200_TXC_OUTPUT_MASK_RB (2 << 20) +# define R200_TXC_OUTPUT_MASK_R (3 << 20) +# define R200_TXC_OUTPUT_MASK_GB (4 << 20) +# define R200_TXC_OUTPUT_MASK_G (5 << 20) +# define R200_TXC_OUTPUT_MASK_B (6 << 20) +# define R200_TXC_OUTPUT_MASK_NONE (7 << 20) +# define R200_TXC_REPL_NORMAL 0 +# define R200_TXC_REPL_RED 1 +# define R200_TXC_REPL_GREEN 2 +# define R200_TXC_REPL_BLUE 3 +# define R200_TXC_REPL_ARG_A_SHIFT 26 +# define R200_TXC_REPL_ARG_A_MASK (3 << 26) +# define R200_TXC_REPL_ARG_B_SHIFT 28 +# define R200_TXC_REPL_ARG_B_MASK (3 << 28) +# define R200_TXC_REPL_ARG_C_SHIFT 30 +# define R200_TXC_REPL_ARG_C_MASK (3 << 30) +#define R200_PP_TXABLEND_0 0x2f08 +# define R200_TXA_ARG_A_ZERO (0) +# define R200_TXA_ARG_A_CURRENT_ALPHA (2) /* guess */ +# define R200_TXA_ARG_A_CURRENT_BLUE (3) /* guess */ +# define R200_TXA_ARG_A_DIFFUSE_ALPHA (4) +# define R200_TXA_ARG_A_DIFFUSE_BLUE (5) +# define R200_TXA_ARG_A_SPECULAR_ALPHA (6) +# define R200_TXA_ARG_A_SPECULAR_BLUE (7) +# define R200_TXA_ARG_A_TFACTOR_ALPHA (8) +# define R200_TXA_ARG_A_TFACTOR_BLUE (9) +# define R200_TXA_ARG_A_R0_ALPHA (10) +# define R200_TXA_ARG_A_R0_BLUE (11) +# define R200_TXA_ARG_A_R1_ALPHA (12) +# define R200_TXA_ARG_A_R1_BLUE (13) +# define R200_TXA_ARG_A_R2_ALPHA (14) +# define R200_TXA_ARG_A_R2_BLUE (15) +# define R200_TXA_ARG_A_R3_ALPHA (16) +# define R200_TXA_ARG_A_R3_BLUE (17) +# define R200_TXA_ARG_A_R4_ALPHA (18) +# define R200_TXA_ARG_A_R4_BLUE (19) +# define R200_TXA_ARG_A_R5_ALPHA (20) +# define R200_TXA_ARG_A_R5_BLUE (21) +# define R200_TXA_ARG_A_TFACTOR1_ALPHA (26) +# define R200_TXA_ARG_A_TFACTOR1_BLUE (27) +# define R200_TXA_ARG_A_MASK (31 << 0) +# define R200_TXA_ARG_A_SHIFT 0 +# define R200_TXA_ARG_B_ZERO (0 << 5) +# define R200_TXA_ARG_B_CURRENT_ALPHA (2 << 5) /* guess */ +# define R200_TXA_ARG_B_CURRENT_BLUE (3 << 5) /* guess */ +# define R200_TXA_ARG_B_DIFFUSE_ALPHA (4 << 5) +# define R200_TXA_ARG_B_DIFFUSE_BLUE (5 << 5) +# define R200_TXA_ARG_B_SPECULAR_ALPHA (6 << 5) +# define R200_TXA_ARG_B_SPECULAR_BLUE (7 << 5) +# define R200_TXA_ARG_B_TFACTOR_ALPHA (8 << 5) +# define R200_TXA_ARG_B_TFACTOR_BLUE (9 << 5) +# define R200_TXA_ARG_B_R0_ALPHA (10 << 5) +# define R200_TXA_ARG_B_R0_BLUE (11 << 5) +# define R200_TXA_ARG_B_R1_ALPHA (12 << 5) +# define R200_TXA_ARG_B_R1_BLUE (13 << 5) +# define R200_TXA_ARG_B_R2_ALPHA (14 << 5) +# define R200_TXA_ARG_B_R2_BLUE (15 << 5) +# define R200_TXA_ARG_B_R3_ALPHA (16 << 5) +# define R200_TXA_ARG_B_R3_BLUE (17 << 5) +# define R200_TXA_ARG_B_R4_ALPHA (18 << 5) +# define R200_TXA_ARG_B_R4_BLUE (19 << 5) +# define R200_TXA_ARG_B_R5_ALPHA (20 << 5) +# define R200_TXA_ARG_B_R5_BLUE (21 << 5) +# define R200_TXA_ARG_B_TFACTOR1_ALPHA (26 << 5) +# define R200_TXA_ARG_B_TFACTOR1_BLUE (27 << 5) +# define R200_TXA_ARG_B_MASK (31 << 5) +# define R200_TXA_ARG_B_SHIFT 5 +# define R200_TXA_ARG_C_ZERO (0 << 10) +# define R200_TXA_ARG_C_CURRENT_ALPHA (2 << 10) /* guess */ +# define R200_TXA_ARG_C_CURRENT_BLUE (3 << 10) /* guess */ +# define R200_TXA_ARG_C_DIFFUSE_ALPHA (4 << 10) +# define R200_TXA_ARG_C_DIFFUSE_BLUE (5 << 10) +# define R200_TXA_ARG_C_SPECULAR_ALPHA (6 << 10) +# define R200_TXA_ARG_C_SPECULAR_BLUE (7 << 10) +# define R200_TXA_ARG_C_TFACTOR_ALPHA (8 << 10) +# define R200_TXA_ARG_C_TFACTOR_BLUE (9 << 10) +# define R200_TXA_ARG_C_R0_ALPHA (10 << 10) +# define R200_TXA_ARG_C_R0_BLUE (11 << 10) +# define R200_TXA_ARG_C_R1_ALPHA (12 << 10) +# define R200_TXA_ARG_C_R1_BLUE (13 << 10) +# define R200_TXA_ARG_C_R2_ALPHA (14 << 10) +# define R200_TXA_ARG_C_R2_BLUE (15 << 10) +# define R200_TXA_ARG_C_R3_ALPHA (16 << 10) +# define R200_TXA_ARG_C_R3_BLUE (17 << 10) +# define R200_TXA_ARG_C_R4_ALPHA (18 << 10) +# define R200_TXA_ARG_C_R4_BLUE (19 << 10) +# define R200_TXA_ARG_C_R5_ALPHA (20 << 10) +# define R200_TXA_ARG_C_R5_BLUE (21 << 10) +# define R200_TXA_ARG_C_TFACTOR1_ALPHA (26 << 10) +# define R200_TXA_ARG_C_TFACTOR1_BLUE (27 << 10) +# define R200_TXA_ARG_C_MASK (31 << 10) +# define R200_TXA_ARG_C_SHIFT 10 +# define R200_TXA_COMP_ARG_A (1 << 16) +# define R200_TXA_COMP_ARG_A_SHIFT (16) +# define R200_TXA_BIAS_ARG_A (1 << 17) +# define R200_TXA_SCALE_ARG_A (1 << 18) +# define R200_TXA_NEG_ARG_A (1 << 19) +# define R200_TXA_COMP_ARG_B (1 << 20) +# define R200_TXA_COMP_ARG_B_SHIFT (20) +# define R200_TXA_BIAS_ARG_B (1 << 21) +# define R200_TXA_SCALE_ARG_B (1 << 22) +# define R200_TXA_NEG_ARG_B (1 << 23) +# define R200_TXA_COMP_ARG_C (1 << 24) +# define R200_TXA_COMP_ARG_C_SHIFT (24) +# define R200_TXA_BIAS_ARG_C (1 << 25) +# define R200_TXA_SCALE_ARG_C (1 << 26) +# define R200_TXA_NEG_ARG_C (1 << 27) +# define R200_TXA_OP_MADD (0 << 28) +# define R200_TXA_OP_CND0 (2 << 28) +# define R200_TXA_OP_LERP (3 << 28) +# define R200_TXA_OP_CONDITIONAL (6 << 28) +# define R200_TXA_OP_MASK (7 << 28) +#define R200_PP_TXABLEND2_0 0x2f0c +# define R200_TXA_TFACTOR_SEL_SHIFT 0 +# define R200_TXA_TFACTOR_SEL_MASK 0x7 +# define R200_TXA_TFACTOR1_SEL_SHIFT 4 +# define R200_TXA_TFACTOR1_SEL_MASK (0x7 << 4) +# define R200_TXA_SCALE_SHIFT 8 +# define R200_TXA_SCALE_MASK (7 << 8) +# define R200_TXA_SCALE_1X (0 << 8) +# define R200_TXA_SCALE_2X (1 << 8) +# define R200_TXA_SCALE_4X (2 << 8) +# define R200_TXA_SCALE_8X (3 << 8) +# define R200_TXA_SCALE_INV2 (5 << 8) +# define R200_TXA_SCALE_INV4 (6 << 8) +# define R200_TXA_SCALE_INV8 (7 << 8) +# define R200_TXA_CLAMP_SHIFT 12 +# define R200_TXA_CLAMP_MASK (3 << 12) +# define R200_TXA_CLAMP_WRAP (0 << 12) +# define R200_TXA_CLAMP_0_1 (1 << 12) +# define R200_TXA_CLAMP_8_8 (2 << 12) +# define R200_TXA_OUTPUT_REG_MASK (7 << 16) +# define R200_TXA_OUTPUT_REG_NONE (0 << 16) +# define R200_TXA_OUTPUT_REG_R0 (1 << 16) +# define R200_TXA_OUTPUT_REG_R1 (2 << 16) +# define R200_TXA_OUTPUT_REG_R2 (3 << 16) +# define R200_TXA_OUTPUT_REG_R3 (4 << 16) +# define R200_TXA_OUTPUT_REG_R4 (5 << 16) +# define R200_TXA_OUTPUT_REG_R5 (6 << 16) +# define R200_TXA_DOT_ALPHA (1 << 20) +# define R200_TXA_REPL_NORMAL 0 +# define R200_TXA_REPL_RED 1 +# define R200_TXA_REPL_GREEN 2 +# define R200_TXA_REPL_ARG_A_SHIFT 26 +# define R200_TXA_REPL_ARG_A_MASK (3 << 26) +# define R200_TXA_REPL_ARG_B_SHIFT 28 +# define R200_TXA_REPL_ARG_B_MASK (3 << 28) +# define R200_TXA_REPL_ARG_C_SHIFT 30 +# define R200_TXA_REPL_ARG_C_MASK (3 << 30) + +#define R200_SE_VTX_FMT_0 0x2088 +# define R200_VTX_XY 0 /* always have xy */ +# define R200_VTX_Z0 (1<<0) +# define R200_VTX_W0 (1<<1) +# define R200_VTX_WEIGHT_COUNT_SHIFT (2) +# define R200_VTX_PV_MATRIX_SEL (1<<5) +# define R200_VTX_N0 (1<<6) +# define R200_VTX_POINT_SIZE (1<<7) +# define R200_VTX_DISCRETE_FOG (1<<8) +# define R200_VTX_SHININESS_0 (1<<9) +# define R200_VTX_SHININESS_1 (1<<10) +# define R200_VTX_COLOR_NOT_PRESENT 0 +# define R200_VTX_PK_RGBA 1 +# define R200_VTX_FP_RGB 2 +# define R200_VTX_FP_RGBA 3 +# define R200_VTX_COLOR_MASK 3 +# define R200_VTX_COLOR_0_SHIFT 11 +# define R200_VTX_COLOR_1_SHIFT 13 +# define R200_VTX_COLOR_2_SHIFT 15 +# define R200_VTX_COLOR_3_SHIFT 17 +# define R200_VTX_COLOR_4_SHIFT 19 +# define R200_VTX_COLOR_5_SHIFT 21 +# define R200_VTX_COLOR_6_SHIFT 23 +# define R200_VTX_COLOR_7_SHIFT 25 +# define R200_VTX_XY1 (1<<28) +# define R200_VTX_Z1 (1<<29) +# define R200_VTX_W1 (1<<30) +# define R200_VTX_N1 (1<<31) +#define R200_SE_VTX_FMT_1 0x208c +# define R200_VTX_TEX0_COMP_CNT_SHIFT 0 +# define R200_VTX_TEX1_COMP_CNT_SHIFT 3 +# define R200_VTX_TEX2_COMP_CNT_SHIFT 6 +# define R200_VTX_TEX3_COMP_CNT_SHIFT 9 +# define R200_VTX_TEX4_COMP_CNT_SHIFT 12 +# define R200_VTX_TEX5_COMP_CNT_SHIFT 15 + +#define R200_SE_TCL_OUTPUT_VTX_FMT_0 0x2090 +#define R200_SE_TCL_OUTPUT_VTX_FMT_1 0x2094 +#define R200_SE_TCL_OUTPUT_VTX_COMP_SEL 0x2250 +# define R200_OUTPUT_XYZW (1<<0) +# define R200_OUTPUT_COLOR_0 (1<<8) +# define R200_OUTPUT_COLOR_1 (1<<9) +# define R200_OUTPUT_TEX_0 (1<<16) +# define R200_OUTPUT_TEX_1 (1<<17) +# define R200_OUTPUT_TEX_2 (1<<18) +# define R200_OUTPUT_TEX_3 (1<<19) +# define R200_OUTPUT_TEX_4 (1<<20) +# define R200_OUTPUT_TEX_5 (1<<21) +# define R200_OUTPUT_TEX_MASK (0x3f<<16) +# define R200_OUTPUT_DISCRETE_FOG (1<<24) +# define R200_OUTPUT_PT_SIZE (1<<25) +# define R200_FORCE_INORDER_PROC (1<<31) +#define R200_PP_CNTL_X 0x2cc4 +#define R200_PP_TXMULTI_CTL_0 0x2c1c +#define R200_PP_TXMULTI_CTL_1 0x2c3c +#define R200_PP_TXMULTI_CTL_2 0x2c5c +#define R200_PP_TXMULTI_CTL_3 0x2c7c +#define R200_PP_TXMULTI_CTL_4 0x2c9c +#define R200_PP_TXMULTI_CTL_5 0x2cbc +#define R200_SE_VTX_STATE_CNTL 0x2180 +# define R200_UPDATE_USER_COLOR_0_ENA_MASK (1<<16) + + /* Registers for CP and Microcode Engine */ +#define RADEON_CP_ME_RAM_ADDR 0x07d4 +#define RADEON_CP_ME_RAM_RADDR 0x07d8 +#define RADEON_CP_ME_RAM_DATAH 0x07dc +#define RADEON_CP_ME_RAM_DATAL 0x07e0 + +#define RADEON_CP_RB_BASE 0x0700 +#define RADEON_CP_RB_CNTL 0x0704 +# define RADEON_RB_BUFSZ_SHIFT 0 +# define RADEON_RB_BUFSZ_MASK (0x3f << 0) +# define RADEON_RB_BLKSZ_SHIFT 8 +# define RADEON_RB_BLKSZ_MASK (0x3f << 8) +# define RADEON_BUF_SWAP_32BIT (2 << 16) +# define RADEON_MAX_FETCH_SHIFT 18 +# define RADEON_MAX_FETCH_MASK (0x3 << 18) +# define RADEON_RB_NO_UPDATE (1 << 27) +# define RADEON_RB_RPTR_WR_ENA (1 << 31) +#define RADEON_CP_RB_RPTR_ADDR 0x070c +#define RADEON_CP_RB_RPTR 0x0710 +#define RADEON_CP_RB_WPTR 0x0714 +#define RADEON_CP_RB_RPTR_WR 0x071c + +#define RADEON_SCRATCH_UMSK 0x0770 +#define RADEON_SCRATCH_ADDR 0x0774 + +#define R600_CP_RB_BASE 0xc100 +#define R600_CP_RB_CNTL 0xc104 +# define R600_RB_BUFSZ(x) ((x) << 0) +# define R600_RB_BLKSZ(x) ((x) << 8) +# define R600_RB_NO_UPDATE (1 << 27) +# define R600_RB_RPTR_WR_ENA (1 << 31) +#define R600_CP_RB_RPTR_WR 0xc108 +#define R600_CP_RB_RPTR_ADDR 0xc10c +#define R600_CP_RB_RPTR_ADDR_HI 0xc110 +#define R600_CP_RB_WPTR 0xc114 +#define R600_CP_RB_WPTR_ADDR 0xc118 +#define R600_CP_RB_WPTR_ADDR_HI 0xc11c +#define R600_CP_RB_RPTR 0x8700 +#define R600_CP_RB_WPTR_DELAY 0x8704 + +#define RADEON_CP_IB_BASE 0x0738 +#define RADEON_CP_IB_BUFSZ 0x073c + +#define RADEON_CP_CSQ_CNTL 0x0740 +# define RADEON_CSQ_CNT_PRIMARY_MASK (0xff << 0) +# define RADEON_CSQ_PRIDIS_INDDIS (0 << 28) +# define RADEON_CSQ_PRIPIO_INDDIS (1 << 28) +# define RADEON_CSQ_PRIBM_INDDIS (2 << 28) +# define RADEON_CSQ_PRIPIO_INDBM (3 << 28) +# define RADEON_CSQ_PRIBM_INDBM (4 << 28) +# define RADEON_CSQ_PRIPIO_INDPIO (15 << 28) + +#define R300_CP_RESYNC_ADDR 0x778 +#define R300_CP_RESYNC_DATA 0x77c + +#define RADEON_CP_CSQ_STAT 0x07f8 +# define RADEON_CSQ_RPTR_PRIMARY_MASK (0xff << 0) +# define RADEON_CSQ_WPTR_PRIMARY_MASK (0xff << 8) +# define RADEON_CSQ_RPTR_INDIRECT_MASK (0xff << 16) +# define RADEON_CSQ_WPTR_INDIRECT_MASK (0xff << 24) +#define RADEON_CP_CSQ2_STAT 0x07fc +#define RADEON_CP_CSQ_ADDR 0x07f0 +#define RADEON_CP_CSQ_DATA 0x07f4 +#define RADEON_CP_CSQ_APER_PRIMARY 0x1000 +#define RADEON_CP_CSQ_APER_INDIRECT 0x1300 + +#define RADEON_CP_RB_WPTR_DELAY 0x0718 +# define RADEON_PRE_WRITE_TIMER_SHIFT 0 +# define RADEON_PRE_WRITE_LIMIT_SHIFT 23 +#define RADEON_CP_CSQ_MODE 0x0744 +# define RADEON_INDIRECT2_START_SHIFT 0 +# define RADEON_INDIRECT2_START_MASK (0x7f << 0) +# define RADEON_INDIRECT1_START_SHIFT 8 +# define RADEON_INDIRECT1_START_MASK (0x7f << 8) + +#define RADEON_AIC_CNTL 0x01d0 +# define RADEON_PCIGART_TRANSLATE_EN (1 << 0) +# define RADEON_DIS_OUT_OF_PCI_GART_ACCESS (1 << 1) +# define RS400_MSI_REARM (1 << 3) /* rs400/rs480 */ +#define RADEON_AIC_LO_ADDR 0x01dc +#define RADEON_AIC_PT_BASE 0x01d8 +#define RADEON_AIC_HI_ADDR 0x01e0 + + + + /* Constants */ +/* #define RADEON_LAST_FRAME_REG RADEON_GUI_SCRATCH_REG0 */ +/* efine RADEON_LAST_CLEAR_REG RADEON_GUI_SCRATCH_REG2 */ + + + + /* CP packet types */ +#define RADEON_CP_PACKET0 0x00000000 +#define RADEON_CP_PACKET1 0x40000000 +#define RADEON_CP_PACKET2 0x80000000 +#define RADEON_CP_PACKET3 0xC0000000 +# define RADEON_CP_PACKET_MASK 0xC0000000 +# define RADEON_CP_PACKET_COUNT_MASK 0x3fff0000 +# define RADEON_CP_PACKET_MAX_DWORDS (1 << 12) +# define RADEON_CP_PACKET0_REG_MASK 0x000007ff +# define R300_CP_PACKET0_REG_MASK 0x00001fff +# define R600_CP_PACKET0_REG_MASK 0x0000ffff +# define RADEON_CP_PACKET1_REG0_MASK 0x000007ff +# define RADEON_CP_PACKET1_REG1_MASK 0x003ff800 + +#define RADEON_CP_PACKET0_ONE_REG_WR 0x00008000 + +#define RADEON_CP_PACKET3_NOP 0xC0001000 +#define RADEON_CP_PACKET3_NEXT_CHAR 0xC0001900 +#define RADEON_CP_PACKET3_PLY_NEXTSCAN 0xC0001D00 +#define RADEON_CP_PACKET3_SET_SCISSORS 0xC0001E00 +#define RADEON_CP_PACKET3_3D_RNDR_GEN_INDX_PRIM 0xC0002300 +#define RADEON_CP_PACKET3_LOAD_MICROCODE 0xC0002400 +#define RADEON_CP_PACKET3_WAIT_FOR_IDLE 0xC0002600 +#define RADEON_CP_PACKET3_3D_DRAW_VBUF 0xC0002800 +#define RADEON_CP_PACKET3_3D_DRAW_IMMD 0xC0002900 +#define RADEON_CP_PACKET3_3D_DRAW_INDX 0xC0002A00 +#define RADEON_CP_PACKET3_LOAD_PALETTE 0xC0002C00 +#define R200_CP_PACKET3_3D_DRAW_IMMD_2 0xc0003500 +#define RADEON_CP_PACKET3_3D_LOAD_VBPNTR 0xC0002F00 +#define RADEON_CP_PACKET3_CNTL_PAINT 0xC0009100 +#define RADEON_CP_PACKET3_CNTL_BITBLT 0xC0009200 +#define RADEON_CP_PACKET3_CNTL_SMALLTEXT 0xC0009300 +#define RADEON_CP_PACKET3_CNTL_HOSTDATA_BLT 0xC0009400 +#define RADEON_CP_PACKET3_CNTL_POLYLINE 0xC0009500 +#define RADEON_CP_PACKET3_CNTL_POLYSCANLINES 0xC0009800 +#define RADEON_CP_PACKET3_CNTL_PAINT_MULTI 0xC0009A00 +#define RADEON_CP_PACKET3_CNTL_BITBLT_MULTI 0xC0009B00 +#define RADEON_CP_PACKET3_CNTL_TRANS_BITBLT 0xC0009C00 + + +#define RADEON_CP_VC_FRMT_XY 0x00000000 +#define RADEON_CP_VC_FRMT_W0 0x00000001 +#define RADEON_CP_VC_FRMT_FPCOLOR 0x00000002 +#define RADEON_CP_VC_FRMT_FPALPHA 0x00000004 +#define RADEON_CP_VC_FRMT_PKCOLOR 0x00000008 +#define RADEON_CP_VC_FRMT_FPSPEC 0x00000010 +#define RADEON_CP_VC_FRMT_FPFOG 0x00000020 +#define RADEON_CP_VC_FRMT_PKSPEC 0x00000040 +#define RADEON_CP_VC_FRMT_ST0 0x00000080 +#define RADEON_CP_VC_FRMT_ST1 0x00000100 +#define RADEON_CP_VC_FRMT_Q1 0x00000200 +#define RADEON_CP_VC_FRMT_ST2 0x00000400 +#define RADEON_CP_VC_FRMT_Q2 0x00000800 +#define RADEON_CP_VC_FRMT_ST3 0x00001000 +#define RADEON_CP_VC_FRMT_Q3 0x00002000 +#define RADEON_CP_VC_FRMT_Q0 0x00004000 +#define RADEON_CP_VC_FRMT_BLND_WEIGHT_CNT_MASK 0x00038000 +#define RADEON_CP_VC_FRMT_N0 0x00040000 +#define RADEON_CP_VC_FRMT_XY1 0x08000000 +#define RADEON_CP_VC_FRMT_Z1 0x10000000 +#define RADEON_CP_VC_FRMT_W1 0x20000000 +#define RADEON_CP_VC_FRMT_N1 0x40000000 +#define RADEON_CP_VC_FRMT_Z 0x80000000 + +#define RADEON_CP_VC_CNTL_PRIM_TYPE_NONE 0x00000000 +#define RADEON_CP_VC_CNTL_PRIM_TYPE_POINT 0x00000001 +#define RADEON_CP_VC_CNTL_PRIM_TYPE_LINE 0x00000002 +#define RADEON_CP_VC_CNTL_PRIM_TYPE_LINE_STRIP 0x00000003 +#define RADEON_CP_VC_CNTL_PRIM_TYPE_TRI_LIST 0x00000004 +#define RADEON_CP_VC_CNTL_PRIM_TYPE_TRI_FAN 0x00000005 +#define RADEON_CP_VC_CNTL_PRIM_TYPE_TRI_STRIP 0x00000006 +#define RADEON_CP_VC_CNTL_PRIM_TYPE_TRI_TYPE_2 0x00000007 +#define RADEON_CP_VC_CNTL_PRIM_TYPE_RECT_LIST 0x00000008 +#define RADEON_CP_VC_CNTL_PRIM_TYPE_3VRT_POINT_LIST 0x00000009 +#define RADEON_CP_VC_CNTL_PRIM_TYPE_3VRT_LINE_LIST 0x0000000a +#define RADEON_CP_VC_CNTL_PRIM_WALK_IND 0x00000010 +#define RADEON_CP_VC_CNTL_PRIM_WALK_LIST 0x00000020 +#define RADEON_CP_VC_CNTL_PRIM_WALK_RING 0x00000030 +#define RADEON_CP_VC_CNTL_COLOR_ORDER_BGRA 0x00000000 +#define RADEON_CP_VC_CNTL_COLOR_ORDER_RGBA 0x00000040 +#define RADEON_CP_VC_CNTL_MAOS_ENABLE 0x00000080 +#define RADEON_CP_VC_CNTL_VTX_FMT_NON_RADEON_MODE 0x00000000 +#define RADEON_CP_VC_CNTL_VTX_FMT_RADEON_MODE 0x00000100 +#define RADEON_CP_VC_CNTL_TCL_DISABLE 0x00000000 +#define RADEON_CP_VC_CNTL_TCL_ENABLE 0x00000200 +#define RADEON_CP_VC_CNTL_NUM_SHIFT 16 + +#define RADEON_VS_MATRIX_0_ADDR 0 +#define RADEON_VS_MATRIX_1_ADDR 4 +#define RADEON_VS_MATRIX_2_ADDR 8 +#define RADEON_VS_MATRIX_3_ADDR 12 +#define RADEON_VS_MATRIX_4_ADDR 16 +#define RADEON_VS_MATRIX_5_ADDR 20 +#define RADEON_VS_MATRIX_6_ADDR 24 +#define RADEON_VS_MATRIX_7_ADDR 28 +#define RADEON_VS_MATRIX_8_ADDR 32 +#define RADEON_VS_MATRIX_9_ADDR 36 +#define RADEON_VS_MATRIX_10_ADDR 40 +#define RADEON_VS_MATRIX_11_ADDR 44 +#define RADEON_VS_MATRIX_12_ADDR 48 +#define RADEON_VS_MATRIX_13_ADDR 52 +#define RADEON_VS_MATRIX_14_ADDR 56 +#define RADEON_VS_MATRIX_15_ADDR 60 +#define RADEON_VS_LIGHT_AMBIENT_ADDR 64 +#define RADEON_VS_LIGHT_DIFFUSE_ADDR 72 +#define RADEON_VS_LIGHT_SPECULAR_ADDR 80 +#define RADEON_VS_LIGHT_DIRPOS_ADDR 88 +#define RADEON_VS_LIGHT_HWVSPOT_ADDR 96 +#define RADEON_VS_LIGHT_ATTENUATION_ADDR 104 +#define RADEON_VS_MATRIX_EYE2CLIP_ADDR 112 +#define RADEON_VS_UCP_ADDR 116 +#define RADEON_VS_GLOBAL_AMBIENT_ADDR 122 +#define RADEON_VS_FOG_PARAM_ADDR 123 +#define RADEON_VS_EYE_VECTOR_ADDR 124 + +#define RADEON_SS_LIGHT_DCD_ADDR 0 +#define RADEON_SS_LIGHT_SPOT_EXPONENT_ADDR 8 +#define RADEON_SS_LIGHT_SPOT_CUTOFF_ADDR 16 +#define RADEON_SS_LIGHT_SPECULAR_THRESH_ADDR 24 +#define RADEON_SS_LIGHT_RANGE_CUTOFF_ADDR 32 +#define RADEON_SS_VERT_GUARD_CLIP_ADJ_ADDR 48 +#define RADEON_SS_VERT_GUARD_DISCARD_ADJ_ADDR 49 +#define RADEON_SS_HORZ_GUARD_CLIP_ADJ_ADDR 50 +#define RADEON_SS_HORZ_GUARD_DISCARD_ADJ_ADDR 51 +#define RADEON_SS_SHININESS 60 + +#define RADEON_TV_MASTER_CNTL 0x0800 +# define RADEON_TV_ASYNC_RST (1 << 0) +# define RADEON_CRT_ASYNC_RST (1 << 1) +# define RADEON_RESTART_PHASE_FIX (1 << 3) +# define RADEON_TV_FIFO_ASYNC_RST (1 << 4) +# define RADEON_VIN_ASYNC_RST (1 << 5) +# define RADEON_AUD_ASYNC_RST (1 << 6) +# define RADEON_DVS_ASYNC_RST (1 << 7) +# define RADEON_CRT_FIFO_CE_EN (1 << 9) +# define RADEON_TV_FIFO_CE_EN (1 << 10) +# define RADEON_RE_SYNC_NOW_SEL_MASK (3 << 14) +# define RADEON_TVCLK_ALWAYS_ONb (1 << 30) +# define RADEON_TV_ON (1 << 31) +#define RADEON_TV_PRE_DAC_MUX_CNTL 0x0888 +# define RADEON_Y_RED_EN (1 << 0) +# define RADEON_C_GRN_EN (1 << 1) +# define RADEON_CMP_BLU_EN (1 << 2) +# define RADEON_DAC_DITHER_EN (1 << 3) +# define RADEON_RED_MX_FORCE_DAC_DATA (6 << 4) +# define RADEON_GRN_MX_FORCE_DAC_DATA (6 << 8) +# define RADEON_BLU_MX_FORCE_DAC_DATA (6 << 12) +# define RADEON_TV_FORCE_DAC_DATA_SHIFT 16 +#define RADEON_TV_RGB_CNTL 0x0804 +# define RADEON_SWITCH_TO_BLUE (1 << 4) +# define RADEON_RGB_DITHER_EN (1 << 5) +# define RADEON_RGB_SRC_SEL_MASK (3 << 8) +# define RADEON_RGB_SRC_SEL_CRTC1 (0 << 8) +# define RADEON_RGB_SRC_SEL_RMX (1 << 8) +# define RADEON_RGB_SRC_SEL_CRTC2 (2 << 8) +# define RADEON_RGB_CONVERT_BY_PASS (1 << 10) +# define RADEON_UVRAM_READ_MARGIN_SHIFT 16 +# define RADEON_FIFORAM_FFMACRO_READ_MARGIN_SHIFT 20 +# define RADEON_RGB_ATTEN_SEL(x) ((x) << 24) +# define RADEON_TVOUT_SCALE_EN (1 << 26) +# define RADEON_RGB_ATTEN_VAL(x) ((x) << 28) +#define RADEON_TV_SYNC_CNTL 0x0808 +# define RADEON_SYNC_OE (1 << 0) +# define RADEON_SYNC_OUT (1 << 1) +# define RADEON_SYNC_IN (1 << 2) +# define RADEON_SYNC_PUB (1 << 3) +# define RADEON_SYNC_PD (1 << 4) +# define RADEON_TV_SYNC_IO_DRIVE (1 << 5) +#define RADEON_TV_HTOTAL 0x080c +#define RADEON_TV_HDISP 0x0810 +#define RADEON_TV_HSTART 0x0818 +#define RADEON_TV_HCOUNT 0x081C +#define RADEON_TV_VTOTAL 0x0820 +#define RADEON_TV_VDISP 0x0824 +#define RADEON_TV_VCOUNT 0x0828 +#define RADEON_TV_FTOTAL 0x082c +#define RADEON_TV_FCOUNT 0x0830 +#define RADEON_TV_FRESTART 0x0834 +#define RADEON_TV_HRESTART 0x0838 +#define RADEON_TV_VRESTART 0x083c +#define RADEON_TV_HOST_READ_DATA 0x0840 +#define RADEON_TV_HOST_WRITE_DATA 0x0844 +#define RADEON_TV_HOST_RD_WT_CNTL 0x0848 +# define RADEON_HOST_FIFO_RD (1 << 12) +# define RADEON_HOST_FIFO_RD_ACK (1 << 13) +# define RADEON_HOST_FIFO_WT (1 << 14) +# define RADEON_HOST_FIFO_WT_ACK (1 << 15) +#define RADEON_TV_VSCALER_CNTL1 0x084c +# define RADEON_UV_INC_MASK 0xffff +# define RADEON_UV_INC_SHIFT 0 +# define RADEON_Y_W_EN (1 << 24) +# define RADEON_RESTART_FIELD (1 << 29) /* restart on field 0 */ +# define RADEON_Y_DEL_W_SIG_SHIFT 26 +#define RADEON_TV_TIMING_CNTL 0x0850 +# define RADEON_H_INC_MASK 0xfff +# define RADEON_H_INC_SHIFT 0 +# define RADEON_REQ_Y_FIRST (1 << 19) +# define RADEON_FORCE_BURST_ALWAYS (1 << 21) +# define RADEON_UV_POST_SCALE_BYPASS (1 << 23) +# define RADEON_UV_OUTPUT_POST_SCALE_SHIFT 24 +#define RADEON_TV_VSCALER_CNTL2 0x0854 +# define RADEON_DITHER_MODE (1 << 0) +# define RADEON_Y_OUTPUT_DITHER_EN (1 << 1) +# define RADEON_UV_OUTPUT_DITHER_EN (1 << 2) +# define RADEON_UV_TO_BUF_DITHER_EN (1 << 3) +#define RADEON_TV_Y_FALL_CNTL 0x0858 +# define RADEON_Y_FALL_PING_PONG (1 << 16) +# define RADEON_Y_COEF_EN (1 << 17) +#define RADEON_TV_Y_RISE_CNTL 0x085c +# define RADEON_Y_RISE_PING_PONG (1 << 16) +#define RADEON_TV_Y_SAW_TOOTH_CNTL 0x0860 +#define RADEON_TV_UPSAMP_AND_GAIN_CNTL 0x0864 +# define RADEON_YUPSAMP_EN (1 << 0) +# define RADEON_UVUPSAMP_EN (1 << 2) +#define RADEON_TV_GAIN_LIMIT_SETTINGS 0x0868 +# define RADEON_Y_GAIN_LIMIT_SHIFT 0 +# define RADEON_UV_GAIN_LIMIT_SHIFT 16 +#define RADEON_TV_LINEAR_GAIN_SETTINGS 0x086c +# define RADEON_Y_GAIN_SHIFT 0 +# define RADEON_UV_GAIN_SHIFT 16 +#define RADEON_TV_MODULATOR_CNTL1 0x0870 +# define RADEON_YFLT_EN (1 << 2) +# define RADEON_UVFLT_EN (1 << 3) +# define RADEON_ALT_PHASE_EN (1 << 6) +# define RADEON_SYNC_TIP_LEVEL (1 << 7) +# define RADEON_BLANK_LEVEL_SHIFT 8 +# define RADEON_SET_UP_LEVEL_SHIFT 16 +# define RADEON_SLEW_RATE_LIMIT (1 << 23) +# define RADEON_CY_FILT_BLEND_SHIFT 28 +#define RADEON_TV_MODULATOR_CNTL2 0x0874 +# define RADEON_TV_U_BURST_LEVEL_MASK 0x1ff +# define RADEON_TV_V_BURST_LEVEL_MASK 0x1ff +# define RADEON_TV_V_BURST_LEVEL_SHIFT 16 +#define RADEON_TV_CRC_CNTL 0x0890 +#define RADEON_TV_UV_ADR 0x08ac +# define RADEON_MAX_UV_ADR_MASK 0x000000ff +# define RADEON_MAX_UV_ADR_SHIFT 0 +# define RADEON_TABLE1_BOT_ADR_MASK 0x0000ff00 +# define RADEON_TABLE1_BOT_ADR_SHIFT 8 +# define RADEON_TABLE3_TOP_ADR_MASK 0x00ff0000 +# define RADEON_TABLE3_TOP_ADR_SHIFT 16 +# define RADEON_HCODE_TABLE_SEL_MASK 0x06000000 +# define RADEON_HCODE_TABLE_SEL_SHIFT 25 +# define RADEON_VCODE_TABLE_SEL_MASK 0x18000000 +# define RADEON_VCODE_TABLE_SEL_SHIFT 27 +# define RADEON_TV_MAX_FIFO_ADDR 0x1a7 +# define RADEON_TV_MAX_FIFO_ADDR_INTERNAL 0x1ff +#define RADEON_TV_PLL_FINE_CNTL 0x0020 /* PLL */ +#define RADEON_TV_PLL_CNTL 0x0021 /* PLL */ +# define RADEON_TV_M0LO_MASK 0xff +# define RADEON_TV_M0HI_MASK 0x7 +# define RADEON_TV_M0HI_SHIFT 18 +# define RADEON_TV_N0LO_MASK 0x1ff +# define RADEON_TV_N0LO_SHIFT 8 +# define RADEON_TV_N0HI_MASK 0x3 +# define RADEON_TV_N0HI_SHIFT 21 +# define RADEON_TV_P_MASK 0xf +# define RADEON_TV_P_SHIFT 24 +# define RADEON_TV_SLIP_EN (1 << 23) +# define RADEON_TV_DTO_EN (1 << 28) +#define RADEON_TV_PLL_CNTL1 0x0022 /* PLL */ +# define RADEON_TVPLL_RESET (1 << 1) +# define RADEON_TVPLL_SLEEP (1 << 3) +# define RADEON_TVPLL_REFCLK_SEL (1 << 4) +# define RADEON_TVPCP_SHIFT 8 +# define RADEON_TVPCP_MASK (7 << 8) +# define RADEON_TVPVG_SHIFT 11 +# define RADEON_TVPVG_MASK (7 << 11) +# define RADEON_TVPDC_SHIFT 14 +# define RADEON_TVPDC_MASK (3 << 14) +# define RADEON_TVPLL_TEST_DIS (1 << 31) +# define RADEON_TVCLK_SRC_SEL_TVPLL (1 << 30) + +#define RS400_DISP2_REQ_CNTL1 0xe30 +# define RS400_DISP2_START_REQ_LEVEL_SHIFT 0 +# define RS400_DISP2_START_REQ_LEVEL_MASK 0x3ff +# define RS400_DISP2_STOP_REQ_LEVEL_SHIFT 12 +# define RS400_DISP2_STOP_REQ_LEVEL_MASK 0x3ff +# define RS400_DISP2_ALLOW_FID_LEVEL_SHIFT 22 +# define RS400_DISP2_ALLOW_FID_LEVEL_MASK 0x3ff +#define RS400_DISP2_REQ_CNTL2 0xe34 +# define RS400_DISP2_CRITICAL_POINT_START_SHIFT 12 +# define RS400_DISP2_CRITICAL_POINT_START_MASK 0x3ff +# define RS400_DISP2_CRITICAL_POINT_STOP_SHIFT 22 +# define RS400_DISP2_CRITICAL_POINT_STOP_MASK 0x3ff +#define RS400_DMIF_MEM_CNTL1 0xe38 +# define RS400_DISP2_START_ADR_SHIFT 0 +# define RS400_DISP2_START_ADR_MASK 0x3ff +# define RS400_DISP1_CRITICAL_POINT_START_SHIFT 12 +# define RS400_DISP1_CRITICAL_POINT_START_MASK 0x3ff +# define RS400_DISP1_CRITICAL_POINT_STOP_SHIFT 22 +# define RS400_DISP1_CRITICAL_POINT_STOP_MASK 0x3ff +#define RS400_DISP1_REQ_CNTL1 0xe3c +# define RS400_DISP1_START_REQ_LEVEL_SHIFT 0 +# define RS400_DISP1_START_REQ_LEVEL_MASK 0x3ff +# define RS400_DISP1_STOP_REQ_LEVEL_SHIFT 12 +# define RS400_DISP1_STOP_REQ_LEVEL_MASK 0x3ff +# define RS400_DISP1_ALLOW_FID_LEVEL_SHIFT 22 +# define RS400_DISP1_ALLOW_FID_LEVEL_MASK 0x3ff + +#define RADEON_PCIE_INDEX 0x0030 +#define RADEON_PCIE_DATA 0x0034 +#define RADEON_PCIE_TX_GART_CNTL 0x10 +# define RADEON_PCIE_TX_GART_EN (1 << 0) +# define RADEON_PCIE_TX_GART_UNMAPPED_ACCESS_PASS_THRU (0 << 1) +# define RADEON_PCIE_TX_GART_UNMAPPED_ACCESS_CLAMP_LO (1 << 1) +# define RADEON_PCIE_TX_GART_UNMAPPED_ACCESS_DISCARD (3 << 1) +# define RADEON_PCIE_TX_GART_MODE_32_128_CACHE (0 << 3) +# define RADEON_PCIE_TX_GART_MODE_8_4_128_CACHE (1 << 3) +# define RADEON_PCIE_TX_GART_CHK_RW_VALID_EN (1 << 5) +# define RADEON_PCIE_TX_GART_INVALIDATE_TLB (1 << 8) +#define RADEON_PCIE_TX_DISCARD_RD_ADDR_LO 0x11 +#define RADEON_PCIE_TX_DISCARD_RD_ADDR_HI 0x12 +#define RADEON_PCIE_TX_GART_BASE 0x13 +#define RADEON_PCIE_TX_GART_START_LO 0x14 +#define RADEON_PCIE_TX_GART_START_HI 0x15 +#define RADEON_PCIE_TX_GART_END_LO 0x16 +#define RADEON_PCIE_TX_GART_END_HI 0x17 +#define RADEON_PCIE_TX_GART_ERROR 0x18 + +#define RADEON_SCRATCH_REG0 0x15e0 +#define RADEON_SCRATCH_REG1 0x15e4 +#define RADEON_SCRATCH_REG2 0x15e8 +#define RADEON_SCRATCH_REG3 0x15ec +#define RADEON_SCRATCH_REG4 0x15f0 +#define RADEON_SCRATCH_REG5 0x15f4 + +#define RV530_GB_PIPE_SELECT2 0x4124 + +#endif diff --git a/sys/dev/drm2/radeon/radeon_ring.c b/sys/dev/drm2/radeon/radeon_ring.c new file mode 100644 index 00000000000..37d0adee879 --- /dev/null +++ b/sys/dev/drm2/radeon/radeon_ring.c @@ -0,0 +1,884 @@ +/* + * Copyright 2008 Advanced Micro Devices, Inc. + * Copyright 2008 Red Hat Inc. + * Copyright 2009 Jerome Glisse. + * + * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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. + * + * Authors: Dave Airlie + * Alex Deucher + * Jerome Glisse + * Christian König + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include "radeon_reg.h" +#include "radeon.h" +#include "atom.h" + +#ifdef DUMBBELL_WIP +/* + * IB + * IBs (Indirect Buffers) and areas of GPU accessible memory where + * commands are stored. You can put a pointer to the IB in the + * command ring and the hw will fetch the commands from the IB + * and execute them. Generally userspace acceleration drivers + * produce command buffers which are send to the kernel and + * put in IBs for execution by the requested ring. + */ +static int radeon_debugfs_sa_init(struct radeon_device *rdev); +#endif /* DUMBBELL_WIP */ + +/** + * radeon_ib_get - request an IB (Indirect Buffer) + * + * @rdev: radeon_device pointer + * @ring: ring index the IB is associated with + * @ib: IB object returned + * @size: requested IB size + * + * Request an IB (all asics). IBs are allocated using the + * suballocator. + * Returns 0 on success, error on failure. + */ +int radeon_ib_get(struct radeon_device *rdev, int ring, + struct radeon_ib *ib, struct radeon_vm *vm, + unsigned size) +{ + int i, r; + + r = radeon_sa_bo_new(rdev, &rdev->ring_tmp_bo, &ib->sa_bo, size, 256, true); + if (r) { + dev_err(rdev->dev, "failed to get a new IB (%d)\n", r); + return r; + } + + r = radeon_semaphore_create(rdev, &ib->semaphore); + if (r) { + return r; + } + + ib->ring = ring; + ib->fence = NULL; + ib->ptr = radeon_sa_bo_cpu_addr(ib->sa_bo); + ib->vm = vm; + if (vm) { + /* ib pool is bound at RADEON_VA_IB_OFFSET in virtual address + * space and soffset is the offset inside the pool bo + */ + ib->gpu_addr = ib->sa_bo->soffset + RADEON_VA_IB_OFFSET; + } else { + ib->gpu_addr = radeon_sa_bo_gpu_addr(ib->sa_bo); + } + ib->is_const_ib = false; + for (i = 0; i < RADEON_NUM_RINGS; ++i) + ib->sync_to[i] = NULL; + + return 0; +} + +/** + * radeon_ib_free - free an IB (Indirect Buffer) + * + * @rdev: radeon_device pointer + * @ib: IB object to free + * + * Free an IB (all asics). + */ +void radeon_ib_free(struct radeon_device *rdev, struct radeon_ib *ib) +{ + radeon_semaphore_free(rdev, &ib->semaphore, ib->fence); + radeon_sa_bo_free(rdev, &ib->sa_bo, ib->fence); + radeon_fence_unref(&ib->fence); +} + +/** + * radeon_ib_schedule - schedule an IB (Indirect Buffer) on the ring + * + * @rdev: radeon_device pointer + * @ib: IB object to schedule + * @const_ib: Const IB to schedule (SI only) + * + * Schedule an IB on the associated ring (all asics). + * Returns 0 on success, error on failure. + * + * On SI, there are two parallel engines fed from the primary ring, + * the CE (Constant Engine) and the DE (Drawing Engine). Since + * resource descriptors have moved to memory, the CE allows you to + * prime the caches while the DE is updating register state so that + * the resource descriptors will be already in cache when the draw is + * processed. To accomplish this, the userspace driver submits two + * IBs, one for the CE and one for the DE. If there is a CE IB (called + * a CONST_IB), it will be put on the ring prior to the DE IB. Prior + * to SI there was just a DE IB. + */ +int radeon_ib_schedule(struct radeon_device *rdev, struct radeon_ib *ib, + struct radeon_ib *const_ib) +{ + struct radeon_ring *ring = &rdev->ring[ib->ring]; + bool need_sync = false; + int i, r = 0; + + if (!ib->length_dw || !ring->ready) { + /* TODO: Nothings in the ib we should report. */ + dev_err(rdev->dev, "couldn't schedule ib\n"); + return -EINVAL; + } + + /* 64 dwords should be enough for fence too */ + r = radeon_ring_lock(rdev, ring, 64 + RADEON_NUM_RINGS * 8); + if (r) { + dev_err(rdev->dev, "scheduling IB failed (%d).\n", r); + return r; + } + for (i = 0; i < RADEON_NUM_RINGS; ++i) { + struct radeon_fence *fence = ib->sync_to[i]; + if (radeon_fence_need_sync(fence, ib->ring)) { + need_sync = true; + radeon_semaphore_sync_rings(rdev, ib->semaphore, + fence->ring, ib->ring); + radeon_fence_note_sync(fence, ib->ring); + } + } + /* immediately free semaphore when we don't need to sync */ + if (!need_sync) { + radeon_semaphore_free(rdev, &ib->semaphore, NULL); + } + /* if we can't remember our last VM flush then flush now! */ + if (ib->vm && !ib->vm->last_flush) { + radeon_ring_vm_flush(rdev, ib->ring, ib->vm); + } + if (const_ib) { + radeon_ring_ib_execute(rdev, const_ib->ring, const_ib); + radeon_semaphore_free(rdev, &const_ib->semaphore, NULL); + } + radeon_ring_ib_execute(rdev, ib->ring, ib); + r = radeon_fence_emit(rdev, &ib->fence, ib->ring); + if (r) { + dev_err(rdev->dev, "failed to emit fence for new IB (%d)\n", r); + radeon_ring_unlock_undo(rdev, ring); + return r; + } + if (const_ib) { + const_ib->fence = radeon_fence_ref(ib->fence); + } + /* we just flushed the VM, remember that */ + if (ib->vm && !ib->vm->last_flush) { + ib->vm->last_flush = radeon_fence_ref(ib->fence); + } + radeon_ring_unlock_commit(rdev, ring); + return 0; +} + +/** + * radeon_ib_pool_init - Init the IB (Indirect Buffer) pool + * + * @rdev: radeon_device pointer + * + * Initialize the suballocator to manage a pool of memory + * for use as IBs (all asics). + * Returns 0 on success, error on failure. + */ +int radeon_ib_pool_init(struct radeon_device *rdev) +{ + int r; + + if (rdev->ib_pool_ready) { + return 0; + } + r = radeon_sa_bo_manager_init(rdev, &rdev->ring_tmp_bo, + RADEON_IB_POOL_SIZE*64*1024, + RADEON_GEM_DOMAIN_GTT); + if (r) { + return r; + } + + r = radeon_sa_bo_manager_start(rdev, &rdev->ring_tmp_bo); + if (r) { + return r; + } + + rdev->ib_pool_ready = true; +#ifdef DUMBBELL_WIP + if (radeon_debugfs_sa_init(rdev)) { + dev_err(rdev->dev, "failed to register debugfs file for SA\n"); + } +#endif /* DUMBBELL_WIP */ + return 0; +} + +/** + * radeon_ib_pool_fini - Free the IB (Indirect Buffer) pool + * + * @rdev: radeon_device pointer + * + * Tear down the suballocator managing the pool of memory + * for use as IBs (all asics). + */ +void radeon_ib_pool_fini(struct radeon_device *rdev) +{ + if (rdev->ib_pool_ready) { + radeon_sa_bo_manager_suspend(rdev, &rdev->ring_tmp_bo); + radeon_sa_bo_manager_fini(rdev, &rdev->ring_tmp_bo); + rdev->ib_pool_ready = false; + } +} + +/** + * radeon_ib_ring_tests - test IBs on the rings + * + * @rdev: radeon_device pointer + * + * Test an IB (Indirect Buffer) on each ring. + * If the test fails, disable the ring. + * Returns 0 on success, error if the primary GFX ring + * IB test fails. + */ +int radeon_ib_ring_tests(struct radeon_device *rdev) +{ + unsigned i; + int r; + + for (i = 0; i < RADEON_NUM_RINGS; ++i) { + struct radeon_ring *ring = &rdev->ring[i]; + + if (!ring->ready) + continue; + + r = radeon_ib_test(rdev, i, ring); + if (r) { + ring->ready = false; + + if (i == RADEON_RING_TYPE_GFX_INDEX) { + /* oh, oh, that's really bad */ + DRM_ERROR("radeon: failed testing IB on GFX ring (%d).\n", r); + rdev->accel_working = false; + return r; + + } else { + /* still not good, but we can live with it */ + DRM_ERROR("radeon: failed testing IB on ring %d (%d).\n", i, r); + } + } + } + return 0; +} + +#ifdef DUMBBELL_WIP +/* + * Rings + * Most engines on the GPU are fed via ring buffers. Ring + * buffers are areas of GPU accessible memory that the host + * writes commands into and the GPU reads commands out of. + * There is a rptr (read pointer) that determines where the + * GPU is currently reading, and a wptr (write pointer) + * which determines where the host has written. When the + * pointers are equal, the ring is idle. When the host + * writes commands to the ring buffer, it increments the + * wptr. The GPU then starts fetching commands and executes + * them until the pointers are equal again. + */ +static int radeon_debugfs_ring_init(struct radeon_device *rdev, struct radeon_ring *ring); +#endif /* DUMBBELL_WIP */ + +#if defined(DRM_DEBUG_CODE) && DRM_DEBUG_CODE != 0 +/** + * radeon_ring_write - write a value to the ring + * + * @ring: radeon_ring structure holding ring information + * @v: dword (dw) value to write + * + * Write a value to the requested ring buffer (all asics). + */ +void radeon_ring_write(struct radeon_ring *ring, uint32_t v) +{ +#if DRM_DEBUG_CODE + if (ring->count_dw <= 0) { + DRM_ERROR("radeon: writing more dwords to the ring than expected!\n"); + } +#endif + ring->ring[ring->wptr++] = v; + ring->wptr &= ring->ptr_mask; + ring->count_dw--; + ring->ring_free_dw--; +} +#endif + +/** + * radeon_ring_supports_scratch_reg - check if the ring supports + * writing to scratch registers + * + * @rdev: radeon_device pointer + * @ring: radeon_ring structure holding ring information + * + * Check if a specific ring supports writing to scratch registers (all asics). + * Returns true if the ring supports writing to scratch regs, false if not. + */ +bool radeon_ring_supports_scratch_reg(struct radeon_device *rdev, + struct radeon_ring *ring) +{ + switch (ring->idx) { + case RADEON_RING_TYPE_GFX_INDEX: + case CAYMAN_RING_TYPE_CP1_INDEX: + case CAYMAN_RING_TYPE_CP2_INDEX: + return true; + default: + return false; + } +} + +/** + * radeon_ring_free_size - update the free size + * + * @rdev: radeon_device pointer + * @ring: radeon_ring structure holding ring information + * + * Update the free dw slots in the ring buffer (all asics). + */ +void radeon_ring_free_size(struct radeon_device *rdev, struct radeon_ring *ring) +{ + u32 rptr; + + if (rdev->wb.enabled) + rptr = le32_to_cpu(rdev->wb.wb[ring->rptr_offs/4]); + else + rptr = RREG32(ring->rptr_reg); + ring->rptr = (rptr & ring->ptr_reg_mask) >> ring->ptr_reg_shift; + /* This works because ring_size is a power of 2 */ + ring->ring_free_dw = (ring->rptr + (ring->ring_size / 4)); + ring->ring_free_dw -= ring->wptr; + ring->ring_free_dw &= ring->ptr_mask; + if (!ring->ring_free_dw) { + ring->ring_free_dw = ring->ring_size / 4; + } +} + +/** + * radeon_ring_alloc - allocate space on the ring buffer + * + * @rdev: radeon_device pointer + * @ring: radeon_ring structure holding ring information + * @ndw: number of dwords to allocate in the ring buffer + * + * Allocate @ndw dwords in the ring buffer (all asics). + * Returns 0 on success, error on failure. + */ +int radeon_ring_alloc(struct radeon_device *rdev, struct radeon_ring *ring, unsigned ndw) +{ + int r; + + /* make sure we aren't trying to allocate more space than there is on the ring */ + if (ndw > (ring->ring_size / 4)) + return -ENOMEM; + /* Align requested size with padding so unlock_commit can + * pad safely */ + ndw = (ndw + ring->align_mask) & ~ring->align_mask; + while (ndw > (ring->ring_free_dw - 1)) { + radeon_ring_free_size(rdev, ring); + if (ndw < ring->ring_free_dw) { + break; + } + r = radeon_fence_wait_next_locked(rdev, ring->idx); + if (r) + return r; + } + ring->count_dw = ndw; + ring->wptr_old = ring->wptr; + return 0; +} + +/** + * radeon_ring_lock - lock the ring and allocate space on it + * + * @rdev: radeon_device pointer + * @ring: radeon_ring structure holding ring information + * @ndw: number of dwords to allocate in the ring buffer + * + * Lock the ring and allocate @ndw dwords in the ring buffer + * (all asics). + * Returns 0 on success, error on failure. + */ +int radeon_ring_lock(struct radeon_device *rdev, struct radeon_ring *ring, unsigned ndw) +{ + int r; + + sx_xlock(&rdev->ring_lock); + r = radeon_ring_alloc(rdev, ring, ndw); + if (r) { + sx_xunlock(&rdev->ring_lock); + return r; + } + return 0; +} + +/** + * radeon_ring_commit - tell the GPU to execute the new + * commands on the ring buffer + * + * @rdev: radeon_device pointer + * @ring: radeon_ring structure holding ring information + * + * Update the wptr (write pointer) to tell the GPU to + * execute new commands on the ring buffer (all asics). + */ +void radeon_ring_commit(struct radeon_device *rdev, struct radeon_ring *ring) +{ + /* We pad to match fetch size */ + while (ring->wptr & ring->align_mask) { + radeon_ring_write(ring, ring->nop); + } + DRM_MEMORYBARRIER(); + WREG32(ring->wptr_reg, (ring->wptr << ring->ptr_reg_shift) & ring->ptr_reg_mask); + (void)RREG32(ring->wptr_reg); +} + +/** + * radeon_ring_unlock_commit - tell the GPU to execute the new + * commands on the ring buffer and unlock it + * + * @rdev: radeon_device pointer + * @ring: radeon_ring structure holding ring information + * + * Call radeon_ring_commit() then unlock the ring (all asics). + */ +void radeon_ring_unlock_commit(struct radeon_device *rdev, struct radeon_ring *ring) +{ + radeon_ring_commit(rdev, ring); + sx_xunlock(&rdev->ring_lock); +} + +/** + * radeon_ring_undo - reset the wptr + * + * @ring: radeon_ring structure holding ring information + * + * Reset the driver's copy of the wptr (all asics). + */ +void radeon_ring_undo(struct radeon_ring *ring) +{ + ring->wptr = ring->wptr_old; +} + +/** + * radeon_ring_unlock_undo - reset the wptr and unlock the ring + * + * @ring: radeon_ring structure holding ring information + * + * Call radeon_ring_undo() then unlock the ring (all asics). + */ +void radeon_ring_unlock_undo(struct radeon_device *rdev, struct radeon_ring *ring) +{ + radeon_ring_undo(ring); + sx_xunlock(&rdev->ring_lock); +} + +/** + * radeon_ring_force_activity - add some nop packets to the ring + * + * @rdev: radeon_device pointer + * @ring: radeon_ring structure holding ring information + * + * Add some nop packets to the ring to force activity (all asics). + * Used for lockup detection to see if the rptr is advancing. + */ +void radeon_ring_force_activity(struct radeon_device *rdev, struct radeon_ring *ring) +{ + int r; + + radeon_ring_free_size(rdev, ring); + if (ring->rptr == ring->wptr) { + r = radeon_ring_alloc(rdev, ring, 1); + if (!r) { + radeon_ring_write(ring, ring->nop); + radeon_ring_commit(rdev, ring); + } + } +} + +/** + * radeon_ring_lockup_update - update lockup variables + * + * @ring: radeon_ring structure holding ring information + * + * Update the last rptr value and timestamp (all asics). + */ +void radeon_ring_lockup_update(struct radeon_ring *ring) +{ + ring->last_rptr = ring->rptr; + ring->last_activity = jiffies; +} + +/** + * radeon_ring_test_lockup() - check if ring is lockedup by recording information + * @rdev: radeon device structure + * @ring: radeon_ring structure holding ring information + * + * We don't need to initialize the lockup tracking information as we will either + * have CP rptr to a different value of jiffies wrap around which will force + * initialization of the lockup tracking informations. + * + * A possible false positivie is if we get call after while and last_cp_rptr == + * the current CP rptr, even if it's unlikely it might happen. To avoid this + * if the elapsed time since last call is bigger than 2 second than we return + * false and update the tracking information. Due to this the caller must call + * radeon_ring_test_lockup several time in less than 2sec for lockup to be reported + * the fencing code should be cautious about that. + * + * Caller should write to the ring to force CP to do something so we don't get + * false positive when CP is just gived nothing to do. + * + **/ +bool radeon_ring_test_lockup(struct radeon_device *rdev, struct radeon_ring *ring) +{ + unsigned long cjiffies, elapsed; + uint32_t rptr; + + cjiffies = jiffies; + if (!time_after(cjiffies, ring->last_activity)) { + /* likely a wrap around */ + radeon_ring_lockup_update(ring); + return false; + } + rptr = RREG32(ring->rptr_reg); + ring->rptr = (rptr & ring->ptr_reg_mask) >> ring->ptr_reg_shift; + if (ring->rptr != ring->last_rptr) { + /* CP is still working no lockup */ + radeon_ring_lockup_update(ring); + return false; + } + elapsed = jiffies_to_msecs(cjiffies - ring->last_activity); + if (radeon_lockup_timeout && elapsed >= radeon_lockup_timeout) { + dev_err(rdev->dev, "GPU lockup CP stall for more than %lumsec\n", elapsed); + return true; + } + /* give a chance to the GPU ... */ + return false; +} + +/** + * radeon_ring_backup - Back up the content of a ring + * + * @rdev: radeon_device pointer + * @ring: the ring we want to back up + * + * Saves all unprocessed commits from a ring, returns the number of dwords saved. + */ +unsigned radeon_ring_backup(struct radeon_device *rdev, struct radeon_ring *ring, + uint32_t **data) +{ + unsigned size, ptr, i; + + /* just in case lock the ring */ + sx_xlock(&rdev->ring_lock); + *data = NULL; + + if (ring->ring_obj == NULL) { + sx_xunlock(&rdev->ring_lock); + return 0; + } + + /* it doesn't make sense to save anything if all fences are signaled */ + if (!radeon_fence_count_emitted(rdev, ring->idx)) { + sx_xunlock(&rdev->ring_lock); + return 0; + } + + /* calculate the number of dw on the ring */ + if (ring->rptr_save_reg) + ptr = RREG32(ring->rptr_save_reg); + else if (rdev->wb.enabled) + ptr = le32_to_cpu(*ring->next_rptr_cpu_addr); + else { + /* no way to read back the next rptr */ + sx_xunlock(&rdev->ring_lock); + return 0; + } + + size = ring->wptr + (ring->ring_size / 4); + size -= ptr; + size &= ring->ptr_mask; + if (size == 0) { + sx_xunlock(&rdev->ring_lock); + return 0; + } + + /* and then save the content of the ring */ + *data = malloc(size * sizeof(uint32_t), DRM_MEM_DRIVER, M_WAITOK); + if (!*data) { + sx_xunlock(&rdev->ring_lock); + return 0; + } + for (i = 0; i < size; ++i) { + (*data)[i] = ring->ring[ptr++]; + ptr &= ring->ptr_mask; + } + + sx_xunlock(&rdev->ring_lock); + return size; +} + +/** + * radeon_ring_restore - append saved commands to the ring again + * + * @rdev: radeon_device pointer + * @ring: ring to append commands to + * @size: number of dwords we want to write + * @data: saved commands + * + * Allocates space on the ring and restore the previously saved commands. + */ +int radeon_ring_restore(struct radeon_device *rdev, struct radeon_ring *ring, + unsigned size, uint32_t *data) +{ + int i, r; + + if (!size || !data) + return 0; + + /* restore the saved ring content */ + r = radeon_ring_lock(rdev, ring, size); + if (r) + return r; + + for (i = 0; i < size; ++i) { + radeon_ring_write(ring, data[i]); + } + + radeon_ring_unlock_commit(rdev, ring); + free(data, DRM_MEM_DRIVER); + return 0; +} + +/** + * radeon_ring_init - init driver ring struct. + * + * @rdev: radeon_device pointer + * @ring: radeon_ring structure holding ring information + * @ring_size: size of the ring + * @rptr_offs: offset of the rptr writeback location in the WB buffer + * @rptr_reg: MMIO offset of the rptr register + * @wptr_reg: MMIO offset of the wptr register + * @ptr_reg_shift: bit offset of the rptr/wptr values + * @ptr_reg_mask: bit mask of the rptr/wptr values + * @nop: nop packet for this ring + * + * Initialize the driver information for the selected ring (all asics). + * Returns 0 on success, error on failure. + */ +int radeon_ring_init(struct radeon_device *rdev, struct radeon_ring *ring, unsigned ring_size, + unsigned rptr_offs, unsigned rptr_reg, unsigned wptr_reg, + u32 ptr_reg_shift, u32 ptr_reg_mask, u32 nop) +{ + int r; + void *ring_ptr; + + ring->ring_size = ring_size; + ring->rptr_offs = rptr_offs; + ring->rptr_reg = rptr_reg; + ring->wptr_reg = wptr_reg; + ring->ptr_reg_shift = ptr_reg_shift; + ring->ptr_reg_mask = ptr_reg_mask; + ring->nop = nop; + /* Allocate ring buffer */ + if (ring->ring_obj == NULL) { + r = radeon_bo_create(rdev, ring->ring_size, PAGE_SIZE, true, + RADEON_GEM_DOMAIN_GTT, + NULL, &ring->ring_obj); + if (r) { + dev_err(rdev->dev, "(%d) ring create failed\n", r); + return r; + } + r = radeon_bo_reserve(ring->ring_obj, false); + if (unlikely(r != 0)) { + radeon_bo_unref(&ring->ring_obj); + return r; + } + r = radeon_bo_pin(ring->ring_obj, RADEON_GEM_DOMAIN_GTT, + &ring->gpu_addr); + if (r) { + radeon_bo_unreserve(ring->ring_obj); + radeon_bo_unref(&ring->ring_obj); + dev_err(rdev->dev, "(%d) ring pin failed\n", r); + return r; + } + ring_ptr = &ring->ring; + r = radeon_bo_kmap(ring->ring_obj, + ring_ptr); + radeon_bo_unreserve(ring->ring_obj); + if (r) { + dev_err(rdev->dev, "(%d) ring map failed\n", r); + radeon_bo_unref(&ring->ring_obj); + return r; + } + } + ring->ptr_mask = (ring->ring_size / 4) - 1; + ring->ring_free_dw = ring->ring_size / 4; + if (rdev->wb.enabled) { + u32 index = RADEON_WB_RING0_NEXT_RPTR + (ring->idx * 4); + ring->next_rptr_gpu_addr = rdev->wb.gpu_addr + index; + ring->next_rptr_cpu_addr = &rdev->wb.wb[index/4]; + } +#ifdef DUMBBELL_WIP + if (radeon_debugfs_ring_init(rdev, ring)) { + DRM_ERROR("Failed to register debugfs file for rings !\n"); + } +#endif /* DUMBBELL_WIP */ + radeon_ring_lockup_update(ring); + return 0; +} + +/** + * radeon_ring_fini - tear down the driver ring struct. + * + * @rdev: radeon_device pointer + * @ring: radeon_ring structure holding ring information + * + * Tear down the driver information for the selected ring (all asics). + */ +void radeon_ring_fini(struct radeon_device *rdev, struct radeon_ring *ring) +{ + int r; + struct radeon_bo *ring_obj; + + sx_xlock(&rdev->ring_lock); + ring_obj = ring->ring_obj; + ring->ready = false; + ring->ring = NULL; + ring->ring_obj = NULL; + sx_xunlock(&rdev->ring_lock); + + if (ring_obj) { + r = radeon_bo_reserve(ring_obj, false); + if (likely(r == 0)) { + radeon_bo_kunmap(ring_obj); + radeon_bo_unpin(ring_obj); + radeon_bo_unreserve(ring_obj); + } + radeon_bo_unref(&ring_obj); + } +} + +/* + * Debugfs info + */ +#if defined(CONFIG_DEBUG_FS) + +static int radeon_debugfs_ring_info(struct seq_file *m, void *data) +{ + struct drm_info_node *node = (struct drm_info_node *) m->private; + struct drm_device *dev = node->minor->dev; + struct radeon_device *rdev = dev->dev_private; + int ridx = *(int*)node->info_ent->data; + struct radeon_ring *ring = &rdev->ring[ridx]; + unsigned count, i, j; + u32 tmp; + + radeon_ring_free_size(rdev, ring); + count = (ring->ring_size / 4) - ring->ring_free_dw; + tmp = RREG32(ring->wptr_reg) >> ring->ptr_reg_shift; + seq_printf(m, "wptr(0x%04x): 0x%08x [%5d]\n", ring->wptr_reg, tmp, tmp); + tmp = RREG32(ring->rptr_reg) >> ring->ptr_reg_shift; + seq_printf(m, "rptr(0x%04x): 0x%08x [%5d]\n", ring->rptr_reg, tmp, tmp); + if (ring->rptr_save_reg) { + seq_printf(m, "rptr next(0x%04x): 0x%08x\n", ring->rptr_save_reg, + RREG32(ring->rptr_save_reg)); + } + seq_printf(m, "driver's copy of the wptr: 0x%08x [%5d]\n", ring->wptr, ring->wptr); + seq_printf(m, "driver's copy of the rptr: 0x%08x [%5d]\n", ring->rptr, ring->rptr); + seq_printf(m, "last semaphore signal addr : 0x%016llx\n", ring->last_semaphore_signal_addr); + seq_printf(m, "last semaphore wait addr : 0x%016llx\n", ring->last_semaphore_wait_addr); + seq_printf(m, "%u free dwords in ring\n", ring->ring_free_dw); + seq_printf(m, "%u dwords in ring\n", count); + /* print 8 dw before current rptr as often it's the last executed + * packet that is the root issue + */ + i = (ring->rptr + ring->ptr_mask + 1 - 32) & ring->ptr_mask; + for (j = 0; j <= (count + 32); j++) { + seq_printf(m, "r[%5d]=0x%08x\n", i, ring->ring[i]); + i = (i + 1) & ring->ptr_mask; + } + return 0; +} + +static int radeon_ring_type_gfx_index = RADEON_RING_TYPE_GFX_INDEX; +static int cayman_ring_type_cp1_index = CAYMAN_RING_TYPE_CP1_INDEX; +static int cayman_ring_type_cp2_index = CAYMAN_RING_TYPE_CP2_INDEX; +static int radeon_ring_type_dma1_index = R600_RING_TYPE_DMA_INDEX; +static int radeon_ring_type_dma2_index = CAYMAN_RING_TYPE_DMA1_INDEX; + +static struct drm_info_list radeon_debugfs_ring_info_list[] = { + {"radeon_ring_gfx", radeon_debugfs_ring_info, 0, &radeon_ring_type_gfx_index}, + {"radeon_ring_cp1", radeon_debugfs_ring_info, 0, &cayman_ring_type_cp1_index}, + {"radeon_ring_cp2", radeon_debugfs_ring_info, 0, &cayman_ring_type_cp2_index}, + {"radeon_ring_dma1", radeon_debugfs_ring_info, 0, &radeon_ring_type_dma1_index}, + {"radeon_ring_dma2", radeon_debugfs_ring_info, 0, &radeon_ring_type_dma2_index}, +}; + +static int radeon_debugfs_sa_info(struct seq_file *m, void *data) +{ + struct drm_info_node *node = (struct drm_info_node *) m->private; + struct drm_device *dev = node->minor->dev; + struct radeon_device *rdev = dev->dev_private; + + radeon_sa_bo_dump_debug_info(&rdev->ring_tmp_bo, m); + + return 0; + +} + +static struct drm_info_list radeon_debugfs_sa_list[] = { + {"radeon_sa_info", &radeon_debugfs_sa_info, 0, NULL}, +}; + +#endif + +#ifdef DUMBBELL_WIP +static int radeon_debugfs_ring_init(struct radeon_device *rdev, struct radeon_ring *ring) +{ +#if defined(CONFIG_DEBUG_FS) + unsigned i; + for (i = 0; i < ARRAY_SIZE(radeon_debugfs_ring_info_list); ++i) { + struct drm_info_list *info = &radeon_debugfs_ring_info_list[i]; + int ridx = *(int*)radeon_debugfs_ring_info_list[i].data; + unsigned r; + + if (&rdev->ring[ridx] != ring) + continue; + + r = radeon_debugfs_add_files(rdev, info, 1); + if (r) + return r; + } +#endif + return 0; +} + +static int radeon_debugfs_sa_init(struct radeon_device *rdev) +{ +#if defined(CONFIG_DEBUG_FS) + return radeon_debugfs_add_files(rdev, radeon_debugfs_sa_list, 1); +#else + return 0; +#endif +} +#endif /* DUMBBELL_WIP */ diff --git a/sys/dev/drm2/radeon/radeon_sa.c b/sys/dev/drm2/radeon/radeon_sa.c new file mode 100644 index 00000000000..e750812697c --- /dev/null +++ b/sys/dev/drm2/radeon/radeon_sa.c @@ -0,0 +1,428 @@ +/* + * Copyright 2011 Red Hat Inc. + * All Rights Reserved. + * + * 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, sub license, 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 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 NON-INFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS 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. + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + */ +/* + * Authors: + * Jerome Glisse + */ +/* Algorithm: + * + * We store the last allocated bo in "hole", we always try to allocate + * after the last allocated bo. Principle is that in a linear GPU ring + * progression was is after last is the oldest bo we allocated and thus + * the first one that should no longer be in use by the GPU. + * + * If it's not the case we skip over the bo after last to the closest + * done bo if such one exist. If none exist and we are not asked to + * block we report failure to allocate. + * + * If we are asked to block we wait on all the oldest fence of all + * rings. We just wait for any of those fence to complete. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include "radeon.h" + +static void radeon_sa_bo_remove_locked(struct radeon_sa_bo *sa_bo); +static void radeon_sa_bo_try_free(struct radeon_sa_manager *sa_manager); + +int radeon_sa_bo_manager_init(struct radeon_device *rdev, + struct radeon_sa_manager *sa_manager, + unsigned size, u32 domain) +{ + int i, r; + + sx_init(&sa_manager->wq_lock, "drm__radeon_sa_manager_wq_mtx"); + cv_init(&sa_manager->wq, "drm__radeon_sa_manager__wq"); + sa_manager->bo = NULL; + sa_manager->size = size; + sa_manager->domain = domain; + sa_manager->hole = &sa_manager->olist; + INIT_LIST_HEAD(&sa_manager->olist); + for (i = 0; i < RADEON_NUM_RINGS; ++i) { + INIT_LIST_HEAD(&sa_manager->flist[i]); + } + + r = radeon_bo_create(rdev, size, RADEON_GPU_PAGE_SIZE, true, + RADEON_GEM_DOMAIN_CPU, NULL, &sa_manager->bo); + if (r) { + dev_err(rdev->dev, "(%d) failed to allocate bo for manager\n", r); + return r; + } + + return r; +} + +void radeon_sa_bo_manager_fini(struct radeon_device *rdev, + struct radeon_sa_manager *sa_manager) +{ + struct radeon_sa_bo *sa_bo, *tmp; + + if (!list_empty(&sa_manager->olist)) { + sa_manager->hole = &sa_manager->olist, + radeon_sa_bo_try_free(sa_manager); + if (!list_empty(&sa_manager->olist)) { + dev_err(rdev->dev, "sa_manager is not empty, clearing anyway\n"); + } + } + list_for_each_entry_safe(sa_bo, tmp, &sa_manager->olist, olist) { + radeon_sa_bo_remove_locked(sa_bo); + } + radeon_bo_unref(&sa_manager->bo); + sa_manager->size = 0; + cv_destroy(&sa_manager->wq); + sx_destroy(&sa_manager->wq_lock); +} + +int radeon_sa_bo_manager_start(struct radeon_device *rdev, + struct radeon_sa_manager *sa_manager) +{ + int r; + + if (sa_manager->bo == NULL) { + dev_err(rdev->dev, "no bo for sa manager\n"); + return -EINVAL; + } + + /* map the buffer */ + r = radeon_bo_reserve(sa_manager->bo, false); + if (r) { + dev_err(rdev->dev, "(%d) failed to reserve manager bo\n", r); + return r; + } + r = radeon_bo_pin(sa_manager->bo, sa_manager->domain, &sa_manager->gpu_addr); + if (r) { + radeon_bo_unreserve(sa_manager->bo); + dev_err(rdev->dev, "(%d) failed to pin manager bo\n", r); + return r; + } + r = radeon_bo_kmap(sa_manager->bo, &sa_manager->cpu_ptr); + radeon_bo_unreserve(sa_manager->bo); + return r; +} + +int radeon_sa_bo_manager_suspend(struct radeon_device *rdev, + struct radeon_sa_manager *sa_manager) +{ + int r; + + if (sa_manager->bo == NULL) { + dev_err(rdev->dev, "no bo for sa manager\n"); + return -EINVAL; + } + + r = radeon_bo_reserve(sa_manager->bo, false); + if (!r) { + radeon_bo_kunmap(sa_manager->bo); + radeon_bo_unpin(sa_manager->bo); + radeon_bo_unreserve(sa_manager->bo); + } + return r; +} + +static void radeon_sa_bo_remove_locked(struct radeon_sa_bo *sa_bo) +{ + struct radeon_sa_manager *sa_manager = sa_bo->manager; + if (sa_manager->hole == &sa_bo->olist) { + sa_manager->hole = sa_bo->olist.prev; + } + list_del_init(&sa_bo->olist); + list_del_init(&sa_bo->flist); + radeon_fence_unref(&sa_bo->fence); + free(sa_bo, DRM_MEM_DRIVER); +} + +static void radeon_sa_bo_try_free(struct radeon_sa_manager *sa_manager) +{ + struct radeon_sa_bo *sa_bo, *tmp; + + if (sa_manager->hole->next == &sa_manager->olist) + return; + + sa_bo = list_entry(sa_manager->hole->next, struct radeon_sa_bo, olist); + list_for_each_entry_safe_from(sa_bo, tmp, &sa_manager->olist, olist) { + if (sa_bo->fence == NULL || !radeon_fence_signaled(sa_bo->fence)) { + return; + } + radeon_sa_bo_remove_locked(sa_bo); + } +} + +static inline unsigned radeon_sa_bo_hole_soffset(struct radeon_sa_manager *sa_manager) +{ + struct list_head *hole = sa_manager->hole; + + if (hole != &sa_manager->olist) { + return list_entry(hole, struct radeon_sa_bo, olist)->eoffset; + } + return 0; +} + +static inline unsigned radeon_sa_bo_hole_eoffset(struct radeon_sa_manager *sa_manager) +{ + struct list_head *hole = sa_manager->hole; + + if (hole->next != &sa_manager->olist) { + return list_entry(hole->next, struct radeon_sa_bo, olist)->soffset; + } + return sa_manager->size; +} + +static bool radeon_sa_bo_try_alloc(struct radeon_sa_manager *sa_manager, + struct radeon_sa_bo *sa_bo, + unsigned size, unsigned align) +{ + unsigned soffset, eoffset, wasted; + + soffset = radeon_sa_bo_hole_soffset(sa_manager); + eoffset = radeon_sa_bo_hole_eoffset(sa_manager); + wasted = (align - (soffset % align)) % align; + + if ((eoffset - soffset) >= (size + wasted)) { + soffset += wasted; + + sa_bo->manager = sa_manager; + sa_bo->soffset = soffset; + sa_bo->eoffset = soffset + size; + list_add(&sa_bo->olist, sa_manager->hole); + INIT_LIST_HEAD(&sa_bo->flist); + sa_manager->hole = &sa_bo->olist; + return true; + } + return false; +} + +/** + * radeon_sa_event - Check if we can stop waiting + * + * @sa_manager: pointer to the sa_manager + * @size: number of bytes we want to allocate + * @align: alignment we need to match + * + * Check if either there is a fence we can wait for or + * enough free memory to satisfy the allocation directly + */ +static bool radeon_sa_event(struct radeon_sa_manager *sa_manager, + unsigned size, unsigned align) +{ + unsigned soffset, eoffset, wasted; + int i; + + for (i = 0; i < RADEON_NUM_RINGS; ++i) { + if (!list_empty(&sa_manager->flist[i])) { + return true; + } + } + + soffset = radeon_sa_bo_hole_soffset(sa_manager); + eoffset = radeon_sa_bo_hole_eoffset(sa_manager); + wasted = (align - (soffset % align)) % align; + + if ((eoffset - soffset) >= (size + wasted)) { + return true; + } + + return false; +} + +static bool radeon_sa_bo_next_hole(struct radeon_sa_manager *sa_manager, + struct radeon_fence **fences, + unsigned *tries) +{ + struct radeon_sa_bo *best_bo = NULL; + unsigned i, soffset, best, tmp; + + /* if hole points to the end of the buffer */ + if (sa_manager->hole->next == &sa_manager->olist) { + /* try again with its beginning */ + sa_manager->hole = &sa_manager->olist; + return true; + } + + soffset = radeon_sa_bo_hole_soffset(sa_manager); + /* to handle wrap around we add sa_manager->size */ + best = sa_manager->size * 2; + /* go over all fence list and try to find the closest sa_bo + * of the current last + */ + for (i = 0; i < RADEON_NUM_RINGS; ++i) { + struct radeon_sa_bo *sa_bo; + + if (list_empty(&sa_manager->flist[i])) { + continue; + } + + sa_bo = list_first_entry(&sa_manager->flist[i], + struct radeon_sa_bo, flist); + + if (!radeon_fence_signaled(sa_bo->fence)) { + fences[i] = sa_bo->fence; + continue; + } + + /* limit the number of tries each ring gets */ + if (tries[i] > 2) { + continue; + } + + tmp = sa_bo->soffset; + if (tmp < soffset) { + /* wrap around, pretend it's after */ + tmp += sa_manager->size; + } + tmp -= soffset; + if (tmp < best) { + /* this sa bo is the closest one */ + best = tmp; + best_bo = sa_bo; + } + } + + if (best_bo) { + ++tries[best_bo->fence->ring]; + sa_manager->hole = best_bo->olist.prev; + + /* we knew that this one is signaled, + so it's save to remote it */ + radeon_sa_bo_remove_locked(best_bo); + return true; + } + return false; +} + +int radeon_sa_bo_new(struct radeon_device *rdev, + struct radeon_sa_manager *sa_manager, + struct radeon_sa_bo **sa_bo, + unsigned size, unsigned align, bool block) +{ + struct radeon_fence *fences[RADEON_NUM_RINGS]; + unsigned tries[RADEON_NUM_RINGS]; + int i, r; + + KASSERT(align <= RADEON_GPU_PAGE_SIZE, ("align > RADEON_GPU_PAGE_SIZE")); + KASSERT(size <= sa_manager->size, ("size > sa_manager->size")); + + *sa_bo = malloc(sizeof(struct radeon_sa_bo), DRM_MEM_DRIVER, M_WAITOK); + if ((*sa_bo) == NULL) { + return -ENOMEM; + } + (*sa_bo)->manager = sa_manager; + (*sa_bo)->fence = NULL; + INIT_LIST_HEAD(&(*sa_bo)->olist); + INIT_LIST_HEAD(&(*sa_bo)->flist); + + sx_xlock(&sa_manager->wq_lock); + do { + for (i = 0; i < RADEON_NUM_RINGS; ++i) { + fences[i] = NULL; + tries[i] = 0; + } + + do { + radeon_sa_bo_try_free(sa_manager); + + if (radeon_sa_bo_try_alloc(sa_manager, *sa_bo, + size, align)) { + sx_xunlock(&sa_manager->wq_lock); + return 0; + } + + /* see if we can skip over some allocations */ + } while (radeon_sa_bo_next_hole(sa_manager, fences, tries)); + + sx_xunlock(&sa_manager->wq_lock); + r = radeon_fence_wait_any(rdev, fences, false); + sx_xlock(&sa_manager->wq_lock); + /* if we have nothing to wait for block */ + if (r == -ENOENT && block) { + while (!radeon_sa_event(sa_manager, size, align)) { + r = -cv_wait_sig(&sa_manager->wq, + &sa_manager->wq_lock); + if (r != 0) + break; + } + + } else if (r == -ENOENT) { + r = -ENOMEM; + } + + } while (!r); + + sx_xunlock(&sa_manager->wq_lock); + free(*sa_bo, DRM_MEM_DRIVER); + *sa_bo = NULL; + return r; +} + +void radeon_sa_bo_free(struct radeon_device *rdev, struct radeon_sa_bo **sa_bo, + struct radeon_fence *fence) +{ + struct radeon_sa_manager *sa_manager; + + if (sa_bo == NULL || *sa_bo == NULL) { + return; + } + + sa_manager = (*sa_bo)->manager; + sx_xlock(&sa_manager->wq_lock); + if (fence && !radeon_fence_signaled(fence)) { + (*sa_bo)->fence = radeon_fence_ref(fence); + list_add_tail(&(*sa_bo)->flist, + &sa_manager->flist[fence->ring]); + } else { + radeon_sa_bo_remove_locked(*sa_bo); + } + cv_broadcast(&sa_manager->wq); + sx_xunlock(&sa_manager->wq_lock); + *sa_bo = NULL; +} + +#if defined(CONFIG_DEBUG_FS) +void radeon_sa_bo_dump_debug_info(struct radeon_sa_manager *sa_manager, + struct seq_file *m) +{ + struct radeon_sa_bo *i; + + spin_lock(&sa_manager->wq.lock); + list_for_each_entry(i, &sa_manager->olist, olist) { + if (&i->olist == sa_manager->hole) { + seq_printf(m, ">"); + } else { + seq_printf(m, " "); + } + seq_printf(m, "[0x%08x 0x%08x] size %8d", + i->soffset, i->eoffset, i->eoffset - i->soffset); + if (i->fence) { + seq_printf(m, " protected by 0x%016llx on ring %d", + i->fence->seq, i->fence->ring); + } + seq_printf(m, "\n"); + } + spin_unlock(&sa_manager->wq.lock); +} +#endif diff --git a/sys/dev/drm2/radeon/radeon_semaphore.c b/sys/dev/drm2/radeon/radeon_semaphore.c new file mode 100644 index 00000000000..717a94a482d --- /dev/null +++ b/sys/dev/drm2/radeon/radeon_semaphore.c @@ -0,0 +1,124 @@ +/* + * Copyright 2011 Christian König. + * All Rights Reserved. + * + * 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, sub license, 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 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 NON-INFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS 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. + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + */ + +#include +__FBSDID("$FreeBSD$"); + +/* + * Authors: + * Christian König + */ +#include +#include "radeon.h" + + +int radeon_semaphore_create(struct radeon_device *rdev, + struct radeon_semaphore **semaphore) +{ + int r; + + *semaphore = malloc(sizeof(struct radeon_semaphore), + DRM_MEM_DRIVER, M_WAITOK); + if (*semaphore == NULL) { + return -ENOMEM; + } + r = radeon_sa_bo_new(rdev, &rdev->ring_tmp_bo, + &(*semaphore)->sa_bo, 8, 8, true); + if (r) { + free(*semaphore, DRM_MEM_DRIVER); + *semaphore = NULL; + return r; + } + (*semaphore)->waiters = 0; + (*semaphore)->gpu_addr = radeon_sa_bo_gpu_addr((*semaphore)->sa_bo); + *((uint64_t*)radeon_sa_bo_cpu_addr((*semaphore)->sa_bo)) = 0; + return 0; +} + +void radeon_semaphore_emit_signal(struct radeon_device *rdev, int ring, + struct radeon_semaphore *semaphore) +{ + --semaphore->waiters; + radeon_semaphore_ring_emit(rdev, ring, &rdev->ring[ring], semaphore, false); +} + +void radeon_semaphore_emit_wait(struct radeon_device *rdev, int ring, + struct radeon_semaphore *semaphore) +{ + ++semaphore->waiters; + radeon_semaphore_ring_emit(rdev, ring, &rdev->ring[ring], semaphore, true); +} + +/* caller must hold ring lock */ +int radeon_semaphore_sync_rings(struct radeon_device *rdev, + struct radeon_semaphore *semaphore, + int signaler, int waiter) +{ + int r; + + /* no need to signal and wait on the same ring */ + if (signaler == waiter) { + return 0; + } + + /* prevent GPU deadlocks */ + if (!rdev->ring[signaler].ready) { + dev_err(rdev->dev, "Trying to sync to a disabled ring!"); + return -EINVAL; + } + + r = radeon_ring_alloc(rdev, &rdev->ring[signaler], 8); + if (r) { + return r; + } + radeon_semaphore_emit_signal(rdev, signaler, semaphore); + radeon_ring_commit(rdev, &rdev->ring[signaler]); + + /* we assume caller has already allocated space on waiters ring */ + radeon_semaphore_emit_wait(rdev, waiter, semaphore); + + /* for debugging lockup only, used by sysfs debug files */ + rdev->ring[signaler].last_semaphore_signal_addr = semaphore->gpu_addr; + rdev->ring[waiter].last_semaphore_wait_addr = semaphore->gpu_addr; + + return 0; +} + +void radeon_semaphore_free(struct radeon_device *rdev, + struct radeon_semaphore **semaphore, + struct radeon_fence *fence) +{ + if (semaphore == NULL || *semaphore == NULL) { + return; + } + if ((*semaphore)->waiters > 0) { + dev_err(rdev->dev, "semaphore %p has more waiters than signalers," + " hardware lockup imminent!\n", *semaphore); + } + radeon_sa_bo_free(rdev, &(*semaphore)->sa_bo, fence); + free(*semaphore, DRM_MEM_DRIVER); + *semaphore = NULL; +} diff --git a/sys/dev/drm2/radeon/radeon_state.c b/sys/dev/drm2/radeon/radeon_state.c new file mode 100644 index 00000000000..1716830f190 --- /dev/null +++ b/sys/dev/drm2/radeon/radeon_state.c @@ -0,0 +1,3262 @@ +/* radeon_state.c -- State support for Radeon -*- linux-c -*- */ +/* + * Copyright 2000 VA Linux Systems, Inc., Fremont, California. + * All Rights Reserved. + * + * 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 (including the next + * paragraph) 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 + * PRECISION INSIGHT AND/OR ITS SUPPLIERS 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. + * + * Authors: + * Gareth Hughes + * Kevin E. Martin + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include "radeon_drv.h" + +/* ================================================================ + * Helper functions for client state checking and fixup + */ + +static __inline__ int radeon_check_and_fixup_offset(drm_radeon_private_t * + dev_priv, + struct drm_file * file_priv, + u32 *offset) +{ + u64 off = *offset; + u32 fb_end = dev_priv->fb_location + dev_priv->fb_size - 1; + struct drm_radeon_driver_file_fields *radeon_priv; + + /* Hrm ... the story of the offset ... So this function converts + * the various ideas of what userland clients might have for an + * offset in the card address space into an offset into the card + * address space :) So with a sane client, it should just keep + * the value intact and just do some boundary checking. However, + * not all clients are sane. Some older clients pass us 0 based + * offsets relative to the start of the framebuffer and some may + * assume the AGP aperture it appended to the framebuffer, so we + * try to detect those cases and fix them up. + * + * Note: It might be a good idea here to make sure the offset lands + * in some "allowed" area to protect things like the PCIE GART... + */ + + /* First, the best case, the offset already lands in either the + * framebuffer or the GART mapped space + */ + if (radeon_check_offset(dev_priv, off)) + return 0; + + /* Ok, that didn't happen... now check if we have a zero based + * offset that fits in the framebuffer + gart space, apply the + * magic offset we get from SETPARAM or calculated from fb_location + */ + if (off < (dev_priv->fb_size + dev_priv->gart_size)) { + radeon_priv = file_priv->driver_priv; + off += radeon_priv->radeon_fb_delta; + } + + /* Finally, assume we aimed at a GART offset if beyond the fb */ + if (off > fb_end) + off = off - fb_end - 1 + dev_priv->gart_vm_start; + + /* Now recheck and fail if out of bounds */ + if (radeon_check_offset(dev_priv, off)) { + DRM_DEBUG("offset fixed up to 0x%x\n", (unsigned int)off); + *offset = off; + return 0; + } + return -EINVAL; +} + +static __inline__ int radeon_check_and_fixup_packets(drm_radeon_private_t * + dev_priv, + struct drm_file *file_priv, + int id, struct drm_buffer *buf) +{ + u32 *data; + switch (id) { + + case RADEON_EMIT_PP_MISC: + data = drm_buffer_pointer_to_dword(buf, + (RADEON_RB3D_DEPTHOFFSET - RADEON_PP_MISC) / 4); + + if (radeon_check_and_fixup_offset(dev_priv, file_priv, data)) { + DRM_ERROR("Invalid depth buffer offset\n"); + return -EINVAL; + } + dev_priv->have_z_offset = 1; + break; + + case RADEON_EMIT_PP_CNTL: + data = drm_buffer_pointer_to_dword(buf, + (RADEON_RB3D_COLOROFFSET - RADEON_PP_CNTL) / 4); + + if (radeon_check_and_fixup_offset(dev_priv, file_priv, data)) { + DRM_ERROR("Invalid colour buffer offset\n"); + return -EINVAL; + } + break; + + case R200_EMIT_PP_TXOFFSET_0: + case R200_EMIT_PP_TXOFFSET_1: + case R200_EMIT_PP_TXOFFSET_2: + case R200_EMIT_PP_TXOFFSET_3: + case R200_EMIT_PP_TXOFFSET_4: + case R200_EMIT_PP_TXOFFSET_5: + data = drm_buffer_pointer_to_dword(buf, 0); + if (radeon_check_and_fixup_offset(dev_priv, file_priv, data)) { + DRM_ERROR("Invalid R200 texture offset\n"); + return -EINVAL; + } + break; + + case RADEON_EMIT_PP_TXFILTER_0: + case RADEON_EMIT_PP_TXFILTER_1: + case RADEON_EMIT_PP_TXFILTER_2: + data = drm_buffer_pointer_to_dword(buf, + (RADEON_PP_TXOFFSET_0 - RADEON_PP_TXFILTER_0) / 4); + if (radeon_check_and_fixup_offset(dev_priv, file_priv, data)) { + DRM_ERROR("Invalid R100 texture offset\n"); + return -EINVAL; + } + break; + + case R200_EMIT_PP_CUBIC_OFFSETS_0: + case R200_EMIT_PP_CUBIC_OFFSETS_1: + case R200_EMIT_PP_CUBIC_OFFSETS_2: + case R200_EMIT_PP_CUBIC_OFFSETS_3: + case R200_EMIT_PP_CUBIC_OFFSETS_4: + case R200_EMIT_PP_CUBIC_OFFSETS_5:{ + int i; + for (i = 0; i < 5; i++) { + data = drm_buffer_pointer_to_dword(buf, i); + if (radeon_check_and_fixup_offset(dev_priv, + file_priv, + data)) { + DRM_ERROR + ("Invalid R200 cubic texture offset\n"); + return -EINVAL; + } + } + break; + } + + case RADEON_EMIT_PP_CUBIC_OFFSETS_T0: + case RADEON_EMIT_PP_CUBIC_OFFSETS_T1: + case RADEON_EMIT_PP_CUBIC_OFFSETS_T2:{ + int i; + for (i = 0; i < 5; i++) { + data = drm_buffer_pointer_to_dword(buf, i); + if (radeon_check_and_fixup_offset(dev_priv, + file_priv, + data)) { + DRM_ERROR + ("Invalid R100 cubic texture offset\n"); + return -EINVAL; + } + } + } + break; + + case R200_EMIT_VAP_CTL:{ + RING_LOCALS; + BEGIN_RING(2); + OUT_RING_REG(RADEON_SE_TCL_STATE_FLUSH, 0); + ADVANCE_RING(); + } + break; + + case RADEON_EMIT_RB3D_COLORPITCH: + case RADEON_EMIT_RE_LINE_PATTERN: + case RADEON_EMIT_SE_LINE_WIDTH: + case RADEON_EMIT_PP_LUM_MATRIX: + case RADEON_EMIT_PP_ROT_MATRIX_0: + case RADEON_EMIT_RB3D_STENCILREFMASK: + case RADEON_EMIT_SE_VPORT_XSCALE: + case RADEON_EMIT_SE_CNTL: + case RADEON_EMIT_SE_CNTL_STATUS: + case RADEON_EMIT_RE_MISC: + case RADEON_EMIT_PP_BORDER_COLOR_0: + case RADEON_EMIT_PP_BORDER_COLOR_1: + case RADEON_EMIT_PP_BORDER_COLOR_2: + case RADEON_EMIT_SE_ZBIAS_FACTOR: + case RADEON_EMIT_SE_TCL_OUTPUT_VTX_FMT: + case RADEON_EMIT_SE_TCL_MATERIAL_EMMISSIVE_RED: + case R200_EMIT_PP_TXCBLEND_0: + case R200_EMIT_PP_TXCBLEND_1: + case R200_EMIT_PP_TXCBLEND_2: + case R200_EMIT_PP_TXCBLEND_3: + case R200_EMIT_PP_TXCBLEND_4: + case R200_EMIT_PP_TXCBLEND_5: + case R200_EMIT_PP_TXCBLEND_6: + case R200_EMIT_PP_TXCBLEND_7: + case R200_EMIT_TCL_LIGHT_MODEL_CTL_0: + case R200_EMIT_TFACTOR_0: + case R200_EMIT_VTX_FMT_0: + case R200_EMIT_MATRIX_SELECT_0: + case R200_EMIT_TEX_PROC_CTL_2: + case R200_EMIT_TCL_UCP_VERT_BLEND_CTL: + case R200_EMIT_PP_TXFILTER_0: + case R200_EMIT_PP_TXFILTER_1: + case R200_EMIT_PP_TXFILTER_2: + case R200_EMIT_PP_TXFILTER_3: + case R200_EMIT_PP_TXFILTER_4: + case R200_EMIT_PP_TXFILTER_5: + case R200_EMIT_VTE_CNTL: + case R200_EMIT_OUTPUT_VTX_COMP_SEL: + case R200_EMIT_PP_TAM_DEBUG3: + case R200_EMIT_PP_CNTL_X: + case R200_EMIT_RB3D_DEPTHXY_OFFSET: + case R200_EMIT_RE_AUX_SCISSOR_CNTL: + case R200_EMIT_RE_SCISSOR_TL_0: + case R200_EMIT_RE_SCISSOR_TL_1: + case R200_EMIT_RE_SCISSOR_TL_2: + case R200_EMIT_SE_VAP_CNTL_STATUS: + case R200_EMIT_SE_VTX_STATE_CNTL: + case R200_EMIT_RE_POINTSIZE: + case R200_EMIT_TCL_INPUT_VTX_VECTOR_ADDR_0: + case R200_EMIT_PP_CUBIC_FACES_0: + case R200_EMIT_PP_CUBIC_FACES_1: + case R200_EMIT_PP_CUBIC_FACES_2: + case R200_EMIT_PP_CUBIC_FACES_3: + case R200_EMIT_PP_CUBIC_FACES_4: + case R200_EMIT_PP_CUBIC_FACES_5: + case RADEON_EMIT_PP_TEX_SIZE_0: + case RADEON_EMIT_PP_TEX_SIZE_1: + case RADEON_EMIT_PP_TEX_SIZE_2: + case R200_EMIT_RB3D_BLENDCOLOR: + case R200_EMIT_TCL_POINT_SPRITE_CNTL: + case RADEON_EMIT_PP_CUBIC_FACES_0: + case RADEON_EMIT_PP_CUBIC_FACES_1: + case RADEON_EMIT_PP_CUBIC_FACES_2: + case R200_EMIT_PP_TRI_PERF_CNTL: + case R200_EMIT_PP_AFS_0: + case R200_EMIT_PP_AFS_1: + case R200_EMIT_ATF_TFACTOR: + case R200_EMIT_PP_TXCTLALL_0: + case R200_EMIT_PP_TXCTLALL_1: + case R200_EMIT_PP_TXCTLALL_2: + case R200_EMIT_PP_TXCTLALL_3: + case R200_EMIT_PP_TXCTLALL_4: + case R200_EMIT_PP_TXCTLALL_5: + case R200_EMIT_VAP_PVS_CNTL: + /* These packets don't contain memory offsets */ + break; + + default: + DRM_ERROR("Unknown state packet ID %d\n", id); + return -EINVAL; + } + + return 0; +} + +static int radeon_check_and_fixup_packet3(drm_radeon_private_t * + dev_priv, + struct drm_file *file_priv, + drm_radeon_kcmd_buffer_t * + cmdbuf, + unsigned int *cmdsz) +{ + u32 *cmd = drm_buffer_pointer_to_dword(cmdbuf->buffer, 0); + u32 offset, narrays; + int count, i, k; + + count = ((*cmd & RADEON_CP_PACKET_COUNT_MASK) >> 16); + *cmdsz = 2 + count; + + if ((*cmd & 0xc0000000) != RADEON_CP_PACKET3) { + DRM_ERROR("Not a type 3 packet\n"); + return -EINVAL; + } + + if (4 * *cmdsz > drm_buffer_unprocessed(cmdbuf->buffer)) { + DRM_ERROR("Packet size larger than size of data provided\n"); + return -EINVAL; + } + + switch (*cmd & 0xff00) { + /* XXX Are there old drivers needing other packets? */ + + case RADEON_3D_DRAW_IMMD: + case RADEON_3D_DRAW_VBUF: + case RADEON_3D_DRAW_INDX: + case RADEON_WAIT_FOR_IDLE: + case RADEON_CP_NOP: + case RADEON_3D_CLEAR_ZMASK: +/* case RADEON_CP_NEXT_CHAR: + case RADEON_CP_PLY_NEXTSCAN: + case RADEON_CP_SET_SCISSORS: */ /* probably safe but will never need them? */ + /* these packets are safe */ + break; + + case RADEON_CP_3D_DRAW_IMMD_2: + case RADEON_CP_3D_DRAW_VBUF_2: + case RADEON_CP_3D_DRAW_INDX_2: + case RADEON_3D_CLEAR_HIZ: + /* safe but r200 only */ + if (dev_priv->microcode_version != UCODE_R200) { + DRM_ERROR("Invalid 3d packet for r100-class chip\n"); + return -EINVAL; + } + break; + + case RADEON_3D_LOAD_VBPNTR: + + if (count > 18) { /* 12 arrays max */ + DRM_ERROR("Too large payload in 3D_LOAD_VBPNTR (count=%d)\n", + count); + return -EINVAL; + } + + /* carefully check packet contents */ + cmd = drm_buffer_pointer_to_dword(cmdbuf->buffer, 1); + + narrays = *cmd & ~0xc000; + k = 0; + i = 2; + while ((k < narrays) && (i < (count + 2))) { + i++; /* skip attribute field */ + cmd = drm_buffer_pointer_to_dword(cmdbuf->buffer, i); + if (radeon_check_and_fixup_offset(dev_priv, file_priv, + cmd)) { + DRM_ERROR + ("Invalid offset (k=%d i=%d) in 3D_LOAD_VBPNTR packet.\n", + k, i); + return -EINVAL; + } + k++; + i++; + if (k == narrays) + break; + /* have one more to process, they come in pairs */ + cmd = drm_buffer_pointer_to_dword(cmdbuf->buffer, i); + + if (radeon_check_and_fixup_offset(dev_priv, + file_priv, cmd)) + { + DRM_ERROR + ("Invalid offset (k=%d i=%d) in 3D_LOAD_VBPNTR packet.\n", + k, i); + return -EINVAL; + } + k++; + i++; + } + /* do the counts match what we expect ? */ + if ((k != narrays) || (i != (count + 2))) { + DRM_ERROR + ("Malformed 3D_LOAD_VBPNTR packet (k=%d i=%d narrays=%d count+1=%d).\n", + k, i, narrays, count + 1); + return -EINVAL; + } + break; + + case RADEON_3D_RNDR_GEN_INDX_PRIM: + if (dev_priv->microcode_version != UCODE_R100) { + DRM_ERROR("Invalid 3d packet for r200-class chip\n"); + return -EINVAL; + } + + cmd = drm_buffer_pointer_to_dword(cmdbuf->buffer, 1); + if (radeon_check_and_fixup_offset(dev_priv, file_priv, cmd)) { + DRM_ERROR("Invalid rndr_gen_indx offset\n"); + return -EINVAL; + } + break; + + case RADEON_CP_INDX_BUFFER: + if (dev_priv->microcode_version != UCODE_R200) { + DRM_ERROR("Invalid 3d packet for r100-class chip\n"); + return -EINVAL; + } + + cmd = drm_buffer_pointer_to_dword(cmdbuf->buffer, 1); + if ((*cmd & 0x8000ffff) != 0x80000810) { + DRM_ERROR("Invalid indx_buffer reg address %08X\n", *cmd); + return -EINVAL; + } + cmd = drm_buffer_pointer_to_dword(cmdbuf->buffer, 2); + if (radeon_check_and_fixup_offset(dev_priv, file_priv, cmd)) { + DRM_ERROR("Invalid indx_buffer offset is %08X\n", *cmd); + return -EINVAL; + } + break; + + case RADEON_CNTL_HOSTDATA_BLT: + case RADEON_CNTL_PAINT_MULTI: + case RADEON_CNTL_BITBLT_MULTI: + /* MSB of opcode: next DWORD GUI_CNTL */ + cmd = drm_buffer_pointer_to_dword(cmdbuf->buffer, 1); + if (*cmd & (RADEON_GMC_SRC_PITCH_OFFSET_CNTL + | RADEON_GMC_DST_PITCH_OFFSET_CNTL)) { + u32 *cmd2 = drm_buffer_pointer_to_dword(cmdbuf->buffer, 2); + offset = *cmd2 << 10; + if (radeon_check_and_fixup_offset + (dev_priv, file_priv, &offset)) { + DRM_ERROR("Invalid first packet offset\n"); + return -EINVAL; + } + *cmd2 = (*cmd2 & 0xffc00000) | offset >> 10; + } + + if ((*cmd & RADEON_GMC_SRC_PITCH_OFFSET_CNTL) && + (*cmd & RADEON_GMC_DST_PITCH_OFFSET_CNTL)) { + u32 *cmd3 = drm_buffer_pointer_to_dword(cmdbuf->buffer, 3); + offset = *cmd3 << 10; + if (radeon_check_and_fixup_offset + (dev_priv, file_priv, &offset)) { + DRM_ERROR("Invalid second packet offset\n"); + return -EINVAL; + } + *cmd3 = (*cmd3 & 0xffc00000) | offset >> 10; + } + break; + + default: + DRM_ERROR("Invalid packet type %x\n", *cmd & 0xff00); + return -EINVAL; + } + + return 0; +} + +/* ================================================================ + * CP hardware state programming functions + */ + +static void radeon_emit_clip_rect(drm_radeon_private_t * dev_priv, + struct drm_clip_rect * box) +{ + RING_LOCALS; + + DRM_DEBUG(" box: x1=%d y1=%d x2=%d y2=%d\n", + box->x1, box->y1, box->x2, box->y2); + + BEGIN_RING(4); + OUT_RING(CP_PACKET0(RADEON_RE_TOP_LEFT, 0)); + OUT_RING((box->y1 << 16) | box->x1); + OUT_RING(CP_PACKET0(RADEON_RE_WIDTH_HEIGHT, 0)); + OUT_RING(((box->y2 - 1) << 16) | (box->x2 - 1)); + ADVANCE_RING(); +} + +/* Emit 1.1 state + */ +static int radeon_emit_state(drm_radeon_private_t * dev_priv, + struct drm_file *file_priv, + drm_radeon_context_regs_t * ctx, + drm_radeon_texture_regs_t * tex, + unsigned int dirty) +{ + RING_LOCALS; + DRM_DEBUG("dirty=0x%08x\n", dirty); + + if (dirty & RADEON_UPLOAD_CONTEXT) { + if (radeon_check_and_fixup_offset(dev_priv, file_priv, + &ctx->rb3d_depthoffset)) { + DRM_ERROR("Invalid depth buffer offset\n"); + return -EINVAL; + } + + if (radeon_check_and_fixup_offset(dev_priv, file_priv, + &ctx->rb3d_coloroffset)) { + DRM_ERROR("Invalid depth buffer offset\n"); + return -EINVAL; + } + + BEGIN_RING(14); + OUT_RING(CP_PACKET0(RADEON_PP_MISC, 6)); + OUT_RING(ctx->pp_misc); + OUT_RING(ctx->pp_fog_color); + OUT_RING(ctx->re_solid_color); + OUT_RING(ctx->rb3d_blendcntl); + OUT_RING(ctx->rb3d_depthoffset); + OUT_RING(ctx->rb3d_depthpitch); + OUT_RING(ctx->rb3d_zstencilcntl); + OUT_RING(CP_PACKET0(RADEON_PP_CNTL, 2)); + OUT_RING(ctx->pp_cntl); + OUT_RING(ctx->rb3d_cntl); + OUT_RING(ctx->rb3d_coloroffset); + OUT_RING(CP_PACKET0(RADEON_RB3D_COLORPITCH, 0)); + OUT_RING(ctx->rb3d_colorpitch); + ADVANCE_RING(); + } + + if (dirty & RADEON_UPLOAD_VERTFMT) { + BEGIN_RING(2); + OUT_RING(CP_PACKET0(RADEON_SE_COORD_FMT, 0)); + OUT_RING(ctx->se_coord_fmt); + ADVANCE_RING(); + } + + if (dirty & RADEON_UPLOAD_LINE) { + BEGIN_RING(5); + OUT_RING(CP_PACKET0(RADEON_RE_LINE_PATTERN, 1)); + OUT_RING(ctx->re_line_pattern); + OUT_RING(ctx->re_line_state); + OUT_RING(CP_PACKET0(RADEON_SE_LINE_WIDTH, 0)); + OUT_RING(ctx->se_line_width); + ADVANCE_RING(); + } + + if (dirty & RADEON_UPLOAD_BUMPMAP) { + BEGIN_RING(5); + OUT_RING(CP_PACKET0(RADEON_PP_LUM_MATRIX, 0)); + OUT_RING(ctx->pp_lum_matrix); + OUT_RING(CP_PACKET0(RADEON_PP_ROT_MATRIX_0, 1)); + OUT_RING(ctx->pp_rot_matrix_0); + OUT_RING(ctx->pp_rot_matrix_1); + ADVANCE_RING(); + } + + if (dirty & RADEON_UPLOAD_MASKS) { + BEGIN_RING(4); + OUT_RING(CP_PACKET0(RADEON_RB3D_STENCILREFMASK, 2)); + OUT_RING(ctx->rb3d_stencilrefmask); + OUT_RING(ctx->rb3d_ropcntl); + OUT_RING(ctx->rb3d_planemask); + ADVANCE_RING(); + } + + if (dirty & RADEON_UPLOAD_VIEWPORT) { + BEGIN_RING(7); + OUT_RING(CP_PACKET0(RADEON_SE_VPORT_XSCALE, 5)); + OUT_RING(ctx->se_vport_xscale); + OUT_RING(ctx->se_vport_xoffset); + OUT_RING(ctx->se_vport_yscale); + OUT_RING(ctx->se_vport_yoffset); + OUT_RING(ctx->se_vport_zscale); + OUT_RING(ctx->se_vport_zoffset); + ADVANCE_RING(); + } + + if (dirty & RADEON_UPLOAD_SETUP) { + BEGIN_RING(4); + OUT_RING(CP_PACKET0(RADEON_SE_CNTL, 0)); + OUT_RING(ctx->se_cntl); + OUT_RING(CP_PACKET0(RADEON_SE_CNTL_STATUS, 0)); + OUT_RING(ctx->se_cntl_status); + ADVANCE_RING(); + } + + if (dirty & RADEON_UPLOAD_MISC) { + BEGIN_RING(2); + OUT_RING(CP_PACKET0(RADEON_RE_MISC, 0)); + OUT_RING(ctx->re_misc); + ADVANCE_RING(); + } + + if (dirty & RADEON_UPLOAD_TEX0) { + if (radeon_check_and_fixup_offset(dev_priv, file_priv, + &tex[0].pp_txoffset)) { + DRM_ERROR("Invalid texture offset for unit 0\n"); + return -EINVAL; + } + + BEGIN_RING(9); + OUT_RING(CP_PACKET0(RADEON_PP_TXFILTER_0, 5)); + OUT_RING(tex[0].pp_txfilter); + OUT_RING(tex[0].pp_txformat); + OUT_RING(tex[0].pp_txoffset); + OUT_RING(tex[0].pp_txcblend); + OUT_RING(tex[0].pp_txablend); + OUT_RING(tex[0].pp_tfactor); + OUT_RING(CP_PACKET0(RADEON_PP_BORDER_COLOR_0, 0)); + OUT_RING(tex[0].pp_border_color); + ADVANCE_RING(); + } + + if (dirty & RADEON_UPLOAD_TEX1) { + if (radeon_check_and_fixup_offset(dev_priv, file_priv, + &tex[1].pp_txoffset)) { + DRM_ERROR("Invalid texture offset for unit 1\n"); + return -EINVAL; + } + + BEGIN_RING(9); + OUT_RING(CP_PACKET0(RADEON_PP_TXFILTER_1, 5)); + OUT_RING(tex[1].pp_txfilter); + OUT_RING(tex[1].pp_txformat); + OUT_RING(tex[1].pp_txoffset); + OUT_RING(tex[1].pp_txcblend); + OUT_RING(tex[1].pp_txablend); + OUT_RING(tex[1].pp_tfactor); + OUT_RING(CP_PACKET0(RADEON_PP_BORDER_COLOR_1, 0)); + OUT_RING(tex[1].pp_border_color); + ADVANCE_RING(); + } + + if (dirty & RADEON_UPLOAD_TEX2) { + if (radeon_check_and_fixup_offset(dev_priv, file_priv, + &tex[2].pp_txoffset)) { + DRM_ERROR("Invalid texture offset for unit 2\n"); + return -EINVAL; + } + + BEGIN_RING(9); + OUT_RING(CP_PACKET0(RADEON_PP_TXFILTER_2, 5)); + OUT_RING(tex[2].pp_txfilter); + OUT_RING(tex[2].pp_txformat); + OUT_RING(tex[2].pp_txoffset); + OUT_RING(tex[2].pp_txcblend); + OUT_RING(tex[2].pp_txablend); + OUT_RING(tex[2].pp_tfactor); + OUT_RING(CP_PACKET0(RADEON_PP_BORDER_COLOR_2, 0)); + OUT_RING(tex[2].pp_border_color); + ADVANCE_RING(); + } + + return 0; +} + +/* Emit 1.2 state + */ +static int radeon_emit_state2(drm_radeon_private_t * dev_priv, + struct drm_file *file_priv, + drm_radeon_state_t * state) +{ + RING_LOCALS; + + if (state->dirty & RADEON_UPLOAD_ZBIAS) { + BEGIN_RING(3); + OUT_RING(CP_PACKET0(RADEON_SE_ZBIAS_FACTOR, 1)); + OUT_RING(state->context2.se_zbias_factor); + OUT_RING(state->context2.se_zbias_constant); + ADVANCE_RING(); + } + + return radeon_emit_state(dev_priv, file_priv, &state->context, + state->tex, state->dirty); +} + +/* New (1.3) state mechanism. 3 commands (packet, scalar, vector) in + * 1.3 cmdbuffers allow all previous state to be updated as well as + * the tcl scalar and vector areas. + */ +static struct { + int start; + int len; + const char *name; +} packet[RADEON_MAX_STATE_PACKETS] = { + {RADEON_PP_MISC, 7, "RADEON_PP_MISC"}, + {RADEON_PP_CNTL, 3, "RADEON_PP_CNTL"}, + {RADEON_RB3D_COLORPITCH, 1, "RADEON_RB3D_COLORPITCH"}, + {RADEON_RE_LINE_PATTERN, 2, "RADEON_RE_LINE_PATTERN"}, + {RADEON_SE_LINE_WIDTH, 1, "RADEON_SE_LINE_WIDTH"}, + {RADEON_PP_LUM_MATRIX, 1, "RADEON_PP_LUM_MATRIX"}, + {RADEON_PP_ROT_MATRIX_0, 2, "RADEON_PP_ROT_MATRIX_0"}, + {RADEON_RB3D_STENCILREFMASK, 3, "RADEON_RB3D_STENCILREFMASK"}, + {RADEON_SE_VPORT_XSCALE, 6, "RADEON_SE_VPORT_XSCALE"}, + {RADEON_SE_CNTL, 2, "RADEON_SE_CNTL"}, + {RADEON_SE_CNTL_STATUS, 1, "RADEON_SE_CNTL_STATUS"}, + {RADEON_RE_MISC, 1, "RADEON_RE_MISC"}, + {RADEON_PP_TXFILTER_0, 6, "RADEON_PP_TXFILTER_0"}, + {RADEON_PP_BORDER_COLOR_0, 1, "RADEON_PP_BORDER_COLOR_0"}, + {RADEON_PP_TXFILTER_1, 6, "RADEON_PP_TXFILTER_1"}, + {RADEON_PP_BORDER_COLOR_1, 1, "RADEON_PP_BORDER_COLOR_1"}, + {RADEON_PP_TXFILTER_2, 6, "RADEON_PP_TXFILTER_2"}, + {RADEON_PP_BORDER_COLOR_2, 1, "RADEON_PP_BORDER_COLOR_2"}, + {RADEON_SE_ZBIAS_FACTOR, 2, "RADEON_SE_ZBIAS_FACTOR"}, + {RADEON_SE_TCL_OUTPUT_VTX_FMT, 11, "RADEON_SE_TCL_OUTPUT_VTX_FMT"}, + {RADEON_SE_TCL_MATERIAL_EMMISSIVE_RED, 17, + "RADEON_SE_TCL_MATERIAL_EMMISSIVE_RED"}, + {R200_PP_TXCBLEND_0, 4, "R200_PP_TXCBLEND_0"}, + {R200_PP_TXCBLEND_1, 4, "R200_PP_TXCBLEND_1"}, + {R200_PP_TXCBLEND_2, 4, "R200_PP_TXCBLEND_2"}, + {R200_PP_TXCBLEND_3, 4, "R200_PP_TXCBLEND_3"}, + {R200_PP_TXCBLEND_4, 4, "R200_PP_TXCBLEND_4"}, + {R200_PP_TXCBLEND_5, 4, "R200_PP_TXCBLEND_5"}, + {R200_PP_TXCBLEND_6, 4, "R200_PP_TXCBLEND_6"}, + {R200_PP_TXCBLEND_7, 4, "R200_PP_TXCBLEND_7"}, + {R200_SE_TCL_LIGHT_MODEL_CTL_0, 6, "R200_SE_TCL_LIGHT_MODEL_CTL_0"}, + {R200_PP_TFACTOR_0, 6, "R200_PP_TFACTOR_0"}, + {R200_SE_VTX_FMT_0, 4, "R200_SE_VTX_FMT_0"}, + {R200_SE_VAP_CNTL, 1, "R200_SE_VAP_CNTL"}, + {R200_SE_TCL_MATRIX_SEL_0, 5, "R200_SE_TCL_MATRIX_SEL_0"}, + {R200_SE_TCL_TEX_PROC_CTL_2, 5, "R200_SE_TCL_TEX_PROC_CTL_2"}, + {R200_SE_TCL_UCP_VERT_BLEND_CTL, 1, "R200_SE_TCL_UCP_VERT_BLEND_CTL"}, + {R200_PP_TXFILTER_0, 6, "R200_PP_TXFILTER_0"}, + {R200_PP_TXFILTER_1, 6, "R200_PP_TXFILTER_1"}, + {R200_PP_TXFILTER_2, 6, "R200_PP_TXFILTER_2"}, + {R200_PP_TXFILTER_3, 6, "R200_PP_TXFILTER_3"}, + {R200_PP_TXFILTER_4, 6, "R200_PP_TXFILTER_4"}, + {R200_PP_TXFILTER_5, 6, "R200_PP_TXFILTER_5"}, + {R200_PP_TXOFFSET_0, 1, "R200_PP_TXOFFSET_0"}, + {R200_PP_TXOFFSET_1, 1, "R200_PP_TXOFFSET_1"}, + {R200_PP_TXOFFSET_2, 1, "R200_PP_TXOFFSET_2"}, + {R200_PP_TXOFFSET_3, 1, "R200_PP_TXOFFSET_3"}, + {R200_PP_TXOFFSET_4, 1, "R200_PP_TXOFFSET_4"}, + {R200_PP_TXOFFSET_5, 1, "R200_PP_TXOFFSET_5"}, + {R200_SE_VTE_CNTL, 1, "R200_SE_VTE_CNTL"}, + {R200_SE_TCL_OUTPUT_VTX_COMP_SEL, 1, + "R200_SE_TCL_OUTPUT_VTX_COMP_SEL"}, + {R200_PP_TAM_DEBUG3, 1, "R200_PP_TAM_DEBUG3"}, + {R200_PP_CNTL_X, 1, "R200_PP_CNTL_X"}, + {R200_RB3D_DEPTHXY_OFFSET, 1, "R200_RB3D_DEPTHXY_OFFSET"}, + {R200_RE_AUX_SCISSOR_CNTL, 1, "R200_RE_AUX_SCISSOR_CNTL"}, + {R200_RE_SCISSOR_TL_0, 2, "R200_RE_SCISSOR_TL_0"}, + {R200_RE_SCISSOR_TL_1, 2, "R200_RE_SCISSOR_TL_1"}, + {R200_RE_SCISSOR_TL_2, 2, "R200_RE_SCISSOR_TL_2"}, + {R200_SE_VAP_CNTL_STATUS, 1, "R200_SE_VAP_CNTL_STATUS"}, + {R200_SE_VTX_STATE_CNTL, 1, "R200_SE_VTX_STATE_CNTL"}, + {R200_RE_POINTSIZE, 1, "R200_RE_POINTSIZE"}, + {R200_SE_TCL_INPUT_VTX_VECTOR_ADDR_0, 4, + "R200_SE_TCL_INPUT_VTX_VECTOR_ADDR_0"}, + {R200_PP_CUBIC_FACES_0, 1, "R200_PP_CUBIC_FACES_0"}, /* 61 */ + {R200_PP_CUBIC_OFFSET_F1_0, 5, "R200_PP_CUBIC_OFFSET_F1_0"}, /* 62 */ + {R200_PP_CUBIC_FACES_1, 1, "R200_PP_CUBIC_FACES_1"}, + {R200_PP_CUBIC_OFFSET_F1_1, 5, "R200_PP_CUBIC_OFFSET_F1_1"}, + {R200_PP_CUBIC_FACES_2, 1, "R200_PP_CUBIC_FACES_2"}, + {R200_PP_CUBIC_OFFSET_F1_2, 5, "R200_PP_CUBIC_OFFSET_F1_2"}, + {R200_PP_CUBIC_FACES_3, 1, "R200_PP_CUBIC_FACES_3"}, + {R200_PP_CUBIC_OFFSET_F1_3, 5, "R200_PP_CUBIC_OFFSET_F1_3"}, + {R200_PP_CUBIC_FACES_4, 1, "R200_PP_CUBIC_FACES_4"}, + {R200_PP_CUBIC_OFFSET_F1_4, 5, "R200_PP_CUBIC_OFFSET_F1_4"}, + {R200_PP_CUBIC_FACES_5, 1, "R200_PP_CUBIC_FACES_5"}, + {R200_PP_CUBIC_OFFSET_F1_5, 5, "R200_PP_CUBIC_OFFSET_F1_5"}, + {RADEON_PP_TEX_SIZE_0, 2, "RADEON_PP_TEX_SIZE_0"}, + {RADEON_PP_TEX_SIZE_1, 2, "RADEON_PP_TEX_SIZE_1"}, + {RADEON_PP_TEX_SIZE_2, 2, "RADEON_PP_TEX_SIZE_2"}, + {R200_RB3D_BLENDCOLOR, 3, "R200_RB3D_BLENDCOLOR"}, + {R200_SE_TCL_POINT_SPRITE_CNTL, 1, "R200_SE_TCL_POINT_SPRITE_CNTL"}, + {RADEON_PP_CUBIC_FACES_0, 1, "RADEON_PP_CUBIC_FACES_0"}, + {RADEON_PP_CUBIC_OFFSET_T0_0, 5, "RADEON_PP_CUBIC_OFFSET_T0_0"}, + {RADEON_PP_CUBIC_FACES_1, 1, "RADEON_PP_CUBIC_FACES_1"}, + {RADEON_PP_CUBIC_OFFSET_T1_0, 5, "RADEON_PP_CUBIC_OFFSET_T1_0"}, + {RADEON_PP_CUBIC_FACES_2, 1, "RADEON_PP_CUBIC_FACES_2"}, + {RADEON_PP_CUBIC_OFFSET_T2_0, 5, "RADEON_PP_CUBIC_OFFSET_T2_0"}, + {R200_PP_TRI_PERF, 2, "R200_PP_TRI_PERF"}, + {R200_PP_AFS_0, 32, "R200_PP_AFS_0"}, /* 85 */ + {R200_PP_AFS_1, 32, "R200_PP_AFS_1"}, + {R200_PP_TFACTOR_0, 8, "R200_ATF_TFACTOR"}, + {R200_PP_TXFILTER_0, 8, "R200_PP_TXCTLALL_0"}, + {R200_PP_TXFILTER_1, 8, "R200_PP_TXCTLALL_1"}, + {R200_PP_TXFILTER_2, 8, "R200_PP_TXCTLALL_2"}, + {R200_PP_TXFILTER_3, 8, "R200_PP_TXCTLALL_3"}, + {R200_PP_TXFILTER_4, 8, "R200_PP_TXCTLALL_4"}, + {R200_PP_TXFILTER_5, 8, "R200_PP_TXCTLALL_5"}, + {R200_VAP_PVS_CNTL_1, 2, "R200_VAP_PVS_CNTL"}, +}; + +/* ================================================================ + * Performance monitoring functions + */ + +static void radeon_clear_box(drm_radeon_private_t * dev_priv, + struct drm_radeon_master_private *master_priv, + int x, int y, int w, int h, int r, int g, int b) +{ + u32 color; + RING_LOCALS; + + x += master_priv->sarea_priv->boxes[0].x1; + y += master_priv->sarea_priv->boxes[0].y1; + + switch (dev_priv->color_fmt) { + case RADEON_COLOR_FORMAT_RGB565: + color = (((r & 0xf8) << 8) | + ((g & 0xfc) << 3) | ((b & 0xf8) >> 3)); + break; + case RADEON_COLOR_FORMAT_ARGB8888: + default: + color = (((0xff) << 24) | (r << 16) | (g << 8) | b); + break; + } + + BEGIN_RING(4); + RADEON_WAIT_UNTIL_3D_IDLE(); + OUT_RING(CP_PACKET0(RADEON_DP_WRITE_MASK, 0)); + OUT_RING(0xffffffff); + ADVANCE_RING(); + + BEGIN_RING(6); + + OUT_RING(CP_PACKET3(RADEON_CNTL_PAINT_MULTI, 4)); + OUT_RING(RADEON_GMC_DST_PITCH_OFFSET_CNTL | + RADEON_GMC_BRUSH_SOLID_COLOR | + (dev_priv->color_fmt << 8) | + RADEON_GMC_SRC_DATATYPE_COLOR | + RADEON_ROP3_P | RADEON_GMC_CLR_CMP_CNTL_DIS); + + if (master_priv->sarea_priv->pfCurrentPage == 1) { + OUT_RING(dev_priv->front_pitch_offset); + } else { + OUT_RING(dev_priv->back_pitch_offset); + } + + OUT_RING(color); + + OUT_RING((x << 16) | y); + OUT_RING((w << 16) | h); + + ADVANCE_RING(); +} + +static void radeon_cp_performance_boxes(drm_radeon_private_t *dev_priv, struct drm_radeon_master_private *master_priv) +{ + /* Collapse various things into a wait flag -- trying to + * guess if userspase slept -- better just to have them tell us. + */ + if (dev_priv->stats.last_frame_reads > 1 || + dev_priv->stats.last_clear_reads > dev_priv->stats.clears) { + dev_priv->stats.boxes |= RADEON_BOX_WAIT_IDLE; + } + + if (dev_priv->stats.freelist_loops) { + dev_priv->stats.boxes |= RADEON_BOX_WAIT_IDLE; + } + + /* Purple box for page flipping + */ + if (dev_priv->stats.boxes & RADEON_BOX_FLIP) + radeon_clear_box(dev_priv, master_priv, 4, 4, 8, 8, 255, 0, 255); + + /* Red box if we have to wait for idle at any point + */ + if (dev_priv->stats.boxes & RADEON_BOX_WAIT_IDLE) + radeon_clear_box(dev_priv, master_priv, 16, 4, 8, 8, 255, 0, 0); + + /* Blue box: lost context? + */ + + /* Yellow box for texture swaps + */ + if (dev_priv->stats.boxes & RADEON_BOX_TEXTURE_LOAD) + radeon_clear_box(dev_priv, master_priv, 40, 4, 8, 8, 255, 255, 0); + + /* Green box if hardware never idles (as far as we can tell) + */ + if (!(dev_priv->stats.boxes & RADEON_BOX_DMA_IDLE)) + radeon_clear_box(dev_priv, master_priv, 64, 4, 8, 8, 0, 255, 0); + + /* Draw bars indicating number of buffers allocated + * (not a great measure, easily confused) + */ + if (dev_priv->stats.requested_bufs) { + if (dev_priv->stats.requested_bufs > 100) + dev_priv->stats.requested_bufs = 100; + + radeon_clear_box(dev_priv, master_priv, 4, 16, + dev_priv->stats.requested_bufs, 4, + 196, 128, 128); + } + + memset(&dev_priv->stats, 0, sizeof(dev_priv->stats)); + +} + +/* ================================================================ + * CP command dispatch functions + */ + +static void radeon_cp_dispatch_clear(struct drm_device * dev, + struct drm_master *master, + drm_radeon_clear_t * clear, + drm_radeon_clear_rect_t * depth_boxes) +{ + drm_radeon_private_t *dev_priv = dev->dev_private; + struct drm_radeon_master_private *master_priv = master->driver_priv; + drm_radeon_sarea_t *sarea_priv = master_priv->sarea_priv; + drm_radeon_depth_clear_t *depth_clear = &dev_priv->depth_clear; + int nbox = sarea_priv->nbox; + struct drm_clip_rect *pbox = sarea_priv->boxes; + unsigned int flags = clear->flags; + u32 rb3d_cntl = 0, rb3d_stencilrefmask = 0; + int i; + RING_LOCALS; + DRM_DEBUG("flags = 0x%x\n", flags); + + dev_priv->stats.clears++; + + if (sarea_priv->pfCurrentPage == 1) { + unsigned int tmp = flags; + + flags &= ~(RADEON_FRONT | RADEON_BACK); + if (tmp & RADEON_FRONT) + flags |= RADEON_BACK; + if (tmp & RADEON_BACK) + flags |= RADEON_FRONT; + } + if (flags & (RADEON_DEPTH|RADEON_STENCIL)) { + if (!dev_priv->have_z_offset) { + DRM_ERROR("radeon: illegal depth clear request. Buggy mesa detected - please update.\n"); + flags &= ~(RADEON_DEPTH | RADEON_STENCIL); + } + } + + if (flags & (RADEON_FRONT | RADEON_BACK)) { + + BEGIN_RING(4); + + /* Ensure the 3D stream is idle before doing a + * 2D fill to clear the front or back buffer. + */ + RADEON_WAIT_UNTIL_3D_IDLE(); + + OUT_RING(CP_PACKET0(RADEON_DP_WRITE_MASK, 0)); + OUT_RING(clear->color_mask); + + ADVANCE_RING(); + + /* Make sure we restore the 3D state next time. + */ + sarea_priv->ctx_owner = 0; + + for (i = 0; i < nbox; i++) { + int x = pbox[i].x1; + int y = pbox[i].y1; + int w = pbox[i].x2 - x; + int h = pbox[i].y2 - y; + + DRM_DEBUG("%d,%d-%d,%d flags 0x%x\n", + x, y, w, h, flags); + + if (flags & RADEON_FRONT) { + BEGIN_RING(6); + + OUT_RING(CP_PACKET3 + (RADEON_CNTL_PAINT_MULTI, 4)); + OUT_RING(RADEON_GMC_DST_PITCH_OFFSET_CNTL | + RADEON_GMC_BRUSH_SOLID_COLOR | + (dev_priv-> + color_fmt << 8) | + RADEON_GMC_SRC_DATATYPE_COLOR | + RADEON_ROP3_P | + RADEON_GMC_CLR_CMP_CNTL_DIS); + + OUT_RING(dev_priv->front_pitch_offset); + OUT_RING(clear->clear_color); + + OUT_RING((x << 16) | y); + OUT_RING((w << 16) | h); + + ADVANCE_RING(); + } + + if (flags & RADEON_BACK) { + BEGIN_RING(6); + + OUT_RING(CP_PACKET3 + (RADEON_CNTL_PAINT_MULTI, 4)); + OUT_RING(RADEON_GMC_DST_PITCH_OFFSET_CNTL | + RADEON_GMC_BRUSH_SOLID_COLOR | + (dev_priv-> + color_fmt << 8) | + RADEON_GMC_SRC_DATATYPE_COLOR | + RADEON_ROP3_P | + RADEON_GMC_CLR_CMP_CNTL_DIS); + + OUT_RING(dev_priv->back_pitch_offset); + OUT_RING(clear->clear_color); + + OUT_RING((x << 16) | y); + OUT_RING((w << 16) | h); + + ADVANCE_RING(); + } + } + } + + /* hyper z clear */ + /* no docs available, based on reverse engineering by Stephane Marchesin */ + if ((flags & (RADEON_DEPTH | RADEON_STENCIL)) + && (flags & RADEON_CLEAR_FASTZ)) { + + int i; + int depthpixperline = + dev_priv->depth_fmt == + RADEON_DEPTH_FORMAT_16BIT_INT_Z ? (dev_priv->depth_pitch / + 2) : (dev_priv-> + depth_pitch / 4); + + u32 clearmask; + + u32 tempRB3D_DEPTHCLEARVALUE = clear->clear_depth | + ((clear->depth_mask & 0xff) << 24); + + /* Make sure we restore the 3D state next time. + * we haven't touched any "normal" state - still need this? + */ + sarea_priv->ctx_owner = 0; + + if ((dev_priv->flags & RADEON_HAS_HIERZ) + && (flags & RADEON_USE_HIERZ)) { + /* FIXME : reverse engineer that for Rx00 cards */ + /* FIXME : the mask supposedly contains low-res z values. So can't set + just to the max (0xff? or actually 0x3fff?), need to take z clear + value into account? */ + /* pattern seems to work for r100, though get slight + rendering errors with glxgears. If hierz is not enabled for r100, + only 4 bits which indicate clear (15,16,31,32, all zero) matter, the + other ones are ignored, and the same clear mask can be used. That's + very different behaviour than R200 which needs different clear mask + and different number of tiles to clear if hierz is enabled or not !?! + */ + clearmask = (0xff << 22) | (0xff << 6) | 0x003f003f; + } else { + /* clear mask : chooses the clearing pattern. + rv250: could be used to clear only parts of macrotiles + (but that would get really complicated...)? + bit 0 and 1 (either or both of them ?!?!) are used to + not clear tile (or maybe one of the bits indicates if the tile is + compressed or not), bit 2 and 3 to not clear tile 1,...,. + Pattern is as follows: + | 0,1 | 4,5 | 8,9 |12,13|16,17|20,21|24,25|28,29| + bits ------------------------------------------------- + | 2,3 | 6,7 |10,11|14,15|18,19|22,23|26,27|30,31| + rv100: clearmask covers 2x8 4x1 tiles, but one clear still + covers 256 pixels ?!? + */ + clearmask = 0x0; + } + + BEGIN_RING(8); + RADEON_WAIT_UNTIL_2D_IDLE(); + OUT_RING_REG(RADEON_RB3D_DEPTHCLEARVALUE, + tempRB3D_DEPTHCLEARVALUE); + /* what offset is this exactly ? */ + OUT_RING_REG(RADEON_RB3D_ZMASKOFFSET, 0); + /* need ctlstat, otherwise get some strange black flickering */ + OUT_RING_REG(RADEON_RB3D_ZCACHE_CTLSTAT, + RADEON_RB3D_ZC_FLUSH_ALL); + ADVANCE_RING(); + + for (i = 0; i < nbox; i++) { + int tileoffset, nrtilesx, nrtilesy, j; + /* it looks like r200 needs rv-style clears, at least if hierz is not enabled? */ + if ((dev_priv->flags & RADEON_HAS_HIERZ) + && !(dev_priv->microcode_version == UCODE_R200)) { + /* FIXME : figure this out for r200 (when hierz is enabled). Or + maybe r200 actually doesn't need to put the low-res z value into + the tile cache like r100, but just needs to clear the hi-level z-buffer? + Works for R100, both with hierz and without. + R100 seems to operate on 2x1 8x8 tiles, but... + odd: offset/nrtiles need to be 64 pix (4 block) aligned? Potentially + problematic with resolutions which are not 64 pix aligned? */ + tileoffset = + ((pbox[i].y1 >> 3) * depthpixperline + + pbox[i].x1) >> 6; + nrtilesx = + ((pbox[i].x2 & ~63) - + (pbox[i].x1 & ~63)) >> 4; + nrtilesy = + (pbox[i].y2 >> 3) - (pbox[i].y1 >> 3); + for (j = 0; j <= nrtilesy; j++) { + BEGIN_RING(4); + OUT_RING(CP_PACKET3 + (RADEON_3D_CLEAR_ZMASK, 2)); + /* first tile */ + OUT_RING(tileoffset * 8); + /* the number of tiles to clear */ + OUT_RING(nrtilesx + 4); + /* clear mask : chooses the clearing pattern. */ + OUT_RING(clearmask); + ADVANCE_RING(); + tileoffset += depthpixperline >> 6; + } + } else if (dev_priv->microcode_version == UCODE_R200) { + /* works for rv250. */ + /* find first macro tile (8x2 4x4 z-pixels on rv250) */ + tileoffset = + ((pbox[i].y1 >> 3) * depthpixperline + + pbox[i].x1) >> 5; + nrtilesx = + (pbox[i].x2 >> 5) - (pbox[i].x1 >> 5); + nrtilesy = + (pbox[i].y2 >> 3) - (pbox[i].y1 >> 3); + for (j = 0; j <= nrtilesy; j++) { + BEGIN_RING(4); + OUT_RING(CP_PACKET3 + (RADEON_3D_CLEAR_ZMASK, 2)); + /* first tile */ + /* judging by the first tile offset needed, could possibly + directly address/clear 4x4 tiles instead of 8x2 * 4x4 + macro tiles, though would still need clear mask for + right/bottom if truly 4x4 granularity is desired ? */ + OUT_RING(tileoffset * 16); + /* the number of tiles to clear */ + OUT_RING(nrtilesx + 1); + /* clear mask : chooses the clearing pattern. */ + OUT_RING(clearmask); + ADVANCE_RING(); + tileoffset += depthpixperline >> 5; + } + } else { /* rv 100 */ + /* rv100 might not need 64 pix alignment, who knows */ + /* offsets are, hmm, weird */ + tileoffset = + ((pbox[i].y1 >> 4) * depthpixperline + + pbox[i].x1) >> 6; + nrtilesx = + ((pbox[i].x2 & ~63) - + (pbox[i].x1 & ~63)) >> 4; + nrtilesy = + (pbox[i].y2 >> 4) - (pbox[i].y1 >> 4); + for (j = 0; j <= nrtilesy; j++) { + BEGIN_RING(4); + OUT_RING(CP_PACKET3 + (RADEON_3D_CLEAR_ZMASK, 2)); + OUT_RING(tileoffset * 128); + /* the number of tiles to clear */ + OUT_RING(nrtilesx + 4); + /* clear mask : chooses the clearing pattern. */ + OUT_RING(clearmask); + ADVANCE_RING(); + tileoffset += depthpixperline >> 6; + } + } + } + + /* TODO don't always clear all hi-level z tiles */ + if ((dev_priv->flags & RADEON_HAS_HIERZ) + && (dev_priv->microcode_version == UCODE_R200) + && (flags & RADEON_USE_HIERZ)) + /* r100 and cards without hierarchical z-buffer have no high-level z-buffer */ + /* FIXME : the mask supposedly contains low-res z values. So can't set + just to the max (0xff? or actually 0x3fff?), need to take z clear + value into account? */ + { + BEGIN_RING(4); + OUT_RING(CP_PACKET3(RADEON_3D_CLEAR_HIZ, 2)); + OUT_RING(0x0); /* First tile */ + OUT_RING(0x3cc0); + OUT_RING((0xff << 22) | (0xff << 6) | 0x003f003f); + ADVANCE_RING(); + } + } + + /* We have to clear the depth and/or stencil buffers by + * rendering a quad into just those buffers. Thus, we have to + * make sure the 3D engine is configured correctly. + */ + else if ((dev_priv->microcode_version == UCODE_R200) && + (flags & (RADEON_DEPTH | RADEON_STENCIL))) { + + int tempPP_CNTL; + int tempRE_CNTL; + int tempRB3D_CNTL; + int tempRB3D_ZSTENCILCNTL; + int tempRB3D_STENCILREFMASK; + int tempRB3D_PLANEMASK; + int tempSE_CNTL; + int tempSE_VTE_CNTL; + int tempSE_VTX_FMT_0; + int tempSE_VTX_FMT_1; + int tempSE_VAP_CNTL; + int tempRE_AUX_SCISSOR_CNTL; + + tempPP_CNTL = 0; + tempRE_CNTL = 0; + + tempRB3D_CNTL = depth_clear->rb3d_cntl; + + tempRB3D_ZSTENCILCNTL = depth_clear->rb3d_zstencilcntl; + tempRB3D_STENCILREFMASK = 0x0; + + tempSE_CNTL = depth_clear->se_cntl; + + /* Disable TCL */ + + tempSE_VAP_CNTL = ( /* SE_VAP_CNTL__FORCE_W_TO_ONE_MASK | */ + (0x9 << + SE_VAP_CNTL__VF_MAX_VTX_NUM__SHIFT)); + + tempRB3D_PLANEMASK = 0x0; + + tempRE_AUX_SCISSOR_CNTL = 0x0; + + tempSE_VTE_CNTL = + SE_VTE_CNTL__VTX_XY_FMT_MASK | SE_VTE_CNTL__VTX_Z_FMT_MASK; + + /* Vertex format (X, Y, Z, W) */ + tempSE_VTX_FMT_0 = + SE_VTX_FMT_0__VTX_Z0_PRESENT_MASK | + SE_VTX_FMT_0__VTX_W0_PRESENT_MASK; + tempSE_VTX_FMT_1 = 0x0; + + /* + * Depth buffer specific enables + */ + if (flags & RADEON_DEPTH) { + /* Enable depth buffer */ + tempRB3D_CNTL |= RADEON_Z_ENABLE; + } else { + /* Disable depth buffer */ + tempRB3D_CNTL &= ~RADEON_Z_ENABLE; + } + + /* + * Stencil buffer specific enables + */ + if (flags & RADEON_STENCIL) { + tempRB3D_CNTL |= RADEON_STENCIL_ENABLE; + tempRB3D_STENCILREFMASK = clear->depth_mask; + } else { + tempRB3D_CNTL &= ~RADEON_STENCIL_ENABLE; + tempRB3D_STENCILREFMASK = 0x00000000; + } + + if (flags & RADEON_USE_COMP_ZBUF) { + tempRB3D_ZSTENCILCNTL |= RADEON_Z_COMPRESSION_ENABLE | + RADEON_Z_DECOMPRESSION_ENABLE; + } + if (flags & RADEON_USE_HIERZ) { + tempRB3D_ZSTENCILCNTL |= RADEON_Z_HIERARCHY_ENABLE; + } + + BEGIN_RING(26); + RADEON_WAIT_UNTIL_2D_IDLE(); + + OUT_RING_REG(RADEON_PP_CNTL, tempPP_CNTL); + OUT_RING_REG(R200_RE_CNTL, tempRE_CNTL); + OUT_RING_REG(RADEON_RB3D_CNTL, tempRB3D_CNTL); + OUT_RING_REG(RADEON_RB3D_ZSTENCILCNTL, tempRB3D_ZSTENCILCNTL); + OUT_RING_REG(RADEON_RB3D_STENCILREFMASK, + tempRB3D_STENCILREFMASK); + OUT_RING_REG(RADEON_RB3D_PLANEMASK, tempRB3D_PLANEMASK); + OUT_RING_REG(RADEON_SE_CNTL, tempSE_CNTL); + OUT_RING_REG(R200_SE_VTE_CNTL, tempSE_VTE_CNTL); + OUT_RING_REG(R200_SE_VTX_FMT_0, tempSE_VTX_FMT_0); + OUT_RING_REG(R200_SE_VTX_FMT_1, tempSE_VTX_FMT_1); + OUT_RING_REG(R200_SE_VAP_CNTL, tempSE_VAP_CNTL); + OUT_RING_REG(R200_RE_AUX_SCISSOR_CNTL, tempRE_AUX_SCISSOR_CNTL); + ADVANCE_RING(); + + /* Make sure we restore the 3D state next time. + */ + sarea_priv->ctx_owner = 0; + + for (i = 0; i < nbox; i++) { + + /* Funny that this should be required -- + * sets top-left? + */ + radeon_emit_clip_rect(dev_priv, &sarea_priv->boxes[i]); + + BEGIN_RING(14); + OUT_RING(CP_PACKET3(R200_3D_DRAW_IMMD_2, 12)); + OUT_RING((RADEON_PRIM_TYPE_RECT_LIST | + RADEON_PRIM_WALK_RING | + (3 << RADEON_NUM_VERTICES_SHIFT))); + OUT_RING(depth_boxes[i].ui[CLEAR_X1]); + OUT_RING(depth_boxes[i].ui[CLEAR_Y1]); + OUT_RING(depth_boxes[i].ui[CLEAR_DEPTH]); + OUT_RING(0x3f800000); + OUT_RING(depth_boxes[i].ui[CLEAR_X1]); + OUT_RING(depth_boxes[i].ui[CLEAR_Y2]); + OUT_RING(depth_boxes[i].ui[CLEAR_DEPTH]); + OUT_RING(0x3f800000); + OUT_RING(depth_boxes[i].ui[CLEAR_X2]); + OUT_RING(depth_boxes[i].ui[CLEAR_Y2]); + OUT_RING(depth_boxes[i].ui[CLEAR_DEPTH]); + OUT_RING(0x3f800000); + ADVANCE_RING(); + } + } else if ((flags & (RADEON_DEPTH | RADEON_STENCIL))) { + + int tempRB3D_ZSTENCILCNTL = depth_clear->rb3d_zstencilcntl; + + rb3d_cntl = depth_clear->rb3d_cntl; + + if (flags & RADEON_DEPTH) { + rb3d_cntl |= RADEON_Z_ENABLE; + } else { + rb3d_cntl &= ~RADEON_Z_ENABLE; + } + + if (flags & RADEON_STENCIL) { + rb3d_cntl |= RADEON_STENCIL_ENABLE; + rb3d_stencilrefmask = clear->depth_mask; /* misnamed field */ + } else { + rb3d_cntl &= ~RADEON_STENCIL_ENABLE; + rb3d_stencilrefmask = 0x00000000; + } + + if (flags & RADEON_USE_COMP_ZBUF) { + tempRB3D_ZSTENCILCNTL |= RADEON_Z_COMPRESSION_ENABLE | + RADEON_Z_DECOMPRESSION_ENABLE; + } + if (flags & RADEON_USE_HIERZ) { + tempRB3D_ZSTENCILCNTL |= RADEON_Z_HIERARCHY_ENABLE; + } + + BEGIN_RING(13); + RADEON_WAIT_UNTIL_2D_IDLE(); + + OUT_RING(CP_PACKET0(RADEON_PP_CNTL, 1)); + OUT_RING(0x00000000); + OUT_RING(rb3d_cntl); + + OUT_RING_REG(RADEON_RB3D_ZSTENCILCNTL, tempRB3D_ZSTENCILCNTL); + OUT_RING_REG(RADEON_RB3D_STENCILREFMASK, rb3d_stencilrefmask); + OUT_RING_REG(RADEON_RB3D_PLANEMASK, 0x00000000); + OUT_RING_REG(RADEON_SE_CNTL, depth_clear->se_cntl); + ADVANCE_RING(); + + /* Make sure we restore the 3D state next time. + */ + sarea_priv->ctx_owner = 0; + + for (i = 0; i < nbox; i++) { + + /* Funny that this should be required -- + * sets top-left? + */ + radeon_emit_clip_rect(dev_priv, &sarea_priv->boxes[i]); + + BEGIN_RING(15); + + OUT_RING(CP_PACKET3(RADEON_3D_DRAW_IMMD, 13)); + OUT_RING(RADEON_VTX_Z_PRESENT | + RADEON_VTX_PKCOLOR_PRESENT); + OUT_RING((RADEON_PRIM_TYPE_RECT_LIST | + RADEON_PRIM_WALK_RING | + RADEON_MAOS_ENABLE | + RADEON_VTX_FMT_RADEON_MODE | + (3 << RADEON_NUM_VERTICES_SHIFT))); + + OUT_RING(depth_boxes[i].ui[CLEAR_X1]); + OUT_RING(depth_boxes[i].ui[CLEAR_Y1]); + OUT_RING(depth_boxes[i].ui[CLEAR_DEPTH]); + OUT_RING(0x0); + + OUT_RING(depth_boxes[i].ui[CLEAR_X1]); + OUT_RING(depth_boxes[i].ui[CLEAR_Y2]); + OUT_RING(depth_boxes[i].ui[CLEAR_DEPTH]); + OUT_RING(0x0); + + OUT_RING(depth_boxes[i].ui[CLEAR_X2]); + OUT_RING(depth_boxes[i].ui[CLEAR_Y2]); + OUT_RING(depth_boxes[i].ui[CLEAR_DEPTH]); + OUT_RING(0x0); + + ADVANCE_RING(); + } + } + + /* Increment the clear counter. The client-side 3D driver must + * wait on this value before performing the clear ioctl. We + * need this because the card's so damned fast... + */ + sarea_priv->last_clear++; + + BEGIN_RING(4); + + RADEON_CLEAR_AGE(sarea_priv->last_clear); + RADEON_WAIT_UNTIL_IDLE(); + + ADVANCE_RING(); +} + +static void radeon_cp_dispatch_swap(struct drm_device *dev, struct drm_master *master) +{ + drm_radeon_private_t *dev_priv = dev->dev_private; + struct drm_radeon_master_private *master_priv = master->driver_priv; + drm_radeon_sarea_t *sarea_priv = master_priv->sarea_priv; + int nbox = sarea_priv->nbox; + struct drm_clip_rect *pbox = sarea_priv->boxes; + int i; + RING_LOCALS; + DRM_DEBUG("\n"); + + /* Do some trivial performance monitoring... + */ + if (dev_priv->do_boxes) + radeon_cp_performance_boxes(dev_priv, master_priv); + + /* Wait for the 3D stream to idle before dispatching the bitblt. + * This will prevent data corruption between the two streams. + */ + BEGIN_RING(2); + + RADEON_WAIT_UNTIL_3D_IDLE(); + + ADVANCE_RING(); + + for (i = 0; i < nbox; i++) { + int x = pbox[i].x1; + int y = pbox[i].y1; + int w = pbox[i].x2 - x; + int h = pbox[i].y2 - y; + + DRM_DEBUG("%d,%d-%d,%d\n", x, y, w, h); + + BEGIN_RING(9); + + OUT_RING(CP_PACKET0(RADEON_DP_GUI_MASTER_CNTL, 0)); + OUT_RING(RADEON_GMC_SRC_PITCH_OFFSET_CNTL | + RADEON_GMC_DST_PITCH_OFFSET_CNTL | + RADEON_GMC_BRUSH_NONE | + (dev_priv->color_fmt << 8) | + RADEON_GMC_SRC_DATATYPE_COLOR | + RADEON_ROP3_S | + RADEON_DP_SRC_SOURCE_MEMORY | + RADEON_GMC_CLR_CMP_CNTL_DIS | RADEON_GMC_WR_MSK_DIS); + + /* Make this work even if front & back are flipped: + */ + OUT_RING(CP_PACKET0(RADEON_SRC_PITCH_OFFSET, 1)); + if (sarea_priv->pfCurrentPage == 0) { + OUT_RING(dev_priv->back_pitch_offset); + OUT_RING(dev_priv->front_pitch_offset); + } else { + OUT_RING(dev_priv->front_pitch_offset); + OUT_RING(dev_priv->back_pitch_offset); + } + + OUT_RING(CP_PACKET0(RADEON_SRC_X_Y, 2)); + OUT_RING((x << 16) | y); + OUT_RING((x << 16) | y); + OUT_RING((w << 16) | h); + + ADVANCE_RING(); + } + + /* Increment the frame counter. The client-side 3D driver must + * throttle the framerate by waiting for this value before + * performing the swapbuffer ioctl. + */ + sarea_priv->last_frame++; + + BEGIN_RING(4); + + RADEON_FRAME_AGE(sarea_priv->last_frame); + RADEON_WAIT_UNTIL_2D_IDLE(); + + ADVANCE_RING(); +} + +void radeon_cp_dispatch_flip(struct drm_device *dev, struct drm_master *master) +{ + drm_radeon_private_t *dev_priv = dev->dev_private; + struct drm_radeon_master_private *master_priv = master->driver_priv; + struct drm_sarea *sarea = (struct drm_sarea *)master_priv->sarea->handle; + int offset = (master_priv->sarea_priv->pfCurrentPage == 1) + ? dev_priv->front_offset : dev_priv->back_offset; + RING_LOCALS; + DRM_DEBUG("pfCurrentPage=%d\n", + master_priv->sarea_priv->pfCurrentPage); + + /* Do some trivial performance monitoring... + */ + if (dev_priv->do_boxes) { + dev_priv->stats.boxes |= RADEON_BOX_FLIP; + radeon_cp_performance_boxes(dev_priv, master_priv); + } + + /* Update the frame offsets for both CRTCs + */ + BEGIN_RING(6); + + RADEON_WAIT_UNTIL_3D_IDLE(); + OUT_RING_REG(RADEON_CRTC_OFFSET, + ((sarea->frame.y * dev_priv->front_pitch + + sarea->frame.x * (dev_priv->color_fmt - 2)) & ~7) + + offset); + OUT_RING_REG(RADEON_CRTC2_OFFSET, master_priv->sarea_priv->crtc2_base + + offset); + + ADVANCE_RING(); + + /* Increment the frame counter. The client-side 3D driver must + * throttle the framerate by waiting for this value before + * performing the swapbuffer ioctl. + */ + master_priv->sarea_priv->last_frame++; + master_priv->sarea_priv->pfCurrentPage = + 1 - master_priv->sarea_priv->pfCurrentPage; + + BEGIN_RING(2); + + RADEON_FRAME_AGE(master_priv->sarea_priv->last_frame); + + ADVANCE_RING(); +} + +static int bad_prim_vertex_nr(int primitive, int nr) +{ + switch (primitive & RADEON_PRIM_TYPE_MASK) { + case RADEON_PRIM_TYPE_NONE: + case RADEON_PRIM_TYPE_POINT: + return nr < 1; + case RADEON_PRIM_TYPE_LINE: + return (nr & 1) || nr == 0; + case RADEON_PRIM_TYPE_LINE_STRIP: + return nr < 2; + case RADEON_PRIM_TYPE_TRI_LIST: + case RADEON_PRIM_TYPE_3VRT_POINT_LIST: + case RADEON_PRIM_TYPE_3VRT_LINE_LIST: + case RADEON_PRIM_TYPE_RECT_LIST: + return nr % 3 || nr == 0; + case RADEON_PRIM_TYPE_TRI_FAN: + case RADEON_PRIM_TYPE_TRI_STRIP: + return nr < 3; + default: + return 1; + } +} + +typedef struct { + unsigned int start; + unsigned int finish; + unsigned int prim; + unsigned int numverts; + unsigned int offset; + unsigned int vc_format; +} drm_radeon_tcl_prim_t; + +static void radeon_cp_dispatch_vertex(struct drm_device * dev, + struct drm_file *file_priv, + struct drm_buf * buf, + drm_radeon_tcl_prim_t * prim) +{ + drm_radeon_private_t *dev_priv = dev->dev_private; + struct drm_radeon_master_private *master_priv = file_priv->masterp->driver_priv; + drm_radeon_sarea_t *sarea_priv = master_priv->sarea_priv; + int offset = dev_priv->gart_buffers_offset + buf->offset + prim->start; + int numverts = (int)prim->numverts; + int nbox = sarea_priv->nbox; + int i = 0; + RING_LOCALS; + + DRM_DEBUG("hwprim 0x%x vfmt 0x%x %d..%d %d verts\n", + prim->prim, + prim->vc_format, prim->start, prim->finish, prim->numverts); + + if (bad_prim_vertex_nr(prim->prim, prim->numverts)) { + DRM_ERROR("bad prim %x numverts %d\n", + prim->prim, prim->numverts); + return; + } + + do { + /* Emit the next cliprect */ + if (i < nbox) { + radeon_emit_clip_rect(dev_priv, &sarea_priv->boxes[i]); + } + + /* Emit the vertex buffer rendering commands */ + BEGIN_RING(5); + + OUT_RING(CP_PACKET3(RADEON_3D_RNDR_GEN_INDX_PRIM, 3)); + OUT_RING(offset); + OUT_RING(numverts); + OUT_RING(prim->vc_format); + OUT_RING(prim->prim | RADEON_PRIM_WALK_LIST | + RADEON_COLOR_ORDER_RGBA | + RADEON_VTX_FMT_RADEON_MODE | + (numverts << RADEON_NUM_VERTICES_SHIFT)); + + ADVANCE_RING(); + + i++; + } while (i < nbox); +} + +void radeon_cp_discard_buffer(struct drm_device *dev, struct drm_master *master, struct drm_buf *buf) +{ + drm_radeon_private_t *dev_priv = dev->dev_private; + struct drm_radeon_master_private *master_priv = master->driver_priv; + drm_radeon_buf_priv_t *buf_priv = buf->dev_private; + RING_LOCALS; + + buf_priv->age = ++master_priv->sarea_priv->last_dispatch; + + /* Emit the vertex buffer age */ + if ((dev_priv->flags & RADEON_FAMILY_MASK) >= CHIP_R600) { + BEGIN_RING(3); + R600_DISPATCH_AGE(buf_priv->age); + ADVANCE_RING(); + } else { + BEGIN_RING(2); + RADEON_DISPATCH_AGE(buf_priv->age); + ADVANCE_RING(); + } + + buf->pending = 1; + buf->used = 0; +} + +static void radeon_cp_dispatch_indirect(struct drm_device * dev, + struct drm_buf * buf, int start, int end) +{ + drm_radeon_private_t *dev_priv = dev->dev_private; + RING_LOCALS; + DRM_DEBUG("buf=%d s=0x%x e=0x%x\n", buf->idx, start, end); + + if (start != end) { + int offset = (dev_priv->gart_buffers_offset + + buf->offset + start); + int dwords = (end - start + 3) / sizeof(u32); + + /* Indirect buffer data must be an even number of + * dwords, so if we've been given an odd number we must + * pad the data with a Type-2 CP packet. + */ + if (dwords & 1) { + u32 *data = (u32 *) + ((char *)dev->agp_buffer_map->handle + + buf->offset + start); + data[dwords++] = RADEON_CP_PACKET2; + } + + /* Fire off the indirect buffer */ + BEGIN_RING(3); + + OUT_RING(CP_PACKET0(RADEON_CP_IB_BASE, 1)); + OUT_RING(offset); + OUT_RING(dwords); + + ADVANCE_RING(); + } +} + +static void radeon_cp_dispatch_indices(struct drm_device *dev, + struct drm_master *master, + struct drm_buf * elt_buf, + drm_radeon_tcl_prim_t * prim) +{ + drm_radeon_private_t *dev_priv = dev->dev_private; + struct drm_radeon_master_private *master_priv = master->driver_priv; + drm_radeon_sarea_t *sarea_priv = master_priv->sarea_priv; + int offset = dev_priv->gart_buffers_offset + prim->offset; + u32 *data; + int dwords; + int i = 0; + int start = prim->start + RADEON_INDEX_PRIM_OFFSET; + int count = (prim->finish - start) / sizeof(u16); + int nbox = sarea_priv->nbox; + + DRM_DEBUG("hwprim 0x%x vfmt 0x%x %d..%d offset: %x nr %d\n", + prim->prim, + prim->vc_format, + prim->start, prim->finish, prim->offset, prim->numverts); + + if (bad_prim_vertex_nr(prim->prim, count)) { + DRM_ERROR("bad prim %x count %d\n", prim->prim, count); + return; + } + + if (start >= prim->finish || (prim->start & 0x7)) { + DRM_ERROR("buffer prim %d\n", prim->prim); + return; + } + + dwords = (prim->finish - prim->start + 3) / sizeof(u32); + + data = (u32 *) ((char *)dev->agp_buffer_map->handle + + elt_buf->offset + prim->start); + + data[0] = CP_PACKET3(RADEON_3D_RNDR_GEN_INDX_PRIM, dwords - 2); + data[1] = offset; + data[2] = prim->numverts; + data[3] = prim->vc_format; + data[4] = (prim->prim | + RADEON_PRIM_WALK_IND | + RADEON_COLOR_ORDER_RGBA | + RADEON_VTX_FMT_RADEON_MODE | + (count << RADEON_NUM_VERTICES_SHIFT)); + + do { + if (i < nbox) + radeon_emit_clip_rect(dev_priv, &sarea_priv->boxes[i]); + + radeon_cp_dispatch_indirect(dev, elt_buf, + prim->start, prim->finish); + + i++; + } while (i < nbox); + +} + +#define RADEON_MAX_TEXTURE_SIZE RADEON_BUFFER_SIZE + +static int radeon_cp_dispatch_texture(struct drm_device * dev, + struct drm_file *file_priv, + drm_radeon_texture_t * tex, + drm_radeon_tex_image_t * image) +{ + drm_radeon_private_t *dev_priv = dev->dev_private; + struct drm_buf *buf; + u32 format; + u32 *buffer; + const u8 __user *data; + int size, dwords, tex_width, blit_width, spitch; + u32 height; + int i; + u32 texpitch, microtile; + u32 offset, byte_offset; + RING_LOCALS; + + if (radeon_check_and_fixup_offset(dev_priv, file_priv, &tex->offset)) { + DRM_ERROR("Invalid destination offset\n"); + return -EINVAL; + } + + dev_priv->stats.boxes |= RADEON_BOX_TEXTURE_LOAD; + + /* Flush the pixel cache. This ensures no pixel data gets mixed + * up with the texture data from the host data blit, otherwise + * part of the texture image may be corrupted. + */ + BEGIN_RING(4); + RADEON_FLUSH_CACHE(); + RADEON_WAIT_UNTIL_IDLE(); + ADVANCE_RING(); + + /* The compiler won't optimize away a division by a variable, + * even if the only legal values are powers of two. Thus, we'll + * use a shift instead. + */ + switch (tex->format) { + case RADEON_TXFORMAT_ARGB8888: + case RADEON_TXFORMAT_RGBA8888: + format = RADEON_COLOR_FORMAT_ARGB8888; + tex_width = tex->width * 4; + blit_width = image->width * 4; + break; + case RADEON_TXFORMAT_AI88: + case RADEON_TXFORMAT_ARGB1555: + case RADEON_TXFORMAT_RGB565: + case RADEON_TXFORMAT_ARGB4444: + case RADEON_TXFORMAT_VYUY422: + case RADEON_TXFORMAT_YVYU422: + format = RADEON_COLOR_FORMAT_RGB565; + tex_width = tex->width * 2; + blit_width = image->width * 2; + break; + case RADEON_TXFORMAT_I8: + case RADEON_TXFORMAT_RGB332: + format = RADEON_COLOR_FORMAT_CI8; + tex_width = tex->width * 1; + blit_width = image->width * 1; + break; + default: + DRM_ERROR("invalid texture format %d\n", tex->format); + return -EINVAL; + } + spitch = blit_width >> 6; + if (spitch == 0 && image->height > 1) + return -EINVAL; + + texpitch = tex->pitch; + if ((texpitch << 22) & RADEON_DST_TILE_MICRO) { + microtile = 1; + if (tex_width < 64) { + texpitch &= ~(RADEON_DST_TILE_MICRO >> 22); + /* we got tiled coordinates, untile them */ + image->x *= 2; + } + } else + microtile = 0; + + /* this might fail for zero-sized uploads - are those illegal? */ + if (!radeon_check_offset(dev_priv, tex->offset + image->height * + blit_width - 1)) { + DRM_ERROR("Invalid final destination offset\n"); + return -EINVAL; + } + + DRM_DEBUG("tex=%dx%d blit=%d\n", tex_width, tex->height, blit_width); + + do { + DRM_DEBUG("tex: ofs=0x%x p=%d f=%d x=%u y=%u w=%u h=%u\n", + tex->offset >> 10, tex->pitch, tex->format, + image->x, image->y, image->width, image->height); + + /* Make a copy of some parameters in case we have to + * update them for a multi-pass texture blit. + */ + height = image->height; + data = (const u8 __user *)image->data; + + size = height * blit_width; + + if (size > RADEON_MAX_TEXTURE_SIZE) { + height = RADEON_MAX_TEXTURE_SIZE / blit_width; + size = height * blit_width; + } else if (size < 4 && size > 0) { + size = 4; + } else if (size == 0) { + return 0; + } + + buf = radeon_freelist_get(dev); + if (0 && !buf) { + radeon_do_cp_idle(dev_priv); + buf = radeon_freelist_get(dev); + } + if (!buf) { + DRM_DEBUG("EAGAIN\n"); + if (DRM_COPY_TO_USER(tex->image, image, sizeof(*image))) + return -EFAULT; + return -EAGAIN; + } + + /* Dispatch the indirect buffer. + */ + buffer = + (u32 *) ((char *)dev->agp_buffer_map->handle + buf->offset); + dwords = size / 4; + +#define RADEON_COPY_MT(_buf, _data, _width) \ + do { \ + if (DRM_COPY_FROM_USER(_buf, _data, (_width))) {\ + DRM_ERROR("EFAULT on pad, %d bytes\n", (_width)); \ + return -EFAULT; \ + } \ + } while(0) + + if (microtile) { + /* texture micro tiling in use, minimum texture width is thus 16 bytes. + however, we cannot use blitter directly for texture width < 64 bytes, + since minimum tex pitch is 64 bytes and we need this to match + the texture width, otherwise the blitter will tile it wrong. + Thus, tiling manually in this case. Additionally, need to special + case tex height = 1, since our actual image will have height 2 + and we need to ensure we don't read beyond the texture size + from user space. */ + if (tex->height == 1) { + if (tex_width >= 64 || tex_width <= 16) { + RADEON_COPY_MT(buffer, data, + (int)(tex_width * sizeof(u32))); + } else if (tex_width == 32) { + RADEON_COPY_MT(buffer, data, 16); + RADEON_COPY_MT(buffer + 8, + data + 16, 16); + } + } else if (tex_width >= 64 || tex_width == 16) { + RADEON_COPY_MT(buffer, data, + (int)(dwords * sizeof(u32))); + } else if (tex_width < 16) { + for (i = 0; i < tex->height; i++) { + RADEON_COPY_MT(buffer, data, tex_width); + buffer += 4; + data += tex_width; + } + } else if (tex_width == 32) { + /* TODO: make sure this works when not fitting in one buffer + (i.e. 32bytes x 2048...) */ + for (i = 0; i < tex->height; i += 2) { + RADEON_COPY_MT(buffer, data, 16); + data += 16; + RADEON_COPY_MT(buffer + 8, data, 16); + data += 16; + RADEON_COPY_MT(buffer + 4, data, 16); + data += 16; + RADEON_COPY_MT(buffer + 12, data, 16); + data += 16; + buffer += 16; + } + } + } else { + if (tex_width >= 32) { + /* Texture image width is larger than the minimum, so we + * can upload it directly. + */ + RADEON_COPY_MT(buffer, data, + (int)(dwords * sizeof(u32))); + } else { + /* Texture image width is less than the minimum, so we + * need to pad out each image scanline to the minimum + * width. + */ + for (i = 0; i < tex->height; i++) { + RADEON_COPY_MT(buffer, data, tex_width); + buffer += 8; + data += tex_width; + } + } + } + +#undef RADEON_COPY_MT + byte_offset = (image->y & ~2047) * blit_width; + buf->file_priv = file_priv; + buf->used = size; + offset = dev_priv->gart_buffers_offset + buf->offset; + BEGIN_RING(9); + OUT_RING(CP_PACKET3(RADEON_CNTL_BITBLT_MULTI, 5)); + OUT_RING(RADEON_GMC_SRC_PITCH_OFFSET_CNTL | + RADEON_GMC_DST_PITCH_OFFSET_CNTL | + RADEON_GMC_BRUSH_NONE | + (format << 8) | + RADEON_GMC_SRC_DATATYPE_COLOR | + RADEON_ROP3_S | + RADEON_DP_SRC_SOURCE_MEMORY | + RADEON_GMC_CLR_CMP_CNTL_DIS | RADEON_GMC_WR_MSK_DIS); + OUT_RING((spitch << 22) | (offset >> 10)); + OUT_RING((texpitch << 22) | ((tex->offset >> 10) + (byte_offset >> 10))); + OUT_RING(0); + OUT_RING((image->x << 16) | (image->y % 2048)); + OUT_RING((image->width << 16) | height); + RADEON_WAIT_UNTIL_2D_IDLE(); + ADVANCE_RING(); + COMMIT_RING(); + + radeon_cp_discard_buffer(dev, file_priv->masterp, buf); + + /* Update the input parameters for next time */ + image->y += height; + image->height -= height; + image->data = (const u8 __user *)image->data + size; + } while (image->height > 0); + + /* Flush the pixel cache after the blit completes. This ensures + * the texture data is written out to memory before rendering + * continues. + */ + BEGIN_RING(4); + RADEON_FLUSH_CACHE(); + RADEON_WAIT_UNTIL_2D_IDLE(); + ADVANCE_RING(); + COMMIT_RING(); + + return 0; +} + +static void radeon_cp_dispatch_stipple(struct drm_device * dev, u32 * stipple) +{ + drm_radeon_private_t *dev_priv = dev->dev_private; + int i; + RING_LOCALS; + DRM_DEBUG("\n"); + + BEGIN_RING(35); + + OUT_RING(CP_PACKET0(RADEON_RE_STIPPLE_ADDR, 0)); + OUT_RING(0x00000000); + + OUT_RING(CP_PACKET0_TABLE(RADEON_RE_STIPPLE_DATA, 31)); + for (i = 0; i < 32; i++) { + OUT_RING(stipple[i]); + } + + ADVANCE_RING(); +} + +static void radeon_apply_surface_regs(int surf_index, + drm_radeon_private_t *dev_priv) +{ + if (!dev_priv->mmio) + return; + + radeon_do_cp_idle(dev_priv); + + RADEON_WRITE(RADEON_SURFACE0_INFO + 16 * surf_index, + dev_priv->surfaces[surf_index].flags); + RADEON_WRITE(RADEON_SURFACE0_LOWER_BOUND + 16 * surf_index, + dev_priv->surfaces[surf_index].lower); + RADEON_WRITE(RADEON_SURFACE0_UPPER_BOUND + 16 * surf_index, + dev_priv->surfaces[surf_index].upper); +} + +/* Allocates a virtual surface + * doesn't always allocate a real surface, will stretch an existing + * surface when possible. + * + * Note that refcount can be at most 2, since during a free refcount=3 + * might mean we have to allocate a new surface which might not always + * be available. + * For example : we allocate three contiguous surfaces ABC. If B is + * freed, we suddenly need two surfaces to store A and C, which might + * not always be available. + */ +static int alloc_surface(drm_radeon_surface_alloc_t *new, + drm_radeon_private_t *dev_priv, + struct drm_file *file_priv) +{ + struct radeon_virt_surface *s; + int i; + int virt_surface_index; + uint32_t new_upper, new_lower; + + new_lower = new->address; + new_upper = new_lower + new->size - 1; + + /* sanity check */ + if ((new_lower >= new_upper) || (new->flags == 0) || (new->size == 0) || + ((new_upper & RADEON_SURF_ADDRESS_FIXED_MASK) != + RADEON_SURF_ADDRESS_FIXED_MASK) + || ((new_lower & RADEON_SURF_ADDRESS_FIXED_MASK) != 0)) + return -1; + + /* make sure there is no overlap with existing surfaces */ + for (i = 0; i < RADEON_MAX_SURFACES; i++) { + if ((dev_priv->surfaces[i].refcount != 0) && + (((new_lower >= dev_priv->surfaces[i].lower) && + (new_lower < dev_priv->surfaces[i].upper)) || + ((new_lower < dev_priv->surfaces[i].lower) && + (new_upper > dev_priv->surfaces[i].lower)))) { + return -1; + } + } + + /* find a virtual surface */ + for (i = 0; i < 2 * RADEON_MAX_SURFACES; i++) + if (dev_priv->virt_surfaces[i].file_priv == NULL) + break; + if (i == 2 * RADEON_MAX_SURFACES) { + return -1; + } + virt_surface_index = i; + + /* try to reuse an existing surface */ + for (i = 0; i < RADEON_MAX_SURFACES; i++) { + /* extend before */ + if ((dev_priv->surfaces[i].refcount == 1) && + (new->flags == dev_priv->surfaces[i].flags) && + (new_upper + 1 == dev_priv->surfaces[i].lower)) { + s = &(dev_priv->virt_surfaces[virt_surface_index]); + s->surface_index = i; + s->lower = new_lower; + s->upper = new_upper; + s->flags = new->flags; + s->file_priv = file_priv; + dev_priv->surfaces[i].refcount++; + dev_priv->surfaces[i].lower = s->lower; + radeon_apply_surface_regs(s->surface_index, dev_priv); + return virt_surface_index; + } + + /* extend after */ + if ((dev_priv->surfaces[i].refcount == 1) && + (new->flags == dev_priv->surfaces[i].flags) && + (new_lower == dev_priv->surfaces[i].upper + 1)) { + s = &(dev_priv->virt_surfaces[virt_surface_index]); + s->surface_index = i; + s->lower = new_lower; + s->upper = new_upper; + s->flags = new->flags; + s->file_priv = file_priv; + dev_priv->surfaces[i].refcount++; + dev_priv->surfaces[i].upper = s->upper; + radeon_apply_surface_regs(s->surface_index, dev_priv); + return virt_surface_index; + } + } + + /* okay, we need a new one */ + for (i = 0; i < RADEON_MAX_SURFACES; i++) { + if (dev_priv->surfaces[i].refcount == 0) { + s = &(dev_priv->virt_surfaces[virt_surface_index]); + s->surface_index = i; + s->lower = new_lower; + s->upper = new_upper; + s->flags = new->flags; + s->file_priv = file_priv; + dev_priv->surfaces[i].refcount = 1; + dev_priv->surfaces[i].lower = s->lower; + dev_priv->surfaces[i].upper = s->upper; + dev_priv->surfaces[i].flags = s->flags; + radeon_apply_surface_regs(s->surface_index, dev_priv); + return virt_surface_index; + } + } + + /* we didn't find anything */ + return -1; +} + +static int free_surface(struct drm_file *file_priv, + drm_radeon_private_t * dev_priv, + int lower) +{ + struct radeon_virt_surface *s; + int i; + /* find the virtual surface */ + for (i = 0; i < 2 * RADEON_MAX_SURFACES; i++) { + s = &(dev_priv->virt_surfaces[i]); + if (s->file_priv) { + if ((lower == s->lower) && (file_priv == s->file_priv)) + { + if (dev_priv->surfaces[s->surface_index]. + lower == s->lower) + dev_priv->surfaces[s->surface_index]. + lower = s->upper; + + if (dev_priv->surfaces[s->surface_index]. + upper == s->upper) + dev_priv->surfaces[s->surface_index]. + upper = s->lower; + + dev_priv->surfaces[s->surface_index].refcount--; + if (dev_priv->surfaces[s->surface_index]. + refcount == 0) + dev_priv->surfaces[s->surface_index]. + flags = 0; + s->file_priv = NULL; + radeon_apply_surface_regs(s->surface_index, + dev_priv); + return 0; + } + } + } + return 1; +} + +static void radeon_surfaces_release(struct drm_file *file_priv, + drm_radeon_private_t * dev_priv) +{ + int i; + for (i = 0; i < 2 * RADEON_MAX_SURFACES; i++) { + if (dev_priv->virt_surfaces[i].file_priv == file_priv) + free_surface(file_priv, dev_priv, + dev_priv->virt_surfaces[i].lower); + } +} + +/* ================================================================ + * IOCTL functions + */ +static int radeon_surface_alloc(struct drm_device *dev, void *data, struct drm_file *file_priv) +{ + drm_radeon_private_t *dev_priv = dev->dev_private; + drm_radeon_surface_alloc_t *alloc = data; + + if (alloc_surface(alloc, dev_priv, file_priv) == -1) + return -EINVAL; + else + return 0; +} + +static int radeon_surface_free(struct drm_device *dev, void *data, struct drm_file *file_priv) +{ + drm_radeon_private_t *dev_priv = dev->dev_private; + drm_radeon_surface_free_t *memfree = data; + + if (free_surface(file_priv, dev_priv, memfree->address)) + return -EINVAL; + else + return 0; +} + +static int radeon_cp_clear(struct drm_device *dev, void *data, struct drm_file *file_priv) +{ + drm_radeon_private_t *dev_priv = dev->dev_private; + struct drm_radeon_master_private *master_priv = file_priv->masterp->driver_priv; + drm_radeon_sarea_t *sarea_priv = master_priv->sarea_priv; + drm_radeon_clear_t *clear = data; + drm_radeon_clear_rect_t depth_boxes[RADEON_NR_SAREA_CLIPRECTS]; + DRM_DEBUG("\n"); + + LOCK_TEST_WITH_RETURN(dev, file_priv); + + RING_SPACE_TEST_WITH_RETURN(dev_priv); + + if (sarea_priv->nbox > RADEON_NR_SAREA_CLIPRECTS) + sarea_priv->nbox = RADEON_NR_SAREA_CLIPRECTS; + + if (DRM_COPY_FROM_USER(&depth_boxes, clear->depth_boxes, + sarea_priv->nbox * sizeof(depth_boxes[0]))) + return -EFAULT; + + radeon_cp_dispatch_clear(dev, file_priv->masterp, clear, depth_boxes); + + COMMIT_RING(); + return 0; +} + +/* Not sure why this isn't set all the time: + */ +static int radeon_do_init_pageflip(struct drm_device *dev, struct drm_master *master) +{ + drm_radeon_private_t *dev_priv = dev->dev_private; + struct drm_radeon_master_private *master_priv = master->driver_priv; + RING_LOCALS; + + DRM_DEBUG("\n"); + + BEGIN_RING(6); + RADEON_WAIT_UNTIL_3D_IDLE(); + OUT_RING(CP_PACKET0(RADEON_CRTC_OFFSET_CNTL, 0)); + OUT_RING(RADEON_READ(RADEON_CRTC_OFFSET_CNTL) | + RADEON_CRTC_OFFSET_FLIP_CNTL); + OUT_RING(CP_PACKET0(RADEON_CRTC2_OFFSET_CNTL, 0)); + OUT_RING(RADEON_READ(RADEON_CRTC2_OFFSET_CNTL) | + RADEON_CRTC_OFFSET_FLIP_CNTL); + ADVANCE_RING(); + + dev_priv->page_flipping = 1; + + if (master_priv->sarea_priv->pfCurrentPage != 1) + master_priv->sarea_priv->pfCurrentPage = 0; + + return 0; +} + +/* Swapping and flipping are different operations, need different ioctls. + * They can & should be intermixed to support multiple 3d windows. + */ +static int radeon_cp_flip(struct drm_device *dev, void *data, struct drm_file *file_priv) +{ + drm_radeon_private_t *dev_priv = dev->dev_private; + DRM_DEBUG("\n"); + + LOCK_TEST_WITH_RETURN(dev, file_priv); + + RING_SPACE_TEST_WITH_RETURN(dev_priv); + + if (!dev_priv->page_flipping) + radeon_do_init_pageflip(dev, file_priv->masterp); + + radeon_cp_dispatch_flip(dev, file_priv->masterp); + + COMMIT_RING(); + return 0; +} + +static int radeon_cp_swap(struct drm_device *dev, void *data, struct drm_file *file_priv) +{ + drm_radeon_private_t *dev_priv = dev->dev_private; + struct drm_radeon_master_private *master_priv = file_priv->masterp->driver_priv; + drm_radeon_sarea_t *sarea_priv = master_priv->sarea_priv; + + DRM_DEBUG("\n"); + + LOCK_TEST_WITH_RETURN(dev, file_priv); + + RING_SPACE_TEST_WITH_RETURN(dev_priv); + + if (sarea_priv->nbox > RADEON_NR_SAREA_CLIPRECTS) + sarea_priv->nbox = RADEON_NR_SAREA_CLIPRECTS; + + if ((dev_priv->flags & RADEON_FAMILY_MASK) >= CHIP_R600) + r600_cp_dispatch_swap(dev, file_priv); + else + radeon_cp_dispatch_swap(dev, file_priv->masterp); + sarea_priv->ctx_owner = 0; + + COMMIT_RING(); + return 0; +} + +static int radeon_cp_vertex(struct drm_device *dev, void *data, struct drm_file *file_priv) +{ + drm_radeon_private_t *dev_priv = dev->dev_private; + struct drm_radeon_master_private *master_priv = file_priv->masterp->driver_priv; + drm_radeon_sarea_t *sarea_priv; + struct drm_device_dma *dma = dev->dma; + struct drm_buf *buf; + drm_radeon_vertex_t *vertex = data; + drm_radeon_tcl_prim_t prim; + + LOCK_TEST_WITH_RETURN(dev, file_priv); + + sarea_priv = master_priv->sarea_priv; + + DRM_DEBUG("pid=%d index=%d count=%d discard=%d\n", + DRM_CURRENTPID, vertex->idx, vertex->count, vertex->discard); + + if (vertex->idx < 0 || vertex->idx >= dma->buf_count) { + DRM_ERROR("buffer index %d (of %d max)\n", + vertex->idx, dma->buf_count - 1); + return -EINVAL; + } + if (vertex->prim < 0 || vertex->prim > RADEON_PRIM_TYPE_3VRT_LINE_LIST) { + DRM_ERROR("buffer prim %d\n", vertex->prim); + return -EINVAL; + } + + RING_SPACE_TEST_WITH_RETURN(dev_priv); + VB_AGE_TEST_WITH_RETURN(dev_priv); + + buf = dma->buflist[vertex->idx]; + + if (buf->file_priv != file_priv) { + DRM_ERROR("process %d using buffer owned by %p\n", + DRM_CURRENTPID, buf->file_priv); + return -EINVAL; + } + if (buf->pending) { + DRM_ERROR("sending pending buffer %d\n", vertex->idx); + return -EINVAL; + } + + /* Build up a prim_t record: + */ + if (vertex->count) { + buf->used = vertex->count; /* not used? */ + + if (sarea_priv->dirty & ~RADEON_UPLOAD_CLIPRECTS) { + if (radeon_emit_state(dev_priv, file_priv, + &sarea_priv->context_state, + sarea_priv->tex_state, + sarea_priv->dirty)) { + DRM_ERROR("radeon_emit_state failed\n"); + return -EINVAL; + } + + sarea_priv->dirty &= ~(RADEON_UPLOAD_TEX0IMAGES | + RADEON_UPLOAD_TEX1IMAGES | + RADEON_UPLOAD_TEX2IMAGES | + RADEON_REQUIRE_QUIESCENCE); + } + + prim.start = 0; + prim.finish = vertex->count; /* unused */ + prim.prim = vertex->prim; + prim.numverts = vertex->count; + prim.vc_format = sarea_priv->vc_format; + + radeon_cp_dispatch_vertex(dev, file_priv, buf, &prim); + } + + if (vertex->discard) { + radeon_cp_discard_buffer(dev, file_priv->masterp, buf); + } + + COMMIT_RING(); + return 0; +} + +static int radeon_cp_indices(struct drm_device *dev, void *data, struct drm_file *file_priv) +{ + drm_radeon_private_t *dev_priv = dev->dev_private; + struct drm_radeon_master_private *master_priv = file_priv->masterp->driver_priv; + drm_radeon_sarea_t *sarea_priv; + struct drm_device_dma *dma = dev->dma; + struct drm_buf *buf; + drm_radeon_indices_t *elts = data; + drm_radeon_tcl_prim_t prim; + int count; + + LOCK_TEST_WITH_RETURN(dev, file_priv); + + sarea_priv = master_priv->sarea_priv; + + DRM_DEBUG("pid=%d index=%d start=%d end=%d discard=%d\n", + DRM_CURRENTPID, elts->idx, elts->start, elts->end, + elts->discard); + + if (elts->idx < 0 || elts->idx >= dma->buf_count) { + DRM_ERROR("buffer index %d (of %d max)\n", + elts->idx, dma->buf_count - 1); + return -EINVAL; + } + if (elts->prim < 0 || elts->prim > RADEON_PRIM_TYPE_3VRT_LINE_LIST) { + DRM_ERROR("buffer prim %d\n", elts->prim); + return -EINVAL; + } + + RING_SPACE_TEST_WITH_RETURN(dev_priv); + VB_AGE_TEST_WITH_RETURN(dev_priv); + + buf = dma->buflist[elts->idx]; + + if (buf->file_priv != file_priv) { + DRM_ERROR("process %d using buffer owned by %p\n", + DRM_CURRENTPID, buf->file_priv); + return -EINVAL; + } + if (buf->pending) { + DRM_ERROR("sending pending buffer %d\n", elts->idx); + return -EINVAL; + } + + count = (elts->end - elts->start) / sizeof(u16); + elts->start -= RADEON_INDEX_PRIM_OFFSET; + + if (elts->start & 0x7) { + DRM_ERROR("misaligned buffer 0x%x\n", elts->start); + return -EINVAL; + } + if (elts->start < buf->used) { + DRM_ERROR("no header 0x%x - 0x%x\n", elts->start, buf->used); + return -EINVAL; + } + + buf->used = elts->end; + + if (sarea_priv->dirty & ~RADEON_UPLOAD_CLIPRECTS) { + if (radeon_emit_state(dev_priv, file_priv, + &sarea_priv->context_state, + sarea_priv->tex_state, + sarea_priv->dirty)) { + DRM_ERROR("radeon_emit_state failed\n"); + return -EINVAL; + } + + sarea_priv->dirty &= ~(RADEON_UPLOAD_TEX0IMAGES | + RADEON_UPLOAD_TEX1IMAGES | + RADEON_UPLOAD_TEX2IMAGES | + RADEON_REQUIRE_QUIESCENCE); + } + + /* Build up a prim_t record: + */ + prim.start = elts->start; + prim.finish = elts->end; + prim.prim = elts->prim; + prim.offset = 0; /* offset from start of dma buffers */ + prim.numverts = RADEON_MAX_VB_VERTS; /* duh */ + prim.vc_format = sarea_priv->vc_format; + + radeon_cp_dispatch_indices(dev, file_priv->masterp, buf, &prim); + if (elts->discard) { + radeon_cp_discard_buffer(dev, file_priv->masterp, buf); + } + + COMMIT_RING(); + return 0; +} + +static int radeon_cp_texture(struct drm_device *dev, void *data, struct drm_file *file_priv) +{ + drm_radeon_private_t *dev_priv = dev->dev_private; + drm_radeon_texture_t *tex = data; + drm_radeon_tex_image_t image; + int ret; + + LOCK_TEST_WITH_RETURN(dev, file_priv); + + if (tex->image == NULL) { + DRM_ERROR("null texture image!\n"); + return -EINVAL; + } + + if (DRM_COPY_FROM_USER(&image, + (drm_radeon_tex_image_t __user *) tex->image, + sizeof(image))) + return -EFAULT; + + RING_SPACE_TEST_WITH_RETURN(dev_priv); + VB_AGE_TEST_WITH_RETURN(dev_priv); + + if ((dev_priv->flags & RADEON_FAMILY_MASK) >= CHIP_R600) + ret = r600_cp_dispatch_texture(dev, file_priv, tex, &image); + else + ret = radeon_cp_dispatch_texture(dev, file_priv, tex, &image); + + return ret; +} + +static int radeon_cp_stipple(struct drm_device *dev, void *data, struct drm_file *file_priv) +{ + drm_radeon_private_t *dev_priv = dev->dev_private; + drm_radeon_stipple_t *stipple = data; + u32 mask[32]; + + LOCK_TEST_WITH_RETURN(dev, file_priv); + + if (DRM_COPY_FROM_USER(&mask, stipple->mask, 32 * sizeof(u32))) + return -EFAULT; + + RING_SPACE_TEST_WITH_RETURN(dev_priv); + + radeon_cp_dispatch_stipple(dev, mask); + + COMMIT_RING(); + return 0; +} + +static int radeon_cp_indirect(struct drm_device *dev, void *data, struct drm_file *file_priv) +{ + drm_radeon_private_t *dev_priv = dev->dev_private; + struct drm_device_dma *dma = dev->dma; + struct drm_buf *buf; + drm_radeon_indirect_t *indirect = data; + RING_LOCALS; + + LOCK_TEST_WITH_RETURN(dev, file_priv); + + DRM_DEBUG("idx=%d s=%d e=%d d=%d\n", + indirect->idx, indirect->start, indirect->end, + indirect->discard); + + if (indirect->idx < 0 || indirect->idx >= dma->buf_count) { + DRM_ERROR("buffer index %d (of %d max)\n", + indirect->idx, dma->buf_count - 1); + return -EINVAL; + } + + buf = dma->buflist[indirect->idx]; + + if (buf->file_priv != file_priv) { + DRM_ERROR("process %d using buffer owned by %p\n", + DRM_CURRENTPID, buf->file_priv); + return -EINVAL; + } + if (buf->pending) { + DRM_ERROR("sending pending buffer %d\n", indirect->idx); + return -EINVAL; + } + + if (indirect->start < buf->used) { + DRM_ERROR("reusing indirect: start=0x%x actual=0x%x\n", + indirect->start, buf->used); + return -EINVAL; + } + + RING_SPACE_TEST_WITH_RETURN(dev_priv); + VB_AGE_TEST_WITH_RETURN(dev_priv); + + buf->used = indirect->end; + + /* Dispatch the indirect buffer full of commands from the + * X server. This is insecure and is thus only available to + * privileged clients. + */ + if ((dev_priv->flags & RADEON_FAMILY_MASK) >= CHIP_R600) + r600_cp_dispatch_indirect(dev, buf, indirect->start, indirect->end); + else { + /* Wait for the 3D stream to idle before the indirect buffer + * containing 2D acceleration commands is processed. + */ + BEGIN_RING(2); + RADEON_WAIT_UNTIL_3D_IDLE(); + ADVANCE_RING(); + radeon_cp_dispatch_indirect(dev, buf, indirect->start, indirect->end); + } + + if (indirect->discard) { + radeon_cp_discard_buffer(dev, file_priv->masterp, buf); + } + + COMMIT_RING(); + return 0; +} + +static int radeon_cp_vertex2(struct drm_device *dev, void *data, struct drm_file *file_priv) +{ + drm_radeon_private_t *dev_priv = dev->dev_private; + struct drm_radeon_master_private *master_priv = file_priv->masterp->driver_priv; + drm_radeon_sarea_t *sarea_priv; + struct drm_device_dma *dma = dev->dma; + struct drm_buf *buf; + drm_radeon_vertex2_t *vertex = data; + int i; + unsigned char laststate; + + LOCK_TEST_WITH_RETURN(dev, file_priv); + + sarea_priv = master_priv->sarea_priv; + + DRM_DEBUG("pid=%d index=%d discard=%d\n", + DRM_CURRENTPID, vertex->idx, vertex->discard); + + if (vertex->idx < 0 || vertex->idx >= dma->buf_count) { + DRM_ERROR("buffer index %d (of %d max)\n", + vertex->idx, dma->buf_count - 1); + return -EINVAL; + } + + RING_SPACE_TEST_WITH_RETURN(dev_priv); + VB_AGE_TEST_WITH_RETURN(dev_priv); + + buf = dma->buflist[vertex->idx]; + + if (buf->file_priv != file_priv) { + DRM_ERROR("process %d using buffer owned by %p\n", + DRM_CURRENTPID, buf->file_priv); + return -EINVAL; + } + + if (buf->pending) { + DRM_ERROR("sending pending buffer %d\n", vertex->idx); + return -EINVAL; + } + + if (sarea_priv->nbox > RADEON_NR_SAREA_CLIPRECTS) + return -EINVAL; + + for (laststate = 0xff, i = 0; i < vertex->nr_prims; i++) { + drm_radeon_prim_t prim; + drm_radeon_tcl_prim_t tclprim; + + if (DRM_COPY_FROM_USER(&prim, &vertex->prim[i], sizeof(prim))) + return -EFAULT; + + if (prim.stateidx != laststate) { + drm_radeon_state_t state; + + if (DRM_COPY_FROM_USER(&state, + &vertex->state[prim.stateidx], + sizeof(state))) + return -EFAULT; + + if (radeon_emit_state2(dev_priv, file_priv, &state)) { + DRM_ERROR("radeon_emit_state2 failed\n"); + return -EINVAL; + } + + laststate = prim.stateidx; + } + + tclprim.start = prim.start; + tclprim.finish = prim.finish; + tclprim.prim = prim.prim; + tclprim.vc_format = prim.vc_format; + + if (prim.prim & RADEON_PRIM_WALK_IND) { + tclprim.offset = prim.numverts * 64; + tclprim.numverts = RADEON_MAX_VB_VERTS; /* duh */ + + radeon_cp_dispatch_indices(dev, file_priv->masterp, buf, &tclprim); + } else { + tclprim.numverts = prim.numverts; + tclprim.offset = 0; /* not used */ + + radeon_cp_dispatch_vertex(dev, file_priv, buf, &tclprim); + } + + if (sarea_priv->nbox == 1) + sarea_priv->nbox = 0; + } + + if (vertex->discard) { + radeon_cp_discard_buffer(dev, file_priv->masterp, buf); + } + + COMMIT_RING(); + return 0; +} + +static int radeon_emit_packets(drm_radeon_private_t * dev_priv, + struct drm_file *file_priv, + drm_radeon_cmd_header_t header, + drm_radeon_kcmd_buffer_t *cmdbuf) +{ + int id = (int)header.packet.packet_id; + int sz, reg; + RING_LOCALS; + + if (id >= RADEON_MAX_STATE_PACKETS) + return -EINVAL; + + sz = packet[id].len; + reg = packet[id].start; + + if (sz * sizeof(u32) > drm_buffer_unprocessed(cmdbuf->buffer)) { + DRM_ERROR("Packet size provided larger than data provided\n"); + return -EINVAL; + } + + if (radeon_check_and_fixup_packets(dev_priv, file_priv, id, + cmdbuf->buffer)) { + DRM_ERROR("Packet verification failed\n"); + return -EINVAL; + } + + BEGIN_RING(sz + 1); + OUT_RING(CP_PACKET0(reg, (sz - 1))); + OUT_RING_DRM_BUFFER(cmdbuf->buffer, sz); + ADVANCE_RING(); + + return 0; +} + +static __inline__ int radeon_emit_scalars(drm_radeon_private_t *dev_priv, + drm_radeon_cmd_header_t header, + drm_radeon_kcmd_buffer_t *cmdbuf) +{ + int sz = header.scalars.count; + int start = header.scalars.offset; + int stride = header.scalars.stride; + RING_LOCALS; + + BEGIN_RING(3 + sz); + OUT_RING(CP_PACKET0(RADEON_SE_TCL_SCALAR_INDX_REG, 0)); + OUT_RING(start | (stride << RADEON_SCAL_INDX_DWORD_STRIDE_SHIFT)); + OUT_RING(CP_PACKET0_TABLE(RADEON_SE_TCL_SCALAR_DATA_REG, sz - 1)); + OUT_RING_DRM_BUFFER(cmdbuf->buffer, sz); + ADVANCE_RING(); + return 0; +} + +/* God this is ugly + */ +static __inline__ int radeon_emit_scalars2(drm_radeon_private_t *dev_priv, + drm_radeon_cmd_header_t header, + drm_radeon_kcmd_buffer_t *cmdbuf) +{ + int sz = header.scalars.count; + int start = ((unsigned int)header.scalars.offset) + 0x100; + int stride = header.scalars.stride; + RING_LOCALS; + + BEGIN_RING(3 + sz); + OUT_RING(CP_PACKET0(RADEON_SE_TCL_SCALAR_INDX_REG, 0)); + OUT_RING(start | (stride << RADEON_SCAL_INDX_DWORD_STRIDE_SHIFT)); + OUT_RING(CP_PACKET0_TABLE(RADEON_SE_TCL_SCALAR_DATA_REG, sz - 1)); + OUT_RING_DRM_BUFFER(cmdbuf->buffer, sz); + ADVANCE_RING(); + return 0; +} + +static __inline__ int radeon_emit_vectors(drm_radeon_private_t *dev_priv, + drm_radeon_cmd_header_t header, + drm_radeon_kcmd_buffer_t *cmdbuf) +{ + int sz = header.vectors.count; + int start = header.vectors.offset; + int stride = header.vectors.stride; + RING_LOCALS; + + BEGIN_RING(5 + sz); + OUT_RING_REG(RADEON_SE_TCL_STATE_FLUSH, 0); + OUT_RING(CP_PACKET0(RADEON_SE_TCL_VECTOR_INDX_REG, 0)); + OUT_RING(start | (stride << RADEON_VEC_INDX_OCTWORD_STRIDE_SHIFT)); + OUT_RING(CP_PACKET0_TABLE(RADEON_SE_TCL_VECTOR_DATA_REG, (sz - 1))); + OUT_RING_DRM_BUFFER(cmdbuf->buffer, sz); + ADVANCE_RING(); + + return 0; +} + +static __inline__ int radeon_emit_veclinear(drm_radeon_private_t *dev_priv, + drm_radeon_cmd_header_t header, + drm_radeon_kcmd_buffer_t *cmdbuf) +{ + int sz = header.veclinear.count * 4; + int start = header.veclinear.addr_lo | (header.veclinear.addr_hi << 8); + RING_LOCALS; + + if (!sz) + return 0; + if (sz * 4 > drm_buffer_unprocessed(cmdbuf->buffer)) + return -EINVAL; + + BEGIN_RING(5 + sz); + OUT_RING_REG(RADEON_SE_TCL_STATE_FLUSH, 0); + OUT_RING(CP_PACKET0(RADEON_SE_TCL_VECTOR_INDX_REG, 0)); + OUT_RING(start | (1 << RADEON_VEC_INDX_OCTWORD_STRIDE_SHIFT)); + OUT_RING(CP_PACKET0_TABLE(RADEON_SE_TCL_VECTOR_DATA_REG, (sz - 1))); + OUT_RING_DRM_BUFFER(cmdbuf->buffer, sz); + ADVANCE_RING(); + + return 0; +} + +static int radeon_emit_packet3(struct drm_device * dev, + struct drm_file *file_priv, + drm_radeon_kcmd_buffer_t *cmdbuf) +{ + drm_radeon_private_t *dev_priv = dev->dev_private; + unsigned int cmdsz; + int ret; + RING_LOCALS; + + DRM_DEBUG("\n"); + + if ((ret = radeon_check_and_fixup_packet3(dev_priv, file_priv, + cmdbuf, &cmdsz))) { + DRM_ERROR("Packet verification failed\n"); + return ret; + } + + BEGIN_RING(cmdsz); + OUT_RING_DRM_BUFFER(cmdbuf->buffer, cmdsz); + ADVANCE_RING(); + + return 0; +} + +static int radeon_emit_packet3_cliprect(struct drm_device *dev, + struct drm_file *file_priv, + drm_radeon_kcmd_buffer_t *cmdbuf, + int orig_nbox) +{ + drm_radeon_private_t *dev_priv = dev->dev_private; + struct drm_clip_rect box; + unsigned int cmdsz; + int ret; + struct drm_clip_rect __user *boxes = cmdbuf->boxes; + int i = 0; + RING_LOCALS; + + DRM_DEBUG("\n"); + + if ((ret = radeon_check_and_fixup_packet3(dev_priv, file_priv, + cmdbuf, &cmdsz))) { + DRM_ERROR("Packet verification failed\n"); + return ret; + } + + if (!orig_nbox) + goto out; + + do { + if (i < cmdbuf->nbox) { + if (DRM_COPY_FROM_USER(&box, &boxes[i], sizeof(box))) + return -EFAULT; + /* FIXME The second and subsequent times round + * this loop, send a WAIT_UNTIL_3D_IDLE before + * calling emit_clip_rect(). This fixes a + * lockup on fast machines when sending + * several cliprects with a cmdbuf, as when + * waving a 2D window over a 3D + * window. Something in the commands from user + * space seems to hang the card when they're + * sent several times in a row. That would be + * the correct place to fix it but this works + * around it until I can figure that out - Tim + * Smith */ + if (i) { + BEGIN_RING(2); + RADEON_WAIT_UNTIL_3D_IDLE(); + ADVANCE_RING(); + } + radeon_emit_clip_rect(dev_priv, &box); + } + + BEGIN_RING(cmdsz); + OUT_RING_DRM_BUFFER(cmdbuf->buffer, cmdsz); + ADVANCE_RING(); + + } while (++i < cmdbuf->nbox); + if (cmdbuf->nbox == 1) + cmdbuf->nbox = 0; + + return 0; + out: + drm_buffer_advance(cmdbuf->buffer, cmdsz * 4); + return 0; +} + +static int radeon_emit_wait(struct drm_device * dev, int flags) +{ + drm_radeon_private_t *dev_priv = dev->dev_private; + RING_LOCALS; + + DRM_DEBUG("%x\n", flags); + switch (flags) { + case RADEON_WAIT_2D: + BEGIN_RING(2); + RADEON_WAIT_UNTIL_2D_IDLE(); + ADVANCE_RING(); + break; + case RADEON_WAIT_3D: + BEGIN_RING(2); + RADEON_WAIT_UNTIL_3D_IDLE(); + ADVANCE_RING(); + break; + case RADEON_WAIT_2D | RADEON_WAIT_3D: + BEGIN_RING(2); + RADEON_WAIT_UNTIL_IDLE(); + ADVANCE_RING(); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int radeon_cp_cmdbuf(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + drm_radeon_private_t *dev_priv = dev->dev_private; + struct drm_device_dma *dma = dev->dma; + struct drm_buf *buf = NULL; + drm_radeon_cmd_header_t stack_header; + int idx; + drm_radeon_kcmd_buffer_t *cmdbuf = data; + int orig_nbox; + + LOCK_TEST_WITH_RETURN(dev, file_priv); + + RING_SPACE_TEST_WITH_RETURN(dev_priv); + VB_AGE_TEST_WITH_RETURN(dev_priv); + + if (cmdbuf->bufsz > 64 * 1024 || cmdbuf->bufsz < 0) { + return -EINVAL; + } + + /* Allocate an in-kernel area and copy in the cmdbuf. Do this to avoid + * races between checking values and using those values in other code, + * and simply to avoid a lot of function calls to copy in data. + */ + if (cmdbuf->bufsz != 0) { + int rv; + void __user *buffer = cmdbuf->buffer; + rv = drm_buffer_alloc(&cmdbuf->buffer, cmdbuf->bufsz); + if (rv) + return rv; + rv = drm_buffer_copy_from_user(cmdbuf->buffer, buffer, + cmdbuf->bufsz); + if (rv) { + drm_buffer_free(cmdbuf->buffer); + return rv; + } + } else + goto done; + + orig_nbox = cmdbuf->nbox; + + if (dev_priv->microcode_version == UCODE_R300) { + int temp; + temp = r300_do_cp_cmdbuf(dev, file_priv, cmdbuf); + + drm_buffer_free(cmdbuf->buffer); + + return temp; + } + + /* microcode_version != r300 */ + while (drm_buffer_unprocessed(cmdbuf->buffer) >= sizeof(stack_header)) { + + drm_radeon_cmd_header_t *header; + header = drm_buffer_read_object(cmdbuf->buffer, + sizeof(stack_header), &stack_header); + + switch (header->header.cmd_type) { + case RADEON_CMD_PACKET: + DRM_DEBUG("RADEON_CMD_PACKET\n"); + if (radeon_emit_packets + (dev_priv, file_priv, *header, cmdbuf)) { + DRM_ERROR("radeon_emit_packets failed\n"); + goto err; + } + break; + + case RADEON_CMD_SCALARS: + DRM_DEBUG("RADEON_CMD_SCALARS\n"); + if (radeon_emit_scalars(dev_priv, *header, cmdbuf)) { + DRM_ERROR("radeon_emit_scalars failed\n"); + goto err; + } + break; + + case RADEON_CMD_VECTORS: + DRM_DEBUG("RADEON_CMD_VECTORS\n"); + if (radeon_emit_vectors(dev_priv, *header, cmdbuf)) { + DRM_ERROR("radeon_emit_vectors failed\n"); + goto err; + } + break; + + case RADEON_CMD_DMA_DISCARD: + DRM_DEBUG("RADEON_CMD_DMA_DISCARD\n"); + idx = header->dma.buf_idx; + if (idx < 0 || idx >= dma->buf_count) { + DRM_ERROR("buffer index %d (of %d max)\n", + idx, dma->buf_count - 1); + goto err; + } + + buf = dma->buflist[idx]; + if (buf->file_priv != file_priv || buf->pending) { + DRM_ERROR("bad buffer %p %p %d\n", + buf->file_priv, file_priv, + buf->pending); + goto err; + } + + radeon_cp_discard_buffer(dev, file_priv->masterp, buf); + break; + + case RADEON_CMD_PACKET3: + DRM_DEBUG("RADEON_CMD_PACKET3\n"); + if (radeon_emit_packet3(dev, file_priv, cmdbuf)) { + DRM_ERROR("radeon_emit_packet3 failed\n"); + goto err; + } + break; + + case RADEON_CMD_PACKET3_CLIP: + DRM_DEBUG("RADEON_CMD_PACKET3_CLIP\n"); + if (radeon_emit_packet3_cliprect + (dev, file_priv, cmdbuf, orig_nbox)) { + DRM_ERROR("radeon_emit_packet3_clip failed\n"); + goto err; + } + break; + + case RADEON_CMD_SCALARS2: + DRM_DEBUG("RADEON_CMD_SCALARS2\n"); + if (radeon_emit_scalars2(dev_priv, *header, cmdbuf)) { + DRM_ERROR("radeon_emit_scalars2 failed\n"); + goto err; + } + break; + + case RADEON_CMD_WAIT: + DRM_DEBUG("RADEON_CMD_WAIT\n"); + if (radeon_emit_wait(dev, header->wait.flags)) { + DRM_ERROR("radeon_emit_wait failed\n"); + goto err; + } + break; + case RADEON_CMD_VECLINEAR: + DRM_DEBUG("RADEON_CMD_VECLINEAR\n"); + if (radeon_emit_veclinear(dev_priv, *header, cmdbuf)) { + DRM_ERROR("radeon_emit_veclinear failed\n"); + goto err; + } + break; + + default: + DRM_ERROR("bad cmd_type %d at byte %d\n", + header->header.cmd_type, + cmdbuf->buffer->iterator); + goto err; + } + } + + drm_buffer_free(cmdbuf->buffer); + + done: + DRM_DEBUG("DONE\n"); + COMMIT_RING(); + return 0; + + err: + drm_buffer_free(cmdbuf->buffer); + return -EINVAL; +} + +static int radeon_cp_getparam(struct drm_device *dev, void *data, struct drm_file *file_priv) +{ + drm_radeon_private_t *dev_priv = dev->dev_private; + drm_radeon_getparam_t *param = data; + int value; + + DRM_DEBUG("pid=%d\n", DRM_CURRENTPID); + + switch (param->param) { + case RADEON_PARAM_GART_BUFFER_OFFSET: + value = dev_priv->gart_buffers_offset; + break; + case RADEON_PARAM_LAST_FRAME: + dev_priv->stats.last_frame_reads++; + value = GET_SCRATCH(dev_priv, 0); + break; + case RADEON_PARAM_LAST_DISPATCH: + value = GET_SCRATCH(dev_priv, 1); + break; + case RADEON_PARAM_LAST_CLEAR: + dev_priv->stats.last_clear_reads++; + value = GET_SCRATCH(dev_priv, 2); + break; + case RADEON_PARAM_IRQ_NR: + if ((dev_priv->flags & RADEON_FAMILY_MASK) >= CHIP_R600) + value = 0; + else + value = dev->irq; + break; + case RADEON_PARAM_GART_BASE: + value = dev_priv->gart_vm_start; + break; + case RADEON_PARAM_REGISTER_HANDLE: + value = dev_priv->mmio->offset; + break; + case RADEON_PARAM_STATUS_HANDLE: + value = dev_priv->ring_rptr_offset; + break; +#ifndef __LP64__ + /* + * This ioctl() doesn't work on 64-bit platforms because hw_lock is a + * pointer which can't fit into an int-sized variable. According to + * Michel Dänzer, the ioctl() is only used on embedded platforms, so + * not supporting it shouldn't be a problem. If the same functionality + * is needed on 64-bit platforms, a new ioctl() would have to be added, + * so backwards-compatibility for the embedded platforms can be + * maintained. --davidm 4-Feb-2004. + */ + case RADEON_PARAM_SAREA_HANDLE: + /* The lock is the first dword in the sarea. */ + /* no users of this parameter */ + break; +#endif + case RADEON_PARAM_GART_TEX_HANDLE: + value = dev_priv->gart_textures_offset; + break; + case RADEON_PARAM_SCRATCH_OFFSET: + if (!dev_priv->writeback_works) + return -EINVAL; + if ((dev_priv->flags & RADEON_FAMILY_MASK) >= CHIP_R600) + value = R600_SCRATCH_REG_OFFSET; + else + value = RADEON_SCRATCH_REG_OFFSET; + break; + case RADEON_PARAM_CARD_TYPE: + if (dev_priv->flags & RADEON_IS_PCIE) + value = RADEON_CARD_PCIE; + else if (dev_priv->flags & RADEON_IS_AGP) + value = RADEON_CARD_AGP; + else + value = RADEON_CARD_PCI; + break; + case RADEON_PARAM_VBLANK_CRTC: + value = radeon_vblank_crtc_get(dev); + break; + case RADEON_PARAM_FB_LOCATION: + value = radeon_read_fb_location(dev_priv); + break; + case RADEON_PARAM_NUM_GB_PIPES: + value = dev_priv->num_gb_pipes; + break; + case RADEON_PARAM_NUM_Z_PIPES: + value = dev_priv->num_z_pipes; + break; + default: + DRM_DEBUG("Invalid parameter %d\n", param->param); + return -EINVAL; + } + + if (DRM_COPY_TO_USER(param->value, &value, sizeof(int))) { + DRM_ERROR("copy_to_user\n"); + return -EFAULT; + } + + return 0; +} + +static int radeon_cp_setparam(struct drm_device *dev, void *data, struct drm_file *file_priv) +{ + drm_radeon_private_t *dev_priv = dev->dev_private; + struct drm_radeon_master_private *master_priv = file_priv->masterp->driver_priv; + drm_radeon_setparam_t *sp = data; + struct drm_radeon_driver_file_fields *radeon_priv; + + switch (sp->param) { + case RADEON_SETPARAM_FB_LOCATION: + radeon_priv = file_priv->driver_priv; + radeon_priv->radeon_fb_delta = dev_priv->fb_location - + sp->value; + break; + case RADEON_SETPARAM_SWITCH_TILING: + if (sp->value == 0) { + DRM_DEBUG("color tiling disabled\n"); + dev_priv->front_pitch_offset &= ~RADEON_DST_TILE_MACRO; + dev_priv->back_pitch_offset &= ~RADEON_DST_TILE_MACRO; + if (master_priv->sarea_priv) + master_priv->sarea_priv->tiling_enabled = 0; + } else if (sp->value == 1) { + DRM_DEBUG("color tiling enabled\n"); + dev_priv->front_pitch_offset |= RADEON_DST_TILE_MACRO; + dev_priv->back_pitch_offset |= RADEON_DST_TILE_MACRO; + if (master_priv->sarea_priv) + master_priv->sarea_priv->tiling_enabled = 1; + } + break; + case RADEON_SETPARAM_PCIGART_LOCATION: + dev_priv->pcigart_offset = sp->value; + dev_priv->pcigart_offset_set = 1; + break; + case RADEON_SETPARAM_NEW_MEMMAP: + dev_priv->new_memmap = sp->value; + break; + case RADEON_SETPARAM_PCIGART_TABLE_SIZE: + dev_priv->gart_info.table_size = sp->value; + if (dev_priv->gart_info.table_size < RADEON_PCIGART_TABLE_SIZE) + dev_priv->gart_info.table_size = RADEON_PCIGART_TABLE_SIZE; + break; + case RADEON_SETPARAM_VBLANK_CRTC: + return radeon_vblank_crtc_set(dev, sp->value); + break; + default: + DRM_DEBUG("Invalid parameter %d\n", sp->param); + return -EINVAL; + } + + return 0; +} + +/* When a client dies: + * - Check for and clean up flipped page state + * - Free any alloced GART memory. + * - Free any alloced radeon surfaces. + * + * DRM infrastructure takes care of reclaiming dma buffers. + */ +void radeon_driver_preclose(struct drm_device *dev, struct drm_file *file_priv) +{ + if (dev->dev_private) { + drm_radeon_private_t *dev_priv = dev->dev_private; + dev_priv->page_flipping = 0; + radeon_mem_release(file_priv, dev_priv->gart_heap); + radeon_mem_release(file_priv, dev_priv->fb_heap); + radeon_surfaces_release(file_priv, dev_priv); + } +} + +void radeon_driver_lastclose(struct drm_device *dev) +{ + radeon_surfaces_release(PCIGART_FILE_PRIV, dev->dev_private); + radeon_do_release(dev); +} + +int radeon_driver_open(struct drm_device *dev, struct drm_file *file_priv) +{ + drm_radeon_private_t *dev_priv = dev->dev_private; + struct drm_radeon_driver_file_fields *radeon_priv; + + DRM_DEBUG("\n"); + radeon_priv = malloc(sizeof(*radeon_priv), DRM_MEM_DRIVER, M_WAITOK); + + if (!radeon_priv) + return -ENOMEM; + + file_priv->driver_priv = radeon_priv; + + if (dev_priv) + radeon_priv->radeon_fb_delta = dev_priv->fb_location; + else + radeon_priv->radeon_fb_delta = 0; + return 0; +} + +void radeon_driver_postclose(struct drm_device *dev, struct drm_file *file_priv) +{ + struct drm_radeon_driver_file_fields *radeon_priv = + file_priv->driver_priv; + + free(radeon_priv, DRM_MEM_DRIVER); +} + +struct drm_ioctl_desc radeon_ioctls[] = { + DRM_IOCTL_DEF_DRV(RADEON_CP_INIT, radeon_cp_init, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), + DRM_IOCTL_DEF_DRV(RADEON_CP_START, radeon_cp_start, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), + DRM_IOCTL_DEF_DRV(RADEON_CP_STOP, radeon_cp_stop, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), + DRM_IOCTL_DEF_DRV(RADEON_CP_RESET, radeon_cp_reset, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), + DRM_IOCTL_DEF_DRV(RADEON_CP_IDLE, radeon_cp_idle, DRM_AUTH), + DRM_IOCTL_DEF_DRV(RADEON_CP_RESUME, radeon_cp_resume, DRM_AUTH), + DRM_IOCTL_DEF_DRV(RADEON_RESET, radeon_engine_reset, DRM_AUTH), + DRM_IOCTL_DEF_DRV(RADEON_FULLSCREEN, radeon_fullscreen, DRM_AUTH), + DRM_IOCTL_DEF_DRV(RADEON_SWAP, radeon_cp_swap, DRM_AUTH), + DRM_IOCTL_DEF_DRV(RADEON_CLEAR, radeon_cp_clear, DRM_AUTH), + DRM_IOCTL_DEF_DRV(RADEON_VERTEX, radeon_cp_vertex, DRM_AUTH), + DRM_IOCTL_DEF_DRV(RADEON_INDICES, radeon_cp_indices, DRM_AUTH), + DRM_IOCTL_DEF_DRV(RADEON_TEXTURE, radeon_cp_texture, DRM_AUTH), + DRM_IOCTL_DEF_DRV(RADEON_STIPPLE, radeon_cp_stipple, DRM_AUTH), + DRM_IOCTL_DEF_DRV(RADEON_INDIRECT, radeon_cp_indirect, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), + DRM_IOCTL_DEF_DRV(RADEON_VERTEX2, radeon_cp_vertex2, DRM_AUTH), + DRM_IOCTL_DEF_DRV(RADEON_CMDBUF, radeon_cp_cmdbuf, DRM_AUTH), + DRM_IOCTL_DEF_DRV(RADEON_GETPARAM, radeon_cp_getparam, DRM_AUTH), + DRM_IOCTL_DEF_DRV(RADEON_FLIP, radeon_cp_flip, DRM_AUTH), + DRM_IOCTL_DEF_DRV(RADEON_ALLOC, radeon_mem_alloc, DRM_AUTH), + DRM_IOCTL_DEF_DRV(RADEON_FREE, radeon_mem_free, DRM_AUTH), + DRM_IOCTL_DEF_DRV(RADEON_INIT_HEAP, radeon_mem_init_heap, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), + DRM_IOCTL_DEF_DRV(RADEON_IRQ_EMIT, radeon_irq_emit, DRM_AUTH), + DRM_IOCTL_DEF_DRV(RADEON_IRQ_WAIT, radeon_irq_wait, DRM_AUTH), + DRM_IOCTL_DEF_DRV(RADEON_SETPARAM, radeon_cp_setparam, DRM_AUTH), + DRM_IOCTL_DEF_DRV(RADEON_SURF_ALLOC, radeon_surface_alloc, DRM_AUTH), + DRM_IOCTL_DEF_DRV(RADEON_SURF_FREE, radeon_surface_free, DRM_AUTH), + DRM_IOCTL_DEF_DRV(RADEON_CS, r600_cs_legacy_ioctl, DRM_AUTH) +}; + +int radeon_max_ioctl = DRM_ARRAY_SIZE(radeon_ioctls); diff --git a/sys/dev/drm2/radeon/radeon_test.c b/sys/dev/drm2/radeon/radeon_test.c new file mode 100644 index 00000000000..7774699332c --- /dev/null +++ b/sys/dev/drm2/radeon/radeon_test.c @@ -0,0 +1,509 @@ +/* + * Copyright 2009 VMware, Inc. + * + * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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. + * + * Authors: Michel Dänzer + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include "radeon_reg.h" +#include "radeon.h" + +#define RADEON_TEST_COPY_BLIT 1 +#define RADEON_TEST_COPY_DMA 0 + + +/* Test BO GTT->VRAM and VRAM->GTT GPU copies across the whole GTT aperture */ +static void radeon_do_test_moves(struct radeon_device *rdev, int flag) +{ + struct radeon_bo *vram_obj = NULL; + struct radeon_bo **gtt_obj = NULL; + struct radeon_fence *fence = NULL; + uint64_t gtt_addr, vram_addr; + unsigned i, n, size; + int r, ring; + + switch (flag) { + case RADEON_TEST_COPY_DMA: + ring = radeon_copy_dma_ring_index(rdev); + break; + case RADEON_TEST_COPY_BLIT: + ring = radeon_copy_blit_ring_index(rdev); + break; + default: + DRM_ERROR("Unknown copy method\n"); + return; + } + + size = 1024 * 1024; + + /* Number of tests = + * (Total GTT - IB pool - writeback page - ring buffers) / test size + */ + n = rdev->mc.gtt_size - RADEON_IB_POOL_SIZE*64*1024; + for (i = 0; i < RADEON_NUM_RINGS; ++i) + n -= rdev->ring[i].ring_size; + if (rdev->wb.wb_obj) + n -= RADEON_GPU_PAGE_SIZE; + if (rdev->ih.ring_obj) + n -= rdev->ih.ring_size; + n /= size; + + gtt_obj = malloc(n * sizeof(*gtt_obj), DRM_MEM_DRIVER, M_ZERO | M_WAITOK); + if (!gtt_obj) { + DRM_ERROR("Failed to allocate %d pointers\n", n); + r = 1; + goto out_cleanup; + } + + r = radeon_bo_create(rdev, size, PAGE_SIZE, true, RADEON_GEM_DOMAIN_VRAM, + NULL, &vram_obj); + if (r) { + DRM_ERROR("Failed to create VRAM object\n"); + goto out_cleanup; + } + r = radeon_bo_reserve(vram_obj, false); + if (unlikely(r != 0)) + goto out_cleanup; + r = radeon_bo_pin(vram_obj, RADEON_GEM_DOMAIN_VRAM, &vram_addr); + if (r) { + DRM_ERROR("Failed to pin VRAM object\n"); + goto out_cleanup; + } + for (i = 0; i < n; i++) { + void *gtt_map, *vram_map; + void **gtt_start, **gtt_end; + void **vram_start, **vram_end; + + r = radeon_bo_create(rdev, size, PAGE_SIZE, true, + RADEON_GEM_DOMAIN_GTT, NULL, gtt_obj + i); + if (r) { + DRM_ERROR("Failed to create GTT object %d\n", i); + goto out_cleanup; + } + + r = radeon_bo_reserve(gtt_obj[i], false); + if (unlikely(r != 0)) + goto out_cleanup; + r = radeon_bo_pin(gtt_obj[i], RADEON_GEM_DOMAIN_GTT, >t_addr); + if (r) { + DRM_ERROR("Failed to pin GTT object %d\n", i); + goto out_cleanup; + } + + r = radeon_bo_kmap(gtt_obj[i], >t_map); + if (r) { + DRM_ERROR("Failed to map GTT object %d\n", i); + goto out_cleanup; + } + + for (gtt_start = gtt_map, gtt_end = (void *)((uintptr_t)gtt_map + size); + gtt_start < gtt_end; + gtt_start++) + *gtt_start = gtt_start; + + radeon_bo_kunmap(gtt_obj[i]); + + if (ring == R600_RING_TYPE_DMA_INDEX) + r = radeon_copy_dma(rdev, gtt_addr, vram_addr, size / RADEON_GPU_PAGE_SIZE, &fence); + else + r = radeon_copy_blit(rdev, gtt_addr, vram_addr, size / RADEON_GPU_PAGE_SIZE, &fence); + if (r) { + DRM_ERROR("Failed GTT->VRAM copy %d\n", i); + goto out_cleanup; + } + + r = radeon_fence_wait(fence, false); + if (r) { + DRM_ERROR("Failed to wait for GTT->VRAM fence %d\n", i); + goto out_cleanup; + } + + radeon_fence_unref(&fence); + + r = radeon_bo_kmap(vram_obj, &vram_map); + if (r) { + DRM_ERROR("Failed to map VRAM object after copy %d\n", i); + goto out_cleanup; + } + + for (gtt_start = gtt_map, gtt_end = (void *)((uintptr_t)gtt_map + size), + vram_start = vram_map, vram_end = (void *)((uintptr_t)vram_map + size); + vram_start < vram_end; + gtt_start++, vram_start++) { + if (*vram_start != gtt_start) { + DRM_ERROR("Incorrect GTT->VRAM copy %d: Got 0x%p, " + "expected 0x%p (GTT/VRAM offset " + "0x%16llx/0x%16llx)\n", + i, *vram_start, gtt_start, + (unsigned long long) + ((uintptr_t)gtt_addr - (uintptr_t)rdev->mc.gtt_start + + (uintptr_t)gtt_start - (uintptr_t)gtt_map), + (unsigned long long) + ((uintptr_t)vram_addr - (uintptr_t)rdev->mc.vram_start + + (uintptr_t)gtt_start - (uintptr_t)gtt_map)); + radeon_bo_kunmap(vram_obj); + goto out_cleanup; + } + *vram_start = vram_start; + } + + radeon_bo_kunmap(vram_obj); + + if (ring == R600_RING_TYPE_DMA_INDEX) + r = radeon_copy_dma(rdev, vram_addr, gtt_addr, size / RADEON_GPU_PAGE_SIZE, &fence); + else + r = radeon_copy_blit(rdev, vram_addr, gtt_addr, size / RADEON_GPU_PAGE_SIZE, &fence); + if (r) { + DRM_ERROR("Failed VRAM->GTT copy %d\n", i); + goto out_cleanup; + } + + r = radeon_fence_wait(fence, false); + if (r) { + DRM_ERROR("Failed to wait for VRAM->GTT fence %d\n", i); + goto out_cleanup; + } + + radeon_fence_unref(&fence); + + r = radeon_bo_kmap(gtt_obj[i], >t_map); + if (r) { + DRM_ERROR("Failed to map GTT object after copy %d\n", i); + goto out_cleanup; + } + + for (gtt_start = gtt_map, gtt_end = (void *)((uintptr_t)gtt_map + size), + vram_start = vram_map, vram_end = (void *)((uintptr_t)vram_map + size); + gtt_start < gtt_end; + gtt_start++, vram_start++) { + if (*gtt_start != vram_start) { + DRM_ERROR("Incorrect VRAM->GTT copy %d: Got 0x%p, " + "expected 0x%p (VRAM/GTT offset " + "0x%16llx/0x%16llx)\n", + i, *gtt_start, vram_start, + (unsigned long long) + ((uintptr_t)vram_addr - (uintptr_t)rdev->mc.vram_start + + (uintptr_t)vram_start - (uintptr_t)vram_map), + (unsigned long long) + ((uintptr_t)gtt_addr - (uintptr_t)rdev->mc.gtt_start + + (uintptr_t)vram_start - (uintptr_t)vram_map)); + radeon_bo_kunmap(gtt_obj[i]); + goto out_cleanup; + } + } + + radeon_bo_kunmap(gtt_obj[i]); + + DRM_INFO("Tested GTT->VRAM and VRAM->GTT copy for GTT offset 0x%jx\n", + (uintmax_t)gtt_addr - rdev->mc.gtt_start); + } + +out_cleanup: + if (vram_obj) { + if (radeon_bo_is_reserved(vram_obj)) { + radeon_bo_unpin(vram_obj); + radeon_bo_unreserve(vram_obj); + } + radeon_bo_unref(&vram_obj); + } + if (gtt_obj) { + for (i = 0; i < n; i++) { + if (gtt_obj[i]) { + if (radeon_bo_is_reserved(gtt_obj[i])) { + radeon_bo_unpin(gtt_obj[i]); + radeon_bo_unreserve(gtt_obj[i]); + } + radeon_bo_unref(>t_obj[i]); + } + } + free(gtt_obj, DRM_MEM_DRIVER); + } + if (fence) { + radeon_fence_unref(&fence); + } + if (r) { + DRM_ERROR("Error while testing BO move.\n"); + } +} + +void radeon_test_moves(struct radeon_device *rdev) +{ + if (rdev->asic->copy.dma) + radeon_do_test_moves(rdev, RADEON_TEST_COPY_DMA); + if (rdev->asic->copy.blit) + radeon_do_test_moves(rdev, RADEON_TEST_COPY_BLIT); +} + +void radeon_test_ring_sync(struct radeon_device *rdev, + struct radeon_ring *ringA, + struct radeon_ring *ringB) +{ + struct radeon_fence *fence1 = NULL, *fence2 = NULL; + struct radeon_semaphore *semaphore = NULL; + int r; + + r = radeon_semaphore_create(rdev, &semaphore); + if (r) { + DRM_ERROR("Failed to create semaphore\n"); + goto out_cleanup; + } + + r = radeon_ring_lock(rdev, ringA, 64); + if (r) { + DRM_ERROR("Failed to lock ring A %d\n", ringA->idx); + goto out_cleanup; + } + radeon_semaphore_emit_wait(rdev, ringA->idx, semaphore); + r = radeon_fence_emit(rdev, &fence1, ringA->idx); + if (r) { + DRM_ERROR("Failed to emit fence 1\n"); + radeon_ring_unlock_undo(rdev, ringA); + goto out_cleanup; + } + radeon_semaphore_emit_wait(rdev, ringA->idx, semaphore); + r = radeon_fence_emit(rdev, &fence2, ringA->idx); + if (r) { + DRM_ERROR("Failed to emit fence 2\n"); + radeon_ring_unlock_undo(rdev, ringA); + goto out_cleanup; + } + radeon_ring_unlock_commit(rdev, ringA); + + DRM_MDELAY(1000); + + if (radeon_fence_signaled(fence1)) { + DRM_ERROR("Fence 1 signaled without waiting for semaphore.\n"); + goto out_cleanup; + } + + r = radeon_ring_lock(rdev, ringB, 64); + if (r) { + DRM_ERROR("Failed to lock ring B %p\n", ringB); + goto out_cleanup; + } + radeon_semaphore_emit_signal(rdev, ringB->idx, semaphore); + radeon_ring_unlock_commit(rdev, ringB); + + r = radeon_fence_wait(fence1, false); + if (r) { + DRM_ERROR("Failed to wait for sync fence 1\n"); + goto out_cleanup; + } + + DRM_MDELAY(1000); + + if (radeon_fence_signaled(fence2)) { + DRM_ERROR("Fence 2 signaled without waiting for semaphore.\n"); + goto out_cleanup; + } + + r = radeon_ring_lock(rdev, ringB, 64); + if (r) { + DRM_ERROR("Failed to lock ring B %p\n", ringB); + goto out_cleanup; + } + radeon_semaphore_emit_signal(rdev, ringB->idx, semaphore); + radeon_ring_unlock_commit(rdev, ringB); + + r = radeon_fence_wait(fence2, false); + if (r) { + DRM_ERROR("Failed to wait for sync fence 1\n"); + goto out_cleanup; + } + +out_cleanup: + radeon_semaphore_free(rdev, &semaphore, NULL); + + if (fence1) + radeon_fence_unref(&fence1); + + if (fence2) + radeon_fence_unref(&fence2); + + if (r) + DRM_ERROR("Error while testing ring sync (%d).\n", r); +} + +static void radeon_test_ring_sync2(struct radeon_device *rdev, + struct radeon_ring *ringA, + struct radeon_ring *ringB, + struct radeon_ring *ringC) +{ + struct radeon_fence *fenceA = NULL, *fenceB = NULL; + struct radeon_semaphore *semaphore = NULL; + bool sigA, sigB; + int i, r; + + r = radeon_semaphore_create(rdev, &semaphore); + if (r) { + DRM_ERROR("Failed to create semaphore\n"); + goto out_cleanup; + } + + r = radeon_ring_lock(rdev, ringA, 64); + if (r) { + DRM_ERROR("Failed to lock ring A %d\n", ringA->idx); + goto out_cleanup; + } + radeon_semaphore_emit_wait(rdev, ringA->idx, semaphore); + r = radeon_fence_emit(rdev, &fenceA, ringA->idx); + if (r) { + DRM_ERROR("Failed to emit sync fence 1\n"); + radeon_ring_unlock_undo(rdev, ringA); + goto out_cleanup; + } + radeon_ring_unlock_commit(rdev, ringA); + + r = radeon_ring_lock(rdev, ringB, 64); + if (r) { + DRM_ERROR("Failed to lock ring B %d\n", ringB->idx); + goto out_cleanup; + } + radeon_semaphore_emit_wait(rdev, ringB->idx, semaphore); + r = radeon_fence_emit(rdev, &fenceB, ringB->idx); + if (r) { + DRM_ERROR("Failed to create sync fence 2\n"); + radeon_ring_unlock_undo(rdev, ringB); + goto out_cleanup; + } + radeon_ring_unlock_commit(rdev, ringB); + + DRM_MDELAY(1000); + + if (radeon_fence_signaled(fenceA)) { + DRM_ERROR("Fence A signaled without waiting for semaphore.\n"); + goto out_cleanup; + } + if (radeon_fence_signaled(fenceB)) { + DRM_ERROR("Fence A signaled without waiting for semaphore.\n"); + goto out_cleanup; + } + + r = radeon_ring_lock(rdev, ringC, 64); + if (r) { + DRM_ERROR("Failed to lock ring B %p\n", ringC); + goto out_cleanup; + } + radeon_semaphore_emit_signal(rdev, ringC->idx, semaphore); + radeon_ring_unlock_commit(rdev, ringC); + + for (i = 0; i < 30; ++i) { + DRM_MDELAY(100); + sigA = radeon_fence_signaled(fenceA); + sigB = radeon_fence_signaled(fenceB); + if (sigA || sigB) + break; + } + + if (!sigA && !sigB) { + DRM_ERROR("Neither fence A nor B has been signaled\n"); + goto out_cleanup; + } else if (sigA && sigB) { + DRM_ERROR("Both fence A and B has been signaled\n"); + goto out_cleanup; + } + + DRM_INFO("Fence %c was first signaled\n", sigA ? 'A' : 'B'); + + r = radeon_ring_lock(rdev, ringC, 64); + if (r) { + DRM_ERROR("Failed to lock ring B %p\n", ringC); + goto out_cleanup; + } + radeon_semaphore_emit_signal(rdev, ringC->idx, semaphore); + radeon_ring_unlock_commit(rdev, ringC); + + DRM_MDELAY(1000); + + r = radeon_fence_wait(fenceA, false); + if (r) { + DRM_ERROR("Failed to wait for sync fence A\n"); + goto out_cleanup; + } + r = radeon_fence_wait(fenceB, false); + if (r) { + DRM_ERROR("Failed to wait for sync fence B\n"); + goto out_cleanup; + } + +out_cleanup: + radeon_semaphore_free(rdev, &semaphore, NULL); + + if (fenceA) + radeon_fence_unref(&fenceA); + + if (fenceB) + radeon_fence_unref(&fenceB); + + if (r) + DRM_ERROR("Error while testing ring sync (%d).\n", r); +} + +void radeon_test_syncing(struct radeon_device *rdev) +{ + int i, j, k; + + for (i = 1; i < RADEON_NUM_RINGS; ++i) { + struct radeon_ring *ringA = &rdev->ring[i]; + if (!ringA->ready) + continue; + + for (j = 0; j < i; ++j) { + struct radeon_ring *ringB = &rdev->ring[j]; + if (!ringB->ready) + continue; + + DRM_INFO("Testing syncing between rings %d and %d...\n", i, j); + radeon_test_ring_sync(rdev, ringA, ringB); + + DRM_INFO("Testing syncing between rings %d and %d...\n", j, i); + radeon_test_ring_sync(rdev, ringB, ringA); + + for (k = 0; k < j; ++k) { + struct radeon_ring *ringC = &rdev->ring[k]; + if (!ringC->ready) + continue; + + DRM_INFO("Testing syncing between rings %d, %d and %d...\n", i, j, k); + radeon_test_ring_sync2(rdev, ringA, ringB, ringC); + + DRM_INFO("Testing syncing between rings %d, %d and %d...\n", i, k, j); + radeon_test_ring_sync2(rdev, ringA, ringC, ringB); + + DRM_INFO("Testing syncing between rings %d, %d and %d...\n", j, i, k); + radeon_test_ring_sync2(rdev, ringB, ringA, ringC); + + DRM_INFO("Testing syncing between rings %d, %d and %d...\n", j, k, i); + radeon_test_ring_sync2(rdev, ringB, ringC, ringA); + + DRM_INFO("Testing syncing between rings %d, %d and %d...\n", k, i, j); + radeon_test_ring_sync2(rdev, ringC, ringA, ringB); + + DRM_INFO("Testing syncing between rings %d, %d and %d...\n", k, j, i); + radeon_test_ring_sync2(rdev, ringC, ringB, ringA); + } + } + } +} diff --git a/sys/dev/drm2/radeon/radeon_trace.h b/sys/dev/drm2/radeon/radeon_trace.h new file mode 100644 index 00000000000..e0fe7253317 --- /dev/null +++ b/sys/dev/drm2/radeon/radeon_trace.h @@ -0,0 +1,85 @@ +#include +__FBSDID("$FreeBSD$"); + +#if !defined(_RADEON_TRACE_H) || defined(TRACE_HEADER_MULTI_READ) +#define _RADEON_TRACE_H_ + +#include +#include +#include + +#include + +#undef TRACE_SYSTEM +#define TRACE_SYSTEM radeon +#define TRACE_SYSTEM_STRING __stringify(TRACE_SYSTEM) +#define TRACE_INCLUDE_FILE radeon_trace + +TRACE_EVENT(radeon_bo_create, + TP_PROTO(struct radeon_bo *bo), + TP_ARGS(bo), + TP_STRUCT__entry( + __field(struct radeon_bo *, bo) + __field(u32, pages) + ), + + TP_fast_assign( + __entry->bo = bo; + __entry->pages = bo->tbo.num_pages; + ), + TP_printk("bo=%p, pages=%u", __entry->bo, __entry->pages) +); + +DECLARE_EVENT_CLASS(radeon_fence_request, + + TP_PROTO(struct drm_device *dev, u32 seqno), + + TP_ARGS(dev, seqno), + + TP_STRUCT__entry( + __field(u32, dev) + __field(u32, seqno) + ), + + TP_fast_assign( + __entry->dev = dev->primary->index; + __entry->seqno = seqno; + ), + + TP_printk("dev=%u, seqno=%u", __entry->dev, __entry->seqno) +); + +DEFINE_EVENT(radeon_fence_request, radeon_fence_emit, + + TP_PROTO(struct drm_device *dev, u32 seqno), + + TP_ARGS(dev, seqno) +); + +DEFINE_EVENT(radeon_fence_request, radeon_fence_retire, + + TP_PROTO(struct drm_device *dev, u32 seqno), + + TP_ARGS(dev, seqno) +); + +DEFINE_EVENT(radeon_fence_request, radeon_fence_wait_begin, + + TP_PROTO(struct drm_device *dev, u32 seqno), + + TP_ARGS(dev, seqno) +); + +DEFINE_EVENT(radeon_fence_request, radeon_fence_wait_end, + + TP_PROTO(struct drm_device *dev, u32 seqno), + + TP_ARGS(dev, seqno) +); + +#endif + +/* This part must be outside protection */ +#undef TRACE_INCLUDE_PATH +#define TRACE_INCLUDE_PATH . +#include diff --git a/sys/dev/drm2/radeon/radeon_trace_points.c b/sys/dev/drm2/radeon/radeon_trace_points.c new file mode 100644 index 00000000000..5602c946d23 --- /dev/null +++ b/sys/dev/drm2/radeon/radeon_trace_points.c @@ -0,0 +1,12 @@ +/* Copyright Red Hat Inc 2010. + * Author : Dave Airlie + */ +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include "radeon.h" + +#define CREATE_TRACE_POINTS +#include "radeon_trace.h" diff --git a/sys/dev/drm2/radeon/radeon_ttm.c b/sys/dev/drm2/radeon/radeon_ttm.c new file mode 100644 index 00000000000..22c0935b84c --- /dev/null +++ b/sys/dev/drm2/radeon/radeon_ttm.c @@ -0,0 +1,931 @@ +/* + * Copyright 2009 Jerome Glisse. + * All Rights Reserved. + * + * 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, sub license, 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 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 NON-INFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS 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. + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + */ +/* + * Authors: + * Jerome Glisse + * Thomas Hellstrom + * Dave Airlie + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include "radeon_reg.h" +#include "radeon.h" + +#define DRM_FILE_PAGE_OFFSET (0x100000000ULL >> PAGE_SHIFT) + +static int radeon_ttm_debugfs_init(struct radeon_device *rdev); + +static struct radeon_device *radeon_get_rdev(struct ttm_bo_device *bdev) +{ + struct radeon_mman *mman; + struct radeon_device *rdev; + + mman = container_of(bdev, struct radeon_mman, bdev); + rdev = container_of(mman, struct radeon_device, mman); + return rdev; +} + + +/* + * Global memory. + */ +static int radeon_ttm_mem_global_init(struct drm_global_reference *ref) +{ + return ttm_mem_global_init(ref->object); +} + +static void radeon_ttm_mem_global_release(struct drm_global_reference *ref) +{ + ttm_mem_global_release(ref->object); +} + +static int radeon_ttm_global_init(struct radeon_device *rdev) +{ + struct drm_global_reference *global_ref; + int r; + + rdev->mman.mem_global_referenced = false; + global_ref = &rdev->mman.mem_global_ref; + global_ref->global_type = DRM_GLOBAL_TTM_MEM; + global_ref->size = sizeof(struct ttm_mem_global); + global_ref->init = &radeon_ttm_mem_global_init; + global_ref->release = &radeon_ttm_mem_global_release; + r = drm_global_item_ref(global_ref); + if (r != 0) { + DRM_ERROR("Failed setting up TTM memory accounting " + "subsystem.\n"); + return r; + } + + rdev->mman.bo_global_ref.mem_glob = + rdev->mman.mem_global_ref.object; + global_ref = &rdev->mman.bo_global_ref.ref; + global_ref->global_type = DRM_GLOBAL_TTM_BO; + global_ref->size = sizeof(struct ttm_bo_global); + global_ref->init = &ttm_bo_global_init; + global_ref->release = &ttm_bo_global_release; + r = drm_global_item_ref(global_ref); + if (r != 0) { + DRM_ERROR("Failed setting up TTM BO subsystem.\n"); + drm_global_item_unref(&rdev->mman.mem_global_ref); + return r; + } + + rdev->mman.mem_global_referenced = true; + return 0; +} + +static void radeon_ttm_global_fini(struct radeon_device *rdev) +{ + if (rdev->mman.mem_global_referenced) { + drm_global_item_unref(&rdev->mman.bo_global_ref.ref); + drm_global_item_unref(&rdev->mman.mem_global_ref); + rdev->mman.mem_global_referenced = false; + } +} + +static int radeon_invalidate_caches(struct ttm_bo_device *bdev, uint32_t flags) +{ + return 0; +} + +static int radeon_init_mem_type(struct ttm_bo_device *bdev, uint32_t type, + struct ttm_mem_type_manager *man) +{ + struct radeon_device *rdev; + + rdev = radeon_get_rdev(bdev); + + switch (type) { + case TTM_PL_SYSTEM: + /* System memory */ + man->flags = TTM_MEMTYPE_FLAG_MAPPABLE; + man->available_caching = TTM_PL_MASK_CACHING; + man->default_caching = TTM_PL_FLAG_CACHED; + break; + case TTM_PL_TT: + man->func = &ttm_bo_manager_func; + man->gpu_offset = rdev->mc.gtt_start; + man->available_caching = TTM_PL_MASK_CACHING; + man->default_caching = TTM_PL_FLAG_CACHED; + man->flags = TTM_MEMTYPE_FLAG_MAPPABLE | TTM_MEMTYPE_FLAG_CMA; +#if __OS_HAS_AGP + if (rdev->flags & RADEON_IS_AGP) { + if (!(drm_core_has_AGP(rdev->ddev) && rdev->ddev->agp)) { + DRM_ERROR("AGP is not enabled for memory type %u\n", + (unsigned)type); + return -EINVAL; + } + if (!rdev->ddev->agp->cant_use_aperture) + man->flags = TTM_MEMTYPE_FLAG_MAPPABLE; + man->available_caching = TTM_PL_FLAG_UNCACHED | + TTM_PL_FLAG_WC; + man->default_caching = TTM_PL_FLAG_WC; + } +#endif + break; + case TTM_PL_VRAM: + /* "On-card" video ram */ + man->func = &ttm_bo_manager_func; + man->gpu_offset = rdev->mc.vram_start; + man->flags = TTM_MEMTYPE_FLAG_FIXED | + TTM_MEMTYPE_FLAG_MAPPABLE; + man->available_caching = TTM_PL_FLAG_UNCACHED | TTM_PL_FLAG_WC; + man->default_caching = TTM_PL_FLAG_WC; + break; + default: + DRM_ERROR("Unsupported memory type %u\n", (unsigned)type); + return -EINVAL; + } + return 0; +} + +static void radeon_evict_flags(struct ttm_buffer_object *bo, + struct ttm_placement *placement) +{ + struct radeon_bo *rbo; + static u32 placements = TTM_PL_MASK_CACHING | TTM_PL_FLAG_SYSTEM; + + if (!radeon_ttm_bo_is_radeon_bo(bo)) { + placement->fpfn = 0; + placement->lpfn = 0; + placement->placement = &placements; + placement->busy_placement = &placements; + placement->num_placement = 1; + placement->num_busy_placement = 1; + return; + } + rbo = container_of(bo, struct radeon_bo, tbo); + switch (bo->mem.mem_type) { + case TTM_PL_VRAM: + if (rbo->rdev->ring[RADEON_RING_TYPE_GFX_INDEX].ready == false) + radeon_ttm_placement_from_domain(rbo, RADEON_GEM_DOMAIN_CPU); + else + radeon_ttm_placement_from_domain(rbo, RADEON_GEM_DOMAIN_GTT); + break; + case TTM_PL_TT: + default: + radeon_ttm_placement_from_domain(rbo, RADEON_GEM_DOMAIN_CPU); + } + *placement = rbo->placement; +} + +static int radeon_verify_access(struct ttm_buffer_object *bo) +{ + return 0; +} + +static void radeon_move_null(struct ttm_buffer_object *bo, + struct ttm_mem_reg *new_mem) +{ + struct ttm_mem_reg *old_mem = &bo->mem; + + KASSERT(old_mem->mm_node == NULL, ("old_mem->mm_node != NULL")); + *old_mem = *new_mem; + new_mem->mm_node = NULL; +} + +static int radeon_move_blit(struct ttm_buffer_object *bo, + bool evict, bool no_wait_gpu, + struct ttm_mem_reg *new_mem, + struct ttm_mem_reg *old_mem) +{ + struct radeon_device *rdev; + uint64_t old_start, new_start; + struct radeon_fence *fence; + int r, ridx; + + rdev = radeon_get_rdev(bo->bdev); + ridx = radeon_copy_ring_index(rdev); + old_start = old_mem->start << PAGE_SHIFT; + new_start = new_mem->start << PAGE_SHIFT; + + switch (old_mem->mem_type) { + case TTM_PL_VRAM: + old_start += rdev->mc.vram_start; + break; + case TTM_PL_TT: + old_start += rdev->mc.gtt_start; + break; + default: + DRM_ERROR("Unknown placement %d\n", old_mem->mem_type); + return -EINVAL; + } + switch (new_mem->mem_type) { + case TTM_PL_VRAM: + new_start += rdev->mc.vram_start; + break; + case TTM_PL_TT: + new_start += rdev->mc.gtt_start; + break; + default: + DRM_ERROR("Unknown placement %d\n", old_mem->mem_type); + return -EINVAL; + } + if (!rdev->ring[ridx].ready) { + DRM_ERROR("Trying to move memory with ring turned off.\n"); + return -EINVAL; + } + + CTASSERT((PAGE_SIZE % RADEON_GPU_PAGE_SIZE) == 0); + + /* sync other rings */ + fence = bo->sync_obj; + r = radeon_copy(rdev, old_start, new_start, + new_mem->num_pages * (PAGE_SIZE / RADEON_GPU_PAGE_SIZE), /* GPU pages */ + &fence); + /* FIXME: handle copy error */ + r = ttm_bo_move_accel_cleanup(bo, (void *)fence, + evict, no_wait_gpu, new_mem); + radeon_fence_unref(&fence); + return r; +} + +static int radeon_move_vram_ram(struct ttm_buffer_object *bo, + bool evict, bool interruptible, + bool no_wait_gpu, + struct ttm_mem_reg *new_mem) +{ + struct radeon_device *rdev; + struct ttm_mem_reg *old_mem = &bo->mem; + struct ttm_mem_reg tmp_mem; + u32 placements; + struct ttm_placement placement; + int r; + + rdev = radeon_get_rdev(bo->bdev); + tmp_mem = *new_mem; + tmp_mem.mm_node = NULL; + placement.fpfn = 0; + placement.lpfn = 0; + placement.num_placement = 1; + placement.placement = &placements; + placement.num_busy_placement = 1; + placement.busy_placement = &placements; + placements = TTM_PL_MASK_CACHING | TTM_PL_FLAG_TT; + r = ttm_bo_mem_space(bo, &placement, &tmp_mem, + interruptible, no_wait_gpu); + if (unlikely(r)) { + return r; + } + + r = ttm_tt_set_placement_caching(bo->ttm, tmp_mem.placement); + if (unlikely(r)) { + goto out_cleanup; + } + + r = ttm_tt_bind(bo->ttm, &tmp_mem); + if (unlikely(r)) { + goto out_cleanup; + } + r = radeon_move_blit(bo, true, no_wait_gpu, &tmp_mem, old_mem); + if (unlikely(r)) { + goto out_cleanup; + } + r = ttm_bo_move_ttm(bo, true, no_wait_gpu, new_mem); +out_cleanup: + ttm_bo_mem_put(bo, &tmp_mem); + return r; +} + +static int radeon_move_ram_vram(struct ttm_buffer_object *bo, + bool evict, bool interruptible, + bool no_wait_gpu, + struct ttm_mem_reg *new_mem) +{ + struct radeon_device *rdev; + struct ttm_mem_reg *old_mem = &bo->mem; + struct ttm_mem_reg tmp_mem; + struct ttm_placement placement; + u32 placements; + int r; + + rdev = radeon_get_rdev(bo->bdev); + tmp_mem = *new_mem; + tmp_mem.mm_node = NULL; + placement.fpfn = 0; + placement.lpfn = 0; + placement.num_placement = 1; + placement.placement = &placements; + placement.num_busy_placement = 1; + placement.busy_placement = &placements; + placements = TTM_PL_MASK_CACHING | TTM_PL_FLAG_TT; + r = ttm_bo_mem_space(bo, &placement, &tmp_mem, + interruptible, no_wait_gpu); + if (unlikely(r)) { + return r; + } + r = ttm_bo_move_ttm(bo, true, no_wait_gpu, &tmp_mem); + if (unlikely(r)) { + goto out_cleanup; + } + r = radeon_move_blit(bo, true, no_wait_gpu, new_mem, old_mem); + if (unlikely(r)) { + goto out_cleanup; + } +out_cleanup: + ttm_bo_mem_put(bo, &tmp_mem); + return r; +} + +static int radeon_bo_move(struct ttm_buffer_object *bo, + bool evict, bool interruptible, + bool no_wait_gpu, + struct ttm_mem_reg *new_mem) +{ + struct radeon_device *rdev; + struct ttm_mem_reg *old_mem = &bo->mem; + int r; + + rdev = radeon_get_rdev(bo->bdev); + if (old_mem->mem_type == TTM_PL_SYSTEM && bo->ttm == NULL) { + radeon_move_null(bo, new_mem); + return 0; + } + if ((old_mem->mem_type == TTM_PL_TT && + new_mem->mem_type == TTM_PL_SYSTEM) || + (old_mem->mem_type == TTM_PL_SYSTEM && + new_mem->mem_type == TTM_PL_TT)) { + /* bind is enough */ + radeon_move_null(bo, new_mem); + return 0; + } + if (!rdev->ring[radeon_copy_ring_index(rdev)].ready || + rdev->asic->copy.copy == NULL) { + /* use memcpy */ + goto memcpy; + } + + if (old_mem->mem_type == TTM_PL_VRAM && + new_mem->mem_type == TTM_PL_SYSTEM) { + r = radeon_move_vram_ram(bo, evict, interruptible, + no_wait_gpu, new_mem); + } else if (old_mem->mem_type == TTM_PL_SYSTEM && + new_mem->mem_type == TTM_PL_VRAM) { + r = radeon_move_ram_vram(bo, evict, interruptible, + no_wait_gpu, new_mem); + } else { + r = radeon_move_blit(bo, evict, no_wait_gpu, new_mem, old_mem); + } + + if (r) { +memcpy: + r = ttm_bo_move_memcpy(bo, evict, no_wait_gpu, new_mem); + } + return r; +} + +static int radeon_ttm_io_mem_reserve(struct ttm_bo_device *bdev, struct ttm_mem_reg *mem) +{ + struct ttm_mem_type_manager *man = &bdev->man[mem->mem_type]; + struct radeon_device *rdev = radeon_get_rdev(bdev); + + mem->bus.addr = NULL; + mem->bus.offset = 0; + mem->bus.size = mem->num_pages << PAGE_SHIFT; + mem->bus.base = 0; + mem->bus.is_iomem = false; + if (!(man->flags & TTM_MEMTYPE_FLAG_MAPPABLE)) + return -EINVAL; + switch (mem->mem_type) { + case TTM_PL_SYSTEM: + /* system memory */ + return 0; + case TTM_PL_TT: +#if __OS_HAS_AGP + if (rdev->flags & RADEON_IS_AGP) { + /* RADEON_IS_AGP is set only if AGP is active */ + mem->bus.offset = mem->start << PAGE_SHIFT; + mem->bus.base = rdev->mc.agp_base; + mem->bus.is_iomem = !rdev->ddev->agp->cant_use_aperture; + } +#endif + break; + case TTM_PL_VRAM: + mem->bus.offset = mem->start << PAGE_SHIFT; + /* check if it's visible */ + if ((mem->bus.offset + mem->bus.size) > rdev->mc.visible_vram_size) + return -EINVAL; + mem->bus.base = rdev->mc.aper_base; + mem->bus.is_iomem = true; +#ifdef __alpha__ + /* + * Alpha: use bus.addr to hold the ioremap() return, + * so we can modify bus.base below. + */ + if (mem->placement & TTM_PL_FLAG_WC) + mem->bus.addr = + ioremap_wc(mem->bus.base + mem->bus.offset, + mem->bus.size); + else + mem->bus.addr = + ioremap_nocache(mem->bus.base + mem->bus.offset, + mem->bus.size); + + /* + * Alpha: Use just the bus offset plus + * the hose/domain memory base for bus.base. + * It then can be used to build PTEs for VRAM + * access, as done in ttm_bo_vm_fault(). + */ + mem->bus.base = (mem->bus.base & 0x0ffffffffUL) + + rdev->ddev->hose->dense_mem_base; +#endif + break; + default: + return -EINVAL; + } + return 0; +} + +static void radeon_ttm_io_mem_free(struct ttm_bo_device *bdev, struct ttm_mem_reg *mem) +{ +} + +static int radeon_sync_obj_wait(void *sync_obj, bool lazy, bool interruptible) +{ + return radeon_fence_wait((struct radeon_fence *)sync_obj, interruptible); +} + +static int radeon_sync_obj_flush(void *sync_obj) +{ + return 0; +} + +static void radeon_sync_obj_unref(void **sync_obj) +{ + radeon_fence_unref((struct radeon_fence **)sync_obj); +} + +static void *radeon_sync_obj_ref(void *sync_obj) +{ + return radeon_fence_ref((struct radeon_fence *)sync_obj); +} + +static bool radeon_sync_obj_signaled(void *sync_obj) +{ + return radeon_fence_signaled((struct radeon_fence *)sync_obj); +} + +/* + * TTM backend functions. + */ +struct radeon_ttm_tt { + struct ttm_dma_tt ttm; + struct radeon_device *rdev; + u64 offset; +}; + +static int radeon_ttm_backend_bind(struct ttm_tt *ttm, + struct ttm_mem_reg *bo_mem) +{ + struct radeon_ttm_tt *gtt = (void*)ttm; + int r; + + gtt->offset = (unsigned long)(bo_mem->start << PAGE_SHIFT); + if (!ttm->num_pages) { + DRM_ERROR("nothing to bind %lu pages for mreg %p back %p!\n", + ttm->num_pages, bo_mem, ttm); + } + r = radeon_gart_bind(gtt->rdev, gtt->offset, + ttm->num_pages, ttm->pages, gtt->ttm.dma_address); + if (r) { + DRM_ERROR("failed to bind %lu pages at 0x%08X\n", + ttm->num_pages, (unsigned)gtt->offset); + return r; + } + return 0; +} + +static int radeon_ttm_backend_unbind(struct ttm_tt *ttm) +{ + struct radeon_ttm_tt *gtt = (void *)ttm; + + radeon_gart_unbind(gtt->rdev, gtt->offset, ttm->num_pages); + return 0; +} + +static void radeon_ttm_backend_destroy(struct ttm_tt *ttm) +{ + struct radeon_ttm_tt *gtt = (void *)ttm; + + ttm_dma_tt_fini(>t->ttm); + free(gtt, DRM_MEM_DRIVER); +} + +static struct ttm_backend_func radeon_backend_func = { + .bind = &radeon_ttm_backend_bind, + .unbind = &radeon_ttm_backend_unbind, + .destroy = &radeon_ttm_backend_destroy, +}; + +static struct ttm_tt *radeon_ttm_tt_create(struct ttm_bo_device *bdev, + unsigned long size, uint32_t page_flags, + vm_page_t dummy_read_page) +{ + struct radeon_device *rdev; + struct radeon_ttm_tt *gtt; + + rdev = radeon_get_rdev(bdev); +#if __OS_HAS_AGP +#ifdef DUMBBELL_WIP + if (rdev->flags & RADEON_IS_AGP) { + return ttm_agp_tt_create(bdev, rdev->ddev->agp->agpdev, + size, page_flags, dummy_read_page); + } +#endif /* DUMBBELL_WIP */ +#endif + + gtt = malloc(sizeof(struct radeon_ttm_tt), + DRM_MEM_DRIVER, M_WAITOK | M_ZERO); + if (gtt == NULL) { + return NULL; + } + gtt->ttm.ttm.func = &radeon_backend_func; + gtt->rdev = rdev; + if (ttm_dma_tt_init(>t->ttm, bdev, size, page_flags, dummy_read_page)) { + free(gtt, DRM_MEM_DRIVER); + return NULL; + } + return >t->ttm.ttm; +} + +static int radeon_ttm_tt_populate(struct ttm_tt *ttm) +{ + struct radeon_device *rdev; + struct radeon_ttm_tt *gtt = (void *)ttm; + unsigned i; + int r; +#ifdef DUMBBELL_WIP + bool slave = !!(ttm->page_flags & TTM_PAGE_FLAG_SG); +#endif /* DUMBBELL_WIP */ + + if (ttm->state != tt_unpopulated) + return 0; + +#ifdef DUMBBELL_WIP + /* + * Maybe unneeded on FreeBSD. + * -- dumbbell@ + */ + if (slave && ttm->sg) { + drm_prime_sg_to_page_addr_arrays(ttm->sg, ttm->pages, + gtt->ttm.dma_address, ttm->num_pages); + ttm->state = tt_unbound; + return 0; + } +#endif /* DUMBBELL_WIP */ + + rdev = radeon_get_rdev(ttm->bdev); +#if __OS_HAS_AGP +#ifdef DUMBBELL_WIP + if (rdev->flags & RADEON_IS_AGP) { + return ttm_agp_tt_populate(ttm); + } +#endif /* DUMBBELL_WIP */ +#endif + +#ifdef CONFIG_SWIOTLB + if (swiotlb_nr_tbl()) { + return ttm_dma_populate(>t->ttm, rdev->dev); + } +#endif + + r = ttm_pool_populate(ttm); + if (r) { + return r; + } + + for (i = 0; i < ttm->num_pages; i++) { + gtt->ttm.dma_address[i] = VM_PAGE_TO_PHYS(ttm->pages[i]); +#ifdef DUMBBELL_WIP + gtt->ttm.dma_address[i] = pci_map_page(rdev->pdev, ttm->pages[i], + 0, PAGE_SIZE, + PCI_DMA_BIDIRECTIONAL); + if (pci_dma_mapping_error(rdev->pdev, gtt->ttm.dma_address[i])) { + while (--i) { + pci_unmap_page(rdev->pdev, gtt->ttm.dma_address[i], + PAGE_SIZE, PCI_DMA_BIDIRECTIONAL); + gtt->ttm.dma_address[i] = 0; + } + ttm_pool_unpopulate(ttm); + return -EFAULT; + } +#endif /* DUMBBELL_WIP */ + } + return 0; +} + +static void radeon_ttm_tt_unpopulate(struct ttm_tt *ttm) +{ + struct radeon_device *rdev; + struct radeon_ttm_tt *gtt = (void *)ttm; + unsigned i; + bool slave = !!(ttm->page_flags & TTM_PAGE_FLAG_SG); + + if (slave) + return; + + rdev = radeon_get_rdev(ttm->bdev); +#if __OS_HAS_AGP +#ifdef DUMBBELL_WIP + if (rdev->flags & RADEON_IS_AGP) { + ttm_agp_tt_unpopulate(ttm); + return; + } +#endif /* DUMBBELL_WIP */ +#endif + +#ifdef CONFIG_SWIOTLB + if (swiotlb_nr_tbl()) { + ttm_dma_unpopulate(>t->ttm, rdev->dev); + return; + } +#endif + + for (i = 0; i < ttm->num_pages; i++) { + if (gtt->ttm.dma_address[i]) { + gtt->ttm.dma_address[i] = 0; +#ifdef DUMBBELL_WIP + pci_unmap_page(rdev->pdev, gtt->ttm.dma_address[i], + PAGE_SIZE, PCI_DMA_BIDIRECTIONAL); +#endif /* DUMBBELL_WIP */ + } + } + + ttm_pool_unpopulate(ttm); +} + +static struct ttm_bo_driver radeon_bo_driver = { + .ttm_tt_create = &radeon_ttm_tt_create, + .ttm_tt_populate = &radeon_ttm_tt_populate, + .ttm_tt_unpopulate = &radeon_ttm_tt_unpopulate, + .invalidate_caches = &radeon_invalidate_caches, + .init_mem_type = &radeon_init_mem_type, + .evict_flags = &radeon_evict_flags, + .move = &radeon_bo_move, + .verify_access = &radeon_verify_access, + .sync_obj_signaled = &radeon_sync_obj_signaled, + .sync_obj_wait = &radeon_sync_obj_wait, + .sync_obj_flush = &radeon_sync_obj_flush, + .sync_obj_unref = &radeon_sync_obj_unref, + .sync_obj_ref = &radeon_sync_obj_ref, + .move_notify = &radeon_bo_move_notify, + .fault_reserve_notify = &radeon_bo_fault_reserve_notify, + .io_mem_reserve = &radeon_ttm_io_mem_reserve, + .io_mem_free = &radeon_ttm_io_mem_free, +}; + +int radeon_ttm_init(struct radeon_device *rdev) +{ + int r, r2; + + r = radeon_ttm_global_init(rdev); + if (r) { + return r; + } + /* No others user of address space so set it to 0 */ + r = ttm_bo_device_init(&rdev->mman.bdev, + rdev->mman.bo_global_ref.ref.object, + &radeon_bo_driver, DRM_FILE_PAGE_OFFSET, + rdev->need_dma32); + if (r) { + DRM_ERROR("failed initializing buffer object driver(%d).\n", r); + return r; + } + rdev->mman.initialized = true; + rdev->ddev->drm_ttm_bdev = &rdev->mman.bdev; + r = ttm_bo_init_mm(&rdev->mman.bdev, TTM_PL_VRAM, + rdev->mc.real_vram_size >> PAGE_SHIFT); + if (r) { + DRM_ERROR("Failed initializing VRAM heap.\n"); + return r; + } + r = radeon_bo_create(rdev, 256 * 1024, PAGE_SIZE, true, + RADEON_GEM_DOMAIN_VRAM, + NULL, &rdev->stollen_vga_memory); + if (r) { + return r; + } + r = radeon_bo_reserve(rdev->stollen_vga_memory, false); + if (r) { + radeon_bo_unref(&rdev->stollen_vga_memory); + return r; + } + r = radeon_bo_pin(rdev->stollen_vga_memory, RADEON_GEM_DOMAIN_VRAM, NULL); + radeon_bo_unreserve(rdev->stollen_vga_memory); + if (r) { + radeon_bo_unref(&rdev->stollen_vga_memory); + return r; + } + DRM_INFO("radeon: %uM of VRAM memory ready\n", + (unsigned)rdev->mc.real_vram_size / (1024 * 1024)); + r = ttm_bo_init_mm(&rdev->mman.bdev, TTM_PL_TT, + rdev->mc.gtt_size >> PAGE_SHIFT); + if (r) { + DRM_ERROR("Failed initializing GTT heap.\n"); + r2 = radeon_bo_reserve(rdev->stollen_vga_memory, false); + if (likely(r2 == 0)) { + radeon_bo_unpin(rdev->stollen_vga_memory); + radeon_bo_unreserve(rdev->stollen_vga_memory); + } + radeon_bo_unref(&rdev->stollen_vga_memory); + return r; + } + DRM_INFO("radeon: %uM of GTT memory ready.\n", + (unsigned)(rdev->mc.gtt_size / (1024 * 1024))); + + r = radeon_ttm_debugfs_init(rdev); + if (r) { + DRM_ERROR("Failed to init debugfs\n"); + r2 = radeon_bo_reserve(rdev->stollen_vga_memory, false); + if (likely(r2 == 0)) { + radeon_bo_unpin(rdev->stollen_vga_memory); + radeon_bo_unreserve(rdev->stollen_vga_memory); + } + radeon_bo_unref(&rdev->stollen_vga_memory); + return r; + } + return 0; +} + +void radeon_ttm_fini(struct radeon_device *rdev) +{ + int r; + + if (!rdev->mman.initialized) + return; + if (rdev->stollen_vga_memory) { + r = radeon_bo_reserve(rdev->stollen_vga_memory, false); + if (r == 0) { + radeon_bo_unpin(rdev->stollen_vga_memory); + radeon_bo_unreserve(rdev->stollen_vga_memory); + } + radeon_bo_unref(&rdev->stollen_vga_memory); + } + ttm_bo_clean_mm(&rdev->mman.bdev, TTM_PL_VRAM); + ttm_bo_clean_mm(&rdev->mman.bdev, TTM_PL_TT); + ttm_bo_device_release(&rdev->mman.bdev); + radeon_gart_fini(rdev); + radeon_ttm_global_fini(rdev); + rdev->mman.initialized = false; + DRM_INFO("radeon: ttm finalized\n"); +} + +/* this should only be called at bootup or when userspace + * isn't running */ +void radeon_ttm_set_active_vram_size(struct radeon_device *rdev, u64 size) +{ + struct ttm_mem_type_manager *man; + + if (!rdev->mman.initialized) + return; + + man = &rdev->mman.bdev.man[TTM_PL_VRAM]; + /* this just adjusts TTM size idea, which sets lpfn to the correct value */ + man->size = size >> PAGE_SHIFT; +} + +#ifdef DUMBBELL_WIP +static struct vm_operations_struct radeon_ttm_vm_ops; +static const struct vm_operations_struct *ttm_vm_ops = NULL; + +static int radeon_ttm_fault(struct vm_area_struct *vma, struct vm_fault *vmf) +{ + struct ttm_buffer_object *bo; + struct radeon_device *rdev; + int r; + + bo = (struct ttm_buffer_object *)vma->vm_private_data; + if (bo == NULL) { + return VM_FAULT_NOPAGE; + } + rdev = radeon_get_rdev(bo->bdev); + sx_slock(&rdev->pm.mclk_lock); + r = ttm_vm_ops->fault(vma, vmf); + sx_sunlock(&rdev->pm.mclk_lock); + return r; +} + +int radeon_mmap(struct file *filp, struct vm_area_struct *vma) +{ + struct drm_file *file_priv; + struct radeon_device *rdev; + int r; + + if (unlikely(vma->vm_pgoff < DRM_FILE_PAGE_OFFSET)) { + return drm_mmap(filp, vma); + } + + file_priv = filp->private_data; + rdev = file_priv->minor->dev->dev_private; + if (rdev == NULL) { + return -EINVAL; + } + r = ttm_bo_mmap(filp, vma, &rdev->mman.bdev); + if (unlikely(r != 0)) { + return r; + } + if (unlikely(ttm_vm_ops == NULL)) { + ttm_vm_ops = vma->vm_ops; + radeon_ttm_vm_ops = *ttm_vm_ops; + radeon_ttm_vm_ops.fault = &radeon_ttm_fault; + } + vma->vm_ops = &radeon_ttm_vm_ops; + return 0; +} +#endif /* DUMBBELL_WIP */ + + +#define RADEON_DEBUGFS_MEM_TYPES 2 + +#if defined(CONFIG_DEBUG_FS) +static int radeon_mm_dump_table(struct seq_file *m, void *data) +{ + struct drm_info_node *node = (struct drm_info_node *)m->private; + struct drm_mm *mm = (struct drm_mm *)node->info_ent->data; + struct drm_device *dev = node->minor->dev; + struct radeon_device *rdev = dev->dev_private; + int ret; + struct ttm_bo_global *glob = rdev->mman.bdev.glob; + + spin_lock(&glob->lru_lock); + ret = drm_mm_dump_table(m, mm); + spin_unlock(&glob->lru_lock); + return ret; +} +#endif + +static int radeon_ttm_debugfs_init(struct radeon_device *rdev) +{ +#if defined(CONFIG_DEBUG_FS) + static struct drm_info_list radeon_mem_types_list[RADEON_DEBUGFS_MEM_TYPES+2]; + static char radeon_mem_types_names[RADEON_DEBUGFS_MEM_TYPES+2][32]; + unsigned i; + + for (i = 0; i < RADEON_DEBUGFS_MEM_TYPES; i++) { + if (i == 0) + sprintf(radeon_mem_types_names[i], "radeon_vram_mm"); + else + sprintf(radeon_mem_types_names[i], "radeon_gtt_mm"); + radeon_mem_types_list[i].name = radeon_mem_types_names[i]; + radeon_mem_types_list[i].show = &radeon_mm_dump_table; + radeon_mem_types_list[i].driver_features = 0; + if (i == 0) + radeon_mem_types_list[i].data = rdev->mman.bdev.man[TTM_PL_VRAM].priv; + else + radeon_mem_types_list[i].data = rdev->mman.bdev.man[TTM_PL_TT].priv; + + } + /* Add ttm page pool to debugfs */ + sprintf(radeon_mem_types_names[i], "ttm_page_pool"); + radeon_mem_types_list[i].name = radeon_mem_types_names[i]; + radeon_mem_types_list[i].show = &ttm_page_alloc_debugfs; + radeon_mem_types_list[i].driver_features = 0; + radeon_mem_types_list[i++].data = NULL; +#ifdef CONFIG_SWIOTLB + if (swiotlb_nr_tbl()) { + sprintf(radeon_mem_types_names[i], "ttm_dma_page_pool"); + radeon_mem_types_list[i].name = radeon_mem_types_names[i]; + radeon_mem_types_list[i].show = &ttm_dma_page_alloc_debugfs; + radeon_mem_types_list[i].driver_features = 0; + radeon_mem_types_list[i++].data = NULL; + } +#endif + return radeon_debugfs_add_files(rdev, radeon_mem_types_list, i); + +#endif + return 0; +} diff --git a/sys/dev/drm2/radeon/reg_srcs/cayman b/sys/dev/drm2/radeon/reg_srcs/cayman new file mode 100644 index 00000000000..a072fa8c46b --- /dev/null +++ b/sys/dev/drm2/radeon/reg_srcs/cayman @@ -0,0 +1,642 @@ +cayman 0x9400 +0x0000802C GRBM_GFX_INDEX +0x00008040 WAIT_UNTIL +0x000084FC CP_STRMOUT_CNTL +0x000085F0 CP_COHER_CNTL +0x000085F4 CP_COHER_SIZE +0x000088B0 VGT_VTX_VECT_EJECT_REG +0x000088C4 VGT_CACHE_INVALIDATION +0x000088D4 VGT_GS_VERTEX_REUSE +0x00008958 VGT_PRIMITIVE_TYPE +0x0000895C VGT_INDEX_TYPE +0x00008970 VGT_NUM_INDICES +0x00008974 VGT_NUM_INSTANCES +0x00008990 VGT_COMPUTE_DIM_X +0x00008994 VGT_COMPUTE_DIM_Y +0x00008998 VGT_COMPUTE_DIM_Z +0x0000899C VGT_COMPUTE_START_X +0x000089A0 VGT_COMPUTE_START_Y +0x000089A4 VGT_COMPUTE_START_Z +0x000089A8 VGT_COMPUTE_INDEX +0x000089AC VGT_COMPUTE_THREAD_GOURP_SIZE +0x000089B0 VGT_HS_OFFCHIP_PARAM +0x00008A14 PA_CL_ENHANCE +0x00008A60 PA_SC_LINE_STIPPLE_VALUE +0x00008B10 PA_SC_LINE_STIPPLE_STATE +0x00008BF0 PA_SC_ENHANCE +0x00008D8C SQ_DYN_GPR_CNTL_PS_FLUSH_REQ +0x00008D94 SQ_DYN_GPR_SIMD_LOCK_EN +0x00008C00 SQ_CONFIG +0x00008C04 SQ_GPR_RESOURCE_MGMT_1 +0x00008C10 SQ_GLOBAL_GPR_RESOURCE_MGMT_1 +0x00008C14 SQ_GLOBAL_GPR_RESOURCE_MGMT_2 +0x00008DF8 SQ_CONST_MEM_BASE +0x00008E20 SQ_STATIC_THREAD_MGMT_1 +0x00008E24 SQ_STATIC_THREAD_MGMT_2 +0x00008E28 SQ_STATIC_THREAD_MGMT_3 +0x00008E48 SQ_EX_ALLOC_TABLE_SLOTS +0x00009100 SPI_CONFIG_CNTL +0x0000913C SPI_CONFIG_CNTL_1 +0x00009508 TA_CNTL_AUX +0x00009830 DB_DEBUG +0x00009834 DB_DEBUG2 +0x00009838 DB_DEBUG3 +0x0000983C DB_DEBUG4 +0x00009854 DB_WATERMARKS +0x0000A400 TD_PS_BORDER_COLOR_INDEX +0x0000A404 TD_PS_BORDER_COLOR_RED +0x0000A408 TD_PS_BORDER_COLOR_GREEN +0x0000A40C TD_PS_BORDER_COLOR_BLUE +0x0000A410 TD_PS_BORDER_COLOR_ALPHA +0x0000A414 TD_VS_BORDER_COLOR_INDEX +0x0000A418 TD_VS_BORDER_COLOR_RED +0x0000A41C TD_VS_BORDER_COLOR_GREEN +0x0000A420 TD_VS_BORDER_COLOR_BLUE +0x0000A424 TD_VS_BORDER_COLOR_ALPHA +0x0000A428 TD_GS_BORDER_COLOR_INDEX +0x0000A42C TD_GS_BORDER_COLOR_RED +0x0000A430 TD_GS_BORDER_COLOR_GREEN +0x0000A434 TD_GS_BORDER_COLOR_BLUE +0x0000A438 TD_GS_BORDER_COLOR_ALPHA +0x0000A43C TD_HS_BORDER_COLOR_INDEX +0x0000A440 TD_HS_BORDER_COLOR_RED +0x0000A444 TD_HS_BORDER_COLOR_GREEN +0x0000A448 TD_HS_BORDER_COLOR_BLUE +0x0000A44C TD_HS_BORDER_COLOR_ALPHA +0x0000A450 TD_LS_BORDER_COLOR_INDEX +0x0000A454 TD_LS_BORDER_COLOR_RED +0x0000A458 TD_LS_BORDER_COLOR_GREEN +0x0000A45C TD_LS_BORDER_COLOR_BLUE +0x0000A460 TD_LS_BORDER_COLOR_ALPHA +0x0000A464 TD_CS_BORDER_COLOR_INDEX +0x0000A468 TD_CS_BORDER_COLOR_RED +0x0000A46C TD_CS_BORDER_COLOR_GREEN +0x0000A470 TD_CS_BORDER_COLOR_BLUE +0x0000A474 TD_CS_BORDER_COLOR_ALPHA +0x00028000 DB_RENDER_CONTROL +0x00028004 DB_COUNT_CONTROL +0x0002800C DB_RENDER_OVERRIDE +0x00028010 DB_RENDER_OVERRIDE2 +0x00028028 DB_STENCIL_CLEAR +0x0002802C DB_DEPTH_CLEAR +0x00028030 PA_SC_SCREEN_SCISSOR_TL +0x00028034 PA_SC_SCREEN_SCISSOR_BR +0x00028140 SQ_ALU_CONST_BUFFER_SIZE_PS_0 +0x00028144 SQ_ALU_CONST_BUFFER_SIZE_PS_1 +0x00028148 SQ_ALU_CONST_BUFFER_SIZE_PS_2 +0x0002814C SQ_ALU_CONST_BUFFER_SIZE_PS_3 +0x00028150 SQ_ALU_CONST_BUFFER_SIZE_PS_4 +0x00028154 SQ_ALU_CONST_BUFFER_SIZE_PS_5 +0x00028158 SQ_ALU_CONST_BUFFER_SIZE_PS_6 +0x0002815C SQ_ALU_CONST_BUFFER_SIZE_PS_7 +0x00028160 SQ_ALU_CONST_BUFFER_SIZE_PS_8 +0x00028164 SQ_ALU_CONST_BUFFER_SIZE_PS_9 +0x00028168 SQ_ALU_CONST_BUFFER_SIZE_PS_10 +0x0002816C SQ_ALU_CONST_BUFFER_SIZE_PS_11 +0x00028170 SQ_ALU_CONST_BUFFER_SIZE_PS_12 +0x00028174 SQ_ALU_CONST_BUFFER_SIZE_PS_13 +0x00028178 SQ_ALU_CONST_BUFFER_SIZE_PS_14 +0x0002817C SQ_ALU_CONST_BUFFER_SIZE_PS_15 +0x00028180 SQ_ALU_CONST_BUFFER_SIZE_VS_0 +0x00028184 SQ_ALU_CONST_BUFFER_SIZE_VS_1 +0x00028188 SQ_ALU_CONST_BUFFER_SIZE_VS_2 +0x0002818C SQ_ALU_CONST_BUFFER_SIZE_VS_3 +0x00028190 SQ_ALU_CONST_BUFFER_SIZE_VS_4 +0x00028194 SQ_ALU_CONST_BUFFER_SIZE_VS_5 +0x00028198 SQ_ALU_CONST_BUFFER_SIZE_VS_6 +0x0002819C SQ_ALU_CONST_BUFFER_SIZE_VS_7 +0x000281A0 SQ_ALU_CONST_BUFFER_SIZE_VS_8 +0x000281A4 SQ_ALU_CONST_BUFFER_SIZE_VS_9 +0x000281A8 SQ_ALU_CONST_BUFFER_SIZE_VS_10 +0x000281AC SQ_ALU_CONST_BUFFER_SIZE_VS_11 +0x000281B0 SQ_ALU_CONST_BUFFER_SIZE_VS_12 +0x000281B4 SQ_ALU_CONST_BUFFER_SIZE_VS_13 +0x000281B8 SQ_ALU_CONST_BUFFER_SIZE_VS_14 +0x000281BC SQ_ALU_CONST_BUFFER_SIZE_VS_15 +0x000281C0 SQ_ALU_CONST_BUFFER_SIZE_GS_0 +0x000281C4 SQ_ALU_CONST_BUFFER_SIZE_GS_1 +0x000281C8 SQ_ALU_CONST_BUFFER_SIZE_GS_2 +0x000281CC SQ_ALU_CONST_BUFFER_SIZE_GS_3 +0x000281D0 SQ_ALU_CONST_BUFFER_SIZE_GS_4 +0x000281D4 SQ_ALU_CONST_BUFFER_SIZE_GS_5 +0x000281D8 SQ_ALU_CONST_BUFFER_SIZE_GS_6 +0x000281DC SQ_ALU_CONST_BUFFER_SIZE_GS_7 +0x000281E0 SQ_ALU_CONST_BUFFER_SIZE_GS_8 +0x000281E4 SQ_ALU_CONST_BUFFER_SIZE_GS_9 +0x000281E8 SQ_ALU_CONST_BUFFER_SIZE_GS_10 +0x000281EC SQ_ALU_CONST_BUFFER_SIZE_GS_11 +0x000281F0 SQ_ALU_CONST_BUFFER_SIZE_GS_12 +0x000281F4 SQ_ALU_CONST_BUFFER_SIZE_GS_13 +0x000281F8 SQ_ALU_CONST_BUFFER_SIZE_GS_14 +0x000281FC SQ_ALU_CONST_BUFFER_SIZE_GS_15 +0x00028200 PA_SC_WINDOW_OFFSET +0x00028204 PA_SC_WINDOW_SCISSOR_TL +0x00028208 PA_SC_WINDOW_SCISSOR_BR +0x0002820C PA_SC_CLIPRECT_RULE +0x00028210 PA_SC_CLIPRECT_0_TL +0x00028214 PA_SC_CLIPRECT_0_BR +0x00028218 PA_SC_CLIPRECT_1_TL +0x0002821C PA_SC_CLIPRECT_1_BR +0x00028220 PA_SC_CLIPRECT_2_TL +0x00028224 PA_SC_CLIPRECT_2_BR +0x00028228 PA_SC_CLIPRECT_3_TL +0x0002822C PA_SC_CLIPRECT_3_BR +0x00028230 PA_SC_EDGERULE +0x00028234 PA_SU_HARDWARE_SCREEN_OFFSET +0x00028240 PA_SC_GENERIC_SCISSOR_TL +0x00028244 PA_SC_GENERIC_SCISSOR_BR +0x00028250 PA_SC_VPORT_SCISSOR_0_TL +0x00028254 PA_SC_VPORT_SCISSOR_0_BR +0x00028258 PA_SC_VPORT_SCISSOR_1_TL +0x0002825C PA_SC_VPORT_SCISSOR_1_BR +0x00028260 PA_SC_VPORT_SCISSOR_2_TL +0x00028264 PA_SC_VPORT_SCISSOR_2_BR +0x00028268 PA_SC_VPORT_SCISSOR_3_TL +0x0002826C PA_SC_VPORT_SCISSOR_3_BR +0x00028270 PA_SC_VPORT_SCISSOR_4_TL +0x00028274 PA_SC_VPORT_SCISSOR_4_BR +0x00028278 PA_SC_VPORT_SCISSOR_5_TL +0x0002827C PA_SC_VPORT_SCISSOR_5_BR +0x00028280 PA_SC_VPORT_SCISSOR_6_TL +0x00028284 PA_SC_VPORT_SCISSOR_6_BR +0x00028288 PA_SC_VPORT_SCISSOR_7_TL +0x0002828C PA_SC_VPORT_SCISSOR_7_BR +0x00028290 PA_SC_VPORT_SCISSOR_8_TL +0x00028294 PA_SC_VPORT_SCISSOR_8_BR +0x00028298 PA_SC_VPORT_SCISSOR_9_TL +0x0002829C PA_SC_VPORT_SCISSOR_9_BR +0x000282A0 PA_SC_VPORT_SCISSOR_10_TL +0x000282A4 PA_SC_VPORT_SCISSOR_10_BR +0x000282A8 PA_SC_VPORT_SCISSOR_11_TL +0x000282AC PA_SC_VPORT_SCISSOR_11_BR +0x000282B0 PA_SC_VPORT_SCISSOR_12_TL +0x000282B4 PA_SC_VPORT_SCISSOR_12_BR +0x000282B8 PA_SC_VPORT_SCISSOR_13_TL +0x000282BC PA_SC_VPORT_SCISSOR_13_BR +0x000282C0 PA_SC_VPORT_SCISSOR_14_TL +0x000282C4 PA_SC_VPORT_SCISSOR_14_BR +0x000282C8 PA_SC_VPORT_SCISSOR_15_TL +0x000282CC PA_SC_VPORT_SCISSOR_15_BR +0x000282D0 PA_SC_VPORT_ZMIN_0 +0x000282D4 PA_SC_VPORT_ZMAX_0 +0x000282D8 PA_SC_VPORT_ZMIN_1 +0x000282DC PA_SC_VPORT_ZMAX_1 +0x000282E0 PA_SC_VPORT_ZMIN_2 +0x000282E4 PA_SC_VPORT_ZMAX_2 +0x000282E8 PA_SC_VPORT_ZMIN_3 +0x000282EC PA_SC_VPORT_ZMAX_3 +0x000282F0 PA_SC_VPORT_ZMIN_4 +0x000282F4 PA_SC_VPORT_ZMAX_4 +0x000282F8 PA_SC_VPORT_ZMIN_5 +0x000282FC PA_SC_VPORT_ZMAX_5 +0x00028300 PA_SC_VPORT_ZMIN_6 +0x00028304 PA_SC_VPORT_ZMAX_6 +0x00028308 PA_SC_VPORT_ZMIN_7 +0x0002830C PA_SC_VPORT_ZMAX_7 +0x00028310 PA_SC_VPORT_ZMIN_8 +0x00028314 PA_SC_VPORT_ZMAX_8 +0x00028318 PA_SC_VPORT_ZMIN_9 +0x0002831C PA_SC_VPORT_ZMAX_9 +0x00028320 PA_SC_VPORT_ZMIN_10 +0x00028324 PA_SC_VPORT_ZMAX_10 +0x00028328 PA_SC_VPORT_ZMIN_11 +0x0002832C PA_SC_VPORT_ZMAX_11 +0x00028330 PA_SC_VPORT_ZMIN_12 +0x00028334 PA_SC_VPORT_ZMAX_12 +0x00028338 PA_SC_VPORT_ZMIN_13 +0x0002833C PA_SC_VPORT_ZMAX_13 +0x00028340 PA_SC_VPORT_ZMIN_14 +0x00028344 PA_SC_VPORT_ZMAX_14 +0x00028348 PA_SC_VPORT_ZMIN_15 +0x0002834C PA_SC_VPORT_ZMAX_15 +0x00028354 SX_SURFACE_SYNC +0x0002835C SX_SCATTER_EXPORT_SIZE +0x00028380 SQ_VTX_SEMANTIC_0 +0x00028384 SQ_VTX_SEMANTIC_1 +0x00028388 SQ_VTX_SEMANTIC_2 +0x0002838C SQ_VTX_SEMANTIC_3 +0x00028390 SQ_VTX_SEMANTIC_4 +0x00028394 SQ_VTX_SEMANTIC_5 +0x00028398 SQ_VTX_SEMANTIC_6 +0x0002839C SQ_VTX_SEMANTIC_7 +0x000283A0 SQ_VTX_SEMANTIC_8 +0x000283A4 SQ_VTX_SEMANTIC_9 +0x000283A8 SQ_VTX_SEMANTIC_10 +0x000283AC SQ_VTX_SEMANTIC_11 +0x000283B0 SQ_VTX_SEMANTIC_12 +0x000283B4 SQ_VTX_SEMANTIC_13 +0x000283B8 SQ_VTX_SEMANTIC_14 +0x000283BC SQ_VTX_SEMANTIC_15 +0x000283C0 SQ_VTX_SEMANTIC_16 +0x000283C4 SQ_VTX_SEMANTIC_17 +0x000283C8 SQ_VTX_SEMANTIC_18 +0x000283CC SQ_VTX_SEMANTIC_19 +0x000283D0 SQ_VTX_SEMANTIC_20 +0x000283D4 SQ_VTX_SEMANTIC_21 +0x000283D8 SQ_VTX_SEMANTIC_22 +0x000283DC SQ_VTX_SEMANTIC_23 +0x000283E0 SQ_VTX_SEMANTIC_24 +0x000283E4 SQ_VTX_SEMANTIC_25 +0x000283E8 SQ_VTX_SEMANTIC_26 +0x000283EC SQ_VTX_SEMANTIC_27 +0x000283F0 SQ_VTX_SEMANTIC_28 +0x000283F4 SQ_VTX_SEMANTIC_29 +0x000283F8 SQ_VTX_SEMANTIC_30 +0x000283FC SQ_VTX_SEMANTIC_31 +0x00028400 VGT_MAX_VTX_INDX +0x00028404 VGT_MIN_VTX_INDX +0x00028408 VGT_INDX_OFFSET +0x0002840C VGT_MULTI_PRIM_IB_RESET_INDX +0x00028410 SX_ALPHA_TEST_CONTROL +0x00028414 CB_BLEND_RED +0x00028418 CB_BLEND_GREEN +0x0002841C CB_BLEND_BLUE +0x00028420 CB_BLEND_ALPHA +0x00028430 DB_STENCILREFMASK +0x00028434 DB_STENCILREFMASK_BF +0x00028438 SX_ALPHA_REF +0x0002843C PA_CL_VPORT_XSCALE_0 +0x00028440 PA_CL_VPORT_XOFFSET_0 +0x00028444 PA_CL_VPORT_YSCALE_0 +0x00028448 PA_CL_VPORT_YOFFSET_0 +0x0002844C PA_CL_VPORT_ZSCALE_0 +0x00028450 PA_CL_VPORT_ZOFFSET_0 +0x00028454 PA_CL_VPORT_XSCALE_1 +0x00028458 PA_CL_VPORT_XOFFSET_1 +0x0002845C PA_CL_VPORT_YSCALE_1 +0x00028460 PA_CL_VPORT_YOFFSET_1 +0x00028464 PA_CL_VPORT_ZSCALE_1 +0x00028468 PA_CL_VPORT_ZOFFSET_1 +0x0002846C PA_CL_VPORT_XSCALE_2 +0x00028470 PA_CL_VPORT_XOFFSET_2 +0x00028474 PA_CL_VPORT_YSCALE_2 +0x00028478 PA_CL_VPORT_YOFFSET_2 +0x0002847C PA_CL_VPORT_ZSCALE_2 +0x00028480 PA_CL_VPORT_ZOFFSET_2 +0x00028484 PA_CL_VPORT_XSCALE_3 +0x00028488 PA_CL_VPORT_XOFFSET_3 +0x0002848C PA_CL_VPORT_YSCALE_3 +0x00028490 PA_CL_VPORT_YOFFSET_3 +0x00028494 PA_CL_VPORT_ZSCALE_3 +0x00028498 PA_CL_VPORT_ZOFFSET_3 +0x0002849C PA_CL_VPORT_XSCALE_4 +0x000284A0 PA_CL_VPORT_XOFFSET_4 +0x000284A4 PA_CL_VPORT_YSCALE_4 +0x000284A8 PA_CL_VPORT_YOFFSET_4 +0x000284AC PA_CL_VPORT_ZSCALE_4 +0x000284B0 PA_CL_VPORT_ZOFFSET_4 +0x000284B4 PA_CL_VPORT_XSCALE_5 +0x000284B8 PA_CL_VPORT_XOFFSET_5 +0x000284BC PA_CL_VPORT_YSCALE_5 +0x000284C0 PA_CL_VPORT_YOFFSET_5 +0x000284C4 PA_CL_VPORT_ZSCALE_5 +0x000284C8 PA_CL_VPORT_ZOFFSET_5 +0x000284CC PA_CL_VPORT_XSCALE_6 +0x000284D0 PA_CL_VPORT_XOFFSET_6 +0x000284D4 PA_CL_VPORT_YSCALE_6 +0x000284D8 PA_CL_VPORT_YOFFSET_6 +0x000284DC PA_CL_VPORT_ZSCALE_6 +0x000284E0 PA_CL_VPORT_ZOFFSET_6 +0x000284E4 PA_CL_VPORT_XSCALE_7 +0x000284E8 PA_CL_VPORT_XOFFSET_7 +0x000284EC PA_CL_VPORT_YSCALE_7 +0x000284F0 PA_CL_VPORT_YOFFSET_7 +0x000284F4 PA_CL_VPORT_ZSCALE_7 +0x000284F8 PA_CL_VPORT_ZOFFSET_7 +0x000284FC PA_CL_VPORT_XSCALE_8 +0x00028500 PA_CL_VPORT_XOFFSET_8 +0x00028504 PA_CL_VPORT_YSCALE_8 +0x00028508 PA_CL_VPORT_YOFFSET_8 +0x0002850C PA_CL_VPORT_ZSCALE_8 +0x00028510 PA_CL_VPORT_ZOFFSET_8 +0x00028514 PA_CL_VPORT_XSCALE_9 +0x00028518 PA_CL_VPORT_XOFFSET_9 +0x0002851C PA_CL_VPORT_YSCALE_9 +0x00028520 PA_CL_VPORT_YOFFSET_9 +0x00028524 PA_CL_VPORT_ZSCALE_9 +0x00028528 PA_CL_VPORT_ZOFFSET_9 +0x0002852C PA_CL_VPORT_XSCALE_10 +0x00028530 PA_CL_VPORT_XOFFSET_10 +0x00028534 PA_CL_VPORT_YSCALE_10 +0x00028538 PA_CL_VPORT_YOFFSET_10 +0x0002853C PA_CL_VPORT_ZSCALE_10 +0x00028540 PA_CL_VPORT_ZOFFSET_10 +0x00028544 PA_CL_VPORT_XSCALE_11 +0x00028548 PA_CL_VPORT_XOFFSET_11 +0x0002854C PA_CL_VPORT_YSCALE_11 +0x00028550 PA_CL_VPORT_YOFFSET_11 +0x00028554 PA_CL_VPORT_ZSCALE_11 +0x00028558 PA_CL_VPORT_ZOFFSET_11 +0x0002855C PA_CL_VPORT_XSCALE_12 +0x00028560 PA_CL_VPORT_XOFFSET_12 +0x00028564 PA_CL_VPORT_YSCALE_12 +0x00028568 PA_CL_VPORT_YOFFSET_12 +0x0002856C PA_CL_VPORT_ZSCALE_12 +0x00028570 PA_CL_VPORT_ZOFFSET_12 +0x00028574 PA_CL_VPORT_XSCALE_13 +0x00028578 PA_CL_VPORT_XOFFSET_13 +0x0002857C PA_CL_VPORT_YSCALE_13 +0x00028580 PA_CL_VPORT_YOFFSET_13 +0x00028584 PA_CL_VPORT_ZSCALE_13 +0x00028588 PA_CL_VPORT_ZOFFSET_13 +0x0002858C PA_CL_VPORT_XSCALE_14 +0x00028590 PA_CL_VPORT_XOFFSET_14 +0x00028594 PA_CL_VPORT_YSCALE_14 +0x00028598 PA_CL_VPORT_YOFFSET_14 +0x0002859C PA_CL_VPORT_ZSCALE_14 +0x000285A0 PA_CL_VPORT_ZOFFSET_14 +0x000285A4 PA_CL_VPORT_XSCALE_15 +0x000285A8 PA_CL_VPORT_XOFFSET_15 +0x000285AC PA_CL_VPORT_YSCALE_15 +0x000285B0 PA_CL_VPORT_YOFFSET_15 +0x000285B4 PA_CL_VPORT_ZSCALE_15 +0x000285B8 PA_CL_VPORT_ZOFFSET_15 +0x000285BC PA_CL_UCP_0_X +0x000285C0 PA_CL_UCP_0_Y +0x000285C4 PA_CL_UCP_0_Z +0x000285C8 PA_CL_UCP_0_W +0x000285CC PA_CL_UCP_1_X +0x000285D0 PA_CL_UCP_1_Y +0x000285D4 PA_CL_UCP_1_Z +0x000285D8 PA_CL_UCP_1_W +0x000285DC PA_CL_UCP_2_X +0x000285E0 PA_CL_UCP_2_Y +0x000285E4 PA_CL_UCP_2_Z +0x000285E8 PA_CL_UCP_2_W +0x000285EC PA_CL_UCP_3_X +0x000285F0 PA_CL_UCP_3_Y +0x000285F4 PA_CL_UCP_3_Z +0x000285F8 PA_CL_UCP_3_W +0x000285FC PA_CL_UCP_4_X +0x00028600 PA_CL_UCP_4_Y +0x00028604 PA_CL_UCP_4_Z +0x00028608 PA_CL_UCP_4_W +0x0002860C PA_CL_UCP_5_X +0x00028610 PA_CL_UCP_5_Y +0x00028614 PA_CL_UCP_5_Z +0x00028618 PA_CL_UCP_5_W +0x0002861C SPI_VS_OUT_ID_0 +0x00028620 SPI_VS_OUT_ID_1 +0x00028624 SPI_VS_OUT_ID_2 +0x00028628 SPI_VS_OUT_ID_3 +0x0002862C SPI_VS_OUT_ID_4 +0x00028630 SPI_VS_OUT_ID_5 +0x00028634 SPI_VS_OUT_ID_6 +0x00028638 SPI_VS_OUT_ID_7 +0x0002863C SPI_VS_OUT_ID_8 +0x00028640 SPI_VS_OUT_ID_9 +0x00028644 SPI_PS_INPUT_CNTL_0 +0x00028648 SPI_PS_INPUT_CNTL_1 +0x0002864C SPI_PS_INPUT_CNTL_2 +0x00028650 SPI_PS_INPUT_CNTL_3 +0x00028654 SPI_PS_INPUT_CNTL_4 +0x00028658 SPI_PS_INPUT_CNTL_5 +0x0002865C SPI_PS_INPUT_CNTL_6 +0x00028660 SPI_PS_INPUT_CNTL_7 +0x00028664 SPI_PS_INPUT_CNTL_8 +0x00028668 SPI_PS_INPUT_CNTL_9 +0x0002866C SPI_PS_INPUT_CNTL_10 +0x00028670 SPI_PS_INPUT_CNTL_11 +0x00028674 SPI_PS_INPUT_CNTL_12 +0x00028678 SPI_PS_INPUT_CNTL_13 +0x0002867C SPI_PS_INPUT_CNTL_14 +0x00028680 SPI_PS_INPUT_CNTL_15 +0x00028684 SPI_PS_INPUT_CNTL_16 +0x00028688 SPI_PS_INPUT_CNTL_17 +0x0002868C SPI_PS_INPUT_CNTL_18 +0x00028690 SPI_PS_INPUT_CNTL_19 +0x00028694 SPI_PS_INPUT_CNTL_20 +0x00028698 SPI_PS_INPUT_CNTL_21 +0x0002869C SPI_PS_INPUT_CNTL_22 +0x000286A0 SPI_PS_INPUT_CNTL_23 +0x000286A4 SPI_PS_INPUT_CNTL_24 +0x000286A8 SPI_PS_INPUT_CNTL_25 +0x000286AC SPI_PS_INPUT_CNTL_26 +0x000286B0 SPI_PS_INPUT_CNTL_27 +0x000286B4 SPI_PS_INPUT_CNTL_28 +0x000286B8 SPI_PS_INPUT_CNTL_29 +0x000286BC SPI_PS_INPUT_CNTL_30 +0x000286C0 SPI_PS_INPUT_CNTL_31 +0x000286C4 SPI_VS_OUT_CONFIG +0x000286C8 SPI_THREAD_GROUPING +0x000286CC SPI_PS_IN_CONTROL_0 +0x000286D0 SPI_PS_IN_CONTROL_1 +0x000286D4 SPI_INTERP_CONTROL_0 +0x000286D8 SPI_INPUT_Z +0x000286DC SPI_FOG_CNTL +0x000286E0 SPI_BARYC_CNTL +0x000286E4 SPI_PS_IN_CONTROL_2 +0x000286E8 SPI_COMPUTE_INPUT_CNTL +0x000286EC SPI_COMPUTE_NUM_THREAD_X +0x000286F0 SPI_COMPUTE_NUM_THREAD_Y +0x000286F4 SPI_COMPUTE_NUM_THREAD_Z +0x000286F8 SPI_GPR_MGMT +0x000286FC SPI_LDS_MGMT +0x00028700 SPI_STACK_MGMT +0x00028704 SPI_WAVE_MGMT_1 +0x00028708 SPI_WAVE_MGMT_2 +0x00028720 GDS_ADDR_BASE +0x00028724 GDS_ADDR_SIZE +0x00028780 CB_BLEND0_CONTROL +0x00028784 CB_BLEND1_CONTROL +0x00028788 CB_BLEND2_CONTROL +0x0002878C CB_BLEND3_CONTROL +0x00028790 CB_BLEND4_CONTROL +0x00028794 CB_BLEND5_CONTROL +0x00028798 CB_BLEND6_CONTROL +0x0002879C CB_BLEND7_CONTROL +0x000287CC CS_COPY_STATE +0x000287D0 GFX_COPY_STATE +0x000287D4 PA_CL_POINT_X_RAD +0x000287D8 PA_CL_POINT_Y_RAD +0x000287DC PA_CL_POINT_SIZE +0x000287E0 PA_CL_POINT_CULL_RAD +0x00028808 CB_COLOR_CONTROL +0x0002880C DB_SHADER_CONTROL +0x00028810 PA_CL_CLIP_CNTL +0x00028814 PA_SU_SC_MODE_CNTL +0x00028818 PA_CL_VTE_CNTL +0x0002881C PA_CL_VS_OUT_CNTL +0x00028820 PA_CL_NANINF_CNTL +0x00028824 PA_SU_LINE_STIPPLE_CNTL +0x00028828 PA_SU_LINE_STIPPLE_SCALE +0x0002882C PA_SU_PRIM_FILTER_CNTL +0x00028844 SQ_PGM_RESOURCES_PS +0x00028848 SQ_PGM_RESOURCES_2_PS +0x0002884C SQ_PGM_EXPORTS_PS +0x00028860 SQ_PGM_RESOURCES_VS +0x00028864 SQ_PGM_RESOURCES_2_VS +0x00028878 SQ_PGM_RESOURCES_GS +0x0002887C SQ_PGM_RESOURCES_2_GS +0x00028890 SQ_PGM_RESOURCES_ES +0x00028894 SQ_PGM_RESOURCES_2_ES +0x000288A8 SQ_PGM_RESOURCES_FS +0x000288BC SQ_PGM_RESOURCES_HS +0x000288C0 SQ_PGM_RESOURCES_2_HS +0x000288D4 SQ_PGM_RESOURCES_LS +0x000288D8 SQ_PGM_RESOURCES_2_LS +0x000288E8 SQ_LDS_ALLOC +0x000288EC SQ_LDS_ALLOC_PS +0x000288F0 SQ_VTX_SEMANTIC_CLEAR +0x00028A00 PA_SU_POINT_SIZE +0x00028A04 PA_SU_POINT_MINMAX +0x00028A08 PA_SU_LINE_CNTL +0x00028A0C PA_SC_LINE_STIPPLE +0x00028A10 VGT_OUTPUT_PATH_CNTL +0x00028A14 VGT_HOS_CNTL +0x00028A18 VGT_HOS_MAX_TESS_LEVEL +0x00028A1C VGT_HOS_MIN_TESS_LEVEL +0x00028A20 VGT_HOS_REUSE_DEPTH +0x00028A24 VGT_GROUP_PRIM_TYPE +0x00028A28 VGT_GROUP_FIRST_DECR +0x00028A2C VGT_GROUP_DECR +0x00028A30 VGT_GROUP_VECT_0_CNTL +0x00028A34 VGT_GROUP_VECT_1_CNTL +0x00028A38 VGT_GROUP_VECT_0_FMT_CNTL +0x00028A3C VGT_GROUP_VECT_1_FMT_CNTL +0x00028A40 VGT_GS_MODE +0x00028A48 PA_SC_MODE_CNTL_0 +0x00028A4C PA_SC_MODE_CNTL_1 +0x00028A50 VGT_ENHANCE +0x00028A54 VGT_GS_PER_ES +0x00028A58 VGT_ES_PER_GS +0x00028A5C VGT_GS_PER_VS +0x00028A6C VGT_GS_OUT_PRIM_TYPE +0x00028A70 IA_ENHANCE +0x00028A84 VGT_PRIMITIVEID_EN +0x00028A94 VGT_MULTI_PRIM_IB_RESET_EN +0x00028AA0 VGT_INSTANCE_STEP_RATE_0 +0x00028AA4 VGT_INSTANCE_STEP_RATE_1 +0x00028AA8 IA_MULTI_VGT_PARAM +0x00028AB4 VGT_REUSE_OFF +0x00028AB8 VGT_VTX_CNT_EN +0x00028AC0 DB_SRESULTS_COMPARE_STATE0 +0x00028AC4 DB_SRESULTS_COMPARE_STATE1 +0x00028AC8 DB_PRELOAD_CONTROL +0x00028AD4 VGT_STRMOUT_VTX_STRIDE_0 +0x00028AE4 VGT_STRMOUT_VTX_STRIDE_1 +0x00028AF4 VGT_STRMOUT_VTX_STRIDE_2 +0x00028B04 VGT_STRMOUT_VTX_STRIDE_3 +0x00028B28 VGT_STRMOUT_DRAW_OPAQUE_OFFSET +0x00028B2C VGT_STRMOUT_DRAW_OPAQUE_BUFFER_FILLED_SIZE +0x00028B30 VGT_STRMOUT_DRAW_OPAQUE_VERTEX_STRIDE +0x00028B38 VGT_GS_MAX_VERT_OUT +0x00028B54 VGT_SHADER_STAGES_EN +0x00028B58 VGT_LS_HS_CONFIG +0x00028B6C VGT_TF_PARAM +0x00028B70 DB_ALPHA_TO_MASK +0x00028B74 VGT_DISPATCH_INITIATOR +0x00028B78 PA_SU_POLY_OFFSET_DB_FMT_CNTL +0x00028B7C PA_SU_POLY_OFFSET_CLAMP +0x00028B80 PA_SU_POLY_OFFSET_FRONT_SCALE +0x00028B84 PA_SU_POLY_OFFSET_FRONT_OFFSET +0x00028B88 PA_SU_POLY_OFFSET_BACK_SCALE +0x00028B8C PA_SU_POLY_OFFSET_BACK_OFFSET +0x00028B74 VGT_GS_INSTANCE_CNT +0x00028BD4 PA_SC_CENTROID_PRIORITY_0 +0x00028BD8 PA_SC_CENTROID_PRIORITY_1 +0x00028BDC PA_SC_LINE_CNTL +0x00028BE4 PA_SU_VTX_CNTL +0x00028BE8 PA_CL_GB_VERT_CLIP_ADJ +0x00028BEC PA_CL_GB_VERT_DISC_ADJ +0x00028BF0 PA_CL_GB_HORZ_CLIP_ADJ +0x00028BF4 PA_CL_GB_HORZ_DISC_ADJ +0x00028BF8 PA_SC_AA_SAMPLE_LOCS_PIXEL_X0_Y0_0 +0x00028BFC PA_SC_AA_SAMPLE_LOCS_PIXEL_X0_Y0_1 +0x00028C00 PA_SC_AA_SAMPLE_LOCS_PIXEL_X0_Y0_2 +0x00028C04 PA_SC_AA_SAMPLE_LOCS_PIXEL_X0_Y0_3 +0x00028C08 PA_SC_AA_SAMPLE_LOCS_PIXEL_X1_Y0_0 +0x00028C0C PA_SC_AA_SAMPLE_LOCS_PIXEL_X1_Y0_1 +0x00028C10 PA_SC_AA_SAMPLE_LOCS_PIXEL_X1_Y0_2 +0x00028C14 PA_SC_AA_SAMPLE_LOCS_PIXEL_X1_Y0_3 +0x00028C18 PA_SC_AA_SAMPLE_LOCS_PIXEL_X0_Y1_0 +0x00028C1C PA_SC_AA_SAMPLE_LOCS_PIXEL_X0_Y1_1 +0x00028C20 PA_SC_AA_SAMPLE_LOCS_PIXEL_X0_Y1_2 +0x00028C24 PA_SC_AA_SAMPLE_LOCS_PIXEL_X0_Y1_3 +0x00028C28 PA_SC_AA_SAMPLE_LOCS_PIXEL_X1_Y1_0 +0x00028C2C PA_SC_AA_SAMPLE_LOCS_PIXEL_X1_Y1_1 +0x00028C30 PA_SC_AA_SAMPLE_LOCS_PIXEL_X1_Y1_2 +0x00028C34 PA_SC_AA_SAMPLE_LOCS_PIXEL_X1_Y1_3 +0x00028C38 PA_SC_AA_MASK_X0_Y0_X1_Y0 +0x00028C3C PA_SC_AA_MASK_X0_Y1_X1_Y1 +0x00028C78 CB_COLOR0_DIM +0x00028CB4 CB_COLOR1_DIM +0x00028CF0 CB_COLOR2_DIM +0x00028D2C CB_COLOR3_DIM +0x00028D68 CB_COLOR4_DIM +0x00028DA4 CB_COLOR5_DIM +0x00028DE0 CB_COLOR6_DIM +0x00028E1C CB_COLOR7_DIM +0x00028E58 CB_COLOR8_DIM +0x00028E74 CB_COLOR9_DIM +0x00028E90 CB_COLOR10_DIM +0x00028EAC CB_COLOR11_DIM +0x00028C8C CB_COLOR0_CLEAR_WORD0 +0x00028C90 CB_COLOR0_CLEAR_WORD1 +0x00028C94 CB_COLOR0_CLEAR_WORD2 +0x00028C98 CB_COLOR0_CLEAR_WORD3 +0x00028CC8 CB_COLOR1_CLEAR_WORD0 +0x00028CCC CB_COLOR1_CLEAR_WORD1 +0x00028CD0 CB_COLOR1_CLEAR_WORD2 +0x00028CD4 CB_COLOR1_CLEAR_WORD3 +0x00028D04 CB_COLOR2_CLEAR_WORD0 +0x00028D08 CB_COLOR2_CLEAR_WORD1 +0x00028D0C CB_COLOR2_CLEAR_WORD2 +0x00028D10 CB_COLOR2_CLEAR_WORD3 +0x00028D40 CB_COLOR3_CLEAR_WORD0 +0x00028D44 CB_COLOR3_CLEAR_WORD1 +0x00028D48 CB_COLOR3_CLEAR_WORD2 +0x00028D4C CB_COLOR3_CLEAR_WORD3 +0x00028D7C CB_COLOR4_CLEAR_WORD0 +0x00028D80 CB_COLOR4_CLEAR_WORD1 +0x00028D84 CB_COLOR4_CLEAR_WORD2 +0x00028D88 CB_COLOR4_CLEAR_WORD3 +0x00028DB8 CB_COLOR5_CLEAR_WORD0 +0x00028DBC CB_COLOR5_CLEAR_WORD1 +0x00028DC0 CB_COLOR5_CLEAR_WORD2 +0x00028DC4 CB_COLOR5_CLEAR_WORD3 +0x00028DF4 CB_COLOR6_CLEAR_WORD0 +0x00028DF8 CB_COLOR6_CLEAR_WORD1 +0x00028DFC CB_COLOR6_CLEAR_WORD2 +0x00028E00 CB_COLOR6_CLEAR_WORD3 +0x00028E30 CB_COLOR7_CLEAR_WORD0 +0x00028E34 CB_COLOR7_CLEAR_WORD1 +0x00028E38 CB_COLOR7_CLEAR_WORD2 +0x00028E3C CB_COLOR7_CLEAR_WORD3 +0x00028F80 SQ_ALU_CONST_BUFFER_SIZE_HS_0 +0x00028F84 SQ_ALU_CONST_BUFFER_SIZE_HS_1 +0x00028F88 SQ_ALU_CONST_BUFFER_SIZE_HS_2 +0x00028F8C SQ_ALU_CONST_BUFFER_SIZE_HS_3 +0x00028F90 SQ_ALU_CONST_BUFFER_SIZE_HS_4 +0x00028F94 SQ_ALU_CONST_BUFFER_SIZE_HS_5 +0x00028F98 SQ_ALU_CONST_BUFFER_SIZE_HS_6 +0x00028F9C SQ_ALU_CONST_BUFFER_SIZE_HS_7 +0x00028FA0 SQ_ALU_CONST_BUFFER_SIZE_HS_8 +0x00028FA4 SQ_ALU_CONST_BUFFER_SIZE_HS_9 +0x00028FA8 SQ_ALU_CONST_BUFFER_SIZE_HS_10 +0x00028FAC SQ_ALU_CONST_BUFFER_SIZE_HS_11 +0x00028FB0 SQ_ALU_CONST_BUFFER_SIZE_HS_12 +0x00028FB4 SQ_ALU_CONST_BUFFER_SIZE_HS_13 +0x00028FB8 SQ_ALU_CONST_BUFFER_SIZE_HS_14 +0x00028FBC SQ_ALU_CONST_BUFFER_SIZE_HS_15 +0x00028FC0 SQ_ALU_CONST_BUFFER_SIZE_LS_0 +0x00028FC4 SQ_ALU_CONST_BUFFER_SIZE_LS_1 +0x00028FC8 SQ_ALU_CONST_BUFFER_SIZE_LS_2 +0x00028FCC SQ_ALU_CONST_BUFFER_SIZE_LS_3 +0x00028FD0 SQ_ALU_CONST_BUFFER_SIZE_LS_4 +0x00028FD4 SQ_ALU_CONST_BUFFER_SIZE_LS_5 +0x00028FD8 SQ_ALU_CONST_BUFFER_SIZE_LS_6 +0x00028FDC SQ_ALU_CONST_BUFFER_SIZE_LS_7 +0x00028FE0 SQ_ALU_CONST_BUFFER_SIZE_LS_8 +0x00028FE4 SQ_ALU_CONST_BUFFER_SIZE_LS_9 +0x00028FE8 SQ_ALU_CONST_BUFFER_SIZE_LS_10 +0x00028FEC SQ_ALU_CONST_BUFFER_SIZE_LS_11 +0x00028FF0 SQ_ALU_CONST_BUFFER_SIZE_LS_12 +0x00028FF4 SQ_ALU_CONST_BUFFER_SIZE_LS_13 +0x00028FF8 SQ_ALU_CONST_BUFFER_SIZE_LS_14 +0x00028FFC SQ_ALU_CONST_BUFFER_SIZE_LS_15 +0x0003CFF0 SQ_VTX_BASE_VTX_LOC +0x0003CFF4 SQ_VTX_START_INST_LOC +0x0003FF00 SQ_TEX_SAMPLER_CLEAR +0x0003FF04 SQ_TEX_RESOURCE_CLEAR +0x0003FF08 SQ_LOOP_BOOL_CLEAR diff --git a/sys/dev/drm2/radeon/reg_srcs/evergreen b/sys/dev/drm2/radeon/reg_srcs/evergreen new file mode 100644 index 00000000000..b912a37689b --- /dev/null +++ b/sys/dev/drm2/radeon/reg_srcs/evergreen @@ -0,0 +1,644 @@ +evergreen 0x9400 +0x0000802C GRBM_GFX_INDEX +0x00008040 WAIT_UNTIL +0x00008044 WAIT_UNTIL_POLL_CNTL +0x00008048 WAIT_UNTIL_POLL_MASK +0x0000804c WAIT_UNTIL_POLL_REFDATA +0x000084FC CP_STRMOUT_CNTL +0x000085F0 CP_COHER_CNTL +0x000085F4 CP_COHER_SIZE +0x000088B0 VGT_VTX_VECT_EJECT_REG +0x000088C4 VGT_CACHE_INVALIDATION +0x000088D4 VGT_GS_VERTEX_REUSE +0x00008958 VGT_PRIMITIVE_TYPE +0x0000895C VGT_INDEX_TYPE +0x00008970 VGT_NUM_INDICES +0x00008974 VGT_NUM_INSTANCES +0x00008990 VGT_COMPUTE_DIM_X +0x00008994 VGT_COMPUTE_DIM_Y +0x00008998 VGT_COMPUTE_DIM_Z +0x0000899C VGT_COMPUTE_START_X +0x000089A0 VGT_COMPUTE_START_Y +0x000089A4 VGT_COMPUTE_START_Z +0x000089AC VGT_COMPUTE_THREAD_GOURP_SIZE +0x00008A14 PA_CL_ENHANCE +0x00008A60 PA_SC_LINE_STIPPLE_VALUE +0x00008B10 PA_SC_LINE_STIPPLE_STATE +0x00008BF0 PA_SC_ENHANCE +0x00008D8C SQ_DYN_GPR_CNTL_PS_FLUSH_REQ +0x00008D90 SQ_DYN_GPR_OPTIMIZATION +0x00008D94 SQ_DYN_GPR_SIMD_LOCK_EN +0x00008D98 SQ_DYN_GPR_THREAD_LIMIT +0x00008D9C SQ_DYN_GPR_LDS_LIMIT +0x00008C00 SQ_CONFIG +0x00008C04 SQ_GPR_RESOURCE_MGMT_1 +0x00008C08 SQ_GPR_RESOURCE_MGMT_2 +0x00008C0C SQ_GPR_RESOURCE_MGMT_3 +0x00008C10 SQ_GLOBAL_GPR_RESOURCE_MGMT_1 +0x00008C14 SQ_GLOBAL_GPR_RESOURCE_MGMT_2 +0x00008C18 SQ_THREAD_RESOURCE_MGMT +0x00008C1C SQ_THREAD_RESOURCE_MGMT_2 +0x00008C20 SQ_STACK_RESOURCE_MGMT_1 +0x00008C24 SQ_STACK_RESOURCE_MGMT_2 +0x00008C28 SQ_STACK_RESOURCE_MGMT_3 +0x00008DF8 SQ_CONST_MEM_BASE +0x00008E20 SQ_STATIC_THREAD_MGMT_1 +0x00008E24 SQ_STATIC_THREAD_MGMT_2 +0x00008E28 SQ_STATIC_THREAD_MGMT_3 +0x00008E2C SQ_LDS_RESOURCE_MGMT +0x00008E48 SQ_EX_ALLOC_TABLE_SLOTS +0x00009014 SX_MEMORY_EXPORT_SIZE +0x00009100 SPI_CONFIG_CNTL +0x0000913C SPI_CONFIG_CNTL_1 +0x00009508 TA_CNTL_AUX +0x00009700 VC_CNTL +0x00009714 VC_ENHANCE +0x00009830 DB_DEBUG +0x00009834 DB_DEBUG2 +0x00009838 DB_DEBUG3 +0x0000983C DB_DEBUG4 +0x00009854 DB_WATERMARKS +0x0000A400 TD_PS_BORDER_COLOR_INDEX +0x0000A404 TD_PS_BORDER_COLOR_RED +0x0000A408 TD_PS_BORDER_COLOR_GREEN +0x0000A40C TD_PS_BORDER_COLOR_BLUE +0x0000A410 TD_PS_BORDER_COLOR_ALPHA +0x0000A414 TD_VS_BORDER_COLOR_INDEX +0x0000A418 TD_VS_BORDER_COLOR_RED +0x0000A41C TD_VS_BORDER_COLOR_GREEN +0x0000A420 TD_VS_BORDER_COLOR_BLUE +0x0000A424 TD_VS_BORDER_COLOR_ALPHA +0x0000A428 TD_GS_BORDER_COLOR_INDEX +0x0000A42C TD_GS_BORDER_COLOR_RED +0x0000A430 TD_GS_BORDER_COLOR_GREEN +0x0000A434 TD_GS_BORDER_COLOR_BLUE +0x0000A438 TD_GS_BORDER_COLOR_ALPHA +0x0000A43C TD_HS_BORDER_COLOR_INDEX +0x0000A440 TD_HS_BORDER_COLOR_RED +0x0000A444 TD_HS_BORDER_COLOR_GREEN +0x0000A448 TD_HS_BORDER_COLOR_BLUE +0x0000A44C TD_HS_BORDER_COLOR_ALPHA +0x0000A450 TD_LS_BORDER_COLOR_INDEX +0x0000A454 TD_LS_BORDER_COLOR_RED +0x0000A458 TD_LS_BORDER_COLOR_GREEN +0x0000A45C TD_LS_BORDER_COLOR_BLUE +0x0000A460 TD_LS_BORDER_COLOR_ALPHA +0x0000A464 TD_CS_BORDER_COLOR_INDEX +0x0000A468 TD_CS_BORDER_COLOR_RED +0x0000A46C TD_CS_BORDER_COLOR_GREEN +0x0000A470 TD_CS_BORDER_COLOR_BLUE +0x0000A474 TD_CS_BORDER_COLOR_ALPHA +0x00028000 DB_RENDER_CONTROL +0x00028004 DB_COUNT_CONTROL +0x0002800C DB_RENDER_OVERRIDE +0x00028010 DB_RENDER_OVERRIDE2 +0x00028028 DB_STENCIL_CLEAR +0x0002802C DB_DEPTH_CLEAR +0x00028030 PA_SC_SCREEN_SCISSOR_TL +0x00028034 PA_SC_SCREEN_SCISSOR_BR +0x00028140 SQ_ALU_CONST_BUFFER_SIZE_PS_0 +0x00028144 SQ_ALU_CONST_BUFFER_SIZE_PS_1 +0x00028148 SQ_ALU_CONST_BUFFER_SIZE_PS_2 +0x0002814C SQ_ALU_CONST_BUFFER_SIZE_PS_3 +0x00028150 SQ_ALU_CONST_BUFFER_SIZE_PS_4 +0x00028154 SQ_ALU_CONST_BUFFER_SIZE_PS_5 +0x00028158 SQ_ALU_CONST_BUFFER_SIZE_PS_6 +0x0002815C SQ_ALU_CONST_BUFFER_SIZE_PS_7 +0x00028160 SQ_ALU_CONST_BUFFER_SIZE_PS_8 +0x00028164 SQ_ALU_CONST_BUFFER_SIZE_PS_9 +0x00028168 SQ_ALU_CONST_BUFFER_SIZE_PS_10 +0x0002816C SQ_ALU_CONST_BUFFER_SIZE_PS_11 +0x00028170 SQ_ALU_CONST_BUFFER_SIZE_PS_12 +0x00028174 SQ_ALU_CONST_BUFFER_SIZE_PS_13 +0x00028178 SQ_ALU_CONST_BUFFER_SIZE_PS_14 +0x0002817C SQ_ALU_CONST_BUFFER_SIZE_PS_15 +0x00028180 SQ_ALU_CONST_BUFFER_SIZE_VS_0 +0x00028184 SQ_ALU_CONST_BUFFER_SIZE_VS_1 +0x00028188 SQ_ALU_CONST_BUFFER_SIZE_VS_2 +0x0002818C SQ_ALU_CONST_BUFFER_SIZE_VS_3 +0x00028190 SQ_ALU_CONST_BUFFER_SIZE_VS_4 +0x00028194 SQ_ALU_CONST_BUFFER_SIZE_VS_5 +0x00028198 SQ_ALU_CONST_BUFFER_SIZE_VS_6 +0x0002819C SQ_ALU_CONST_BUFFER_SIZE_VS_7 +0x000281A0 SQ_ALU_CONST_BUFFER_SIZE_VS_8 +0x000281A4 SQ_ALU_CONST_BUFFER_SIZE_VS_9 +0x000281A8 SQ_ALU_CONST_BUFFER_SIZE_VS_10 +0x000281AC SQ_ALU_CONST_BUFFER_SIZE_VS_11 +0x000281B0 SQ_ALU_CONST_BUFFER_SIZE_VS_12 +0x000281B4 SQ_ALU_CONST_BUFFER_SIZE_VS_13 +0x000281B8 SQ_ALU_CONST_BUFFER_SIZE_VS_14 +0x000281BC SQ_ALU_CONST_BUFFER_SIZE_VS_15 +0x000281C0 SQ_ALU_CONST_BUFFER_SIZE_GS_0 +0x000281C4 SQ_ALU_CONST_BUFFER_SIZE_GS_1 +0x000281C8 SQ_ALU_CONST_BUFFER_SIZE_GS_2 +0x000281CC SQ_ALU_CONST_BUFFER_SIZE_GS_3 +0x000281D0 SQ_ALU_CONST_BUFFER_SIZE_GS_4 +0x000281D4 SQ_ALU_CONST_BUFFER_SIZE_GS_5 +0x000281D8 SQ_ALU_CONST_BUFFER_SIZE_GS_6 +0x000281DC SQ_ALU_CONST_BUFFER_SIZE_GS_7 +0x000281E0 SQ_ALU_CONST_BUFFER_SIZE_GS_8 +0x000281E4 SQ_ALU_CONST_BUFFER_SIZE_GS_9 +0x000281E8 SQ_ALU_CONST_BUFFER_SIZE_GS_10 +0x000281EC SQ_ALU_CONST_BUFFER_SIZE_GS_11 +0x000281F0 SQ_ALU_CONST_BUFFER_SIZE_GS_12 +0x000281F4 SQ_ALU_CONST_BUFFER_SIZE_GS_13 +0x000281F8 SQ_ALU_CONST_BUFFER_SIZE_GS_14 +0x000281FC SQ_ALU_CONST_BUFFER_SIZE_GS_15 +0x00028200 PA_SC_WINDOW_OFFSET +0x00028204 PA_SC_WINDOW_SCISSOR_TL +0x00028208 PA_SC_WINDOW_SCISSOR_BR +0x0002820C PA_SC_CLIPRECT_RULE +0x00028210 PA_SC_CLIPRECT_0_TL +0x00028214 PA_SC_CLIPRECT_0_BR +0x00028218 PA_SC_CLIPRECT_1_TL +0x0002821C PA_SC_CLIPRECT_1_BR +0x00028220 PA_SC_CLIPRECT_2_TL +0x00028224 PA_SC_CLIPRECT_2_BR +0x00028228 PA_SC_CLIPRECT_3_TL +0x0002822C PA_SC_CLIPRECT_3_BR +0x00028230 PA_SC_EDGERULE +0x00028234 PA_SU_HARDWARE_SCREEN_OFFSET +0x00028240 PA_SC_GENERIC_SCISSOR_TL +0x00028244 PA_SC_GENERIC_SCISSOR_BR +0x00028250 PA_SC_VPORT_SCISSOR_0_TL +0x00028254 PA_SC_VPORT_SCISSOR_0_BR +0x00028258 PA_SC_VPORT_SCISSOR_1_TL +0x0002825C PA_SC_VPORT_SCISSOR_1_BR +0x00028260 PA_SC_VPORT_SCISSOR_2_TL +0x00028264 PA_SC_VPORT_SCISSOR_2_BR +0x00028268 PA_SC_VPORT_SCISSOR_3_TL +0x0002826C PA_SC_VPORT_SCISSOR_3_BR +0x00028270 PA_SC_VPORT_SCISSOR_4_TL +0x00028274 PA_SC_VPORT_SCISSOR_4_BR +0x00028278 PA_SC_VPORT_SCISSOR_5_TL +0x0002827C PA_SC_VPORT_SCISSOR_5_BR +0x00028280 PA_SC_VPORT_SCISSOR_6_TL +0x00028284 PA_SC_VPORT_SCISSOR_6_BR +0x00028288 PA_SC_VPORT_SCISSOR_7_TL +0x0002828C PA_SC_VPORT_SCISSOR_7_BR +0x00028290 PA_SC_VPORT_SCISSOR_8_TL +0x00028294 PA_SC_VPORT_SCISSOR_8_BR +0x00028298 PA_SC_VPORT_SCISSOR_9_TL +0x0002829C PA_SC_VPORT_SCISSOR_9_BR +0x000282A0 PA_SC_VPORT_SCISSOR_10_TL +0x000282A4 PA_SC_VPORT_SCISSOR_10_BR +0x000282A8 PA_SC_VPORT_SCISSOR_11_TL +0x000282AC PA_SC_VPORT_SCISSOR_11_BR +0x000282B0 PA_SC_VPORT_SCISSOR_12_TL +0x000282B4 PA_SC_VPORT_SCISSOR_12_BR +0x000282B8 PA_SC_VPORT_SCISSOR_13_TL +0x000282BC PA_SC_VPORT_SCISSOR_13_BR +0x000282C0 PA_SC_VPORT_SCISSOR_14_TL +0x000282C4 PA_SC_VPORT_SCISSOR_14_BR +0x000282C8 PA_SC_VPORT_SCISSOR_15_TL +0x000282CC PA_SC_VPORT_SCISSOR_15_BR +0x000282D0 PA_SC_VPORT_ZMIN_0 +0x000282D4 PA_SC_VPORT_ZMAX_0 +0x000282D8 PA_SC_VPORT_ZMIN_1 +0x000282DC PA_SC_VPORT_ZMAX_1 +0x000282E0 PA_SC_VPORT_ZMIN_2 +0x000282E4 PA_SC_VPORT_ZMAX_2 +0x000282E8 PA_SC_VPORT_ZMIN_3 +0x000282EC PA_SC_VPORT_ZMAX_3 +0x000282F0 PA_SC_VPORT_ZMIN_4 +0x000282F4 PA_SC_VPORT_ZMAX_4 +0x000282F8 PA_SC_VPORT_ZMIN_5 +0x000282FC PA_SC_VPORT_ZMAX_5 +0x00028300 PA_SC_VPORT_ZMIN_6 +0x00028304 PA_SC_VPORT_ZMAX_6 +0x00028308 PA_SC_VPORT_ZMIN_7 +0x0002830C PA_SC_VPORT_ZMAX_7 +0x00028310 PA_SC_VPORT_ZMIN_8 +0x00028314 PA_SC_VPORT_ZMAX_8 +0x00028318 PA_SC_VPORT_ZMIN_9 +0x0002831C PA_SC_VPORT_ZMAX_9 +0x00028320 PA_SC_VPORT_ZMIN_10 +0x00028324 PA_SC_VPORT_ZMAX_10 +0x00028328 PA_SC_VPORT_ZMIN_11 +0x0002832C PA_SC_VPORT_ZMAX_11 +0x00028330 PA_SC_VPORT_ZMIN_12 +0x00028334 PA_SC_VPORT_ZMAX_12 +0x00028338 PA_SC_VPORT_ZMIN_13 +0x0002833C PA_SC_VPORT_ZMAX_13 +0x00028340 PA_SC_VPORT_ZMIN_14 +0x00028344 PA_SC_VPORT_ZMAX_14 +0x00028348 PA_SC_VPORT_ZMIN_15 +0x0002834C PA_SC_VPORT_ZMAX_15 +0x00028354 SX_SURFACE_SYNC +0x00028380 SQ_VTX_SEMANTIC_0 +0x00028384 SQ_VTX_SEMANTIC_1 +0x00028388 SQ_VTX_SEMANTIC_2 +0x0002838C SQ_VTX_SEMANTIC_3 +0x00028390 SQ_VTX_SEMANTIC_4 +0x00028394 SQ_VTX_SEMANTIC_5 +0x00028398 SQ_VTX_SEMANTIC_6 +0x0002839C SQ_VTX_SEMANTIC_7 +0x000283A0 SQ_VTX_SEMANTIC_8 +0x000283A4 SQ_VTX_SEMANTIC_9 +0x000283A8 SQ_VTX_SEMANTIC_10 +0x000283AC SQ_VTX_SEMANTIC_11 +0x000283B0 SQ_VTX_SEMANTIC_12 +0x000283B4 SQ_VTX_SEMANTIC_13 +0x000283B8 SQ_VTX_SEMANTIC_14 +0x000283BC SQ_VTX_SEMANTIC_15 +0x000283C0 SQ_VTX_SEMANTIC_16 +0x000283C4 SQ_VTX_SEMANTIC_17 +0x000283C8 SQ_VTX_SEMANTIC_18 +0x000283CC SQ_VTX_SEMANTIC_19 +0x000283D0 SQ_VTX_SEMANTIC_20 +0x000283D4 SQ_VTX_SEMANTIC_21 +0x000283D8 SQ_VTX_SEMANTIC_22 +0x000283DC SQ_VTX_SEMANTIC_23 +0x000283E0 SQ_VTX_SEMANTIC_24 +0x000283E4 SQ_VTX_SEMANTIC_25 +0x000283E8 SQ_VTX_SEMANTIC_26 +0x000283EC SQ_VTX_SEMANTIC_27 +0x000283F0 SQ_VTX_SEMANTIC_28 +0x000283F4 SQ_VTX_SEMANTIC_29 +0x000283F8 SQ_VTX_SEMANTIC_30 +0x000283FC SQ_VTX_SEMANTIC_31 +0x00028400 VGT_MAX_VTX_INDX +0x00028404 VGT_MIN_VTX_INDX +0x00028408 VGT_INDX_OFFSET +0x0002840C VGT_MULTI_PRIM_IB_RESET_INDX +0x00028410 SX_ALPHA_TEST_CONTROL +0x00028414 CB_BLEND_RED +0x00028418 CB_BLEND_GREEN +0x0002841C CB_BLEND_BLUE +0x00028420 CB_BLEND_ALPHA +0x00028430 DB_STENCILREFMASK +0x00028434 DB_STENCILREFMASK_BF +0x00028438 SX_ALPHA_REF +0x0002843C PA_CL_VPORT_XSCALE_0 +0x00028440 PA_CL_VPORT_XOFFSET_0 +0x00028444 PA_CL_VPORT_YSCALE_0 +0x00028448 PA_CL_VPORT_YOFFSET_0 +0x0002844C PA_CL_VPORT_ZSCALE_0 +0x00028450 PA_CL_VPORT_ZOFFSET_0 +0x00028454 PA_CL_VPORT_XSCALE_1 +0x00028458 PA_CL_VPORT_XOFFSET_1 +0x0002845C PA_CL_VPORT_YSCALE_1 +0x00028460 PA_CL_VPORT_YOFFSET_1 +0x00028464 PA_CL_VPORT_ZSCALE_1 +0x00028468 PA_CL_VPORT_ZOFFSET_1 +0x0002846C PA_CL_VPORT_XSCALE_2 +0x00028470 PA_CL_VPORT_XOFFSET_2 +0x00028474 PA_CL_VPORT_YSCALE_2 +0x00028478 PA_CL_VPORT_YOFFSET_2 +0x0002847C PA_CL_VPORT_ZSCALE_2 +0x00028480 PA_CL_VPORT_ZOFFSET_2 +0x00028484 PA_CL_VPORT_XSCALE_3 +0x00028488 PA_CL_VPORT_XOFFSET_3 +0x0002848C PA_CL_VPORT_YSCALE_3 +0x00028490 PA_CL_VPORT_YOFFSET_3 +0x00028494 PA_CL_VPORT_ZSCALE_3 +0x00028498 PA_CL_VPORT_ZOFFSET_3 +0x0002849C PA_CL_VPORT_XSCALE_4 +0x000284A0 PA_CL_VPORT_XOFFSET_4 +0x000284A4 PA_CL_VPORT_YSCALE_4 +0x000284A8 PA_CL_VPORT_YOFFSET_4 +0x000284AC PA_CL_VPORT_ZSCALE_4 +0x000284B0 PA_CL_VPORT_ZOFFSET_4 +0x000284B4 PA_CL_VPORT_XSCALE_5 +0x000284B8 PA_CL_VPORT_XOFFSET_5 +0x000284BC PA_CL_VPORT_YSCALE_5 +0x000284C0 PA_CL_VPORT_YOFFSET_5 +0x000284C4 PA_CL_VPORT_ZSCALE_5 +0x000284C8 PA_CL_VPORT_ZOFFSET_5 +0x000284CC PA_CL_VPORT_XSCALE_6 +0x000284D0 PA_CL_VPORT_XOFFSET_6 +0x000284D4 PA_CL_VPORT_YSCALE_6 +0x000284D8 PA_CL_VPORT_YOFFSET_6 +0x000284DC PA_CL_VPORT_ZSCALE_6 +0x000284E0 PA_CL_VPORT_ZOFFSET_6 +0x000284E4 PA_CL_VPORT_XSCALE_7 +0x000284E8 PA_CL_VPORT_XOFFSET_7 +0x000284EC PA_CL_VPORT_YSCALE_7 +0x000284F0 PA_CL_VPORT_YOFFSET_7 +0x000284F4 PA_CL_VPORT_ZSCALE_7 +0x000284F8 PA_CL_VPORT_ZOFFSET_7 +0x000284FC PA_CL_VPORT_XSCALE_8 +0x00028500 PA_CL_VPORT_XOFFSET_8 +0x00028504 PA_CL_VPORT_YSCALE_8 +0x00028508 PA_CL_VPORT_YOFFSET_8 +0x0002850C PA_CL_VPORT_ZSCALE_8 +0x00028510 PA_CL_VPORT_ZOFFSET_8 +0x00028514 PA_CL_VPORT_XSCALE_9 +0x00028518 PA_CL_VPORT_XOFFSET_9 +0x0002851C PA_CL_VPORT_YSCALE_9 +0x00028520 PA_CL_VPORT_YOFFSET_9 +0x00028524 PA_CL_VPORT_ZSCALE_9 +0x00028528 PA_CL_VPORT_ZOFFSET_9 +0x0002852C PA_CL_VPORT_XSCALE_10 +0x00028530 PA_CL_VPORT_XOFFSET_10 +0x00028534 PA_CL_VPORT_YSCALE_10 +0x00028538 PA_CL_VPORT_YOFFSET_10 +0x0002853C PA_CL_VPORT_ZSCALE_10 +0x00028540 PA_CL_VPORT_ZOFFSET_10 +0x00028544 PA_CL_VPORT_XSCALE_11 +0x00028548 PA_CL_VPORT_XOFFSET_11 +0x0002854C PA_CL_VPORT_YSCALE_11 +0x00028550 PA_CL_VPORT_YOFFSET_11 +0x00028554 PA_CL_VPORT_ZSCALE_11 +0x00028558 PA_CL_VPORT_ZOFFSET_11 +0x0002855C PA_CL_VPORT_XSCALE_12 +0x00028560 PA_CL_VPORT_XOFFSET_12 +0x00028564 PA_CL_VPORT_YSCALE_12 +0x00028568 PA_CL_VPORT_YOFFSET_12 +0x0002856C PA_CL_VPORT_ZSCALE_12 +0x00028570 PA_CL_VPORT_ZOFFSET_12 +0x00028574 PA_CL_VPORT_XSCALE_13 +0x00028578 PA_CL_VPORT_XOFFSET_13 +0x0002857C PA_CL_VPORT_YSCALE_13 +0x00028580 PA_CL_VPORT_YOFFSET_13 +0x00028584 PA_CL_VPORT_ZSCALE_13 +0x00028588 PA_CL_VPORT_ZOFFSET_13 +0x0002858C PA_CL_VPORT_XSCALE_14 +0x00028590 PA_CL_VPORT_XOFFSET_14 +0x00028594 PA_CL_VPORT_YSCALE_14 +0x00028598 PA_CL_VPORT_YOFFSET_14 +0x0002859C PA_CL_VPORT_ZSCALE_14 +0x000285A0 PA_CL_VPORT_ZOFFSET_14 +0x000285A4 PA_CL_VPORT_XSCALE_15 +0x000285A8 PA_CL_VPORT_XOFFSET_15 +0x000285AC PA_CL_VPORT_YSCALE_15 +0x000285B0 PA_CL_VPORT_YOFFSET_15 +0x000285B4 PA_CL_VPORT_ZSCALE_15 +0x000285B8 PA_CL_VPORT_ZOFFSET_15 +0x000285BC PA_CL_UCP_0_X +0x000285C0 PA_CL_UCP_0_Y +0x000285C4 PA_CL_UCP_0_Z +0x000285C8 PA_CL_UCP_0_W +0x000285CC PA_CL_UCP_1_X +0x000285D0 PA_CL_UCP_1_Y +0x000285D4 PA_CL_UCP_1_Z +0x000285D8 PA_CL_UCP_1_W +0x000285DC PA_CL_UCP_2_X +0x000285E0 PA_CL_UCP_2_Y +0x000285E4 PA_CL_UCP_2_Z +0x000285E8 PA_CL_UCP_2_W +0x000285EC PA_CL_UCP_3_X +0x000285F0 PA_CL_UCP_3_Y +0x000285F4 PA_CL_UCP_3_Z +0x000285F8 PA_CL_UCP_3_W +0x000285FC PA_CL_UCP_4_X +0x00028600 PA_CL_UCP_4_Y +0x00028604 PA_CL_UCP_4_Z +0x00028608 PA_CL_UCP_4_W +0x0002860C PA_CL_UCP_5_X +0x00028610 PA_CL_UCP_5_Y +0x00028614 PA_CL_UCP_5_Z +0x00028618 PA_CL_UCP_5_W +0x0002861C SPI_VS_OUT_ID_0 +0x00028620 SPI_VS_OUT_ID_1 +0x00028624 SPI_VS_OUT_ID_2 +0x00028628 SPI_VS_OUT_ID_3 +0x0002862C SPI_VS_OUT_ID_4 +0x00028630 SPI_VS_OUT_ID_5 +0x00028634 SPI_VS_OUT_ID_6 +0x00028638 SPI_VS_OUT_ID_7 +0x0002863C SPI_VS_OUT_ID_8 +0x00028640 SPI_VS_OUT_ID_9 +0x00028644 SPI_PS_INPUT_CNTL_0 +0x00028648 SPI_PS_INPUT_CNTL_1 +0x0002864C SPI_PS_INPUT_CNTL_2 +0x00028650 SPI_PS_INPUT_CNTL_3 +0x00028654 SPI_PS_INPUT_CNTL_4 +0x00028658 SPI_PS_INPUT_CNTL_5 +0x0002865C SPI_PS_INPUT_CNTL_6 +0x00028660 SPI_PS_INPUT_CNTL_7 +0x00028664 SPI_PS_INPUT_CNTL_8 +0x00028668 SPI_PS_INPUT_CNTL_9 +0x0002866C SPI_PS_INPUT_CNTL_10 +0x00028670 SPI_PS_INPUT_CNTL_11 +0x00028674 SPI_PS_INPUT_CNTL_12 +0x00028678 SPI_PS_INPUT_CNTL_13 +0x0002867C SPI_PS_INPUT_CNTL_14 +0x00028680 SPI_PS_INPUT_CNTL_15 +0x00028684 SPI_PS_INPUT_CNTL_16 +0x00028688 SPI_PS_INPUT_CNTL_17 +0x0002868C SPI_PS_INPUT_CNTL_18 +0x00028690 SPI_PS_INPUT_CNTL_19 +0x00028694 SPI_PS_INPUT_CNTL_20 +0x00028698 SPI_PS_INPUT_CNTL_21 +0x0002869C SPI_PS_INPUT_CNTL_22 +0x000286A0 SPI_PS_INPUT_CNTL_23 +0x000286A4 SPI_PS_INPUT_CNTL_24 +0x000286A8 SPI_PS_INPUT_CNTL_25 +0x000286AC SPI_PS_INPUT_CNTL_26 +0x000286B0 SPI_PS_INPUT_CNTL_27 +0x000286B4 SPI_PS_INPUT_CNTL_28 +0x000286B8 SPI_PS_INPUT_CNTL_29 +0x000286BC SPI_PS_INPUT_CNTL_30 +0x000286C0 SPI_PS_INPUT_CNTL_31 +0x000286C4 SPI_VS_OUT_CONFIG +0x000286C8 SPI_THREAD_GROUPING +0x000286CC SPI_PS_IN_CONTROL_0 +0x000286D0 SPI_PS_IN_CONTROL_1 +0x000286D4 SPI_INTERP_CONTROL_0 +0x000286D8 SPI_INPUT_Z +0x000286DC SPI_FOG_CNTL +0x000286E0 SPI_BARYC_CNTL +0x000286E4 SPI_PS_IN_CONTROL_2 +0x000286E8 SPI_COMPUTE_INPUT_CNTL +0x000286EC SPI_COMPUTE_NUM_THREAD_X +0x000286F0 SPI_COMPUTE_NUM_THREAD_Y +0x000286F4 SPI_COMPUTE_NUM_THREAD_Z +0x00028720 GDS_ADDR_BASE +0x00028724 GDS_ADDR_SIZE +0x00028728 GDS_ORDERED_WAVE_PER_SE +0x00028780 CB_BLEND0_CONTROL +0x00028784 CB_BLEND1_CONTROL +0x00028788 CB_BLEND2_CONTROL +0x0002878C CB_BLEND3_CONTROL +0x00028790 CB_BLEND4_CONTROL +0x00028794 CB_BLEND5_CONTROL +0x00028798 CB_BLEND6_CONTROL +0x0002879C CB_BLEND7_CONTROL +0x000287CC CS_COPY_STATE +0x000287D0 GFX_COPY_STATE +0x000287D4 PA_CL_POINT_X_RAD +0x000287D8 PA_CL_POINT_Y_RAD +0x000287DC PA_CL_POINT_SIZE +0x000287E0 PA_CL_POINT_CULL_RAD +0x00028808 CB_COLOR_CONTROL +0x0002880C DB_SHADER_CONTROL +0x00028810 PA_CL_CLIP_CNTL +0x00028814 PA_SU_SC_MODE_CNTL +0x00028818 PA_CL_VTE_CNTL +0x0002881C PA_CL_VS_OUT_CNTL +0x00028820 PA_CL_NANINF_CNTL +0x00028824 PA_SU_LINE_STIPPLE_CNTL +0x00028828 PA_SU_LINE_STIPPLE_SCALE +0x0002882C PA_SU_PRIM_FILTER_CNTL +0x00028838 SQ_DYN_GPR_RESOURCE_LIMIT_1 +0x00028844 SQ_PGM_RESOURCES_PS +0x00028848 SQ_PGM_RESOURCES_2_PS +0x0002884C SQ_PGM_EXPORTS_PS +0x00028860 SQ_PGM_RESOURCES_VS +0x00028864 SQ_PGM_RESOURCES_2_VS +0x00028878 SQ_PGM_RESOURCES_GS +0x0002887C SQ_PGM_RESOURCES_2_GS +0x00028890 SQ_PGM_RESOURCES_ES +0x00028894 SQ_PGM_RESOURCES_2_ES +0x000288A8 SQ_PGM_RESOURCES_FS +0x000288BC SQ_PGM_RESOURCES_HS +0x000288C0 SQ_PGM_RESOURCES_2_HS +0x000288D4 SQ_PGM_RESOURCES_LS +0x000288D8 SQ_PGM_RESOURCES_2_LS +0x000288E8 SQ_LDS_ALLOC +0x000288EC SQ_LDS_ALLOC_PS +0x000288F0 SQ_VTX_SEMANTIC_CLEAR +0x00028A00 PA_SU_POINT_SIZE +0x00028A04 PA_SU_POINT_MINMAX +0x00028A08 PA_SU_LINE_CNTL +0x00028A0C PA_SC_LINE_STIPPLE +0x00028A10 VGT_OUTPUT_PATH_CNTL +0x00028A14 VGT_HOS_CNTL +0x00028A18 VGT_HOS_MAX_TESS_LEVEL +0x00028A1C VGT_HOS_MIN_TESS_LEVEL +0x00028A20 VGT_HOS_REUSE_DEPTH +0x00028A24 VGT_GROUP_PRIM_TYPE +0x00028A28 VGT_GROUP_FIRST_DECR +0x00028A2C VGT_GROUP_DECR +0x00028A30 VGT_GROUP_VECT_0_CNTL +0x00028A34 VGT_GROUP_VECT_1_CNTL +0x00028A38 VGT_GROUP_VECT_0_FMT_CNTL +0x00028A3C VGT_GROUP_VECT_1_FMT_CNTL +0x00028A40 VGT_GS_MODE +0x00028A48 PA_SC_MODE_CNTL_0 +0x00028A4C PA_SC_MODE_CNTL_1 +0x00028A50 VGT_ENHANCE +0x00028A54 VGT_GS_PER_ES +0x00028A58 VGT_ES_PER_GS +0x00028A5C VGT_GS_PER_VS +0x00028A6C VGT_GS_OUT_PRIM_TYPE +0x00028A84 VGT_PRIMITIVEID_EN +0x00028A94 VGT_MULTI_PRIM_IB_RESET_EN +0x00028AA0 VGT_INSTANCE_STEP_RATE_0 +0x00028AA4 VGT_INSTANCE_STEP_RATE_1 +0x00028AB4 VGT_REUSE_OFF +0x00028AB8 VGT_VTX_CNT_EN +0x00028AC0 DB_SRESULTS_COMPARE_STATE0 +0x00028AC4 DB_SRESULTS_COMPARE_STATE1 +0x00028AC8 DB_PRELOAD_CONTROL +0x00028AD4 VGT_STRMOUT_VTX_STRIDE_0 +0x00028AE4 VGT_STRMOUT_VTX_STRIDE_1 +0x00028AF4 VGT_STRMOUT_VTX_STRIDE_2 +0x00028B04 VGT_STRMOUT_VTX_STRIDE_3 +0x00028B28 VGT_STRMOUT_DRAW_OPAQUE_OFFSET +0x00028B2C VGT_STRMOUT_DRAW_OPAQUE_BUFFER_FILLED_SIZE +0x00028B30 VGT_STRMOUT_DRAW_OPAQUE_VERTEX_STRIDE +0x00028B38 VGT_GS_MAX_VERT_OUT +0x00028B54 VGT_SHADER_STAGES_EN +0x00028B58 VGT_LS_HS_CONFIG +0x00028B5C VGT_LS_SIZE +0x00028B60 VGT_HS_SIZE +0x00028B64 VGT_LS_HS_ALLOC +0x00028B68 VGT_HS_PATCH_CONST +0x00028B6C VGT_TF_PARAM +0x00028B70 DB_ALPHA_TO_MASK +0x00028B74 VGT_DISPATCH_INITIATOR +0x00028B78 PA_SU_POLY_OFFSET_DB_FMT_CNTL +0x00028B7C PA_SU_POLY_OFFSET_CLAMP +0x00028B80 PA_SU_POLY_OFFSET_FRONT_SCALE +0x00028B84 PA_SU_POLY_OFFSET_FRONT_OFFSET +0x00028B88 PA_SU_POLY_OFFSET_BACK_SCALE +0x00028B8C PA_SU_POLY_OFFSET_BACK_OFFSET +0x00028B74 VGT_GS_INSTANCE_CNT +0x00028C00 PA_SC_LINE_CNTL +0x00028C08 PA_SU_VTX_CNTL +0x00028C0C PA_CL_GB_VERT_CLIP_ADJ +0x00028C10 PA_CL_GB_VERT_DISC_ADJ +0x00028C14 PA_CL_GB_HORZ_CLIP_ADJ +0x00028C18 PA_CL_GB_HORZ_DISC_ADJ +0x00028C1C PA_SC_AA_SAMPLE_LOCS_0 +0x00028C20 PA_SC_AA_SAMPLE_LOCS_1 +0x00028C24 PA_SC_AA_SAMPLE_LOCS_2 +0x00028C28 PA_SC_AA_SAMPLE_LOCS_3 +0x00028C2C PA_SC_AA_SAMPLE_LOCS_4 +0x00028C30 PA_SC_AA_SAMPLE_LOCS_5 +0x00028C34 PA_SC_AA_SAMPLE_LOCS_6 +0x00028C38 PA_SC_AA_SAMPLE_LOCS_7 +0x00028C3C PA_SC_AA_MASK +0x00028C78 CB_COLOR0_DIM +0x00028CB4 CB_COLOR1_DIM +0x00028CF0 CB_COLOR2_DIM +0x00028D2C CB_COLOR3_DIM +0x00028D68 CB_COLOR4_DIM +0x00028DA4 CB_COLOR5_DIM +0x00028DE0 CB_COLOR6_DIM +0x00028E1C CB_COLOR7_DIM +0x00028E58 CB_COLOR8_DIM +0x00028E74 CB_COLOR9_DIM +0x00028E90 CB_COLOR10_DIM +0x00028EAC CB_COLOR11_DIM +0x00028C8C CB_COLOR0_CLEAR_WORD0 +0x00028C90 CB_COLOR0_CLEAR_WORD1 +0x00028C94 CB_COLOR0_CLEAR_WORD2 +0x00028C98 CB_COLOR0_CLEAR_WORD3 +0x00028CC8 CB_COLOR1_CLEAR_WORD0 +0x00028CCC CB_COLOR1_CLEAR_WORD1 +0x00028CD0 CB_COLOR1_CLEAR_WORD2 +0x00028CD4 CB_COLOR1_CLEAR_WORD3 +0x00028D04 CB_COLOR2_CLEAR_WORD0 +0x00028D08 CB_COLOR2_CLEAR_WORD1 +0x00028D0C CB_COLOR2_CLEAR_WORD2 +0x00028D10 CB_COLOR2_CLEAR_WORD3 +0x00028D40 CB_COLOR3_CLEAR_WORD0 +0x00028D44 CB_COLOR3_CLEAR_WORD1 +0x00028D48 CB_COLOR3_CLEAR_WORD2 +0x00028D4C CB_COLOR3_CLEAR_WORD3 +0x00028D7C CB_COLOR4_CLEAR_WORD0 +0x00028D80 CB_COLOR4_CLEAR_WORD1 +0x00028D84 CB_COLOR4_CLEAR_WORD2 +0x00028D88 CB_COLOR4_CLEAR_WORD3 +0x00028DB8 CB_COLOR5_CLEAR_WORD0 +0x00028DBC CB_COLOR5_CLEAR_WORD1 +0x00028DC0 CB_COLOR5_CLEAR_WORD2 +0x00028DC4 CB_COLOR5_CLEAR_WORD3 +0x00028DF4 CB_COLOR6_CLEAR_WORD0 +0x00028DF8 CB_COLOR6_CLEAR_WORD1 +0x00028DFC CB_COLOR6_CLEAR_WORD2 +0x00028E00 CB_COLOR6_CLEAR_WORD3 +0x00028E30 CB_COLOR7_CLEAR_WORD0 +0x00028E34 CB_COLOR7_CLEAR_WORD1 +0x00028E38 CB_COLOR7_CLEAR_WORD2 +0x00028E3C CB_COLOR7_CLEAR_WORD3 +0x00028F80 SQ_ALU_CONST_BUFFER_SIZE_HS_0 +0x00028F84 SQ_ALU_CONST_BUFFER_SIZE_HS_1 +0x00028F88 SQ_ALU_CONST_BUFFER_SIZE_HS_2 +0x00028F8C SQ_ALU_CONST_BUFFER_SIZE_HS_3 +0x00028F90 SQ_ALU_CONST_BUFFER_SIZE_HS_4 +0x00028F94 SQ_ALU_CONST_BUFFER_SIZE_HS_5 +0x00028F98 SQ_ALU_CONST_BUFFER_SIZE_HS_6 +0x00028F9C SQ_ALU_CONST_BUFFER_SIZE_HS_7 +0x00028FA0 SQ_ALU_CONST_BUFFER_SIZE_HS_8 +0x00028FA4 SQ_ALU_CONST_BUFFER_SIZE_HS_9 +0x00028FA8 SQ_ALU_CONST_BUFFER_SIZE_HS_10 +0x00028FAC SQ_ALU_CONST_BUFFER_SIZE_HS_11 +0x00028FB0 SQ_ALU_CONST_BUFFER_SIZE_HS_12 +0x00028FB4 SQ_ALU_CONST_BUFFER_SIZE_HS_13 +0x00028FB8 SQ_ALU_CONST_BUFFER_SIZE_HS_14 +0x00028FBC SQ_ALU_CONST_BUFFER_SIZE_HS_15 +0x00028FC0 SQ_ALU_CONST_BUFFER_SIZE_LS_0 +0x00028FC4 SQ_ALU_CONST_BUFFER_SIZE_LS_1 +0x00028FC8 SQ_ALU_CONST_BUFFER_SIZE_LS_2 +0x00028FCC SQ_ALU_CONST_BUFFER_SIZE_LS_3 +0x00028FD0 SQ_ALU_CONST_BUFFER_SIZE_LS_4 +0x00028FD4 SQ_ALU_CONST_BUFFER_SIZE_LS_5 +0x00028FD8 SQ_ALU_CONST_BUFFER_SIZE_LS_6 +0x00028FDC SQ_ALU_CONST_BUFFER_SIZE_LS_7 +0x00028FE0 SQ_ALU_CONST_BUFFER_SIZE_LS_8 +0x00028FE4 SQ_ALU_CONST_BUFFER_SIZE_LS_9 +0x00028FE8 SQ_ALU_CONST_BUFFER_SIZE_LS_10 +0x00028FEC SQ_ALU_CONST_BUFFER_SIZE_LS_11 +0x00028FF0 SQ_ALU_CONST_BUFFER_SIZE_LS_12 +0x00028FF4 SQ_ALU_CONST_BUFFER_SIZE_LS_13 +0x00028FF8 SQ_ALU_CONST_BUFFER_SIZE_LS_14 +0x00028FFC SQ_ALU_CONST_BUFFER_SIZE_LS_15 +0x0003CFF0 SQ_VTX_BASE_VTX_LOC +0x0003CFF4 SQ_VTX_START_INST_LOC +0x0003FF00 SQ_TEX_SAMPLER_CLEAR +0x0003FF04 SQ_TEX_RESOURCE_CLEAR +0x0003FF08 SQ_LOOP_BOOL_CLEAR diff --git a/sys/dev/drm2/radeon/reg_srcs/r100 b/sys/dev/drm2/radeon/reg_srcs/r100 new file mode 100644 index 00000000000..f7ee062f118 --- /dev/null +++ b/sys/dev/drm2/radeon/reg_srcs/r100 @@ -0,0 +1,105 @@ +r100 0x3294 +0x1434 SRC_Y_X +0x1438 DST_Y_X +0x143C DST_HEIGHT_WIDTH +0x146C DP_GUI_MASTER_CNTL +0x1474 BRUSH_Y_X +0x1478 DP_BRUSH_BKGD_CLR +0x147C DP_BRUSH_FRGD_CLR +0x1480 BRUSH_DATA0 +0x1484 BRUSH_DATA1 +0x1598 DST_WIDTH_HEIGHT +0x15C0 CLR_CMP_CNTL +0x15C4 CLR_CMP_CLR_SRC +0x15C8 CLR_CMP_CLR_DST +0x15CC CLR_CMP_MSK +0x15D8 DP_SRC_FRGD_CLR +0x15DC DP_SRC_BKGD_CLR +0x1600 DST_LINE_START +0x1604 DST_LINE_END +0x1608 DST_LINE_PATCOUNT +0x16C0 DP_CNTL +0x16CC DP_WRITE_MSK +0x16D0 DP_CNTL_XDIR_YDIR_YMAJOR +0x16E8 DEFAULT_SC_BOTTOM_RIGHT +0x16EC SC_TOP_LEFT +0x16F0 SC_BOTTOM_RIGHT +0x16F4 SRC_SC_BOTTOM_RIGHT +0x1714 DSTCACHE_CTLSTAT +0x1720 WAIT_UNTIL +0x172C RBBM_GUICNTL +0x1810 FOG_3D_TABLE_START +0x1814 FOG_3D_TABLE_END +0x1a14 FOG_TABLE_INDEX +0x1a18 FOG_TABLE_DATA +0x1c14 PP_MISC +0x1c18 PP_FOG_COLOR +0x1c1c RE_SOLID_COLOR +0x1c20 RB3D_BLENDCNTL +0x1c4c SE_CNTL +0x1c50 SE_COORD_FMT +0x1c60 PP_TXCBLEND_0 +0x1c64 PP_TXABLEND_0 +0x1c68 PP_TFACTOR_0 +0x1c78 PP_TXCBLEND_1 +0x1c7c PP_TXABLEND_1 +0x1c80 PP_TFACTOR_1 +0x1c90 PP_TXCBLEND_2 +0x1c94 PP_TXABLEND_2 +0x1c98 PP_TFACTOR_2 +0x1cc8 RE_STIPPLE_ADDR +0x1ccc RE_STIPPLE_DATA +0x1cd0 RE_LINE_PATTERN +0x1cd4 RE_LINE_STATE +0x1d40 PP_BORDER_COLOR0 +0x1d44 PP_BORDER_COLOR1 +0x1d48 PP_BORDER_COLOR2 +0x1d7c RB3D_STENCILREFMASK +0x1d80 RB3D_ROPCNTL +0x1d84 RB3D_PLANEMASK +0x1d98 VAP_VPORT_XSCALE +0x1d9C VAP_VPORT_XOFFSET +0x1da0 VAP_VPORT_YSCALE +0x1da4 VAP_VPORT_YOFFSET +0x1da8 VAP_VPORT_ZSCALE +0x1dac VAP_VPORT_ZOFFSET +0x1db0 SE_ZBIAS_FACTOR +0x1db4 SE_ZBIAS_CONSTANT +0x1db8 SE_LINE_WIDTH +0x2140 SE_CNTL_STATUS +0x2200 SE_TCL_VECTOR_INDX_REG +0x2204 SE_TCL_VECTOR_DATA_REG +0x2208 SE_TCL_SCALAR_INDX_REG +0x220c SE_TCL_SCALAR_DATA_REG +0x2210 SE_TCL_MATERIAL_EMISSIVE_RED +0x2214 SE_TCL_MATERIAL_EMISSIVE_GREEN +0x2218 SE_TCL_MATERIAL_EMISSIVE_BLUE +0x221c SE_TCL_MATERIAL_EMISSIVE_ALPHA +0x2220 SE_TCL_MATERIAL_AMBIENT_RED +0x2224 SE_TCL_MATERIAL_AMBIENT_GREEN +0x2228 SE_TCL_MATERIAL_AMBIENT_BLUE +0x222c SE_TCL_MATERIAL_AMBIENT_ALPHA +0x2230 SE_TCL_MATERIAL_DIFFUSE_RED +0x2234 SE_TCL_MATERIAL_DIFFUSE_GREEN +0x2238 SE_TCL_MATERIAL_DIFFUSE_BLUE +0x223c SE_TCL_MATERIAL_DIFFUSE_ALPHA +0x2240 SE_TCL_MATERIAL_SPECULAR_RED +0x2244 SE_TCL_MATERIAL_SPECULAR_GREEN +0x2248 SE_TCL_MATERIAL_SPECULAR_BLUE +0x224c SE_TCL_MATERIAL_SPECULAR_ALPHA +0x2250 SE_TCL_SHININESS +0x2254 SE_TCL_OUTPUT_VTX_FMT +0x2258 SE_TCL_OUTPUT_VTX_SEL +0x225c SE_TCL_MATRIX_SELECT_0 +0x2260 SE_TCL_MATRIX_SELECT_1 +0x2264 SE_TCL_UCP_VERT_BLEND_CNTL +0x2268 SE_TCL_TEXTURE_PROC_CTL +0x226c SE_TCL_LIGHT_MODEL_CTL +0x2270 SE_TCL_PER_LIGHT_CTL_0 +0x2274 SE_TCL_PER_LIGHT_CTL_1 +0x2278 SE_TCL_PER_LIGHT_CTL_2 +0x227c SE_TCL_PER_LIGHT_CTL_3 +0x2284 SE_TCL_STATE_FLUSH +0x26c0 RE_TOP_LEFT +0x26c4 RE_MISC +0x3290 RB3D_ZPASS_DATA diff --git a/sys/dev/drm2/radeon/reg_srcs/r200 b/sys/dev/drm2/radeon/reg_srcs/r200 new file mode 100644 index 00000000000..c29ac434ac9 --- /dev/null +++ b/sys/dev/drm2/radeon/reg_srcs/r200 @@ -0,0 +1,186 @@ +r200 0x3294 +0x1434 SRC_Y_X +0x1438 DST_Y_X +0x143C DST_HEIGHT_WIDTH +0x146C DP_GUI_MASTER_CNTL +0x1474 BRUSH_Y_X +0x1478 DP_BRUSH_BKGD_CLR +0x147C DP_BRUSH_FRGD_CLR +0x1480 BRUSH_DATA0 +0x1484 BRUSH_DATA1 +0x1598 DST_WIDTH_HEIGHT +0x15C0 CLR_CMP_CNTL +0x15C4 CLR_CMP_CLR_SRC +0x15C8 CLR_CMP_CLR_DST +0x15CC CLR_CMP_MSK +0x15D8 DP_SRC_FRGD_CLR +0x15DC DP_SRC_BKGD_CLR +0x1600 DST_LINE_START +0x1604 DST_LINE_END +0x1608 DST_LINE_PATCOUNT +0x16C0 DP_CNTL +0x16CC DP_WRITE_MSK +0x16D0 DP_CNTL_XDIR_YDIR_YMAJOR +0x16E8 DEFAULT_SC_BOTTOM_RIGHT +0x16EC SC_TOP_LEFT +0x16F0 SC_BOTTOM_RIGHT +0x16F4 SRC_SC_BOTTOM_RIGHT +0x1714 DSTCACHE_CTLSTAT +0x1720 WAIT_UNTIL +0x172C RBBM_GUICNTL +0x1c14 PP_MISC +0x1c18 PP_FOG_COLOR +0x1c1c RE_SOLID_COLOR +0x1c20 RB3D_BLENDCNTL +0x1c4c SE_CNTL +0x1c50 RE_CNTL +0x1cc8 RE_STIPPLE_ADDR +0x1ccc RE_STIPPLE_DATA +0x1cd0 RE_LINE_PATTERN +0x1cd4 RE_LINE_STATE +0x1cd8 RE_SCISSOR_TL_0 +0x1cdc RE_SCISSOR_BR_0 +0x1ce0 RE_SCISSOR_TL_1 +0x1ce4 RE_SCISSOR_BR_1 +0x1ce8 RE_SCISSOR_TL_2 +0x1cec RE_SCISSOR_BR_2 +0x1d60 RB3D_DEPTHXY_OFFSET +0x1d7c RB3D_STENCILREFMASK +0x1d80 RB3D_ROPCNTL +0x1d84 RB3D_PLANEMASK +0x1d98 VAP_VPORT_XSCALE +0x1d9c VAP_VPORT_XOFFSET +0x1da0 VAP_VPORT_YSCALE +0x1da4 VAP_VPORT_YOFFSET +0x1da8 VAP_VPORT_ZSCALE +0x1dac VAP_VPORT_ZOFFSET +0x1db0 SE_ZBIAS_FACTOR +0x1db4 SE_ZBIAS_CONSTANT +0x1db8 SE_LINE_WIDTH +0x2080 SE_VAP_CNTL +0x2090 SE_TCL_OUTPUT_VTX_FMT_0 +0x2094 SE_TCL_OUTPUT_VTX_FMT_1 +0x20b0 SE_VTE_CNTL +0x2140 SE_CNTL_STATUS +0x2180 SE_VTX_STATE_CNTL +0x2200 SE_TCL_VECTOR_INDX_REG +0x2204 SE_TCL_VECTOR_DATA_REG +0x2208 SE_TCL_SCALAR_INDX_REG +0x220c SE_TCL_SCALAR_DATA_REG +0x2230 SE_TCL_MATRIX_SEL_0 +0x2234 SE_TCL_MATRIX_SEL_1 +0x2238 SE_TCL_MATRIX_SEL_2 +0x223c SE_TCL_MATRIX_SEL_3 +0x2240 SE_TCL_MATRIX_SEL_4 +0x2250 SE_TCL_OUTPUT_VTX_COMP_SEL +0x2254 SE_TCL_INPUT_VTX_VECTOR_ADDR_0 +0x2258 SE_TCL_INPUT_VTX_VECTOR_ADDR_1 +0x225c SE_TCL_INPUT_VTX_VECTOR_ADDR_2 +0x2260 SE_TCL_INPUT_VTX_VECTOR_ADDR_3 +0x2268 SE_TCL_LIGHT_MODEL_CTL_0 +0x226c SE_TCL_LIGHT_MODEL_CTL_1 +0x2270 SE_TCL_PER_LIGHT_CTL_0 +0x2274 SE_TCL_PER_LIGHT_CTL_1 +0x2278 SE_TCL_PER_LIGHT_CTL_2 +0x227c SE_TCL_PER_LIGHT_CTL_3 +0x2284 VAP_PVS_STATE_FLUSH_REG +0x22a8 SE_TCL_TEX_PROC_CTL_2 +0x22ac SE_TCL_TEX_PROC_CTL_3 +0x22b0 SE_TCL_TEX_PROC_CTL_0 +0x22b4 SE_TCL_TEX_PROC_CTL_1 +0x22b8 SE_TCL_TEX_CYL_WRAP_CTL +0x22c0 SE_TCL_UCP_VERT_BLEND_CNTL +0x22c4 SE_TCL_POINT_SPRITE_CNTL +0x22d0 SE_PVS_CNTL +0x22d4 SE_PVS_CONST_CNTL +0x2648 RE_POINTSIZE +0x26c0 RE_TOP_LEFT +0x26c4 RE_MISC +0x26f0 RE_AUX_SCISSOR_CNTL +0x2c14 PP_BORDER_COLOR_0 +0x2c34 PP_BORDER_COLOR_1 +0x2c54 PP_BORDER_COLOR_2 +0x2c74 PP_BORDER_COLOR_3 +0x2c94 PP_BORDER_COLOR_4 +0x2cb4 PP_BORDER_COLOR_5 +0x2cc4 PP_CNTL_X +0x2cf8 PP_TRI_PERF +0x2cfc PP_PERF_CNTL +0x2d9c PP_TAM_DEBUG3 +0x2ee0 PP_TFACTOR_0 +0x2ee4 PP_TFACTOR_1 +0x2ee8 PP_TFACTOR_2 +0x2eec PP_TFACTOR_3 +0x2ef0 PP_TFACTOR_4 +0x2ef4 PP_TFACTOR_5 +0x2ef8 PP_TFACTOR_6 +0x2efc PP_TFACTOR_7 +0x2f00 PP_TXCBLEND_0 +0x2f04 PP_TXCBLEND2_0 +0x2f08 PP_TXABLEND_0 +0x2f0c PP_TXABLEND2_0 +0x2f10 PP_TXCBLEND_1 +0x2f14 PP_TXCBLEND2_1 +0x2f18 PP_TXABLEND_1 +0x2f1c PP_TXABLEND2_1 +0x2f20 PP_TXCBLEND_2 +0x2f24 PP_TXCBLEND2_2 +0x2f28 PP_TXABLEND_2 +0x2f2c PP_TXABLEND2_2 +0x2f30 PP_TXCBLEND_3 +0x2f34 PP_TXCBLEND2_3 +0x2f38 PP_TXABLEND_3 +0x2f3c PP_TXABLEND2_3 +0x2f40 PP_TXCBLEND_4 +0x2f44 PP_TXCBLEND2_4 +0x2f48 PP_TXABLEND_4 +0x2f4c PP_TXABLEND2_4 +0x2f50 PP_TXCBLEND_5 +0x2f54 PP_TXCBLEND2_5 +0x2f58 PP_TXABLEND_5 +0x2f5c PP_TXABLEND2_5 +0x2f60 PP_TXCBLEND_6 +0x2f64 PP_TXCBLEND2_6 +0x2f68 PP_TXABLEND_6 +0x2f6c PP_TXABLEND2_6 +0x2f70 PP_TXCBLEND_7 +0x2f74 PP_TXCBLEND2_7 +0x2f78 PP_TXABLEND_7 +0x2f7c PP_TXABLEND2_7 +0x2f80 PP_TXCBLEND_8 +0x2f84 PP_TXCBLEND2_8 +0x2f88 PP_TXABLEND_8 +0x2f8c PP_TXABLEND2_8 +0x2f90 PP_TXCBLEND_9 +0x2f94 PP_TXCBLEND2_9 +0x2f98 PP_TXABLEND_9 +0x2f9c PP_TXABLEND2_9 +0x2fa0 PP_TXCBLEND_10 +0x2fa4 PP_TXCBLEND2_10 +0x2fa8 PP_TXABLEND_10 +0x2fac PP_TXABLEND2_10 +0x2fb0 PP_TXCBLEND_11 +0x2fb4 PP_TXCBLEND2_11 +0x2fb8 PP_TXABLEND_11 +0x2fbc PP_TXABLEND2_11 +0x2fc0 PP_TXCBLEND_12 +0x2fc4 PP_TXCBLEND2_12 +0x2fc8 PP_TXABLEND_12 +0x2fcc PP_TXABLEND2_12 +0x2fd0 PP_TXCBLEND_13 +0x2fd4 PP_TXCBLEND2_13 +0x2fd8 PP_TXABLEND_13 +0x2fdc PP_TXABLEND2_13 +0x2fe0 PP_TXCBLEND_14 +0x2fe4 PP_TXCBLEND2_14 +0x2fe8 PP_TXABLEND_14 +0x2fec PP_TXABLEND2_14 +0x2ff0 PP_TXCBLEND_15 +0x2ff4 PP_TXCBLEND2_15 +0x2ff8 PP_TXABLEND_15 +0x2ffc PP_TXABLEND2_15 +0x3218 RB3D_BLENCOLOR +0x321c RB3D_ABLENDCNTL +0x3220 RB3D_CBLENDCNTL +0x3290 RB3D_ZPASS_DATA + diff --git a/sys/dev/drm2/radeon/reg_srcs/r300 b/sys/dev/drm2/radeon/reg_srcs/r300 new file mode 100644 index 00000000000..e8a1786b642 --- /dev/null +++ b/sys/dev/drm2/radeon/reg_srcs/r300 @@ -0,0 +1,714 @@ +r300 0x4f60 +0x1434 SRC_Y_X +0x1438 DST_Y_X +0x143C DST_HEIGHT_WIDTH +0x146C DP_GUI_MASTER_CNTL +0x1474 BRUSH_Y_X +0x1478 DP_BRUSH_BKGD_CLR +0x147C DP_BRUSH_FRGD_CLR +0x1480 BRUSH_DATA0 +0x1484 BRUSH_DATA1 +0x1598 DST_WIDTH_HEIGHT +0x15C0 CLR_CMP_CNTL +0x15C4 CLR_CMP_CLR_SRC +0x15C8 CLR_CMP_CLR_DST +0x15CC CLR_CMP_MSK +0x15D8 DP_SRC_FRGD_CLR +0x15DC DP_SRC_BKGD_CLR +0x1600 DST_LINE_START +0x1604 DST_LINE_END +0x1608 DST_LINE_PATCOUNT +0x16C0 DP_CNTL +0x16CC DP_WRITE_MSK +0x16D0 DP_CNTL_XDIR_YDIR_YMAJOR +0x16E8 DEFAULT_SC_BOTTOM_RIGHT +0x16EC SC_TOP_LEFT +0x16F0 SC_BOTTOM_RIGHT +0x16F4 SRC_SC_BOTTOM_RIGHT +0x1714 DSTCACHE_CTLSTAT +0x1720 WAIT_UNTIL +0x172C RBBM_GUICNTL +0x1D98 VAP_VPORT_XSCALE +0x1D9C VAP_VPORT_XOFFSET +0x1DA0 VAP_VPORT_YSCALE +0x1DA4 VAP_VPORT_YOFFSET +0x1DA8 VAP_VPORT_ZSCALE +0x1DAC VAP_VPORT_ZOFFSET +0x2080 VAP_CNTL +0x2090 VAP_OUT_VTX_FMT_0 +0x2094 VAP_OUT_VTX_FMT_1 +0x20B0 VAP_VTE_CNTL +0x2138 VAP_VF_MIN_VTX_INDX +0x2140 VAP_CNTL_STATUS +0x2150 VAP_PROG_STREAM_CNTL_0 +0x2154 VAP_PROG_STREAM_CNTL_1 +0x2158 VAP_PROG_STREAM_CNTL_2 +0x215C VAP_PROG_STREAM_CNTL_3 +0x2160 VAP_PROG_STREAM_CNTL_4 +0x2164 VAP_PROG_STREAM_CNTL_5 +0x2168 VAP_PROG_STREAM_CNTL_6 +0x216C VAP_PROG_STREAM_CNTL_7 +0x2180 VAP_VTX_STATE_CNTL +0x2184 VAP_VSM_VTX_ASSM +0x2188 VAP_VTX_STATE_IND_REG_0 +0x218C VAP_VTX_STATE_IND_REG_1 +0x2190 VAP_VTX_STATE_IND_REG_2 +0x2194 VAP_VTX_STATE_IND_REG_3 +0x2198 VAP_VTX_STATE_IND_REG_4 +0x219C VAP_VTX_STATE_IND_REG_5 +0x21A0 VAP_VTX_STATE_IND_REG_6 +0x21A4 VAP_VTX_STATE_IND_REG_7 +0x21A8 VAP_VTX_STATE_IND_REG_8 +0x21AC VAP_VTX_STATE_IND_REG_9 +0x21B0 VAP_VTX_STATE_IND_REG_10 +0x21B4 VAP_VTX_STATE_IND_REG_11 +0x21B8 VAP_VTX_STATE_IND_REG_12 +0x21BC VAP_VTX_STATE_IND_REG_13 +0x21C0 VAP_VTX_STATE_IND_REG_14 +0x21C4 VAP_VTX_STATE_IND_REG_15 +0x21DC VAP_PSC_SGN_NORM_CNTL +0x21E0 VAP_PROG_STREAM_CNTL_EXT_0 +0x21E4 VAP_PROG_STREAM_CNTL_EXT_1 +0x21E8 VAP_PROG_STREAM_CNTL_EXT_2 +0x21EC VAP_PROG_STREAM_CNTL_EXT_3 +0x21F0 VAP_PROG_STREAM_CNTL_EXT_4 +0x21F4 VAP_PROG_STREAM_CNTL_EXT_5 +0x21F8 VAP_PROG_STREAM_CNTL_EXT_6 +0x21FC VAP_PROG_STREAM_CNTL_EXT_7 +0x2200 VAP_PVS_VECTOR_INDX_REG +0x2204 VAP_PVS_VECTOR_DATA_REG +0x2208 VAP_PVS_VECTOR_DATA_REG_128 +0x221C VAP_CLIP_CNTL +0x2220 VAP_GB_VERT_CLIP_ADJ +0x2224 VAP_GB_VERT_DISC_ADJ +0x2228 VAP_GB_HORZ_CLIP_ADJ +0x222C VAP_GB_HORZ_DISC_ADJ +0x2230 VAP_PVS_FLOW_CNTL_ADDRS_0 +0x2234 VAP_PVS_FLOW_CNTL_ADDRS_1 +0x2238 VAP_PVS_FLOW_CNTL_ADDRS_2 +0x223C VAP_PVS_FLOW_CNTL_ADDRS_3 +0x2240 VAP_PVS_FLOW_CNTL_ADDRS_4 +0x2244 VAP_PVS_FLOW_CNTL_ADDRS_5 +0x2248 VAP_PVS_FLOW_CNTL_ADDRS_6 +0x224C VAP_PVS_FLOW_CNTL_ADDRS_7 +0x2250 VAP_PVS_FLOW_CNTL_ADDRS_8 +0x2254 VAP_PVS_FLOW_CNTL_ADDRS_9 +0x2258 VAP_PVS_FLOW_CNTL_ADDRS_10 +0x225C VAP_PVS_FLOW_CNTL_ADDRS_11 +0x2260 VAP_PVS_FLOW_CNTL_ADDRS_12 +0x2264 VAP_PVS_FLOW_CNTL_ADDRS_13 +0x2268 VAP_PVS_FLOW_CNTL_ADDRS_14 +0x226C VAP_PVS_FLOW_CNTL_ADDRS_15 +0x2284 VAP_PVS_STATE_FLUSH_REG +0x2288 VAP_PVS_VTX_TIMEOUT_REG +0x2290 VAP_PVS_FLOW_CNTL_LOOP_INDEX_0 +0x2294 VAP_PVS_FLOW_CNTL_LOOP_INDEX_1 +0x2298 VAP_PVS_FLOW_CNTL_LOOP_INDEX_2 +0x229C VAP_PVS_FLOW_CNTL_LOOP_INDEX_3 +0x22A0 VAP_PVS_FLOW_CNTL_LOOP_INDEX_4 +0x22A4 VAP_PVS_FLOW_CNTL_LOOP_INDEX_5 +0x22A8 VAP_PVS_FLOW_CNTL_LOOP_INDEX_6 +0x22AC VAP_PVS_FLOW_CNTL_LOOP_INDEX_7 +0x22B0 VAP_PVS_FLOW_CNTL_LOOP_INDEX_8 +0x22B4 VAP_PVS_FLOW_CNTL_LOOP_INDEX_9 +0x22B8 VAP_PVS_FLOW_CNTL_LOOP_INDEX_10 +0x22BC VAP_PVS_FLOW_CNTL_LOOP_INDEX_11 +0x22C0 VAP_PVS_FLOW_CNTL_LOOP_INDEX_12 +0x22C4 VAP_PVS_FLOW_CNTL_LOOP_INDEX_13 +0x22C8 VAP_PVS_FLOW_CNTL_LOOP_INDEX_14 +0x22CC VAP_PVS_FLOW_CNTL_LOOP_INDEX_15 +0x22D0 VAP_PVS_CODE_CNTL_0 +0x22D4 VAP_PVS_CONST_CNTL +0x22D8 VAP_PVS_CODE_CNTL_1 +0x22DC VAP_PVS_FLOW_CNTL_OPC +0x342C RB2D_DSTCACHE_CTLSTAT +0x4000 GB_VAP_RASTER_VTX_FMT_0 +0x4004 GB_VAP_RASTER_VTX_FMT_1 +0x4008 GB_ENABLE +0x4010 GB_MSPOS0 +0x4014 GB_MSPOS1 +0x401C GB_SELECT +0x4020 GB_AA_CONFIG +0x4024 GB_FIFO_SIZE +0x4100 TX_INVALTAGS +0x4200 GA_POINT_S0 +0x4204 GA_POINT_T0 +0x4208 GA_POINT_S1 +0x420C GA_POINT_T1 +0x4214 GA_TRIANGLE_STIPPLE +0x421C GA_POINT_SIZE +0x4230 GA_POINT_MINMAX +0x4234 GA_LINE_CNTL +0x4238 GA_LINE_STIPPLE_CONFIG +0x4260 GA_LINE_STIPPLE_VALUE +0x4264 GA_LINE_S0 +0x4268 GA_LINE_S1 +0x4278 GA_COLOR_CONTROL +0x427C GA_SOLID_RG +0x4280 GA_SOLID_BA +0x4288 GA_POLY_MODE +0x428C GA_ROUND_MODE +0x4290 GA_OFFSET +0x4294 GA_FOG_SCALE +0x4298 GA_FOG_OFFSET +0x42A0 SU_TEX_WRAP +0x42A4 SU_POLY_OFFSET_FRONT_SCALE +0x42A8 SU_POLY_OFFSET_FRONT_OFFSET +0x42AC SU_POLY_OFFSET_BACK_SCALE +0x42B0 SU_POLY_OFFSET_BACK_OFFSET +0x42B4 SU_POLY_OFFSET_ENABLE +0x42B8 SU_CULL_MODE +0x42C0 SU_DEPTH_SCALE +0x42C4 SU_DEPTH_OFFSET +0x42C8 SU_REG_DEST +0x4300 RS_COUNT +0x4304 RS_INST_COUNT +0x4310 RS_IP_0 +0x4314 RS_IP_1 +0x4318 RS_IP_2 +0x431C RS_IP_3 +0x4320 RS_IP_4 +0x4324 RS_IP_5 +0x4328 RS_IP_6 +0x432C RS_IP_7 +0x4330 RS_INST_0 +0x4334 RS_INST_1 +0x4338 RS_INST_2 +0x433C RS_INST_3 +0x4340 RS_INST_4 +0x4344 RS_INST_5 +0x4348 RS_INST_6 +0x434C RS_INST_7 +0x4350 RS_INST_8 +0x4354 RS_INST_9 +0x4358 RS_INST_10 +0x435C RS_INST_11 +0x4360 RS_INST_12 +0x4364 RS_INST_13 +0x4368 RS_INST_14 +0x436C RS_INST_15 +0x43A8 SC_EDGERULE +0x43B0 SC_CLIP_0_A +0x43B4 SC_CLIP_0_B +0x43B8 SC_CLIP_1_A +0x43BC SC_CLIP_1_B +0x43C0 SC_CLIP_2_A +0x43C4 SC_CLIP_2_B +0x43C8 SC_CLIP_3_A +0x43CC SC_CLIP_3_B +0x43D0 SC_CLIP_RULE +0x43E0 SC_SCISSOR0 +0x43E8 SC_SCREENDOOR +0x4440 TX_FILTER1_0 +0x4444 TX_FILTER1_1 +0x4448 TX_FILTER1_2 +0x444C TX_FILTER1_3 +0x4450 TX_FILTER1_4 +0x4454 TX_FILTER1_5 +0x4458 TX_FILTER1_6 +0x445C TX_FILTER1_7 +0x4460 TX_FILTER1_8 +0x4464 TX_FILTER1_9 +0x4468 TX_FILTER1_10 +0x446C TX_FILTER1_11 +0x4470 TX_FILTER1_12 +0x4474 TX_FILTER1_13 +0x4478 TX_FILTER1_14 +0x447C TX_FILTER1_15 +0x4580 TX_CHROMA_KEY_0 +0x4584 TX_CHROMA_KEY_1 +0x4588 TX_CHROMA_KEY_2 +0x458C TX_CHROMA_KEY_3 +0x4590 TX_CHROMA_KEY_4 +0x4594 TX_CHROMA_KEY_5 +0x4598 TX_CHROMA_KEY_6 +0x459C TX_CHROMA_KEY_7 +0x45A0 TX_CHROMA_KEY_8 +0x45A4 TX_CHROMA_KEY_9 +0x45A8 TX_CHROMA_KEY_10 +0x45AC TX_CHROMA_KEY_11 +0x45B0 TX_CHROMA_KEY_12 +0x45B4 TX_CHROMA_KEY_13 +0x45B8 TX_CHROMA_KEY_14 +0x45BC TX_CHROMA_KEY_15 +0x45C0 TX_BORDER_COLOR_0 +0x45C4 TX_BORDER_COLOR_1 +0x45C8 TX_BORDER_COLOR_2 +0x45CC TX_BORDER_COLOR_3 +0x45D0 TX_BORDER_COLOR_4 +0x45D4 TX_BORDER_COLOR_5 +0x45D8 TX_BORDER_COLOR_6 +0x45DC TX_BORDER_COLOR_7 +0x45E0 TX_BORDER_COLOR_8 +0x45E4 TX_BORDER_COLOR_9 +0x45E8 TX_BORDER_COLOR_10 +0x45EC TX_BORDER_COLOR_11 +0x45F0 TX_BORDER_COLOR_12 +0x45F4 TX_BORDER_COLOR_13 +0x45F8 TX_BORDER_COLOR_14 +0x45FC TX_BORDER_COLOR_15 +0x4600 US_CONFIG +0x4604 US_PIXSIZE +0x4608 US_CODE_OFFSET +0x460C US_RESET +0x4610 US_CODE_ADDR_0 +0x4614 US_CODE_ADDR_1 +0x4618 US_CODE_ADDR_2 +0x461C US_CODE_ADDR_3 +0x4620 US_TEX_INST_0 +0x4624 US_TEX_INST_1 +0x4628 US_TEX_INST_2 +0x462C US_TEX_INST_3 +0x4630 US_TEX_INST_4 +0x4634 US_TEX_INST_5 +0x4638 US_TEX_INST_6 +0x463C US_TEX_INST_7 +0x4640 US_TEX_INST_8 +0x4644 US_TEX_INST_9 +0x4648 US_TEX_INST_10 +0x464C US_TEX_INST_11 +0x4650 US_TEX_INST_12 +0x4654 US_TEX_INST_13 +0x4658 US_TEX_INST_14 +0x465C US_TEX_INST_15 +0x4660 US_TEX_INST_16 +0x4664 US_TEX_INST_17 +0x4668 US_TEX_INST_18 +0x466C US_TEX_INST_19 +0x4670 US_TEX_INST_20 +0x4674 US_TEX_INST_21 +0x4678 US_TEX_INST_22 +0x467C US_TEX_INST_23 +0x4680 US_TEX_INST_24 +0x4684 US_TEX_INST_25 +0x4688 US_TEX_INST_26 +0x468C US_TEX_INST_27 +0x4690 US_TEX_INST_28 +0x4694 US_TEX_INST_29 +0x4698 US_TEX_INST_30 +0x469C US_TEX_INST_31 +0x46A4 US_OUT_FMT_0 +0x46A8 US_OUT_FMT_1 +0x46AC US_OUT_FMT_2 +0x46B0 US_OUT_FMT_3 +0x46B4 US_W_FMT +0x46C0 US_ALU_RGB_ADDR_0 +0x46C4 US_ALU_RGB_ADDR_1 +0x46C8 US_ALU_RGB_ADDR_2 +0x46CC US_ALU_RGB_ADDR_3 +0x46D0 US_ALU_RGB_ADDR_4 +0x46D4 US_ALU_RGB_ADDR_5 +0x46D8 US_ALU_RGB_ADDR_6 +0x46DC US_ALU_RGB_ADDR_7 +0x46E0 US_ALU_RGB_ADDR_8 +0x46E4 US_ALU_RGB_ADDR_9 +0x46E8 US_ALU_RGB_ADDR_10 +0x46EC US_ALU_RGB_ADDR_11 +0x46F0 US_ALU_RGB_ADDR_12 +0x46F4 US_ALU_RGB_ADDR_13 +0x46F8 US_ALU_RGB_ADDR_14 +0x46FC US_ALU_RGB_ADDR_15 +0x4700 US_ALU_RGB_ADDR_16 +0x4704 US_ALU_RGB_ADDR_17 +0x4708 US_ALU_RGB_ADDR_18 +0x470C US_ALU_RGB_ADDR_19 +0x4710 US_ALU_RGB_ADDR_20 +0x4714 US_ALU_RGB_ADDR_21 +0x4718 US_ALU_RGB_ADDR_22 +0x471C US_ALU_RGB_ADDR_23 +0x4720 US_ALU_RGB_ADDR_24 +0x4724 US_ALU_RGB_ADDR_25 +0x4728 US_ALU_RGB_ADDR_26 +0x472C US_ALU_RGB_ADDR_27 +0x4730 US_ALU_RGB_ADDR_28 +0x4734 US_ALU_RGB_ADDR_29 +0x4738 US_ALU_RGB_ADDR_30 +0x473C US_ALU_RGB_ADDR_31 +0x4740 US_ALU_RGB_ADDR_32 +0x4744 US_ALU_RGB_ADDR_33 +0x4748 US_ALU_RGB_ADDR_34 +0x474C US_ALU_RGB_ADDR_35 +0x4750 US_ALU_RGB_ADDR_36 +0x4754 US_ALU_RGB_ADDR_37 +0x4758 US_ALU_RGB_ADDR_38 +0x475C US_ALU_RGB_ADDR_39 +0x4760 US_ALU_RGB_ADDR_40 +0x4764 US_ALU_RGB_ADDR_41 +0x4768 US_ALU_RGB_ADDR_42 +0x476C US_ALU_RGB_ADDR_43 +0x4770 US_ALU_RGB_ADDR_44 +0x4774 US_ALU_RGB_ADDR_45 +0x4778 US_ALU_RGB_ADDR_46 +0x477C US_ALU_RGB_ADDR_47 +0x4780 US_ALU_RGB_ADDR_48 +0x4784 US_ALU_RGB_ADDR_49 +0x4788 US_ALU_RGB_ADDR_50 +0x478C US_ALU_RGB_ADDR_51 +0x4790 US_ALU_RGB_ADDR_52 +0x4794 US_ALU_RGB_ADDR_53 +0x4798 US_ALU_RGB_ADDR_54 +0x479C US_ALU_RGB_ADDR_55 +0x47A0 US_ALU_RGB_ADDR_56 +0x47A4 US_ALU_RGB_ADDR_57 +0x47A8 US_ALU_RGB_ADDR_58 +0x47AC US_ALU_RGB_ADDR_59 +0x47B0 US_ALU_RGB_ADDR_60 +0x47B4 US_ALU_RGB_ADDR_61 +0x47B8 US_ALU_RGB_ADDR_62 +0x47BC US_ALU_RGB_ADDR_63 +0x47C0 US_ALU_ALPHA_ADDR_0 +0x47C4 US_ALU_ALPHA_ADDR_1 +0x47C8 US_ALU_ALPHA_ADDR_2 +0x47CC US_ALU_ALPHA_ADDR_3 +0x47D0 US_ALU_ALPHA_ADDR_4 +0x47D4 US_ALU_ALPHA_ADDR_5 +0x47D8 US_ALU_ALPHA_ADDR_6 +0x47DC US_ALU_ALPHA_ADDR_7 +0x47E0 US_ALU_ALPHA_ADDR_8 +0x47E4 US_ALU_ALPHA_ADDR_9 +0x47E8 US_ALU_ALPHA_ADDR_10 +0x47EC US_ALU_ALPHA_ADDR_11 +0x47F0 US_ALU_ALPHA_ADDR_12 +0x47F4 US_ALU_ALPHA_ADDR_13 +0x47F8 US_ALU_ALPHA_ADDR_14 +0x47FC US_ALU_ALPHA_ADDR_15 +0x4800 US_ALU_ALPHA_ADDR_16 +0x4804 US_ALU_ALPHA_ADDR_17 +0x4808 US_ALU_ALPHA_ADDR_18 +0x480C US_ALU_ALPHA_ADDR_19 +0x4810 US_ALU_ALPHA_ADDR_20 +0x4814 US_ALU_ALPHA_ADDR_21 +0x4818 US_ALU_ALPHA_ADDR_22 +0x481C US_ALU_ALPHA_ADDR_23 +0x4820 US_ALU_ALPHA_ADDR_24 +0x4824 US_ALU_ALPHA_ADDR_25 +0x4828 US_ALU_ALPHA_ADDR_26 +0x482C US_ALU_ALPHA_ADDR_27 +0x4830 US_ALU_ALPHA_ADDR_28 +0x4834 US_ALU_ALPHA_ADDR_29 +0x4838 US_ALU_ALPHA_ADDR_30 +0x483C US_ALU_ALPHA_ADDR_31 +0x4840 US_ALU_ALPHA_ADDR_32 +0x4844 US_ALU_ALPHA_ADDR_33 +0x4848 US_ALU_ALPHA_ADDR_34 +0x484C US_ALU_ALPHA_ADDR_35 +0x4850 US_ALU_ALPHA_ADDR_36 +0x4854 US_ALU_ALPHA_ADDR_37 +0x4858 US_ALU_ALPHA_ADDR_38 +0x485C US_ALU_ALPHA_ADDR_39 +0x4860 US_ALU_ALPHA_ADDR_40 +0x4864 US_ALU_ALPHA_ADDR_41 +0x4868 US_ALU_ALPHA_ADDR_42 +0x486C US_ALU_ALPHA_ADDR_43 +0x4870 US_ALU_ALPHA_ADDR_44 +0x4874 US_ALU_ALPHA_ADDR_45 +0x4878 US_ALU_ALPHA_ADDR_46 +0x487C US_ALU_ALPHA_ADDR_47 +0x4880 US_ALU_ALPHA_ADDR_48 +0x4884 US_ALU_ALPHA_ADDR_49 +0x4888 US_ALU_ALPHA_ADDR_50 +0x488C US_ALU_ALPHA_ADDR_51 +0x4890 US_ALU_ALPHA_ADDR_52 +0x4894 US_ALU_ALPHA_ADDR_53 +0x4898 US_ALU_ALPHA_ADDR_54 +0x489C US_ALU_ALPHA_ADDR_55 +0x48A0 US_ALU_ALPHA_ADDR_56 +0x48A4 US_ALU_ALPHA_ADDR_57 +0x48A8 US_ALU_ALPHA_ADDR_58 +0x48AC US_ALU_ALPHA_ADDR_59 +0x48B0 US_ALU_ALPHA_ADDR_60 +0x48B4 US_ALU_ALPHA_ADDR_61 +0x48B8 US_ALU_ALPHA_ADDR_62 +0x48BC US_ALU_ALPHA_ADDR_63 +0x48C0 US_ALU_RGB_INST_0 +0x48C4 US_ALU_RGB_INST_1 +0x48C8 US_ALU_RGB_INST_2 +0x48CC US_ALU_RGB_INST_3 +0x48D0 US_ALU_RGB_INST_4 +0x48D4 US_ALU_RGB_INST_5 +0x48D8 US_ALU_RGB_INST_6 +0x48DC US_ALU_RGB_INST_7 +0x48E0 US_ALU_RGB_INST_8 +0x48E4 US_ALU_RGB_INST_9 +0x48E8 US_ALU_RGB_INST_10 +0x48EC US_ALU_RGB_INST_11 +0x48F0 US_ALU_RGB_INST_12 +0x48F4 US_ALU_RGB_INST_13 +0x48F8 US_ALU_RGB_INST_14 +0x48FC US_ALU_RGB_INST_15 +0x4900 US_ALU_RGB_INST_16 +0x4904 US_ALU_RGB_INST_17 +0x4908 US_ALU_RGB_INST_18 +0x490C US_ALU_RGB_INST_19 +0x4910 US_ALU_RGB_INST_20 +0x4914 US_ALU_RGB_INST_21 +0x4918 US_ALU_RGB_INST_22 +0x491C US_ALU_RGB_INST_23 +0x4920 US_ALU_RGB_INST_24 +0x4924 US_ALU_RGB_INST_25 +0x4928 US_ALU_RGB_INST_26 +0x492C US_ALU_RGB_INST_27 +0x4930 US_ALU_RGB_INST_28 +0x4934 US_ALU_RGB_INST_29 +0x4938 US_ALU_RGB_INST_30 +0x493C US_ALU_RGB_INST_31 +0x4940 US_ALU_RGB_INST_32 +0x4944 US_ALU_RGB_INST_33 +0x4948 US_ALU_RGB_INST_34 +0x494C US_ALU_RGB_INST_35 +0x4950 US_ALU_RGB_INST_36 +0x4954 US_ALU_RGB_INST_37 +0x4958 US_ALU_RGB_INST_38 +0x495C US_ALU_RGB_INST_39 +0x4960 US_ALU_RGB_INST_40 +0x4964 US_ALU_RGB_INST_41 +0x4968 US_ALU_RGB_INST_42 +0x496C US_ALU_RGB_INST_43 +0x4970 US_ALU_RGB_INST_44 +0x4974 US_ALU_RGB_INST_45 +0x4978 US_ALU_RGB_INST_46 +0x497C US_ALU_RGB_INST_47 +0x4980 US_ALU_RGB_INST_48 +0x4984 US_ALU_RGB_INST_49 +0x4988 US_ALU_RGB_INST_50 +0x498C US_ALU_RGB_INST_51 +0x4990 US_ALU_RGB_INST_52 +0x4994 US_ALU_RGB_INST_53 +0x4998 US_ALU_RGB_INST_54 +0x499C US_ALU_RGB_INST_55 +0x49A0 US_ALU_RGB_INST_56 +0x49A4 US_ALU_RGB_INST_57 +0x49A8 US_ALU_RGB_INST_58 +0x49AC US_ALU_RGB_INST_59 +0x49B0 US_ALU_RGB_INST_60 +0x49B4 US_ALU_RGB_INST_61 +0x49B8 US_ALU_RGB_INST_62 +0x49BC US_ALU_RGB_INST_63 +0x49C0 US_ALU_ALPHA_INST_0 +0x49C4 US_ALU_ALPHA_INST_1 +0x49C8 US_ALU_ALPHA_INST_2 +0x49CC US_ALU_ALPHA_INST_3 +0x49D0 US_ALU_ALPHA_INST_4 +0x49D4 US_ALU_ALPHA_INST_5 +0x49D8 US_ALU_ALPHA_INST_6 +0x49DC US_ALU_ALPHA_INST_7 +0x49E0 US_ALU_ALPHA_INST_8 +0x49E4 US_ALU_ALPHA_INST_9 +0x49E8 US_ALU_ALPHA_INST_10 +0x49EC US_ALU_ALPHA_INST_11 +0x49F0 US_ALU_ALPHA_INST_12 +0x49F4 US_ALU_ALPHA_INST_13 +0x49F8 US_ALU_ALPHA_INST_14 +0x49FC US_ALU_ALPHA_INST_15 +0x4A00 US_ALU_ALPHA_INST_16 +0x4A04 US_ALU_ALPHA_INST_17 +0x4A08 US_ALU_ALPHA_INST_18 +0x4A0C US_ALU_ALPHA_INST_19 +0x4A10 US_ALU_ALPHA_INST_20 +0x4A14 US_ALU_ALPHA_INST_21 +0x4A18 US_ALU_ALPHA_INST_22 +0x4A1C US_ALU_ALPHA_INST_23 +0x4A20 US_ALU_ALPHA_INST_24 +0x4A24 US_ALU_ALPHA_INST_25 +0x4A28 US_ALU_ALPHA_INST_26 +0x4A2C US_ALU_ALPHA_INST_27 +0x4A30 US_ALU_ALPHA_INST_28 +0x4A34 US_ALU_ALPHA_INST_29 +0x4A38 US_ALU_ALPHA_INST_30 +0x4A3C US_ALU_ALPHA_INST_31 +0x4A40 US_ALU_ALPHA_INST_32 +0x4A44 US_ALU_ALPHA_INST_33 +0x4A48 US_ALU_ALPHA_INST_34 +0x4A4C US_ALU_ALPHA_INST_35 +0x4A50 US_ALU_ALPHA_INST_36 +0x4A54 US_ALU_ALPHA_INST_37 +0x4A58 US_ALU_ALPHA_INST_38 +0x4A5C US_ALU_ALPHA_INST_39 +0x4A60 US_ALU_ALPHA_INST_40 +0x4A64 US_ALU_ALPHA_INST_41 +0x4A68 US_ALU_ALPHA_INST_42 +0x4A6C US_ALU_ALPHA_INST_43 +0x4A70 US_ALU_ALPHA_INST_44 +0x4A74 US_ALU_ALPHA_INST_45 +0x4A78 US_ALU_ALPHA_INST_46 +0x4A7C US_ALU_ALPHA_INST_47 +0x4A80 US_ALU_ALPHA_INST_48 +0x4A84 US_ALU_ALPHA_INST_49 +0x4A88 US_ALU_ALPHA_INST_50 +0x4A8C US_ALU_ALPHA_INST_51 +0x4A90 US_ALU_ALPHA_INST_52 +0x4A94 US_ALU_ALPHA_INST_53 +0x4A98 US_ALU_ALPHA_INST_54 +0x4A9C US_ALU_ALPHA_INST_55 +0x4AA0 US_ALU_ALPHA_INST_56 +0x4AA4 US_ALU_ALPHA_INST_57 +0x4AA8 US_ALU_ALPHA_INST_58 +0x4AAC US_ALU_ALPHA_INST_59 +0x4AB0 US_ALU_ALPHA_INST_60 +0x4AB4 US_ALU_ALPHA_INST_61 +0x4AB8 US_ALU_ALPHA_INST_62 +0x4ABC US_ALU_ALPHA_INST_63 +0x4BC0 FG_FOG_BLEND +0x4BC4 FG_FOG_FACTOR +0x4BC8 FG_FOG_COLOR_R +0x4BCC FG_FOG_COLOR_G +0x4BD0 FG_FOG_COLOR_B +0x4BD4 FG_ALPHA_FUNC +0x4BD8 FG_DEPTH_SRC +0x4C00 US_ALU_CONST_R_0 +0x4C04 US_ALU_CONST_G_0 +0x4C08 US_ALU_CONST_B_0 +0x4C0C US_ALU_CONST_A_0 +0x4C10 US_ALU_CONST_R_1 +0x4C14 US_ALU_CONST_G_1 +0x4C18 US_ALU_CONST_B_1 +0x4C1C US_ALU_CONST_A_1 +0x4C20 US_ALU_CONST_R_2 +0x4C24 US_ALU_CONST_G_2 +0x4C28 US_ALU_CONST_B_2 +0x4C2C US_ALU_CONST_A_2 +0x4C30 US_ALU_CONST_R_3 +0x4C34 US_ALU_CONST_G_3 +0x4C38 US_ALU_CONST_B_3 +0x4C3C US_ALU_CONST_A_3 +0x4C40 US_ALU_CONST_R_4 +0x4C44 US_ALU_CONST_G_4 +0x4C48 US_ALU_CONST_B_4 +0x4C4C US_ALU_CONST_A_4 +0x4C50 US_ALU_CONST_R_5 +0x4C54 US_ALU_CONST_G_5 +0x4C58 US_ALU_CONST_B_5 +0x4C5C US_ALU_CONST_A_5 +0x4C60 US_ALU_CONST_R_6 +0x4C64 US_ALU_CONST_G_6 +0x4C68 US_ALU_CONST_B_6 +0x4C6C US_ALU_CONST_A_6 +0x4C70 US_ALU_CONST_R_7 +0x4C74 US_ALU_CONST_G_7 +0x4C78 US_ALU_CONST_B_7 +0x4C7C US_ALU_CONST_A_7 +0x4C80 US_ALU_CONST_R_8 +0x4C84 US_ALU_CONST_G_8 +0x4C88 US_ALU_CONST_B_8 +0x4C8C US_ALU_CONST_A_8 +0x4C90 US_ALU_CONST_R_9 +0x4C94 US_ALU_CONST_G_9 +0x4C98 US_ALU_CONST_B_9 +0x4C9C US_ALU_CONST_A_9 +0x4CA0 US_ALU_CONST_R_10 +0x4CA4 US_ALU_CONST_G_10 +0x4CA8 US_ALU_CONST_B_10 +0x4CAC US_ALU_CONST_A_10 +0x4CB0 US_ALU_CONST_R_11 +0x4CB4 US_ALU_CONST_G_11 +0x4CB8 US_ALU_CONST_B_11 +0x4CBC US_ALU_CONST_A_11 +0x4CC0 US_ALU_CONST_R_12 +0x4CC4 US_ALU_CONST_G_12 +0x4CC8 US_ALU_CONST_B_12 +0x4CCC US_ALU_CONST_A_12 +0x4CD0 US_ALU_CONST_R_13 +0x4CD4 US_ALU_CONST_G_13 +0x4CD8 US_ALU_CONST_B_13 +0x4CDC US_ALU_CONST_A_13 +0x4CE0 US_ALU_CONST_R_14 +0x4CE4 US_ALU_CONST_G_14 +0x4CE8 US_ALU_CONST_B_14 +0x4CEC US_ALU_CONST_A_14 +0x4CF0 US_ALU_CONST_R_15 +0x4CF4 US_ALU_CONST_G_15 +0x4CF8 US_ALU_CONST_B_15 +0x4CFC US_ALU_CONST_A_15 +0x4D00 US_ALU_CONST_R_16 +0x4D04 US_ALU_CONST_G_16 +0x4D08 US_ALU_CONST_B_16 +0x4D0C US_ALU_CONST_A_16 +0x4D10 US_ALU_CONST_R_17 +0x4D14 US_ALU_CONST_G_17 +0x4D18 US_ALU_CONST_B_17 +0x4D1C US_ALU_CONST_A_17 +0x4D20 US_ALU_CONST_R_18 +0x4D24 US_ALU_CONST_G_18 +0x4D28 US_ALU_CONST_B_18 +0x4D2C US_ALU_CONST_A_18 +0x4D30 US_ALU_CONST_R_19 +0x4D34 US_ALU_CONST_G_19 +0x4D38 US_ALU_CONST_B_19 +0x4D3C US_ALU_CONST_A_19 +0x4D40 US_ALU_CONST_R_20 +0x4D44 US_ALU_CONST_G_20 +0x4D48 US_ALU_CONST_B_20 +0x4D4C US_ALU_CONST_A_20 +0x4D50 US_ALU_CONST_R_21 +0x4D54 US_ALU_CONST_G_21 +0x4D58 US_ALU_CONST_B_21 +0x4D5C US_ALU_CONST_A_21 +0x4D60 US_ALU_CONST_R_22 +0x4D64 US_ALU_CONST_G_22 +0x4D68 US_ALU_CONST_B_22 +0x4D6C US_ALU_CONST_A_22 +0x4D70 US_ALU_CONST_R_23 +0x4D74 US_ALU_CONST_G_23 +0x4D78 US_ALU_CONST_B_23 +0x4D7C US_ALU_CONST_A_23 +0x4D80 US_ALU_CONST_R_24 +0x4D84 US_ALU_CONST_G_24 +0x4D88 US_ALU_CONST_B_24 +0x4D8C US_ALU_CONST_A_24 +0x4D90 US_ALU_CONST_R_25 +0x4D94 US_ALU_CONST_G_25 +0x4D98 US_ALU_CONST_B_25 +0x4D9C US_ALU_CONST_A_25 +0x4DA0 US_ALU_CONST_R_26 +0x4DA4 US_ALU_CONST_G_26 +0x4DA8 US_ALU_CONST_B_26 +0x4DAC US_ALU_CONST_A_26 +0x4DB0 US_ALU_CONST_R_27 +0x4DB4 US_ALU_CONST_G_27 +0x4DB8 US_ALU_CONST_B_27 +0x4DBC US_ALU_CONST_A_27 +0x4DC0 US_ALU_CONST_R_28 +0x4DC4 US_ALU_CONST_G_28 +0x4DC8 US_ALU_CONST_B_28 +0x4DCC US_ALU_CONST_A_28 +0x4DD0 US_ALU_CONST_R_29 +0x4DD4 US_ALU_CONST_G_29 +0x4DD8 US_ALU_CONST_B_29 +0x4DDC US_ALU_CONST_A_29 +0x4DE0 US_ALU_CONST_R_30 +0x4DE4 US_ALU_CONST_G_30 +0x4DE8 US_ALU_CONST_B_30 +0x4DEC US_ALU_CONST_A_30 +0x4DF0 US_ALU_CONST_R_31 +0x4DF4 US_ALU_CONST_G_31 +0x4DF8 US_ALU_CONST_B_31 +0x4DFC US_ALU_CONST_A_31 +0x4E08 RB3D_ABLENDCNTL_R3 +0x4E10 RB3D_CONSTANT_COLOR +0x4E14 RB3D_COLOR_CLEAR_VALUE +0x4E18 RB3D_ROPCNTL_R3 +0x4E1C RB3D_CLRCMP_FLIPE_R3 +0x4E20 RB3D_CLRCMP_CLR_R3 +0x4E24 RB3D_CLRCMP_MSK_R3 +0x4E48 RB3D_DEBUG_CTL +0x4E4C RB3D_DSTCACHE_CTLSTAT_R3 +0x4E50 RB3D_DITHER_CTL +0x4E54 RB3D_CMASK_OFFSET0 +0x4E58 RB3D_CMASK_OFFSET1 +0x4E5C RB3D_CMASK_OFFSET2 +0x4E60 RB3D_CMASK_OFFSET3 +0x4E64 RB3D_CMASK_PITCH0 +0x4E68 RB3D_CMASK_PITCH1 +0x4E6C RB3D_CMASK_PITCH2 +0x4E70 RB3D_CMASK_PITCH3 +0x4E74 RB3D_CMASK_WRINDEX +0x4E78 RB3D_CMASK_DWORD +0x4E7C RB3D_CMASK_RDINDEX +0x4EA0 RB3D_DISCARD_SRC_PIXEL_LTE_THRESHOLD +0x4EA4 RB3D_DISCARD_SRC_PIXEL_GTE_THRESHOLD +0x4F04 ZB_ZSTENCILCNTL +0x4F08 ZB_STENCILREFMASK +0x4F14 ZB_ZTOP +0x4F18 ZB_ZCACHE_CTLSTAT +0x4F28 ZB_DEPTHCLEARVALUE +0x4F58 ZB_ZPASS_DATA diff --git a/sys/dev/drm2/radeon/reg_srcs/r420 b/sys/dev/drm2/radeon/reg_srcs/r420 new file mode 100644 index 00000000000..722074e21e2 --- /dev/null +++ b/sys/dev/drm2/radeon/reg_srcs/r420 @@ -0,0 +1,780 @@ +r420 0x4f60 +0x1434 SRC_Y_X +0x1438 DST_Y_X +0x143C DST_HEIGHT_WIDTH +0x146C DP_GUI_MASTER_CNTL +0x1474 BRUSH_Y_X +0x1478 DP_BRUSH_BKGD_CLR +0x147C DP_BRUSH_FRGD_CLR +0x1480 BRUSH_DATA0 +0x1484 BRUSH_DATA1 +0x1598 DST_WIDTH_HEIGHT +0x15C0 CLR_CMP_CNTL +0x15C4 CLR_CMP_CLR_SRC +0x15C8 CLR_CMP_CLR_DST +0x15CC CLR_CMP_MSK +0x15D8 DP_SRC_FRGD_CLR +0x15DC DP_SRC_BKGD_CLR +0x1600 DST_LINE_START +0x1604 DST_LINE_END +0x1608 DST_LINE_PATCOUNT +0x16C0 DP_CNTL +0x16CC DP_WRITE_MSK +0x16D0 DP_CNTL_XDIR_YDIR_YMAJOR +0x16E8 DEFAULT_SC_BOTTOM_RIGHT +0x16EC SC_TOP_LEFT +0x16F0 SC_BOTTOM_RIGHT +0x16F4 SRC_SC_BOTTOM_RIGHT +0x1714 DSTCACHE_CTLSTAT +0x1720 WAIT_UNTIL +0x172C RBBM_GUICNTL +0x1D98 VAP_VPORT_XSCALE +0x1D9C VAP_VPORT_XOFFSET +0x1DA0 VAP_VPORT_YSCALE +0x1DA4 VAP_VPORT_YOFFSET +0x1DA8 VAP_VPORT_ZSCALE +0x1DAC VAP_VPORT_ZOFFSET +0x2080 VAP_CNTL +0x2090 VAP_OUT_VTX_FMT_0 +0x2094 VAP_OUT_VTX_FMT_1 +0x20B0 VAP_VTE_CNTL +0x2138 VAP_VF_MIN_VTX_INDX +0x2140 VAP_CNTL_STATUS +0x2150 VAP_PROG_STREAM_CNTL_0 +0x2154 VAP_PROG_STREAM_CNTL_1 +0x2158 VAP_PROG_STREAM_CNTL_2 +0x215C VAP_PROG_STREAM_CNTL_3 +0x2160 VAP_PROG_STREAM_CNTL_4 +0x2164 VAP_PROG_STREAM_CNTL_5 +0x2168 VAP_PROG_STREAM_CNTL_6 +0x216C VAP_PROG_STREAM_CNTL_7 +0x2180 VAP_VTX_STATE_CNTL +0x2184 VAP_VSM_VTX_ASSM +0x2188 VAP_VTX_STATE_IND_REG_0 +0x218C VAP_VTX_STATE_IND_REG_1 +0x2190 VAP_VTX_STATE_IND_REG_2 +0x2194 VAP_VTX_STATE_IND_REG_3 +0x2198 VAP_VTX_STATE_IND_REG_4 +0x219C VAP_VTX_STATE_IND_REG_5 +0x21A0 VAP_VTX_STATE_IND_REG_6 +0x21A4 VAP_VTX_STATE_IND_REG_7 +0x21A8 VAP_VTX_STATE_IND_REG_8 +0x21AC VAP_VTX_STATE_IND_REG_9 +0x21B0 VAP_VTX_STATE_IND_REG_10 +0x21B4 VAP_VTX_STATE_IND_REG_11 +0x21B8 VAP_VTX_STATE_IND_REG_12 +0x21BC VAP_VTX_STATE_IND_REG_13 +0x21C0 VAP_VTX_STATE_IND_REG_14 +0x21C4 VAP_VTX_STATE_IND_REG_15 +0x21DC VAP_PSC_SGN_NORM_CNTL +0x21E0 VAP_PROG_STREAM_CNTL_EXT_0 +0x21E4 VAP_PROG_STREAM_CNTL_EXT_1 +0x21E8 VAP_PROG_STREAM_CNTL_EXT_2 +0x21EC VAP_PROG_STREAM_CNTL_EXT_3 +0x21F0 VAP_PROG_STREAM_CNTL_EXT_4 +0x21F4 VAP_PROG_STREAM_CNTL_EXT_5 +0x21F8 VAP_PROG_STREAM_CNTL_EXT_6 +0x21FC VAP_PROG_STREAM_CNTL_EXT_7 +0x2200 VAP_PVS_VECTOR_INDX_REG +0x2204 VAP_PVS_VECTOR_DATA_REG +0x2208 VAP_PVS_VECTOR_DATA_REG_128 +0x221C VAP_CLIP_CNTL +0x2220 VAP_GB_VERT_CLIP_ADJ +0x2224 VAP_GB_VERT_DISC_ADJ +0x2228 VAP_GB_HORZ_CLIP_ADJ +0x222C VAP_GB_HORZ_DISC_ADJ +0x2230 VAP_PVS_FLOW_CNTL_ADDRS_0 +0x2234 VAP_PVS_FLOW_CNTL_ADDRS_1 +0x2238 VAP_PVS_FLOW_CNTL_ADDRS_2 +0x223C VAP_PVS_FLOW_CNTL_ADDRS_3 +0x2240 VAP_PVS_FLOW_CNTL_ADDRS_4 +0x2244 VAP_PVS_FLOW_CNTL_ADDRS_5 +0x2248 VAP_PVS_FLOW_CNTL_ADDRS_6 +0x224C VAP_PVS_FLOW_CNTL_ADDRS_7 +0x2250 VAP_PVS_FLOW_CNTL_ADDRS_8 +0x2254 VAP_PVS_FLOW_CNTL_ADDRS_9 +0x2258 VAP_PVS_FLOW_CNTL_ADDRS_10 +0x225C VAP_PVS_FLOW_CNTL_ADDRS_11 +0x2260 VAP_PVS_FLOW_CNTL_ADDRS_12 +0x2264 VAP_PVS_FLOW_CNTL_ADDRS_13 +0x2268 VAP_PVS_FLOW_CNTL_ADDRS_14 +0x226C VAP_PVS_FLOW_CNTL_ADDRS_15 +0x2284 VAP_PVS_STATE_FLUSH_REG +0x2288 VAP_PVS_VTX_TIMEOUT_REG +0x2290 VAP_PVS_FLOW_CNTL_LOOP_INDEX_0 +0x2294 VAP_PVS_FLOW_CNTL_LOOP_INDEX_1 +0x2298 VAP_PVS_FLOW_CNTL_LOOP_INDEX_2 +0x229C VAP_PVS_FLOW_CNTL_LOOP_INDEX_3 +0x22A0 VAP_PVS_FLOW_CNTL_LOOP_INDEX_4 +0x22A4 VAP_PVS_FLOW_CNTL_LOOP_INDEX_5 +0x22A8 VAP_PVS_FLOW_CNTL_LOOP_INDEX_6 +0x22AC VAP_PVS_FLOW_CNTL_LOOP_INDEX_7 +0x22B0 VAP_PVS_FLOW_CNTL_LOOP_INDEX_8 +0x22B4 VAP_PVS_FLOW_CNTL_LOOP_INDEX_9 +0x22B8 VAP_PVS_FLOW_CNTL_LOOP_INDEX_10 +0x22BC VAP_PVS_FLOW_CNTL_LOOP_INDEX_11 +0x22C0 VAP_PVS_FLOW_CNTL_LOOP_INDEX_12 +0x22C4 VAP_PVS_FLOW_CNTL_LOOP_INDEX_13 +0x22C8 VAP_PVS_FLOW_CNTL_LOOP_INDEX_14 +0x22CC VAP_PVS_FLOW_CNTL_LOOP_INDEX_15 +0x22D0 VAP_PVS_CODE_CNTL_0 +0x22D4 VAP_PVS_CONST_CNTL +0x22D8 VAP_PVS_CODE_CNTL_1 +0x22DC VAP_PVS_FLOW_CNTL_OPC +0x342C RB2D_DSTCACHE_CTLSTAT +0x4000 GB_VAP_RASTER_VTX_FMT_0 +0x4004 GB_VAP_RASTER_VTX_FMT_1 +0x4008 GB_ENABLE +0x4010 GB_MSPOS0 +0x4014 GB_MSPOS1 +0x401C GB_SELECT +0x4020 GB_AA_CONFIG +0x4024 GB_FIFO_SIZE +0x4100 TX_INVALTAGS +0x4200 GA_POINT_S0 +0x4204 GA_POINT_T0 +0x4208 GA_POINT_S1 +0x420C GA_POINT_T1 +0x4214 GA_TRIANGLE_STIPPLE +0x421C GA_POINT_SIZE +0x4230 GA_POINT_MINMAX +0x4234 GA_LINE_CNTL +0x4238 GA_LINE_STIPPLE_CONFIG +0x4260 GA_LINE_STIPPLE_VALUE +0x4264 GA_LINE_S0 +0x4268 GA_LINE_S1 +0x4278 GA_COLOR_CONTROL +0x427C GA_SOLID_RG +0x4280 GA_SOLID_BA +0x4288 GA_POLY_MODE +0x428C GA_ROUND_MODE +0x4290 GA_OFFSET +0x4294 GA_FOG_SCALE +0x4298 GA_FOG_OFFSET +0x42A0 SU_TEX_WRAP +0x42A4 SU_POLY_OFFSET_FRONT_SCALE +0x42A8 SU_POLY_OFFSET_FRONT_OFFSET +0x42AC SU_POLY_OFFSET_BACK_SCALE +0x42B0 SU_POLY_OFFSET_BACK_OFFSET +0x42B4 SU_POLY_OFFSET_ENABLE +0x42B8 SU_CULL_MODE +0x42C0 SU_DEPTH_SCALE +0x42C4 SU_DEPTH_OFFSET +0x42C8 SU_REG_DEST +0x4300 RS_COUNT +0x4304 RS_INST_COUNT +0x4310 RS_IP_0 +0x4314 RS_IP_1 +0x4318 RS_IP_2 +0x431C RS_IP_3 +0x4320 RS_IP_4 +0x4324 RS_IP_5 +0x4328 RS_IP_6 +0x432C RS_IP_7 +0x4330 RS_INST_0 +0x4334 RS_INST_1 +0x4338 RS_INST_2 +0x433C RS_INST_3 +0x4340 RS_INST_4 +0x4344 RS_INST_5 +0x4348 RS_INST_6 +0x434C RS_INST_7 +0x4350 RS_INST_8 +0x4354 RS_INST_9 +0x4358 RS_INST_10 +0x435C RS_INST_11 +0x4360 RS_INST_12 +0x4364 RS_INST_13 +0x4368 RS_INST_14 +0x436C RS_INST_15 +0x43A8 SC_EDGERULE +0x43B0 SC_CLIP_0_A +0x43B4 SC_CLIP_0_B +0x43B8 SC_CLIP_1_A +0x43BC SC_CLIP_1_B +0x43C0 SC_CLIP_2_A +0x43C4 SC_CLIP_2_B +0x43C8 SC_CLIP_3_A +0x43CC SC_CLIP_3_B +0x43D0 SC_CLIP_RULE +0x43E0 SC_SCISSOR0 +0x43E8 SC_SCREENDOOR +0x4440 TX_FILTER1_0 +0x4444 TX_FILTER1_1 +0x4448 TX_FILTER1_2 +0x444C TX_FILTER1_3 +0x4450 TX_FILTER1_4 +0x4454 TX_FILTER1_5 +0x4458 TX_FILTER1_6 +0x445C TX_FILTER1_7 +0x4460 TX_FILTER1_8 +0x4464 TX_FILTER1_9 +0x4468 TX_FILTER1_10 +0x446C TX_FILTER1_11 +0x4470 TX_FILTER1_12 +0x4474 TX_FILTER1_13 +0x4478 TX_FILTER1_14 +0x447C TX_FILTER1_15 +0x4580 TX_CHROMA_KEY_0 +0x4584 TX_CHROMA_KEY_1 +0x4588 TX_CHROMA_KEY_2 +0x458C TX_CHROMA_KEY_3 +0x4590 TX_CHROMA_KEY_4 +0x4594 TX_CHROMA_KEY_5 +0x4598 TX_CHROMA_KEY_6 +0x459C TX_CHROMA_KEY_7 +0x45A0 TX_CHROMA_KEY_8 +0x45A4 TX_CHROMA_KEY_9 +0x45A8 TX_CHROMA_KEY_10 +0x45AC TX_CHROMA_KEY_11 +0x45B0 TX_CHROMA_KEY_12 +0x45B4 TX_CHROMA_KEY_13 +0x45B8 TX_CHROMA_KEY_14 +0x45BC TX_CHROMA_KEY_15 +0x45C0 TX_BORDER_COLOR_0 +0x45C4 TX_BORDER_COLOR_1 +0x45C8 TX_BORDER_COLOR_2 +0x45CC TX_BORDER_COLOR_3 +0x45D0 TX_BORDER_COLOR_4 +0x45D4 TX_BORDER_COLOR_5 +0x45D8 TX_BORDER_COLOR_6 +0x45DC TX_BORDER_COLOR_7 +0x45E0 TX_BORDER_COLOR_8 +0x45E4 TX_BORDER_COLOR_9 +0x45E8 TX_BORDER_COLOR_10 +0x45EC TX_BORDER_COLOR_11 +0x45F0 TX_BORDER_COLOR_12 +0x45F4 TX_BORDER_COLOR_13 +0x45F8 TX_BORDER_COLOR_14 +0x45FC TX_BORDER_COLOR_15 +0x4600 US_CONFIG +0x4604 US_PIXSIZE +0x4608 US_CODE_OFFSET +0x460C US_RESET +0x4610 US_CODE_ADDR_0 +0x4614 US_CODE_ADDR_1 +0x4618 US_CODE_ADDR_2 +0x461C US_CODE_ADDR_3 +0x4620 US_TEX_INST_0 +0x4624 US_TEX_INST_1 +0x4628 US_TEX_INST_2 +0x462C US_TEX_INST_3 +0x4630 US_TEX_INST_4 +0x4634 US_TEX_INST_5 +0x4638 US_TEX_INST_6 +0x463C US_TEX_INST_7 +0x4640 US_TEX_INST_8 +0x4644 US_TEX_INST_9 +0x4648 US_TEX_INST_10 +0x464C US_TEX_INST_11 +0x4650 US_TEX_INST_12 +0x4654 US_TEX_INST_13 +0x4658 US_TEX_INST_14 +0x465C US_TEX_INST_15 +0x4660 US_TEX_INST_16 +0x4664 US_TEX_INST_17 +0x4668 US_TEX_INST_18 +0x466C US_TEX_INST_19 +0x4670 US_TEX_INST_20 +0x4674 US_TEX_INST_21 +0x4678 US_TEX_INST_22 +0x467C US_TEX_INST_23 +0x4680 US_TEX_INST_24 +0x4684 US_TEX_INST_25 +0x4688 US_TEX_INST_26 +0x468C US_TEX_INST_27 +0x4690 US_TEX_INST_28 +0x4694 US_TEX_INST_29 +0x4698 US_TEX_INST_30 +0x469C US_TEX_INST_31 +0x46A4 US_OUT_FMT_0 +0x46A8 US_OUT_FMT_1 +0x46AC US_OUT_FMT_2 +0x46B0 US_OUT_FMT_3 +0x46B4 US_W_FMT +0x46B8 US_CODE_BANK +0x46BC US_CODE_EXT +0x46C0 US_ALU_RGB_ADDR_0 +0x46C4 US_ALU_RGB_ADDR_1 +0x46C8 US_ALU_RGB_ADDR_2 +0x46CC US_ALU_RGB_ADDR_3 +0x46D0 US_ALU_RGB_ADDR_4 +0x46D4 US_ALU_RGB_ADDR_5 +0x46D8 US_ALU_RGB_ADDR_6 +0x46DC US_ALU_RGB_ADDR_7 +0x46E0 US_ALU_RGB_ADDR_8 +0x46E4 US_ALU_RGB_ADDR_9 +0x46E8 US_ALU_RGB_ADDR_10 +0x46EC US_ALU_RGB_ADDR_11 +0x46F0 US_ALU_RGB_ADDR_12 +0x46F4 US_ALU_RGB_ADDR_13 +0x46F8 US_ALU_RGB_ADDR_14 +0x46FC US_ALU_RGB_ADDR_15 +0x4700 US_ALU_RGB_ADDR_16 +0x4704 US_ALU_RGB_ADDR_17 +0x4708 US_ALU_RGB_ADDR_18 +0x470C US_ALU_RGB_ADDR_19 +0x4710 US_ALU_RGB_ADDR_20 +0x4714 US_ALU_RGB_ADDR_21 +0x4718 US_ALU_RGB_ADDR_22 +0x471C US_ALU_RGB_ADDR_23 +0x4720 US_ALU_RGB_ADDR_24 +0x4724 US_ALU_RGB_ADDR_25 +0x4728 US_ALU_RGB_ADDR_26 +0x472C US_ALU_RGB_ADDR_27 +0x4730 US_ALU_RGB_ADDR_28 +0x4734 US_ALU_RGB_ADDR_29 +0x4738 US_ALU_RGB_ADDR_30 +0x473C US_ALU_RGB_ADDR_31 +0x4740 US_ALU_RGB_ADDR_32 +0x4744 US_ALU_RGB_ADDR_33 +0x4748 US_ALU_RGB_ADDR_34 +0x474C US_ALU_RGB_ADDR_35 +0x4750 US_ALU_RGB_ADDR_36 +0x4754 US_ALU_RGB_ADDR_37 +0x4758 US_ALU_RGB_ADDR_38 +0x475C US_ALU_RGB_ADDR_39 +0x4760 US_ALU_RGB_ADDR_40 +0x4764 US_ALU_RGB_ADDR_41 +0x4768 US_ALU_RGB_ADDR_42 +0x476C US_ALU_RGB_ADDR_43 +0x4770 US_ALU_RGB_ADDR_44 +0x4774 US_ALU_RGB_ADDR_45 +0x4778 US_ALU_RGB_ADDR_46 +0x477C US_ALU_RGB_ADDR_47 +0x4780 US_ALU_RGB_ADDR_48 +0x4784 US_ALU_RGB_ADDR_49 +0x4788 US_ALU_RGB_ADDR_50 +0x478C US_ALU_RGB_ADDR_51 +0x4790 US_ALU_RGB_ADDR_52 +0x4794 US_ALU_RGB_ADDR_53 +0x4798 US_ALU_RGB_ADDR_54 +0x479C US_ALU_RGB_ADDR_55 +0x47A0 US_ALU_RGB_ADDR_56 +0x47A4 US_ALU_RGB_ADDR_57 +0x47A8 US_ALU_RGB_ADDR_58 +0x47AC US_ALU_RGB_ADDR_59 +0x47B0 US_ALU_RGB_ADDR_60 +0x47B4 US_ALU_RGB_ADDR_61 +0x47B8 US_ALU_RGB_ADDR_62 +0x47BC US_ALU_RGB_ADDR_63 +0x47C0 US_ALU_ALPHA_ADDR_0 +0x47C4 US_ALU_ALPHA_ADDR_1 +0x47C8 US_ALU_ALPHA_ADDR_2 +0x47CC US_ALU_ALPHA_ADDR_3 +0x47D0 US_ALU_ALPHA_ADDR_4 +0x47D4 US_ALU_ALPHA_ADDR_5 +0x47D8 US_ALU_ALPHA_ADDR_6 +0x47DC US_ALU_ALPHA_ADDR_7 +0x47E0 US_ALU_ALPHA_ADDR_8 +0x47E4 US_ALU_ALPHA_ADDR_9 +0x47E8 US_ALU_ALPHA_ADDR_10 +0x47EC US_ALU_ALPHA_ADDR_11 +0x47F0 US_ALU_ALPHA_ADDR_12 +0x47F4 US_ALU_ALPHA_ADDR_13 +0x47F8 US_ALU_ALPHA_ADDR_14 +0x47FC US_ALU_ALPHA_ADDR_15 +0x4800 US_ALU_ALPHA_ADDR_16 +0x4804 US_ALU_ALPHA_ADDR_17 +0x4808 US_ALU_ALPHA_ADDR_18 +0x480C US_ALU_ALPHA_ADDR_19 +0x4810 US_ALU_ALPHA_ADDR_20 +0x4814 US_ALU_ALPHA_ADDR_21 +0x4818 US_ALU_ALPHA_ADDR_22 +0x481C US_ALU_ALPHA_ADDR_23 +0x4820 US_ALU_ALPHA_ADDR_24 +0x4824 US_ALU_ALPHA_ADDR_25 +0x4828 US_ALU_ALPHA_ADDR_26 +0x482C US_ALU_ALPHA_ADDR_27 +0x4830 US_ALU_ALPHA_ADDR_28 +0x4834 US_ALU_ALPHA_ADDR_29 +0x4838 US_ALU_ALPHA_ADDR_30 +0x483C US_ALU_ALPHA_ADDR_31 +0x4840 US_ALU_ALPHA_ADDR_32 +0x4844 US_ALU_ALPHA_ADDR_33 +0x4848 US_ALU_ALPHA_ADDR_34 +0x484C US_ALU_ALPHA_ADDR_35 +0x4850 US_ALU_ALPHA_ADDR_36 +0x4854 US_ALU_ALPHA_ADDR_37 +0x4858 US_ALU_ALPHA_ADDR_38 +0x485C US_ALU_ALPHA_ADDR_39 +0x4860 US_ALU_ALPHA_ADDR_40 +0x4864 US_ALU_ALPHA_ADDR_41 +0x4868 US_ALU_ALPHA_ADDR_42 +0x486C US_ALU_ALPHA_ADDR_43 +0x4870 US_ALU_ALPHA_ADDR_44 +0x4874 US_ALU_ALPHA_ADDR_45 +0x4878 US_ALU_ALPHA_ADDR_46 +0x487C US_ALU_ALPHA_ADDR_47 +0x4880 US_ALU_ALPHA_ADDR_48 +0x4884 US_ALU_ALPHA_ADDR_49 +0x4888 US_ALU_ALPHA_ADDR_50 +0x488C US_ALU_ALPHA_ADDR_51 +0x4890 US_ALU_ALPHA_ADDR_52 +0x4894 US_ALU_ALPHA_ADDR_53 +0x4898 US_ALU_ALPHA_ADDR_54 +0x489C US_ALU_ALPHA_ADDR_55 +0x48A0 US_ALU_ALPHA_ADDR_56 +0x48A4 US_ALU_ALPHA_ADDR_57 +0x48A8 US_ALU_ALPHA_ADDR_58 +0x48AC US_ALU_ALPHA_ADDR_59 +0x48B0 US_ALU_ALPHA_ADDR_60 +0x48B4 US_ALU_ALPHA_ADDR_61 +0x48B8 US_ALU_ALPHA_ADDR_62 +0x48BC US_ALU_ALPHA_ADDR_63 +0x48C0 US_ALU_RGB_INST_0 +0x48C4 US_ALU_RGB_INST_1 +0x48C8 US_ALU_RGB_INST_2 +0x48CC US_ALU_RGB_INST_3 +0x48D0 US_ALU_RGB_INST_4 +0x48D4 US_ALU_RGB_INST_5 +0x48D8 US_ALU_RGB_INST_6 +0x48DC US_ALU_RGB_INST_7 +0x48E0 US_ALU_RGB_INST_8 +0x48E4 US_ALU_RGB_INST_9 +0x48E8 US_ALU_RGB_INST_10 +0x48EC US_ALU_RGB_INST_11 +0x48F0 US_ALU_RGB_INST_12 +0x48F4 US_ALU_RGB_INST_13 +0x48F8 US_ALU_RGB_INST_14 +0x48FC US_ALU_RGB_INST_15 +0x4900 US_ALU_RGB_INST_16 +0x4904 US_ALU_RGB_INST_17 +0x4908 US_ALU_RGB_INST_18 +0x490C US_ALU_RGB_INST_19 +0x4910 US_ALU_RGB_INST_20 +0x4914 US_ALU_RGB_INST_21 +0x4918 US_ALU_RGB_INST_22 +0x491C US_ALU_RGB_INST_23 +0x4920 US_ALU_RGB_INST_24 +0x4924 US_ALU_RGB_INST_25 +0x4928 US_ALU_RGB_INST_26 +0x492C US_ALU_RGB_INST_27 +0x4930 US_ALU_RGB_INST_28 +0x4934 US_ALU_RGB_INST_29 +0x4938 US_ALU_RGB_INST_30 +0x493C US_ALU_RGB_INST_31 +0x4940 US_ALU_RGB_INST_32 +0x4944 US_ALU_RGB_INST_33 +0x4948 US_ALU_RGB_INST_34 +0x494C US_ALU_RGB_INST_35 +0x4950 US_ALU_RGB_INST_36 +0x4954 US_ALU_RGB_INST_37 +0x4958 US_ALU_RGB_INST_38 +0x495C US_ALU_RGB_INST_39 +0x4960 US_ALU_RGB_INST_40 +0x4964 US_ALU_RGB_INST_41 +0x4968 US_ALU_RGB_INST_42 +0x496C US_ALU_RGB_INST_43 +0x4970 US_ALU_RGB_INST_44 +0x4974 US_ALU_RGB_INST_45 +0x4978 US_ALU_RGB_INST_46 +0x497C US_ALU_RGB_INST_47 +0x4980 US_ALU_RGB_INST_48 +0x4984 US_ALU_RGB_INST_49 +0x4988 US_ALU_RGB_INST_50 +0x498C US_ALU_RGB_INST_51 +0x4990 US_ALU_RGB_INST_52 +0x4994 US_ALU_RGB_INST_53 +0x4998 US_ALU_RGB_INST_54 +0x499C US_ALU_RGB_INST_55 +0x49A0 US_ALU_RGB_INST_56 +0x49A4 US_ALU_RGB_INST_57 +0x49A8 US_ALU_RGB_INST_58 +0x49AC US_ALU_RGB_INST_59 +0x49B0 US_ALU_RGB_INST_60 +0x49B4 US_ALU_RGB_INST_61 +0x49B8 US_ALU_RGB_INST_62 +0x49BC US_ALU_RGB_INST_63 +0x49C0 US_ALU_ALPHA_INST_0 +0x49C4 US_ALU_ALPHA_INST_1 +0x49C8 US_ALU_ALPHA_INST_2 +0x49CC US_ALU_ALPHA_INST_3 +0x49D0 US_ALU_ALPHA_INST_4 +0x49D4 US_ALU_ALPHA_INST_5 +0x49D8 US_ALU_ALPHA_INST_6 +0x49DC US_ALU_ALPHA_INST_7 +0x49E0 US_ALU_ALPHA_INST_8 +0x49E4 US_ALU_ALPHA_INST_9 +0x49E8 US_ALU_ALPHA_INST_10 +0x49EC US_ALU_ALPHA_INST_11 +0x49F0 US_ALU_ALPHA_INST_12 +0x49F4 US_ALU_ALPHA_INST_13 +0x49F8 US_ALU_ALPHA_INST_14 +0x49FC US_ALU_ALPHA_INST_15 +0x4A00 US_ALU_ALPHA_INST_16 +0x4A04 US_ALU_ALPHA_INST_17 +0x4A08 US_ALU_ALPHA_INST_18 +0x4A0C US_ALU_ALPHA_INST_19 +0x4A10 US_ALU_ALPHA_INST_20 +0x4A14 US_ALU_ALPHA_INST_21 +0x4A18 US_ALU_ALPHA_INST_22 +0x4A1C US_ALU_ALPHA_INST_23 +0x4A20 US_ALU_ALPHA_INST_24 +0x4A24 US_ALU_ALPHA_INST_25 +0x4A28 US_ALU_ALPHA_INST_26 +0x4A2C US_ALU_ALPHA_INST_27 +0x4A30 US_ALU_ALPHA_INST_28 +0x4A34 US_ALU_ALPHA_INST_29 +0x4A38 US_ALU_ALPHA_INST_30 +0x4A3C US_ALU_ALPHA_INST_31 +0x4A40 US_ALU_ALPHA_INST_32 +0x4A44 US_ALU_ALPHA_INST_33 +0x4A48 US_ALU_ALPHA_INST_34 +0x4A4C US_ALU_ALPHA_INST_35 +0x4A50 US_ALU_ALPHA_INST_36 +0x4A54 US_ALU_ALPHA_INST_37 +0x4A58 US_ALU_ALPHA_INST_38 +0x4A5C US_ALU_ALPHA_INST_39 +0x4A60 US_ALU_ALPHA_INST_40 +0x4A64 US_ALU_ALPHA_INST_41 +0x4A68 US_ALU_ALPHA_INST_42 +0x4A6C US_ALU_ALPHA_INST_43 +0x4A70 US_ALU_ALPHA_INST_44 +0x4A74 US_ALU_ALPHA_INST_45 +0x4A78 US_ALU_ALPHA_INST_46 +0x4A7C US_ALU_ALPHA_INST_47 +0x4A80 US_ALU_ALPHA_INST_48 +0x4A84 US_ALU_ALPHA_INST_49 +0x4A88 US_ALU_ALPHA_INST_50 +0x4A8C US_ALU_ALPHA_INST_51 +0x4A90 US_ALU_ALPHA_INST_52 +0x4A94 US_ALU_ALPHA_INST_53 +0x4A98 US_ALU_ALPHA_INST_54 +0x4A9C US_ALU_ALPHA_INST_55 +0x4AA0 US_ALU_ALPHA_INST_56 +0x4AA4 US_ALU_ALPHA_INST_57 +0x4AA8 US_ALU_ALPHA_INST_58 +0x4AAC US_ALU_ALPHA_INST_59 +0x4AB0 US_ALU_ALPHA_INST_60 +0x4AB4 US_ALU_ALPHA_INST_61 +0x4AB8 US_ALU_ALPHA_INST_62 +0x4ABC US_ALU_ALPHA_INST_63 +0x4AC0 US_ALU_EXT_ADDR_0 +0x4AC4 US_ALU_EXT_ADDR_1 +0x4AC8 US_ALU_EXT_ADDR_2 +0x4ACC US_ALU_EXT_ADDR_3 +0x4AD0 US_ALU_EXT_ADDR_4 +0x4AD4 US_ALU_EXT_ADDR_5 +0x4AD8 US_ALU_EXT_ADDR_6 +0x4ADC US_ALU_EXT_ADDR_7 +0x4AE0 US_ALU_EXT_ADDR_8 +0x4AE4 US_ALU_EXT_ADDR_9 +0x4AE8 US_ALU_EXT_ADDR_10 +0x4AEC US_ALU_EXT_ADDR_11 +0x4AF0 US_ALU_EXT_ADDR_12 +0x4AF4 US_ALU_EXT_ADDR_13 +0x4AF8 US_ALU_EXT_ADDR_14 +0x4AFC US_ALU_EXT_ADDR_15 +0x4B00 US_ALU_EXT_ADDR_16 +0x4B04 US_ALU_EXT_ADDR_17 +0x4B08 US_ALU_EXT_ADDR_18 +0x4B0C US_ALU_EXT_ADDR_19 +0x4B10 US_ALU_EXT_ADDR_20 +0x4B14 US_ALU_EXT_ADDR_21 +0x4B18 US_ALU_EXT_ADDR_22 +0x4B1C US_ALU_EXT_ADDR_23 +0x4B20 US_ALU_EXT_ADDR_24 +0x4B24 US_ALU_EXT_ADDR_25 +0x4B28 US_ALU_EXT_ADDR_26 +0x4B2C US_ALU_EXT_ADDR_27 +0x4B30 US_ALU_EXT_ADDR_28 +0x4B34 US_ALU_EXT_ADDR_29 +0x4B38 US_ALU_EXT_ADDR_30 +0x4B3C US_ALU_EXT_ADDR_31 +0x4B40 US_ALU_EXT_ADDR_32 +0x4B44 US_ALU_EXT_ADDR_33 +0x4B48 US_ALU_EXT_ADDR_34 +0x4B4C US_ALU_EXT_ADDR_35 +0x4B50 US_ALU_EXT_ADDR_36 +0x4B54 US_ALU_EXT_ADDR_37 +0x4B58 US_ALU_EXT_ADDR_38 +0x4B5C US_ALU_EXT_ADDR_39 +0x4B60 US_ALU_EXT_ADDR_40 +0x4B64 US_ALU_EXT_ADDR_41 +0x4B68 US_ALU_EXT_ADDR_42 +0x4B6C US_ALU_EXT_ADDR_43 +0x4B70 US_ALU_EXT_ADDR_44 +0x4B74 US_ALU_EXT_ADDR_45 +0x4B78 US_ALU_EXT_ADDR_46 +0x4B7C US_ALU_EXT_ADDR_47 +0x4B80 US_ALU_EXT_ADDR_48 +0x4B84 US_ALU_EXT_ADDR_49 +0x4B88 US_ALU_EXT_ADDR_50 +0x4B8C US_ALU_EXT_ADDR_51 +0x4B90 US_ALU_EXT_ADDR_52 +0x4B94 US_ALU_EXT_ADDR_53 +0x4B98 US_ALU_EXT_ADDR_54 +0x4B9C US_ALU_EXT_ADDR_55 +0x4BA0 US_ALU_EXT_ADDR_56 +0x4BA4 US_ALU_EXT_ADDR_57 +0x4BA8 US_ALU_EXT_ADDR_58 +0x4BAC US_ALU_EXT_ADDR_59 +0x4BB0 US_ALU_EXT_ADDR_60 +0x4BB4 US_ALU_EXT_ADDR_61 +0x4BB8 US_ALU_EXT_ADDR_62 +0x4BBC US_ALU_EXT_ADDR_63 +0x4BC0 FG_FOG_BLEND +0x4BC4 FG_FOG_FACTOR +0x4BC8 FG_FOG_COLOR_R +0x4BCC FG_FOG_COLOR_G +0x4BD0 FG_FOG_COLOR_B +0x4BD4 FG_ALPHA_FUNC +0x4BD8 FG_DEPTH_SRC +0x4C00 US_ALU_CONST_R_0 +0x4C04 US_ALU_CONST_G_0 +0x4C08 US_ALU_CONST_B_0 +0x4C0C US_ALU_CONST_A_0 +0x4C10 US_ALU_CONST_R_1 +0x4C14 US_ALU_CONST_G_1 +0x4C18 US_ALU_CONST_B_1 +0x4C1C US_ALU_CONST_A_1 +0x4C20 US_ALU_CONST_R_2 +0x4C24 US_ALU_CONST_G_2 +0x4C28 US_ALU_CONST_B_2 +0x4C2C US_ALU_CONST_A_2 +0x4C30 US_ALU_CONST_R_3 +0x4C34 US_ALU_CONST_G_3 +0x4C38 US_ALU_CONST_B_3 +0x4C3C US_ALU_CONST_A_3 +0x4C40 US_ALU_CONST_R_4 +0x4C44 US_ALU_CONST_G_4 +0x4C48 US_ALU_CONST_B_4 +0x4C4C US_ALU_CONST_A_4 +0x4C50 US_ALU_CONST_R_5 +0x4C54 US_ALU_CONST_G_5 +0x4C58 US_ALU_CONST_B_5 +0x4C5C US_ALU_CONST_A_5 +0x4C60 US_ALU_CONST_R_6 +0x4C64 US_ALU_CONST_G_6 +0x4C68 US_ALU_CONST_B_6 +0x4C6C US_ALU_CONST_A_6 +0x4C70 US_ALU_CONST_R_7 +0x4C74 US_ALU_CONST_G_7 +0x4C78 US_ALU_CONST_B_7 +0x4C7C US_ALU_CONST_A_7 +0x4C80 US_ALU_CONST_R_8 +0x4C84 US_ALU_CONST_G_8 +0x4C88 US_ALU_CONST_B_8 +0x4C8C US_ALU_CONST_A_8 +0x4C90 US_ALU_CONST_R_9 +0x4C94 US_ALU_CONST_G_9 +0x4C98 US_ALU_CONST_B_9 +0x4C9C US_ALU_CONST_A_9 +0x4CA0 US_ALU_CONST_R_10 +0x4CA4 US_ALU_CONST_G_10 +0x4CA8 US_ALU_CONST_B_10 +0x4CAC US_ALU_CONST_A_10 +0x4CB0 US_ALU_CONST_R_11 +0x4CB4 US_ALU_CONST_G_11 +0x4CB8 US_ALU_CONST_B_11 +0x4CBC US_ALU_CONST_A_11 +0x4CC0 US_ALU_CONST_R_12 +0x4CC4 US_ALU_CONST_G_12 +0x4CC8 US_ALU_CONST_B_12 +0x4CCC US_ALU_CONST_A_12 +0x4CD0 US_ALU_CONST_R_13 +0x4CD4 US_ALU_CONST_G_13 +0x4CD8 US_ALU_CONST_B_13 +0x4CDC US_ALU_CONST_A_13 +0x4CE0 US_ALU_CONST_R_14 +0x4CE4 US_ALU_CONST_G_14 +0x4CE8 US_ALU_CONST_B_14 +0x4CEC US_ALU_CONST_A_14 +0x4CF0 US_ALU_CONST_R_15 +0x4CF4 US_ALU_CONST_G_15 +0x4CF8 US_ALU_CONST_B_15 +0x4CFC US_ALU_CONST_A_15 +0x4D00 US_ALU_CONST_R_16 +0x4D04 US_ALU_CONST_G_16 +0x4D08 US_ALU_CONST_B_16 +0x4D0C US_ALU_CONST_A_16 +0x4D10 US_ALU_CONST_R_17 +0x4D14 US_ALU_CONST_G_17 +0x4D18 US_ALU_CONST_B_17 +0x4D1C US_ALU_CONST_A_17 +0x4D20 US_ALU_CONST_R_18 +0x4D24 US_ALU_CONST_G_18 +0x4D28 US_ALU_CONST_B_18 +0x4D2C US_ALU_CONST_A_18 +0x4D30 US_ALU_CONST_R_19 +0x4D34 US_ALU_CONST_G_19 +0x4D38 US_ALU_CONST_B_19 +0x4D3C US_ALU_CONST_A_19 +0x4D40 US_ALU_CONST_R_20 +0x4D44 US_ALU_CONST_G_20 +0x4D48 US_ALU_CONST_B_20 +0x4D4C US_ALU_CONST_A_20 +0x4D50 US_ALU_CONST_R_21 +0x4D54 US_ALU_CONST_G_21 +0x4D58 US_ALU_CONST_B_21 +0x4D5C US_ALU_CONST_A_21 +0x4D60 US_ALU_CONST_R_22 +0x4D64 US_ALU_CONST_G_22 +0x4D68 US_ALU_CONST_B_22 +0x4D6C US_ALU_CONST_A_22 +0x4D70 US_ALU_CONST_R_23 +0x4D74 US_ALU_CONST_G_23 +0x4D78 US_ALU_CONST_B_23 +0x4D7C US_ALU_CONST_A_23 +0x4D80 US_ALU_CONST_R_24 +0x4D84 US_ALU_CONST_G_24 +0x4D88 US_ALU_CONST_B_24 +0x4D8C US_ALU_CONST_A_24 +0x4D90 US_ALU_CONST_R_25 +0x4D94 US_ALU_CONST_G_25 +0x4D98 US_ALU_CONST_B_25 +0x4D9C US_ALU_CONST_A_25 +0x4DA0 US_ALU_CONST_R_26 +0x4DA4 US_ALU_CONST_G_26 +0x4DA8 US_ALU_CONST_B_26 +0x4DAC US_ALU_CONST_A_26 +0x4DB0 US_ALU_CONST_R_27 +0x4DB4 US_ALU_CONST_G_27 +0x4DB8 US_ALU_CONST_B_27 +0x4DBC US_ALU_CONST_A_27 +0x4DC0 US_ALU_CONST_R_28 +0x4DC4 US_ALU_CONST_G_28 +0x4DC8 US_ALU_CONST_B_28 +0x4DCC US_ALU_CONST_A_28 +0x4DD0 US_ALU_CONST_R_29 +0x4DD4 US_ALU_CONST_G_29 +0x4DD8 US_ALU_CONST_B_29 +0x4DDC US_ALU_CONST_A_29 +0x4DE0 US_ALU_CONST_R_30 +0x4DE4 US_ALU_CONST_G_30 +0x4DE8 US_ALU_CONST_B_30 +0x4DEC US_ALU_CONST_A_30 +0x4DF0 US_ALU_CONST_R_31 +0x4DF4 US_ALU_CONST_G_31 +0x4DF8 US_ALU_CONST_B_31 +0x4DFC US_ALU_CONST_A_31 +0x4E08 RB3D_ABLENDCNTL_R3 +0x4E10 RB3D_CONSTANT_COLOR +0x4E14 RB3D_COLOR_CLEAR_VALUE +0x4E18 RB3D_ROPCNTL_R3 +0x4E1C RB3D_CLRCMP_FLIPE_R3 +0x4E20 RB3D_CLRCMP_CLR_R3 +0x4E24 RB3D_CLRCMP_MSK_R3 +0x4E48 RB3D_DEBUG_CTL +0x4E4C RB3D_DSTCACHE_CTLSTAT_R3 +0x4E50 RB3D_DITHER_CTL +0x4E54 RB3D_CMASK_OFFSET0 +0x4E58 RB3D_CMASK_OFFSET1 +0x4E5C RB3D_CMASK_OFFSET2 +0x4E60 RB3D_CMASK_OFFSET3 +0x4E64 RB3D_CMASK_PITCH0 +0x4E68 RB3D_CMASK_PITCH1 +0x4E6C RB3D_CMASK_PITCH2 +0x4E70 RB3D_CMASK_PITCH3 +0x4E74 RB3D_CMASK_WRINDEX +0x4E78 RB3D_CMASK_DWORD +0x4E7C RB3D_CMASK_RDINDEX +0x4EA0 RB3D_DISCARD_SRC_PIXEL_LTE_THRESHOLD +0x4EA4 RB3D_DISCARD_SRC_PIXEL_GTE_THRESHOLD +0x4F04 ZB_ZSTENCILCNTL +0x4F08 ZB_STENCILREFMASK +0x4F14 ZB_ZTOP +0x4F18 ZB_ZCACHE_CTLSTAT +0x4F28 ZB_DEPTHCLEARVALUE +0x4F58 ZB_ZPASS_DATA diff --git a/sys/dev/drm2/radeon/reg_srcs/r600 b/sys/dev/drm2/radeon/reg_srcs/r600 new file mode 100644 index 00000000000..20bfbda7b3f --- /dev/null +++ b/sys/dev/drm2/radeon/reg_srcs/r600 @@ -0,0 +1,755 @@ +r600 0x9400 +0x000287A0 R7xx_CB_SHADER_CONTROL +0x00028230 R7xx_PA_SC_EDGERULE +0x000286C8 R7xx_SPI_THREAD_GROUPING +0x00008D8C R7xx_SQ_DYN_GPR_CNTL_PS_FLUSH_REQ +0x00008490 CP_STRMOUT_CNTL +0x000085F0 CP_COHER_CNTL +0x000085F4 CP_COHER_SIZE +0x000088C4 VGT_CACHE_INVALIDATION +0x00028A50 VGT_ENHANCE +0x000088CC VGT_ES_PER_GS +0x00028A2C VGT_GROUP_DECR +0x00028A28 VGT_GROUP_FIRST_DECR +0x00028A24 VGT_GROUP_PRIM_TYPE +0x00028A30 VGT_GROUP_VECT_0_CNTL +0x00028A38 VGT_GROUP_VECT_0_FMT_CNTL +0x00028A34 VGT_GROUP_VECT_1_CNTL +0x00028A3C VGT_GROUP_VECT_1_FMT_CNTL +0x00028A40 VGT_GS_MODE +0x00028A6C VGT_GS_OUT_PRIM_TYPE +0x000088C8 VGT_GS_PER_ES +0x000088E8 VGT_GS_PER_VS +0x000088D4 VGT_GS_VERTEX_REUSE +0x00028A14 VGT_HOS_CNTL +0x00028A18 VGT_HOS_MAX_TESS_LEVEL +0x00028A1C VGT_HOS_MIN_TESS_LEVEL +0x00028A20 VGT_HOS_REUSE_DEPTH +0x0000895C VGT_INDEX_TYPE +0x00028408 VGT_INDX_OFFSET +0x00028AA0 VGT_INSTANCE_STEP_RATE_0 +0x00028AA4 VGT_INSTANCE_STEP_RATE_1 +0x00028400 VGT_MAX_VTX_INDX +0x00028404 VGT_MIN_VTX_INDX +0x00028A94 VGT_MULTI_PRIM_IB_RESET_EN +0x0002840C VGT_MULTI_PRIM_IB_RESET_INDX +0x00008970 VGT_NUM_INDICES +0x00008974 VGT_NUM_INSTANCES +0x00028A10 VGT_OUTPUT_PATH_CNTL +0x00028A84 VGT_PRIMITIVEID_EN +0x00008958 VGT_PRIMITIVE_TYPE +0x00028AB4 VGT_REUSE_OFF +0x00028AB8 VGT_VTX_CNT_EN +0x000088B0 VGT_VTX_VECT_EJECT_REG +0x00028AD4 VGT_STRMOUT_VTX_STRIDE_0 +0x00028AE4 VGT_STRMOUT_VTX_STRIDE_1 +0x00028AF4 VGT_STRMOUT_VTX_STRIDE_2 +0x00028B04 VGT_STRMOUT_VTX_STRIDE_3 +0x00028B28 VGT_STRMOUT_DRAW_OPAQUE_OFFSET +0x00028B2C VGT_STRMOUT_DRAW_OPAQUE_BUFFER_FILLED_SIZE +0x00028B30 VGT_STRMOUT_DRAW_OPAQUE_VERTEX_STRIDE +0x00028810 PA_CL_CLIP_CNTL +0x00008A14 PA_CL_ENHANCE +0x00028C14 PA_CL_GB_HORZ_CLIP_ADJ +0x00028C18 PA_CL_GB_HORZ_DISC_ADJ +0x00028C0C PA_CL_GB_VERT_CLIP_ADJ +0x00028C10 PA_CL_GB_VERT_DISC_ADJ +0x00028820 PA_CL_NANINF_CNTL +0x00028E1C PA_CL_POINT_CULL_RAD +0x00028E18 PA_CL_POINT_SIZE +0x00028E10 PA_CL_POINT_X_RAD +0x00028E14 PA_CL_POINT_Y_RAD +0x00028E2C PA_CL_UCP_0_W +0x00028E3C PA_CL_UCP_1_W +0x00028E4C PA_CL_UCP_2_W +0x00028E5C PA_CL_UCP_3_W +0x00028E6C PA_CL_UCP_4_W +0x00028E7C PA_CL_UCP_5_W +0x00028E20 PA_CL_UCP_0_X +0x00028E30 PA_CL_UCP_1_X +0x00028E40 PA_CL_UCP_2_X +0x00028E50 PA_CL_UCP_3_X +0x00028E60 PA_CL_UCP_4_X +0x00028E70 PA_CL_UCP_5_X +0x00028E24 PA_CL_UCP_0_Y +0x00028E34 PA_CL_UCP_1_Y +0x00028E44 PA_CL_UCP_2_Y +0x00028E54 PA_CL_UCP_3_Y +0x00028E64 PA_CL_UCP_4_Y +0x00028E74 PA_CL_UCP_5_Y +0x00028E28 PA_CL_UCP_0_Z +0x00028E38 PA_CL_UCP_1_Z +0x00028E48 PA_CL_UCP_2_Z +0x00028E58 PA_CL_UCP_3_Z +0x00028E68 PA_CL_UCP_4_Z +0x00028E78 PA_CL_UCP_5_Z +0x00028440 PA_CL_VPORT_XOFFSET_0 +0x00028458 PA_CL_VPORT_XOFFSET_1 +0x00028470 PA_CL_VPORT_XOFFSET_2 +0x00028488 PA_CL_VPORT_XOFFSET_3 +0x000284A0 PA_CL_VPORT_XOFFSET_4 +0x000284B8 PA_CL_VPORT_XOFFSET_5 +0x000284D0 PA_CL_VPORT_XOFFSET_6 +0x000284E8 PA_CL_VPORT_XOFFSET_7 +0x00028500 PA_CL_VPORT_XOFFSET_8 +0x00028518 PA_CL_VPORT_XOFFSET_9 +0x00028530 PA_CL_VPORT_XOFFSET_10 +0x00028548 PA_CL_VPORT_XOFFSET_11 +0x00028560 PA_CL_VPORT_XOFFSET_12 +0x00028578 PA_CL_VPORT_XOFFSET_13 +0x00028590 PA_CL_VPORT_XOFFSET_14 +0x000285A8 PA_CL_VPORT_XOFFSET_15 +0x0002843C PA_CL_VPORT_XSCALE_0 +0x00028454 PA_CL_VPORT_XSCALE_1 +0x0002846C PA_CL_VPORT_XSCALE_2 +0x00028484 PA_CL_VPORT_XSCALE_3 +0x0002849C PA_CL_VPORT_XSCALE_4 +0x000284B4 PA_CL_VPORT_XSCALE_5 +0x000284CC PA_CL_VPORT_XSCALE_6 +0x000284E4 PA_CL_VPORT_XSCALE_7 +0x000284FC PA_CL_VPORT_XSCALE_8 +0x00028514 PA_CL_VPORT_XSCALE_9 +0x0002852C PA_CL_VPORT_XSCALE_10 +0x00028544 PA_CL_VPORT_XSCALE_11 +0x0002855C PA_CL_VPORT_XSCALE_12 +0x00028574 PA_CL_VPORT_XSCALE_13 +0x0002858C PA_CL_VPORT_XSCALE_14 +0x000285A4 PA_CL_VPORT_XSCALE_15 +0x00028448 PA_CL_VPORT_YOFFSET_0 +0x00028460 PA_CL_VPORT_YOFFSET_1 +0x00028478 PA_CL_VPORT_YOFFSET_2 +0x00028490 PA_CL_VPORT_YOFFSET_3 +0x000284A8 PA_CL_VPORT_YOFFSET_4 +0x000284C0 PA_CL_VPORT_YOFFSET_5 +0x000284D8 PA_CL_VPORT_YOFFSET_6 +0x000284F0 PA_CL_VPORT_YOFFSET_7 +0x00028508 PA_CL_VPORT_YOFFSET_8 +0x00028520 PA_CL_VPORT_YOFFSET_9 +0x00028538 PA_CL_VPORT_YOFFSET_10 +0x00028550 PA_CL_VPORT_YOFFSET_11 +0x00028568 PA_CL_VPORT_YOFFSET_12 +0x00028580 PA_CL_VPORT_YOFFSET_13 +0x00028598 PA_CL_VPORT_YOFFSET_14 +0x000285B0 PA_CL_VPORT_YOFFSET_15 +0x00028444 PA_CL_VPORT_YSCALE_0 +0x0002845C PA_CL_VPORT_YSCALE_1 +0x00028474 PA_CL_VPORT_YSCALE_2 +0x0002848C PA_CL_VPORT_YSCALE_3 +0x000284A4 PA_CL_VPORT_YSCALE_4 +0x000284BC PA_CL_VPORT_YSCALE_5 +0x000284D4 PA_CL_VPORT_YSCALE_6 +0x000284EC PA_CL_VPORT_YSCALE_7 +0x00028504 PA_CL_VPORT_YSCALE_8 +0x0002851C PA_CL_VPORT_YSCALE_9 +0x00028534 PA_CL_VPORT_YSCALE_10 +0x0002854C PA_CL_VPORT_YSCALE_11 +0x00028564 PA_CL_VPORT_YSCALE_12 +0x0002857C PA_CL_VPORT_YSCALE_13 +0x00028594 PA_CL_VPORT_YSCALE_14 +0x000285AC PA_CL_VPORT_YSCALE_15 +0x00028450 PA_CL_VPORT_ZOFFSET_0 +0x00028468 PA_CL_VPORT_ZOFFSET_1 +0x00028480 PA_CL_VPORT_ZOFFSET_2 +0x00028498 PA_CL_VPORT_ZOFFSET_3 +0x000284B0 PA_CL_VPORT_ZOFFSET_4 +0x000284C8 PA_CL_VPORT_ZOFFSET_5 +0x000284E0 PA_CL_VPORT_ZOFFSET_6 +0x000284F8 PA_CL_VPORT_ZOFFSET_7 +0x00028510 PA_CL_VPORT_ZOFFSET_8 +0x00028528 PA_CL_VPORT_ZOFFSET_9 +0x00028540 PA_CL_VPORT_ZOFFSET_10 +0x00028558 PA_CL_VPORT_ZOFFSET_11 +0x00028570 PA_CL_VPORT_ZOFFSET_12 +0x00028588 PA_CL_VPORT_ZOFFSET_13 +0x000285A0 PA_CL_VPORT_ZOFFSET_14 +0x000285B8 PA_CL_VPORT_ZOFFSET_15 +0x0002844C PA_CL_VPORT_ZSCALE_0 +0x00028464 PA_CL_VPORT_ZSCALE_1 +0x0002847C PA_CL_VPORT_ZSCALE_2 +0x00028494 PA_CL_VPORT_ZSCALE_3 +0x000284AC PA_CL_VPORT_ZSCALE_4 +0x000284C4 PA_CL_VPORT_ZSCALE_5 +0x000284DC PA_CL_VPORT_ZSCALE_6 +0x000284F4 PA_CL_VPORT_ZSCALE_7 +0x0002850C PA_CL_VPORT_ZSCALE_8 +0x00028524 PA_CL_VPORT_ZSCALE_9 +0x0002853C PA_CL_VPORT_ZSCALE_10 +0x00028554 PA_CL_VPORT_ZSCALE_11 +0x0002856C PA_CL_VPORT_ZSCALE_12 +0x00028584 PA_CL_VPORT_ZSCALE_13 +0x0002859C PA_CL_VPORT_ZSCALE_14 +0x000285B4 PA_CL_VPORT_ZSCALE_15 +0x0002881C PA_CL_VS_OUT_CNTL +0x00028818 PA_CL_VTE_CNTL +0x00028C48 PA_SC_AA_MASK +0x00008B40 PA_SC_AA_SAMPLE_LOCS_2S +0x00008B44 PA_SC_AA_SAMPLE_LOCS_4S +0x00008B48 PA_SC_AA_SAMPLE_LOCS_8S_WD0 +0x00008B4C PA_SC_AA_SAMPLE_LOCS_8S_WD1 +0x00028C20 PA_SC_AA_SAMPLE_LOCS_8S_WD1_MCTX +0x00028C1C PA_SC_AA_SAMPLE_LOCS_MCTX +0x00028214 PA_SC_CLIPRECT_0_BR +0x0002821C PA_SC_CLIPRECT_1_BR +0x00028224 PA_SC_CLIPRECT_2_BR +0x0002822C PA_SC_CLIPRECT_3_BR +0x00028210 PA_SC_CLIPRECT_0_TL +0x00028218 PA_SC_CLIPRECT_1_TL +0x00028220 PA_SC_CLIPRECT_2_TL +0x00028228 PA_SC_CLIPRECT_3_TL +0x0002820C PA_SC_CLIPRECT_RULE +0x00008BF0 PA_SC_ENHANCE +0x00028244 PA_SC_GENERIC_SCISSOR_BR +0x00028240 PA_SC_GENERIC_SCISSOR_TL +0x00028C00 PA_SC_LINE_CNTL +0x00028A0C PA_SC_LINE_STIPPLE +0x00008B10 PA_SC_LINE_STIPPLE_STATE +0x00028A4C PA_SC_MODE_CNTL +0x00028A48 PA_SC_MPASS_PS_CNTL +0x00008B20 PA_SC_MULTI_CHIP_CNTL +0x00028034 PA_SC_SCREEN_SCISSOR_BR +0x00028030 PA_SC_SCREEN_SCISSOR_TL +0x00028254 PA_SC_VPORT_SCISSOR_0_BR +0x0002825C PA_SC_VPORT_SCISSOR_1_BR +0x00028264 PA_SC_VPORT_SCISSOR_2_BR +0x0002826C PA_SC_VPORT_SCISSOR_3_BR +0x00028274 PA_SC_VPORT_SCISSOR_4_BR +0x0002827C PA_SC_VPORT_SCISSOR_5_BR +0x00028284 PA_SC_VPORT_SCISSOR_6_BR +0x0002828C PA_SC_VPORT_SCISSOR_7_BR +0x00028294 PA_SC_VPORT_SCISSOR_8_BR +0x0002829C PA_SC_VPORT_SCISSOR_9_BR +0x000282A4 PA_SC_VPORT_SCISSOR_10_BR +0x000282AC PA_SC_VPORT_SCISSOR_11_BR +0x000282B4 PA_SC_VPORT_SCISSOR_12_BR +0x000282BC PA_SC_VPORT_SCISSOR_13_BR +0x000282C4 PA_SC_VPORT_SCISSOR_14_BR +0x000282CC PA_SC_VPORT_SCISSOR_15_BR +0x00028250 PA_SC_VPORT_SCISSOR_0_TL +0x00028258 PA_SC_VPORT_SCISSOR_1_TL +0x00028260 PA_SC_VPORT_SCISSOR_2_TL +0x00028268 PA_SC_VPORT_SCISSOR_3_TL +0x00028270 PA_SC_VPORT_SCISSOR_4_TL +0x00028278 PA_SC_VPORT_SCISSOR_5_TL +0x00028280 PA_SC_VPORT_SCISSOR_6_TL +0x00028288 PA_SC_VPORT_SCISSOR_7_TL +0x00028290 PA_SC_VPORT_SCISSOR_8_TL +0x00028298 PA_SC_VPORT_SCISSOR_9_TL +0x000282A0 PA_SC_VPORT_SCISSOR_10_TL +0x000282A8 PA_SC_VPORT_SCISSOR_11_TL +0x000282B0 PA_SC_VPORT_SCISSOR_12_TL +0x000282B8 PA_SC_VPORT_SCISSOR_13_TL +0x000282C0 PA_SC_VPORT_SCISSOR_14_TL +0x000282C8 PA_SC_VPORT_SCISSOR_15_TL +0x000282D4 PA_SC_VPORT_ZMAX_0 +0x000282DC PA_SC_VPORT_ZMAX_1 +0x000282E4 PA_SC_VPORT_ZMAX_2 +0x000282EC PA_SC_VPORT_ZMAX_3 +0x000282F4 PA_SC_VPORT_ZMAX_4 +0x000282FC PA_SC_VPORT_ZMAX_5 +0x00028304 PA_SC_VPORT_ZMAX_6 +0x0002830C PA_SC_VPORT_ZMAX_7 +0x00028314 PA_SC_VPORT_ZMAX_8 +0x0002831C PA_SC_VPORT_ZMAX_9 +0x00028324 PA_SC_VPORT_ZMAX_10 +0x0002832C PA_SC_VPORT_ZMAX_11 +0x00028334 PA_SC_VPORT_ZMAX_12 +0x0002833C PA_SC_VPORT_ZMAX_13 +0x00028344 PA_SC_VPORT_ZMAX_14 +0x0002834C PA_SC_VPORT_ZMAX_15 +0x000282D0 PA_SC_VPORT_ZMIN_0 +0x000282D8 PA_SC_VPORT_ZMIN_1 +0x000282E0 PA_SC_VPORT_ZMIN_2 +0x000282E8 PA_SC_VPORT_ZMIN_3 +0x000282F0 PA_SC_VPORT_ZMIN_4 +0x000282F8 PA_SC_VPORT_ZMIN_5 +0x00028300 PA_SC_VPORT_ZMIN_6 +0x00028308 PA_SC_VPORT_ZMIN_7 +0x00028310 PA_SC_VPORT_ZMIN_8 +0x00028318 PA_SC_VPORT_ZMIN_9 +0x00028320 PA_SC_VPORT_ZMIN_10 +0x00028328 PA_SC_VPORT_ZMIN_11 +0x00028330 PA_SC_VPORT_ZMIN_12 +0x00028338 PA_SC_VPORT_ZMIN_13 +0x00028340 PA_SC_VPORT_ZMIN_14 +0x00028348 PA_SC_VPORT_ZMIN_15 +0x00028200 PA_SC_WINDOW_OFFSET +0x00028208 PA_SC_WINDOW_SCISSOR_BR +0x00028204 PA_SC_WINDOW_SCISSOR_TL +0x00028A08 PA_SU_LINE_CNTL +0x00028A04 PA_SU_POINT_MINMAX +0x00028A00 PA_SU_POINT_SIZE +0x00028E0C PA_SU_POLY_OFFSET_BACK_OFFSET +0x00028E08 PA_SU_POLY_OFFSET_BACK_SCALE +0x00028DFC PA_SU_POLY_OFFSET_CLAMP +0x00028DF8 PA_SU_POLY_OFFSET_DB_FMT_CNTL +0x00028E04 PA_SU_POLY_OFFSET_FRONT_OFFSET +0x00028E00 PA_SU_POLY_OFFSET_FRONT_SCALE +0x00028814 PA_SU_SC_MODE_CNTL +0x00028C08 PA_SU_VTX_CNTL +0x00008C04 SQ_GPR_RESOURCE_MGMT_1 +0x00008C08 SQ_GPR_RESOURCE_MGMT_2 +0x00008C10 SQ_STACK_RESOURCE_MGMT_1 +0x00008C14 SQ_STACK_RESOURCE_MGMT_2 +0x00008C0C SQ_THREAD_RESOURCE_MGMT +0x00028380 SQ_VTX_SEMANTIC_0 +0x00028384 SQ_VTX_SEMANTIC_1 +0x00028388 SQ_VTX_SEMANTIC_2 +0x0002838C SQ_VTX_SEMANTIC_3 +0x00028390 SQ_VTX_SEMANTIC_4 +0x00028394 SQ_VTX_SEMANTIC_5 +0x00028398 SQ_VTX_SEMANTIC_6 +0x0002839C SQ_VTX_SEMANTIC_7 +0x000283A0 SQ_VTX_SEMANTIC_8 +0x000283A4 SQ_VTX_SEMANTIC_9 +0x000283A8 SQ_VTX_SEMANTIC_10 +0x000283AC SQ_VTX_SEMANTIC_11 +0x000283B0 SQ_VTX_SEMANTIC_12 +0x000283B4 SQ_VTX_SEMANTIC_13 +0x000283B8 SQ_VTX_SEMANTIC_14 +0x000283BC SQ_VTX_SEMANTIC_15 +0x000283C0 SQ_VTX_SEMANTIC_16 +0x000283C4 SQ_VTX_SEMANTIC_17 +0x000283C8 SQ_VTX_SEMANTIC_18 +0x000283CC SQ_VTX_SEMANTIC_19 +0x000283D0 SQ_VTX_SEMANTIC_20 +0x000283D4 SQ_VTX_SEMANTIC_21 +0x000283D8 SQ_VTX_SEMANTIC_22 +0x000283DC SQ_VTX_SEMANTIC_23 +0x000283E0 SQ_VTX_SEMANTIC_24 +0x000283E4 SQ_VTX_SEMANTIC_25 +0x000283E8 SQ_VTX_SEMANTIC_26 +0x000283EC SQ_VTX_SEMANTIC_27 +0x000283F0 SQ_VTX_SEMANTIC_28 +0x000283F4 SQ_VTX_SEMANTIC_29 +0x000283F8 SQ_VTX_SEMANTIC_30 +0x000283FC SQ_VTX_SEMANTIC_31 +0x000288E0 SQ_VTX_SEMANTIC_CLEAR +0x0003CFF4 SQ_VTX_START_INST_LOC +0x000281C0 SQ_ALU_CONST_BUFFER_SIZE_GS_0 +0x000281C4 SQ_ALU_CONST_BUFFER_SIZE_GS_1 +0x000281C8 SQ_ALU_CONST_BUFFER_SIZE_GS_2 +0x000281CC SQ_ALU_CONST_BUFFER_SIZE_GS_3 +0x000281D0 SQ_ALU_CONST_BUFFER_SIZE_GS_4 +0x000281D4 SQ_ALU_CONST_BUFFER_SIZE_GS_5 +0x000281D8 SQ_ALU_CONST_BUFFER_SIZE_GS_6 +0x000281DC SQ_ALU_CONST_BUFFER_SIZE_GS_7 +0x000281E0 SQ_ALU_CONST_BUFFER_SIZE_GS_8 +0x000281E4 SQ_ALU_CONST_BUFFER_SIZE_GS_9 +0x000281E8 SQ_ALU_CONST_BUFFER_SIZE_GS_10 +0x000281EC SQ_ALU_CONST_BUFFER_SIZE_GS_11 +0x000281F0 SQ_ALU_CONST_BUFFER_SIZE_GS_12 +0x000281F4 SQ_ALU_CONST_BUFFER_SIZE_GS_13 +0x000281F8 SQ_ALU_CONST_BUFFER_SIZE_GS_14 +0x000281FC SQ_ALU_CONST_BUFFER_SIZE_GS_15 +0x00028140 SQ_ALU_CONST_BUFFER_SIZE_PS_0 +0x00028144 SQ_ALU_CONST_BUFFER_SIZE_PS_1 +0x00028148 SQ_ALU_CONST_BUFFER_SIZE_PS_2 +0x0002814C SQ_ALU_CONST_BUFFER_SIZE_PS_3 +0x00028150 SQ_ALU_CONST_BUFFER_SIZE_PS_4 +0x00028154 SQ_ALU_CONST_BUFFER_SIZE_PS_5 +0x00028158 SQ_ALU_CONST_BUFFER_SIZE_PS_6 +0x0002815C SQ_ALU_CONST_BUFFER_SIZE_PS_7 +0x00028160 SQ_ALU_CONST_BUFFER_SIZE_PS_8 +0x00028164 SQ_ALU_CONST_BUFFER_SIZE_PS_9 +0x00028168 SQ_ALU_CONST_BUFFER_SIZE_PS_10 +0x0002816C SQ_ALU_CONST_BUFFER_SIZE_PS_11 +0x00028170 SQ_ALU_CONST_BUFFER_SIZE_PS_12 +0x00028174 SQ_ALU_CONST_BUFFER_SIZE_PS_13 +0x00028178 SQ_ALU_CONST_BUFFER_SIZE_PS_14 +0x0002817C SQ_ALU_CONST_BUFFER_SIZE_PS_15 +0x00028180 SQ_ALU_CONST_BUFFER_SIZE_VS_0 +0x00028184 SQ_ALU_CONST_BUFFER_SIZE_VS_1 +0x00028188 SQ_ALU_CONST_BUFFER_SIZE_VS_2 +0x0002818C SQ_ALU_CONST_BUFFER_SIZE_VS_3 +0x00028190 SQ_ALU_CONST_BUFFER_SIZE_VS_4 +0x00028194 SQ_ALU_CONST_BUFFER_SIZE_VS_5 +0x00028198 SQ_ALU_CONST_BUFFER_SIZE_VS_6 +0x0002819C SQ_ALU_CONST_BUFFER_SIZE_VS_7 +0x000281A0 SQ_ALU_CONST_BUFFER_SIZE_VS_8 +0x000281A4 SQ_ALU_CONST_BUFFER_SIZE_VS_9 +0x000281A8 SQ_ALU_CONST_BUFFER_SIZE_VS_10 +0x000281AC SQ_ALU_CONST_BUFFER_SIZE_VS_11 +0x000281B0 SQ_ALU_CONST_BUFFER_SIZE_VS_12 +0x000281B4 SQ_ALU_CONST_BUFFER_SIZE_VS_13 +0x000281B8 SQ_ALU_CONST_BUFFER_SIZE_VS_14 +0x000281BC SQ_ALU_CONST_BUFFER_SIZE_VS_15 +0x000288D8 SQ_PGM_CF_OFFSET_ES +0x000288DC SQ_PGM_CF_OFFSET_FS +0x000288D4 SQ_PGM_CF_OFFSET_GS +0x000288CC SQ_PGM_CF_OFFSET_PS +0x000288D0 SQ_PGM_CF_OFFSET_VS +0x00028854 SQ_PGM_EXPORTS_PS +0x00028890 SQ_PGM_RESOURCES_ES +0x000288A4 SQ_PGM_RESOURCES_FS +0x0002887C SQ_PGM_RESOURCES_GS +0x00028850 SQ_PGM_RESOURCES_PS +0x00028868 SQ_PGM_RESOURCES_VS +0x00009100 SPI_CONFIG_CNTL +0x0000913C SPI_CONFIG_CNTL_1 +0x000286DC SPI_FOG_CNTL +0x000286E4 SPI_FOG_FUNC_BIAS +0x000286E0 SPI_FOG_FUNC_SCALE +0x000286D8 SPI_INPUT_Z +0x000286D4 SPI_INTERP_CONTROL_0 +0x00028644 SPI_PS_INPUT_CNTL_0 +0x00028648 SPI_PS_INPUT_CNTL_1 +0x0002864C SPI_PS_INPUT_CNTL_2 +0x00028650 SPI_PS_INPUT_CNTL_3 +0x00028654 SPI_PS_INPUT_CNTL_4 +0x00028658 SPI_PS_INPUT_CNTL_5 +0x0002865C SPI_PS_INPUT_CNTL_6 +0x00028660 SPI_PS_INPUT_CNTL_7 +0x00028664 SPI_PS_INPUT_CNTL_8 +0x00028668 SPI_PS_INPUT_CNTL_9 +0x0002866C SPI_PS_INPUT_CNTL_10 +0x00028670 SPI_PS_INPUT_CNTL_11 +0x00028674 SPI_PS_INPUT_CNTL_12 +0x00028678 SPI_PS_INPUT_CNTL_13 +0x0002867C SPI_PS_INPUT_CNTL_14 +0x00028680 SPI_PS_INPUT_CNTL_15 +0x00028684 SPI_PS_INPUT_CNTL_16 +0x00028688 SPI_PS_INPUT_CNTL_17 +0x0002868C SPI_PS_INPUT_CNTL_18 +0x00028690 SPI_PS_INPUT_CNTL_19 +0x00028694 SPI_PS_INPUT_CNTL_20 +0x00028698 SPI_PS_INPUT_CNTL_21 +0x0002869C SPI_PS_INPUT_CNTL_22 +0x000286A0 SPI_PS_INPUT_CNTL_23 +0x000286A4 SPI_PS_INPUT_CNTL_24 +0x000286A8 SPI_PS_INPUT_CNTL_25 +0x000286AC SPI_PS_INPUT_CNTL_26 +0x000286B0 SPI_PS_INPUT_CNTL_27 +0x000286B4 SPI_PS_INPUT_CNTL_28 +0x000286B8 SPI_PS_INPUT_CNTL_29 +0x000286BC SPI_PS_INPUT_CNTL_30 +0x000286C0 SPI_PS_INPUT_CNTL_31 +0x000286CC SPI_PS_IN_CONTROL_0 +0x000286D0 SPI_PS_IN_CONTROL_1 +0x000286C4 SPI_VS_OUT_CONFIG +0x00028614 SPI_VS_OUT_ID_0 +0x00028618 SPI_VS_OUT_ID_1 +0x0002861C SPI_VS_OUT_ID_2 +0x00028620 SPI_VS_OUT_ID_3 +0x00028624 SPI_VS_OUT_ID_4 +0x00028628 SPI_VS_OUT_ID_5 +0x0002862C SPI_VS_OUT_ID_6 +0x00028630 SPI_VS_OUT_ID_7 +0x00028634 SPI_VS_OUT_ID_8 +0x00028638 SPI_VS_OUT_ID_9 +0x00028438 SX_ALPHA_REF +0x00028410 SX_ALPHA_TEST_CONTROL +0x00028354 SX_SURFACE_SYNC +0x00009014 SX_MEMORY_EXPORT_SIZE +0x00009604 TC_INVALIDATE +0x00009400 TD_FILTER4 +0x00009404 TD_FILTER4_1 +0x00009408 TD_FILTER4_2 +0x0000940C TD_FILTER4_3 +0x00009410 TD_FILTER4_4 +0x00009414 TD_FILTER4_5 +0x00009418 TD_FILTER4_6 +0x0000941C TD_FILTER4_7 +0x00009420 TD_FILTER4_8 +0x00009424 TD_FILTER4_9 +0x00009428 TD_FILTER4_10 +0x0000942C TD_FILTER4_11 +0x00009430 TD_FILTER4_12 +0x00009434 TD_FILTER4_13 +0x00009438 TD_FILTER4_14 +0x0000943C TD_FILTER4_15 +0x00009440 TD_FILTER4_16 +0x00009444 TD_FILTER4_17 +0x00009448 TD_FILTER4_18 +0x0000944C TD_FILTER4_19 +0x00009450 TD_FILTER4_20 +0x00009454 TD_FILTER4_21 +0x00009458 TD_FILTER4_22 +0x0000945C TD_FILTER4_23 +0x00009460 TD_FILTER4_24 +0x00009464 TD_FILTER4_25 +0x00009468 TD_FILTER4_26 +0x0000946C TD_FILTER4_27 +0x00009470 TD_FILTER4_28 +0x00009474 TD_FILTER4_29 +0x00009478 TD_FILTER4_30 +0x0000947C TD_FILTER4_31 +0x00009480 TD_FILTER4_32 +0x00009484 TD_FILTER4_33 +0x00009488 TD_FILTER4_34 +0x0000948C TD_FILTER4_35 +0x0000A80C TD_GS_SAMPLER0_BORDER_ALPHA +0x0000A81C TD_GS_SAMPLER1_BORDER_ALPHA +0x0000A82C TD_GS_SAMPLER2_BORDER_ALPHA +0x0000A83C TD_GS_SAMPLER3_BORDER_ALPHA +0x0000A84C TD_GS_SAMPLER4_BORDER_ALPHA +0x0000A85C TD_GS_SAMPLER5_BORDER_ALPHA +0x0000A86C TD_GS_SAMPLER6_BORDER_ALPHA +0x0000A87C TD_GS_SAMPLER7_BORDER_ALPHA +0x0000A88C TD_GS_SAMPLER8_BORDER_ALPHA +0x0000A89C TD_GS_SAMPLER9_BORDER_ALPHA +0x0000A8AC TD_GS_SAMPLER10_BORDER_ALPHA +0x0000A8BC TD_GS_SAMPLER11_BORDER_ALPHA +0x0000A8CC TD_GS_SAMPLER12_BORDER_ALPHA +0x0000A8DC TD_GS_SAMPLER13_BORDER_ALPHA +0x0000A8EC TD_GS_SAMPLER14_BORDER_ALPHA +0x0000A8FC TD_GS_SAMPLER15_BORDER_ALPHA +0x0000A90C TD_GS_SAMPLER16_BORDER_ALPHA +0x0000A91C TD_GS_SAMPLER17_BORDER_ALPHA +0x0000A808 TD_GS_SAMPLER0_BORDER_BLUE +0x0000A818 TD_GS_SAMPLER1_BORDER_BLUE +0x0000A828 TD_GS_SAMPLER2_BORDER_BLUE +0x0000A838 TD_GS_SAMPLER3_BORDER_BLUE +0x0000A848 TD_GS_SAMPLER4_BORDER_BLUE +0x0000A858 TD_GS_SAMPLER5_BORDER_BLUE +0x0000A868 TD_GS_SAMPLER6_BORDER_BLUE +0x0000A878 TD_GS_SAMPLER7_BORDER_BLUE +0x0000A888 TD_GS_SAMPLER8_BORDER_BLUE +0x0000A898 TD_GS_SAMPLER9_BORDER_BLUE +0x0000A8A8 TD_GS_SAMPLER10_BORDER_BLUE +0x0000A8B8 TD_GS_SAMPLER11_BORDER_BLUE +0x0000A8C8 TD_GS_SAMPLER12_BORDER_BLUE +0x0000A8D8 TD_GS_SAMPLER13_BORDER_BLUE +0x0000A8E8 TD_GS_SAMPLER14_BORDER_BLUE +0x0000A8F8 TD_GS_SAMPLER15_BORDER_BLUE +0x0000A908 TD_GS_SAMPLER16_BORDER_BLUE +0x0000A918 TD_GS_SAMPLER17_BORDER_BLUE +0x0000A804 TD_GS_SAMPLER0_BORDER_GREEN +0x0000A814 TD_GS_SAMPLER1_BORDER_GREEN +0x0000A824 TD_GS_SAMPLER2_BORDER_GREEN +0x0000A834 TD_GS_SAMPLER3_BORDER_GREEN +0x0000A844 TD_GS_SAMPLER4_BORDER_GREEN +0x0000A854 TD_GS_SAMPLER5_BORDER_GREEN +0x0000A864 TD_GS_SAMPLER6_BORDER_GREEN +0x0000A874 TD_GS_SAMPLER7_BORDER_GREEN +0x0000A884 TD_GS_SAMPLER8_BORDER_GREEN +0x0000A894 TD_GS_SAMPLER9_BORDER_GREEN +0x0000A8A4 TD_GS_SAMPLER10_BORDER_GREEN +0x0000A8B4 TD_GS_SAMPLER11_BORDER_GREEN +0x0000A8C4 TD_GS_SAMPLER12_BORDER_GREEN +0x0000A8D4 TD_GS_SAMPLER13_BORDER_GREEN +0x0000A8E4 TD_GS_SAMPLER14_BORDER_GREEN +0x0000A8F4 TD_GS_SAMPLER15_BORDER_GREEN +0x0000A904 TD_GS_SAMPLER16_BORDER_GREEN +0x0000A914 TD_GS_SAMPLER17_BORDER_GREEN +0x0000A800 TD_GS_SAMPLER0_BORDER_RED +0x0000A810 TD_GS_SAMPLER1_BORDER_RED +0x0000A820 TD_GS_SAMPLER2_BORDER_RED +0x0000A830 TD_GS_SAMPLER3_BORDER_RED +0x0000A840 TD_GS_SAMPLER4_BORDER_RED +0x0000A850 TD_GS_SAMPLER5_BORDER_RED +0x0000A860 TD_GS_SAMPLER6_BORDER_RED +0x0000A870 TD_GS_SAMPLER7_BORDER_RED +0x0000A880 TD_GS_SAMPLER8_BORDER_RED +0x0000A890 TD_GS_SAMPLER9_BORDER_RED +0x0000A8A0 TD_GS_SAMPLER10_BORDER_RED +0x0000A8B0 TD_GS_SAMPLER11_BORDER_RED +0x0000A8C0 TD_GS_SAMPLER12_BORDER_RED +0x0000A8D0 TD_GS_SAMPLER13_BORDER_RED +0x0000A8E0 TD_GS_SAMPLER14_BORDER_RED +0x0000A8F0 TD_GS_SAMPLER15_BORDER_RED +0x0000A900 TD_GS_SAMPLER16_BORDER_RED +0x0000A910 TD_GS_SAMPLER17_BORDER_RED +0x0000A40C TD_PS_SAMPLER0_BORDER_ALPHA +0x0000A41C TD_PS_SAMPLER1_BORDER_ALPHA +0x0000A42C TD_PS_SAMPLER2_BORDER_ALPHA +0x0000A43C TD_PS_SAMPLER3_BORDER_ALPHA +0x0000A44C TD_PS_SAMPLER4_BORDER_ALPHA +0x0000A45C TD_PS_SAMPLER5_BORDER_ALPHA +0x0000A46C TD_PS_SAMPLER6_BORDER_ALPHA +0x0000A47C TD_PS_SAMPLER7_BORDER_ALPHA +0x0000A48C TD_PS_SAMPLER8_BORDER_ALPHA +0x0000A49C TD_PS_SAMPLER9_BORDER_ALPHA +0x0000A4AC TD_PS_SAMPLER10_BORDER_ALPHA +0x0000A4BC TD_PS_SAMPLER11_BORDER_ALPHA +0x0000A4CC TD_PS_SAMPLER12_BORDER_ALPHA +0x0000A4DC TD_PS_SAMPLER13_BORDER_ALPHA +0x0000A4EC TD_PS_SAMPLER14_BORDER_ALPHA +0x0000A4FC TD_PS_SAMPLER15_BORDER_ALPHA +0x0000A50C TD_PS_SAMPLER16_BORDER_ALPHA +0x0000A51C TD_PS_SAMPLER17_BORDER_ALPHA +0x0000A408 TD_PS_SAMPLER0_BORDER_BLUE +0x0000A418 TD_PS_SAMPLER1_BORDER_BLUE +0x0000A428 TD_PS_SAMPLER2_BORDER_BLUE +0x0000A438 TD_PS_SAMPLER3_BORDER_BLUE +0x0000A448 TD_PS_SAMPLER4_BORDER_BLUE +0x0000A458 TD_PS_SAMPLER5_BORDER_BLUE +0x0000A468 TD_PS_SAMPLER6_BORDER_BLUE +0x0000A478 TD_PS_SAMPLER7_BORDER_BLUE +0x0000A488 TD_PS_SAMPLER8_BORDER_BLUE +0x0000A498 TD_PS_SAMPLER9_BORDER_BLUE +0x0000A4A8 TD_PS_SAMPLER10_BORDER_BLUE +0x0000A4B8 TD_PS_SAMPLER11_BORDER_BLUE +0x0000A4C8 TD_PS_SAMPLER12_BORDER_BLUE +0x0000A4D8 TD_PS_SAMPLER13_BORDER_BLUE +0x0000A4E8 TD_PS_SAMPLER14_BORDER_BLUE +0x0000A4F8 TD_PS_SAMPLER15_BORDER_BLUE +0x0000A508 TD_PS_SAMPLER16_BORDER_BLUE +0x0000A518 TD_PS_SAMPLER17_BORDER_BLUE +0x0000A404 TD_PS_SAMPLER0_BORDER_GREEN +0x0000A414 TD_PS_SAMPLER1_BORDER_GREEN +0x0000A424 TD_PS_SAMPLER2_BORDER_GREEN +0x0000A434 TD_PS_SAMPLER3_BORDER_GREEN +0x0000A444 TD_PS_SAMPLER4_BORDER_GREEN +0x0000A454 TD_PS_SAMPLER5_BORDER_GREEN +0x0000A464 TD_PS_SAMPLER6_BORDER_GREEN +0x0000A474 TD_PS_SAMPLER7_BORDER_GREEN +0x0000A484 TD_PS_SAMPLER8_BORDER_GREEN +0x0000A494 TD_PS_SAMPLER9_BORDER_GREEN +0x0000A4A4 TD_PS_SAMPLER10_BORDER_GREEN +0x0000A4B4 TD_PS_SAMPLER11_BORDER_GREEN +0x0000A4C4 TD_PS_SAMPLER12_BORDER_GREEN +0x0000A4D4 TD_PS_SAMPLER13_BORDER_GREEN +0x0000A4E4 TD_PS_SAMPLER14_BORDER_GREEN +0x0000A4F4 TD_PS_SAMPLER15_BORDER_GREEN +0x0000A504 TD_PS_SAMPLER16_BORDER_GREEN +0x0000A514 TD_PS_SAMPLER17_BORDER_GREEN +0x0000A400 TD_PS_SAMPLER0_BORDER_RED +0x0000A410 TD_PS_SAMPLER1_BORDER_RED +0x0000A420 TD_PS_SAMPLER2_BORDER_RED +0x0000A430 TD_PS_SAMPLER3_BORDER_RED +0x0000A440 TD_PS_SAMPLER4_BORDER_RED +0x0000A450 TD_PS_SAMPLER5_BORDER_RED +0x0000A460 TD_PS_SAMPLER6_BORDER_RED +0x0000A470 TD_PS_SAMPLER7_BORDER_RED +0x0000A480 TD_PS_SAMPLER8_BORDER_RED +0x0000A490 TD_PS_SAMPLER9_BORDER_RED +0x0000A4A0 TD_PS_SAMPLER10_BORDER_RED +0x0000A4B0 TD_PS_SAMPLER11_BORDER_RED +0x0000A4C0 TD_PS_SAMPLER12_BORDER_RED +0x0000A4D0 TD_PS_SAMPLER13_BORDER_RED +0x0000A4E0 TD_PS_SAMPLER14_BORDER_RED +0x0000A4F0 TD_PS_SAMPLER15_BORDER_RED +0x0000A500 TD_PS_SAMPLER16_BORDER_RED +0x0000A510 TD_PS_SAMPLER17_BORDER_RED +0x0000AA00 TD_PS_SAMPLER0_CLEARTYPE_KERNEL +0x0000AA04 TD_PS_SAMPLER1_CLEARTYPE_KERNEL +0x0000AA08 TD_PS_SAMPLER2_CLEARTYPE_KERNEL +0x0000AA0C TD_PS_SAMPLER3_CLEARTYPE_KERNEL +0x0000AA10 TD_PS_SAMPLER4_CLEARTYPE_KERNEL +0x0000AA14 TD_PS_SAMPLER5_CLEARTYPE_KERNEL +0x0000AA18 TD_PS_SAMPLER6_CLEARTYPE_KERNEL +0x0000AA1C TD_PS_SAMPLER7_CLEARTYPE_KERNEL +0x0000AA20 TD_PS_SAMPLER8_CLEARTYPE_KERNEL +0x0000AA24 TD_PS_SAMPLER9_CLEARTYPE_KERNEL +0x0000AA28 TD_PS_SAMPLER10_CLEARTYPE_KERNEL +0x0000AA2C TD_PS_SAMPLER11_CLEARTYPE_KERNEL +0x0000AA30 TD_PS_SAMPLER12_CLEARTYPE_KERNEL +0x0000AA34 TD_PS_SAMPLER13_CLEARTYPE_KERNEL +0x0000AA38 TD_PS_SAMPLER14_CLEARTYPE_KERNEL +0x0000AA3C TD_PS_SAMPLER15_CLEARTYPE_KERNEL +0x0000AA40 TD_PS_SAMPLER16_CLEARTYPE_KERNEL +0x0000AA44 TD_PS_SAMPLER17_CLEARTYPE_KERNEL +0x0000A60C TD_VS_SAMPLER0_BORDER_ALPHA +0x0000A61C TD_VS_SAMPLER1_BORDER_ALPHA +0x0000A62C TD_VS_SAMPLER2_BORDER_ALPHA +0x0000A63C TD_VS_SAMPLER3_BORDER_ALPHA +0x0000A64C TD_VS_SAMPLER4_BORDER_ALPHA +0x0000A65C TD_VS_SAMPLER5_BORDER_ALPHA +0x0000A66C TD_VS_SAMPLER6_BORDER_ALPHA +0x0000A67C TD_VS_SAMPLER7_BORDER_ALPHA +0x0000A68C TD_VS_SAMPLER8_BORDER_ALPHA +0x0000A69C TD_VS_SAMPLER9_BORDER_ALPHA +0x0000A6AC TD_VS_SAMPLER10_BORDER_ALPHA +0x0000A6BC TD_VS_SAMPLER11_BORDER_ALPHA +0x0000A6CC TD_VS_SAMPLER12_BORDER_ALPHA +0x0000A6DC TD_VS_SAMPLER13_BORDER_ALPHA +0x0000A6EC TD_VS_SAMPLER14_BORDER_ALPHA +0x0000A6FC TD_VS_SAMPLER15_BORDER_ALPHA +0x0000A70C TD_VS_SAMPLER16_BORDER_ALPHA +0x0000A71C TD_VS_SAMPLER17_BORDER_ALPHA +0x0000A608 TD_VS_SAMPLER0_BORDER_BLUE +0x0000A618 TD_VS_SAMPLER1_BORDER_BLUE +0x0000A628 TD_VS_SAMPLER2_BORDER_BLUE +0x0000A638 TD_VS_SAMPLER3_BORDER_BLUE +0x0000A648 TD_VS_SAMPLER4_BORDER_BLUE +0x0000A658 TD_VS_SAMPLER5_BORDER_BLUE +0x0000A668 TD_VS_SAMPLER6_BORDER_BLUE +0x0000A678 TD_VS_SAMPLER7_BORDER_BLUE +0x0000A688 TD_VS_SAMPLER8_BORDER_BLUE +0x0000A698 TD_VS_SAMPLER9_BORDER_BLUE +0x0000A6A8 TD_VS_SAMPLER10_BORDER_BLUE +0x0000A6B8 TD_VS_SAMPLER11_BORDER_BLUE +0x0000A6C8 TD_VS_SAMPLER12_BORDER_BLUE +0x0000A6D8 TD_VS_SAMPLER13_BORDER_BLUE +0x0000A6E8 TD_VS_SAMPLER14_BORDER_BLUE +0x0000A6F8 TD_VS_SAMPLER15_BORDER_BLUE +0x0000A708 TD_VS_SAMPLER16_BORDER_BLUE +0x0000A718 TD_VS_SAMPLER17_BORDER_BLUE +0x0000A604 TD_VS_SAMPLER0_BORDER_GREEN +0x0000A614 TD_VS_SAMPLER1_BORDER_GREEN +0x0000A624 TD_VS_SAMPLER2_BORDER_GREEN +0x0000A634 TD_VS_SAMPLER3_BORDER_GREEN +0x0000A644 TD_VS_SAMPLER4_BORDER_GREEN +0x0000A654 TD_VS_SAMPLER5_BORDER_GREEN +0x0000A664 TD_VS_SAMPLER6_BORDER_GREEN +0x0000A674 TD_VS_SAMPLER7_BORDER_GREEN +0x0000A684 TD_VS_SAMPLER8_BORDER_GREEN +0x0000A694 TD_VS_SAMPLER9_BORDER_GREEN +0x0000A6A4 TD_VS_SAMPLER10_BORDER_GREEN +0x0000A6B4 TD_VS_SAMPLER11_BORDER_GREEN +0x0000A6C4 TD_VS_SAMPLER12_BORDER_GREEN +0x0000A6D4 TD_VS_SAMPLER13_BORDER_GREEN +0x0000A6E4 TD_VS_SAMPLER14_BORDER_GREEN +0x0000A6F4 TD_VS_SAMPLER15_BORDER_GREEN +0x0000A704 TD_VS_SAMPLER16_BORDER_GREEN +0x0000A714 TD_VS_SAMPLER17_BORDER_GREEN +0x0000A600 TD_VS_SAMPLER0_BORDER_RED +0x0000A610 TD_VS_SAMPLER1_BORDER_RED +0x0000A620 TD_VS_SAMPLER2_BORDER_RED +0x0000A630 TD_VS_SAMPLER3_BORDER_RED +0x0000A640 TD_VS_SAMPLER4_BORDER_RED +0x0000A650 TD_VS_SAMPLER5_BORDER_RED +0x0000A660 TD_VS_SAMPLER6_BORDER_RED +0x0000A670 TD_VS_SAMPLER7_BORDER_RED +0x0000A680 TD_VS_SAMPLER8_BORDER_RED +0x0000A690 TD_VS_SAMPLER9_BORDER_RED +0x0000A6A0 TD_VS_SAMPLER10_BORDER_RED +0x0000A6B0 TD_VS_SAMPLER11_BORDER_RED +0x0000A6C0 TD_VS_SAMPLER12_BORDER_RED +0x0000A6D0 TD_VS_SAMPLER13_BORDER_RED +0x0000A6E0 TD_VS_SAMPLER14_BORDER_RED +0x0000A6F0 TD_VS_SAMPLER15_BORDER_RED +0x0000A700 TD_VS_SAMPLER16_BORDER_RED +0x0000A710 TD_VS_SAMPLER17_BORDER_RED +0x00009508 TA_CNTL_AUX +0x0002802C DB_DEPTH_CLEAR +0x00028D34 DB_PREFETCH_LIMIT +0x00028D30 DB_PRELOAD_CONTROL +0x00028D0C DB_RENDER_CONTROL +0x00028D10 DB_RENDER_OVERRIDE +0x0002880C DB_SHADER_CONTROL +0x00028D28 DB_SRESULTS_COMPARE_STATE0 +0x00028D2C DB_SRESULTS_COMPARE_STATE1 +0x00028430 DB_STENCILREFMASK +0x00028434 DB_STENCILREFMASK_BF +0x00028028 DB_STENCIL_CLEAR +0x00028780 CB_BLEND0_CONTROL +0x00028784 CB_BLEND1_CONTROL +0x00028788 CB_BLEND2_CONTROL +0x0002878C CB_BLEND3_CONTROL +0x00028790 CB_BLEND4_CONTROL +0x00028794 CB_BLEND5_CONTROL +0x00028798 CB_BLEND6_CONTROL +0x0002879C CB_BLEND7_CONTROL +0x00028804 CB_BLEND_CONTROL +0x00028420 CB_BLEND_ALPHA +0x0002841C CB_BLEND_BLUE +0x00028418 CB_BLEND_GREEN +0x00028414 CB_BLEND_RED +0x0002812C CB_CLEAR_ALPHA +0x00028128 CB_CLEAR_BLUE +0x00028124 CB_CLEAR_GREEN +0x00028120 CB_CLEAR_RED +0x00028C30 CB_CLRCMP_CONTROL +0x00028C38 CB_CLRCMP_DST +0x00028C3C CB_CLRCMP_MSK +0x00028C34 CB_CLRCMP_SRC +0x0002842C CB_FOG_BLUE +0x00028428 CB_FOG_GREEN +0x00028424 CB_FOG_RED +0x00008040 WAIT_UNTIL +0x00009714 VC_ENHANCE +0x00009830 DB_DEBUG +0x00009838 DB_WATERMARKS +0x00028D44 DB_ALPHA_TO_MASK +0x00009700 VC_CNTL diff --git a/sys/dev/drm2/radeon/reg_srcs/rn50 b/sys/dev/drm2/radeon/reg_srcs/rn50 new file mode 100644 index 00000000000..2687b630726 --- /dev/null +++ b/sys/dev/drm2/radeon/reg_srcs/rn50 @@ -0,0 +1,30 @@ +rn50 0x3294 +0x1434 SRC_Y_X +0x1438 DST_Y_X +0x143C DST_HEIGHT_WIDTH +0x146C DP_GUI_MASTER_CNTL +0x1474 BRUSH_Y_X +0x1478 DP_BRUSH_BKGD_CLR +0x147C DP_BRUSH_FRGD_CLR +0x1480 BRUSH_DATA0 +0x1484 BRUSH_DATA1 +0x1598 DST_WIDTH_HEIGHT +0x15C0 CLR_CMP_CNTL +0x15C4 CLR_CMP_CLR_SRC +0x15C8 CLR_CMP_CLR_DST +0x15CC CLR_CMP_MSK +0x15D8 DP_SRC_FRGD_CLR +0x15DC DP_SRC_BKGD_CLR +0x1600 DST_LINE_START +0x1604 DST_LINE_END +0x1608 DST_LINE_PATCOUNT +0x16C0 DP_CNTL +0x16CC DP_WRITE_MSK +0x16D0 DP_CNTL_XDIR_YDIR_YMAJOR +0x16E8 DEFAULT_SC_BOTTOM_RIGHT +0x16EC SC_TOP_LEFT +0x16F0 SC_BOTTOM_RIGHT +0x16F4 SRC_SC_BOTTOM_RIGHT +0x1714 DSTCACHE_CTLSTAT +0x1720 WAIT_UNTIL +0x172C RBBM_GUICNTL diff --git a/sys/dev/drm2/radeon/reg_srcs/rs600 b/sys/dev/drm2/radeon/reg_srcs/rs600 new file mode 100644 index 00000000000..d9f62866bbc --- /dev/null +++ b/sys/dev/drm2/radeon/reg_srcs/rs600 @@ -0,0 +1,780 @@ +rs600 0x6d40 +0x1434 SRC_Y_X +0x1438 DST_Y_X +0x143C DST_HEIGHT_WIDTH +0x146C DP_GUI_MASTER_CNTL +0x1474 BRUSH_Y_X +0x1478 DP_BRUSH_BKGD_CLR +0x147C DP_BRUSH_FRGD_CLR +0x1480 BRUSH_DATA0 +0x1484 BRUSH_DATA1 +0x1598 DST_WIDTH_HEIGHT +0x15C0 CLR_CMP_CNTL +0x15C4 CLR_CMP_CLR_SRC +0x15C8 CLR_CMP_CLR_DST +0x15CC CLR_CMP_MSK +0x15D8 DP_SRC_FRGD_CLR +0x15DC DP_SRC_BKGD_CLR +0x1600 DST_LINE_START +0x1604 DST_LINE_END +0x1608 DST_LINE_PATCOUNT +0x16C0 DP_CNTL +0x16CC DP_WRITE_MSK +0x16D0 DP_CNTL_XDIR_YDIR_YMAJOR +0x16E8 DEFAULT_SC_BOTTOM_RIGHT +0x16EC SC_TOP_LEFT +0x16F0 SC_BOTTOM_RIGHT +0x16F4 SRC_SC_BOTTOM_RIGHT +0x1714 DSTCACHE_CTLSTAT +0x1720 WAIT_UNTIL +0x172C RBBM_GUICNTL +0x1D98 VAP_VPORT_XSCALE +0x1D9C VAP_VPORT_XOFFSET +0x1DA0 VAP_VPORT_YSCALE +0x1DA4 VAP_VPORT_YOFFSET +0x1DA8 VAP_VPORT_ZSCALE +0x1DAC VAP_VPORT_ZOFFSET +0x2080 VAP_CNTL +0x2090 VAP_OUT_VTX_FMT_0 +0x2094 VAP_OUT_VTX_FMT_1 +0x20B0 VAP_VTE_CNTL +0x2138 VAP_VF_MIN_VTX_INDX +0x2140 VAP_CNTL_STATUS +0x2150 VAP_PROG_STREAM_CNTL_0 +0x2154 VAP_PROG_STREAM_CNTL_1 +0x2158 VAP_PROG_STREAM_CNTL_2 +0x215C VAP_PROG_STREAM_CNTL_3 +0x2160 VAP_PROG_STREAM_CNTL_4 +0x2164 VAP_PROG_STREAM_CNTL_5 +0x2168 VAP_PROG_STREAM_CNTL_6 +0x216C VAP_PROG_STREAM_CNTL_7 +0x2180 VAP_VTX_STATE_CNTL +0x2184 VAP_VSM_VTX_ASSM +0x2188 VAP_VTX_STATE_IND_REG_0 +0x218C VAP_VTX_STATE_IND_REG_1 +0x2190 VAP_VTX_STATE_IND_REG_2 +0x2194 VAP_VTX_STATE_IND_REG_3 +0x2198 VAP_VTX_STATE_IND_REG_4 +0x219C VAP_VTX_STATE_IND_REG_5 +0x21A0 VAP_VTX_STATE_IND_REG_6 +0x21A4 VAP_VTX_STATE_IND_REG_7 +0x21A8 VAP_VTX_STATE_IND_REG_8 +0x21AC VAP_VTX_STATE_IND_REG_9 +0x21B0 VAP_VTX_STATE_IND_REG_10 +0x21B4 VAP_VTX_STATE_IND_REG_11 +0x21B8 VAP_VTX_STATE_IND_REG_12 +0x21BC VAP_VTX_STATE_IND_REG_13 +0x21C0 VAP_VTX_STATE_IND_REG_14 +0x21C4 VAP_VTX_STATE_IND_REG_15 +0x21DC VAP_PSC_SGN_NORM_CNTL +0x21E0 VAP_PROG_STREAM_CNTL_EXT_0 +0x21E4 VAP_PROG_STREAM_CNTL_EXT_1 +0x21E8 VAP_PROG_STREAM_CNTL_EXT_2 +0x21EC VAP_PROG_STREAM_CNTL_EXT_3 +0x21F0 VAP_PROG_STREAM_CNTL_EXT_4 +0x21F4 VAP_PROG_STREAM_CNTL_EXT_5 +0x21F8 VAP_PROG_STREAM_CNTL_EXT_6 +0x21FC VAP_PROG_STREAM_CNTL_EXT_7 +0x2200 VAP_PVS_VECTOR_INDX_REG +0x2204 VAP_PVS_VECTOR_DATA_REG +0x2208 VAP_PVS_VECTOR_DATA_REG_128 +0x221C VAP_CLIP_CNTL +0x2220 VAP_GB_VERT_CLIP_ADJ +0x2224 VAP_GB_VERT_DISC_ADJ +0x2228 VAP_GB_HORZ_CLIP_ADJ +0x222C VAP_GB_HORZ_DISC_ADJ +0x2230 VAP_PVS_FLOW_CNTL_ADDRS_0 +0x2234 VAP_PVS_FLOW_CNTL_ADDRS_1 +0x2238 VAP_PVS_FLOW_CNTL_ADDRS_2 +0x223C VAP_PVS_FLOW_CNTL_ADDRS_3 +0x2240 VAP_PVS_FLOW_CNTL_ADDRS_4 +0x2244 VAP_PVS_FLOW_CNTL_ADDRS_5 +0x2248 VAP_PVS_FLOW_CNTL_ADDRS_6 +0x224C VAP_PVS_FLOW_CNTL_ADDRS_7 +0x2250 VAP_PVS_FLOW_CNTL_ADDRS_8 +0x2254 VAP_PVS_FLOW_CNTL_ADDRS_9 +0x2258 VAP_PVS_FLOW_CNTL_ADDRS_10 +0x225C VAP_PVS_FLOW_CNTL_ADDRS_11 +0x2260 VAP_PVS_FLOW_CNTL_ADDRS_12 +0x2264 VAP_PVS_FLOW_CNTL_ADDRS_13 +0x2268 VAP_PVS_FLOW_CNTL_ADDRS_14 +0x226C VAP_PVS_FLOW_CNTL_ADDRS_15 +0x2284 VAP_PVS_STATE_FLUSH_REG +0x2288 VAP_PVS_VTX_TIMEOUT_REG +0x2290 VAP_PVS_FLOW_CNTL_LOOP_INDEX_0 +0x2294 VAP_PVS_FLOW_CNTL_LOOP_INDEX_1 +0x2298 VAP_PVS_FLOW_CNTL_LOOP_INDEX_2 +0x229C VAP_PVS_FLOW_CNTL_LOOP_INDEX_3 +0x22A0 VAP_PVS_FLOW_CNTL_LOOP_INDEX_4 +0x22A4 VAP_PVS_FLOW_CNTL_LOOP_INDEX_5 +0x22A8 VAP_PVS_FLOW_CNTL_LOOP_INDEX_6 +0x22AC VAP_PVS_FLOW_CNTL_LOOP_INDEX_7 +0x22B0 VAP_PVS_FLOW_CNTL_LOOP_INDEX_8 +0x22B4 VAP_PVS_FLOW_CNTL_LOOP_INDEX_9 +0x22B8 VAP_PVS_FLOW_CNTL_LOOP_INDEX_10 +0x22BC VAP_PVS_FLOW_CNTL_LOOP_INDEX_11 +0x22C0 VAP_PVS_FLOW_CNTL_LOOP_INDEX_12 +0x22C4 VAP_PVS_FLOW_CNTL_LOOP_INDEX_13 +0x22C8 VAP_PVS_FLOW_CNTL_LOOP_INDEX_14 +0x22CC VAP_PVS_FLOW_CNTL_LOOP_INDEX_15 +0x22D0 VAP_PVS_CODE_CNTL_0 +0x22D4 VAP_PVS_CONST_CNTL +0x22D8 VAP_PVS_CODE_CNTL_1 +0x22DC VAP_PVS_FLOW_CNTL_OPC +0x342C RB2D_DSTCACHE_CTLSTAT +0x4000 GB_VAP_RASTER_VTX_FMT_0 +0x4004 GB_VAP_RASTER_VTX_FMT_1 +0x4008 GB_ENABLE +0x4010 GB_MSPOS0 +0x4014 GB_MSPOS1 +0x401C GB_SELECT +0x4020 GB_AA_CONFIG +0x4024 GB_FIFO_SIZE +0x4100 TX_INVALTAGS +0x4200 GA_POINT_S0 +0x4204 GA_POINT_T0 +0x4208 GA_POINT_S1 +0x420C GA_POINT_T1 +0x4214 GA_TRIANGLE_STIPPLE +0x421C GA_POINT_SIZE +0x4230 GA_POINT_MINMAX +0x4234 GA_LINE_CNTL +0x4238 GA_LINE_STIPPLE_CONFIG +0x4260 GA_LINE_STIPPLE_VALUE +0x4264 GA_LINE_S0 +0x4268 GA_LINE_S1 +0x4278 GA_COLOR_CONTROL +0x427C GA_SOLID_RG +0x4280 GA_SOLID_BA +0x4288 GA_POLY_MODE +0x428C GA_ROUND_MODE +0x4290 GA_OFFSET +0x4294 GA_FOG_SCALE +0x4298 GA_FOG_OFFSET +0x42A0 SU_TEX_WRAP +0x42A4 SU_POLY_OFFSET_FRONT_SCALE +0x42A8 SU_POLY_OFFSET_FRONT_OFFSET +0x42AC SU_POLY_OFFSET_BACK_SCALE +0x42B0 SU_POLY_OFFSET_BACK_OFFSET +0x42B4 SU_POLY_OFFSET_ENABLE +0x42B8 SU_CULL_MODE +0x42C0 SU_DEPTH_SCALE +0x42C4 SU_DEPTH_OFFSET +0x42C8 SU_REG_DEST +0x4300 RS_COUNT +0x4304 RS_INST_COUNT +0x4310 RS_IP_0 +0x4314 RS_IP_1 +0x4318 RS_IP_2 +0x431C RS_IP_3 +0x4320 RS_IP_4 +0x4324 RS_IP_5 +0x4328 RS_IP_6 +0x432C RS_IP_7 +0x4330 RS_INST_0 +0x4334 RS_INST_1 +0x4338 RS_INST_2 +0x433C RS_INST_3 +0x4340 RS_INST_4 +0x4344 RS_INST_5 +0x4348 RS_INST_6 +0x434C RS_INST_7 +0x4350 RS_INST_8 +0x4354 RS_INST_9 +0x4358 RS_INST_10 +0x435C RS_INST_11 +0x4360 RS_INST_12 +0x4364 RS_INST_13 +0x4368 RS_INST_14 +0x436C RS_INST_15 +0x43A8 SC_EDGERULE +0x43B0 SC_CLIP_0_A +0x43B4 SC_CLIP_0_B +0x43B8 SC_CLIP_1_A +0x43BC SC_CLIP_1_B +0x43C0 SC_CLIP_2_A +0x43C4 SC_CLIP_2_B +0x43C8 SC_CLIP_3_A +0x43CC SC_CLIP_3_B +0x43D0 SC_CLIP_RULE +0x43E0 SC_SCISSOR0 +0x43E8 SC_SCREENDOOR +0x4440 TX_FILTER1_0 +0x4444 TX_FILTER1_1 +0x4448 TX_FILTER1_2 +0x444C TX_FILTER1_3 +0x4450 TX_FILTER1_4 +0x4454 TX_FILTER1_5 +0x4458 TX_FILTER1_6 +0x445C TX_FILTER1_7 +0x4460 TX_FILTER1_8 +0x4464 TX_FILTER1_9 +0x4468 TX_FILTER1_10 +0x446C TX_FILTER1_11 +0x4470 TX_FILTER1_12 +0x4474 TX_FILTER1_13 +0x4478 TX_FILTER1_14 +0x447C TX_FILTER1_15 +0x4580 TX_CHROMA_KEY_0 +0x4584 TX_CHROMA_KEY_1 +0x4588 TX_CHROMA_KEY_2 +0x458C TX_CHROMA_KEY_3 +0x4590 TX_CHROMA_KEY_4 +0x4594 TX_CHROMA_KEY_5 +0x4598 TX_CHROMA_KEY_6 +0x459C TX_CHROMA_KEY_7 +0x45A0 TX_CHROMA_KEY_8 +0x45A4 TX_CHROMA_KEY_9 +0x45A8 TX_CHROMA_KEY_10 +0x45AC TX_CHROMA_KEY_11 +0x45B0 TX_CHROMA_KEY_12 +0x45B4 TX_CHROMA_KEY_13 +0x45B8 TX_CHROMA_KEY_14 +0x45BC TX_CHROMA_KEY_15 +0x45C0 TX_BORDER_COLOR_0 +0x45C4 TX_BORDER_COLOR_1 +0x45C8 TX_BORDER_COLOR_2 +0x45CC TX_BORDER_COLOR_3 +0x45D0 TX_BORDER_COLOR_4 +0x45D4 TX_BORDER_COLOR_5 +0x45D8 TX_BORDER_COLOR_6 +0x45DC TX_BORDER_COLOR_7 +0x45E0 TX_BORDER_COLOR_8 +0x45E4 TX_BORDER_COLOR_9 +0x45E8 TX_BORDER_COLOR_10 +0x45EC TX_BORDER_COLOR_11 +0x45F0 TX_BORDER_COLOR_12 +0x45F4 TX_BORDER_COLOR_13 +0x45F8 TX_BORDER_COLOR_14 +0x45FC TX_BORDER_COLOR_15 +0x4600 US_CONFIG +0x4604 US_PIXSIZE +0x4608 US_CODE_OFFSET +0x460C US_RESET +0x4610 US_CODE_ADDR_0 +0x4614 US_CODE_ADDR_1 +0x4618 US_CODE_ADDR_2 +0x461C US_CODE_ADDR_3 +0x4620 US_TEX_INST_0 +0x4624 US_TEX_INST_1 +0x4628 US_TEX_INST_2 +0x462C US_TEX_INST_3 +0x4630 US_TEX_INST_4 +0x4634 US_TEX_INST_5 +0x4638 US_TEX_INST_6 +0x463C US_TEX_INST_7 +0x4640 US_TEX_INST_8 +0x4644 US_TEX_INST_9 +0x4648 US_TEX_INST_10 +0x464C US_TEX_INST_11 +0x4650 US_TEX_INST_12 +0x4654 US_TEX_INST_13 +0x4658 US_TEX_INST_14 +0x465C US_TEX_INST_15 +0x4660 US_TEX_INST_16 +0x4664 US_TEX_INST_17 +0x4668 US_TEX_INST_18 +0x466C US_TEX_INST_19 +0x4670 US_TEX_INST_20 +0x4674 US_TEX_INST_21 +0x4678 US_TEX_INST_22 +0x467C US_TEX_INST_23 +0x4680 US_TEX_INST_24 +0x4684 US_TEX_INST_25 +0x4688 US_TEX_INST_26 +0x468C US_TEX_INST_27 +0x4690 US_TEX_INST_28 +0x4694 US_TEX_INST_29 +0x4698 US_TEX_INST_30 +0x469C US_TEX_INST_31 +0x46A4 US_OUT_FMT_0 +0x46A8 US_OUT_FMT_1 +0x46AC US_OUT_FMT_2 +0x46B0 US_OUT_FMT_3 +0x46B4 US_W_FMT +0x46B8 US_CODE_BANK +0x46BC US_CODE_EXT +0x46C0 US_ALU_RGB_ADDR_0 +0x46C4 US_ALU_RGB_ADDR_1 +0x46C8 US_ALU_RGB_ADDR_2 +0x46CC US_ALU_RGB_ADDR_3 +0x46D0 US_ALU_RGB_ADDR_4 +0x46D4 US_ALU_RGB_ADDR_5 +0x46D8 US_ALU_RGB_ADDR_6 +0x46DC US_ALU_RGB_ADDR_7 +0x46E0 US_ALU_RGB_ADDR_8 +0x46E4 US_ALU_RGB_ADDR_9 +0x46E8 US_ALU_RGB_ADDR_10 +0x46EC US_ALU_RGB_ADDR_11 +0x46F0 US_ALU_RGB_ADDR_12 +0x46F4 US_ALU_RGB_ADDR_13 +0x46F8 US_ALU_RGB_ADDR_14 +0x46FC US_ALU_RGB_ADDR_15 +0x4700 US_ALU_RGB_ADDR_16 +0x4704 US_ALU_RGB_ADDR_17 +0x4708 US_ALU_RGB_ADDR_18 +0x470C US_ALU_RGB_ADDR_19 +0x4710 US_ALU_RGB_ADDR_20 +0x4714 US_ALU_RGB_ADDR_21 +0x4718 US_ALU_RGB_ADDR_22 +0x471C US_ALU_RGB_ADDR_23 +0x4720 US_ALU_RGB_ADDR_24 +0x4724 US_ALU_RGB_ADDR_25 +0x4728 US_ALU_RGB_ADDR_26 +0x472C US_ALU_RGB_ADDR_27 +0x4730 US_ALU_RGB_ADDR_28 +0x4734 US_ALU_RGB_ADDR_29 +0x4738 US_ALU_RGB_ADDR_30 +0x473C US_ALU_RGB_ADDR_31 +0x4740 US_ALU_RGB_ADDR_32 +0x4744 US_ALU_RGB_ADDR_33 +0x4748 US_ALU_RGB_ADDR_34 +0x474C US_ALU_RGB_ADDR_35 +0x4750 US_ALU_RGB_ADDR_36 +0x4754 US_ALU_RGB_ADDR_37 +0x4758 US_ALU_RGB_ADDR_38 +0x475C US_ALU_RGB_ADDR_39 +0x4760 US_ALU_RGB_ADDR_40 +0x4764 US_ALU_RGB_ADDR_41 +0x4768 US_ALU_RGB_ADDR_42 +0x476C US_ALU_RGB_ADDR_43 +0x4770 US_ALU_RGB_ADDR_44 +0x4774 US_ALU_RGB_ADDR_45 +0x4778 US_ALU_RGB_ADDR_46 +0x477C US_ALU_RGB_ADDR_47 +0x4780 US_ALU_RGB_ADDR_48 +0x4784 US_ALU_RGB_ADDR_49 +0x4788 US_ALU_RGB_ADDR_50 +0x478C US_ALU_RGB_ADDR_51 +0x4790 US_ALU_RGB_ADDR_52 +0x4794 US_ALU_RGB_ADDR_53 +0x4798 US_ALU_RGB_ADDR_54 +0x479C US_ALU_RGB_ADDR_55 +0x47A0 US_ALU_RGB_ADDR_56 +0x47A4 US_ALU_RGB_ADDR_57 +0x47A8 US_ALU_RGB_ADDR_58 +0x47AC US_ALU_RGB_ADDR_59 +0x47B0 US_ALU_RGB_ADDR_60 +0x47B4 US_ALU_RGB_ADDR_61 +0x47B8 US_ALU_RGB_ADDR_62 +0x47BC US_ALU_RGB_ADDR_63 +0x47C0 US_ALU_ALPHA_ADDR_0 +0x47C4 US_ALU_ALPHA_ADDR_1 +0x47C8 US_ALU_ALPHA_ADDR_2 +0x47CC US_ALU_ALPHA_ADDR_3 +0x47D0 US_ALU_ALPHA_ADDR_4 +0x47D4 US_ALU_ALPHA_ADDR_5 +0x47D8 US_ALU_ALPHA_ADDR_6 +0x47DC US_ALU_ALPHA_ADDR_7 +0x47E0 US_ALU_ALPHA_ADDR_8 +0x47E4 US_ALU_ALPHA_ADDR_9 +0x47E8 US_ALU_ALPHA_ADDR_10 +0x47EC US_ALU_ALPHA_ADDR_11 +0x47F0 US_ALU_ALPHA_ADDR_12 +0x47F4 US_ALU_ALPHA_ADDR_13 +0x47F8 US_ALU_ALPHA_ADDR_14 +0x47FC US_ALU_ALPHA_ADDR_15 +0x4800 US_ALU_ALPHA_ADDR_16 +0x4804 US_ALU_ALPHA_ADDR_17 +0x4808 US_ALU_ALPHA_ADDR_18 +0x480C US_ALU_ALPHA_ADDR_19 +0x4810 US_ALU_ALPHA_ADDR_20 +0x4814 US_ALU_ALPHA_ADDR_21 +0x4818 US_ALU_ALPHA_ADDR_22 +0x481C US_ALU_ALPHA_ADDR_23 +0x4820 US_ALU_ALPHA_ADDR_24 +0x4824 US_ALU_ALPHA_ADDR_25 +0x4828 US_ALU_ALPHA_ADDR_26 +0x482C US_ALU_ALPHA_ADDR_27 +0x4830 US_ALU_ALPHA_ADDR_28 +0x4834 US_ALU_ALPHA_ADDR_29 +0x4838 US_ALU_ALPHA_ADDR_30 +0x483C US_ALU_ALPHA_ADDR_31 +0x4840 US_ALU_ALPHA_ADDR_32 +0x4844 US_ALU_ALPHA_ADDR_33 +0x4848 US_ALU_ALPHA_ADDR_34 +0x484C US_ALU_ALPHA_ADDR_35 +0x4850 US_ALU_ALPHA_ADDR_36 +0x4854 US_ALU_ALPHA_ADDR_37 +0x4858 US_ALU_ALPHA_ADDR_38 +0x485C US_ALU_ALPHA_ADDR_39 +0x4860 US_ALU_ALPHA_ADDR_40 +0x4864 US_ALU_ALPHA_ADDR_41 +0x4868 US_ALU_ALPHA_ADDR_42 +0x486C US_ALU_ALPHA_ADDR_43 +0x4870 US_ALU_ALPHA_ADDR_44 +0x4874 US_ALU_ALPHA_ADDR_45 +0x4878 US_ALU_ALPHA_ADDR_46 +0x487C US_ALU_ALPHA_ADDR_47 +0x4880 US_ALU_ALPHA_ADDR_48 +0x4884 US_ALU_ALPHA_ADDR_49 +0x4888 US_ALU_ALPHA_ADDR_50 +0x488C US_ALU_ALPHA_ADDR_51 +0x4890 US_ALU_ALPHA_ADDR_52 +0x4894 US_ALU_ALPHA_ADDR_53 +0x4898 US_ALU_ALPHA_ADDR_54 +0x489C US_ALU_ALPHA_ADDR_55 +0x48A0 US_ALU_ALPHA_ADDR_56 +0x48A4 US_ALU_ALPHA_ADDR_57 +0x48A8 US_ALU_ALPHA_ADDR_58 +0x48AC US_ALU_ALPHA_ADDR_59 +0x48B0 US_ALU_ALPHA_ADDR_60 +0x48B4 US_ALU_ALPHA_ADDR_61 +0x48B8 US_ALU_ALPHA_ADDR_62 +0x48BC US_ALU_ALPHA_ADDR_63 +0x48C0 US_ALU_RGB_INST_0 +0x48C4 US_ALU_RGB_INST_1 +0x48C8 US_ALU_RGB_INST_2 +0x48CC US_ALU_RGB_INST_3 +0x48D0 US_ALU_RGB_INST_4 +0x48D4 US_ALU_RGB_INST_5 +0x48D8 US_ALU_RGB_INST_6 +0x48DC US_ALU_RGB_INST_7 +0x48E0 US_ALU_RGB_INST_8 +0x48E4 US_ALU_RGB_INST_9 +0x48E8 US_ALU_RGB_INST_10 +0x48EC US_ALU_RGB_INST_11 +0x48F0 US_ALU_RGB_INST_12 +0x48F4 US_ALU_RGB_INST_13 +0x48F8 US_ALU_RGB_INST_14 +0x48FC US_ALU_RGB_INST_15 +0x4900 US_ALU_RGB_INST_16 +0x4904 US_ALU_RGB_INST_17 +0x4908 US_ALU_RGB_INST_18 +0x490C US_ALU_RGB_INST_19 +0x4910 US_ALU_RGB_INST_20 +0x4914 US_ALU_RGB_INST_21 +0x4918 US_ALU_RGB_INST_22 +0x491C US_ALU_RGB_INST_23 +0x4920 US_ALU_RGB_INST_24 +0x4924 US_ALU_RGB_INST_25 +0x4928 US_ALU_RGB_INST_26 +0x492C US_ALU_RGB_INST_27 +0x4930 US_ALU_RGB_INST_28 +0x4934 US_ALU_RGB_INST_29 +0x4938 US_ALU_RGB_INST_30 +0x493C US_ALU_RGB_INST_31 +0x4940 US_ALU_RGB_INST_32 +0x4944 US_ALU_RGB_INST_33 +0x4948 US_ALU_RGB_INST_34 +0x494C US_ALU_RGB_INST_35 +0x4950 US_ALU_RGB_INST_36 +0x4954 US_ALU_RGB_INST_37 +0x4958 US_ALU_RGB_INST_38 +0x495C US_ALU_RGB_INST_39 +0x4960 US_ALU_RGB_INST_40 +0x4964 US_ALU_RGB_INST_41 +0x4968 US_ALU_RGB_INST_42 +0x496C US_ALU_RGB_INST_43 +0x4970 US_ALU_RGB_INST_44 +0x4974 US_ALU_RGB_INST_45 +0x4978 US_ALU_RGB_INST_46 +0x497C US_ALU_RGB_INST_47 +0x4980 US_ALU_RGB_INST_48 +0x4984 US_ALU_RGB_INST_49 +0x4988 US_ALU_RGB_INST_50 +0x498C US_ALU_RGB_INST_51 +0x4990 US_ALU_RGB_INST_52 +0x4994 US_ALU_RGB_INST_53 +0x4998 US_ALU_RGB_INST_54 +0x499C US_ALU_RGB_INST_55 +0x49A0 US_ALU_RGB_INST_56 +0x49A4 US_ALU_RGB_INST_57 +0x49A8 US_ALU_RGB_INST_58 +0x49AC US_ALU_RGB_INST_59 +0x49B0 US_ALU_RGB_INST_60 +0x49B4 US_ALU_RGB_INST_61 +0x49B8 US_ALU_RGB_INST_62 +0x49BC US_ALU_RGB_INST_63 +0x49C0 US_ALU_ALPHA_INST_0 +0x49C4 US_ALU_ALPHA_INST_1 +0x49C8 US_ALU_ALPHA_INST_2 +0x49CC US_ALU_ALPHA_INST_3 +0x49D0 US_ALU_ALPHA_INST_4 +0x49D4 US_ALU_ALPHA_INST_5 +0x49D8 US_ALU_ALPHA_INST_6 +0x49DC US_ALU_ALPHA_INST_7 +0x49E0 US_ALU_ALPHA_INST_8 +0x49E4 US_ALU_ALPHA_INST_9 +0x49E8 US_ALU_ALPHA_INST_10 +0x49EC US_ALU_ALPHA_INST_11 +0x49F0 US_ALU_ALPHA_INST_12 +0x49F4 US_ALU_ALPHA_INST_13 +0x49F8 US_ALU_ALPHA_INST_14 +0x49FC US_ALU_ALPHA_INST_15 +0x4A00 US_ALU_ALPHA_INST_16 +0x4A04 US_ALU_ALPHA_INST_17 +0x4A08 US_ALU_ALPHA_INST_18 +0x4A0C US_ALU_ALPHA_INST_19 +0x4A10 US_ALU_ALPHA_INST_20 +0x4A14 US_ALU_ALPHA_INST_21 +0x4A18 US_ALU_ALPHA_INST_22 +0x4A1C US_ALU_ALPHA_INST_23 +0x4A20 US_ALU_ALPHA_INST_24 +0x4A24 US_ALU_ALPHA_INST_25 +0x4A28 US_ALU_ALPHA_INST_26 +0x4A2C US_ALU_ALPHA_INST_27 +0x4A30 US_ALU_ALPHA_INST_28 +0x4A34 US_ALU_ALPHA_INST_29 +0x4A38 US_ALU_ALPHA_INST_30 +0x4A3C US_ALU_ALPHA_INST_31 +0x4A40 US_ALU_ALPHA_INST_32 +0x4A44 US_ALU_ALPHA_INST_33 +0x4A48 US_ALU_ALPHA_INST_34 +0x4A4C US_ALU_ALPHA_INST_35 +0x4A50 US_ALU_ALPHA_INST_36 +0x4A54 US_ALU_ALPHA_INST_37 +0x4A58 US_ALU_ALPHA_INST_38 +0x4A5C US_ALU_ALPHA_INST_39 +0x4A60 US_ALU_ALPHA_INST_40 +0x4A64 US_ALU_ALPHA_INST_41 +0x4A68 US_ALU_ALPHA_INST_42 +0x4A6C US_ALU_ALPHA_INST_43 +0x4A70 US_ALU_ALPHA_INST_44 +0x4A74 US_ALU_ALPHA_INST_45 +0x4A78 US_ALU_ALPHA_INST_46 +0x4A7C US_ALU_ALPHA_INST_47 +0x4A80 US_ALU_ALPHA_INST_48 +0x4A84 US_ALU_ALPHA_INST_49 +0x4A88 US_ALU_ALPHA_INST_50 +0x4A8C US_ALU_ALPHA_INST_51 +0x4A90 US_ALU_ALPHA_INST_52 +0x4A94 US_ALU_ALPHA_INST_53 +0x4A98 US_ALU_ALPHA_INST_54 +0x4A9C US_ALU_ALPHA_INST_55 +0x4AA0 US_ALU_ALPHA_INST_56 +0x4AA4 US_ALU_ALPHA_INST_57 +0x4AA8 US_ALU_ALPHA_INST_58 +0x4AAC US_ALU_ALPHA_INST_59 +0x4AB0 US_ALU_ALPHA_INST_60 +0x4AB4 US_ALU_ALPHA_INST_61 +0x4AB8 US_ALU_ALPHA_INST_62 +0x4ABC US_ALU_ALPHA_INST_63 +0x4AC0 US_ALU_EXT_ADDR_0 +0x4AC4 US_ALU_EXT_ADDR_1 +0x4AC8 US_ALU_EXT_ADDR_2 +0x4ACC US_ALU_EXT_ADDR_3 +0x4AD0 US_ALU_EXT_ADDR_4 +0x4AD4 US_ALU_EXT_ADDR_5 +0x4AD8 US_ALU_EXT_ADDR_6 +0x4ADC US_ALU_EXT_ADDR_7 +0x4AE0 US_ALU_EXT_ADDR_8 +0x4AE4 US_ALU_EXT_ADDR_9 +0x4AE8 US_ALU_EXT_ADDR_10 +0x4AEC US_ALU_EXT_ADDR_11 +0x4AF0 US_ALU_EXT_ADDR_12 +0x4AF4 US_ALU_EXT_ADDR_13 +0x4AF8 US_ALU_EXT_ADDR_14 +0x4AFC US_ALU_EXT_ADDR_15 +0x4B00 US_ALU_EXT_ADDR_16 +0x4B04 US_ALU_EXT_ADDR_17 +0x4B08 US_ALU_EXT_ADDR_18 +0x4B0C US_ALU_EXT_ADDR_19 +0x4B10 US_ALU_EXT_ADDR_20 +0x4B14 US_ALU_EXT_ADDR_21 +0x4B18 US_ALU_EXT_ADDR_22 +0x4B1C US_ALU_EXT_ADDR_23 +0x4B20 US_ALU_EXT_ADDR_24 +0x4B24 US_ALU_EXT_ADDR_25 +0x4B28 US_ALU_EXT_ADDR_26 +0x4B2C US_ALU_EXT_ADDR_27 +0x4B30 US_ALU_EXT_ADDR_28 +0x4B34 US_ALU_EXT_ADDR_29 +0x4B38 US_ALU_EXT_ADDR_30 +0x4B3C US_ALU_EXT_ADDR_31 +0x4B40 US_ALU_EXT_ADDR_32 +0x4B44 US_ALU_EXT_ADDR_33 +0x4B48 US_ALU_EXT_ADDR_34 +0x4B4C US_ALU_EXT_ADDR_35 +0x4B50 US_ALU_EXT_ADDR_36 +0x4B54 US_ALU_EXT_ADDR_37 +0x4B58 US_ALU_EXT_ADDR_38 +0x4B5C US_ALU_EXT_ADDR_39 +0x4B60 US_ALU_EXT_ADDR_40 +0x4B64 US_ALU_EXT_ADDR_41 +0x4B68 US_ALU_EXT_ADDR_42 +0x4B6C US_ALU_EXT_ADDR_43 +0x4B70 US_ALU_EXT_ADDR_44 +0x4B74 US_ALU_EXT_ADDR_45 +0x4B78 US_ALU_EXT_ADDR_46 +0x4B7C US_ALU_EXT_ADDR_47 +0x4B80 US_ALU_EXT_ADDR_48 +0x4B84 US_ALU_EXT_ADDR_49 +0x4B88 US_ALU_EXT_ADDR_50 +0x4B8C US_ALU_EXT_ADDR_51 +0x4B90 US_ALU_EXT_ADDR_52 +0x4B94 US_ALU_EXT_ADDR_53 +0x4B98 US_ALU_EXT_ADDR_54 +0x4B9C US_ALU_EXT_ADDR_55 +0x4BA0 US_ALU_EXT_ADDR_56 +0x4BA4 US_ALU_EXT_ADDR_57 +0x4BA8 US_ALU_EXT_ADDR_58 +0x4BAC US_ALU_EXT_ADDR_59 +0x4BB0 US_ALU_EXT_ADDR_60 +0x4BB4 US_ALU_EXT_ADDR_61 +0x4BB8 US_ALU_EXT_ADDR_62 +0x4BBC US_ALU_EXT_ADDR_63 +0x4BC0 FG_FOG_BLEND +0x4BC4 FG_FOG_FACTOR +0x4BC8 FG_FOG_COLOR_R +0x4BCC FG_FOG_COLOR_G +0x4BD0 FG_FOG_COLOR_B +0x4BD4 FG_ALPHA_FUNC +0x4BD8 FG_DEPTH_SRC +0x4C00 US_ALU_CONST_R_0 +0x4C04 US_ALU_CONST_G_0 +0x4C08 US_ALU_CONST_B_0 +0x4C0C US_ALU_CONST_A_0 +0x4C10 US_ALU_CONST_R_1 +0x4C14 US_ALU_CONST_G_1 +0x4C18 US_ALU_CONST_B_1 +0x4C1C US_ALU_CONST_A_1 +0x4C20 US_ALU_CONST_R_2 +0x4C24 US_ALU_CONST_G_2 +0x4C28 US_ALU_CONST_B_2 +0x4C2C US_ALU_CONST_A_2 +0x4C30 US_ALU_CONST_R_3 +0x4C34 US_ALU_CONST_G_3 +0x4C38 US_ALU_CONST_B_3 +0x4C3C US_ALU_CONST_A_3 +0x4C40 US_ALU_CONST_R_4 +0x4C44 US_ALU_CONST_G_4 +0x4C48 US_ALU_CONST_B_4 +0x4C4C US_ALU_CONST_A_4 +0x4C50 US_ALU_CONST_R_5 +0x4C54 US_ALU_CONST_G_5 +0x4C58 US_ALU_CONST_B_5 +0x4C5C US_ALU_CONST_A_5 +0x4C60 US_ALU_CONST_R_6 +0x4C64 US_ALU_CONST_G_6 +0x4C68 US_ALU_CONST_B_6 +0x4C6C US_ALU_CONST_A_6 +0x4C70 US_ALU_CONST_R_7 +0x4C74 US_ALU_CONST_G_7 +0x4C78 US_ALU_CONST_B_7 +0x4C7C US_ALU_CONST_A_7 +0x4C80 US_ALU_CONST_R_8 +0x4C84 US_ALU_CONST_G_8 +0x4C88 US_ALU_CONST_B_8 +0x4C8C US_ALU_CONST_A_8 +0x4C90 US_ALU_CONST_R_9 +0x4C94 US_ALU_CONST_G_9 +0x4C98 US_ALU_CONST_B_9 +0x4C9C US_ALU_CONST_A_9 +0x4CA0 US_ALU_CONST_R_10 +0x4CA4 US_ALU_CONST_G_10 +0x4CA8 US_ALU_CONST_B_10 +0x4CAC US_ALU_CONST_A_10 +0x4CB0 US_ALU_CONST_R_11 +0x4CB4 US_ALU_CONST_G_11 +0x4CB8 US_ALU_CONST_B_11 +0x4CBC US_ALU_CONST_A_11 +0x4CC0 US_ALU_CONST_R_12 +0x4CC4 US_ALU_CONST_G_12 +0x4CC8 US_ALU_CONST_B_12 +0x4CCC US_ALU_CONST_A_12 +0x4CD0 US_ALU_CONST_R_13 +0x4CD4 US_ALU_CONST_G_13 +0x4CD8 US_ALU_CONST_B_13 +0x4CDC US_ALU_CONST_A_13 +0x4CE0 US_ALU_CONST_R_14 +0x4CE4 US_ALU_CONST_G_14 +0x4CE8 US_ALU_CONST_B_14 +0x4CEC US_ALU_CONST_A_14 +0x4CF0 US_ALU_CONST_R_15 +0x4CF4 US_ALU_CONST_G_15 +0x4CF8 US_ALU_CONST_B_15 +0x4CFC US_ALU_CONST_A_15 +0x4D00 US_ALU_CONST_R_16 +0x4D04 US_ALU_CONST_G_16 +0x4D08 US_ALU_CONST_B_16 +0x4D0C US_ALU_CONST_A_16 +0x4D10 US_ALU_CONST_R_17 +0x4D14 US_ALU_CONST_G_17 +0x4D18 US_ALU_CONST_B_17 +0x4D1C US_ALU_CONST_A_17 +0x4D20 US_ALU_CONST_R_18 +0x4D24 US_ALU_CONST_G_18 +0x4D28 US_ALU_CONST_B_18 +0x4D2C US_ALU_CONST_A_18 +0x4D30 US_ALU_CONST_R_19 +0x4D34 US_ALU_CONST_G_19 +0x4D38 US_ALU_CONST_B_19 +0x4D3C US_ALU_CONST_A_19 +0x4D40 US_ALU_CONST_R_20 +0x4D44 US_ALU_CONST_G_20 +0x4D48 US_ALU_CONST_B_20 +0x4D4C US_ALU_CONST_A_20 +0x4D50 US_ALU_CONST_R_21 +0x4D54 US_ALU_CONST_G_21 +0x4D58 US_ALU_CONST_B_21 +0x4D5C US_ALU_CONST_A_21 +0x4D60 US_ALU_CONST_R_22 +0x4D64 US_ALU_CONST_G_22 +0x4D68 US_ALU_CONST_B_22 +0x4D6C US_ALU_CONST_A_22 +0x4D70 US_ALU_CONST_R_23 +0x4D74 US_ALU_CONST_G_23 +0x4D78 US_ALU_CONST_B_23 +0x4D7C US_ALU_CONST_A_23 +0x4D80 US_ALU_CONST_R_24 +0x4D84 US_ALU_CONST_G_24 +0x4D88 US_ALU_CONST_B_24 +0x4D8C US_ALU_CONST_A_24 +0x4D90 US_ALU_CONST_R_25 +0x4D94 US_ALU_CONST_G_25 +0x4D98 US_ALU_CONST_B_25 +0x4D9C US_ALU_CONST_A_25 +0x4DA0 US_ALU_CONST_R_26 +0x4DA4 US_ALU_CONST_G_26 +0x4DA8 US_ALU_CONST_B_26 +0x4DAC US_ALU_CONST_A_26 +0x4DB0 US_ALU_CONST_R_27 +0x4DB4 US_ALU_CONST_G_27 +0x4DB8 US_ALU_CONST_B_27 +0x4DBC US_ALU_CONST_A_27 +0x4DC0 US_ALU_CONST_R_28 +0x4DC4 US_ALU_CONST_G_28 +0x4DC8 US_ALU_CONST_B_28 +0x4DCC US_ALU_CONST_A_28 +0x4DD0 US_ALU_CONST_R_29 +0x4DD4 US_ALU_CONST_G_29 +0x4DD8 US_ALU_CONST_B_29 +0x4DDC US_ALU_CONST_A_29 +0x4DE0 US_ALU_CONST_R_30 +0x4DE4 US_ALU_CONST_G_30 +0x4DE8 US_ALU_CONST_B_30 +0x4DEC US_ALU_CONST_A_30 +0x4DF0 US_ALU_CONST_R_31 +0x4DF4 US_ALU_CONST_G_31 +0x4DF8 US_ALU_CONST_B_31 +0x4DFC US_ALU_CONST_A_31 +0x4E08 RB3D_ABLENDCNTL_R3 +0x4E10 RB3D_CONSTANT_COLOR +0x4E14 RB3D_COLOR_CLEAR_VALUE +0x4E18 RB3D_ROPCNTL_R3 +0x4E1C RB3D_CLRCMP_FLIPE_R3 +0x4E20 RB3D_CLRCMP_CLR_R3 +0x4E24 RB3D_CLRCMP_MSK_R3 +0x4E48 RB3D_DEBUG_CTL +0x4E4C RB3D_DSTCACHE_CTLSTAT_R3 +0x4E50 RB3D_DITHER_CTL +0x4E54 RB3D_CMASK_OFFSET0 +0x4E58 RB3D_CMASK_OFFSET1 +0x4E5C RB3D_CMASK_OFFSET2 +0x4E60 RB3D_CMASK_OFFSET3 +0x4E64 RB3D_CMASK_PITCH0 +0x4E68 RB3D_CMASK_PITCH1 +0x4E6C RB3D_CMASK_PITCH2 +0x4E70 RB3D_CMASK_PITCH3 +0x4E74 RB3D_CMASK_WRINDEX +0x4E78 RB3D_CMASK_DWORD +0x4E7C RB3D_CMASK_RDINDEX +0x4EA0 RB3D_DISCARD_SRC_PIXEL_LTE_THRESHOLD +0x4EA4 RB3D_DISCARD_SRC_PIXEL_GTE_THRESHOLD +0x4F04 ZB_ZSTENCILCNTL +0x4F08 ZB_STENCILREFMASK +0x4F14 ZB_ZTOP +0x4F18 ZB_ZCACHE_CTLSTAT +0x4F28 ZB_DEPTHCLEARVALUE +0x4F58 ZB_ZPASS_DATA diff --git a/sys/dev/drm2/radeon/reg_srcs/rv515 b/sys/dev/drm2/radeon/reg_srcs/rv515 new file mode 100644 index 00000000000..78d5e99d759 --- /dev/null +++ b/sys/dev/drm2/radeon/reg_srcs/rv515 @@ -0,0 +1,496 @@ +rv515 0x6d40 +0x1434 SRC_Y_X +0x1438 DST_Y_X +0x143C DST_HEIGHT_WIDTH +0x146C DP_GUI_MASTER_CNTL +0x1474 BRUSH_Y_X +0x1478 DP_BRUSH_BKGD_CLR +0x147C DP_BRUSH_FRGD_CLR +0x1480 BRUSH_DATA0 +0x1484 BRUSH_DATA1 +0x1598 DST_WIDTH_HEIGHT +0x15C0 CLR_CMP_CNTL +0x15C4 CLR_CMP_CLR_SRC +0x15C8 CLR_CMP_CLR_DST +0x15CC CLR_CMP_MSK +0x15D8 DP_SRC_FRGD_CLR +0x15DC DP_SRC_BKGD_CLR +0x1600 DST_LINE_START +0x1604 DST_LINE_END +0x1608 DST_LINE_PATCOUNT +0x16C0 DP_CNTL +0x16CC DP_WRITE_MSK +0x16D0 DP_CNTL_XDIR_YDIR_YMAJOR +0x16E8 DEFAULT_SC_BOTTOM_RIGHT +0x16EC SC_TOP_LEFT +0x16F0 SC_BOTTOM_RIGHT +0x16F4 SRC_SC_BOTTOM_RIGHT +0x1714 DSTCACHE_CTLSTAT +0x1720 WAIT_UNTIL +0x172C RBBM_GUICNTL +0x1D98 VAP_VPORT_XSCALE +0x1D9C VAP_VPORT_XOFFSET +0x1DA0 VAP_VPORT_YSCALE +0x1DA4 VAP_VPORT_YOFFSET +0x1DA8 VAP_VPORT_ZSCALE +0x1DAC VAP_VPORT_ZOFFSET +0x2080 VAP_CNTL +0x208C VAP_INDEX_OFFSET +0x2090 VAP_OUT_VTX_FMT_0 +0x2094 VAP_OUT_VTX_FMT_1 +0x20B0 VAP_VTE_CNTL +0x2138 VAP_VF_MIN_VTX_INDX +0x2140 VAP_CNTL_STATUS +0x2150 VAP_PROG_STREAM_CNTL_0 +0x2154 VAP_PROG_STREAM_CNTL_1 +0x2158 VAP_PROG_STREAM_CNTL_2 +0x215C VAP_PROG_STREAM_CNTL_3 +0x2160 VAP_PROG_STREAM_CNTL_4 +0x2164 VAP_PROG_STREAM_CNTL_5 +0x2168 VAP_PROG_STREAM_CNTL_6 +0x216C VAP_PROG_STREAM_CNTL_7 +0x2180 VAP_VTX_STATE_CNTL +0x2184 VAP_VSM_VTX_ASSM +0x2188 VAP_VTX_STATE_IND_REG_0 +0x218C VAP_VTX_STATE_IND_REG_1 +0x2190 VAP_VTX_STATE_IND_REG_2 +0x2194 VAP_VTX_STATE_IND_REG_3 +0x2198 VAP_VTX_STATE_IND_REG_4 +0x219C VAP_VTX_STATE_IND_REG_5 +0x21A0 VAP_VTX_STATE_IND_REG_6 +0x21A4 VAP_VTX_STATE_IND_REG_7 +0x21A8 VAP_VTX_STATE_IND_REG_8 +0x21AC VAP_VTX_STATE_IND_REG_9 +0x21B0 VAP_VTX_STATE_IND_REG_10 +0x21B4 VAP_VTX_STATE_IND_REG_11 +0x21B8 VAP_VTX_STATE_IND_REG_12 +0x21BC VAP_VTX_STATE_IND_REG_13 +0x21C0 VAP_VTX_STATE_IND_REG_14 +0x21C4 VAP_VTX_STATE_IND_REG_15 +0x21DC VAP_PSC_SGN_NORM_CNTL +0x21E0 VAP_PROG_STREAM_CNTL_EXT_0 +0x21E4 VAP_PROG_STREAM_CNTL_EXT_1 +0x21E8 VAP_PROG_STREAM_CNTL_EXT_2 +0x21EC VAP_PROG_STREAM_CNTL_EXT_3 +0x21F0 VAP_PROG_STREAM_CNTL_EXT_4 +0x21F4 VAP_PROG_STREAM_CNTL_EXT_5 +0x21F8 VAP_PROG_STREAM_CNTL_EXT_6 +0x21FC VAP_PROG_STREAM_CNTL_EXT_7 +0x2200 VAP_PVS_VECTOR_INDX_REG +0x2204 VAP_PVS_VECTOR_DATA_REG +0x2208 VAP_PVS_VECTOR_DATA_REG_128 +0x2218 VAP_TEX_TO_COLOR_CNTL +0x221C VAP_CLIP_CNTL +0x2220 VAP_GB_VERT_CLIP_ADJ +0x2224 VAP_GB_VERT_DISC_ADJ +0x2228 VAP_GB_HORZ_CLIP_ADJ +0x222C VAP_GB_HORZ_DISC_ADJ +0x2230 VAP_PVS_FLOW_CNTL_ADDRS_0 +0x2234 VAP_PVS_FLOW_CNTL_ADDRS_1 +0x2238 VAP_PVS_FLOW_CNTL_ADDRS_2 +0x223C VAP_PVS_FLOW_CNTL_ADDRS_3 +0x2240 VAP_PVS_FLOW_CNTL_ADDRS_4 +0x2244 VAP_PVS_FLOW_CNTL_ADDRS_5 +0x2248 VAP_PVS_FLOW_CNTL_ADDRS_6 +0x224C VAP_PVS_FLOW_CNTL_ADDRS_7 +0x2250 VAP_PVS_FLOW_CNTL_ADDRS_8 +0x2254 VAP_PVS_FLOW_CNTL_ADDRS_9 +0x2258 VAP_PVS_FLOW_CNTL_ADDRS_10 +0x225C VAP_PVS_FLOW_CNTL_ADDRS_11 +0x2260 VAP_PVS_FLOW_CNTL_ADDRS_12 +0x2264 VAP_PVS_FLOW_CNTL_ADDRS_13 +0x2268 VAP_PVS_FLOW_CNTL_ADDRS_14 +0x226C VAP_PVS_FLOW_CNTL_ADDRS_15 +0x2284 VAP_PVS_STATE_FLUSH_REG +0x2288 VAP_PVS_VTX_TIMEOUT_REG +0x2290 VAP_PVS_FLOW_CNTL_LOOP_INDEX_0 +0x2294 VAP_PVS_FLOW_CNTL_LOOP_INDEX_1 +0x2298 VAP_PVS_FLOW_CNTL_LOOP_INDEX_2 +0x229C VAP_PVS_FLOW_CNTL_LOOP_INDEX_3 +0x22A0 VAP_PVS_FLOW_CNTL_LOOP_INDEX_4 +0x22A4 VAP_PVS_FLOW_CNTL_LOOP_INDEX_5 +0x22A8 VAP_PVS_FLOW_CNTL_LOOP_INDEX_6 +0x22AC VAP_PVS_FLOW_CNTL_LOOP_INDEX_7 +0x22B0 VAP_PVS_FLOW_CNTL_LOOP_INDEX_8 +0x22B4 VAP_PVS_FLOW_CNTL_LOOP_INDEX_9 +0x22B8 VAP_PVS_FLOW_CNTL_LOOP_INDEX_10 +0x22BC VAP_PVS_FLOW_CNTL_LOOP_INDEX_11 +0x22C0 VAP_PVS_FLOW_CNTL_LOOP_INDEX_12 +0x22C4 VAP_PVS_FLOW_CNTL_LOOP_INDEX_13 +0x22C8 VAP_PVS_FLOW_CNTL_LOOP_INDEX_14 +0x22CC VAP_PVS_FLOW_CNTL_LOOP_INDEX_15 +0x22D0 VAP_PVS_CODE_CNTL_0 +0x22D4 VAP_PVS_CONST_CNTL +0x22D8 VAP_PVS_CODE_CNTL_1 +0x22DC VAP_PVS_FLOW_CNTL_OPC +0x2500 VAP_PVS_FLOW_CNTL_ADDRS_LW_0 +0x2504 VAP_PVS_FLOW_CNTL_ADDRS_UW_0 +0x2508 VAP_PVS_FLOW_CNTL_ADDRS_LW_1 +0x250C VAP_PVS_FLOW_CNTL_ADDRS_UW_1 +0x2510 VAP_PVS_FLOW_CNTL_ADDRS_LW_2 +0x2514 VAP_PVS_FLOW_CNTL_ADDRS_UW_2 +0x2518 VAP_PVS_FLOW_CNTL_ADDRS_LW_3 +0x251C VAP_PVS_FLOW_CNTL_ADDRS_UW_3 +0x2520 VAP_PVS_FLOW_CNTL_ADDRS_LW_4 +0x2524 VAP_PVS_FLOW_CNTL_ADDRS_UW_4 +0x2528 VAP_PVS_FLOW_CNTL_ADDRS_LW_5 +0x252C VAP_PVS_FLOW_CNTL_ADDRS_UW_5 +0x2530 VAP_PVS_FLOW_CNTL_ADDRS_LW_6 +0x2534 VAP_PVS_FLOW_CNTL_ADDRS_UW_6 +0x2538 VAP_PVS_FLOW_CNTL_ADDRS_LW_7 +0x253C VAP_PVS_FLOW_CNTL_ADDRS_UW_7 +0x2540 VAP_PVS_FLOW_CNTL_ADDRS_LW_8 +0x2544 VAP_PVS_FLOW_CNTL_ADDRS_UW_8 +0x2548 VAP_PVS_FLOW_CNTL_ADDRS_LW_9 +0x254C VAP_PVS_FLOW_CNTL_ADDRS_UW_9 +0x2550 VAP_PVS_FLOW_CNTL_ADDRS_LW_10 +0x2554 VAP_PVS_FLOW_CNTL_ADDRS_UW_10 +0x2558 VAP_PVS_FLOW_CNTL_ADDRS_LW_11 +0x255C VAP_PVS_FLOW_CNTL_ADDRS_UW_11 +0x2560 VAP_PVS_FLOW_CNTL_ADDRS_LW_12 +0x2564 VAP_PVS_FLOW_CNTL_ADDRS_UW_12 +0x2568 VAP_PVS_FLOW_CNTL_ADDRS_LW_13 +0x256C VAP_PVS_FLOW_CNTL_ADDRS_UW_13 +0x2570 VAP_PVS_FLOW_CNTL_ADDRS_LW_14 +0x2574 VAP_PVS_FLOW_CNTL_ADDRS_UW_14 +0x2578 VAP_PVS_FLOW_CNTL_ADDRS_LW_15 +0x257C VAP_PVS_FLOW_CNTL_ADDRS_UW_15 +0x342C RB2D_DSTCACHE_CTLSTAT +0x4000 GB_VAP_RASTER_VTX_FMT_0 +0x4004 GB_VAP_RASTER_VTX_FMT_1 +0x4008 GB_ENABLE +0x4010 GB_MSPOS0 +0x4014 GB_MSPOS1 +0x401C GB_SELECT +0x4020 GB_AA_CONFIG +0x4024 GB_FIFO_SIZE +0x4100 TX_INVALTAGS +0x4114 SU_TEX_WRAP_PS3 +0x4118 PS3_ENABLE +0x411c PS3_VTX_FMT +0x4120 PS3_TEX_SOURCE +0x4200 GA_POINT_S0 +0x4204 GA_POINT_T0 +0x4208 GA_POINT_S1 +0x420C GA_POINT_T1 +0x4214 GA_TRIANGLE_STIPPLE +0x421C GA_POINT_SIZE +0x4230 GA_POINT_MINMAX +0x4234 GA_LINE_CNTL +0x4238 GA_LINE_STIPPLE_CONFIG +0x4258 GA_COLOR_CONTROL_PS3 +0x4260 GA_LINE_STIPPLE_VALUE +0x4264 GA_LINE_S0 +0x4268 GA_LINE_S1 +0x4278 GA_COLOR_CONTROL +0x427C GA_SOLID_RG +0x4280 GA_SOLID_BA +0x4288 GA_POLY_MODE +0x428C GA_ROUND_MODE +0x4290 GA_OFFSET +0x4294 GA_FOG_SCALE +0x4298 GA_FOG_OFFSET +0x42A0 SU_TEX_WRAP +0x42A4 SU_POLY_OFFSET_FRONT_SCALE +0x42A8 SU_POLY_OFFSET_FRONT_OFFSET +0x42AC SU_POLY_OFFSET_BACK_SCALE +0x42B0 SU_POLY_OFFSET_BACK_OFFSET +0x42B4 SU_POLY_OFFSET_ENABLE +0x42B8 SU_CULL_MODE +0x42C0 SU_DEPTH_SCALE +0x42C4 SU_DEPTH_OFFSET +0x42C8 SU_REG_DEST +0x4300 RS_COUNT +0x4304 RS_INST_COUNT +0x4074 RS_IP_0 +0x4078 RS_IP_1 +0x407C RS_IP_2 +0x4080 RS_IP_3 +0x4084 RS_IP_4 +0x4088 RS_IP_5 +0x408C RS_IP_6 +0x4090 RS_IP_7 +0x4094 RS_IP_8 +0x4098 RS_IP_9 +0x409C RS_IP_10 +0x40A0 RS_IP_11 +0x40A4 RS_IP_12 +0x40A8 RS_IP_13 +0x40AC RS_IP_14 +0x40B0 RS_IP_15 +0x4320 RS_INST_0 +0x4324 RS_INST_1 +0x4328 RS_INST_2 +0x432C RS_INST_3 +0x4330 RS_INST_4 +0x4334 RS_INST_5 +0x4338 RS_INST_6 +0x433C RS_INST_7 +0x4340 RS_INST_8 +0x4344 RS_INST_9 +0x4348 RS_INST_10 +0x434C RS_INST_11 +0x4350 RS_INST_12 +0x4354 RS_INST_13 +0x4358 RS_INST_14 +0x435C RS_INST_15 +0x43A8 SC_EDGERULE +0x43B0 SC_CLIP_0_A +0x43B4 SC_CLIP_0_B +0x43B8 SC_CLIP_1_A +0x43BC SC_CLIP_1_B +0x43C0 SC_CLIP_2_A +0x43C4 SC_CLIP_2_B +0x43C8 SC_CLIP_3_A +0x43CC SC_CLIP_3_B +0x43D0 SC_CLIP_RULE +0x43E0 SC_SCISSOR0 +0x43E8 SC_SCREENDOOR +0x4440 TX_FILTER1_0 +0x4444 TX_FILTER1_1 +0x4448 TX_FILTER1_2 +0x444C TX_FILTER1_3 +0x4450 TX_FILTER1_4 +0x4454 TX_FILTER1_5 +0x4458 TX_FILTER1_6 +0x445C TX_FILTER1_7 +0x4460 TX_FILTER1_8 +0x4464 TX_FILTER1_9 +0x4468 TX_FILTER1_10 +0x446C TX_FILTER1_11 +0x4470 TX_FILTER1_12 +0x4474 TX_FILTER1_13 +0x4478 TX_FILTER1_14 +0x447C TX_FILTER1_15 +0x4580 TX_CHROMA_KEY_0 +0x4584 TX_CHROMA_KEY_1 +0x4588 TX_CHROMA_KEY_2 +0x458C TX_CHROMA_KEY_3 +0x4590 TX_CHROMA_KEY_4 +0x4594 TX_CHROMA_KEY_5 +0x4598 TX_CHROMA_KEY_6 +0x459C TX_CHROMA_KEY_7 +0x45A0 TX_CHROMA_KEY_8 +0x45A4 TX_CHROMA_KEY_9 +0x45A8 TX_CHROMA_KEY_10 +0x45AC TX_CHROMA_KEY_11 +0x45B0 TX_CHROMA_KEY_12 +0x45B4 TX_CHROMA_KEY_13 +0x45B8 TX_CHROMA_KEY_14 +0x45BC TX_CHROMA_KEY_15 +0x45C0 TX_BORDER_COLOR_0 +0x45C4 TX_BORDER_COLOR_1 +0x45C8 TX_BORDER_COLOR_2 +0x45CC TX_BORDER_COLOR_3 +0x45D0 TX_BORDER_COLOR_4 +0x45D4 TX_BORDER_COLOR_5 +0x45D8 TX_BORDER_COLOR_6 +0x45DC TX_BORDER_COLOR_7 +0x45E0 TX_BORDER_COLOR_8 +0x45E4 TX_BORDER_COLOR_9 +0x45E8 TX_BORDER_COLOR_10 +0x45EC TX_BORDER_COLOR_11 +0x45F0 TX_BORDER_COLOR_12 +0x45F4 TX_BORDER_COLOR_13 +0x45F8 TX_BORDER_COLOR_14 +0x45FC TX_BORDER_COLOR_15 +0x4250 GA_US_VECTOR_INDEX +0x4254 GA_US_VECTOR_DATA +0x4600 US_CONFIG +0x4604 US_PIXSIZE +0x4620 US_FC_BOOL_CONST +0x4624 US_FC_CTRL +0x4630 US_CODE_ADDR +0x4634 US_CODE_RANGE +0x4638 US_CODE_OFFSET +0x4640 US_FORMAT0_0 +0x4644 US_FORMAT0_1 +0x4648 US_FORMAT0_2 +0x464C US_FORMAT0_3 +0x4650 US_FORMAT0_4 +0x4654 US_FORMAT0_5 +0x4658 US_FORMAT0_6 +0x465C US_FORMAT0_7 +0x4660 US_FORMAT0_8 +0x4664 US_FORMAT0_9 +0x4668 US_FORMAT0_10 +0x466C US_FORMAT0_11 +0x4670 US_FORMAT0_12 +0x4674 US_FORMAT0_13 +0x4678 US_FORMAT0_14 +0x467C US_FORMAT0_15 +0x46A4 US_OUT_FMT_0 +0x46A8 US_OUT_FMT_1 +0x46AC US_OUT_FMT_2 +0x46B0 US_OUT_FMT_3 +0x46B4 US_W_FMT +0x46C0 RB3D_COLOR_CLEAR_VALUE_AR +0x46C4 RB3D_COLOR_CLEAR_VALUE_GB +0x4BC0 FG_FOG_BLEND +0x4BC4 FG_FOG_FACTOR +0x4BC8 FG_FOG_COLOR_R +0x4BCC FG_FOG_COLOR_G +0x4BD0 FG_FOG_COLOR_B +0x4BD4 FG_ALPHA_FUNC +0x4BD8 FG_DEPTH_SRC +0x4BE0 FG_ALPHA_VALUE +0x4C00 US_ALU_CONST_R_0 +0x4C04 US_ALU_CONST_G_0 +0x4C08 US_ALU_CONST_B_0 +0x4C0C US_ALU_CONST_A_0 +0x4C10 US_ALU_CONST_R_1 +0x4C14 US_ALU_CONST_G_1 +0x4C18 US_ALU_CONST_B_1 +0x4C1C US_ALU_CONST_A_1 +0x4C20 US_ALU_CONST_R_2 +0x4C24 US_ALU_CONST_G_2 +0x4C28 US_ALU_CONST_B_2 +0x4C2C US_ALU_CONST_A_2 +0x4C30 US_ALU_CONST_R_3 +0x4C34 US_ALU_CONST_G_3 +0x4C38 US_ALU_CONST_B_3 +0x4C3C US_ALU_CONST_A_3 +0x4C40 US_ALU_CONST_R_4 +0x4C44 US_ALU_CONST_G_4 +0x4C48 US_ALU_CONST_B_4 +0x4C4C US_ALU_CONST_A_4 +0x4C50 US_ALU_CONST_R_5 +0x4C54 US_ALU_CONST_G_5 +0x4C58 US_ALU_CONST_B_5 +0x4C5C US_ALU_CONST_A_5 +0x4C60 US_ALU_CONST_R_6 +0x4C64 US_ALU_CONST_G_6 +0x4C68 US_ALU_CONST_B_6 +0x4C6C US_ALU_CONST_A_6 +0x4C70 US_ALU_CONST_R_7 +0x4C74 US_ALU_CONST_G_7 +0x4C78 US_ALU_CONST_B_7 +0x4C7C US_ALU_CONST_A_7 +0x4C80 US_ALU_CONST_R_8 +0x4C84 US_ALU_CONST_G_8 +0x4C88 US_ALU_CONST_B_8 +0x4C8C US_ALU_CONST_A_8 +0x4C90 US_ALU_CONST_R_9 +0x4C94 US_ALU_CONST_G_9 +0x4C98 US_ALU_CONST_B_9 +0x4C9C US_ALU_CONST_A_9 +0x4CA0 US_ALU_CONST_R_10 +0x4CA4 US_ALU_CONST_G_10 +0x4CA8 US_ALU_CONST_B_10 +0x4CAC US_ALU_CONST_A_10 +0x4CB0 US_ALU_CONST_R_11 +0x4CB4 US_ALU_CONST_G_11 +0x4CB8 US_ALU_CONST_B_11 +0x4CBC US_ALU_CONST_A_11 +0x4CC0 US_ALU_CONST_R_12 +0x4CC4 US_ALU_CONST_G_12 +0x4CC8 US_ALU_CONST_B_12 +0x4CCC US_ALU_CONST_A_12 +0x4CD0 US_ALU_CONST_R_13 +0x4CD4 US_ALU_CONST_G_13 +0x4CD8 US_ALU_CONST_B_13 +0x4CDC US_ALU_CONST_A_13 +0x4CE0 US_ALU_CONST_R_14 +0x4CE4 US_ALU_CONST_G_14 +0x4CE8 US_ALU_CONST_B_14 +0x4CEC US_ALU_CONST_A_14 +0x4CF0 US_ALU_CONST_R_15 +0x4CF4 US_ALU_CONST_G_15 +0x4CF8 US_ALU_CONST_B_15 +0x4CFC US_ALU_CONST_A_15 +0x4D00 US_ALU_CONST_R_16 +0x4D04 US_ALU_CONST_G_16 +0x4D08 US_ALU_CONST_B_16 +0x4D0C US_ALU_CONST_A_16 +0x4D10 US_ALU_CONST_R_17 +0x4D14 US_ALU_CONST_G_17 +0x4D18 US_ALU_CONST_B_17 +0x4D1C US_ALU_CONST_A_17 +0x4D20 US_ALU_CONST_R_18 +0x4D24 US_ALU_CONST_G_18 +0x4D28 US_ALU_CONST_B_18 +0x4D2C US_ALU_CONST_A_18 +0x4D30 US_ALU_CONST_R_19 +0x4D34 US_ALU_CONST_G_19 +0x4D38 US_ALU_CONST_B_19 +0x4D3C US_ALU_CONST_A_19 +0x4D40 US_ALU_CONST_R_20 +0x4D44 US_ALU_CONST_G_20 +0x4D48 US_ALU_CONST_B_20 +0x4D4C US_ALU_CONST_A_20 +0x4D50 US_ALU_CONST_R_21 +0x4D54 US_ALU_CONST_G_21 +0x4D58 US_ALU_CONST_B_21 +0x4D5C US_ALU_CONST_A_21 +0x4D60 US_ALU_CONST_R_22 +0x4D64 US_ALU_CONST_G_22 +0x4D68 US_ALU_CONST_B_22 +0x4D6C US_ALU_CONST_A_22 +0x4D70 US_ALU_CONST_R_23 +0x4D74 US_ALU_CONST_G_23 +0x4D78 US_ALU_CONST_B_23 +0x4D7C US_ALU_CONST_A_23 +0x4D80 US_ALU_CONST_R_24 +0x4D84 US_ALU_CONST_G_24 +0x4D88 US_ALU_CONST_B_24 +0x4D8C US_ALU_CONST_A_24 +0x4D90 US_ALU_CONST_R_25 +0x4D94 US_ALU_CONST_G_25 +0x4D98 US_ALU_CONST_B_25 +0x4D9C US_ALU_CONST_A_25 +0x4DA0 US_ALU_CONST_R_26 +0x4DA4 US_ALU_CONST_G_26 +0x4DA8 US_ALU_CONST_B_26 +0x4DAC US_ALU_CONST_A_26 +0x4DB0 US_ALU_CONST_R_27 +0x4DB4 US_ALU_CONST_G_27 +0x4DB8 US_ALU_CONST_B_27 +0x4DBC US_ALU_CONST_A_27 +0x4DC0 US_ALU_CONST_R_28 +0x4DC4 US_ALU_CONST_G_28 +0x4DC8 US_ALU_CONST_B_28 +0x4DCC US_ALU_CONST_A_28 +0x4DD0 US_ALU_CONST_R_29 +0x4DD4 US_ALU_CONST_G_29 +0x4DD8 US_ALU_CONST_B_29 +0x4DDC US_ALU_CONST_A_29 +0x4DE0 US_ALU_CONST_R_30 +0x4DE4 US_ALU_CONST_G_30 +0x4DE8 US_ALU_CONST_B_30 +0x4DEC US_ALU_CONST_A_30 +0x4DF0 US_ALU_CONST_R_31 +0x4DF4 US_ALU_CONST_G_31 +0x4DF8 US_ALU_CONST_B_31 +0x4DFC US_ALU_CONST_A_31 +0x4E08 RB3D_ABLENDCNTL_R3 +0x4E10 RB3D_CONSTANT_COLOR +0x4E14 RB3D_COLOR_CLEAR_VALUE +0x4E18 RB3D_ROPCNTL_R3 +0x4E1C RB3D_CLRCMP_FLIPE_R3 +0x4E20 RB3D_CLRCMP_CLR_R3 +0x4E24 RB3D_CLRCMP_MSK_R3 +0x4E48 RB3D_DEBUG_CTL +0x4E4C RB3D_DSTCACHE_CTLSTAT_R3 +0x4E50 RB3D_DITHER_CTL +0x4E54 RB3D_CMASK_OFFSET0 +0x4E58 RB3D_CMASK_OFFSET1 +0x4E5C RB3D_CMASK_OFFSET2 +0x4E60 RB3D_CMASK_OFFSET3 +0x4E64 RB3D_CMASK_PITCH0 +0x4E68 RB3D_CMASK_PITCH1 +0x4E6C RB3D_CMASK_PITCH2 +0x4E70 RB3D_CMASK_PITCH3 +0x4E74 RB3D_CMASK_WRINDEX +0x4E78 RB3D_CMASK_DWORD +0x4E7C RB3D_CMASK_RDINDEX +0x4EA0 RB3D_DISCARD_SRC_PIXEL_LTE_THRESHOLD +0x4EA4 RB3D_DISCARD_SRC_PIXEL_GTE_THRESHOLD +0x4EF8 RB3D_CONSTANT_COLOR_AR +0x4EFC RB3D_CONSTANT_COLOR_GB +0x4F04 ZB_ZSTENCILCNTL +0x4F08 ZB_STENCILREFMASK +0x4F14 ZB_ZTOP +0x4F18 ZB_ZCACHE_CTLSTAT +0x4F58 ZB_ZPASS_DATA +0x4F28 ZB_DEPTHCLEARVALUE +0x4FD4 ZB_STENCILREFMASK_BF diff --git a/sys/dev/drm2/radeon/rn50_reg_safe.h b/sys/dev/drm2/radeon/rn50_reg_safe.h new file mode 100644 index 00000000000..043d8a45bcc --- /dev/null +++ b/sys/dev/drm2/radeon/rn50_reg_safe.h @@ -0,0 +1,31 @@ +#include +__FBSDID("$FreeBSD$"); + +static const unsigned rn50_reg_safe_bm[102] = { + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0x17FF1FFF, 0xFFFFFFFC, 0xFFFFFFFF, 0xFF30FFBF, + 0xFFFFFFF8, 0xC3E6FFFF, 0xFFFFF6DF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, +}; diff --git a/sys/dev/drm2/radeon/rs100d.h b/sys/dev/drm2/radeon/rs100d.h new file mode 100644 index 00000000000..d90bb5157ce --- /dev/null +++ b/sys/dev/drm2/radeon/rs100d.h @@ -0,0 +1,43 @@ +/* + * Copyright 2008 Advanced Micro Devices, Inc. + * Copyright 2008 Red Hat Inc. + * Copyright 2009 Jerome Glisse. + * + * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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. + * + * Authors: Dave Airlie + * Alex Deucher + * Jerome Glisse + */ +#ifndef __RS100D_H__ +#define __RS100D_H__ + +#include +__FBSDID("$FreeBSD$"); + +/* Registers */ +#define R_00015C_NB_TOM 0x00015C +#define S_00015C_MC_FB_START(x) (((x) & 0xFFFF) << 0) +#define G_00015C_MC_FB_START(x) (((x) >> 0) & 0xFFFF) +#define C_00015C_MC_FB_START 0xFFFF0000 +#define S_00015C_MC_FB_TOP(x) (((x) & 0xFFFF) << 16) +#define G_00015C_MC_FB_TOP(x) (((x) >> 16) & 0xFFFF) +#define C_00015C_MC_FB_TOP 0x0000FFFF + +#endif diff --git a/sys/dev/drm2/radeon/rs400.c b/sys/dev/drm2/radeon/rs400.c new file mode 100644 index 00000000000..f9d638d013e --- /dev/null +++ b/sys/dev/drm2/radeon/rs400.c @@ -0,0 +1,568 @@ +/* + * Copyright 2008 Advanced Micro Devices, Inc. + * Copyright 2008 Red Hat Inc. + * Copyright 2009 Jerome Glisse. + * + * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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. + * + * Authors: Dave Airlie + * Alex Deucher + * Jerome Glisse + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include "radeon.h" +#include "radeon_asic.h" +#include "rs400d.h" + +/* This files gather functions specifics to : rs400,rs480 */ +static int rs400_debugfs_pcie_gart_info_init(struct radeon_device *rdev); + +void rs400_gart_adjust_size(struct radeon_device *rdev) +{ + /* Check gart size */ + switch (rdev->mc.gtt_size/(1024*1024)) { + case 32: + case 64: + case 128: + case 256: + case 512: + case 1024: + case 2048: + break; + default: + DRM_ERROR("Unable to use IGP GART size %uM\n", + (unsigned)(rdev->mc.gtt_size >> 20)); + DRM_ERROR("Valid GART size for IGP are 32M,64M,128M,256M,512M,1G,2G\n"); + DRM_ERROR("Forcing to 32M GART size\n"); + rdev->mc.gtt_size = 32 * 1024 * 1024; + return; + } +} + +void rs400_gart_tlb_flush(struct radeon_device *rdev) +{ + uint32_t tmp; + unsigned int timeout = rdev->usec_timeout; + + WREG32_MC(RS480_GART_CACHE_CNTRL, RS480_GART_CACHE_INVALIDATE); + do { + tmp = RREG32_MC(RS480_GART_CACHE_CNTRL); + if ((tmp & RS480_GART_CACHE_INVALIDATE) == 0) + break; + DRM_UDELAY(1); + timeout--; + } while (timeout > 0); + WREG32_MC(RS480_GART_CACHE_CNTRL, 0); +} + +int rs400_gart_init(struct radeon_device *rdev) +{ + int r; + + if (rdev->gart.ptr) { + DRM_ERROR("RS400 GART already initialized\n"); + return 0; + } + /* Check gart size */ + switch(rdev->mc.gtt_size / (1024 * 1024)) { + case 32: + case 64: + case 128: + case 256: + case 512: + case 1024: + case 2048: + break; + default: + return -EINVAL; + } + /* Initialize common gart structure */ + r = radeon_gart_init(rdev); + if (r) + return r; + if (rs400_debugfs_pcie_gart_info_init(rdev)) + DRM_ERROR("Failed to register debugfs file for RS400 GART !\n"); + rdev->gart.table_size = rdev->gart.num_gpu_pages * 4; + return radeon_gart_table_ram_alloc(rdev); +} + +int rs400_gart_enable(struct radeon_device *rdev) +{ + uint32_t size_reg; + uint32_t tmp; + + radeon_gart_restore(rdev); + tmp = RREG32_MC(RS690_AIC_CTRL_SCRATCH); + tmp |= RS690_DIS_OUT_OF_PCI_GART_ACCESS; + WREG32_MC(RS690_AIC_CTRL_SCRATCH, tmp); + /* Check gart size */ + switch(rdev->mc.gtt_size / (1024 * 1024)) { + case 32: + size_reg = RS480_VA_SIZE_32MB; + break; + case 64: + size_reg = RS480_VA_SIZE_64MB; + break; + case 128: + size_reg = RS480_VA_SIZE_128MB; + break; + case 256: + size_reg = RS480_VA_SIZE_256MB; + break; + case 512: + size_reg = RS480_VA_SIZE_512MB; + break; + case 1024: + size_reg = RS480_VA_SIZE_1GB; + break; + case 2048: + size_reg = RS480_VA_SIZE_2GB; + break; + default: + return -EINVAL; + } + /* It should be fine to program it to max value */ + if (rdev->family == CHIP_RS690 || (rdev->family == CHIP_RS740)) { + WREG32_MC(RS690_MCCFG_AGP_BASE, 0xFFFFFFFF); + WREG32_MC(RS690_MCCFG_AGP_BASE_2, 0); + } else { + WREG32(RADEON_AGP_BASE, 0xFFFFFFFF); + WREG32(RS480_AGP_BASE_2, 0); + } + tmp = REG_SET(RS690_MC_AGP_TOP, rdev->mc.gtt_end >> 16); + tmp |= REG_SET(RS690_MC_AGP_START, rdev->mc.gtt_start >> 16); + if ((rdev->family == CHIP_RS690) || (rdev->family == CHIP_RS740)) { + WREG32_MC(RS690_MCCFG_AGP_LOCATION, tmp); + tmp = RREG32(RADEON_BUS_CNTL) & ~RS600_BUS_MASTER_DIS; + WREG32(RADEON_BUS_CNTL, tmp); + } else { + WREG32(RADEON_MC_AGP_LOCATION, tmp); + tmp = RREG32(RADEON_BUS_CNTL) & ~RADEON_BUS_MASTER_DIS; + WREG32(RADEON_BUS_CNTL, tmp); + } + /* Table should be in 32bits address space so ignore bits above. */ + tmp = (u32)rdev->gart.table_addr & 0xfffff000; + tmp |= (upper_32_bits(rdev->gart.table_addr) & 0xff) << 4; + + WREG32_MC(RS480_GART_BASE, tmp); + /* TODO: more tweaking here */ + WREG32_MC(RS480_GART_FEATURE_ID, + (RS480_TLB_ENABLE | + RS480_GTW_LAC_EN | RS480_1LEVEL_GART)); + /* Disable snooping */ + WREG32_MC(RS480_AGP_MODE_CNTL, + (1 << RS480_REQ_TYPE_SNOOP_SHIFT) | RS480_REQ_TYPE_SNOOP_DIS); + /* Disable AGP mode */ + if ((rdev->family == CHIP_RS690) || (rdev->family == CHIP_RS740)) { + tmp = RREG32_MC(RS690_MC_NB_CNTL); + tmp &= ~(RS690_HIDE_MMCFG_BAR | + RS690_AGPMODE30 | + RS690_AGP30ENHANCED); + WREG32_MC(RS690_MC_NB_CNTL, tmp); + WREG32_MC(RS480_MC_MISC_CNTL, + (RS480_GART_INDEX_REG_EN | RS690_BLOCK_GFX_D3_EN)); + } else { + WREG32_MC(RS480_MC_MISC_CNTL, RS480_GART_INDEX_REG_EN); + } + /* Enable gart */ + WREG32_MC(RS480_AGP_ADDRESS_SPACE_SIZE, (RS480_GART_EN | size_reg)); + rs400_gart_tlb_flush(rdev); + DRM_INFO("PCIE GART of %uM enabled (table at 0x%016llX).\n", + (unsigned)(rdev->mc.gtt_size >> 20), + (unsigned long long)rdev->gart.table_addr); + rdev->gart.ready = true; + return 0; +} + +void rs400_gart_disable(struct radeon_device *rdev) +{ + uint32_t tmp; + + tmp = RREG32_MC(RS690_AIC_CTRL_SCRATCH); + tmp |= RS690_DIS_OUT_OF_PCI_GART_ACCESS; + WREG32_MC(RS690_AIC_CTRL_SCRATCH, tmp); + WREG32_MC(RS480_AGP_ADDRESS_SPACE_SIZE, 0); +} + +void rs400_gart_fini(struct radeon_device *rdev) +{ + radeon_gart_fini(rdev); + rs400_gart_disable(rdev); + radeon_gart_table_ram_free(rdev); +} + +#define RS400_PTE_WRITEABLE (1 << 2) +#define RS400_PTE_READABLE (1 << 3) + +int rs400_gart_set_page(struct radeon_device *rdev, int i, uint64_t addr) +{ + uint32_t entry; + u32 *gtt = rdev->gart.ptr; + + if (i < 0 || i > rdev->gart.num_gpu_pages) { + return -EINVAL; + } + + entry = (lower_32_bits(addr) & 0xfffff000) | + ((upper_32_bits(addr) & 0xff) << 4) | + RS400_PTE_WRITEABLE | RS400_PTE_READABLE; + entry = cpu_to_le32(entry); + gtt[i] = entry; + return 0; +} + +int rs400_mc_wait_for_idle(struct radeon_device *rdev) +{ + unsigned i; + uint32_t tmp; + + for (i = 0; i < rdev->usec_timeout; i++) { + /* read MC_STATUS */ + tmp = RREG32(RADEON_MC_STATUS); + if (tmp & RADEON_MC_IDLE) { + return 0; + } + DRM_UDELAY(1); + } + return -1; +} + +static void rs400_gpu_init(struct radeon_device *rdev) +{ + /* FIXME: is this correct ? */ + r420_pipes_init(rdev); + if (rs400_mc_wait_for_idle(rdev)) { + DRM_ERROR("rs400: Failed to wait MC idle while " + "programming pipes. Bad things might happen. %08x\n", RREG32(RADEON_MC_STATUS)); + } +} + +static void rs400_mc_init(struct radeon_device *rdev) +{ + u64 base; + + rs400_gart_adjust_size(rdev); + rdev->mc.igp_sideport_enabled = radeon_combios_sideport_present(rdev); + /* DDR for all card after R300 & IGP */ + rdev->mc.vram_is_ddr = true; + rdev->mc.vram_width = 128; + r100_vram_init_sizes(rdev); + base = (RREG32(RADEON_NB_TOM) & 0xffff) << 16; + radeon_vram_location(rdev, &rdev->mc, base); + rdev->mc.gtt_base_align = rdev->mc.gtt_size - 1; + radeon_gtt_location(rdev, &rdev->mc); + radeon_update_bandwidth_info(rdev); +} + +uint32_t rs400_mc_rreg(struct radeon_device *rdev, uint32_t reg) +{ + uint32_t r; + + WREG32(RS480_NB_MC_INDEX, reg & 0xff); + r = RREG32(RS480_NB_MC_DATA); + WREG32(RS480_NB_MC_INDEX, 0xff); + return r; +} + +void rs400_mc_wreg(struct radeon_device *rdev, uint32_t reg, uint32_t v) +{ + WREG32(RS480_NB_MC_INDEX, ((reg) & 0xff) | RS480_NB_MC_IND_WR_EN); + WREG32(RS480_NB_MC_DATA, (v)); + WREG32(RS480_NB_MC_INDEX, 0xff); +} + +#if defined(CONFIG_DEBUG_FS) +static int rs400_debugfs_gart_info(struct seq_file *m, void *data) +{ + struct drm_info_node *node = (struct drm_info_node *) m->private; + struct drm_device *dev = node->minor->dev; + struct radeon_device *rdev = dev->dev_private; + uint32_t tmp; + + tmp = RREG32(RADEON_HOST_PATH_CNTL); + seq_printf(m, "HOST_PATH_CNTL 0x%08x\n", tmp); + tmp = RREG32(RADEON_BUS_CNTL); + seq_printf(m, "BUS_CNTL 0x%08x\n", tmp); + tmp = RREG32_MC(RS690_AIC_CTRL_SCRATCH); + seq_printf(m, "AIC_CTRL_SCRATCH 0x%08x\n", tmp); + if (rdev->family == CHIP_RS690 || (rdev->family == CHIP_RS740)) { + tmp = RREG32_MC(RS690_MCCFG_AGP_BASE); + seq_printf(m, "MCCFG_AGP_BASE 0x%08x\n", tmp); + tmp = RREG32_MC(RS690_MCCFG_AGP_BASE_2); + seq_printf(m, "MCCFG_AGP_BASE_2 0x%08x\n", tmp); + tmp = RREG32_MC(RS690_MCCFG_AGP_LOCATION); + seq_printf(m, "MCCFG_AGP_LOCATION 0x%08x\n", tmp); + tmp = RREG32_MC(RS690_MCCFG_FB_LOCATION); + seq_printf(m, "MCCFG_FB_LOCATION 0x%08x\n", tmp); + tmp = RREG32(RS690_HDP_FB_LOCATION); + seq_printf(m, "HDP_FB_LOCATION 0x%08x\n", tmp); + } else { + tmp = RREG32(RADEON_AGP_BASE); + seq_printf(m, "AGP_BASE 0x%08x\n", tmp); + tmp = RREG32(RS480_AGP_BASE_2); + seq_printf(m, "AGP_BASE_2 0x%08x\n", tmp); + tmp = RREG32(RADEON_MC_AGP_LOCATION); + seq_printf(m, "MC_AGP_LOCATION 0x%08x\n", tmp); + } + tmp = RREG32_MC(RS480_GART_BASE); + seq_printf(m, "GART_BASE 0x%08x\n", tmp); + tmp = RREG32_MC(RS480_GART_FEATURE_ID); + seq_printf(m, "GART_FEATURE_ID 0x%08x\n", tmp); + tmp = RREG32_MC(RS480_AGP_MODE_CNTL); + seq_printf(m, "AGP_MODE_CONTROL 0x%08x\n", tmp); + tmp = RREG32_MC(RS480_MC_MISC_CNTL); + seq_printf(m, "MC_MISC_CNTL 0x%08x\n", tmp); + tmp = RREG32_MC(0x5F); + seq_printf(m, "MC_MISC_UMA_CNTL 0x%08x\n", tmp); + tmp = RREG32_MC(RS480_AGP_ADDRESS_SPACE_SIZE); + seq_printf(m, "AGP_ADDRESS_SPACE_SIZE 0x%08x\n", tmp); + tmp = RREG32_MC(RS480_GART_CACHE_CNTRL); + seq_printf(m, "GART_CACHE_CNTRL 0x%08x\n", tmp); + tmp = RREG32_MC(0x3B); + seq_printf(m, "MC_GART_ERROR_ADDRESS 0x%08x\n", tmp); + tmp = RREG32_MC(0x3C); + seq_printf(m, "MC_GART_ERROR_ADDRESS_HI 0x%08x\n", tmp); + tmp = RREG32_MC(0x30); + seq_printf(m, "GART_ERROR_0 0x%08x\n", tmp); + tmp = RREG32_MC(0x31); + seq_printf(m, "GART_ERROR_1 0x%08x\n", tmp); + tmp = RREG32_MC(0x32); + seq_printf(m, "GART_ERROR_2 0x%08x\n", tmp); + tmp = RREG32_MC(0x33); + seq_printf(m, "GART_ERROR_3 0x%08x\n", tmp); + tmp = RREG32_MC(0x34); + seq_printf(m, "GART_ERROR_4 0x%08x\n", tmp); + tmp = RREG32_MC(0x35); + seq_printf(m, "GART_ERROR_5 0x%08x\n", tmp); + tmp = RREG32_MC(0x36); + seq_printf(m, "GART_ERROR_6 0x%08x\n", tmp); + tmp = RREG32_MC(0x37); + seq_printf(m, "GART_ERROR_7 0x%08x\n", tmp); + return 0; +} + +static struct drm_info_list rs400_gart_info_list[] = { + {"rs400_gart_info", rs400_debugfs_gart_info, 0, NULL}, +}; +#endif + +static int rs400_debugfs_pcie_gart_info_init(struct radeon_device *rdev) +{ +#if defined(CONFIG_DEBUG_FS) + return radeon_debugfs_add_files(rdev, rs400_gart_info_list, 1); +#else + return 0; +#endif +} + +static void rs400_mc_program(struct radeon_device *rdev) +{ + struct r100_mc_save save; + + /* Stops all mc clients */ + r100_mc_stop(rdev, &save); + + /* Wait for mc idle */ + if (rs400_mc_wait_for_idle(rdev)) + dev_warn(rdev->dev, "rs400: Wait MC idle timeout before updating MC.\n"); + WREG32(R_000148_MC_FB_LOCATION, + S_000148_MC_FB_START(rdev->mc.vram_start >> 16) | + S_000148_MC_FB_TOP(rdev->mc.vram_end >> 16)); + + r100_mc_resume(rdev, &save); +} + +static int rs400_startup(struct radeon_device *rdev) +{ + int r; + + r100_set_common_regs(rdev); + + rs400_mc_program(rdev); + /* Resume clock */ + r300_clock_startup(rdev); + /* Initialize GPU configuration (# pipes, ...) */ + rs400_gpu_init(rdev); + r100_enable_bm(rdev); + /* Initialize GART (initialize after TTM so we can allocate + * memory through TTM but finalize after TTM) */ + r = rs400_gart_enable(rdev); + if (r) + return r; + + /* allocate wb buffer */ + r = radeon_wb_init(rdev); + if (r) + return r; + + r = radeon_fence_driver_start_ring(rdev, RADEON_RING_TYPE_GFX_INDEX); + if (r) { + dev_err(rdev->dev, "failed initializing CP fences (%d).\n", r); + return r; + } + + /* Enable IRQ */ + r100_irq_set(rdev); + rdev->config.r300.hdp_cntl = RREG32(RADEON_HOST_PATH_CNTL); + /* 1M ring buffer */ + r = r100_cp_init(rdev, 1024 * 1024); + if (r) { + dev_err(rdev->dev, "failed initializing CP (%d).\n", r); + return r; + } + + r = radeon_ib_pool_init(rdev); + if (r) { + dev_err(rdev->dev, "IB initialization failed (%d).\n", r); + return r; + } + + return 0; +} + +int rs400_resume(struct radeon_device *rdev) +{ + int r; + + /* Make sur GART are not working */ + rs400_gart_disable(rdev); + /* Resume clock before doing reset */ + r300_clock_startup(rdev); + /* setup MC before calling post tables */ + rs400_mc_program(rdev); + /* Reset gpu before posting otherwise ATOM will enter infinite loop */ + if (radeon_asic_reset(rdev)) { + dev_warn(rdev->dev, "GPU reset failed ! (0xE40=0x%08X, 0x7C0=0x%08X)\n", + RREG32(R_000E40_RBBM_STATUS), + RREG32(R_0007C0_CP_STAT)); + } + /* post */ + radeon_combios_asic_init(rdev->ddev); + /* Resume clock after posting */ + r300_clock_startup(rdev); + /* Initialize surface registers */ + radeon_surface_init(rdev); + + rdev->accel_working = true; + r = rs400_startup(rdev); + if (r) { + rdev->accel_working = false; + } + return r; +} + +int rs400_suspend(struct radeon_device *rdev) +{ + r100_cp_disable(rdev); + radeon_wb_disable(rdev); + r100_irq_disable(rdev); + rs400_gart_disable(rdev); + return 0; +} + +void rs400_fini(struct radeon_device *rdev) +{ + r100_cp_fini(rdev); + radeon_wb_fini(rdev); + radeon_ib_pool_fini(rdev); + radeon_gem_fini(rdev); + rs400_gart_fini(rdev); + radeon_irq_kms_fini(rdev); + radeon_fence_driver_fini(rdev); + radeon_bo_fini(rdev); + radeon_atombios_fini(rdev); + free(rdev->bios, DRM_MEM_DRIVER); + rdev->bios = NULL; +} + +int rs400_init(struct radeon_device *rdev) +{ + int r; + + /* Disable VGA */ + r100_vga_render_disable(rdev); + /* Initialize scratch registers */ + radeon_scratch_init(rdev); + /* Initialize surface registers */ + radeon_surface_init(rdev); + /* TODO: disable VGA need to use VGA request */ + /* restore some register to sane defaults */ + r100_restore_sanity(rdev); + /* BIOS*/ + if (!radeon_get_bios(rdev)) { + if (ASIC_IS_AVIVO(rdev)) + return -EINVAL; + } + if (rdev->is_atom_bios) { + dev_err(rdev->dev, "Expecting combios for RS400/RS480 GPU\n"); + return -EINVAL; + } else { + r = radeon_combios_init(rdev); + if (r) + return r; + } + /* Reset gpu before posting otherwise ATOM will enter infinite loop */ + if (radeon_asic_reset(rdev)) { + dev_warn(rdev->dev, + "GPU reset failed ! (0xE40=0x%08X, 0x7C0=0x%08X)\n", + RREG32(R_000E40_RBBM_STATUS), + RREG32(R_0007C0_CP_STAT)); + } + /* check if cards are posted or not */ + if (radeon_boot_test_post_card(rdev) == false) + return -EINVAL; + + /* Initialize clocks */ + radeon_get_clock_info(rdev->ddev); + /* initialize memory controller */ + rs400_mc_init(rdev); + /* Fence driver */ + r = radeon_fence_driver_init(rdev); + if (r) + return r; + r = radeon_irq_kms_init(rdev); + if (r) + return r; + /* Memory manager */ + r = radeon_bo_init(rdev); + if (r) + return r; + r = rs400_gart_init(rdev); + if (r) + return r; + r300_set_reg_safe(rdev); + + rdev->accel_working = true; + r = rs400_startup(rdev); + if (r) { + /* Somethings want wront with the accel init stop accel */ + dev_err(rdev->dev, "Disabling GPU acceleration\n"); + r100_cp_fini(rdev); + radeon_wb_fini(rdev); + radeon_ib_pool_fini(rdev); + rs400_gart_fini(rdev); + radeon_irq_kms_fini(rdev); + rdev->accel_working = false; + } + return 0; +} diff --git a/sys/dev/drm2/radeon/rs400d.h b/sys/dev/drm2/radeon/rs400d.h new file mode 100644 index 00000000000..7a01d202ec4 --- /dev/null +++ b/sys/dev/drm2/radeon/rs400d.h @@ -0,0 +1,163 @@ +/* + * Copyright 2008 Advanced Micro Devices, Inc. + * Copyright 2008 Red Hat Inc. + * Copyright 2009 Jerome Glisse. + * + * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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. + * + * Authors: Dave Airlie + * Alex Deucher + * Jerome Glisse + */ +#ifndef __RS400D_H__ +#define __RS400D_H__ + +#include +__FBSDID("$FreeBSD$"); + +/* Registers */ +#define R_000148_MC_FB_LOCATION 0x000148 +#define S_000148_MC_FB_START(x) (((x) & 0xFFFF) << 0) +#define G_000148_MC_FB_START(x) (((x) >> 0) & 0xFFFF) +#define C_000148_MC_FB_START 0xFFFF0000 +#define S_000148_MC_FB_TOP(x) (((x) & 0xFFFF) << 16) +#define G_000148_MC_FB_TOP(x) (((x) >> 16) & 0xFFFF) +#define C_000148_MC_FB_TOP 0x0000FFFF +#define R_00015C_NB_TOM 0x00015C +#define S_00015C_MC_FB_START(x) (((x) & 0xFFFF) << 0) +#define G_00015C_MC_FB_START(x) (((x) >> 0) & 0xFFFF) +#define C_00015C_MC_FB_START 0xFFFF0000 +#define S_00015C_MC_FB_TOP(x) (((x) & 0xFFFF) << 16) +#define G_00015C_MC_FB_TOP(x) (((x) >> 16) & 0xFFFF) +#define C_00015C_MC_FB_TOP 0x0000FFFF +#define R_0007C0_CP_STAT 0x0007C0 +#define S_0007C0_MRU_BUSY(x) (((x) & 0x1) << 0) +#define G_0007C0_MRU_BUSY(x) (((x) >> 0) & 0x1) +#define C_0007C0_MRU_BUSY 0xFFFFFFFE +#define S_0007C0_MWU_BUSY(x) (((x) & 0x1) << 1) +#define G_0007C0_MWU_BUSY(x) (((x) >> 1) & 0x1) +#define C_0007C0_MWU_BUSY 0xFFFFFFFD +#define S_0007C0_RSIU_BUSY(x) (((x) & 0x1) << 2) +#define G_0007C0_RSIU_BUSY(x) (((x) >> 2) & 0x1) +#define C_0007C0_RSIU_BUSY 0xFFFFFFFB +#define S_0007C0_RCIU_BUSY(x) (((x) & 0x1) << 3) +#define G_0007C0_RCIU_BUSY(x) (((x) >> 3) & 0x1) +#define C_0007C0_RCIU_BUSY 0xFFFFFFF7 +#define S_0007C0_CSF_PRIMARY_BUSY(x) (((x) & 0x1) << 9) +#define G_0007C0_CSF_PRIMARY_BUSY(x) (((x) >> 9) & 0x1) +#define C_0007C0_CSF_PRIMARY_BUSY 0xFFFFFDFF +#define S_0007C0_CSF_INDIRECT_BUSY(x) (((x) & 0x1) << 10) +#define G_0007C0_CSF_INDIRECT_BUSY(x) (((x) >> 10) & 0x1) +#define C_0007C0_CSF_INDIRECT_BUSY 0xFFFFFBFF +#define S_0007C0_CSQ_PRIMARY_BUSY(x) (((x) & 0x1) << 11) +#define G_0007C0_CSQ_PRIMARY_BUSY(x) (((x) >> 11) & 0x1) +#define C_0007C0_CSQ_PRIMARY_BUSY 0xFFFFF7FF +#define S_0007C0_CSQ_INDIRECT_BUSY(x) (((x) & 0x1) << 12) +#define G_0007C0_CSQ_INDIRECT_BUSY(x) (((x) >> 12) & 0x1) +#define C_0007C0_CSQ_INDIRECT_BUSY 0xFFFFEFFF +#define S_0007C0_CSI_BUSY(x) (((x) & 0x1) << 13) +#define G_0007C0_CSI_BUSY(x) (((x) >> 13) & 0x1) +#define C_0007C0_CSI_BUSY 0xFFFFDFFF +#define S_0007C0_CSF_INDIRECT2_BUSY(x) (((x) & 0x1) << 14) +#define G_0007C0_CSF_INDIRECT2_BUSY(x) (((x) >> 14) & 0x1) +#define C_0007C0_CSF_INDIRECT2_BUSY 0xFFFFBFFF +#define S_0007C0_CSQ_INDIRECT2_BUSY(x) (((x) & 0x1) << 15) +#define G_0007C0_CSQ_INDIRECT2_BUSY(x) (((x) >> 15) & 0x1) +#define C_0007C0_CSQ_INDIRECT2_BUSY 0xFFFF7FFF +#define S_0007C0_GUIDMA_BUSY(x) (((x) & 0x1) << 28) +#define G_0007C0_GUIDMA_BUSY(x) (((x) >> 28) & 0x1) +#define C_0007C0_GUIDMA_BUSY 0xEFFFFFFF +#define S_0007C0_VIDDMA_BUSY(x) (((x) & 0x1) << 29) +#define G_0007C0_VIDDMA_BUSY(x) (((x) >> 29) & 0x1) +#define C_0007C0_VIDDMA_BUSY 0xDFFFFFFF +#define S_0007C0_CMDSTRM_BUSY(x) (((x) & 0x1) << 30) +#define G_0007C0_CMDSTRM_BUSY(x) (((x) >> 30) & 0x1) +#define C_0007C0_CMDSTRM_BUSY 0xBFFFFFFF +#define S_0007C0_CP_BUSY(x) (((x) & 0x1) << 31) +#define G_0007C0_CP_BUSY(x) (((x) >> 31) & 0x1) +#define C_0007C0_CP_BUSY 0x7FFFFFFF +#define R_000E40_RBBM_STATUS 0x000E40 +#define S_000E40_CMDFIFO_AVAIL(x) (((x) & 0x7F) << 0) +#define G_000E40_CMDFIFO_AVAIL(x) (((x) >> 0) & 0x7F) +#define C_000E40_CMDFIFO_AVAIL 0xFFFFFF80 +#define S_000E40_HIRQ_ON_RBB(x) (((x) & 0x1) << 8) +#define G_000E40_HIRQ_ON_RBB(x) (((x) >> 8) & 0x1) +#define C_000E40_HIRQ_ON_RBB 0xFFFFFEFF +#define S_000E40_CPRQ_ON_RBB(x) (((x) & 0x1) << 9) +#define G_000E40_CPRQ_ON_RBB(x) (((x) >> 9) & 0x1) +#define C_000E40_CPRQ_ON_RBB 0xFFFFFDFF +#define S_000E40_CFRQ_ON_RBB(x) (((x) & 0x1) << 10) +#define G_000E40_CFRQ_ON_RBB(x) (((x) >> 10) & 0x1) +#define C_000E40_CFRQ_ON_RBB 0xFFFFFBFF +#define S_000E40_HIRQ_IN_RTBUF(x) (((x) & 0x1) << 11) +#define G_000E40_HIRQ_IN_RTBUF(x) (((x) >> 11) & 0x1) +#define C_000E40_HIRQ_IN_RTBUF 0xFFFFF7FF +#define S_000E40_CPRQ_IN_RTBUF(x) (((x) & 0x1) << 12) +#define G_000E40_CPRQ_IN_RTBUF(x) (((x) >> 12) & 0x1) +#define C_000E40_CPRQ_IN_RTBUF 0xFFFFEFFF +#define S_000E40_CFRQ_IN_RTBUF(x) (((x) & 0x1) << 13) +#define G_000E40_CFRQ_IN_RTBUF(x) (((x) >> 13) & 0x1) +#define C_000E40_CFRQ_IN_RTBUF 0xFFFFDFFF +#define S_000E40_CF_PIPE_BUSY(x) (((x) & 0x1) << 14) +#define G_000E40_CF_PIPE_BUSY(x) (((x) >> 14) & 0x1) +#define C_000E40_CF_PIPE_BUSY 0xFFFFBFFF +#define S_000E40_ENG_EV_BUSY(x) (((x) & 0x1) << 15) +#define G_000E40_ENG_EV_BUSY(x) (((x) >> 15) & 0x1) +#define C_000E40_ENG_EV_BUSY 0xFFFF7FFF +#define S_000E40_CP_CMDSTRM_BUSY(x) (((x) & 0x1) << 16) +#define G_000E40_CP_CMDSTRM_BUSY(x) (((x) >> 16) & 0x1) +#define C_000E40_CP_CMDSTRM_BUSY 0xFFFEFFFF +#define S_000E40_E2_BUSY(x) (((x) & 0x1) << 17) +#define G_000E40_E2_BUSY(x) (((x) >> 17) & 0x1) +#define C_000E40_E2_BUSY 0xFFFDFFFF +#define S_000E40_RB2D_BUSY(x) (((x) & 0x1) << 18) +#define G_000E40_RB2D_BUSY(x) (((x) >> 18) & 0x1) +#define C_000E40_RB2D_BUSY 0xFFFBFFFF +#define S_000E40_RB3D_BUSY(x) (((x) & 0x1) << 19) +#define G_000E40_RB3D_BUSY(x) (((x) >> 19) & 0x1) +#define C_000E40_RB3D_BUSY 0xFFF7FFFF +#define S_000E40_VAP_BUSY(x) (((x) & 0x1) << 20) +#define G_000E40_VAP_BUSY(x) (((x) >> 20) & 0x1) +#define C_000E40_VAP_BUSY 0xFFEFFFFF +#define S_000E40_RE_BUSY(x) (((x) & 0x1) << 21) +#define G_000E40_RE_BUSY(x) (((x) >> 21) & 0x1) +#define C_000E40_RE_BUSY 0xFFDFFFFF +#define S_000E40_TAM_BUSY(x) (((x) & 0x1) << 22) +#define G_000E40_TAM_BUSY(x) (((x) >> 22) & 0x1) +#define C_000E40_TAM_BUSY 0xFFBFFFFF +#define S_000E40_TDM_BUSY(x) (((x) & 0x1) << 23) +#define G_000E40_TDM_BUSY(x) (((x) >> 23) & 0x1) +#define C_000E40_TDM_BUSY 0xFF7FFFFF +#define S_000E40_PB_BUSY(x) (((x) & 0x1) << 24) +#define G_000E40_PB_BUSY(x) (((x) >> 24) & 0x1) +#define C_000E40_PB_BUSY 0xFEFFFFFF +#define S_000E40_TIM_BUSY(x) (((x) & 0x1) << 25) +#define G_000E40_TIM_BUSY(x) (((x) >> 25) & 0x1) +#define C_000E40_TIM_BUSY 0xFDFFFFFF +#define S_000E40_GA_BUSY(x) (((x) & 0x1) << 26) +#define G_000E40_GA_BUSY(x) (((x) >> 26) & 0x1) +#define C_000E40_GA_BUSY 0xFBFFFFFF +#define S_000E40_CBA2D_BUSY(x) (((x) & 0x1) << 27) +#define G_000E40_CBA2D_BUSY(x) (((x) >> 27) & 0x1) +#define C_000E40_CBA2D_BUSY 0xF7FFFFFF +#define S_000E40_GUI_ACTIVE(x) (((x) & 0x1) << 31) +#define G_000E40_GUI_ACTIVE(x) (((x) >> 31) & 0x1) +#define C_000E40_GUI_ACTIVE 0x7FFFFFFF + +#endif diff --git a/sys/dev/drm2/radeon/rs600.c b/sys/dev/drm2/radeon/rs600.c new file mode 100644 index 00000000000..11edbdfc5df --- /dev/null +++ b/sys/dev/drm2/radeon/rs600.c @@ -0,0 +1,1042 @@ +/* + * Copyright 2008 Advanced Micro Devices, Inc. + * Copyright 2008 Red Hat Inc. + * Copyright 2009 Jerome Glisse. + * + * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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. + * + * Authors: Dave Airlie + * Alex Deucher + * Jerome Glisse + */ +/* RS600 / Radeon X1250/X1270 integrated GPU + * + * This file gather function specific to RS600 which is the IGP of + * the X1250/X1270 family supporting intel CPU (while RS690/RS740 + * is the X1250/X1270 supporting AMD CPU). The display engine are + * the avivo one, bios is an atombios, 3D block are the one of the + * R4XX family. The GART is different from the RS400 one and is very + * close to the one of the R600 family (R600 likely being an evolution + * of the RS600 GART block). + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include "radeon.h" +#include "radeon_asic.h" +#include "atom.h" +#include "rs600d.h" + +#include "rs600_reg_safe.h" + +static void rs600_gpu_init(struct radeon_device *rdev); + +static const u32 crtc_offsets[2] = +{ + 0, + AVIVO_D2CRTC_H_TOTAL - AVIVO_D1CRTC_H_TOTAL +}; + +void avivo_wait_for_vblank(struct radeon_device *rdev, int crtc) +{ + int i; + + if (crtc >= rdev->num_crtc) + return; + + if (RREG32(AVIVO_D1CRTC_CONTROL + crtc_offsets[crtc]) & AVIVO_CRTC_EN) { + for (i = 0; i < rdev->usec_timeout; i++) { + if (!(RREG32(AVIVO_D1CRTC_STATUS + crtc_offsets[crtc]) & AVIVO_D1CRTC_V_BLANK)) + break; + DRM_UDELAY(1); + } + for (i = 0; i < rdev->usec_timeout; i++) { + if (RREG32(AVIVO_D1CRTC_STATUS + crtc_offsets[crtc]) & AVIVO_D1CRTC_V_BLANK) + break; + DRM_UDELAY(1); + } + } +} + +void rs600_pre_page_flip(struct radeon_device *rdev, int crtc) +{ + /* enable the pflip int */ + radeon_irq_kms_pflip_irq_get(rdev, crtc); +} + +void rs600_post_page_flip(struct radeon_device *rdev, int crtc) +{ + /* disable the pflip int */ + radeon_irq_kms_pflip_irq_put(rdev, crtc); +} + +u32 rs600_page_flip(struct radeon_device *rdev, int crtc_id, u64 crtc_base) +{ + struct radeon_crtc *radeon_crtc = rdev->mode_info.crtcs[crtc_id]; + u32 tmp = RREG32(AVIVO_D1GRPH_UPDATE + radeon_crtc->crtc_offset); + int i; + + /* Lock the graphics update lock */ + tmp |= AVIVO_D1GRPH_UPDATE_LOCK; + WREG32(AVIVO_D1GRPH_UPDATE + radeon_crtc->crtc_offset, tmp); + + /* update the scanout addresses */ + WREG32(AVIVO_D1GRPH_SECONDARY_SURFACE_ADDRESS + radeon_crtc->crtc_offset, + (u32)crtc_base); + WREG32(AVIVO_D1GRPH_PRIMARY_SURFACE_ADDRESS + radeon_crtc->crtc_offset, + (u32)crtc_base); + + /* Wait for update_pending to go high. */ + for (i = 0; i < rdev->usec_timeout; i++) { + if (RREG32(AVIVO_D1GRPH_UPDATE + radeon_crtc->crtc_offset) & AVIVO_D1GRPH_SURFACE_UPDATE_PENDING) + break; + DRM_UDELAY(1); + } + DRM_DEBUG("Update pending now high. Unlocking vupdate_lock.\n"); + + /* Unlock the lock, so double-buffering can take place inside vblank */ + tmp &= ~AVIVO_D1GRPH_UPDATE_LOCK; + WREG32(AVIVO_D1GRPH_UPDATE + radeon_crtc->crtc_offset, tmp); + + /* Return current update_pending status: */ + return RREG32(AVIVO_D1GRPH_UPDATE + radeon_crtc->crtc_offset) & AVIVO_D1GRPH_SURFACE_UPDATE_PENDING; +} + +void rs600_pm_misc(struct radeon_device *rdev) +{ + int requested_index = rdev->pm.requested_power_state_index; + struct radeon_power_state *ps = &rdev->pm.power_state[requested_index]; + struct radeon_voltage *voltage = &ps->clock_info[0].voltage; + u32 tmp, dyn_pwrmgt_sclk_length, dyn_sclk_vol_cntl; + u32 hdp_dyn_cntl, /*mc_host_dyn_cntl,*/ dyn_backbias_cntl; + + if ((voltage->type == VOLTAGE_GPIO) && (voltage->gpio.valid)) { + if (ps->misc & ATOM_PM_MISCINFO_VOLTAGE_DROP_SUPPORT) { + tmp = RREG32(voltage->gpio.reg); + if (voltage->active_high) + tmp |= voltage->gpio.mask; + else + tmp &= ~(voltage->gpio.mask); + WREG32(voltage->gpio.reg, tmp); + if (voltage->delay) + DRM_UDELAY(voltage->delay); + } else { + tmp = RREG32(voltage->gpio.reg); + if (voltage->active_high) + tmp &= ~voltage->gpio.mask; + else + tmp |= voltage->gpio.mask; + WREG32(voltage->gpio.reg, tmp); + if (voltage->delay) + DRM_UDELAY(voltage->delay); + } + } else if (voltage->type == VOLTAGE_VDDC) + radeon_atom_set_voltage(rdev, voltage->vddc_id, SET_VOLTAGE_TYPE_ASIC_VDDC); + + dyn_pwrmgt_sclk_length = RREG32_PLL(DYN_PWRMGT_SCLK_LENGTH); + dyn_pwrmgt_sclk_length &= ~REDUCED_POWER_SCLK_HILEN(0xf); + dyn_pwrmgt_sclk_length &= ~REDUCED_POWER_SCLK_LOLEN(0xf); + if (ps->misc & ATOM_PM_MISCINFO_ASIC_REDUCED_SPEED_SCLK_EN) { + if (ps->misc & ATOM_PM_MISCINFO_DYNAMIC_CLOCK_DIVIDER_BY_2) { + dyn_pwrmgt_sclk_length |= REDUCED_POWER_SCLK_HILEN(2); + dyn_pwrmgt_sclk_length |= REDUCED_POWER_SCLK_LOLEN(2); + } else if (ps->misc & ATOM_PM_MISCINFO_DYNAMIC_CLOCK_DIVIDER_BY_4) { + dyn_pwrmgt_sclk_length |= REDUCED_POWER_SCLK_HILEN(4); + dyn_pwrmgt_sclk_length |= REDUCED_POWER_SCLK_LOLEN(4); + } + } else { + dyn_pwrmgt_sclk_length |= REDUCED_POWER_SCLK_HILEN(1); + dyn_pwrmgt_sclk_length |= REDUCED_POWER_SCLK_LOLEN(1); + } + WREG32_PLL(DYN_PWRMGT_SCLK_LENGTH, dyn_pwrmgt_sclk_length); + + dyn_sclk_vol_cntl = RREG32_PLL(DYN_SCLK_VOL_CNTL); + if (ps->misc & ATOM_PM_MISCINFO_ASIC_DYNAMIC_VOLTAGE_EN) { + dyn_sclk_vol_cntl |= IO_CG_VOLTAGE_DROP; + if (voltage->delay) { + dyn_sclk_vol_cntl |= VOLTAGE_DROP_SYNC; + dyn_sclk_vol_cntl |= VOLTAGE_DELAY_SEL(voltage->delay); + } else + dyn_sclk_vol_cntl &= ~VOLTAGE_DROP_SYNC; + } else + dyn_sclk_vol_cntl &= ~IO_CG_VOLTAGE_DROP; + WREG32_PLL(DYN_SCLK_VOL_CNTL, dyn_sclk_vol_cntl); + + hdp_dyn_cntl = RREG32_PLL(HDP_DYN_CNTL); + if (ps->misc & ATOM_PM_MISCINFO_DYNAMIC_HDP_BLOCK_EN) + hdp_dyn_cntl &= ~HDP_FORCEON; + else + hdp_dyn_cntl |= HDP_FORCEON; + WREG32_PLL(HDP_DYN_CNTL, hdp_dyn_cntl); +#if 0 + /* mc_host_dyn seems to cause hangs from time to time */ + mc_host_dyn_cntl = RREG32_PLL(MC_HOST_DYN_CNTL); + if (ps->misc & ATOM_PM_MISCINFO_DYNAMIC_MC_HOST_BLOCK_EN) + mc_host_dyn_cntl &= ~MC_HOST_FORCEON; + else + mc_host_dyn_cntl |= MC_HOST_FORCEON; + WREG32_PLL(MC_HOST_DYN_CNTL, mc_host_dyn_cntl); +#endif + dyn_backbias_cntl = RREG32_PLL(DYN_BACKBIAS_CNTL); + if (ps->misc & ATOM_PM_MISCINFO2_DYNAMIC_BACK_BIAS_EN) + dyn_backbias_cntl |= IO_CG_BACKBIAS_EN; + else + dyn_backbias_cntl &= ~IO_CG_BACKBIAS_EN; + WREG32_PLL(DYN_BACKBIAS_CNTL, dyn_backbias_cntl); + + /* set pcie lanes */ + if ((rdev->flags & RADEON_IS_PCIE) && + !(rdev->flags & RADEON_IS_IGP) && + rdev->asic->pm.set_pcie_lanes && + (ps->pcie_lanes != + rdev->pm.power_state[rdev->pm.current_power_state_index].pcie_lanes)) { + radeon_set_pcie_lanes(rdev, + ps->pcie_lanes); + DRM_DEBUG("Setting: p: %d\n", ps->pcie_lanes); + } +} + +void rs600_pm_prepare(struct radeon_device *rdev) +{ + struct drm_device *ddev = rdev->ddev; + struct drm_crtc *crtc; + struct radeon_crtc *radeon_crtc; + u32 tmp; + + /* disable any active CRTCs */ + list_for_each_entry(crtc, &ddev->mode_config.crtc_list, head) { + radeon_crtc = to_radeon_crtc(crtc); + if (radeon_crtc->enabled) { + tmp = RREG32(AVIVO_D1CRTC_CONTROL + radeon_crtc->crtc_offset); + tmp |= AVIVO_CRTC_DISP_READ_REQUEST_DISABLE; + WREG32(AVIVO_D1CRTC_CONTROL + radeon_crtc->crtc_offset, tmp); + } + } +} + +void rs600_pm_finish(struct radeon_device *rdev) +{ + struct drm_device *ddev = rdev->ddev; + struct drm_crtc *crtc; + struct radeon_crtc *radeon_crtc; + u32 tmp; + + /* enable any active CRTCs */ + list_for_each_entry(crtc, &ddev->mode_config.crtc_list, head) { + radeon_crtc = to_radeon_crtc(crtc); + if (radeon_crtc->enabled) { + tmp = RREG32(AVIVO_D1CRTC_CONTROL + radeon_crtc->crtc_offset); + tmp &= ~AVIVO_CRTC_DISP_READ_REQUEST_DISABLE; + WREG32(AVIVO_D1CRTC_CONTROL + radeon_crtc->crtc_offset, tmp); + } + } +} + +/* hpd for digital panel detect/disconnect */ +bool rs600_hpd_sense(struct radeon_device *rdev, enum radeon_hpd_id hpd) +{ + u32 tmp; + bool connected = false; + + switch (hpd) { + case RADEON_HPD_1: + tmp = RREG32(R_007D04_DC_HOT_PLUG_DETECT1_INT_STATUS); + if (G_007D04_DC_HOT_PLUG_DETECT1_SENSE(tmp)) + connected = true; + break; + case RADEON_HPD_2: + tmp = RREG32(R_007D14_DC_HOT_PLUG_DETECT2_INT_STATUS); + if (G_007D14_DC_HOT_PLUG_DETECT2_SENSE(tmp)) + connected = true; + break; + default: + break; + } + return connected; +} + +void rs600_hpd_set_polarity(struct radeon_device *rdev, + enum radeon_hpd_id hpd) +{ + u32 tmp; + bool connected = rs600_hpd_sense(rdev, hpd); + + switch (hpd) { + case RADEON_HPD_1: + tmp = RREG32(R_007D08_DC_HOT_PLUG_DETECT1_INT_CONTROL); + if (connected) + tmp &= ~S_007D08_DC_HOT_PLUG_DETECT1_INT_POLARITY(1); + else + tmp |= S_007D08_DC_HOT_PLUG_DETECT1_INT_POLARITY(1); + WREG32(R_007D08_DC_HOT_PLUG_DETECT1_INT_CONTROL, tmp); + break; + case RADEON_HPD_2: + tmp = RREG32(R_007D18_DC_HOT_PLUG_DETECT2_INT_CONTROL); + if (connected) + tmp &= ~S_007D18_DC_HOT_PLUG_DETECT2_INT_POLARITY(1); + else + tmp |= S_007D18_DC_HOT_PLUG_DETECT2_INT_POLARITY(1); + WREG32(R_007D18_DC_HOT_PLUG_DETECT2_INT_CONTROL, tmp); + break; + default: + break; + } +} + +void rs600_hpd_init(struct radeon_device *rdev) +{ + struct drm_device *dev = rdev->ddev; + struct drm_connector *connector; + unsigned enable = 0; + + list_for_each_entry(connector, &dev->mode_config.connector_list, head) { + struct radeon_connector *radeon_connector = to_radeon_connector(connector); + switch (radeon_connector->hpd.hpd) { + case RADEON_HPD_1: + WREG32(R_007D00_DC_HOT_PLUG_DETECT1_CONTROL, + S_007D00_DC_HOT_PLUG_DETECT1_EN(1)); + break; + case RADEON_HPD_2: + WREG32(R_007D10_DC_HOT_PLUG_DETECT2_CONTROL, + S_007D10_DC_HOT_PLUG_DETECT2_EN(1)); + break; + default: + break; + } + enable |= 1 << radeon_connector->hpd.hpd; + radeon_hpd_set_polarity(rdev, radeon_connector->hpd.hpd); + } + radeon_irq_kms_enable_hpd(rdev, enable); +} + +void rs600_hpd_fini(struct radeon_device *rdev) +{ + struct drm_device *dev = rdev->ddev; + struct drm_connector *connector; + unsigned disable = 0; + + list_for_each_entry(connector, &dev->mode_config.connector_list, head) { + struct radeon_connector *radeon_connector = to_radeon_connector(connector); + switch (radeon_connector->hpd.hpd) { + case RADEON_HPD_1: + WREG32(R_007D00_DC_HOT_PLUG_DETECT1_CONTROL, + S_007D00_DC_HOT_PLUG_DETECT1_EN(0)); + break; + case RADEON_HPD_2: + WREG32(R_007D10_DC_HOT_PLUG_DETECT2_CONTROL, + S_007D10_DC_HOT_PLUG_DETECT2_EN(0)); + break; + default: + break; + } + disable |= 1 << radeon_connector->hpd.hpd; + } + radeon_irq_kms_disable_hpd(rdev, disable); +} + +int rs600_asic_reset(struct radeon_device *rdev) +{ + struct rv515_mc_save save; + u32 status, tmp; + int ret = 0; + + status = RREG32(R_000E40_RBBM_STATUS); + if (!G_000E40_GUI_ACTIVE(status)) { + return 0; + } + /* Stops all mc clients */ + rv515_mc_stop(rdev, &save); + status = RREG32(R_000E40_RBBM_STATUS); + dev_info(rdev->dev, "(%s:%d) RBBM_STATUS=0x%08X\n", __func__, __LINE__, status); + /* stop CP */ + WREG32(RADEON_CP_CSQ_CNTL, 0); + tmp = RREG32(RADEON_CP_RB_CNTL); + WREG32(RADEON_CP_RB_CNTL, tmp | RADEON_RB_RPTR_WR_ENA); + WREG32(RADEON_CP_RB_RPTR_WR, 0); + WREG32(RADEON_CP_RB_WPTR, 0); + WREG32(RADEON_CP_RB_CNTL, tmp); + pci_save_state(device_get_parent(rdev->dev)); + /* disable bus mastering */ + pci_disable_busmaster(rdev->dev); + DRM_MDELAY(1); + /* reset GA+VAP */ + WREG32(R_0000F0_RBBM_SOFT_RESET, S_0000F0_SOFT_RESET_VAP(1) | + S_0000F0_SOFT_RESET_GA(1)); + RREG32(R_0000F0_RBBM_SOFT_RESET); + DRM_MDELAY(500); + WREG32(R_0000F0_RBBM_SOFT_RESET, 0); + DRM_MDELAY(1); + status = RREG32(R_000E40_RBBM_STATUS); + dev_info(rdev->dev, "(%s:%d) RBBM_STATUS=0x%08X\n", __func__, __LINE__, status); + /* reset CP */ + WREG32(R_0000F0_RBBM_SOFT_RESET, S_0000F0_SOFT_RESET_CP(1)); + RREG32(R_0000F0_RBBM_SOFT_RESET); + DRM_MDELAY(500); + WREG32(R_0000F0_RBBM_SOFT_RESET, 0); + DRM_MDELAY(1); + status = RREG32(R_000E40_RBBM_STATUS); + dev_info(rdev->dev, "(%s:%d) RBBM_STATUS=0x%08X\n", __func__, __LINE__, status); + /* reset MC */ + WREG32(R_0000F0_RBBM_SOFT_RESET, S_0000F0_SOFT_RESET_MC(1)); + RREG32(R_0000F0_RBBM_SOFT_RESET); + DRM_MDELAY(500); + WREG32(R_0000F0_RBBM_SOFT_RESET, 0); + DRM_MDELAY(1); + status = RREG32(R_000E40_RBBM_STATUS); + dev_info(rdev->dev, "(%s:%d) RBBM_STATUS=0x%08X\n", __func__, __LINE__, status); + /* restore PCI & busmastering */ + pci_restore_state(device_get_parent(rdev->dev)); + /* Check if GPU is idle */ + if (G_000E40_GA_BUSY(status) || G_000E40_VAP_BUSY(status)) { + dev_err(rdev->dev, "failed to reset GPU\n"); + ret = -1; + } else + dev_info(rdev->dev, "GPU reset succeed\n"); + rv515_mc_resume(rdev, &save); + return ret; +} + +/* + * GART. + */ +void rs600_gart_tlb_flush(struct radeon_device *rdev) +{ + uint32_t tmp; + + tmp = RREG32_MC(R_000100_MC_PT0_CNTL); + tmp &= C_000100_INVALIDATE_ALL_L1_TLBS & C_000100_INVALIDATE_L2_CACHE; + WREG32_MC(R_000100_MC_PT0_CNTL, tmp); + + tmp = RREG32_MC(R_000100_MC_PT0_CNTL); + tmp |= S_000100_INVALIDATE_ALL_L1_TLBS(1) | S_000100_INVALIDATE_L2_CACHE(1); + WREG32_MC(R_000100_MC_PT0_CNTL, tmp); + + tmp = RREG32_MC(R_000100_MC_PT0_CNTL); + tmp &= C_000100_INVALIDATE_ALL_L1_TLBS & C_000100_INVALIDATE_L2_CACHE; + WREG32_MC(R_000100_MC_PT0_CNTL, tmp); + tmp = RREG32_MC(R_000100_MC_PT0_CNTL); +} + +static int rs600_gart_init(struct radeon_device *rdev) +{ + int r; + + if (rdev->gart.robj) { + DRM_ERROR("RS600 GART already initialized\n"); + return 0; + } + /* Initialize common gart structure */ + r = radeon_gart_init(rdev); + if (r) { + return r; + } + rdev->gart.table_size = rdev->gart.num_gpu_pages * 8; + return radeon_gart_table_vram_alloc(rdev); +} + +static int rs600_gart_enable(struct radeon_device *rdev) +{ + u32 tmp; + int r, i; + + if (rdev->gart.robj == NULL) { + dev_err(rdev->dev, "No VRAM object for PCIE GART.\n"); + return -EINVAL; + } + r = radeon_gart_table_vram_pin(rdev); + if (r) + return r; + radeon_gart_restore(rdev); + /* Enable bus master */ + tmp = RREG32(RADEON_BUS_CNTL) & ~RS600_BUS_MASTER_DIS; + WREG32(RADEON_BUS_CNTL, tmp); + /* FIXME: setup default page */ + WREG32_MC(R_000100_MC_PT0_CNTL, + (S_000100_EFFECTIVE_L2_CACHE_SIZE(6) | + S_000100_EFFECTIVE_L2_QUEUE_SIZE(6))); + + for (i = 0; i < 19; i++) { + WREG32_MC(R_00016C_MC_PT0_CLIENT0_CNTL + i, + S_00016C_ENABLE_TRANSLATION_MODE_OVERRIDE(1) | + S_00016C_SYSTEM_ACCESS_MODE_MASK( + V_00016C_SYSTEM_ACCESS_MODE_NOT_IN_SYS) | + S_00016C_SYSTEM_APERTURE_UNMAPPED_ACCESS( + V_00016C_SYSTEM_APERTURE_UNMAPPED_PASSTHROUGH) | + S_00016C_EFFECTIVE_L1_CACHE_SIZE(3) | + S_00016C_ENABLE_FRAGMENT_PROCESSING(1) | + S_00016C_EFFECTIVE_L1_QUEUE_SIZE(3)); + } + /* enable first context */ + WREG32_MC(R_000102_MC_PT0_CONTEXT0_CNTL, + S_000102_ENABLE_PAGE_TABLE(1) | + S_000102_PAGE_TABLE_DEPTH(V_000102_PAGE_TABLE_FLAT)); + + /* disable all other contexts */ + for (i = 1; i < 8; i++) + WREG32_MC(R_000102_MC_PT0_CONTEXT0_CNTL + i, 0); + + /* setup the page table */ + WREG32_MC(R_00012C_MC_PT0_CONTEXT0_FLAT_BASE_ADDR, + rdev->gart.table_addr); + WREG32_MC(R_00013C_MC_PT0_CONTEXT0_FLAT_START_ADDR, rdev->mc.gtt_start); + WREG32_MC(R_00014C_MC_PT0_CONTEXT0_FLAT_END_ADDR, rdev->mc.gtt_end); + WREG32_MC(R_00011C_MC_PT0_CONTEXT0_DEFAULT_READ_ADDR, 0); + + /* System context maps to VRAM space */ + WREG32_MC(R_000112_MC_PT0_SYSTEM_APERTURE_LOW_ADDR, rdev->mc.vram_start); + WREG32_MC(R_000114_MC_PT0_SYSTEM_APERTURE_HIGH_ADDR, rdev->mc.vram_end); + + /* enable page tables */ + tmp = RREG32_MC(R_000100_MC_PT0_CNTL); + WREG32_MC(R_000100_MC_PT0_CNTL, (tmp | S_000100_ENABLE_PT(1))); + tmp = RREG32_MC(R_000009_MC_CNTL1); + WREG32_MC(R_000009_MC_CNTL1, (tmp | S_000009_ENABLE_PAGE_TABLES(1))); + rs600_gart_tlb_flush(rdev); + DRM_INFO("PCIE GART of %uM enabled (table at 0x%016llX).\n", + (unsigned)(rdev->mc.gtt_size >> 20), + (unsigned long long)rdev->gart.table_addr); + rdev->gart.ready = true; + return 0; +} + +static void rs600_gart_disable(struct radeon_device *rdev) +{ + u32 tmp; + + /* FIXME: disable out of gart access */ + WREG32_MC(R_000100_MC_PT0_CNTL, 0); + tmp = RREG32_MC(R_000009_MC_CNTL1); + WREG32_MC(R_000009_MC_CNTL1, tmp & C_000009_ENABLE_PAGE_TABLES); + radeon_gart_table_vram_unpin(rdev); +} + +static void rs600_gart_fini(struct radeon_device *rdev) +{ + radeon_gart_fini(rdev); + rs600_gart_disable(rdev); + radeon_gart_table_vram_free(rdev); +} + +#define R600_PTE_VALID (1 << 0) +#define R600_PTE_SYSTEM (1 << 1) +#define R600_PTE_SNOOPED (1 << 2) +#define R600_PTE_READABLE (1 << 5) +#define R600_PTE_WRITEABLE (1 << 6) + +int rs600_gart_set_page(struct radeon_device *rdev, int i, uint64_t addr) +{ + uint64_t *ptr = rdev->gart.ptr; + + if (i < 0 || i > rdev->gart.num_gpu_pages) { + return -EINVAL; + } + addr = addr & 0xFFFFFFFFFFFFF000ULL; + addr |= R600_PTE_VALID | R600_PTE_SYSTEM | R600_PTE_SNOOPED; + addr |= R600_PTE_READABLE | R600_PTE_WRITEABLE; + ptr[i] = addr; + return 0; +} + +int rs600_irq_set(struct radeon_device *rdev) +{ + uint32_t tmp = 0; + uint32_t mode_int = 0; + u32 hpd1 = RREG32(R_007D08_DC_HOT_PLUG_DETECT1_INT_CONTROL) & + ~S_007D08_DC_HOT_PLUG_DETECT1_INT_EN(1); + u32 hpd2 = RREG32(R_007D18_DC_HOT_PLUG_DETECT2_INT_CONTROL) & + ~S_007D18_DC_HOT_PLUG_DETECT2_INT_EN(1); + u32 hdmi0; + if (ASIC_IS_DCE2(rdev)) + hdmi0 = RREG32(R_007408_HDMI0_AUDIO_PACKET_CONTROL) & + ~S_007408_HDMI0_AZ_FORMAT_WTRIG_MASK(1); + else + hdmi0 = 0; + + if (!rdev->irq.installed) { + DRM_ERROR("Can't enable IRQ/MSI because no handler is installed\n"); + WREG32(R_000040_GEN_INT_CNTL, 0); + return -EINVAL; + } + if (atomic_read(&rdev->irq.ring_int[RADEON_RING_TYPE_GFX_INDEX])) { + tmp |= S_000040_SW_INT_EN(1); + } + if (rdev->irq.crtc_vblank_int[0] || + atomic_read(&rdev->irq.pflip[0])) { + mode_int |= S_006540_D1MODE_VBLANK_INT_MASK(1); + } + if (rdev->irq.crtc_vblank_int[1] || + atomic_read(&rdev->irq.pflip[1])) { + mode_int |= S_006540_D2MODE_VBLANK_INT_MASK(1); + } + if (rdev->irq.hpd[0]) { + hpd1 |= S_007D08_DC_HOT_PLUG_DETECT1_INT_EN(1); + } + if (rdev->irq.hpd[1]) { + hpd2 |= S_007D18_DC_HOT_PLUG_DETECT2_INT_EN(1); + } + if (rdev->irq.afmt[0]) { + hdmi0 |= S_007408_HDMI0_AZ_FORMAT_WTRIG_MASK(1); + } + WREG32(R_000040_GEN_INT_CNTL, tmp); + WREG32(R_006540_DxMODE_INT_MASK, mode_int); + WREG32(R_007D08_DC_HOT_PLUG_DETECT1_INT_CONTROL, hpd1); + WREG32(R_007D18_DC_HOT_PLUG_DETECT2_INT_CONTROL, hpd2); + if (ASIC_IS_DCE2(rdev)) + WREG32(R_007408_HDMI0_AUDIO_PACKET_CONTROL, hdmi0); + return 0; +} + +static inline u32 rs600_irq_ack(struct radeon_device *rdev) +{ + uint32_t irqs = RREG32(R_000044_GEN_INT_STATUS); + uint32_t irq_mask = S_000044_SW_INT(1); + u32 tmp; + + if (G_000044_DISPLAY_INT_STAT(irqs)) { + rdev->irq.stat_regs.r500.disp_int = RREG32(R_007EDC_DISP_INTERRUPT_STATUS); + if (G_007EDC_LB_D1_VBLANK_INTERRUPT(rdev->irq.stat_regs.r500.disp_int)) { + WREG32(R_006534_D1MODE_VBLANK_STATUS, + S_006534_D1MODE_VBLANK_ACK(1)); + } + if (G_007EDC_LB_D2_VBLANK_INTERRUPT(rdev->irq.stat_regs.r500.disp_int)) { + WREG32(R_006D34_D2MODE_VBLANK_STATUS, + S_006D34_D2MODE_VBLANK_ACK(1)); + } + if (G_007EDC_DC_HOT_PLUG_DETECT1_INTERRUPT(rdev->irq.stat_regs.r500.disp_int)) { + tmp = RREG32(R_007D08_DC_HOT_PLUG_DETECT1_INT_CONTROL); + tmp |= S_007D08_DC_HOT_PLUG_DETECT1_INT_ACK(1); + WREG32(R_007D08_DC_HOT_PLUG_DETECT1_INT_CONTROL, tmp); + } + if (G_007EDC_DC_HOT_PLUG_DETECT2_INTERRUPT(rdev->irq.stat_regs.r500.disp_int)) { + tmp = RREG32(R_007D18_DC_HOT_PLUG_DETECT2_INT_CONTROL); + tmp |= S_007D18_DC_HOT_PLUG_DETECT2_INT_ACK(1); + WREG32(R_007D18_DC_HOT_PLUG_DETECT2_INT_CONTROL, tmp); + } + } else { + rdev->irq.stat_regs.r500.disp_int = 0; + } + + if (ASIC_IS_DCE2(rdev)) { + rdev->irq.stat_regs.r500.hdmi0_status = RREG32(R_007404_HDMI0_STATUS) & + S_007404_HDMI0_AZ_FORMAT_WTRIG(1); + if (G_007404_HDMI0_AZ_FORMAT_WTRIG(rdev->irq.stat_regs.r500.hdmi0_status)) { + tmp = RREG32(R_007408_HDMI0_AUDIO_PACKET_CONTROL); + tmp |= S_007408_HDMI0_AZ_FORMAT_WTRIG_ACK(1); + WREG32(R_007408_HDMI0_AUDIO_PACKET_CONTROL, tmp); + } + } else + rdev->irq.stat_regs.r500.hdmi0_status = 0; + + if (irqs) { + WREG32(R_000044_GEN_INT_STATUS, irqs); + } + return irqs & irq_mask; +} + +void rs600_irq_disable(struct radeon_device *rdev) +{ + u32 hdmi0 = RREG32(R_007408_HDMI0_AUDIO_PACKET_CONTROL) & + ~S_007408_HDMI0_AZ_FORMAT_WTRIG_MASK(1); + WREG32(R_007408_HDMI0_AUDIO_PACKET_CONTROL, hdmi0); + WREG32(R_000040_GEN_INT_CNTL, 0); + WREG32(R_006540_DxMODE_INT_MASK, 0); + /* Wait and acknowledge irq */ + DRM_MDELAY(1); + rs600_irq_ack(rdev); +} + +irqreturn_t rs600_irq_process(struct radeon_device *rdev) +{ + u32 status, msi_rearm; + bool queue_hotplug = false; + bool queue_hdmi = false; + + status = rs600_irq_ack(rdev); + if (!status && + !rdev->irq.stat_regs.r500.disp_int && + !rdev->irq.stat_regs.r500.hdmi0_status) { + return IRQ_NONE; + } + while (status || + rdev->irq.stat_regs.r500.disp_int || + rdev->irq.stat_regs.r500.hdmi0_status) { + /* SW interrupt */ + if (G_000044_SW_INT(status)) { + radeon_fence_process(rdev, RADEON_RING_TYPE_GFX_INDEX); + } + /* Vertical blank interrupts */ + if (G_007EDC_LB_D1_VBLANK_INTERRUPT(rdev->irq.stat_regs.r500.disp_int)) { + if (rdev->irq.crtc_vblank_int[0]) { + drm_handle_vblank(rdev->ddev, 0); + rdev->pm.vblank_sync = true; + DRM_WAKEUP(&rdev->irq.vblank_queue); + } + if (atomic_read(&rdev->irq.pflip[0])) + radeon_crtc_handle_flip(rdev, 0); + } + if (G_007EDC_LB_D2_VBLANK_INTERRUPT(rdev->irq.stat_regs.r500.disp_int)) { + if (rdev->irq.crtc_vblank_int[1]) { + drm_handle_vblank(rdev->ddev, 1); + rdev->pm.vblank_sync = true; + DRM_WAKEUP(&rdev->irq.vblank_queue); + } + if (atomic_read(&rdev->irq.pflip[1])) + radeon_crtc_handle_flip(rdev, 1); + } + if (G_007EDC_DC_HOT_PLUG_DETECT1_INTERRUPT(rdev->irq.stat_regs.r500.disp_int)) { + queue_hotplug = true; + DRM_DEBUG("HPD1\n"); + } + if (G_007EDC_DC_HOT_PLUG_DETECT2_INTERRUPT(rdev->irq.stat_regs.r500.disp_int)) { + queue_hotplug = true; + DRM_DEBUG("HPD2\n"); + } + if (G_007404_HDMI0_AZ_FORMAT_WTRIG(rdev->irq.stat_regs.r500.hdmi0_status)) { + queue_hdmi = true; + DRM_DEBUG("HDMI0\n"); + } + status = rs600_irq_ack(rdev); + } + if (queue_hotplug) + taskqueue_enqueue(rdev->tq, &rdev->hotplug_work); + if (queue_hdmi) + taskqueue_enqueue(rdev->tq, &rdev->audio_work); + if (rdev->msi_enabled) { + switch (rdev->family) { + case CHIP_RS600: + case CHIP_RS690: + case CHIP_RS740: + msi_rearm = RREG32(RADEON_BUS_CNTL) & ~RS600_MSI_REARM; + WREG32(RADEON_BUS_CNTL, msi_rearm); + WREG32(RADEON_BUS_CNTL, msi_rearm | RS600_MSI_REARM); + break; + default: + WREG32(RADEON_MSI_REARM_EN, RV370_MSI_REARM_EN); + break; + } + } + return IRQ_HANDLED; +} + +u32 rs600_get_vblank_counter(struct radeon_device *rdev, int crtc) +{ + if (crtc == 0) + return RREG32(R_0060A4_D1CRTC_STATUS_FRAME_COUNT); + else + return RREG32(R_0068A4_D2CRTC_STATUS_FRAME_COUNT); +} + +int rs600_mc_wait_for_idle(struct radeon_device *rdev) +{ + unsigned i; + + for (i = 0; i < rdev->usec_timeout; i++) { + if (G_000000_MC_IDLE(RREG32_MC(R_000000_MC_STATUS))) + return 0; + DRM_UDELAY(1); + } + return -1; +} + +static void rs600_gpu_init(struct radeon_device *rdev) +{ + r420_pipes_init(rdev); + /* Wait for mc idle */ + if (rs600_mc_wait_for_idle(rdev)) + dev_warn(rdev->dev, "Wait MC idle timeout before updating MC.\n"); +} + +static void rs600_mc_init(struct radeon_device *rdev) +{ + u64 base; + + rdev->mc.aper_base = drm_get_resource_start(rdev->ddev, 0); + rdev->mc.aper_size = drm_get_resource_len(rdev->ddev, 0); + rdev->mc.vram_is_ddr = true; + rdev->mc.vram_width = 128; + rdev->mc.real_vram_size = RREG32(RADEON_CONFIG_MEMSIZE); + rdev->mc.mc_vram_size = rdev->mc.real_vram_size; + rdev->mc.visible_vram_size = rdev->mc.aper_size; + rdev->mc.igp_sideport_enabled = radeon_atombios_sideport_present(rdev); + base = RREG32_MC(R_000004_MC_FB_LOCATION); + base = G_000004_MC_FB_START(base) << 16; + radeon_vram_location(rdev, &rdev->mc, base); + rdev->mc.gtt_base_align = 0; + radeon_gtt_location(rdev, &rdev->mc); + radeon_update_bandwidth_info(rdev); +} + +void rs600_bandwidth_update(struct radeon_device *rdev) +{ + struct drm_display_mode *mode0 = NULL; + struct drm_display_mode *mode1 = NULL; + u32 d1mode_priority_a_cnt, d2mode_priority_a_cnt; + /* FIXME: implement full support */ + + radeon_update_display_priority(rdev); + + if (rdev->mode_info.crtcs[0]->base.enabled) + mode0 = &rdev->mode_info.crtcs[0]->base.mode; + if (rdev->mode_info.crtcs[1]->base.enabled) + mode1 = &rdev->mode_info.crtcs[1]->base.mode; + + rs690_line_buffer_adjust(rdev, mode0, mode1); + + if (rdev->disp_priority == 2) { + d1mode_priority_a_cnt = RREG32(R_006548_D1MODE_PRIORITY_A_CNT); + d2mode_priority_a_cnt = RREG32(R_006D48_D2MODE_PRIORITY_A_CNT); + d1mode_priority_a_cnt |= S_006548_D1MODE_PRIORITY_A_ALWAYS_ON(1); + d2mode_priority_a_cnt |= S_006D48_D2MODE_PRIORITY_A_ALWAYS_ON(1); + WREG32(R_006548_D1MODE_PRIORITY_A_CNT, d1mode_priority_a_cnt); + WREG32(R_00654C_D1MODE_PRIORITY_B_CNT, d1mode_priority_a_cnt); + WREG32(R_006D48_D2MODE_PRIORITY_A_CNT, d2mode_priority_a_cnt); + WREG32(R_006D4C_D2MODE_PRIORITY_B_CNT, d2mode_priority_a_cnt); + } +} + +uint32_t rs600_mc_rreg(struct radeon_device *rdev, uint32_t reg) +{ + WREG32(R_000070_MC_IND_INDEX, S_000070_MC_IND_ADDR(reg) | + S_000070_MC_IND_CITF_ARB0(1)); + return RREG32(R_000074_MC_IND_DATA); +} + +void rs600_mc_wreg(struct radeon_device *rdev, uint32_t reg, uint32_t v) +{ + WREG32(R_000070_MC_IND_INDEX, S_000070_MC_IND_ADDR(reg) | + S_000070_MC_IND_CITF_ARB0(1) | S_000070_MC_IND_WR_EN(1)); + WREG32(R_000074_MC_IND_DATA, v); +} + +static void rs600_debugfs(struct radeon_device *rdev) +{ + if (r100_debugfs_rbbm_init(rdev)) + DRM_ERROR("Failed to register debugfs file for RBBM !\n"); +} + +void rs600_set_safe_registers(struct radeon_device *rdev) +{ + rdev->config.r300.reg_safe_bm = rs600_reg_safe_bm; + rdev->config.r300.reg_safe_bm_size = DRM_ARRAY_SIZE(rs600_reg_safe_bm); +} + +static void rs600_mc_program(struct radeon_device *rdev) +{ + struct rv515_mc_save save; + + /* Stops all mc clients */ + rv515_mc_stop(rdev, &save); + + /* Wait for mc idle */ + if (rs600_mc_wait_for_idle(rdev)) + dev_warn(rdev->dev, "Wait MC idle timeout before updating MC.\n"); + + /* FIXME: What does AGP means for such chipset ? */ + WREG32_MC(R_000005_MC_AGP_LOCATION, 0x0FFFFFFF); + WREG32_MC(R_000006_AGP_BASE, 0); + WREG32_MC(R_000007_AGP_BASE_2, 0); + /* Program MC */ + WREG32_MC(R_000004_MC_FB_LOCATION, + S_000004_MC_FB_START(rdev->mc.vram_start >> 16) | + S_000004_MC_FB_TOP(rdev->mc.vram_end >> 16)); + WREG32(R_000134_HDP_FB_LOCATION, + S_000134_HDP_FB_START(rdev->mc.vram_start >> 16)); + + rv515_mc_resume(rdev, &save); +} + +static int rs600_startup(struct radeon_device *rdev) +{ + int r; + + rs600_mc_program(rdev); + /* Resume clock */ + rv515_clock_startup(rdev); + /* Initialize GPU configuration (# pipes, ...) */ + rs600_gpu_init(rdev); + /* Initialize GART (initialize after TTM so we can allocate + * memory through TTM but finalize after TTM) */ + r = rs600_gart_enable(rdev); + if (r) + return r; + + /* allocate wb buffer */ + r = radeon_wb_init(rdev); + if (r) + return r; + + r = radeon_fence_driver_start_ring(rdev, RADEON_RING_TYPE_GFX_INDEX); + if (r) { + dev_err(rdev->dev, "failed initializing CP fences (%d).\n", r); + return r; + } + + /* Enable IRQ */ + rs600_irq_set(rdev); + rdev->config.r300.hdp_cntl = RREG32(RADEON_HOST_PATH_CNTL); + /* 1M ring buffer */ + r = r100_cp_init(rdev, 1024 * 1024); + if (r) { + dev_err(rdev->dev, "failed initializing CP (%d).\n", r); + return r; + } + + r = radeon_ib_pool_init(rdev); + if (r) { + dev_err(rdev->dev, "IB initialization failed (%d).\n", r); + return r; + } + + r = r600_audio_init(rdev); + if (r) { + dev_err(rdev->dev, "failed initializing audio\n"); + return r; + } + + return 0; +} + +int rs600_resume(struct radeon_device *rdev) +{ + int r; + + /* Make sur GART are not working */ + rs600_gart_disable(rdev); + /* Resume clock before doing reset */ + rv515_clock_startup(rdev); + /* Reset gpu before posting otherwise ATOM will enter infinite loop */ + if (radeon_asic_reset(rdev)) { + dev_warn(rdev->dev, "GPU reset failed ! (0xE40=0x%08X, 0x7C0=0x%08X)\n", + RREG32(R_000E40_RBBM_STATUS), + RREG32(R_0007C0_CP_STAT)); + } + /* post */ + atom_asic_init(rdev->mode_info.atom_context); + /* Resume clock after posting */ + rv515_clock_startup(rdev); + /* Initialize surface registers */ + radeon_surface_init(rdev); + + rdev->accel_working = true; + r = rs600_startup(rdev); + if (r) { + rdev->accel_working = false; + } + return r; +} + +int rs600_suspend(struct radeon_device *rdev) +{ + r600_audio_fini(rdev); + r100_cp_disable(rdev); + radeon_wb_disable(rdev); + rs600_irq_disable(rdev); + rs600_gart_disable(rdev); + return 0; +} + +void rs600_fini(struct radeon_device *rdev) +{ + r600_audio_fini(rdev); + r100_cp_fini(rdev); + radeon_wb_fini(rdev); + radeon_ib_pool_fini(rdev); + radeon_gem_fini(rdev); + rs600_gart_fini(rdev); + radeon_irq_kms_fini(rdev); + radeon_fence_driver_fini(rdev); + radeon_bo_fini(rdev); + radeon_atombios_fini(rdev); + free(rdev->bios, DRM_MEM_DRIVER); + rdev->bios = NULL; +} + +int rs600_init(struct radeon_device *rdev) +{ + int r; + + /* Disable VGA */ + rv515_vga_render_disable(rdev); + /* Initialize scratch registers */ + radeon_scratch_init(rdev); + /* Initialize surface registers */ + radeon_surface_init(rdev); + /* restore some register to sane defaults */ + r100_restore_sanity(rdev); + /* BIOS */ + if (!radeon_get_bios(rdev)) { + if (ASIC_IS_AVIVO(rdev)) + return -EINVAL; + } + if (rdev->is_atom_bios) { + r = radeon_atombios_init(rdev); + if (r) + return r; + } else { + dev_err(rdev->dev, "Expecting atombios for RS600 GPU\n"); + return -EINVAL; + } + /* Reset gpu before posting otherwise ATOM will enter infinite loop */ + if (radeon_asic_reset(rdev)) { + dev_warn(rdev->dev, + "GPU reset failed ! (0xE40=0x%08X, 0x7C0=0x%08X)\n", + RREG32(R_000E40_RBBM_STATUS), + RREG32(R_0007C0_CP_STAT)); + } + /* check if cards are posted or not */ + if (radeon_boot_test_post_card(rdev) == false) + return -EINVAL; + + /* Initialize clocks */ + radeon_get_clock_info(rdev->ddev); + /* initialize memory controller */ + rs600_mc_init(rdev); + rs600_debugfs(rdev); + /* Fence driver */ + r = radeon_fence_driver_init(rdev); + if (r) + return r; + r = radeon_irq_kms_init(rdev); + if (r) + return r; + /* Memory manager */ + r = radeon_bo_init(rdev); + if (r) + return r; + r = rs600_gart_init(rdev); + if (r) + return r; + rs600_set_safe_registers(rdev); + + rdev->accel_working = true; + r = rs600_startup(rdev); + if (r) { + /* Somethings want wront with the accel init stop accel */ + dev_err(rdev->dev, "Disabling GPU acceleration\n"); + r100_cp_fini(rdev); + radeon_wb_fini(rdev); + radeon_ib_pool_fini(rdev); + rs600_gart_fini(rdev); + radeon_irq_kms_fini(rdev); + rdev->accel_working = false; + } + return 0; +} diff --git a/sys/dev/drm2/radeon/rs600_reg_safe.h b/sys/dev/drm2/radeon/rs600_reg_safe.h new file mode 100644 index 00000000000..07913c90e17 --- /dev/null +++ b/sys/dev/drm2/radeon/rs600_reg_safe.h @@ -0,0 +1,60 @@ +#include +__FBSDID("$FreeBSD$"); + +static const unsigned rs600_reg_safe_bm[219] = { + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0x17FF1FFF, 0xFFFFFFFC, 0xFFFFFFFF, 0xFF30FFBF, + 0xFFFFFFF8, 0xC3E6FFFF, 0xFFFFF6DF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFF03F, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFEFCE, 0xF00EBFFF, 0x007C0000, + 0xF0000078, 0xFF000009, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFF7FF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFC48, 0xFFFFFFFF, 0xFFFFFFFE, 0xFFFFFFFF, + 0x38FF8F50, 0xFFF88082, 0xF000000C, 0xFAE00BFF, + 0x0000FFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, + 0x00000000, 0x00000100, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0xFF800000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x0003FC0B, 0xFFFFFCFF, 0xFFBFFB99, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, +}; diff --git a/sys/dev/drm2/radeon/rs600d.h b/sys/dev/drm2/radeon/rs600d.h new file mode 100644 index 00000000000..f7a5b716f39 --- /dev/null +++ b/sys/dev/drm2/radeon/rs600d.h @@ -0,0 +1,688 @@ +/* + * Copyright 2008 Advanced Micro Devices, Inc. + * Copyright 2008 Red Hat Inc. + * Copyright 2009 Jerome Glisse. + * + * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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. + * + * Authors: Dave Airlie + * Alex Deucher + * Jerome Glisse + */ +#ifndef __RS600D_H__ +#define __RS600D_H__ + +#include +__FBSDID("$FreeBSD$"); + +/* Registers */ +#define R_000040_GEN_INT_CNTL 0x000040 +#define S_000040_SCRATCH_INT_MASK(x) (((x) & 0x1) << 18) +#define G_000040_SCRATCH_INT_MASK(x) (((x) >> 18) & 0x1) +#define C_000040_SCRATCH_INT_MASK 0xFFFBFFFF +#define S_000040_GUI_IDLE_MASK(x) (((x) & 0x1) << 19) +#define G_000040_GUI_IDLE_MASK(x) (((x) >> 19) & 0x1) +#define C_000040_GUI_IDLE_MASK 0xFFF7FFFF +#define S_000040_DMA_VIPH1_INT_EN(x) (((x) & 0x1) << 13) +#define G_000040_DMA_VIPH1_INT_EN(x) (((x) >> 13) & 0x1) +#define C_000040_DMA_VIPH1_INT_EN 0xFFFFDFFF +#define S_000040_DMA_VIPH2_INT_EN(x) (((x) & 0x1) << 14) +#define G_000040_DMA_VIPH2_INT_EN(x) (((x) >> 14) & 0x1) +#define C_000040_DMA_VIPH2_INT_EN 0xFFFFBFFF +#define S_000040_DMA_VIPH3_INT_EN(x) (((x) & 0x1) << 15) +#define G_000040_DMA_VIPH3_INT_EN(x) (((x) >> 15) & 0x1) +#define C_000040_DMA_VIPH3_INT_EN 0xFFFF7FFF +#define S_000040_I2C_INT_EN(x) (((x) & 0x1) << 17) +#define G_000040_I2C_INT_EN(x) (((x) >> 17) & 0x1) +#define C_000040_I2C_INT_EN 0xFFFDFFFF +#define S_000040_GUI_IDLE(x) (((x) & 0x1) << 19) +#define G_000040_GUI_IDLE(x) (((x) >> 19) & 0x1) +#define C_000040_GUI_IDLE 0xFFF7FFFF +#define S_000040_VIPH_INT_EN(x) (((x) & 0x1) << 24) +#define G_000040_VIPH_INT_EN(x) (((x) >> 24) & 0x1) +#define C_000040_VIPH_INT_EN 0xFEFFFFFF +#define S_000040_SW_INT_EN(x) (((x) & 0x1) << 25) +#define G_000040_SW_INT_EN(x) (((x) >> 25) & 0x1) +#define C_000040_SW_INT_EN 0xFDFFFFFF +#define S_000040_GEYSERVILLE(x) (((x) & 0x1) << 27) +#define G_000040_GEYSERVILLE(x) (((x) >> 27) & 0x1) +#define C_000040_GEYSERVILLE 0xF7FFFFFF +#define S_000040_HDCP_AUTHORIZED_INT(x) (((x) & 0x1) << 28) +#define G_000040_HDCP_AUTHORIZED_INT(x) (((x) >> 28) & 0x1) +#define C_000040_HDCP_AUTHORIZED_INT 0xEFFFFFFF +#define S_000040_DVI_I2C_INT(x) (((x) & 0x1) << 29) +#define G_000040_DVI_I2C_INT(x) (((x) >> 29) & 0x1) +#define C_000040_DVI_I2C_INT 0xDFFFFFFF +#define S_000040_GUIDMA(x) (((x) & 0x1) << 30) +#define G_000040_GUIDMA(x) (((x) >> 30) & 0x1) +#define C_000040_GUIDMA 0xBFFFFFFF +#define S_000040_VIDDMA(x) (((x) & 0x1) << 31) +#define G_000040_VIDDMA(x) (((x) >> 31) & 0x1) +#define C_000040_VIDDMA 0x7FFFFFFF +#define R_000044_GEN_INT_STATUS 0x000044 +#define S_000044_DISPLAY_INT_STAT(x) (((x) & 0x1) << 0) +#define G_000044_DISPLAY_INT_STAT(x) (((x) >> 0) & 0x1) +#define C_000044_DISPLAY_INT_STAT 0xFFFFFFFE +#define S_000044_VGA_INT_STAT(x) (((x) & 0x1) << 1) +#define G_000044_VGA_INT_STAT(x) (((x) >> 1) & 0x1) +#define C_000044_VGA_INT_STAT 0xFFFFFFFD +#define S_000044_CAP0_INT_ACTIVE(x) (((x) & 0x1) << 8) +#define G_000044_CAP0_INT_ACTIVE(x) (((x) >> 8) & 0x1) +#define C_000044_CAP0_INT_ACTIVE 0xFFFFFEFF +#define S_000044_DMA_VIPH0_INT(x) (((x) & 0x1) << 12) +#define G_000044_DMA_VIPH0_INT(x) (((x) >> 12) & 0x1) +#define C_000044_DMA_VIPH0_INT 0xFFFFEFFF +#define S_000044_DMA_VIPH1_INT(x) (((x) & 0x1) << 13) +#define G_000044_DMA_VIPH1_INT(x) (((x) >> 13) & 0x1) +#define C_000044_DMA_VIPH1_INT 0xFFFFDFFF +#define S_000044_DMA_VIPH2_INT(x) (((x) & 0x1) << 14) +#define G_000044_DMA_VIPH2_INT(x) (((x) >> 14) & 0x1) +#define C_000044_DMA_VIPH2_INT 0xFFFFBFFF +#define S_000044_DMA_VIPH3_INT(x) (((x) & 0x1) << 15) +#define G_000044_DMA_VIPH3_INT(x) (((x) >> 15) & 0x1) +#define C_000044_DMA_VIPH3_INT 0xFFFF7FFF +#define S_000044_MC_PROBE_FAULT_STAT(x) (((x) & 0x1) << 16) +#define G_000044_MC_PROBE_FAULT_STAT(x) (((x) >> 16) & 0x1) +#define C_000044_MC_PROBE_FAULT_STAT 0xFFFEFFFF +#define S_000044_I2C_INT(x) (((x) & 0x1) << 17) +#define G_000044_I2C_INT(x) (((x) >> 17) & 0x1) +#define C_000044_I2C_INT 0xFFFDFFFF +#define S_000044_SCRATCH_INT_STAT(x) (((x) & 0x1) << 18) +#define G_000044_SCRATCH_INT_STAT(x) (((x) >> 18) & 0x1) +#define C_000044_SCRATCH_INT_STAT 0xFFFBFFFF +#define S_000044_GUI_IDLE_STAT(x) (((x) & 0x1) << 19) +#define G_000044_GUI_IDLE_STAT(x) (((x) >> 19) & 0x1) +#define C_000044_GUI_IDLE_STAT 0xFFF7FFFF +#define S_000044_ATI_OVERDRIVE_INT_STAT(x) (((x) & 0x1) << 20) +#define G_000044_ATI_OVERDRIVE_INT_STAT(x) (((x) >> 20) & 0x1) +#define C_000044_ATI_OVERDRIVE_INT_STAT 0xFFEFFFFF +#define S_000044_MC_PROTECTION_FAULT_STAT(x) (((x) & 0x1) << 21) +#define G_000044_MC_PROTECTION_FAULT_STAT(x) (((x) >> 21) & 0x1) +#define C_000044_MC_PROTECTION_FAULT_STAT 0xFFDFFFFF +#define S_000044_RBBM_READ_INT_STAT(x) (((x) & 0x1) << 22) +#define G_000044_RBBM_READ_INT_STAT(x) (((x) >> 22) & 0x1) +#define C_000044_RBBM_READ_INT_STAT 0xFFBFFFFF +#define S_000044_CB_CONTEXT_SWITCH_STAT(x) (((x) & 0x1) << 23) +#define G_000044_CB_CONTEXT_SWITCH_STAT(x) (((x) >> 23) & 0x1) +#define C_000044_CB_CONTEXT_SWITCH_STAT 0xFF7FFFFF +#define S_000044_VIPH_INT(x) (((x) & 0x1) << 24) +#define G_000044_VIPH_INT(x) (((x) >> 24) & 0x1) +#define C_000044_VIPH_INT 0xFEFFFFFF +#define S_000044_SW_INT(x) (((x) & 0x1) << 25) +#define G_000044_SW_INT(x) (((x) >> 25) & 0x1) +#define C_000044_SW_INT 0xFDFFFFFF +#define S_000044_SW_INT_SET(x) (((x) & 0x1) << 26) +#define G_000044_SW_INT_SET(x) (((x) >> 26) & 0x1) +#define C_000044_SW_INT_SET 0xFBFFFFFF +#define S_000044_IDCT_INT_STAT(x) (((x) & 0x1) << 27) +#define G_000044_IDCT_INT_STAT(x) (((x) >> 27) & 0x1) +#define C_000044_IDCT_INT_STAT 0xF7FFFFFF +#define S_000044_GUIDMA_STAT(x) (((x) & 0x1) << 30) +#define G_000044_GUIDMA_STAT(x) (((x) >> 30) & 0x1) +#define C_000044_GUIDMA_STAT 0xBFFFFFFF +#define S_000044_VIDDMA_STAT(x) (((x) & 0x1) << 31) +#define G_000044_VIDDMA_STAT(x) (((x) >> 31) & 0x1) +#define C_000044_VIDDMA_STAT 0x7FFFFFFF +#define R_00004C_BUS_CNTL 0x00004C +#define S_00004C_BUS_MASTER_DIS(x) (((x) & 0x1) << 14) +#define G_00004C_BUS_MASTER_DIS(x) (((x) >> 14) & 0x1) +#define C_00004C_BUS_MASTER_DIS 0xFFFFBFFF +#define S_00004C_BUS_MSI_REARM(x) (((x) & 0x1) << 20) +#define G_00004C_BUS_MSI_REARM(x) (((x) >> 20) & 0x1) +#define C_00004C_BUS_MSI_REARM 0xFFEFFFFF +#define R_000070_MC_IND_INDEX 0x000070 +#define S_000070_MC_IND_ADDR(x) (((x) & 0xFFFF) << 0) +#define G_000070_MC_IND_ADDR(x) (((x) >> 0) & 0xFFFF) +#define C_000070_MC_IND_ADDR 0xFFFF0000 +#define S_000070_MC_IND_SEQ_RBS_0(x) (((x) & 0x1) << 16) +#define G_000070_MC_IND_SEQ_RBS_0(x) (((x) >> 16) & 0x1) +#define C_000070_MC_IND_SEQ_RBS_0 0xFFFEFFFF +#define S_000070_MC_IND_SEQ_RBS_1(x) (((x) & 0x1) << 17) +#define G_000070_MC_IND_SEQ_RBS_1(x) (((x) >> 17) & 0x1) +#define C_000070_MC_IND_SEQ_RBS_1 0xFFFDFFFF +#define S_000070_MC_IND_SEQ_RBS_2(x) (((x) & 0x1) << 18) +#define G_000070_MC_IND_SEQ_RBS_2(x) (((x) >> 18) & 0x1) +#define C_000070_MC_IND_SEQ_RBS_2 0xFFFBFFFF +#define S_000070_MC_IND_SEQ_RBS_3(x) (((x) & 0x1) << 19) +#define G_000070_MC_IND_SEQ_RBS_3(x) (((x) >> 19) & 0x1) +#define C_000070_MC_IND_SEQ_RBS_3 0xFFF7FFFF +#define S_000070_MC_IND_AIC_RBS(x) (((x) & 0x1) << 20) +#define G_000070_MC_IND_AIC_RBS(x) (((x) >> 20) & 0x1) +#define C_000070_MC_IND_AIC_RBS 0xFFEFFFFF +#define S_000070_MC_IND_CITF_ARB0(x) (((x) & 0x1) << 21) +#define G_000070_MC_IND_CITF_ARB0(x) (((x) >> 21) & 0x1) +#define C_000070_MC_IND_CITF_ARB0 0xFFDFFFFF +#define S_000070_MC_IND_CITF_ARB1(x) (((x) & 0x1) << 22) +#define G_000070_MC_IND_CITF_ARB1(x) (((x) >> 22) & 0x1) +#define C_000070_MC_IND_CITF_ARB1 0xFFBFFFFF +#define S_000070_MC_IND_WR_EN(x) (((x) & 0x1) << 23) +#define G_000070_MC_IND_WR_EN(x) (((x) >> 23) & 0x1) +#define C_000070_MC_IND_WR_EN 0xFF7FFFFF +#define S_000070_MC_IND_RD_INV(x) (((x) & 0x1) << 24) +#define G_000070_MC_IND_RD_INV(x) (((x) >> 24) & 0x1) +#define C_000070_MC_IND_RD_INV 0xFEFFFFFF +#define R_000074_MC_IND_DATA 0x000074 +#define S_000074_MC_IND_DATA(x) (((x) & 0xFFFFFFFF) << 0) +#define G_000074_MC_IND_DATA(x) (((x) >> 0) & 0xFFFFFFFF) +#define C_000074_MC_IND_DATA 0x00000000 +#define R_0000F0_RBBM_SOFT_RESET 0x0000F0 +#define S_0000F0_SOFT_RESET_CP(x) (((x) & 0x1) << 0) +#define G_0000F0_SOFT_RESET_CP(x) (((x) >> 0) & 0x1) +#define C_0000F0_SOFT_RESET_CP 0xFFFFFFFE +#define S_0000F0_SOFT_RESET_HI(x) (((x) & 0x1) << 1) +#define G_0000F0_SOFT_RESET_HI(x) (((x) >> 1) & 0x1) +#define C_0000F0_SOFT_RESET_HI 0xFFFFFFFD +#define S_0000F0_SOFT_RESET_VAP(x) (((x) & 0x1) << 2) +#define G_0000F0_SOFT_RESET_VAP(x) (((x) >> 2) & 0x1) +#define C_0000F0_SOFT_RESET_VAP 0xFFFFFFFB +#define S_0000F0_SOFT_RESET_RE(x) (((x) & 0x1) << 3) +#define G_0000F0_SOFT_RESET_RE(x) (((x) >> 3) & 0x1) +#define C_0000F0_SOFT_RESET_RE 0xFFFFFFF7 +#define S_0000F0_SOFT_RESET_PP(x) (((x) & 0x1) << 4) +#define G_0000F0_SOFT_RESET_PP(x) (((x) >> 4) & 0x1) +#define C_0000F0_SOFT_RESET_PP 0xFFFFFFEF +#define S_0000F0_SOFT_RESET_E2(x) (((x) & 0x1) << 5) +#define G_0000F0_SOFT_RESET_E2(x) (((x) >> 5) & 0x1) +#define C_0000F0_SOFT_RESET_E2 0xFFFFFFDF +#define S_0000F0_SOFT_RESET_RB(x) (((x) & 0x1) << 6) +#define G_0000F0_SOFT_RESET_RB(x) (((x) >> 6) & 0x1) +#define C_0000F0_SOFT_RESET_RB 0xFFFFFFBF +#define S_0000F0_SOFT_RESET_HDP(x) (((x) & 0x1) << 7) +#define G_0000F0_SOFT_RESET_HDP(x) (((x) >> 7) & 0x1) +#define C_0000F0_SOFT_RESET_HDP 0xFFFFFF7F +#define S_0000F0_SOFT_RESET_MC(x) (((x) & 0x1) << 8) +#define G_0000F0_SOFT_RESET_MC(x) (((x) >> 8) & 0x1) +#define C_0000F0_SOFT_RESET_MC 0xFFFFFEFF +#define S_0000F0_SOFT_RESET_AIC(x) (((x) & 0x1) << 9) +#define G_0000F0_SOFT_RESET_AIC(x) (((x) >> 9) & 0x1) +#define C_0000F0_SOFT_RESET_AIC 0xFFFFFDFF +#define S_0000F0_SOFT_RESET_VIP(x) (((x) & 0x1) << 10) +#define G_0000F0_SOFT_RESET_VIP(x) (((x) >> 10) & 0x1) +#define C_0000F0_SOFT_RESET_VIP 0xFFFFFBFF +#define S_0000F0_SOFT_RESET_DISP(x) (((x) & 0x1) << 11) +#define G_0000F0_SOFT_RESET_DISP(x) (((x) >> 11) & 0x1) +#define C_0000F0_SOFT_RESET_DISP 0xFFFFF7FF +#define S_0000F0_SOFT_RESET_CG(x) (((x) & 0x1) << 12) +#define G_0000F0_SOFT_RESET_CG(x) (((x) >> 12) & 0x1) +#define C_0000F0_SOFT_RESET_CG 0xFFFFEFFF +#define S_0000F0_SOFT_RESET_GA(x) (((x) & 0x1) << 13) +#define G_0000F0_SOFT_RESET_GA(x) (((x) >> 13) & 0x1) +#define C_0000F0_SOFT_RESET_GA 0xFFFFDFFF +#define S_0000F0_SOFT_RESET_IDCT(x) (((x) & 0x1) << 14) +#define G_0000F0_SOFT_RESET_IDCT(x) (((x) >> 14) & 0x1) +#define C_0000F0_SOFT_RESET_IDCT 0xFFFFBFFF +#define R_000134_HDP_FB_LOCATION 0x000134 +#define S_000134_HDP_FB_START(x) (((x) & 0xFFFF) << 0) +#define G_000134_HDP_FB_START(x) (((x) >> 0) & 0xFFFF) +#define C_000134_HDP_FB_START 0xFFFF0000 +#define R_0007C0_CP_STAT 0x0007C0 +#define S_0007C0_MRU_BUSY(x) (((x) & 0x1) << 0) +#define G_0007C0_MRU_BUSY(x) (((x) >> 0) & 0x1) +#define C_0007C0_MRU_BUSY 0xFFFFFFFE +#define S_0007C0_MWU_BUSY(x) (((x) & 0x1) << 1) +#define G_0007C0_MWU_BUSY(x) (((x) >> 1) & 0x1) +#define C_0007C0_MWU_BUSY 0xFFFFFFFD +#define S_0007C0_RSIU_BUSY(x) (((x) & 0x1) << 2) +#define G_0007C0_RSIU_BUSY(x) (((x) >> 2) & 0x1) +#define C_0007C0_RSIU_BUSY 0xFFFFFFFB +#define S_0007C0_RCIU_BUSY(x) (((x) & 0x1) << 3) +#define G_0007C0_RCIU_BUSY(x) (((x) >> 3) & 0x1) +#define C_0007C0_RCIU_BUSY 0xFFFFFFF7 +#define S_0007C0_CSF_PRIMARY_BUSY(x) (((x) & 0x1) << 9) +#define G_0007C0_CSF_PRIMARY_BUSY(x) (((x) >> 9) & 0x1) +#define C_0007C0_CSF_PRIMARY_BUSY 0xFFFFFDFF +#define S_0007C0_CSF_INDIRECT_BUSY(x) (((x) & 0x1) << 10) +#define G_0007C0_CSF_INDIRECT_BUSY(x) (((x) >> 10) & 0x1) +#define C_0007C0_CSF_INDIRECT_BUSY 0xFFFFFBFF +#define S_0007C0_CSQ_PRIMARY_BUSY(x) (((x) & 0x1) << 11) +#define G_0007C0_CSQ_PRIMARY_BUSY(x) (((x) >> 11) & 0x1) +#define C_0007C0_CSQ_PRIMARY_BUSY 0xFFFFF7FF +#define S_0007C0_CSQ_INDIRECT_BUSY(x) (((x) & 0x1) << 12) +#define G_0007C0_CSQ_INDIRECT_BUSY(x) (((x) >> 12) & 0x1) +#define C_0007C0_CSQ_INDIRECT_BUSY 0xFFFFEFFF +#define S_0007C0_CSI_BUSY(x) (((x) & 0x1) << 13) +#define G_0007C0_CSI_BUSY(x) (((x) >> 13) & 0x1) +#define C_0007C0_CSI_BUSY 0xFFFFDFFF +#define S_0007C0_CSF_INDIRECT2_BUSY(x) (((x) & 0x1) << 14) +#define G_0007C0_CSF_INDIRECT2_BUSY(x) (((x) >> 14) & 0x1) +#define C_0007C0_CSF_INDIRECT2_BUSY 0xFFFFBFFF +#define S_0007C0_CSQ_INDIRECT2_BUSY(x) (((x) & 0x1) << 15) +#define G_0007C0_CSQ_INDIRECT2_BUSY(x) (((x) >> 15) & 0x1) +#define C_0007C0_CSQ_INDIRECT2_BUSY 0xFFFF7FFF +#define S_0007C0_GUIDMA_BUSY(x) (((x) & 0x1) << 28) +#define G_0007C0_GUIDMA_BUSY(x) (((x) >> 28) & 0x1) +#define C_0007C0_GUIDMA_BUSY 0xEFFFFFFF +#define S_0007C0_VIDDMA_BUSY(x) (((x) & 0x1) << 29) +#define G_0007C0_VIDDMA_BUSY(x) (((x) >> 29) & 0x1) +#define C_0007C0_VIDDMA_BUSY 0xDFFFFFFF +#define S_0007C0_CMDSTRM_BUSY(x) (((x) & 0x1) << 30) +#define G_0007C0_CMDSTRM_BUSY(x) (((x) >> 30) & 0x1) +#define C_0007C0_CMDSTRM_BUSY 0xBFFFFFFF +#define S_0007C0_CP_BUSY(x) (((x) & 0x1) << 31) +#define G_0007C0_CP_BUSY(x) (((x) >> 31) & 0x1) +#define C_0007C0_CP_BUSY 0x7FFFFFFF +#define R_000E40_RBBM_STATUS 0x000E40 +#define S_000E40_CMDFIFO_AVAIL(x) (((x) & 0x7F) << 0) +#define G_000E40_CMDFIFO_AVAIL(x) (((x) >> 0) & 0x7F) +#define C_000E40_CMDFIFO_AVAIL 0xFFFFFF80 +#define S_000E40_HIRQ_ON_RBB(x) (((x) & 0x1) << 8) +#define G_000E40_HIRQ_ON_RBB(x) (((x) >> 8) & 0x1) +#define C_000E40_HIRQ_ON_RBB 0xFFFFFEFF +#define S_000E40_CPRQ_ON_RBB(x) (((x) & 0x1) << 9) +#define G_000E40_CPRQ_ON_RBB(x) (((x) >> 9) & 0x1) +#define C_000E40_CPRQ_ON_RBB 0xFFFFFDFF +#define S_000E40_CFRQ_ON_RBB(x) (((x) & 0x1) << 10) +#define G_000E40_CFRQ_ON_RBB(x) (((x) >> 10) & 0x1) +#define C_000E40_CFRQ_ON_RBB 0xFFFFFBFF +#define S_000E40_HIRQ_IN_RTBUF(x) (((x) & 0x1) << 11) +#define G_000E40_HIRQ_IN_RTBUF(x) (((x) >> 11) & 0x1) +#define C_000E40_HIRQ_IN_RTBUF 0xFFFFF7FF +#define S_000E40_CPRQ_IN_RTBUF(x) (((x) & 0x1) << 12) +#define G_000E40_CPRQ_IN_RTBUF(x) (((x) >> 12) & 0x1) +#define C_000E40_CPRQ_IN_RTBUF 0xFFFFEFFF +#define S_000E40_CFRQ_IN_RTBUF(x) (((x) & 0x1) << 13) +#define G_000E40_CFRQ_IN_RTBUF(x) (((x) >> 13) & 0x1) +#define C_000E40_CFRQ_IN_RTBUF 0xFFFFDFFF +#define S_000E40_CF_PIPE_BUSY(x) (((x) & 0x1) << 14) +#define G_000E40_CF_PIPE_BUSY(x) (((x) >> 14) & 0x1) +#define C_000E40_CF_PIPE_BUSY 0xFFFFBFFF +#define S_000E40_ENG_EV_BUSY(x) (((x) & 0x1) << 15) +#define G_000E40_ENG_EV_BUSY(x) (((x) >> 15) & 0x1) +#define C_000E40_ENG_EV_BUSY 0xFFFF7FFF +#define S_000E40_CP_CMDSTRM_BUSY(x) (((x) & 0x1) << 16) +#define G_000E40_CP_CMDSTRM_BUSY(x) (((x) >> 16) & 0x1) +#define C_000E40_CP_CMDSTRM_BUSY 0xFFFEFFFF +#define S_000E40_E2_BUSY(x) (((x) & 0x1) << 17) +#define G_000E40_E2_BUSY(x) (((x) >> 17) & 0x1) +#define C_000E40_E2_BUSY 0xFFFDFFFF +#define S_000E40_RB2D_BUSY(x) (((x) & 0x1) << 18) +#define G_000E40_RB2D_BUSY(x) (((x) >> 18) & 0x1) +#define C_000E40_RB2D_BUSY 0xFFFBFFFF +#define S_000E40_RB3D_BUSY(x) (((x) & 0x1) << 19) +#define G_000E40_RB3D_BUSY(x) (((x) >> 19) & 0x1) +#define C_000E40_RB3D_BUSY 0xFFF7FFFF +#define S_000E40_VAP_BUSY(x) (((x) & 0x1) << 20) +#define G_000E40_VAP_BUSY(x) (((x) >> 20) & 0x1) +#define C_000E40_VAP_BUSY 0xFFEFFFFF +#define S_000E40_RE_BUSY(x) (((x) & 0x1) << 21) +#define G_000E40_RE_BUSY(x) (((x) >> 21) & 0x1) +#define C_000E40_RE_BUSY 0xFFDFFFFF +#define S_000E40_TAM_BUSY(x) (((x) & 0x1) << 22) +#define G_000E40_TAM_BUSY(x) (((x) >> 22) & 0x1) +#define C_000E40_TAM_BUSY 0xFFBFFFFF +#define S_000E40_TDM_BUSY(x) (((x) & 0x1) << 23) +#define G_000E40_TDM_BUSY(x) (((x) >> 23) & 0x1) +#define C_000E40_TDM_BUSY 0xFF7FFFFF +#define S_000E40_PB_BUSY(x) (((x) & 0x1) << 24) +#define G_000E40_PB_BUSY(x) (((x) >> 24) & 0x1) +#define C_000E40_PB_BUSY 0xFEFFFFFF +#define S_000E40_TIM_BUSY(x) (((x) & 0x1) << 25) +#define G_000E40_TIM_BUSY(x) (((x) >> 25) & 0x1) +#define C_000E40_TIM_BUSY 0xFDFFFFFF +#define S_000E40_GA_BUSY(x) (((x) & 0x1) << 26) +#define G_000E40_GA_BUSY(x) (((x) >> 26) & 0x1) +#define C_000E40_GA_BUSY 0xFBFFFFFF +#define S_000E40_CBA2D_BUSY(x) (((x) & 0x1) << 27) +#define G_000E40_CBA2D_BUSY(x) (((x) >> 27) & 0x1) +#define C_000E40_CBA2D_BUSY 0xF7FFFFFF +#define S_000E40_GUI_ACTIVE(x) (((x) & 0x1) << 31) +#define G_000E40_GUI_ACTIVE(x) (((x) >> 31) & 0x1) +#define C_000E40_GUI_ACTIVE 0x7FFFFFFF +#define R_0060A4_D1CRTC_STATUS_FRAME_COUNT 0x0060A4 +#define S_0060A4_D1CRTC_FRAME_COUNT(x) (((x) & 0xFFFFFF) << 0) +#define G_0060A4_D1CRTC_FRAME_COUNT(x) (((x) >> 0) & 0xFFFFFF) +#define C_0060A4_D1CRTC_FRAME_COUNT 0xFF000000 +#define R_006534_D1MODE_VBLANK_STATUS 0x006534 +#define S_006534_D1MODE_VBLANK_OCCURRED(x) (((x) & 0x1) << 0) +#define G_006534_D1MODE_VBLANK_OCCURRED(x) (((x) >> 0) & 0x1) +#define C_006534_D1MODE_VBLANK_OCCURRED 0xFFFFFFFE +#define S_006534_D1MODE_VBLANK_ACK(x) (((x) & 0x1) << 4) +#define G_006534_D1MODE_VBLANK_ACK(x) (((x) >> 4) & 0x1) +#define C_006534_D1MODE_VBLANK_ACK 0xFFFFFFEF +#define S_006534_D1MODE_VBLANK_STAT(x) (((x) & 0x1) << 12) +#define G_006534_D1MODE_VBLANK_STAT(x) (((x) >> 12) & 0x1) +#define C_006534_D1MODE_VBLANK_STAT 0xFFFFEFFF +#define S_006534_D1MODE_VBLANK_INTERRUPT(x) (((x) & 0x1) << 16) +#define G_006534_D1MODE_VBLANK_INTERRUPT(x) (((x) >> 16) & 0x1) +#define C_006534_D1MODE_VBLANK_INTERRUPT 0xFFFEFFFF +#define R_006540_DxMODE_INT_MASK 0x006540 +#define S_006540_D1MODE_VBLANK_INT_MASK(x) (((x) & 0x1) << 0) +#define G_006540_D1MODE_VBLANK_INT_MASK(x) (((x) >> 0) & 0x1) +#define C_006540_D1MODE_VBLANK_INT_MASK 0xFFFFFFFE +#define S_006540_D1MODE_VLINE_INT_MASK(x) (((x) & 0x1) << 4) +#define G_006540_D1MODE_VLINE_INT_MASK(x) (((x) >> 4) & 0x1) +#define C_006540_D1MODE_VLINE_INT_MASK 0xFFFFFFEF +#define S_006540_D2MODE_VBLANK_INT_MASK(x) (((x) & 0x1) << 8) +#define G_006540_D2MODE_VBLANK_INT_MASK(x) (((x) >> 8) & 0x1) +#define C_006540_D2MODE_VBLANK_INT_MASK 0xFFFFFEFF +#define S_006540_D2MODE_VLINE_INT_MASK(x) (((x) & 0x1) << 12) +#define G_006540_D2MODE_VLINE_INT_MASK(x) (((x) >> 12) & 0x1) +#define C_006540_D2MODE_VLINE_INT_MASK 0xFFFFEFFF +#define S_006540_D1MODE_VBLANK_CP_SEL(x) (((x) & 0x1) << 30) +#define G_006540_D1MODE_VBLANK_CP_SEL(x) (((x) >> 30) & 0x1) +#define C_006540_D1MODE_VBLANK_CP_SEL 0xBFFFFFFF +#define S_006540_D2MODE_VBLANK_CP_SEL(x) (((x) & 0x1) << 31) +#define G_006540_D2MODE_VBLANK_CP_SEL(x) (((x) >> 31) & 0x1) +#define C_006540_D2MODE_VBLANK_CP_SEL 0x7FFFFFFF +#define R_0068A4_D2CRTC_STATUS_FRAME_COUNT 0x0068A4 +#define S_0068A4_D2CRTC_FRAME_COUNT(x) (((x) & 0xFFFFFF) << 0) +#define G_0068A4_D2CRTC_FRAME_COUNT(x) (((x) >> 0) & 0xFFFFFF) +#define C_0068A4_D2CRTC_FRAME_COUNT 0xFF000000 +#define R_006D34_D2MODE_VBLANK_STATUS 0x006D34 +#define S_006D34_D2MODE_VBLANK_OCCURRED(x) (((x) & 0x1) << 0) +#define G_006D34_D2MODE_VBLANK_OCCURRED(x) (((x) >> 0) & 0x1) +#define C_006D34_D2MODE_VBLANK_OCCURRED 0xFFFFFFFE +#define S_006D34_D2MODE_VBLANK_ACK(x) (((x) & 0x1) << 4) +#define G_006D34_D2MODE_VBLANK_ACK(x) (((x) >> 4) & 0x1) +#define C_006D34_D2MODE_VBLANK_ACK 0xFFFFFFEF +#define S_006D34_D2MODE_VBLANK_STAT(x) (((x) & 0x1) << 12) +#define G_006D34_D2MODE_VBLANK_STAT(x) (((x) >> 12) & 0x1) +#define C_006D34_D2MODE_VBLANK_STAT 0xFFFFEFFF +#define S_006D34_D2MODE_VBLANK_INTERRUPT(x) (((x) & 0x1) << 16) +#define G_006D34_D2MODE_VBLANK_INTERRUPT(x) (((x) >> 16) & 0x1) +#define C_006D34_D2MODE_VBLANK_INTERRUPT 0xFFFEFFFF +#define R_007EDC_DISP_INTERRUPT_STATUS 0x007EDC +#define S_007EDC_LB_D1_VBLANK_INTERRUPT(x) (((x) & 0x1) << 4) +#define G_007EDC_LB_D1_VBLANK_INTERRUPT(x) (((x) >> 4) & 0x1) +#define C_007EDC_LB_D1_VBLANK_INTERRUPT 0xFFFFFFEF +#define S_007EDC_LB_D2_VBLANK_INTERRUPT(x) (((x) & 0x1) << 5) +#define G_007EDC_LB_D2_VBLANK_INTERRUPT(x) (((x) >> 5) & 0x1) +#define C_007EDC_LB_D2_VBLANK_INTERRUPT 0xFFFFFFDF +#define S_007EDC_DACA_AUTODETECT_INTERRUPT(x) (((x) & 0x1) << 16) +#define G_007EDC_DACA_AUTODETECT_INTERRUPT(x) (((x) >> 16) & 0x1) +#define C_007EDC_DACA_AUTODETECT_INTERRUPT 0xFFFEFFFF +#define S_007EDC_DACB_AUTODETECT_INTERRUPT(x) (((x) & 0x1) << 17) +#define G_007EDC_DACB_AUTODETECT_INTERRUPT(x) (((x) >> 17) & 0x1) +#define C_007EDC_DACB_AUTODETECT_INTERRUPT 0xFFFDFFFF +#define S_007EDC_DC_HOT_PLUG_DETECT1_INTERRUPT(x) (((x) & 0x1) << 18) +#define G_007EDC_DC_HOT_PLUG_DETECT1_INTERRUPT(x) (((x) >> 18) & 0x1) +#define C_007EDC_DC_HOT_PLUG_DETECT1_INTERRUPT 0xFFFBFFFF +#define S_007EDC_DC_HOT_PLUG_DETECT2_INTERRUPT(x) (((x) & 0x1) << 19) +#define G_007EDC_DC_HOT_PLUG_DETECT2_INTERRUPT(x) (((x) >> 19) & 0x1) +#define C_007EDC_DC_HOT_PLUG_DETECT2_INTERRUPT 0xFFF7FFFF +#define R_007828_DACA_AUTODETECT_CONTROL 0x007828 +#define S_007828_DACA_AUTODETECT_MODE(x) (((x) & 0x3) << 0) +#define G_007828_DACA_AUTODETECT_MODE(x) (((x) >> 0) & 0x3) +#define C_007828_DACA_AUTODETECT_MODE 0xFFFFFFFC +#define S_007828_DACA_AUTODETECT_FRAME_TIME_COUNTER(x) (((x) & 0xff) << 8) +#define G_007828_DACA_AUTODETECT_FRAME_TIME_COUNTER(x) (((x) >> 8) & 0xff) +#define C_007828_DACA_AUTODETECT_FRAME_TIME_COUNTER 0xFFFF00FF +#define S_007828_DACA_AUTODETECT_CHECK_MASK(x) (((x) & 0x3) << 16) +#define G_007828_DACA_AUTODETECT_CHECK_MASK(x) (((x) >> 16) & 0x3) +#define C_007828_DACA_AUTODETECT_CHECK_MASK 0xFFFCFFFF +#define R_007838_DACA_AUTODETECT_INT_CONTROL 0x007838 +#define S_007838_DACA_AUTODETECT_ACK(x) (((x) & 0x1) << 0) +#define C_007838_DACA_DACA_AUTODETECT_ACK 0xFFFFFFFE +#define S_007838_DACA_AUTODETECT_INT_ENABLE(x) (((x) & 0x1) << 16) +#define G_007838_DACA_AUTODETECT_INT_ENABLE(x) (((x) >> 16) & 0x1) +#define C_007838_DACA_AUTODETECT_INT_ENABLE 0xFFFCFFFF +#define R_007A28_DACB_AUTODETECT_CONTROL 0x007A28 +#define S_007A28_DACB_AUTODETECT_MODE(x) (((x) & 0x3) << 0) +#define G_007A28_DACB_AUTODETECT_MODE(x) (((x) >> 0) & 0x3) +#define C_007A28_DACB_AUTODETECT_MODE 0xFFFFFFFC +#define S_007A28_DACB_AUTODETECT_FRAME_TIME_COUNTER(x) (((x) & 0xff) << 8) +#define G_007A28_DACB_AUTODETECT_FRAME_TIME_COUNTER(x) (((x) >> 8) & 0xff) +#define C_007A28_DACB_AUTODETECT_FRAME_TIME_COUNTER 0xFFFF00FF +#define S_007A28_DACB_AUTODETECT_CHECK_MASK(x) (((x) & 0x3) << 16) +#define G_007A28_DACB_AUTODETECT_CHECK_MASK(x) (((x) >> 16) & 0x3) +#define C_007A28_DACB_AUTODETECT_CHECK_MASK 0xFFFCFFFF +#define R_007A38_DACB_AUTODETECT_INT_CONTROL 0x007A38 +#define S_007A38_DACB_AUTODETECT_ACK(x) (((x) & 0x1) << 0) +#define C_007A38_DACB_DACA_AUTODETECT_ACK 0xFFFFFFFE +#define S_007A38_DACB_AUTODETECT_INT_ENABLE(x) (((x) & 0x1) << 16) +#define G_007A38_DACB_AUTODETECT_INT_ENABLE(x) (((x) >> 16) & 0x1) +#define C_007A38_DACB_AUTODETECT_INT_ENABLE 0xFFFCFFFF +#define R_007D00_DC_HOT_PLUG_DETECT1_CONTROL 0x007D00 +#define S_007D00_DC_HOT_PLUG_DETECT1_EN(x) (((x) & 0x1) << 0) +#define G_007D00_DC_HOT_PLUG_DETECT1_EN(x) (((x) >> 0) & 0x1) +#define C_007D00_DC_HOT_PLUG_DETECT1_EN 0xFFFFFFFE +#define R_007D04_DC_HOT_PLUG_DETECT1_INT_STATUS 0x007D04 +#define S_007D04_DC_HOT_PLUG_DETECT1_INT_STATUS(x) (((x) & 0x1) << 0) +#define G_007D04_DC_HOT_PLUG_DETECT1_INT_STATUS(x) (((x) >> 0) & 0x1) +#define C_007D04_DC_HOT_PLUG_DETECT1_INT_STATUS 0xFFFFFFFE +#define S_007D04_DC_HOT_PLUG_DETECT1_SENSE(x) (((x) & 0x1) << 1) +#define G_007D04_DC_HOT_PLUG_DETECT1_SENSE(x) (((x) >> 1) & 0x1) +#define C_007D04_DC_HOT_PLUG_DETECT1_SENSE 0xFFFFFFFD +#define R_007D08_DC_HOT_PLUG_DETECT1_INT_CONTROL 0x007D08 +#define S_007D08_DC_HOT_PLUG_DETECT1_INT_ACK(x) (((x) & 0x1) << 0) +#define C_007D08_DC_HOT_PLUG_DETECT1_INT_ACK 0xFFFFFFFE +#define S_007D08_DC_HOT_PLUG_DETECT1_INT_POLARITY(x) (((x) & 0x1) << 8) +#define G_007D08_DC_HOT_PLUG_DETECT1_INT_POLARITY(x) (((x) >> 8) & 0x1) +#define C_007D08_DC_HOT_PLUG_DETECT1_INT_POLARITY 0xFFFFFEFF +#define S_007D08_DC_HOT_PLUG_DETECT1_INT_EN(x) (((x) & 0x1) << 16) +#define G_007D08_DC_HOT_PLUG_DETECT1_INT_EN(x) (((x) >> 16) & 0x1) +#define C_007D08_DC_HOT_PLUG_DETECT1_INT_EN 0xFFFEFFFF +#define R_007D10_DC_HOT_PLUG_DETECT2_CONTROL 0x007D10 +#define S_007D10_DC_HOT_PLUG_DETECT2_EN(x) (((x) & 0x1) << 0) +#define G_007D10_DC_HOT_PLUG_DETECT2_EN(x) (((x) >> 0) & 0x1) +#define C_007D10_DC_HOT_PLUG_DETECT2_EN 0xFFFFFFFE +#define R_007D14_DC_HOT_PLUG_DETECT2_INT_STATUS 0x007D14 +#define S_007D14_DC_HOT_PLUG_DETECT2_INT_STATUS(x) (((x) & 0x1) << 0) +#define G_007D14_DC_HOT_PLUG_DETECT2_INT_STATUS(x) (((x) >> 0) & 0x1) +#define C_007D14_DC_HOT_PLUG_DETECT2_INT_STATUS 0xFFFFFFFE +#define S_007D14_DC_HOT_PLUG_DETECT2_SENSE(x) (((x) & 0x1) << 1) +#define G_007D14_DC_HOT_PLUG_DETECT2_SENSE(x) (((x) >> 1) & 0x1) +#define C_007D14_DC_HOT_PLUG_DETECT2_SENSE 0xFFFFFFFD +#define R_007D18_DC_HOT_PLUG_DETECT2_INT_CONTROL 0x007D18 +#define S_007D18_DC_HOT_PLUG_DETECT2_INT_ACK(x) (((x) & 0x1) << 0) +#define C_007D18_DC_HOT_PLUG_DETECT2_INT_ACK 0xFFFFFFFE +#define S_007D18_DC_HOT_PLUG_DETECT2_INT_POLARITY(x) (((x) & 0x1) << 8) +#define G_007D18_DC_HOT_PLUG_DETECT2_INT_POLARITY(x) (((x) >> 8) & 0x1) +#define C_007D18_DC_HOT_PLUG_DETECT2_INT_POLARITY 0xFFFFFEFF +#define S_007D18_DC_HOT_PLUG_DETECT2_INT_EN(x) (((x) & 0x1) << 16) +#define G_007D18_DC_HOT_PLUG_DETECT2_INT_EN(x) (((x) >> 16) & 0x1) +#define C_007D18_DC_HOT_PLUG_DETECT2_INT_EN 0xFFFEFFFF +#define R_007404_HDMI0_STATUS 0x007404 +#define S_007404_HDMI0_AZ_FORMAT_WTRIG(x) (((x) & 0x1) << 28) +#define G_007404_HDMI0_AZ_FORMAT_WTRIG(x) (((x) >> 28) & 0x1) +#define C_007404_HDMI0_AZ_FORMAT_WTRIG 0xEFFFFFFF +#define S_007404_HDMI0_AZ_FORMAT_WTRIG_INT(x) (((x) & 0x1) << 29) +#define G_007404_HDMI0_AZ_FORMAT_WTRIG_INT(x) (((x) >> 29) & 0x1) +#define C_007404_HDMI0_AZ_FORMAT_WTRIG_INT 0xDFFFFFFF +#define R_007408_HDMI0_AUDIO_PACKET_CONTROL 0x007408 +#define S_007408_HDMI0_AZ_FORMAT_WTRIG_MASK(x) (((x) & 0x1) << 28) +#define G_007408_HDMI0_AZ_FORMAT_WTRIG_MASK(x) (((x) >> 28) & 0x1) +#define C_007408_HDMI0_AZ_FORMAT_WTRIG_MASK 0xEFFFFFFF +#define S_007408_HDMI0_AZ_FORMAT_WTRIG_ACK(x) (((x) & 0x1) << 29) +#define G_007408_HDMI0_AZ_FORMAT_WTRIG_ACK(x) (((x) >> 29) & 0x1) +#define C_007408_HDMI0_AZ_FORMAT_WTRIG_ACK 0xDFFFFFFF + +/* MC registers */ +#define R_000000_MC_STATUS 0x000000 +#define S_000000_MC_IDLE(x) (((x) & 0x1) << 0) +#define G_000000_MC_IDLE(x) (((x) >> 0) & 0x1) +#define C_000000_MC_IDLE 0xFFFFFFFE +#define R_000004_MC_FB_LOCATION 0x000004 +#define S_000004_MC_FB_START(x) (((x) & 0xFFFF) << 0) +#define G_000004_MC_FB_START(x) (((x) >> 0) & 0xFFFF) +#define C_000004_MC_FB_START 0xFFFF0000 +#define S_000004_MC_FB_TOP(x) (((x) & 0xFFFF) << 16) +#define G_000004_MC_FB_TOP(x) (((x) >> 16) & 0xFFFF) +#define C_000004_MC_FB_TOP 0x0000FFFF +#define R_000005_MC_AGP_LOCATION 0x000005 +#define S_000005_MC_AGP_START(x) (((x) & 0xFFFF) << 0) +#define G_000005_MC_AGP_START(x) (((x) >> 0) & 0xFFFF) +#define C_000005_MC_AGP_START 0xFFFF0000 +#define S_000005_MC_AGP_TOP(x) (((x) & 0xFFFF) << 16) +#define G_000005_MC_AGP_TOP(x) (((x) >> 16) & 0xFFFF) +#define C_000005_MC_AGP_TOP 0x0000FFFF +#define R_000006_AGP_BASE 0x000006 +#define S_000006_AGP_BASE_ADDR(x) (((x) & 0xFFFFFFFF) << 0) +#define G_000006_AGP_BASE_ADDR(x) (((x) >> 0) & 0xFFFFFFFF) +#define C_000006_AGP_BASE_ADDR 0x00000000 +#define R_000007_AGP_BASE_2 0x000007 +#define S_000007_AGP_BASE_ADDR_2(x) (((x) & 0xF) << 0) +#define G_000007_AGP_BASE_ADDR_2(x) (((x) >> 0) & 0xF) +#define C_000007_AGP_BASE_ADDR_2 0xFFFFFFF0 +#define R_000009_MC_CNTL1 0x000009 +#define S_000009_ENABLE_PAGE_TABLES(x) (((x) & 0x1) << 26) +#define G_000009_ENABLE_PAGE_TABLES(x) (((x) >> 26) & 0x1) +#define C_000009_ENABLE_PAGE_TABLES 0xFBFFFFFF +/* FIXME don't know the various field size need feedback from AMD */ +#define R_000100_MC_PT0_CNTL 0x000100 +#define S_000100_ENABLE_PT(x) (((x) & 0x1) << 0) +#define G_000100_ENABLE_PT(x) (((x) >> 0) & 0x1) +#define C_000100_ENABLE_PT 0xFFFFFFFE +#define S_000100_EFFECTIVE_L2_CACHE_SIZE(x) (((x) & 0x7) << 15) +#define G_000100_EFFECTIVE_L2_CACHE_SIZE(x) (((x) >> 15) & 0x7) +#define C_000100_EFFECTIVE_L2_CACHE_SIZE 0xFFFC7FFF +#define S_000100_EFFECTIVE_L2_QUEUE_SIZE(x) (((x) & 0x7) << 21) +#define G_000100_EFFECTIVE_L2_QUEUE_SIZE(x) (((x) >> 21) & 0x7) +#define C_000100_EFFECTIVE_L2_QUEUE_SIZE 0xFF1FFFFF +#define S_000100_INVALIDATE_ALL_L1_TLBS(x) (((x) & 0x1) << 28) +#define G_000100_INVALIDATE_ALL_L1_TLBS(x) (((x) >> 28) & 0x1) +#define C_000100_INVALIDATE_ALL_L1_TLBS 0xEFFFFFFF +#define S_000100_INVALIDATE_L2_CACHE(x) (((x) & 0x1) << 29) +#define G_000100_INVALIDATE_L2_CACHE(x) (((x) >> 29) & 0x1) +#define C_000100_INVALIDATE_L2_CACHE 0xDFFFFFFF +#define R_000102_MC_PT0_CONTEXT0_CNTL 0x000102 +#define S_000102_ENABLE_PAGE_TABLE(x) (((x) & 0x1) << 0) +#define G_000102_ENABLE_PAGE_TABLE(x) (((x) >> 0) & 0x1) +#define C_000102_ENABLE_PAGE_TABLE 0xFFFFFFFE +#define S_000102_PAGE_TABLE_DEPTH(x) (((x) & 0x3) << 1) +#define G_000102_PAGE_TABLE_DEPTH(x) (((x) >> 1) & 0x3) +#define C_000102_PAGE_TABLE_DEPTH 0xFFFFFFF9 +#define V_000102_PAGE_TABLE_FLAT 0 +/* R600 documentation suggest that this should be a number of pages */ +#define R_000112_MC_PT0_SYSTEM_APERTURE_LOW_ADDR 0x000112 +#define R_000114_MC_PT0_SYSTEM_APERTURE_HIGH_ADDR 0x000114 +#define R_00011C_MC_PT0_CONTEXT0_DEFAULT_READ_ADDR 0x00011C +#define R_00012C_MC_PT0_CONTEXT0_FLAT_BASE_ADDR 0x00012C +#define R_00013C_MC_PT0_CONTEXT0_FLAT_START_ADDR 0x00013C +#define R_00014C_MC_PT0_CONTEXT0_FLAT_END_ADDR 0x00014C +#define R_00016C_MC_PT0_CLIENT0_CNTL 0x00016C +#define S_00016C_ENABLE_TRANSLATION_MODE_OVERRIDE(x) (((x) & 0x1) << 0) +#define G_00016C_ENABLE_TRANSLATION_MODE_OVERRIDE(x) (((x) >> 0) & 0x1) +#define C_00016C_ENABLE_TRANSLATION_MODE_OVERRIDE 0xFFFFFFFE +#define S_00016C_TRANSLATION_MODE_OVERRIDE(x) (((x) & 0x1) << 1) +#define G_00016C_TRANSLATION_MODE_OVERRIDE(x) (((x) >> 1) & 0x1) +#define C_00016C_TRANSLATION_MODE_OVERRIDE 0xFFFFFFFD +#define S_00016C_SYSTEM_ACCESS_MODE_MASK(x) (((x) & 0x3) << 8) +#define G_00016C_SYSTEM_ACCESS_MODE_MASK(x) (((x) >> 8) & 0x3) +#define C_00016C_SYSTEM_ACCESS_MODE_MASK 0xFFFFFCFF +#define V_00016C_SYSTEM_ACCESS_MODE_PA_ONLY 0 +#define V_00016C_SYSTEM_ACCESS_MODE_USE_SYS_MAP 1 +#define V_00016C_SYSTEM_ACCESS_MODE_IN_SYS 2 +#define V_00016C_SYSTEM_ACCESS_MODE_NOT_IN_SYS 3 +#define S_00016C_SYSTEM_APERTURE_UNMAPPED_ACCESS(x) (((x) & 0x1) << 10) +#define G_00016C_SYSTEM_APERTURE_UNMAPPED_ACCESS(x) (((x) >> 10) & 0x1) +#define C_00016C_SYSTEM_APERTURE_UNMAPPED_ACCESS 0xFFFFFBFF +#define V_00016C_SYSTEM_APERTURE_UNMAPPED_PASSTHROUGH 0 +#define V_00016C_SYSTEM_APERTURE_UNMAPPED_DEFAULT_PAGE 1 +#define S_00016C_EFFECTIVE_L1_CACHE_SIZE(x) (((x) & 0x7) << 11) +#define G_00016C_EFFECTIVE_L1_CACHE_SIZE(x) (((x) >> 11) & 0x7) +#define C_00016C_EFFECTIVE_L1_CACHE_SIZE 0xFFFFC7FF +#define S_00016C_ENABLE_FRAGMENT_PROCESSING(x) (((x) & 0x1) << 14) +#define G_00016C_ENABLE_FRAGMENT_PROCESSING(x) (((x) >> 14) & 0x1) +#define C_00016C_ENABLE_FRAGMENT_PROCESSING 0xFFFFBFFF +#define S_00016C_EFFECTIVE_L1_QUEUE_SIZE(x) (((x) & 0x7) << 15) +#define G_00016C_EFFECTIVE_L1_QUEUE_SIZE(x) (((x) >> 15) & 0x7) +#define C_00016C_EFFECTIVE_L1_QUEUE_SIZE 0xFFFC7FFF +#define S_00016C_INVALIDATE_L1_TLB(x) (((x) & 0x1) << 20) +#define G_00016C_INVALIDATE_L1_TLB(x) (((x) >> 20) & 0x1) +#define C_00016C_INVALIDATE_L1_TLB 0xFFEFFFFF + +#define R_006548_D1MODE_PRIORITY_A_CNT 0x006548 +#define S_006548_D1MODE_PRIORITY_MARK_A(x) (((x) & 0x7FFF) << 0) +#define G_006548_D1MODE_PRIORITY_MARK_A(x) (((x) >> 0) & 0x7FFF) +#define C_006548_D1MODE_PRIORITY_MARK_A 0xFFFF8000 +#define S_006548_D1MODE_PRIORITY_A_OFF(x) (((x) & 0x1) << 16) +#define G_006548_D1MODE_PRIORITY_A_OFF(x) (((x) >> 16) & 0x1) +#define C_006548_D1MODE_PRIORITY_A_OFF 0xFFFEFFFF +#define S_006548_D1MODE_PRIORITY_A_ALWAYS_ON(x) (((x) & 0x1) << 20) +#define G_006548_D1MODE_PRIORITY_A_ALWAYS_ON(x) (((x) >> 20) & 0x1) +#define C_006548_D1MODE_PRIORITY_A_ALWAYS_ON 0xFFEFFFFF +#define S_006548_D1MODE_PRIORITY_A_FORCE_MASK(x) (((x) & 0x1) << 24) +#define G_006548_D1MODE_PRIORITY_A_FORCE_MASK(x) (((x) >> 24) & 0x1) +#define C_006548_D1MODE_PRIORITY_A_FORCE_MASK 0xFEFFFFFF +#define R_00654C_D1MODE_PRIORITY_B_CNT 0x00654C +#define S_00654C_D1MODE_PRIORITY_MARK_B(x) (((x) & 0x7FFF) << 0) +#define G_00654C_D1MODE_PRIORITY_MARK_B(x) (((x) >> 0) & 0x7FFF) +#define C_00654C_D1MODE_PRIORITY_MARK_B 0xFFFF8000 +#define S_00654C_D1MODE_PRIORITY_B_OFF(x) (((x) & 0x1) << 16) +#define G_00654C_D1MODE_PRIORITY_B_OFF(x) (((x) >> 16) & 0x1) +#define C_00654C_D1MODE_PRIORITY_B_OFF 0xFFFEFFFF +#define S_00654C_D1MODE_PRIORITY_B_ALWAYS_ON(x) (((x) & 0x1) << 20) +#define G_00654C_D1MODE_PRIORITY_B_ALWAYS_ON(x) (((x) >> 20) & 0x1) +#define C_00654C_D1MODE_PRIORITY_B_ALWAYS_ON 0xFFEFFFFF +#define S_00654C_D1MODE_PRIORITY_B_FORCE_MASK(x) (((x) & 0x1) << 24) +#define G_00654C_D1MODE_PRIORITY_B_FORCE_MASK(x) (((x) >> 24) & 0x1) +#define C_00654C_D1MODE_PRIORITY_B_FORCE_MASK 0xFEFFFFFF +#define R_006D48_D2MODE_PRIORITY_A_CNT 0x006D48 +#define S_006D48_D2MODE_PRIORITY_MARK_A(x) (((x) & 0x7FFF) << 0) +#define G_006D48_D2MODE_PRIORITY_MARK_A(x) (((x) >> 0) & 0x7FFF) +#define C_006D48_D2MODE_PRIORITY_MARK_A 0xFFFF8000 +#define S_006D48_D2MODE_PRIORITY_A_OFF(x) (((x) & 0x1) << 16) +#define G_006D48_D2MODE_PRIORITY_A_OFF(x) (((x) >> 16) & 0x1) +#define C_006D48_D2MODE_PRIORITY_A_OFF 0xFFFEFFFF +#define S_006D48_D2MODE_PRIORITY_A_ALWAYS_ON(x) (((x) & 0x1) << 20) +#define G_006D48_D2MODE_PRIORITY_A_ALWAYS_ON(x) (((x) >> 20) & 0x1) +#define C_006D48_D2MODE_PRIORITY_A_ALWAYS_ON 0xFFEFFFFF +#define S_006D48_D2MODE_PRIORITY_A_FORCE_MASK(x) (((x) & 0x1) << 24) +#define G_006D48_D2MODE_PRIORITY_A_FORCE_MASK(x) (((x) >> 24) & 0x1) +#define C_006D48_D2MODE_PRIORITY_A_FORCE_MASK 0xFEFFFFFF +#define R_006D4C_D2MODE_PRIORITY_B_CNT 0x006D4C +#define S_006D4C_D2MODE_PRIORITY_MARK_B(x) (((x) & 0x7FFF) << 0) +#define G_006D4C_D2MODE_PRIORITY_MARK_B(x) (((x) >> 0) & 0x7FFF) +#define C_006D4C_D2MODE_PRIORITY_MARK_B 0xFFFF8000 +#define S_006D4C_D2MODE_PRIORITY_B_OFF(x) (((x) & 0x1) << 16) +#define G_006D4C_D2MODE_PRIORITY_B_OFF(x) (((x) >> 16) & 0x1) +#define C_006D4C_D2MODE_PRIORITY_B_OFF 0xFFFEFFFF +#define S_006D4C_D2MODE_PRIORITY_B_ALWAYS_ON(x) (((x) & 0x1) << 20) +#define G_006D4C_D2MODE_PRIORITY_B_ALWAYS_ON(x) (((x) >> 20) & 0x1) +#define C_006D4C_D2MODE_PRIORITY_B_ALWAYS_ON 0xFFEFFFFF +#define S_006D4C_D2MODE_PRIORITY_B_FORCE_MASK(x) (((x) & 0x1) << 24) +#define G_006D4C_D2MODE_PRIORITY_B_FORCE_MASK(x) (((x) >> 24) & 0x1) +#define C_006D4C_D2MODE_PRIORITY_B_FORCE_MASK 0xFEFFFFFF + +/* PLL regs */ +#define GENERAL_PWRMGT 0x8 +#define GLOBAL_PWRMGT_EN (1 << 0) +#define MOBILE_SU (1 << 2) +#define DYN_PWRMGT_SCLK_LENGTH 0xc +#define NORMAL_POWER_SCLK_HILEN(x) ((x) << 0) +#define NORMAL_POWER_SCLK_LOLEN(x) ((x) << 4) +#define REDUCED_POWER_SCLK_HILEN(x) ((x) << 8) +#define REDUCED_POWER_SCLK_LOLEN(x) ((x) << 12) +#define POWER_D1_SCLK_HILEN(x) ((x) << 16) +#define POWER_D1_SCLK_LOLEN(x) ((x) << 20) +#define STATIC_SCREEN_HILEN(x) ((x) << 24) +#define STATIC_SCREEN_LOLEN(x) ((x) << 28) +#define DYN_SCLK_VOL_CNTL 0xe +#define IO_CG_VOLTAGE_DROP (1 << 0) +#define VOLTAGE_DROP_SYNC (1 << 2) +#define VOLTAGE_DELAY_SEL(x) ((x) << 3) +#define HDP_DYN_CNTL 0x10 +#define HDP_FORCEON (1 << 0) +#define MC_HOST_DYN_CNTL 0x1e +#define MC_HOST_FORCEON (1 << 0) +#define DYN_BACKBIAS_CNTL 0x29 +#define IO_CG_BACKBIAS_EN (1 << 0) + +/* mmreg */ +#define DOUT_POWER_MANAGEMENT_CNTL 0x7ee0 +#define PWRDN_WAIT_BUSY_OFF (1 << 0) +#define PWRDN_WAIT_PWRSEQ_OFF (1 << 4) +#define PWRDN_WAIT_PPLL_OFF (1 << 8) +#define PWRUP_WAIT_PPLL_ON (1 << 12) +#define PWRUP_WAIT_MEM_INIT_DONE (1 << 16) +#define PM_ASSERT_RESET (1 << 20) +#define PM_PWRDN_PPLL (1 << 24) + +#endif diff --git a/sys/dev/drm2/radeon/rs690.c b/sys/dev/drm2/radeon/rs690.c new file mode 100644 index 00000000000..232053805f3 --- /dev/null +++ b/sys/dev/drm2/radeon/rs690.c @@ -0,0 +1,788 @@ +/* + * Copyright 2008 Advanced Micro Devices, Inc. + * Copyright 2008 Red Hat Inc. + * Copyright 2009 Jerome Glisse. + * + * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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. + * + * Authors: Dave Airlie + * Alex Deucher + * Jerome Glisse + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include "radeon.h" +#include "radeon_asic.h" +#include "atom.h" +#include "rs690d.h" + +int rs690_mc_wait_for_idle(struct radeon_device *rdev) +{ + unsigned i; + uint32_t tmp; + + for (i = 0; i < rdev->usec_timeout; i++) { + /* read MC_STATUS */ + tmp = RREG32_MC(R_000090_MC_SYSTEM_STATUS); + if (G_000090_MC_SYSTEM_IDLE(tmp)) + return 0; + DRM_UDELAY(1); + } + return -1; +} + +static void rs690_gpu_init(struct radeon_device *rdev) +{ + /* FIXME: is this correct ? */ + r420_pipes_init(rdev); + if (rs690_mc_wait_for_idle(rdev)) { + DRM_ERROR("Failed to wait MC idle while " + "programming pipes. Bad things might happen.\n"); + } +} + +union igp_info { + struct _ATOM_INTEGRATED_SYSTEM_INFO info; + struct _ATOM_INTEGRATED_SYSTEM_INFO_V2 info_v2; +}; + +void rs690_pm_info(struct radeon_device *rdev) +{ + int index = GetIndexIntoMasterTable(DATA, IntegratedSystemInfo); + union igp_info *info; + uint16_t data_offset; + uint8_t frev, crev; + fixed20_12 tmp; + + if (atom_parse_data_header(rdev->mode_info.atom_context, index, NULL, + &frev, &crev, &data_offset)) { + info = (union igp_info *)((uintptr_t)rdev->mode_info.atom_context->bios + data_offset); + + /* Get various system informations from bios */ + switch (crev) { + case 1: + tmp.full = dfixed_const(100); + rdev->pm.igp_sideport_mclk.full = dfixed_const(le32_to_cpu(info->info.ulBootUpMemoryClock)); + rdev->pm.igp_sideport_mclk.full = dfixed_div(rdev->pm.igp_sideport_mclk, tmp); + if (le16_to_cpu(info->info.usK8MemoryClock)) + rdev->pm.igp_system_mclk.full = dfixed_const(le16_to_cpu(info->info.usK8MemoryClock)); + else if (rdev->clock.default_mclk) { + rdev->pm.igp_system_mclk.full = dfixed_const(rdev->clock.default_mclk); + rdev->pm.igp_system_mclk.full = dfixed_div(rdev->pm.igp_system_mclk, tmp); + } else + rdev->pm.igp_system_mclk.full = dfixed_const(400); + rdev->pm.igp_ht_link_clk.full = dfixed_const(le16_to_cpu(info->info.usFSBClock)); + rdev->pm.igp_ht_link_width.full = dfixed_const(info->info.ucHTLinkWidth); + break; + case 2: + tmp.full = dfixed_const(100); + rdev->pm.igp_sideport_mclk.full = dfixed_const(le32_to_cpu(info->info_v2.ulBootUpSidePortClock)); + rdev->pm.igp_sideport_mclk.full = dfixed_div(rdev->pm.igp_sideport_mclk, tmp); + if (le32_to_cpu(info->info_v2.ulBootUpUMAClock)) + rdev->pm.igp_system_mclk.full = dfixed_const(le32_to_cpu(info->info_v2.ulBootUpUMAClock)); + else if (rdev->clock.default_mclk) + rdev->pm.igp_system_mclk.full = dfixed_const(rdev->clock.default_mclk); + else + rdev->pm.igp_system_mclk.full = dfixed_const(66700); + rdev->pm.igp_system_mclk.full = dfixed_div(rdev->pm.igp_system_mclk, tmp); + rdev->pm.igp_ht_link_clk.full = dfixed_const(le32_to_cpu(info->info_v2.ulHTLinkFreq)); + rdev->pm.igp_ht_link_clk.full = dfixed_div(rdev->pm.igp_ht_link_clk, tmp); + rdev->pm.igp_ht_link_width.full = dfixed_const(le16_to_cpu(info->info_v2.usMinHTLinkWidth)); + break; + default: + /* We assume the slower possible clock ie worst case */ + rdev->pm.igp_sideport_mclk.full = dfixed_const(200); + rdev->pm.igp_system_mclk.full = dfixed_const(200); + rdev->pm.igp_ht_link_clk.full = dfixed_const(1000); + rdev->pm.igp_ht_link_width.full = dfixed_const(8); + DRM_ERROR("No integrated system info for your GPU, using safe default\n"); + break; + } + } else { + /* We assume the slower possible clock ie worst case */ + rdev->pm.igp_sideport_mclk.full = dfixed_const(200); + rdev->pm.igp_system_mclk.full = dfixed_const(200); + rdev->pm.igp_ht_link_clk.full = dfixed_const(1000); + rdev->pm.igp_ht_link_width.full = dfixed_const(8); + DRM_ERROR("No integrated system info for your GPU, using safe default\n"); + } + /* Compute various bandwidth */ + /* k8_bandwidth = (memory_clk / 2) * 2 * 8 * 0.5 = memory_clk * 4 */ + tmp.full = dfixed_const(4); + rdev->pm.k8_bandwidth.full = dfixed_mul(rdev->pm.igp_system_mclk, tmp); + /* ht_bandwidth = ht_clk * 2 * ht_width / 8 * 0.8 + * = ht_clk * ht_width / 5 + */ + tmp.full = dfixed_const(5); + rdev->pm.ht_bandwidth.full = dfixed_mul(rdev->pm.igp_ht_link_clk, + rdev->pm.igp_ht_link_width); + rdev->pm.ht_bandwidth.full = dfixed_div(rdev->pm.ht_bandwidth, tmp); + if (tmp.full < rdev->pm.max_bandwidth.full) { + /* HT link is a limiting factor */ + rdev->pm.max_bandwidth.full = tmp.full; + } + /* sideport_bandwidth = (sideport_clk / 2) * 2 * 2 * 0.7 + * = (sideport_clk * 14) / 10 + */ + tmp.full = dfixed_const(14); + rdev->pm.sideport_bandwidth.full = dfixed_mul(rdev->pm.igp_sideport_mclk, tmp); + tmp.full = dfixed_const(10); + rdev->pm.sideport_bandwidth.full = dfixed_div(rdev->pm.sideport_bandwidth, tmp); +} + +static void rs690_mc_init(struct radeon_device *rdev) +{ + u64 base; + + rs400_gart_adjust_size(rdev); + rdev->mc.vram_is_ddr = true; + rdev->mc.vram_width = 128; + rdev->mc.real_vram_size = RREG32(RADEON_CONFIG_MEMSIZE); + rdev->mc.mc_vram_size = rdev->mc.real_vram_size; + rdev->mc.aper_base = drm_get_resource_start(rdev->ddev, 0); + rdev->mc.aper_size = drm_get_resource_len(rdev->ddev, 0); + rdev->mc.visible_vram_size = rdev->mc.aper_size; + base = RREG32_MC(R_000100_MCCFG_FB_LOCATION); + base = G_000100_MC_FB_START(base) << 16; + rdev->mc.igp_sideport_enabled = radeon_atombios_sideport_present(rdev); + rs690_pm_info(rdev); + radeon_vram_location(rdev, &rdev->mc, base); + rdev->mc.gtt_base_align = rdev->mc.gtt_size - 1; + radeon_gtt_location(rdev, &rdev->mc); + radeon_update_bandwidth_info(rdev); +} + +void rs690_line_buffer_adjust(struct radeon_device *rdev, + struct drm_display_mode *mode1, + struct drm_display_mode *mode2) +{ + u32 tmp; + + /* + * Line Buffer Setup + * There is a single line buffer shared by both display controllers. + * R_006520_DC_LB_MEMORY_SPLIT controls how that line buffer is shared between + * the display controllers. The paritioning can either be done + * manually or via one of four preset allocations specified in bits 1:0: + * 0 - line buffer is divided in half and shared between crtc + * 1 - D1 gets 3/4 of the line buffer, D2 gets 1/4 + * 2 - D1 gets the whole buffer + * 3 - D1 gets 1/4 of the line buffer, D2 gets 3/4 + * Setting bit 2 of R_006520_DC_LB_MEMORY_SPLIT controls switches to manual + * allocation mode. In manual allocation mode, D1 always starts at 0, + * D1 end/2 is specified in bits 14:4; D2 allocation follows D1. + */ + tmp = RREG32(R_006520_DC_LB_MEMORY_SPLIT) & C_006520_DC_LB_MEMORY_SPLIT; + tmp &= ~C_006520_DC_LB_MEMORY_SPLIT_MODE; + /* auto */ + if (mode1 && mode2) { + if (mode1->hdisplay > mode2->hdisplay) { + if (mode1->hdisplay > 2560) + tmp |= V_006520_DC_LB_MEMORY_SPLIT_D1_3Q_D2_1Q; + else + tmp |= V_006520_DC_LB_MEMORY_SPLIT_D1HALF_D2HALF; + } else if (mode2->hdisplay > mode1->hdisplay) { + if (mode2->hdisplay > 2560) + tmp |= V_006520_DC_LB_MEMORY_SPLIT_D1_1Q_D2_3Q; + else + tmp |= V_006520_DC_LB_MEMORY_SPLIT_D1HALF_D2HALF; + } else + tmp |= V_006520_DC_LB_MEMORY_SPLIT_D1HALF_D2HALF; + } else if (mode1) { + tmp |= V_006520_DC_LB_MEMORY_SPLIT_D1_ONLY; + } else if (mode2) { + tmp |= V_006520_DC_LB_MEMORY_SPLIT_D1_1Q_D2_3Q; + } + WREG32(R_006520_DC_LB_MEMORY_SPLIT, tmp); +} + +struct rs690_watermark { + u32 lb_request_fifo_depth; + fixed20_12 num_line_pair; + fixed20_12 estimated_width; + fixed20_12 worst_case_latency; + fixed20_12 consumption_rate; + fixed20_12 active_time; + fixed20_12 dbpp; + fixed20_12 priority_mark_max; + fixed20_12 priority_mark; + fixed20_12 sclk; +}; + +static void rs690_crtc_bandwidth_compute(struct radeon_device *rdev, + struct radeon_crtc *crtc, + struct rs690_watermark *wm) +{ + struct drm_display_mode *mode = &crtc->base.mode; + fixed20_12 a, b, c; + fixed20_12 pclk, request_fifo_depth, tolerable_latency, estimated_width; + fixed20_12 consumption_time, line_time, chunk_time, read_delay_latency; + + if (!crtc->base.enabled) { + /* FIXME: wouldn't it better to set priority mark to maximum */ + wm->lb_request_fifo_depth = 4; + return; + } + + if (crtc->vsc.full > dfixed_const(2)) + wm->num_line_pair.full = dfixed_const(2); + else + wm->num_line_pair.full = dfixed_const(1); + + b.full = dfixed_const(mode->crtc_hdisplay); + c.full = dfixed_const(256); + a.full = dfixed_div(b, c); + request_fifo_depth.full = dfixed_mul(a, wm->num_line_pair); + request_fifo_depth.full = dfixed_ceil(request_fifo_depth); + if (a.full < dfixed_const(4)) { + wm->lb_request_fifo_depth = 4; + } else { + wm->lb_request_fifo_depth = dfixed_trunc(request_fifo_depth); + } + + /* Determine consumption rate + * pclk = pixel clock period(ns) = 1000 / (mode.clock / 1000) + * vtaps = number of vertical taps, + * vsc = vertical scaling ratio, defined as source/destination + * hsc = horizontal scaling ration, defined as source/destination + */ + a.full = dfixed_const(mode->clock); + b.full = dfixed_const(1000); + a.full = dfixed_div(a, b); + pclk.full = dfixed_div(b, a); + if (crtc->rmx_type != RMX_OFF) { + b.full = dfixed_const(2); + if (crtc->vsc.full > b.full) + b.full = crtc->vsc.full; + b.full = dfixed_mul(b, crtc->hsc); + c.full = dfixed_const(2); + b.full = dfixed_div(b, c); + consumption_time.full = dfixed_div(pclk, b); + } else { + consumption_time.full = pclk.full; + } + a.full = dfixed_const(1); + wm->consumption_rate.full = dfixed_div(a, consumption_time); + + + /* Determine line time + * LineTime = total time for one line of displayhtotal + * LineTime = total number of horizontal pixels + * pclk = pixel clock period(ns) + */ + a.full = dfixed_const(crtc->base.mode.crtc_htotal); + line_time.full = dfixed_mul(a, pclk); + + /* Determine active time + * ActiveTime = time of active region of display within one line, + * hactive = total number of horizontal active pixels + * htotal = total number of horizontal pixels + */ + a.full = dfixed_const(crtc->base.mode.crtc_htotal); + b.full = dfixed_const(crtc->base.mode.crtc_hdisplay); + wm->active_time.full = dfixed_mul(line_time, b); + wm->active_time.full = dfixed_div(wm->active_time, a); + + /* Maximun bandwidth is the minimun bandwidth of all component */ + rdev->pm.max_bandwidth = rdev->pm.core_bandwidth; + if (rdev->mc.igp_sideport_enabled) { + if (rdev->pm.max_bandwidth.full > rdev->pm.sideport_bandwidth.full && + rdev->pm.sideport_bandwidth.full) + rdev->pm.max_bandwidth = rdev->pm.sideport_bandwidth; +#ifdef DUMBBELL_WIP + read_delay_latency.full = dfixed_const(370 * 800 * 1000); +#endif /* DUMBBELL_WIP */ + read_delay_latency.full = UINT_MAX; + read_delay_latency.full = dfixed_div(read_delay_latency, + rdev->pm.igp_sideport_mclk); + } else { + if (rdev->pm.max_bandwidth.full > rdev->pm.k8_bandwidth.full && + rdev->pm.k8_bandwidth.full) + rdev->pm.max_bandwidth = rdev->pm.k8_bandwidth; + if (rdev->pm.max_bandwidth.full > rdev->pm.ht_bandwidth.full && + rdev->pm.ht_bandwidth.full) + rdev->pm.max_bandwidth = rdev->pm.ht_bandwidth; + read_delay_latency.full = dfixed_const(5000); + } + + /* sclk = system clocks(ns) = 1000 / max_bandwidth / 16 */ + a.full = dfixed_const(16); + rdev->pm.sclk.full = dfixed_mul(rdev->pm.max_bandwidth, a); + a.full = dfixed_const(1000); + rdev->pm.sclk.full = dfixed_div(a, rdev->pm.sclk); + /* Determine chunk time + * ChunkTime = the time it takes the DCP to send one chunk of data + * to the LB which consists of pipeline delay and inter chunk gap + * sclk = system clock(ns) + */ + a.full = dfixed_const(256 * 13); + chunk_time.full = dfixed_mul(rdev->pm.sclk, a); + a.full = dfixed_const(10); + chunk_time.full = dfixed_div(chunk_time, a); + + /* Determine the worst case latency + * NumLinePair = Number of line pairs to request(1=2 lines, 2=4 lines) + * WorstCaseLatency = worst case time from urgent to when the MC starts + * to return data + * READ_DELAY_IDLE_MAX = constant of 1us + * ChunkTime = time it takes the DCP to send one chunk of data to the LB + * which consists of pipeline delay and inter chunk gap + */ + if (dfixed_trunc(wm->num_line_pair) > 1) { + a.full = dfixed_const(3); + wm->worst_case_latency.full = dfixed_mul(a, chunk_time); + wm->worst_case_latency.full += read_delay_latency.full; + } else { + a.full = dfixed_const(2); + wm->worst_case_latency.full = dfixed_mul(a, chunk_time); + wm->worst_case_latency.full += read_delay_latency.full; + } + + /* Determine the tolerable latency + * TolerableLatency = Any given request has only 1 line time + * for the data to be returned + * LBRequestFifoDepth = Number of chunk requests the LB can + * put into the request FIFO for a display + * LineTime = total time for one line of display + * ChunkTime = the time it takes the DCP to send one chunk + * of data to the LB which consists of + * pipeline delay and inter chunk gap + */ + if ((2+wm->lb_request_fifo_depth) >= dfixed_trunc(request_fifo_depth)) { + tolerable_latency.full = line_time.full; + } else { + tolerable_latency.full = dfixed_const(wm->lb_request_fifo_depth - 2); + tolerable_latency.full = request_fifo_depth.full - tolerable_latency.full; + tolerable_latency.full = dfixed_mul(tolerable_latency, chunk_time); + tolerable_latency.full = line_time.full - tolerable_latency.full; + } + /* We assume worst case 32bits (4 bytes) */ + wm->dbpp.full = dfixed_const(4 * 8); + + /* Determine the maximum priority mark + * width = viewport width in pixels + */ + a.full = dfixed_const(16); + wm->priority_mark_max.full = dfixed_const(crtc->base.mode.crtc_hdisplay); + wm->priority_mark_max.full = dfixed_div(wm->priority_mark_max, a); + wm->priority_mark_max.full = dfixed_ceil(wm->priority_mark_max); + + /* Determine estimated width */ + estimated_width.full = tolerable_latency.full - wm->worst_case_latency.full; + estimated_width.full = dfixed_div(estimated_width, consumption_time); + if (dfixed_trunc(estimated_width) > crtc->base.mode.crtc_hdisplay) { + wm->priority_mark.full = dfixed_const(10); + } else { + a.full = dfixed_const(16); + wm->priority_mark.full = dfixed_div(estimated_width, a); + wm->priority_mark.full = dfixed_ceil(wm->priority_mark); + wm->priority_mark.full = wm->priority_mark_max.full - wm->priority_mark.full; + } +} + +void rs690_bandwidth_update(struct radeon_device *rdev) +{ + struct drm_display_mode *mode0 = NULL; + struct drm_display_mode *mode1 = NULL; + struct rs690_watermark wm0; + struct rs690_watermark wm1; + u32 tmp; + u32 d1mode_priority_a_cnt = S_006548_D1MODE_PRIORITY_A_OFF(1); + u32 d2mode_priority_a_cnt = S_006548_D1MODE_PRIORITY_A_OFF(1); + fixed20_12 priority_mark02, priority_mark12, fill_rate; + fixed20_12 a, b; + + radeon_update_display_priority(rdev); + + if (rdev->mode_info.crtcs[0]->base.enabled) + mode0 = &rdev->mode_info.crtcs[0]->base.mode; + if (rdev->mode_info.crtcs[1]->base.enabled) + mode1 = &rdev->mode_info.crtcs[1]->base.mode; + /* + * Set display0/1 priority up in the memory controller for + * modes if the user specifies HIGH for displaypriority + * option. + */ + if ((rdev->disp_priority == 2) && + ((rdev->family == CHIP_RS690) || (rdev->family == CHIP_RS740))) { + tmp = RREG32_MC(R_000104_MC_INIT_MISC_LAT_TIMER); + tmp &= C_000104_MC_DISP0R_INIT_LAT; + tmp &= C_000104_MC_DISP1R_INIT_LAT; + if (mode0) + tmp |= S_000104_MC_DISP0R_INIT_LAT(1); + if (mode1) + tmp |= S_000104_MC_DISP1R_INIT_LAT(1); + WREG32_MC(R_000104_MC_INIT_MISC_LAT_TIMER, tmp); + } + rs690_line_buffer_adjust(rdev, mode0, mode1); + + if ((rdev->family == CHIP_RS690) || (rdev->family == CHIP_RS740)) + WREG32(R_006C9C_DCP_CONTROL, 0); + if ((rdev->family == CHIP_RS780) || (rdev->family == CHIP_RS880)) + WREG32(R_006C9C_DCP_CONTROL, 2); + + rs690_crtc_bandwidth_compute(rdev, rdev->mode_info.crtcs[0], &wm0); + rs690_crtc_bandwidth_compute(rdev, rdev->mode_info.crtcs[1], &wm1); + + tmp = (wm0.lb_request_fifo_depth - 1); + tmp |= (wm1.lb_request_fifo_depth - 1) << 16; + WREG32(R_006D58_LB_MAX_REQ_OUTSTANDING, tmp); + + if (mode0 && mode1) { + if (dfixed_trunc(wm0.dbpp) > 64) + a.full = dfixed_mul(wm0.dbpp, wm0.num_line_pair); + else + a.full = wm0.num_line_pair.full; + if (dfixed_trunc(wm1.dbpp) > 64) + b.full = dfixed_mul(wm1.dbpp, wm1.num_line_pair); + else + b.full = wm1.num_line_pair.full; + a.full += b.full; + fill_rate.full = dfixed_div(wm0.sclk, a); + if (wm0.consumption_rate.full > fill_rate.full) { + b.full = wm0.consumption_rate.full - fill_rate.full; + b.full = dfixed_mul(b, wm0.active_time); + a.full = dfixed_mul(wm0.worst_case_latency, + wm0.consumption_rate); + a.full = a.full + b.full; + b.full = dfixed_const(16 * 1000); + priority_mark02.full = dfixed_div(a, b); + } else { + a.full = dfixed_mul(wm0.worst_case_latency, + wm0.consumption_rate); + b.full = dfixed_const(16 * 1000); + priority_mark02.full = dfixed_div(a, b); + } + if (wm1.consumption_rate.full > fill_rate.full) { + b.full = wm1.consumption_rate.full - fill_rate.full; + b.full = dfixed_mul(b, wm1.active_time); + a.full = dfixed_mul(wm1.worst_case_latency, + wm1.consumption_rate); + a.full = a.full + b.full; + b.full = dfixed_const(16 * 1000); + priority_mark12.full = dfixed_div(a, b); + } else { + a.full = dfixed_mul(wm1.worst_case_latency, + wm1.consumption_rate); + b.full = dfixed_const(16 * 1000); + priority_mark12.full = dfixed_div(a, b); + } + if (wm0.priority_mark.full > priority_mark02.full) + priority_mark02.full = wm0.priority_mark.full; + if (dfixed_trunc(priority_mark02) < 0) + priority_mark02.full = 0; + if (wm0.priority_mark_max.full > priority_mark02.full) + priority_mark02.full = wm0.priority_mark_max.full; + if (wm1.priority_mark.full > priority_mark12.full) + priority_mark12.full = wm1.priority_mark.full; + if (dfixed_trunc(priority_mark12) < 0) + priority_mark12.full = 0; + if (wm1.priority_mark_max.full > priority_mark12.full) + priority_mark12.full = wm1.priority_mark_max.full; + d1mode_priority_a_cnt = dfixed_trunc(priority_mark02); + d2mode_priority_a_cnt = dfixed_trunc(priority_mark12); + if (rdev->disp_priority == 2) { + d1mode_priority_a_cnt |= S_006548_D1MODE_PRIORITY_A_ALWAYS_ON(1); + d2mode_priority_a_cnt |= S_006D48_D2MODE_PRIORITY_A_ALWAYS_ON(1); + } + } else if (mode0) { + if (dfixed_trunc(wm0.dbpp) > 64) + a.full = dfixed_mul(wm0.dbpp, wm0.num_line_pair); + else + a.full = wm0.num_line_pair.full; + fill_rate.full = dfixed_div(wm0.sclk, a); + if (wm0.consumption_rate.full > fill_rate.full) { + b.full = wm0.consumption_rate.full - fill_rate.full; + b.full = dfixed_mul(b, wm0.active_time); + a.full = dfixed_mul(wm0.worst_case_latency, + wm0.consumption_rate); + a.full = a.full + b.full; + b.full = dfixed_const(16 * 1000); + priority_mark02.full = dfixed_div(a, b); + } else { + a.full = dfixed_mul(wm0.worst_case_latency, + wm0.consumption_rate); + b.full = dfixed_const(16 * 1000); + priority_mark02.full = dfixed_div(a, b); + } + if (wm0.priority_mark.full > priority_mark02.full) + priority_mark02.full = wm0.priority_mark.full; + if (dfixed_trunc(priority_mark02) < 0) + priority_mark02.full = 0; + if (wm0.priority_mark_max.full > priority_mark02.full) + priority_mark02.full = wm0.priority_mark_max.full; + d1mode_priority_a_cnt = dfixed_trunc(priority_mark02); + if (rdev->disp_priority == 2) + d1mode_priority_a_cnt |= S_006548_D1MODE_PRIORITY_A_ALWAYS_ON(1); + } else if (mode1) { + if (dfixed_trunc(wm1.dbpp) > 64) + a.full = dfixed_mul(wm1.dbpp, wm1.num_line_pair); + else + a.full = wm1.num_line_pair.full; + fill_rate.full = dfixed_div(wm1.sclk, a); + if (wm1.consumption_rate.full > fill_rate.full) { + b.full = wm1.consumption_rate.full - fill_rate.full; + b.full = dfixed_mul(b, wm1.active_time); + a.full = dfixed_mul(wm1.worst_case_latency, + wm1.consumption_rate); + a.full = a.full + b.full; + b.full = dfixed_const(16 * 1000); + priority_mark12.full = dfixed_div(a, b); + } else { + a.full = dfixed_mul(wm1.worst_case_latency, + wm1.consumption_rate); + b.full = dfixed_const(16 * 1000); + priority_mark12.full = dfixed_div(a, b); + } + if (wm1.priority_mark.full > priority_mark12.full) + priority_mark12.full = wm1.priority_mark.full; + if (dfixed_trunc(priority_mark12) < 0) + priority_mark12.full = 0; + if (wm1.priority_mark_max.full > priority_mark12.full) + priority_mark12.full = wm1.priority_mark_max.full; + d2mode_priority_a_cnt = dfixed_trunc(priority_mark12); + if (rdev->disp_priority == 2) + d2mode_priority_a_cnt |= S_006D48_D2MODE_PRIORITY_A_ALWAYS_ON(1); + } + + WREG32(R_006548_D1MODE_PRIORITY_A_CNT, d1mode_priority_a_cnt); + WREG32(R_00654C_D1MODE_PRIORITY_B_CNT, d1mode_priority_a_cnt); + WREG32(R_006D48_D2MODE_PRIORITY_A_CNT, d2mode_priority_a_cnt); + WREG32(R_006D4C_D2MODE_PRIORITY_B_CNT, d2mode_priority_a_cnt); +} + +uint32_t rs690_mc_rreg(struct radeon_device *rdev, uint32_t reg) +{ + uint32_t r; + + WREG32(R_000078_MC_INDEX, S_000078_MC_IND_ADDR(reg)); + r = RREG32(R_00007C_MC_DATA); + WREG32(R_000078_MC_INDEX, ~C_000078_MC_IND_ADDR); + return r; +} + +void rs690_mc_wreg(struct radeon_device *rdev, uint32_t reg, uint32_t v) +{ + WREG32(R_000078_MC_INDEX, S_000078_MC_IND_ADDR(reg) | + S_000078_MC_IND_WR_EN(1)); + WREG32(R_00007C_MC_DATA, v); + WREG32(R_000078_MC_INDEX, 0x7F); +} + +static void rs690_mc_program(struct radeon_device *rdev) +{ + struct rv515_mc_save save; + + /* Stops all mc clients */ + rv515_mc_stop(rdev, &save); + + /* Wait for mc idle */ + if (rs690_mc_wait_for_idle(rdev)) + dev_warn(rdev->dev, "Wait MC idle timeout before updating MC.\n"); + /* Program MC, should be a 32bits limited address space */ + WREG32_MC(R_000100_MCCFG_FB_LOCATION, + S_000100_MC_FB_START(rdev->mc.vram_start >> 16) | + S_000100_MC_FB_TOP(rdev->mc.vram_end >> 16)); + WREG32(R_000134_HDP_FB_LOCATION, + S_000134_HDP_FB_START(rdev->mc.vram_start >> 16)); + + rv515_mc_resume(rdev, &save); +} + +static int rs690_startup(struct radeon_device *rdev) +{ + int r; + + rs690_mc_program(rdev); + /* Resume clock */ + rv515_clock_startup(rdev); + /* Initialize GPU configuration (# pipes, ...) */ + rs690_gpu_init(rdev); + /* Initialize GART (initialize after TTM so we can allocate + * memory through TTM but finalize after TTM) */ + r = rs400_gart_enable(rdev); + if (r) + return r; + + /* allocate wb buffer */ + r = radeon_wb_init(rdev); + if (r) + return r; + + r = radeon_fence_driver_start_ring(rdev, RADEON_RING_TYPE_GFX_INDEX); + if (r) { + dev_err(rdev->dev, "failed initializing CP fences (%d).\n", r); + return r; + } + + /* Enable IRQ */ + rs600_irq_set(rdev); + rdev->config.r300.hdp_cntl = RREG32(RADEON_HOST_PATH_CNTL); + /* 1M ring buffer */ + r = r100_cp_init(rdev, 1024 * 1024); + if (r) { + dev_err(rdev->dev, "failed initializing CP (%d).\n", r); + return r; + } + + r = radeon_ib_pool_init(rdev); + if (r) { + dev_err(rdev->dev, "IB initialization failed (%d).\n", r); + return r; + } + + r = r600_audio_init(rdev); + if (r) { + dev_err(rdev->dev, "failed initializing audio\n"); + return r; + } + + return 0; +} + +int rs690_resume(struct radeon_device *rdev) +{ + int r; + + /* Make sur GART are not working */ + rs400_gart_disable(rdev); + /* Resume clock before doing reset */ + rv515_clock_startup(rdev); + /* Reset gpu before posting otherwise ATOM will enter infinite loop */ + if (radeon_asic_reset(rdev)) { + dev_warn(rdev->dev, "GPU reset failed ! (0xE40=0x%08X, 0x7C0=0x%08X)\n", + RREG32(R_000E40_RBBM_STATUS), + RREG32(R_0007C0_CP_STAT)); + } + /* post */ + atom_asic_init(rdev->mode_info.atom_context); + /* Resume clock after posting */ + rv515_clock_startup(rdev); + /* Initialize surface registers */ + radeon_surface_init(rdev); + + rdev->accel_working = true; + r = rs690_startup(rdev); + if (r) { + rdev->accel_working = false; + } + return r; +} + +int rs690_suspend(struct radeon_device *rdev) +{ + r600_audio_fini(rdev); + r100_cp_disable(rdev); + radeon_wb_disable(rdev); + rs600_irq_disable(rdev); + rs400_gart_disable(rdev); + return 0; +} + +void rs690_fini(struct radeon_device *rdev) +{ + r600_audio_fini(rdev); + r100_cp_fini(rdev); + radeon_wb_fini(rdev); + radeon_ib_pool_fini(rdev); + radeon_gem_fini(rdev); + rs400_gart_fini(rdev); + radeon_irq_kms_fini(rdev); + radeon_fence_driver_fini(rdev); + radeon_bo_fini(rdev); + radeon_atombios_fini(rdev); + free(rdev->bios, DRM_MEM_DRIVER); + rdev->bios = NULL; +} + +int rs690_init(struct radeon_device *rdev) +{ + int r; + + /* Disable VGA */ + rv515_vga_render_disable(rdev); + /* Initialize scratch registers */ + radeon_scratch_init(rdev); + /* Initialize surface registers */ + radeon_surface_init(rdev); + /* restore some register to sane defaults */ + r100_restore_sanity(rdev); + /* TODO: disable VGA need to use VGA request */ + /* BIOS*/ + if (!radeon_get_bios(rdev)) { + if (ASIC_IS_AVIVO(rdev)) + return -EINVAL; + } + if (rdev->is_atom_bios) { + r = radeon_atombios_init(rdev); + if (r) + return r; + } else { + dev_err(rdev->dev, "Expecting atombios for RV515 GPU\n"); + return -EINVAL; + } + /* Reset gpu before posting otherwise ATOM will enter infinite loop */ + if (radeon_asic_reset(rdev)) { + dev_warn(rdev->dev, + "GPU reset failed ! (0xE40=0x%08X, 0x7C0=0x%08X)\n", + RREG32(R_000E40_RBBM_STATUS), + RREG32(R_0007C0_CP_STAT)); + } + /* check if cards are posted or not */ + if (radeon_boot_test_post_card(rdev) == false) + return -EINVAL; + + /* Initialize clocks */ + radeon_get_clock_info(rdev->ddev); + /* initialize memory controller */ + rs690_mc_init(rdev); + rv515_debugfs(rdev); + /* Fence driver */ + r = radeon_fence_driver_init(rdev); + if (r) + return r; + r = radeon_irq_kms_init(rdev); + if (r) + return r; + /* Memory manager */ + r = radeon_bo_init(rdev); + if (r) + return r; + r = rs400_gart_init(rdev); + if (r) + return r; + rs600_set_safe_registers(rdev); + + rdev->accel_working = true; + r = rs690_startup(rdev); + if (r) { + /* Somethings want wront with the accel init stop accel */ + dev_err(rdev->dev, "Disabling GPU acceleration\n"); + r100_cp_fini(rdev); + radeon_wb_fini(rdev); + radeon_ib_pool_fini(rdev); + rs400_gart_fini(rdev); + radeon_irq_kms_fini(rdev); + rdev->accel_working = false; + } + return 0; +} diff --git a/sys/dev/drm2/radeon/rs690d.h b/sys/dev/drm2/radeon/rs690d.h new file mode 100644 index 00000000000..af90d7cb1d0 --- /dev/null +++ b/sys/dev/drm2/radeon/rs690d.h @@ -0,0 +1,313 @@ +/* + * Copyright 2008 Advanced Micro Devices, Inc. + * Copyright 2008 Red Hat Inc. + * Copyright 2009 Jerome Glisse. + * + * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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. + * + * Authors: Dave Airlie + * Alex Deucher + * Jerome Glisse + */ +#ifndef __RS690D_H__ +#define __RS690D_H__ + +#include +__FBSDID("$FreeBSD$"); + +/* Registers */ +#define R_000078_MC_INDEX 0x000078 +#define S_000078_MC_IND_ADDR(x) (((x) & 0x1FF) << 0) +#define G_000078_MC_IND_ADDR(x) (((x) >> 0) & 0x1FF) +#define C_000078_MC_IND_ADDR 0xFFFFFE00 +#define S_000078_MC_IND_WR_EN(x) (((x) & 0x1) << 9) +#define G_000078_MC_IND_WR_EN(x) (((x) >> 9) & 0x1) +#define C_000078_MC_IND_WR_EN 0xFFFFFDFF +#define R_00007C_MC_DATA 0x00007C +#define S_00007C_MC_DATA(x) (((x) & 0xFFFFFFFF) << 0) +#define G_00007C_MC_DATA(x) (((x) >> 0) & 0xFFFFFFFF) +#define C_00007C_MC_DATA 0x00000000 +#define R_0000F8_CONFIG_MEMSIZE 0x0000F8 +#define S_0000F8_CONFIG_MEMSIZE(x) (((x) & 0xFFFFFFFF) << 0) +#define G_0000F8_CONFIG_MEMSIZE(x) (((x) >> 0) & 0xFFFFFFFF) +#define C_0000F8_CONFIG_MEMSIZE 0x00000000 +#define R_000134_HDP_FB_LOCATION 0x000134 +#define S_000134_HDP_FB_START(x) (((x) & 0xFFFF) << 0) +#define G_000134_HDP_FB_START(x) (((x) >> 0) & 0xFFFF) +#define C_000134_HDP_FB_START 0xFFFF0000 +#define R_0007C0_CP_STAT 0x0007C0 +#define S_0007C0_MRU_BUSY(x) (((x) & 0x1) << 0) +#define G_0007C0_MRU_BUSY(x) (((x) >> 0) & 0x1) +#define C_0007C0_MRU_BUSY 0xFFFFFFFE +#define S_0007C0_MWU_BUSY(x) (((x) & 0x1) << 1) +#define G_0007C0_MWU_BUSY(x) (((x) >> 1) & 0x1) +#define C_0007C0_MWU_BUSY 0xFFFFFFFD +#define S_0007C0_RSIU_BUSY(x) (((x) & 0x1) << 2) +#define G_0007C0_RSIU_BUSY(x) (((x) >> 2) & 0x1) +#define C_0007C0_RSIU_BUSY 0xFFFFFFFB +#define S_0007C0_RCIU_BUSY(x) (((x) & 0x1) << 3) +#define G_0007C0_RCIU_BUSY(x) (((x) >> 3) & 0x1) +#define C_0007C0_RCIU_BUSY 0xFFFFFFF7 +#define S_0007C0_CSF_PRIMARY_BUSY(x) (((x) & 0x1) << 9) +#define G_0007C0_CSF_PRIMARY_BUSY(x) (((x) >> 9) & 0x1) +#define C_0007C0_CSF_PRIMARY_BUSY 0xFFFFFDFF +#define S_0007C0_CSF_INDIRECT_BUSY(x) (((x) & 0x1) << 10) +#define G_0007C0_CSF_INDIRECT_BUSY(x) (((x) >> 10) & 0x1) +#define C_0007C0_CSF_INDIRECT_BUSY 0xFFFFFBFF +#define S_0007C0_CSQ_PRIMARY_BUSY(x) (((x) & 0x1) << 11) +#define G_0007C0_CSQ_PRIMARY_BUSY(x) (((x) >> 11) & 0x1) +#define C_0007C0_CSQ_PRIMARY_BUSY 0xFFFFF7FF +#define S_0007C0_CSQ_INDIRECT_BUSY(x) (((x) & 0x1) << 12) +#define G_0007C0_CSQ_INDIRECT_BUSY(x) (((x) >> 12) & 0x1) +#define C_0007C0_CSQ_INDIRECT_BUSY 0xFFFFEFFF +#define S_0007C0_CSI_BUSY(x) (((x) & 0x1) << 13) +#define G_0007C0_CSI_BUSY(x) (((x) >> 13) & 0x1) +#define C_0007C0_CSI_BUSY 0xFFFFDFFF +#define S_0007C0_CSF_INDIRECT2_BUSY(x) (((x) & 0x1) << 14) +#define G_0007C0_CSF_INDIRECT2_BUSY(x) (((x) >> 14) & 0x1) +#define C_0007C0_CSF_INDIRECT2_BUSY 0xFFFFBFFF +#define S_0007C0_CSQ_INDIRECT2_BUSY(x) (((x) & 0x1) << 15) +#define G_0007C0_CSQ_INDIRECT2_BUSY(x) (((x) >> 15) & 0x1) +#define C_0007C0_CSQ_INDIRECT2_BUSY 0xFFFF7FFF +#define S_0007C0_GUIDMA_BUSY(x) (((x) & 0x1) << 28) +#define G_0007C0_GUIDMA_BUSY(x) (((x) >> 28) & 0x1) +#define C_0007C0_GUIDMA_BUSY 0xEFFFFFFF +#define S_0007C0_VIDDMA_BUSY(x) (((x) & 0x1) << 29) +#define G_0007C0_VIDDMA_BUSY(x) (((x) >> 29) & 0x1) +#define C_0007C0_VIDDMA_BUSY 0xDFFFFFFF +#define S_0007C0_CMDSTRM_BUSY(x) (((x) & 0x1) << 30) +#define G_0007C0_CMDSTRM_BUSY(x) (((x) >> 30) & 0x1) +#define C_0007C0_CMDSTRM_BUSY 0xBFFFFFFF +#define S_0007C0_CP_BUSY(x) (((x) & 0x1) << 31) +#define G_0007C0_CP_BUSY(x) (((x) >> 31) & 0x1) +#define C_0007C0_CP_BUSY 0x7FFFFFFF +#define R_000E40_RBBM_STATUS 0x000E40 +#define S_000E40_CMDFIFO_AVAIL(x) (((x) & 0x7F) << 0) +#define G_000E40_CMDFIFO_AVAIL(x) (((x) >> 0) & 0x7F) +#define C_000E40_CMDFIFO_AVAIL 0xFFFFFF80 +#define S_000E40_HIRQ_ON_RBB(x) (((x) & 0x1) << 8) +#define G_000E40_HIRQ_ON_RBB(x) (((x) >> 8) & 0x1) +#define C_000E40_HIRQ_ON_RBB 0xFFFFFEFF +#define S_000E40_CPRQ_ON_RBB(x) (((x) & 0x1) << 9) +#define G_000E40_CPRQ_ON_RBB(x) (((x) >> 9) & 0x1) +#define C_000E40_CPRQ_ON_RBB 0xFFFFFDFF +#define S_000E40_CFRQ_ON_RBB(x) (((x) & 0x1) << 10) +#define G_000E40_CFRQ_ON_RBB(x) (((x) >> 10) & 0x1) +#define C_000E40_CFRQ_ON_RBB 0xFFFFFBFF +#define S_000E40_HIRQ_IN_RTBUF(x) (((x) & 0x1) << 11) +#define G_000E40_HIRQ_IN_RTBUF(x) (((x) >> 11) & 0x1) +#define C_000E40_HIRQ_IN_RTBUF 0xFFFFF7FF +#define S_000E40_CPRQ_IN_RTBUF(x) (((x) & 0x1) << 12) +#define G_000E40_CPRQ_IN_RTBUF(x) (((x) >> 12) & 0x1) +#define C_000E40_CPRQ_IN_RTBUF 0xFFFFEFFF +#define S_000E40_CFRQ_IN_RTBUF(x) (((x) & 0x1) << 13) +#define G_000E40_CFRQ_IN_RTBUF(x) (((x) >> 13) & 0x1) +#define C_000E40_CFRQ_IN_RTBUF 0xFFFFDFFF +#define S_000E40_CF_PIPE_BUSY(x) (((x) & 0x1) << 14) +#define G_000E40_CF_PIPE_BUSY(x) (((x) >> 14) & 0x1) +#define C_000E40_CF_PIPE_BUSY 0xFFFFBFFF +#define S_000E40_ENG_EV_BUSY(x) (((x) & 0x1) << 15) +#define G_000E40_ENG_EV_BUSY(x) (((x) >> 15) & 0x1) +#define C_000E40_ENG_EV_BUSY 0xFFFF7FFF +#define S_000E40_CP_CMDSTRM_BUSY(x) (((x) & 0x1) << 16) +#define G_000E40_CP_CMDSTRM_BUSY(x) (((x) >> 16) & 0x1) +#define C_000E40_CP_CMDSTRM_BUSY 0xFFFEFFFF +#define S_000E40_E2_BUSY(x) (((x) & 0x1) << 17) +#define G_000E40_E2_BUSY(x) (((x) >> 17) & 0x1) +#define C_000E40_E2_BUSY 0xFFFDFFFF +#define S_000E40_RB2D_BUSY(x) (((x) & 0x1) << 18) +#define G_000E40_RB2D_BUSY(x) (((x) >> 18) & 0x1) +#define C_000E40_RB2D_BUSY 0xFFFBFFFF +#define S_000E40_RB3D_BUSY(x) (((x) & 0x1) << 19) +#define G_000E40_RB3D_BUSY(x) (((x) >> 19) & 0x1) +#define C_000E40_RB3D_BUSY 0xFFF7FFFF +#define S_000E40_VAP_BUSY(x) (((x) & 0x1) << 20) +#define G_000E40_VAP_BUSY(x) (((x) >> 20) & 0x1) +#define C_000E40_VAP_BUSY 0xFFEFFFFF +#define S_000E40_RE_BUSY(x) (((x) & 0x1) << 21) +#define G_000E40_RE_BUSY(x) (((x) >> 21) & 0x1) +#define C_000E40_RE_BUSY 0xFFDFFFFF +#define S_000E40_TAM_BUSY(x) (((x) & 0x1) << 22) +#define G_000E40_TAM_BUSY(x) (((x) >> 22) & 0x1) +#define C_000E40_TAM_BUSY 0xFFBFFFFF +#define S_000E40_TDM_BUSY(x) (((x) & 0x1) << 23) +#define G_000E40_TDM_BUSY(x) (((x) >> 23) & 0x1) +#define C_000E40_TDM_BUSY 0xFF7FFFFF +#define S_000E40_PB_BUSY(x) (((x) & 0x1) << 24) +#define G_000E40_PB_BUSY(x) (((x) >> 24) & 0x1) +#define C_000E40_PB_BUSY 0xFEFFFFFF +#define S_000E40_TIM_BUSY(x) (((x) & 0x1) << 25) +#define G_000E40_TIM_BUSY(x) (((x) >> 25) & 0x1) +#define C_000E40_TIM_BUSY 0xFDFFFFFF +#define S_000E40_GA_BUSY(x) (((x) & 0x1) << 26) +#define G_000E40_GA_BUSY(x) (((x) >> 26) & 0x1) +#define C_000E40_GA_BUSY 0xFBFFFFFF +#define S_000E40_CBA2D_BUSY(x) (((x) & 0x1) << 27) +#define G_000E40_CBA2D_BUSY(x) (((x) >> 27) & 0x1) +#define C_000E40_CBA2D_BUSY 0xF7FFFFFF +#define S_000E40_GUI_ACTIVE(x) (((x) & 0x1) << 31) +#define G_000E40_GUI_ACTIVE(x) (((x) >> 31) & 0x1) +#define C_000E40_GUI_ACTIVE 0x7FFFFFFF +#define R_006520_DC_LB_MEMORY_SPLIT 0x006520 +#define S_006520_DC_LB_MEMORY_SPLIT(x) (((x) & 0x3) << 0) +#define G_006520_DC_LB_MEMORY_SPLIT(x) (((x) >> 0) & 0x3) +#define C_006520_DC_LB_MEMORY_SPLIT 0xFFFFFFFC +#define S_006520_DC_LB_MEMORY_SPLIT_MODE(x) (((x) & 0x1) << 2) +#define G_006520_DC_LB_MEMORY_SPLIT_MODE(x) (((x) >> 2) & 0x1) +#define C_006520_DC_LB_MEMORY_SPLIT_MODE 0xFFFFFFFB +#define V_006520_DC_LB_MEMORY_SPLIT_D1HALF_D2HALF 0 +#define V_006520_DC_LB_MEMORY_SPLIT_D1_3Q_D2_1Q 1 +#define V_006520_DC_LB_MEMORY_SPLIT_D1_ONLY 2 +#define V_006520_DC_LB_MEMORY_SPLIT_D1_1Q_D2_3Q 3 +#define S_006520_DC_LB_DISP1_END_ADR(x) (((x) & 0x7FF) << 4) +#define G_006520_DC_LB_DISP1_END_ADR(x) (((x) >> 4) & 0x7FF) +#define C_006520_DC_LB_DISP1_END_ADR 0xFFFF800F +#define R_006548_D1MODE_PRIORITY_A_CNT 0x006548 +#define S_006548_D1MODE_PRIORITY_MARK_A(x) (((x) & 0x7FFF) << 0) +#define G_006548_D1MODE_PRIORITY_MARK_A(x) (((x) >> 0) & 0x7FFF) +#define C_006548_D1MODE_PRIORITY_MARK_A 0xFFFF8000 +#define S_006548_D1MODE_PRIORITY_A_OFF(x) (((x) & 0x1) << 16) +#define G_006548_D1MODE_PRIORITY_A_OFF(x) (((x) >> 16) & 0x1) +#define C_006548_D1MODE_PRIORITY_A_OFF 0xFFFEFFFF +#define S_006548_D1MODE_PRIORITY_A_ALWAYS_ON(x) (((x) & 0x1) << 20) +#define G_006548_D1MODE_PRIORITY_A_ALWAYS_ON(x) (((x) >> 20) & 0x1) +#define C_006548_D1MODE_PRIORITY_A_ALWAYS_ON 0xFFEFFFFF +#define S_006548_D1MODE_PRIORITY_A_FORCE_MASK(x) (((x) & 0x1) << 24) +#define G_006548_D1MODE_PRIORITY_A_FORCE_MASK(x) (((x) >> 24) & 0x1) +#define C_006548_D1MODE_PRIORITY_A_FORCE_MASK 0xFEFFFFFF +#define R_00654C_D1MODE_PRIORITY_B_CNT 0x00654C +#define S_00654C_D1MODE_PRIORITY_MARK_B(x) (((x) & 0x7FFF) << 0) +#define G_00654C_D1MODE_PRIORITY_MARK_B(x) (((x) >> 0) & 0x7FFF) +#define C_00654C_D1MODE_PRIORITY_MARK_B 0xFFFF8000 +#define S_00654C_D1MODE_PRIORITY_B_OFF(x) (((x) & 0x1) << 16) +#define G_00654C_D1MODE_PRIORITY_B_OFF(x) (((x) >> 16) & 0x1) +#define C_00654C_D1MODE_PRIORITY_B_OFF 0xFFFEFFFF +#define S_00654C_D1MODE_PRIORITY_B_ALWAYS_ON(x) (((x) & 0x1) << 20) +#define G_00654C_D1MODE_PRIORITY_B_ALWAYS_ON(x) (((x) >> 20) & 0x1) +#define C_00654C_D1MODE_PRIORITY_B_ALWAYS_ON 0xFFEFFFFF +#define S_00654C_D1MODE_PRIORITY_B_FORCE_MASK(x) (((x) & 0x1) << 24) +#define G_00654C_D1MODE_PRIORITY_B_FORCE_MASK(x) (((x) >> 24) & 0x1) +#define C_00654C_D1MODE_PRIORITY_B_FORCE_MASK 0xFEFFFFFF +#define R_006C9C_DCP_CONTROL 0x006C9C +#define R_006D48_D2MODE_PRIORITY_A_CNT 0x006D48 +#define S_006D48_D2MODE_PRIORITY_MARK_A(x) (((x) & 0x7FFF) << 0) +#define G_006D48_D2MODE_PRIORITY_MARK_A(x) (((x) >> 0) & 0x7FFF) +#define C_006D48_D2MODE_PRIORITY_MARK_A 0xFFFF8000 +#define S_006D48_D2MODE_PRIORITY_A_OFF(x) (((x) & 0x1) << 16) +#define G_006D48_D2MODE_PRIORITY_A_OFF(x) (((x) >> 16) & 0x1) +#define C_006D48_D2MODE_PRIORITY_A_OFF 0xFFFEFFFF +#define S_006D48_D2MODE_PRIORITY_A_ALWAYS_ON(x) (((x) & 0x1) << 20) +#define G_006D48_D2MODE_PRIORITY_A_ALWAYS_ON(x) (((x) >> 20) & 0x1) +#define C_006D48_D2MODE_PRIORITY_A_ALWAYS_ON 0xFFEFFFFF +#define S_006D48_D2MODE_PRIORITY_A_FORCE_MASK(x) (((x) & 0x1) << 24) +#define G_006D48_D2MODE_PRIORITY_A_FORCE_MASK(x) (((x) >> 24) & 0x1) +#define C_006D48_D2MODE_PRIORITY_A_FORCE_MASK 0xFEFFFFFF +#define R_006D4C_D2MODE_PRIORITY_B_CNT 0x006D4C +#define S_006D4C_D2MODE_PRIORITY_MARK_B(x) (((x) & 0x7FFF) << 0) +#define G_006D4C_D2MODE_PRIORITY_MARK_B(x) (((x) >> 0) & 0x7FFF) +#define C_006D4C_D2MODE_PRIORITY_MARK_B 0xFFFF8000 +#define S_006D4C_D2MODE_PRIORITY_B_OFF(x) (((x) & 0x1) << 16) +#define G_006D4C_D2MODE_PRIORITY_B_OFF(x) (((x) >> 16) & 0x1) +#define C_006D4C_D2MODE_PRIORITY_B_OFF 0xFFFEFFFF +#define S_006D4C_D2MODE_PRIORITY_B_ALWAYS_ON(x) (((x) & 0x1) << 20) +#define G_006D4C_D2MODE_PRIORITY_B_ALWAYS_ON(x) (((x) >> 20) & 0x1) +#define C_006D4C_D2MODE_PRIORITY_B_ALWAYS_ON 0xFFEFFFFF +#define S_006D4C_D2MODE_PRIORITY_B_FORCE_MASK(x) (((x) & 0x1) << 24) +#define G_006D4C_D2MODE_PRIORITY_B_FORCE_MASK(x) (((x) >> 24) & 0x1) +#define C_006D4C_D2MODE_PRIORITY_B_FORCE_MASK 0xFEFFFFFF +#define R_006D58_LB_MAX_REQ_OUTSTANDING 0x006D58 +#define S_006D58_LB_D1_MAX_REQ_OUTSTANDING(x) (((x) & 0xF) << 0) +#define G_006D58_LB_D1_MAX_REQ_OUTSTANDING(x) (((x) >> 0) & 0xF) +#define C_006D58_LB_D1_MAX_REQ_OUTSTANDING 0xFFFFFFF0 +#define S_006D58_LB_D2_MAX_REQ_OUTSTANDING(x) (((x) & 0xF) << 16) +#define G_006D58_LB_D2_MAX_REQ_OUTSTANDING(x) (((x) >> 16) & 0xF) +#define C_006D58_LB_D2_MAX_REQ_OUTSTANDING 0xFFF0FFFF + + +#define R_000090_MC_SYSTEM_STATUS 0x000090 +#define S_000090_MC_SYSTEM_IDLE(x) (((x) & 0x1) << 0) +#define G_000090_MC_SYSTEM_IDLE(x) (((x) >> 0) & 0x1) +#define C_000090_MC_SYSTEM_IDLE 0xFFFFFFFE +#define S_000090_MC_SEQUENCER_IDLE(x) (((x) & 0x1) << 1) +#define G_000090_MC_SEQUENCER_IDLE(x) (((x) >> 1) & 0x1) +#define C_000090_MC_SEQUENCER_IDLE 0xFFFFFFFD +#define S_000090_MC_ARBITER_IDLE(x) (((x) & 0x1) << 2) +#define G_000090_MC_ARBITER_IDLE(x) (((x) >> 2) & 0x1) +#define C_000090_MC_ARBITER_IDLE 0xFFFFFFFB +#define S_000090_MC_SELECT_PM(x) (((x) & 0x1) << 3) +#define G_000090_MC_SELECT_PM(x) (((x) >> 3) & 0x1) +#define C_000090_MC_SELECT_PM 0xFFFFFFF7 +#define S_000090_RESERVED4(x) (((x) & 0xF) << 4) +#define G_000090_RESERVED4(x) (((x) >> 4) & 0xF) +#define C_000090_RESERVED4 0xFFFFFF0F +#define S_000090_RESERVED8(x) (((x) & 0xF) << 8) +#define G_000090_RESERVED8(x) (((x) >> 8) & 0xF) +#define C_000090_RESERVED8 0xFFFFF0FF +#define S_000090_RESERVED12(x) (((x) & 0xF) << 12) +#define G_000090_RESERVED12(x) (((x) >> 12) & 0xF) +#define C_000090_RESERVED12 0xFFFF0FFF +#define S_000090_MCA_INIT_EXECUTED(x) (((x) & 0x1) << 16) +#define G_000090_MCA_INIT_EXECUTED(x) (((x) >> 16) & 0x1) +#define C_000090_MCA_INIT_EXECUTED 0xFFFEFFFF +#define S_000090_MCA_IDLE(x) (((x) & 0x1) << 17) +#define G_000090_MCA_IDLE(x) (((x) >> 17) & 0x1) +#define C_000090_MCA_IDLE 0xFFFDFFFF +#define S_000090_MCA_SEQ_IDLE(x) (((x) & 0x1) << 18) +#define G_000090_MCA_SEQ_IDLE(x) (((x) >> 18) & 0x1) +#define C_000090_MCA_SEQ_IDLE 0xFFFBFFFF +#define S_000090_MCA_ARB_IDLE(x) (((x) & 0x1) << 19) +#define G_000090_MCA_ARB_IDLE(x) (((x) >> 19) & 0x1) +#define C_000090_MCA_ARB_IDLE 0xFFF7FFFF +#define S_000090_RESERVED20(x) (((x) & 0xFFF) << 20) +#define G_000090_RESERVED20(x) (((x) >> 20) & 0xFFF) +#define C_000090_RESERVED20 0x000FFFFF +#define R_000100_MCCFG_FB_LOCATION 0x000100 +#define S_000100_MC_FB_START(x) (((x) & 0xFFFF) << 0) +#define G_000100_MC_FB_START(x) (((x) >> 0) & 0xFFFF) +#define C_000100_MC_FB_START 0xFFFF0000 +#define S_000100_MC_FB_TOP(x) (((x) & 0xFFFF) << 16) +#define G_000100_MC_FB_TOP(x) (((x) >> 16) & 0xFFFF) +#define C_000100_MC_FB_TOP 0x0000FFFF +#define R_000104_MC_INIT_MISC_LAT_TIMER 0x000104 +#define S_000104_MC_CPR_INIT_LAT(x) (((x) & 0xF) << 0) +#define G_000104_MC_CPR_INIT_LAT(x) (((x) >> 0) & 0xF) +#define C_000104_MC_CPR_INIT_LAT 0xFFFFFFF0 +#define S_000104_MC_VF_INIT_LAT(x) (((x) & 0xF) << 4) +#define G_000104_MC_VF_INIT_LAT(x) (((x) >> 4) & 0xF) +#define C_000104_MC_VF_INIT_LAT 0xFFFFFF0F +#define S_000104_MC_DISP0R_INIT_LAT(x) (((x) & 0xF) << 8) +#define G_000104_MC_DISP0R_INIT_LAT(x) (((x) >> 8) & 0xF) +#define C_000104_MC_DISP0R_INIT_LAT 0xFFFFF0FF +#define S_000104_MC_DISP1R_INIT_LAT(x) (((x) & 0xF) << 12) +#define G_000104_MC_DISP1R_INIT_LAT(x) (((x) >> 12) & 0xF) +#define C_000104_MC_DISP1R_INIT_LAT 0xFFFF0FFF +#define S_000104_MC_FIXED_INIT_LAT(x) (((x) & 0xF) << 16) +#define G_000104_MC_FIXED_INIT_LAT(x) (((x) >> 16) & 0xF) +#define C_000104_MC_FIXED_INIT_LAT 0xFFF0FFFF +#define S_000104_MC_E2R_INIT_LAT(x) (((x) & 0xF) << 20) +#define G_000104_MC_E2R_INIT_LAT(x) (((x) >> 20) & 0xF) +#define C_000104_MC_E2R_INIT_LAT 0xFF0FFFFF +#define S_000104_SAME_PAGE_PRIO(x) (((x) & 0xF) << 24) +#define G_000104_SAME_PAGE_PRIO(x) (((x) >> 24) & 0xF) +#define C_000104_SAME_PAGE_PRIO 0xF0FFFFFF +#define S_000104_MC_GLOBW_INIT_LAT(x) (((x) & 0xF) << 28) +#define G_000104_MC_GLOBW_INIT_LAT(x) (((x) >> 28) & 0xF) +#define C_000104_MC_GLOBW_INIT_LAT 0x0FFFFFFF + +#endif diff --git a/sys/dev/drm2/radeon/rv200d.h b/sys/dev/drm2/radeon/rv200d.h new file mode 100644 index 00000000000..0fec87cffae --- /dev/null +++ b/sys/dev/drm2/radeon/rv200d.h @@ -0,0 +1,39 @@ +/* + * Copyright 2008 Advanced Micro Devices, Inc. + * Copyright 2008 Red Hat Inc. + * Copyright 2009 Jerome Glisse. + * + * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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. + * + * Authors: Dave Airlie + * Alex Deucher + * Jerome Glisse + */ +#ifndef __RV200D_H__ +#define __RV200D_H__ + +#include +__FBSDID("$FreeBSD$"); + +#define R_00015C_AGP_BASE_2 0x00015C +#define S_00015C_AGP_BASE_ADDR_2(x) (((x) & 0xF) << 0) +#define G_00015C_AGP_BASE_ADDR_2(x) (((x) >> 0) & 0xF) +#define C_00015C_AGP_BASE_ADDR_2 0xFFFFFFF0 + +#endif diff --git a/sys/dev/drm2/radeon/rv250d.h b/sys/dev/drm2/radeon/rv250d.h new file mode 100644 index 00000000000..612273559ab --- /dev/null +++ b/sys/dev/drm2/radeon/rv250d.h @@ -0,0 +1,126 @@ +/* + * Copyright 2008 Advanced Micro Devices, Inc. + * Copyright 2008 Red Hat Inc. + * Copyright 2009 Jerome Glisse. + * + * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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. + * + * Authors: Dave Airlie + * Alex Deucher + * Jerome Glisse + */ +#ifndef __RV250D_H__ +#define __RV250D_H__ + +#include +__FBSDID("$FreeBSD$"); + +#define R_00000D_SCLK_CNTL_M6 0x00000D +#define S_00000D_SCLK_SRC_SEL(x) (((x) & 0x7) << 0) +#define G_00000D_SCLK_SRC_SEL(x) (((x) >> 0) & 0x7) +#define C_00000D_SCLK_SRC_SEL 0xFFFFFFF8 +#define S_00000D_CP_MAX_DYN_STOP_LAT(x) (((x) & 0x1) << 3) +#define G_00000D_CP_MAX_DYN_STOP_LAT(x) (((x) >> 3) & 0x1) +#define C_00000D_CP_MAX_DYN_STOP_LAT 0xFFFFFFF7 +#define S_00000D_HDP_MAX_DYN_STOP_LAT(x) (((x) & 0x1) << 4) +#define G_00000D_HDP_MAX_DYN_STOP_LAT(x) (((x) >> 4) & 0x1) +#define C_00000D_HDP_MAX_DYN_STOP_LAT 0xFFFFFFEF +#define S_00000D_TV_MAX_DYN_STOP_LAT(x) (((x) & 0x1) << 5) +#define G_00000D_TV_MAX_DYN_STOP_LAT(x) (((x) >> 5) & 0x1) +#define C_00000D_TV_MAX_DYN_STOP_LAT 0xFFFFFFDF +#define S_00000D_E2_MAX_DYN_STOP_LAT(x) (((x) & 0x1) << 6) +#define G_00000D_E2_MAX_DYN_STOP_LAT(x) (((x) >> 6) & 0x1) +#define C_00000D_E2_MAX_DYN_STOP_LAT 0xFFFFFFBF +#define S_00000D_SE_MAX_DYN_STOP_LAT(x) (((x) & 0x1) << 7) +#define G_00000D_SE_MAX_DYN_STOP_LAT(x) (((x) >> 7) & 0x1) +#define C_00000D_SE_MAX_DYN_STOP_LAT 0xFFFFFF7F +#define S_00000D_IDCT_MAX_DYN_STOP_LAT(x) (((x) & 0x1) << 8) +#define G_00000D_IDCT_MAX_DYN_STOP_LAT(x) (((x) >> 8) & 0x1) +#define C_00000D_IDCT_MAX_DYN_STOP_LAT 0xFFFFFEFF +#define S_00000D_VIP_MAX_DYN_STOP_LAT(x) (((x) & 0x1) << 9) +#define G_00000D_VIP_MAX_DYN_STOP_LAT(x) (((x) >> 9) & 0x1) +#define C_00000D_VIP_MAX_DYN_STOP_LAT 0xFFFFFDFF +#define S_00000D_RE_MAX_DYN_STOP_LAT(x) (((x) & 0x1) << 10) +#define G_00000D_RE_MAX_DYN_STOP_LAT(x) (((x) >> 10) & 0x1) +#define C_00000D_RE_MAX_DYN_STOP_LAT 0xFFFFFBFF +#define S_00000D_PB_MAX_DYN_STOP_LAT(x) (((x) & 0x1) << 11) +#define G_00000D_PB_MAX_DYN_STOP_LAT(x) (((x) >> 11) & 0x1) +#define C_00000D_PB_MAX_DYN_STOP_LAT 0xFFFFF7FF +#define S_00000D_TAM_MAX_DYN_STOP_LAT(x) (((x) & 0x1) << 12) +#define G_00000D_TAM_MAX_DYN_STOP_LAT(x) (((x) >> 12) & 0x1) +#define C_00000D_TAM_MAX_DYN_STOP_LAT 0xFFFFEFFF +#define S_00000D_TDM_MAX_DYN_STOP_LAT(x) (((x) & 0x1) << 13) +#define G_00000D_TDM_MAX_DYN_STOP_LAT(x) (((x) >> 13) & 0x1) +#define C_00000D_TDM_MAX_DYN_STOP_LAT 0xFFFFDFFF +#define S_00000D_RB_MAX_DYN_STOP_LAT(x) (((x) & 0x1) << 14) +#define G_00000D_RB_MAX_DYN_STOP_LAT(x) (((x) >> 14) & 0x1) +#define C_00000D_RB_MAX_DYN_STOP_LAT 0xFFFFBFFF +#define S_00000D_FORCE_DISP2(x) (((x) & 0x1) << 15) +#define G_00000D_FORCE_DISP2(x) (((x) >> 15) & 0x1) +#define C_00000D_FORCE_DISP2 0xFFFF7FFF +#define S_00000D_FORCE_CP(x) (((x) & 0x1) << 16) +#define G_00000D_FORCE_CP(x) (((x) >> 16) & 0x1) +#define C_00000D_FORCE_CP 0xFFFEFFFF +#define S_00000D_FORCE_HDP(x) (((x) & 0x1) << 17) +#define G_00000D_FORCE_HDP(x) (((x) >> 17) & 0x1) +#define C_00000D_FORCE_HDP 0xFFFDFFFF +#define S_00000D_FORCE_DISP1(x) (((x) & 0x1) << 18) +#define G_00000D_FORCE_DISP1(x) (((x) >> 18) & 0x1) +#define C_00000D_FORCE_DISP1 0xFFFBFFFF +#define S_00000D_FORCE_TOP(x) (((x) & 0x1) << 19) +#define G_00000D_FORCE_TOP(x) (((x) >> 19) & 0x1) +#define C_00000D_FORCE_TOP 0xFFF7FFFF +#define S_00000D_FORCE_E2(x) (((x) & 0x1) << 20) +#define G_00000D_FORCE_E2(x) (((x) >> 20) & 0x1) +#define C_00000D_FORCE_E2 0xFFEFFFFF +#define S_00000D_FORCE_SE(x) (((x) & 0x1) << 21) +#define G_00000D_FORCE_SE(x) (((x) >> 21) & 0x1) +#define C_00000D_FORCE_SE 0xFFDFFFFF +#define S_00000D_FORCE_IDCT(x) (((x) & 0x1) << 22) +#define G_00000D_FORCE_IDCT(x) (((x) >> 22) & 0x1) +#define C_00000D_FORCE_IDCT 0xFFBFFFFF +#define S_00000D_FORCE_VIP(x) (((x) & 0x1) << 23) +#define G_00000D_FORCE_VIP(x) (((x) >> 23) & 0x1) +#define C_00000D_FORCE_VIP 0xFF7FFFFF +#define S_00000D_FORCE_RE(x) (((x) & 0x1) << 24) +#define G_00000D_FORCE_RE(x) (((x) >> 24) & 0x1) +#define C_00000D_FORCE_RE 0xFEFFFFFF +#define S_00000D_FORCE_PB(x) (((x) & 0x1) << 25) +#define G_00000D_FORCE_PB(x) (((x) >> 25) & 0x1) +#define C_00000D_FORCE_PB 0xFDFFFFFF +#define S_00000D_FORCE_TAM(x) (((x) & 0x1) << 26) +#define G_00000D_FORCE_TAM(x) (((x) >> 26) & 0x1) +#define C_00000D_FORCE_TAM 0xFBFFFFFF +#define S_00000D_FORCE_TDM(x) (((x) & 0x1) << 27) +#define G_00000D_FORCE_TDM(x) (((x) >> 27) & 0x1) +#define C_00000D_FORCE_TDM 0xF7FFFFFF +#define S_00000D_FORCE_RB(x) (((x) & 0x1) << 28) +#define G_00000D_FORCE_RB(x) (((x) >> 28) & 0x1) +#define C_00000D_FORCE_RB 0xEFFFFFFF +#define S_00000D_FORCE_TV_SCLK(x) (((x) & 0x1) << 29) +#define G_00000D_FORCE_TV_SCLK(x) (((x) >> 29) & 0x1) +#define C_00000D_FORCE_TV_SCLK 0xDFFFFFFF +#define S_00000D_FORCE_SUBPIC(x) (((x) & 0x1) << 30) +#define G_00000D_FORCE_SUBPIC(x) (((x) >> 30) & 0x1) +#define C_00000D_FORCE_SUBPIC 0xBFFFFFFF +#define S_00000D_FORCE_OV0(x) (((x) & 0x1) << 31) +#define G_00000D_FORCE_OV0(x) (((x) >> 31) & 0x1) +#define C_00000D_FORCE_OV0 0x7FFFFFFF + +#endif diff --git a/sys/dev/drm2/radeon/rv350d.h b/sys/dev/drm2/radeon/rv350d.h new file mode 100644 index 00000000000..dbeaa36e0a0 --- /dev/null +++ b/sys/dev/drm2/radeon/rv350d.h @@ -0,0 +1,55 @@ +/* + * Copyright 2008 Advanced Micro Devices, Inc. + * Copyright 2008 Red Hat Inc. + * Copyright 2009 Jerome Glisse. + * + * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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. + * + * Authors: Dave Airlie + * Alex Deucher + * Jerome Glisse + */ +#ifndef __RV350D_H__ +#define __RV350D_H__ + +#include +__FBSDID("$FreeBSD$"); + +/* RV350, RV380 registers */ +/* #define R_00000D_SCLK_CNTL 0x00000D */ +#define S_00000D_FORCE_VAP(x) (((x) & 0x1) << 21) +#define G_00000D_FORCE_VAP(x) (((x) >> 21) & 0x1) +#define C_00000D_FORCE_VAP 0xFFDFFFFF +#define S_00000D_FORCE_SR(x) (((x) & 0x1) << 25) +#define G_00000D_FORCE_SR(x) (((x) >> 25) & 0x1) +#define C_00000D_FORCE_SR 0xFDFFFFFF +#define S_00000D_FORCE_PX(x) (((x) & 0x1) << 26) +#define G_00000D_FORCE_PX(x) (((x) >> 26) & 0x1) +#define C_00000D_FORCE_PX 0xFBFFFFFF +#define S_00000D_FORCE_TX(x) (((x) & 0x1) << 27) +#define G_00000D_FORCE_TX(x) (((x) >> 27) & 0x1) +#define C_00000D_FORCE_TX 0xF7FFFFFF +#define S_00000D_FORCE_US(x) (((x) & 0x1) << 28) +#define G_00000D_FORCE_US(x) (((x) >> 28) & 0x1) +#define C_00000D_FORCE_US 0xEFFFFFFF +#define S_00000D_FORCE_SU(x) (((x) & 0x1) << 30) +#define G_00000D_FORCE_SU(x) (((x) >> 30) & 0x1) +#define C_00000D_FORCE_SU 0xBFFFFFFF + +#endif diff --git a/sys/dev/drm2/radeon/rv515.c b/sys/dev/drm2/radeon/rv515.c new file mode 100644 index 00000000000..d83cf1ba07f --- /dev/null +++ b/sys/dev/drm2/radeon/rv515.c @@ -0,0 +1,1201 @@ +/* + * Copyright 2008 Advanced Micro Devices, Inc. + * Copyright 2008 Red Hat Inc. + * Copyright 2009 Jerome Glisse. + * + * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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. + * + * Authors: Dave Airlie + * Alex Deucher + * Jerome Glisse + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include "rv515d.h" +#include "radeon.h" +#include "radeon_asic.h" +#include "atom.h" +#include "rv515_reg_safe.h" + +/* This files gather functions specifics to: rv515 */ +static int rv515_debugfs_pipes_info_init(struct radeon_device *rdev); +static int rv515_debugfs_ga_info_init(struct radeon_device *rdev); +static void rv515_gpu_init(struct radeon_device *rdev); + +static const u32 crtc_offsets[2] = +{ + 0, + AVIVO_D2CRTC_H_TOTAL - AVIVO_D1CRTC_H_TOTAL +}; + +void rv515_debugfs(struct radeon_device *rdev) +{ + if (r100_debugfs_rbbm_init(rdev)) { + DRM_ERROR("Failed to register debugfs file for RBBM !\n"); + } + if (rv515_debugfs_pipes_info_init(rdev)) { + DRM_ERROR("Failed to register debugfs file for pipes !\n"); + } + if (rv515_debugfs_ga_info_init(rdev)) { + DRM_ERROR("Failed to register debugfs file for pipes !\n"); + } +} + +void rv515_ring_start(struct radeon_device *rdev, struct radeon_ring *ring) +{ + int r; + + r = radeon_ring_lock(rdev, ring, 64); + if (r) { + return; + } + radeon_ring_write(ring, PACKET0(ISYNC_CNTL, 0)); + radeon_ring_write(ring, + ISYNC_ANY2D_IDLE3D | + ISYNC_ANY3D_IDLE2D | + ISYNC_WAIT_IDLEGUI | + ISYNC_CPSCRATCH_IDLEGUI); + radeon_ring_write(ring, PACKET0(WAIT_UNTIL, 0)); + radeon_ring_write(ring, WAIT_2D_IDLECLEAN | WAIT_3D_IDLECLEAN); + radeon_ring_write(ring, PACKET0(R300_DST_PIPE_CONFIG, 0)); + radeon_ring_write(ring, R300_PIPE_AUTO_CONFIG); + radeon_ring_write(ring, PACKET0(GB_SELECT, 0)); + radeon_ring_write(ring, 0); + radeon_ring_write(ring, PACKET0(GB_ENABLE, 0)); + radeon_ring_write(ring, 0); + radeon_ring_write(ring, PACKET0(R500_SU_REG_DEST, 0)); + radeon_ring_write(ring, (1 << rdev->num_gb_pipes) - 1); + radeon_ring_write(ring, PACKET0(VAP_INDEX_OFFSET, 0)); + radeon_ring_write(ring, 0); + radeon_ring_write(ring, PACKET0(RB3D_DSTCACHE_CTLSTAT, 0)); + radeon_ring_write(ring, RB3D_DC_FLUSH | RB3D_DC_FREE); + radeon_ring_write(ring, PACKET0(ZB_ZCACHE_CTLSTAT, 0)); + radeon_ring_write(ring, ZC_FLUSH | ZC_FREE); + radeon_ring_write(ring, PACKET0(WAIT_UNTIL, 0)); + radeon_ring_write(ring, WAIT_2D_IDLECLEAN | WAIT_3D_IDLECLEAN); + radeon_ring_write(ring, PACKET0(GB_AA_CONFIG, 0)); + radeon_ring_write(ring, 0); + radeon_ring_write(ring, PACKET0(RB3D_DSTCACHE_CTLSTAT, 0)); + radeon_ring_write(ring, RB3D_DC_FLUSH | RB3D_DC_FREE); + radeon_ring_write(ring, PACKET0(ZB_ZCACHE_CTLSTAT, 0)); + radeon_ring_write(ring, ZC_FLUSH | ZC_FREE); + radeon_ring_write(ring, PACKET0(GB_MSPOS0, 0)); + radeon_ring_write(ring, + ((6 << MS_X0_SHIFT) | + (6 << MS_Y0_SHIFT) | + (6 << MS_X1_SHIFT) | + (6 << MS_Y1_SHIFT) | + (6 << MS_X2_SHIFT) | + (6 << MS_Y2_SHIFT) | + (6 << MSBD0_Y_SHIFT) | + (6 << MSBD0_X_SHIFT))); + radeon_ring_write(ring, PACKET0(GB_MSPOS1, 0)); + radeon_ring_write(ring, + ((6 << MS_X3_SHIFT) | + (6 << MS_Y3_SHIFT) | + (6 << MS_X4_SHIFT) | + (6 << MS_Y4_SHIFT) | + (6 << MS_X5_SHIFT) | + (6 << MS_Y5_SHIFT) | + (6 << MSBD1_SHIFT))); + radeon_ring_write(ring, PACKET0(GA_ENHANCE, 0)); + radeon_ring_write(ring, GA_DEADLOCK_CNTL | GA_FASTSYNC_CNTL); + radeon_ring_write(ring, PACKET0(GA_POLY_MODE, 0)); + radeon_ring_write(ring, FRONT_PTYPE_TRIANGE | BACK_PTYPE_TRIANGE); + radeon_ring_write(ring, PACKET0(GA_ROUND_MODE, 0)); + radeon_ring_write(ring, GEOMETRY_ROUND_NEAREST | COLOR_ROUND_NEAREST); + radeon_ring_write(ring, PACKET0(0x20C8, 0)); + radeon_ring_write(ring, 0); + radeon_ring_unlock_commit(rdev, ring); +} + +int rv515_mc_wait_for_idle(struct radeon_device *rdev) +{ + unsigned i; + uint32_t tmp; + + for (i = 0; i < rdev->usec_timeout; i++) { + /* read MC_STATUS */ + tmp = RREG32_MC(MC_STATUS); + if (tmp & MC_STATUS_IDLE) { + return 0; + } + DRM_UDELAY(1); + } + return -1; +} + +void rv515_vga_render_disable(struct radeon_device *rdev) +{ + WREG32(R_000300_VGA_RENDER_CONTROL, + RREG32(R_000300_VGA_RENDER_CONTROL) & C_000300_VGA_VSTATUS_CNTL); +} + +static void rv515_gpu_init(struct radeon_device *rdev) +{ + unsigned pipe_select_current, gb_pipe_select, tmp; + + if (r100_gui_wait_for_idle(rdev)) { + DRM_ERROR("Failed to wait GUI idle while " + "resetting GPU. Bad things might happen.\n"); + } + rv515_vga_render_disable(rdev); + r420_pipes_init(rdev); + gb_pipe_select = RREG32(R400_GB_PIPE_SELECT); + tmp = RREG32(R300_DST_PIPE_CONFIG); + pipe_select_current = (tmp >> 2) & 3; + tmp = (1 << pipe_select_current) | + (((gb_pipe_select >> 8) & 0xF) << 4); + WREG32_PLL(0x000D, tmp); + if (r100_gui_wait_for_idle(rdev)) { + DRM_ERROR("Failed to wait GUI idle while " + "resetting GPU. Bad things might happen.\n"); + } + if (rv515_mc_wait_for_idle(rdev)) { + DRM_ERROR("Failed to wait MC idle while " + "programming pipes. Bad things might happen.\n"); + } +} + +static void rv515_vram_get_type(struct radeon_device *rdev) +{ + uint32_t tmp; + + rdev->mc.vram_width = 128; + rdev->mc.vram_is_ddr = true; + tmp = RREG32_MC(RV515_MC_CNTL) & MEM_NUM_CHANNELS_MASK; + switch (tmp) { + case 0: + rdev->mc.vram_width = 64; + break; + case 1: + rdev->mc.vram_width = 128; + break; + default: + rdev->mc.vram_width = 128; + break; + } +} + +static void rv515_mc_init(struct radeon_device *rdev) +{ + + rv515_vram_get_type(rdev); + r100_vram_init_sizes(rdev); + radeon_vram_location(rdev, &rdev->mc, 0); + rdev->mc.gtt_base_align = 0; + if (!(rdev->flags & RADEON_IS_AGP)) + radeon_gtt_location(rdev, &rdev->mc); + radeon_update_bandwidth_info(rdev); +} + +uint32_t rv515_mc_rreg(struct radeon_device *rdev, uint32_t reg) +{ + uint32_t r; + + WREG32(MC_IND_INDEX, 0x7f0000 | (reg & 0xffff)); + r = RREG32(MC_IND_DATA); + WREG32(MC_IND_INDEX, 0); + return r; +} + +void rv515_mc_wreg(struct radeon_device *rdev, uint32_t reg, uint32_t v) +{ + WREG32(MC_IND_INDEX, 0xff0000 | ((reg) & 0xffff)); + WREG32(MC_IND_DATA, (v)); + WREG32(MC_IND_INDEX, 0); +} + +#if defined(CONFIG_DEBUG_FS) +static int rv515_debugfs_pipes_info(struct seq_file *m, void *data) +{ + struct drm_info_node *node = (struct drm_info_node *) m->private; + struct drm_device *dev = node->minor->dev; + struct radeon_device *rdev = dev->dev_private; + uint32_t tmp; + + tmp = RREG32(GB_PIPE_SELECT); + seq_printf(m, "GB_PIPE_SELECT 0x%08x\n", tmp); + tmp = RREG32(SU_REG_DEST); + seq_printf(m, "SU_REG_DEST 0x%08x\n", tmp); + tmp = RREG32(GB_TILE_CONFIG); + seq_printf(m, "GB_TILE_CONFIG 0x%08x\n", tmp); + tmp = RREG32(DST_PIPE_CONFIG); + seq_printf(m, "DST_PIPE_CONFIG 0x%08x\n", tmp); + return 0; +} + +static int rv515_debugfs_ga_info(struct seq_file *m, void *data) +{ + struct drm_info_node *node = (struct drm_info_node *) m->private; + struct drm_device *dev = node->minor->dev; + struct radeon_device *rdev = dev->dev_private; + uint32_t tmp; + + tmp = RREG32(0x2140); + seq_printf(m, "VAP_CNTL_STATUS 0x%08x\n", tmp); + radeon_asic_reset(rdev); + tmp = RREG32(0x425C); + seq_printf(m, "GA_IDLE 0x%08x\n", tmp); + return 0; +} + +static struct drm_info_list rv515_pipes_info_list[] = { + {"rv515_pipes_info", rv515_debugfs_pipes_info, 0, NULL}, +}; + +static struct drm_info_list rv515_ga_info_list[] = { + {"rv515_ga_info", rv515_debugfs_ga_info, 0, NULL}, +}; +#endif + +static int rv515_debugfs_pipes_info_init(struct radeon_device *rdev) +{ +#if defined(CONFIG_DEBUG_FS) + return radeon_debugfs_add_files(rdev, rv515_pipes_info_list, 1); +#else + return 0; +#endif +} + +static int rv515_debugfs_ga_info_init(struct radeon_device *rdev) +{ +#if defined(CONFIG_DEBUG_FS) + return radeon_debugfs_add_files(rdev, rv515_ga_info_list, 1); +#else + return 0; +#endif +} + +void rv515_mc_stop(struct radeon_device *rdev, struct rv515_mc_save *save) +{ + u32 crtc_enabled, tmp, frame_count, blackout; + int i, j; + + save->vga_render_control = RREG32(R_000300_VGA_RENDER_CONTROL); + save->vga_hdp_control = RREG32(R_000328_VGA_HDP_CONTROL); + + /* disable VGA render */ + WREG32(R_000300_VGA_RENDER_CONTROL, 0); + /* blank the display controllers */ + for (i = 0; i < rdev->num_crtc; i++) { + crtc_enabled = RREG32(AVIVO_D1CRTC_CONTROL + crtc_offsets[i]) & AVIVO_CRTC_EN; + if (crtc_enabled) { + save->crtc_enabled[i] = true; + tmp = RREG32(AVIVO_D1CRTC_CONTROL + crtc_offsets[i]); + if (!(tmp & AVIVO_CRTC_DISP_READ_REQUEST_DISABLE)) { + radeon_wait_for_vblank(rdev, i); + tmp |= AVIVO_CRTC_DISP_READ_REQUEST_DISABLE; + WREG32(AVIVO_D1CRTC_CONTROL + crtc_offsets[i], tmp); + } + /* wait for the next frame */ + frame_count = radeon_get_vblank_counter(rdev, i); + for (j = 0; j < rdev->usec_timeout; j++) { + if (radeon_get_vblank_counter(rdev, i) != frame_count) + break; + DRM_UDELAY(1); + } + } else { + save->crtc_enabled[i] = false; + } + } + + radeon_mc_wait_for_idle(rdev); + + if (rdev->family >= CHIP_R600) { + if (rdev->family >= CHIP_RV770) + blackout = RREG32(R700_MC_CITF_CNTL); + else + blackout = RREG32(R600_CITF_CNTL); + if ((blackout & R600_BLACKOUT_MASK) != R600_BLACKOUT_MASK) { + /* Block CPU access */ + WREG32(R600_BIF_FB_EN, 0); + /* blackout the MC */ + blackout |= R600_BLACKOUT_MASK; + if (rdev->family >= CHIP_RV770) + WREG32(R700_MC_CITF_CNTL, blackout); + else + WREG32(R600_CITF_CNTL, blackout); + } + } + /* wait for the MC to settle */ + DRM_UDELAY(100); +} + +void rv515_mc_resume(struct radeon_device *rdev, struct rv515_mc_save *save) +{ + u32 tmp, frame_count; + int i, j; + + /* update crtc base addresses */ + for (i = 0; i < rdev->num_crtc; i++) { + if (rdev->family >= CHIP_RV770) { + if (i == 1) { + WREG32(R700_D1GRPH_PRIMARY_SURFACE_ADDRESS_HIGH, + upper_32_bits(rdev->mc.vram_start)); + WREG32(R700_D1GRPH_SECONDARY_SURFACE_ADDRESS_HIGH, + upper_32_bits(rdev->mc.vram_start)); + } else { + WREG32(R700_D2GRPH_PRIMARY_SURFACE_ADDRESS_HIGH, + upper_32_bits(rdev->mc.vram_start)); + WREG32(R700_D2GRPH_SECONDARY_SURFACE_ADDRESS_HIGH, + upper_32_bits(rdev->mc.vram_start)); + } + } + WREG32(R_006110_D1GRPH_PRIMARY_SURFACE_ADDRESS + crtc_offsets[i], + (u32)rdev->mc.vram_start); + WREG32(R_006118_D1GRPH_SECONDARY_SURFACE_ADDRESS + crtc_offsets[i], + (u32)rdev->mc.vram_start); + } + WREG32(R_000310_VGA_MEMORY_BASE_ADDRESS, (u32)rdev->mc.vram_start); + + if (rdev->family >= CHIP_R600) { + /* unblackout the MC */ + if (rdev->family >= CHIP_RV770) + tmp = RREG32(R700_MC_CITF_CNTL); + else + tmp = RREG32(R600_CITF_CNTL); + tmp &= ~R600_BLACKOUT_MASK; + if (rdev->family >= CHIP_RV770) + WREG32(R700_MC_CITF_CNTL, tmp); + else + WREG32(R600_CITF_CNTL, tmp); + /* allow CPU access */ + WREG32(R600_BIF_FB_EN, R600_FB_READ_EN | R600_FB_WRITE_EN); + } + + for (i = 0; i < rdev->num_crtc; i++) { + if (save->crtc_enabled[i]) { + tmp = RREG32(AVIVO_D1CRTC_CONTROL + crtc_offsets[i]); + tmp &= ~AVIVO_CRTC_DISP_READ_REQUEST_DISABLE; + WREG32(AVIVO_D1CRTC_CONTROL + crtc_offsets[i], tmp); + /* wait for the next frame */ + frame_count = radeon_get_vblank_counter(rdev, i); + for (j = 0; j < rdev->usec_timeout; j++) { + if (radeon_get_vblank_counter(rdev, i) != frame_count) + break; + DRM_UDELAY(1); + } + } + } + /* Unlock vga access */ + WREG32(R_000328_VGA_HDP_CONTROL, save->vga_hdp_control); + DRM_MDELAY(1); + WREG32(R_000300_VGA_RENDER_CONTROL, save->vga_render_control); +} + +static void rv515_mc_program(struct radeon_device *rdev) +{ + struct rv515_mc_save save; + + /* Stops all mc clients */ + rv515_mc_stop(rdev, &save); + + /* Wait for mc idle */ + if (rv515_mc_wait_for_idle(rdev)) + dev_warn(rdev->dev, "Wait MC idle timeout before updating MC.\n"); + /* Write VRAM size in case we are limiting it */ + WREG32(R_0000F8_CONFIG_MEMSIZE, rdev->mc.real_vram_size); + /* Program MC, should be a 32bits limited address space */ + WREG32_MC(R_000001_MC_FB_LOCATION, + S_000001_MC_FB_START(rdev->mc.vram_start >> 16) | + S_000001_MC_FB_TOP(rdev->mc.vram_end >> 16)); + WREG32(R_000134_HDP_FB_LOCATION, + S_000134_HDP_FB_START(rdev->mc.vram_start >> 16)); + if (rdev->flags & RADEON_IS_AGP) { + WREG32_MC(R_000002_MC_AGP_LOCATION, + S_000002_MC_AGP_START(rdev->mc.gtt_start >> 16) | + S_000002_MC_AGP_TOP(rdev->mc.gtt_end >> 16)); + WREG32_MC(R_000003_MC_AGP_BASE, lower_32_bits(rdev->mc.agp_base)); + WREG32_MC(R_000004_MC_AGP_BASE_2, + S_000004_AGP_BASE_ADDR_2(upper_32_bits(rdev->mc.agp_base))); + } else { + WREG32_MC(R_000002_MC_AGP_LOCATION, 0xFFFFFFFF); + WREG32_MC(R_000003_MC_AGP_BASE, 0); + WREG32_MC(R_000004_MC_AGP_BASE_2, 0); + } + + rv515_mc_resume(rdev, &save); +} + +void rv515_clock_startup(struct radeon_device *rdev) +{ + if (radeon_dynclks != -1 && radeon_dynclks) + radeon_atom_set_clock_gating(rdev, 1); + /* We need to force on some of the block */ + WREG32_PLL(R_00000F_CP_DYN_CNTL, + RREG32_PLL(R_00000F_CP_DYN_CNTL) | S_00000F_CP_FORCEON(1)); + WREG32_PLL(R_000011_E2_DYN_CNTL, + RREG32_PLL(R_000011_E2_DYN_CNTL) | S_000011_E2_FORCEON(1)); + WREG32_PLL(R_000013_IDCT_DYN_CNTL, + RREG32_PLL(R_000013_IDCT_DYN_CNTL) | S_000013_IDCT_FORCEON(1)); +} + +static int rv515_startup(struct radeon_device *rdev) +{ + int r; + + rv515_mc_program(rdev); + /* Resume clock */ + rv515_clock_startup(rdev); + /* Initialize GPU configuration (# pipes, ...) */ + rv515_gpu_init(rdev); + /* Initialize GART (initialize after TTM so we can allocate + * memory through TTM but finalize after TTM) */ + if (rdev->flags & RADEON_IS_PCIE) { + r = rv370_pcie_gart_enable(rdev); + if (r) + return r; + } + + /* allocate wb buffer */ + r = radeon_wb_init(rdev); + if (r) + return r; + + r = radeon_fence_driver_start_ring(rdev, RADEON_RING_TYPE_GFX_INDEX); + if (r) { + dev_err(rdev->dev, "failed initializing CP fences (%d).\n", r); + return r; + } + + /* Enable IRQ */ + rs600_irq_set(rdev); + rdev->config.r300.hdp_cntl = RREG32(RADEON_HOST_PATH_CNTL); + /* 1M ring buffer */ + r = r100_cp_init(rdev, 1024 * 1024); + if (r) { + dev_err(rdev->dev, "failed initializing CP (%d).\n", r); + return r; + } + + r = radeon_ib_pool_init(rdev); + if (r) { + dev_err(rdev->dev, "IB initialization failed (%d).\n", r); + return r; + } + + return 0; +} + +int rv515_resume(struct radeon_device *rdev) +{ + int r; + + /* Make sur GART are not working */ + if (rdev->flags & RADEON_IS_PCIE) + rv370_pcie_gart_disable(rdev); + /* Resume clock before doing reset */ + rv515_clock_startup(rdev); + /* Reset gpu before posting otherwise ATOM will enter infinite loop */ + if (radeon_asic_reset(rdev)) { + dev_warn(rdev->dev, "GPU reset failed ! (0xE40=0x%08X, 0x7C0=0x%08X)\n", + RREG32(R_000E40_RBBM_STATUS), + RREG32(R_0007C0_CP_STAT)); + } + /* post */ + atom_asic_init(rdev->mode_info.atom_context); + /* Resume clock after posting */ + rv515_clock_startup(rdev); + /* Initialize surface registers */ + radeon_surface_init(rdev); + + rdev->accel_working = true; + r = rv515_startup(rdev); + if (r) { + rdev->accel_working = false; + } + return r; +} + +int rv515_suspend(struct radeon_device *rdev) +{ + r100_cp_disable(rdev); + radeon_wb_disable(rdev); + rs600_irq_disable(rdev); + if (rdev->flags & RADEON_IS_PCIE) + rv370_pcie_gart_disable(rdev); + return 0; +} + +void rv515_set_safe_registers(struct radeon_device *rdev) +{ + rdev->config.r300.reg_safe_bm = rv515_reg_safe_bm; + rdev->config.r300.reg_safe_bm_size = DRM_ARRAY_SIZE(rv515_reg_safe_bm); +} + +void rv515_fini(struct radeon_device *rdev) +{ + r100_cp_fini(rdev); + radeon_wb_fini(rdev); + radeon_ib_pool_fini(rdev); + radeon_gem_fini(rdev); + rv370_pcie_gart_fini(rdev); + radeon_agp_fini(rdev); + radeon_irq_kms_fini(rdev); + radeon_fence_driver_fini(rdev); + radeon_bo_fini(rdev); + radeon_atombios_fini(rdev); + free(rdev->bios, DRM_MEM_DRIVER); + rdev->bios = NULL; +} + +int rv515_init(struct radeon_device *rdev) +{ + int r; + + /* Initialize scratch registers */ + radeon_scratch_init(rdev); + /* Initialize surface registers */ + radeon_surface_init(rdev); + /* TODO: disable VGA need to use VGA request */ + /* restore some register to sane defaults */ + r100_restore_sanity(rdev); + /* BIOS*/ + if (!radeon_get_bios(rdev)) { + if (ASIC_IS_AVIVO(rdev)) + return -EINVAL; + } + if (rdev->is_atom_bios) { + r = radeon_atombios_init(rdev); + if (r) + return r; + } else { + dev_err(rdev->dev, "Expecting atombios for RV515 GPU\n"); + return -EINVAL; + } + /* Reset gpu before posting otherwise ATOM will enter infinite loop */ + if (radeon_asic_reset(rdev)) { + dev_warn(rdev->dev, + "GPU reset failed ! (0xE40=0x%08X, 0x7C0=0x%08X)\n", + RREG32(R_000E40_RBBM_STATUS), + RREG32(R_0007C0_CP_STAT)); + } + /* check if cards are posted or not */ + if (radeon_boot_test_post_card(rdev) == false) + return -EINVAL; + /* Initialize clocks */ + radeon_get_clock_info(rdev->ddev); + /* initialize AGP */ + if (rdev->flags & RADEON_IS_AGP) { + r = radeon_agp_init(rdev); + if (r) { + radeon_agp_disable(rdev); + } + } + /* initialize memory controller */ + rv515_mc_init(rdev); + rv515_debugfs(rdev); + /* Fence driver */ + r = radeon_fence_driver_init(rdev); + if (r) + return r; + r = radeon_irq_kms_init(rdev); + if (r) + return r; + /* Memory manager */ + r = radeon_bo_init(rdev); + if (r) + return r; + r = rv370_pcie_gart_init(rdev); + if (r) + return r; + rv515_set_safe_registers(rdev); + + rdev->accel_working = true; + r = rv515_startup(rdev); + if (r) { + /* Somethings want wront with the accel init stop accel */ + dev_err(rdev->dev, "Disabling GPU acceleration\n"); + r100_cp_fini(rdev); + radeon_wb_fini(rdev); + radeon_ib_pool_fini(rdev); + radeon_irq_kms_fini(rdev); + rv370_pcie_gart_fini(rdev); + radeon_agp_fini(rdev); + rdev->accel_working = false; + } + return 0; +} + +void atom_rv515_force_tv_scaler(struct radeon_device *rdev, struct radeon_crtc *crtc) +{ + int index_reg = 0x6578 + crtc->crtc_offset; + int data_reg = 0x657c + crtc->crtc_offset; + + WREG32(0x659C + crtc->crtc_offset, 0x0); + WREG32(0x6594 + crtc->crtc_offset, 0x705); + WREG32(0x65A4 + crtc->crtc_offset, 0x10001); + WREG32(0x65D8 + crtc->crtc_offset, 0x0); + WREG32(0x65B0 + crtc->crtc_offset, 0x0); + WREG32(0x65C0 + crtc->crtc_offset, 0x0); + WREG32(0x65D4 + crtc->crtc_offset, 0x0); + WREG32(index_reg, 0x0); + WREG32(data_reg, 0x841880A8); + WREG32(index_reg, 0x1); + WREG32(data_reg, 0x84208680); + WREG32(index_reg, 0x2); + WREG32(data_reg, 0xBFF880B0); + WREG32(index_reg, 0x100); + WREG32(data_reg, 0x83D88088); + WREG32(index_reg, 0x101); + WREG32(data_reg, 0x84608680); + WREG32(index_reg, 0x102); + WREG32(data_reg, 0xBFF080D0); + WREG32(index_reg, 0x200); + WREG32(data_reg, 0x83988068); + WREG32(index_reg, 0x201); + WREG32(data_reg, 0x84A08680); + WREG32(index_reg, 0x202); + WREG32(data_reg, 0xBFF080F8); + WREG32(index_reg, 0x300); + WREG32(data_reg, 0x83588058); + WREG32(index_reg, 0x301); + WREG32(data_reg, 0x84E08660); + WREG32(index_reg, 0x302); + WREG32(data_reg, 0xBFF88120); + WREG32(index_reg, 0x400); + WREG32(data_reg, 0x83188040); + WREG32(index_reg, 0x401); + WREG32(data_reg, 0x85008660); + WREG32(index_reg, 0x402); + WREG32(data_reg, 0xBFF88150); + WREG32(index_reg, 0x500); + WREG32(data_reg, 0x82D88030); + WREG32(index_reg, 0x501); + WREG32(data_reg, 0x85408640); + WREG32(index_reg, 0x502); + WREG32(data_reg, 0xBFF88180); + WREG32(index_reg, 0x600); + WREG32(data_reg, 0x82A08018); + WREG32(index_reg, 0x601); + WREG32(data_reg, 0x85808620); + WREG32(index_reg, 0x602); + WREG32(data_reg, 0xBFF081B8); + WREG32(index_reg, 0x700); + WREG32(data_reg, 0x82608010); + WREG32(index_reg, 0x701); + WREG32(data_reg, 0x85A08600); + WREG32(index_reg, 0x702); + WREG32(data_reg, 0x800081F0); + WREG32(index_reg, 0x800); + WREG32(data_reg, 0x8228BFF8); + WREG32(index_reg, 0x801); + WREG32(data_reg, 0x85E085E0); + WREG32(index_reg, 0x802); + WREG32(data_reg, 0xBFF88228); + WREG32(index_reg, 0x10000); + WREG32(data_reg, 0x82A8BF00); + WREG32(index_reg, 0x10001); + WREG32(data_reg, 0x82A08CC0); + WREG32(index_reg, 0x10002); + WREG32(data_reg, 0x8008BEF8); + WREG32(index_reg, 0x10100); + WREG32(data_reg, 0x81F0BF28); + WREG32(index_reg, 0x10101); + WREG32(data_reg, 0x83608CA0); + WREG32(index_reg, 0x10102); + WREG32(data_reg, 0x8018BED0); + WREG32(index_reg, 0x10200); + WREG32(data_reg, 0x8148BF38); + WREG32(index_reg, 0x10201); + WREG32(data_reg, 0x84408C80); + WREG32(index_reg, 0x10202); + WREG32(data_reg, 0x8008BEB8); + WREG32(index_reg, 0x10300); + WREG32(data_reg, 0x80B0BF78); + WREG32(index_reg, 0x10301); + WREG32(data_reg, 0x85008C20); + WREG32(index_reg, 0x10302); + WREG32(data_reg, 0x8020BEA0); + WREG32(index_reg, 0x10400); + WREG32(data_reg, 0x8028BF90); + WREG32(index_reg, 0x10401); + WREG32(data_reg, 0x85E08BC0); + WREG32(index_reg, 0x10402); + WREG32(data_reg, 0x8018BE90); + WREG32(index_reg, 0x10500); + WREG32(data_reg, 0xBFB8BFB0); + WREG32(index_reg, 0x10501); + WREG32(data_reg, 0x86C08B40); + WREG32(index_reg, 0x10502); + WREG32(data_reg, 0x8010BE90); + WREG32(index_reg, 0x10600); + WREG32(data_reg, 0xBF58BFC8); + WREG32(index_reg, 0x10601); + WREG32(data_reg, 0x87A08AA0); + WREG32(index_reg, 0x10602); + WREG32(data_reg, 0x8010BE98); + WREG32(index_reg, 0x10700); + WREG32(data_reg, 0xBF10BFF0); + WREG32(index_reg, 0x10701); + WREG32(data_reg, 0x886089E0); + WREG32(index_reg, 0x10702); + WREG32(data_reg, 0x8018BEB0); + WREG32(index_reg, 0x10800); + WREG32(data_reg, 0xBED8BFE8); + WREG32(index_reg, 0x10801); + WREG32(data_reg, 0x89408940); + WREG32(index_reg, 0x10802); + WREG32(data_reg, 0xBFE8BED8); + WREG32(index_reg, 0x20000); + WREG32(data_reg, 0x80008000); + WREG32(index_reg, 0x20001); + WREG32(data_reg, 0x90008000); + WREG32(index_reg, 0x20002); + WREG32(data_reg, 0x80008000); + WREG32(index_reg, 0x20003); + WREG32(data_reg, 0x80008000); + WREG32(index_reg, 0x20100); + WREG32(data_reg, 0x80108000); + WREG32(index_reg, 0x20101); + WREG32(data_reg, 0x8FE0BF70); + WREG32(index_reg, 0x20102); + WREG32(data_reg, 0xBFE880C0); + WREG32(index_reg, 0x20103); + WREG32(data_reg, 0x80008000); + WREG32(index_reg, 0x20200); + WREG32(data_reg, 0x8018BFF8); + WREG32(index_reg, 0x20201); + WREG32(data_reg, 0x8F80BF08); + WREG32(index_reg, 0x20202); + WREG32(data_reg, 0xBFD081A0); + WREG32(index_reg, 0x20203); + WREG32(data_reg, 0xBFF88000); + WREG32(index_reg, 0x20300); + WREG32(data_reg, 0x80188000); + WREG32(index_reg, 0x20301); + WREG32(data_reg, 0x8EE0BEC0); + WREG32(index_reg, 0x20302); + WREG32(data_reg, 0xBFB082A0); + WREG32(index_reg, 0x20303); + WREG32(data_reg, 0x80008000); + WREG32(index_reg, 0x20400); + WREG32(data_reg, 0x80188000); + WREG32(index_reg, 0x20401); + WREG32(data_reg, 0x8E00BEA0); + WREG32(index_reg, 0x20402); + WREG32(data_reg, 0xBF8883C0); + WREG32(index_reg, 0x20403); + WREG32(data_reg, 0x80008000); + WREG32(index_reg, 0x20500); + WREG32(data_reg, 0x80188000); + WREG32(index_reg, 0x20501); + WREG32(data_reg, 0x8D00BE90); + WREG32(index_reg, 0x20502); + WREG32(data_reg, 0xBF588500); + WREG32(index_reg, 0x20503); + WREG32(data_reg, 0x80008008); + WREG32(index_reg, 0x20600); + WREG32(data_reg, 0x80188000); + WREG32(index_reg, 0x20601); + WREG32(data_reg, 0x8BC0BE98); + WREG32(index_reg, 0x20602); + WREG32(data_reg, 0xBF308660); + WREG32(index_reg, 0x20603); + WREG32(data_reg, 0x80008008); + WREG32(index_reg, 0x20700); + WREG32(data_reg, 0x80108000); + WREG32(index_reg, 0x20701); + WREG32(data_reg, 0x8A80BEB0); + WREG32(index_reg, 0x20702); + WREG32(data_reg, 0xBF0087C0); + WREG32(index_reg, 0x20703); + WREG32(data_reg, 0x80008008); + WREG32(index_reg, 0x20800); + WREG32(data_reg, 0x80108000); + WREG32(index_reg, 0x20801); + WREG32(data_reg, 0x8920BED0); + WREG32(index_reg, 0x20802); + WREG32(data_reg, 0xBED08920); + WREG32(index_reg, 0x20803); + WREG32(data_reg, 0x80008010); + WREG32(index_reg, 0x30000); + WREG32(data_reg, 0x90008000); + WREG32(index_reg, 0x30001); + WREG32(data_reg, 0x80008000); + WREG32(index_reg, 0x30100); + WREG32(data_reg, 0x8FE0BF90); + WREG32(index_reg, 0x30101); + WREG32(data_reg, 0xBFF880A0); + WREG32(index_reg, 0x30200); + WREG32(data_reg, 0x8F60BF40); + WREG32(index_reg, 0x30201); + WREG32(data_reg, 0xBFE88180); + WREG32(index_reg, 0x30300); + WREG32(data_reg, 0x8EC0BF00); + WREG32(index_reg, 0x30301); + WREG32(data_reg, 0xBFC88280); + WREG32(index_reg, 0x30400); + WREG32(data_reg, 0x8DE0BEE0); + WREG32(index_reg, 0x30401); + WREG32(data_reg, 0xBFA083A0); + WREG32(index_reg, 0x30500); + WREG32(data_reg, 0x8CE0BED0); + WREG32(index_reg, 0x30501); + WREG32(data_reg, 0xBF7884E0); + WREG32(index_reg, 0x30600); + WREG32(data_reg, 0x8BA0BED8); + WREG32(index_reg, 0x30601); + WREG32(data_reg, 0xBF508640); + WREG32(index_reg, 0x30700); + WREG32(data_reg, 0x8A60BEE8); + WREG32(index_reg, 0x30701); + WREG32(data_reg, 0xBF2087A0); + WREG32(index_reg, 0x30800); + WREG32(data_reg, 0x8900BF00); + WREG32(index_reg, 0x30801); + WREG32(data_reg, 0xBF008900); +} + +struct rv515_watermark { + u32 lb_request_fifo_depth; + fixed20_12 num_line_pair; + fixed20_12 estimated_width; + fixed20_12 worst_case_latency; + fixed20_12 consumption_rate; + fixed20_12 active_time; + fixed20_12 dbpp; + fixed20_12 priority_mark_max; + fixed20_12 priority_mark; + fixed20_12 sclk; +}; + +static void rv515_crtc_bandwidth_compute(struct radeon_device *rdev, + struct radeon_crtc *crtc, + struct rv515_watermark *wm) +{ + struct drm_display_mode *mode = &crtc->base.mode; + fixed20_12 a, b, c; + fixed20_12 pclk, request_fifo_depth, tolerable_latency, estimated_width; + fixed20_12 consumption_time, line_time, chunk_time, read_delay_latency; + + if (!crtc->base.enabled) { + /* FIXME: wouldn't it better to set priority mark to maximum */ + wm->lb_request_fifo_depth = 4; + return; + } + + if (crtc->vsc.full > dfixed_const(2)) + wm->num_line_pair.full = dfixed_const(2); + else + wm->num_line_pair.full = dfixed_const(1); + + b.full = dfixed_const(mode->crtc_hdisplay); + c.full = dfixed_const(256); + a.full = dfixed_div(b, c); + request_fifo_depth.full = dfixed_mul(a, wm->num_line_pair); + request_fifo_depth.full = dfixed_ceil(request_fifo_depth); + if (a.full < dfixed_const(4)) { + wm->lb_request_fifo_depth = 4; + } else { + wm->lb_request_fifo_depth = dfixed_trunc(request_fifo_depth); + } + + /* Determine consumption rate + * pclk = pixel clock period(ns) = 1000 / (mode.clock / 1000) + * vtaps = number of vertical taps, + * vsc = vertical scaling ratio, defined as source/destination + * hsc = horizontal scaling ration, defined as source/destination + */ + a.full = dfixed_const(mode->clock); + b.full = dfixed_const(1000); + a.full = dfixed_div(a, b); + pclk.full = dfixed_div(b, a); + if (crtc->rmx_type != RMX_OFF) { + b.full = dfixed_const(2); + if (crtc->vsc.full > b.full) + b.full = crtc->vsc.full; + b.full = dfixed_mul(b, crtc->hsc); + c.full = dfixed_const(2); + b.full = dfixed_div(b, c); + consumption_time.full = dfixed_div(pclk, b); + } else { + consumption_time.full = pclk.full; + } + a.full = dfixed_const(1); + wm->consumption_rate.full = dfixed_div(a, consumption_time); + + + /* Determine line time + * LineTime = total time for one line of displayhtotal + * LineTime = total number of horizontal pixels + * pclk = pixel clock period(ns) + */ + a.full = dfixed_const(crtc->base.mode.crtc_htotal); + line_time.full = dfixed_mul(a, pclk); + + /* Determine active time + * ActiveTime = time of active region of display within one line, + * hactive = total number of horizontal active pixels + * htotal = total number of horizontal pixels + */ + a.full = dfixed_const(crtc->base.mode.crtc_htotal); + b.full = dfixed_const(crtc->base.mode.crtc_hdisplay); + wm->active_time.full = dfixed_mul(line_time, b); + wm->active_time.full = dfixed_div(wm->active_time, a); + + /* Determine chunk time + * ChunkTime = the time it takes the DCP to send one chunk of data + * to the LB which consists of pipeline delay and inter chunk gap + * sclk = system clock(Mhz) + */ + a.full = dfixed_const(600 * 1000); + chunk_time.full = dfixed_div(a, rdev->pm.sclk); + read_delay_latency.full = dfixed_const(1000); + + /* Determine the worst case latency + * NumLinePair = Number of line pairs to request(1=2 lines, 2=4 lines) + * WorstCaseLatency = worst case time from urgent to when the MC starts + * to return data + * READ_DELAY_IDLE_MAX = constant of 1us + * ChunkTime = time it takes the DCP to send one chunk of data to the LB + * which consists of pipeline delay and inter chunk gap + */ + if (dfixed_trunc(wm->num_line_pair) > 1) { + a.full = dfixed_const(3); + wm->worst_case_latency.full = dfixed_mul(a, chunk_time); + wm->worst_case_latency.full += read_delay_latency.full; + } else { + wm->worst_case_latency.full = chunk_time.full + read_delay_latency.full; + } + + /* Determine the tolerable latency + * TolerableLatency = Any given request has only 1 line time + * for the data to be returned + * LBRequestFifoDepth = Number of chunk requests the LB can + * put into the request FIFO for a display + * LineTime = total time for one line of display + * ChunkTime = the time it takes the DCP to send one chunk + * of data to the LB which consists of + * pipeline delay and inter chunk gap + */ + if ((2+wm->lb_request_fifo_depth) >= dfixed_trunc(request_fifo_depth)) { + tolerable_latency.full = line_time.full; + } else { + tolerable_latency.full = dfixed_const(wm->lb_request_fifo_depth - 2); + tolerable_latency.full = request_fifo_depth.full - tolerable_latency.full; + tolerable_latency.full = dfixed_mul(tolerable_latency, chunk_time); + tolerable_latency.full = line_time.full - tolerable_latency.full; + } + /* We assume worst case 32bits (4 bytes) */ + wm->dbpp.full = dfixed_const(2 * 16); + + /* Determine the maximum priority mark + * width = viewport width in pixels + */ + a.full = dfixed_const(16); + wm->priority_mark_max.full = dfixed_const(crtc->base.mode.crtc_hdisplay); + wm->priority_mark_max.full = dfixed_div(wm->priority_mark_max, a); + wm->priority_mark_max.full = dfixed_ceil(wm->priority_mark_max); + + /* Determine estimated width */ + estimated_width.full = tolerable_latency.full - wm->worst_case_latency.full; + estimated_width.full = dfixed_div(estimated_width, consumption_time); + if (dfixed_trunc(estimated_width) > crtc->base.mode.crtc_hdisplay) { + wm->priority_mark.full = wm->priority_mark_max.full; + } else { + a.full = dfixed_const(16); + wm->priority_mark.full = dfixed_div(estimated_width, a); + wm->priority_mark.full = dfixed_ceil(wm->priority_mark); + wm->priority_mark.full = wm->priority_mark_max.full - wm->priority_mark.full; + } +} + +void rv515_bandwidth_avivo_update(struct radeon_device *rdev) +{ + struct drm_display_mode *mode0 = NULL; + struct drm_display_mode *mode1 = NULL; + struct rv515_watermark wm0; + struct rv515_watermark wm1; + u32 tmp; + u32 d1mode_priority_a_cnt = MODE_PRIORITY_OFF; + u32 d2mode_priority_a_cnt = MODE_PRIORITY_OFF; + fixed20_12 priority_mark02, priority_mark12, fill_rate; + fixed20_12 a, b; + + if (rdev->mode_info.crtcs[0]->base.enabled) + mode0 = &rdev->mode_info.crtcs[0]->base.mode; + if (rdev->mode_info.crtcs[1]->base.enabled) + mode1 = &rdev->mode_info.crtcs[1]->base.mode; + rs690_line_buffer_adjust(rdev, mode0, mode1); + + rv515_crtc_bandwidth_compute(rdev, rdev->mode_info.crtcs[0], &wm0); + rv515_crtc_bandwidth_compute(rdev, rdev->mode_info.crtcs[1], &wm1); + + tmp = wm0.lb_request_fifo_depth; + tmp |= wm1.lb_request_fifo_depth << 16; + WREG32(LB_MAX_REQ_OUTSTANDING, tmp); + + if (mode0 && mode1) { + if (dfixed_trunc(wm0.dbpp) > 64) + a.full = dfixed_div(wm0.dbpp, wm0.num_line_pair); + else + a.full = wm0.num_line_pair.full; + if (dfixed_trunc(wm1.dbpp) > 64) + b.full = dfixed_div(wm1.dbpp, wm1.num_line_pair); + else + b.full = wm1.num_line_pair.full; + a.full += b.full; + fill_rate.full = dfixed_div(wm0.sclk, a); + if (wm0.consumption_rate.full > fill_rate.full) { + b.full = wm0.consumption_rate.full - fill_rate.full; + b.full = dfixed_mul(b, wm0.active_time); + a.full = dfixed_const(16); + b.full = dfixed_div(b, a); + a.full = dfixed_mul(wm0.worst_case_latency, + wm0.consumption_rate); + priority_mark02.full = a.full + b.full; + } else { + a.full = dfixed_mul(wm0.worst_case_latency, + wm0.consumption_rate); + b.full = dfixed_const(16 * 1000); + priority_mark02.full = dfixed_div(a, b); + } + if (wm1.consumption_rate.full > fill_rate.full) { + b.full = wm1.consumption_rate.full - fill_rate.full; + b.full = dfixed_mul(b, wm1.active_time); + a.full = dfixed_const(16); + b.full = dfixed_div(b, a); + a.full = dfixed_mul(wm1.worst_case_latency, + wm1.consumption_rate); + priority_mark12.full = a.full + b.full; + } else { + a.full = dfixed_mul(wm1.worst_case_latency, + wm1.consumption_rate); + b.full = dfixed_const(16 * 1000); + priority_mark12.full = dfixed_div(a, b); + } + if (wm0.priority_mark.full > priority_mark02.full) + priority_mark02.full = wm0.priority_mark.full; + if (dfixed_trunc(priority_mark02) < 0) + priority_mark02.full = 0; + if (wm0.priority_mark_max.full > priority_mark02.full) + priority_mark02.full = wm0.priority_mark_max.full; + if (wm1.priority_mark.full > priority_mark12.full) + priority_mark12.full = wm1.priority_mark.full; + if (dfixed_trunc(priority_mark12) < 0) + priority_mark12.full = 0; + if (wm1.priority_mark_max.full > priority_mark12.full) + priority_mark12.full = wm1.priority_mark_max.full; + d1mode_priority_a_cnt = dfixed_trunc(priority_mark02); + d2mode_priority_a_cnt = dfixed_trunc(priority_mark12); + if (rdev->disp_priority == 2) { + d1mode_priority_a_cnt |= MODE_PRIORITY_ALWAYS_ON; + d2mode_priority_a_cnt |= MODE_PRIORITY_ALWAYS_ON; + } + } else if (mode0) { + if (dfixed_trunc(wm0.dbpp) > 64) + a.full = dfixed_div(wm0.dbpp, wm0.num_line_pair); + else + a.full = wm0.num_line_pair.full; + fill_rate.full = dfixed_div(wm0.sclk, a); + if (wm0.consumption_rate.full > fill_rate.full) { + b.full = wm0.consumption_rate.full - fill_rate.full; + b.full = dfixed_mul(b, wm0.active_time); + a.full = dfixed_const(16); + b.full = dfixed_div(b, a); + a.full = dfixed_mul(wm0.worst_case_latency, + wm0.consumption_rate); + priority_mark02.full = a.full + b.full; + } else { + a.full = dfixed_mul(wm0.worst_case_latency, + wm0.consumption_rate); + b.full = dfixed_const(16); + priority_mark02.full = dfixed_div(a, b); + } + if (wm0.priority_mark.full > priority_mark02.full) + priority_mark02.full = wm0.priority_mark.full; + if (dfixed_trunc(priority_mark02) < 0) + priority_mark02.full = 0; + if (wm0.priority_mark_max.full > priority_mark02.full) + priority_mark02.full = wm0.priority_mark_max.full; + d1mode_priority_a_cnt = dfixed_trunc(priority_mark02); + if (rdev->disp_priority == 2) + d1mode_priority_a_cnt |= MODE_PRIORITY_ALWAYS_ON; + } else if (mode1) { + if (dfixed_trunc(wm1.dbpp) > 64) + a.full = dfixed_div(wm1.dbpp, wm1.num_line_pair); + else + a.full = wm1.num_line_pair.full; + fill_rate.full = dfixed_div(wm1.sclk, a); + if (wm1.consumption_rate.full > fill_rate.full) { + b.full = wm1.consumption_rate.full - fill_rate.full; + b.full = dfixed_mul(b, wm1.active_time); + a.full = dfixed_const(16); + b.full = dfixed_div(b, a); + a.full = dfixed_mul(wm1.worst_case_latency, + wm1.consumption_rate); + priority_mark12.full = a.full + b.full; + } else { + a.full = dfixed_mul(wm1.worst_case_latency, + wm1.consumption_rate); + b.full = dfixed_const(16 * 1000); + priority_mark12.full = dfixed_div(a, b); + } + if (wm1.priority_mark.full > priority_mark12.full) + priority_mark12.full = wm1.priority_mark.full; + if (dfixed_trunc(priority_mark12) < 0) + priority_mark12.full = 0; + if (wm1.priority_mark_max.full > priority_mark12.full) + priority_mark12.full = wm1.priority_mark_max.full; + d2mode_priority_a_cnt = dfixed_trunc(priority_mark12); + if (rdev->disp_priority == 2) + d2mode_priority_a_cnt |= MODE_PRIORITY_ALWAYS_ON; + } + + WREG32(D1MODE_PRIORITY_A_CNT, d1mode_priority_a_cnt); + WREG32(D1MODE_PRIORITY_B_CNT, d1mode_priority_a_cnt); + WREG32(D2MODE_PRIORITY_A_CNT, d2mode_priority_a_cnt); + WREG32(D2MODE_PRIORITY_B_CNT, d2mode_priority_a_cnt); +} + +void rv515_bandwidth_update(struct radeon_device *rdev) +{ + uint32_t tmp; + struct drm_display_mode *mode0 = NULL; + struct drm_display_mode *mode1 = NULL; + + radeon_update_display_priority(rdev); + + if (rdev->mode_info.crtcs[0]->base.enabled) + mode0 = &rdev->mode_info.crtcs[0]->base.mode; + if (rdev->mode_info.crtcs[1]->base.enabled) + mode1 = &rdev->mode_info.crtcs[1]->base.mode; + /* + * Set display0/1 priority up in the memory controller for + * modes if the user specifies HIGH for displaypriority + * option. + */ + if ((rdev->disp_priority == 2) && + (rdev->family == CHIP_RV515)) { + tmp = RREG32_MC(MC_MISC_LAT_TIMER); + tmp &= ~MC_DISP1R_INIT_LAT_MASK; + tmp &= ~MC_DISP0R_INIT_LAT_MASK; + if (mode1) + tmp |= (1 << MC_DISP1R_INIT_LAT_SHIFT); + if (mode0) + tmp |= (1 << MC_DISP0R_INIT_LAT_SHIFT); + WREG32_MC(MC_MISC_LAT_TIMER, tmp); + } + rv515_bandwidth_avivo_update(rdev); +} diff --git a/sys/dev/drm2/radeon/rv515_reg_safe.h b/sys/dev/drm2/radeon/rv515_reg_safe.h new file mode 100644 index 00000000000..07bd8276029 --- /dev/null +++ b/sys/dev/drm2/radeon/rv515_reg_safe.h @@ -0,0 +1,60 @@ +#include +__FBSDID("$FreeBSD$"); + +static const unsigned rv515_reg_safe_bm[219] = { + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0x17FF1FFF, 0xFFFFFFFC, 0xFFFFFFFF, 0xFF30FFBF, + 0xFFFFFFF8, 0xC3E6FFFF, 0xFFFFF6DF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFF03F, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFEFC6, 0xF00EBFFF, 0x007C0000, + 0xF0000038, 0xFF000009, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFF7FF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0x1FFFFC48, 0xFFFFE000, 0xFFFFFE1E, 0xFFFFFFFF, + 0x388F8F50, 0xFFF88082, 0xFF0000FC, 0xFAE00BFF, + 0x0000FFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, + 0x00008CFC, 0xFFFCC1FF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFE80FFFF, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x0003FC0B, 0x3FFFFCFF, 0xFFBFFB99, 0xFFDFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, +}; diff --git a/sys/dev/drm2/radeon/rv515d.h b/sys/dev/drm2/radeon/rv515d.h new file mode 100644 index 00000000000..38b3a2e4f27 --- /dev/null +++ b/sys/dev/drm2/radeon/rv515d.h @@ -0,0 +1,652 @@ +/* + * Copyright 2008 Advanced Micro Devices, Inc. + * Copyright 2008 Red Hat Inc. + * Copyright 2009 Jerome Glisse. + * + * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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. + * + * Authors: Dave Airlie + * Alex Deucher + * Jerome Glisse + */ +#ifndef __RV515D_H__ +#define __RV515D_H__ + +#include +__FBSDID("$FreeBSD$"); + +/* + * RV515 registers + */ +#define PCIE_INDEX 0x0030 +#define PCIE_DATA 0x0034 +#define MC_IND_INDEX 0x0070 +#define MC_IND_WR_EN (1 << 24) +#define MC_IND_DATA 0x0074 +#define RBBM_SOFT_RESET 0x00F0 +#define CONFIG_MEMSIZE 0x00F8 +#define HDP_FB_LOCATION 0x0134 +#define CP_CSQ_CNTL 0x0740 +#define CP_CSQ_MODE 0x0744 +#define CP_CSQ_ADDR 0x07F0 +#define CP_CSQ_DATA 0x07F4 +#define CP_CSQ_STAT 0x07F8 +#define CP_CSQ2_STAT 0x07FC +#define RBBM_STATUS 0x0E40 +#define DST_PIPE_CONFIG 0x170C +#define WAIT_UNTIL 0x1720 +#define WAIT_2D_IDLE (1 << 14) +#define WAIT_3D_IDLE (1 << 15) +#define WAIT_2D_IDLECLEAN (1 << 16) +#define WAIT_3D_IDLECLEAN (1 << 17) +#define ISYNC_CNTL 0x1724 +#define ISYNC_ANY2D_IDLE3D (1 << 0) +#define ISYNC_ANY3D_IDLE2D (1 << 1) +#define ISYNC_TRIG2D_IDLE3D (1 << 2) +#define ISYNC_TRIG3D_IDLE2D (1 << 3) +#define ISYNC_WAIT_IDLEGUI (1 << 4) +#define ISYNC_CPSCRATCH_IDLEGUI (1 << 5) +#define VAP_INDEX_OFFSET 0x208C +#define VAP_PVS_STATE_FLUSH_REG 0x2284 +#define GB_ENABLE 0x4008 +#define GB_MSPOS0 0x4010 +#define MS_X0_SHIFT 0 +#define MS_Y0_SHIFT 4 +#define MS_X1_SHIFT 8 +#define MS_Y1_SHIFT 12 +#define MS_X2_SHIFT 16 +#define MS_Y2_SHIFT 20 +#define MSBD0_Y_SHIFT 24 +#define MSBD0_X_SHIFT 28 +#define GB_MSPOS1 0x4014 +#define MS_X3_SHIFT 0 +#define MS_Y3_SHIFT 4 +#define MS_X4_SHIFT 8 +#define MS_Y4_SHIFT 12 +#define MS_X5_SHIFT 16 +#define MS_Y5_SHIFT 20 +#define MSBD1_SHIFT 24 +#define GB_TILE_CONFIG 0x4018 +#define ENABLE_TILING (1 << 0) +#define PIPE_COUNT_MASK 0x0000000E +#define PIPE_COUNT_SHIFT 1 +#define TILE_SIZE_8 (0 << 4) +#define TILE_SIZE_16 (1 << 4) +#define TILE_SIZE_32 (2 << 4) +#define SUBPIXEL_1_12 (0 << 16) +#define SUBPIXEL_1_16 (1 << 16) +#define GB_SELECT 0x401C +#define GB_AA_CONFIG 0x4020 +#define GB_PIPE_SELECT 0x402C +#define GA_ENHANCE 0x4274 +#define GA_DEADLOCK_CNTL (1 << 0) +#define GA_FASTSYNC_CNTL (1 << 1) +#define GA_POLY_MODE 0x4288 +#define FRONT_PTYPE_POINT (0 << 4) +#define FRONT_PTYPE_LINE (1 << 4) +#define FRONT_PTYPE_TRIANGE (2 << 4) +#define BACK_PTYPE_POINT (0 << 7) +#define BACK_PTYPE_LINE (1 << 7) +#define BACK_PTYPE_TRIANGE (2 << 7) +#define GA_ROUND_MODE 0x428C +#define GEOMETRY_ROUND_TRUNC (0 << 0) +#define GEOMETRY_ROUND_NEAREST (1 << 0) +#define COLOR_ROUND_TRUNC (0 << 2) +#define COLOR_ROUND_NEAREST (1 << 2) +#define SU_REG_DEST 0x42C8 +#define RB3D_DSTCACHE_CTLSTAT 0x4E4C +#define RB3D_DC_FLUSH (2 << 0) +#define RB3D_DC_FREE (2 << 2) +#define RB3D_DC_FINISH (1 << 4) +#define ZB_ZCACHE_CTLSTAT 0x4F18 +#define ZC_FLUSH (1 << 0) +#define ZC_FREE (1 << 1) +#define DC_LB_MEMORY_SPLIT 0x6520 +#define DC_LB_MEMORY_SPLIT_MASK 0x00000003 +#define DC_LB_MEMORY_SPLIT_SHIFT 0 +#define DC_LB_MEMORY_SPLIT_D1HALF_D2HALF 0 +#define DC_LB_MEMORY_SPLIT_D1_3Q_D2_1Q 1 +#define DC_LB_MEMORY_SPLIT_D1_ONLY 2 +#define DC_LB_MEMORY_SPLIT_D1_1Q_D2_3Q 3 +#define DC_LB_MEMORY_SPLIT_SHIFT_MODE (1 << 2) +#define DC_LB_DISP1_END_ADR_SHIFT 4 +#define DC_LB_DISP1_END_ADR_MASK 0x00007FF0 +#define D1MODE_PRIORITY_A_CNT 0x6548 +#define MODE_PRIORITY_MARK_MASK 0x00007FFF +#define MODE_PRIORITY_OFF (1 << 16) +#define MODE_PRIORITY_ALWAYS_ON (1 << 20) +#define MODE_PRIORITY_FORCE_MASK (1 << 24) +#define D1MODE_PRIORITY_B_CNT 0x654C +#define LB_MAX_REQ_OUTSTANDING 0x6D58 +#define LB_D1_MAX_REQ_OUTSTANDING_MASK 0x0000000F +#define LB_D1_MAX_REQ_OUTSTANDING_SHIFT 0 +#define LB_D2_MAX_REQ_OUTSTANDING_MASK 0x000F0000 +#define LB_D2_MAX_REQ_OUTSTANDING_SHIFT 16 +#define D2MODE_PRIORITY_A_CNT 0x6D48 +#define D2MODE_PRIORITY_B_CNT 0x6D4C + +/* ix[MC] registers */ +#define MC_FB_LOCATION 0x01 +#define MC_FB_START_MASK 0x0000FFFF +#define MC_FB_START_SHIFT 0 +#define MC_FB_TOP_MASK 0xFFFF0000 +#define MC_FB_TOP_SHIFT 16 +#define MC_AGP_LOCATION 0x02 +#define MC_AGP_START_MASK 0x0000FFFF +#define MC_AGP_START_SHIFT 0 +#define MC_AGP_TOP_MASK 0xFFFF0000 +#define MC_AGP_TOP_SHIFT 16 +#define MC_AGP_BASE 0x03 +#define MC_AGP_BASE_2 0x04 +#define MC_CNTL 0x5 +#define MEM_NUM_CHANNELS_MASK 0x00000003 +#define MC_STATUS 0x08 +#define MC_STATUS_IDLE (1 << 4) +#define MC_MISC_LAT_TIMER 0x09 +#define MC_CPR_INIT_LAT_MASK 0x0000000F +#define MC_VF_INIT_LAT_MASK 0x000000F0 +#define MC_DISP0R_INIT_LAT_MASK 0x00000F00 +#define MC_DISP0R_INIT_LAT_SHIFT 8 +#define MC_DISP1R_INIT_LAT_MASK 0x0000F000 +#define MC_DISP1R_INIT_LAT_SHIFT 12 +#define MC_FIXED_INIT_LAT_MASK 0x000F0000 +#define MC_E2R_INIT_LAT_MASK 0x00F00000 +#define SAME_PAGE_PRIO_MASK 0x0F000000 +#define MC_GLOBW_INIT_LAT_MASK 0xF0000000 + + +/* + * PM4 packet + */ +#define CP_PACKET0 0x00000000 +#define PACKET0_BASE_INDEX_SHIFT 0 +#define PACKET0_BASE_INDEX_MASK (0x1ffff << 0) +#define PACKET0_COUNT_SHIFT 16 +#define PACKET0_COUNT_MASK (0x3fff << 16) +#define CP_PACKET1 0x40000000 +#define CP_PACKET2 0x80000000 +#define PACKET2_PAD_SHIFT 0 +#define PACKET2_PAD_MASK (0x3fffffff << 0) +#define CP_PACKET3 0xC0000000 +#define PACKET3_IT_OPCODE_SHIFT 8 +#define PACKET3_IT_OPCODE_MASK (0xff << 8) +#define PACKET3_COUNT_SHIFT 16 +#define PACKET3_COUNT_MASK (0x3fff << 16) +/* PACKET3 op code */ +#define PACKET3_NOP 0x10 +#define PACKET3_3D_DRAW_VBUF 0x28 +#define PACKET3_3D_DRAW_IMMD 0x29 +#define PACKET3_3D_DRAW_INDX 0x2A +#define PACKET3_3D_LOAD_VBPNTR 0x2F +#define PACKET3_INDX_BUFFER 0x33 +#define PACKET3_3D_DRAW_VBUF_2 0x34 +#define PACKET3_3D_DRAW_IMMD_2 0x35 +#define PACKET3_3D_DRAW_INDX_2 0x36 +#define PACKET3_BITBLT_MULTI 0x9B + +#define PACKET0(reg, n) (CP_PACKET0 | \ + REG_SET(PACKET0_BASE_INDEX, (reg) >> 2) | \ + REG_SET(PACKET0_COUNT, (n))) +#define PACKET2(v) (CP_PACKET2 | REG_SET(PACKET2_PAD, (v))) +#define PACKET3(op, n) (CP_PACKET3 | \ + REG_SET(PACKET3_IT_OPCODE, (op)) | \ + REG_SET(PACKET3_COUNT, (n))) + +#define PACKET_TYPE0 0 +#define PACKET_TYPE1 1 +#define PACKET_TYPE2 2 +#define PACKET_TYPE3 3 + +#define CP_PACKET_GET_TYPE(h) (((h) >> 30) & 3) +#define CP_PACKET_GET_COUNT(h) (((h) >> 16) & 0x3FFF) +#define CP_PACKET0_GET_REG(h) (((h) & 0x1FFF) << 2) +#define CP_PACKET0_GET_ONE_REG_WR(h) (((h) >> 15) & 1) +#define CP_PACKET3_GET_OPCODE(h) (((h) >> 8) & 0xFF) + +/* Registers */ +#define R_0000F0_RBBM_SOFT_RESET 0x0000F0 +#define S_0000F0_SOFT_RESET_CP(x) (((x) & 0x1) << 0) +#define G_0000F0_SOFT_RESET_CP(x) (((x) >> 0) & 0x1) +#define C_0000F0_SOFT_RESET_CP 0xFFFFFFFE +#define S_0000F0_SOFT_RESET_HI(x) (((x) & 0x1) << 1) +#define G_0000F0_SOFT_RESET_HI(x) (((x) >> 1) & 0x1) +#define C_0000F0_SOFT_RESET_HI 0xFFFFFFFD +#define S_0000F0_SOFT_RESET_VAP(x) (((x) & 0x1) << 2) +#define G_0000F0_SOFT_RESET_VAP(x) (((x) >> 2) & 0x1) +#define C_0000F0_SOFT_RESET_VAP 0xFFFFFFFB +#define S_0000F0_SOFT_RESET_RE(x) (((x) & 0x1) << 3) +#define G_0000F0_SOFT_RESET_RE(x) (((x) >> 3) & 0x1) +#define C_0000F0_SOFT_RESET_RE 0xFFFFFFF7 +#define S_0000F0_SOFT_RESET_PP(x) (((x) & 0x1) << 4) +#define G_0000F0_SOFT_RESET_PP(x) (((x) >> 4) & 0x1) +#define C_0000F0_SOFT_RESET_PP 0xFFFFFFEF +#define S_0000F0_SOFT_RESET_E2(x) (((x) & 0x1) << 5) +#define G_0000F0_SOFT_RESET_E2(x) (((x) >> 5) & 0x1) +#define C_0000F0_SOFT_RESET_E2 0xFFFFFFDF +#define S_0000F0_SOFT_RESET_RB(x) (((x) & 0x1) << 6) +#define G_0000F0_SOFT_RESET_RB(x) (((x) >> 6) & 0x1) +#define C_0000F0_SOFT_RESET_RB 0xFFFFFFBF +#define S_0000F0_SOFT_RESET_HDP(x) (((x) & 0x1) << 7) +#define G_0000F0_SOFT_RESET_HDP(x) (((x) >> 7) & 0x1) +#define C_0000F0_SOFT_RESET_HDP 0xFFFFFF7F +#define S_0000F0_SOFT_RESET_MC(x) (((x) & 0x1) << 8) +#define G_0000F0_SOFT_RESET_MC(x) (((x) >> 8) & 0x1) +#define C_0000F0_SOFT_RESET_MC 0xFFFFFEFF +#define S_0000F0_SOFT_RESET_AIC(x) (((x) & 0x1) << 9) +#define G_0000F0_SOFT_RESET_AIC(x) (((x) >> 9) & 0x1) +#define C_0000F0_SOFT_RESET_AIC 0xFFFFFDFF +#define S_0000F0_SOFT_RESET_VIP(x) (((x) & 0x1) << 10) +#define G_0000F0_SOFT_RESET_VIP(x) (((x) >> 10) & 0x1) +#define C_0000F0_SOFT_RESET_VIP 0xFFFFFBFF +#define S_0000F0_SOFT_RESET_DISP(x) (((x) & 0x1) << 11) +#define G_0000F0_SOFT_RESET_DISP(x) (((x) >> 11) & 0x1) +#define C_0000F0_SOFT_RESET_DISP 0xFFFFF7FF +#define S_0000F0_SOFT_RESET_CG(x) (((x) & 0x1) << 12) +#define G_0000F0_SOFT_RESET_CG(x) (((x) >> 12) & 0x1) +#define C_0000F0_SOFT_RESET_CG 0xFFFFEFFF +#define S_0000F0_SOFT_RESET_GA(x) (((x) & 0x1) << 13) +#define G_0000F0_SOFT_RESET_GA(x) (((x) >> 13) & 0x1) +#define C_0000F0_SOFT_RESET_GA 0xFFFFDFFF +#define S_0000F0_SOFT_RESET_IDCT(x) (((x) & 0x1) << 14) +#define G_0000F0_SOFT_RESET_IDCT(x) (((x) >> 14) & 0x1) +#define C_0000F0_SOFT_RESET_IDCT 0xFFFFBFFF +#define R_0000F8_CONFIG_MEMSIZE 0x0000F8 +#define S_0000F8_CONFIG_MEMSIZE(x) (((x) & 0xFFFFFFFF) << 0) +#define G_0000F8_CONFIG_MEMSIZE(x) (((x) >> 0) & 0xFFFFFFFF) +#define C_0000F8_CONFIG_MEMSIZE 0x00000000 +#define R_000134_HDP_FB_LOCATION 0x000134 +#define S_000134_HDP_FB_START(x) (((x) & 0xFFFF) << 0) +#define G_000134_HDP_FB_START(x) (((x) >> 0) & 0xFFFF) +#define C_000134_HDP_FB_START 0xFFFF0000 +#define R_000300_VGA_RENDER_CONTROL 0x000300 +#define S_000300_VGA_BLINK_RATE(x) (((x) & 0x1F) << 0) +#define G_000300_VGA_BLINK_RATE(x) (((x) >> 0) & 0x1F) +#define C_000300_VGA_BLINK_RATE 0xFFFFFFE0 +#define S_000300_VGA_BLINK_MODE(x) (((x) & 0x3) << 5) +#define G_000300_VGA_BLINK_MODE(x) (((x) >> 5) & 0x3) +#define C_000300_VGA_BLINK_MODE 0xFFFFFF9F +#define S_000300_VGA_CURSOR_BLINK_INVERT(x) (((x) & 0x1) << 7) +#define G_000300_VGA_CURSOR_BLINK_INVERT(x) (((x) >> 7) & 0x1) +#define C_000300_VGA_CURSOR_BLINK_INVERT 0xFFFFFF7F +#define S_000300_VGA_EXTD_ADDR_COUNT_ENABLE(x) (((x) & 0x1) << 8) +#define G_000300_VGA_EXTD_ADDR_COUNT_ENABLE(x) (((x) >> 8) & 0x1) +#define C_000300_VGA_EXTD_ADDR_COUNT_ENABLE 0xFFFFFEFF +#define S_000300_VGA_VSTATUS_CNTL(x) (((x) & 0x3) << 16) +#define G_000300_VGA_VSTATUS_CNTL(x) (((x) >> 16) & 0x3) +#define C_000300_VGA_VSTATUS_CNTL 0xFFFCFFFF +#define S_000300_VGA_LOCK_8DOT(x) (((x) & 0x1) << 24) +#define G_000300_VGA_LOCK_8DOT(x) (((x) >> 24) & 0x1) +#define C_000300_VGA_LOCK_8DOT 0xFEFFFFFF +#define S_000300_VGAREG_LINECMP_COMPATIBILITY_SEL(x) (((x) & 0x1) << 25) +#define G_000300_VGAREG_LINECMP_COMPATIBILITY_SEL(x) (((x) >> 25) & 0x1) +#define C_000300_VGAREG_LINECMP_COMPATIBILITY_SEL 0xFDFFFFFF +#define R_000310_VGA_MEMORY_BASE_ADDRESS 0x000310 +#define S_000310_VGA_MEMORY_BASE_ADDRESS(x) (((x) & 0xFFFFFFFF) << 0) +#define G_000310_VGA_MEMORY_BASE_ADDRESS(x) (((x) >> 0) & 0xFFFFFFFF) +#define C_000310_VGA_MEMORY_BASE_ADDRESS 0x00000000 +#define R_000328_VGA_HDP_CONTROL 0x000328 +#define S_000328_VGA_MEM_PAGE_SELECT_EN(x) (((x) & 0x1) << 0) +#define G_000328_VGA_MEM_PAGE_SELECT_EN(x) (((x) >> 0) & 0x1) +#define C_000328_VGA_MEM_PAGE_SELECT_EN 0xFFFFFFFE +#define S_000328_VGA_RBBM_LOCK_DISABLE(x) (((x) & 0x1) << 8) +#define G_000328_VGA_RBBM_LOCK_DISABLE(x) (((x) >> 8) & 0x1) +#define C_000328_VGA_RBBM_LOCK_DISABLE 0xFFFFFEFF +#define S_000328_VGA_SOFT_RESET(x) (((x) & 0x1) << 16) +#define G_000328_VGA_SOFT_RESET(x) (((x) >> 16) & 0x1) +#define C_000328_VGA_SOFT_RESET 0xFFFEFFFF +#define S_000328_VGA_TEST_RESET_CONTROL(x) (((x) & 0x1) << 24) +#define G_000328_VGA_TEST_RESET_CONTROL(x) (((x) >> 24) & 0x1) +#define C_000328_VGA_TEST_RESET_CONTROL 0xFEFFFFFF +#define R_000330_D1VGA_CONTROL 0x000330 +#define S_000330_D1VGA_MODE_ENABLE(x) (((x) & 0x1) << 0) +#define G_000330_D1VGA_MODE_ENABLE(x) (((x) >> 0) & 0x1) +#define C_000330_D1VGA_MODE_ENABLE 0xFFFFFFFE +#define S_000330_D1VGA_TIMING_SELECT(x) (((x) & 0x1) << 8) +#define G_000330_D1VGA_TIMING_SELECT(x) (((x) >> 8) & 0x1) +#define C_000330_D1VGA_TIMING_SELECT 0xFFFFFEFF +#define S_000330_D1VGA_SYNC_POLARITY_SELECT(x) (((x) & 0x1) << 9) +#define G_000330_D1VGA_SYNC_POLARITY_SELECT(x) (((x) >> 9) & 0x1) +#define C_000330_D1VGA_SYNC_POLARITY_SELECT 0xFFFFFDFF +#define S_000330_D1VGA_OVERSCAN_TIMING_SELECT(x) (((x) & 0x1) << 10) +#define G_000330_D1VGA_OVERSCAN_TIMING_SELECT(x) (((x) >> 10) & 0x1) +#define C_000330_D1VGA_OVERSCAN_TIMING_SELECT 0xFFFFFBFF +#define S_000330_D1VGA_OVERSCAN_COLOR_EN(x) (((x) & 0x1) << 16) +#define G_000330_D1VGA_OVERSCAN_COLOR_EN(x) (((x) >> 16) & 0x1) +#define C_000330_D1VGA_OVERSCAN_COLOR_EN 0xFFFEFFFF +#define S_000330_D1VGA_ROTATE(x) (((x) & 0x3) << 24) +#define G_000330_D1VGA_ROTATE(x) (((x) >> 24) & 0x3) +#define C_000330_D1VGA_ROTATE 0xFCFFFFFF +#define R_000338_D2VGA_CONTROL 0x000338 +#define S_000338_D2VGA_MODE_ENABLE(x) (((x) & 0x1) << 0) +#define G_000338_D2VGA_MODE_ENABLE(x) (((x) >> 0) & 0x1) +#define C_000338_D2VGA_MODE_ENABLE 0xFFFFFFFE +#define S_000338_D2VGA_TIMING_SELECT(x) (((x) & 0x1) << 8) +#define G_000338_D2VGA_TIMING_SELECT(x) (((x) >> 8) & 0x1) +#define C_000338_D2VGA_TIMING_SELECT 0xFFFFFEFF +#define S_000338_D2VGA_SYNC_POLARITY_SELECT(x) (((x) & 0x1) << 9) +#define G_000338_D2VGA_SYNC_POLARITY_SELECT(x) (((x) >> 9) & 0x1) +#define C_000338_D2VGA_SYNC_POLARITY_SELECT 0xFFFFFDFF +#define S_000338_D2VGA_OVERSCAN_TIMING_SELECT(x) (((x) & 0x1) << 10) +#define G_000338_D2VGA_OVERSCAN_TIMING_SELECT(x) (((x) >> 10) & 0x1) +#define C_000338_D2VGA_OVERSCAN_TIMING_SELECT 0xFFFFFBFF +#define S_000338_D2VGA_OVERSCAN_COLOR_EN(x) (((x) & 0x1) << 16) +#define G_000338_D2VGA_OVERSCAN_COLOR_EN(x) (((x) >> 16) & 0x1) +#define C_000338_D2VGA_OVERSCAN_COLOR_EN 0xFFFEFFFF +#define S_000338_D2VGA_ROTATE(x) (((x) & 0x3) << 24) +#define G_000338_D2VGA_ROTATE(x) (((x) >> 24) & 0x3) +#define C_000338_D2VGA_ROTATE 0xFCFFFFFF +#define R_0007C0_CP_STAT 0x0007C0 +#define S_0007C0_MRU_BUSY(x) (((x) & 0x1) << 0) +#define G_0007C0_MRU_BUSY(x) (((x) >> 0) & 0x1) +#define C_0007C0_MRU_BUSY 0xFFFFFFFE +#define S_0007C0_MWU_BUSY(x) (((x) & 0x1) << 1) +#define G_0007C0_MWU_BUSY(x) (((x) >> 1) & 0x1) +#define C_0007C0_MWU_BUSY 0xFFFFFFFD +#define S_0007C0_RSIU_BUSY(x) (((x) & 0x1) << 2) +#define G_0007C0_RSIU_BUSY(x) (((x) >> 2) & 0x1) +#define C_0007C0_RSIU_BUSY 0xFFFFFFFB +#define S_0007C0_RCIU_BUSY(x) (((x) & 0x1) << 3) +#define G_0007C0_RCIU_BUSY(x) (((x) >> 3) & 0x1) +#define C_0007C0_RCIU_BUSY 0xFFFFFFF7 +#define S_0007C0_CSF_PRIMARY_BUSY(x) (((x) & 0x1) << 9) +#define G_0007C0_CSF_PRIMARY_BUSY(x) (((x) >> 9) & 0x1) +#define C_0007C0_CSF_PRIMARY_BUSY 0xFFFFFDFF +#define S_0007C0_CSF_INDIRECT_BUSY(x) (((x) & 0x1) << 10) +#define G_0007C0_CSF_INDIRECT_BUSY(x) (((x) >> 10) & 0x1) +#define C_0007C0_CSF_INDIRECT_BUSY 0xFFFFFBFF +#define S_0007C0_CSQ_PRIMARY_BUSY(x) (((x) & 0x1) << 11) +#define G_0007C0_CSQ_PRIMARY_BUSY(x) (((x) >> 11) & 0x1) +#define C_0007C0_CSQ_PRIMARY_BUSY 0xFFFFF7FF +#define S_0007C0_CSQ_INDIRECT_BUSY(x) (((x) & 0x1) << 12) +#define G_0007C0_CSQ_INDIRECT_BUSY(x) (((x) >> 12) & 0x1) +#define C_0007C0_CSQ_INDIRECT_BUSY 0xFFFFEFFF +#define S_0007C0_CSI_BUSY(x) (((x) & 0x1) << 13) +#define G_0007C0_CSI_BUSY(x) (((x) >> 13) & 0x1) +#define C_0007C0_CSI_BUSY 0xFFFFDFFF +#define S_0007C0_CSF_INDIRECT2_BUSY(x) (((x) & 0x1) << 14) +#define G_0007C0_CSF_INDIRECT2_BUSY(x) (((x) >> 14) & 0x1) +#define C_0007C0_CSF_INDIRECT2_BUSY 0xFFFFBFFF +#define S_0007C0_CSQ_INDIRECT2_BUSY(x) (((x) & 0x1) << 15) +#define G_0007C0_CSQ_INDIRECT2_BUSY(x) (((x) >> 15) & 0x1) +#define C_0007C0_CSQ_INDIRECT2_BUSY 0xFFFF7FFF +#define S_0007C0_GUIDMA_BUSY(x) (((x) & 0x1) << 28) +#define G_0007C0_GUIDMA_BUSY(x) (((x) >> 28) & 0x1) +#define C_0007C0_GUIDMA_BUSY 0xEFFFFFFF +#define S_0007C0_VIDDMA_BUSY(x) (((x) & 0x1) << 29) +#define G_0007C0_VIDDMA_BUSY(x) (((x) >> 29) & 0x1) +#define C_0007C0_VIDDMA_BUSY 0xDFFFFFFF +#define S_0007C0_CMDSTRM_BUSY(x) (((x) & 0x1) << 30) +#define G_0007C0_CMDSTRM_BUSY(x) (((x) >> 30) & 0x1) +#define C_0007C0_CMDSTRM_BUSY 0xBFFFFFFF +#define S_0007C0_CP_BUSY(x) (((x) & 0x1) << 31) +#define G_0007C0_CP_BUSY(x) (((x) >> 31) & 0x1) +#define C_0007C0_CP_BUSY 0x7FFFFFFF +#define R_000E40_RBBM_STATUS 0x000E40 +#define S_000E40_CMDFIFO_AVAIL(x) (((x) & 0x7F) << 0) +#define G_000E40_CMDFIFO_AVAIL(x) (((x) >> 0) & 0x7F) +#define C_000E40_CMDFIFO_AVAIL 0xFFFFFF80 +#define S_000E40_HIRQ_ON_RBB(x) (((x) & 0x1) << 8) +#define G_000E40_HIRQ_ON_RBB(x) (((x) >> 8) & 0x1) +#define C_000E40_HIRQ_ON_RBB 0xFFFFFEFF +#define S_000E40_CPRQ_ON_RBB(x) (((x) & 0x1) << 9) +#define G_000E40_CPRQ_ON_RBB(x) (((x) >> 9) & 0x1) +#define C_000E40_CPRQ_ON_RBB 0xFFFFFDFF +#define S_000E40_CFRQ_ON_RBB(x) (((x) & 0x1) << 10) +#define G_000E40_CFRQ_ON_RBB(x) (((x) >> 10) & 0x1) +#define C_000E40_CFRQ_ON_RBB 0xFFFFFBFF +#define S_000E40_HIRQ_IN_RTBUF(x) (((x) & 0x1) << 11) +#define G_000E40_HIRQ_IN_RTBUF(x) (((x) >> 11) & 0x1) +#define C_000E40_HIRQ_IN_RTBUF 0xFFFFF7FF +#define S_000E40_CPRQ_IN_RTBUF(x) (((x) & 0x1) << 12) +#define G_000E40_CPRQ_IN_RTBUF(x) (((x) >> 12) & 0x1) +#define C_000E40_CPRQ_IN_RTBUF 0xFFFFEFFF +#define S_000E40_CFRQ_IN_RTBUF(x) (((x) & 0x1) << 13) +#define G_000E40_CFRQ_IN_RTBUF(x) (((x) >> 13) & 0x1) +#define C_000E40_CFRQ_IN_RTBUF 0xFFFFDFFF +#define S_000E40_CF_PIPE_BUSY(x) (((x) & 0x1) << 14) +#define G_000E40_CF_PIPE_BUSY(x) (((x) >> 14) & 0x1) +#define C_000E40_CF_PIPE_BUSY 0xFFFFBFFF +#define S_000E40_ENG_EV_BUSY(x) (((x) & 0x1) << 15) +#define G_000E40_ENG_EV_BUSY(x) (((x) >> 15) & 0x1) +#define C_000E40_ENG_EV_BUSY 0xFFFF7FFF +#define S_000E40_CP_CMDSTRM_BUSY(x) (((x) & 0x1) << 16) +#define G_000E40_CP_CMDSTRM_BUSY(x) (((x) >> 16) & 0x1) +#define C_000E40_CP_CMDSTRM_BUSY 0xFFFEFFFF +#define S_000E40_E2_BUSY(x) (((x) & 0x1) << 17) +#define G_000E40_E2_BUSY(x) (((x) >> 17) & 0x1) +#define C_000E40_E2_BUSY 0xFFFDFFFF +#define S_000E40_RB2D_BUSY(x) (((x) & 0x1) << 18) +#define G_000E40_RB2D_BUSY(x) (((x) >> 18) & 0x1) +#define C_000E40_RB2D_BUSY 0xFFFBFFFF +#define S_000E40_RB3D_BUSY(x) (((x) & 0x1) << 19) +#define G_000E40_RB3D_BUSY(x) (((x) >> 19) & 0x1) +#define C_000E40_RB3D_BUSY 0xFFF7FFFF +#define S_000E40_VAP_BUSY(x) (((x) & 0x1) << 20) +#define G_000E40_VAP_BUSY(x) (((x) >> 20) & 0x1) +#define C_000E40_VAP_BUSY 0xFFEFFFFF +#define S_000E40_RE_BUSY(x) (((x) & 0x1) << 21) +#define G_000E40_RE_BUSY(x) (((x) >> 21) & 0x1) +#define C_000E40_RE_BUSY 0xFFDFFFFF +#define S_000E40_TAM_BUSY(x) (((x) & 0x1) << 22) +#define G_000E40_TAM_BUSY(x) (((x) >> 22) & 0x1) +#define C_000E40_TAM_BUSY 0xFFBFFFFF +#define S_000E40_TDM_BUSY(x) (((x) & 0x1) << 23) +#define G_000E40_TDM_BUSY(x) (((x) >> 23) & 0x1) +#define C_000E40_TDM_BUSY 0xFF7FFFFF +#define S_000E40_PB_BUSY(x) (((x) & 0x1) << 24) +#define G_000E40_PB_BUSY(x) (((x) >> 24) & 0x1) +#define C_000E40_PB_BUSY 0xFEFFFFFF +#define S_000E40_TIM_BUSY(x) (((x) & 0x1) << 25) +#define G_000E40_TIM_BUSY(x) (((x) >> 25) & 0x1) +#define C_000E40_TIM_BUSY 0xFDFFFFFF +#define S_000E40_GA_BUSY(x) (((x) & 0x1) << 26) +#define G_000E40_GA_BUSY(x) (((x) >> 26) & 0x1) +#define C_000E40_GA_BUSY 0xFBFFFFFF +#define S_000E40_CBA2D_BUSY(x) (((x) & 0x1) << 27) +#define G_000E40_CBA2D_BUSY(x) (((x) >> 27) & 0x1) +#define C_000E40_CBA2D_BUSY 0xF7FFFFFF +#define S_000E40_RBBM_HIBUSY(x) (((x) & 0x1) << 28) +#define G_000E40_RBBM_HIBUSY(x) (((x) >> 28) & 0x1) +#define C_000E40_RBBM_HIBUSY 0xEFFFFFFF +#define S_000E40_SKID_CFBUSY(x) (((x) & 0x1) << 29) +#define G_000E40_SKID_CFBUSY(x) (((x) >> 29) & 0x1) +#define C_000E40_SKID_CFBUSY 0xDFFFFFFF +#define S_000E40_VAP_VF_BUSY(x) (((x) & 0x1) << 30) +#define G_000E40_VAP_VF_BUSY(x) (((x) >> 30) & 0x1) +#define C_000E40_VAP_VF_BUSY 0xBFFFFFFF +#define S_000E40_GUI_ACTIVE(x) (((x) & 0x1) << 31) +#define G_000E40_GUI_ACTIVE(x) (((x) >> 31) & 0x1) +#define C_000E40_GUI_ACTIVE 0x7FFFFFFF +#define R_006080_D1CRTC_CONTROL 0x006080 +#define S_006080_D1CRTC_MASTER_EN(x) (((x) & 0x1) << 0) +#define G_006080_D1CRTC_MASTER_EN(x) (((x) >> 0) & 0x1) +#define C_006080_D1CRTC_MASTER_EN 0xFFFFFFFE +#define S_006080_D1CRTC_SYNC_RESET_SEL(x) (((x) & 0x1) << 4) +#define G_006080_D1CRTC_SYNC_RESET_SEL(x) (((x) >> 4) & 0x1) +#define C_006080_D1CRTC_SYNC_RESET_SEL 0xFFFFFFEF +#define S_006080_D1CRTC_DISABLE_POINT_CNTL(x) (((x) & 0x3) << 8) +#define G_006080_D1CRTC_DISABLE_POINT_CNTL(x) (((x) >> 8) & 0x3) +#define C_006080_D1CRTC_DISABLE_POINT_CNTL 0xFFFFFCFF +#define S_006080_D1CRTC_CURRENT_MASTER_EN_STATE(x) (((x) & 0x1) << 16) +#define G_006080_D1CRTC_CURRENT_MASTER_EN_STATE(x) (((x) >> 16) & 0x1) +#define C_006080_D1CRTC_CURRENT_MASTER_EN_STATE 0xFFFEFFFF +#define S_006080_D1CRTC_DISP_READ_REQUEST_DISABLE(x) (((x) & 0x1) << 24) +#define G_006080_D1CRTC_DISP_READ_REQUEST_DISABLE(x) (((x) >> 24) & 0x1) +#define C_006080_D1CRTC_DISP_READ_REQUEST_DISABLE 0xFEFFFFFF +#define R_0060E8_D1CRTC_UPDATE_LOCK 0x0060E8 +#define S_0060E8_D1CRTC_UPDATE_LOCK(x) (((x) & 0x1) << 0) +#define G_0060E8_D1CRTC_UPDATE_LOCK(x) (((x) >> 0) & 0x1) +#define C_0060E8_D1CRTC_UPDATE_LOCK 0xFFFFFFFE +#define R_006110_D1GRPH_PRIMARY_SURFACE_ADDRESS 0x006110 +#define S_006110_D1GRPH_PRIMARY_SURFACE_ADDRESS(x) (((x) & 0xFFFFFFFF) << 0) +#define G_006110_D1GRPH_PRIMARY_SURFACE_ADDRESS(x) (((x) >> 0) & 0xFFFFFFFF) +#define C_006110_D1GRPH_PRIMARY_SURFACE_ADDRESS 0x00000000 +#define R_006118_D1GRPH_SECONDARY_SURFACE_ADDRESS 0x006118 +#define S_006118_D1GRPH_SECONDARY_SURFACE_ADDRESS(x) (((x) & 0xFFFFFFFF) << 0) +#define G_006118_D1GRPH_SECONDARY_SURFACE_ADDRESS(x) (((x) >> 0) & 0xFFFFFFFF) +#define C_006118_D1GRPH_SECONDARY_SURFACE_ADDRESS 0x00000000 +#define R_006880_D2CRTC_CONTROL 0x006880 +#define S_006880_D2CRTC_MASTER_EN(x) (((x) & 0x1) << 0) +#define G_006880_D2CRTC_MASTER_EN(x) (((x) >> 0) & 0x1) +#define C_006880_D2CRTC_MASTER_EN 0xFFFFFFFE +#define S_006880_D2CRTC_SYNC_RESET_SEL(x) (((x) & 0x1) << 4) +#define G_006880_D2CRTC_SYNC_RESET_SEL(x) (((x) >> 4) & 0x1) +#define C_006880_D2CRTC_SYNC_RESET_SEL 0xFFFFFFEF +#define S_006880_D2CRTC_DISABLE_POINT_CNTL(x) (((x) & 0x3) << 8) +#define G_006880_D2CRTC_DISABLE_POINT_CNTL(x) (((x) >> 8) & 0x3) +#define C_006880_D2CRTC_DISABLE_POINT_CNTL 0xFFFFFCFF +#define S_006880_D2CRTC_CURRENT_MASTER_EN_STATE(x) (((x) & 0x1) << 16) +#define G_006880_D2CRTC_CURRENT_MASTER_EN_STATE(x) (((x) >> 16) & 0x1) +#define C_006880_D2CRTC_CURRENT_MASTER_EN_STATE 0xFFFEFFFF +#define S_006880_D2CRTC_DISP_READ_REQUEST_DISABLE(x) (((x) & 0x1) << 24) +#define G_006880_D2CRTC_DISP_READ_REQUEST_DISABLE(x) (((x) >> 24) & 0x1) +#define C_006880_D2CRTC_DISP_READ_REQUEST_DISABLE 0xFEFFFFFF +#define R_0068E8_D2CRTC_UPDATE_LOCK 0x0068E8 +#define S_0068E8_D2CRTC_UPDATE_LOCK(x) (((x) & 0x1) << 0) +#define G_0068E8_D2CRTC_UPDATE_LOCK(x) (((x) >> 0) & 0x1) +#define C_0068E8_D2CRTC_UPDATE_LOCK 0xFFFFFFFE +#define R_006910_D2GRPH_PRIMARY_SURFACE_ADDRESS 0x006910 +#define S_006910_D2GRPH_PRIMARY_SURFACE_ADDRESS(x) (((x) & 0xFFFFFFFF) << 0) +#define G_006910_D2GRPH_PRIMARY_SURFACE_ADDRESS(x) (((x) >> 0) & 0xFFFFFFFF) +#define C_006910_D2GRPH_PRIMARY_SURFACE_ADDRESS 0x00000000 +#define R_006918_D2GRPH_SECONDARY_SURFACE_ADDRESS 0x006918 +#define S_006918_D2GRPH_SECONDARY_SURFACE_ADDRESS(x) (((x) & 0xFFFFFFFF) << 0) +#define G_006918_D2GRPH_SECONDARY_SURFACE_ADDRESS(x) (((x) >> 0) & 0xFFFFFFFF) +#define C_006918_D2GRPH_SECONDARY_SURFACE_ADDRESS 0x00000000 + + +#define R_000001_MC_FB_LOCATION 0x000001 +#define S_000001_MC_FB_START(x) (((x) & 0xFFFF) << 0) +#define G_000001_MC_FB_START(x) (((x) >> 0) & 0xFFFF) +#define C_000001_MC_FB_START 0xFFFF0000 +#define S_000001_MC_FB_TOP(x) (((x) & 0xFFFF) << 16) +#define G_000001_MC_FB_TOP(x) (((x) >> 16) & 0xFFFF) +#define C_000001_MC_FB_TOP 0x0000FFFF +#define R_000002_MC_AGP_LOCATION 0x000002 +#define S_000002_MC_AGP_START(x) (((x) & 0xFFFF) << 0) +#define G_000002_MC_AGP_START(x) (((x) >> 0) & 0xFFFF) +#define C_000002_MC_AGP_START 0xFFFF0000 +#define S_000002_MC_AGP_TOP(x) (((x) & 0xFFFF) << 16) +#define G_000002_MC_AGP_TOP(x) (((x) >> 16) & 0xFFFF) +#define C_000002_MC_AGP_TOP 0x0000FFFF +#define R_000003_MC_AGP_BASE 0x000003 +#define S_000003_AGP_BASE_ADDR(x) (((x) & 0xFFFFFFFF) << 0) +#define G_000003_AGP_BASE_ADDR(x) (((x) >> 0) & 0xFFFFFFFF) +#define C_000003_AGP_BASE_ADDR 0x00000000 +#define R_000004_MC_AGP_BASE_2 0x000004 +#define S_000004_AGP_BASE_ADDR_2(x) (((x) & 0xF) << 0) +#define G_000004_AGP_BASE_ADDR_2(x) (((x) >> 0) & 0xF) +#define C_000004_AGP_BASE_ADDR_2 0xFFFFFFF0 + + +#define R_00000F_CP_DYN_CNTL 0x00000F +#define S_00000F_CP_FORCEON(x) (((x) & 0x1) << 0) +#define G_00000F_CP_FORCEON(x) (((x) >> 0) & 0x1) +#define C_00000F_CP_FORCEON 0xFFFFFFFE +#define S_00000F_CP_MAX_DYN_STOP_LAT(x) (((x) & 0x1) << 1) +#define G_00000F_CP_MAX_DYN_STOP_LAT(x) (((x) >> 1) & 0x1) +#define C_00000F_CP_MAX_DYN_STOP_LAT 0xFFFFFFFD +#define S_00000F_CP_CLOCK_STATUS(x) (((x) & 0x1) << 2) +#define G_00000F_CP_CLOCK_STATUS(x) (((x) >> 2) & 0x1) +#define C_00000F_CP_CLOCK_STATUS 0xFFFFFFFB +#define S_00000F_CP_PROG_SHUTOFF(x) (((x) & 0x1) << 3) +#define G_00000F_CP_PROG_SHUTOFF(x) (((x) >> 3) & 0x1) +#define C_00000F_CP_PROG_SHUTOFF 0xFFFFFFF7 +#define S_00000F_CP_PROG_DELAY_VALUE(x) (((x) & 0xFF) << 4) +#define G_00000F_CP_PROG_DELAY_VALUE(x) (((x) >> 4) & 0xFF) +#define C_00000F_CP_PROG_DELAY_VALUE 0xFFFFF00F +#define S_00000F_CP_LOWER_POWER_IDLE(x) (((x) & 0xFF) << 12) +#define G_00000F_CP_LOWER_POWER_IDLE(x) (((x) >> 12) & 0xFF) +#define C_00000F_CP_LOWER_POWER_IDLE 0xFFF00FFF +#define S_00000F_CP_LOWER_POWER_IGNORE(x) (((x) & 0x1) << 20) +#define G_00000F_CP_LOWER_POWER_IGNORE(x) (((x) >> 20) & 0x1) +#define C_00000F_CP_LOWER_POWER_IGNORE 0xFFEFFFFF +#define S_00000F_CP_NORMAL_POWER_IGNORE(x) (((x) & 0x1) << 21) +#define G_00000F_CP_NORMAL_POWER_IGNORE(x) (((x) >> 21) & 0x1) +#define C_00000F_CP_NORMAL_POWER_IGNORE 0xFFDFFFFF +#define S_00000F_SPARE(x) (((x) & 0x3) << 22) +#define G_00000F_SPARE(x) (((x) >> 22) & 0x3) +#define C_00000F_SPARE 0xFF3FFFFF +#define S_00000F_CP_NORMAL_POWER_BUSY(x) (((x) & 0xFF) << 24) +#define G_00000F_CP_NORMAL_POWER_BUSY(x) (((x) >> 24) & 0xFF) +#define C_00000F_CP_NORMAL_POWER_BUSY 0x00FFFFFF +#define R_000011_E2_DYN_CNTL 0x000011 +#define S_000011_E2_FORCEON(x) (((x) & 0x1) << 0) +#define G_000011_E2_FORCEON(x) (((x) >> 0) & 0x1) +#define C_000011_E2_FORCEON 0xFFFFFFFE +#define S_000011_E2_MAX_DYN_STOP_LAT(x) (((x) & 0x1) << 1) +#define G_000011_E2_MAX_DYN_STOP_LAT(x) (((x) >> 1) & 0x1) +#define C_000011_E2_MAX_DYN_STOP_LAT 0xFFFFFFFD +#define S_000011_E2_CLOCK_STATUS(x) (((x) & 0x1) << 2) +#define G_000011_E2_CLOCK_STATUS(x) (((x) >> 2) & 0x1) +#define C_000011_E2_CLOCK_STATUS 0xFFFFFFFB +#define S_000011_E2_PROG_SHUTOFF(x) (((x) & 0x1) << 3) +#define G_000011_E2_PROG_SHUTOFF(x) (((x) >> 3) & 0x1) +#define C_000011_E2_PROG_SHUTOFF 0xFFFFFFF7 +#define S_000011_E2_PROG_DELAY_VALUE(x) (((x) & 0xFF) << 4) +#define G_000011_E2_PROG_DELAY_VALUE(x) (((x) >> 4) & 0xFF) +#define C_000011_E2_PROG_DELAY_VALUE 0xFFFFF00F +#define S_000011_E2_LOWER_POWER_IDLE(x) (((x) & 0xFF) << 12) +#define G_000011_E2_LOWER_POWER_IDLE(x) (((x) >> 12) & 0xFF) +#define C_000011_E2_LOWER_POWER_IDLE 0xFFF00FFF +#define S_000011_E2_LOWER_POWER_IGNORE(x) (((x) & 0x1) << 20) +#define G_000011_E2_LOWER_POWER_IGNORE(x) (((x) >> 20) & 0x1) +#define C_000011_E2_LOWER_POWER_IGNORE 0xFFEFFFFF +#define S_000011_E2_NORMAL_POWER_IGNORE(x) (((x) & 0x1) << 21) +#define G_000011_E2_NORMAL_POWER_IGNORE(x) (((x) >> 21) & 0x1) +#define C_000011_E2_NORMAL_POWER_IGNORE 0xFFDFFFFF +#define S_000011_SPARE(x) (((x) & 0x3) << 22) +#define G_000011_SPARE(x) (((x) >> 22) & 0x3) +#define C_000011_SPARE 0xFF3FFFFF +#define S_000011_E2_NORMAL_POWER_BUSY(x) (((x) & 0xFF) << 24) +#define G_000011_E2_NORMAL_POWER_BUSY(x) (((x) >> 24) & 0xFF) +#define C_000011_E2_NORMAL_POWER_BUSY 0x00FFFFFF +#define R_000013_IDCT_DYN_CNTL 0x000013 +#define S_000013_IDCT_FORCEON(x) (((x) & 0x1) << 0) +#define G_000013_IDCT_FORCEON(x) (((x) >> 0) & 0x1) +#define C_000013_IDCT_FORCEON 0xFFFFFFFE +#define S_000013_IDCT_MAX_DYN_STOP_LAT(x) (((x) & 0x1) << 1) +#define G_000013_IDCT_MAX_DYN_STOP_LAT(x) (((x) >> 1) & 0x1) +#define C_000013_IDCT_MAX_DYN_STOP_LAT 0xFFFFFFFD +#define S_000013_IDCT_CLOCK_STATUS(x) (((x) & 0x1) << 2) +#define G_000013_IDCT_CLOCK_STATUS(x) (((x) >> 2) & 0x1) +#define C_000013_IDCT_CLOCK_STATUS 0xFFFFFFFB +#define S_000013_IDCT_PROG_SHUTOFF(x) (((x) & 0x1) << 3) +#define G_000013_IDCT_PROG_SHUTOFF(x) (((x) >> 3) & 0x1) +#define C_000013_IDCT_PROG_SHUTOFF 0xFFFFFFF7 +#define S_000013_IDCT_PROG_DELAY_VALUE(x) (((x) & 0xFF) << 4) +#define G_000013_IDCT_PROG_DELAY_VALUE(x) (((x) >> 4) & 0xFF) +#define C_000013_IDCT_PROG_DELAY_VALUE 0xFFFFF00F +#define S_000013_IDCT_LOWER_POWER_IDLE(x) (((x) & 0xFF) << 12) +#define G_000013_IDCT_LOWER_POWER_IDLE(x) (((x) >> 12) & 0xFF) +#define C_000013_IDCT_LOWER_POWER_IDLE 0xFFF00FFF +#define S_000013_IDCT_LOWER_POWER_IGNORE(x) (((x) & 0x1) << 20) +#define G_000013_IDCT_LOWER_POWER_IGNORE(x) (((x) >> 20) & 0x1) +#define C_000013_IDCT_LOWER_POWER_IGNORE 0xFFEFFFFF +#define S_000013_IDCT_NORMAL_POWER_IGNORE(x) (((x) & 0x1) << 21) +#define G_000013_IDCT_NORMAL_POWER_IGNORE(x) (((x) >> 21) & 0x1) +#define C_000013_IDCT_NORMAL_POWER_IGNORE 0xFFDFFFFF +#define S_000013_SPARE(x) (((x) & 0x3) << 22) +#define G_000013_SPARE(x) (((x) >> 22) & 0x3) +#define C_000013_SPARE 0xFF3FFFFF +#define S_000013_IDCT_NORMAL_POWER_BUSY(x) (((x) & 0xFF) << 24) +#define G_000013_IDCT_NORMAL_POWER_BUSY(x) (((x) >> 24) & 0xFF) +#define C_000013_IDCT_NORMAL_POWER_BUSY 0x00FFFFFF + +#endif diff --git a/sys/dev/drm2/radeon/rv770.c b/sys/dev/drm2/radeon/rv770.c new file mode 100644 index 00000000000..cb875bd9da9 --- /dev/null +++ b/sys/dev/drm2/radeon/rv770.c @@ -0,0 +1,1297 @@ +/* + * Copyright 2008 Advanced Micro Devices, Inc. + * Copyright 2008 Red Hat Inc. + * Copyright 2009 Jerome Glisse. + * + * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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. + * + * Authors: Dave Airlie + * Alex Deucher + * Jerome Glisse + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include "radeon.h" +#include "radeon_asic.h" +#include +#include "rv770d.h" +#include "atom.h" +#include "avivod.h" + +#define R700_PFP_UCODE_SIZE 848 +#define R700_PM4_UCODE_SIZE 1360 + +static void rv770_gpu_init(struct radeon_device *rdev); +static void rv770_pcie_gen2_enable(struct radeon_device *rdev); + +u32 rv770_page_flip(struct radeon_device *rdev, int crtc_id, u64 crtc_base) +{ + struct radeon_crtc *radeon_crtc = rdev->mode_info.crtcs[crtc_id]; + u32 tmp = RREG32(AVIVO_D1GRPH_UPDATE + radeon_crtc->crtc_offset); + int i; + + /* Lock the graphics update lock */ + tmp |= AVIVO_D1GRPH_UPDATE_LOCK; + WREG32(AVIVO_D1GRPH_UPDATE + radeon_crtc->crtc_offset, tmp); + + /* update the scanout addresses */ + if (radeon_crtc->crtc_id) { + WREG32(D2GRPH_SECONDARY_SURFACE_ADDRESS_HIGH, upper_32_bits(crtc_base)); + WREG32(D2GRPH_PRIMARY_SURFACE_ADDRESS_HIGH, upper_32_bits(crtc_base)); + } else { + WREG32(D1GRPH_SECONDARY_SURFACE_ADDRESS_HIGH, upper_32_bits(crtc_base)); + WREG32(D1GRPH_PRIMARY_SURFACE_ADDRESS_HIGH, upper_32_bits(crtc_base)); + } + WREG32(D1GRPH_SECONDARY_SURFACE_ADDRESS + radeon_crtc->crtc_offset, + (u32)crtc_base); + WREG32(D1GRPH_PRIMARY_SURFACE_ADDRESS + radeon_crtc->crtc_offset, + (u32)crtc_base); + + /* Wait for update_pending to go high. */ + for (i = 0; i < rdev->usec_timeout; i++) { + if (RREG32(AVIVO_D1GRPH_UPDATE + radeon_crtc->crtc_offset) & AVIVO_D1GRPH_SURFACE_UPDATE_PENDING) + break; + DRM_UDELAY(1); + } + DRM_DEBUG("Update pending now high. Unlocking vupdate_lock.\n"); + + /* Unlock the lock, so double-buffering can take place inside vblank */ + tmp &= ~AVIVO_D1GRPH_UPDATE_LOCK; + WREG32(AVIVO_D1GRPH_UPDATE + radeon_crtc->crtc_offset, tmp); + + /* Return current update_pending status: */ + return RREG32(AVIVO_D1GRPH_UPDATE + radeon_crtc->crtc_offset) & AVIVO_D1GRPH_SURFACE_UPDATE_PENDING; +} + +/* get temperature in millidegrees */ +int rv770_get_temp(struct radeon_device *rdev) +{ + u32 temp = (RREG32(CG_MULT_THERMAL_STATUS) & ASIC_T_MASK) >> + ASIC_T_SHIFT; + int actual_temp; + + if (temp & 0x400) + actual_temp = -256; + else if (temp & 0x200) + actual_temp = 255; + else if (temp & 0x100) { + actual_temp = temp & 0x1ff; + actual_temp |= ~0x1ff; + } else + actual_temp = temp & 0xff; + + return (actual_temp * 1000) / 2; +} + +void rv770_pm_misc(struct radeon_device *rdev) +{ + int req_ps_idx = rdev->pm.requested_power_state_index; + int req_cm_idx = rdev->pm.requested_clock_mode_index; + struct radeon_power_state *ps = &rdev->pm.power_state[req_ps_idx]; + struct radeon_voltage *voltage = &ps->clock_info[req_cm_idx].voltage; + + if ((voltage->type == VOLTAGE_SW) && voltage->voltage) { + /* 0xff01 is a flag rather then an actual voltage */ + if (voltage->voltage == 0xff01) + return; + if (voltage->voltage != rdev->pm.current_vddc) { + radeon_atom_set_voltage(rdev, voltage->voltage, SET_VOLTAGE_TYPE_ASIC_VDDC); + rdev->pm.current_vddc = voltage->voltage; + DRM_DEBUG("Setting: v: %d\n", voltage->voltage); + } + } +} + +/* + * GART + */ +static int rv770_pcie_gart_enable(struct radeon_device *rdev) +{ + u32 tmp; + int r, i; + + if (rdev->gart.robj == NULL) { + dev_err(rdev->dev, "No VRAM object for PCIE GART.\n"); + return -EINVAL; + } + r = radeon_gart_table_vram_pin(rdev); + if (r) + return r; + radeon_gart_restore(rdev); + /* Setup L2 cache */ + WREG32(VM_L2_CNTL, ENABLE_L2_CACHE | ENABLE_L2_FRAGMENT_PROCESSING | + ENABLE_L2_PTE_CACHE_LRU_UPDATE_BY_WRITE | + EFFECTIVE_L2_QUEUE_SIZE(7)); + WREG32(VM_L2_CNTL2, 0); + WREG32(VM_L2_CNTL3, BANK_SELECT(0) | CACHE_UPDATE_MODE(2)); + /* Setup TLB control */ + tmp = ENABLE_L1_TLB | ENABLE_L1_FRAGMENT_PROCESSING | + SYSTEM_ACCESS_MODE_NOT_IN_SYS | + SYSTEM_APERTURE_UNMAPPED_ACCESS_PASS_THRU | + EFFECTIVE_L1_TLB_SIZE(5) | EFFECTIVE_L1_QUEUE_SIZE(5); + WREG32(MC_VM_MD_L1_TLB0_CNTL, tmp); + WREG32(MC_VM_MD_L1_TLB1_CNTL, tmp); + WREG32(MC_VM_MD_L1_TLB2_CNTL, tmp); + if (rdev->family == CHIP_RV740) + WREG32(MC_VM_MD_L1_TLB3_CNTL, tmp); + WREG32(MC_VM_MB_L1_TLB0_CNTL, tmp); + WREG32(MC_VM_MB_L1_TLB1_CNTL, tmp); + WREG32(MC_VM_MB_L1_TLB2_CNTL, tmp); + WREG32(MC_VM_MB_L1_TLB3_CNTL, tmp); + WREG32(VM_CONTEXT0_PAGE_TABLE_START_ADDR, rdev->mc.gtt_start >> 12); + WREG32(VM_CONTEXT0_PAGE_TABLE_END_ADDR, rdev->mc.gtt_end >> 12); + WREG32(VM_CONTEXT0_PAGE_TABLE_BASE_ADDR, rdev->gart.table_addr >> 12); + WREG32(VM_CONTEXT0_CNTL, ENABLE_CONTEXT | PAGE_TABLE_DEPTH(0) | + RANGE_PROTECTION_FAULT_ENABLE_DEFAULT); + WREG32(VM_CONTEXT0_PROTECTION_FAULT_DEFAULT_ADDR, + (u32)(rdev->dummy_page.addr >> 12)); + for (i = 1; i < 7; i++) + WREG32(VM_CONTEXT0_CNTL + (i * 4), 0); + + r600_pcie_gart_tlb_flush(rdev); + DRM_INFO("PCIE GART of %uM enabled (table at 0x%016llX).\n", + (unsigned)(rdev->mc.gtt_size >> 20), + (unsigned long long)rdev->gart.table_addr); + rdev->gart.ready = true; + return 0; +} + +static void rv770_pcie_gart_disable(struct radeon_device *rdev) +{ + u32 tmp; + int i; + + /* Disable all tables */ + for (i = 0; i < 7; i++) + WREG32(VM_CONTEXT0_CNTL + (i * 4), 0); + + /* Setup L2 cache */ + WREG32(VM_L2_CNTL, ENABLE_L2_FRAGMENT_PROCESSING | + EFFECTIVE_L2_QUEUE_SIZE(7)); + WREG32(VM_L2_CNTL2, 0); + WREG32(VM_L2_CNTL3, BANK_SELECT(0) | CACHE_UPDATE_MODE(2)); + /* Setup TLB control */ + tmp = EFFECTIVE_L1_TLB_SIZE(5) | EFFECTIVE_L1_QUEUE_SIZE(5); + WREG32(MC_VM_MD_L1_TLB0_CNTL, tmp); + WREG32(MC_VM_MD_L1_TLB1_CNTL, tmp); + WREG32(MC_VM_MD_L1_TLB2_CNTL, tmp); + WREG32(MC_VM_MB_L1_TLB0_CNTL, tmp); + WREG32(MC_VM_MB_L1_TLB1_CNTL, tmp); + WREG32(MC_VM_MB_L1_TLB2_CNTL, tmp); + WREG32(MC_VM_MB_L1_TLB3_CNTL, tmp); + radeon_gart_table_vram_unpin(rdev); +} + +static void rv770_pcie_gart_fini(struct radeon_device *rdev) +{ + radeon_gart_fini(rdev); + rv770_pcie_gart_disable(rdev); + radeon_gart_table_vram_free(rdev); +} + + +static void rv770_agp_enable(struct radeon_device *rdev) +{ + u32 tmp; + int i; + + /* Setup L2 cache */ + WREG32(VM_L2_CNTL, ENABLE_L2_CACHE | ENABLE_L2_FRAGMENT_PROCESSING | + ENABLE_L2_PTE_CACHE_LRU_UPDATE_BY_WRITE | + EFFECTIVE_L2_QUEUE_SIZE(7)); + WREG32(VM_L2_CNTL2, 0); + WREG32(VM_L2_CNTL3, BANK_SELECT(0) | CACHE_UPDATE_MODE(2)); + /* Setup TLB control */ + tmp = ENABLE_L1_TLB | ENABLE_L1_FRAGMENT_PROCESSING | + SYSTEM_ACCESS_MODE_NOT_IN_SYS | + SYSTEM_APERTURE_UNMAPPED_ACCESS_PASS_THRU | + EFFECTIVE_L1_TLB_SIZE(5) | EFFECTIVE_L1_QUEUE_SIZE(5); + WREG32(MC_VM_MD_L1_TLB0_CNTL, tmp); + WREG32(MC_VM_MD_L1_TLB1_CNTL, tmp); + WREG32(MC_VM_MD_L1_TLB2_CNTL, tmp); + WREG32(MC_VM_MB_L1_TLB0_CNTL, tmp); + WREG32(MC_VM_MB_L1_TLB1_CNTL, tmp); + WREG32(MC_VM_MB_L1_TLB2_CNTL, tmp); + WREG32(MC_VM_MB_L1_TLB3_CNTL, tmp); + for (i = 0; i < 7; i++) + WREG32(VM_CONTEXT0_CNTL + (i * 4), 0); +} + +static void rv770_mc_program(struct radeon_device *rdev) +{ + struct rv515_mc_save save; + u32 tmp; + int i, j; + + /* Initialize HDP */ + for (i = 0, j = 0; i < 32; i++, j += 0x18) { + WREG32((0x2c14 + j), 0x00000000); + WREG32((0x2c18 + j), 0x00000000); + WREG32((0x2c1c + j), 0x00000000); + WREG32((0x2c20 + j), 0x00000000); + WREG32((0x2c24 + j), 0x00000000); + } + /* r7xx hw bug. Read from HDP_DEBUG1 rather + * than writing to HDP_REG_COHERENCY_FLUSH_CNTL + */ + tmp = RREG32(HDP_DEBUG1); + + rv515_mc_stop(rdev, &save); + if (r600_mc_wait_for_idle(rdev)) { + dev_warn(rdev->dev, "Wait for MC idle timedout !\n"); + } + /* Lockout access through VGA aperture*/ + WREG32(VGA_HDP_CONTROL, VGA_MEMORY_DISABLE); + /* Update configuration */ + if (rdev->flags & RADEON_IS_AGP) { + if (rdev->mc.vram_start < rdev->mc.gtt_start) { + /* VRAM before AGP */ + WREG32(MC_VM_SYSTEM_APERTURE_LOW_ADDR, + rdev->mc.vram_start >> 12); + WREG32(MC_VM_SYSTEM_APERTURE_HIGH_ADDR, + rdev->mc.gtt_end >> 12); + } else { + /* VRAM after AGP */ + WREG32(MC_VM_SYSTEM_APERTURE_LOW_ADDR, + rdev->mc.gtt_start >> 12); + WREG32(MC_VM_SYSTEM_APERTURE_HIGH_ADDR, + rdev->mc.vram_end >> 12); + } + } else { + WREG32(MC_VM_SYSTEM_APERTURE_LOW_ADDR, + rdev->mc.vram_start >> 12); + WREG32(MC_VM_SYSTEM_APERTURE_HIGH_ADDR, + rdev->mc.vram_end >> 12); + } + WREG32(MC_VM_SYSTEM_APERTURE_DEFAULT_ADDR, rdev->vram_scratch.gpu_addr >> 12); + tmp = ((rdev->mc.vram_end >> 24) & 0xFFFF) << 16; + tmp |= ((rdev->mc.vram_start >> 24) & 0xFFFF); + WREG32(MC_VM_FB_LOCATION, tmp); + WREG32(HDP_NONSURFACE_BASE, (rdev->mc.vram_start >> 8)); + WREG32(HDP_NONSURFACE_INFO, (2 << 7)); + WREG32(HDP_NONSURFACE_SIZE, 0x3FFFFFFF); + if (rdev->flags & RADEON_IS_AGP) { + WREG32(MC_VM_AGP_TOP, rdev->mc.gtt_end >> 16); + WREG32(MC_VM_AGP_BOT, rdev->mc.gtt_start >> 16); + WREG32(MC_VM_AGP_BASE, rdev->mc.agp_base >> 22); + } else { + WREG32(MC_VM_AGP_BASE, 0); + WREG32(MC_VM_AGP_TOP, 0x0FFFFFFF); + WREG32(MC_VM_AGP_BOT, 0x0FFFFFFF); + } + if (r600_mc_wait_for_idle(rdev)) { + dev_warn(rdev->dev, "Wait for MC idle timedout !\n"); + } + rv515_mc_resume(rdev, &save); + /* we need to own VRAM, so turn off the VGA renderer here + * to stop it overwriting our objects */ + rv515_vga_render_disable(rdev); +} + + +/* + * CP. + */ +void r700_cp_stop(struct radeon_device *rdev) +{ + radeon_ttm_set_active_vram_size(rdev, rdev->mc.visible_vram_size); + WREG32(CP_ME_CNTL, (CP_ME_HALT | CP_PFP_HALT)); + WREG32(SCRATCH_UMSK, 0); + rdev->ring[RADEON_RING_TYPE_GFX_INDEX].ready = false; +} + +static int rv770_cp_load_microcode(struct radeon_device *rdev) +{ + const __be32 *fw_data; + int i; + + if (!rdev->me_fw || !rdev->pfp_fw) + return -EINVAL; + + r700_cp_stop(rdev); + WREG32(CP_RB_CNTL, +#ifdef __BIG_ENDIAN + BUF_SWAP_32BIT | +#endif + RB_NO_UPDATE | RB_BLKSZ(15) | RB_BUFSZ(3)); + + /* Reset cp */ + WREG32(GRBM_SOFT_RESET, SOFT_RESET_CP); + RREG32(GRBM_SOFT_RESET); + DRM_MDELAY(15); + WREG32(GRBM_SOFT_RESET, 0); + + fw_data = (const __be32 *)rdev->pfp_fw->data; + WREG32(CP_PFP_UCODE_ADDR, 0); + for (i = 0; i < R700_PFP_UCODE_SIZE; i++) + WREG32(CP_PFP_UCODE_DATA, be32_to_cpup(fw_data++)); + WREG32(CP_PFP_UCODE_ADDR, 0); + + fw_data = (const __be32 *)rdev->me_fw->data; + WREG32(CP_ME_RAM_WADDR, 0); + for (i = 0; i < R700_PM4_UCODE_SIZE; i++) + WREG32(CP_ME_RAM_DATA, be32_to_cpup(fw_data++)); + + WREG32(CP_PFP_UCODE_ADDR, 0); + WREG32(CP_ME_RAM_WADDR, 0); + WREG32(CP_ME_RAM_RADDR, 0); + return 0; +} + +void r700_cp_fini(struct radeon_device *rdev) +{ + struct radeon_ring *ring = &rdev->ring[RADEON_RING_TYPE_GFX_INDEX]; + r700_cp_stop(rdev); + radeon_ring_fini(rdev, ring); + radeon_scratch_free(rdev, ring->rptr_save_reg); +} + +/* + * Core functions + */ +static void rv770_gpu_init(struct radeon_device *rdev) +{ + int i, j, num_qd_pipes; + u32 ta_aux_cntl; + u32 sx_debug_1; + u32 smx_dc_ctl0; + u32 db_debug3; + u32 num_gs_verts_per_thread; + u32 vgt_gs_per_es; + u32 gs_prim_buffer_depth = 0; + u32 sq_ms_fifo_sizes; + u32 sq_config; + u32 sq_thread_resource_mgmt; + u32 hdp_host_path_cntl; + u32 sq_dyn_gpr_size_simd_ab_0; + u32 gb_tiling_config = 0; + u32 cc_rb_backend_disable = 0; + u32 cc_gc_shader_pipe_config = 0; + u32 mc_arb_ramcfg; + u32 db_debug4, tmp; + u32 inactive_pipes, shader_pipe_config; + u32 disabled_rb_mask; + unsigned active_number; + + /* setup chip specs */ + rdev->config.rv770.tiling_group_size = 256; + switch (rdev->family) { + case CHIP_RV770: + rdev->config.rv770.max_pipes = 4; + rdev->config.rv770.max_tile_pipes = 8; + rdev->config.rv770.max_simds = 10; + rdev->config.rv770.max_backends = 4; + rdev->config.rv770.max_gprs = 256; + rdev->config.rv770.max_threads = 248; + rdev->config.rv770.max_stack_entries = 512; + rdev->config.rv770.max_hw_contexts = 8; + rdev->config.rv770.max_gs_threads = 16 * 2; + rdev->config.rv770.sx_max_export_size = 128; + rdev->config.rv770.sx_max_export_pos_size = 16; + rdev->config.rv770.sx_max_export_smx_size = 112; + rdev->config.rv770.sq_num_cf_insts = 2; + + rdev->config.rv770.sx_num_of_sets = 7; + rdev->config.rv770.sc_prim_fifo_size = 0xF9; + rdev->config.rv770.sc_hiz_tile_fifo_size = 0x30; + rdev->config.rv770.sc_earlyz_tile_fifo_fize = 0x130; + break; + case CHIP_RV730: + rdev->config.rv770.max_pipes = 2; + rdev->config.rv770.max_tile_pipes = 4; + rdev->config.rv770.max_simds = 8; + rdev->config.rv770.max_backends = 2; + rdev->config.rv770.max_gprs = 128; + rdev->config.rv770.max_threads = 248; + rdev->config.rv770.max_stack_entries = 256; + rdev->config.rv770.max_hw_contexts = 8; + rdev->config.rv770.max_gs_threads = 16 * 2; + rdev->config.rv770.sx_max_export_size = 256; + rdev->config.rv770.sx_max_export_pos_size = 32; + rdev->config.rv770.sx_max_export_smx_size = 224; + rdev->config.rv770.sq_num_cf_insts = 2; + + rdev->config.rv770.sx_num_of_sets = 7; + rdev->config.rv770.sc_prim_fifo_size = 0xf9; + rdev->config.rv770.sc_hiz_tile_fifo_size = 0x30; + rdev->config.rv770.sc_earlyz_tile_fifo_fize = 0x130; + if (rdev->config.rv770.sx_max_export_pos_size > 16) { + rdev->config.rv770.sx_max_export_pos_size -= 16; + rdev->config.rv770.sx_max_export_smx_size += 16; + } + break; + case CHIP_RV710: + rdev->config.rv770.max_pipes = 2; + rdev->config.rv770.max_tile_pipes = 2; + rdev->config.rv770.max_simds = 2; + rdev->config.rv770.max_backends = 1; + rdev->config.rv770.max_gprs = 256; + rdev->config.rv770.max_threads = 192; + rdev->config.rv770.max_stack_entries = 256; + rdev->config.rv770.max_hw_contexts = 4; + rdev->config.rv770.max_gs_threads = 8 * 2; + rdev->config.rv770.sx_max_export_size = 128; + rdev->config.rv770.sx_max_export_pos_size = 16; + rdev->config.rv770.sx_max_export_smx_size = 112; + rdev->config.rv770.sq_num_cf_insts = 1; + + rdev->config.rv770.sx_num_of_sets = 7; + rdev->config.rv770.sc_prim_fifo_size = 0x40; + rdev->config.rv770.sc_hiz_tile_fifo_size = 0x30; + rdev->config.rv770.sc_earlyz_tile_fifo_fize = 0x130; + break; + case CHIP_RV740: + rdev->config.rv770.max_pipes = 4; + rdev->config.rv770.max_tile_pipes = 4; + rdev->config.rv770.max_simds = 8; + rdev->config.rv770.max_backends = 4; + rdev->config.rv770.max_gprs = 256; + rdev->config.rv770.max_threads = 248; + rdev->config.rv770.max_stack_entries = 512; + rdev->config.rv770.max_hw_contexts = 8; + rdev->config.rv770.max_gs_threads = 16 * 2; + rdev->config.rv770.sx_max_export_size = 256; + rdev->config.rv770.sx_max_export_pos_size = 32; + rdev->config.rv770.sx_max_export_smx_size = 224; + rdev->config.rv770.sq_num_cf_insts = 2; + + rdev->config.rv770.sx_num_of_sets = 7; + rdev->config.rv770.sc_prim_fifo_size = 0x100; + rdev->config.rv770.sc_hiz_tile_fifo_size = 0x30; + rdev->config.rv770.sc_earlyz_tile_fifo_fize = 0x130; + + if (rdev->config.rv770.sx_max_export_pos_size > 16) { + rdev->config.rv770.sx_max_export_pos_size -= 16; + rdev->config.rv770.sx_max_export_smx_size += 16; + } + break; + default: + break; + } + + /* Initialize HDP */ + j = 0; + for (i = 0; i < 32; i++) { + WREG32((0x2c14 + j), 0x00000000); + WREG32((0x2c18 + j), 0x00000000); + WREG32((0x2c1c + j), 0x00000000); + WREG32((0x2c20 + j), 0x00000000); + WREG32((0x2c24 + j), 0x00000000); + j += 0x18; + } + + WREG32(GRBM_CNTL, GRBM_READ_TIMEOUT(0xff)); + + /* setup tiling, simd, pipe config */ + mc_arb_ramcfg = RREG32(MC_ARB_RAMCFG); + + shader_pipe_config = RREG32(CC_GC_SHADER_PIPE_CONFIG); + inactive_pipes = (shader_pipe_config & INACTIVE_QD_PIPES_MASK) >> INACTIVE_QD_PIPES_SHIFT; + for (i = 0, tmp = 1, active_number = 0; i < R7XX_MAX_PIPES; i++) { + if (!(inactive_pipes & tmp)) { + active_number++; + } + tmp <<= 1; + } + if (active_number == 1) { + WREG32(SPI_CONFIG_CNTL, DISABLE_INTERP_1); + } else { + WREG32(SPI_CONFIG_CNTL, 0); + } + + cc_rb_backend_disable = RREG32(CC_RB_BACKEND_DISABLE) & 0x00ff0000; + tmp = R7XX_MAX_BACKENDS - r600_count_pipe_bits(cc_rb_backend_disable >> 16); + if (tmp < rdev->config.rv770.max_backends) { + rdev->config.rv770.max_backends = tmp; + } + + cc_gc_shader_pipe_config = RREG32(CC_GC_SHADER_PIPE_CONFIG) & 0xffffff00; + tmp = R7XX_MAX_PIPES - r600_count_pipe_bits((cc_gc_shader_pipe_config >> 8) & R7XX_MAX_PIPES_MASK); + if (tmp < rdev->config.rv770.max_pipes) { + rdev->config.rv770.max_pipes = tmp; + } + tmp = R7XX_MAX_SIMDS - r600_count_pipe_bits((cc_gc_shader_pipe_config >> 16) & R7XX_MAX_SIMDS_MASK); + if (tmp < rdev->config.rv770.max_simds) { + rdev->config.rv770.max_simds = tmp; + } + + switch (rdev->config.rv770.max_tile_pipes) { + case 1: + default: + gb_tiling_config = PIPE_TILING(0); + break; + case 2: + gb_tiling_config = PIPE_TILING(1); + break; + case 4: + gb_tiling_config = PIPE_TILING(2); + break; + case 8: + gb_tiling_config = PIPE_TILING(3); + break; + } + rdev->config.rv770.tiling_npipes = rdev->config.rv770.max_tile_pipes; + + disabled_rb_mask = (RREG32(CC_RB_BACKEND_DISABLE) >> 16) & R7XX_MAX_BACKENDS_MASK; + tmp = (gb_tiling_config & PIPE_TILING__MASK) >> PIPE_TILING__SHIFT; + tmp = r6xx_remap_render_backend(rdev, tmp, rdev->config.rv770.max_backends, + R7XX_MAX_BACKENDS, disabled_rb_mask); + gb_tiling_config |= tmp << 16; + rdev->config.rv770.backend_map = tmp; + + if (rdev->family == CHIP_RV770) + gb_tiling_config |= BANK_TILING(1); + else { + if ((mc_arb_ramcfg & NOOFBANK_MASK) >> NOOFBANK_SHIFT) + gb_tiling_config |= BANK_TILING(1); + else + gb_tiling_config |= BANK_TILING(0); + } + rdev->config.rv770.tiling_nbanks = 4 << ((gb_tiling_config >> 4) & 0x3); + gb_tiling_config |= GROUP_SIZE((mc_arb_ramcfg & BURSTLENGTH_MASK) >> BURSTLENGTH_SHIFT); + if (((mc_arb_ramcfg & NOOFROWS_MASK) >> NOOFROWS_SHIFT) > 3) { + gb_tiling_config |= ROW_TILING(3); + gb_tiling_config |= SAMPLE_SPLIT(3); + } else { + gb_tiling_config |= + ROW_TILING(((mc_arb_ramcfg & NOOFROWS_MASK) >> NOOFROWS_SHIFT)); + gb_tiling_config |= + SAMPLE_SPLIT(((mc_arb_ramcfg & NOOFROWS_MASK) >> NOOFROWS_SHIFT)); + } + + gb_tiling_config |= BANK_SWAPS(1); + rdev->config.rv770.tile_config = gb_tiling_config; + + WREG32(GB_TILING_CONFIG, gb_tiling_config); + WREG32(DCP_TILING_CONFIG, (gb_tiling_config & 0xffff)); + WREG32(HDP_TILING_CONFIG, (gb_tiling_config & 0xffff)); + WREG32(DMA_TILING_CONFIG, (gb_tiling_config & 0xffff)); + WREG32(DMA_TILING_CONFIG2, (gb_tiling_config & 0xffff)); + + WREG32(CGTS_SYS_TCC_DISABLE, 0); + WREG32(CGTS_TCC_DISABLE, 0); + WREG32(CGTS_USER_SYS_TCC_DISABLE, 0); + WREG32(CGTS_USER_TCC_DISABLE, 0); + + + num_qd_pipes = R7XX_MAX_PIPES - r600_count_pipe_bits((cc_gc_shader_pipe_config & INACTIVE_QD_PIPES_MASK) >> 8); + WREG32(VGT_OUT_DEALLOC_CNTL, (num_qd_pipes * 4) & DEALLOC_DIST_MASK); + WREG32(VGT_VERTEX_REUSE_BLOCK_CNTL, ((num_qd_pipes * 4) - 2) & VTX_REUSE_DEPTH_MASK); + + /* set HW defaults for 3D engine */ + WREG32(CP_QUEUE_THRESHOLDS, (ROQ_IB1_START(0x16) | + ROQ_IB2_START(0x2b))); + + WREG32(CP_MEQ_THRESHOLDS, STQ_SPLIT(0x30)); + + ta_aux_cntl = RREG32(TA_CNTL_AUX); + WREG32(TA_CNTL_AUX, ta_aux_cntl | DISABLE_CUBE_ANISO); + + sx_debug_1 = RREG32(SX_DEBUG_1); + sx_debug_1 |= ENABLE_NEW_SMX_ADDRESS; + WREG32(SX_DEBUG_1, sx_debug_1); + + smx_dc_ctl0 = RREG32(SMX_DC_CTL0); + smx_dc_ctl0 &= ~CACHE_DEPTH(0x1ff); + smx_dc_ctl0 |= CACHE_DEPTH((rdev->config.rv770.sx_num_of_sets * 64) - 1); + WREG32(SMX_DC_CTL0, smx_dc_ctl0); + + if (rdev->family != CHIP_RV740) + WREG32(SMX_EVENT_CTL, (ES_FLUSH_CTL(4) | + GS_FLUSH_CTL(4) | + ACK_FLUSH_CTL(3) | + SYNC_FLUSH_CTL)); + + if (rdev->family != CHIP_RV770) + WREG32(SMX_SAR_CTL0, 0x00003f3f); + + db_debug3 = RREG32(DB_DEBUG3); + db_debug3 &= ~DB_CLK_OFF_DELAY(0x1f); + switch (rdev->family) { + case CHIP_RV770: + case CHIP_RV740: + db_debug3 |= DB_CLK_OFF_DELAY(0x1f); + break; + case CHIP_RV710: + case CHIP_RV730: + default: + db_debug3 |= DB_CLK_OFF_DELAY(2); + break; + } + WREG32(DB_DEBUG3, db_debug3); + + if (rdev->family != CHIP_RV770) { + db_debug4 = RREG32(DB_DEBUG4); + db_debug4 |= DISABLE_TILE_COVERED_FOR_PS_ITER; + WREG32(DB_DEBUG4, db_debug4); + } + + WREG32(SX_EXPORT_BUFFER_SIZES, (COLOR_BUFFER_SIZE((rdev->config.rv770.sx_max_export_size / 4) - 1) | + POSITION_BUFFER_SIZE((rdev->config.rv770.sx_max_export_pos_size / 4) - 1) | + SMX_BUFFER_SIZE((rdev->config.rv770.sx_max_export_smx_size / 4) - 1))); + + WREG32(PA_SC_FIFO_SIZE, (SC_PRIM_FIFO_SIZE(rdev->config.rv770.sc_prim_fifo_size) | + SC_HIZ_TILE_FIFO_SIZE(rdev->config.rv770.sc_hiz_tile_fifo_size) | + SC_EARLYZ_TILE_FIFO_SIZE(rdev->config.rv770.sc_earlyz_tile_fifo_fize))); + + WREG32(PA_SC_MULTI_CHIP_CNTL, 0); + + WREG32(VGT_NUM_INSTANCES, 1); + + WREG32(SPI_CONFIG_CNTL_1, VTX_DONE_DELAY(4)); + + WREG32(CP_PERFMON_CNTL, 0); + + sq_ms_fifo_sizes = (CACHE_FIFO_SIZE(16 * rdev->config.rv770.sq_num_cf_insts) | + DONE_FIFO_HIWATER(0xe0) | + ALU_UPDATE_FIFO_HIWATER(0x8)); + switch (rdev->family) { + case CHIP_RV770: + case CHIP_RV730: + case CHIP_RV710: + sq_ms_fifo_sizes |= FETCH_FIFO_HIWATER(0x1); + break; + case CHIP_RV740: + default: + sq_ms_fifo_sizes |= FETCH_FIFO_HIWATER(0x4); + break; + } + WREG32(SQ_MS_FIFO_SIZES, sq_ms_fifo_sizes); + + /* SQ_CONFIG, SQ_GPR_RESOURCE_MGMT, SQ_THREAD_RESOURCE_MGMT, SQ_STACK_RESOURCE_MGMT + * should be adjusted as needed by the 2D/3D drivers. This just sets default values + */ + sq_config = RREG32(SQ_CONFIG); + sq_config &= ~(PS_PRIO(3) | + VS_PRIO(3) | + GS_PRIO(3) | + ES_PRIO(3)); + sq_config |= (DX9_CONSTS | + VC_ENABLE | + EXPORT_SRC_C | + PS_PRIO(0) | + VS_PRIO(1) | + GS_PRIO(2) | + ES_PRIO(3)); + if (rdev->family == CHIP_RV710) + /* no vertex cache */ + sq_config &= ~VC_ENABLE; + + WREG32(SQ_CONFIG, sq_config); + + WREG32(SQ_GPR_RESOURCE_MGMT_1, (NUM_PS_GPRS((rdev->config.rv770.max_gprs * 24)/64) | + NUM_VS_GPRS((rdev->config.rv770.max_gprs * 24)/64) | + NUM_CLAUSE_TEMP_GPRS(((rdev->config.rv770.max_gprs * 24)/64)/2))); + + WREG32(SQ_GPR_RESOURCE_MGMT_2, (NUM_GS_GPRS((rdev->config.rv770.max_gprs * 7)/64) | + NUM_ES_GPRS((rdev->config.rv770.max_gprs * 7)/64))); + + sq_thread_resource_mgmt = (NUM_PS_THREADS((rdev->config.rv770.max_threads * 4)/8) | + NUM_VS_THREADS((rdev->config.rv770.max_threads * 2)/8) | + NUM_ES_THREADS((rdev->config.rv770.max_threads * 1)/8)); + if (((rdev->config.rv770.max_threads * 1) / 8) > rdev->config.rv770.max_gs_threads) + sq_thread_resource_mgmt |= NUM_GS_THREADS(rdev->config.rv770.max_gs_threads); + else + sq_thread_resource_mgmt |= NUM_GS_THREADS((rdev->config.rv770.max_gs_threads * 1)/8); + WREG32(SQ_THREAD_RESOURCE_MGMT, sq_thread_resource_mgmt); + + WREG32(SQ_STACK_RESOURCE_MGMT_1, (NUM_PS_STACK_ENTRIES((rdev->config.rv770.max_stack_entries * 1)/4) | + NUM_VS_STACK_ENTRIES((rdev->config.rv770.max_stack_entries * 1)/4))); + + WREG32(SQ_STACK_RESOURCE_MGMT_2, (NUM_GS_STACK_ENTRIES((rdev->config.rv770.max_stack_entries * 1)/4) | + NUM_ES_STACK_ENTRIES((rdev->config.rv770.max_stack_entries * 1)/4))); + + sq_dyn_gpr_size_simd_ab_0 = (SIMDA_RING0((rdev->config.rv770.max_gprs * 38)/64) | + SIMDA_RING1((rdev->config.rv770.max_gprs * 38)/64) | + SIMDB_RING0((rdev->config.rv770.max_gprs * 38)/64) | + SIMDB_RING1((rdev->config.rv770.max_gprs * 38)/64)); + + WREG32(SQ_DYN_GPR_SIZE_SIMD_AB_0, sq_dyn_gpr_size_simd_ab_0); + WREG32(SQ_DYN_GPR_SIZE_SIMD_AB_1, sq_dyn_gpr_size_simd_ab_0); + WREG32(SQ_DYN_GPR_SIZE_SIMD_AB_2, sq_dyn_gpr_size_simd_ab_0); + WREG32(SQ_DYN_GPR_SIZE_SIMD_AB_3, sq_dyn_gpr_size_simd_ab_0); + WREG32(SQ_DYN_GPR_SIZE_SIMD_AB_4, sq_dyn_gpr_size_simd_ab_0); + WREG32(SQ_DYN_GPR_SIZE_SIMD_AB_5, sq_dyn_gpr_size_simd_ab_0); + WREG32(SQ_DYN_GPR_SIZE_SIMD_AB_6, sq_dyn_gpr_size_simd_ab_0); + WREG32(SQ_DYN_GPR_SIZE_SIMD_AB_7, sq_dyn_gpr_size_simd_ab_0); + + WREG32(PA_SC_FORCE_EOV_MAX_CNTS, (FORCE_EOV_MAX_CLK_CNT(4095) | + FORCE_EOV_MAX_REZ_CNT(255))); + + if (rdev->family == CHIP_RV710) + WREG32(VGT_CACHE_INVALIDATION, (CACHE_INVALIDATION(TC_ONLY) | + AUTO_INVLD_EN(ES_AND_GS_AUTO))); + else + WREG32(VGT_CACHE_INVALIDATION, (CACHE_INVALIDATION(VC_AND_TC) | + AUTO_INVLD_EN(ES_AND_GS_AUTO))); + + switch (rdev->family) { + case CHIP_RV770: + case CHIP_RV730: + case CHIP_RV740: + gs_prim_buffer_depth = 384; + break; + case CHIP_RV710: + gs_prim_buffer_depth = 128; + break; + default: + break; + } + + num_gs_verts_per_thread = rdev->config.rv770.max_pipes * 16; + vgt_gs_per_es = gs_prim_buffer_depth + num_gs_verts_per_thread; + /* Max value for this is 256 */ + if (vgt_gs_per_es > 256) + vgt_gs_per_es = 256; + + WREG32(VGT_ES_PER_GS, 128); + WREG32(VGT_GS_PER_ES, vgt_gs_per_es); + WREG32(VGT_GS_PER_VS, 2); + + /* more default values. 2D/3D driver should adjust as needed */ + WREG32(VGT_GS_VERTEX_REUSE, 16); + WREG32(PA_SC_LINE_STIPPLE_STATE, 0); + WREG32(VGT_STRMOUT_EN, 0); + WREG32(SX_MISC, 0); + WREG32(PA_SC_MODE_CNTL, 0); + WREG32(PA_SC_EDGERULE, 0xaaaaaaaa); + WREG32(PA_SC_AA_CONFIG, 0); + WREG32(PA_SC_CLIPRECT_RULE, 0xffff); + WREG32(PA_SC_LINE_STIPPLE, 0); + WREG32(SPI_INPUT_Z, 0); + WREG32(SPI_PS_IN_CONTROL_0, NUM_INTERP(2)); + WREG32(CB_COLOR7_FRAG, 0); + + /* clear render buffer base addresses */ + WREG32(CB_COLOR0_BASE, 0); + WREG32(CB_COLOR1_BASE, 0); + WREG32(CB_COLOR2_BASE, 0); + WREG32(CB_COLOR3_BASE, 0); + WREG32(CB_COLOR4_BASE, 0); + WREG32(CB_COLOR5_BASE, 0); + WREG32(CB_COLOR6_BASE, 0); + WREG32(CB_COLOR7_BASE, 0); + + WREG32(TCP_CNTL, 0); + + hdp_host_path_cntl = RREG32(HDP_HOST_PATH_CNTL); + WREG32(HDP_HOST_PATH_CNTL, hdp_host_path_cntl); + + WREG32(PA_SC_MULTI_CHIP_CNTL, 0); + + WREG32(PA_CL_ENHANCE, (CLIP_VTX_REORDER_ENA | + NUM_CLIP_SEQ(3))); + WREG32(VC_ENHANCE, 0); +} + +void r700_vram_gtt_location(struct radeon_device *rdev, struct radeon_mc *mc) +{ + u64 size_bf, size_af; + + if (mc->mc_vram_size > 0xE0000000) { + /* leave room for at least 512M GTT */ + dev_warn(rdev->dev, "limiting VRAM\n"); + mc->real_vram_size = 0xE0000000; + mc->mc_vram_size = 0xE0000000; + } + if (rdev->flags & RADEON_IS_AGP) { + size_bf = mc->gtt_start; + size_af = 0xFFFFFFFF - mc->gtt_end; + if (size_bf > size_af) { + if (mc->mc_vram_size > size_bf) { + dev_warn(rdev->dev, "limiting VRAM\n"); + mc->real_vram_size = size_bf; + mc->mc_vram_size = size_bf; + } + mc->vram_start = mc->gtt_start - mc->mc_vram_size; + } else { + if (mc->mc_vram_size > size_af) { + dev_warn(rdev->dev, "limiting VRAM\n"); + mc->real_vram_size = size_af; + mc->mc_vram_size = size_af; + } + mc->vram_start = mc->gtt_end + 1; + } + mc->vram_end = mc->vram_start + mc->mc_vram_size - 1; + dev_info(rdev->dev, "VRAM: %juM 0x%08jX - 0x%08jX (%juM used)\n", + (uintmax_t)mc->mc_vram_size >> 20, (uintmax_t)mc->vram_start, + (uintmax_t)mc->vram_end, (uintmax_t)mc->real_vram_size >> 20); + } else { + radeon_vram_location(rdev, &rdev->mc, 0); + rdev->mc.gtt_base_align = 0; + radeon_gtt_location(rdev, mc); + } +} + +static int rv770_mc_init(struct radeon_device *rdev) +{ + u32 tmp; + int chansize, numchan; + + /* Get VRAM informations */ + rdev->mc.vram_is_ddr = true; + tmp = RREG32(MC_ARB_RAMCFG); + if (tmp & CHANSIZE_OVERRIDE) { + chansize = 16; + } else if (tmp & CHANSIZE_MASK) { + chansize = 64; + } else { + chansize = 32; + } + tmp = RREG32(MC_SHARED_CHMAP); + switch ((tmp & NOOFCHAN_MASK) >> NOOFCHAN_SHIFT) { + case 0: + default: + numchan = 1; + break; + case 1: + numchan = 2; + break; + case 2: + numchan = 4; + break; + case 3: + numchan = 8; + break; + } + rdev->mc.vram_width = numchan * chansize; + /* Could aper size report 0 ? */ + rdev->mc.aper_base = drm_get_resource_start(rdev->ddev, 0); + rdev->mc.aper_size = drm_get_resource_len(rdev->ddev, 0); + /* Setup GPU memory space */ + rdev->mc.mc_vram_size = RREG32(CONFIG_MEMSIZE); + rdev->mc.real_vram_size = RREG32(CONFIG_MEMSIZE); + rdev->mc.visible_vram_size = rdev->mc.aper_size; + r700_vram_gtt_location(rdev, &rdev->mc); + radeon_update_bandwidth_info(rdev); + + return 0; +} + +/** + * rv770_copy_dma - copy pages using the DMA engine + * + * @rdev: radeon_device pointer + * @src_offset: src GPU address + * @dst_offset: dst GPU address + * @num_gpu_pages: number of GPU pages to xfer + * @fence: radeon fence object + * + * Copy GPU paging using the DMA engine (r7xx). + * Used by the radeon ttm implementation to move pages if + * registered as the asic copy callback. + */ +int rv770_copy_dma(struct radeon_device *rdev, + uint64_t src_offset, uint64_t dst_offset, + unsigned num_gpu_pages, + struct radeon_fence **fence) +{ + struct radeon_semaphore *sem = NULL; + int ring_index = rdev->asic->copy.dma_ring_index; + struct radeon_ring *ring = &rdev->ring[ring_index]; + u32 size_in_dw, cur_size_in_dw; + int i, num_loops; + int r = 0; + + r = radeon_semaphore_create(rdev, &sem); + if (r) { + DRM_ERROR("radeon: moving bo (%d).\n", r); + return r; + } + + size_in_dw = (num_gpu_pages << RADEON_GPU_PAGE_SHIFT) / 4; + num_loops = DIV_ROUND_UP(size_in_dw, 0xFFFF); + r = radeon_ring_lock(rdev, ring, num_loops * 5 + 8); + if (r) { + DRM_ERROR("radeon: moving bo (%d).\n", r); + radeon_semaphore_free(rdev, &sem, NULL); + return r; + } + + if (radeon_fence_need_sync(*fence, ring->idx)) { + radeon_semaphore_sync_rings(rdev, sem, (*fence)->ring, + ring->idx); + radeon_fence_note_sync(*fence, ring->idx); + } else { + radeon_semaphore_free(rdev, &sem, NULL); + } + + for (i = 0; i < num_loops; i++) { + cur_size_in_dw = size_in_dw; + if (cur_size_in_dw > 0xFFFF) + cur_size_in_dw = 0xFFFF; + size_in_dw -= cur_size_in_dw; + radeon_ring_write(ring, DMA_PACKET(DMA_PACKET_COPY, 0, 0, cur_size_in_dw)); + radeon_ring_write(ring, dst_offset & 0xfffffffc); + radeon_ring_write(ring, src_offset & 0xfffffffc); + radeon_ring_write(ring, upper_32_bits(dst_offset) & 0xff); + radeon_ring_write(ring, upper_32_bits(src_offset) & 0xff); + src_offset += cur_size_in_dw * 4; + dst_offset += cur_size_in_dw * 4; + } + + r = radeon_fence_emit(rdev, fence, ring->idx); + if (r) { + radeon_ring_unlock_undo(rdev, ring); + return r; + } + + radeon_ring_unlock_commit(rdev, ring); + radeon_semaphore_free(rdev, &sem, *fence); + + return r; +} + +static int rv770_startup(struct radeon_device *rdev) +{ + struct radeon_ring *ring; + int r; + + /* enable pcie gen2 link */ + rv770_pcie_gen2_enable(rdev); + + if (!rdev->me_fw || !rdev->pfp_fw || !rdev->rlc_fw) { + r = r600_init_microcode(rdev); + if (r) { + DRM_ERROR("Failed to load firmware!\n"); + return r; + } + } + + r = r600_vram_scratch_init(rdev); + if (r) + return r; + + rv770_mc_program(rdev); + if (rdev->flags & RADEON_IS_AGP) { + rv770_agp_enable(rdev); + } else { + r = rv770_pcie_gart_enable(rdev); + if (r) + return r; + } + + rv770_gpu_init(rdev); + r = r600_blit_init(rdev); + if (r) { + r600_blit_fini(rdev); + rdev->asic->copy.copy = NULL; + dev_warn(rdev->dev, "failed blitter (%d) falling back to memcpy\n", r); + } + + /* allocate wb buffer */ + r = radeon_wb_init(rdev); + if (r) + return r; + + r = radeon_fence_driver_start_ring(rdev, RADEON_RING_TYPE_GFX_INDEX); + if (r) { + dev_err(rdev->dev, "failed initializing CP fences (%d).\n", r); + return r; + } + + r = radeon_fence_driver_start_ring(rdev, R600_RING_TYPE_DMA_INDEX); + if (r) { + dev_err(rdev->dev, "failed initializing DMA fences (%d).\n", r); + return r; + } + + /* Enable IRQ */ + r = r600_irq_init(rdev); + if (r) { + DRM_ERROR("radeon: IH init failed (%d).\n", r); + radeon_irq_kms_fini(rdev); + return r; + } + r600_irq_set(rdev); + + ring = &rdev->ring[RADEON_RING_TYPE_GFX_INDEX]; + r = radeon_ring_init(rdev, ring, ring->ring_size, RADEON_WB_CP_RPTR_OFFSET, + R600_CP_RB_RPTR, R600_CP_RB_WPTR, + 0, 0xfffff, RADEON_CP_PACKET2); + if (r) + return r; + + ring = &rdev->ring[R600_RING_TYPE_DMA_INDEX]; + r = radeon_ring_init(rdev, ring, ring->ring_size, R600_WB_DMA_RPTR_OFFSET, + DMA_RB_RPTR, DMA_RB_WPTR, + 2, 0x3fffc, DMA_PACKET(DMA_PACKET_NOP, 0, 0, 0)); + if (r) + return r; + + r = rv770_cp_load_microcode(rdev); + if (r) + return r; + r = r600_cp_resume(rdev); + if (r) + return r; + + r = r600_dma_resume(rdev); + if (r) + return r; + + r = radeon_ib_pool_init(rdev); + if (r) { + dev_err(rdev->dev, "IB initialization failed (%d).\n", r); + return r; + } + + r = r600_audio_init(rdev); + if (r) { + DRM_ERROR("radeon: audio init failed\n"); + return r; + } + + return 0; +} + +int rv770_resume(struct radeon_device *rdev) +{ + int r; + + /* Do not reset GPU before posting, on rv770 hw unlike on r500 hw, + * posting will perform necessary task to bring back GPU into good + * shape. + */ + /* post card */ + atom_asic_init(rdev->mode_info.atom_context); + + rdev->accel_working = true; + r = rv770_startup(rdev); + if (r) { + DRM_ERROR("r600 startup failed on resume\n"); + rdev->accel_working = false; + return r; + } + + return r; + +} + +int rv770_suspend(struct radeon_device *rdev) +{ + r600_audio_fini(rdev); + r700_cp_stop(rdev); + r600_dma_stop(rdev); + r600_irq_suspend(rdev); + radeon_wb_disable(rdev); + rv770_pcie_gart_disable(rdev); + + return 0; +} + +/* Plan is to move initialization in that function and use + * helper function so that radeon_device_init pretty much + * do nothing more than calling asic specific function. This + * should also allow to remove a bunch of callback function + * like vram_info. + */ +int rv770_init(struct radeon_device *rdev) +{ + int r; + + /* Read BIOS */ + if (!radeon_get_bios(rdev)) { + if (ASIC_IS_AVIVO(rdev)) + return -EINVAL; + } + /* Must be an ATOMBIOS */ + if (!rdev->is_atom_bios) { + dev_err(rdev->dev, "Expecting atombios for R600 GPU\n"); + return -EINVAL; + } + r = radeon_atombios_init(rdev); + if (r) + return r; + /* Post card if necessary */ + if (!radeon_card_posted(rdev)) { + if (!rdev->bios) { + dev_err(rdev->dev, "Card not posted and no BIOS - ignoring\n"); + return -EINVAL; + } + DRM_INFO("GPU not posted. posting now...\n"); + atom_asic_init(rdev->mode_info.atom_context); + } + /* Initialize scratch registers */ + r600_scratch_init(rdev); + /* Initialize surface registers */ + radeon_surface_init(rdev); + /* Initialize clocks */ + radeon_get_clock_info(rdev->ddev); + /* Fence driver */ + r = radeon_fence_driver_init(rdev); + if (r) + return r; + /* initialize AGP */ + if (rdev->flags & RADEON_IS_AGP) { + r = radeon_agp_init(rdev); + if (r) + radeon_agp_disable(rdev); + } + r = rv770_mc_init(rdev); + if (r) + return r; + /* Memory manager */ + r = radeon_bo_init(rdev); + if (r) + return r; + + r = radeon_irq_kms_init(rdev); + if (r) + return r; + + rdev->ring[RADEON_RING_TYPE_GFX_INDEX].ring_obj = NULL; + r600_ring_init(rdev, &rdev->ring[RADEON_RING_TYPE_GFX_INDEX], 1024 * 1024); + + rdev->ring[R600_RING_TYPE_DMA_INDEX].ring_obj = NULL; + r600_ring_init(rdev, &rdev->ring[R600_RING_TYPE_DMA_INDEX], 64 * 1024); + + rdev->ih.ring_obj = NULL; + r600_ih_ring_init(rdev, 64 * 1024); + + r = r600_pcie_gart_init(rdev); + if (r) + return r; + + rdev->accel_working = true; + r = rv770_startup(rdev); + if (r) { + dev_err(rdev->dev, "disabling GPU acceleration\n"); + r700_cp_fini(rdev); + r600_dma_fini(rdev); + r600_irq_fini(rdev); + radeon_wb_fini(rdev); + radeon_ib_pool_fini(rdev); + radeon_irq_kms_fini(rdev); + rv770_pcie_gart_fini(rdev); + rdev->accel_working = false; + } + + return 0; +} + +void rv770_fini(struct radeon_device *rdev) +{ + r600_blit_fini(rdev); + r700_cp_fini(rdev); + r600_dma_fini(rdev); + r600_irq_fini(rdev); + radeon_wb_fini(rdev); + radeon_ib_pool_fini(rdev); + radeon_irq_kms_fini(rdev); + rv770_pcie_gart_fini(rdev); + r600_vram_scratch_fini(rdev); + radeon_gem_fini(rdev); + radeon_fence_driver_fini(rdev); + radeon_agp_fini(rdev); + radeon_bo_fini(rdev); + radeon_atombios_fini(rdev); + r600_fini_microcode(rdev); + free(rdev->bios, DRM_MEM_DRIVER); + rdev->bios = NULL; +} + +static void rv770_pcie_gen2_enable(struct radeon_device *rdev) +{ + u32 link_width_cntl, lanes, speed_cntl, tmp; + u16 link_cntl2; + u32 mask; + int ret; + + if (radeon_pcie_gen2 == 0) + return; + + if (rdev->flags & RADEON_IS_IGP) + return; + + if (!(rdev->flags & RADEON_IS_PCIE)) + return; + + /* x2 cards have a special sequence */ + if (ASIC_IS_X2(rdev)) + return; + + ret = drm_pcie_get_speed_cap_mask(rdev->ddev, &mask); + if (ret != 0) + return; + + if (!(mask & DRM_PCIE_SPEED_50)) + return; + + DRM_INFO("enabling PCIE gen 2 link speeds, disable with radeon.pcie_gen2=0\n"); + + /* advertise upconfig capability */ + link_width_cntl = RREG32_PCIE_P(PCIE_LC_LINK_WIDTH_CNTL); + link_width_cntl &= ~LC_UPCONFIGURE_DIS; + WREG32_PCIE_P(PCIE_LC_LINK_WIDTH_CNTL, link_width_cntl); + link_width_cntl = RREG32_PCIE_P(PCIE_LC_LINK_WIDTH_CNTL); + if (link_width_cntl & LC_RENEGOTIATION_SUPPORT) { + lanes = (link_width_cntl & LC_LINK_WIDTH_RD_MASK) >> LC_LINK_WIDTH_RD_SHIFT; + link_width_cntl &= ~(LC_LINK_WIDTH_MASK | + LC_RECONFIG_ARC_MISSING_ESCAPE); + link_width_cntl |= lanes | LC_RECONFIG_NOW | + LC_RENEGOTIATE_EN | LC_UPCONFIGURE_SUPPORT; + WREG32_PCIE_P(PCIE_LC_LINK_WIDTH_CNTL, link_width_cntl); + } else { + link_width_cntl |= LC_UPCONFIGURE_DIS; + WREG32_PCIE_P(PCIE_LC_LINK_WIDTH_CNTL, link_width_cntl); + } + + speed_cntl = RREG32_PCIE_P(PCIE_LC_SPEED_CNTL); + if ((speed_cntl & LC_OTHER_SIDE_EVER_SENT_GEN2) && + (speed_cntl & LC_OTHER_SIDE_SUPPORTS_GEN2)) { + + tmp = RREG32(0x541c); + WREG32(0x541c, tmp | 0x8); + WREG32(MM_CFGREGS_CNTL, MM_WR_TO_CFG_EN); + link_cntl2 = RREG16(0x4088); + link_cntl2 &= ~TARGET_LINK_SPEED_MASK; + link_cntl2 |= 0x2; + WREG16(0x4088, link_cntl2); + WREG32(MM_CFGREGS_CNTL, 0); + + speed_cntl = RREG32_PCIE_P(PCIE_LC_SPEED_CNTL); + speed_cntl &= ~LC_TARGET_LINK_SPEED_OVERRIDE_EN; + WREG32_PCIE_P(PCIE_LC_SPEED_CNTL, speed_cntl); + + speed_cntl = RREG32_PCIE_P(PCIE_LC_SPEED_CNTL); + speed_cntl |= LC_CLR_FAILED_SPD_CHANGE_CNT; + WREG32_PCIE_P(PCIE_LC_SPEED_CNTL, speed_cntl); + + speed_cntl = RREG32_PCIE_P(PCIE_LC_SPEED_CNTL); + speed_cntl &= ~LC_CLR_FAILED_SPD_CHANGE_CNT; + WREG32_PCIE_P(PCIE_LC_SPEED_CNTL, speed_cntl); + + speed_cntl = RREG32_PCIE_P(PCIE_LC_SPEED_CNTL); + speed_cntl |= LC_GEN2_EN_STRAP; + WREG32_PCIE_P(PCIE_LC_SPEED_CNTL, speed_cntl); + + } else { + link_width_cntl = RREG32_PCIE_P(PCIE_LC_LINK_WIDTH_CNTL); + /* XXX: only disable it if gen1 bridge vendor == 0x111d or 0x1106 */ + if (1) + link_width_cntl |= LC_UPCONFIGURE_DIS; + else + link_width_cntl &= ~LC_UPCONFIGURE_DIS; + WREG32_PCIE_P(PCIE_LC_LINK_WIDTH_CNTL, link_width_cntl); + } +} diff --git a/sys/dev/drm2/radeon/rv770d.h b/sys/dev/drm2/radeon/rv770d.h new file mode 100644 index 00000000000..1f2c23c554f --- /dev/null +++ b/sys/dev/drm2/radeon/rv770d.h @@ -0,0 +1,673 @@ +/* + * Copyright 2009 Advanced Micro Devices, Inc. + * Copyright 2009 Red Hat Inc. + * + * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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. + * + * Authors: Dave Airlie + * Alex Deucher + * Jerome Glisse + */ +#ifndef RV770_H +#define RV770_H + +#include +__FBSDID("$FreeBSD$"); + +#define R7XX_MAX_SH_GPRS 256 +#define R7XX_MAX_TEMP_GPRS 16 +#define R7XX_MAX_SH_THREADS 256 +#define R7XX_MAX_SH_STACK_ENTRIES 4096 +#define R7XX_MAX_BACKENDS 8 +#define R7XX_MAX_BACKENDS_MASK 0xff +#define R7XX_MAX_SIMDS 16 +#define R7XX_MAX_SIMDS_MASK 0xffff +#define R7XX_MAX_PIPES 8 +#define R7XX_MAX_PIPES_MASK 0xff + +/* Registers */ +#define CB_COLOR0_BASE 0x28040 +#define CB_COLOR1_BASE 0x28044 +#define CB_COLOR2_BASE 0x28048 +#define CB_COLOR3_BASE 0x2804C +#define CB_COLOR4_BASE 0x28050 +#define CB_COLOR5_BASE 0x28054 +#define CB_COLOR6_BASE 0x28058 +#define CB_COLOR7_BASE 0x2805C +#define CB_COLOR7_FRAG 0x280FC + +#define CC_GC_SHADER_PIPE_CONFIG 0x8950 +#define CC_RB_BACKEND_DISABLE 0x98F4 +#define BACKEND_DISABLE(x) ((x) << 16) +#define CC_SYS_RB_BACKEND_DISABLE 0x3F88 + +#define CGTS_SYS_TCC_DISABLE 0x3F90 +#define CGTS_TCC_DISABLE 0x9148 +#define CGTS_USER_SYS_TCC_DISABLE 0x3F94 +#define CGTS_USER_TCC_DISABLE 0x914C + +#define CONFIG_MEMSIZE 0x5428 + +#define CP_ME_CNTL 0x86D8 +#define CP_ME_HALT (1<<28) +#define CP_PFP_HALT (1<<26) +#define CP_ME_RAM_DATA 0xC160 +#define CP_ME_RAM_RADDR 0xC158 +#define CP_ME_RAM_WADDR 0xC15C +#define CP_MEQ_THRESHOLDS 0x8764 +#define STQ_SPLIT(x) ((x) << 0) +#define CP_PERFMON_CNTL 0x87FC +#define CP_PFP_UCODE_ADDR 0xC150 +#define CP_PFP_UCODE_DATA 0xC154 +#define CP_QUEUE_THRESHOLDS 0x8760 +#define ROQ_IB1_START(x) ((x) << 0) +#define ROQ_IB2_START(x) ((x) << 8) +#define CP_RB_CNTL 0xC104 +#define RB_BUFSZ(x) ((x) << 0) +#define RB_BLKSZ(x) ((x) << 8) +#define RB_NO_UPDATE (1 << 27) +#define RB_RPTR_WR_ENA (1 << 31) +#define BUF_SWAP_32BIT (2 << 16) +#define CP_RB_RPTR 0x8700 +#define CP_RB_RPTR_ADDR 0xC10C +#define CP_RB_RPTR_ADDR_HI 0xC110 +#define CP_RB_RPTR_WR 0xC108 +#define CP_RB_WPTR 0xC114 +#define CP_RB_WPTR_ADDR 0xC118 +#define CP_RB_WPTR_ADDR_HI 0xC11C +#define CP_RB_WPTR_DELAY 0x8704 +#define CP_SEM_WAIT_TIMER 0x85BC + +#define DB_DEBUG3 0x98B0 +#define DB_CLK_OFF_DELAY(x) ((x) << 11) +#define DB_DEBUG4 0x9B8C +#define DISABLE_TILE_COVERED_FOR_PS_ITER (1 << 6) + +#define DCP_TILING_CONFIG 0x6CA0 +#define PIPE_TILING(x) ((x) << 1) +#define BANK_TILING(x) ((x) << 4) +#define GROUP_SIZE(x) ((x) << 6) +#define ROW_TILING(x) ((x) << 8) +#define BANK_SWAPS(x) ((x) << 11) +#define SAMPLE_SPLIT(x) ((x) << 14) +#define BACKEND_MAP(x) ((x) << 16) + +#define GB_TILING_CONFIG 0x98F0 +#define PIPE_TILING__SHIFT 1 +#define PIPE_TILING__MASK 0x0000000e + +#define DMA_TILING_CONFIG 0x3ec8 +#define DMA_TILING_CONFIG2 0xd0b8 + +#define GC_USER_SHADER_PIPE_CONFIG 0x8954 +#define INACTIVE_QD_PIPES(x) ((x) << 8) +#define INACTIVE_QD_PIPES_MASK 0x0000FF00 +#define INACTIVE_QD_PIPES_SHIFT 8 +#define INACTIVE_SIMDS(x) ((x) << 16) +#define INACTIVE_SIMDS_MASK 0x00FF0000 + +#define GRBM_CNTL 0x8000 +#define GRBM_READ_TIMEOUT(x) ((x) << 0) +#define GRBM_SOFT_RESET 0x8020 +#define SOFT_RESET_CP (1<<0) +#define GRBM_STATUS 0x8010 +#define CMDFIFO_AVAIL_MASK 0x0000000F +#define GUI_ACTIVE (1<<31) +#define GRBM_STATUS2 0x8014 + +#define CG_MULT_THERMAL_STATUS 0x740 +#define ASIC_T(x) ((x) << 16) +#define ASIC_T_MASK 0x3FF0000 +#define ASIC_T_SHIFT 16 + +#define HDP_HOST_PATH_CNTL 0x2C00 +#define HDP_NONSURFACE_BASE 0x2C04 +#define HDP_NONSURFACE_INFO 0x2C08 +#define HDP_NONSURFACE_SIZE 0x2C0C +#define HDP_REG_COHERENCY_FLUSH_CNTL 0x54A0 +#define HDP_TILING_CONFIG 0x2F3C +#define HDP_DEBUG1 0x2F34 + +#define MC_SHARED_CHMAP 0x2004 +#define NOOFCHAN_SHIFT 12 +#define NOOFCHAN_MASK 0x00003000 +#define MC_SHARED_CHREMAP 0x2008 + +#define MC_ARB_RAMCFG 0x2760 +#define NOOFBANK_SHIFT 0 +#define NOOFBANK_MASK 0x00000003 +#define NOOFRANK_SHIFT 2 +#define NOOFRANK_MASK 0x00000004 +#define NOOFROWS_SHIFT 3 +#define NOOFROWS_MASK 0x00000038 +#define NOOFCOLS_SHIFT 6 +#define NOOFCOLS_MASK 0x000000C0 +#define CHANSIZE_SHIFT 8 +#define CHANSIZE_MASK 0x00000100 +#define BURSTLENGTH_SHIFT 9 +#define BURSTLENGTH_MASK 0x00000200 +#define CHANSIZE_OVERRIDE (1 << 11) +#define MC_VM_AGP_TOP 0x2028 +#define MC_VM_AGP_BOT 0x202C +#define MC_VM_AGP_BASE 0x2030 +#define MC_VM_FB_LOCATION 0x2024 +#define MC_VM_MB_L1_TLB0_CNTL 0x2234 +#define MC_VM_MB_L1_TLB1_CNTL 0x2238 +#define MC_VM_MB_L1_TLB2_CNTL 0x223C +#define MC_VM_MB_L1_TLB3_CNTL 0x2240 +#define ENABLE_L1_TLB (1 << 0) +#define ENABLE_L1_FRAGMENT_PROCESSING (1 << 1) +#define SYSTEM_ACCESS_MODE_PA_ONLY (0 << 3) +#define SYSTEM_ACCESS_MODE_USE_SYS_MAP (1 << 3) +#define SYSTEM_ACCESS_MODE_IN_SYS (2 << 3) +#define SYSTEM_ACCESS_MODE_NOT_IN_SYS (3 << 3) +#define SYSTEM_APERTURE_UNMAPPED_ACCESS_PASS_THRU (0 << 5) +#define EFFECTIVE_L1_TLB_SIZE(x) ((x)<<15) +#define EFFECTIVE_L1_QUEUE_SIZE(x) ((x)<<18) +#define MC_VM_MD_L1_TLB0_CNTL 0x2654 +#define MC_VM_MD_L1_TLB1_CNTL 0x2658 +#define MC_VM_MD_L1_TLB2_CNTL 0x265C +#define MC_VM_MD_L1_TLB3_CNTL 0x2698 +#define MC_VM_SYSTEM_APERTURE_DEFAULT_ADDR 0x203C +#define MC_VM_SYSTEM_APERTURE_HIGH_ADDR 0x2038 +#define MC_VM_SYSTEM_APERTURE_LOW_ADDR 0x2034 + +#define PA_CL_ENHANCE 0x8A14 +#define CLIP_VTX_REORDER_ENA (1 << 0) +#define NUM_CLIP_SEQ(x) ((x) << 1) +#define PA_SC_AA_CONFIG 0x28C04 +#define PA_SC_CLIPRECT_RULE 0x2820C +#define PA_SC_EDGERULE 0x28230 +#define PA_SC_FIFO_SIZE 0x8BCC +#define SC_PRIM_FIFO_SIZE(x) ((x) << 0) +#define SC_HIZ_TILE_FIFO_SIZE(x) ((x) << 12) +#define PA_SC_FORCE_EOV_MAX_CNTS 0x8B24 +#define FORCE_EOV_MAX_CLK_CNT(x) ((x)<<0) +#define FORCE_EOV_MAX_REZ_CNT(x) ((x)<<16) +#define PA_SC_LINE_STIPPLE 0x28A0C +#define PA_SC_LINE_STIPPLE_STATE 0x8B10 +#define PA_SC_MODE_CNTL 0x28A4C +#define PA_SC_MULTI_CHIP_CNTL 0x8B20 +#define SC_EARLYZ_TILE_FIFO_SIZE(x) ((x) << 20) + +#define SCRATCH_REG0 0x8500 +#define SCRATCH_REG1 0x8504 +#define SCRATCH_REG2 0x8508 +#define SCRATCH_REG3 0x850C +#define SCRATCH_REG4 0x8510 +#define SCRATCH_REG5 0x8514 +#define SCRATCH_REG6 0x8518 +#define SCRATCH_REG7 0x851C +#define SCRATCH_UMSK 0x8540 +#define SCRATCH_ADDR 0x8544 + +#define SMX_SAR_CTL0 0xA008 +#define SMX_DC_CTL0 0xA020 +#define USE_HASH_FUNCTION (1 << 0) +#define CACHE_DEPTH(x) ((x) << 1) +#define FLUSH_ALL_ON_EVENT (1 << 10) +#define STALL_ON_EVENT (1 << 11) +#define SMX_EVENT_CTL 0xA02C +#define ES_FLUSH_CTL(x) ((x) << 0) +#define GS_FLUSH_CTL(x) ((x) << 3) +#define ACK_FLUSH_CTL(x) ((x) << 6) +#define SYNC_FLUSH_CTL (1 << 8) + +#define SPI_CONFIG_CNTL 0x9100 +#define GPR_WRITE_PRIORITY(x) ((x) << 0) +#define DISABLE_INTERP_1 (1 << 5) +#define SPI_CONFIG_CNTL_1 0x913C +#define VTX_DONE_DELAY(x) ((x) << 0) +#define INTERP_ONE_PRIM_PER_ROW (1 << 4) +#define SPI_INPUT_Z 0x286D8 +#define SPI_PS_IN_CONTROL_0 0x286CC +#define NUM_INTERP(x) ((x)<<0) +#define POSITION_ENA (1<<8) +#define POSITION_CENTROID (1<<9) +#define POSITION_ADDR(x) ((x)<<10) +#define PARAM_GEN(x) ((x)<<15) +#define PARAM_GEN_ADDR(x) ((x)<<19) +#define BARYC_SAMPLE_CNTL(x) ((x)<<26) +#define PERSP_GRADIENT_ENA (1<<28) +#define LINEAR_GRADIENT_ENA (1<<29) +#define POSITION_SAMPLE (1<<30) +#define BARYC_AT_SAMPLE_ENA (1<<31) + +#define SQ_CONFIG 0x8C00 +#define VC_ENABLE (1 << 0) +#define EXPORT_SRC_C (1 << 1) +#define DX9_CONSTS (1 << 2) +#define ALU_INST_PREFER_VECTOR (1 << 3) +#define DX10_CLAMP (1 << 4) +#define CLAUSE_SEQ_PRIO(x) ((x) << 8) +#define PS_PRIO(x) ((x) << 24) +#define VS_PRIO(x) ((x) << 26) +#define GS_PRIO(x) ((x) << 28) +#define SQ_DYN_GPR_SIZE_SIMD_AB_0 0x8DB0 +#define SIMDA_RING0(x) ((x)<<0) +#define SIMDA_RING1(x) ((x)<<8) +#define SIMDB_RING0(x) ((x)<<16) +#define SIMDB_RING1(x) ((x)<<24) +#define SQ_DYN_GPR_SIZE_SIMD_AB_1 0x8DB4 +#define SQ_DYN_GPR_SIZE_SIMD_AB_2 0x8DB8 +#define SQ_DYN_GPR_SIZE_SIMD_AB_3 0x8DBC +#define SQ_DYN_GPR_SIZE_SIMD_AB_4 0x8DC0 +#define SQ_DYN_GPR_SIZE_SIMD_AB_5 0x8DC4 +#define SQ_DYN_GPR_SIZE_SIMD_AB_6 0x8DC8 +#define SQ_DYN_GPR_SIZE_SIMD_AB_7 0x8DCC +#define ES_PRIO(x) ((x) << 30) +#define SQ_GPR_RESOURCE_MGMT_1 0x8C04 +#define NUM_PS_GPRS(x) ((x) << 0) +#define NUM_VS_GPRS(x) ((x) << 16) +#define DYN_GPR_ENABLE (1 << 27) +#define NUM_CLAUSE_TEMP_GPRS(x) ((x) << 28) +#define SQ_GPR_RESOURCE_MGMT_2 0x8C08 +#define NUM_GS_GPRS(x) ((x) << 0) +#define NUM_ES_GPRS(x) ((x) << 16) +#define SQ_MS_FIFO_SIZES 0x8CF0 +#define CACHE_FIFO_SIZE(x) ((x) << 0) +#define FETCH_FIFO_HIWATER(x) ((x) << 8) +#define DONE_FIFO_HIWATER(x) ((x) << 16) +#define ALU_UPDATE_FIFO_HIWATER(x) ((x) << 24) +#define SQ_STACK_RESOURCE_MGMT_1 0x8C10 +#define NUM_PS_STACK_ENTRIES(x) ((x) << 0) +#define NUM_VS_STACK_ENTRIES(x) ((x) << 16) +#define SQ_STACK_RESOURCE_MGMT_2 0x8C14 +#define NUM_GS_STACK_ENTRIES(x) ((x) << 0) +#define NUM_ES_STACK_ENTRIES(x) ((x) << 16) +#define SQ_THREAD_RESOURCE_MGMT 0x8C0C +#define NUM_PS_THREADS(x) ((x) << 0) +#define NUM_VS_THREADS(x) ((x) << 8) +#define NUM_GS_THREADS(x) ((x) << 16) +#define NUM_ES_THREADS(x) ((x) << 24) + +#define SX_DEBUG_1 0x9058 +#define ENABLE_NEW_SMX_ADDRESS (1 << 16) +#define SX_EXPORT_BUFFER_SIZES 0x900C +#define COLOR_BUFFER_SIZE(x) ((x) << 0) +#define POSITION_BUFFER_SIZE(x) ((x) << 8) +#define SMX_BUFFER_SIZE(x) ((x) << 16) +#define SX_MISC 0x28350 + +#define TA_CNTL_AUX 0x9508 +#define DISABLE_CUBE_WRAP (1 << 0) +#define DISABLE_CUBE_ANISO (1 << 1) +#define SYNC_GRADIENT (1 << 24) +#define SYNC_WALKER (1 << 25) +#define SYNC_ALIGNER (1 << 26) +#define BILINEAR_PRECISION_6_BIT (0 << 31) +#define BILINEAR_PRECISION_8_BIT (1 << 31) + +#define TCP_CNTL 0x9610 +#define TCP_CHAN_STEER 0x9614 + +#define VC_ENHANCE 0x9714 + +#define VGT_CACHE_INVALIDATION 0x88C4 +#define CACHE_INVALIDATION(x) ((x)<<0) +#define VC_ONLY 0 +#define TC_ONLY 1 +#define VC_AND_TC 2 +#define AUTO_INVLD_EN(x) ((x) << 6) +#define NO_AUTO 0 +#define ES_AUTO 1 +#define GS_AUTO 2 +#define ES_AND_GS_AUTO 3 +#define VGT_ES_PER_GS 0x88CC +#define VGT_GS_PER_ES 0x88C8 +#define VGT_GS_PER_VS 0x88E8 +#define VGT_GS_VERTEX_REUSE 0x88D4 +#define VGT_NUM_INSTANCES 0x8974 +#define VGT_OUT_DEALLOC_CNTL 0x28C5C +#define DEALLOC_DIST_MASK 0x0000007F +#define VGT_STRMOUT_EN 0x28AB0 +#define VGT_VERTEX_REUSE_BLOCK_CNTL 0x28C58 +#define VTX_REUSE_DEPTH_MASK 0x000000FF + +#define VM_CONTEXT0_CNTL 0x1410 +#define ENABLE_CONTEXT (1 << 0) +#define PAGE_TABLE_DEPTH(x) (((x) & 3) << 1) +#define RANGE_PROTECTION_FAULT_ENABLE_DEFAULT (1 << 4) +#define VM_CONTEXT0_PAGE_TABLE_BASE_ADDR 0x153C +#define VM_CONTEXT0_PAGE_TABLE_END_ADDR 0x157C +#define VM_CONTEXT0_PAGE_TABLE_START_ADDR 0x155C +#define VM_CONTEXT0_PROTECTION_FAULT_DEFAULT_ADDR 0x1518 +#define VM_L2_CNTL 0x1400 +#define ENABLE_L2_CACHE (1 << 0) +#define ENABLE_L2_FRAGMENT_PROCESSING (1 << 1) +#define ENABLE_L2_PTE_CACHE_LRU_UPDATE_BY_WRITE (1 << 9) +#define EFFECTIVE_L2_QUEUE_SIZE(x) (((x) & 7) << 14) +#define VM_L2_CNTL2 0x1404 +#define INVALIDATE_ALL_L1_TLBS (1 << 0) +#define INVALIDATE_L2_CACHE (1 << 1) +#define VM_L2_CNTL3 0x1408 +#define BANK_SELECT(x) ((x) << 0) +#define CACHE_UPDATE_MODE(x) ((x) << 6) +#define VM_L2_STATUS 0x140C +#define L2_BUSY (1 << 0) + +#define WAIT_UNTIL 0x8040 + +/* async DMA */ +#define DMA_RB_RPTR 0xd008 +#define DMA_RB_WPTR 0xd00c + +/* async DMA packets */ +#define DMA_PACKET(cmd, t, s, n) ((((cmd) & 0xF) << 28) | \ + (((t) & 0x1) << 23) | \ + (((s) & 0x1) << 22) | \ + (((n) & 0xFFFF) << 0)) +/* async DMA Packet types */ +#define DMA_PACKET_WRITE 0x2 +#define DMA_PACKET_COPY 0x3 +#define DMA_PACKET_INDIRECT_BUFFER 0x4 +#define DMA_PACKET_SEMAPHORE 0x5 +#define DMA_PACKET_FENCE 0x6 +#define DMA_PACKET_TRAP 0x7 +#define DMA_PACKET_CONSTANT_FILL 0xd +#define DMA_PACKET_NOP 0xf + + +#define SRBM_STATUS 0x0E50 + +/* DCE 3.2 HDMI */ +#define HDMI_CONTROL 0x7400 +# define HDMI_KEEPOUT_MODE (1 << 0) +# define HDMI_PACKET_GEN_VERSION (1 << 4) /* 0 = r6xx compat */ +# define HDMI_ERROR_ACK (1 << 8) +# define HDMI_ERROR_MASK (1 << 9) +#define HDMI_STATUS 0x7404 +# define HDMI_ACTIVE_AVMUTE (1 << 0) +# define HDMI_AUDIO_PACKET_ERROR (1 << 16) +# define HDMI_VBI_PACKET_ERROR (1 << 20) +#define HDMI_AUDIO_PACKET_CONTROL 0x7408 +# define HDMI_AUDIO_DELAY_EN(x) (((x) & 3) << 4) +# define HDMI_AUDIO_PACKETS_PER_LINE(x) (((x) & 0x1f) << 16) +#define HDMI_ACR_PACKET_CONTROL 0x740c +# define HDMI_ACR_SEND (1 << 0) +# define HDMI_ACR_CONT (1 << 1) +# define HDMI_ACR_SELECT(x) (((x) & 3) << 4) +# define HDMI_ACR_HW 0 +# define HDMI_ACR_32 1 +# define HDMI_ACR_44 2 +# define HDMI_ACR_48 3 +# define HDMI_ACR_SOURCE (1 << 8) /* 0 - hw; 1 - cts value */ +# define HDMI_ACR_AUTO_SEND (1 << 12) +#define HDMI_VBI_PACKET_CONTROL 0x7410 +# define HDMI_NULL_SEND (1 << 0) +# define HDMI_GC_SEND (1 << 4) +# define HDMI_GC_CONT (1 << 5) /* 0 - once; 1 - every frame */ +#define HDMI_INFOFRAME_CONTROL0 0x7414 +# define HDMI_AVI_INFO_SEND (1 << 0) +# define HDMI_AVI_INFO_CONT (1 << 1) +# define HDMI_AUDIO_INFO_SEND (1 << 4) +# define HDMI_AUDIO_INFO_CONT (1 << 5) +# define HDMI_MPEG_INFO_SEND (1 << 8) +# define HDMI_MPEG_INFO_CONT (1 << 9) +#define HDMI_INFOFRAME_CONTROL1 0x7418 +# define HDMI_AVI_INFO_LINE(x) (((x) & 0x3f) << 0) +# define HDMI_AUDIO_INFO_LINE(x) (((x) & 0x3f) << 8) +# define HDMI_MPEG_INFO_LINE(x) (((x) & 0x3f) << 16) +#define HDMI_GENERIC_PACKET_CONTROL 0x741c +# define HDMI_GENERIC0_SEND (1 << 0) +# define HDMI_GENERIC0_CONT (1 << 1) +# define HDMI_GENERIC1_SEND (1 << 4) +# define HDMI_GENERIC1_CONT (1 << 5) +# define HDMI_GENERIC0_LINE(x) (((x) & 0x3f) << 16) +# define HDMI_GENERIC1_LINE(x) (((x) & 0x3f) << 24) +#define HDMI_GC 0x7428 +# define HDMI_GC_AVMUTE (1 << 0) +#define AFMT_AUDIO_PACKET_CONTROL2 0x742c +# define AFMT_AUDIO_LAYOUT_OVRD (1 << 0) +# define AFMT_AUDIO_LAYOUT_SELECT (1 << 1) +# define AFMT_60958_CS_SOURCE (1 << 4) +# define AFMT_AUDIO_CHANNEL_ENABLE(x) (((x) & 0xff) << 8) +# define AFMT_DP_AUDIO_STREAM_ID(x) (((x) & 0xff) << 16) +#define AFMT_AVI_INFO0 0x7454 +# define AFMT_AVI_INFO_CHECKSUM(x) (((x) & 0xff) << 0) +# define AFMT_AVI_INFO_S(x) (((x) & 3) << 8) +# define AFMT_AVI_INFO_B(x) (((x) & 3) << 10) +# define AFMT_AVI_INFO_A(x) (((x) & 1) << 12) +# define AFMT_AVI_INFO_Y(x) (((x) & 3) << 13) +# define AFMT_AVI_INFO_Y_RGB 0 +# define AFMT_AVI_INFO_Y_YCBCR422 1 +# define AFMT_AVI_INFO_Y_YCBCR444 2 +# define AFMT_AVI_INFO_Y_A_B_S(x) (((x) & 0xff) << 8) +# define AFMT_AVI_INFO_R(x) (((x) & 0xf) << 16) +# define AFMT_AVI_INFO_M(x) (((x) & 0x3) << 20) +# define AFMT_AVI_INFO_C(x) (((x) & 0x3) << 22) +# define AFMT_AVI_INFO_C_M_R(x) (((x) & 0xff) << 16) +# define AFMT_AVI_INFO_SC(x) (((x) & 0x3) << 24) +# define AFMT_AVI_INFO_Q(x) (((x) & 0x3) << 26) +# define AFMT_AVI_INFO_EC(x) (((x) & 0x3) << 28) +# define AFMT_AVI_INFO_ITC(x) (((x) & 0x1) << 31) +# define AFMT_AVI_INFO_ITC_EC_Q_SC(x) (((x) & 0xff) << 24) +#define AFMT_AVI_INFO1 0x7458 +# define AFMT_AVI_INFO_VIC(x) (((x) & 0x7f) << 0) /* don't use avi infoframe v1 */ +# define AFMT_AVI_INFO_PR(x) (((x) & 0xf) << 8) /* don't use avi infoframe v1 */ +# define AFMT_AVI_INFO_TOP(x) (((x) & 0xffff) << 16) +#define AFMT_AVI_INFO2 0x745c +# define AFMT_AVI_INFO_BOTTOM(x) (((x) & 0xffff) << 0) +# define AFMT_AVI_INFO_LEFT(x) (((x) & 0xffff) << 16) +#define AFMT_AVI_INFO3 0x7460 +# define AFMT_AVI_INFO_RIGHT(x) (((x) & 0xffff) << 0) +# define AFMT_AVI_INFO_VERSION(x) (((x) & 3) << 24) +#define AFMT_MPEG_INFO0 0x7464 +# define AFMT_MPEG_INFO_CHECKSUM(x) (((x) & 0xff) << 0) +# define AFMT_MPEG_INFO_MB0(x) (((x) & 0xff) << 8) +# define AFMT_MPEG_INFO_MB1(x) (((x) & 0xff) << 16) +# define AFMT_MPEG_INFO_MB2(x) (((x) & 0xff) << 24) +#define AFMT_MPEG_INFO1 0x7468 +# define AFMT_MPEG_INFO_MB3(x) (((x) & 0xff) << 0) +# define AFMT_MPEG_INFO_MF(x) (((x) & 3) << 8) +# define AFMT_MPEG_INFO_FR(x) (((x) & 1) << 12) +#define AFMT_GENERIC0_HDR 0x746c +#define AFMT_GENERIC0_0 0x7470 +#define AFMT_GENERIC0_1 0x7474 +#define AFMT_GENERIC0_2 0x7478 +#define AFMT_GENERIC0_3 0x747c +#define AFMT_GENERIC0_4 0x7480 +#define AFMT_GENERIC0_5 0x7484 +#define AFMT_GENERIC0_6 0x7488 +#define AFMT_GENERIC1_HDR 0x748c +#define AFMT_GENERIC1_0 0x7490 +#define AFMT_GENERIC1_1 0x7494 +#define AFMT_GENERIC1_2 0x7498 +#define AFMT_GENERIC1_3 0x749c +#define AFMT_GENERIC1_4 0x74a0 +#define AFMT_GENERIC1_5 0x74a4 +#define AFMT_GENERIC1_6 0x74a8 +#define HDMI_ACR_32_0 0x74ac +# define HDMI_ACR_CTS_32(x) (((x) & 0xfffff) << 12) +#define HDMI_ACR_32_1 0x74b0 +# define HDMI_ACR_N_32(x) (((x) & 0xfffff) << 0) +#define HDMI_ACR_44_0 0x74b4 +# define HDMI_ACR_CTS_44(x) (((x) & 0xfffff) << 12) +#define HDMI_ACR_44_1 0x74b8 +# define HDMI_ACR_N_44(x) (((x) & 0xfffff) << 0) +#define HDMI_ACR_48_0 0x74bc +# define HDMI_ACR_CTS_48(x) (((x) & 0xfffff) << 12) +#define HDMI_ACR_48_1 0x74c0 +# define HDMI_ACR_N_48(x) (((x) & 0xfffff) << 0) +#define HDMI_ACR_STATUS_0 0x74c4 +#define HDMI_ACR_STATUS_1 0x74c8 +#define AFMT_AUDIO_INFO0 0x74cc +# define AFMT_AUDIO_INFO_CHECKSUM(x) (((x) & 0xff) << 0) +# define AFMT_AUDIO_INFO_CC(x) (((x) & 7) << 8) +# define AFMT_AUDIO_INFO_CHECKSUM_OFFSET(x) (((x) & 0xff) << 16) +#define AFMT_AUDIO_INFO1 0x74d0 +# define AFMT_AUDIO_INFO_CA(x) (((x) & 0xff) << 0) +# define AFMT_AUDIO_INFO_LSV(x) (((x) & 0xf) << 11) +# define AFMT_AUDIO_INFO_DM_INH(x) (((x) & 1) << 15) +# define AFMT_AUDIO_INFO_DM_INH_LSV(x) (((x) & 0xff) << 8) +#define AFMT_60958_0 0x74d4 +# define AFMT_60958_CS_A(x) (((x) & 1) << 0) +# define AFMT_60958_CS_B(x) (((x) & 1) << 1) +# define AFMT_60958_CS_C(x) (((x) & 1) << 2) +# define AFMT_60958_CS_D(x) (((x) & 3) << 3) +# define AFMT_60958_CS_MODE(x) (((x) & 3) << 6) +# define AFMT_60958_CS_CATEGORY_CODE(x) (((x) & 0xff) << 8) +# define AFMT_60958_CS_SOURCE_NUMBER(x) (((x) & 0xf) << 16) +# define AFMT_60958_CS_CHANNEL_NUMBER_L(x) (((x) & 0xf) << 20) +# define AFMT_60958_CS_SAMPLING_FREQUENCY(x) (((x) & 0xf) << 24) +# define AFMT_60958_CS_CLOCK_ACCURACY(x) (((x) & 3) << 28) +#define AFMT_60958_1 0x74d8 +# define AFMT_60958_CS_WORD_LENGTH(x) (((x) & 0xf) << 0) +# define AFMT_60958_CS_ORIGINAL_SAMPLING_FREQUENCY(x) (((x) & 0xf) << 4) +# define AFMT_60958_CS_VALID_L(x) (((x) & 1) << 16) +# define AFMT_60958_CS_VALID_R(x) (((x) & 1) << 18) +# define AFMT_60958_CS_CHANNEL_NUMBER_R(x) (((x) & 0xf) << 20) +#define AFMT_AUDIO_CRC_CONTROL 0x74dc +# define AFMT_AUDIO_CRC_EN (1 << 0) +#define AFMT_RAMP_CONTROL0 0x74e0 +# define AFMT_RAMP_MAX_COUNT(x) (((x) & 0xffffff) << 0) +# define AFMT_RAMP_DATA_SIGN (1 << 31) +#define AFMT_RAMP_CONTROL1 0x74e4 +# define AFMT_RAMP_MIN_COUNT(x) (((x) & 0xffffff) << 0) +# define AFMT_AUDIO_TEST_CH_DISABLE(x) (((x) & 0xff) << 24) +#define AFMT_RAMP_CONTROL2 0x74e8 +# define AFMT_RAMP_INC_COUNT(x) (((x) & 0xffffff) << 0) +#define AFMT_RAMP_CONTROL3 0x74ec +# define AFMT_RAMP_DEC_COUNT(x) (((x) & 0xffffff) << 0) +#define AFMT_60958_2 0x74f0 +# define AFMT_60958_CS_CHANNEL_NUMBER_2(x) (((x) & 0xf) << 0) +# define AFMT_60958_CS_CHANNEL_NUMBER_3(x) (((x) & 0xf) << 4) +# define AFMT_60958_CS_CHANNEL_NUMBER_4(x) (((x) & 0xf) << 8) +# define AFMT_60958_CS_CHANNEL_NUMBER_5(x) (((x) & 0xf) << 12) +# define AFMT_60958_CS_CHANNEL_NUMBER_6(x) (((x) & 0xf) << 16) +# define AFMT_60958_CS_CHANNEL_NUMBER_7(x) (((x) & 0xf) << 20) +#define AFMT_STATUS 0x7600 +# define AFMT_AUDIO_ENABLE (1 << 4) +# define AFMT_AZ_FORMAT_WTRIG (1 << 28) +# define AFMT_AZ_FORMAT_WTRIG_INT (1 << 29) +# define AFMT_AZ_AUDIO_ENABLE_CHG (1 << 30) +#define AFMT_AUDIO_PACKET_CONTROL 0x7604 +# define AFMT_AUDIO_SAMPLE_SEND (1 << 0) +# define AFMT_AUDIO_TEST_EN (1 << 12) +# define AFMT_AUDIO_CHANNEL_SWAP (1 << 24) +# define AFMT_60958_CS_UPDATE (1 << 26) +# define AFMT_AZ_AUDIO_ENABLE_CHG_MASK (1 << 27) +# define AFMT_AZ_FORMAT_WTRIG_MASK (1 << 28) +# define AFMT_AZ_FORMAT_WTRIG_ACK (1 << 29) +# define AFMT_AZ_AUDIO_ENABLE_CHG_ACK (1 << 30) +#define AFMT_VBI_PACKET_CONTROL 0x7608 +# define AFMT_GENERIC0_UPDATE (1 << 2) +#define AFMT_INFOFRAME_CONTROL0 0x760c +# define AFMT_AUDIO_INFO_SOURCE (1 << 6) /* 0 - sound block; 1 - hmdi regs */ +# define AFMT_AUDIO_INFO_UPDATE (1 << 7) +# define AFMT_MPEG_INFO_UPDATE (1 << 10) +#define AFMT_GENERIC0_7 0x7610 +/* second instance starts at 0x7800 */ +#define HDMI_OFFSET0 (0x7400 - 0x7400) +#define HDMI_OFFSET1 (0x7800 - 0x7400) + +/* DCE3.2 ELD audio interface */ +#define AZ_F0_CODEC_PIN0_CONTROL_AUDIO_DESCRIPTOR0 0x71c8 /* LPCM */ +#define AZ_F0_CODEC_PIN0_CONTROL_AUDIO_DESCRIPTOR1 0x71cc /* AC3 */ +#define AZ_F0_CODEC_PIN0_CONTROL_AUDIO_DESCRIPTOR2 0x71d0 /* MPEG1 */ +#define AZ_F0_CODEC_PIN0_CONTROL_AUDIO_DESCRIPTOR3 0x71d4 /* MP3 */ +#define AZ_F0_CODEC_PIN0_CONTROL_AUDIO_DESCRIPTOR4 0x71d8 /* MPEG2 */ +#define AZ_F0_CODEC_PIN0_CONTROL_AUDIO_DESCRIPTOR5 0x71dc /* AAC */ +#define AZ_F0_CODEC_PIN0_CONTROL_AUDIO_DESCRIPTOR6 0x71e0 /* DTS */ +#define AZ_F0_CODEC_PIN0_CONTROL_AUDIO_DESCRIPTOR7 0x71e4 /* ATRAC */ +#define AZ_F0_CODEC_PIN0_CONTROL_AUDIO_DESCRIPTOR8 0x71e8 /* one bit audio - leave at 0 (default) */ +#define AZ_F0_CODEC_PIN0_CONTROL_AUDIO_DESCRIPTOR9 0x71ec /* Dolby Digital */ +#define AZ_F0_CODEC_PIN0_CONTROL_AUDIO_DESCRIPTOR10 0x71f0 /* DTS-HD */ +#define AZ_F0_CODEC_PIN0_CONTROL_AUDIO_DESCRIPTOR11 0x71f4 /* MAT-MLP */ +#define AZ_F0_CODEC_PIN0_CONTROL_AUDIO_DESCRIPTOR12 0x71f8 /* DTS */ +#define AZ_F0_CODEC_PIN0_CONTROL_AUDIO_DESCRIPTOR13 0x71fc /* WMA Pro */ +# define MAX_CHANNELS(x) (((x) & 0x7) << 0) +/* max channels minus one. 7 = 8 channels */ +# define SUPPORTED_FREQUENCIES(x) (((x) & 0xff) << 8) +# define DESCRIPTOR_BYTE_2(x) (((x) & 0xff) << 16) +# define SUPPORTED_FREQUENCIES_STEREO(x) (((x) & 0xff) << 24) /* LPCM only */ +/* SUPPORTED_FREQUENCIES, SUPPORTED_FREQUENCIES_STEREO + * bit0 = 32 kHz + * bit1 = 44.1 kHz + * bit2 = 48 kHz + * bit3 = 88.2 kHz + * bit4 = 96 kHz + * bit5 = 176.4 kHz + * bit6 = 192 kHz + */ + +#define AZ_HOT_PLUG_CONTROL 0x7300 +# define AZ_FORCE_CODEC_WAKE (1 << 0) +# define PIN0_JACK_DETECTION_ENABLE (1 << 4) +# define PIN1_JACK_DETECTION_ENABLE (1 << 5) +# define PIN2_JACK_DETECTION_ENABLE (1 << 6) +# define PIN3_JACK_DETECTION_ENABLE (1 << 7) +# define PIN0_UNSOLICITED_RESPONSE_ENABLE (1 << 8) +# define PIN1_UNSOLICITED_RESPONSE_ENABLE (1 << 9) +# define PIN2_UNSOLICITED_RESPONSE_ENABLE (1 << 10) +# define PIN3_UNSOLICITED_RESPONSE_ENABLE (1 << 11) +# define CODEC_HOT_PLUG_ENABLE (1 << 12) +# define PIN0_AUDIO_ENABLED (1 << 24) +# define PIN1_AUDIO_ENABLED (1 << 25) +# define PIN2_AUDIO_ENABLED (1 << 26) +# define PIN3_AUDIO_ENABLED (1 << 27) +# define AUDIO_ENABLED (1 << 31) + + +#define D1GRPH_PRIMARY_SURFACE_ADDRESS 0x6110 +#define D1GRPH_PRIMARY_SURFACE_ADDRESS_HIGH 0x6914 +#define D2GRPH_PRIMARY_SURFACE_ADDRESS_HIGH 0x6114 +#define D1GRPH_SECONDARY_SURFACE_ADDRESS 0x6118 +#define D1GRPH_SECONDARY_SURFACE_ADDRESS_HIGH 0x691c +#define D2GRPH_SECONDARY_SURFACE_ADDRESS_HIGH 0x611c + +/* PCIE link stuff */ +#define PCIE_LC_TRAINING_CNTL 0xa1 /* PCIE_P */ +#define PCIE_LC_LINK_WIDTH_CNTL 0xa2 /* PCIE_P */ +# define LC_LINK_WIDTH_SHIFT 0 +# define LC_LINK_WIDTH_MASK 0x7 +# define LC_LINK_WIDTH_X0 0 +# define LC_LINK_WIDTH_X1 1 +# define LC_LINK_WIDTH_X2 2 +# define LC_LINK_WIDTH_X4 3 +# define LC_LINK_WIDTH_X8 4 +# define LC_LINK_WIDTH_X16 6 +# define LC_LINK_WIDTH_RD_SHIFT 4 +# define LC_LINK_WIDTH_RD_MASK 0x70 +# define LC_RECONFIG_ARC_MISSING_ESCAPE (1 << 7) +# define LC_RECONFIG_NOW (1 << 8) +# define LC_RENEGOTIATION_SUPPORT (1 << 9) +# define LC_RENEGOTIATE_EN (1 << 10) +# define LC_SHORT_RECONFIG_EN (1 << 11) +# define LC_UPCONFIGURE_SUPPORT (1 << 12) +# define LC_UPCONFIGURE_DIS (1 << 13) +#define PCIE_LC_SPEED_CNTL 0xa4 /* PCIE_P */ +# define LC_GEN2_EN_STRAP (1 << 0) +# define LC_TARGET_LINK_SPEED_OVERRIDE_EN (1 << 1) +# define LC_FORCE_EN_HW_SPEED_CHANGE (1 << 5) +# define LC_FORCE_DIS_HW_SPEED_CHANGE (1 << 6) +# define LC_SPEED_CHANGE_ATTEMPTS_ALLOWED_MASK (0x3 << 8) +# define LC_SPEED_CHANGE_ATTEMPTS_ALLOWED_SHIFT 3 +# define LC_CURRENT_DATA_RATE (1 << 11) +# define LC_VOLTAGE_TIMER_SEL_MASK (0xf << 14) +# define LC_CLR_FAILED_SPD_CHANGE_CNT (1 << 21) +# define LC_OTHER_SIDE_EVER_SENT_GEN2 (1 << 23) +# define LC_OTHER_SIDE_SUPPORTS_GEN2 (1 << 24) +#define MM_CFGREGS_CNTL 0x544c +# define MM_WR_TO_CFG_EN (1 << 3) +#define LINK_CNTL2 0x88 /* F0 */ +# define TARGET_LINK_SPEED_MASK (0xf << 0) +# define SELECTABLE_DEEMPHASIS (1 << 6) + +#endif diff --git a/sys/dev/drm2/radeon/si.c b/sys/dev/drm2/radeon/si.c new file mode 100644 index 00000000000..6a04b945ca3 --- /dev/null +++ b/sys/dev/drm2/radeon/si.c @@ -0,0 +1,4424 @@ +/* + * Copyright 2011 Advanced Micro Devices, Inc. + * + * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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. + * + * Authors: Alex Deucher + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include "radeon.h" +#include "radeon_asic.h" +#include +#include "sid.h" +#include "atom.h" +#include "si_blit_shaders.h" + +#define SI_PFP_UCODE_SIZE 2144 +#define SI_PM4_UCODE_SIZE 2144 +#define SI_CE_UCODE_SIZE 2144 +#define SI_RLC_UCODE_SIZE 2048 +#define SI_MC_UCODE_SIZE 7769 + +/* get temperature in millidegrees */ +int si_get_temp(struct radeon_device *rdev) +{ + u32 temp; + int actual_temp = 0; + + temp = (RREG32(CG_MULT_THERMAL_STATUS) & CTF_TEMP_MASK) >> + CTF_TEMP_SHIFT; + + if (temp & 0x200) + actual_temp = 255; + else + actual_temp = temp & 0x1ff; + + actual_temp = (actual_temp * 1000); + + return actual_temp; +} + +#define TAHITI_IO_MC_REGS_SIZE 36 + +static const u32 tahiti_io_mc_regs[TAHITI_IO_MC_REGS_SIZE][2] = { + {0x0000006f, 0x03044000}, + {0x00000070, 0x0480c018}, + {0x00000071, 0x00000040}, + {0x00000072, 0x01000000}, + {0x00000074, 0x000000ff}, + {0x00000075, 0x00143400}, + {0x00000076, 0x08ec0800}, + {0x00000077, 0x040000cc}, + {0x00000079, 0x00000000}, + {0x0000007a, 0x21000409}, + {0x0000007c, 0x00000000}, + {0x0000007d, 0xe8000000}, + {0x0000007e, 0x044408a8}, + {0x0000007f, 0x00000003}, + {0x00000080, 0x00000000}, + {0x00000081, 0x01000000}, + {0x00000082, 0x02000000}, + {0x00000083, 0x00000000}, + {0x00000084, 0xe3f3e4f4}, + {0x00000085, 0x00052024}, + {0x00000087, 0x00000000}, + {0x00000088, 0x66036603}, + {0x00000089, 0x01000000}, + {0x0000008b, 0x1c0a0000}, + {0x0000008c, 0xff010000}, + {0x0000008e, 0xffffefff}, + {0x0000008f, 0xfff3efff}, + {0x00000090, 0xfff3efbf}, + {0x00000094, 0x00101101}, + {0x00000095, 0x00000fff}, + {0x00000096, 0x00116fff}, + {0x00000097, 0x60010000}, + {0x00000098, 0x10010000}, + {0x00000099, 0x00006000}, + {0x0000009a, 0x00001000}, + {0x0000009f, 0x00a77400} +}; + +static const u32 pitcairn_io_mc_regs[TAHITI_IO_MC_REGS_SIZE][2] = { + {0x0000006f, 0x03044000}, + {0x00000070, 0x0480c018}, + {0x00000071, 0x00000040}, + {0x00000072, 0x01000000}, + {0x00000074, 0x000000ff}, + {0x00000075, 0x00143400}, + {0x00000076, 0x08ec0800}, + {0x00000077, 0x040000cc}, + {0x00000079, 0x00000000}, + {0x0000007a, 0x21000409}, + {0x0000007c, 0x00000000}, + {0x0000007d, 0xe8000000}, + {0x0000007e, 0x044408a8}, + {0x0000007f, 0x00000003}, + {0x00000080, 0x00000000}, + {0x00000081, 0x01000000}, + {0x00000082, 0x02000000}, + {0x00000083, 0x00000000}, + {0x00000084, 0xe3f3e4f4}, + {0x00000085, 0x00052024}, + {0x00000087, 0x00000000}, + {0x00000088, 0x66036603}, + {0x00000089, 0x01000000}, + {0x0000008b, 0x1c0a0000}, + {0x0000008c, 0xff010000}, + {0x0000008e, 0xffffefff}, + {0x0000008f, 0xfff3efff}, + {0x00000090, 0xfff3efbf}, + {0x00000094, 0x00101101}, + {0x00000095, 0x00000fff}, + {0x00000096, 0x00116fff}, + {0x00000097, 0x60010000}, + {0x00000098, 0x10010000}, + {0x00000099, 0x00006000}, + {0x0000009a, 0x00001000}, + {0x0000009f, 0x00a47400} +}; + +static const u32 verde_io_mc_regs[TAHITI_IO_MC_REGS_SIZE][2] = { + {0x0000006f, 0x03044000}, + {0x00000070, 0x0480c018}, + {0x00000071, 0x00000040}, + {0x00000072, 0x01000000}, + {0x00000074, 0x000000ff}, + {0x00000075, 0x00143400}, + {0x00000076, 0x08ec0800}, + {0x00000077, 0x040000cc}, + {0x00000079, 0x00000000}, + {0x0000007a, 0x21000409}, + {0x0000007c, 0x00000000}, + {0x0000007d, 0xe8000000}, + {0x0000007e, 0x044408a8}, + {0x0000007f, 0x00000003}, + {0x00000080, 0x00000000}, + {0x00000081, 0x01000000}, + {0x00000082, 0x02000000}, + {0x00000083, 0x00000000}, + {0x00000084, 0xe3f3e4f4}, + {0x00000085, 0x00052024}, + {0x00000087, 0x00000000}, + {0x00000088, 0x66036603}, + {0x00000089, 0x01000000}, + {0x0000008b, 0x1c0a0000}, + {0x0000008c, 0xff010000}, + {0x0000008e, 0xffffefff}, + {0x0000008f, 0xfff3efff}, + {0x00000090, 0xfff3efbf}, + {0x00000094, 0x00101101}, + {0x00000095, 0x00000fff}, + {0x00000096, 0x00116fff}, + {0x00000097, 0x60010000}, + {0x00000098, 0x10010000}, + {0x00000099, 0x00006000}, + {0x0000009a, 0x00001000}, + {0x0000009f, 0x00a37400} +}; + +/* ucode loading */ +static int si_mc_load_microcode(struct radeon_device *rdev) +{ + const __be32 *fw_data; + u32 running, blackout = 0; + u32 *io_mc_regs; + int i, ucode_size, regs_size; + + if (!rdev->mc_fw) + return -EINVAL; + + switch (rdev->family) { + case CHIP_TAHITI: + io_mc_regs = (u32 *)&tahiti_io_mc_regs; + ucode_size = SI_MC_UCODE_SIZE; + regs_size = TAHITI_IO_MC_REGS_SIZE; + break; + case CHIP_PITCAIRN: + io_mc_regs = (u32 *)&pitcairn_io_mc_regs; + ucode_size = SI_MC_UCODE_SIZE; + regs_size = TAHITI_IO_MC_REGS_SIZE; + break; + case CHIP_VERDE: + default: + io_mc_regs = (u32 *)&verde_io_mc_regs; + ucode_size = SI_MC_UCODE_SIZE; + regs_size = TAHITI_IO_MC_REGS_SIZE; + break; + } + + running = RREG32(MC_SEQ_SUP_CNTL) & RUN_MASK; + + if (running == 0) { + if (running) { + blackout = RREG32(MC_SHARED_BLACKOUT_CNTL); + WREG32(MC_SHARED_BLACKOUT_CNTL, blackout | 1); + } + + /* reset the engine and set to writable */ + WREG32(MC_SEQ_SUP_CNTL, 0x00000008); + WREG32(MC_SEQ_SUP_CNTL, 0x00000010); + + /* load mc io regs */ + for (i = 0; i < regs_size; i++) { + WREG32(MC_SEQ_IO_DEBUG_INDEX, io_mc_regs[(i << 1)]); + WREG32(MC_SEQ_IO_DEBUG_DATA, io_mc_regs[(i << 1) + 1]); + } + /* load the MC ucode */ + fw_data = (const __be32 *)rdev->mc_fw->data; + for (i = 0; i < ucode_size; i++) + WREG32(MC_SEQ_SUP_PGM, be32_to_cpup(fw_data++)); + + /* put the engine back into the active state */ + WREG32(MC_SEQ_SUP_CNTL, 0x00000008); + WREG32(MC_SEQ_SUP_CNTL, 0x00000004); + WREG32(MC_SEQ_SUP_CNTL, 0x00000001); + + /* wait for training to complete */ + for (i = 0; i < rdev->usec_timeout; i++) { + if (RREG32(MC_SEQ_TRAIN_WAKEUP_CNTL) & TRAIN_DONE_D0) + break; + DRM_UDELAY(1); + } + for (i = 0; i < rdev->usec_timeout; i++) { + if (RREG32(MC_SEQ_TRAIN_WAKEUP_CNTL) & TRAIN_DONE_D1) + break; + DRM_UDELAY(1); + } + + if (running) + WREG32(MC_SHARED_BLACKOUT_CNTL, blackout); + } + + return 0; +} + +static int si_init_microcode(struct radeon_device *rdev) +{ + const char *chip_name; + const char *rlc_chip_name; + size_t pfp_req_size, me_req_size, ce_req_size, rlc_req_size, mc_req_size; + char fw_name[30]; + int err; + + DRM_DEBUG("\n"); + + switch (rdev->family) { + case CHIP_TAHITI: + chip_name = "TAHITI"; + rlc_chip_name = "TAHITI"; + pfp_req_size = SI_PFP_UCODE_SIZE * 4; + me_req_size = SI_PM4_UCODE_SIZE * 4; + ce_req_size = SI_CE_UCODE_SIZE * 4; + rlc_req_size = SI_RLC_UCODE_SIZE * 4; + mc_req_size = SI_MC_UCODE_SIZE * 4; + break; + case CHIP_PITCAIRN: + chip_name = "PITCAIRN"; + rlc_chip_name = "PITCAIRN"; + pfp_req_size = SI_PFP_UCODE_SIZE * 4; + me_req_size = SI_PM4_UCODE_SIZE * 4; + ce_req_size = SI_CE_UCODE_SIZE * 4; + rlc_req_size = SI_RLC_UCODE_SIZE * 4; + mc_req_size = SI_MC_UCODE_SIZE * 4; + break; + case CHIP_VERDE: + chip_name = "VERDE"; + rlc_chip_name = "VERDE"; + pfp_req_size = SI_PFP_UCODE_SIZE * 4; + me_req_size = SI_PM4_UCODE_SIZE * 4; + ce_req_size = SI_CE_UCODE_SIZE * 4; + rlc_req_size = SI_RLC_UCODE_SIZE * 4; + mc_req_size = SI_MC_UCODE_SIZE * 4; + break; + default: panic("%s: Unsupported family %d", __func__, rdev->family); + } + + DRM_INFO("Loading %s Microcode\n", chip_name); + err = 0; + + snprintf(fw_name, sizeof(fw_name), "radeonkmsfw_%s_pfp", chip_name); + rdev->pfp_fw = firmware_get(fw_name); + if (rdev->pfp_fw == NULL) { + err = -ENOENT; + goto out; + } + if (rdev->pfp_fw->datasize != pfp_req_size) { + DRM_ERROR( + "si_cp: Bogus length %zu in firmware \"%s\"\n", + rdev->pfp_fw->datasize, fw_name); + err = -EINVAL; + goto out; + } + + snprintf(fw_name, sizeof(fw_name), "radeonkmsfw_%s_me", chip_name); + rdev->me_fw = firmware_get(fw_name); + if (rdev->me_fw == NULL) { + err = -ENOENT; + goto out; + } + if (rdev->me_fw->datasize != me_req_size) { + DRM_ERROR( + "si_cp: Bogus length %zu in firmware \"%s\"\n", + rdev->me_fw->datasize, fw_name); + err = -EINVAL; + } + + snprintf(fw_name, sizeof(fw_name), "radeonkmsfw_%s_ce", chip_name); + rdev->ce_fw = firmware_get(fw_name); + if (rdev->ce_fw == NULL) { + err = -ENOENT; + goto out; + } + if (rdev->ce_fw->datasize != ce_req_size) { + DRM_ERROR( + "si_cp: Bogus length %zu in firmware \"%s\"\n", + rdev->ce_fw->datasize, fw_name); + err = -EINVAL; + } + + snprintf(fw_name, sizeof(fw_name), "radeonkmsfw_%s_rlc", rlc_chip_name); + rdev->rlc_fw = firmware_get(fw_name); + if (rdev->rlc_fw == NULL) { + err = -ENOENT; + goto out; + } + if (rdev->rlc_fw->datasize != rlc_req_size) { + DRM_ERROR( + "si_rlc: Bogus length %zu in firmware \"%s\"\n", + rdev->rlc_fw->datasize, fw_name); + err = -EINVAL; + } + + snprintf(fw_name, sizeof(fw_name), "radeonkmsfw_%s_mc", chip_name); + rdev->mc_fw = firmware_get(fw_name); + if (rdev->mc_fw == NULL) { + err = -ENOENT; + goto out; + } + if (rdev->mc_fw->datasize != mc_req_size) { + DRM_ERROR( + "si_mc: Bogus length %zu in firmware \"%s\"\n", + rdev->mc_fw->datasize, fw_name); + err = -EINVAL; + } + +out: + if (err) { + if (err != -EINVAL) + DRM_ERROR( + "si_cp: Failed to load firmware \"%s\"\n", + fw_name); + if (rdev->pfp_fw != NULL) { + firmware_put(rdev->pfp_fw, FIRMWARE_UNLOAD); + rdev->pfp_fw = NULL; + } + if (rdev->me_fw != NULL) { + firmware_put(rdev->me_fw, FIRMWARE_UNLOAD); + rdev->me_fw = NULL; + } + if (rdev->ce_fw != NULL) { + firmware_put(rdev->ce_fw, FIRMWARE_UNLOAD); + rdev->ce_fw = NULL; + } + if (rdev->rlc_fw != NULL) { + firmware_put(rdev->rlc_fw, FIRMWARE_UNLOAD); + rdev->rlc_fw = NULL; + } + if (rdev->mc_fw != NULL) { + firmware_put(rdev->mc_fw, FIRMWARE_UNLOAD); + rdev->mc_fw = NULL; + } + } + return err; +} + +/** + * si_fini_microcode - drop the firmwares image references + * + * @rdev: radeon_device pointer + * + * Drop the pfp, me, rlc, mc and ce firmware image references. + * Called at driver shutdown. + */ +static void si_fini_microcode(struct radeon_device *rdev) +{ + + if (rdev->pfp_fw != NULL) { + firmware_put(rdev->pfp_fw, FIRMWARE_UNLOAD); + rdev->pfp_fw = NULL; + } + + if (rdev->me_fw != NULL) { + firmware_put(rdev->me_fw, FIRMWARE_UNLOAD); + rdev->me_fw = NULL; + } + + if (rdev->rlc_fw != NULL) { + firmware_put(rdev->rlc_fw, FIRMWARE_UNLOAD); + rdev->rlc_fw = NULL; + } + + if (rdev->mc_fw != NULL) { + firmware_put(rdev->mc_fw, FIRMWARE_UNLOAD); + rdev->mc_fw = NULL; + } + + if (rdev->ce_fw != NULL) { + firmware_put(rdev->ce_fw, FIRMWARE_UNLOAD); + rdev->ce_fw = NULL; + } +} + +/* watermark setup */ +static u32 dce6_line_buffer_adjust(struct radeon_device *rdev, + struct radeon_crtc *radeon_crtc, + struct drm_display_mode *mode, + struct drm_display_mode *other_mode) +{ + u32 tmp; + /* + * Line Buffer Setup + * There are 3 line buffers, each one shared by 2 display controllers. + * DC_LB_MEMORY_SPLIT controls how that line buffer is shared between + * the display controllers. The paritioning is done via one of four + * preset allocations specified in bits 21:20: + * 0 - half lb + * 2 - whole lb, other crtc must be disabled + */ + /* this can get tricky if we have two large displays on a paired group + * of crtcs. Ideally for multiple large displays we'd assign them to + * non-linked crtcs for maximum line buffer allocation. + */ + if (radeon_crtc->base.enabled && mode) { + if (other_mode) + tmp = 0; /* 1/2 */ + else + tmp = 2; /* whole */ + } else + tmp = 0; + + WREG32(DC_LB_MEMORY_SPLIT + radeon_crtc->crtc_offset, + DC_LB_MEMORY_CONFIG(tmp)); + + if (radeon_crtc->base.enabled && mode) { + switch (tmp) { + case 0: + default: + return 4096 * 2; + case 2: + return 8192 * 2; + } + } + + /* controller not enabled, so no lb used */ + return 0; +} + +static u32 si_get_number_of_dram_channels(struct radeon_device *rdev) +{ + u32 tmp = RREG32(MC_SHARED_CHMAP); + + switch ((tmp & NOOFCHAN_MASK) >> NOOFCHAN_SHIFT) { + case 0: + default: + return 1; + case 1: + return 2; + case 2: + return 4; + case 3: + return 8; + case 4: + return 3; + case 5: + return 6; + case 6: + return 10; + case 7: + return 12; + case 8: + return 16; + } +} + +struct dce6_wm_params { + u32 dram_channels; /* number of dram channels */ + u32 yclk; /* bandwidth per dram data pin in kHz */ + u32 sclk; /* engine clock in kHz */ + u32 disp_clk; /* display clock in kHz */ + u32 src_width; /* viewport width */ + u32 active_time; /* active display time in ns */ + u32 blank_time; /* blank time in ns */ + bool interlaced; /* mode is interlaced */ + fixed20_12 vsc; /* vertical scale ratio */ + u32 num_heads; /* number of active crtcs */ + u32 bytes_per_pixel; /* bytes per pixel display + overlay */ + u32 lb_size; /* line buffer allocated to pipe */ + u32 vtaps; /* vertical scaler taps */ +}; + +static u32 dce6_dram_bandwidth(struct dce6_wm_params *wm) +{ + /* Calculate raw DRAM Bandwidth */ + fixed20_12 dram_efficiency; /* 0.7 */ + fixed20_12 yclk, dram_channels, bandwidth; + fixed20_12 a; + + a.full = dfixed_const(1000); + yclk.full = dfixed_const(wm->yclk); + yclk.full = dfixed_div(yclk, a); + dram_channels.full = dfixed_const(wm->dram_channels * 4); + a.full = dfixed_const(10); + dram_efficiency.full = dfixed_const(7); + dram_efficiency.full = dfixed_div(dram_efficiency, a); + bandwidth.full = dfixed_mul(dram_channels, yclk); + bandwidth.full = dfixed_mul(bandwidth, dram_efficiency); + + return dfixed_trunc(bandwidth); +} + +static u32 dce6_dram_bandwidth_for_display(struct dce6_wm_params *wm) +{ + /* Calculate DRAM Bandwidth and the part allocated to display. */ + fixed20_12 disp_dram_allocation; /* 0.3 to 0.7 */ + fixed20_12 yclk, dram_channels, bandwidth; + fixed20_12 a; + + a.full = dfixed_const(1000); + yclk.full = dfixed_const(wm->yclk); + yclk.full = dfixed_div(yclk, a); + dram_channels.full = dfixed_const(wm->dram_channels * 4); + a.full = dfixed_const(10); + disp_dram_allocation.full = dfixed_const(3); /* XXX worse case value 0.3 */ + disp_dram_allocation.full = dfixed_div(disp_dram_allocation, a); + bandwidth.full = dfixed_mul(dram_channels, yclk); + bandwidth.full = dfixed_mul(bandwidth, disp_dram_allocation); + + return dfixed_trunc(bandwidth); +} + +static u32 dce6_data_return_bandwidth(struct dce6_wm_params *wm) +{ + /* Calculate the display Data return Bandwidth */ + fixed20_12 return_efficiency; /* 0.8 */ + fixed20_12 sclk, bandwidth; + fixed20_12 a; + + a.full = dfixed_const(1000); + sclk.full = dfixed_const(wm->sclk); + sclk.full = dfixed_div(sclk, a); + a.full = dfixed_const(10); + return_efficiency.full = dfixed_const(8); + return_efficiency.full = dfixed_div(return_efficiency, a); + a.full = dfixed_const(32); + bandwidth.full = dfixed_mul(a, sclk); + bandwidth.full = dfixed_mul(bandwidth, return_efficiency); + + return dfixed_trunc(bandwidth); +} + +static u32 dce6_get_dmif_bytes_per_request(struct dce6_wm_params *wm) +{ + return 32; +} + +static u32 dce6_dmif_request_bandwidth(struct dce6_wm_params *wm) +{ + /* Calculate the DMIF Request Bandwidth */ + fixed20_12 disp_clk_request_efficiency; /* 0.8 */ + fixed20_12 disp_clk, sclk, bandwidth; + fixed20_12 a, b1, b2; + u32 min_bandwidth; + + a.full = dfixed_const(1000); + disp_clk.full = dfixed_const(wm->disp_clk); + disp_clk.full = dfixed_div(disp_clk, a); + a.full = dfixed_const(dce6_get_dmif_bytes_per_request(wm) / 2); + b1.full = dfixed_mul(a, disp_clk); + + a.full = dfixed_const(1000); + sclk.full = dfixed_const(wm->sclk); + sclk.full = dfixed_div(sclk, a); + a.full = dfixed_const(dce6_get_dmif_bytes_per_request(wm)); + b2.full = dfixed_mul(a, sclk); + + a.full = dfixed_const(10); + disp_clk_request_efficiency.full = dfixed_const(8); + disp_clk_request_efficiency.full = dfixed_div(disp_clk_request_efficiency, a); + + min_bandwidth = min(dfixed_trunc(b1), dfixed_trunc(b2)); + + a.full = dfixed_const(min_bandwidth); + bandwidth.full = dfixed_mul(a, disp_clk_request_efficiency); + + return dfixed_trunc(bandwidth); +} + +static u32 dce6_available_bandwidth(struct dce6_wm_params *wm) +{ + /* Calculate the Available bandwidth. Display can use this temporarily but not in average. */ + u32 dram_bandwidth = dce6_dram_bandwidth(wm); + u32 data_return_bandwidth = dce6_data_return_bandwidth(wm); + u32 dmif_req_bandwidth = dce6_dmif_request_bandwidth(wm); + + return min(dram_bandwidth, min(data_return_bandwidth, dmif_req_bandwidth)); +} + +static u32 dce6_average_bandwidth(struct dce6_wm_params *wm) +{ + /* Calculate the display mode Average Bandwidth + * DisplayMode should contain the source and destination dimensions, + * timing, etc. + */ + fixed20_12 bpp; + fixed20_12 line_time; + fixed20_12 src_width; + fixed20_12 bandwidth; + fixed20_12 a; + + a.full = dfixed_const(1000); + line_time.full = dfixed_const(wm->active_time + wm->blank_time); + line_time.full = dfixed_div(line_time, a); + bpp.full = dfixed_const(wm->bytes_per_pixel); + src_width.full = dfixed_const(wm->src_width); + bandwidth.full = dfixed_mul(src_width, bpp); + bandwidth.full = dfixed_mul(bandwidth, wm->vsc); + bandwidth.full = dfixed_div(bandwidth, line_time); + + return dfixed_trunc(bandwidth); +} + +static u32 dce6_latency_watermark(struct dce6_wm_params *wm) +{ + /* First calcualte the latency in ns */ + u32 mc_latency = 2000; /* 2000 ns. */ + u32 available_bandwidth = dce6_available_bandwidth(wm); + u32 worst_chunk_return_time = (512 * 8 * 1000) / available_bandwidth; + u32 cursor_line_pair_return_time = (128 * 4 * 1000) / available_bandwidth; + u32 dc_latency = 40000000 / wm->disp_clk; /* dc pipe latency */ + u32 other_heads_data_return_time = ((wm->num_heads + 1) * worst_chunk_return_time) + + (wm->num_heads * cursor_line_pair_return_time); + u32 latency = mc_latency + other_heads_data_return_time + dc_latency; + u32 max_src_lines_per_dst_line, lb_fill_bw, line_fill_time; + u32 tmp, dmif_size = 12288; + fixed20_12 a, b, c; + + if (wm->num_heads == 0) + return 0; + + a.full = dfixed_const(2); + b.full = dfixed_const(1); + if ((wm->vsc.full > a.full) || + ((wm->vsc.full > b.full) && (wm->vtaps >= 3)) || + (wm->vtaps >= 5) || + ((wm->vsc.full >= a.full) && wm->interlaced)) + max_src_lines_per_dst_line = 4; + else + max_src_lines_per_dst_line = 2; + + a.full = dfixed_const(available_bandwidth); + b.full = dfixed_const(wm->num_heads); + a.full = dfixed_div(a, b); + + b.full = dfixed_const(mc_latency + 512); + c.full = dfixed_const(wm->disp_clk); + b.full = dfixed_div(b, c); + + c.full = dfixed_const(dmif_size); + b.full = dfixed_div(c, b); + + tmp = min(dfixed_trunc(a), dfixed_trunc(b)); + + b.full = dfixed_const(1000); + c.full = dfixed_const(wm->disp_clk); + b.full = dfixed_div(c, b); + c.full = dfixed_const(wm->bytes_per_pixel); + b.full = dfixed_mul(b, c); + + lb_fill_bw = min(tmp, dfixed_trunc(b)); + + a.full = dfixed_const(max_src_lines_per_dst_line * wm->src_width * wm->bytes_per_pixel); + b.full = dfixed_const(1000); + c.full = dfixed_const(lb_fill_bw); + b.full = dfixed_div(c, b); + a.full = dfixed_div(a, b); + line_fill_time = dfixed_trunc(a); + + if (line_fill_time < wm->active_time) + return latency; + else + return latency + (line_fill_time - wm->active_time); + +} + +static bool dce6_average_bandwidth_vs_dram_bandwidth_for_display(struct dce6_wm_params *wm) +{ + if (dce6_average_bandwidth(wm) <= + (dce6_dram_bandwidth_for_display(wm) / wm->num_heads)) + return true; + else + return false; +}; + +static bool dce6_average_bandwidth_vs_available_bandwidth(struct dce6_wm_params *wm) +{ + if (dce6_average_bandwidth(wm) <= + (dce6_available_bandwidth(wm) / wm->num_heads)) + return true; + else + return false; +}; + +static bool dce6_check_latency_hiding(struct dce6_wm_params *wm) +{ + u32 lb_partitions = wm->lb_size / wm->src_width; + u32 line_time = wm->active_time + wm->blank_time; + u32 latency_tolerant_lines; + u32 latency_hiding; + fixed20_12 a; + + a.full = dfixed_const(1); + if (wm->vsc.full > a.full) + latency_tolerant_lines = 1; + else { + if (lb_partitions <= (wm->vtaps + 1)) + latency_tolerant_lines = 1; + else + latency_tolerant_lines = 2; + } + + latency_hiding = (latency_tolerant_lines * line_time + wm->blank_time); + + if (dce6_latency_watermark(wm) <= latency_hiding) + return true; + else + return false; +} + +static void dce6_program_watermarks(struct radeon_device *rdev, + struct radeon_crtc *radeon_crtc, + u32 lb_size, u32 num_heads) +{ + struct drm_display_mode *mode = &radeon_crtc->base.mode; + struct dce6_wm_params wm; + u32 pixel_period; + u32 line_time = 0; + u32 latency_watermark_a = 0, latency_watermark_b = 0; + u32 priority_a_mark = 0, priority_b_mark = 0; + u32 priority_a_cnt = PRIORITY_OFF; + u32 priority_b_cnt = PRIORITY_OFF; + u32 tmp, arb_control3; + fixed20_12 a, b, c; + + if (radeon_crtc->base.enabled && num_heads && mode) { + pixel_period = 1000000 / (u32)mode->clock; + line_time = min((u32)mode->crtc_htotal * pixel_period, (u32)65535); + priority_a_cnt = 0; + priority_b_cnt = 0; + + wm.yclk = rdev->pm.current_mclk * 10; + wm.sclk = rdev->pm.current_sclk * 10; + wm.disp_clk = mode->clock; + wm.src_width = mode->crtc_hdisplay; + wm.active_time = mode->crtc_hdisplay * pixel_period; + wm.blank_time = line_time - wm.active_time; + wm.interlaced = false; + if (mode->flags & DRM_MODE_FLAG_INTERLACE) + wm.interlaced = true; + wm.vsc = radeon_crtc->vsc; + wm.vtaps = 1; + if (radeon_crtc->rmx_type != RMX_OFF) + wm.vtaps = 2; + wm.bytes_per_pixel = 4; /* XXX: get this from fb config */ + wm.lb_size = lb_size; + if (rdev->family == CHIP_ARUBA) + wm.dram_channels = evergreen_get_number_of_dram_channels(rdev); + else + wm.dram_channels = si_get_number_of_dram_channels(rdev); + wm.num_heads = num_heads; + + /* set for high clocks */ + latency_watermark_a = min(dce6_latency_watermark(&wm), (u32)65535); + /* set for low clocks */ + /* wm.yclk = low clk; wm.sclk = low clk */ + latency_watermark_b = min(dce6_latency_watermark(&wm), (u32)65535); + + /* possibly force display priority to high */ + /* should really do this at mode validation time... */ + if (!dce6_average_bandwidth_vs_dram_bandwidth_for_display(&wm) || + !dce6_average_bandwidth_vs_available_bandwidth(&wm) || + !dce6_check_latency_hiding(&wm) || + (rdev->disp_priority == 2)) { + DRM_DEBUG_KMS("force priority to high\n"); + priority_a_cnt |= PRIORITY_ALWAYS_ON; + priority_b_cnt |= PRIORITY_ALWAYS_ON; + } + + a.full = dfixed_const(1000); + b.full = dfixed_const(mode->clock); + b.full = dfixed_div(b, a); + c.full = dfixed_const(latency_watermark_a); + c.full = dfixed_mul(c, b); + c.full = dfixed_mul(c, radeon_crtc->hsc); + c.full = dfixed_div(c, a); + a.full = dfixed_const(16); + c.full = dfixed_div(c, a); + priority_a_mark = dfixed_trunc(c); + priority_a_cnt |= priority_a_mark & PRIORITY_MARK_MASK; + + a.full = dfixed_const(1000); + b.full = dfixed_const(mode->clock); + b.full = dfixed_div(b, a); + c.full = dfixed_const(latency_watermark_b); + c.full = dfixed_mul(c, b); + c.full = dfixed_mul(c, radeon_crtc->hsc); + c.full = dfixed_div(c, a); + a.full = dfixed_const(16); + c.full = dfixed_div(c, a); + priority_b_mark = dfixed_trunc(c); + priority_b_cnt |= priority_b_mark & PRIORITY_MARK_MASK; + } + + /* select wm A */ + arb_control3 = RREG32(DPG_PIPE_ARBITRATION_CONTROL3 + radeon_crtc->crtc_offset); + tmp = arb_control3; + tmp &= ~LATENCY_WATERMARK_MASK(3); + tmp |= LATENCY_WATERMARK_MASK(1); + WREG32(DPG_PIPE_ARBITRATION_CONTROL3 + radeon_crtc->crtc_offset, tmp); + WREG32(DPG_PIPE_LATENCY_CONTROL + radeon_crtc->crtc_offset, + (LATENCY_LOW_WATERMARK(latency_watermark_a) | + LATENCY_HIGH_WATERMARK(line_time))); + /* select wm B */ + tmp = RREG32(DPG_PIPE_ARBITRATION_CONTROL3 + radeon_crtc->crtc_offset); + tmp &= ~LATENCY_WATERMARK_MASK(3); + tmp |= LATENCY_WATERMARK_MASK(2); + WREG32(DPG_PIPE_ARBITRATION_CONTROL3 + radeon_crtc->crtc_offset, tmp); + WREG32(DPG_PIPE_LATENCY_CONTROL + radeon_crtc->crtc_offset, + (LATENCY_LOW_WATERMARK(latency_watermark_b) | + LATENCY_HIGH_WATERMARK(line_time))); + /* restore original selection */ + WREG32(DPG_PIPE_ARBITRATION_CONTROL3 + radeon_crtc->crtc_offset, arb_control3); + + /* write the priority marks */ + WREG32(PRIORITY_A_CNT + radeon_crtc->crtc_offset, priority_a_cnt); + WREG32(PRIORITY_B_CNT + radeon_crtc->crtc_offset, priority_b_cnt); + +} + +void dce6_bandwidth_update(struct radeon_device *rdev) +{ + struct drm_display_mode *mode0 = NULL; + struct drm_display_mode *mode1 = NULL; + u32 num_heads = 0, lb_size; + int i; + + radeon_update_display_priority(rdev); + + for (i = 0; i < rdev->num_crtc; i++) { + if (rdev->mode_info.crtcs[i]->base.enabled) + num_heads++; + } + for (i = 0; i < rdev->num_crtc; i += 2) { + mode0 = &rdev->mode_info.crtcs[i]->base.mode; + mode1 = &rdev->mode_info.crtcs[i+1]->base.mode; + lb_size = dce6_line_buffer_adjust(rdev, rdev->mode_info.crtcs[i], mode0, mode1); + dce6_program_watermarks(rdev, rdev->mode_info.crtcs[i], lb_size, num_heads); + lb_size = dce6_line_buffer_adjust(rdev, rdev->mode_info.crtcs[i+1], mode1, mode0); + dce6_program_watermarks(rdev, rdev->mode_info.crtcs[i+1], lb_size, num_heads); + } +} + +/* + * Core functions + */ +static void si_tiling_mode_table_init(struct radeon_device *rdev) +{ + const u32 num_tile_mode_states = 32; + u32 reg_offset, gb_tile_moden, split_equal_to_row_size; + + switch (rdev->config.si.mem_row_size_in_kb) { + case 1: + split_equal_to_row_size = ADDR_SURF_TILE_SPLIT_1KB; + break; + case 2: + default: + split_equal_to_row_size = ADDR_SURF_TILE_SPLIT_2KB; + break; + case 4: + split_equal_to_row_size = ADDR_SURF_TILE_SPLIT_4KB; + break; + } + + if ((rdev->family == CHIP_TAHITI) || + (rdev->family == CHIP_PITCAIRN)) { + for (reg_offset = 0; reg_offset < num_tile_mode_states; reg_offset++) { + switch (reg_offset) { + case 0: /* non-AA compressed depth or any compressed stencil */ + gb_tile_moden = (ARRAY_MODE(ARRAY_2D_TILED_THIN1) | + MICRO_TILE_MODE(ADDR_SURF_DEPTH_MICRO_TILING) | + PIPE_CONFIG(ADDR_SURF_P8_32x32_8x16) | + TILE_SPLIT(ADDR_SURF_TILE_SPLIT_64B) | + NUM_BANKS(ADDR_SURF_16_BANK) | + BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) | + BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_4) | + MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_2)); + break; + case 1: /* 2xAA/4xAA compressed depth only */ + gb_tile_moden = (ARRAY_MODE(ARRAY_2D_TILED_THIN1) | + MICRO_TILE_MODE(ADDR_SURF_DEPTH_MICRO_TILING) | + PIPE_CONFIG(ADDR_SURF_P8_32x32_8x16) | + TILE_SPLIT(ADDR_SURF_TILE_SPLIT_128B) | + NUM_BANKS(ADDR_SURF_16_BANK) | + BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) | + BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_4) | + MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_2)); + break; + case 2: /* 8xAA compressed depth only */ + gb_tile_moden = (ARRAY_MODE(ARRAY_2D_TILED_THIN1) | + MICRO_TILE_MODE(ADDR_SURF_DEPTH_MICRO_TILING) | + PIPE_CONFIG(ADDR_SURF_P8_32x32_8x16) | + TILE_SPLIT(ADDR_SURF_TILE_SPLIT_256B) | + NUM_BANKS(ADDR_SURF_16_BANK) | + BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) | + BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_4) | + MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_2)); + break; + case 3: /* 2xAA/4xAA compressed depth with stencil (for depth buffer) */ + gb_tile_moden = (ARRAY_MODE(ARRAY_2D_TILED_THIN1) | + MICRO_TILE_MODE(ADDR_SURF_DEPTH_MICRO_TILING) | + PIPE_CONFIG(ADDR_SURF_P8_32x32_8x16) | + TILE_SPLIT(ADDR_SURF_TILE_SPLIT_128B) | + NUM_BANKS(ADDR_SURF_16_BANK) | + BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) | + BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_4) | + MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_2)); + break; + case 4: /* Maps w/ a dimension less than the 2D macro-tile dimensions (for mipmapped depth textures) */ + gb_tile_moden = (ARRAY_MODE(ARRAY_1D_TILED_THIN1) | + MICRO_TILE_MODE(ADDR_SURF_DEPTH_MICRO_TILING) | + PIPE_CONFIG(ADDR_SURF_P8_32x32_8x16) | + TILE_SPLIT(ADDR_SURF_TILE_SPLIT_64B) | + NUM_BANKS(ADDR_SURF_16_BANK) | + BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) | + BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_2) | + MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_2)); + break; + case 5: /* Uncompressed 16bpp depth - and stencil buffer allocated with it */ + gb_tile_moden = (ARRAY_MODE(ARRAY_2D_TILED_THIN1) | + MICRO_TILE_MODE(ADDR_SURF_DEPTH_MICRO_TILING) | + PIPE_CONFIG(ADDR_SURF_P8_32x32_8x16) | + TILE_SPLIT(split_equal_to_row_size) | + NUM_BANKS(ADDR_SURF_16_BANK) | + BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) | + BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_2) | + MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_2)); + break; + case 6: /* Uncompressed 32bpp depth - and stencil buffer allocated with it */ + gb_tile_moden = (ARRAY_MODE(ARRAY_2D_TILED_THIN1) | + MICRO_TILE_MODE(ADDR_SURF_DEPTH_MICRO_TILING) | + PIPE_CONFIG(ADDR_SURF_P8_32x32_8x16) | + TILE_SPLIT(split_equal_to_row_size) | + NUM_BANKS(ADDR_SURF_16_BANK) | + BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) | + BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_1) | + MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_1)); + break; + case 7: /* Uncompressed 8bpp stencil without depth (drivers typically do not use) */ + gb_tile_moden = (ARRAY_MODE(ARRAY_2D_TILED_THIN1) | + MICRO_TILE_MODE(ADDR_SURF_DEPTH_MICRO_TILING) | + PIPE_CONFIG(ADDR_SURF_P8_32x32_8x16) | + TILE_SPLIT(split_equal_to_row_size) | + NUM_BANKS(ADDR_SURF_16_BANK) | + BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) | + BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_4) | + MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_2)); + break; + case 8: /* 1D and 1D Array Surfaces */ + gb_tile_moden = (ARRAY_MODE(ARRAY_LINEAR_ALIGNED) | + MICRO_TILE_MODE(ADDR_SURF_DISPLAY_MICRO_TILING) | + PIPE_CONFIG(ADDR_SURF_P8_32x32_8x16) | + TILE_SPLIT(ADDR_SURF_TILE_SPLIT_64B) | + NUM_BANKS(ADDR_SURF_16_BANK) | + BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) | + BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_2) | + MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_2)); + break; + case 9: /* Displayable maps. */ + gb_tile_moden = (ARRAY_MODE(ARRAY_1D_TILED_THIN1) | + MICRO_TILE_MODE(ADDR_SURF_DISPLAY_MICRO_TILING) | + PIPE_CONFIG(ADDR_SURF_P8_32x32_8x16) | + TILE_SPLIT(ADDR_SURF_TILE_SPLIT_64B) | + NUM_BANKS(ADDR_SURF_16_BANK) | + BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) | + BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_2) | + MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_2)); + break; + case 10: /* Display 8bpp. */ + gb_tile_moden = (ARRAY_MODE(ARRAY_2D_TILED_THIN1) | + MICRO_TILE_MODE(ADDR_SURF_DISPLAY_MICRO_TILING) | + PIPE_CONFIG(ADDR_SURF_P8_32x32_8x16) | + TILE_SPLIT(ADDR_SURF_TILE_SPLIT_256B) | + NUM_BANKS(ADDR_SURF_16_BANK) | + BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) | + BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_4) | + MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_2)); + break; + case 11: /* Display 16bpp. */ + gb_tile_moden = (ARRAY_MODE(ARRAY_2D_TILED_THIN1) | + MICRO_TILE_MODE(ADDR_SURF_DISPLAY_MICRO_TILING) | + PIPE_CONFIG(ADDR_SURF_P8_32x32_8x16) | + TILE_SPLIT(ADDR_SURF_TILE_SPLIT_256B) | + NUM_BANKS(ADDR_SURF_16_BANK) | + BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) | + BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_2) | + MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_2)); + break; + case 12: /* Display 32bpp. */ + gb_tile_moden = (ARRAY_MODE(ARRAY_2D_TILED_THIN1) | + MICRO_TILE_MODE(ADDR_SURF_DISPLAY_MICRO_TILING) | + PIPE_CONFIG(ADDR_SURF_P8_32x32_8x16) | + TILE_SPLIT(ADDR_SURF_TILE_SPLIT_512B) | + NUM_BANKS(ADDR_SURF_16_BANK) | + BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) | + BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_1) | + MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_1)); + break; + case 13: /* Thin. */ + gb_tile_moden = (ARRAY_MODE(ARRAY_1D_TILED_THIN1) | + MICRO_TILE_MODE(ADDR_SURF_THIN_MICRO_TILING) | + PIPE_CONFIG(ADDR_SURF_P8_32x32_8x16) | + TILE_SPLIT(ADDR_SURF_TILE_SPLIT_64B) | + NUM_BANKS(ADDR_SURF_16_BANK) | + BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) | + BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_2) | + MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_2)); + break; + case 14: /* Thin 8 bpp. */ + gb_tile_moden = (ARRAY_MODE(ARRAY_2D_TILED_THIN1) | + MICRO_TILE_MODE(ADDR_SURF_THIN_MICRO_TILING) | + PIPE_CONFIG(ADDR_SURF_P8_32x32_8x16) | + TILE_SPLIT(ADDR_SURF_TILE_SPLIT_256B) | + NUM_BANKS(ADDR_SURF_16_BANK) | + BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) | + BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_4) | + MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_1)); + break; + case 15: /* Thin 16 bpp. */ + gb_tile_moden = (ARRAY_MODE(ARRAY_2D_TILED_THIN1) | + MICRO_TILE_MODE(ADDR_SURF_THIN_MICRO_TILING) | + PIPE_CONFIG(ADDR_SURF_P8_32x32_8x16) | + TILE_SPLIT(ADDR_SURF_TILE_SPLIT_256B) | + NUM_BANKS(ADDR_SURF_16_BANK) | + BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) | + BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_2) | + MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_1)); + break; + case 16: /* Thin 32 bpp. */ + gb_tile_moden = (ARRAY_MODE(ARRAY_2D_TILED_THIN1) | + MICRO_TILE_MODE(ADDR_SURF_THIN_MICRO_TILING) | + PIPE_CONFIG(ADDR_SURF_P8_32x32_8x16) | + TILE_SPLIT(ADDR_SURF_TILE_SPLIT_512B) | + NUM_BANKS(ADDR_SURF_16_BANK) | + BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) | + BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_1) | + MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_1)); + break; + case 17: /* Thin 64 bpp. */ + gb_tile_moden = (ARRAY_MODE(ARRAY_2D_TILED_THIN1) | + MICRO_TILE_MODE(ADDR_SURF_THIN_MICRO_TILING) | + PIPE_CONFIG(ADDR_SURF_P8_32x32_8x16) | + TILE_SPLIT(split_equal_to_row_size) | + NUM_BANKS(ADDR_SURF_16_BANK) | + BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) | + BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_1) | + MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_1)); + break; + case 21: /* 8 bpp PRT. */ + gb_tile_moden = (ARRAY_MODE(ARRAY_2D_TILED_THIN1) | + MICRO_TILE_MODE(ADDR_SURF_THIN_MICRO_TILING) | + PIPE_CONFIG(ADDR_SURF_P8_32x32_8x16) | + TILE_SPLIT(ADDR_SURF_TILE_SPLIT_256B) | + NUM_BANKS(ADDR_SURF_16_BANK) | + BANK_WIDTH(ADDR_SURF_BANK_WIDTH_2) | + BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_4) | + MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_2)); + break; + case 22: /* 16 bpp PRT */ + gb_tile_moden = (ARRAY_MODE(ARRAY_2D_TILED_THIN1) | + MICRO_TILE_MODE(ADDR_SURF_THIN_MICRO_TILING) | + PIPE_CONFIG(ADDR_SURF_P8_32x32_8x16) | + TILE_SPLIT(ADDR_SURF_TILE_SPLIT_256B) | + NUM_BANKS(ADDR_SURF_16_BANK) | + BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) | + BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_4) | + MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_4)); + break; + case 23: /* 32 bpp PRT */ + gb_tile_moden = (ARRAY_MODE(ARRAY_2D_TILED_THIN1) | + MICRO_TILE_MODE(ADDR_SURF_THIN_MICRO_TILING) | + PIPE_CONFIG(ADDR_SURF_P8_32x32_8x16) | + TILE_SPLIT(ADDR_SURF_TILE_SPLIT_256B) | + NUM_BANKS(ADDR_SURF_16_BANK) | + BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) | + BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_2) | + MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_2)); + break; + case 24: /* 64 bpp PRT */ + gb_tile_moden = (ARRAY_MODE(ARRAY_2D_TILED_THIN1) | + MICRO_TILE_MODE(ADDR_SURF_THIN_MICRO_TILING) | + PIPE_CONFIG(ADDR_SURF_P8_32x32_8x16) | + TILE_SPLIT(ADDR_SURF_TILE_SPLIT_512B) | + NUM_BANKS(ADDR_SURF_16_BANK) | + BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) | + BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_1) | + MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_2)); + break; + case 25: /* 128 bpp PRT */ + gb_tile_moden = (ARRAY_MODE(ARRAY_2D_TILED_THIN1) | + MICRO_TILE_MODE(ADDR_SURF_THIN_MICRO_TILING) | + PIPE_CONFIG(ADDR_SURF_P8_32x32_8x16) | + TILE_SPLIT(ADDR_SURF_TILE_SPLIT_1KB) | + NUM_BANKS(ADDR_SURF_8_BANK) | + BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) | + BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_1) | + MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_1)); + break; + default: + gb_tile_moden = 0; + break; + } + WREG32(GB_TILE_MODE0 + (reg_offset * 4), gb_tile_moden); + } + } else if (rdev->family == CHIP_VERDE) { + for (reg_offset = 0; reg_offset < num_tile_mode_states; reg_offset++) { + switch (reg_offset) { + case 0: /* non-AA compressed depth or any compressed stencil */ + gb_tile_moden = (ARRAY_MODE(ARRAY_2D_TILED_THIN1) | + MICRO_TILE_MODE(ADDR_SURF_DEPTH_MICRO_TILING) | + PIPE_CONFIG(ADDR_SURF_P4_8x16) | + TILE_SPLIT(ADDR_SURF_TILE_SPLIT_64B) | + NUM_BANKS(ADDR_SURF_16_BANK) | + BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) | + BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_4) | + MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_4)); + break; + case 1: /* 2xAA/4xAA compressed depth only */ + gb_tile_moden = (ARRAY_MODE(ARRAY_2D_TILED_THIN1) | + MICRO_TILE_MODE(ADDR_SURF_DEPTH_MICRO_TILING) | + PIPE_CONFIG(ADDR_SURF_P4_8x16) | + TILE_SPLIT(ADDR_SURF_TILE_SPLIT_128B) | + NUM_BANKS(ADDR_SURF_16_BANK) | + BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) | + BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_4) | + MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_4)); + break; + case 2: /* 8xAA compressed depth only */ + gb_tile_moden = (ARRAY_MODE(ARRAY_2D_TILED_THIN1) | + MICRO_TILE_MODE(ADDR_SURF_DEPTH_MICRO_TILING) | + PIPE_CONFIG(ADDR_SURF_P4_8x16) | + TILE_SPLIT(ADDR_SURF_TILE_SPLIT_256B) | + NUM_BANKS(ADDR_SURF_16_BANK) | + BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) | + BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_4) | + MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_4)); + break; + case 3: /* 2xAA/4xAA compressed depth with stencil (for depth buffer) */ + gb_tile_moden = (ARRAY_MODE(ARRAY_2D_TILED_THIN1) | + MICRO_TILE_MODE(ADDR_SURF_DEPTH_MICRO_TILING) | + PIPE_CONFIG(ADDR_SURF_P4_8x16) | + TILE_SPLIT(ADDR_SURF_TILE_SPLIT_128B) | + NUM_BANKS(ADDR_SURF_16_BANK) | + BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) | + BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_4) | + MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_4)); + break; + case 4: /* Maps w/ a dimension less than the 2D macro-tile dimensions (for mipmapped depth textures) */ + gb_tile_moden = (ARRAY_MODE(ARRAY_1D_TILED_THIN1) | + MICRO_TILE_MODE(ADDR_SURF_DEPTH_MICRO_TILING) | + PIPE_CONFIG(ADDR_SURF_P4_8x16) | + TILE_SPLIT(ADDR_SURF_TILE_SPLIT_64B) | + NUM_BANKS(ADDR_SURF_16_BANK) | + BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) | + BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_2) | + MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_2)); + break; + case 5: /* Uncompressed 16bpp depth - and stencil buffer allocated with it */ + gb_tile_moden = (ARRAY_MODE(ARRAY_2D_TILED_THIN1) | + MICRO_TILE_MODE(ADDR_SURF_DEPTH_MICRO_TILING) | + PIPE_CONFIG(ADDR_SURF_P4_8x16) | + TILE_SPLIT(split_equal_to_row_size) | + NUM_BANKS(ADDR_SURF_16_BANK) | + BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) | + BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_2) | + MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_2)); + break; + case 6: /* Uncompressed 32bpp depth - and stencil buffer allocated with it */ + gb_tile_moden = (ARRAY_MODE(ARRAY_2D_TILED_THIN1) | + MICRO_TILE_MODE(ADDR_SURF_DEPTH_MICRO_TILING) | + PIPE_CONFIG(ADDR_SURF_P4_8x16) | + TILE_SPLIT(split_equal_to_row_size) | + NUM_BANKS(ADDR_SURF_16_BANK) | + BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) | + BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_1) | + MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_2)); + break; + case 7: /* Uncompressed 8bpp stencil without depth (drivers typically do not use) */ + gb_tile_moden = (ARRAY_MODE(ARRAY_2D_TILED_THIN1) | + MICRO_TILE_MODE(ADDR_SURF_DEPTH_MICRO_TILING) | + PIPE_CONFIG(ADDR_SURF_P4_8x16) | + TILE_SPLIT(split_equal_to_row_size) | + NUM_BANKS(ADDR_SURF_16_BANK) | + BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) | + BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_4) | + MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_4)); + break; + case 8: /* 1D and 1D Array Surfaces */ + gb_tile_moden = (ARRAY_MODE(ARRAY_LINEAR_ALIGNED) | + MICRO_TILE_MODE(ADDR_SURF_DISPLAY_MICRO_TILING) | + PIPE_CONFIG(ADDR_SURF_P4_8x16) | + TILE_SPLIT(ADDR_SURF_TILE_SPLIT_64B) | + NUM_BANKS(ADDR_SURF_16_BANK) | + BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) | + BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_2) | + MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_2)); + break; + case 9: /* Displayable maps. */ + gb_tile_moden = (ARRAY_MODE(ARRAY_1D_TILED_THIN1) | + MICRO_TILE_MODE(ADDR_SURF_DISPLAY_MICRO_TILING) | + PIPE_CONFIG(ADDR_SURF_P4_8x16) | + TILE_SPLIT(ADDR_SURF_TILE_SPLIT_64B) | + NUM_BANKS(ADDR_SURF_16_BANK) | + BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) | + BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_2) | + MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_2)); + break; + case 10: /* Display 8bpp. */ + gb_tile_moden = (ARRAY_MODE(ARRAY_2D_TILED_THIN1) | + MICRO_TILE_MODE(ADDR_SURF_DISPLAY_MICRO_TILING) | + PIPE_CONFIG(ADDR_SURF_P4_8x16) | + TILE_SPLIT(ADDR_SURF_TILE_SPLIT_256B) | + NUM_BANKS(ADDR_SURF_16_BANK) | + BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) | + BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_4) | + MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_4)); + break; + case 11: /* Display 16bpp. */ + gb_tile_moden = (ARRAY_MODE(ARRAY_2D_TILED_THIN1) | + MICRO_TILE_MODE(ADDR_SURF_DISPLAY_MICRO_TILING) | + PIPE_CONFIG(ADDR_SURF_P4_8x16) | + TILE_SPLIT(ADDR_SURF_TILE_SPLIT_256B) | + NUM_BANKS(ADDR_SURF_16_BANK) | + BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) | + BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_2) | + MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_2)); + break; + case 12: /* Display 32bpp. */ + gb_tile_moden = (ARRAY_MODE(ARRAY_2D_TILED_THIN1) | + MICRO_TILE_MODE(ADDR_SURF_DISPLAY_MICRO_TILING) | + PIPE_CONFIG(ADDR_SURF_P4_8x16) | + TILE_SPLIT(ADDR_SURF_TILE_SPLIT_512B) | + NUM_BANKS(ADDR_SURF_16_BANK) | + BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) | + BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_1) | + MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_2)); + break; + case 13: /* Thin. */ + gb_tile_moden = (ARRAY_MODE(ARRAY_1D_TILED_THIN1) | + MICRO_TILE_MODE(ADDR_SURF_THIN_MICRO_TILING) | + PIPE_CONFIG(ADDR_SURF_P4_8x16) | + TILE_SPLIT(ADDR_SURF_TILE_SPLIT_64B) | + NUM_BANKS(ADDR_SURF_16_BANK) | + BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) | + BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_2) | + MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_2)); + break; + case 14: /* Thin 8 bpp. */ + gb_tile_moden = (ARRAY_MODE(ARRAY_2D_TILED_THIN1) | + MICRO_TILE_MODE(ADDR_SURF_THIN_MICRO_TILING) | + PIPE_CONFIG(ADDR_SURF_P4_8x16) | + TILE_SPLIT(ADDR_SURF_TILE_SPLIT_256B) | + NUM_BANKS(ADDR_SURF_16_BANK) | + BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) | + BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_4) | + MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_2)); + break; + case 15: /* Thin 16 bpp. */ + gb_tile_moden = (ARRAY_MODE(ARRAY_2D_TILED_THIN1) | + MICRO_TILE_MODE(ADDR_SURF_THIN_MICRO_TILING) | + PIPE_CONFIG(ADDR_SURF_P4_8x16) | + TILE_SPLIT(ADDR_SURF_TILE_SPLIT_256B) | + NUM_BANKS(ADDR_SURF_16_BANK) | + BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) | + BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_2) | + MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_2)); + break; + case 16: /* Thin 32 bpp. */ + gb_tile_moden = (ARRAY_MODE(ARRAY_2D_TILED_THIN1) | + MICRO_TILE_MODE(ADDR_SURF_THIN_MICRO_TILING) | + PIPE_CONFIG(ADDR_SURF_P4_8x16) | + TILE_SPLIT(ADDR_SURF_TILE_SPLIT_512B) | + NUM_BANKS(ADDR_SURF_16_BANK) | + BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) | + BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_1) | + MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_2)); + break; + case 17: /* Thin 64 bpp. */ + gb_tile_moden = (ARRAY_MODE(ARRAY_2D_TILED_THIN1) | + MICRO_TILE_MODE(ADDR_SURF_THIN_MICRO_TILING) | + PIPE_CONFIG(ADDR_SURF_P4_8x16) | + TILE_SPLIT(split_equal_to_row_size) | + NUM_BANKS(ADDR_SURF_16_BANK) | + BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) | + BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_1) | + MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_2)); + break; + case 21: /* 8 bpp PRT. */ + gb_tile_moden = (ARRAY_MODE(ARRAY_2D_TILED_THIN1) | + MICRO_TILE_MODE(ADDR_SURF_THIN_MICRO_TILING) | + PIPE_CONFIG(ADDR_SURF_P8_32x32_8x16) | + TILE_SPLIT(ADDR_SURF_TILE_SPLIT_256B) | + NUM_BANKS(ADDR_SURF_16_BANK) | + BANK_WIDTH(ADDR_SURF_BANK_WIDTH_2) | + BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_4) | + MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_2)); + break; + case 22: /* 16 bpp PRT */ + gb_tile_moden = (ARRAY_MODE(ARRAY_2D_TILED_THIN1) | + MICRO_TILE_MODE(ADDR_SURF_THIN_MICRO_TILING) | + PIPE_CONFIG(ADDR_SURF_P8_32x32_8x16) | + TILE_SPLIT(ADDR_SURF_TILE_SPLIT_256B) | + NUM_BANKS(ADDR_SURF_16_BANK) | + BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) | + BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_4) | + MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_4)); + break; + case 23: /* 32 bpp PRT */ + gb_tile_moden = (ARRAY_MODE(ARRAY_2D_TILED_THIN1) | + MICRO_TILE_MODE(ADDR_SURF_THIN_MICRO_TILING) | + PIPE_CONFIG(ADDR_SURF_P8_32x32_8x16) | + TILE_SPLIT(ADDR_SURF_TILE_SPLIT_256B) | + NUM_BANKS(ADDR_SURF_16_BANK) | + BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) | + BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_2) | + MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_2)); + break; + case 24: /* 64 bpp PRT */ + gb_tile_moden = (ARRAY_MODE(ARRAY_2D_TILED_THIN1) | + MICRO_TILE_MODE(ADDR_SURF_THIN_MICRO_TILING) | + PIPE_CONFIG(ADDR_SURF_P8_32x32_8x16) | + TILE_SPLIT(ADDR_SURF_TILE_SPLIT_512B) | + NUM_BANKS(ADDR_SURF_16_BANK) | + BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) | + BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_1) | + MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_2)); + break; + case 25: /* 128 bpp PRT */ + gb_tile_moden = (ARRAY_MODE(ARRAY_2D_TILED_THIN1) | + MICRO_TILE_MODE(ADDR_SURF_THIN_MICRO_TILING) | + PIPE_CONFIG(ADDR_SURF_P8_32x32_8x16) | + TILE_SPLIT(ADDR_SURF_TILE_SPLIT_1KB) | + NUM_BANKS(ADDR_SURF_8_BANK) | + BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) | + BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_1) | + MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_1)); + break; + default: + gb_tile_moden = 0; + break; + } + WREG32(GB_TILE_MODE0 + (reg_offset * 4), gb_tile_moden); + } + } else + DRM_ERROR("unknown asic: 0x%x\n", rdev->family); +} + +static void si_select_se_sh(struct radeon_device *rdev, + u32 se_num, u32 sh_num) +{ + u32 data = INSTANCE_BROADCAST_WRITES; + + if ((se_num == 0xffffffff) && (sh_num == 0xffffffff)) + data = SH_BROADCAST_WRITES | SE_BROADCAST_WRITES; + else if (se_num == 0xffffffff) + data |= SE_BROADCAST_WRITES | SH_INDEX(sh_num); + else if (sh_num == 0xffffffff) + data |= SH_BROADCAST_WRITES | SE_INDEX(se_num); + else + data |= SH_INDEX(sh_num) | SE_INDEX(se_num); + WREG32(GRBM_GFX_INDEX, data); +} + +static u32 si_create_bitmask(u32 bit_width) +{ + u32 i, mask = 0; + + for (i = 0; i < bit_width; i++) { + mask <<= 1; + mask |= 1; + } + return mask; +} + +static u32 si_get_cu_enabled(struct radeon_device *rdev, u32 cu_per_sh) +{ + u32 data, mask; + + data = RREG32(CC_GC_SHADER_ARRAY_CONFIG); + if (data & 1) + data &= INACTIVE_CUS_MASK; + else + data = 0; + data |= RREG32(GC_USER_SHADER_ARRAY_CONFIG); + + data >>= INACTIVE_CUS_SHIFT; + + mask = si_create_bitmask(cu_per_sh); + + return ~data & mask; +} + +static void si_setup_spi(struct radeon_device *rdev, + u32 se_num, u32 sh_per_se, + u32 cu_per_sh) +{ + int i, j, k; + u32 data, mask, active_cu; + + for (i = 0; i < se_num; i++) { + for (j = 0; j < sh_per_se; j++) { + si_select_se_sh(rdev, i, j); + data = RREG32(SPI_STATIC_THREAD_MGMT_3); + active_cu = si_get_cu_enabled(rdev, cu_per_sh); + + mask = 1; + for (k = 0; k < 16; k++) { + mask <<= k; + if (active_cu & mask) { + data &= ~mask; + WREG32(SPI_STATIC_THREAD_MGMT_3, data); + break; + } + } + } + } + si_select_se_sh(rdev, 0xffffffff, 0xffffffff); +} + +static u32 si_get_rb_disabled(struct radeon_device *rdev, + u32 max_rb_num, u32 se_num, + u32 sh_per_se) +{ + u32 data, mask; + + data = RREG32(CC_RB_BACKEND_DISABLE); + if (data & 1) + data &= BACKEND_DISABLE_MASK; + else + data = 0; + data |= RREG32(GC_USER_RB_BACKEND_DISABLE); + + data >>= BACKEND_DISABLE_SHIFT; + + mask = si_create_bitmask(max_rb_num / se_num / sh_per_se); + + return data & mask; +} + +static void si_setup_rb(struct radeon_device *rdev, + u32 se_num, u32 sh_per_se, + u32 max_rb_num) +{ + int i, j; + u32 data, mask; + u32 disabled_rbs = 0; + u32 enabled_rbs = 0; + + for (i = 0; i < se_num; i++) { + for (j = 0; j < sh_per_se; j++) { + si_select_se_sh(rdev, i, j); + data = si_get_rb_disabled(rdev, max_rb_num, se_num, sh_per_se); + disabled_rbs |= data << ((i * sh_per_se + j) * TAHITI_RB_BITMAP_WIDTH_PER_SH); + } + } + si_select_se_sh(rdev, 0xffffffff, 0xffffffff); + + mask = 1; + for (i = 0; i < max_rb_num; i++) { + if (!(disabled_rbs & mask)) + enabled_rbs |= mask; + mask <<= 1; + } + + for (i = 0; i < se_num; i++) { + si_select_se_sh(rdev, i, 0xffffffff); + data = 0; + for (j = 0; j < sh_per_se; j++) { + switch (enabled_rbs & 3) { + case 1: + data |= (RASTER_CONFIG_RB_MAP_0 << (i * sh_per_se + j) * 2); + break; + case 2: + data |= (RASTER_CONFIG_RB_MAP_3 << (i * sh_per_se + j) * 2); + break; + case 3: + default: + data |= (RASTER_CONFIG_RB_MAP_2 << (i * sh_per_se + j) * 2); + break; + } + enabled_rbs >>= 2; + } + WREG32(PA_SC_RASTER_CONFIG, data); + } + si_select_se_sh(rdev, 0xffffffff, 0xffffffff); +} + +static void si_gpu_init(struct radeon_device *rdev) +{ + u32 gb_addr_config = 0; + u32 mc_shared_chmap, mc_arb_ramcfg; + u32 sx_debug_1; + u32 hdp_host_path_cntl; + u32 tmp; + int i, j; + + switch (rdev->family) { + case CHIP_TAHITI: + rdev->config.si.max_shader_engines = 2; + rdev->config.si.max_tile_pipes = 12; + rdev->config.si.max_cu_per_sh = 8; + rdev->config.si.max_sh_per_se = 2; + rdev->config.si.max_backends_per_se = 4; + rdev->config.si.max_texture_channel_caches = 12; + rdev->config.si.max_gprs = 256; + rdev->config.si.max_gs_threads = 32; + rdev->config.si.max_hw_contexts = 8; + + rdev->config.si.sc_prim_fifo_size_frontend = 0x20; + rdev->config.si.sc_prim_fifo_size_backend = 0x100; + rdev->config.si.sc_hiz_tile_fifo_size = 0x30; + rdev->config.si.sc_earlyz_tile_fifo_size = 0x130; + gb_addr_config = TAHITI_GB_ADDR_CONFIG_GOLDEN; + break; + case CHIP_PITCAIRN: + rdev->config.si.max_shader_engines = 2; + rdev->config.si.max_tile_pipes = 8; + rdev->config.si.max_cu_per_sh = 5; + rdev->config.si.max_sh_per_se = 2; + rdev->config.si.max_backends_per_se = 4; + rdev->config.si.max_texture_channel_caches = 8; + rdev->config.si.max_gprs = 256; + rdev->config.si.max_gs_threads = 32; + rdev->config.si.max_hw_contexts = 8; + + rdev->config.si.sc_prim_fifo_size_frontend = 0x20; + rdev->config.si.sc_prim_fifo_size_backend = 0x100; + rdev->config.si.sc_hiz_tile_fifo_size = 0x30; + rdev->config.si.sc_earlyz_tile_fifo_size = 0x130; + gb_addr_config = TAHITI_GB_ADDR_CONFIG_GOLDEN; + break; + case CHIP_VERDE: + default: + rdev->config.si.max_shader_engines = 1; + rdev->config.si.max_tile_pipes = 4; + rdev->config.si.max_cu_per_sh = 2; + rdev->config.si.max_sh_per_se = 2; + rdev->config.si.max_backends_per_se = 4; + rdev->config.si.max_texture_channel_caches = 4; + rdev->config.si.max_gprs = 256; + rdev->config.si.max_gs_threads = 32; + rdev->config.si.max_hw_contexts = 8; + + rdev->config.si.sc_prim_fifo_size_frontend = 0x20; + rdev->config.si.sc_prim_fifo_size_backend = 0x40; + rdev->config.si.sc_hiz_tile_fifo_size = 0x30; + rdev->config.si.sc_earlyz_tile_fifo_size = 0x130; + gb_addr_config = VERDE_GB_ADDR_CONFIG_GOLDEN; + break; + } + + /* Initialize HDP */ + for (i = 0, j = 0; i < 32; i++, j += 0x18) { + WREG32((0x2c14 + j), 0x00000000); + WREG32((0x2c18 + j), 0x00000000); + WREG32((0x2c1c + j), 0x00000000); + WREG32((0x2c20 + j), 0x00000000); + WREG32((0x2c24 + j), 0x00000000); + } + + WREG32(GRBM_CNTL, GRBM_READ_TIMEOUT(0xff)); + + evergreen_fix_pci_max_read_req_size(rdev); + + WREG32(BIF_FB_EN, FB_READ_EN | FB_WRITE_EN); + + mc_shared_chmap = RREG32(MC_SHARED_CHMAP); + mc_arb_ramcfg = RREG32(MC_ARB_RAMCFG); + + rdev->config.si.num_tile_pipes = rdev->config.si.max_tile_pipes; + rdev->config.si.mem_max_burst_length_bytes = 256; + tmp = (mc_arb_ramcfg & NOOFCOLS_MASK) >> NOOFCOLS_SHIFT; + rdev->config.si.mem_row_size_in_kb = (4 * (1 << (8 + tmp))) / 1024; + if (rdev->config.si.mem_row_size_in_kb > 4) + rdev->config.si.mem_row_size_in_kb = 4; + /* XXX use MC settings? */ + rdev->config.si.shader_engine_tile_size = 32; + rdev->config.si.num_gpus = 1; + rdev->config.si.multi_gpu_tile_size = 64; + + /* fix up row size */ + gb_addr_config &= ~ROW_SIZE_MASK; + switch (rdev->config.si.mem_row_size_in_kb) { + case 1: + default: + gb_addr_config |= ROW_SIZE(0); + break; + case 2: + gb_addr_config |= ROW_SIZE(1); + break; + case 4: + gb_addr_config |= ROW_SIZE(2); + break; + } + + /* setup tiling info dword. gb_addr_config is not adequate since it does + * not have bank info, so create a custom tiling dword. + * bits 3:0 num_pipes + * bits 7:4 num_banks + * bits 11:8 group_size + * bits 15:12 row_size + */ + rdev->config.si.tile_config = 0; + switch (rdev->config.si.num_tile_pipes) { + case 1: + rdev->config.si.tile_config |= (0 << 0); + break; + case 2: + rdev->config.si.tile_config |= (1 << 0); + break; + case 4: + rdev->config.si.tile_config |= (2 << 0); + break; + case 8: + default: + /* XXX what about 12? */ + rdev->config.si.tile_config |= (3 << 0); + break; + } + switch ((mc_arb_ramcfg & NOOFBANK_MASK) >> NOOFBANK_SHIFT) { + case 0: /* four banks */ + rdev->config.si.tile_config |= 0 << 4; + break; + case 1: /* eight banks */ + rdev->config.si.tile_config |= 1 << 4; + break; + case 2: /* sixteen banks */ + default: + rdev->config.si.tile_config |= 2 << 4; + break; + } + rdev->config.si.tile_config |= + ((gb_addr_config & PIPE_INTERLEAVE_SIZE_MASK) >> PIPE_INTERLEAVE_SIZE_SHIFT) << 8; + rdev->config.si.tile_config |= + ((gb_addr_config & ROW_SIZE_MASK) >> ROW_SIZE_SHIFT) << 12; + + WREG32(GB_ADDR_CONFIG, gb_addr_config); + WREG32(DMIF_ADDR_CONFIG, gb_addr_config); + WREG32(HDP_ADDR_CONFIG, gb_addr_config); + WREG32(DMA_TILING_CONFIG + DMA0_REGISTER_OFFSET, gb_addr_config); + WREG32(DMA_TILING_CONFIG + DMA1_REGISTER_OFFSET, gb_addr_config); + + si_tiling_mode_table_init(rdev); + + si_setup_rb(rdev, rdev->config.si.max_shader_engines, + rdev->config.si.max_sh_per_se, + rdev->config.si.max_backends_per_se); + + si_setup_spi(rdev, rdev->config.si.max_shader_engines, + rdev->config.si.max_sh_per_se, + rdev->config.si.max_cu_per_sh); + + + /* set HW defaults for 3D engine */ + WREG32(CP_QUEUE_THRESHOLDS, (ROQ_IB1_START(0x16) | + ROQ_IB2_START(0x2b))); + WREG32(CP_MEQ_THRESHOLDS, MEQ1_START(0x30) | MEQ2_START(0x60)); + + sx_debug_1 = RREG32(SX_DEBUG_1); + WREG32(SX_DEBUG_1, sx_debug_1); + + WREG32(SPI_CONFIG_CNTL_1, VTX_DONE_DELAY(4)); + + WREG32(PA_SC_FIFO_SIZE, (SC_FRONTEND_PRIM_FIFO_SIZE(rdev->config.si.sc_prim_fifo_size_frontend) | + SC_BACKEND_PRIM_FIFO_SIZE(rdev->config.si.sc_prim_fifo_size_backend) | + SC_HIZ_TILE_FIFO_SIZE(rdev->config.si.sc_hiz_tile_fifo_size) | + SC_EARLYZ_TILE_FIFO_SIZE(rdev->config.si.sc_earlyz_tile_fifo_size))); + + WREG32(VGT_NUM_INSTANCES, 1); + + WREG32(CP_PERFMON_CNTL, 0); + + WREG32(SQ_CONFIG, 0); + + WREG32(PA_SC_FORCE_EOV_MAX_CNTS, (FORCE_EOV_MAX_CLK_CNT(4095) | + FORCE_EOV_MAX_REZ_CNT(255))); + + WREG32(VGT_CACHE_INVALIDATION, CACHE_INVALIDATION(VC_AND_TC) | + AUTO_INVLD_EN(ES_AND_GS_AUTO)); + + WREG32(VGT_GS_VERTEX_REUSE, 16); + WREG32(PA_SC_LINE_STIPPLE_STATE, 0); + + WREG32(CB_PERFCOUNTER0_SELECT0, 0); + WREG32(CB_PERFCOUNTER0_SELECT1, 0); + WREG32(CB_PERFCOUNTER1_SELECT0, 0); + WREG32(CB_PERFCOUNTER1_SELECT1, 0); + WREG32(CB_PERFCOUNTER2_SELECT0, 0); + WREG32(CB_PERFCOUNTER2_SELECT1, 0); + WREG32(CB_PERFCOUNTER3_SELECT0, 0); + WREG32(CB_PERFCOUNTER3_SELECT1, 0); + + tmp = RREG32(HDP_MISC_CNTL); + tmp |= HDP_FLUSH_INVALIDATE_CACHE; + WREG32(HDP_MISC_CNTL, tmp); + + hdp_host_path_cntl = RREG32(HDP_HOST_PATH_CNTL); + WREG32(HDP_HOST_PATH_CNTL, hdp_host_path_cntl); + + WREG32(PA_CL_ENHANCE, CLIP_VTX_REORDER_ENA | NUM_CLIP_SEQ(3)); + + DRM_UDELAY(50); +} + +/* + * GPU scratch registers helpers function. + */ +static void si_scratch_init(struct radeon_device *rdev) +{ + int i; + + rdev->scratch.num_reg = 7; + rdev->scratch.reg_base = SCRATCH_REG0; + for (i = 0; i < rdev->scratch.num_reg; i++) { + rdev->scratch.free[i] = true; + rdev->scratch.reg[i] = rdev->scratch.reg_base + (i * 4); + } +} + +void si_fence_ring_emit(struct radeon_device *rdev, + struct radeon_fence *fence) +{ + struct radeon_ring *ring = &rdev->ring[fence->ring]; + u64 addr = rdev->fence_drv[fence->ring].gpu_addr; + + /* flush read cache over gart */ + radeon_ring_write(ring, PACKET3(PACKET3_SET_CONFIG_REG, 1)); + radeon_ring_write(ring, (CP_COHER_CNTL2 - PACKET3_SET_CONFIG_REG_START) >> 2); + radeon_ring_write(ring, 0); + radeon_ring_write(ring, PACKET3(PACKET3_SURFACE_SYNC, 3)); + radeon_ring_write(ring, PACKET3_TCL1_ACTION_ENA | + PACKET3_TC_ACTION_ENA | + PACKET3_SH_KCACHE_ACTION_ENA | + PACKET3_SH_ICACHE_ACTION_ENA); + radeon_ring_write(ring, 0xFFFFFFFF); + radeon_ring_write(ring, 0); + radeon_ring_write(ring, 10); /* poll interval */ + /* EVENT_WRITE_EOP - flush caches, send int */ + radeon_ring_write(ring, PACKET3(PACKET3_EVENT_WRITE_EOP, 4)); + radeon_ring_write(ring, EVENT_TYPE(CACHE_FLUSH_AND_INV_TS_EVENT) | EVENT_INDEX(5)); + radeon_ring_write(ring, addr & 0xffffffff); + radeon_ring_write(ring, (upper_32_bits(addr) & 0xff) | DATA_SEL(1) | INT_SEL(2)); + radeon_ring_write(ring, fence->seq); + radeon_ring_write(ring, 0); +} + +/* + * IB stuff + */ +void si_ring_ib_execute(struct radeon_device *rdev, struct radeon_ib *ib) +{ + struct radeon_ring *ring = &rdev->ring[ib->ring]; + u32 header; + + if (ib->is_const_ib) { + /* set switch buffer packet before const IB */ + radeon_ring_write(ring, PACKET3(PACKET3_SWITCH_BUFFER, 0)); + radeon_ring_write(ring, 0); + + header = PACKET3(PACKET3_INDIRECT_BUFFER_CONST, 2); + } else { + u32 next_rptr; + if (ring->rptr_save_reg) { + next_rptr = ring->wptr + 3 + 4 + 8; + radeon_ring_write(ring, PACKET3(PACKET3_SET_CONFIG_REG, 1)); + radeon_ring_write(ring, ((ring->rptr_save_reg - + PACKET3_SET_CONFIG_REG_START) >> 2)); + radeon_ring_write(ring, next_rptr); + } else if (rdev->wb.enabled) { + next_rptr = ring->wptr + 5 + 4 + 8; + radeon_ring_write(ring, PACKET3(PACKET3_WRITE_DATA, 3)); + radeon_ring_write(ring, (1 << 8)); + radeon_ring_write(ring, ring->next_rptr_gpu_addr & 0xfffffffc); + radeon_ring_write(ring, upper_32_bits(ring->next_rptr_gpu_addr) & 0xffffffff); + radeon_ring_write(ring, next_rptr); + } + + header = PACKET3(PACKET3_INDIRECT_BUFFER, 2); + } + + radeon_ring_write(ring, header); + radeon_ring_write(ring, +#ifdef __BIG_ENDIAN + (2 << 0) | +#endif + (ib->gpu_addr & 0xFFFFFFFC)); + radeon_ring_write(ring, upper_32_bits(ib->gpu_addr) & 0xFFFF); + radeon_ring_write(ring, ib->length_dw | + (ib->vm ? (ib->vm->id << 24) : 0)); + + if (!ib->is_const_ib) { + /* flush read cache over gart for this vmid */ + radeon_ring_write(ring, PACKET3(PACKET3_SET_CONFIG_REG, 1)); + radeon_ring_write(ring, (CP_COHER_CNTL2 - PACKET3_SET_CONFIG_REG_START) >> 2); + radeon_ring_write(ring, ib->vm ? ib->vm->id : 0); + radeon_ring_write(ring, PACKET3(PACKET3_SURFACE_SYNC, 3)); + radeon_ring_write(ring, PACKET3_TCL1_ACTION_ENA | + PACKET3_TC_ACTION_ENA | + PACKET3_SH_KCACHE_ACTION_ENA | + PACKET3_SH_ICACHE_ACTION_ENA); + radeon_ring_write(ring, 0xFFFFFFFF); + radeon_ring_write(ring, 0); + radeon_ring_write(ring, 10); /* poll interval */ + } +} + +/* + * CP. + */ +static void si_cp_enable(struct radeon_device *rdev, bool enable) +{ + if (enable) + WREG32(CP_ME_CNTL, 0); + else { + radeon_ttm_set_active_vram_size(rdev, rdev->mc.visible_vram_size); + WREG32(CP_ME_CNTL, (CP_ME_HALT | CP_PFP_HALT | CP_CE_HALT)); + WREG32(SCRATCH_UMSK, 0); + rdev->ring[RADEON_RING_TYPE_GFX_INDEX].ready = false; + rdev->ring[CAYMAN_RING_TYPE_CP1_INDEX].ready = false; + rdev->ring[CAYMAN_RING_TYPE_CP2_INDEX].ready = false; + } + DRM_UDELAY(50); +} + +static int si_cp_load_microcode(struct radeon_device *rdev) +{ + const __be32 *fw_data; + int i; + + if (!rdev->me_fw || !rdev->pfp_fw) + return -EINVAL; + + si_cp_enable(rdev, false); + + /* PFP */ + fw_data = (const __be32 *)rdev->pfp_fw->data; + WREG32(CP_PFP_UCODE_ADDR, 0); + for (i = 0; i < SI_PFP_UCODE_SIZE; i++) + WREG32(CP_PFP_UCODE_DATA, be32_to_cpup(fw_data++)); + WREG32(CP_PFP_UCODE_ADDR, 0); + + /* CE */ + fw_data = (const __be32 *)rdev->ce_fw->data; + WREG32(CP_CE_UCODE_ADDR, 0); + for (i = 0; i < SI_CE_UCODE_SIZE; i++) + WREG32(CP_CE_UCODE_DATA, be32_to_cpup(fw_data++)); + WREG32(CP_CE_UCODE_ADDR, 0); + + /* ME */ + fw_data = (const __be32 *)rdev->me_fw->data; + WREG32(CP_ME_RAM_WADDR, 0); + for (i = 0; i < SI_PM4_UCODE_SIZE; i++) + WREG32(CP_ME_RAM_DATA, be32_to_cpup(fw_data++)); + WREG32(CP_ME_RAM_WADDR, 0); + + WREG32(CP_PFP_UCODE_ADDR, 0); + WREG32(CP_CE_UCODE_ADDR, 0); + WREG32(CP_ME_RAM_WADDR, 0); + WREG32(CP_ME_RAM_RADDR, 0); + return 0; +} + +static int si_cp_start(struct radeon_device *rdev) +{ + struct radeon_ring *ring = &rdev->ring[RADEON_RING_TYPE_GFX_INDEX]; + int r, i; + + r = radeon_ring_lock(rdev, ring, 7 + 4); + if (r) { + DRM_ERROR("radeon: cp failed to lock ring (%d).\n", r); + return r; + } + /* init the CP */ + radeon_ring_write(ring, PACKET3(PACKET3_ME_INITIALIZE, 5)); + radeon_ring_write(ring, 0x1); + radeon_ring_write(ring, 0x0); + radeon_ring_write(ring, rdev->config.si.max_hw_contexts - 1); + radeon_ring_write(ring, PACKET3_ME_INITIALIZE_DEVICE_ID(1)); + radeon_ring_write(ring, 0); + radeon_ring_write(ring, 0); + + /* init the CE partitions */ + radeon_ring_write(ring, PACKET3(PACKET3_SET_BASE, 2)); + radeon_ring_write(ring, PACKET3_BASE_INDEX(CE_PARTITION_BASE)); + radeon_ring_write(ring, 0xc000); + radeon_ring_write(ring, 0xe000); + radeon_ring_unlock_commit(rdev, ring); + + si_cp_enable(rdev, true); + + r = radeon_ring_lock(rdev, ring, si_default_size + 10); + if (r) { + DRM_ERROR("radeon: cp failed to lock ring (%d).\n", r); + return r; + } + + /* setup clear context state */ + radeon_ring_write(ring, PACKET3(PACKET3_PREAMBLE_CNTL, 0)); + radeon_ring_write(ring, PACKET3_PREAMBLE_BEGIN_CLEAR_STATE); + + for (i = 0; i < si_default_size; i++) + radeon_ring_write(ring, si_default_state[i]); + + radeon_ring_write(ring, PACKET3(PACKET3_PREAMBLE_CNTL, 0)); + radeon_ring_write(ring, PACKET3_PREAMBLE_END_CLEAR_STATE); + + /* set clear context state */ + radeon_ring_write(ring, PACKET3(PACKET3_CLEAR_STATE, 0)); + radeon_ring_write(ring, 0); + + radeon_ring_write(ring, PACKET3(PACKET3_SET_CONTEXT_REG, 2)); + radeon_ring_write(ring, 0x00000316); + radeon_ring_write(ring, 0x0000000e); /* VGT_VERTEX_REUSE_BLOCK_CNTL */ + radeon_ring_write(ring, 0x00000010); /* VGT_OUT_DEALLOC_CNTL */ + + radeon_ring_unlock_commit(rdev, ring); + + for (i = RADEON_RING_TYPE_GFX_INDEX; i <= CAYMAN_RING_TYPE_CP2_INDEX; ++i) { + ring = &rdev->ring[i]; + r = radeon_ring_lock(rdev, ring, 2); + + /* clear the compute context state */ + radeon_ring_write(ring, PACKET3_COMPUTE(PACKET3_CLEAR_STATE, 0)); + radeon_ring_write(ring, 0); + + radeon_ring_unlock_commit(rdev, ring); + } + + return 0; +} + +static void si_cp_fini(struct radeon_device *rdev) +{ + struct radeon_ring *ring; + si_cp_enable(rdev, false); + + ring = &rdev->ring[RADEON_RING_TYPE_GFX_INDEX]; + radeon_ring_fini(rdev, ring); + radeon_scratch_free(rdev, ring->rptr_save_reg); + + ring = &rdev->ring[CAYMAN_RING_TYPE_CP1_INDEX]; + radeon_ring_fini(rdev, ring); + radeon_scratch_free(rdev, ring->rptr_save_reg); + + ring = &rdev->ring[CAYMAN_RING_TYPE_CP2_INDEX]; + radeon_ring_fini(rdev, ring); + radeon_scratch_free(rdev, ring->rptr_save_reg); +} + +static int si_cp_resume(struct radeon_device *rdev) +{ + struct radeon_ring *ring; + u32 tmp; + u32 rb_bufsz; + int r; + + /* Reset cp; if cp is reset, then PA, SH, VGT also need to be reset */ + WREG32(GRBM_SOFT_RESET, (SOFT_RESET_CP | + SOFT_RESET_PA | + SOFT_RESET_VGT | + SOFT_RESET_SPI | + SOFT_RESET_SX)); + RREG32(GRBM_SOFT_RESET); + DRM_MDELAY(15); + WREG32(GRBM_SOFT_RESET, 0); + RREG32(GRBM_SOFT_RESET); + + WREG32(CP_SEM_WAIT_TIMER, 0x0); + WREG32(CP_SEM_INCOMPLETE_TIMER_CNTL, 0x0); + + /* Set the write pointer delay */ + WREG32(CP_RB_WPTR_DELAY, 0); + + WREG32(CP_DEBUG, 0); + WREG32(SCRATCH_ADDR, ((rdev->wb.gpu_addr + RADEON_WB_SCRATCH_OFFSET) >> 8) & 0xFFFFFFFF); + + /* ring 0 - compute and gfx */ + /* Set ring buffer size */ + ring = &rdev->ring[RADEON_RING_TYPE_GFX_INDEX]; + rb_bufsz = drm_order(ring->ring_size / 8); + tmp = (drm_order(RADEON_GPU_PAGE_SIZE/8) << 8) | rb_bufsz; +#ifdef __BIG_ENDIAN + tmp |= BUF_SWAP_32BIT; +#endif + WREG32(CP_RB0_CNTL, tmp); + + /* Initialize the ring buffer's read and write pointers */ + WREG32(CP_RB0_CNTL, tmp | RB_RPTR_WR_ENA); + ring->wptr = 0; + WREG32(CP_RB0_WPTR, ring->wptr); + + /* set the wb address whether it's enabled or not */ + WREG32(CP_RB0_RPTR_ADDR, (rdev->wb.gpu_addr + RADEON_WB_CP_RPTR_OFFSET) & 0xFFFFFFFC); + WREG32(CP_RB0_RPTR_ADDR_HI, upper_32_bits(rdev->wb.gpu_addr + RADEON_WB_CP_RPTR_OFFSET) & 0xFF); + + if (rdev->wb.enabled) + WREG32(SCRATCH_UMSK, 0xff); + else { + tmp |= RB_NO_UPDATE; + WREG32(SCRATCH_UMSK, 0); + } + + DRM_MDELAY(1); + WREG32(CP_RB0_CNTL, tmp); + + WREG32(CP_RB0_BASE, ring->gpu_addr >> 8); + + ring->rptr = RREG32(CP_RB0_RPTR); + + /* ring1 - compute only */ + /* Set ring buffer size */ + ring = &rdev->ring[CAYMAN_RING_TYPE_CP1_INDEX]; + rb_bufsz = drm_order(ring->ring_size / 8); + tmp = (drm_order(RADEON_GPU_PAGE_SIZE/8) << 8) | rb_bufsz; +#ifdef __BIG_ENDIAN + tmp |= BUF_SWAP_32BIT; +#endif + WREG32(CP_RB1_CNTL, tmp); + + /* Initialize the ring buffer's read and write pointers */ + WREG32(CP_RB1_CNTL, tmp | RB_RPTR_WR_ENA); + ring->wptr = 0; + WREG32(CP_RB1_WPTR, ring->wptr); + + /* set the wb address whether it's enabled or not */ + WREG32(CP_RB1_RPTR_ADDR, (rdev->wb.gpu_addr + RADEON_WB_CP1_RPTR_OFFSET) & 0xFFFFFFFC); + WREG32(CP_RB1_RPTR_ADDR_HI, upper_32_bits(rdev->wb.gpu_addr + RADEON_WB_CP1_RPTR_OFFSET) & 0xFF); + + DRM_MDELAY(1); + WREG32(CP_RB1_CNTL, tmp); + + WREG32(CP_RB1_BASE, ring->gpu_addr >> 8); + + ring->rptr = RREG32(CP_RB1_RPTR); + + /* ring2 - compute only */ + /* Set ring buffer size */ + ring = &rdev->ring[CAYMAN_RING_TYPE_CP2_INDEX]; + rb_bufsz = drm_order(ring->ring_size / 8); + tmp = (drm_order(RADEON_GPU_PAGE_SIZE/8) << 8) | rb_bufsz; +#ifdef __BIG_ENDIAN + tmp |= BUF_SWAP_32BIT; +#endif + WREG32(CP_RB2_CNTL, tmp); + + /* Initialize the ring buffer's read and write pointers */ + WREG32(CP_RB2_CNTL, tmp | RB_RPTR_WR_ENA); + ring->wptr = 0; + WREG32(CP_RB2_WPTR, ring->wptr); + + /* set the wb address whether it's enabled or not */ + WREG32(CP_RB2_RPTR_ADDR, (rdev->wb.gpu_addr + RADEON_WB_CP2_RPTR_OFFSET) & 0xFFFFFFFC); + WREG32(CP_RB2_RPTR_ADDR_HI, upper_32_bits(rdev->wb.gpu_addr + RADEON_WB_CP2_RPTR_OFFSET) & 0xFF); + + DRM_MDELAY(1); + WREG32(CP_RB2_CNTL, tmp); + + WREG32(CP_RB2_BASE, ring->gpu_addr >> 8); + + ring->rptr = RREG32(CP_RB2_RPTR); + + /* start the rings */ + si_cp_start(rdev); + rdev->ring[RADEON_RING_TYPE_GFX_INDEX].ready = true; + rdev->ring[CAYMAN_RING_TYPE_CP1_INDEX].ready = true; + rdev->ring[CAYMAN_RING_TYPE_CP2_INDEX].ready = true; + r = radeon_ring_test(rdev, RADEON_RING_TYPE_GFX_INDEX, &rdev->ring[RADEON_RING_TYPE_GFX_INDEX]); + if (r) { + rdev->ring[RADEON_RING_TYPE_GFX_INDEX].ready = false; + rdev->ring[CAYMAN_RING_TYPE_CP1_INDEX].ready = false; + rdev->ring[CAYMAN_RING_TYPE_CP2_INDEX].ready = false; + return r; + } + r = radeon_ring_test(rdev, CAYMAN_RING_TYPE_CP1_INDEX, &rdev->ring[CAYMAN_RING_TYPE_CP1_INDEX]); + if (r) { + rdev->ring[CAYMAN_RING_TYPE_CP1_INDEX].ready = false; + } + r = radeon_ring_test(rdev, CAYMAN_RING_TYPE_CP2_INDEX, &rdev->ring[CAYMAN_RING_TYPE_CP2_INDEX]); + if (r) { + rdev->ring[CAYMAN_RING_TYPE_CP2_INDEX].ready = false; + } + + return 0; +} + +bool si_gpu_is_lockup(struct radeon_device *rdev, struct radeon_ring *ring) +{ + u32 srbm_status; + u32 grbm_status, grbm_status2; + u32 grbm_status_se0, grbm_status_se1; + + srbm_status = RREG32(SRBM_STATUS); + grbm_status = RREG32(GRBM_STATUS); + grbm_status2 = RREG32(GRBM_STATUS2); + grbm_status_se0 = RREG32(GRBM_STATUS_SE0); + grbm_status_se1 = RREG32(GRBM_STATUS_SE1); + if (!(grbm_status & GUI_ACTIVE)) { + radeon_ring_lockup_update(ring); + return false; + } + /* force CP activities */ + radeon_ring_force_activity(rdev, ring); + return radeon_ring_test_lockup(rdev, ring); +} + +static void si_gpu_soft_reset_gfx(struct radeon_device *rdev) +{ + u32 grbm_reset = 0; + + if (!(RREG32(GRBM_STATUS) & GUI_ACTIVE)) + return; + + dev_info(rdev->dev, " GRBM_STATUS=0x%08X\n", + RREG32(GRBM_STATUS)); + dev_info(rdev->dev, " GRBM_STATUS2=0x%08X\n", + RREG32(GRBM_STATUS2)); + dev_info(rdev->dev, " GRBM_STATUS_SE0=0x%08X\n", + RREG32(GRBM_STATUS_SE0)); + dev_info(rdev->dev, " GRBM_STATUS_SE1=0x%08X\n", + RREG32(GRBM_STATUS_SE1)); + dev_info(rdev->dev, " SRBM_STATUS=0x%08X\n", + RREG32(SRBM_STATUS)); + + /* Disable CP parsing/prefetching */ + WREG32(CP_ME_CNTL, CP_ME_HALT | CP_PFP_HALT | CP_CE_HALT); + + /* reset all the gfx blocks */ + grbm_reset = (SOFT_RESET_CP | + SOFT_RESET_CB | + SOFT_RESET_DB | + SOFT_RESET_GDS | + SOFT_RESET_PA | + SOFT_RESET_SC | + SOFT_RESET_BCI | + SOFT_RESET_SPI | + SOFT_RESET_SX | + SOFT_RESET_TC | + SOFT_RESET_TA | + SOFT_RESET_VGT | + SOFT_RESET_IA); + + dev_info(rdev->dev, " GRBM_SOFT_RESET=0x%08X\n", grbm_reset); + WREG32(GRBM_SOFT_RESET, grbm_reset); + (void)RREG32(GRBM_SOFT_RESET); + DRM_UDELAY(50); + WREG32(GRBM_SOFT_RESET, 0); + (void)RREG32(GRBM_SOFT_RESET); + + dev_info(rdev->dev, " GRBM_STATUS=0x%08X\n", + RREG32(GRBM_STATUS)); + dev_info(rdev->dev, " GRBM_STATUS2=0x%08X\n", + RREG32(GRBM_STATUS2)); + dev_info(rdev->dev, " GRBM_STATUS_SE0=0x%08X\n", + RREG32(GRBM_STATUS_SE0)); + dev_info(rdev->dev, " GRBM_STATUS_SE1=0x%08X\n", + RREG32(GRBM_STATUS_SE1)); + dev_info(rdev->dev, " SRBM_STATUS=0x%08X\n", + RREG32(SRBM_STATUS)); +} + +static void si_gpu_soft_reset_dma(struct radeon_device *rdev) +{ + u32 tmp; + + if (RREG32(DMA_STATUS_REG) & DMA_IDLE) + return; + + dev_info(rdev->dev, " DMA_STATUS_REG = 0x%08X\n", + RREG32(DMA_STATUS_REG)); + + /* dma0 */ + tmp = RREG32(DMA_RB_CNTL + DMA0_REGISTER_OFFSET); + tmp &= ~DMA_RB_ENABLE; + WREG32(DMA_RB_CNTL + DMA0_REGISTER_OFFSET, tmp); + + /* dma1 */ + tmp = RREG32(DMA_RB_CNTL + DMA1_REGISTER_OFFSET); + tmp &= ~DMA_RB_ENABLE; + WREG32(DMA_RB_CNTL + DMA1_REGISTER_OFFSET, tmp); + + /* Reset dma */ + WREG32(SRBM_SOFT_RESET, SOFT_RESET_DMA | SOFT_RESET_DMA1); + RREG32(SRBM_SOFT_RESET); + DRM_UDELAY(50); + WREG32(SRBM_SOFT_RESET, 0); + + dev_info(rdev->dev, " DMA_STATUS_REG = 0x%08X\n", + RREG32(DMA_STATUS_REG)); +} + +static int si_gpu_soft_reset(struct radeon_device *rdev, u32 reset_mask) +{ + struct evergreen_mc_save save; + + if (!(RREG32(GRBM_STATUS) & GUI_ACTIVE)) + reset_mask &= ~(RADEON_RESET_GFX | RADEON_RESET_COMPUTE); + + if (RREG32(DMA_STATUS_REG) & DMA_IDLE) + reset_mask &= ~RADEON_RESET_DMA; + + if (reset_mask == 0) + return 0; + + dev_info(rdev->dev, "GPU softreset: 0x%08X\n", reset_mask); + + dev_info(rdev->dev, " VM_CONTEXT1_PROTECTION_FAULT_ADDR 0x%08X\n", + RREG32(VM_CONTEXT1_PROTECTION_FAULT_ADDR)); + dev_info(rdev->dev, " VM_CONTEXT1_PROTECTION_FAULT_STATUS 0x%08X\n", + RREG32(VM_CONTEXT1_PROTECTION_FAULT_STATUS)); + + evergreen_mc_stop(rdev, &save); + if (radeon_mc_wait_for_idle(rdev)) { + dev_warn(rdev->dev, "Wait for MC idle timedout !\n"); + } + + if (reset_mask & (RADEON_RESET_GFX | RADEON_RESET_COMPUTE)) + si_gpu_soft_reset_gfx(rdev); + + if (reset_mask & RADEON_RESET_DMA) + si_gpu_soft_reset_dma(rdev); + + /* Wait a little for things to settle down */ + DRM_UDELAY(50); + + evergreen_mc_resume(rdev, &save); + return 0; +} + +int si_asic_reset(struct radeon_device *rdev) +{ + return si_gpu_soft_reset(rdev, (RADEON_RESET_GFX | + RADEON_RESET_COMPUTE | + RADEON_RESET_DMA)); +} + +/* MC */ +static void si_mc_program(struct radeon_device *rdev) +{ + struct evergreen_mc_save save; + u32 tmp; + int i, j; + + /* Initialize HDP */ + for (i = 0, j = 0; i < 32; i++, j += 0x18) { + WREG32((0x2c14 + j), 0x00000000); + WREG32((0x2c18 + j), 0x00000000); + WREG32((0x2c1c + j), 0x00000000); + WREG32((0x2c20 + j), 0x00000000); + WREG32((0x2c24 + j), 0x00000000); + } + WREG32(HDP_REG_COHERENCY_FLUSH_CNTL, 0); + + evergreen_mc_stop(rdev, &save); + if (radeon_mc_wait_for_idle(rdev)) { + dev_warn(rdev->dev, "Wait for MC idle timedout !\n"); + } + /* Lockout access through VGA aperture*/ + WREG32(VGA_HDP_CONTROL, VGA_MEMORY_DISABLE); + /* Update configuration */ + WREG32(MC_VM_SYSTEM_APERTURE_LOW_ADDR, + rdev->mc.vram_start >> 12); + WREG32(MC_VM_SYSTEM_APERTURE_HIGH_ADDR, + rdev->mc.vram_end >> 12); + WREG32(MC_VM_SYSTEM_APERTURE_DEFAULT_ADDR, + rdev->vram_scratch.gpu_addr >> 12); + tmp = ((rdev->mc.vram_end >> 24) & 0xFFFF) << 16; + tmp |= ((rdev->mc.vram_start >> 24) & 0xFFFF); + WREG32(MC_VM_FB_LOCATION, tmp); + /* XXX double check these! */ + WREG32(HDP_NONSURFACE_BASE, (rdev->mc.vram_start >> 8)); + WREG32(HDP_NONSURFACE_INFO, (2 << 7) | (1 << 30)); + WREG32(HDP_NONSURFACE_SIZE, 0x3FFFFFFF); + WREG32(MC_VM_AGP_BASE, 0); + WREG32(MC_VM_AGP_TOP, 0x0FFFFFFF); + WREG32(MC_VM_AGP_BOT, 0x0FFFFFFF); + if (radeon_mc_wait_for_idle(rdev)) { + dev_warn(rdev->dev, "Wait for MC idle timedout !\n"); + } + evergreen_mc_resume(rdev, &save); + /* we need to own VRAM, so turn off the VGA renderer here + * to stop it overwriting our objects */ + rv515_vga_render_disable(rdev); +} + +/* SI MC address space is 40 bits */ +static void si_vram_location(struct radeon_device *rdev, + struct radeon_mc *mc, u64 base) +{ + mc->vram_start = base; + if (mc->mc_vram_size > (0xFFFFFFFFFFULL - base + 1)) { + dev_warn(rdev->dev, "limiting VRAM to PCI aperture size\n"); + mc->real_vram_size = mc->aper_size; + mc->mc_vram_size = mc->aper_size; + } + mc->vram_end = mc->vram_start + mc->mc_vram_size - 1; + dev_info(rdev->dev, "VRAM: %juM 0x%016jX - 0x%016jX (%juM used)\n", + (uintmax_t)mc->mc_vram_size >> 20, (uintmax_t)mc->vram_start, + (uintmax_t)mc->vram_end, (uintmax_t)mc->real_vram_size >> 20); +} + +static void si_gtt_location(struct radeon_device *rdev, struct radeon_mc *mc) +{ + u64 size_af, size_bf; + + size_af = ((0xFFFFFFFFFFULL - mc->vram_end) + mc->gtt_base_align) & ~mc->gtt_base_align; + size_bf = mc->vram_start & ~mc->gtt_base_align; + if (size_bf > size_af) { + if (mc->gtt_size > size_bf) { + dev_warn(rdev->dev, "limiting GTT\n"); + mc->gtt_size = size_bf; + } + mc->gtt_start = (mc->vram_start & ~mc->gtt_base_align) - mc->gtt_size; + } else { + if (mc->gtt_size > size_af) { + dev_warn(rdev->dev, "limiting GTT\n"); + mc->gtt_size = size_af; + } + mc->gtt_start = (mc->vram_end + 1 + mc->gtt_base_align) & ~mc->gtt_base_align; + } + mc->gtt_end = mc->gtt_start + mc->gtt_size - 1; + dev_info(rdev->dev, "GTT: %juM 0x%016jX - 0x%016jX\n", + (uintmax_t)mc->gtt_size >> 20, (uintmax_t)mc->gtt_start, (uintmax_t)mc->gtt_end); +} + +static void si_vram_gtt_location(struct radeon_device *rdev, + struct radeon_mc *mc) +{ + if (mc->mc_vram_size > 0xFFC0000000ULL) { + /* leave room for at least 1024M GTT */ + dev_warn(rdev->dev, "limiting VRAM\n"); + mc->real_vram_size = 0xFFC0000000ULL; + mc->mc_vram_size = 0xFFC0000000ULL; + } + si_vram_location(rdev, &rdev->mc, 0); + rdev->mc.gtt_base_align = 0; + si_gtt_location(rdev, mc); +} + +static int si_mc_init(struct radeon_device *rdev) +{ + u32 tmp; + int chansize, numchan; + + /* Get VRAM informations */ + rdev->mc.vram_is_ddr = true; + tmp = RREG32(MC_ARB_RAMCFG); + if (tmp & CHANSIZE_OVERRIDE) { + chansize = 16; + } else if (tmp & CHANSIZE_MASK) { + chansize = 64; + } else { + chansize = 32; + } + tmp = RREG32(MC_SHARED_CHMAP); + switch ((tmp & NOOFCHAN_MASK) >> NOOFCHAN_SHIFT) { + case 0: + default: + numchan = 1; + break; + case 1: + numchan = 2; + break; + case 2: + numchan = 4; + break; + case 3: + numchan = 8; + break; + case 4: + numchan = 3; + break; + case 5: + numchan = 6; + break; + case 6: + numchan = 10; + break; + case 7: + numchan = 12; + break; + case 8: + numchan = 16; + break; + } + rdev->mc.vram_width = numchan * chansize; + /* Could aper size report 0 ? */ + rdev->mc.aper_base = drm_get_resource_start(rdev->ddev, 0); + rdev->mc.aper_size = drm_get_resource_len(rdev->ddev, 0); + /* size in MB on si */ + rdev->mc.mc_vram_size = RREG32(CONFIG_MEMSIZE) * 1024 * 1024; + rdev->mc.real_vram_size = RREG32(CONFIG_MEMSIZE) * 1024 * 1024; + rdev->mc.visible_vram_size = rdev->mc.aper_size; + si_vram_gtt_location(rdev, &rdev->mc); + radeon_update_bandwidth_info(rdev); + + return 0; +} + +/* + * GART + */ +void si_pcie_gart_tlb_flush(struct radeon_device *rdev) +{ + /* flush hdp cache */ + WREG32(HDP_MEM_COHERENCY_FLUSH_CNTL, 0x1); + + /* bits 0-15 are the VM contexts0-15 */ + WREG32(VM_INVALIDATE_REQUEST, 1); +} + +static int si_pcie_gart_enable(struct radeon_device *rdev) +{ + int r, i; + + if (rdev->gart.robj == NULL) { + dev_err(rdev->dev, "No VRAM object for PCIE GART.\n"); + return -EINVAL; + } + r = radeon_gart_table_vram_pin(rdev); + if (r) + return r; + radeon_gart_restore(rdev); + /* Setup TLB control */ + WREG32(MC_VM_MX_L1_TLB_CNTL, + (0xA << 7) | + ENABLE_L1_TLB | + SYSTEM_ACCESS_MODE_NOT_IN_SYS | + ENABLE_ADVANCED_DRIVER_MODEL | + SYSTEM_APERTURE_UNMAPPED_ACCESS_PASS_THRU); + /* Setup L2 cache */ + WREG32(VM_L2_CNTL, ENABLE_L2_CACHE | + ENABLE_L2_PTE_CACHE_LRU_UPDATE_BY_WRITE | + ENABLE_L2_PDE0_CACHE_LRU_UPDATE_BY_WRITE | + EFFECTIVE_L2_QUEUE_SIZE(7) | + CONTEXT1_IDENTITY_ACCESS_MODE(1)); + WREG32(VM_L2_CNTL2, INVALIDATE_ALL_L1_TLBS | INVALIDATE_L2_CACHE); + WREG32(VM_L2_CNTL3, L2_CACHE_BIGK_ASSOCIATIVITY | + L2_CACHE_BIGK_FRAGMENT_SIZE(0)); + /* setup context0 */ + WREG32(VM_CONTEXT0_PAGE_TABLE_START_ADDR, rdev->mc.gtt_start >> 12); + WREG32(VM_CONTEXT0_PAGE_TABLE_END_ADDR, rdev->mc.gtt_end >> 12); + WREG32(VM_CONTEXT0_PAGE_TABLE_BASE_ADDR, rdev->gart.table_addr >> 12); + WREG32(VM_CONTEXT0_PROTECTION_FAULT_DEFAULT_ADDR, + (u32)(rdev->dummy_page.addr >> 12)); + WREG32(VM_CONTEXT0_CNTL2, 0); + WREG32(VM_CONTEXT0_CNTL, (ENABLE_CONTEXT | PAGE_TABLE_DEPTH(0) | + RANGE_PROTECTION_FAULT_ENABLE_DEFAULT)); + + WREG32(0x15D4, 0); + WREG32(0x15D8, 0); + WREG32(0x15DC, 0); + + /* empty context1-15 */ + /* set vm size, must be a multiple of 4 */ + WREG32(VM_CONTEXT1_PAGE_TABLE_START_ADDR, 0); + WREG32(VM_CONTEXT1_PAGE_TABLE_END_ADDR, rdev->vm_manager.max_pfn); + /* Assign the pt base to something valid for now; the pts used for + * the VMs are determined by the application and setup and assigned + * on the fly in the vm part of radeon_gart.c + */ + for (i = 1; i < 16; i++) { + if (i < 8) + WREG32(VM_CONTEXT0_PAGE_TABLE_BASE_ADDR + (i << 2), + rdev->gart.table_addr >> 12); + else + WREG32(VM_CONTEXT8_PAGE_TABLE_BASE_ADDR + ((i - 8) << 2), + rdev->gart.table_addr >> 12); + } + + /* enable context1-15 */ + WREG32(VM_CONTEXT1_PROTECTION_FAULT_DEFAULT_ADDR, + (u32)(rdev->dummy_page.addr >> 12)); + WREG32(VM_CONTEXT1_CNTL2, 4); + WREG32(VM_CONTEXT1_CNTL, ENABLE_CONTEXT | PAGE_TABLE_DEPTH(1) | + RANGE_PROTECTION_FAULT_ENABLE_INTERRUPT | + RANGE_PROTECTION_FAULT_ENABLE_DEFAULT | + DUMMY_PAGE_PROTECTION_FAULT_ENABLE_INTERRUPT | + DUMMY_PAGE_PROTECTION_FAULT_ENABLE_DEFAULT | + PDE0_PROTECTION_FAULT_ENABLE_INTERRUPT | + PDE0_PROTECTION_FAULT_ENABLE_DEFAULT | + VALID_PROTECTION_FAULT_ENABLE_INTERRUPT | + VALID_PROTECTION_FAULT_ENABLE_DEFAULT | + READ_PROTECTION_FAULT_ENABLE_INTERRUPT | + READ_PROTECTION_FAULT_ENABLE_DEFAULT | + WRITE_PROTECTION_FAULT_ENABLE_INTERRUPT | + WRITE_PROTECTION_FAULT_ENABLE_DEFAULT); + + si_pcie_gart_tlb_flush(rdev); + DRM_INFO("PCIE GART of %uM enabled (table at 0x%016llX).\n", + (unsigned)(rdev->mc.gtt_size >> 20), + (unsigned long long)rdev->gart.table_addr); + rdev->gart.ready = true; + return 0; +} + +static void si_pcie_gart_disable(struct radeon_device *rdev) +{ + /* Disable all tables */ + WREG32(VM_CONTEXT0_CNTL, 0); + WREG32(VM_CONTEXT1_CNTL, 0); + /* Setup TLB control */ + WREG32(MC_VM_MX_L1_TLB_CNTL, SYSTEM_ACCESS_MODE_NOT_IN_SYS | + SYSTEM_APERTURE_UNMAPPED_ACCESS_PASS_THRU); + /* Setup L2 cache */ + WREG32(VM_L2_CNTL, ENABLE_L2_PTE_CACHE_LRU_UPDATE_BY_WRITE | + ENABLE_L2_PDE0_CACHE_LRU_UPDATE_BY_WRITE | + EFFECTIVE_L2_QUEUE_SIZE(7) | + CONTEXT1_IDENTITY_ACCESS_MODE(1)); + WREG32(VM_L2_CNTL2, 0); + WREG32(VM_L2_CNTL3, L2_CACHE_BIGK_ASSOCIATIVITY | + L2_CACHE_BIGK_FRAGMENT_SIZE(0)); + radeon_gart_table_vram_unpin(rdev); +} + +static void si_pcie_gart_fini(struct radeon_device *rdev) +{ + si_pcie_gart_disable(rdev); + radeon_gart_table_vram_free(rdev); + radeon_gart_fini(rdev); +} + +/* vm parser */ +static bool si_vm_reg_valid(u32 reg) +{ + /* context regs are fine */ + if (reg >= 0x28000) + return true; + + /* check config regs */ + switch (reg) { + case GRBM_GFX_INDEX: + case CP_STRMOUT_CNTL: + case VGT_VTX_VECT_EJECT_REG: + case VGT_CACHE_INVALIDATION: + case VGT_ESGS_RING_SIZE: + case VGT_GSVS_RING_SIZE: + case VGT_GS_VERTEX_REUSE: + case VGT_PRIMITIVE_TYPE: + case VGT_INDEX_TYPE: + case VGT_NUM_INDICES: + case VGT_NUM_INSTANCES: + case VGT_TF_RING_SIZE: + case VGT_HS_OFFCHIP_PARAM: + case VGT_TF_MEMORY_BASE: + case PA_CL_ENHANCE: + case PA_SU_LINE_STIPPLE_VALUE: + case PA_SC_LINE_STIPPLE_STATE: + case PA_SC_ENHANCE: + case SQC_CACHES: + case SPI_STATIC_THREAD_MGMT_1: + case SPI_STATIC_THREAD_MGMT_2: + case SPI_STATIC_THREAD_MGMT_3: + case SPI_PS_MAX_WAVE_ID: + case SPI_CONFIG_CNTL: + case SPI_CONFIG_CNTL_1: + case TA_CNTL_AUX: + return true; + default: + DRM_ERROR("Invalid register 0x%x in CS\n", reg); + return false; + } +} + +static int si_vm_packet3_ce_check(struct radeon_device *rdev, + u32 *ib, struct radeon_cs_packet *pkt) +{ + switch (pkt->opcode) { + case PACKET3_NOP: + case PACKET3_SET_BASE: + case PACKET3_SET_CE_DE_COUNTERS: + case PACKET3_LOAD_CONST_RAM: + case PACKET3_WRITE_CONST_RAM: + case PACKET3_WRITE_CONST_RAM_OFFSET: + case PACKET3_DUMP_CONST_RAM: + case PACKET3_INCREMENT_CE_COUNTER: + case PACKET3_WAIT_ON_DE_COUNTER: + case PACKET3_CE_WRITE: + break; + default: + DRM_ERROR("Invalid CE packet3: 0x%x\n", pkt->opcode); + return -EINVAL; + } + return 0; +} + +static int si_vm_packet3_gfx_check(struct radeon_device *rdev, + u32 *ib, struct radeon_cs_packet *pkt) +{ + u32 idx = pkt->idx + 1; + u32 idx_value = ib[idx]; + u32 start_reg, end_reg, reg, i; + u32 command, info; + + switch (pkt->opcode) { + case PACKET3_NOP: + case PACKET3_SET_BASE: + case PACKET3_CLEAR_STATE: + case PACKET3_INDEX_BUFFER_SIZE: + case PACKET3_DISPATCH_DIRECT: + case PACKET3_DISPATCH_INDIRECT: + case PACKET3_ALLOC_GDS: + case PACKET3_WRITE_GDS_RAM: + case PACKET3_ATOMIC_GDS: + case PACKET3_ATOMIC: + case PACKET3_OCCLUSION_QUERY: + case PACKET3_SET_PREDICATION: + case PACKET3_COND_EXEC: + case PACKET3_PRED_EXEC: + case PACKET3_DRAW_INDIRECT: + case PACKET3_DRAW_INDEX_INDIRECT: + case PACKET3_INDEX_BASE: + case PACKET3_DRAW_INDEX_2: + case PACKET3_CONTEXT_CONTROL: + case PACKET3_INDEX_TYPE: + case PACKET3_DRAW_INDIRECT_MULTI: + case PACKET3_DRAW_INDEX_AUTO: + case PACKET3_DRAW_INDEX_IMMD: + case PACKET3_NUM_INSTANCES: + case PACKET3_DRAW_INDEX_MULTI_AUTO: + case PACKET3_STRMOUT_BUFFER_UPDATE: + case PACKET3_DRAW_INDEX_OFFSET_2: + case PACKET3_DRAW_INDEX_MULTI_ELEMENT: + case PACKET3_DRAW_INDEX_INDIRECT_MULTI: + case PACKET3_MPEG_INDEX: + case PACKET3_WAIT_REG_MEM: + case PACKET3_MEM_WRITE: + case PACKET3_PFP_SYNC_ME: + case PACKET3_SURFACE_SYNC: + case PACKET3_EVENT_WRITE: + case PACKET3_EVENT_WRITE_EOP: + case PACKET3_EVENT_WRITE_EOS: + case PACKET3_SET_CONTEXT_REG: + case PACKET3_SET_CONTEXT_REG_INDIRECT: + case PACKET3_SET_SH_REG: + case PACKET3_SET_SH_REG_OFFSET: + case PACKET3_INCREMENT_DE_COUNTER: + case PACKET3_WAIT_ON_CE_COUNTER: + case PACKET3_WAIT_ON_AVAIL_BUFFER: + case PACKET3_ME_WRITE: + break; + case PACKET3_COPY_DATA: + if ((idx_value & 0xf00) == 0) { + reg = ib[idx + 3] * 4; + if (!si_vm_reg_valid(reg)) + return -EINVAL; + } + break; + case PACKET3_WRITE_DATA: + if ((idx_value & 0xf00) == 0) { + start_reg = ib[idx + 1] * 4; + if (idx_value & 0x10000) { + if (!si_vm_reg_valid(start_reg)) + return -EINVAL; + } else { + for (i = 0; i < (pkt->count - 2); i++) { + reg = start_reg + (4 * i); + if (!si_vm_reg_valid(reg)) + return -EINVAL; + } + } + } + break; + case PACKET3_COND_WRITE: + if (idx_value & 0x100) { + reg = ib[idx + 5] * 4; + if (!si_vm_reg_valid(reg)) + return -EINVAL; + } + break; + case PACKET3_COPY_DW: + if (idx_value & 0x2) { + reg = ib[idx + 3] * 4; + if (!si_vm_reg_valid(reg)) + return -EINVAL; + } + break; + case PACKET3_SET_CONFIG_REG: + start_reg = (idx_value << 2) + PACKET3_SET_CONFIG_REG_START; + end_reg = 4 * pkt->count + start_reg - 4; + if ((start_reg < PACKET3_SET_CONFIG_REG_START) || + (start_reg >= PACKET3_SET_CONFIG_REG_END) || + (end_reg >= PACKET3_SET_CONFIG_REG_END)) { + DRM_ERROR("bad PACKET3_SET_CONFIG_REG\n"); + return -EINVAL; + } + for (i = 0; i < pkt->count; i++) { + reg = start_reg + (4 * i); + if (!si_vm_reg_valid(reg)) + return -EINVAL; + } + break; + case PACKET3_CP_DMA: + command = ib[idx + 4]; + info = ib[idx + 1]; + if (command & PACKET3_CP_DMA_CMD_SAS) { + /* src address space is register */ + if (((info & 0x60000000) >> 29) == 0) { + start_reg = idx_value << 2; + if (command & PACKET3_CP_DMA_CMD_SAIC) { + reg = start_reg; + if (!si_vm_reg_valid(reg)) { + DRM_ERROR("CP DMA Bad SRC register\n"); + return -EINVAL; + } + } else { + for (i = 0; i < (command & 0x1fffff); i++) { + reg = start_reg + (4 * i); + if (!si_vm_reg_valid(reg)) { + DRM_ERROR("CP DMA Bad SRC register\n"); + return -EINVAL; + } + } + } + } + } + if (command & PACKET3_CP_DMA_CMD_DAS) { + /* dst address space is register */ + if (((info & 0x00300000) >> 20) == 0) { + start_reg = ib[idx + 2]; + if (command & PACKET3_CP_DMA_CMD_DAIC) { + reg = start_reg; + if (!si_vm_reg_valid(reg)) { + DRM_ERROR("CP DMA Bad DST register\n"); + return -EINVAL; + } + } else { + for (i = 0; i < (command & 0x1fffff); i++) { + reg = start_reg + (4 * i); + if (!si_vm_reg_valid(reg)) { + DRM_ERROR("CP DMA Bad DST register\n"); + return -EINVAL; + } + } + } + } + } + break; + default: + DRM_ERROR("Invalid GFX packet3: 0x%x\n", pkt->opcode); + return -EINVAL; + } + return 0; +} + +static int si_vm_packet3_compute_check(struct radeon_device *rdev, + u32 *ib, struct radeon_cs_packet *pkt) +{ + u32 idx = pkt->idx + 1; + u32 idx_value = ib[idx]; + u32 start_reg, reg, i; + + switch (pkt->opcode) { + case PACKET3_NOP: + case PACKET3_SET_BASE: + case PACKET3_CLEAR_STATE: + case PACKET3_DISPATCH_DIRECT: + case PACKET3_DISPATCH_INDIRECT: + case PACKET3_ALLOC_GDS: + case PACKET3_WRITE_GDS_RAM: + case PACKET3_ATOMIC_GDS: + case PACKET3_ATOMIC: + case PACKET3_OCCLUSION_QUERY: + case PACKET3_SET_PREDICATION: + case PACKET3_COND_EXEC: + case PACKET3_PRED_EXEC: + case PACKET3_CONTEXT_CONTROL: + case PACKET3_STRMOUT_BUFFER_UPDATE: + case PACKET3_WAIT_REG_MEM: + case PACKET3_MEM_WRITE: + case PACKET3_PFP_SYNC_ME: + case PACKET3_SURFACE_SYNC: + case PACKET3_EVENT_WRITE: + case PACKET3_EVENT_WRITE_EOP: + case PACKET3_EVENT_WRITE_EOS: + case PACKET3_SET_CONTEXT_REG: + case PACKET3_SET_CONTEXT_REG_INDIRECT: + case PACKET3_SET_SH_REG: + case PACKET3_SET_SH_REG_OFFSET: + case PACKET3_INCREMENT_DE_COUNTER: + case PACKET3_WAIT_ON_CE_COUNTER: + case PACKET3_WAIT_ON_AVAIL_BUFFER: + case PACKET3_ME_WRITE: + break; + case PACKET3_COPY_DATA: + if ((idx_value & 0xf00) == 0) { + reg = ib[idx + 3] * 4; + if (!si_vm_reg_valid(reg)) + return -EINVAL; + } + break; + case PACKET3_WRITE_DATA: + if ((idx_value & 0xf00) == 0) { + start_reg = ib[idx + 1] * 4; + if (idx_value & 0x10000) { + if (!si_vm_reg_valid(start_reg)) + return -EINVAL; + } else { + for (i = 0; i < (pkt->count - 2); i++) { + reg = start_reg + (4 * i); + if (!si_vm_reg_valid(reg)) + return -EINVAL; + } + } + } + break; + case PACKET3_COND_WRITE: + if (idx_value & 0x100) { + reg = ib[idx + 5] * 4; + if (!si_vm_reg_valid(reg)) + return -EINVAL; + } + break; + case PACKET3_COPY_DW: + if (idx_value & 0x2) { + reg = ib[idx + 3] * 4; + if (!si_vm_reg_valid(reg)) + return -EINVAL; + } + break; + default: + DRM_ERROR("Invalid Compute packet3: 0x%x\n", pkt->opcode); + return -EINVAL; + } + return 0; +} + +int si_ib_parse(struct radeon_device *rdev, struct radeon_ib *ib) +{ + int ret = 0; + u32 idx = 0; + struct radeon_cs_packet pkt; + + do { + pkt.idx = idx; + pkt.type = CP_PACKET_GET_TYPE(ib->ptr[idx]); + pkt.count = CP_PACKET_GET_COUNT(ib->ptr[idx]); + pkt.one_reg_wr = 0; + switch (pkt.type) { + case PACKET_TYPE0: + dev_err(rdev->dev, "Packet0 not allowed!\n"); + ret = -EINVAL; + break; + case PACKET_TYPE2: + idx += 1; + break; + case PACKET_TYPE3: + pkt.opcode = CP_PACKET3_GET_OPCODE(ib->ptr[idx]); + if (ib->is_const_ib) + ret = si_vm_packet3_ce_check(rdev, ib->ptr, &pkt); + else { + switch (ib->ring) { + case RADEON_RING_TYPE_GFX_INDEX: + ret = si_vm_packet3_gfx_check(rdev, ib->ptr, &pkt); + break; + case CAYMAN_RING_TYPE_CP1_INDEX: + case CAYMAN_RING_TYPE_CP2_INDEX: + ret = si_vm_packet3_compute_check(rdev, ib->ptr, &pkt); + break; + default: + dev_err(rdev->dev, "Non-PM4 ring %d !\n", ib->ring); + ret = -EINVAL; + break; + } + } + idx += pkt.count + 2; + break; + default: + dev_err(rdev->dev, "Unknown packet type %d !\n", pkt.type); + ret = -EINVAL; + break; + } + if (ret) + break; + } while (idx < ib->length_dw); + + return ret; +} + +/* + * vm + */ +int si_vm_init(struct radeon_device *rdev) +{ + /* number of VMs */ + rdev->vm_manager.nvm = 16; + /* base offset of vram pages */ + rdev->vm_manager.vram_base_offset = 0; + + return 0; +} + +void si_vm_fini(struct radeon_device *rdev) +{ +} + +/** + * si_vm_set_page - update the page tables using the CP + * + * @rdev: radeon_device pointer + * @pe: addr of the page entry + * @addr: dst addr to write into pe + * @count: number of page entries to update + * @incr: increase next addr by incr bytes + * @flags: access flags + * + * Update the page tables using the CP (cayman-si). + */ +void si_vm_set_page(struct radeon_device *rdev, uint64_t pe, + uint64_t addr, unsigned count, + uint32_t incr, uint32_t flags) +{ + struct radeon_ring *ring = &rdev->ring[rdev->asic->vm.pt_ring_index]; + uint32_t r600_flags = cayman_vm_page_flags(rdev, flags); + uint64_t value; + unsigned ndw; + + if (rdev->asic->vm.pt_ring_index == RADEON_RING_TYPE_GFX_INDEX) { + while (count) { + ndw = 2 + count * 2; + if (ndw > 0x3FFE) + ndw = 0x3FFE; + + radeon_ring_write(ring, PACKET3(PACKET3_WRITE_DATA, ndw)); + radeon_ring_write(ring, (WRITE_DATA_ENGINE_SEL(0) | + WRITE_DATA_DST_SEL(1))); + radeon_ring_write(ring, pe); + radeon_ring_write(ring, upper_32_bits(pe)); + for (; ndw > 2; ndw -= 2, --count, pe += 8) { + if (flags & RADEON_VM_PAGE_SYSTEM) { + value = radeon_vm_map_gart(rdev, addr); + value &= 0xFFFFFFFFFFFFF000ULL; + } else if (flags & RADEON_VM_PAGE_VALID) { + value = addr; + } else { + value = 0; + } + addr += incr; + value |= r600_flags; + radeon_ring_write(ring, value); + radeon_ring_write(ring, upper_32_bits(value)); + } + } + } else { + /* DMA */ + if (flags & RADEON_VM_PAGE_SYSTEM) { + while (count) { + ndw = count * 2; + if (ndw > 0xFFFFE) + ndw = 0xFFFFE; + + /* for non-physically contiguous pages (system) */ + radeon_ring_write(ring, DMA_PACKET(DMA_PACKET_WRITE, 0, 0, 0, ndw)); + radeon_ring_write(ring, pe); + radeon_ring_write(ring, upper_32_bits(pe) & 0xff); + for (; ndw > 0; ndw -= 2, --count, pe += 8) { + if (flags & RADEON_VM_PAGE_SYSTEM) { + value = radeon_vm_map_gart(rdev, addr); + value &= 0xFFFFFFFFFFFFF000ULL; + } else if (flags & RADEON_VM_PAGE_VALID) { + value = addr; + } else { + value = 0; + } + addr += incr; + value |= r600_flags; + radeon_ring_write(ring, value); + radeon_ring_write(ring, upper_32_bits(value)); + } + } + } else { + while (count) { + ndw = count * 2; + if (ndw > 0xFFFFE) + ndw = 0xFFFFE; + + if (flags & RADEON_VM_PAGE_VALID) + value = addr; + else + value = 0; + /* for physically contiguous pages (vram) */ + radeon_ring_write(ring, DMA_PTE_PDE_PACKET(ndw)); + radeon_ring_write(ring, pe); /* dst addr */ + radeon_ring_write(ring, upper_32_bits(pe) & 0xff); + radeon_ring_write(ring, r600_flags); /* mask */ + radeon_ring_write(ring, 0); + radeon_ring_write(ring, value); /* value */ + radeon_ring_write(ring, upper_32_bits(value)); + radeon_ring_write(ring, incr); /* increment size */ + radeon_ring_write(ring, 0); + pe += ndw * 4; + addr += (ndw / 2) * incr; + count -= ndw / 2; + } + } + } +} + +void si_vm_flush(struct radeon_device *rdev, int ridx, struct radeon_vm *vm) +{ + struct radeon_ring *ring = &rdev->ring[ridx]; + + if (vm == NULL) + return; + + /* write new base address */ + radeon_ring_write(ring, PACKET3(PACKET3_WRITE_DATA, 3)); + radeon_ring_write(ring, (WRITE_DATA_ENGINE_SEL(0) | + WRITE_DATA_DST_SEL(0))); + + if (vm->id < 8) { + radeon_ring_write(ring, + (VM_CONTEXT0_PAGE_TABLE_BASE_ADDR + (vm->id << 2)) >> 2); + } else { + radeon_ring_write(ring, + (VM_CONTEXT8_PAGE_TABLE_BASE_ADDR + ((vm->id - 8) << 2)) >> 2); + } + radeon_ring_write(ring, 0); + radeon_ring_write(ring, vm->pd_gpu_addr >> 12); + + /* flush hdp cache */ + radeon_ring_write(ring, PACKET3(PACKET3_WRITE_DATA, 3)); + radeon_ring_write(ring, (WRITE_DATA_ENGINE_SEL(0) | + WRITE_DATA_DST_SEL(0))); + radeon_ring_write(ring, HDP_MEM_COHERENCY_FLUSH_CNTL >> 2); + radeon_ring_write(ring, 0); + radeon_ring_write(ring, 0x1); + + /* bits 0-15 are the VM contexts0-15 */ + radeon_ring_write(ring, PACKET3(PACKET3_WRITE_DATA, 3)); + radeon_ring_write(ring, (WRITE_DATA_ENGINE_SEL(0) | + WRITE_DATA_DST_SEL(0))); + radeon_ring_write(ring, VM_INVALIDATE_REQUEST >> 2); + radeon_ring_write(ring, 0); + radeon_ring_write(ring, 1 << vm->id); + + /* sync PFP to ME, otherwise we might get invalid PFP reads */ + radeon_ring_write(ring, PACKET3(PACKET3_PFP_SYNC_ME, 0)); + radeon_ring_write(ring, 0x0); +} + +void si_dma_vm_flush(struct radeon_device *rdev, int ridx, struct radeon_vm *vm) +{ + struct radeon_ring *ring = &rdev->ring[ridx]; + + if (vm == NULL) + return; + + radeon_ring_write(ring, DMA_PACKET(DMA_PACKET_SRBM_WRITE, 0, 0, 0, 0)); + if (vm->id < 8) { + radeon_ring_write(ring, (0xf << 16) | ((VM_CONTEXT0_PAGE_TABLE_BASE_ADDR + (vm->id << 2)) >> 2)); + } else { + radeon_ring_write(ring, (0xf << 16) | ((VM_CONTEXT8_PAGE_TABLE_BASE_ADDR + ((vm->id - 8) << 2)) >> 2)); + } + radeon_ring_write(ring, vm->pd_gpu_addr >> 12); + + /* flush hdp cache */ + radeon_ring_write(ring, DMA_PACKET(DMA_PACKET_SRBM_WRITE, 0, 0, 0, 0)); + radeon_ring_write(ring, (0xf << 16) | (HDP_MEM_COHERENCY_FLUSH_CNTL >> 2)); + radeon_ring_write(ring, 1); + + /* bits 0-7 are the VM contexts0-7 */ + radeon_ring_write(ring, DMA_PACKET(DMA_PACKET_SRBM_WRITE, 0, 0, 0, 0)); + radeon_ring_write(ring, (0xf << 16) | (VM_INVALIDATE_REQUEST >> 2)); + radeon_ring_write(ring, 1 << vm->id); +} + +/* + * RLC + */ +void si_rlc_fini(struct radeon_device *rdev) +{ + int r; + + /* save restore block */ + if (rdev->rlc.save_restore_obj) { + r = radeon_bo_reserve(rdev->rlc.save_restore_obj, false); + if (unlikely(r != 0)) + dev_warn(rdev->dev, "(%d) reserve RLC sr bo failed\n", r); + radeon_bo_unpin(rdev->rlc.save_restore_obj); + radeon_bo_unreserve(rdev->rlc.save_restore_obj); + + radeon_bo_unref(&rdev->rlc.save_restore_obj); + rdev->rlc.save_restore_obj = NULL; + } + + /* clear state block */ + if (rdev->rlc.clear_state_obj) { + r = radeon_bo_reserve(rdev->rlc.clear_state_obj, false); + if (unlikely(r != 0)) + dev_warn(rdev->dev, "(%d) reserve RLC c bo failed\n", r); + radeon_bo_unpin(rdev->rlc.clear_state_obj); + radeon_bo_unreserve(rdev->rlc.clear_state_obj); + + radeon_bo_unref(&rdev->rlc.clear_state_obj); + rdev->rlc.clear_state_obj = NULL; + } +} + +int si_rlc_init(struct radeon_device *rdev) +{ + int r; + + /* save restore block */ + if (rdev->rlc.save_restore_obj == NULL) { + r = radeon_bo_create(rdev, RADEON_GPU_PAGE_SIZE, PAGE_SIZE, true, + RADEON_GEM_DOMAIN_VRAM, NULL, + &rdev->rlc.save_restore_obj); + if (r) { + dev_warn(rdev->dev, "(%d) create RLC sr bo failed\n", r); + return r; + } + } + + r = radeon_bo_reserve(rdev->rlc.save_restore_obj, false); + if (unlikely(r != 0)) { + si_rlc_fini(rdev); + return r; + } + r = radeon_bo_pin(rdev->rlc.save_restore_obj, RADEON_GEM_DOMAIN_VRAM, + &rdev->rlc.save_restore_gpu_addr); + radeon_bo_unreserve(rdev->rlc.save_restore_obj); + if (r) { + dev_warn(rdev->dev, "(%d) pin RLC sr bo failed\n", r); + si_rlc_fini(rdev); + return r; + } + + /* clear state block */ + if (rdev->rlc.clear_state_obj == NULL) { + r = radeon_bo_create(rdev, RADEON_GPU_PAGE_SIZE, PAGE_SIZE, true, + RADEON_GEM_DOMAIN_VRAM, NULL, + &rdev->rlc.clear_state_obj); + if (r) { + dev_warn(rdev->dev, "(%d) create RLC c bo failed\n", r); + si_rlc_fini(rdev); + return r; + } + } + r = radeon_bo_reserve(rdev->rlc.clear_state_obj, false); + if (unlikely(r != 0)) { + si_rlc_fini(rdev); + return r; + } + r = radeon_bo_pin(rdev->rlc.clear_state_obj, RADEON_GEM_DOMAIN_VRAM, + &rdev->rlc.clear_state_gpu_addr); + radeon_bo_unreserve(rdev->rlc.clear_state_obj); + if (r) { + dev_warn(rdev->dev, "(%d) pin RLC c bo failed\n", r); + si_rlc_fini(rdev); + return r; + } + + return 0; +} + +static void si_rlc_stop(struct radeon_device *rdev) +{ + WREG32(RLC_CNTL, 0); +} + +static void si_rlc_start(struct radeon_device *rdev) +{ + WREG32(RLC_CNTL, RLC_ENABLE); +} + +static int si_rlc_resume(struct radeon_device *rdev) +{ + u32 i; + const __be32 *fw_data; + + if (!rdev->rlc_fw) + return -EINVAL; + + si_rlc_stop(rdev); + + WREG32(RLC_RL_BASE, 0); + WREG32(RLC_RL_SIZE, 0); + WREG32(RLC_LB_CNTL, 0); + WREG32(RLC_LB_CNTR_MAX, 0xffffffff); + WREG32(RLC_LB_CNTR_INIT, 0); + + WREG32(RLC_SAVE_AND_RESTORE_BASE, rdev->rlc.save_restore_gpu_addr >> 8); + WREG32(RLC_CLEAR_STATE_RESTORE_BASE, rdev->rlc.clear_state_gpu_addr >> 8); + + WREG32(RLC_MC_CNTL, 0); + WREG32(RLC_UCODE_CNTL, 0); + + fw_data = (const __be32 *)rdev->rlc_fw->data; + for (i = 0; i < SI_RLC_UCODE_SIZE; i++) { + WREG32(RLC_UCODE_ADDR, i); + WREG32(RLC_UCODE_DATA, be32_to_cpup(fw_data++)); + } + WREG32(RLC_UCODE_ADDR, 0); + + si_rlc_start(rdev); + + return 0; +} + +static void si_enable_interrupts(struct radeon_device *rdev) +{ + u32 ih_cntl = RREG32(IH_CNTL); + u32 ih_rb_cntl = RREG32(IH_RB_CNTL); + + ih_cntl |= ENABLE_INTR; + ih_rb_cntl |= IH_RB_ENABLE; + WREG32(IH_CNTL, ih_cntl); + WREG32(IH_RB_CNTL, ih_rb_cntl); + rdev->ih.enabled = true; +} + +static void si_disable_interrupts(struct radeon_device *rdev) +{ + u32 ih_rb_cntl = RREG32(IH_RB_CNTL); + u32 ih_cntl = RREG32(IH_CNTL); + + ih_rb_cntl &= ~IH_RB_ENABLE; + ih_cntl &= ~ENABLE_INTR; + WREG32(IH_RB_CNTL, ih_rb_cntl); + WREG32(IH_CNTL, ih_cntl); + /* set rptr, wptr to 0 */ + WREG32(IH_RB_RPTR, 0); + WREG32(IH_RB_WPTR, 0); + rdev->ih.enabled = false; + rdev->ih.rptr = 0; +} + +static void si_disable_interrupt_state(struct radeon_device *rdev) +{ + u32 tmp; + + WREG32(CP_INT_CNTL_RING0, CNTX_BUSY_INT_ENABLE | CNTX_EMPTY_INT_ENABLE); + WREG32(CP_INT_CNTL_RING1, 0); + WREG32(CP_INT_CNTL_RING2, 0); + tmp = RREG32(DMA_CNTL + DMA0_REGISTER_OFFSET) & ~TRAP_ENABLE; + WREG32(DMA_CNTL + DMA0_REGISTER_OFFSET, tmp); + tmp = RREG32(DMA_CNTL + DMA1_REGISTER_OFFSET) & ~TRAP_ENABLE; + WREG32(DMA_CNTL + DMA1_REGISTER_OFFSET, tmp); + WREG32(GRBM_INT_CNTL, 0); + WREG32(INT_MASK + EVERGREEN_CRTC0_REGISTER_OFFSET, 0); + WREG32(INT_MASK + EVERGREEN_CRTC1_REGISTER_OFFSET, 0); + if (rdev->num_crtc >= 4) { + WREG32(INT_MASK + EVERGREEN_CRTC2_REGISTER_OFFSET, 0); + WREG32(INT_MASK + EVERGREEN_CRTC3_REGISTER_OFFSET, 0); + } + if (rdev->num_crtc >= 6) { + WREG32(INT_MASK + EVERGREEN_CRTC4_REGISTER_OFFSET, 0); + WREG32(INT_MASK + EVERGREEN_CRTC5_REGISTER_OFFSET, 0); + } + + WREG32(GRPH_INT_CONTROL + EVERGREEN_CRTC0_REGISTER_OFFSET, 0); + WREG32(GRPH_INT_CONTROL + EVERGREEN_CRTC1_REGISTER_OFFSET, 0); + if (rdev->num_crtc >= 4) { + WREG32(GRPH_INT_CONTROL + EVERGREEN_CRTC2_REGISTER_OFFSET, 0); + WREG32(GRPH_INT_CONTROL + EVERGREEN_CRTC3_REGISTER_OFFSET, 0); + } + if (rdev->num_crtc >= 6) { + WREG32(GRPH_INT_CONTROL + EVERGREEN_CRTC4_REGISTER_OFFSET, 0); + WREG32(GRPH_INT_CONTROL + EVERGREEN_CRTC5_REGISTER_OFFSET, 0); + } + + WREG32(DACA_AUTODETECT_INT_CONTROL, 0); + + tmp = RREG32(DC_HPD1_INT_CONTROL) & DC_HPDx_INT_POLARITY; + WREG32(DC_HPD1_INT_CONTROL, tmp); + tmp = RREG32(DC_HPD2_INT_CONTROL) & DC_HPDx_INT_POLARITY; + WREG32(DC_HPD2_INT_CONTROL, tmp); + tmp = RREG32(DC_HPD3_INT_CONTROL) & DC_HPDx_INT_POLARITY; + WREG32(DC_HPD3_INT_CONTROL, tmp); + tmp = RREG32(DC_HPD4_INT_CONTROL) & DC_HPDx_INT_POLARITY; + WREG32(DC_HPD4_INT_CONTROL, tmp); + tmp = RREG32(DC_HPD5_INT_CONTROL) & DC_HPDx_INT_POLARITY; + WREG32(DC_HPD5_INT_CONTROL, tmp); + tmp = RREG32(DC_HPD6_INT_CONTROL) & DC_HPDx_INT_POLARITY; + WREG32(DC_HPD6_INT_CONTROL, tmp); + +} + +static int si_irq_init(struct radeon_device *rdev) +{ + int ret = 0; + int rb_bufsz; + u32 interrupt_cntl, ih_cntl, ih_rb_cntl; + + /* allocate ring */ + ret = r600_ih_ring_alloc(rdev); + if (ret) + return ret; + + /* disable irqs */ + si_disable_interrupts(rdev); + + /* init rlc */ + ret = si_rlc_resume(rdev); + if (ret) { + r600_ih_ring_fini(rdev); + return ret; + } + + /* setup interrupt control */ + /* set dummy read address to ring address */ + WREG32(INTERRUPT_CNTL2, rdev->ih.gpu_addr >> 8); + interrupt_cntl = RREG32(INTERRUPT_CNTL); + /* IH_DUMMY_RD_OVERRIDE=0 - dummy read disabled with msi, enabled without msi + * IH_DUMMY_RD_OVERRIDE=1 - dummy read controlled by IH_DUMMY_RD_EN + */ + interrupt_cntl &= ~IH_DUMMY_RD_OVERRIDE; + /* IH_REQ_NONSNOOP_EN=1 if ring is in non-cacheable memory, e.g., vram */ + interrupt_cntl &= ~IH_REQ_NONSNOOP_EN; + WREG32(INTERRUPT_CNTL, interrupt_cntl); + + WREG32(IH_RB_BASE, rdev->ih.gpu_addr >> 8); + rb_bufsz = drm_order(rdev->ih.ring_size / 4); + + ih_rb_cntl = (IH_WPTR_OVERFLOW_ENABLE | + IH_WPTR_OVERFLOW_CLEAR | + (rb_bufsz << 1)); + + if (rdev->wb.enabled) + ih_rb_cntl |= IH_WPTR_WRITEBACK_ENABLE; + + /* set the writeback address whether it's enabled or not */ + WREG32(IH_RB_WPTR_ADDR_LO, (rdev->wb.gpu_addr + R600_WB_IH_WPTR_OFFSET) & 0xFFFFFFFC); + WREG32(IH_RB_WPTR_ADDR_HI, upper_32_bits(rdev->wb.gpu_addr + R600_WB_IH_WPTR_OFFSET) & 0xFF); + + WREG32(IH_RB_CNTL, ih_rb_cntl); + + /* set rptr, wptr to 0 */ + WREG32(IH_RB_RPTR, 0); + WREG32(IH_RB_WPTR, 0); + + /* Default settings for IH_CNTL (disabled at first) */ + ih_cntl = MC_WRREQ_CREDIT(0x10) | MC_WR_CLEAN_CNT(0x10) | MC_VMID(0); + /* RPTR_REARM only works if msi's are enabled */ + if (rdev->msi_enabled) + ih_cntl |= RPTR_REARM; + WREG32(IH_CNTL, ih_cntl); + + /* force the active interrupt state to all disabled */ + si_disable_interrupt_state(rdev); + + pci_enable_busmaster(rdev->dev); + + /* enable irqs */ + si_enable_interrupts(rdev); + + return ret; +} + +int si_irq_set(struct radeon_device *rdev) +{ + u32 cp_int_cntl = CNTX_BUSY_INT_ENABLE | CNTX_EMPTY_INT_ENABLE; + u32 cp_int_cntl1 = 0, cp_int_cntl2 = 0; + u32 crtc1 = 0, crtc2 = 0, crtc3 = 0, crtc4 = 0, crtc5 = 0, crtc6 = 0; + u32 hpd1, hpd2, hpd3, hpd4, hpd5, hpd6; + u32 grbm_int_cntl = 0; + u32 grph1 = 0, grph2 = 0, grph3 = 0, grph4 = 0, grph5 = 0, grph6 = 0; + u32 dma_cntl, dma_cntl1; + + if (!rdev->irq.installed) { + DRM_ERROR("Can't enable IRQ/MSI because no handler is installed\n"); + return -EINVAL; + } + /* don't enable anything if the ih is disabled */ + if (!rdev->ih.enabled) { + si_disable_interrupts(rdev); + /* force the active interrupt state to all disabled */ + si_disable_interrupt_state(rdev); + return 0; + } + + hpd1 = RREG32(DC_HPD1_INT_CONTROL) & ~DC_HPDx_INT_EN; + hpd2 = RREG32(DC_HPD2_INT_CONTROL) & ~DC_HPDx_INT_EN; + hpd3 = RREG32(DC_HPD3_INT_CONTROL) & ~DC_HPDx_INT_EN; + hpd4 = RREG32(DC_HPD4_INT_CONTROL) & ~DC_HPDx_INT_EN; + hpd5 = RREG32(DC_HPD5_INT_CONTROL) & ~DC_HPDx_INT_EN; + hpd6 = RREG32(DC_HPD6_INT_CONTROL) & ~DC_HPDx_INT_EN; + + dma_cntl = RREG32(DMA_CNTL + DMA0_REGISTER_OFFSET) & ~TRAP_ENABLE; + dma_cntl1 = RREG32(DMA_CNTL + DMA1_REGISTER_OFFSET) & ~TRAP_ENABLE; + + /* enable CP interrupts on all rings */ + if (atomic_read(&rdev->irq.ring_int[RADEON_RING_TYPE_GFX_INDEX])) { + DRM_DEBUG("si_irq_set: sw int gfx\n"); + cp_int_cntl |= TIME_STAMP_INT_ENABLE; + } + if (atomic_read(&rdev->irq.ring_int[CAYMAN_RING_TYPE_CP1_INDEX])) { + DRM_DEBUG("si_irq_set: sw int cp1\n"); + cp_int_cntl1 |= TIME_STAMP_INT_ENABLE; + } + if (atomic_read(&rdev->irq.ring_int[CAYMAN_RING_TYPE_CP2_INDEX])) { + DRM_DEBUG("si_irq_set: sw int cp2\n"); + cp_int_cntl2 |= TIME_STAMP_INT_ENABLE; + } + if (atomic_read(&rdev->irq.ring_int[R600_RING_TYPE_DMA_INDEX])) { + DRM_DEBUG("si_irq_set: sw int dma\n"); + dma_cntl |= TRAP_ENABLE; + } + + if (atomic_read(&rdev->irq.ring_int[CAYMAN_RING_TYPE_DMA1_INDEX])) { + DRM_DEBUG("si_irq_set: sw int dma1\n"); + dma_cntl1 |= TRAP_ENABLE; + } + if (rdev->irq.crtc_vblank_int[0] || + atomic_read(&rdev->irq.pflip[0])) { + DRM_DEBUG("si_irq_set: vblank 0\n"); + crtc1 |= VBLANK_INT_MASK; + } + if (rdev->irq.crtc_vblank_int[1] || + atomic_read(&rdev->irq.pflip[1])) { + DRM_DEBUG("si_irq_set: vblank 1\n"); + crtc2 |= VBLANK_INT_MASK; + } + if (rdev->irq.crtc_vblank_int[2] || + atomic_read(&rdev->irq.pflip[2])) { + DRM_DEBUG("si_irq_set: vblank 2\n"); + crtc3 |= VBLANK_INT_MASK; + } + if (rdev->irq.crtc_vblank_int[3] || + atomic_read(&rdev->irq.pflip[3])) { + DRM_DEBUG("si_irq_set: vblank 3\n"); + crtc4 |= VBLANK_INT_MASK; + } + if (rdev->irq.crtc_vblank_int[4] || + atomic_read(&rdev->irq.pflip[4])) { + DRM_DEBUG("si_irq_set: vblank 4\n"); + crtc5 |= VBLANK_INT_MASK; + } + if (rdev->irq.crtc_vblank_int[5] || + atomic_read(&rdev->irq.pflip[5])) { + DRM_DEBUG("si_irq_set: vblank 5\n"); + crtc6 |= VBLANK_INT_MASK; + } + if (rdev->irq.hpd[0]) { + DRM_DEBUG("si_irq_set: hpd 1\n"); + hpd1 |= DC_HPDx_INT_EN; + } + if (rdev->irq.hpd[1]) { + DRM_DEBUG("si_irq_set: hpd 2\n"); + hpd2 |= DC_HPDx_INT_EN; + } + if (rdev->irq.hpd[2]) { + DRM_DEBUG("si_irq_set: hpd 3\n"); + hpd3 |= DC_HPDx_INT_EN; + } + if (rdev->irq.hpd[3]) { + DRM_DEBUG("si_irq_set: hpd 4\n"); + hpd4 |= DC_HPDx_INT_EN; + } + if (rdev->irq.hpd[4]) { + DRM_DEBUG("si_irq_set: hpd 5\n"); + hpd5 |= DC_HPDx_INT_EN; + } + if (rdev->irq.hpd[5]) { + DRM_DEBUG("si_irq_set: hpd 6\n"); + hpd6 |= DC_HPDx_INT_EN; + } + + WREG32(CP_INT_CNTL_RING0, cp_int_cntl); + WREG32(CP_INT_CNTL_RING1, cp_int_cntl1); + WREG32(CP_INT_CNTL_RING2, cp_int_cntl2); + + WREG32(DMA_CNTL + DMA0_REGISTER_OFFSET, dma_cntl); + WREG32(DMA_CNTL + DMA1_REGISTER_OFFSET, dma_cntl1); + + WREG32(GRBM_INT_CNTL, grbm_int_cntl); + + WREG32(INT_MASK + EVERGREEN_CRTC0_REGISTER_OFFSET, crtc1); + WREG32(INT_MASK + EVERGREEN_CRTC1_REGISTER_OFFSET, crtc2); + if (rdev->num_crtc >= 4) { + WREG32(INT_MASK + EVERGREEN_CRTC2_REGISTER_OFFSET, crtc3); + WREG32(INT_MASK + EVERGREEN_CRTC3_REGISTER_OFFSET, crtc4); + } + if (rdev->num_crtc >= 6) { + WREG32(INT_MASK + EVERGREEN_CRTC4_REGISTER_OFFSET, crtc5); + WREG32(INT_MASK + EVERGREEN_CRTC5_REGISTER_OFFSET, crtc6); + } + + WREG32(GRPH_INT_CONTROL + EVERGREEN_CRTC0_REGISTER_OFFSET, grph1); + WREG32(GRPH_INT_CONTROL + EVERGREEN_CRTC1_REGISTER_OFFSET, grph2); + if (rdev->num_crtc >= 4) { + WREG32(GRPH_INT_CONTROL + EVERGREEN_CRTC2_REGISTER_OFFSET, grph3); + WREG32(GRPH_INT_CONTROL + EVERGREEN_CRTC3_REGISTER_OFFSET, grph4); + } + if (rdev->num_crtc >= 6) { + WREG32(GRPH_INT_CONTROL + EVERGREEN_CRTC4_REGISTER_OFFSET, grph5); + WREG32(GRPH_INT_CONTROL + EVERGREEN_CRTC5_REGISTER_OFFSET, grph6); + } + + WREG32(DC_HPD1_INT_CONTROL, hpd1); + WREG32(DC_HPD2_INT_CONTROL, hpd2); + WREG32(DC_HPD3_INT_CONTROL, hpd3); + WREG32(DC_HPD4_INT_CONTROL, hpd4); + WREG32(DC_HPD5_INT_CONTROL, hpd5); + WREG32(DC_HPD6_INT_CONTROL, hpd6); + + return 0; +} + +static inline void si_irq_ack(struct radeon_device *rdev) +{ + u32 tmp; + + rdev->irq.stat_regs.evergreen.disp_int = RREG32(DISP_INTERRUPT_STATUS); + rdev->irq.stat_regs.evergreen.disp_int_cont = RREG32(DISP_INTERRUPT_STATUS_CONTINUE); + rdev->irq.stat_regs.evergreen.disp_int_cont2 = RREG32(DISP_INTERRUPT_STATUS_CONTINUE2); + rdev->irq.stat_regs.evergreen.disp_int_cont3 = RREG32(DISP_INTERRUPT_STATUS_CONTINUE3); + rdev->irq.stat_regs.evergreen.disp_int_cont4 = RREG32(DISP_INTERRUPT_STATUS_CONTINUE4); + rdev->irq.stat_regs.evergreen.disp_int_cont5 = RREG32(DISP_INTERRUPT_STATUS_CONTINUE5); + rdev->irq.stat_regs.evergreen.d1grph_int = RREG32(GRPH_INT_STATUS + EVERGREEN_CRTC0_REGISTER_OFFSET); + rdev->irq.stat_regs.evergreen.d2grph_int = RREG32(GRPH_INT_STATUS + EVERGREEN_CRTC1_REGISTER_OFFSET); + if (rdev->num_crtc >= 4) { + rdev->irq.stat_regs.evergreen.d3grph_int = RREG32(GRPH_INT_STATUS + EVERGREEN_CRTC2_REGISTER_OFFSET); + rdev->irq.stat_regs.evergreen.d4grph_int = RREG32(GRPH_INT_STATUS + EVERGREEN_CRTC3_REGISTER_OFFSET); + } + if (rdev->num_crtc >= 6) { + rdev->irq.stat_regs.evergreen.d5grph_int = RREG32(GRPH_INT_STATUS + EVERGREEN_CRTC4_REGISTER_OFFSET); + rdev->irq.stat_regs.evergreen.d6grph_int = RREG32(GRPH_INT_STATUS + EVERGREEN_CRTC5_REGISTER_OFFSET); + } + + if (rdev->irq.stat_regs.evergreen.d1grph_int & GRPH_PFLIP_INT_OCCURRED) + WREG32(GRPH_INT_STATUS + EVERGREEN_CRTC0_REGISTER_OFFSET, GRPH_PFLIP_INT_CLEAR); + if (rdev->irq.stat_regs.evergreen.d2grph_int & GRPH_PFLIP_INT_OCCURRED) + WREG32(GRPH_INT_STATUS + EVERGREEN_CRTC1_REGISTER_OFFSET, GRPH_PFLIP_INT_CLEAR); + if (rdev->irq.stat_regs.evergreen.disp_int & LB_D1_VBLANK_INTERRUPT) + WREG32(VBLANK_STATUS + EVERGREEN_CRTC0_REGISTER_OFFSET, VBLANK_ACK); + if (rdev->irq.stat_regs.evergreen.disp_int & LB_D1_VLINE_INTERRUPT) + WREG32(VLINE_STATUS + EVERGREEN_CRTC0_REGISTER_OFFSET, VLINE_ACK); + if (rdev->irq.stat_regs.evergreen.disp_int_cont & LB_D2_VBLANK_INTERRUPT) + WREG32(VBLANK_STATUS + EVERGREEN_CRTC1_REGISTER_OFFSET, VBLANK_ACK); + if (rdev->irq.stat_regs.evergreen.disp_int_cont & LB_D2_VLINE_INTERRUPT) + WREG32(VLINE_STATUS + EVERGREEN_CRTC1_REGISTER_OFFSET, VLINE_ACK); + + if (rdev->num_crtc >= 4) { + if (rdev->irq.stat_regs.evergreen.d3grph_int & GRPH_PFLIP_INT_OCCURRED) + WREG32(GRPH_INT_STATUS + EVERGREEN_CRTC2_REGISTER_OFFSET, GRPH_PFLIP_INT_CLEAR); + if (rdev->irq.stat_regs.evergreen.d4grph_int & GRPH_PFLIP_INT_OCCURRED) + WREG32(GRPH_INT_STATUS + EVERGREEN_CRTC3_REGISTER_OFFSET, GRPH_PFLIP_INT_CLEAR); + if (rdev->irq.stat_regs.evergreen.disp_int_cont2 & LB_D3_VBLANK_INTERRUPT) + WREG32(VBLANK_STATUS + EVERGREEN_CRTC2_REGISTER_OFFSET, VBLANK_ACK); + if (rdev->irq.stat_regs.evergreen.disp_int_cont2 & LB_D3_VLINE_INTERRUPT) + WREG32(VLINE_STATUS + EVERGREEN_CRTC2_REGISTER_OFFSET, VLINE_ACK); + if (rdev->irq.stat_regs.evergreen.disp_int_cont3 & LB_D4_VBLANK_INTERRUPT) + WREG32(VBLANK_STATUS + EVERGREEN_CRTC3_REGISTER_OFFSET, VBLANK_ACK); + if (rdev->irq.stat_regs.evergreen.disp_int_cont3 & LB_D4_VLINE_INTERRUPT) + WREG32(VLINE_STATUS + EVERGREEN_CRTC3_REGISTER_OFFSET, VLINE_ACK); + } + + if (rdev->num_crtc >= 6) { + if (rdev->irq.stat_regs.evergreen.d5grph_int & GRPH_PFLIP_INT_OCCURRED) + WREG32(GRPH_INT_STATUS + EVERGREEN_CRTC4_REGISTER_OFFSET, GRPH_PFLIP_INT_CLEAR); + if (rdev->irq.stat_regs.evergreen.d6grph_int & GRPH_PFLIP_INT_OCCURRED) + WREG32(GRPH_INT_STATUS + EVERGREEN_CRTC5_REGISTER_OFFSET, GRPH_PFLIP_INT_CLEAR); + if (rdev->irq.stat_regs.evergreen.disp_int_cont4 & LB_D5_VBLANK_INTERRUPT) + WREG32(VBLANK_STATUS + EVERGREEN_CRTC4_REGISTER_OFFSET, VBLANK_ACK); + if (rdev->irq.stat_regs.evergreen.disp_int_cont4 & LB_D5_VLINE_INTERRUPT) + WREG32(VLINE_STATUS + EVERGREEN_CRTC4_REGISTER_OFFSET, VLINE_ACK); + if (rdev->irq.stat_regs.evergreen.disp_int_cont5 & LB_D6_VBLANK_INTERRUPT) + WREG32(VBLANK_STATUS + EVERGREEN_CRTC5_REGISTER_OFFSET, VBLANK_ACK); + if (rdev->irq.stat_regs.evergreen.disp_int_cont5 & LB_D6_VLINE_INTERRUPT) + WREG32(VLINE_STATUS + EVERGREEN_CRTC5_REGISTER_OFFSET, VLINE_ACK); + } + + if (rdev->irq.stat_regs.evergreen.disp_int & DC_HPD1_INTERRUPT) { + tmp = RREG32(DC_HPD1_INT_CONTROL); + tmp |= DC_HPDx_INT_ACK; + WREG32(DC_HPD1_INT_CONTROL, tmp); + } + if (rdev->irq.stat_regs.evergreen.disp_int_cont & DC_HPD2_INTERRUPT) { + tmp = RREG32(DC_HPD2_INT_CONTROL); + tmp |= DC_HPDx_INT_ACK; + WREG32(DC_HPD2_INT_CONTROL, tmp); + } + if (rdev->irq.stat_regs.evergreen.disp_int_cont2 & DC_HPD3_INTERRUPT) { + tmp = RREG32(DC_HPD3_INT_CONTROL); + tmp |= DC_HPDx_INT_ACK; + WREG32(DC_HPD3_INT_CONTROL, tmp); + } + if (rdev->irq.stat_regs.evergreen.disp_int_cont3 & DC_HPD4_INTERRUPT) { + tmp = RREG32(DC_HPD4_INT_CONTROL); + tmp |= DC_HPDx_INT_ACK; + WREG32(DC_HPD4_INT_CONTROL, tmp); + } + if (rdev->irq.stat_regs.evergreen.disp_int_cont4 & DC_HPD5_INTERRUPT) { + tmp = RREG32(DC_HPD5_INT_CONTROL); + tmp |= DC_HPDx_INT_ACK; + WREG32(DC_HPD5_INT_CONTROL, tmp); + } + if (rdev->irq.stat_regs.evergreen.disp_int_cont5 & DC_HPD6_INTERRUPT) { + tmp = RREG32(DC_HPD5_INT_CONTROL); + tmp |= DC_HPDx_INT_ACK; + WREG32(DC_HPD6_INT_CONTROL, tmp); + } +} + +static void si_irq_disable(struct radeon_device *rdev) +{ + si_disable_interrupts(rdev); + /* Wait and acknowledge irq */ + DRM_MDELAY(1); + si_irq_ack(rdev); + si_disable_interrupt_state(rdev); +} + +static void si_irq_suspend(struct radeon_device *rdev) +{ + si_irq_disable(rdev); + si_rlc_stop(rdev); +} + +static void si_irq_fini(struct radeon_device *rdev) +{ + si_irq_suspend(rdev); + r600_ih_ring_fini(rdev); +} + +static inline u32 si_get_ih_wptr(struct radeon_device *rdev) +{ + u32 wptr, tmp; + + if (rdev->wb.enabled) + wptr = le32_to_cpu(rdev->wb.wb[R600_WB_IH_WPTR_OFFSET/4]); + else + wptr = RREG32(IH_RB_WPTR); + + if (wptr & RB_OVERFLOW) { + /* When a ring buffer overflow happen start parsing interrupt + * from the last not overwritten vector (wptr + 16). Hopefully + * this should allow us to catchup. + */ + dev_warn(rdev->dev, "IH ring buffer overflow (0x%08X, %d, %d)\n", + wptr, rdev->ih.rptr, (wptr + 16) + rdev->ih.ptr_mask); + rdev->ih.rptr = (wptr + 16) & rdev->ih.ptr_mask; + tmp = RREG32(IH_RB_CNTL); + tmp |= IH_WPTR_OVERFLOW_CLEAR; + WREG32(IH_RB_CNTL, tmp); + } + return (wptr & rdev->ih.ptr_mask); +} + +/* SI IV Ring + * Each IV ring entry is 128 bits: + * [7:0] - interrupt source id + * [31:8] - reserved + * [59:32] - interrupt source data + * [63:60] - reserved + * [71:64] - RINGID + * [79:72] - VMID + * [127:80] - reserved + */ +irqreturn_t si_irq_process(struct radeon_device *rdev) +{ + u32 wptr; + u32 rptr; + u32 src_id, src_data, ring_id; + u32 ring_index; + bool queue_hotplug = false; + + if (!rdev->ih.enabled || rdev->shutdown) + return IRQ_NONE; + + wptr = si_get_ih_wptr(rdev); + +restart_ih: + /* is somebody else already processing irqs? */ + if (atomic_xchg(&rdev->ih.lock, 1)) + return IRQ_NONE; + + rptr = rdev->ih.rptr; + DRM_DEBUG("si_irq_process start: rptr %d, wptr %d\n", rptr, wptr); + + /* Order reading of wptr vs. reading of IH ring data */ + rmb(); + + /* display interrupts */ + si_irq_ack(rdev); + + while (rptr != wptr) { + /* wptr/rptr are in bytes! */ + ring_index = rptr / 4; + src_id = le32_to_cpu(rdev->ih.ring[ring_index]) & 0xff; + src_data = le32_to_cpu(rdev->ih.ring[ring_index + 1]) & 0xfffffff; + ring_id = le32_to_cpu(rdev->ih.ring[ring_index + 2]) & 0xff; + + switch (src_id) { + case 1: /* D1 vblank/vline */ + switch (src_data) { + case 0: /* D1 vblank */ + if (rdev->irq.stat_regs.evergreen.disp_int & LB_D1_VBLANK_INTERRUPT) { + if (rdev->irq.crtc_vblank_int[0]) { + drm_handle_vblank(rdev->ddev, 0); + rdev->pm.vblank_sync = true; + DRM_WAKEUP(&rdev->irq.vblank_queue); + } + if (atomic_read(&rdev->irq.pflip[0])) + radeon_crtc_handle_flip(rdev, 0); + rdev->irq.stat_regs.evergreen.disp_int &= ~LB_D1_VBLANK_INTERRUPT; + DRM_DEBUG("IH: D1 vblank\n"); + } + break; + case 1: /* D1 vline */ + if (rdev->irq.stat_regs.evergreen.disp_int & LB_D1_VLINE_INTERRUPT) { + rdev->irq.stat_regs.evergreen.disp_int &= ~LB_D1_VLINE_INTERRUPT; + DRM_DEBUG("IH: D1 vline\n"); + } + break; + default: + DRM_DEBUG("Unhandled interrupt: %d %d\n", src_id, src_data); + break; + } + break; + case 2: /* D2 vblank/vline */ + switch (src_data) { + case 0: /* D2 vblank */ + if (rdev->irq.stat_regs.evergreen.disp_int_cont & LB_D2_VBLANK_INTERRUPT) { + if (rdev->irq.crtc_vblank_int[1]) { + drm_handle_vblank(rdev->ddev, 1); + rdev->pm.vblank_sync = true; + DRM_WAKEUP(&rdev->irq.vblank_queue); + } + if (atomic_read(&rdev->irq.pflip[1])) + radeon_crtc_handle_flip(rdev, 1); + rdev->irq.stat_regs.evergreen.disp_int_cont &= ~LB_D2_VBLANK_INTERRUPT; + DRM_DEBUG("IH: D2 vblank\n"); + } + break; + case 1: /* D2 vline */ + if (rdev->irq.stat_regs.evergreen.disp_int_cont & LB_D2_VLINE_INTERRUPT) { + rdev->irq.stat_regs.evergreen.disp_int_cont &= ~LB_D2_VLINE_INTERRUPT; + DRM_DEBUG("IH: D2 vline\n"); + } + break; + default: + DRM_DEBUG("Unhandled interrupt: %d %d\n", src_id, src_data); + break; + } + break; + case 3: /* D3 vblank/vline */ + switch (src_data) { + case 0: /* D3 vblank */ + if (rdev->irq.stat_regs.evergreen.disp_int_cont2 & LB_D3_VBLANK_INTERRUPT) { + if (rdev->irq.crtc_vblank_int[2]) { + drm_handle_vblank(rdev->ddev, 2); + rdev->pm.vblank_sync = true; + DRM_WAKEUP(&rdev->irq.vblank_queue); + } + if (atomic_read(&rdev->irq.pflip[2])) + radeon_crtc_handle_flip(rdev, 2); + rdev->irq.stat_regs.evergreen.disp_int_cont2 &= ~LB_D3_VBLANK_INTERRUPT; + DRM_DEBUG("IH: D3 vblank\n"); + } + break; + case 1: /* D3 vline */ + if (rdev->irq.stat_regs.evergreen.disp_int_cont2 & LB_D3_VLINE_INTERRUPT) { + rdev->irq.stat_regs.evergreen.disp_int_cont2 &= ~LB_D3_VLINE_INTERRUPT; + DRM_DEBUG("IH: D3 vline\n"); + } + break; + default: + DRM_DEBUG("Unhandled interrupt: %d %d\n", src_id, src_data); + break; + } + break; + case 4: /* D4 vblank/vline */ + switch (src_data) { + case 0: /* D4 vblank */ + if (rdev->irq.stat_regs.evergreen.disp_int_cont3 & LB_D4_VBLANK_INTERRUPT) { + if (rdev->irq.crtc_vblank_int[3]) { + drm_handle_vblank(rdev->ddev, 3); + rdev->pm.vblank_sync = true; + DRM_WAKEUP(&rdev->irq.vblank_queue); + } + if (atomic_read(&rdev->irq.pflip[3])) + radeon_crtc_handle_flip(rdev, 3); + rdev->irq.stat_regs.evergreen.disp_int_cont3 &= ~LB_D4_VBLANK_INTERRUPT; + DRM_DEBUG("IH: D4 vblank\n"); + } + break; + case 1: /* D4 vline */ + if (rdev->irq.stat_regs.evergreen.disp_int_cont3 & LB_D4_VLINE_INTERRUPT) { + rdev->irq.stat_regs.evergreen.disp_int_cont3 &= ~LB_D4_VLINE_INTERRUPT; + DRM_DEBUG("IH: D4 vline\n"); + } + break; + default: + DRM_DEBUG("Unhandled interrupt: %d %d\n", src_id, src_data); + break; + } + break; + case 5: /* D5 vblank/vline */ + switch (src_data) { + case 0: /* D5 vblank */ + if (rdev->irq.stat_regs.evergreen.disp_int_cont4 & LB_D5_VBLANK_INTERRUPT) { + if (rdev->irq.crtc_vblank_int[4]) { + drm_handle_vblank(rdev->ddev, 4); + rdev->pm.vblank_sync = true; + DRM_WAKEUP(&rdev->irq.vblank_queue); + } + if (atomic_read(&rdev->irq.pflip[4])) + radeon_crtc_handle_flip(rdev, 4); + rdev->irq.stat_regs.evergreen.disp_int_cont4 &= ~LB_D5_VBLANK_INTERRUPT; + DRM_DEBUG("IH: D5 vblank\n"); + } + break; + case 1: /* D5 vline */ + if (rdev->irq.stat_regs.evergreen.disp_int_cont4 & LB_D5_VLINE_INTERRUPT) { + rdev->irq.stat_regs.evergreen.disp_int_cont4 &= ~LB_D5_VLINE_INTERRUPT; + DRM_DEBUG("IH: D5 vline\n"); + } + break; + default: + DRM_DEBUG("Unhandled interrupt: %d %d\n", src_id, src_data); + break; + } + break; + case 6: /* D6 vblank/vline */ + switch (src_data) { + case 0: /* D6 vblank */ + if (rdev->irq.stat_regs.evergreen.disp_int_cont5 & LB_D6_VBLANK_INTERRUPT) { + if (rdev->irq.crtc_vblank_int[5]) { + drm_handle_vblank(rdev->ddev, 5); + rdev->pm.vblank_sync = true; + DRM_WAKEUP(&rdev->irq.vblank_queue); + } + if (atomic_read(&rdev->irq.pflip[5])) + radeon_crtc_handle_flip(rdev, 5); + rdev->irq.stat_regs.evergreen.disp_int_cont5 &= ~LB_D6_VBLANK_INTERRUPT; + DRM_DEBUG("IH: D6 vblank\n"); + } + break; + case 1: /* D6 vline */ + if (rdev->irq.stat_regs.evergreen.disp_int_cont5 & LB_D6_VLINE_INTERRUPT) { + rdev->irq.stat_regs.evergreen.disp_int_cont5 &= ~LB_D6_VLINE_INTERRUPT; + DRM_DEBUG("IH: D6 vline\n"); + } + break; + default: + DRM_DEBUG("Unhandled interrupt: %d %d\n", src_id, src_data); + break; + } + break; + case 42: /* HPD hotplug */ + switch (src_data) { + case 0: + if (rdev->irq.stat_regs.evergreen.disp_int & DC_HPD1_INTERRUPT) { + rdev->irq.stat_regs.evergreen.disp_int &= ~DC_HPD1_INTERRUPT; + queue_hotplug = true; + DRM_DEBUG("IH: HPD1\n"); + } + break; + case 1: + if (rdev->irq.stat_regs.evergreen.disp_int_cont & DC_HPD2_INTERRUPT) { + rdev->irq.stat_regs.evergreen.disp_int_cont &= ~DC_HPD2_INTERRUPT; + queue_hotplug = true; + DRM_DEBUG("IH: HPD2\n"); + } + break; + case 2: + if (rdev->irq.stat_regs.evergreen.disp_int_cont2 & DC_HPD3_INTERRUPT) { + rdev->irq.stat_regs.evergreen.disp_int_cont2 &= ~DC_HPD3_INTERRUPT; + queue_hotplug = true; + DRM_DEBUG("IH: HPD3\n"); + } + break; + case 3: + if (rdev->irq.stat_regs.evergreen.disp_int_cont3 & DC_HPD4_INTERRUPT) { + rdev->irq.stat_regs.evergreen.disp_int_cont3 &= ~DC_HPD4_INTERRUPT; + queue_hotplug = true; + DRM_DEBUG("IH: HPD4\n"); + } + break; + case 4: + if (rdev->irq.stat_regs.evergreen.disp_int_cont4 & DC_HPD5_INTERRUPT) { + rdev->irq.stat_regs.evergreen.disp_int_cont4 &= ~DC_HPD5_INTERRUPT; + queue_hotplug = true; + DRM_DEBUG("IH: HPD5\n"); + } + break; + case 5: + if (rdev->irq.stat_regs.evergreen.disp_int_cont5 & DC_HPD6_INTERRUPT) { + rdev->irq.stat_regs.evergreen.disp_int_cont5 &= ~DC_HPD6_INTERRUPT; + queue_hotplug = true; + DRM_DEBUG("IH: HPD6\n"); + } + break; + default: + DRM_DEBUG("Unhandled interrupt: %d %d\n", src_id, src_data); + break; + } + break; + case 146: + case 147: + dev_err(rdev->dev, "GPU fault detected: %d 0x%08x\n", src_id, src_data); + dev_err(rdev->dev, " VM_CONTEXT1_PROTECTION_FAULT_ADDR 0x%08X\n", + RREG32(VM_CONTEXT1_PROTECTION_FAULT_ADDR)); + dev_err(rdev->dev, " VM_CONTEXT1_PROTECTION_FAULT_STATUS 0x%08X\n", + RREG32(VM_CONTEXT1_PROTECTION_FAULT_STATUS)); + /* reset addr and status */ + WREG32_P(VM_CONTEXT1_CNTL2, 1, ~1); + break; + case 176: /* RINGID0 CP_INT */ + radeon_fence_process(rdev, RADEON_RING_TYPE_GFX_INDEX); + break; + case 177: /* RINGID1 CP_INT */ + radeon_fence_process(rdev, CAYMAN_RING_TYPE_CP1_INDEX); + break; + case 178: /* RINGID2 CP_INT */ + radeon_fence_process(rdev, CAYMAN_RING_TYPE_CP2_INDEX); + break; + case 181: /* CP EOP event */ + DRM_DEBUG("IH: CP EOP\n"); + switch (ring_id) { + case 0: + radeon_fence_process(rdev, RADEON_RING_TYPE_GFX_INDEX); + break; + case 1: + radeon_fence_process(rdev, CAYMAN_RING_TYPE_CP1_INDEX); + break; + case 2: + radeon_fence_process(rdev, CAYMAN_RING_TYPE_CP2_INDEX); + break; + } + break; + case 224: /* DMA trap event */ + DRM_DEBUG("IH: DMA trap\n"); + radeon_fence_process(rdev, R600_RING_TYPE_DMA_INDEX); + break; + case 233: /* GUI IDLE */ + DRM_DEBUG("IH: GUI idle\n"); + break; + case 244: /* DMA trap event */ + DRM_DEBUG("IH: DMA1 trap\n"); + radeon_fence_process(rdev, CAYMAN_RING_TYPE_DMA1_INDEX); + break; + default: + DRM_DEBUG("Unhandled interrupt: %d %d\n", src_id, src_data); + break; + } + + /* wptr/rptr are in bytes! */ + rptr += 16; + rptr &= rdev->ih.ptr_mask; + } + if (queue_hotplug) + taskqueue_enqueue(rdev->tq, &rdev->hotplug_work); + rdev->ih.rptr = rptr; + WREG32(IH_RB_RPTR, rdev->ih.rptr); + atomic_set(&rdev->ih.lock, 0); + + /* make sure wptr hasn't changed while processing */ + wptr = si_get_ih_wptr(rdev); + if (wptr != rptr) + goto restart_ih; + + return IRQ_HANDLED; +} + +/** + * si_copy_dma - copy pages using the DMA engine + * + * @rdev: radeon_device pointer + * @src_offset: src GPU address + * @dst_offset: dst GPU address + * @num_gpu_pages: number of GPU pages to xfer + * @fence: radeon fence object + * + * Copy GPU paging using the DMA engine (SI). + * Used by the radeon ttm implementation to move pages if + * registered as the asic copy callback. + */ +int si_copy_dma(struct radeon_device *rdev, + uint64_t src_offset, uint64_t dst_offset, + unsigned num_gpu_pages, + struct radeon_fence **fence) +{ + struct radeon_semaphore *sem = NULL; + int ring_index = rdev->asic->copy.dma_ring_index; + struct radeon_ring *ring = &rdev->ring[ring_index]; + u32 size_in_bytes, cur_size_in_bytes; + int i, num_loops; + int r = 0; + + r = radeon_semaphore_create(rdev, &sem); + if (r) { + DRM_ERROR("radeon: moving bo (%d).\n", r); + return r; + } + + size_in_bytes = (num_gpu_pages << RADEON_GPU_PAGE_SHIFT); + num_loops = DIV_ROUND_UP(size_in_bytes, 0xfffff); + r = radeon_ring_lock(rdev, ring, num_loops * 5 + 11); + if (r) { + DRM_ERROR("radeon: moving bo (%d).\n", r); + radeon_semaphore_free(rdev, &sem, NULL); + return r; + } + + if (radeon_fence_need_sync(*fence, ring->idx)) { + radeon_semaphore_sync_rings(rdev, sem, (*fence)->ring, + ring->idx); + radeon_fence_note_sync(*fence, ring->idx); + } else { + radeon_semaphore_free(rdev, &sem, NULL); + } + + for (i = 0; i < num_loops; i++) { + cur_size_in_bytes = size_in_bytes; + if (cur_size_in_bytes > 0xFFFFF) + cur_size_in_bytes = 0xFFFFF; + size_in_bytes -= cur_size_in_bytes; + radeon_ring_write(ring, DMA_PACKET(DMA_PACKET_COPY, 1, 0, 0, cur_size_in_bytes)); + radeon_ring_write(ring, dst_offset & 0xffffffff); + radeon_ring_write(ring, src_offset & 0xffffffff); + radeon_ring_write(ring, upper_32_bits(dst_offset) & 0xff); + radeon_ring_write(ring, upper_32_bits(src_offset) & 0xff); + src_offset += cur_size_in_bytes; + dst_offset += cur_size_in_bytes; + } + + r = radeon_fence_emit(rdev, fence, ring->idx); + if (r) { + radeon_ring_unlock_undo(rdev, ring); + return r; + } + + radeon_ring_unlock_commit(rdev, ring); + radeon_semaphore_free(rdev, &sem, *fence); + + return r; +} + +/* + * startup/shutdown callbacks + */ +static int si_startup(struct radeon_device *rdev) +{ + struct radeon_ring *ring; + int r; + + if (!rdev->me_fw || !rdev->pfp_fw || !rdev->ce_fw || + !rdev->rlc_fw || !rdev->mc_fw) { + r = si_init_microcode(rdev); + if (r) { + DRM_ERROR("Failed to load firmware!\n"); + return r; + } + } + + r = si_mc_load_microcode(rdev); + if (r) { + DRM_ERROR("Failed to load MC firmware!\n"); + return r; + } + + r = r600_vram_scratch_init(rdev); + if (r) + return r; + + si_mc_program(rdev); + r = si_pcie_gart_enable(rdev); + if (r) + return r; + si_gpu_init(rdev); + +#if 0 + r = evergreen_blit_init(rdev); + if (r) { + r600_blit_fini(rdev); + rdev->asic->copy = NULL; + dev_warn(rdev->dev, "failed blitter (%d) falling back to memcpy\n", r); + } +#endif + /* allocate rlc buffers */ + r = si_rlc_init(rdev); + if (r) { + DRM_ERROR("Failed to init rlc BOs!\n"); + return r; + } + + /* allocate wb buffer */ + r = radeon_wb_init(rdev); + if (r) + return r; + + r = radeon_fence_driver_start_ring(rdev, RADEON_RING_TYPE_GFX_INDEX); + if (r) { + dev_err(rdev->dev, "failed initializing CP fences (%d).\n", r); + return r; + } + + r = radeon_fence_driver_start_ring(rdev, CAYMAN_RING_TYPE_CP1_INDEX); + if (r) { + dev_err(rdev->dev, "failed initializing CP fences (%d).\n", r); + return r; + } + + r = radeon_fence_driver_start_ring(rdev, CAYMAN_RING_TYPE_CP2_INDEX); + if (r) { + dev_err(rdev->dev, "failed initializing CP fences (%d).\n", r); + return r; + } + + r = radeon_fence_driver_start_ring(rdev, R600_RING_TYPE_DMA_INDEX); + if (r) { + dev_err(rdev->dev, "failed initializing DMA fences (%d).\n", r); + return r; + } + + r = radeon_fence_driver_start_ring(rdev, CAYMAN_RING_TYPE_DMA1_INDEX); + if (r) { + dev_err(rdev->dev, "failed initializing DMA fences (%d).\n", r); + return r; + } + + /* Enable IRQ */ + r = si_irq_init(rdev); + if (r) { + DRM_ERROR("radeon: IH init failed (%d).\n", r); + radeon_irq_kms_fini(rdev); + return r; + } + si_irq_set(rdev); + + ring = &rdev->ring[RADEON_RING_TYPE_GFX_INDEX]; + r = radeon_ring_init(rdev, ring, ring->ring_size, RADEON_WB_CP_RPTR_OFFSET, + CP_RB0_RPTR, CP_RB0_WPTR, + 0, 0xfffff, RADEON_CP_PACKET2); + if (r) + return r; + + ring = &rdev->ring[CAYMAN_RING_TYPE_CP1_INDEX]; + r = radeon_ring_init(rdev, ring, ring->ring_size, RADEON_WB_CP1_RPTR_OFFSET, + CP_RB1_RPTR, CP_RB1_WPTR, + 0, 0xfffff, RADEON_CP_PACKET2); + if (r) + return r; + + ring = &rdev->ring[CAYMAN_RING_TYPE_CP2_INDEX]; + r = radeon_ring_init(rdev, ring, ring->ring_size, RADEON_WB_CP2_RPTR_OFFSET, + CP_RB2_RPTR, CP_RB2_WPTR, + 0, 0xfffff, RADEON_CP_PACKET2); + if (r) + return r; + + ring = &rdev->ring[R600_RING_TYPE_DMA_INDEX]; + r = radeon_ring_init(rdev, ring, ring->ring_size, R600_WB_DMA_RPTR_OFFSET, + DMA_RB_RPTR + DMA0_REGISTER_OFFSET, + DMA_RB_WPTR + DMA0_REGISTER_OFFSET, + 2, 0x3fffc, DMA_PACKET(DMA_PACKET_NOP, 0, 0, 0, 0)); + if (r) + return r; + + ring = &rdev->ring[CAYMAN_RING_TYPE_DMA1_INDEX]; + r = radeon_ring_init(rdev, ring, ring->ring_size, CAYMAN_WB_DMA1_RPTR_OFFSET, + DMA_RB_RPTR + DMA1_REGISTER_OFFSET, + DMA_RB_WPTR + DMA1_REGISTER_OFFSET, + 2, 0x3fffc, DMA_PACKET(DMA_PACKET_NOP, 0, 0, 0, 0)); + if (r) + return r; + + r = si_cp_load_microcode(rdev); + if (r) + return r; + r = si_cp_resume(rdev); + if (r) + return r; + + r = cayman_dma_resume(rdev); + if (r) + return r; + + r = radeon_ib_pool_init(rdev); + if (r) { + dev_err(rdev->dev, "IB initialization failed (%d).\n", r); + return r; + } + + r = radeon_vm_manager_init(rdev); + if (r) { + dev_err(rdev->dev, "vm manager initialization failed (%d).\n", r); + return r; + } + + return 0; +} + +int si_resume(struct radeon_device *rdev) +{ + int r; + + /* Do not reset GPU before posting, on rv770 hw unlike on r500 hw, + * posting will perform necessary task to bring back GPU into good + * shape. + */ + /* post card */ + atom_asic_init(rdev->mode_info.atom_context); + + rdev->accel_working = true; + r = si_startup(rdev); + if (r) { + DRM_ERROR("si startup failed on resume\n"); + rdev->accel_working = false; + return r; + } + + return r; + +} + +int si_suspend(struct radeon_device *rdev) +{ + si_cp_enable(rdev, false); + cayman_dma_stop(rdev); + si_irq_suspend(rdev); + radeon_wb_disable(rdev); + si_pcie_gart_disable(rdev); + return 0; +} + +/* Plan is to move initialization in that function and use + * helper function so that radeon_device_init pretty much + * do nothing more than calling asic specific function. This + * should also allow to remove a bunch of callback function + * like vram_info. + */ +int si_init(struct radeon_device *rdev) +{ + struct radeon_ring *ring = &rdev->ring[RADEON_RING_TYPE_GFX_INDEX]; + int r; + + /* Read BIOS */ + if (!radeon_get_bios(rdev)) { + if (ASIC_IS_AVIVO(rdev)) + return -EINVAL; + } + /* Must be an ATOMBIOS */ + if (!rdev->is_atom_bios) { + dev_err(rdev->dev, "Expecting atombios for cayman GPU\n"); + return -EINVAL; + } + r = radeon_atombios_init(rdev); + if (r) + return r; + + /* Post card if necessary */ + if (!radeon_card_posted(rdev)) { + if (!rdev->bios) { + dev_err(rdev->dev, "Card not posted and no BIOS - ignoring\n"); + return -EINVAL; + } + DRM_INFO("GPU not posted. posting now...\n"); + atom_asic_init(rdev->mode_info.atom_context); + } + /* Initialize scratch registers */ + si_scratch_init(rdev); + /* Initialize surface registers */ + radeon_surface_init(rdev); + /* Initialize clocks */ + radeon_get_clock_info(rdev->ddev); + + /* Fence driver */ + r = radeon_fence_driver_init(rdev); + if (r) + return r; + + /* initialize memory controller */ + r = si_mc_init(rdev); + if (r) + return r; + /* Memory manager */ + r = radeon_bo_init(rdev); + if (r) + return r; + + r = radeon_irq_kms_init(rdev); + if (r) + return r; + + ring = &rdev->ring[RADEON_RING_TYPE_GFX_INDEX]; + ring->ring_obj = NULL; + r600_ring_init(rdev, ring, 1024 * 1024); + + ring = &rdev->ring[CAYMAN_RING_TYPE_CP1_INDEX]; + ring->ring_obj = NULL; + r600_ring_init(rdev, ring, 1024 * 1024); + + ring = &rdev->ring[CAYMAN_RING_TYPE_CP2_INDEX]; + ring->ring_obj = NULL; + r600_ring_init(rdev, ring, 1024 * 1024); + + ring = &rdev->ring[R600_RING_TYPE_DMA_INDEX]; + ring->ring_obj = NULL; + r600_ring_init(rdev, ring, 64 * 1024); + + ring = &rdev->ring[CAYMAN_RING_TYPE_DMA1_INDEX]; + ring->ring_obj = NULL; + r600_ring_init(rdev, ring, 64 * 1024); + + rdev->ih.ring_obj = NULL; + r600_ih_ring_init(rdev, 64 * 1024); + + r = r600_pcie_gart_init(rdev); + if (r) + return r; + + rdev->accel_working = true; + r = si_startup(rdev); + if (r) { + dev_err(rdev->dev, "disabling GPU acceleration\n"); + si_cp_fini(rdev); + cayman_dma_fini(rdev); + si_irq_fini(rdev); + si_rlc_fini(rdev); + radeon_wb_fini(rdev); + radeon_ib_pool_fini(rdev); + radeon_vm_manager_fini(rdev); + radeon_irq_kms_fini(rdev); + si_pcie_gart_fini(rdev); + rdev->accel_working = false; + } + + /* Don't start up if the MC ucode is missing. + * The default clocks and voltages before the MC ucode + * is loaded are not suffient for advanced operations. + */ + if (!rdev->mc_fw) { + DRM_ERROR("radeon: MC ucode required for NI+.\n"); + return -EINVAL; + } + + return 0; +} + +void si_fini(struct radeon_device *rdev) +{ +#if 0 + r600_blit_fini(rdev); +#endif + si_cp_fini(rdev); + cayman_dma_fini(rdev); + si_irq_fini(rdev); + si_rlc_fini(rdev); + radeon_wb_fini(rdev); + radeon_vm_manager_fini(rdev); + radeon_ib_pool_fini(rdev); + radeon_irq_kms_fini(rdev); + si_pcie_gart_fini(rdev); + r600_vram_scratch_fini(rdev); + radeon_gem_fini(rdev); + radeon_fence_driver_fini(rdev); + radeon_bo_fini(rdev); + radeon_atombios_fini(rdev); + si_fini_microcode(rdev); + free(rdev->bios, DRM_MEM_DRIVER); + rdev->bios = NULL; +} + +/** + * si_get_gpu_clock - return GPU clock counter snapshot + * + * @rdev: radeon_device pointer + * + * Fetches a GPU clock counter snapshot (SI). + * Returns the 64 bit clock counter snapshot. + */ +uint64_t si_get_gpu_clock(struct radeon_device *rdev) +{ + uint64_t clock; + + sx_xlock(&rdev->gpu_clock_mutex); + WREG32(RLC_CAPTURE_GPU_CLOCK_COUNT, 1); + clock = (uint64_t)RREG32(RLC_GPU_CLOCK_COUNT_LSB) | + ((uint64_t)RREG32(RLC_GPU_CLOCK_COUNT_MSB) << 32ULL); + sx_xunlock(&rdev->gpu_clock_mutex); + return clock; +} diff --git a/sys/dev/drm2/radeon/si_blit_shaders.c b/sys/dev/drm2/radeon/si_blit_shaders.c new file mode 100644 index 00000000000..dc31fe307b3 --- /dev/null +++ b/sys/dev/drm2/radeon/si_blit_shaders.c @@ -0,0 +1,254 @@ +/* + * Copyright 2011 Advanced Micro Devices, Inc. + * + * 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 (including the next + * paragraph) 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 COPYRIGHT HOLDER(S) AND/OR ITS SUPPLIERS 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. + * + * Authors: + * Alex Deucher + */ + +#include +__FBSDID("$FreeBSD$"); + +#include + +const u32 si_default_state[] = +{ + 0xc0066900, + 0x00000000, + 0x00000060, /* DB_RENDER_CONTROL */ + 0x00000000, /* DB_COUNT_CONTROL */ + 0x00000000, /* DB_DEPTH_VIEW */ + 0x0000002a, /* DB_RENDER_OVERRIDE */ + 0x00000000, /* DB_RENDER_OVERRIDE2 */ + 0x00000000, /* DB_HTILE_DATA_BASE */ + + 0xc0046900, + 0x00000008, + 0x00000000, /* DB_DEPTH_BOUNDS_MIN */ + 0x00000000, /* DB_DEPTH_BOUNDS_MAX */ + 0x00000000, /* DB_STENCIL_CLEAR */ + 0x00000000, /* DB_DEPTH_CLEAR */ + + 0xc0036900, + 0x0000000f, + 0x00000000, /* DB_DEPTH_INFO */ + 0x00000000, /* DB_Z_INFO */ + 0x00000000, /* DB_STENCIL_INFO */ + + 0xc0016900, + 0x00000080, + 0x00000000, /* PA_SC_WINDOW_OFFSET */ + + 0xc00d6900, + 0x00000083, + 0x0000ffff, /* PA_SC_CLIPRECT_RULE */ + 0x00000000, /* PA_SC_CLIPRECT_0_TL */ + 0x20002000, /* PA_SC_CLIPRECT_0_BR */ + 0x00000000, + 0x20002000, + 0x00000000, + 0x20002000, + 0x00000000, + 0x20002000, + 0xaaaaaaaa, /* PA_SC_EDGERULE */ + 0x00000000, /* PA_SU_HARDWARE_SCREEN_OFFSET */ + 0x0000000f, /* CB_TARGET_MASK */ + 0x0000000f, /* CB_SHADER_MASK */ + + 0xc0226900, + 0x00000094, + 0x80000000, /* PA_SC_VPORT_SCISSOR_0_TL */ + 0x20002000, /* PA_SC_VPORT_SCISSOR_0_BR */ + 0x80000000, + 0x20002000, + 0x80000000, + 0x20002000, + 0x80000000, + 0x20002000, + 0x80000000, + 0x20002000, + 0x80000000, + 0x20002000, + 0x80000000, + 0x20002000, + 0x80000000, + 0x20002000, + 0x80000000, + 0x20002000, + 0x80000000, + 0x20002000, + 0x80000000, + 0x20002000, + 0x80000000, + 0x20002000, + 0x80000000, + 0x20002000, + 0x80000000, + 0x20002000, + 0x80000000, + 0x20002000, + 0x80000000, + 0x20002000, + 0x00000000, /* PA_SC_VPORT_ZMIN_0 */ + 0x3f800000, /* PA_SC_VPORT_ZMAX_0 */ + + 0xc0026900, + 0x000000d9, + 0x00000000, /* CP_RINGID */ + 0x00000000, /* CP_VMID */ + + 0xc0046900, + 0x00000100, + 0xffffffff, /* VGT_MAX_VTX_INDX */ + 0x00000000, /* VGT_MIN_VTX_INDX */ + 0x00000000, /* VGT_INDX_OFFSET */ + 0x00000000, /* VGT_MULTI_PRIM_IB_RESET_INDX */ + + 0xc0046900, + 0x00000105, + 0x00000000, /* CB_BLEND_RED */ + 0x00000000, /* CB_BLEND_GREEN */ + 0x00000000, /* CB_BLEND_BLUE */ + 0x00000000, /* CB_BLEND_ALPHA */ + + 0xc0016900, + 0x000001e0, + 0x00000000, /* CB_BLEND0_CONTROL */ + + 0xc00e6900, + 0x00000200, + 0x00000000, /* DB_DEPTH_CONTROL */ + 0x00000000, /* DB_EQAA */ + 0x00cc0010, /* CB_COLOR_CONTROL */ + 0x00000210, /* DB_SHADER_CONTROL */ + 0x00010000, /* PA_CL_CLIP_CNTL */ + 0x00000004, /* PA_SU_SC_MODE_CNTL */ + 0x00000100, /* PA_CL_VTE_CNTL */ + 0x00000000, /* PA_CL_VS_OUT_CNTL */ + 0x00000000, /* PA_CL_NANINF_CNTL */ + 0x00000000, /* PA_SU_LINE_STIPPLE_CNTL */ + 0x00000000, /* PA_SU_LINE_STIPPLE_SCALE */ + 0x00000000, /* PA_SU_PRIM_FILTER_CNTL */ + 0x00000000, /* */ + 0x00000000, /* */ + + 0xc0116900, + 0x00000280, + 0x00000000, /* PA_SU_POINT_SIZE */ + 0x00000000, /* PA_SU_POINT_MINMAX */ + 0x00000008, /* PA_SU_LINE_CNTL */ + 0x00000000, /* PA_SC_LINE_STIPPLE */ + 0x00000000, /* VGT_OUTPUT_PATH_CNTL */ + 0x00000000, /* VGT_HOS_CNTL */ + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, /* VGT_GS_MODE */ + + 0xc0026900, + 0x00000292, + 0x00000000, /* PA_SC_MODE_CNTL_0 */ + 0x00000000, /* PA_SC_MODE_CNTL_1 */ + + 0xc0016900, + 0x000002a1, + 0x00000000, /* VGT_PRIMITIVEID_EN */ + + 0xc0016900, + 0x000002a5, + 0x00000000, /* VGT_MULTI_PRIM_IB_RESET_EN */ + + 0xc0026900, + 0x000002a8, + 0x00000000, /* VGT_INSTANCE_STEP_RATE_0 */ + 0x00000000, + + 0xc0026900, + 0x000002ad, + 0x00000000, /* VGT_REUSE_OFF */ + 0x00000000, + + 0xc0016900, + 0x000002d5, + 0x00000000, /* VGT_SHADER_STAGES_EN */ + + 0xc0016900, + 0x000002dc, + 0x0000aa00, /* DB_ALPHA_TO_MASK */ + + 0xc0066900, + 0x000002de, + 0x00000000, /* PA_SU_POLY_OFFSET_DB_FMT_CNTL */ + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + + 0xc0026900, + 0x000002e5, + 0x00000000, /* VGT_STRMOUT_CONFIG */ + 0x00000000, + + 0xc01b6900, + 0x000002f5, + 0x76543210, /* PA_SC_CENTROID_PRIORITY_0 */ + 0xfedcba98, /* PA_SC_CENTROID_PRIORITY_1 */ + 0x00000000, /* PA_SC_LINE_CNTL */ + 0x00000000, /* PA_SC_AA_CONFIG */ + 0x00000005, /* PA_SU_VTX_CNTL */ + 0x3f800000, /* PA_CL_GB_VERT_CLIP_ADJ */ + 0x3f800000, /* PA_CL_GB_VERT_DISC_ADJ */ + 0x3f800000, /* PA_CL_GB_HORZ_CLIP_ADJ */ + 0x3f800000, /* PA_CL_GB_HORZ_DISC_ADJ */ + 0x00000000, /* PA_SC_AA_SAMPLE_LOCS_PIXEL_X0Y0_0 */ + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0xffffffff, /* PA_SC_AA_MASK_X0Y0_X1Y0 */ + 0xffffffff, + + 0xc0026900, + 0x00000316, + 0x0000000e, /* VGT_VERTEX_REUSE_BLOCK_CNTL */ + 0x00000010, /* */ +}; + +const u32 si_default_size = DRM_ARRAY_SIZE(si_default_state); diff --git a/sys/dev/drm2/radeon/si_blit_shaders.h b/sys/dev/drm2/radeon/si_blit_shaders.h new file mode 100644 index 00000000000..0ba0aa89718 --- /dev/null +++ b/sys/dev/drm2/radeon/si_blit_shaders.h @@ -0,0 +1,35 @@ +/* + * Copyright 2011 Advanced Micro Devices, Inc. + * + * 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 (including the next + * paragraph) 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 COPYRIGHT HOLDER(S) AND/OR ITS SUPPLIERS 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 SI_BLIT_SHADERS_H +#define SI_BLIT_SHADERS_H + +#include +__FBSDID("$FreeBSD$"); + +extern const u32 si_default_state[]; + +extern const u32 si_default_size; + +#endif diff --git a/sys/dev/drm2/radeon/si_reg.h b/sys/dev/drm2/radeon/si_reg.h new file mode 100644 index 00000000000..aa94b66ec86 --- /dev/null +++ b/sys/dev/drm2/radeon/si_reg.h @@ -0,0 +1,108 @@ +/* + * Copyright 2010 Advanced Micro Devices, Inc. + * + * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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. + * + * Authors: Alex Deucher + */ +#ifndef __SI_REG_H__ +#define __SI_REG_H__ + +#include +__FBSDID("$FreeBSD$"); + +/* SI */ +#define SI_DC_GPIO_HPD_MASK 0x65b0 +#define SI_DC_GPIO_HPD_A 0x65b4 +#define SI_DC_GPIO_HPD_EN 0x65b8 +#define SI_DC_GPIO_HPD_Y 0x65bc + +#define SI_GRPH_CONTROL 0x6804 +# define SI_GRPH_DEPTH(x) (((x) & 0x3) << 0) +# define SI_GRPH_DEPTH_8BPP 0 +# define SI_GRPH_DEPTH_16BPP 1 +# define SI_GRPH_DEPTH_32BPP 2 +# define SI_GRPH_NUM_BANKS(x) (((x) & 0x3) << 2) +# define SI_ADDR_SURF_2_BANK 0 +# define SI_ADDR_SURF_4_BANK 1 +# define SI_ADDR_SURF_8_BANK 2 +# define SI_ADDR_SURF_16_BANK 3 +# define SI_GRPH_Z(x) (((x) & 0x3) << 4) +# define SI_GRPH_BANK_WIDTH(x) (((x) & 0x3) << 6) +# define SI_ADDR_SURF_BANK_WIDTH_1 0 +# define SI_ADDR_SURF_BANK_WIDTH_2 1 +# define SI_ADDR_SURF_BANK_WIDTH_4 2 +# define SI_ADDR_SURF_BANK_WIDTH_8 3 +# define SI_GRPH_FORMAT(x) (((x) & 0x7) << 8) +/* 8 BPP */ +# define SI_GRPH_FORMAT_INDEXED 0 +/* 16 BPP */ +# define SI_GRPH_FORMAT_ARGB1555 0 +# define SI_GRPH_FORMAT_ARGB565 1 +# define SI_GRPH_FORMAT_ARGB4444 2 +# define SI_GRPH_FORMAT_AI88 3 +# define SI_GRPH_FORMAT_MONO16 4 +# define SI_GRPH_FORMAT_BGRA5551 5 +/* 32 BPP */ +# define SI_GRPH_FORMAT_ARGB8888 0 +# define SI_GRPH_FORMAT_ARGB2101010 1 +# define SI_GRPH_FORMAT_32BPP_DIG 2 +# define SI_GRPH_FORMAT_8B_ARGB2101010 3 +# define SI_GRPH_FORMAT_BGRA1010102 4 +# define SI_GRPH_FORMAT_8B_BGRA1010102 5 +# define SI_GRPH_FORMAT_RGB111110 6 +# define SI_GRPH_FORMAT_BGR101111 7 +# define SI_GRPH_BANK_HEIGHT(x) (((x) & 0x3) << 11) +# define SI_ADDR_SURF_BANK_HEIGHT_1 0 +# define SI_ADDR_SURF_BANK_HEIGHT_2 1 +# define SI_ADDR_SURF_BANK_HEIGHT_4 2 +# define SI_ADDR_SURF_BANK_HEIGHT_8 3 +# define SI_GRPH_TILE_SPLIT(x) (((x) & 0x7) << 13) +# define SI_ADDR_SURF_TILE_SPLIT_64B 0 +# define SI_ADDR_SURF_TILE_SPLIT_128B 1 +# define SI_ADDR_SURF_TILE_SPLIT_256B 2 +# define SI_ADDR_SURF_TILE_SPLIT_512B 3 +# define SI_ADDR_SURF_TILE_SPLIT_1KB 4 +# define SI_ADDR_SURF_TILE_SPLIT_2KB 5 +# define SI_ADDR_SURF_TILE_SPLIT_4KB 6 +# define SI_GRPH_MACRO_TILE_ASPECT(x) (((x) & 0x3) << 18) +# define SI_ADDR_SURF_MACRO_TILE_ASPECT_1 0 +# define SI_ADDR_SURF_MACRO_TILE_ASPECT_2 1 +# define SI_ADDR_SURF_MACRO_TILE_ASPECT_4 2 +# define SI_ADDR_SURF_MACRO_TILE_ASPECT_8 3 +# define SI_GRPH_ARRAY_MODE(x) (((x) & 0x7) << 20) +# define SI_GRPH_ARRAY_LINEAR_GENERAL 0 +# define SI_GRPH_ARRAY_LINEAR_ALIGNED 1 +# define SI_GRPH_ARRAY_1D_TILED_THIN1 2 +# define SI_GRPH_ARRAY_2D_TILED_THIN1 4 +# define SI_GRPH_PIPE_CONFIG(x) (((x) & 0x1f) << 24) +# define SI_ADDR_SURF_P2 0 +# define SI_ADDR_SURF_P4_8x16 4 +# define SI_ADDR_SURF_P4_16x16 5 +# define SI_ADDR_SURF_P4_16x32 6 +# define SI_ADDR_SURF_P4_32x32 7 +# define SI_ADDR_SURF_P8_16x16_8x16 8 +# define SI_ADDR_SURF_P8_16x32_8x16 9 +# define SI_ADDR_SURF_P8_32x32_8x16 10 +# define SI_ADDR_SURF_P8_16x32_16x16 11 +# define SI_ADDR_SURF_P8_32x32_16x16 12 +# define SI_ADDR_SURF_P8_32x32_16x32 13 +# define SI_ADDR_SURF_P8_32x64_32x32 14 + +#endif diff --git a/sys/dev/drm2/radeon/sid.h b/sys/dev/drm2/radeon/sid.h new file mode 100644 index 00000000000..028916f17eb --- /dev/null +++ b/sys/dev/drm2/radeon/sid.h @@ -0,0 +1,1065 @@ +/* + * Copyright 2011 Advanced Micro Devices, Inc. + * + * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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. + * + * Authors: Alex Deucher + */ +#ifndef SI_H +#define SI_H + +#include +__FBSDID("$FreeBSD$"); + +#define TAHITI_RB_BITMAP_WIDTH_PER_SH 2 + +#define TAHITI_GB_ADDR_CONFIG_GOLDEN 0x12011003 +#define VERDE_GB_ADDR_CONFIG_GOLDEN 0x12010002 + +#define CG_MULT_THERMAL_STATUS 0x714 +#define ASIC_MAX_TEMP(x) ((x) << 0) +#define ASIC_MAX_TEMP_MASK 0x000001ff +#define ASIC_MAX_TEMP_SHIFT 0 +#define CTF_TEMP(x) ((x) << 9) +#define CTF_TEMP_MASK 0x0003fe00 +#define CTF_TEMP_SHIFT 9 + +#define SI_MAX_SH_GPRS 256 +#define SI_MAX_TEMP_GPRS 16 +#define SI_MAX_SH_THREADS 256 +#define SI_MAX_SH_STACK_ENTRIES 4096 +#define SI_MAX_FRC_EOV_CNT 16384 +#define SI_MAX_BACKENDS 8 +#define SI_MAX_BACKENDS_MASK 0xFF +#define SI_MAX_BACKENDS_PER_SE_MASK 0x0F +#define SI_MAX_SIMDS 12 +#define SI_MAX_SIMDS_MASK 0x0FFF +#define SI_MAX_SIMDS_PER_SE_MASK 0x00FF +#define SI_MAX_PIPES 8 +#define SI_MAX_PIPES_MASK 0xFF +#define SI_MAX_PIPES_PER_SIMD_MASK 0x3F +#define SI_MAX_LDS_NUM 0xFFFF +#define SI_MAX_TCC 16 +#define SI_MAX_TCC_MASK 0xFFFF + +#define VGA_HDP_CONTROL 0x328 +#define VGA_MEMORY_DISABLE (1 << 4) + +#define DMIF_ADDR_CONFIG 0xBD4 + +#define SRBM_STATUS 0xE50 + +#define SRBM_SOFT_RESET 0x0E60 +#define SOFT_RESET_BIF (1 << 1) +#define SOFT_RESET_DC (1 << 5) +#define SOFT_RESET_DMA1 (1 << 6) +#define SOFT_RESET_GRBM (1 << 8) +#define SOFT_RESET_HDP (1 << 9) +#define SOFT_RESET_IH (1 << 10) +#define SOFT_RESET_MC (1 << 11) +#define SOFT_RESET_ROM (1 << 14) +#define SOFT_RESET_SEM (1 << 15) +#define SOFT_RESET_VMC (1 << 17) +#define SOFT_RESET_DMA (1 << 20) +#define SOFT_RESET_TST (1 << 21) +#define SOFT_RESET_REGBB (1 << 22) +#define SOFT_RESET_ORB (1 << 23) + +#define CC_SYS_RB_BACKEND_DISABLE 0xe80 +#define GC_USER_SYS_RB_BACKEND_DISABLE 0xe84 + +#define VM_L2_CNTL 0x1400 +#define ENABLE_L2_CACHE (1 << 0) +#define ENABLE_L2_FRAGMENT_PROCESSING (1 << 1) +#define L2_CACHE_PTE_ENDIAN_SWAP_MODE(x) ((x) << 2) +#define L2_CACHE_PDE_ENDIAN_SWAP_MODE(x) ((x) << 4) +#define ENABLE_L2_PTE_CACHE_LRU_UPDATE_BY_WRITE (1 << 9) +#define ENABLE_L2_PDE0_CACHE_LRU_UPDATE_BY_WRITE (1 << 10) +#define EFFECTIVE_L2_QUEUE_SIZE(x) (((x) & 7) << 15) +#define CONTEXT1_IDENTITY_ACCESS_MODE(x) (((x) & 3) << 19) +#define VM_L2_CNTL2 0x1404 +#define INVALIDATE_ALL_L1_TLBS (1 << 0) +#define INVALIDATE_L2_CACHE (1 << 1) +#define INVALIDATE_CACHE_MODE(x) ((x) << 26) +#define INVALIDATE_PTE_AND_PDE_CACHES 0 +#define INVALIDATE_ONLY_PTE_CACHES 1 +#define INVALIDATE_ONLY_PDE_CACHES 2 +#define VM_L2_CNTL3 0x1408 +#define BANK_SELECT(x) ((x) << 0) +#define L2_CACHE_UPDATE_MODE(x) ((x) << 6) +#define L2_CACHE_BIGK_FRAGMENT_SIZE(x) ((x) << 15) +#define L2_CACHE_BIGK_ASSOCIATIVITY (1 << 20) +#define VM_L2_STATUS 0x140C +#define L2_BUSY (1 << 0) +#define VM_CONTEXT0_CNTL 0x1410 +#define ENABLE_CONTEXT (1 << 0) +#define PAGE_TABLE_DEPTH(x) (((x) & 3) << 1) +#define RANGE_PROTECTION_FAULT_ENABLE_INTERRUPT (1 << 3) +#define RANGE_PROTECTION_FAULT_ENABLE_DEFAULT (1 << 4) +#define DUMMY_PAGE_PROTECTION_FAULT_ENABLE_INTERRUPT (1 << 6) +#define DUMMY_PAGE_PROTECTION_FAULT_ENABLE_DEFAULT (1 << 7) +#define PDE0_PROTECTION_FAULT_ENABLE_INTERRUPT (1 << 9) +#define PDE0_PROTECTION_FAULT_ENABLE_DEFAULT (1 << 10) +#define VALID_PROTECTION_FAULT_ENABLE_INTERRUPT (1 << 12) +#define VALID_PROTECTION_FAULT_ENABLE_DEFAULT (1 << 13) +#define READ_PROTECTION_FAULT_ENABLE_INTERRUPT (1 << 15) +#define READ_PROTECTION_FAULT_ENABLE_DEFAULT (1 << 16) +#define WRITE_PROTECTION_FAULT_ENABLE_INTERRUPT (1 << 18) +#define WRITE_PROTECTION_FAULT_ENABLE_DEFAULT (1 << 19) +#define VM_CONTEXT1_CNTL 0x1414 +#define VM_CONTEXT0_CNTL2 0x1430 +#define VM_CONTEXT1_CNTL2 0x1434 +#define VM_CONTEXT8_PAGE_TABLE_BASE_ADDR 0x1438 +#define VM_CONTEXT9_PAGE_TABLE_BASE_ADDR 0x143c +#define VM_CONTEXT10_PAGE_TABLE_BASE_ADDR 0x1440 +#define VM_CONTEXT11_PAGE_TABLE_BASE_ADDR 0x1444 +#define VM_CONTEXT12_PAGE_TABLE_BASE_ADDR 0x1448 +#define VM_CONTEXT13_PAGE_TABLE_BASE_ADDR 0x144c +#define VM_CONTEXT14_PAGE_TABLE_BASE_ADDR 0x1450 +#define VM_CONTEXT15_PAGE_TABLE_BASE_ADDR 0x1454 + +#define VM_CONTEXT1_PROTECTION_FAULT_ADDR 0x14FC +#define VM_CONTEXT1_PROTECTION_FAULT_STATUS 0x14DC + +#define VM_INVALIDATE_REQUEST 0x1478 +#define VM_INVALIDATE_RESPONSE 0x147c + +#define VM_CONTEXT0_PROTECTION_FAULT_DEFAULT_ADDR 0x1518 +#define VM_CONTEXT1_PROTECTION_FAULT_DEFAULT_ADDR 0x151c + +#define VM_CONTEXT0_PAGE_TABLE_BASE_ADDR 0x153c +#define VM_CONTEXT1_PAGE_TABLE_BASE_ADDR 0x1540 +#define VM_CONTEXT2_PAGE_TABLE_BASE_ADDR 0x1544 +#define VM_CONTEXT3_PAGE_TABLE_BASE_ADDR 0x1548 +#define VM_CONTEXT4_PAGE_TABLE_BASE_ADDR 0x154c +#define VM_CONTEXT5_PAGE_TABLE_BASE_ADDR 0x1550 +#define VM_CONTEXT6_PAGE_TABLE_BASE_ADDR 0x1554 +#define VM_CONTEXT7_PAGE_TABLE_BASE_ADDR 0x1558 +#define VM_CONTEXT0_PAGE_TABLE_START_ADDR 0x155c +#define VM_CONTEXT1_PAGE_TABLE_START_ADDR 0x1560 + +#define VM_CONTEXT0_PAGE_TABLE_END_ADDR 0x157C +#define VM_CONTEXT1_PAGE_TABLE_END_ADDR 0x1580 + +#define MC_SHARED_CHMAP 0x2004 +#define NOOFCHAN_SHIFT 12 +#define NOOFCHAN_MASK 0x0000f000 +#define MC_SHARED_CHREMAP 0x2008 + +#define MC_VM_FB_LOCATION 0x2024 +#define MC_VM_AGP_TOP 0x2028 +#define MC_VM_AGP_BOT 0x202C +#define MC_VM_AGP_BASE 0x2030 +#define MC_VM_SYSTEM_APERTURE_LOW_ADDR 0x2034 +#define MC_VM_SYSTEM_APERTURE_HIGH_ADDR 0x2038 +#define MC_VM_SYSTEM_APERTURE_DEFAULT_ADDR 0x203C + +#define MC_VM_MX_L1_TLB_CNTL 0x2064 +#define ENABLE_L1_TLB (1 << 0) +#define ENABLE_L1_FRAGMENT_PROCESSING (1 << 1) +#define SYSTEM_ACCESS_MODE_PA_ONLY (0 << 3) +#define SYSTEM_ACCESS_MODE_USE_SYS_MAP (1 << 3) +#define SYSTEM_ACCESS_MODE_IN_SYS (2 << 3) +#define SYSTEM_ACCESS_MODE_NOT_IN_SYS (3 << 3) +#define SYSTEM_APERTURE_UNMAPPED_ACCESS_PASS_THRU (0 << 5) +#define ENABLE_ADVANCED_DRIVER_MODEL (1 << 6) + +#define MC_SHARED_BLACKOUT_CNTL 0x20ac + +#define MC_ARB_RAMCFG 0x2760 +#define NOOFBANK_SHIFT 0 +#define NOOFBANK_MASK 0x00000003 +#define NOOFRANK_SHIFT 2 +#define NOOFRANK_MASK 0x00000004 +#define NOOFROWS_SHIFT 3 +#define NOOFROWS_MASK 0x00000038 +#define NOOFCOLS_SHIFT 6 +#define NOOFCOLS_MASK 0x000000C0 +#define CHANSIZE_SHIFT 8 +#define CHANSIZE_MASK 0x00000100 +#define CHANSIZE_OVERRIDE (1 << 11) +#define NOOFGROUPS_SHIFT 12 +#define NOOFGROUPS_MASK 0x00001000 + +#define MC_SEQ_TRAIN_WAKEUP_CNTL 0x2808 +#define TRAIN_DONE_D0 (1 << 30) +#define TRAIN_DONE_D1 (1 << 31) + +#define MC_SEQ_SUP_CNTL 0x28c8 +#define RUN_MASK (1 << 0) +#define MC_SEQ_SUP_PGM 0x28cc + +#define MC_IO_PAD_CNTL_D0 0x29d0 +#define MEM_FALL_OUT_CMD (1 << 8) + +#define MC_SEQ_IO_DEBUG_INDEX 0x2a44 +#define MC_SEQ_IO_DEBUG_DATA 0x2a48 + +#define HDP_HOST_PATH_CNTL 0x2C00 +#define HDP_NONSURFACE_BASE 0x2C04 +#define HDP_NONSURFACE_INFO 0x2C08 +#define HDP_NONSURFACE_SIZE 0x2C0C + +#define HDP_ADDR_CONFIG 0x2F48 +#define HDP_MISC_CNTL 0x2F4C +#define HDP_FLUSH_INVALIDATE_CACHE (1 << 0) + +#define IH_RB_CNTL 0x3e00 +# define IH_RB_ENABLE (1 << 0) +# define IH_IB_SIZE(x) ((x) << 1) /* log2 */ +# define IH_RB_FULL_DRAIN_ENABLE (1 << 6) +# define IH_WPTR_WRITEBACK_ENABLE (1 << 8) +# define IH_WPTR_WRITEBACK_TIMER(x) ((x) << 9) /* log2 */ +# define IH_WPTR_OVERFLOW_ENABLE (1 << 16) +# define IH_WPTR_OVERFLOW_CLEAR (1 << 31) +#define IH_RB_BASE 0x3e04 +#define IH_RB_RPTR 0x3e08 +#define IH_RB_WPTR 0x3e0c +# define RB_OVERFLOW (1 << 0) +# define WPTR_OFFSET_MASK 0x3fffc +#define IH_RB_WPTR_ADDR_HI 0x3e10 +#define IH_RB_WPTR_ADDR_LO 0x3e14 +#define IH_CNTL 0x3e18 +# define ENABLE_INTR (1 << 0) +# define IH_MC_SWAP(x) ((x) << 1) +# define IH_MC_SWAP_NONE 0 +# define IH_MC_SWAP_16BIT 1 +# define IH_MC_SWAP_32BIT 2 +# define IH_MC_SWAP_64BIT 3 +# define RPTR_REARM (1 << 4) +# define MC_WRREQ_CREDIT(x) ((x) << 15) +# define MC_WR_CLEAN_CNT(x) ((x) << 20) +# define MC_VMID(x) ((x) << 25) + +#define CONFIG_MEMSIZE 0x5428 + +#define INTERRUPT_CNTL 0x5468 +# define IH_DUMMY_RD_OVERRIDE (1 << 0) +# define IH_DUMMY_RD_EN (1 << 1) +# define IH_REQ_NONSNOOP_EN (1 << 3) +# define GEN_IH_INT_EN (1 << 8) +#define INTERRUPT_CNTL2 0x546c + +#define HDP_MEM_COHERENCY_FLUSH_CNTL 0x5480 + +#define BIF_FB_EN 0x5490 +#define FB_READ_EN (1 << 0) +#define FB_WRITE_EN (1 << 1) + +#define HDP_REG_COHERENCY_FLUSH_CNTL 0x54A0 + +#define DC_LB_MEMORY_SPLIT 0x6b0c +#define DC_LB_MEMORY_CONFIG(x) ((x) << 20) + +#define PRIORITY_A_CNT 0x6b18 +#define PRIORITY_MARK_MASK 0x7fff +#define PRIORITY_OFF (1 << 16) +#define PRIORITY_ALWAYS_ON (1 << 20) +#define PRIORITY_B_CNT 0x6b1c + +#define DPG_PIPE_ARBITRATION_CONTROL3 0x6cc8 +# define LATENCY_WATERMARK_MASK(x) ((x) << 16) +#define DPG_PIPE_LATENCY_CONTROL 0x6ccc +# define LATENCY_LOW_WATERMARK(x) ((x) << 0) +# define LATENCY_HIGH_WATERMARK(x) ((x) << 16) + +/* 0x6bb8, 0x77b8, 0x103b8, 0x10fb8, 0x11bb8, 0x127b8 */ +#define VLINE_STATUS 0x6bb8 +# define VLINE_OCCURRED (1 << 0) +# define VLINE_ACK (1 << 4) +# define VLINE_STAT (1 << 12) +# define VLINE_INTERRUPT (1 << 16) +# define VLINE_INTERRUPT_TYPE (1 << 17) +/* 0x6bbc, 0x77bc, 0x103bc, 0x10fbc, 0x11bbc, 0x127bc */ +#define VBLANK_STATUS 0x6bbc +# define VBLANK_OCCURRED (1 << 0) +# define VBLANK_ACK (1 << 4) +# define VBLANK_STAT (1 << 12) +# define VBLANK_INTERRUPT (1 << 16) +# define VBLANK_INTERRUPT_TYPE (1 << 17) + +/* 0x6b40, 0x7740, 0x10340, 0x10f40, 0x11b40, 0x12740 */ +#define INT_MASK 0x6b40 +# define VBLANK_INT_MASK (1 << 0) +# define VLINE_INT_MASK (1 << 4) + +#define DISP_INTERRUPT_STATUS 0x60f4 +# define LB_D1_VLINE_INTERRUPT (1 << 2) +# define LB_D1_VBLANK_INTERRUPT (1 << 3) +# define DC_HPD1_INTERRUPT (1 << 17) +# define DC_HPD1_RX_INTERRUPT (1 << 18) +# define DACA_AUTODETECT_INTERRUPT (1 << 22) +# define DACB_AUTODETECT_INTERRUPT (1 << 23) +# define DC_I2C_SW_DONE_INTERRUPT (1 << 24) +# define DC_I2C_HW_DONE_INTERRUPT (1 << 25) +#define DISP_INTERRUPT_STATUS_CONTINUE 0x60f8 +# define LB_D2_VLINE_INTERRUPT (1 << 2) +# define LB_D2_VBLANK_INTERRUPT (1 << 3) +# define DC_HPD2_INTERRUPT (1 << 17) +# define DC_HPD2_RX_INTERRUPT (1 << 18) +# define DISP_TIMER_INTERRUPT (1 << 24) +#define DISP_INTERRUPT_STATUS_CONTINUE2 0x60fc +# define LB_D3_VLINE_INTERRUPT (1 << 2) +# define LB_D3_VBLANK_INTERRUPT (1 << 3) +# define DC_HPD3_INTERRUPT (1 << 17) +# define DC_HPD3_RX_INTERRUPT (1 << 18) +#define DISP_INTERRUPT_STATUS_CONTINUE3 0x6100 +# define LB_D4_VLINE_INTERRUPT (1 << 2) +# define LB_D4_VBLANK_INTERRUPT (1 << 3) +# define DC_HPD4_INTERRUPT (1 << 17) +# define DC_HPD4_RX_INTERRUPT (1 << 18) +#define DISP_INTERRUPT_STATUS_CONTINUE4 0x614c +# define LB_D5_VLINE_INTERRUPT (1 << 2) +# define LB_D5_VBLANK_INTERRUPT (1 << 3) +# define DC_HPD5_INTERRUPT (1 << 17) +# define DC_HPD5_RX_INTERRUPT (1 << 18) +#define DISP_INTERRUPT_STATUS_CONTINUE5 0x6150 +# define LB_D6_VLINE_INTERRUPT (1 << 2) +# define LB_D6_VBLANK_INTERRUPT (1 << 3) +# define DC_HPD6_INTERRUPT (1 << 17) +# define DC_HPD6_RX_INTERRUPT (1 << 18) + +/* 0x6858, 0x7458, 0x10058, 0x10c58, 0x11858, 0x12458 */ +#define GRPH_INT_STATUS 0x6858 +# define GRPH_PFLIP_INT_OCCURRED (1 << 0) +# define GRPH_PFLIP_INT_CLEAR (1 << 8) +/* 0x685c, 0x745c, 0x1005c, 0x10c5c, 0x1185c, 0x1245c */ +#define GRPH_INT_CONTROL 0x685c +# define GRPH_PFLIP_INT_MASK (1 << 0) +# define GRPH_PFLIP_INT_TYPE (1 << 8) + +#define DACA_AUTODETECT_INT_CONTROL 0x66c8 + +#define DC_HPD1_INT_STATUS 0x601c +#define DC_HPD2_INT_STATUS 0x6028 +#define DC_HPD3_INT_STATUS 0x6034 +#define DC_HPD4_INT_STATUS 0x6040 +#define DC_HPD5_INT_STATUS 0x604c +#define DC_HPD6_INT_STATUS 0x6058 +# define DC_HPDx_INT_STATUS (1 << 0) +# define DC_HPDx_SENSE (1 << 1) +# define DC_HPDx_RX_INT_STATUS (1 << 8) + +#define DC_HPD1_INT_CONTROL 0x6020 +#define DC_HPD2_INT_CONTROL 0x602c +#define DC_HPD3_INT_CONTROL 0x6038 +#define DC_HPD4_INT_CONTROL 0x6044 +#define DC_HPD5_INT_CONTROL 0x6050 +#define DC_HPD6_INT_CONTROL 0x605c +# define DC_HPDx_INT_ACK (1 << 0) +# define DC_HPDx_INT_POLARITY (1 << 8) +# define DC_HPDx_INT_EN (1 << 16) +# define DC_HPDx_RX_INT_ACK (1 << 20) +# define DC_HPDx_RX_INT_EN (1 << 24) + +#define DC_HPD1_CONTROL 0x6024 +#define DC_HPD2_CONTROL 0x6030 +#define DC_HPD3_CONTROL 0x603c +#define DC_HPD4_CONTROL 0x6048 +#define DC_HPD5_CONTROL 0x6054 +#define DC_HPD6_CONTROL 0x6060 +# define DC_HPDx_CONNECTION_TIMER(x) ((x) << 0) +# define DC_HPDx_RX_INT_TIMER(x) ((x) << 16) +# define DC_HPDx_EN (1 << 28) + +/* 0x6e98, 0x7a98, 0x10698, 0x11298, 0x11e98, 0x12a98 */ +#define CRTC_STATUS_FRAME_COUNT 0x6e98 + +#define GRBM_CNTL 0x8000 +#define GRBM_READ_TIMEOUT(x) ((x) << 0) + +#define GRBM_STATUS2 0x8008 +#define RLC_RQ_PENDING (1 << 0) +#define RLC_BUSY (1 << 8) +#define TC_BUSY (1 << 9) + +#define GRBM_STATUS 0x8010 +#define CMDFIFO_AVAIL_MASK 0x0000000F +#define RING2_RQ_PENDING (1 << 4) +#define SRBM_RQ_PENDING (1 << 5) +#define RING1_RQ_PENDING (1 << 6) +#define CF_RQ_PENDING (1 << 7) +#define PF_RQ_PENDING (1 << 8) +#define GDS_DMA_RQ_PENDING (1 << 9) +#define GRBM_EE_BUSY (1 << 10) +#define DB_CLEAN (1 << 12) +#define CB_CLEAN (1 << 13) +#define TA_BUSY (1 << 14) +#define GDS_BUSY (1 << 15) +#define VGT_BUSY (1 << 17) +#define IA_BUSY_NO_DMA (1 << 18) +#define IA_BUSY (1 << 19) +#define SX_BUSY (1 << 20) +#define SPI_BUSY (1 << 22) +#define BCI_BUSY (1 << 23) +#define SC_BUSY (1 << 24) +#define PA_BUSY (1 << 25) +#define DB_BUSY (1 << 26) +#define CP_COHERENCY_BUSY (1 << 28) +#define CP_BUSY (1 << 29) +#define CB_BUSY (1 << 30) +#define GUI_ACTIVE (1 << 31) +#define GRBM_STATUS_SE0 0x8014 +#define GRBM_STATUS_SE1 0x8018 +#define SE_DB_CLEAN (1 << 1) +#define SE_CB_CLEAN (1 << 2) +#define SE_BCI_BUSY (1 << 22) +#define SE_VGT_BUSY (1 << 23) +#define SE_PA_BUSY (1 << 24) +#define SE_TA_BUSY (1 << 25) +#define SE_SX_BUSY (1 << 26) +#define SE_SPI_BUSY (1 << 27) +#define SE_SC_BUSY (1 << 29) +#define SE_DB_BUSY (1 << 30) +#define SE_CB_BUSY (1 << 31) + +#define GRBM_SOFT_RESET 0x8020 +#define SOFT_RESET_CP (1 << 0) +#define SOFT_RESET_CB (1 << 1) +#define SOFT_RESET_RLC (1 << 2) +#define SOFT_RESET_DB (1 << 3) +#define SOFT_RESET_GDS (1 << 4) +#define SOFT_RESET_PA (1 << 5) +#define SOFT_RESET_SC (1 << 6) +#define SOFT_RESET_BCI (1 << 7) +#define SOFT_RESET_SPI (1 << 8) +#define SOFT_RESET_SX (1 << 10) +#define SOFT_RESET_TC (1 << 11) +#define SOFT_RESET_TA (1 << 12) +#define SOFT_RESET_VGT (1 << 14) +#define SOFT_RESET_IA (1 << 15) + +#define GRBM_GFX_INDEX 0x802C +#define INSTANCE_INDEX(x) ((x) << 0) +#define SH_INDEX(x) ((x) << 8) +#define SE_INDEX(x) ((x) << 16) +#define SH_BROADCAST_WRITES (1 << 29) +#define INSTANCE_BROADCAST_WRITES (1 << 30) +#define SE_BROADCAST_WRITES (1 << 31) + +#define GRBM_INT_CNTL 0x8060 +# define RDERR_INT_ENABLE (1 << 0) +# define GUI_IDLE_INT_ENABLE (1 << 19) + +#define CP_STRMOUT_CNTL 0x84FC +#define SCRATCH_REG0 0x8500 +#define SCRATCH_REG1 0x8504 +#define SCRATCH_REG2 0x8508 +#define SCRATCH_REG3 0x850C +#define SCRATCH_REG4 0x8510 +#define SCRATCH_REG5 0x8514 +#define SCRATCH_REG6 0x8518 +#define SCRATCH_REG7 0x851C + +#define SCRATCH_UMSK 0x8540 +#define SCRATCH_ADDR 0x8544 + +#define CP_SEM_WAIT_TIMER 0x85BC + +#define CP_SEM_INCOMPLETE_TIMER_CNTL 0x85C8 + +#define CP_ME_CNTL 0x86D8 +#define CP_CE_HALT (1 << 24) +#define CP_PFP_HALT (1 << 26) +#define CP_ME_HALT (1 << 28) + +#define CP_COHER_CNTL2 0x85E8 + +#define CP_RB2_RPTR 0x86f8 +#define CP_RB1_RPTR 0x86fc +#define CP_RB0_RPTR 0x8700 +#define CP_RB_WPTR_DELAY 0x8704 + +#define CP_QUEUE_THRESHOLDS 0x8760 +#define ROQ_IB1_START(x) ((x) << 0) +#define ROQ_IB2_START(x) ((x) << 8) +#define CP_MEQ_THRESHOLDS 0x8764 +#define MEQ1_START(x) ((x) << 0) +#define MEQ2_START(x) ((x) << 8) + +#define CP_PERFMON_CNTL 0x87FC + +#define VGT_VTX_VECT_EJECT_REG 0x88B0 + +#define VGT_CACHE_INVALIDATION 0x88C4 +#define CACHE_INVALIDATION(x) ((x) << 0) +#define VC_ONLY 0 +#define TC_ONLY 1 +#define VC_AND_TC 2 +#define AUTO_INVLD_EN(x) ((x) << 6) +#define NO_AUTO 0 +#define ES_AUTO 1 +#define GS_AUTO 2 +#define ES_AND_GS_AUTO 3 +#define VGT_ESGS_RING_SIZE 0x88C8 +#define VGT_GSVS_RING_SIZE 0x88CC + +#define VGT_GS_VERTEX_REUSE 0x88D4 + +#define VGT_PRIMITIVE_TYPE 0x8958 +#define VGT_INDEX_TYPE 0x895C + +#define VGT_NUM_INDICES 0x8970 +#define VGT_NUM_INSTANCES 0x8974 + +#define VGT_TF_RING_SIZE 0x8988 + +#define VGT_HS_OFFCHIP_PARAM 0x89B0 + +#define VGT_TF_MEMORY_BASE 0x89B8 + +#define CC_GC_SHADER_ARRAY_CONFIG 0x89bc +#define INACTIVE_CUS_MASK 0xFFFF0000 +#define INACTIVE_CUS_SHIFT 16 +#define GC_USER_SHADER_ARRAY_CONFIG 0x89c0 + +#define PA_CL_ENHANCE 0x8A14 +#define CLIP_VTX_REORDER_ENA (1 << 0) +#define NUM_CLIP_SEQ(x) ((x) << 1) + +#define PA_SU_LINE_STIPPLE_VALUE 0x8A60 + +#define PA_SC_LINE_STIPPLE_STATE 0x8B10 + +#define PA_SC_FORCE_EOV_MAX_CNTS 0x8B24 +#define FORCE_EOV_MAX_CLK_CNT(x) ((x) << 0) +#define FORCE_EOV_MAX_REZ_CNT(x) ((x) << 16) + +#define PA_SC_FIFO_SIZE 0x8BCC +#define SC_FRONTEND_PRIM_FIFO_SIZE(x) ((x) << 0) +#define SC_BACKEND_PRIM_FIFO_SIZE(x) ((x) << 6) +#define SC_HIZ_TILE_FIFO_SIZE(x) ((x) << 15) +#define SC_EARLYZ_TILE_FIFO_SIZE(x) ((x) << 23) + +#define PA_SC_ENHANCE 0x8BF0 + +#define SQ_CONFIG 0x8C00 + +#define SQC_CACHES 0x8C08 + +#define SX_DEBUG_1 0x9060 + +#define SPI_STATIC_THREAD_MGMT_1 0x90E0 +#define SPI_STATIC_THREAD_MGMT_2 0x90E4 +#define SPI_STATIC_THREAD_MGMT_3 0x90E8 +#define SPI_PS_MAX_WAVE_ID 0x90EC + +#define SPI_CONFIG_CNTL 0x9100 + +#define SPI_CONFIG_CNTL_1 0x913C +#define VTX_DONE_DELAY(x) ((x) << 0) +#define INTERP_ONE_PRIM_PER_ROW (1 << 4) + +#define CGTS_TCC_DISABLE 0x9148 +#define CGTS_USER_TCC_DISABLE 0x914C +#define TCC_DISABLE_MASK 0xFFFF0000 +#define TCC_DISABLE_SHIFT 16 + +#define TA_CNTL_AUX 0x9508 + +#define CC_RB_BACKEND_DISABLE 0x98F4 +#define BACKEND_DISABLE(x) ((x) << 16) +#define GB_ADDR_CONFIG 0x98F8 +#define NUM_PIPES(x) ((x) << 0) +#define NUM_PIPES_MASK 0x00000007 +#define NUM_PIPES_SHIFT 0 +#define PIPE_INTERLEAVE_SIZE(x) ((x) << 4) +#define PIPE_INTERLEAVE_SIZE_MASK 0x00000070 +#define PIPE_INTERLEAVE_SIZE_SHIFT 4 +#define NUM_SHADER_ENGINES(x) ((x) << 12) +#define NUM_SHADER_ENGINES_MASK 0x00003000 +#define NUM_SHADER_ENGINES_SHIFT 12 +#define SHADER_ENGINE_TILE_SIZE(x) ((x) << 16) +#define SHADER_ENGINE_TILE_SIZE_MASK 0x00070000 +#define SHADER_ENGINE_TILE_SIZE_SHIFT 16 +#define NUM_GPUS(x) ((x) << 20) +#define NUM_GPUS_MASK 0x00700000 +#define NUM_GPUS_SHIFT 20 +#define MULTI_GPU_TILE_SIZE(x) ((x) << 24) +#define MULTI_GPU_TILE_SIZE_MASK 0x03000000 +#define MULTI_GPU_TILE_SIZE_SHIFT 24 +#define ROW_SIZE(x) ((x) << 28) +#define ROW_SIZE_MASK 0x30000000 +#define ROW_SIZE_SHIFT 28 + +#define GB_TILE_MODE0 0x9910 +# define MICRO_TILE_MODE(x) ((x) << 0) +# define ADDR_SURF_DISPLAY_MICRO_TILING 0 +# define ADDR_SURF_THIN_MICRO_TILING 1 +# define ADDR_SURF_DEPTH_MICRO_TILING 2 +# define ARRAY_MODE(x) ((x) << 2) +# define ARRAY_LINEAR_GENERAL 0 +# define ARRAY_LINEAR_ALIGNED 1 +# define ARRAY_1D_TILED_THIN1 2 +# define ARRAY_2D_TILED_THIN1 4 +# define PIPE_CONFIG(x) ((x) << 6) +# define ADDR_SURF_P2 0 +# define ADDR_SURF_P4_8x16 4 +# define ADDR_SURF_P4_16x16 5 +# define ADDR_SURF_P4_16x32 6 +# define ADDR_SURF_P4_32x32 7 +# define ADDR_SURF_P8_16x16_8x16 8 +# define ADDR_SURF_P8_16x32_8x16 9 +# define ADDR_SURF_P8_32x32_8x16 10 +# define ADDR_SURF_P8_16x32_16x16 11 +# define ADDR_SURF_P8_32x32_16x16 12 +# define ADDR_SURF_P8_32x32_16x32 13 +# define ADDR_SURF_P8_32x64_32x32 14 +# define TILE_SPLIT(x) ((x) << 11) +# define ADDR_SURF_TILE_SPLIT_64B 0 +# define ADDR_SURF_TILE_SPLIT_128B 1 +# define ADDR_SURF_TILE_SPLIT_256B 2 +# define ADDR_SURF_TILE_SPLIT_512B 3 +# define ADDR_SURF_TILE_SPLIT_1KB 4 +# define ADDR_SURF_TILE_SPLIT_2KB 5 +# define ADDR_SURF_TILE_SPLIT_4KB 6 +# define BANK_WIDTH(x) ((x) << 14) +# define ADDR_SURF_BANK_WIDTH_1 0 +# define ADDR_SURF_BANK_WIDTH_2 1 +# define ADDR_SURF_BANK_WIDTH_4 2 +# define ADDR_SURF_BANK_WIDTH_8 3 +# define BANK_HEIGHT(x) ((x) << 16) +# define ADDR_SURF_BANK_HEIGHT_1 0 +# define ADDR_SURF_BANK_HEIGHT_2 1 +# define ADDR_SURF_BANK_HEIGHT_4 2 +# define ADDR_SURF_BANK_HEIGHT_8 3 +# define MACRO_TILE_ASPECT(x) ((x) << 18) +# define ADDR_SURF_MACRO_ASPECT_1 0 +# define ADDR_SURF_MACRO_ASPECT_2 1 +# define ADDR_SURF_MACRO_ASPECT_4 2 +# define ADDR_SURF_MACRO_ASPECT_8 3 +# define NUM_BANKS(x) ((x) << 20) +# define ADDR_SURF_2_BANK 0 +# define ADDR_SURF_4_BANK 1 +# define ADDR_SURF_8_BANK 2 +# define ADDR_SURF_16_BANK 3 + +#define CB_PERFCOUNTER0_SELECT0 0x9a20 +#define CB_PERFCOUNTER0_SELECT1 0x9a24 +#define CB_PERFCOUNTER1_SELECT0 0x9a28 +#define CB_PERFCOUNTER1_SELECT1 0x9a2c +#define CB_PERFCOUNTER2_SELECT0 0x9a30 +#define CB_PERFCOUNTER2_SELECT1 0x9a34 +#define CB_PERFCOUNTER3_SELECT0 0x9a38 +#define CB_PERFCOUNTER3_SELECT1 0x9a3c + +#define GC_USER_RB_BACKEND_DISABLE 0x9B7C +#define BACKEND_DISABLE_MASK 0x00FF0000 +#define BACKEND_DISABLE_SHIFT 16 + +#define TCP_CHAN_STEER_LO 0xac0c +#define TCP_CHAN_STEER_HI 0xac10 + +#define CP_RB0_BASE 0xC100 +#define CP_RB0_CNTL 0xC104 +#define RB_BUFSZ(x) ((x) << 0) +#define RB_BLKSZ(x) ((x) << 8) +#define BUF_SWAP_32BIT (2 << 16) +#define RB_NO_UPDATE (1 << 27) +#define RB_RPTR_WR_ENA (1 << 31) + +#define CP_RB0_RPTR_ADDR 0xC10C +#define CP_RB0_RPTR_ADDR_HI 0xC110 +#define CP_RB0_WPTR 0xC114 + +#define CP_PFP_UCODE_ADDR 0xC150 +#define CP_PFP_UCODE_DATA 0xC154 +#define CP_ME_RAM_RADDR 0xC158 +#define CP_ME_RAM_WADDR 0xC15C +#define CP_ME_RAM_DATA 0xC160 + +#define CP_CE_UCODE_ADDR 0xC168 +#define CP_CE_UCODE_DATA 0xC16C + +#define CP_RB1_BASE 0xC180 +#define CP_RB1_CNTL 0xC184 +#define CP_RB1_RPTR_ADDR 0xC188 +#define CP_RB1_RPTR_ADDR_HI 0xC18C +#define CP_RB1_WPTR 0xC190 +#define CP_RB2_BASE 0xC194 +#define CP_RB2_CNTL 0xC198 +#define CP_RB2_RPTR_ADDR 0xC19C +#define CP_RB2_RPTR_ADDR_HI 0xC1A0 +#define CP_RB2_WPTR 0xC1A4 +#define CP_INT_CNTL_RING0 0xC1A8 +#define CP_INT_CNTL_RING1 0xC1AC +#define CP_INT_CNTL_RING2 0xC1B0 +# define CNTX_BUSY_INT_ENABLE (1 << 19) +# define CNTX_EMPTY_INT_ENABLE (1 << 20) +# define WAIT_MEM_SEM_INT_ENABLE (1 << 21) +# define TIME_STAMP_INT_ENABLE (1 << 26) +# define CP_RINGID2_INT_ENABLE (1 << 29) +# define CP_RINGID1_INT_ENABLE (1 << 30) +# define CP_RINGID0_INT_ENABLE (1 << 31) +#define CP_INT_STATUS_RING0 0xC1B4 +#define CP_INT_STATUS_RING1 0xC1B8 +#define CP_INT_STATUS_RING2 0xC1BC +# define WAIT_MEM_SEM_INT_STAT (1 << 21) +# define TIME_STAMP_INT_STAT (1 << 26) +# define CP_RINGID2_INT_STAT (1 << 29) +# define CP_RINGID1_INT_STAT (1 << 30) +# define CP_RINGID0_INT_STAT (1 << 31) + +#define CP_DEBUG 0xC1FC + +#define RLC_CNTL 0xC300 +# define RLC_ENABLE (1 << 0) +#define RLC_RL_BASE 0xC304 +#define RLC_RL_SIZE 0xC308 +#define RLC_LB_CNTL 0xC30C +#define RLC_SAVE_AND_RESTORE_BASE 0xC310 +#define RLC_LB_CNTR_MAX 0xC314 +#define RLC_LB_CNTR_INIT 0xC318 + +#define RLC_CLEAR_STATE_RESTORE_BASE 0xC320 + +#define RLC_UCODE_ADDR 0xC32C +#define RLC_UCODE_DATA 0xC330 + +#define RLC_GPU_CLOCK_COUNT_LSB 0xC338 +#define RLC_GPU_CLOCK_COUNT_MSB 0xC33C +#define RLC_CAPTURE_GPU_CLOCK_COUNT 0xC340 +#define RLC_MC_CNTL 0xC344 +#define RLC_UCODE_CNTL 0xC348 + +#define PA_SC_RASTER_CONFIG 0x28350 +# define RASTER_CONFIG_RB_MAP_0 0 +# define RASTER_CONFIG_RB_MAP_1 1 +# define RASTER_CONFIG_RB_MAP_2 2 +# define RASTER_CONFIG_RB_MAP_3 3 + +#define VGT_EVENT_INITIATOR 0x28a90 +# define SAMPLE_STREAMOUTSTATS1 (1 << 0) +# define SAMPLE_STREAMOUTSTATS2 (2 << 0) +# define SAMPLE_STREAMOUTSTATS3 (3 << 0) +# define CACHE_FLUSH_TS (4 << 0) +# define CACHE_FLUSH (6 << 0) +# define CS_PARTIAL_FLUSH (7 << 0) +# define VGT_STREAMOUT_RESET (10 << 0) +# define END_OF_PIPE_INCR_DE (11 << 0) +# define END_OF_PIPE_IB_END (12 << 0) +# define RST_PIX_CNT (13 << 0) +# define VS_PARTIAL_FLUSH (15 << 0) +# define PS_PARTIAL_FLUSH (16 << 0) +# define CACHE_FLUSH_AND_INV_TS_EVENT (20 << 0) +# define ZPASS_DONE (21 << 0) +# define CACHE_FLUSH_AND_INV_EVENT (22 << 0) +# define PERFCOUNTER_START (23 << 0) +# define PERFCOUNTER_STOP (24 << 0) +# define PIPELINESTAT_START (25 << 0) +# define PIPELINESTAT_STOP (26 << 0) +# define PERFCOUNTER_SAMPLE (27 << 0) +# define SAMPLE_PIPELINESTAT (30 << 0) +# define SAMPLE_STREAMOUTSTATS (32 << 0) +# define RESET_VTX_CNT (33 << 0) +# define VGT_FLUSH (36 << 0) +# define BOTTOM_OF_PIPE_TS (40 << 0) +# define DB_CACHE_FLUSH_AND_INV (42 << 0) +# define FLUSH_AND_INV_DB_DATA_TS (43 << 0) +# define FLUSH_AND_INV_DB_META (44 << 0) +# define FLUSH_AND_INV_CB_DATA_TS (45 << 0) +# define FLUSH_AND_INV_CB_META (46 << 0) +# define CS_DONE (47 << 0) +# define PS_DONE (48 << 0) +# define FLUSH_AND_INV_CB_PIXEL_DATA (49 << 0) +# define THREAD_TRACE_START (51 << 0) +# define THREAD_TRACE_STOP (52 << 0) +# define THREAD_TRACE_FLUSH (54 << 0) +# define THREAD_TRACE_FINISH (55 << 0) + +/* + * PM4 + */ +#define PACKET_TYPE0 0 +#define PACKET_TYPE1 1 +#define PACKET_TYPE2 2 +#define PACKET_TYPE3 3 + +#define CP_PACKET_GET_TYPE(h) (((h) >> 30) & 3) +#define CP_PACKET_GET_COUNT(h) (((h) >> 16) & 0x3FFF) +#define CP_PACKET0_GET_REG(h) (((h) & 0xFFFF) << 2) +#define CP_PACKET3_GET_OPCODE(h) (((h) >> 8) & 0xFF) +#define PACKET0(reg, n) ((PACKET_TYPE0 << 30) | \ + (((reg) >> 2) & 0xFFFF) | \ + ((n) & 0x3FFF) << 16) +#define CP_PACKET2 0x80000000 +#define PACKET2_PAD_SHIFT 0 +#define PACKET2_PAD_MASK (0x3fffffff << 0) + +#define PACKET2(v) (CP_PACKET2 | REG_SET(PACKET2_PAD, (v))) + +#define PACKET3(op, n) ((PACKET_TYPE3 << 30) | \ + (((op) & 0xFF) << 8) | \ + ((n) & 0x3FFF) << 16) + +#define PACKET3_COMPUTE(op, n) (PACKET3(op, n) | 1 << 1) + +/* Packet 3 types */ +#define PACKET3_NOP 0x10 +#define PACKET3_SET_BASE 0x11 +#define PACKET3_BASE_INDEX(x) ((x) << 0) +#define GDS_PARTITION_BASE 2 +#define CE_PARTITION_BASE 3 +#define PACKET3_CLEAR_STATE 0x12 +#define PACKET3_INDEX_BUFFER_SIZE 0x13 +#define PACKET3_DISPATCH_DIRECT 0x15 +#define PACKET3_DISPATCH_INDIRECT 0x16 +#define PACKET3_ALLOC_GDS 0x1B +#define PACKET3_WRITE_GDS_RAM 0x1C +#define PACKET3_ATOMIC_GDS 0x1D +#define PACKET3_ATOMIC 0x1E +#define PACKET3_OCCLUSION_QUERY 0x1F +#define PACKET3_SET_PREDICATION 0x20 +#define PACKET3_REG_RMW 0x21 +#define PACKET3_COND_EXEC 0x22 +#define PACKET3_PRED_EXEC 0x23 +#define PACKET3_DRAW_INDIRECT 0x24 +#define PACKET3_DRAW_INDEX_INDIRECT 0x25 +#define PACKET3_INDEX_BASE 0x26 +#define PACKET3_DRAW_INDEX_2 0x27 +#define PACKET3_CONTEXT_CONTROL 0x28 +#define PACKET3_INDEX_TYPE 0x2A +#define PACKET3_DRAW_INDIRECT_MULTI 0x2C +#define PACKET3_DRAW_INDEX_AUTO 0x2D +#define PACKET3_DRAW_INDEX_IMMD 0x2E +#define PACKET3_NUM_INSTANCES 0x2F +#define PACKET3_DRAW_INDEX_MULTI_AUTO 0x30 +#define PACKET3_INDIRECT_BUFFER_CONST 0x31 +#define PACKET3_INDIRECT_BUFFER 0x32 +#define PACKET3_STRMOUT_BUFFER_UPDATE 0x34 +#define PACKET3_DRAW_INDEX_OFFSET_2 0x35 +#define PACKET3_DRAW_INDEX_MULTI_ELEMENT 0x36 +#define PACKET3_WRITE_DATA 0x37 +#define WRITE_DATA_DST_SEL(x) ((x) << 8) + /* 0 - register + * 1 - memory (sync - via GRBM) + * 2 - tc/l2 + * 3 - gds + * 4 - reserved + * 5 - memory (async - direct) + */ +#define WR_ONE_ADDR (1 << 16) +#define WR_CONFIRM (1 << 20) +#define WRITE_DATA_ENGINE_SEL(x) ((x) << 30) + /* 0 - me + * 1 - pfp + * 2 - ce + */ +#define PACKET3_DRAW_INDEX_INDIRECT_MULTI 0x38 +#define PACKET3_MEM_SEMAPHORE 0x39 +#define PACKET3_MPEG_INDEX 0x3A +#define PACKET3_COPY_DW 0x3B +#define PACKET3_WAIT_REG_MEM 0x3C +#define PACKET3_MEM_WRITE 0x3D +#define PACKET3_COPY_DATA 0x40 +#define PACKET3_CP_DMA 0x41 +/* 1. header + * 2. SRC_ADDR_LO or DATA [31:0] + * 3. CP_SYNC [31] | SRC_SEL [30:29] | ENGINE [27] | DST_SEL [21:20] | + * SRC_ADDR_HI [7:0] + * 4. DST_ADDR_LO [31:0] + * 5. DST_ADDR_HI [7:0] + * 6. COMMAND [30:21] | BYTE_COUNT [20:0] + */ +# define PACKET3_CP_DMA_DST_SEL(x) ((x) << 20) + /* 0 - SRC_ADDR + * 1 - GDS + */ +# define PACKET3_CP_DMA_ENGINE(x) ((x) << 27) + /* 0 - ME + * 1 - PFP + */ +# define PACKET3_CP_DMA_SRC_SEL(x) ((x) << 29) + /* 0 - SRC_ADDR + * 1 - GDS + * 2 - DATA + */ +# define PACKET3_CP_DMA_CP_SYNC (1 << 31) +/* COMMAND */ +# define PACKET3_CP_DMA_DIS_WC (1 << 21) +# define PACKET3_CP_DMA_CMD_SRC_SWAP(x) ((x) << 23) + /* 0 - none + * 1 - 8 in 16 + * 2 - 8 in 32 + * 3 - 8 in 64 + */ +# define PACKET3_CP_DMA_CMD_DST_SWAP(x) ((x) << 24) + /* 0 - none + * 1 - 8 in 16 + * 2 - 8 in 32 + * 3 - 8 in 64 + */ +# define PACKET3_CP_DMA_CMD_SAS (1 << 26) + /* 0 - memory + * 1 - register + */ +# define PACKET3_CP_DMA_CMD_DAS (1 << 27) + /* 0 - memory + * 1 - register + */ +# define PACKET3_CP_DMA_CMD_SAIC (1 << 28) +# define PACKET3_CP_DMA_CMD_DAIC (1 << 29) +# define PACKET3_CP_DMA_CMD_RAW_WAIT (1 << 30) +#define PACKET3_PFP_SYNC_ME 0x42 +#define PACKET3_SURFACE_SYNC 0x43 +# define PACKET3_DEST_BASE_0_ENA (1 << 0) +# define PACKET3_DEST_BASE_1_ENA (1 << 1) +# define PACKET3_CB0_DEST_BASE_ENA (1 << 6) +# define PACKET3_CB1_DEST_BASE_ENA (1 << 7) +# define PACKET3_CB2_DEST_BASE_ENA (1 << 8) +# define PACKET3_CB3_DEST_BASE_ENA (1 << 9) +# define PACKET3_CB4_DEST_BASE_ENA (1 << 10) +# define PACKET3_CB5_DEST_BASE_ENA (1 << 11) +# define PACKET3_CB6_DEST_BASE_ENA (1 << 12) +# define PACKET3_CB7_DEST_BASE_ENA (1 << 13) +# define PACKET3_DB_DEST_BASE_ENA (1 << 14) +# define PACKET3_DEST_BASE_2_ENA (1 << 19) +# define PACKET3_DEST_BASE_3_ENA (1 << 21) +# define PACKET3_TCL1_ACTION_ENA (1 << 22) +# define PACKET3_TC_ACTION_ENA (1 << 23) +# define PACKET3_CB_ACTION_ENA (1 << 25) +# define PACKET3_DB_ACTION_ENA (1 << 26) +# define PACKET3_SH_KCACHE_ACTION_ENA (1 << 27) +# define PACKET3_SH_ICACHE_ACTION_ENA (1 << 29) +#define PACKET3_ME_INITIALIZE 0x44 +#define PACKET3_ME_INITIALIZE_DEVICE_ID(x) ((x) << 16) +#define PACKET3_COND_WRITE 0x45 +#define PACKET3_EVENT_WRITE 0x46 +#define EVENT_TYPE(x) ((x) << 0) +#define EVENT_INDEX(x) ((x) << 8) + /* 0 - any non-TS event + * 1 - ZPASS_DONE + * 2 - SAMPLE_PIPELINESTAT + * 3 - SAMPLE_STREAMOUTSTAT* + * 4 - *S_PARTIAL_FLUSH + * 5 - EOP events + * 6 - EOS events + * 7 - CACHE_FLUSH, CACHE_FLUSH_AND_INV_EVENT + */ +#define INV_L2 (1 << 20) + /* INV TC L2 cache when EVENT_INDEX = 7 */ +#define PACKET3_EVENT_WRITE_EOP 0x47 +#define DATA_SEL(x) ((x) << 29) + /* 0 - discard + * 1 - send low 32bit data + * 2 - send 64bit data + * 3 - send 64bit counter value + */ +#define INT_SEL(x) ((x) << 24) + /* 0 - none + * 1 - interrupt only (DATA_SEL = 0) + * 2 - interrupt when data write is confirmed + */ +#define PACKET3_EVENT_WRITE_EOS 0x48 +#define PACKET3_PREAMBLE_CNTL 0x4A +# define PACKET3_PREAMBLE_BEGIN_CLEAR_STATE (2 << 28) +# define PACKET3_PREAMBLE_END_CLEAR_STATE (3 << 28) +#define PACKET3_ONE_REG_WRITE 0x57 +#define PACKET3_LOAD_CONFIG_REG 0x5F +#define PACKET3_LOAD_CONTEXT_REG 0x60 +#define PACKET3_LOAD_SH_REG 0x61 +#define PACKET3_SET_CONFIG_REG 0x68 +#define PACKET3_SET_CONFIG_REG_START 0x00008000 +#define PACKET3_SET_CONFIG_REG_END 0x0000b000 +#define PACKET3_SET_CONTEXT_REG 0x69 +#define PACKET3_SET_CONTEXT_REG_START 0x00028000 +#define PACKET3_SET_CONTEXT_REG_END 0x00029000 +#define PACKET3_SET_CONTEXT_REG_INDIRECT 0x73 +#define PACKET3_SET_RESOURCE_INDIRECT 0x74 +#define PACKET3_SET_SH_REG 0x76 +#define PACKET3_SET_SH_REG_START 0x0000b000 +#define PACKET3_SET_SH_REG_END 0x0000c000 +#define PACKET3_SET_SH_REG_OFFSET 0x77 +#define PACKET3_ME_WRITE 0x7A +#define PACKET3_SCRATCH_RAM_WRITE 0x7D +#define PACKET3_SCRATCH_RAM_READ 0x7E +#define PACKET3_CE_WRITE 0x7F +#define PACKET3_LOAD_CONST_RAM 0x80 +#define PACKET3_WRITE_CONST_RAM 0x81 +#define PACKET3_WRITE_CONST_RAM_OFFSET 0x82 +#define PACKET3_DUMP_CONST_RAM 0x83 +#define PACKET3_INCREMENT_CE_COUNTER 0x84 +#define PACKET3_INCREMENT_DE_COUNTER 0x85 +#define PACKET3_WAIT_ON_CE_COUNTER 0x86 +#define PACKET3_WAIT_ON_DE_COUNTER 0x87 +#define PACKET3_WAIT_ON_DE_COUNTER_DIFF 0x88 +#define PACKET3_SET_CE_DE_COUNTERS 0x89 +#define PACKET3_WAIT_ON_AVAIL_BUFFER 0x8A +#define PACKET3_SWITCH_BUFFER 0x8B + +/* ASYNC DMA - first instance at 0xd000, second at 0xd800 */ +#define DMA0_REGISTER_OFFSET 0x0 /* not a register */ +#define DMA1_REGISTER_OFFSET 0x800 /* not a register */ + +#define DMA_RB_CNTL 0xd000 +# define DMA_RB_ENABLE (1 << 0) +# define DMA_RB_SIZE(x) ((x) << 1) /* log2 */ +# define DMA_RB_SWAP_ENABLE (1 << 9) /* 8IN32 */ +# define DMA_RPTR_WRITEBACK_ENABLE (1 << 12) +# define DMA_RPTR_WRITEBACK_SWAP_ENABLE (1 << 13) /* 8IN32 */ +# define DMA_RPTR_WRITEBACK_TIMER(x) ((x) << 16) /* log2 */ +#define DMA_RB_BASE 0xd004 +#define DMA_RB_RPTR 0xd008 +#define DMA_RB_WPTR 0xd00c + +#define DMA_RB_RPTR_ADDR_HI 0xd01c +#define DMA_RB_RPTR_ADDR_LO 0xd020 + +#define DMA_IB_CNTL 0xd024 +# define DMA_IB_ENABLE (1 << 0) +# define DMA_IB_SWAP_ENABLE (1 << 4) +#define DMA_IB_RPTR 0xd028 +#define DMA_CNTL 0xd02c +# define TRAP_ENABLE (1 << 0) +# define SEM_INCOMPLETE_INT_ENABLE (1 << 1) +# define SEM_WAIT_INT_ENABLE (1 << 2) +# define DATA_SWAP_ENABLE (1 << 3) +# define FENCE_SWAP_ENABLE (1 << 4) +# define CTXEMPTY_INT_ENABLE (1 << 28) +#define DMA_STATUS_REG 0xd034 +# define DMA_IDLE (1 << 0) +#define DMA_TILING_CONFIG 0xd0b8 + +#define DMA_PACKET(cmd, b, t, s, n) ((((cmd) & 0xF) << 28) | \ + (((b) & 0x1) << 26) | \ + (((t) & 0x1) << 23) | \ + (((s) & 0x1) << 22) | \ + (((n) & 0xFFFFF) << 0)) + +#define DMA_IB_PACKET(cmd, vmid, n) ((((cmd) & 0xF) << 28) | \ + (((vmid) & 0xF) << 20) | \ + (((n) & 0xFFFFF) << 0)) + +#define DMA_PTE_PDE_PACKET(n) ((2 << 28) | \ + (1 << 26) | \ + (1 << 21) | \ + (((n) & 0xFFFFF) << 0)) + +/* async DMA Packet types */ +#define DMA_PACKET_WRITE 0x2 +#define DMA_PACKET_COPY 0x3 +#define DMA_PACKET_INDIRECT_BUFFER 0x4 +#define DMA_PACKET_SEMAPHORE 0x5 +#define DMA_PACKET_FENCE 0x6 +#define DMA_PACKET_TRAP 0x7 +#define DMA_PACKET_SRBM_WRITE 0x9 +#define DMA_PACKET_CONSTANT_FILL 0xd +#define DMA_PACKET_NOP 0xf + +#endif diff --git a/sys/dev/drm2/ttm/ttm_bo.c b/sys/dev/drm2/ttm/ttm_bo.c index 9cb9336ea7f..3b135404ed6 100644 --- a/sys/dev/drm2/ttm/ttm_bo.c +++ b/sys/dev/drm2/ttm/ttm_bo.c @@ -131,7 +131,7 @@ static void ttm_bo_release_list(struct ttm_buffer_object *bo) ttm_mem_global_free(bdev->glob->mem_glob, acc_size); } -int +static int ttm_bo_wait_unreserved_locked(struct ttm_buffer_object *bo, bool interruptible) { const char *wmsg; @@ -145,7 +145,7 @@ ttm_bo_wait_unreserved_locked(struct ttm_buffer_object *bo, bool interruptible) flags = 0; wmsg = "ttbowu"; } - while (!ttm_bo_is_reserved(bo)) { + while (ttm_bo_is_reserved(bo)) { ret = -msleep(bo, &bo->glob->lru_lock, flags, wmsg, 0); if (ret != 0) break; @@ -196,13 +196,13 @@ int ttm_bo_del_from_lru(struct ttm_buffer_object *bo) return put_count; } -int ttm_bo_reserve_locked(struct ttm_buffer_object *bo, +int ttm_bo_reserve_nolru(struct ttm_buffer_object *bo, bool interruptible, bool no_wait, bool use_sequence, uint32_t sequence) { int ret; - while (unlikely(atomic_read(&bo->reserved) != 0)) { + while (unlikely(atomic_xchg(&bo->reserved, 1) != 0)) { /** * Deadlock avoidance for multi-bo reserving. */ @@ -224,22 +224,35 @@ int ttm_bo_reserve_locked(struct ttm_buffer_object *bo, return -EBUSY; ret = ttm_bo_wait_unreserved_locked(bo, interruptible); + if (unlikely(ret)) return ret; } - atomic_set(&bo->reserved, 1); if (use_sequence) { + bool wake_up = false; /** * Wake up waiters that may need to recheck for deadlock, * if we decreased the sequence number. */ if (unlikely((bo->val_seq - sequence < (1 << 31)) || !bo->seq_valid)) - wakeup(bo); + wake_up = true; + /* + * In the worst case with memory ordering these values can be + * seen in the wrong order. However since we call wake_up_all + * in that case, this will hopefully not pose a problem, + * and the worst case would only cause someone to accidentally + * hit -EAGAIN in ttm_bo_reserve when they see old value of + * val_seq. However this would only happen if seq_valid was + * written before val_seq was, and just means some slightly + * increased cpu usage + */ bo->val_seq = sequence; bo->seq_valid = true; + if (wake_up) + wakeup(bo); } else { bo->seq_valid = false; } @@ -268,15 +281,67 @@ int ttm_bo_reserve(struct ttm_buffer_object *bo, int put_count = 0; int ret; - mtx_lock(&glob->lru_lock); - ret = ttm_bo_reserve_locked(bo, interruptible, no_wait, use_sequence, - sequence); - if (likely(ret == 0)) + mtx_lock(&bo->glob->lru_lock); + ret = ttm_bo_reserve_nolru(bo, interruptible, no_wait, use_sequence, + sequence); + if (likely(ret == 0)) { put_count = ttm_bo_del_from_lru(bo); - mtx_unlock(&glob->lru_lock); + mtx_unlock(&glob->lru_lock); + ttm_bo_list_ref_sub(bo, put_count, true); + } else + mtx_unlock(&bo->glob->lru_lock); - ttm_bo_list_ref_sub(bo, put_count, true); + return ret; +} +int ttm_bo_reserve_slowpath_nolru(struct ttm_buffer_object *bo, + bool interruptible, uint32_t sequence) +{ + bool wake_up = false; + int ret; + + while (unlikely(atomic_xchg(&bo->reserved, 1) != 0)) { + if (bo->seq_valid && sequence == bo->val_seq) { + DRM_ERROR( + "%s: bo->seq_valid && sequence == bo->val_seq", + __func__); + } + + ret = ttm_bo_wait_unreserved_locked(bo, interruptible); + + if (unlikely(ret)) + return ret; + } + + if ((bo->val_seq - sequence < (1 << 31)) || !bo->seq_valid) + wake_up = true; + + /** + * Wake up waiters that may need to recheck for deadlock, + * if we decreased the sequence number. + */ + bo->val_seq = sequence; + bo->seq_valid = true; + if (wake_up) + wakeup(bo); + + return 0; +} + +int ttm_bo_reserve_slowpath(struct ttm_buffer_object *bo, + bool interruptible, uint32_t sequence) +{ + struct ttm_bo_global *glob = bo->glob; + int put_count, ret; + + mtx_lock(&glob->lru_lock); + ret = ttm_bo_reserve_slowpath_nolru(bo, interruptible, sequence); + if (likely(!ret)) { + put_count = ttm_bo_del_from_lru(bo); + mtx_unlock(&glob->lru_lock); + ttm_bo_list_ref_sub(bo, put_count, true); + } else + mtx_unlock(&glob->lru_lock); return ret; } @@ -412,6 +477,7 @@ static int ttm_bo_handle_move_mem(struct ttm_buffer_object *bo, bo->mem = tmp_mem; bdev->driver->move_notify(bo, mem); bo->mem = *mem; + *mem = tmp_mem; } goto out_err; @@ -488,7 +554,7 @@ static void ttm_bo_cleanup_refs_or_queue(struct ttm_buffer_object *bo) int ret; mtx_lock(&glob->lru_lock); - ret = ttm_bo_reserve_locked(bo, false, true, false, 0); + ret = ttm_bo_reserve_nolru(bo, false, true, false, 0); mtx_lock(&bdev->fence_lock); (void) ttm_bo_wait(bo, false, false, true); @@ -580,7 +646,7 @@ static int ttm_bo_cleanup_refs_and_unlock(struct ttm_buffer_object *bo, return ret; mtx_lock(&glob->lru_lock); - ret = ttm_bo_reserve_locked(bo, false, true, false, 0); + ret = ttm_bo_reserve_nolru(bo, false, true, false, 0); /* * We raced, and lost, someone else holds the reservation now, @@ -644,7 +710,12 @@ static int ttm_bo_delayed_delete(struct ttm_bo_device *bdev, bool remove_all) refcount_acquire(&nentry->list_kref); } - ret = ttm_bo_reserve_locked(entry, false, !remove_all, false, 0); + ret = ttm_bo_reserve_nolru(entry, false, true, false, 0); + if (remove_all && ret) { + ret = ttm_bo_reserve_nolru(entry, false, false, + false, 0); + } + if (!ret) ret = ttm_bo_cleanup_refs_and_unlock(entry, false, !remove_all); @@ -796,7 +867,7 @@ static int ttm_mem_evict_first(struct ttm_bo_device *bdev, mtx_lock(&glob->lru_lock); list_for_each_entry(bo, &man->lru, lru) { - ret = ttm_bo_reserve_locked(bo, false, true, false, 0); + ret = ttm_bo_reserve_nolru(bo, false, true, false, 0); if (!ret) break; } @@ -1565,13 +1636,8 @@ bool ttm_mem_reg_is_pci(struct ttm_bo_device *bdev, struct ttm_mem_reg *mem) void ttm_bo_unmap_virtual_locked(struct ttm_buffer_object *bo) { - struct ttm_bo_device *bdev = bo->bdev; - /* off_t offset = (off_t)bo->addr_space_offset;XXXKIB */ - /* off_t holelen = ((off_t)bo->mem.num_pages) << PAGE_SHIFT;XXXKIB */ - if (!bdev->dev_mapping) - return; - /* unmap_mapping_range(bdev->dev_mapping, offset, holelen, 1); XXXKIB */ + ttm_bo_release_mmap(bo); ttm_mem_io_free_vm(bo); } @@ -1737,7 +1803,7 @@ static int ttm_bo_swapout(struct ttm_mem_shrink *shrink) mtx_lock(&glob->lru_lock); list_for_each_entry(bo, &glob->swap_lru, swap) { - ret = ttm_bo_reserve_locked(bo, false, true, false, 0); + ret = ttm_bo_reserve_nolru(bo, false, true, false, 0); if (!ret) break; } diff --git a/sys/dev/drm2/ttm/ttm_bo_driver.h b/sys/dev/drm2/ttm/ttm_bo_driver.h index 3f08976032d..68c5c0ea001 100644 --- a/sys/dev/drm2/ttm/ttm_bo_driver.h +++ b/sys/dev/drm2/ttm/ttm_bo_driver.h @@ -791,16 +791,7 @@ extern void ttm_mem_io_unlock(struct ttm_mem_type_manager *man); * to make room for a buffer already reserved. (Buffers are reserved before * they are evicted). The following algorithm prevents such deadlocks from * occurring: - * 1) Buffers are reserved with the lru spinlock held. Upon successful - * reservation they are removed from the lru list. This stops a reserved buffer - * from being evicted. However the lru spinlock is released between the time - * a buffer is selected for eviction and the time it is reserved. - * Therefore a check is made when a buffer is reserved for eviction, that it - * is still the first buffer in the lru list, before it is removed from the - * list. @check_lru == 1 forces this check. If it fails, the function returns - * -EINVAL, and the caller should then choose a new buffer to evict and repeat - * the procedure. - * 2) Processes attempting to reserve multiple buffers other than for eviction, + * Processes attempting to reserve multiple buffers other than for eviction, * (typically execbuf), should first obtain a unique 32-bit * validation sequence number, * and call this function with @use_sequence == 1 and @sequence == the unique @@ -831,9 +822,39 @@ extern int ttm_bo_reserve(struct ttm_buffer_object *bo, bool interruptible, bool no_wait, bool use_sequence, uint32_t sequence); +/** + * ttm_bo_reserve_slowpath_nolru: + * @bo: A pointer to a struct ttm_buffer_object. + * @interruptible: Sleep interruptible if waiting. + * @sequence: Set (@bo)->sequence to this value after lock + * + * This is called after ttm_bo_reserve returns -EAGAIN and we backed off + * from all our other reservations. Because there are no other reservations + * held by us, this function cannot deadlock any more. + * + * Will not remove reserved buffers from the lru lists. + * Otherwise identical to ttm_bo_reserve_slowpath. + */ +extern int ttm_bo_reserve_slowpath_nolru(struct ttm_buffer_object *bo, + bool interruptible, + uint32_t sequence); + /** - * ttm_bo_reserve_locked: + * ttm_bo_reserve_slowpath: + * @bo: A pointer to a struct ttm_buffer_object. + * @interruptible: Sleep interruptible if waiting. + * @sequence: Set (@bo)->sequence to this value after lock + * + * This is called after ttm_bo_reserve returns -EAGAIN and we backed off + * from all our other reservations. Because there are no other reservations + * held by us, this function cannot deadlock any more. + */ +extern int ttm_bo_reserve_slowpath(struct ttm_buffer_object *bo, + bool interruptible, uint32_t sequence); + +/** + * ttm_bo_reserve_nolru: * * @bo: A pointer to a struct ttm_buffer_object. * @interruptible: Sleep interruptible if waiting. @@ -841,9 +862,7 @@ extern int ttm_bo_reserve(struct ttm_buffer_object *bo, * @use_sequence: If @bo is already reserved, Only sleep waiting for * it to become unreserved if @sequence < (@bo)->sequence. * - * Must be called with struct ttm_bo_global::lru_lock held, - * and will not remove reserved buffers from the lru lists. - * The function may release the LRU spinlock if it needs to sleep. + * Will not remove reserved buffers from the lru lists. * Otherwise identical to ttm_bo_reserve. * * Returns: @@ -856,7 +875,7 @@ extern int ttm_bo_reserve(struct ttm_buffer_object *bo, * -EDEADLK: Bo already reserved using @sequence. This error code will only * be returned if @use_sequence is set to true. */ -extern int ttm_bo_reserve_locked(struct ttm_buffer_object *bo, +extern int ttm_bo_reserve_nolru(struct ttm_buffer_object *bo, bool interruptible, bool no_wait, bool use_sequence, uint32_t sequence); @@ -880,18 +899,6 @@ extern void ttm_bo_unreserve(struct ttm_buffer_object *bo); */ extern void ttm_bo_unreserve_locked(struct ttm_buffer_object *bo); -/** - * ttm_bo_wait_unreserved - * - * @bo: A pointer to a struct ttm_buffer_object. - * - * Wait for a struct ttm_buffer_object to become unreserved. - * This is typically used in the execbuf code to relax cpu-usage when - * a potential deadlock condition backoff. - */ -extern int ttm_bo_wait_unreserved_locked(struct ttm_buffer_object *bo, - bool interruptible); - /* * ttm_bo_util.c */ diff --git a/sys/dev/drm2/ttm/ttm_bo_util.c b/sys/dev/drm2/ttm/ttm_bo_util.c index b977acba022..aed8e0e4a7e 100644 --- a/sys/dev/drm2/ttm/ttm_bo_util.c +++ b/sys/dev/drm2/ttm/ttm_bo_util.c @@ -321,8 +321,12 @@ int ttm_bo_move_memcpy(struct ttm_buffer_object *bo, if (ttm->state == tt_unpopulated) { ret = ttm->bdev->driver->ttm_tt_populate(ttm); - if (ret) + if (ret) { + /* if we fail here don't nuke the mm node + * as the bo still owns it */ + old_copy.mm_node = NULL; goto out1; + } } add = 0; @@ -346,8 +350,11 @@ int ttm_bo_move_memcpy(struct ttm_buffer_object *bo, prot); } else ret = ttm_copy_io_page(new_iomap, old_iomap, page); - if (ret) + if (ret) { + /* failing here, means keep old copy as-is */ + old_copy.mm_node = NULL; goto out1; + } } mb(); out2: @@ -393,11 +400,13 @@ static void ttm_transfered_destroy(struct ttm_buffer_object *bo) static int ttm_buffer_object_transfer(struct ttm_buffer_object *bo, - void *sync_obj, struct ttm_buffer_object **new_obj) + struct ttm_buffer_object **new_obj) { struct ttm_buffer_object *fbo; + struct ttm_bo_device *bdev = bo->bdev; + struct ttm_bo_driver *driver = bdev->driver; - fbo = malloc(sizeof(*fbo), M_TTM_TRANSF_OBJ, M_ZERO | M_WAITOK); + fbo = malloc(sizeof(*fbo), M_TTM_TRANSF_OBJ, M_WAITOK); *fbo = *bo; /** @@ -412,7 +421,12 @@ ttm_buffer_object_transfer(struct ttm_buffer_object *bo, fbo->vm_node = NULL; atomic_set(&fbo->cpu_writers, 0); - fbo->sync_obj = sync_obj; + mtx_lock(&bdev->fence_lock); + if (bo->sync_obj) + fbo->sync_obj = driver->sync_obj_ref(bo->sync_obj); + else + fbo->sync_obj = NULL; + mtx_unlock(&bdev->fence_lock); refcount_init(&fbo->list_kref, 1); refcount_init(&fbo->kref, 1); fbo->destroy = &ttm_transfered_destroy; @@ -498,8 +512,7 @@ static int ttm_bo_kmap_ttm(struct ttm_buffer_object *bo, ttm_io_prot(mem->placement); map->bo_kmap_type = ttm_bo_map_vmap; map->num_pages = num_pages; - map->virtual = (void *)kmem_alloc_nofault(kernel_map, - num_pages * PAGE_SIZE); + map->virtual = (void *)kva_alloc(num_pages * PAGE_SIZE); if (map->virtual != NULL) { for (i = 0; i < num_pages; i++) { /* XXXKIB hack */ @@ -561,7 +574,7 @@ void ttm_bo_kunmap(struct ttm_bo_kmap_obj *map) break; case ttm_bo_map_vmap: pmap_qremove((vm_offset_t)(map->virtual), map->num_pages); - kmem_free(kernel_map, (vm_offset_t)map->virtual, + kva_free((vm_offset_t)map->virtual, map->num_pages * PAGE_SIZE); break; case ttm_bo_map_kmap: @@ -593,7 +606,6 @@ int ttm_bo_move_accel_cleanup(struct ttm_buffer_object *bo, int ret; struct ttm_buffer_object *ghost_obj; void *tmp_obj = NULL; - void *sync_obj_ref; mtx_lock(&bdev->fence_lock); if (bo->sync_obj) { @@ -626,14 +638,11 @@ int ttm_bo_move_accel_cleanup(struct ttm_buffer_object *bo, */ set_bit(TTM_BO_PRIV_FLAG_MOVING, &bo->priv_flags); - - sync_obj_ref = bo->bdev->driver->sync_obj_ref(bo->sync_obj); mtx_unlock(&bdev->fence_lock); - /* ttm_buffer_object_transfer accesses bo->sync_obj */ - ret = ttm_buffer_object_transfer(bo, sync_obj_ref, &ghost_obj); if (tmp_obj) driver->sync_obj_unref(&tmp_obj); + ret = ttm_buffer_object_transfer(bo, &ghost_obj); if (ret) return ret; diff --git a/sys/dev/drm2/ttm/ttm_bo_vm.c b/sys/dev/drm2/ttm/ttm_bo_vm.c index 08e1c8777c6..b87380cfe1d 100644 --- a/sys/dev/drm2/ttm/ttm_bo_vm.c +++ b/sys/dev/drm2/ttm/ttm_bo_vm.c @@ -47,6 +47,7 @@ __FBSDID("$FreeBSD$"); #include #include +#include #define TTM_BO_VM_NUM_PREFAULT 16 @@ -153,7 +154,23 @@ reserve: mtx_lock(&bdev->fence_lock); if (test_bit(TTM_BO_PRIV_FLAG_MOVING, &bo->priv_flags)) { - ret = ttm_bo_wait(bo, false, true, false); + /* + * Here, the behavior differs between Linux and FreeBSD. + * + * On Linux, the wait is interruptible (3rd argument to + * ttm_bo_wait). There must be some mechanism to resume + * page fault handling, once the signal is processed. + * + * On FreeBSD, the wait is uninteruptible. This is not a + * problem as we can't end up with an unkillable process + * here, because the wait will eventually time out. + * + * An example of this situation is the Xorg process + * which uses SIGALRM internally. The signal could + * interrupt the wait, causing the page fault to fail + * and the process to receive SIGSEGV. + */ + ret = ttm_bo_wait(bo, false, false, false); mtx_unlock(&bdev->fence_lock); if (unlikely(ret != 0)) { retval = VM_PAGER_ERROR; @@ -212,23 +229,33 @@ reserve: } VM_OBJECT_WLOCK(vm_obj); - if ((m->flags & VPO_BUSY) != 0) { - vm_page_sleep(m, "ttmpbs"); + if (vm_page_busied(m)) { + vm_page_lock(m); + VM_OBJECT_WUNLOCK(vm_obj); + vm_page_busy_sleep(m, "ttmpbs"); + VM_OBJECT_WLOCK(vm_obj); ttm_mem_io_unlock(man); ttm_bo_unreserve(bo); goto retry; } - m->valid = VM_PAGE_BITS_ALL; - *mres = m; m1 = vm_page_lookup(vm_obj, OFF_TO_IDX(offset)); if (m1 == NULL) { - vm_page_insert(m, vm_obj, OFF_TO_IDX(offset)); + if (vm_page_insert(m, vm_obj, OFF_TO_IDX(offset))) { + VM_OBJECT_WUNLOCK(vm_obj); + VM_WAIT; + VM_OBJECT_WLOCK(vm_obj); + ttm_mem_io_unlock(man); + ttm_bo_unreserve(bo); + goto retry; + } } else { KASSERT(m == m1, ("inconsistent insert bo %p m %p m1 %p offset %jx", bo, m, m1, (uintmax_t)offset)); } - vm_page_busy(m); + m->valid = VM_PAGE_BITS_ALL; + *mres = m; + vm_page_xbusy(m); if (oldm != NULL) { vm_page_lock(oldm); @@ -258,8 +285,16 @@ ttm_bo_vm_ctor(void *handle, vm_ooffset_t size, vm_prot_t prot, { /* - * We don't acquire a reference on bo->kref here, because it was - * already done in ttm_bo_mmap_single(). + * On Linux, a reference to the buffer object is acquired here. + * The reason is that this function is not called when the + * mmap() is initialized, but only when a process forks for + * instance. Therefore on Linux, the reference on the bo is + * acquired either in ttm_bo_mmap() or ttm_bo_vm_open(). It's + * then released in ttm_bo_vm_close(). + * + * Here, this function is called during mmap() intialization. + * Thus, the reference acquired in ttm_bo_mmap_single() is + * sufficient. */ *color = 0; @@ -326,6 +361,32 @@ out_unref: return ret; } +void +ttm_bo_release_mmap(struct ttm_buffer_object *bo) +{ + vm_object_t vm_obj; + vm_page_t m; + int i; + + vm_obj = cdev_pager_lookup(bo); + if (vm_obj == NULL) + return; + + VM_OBJECT_WLOCK(vm_obj); +retry: + for (i = 0; i < bo->num_pages; i++) { + m = vm_page_lookup(vm_obj, i); + if (m == NULL) + continue; + if (vm_page_sleep_if_busy(m, "ttm_unm")) + goto retry; + cdev_pager_free_page(vm_obj, m); + } + VM_OBJECT_WUNLOCK(vm_obj); + + vm_object_deallocate(vm_obj); +} + #if 0 int ttm_fbdev_mmap(struct vm_area_struct *vma, struct ttm_buffer_object *bo) { diff --git a/sys/dev/drm2/ttm/ttm_execbuf_util.c b/sys/dev/drm2/ttm/ttm_execbuf_util.c index ce5cf158928..6710d887fcc 100644 --- a/sys/dev/drm2/ttm/ttm_execbuf_util.c +++ b/sys/dev/drm2/ttm/ttm_execbuf_util.c @@ -83,19 +83,6 @@ static void ttm_eu_list_ref_sub(struct list_head *list) } } -static int ttm_eu_wait_unreserved_locked(struct list_head *list, - struct ttm_buffer_object *bo) -{ - int ret; - - ttm_eu_del_from_lru_locked(list); - ret = ttm_bo_wait_unreserved_locked(bo, true); - if (unlikely(ret != 0)) - ttm_eu_backoff_reservation_locked(list); - return ret; -} - - void ttm_eu_backoff_reservation(struct list_head *list) { struct ttm_validate_buffer *entry; @@ -143,47 +130,62 @@ int ttm_eu_reserve_buffers(struct list_head *list) glob = entry->bo->glob; mtx_lock(&glob->lru_lock); -retry_locked: val_seq = entry->bo->bdev->val_seq++; +retry_locked: list_for_each_entry(entry, list, head) { struct ttm_buffer_object *bo = entry->bo; -retry_this_bo: - ret = ttm_bo_reserve_locked(bo, true, true, true, val_seq); + /* already slowpath reserved? */ + if (entry->reserved) + continue; + + ret = ttm_bo_reserve_nolru(bo, true, true, true, val_seq); switch (ret) { case 0: break; case -EBUSY: - ret = ttm_eu_wait_unreserved_locked(list, bo); - if (unlikely(ret != 0)) { - mtx_unlock(&glob->lru_lock); - ttm_eu_list_ref_sub(list); - return ret; - } - goto retry_this_bo; + ttm_eu_del_from_lru_locked(list); + ret = ttm_bo_reserve_nolru(bo, true, false, + true, val_seq); + if (!ret) + break; + + if (unlikely(ret != -EAGAIN)) + goto err; + + /* fallthrough */ case -EAGAIN: ttm_eu_backoff_reservation_locked(list); + + /* + * temporarily increase sequence number every retry, + * to prevent us from seeing our old reservation + * sequence when someone else reserved the buffer, + * but hasn't updated the seq_valid/seqno members yet. + */ + val_seq = entry->bo->bdev->val_seq++; + ttm_eu_list_ref_sub(list); - ret = ttm_bo_wait_unreserved_locked(bo, true); + ret = ttm_bo_reserve_slowpath_nolru(bo, true, val_seq); if (unlikely(ret != 0)) { mtx_unlock(&glob->lru_lock); return ret; } + entry->reserved = true; + if (unlikely(atomic_read(&bo->cpu_writers) > 0)) { + ret = -EBUSY; + goto err; + } goto retry_locked; default: - ttm_eu_backoff_reservation_locked(list); - mtx_unlock(&glob->lru_lock); - ttm_eu_list_ref_sub(list); - return ret; + goto err; } entry->reserved = true; if (unlikely(atomic_read(&bo->cpu_writers) > 0)) { - ttm_eu_backoff_reservation_locked(list); - mtx_unlock(&glob->lru_lock); - ttm_eu_list_ref_sub(list); - return -EBUSY; + ret = -EBUSY; + goto err; } } @@ -192,6 +194,12 @@ retry_this_bo: ttm_eu_list_ref_sub(list); return 0; + +err: + ttm_eu_backoff_reservation_locked(list); + mtx_unlock(&glob->lru_lock); + ttm_eu_list_ref_sub(list); + return ret; } void ttm_eu_fence_buffer_objects(struct list_head *list, void *sync_obj) diff --git a/sys/dev/drm2/ttm/ttm_page_alloc.c b/sys/dev/drm2/ttm/ttm_page_alloc.c index 9a30a4640e6..3c0f18aa397 100644 --- a/sys/dev/drm2/ttm/ttm_page_alloc.c +++ b/sys/dev/drm2/ttm/ttm_page_alloc.c @@ -49,8 +49,6 @@ __FBSDID("$FreeBSD$"); #include #endif -#define VM_ALLOC_DMA32 VM_ALLOC_RESERVED1 - #define NUM_PAGES_TO_ALLOC (PAGE_SIZE/sizeof(vm_page_t)) #define SMALL_ALLOCATION 16 #define FREE_ALL_PAGES (~0U) @@ -320,6 +318,7 @@ static int ttm_page_pool_free(struct ttm_page_pool *pool, unsigned nr_free) vm_page_t *pages_to_free; unsigned freed_pages = 0, npages_to_free = nr_free; + unsigned i; if (NUM_PAGES_TO_ALLOC < nr_free) npages_to_free = NUM_PAGES_TO_ALLOC; @@ -330,7 +329,7 @@ static int ttm_page_pool_free(struct ttm_page_pool *pool, unsigned nr_free) restart: mtx_lock(&pool->lock); - TAILQ_FOREACH_REVERSE_SAFE(p, &pool->list, pglist, pageq, p1) { + TAILQ_FOREACH_REVERSE_SAFE(p, &pool->list, pglist, plinks.q, p1) { if (freed_pages >= npages_to_free) break; @@ -338,7 +337,8 @@ restart: /* We can only remove NUM_PAGES_TO_ALLOC at a time. */ if (freed_pages >= NUM_PAGES_TO_ALLOC) { /* remove range of pages from the pool */ - TAILQ_REMOVE(&pool->list, p, pageq); + for (i = 0; i < freed_pages; i++) + TAILQ_REMOVE(&pool->list, pages_to_free[i], plinks.q); ttm_pool_update_free_locked(pool, freed_pages); /** @@ -373,7 +373,8 @@ restart: /* remove range of pages from the pool */ if (freed_pages) { - TAILQ_REMOVE(&pool->list, p, pageq); + for (i = 0; i < freed_pages; i++) + TAILQ_REMOVE(&pool->list, pages_to_free[i], plinks.q); ttm_pool_update_free_locked(pool, freed_pages); nr_free -= freed_pages; @@ -470,7 +471,7 @@ static void ttm_handle_caching_state_failure(struct pglist *pages, unsigned i; /* Failed pages have to be freed */ for (i = 0; i < cpages; ++i) { - TAILQ_REMOVE(pages, failed_pages[i], pageq); + TAILQ_REMOVE(pages, failed_pages[i], plinks.q); ttm_vm_page_free(failed_pages[i]); } } @@ -545,7 +546,7 @@ static int ttm_alloc_new_pages(struct pglist *pages, int ttm_alloc_flags, } } - TAILQ_INSERT_HEAD(pages, p, pageq); + TAILQ_INSERT_HEAD(pages, p, plinks.q); } if (cpages) { @@ -600,16 +601,16 @@ static void ttm_page_pool_fill_locked(struct ttm_page_pool *pool, mtx_lock(&pool->lock); if (!r) { - TAILQ_CONCAT(&pool->list, &new_pages, pageq); + TAILQ_CONCAT(&pool->list, &new_pages, plinks.q); ++pool->nrefills; pool->npages += alloc_size; } else { printf("[TTM] Failed to fill pool (%p)\n", pool); /* If we have any pages left put them to the pool. */ - TAILQ_FOREACH(p, &pool->list, pageq) { + TAILQ_FOREACH(p, &pool->list, plinks.q) { ++cpages; } - TAILQ_CONCAT(&pool->list, &new_pages, pageq); + TAILQ_CONCAT(&pool->list, &new_pages, plinks.q); pool->npages += cpages; } @@ -636,15 +637,15 @@ static unsigned ttm_page_pool_get_pages(struct ttm_page_pool *pool, if (count >= pool->npages) { /* take all pages from the pool */ - TAILQ_CONCAT(pages, &pool->list, pageq); + TAILQ_CONCAT(pages, &pool->list, plinks.q); count -= pool->npages; pool->npages = 0; goto out; } for (i = 0; i < count; i++) { p = TAILQ_FIRST(&pool->list); - TAILQ_REMOVE(&pool->list, p, pageq); - TAILQ_INSERT_TAIL(pages, p, pageq); + TAILQ_REMOVE(&pool->list, p, plinks.q); + TAILQ_INSERT_TAIL(pages, p, plinks.q); } pool->npages -= count; count = 0; @@ -674,7 +675,7 @@ static void ttm_put_pages(vm_page_t *pages, unsigned npages, int flags, mtx_lock(&pool->lock); for (i = 0; i < npages; i++) { if (pages[i]) { - TAILQ_INSERT_TAIL(&pool->list, pages[i], pageq); + TAILQ_INSERT_TAIL(&pool->list, pages[i], plinks.q); pages[i] = NULL; pool->npages++; } @@ -735,13 +736,13 @@ static int ttm_get_pages(vm_page_t *pages, unsigned npages, int flags, TAILQ_INIT(&plist); npages = ttm_page_pool_get_pages(pool, &plist, flags, cstate, npages); count = 0; - TAILQ_FOREACH(p, &plist, pageq) { + TAILQ_FOREACH(p, &plist, plinks.q) { pages[count++] = p; } /* clear the pages coming from the pool if requested */ if (flags & TTM_PAGE_FLAG_ZERO_ALLOC) { - TAILQ_FOREACH(p, &plist, pageq) { + TAILQ_FOREACH(p, &plist, plinks.q) { pmap_zero_page(p); } } @@ -754,7 +755,7 @@ static int ttm_get_pages(vm_page_t *pages, unsigned npages, int flags, TAILQ_INIT(&plist); r = ttm_alloc_new_pages(&plist, gfp_flags, flags, cstate, npages); - TAILQ_FOREACH(p, &plist, pageq) { + TAILQ_FOREACH(p, &plist, plinks.q) { pages[count++] = p; } if (r) { diff --git a/sys/dev/drm2/ttm/ttm_tt.c b/sys/dev/drm2/ttm/ttm_tt.c index c0fbb939c4e..2dd6fb4d1a3 100644 --- a/sys/dev/drm2/ttm/ttm_tt.c +++ b/sys/dev/drm2/ttm/ttm_tt.c @@ -288,10 +288,8 @@ int ttm_tt_swapin(struct ttm_tt *ttm) VM_OBJECT_WLOCK(obj); vm_object_pip_add(obj, 1); for (i = 0; i < ttm->num_pages; ++i) { - from_page = vm_page_grab(obj, i, VM_ALLOC_NOBUSY | - VM_ALLOC_RETRY); + from_page = vm_page_grab(obj, i, VM_ALLOC_NORMAL); if (from_page->valid != VM_PAGE_BITS_ALL) { - vm_page_busy(from_page); if (vm_pager_has_page(obj, i, NULL, NULL)) { rv = vm_pager_get_pages(obj, &from_page, 1, 0); if (rv != VM_PAGER_OK) { @@ -303,8 +301,8 @@ int ttm_tt_swapin(struct ttm_tt *ttm) } } else vm_page_zero_invalid(from_page, TRUE); - vm_page_wakeup(from_page); } + vm_page_xunbusy(from_page); to_page = ttm->pages[i]; if (unlikely(to_page == NULL)) { ret = -ENOMEM; @@ -353,11 +351,11 @@ int ttm_tt_swapout(struct ttm_tt *ttm, vm_object_t persistent_swap_storage) from_page = ttm->pages[i]; if (unlikely(from_page == NULL)) continue; - to_page = vm_page_grab(obj, i, VM_ALLOC_RETRY); + to_page = vm_page_grab(obj, i, VM_ALLOC_NORMAL); pmap_copy_page(from_page, to_page); - vm_page_dirty(to_page); to_page->valid = VM_PAGE_BITS_ALL; - vm_page_wakeup(to_page); + vm_page_dirty(to_page); + vm_page_xunbusy(to_page); } vm_object_pip_wakeup(obj); VM_OBJECT_WUNLOCK(obj); diff --git a/sys/dev/e1000/if_em.c b/sys/dev/e1000/if_em.c index 9499953db40..16e1d6f565b 100644 --- a/sys/dev/e1000/if_em.c +++ b/sys/dev/e1000/if_em.c @@ -2277,7 +2277,7 @@ em_local_timer(void *arg) /* Mask to use in the irq trigger */ if (adapter->msix_mem) - trigger = rxr->ims; /* RX for 82574 */ + trigger = rxr->ims; else trigger = E1000_ICS_RXDMT0; @@ -2442,16 +2442,8 @@ em_identify_hardware(struct adapter *adapter) device_t dev = adapter->dev; /* Make sure our PCI config space has the necessary stuff set */ + pci_enable_busmaster(dev); adapter->hw.bus.pci_cmd_word = pci_read_config(dev, PCIR_COMMAND, 2); - if (!((adapter->hw.bus.pci_cmd_word & PCIM_CMD_BUSMASTEREN) && - (adapter->hw.bus.pci_cmd_word & PCIM_CMD_MEMEN))) { - device_printf(dev, "Memory Access and/or Bus Master bits " - "were not set!\n"); - adapter->hw.bus.pci_cmd_word |= - (PCIM_CMD_BUSMASTEREN | PCIM_CMD_MEMEN); - pci_write_config(dev, PCIR_COMMAND, - adapter->hw.bus.pci_cmd_word, 2); - } /* Save off the information about this board */ adapter->hw.vendor_id = pci_get_vendor(dev); @@ -2775,23 +2767,30 @@ em_setup_msix(struct adapter *adapter) if (val >= 3) val = 3; else { - bus_release_resource(dev, SYS_RES_MEMORY, - PCIR_BAR(EM_MSIX_BAR), adapter->msix_mem); - adapter->msix_mem = NULL; device_printf(adapter->dev, - "MSIX: incorrect vectors, using MSI\n"); + "MSIX: insufficient vectors, using MSI\n"); goto msi; } - if (pci_alloc_msix(dev, &val) == 0) { + if ((pci_alloc_msix(dev, &val) == 0) && (val == 3)) { device_printf(adapter->dev, "Using MSIX interrupts " "with %d vectors\n", val); return (val); } - /* Fall through to MSI */ + + /* + ** If MSIX alloc failed or provided us with + ** less than needed, free and fall through to MSI + */ + pci_release_msi(dev); } msi: + if (adapter->msix_mem != NULL) { + bus_release_resource(dev, SYS_RES_MEMORY, + PCIR_BAR(EM_MSIX_BAR), adapter->msix_mem); + adapter->msix_mem = NULL; + } val = 1; if (pci_alloc_msi(dev, &val) == 0) { device_printf(adapter->dev,"Using an MSI interrupt\n"); diff --git a/sys/dev/e1000/if_igb.c b/sys/dev/e1000/if_igb.c index d52483248fd..1c8e9bb53c4 100644 --- a/sys/dev/e1000/if_igb.c +++ b/sys/dev/e1000/if_igb.c @@ -972,7 +972,13 @@ igb_mq_start(struct ifnet *ifp, struct mbuf *m) que = &adapter->queues[i]; err = drbr_enqueue(ifp, txr->br, m); - taskqueue_enqueue(que->tq, &txr->txq_task); + if (err) + return (err); + if (IGB_TX_TRYLOCK(txr)) { + err = igb_mq_start_locked(ifp, txr); + IGB_TX_UNLOCK(txr); + } else + taskqueue_enqueue(que->tq, &txr->txq_task); return (err); } @@ -2410,16 +2416,8 @@ igb_identify_hardware(struct adapter *adapter) device_t dev = adapter->dev; /* Make sure our PCI config space has the necessary stuff set */ + pci_enable_busmaster(dev); adapter->hw.bus.pci_cmd_word = pci_read_config(dev, PCIR_COMMAND, 2); - if (!((adapter->hw.bus.pci_cmd_word & PCIM_CMD_BUSMASTEREN) && - (adapter->hw.bus.pci_cmd_word & PCIM_CMD_MEMEN))) { - INIT_DEBUGOUT("Memory Access and/or Bus Master " - "bits were not set!\n"); - adapter->hw.bus.pci_cmd_word |= - (PCIM_CMD_BUSMASTEREN | PCIM_CMD_MEMEN); - pci_write_config(dev, PCIR_COMMAND, - adapter->hw.bus.pci_cmd_word, 2); - } /* Save off the information about this board */ adapter->hw.vendor_id = pci_get_vendor(dev); @@ -2899,13 +2897,18 @@ igb_setup_msix(struct adapter *adapter) msgs, want); goto msi; } - if (pci_alloc_msix(dev, &msgs) == 0) { + if ((pci_alloc_msix(dev, &msgs) == 0) && (msgs == want)) { device_printf(adapter->dev, "Using MSIX interrupts with %d vectors\n", msgs); adapter->num_queues = queues; return (msgs); } - /* Fallback to MSI configuration */ + /* + ** If MSIX alloc failed or provided us with + ** less than needed, free and fall through to MSI + */ + pci_release_msi(dev); + msi: if (adapter->msix_mem != NULL) { bus_release_resource(dev, SYS_RES_MEMORY, @@ -2914,10 +2917,10 @@ msi: } msgs = 1; if (pci_alloc_msi(dev, &msgs) == 0) { - device_printf(adapter->dev," Using MSI interrupt\n"); + device_printf(adapter->dev," Using an MSI interrupt\n"); return (msgs); } - /* Default to a legacy interrupt */ + device_printf(adapter->dev," Using a Legacy interrupt\n"); return (0); } @@ -4978,7 +4981,7 @@ igb_rx_checksum(u32 staterr, struct mbuf *mp, u32 ptype) } if (status & (E1000_RXD_STAT_TCPCS | E1000_RXD_STAT_UDPCS)) { - u16 type = (CSUM_DATA_VALID | CSUM_PSEUDO_HDR); + u64 type = (CSUM_DATA_VALID | CSUM_PSEUDO_HDR); #if __FreeBSD_version >= 800000 if (sctp) /* reassign */ type = CSUM_SCTP_VALID; diff --git a/sys/dev/e1000/if_lem.c b/sys/dev/e1000/if_lem.c index 09869eb8e47..57e88a48c1f 100644 --- a/sys/dev/e1000/if_lem.c +++ b/sys/dev/e1000/if_lem.c @@ -2119,16 +2119,8 @@ lem_identify_hardware(struct adapter *adapter) device_t dev = adapter->dev; /* Make sure our PCI config space has the necessary stuff set */ + pci_enable_busmaster(dev); adapter->hw.bus.pci_cmd_word = pci_read_config(dev, PCIR_COMMAND, 2); - if (!((adapter->hw.bus.pci_cmd_word & PCIM_CMD_BUSMASTEREN) && - (adapter->hw.bus.pci_cmd_word & PCIM_CMD_MEMEN))) { - device_printf(dev, "Memory Access and/or Bus Master bits " - "were not set!\n"); - adapter->hw.bus.pci_cmd_word |= - (PCIM_CMD_BUSMASTEREN | PCIM_CMD_MEMEN); - pci_write_config(dev, PCIR_COMMAND, - adapter->hw.bus.pci_cmd_word, 2); - } /* Save off the information about this board */ adapter->hw.vendor_id = pci_get_vendor(dev); diff --git a/sys/dev/fatm/if_fatm.c b/sys/dev/fatm/if_fatm.c index 9020d22d588..abd8e79750d 100644 --- a/sys/dev/fatm/if_fatm.c +++ b/sys/dev/fatm/if_fatm.c @@ -2829,21 +2829,13 @@ fatm_attach(device_t dev) ifp->if_linkmiblen = sizeof(IFP2IFATM(sc->ifp)->mib); /* - * Enable memory and bustmaster + * Enable busmaster */ - cfg = pci_read_config(dev, PCIR_COMMAND, 2); - cfg |= PCIM_CMD_MEMEN | PCIM_CMD_BUSMASTEREN; - pci_write_config(dev, PCIR_COMMAND, cfg, 2); + pci_enable_busmaster(dev); /* * Map memory */ - cfg = pci_read_config(dev, PCIR_COMMAND, 2); - if (!(cfg & PCIM_CMD_MEMEN)) { - if_printf(ifp, "failed to enable memory mapping\n"); - error = ENXIO; - goto fail; - } sc->memid = 0x10; sc->memres = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->memid, RF_ACTIVE); diff --git a/sys/dev/fb/vesa.c b/sys/dev/fb/vesa.c index 82fbeac09b2..48067b6401f 100644 --- a/sys/dev/fb/vesa.c +++ b/sys/dev/fb/vesa.c @@ -1,6 +1,6 @@ /*- * Copyright (c) 1998 Kazutaka YOKOTA and Michael Smith - * Copyright (c) 2009-2012 Jung-uk Kim + * Copyright (c) 2009-2013 Jung-uk Kim * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -83,7 +83,7 @@ static struct mtx vesa_lock; static int vesa_state; static void *vesa_state_buf; static uint32_t vesa_state_buf_offs; -static ssize_t vesa_state_buf_size; +static size_t vesa_state_buf_size; static u_char *vesa_palette; static uint32_t vesa_palette_offs; @@ -207,7 +207,7 @@ static int vesa_bios_load_palette2(int start, int colors, u_char *r, u_char *g, #define STATE_SIZE 0 #define STATE_SAVE 1 #define STATE_LOAD 2 -static ssize_t vesa_bios_state_buf_size(int); +static size_t vesa_bios_state_buf_size(int); static int vesa_bios_save_restore(int code, void *p); #ifdef MODE_TABLE_BROKEN static int vesa_bios_get_line_length(void); @@ -505,7 +505,7 @@ vesa_bios_load_palette2(int start, int colors, u_char *r, u_char *g, u_char *b, return (regs.R_AX != 0x004f); } -static ssize_t +static size_t vesa_bios_state_buf_size(int state) { x86regs_t regs; @@ -1463,7 +1463,7 @@ vesa_set_border(video_adapter_t *adp, int color) static int vesa_save_state(video_adapter_t *adp, void *p, size_t size) { - vm_offset_t buf; + void *buf; size_t bsize; if (adp != vesa_adp || (size == 0 && vesa_state_buf_size == 0)) @@ -1475,19 +1475,19 @@ vesa_save_state(video_adapter_t *adp, void *p, size_t size) if (vesa_state_buf_size > 0 && size < bsize) return (EINVAL); - if (VESA_MODE(adp->va_mode) && adp->va_buffer != 0) { - buf = adp->va_buffer; - bsize = adp->va_buffer_size; - } else { - buf = adp->va_window; - bsize = adp->va_window_size; - } - if (buf != 0) { - vesa_vmem_buf = malloc(bsize, M_DEVBUF, M_NOWAIT); - if (vesa_vmem_buf != NULL) - bcopy((void *)buf, vesa_vmem_buf, bsize); - } else + if (vesa_vmem_buf != NULL) { + free(vesa_vmem_buf, M_DEVBUF); vesa_vmem_buf = NULL; + } + if (VESA_MODE(adp->va_mode)) { + buf = (void *)adp->va_buffer; + if (buf != NULL) { + bsize = adp->va_buffer_size; + vesa_vmem_buf = malloc(bsize, M_DEVBUF, M_NOWAIT); + if (vesa_vmem_buf != NULL) + bcopy(buf, vesa_vmem_buf, bsize); + } + } if (vesa_state_buf_size == 0) return ((*prevvidsw->save_state)(adp, p, size)); ((adp_state_t *)p)->sig = V_STATE_SIG; @@ -1497,7 +1497,7 @@ vesa_save_state(video_adapter_t *adp, void *p, size_t size) static int vesa_load_state(video_adapter_t *adp, void *p) { - vm_offset_t buf; + void *buf; size_t bsize; int error, mode; @@ -1506,24 +1506,20 @@ vesa_load_state(video_adapter_t *adp, void *p) /* Try BIOS POST to restore a sane state. */ (void)vesa_bios_post(); + bsize = adp->va_buffer_size; mode = adp->va_mode; error = vesa_set_mode(adp, adp->va_initial_mode); if (mode != adp->va_initial_mode) error = vesa_set_mode(adp, mode); if (vesa_vmem_buf != NULL) { - if (error == 0) { - if (VESA_MODE(mode) && adp->va_buffer != 0) { - buf = adp->va_buffer; - bsize = adp->va_buffer_size; - } else { - buf = adp->va_window; - bsize = adp->va_window_size; - } - if (buf != 0) - bcopy(vesa_vmem_buf, (void *)buf, bsize); + if (error == 0 && VESA_MODE(mode)) { + buf = (void *)adp->va_buffer; + if (buf != NULL) + bcopy(vesa_vmem_buf, buf, bsize); } free(vesa_vmem_buf, M_DEVBUF); + vesa_vmem_buf = NULL; } if (((adp_state_t *)p)->sig != V_STATE_SIG) return ((*prevvidsw->load_state)(adp, p)); diff --git a/sys/dev/fdc/fdc.c b/sys/dev/fdc/fdc.c index 985e2296df7..9fe1e7ef8bd 100644 --- a/sys/dev/fdc/fdc.c +++ b/sys/dev/fdc/fdc.c @@ -528,7 +528,8 @@ fdc_reset(struct fdc_data *fdc) if (fdc_cmd(fdc, 4, I8207X_CONFIG, 0, - 0x40 | /* Enable Implied Seek */ + /* 0x40 | */ /* Enable Implied Seek - + * breaks 2step! */ 0x10 | /* Polling disabled */ (fifo_threshold - 1), /* Fifo threshold */ 0x00, /* Precomp track */ @@ -760,10 +761,13 @@ fdc_worker(struct fdc_data *fdc) int i, nsect; int st0, st3, cyl, mfm, steptrac, cylinder, descyl, sec; int head; + int override_error; static int need_recal; struct fdc_readid *idp; struct fd_formb *finfo; + override_error = 0; + /* Have we exhausted our retries ? */ bp = fdc->bp; fd = fdc->fd; @@ -922,14 +926,8 @@ fdc_worker(struct fdc_data *fdc) /* * SEEK to where we want to be - * - * Enhanced controllers do implied seeks for read&write as long as - * we do not need multiple steps per track. */ - if (cylinder != fd->track && ( - fdc->fdct != FDC_ENHANCED || - descyl != cylinder || - (bp->bio_cmd & (BIO_RDID|BIO_FMT)))) { + if (cylinder != fd->track) { retry_line = __LINE__; if (fdc_cmd(fdc, 3, NE7CMD_SEEK, fd->fdsu, descyl, 0)) return (1); @@ -1095,7 +1093,10 @@ fdc_worker(struct fdc_data *fdc) fdc->status[3], fdc->status[4], fdc->status[5]); } retry_line = __LINE__; - return (1); + if (fd->options & FDOPT_NOERROR) + override_error = 1; + else + return (1); } /* All OK */ switch(bp->bio_cmd) { @@ -1116,10 +1117,16 @@ fdc_worker(struct fdc_data *fdc) bp->bio_resid -= fd->fd_iosize; bp->bio_completed += fd->fd_iosize; fd->fd_ioptr += fd->fd_iosize; - /* Since we managed to get something done, reset the retry */ - fdc->retry = 0; - if (bp->bio_resid > 0) - return (0); + if (override_error) { + if ((debugflags & 4)) + printf("FDOPT_NOERROR: returning bad data\n"); + } else { + /* Since we managed to get something done, + * reset the retry */ + fdc->retry = 0; + if (bp->bio_resid > 0) + return (0); + } break; case BIO_FMT: break; @@ -1411,6 +1418,7 @@ fd_access(struct g_provider *pp, int r, int w, int e) ae = e + pp->ace; if (ar == 0 && aw == 0 && ae == 0) { + fd->options &= ~(FDOPT_NORETRY | FDOPT_NOERRLOG | FDOPT_NOERROR); device_unbusy(fd->dev); return (0); } diff --git a/sys/dev/filemon/filemon.c b/sys/dev/filemon/filemon.c index ce84e3df425..e3fda1829ec 100644 --- a/sys/dev/filemon/filemon.c +++ b/sys/dev/filemon/filemon.c @@ -138,12 +138,6 @@ filemon_dtr(void *data) } } -#if __FreeBSD_version < 900041 -#define FGET_WRITE(a1, a2, a3) fget_write((a1), (a2), (a3)) -#else -#define FGET_WRITE(a1, a2, a3) fget_write((a1), (a2), CAP_WRITE | CAP_SEEK, (a3)) -#endif - static int filemon_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag __unused, struct thread *td) @@ -151,13 +145,21 @@ filemon_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag __unused, int error = 0; struct filemon *filemon; struct proc *p; +#if __FreeBSD_version >= 900041 + cap_rights_t rights; +#endif devfs_get_cdevpriv((void **) &filemon); switch (cmd) { /* Set the output file descriptor. */ case FILEMON_SET_FD: - if ((error = FGET_WRITE(td, *(int *)data, &filemon->fp)) == 0) + error = fget_write(td, *(int *)data, +#if __FreeBSD_version >= 900041 + cap_rights_init(&rights, CAP_PWRITE), +#endif + &filemon->fp); + if (error == 0) /* Write the file header. */ filemon_comment(filemon); break; diff --git a/sys/dev/firewire/fwdev.c b/sys/dev/firewire/fwdev.c index 1475d00e3c3..d26810d3054 100644 --- a/sys/dev/firewire/fwdev.c +++ b/sys/dev/firewire/fwdev.c @@ -992,11 +992,9 @@ found: sc = devclass_get_softc(firewire_devclass, unit); if (sc == NULL) return; - *dev = make_dev(&firewire_cdevsw, MAKEMINOR(devflag[i], unit, sub), - UID_ROOT, GID_OPERATOR, 0660, - "%s%d.%d", devnames[i], unit, sub); - dev_ref(*dev); - (*dev)->si_flags |= SI_CHEAPCLONE; + *dev = make_dev_credf(MAKEDEV_REF, &firewire_cdevsw, + MAKEMINOR(devflag[i], unit, sub), cred, UID_ROOT, GID_OPERATOR, + 0660, "%s%d.%d", devnames[i], unit, sub); dev_depends(sc->dev, *dev); return; } diff --git a/sys/dev/firewire/fwohci_pci.c b/sys/dev/firewire/fwohci_pci.c index 979f587c1ff..e1e20b5da95 100644 --- a/sys/dev/firewire/fwohci_pci.c +++ b/sys/dev/firewire/fwohci_pci.c @@ -242,7 +242,7 @@ fwohci_pci_init(device_t self) uint16_t cmd; cmd = pci_read_config(self, PCIR_COMMAND, 2); - cmd |= PCIM_CMD_MEMEN | PCIM_CMD_BUSMASTEREN | PCIM_CMD_MWRICEN; + cmd |= PCIM_CMD_BUSMASTEREN | PCIM_CMD_MWRICEN; #if 1 /* for broken hardware */ cmd &= ~PCIM_CMD_MWRICEN; #endif diff --git a/sys/dev/flash/mx25l.c b/sys/dev/flash/mx25l.c index 8956c307313..24c9323a9d3 100644 --- a/sys/dev/flash/mx25l.c +++ b/sys/dev/flash/mx25l.c @@ -104,9 +104,9 @@ struct mx25l_flash_ident flash_devices[] = { { "mx25ll32", 0xc2, 0x2016, 64 * 1024, 64, FL_NONE }, { "mx25ll64", 0xc2, 0x2017, 64 * 1024, 128, FL_NONE }, { "mx25ll128", 0xc2, 0x2018, 64 * 1024, 256, FL_ERASE_4K | FL_ERASE_32K }, + { "s25fl032", 0x01, 0x0215, 64 * 1024, 64, FL_NONE }, + { "s25fl064", 0x01, 0x0216, 64 * 1024, 128, FL_NONE }, { "s25fl128", 0x01, 0x2018, 64 * 1024, 256, FL_NONE }, - { "s25s1032", 0x01, 0x0215, 64 * 1024, 64, FL_NONE }, - { "s25sl064a", 0x01, 0x0216, 64 * 1024, 128, FL_NONE }, { "SST25VF032B", 0xbf, 0x254a, 64 * 1024, 64, FL_ERASE_4K | FL_ERASE_32K }, /* Winbond -- w25x "blocks" are 64K, "sectors" are 4KiB */ diff --git a/sys/dev/fxp/if_fxp.c b/sys/dev/fxp/if_fxp.c index 11b105ef3d2..1896d90af28 100644 --- a/sys/dev/fxp/if_fxp.c +++ b/sys/dev/fxp/if_fxp.c @@ -452,7 +452,6 @@ fxp_attach(device_t dev) * Enable bus mastering. */ pci_enable_busmaster(dev); - val = pci_read_config(dev, PCIR_COMMAND, 2); /* * Figure out which we should try first - memory mapping or i/o mapping? @@ -610,6 +609,7 @@ fxp_attach(device_t dev) * is a valid cacheline size (8 or 16 dwords), then tell * the board to turn on MWI. */ + val = pci_read_config(dev, PCIR_COMMAND, 2); if (val & PCIM_CMD_MWRICEN && pci_read_config(dev, PCIR_CACHELNSZ, 1) != 0) sc->flags |= FXP_FLAG_MWI_ENABLE; diff --git a/sys/dev/glxsb/glxsb.c b/sys/dev/glxsb/glxsb.c index aa1721742d6..52041534da3 100644 --- a/sys/dev/glxsb/glxsb.c +++ b/sys/dev/glxsb/glxsb.c @@ -476,7 +476,7 @@ glxsb_rnd(void *v) if (status & SB_RNS_TRNG_VALID) { value = bus_read_4(sc->sc_sr, SB_RANDOM_NUM); /* feed with one uint32 */ - random_harvest(&value, 4, 32, 0, RANDOM_PURE); + random_harvest(&value, 4, 32/2, 0, RANDOM_PURE); } callout_reset(&sc->sc_rngco, sc->sc_rnghz, glxsb_rnd, sc); diff --git a/sys/dev/gpio/gpiobus.c b/sys/dev/gpio/gpiobus.c index c4a62f77978..6abb10c1e09 100644 --- a/sys/dev/gpio/gpiobus.c +++ b/sys/dev/gpio/gpiobus.c @@ -131,7 +131,7 @@ gpiobus_parse_pins(struct gpiobus_softc *sc, device_t child, int mask) } if (npins == 0) { - device_printf(child, "empty pin mask"); + device_printf(child, "empty pin mask\n"); return (EINVAL); } @@ -151,6 +151,7 @@ gpiobus_parse_pins(struct gpiobus_softc *sc, device_t child, int mask) if (i >= sc->sc_npins) { device_printf(child, "invalid pin %d, max: %d\n", i, sc->sc_npins - 1); + free(devi->pins, M_DEVBUF); return (EINVAL); } @@ -161,6 +162,7 @@ gpiobus_parse_pins(struct gpiobus_softc *sc, device_t child, int mask) if (sc->sc_pins_mapped[i]) { device_printf(child, "warning: pin %d is already mapped\n", i); + free(devi->pins, M_DEVBUF); return (EINVAL); } sc->sc_pins_mapped[i] = 1; @@ -218,9 +220,12 @@ gpiobus_attach(device_t dev) static int gpiobus_detach(device_t dev) { - struct gpiobus_softc *sc = GPIOBUS_SOFTC(dev); - int err; + struct gpiobus_softc *sc; + struct gpiobus_ivar *devi; + device_t *devlist; + int i, err, ndevs; + sc = GPIOBUS_SOFTC(dev); KASSERT(mtx_initialized(&sc->sc_mtx), ("gpiobus mutex not initialized")); GPIOBUS_LOCK_DESTROY(sc); @@ -228,8 +233,17 @@ gpiobus_detach(device_t dev) if ((err = bus_generic_detach(dev)) != 0) return (err); - /* detach and delete all children */ - device_delete_children(dev); + if ((err = device_get_children(dev, &devlist, &ndevs)) != 0) + return (err); + for (i = 0; i < ndevs; i++) { + device_delete_child(dev, devlist[i]); + devi = GPIOBUS_IVAR(devlist[i]); + if (devi->pins) { + free(devi->pins, M_DEVBUF); + devi->pins = NULL; + } + } + free(devlist, M_TEMP); if (sc->sc_pins_mapped) { free(sc->sc_pins_mapped, M_DEVBUF); diff --git a/sys/dev/gxemul/cons/gxemul_cons.c b/sys/dev/gxemul/cons/gxemul_cons.c index b83aa9400bf..cb3b0001ee6 100644 --- a/sys/dev/gxemul/cons/gxemul_cons.c +++ b/sys/dev/gxemul/cons/gxemul_cons.c @@ -99,18 +99,16 @@ static void gxemul_cons_timeout(void *); * XXXRW: Should be using FreeBSD's bus routines here, but they are not * available until later in the boot. */ -typedef uint64_t paddr_t; -typedef uint64_t vaddr_t; -static inline vaddr_t -mips_phys_to_uncached(paddr_t phys) +static inline vm_offset_t +mips_phys_to_uncached(vm_paddr_t phys) { return (MIPS_PHYS_TO_DIRECT_UNCACHED(phys)); } static inline uint8_t -mips_ioread_uint8(vaddr_t vaddr) +mips_ioread_uint8(vm_offset_t vaddr) { uint8_t v; @@ -119,7 +117,7 @@ mips_ioread_uint8(vaddr_t vaddr) } static inline void -mips_iowrite_uint8(vaddr_t vaddr, uint8_t v) +mips_iowrite_uint8(vm_offset_t vaddr, uint8_t v) { __asm__ __volatile__ ("sb %0, 0(%1)" : : "r" (v), "r" (vaddr)); diff --git a/sys/dev/gxemul/disk/gxemul_disk.c b/sys/dev/gxemul/disk/gxemul_disk.c index 8cf52e42824..3b7e649c655 100644 --- a/sys/dev/gxemul/disk/gxemul_disk.c +++ b/sys/dev/gxemul/disk/gxemul_disk.c @@ -214,7 +214,14 @@ gxemul_disk_read(unsigned diskid, void *buf, off_t off) if (off < 0 || off % GXEMUL_DISK_DEV_BLOCKSIZE != 0) return (EINVAL); +#ifdef _LP64 GXEMUL_DISK_DEV_WRITE(GXEMUL_DISK_DEV_OFFSET, (uint64_t)off); +#else + GXEMUL_DISK_DEV_WRITE(GXEMUL_DISK_DEV_OFFSET_LO, + (uint32_t)(off & 0xffffffff)); + GXEMUL_DISK_DEV_WRITE(GXEMUL_DISK_DEV_OFFSET_HI, + (uint32_t)((off >> 32) & 0xffffffff)); +#endif GXEMUL_DISK_DEV_WRITE(GXEMUL_DISK_DEV_DISKID, diskid); GXEMUL_DISK_DEV_WRITE(GXEMUL_DISK_DEV_START, GXEMUL_DISK_DEV_START_READ); switch (GXEMUL_DISK_DEV_READ(GXEMUL_DISK_DEV_STATUS)) { @@ -280,7 +287,15 @@ gxemul_disk_write(unsigned diskid, const void *buf, off_t off) if (off < 0 || off % GXEMUL_DISK_DEV_BLOCKSIZE != 0) return (EINVAL); +#ifdef _LP64 GXEMUL_DISK_DEV_WRITE(GXEMUL_DISK_DEV_OFFSET, (uint64_t)off); +#else + GXEMUL_DISK_DEV_WRITE(GXEMUL_DISK_DEV_OFFSET_LO, + (uint32_t)(off & 0xffffffff)); + GXEMUL_DISK_DEV_WRITE(GXEMUL_DISK_DEV_OFFSET_HI, + (uint32_t)((off >> 32) & 0xffffffff)); +#endif + GXEMUL_DISK_DEV_WRITE(GXEMUL_DISK_DEV_DISKID, diskid); dst = GXEMUL_DISK_DEV_FUNCTION(GXEMUL_DISK_DEV_BLOCK); diff --git a/sys/dev/gxemul/disk/gxemul_diskreg.h b/sys/dev/gxemul/disk/gxemul_diskreg.h index c3460e55299..f83794486b1 100644 --- a/sys/dev/gxemul/disk/gxemul_diskreg.h +++ b/sys/dev/gxemul/disk/gxemul_diskreg.h @@ -36,16 +36,28 @@ #define GXEMUL_DISK_DEV_ID_START (0x0000) #define GXEMUL_DISK_DEV_ID_END (0x0100) -#define GXEMUL_DISK_DEV_OFFSET (0x0000) +#ifdef _LP64 +#define GXEMUL_DISK_DEV_OFFSET (0x0000) +#else +#define GXEMUL_DISK_DEV_OFFSET_LO (0x0000) +#define GXEMUL_DISK_DEV_OFFSET_HI (0x0008) +#endif #define GXEMUL_DISK_DEV_DISKID (0x0010) #define GXEMUL_DISK_DEV_START (0x0020) #define GXEMUL_DISK_DEV_STATUS (0x0030) #define GXEMUL_DISK_DEV_BLOCK (0x4000) +#ifdef _LP64 #define GXEMUL_DISK_DEV_FUNCTION(f) \ (volatile uint64_t *)MIPS_PHYS_TO_DIRECT_UNCACHED(GXEMUL_DISK_DEV_BASE + (f)) #define GXEMUL_DISK_DEV_READ(f) \ (volatile uint64_t)*GXEMUL_DISK_DEV_FUNCTION(f) +#else +#define GXEMUL_DISK_DEV_FUNCTION(f) \ + (volatile uint32_t *)MIPS_PHYS_TO_DIRECT_UNCACHED(GXEMUL_DISK_DEV_BASE + (f)) +#define GXEMUL_DISK_DEV_READ(f) \ + (volatile uint32_t)*GXEMUL_DISK_DEV_FUNCTION(f) +#endif #define GXEMUL_DISK_DEV_WRITE(f, v) \ *GXEMUL_DISK_DEV_FUNCTION(f) = (v) diff --git a/sys/dev/gxemul/ether/gxreg.h b/sys/dev/gxemul/ether/gxreg.h index e67f43d0ce8..a528250b2aa 100644 --- a/sys/dev/gxemul/ether/gxreg.h +++ b/sys/dev/gxemul/ether/gxreg.h @@ -40,10 +40,17 @@ #define GXEMUL_ETHER_DEV_COMMAND (0x4020) #define GXEMUL_ETHER_DEV_MAC (0x4040) +#ifdef _LP64 #define GXEMUL_ETHER_DEV_FUNCTION(f) \ (volatile uint64_t *)MIPS_PHYS_TO_DIRECT_UNCACHED(GXEMUL_ETHER_DEV_BASE + (f)) #define GXEMUL_ETHER_DEV_READ(f) \ (volatile uint64_t)*GXEMUL_ETHER_DEV_FUNCTION(f) +#else +#define GXEMUL_ETHER_DEV_FUNCTION(f) \ + (volatile uint32_t *)MIPS_PHYS_TO_DIRECT_UNCACHED(GXEMUL_ETHER_DEV_BASE + (f)) +#define GXEMUL_ETHER_DEV_READ(f) \ + (volatile uint32_t)*GXEMUL_ETHER_DEV_FUNCTION(f) +#endif #define GXEMUL_ETHER_DEV_WRITE(f, v) \ *GXEMUL_ETHER_DEV_FUNCTION(f) = (v) diff --git a/sys/dev/hatm/if_hatm.c b/sys/dev/hatm/if_hatm.c index 0aa090e5f87..b8ad577316a 100644 --- a/sys/dev/hatm/if_hatm.c +++ b/sys/dev/hatm/if_hatm.c @@ -1686,7 +1686,7 @@ hatm_attach(device_t dev) * 4.2 BIOS Configuration */ v = pci_read_config(dev, PCIR_COMMAND, 2); - v |= PCIM_CMD_MEMEN | PCIM_CMD_BUSMASTEREN | PCIM_CMD_MWRICEN; + v |= PCIM_CMD_BUSMASTEREN | PCIM_CMD_MWRICEN; pci_write_config(dev, PCIR_COMMAND, v, 2); /* @@ -1702,12 +1702,6 @@ hatm_attach(device_t dev) /* * Map memory */ - v = pci_read_config(dev, PCIR_COMMAND, 2); - if (!(v & PCIM_CMD_MEMEN)) { - device_printf(dev, "failed to enable memory\n"); - error = ENXIO; - goto failed; - } sc->memid = PCIR_BAR(0); sc->memres = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->memid, RF_ACTIVE); diff --git a/sys/dev/hatm/if_hatm_intr.c b/sys/dev/hatm/if_hatm_intr.c index b6a53680eb1..697dd2bdcc5 100644 --- a/sys/dev/hatm/if_hatm_intr.c +++ b/sys/dev/hatm/if_hatm_intr.c @@ -260,8 +260,8 @@ hatm_mbuf_page_alloc(struct hatm_softc *sc, u_int group) /* * Free an mbuf and put it onto the free list. */ -static void -hatm_mbuf0_free(void *buf, void *args) +static int +hatm_mbuf0_free(struct mbuf *m, void *buf, void *args) { struct hatm_softc *sc = args; struct mbuf0_chunk *c = buf; @@ -270,9 +270,10 @@ hatm_mbuf0_free(void *buf, void *args) ("freeing unused mbuf %x", c->hdr.flags)); c->hdr.flags &= ~MBUF_USED; hatm_ext_free(&sc->mbuf_list[0], (struct mbufx_free *)c); + return (EXT_FREE_OK); } -static void -hatm_mbuf1_free(void *buf, void *args) +static int +hatm_mbuf1_free(struct mbuf *m, void *buf, void *args) { struct hatm_softc *sc = args; struct mbuf1_chunk *c = buf; @@ -281,6 +282,7 @@ hatm_mbuf1_free(void *buf, void *args) ("freeing unused mbuf %x", c->hdr.flags)); c->hdr.flags &= ~MBUF_USED; hatm_ext_free(&sc->mbuf_list[1], (struct mbufx_free *)c); + return (EXT_FREE_OK); } static void @@ -461,7 +463,7 @@ hatm_rx_buffer(struct hatm_softc *sc, u_int group, u_int handle) hatm_mbuf0_free, c0, sc, M_PKTHDR, EXT_EXTREF); m->m_data += MBUF0_OFFSET; } else - hatm_mbuf0_free(c0, sc); + (void)hatm_mbuf0_free(NULL, c0, sc); } else { struct mbuf1_chunk *c1; @@ -485,7 +487,7 @@ hatm_rx_buffer(struct hatm_softc *sc, u_int group, u_int handle) hatm_mbuf1_free, c1, sc, M_PKTHDR, EXT_EXTREF); m->m_data += MBUF1_OFFSET; } else - hatm_mbuf1_free(c1, sc); + (void)hatm_mbuf1_free(NULL, c1, sc); } return (m); diff --git a/sys/dev/hifn/hifn7751.c b/sys/dev/hifn/hifn7751.c index c5c5af1b3fa..ae6c5ac6353 100644 --- a/sys/dev/hifn/hifn7751.c +++ b/sys/dev/hifn/hifn7751.c @@ -258,7 +258,7 @@ hifn_partname(struct hifn_softc *sc) static void default_harvest(struct rndtest_state *rsp, void *buf, u_int count) { - random_harvest(buf, count, count*NBBY, 0, RANDOM_PURE); + random_harvest(buf, count, count*NBBY/2, 0, RANDOM_PURE); } static u_int diff --git a/sys/dev/hpt27xx/hpt27xx_osm_bsd.c b/sys/dev/hpt27xx/hpt27xx_osm_bsd.c index 636906d1437..3c9f0ace838 100644 --- a/sys/dev/hpt27xx/hpt27xx_osm_bsd.c +++ b/sys/dev/hpt27xx/hpt27xx_osm_bsd.c @@ -52,7 +52,7 @@ static int hpt_probe(device_t dev) memset(hba, 0, sizeof(HBA)); hba->ext_type = EXT_TYPE_HBA; hba->ldm_adapter.him = him; - return 0; + return (BUS_PROBE_DEFAULT); } } } diff --git a/sys/dev/hwpmc/hwpmc_core.c b/sys/dev/hwpmc/hwpmc_core.c index ddc3c0ce840..d4fae5ba786 100644 --- a/sys/dev/hwpmc/hwpmc_core.c +++ b/sys/dev/hwpmc/hwpmc_core.c @@ -1514,19 +1514,22 @@ static struct iap_event_descr iap_events[] = { IAPDESCR(CEH_00H, 0xCE, 0x00, IAP_F_FM | IAP_F_ALLCPUSCORE2), IAPDESCR(CFH_00H, 0xCF, 0x00, IAP_F_FM | IAP_F_CA | IAP_F_CC2), + /* Sandy Bridge / Sandy Bridge Xeon - 11, 12, 21, 41, 42, 81, 82 */ IAPDESCR(D0H_00H, 0xD0, 0x00, IAP_F_FM | IAP_F_CC), - IAPDESCR(D0H_01H, 0xD0, 0x01, IAP_F_FM | IAP_F_I7 | IAP_F_WM | - IAP_F_SB | IAP_F_IB | IAP_F_SBX | IAP_F_IBX | IAP_F_HW), - IAPDESCR(D0H_02H, 0xD0, 0x02, IAP_F_FM | IAP_F_SB | IAP_F_IB | - IAP_F_SBX | IAP_F_IBX | IAP_F_HW), - IAPDESCR(D0H_10H, 0xD0, 0x10, IAP_F_FM | IAP_F_SB | IAP_F_IB | - IAP_F_SBX | IAP_F_IBX | IAP_F_HW), - IAPDESCR(D0H_20H, 0xD0, 0x20, IAP_F_FM | IAP_F_SB | IAP_F_IB | - IAP_F_SBX | IAP_F_IBX | IAP_F_HW), - IAPDESCR(D0H_40H, 0xD0, 0x40, IAP_F_FM | IAP_F_SB | IAP_F_IB | - IAP_F_SBX | IAP_F_IBX | IAP_F_HW), - IAPDESCR(D0H_80H, 0xD0, 0X80, IAP_F_FM | IAP_F_SB | IAP_F_IB | - IAP_F_SBX | IAP_F_IBX | IAP_F_HW), + IAPDESCR(D0H_01H, 0xD0, 0x01, IAP_F_FM | IAP_F_I7 | IAP_F_WM | IAP_F_IB | + IAP_F_IBX | IAP_F_HW), + IAPDESCR(D0H_02H, 0xD0, 0x02, IAP_F_FM | IAP_F_IB | IAP_F_IBX | IAP_F_HW), + IAPDESCR(D0H_10H, 0xD0, 0x10, IAP_F_FM | IAP_F_IB | IAP_F_IBX | IAP_F_HW), + IAPDESCR(D0H_11H, 0xD0, 0x11, IAP_F_FM | IAP_F_SB | IAP_F_SBX), + IAPDESCR(D0H_12H, 0xD0, 0x12, IAP_F_FM | IAP_F_SB | IAP_F_SBX), + IAPDESCR(D0H_20H, 0xD0, 0x20, IAP_F_FM | IAP_F_IB | IAP_F_IBX | IAP_F_HW), + IAPDESCR(D0H_21H, 0xD0, 0x21, IAP_F_FM | IAP_F_SB | IAP_F_SBX), + IAPDESCR(D0H_40H, 0xD0, 0x40, IAP_F_FM | IAP_F_IB | IAP_F_IBX | IAP_F_HW), + IAPDESCR(D0H_41H, 0xD0, 0x41, IAP_F_FM | IAP_F_SB | IAP_F_SBX), + IAPDESCR(D0H_42H, 0xD0, 0x42, IAP_F_FM | IAP_F_SB | IAP_F_SBX), + IAPDESCR(D0H_80H, 0xD0, 0x80, IAP_F_FM | IAP_F_IB | IAP_F_IBX | IAP_F_HW), + IAPDESCR(D0H_81H, 0xD0, 0x81, IAP_F_FM | IAP_F_SB | IAP_F_SBX), + IAPDESCR(D0H_82H, 0xD0, 0x82, IAP_F_FM | IAP_F_SB | IAP_F_SBX), IAPDESCR(D1H_01H, 0xD1, 0x01, IAP_F_FM | IAP_F_WM | IAP_F_SB | IAP_F_IB | IAP_F_SBX | IAP_F_IBX | IAP_F_HW), @@ -1541,15 +1544,21 @@ static struct iap_event_descr iap_events[] = { IAP_F_SBX | IAP_F_IBX | IAP_F_HW), IAPDESCR(D2H_01H, 0xD2, 0x01, IAP_F_FM | IAP_F_CA | IAP_F_CC2 | - IAP_F_I7 | IAP_F_WM | IAP_F_SB | IAP_F_IB | IAP_F_IBX | IAP_F_HW), + IAP_F_I7 | IAP_F_WM | IAP_F_SB | IAP_F_SBX | IAP_F_IB | + IAP_F_IBX | IAP_F_HW), IAPDESCR(D2H_02H, 0xD2, 0x02, IAP_F_FM | IAP_F_CA | IAP_F_CC2 | - IAP_F_I7 | IAP_F_WM | IAP_F_SB | IAP_F_IB | IAP_F_IBX | IAP_F_HW), + IAP_F_I7 | IAP_F_WM | IAP_F_SB | IAP_F_SBX | IAP_F_IB | + IAP_F_IBX | IAP_F_HW), IAPDESCR(D2H_04H, 0xD2, 0x04, IAP_F_FM | IAP_F_CA | IAP_F_CC2 | - IAP_F_I7 | IAP_F_WM | IAP_F_SB | IAP_F_IB | IAP_F_IBX | IAP_F_HW), + IAP_F_I7 | IAP_F_WM | IAP_F_SB | IAP_F_SBX | IAP_F_IB | + IAP_F_IBX | IAP_F_HW), IAPDESCR(D2H_08H, 0xD2, 0x08, IAP_F_FM | IAP_F_CA | IAP_F_CC2 | - IAP_F_I7 | IAP_F_WM | IAP_F_SB | IAP_F_IB | IAP_F_IBX | IAP_F_HW), + IAP_F_I7 | IAP_F_WM | IAP_F_SB | IAP_F_SBX | IAP_F_IB | + IAP_F_IBX | IAP_F_HW), IAPDESCR(D2H_0FH, 0xD2, 0x0F, IAP_F_FM | IAP_F_CA | IAP_F_CC2 | - IAP_F_I7 | IAP_F_WM), + IAP_F_I7 | IAP_F_WM | IAP_F_SB | IAP_F_SBX | IAP_F_IB | + IAP_F_IBX | IAP_F_HW), + IAPDESCR(D2H_10H, 0xD2, 0x10, IAP_F_FM | IAP_F_CC2E), IAPDESCR(D3H_01H, 0xD3, 0x01, IAP_F_FM | IAP_F_IB | IAP_F_SBX | diff --git a/sys/dev/hwpmc/hwpmc_logging.c b/sys/dev/hwpmc/hwpmc_logging.c index 880bcaac0a0..a60e096eed2 100644 --- a/sys/dev/hwpmc/hwpmc_logging.c +++ b/sys/dev/hwpmc/hwpmc_logging.c @@ -570,6 +570,7 @@ pmclog_configure_log(struct pmc_mdep *md, struct pmc_owner *po, int logfd) { int error; struct proc *p; + cap_rights_t rights; /* * As long as it is possible to get a LOR between pmc_sx lock and @@ -593,7 +594,8 @@ pmclog_configure_log(struct pmc_mdep *md, struct pmc_owner *po, int logfd) po->po_file)); /* get a reference to the file state */ - error = fget_write(curthread, logfd, CAP_WRITE, &po->po_file); + error = fget_write(curthread, logfd, + cap_rights_init(&rights, CAP_WRITE), &po->po_file); if (error) goto error; diff --git a/sys/dev/hwpmc/hwpmc_mod.c b/sys/dev/hwpmc/hwpmc_mod.c index 150c69a90fd..8e5eac879d2 100644 --- a/sys/dev/hwpmc/hwpmc_mod.c +++ b/sys/dev/hwpmc/hwpmc_mod.c @@ -132,7 +132,8 @@ static int *pmc_pmcdisp; /* PMC row dispositions */ /* various event handlers */ -static eventhandler_tag pmc_exit_tag, pmc_fork_tag; +static eventhandler_tag pmc_exit_tag, pmc_fork_tag, pmc_kld_load_tag, + pmc_kld_unload_tag; /* Module statistics */ struct pmc_op_getdriverstats pmc_stats; @@ -1475,50 +1476,6 @@ pmc_process_csw_out(struct thread *td) critical_exit(); } -/* - * Log a KLD operation. - */ - -static void -pmc_process_kld_load(struct pmckern_map_in *pkm) -{ - struct pmc_owner *po; - - sx_assert(&pmc_sx, SX_LOCKED); - - /* - * Notify owners of system sampling PMCs about KLD operations. - */ - - LIST_FOREACH(po, &pmc_ss_owners, po_ssnext) - if (po->po_flags & PMC_PO_OWNS_LOGFILE) - pmclog_process_map_in(po, (pid_t) -1, pkm->pm_address, - (char *) pkm->pm_file); - - /* - * TODO: Notify owners of (all) process-sampling PMCs too. - */ - - return; -} - -static void -pmc_process_kld_unload(struct pmckern_map_out *pkm) -{ - struct pmc_owner *po; - - sx_assert(&pmc_sx, SX_LOCKED); - - LIST_FOREACH(po, &pmc_ss_owners, po_ssnext) - if (po->po_flags & PMC_PO_OWNS_LOGFILE) - pmclog_process_map_out(po, (pid_t) -1, - pkm->pm_address, pkm->pm_address + pkm->pm_size); - - /* - * TODO: Notify owners of process-sampling PMCs. - */ -} - /* * A mapping change for a process. */ @@ -1833,8 +1790,8 @@ const char *pmc_hooknames[] = { "CSW-IN", "CSW-OUT", "SAMPLE", - "KLDLOAD", - "KLDUNLOAD", + "UNUSED1", + "UNUSED2", "MMAP", "MUNMAP", "CALLCHAIN-NMI", @@ -2002,17 +1959,6 @@ pmc_hook_handler(struct thread *td, int function, void *arg) pmc_process_samples(PCPU_GET(cpuid), PMC_SR); break; - - case PMC_FN_KLD_LOAD: - sx_assert(&pmc_sx, SX_LOCKED); - pmc_process_kld_load((struct pmckern_map_in *) arg); - break; - - case PMC_FN_KLD_UNLOAD: - sx_assert(&pmc_sx, SX_LOCKED); - pmc_process_kld_unload((struct pmckern_map_out *) arg); - break; - case PMC_FN_MMAP: sx_assert(&pmc_sx, SX_LOCKED); pmc_process_mmap(td, (struct pmckern_map_in *) arg); @@ -2080,11 +2026,7 @@ pmc_allocate_owner_descriptor(struct proc *p) /* allocate space for N pointers and one descriptor struct */ po = malloc(sizeof(struct pmc_owner), M_PMC, M_WAITOK|M_ZERO); - po->po_sscount = po->po_error = po->po_flags = po->po_logprocmaps = 0; - po->po_file = NULL; po->po_owner = p; - po->po_kthread = NULL; - LIST_INIT(&po->po_pmcs); LIST_INSERT_HEAD(poh, po, po_next); /* insert into hash table */ TAILQ_INIT(&po->po_logbuffers); @@ -2210,8 +2152,6 @@ pmc_allocate_pmc_descriptor(void) struct pmc *pmc; pmc = malloc(sizeof(struct pmc), M_PMC, M_WAITOK|M_ZERO); - pmc->pm_owner = NULL; - LIST_INIT(&pmc->pm_targets); PMCDBG(PMC,ALL,1, "allocate-pmc -> pmc=%p", pmc); @@ -4644,6 +4584,47 @@ pmc_process_fork(void *arg __unused, struct proc *p1, struct proc *newproc, sx_xunlock(&pmc_sx); } +static void +pmc_kld_load(void *arg __unused, linker_file_t lf) +{ + struct pmc_owner *po; + + sx_slock(&pmc_sx); + + /* + * Notify owners of system sampling PMCs about KLD operations. + */ + LIST_FOREACH(po, &pmc_ss_owners, po_ssnext) + if (po->po_flags & PMC_PO_OWNS_LOGFILE) + pmclog_process_map_in(po, (pid_t) -1, + (uintfptr_t) lf->address, lf->filename); + + /* + * TODO: Notify owners of (all) process-sampling PMCs too. + */ + + sx_sunlock(&pmc_sx); +} + +static void +pmc_kld_unload(void *arg __unused, const char *filename __unused, + caddr_t address, size_t size) +{ + struct pmc_owner *po; + + sx_slock(&pmc_sx); + + LIST_FOREACH(po, &pmc_ss_owners, po_ssnext) + if (po->po_flags & PMC_PO_OWNS_LOGFILE) + pmclog_process_map_out(po, (pid_t) -1, + (uintfptr_t) address, (uintfptr_t) address + size); + + /* + * TODO: Notify owners of process-sampling PMCs. + */ + + sx_sunlock(&pmc_sx); +} /* * initialization @@ -4913,6 +4894,12 @@ pmc_initialize(void) pmc_fork_tag = EVENTHANDLER_REGISTER(process_fork, pmc_process_fork, NULL, EVENTHANDLER_PRI_ANY); + /* register kld event handlers */ + pmc_kld_load_tag = EVENTHANDLER_REGISTER(kld_load, pmc_kld_load, + NULL, EVENTHANDLER_PRI_ANY); + pmc_kld_unload_tag = EVENTHANDLER_REGISTER(kld_unload, pmc_kld_unload, + NULL, EVENTHANDLER_PRI_ANY); + /* initialize logging */ pmclog_initialize(); @@ -4970,6 +4957,8 @@ pmc_cleanup(void) /* deregister event handlers */ EVENTHANDLER_DEREGISTER(process_fork, pmc_fork_tag); EVENTHANDLER_DEREGISTER(process_exit, pmc_exit_tag); + EVENTHANDLER_DEREGISTER(kld_load, pmc_kld_load_tag); + EVENTHANDLER_DEREGISTER(kld_unload, pmc_kld_unload_tag); /* send SIGBUS to all owner threads, free up allocations */ if (pmc_ownerhash) diff --git a/sys/dev/hwpmc/hwpmc_mpc7xxx.c b/sys/dev/hwpmc/hwpmc_mpc7xxx.c new file mode 100644 index 00000000000..93b5c7420fc --- /dev/null +++ b/sys/dev/hwpmc/hwpmc_mpc7xxx.c @@ -0,0 +1,748 @@ +/*- + * Copyright (c) 2011 Justin Hibbits + * Copyright (c) 2005, Joseph Koshy + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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 +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include + +#include +#include +#include + +#include "hwpmc_powerpc.h" + +#define POWERPC_PMC_CAPS (PMC_CAP_INTERRUPT | PMC_CAP_USER | \ + PMC_CAP_SYSTEM | PMC_CAP_EDGE | \ + PMC_CAP_THRESHOLD | PMC_CAP_READ | \ + PMC_CAP_WRITE | PMC_CAP_INVERT | \ + PMC_CAP_QUALIFIER) + +#define PPC_SET_PMC1SEL(r, x) ((r & ~(SPR_MMCR0_PMC1SEL(0x3f))) | SPR_MMCR0_PMC1SEL(x)) +#define PPC_SET_PMC2SEL(r, x) ((r & ~(SPR_MMCR0_PMC2SEL(0x3f))) | SPR_MMCR0_PMC2SEL(x)) +#define PPC_SET_PMC3SEL(r, x) ((r & ~(SPR_MMCR1_PMC3SEL(0x1f))) | SPR_MMCR1_PMC3SEL(x)) +#define PPC_SET_PMC4SEL(r, x) ((r & ~(SPR_MMCR1_PMC4SEL(0x1f))) | SPR_MMCR1_PMC4SEL(x)) +#define PPC_SET_PMC5SEL(r, x) ((r & ~(SPR_MMCR1_PMC5SEL(0x1f))) | SPR_MMCR1_PMC5SEL(x)) +#define PPC_SET_PMC6SEL(r, x) ((r & ~(SPR_MMCR1_PMC6SEL(0x3f))) | SPR_MMCR1_PMC6SEL(x)) + +/* Change this when we support more than just the 7450. */ +#define MPC7XXX_MAX_PMCS 6 + +#define MPC7XXX_PMC_HAS_OVERFLOWED(x) (mpc7xxx_pmcn_read(x) & (0x1 << 31)) + +/* + * Things to improve on this: + * - It stops (clears to 0) the PMC and resets it at every context switch + * currently. + */ + +/* + * This should work for every 32-bit PowerPC implementation I know of (G3 and G4 + * specifically). + */ + +struct powerpc_event_code_map { + enum pmc_event pe_ev; /* enum value */ + uint8_t pe_counter_mask; /* Which counter this can be counted in. */ + uint8_t pe_code; /* numeric code */ +}; + +#define PPC_PMC_MASK1 0 +#define PPC_PMC_MASK2 1 +#define PPC_PMC_MASK3 2 +#define PPC_PMC_MASK4 3 +#define PPC_PMC_MASK5 4 +#define PPC_PMC_MASK6 5 +#define PPC_PMC_MASK_ALL 0x3f +#define PMC_POWERPC_EVENT(id, mask, number) \ + { .pe_ev = PMC_EV_PPC7450_##id, .pe_counter_mask = mask, .pe_code = number } + +static struct powerpc_event_code_map powerpc_event_codes[] = { + PMC_POWERPC_EVENT(CYCLE,PPC_PMC_MASK_ALL, 1), + PMC_POWERPC_EVENT(INSTR_COMPLETED, 0x0f, 2), + PMC_POWERPC_EVENT(TLB_BIT_TRANSITIONS, 0x0f, 3), + PMC_POWERPC_EVENT(INSTR_DISPATCHED, 0x0f, 4), + PMC_POWERPC_EVENT(PMON_EXCEPT, 0x0f, 5), + PMC_POWERPC_EVENT(PMON_SIG, 0x0f, 7), + PMC_POWERPC_EVENT(VPU_INSTR_COMPLETED, 0x03, 8), + PMC_POWERPC_EVENT(VFPU_INSTR_COMPLETED, 0x03, 9), + PMC_POWERPC_EVENT(VIU1_INSTR_COMPLETED, 0x03, 10), + PMC_POWERPC_EVENT(VIU2_INSTR_COMPLETED, 0x03, 11), + PMC_POWERPC_EVENT(MTVSCR_INSTR_COMPLETED, 0x03, 12), + PMC_POWERPC_EVENT(MTVRSAVE_INSTR_COMPLETED, 0x03, 13), + PMC_POWERPC_EVENT(VPU_INSTR_WAIT_CYCLES, 0x03, 14), + PMC_POWERPC_EVENT(VFPU_INSTR_WAIT_CYCLES, 0x03, 15), + PMC_POWERPC_EVENT(VIU1_INSTR_WAIT_CYCLES, 0x03, 16), + PMC_POWERPC_EVENT(VIU2_INSTR_WAIT_CYCLES, 0x03, 17), + PMC_POWERPC_EVENT(MFVSCR_SYNC_CYCLES, 0x03, 18), + PMC_POWERPC_EVENT(VSCR_SAT_SET, 0x03, 19), + PMC_POWERPC_EVENT(STORE_INSTR_COMPLETED, 0x03, 20), + PMC_POWERPC_EVENT(L1_INSTR_CACHE_MISSES, 0x03, 21), + PMC_POWERPC_EVENT(L1_DATA_SNOOPS, 0x03, 22), + PMC_POWERPC_EVENT(UNRESOLVED_BRANCHES, 0x01, 23), + PMC_POWERPC_EVENT(SPEC_BUFFER_CYCLES, 0x01, 24), + PMC_POWERPC_EVENT(BRANCH_UNIT_STALL_CYCLES, 0x01, 25), + PMC_POWERPC_EVENT(TRUE_BRANCH_TARGET_HITS, 0x01, 26), + PMC_POWERPC_EVENT(BRANCH_LINK_STAC_PREDICTED, 0x01, 27), + PMC_POWERPC_EVENT(GPR_ISSUE_QUEUE_DISPATCHES, 0x01, 28), + PMC_POWERPC_EVENT(CYCLES_THREE_INSTR_DISPATCHED, 0x01, 29), + PMC_POWERPC_EVENT(THRESHOLD_INSTR_QUEUE_ENTRIES_CYCLES, 0x01, 30), + PMC_POWERPC_EVENT(THRESHOLD_VEC_INSTR_QUEUE_ENTRIES_CYCLES, 0x01, 31), + PMC_POWERPC_EVENT(CYCLES_NO_COMPLETED_INSTRS, 0x01, 32), + PMC_POWERPC_EVENT(IU2_INSTR_COMPLETED, 0x01, 33), + PMC_POWERPC_EVENT(BRANCHES_COMPLETED, 0x01, 34), + PMC_POWERPC_EVENT(EIEIO_INSTR_COMPLETED, 0x01, 35), + PMC_POWERPC_EVENT(MTSPR_INSTR_COMPLETED, 0x01, 36), + PMC_POWERPC_EVENT(SC_INSTR_COMPLETED, 0x01, 37), + PMC_POWERPC_EVENT(LS_LM_COMPLETED, 0x01, 38), + PMC_POWERPC_EVENT(ITLB_HW_TABLE_SEARCH_CYCLES, 0x01, 39), + PMC_POWERPC_EVENT(DTLB_HW_SEARCH_CYCLES_OVER_THRESHOLD, 0x01, 40), + PMC_POWERPC_EVENT(L1_INSTR_CACHE_ACCESSES, 0x01, 41), + PMC_POWERPC_EVENT(INSTR_BKPT_MATCHES, 0x01, 42), + PMC_POWERPC_EVENT(L1_DATA_CACHE_LOAD_MISS_CYCLES_OVER_THRESHOLD, 0x01, 43), + PMC_POWERPC_EVENT(L1_DATA_SNOOP_HIT_ON_MODIFIED, 0x01, 44), + PMC_POWERPC_EVENT(LOAD_MISS_ALIAS, 0x01, 45), + PMC_POWERPC_EVENT(LOAD_MISS_ALIAS_ON_TOUCH, 0x01, 46), + PMC_POWERPC_EVENT(TOUCH_ALIAS, 0x01, 47), + PMC_POWERPC_EVENT(L1_DATA_SNOOP_HIT_CASTOUT_QUEUE, 0x01, 48), + PMC_POWERPC_EVENT(L1_DATA_SNOOP_HIT_CASTOUT, 0x01, 49), + PMC_POWERPC_EVENT(L1_DATA_SNOOP_HITS, 0x01, 50), + PMC_POWERPC_EVENT(WRITE_THROUGH_STORES, 0x01, 51), + PMC_POWERPC_EVENT(CACHE_INHIBITED_STORES, 0x01, 52), + PMC_POWERPC_EVENT(L1_DATA_LOAD_HIT, 0x01, 53), + PMC_POWERPC_EVENT(L1_DATA_TOUCH_HIT, 0x01, 54), + PMC_POWERPC_EVENT(L1_DATA_STORE_HIT, 0x01, 55), + PMC_POWERPC_EVENT(L1_DATA_TOTAL_HITS, 0x01, 56), + PMC_POWERPC_EVENT(DST_INSTR_DISPATCHED, 0x01, 57), + PMC_POWERPC_EVENT(REFRESHED_DSTS, 0x01, 58), + PMC_POWERPC_EVENT(SUCCESSFUL_DST_TABLE_SEARCHES, 0x01, 59), + PMC_POWERPC_EVENT(DSS_INSTR_COMPLETED, 0x01, 60), + PMC_POWERPC_EVENT(DST_STREAM_0_CACHE_LINE_FETCHES, 0x01, 61), + PMC_POWERPC_EVENT(VTQ_SUSPENDS_DUE_TO_CTX_CHANGE, 0x01, 62), + PMC_POWERPC_EVENT(VTQ_LINE_FETCH_HIT, 0x01, 63), + PMC_POWERPC_EVENT(VEC_LOAD_INSTR_COMPLETED, 0x01, 64), + PMC_POWERPC_EVENT(FP_STORE_INSTR_COMPLETED_IN_LSU, 0x01, 65), + PMC_POWERPC_EVENT(FPU_RENORMALIZATION, 0x01, 66), + PMC_POWERPC_EVENT(FPU_DENORMALIZATION, 0x01, 67), + PMC_POWERPC_EVENT(FP_STORE_CAUSES_STALL_IN_LSU, 0x01, 68), + PMC_POWERPC_EVENT(LD_ST_TRUE_ALIAS_STALL, 0x01, 70), + PMC_POWERPC_EVENT(LSU_INDEXED_ALIAS_STALL, 0x01, 71), + PMC_POWERPC_EVENT(LSU_ALIAS_VS_FSQ_WB0_WB1, 0x01, 72), + PMC_POWERPC_EVENT(LSU_ALIAS_VS_CSQ, 0x01, 73), + PMC_POWERPC_EVENT(LSU_LOAD_HIT_LINE_ALIAS_VS_CSQ0, 0x01, 74), + PMC_POWERPC_EVENT(LSU_LOAD_MISS_LINE_ALIAS_VS_CSQ0, 0x01, 75), + PMC_POWERPC_EVENT(LSU_TOUCH_LINE_ALIAS_VS_FSQ_WB0_WB1, 0x01, 76), + PMC_POWERPC_EVENT(LSU_TOUCH_ALIAS_VS_CSQ, 0x01, 77), + PMC_POWERPC_EVENT(LSU_LMQ_FULL_STALL, 0x01, 78), + PMC_POWERPC_EVENT(FP_LOAD_INSTR_COMPLETED_IN_LSU, 0x01, 79), + PMC_POWERPC_EVENT(FP_LOAD_SINGLE_INSTR_COMPLETED_IN_LSU, 0x01, 80), + PMC_POWERPC_EVENT(FP_LOAD_DOUBLE_COMPLETED_IN_LSU, 0x01, 81), + PMC_POWERPC_EVENT(LSU_RA_LATCH_STALL, 0x01, 82), + PMC_POWERPC_EVENT(LSU_LOAD_VS_STORE_QUEUE_ALIAS_STALL, 0x01, 83), + PMC_POWERPC_EVENT(LSU_LMQ_INDEX_ALIAS, 0x01, 84), + PMC_POWERPC_EVENT(LSU_STORE_QUEUE_INDEX_ALIAS, 0x01, 85), + PMC_POWERPC_EVENT(LSU_CSQ_FORWARDING, 0x01, 86), + PMC_POWERPC_EVENT(LSU_MISALIGNED_LOAD_FINISH, 0x01, 87), + PMC_POWERPC_EVENT(LSU_MISALIGN_STORE_COMPLETED, 0x01, 88), + PMC_POWERPC_EVENT(LSU_MISALIGN_STALL, 0x01, 89), + PMC_POWERPC_EVENT(FP_ONE_QUARTER_FPSCR_RENAMES_BUSY, 0x01, 90), + PMC_POWERPC_EVENT(FP_ONE_HALF_FPSCR_RENAMES_BUSY, 0x01, 91), + PMC_POWERPC_EVENT(FP_THREE_QUARTERS_FPSCR_RENAMES_BUSY, 0x01, 92), + PMC_POWERPC_EVENT(FP_ALL_FPSCR_RENAMES_BUSY, 0x01, 93), + PMC_POWERPC_EVENT(FP_DENORMALIZED_RESULT, 0x01, 94), + PMC_POWERPC_EVENT(L1_DATA_TOTAL_MISSES, 0x02, 23), + PMC_POWERPC_EVENT(DISPATCHES_TO_FPR_ISSUE_QUEUE, 0x02, 24), + PMC_POWERPC_EVENT(LSU_INSTR_COMPLETED, 0x02, 25), + PMC_POWERPC_EVENT(LOAD_INSTR_COMPLETED, 0x02, 26), + PMC_POWERPC_EVENT(SS_SM_INSTR_COMPLETED, 0x02, 27), + PMC_POWERPC_EVENT(TLBIE_INSTR_COMPLETED, 0x02, 28), + PMC_POWERPC_EVENT(LWARX_INSTR_COMPLETED, 0x02, 29), + PMC_POWERPC_EVENT(MFSPR_INSTR_COMPLETED, 0x02, 30), + PMC_POWERPC_EVENT(REFETCH_SERIALIZATION, 0x02, 31), + PMC_POWERPC_EVENT(COMPLETION_QUEUE_ENTRIES_OVER_THRESHOLD, 0x02, 32), + PMC_POWERPC_EVENT(CYCLES_ONE_INSTR_DISPATCHED, 0x02, 33), + PMC_POWERPC_EVENT(CYCLES_TWO_INSTR_COMPLETED, 0x02, 34), + PMC_POWERPC_EVENT(ITLB_NON_SPECULATIVE_MISSES, 0x02, 35), + PMC_POWERPC_EVENT(CYCLES_WAITING_FROM_L1_INSTR_CACHE_MISS, 0x02, 36), + PMC_POWERPC_EVENT(L1_DATA_LOAD_ACCESS_MISS, 0x02, 37), + PMC_POWERPC_EVENT(L1_DATA_TOUCH_MISS, 0x02, 38), + PMC_POWERPC_EVENT(L1_DATA_STORE_MISS, 0x02, 39), + PMC_POWERPC_EVENT(L1_DATA_TOUCH_MISS_CYCLES, 0x02, 40), + PMC_POWERPC_EVENT(L1_DATA_CYCLES_USED, 0x02, 41), + PMC_POWERPC_EVENT(DST_STREAM_1_CACHE_LINE_FETCHES, 0x02, 42), + PMC_POWERPC_EVENT(VTQ_STREAM_CANCELED_PREMATURELY, 0x02, 43), + PMC_POWERPC_EVENT(VTQ_RESUMES_DUE_TO_CTX_CHANGE, 0x02, 44), + PMC_POWERPC_EVENT(VTQ_LINE_FETCH_MISS, 0x02, 45), + PMC_POWERPC_EVENT(VTQ_LINE_FETCH, 0x02, 46), + PMC_POWERPC_EVENT(TLBIE_SNOOPS, 0x02, 47), + PMC_POWERPC_EVENT(L1_INSTR_CACHE_RELOADS, 0x02, 48), + PMC_POWERPC_EVENT(L1_DATA_CACHE_RELOADS, 0x02, 49), + PMC_POWERPC_EVENT(L1_DATA_CACHE_CASTOUTS_TO_L2, 0x02, 50), + PMC_POWERPC_EVENT(STORE_MERGE_GATHER, 0x02, 51), + PMC_POWERPC_EVENT(CACHEABLE_STORE_MERGE_TO_32_BYTES, 0x02, 52), + PMC_POWERPC_EVENT(DATA_BKPT_MATCHES, 0x02, 53), + PMC_POWERPC_EVENT(FALL_THROUGH_BRANCHES_PROCESSED, 0x02, 54), + PMC_POWERPC_EVENT(FIRST_SPECULATIVE_BRANCH_BUFFER_RESOLVED_CORRECTLY, 0x02, 55), + PMC_POWERPC_EVENT(SECOND_SPECULATION_BUFFER_ACTIVE, 0x02, 56), + PMC_POWERPC_EVENT(BPU_STALL_ON_LR_DEPENDENCY, 0x02, 57), + PMC_POWERPC_EVENT(BTIC_MISS, 0x02, 58), + PMC_POWERPC_EVENT(BRANCH_LINK_STACK_CORRECTLY_RESOLVED, 0x02, 59), + PMC_POWERPC_EVENT(FPR_ISSUE_STALLED, 0x02, 60), + PMC_POWERPC_EVENT(SWITCHES_BETWEEN_PRIV_USER, 0x02, 61), + PMC_POWERPC_EVENT(LSU_COMPLETES_FP_STORE_SINGLE, 0x02, 62), + PMC_POWERPC_EVENT(CYCLES_TWO_INSTR_COMPLETED, 0x04, 8), + PMC_POWERPC_EVENT(CYCLES_ONE_INSTR_DISPATCHED, 0x04, 9), + PMC_POWERPC_EVENT(VR_ISSUE_QUEUE_DISPATCHES, 0x04, 10), + PMC_POWERPC_EVENT(VR_STALLS, 0x04, 11), + PMC_POWERPC_EVENT(GPR_RENAME_BUFFER_ENTRIES_OVER_THRESHOLD, 0x04, 12), + PMC_POWERPC_EVENT(FPR_ISSUE_QUEUE_ENTRIES, 0x04, 13), + PMC_POWERPC_EVENT(FPU_INSTR_COMPLETED, 0x04, 14), + PMC_POWERPC_EVENT(STWCX_INSTR_COMPLETED, 0x04, 15), + PMC_POWERPC_EVENT(LS_LM_INSTR_PIECES, 0x04, 16), + PMC_POWERPC_EVENT(ITLB_HW_SEARCH_CYCLES_OVER_THRESHOLD, 0x04, 17), + PMC_POWERPC_EVENT(DTLB_MISSES, 0x04, 18), + PMC_POWERPC_EVENT(CANCELLED_L1_INSTR_CACHE_MISSES, 0x04, 19), + PMC_POWERPC_EVENT(L1_DATA_CACHE_OP_HIT, 0x04, 20), + PMC_POWERPC_EVENT(L1_DATA_LOAD_MISS_CYCLES, 0x04, 21), + PMC_POWERPC_EVENT(L1_DATA_PUSHES, 0x04, 22), + PMC_POWERPC_EVENT(L1_DATA_TOTAL_MISS, 0x04, 23), + PMC_POWERPC_EVENT(VT2_FETCHES, 0x04, 24), + PMC_POWERPC_EVENT(TAKEN_BRANCHES_PROCESSED, 0x04, 25), + PMC_POWERPC_EVENT(BRANCH_FLUSHES, 0x04, 26), + PMC_POWERPC_EVENT(SECOND_SPECULATIVE_BRANCH_BUFFER_RESOLVED_CORRECTLY, 0x04, 27), + PMC_POWERPC_EVENT(THIRD_SPECULATION_BUFFER_ACTIVE, 0x04, 28), + PMC_POWERPC_EVENT(BRANCH_UNIT_STALL_ON_CTR_DEPENDENCY, 0x04, 29), + PMC_POWERPC_EVENT(FAST_BTIC_HIT, 0x04, 30), + PMC_POWERPC_EVENT(BRANCH_LINK_STACK_MISPREDICTED, 0x04, 31), + PMC_POWERPC_EVENT(CYCLES_THREE_INSTR_COMPLETED, 0x08, 14), + PMC_POWERPC_EVENT(CYCLES_NO_INSTR_DISPATCHED, 0x08, 15), + PMC_POWERPC_EVENT(GPR_ISSUE_QUEUE_ENTRIES_OVER_THRESHOLD, 0x08, 16), + PMC_POWERPC_EVENT(GPR_ISSUE_QUEUE_STALLED, 0x08, 17), + PMC_POWERPC_EVENT(IU1_INSTR_COMPLETED, 0x08, 18), + PMC_POWERPC_EVENT(DSSALL_INSTR_COMPLETED, 0x08, 19), + PMC_POWERPC_EVENT(TLBSYNC_INSTR_COMPLETED, 0x08, 20), + PMC_POWERPC_EVENT(SYNC_INSTR_COMPLETED, 0x08, 21), + PMC_POWERPC_EVENT(SS_SM_INSTR_PIECES, 0x08, 22), + PMC_POWERPC_EVENT(DTLB_HW_SEARCH_CYCLES, 0x08, 23), + PMC_POWERPC_EVENT(SNOOP_RETRIES, 0x08, 24), + PMC_POWERPC_EVENT(SUCCESSFUL_STWCX, 0x08, 25), + PMC_POWERPC_EVENT(DST_STREAM_3_CACHE_LINE_FETCHES, 0x08, 26), + PMC_POWERPC_EVENT(THIRD_SPECULATIVE_BRANCH_BUFFER_RESOLVED_CORRECTLY, 0x08, 27), + PMC_POWERPC_EVENT(MISPREDICTED_BRANCHES, 0x08, 28), + PMC_POWERPC_EVENT(FOLDED_BRANCHES, 0x08, 29), + PMC_POWERPC_EVENT(FP_STORE_DOUBLE_COMPLETES_IN_LSU, 0x08, 30), + PMC_POWERPC_EVENT(L2_CACHE_HITS, 0x30, 2), + PMC_POWERPC_EVENT(L3_CACHE_HITS, 0x30, 3), + PMC_POWERPC_EVENT(L2_INSTR_CACHE_MISSES, 0x30, 4), + PMC_POWERPC_EVENT(L3_INSTR_CACHE_MISSES, 0x30, 5), + PMC_POWERPC_EVENT(L2_DATA_CACHE_MISSES, 0x30, 6), + PMC_POWERPC_EVENT(L3_DATA_CACHE_MISSES, 0x30, 7), + PMC_POWERPC_EVENT(L2_LOAD_HITS, 0x10, 8), + PMC_POWERPC_EVENT(L2_STORE_HITS, 0x10, 9), + PMC_POWERPC_EVENT(L3_LOAD_HITS, 0x10, 10), + PMC_POWERPC_EVENT(L3_STORE_HITS, 0x10, 11), + PMC_POWERPC_EVENT(L2_TOUCH_HITS, 0x30, 13), + PMC_POWERPC_EVENT(L3_TOUCH_HITS, 0x30, 14), + PMC_POWERPC_EVENT(SNOOP_RETRIES, 0x30, 15), + PMC_POWERPC_EVENT(SNOOP_MODIFIED, 0x10, 16), + PMC_POWERPC_EVENT(SNOOP_VALID, 0x10, 17), + PMC_POWERPC_EVENT(INTERVENTION, 0x30, 18), + PMC_POWERPC_EVENT(L2_CACHE_MISSES, 0x10, 19), + PMC_POWERPC_EVENT(L3_CACHE_MISSES, 0x10, 20), + PMC_POWERPC_EVENT(L2_CACHE_CASTOUTS, 0x20, 8), + PMC_POWERPC_EVENT(L3_CACHE_CASTOUTS, 0x20, 9), + PMC_POWERPC_EVENT(L2SQ_FULL_CYCLES, 0x20, 10), + PMC_POWERPC_EVENT(L3SQ_FULL_CYCLES, 0x20, 11), + PMC_POWERPC_EVENT(RAQ_FULL_CYCLES, 0x20, 16), + PMC_POWERPC_EVENT(WAQ_FULL_CYCLES, 0x20, 17), + PMC_POWERPC_EVENT(L1_EXTERNAL_INTERVENTIONS, 0x20, 19), + PMC_POWERPC_EVENT(L2_EXTERNAL_INTERVENTIONS, 0x20, 20), + PMC_POWERPC_EVENT(L3_EXTERNAL_INTERVENTIONS, 0x20, 21), + PMC_POWERPC_EVENT(EXTERNAL_INTERVENTIONS, 0x20, 22), + PMC_POWERPC_EVENT(EXTERNAL_PUSHES, 0x20, 23), + PMC_POWERPC_EVENT(EXTERNAL_SNOOP_RETRY, 0x20, 24), + PMC_POWERPC_EVENT(DTQ_FULL_CYCLES, 0x20, 25), + PMC_POWERPC_EVENT(BUS_RETRY, 0x20, 26), + PMC_POWERPC_EVENT(L2_VALID_REQUEST, 0x20, 27), + PMC_POWERPC_EVENT(BORDQ_FULL, 0x20, 28), + PMC_POWERPC_EVENT(BUS_TAS_FOR_READS, 0x20, 42), + PMC_POWERPC_EVENT(BUS_TAS_FOR_WRITES, 0x20, 43), + PMC_POWERPC_EVENT(BUS_READS_NOT_RETRIED, 0x20, 44), + PMC_POWERPC_EVENT(BUS_WRITES_NOT_RETRIED, 0x20, 45), + PMC_POWERPC_EVENT(BUS_READS_WRITES_NOT_RETRIED, 0x20, 46), + PMC_POWERPC_EVENT(BUS_RETRY_DUE_TO_L1_RETRY, 0x20, 47), + PMC_POWERPC_EVENT(BUS_RETRY_DUE_TO_PREVIOUS_ADJACENT, 0x20, 48), + PMC_POWERPC_EVENT(BUS_RETRY_DUE_TO_COLLISION, 0x20, 49), + PMC_POWERPC_EVENT(BUS_RETRY_DUE_TO_INTERVENTION_ORDERING, 0x20, 50), + PMC_POWERPC_EVENT(SNOOP_REQUESTS, 0x20, 51), + PMC_POWERPC_EVENT(PREFETCH_ENGINE_REQUEST, 0x20, 52), + PMC_POWERPC_EVENT(PREFETCH_ENGINE_COLLISION_VS_LOAD, 0x20, 53), + PMC_POWERPC_EVENT(PREFETCH_ENGINE_COLLISION_VS_STORE, 0x20, 54), + PMC_POWERPC_EVENT(PREFETCH_ENGINE_COLLISION_VS_INSTR_FETCH, 0x20, 55), + PMC_POWERPC_EVENT(PREFETCH_ENGINE_COLLISION_VS_LOAD_STORE_INSTR_FETCH, 0x20, 56), + PMC_POWERPC_EVENT(PREFETCH_ENGINE_FULL, 0x20, 57) +}; + +const size_t powerpc_event_codes_size = + sizeof(powerpc_event_codes) / sizeof(powerpc_event_codes[0]); + +static pmc_value_t +mpc7xxx_pmcn_read(unsigned int pmc) +{ + switch (pmc) { + case 0: + return mfspr(SPR_PMC1); + break; + case 1: + return mfspr(SPR_PMC2); + break; + case 2: + return mfspr(SPR_PMC3); + break; + case 3: + return mfspr(SPR_PMC4); + break; + case 4: + return mfspr(SPR_PMC5); + break; + case 5: + return mfspr(SPR_PMC6); + default: + panic("Invalid PMC number: %d\n", pmc); + } +} + +static void +mpc7xxx_pmcn_write(unsigned int pmc, uint32_t val) +{ + switch (pmc) { + case 0: + mtspr(SPR_PMC1, val); + break; + case 1: + mtspr(SPR_PMC2, val); + break; + case 2: + mtspr(SPR_PMC3, val); + break; + case 3: + mtspr(SPR_PMC4, val); + break; + case 4: + mtspr(SPR_PMC5, val); + break; + case 5: + mtspr(SPR_PMC6, val); + break; + default: + panic("Invalid PMC number: %d\n", pmc); + } +} + +static int +mpc7xxx_read_pmc(int cpu, int ri, pmc_value_t *v) +{ + struct pmc *pm; + pmc_value_t tmp; + + KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), + ("[powerpc,%d] illegal CPU value %d", __LINE__, cpu)); + KASSERT(ri >= 0 && ri < MPC7XXX_MAX_PMCS, + ("[powerpc,%d] illegal row index %d", __LINE__, ri)); + + pm = powerpc_pcpu[cpu]->pc_ppcpmcs[ri].phw_pmc; + KASSERT(pm, + ("[core,%d] cpu %d ri %d pmc not configured", __LINE__, cpu, + ri)); + + tmp = mpc7xxx_pmcn_read(ri); + PMCDBG(MDP,REA,2,"ppc-read id=%d -> %jd", ri, tmp); + if (PMC_IS_SAMPLING_MODE(PMC_TO_MODE(pm))) + *v = POWERPC_PERFCTR_VALUE_TO_RELOAD_COUNT(tmp); + else + *v = tmp; + + return 0; +} + +static int +mpc7xxx_write_pmc(int cpu, int ri, pmc_value_t v) +{ + struct pmc *pm; + + KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), + ("[powerpc,%d] illegal CPU value %d", __LINE__, cpu)); + KASSERT(ri >= 0 && ri < MPC7XXX_MAX_PMCS, + ("[powerpc,%d] illegal row-index %d", __LINE__, ri)); + + pm = powerpc_pcpu[cpu]->pc_ppcpmcs[ri].phw_pmc; + + if (PMC_IS_SAMPLING_MODE(PMC_TO_MODE(pm))) + v = POWERPC_RELOAD_COUNT_TO_PERFCTR_VALUE(v); + + PMCDBG(MDP,WRI,1,"powerpc-write cpu=%d ri=%d v=%jx", cpu, ri, v); + + mpc7xxx_pmcn_write(ri, v); + + return 0; +} + +static int +mpc7xxx_config_pmc(int cpu, int ri, struct pmc *pm) +{ + struct pmc_hw *phw; + + PMCDBG(MDP,CFG,1, "cpu=%d ri=%d pm=%p", cpu, ri, pm); + + KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), + ("[powerpc,%d] illegal CPU value %d", __LINE__, cpu)); + KASSERT(ri >= 0 && ri < MPC7XXX_MAX_PMCS, + ("[powerpc,%d] illegal row-index %d", __LINE__, ri)); + + phw = &powerpc_pcpu[cpu]->pc_ppcpmcs[ri]; + + KASSERT(pm == NULL || phw->phw_pmc == NULL, + ("[powerpc,%d] pm=%p phw->pm=%p hwpmc not unconfigured", + __LINE__, pm, phw->phw_pmc)); + + phw->phw_pmc = pm; + + return 0; +} + +static int +mpc7xxx_start_pmc(int cpu, int ri) +{ + uint32_t config; + struct pmc *pm; + struct pmc_hw *phw; + register_t pmc_mmcr; + + phw = &powerpc_pcpu[cpu]->pc_ppcpmcs[ri]; + pm = phw->phw_pmc; + config = pm->pm_md.pm_powerpc.pm_powerpc_evsel & ~POWERPC_PMC_ENABLE; + + /* Enable the PMC. */ + switch (ri) { + case 0: + pmc_mmcr = mfspr(SPR_MMCR0); + pmc_mmcr = PPC_SET_PMC1SEL(pmc_mmcr, config); + mtspr(SPR_MMCR0, pmc_mmcr); + break; + case 1: + pmc_mmcr = mfspr(SPR_MMCR0); + pmc_mmcr = PPC_SET_PMC2SEL(pmc_mmcr, config); + mtspr(SPR_MMCR0, pmc_mmcr); + break; + case 2: + pmc_mmcr = mfspr(SPR_MMCR1); + pmc_mmcr = PPC_SET_PMC3SEL(pmc_mmcr, config); + mtspr(SPR_MMCR1, pmc_mmcr); + break; + case 3: + pmc_mmcr = mfspr(SPR_MMCR0); + pmc_mmcr = PPC_SET_PMC4SEL(pmc_mmcr, config); + mtspr(SPR_MMCR0, pmc_mmcr); + break; + case 4: + pmc_mmcr = mfspr(SPR_MMCR1); + pmc_mmcr = PPC_SET_PMC5SEL(pmc_mmcr, config); + mtspr(SPR_MMCR1, pmc_mmcr); + break; + case 5: + pmc_mmcr = mfspr(SPR_MMCR1); + pmc_mmcr = PPC_SET_PMC6SEL(pmc_mmcr, config); + mtspr(SPR_MMCR1, pmc_mmcr); + break; + default: + break; + } + + /* The mask is inverted (enable is 1) compared to the flags in MMCR0, which + * are Freeze flags. + */ + config = ~pm->pm_md.pm_powerpc.pm_powerpc_evsel & POWERPC_PMC_ENABLE; + + pmc_mmcr = mfspr(SPR_MMCR0); + pmc_mmcr &= ~SPR_MMCR0_FC; + pmc_mmcr |= config; + mtspr(SPR_MMCR0, pmc_mmcr); + + return 0; +} + +static int +mpc7xxx_stop_pmc(int cpu, int ri) +{ + struct pmc *pm; + struct pmc_hw *phw; + register_t pmc_mmcr; + + phw = &powerpc_pcpu[cpu]->pc_ppcpmcs[ri]; + pm = phw->phw_pmc; + + /* + * Disable the PMCs. + */ + switch (ri) { + case 0: + pmc_mmcr = mfspr(SPR_MMCR0); + pmc_mmcr = PPC_SET_PMC1SEL(pmc_mmcr, 0); + mtspr(SPR_MMCR0, pmc_mmcr); + break; + case 1: + pmc_mmcr = mfspr(SPR_MMCR0); + pmc_mmcr = PPC_SET_PMC2SEL(pmc_mmcr, 0); + mtspr(SPR_MMCR0, pmc_mmcr); + break; + case 2: + pmc_mmcr = mfspr(SPR_MMCR1); + pmc_mmcr = PPC_SET_PMC3SEL(pmc_mmcr, 0); + mtspr(SPR_MMCR1, pmc_mmcr); + break; + case 3: + pmc_mmcr = mfspr(SPR_MMCR0); + pmc_mmcr = PPC_SET_PMC4SEL(pmc_mmcr, 0); + mtspr(SPR_MMCR0, pmc_mmcr); + break; + case 4: + pmc_mmcr = mfspr(SPR_MMCR1); + pmc_mmcr = PPC_SET_PMC5SEL(pmc_mmcr, 0); + mtspr(SPR_MMCR1, pmc_mmcr); + break; + case 5: + pmc_mmcr = mfspr(SPR_MMCR1); + pmc_mmcr = PPC_SET_PMC6SEL(pmc_mmcr, 0); + mtspr(SPR_MMCR1, pmc_mmcr); + break; + default: + break; + } + return 0; +} + +static int +mpc7xxx_pcpu_init(struct pmc_mdep *md, int cpu) +{ + int first_ri, i; + struct pmc_cpu *pc; + struct powerpc_cpu *pac; + struct pmc_hw *phw; + + KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), + ("[powerpc,%d] wrong cpu number %d", __LINE__, cpu)); + PMCDBG(MDP,INI,1,"powerpc-init cpu=%d", cpu); + + powerpc_pcpu[cpu] = pac = malloc(sizeof(struct powerpc_cpu), M_PMC, + M_WAITOK|M_ZERO); + pac->pc_ppcpmcs = malloc(sizeof(struct pmc_hw) * MPC7XXX_MAX_PMCS, + M_PMC, M_WAITOK|M_ZERO); + pc = pmc_pcpu[cpu]; + first_ri = md->pmd_classdep[PMC_MDEP_CLASS_INDEX_PPC7450].pcd_ri; + KASSERT(pc != NULL, ("[powerpc,%d] NULL per-cpu pointer", __LINE__)); + + for (i = 0, phw = pac->pc_ppcpmcs; i < MPC7XXX_MAX_PMCS; i++, phw++) { + phw->phw_state = PMC_PHW_FLAG_IS_ENABLED | + PMC_PHW_CPU_TO_STATE(cpu) | PMC_PHW_INDEX_TO_STATE(i); + phw->phw_pmc = NULL; + pc->pc_hwpmcs[i + first_ri] = phw; + } + + /* Clear the MMCRs, and set FC, to disable all PMCs. */ + mtspr(SPR_MMCR0, SPR_MMCR0_FC | SPR_MMCR0_PMXE | SPR_MMCR0_PMC1CE | SPR_MMCR0_PMCNCE); + mtspr(SPR_MMCR1, 0); + + return 0; +} + +static int +mpc7xxx_pcpu_fini(struct pmc_mdep *md, int cpu) +{ + uint32_t mmcr0 = mfspr(SPR_MMCR0); + + mmcr0 |= SPR_MMCR0_FC; + mtspr(SPR_MMCR0, mmcr0); + free(powerpc_pcpu[cpu]->pc_ppcpmcs, M_PMC); + free(powerpc_pcpu[cpu], M_PMC); + return 0; +} + +static int +mpc7xxx_allocate_pmc(int cpu, int ri, struct pmc *pm, + const struct pmc_op_pmcallocate *a) +{ + enum pmc_event pe; + uint32_t caps, config, counter; + int i; + + KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), + ("[powerpc,%d] illegal CPU value %d", __LINE__, cpu)); + KASSERT(ri >= 0 && ri < MPC7XXX_MAX_PMCS, + ("[powerpc,%d] illegal row index %d", __LINE__, ri)); + + caps = a->pm_caps; + + pe = a->pm_ev; + for (i = 0; i < powerpc_event_codes_size; i++) { + if (powerpc_event_codes[i].pe_ev == pe) { + config = powerpc_event_codes[i].pe_code; + counter = powerpc_event_codes[i].pe_counter_mask; + break; + } + } + if (i == powerpc_event_codes_size) + return (EINVAL); + + if ((counter & (1 << ri)) == 0) + return (EINVAL); + + if (caps & PMC_CAP_SYSTEM) + config |= POWERPC_PMC_KERNEL_ENABLE; + if (caps & PMC_CAP_USER) + config |= POWERPC_PMC_USER_ENABLE; + if ((caps & (PMC_CAP_USER | PMC_CAP_SYSTEM)) == 0) + config |= POWERPC_PMC_ENABLE; + + pm->pm_md.pm_powerpc.pm_powerpc_evsel = config; + + PMCDBG(MDP,ALL,2,"powerpc-allocate ri=%d -> config=0x%x", ri, config); + + return 0; +} + +static int +mpc7xxx_release_pmc(int cpu, int ri, struct pmc *pmc) +{ + struct pmc_hw *phw; + + KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), + ("[powerpc,%d] illegal CPU value %d", __LINE__, cpu)); + KASSERT(ri >= 0 && ri < MPC7XXX_MAX_PMCS, + ("[powerpc,%d] illegal row-index %d", __LINE__, ri)); + + phw = &powerpc_pcpu[cpu]->pc_ppcpmcs[ri]; + KASSERT(phw->phw_pmc == NULL, + ("[powerpc,%d] PHW pmc %p non-NULL", __LINE__, phw->phw_pmc)); + + return 0; +} + +static int +mpc7xxx_intr(int cpu, struct trapframe *tf) +{ + int i, error, retval; + uint32_t config; + struct pmc *pm; + struct powerpc_cpu *pac; + pmc_value_t v; + + KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), + ("[powerpc,%d] out of range CPU %d", __LINE__, cpu)); + + PMCDBG(MDP,INT,1, "cpu=%d tf=%p um=%d", cpu, (void *) tf, + TRAPF_USERMODE(tf)); + + retval = 0; + + pac = powerpc_pcpu[cpu]; + + config = mfspr(SPR_MMCR0); + mtspr(SPR_MMCR0, config | SPR_MMCR0_FC); + + /* + * look for all PMCs that have interrupted: + * - look for a running, sampling PMC which has overflowed + * and which has a valid 'struct pmc' association + * + * If found, we call a helper to process the interrupt. + */ + + for (i = 0; i < MPC7XXX_MAX_PMCS; i++) { + if ((pm = pac->pc_ppcpmcs[i].phw_pmc) == NULL || + !PMC_IS_SAMPLING_MODE(PMC_TO_MODE(pm))) { + continue; + } + + if (!MPC7XXX_PMC_HAS_OVERFLOWED(i)) + continue; + + retval = 1; /* Found an interrupting PMC. */ + + if (pm->pm_state != PMC_STATE_RUNNING) + continue; + + /* Stop the PMC, reload count. */ + v = pm->pm_sc.pm_reloadcount; + mpc7xxx_pmcn_write(i, v); + + /* Restart the counter if logging succeeded. */ + error = pmc_process_interrupt(cpu, PMC_HR, pm, tf, + TRAPF_USERMODE(tf)); + if (error != 0) + mpc7xxx_stop_pmc(cpu, i); + atomic_add_int(retval ? &pmc_stats.pm_intr_processed : + &pmc_stats.pm_intr_ignored, 1); + + } + + /* Re-enable PERF exceptions. */ + mtspr(SPR_MMCR0, config | SPR_MMCR0_PMXE); + + return (retval); +} + +int +pmc_mpc7xxx_initialize(struct pmc_mdep *pmc_mdep) +{ + struct pmc_classdep *pcd; + + pcd = &pmc_mdep->pmd_classdep[PMC_MDEP_CLASS_INDEX_PPC7450]; + pcd->pcd_caps = POWERPC_PMC_CAPS; + pcd->pcd_class = PMC_CLASS_PPC7450; + pcd->pcd_num = MPC7XXX_MAX_PMCS; + pcd->pcd_ri = pmc_mdep->pmd_npmc; + pcd->pcd_width = 32; /* All PMCs, even in ppc970, are 32-bit */ + + pcd->pcd_allocate_pmc = mpc7xxx_allocate_pmc; + pcd->pcd_config_pmc = mpc7xxx_config_pmc; + pcd->pcd_pcpu_fini = mpc7xxx_pcpu_fini; + pcd->pcd_pcpu_init = mpc7xxx_pcpu_init; + pcd->pcd_read_pmc = mpc7xxx_read_pmc; + pcd->pcd_release_pmc = mpc7xxx_release_pmc; + pcd->pcd_start_pmc = mpc7xxx_start_pmc; + pcd->pcd_stop_pmc = mpc7xxx_stop_pmc; + pcd->pcd_write_pmc = mpc7xxx_write_pmc; + + pmc_mdep->pmd_npmc += MPC7XXX_MAX_PMCS; + pmc_mdep->pmd_intr = mpc7xxx_intr; + + return 0; +} diff --git a/sys/dev/hwpmc/hwpmc_powerpc.c b/sys/dev/hwpmc/hwpmc_powerpc.c index a54ee62da0b..25a32fa35d3 100644 --- a/sys/dev/hwpmc/hwpmc_powerpc.c +++ b/sys/dev/hwpmc/hwpmc_powerpc.c @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2011 Justin Hibbits + * Copyright (c) 2011,2013 Justin Hibbits * Copyright (c) 2005, Joseph Koshy * All rights reserved. * @@ -36,677 +36,50 @@ __FBSDID("$FreeBSD$"); #include #include +#include +#include #include +#include /* For VM_MIN_KERNEL_ADDRESS/VM_MAX_KERNEL_ADDRESS */ -#define POWERPC_PMC_CAPS (PMC_CAP_INTERRUPT | PMC_CAP_USER | \ - PMC_CAP_SYSTEM | PMC_CAP_EDGE | \ - PMC_CAP_THRESHOLD | PMC_CAP_READ | \ - PMC_CAP_WRITE | PMC_CAP_INVERT | \ - PMC_CAP_QUALIFIER) +#include "hwpmc_powerpc.h" -#define PPC_SET_PMC1SEL(r, x) ((r & ~(SPR_MMCR0_PMC1SEL(0x3f))) | SPR_MMCR0_PMC1SEL(x)) -#define PPC_SET_PMC2SEL(r, x) ((r & ~(SPR_MMCR0_PMC2SEL(0x3f))) | SPR_MMCR0_PMC2SEL(x)) -#define PPC_SET_PMC3SEL(r, x) ((r & ~(SPR_MMCR1_PMC3SEL(0x1f))) | SPR_MMCR1_PMC3SEL(x)) -#define PPC_SET_PMC4SEL(r, x) ((r & ~(SPR_MMCR1_PMC4SEL(0x1f))) | SPR_MMCR1_PMC4SEL(x)) -#define PPC_SET_PMC5SEL(r, x) ((r & ~(SPR_MMCR1_PMC5SEL(0x1f))) | SPR_MMCR1_PMC5SEL(x)) -#define PPC_SET_PMC6SEL(r, x) ((r & ~(SPR_MMCR1_PMC6SEL(0x3f))) | SPR_MMCR1_PMC6SEL(x)) +#define INKERNEL(x) (((vm_offset_t)(x)) <= VM_MAX_KERNEL_ADDRESS && \ + ((vm_offset_t)(x)) >= VM_MIN_KERNEL_ADDRESS) -/* Change this when we support more than just the 7450. */ -#define PPC_MAX_PMCS 6 - -#define POWERPC_PMC_KERNEL_ENABLE (0x1 << 30) -#define POWERPC_PMC_USER_ENABLE (0x1 << 31) - -#define POWERPC_PMC_ENABLE (POWERPC_PMC_KERNEL_ENABLE | POWERPC_PMC_USER_ENABLE) -#define POWERPC_RELOAD_COUNT_TO_PERFCTR_VALUE(V) (0x80000000-(V)) -#define POWERPC_PERFCTR_VALUE_TO_RELOAD_COUNT(P) ((P)-0x80000000) -#define POWERPC_PMC_HAS_OVERFLOWED(x) (powerpc_pmcn_read(x) & (0x1 << 31)) - - -/* - * This should work for every 32-bit PowerPC implementation I know of (G3 and G4 - * specifically). PoewrPC 970 will take more work. - */ - -/* - * Per-processor information. - */ -struct powerpc_cpu { - struct pmc_hw *pc_ppcpmcs; -}; - -static struct powerpc_cpu **powerpc_pcpu; - -struct powerpc_event_code_map { - enum pmc_event pe_ev; /* enum value */ - uint8_t pe_counter_mask; /* Which counter this can be counted in. */ - uint8_t pe_code; /* numeric code */ -}; - -#define PPC_PMC_MASK1 0 -#define PPC_PMC_MASK2 1 -#define PPC_PMC_MASK3 2 -#define PPC_PMC_MASK4 3 -#define PPC_PMC_MASK5 4 -#define PPC_PMC_MASK6 5 -#define PPC_PMC_MASK_ALL 0x3f - -#define PMC_POWERPC_EVENT(id, mask, number) \ - { .pe_ev = PMC_EV_PPC7450_##id, .pe_counter_mask = mask, .pe_code = number } - -static struct powerpc_event_code_map powerpc_event_codes[] = { - PMC_POWERPC_EVENT(CYCLE,PPC_PMC_MASK_ALL, 1), - PMC_POWERPC_EVENT(INSTR_COMPLETED, 0x0f, 2), - PMC_POWERPC_EVENT(TLB_BIT_TRANSITIONS, 0x0f, 3), - PMC_POWERPC_EVENT(INSTR_DISPATCHED, 0x0f, 4), - PMC_POWERPC_EVENT(PMON_EXCEPT, 0x0f, 5), - PMC_POWERPC_EVENT(PMON_SIG, 0x0f, 7), - PMC_POWERPC_EVENT(VPU_INSTR_COMPLETED, 0x03, 8), - PMC_POWERPC_EVENT(VFPU_INSTR_COMPLETED, 0x03, 9), - PMC_POWERPC_EVENT(VIU1_INSTR_COMPLETED, 0x03, 10), - PMC_POWERPC_EVENT(VIU2_INSTR_COMPLETED, 0x03, 11), - PMC_POWERPC_EVENT(MTVSCR_INSTR_COMPLETED, 0x03, 12), - PMC_POWERPC_EVENT(MTVRSAVE_INSTR_COMPLETED, 0x03, 13), - PMC_POWERPC_EVENT(VPU_INSTR_WAIT_CYCLES, 0x03, 14), - PMC_POWERPC_EVENT(VFPU_INSTR_WAIT_CYCLES, 0x03, 15), - PMC_POWERPC_EVENT(VIU1_INSTR_WAIT_CYCLES, 0x03, 16), - PMC_POWERPC_EVENT(VIU2_INSTR_WAIT_CYCLES, 0x03, 17), - PMC_POWERPC_EVENT(MFVSCR_SYNC_CYCLES, 0x03, 18), - PMC_POWERPC_EVENT(VSCR_SAT_SET, 0x03, 19), - PMC_POWERPC_EVENT(STORE_INSTR_COMPLETED, 0x03, 20), - PMC_POWERPC_EVENT(L1_INSTR_CACHE_MISSES, 0x03, 21), - PMC_POWERPC_EVENT(L1_DATA_SNOOPS, 0x03, 22), - PMC_POWERPC_EVENT(UNRESOLVED_BRANCHES, 0x01, 23), - PMC_POWERPC_EVENT(SPEC_BUFFER_CYCLES, 0x01, 24), - PMC_POWERPC_EVENT(BRANCH_UNIT_STALL_CYCLES, 0x01, 25), - PMC_POWERPC_EVENT(TRUE_BRANCH_TARGET_HITS, 0x01, 26), - PMC_POWERPC_EVENT(BRANCH_LINK_STAC_PREDICTED, 0x01, 27), - PMC_POWERPC_EVENT(GPR_ISSUE_QUEUE_DISPATCHES, 0x01, 28), - PMC_POWERPC_EVENT(CYCLES_THREE_INSTR_DISPATCHED, 0x01, 29), - PMC_POWERPC_EVENT(THRESHOLD_INSTR_QUEUE_ENTRIES_CYCLES, 0x01, 30), - PMC_POWERPC_EVENT(THRESHOLD_VEC_INSTR_QUEUE_ENTRIES_CYCLES, 0x01, 31), - PMC_POWERPC_EVENT(CYCLES_NO_COMPLETED_INSTRS, 0x01, 32), - PMC_POWERPC_EVENT(IU2_INSTR_COMPLETED, 0x01, 33), - PMC_POWERPC_EVENT(BRANCHES_COMPLETED, 0x01, 34), - PMC_POWERPC_EVENT(EIEIO_INSTR_COMPLETED, 0x01, 35), - PMC_POWERPC_EVENT(MTSPR_INSTR_COMPLETED, 0x01, 36), - PMC_POWERPC_EVENT(SC_INSTR_COMPLETED, 0x01, 37), - PMC_POWERPC_EVENT(LS_LM_COMPLETED, 0x01, 38), - PMC_POWERPC_EVENT(ITLB_HW_TABLE_SEARCH_CYCLES, 0x01, 39), - PMC_POWERPC_EVENT(DTLB_HW_SEARCH_CYCLES_OVER_THRESHOLD, 0x01, 40), - PMC_POWERPC_EVENT(L1_INSTR_CACHE_ACCESSES, 0x01, 41), - PMC_POWERPC_EVENT(INSTR_BKPT_MATCHES, 0x01, 42), - PMC_POWERPC_EVENT(L1_DATA_CACHE_LOAD_MISS_CYCLES_OVER_THRESHOLD, 0x01, 43), - PMC_POWERPC_EVENT(L1_DATA_SNOOP_HIT_ON_MODIFIED, 0x01, 44), - PMC_POWERPC_EVENT(LOAD_MISS_ALIAS, 0x01, 45), - PMC_POWERPC_EVENT(LOAD_MISS_ALIAS_ON_TOUCH, 0x01, 46), - PMC_POWERPC_EVENT(TOUCH_ALIAS, 0x01, 47), - PMC_POWERPC_EVENT(L1_DATA_SNOOP_HIT_CASTOUT_QUEUE, 0x01, 48), - PMC_POWERPC_EVENT(L1_DATA_SNOOP_HIT_CASTOUT, 0x01, 49), - PMC_POWERPC_EVENT(L1_DATA_SNOOP_HITS, 0x01, 50), - PMC_POWERPC_EVENT(WRITE_THROUGH_STORES, 0x01, 51), - PMC_POWERPC_EVENT(CACHE_INHIBITED_STORES, 0x01, 52), - PMC_POWERPC_EVENT(L1_DATA_LOAD_HIT, 0x01, 53), - PMC_POWERPC_EVENT(L1_DATA_TOUCH_HIT, 0x01, 54), - PMC_POWERPC_EVENT(L1_DATA_STORE_HIT, 0x01, 55), - PMC_POWERPC_EVENT(L1_DATA_TOTAL_HITS, 0x01, 56), - PMC_POWERPC_EVENT(DST_INSTR_DISPATCHED, 0x01, 57), - PMC_POWERPC_EVENT(REFRESHED_DSTS, 0x01, 58), - PMC_POWERPC_EVENT(SUCCESSFUL_DST_TABLE_SEARCHES, 0x01, 59), - PMC_POWERPC_EVENT(DSS_INSTR_COMPLETED, 0x01, 60), - PMC_POWERPC_EVENT(DST_STREAM_0_CACHE_LINE_FETCHES, 0x01, 61), - PMC_POWERPC_EVENT(VTQ_SUSPENDS_DUE_TO_CTX_CHANGE, 0x01, 62), - PMC_POWERPC_EVENT(VTQ_LINE_FETCH_HIT, 0x01, 63), - PMC_POWERPC_EVENT(VEC_LOAD_INSTR_COMPLETED, 0x01, 64), - PMC_POWERPC_EVENT(FP_STORE_INSTR_COMPLETED_IN_LSU, 0x01, 65), - PMC_POWERPC_EVENT(FPU_RENORMALIZATION, 0x01, 66), - PMC_POWERPC_EVENT(FPU_DENORMALIZATION, 0x01, 67), - PMC_POWERPC_EVENT(FP_STORE_CAUSES_STALL_IN_LSU, 0x01, 68), - PMC_POWERPC_EVENT(LD_ST_TRUE_ALIAS_STALL, 0x01, 70), - PMC_POWERPC_EVENT(LSU_INDEXED_ALIAS_STALL, 0x01, 71), - PMC_POWERPC_EVENT(LSU_ALIAS_VS_FSQ_WB0_WB1, 0x01, 72), - PMC_POWERPC_EVENT(LSU_ALIAS_VS_CSQ, 0x01, 73), - PMC_POWERPC_EVENT(LSU_LOAD_HIT_LINE_ALIAS_VS_CSQ0, 0x01, 74), - PMC_POWERPC_EVENT(LSU_LOAD_MISS_LINE_ALIAS_VS_CSQ0, 0x01, 75), - PMC_POWERPC_EVENT(LSU_TOUCH_LINE_ALIAS_VS_FSQ_WB0_WB1, 0x01, 76), - PMC_POWERPC_EVENT(LSU_TOUCH_ALIAS_VS_CSQ, 0x01, 77), - PMC_POWERPC_EVENT(LSU_LMQ_FULL_STALL, 0x01, 78), - PMC_POWERPC_EVENT(FP_LOAD_INSTR_COMPLETED_IN_LSU, 0x01, 79), - PMC_POWERPC_EVENT(FP_LOAD_SINGLE_INSTR_COMPLETED_IN_LSU, 0x01, 80), - PMC_POWERPC_EVENT(FP_LOAD_DOUBLE_COMPLETED_IN_LSU, 0x01, 81), - PMC_POWERPC_EVENT(LSU_RA_LATCH_STALL, 0x01, 82), - PMC_POWERPC_EVENT(LSU_LOAD_VS_STORE_QUEUE_ALIAS_STALL, 0x01, 83), - PMC_POWERPC_EVENT(LSU_LMQ_INDEX_ALIAS, 0x01, 84), - PMC_POWERPC_EVENT(LSU_STORE_QUEUE_INDEX_ALIAS, 0x01, 85), - PMC_POWERPC_EVENT(LSU_CSQ_FORWARDING, 0x01, 86), - PMC_POWERPC_EVENT(LSU_MISALIGNED_LOAD_FINISH, 0x01, 87), - PMC_POWERPC_EVENT(LSU_MISALIGN_STORE_COMPLETED, 0x01, 88), - PMC_POWERPC_EVENT(LSU_MISALIGN_STALL, 0x01, 89), - PMC_POWERPC_EVENT(FP_ONE_QUARTER_FPSCR_RENAMES_BUSY, 0x01, 90), - PMC_POWERPC_EVENT(FP_ONE_HALF_FPSCR_RENAMES_BUSY, 0x01, 91), - PMC_POWERPC_EVENT(FP_THREE_QUARTERS_FPSCR_RENAMES_BUSY, 0x01, 92), - PMC_POWERPC_EVENT(FP_ALL_FPSCR_RENAMES_BUSY, 0x01, 93), - PMC_POWERPC_EVENT(FP_DENORMALIZED_RESULT, 0x01, 94), - PMC_POWERPC_EVENT(L1_DATA_TOTAL_MISSES, 0x02, 23), - PMC_POWERPC_EVENT(DISPATCHES_TO_FPR_ISSUE_QUEUE, 0x02, 24), - PMC_POWERPC_EVENT(LSU_INSTR_COMPLETED, 0x02, 25), - PMC_POWERPC_EVENT(LOAD_INSTR_COMPLETED, 0x02, 26), - PMC_POWERPC_EVENT(SS_SM_INSTR_COMPLETED, 0x02, 27), - PMC_POWERPC_EVENT(TLBIE_INSTR_COMPLETED, 0x02, 28), - PMC_POWERPC_EVENT(LWARX_INSTR_COMPLETED, 0x02, 29), - PMC_POWERPC_EVENT(MFSPR_INSTR_COMPLETED, 0x02, 30), - PMC_POWERPC_EVENT(REFETCH_SERIALIZATION, 0x02, 31), - PMC_POWERPC_EVENT(COMPLETION_QUEUE_ENTRIES_OVER_THRESHOLD, 0x02, 32), - PMC_POWERPC_EVENT(CYCLES_ONE_INSTR_DISPATCHED, 0x02, 33), - PMC_POWERPC_EVENT(CYCLES_TWO_INSTR_COMPLETED, 0x02, 34), - PMC_POWERPC_EVENT(ITLB_NON_SPECULATIVE_MISSES, 0x02, 35), - PMC_POWERPC_EVENT(CYCLES_WAITING_FROM_L1_INSTR_CACHE_MISS, 0x02, 36), - PMC_POWERPC_EVENT(L1_DATA_LOAD_ACCESS_MISS, 0x02, 37), - PMC_POWERPC_EVENT(L1_DATA_TOUCH_MISS, 0x02, 38), - PMC_POWERPC_EVENT(L1_DATA_STORE_MISS, 0x02, 39), - PMC_POWERPC_EVENT(L1_DATA_TOUCH_MISS_CYCLES, 0x02, 40), - PMC_POWERPC_EVENT(L1_DATA_CYCLES_USED, 0x02, 41), - PMC_POWERPC_EVENT(DST_STREAM_1_CACHE_LINE_FETCHES, 0x02, 42), - PMC_POWERPC_EVENT(VTQ_STREAM_CANCELED_PREMATURELY, 0x02, 43), - PMC_POWERPC_EVENT(VTQ_RESUMES_DUE_TO_CTX_CHANGE, 0x02, 44), - PMC_POWERPC_EVENT(VTQ_LINE_FETCH_MISS, 0x02, 45), - PMC_POWERPC_EVENT(VTQ_LINE_FETCH, 0x02, 46), - PMC_POWERPC_EVENT(TLBIE_SNOOPS, 0x02, 47), - PMC_POWERPC_EVENT(L1_INSTR_CACHE_RELOADS, 0x02, 48), - PMC_POWERPC_EVENT(L1_DATA_CACHE_RELOADS, 0x02, 49), - PMC_POWERPC_EVENT(L1_DATA_CACHE_CASTOUTS_TO_L2, 0x02, 50), - PMC_POWERPC_EVENT(STORE_MERGE_GATHER, 0x02, 51), - PMC_POWERPC_EVENT(CACHEABLE_STORE_MERGE_TO_32_BYTES, 0x02, 52), - PMC_POWERPC_EVENT(DATA_BKPT_MATCHES, 0x02, 53), - PMC_POWERPC_EVENT(FALL_THROUGH_BRANCHES_PROCESSED, 0x02, 54), - PMC_POWERPC_EVENT(FIRST_SPECULATIVE_BRANCH_BUFFER_RESOLVED_CORRECTLY, 0x02, 55), - PMC_POWERPC_EVENT(SECOND_SPECULATION_BUFFER_ACTIVE, 0x02, 56), - PMC_POWERPC_EVENT(BPU_STALL_ON_LR_DEPENDENCY, 0x02, 57), - PMC_POWERPC_EVENT(BTIC_MISS, 0x02, 58), - PMC_POWERPC_EVENT(BRANCH_LINK_STACK_CORRECTLY_RESOLVED, 0x02, 59), - PMC_POWERPC_EVENT(FPR_ISSUE_STALLED, 0x02, 60), - PMC_POWERPC_EVENT(SWITCHES_BETWEEN_PRIV_USER, 0x02, 61), - PMC_POWERPC_EVENT(LSU_COMPLETES_FP_STORE_SINGLE, 0x02, 62), - PMC_POWERPC_EVENT(CYCLES_TWO_INSTR_COMPLETED, 0x04, 8), - PMC_POWERPC_EVENT(CYCLES_ONE_INSTR_DISPATCHED, 0x04, 9), - PMC_POWERPC_EVENT(VR_ISSUE_QUEUE_DISPATCHES, 0x04, 10), - PMC_POWERPC_EVENT(VR_STALLS, 0x04, 11), - PMC_POWERPC_EVENT(GPR_RENAME_BUFFER_ENTRIES_OVER_THRESHOLD, 0x04, 12), - PMC_POWERPC_EVENT(FPR_ISSUE_QUEUE_ENTRIES, 0x04, 13), - PMC_POWERPC_EVENT(FPU_INSTR_COMPLETED, 0x04, 14), - PMC_POWERPC_EVENT(STWCX_INSTR_COMPLETED, 0x04, 15), - PMC_POWERPC_EVENT(LS_LM_INSTR_PIECES, 0x04, 16), - PMC_POWERPC_EVENT(ITLB_HW_SEARCH_CYCLES_OVER_THRESHOLD, 0x04, 17), - PMC_POWERPC_EVENT(DTLB_MISSES, 0x04, 18), - PMC_POWERPC_EVENT(CANCELLED_L1_INSTR_CACHE_MISSES, 0x04, 19), - PMC_POWERPC_EVENT(L1_DATA_CACHE_OP_HIT, 0x04, 20), - PMC_POWERPC_EVENT(L1_DATA_LOAD_MISS_CYCLES, 0x04, 21), - PMC_POWERPC_EVENT(L1_DATA_PUSHES, 0x04, 22), - PMC_POWERPC_EVENT(L1_DATA_TOTAL_MISS, 0x04, 23), - PMC_POWERPC_EVENT(VT2_FETCHES, 0x04, 24), - PMC_POWERPC_EVENT(TAKEN_BRANCHES_PROCESSED, 0x04, 25), - PMC_POWERPC_EVENT(BRANCH_FLUSHES, 0x04, 26), - PMC_POWERPC_EVENT(SECOND_SPECULATIVE_BRANCH_BUFFER_RESOLVED_CORRECTLY, 0x04, 27), - PMC_POWERPC_EVENT(THIRD_SPECULATION_BUFFER_ACTIVE, 0x04, 28), - PMC_POWERPC_EVENT(BRANCH_UNIT_STALL_ON_CTR_DEPENDENCY, 0x04, 29), - PMC_POWERPC_EVENT(FAST_BTIC_HIT, 0x04, 30), - PMC_POWERPC_EVENT(BRANCH_LINK_STACK_MISPREDICTED, 0x04, 31), - PMC_POWERPC_EVENT(CYCLES_THREE_INSTR_COMPLETED, 0x08, 14), - PMC_POWERPC_EVENT(CYCLES_NO_INSTR_DISPATCHED, 0x08, 15), - PMC_POWERPC_EVENT(GPR_ISSUE_QUEUE_ENTRIES_OVER_THRESHOLD, 0x08, 16), - PMC_POWERPC_EVENT(GPR_ISSUE_QUEUE_STALLED, 0x08, 17), - PMC_POWERPC_EVENT(IU1_INSTR_COMPLETED, 0x08, 18), - PMC_POWERPC_EVENT(DSSALL_INSTR_COMPLETED, 0x08, 19), - PMC_POWERPC_EVENT(TLBSYNC_INSTR_COMPLETED, 0x08, 20), - PMC_POWERPC_EVENT(SYNC_INSTR_COMPLETED, 0x08, 21), - PMC_POWERPC_EVENT(SS_SM_INSTR_PIECES, 0x08, 22), - PMC_POWERPC_EVENT(DTLB_HW_SEARCH_CYCLES, 0x08, 23), - PMC_POWERPC_EVENT(SNOOP_RETRIES, 0x08, 24), - PMC_POWERPC_EVENT(SUCCESSFUL_STWCX, 0x08, 25), - PMC_POWERPC_EVENT(DST_STREAM_3_CACHE_LINE_FETCHES, 0x08, 26), - PMC_POWERPC_EVENT(THIRD_SPECULATIVE_BRANCH_BUFFER_RESOLVED_CORRECTLY, 0x08, 27), - PMC_POWERPC_EVENT(MISPREDICTED_BRANCHES, 0x08, 28), - PMC_POWERPC_EVENT(FOLDED_BRANCHES, 0x08, 29), - PMC_POWERPC_EVENT(FP_STORE_DOUBLE_COMPLETES_IN_LSU, 0x08, 30), - PMC_POWERPC_EVENT(L2_CACHE_HITS, 0x30, 2), - PMC_POWERPC_EVENT(L3_CACHE_HITS, 0x30, 3), - PMC_POWERPC_EVENT(L2_INSTR_CACHE_MISSES, 0x30, 4), - PMC_POWERPC_EVENT(L3_INSTR_CACHE_MISSES, 0x30, 5), - PMC_POWERPC_EVENT(L2_DATA_CACHE_MISSES, 0x30, 6), - PMC_POWERPC_EVENT(L3_DATA_CACHE_MISSES, 0x30, 7), - PMC_POWERPC_EVENT(L2_LOAD_HITS, 0x10, 8), - PMC_POWERPC_EVENT(L2_STORE_HITS, 0x10, 9), - PMC_POWERPC_EVENT(L3_LOAD_HITS, 0x10, 10), - PMC_POWERPC_EVENT(L3_STORE_HITS, 0x10, 11), - PMC_POWERPC_EVENT(L2_TOUCH_HITS, 0x30, 13), - PMC_POWERPC_EVENT(L3_TOUCH_HITS, 0x30, 14), - PMC_POWERPC_EVENT(SNOOP_RETRIES, 0x30, 15), - PMC_POWERPC_EVENT(SNOOP_MODIFIED, 0x10, 16), - PMC_POWERPC_EVENT(SNOOP_VALID, 0x10, 17), - PMC_POWERPC_EVENT(INTERVENTION, 0x30, 18), - PMC_POWERPC_EVENT(L2_CACHE_MISSES, 0x10, 19), - PMC_POWERPC_EVENT(L3_CACHE_MISSES, 0x10, 20), - PMC_POWERPC_EVENT(L2_CACHE_CASTOUTS, 0x20, 8), - PMC_POWERPC_EVENT(L3_CACHE_CASTOUTS, 0x20, 9), - PMC_POWERPC_EVENT(L2SQ_FULL_CYCLES, 0x20, 10), - PMC_POWERPC_EVENT(L3SQ_FULL_CYCLES, 0x20, 11), - PMC_POWERPC_EVENT(RAQ_FULL_CYCLES, 0x20, 16), - PMC_POWERPC_EVENT(WAQ_FULL_CYCLES, 0x20, 17), - PMC_POWERPC_EVENT(L1_EXTERNAL_INTERVENTIONS, 0x20, 19), - PMC_POWERPC_EVENT(L2_EXTERNAL_INTERVENTIONS, 0x20, 20), - PMC_POWERPC_EVENT(L3_EXTERNAL_INTERVENTIONS, 0x20, 21), - PMC_POWERPC_EVENT(EXTERNAL_INTERVENTIONS, 0x20, 22), - PMC_POWERPC_EVENT(EXTERNAL_PUSHES, 0x20, 23), - PMC_POWERPC_EVENT(EXTERNAL_SNOOP_RETRY, 0x20, 24), - PMC_POWERPC_EVENT(DTQ_FULL_CYCLES, 0x20, 25), - PMC_POWERPC_EVENT(BUS_RETRY, 0x20, 26), - PMC_POWERPC_EVENT(L2_VALID_REQUEST, 0x20, 27), - PMC_POWERPC_EVENT(BORDQ_FULL, 0x20, 28), - PMC_POWERPC_EVENT(BUS_TAS_FOR_READS, 0x20, 42), - PMC_POWERPC_EVENT(BUS_TAS_FOR_WRITES, 0x20, 43), - PMC_POWERPC_EVENT(BUS_READS_NOT_RETRIED, 0x20, 44), - PMC_POWERPC_EVENT(BUS_WRITES_NOT_RETRIED, 0x20, 45), - PMC_POWERPC_EVENT(BUS_READS_WRITES_NOT_RETRIED, 0x20, 46), - PMC_POWERPC_EVENT(BUS_RETRY_DUE_TO_L1_RETRY, 0x20, 47), - PMC_POWERPC_EVENT(BUS_RETRY_DUE_TO_PREVIOUS_ADJACENT, 0x20, 48), - PMC_POWERPC_EVENT(BUS_RETRY_DUE_TO_COLLISION, 0x20, 49), - PMC_POWERPC_EVENT(BUS_RETRY_DUE_TO_INTERVENTION_ORDERING, 0x20, 50), - PMC_POWERPC_EVENT(SNOOP_REQUESTS, 0x20, 51), - PMC_POWERPC_EVENT(PREFETCH_ENGINE_REQUEST, 0x20, 52), - PMC_POWERPC_EVENT(PREFETCH_ENGINE_COLLISION_VS_LOAD, 0x20, 53), - PMC_POWERPC_EVENT(PREFETCH_ENGINE_COLLISION_VS_STORE, 0x20, 54), - PMC_POWERPC_EVENT(PREFETCH_ENGINE_COLLISION_VS_INSTR_FETCH, 0x20, 55), - PMC_POWERPC_EVENT(PREFETCH_ENGINE_COLLISION_VS_LOAD_STORE_INSTR_FETCH, 0x20, 56), - PMC_POWERPC_EVENT(PREFETCH_ENGINE_FULL, 0x20, 57) -}; - -const size_t powerpc_event_codes_size = - sizeof(powerpc_event_codes) / sizeof(powerpc_event_codes[0]); +struct powerpc_cpu **powerpc_pcpu; int pmc_save_kernel_callchain(uintptr_t *cc, int maxsamples, struct trapframe *tf) { - (void) cc; - (void) maxsamples; - (void) tf; - return (0); -} + int frames = 0; + uintptr_t *sp; -static pmc_value_t -powerpc_pmcn_read(unsigned int pmc) -{ - switch (pmc) { - case 0: - return mfspr(SPR_PMC1); + cc[frames++] = tf->srr0; + sp = (uintptr_t *)tf->fixreg[1]; + + for (frames = 1; frames < maxsamples; frames++) { + if (!INKERNEL(sp)) break; - case 1: - return mfspr(SPR_PMC2); - break; - case 2: - return mfspr(SPR_PMC3); - break; - case 3: - return mfspr(SPR_PMC4); - break; - case 4: - return mfspr(SPR_PMC5); - break; - case 5: - return mfspr(SPR_PMC6); - default: - panic("Invalid PMC number: %d\n", pmc); + cc[frames++] = *(sp + 1); + sp = (uintptr_t *)*sp; } -} - -static void -powerpc_pmcn_write(unsigned int pmc, uint32_t val) -{ - switch (pmc) { - case 0: - mtspr(SPR_PMC1, val); - break; - case 1: - mtspr(SPR_PMC2, val); - break; - case 2: - mtspr(SPR_PMC3, val); - break; - case 3: - mtspr(SPR_PMC4, val); - break; - case 4: - mtspr(SPR_PMC5, val); - break; - case 5: - mtspr(SPR_PMC6, val); - break; - default: - panic("Invalid PMC number: %d\n", pmc); - } -} - -static int -powerpc_allocate_pmc(int cpu, int ri, struct pmc *pm, - const struct pmc_op_pmcallocate *a) -{ - enum pmc_event pe; - uint32_t caps, config, counter; - int i; - - KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), - ("[powerpc,%d] illegal CPU value %d", __LINE__, cpu)); - KASSERT(ri >= 0 && ri < PPC_MAX_PMCS, - ("[powerpc,%d] illegal row index %d", __LINE__, ri)); - - caps = a->pm_caps; - - /* - * TODO: Check actual class for different generations. - */ - if (a->pm_class != PMC_CLASS_PPC7450) - return (EINVAL); - pe = a->pm_ev; - for (i = 0; i < powerpc_event_codes_size; i++) { - if (powerpc_event_codes[i].pe_ev == pe) { - config = powerpc_event_codes[i].pe_code; - counter = powerpc_event_codes[i].pe_counter_mask; - break; - } - } - if (i == powerpc_event_codes_size) - return (EINVAL); - - if ((counter & (1 << ri)) == 0) - return (EINVAL); - - if (caps & PMC_CAP_SYSTEM) - config |= POWERPC_PMC_KERNEL_ENABLE; - if (caps & PMC_CAP_USER) - config |= POWERPC_PMC_USER_ENABLE; - if ((caps & (PMC_CAP_USER | PMC_CAP_SYSTEM)) == 0) - config |= POWERPC_PMC_ENABLE; - - pm->pm_md.pm_powerpc.pm_powerpc_evsel = config; - - PMCDBG(MDP,ALL,2,"powerpc-allocate ri=%d -> config=0x%x", ri, config); - - return 0; -} - -static int -powerpc_read_pmc(int cpu, int ri, pmc_value_t *v) -{ - struct pmc *pm; - pmc_value_t tmp; - - KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), - ("[powerpc,%d] illegal CPU value %d", __LINE__, cpu)); - KASSERT(ri >= 0 && ri < PPC_MAX_PMCS, - ("[powerpc,%d] illegal row index %d", __LINE__, ri)); - - pm = powerpc_pcpu[cpu]->pc_ppcpmcs[ri].phw_pmc; - tmp = powerpc_pmcn_read(ri); - PMCDBG(MDP,REA,2,"ppc-read id=%d -> %jd", ri, tmp); - if (PMC_IS_SAMPLING_MODE(PMC_TO_MODE(pm))) - *v = POWERPC_PERFCTR_VALUE_TO_RELOAD_COUNT(tmp); - else - *v = tmp; - - return 0; -} - -static int -powerpc_write_pmc(int cpu, int ri, pmc_value_t v) -{ - struct pmc *pm; - - KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), - ("[powerpc,%d] illegal CPU value %d", __LINE__, cpu)); - KASSERT(ri >= 0 && ri < PPC_MAX_PMCS, - ("[powerpc,%d] illegal row-index %d", __LINE__, ri)); - - pm = powerpc_pcpu[cpu]->pc_ppcpmcs[ri].phw_pmc; - - if (PMC_IS_SAMPLING_MODE(PMC_TO_MODE(pm))) - v = POWERPC_RELOAD_COUNT_TO_PERFCTR_VALUE(v); - - PMCDBG(MDP,WRI,1,"powerpc-write cpu=%d ri=%d v=%jx", cpu, ri, v); - - powerpc_pmcn_write(ri, v); - - return 0; -} - -static int -powerpc_config_pmc(int cpu, int ri, struct pmc *pm) -{ - struct pmc_hw *phw; - - PMCDBG(MDP,CFG,1, "cpu=%d ri=%d pm=%p", cpu, ri, pm); - - KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), - ("[powerpc,%d] illegal CPU value %d", __LINE__, cpu)); - KASSERT(ri >= 0 && ri < PPC_MAX_PMCS, - ("[powerpc,%d] illegal row-index %d", __LINE__, ri)); - - phw = &powerpc_pcpu[cpu]->pc_ppcpmcs[ri]; - - KASSERT(pm == NULL || phw->phw_pmc == NULL, - ("[powerpc,%d] pm=%p phw->pm=%p hwpmc not unconfigured", - __LINE__, pm, phw->phw_pmc)); - - phw->phw_pmc = pm; - - return 0; -} - -static int -powerpc_start_pmc(int cpu, int ri) -{ - uint32_t config; - struct pmc *pm; - struct pmc_hw *phw; - register_t pmc_mmcr; - - phw = &powerpc_pcpu[cpu]->pc_ppcpmcs[ri]; - pm = phw->phw_pmc; - config = pm->pm_md.pm_powerpc.pm_powerpc_evsel & ~POWERPC_PMC_ENABLE; - - /* Enable the PMC. */ - switch (ri) { - case 0: - pmc_mmcr = mfspr(SPR_MMCR0); - pmc_mmcr = PPC_SET_PMC1SEL(pmc_mmcr, config); - mtspr(SPR_MMCR0, pmc_mmcr); - break; - case 1: - pmc_mmcr = mfspr(SPR_MMCR0); - pmc_mmcr = PPC_SET_PMC2SEL(pmc_mmcr, config); - mtspr(SPR_MMCR0, pmc_mmcr); - break; - case 2: - pmc_mmcr = mfspr(SPR_MMCR1); - pmc_mmcr = PPC_SET_PMC3SEL(pmc_mmcr, config); - mtspr(SPR_MMCR1, pmc_mmcr); - break; - case 3: - pmc_mmcr = mfspr(SPR_MMCR0); - pmc_mmcr = PPC_SET_PMC4SEL(pmc_mmcr, config); - mtspr(SPR_MMCR0, pmc_mmcr); - break; - case 4: - pmc_mmcr = mfspr(SPR_MMCR1); - pmc_mmcr = PPC_SET_PMC5SEL(pmc_mmcr, config); - mtspr(SPR_MMCR1, pmc_mmcr); - break; - case 5: - pmc_mmcr = mfspr(SPR_MMCR1); - pmc_mmcr = PPC_SET_PMC6SEL(pmc_mmcr, config); - mtspr(SPR_MMCR1, pmc_mmcr); - break; - default: - break; - } - - /* The mask is inverted (enable is 1) compared to the flags in MMCR0, which - * are Freeze flags. - */ - config = ~pm->pm_md.pm_powerpc.pm_powerpc_evsel & POWERPC_PMC_ENABLE; - - pmc_mmcr = mfspr(SPR_MMCR0); - pmc_mmcr &= ~SPR_MMCR0_FC; - pmc_mmcr |= config; - mtspr(SPR_MMCR0, pmc_mmcr); - - return 0; -} - -static int -powerpc_stop_pmc(int cpu, int ri) -{ - struct pmc *pm; - struct pmc_hw *phw; - register_t pmc_mmcr; - - phw = &powerpc_pcpu[cpu]->pc_ppcpmcs[ri]; - pm = phw->phw_pmc; - - /* - * Disable the PMCs. - */ - switch (ri) { - case 0: - pmc_mmcr = mfspr(SPR_MMCR0); - pmc_mmcr = PPC_SET_PMC1SEL(pmc_mmcr, 0); - mtspr(SPR_MMCR0, pmc_mmcr); - break; - case 1: - pmc_mmcr = mfspr(SPR_MMCR0); - pmc_mmcr = PPC_SET_PMC2SEL(pmc_mmcr, 0); - mtspr(SPR_MMCR0, pmc_mmcr); - break; - case 2: - pmc_mmcr = mfspr(SPR_MMCR1); - pmc_mmcr = PPC_SET_PMC3SEL(pmc_mmcr, 0); - mtspr(SPR_MMCR1, pmc_mmcr); - break; - case 3: - pmc_mmcr = mfspr(SPR_MMCR0); - pmc_mmcr = PPC_SET_PMC4SEL(pmc_mmcr, 0); - mtspr(SPR_MMCR0, pmc_mmcr); - break; - case 4: - pmc_mmcr = mfspr(SPR_MMCR1); - pmc_mmcr = PPC_SET_PMC5SEL(pmc_mmcr, 0); - mtspr(SPR_MMCR1, pmc_mmcr); - break; - case 5: - pmc_mmcr = mfspr(SPR_MMCR1); - pmc_mmcr = PPC_SET_PMC6SEL(pmc_mmcr, 0); - mtspr(SPR_MMCR1, pmc_mmcr); - break; - default: - break; - } - return 0; -} - -static int -powerpc_release_pmc(int cpu, int ri, struct pmc *pmc) -{ - struct pmc_hw *phw; - - KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), - ("[powerpc,%d] illegal CPU value %d", __LINE__, cpu)); - KASSERT(ri >= 0 && ri < PPC_MAX_PMCS, - ("[powerpc,%d] illegal row-index %d", __LINE__, ri)); - - phw = &powerpc_pcpu[cpu]->pc_ppcpmcs[ri]; - KASSERT(phw->phw_pmc == NULL, - ("[powerpc,%d] PHW pmc %p non-NULL", __LINE__, phw->phw_pmc)); - - return 0; + return (frames); } static int powerpc_switch_in(struct pmc_cpu *pc, struct pmc_process *pp) { - return 0; + return (0); } static int powerpc_switch_out(struct pmc_cpu *pc, struct pmc_process *pp) { - return 0; + return (0); } -static int -powerpc_intr(int cpu, struct trapframe *tf) -{ - int i, error, retval; - uint32_t config; - struct pmc *pm; - struct powerpc_cpu *pac; - pmc_value_t v; - - KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), - ("[powerpc,%d] out of range CPU %d", __LINE__, cpu)); - - PMCDBG(MDP,INT,1, "cpu=%d tf=%p um=%d", cpu, (void *) tf, - TRAPF_USERMODE(tf)); - - retval = 0; - - pac = powerpc_pcpu[cpu]; - - /* - * look for all PMCs that have interrupted: - * - look for a running, sampling PMC which has overflowed - * and which has a valid 'struct pmc' association - * - * If found, we call a helper to process the interrupt. - */ - - for (i = 0; i < PPC_MAX_PMCS; i++) { - if ((pm = pac->pc_ppcpmcs[i].phw_pmc) == NULL || - !PMC_IS_SAMPLING_MODE(PMC_TO_MODE(pm))) { - continue; - } - - if (!POWERPC_PMC_HAS_OVERFLOWED(i)) - continue; - - retval = 1; /* Found an interrupting PMC. */ - - if (pm->pm_state != PMC_STATE_RUNNING) - continue; - - /* Stop the PMC, reload count. */ - v = pm->pm_sc.pm_reloadcount; - config = mfspr(SPR_MMCR0); - - mtspr(SPR_MMCR0, config | SPR_MMCR0_FC); - powerpc_pmcn_write(i, v); - - /* Restart the counter if logging succeeded. */ - error = pmc_process_interrupt(cpu, PMC_HR, pm, tf, - TRAPF_USERMODE(tf)); - mtspr(SPR_MMCR0, config); - if (error != 0) - powerpc_stop_pmc(cpu, i); - atomic_add_int(retval ? &pmc_stats.pm_intr_processed : - &pmc_stats.pm_intr_ignored, 1); - - } - - /* Re-enable PERF exceptions. */ - mtspr(SPR_MMCR0, mfspr(SPR_MMCR0) | SPR_MMCR0_PMXE); - - return (retval); -} - -static int +int powerpc_describe(int cpu, int ri, struct pmc_info *pi, struct pmc **ppmc) { int error; @@ -715,8 +88,6 @@ powerpc_describe(int cpu, int ri, struct pmc_info *pi, struct pmc **ppmc) KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), ("[powerpc,%d], illegal CPU %d", __LINE__, cpu)); - KASSERT(ri >= 0 && ri < PPC_MAX_PMCS, - ("[powerpc,%d] row-index %d out of range", __LINE__, ri)); phw = &powerpc_pcpu[cpu]->pc_ppcpmcs[ri]; snprintf(powerpc_name, sizeof(powerpc_name), "POWERPC-%d", ri); @@ -735,65 +106,20 @@ powerpc_describe(int cpu, int ri, struct pmc_info *pi, struct pmc **ppmc) return (0); } -static int +int powerpc_get_config(int cpu, int ri, struct pmc **ppm) { *ppm = powerpc_pcpu[cpu]->pc_ppcpmcs[ri].phw_pmc; - return 0; -} - -static int -powerpc_pcpu_init(struct pmc_mdep *md, int cpu) -{ - int first_ri, i; - struct pmc_cpu *pc; - struct powerpc_cpu *pac; - struct pmc_hw *phw; - - KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), - ("[powerpc,%d] wrong cpu number %d", __LINE__, cpu)); - PMCDBG(MDP,INI,1,"powerpc-init cpu=%d", cpu); - - powerpc_pcpu[cpu] = pac = malloc(sizeof(struct powerpc_cpu), M_PMC, - M_WAITOK|M_ZERO); - pac->pc_ppcpmcs = malloc(sizeof(struct pmc_hw) * PPC_MAX_PMCS, - M_PMC, M_WAITOK|M_ZERO); - pc = pmc_pcpu[cpu]; - first_ri = md->pmd_classdep[PMC_MDEP_CLASS_INDEX_PPC7450].pcd_ri; - KASSERT(pc != NULL, ("[powerpc,%d] NULL per-cpu pointer", __LINE__)); - - for (i = 0, phw = pac->pc_ppcpmcs; i < PPC_MAX_PMCS; i++, phw++) { - phw->phw_state = PMC_PHW_FLAG_IS_ENABLED | - PMC_PHW_CPU_TO_STATE(cpu) | PMC_PHW_INDEX_TO_STATE(i); - phw->phw_pmc = NULL; - pc->pc_hwpmcs[i + first_ri] = phw; - } - - /* Clear the MMCRs, and set FC, to disable all PMCs. */ - mtspr(SPR_MMCR0, SPR_MMCR0_FC | SPR_MMCR0_PMXE | SPR_MMCR0_PMC1CE | SPR_MMCR0_PMCNCE); - mtspr(SPR_MMCR1, 0); - - return 0; -} - -static int -powerpc_pcpu_fini(struct pmc_mdep *md, int cpu) -{ - uint32_t mmcr0 = mfspr(SPR_MMCR0); - - mmcr0 |= SPR_MMCR0_FC; - mtspr(SPR_MMCR0, mmcr0); - free(powerpc_pcpu[cpu]->pc_ppcpmcs, M_PMC); - free(powerpc_pcpu[cpu], M_PMC); - return 0; + return (0); } struct pmc_mdep * pmc_md_initialize() { struct pmc_mdep *pmc_mdep; - struct pmc_classdep *pcd; + int error; + uint16_t vers; /* * Allocate space for pointers to PMC HW descriptors and for @@ -807,30 +133,31 @@ pmc_md_initialize() pmc_mdep->pmd_cputype = PMC_CPU_PPC_7450; - pcd = &pmc_mdep->pmd_classdep[PMC_MDEP_CLASS_INDEX_PPC7450]; - pcd->pcd_caps = POWERPC_PMC_CAPS; - pcd->pcd_class = PMC_CLASS_PPC7450; - pcd->pcd_num = PPC_MAX_PMCS; - pcd->pcd_ri = pmc_mdep->pmd_npmc; - pcd->pcd_width = 32; /* All PMCs, even in ppc970, are 32-bit */ + vers = mfpvr() >> 16; - pcd->pcd_allocate_pmc = powerpc_allocate_pmc; - pcd->pcd_config_pmc = powerpc_config_pmc; - pcd->pcd_pcpu_fini = powerpc_pcpu_fini; - pcd->pcd_pcpu_init = powerpc_pcpu_init; - pcd->pcd_describe = powerpc_describe; - pcd->pcd_get_config = powerpc_get_config; - pcd->pcd_read_pmc = powerpc_read_pmc; - pcd->pcd_release_pmc = powerpc_release_pmc; - pcd->pcd_start_pmc = powerpc_start_pmc; - pcd->pcd_stop_pmc = powerpc_stop_pmc; - pcd->pcd_write_pmc = powerpc_write_pmc; - - pmc_mdep->pmd_intr = powerpc_intr; pmc_mdep->pmd_switch_in = powerpc_switch_in; pmc_mdep->pmd_switch_out = powerpc_switch_out; - pmc_mdep->pmd_npmc += PPC_MAX_PMCS; + switch (vers) { + case MPC7447A: + case MPC7448: + case MPC7450: + case MPC7455: + case MPC7457: + error = pmc_mpc7xxx_initialize(pmc_mdep); + case IBM970: + case IBM970FX: + case IBM970MP: + default: + error = -1; + break; + } + + if (error != 0) { + pmc_mdep_free(pmc_mdep); + pmc_mdep = NULL; + return NULL; + } return (pmc_mdep); } diff --git a/sys/dev/hwpmc/hwpmc_powerpc.h b/sys/dev/hwpmc/hwpmc_powerpc.h new file mode 100644 index 00000000000..a9b54f4ce61 --- /dev/null +++ b/sys/dev/hwpmc/hwpmc_powerpc.h @@ -0,0 +1,59 @@ +/*- + * Copyright (c) 2013 Justin Hibbits + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + * $FreeBSD$ + */ + +#ifndef _DEV_HWPMC_POWERPC_H_ +#define _DEV_HWPMC_POWERPC_H_ 1 + +#ifdef _KERNEL + +#define POWERPC_PMC_CAPS (PMC_CAP_INTERRUPT | PMC_CAP_USER | \ + PMC_CAP_SYSTEM | PMC_CAP_EDGE | \ + PMC_CAP_THRESHOLD | PMC_CAP_READ | \ + PMC_CAP_WRITE | PMC_CAP_INVERT | \ + PMC_CAP_QUALIFIER) + +#define POWERPC_PMC_KERNEL_ENABLE (0x1 << 30) +#define POWERPC_PMC_USER_ENABLE (0x1 << 31) + +#define POWERPC_PMC_ENABLE (POWERPC_PMC_KERNEL_ENABLE | POWERPC_PMC_USER_ENABLE) +#define POWERPC_RELOAD_COUNT_TO_PERFCTR_VALUE(V) (0x80000000-(V)) +#define POWERPC_PERFCTR_VALUE_TO_RELOAD_COUNT(P) (0x80000000-(P)) + +struct powerpc_cpu { + struct pmc_hw *pc_ppcpmcs; +}; + +extern struct powerpc_cpu **powerpc_pcpu; + +extern int pmc_mpc7xxx_initialize(struct pmc_mdep *pmc_mdep); + +extern int powerpc_describe(int cpu, int ri, struct pmc_info *pi, struct pmc **ppmc); +extern int powerpc_get_config(int cpu, int ri, struct pmc **ppm); +#endif /* _KERNEL */ + +#endif /* _DEV_HWPMC_POWERPC_H_ */ diff --git a/sys/dev/hwpmc/pmc_events.h b/sys/dev/hwpmc/pmc_events.h index b8c97c65b0e..ac933fee615 100644 --- a/sys/dev/hwpmc/pmc_events.h +++ b/sys/dev/hwpmc/pmc_events.h @@ -1053,9 +1053,16 @@ __PMC_EV(IAP, EVENT_D0H_00H) \ __PMC_EV(IAP, EVENT_D0H_01H) \ __PMC_EV(IAP, EVENT_D0H_02H) \ __PMC_EV(IAP, EVENT_D0H_10H) \ +__PMC_EV(IAP, EVENT_D0H_11H) \ +__PMC_EV(IAP, EVENT_D0H_12H) \ __PMC_EV(IAP, EVENT_D0H_20H) \ +__PMC_EV(IAP, EVENT_D0H_21H) \ __PMC_EV(IAP, EVENT_D0H_40H) \ +__PMC_EV(IAP, EVENT_D0H_41H) \ +__PMC_EV(IAP, EVENT_D0H_42H) \ __PMC_EV(IAP, EVENT_D0H_80H) \ +__PMC_EV(IAP, EVENT_D0H_81H) \ +__PMC_EV(IAP, EVENT_D0H_82H) \ __PMC_EV(IAP, EVENT_D1H_01H) \ __PMC_EV(IAP, EVENT_D1H_02H) \ __PMC_EV(IAP, EVENT_D1H_04H) \ @@ -2656,6 +2663,8 @@ __PMC_EV_ALIAS("MEM_LOAD_UOPS_LLC_HIT_RETIRED.XSNP_HITM", \ IAP_EVENT_D2H_04H) \ __PMC_EV_ALIAS("MEM_LOAD_UOPS_LLC_HIT_RETIRED.XSNP_NONE", \ IAP_EVENT_D2H_08H) \ +__PMC_EV_ALIAS("MEM_LOAD_UOPS_LLC_HIT_RETIRED.ALL", \ + IAP_EVENT_D2H_0FH) \ __PMC_EV_ALIAS("MEM_LOAD_UOPS_LLC_MISS_RETIRED.LOCAL_DRAM", \ IAP_EVENT_D3H_01H) \ __PMC_EV_ALIAS("BACLEARS.ANY", IAP_EVENT_E6H_1FH) \ @@ -2859,6 +2868,8 @@ __PMC_EV_ALIAS("MEM_LOAD_UOPS_LLC_HIT_RETIRED.XSNP_HITM", \ IAP_EVENT_D2H_04H) \ __PMC_EV_ALIAS("MEM_LOAD_UOPS_LLC_HIT_RETIRED.XSNP_NONE", \ IAP_EVENT_D2H_08H) \ +__PMC_EV_ALIAS("MEM_LOAD_UOPS_LLC_HIT_RETIRED.ALL", \ + IAP_EVENT_D2H_0FH) \ __PMC_EV_ALIAS("MEM_LOAD_UOPS_LLC_MISS_RETIRED.LOCAL_DRAM", \ IAP_EVENT_D3H_01H) \ __PMC_EV_ALIAS("L2_TRANS.DEMAND_DATA_RD", IAP_EVENT_F0H_01H) \ @@ -3181,7 +3192,7 @@ __PMC_EV_ALIAS("LOCK_CYCLES.SPLIT_LOCK_UC_LOCK_DURATION", \ IAP_EVENT_63H_01H) \ __PMC_EV_ALIAS("LOCK_CYCLES.CACHE_LOCK_DURATION", IAP_EVENT_63H_02H) \ __PMC_EV_ALIAS("IDQ.EMPTY", IAP_EVENT_79H_02H) \ -__PMC_EV_ALIAS("IQD.MITE_UOPS", IAP_EVENT_79H_04H) \ +__PMC_EV_ALIAS("IDQ.MITE_UOPS", IAP_EVENT_79H_04H) \ __PMC_EV_ALIAS("IDQ.DSB_UOPS", IAP_EVENT_79H_08H) \ __PMC_EV_ALIAS("IDQ.MS_DSB_UOPS", IAP_EVENT_79H_10H) \ __PMC_EV_ALIAS("IDQ.MS_MITE_UOPS", IAP_EVENT_79H_20H) \ @@ -3271,7 +3282,7 @@ __PMC_EV_ALIAS("BR_INST_RETIRED.FAR_BRANCH", IAP_EVENT_C4H_40H) \ __PMC_EV_ALIAS("BR_MISP_RETIRED.ALL_BRANCHES", IAP_EVENT_C5H_00H) \ __PMC_EV_ALIAS("BR_MISP_RETIRED.CONDITIONAL", IAP_EVENT_C5H_01H) \ __PMC_EV_ALIAS("BR_MISP_RETIRED.NEAR_CALL", IAP_EVENT_C5H_02H) \ -__PMC_EV_ALIAS("BR_MISP_RETIRED.ALL_BRANCHES", IAP_EVENT_C5H_04H) \ +__PMC_EV_ALIAS("BR_MISP_RETIRED.ALL_BRANCHES_PS", IAP_EVENT_C5H_04H) \ __PMC_EV_ALIAS("BR_MISP_RETIRED.NOT_TAKEN", IAP_EVENT_C5H_10H) \ __PMC_EV_ALIAS("BR_MISP_RETIRED.TAKEN", IAP_EVENT_C5H_20H) \ __PMC_EV_ALIAS("FP_ASSIST.X87_OUTPUT", IAP_EVENT_CAH_02H) \ @@ -3282,12 +3293,13 @@ __PMC_EV_ALIAS("FP_ASSIST.ANY", IAP_EVENT_CAH_1EH) \ __PMC_EV_ALIAS("ROB_MISC_EVENTS.LBR_INSERTS", IAP_EVENT_CCH_20H) \ __PMC_EV_ALIAS("MEM_TRANS_RETIRED.LOAD_LATENCY", IAP_EVENT_CDH_01H) \ __PMC_EV_ALIAS("MEM_TRANS_RETIRED.PRECISE_STORE", IAP_EVENT_CDH_02H) \ -__PMC_EV_ALIAS("MEM_UOP_RETIRED.LOADS", IAP_EVENT_D0H_01H) \ -__PMC_EV_ALIAS("MEM_UOP_RETIRED.STORES", IAP_EVENT_D0H_02H) \ -__PMC_EV_ALIAS("MEM_UOP_RETIRED.STLB_MISS", IAP_EVENT_D0H_10H) \ -__PMC_EV_ALIAS("MEM_UOP_RETIRED.LOCK", IAP_EVENT_D0H_20H) \ -__PMC_EV_ALIAS("MEM_UOP_RETIRED.SPLIT", IAP_EVENT_D0H_40H) \ -__PMC_EV_ALIAS("MEM_UOP_RETIRED_ALL", IAP_EVENT_D0H_80H) \ +__PMC_EV_ALIAS("MEM_UOP_RETIRED.STLB_MISS_LOADS", IAP_EVENT_D0H_11H) \ +__PMC_EV_ALIAS("MEM_UOP_RETIRED.STLB_MISS_STORES", IAP_EVENT_D0H_12H) \ +__PMC_EV_ALIAS("MEM_UOP_RETIRED.LOCK_LOADS", IAP_EVENT_D0H_21H) \ +__PMC_EV_ALIAS("MEM_UOP_RETIRED.SPLIT_LOADS", IAP_EVENT_D0H_41H) \ +__PMC_EV_ALIAS("MEM_UOP_RETIRED.SPLIT_STORES", IAP_EVENT_D0H_42H) \ +__PMC_EV_ALIAS("MEM_UOP_RETIRED.ALL_LOADS", IAP_EVENT_D0H_81H) \ +__PMC_EV_ALIAS("MEM_UOP_RETIRED.ALL_STORES", IAP_EVENT_D0H_82H) \ __PMC_EV_ALIAS("MEM_LOAD_UOPS_RETIRED.L1_HIT", IAP_EVENT_D1H_01H) \ __PMC_EV_ALIAS("MEM_LOAD_UOPS_RETIRED.L2_HIT", IAP_EVENT_D1H_02H) \ __PMC_EV_ALIAS("MEM_LOAD_UOPS_RETIRED.LLC_HIT", IAP_EVENT_D1H_04H) \ @@ -3300,7 +3312,7 @@ __PMC_EV_ALIAS("MEM_LOAD_UOPS_LLC_HIT_RETIRED.XSNP_HITM", \ IAP_EVENT_D2H_04H) \ __PMC_EV_ALIAS("MEM_LOAD_UOPS_LLC_HIT_RETIRED.XSNP_NONE", \ IAP_EVENT_D2H_08H) \ -__PMC_EV_ALIAS("MEM_LOAD_UOPS_LLC_HIT_RETIRED.LLC_MISS", \ +__PMC_EV_ALIAS("MEM_LOAD_UOPS_MISC_RETIRED.LLC_MISS", \ IAP_EVENT_D4H_02H) \ __PMC_EV_ALIAS("L2_TRANS.DEMAND_DATA_RD", IAP_EVENT_F0H_01H) \ __PMC_EV_ALIAS("L2_TRANS.RFO", IAP_EVENT_F0H_02H) \ @@ -3502,7 +3514,7 @@ __PMC_EV_ALIAS("BR_INST_RETIRED.FAR_BRANCH", IAP_EVENT_C4H_40H) \ __PMC_EV_ALIAS("BR_MISP_RETIRED.ALL_BRANCHES", IAP_EVENT_C5H_00H) \ __PMC_EV_ALIAS("BR_MISP_RETIRED.CONDITIONAL", IAP_EVENT_C5H_01H) \ __PMC_EV_ALIAS("BR_MISP_RETIRED.NEAR_CALL", IAP_EVENT_C5H_02H) \ -__PMC_EV_ALIAS("BR_MISP_RETIRED.ALL_BRANCHES", IAP_EVENT_C5H_04H) \ +__PMC_EV_ALIAS("BR_MISP_RETIRED.ALL_BRANCHES_PS", IAP_EVENT_C5H_04H) \ __PMC_EV_ALIAS("BR_MISP_RETIRED.NOT_TAKEN", IAP_EVENT_C5H_10H) \ __PMC_EV_ALIAS("BR_MISP_RETIRED.TAKEN", IAP_EVENT_C5H_20H) \ __PMC_EV_ALIAS("FP_ASSIST.X87_OUTPUT", IAP_EVENT_CAH_02H) \ @@ -3513,22 +3525,34 @@ __PMC_EV_ALIAS("FP_ASSIST.ANY", IAP_EVENT_CAH_1EH) \ __PMC_EV_ALIAS("ROB_MISC_EVENTS.LBR_INSERTS", IAP_EVENT_CCH_20H) \ __PMC_EV_ALIAS("MEM_TRANS_RETIRED.LOAD_LATENCY", IAP_EVENT_CDH_01H) \ __PMC_EV_ALIAS("MEM_TRANS_RETIRED.PRECISE_STORE", IAP_EVENT_CDH_02H) \ -__PMC_EV_ALIAS("MEM_UOP_RETIRED.LOADS", IAP_EVENT_D0H_01H) \ -__PMC_EV_ALIAS("MEM_UOP_RETIRED.STORES", IAP_EVENT_D0H_02H) \ -__PMC_EV_ALIAS("MEM_UOP_RETIRED.STLB_MISS", IAP_EVENT_D0H_10H) \ -__PMC_EV_ALIAS("MEM_UOP_RETIRED.LOCK", IAP_EVENT_D0H_20H) \ -__PMC_EV_ALIAS("MEM_UOP_RETIRED.SPLIT", IAP_EVENT_D0H_40H) \ -__PMC_EV_ALIAS("MEM_UOP_RETIRED_ALL", IAP_EVENT_D0H_80H) \ +__PMC_EV_ALIAS("MEM_UOP_RETIRED.STLB_MISS_LOADS", IAP_EVENT_D0H_11H) \ +__PMC_EV_ALIAS("MEM_UOP_RETIRED.STLB_MISS_STORES", IAP_EVENT_D0H_12H) \ +__PMC_EV_ALIAS("MEM_UOP_RETIRED.LOCK_LOADS", IAP_EVENT_D0H_21H) \ +__PMC_EV_ALIAS("MEM_UOP_RETIRED.SPLIT_LOADS", IAP_EVENT_D0H_41H) \ +__PMC_EV_ALIAS("MEM_UOP_RETIRED.SPLIT_STORES", IAP_EVENT_D0H_42H) \ +__PMC_EV_ALIAS("MEM_UOP_RETIRED.ALL_LOADS", IAP_EVENT_D0H_81H) \ +__PMC_EV_ALIAS("MEM_UOP_RETIRED.ALL_STORES", IAP_EVENT_D0H_82H) \ __PMC_EV_ALIAS("MEM_LOAD_UOPS_RETIRED.L1_HIT", IAP_EVENT_D1H_01H) \ __PMC_EV_ALIAS("MEM_LOAD_UOPS_RETIRED.L2_HIT", IAP_EVENT_D1H_02H) \ __PMC_EV_ALIAS("MEM_LOAD_UOPS_RETIRED.LLC_HIT", IAP_EVENT_D1H_04H) \ __PMC_EV_ALIAS("MEM_LOAD_UOPS_RETIRED.LLC_MISS", IAP_EVENT_D1H_20H) \ __PMC_EV_ALIAS("MEM_LOAD_UOPS_RETIRED.HIT_LFB", IAP_EVENT_D1H_40H) \ +__PMC_EV_ALIAS("MEM_LOAD_UOPS_LLC_HIT_RETIRED.XSNP_MISS", \ + IAP_EVENT_D2H_01H) \ +__PMC_EV_ALIAS("MEM_LOAD_UOPS_LLC_HIT_RETIRED.XSNP_HIT", \ + IAP_EVENT_D2H_02H) \ +__PMC_EV_ALIAS("MEM_LOAD_UOPS_LLC_HIT_RETIRED.XSNP_HITM", \ + IAP_EVENT_D2H_04H) \ +__PMC_EV_ALIAS("MEM_LOAD_UOPS_LLC_HIT_RETIRED.XSNP_NONE", \ + IAP_EVENT_D2H_08H) \ +__PMC_EV_ALIAS("MEM_LOAD_UOPS_LLC_HIT_RETIRED.ALL", \ + IAP_EVENT_D2H_0FH) \ __PMC_EV_ALIAS("MEM_LOAD_UOPS_LLC_MISS_RETIRED.LOCAL_DRAM", \ IAP_EVENT_D3H_01H) \ __PMC_EV_ALIAS("MEM_LOAD_UOPS_LLC_MISS_RETIRED.REMOTE_DRAM", \ IAP_EVENT_D3H_04H) \ -__PMC_EV_ALIAS("MEM_LOAD_UOPS_MISC_RETIRED.LLC_MISS", IAP_EVENT_D4H_02H)\ +__PMC_EV_ALIAS("MEM_LOAD_UOPS_MISC_RETIRED.LLC_MISS", \ + IAP_EVENT_D4H_02H) \ __PMC_EV_ALIAS("BACLEARS.ANY", IAP_EVENT_E6H_01H) \ __PMC_EV_ALIAS("L2_TRANS.DEMAND_DATA_RD", IAP_EVENT_F0H_01H) \ __PMC_EV_ALIAS("L2_TRANS.RFO", IAP_EVENT_F0H_02H) \ diff --git a/sys/dev/hyperv/include/hyperv.h b/sys/dev/hyperv/include/hyperv.h new file mode 100644 index 00000000000..36512698fa7 --- /dev/null +++ b/sys/dev/hyperv/include/hyperv.h @@ -0,0 +1,796 @@ +/*- + * Copyright (c) 2009-2012 Microsoft Corp. + * Copyright (c) 2012 NetApp Inc. + * Copyright (c) 2012 Citrix Inc. + * 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 unmodified, 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +/** + * HyperV definitions for messages that are sent between instances of the + * Channel Management Library in separate partitions, or in some cases, + * back to itself. + */ + +#ifndef __HYPERV_H__ +#define __HYPERV_H__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +typedef uint8_t hv_bool_uint8_t; + +#define HV_S_OK 0x00000000 +#define HV_E_FAIL 0x80004005 +#define HV_ERROR_NOT_SUPPORTED 0x80070032 +#define HV_ERROR_MACHINE_LOCKED 0x800704F7 + +/* + * A revision number of vmbus that is used for ensuring both ends on a + * partition are using compatible versions. + */ + +#define HV_VMBUS_REVISION_NUMBER 13 + +/* + * Make maximum size of pipe payload of 16K + */ + +#define HV_MAX_PIPE_DATA_PAYLOAD (sizeof(BYTE) * 16384) + +/* + * Define pipe_mode values + */ + +#define HV_VMBUS_PIPE_TYPE_BYTE 0x00000000 +#define HV_VMBUS_PIPE_TYPE_MESSAGE 0x00000004 + +/* + * The size of the user defined data buffer for non-pipe offers + */ + +#define HV_MAX_USER_DEFINED_BYTES 120 + +/* + * The size of the user defined data buffer for pipe offers + */ + +#define HV_MAX_PIPE_USER_DEFINED_BYTES 116 + + +#define HV_MAX_PAGE_BUFFER_COUNT 16 +#define HV_MAX_MULTIPAGE_BUFFER_COUNT 32 + +#define HV_ALIGN_UP(value, align) \ + (((value) & (align-1)) ? \ + (((value) + (align-1)) & ~(align-1) ) : (value)) + +#define HV_ALIGN_DOWN(value, align) ( (value) & ~(align-1) ) + +#define HV_NUM_PAGES_SPANNED(addr, len) \ + ((HV_ALIGN_UP(addr+len, PAGE_SIZE) - \ + HV_ALIGN_DOWN(addr, PAGE_SIZE)) >> PAGE_SHIFT ) + +typedef struct hv_guid { + unsigned char data[16]; +} __packed hv_guid; + +/* + * At the center of the Channel Management library is + * the Channel Offer. This struct contains the + * fundamental information about an offer. + */ + +typedef struct hv_vmbus_channel_offer { + hv_guid interface_type; + hv_guid interface_instance; + uint64_t interrupt_latency_in_100ns_units; + uint32_t interface_revision; + uint32_t server_context_area_size; /* in bytes */ + uint16_t channel_flags; + uint16_t mmio_megabytes; /* in bytes * 1024 * 1024 */ + union + { + /* + * Non-pipes: The user has HV_MAX_USER_DEFINED_BYTES bytes. + */ + struct { + uint8_t user_defined[HV_MAX_USER_DEFINED_BYTES]; + } __packed standard; + + /* + * Pipes: The following structure is an integrated pipe protocol, which + * is implemented on top of standard user-defined data. pipe + * clients have HV_MAX_PIPE_USER_DEFINED_BYTES left for their + * own use. + */ + struct { + uint32_t pipe_mode; + uint8_t user_defined[HV_MAX_PIPE_USER_DEFINED_BYTES]; + } __packed pipe; + } u; + + uint32_t padding; + +} __packed hv_vmbus_channel_offer; + +typedef uint32_t hv_gpadl_handle; + +typedef struct { + uint16_t type; + uint16_t data_offset8; + uint16_t length8; + uint16_t flags; + uint64_t transaction_id; +} __packed hv_vm_packet_descriptor; + +typedef uint32_t hv_previous_packet_offset; + +typedef struct { + hv_previous_packet_offset previous_packet_start_offset; + hv_vm_packet_descriptor descriptor; +} __packed hv_vm_packet_header; + +typedef struct { + uint32_t byte_count; + uint32_t byte_offset; +} __packed hv_vm_transfer_page; + +typedef struct { + hv_vm_packet_descriptor d; + uint16_t transfer_page_set_id; + hv_bool_uint8_t sender_owns_set; + uint8_t reserved; + uint32_t range_count; + hv_vm_transfer_page ranges[1]; +} __packed hv_vm_transfer_page_packet_header; + +typedef struct { + hv_vm_packet_descriptor d; + uint32_t gpadl; + uint32_t reserved; +} __packed hv_vm_gpadl_packet_header; + +typedef struct { + hv_vm_packet_descriptor d; + uint32_t gpadl; + uint16_t transfer_page_set_id; + uint16_t reserved; +} __packed hv_vm_add_remove_transfer_page_set; + +/* + * This structure defines a range in guest + * physical space that can be made + * to look virtually contiguous. + */ + +typedef struct { + uint32_t byte_count; + uint32_t byte_offset; + uint64_t pfn_array[0]; +} __packed hv_gpa_range; + +/* + * This is the format for an Establish Gpadl packet, which contains a handle + * by which this GPADL will be known and a set of GPA ranges associated with + * it. This can be converted to a MDL by the guest OS. If there are multiple + * GPA ranges, then the resulting MDL will be "chained," representing multiple + * VA ranges. + */ + +typedef struct { + hv_vm_packet_descriptor d; + uint32_t gpadl; + uint32_t range_count; + hv_gpa_range range[1]; +} __packed hv_vm_establish_gpadl; + +/* + * This is the format for a Teardown Gpadl packet, which indicates that the + * GPADL handle in the Establish Gpadl packet will never be referenced again. + */ + +typedef struct { + hv_vm_packet_descriptor d; + uint32_t gpadl; + /* for alignment to a 8-byte boundary */ + uint32_t reserved; +} __packed hv_vm_teardown_gpadl; + +/* + * This is the format for a GPA-Direct packet, which contains a set of GPA + * ranges, in addition to commands and/or data. + */ + +typedef struct { + hv_vm_packet_descriptor d; + uint32_t reserved; + uint32_t range_count; + hv_gpa_range range[1]; +} __packed hv_vm_data_gpa_direct; + +/* + * This is the format for a Additional data Packet. + */ +typedef struct { + hv_vm_packet_descriptor d; + uint64_t total_bytes; + uint32_t byte_offset; + uint32_t byte_count; + uint8_t data[1]; +} __packed hv_vm_additional_data; + +typedef union { + hv_vm_packet_descriptor simple_header; + hv_vm_transfer_page_packet_header transfer_page_header; + hv_vm_gpadl_packet_header gpadl_header; + hv_vm_add_remove_transfer_page_set add_remove_transfer_page_header; + hv_vm_establish_gpadl establish_gpadl_header; + hv_vm_teardown_gpadl teardown_gpadl_header; + hv_vm_data_gpa_direct data_gpa_direct_header; +} __packed hv_vm_packet_largest_possible_header; + +typedef enum { + HV_VMBUS_PACKET_TYPE_INVALID = 0x0, + HV_VMBUS_PACKET_TYPES_SYNCH = 0x1, + HV_VMBUS_PACKET_TYPE_ADD_TRANSFER_PAGE_SET = 0x2, + HV_VMBUS_PACKET_TYPE_REMOVE_TRANSFER_PAGE_SET = 0x3, + HV_VMBUS_PACKET_TYPE_ESTABLISH_GPADL = 0x4, + HV_VMBUS_PACKET_TYPE_TEAR_DOWN_GPADL = 0x5, + HV_VMBUS_PACKET_TYPE_DATA_IN_BAND = 0x6, + HV_VMBUS_PACKET_TYPE_DATA_USING_TRANSFER_PAGES = 0x7, + HV_VMBUS_PACKET_TYPE_DATA_USING_GPADL = 0x8, + HV_VMBUS_PACKET_TYPE_DATA_USING_GPA_DIRECT = 0x9, + HV_VMBUS_PACKET_TYPE_CANCEL_REQUEST = 0xa, + HV_VMBUS_PACKET_TYPE_COMPLETION = 0xb, + HV_VMBUS_PACKET_TYPE_DATA_USING_ADDITIONAL_PACKETS = 0xc, + HV_VMBUS_PACKET_TYPE_ADDITIONAL_DATA = 0xd +} hv_vmbus_packet_type; + +#define HV_VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED 1 + +/* + * Version 1 messages + */ +typedef enum { + HV_CHANNEL_MESSAGE_INVALID = 0, + HV_CHANNEL_MESSAGE_OFFER_CHANNEL = 1, + HV_CHANNEL_MESSAGE_RESCIND_CHANNEL_OFFER = 2, + HV_CHANNEL_MESSAGE_REQUEST_OFFERS = 3, + HV_CHANNEL_MESSAGE_ALL_OFFERS_DELIVERED = 4, + HV_CHANNEL_MESSAGE_OPEN_CHANNEL = 5, + HV_CHANNEL_MESSAGE_OPEN_CHANNEL_RESULT = 6, + HV_CHANNEL_MESSAGE_CLOSE_CHANNEL = 7, + HV_CHANNEL_MESSAGEL_GPADL_HEADER = 8, + HV_CHANNEL_MESSAGE_GPADL_BODY = 9, + HV_CHANNEL_MESSAGE_GPADL_CREATED = 10, + HV_CHANNEL_MESSAGE_GPADL_TEARDOWN = 11, + HV_CHANNEL_MESSAGE_GPADL_TORNDOWN = 12, + HV_CHANNEL_MESSAGE_REL_ID_RELEASED = 13, + HV_CHANNEL_MESSAGE_INITIATED_CONTACT = 14, + HV_CHANNEL_MESSAGE_VERSION_RESPONSE = 15, + HV_CHANNEL_MESSAGE_UNLOAD = 16, + +#ifdef HV_VMBUS_FEATURE_PARENT_OR_PEER_MEMORY_MAPPED_INTO_A_CHILD + HV_CHANNEL_MESSAGE_VIEW_RANGE_ADD = 17, + HV_CHANNEL_MESSAGE_VIEW_RANGE_REMOVE = 18, +#endif + HV_CHANNEL_MESSAGE_COUNT +} hv_vmbus_channel_msg_type; + +typedef struct { + hv_vmbus_channel_msg_type message_type; + uint32_t padding; +} __packed hv_vmbus_channel_msg_header; + +/* + * Query VMBus Version parameters + */ +typedef struct { + hv_vmbus_channel_msg_header header; + uint32_t version; +} __packed hv_vmbus_channel_query_vmbus_version; + +/* + * VMBus Version Supported parameters + */ +typedef struct { + hv_vmbus_channel_msg_header header; + hv_bool_uint8_t version_supported; +} __packed hv_vmbus_channel_version_supported; + +/* + * Channel Offer parameters + */ +typedef struct { + hv_vmbus_channel_msg_header header; + hv_vmbus_channel_offer offer; + uint32_t child_rel_id; + uint8_t monitor_id; + hv_bool_uint8_t monitor_allocated; +} __packed hv_vmbus_channel_offer_channel; + +/* + * Rescind Offer parameters + */ +typedef struct +{ + hv_vmbus_channel_msg_header header; + uint32_t child_rel_id; +} __packed hv_vmbus_channel_rescind_offer; + + +/* + * Request Offer -- no parameters, SynIC message contains the partition ID + * + * Set Snoop -- no parameters, SynIC message contains the partition ID + * + * Clear Snoop -- no parameters, SynIC message contains the partition ID + * + * All Offers Delivered -- no parameters, SynIC message contains the + * partition ID + * + * Flush Client -- no parameters, SynIC message contains the partition ID + */ + + +/* + * Open Channel parameters + */ +typedef struct +{ + hv_vmbus_channel_msg_header header; + + /* + * Identifies the specific VMBus channel that is being opened. + */ + uint32_t child_rel_id; + + /* + * ID making a particular open request at a channel offer unique. + */ + uint32_t open_id; + + /* + * GPADL for the channel's ring buffer. + */ + hv_gpadl_handle ring_buffer_gpadl_handle; + + /* + * GPADL for the channel's server context save area. + */ + hv_gpadl_handle server_context_area_gpadl_handle; + + /* + * The upstream ring buffer begins at offset zero in the memory described + * by ring_buffer_gpadl_handle. The downstream ring buffer follows it at + * this offset (in pages). + */ + uint32_t downstream_ring_buffer_page_offset; + + /* + * User-specific data to be passed along to the server endpoint. + */ + uint8_t user_data[HV_MAX_USER_DEFINED_BYTES]; + +} __packed hv_vmbus_channel_open_channel; + +typedef uint32_t hv_nt_status; + +/* + * Open Channel Result parameters + */ +typedef struct +{ + hv_vmbus_channel_msg_header header; + uint32_t child_rel_id; + uint32_t open_id; + hv_nt_status status; +} __packed hv_vmbus_channel_open_result; + +/* + * Close channel parameters + */ +typedef struct +{ + hv_vmbus_channel_msg_header header; + uint32_t child_rel_id; +} __packed hv_vmbus_channel_close_channel; + +/* + * Channel Message GPADL + */ +#define HV_GPADL_TYPE_RING_BUFFER 1 +#define HV_GPADL_TYPE_SERVER_SAVE_AREA 2 +#define HV_GPADL_TYPE_TRANSACTION 8 + +/* + * The number of PFNs in a GPADL message is defined by the number of pages + * that would be spanned by byte_count and byte_offset. If the implied number + * of PFNs won't fit in this packet, there will be a follow-up packet that + * contains more + */ + +typedef struct { + hv_vmbus_channel_msg_header header; + uint32_t child_rel_id; + uint32_t gpadl; + uint16_t range_buf_len; + uint16_t range_count; + hv_gpa_range range[0]; +} __packed hv_vmbus_channel_gpadl_header; + +/* + * This is the follow-up packet that contains more PFNs + */ +typedef struct { + hv_vmbus_channel_msg_header header; + uint32_t message_number; + uint32_t gpadl; + uint64_t pfn[0]; +} __packed hv_vmbus_channel_gpadl_body; + +typedef struct { + hv_vmbus_channel_msg_header header; + uint32_t child_rel_id; + uint32_t gpadl; + uint32_t creation_status; +} __packed hv_vmbus_channel_gpadl_created; + +typedef struct { + hv_vmbus_channel_msg_header header; + uint32_t child_rel_id; + uint32_t gpadl; +} __packed hv_vmbus_channel_gpadl_teardown; + +typedef struct { + hv_vmbus_channel_msg_header header; + uint32_t gpadl; +} __packed hv_vmbus_channel_gpadl_torndown; + +typedef struct { + hv_vmbus_channel_msg_header header; + uint32_t child_rel_id; +} __packed hv_vmbus_channel_relid_released; + +typedef struct { + hv_vmbus_channel_msg_header header; + uint32_t vmbus_version_requested; + uint32_t padding2; + uint64_t interrupt_page; + uint64_t monitor_page_1; + uint64_t monitor_page_2; +} __packed hv_vmbus_channel_initiate_contact; + +typedef struct { + hv_vmbus_channel_msg_header header; + hv_bool_uint8_t version_supported; +} __packed hv_vmbus_channel_version_response; + +typedef hv_vmbus_channel_msg_header hv_vmbus_channel_unload; + +#define HW_MACADDR_LEN 6 + +/* + * Fixme: Added to quiet "typeof" errors involving hv_vmbus.h when + * the including C file was compiled with "-std=c99". + */ +#ifndef typeof +#define typeof __typeof +#endif + +#ifndef NULL +#define NULL (void *)0 +#endif + +typedef void *hv_vmbus_handle; + +#ifndef CONTAINING_RECORD +#define CONTAINING_RECORD(address, type, field) ((type *)( \ + (uint8_t *)(address) - \ + (uint8_t *)(&((type *)0)->field))) +#endif /* CONTAINING_RECORD */ + + +#define container_of(ptr, type, member) ({ \ + __typeof__( ((type *)0)->member ) *__mptr = (ptr); \ + (type *)( (char *)__mptr - offsetof(type,member) );}) + +enum { + HV_VMBUS_IVAR_TYPE, + HV_VMBUS_IVAR_INSTANCE, + HV_VMBUS_IVAR_NODE, + HV_VMBUS_IVAR_DEVCTX +}; + +#define HV_VMBUS_ACCESSOR(var, ivar, type) \ + __BUS_ACCESSOR(vmbus, var, HV_VMBUS, ivar, type) + +HV_VMBUS_ACCESSOR(type, TYPE, const char *) +HV_VMBUS_ACCESSOR(devctx, DEVCTX, struct hv_device *) + + +/* + * Common defines for Hyper-V ICs + */ +#define HV_ICMSGTYPE_NEGOTIATE 0 +#define HV_ICMSGTYPE_HEARTBEAT 1 +#define HV_ICMSGTYPE_KVPEXCHANGE 2 +#define HV_ICMSGTYPE_SHUTDOWN 3 +#define HV_ICMSGTYPE_TIMESYNC 4 +#define HV_ICMSGTYPE_VSS 5 + +#define HV_ICMSGHDRFLAG_TRANSACTION 1 +#define HV_ICMSGHDRFLAG_REQUEST 2 +#define HV_ICMSGHDRFLAG_RESPONSE 4 + +typedef struct hv_vmbus_pipe_hdr { + uint32_t flags; + uint32_t msgsize; +} __packed hv_vmbus_pipe_hdr; + +typedef struct hv_vmbus_ic_version { + uint16_t major; + uint16_t minor; +} __packed hv_vmbus_ic_version; + +typedef struct hv_vmbus_icmsg_hdr { + hv_vmbus_ic_version icverframe; + uint16_t icmsgtype; + hv_vmbus_ic_version icvermsg; + uint16_t icmsgsize; + uint32_t status; + uint8_t ictransaction_id; + uint8_t icflags; + uint8_t reserved[2]; +} __packed hv_vmbus_icmsg_hdr; + +typedef struct hv_vmbus_icmsg_negotiate { + uint16_t icframe_vercnt; + uint16_t icmsg_vercnt; + uint32_t reserved; + hv_vmbus_ic_version icversion_data[1]; /* any size array */ +} __packed hv_vmbus_icmsg_negotiate; + +typedef struct hv_vmbus_shutdown_msg_data { + uint32_t reason_code; + uint32_t timeout_seconds; + uint32_t flags; + uint8_t display_message[2048]; +} __packed hv_vmbus_shutdown_msg_data; + +typedef struct hv_vmbus_heartbeat_msg_data { + uint64_t seq_num; + uint32_t reserved[8]; +} __packed hv_vmbus_heartbeat_msg_data; + +typedef struct { + /* + * offset in bytes from the start of ring data below + */ + volatile uint32_t write_index; + /* + * offset in bytes from the start of ring data below + */ + volatile uint32_t read_index; + /* + * NOTE: The interrupt_mask field is used only for channels, but + * vmbus connection also uses this data structure + */ + volatile uint32_t interrupt_mask; + /* pad it to PAGE_SIZE so that data starts on a page */ + uint8_t reserved[4084]; + + /* + * WARNING: Ring data starts here + ring_data_start_offset + * !!! DO NOT place any fields below this !!! + */ + uint8_t buffer[0]; /* doubles as interrupt mask */ +} __packed hv_vmbus_ring_buffer; + +typedef struct { + int length; + int offset; + uint64_t pfn; +} __packed hv_vmbus_page_buffer; + +typedef struct { + int length; + int offset; + uint64_t pfn_array[HV_MAX_MULTIPAGE_BUFFER_COUNT]; +} __packed hv_vmbus_multipage_buffer; + +typedef struct { + hv_vmbus_ring_buffer* ring_buffer; + uint32_t ring_size; /* Include the shared header */ + struct mtx ring_lock; + uint32_t ring_data_size; /* ring_size */ + uint32_t ring_data_start_offset; +} hv_vmbus_ring_buffer_info; + +typedef void (*hv_vmbus_pfn_channel_callback)(void *context); + +typedef enum { + HV_CHANNEL_OFFER_STATE, + HV_CHANNEL_OPENING_STATE, + HV_CHANNEL_OPEN_STATE, + HV_CHANNEL_CLOSING_NONDESTRUCTIVE_STATE, +} hv_vmbus_channel_state; + +typedef struct hv_vmbus_channel { + TAILQ_ENTRY(hv_vmbus_channel) list_entry; + struct hv_device* device; + hv_vmbus_channel_state state; + hv_vmbus_channel_offer_channel offer_msg; + /* + * These are based on the offer_msg.monitor_id. + * Save it here for easy access. + */ + uint8_t monitor_group; + uint8_t monitor_bit; + + uint32_t ring_buffer_gpadl_handle; + /* + * Allocated memory for ring buffer + */ + void* ring_buffer_pages; + uint32_t ring_buffer_page_count; + /* + * send to parent + */ + hv_vmbus_ring_buffer_info outbound; + /* + * receive from parent + */ + hv_vmbus_ring_buffer_info inbound; + + struct mtx inbound_lock; + hv_vmbus_handle control_work_queue; + + hv_vmbus_pfn_channel_callback on_channel_callback; + void* channel_callback_context; + +} hv_vmbus_channel; + +typedef struct hv_device { + hv_guid class_id; + hv_guid device_id; + device_t device; + hv_vmbus_channel* channel; +} hv_device; + + + +int hv_vmbus_channel_recv_packet( + hv_vmbus_channel* channel, + void* buffer, + uint32_t buffer_len, + uint32_t* buffer_actual_len, + uint64_t* request_id); + +int hv_vmbus_channel_recv_packet_raw( + hv_vmbus_channel* channel, + void* buffer, + uint32_t buffer_len, + uint32_t* buffer_actual_len, + uint64_t* request_id); + +int hv_vmbus_channel_open( + hv_vmbus_channel* channel, + uint32_t send_ring_buffer_size, + uint32_t recv_ring_buffer_size, + void* user_data, + uint32_t user_data_len, + hv_vmbus_pfn_channel_callback + pfn_on_channel_callback, + void* context); + +void hv_vmbus_channel_close(hv_vmbus_channel *channel); + +int hv_vmbus_channel_send_packet( + hv_vmbus_channel* channel, + void* buffer, + uint32_t buffer_len, + uint64_t request_id, + hv_vmbus_packet_type type, + uint32_t flags); + +int hv_vmbus_channel_send_packet_pagebuffer( + hv_vmbus_channel* channel, + hv_vmbus_page_buffer page_buffers[], + uint32_t page_count, + void* buffer, + uint32_t buffer_len, + uint64_t request_id); + +int hv_vmbus_channel_send_packet_multipagebuffer( + hv_vmbus_channel* channel, + hv_vmbus_multipage_buffer* multi_page_buffer, + void* buffer, + uint32_t buffer_len, + uint64_t request_id); + +int hv_vmbus_channel_establish_gpadl( + hv_vmbus_channel* channel, + /* must be phys and virt contiguous */ + void* contig_buffer, + /* page-size multiple */ + uint32_t size, + uint32_t* gpadl_handle); + +int hv_vmbus_channel_teardown_gpdal( + hv_vmbus_channel* channel, + uint32_t gpadl_handle); + +/* + * Work abstraction defines + */ +typedef struct hv_work_queue { + struct taskqueue* queue; + struct proc* proc; + struct sema* work_sema; +} hv_work_queue; + +typedef struct hv_work_item { + struct task work; + void (*callback)(void *); + void* context; + hv_work_queue* wq; +} hv_work_item; + +struct hv_work_queue* hv_work_queue_create(char* name); + +void hv_work_queue_close(struct hv_work_queue* wq); + +int hv_queue_work_item( + hv_work_queue* wq, + void (*callback)(void *), + void* context); +/** + * @brief Get physical address from virtual + */ +static inline unsigned long +hv_get_phys_addr(void *virt) +{ + unsigned long ret; + ret = (vtophys(virt) | ((vm_offset_t) virt & PAGE_MASK)); + return (ret); +} + +#endif /* __HYPERV_H__ */ + diff --git a/sys/dev/hyperv/netvsc/hv_net_vsc.c b/sys/dev/hyperv/netvsc/hv_net_vsc.c new file mode 100644 index 00000000000..aeee94d8345 --- /dev/null +++ b/sys/dev/hyperv/netvsc/hv_net_vsc.c @@ -0,0 +1,1141 @@ +/*- + * Copyright (c) 2009-2012 Microsoft Corp. + * Copyright (c) 2010-2012 Citrix Inc. + * Copyright (c) 2012 NetApp Inc. + * 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 unmodified, 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +/** + * HyperV vmbus network VSC (virtual services client) module + * + */ + + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "hv_net_vsc.h" +#include "hv_rndis.h" +#include "hv_rndis_filter.h" + + +/* + * Forward declarations + */ +static void hv_nv_on_channel_callback(void *context); +static int hv_nv_init_send_buffer_with_net_vsp(struct hv_device *device); +static int hv_nv_init_rx_buffer_with_net_vsp(struct hv_device *device); +static int hv_nv_destroy_send_buffer(netvsc_dev *net_dev); +static int hv_nv_destroy_rx_buffer(netvsc_dev *net_dev); +static int hv_nv_connect_to_vsp(struct hv_device *device); +static void hv_nv_on_send_completion(struct hv_device *device, + hv_vm_packet_descriptor *pkt); +static void hv_nv_on_receive(struct hv_device *device, + hv_vm_packet_descriptor *pkt); +static void hv_nv_send_receive_completion(struct hv_device *device, + uint64_t tid); + + +/* + * + */ +static inline netvsc_dev * +hv_nv_alloc_net_device(struct hv_device *device) +{ + netvsc_dev *net_dev; + hn_softc_t *sc = device_get_softc(device->device); + + net_dev = malloc(sizeof(netvsc_dev), M_DEVBUF, M_NOWAIT | M_ZERO); + if (net_dev == NULL) { + return (NULL); + } + + net_dev->dev = device; + net_dev->destroy = FALSE; + sc->net_dev = net_dev; + + return (net_dev); +} + +/* + * + */ +static inline netvsc_dev * +hv_nv_get_outbound_net_device(struct hv_device *device) +{ + hn_softc_t *sc = device_get_softc(device->device); + netvsc_dev *net_dev = sc->net_dev;; + + if ((net_dev != NULL) && net_dev->destroy) { + return (NULL); + } + + return (net_dev); +} + +/* + * + */ +static inline netvsc_dev * +hv_nv_get_inbound_net_device(struct hv_device *device) +{ + hn_softc_t *sc = device_get_softc(device->device); + netvsc_dev *net_dev = sc->net_dev;; + + if (net_dev == NULL) { + return (net_dev); + } + /* + * When the device is being destroyed; we only + * permit incoming packets if and only if there + * are outstanding sends. + */ + if (net_dev->destroy && net_dev->num_outstanding_sends == 0) { + return (NULL); + } + + return (net_dev); +} + +/* + * Net VSC initialize receive buffer with net VSP + * + * Net VSP: Network virtual services client, also known as the + * Hyper-V extensible switch and the synthetic data path. + */ +static int +hv_nv_init_rx_buffer_with_net_vsp(struct hv_device *device) +{ + netvsc_dev *net_dev; + nvsp_msg *init_pkt; + int ret = 0; + + net_dev = hv_nv_get_outbound_net_device(device); + if (!net_dev) { + return (ENODEV); + } + + net_dev->rx_buf = contigmalloc(net_dev->rx_buf_size, M_DEVBUF, + M_ZERO, 0UL, BUS_SPACE_MAXADDR, PAGE_SIZE, 0); + if (net_dev->rx_buf == NULL) { + ret = ENOMEM; + goto cleanup; + } + + /* + * Establish the GPADL handle for this buffer on this channel. + * Note: This call uses the vmbus connection rather than the + * channel to establish the gpadl handle. + * GPADL: Guest physical address descriptor list. + */ + ret = hv_vmbus_channel_establish_gpadl( + device->channel, net_dev->rx_buf, + net_dev->rx_buf_size, &net_dev->rx_buf_gpadl_handle); + if (ret != 0) { + goto cleanup; + } + + /* sema_wait(&ext->channel_init_sema); KYS CHECK */ + + /* Notify the NetVsp of the gpadl handle */ + init_pkt = &net_dev->channel_init_packet; + + memset(init_pkt, 0, sizeof(nvsp_msg)); + + init_pkt->hdr.msg_type = nvsp_msg_1_type_send_rx_buf; + init_pkt->msgs.vers_1_msgs.send_rx_buf.gpadl_handle = + net_dev->rx_buf_gpadl_handle; + init_pkt->msgs.vers_1_msgs.send_rx_buf.id = + NETVSC_RECEIVE_BUFFER_ID; + + /* Send the gpadl notification request */ + + ret = hv_vmbus_channel_send_packet(device->channel, init_pkt, + sizeof(nvsp_msg), (uint64_t)init_pkt, + HV_VMBUS_PACKET_TYPE_DATA_IN_BAND, + HV_VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); + if (ret != 0) { + goto cleanup; + } + + sema_wait(&net_dev->channel_init_sema); + + /* Check the response */ + if (init_pkt->msgs.vers_1_msgs.send_rx_buf_complete.status + != nvsp_status_success) { + ret = EINVAL; + goto cleanup; + } + + net_dev->rx_section_count = + init_pkt->msgs.vers_1_msgs.send_rx_buf_complete.num_sections; + + net_dev->rx_sections = malloc(net_dev->rx_section_count * + sizeof(nvsp_1_rx_buf_section), M_DEVBUF, M_NOWAIT); + if (net_dev->rx_sections == NULL) { + ret = EINVAL; + goto cleanup; + } + memcpy(net_dev->rx_sections, + init_pkt->msgs.vers_1_msgs.send_rx_buf_complete.sections, + net_dev->rx_section_count * sizeof(nvsp_1_rx_buf_section)); + + + /* + * For first release, there should only be 1 section that represents + * the entire receive buffer + */ + if (net_dev->rx_section_count != 1 + || net_dev->rx_sections->offset != 0) { + ret = EINVAL; + goto cleanup; + } + + goto exit; + +cleanup: + hv_nv_destroy_rx_buffer(net_dev); + +exit: + return (ret); +} + +/* + * Net VSC initialize send buffer with net VSP + */ +static int +hv_nv_init_send_buffer_with_net_vsp(struct hv_device *device) +{ + netvsc_dev *net_dev; + nvsp_msg *init_pkt; + int ret = 0; + + net_dev = hv_nv_get_outbound_net_device(device); + if (!net_dev) { + return (ENODEV); + } + + net_dev->send_buf = contigmalloc(net_dev->send_buf_size, M_DEVBUF, + M_ZERO, 0UL, BUS_SPACE_MAXADDR, PAGE_SIZE, 0); + if (net_dev->send_buf == NULL) { + ret = ENOMEM; + goto cleanup; + } + + /* + * Establish the gpadl handle for this buffer on this channel. + * Note: This call uses the vmbus connection rather than the + * channel to establish the gpadl handle. + */ + ret = hv_vmbus_channel_establish_gpadl(device->channel, + net_dev->send_buf, net_dev->send_buf_size, + &net_dev->send_buf_gpadl_handle); + if (ret != 0) { + goto cleanup; + } + + /* Notify the NetVsp of the gpadl handle */ + + init_pkt = &net_dev->channel_init_packet; + + memset(init_pkt, 0, sizeof(nvsp_msg)); + + init_pkt->hdr.msg_type = nvsp_msg_1_type_send_send_buf; + init_pkt->msgs.vers_1_msgs.send_rx_buf.gpadl_handle = + net_dev->send_buf_gpadl_handle; + init_pkt->msgs.vers_1_msgs.send_rx_buf.id = + NETVSC_SEND_BUFFER_ID; + + /* Send the gpadl notification request */ + + ret = hv_vmbus_channel_send_packet(device->channel, init_pkt, + sizeof(nvsp_msg), (uint64_t)init_pkt, + HV_VMBUS_PACKET_TYPE_DATA_IN_BAND, + HV_VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); + if (ret != 0) { + goto cleanup; + } + + sema_wait(&net_dev->channel_init_sema); + + /* Check the response */ + if (init_pkt->msgs.vers_1_msgs.send_send_buf_complete.status + != nvsp_status_success) { + ret = EINVAL; + goto cleanup; + } + + net_dev->send_section_size = + init_pkt->msgs.vers_1_msgs.send_send_buf_complete.section_size; + + goto exit; + +cleanup: + hv_nv_destroy_send_buffer(net_dev); + +exit: + return (ret); +} + +/* + * Net VSC destroy receive buffer + */ +static int +hv_nv_destroy_rx_buffer(netvsc_dev *net_dev) +{ + nvsp_msg *revoke_pkt; + int ret = 0; + + /* + * If we got a section count, it means we received a + * send_rx_buf_complete msg + * (ie sent nvsp_msg_1_type_send_rx_buf msg) therefore, + * we need to send a revoke msg here + */ + if (net_dev->rx_section_count) { + /* Send the revoke receive buffer */ + revoke_pkt = &net_dev->revoke_packet; + memset(revoke_pkt, 0, sizeof(nvsp_msg)); + + revoke_pkt->hdr.msg_type = nvsp_msg_1_type_revoke_rx_buf; + revoke_pkt->msgs.vers_1_msgs.revoke_rx_buf.id = + NETVSC_RECEIVE_BUFFER_ID; + + ret = hv_vmbus_channel_send_packet(net_dev->dev->channel, + revoke_pkt, sizeof(nvsp_msg), + (uint64_t)revoke_pkt, + HV_VMBUS_PACKET_TYPE_DATA_IN_BAND, 0); + + /* + * If we failed here, we might as well return and have a leak + * rather than continue and a bugchk + */ + if (ret != 0) { + return (ret); + } + } + + /* Tear down the gpadl on the vsp end */ + if (net_dev->rx_buf_gpadl_handle) { + ret = hv_vmbus_channel_teardown_gpdal(net_dev->dev->channel, + net_dev->rx_buf_gpadl_handle); + /* + * If we failed here, we might as well return and have a leak + * rather than continue and a bugchk + */ + if (ret != 0) { + return (ret); + } + net_dev->rx_buf_gpadl_handle = 0; + } + + if (net_dev->rx_buf) { + /* Free up the receive buffer */ + contigfree(net_dev->rx_buf, net_dev->rx_buf_size, M_DEVBUF); + net_dev->rx_buf = NULL; + } + + if (net_dev->rx_sections) { + free(net_dev->rx_sections, M_DEVBUF); + net_dev->rx_sections = NULL; + net_dev->rx_section_count = 0; + } + + return (ret); +} + +/* + * Net VSC destroy send buffer + */ +static int +hv_nv_destroy_send_buffer(netvsc_dev *net_dev) +{ + nvsp_msg *revoke_pkt; + int ret = 0; + + /* + * If we got a section count, it means we received a + * send_rx_buf_complete msg + * (ie sent nvsp_msg_1_type_send_rx_buf msg) therefore, + * we need to send a revoke msg here + */ + if (net_dev->send_section_size) { + /* Send the revoke send buffer */ + revoke_pkt = &net_dev->revoke_packet; + memset(revoke_pkt, 0, sizeof(nvsp_msg)); + + revoke_pkt->hdr.msg_type = + nvsp_msg_1_type_revoke_send_buf; + revoke_pkt->msgs.vers_1_msgs.revoke_send_buf.id = + NETVSC_SEND_BUFFER_ID; + + ret = hv_vmbus_channel_send_packet(net_dev->dev->channel, + revoke_pkt, sizeof(nvsp_msg), + (uint64_t)revoke_pkt, + HV_VMBUS_PACKET_TYPE_DATA_IN_BAND, 0); + /* + * If we failed here, we might as well return and have a leak + * rather than continue and a bugchk + */ + if (ret != 0) { + return (ret); + } + } + + /* Tear down the gpadl on the vsp end */ + if (net_dev->send_buf_gpadl_handle) { + ret = hv_vmbus_channel_teardown_gpdal(net_dev->dev->channel, + net_dev->send_buf_gpadl_handle); + + /* + * If we failed here, we might as well return and have a leak + * rather than continue and a bugchk + */ + if (ret != 0) { + return (ret); + } + net_dev->send_buf_gpadl_handle = 0; + } + + if (net_dev->send_buf) { + /* Free up the receive buffer */ + contigfree(net_dev->send_buf, net_dev->send_buf_size, M_DEVBUF); + net_dev->send_buf = NULL; + } + + return (ret); +} + + +/* + * Attempt to negotiate the caller-specified NVSP version + * + * For NVSP v2, Server 2008 R2 does not set + * init_pkt->msgs.init_msgs.init_compl.negotiated_prot_vers + * to the negotiated version, so we cannot rely on that. + */ +static int +hv_nv_negotiate_nvsp_protocol(struct hv_device *device, netvsc_dev *net_dev, + uint32_t nvsp_ver) +{ + nvsp_msg *init_pkt; + int ret; + + init_pkt = &net_dev->channel_init_packet; + memset(init_pkt, 0, sizeof(nvsp_msg)); + init_pkt->hdr.msg_type = nvsp_msg_type_init; + + /* + * Specify parameter as the only acceptable protocol version + */ + init_pkt->msgs.init_msgs.init.p1.protocol_version = nvsp_ver; + init_pkt->msgs.init_msgs.init.protocol_version_2 = nvsp_ver; + + /* Send the init request */ + ret = hv_vmbus_channel_send_packet(device->channel, init_pkt, + sizeof(nvsp_msg), (uint64_t)init_pkt, + HV_VMBUS_PACKET_TYPE_DATA_IN_BAND, + HV_VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); + if (ret != 0) + return (-1); + + sema_wait(&net_dev->channel_init_sema); + + if (init_pkt->msgs.init_msgs.init_compl.status != nvsp_status_success) + return (EINVAL); + + return (0); +} + +/* + * Send NDIS version 2 config packet containing MTU. + * + * Not valid for NDIS version 1. + */ +static int +hv_nv_send_ndis_config(struct hv_device *device, uint32_t mtu) +{ + netvsc_dev *net_dev; + nvsp_msg *init_pkt; + int ret; + + net_dev = hv_nv_get_outbound_net_device(device); + if (!net_dev) + return (-ENODEV); + + /* + * Set up configuration packet, write MTU + * Indicate we are capable of handling VLAN tags + */ + init_pkt = &net_dev->channel_init_packet; + memset(init_pkt, 0, sizeof(nvsp_msg)); + init_pkt->hdr.msg_type = nvsp_msg_2_type_send_ndis_config; + init_pkt->msgs.vers_2_msgs.send_ndis_config.mtu = mtu; + init_pkt-> + msgs.vers_2_msgs.send_ndis_config.capabilities.u1.u2.ieee8021q + = 1; + + /* Send the configuration packet */ + ret = hv_vmbus_channel_send_packet(device->channel, init_pkt, + sizeof(nvsp_msg), (uint64_t)init_pkt, + HV_VMBUS_PACKET_TYPE_DATA_IN_BAND, 0); + if (ret != 0) + return (-EINVAL); + + return (0); +} + +/* + * Net VSC connect to VSP + */ +static int +hv_nv_connect_to_vsp(struct hv_device *device) +{ + netvsc_dev *net_dev; + nvsp_msg *init_pkt; + uint32_t nvsp_vers; + uint32_t ndis_version; + int ret = 0; + device_t dev = device->device; + hn_softc_t *sc = device_get_softc(dev); + struct ifnet *ifp = sc->arpcom.ac_ifp; + + net_dev = hv_nv_get_outbound_net_device(device); + if (!net_dev) { + return (ENODEV); + } + + /* + * Negotiate the NVSP version. Try NVSP v2 first. + */ + nvsp_vers = NVSP_PROTOCOL_VERSION_2; + ret = hv_nv_negotiate_nvsp_protocol(device, net_dev, nvsp_vers); + if (ret != 0) { + /* NVSP v2 failed, try NVSP v1 */ + nvsp_vers = NVSP_PROTOCOL_VERSION_1; + ret = hv_nv_negotiate_nvsp_protocol(device, net_dev, nvsp_vers); + if (ret != 0) { + /* NVSP v1 failed, return bad status */ + return (ret); + } + } + net_dev->nvsp_version = nvsp_vers; + + /* + * Set the MTU if supported by this NVSP protocol version + * This needs to be right after the NVSP init message per Haiyang + */ + if (nvsp_vers >= NVSP_PROTOCOL_VERSION_2) + ret = hv_nv_send_ndis_config(device, ifp->if_mtu); + + /* + * Send the NDIS version + */ + init_pkt = &net_dev->channel_init_packet; + + memset(init_pkt, 0, sizeof(nvsp_msg)); + + /* + * Updated to version 5.1, minimum, for VLAN per Haiyang + */ + ndis_version = NDIS_VERSION; + + init_pkt->hdr.msg_type = nvsp_msg_1_type_send_ndis_vers; + init_pkt->msgs.vers_1_msgs.send_ndis_vers.ndis_major_vers = + (ndis_version & 0xFFFF0000) >> 16; + init_pkt->msgs.vers_1_msgs.send_ndis_vers.ndis_minor_vers = + ndis_version & 0xFFFF; + + /* Send the init request */ + + ret = hv_vmbus_channel_send_packet(device->channel, init_pkt, + sizeof(nvsp_msg), (uint64_t)init_pkt, + HV_VMBUS_PACKET_TYPE_DATA_IN_BAND, 0); + if (ret != 0) { + goto cleanup; + } + /* + * TODO: BUGBUG - We have to wait for the above msg since the netvsp + * uses KMCL which acknowledges packet (completion packet) + * since our Vmbus always set the + * HV_VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED flag + */ + /* sema_wait(&NetVscChannel->channel_init_sema); */ + + /* Post the big receive buffer to NetVSP */ + ret = hv_nv_init_rx_buffer_with_net_vsp(device); + if (ret == 0) + ret = hv_nv_init_send_buffer_with_net_vsp(device); + +cleanup: + return (ret); +} + +/* + * Net VSC disconnect from VSP + */ +static void +hv_nv_disconnect_from_vsp(netvsc_dev *net_dev) +{ + hv_nv_destroy_rx_buffer(net_dev); + hv_nv_destroy_send_buffer(net_dev); +} + +/* + * Net VSC on device add + * + * Callback when the device belonging to this driver is added + */ +netvsc_dev * +hv_nv_on_device_add(struct hv_device *device, void *additional_info) +{ + netvsc_dev *net_dev; + netvsc_packet *packet; + netvsc_packet *next_packet; + int i, ret = 0; + + net_dev = hv_nv_alloc_net_device(device); + if (!net_dev) + goto cleanup; + + /* Initialize the NetVSC channel extension */ + net_dev->rx_buf_size = NETVSC_RECEIVE_BUFFER_SIZE; + mtx_init(&net_dev->rx_pkt_list_lock, "HV-RPL", NULL, + MTX_SPIN | MTX_RECURSE); + + net_dev->send_buf_size = NETVSC_SEND_BUFFER_SIZE; + + /* Same effect as STAILQ_HEAD_INITIALIZER() static initializer */ + STAILQ_INIT(&net_dev->myrx_packet_list); + + /* + * malloc a sufficient number of netvsc_packet buffers to hold + * a packet list. Add them to the netvsc device packet queue. + */ + for (i=0; i < NETVSC_RECEIVE_PACKETLIST_COUNT; i++) { + packet = malloc(sizeof(netvsc_packet) + + (NETVSC_RECEIVE_SG_COUNT * sizeof(hv_vmbus_page_buffer)), + M_DEVBUF, M_NOWAIT | M_ZERO); + if (!packet) { + break; + } + STAILQ_INSERT_TAIL(&net_dev->myrx_packet_list, packet, + mylist_entry); + } + + sema_init(&net_dev->channel_init_sema, 0, "netdev_sema"); + + /* + * Open the channel + */ + ret = hv_vmbus_channel_open(device->channel, + NETVSC_DEVICE_RING_BUFFER_SIZE, NETVSC_DEVICE_RING_BUFFER_SIZE, + NULL, 0, hv_nv_on_channel_callback, device); + if (ret != 0) + goto cleanup; + + /* + * Connect with the NetVsp + */ + ret = hv_nv_connect_to_vsp(device); + if (ret != 0) + goto close; + + return (net_dev); + +close: + /* Now, we can close the channel safely */ + + hv_vmbus_channel_close(device->channel); + +cleanup: + /* + * Free the packet buffers on the netvsc device packet queue. + * Release other resources. + */ + if (net_dev) { + sema_destroy(&net_dev->channel_init_sema); + + packet = STAILQ_FIRST(&net_dev->myrx_packet_list); + while (packet != NULL) { + next_packet = STAILQ_NEXT(packet, mylist_entry); + free(packet, M_DEVBUF); + packet = next_packet; + } + /* Reset the list to initial state */ + STAILQ_INIT(&net_dev->myrx_packet_list); + + mtx_destroy(&net_dev->rx_pkt_list_lock); + + free(net_dev, M_DEVBUF); + } + + return (NULL); +} + +/* + * Net VSC on device remove + */ +int +hv_nv_on_device_remove(struct hv_device *device, boolean_t destroy_channel) +{ + netvsc_packet *net_vsc_pkt; + netvsc_packet *next_net_vsc_pkt; + hn_softc_t *sc = device_get_softc(device->device); + netvsc_dev *net_dev = sc->net_dev;; + + /* Stop outbound traffic ie sends and receives completions */ + mtx_lock(&device->channel->inbound_lock); + net_dev->destroy = TRUE; + mtx_unlock(&device->channel->inbound_lock); + + /* Wait for all send completions */ + while (net_dev->num_outstanding_sends) { + DELAY(100); + } + + hv_nv_disconnect_from_vsp(net_dev); + + /* At this point, no one should be accessing net_dev except in here */ + + /* Now, we can close the channel safely */ + + if (!destroy_channel) { + device->channel->state = + HV_CHANNEL_CLOSING_NONDESTRUCTIVE_STATE; + } + + hv_vmbus_channel_close(device->channel); + + /* Release all resources */ + net_vsc_pkt = STAILQ_FIRST(&net_dev->myrx_packet_list); + while (net_vsc_pkt != NULL) { + next_net_vsc_pkt = STAILQ_NEXT(net_vsc_pkt, mylist_entry); + free(net_vsc_pkt, M_DEVBUF); + net_vsc_pkt = next_net_vsc_pkt; + } + + /* Reset the list to initial state */ + STAILQ_INIT(&net_dev->myrx_packet_list); + + mtx_destroy(&net_dev->rx_pkt_list_lock); + sema_destroy(&net_dev->channel_init_sema); + free(net_dev, M_DEVBUF); + + return (0); +} + +/* + * Net VSC on send completion + */ +static void +hv_nv_on_send_completion(struct hv_device *device, hv_vm_packet_descriptor *pkt) +{ + netvsc_dev *net_dev; + nvsp_msg *nvsp_msg_pkt; + netvsc_packet *net_vsc_pkt; + + net_dev = hv_nv_get_inbound_net_device(device); + if (!net_dev) { + return; + } + + nvsp_msg_pkt = + (nvsp_msg *)((unsigned long)pkt + (pkt->data_offset8 << 3)); + + if (nvsp_msg_pkt->hdr.msg_type == nvsp_msg_type_init_complete + || nvsp_msg_pkt->hdr.msg_type + == nvsp_msg_1_type_send_rx_buf_complete + || nvsp_msg_pkt->hdr.msg_type + == nvsp_msg_1_type_send_send_buf_complete) { + /* Copy the response back */ + memcpy(&net_dev->channel_init_packet, nvsp_msg_pkt, + sizeof(nvsp_msg)); + sema_post(&net_dev->channel_init_sema); + } else if (nvsp_msg_pkt->hdr.msg_type == + nvsp_msg_1_type_send_rndis_pkt_complete) { + /* Get the send context */ + net_vsc_pkt = + (netvsc_packet *)(unsigned long)pkt->transaction_id; + + /* Notify the layer above us */ + net_vsc_pkt->compl.send.on_send_completion( + net_vsc_pkt->compl.send.send_completion_context); + + atomic_subtract_int(&net_dev->num_outstanding_sends, 1); + } +} + +/* + * Net VSC on send + * Sends a packet on the specified Hyper-V device. + * Returns 0 on success, non-zero on failure. + */ +int +hv_nv_on_send(struct hv_device *device, netvsc_packet *pkt) +{ + netvsc_dev *net_dev; + nvsp_msg send_msg; + int ret; + + net_dev = hv_nv_get_outbound_net_device(device); + if (!net_dev) + return (ENODEV); + + send_msg.hdr.msg_type = nvsp_msg_1_type_send_rndis_pkt; + if (pkt->is_data_pkt) { + /* 0 is RMC_DATA */ + send_msg.msgs.vers_1_msgs.send_rndis_pkt.chan_type = 0; + } else { + /* 1 is RMC_CONTROL */ + send_msg.msgs.vers_1_msgs.send_rndis_pkt.chan_type = 1; + } + + /* Not using send buffer section */ + send_msg.msgs.vers_1_msgs.send_rndis_pkt.send_buf_section_idx = + 0xFFFFFFFF; + send_msg.msgs.vers_1_msgs.send_rndis_pkt.send_buf_section_size = 0; + + if (pkt->page_buf_count) { + ret = hv_vmbus_channel_send_packet_pagebuffer(device->channel, + pkt->page_buffers, pkt->page_buf_count, + &send_msg, sizeof(nvsp_msg), (uint64_t)pkt); + } else { + ret = hv_vmbus_channel_send_packet(device->channel, + &send_msg, sizeof(nvsp_msg), (uint64_t)pkt, + HV_VMBUS_PACKET_TYPE_DATA_IN_BAND, + HV_VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); + } + + /* Record outstanding send only if send_packet() succeeded */ + if (ret == 0) + atomic_add_int(&net_dev->num_outstanding_sends, 1); + + return (ret); +} + +/* + * Net VSC on receive + * + * In the FreeBSD Hyper-V virtual world, this function deals exclusively + * with virtual addresses. + */ +static void +hv_nv_on_receive(struct hv_device *device, hv_vm_packet_descriptor *pkt) +{ + netvsc_dev *net_dev; + hv_vm_transfer_page_packet_header *vm_xfer_page_pkt; + nvsp_msg *nvsp_msg_pkt; + netvsc_packet *net_vsc_pkt = NULL; + unsigned long start; + xfer_page_packet *xfer_page_pkt = NULL; + STAILQ_HEAD(PKT_LIST, netvsc_packet_) mylist_head = + STAILQ_HEAD_INITIALIZER(mylist_head); + int count = 0; + int i = 0; + + net_dev = hv_nv_get_inbound_net_device(device); + if (!net_dev) + return; + + /* + * All inbound packets other than send completion should be + * xfer page packet. + */ + if (pkt->type != HV_VMBUS_PACKET_TYPE_DATA_USING_TRANSFER_PAGES) + return; + + nvsp_msg_pkt = (nvsp_msg *)((unsigned long)pkt + + (pkt->data_offset8 << 3)); + + /* Make sure this is a valid nvsp packet */ + if (nvsp_msg_pkt->hdr.msg_type != nvsp_msg_1_type_send_rndis_pkt) + return; + + vm_xfer_page_pkt = (hv_vm_transfer_page_packet_header *)pkt; + + if (vm_xfer_page_pkt->transfer_page_set_id + != NETVSC_RECEIVE_BUFFER_ID) { + return; + } + + STAILQ_INIT(&mylist_head); + + /* + * Grab free packets (range count + 1) to represent this xfer page + * packet. +1 to represent the xfer page packet itself. We grab it + * here so that we know exactly how many we can fulfill. + */ + mtx_lock_spin(&net_dev->rx_pkt_list_lock); + while (!STAILQ_EMPTY(&net_dev->myrx_packet_list)) { + net_vsc_pkt = STAILQ_FIRST(&net_dev->myrx_packet_list); + STAILQ_REMOVE_HEAD(&net_dev->myrx_packet_list, mylist_entry); + + STAILQ_INSERT_TAIL(&mylist_head, net_vsc_pkt, mylist_entry); + + if (++count == vm_xfer_page_pkt->range_count + 1) + break; + } + + mtx_unlock_spin(&net_dev->rx_pkt_list_lock); + + /* + * We need at least 2 netvsc pkts (1 to represent the xfer page + * and at least 1 for the range) i.e. we can handle some of the + * xfer page packet ranges... + */ + if (count < 2) { + /* Return netvsc packet to the freelist */ + mtx_lock_spin(&net_dev->rx_pkt_list_lock); + for (i=count; i != 0; i--) { + net_vsc_pkt = STAILQ_FIRST(&mylist_head); + STAILQ_REMOVE_HEAD(&mylist_head, mylist_entry); + + STAILQ_INSERT_TAIL(&net_dev->myrx_packet_list, + net_vsc_pkt, mylist_entry); + } + mtx_unlock_spin(&net_dev->rx_pkt_list_lock); + + hv_nv_send_receive_completion(device, + vm_xfer_page_pkt->d.transaction_id); + + return; + } + + /* Take the first packet in the list */ + xfer_page_pkt = (xfer_page_packet *)STAILQ_FIRST(&mylist_head); + STAILQ_REMOVE_HEAD(&mylist_head, mylist_entry); + + /* This is how many data packets we can supply */ + xfer_page_pkt->count = count - 1; + + /* Each range represents 1 RNDIS pkt that contains 1 Ethernet frame */ + for (i=0; i < (count - 1); i++) { + net_vsc_pkt = STAILQ_FIRST(&mylist_head); + STAILQ_REMOVE_HEAD(&mylist_head, mylist_entry); + + /* + * Initialize the netvsc packet + */ + net_vsc_pkt->xfer_page_pkt = xfer_page_pkt; + net_vsc_pkt->compl.rx.rx_completion_context = net_vsc_pkt; + net_vsc_pkt->device = device; + /* Save this so that we can send it back */ + net_vsc_pkt->compl.rx.rx_completion_tid = + vm_xfer_page_pkt->d.transaction_id; + + net_vsc_pkt->tot_data_buf_len = + vm_xfer_page_pkt->ranges[i].byte_count; + net_vsc_pkt->page_buf_count = 1; + + net_vsc_pkt->page_buffers[0].length = + vm_xfer_page_pkt->ranges[i].byte_count; + + /* The virtual address of the packet in the receive buffer */ + start = ((unsigned long)net_dev->rx_buf + + vm_xfer_page_pkt->ranges[i].byte_offset); + start = ((unsigned long)start) & ~(PAGE_SIZE - 1); + + /* Page number of the virtual page containing packet start */ + net_vsc_pkt->page_buffers[0].pfn = start >> PAGE_SHIFT; + + /* Calculate the page relative offset */ + net_vsc_pkt->page_buffers[0].offset = + vm_xfer_page_pkt->ranges[i].byte_offset & (PAGE_SIZE - 1); + + /* + * In this implementation, we are dealing with virtual + * addresses exclusively. Since we aren't using physical + * addresses at all, we don't care if a packet crosses a + * page boundary. For this reason, the original code to + * check for and handle page crossings has been removed. + */ + + /* + * Pass it to the upper layer. The receive completion call + * has been moved into this function. + */ + hv_rf_on_receive(device, net_vsc_pkt); + + /* + * Moved completion call back here so that all received + * messages (not just data messages) will trigger a response + * message back to the host. + */ + hv_nv_on_receive_completion(net_vsc_pkt); + } +} + +/* + * Net VSC send receive completion + */ +static void +hv_nv_send_receive_completion(struct hv_device *device, uint64_t tid) +{ + nvsp_msg rx_comp_msg; + int retries = 0; + int ret = 0; + + rx_comp_msg.hdr.msg_type = nvsp_msg_1_type_send_rndis_pkt_complete; + + /* Pass in the status */ + rx_comp_msg.msgs.vers_1_msgs.send_rndis_pkt_complete.status = + nvsp_status_success; + +retry_send_cmplt: + /* Send the completion */ + ret = hv_vmbus_channel_send_packet(device->channel, &rx_comp_msg, + sizeof(nvsp_msg), tid, HV_VMBUS_PACKET_TYPE_COMPLETION, 0); + if (ret == 0) { + /* success */ + /* no-op */ + } else if (ret == EAGAIN) { + /* no more room... wait a bit and attempt to retry 3 times */ + retries++; + + if (retries < 4) { + DELAY(100); + goto retry_send_cmplt; + } + } +} + +/* + * Net VSC on receive completion + * + * Send a receive completion packet to RNDIS device (ie NetVsp) + */ +void +hv_nv_on_receive_completion(void *context) +{ + netvsc_packet *packet = (netvsc_packet *)context; + struct hv_device *device = (struct hv_device *)packet->device; + netvsc_dev *net_dev; + uint64_t tid = 0; + boolean_t send_rx_completion = FALSE; + + /* + * Even though it seems logical to do a hv_nv_get_outbound_net_device() + * here to send out receive completion, we are using + * hv_nv_get_inbound_net_device() since we may have disabled + * outbound traffic already. + */ + net_dev = hv_nv_get_inbound_net_device(device); + if (net_dev == NULL) + return; + + /* Overloading use of the lock. */ + mtx_lock_spin(&net_dev->rx_pkt_list_lock); + + packet->xfer_page_pkt->count--; + + /* + * Last one in the line that represent 1 xfer page packet. + * Return the xfer page packet itself to the free list. + */ + if (packet->xfer_page_pkt->count == 0) { + send_rx_completion = TRUE; + tid = packet->compl.rx.rx_completion_tid; + STAILQ_INSERT_TAIL(&net_dev->myrx_packet_list, + (netvsc_packet *)(packet->xfer_page_pkt), mylist_entry); + } + + /* Put the packet back on the free list */ + STAILQ_INSERT_TAIL(&net_dev->myrx_packet_list, packet, mylist_entry); + mtx_unlock_spin(&net_dev->rx_pkt_list_lock); + + /* Send a receive completion for the xfer page packet */ + if (send_rx_completion) + hv_nv_send_receive_completion(device, tid); +} + +/* + * Net VSC on channel callback + */ +static void +hv_nv_on_channel_callback(void *context) +{ + /* Fixme: Magic number */ + const int net_pkt_size = 2048; + struct hv_device *device = (struct hv_device *)context; + netvsc_dev *net_dev; + uint32_t bytes_rxed; + uint64_t request_id; + uint8_t *packet; + hv_vm_packet_descriptor *desc; + uint8_t *buffer; + int bufferlen = net_pkt_size; + int ret = 0; + + packet = malloc(net_pkt_size * sizeof(uint8_t), M_DEVBUF, M_NOWAIT); + if (!packet) + return; + + buffer = packet; + + net_dev = hv_nv_get_inbound_net_device(device); + if (net_dev == NULL) + goto out; + + do { + ret = hv_vmbus_channel_recv_packet_raw(device->channel, + buffer, bufferlen, &bytes_rxed, &request_id); + if (ret == 0) { + if (bytes_rxed > 0) { + desc = (hv_vm_packet_descriptor *)buffer; + switch (desc->type) { + case HV_VMBUS_PACKET_TYPE_COMPLETION: + hv_nv_on_send_completion(device, desc); + break; + case HV_VMBUS_PACKET_TYPE_DATA_USING_TRANSFER_PAGES: + hv_nv_on_receive(device, desc); + break; + default: + break; + } + } else { + break; + } + } else if (ret == ENOBUFS) { + /* Handle large packet */ + free(buffer, M_DEVBUF); + buffer = malloc(bytes_rxed, M_DEVBUF, M_NOWAIT); + if (buffer == NULL) { + break; + } + bufferlen = bytes_rxed; + } + } while (1); + +out: + free(buffer, M_DEVBUF); +} + diff --git a/sys/dev/hyperv/netvsc/hv_net_vsc.h b/sys/dev/hyperv/netvsc/hv_net_vsc.h new file mode 100644 index 00000000000..f7e7d00a903 --- /dev/null +++ b/sys/dev/hyperv/netvsc/hv_net_vsc.h @@ -0,0 +1,995 @@ +/*- + * Copyright (c) 2009-2012 Microsoft Corp. + * Copyright (c) 2010-2012 Citrix Inc. + * Copyright (c) 2012 NetApp Inc. + * 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 unmodified, 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +/* + * HyperV vmbus (virtual machine bus) network VSC (virtual services client) + * header file + * + * (Updated from unencumbered NvspProtocol.h) + */ + +#ifndef __HV_NET_VSC_H__ +#define __HV_NET_VSC_H__ + +#include +#include +#include +#include + +#include + + +#define NVSP_INVALID_PROTOCOL_VERSION (0xFFFFFFFF) + +#define NVSP_PROTOCOL_VERSION_1 2 +#define NVSP_PROTOCOL_VERSION_2 0x30002 +#define NVSP_MIN_PROTOCOL_VERSION (NVSP_PROTOCOL_VERSION_1) +#define NVSP_MAX_PROTOCOL_VERSION (NVSP_PROTOCOL_VERSION_2) + +#define NVSP_PROTOCOL_VERSION_CURRENT NVSP_PROTOCOL_VERSION_2 + +#define NVSP_OPERATIONAL_STATUS_OK (0x00000000) +#define NVSP_OPERATIONAL_STATUS_DEGRADED (0x00000001) +#define NVSP_OPERATIONAL_STATUS_NONRECOVERABLE (0x00000002) +#define NVSP_OPERATIONAL_STATUS_NO_CONTACT (0x00000003) +#define NVSP_OPERATIONAL_STATUS_LOST_COMMUNICATION (0x00000004) + +/* + * Maximun number of transfer pages (packets) the VSP will use on a receive + */ +#define NVSP_MAX_PACKETS_PER_RECEIVE 375 + + +typedef enum nvsp_msg_type_ { + nvsp_msg_type_none = 0, + + /* + * Init Messages + */ + nvsp_msg_type_init = 1, + nvsp_msg_type_init_complete = 2, + + nvsp_version_msg_start = 100, + + /* + * Version 1 Messages + */ + nvsp_msg_1_type_send_ndis_vers = nvsp_version_msg_start, + + nvsp_msg_1_type_send_rx_buf, + nvsp_msg_1_type_send_rx_buf_complete, + nvsp_msg_1_type_revoke_rx_buf, + + nvsp_msg_1_type_send_send_buf, + nvsp_msg_1_type_send_send_buf_complete, + nvsp_msg_1_type_revoke_send_buf, + + nvsp_msg_1_type_send_rndis_pkt, + nvsp_msg_1_type_send_rndis_pkt_complete, + + /* + * Version 2 Messages + */ + nvsp_msg_2_type_send_chimney_delegated_buf, + nvsp_msg_2_type_send_chimney_delegated_buf_complete, + nvsp_msg_2_type_revoke_chimney_delegated_buf, + + nvsp_msg_2_type_resume_chimney_rx_indication, + + nvsp_msg_2_type_terminate_chimney, + nvsp_msg_2_type_terminate_chimney_complete, + + nvsp_msg_2_type_indicate_chimney_event, + + nvsp_msg_2_type_send_chimney_packet, + nvsp_msg_2_type_send_chimney_packet_complete, + + nvsp_msg_2_type_post_chimney_rx_request, + nvsp_msg_2_type_post_chimney_rx_request_complete, + + nvsp_msg_2_type_alloc_rx_buf, + nvsp_msg_2_type_alloc_rx_buf_complete, + + nvsp_msg_2_type_free_rx_buf, + + nvsp_msg_2_send_vmq_rndis_pkt, + nvsp_msg_2_send_vmq_rndis_pkt_complete, + + nvsp_msg_2_type_send_ndis_config, + + nvsp_msg_2_type_alloc_chimney_handle, + nvsp_msg_2_type_alloc_chimney_handle_complete, +} nvsp_msg_type; + +typedef enum nvsp_status_ { + nvsp_status_none = 0, + nvsp_status_success, + nvsp_status_failure, + /* Deprecated */ + nvsp_status_prot_vers_range_too_new, + /* Deprecated */ + nvsp_status_prot_vers_range_too_old, + nvsp_status_invalid_rndis_pkt, + nvsp_status_busy, + nvsp_status_max, +} nvsp_status; + +typedef struct nvsp_msg_hdr_ { + uint32_t msg_type; +} __packed nvsp_msg_hdr; + +/* + * Init Messages + */ + +/* + * This message is used by the VSC to initialize the channel + * after the channels has been opened. This message should + * never include anything other then versioning (i.e. this + * message will be the same for ever). + * + * Forever is a long time. The values have been redefined + * in Win7 to indicate major and minor protocol version + * number. + */ +typedef struct nvsp_msg_init_ { + union { + struct { + uint16_t minor_protocol_version; + uint16_t major_protocol_version; + } s; + /* Formerly min_protocol_version */ + uint32_t protocol_version; + } p1; + /* Formerly max_protocol_version */ + uint32_t protocol_version_2; +} __packed nvsp_msg_init; + +/* + * This message is used by the VSP to complete the initialization + * of the channel. This message should never include anything other + * then versioning (i.e. this message will be the same forever). + */ +typedef struct nvsp_msg_init_complete_ { + /* Deprecated */ + uint32_t negotiated_prot_vers; + uint32_t max_mdl_chain_len; + uint32_t status; +} __packed nvsp_msg_init_complete; + +typedef union nvsp_msg_init_uber_ { + nvsp_msg_init init; + nvsp_msg_init_complete init_compl; +} __packed nvsp_msg_init_uber; + +/* + * Version 1 Messages + */ + +/* + * This message is used by the VSC to send the NDIS version + * to the VSP. The VSP can use this information when handling + * OIDs sent by the VSC. + */ +typedef struct nvsp_1_msg_send_ndis_version_ { + uint32_t ndis_major_vers; + /* Deprecated */ + uint32_t ndis_minor_vers; +} __packed nvsp_1_msg_send_ndis_version; + +/* + * This message is used by the VSC to send a receive buffer + * to the VSP. The VSP can then use the receive buffer to + * send data to the VSC. + */ +typedef struct nvsp_1_msg_send_rx_buf_ { + uint32_t gpadl_handle; + uint16_t id; +} __packed nvsp_1_msg_send_rx_buf; + +typedef struct nvsp_1_rx_buf_section_ { + uint32_t offset; + uint32_t sub_allocation_size; + uint32_t num_sub_allocations; + uint32_t end_offset; +} __packed nvsp_1_rx_buf_section; + +/* + * This message is used by the VSP to acknowledge a receive + * buffer send by the VSC. This message must be sent by the + * VSP before the VSP uses the receive buffer. + */ +typedef struct nvsp_1_msg_send_rx_buf_complete_ { + uint32_t status; + uint32_t num_sections; + + /* + * The receive buffer is split into two parts, a large + * suballocation section and a small suballocation + * section. These sections are then suballocated by a + * certain size. + * + * For example, the following break up of the receive + * buffer has 6 large suballocations and 10 small + * suballocations. + * + * | Large Section | | Small Section | + * ------------------------------------------------------------ + * | | | | | | | | | | | | | | | | | | + * | | + * LargeOffset SmallOffset + */ + nvsp_1_rx_buf_section sections[1]; + +} __packed nvsp_1_msg_send_rx_buf_complete; + +/* + * This message is sent by the VSC to revoke the receive buffer. + * After the VSP completes this transaction, the VSP should never + * use the receive buffer again. + */ +typedef struct nvsp_1_msg_revoke_rx_buf_ { + uint16_t id; +} __packed nvsp_1_msg_revoke_rx_buf; + +/* + * This message is used by the VSC to send a send buffer + * to the VSP. The VSC can then use the send buffer to + * send data to the VSP. + */ +typedef struct nvsp_1_msg_send_send_buf_ { + uint32_t gpadl_handle; + uint16_t id; +} __packed nvsp_1_msg_send_send_buf; + +/* + * This message is used by the VSP to acknowledge a send + * buffer sent by the VSC. This message must be sent by the + * VSP before the VSP uses the sent buffer. + */ +typedef struct nvsp_1_msg_send_send_buf_complete_ { + uint32_t status; + + /* + * The VSC gets to choose the size of the send buffer and + * the VSP gets to choose the sections size of the buffer. + * This was done to enable dynamic reconfigurations when + * the cost of GPA-direct buffers decreases. + */ + uint32_t section_size; +} __packed nvsp_1_msg_send_send_buf_complete; + +/* + * This message is sent by the VSC to revoke the send buffer. + * After the VSP completes this transaction, the vsp should never + * use the send buffer again. + */ +typedef struct nvsp_1_msg_revoke_send_buf_ { + uint16_t id; +} __packed nvsp_1_msg_revoke_send_buf; + +/* + * This message is used by both the VSP and the VSC to send + * an RNDIS message to the opposite channel endpoint. + */ +typedef struct nvsp_1_msg_send_rndis_pkt_ { + /* + * This field is specified by RNIDS. They assume there's + * two different channels of communication. However, + * the Network VSP only has one. Therefore, the channel + * travels with the RNDIS packet. + */ + uint32_t chan_type; + + /* + * This field is used to send part or all of the data + * through a send buffer. This values specifies an + * index into the send buffer. If the index is + * 0xFFFFFFFF, then the send buffer is not being used + * and all of the data was sent through other VMBus + * mechanisms. + */ + uint32_t send_buf_section_idx; + uint32_t send_buf_section_size; +} __packed nvsp_1_msg_send_rndis_pkt; + +/* + * This message is used by both the VSP and the VSC to complete + * a RNDIS message to the opposite channel endpoint. At this + * point, the initiator of this message cannot use any resources + * associated with the original RNDIS packet. + */ +typedef struct nvsp_1_msg_send_rndis_pkt_complete_ { + uint32_t status; +} __packed nvsp_1_msg_send_rndis_pkt_complete; + + +/* + * Version 2 Messages + */ + +/* + * This message is used by the VSC to send the NDIS version + * to the VSP. The VSP can use this information when handling + * OIDs sent by the VSC. + */ +typedef struct nvsp_2_netvsc_capabilities_ { + union { + uint64_t as_uint64; + struct { + uint64_t vmq : 1; + uint64_t chimney : 1; + uint64_t sriov : 1; + uint64_t ieee8021q : 1; + uint64_t correlationid : 1; + uint64_t teaming : 1; + } u2; + } u1; +} __packed nvsp_2_netvsc_capabilities; + +typedef struct nvsp_2_msg_send_ndis_config_ { + uint32_t mtu; + uint32_t reserved; + nvsp_2_netvsc_capabilities capabilities; +} __packed nvsp_2_msg_send_ndis_config; + +/* + * NvspMessage2TypeSendChimneyDelegatedBuffer + */ +typedef struct nvsp_2_msg_send_chimney_buf_ +{ + /* + * On WIN7 beta, delegated_obj_max_size is defined as a uint32_t + * Since WIN7 RC, it was split into two uint16_t. To have the same + * struct layout, delegated_obj_max_size shall be the first field. + */ + uint16_t delegated_obj_max_size; + + /* + * The revision # of chimney protocol used between NVSC and NVSP. + * + * This revision is NOT related to the chimney revision between + * NDIS protocol and miniport drivers. + */ + uint16_t revision; + + uint32_t gpadl_handle; +} __packed nvsp_2_msg_send_chimney_buf; + + +/* Unsupported chimney revision 0 (only present in WIN7 beta) */ +#define NVSP_CHIMNEY_REVISION_0 0 + +/* WIN7 Beta Chimney QFE */ +#define NVSP_CHIMNEY_REVISION_1 1 + +/* The chimney revision since WIN7 RC */ +#define NVSP_CHIMNEY_REVISION_2 2 + + +/* + * NvspMessage2TypeSendChimneyDelegatedBufferComplete + */ +typedef struct nvsp_2_msg_send_chimney_buf_complete_ { + uint32_t status; + + /* + * Maximum number outstanding sends and pre-posted receives. + * + * NVSC should not post more than SendQuota/ReceiveQuota packets. + * Otherwise, it can block the non-chimney path for an indefinite + * amount of time. + * (since chimney sends/receives are affected by the remote peer). + * + * Note: NVSP enforces the quota restrictions on a per-VMBCHANNEL + * basis. It doesn't enforce the restriction separately for chimney + * send/receive. If NVSC doesn't voluntarily enforce "SendQuota", + * it may kill its own network connectivity. + */ + uint32_t send_quota; + uint32_t rx_quota; +} __packed nvsp_2_msg_send_chimney_buf_complete; + +/* + * NvspMessage2TypeRevokeChimneyDelegatedBuffer + */ +typedef struct nvsp_2_msg_revoke_chimney_buf_ { + uint32_t gpadl_handle; +} __packed nvsp_2_msg_revoke_chimney_buf; + + +#define NVSP_CHIMNEY_OBJECT_TYPE_NEIGHBOR 0 +#define NVSP_CHIMNEY_OBJECT_TYPE_PATH4 1 +#define NVSP_CHIMNEY_OBJECT_TYPE_PATH6 2 +#define NVSP_CHIMNEY_OBJECT_TYPE_TCP 3 + +/* + * NvspMessage2TypeAllocateChimneyHandle + */ +typedef struct nvsp_2_msg_alloc_chimney_handle_ { + uint64_t vsc_context; + uint32_t object_type; +} __packed nvsp_2_msg_alloc_chimney_handle; + +/* + * NvspMessage2TypeAllocateChimneyHandleComplete + */ +typedef struct nvsp_2_msg_alloc_chimney_handle_complete_ { + uint32_t vsp_handle; +} __packed nvsp_2_msg_alloc_chimney_handle_complete; + + +/* + * NvspMessage2TypeResumeChimneyRXIndication + */ +typedef struct nvsp_2_msg_resume_chimney_rx_indication { + /* + * Handle identifying the offloaded connection + */ + uint32_t vsp_tcp_handle; +} __packed nvsp_2_msg_resume_chimney_rx_indication; + + +#define NVSP_2_MSG_TERMINATE_CHIMNEY_FLAGS_FIRST_STAGE (0x01u) +#define NVSP_2_MSG_TERMINATE_CHIMNEY_FLAGS_RESERVED (~(0x01u)) + +/* + * NvspMessage2TypeTerminateChimney + */ +typedef struct nvsp_2_msg_terminate_chimney_ { + /* + * Handle identifying the offloaded object + */ + uint32_t vsp_handle; + + /* + * Terminate Offload Flags + * Bit 0: + * When set to 0, terminate the offload at the destination NIC + * Bit 1-31: Reserved, shall be zero + */ + uint32_t flags; + + union { + /* + * This field is valid only when bit 0 of flags is clear. + * It specifies the index into the premapped delegated + * object buffer. The buffer was sent through the + * NvspMessage2TypeSendChimneyDelegatedBuffer + * message at initialization time. + * + * NVSP will write the delegated state into the delegated + * buffer upon upload completion. + */ + uint32_t index; + + /* + * This field is valid only when bit 0 of flags is set. + * + * The seqence number of the most recently accepted RX + * indication when VSC sets its TCP context into + * "terminating" state. + * + * This allows NVSP to determines if there are any in-flight + * RX indications for which the acceptance state is still + * undefined. + */ + uint64_t last_accepted_rx_seq_no; + } f0; +} __packed nvsp_2_msg_terminate_chimney; + + +#define NVSP_TERMINATE_CHIMNEY_COMPLETE_FLAG_DATA_CORRUPTED 0x0000001u + +/* + * NvspMessage2TypeTerminateChimneyComplete + */ +typedef struct nvsp_2_msg_terminate_chimney_complete_ { + uint64_t vsc_context; + uint32_t flags; +} __packed nvsp_2_msg_terminate_chimney_complete; + +/* + * NvspMessage2TypeIndicateChimneyEvent + */ +typedef struct nvsp_2_msg_indicate_chimney_event_ { + /* + * When VscTcpContext is 0, event_type is an NDIS_STATUS event code + * Otherwise, EventType is an TCP connection event (defined in + * NdisTcpOffloadEventHandler chimney DDK document). + */ + uint32_t event_type; + + /* + * When VscTcpContext is 0, EventType is an NDIS_STATUS event code + * Otherwise, EventType is an TCP connection event specific information + * (defined in NdisTcpOffloadEventHandler chimney DDK document). + */ + uint32_t event_specific_info; + + /* + * If not 0, the event is per-TCP connection event. This field + * contains the VSC's TCP context. + * If 0, the event indication is global. + */ + uint64_t vsc_tcp_context; +} __packed nvsp_2_msg_indicate_chimney_event; + + +#define NVSP_1_CHIMNEY_SEND_INVALID_OOB_INDEX 0xffffu +#define NVSP_1_CHIMNEY_SEND_INVALID_SECTION_INDEX 0xffffu + +/* + * NvspMessage2TypeSendChimneyPacket + */ +typedef struct nvsp_2_msg_send_chimney_pkt_ { + /* + * Identify the TCP connection for which this chimney send is + */ + uint32_t vsp_tcp_handle; + + /* + * This field is used to send part or all of the data + * through a send buffer. This values specifies an + * index into the send buffer. If the index is + * 0xFFFF, then the send buffer is not being used + * and all of the data was sent through other VMBus + * mechanisms. + */ + uint16_t send_buf_section_index; + uint16_t send_buf_section_size; + + /* + * OOB Data Index + * This an index to the OOB data buffer. If the index is 0xFFFFFFFF, + * then there is no OOB data. + * + * This field shall be always 0xFFFFFFFF for now. It is reserved for + * the future. + */ + uint16_t oob_data_index; + + /* + * DisconnectFlags = 0 + * Normal chimney send. See MiniportTcpOffloadSend for details. + * + * DisconnectFlags = TCP_DISCONNECT_GRACEFUL_CLOSE (0x01) + * Graceful disconnect. See MiniportTcpOffloadDisconnect for details. + * + * DisconnectFlags = TCP_DISCONNECT_ABORTIVE_CLOSE (0x02) + * Abortive disconnect. See MiniportTcpOffloadDisconnect for details. + */ + uint16_t disconnect_flags; + + uint32_t seq_no; +} __packed nvsp_2_msg_send_chimney_pkt; + +/* + * NvspMessage2TypeSendChimneyPacketComplete + */ +typedef struct nvsp_2_msg_send_chimney_pkt_complete_ { + /* + * The NDIS_STATUS for the chimney send + */ + uint32_t status; + + /* + * Number of bytes that have been sent to the peer (and ACKed by the peer). + */ + uint32_t bytes_transferred; +} __packed nvsp_2_msg_send_chimney_pkt_complete; + + +#define NVSP_1_CHIMNEY_RECV_FLAG_NO_PUSH 0x0001u +#define NVSP_1_CHIMNEY_RECV_INVALID_OOB_INDEX 0xffffu + +/* + * NvspMessage2TypePostChimneyRecvRequest + */ +typedef struct nvsp_2_msg_post_chimney_rx_request_ { + /* + * Identify the TCP connection which this chimney receive request + * is for. + */ + uint32_t vsp_tcp_handle; + + /* + * OOB Data Index + * This an index to the OOB data buffer. If the index is 0xFFFFFFFF, + * then there is no OOB data. + * + * This field shall be always 0xFFFFFFFF for now. It is reserved for + * the future. + */ + uint32_t oob_data_index; + + /* + * Bit 0 + * When it is set, this is a "no-push" receive. + * When it is clear, this is a "push" receive. + * + * Bit 1-15: Reserved and shall be zero + */ + uint16_t flags; + + /* + * For debugging and diagnoses purpose. + * The SeqNo is per TCP connection and starts from 0. + */ + uint32_t seq_no; +} __packed nvsp_2_msg_post_chimney_rx_request; + +/* + * NvspMessage2TypePostChimneyRecvRequestComplete + */ +typedef struct nvsp_2_msg_post_chimney_rx_request_complete_ { + /* + * The NDIS_STATUS for the chimney send + */ + uint32_t status; + + /* + * Number of bytes that have been sent to the peer (and ACKed by + * the peer). + */ + uint32_t bytes_xferred; +} __packed nvsp_2_msg_post_chimney_rx_request_complete; + +/* + * NvspMessage2TypeAllocateReceiveBuffer + */ +typedef struct nvsp_2_msg_alloc_rx_buf_ { + /* + * Allocation ID to match the allocation request and response + */ + uint32_t allocation_id; + + /* + * Length of the VM shared memory receive buffer that needs to + * be allocated + */ + uint32_t length; +} __packed nvsp_2_msg_alloc_rx_buf; + +/* + * NvspMessage2TypeAllocateReceiveBufferComplete + */ +typedef struct nvsp_2_msg_alloc_rx_buf_complete_ { + /* + * The NDIS_STATUS code for buffer allocation + */ + uint32_t status; + + /* + * Allocation ID from NVSP_2_MESSAGE_ALLOCATE_RECEIVE_BUFFER + */ + uint32_t allocation_id; + + /* + * GPADL handle for the allocated receive buffer + */ + uint32_t gpadl_handle; + + /* + * Receive buffer ID that is further used in + * NvspMessage2SendVmqRndisPacket + */ + uint64_t rx_buf_id; +} __packed nvsp_2_msg_alloc_rx_buf_complete; + +/* + * NvspMessage2TypeFreeReceiveBuffer + */ +typedef struct nvsp_2_msg_free_rx_buf_ { + /* + * Receive buffer ID previous returned in + * NvspMessage2TypeAllocateReceiveBufferComplete message + */ + uint64_t rx_buf_id; +} __packed nvsp_2_msg_free_rx_buf; + +/* + * This structure is used in defining the buffers in + * NVSP_2_MESSAGE_SEND_VMQ_RNDIS_PACKET structure + */ +typedef struct nvsp_xfer_page_range_ { + /* + * Specifies the ID of the receive buffer that has the buffer. This + * ID can be the general receive buffer ID specified in + * NvspMessage1TypeSendReceiveBuffer or it can be the shared memory + * receive buffer ID allocated by the VSC and specified in + * NvspMessage2TypeAllocateReceiveBufferComplete message + */ + uint64_t xfer_page_set_id; + + /* + * Number of bytes + */ + uint32_t byte_count; + + /* + * Offset in bytes from the beginning of the buffer + */ + uint32_t byte_offset; +} __packed nvsp_xfer_page_range; + +/* + * NvspMessage2SendVmqRndisPacket + */ +typedef struct nvsp_2_msg_send_vmq_rndis_pkt_ { + /* + * This field is specified by RNIDS. They assume there's + * two different channels of communication. However, + * the Network VSP only has one. Therefore, the channel + * travels with the RNDIS packet. It must be RMC_DATA + */ + uint32_t channel_type; + + /* + * Only the Range element corresponding to the RNDIS header of + * the first RNDIS message in the multiple RNDIS messages sent + * in one NVSP message. Information about the data portions as well + * as the subsequent RNDIS messages in the same NVSP message are + * embedded in the RNDIS header itself + */ + nvsp_xfer_page_range range; +} __packed nvsp_2_msg_send_vmq_rndis_pkt; + +/* + * This message is used by the VSC to complete + * a RNDIS VMQ message to the VSP. At this point, + * the initiator of this message can use any resources + * associated with the original RNDIS VMQ packet. + */ +typedef struct nvsp_2_msg_send_vmq_rndis_pkt_complete_ +{ + uint32_t status; +} __packed nvsp_2_msg_send_vmq_rndis_pkt_complete; + + +typedef union nvsp_1_msg_uber_ { + nvsp_1_msg_send_ndis_version send_ndis_vers; + + nvsp_1_msg_send_rx_buf send_rx_buf; + nvsp_1_msg_send_rx_buf_complete send_rx_buf_complete; + nvsp_1_msg_revoke_rx_buf revoke_rx_buf; + + nvsp_1_msg_send_send_buf send_send_buf; + nvsp_1_msg_send_send_buf_complete send_send_buf_complete; + nvsp_1_msg_revoke_send_buf revoke_send_buf; + + nvsp_1_msg_send_rndis_pkt send_rndis_pkt; + nvsp_1_msg_send_rndis_pkt_complete send_rndis_pkt_complete; +} __packed nvsp_1_msg_uber; + + +typedef union nvsp_2_msg_uber_ { + nvsp_2_msg_send_ndis_config send_ndis_config; + + nvsp_2_msg_send_chimney_buf send_chimney_buf; + nvsp_2_msg_send_chimney_buf_complete send_chimney_buf_complete; + nvsp_2_msg_revoke_chimney_buf revoke_chimney_buf; + + nvsp_2_msg_resume_chimney_rx_indication resume_chimney_rx_indication; + nvsp_2_msg_terminate_chimney terminate_chimney; + nvsp_2_msg_terminate_chimney_complete terminate_chimney_complete; + nvsp_2_msg_indicate_chimney_event indicate_chimney_event; + + nvsp_2_msg_send_chimney_pkt send_chimney_packet; + nvsp_2_msg_send_chimney_pkt_complete send_chimney_packet_complete; + nvsp_2_msg_post_chimney_rx_request post_chimney_rx_request; + nvsp_2_msg_post_chimney_rx_request_complete + post_chimney_rx_request_complete; + + nvsp_2_msg_alloc_rx_buf alloc_rx_buffer; + nvsp_2_msg_alloc_rx_buf_complete alloc_rx_buffer_complete; + nvsp_2_msg_free_rx_buf free_rx_buffer; + + nvsp_2_msg_send_vmq_rndis_pkt send_vmq_rndis_pkt; + nvsp_2_msg_send_vmq_rndis_pkt_complete send_vmq_rndis_pkt_complete; + nvsp_2_msg_alloc_chimney_handle alloc_chimney_handle; + nvsp_2_msg_alloc_chimney_handle_complete alloc_chimney_handle_complete; +} __packed nvsp_2_msg_uber; + + +typedef union nvsp_all_msgs_ { + nvsp_msg_init_uber init_msgs; + nvsp_1_msg_uber vers_1_msgs; + nvsp_2_msg_uber vers_2_msgs; +} __packed nvsp_all_msgs; + +/* + * ALL Messages + */ +typedef struct nvsp_msg_ { + nvsp_msg_hdr hdr; + nvsp_all_msgs msgs; +} __packed nvsp_msg; + + +/* + * The following arguably belongs in a separate header file + */ + +/* + * Defines + */ + +#define NETVSC_SEND_BUFFER_SIZE (64*1024) /* 64K */ +#define NETVSC_SEND_BUFFER_ID 0xface + + +#define NETVSC_RECEIVE_BUFFER_SIZE (1024*1024) /* 1MB */ + +#define NETVSC_RECEIVE_BUFFER_ID 0xcafe + +#define NETVSC_RECEIVE_SG_COUNT 1 + +/* Preallocated receive packets */ +#define NETVSC_RECEIVE_PACKETLIST_COUNT 256 + +/* + * Maximum MTU we permit to be configured for a netvsc interface. + * When the code was developed, a max MTU of 12232 was tested and + * proven to work. 9K is a reasonable maximum for an Ethernet. + */ +#define NETVSC_MAX_CONFIGURABLE_MTU (9 * 1024) + +/* + * Data types + */ + +/* + * Per netvsc channel-specific + */ +typedef struct netvsc_dev_ { + struct hv_device *dev; + int num_outstanding_sends; + + /* List of free preallocated NETVSC_PACKET to represent RX packet */ + STAILQ_HEAD(PQ, netvsc_packet_) myrx_packet_list; + struct mtx rx_pkt_list_lock; + + /* Send buffer allocated by us but manages by NetVSP */ + void *send_buf; + uint32_t send_buf_size; + uint32_t send_buf_gpadl_handle; + uint32_t send_section_size; + + /* Receive buffer allocated by us but managed by NetVSP */ + void *rx_buf; + uint32_t rx_buf_size; + uint32_t rx_buf_gpadl_handle; + uint32_t rx_section_count; + nvsp_1_rx_buf_section *rx_sections; + + /* Used for NetVSP initialization protocol */ + struct sema channel_init_sema; + nvsp_msg channel_init_packet; + + nvsp_msg revoke_packet; + /*uint8_t hw_mac_addr[HW_MACADDR_LEN];*/ + + /* Holds rndis device info */ + void *extension; + + hv_bool_uint8_t destroy; + /* Negotiated NVSP version */ + uint32_t nvsp_version; +} netvsc_dev; + + +typedef void (*pfn_on_send_rx_completion)(void *); + +#define NETVSC_DEVICE_RING_BUFFER_SIZE (64 * PAGE_SIZE) +#define NETVSC_PACKET_MAXPAGE 16 + + +typedef struct xfer_page_packet_ { + /* + * This needs to be here because the network RX code casts + * an instantiation of this structure to a netvsc_packet. + */ + STAILQ_ENTRY(netvsc_packet_) mylist_entry; + + uint32_t count; +} xfer_page_packet; + +typedef struct netvsc_packet_ { + /* + * List used when enqueued on &net_dev->rx_packet_list, + * and when enqueued within the netvsc code + */ + STAILQ_ENTRY(netvsc_packet_) mylist_entry; + struct hv_device *device; + hv_bool_uint8_t is_data_pkt; /* One byte */ + uint16_t vlan_tci; + xfer_page_packet *xfer_page_pkt; + + /* Completion */ + union { + struct { + uint64_t rx_completion_tid; + void *rx_completion_context; + /* This is no longer used */ + pfn_on_send_rx_completion on_rx_completion; + } rx; + struct { + uint64_t send_completion_tid; + void *send_completion_context; + /* Still used in netvsc and filter code */ + pfn_on_send_rx_completion on_send_completion; + } send; + } compl; + + void *extension; + uint32_t tot_data_buf_len; + uint32_t page_buf_count; + hv_vmbus_page_buffer page_buffers[NETVSC_PACKET_MAXPAGE]; +} netvsc_packet; + +typedef struct { + uint8_t mac_addr[6]; /* Assumption unsigned long */ + hv_bool_uint8_t link_state; +} netvsc_device_info; + +/* + * Device-specific softc structure + */ +typedef struct hn_softc { + struct ifnet *hn_ifp; + struct arpcom arpcom; + device_t hn_dev; + uint8_t hn_unit; + int hn_carrier; + int hn_if_flags; + struct mtx hn_lock; + int hn_initdone; + struct hv_device *hn_dev_obj; + netvsc_dev *net_dev; +} hn_softc_t; + + +/* + * Externs + */ +extern int hv_promisc_mode; + +extern void netvsc_linkstatus_callback(struct hv_device *device_obj, + uint32_t status); +extern int netvsc_recv(struct hv_device *device_obj, netvsc_packet *packet); +extern void netvsc_xmit_completion(void *context); + +extern void hv_nv_on_receive_completion(void *context); +extern netvsc_dev *hv_nv_on_device_add(struct hv_device *device, void *additional_info); +extern int hv_nv_on_device_remove(struct hv_device *device, + boolean_t destroy_channel); +extern int hv_nv_on_send(struct hv_device *device, netvsc_packet *pkt); + +#endif /* __HV_NET_VSC_H__ */ + diff --git a/sys/dev/hyperv/netvsc/hv_netvsc_drv_freebsd.c b/sys/dev/hyperv/netvsc/hv_netvsc_drv_freebsd.c new file mode 100644 index 00000000000..74700061e54 --- /dev/null +++ b/sys/dev/hyperv/netvsc/hv_netvsc_drv_freebsd.c @@ -0,0 +1,948 @@ +/*- + * Copyright (c) 2010-2012 Citrix Inc. + * Copyright (c) 2009-2012 Microsoft Corp. + * Copyright (c) 2012 NetApp Inc. + * 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 unmodified, 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +/*- + * Copyright (c) 2004-2006 Kip Macy + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include "hv_net_vsc.h" +#include "hv_rndis.h" +#include "hv_rndis_filter.h" + + +/* Short for Hyper-V network interface */ +#define NETVSC_DEVNAME "hn" + +/* + * It looks like offset 0 of buf is reserved to hold the softc pointer. + * The sc pointer evidently not needed, and is not presently populated. + * The packet offset is where the netvsc_packet starts in the buffer. + */ +#define HV_NV_SC_PTR_OFFSET_IN_BUF 0 +#define HV_NV_PACKET_OFFSET_IN_BUF 16 + + +/* + * Data types + */ + +struct hv_netvsc_driver_context { + uint32_t drv_inited; +}; + +/* + * Be aware that this sleepable mutex will exhibit WITNESS errors when + * certain TCP and ARP code paths are taken. This appears to be a + * well-known condition, as all other drivers checked use a sleeping + * mutex to protect their transmit paths. + * Also Be aware that mutexes do not play well with semaphores, and there + * is a conflicting semaphore in a certain channel code path. + */ +#define NV_LOCK_INIT(_sc, _name) \ + mtx_init(&(_sc)->hn_lock, _name, MTX_NETWORK_LOCK, MTX_DEF) +#define NV_LOCK(_sc) mtx_lock(&(_sc)->hn_lock) +#define NV_LOCK_ASSERT(_sc) mtx_assert(&(_sc)->hn_lock, MA_OWNED) +#define NV_UNLOCK(_sc) mtx_unlock(&(_sc)->hn_lock) +#define NV_LOCK_DESTROY(_sc) mtx_destroy(&(_sc)->hn_lock) + + +/* + * Globals + */ + +int hv_promisc_mode = 0; /* normal mode by default */ + +/* The one and only one */ +static struct hv_netvsc_driver_context g_netvsc_drv; + + +/* + * Forward declarations + */ +static void hn_stop(hn_softc_t *sc); +static void hn_ifinit_locked(hn_softc_t *sc); +static void hn_ifinit(void *xsc); +static int hn_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data); +static int hn_start_locked(struct ifnet *ifp); +static void hn_start(struct ifnet *ifp); + + +/* + * NetVsc driver initialization + * Note: Filter init is no longer required + */ +static int +netvsc_drv_init(void) +{ + return (0); +} + +/* + * NetVsc global initialization entry point + */ +static void +netvsc_init(void) +{ + printf("Netvsc initializing... "); + + /* + * XXXKYS: cleanup initialization + */ + if (!cold && !g_netvsc_drv.drv_inited) { + g_netvsc_drv.drv_inited = 1; + netvsc_drv_init(); + } else { + printf("Already initialized!\n"); + } +} + +/* {F8615163-DF3E-46c5-913F-F2D2F965ED0E} */ +static const hv_guid g_net_vsc_device_type = { + .data = {0x63, 0x51, 0x61, 0xF8, 0x3E, 0xDF, 0xc5, 0x46, + 0x91, 0x3F, 0xF2, 0xD2, 0xF9, 0x65, 0xED, 0x0E} +}; + +/* + * Standard probe entry point. + * + */ +static int +netvsc_probe(device_t dev) +{ + const char *p; + + p = vmbus_get_type(dev); + if (!memcmp(p, &g_net_vsc_device_type.data, sizeof(hv_guid))) { + device_set_desc(dev, "Synthetic Network Interface"); + printf("Netvsc probe... DONE \n"); + + return (0); + } + + return (ENXIO); +} + +/* + * Standard attach entry point. + * + * Called when the driver is loaded. It allocates needed resources, + * and initializes the "hardware" and software. + */ +static int +netvsc_attach(device_t dev) +{ + struct hv_device *device_ctx = vmbus_get_devctx(dev); + netvsc_device_info device_info; + hn_softc_t *sc; + int unit = device_get_unit(dev); + struct ifnet *ifp; + int ret; + + netvsc_init(); + + sc = device_get_softc(dev); + if (sc == NULL) { + return (ENOMEM); + } + + bzero(sc, sizeof(hn_softc_t)); + sc->hn_unit = unit; + sc->hn_dev = dev; + + NV_LOCK_INIT(sc, "NetVSCLock"); + + sc->hn_dev_obj = device_ctx; + + ifp = sc->hn_ifp = sc->arpcom.ac_ifp = if_alloc(IFT_ETHER); + ifp->if_softc = sc; + + if_initname(ifp, device_get_name(dev), device_get_unit(dev)); + ifp->if_dunit = unit; + ifp->if_dname = NETVSC_DEVNAME; + + ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; + ifp->if_ioctl = hn_ioctl; + ifp->if_start = hn_start; + ifp->if_init = hn_ifinit; + /* needed by hv_rf_on_device_add() code */ + ifp->if_mtu = ETHERMTU; + IFQ_SET_MAXLEN(&ifp->if_snd, 512); + ifp->if_snd.ifq_drv_maxlen = 511; + IFQ_SET_READY(&ifp->if_snd); + + /* + * Tell upper layers that we support full VLAN capability. + */ + ifp->if_data.ifi_hdrlen = sizeof(struct ether_vlan_header); + ifp->if_capabilities |= IFCAP_VLAN_HWTAGGING | IFCAP_VLAN_MTU; + ifp->if_capenable |= IFCAP_VLAN_HWTAGGING | IFCAP_VLAN_MTU; + + ret = hv_rf_on_device_add(device_ctx, &device_info); + if (ret != 0) { + if_free(ifp); + + return (ret); + } + if (device_info.link_state == 0) { + sc->hn_carrier = 1; + } + + ether_ifattach(ifp, device_info.mac_addr); + + return (0); +} + +/* + * Standard detach entry point + */ +static int +netvsc_detach(device_t dev) +{ + struct hv_device *hv_device = vmbus_get_devctx(dev); + + printf("netvsc_detach\n"); + + /* + * XXXKYS: Need to clean up all our + * driver state; this is the driver + * unloading. + */ + + /* + * XXXKYS: Need to stop outgoing traffic and unregister + * the netdevice. + */ + + hv_rf_on_device_remove(hv_device, HV_RF_NV_DESTROY_CHANNEL); + + return (0); +} + +/* + * Standard shutdown entry point + */ +static int +netvsc_shutdown(device_t dev) +{ + return (0); +} + +/* + * Send completion processing + * + * Note: It looks like offset 0 of buf is reserved to hold the softc + * pointer. The sc pointer is not currently needed in this function, and + * it is not presently populated by the TX function. + */ +void +netvsc_xmit_completion(void *context) +{ + netvsc_packet *packet = (netvsc_packet *)context; + struct mbuf *mb; + uint8_t *buf; + + mb = (struct mbuf *)packet->compl.send.send_completion_tid; + buf = ((uint8_t *)packet) - HV_NV_PACKET_OFFSET_IN_BUF; + + free(buf, M_DEVBUF); + + if (mb != NULL) { + m_freem(mb); + } +} + +/* + * Start a transmit of one or more packets + */ +static int +hn_start_locked(struct ifnet *ifp) +{ + hn_softc_t *sc = ifp->if_softc; + struct hv_device *device_ctx = vmbus_get_devctx(sc->hn_dev); + uint8_t *buf; + netvsc_packet *packet; + struct mbuf *m_head, *m; + struct mbuf *mc_head = NULL; + int i; + int num_frags; + int len; + int xlen; + int rppi_size; + int retries = 0; + int ret = 0; + + while (!IFQ_DRV_IS_EMPTY(&sc->hn_ifp->if_snd)) { + IFQ_DRV_DEQUEUE(&sc->hn_ifp->if_snd, m_head); + if (m_head == NULL) { + break; + } + + len = 0; + num_frags = 0; + xlen = 0; + + /* Walk the mbuf list computing total length and num frags */ + for (m = m_head; m != NULL; m = m->m_next) { + if (m->m_len != 0) { + num_frags++; + len += m->m_len; + } + } + + /* + * Reserve the number of pages requested. Currently, + * one page is reserved for the message in the RNDIS + * filter packet + */ + num_frags += HV_RF_NUM_TX_RESERVED_PAGE_BUFS; + + /* If exceeds # page_buffers in netvsc_packet */ + if (num_frags > NETVSC_PACKET_MAXPAGE) { + m_freem(m); + + return (EINVAL); + } + + rppi_size = 0; + if (m_head->m_flags & M_VLANTAG) { + rppi_size = sizeof(rndis_per_packet_info) + + sizeof(ndis_8021q_info); + } + + /* + * Allocate a buffer with space for a netvsc packet plus a + * number of reserved areas. First comes a (currently 16 + * bytes, currently unused) reserved data area. Second is + * the netvsc_packet, which includes (currently 4) page + * buffers. Third (optional) is a rndis_per_packet_info + * struct, but only if a VLAN tag should be inserted into the + * Ethernet frame by the Hyper-V infrastructure. Fourth is + * an area reserved for an rndis_filter_packet struct. + * Changed malloc to M_NOWAIT to avoid sleep under spin lock. + * No longer reserving extra space for page buffers, as they + * are already part of the netvsc_packet. + */ + buf = malloc(HV_NV_PACKET_OFFSET_IN_BUF + + sizeof(netvsc_packet) + rppi_size + + sizeof(rndis_filter_packet), + M_DEVBUF, M_ZERO | M_NOWAIT); + if (buf == NULL) { + m_freem(m); + + return (ENOMEM); + } + + packet = (netvsc_packet *)(buf + HV_NV_PACKET_OFFSET_IN_BUF); + *(vm_offset_t *)buf = HV_NV_SC_PTR_OFFSET_IN_BUF; + + /* + * extension points to the area reserved for the + * rndis_filter_packet, which is placed just after + * the netvsc_packet (and rppi struct, if present; + * length is updated later). + */ + packet->extension = packet + 1; + + /* Set up the rndis header */ + packet->page_buf_count = num_frags; + + /* Initialize it from the mbuf */ + packet->tot_data_buf_len = len; + + /* + * If the Hyper-V infrastructure needs to embed a VLAN tag, + * initialize netvsc_packet and rppi struct values as needed. + */ + if (rppi_size) { + /* Lower layers need the VLAN TCI */ + packet->vlan_tci = m_head->m_pkthdr.ether_vtag; + } + + /* + * Fill the page buffers with mbuf info starting at index + * HV_RF_NUM_TX_RESERVED_PAGE_BUFS. + */ + i = HV_RF_NUM_TX_RESERVED_PAGE_BUFS; + for (m = m_head; m != NULL; m = m->m_next) { + if (m->m_len) { + vm_offset_t paddr = + vtophys(mtod(m, vm_offset_t)); + packet->page_buffers[i].pfn = + paddr >> PAGE_SHIFT; + packet->page_buffers[i].offset = + paddr & (PAGE_SIZE - 1); + packet->page_buffers[i].length = m->m_len; + i++; + } + } + + /* + * If bpf, copy the mbuf chain. This is less expensive than + * it appears; the mbuf clusters are not copied, only their + * reference counts are incremented. + * Needed to avoid a race condition where the completion + * callback is invoked, freeing the mbuf chain, before the + * bpf_mtap code has a chance to run. + */ + if (ifp->if_bpf) { + mc_head = m_copypacket(m_head, M_DONTWAIT); + } +retry_send: + /* Set the completion routine */ + packet->compl.send.on_send_completion = netvsc_xmit_completion; + packet->compl.send.send_completion_context = packet; + packet->compl.send.send_completion_tid = (uint64_t)m_head; + + /* Removed critical_enter(), does not appear necessary */ + ret = hv_rf_on_send(device_ctx, packet); + + if (ret == 0) { + ifp->if_opackets++; + /* if bpf && mc_head, call bpf_mtap code */ + if (mc_head) { + ETHER_BPF_MTAP(ifp, mc_head); + } + } else { + retries++; + if (retries < 4) { + goto retry_send; + } + + IF_PREPEND(&ifp->if_snd, m_head); + ifp->if_drv_flags |= IFF_DRV_OACTIVE; + + /* + * Null the mbuf pointer so the completion function + * does not free the mbuf chain. We just pushed the + * mbuf chain back on the if_snd queue. + */ + packet->compl.send.send_completion_tid = 0; + + /* + * Release the resources since we will not get any + * send completion + */ + netvsc_xmit_completion(packet); + } + + /* if bpf && mc_head, free the mbuf chain copy */ + if (mc_head) { + m_freem(mc_head); + } + } + + return (ret); +} + +/* + * Link up/down notification + */ +void +netvsc_linkstatus_callback(struct hv_device *device_obj, uint32_t status) +{ + hn_softc_t *sc = device_get_softc(device_obj->device); + + if (sc == NULL) { + return; + } + + if (status == 1) { + sc->hn_carrier = 1; + } else { + sc->hn_carrier = 0; + } +} + +/* + * Append the specified data to the indicated mbuf chain, + * Extend the mbuf chain if the new data does not fit in + * existing space. + * + * This is a minor rewrite of m_append() from sys/kern/uipc_mbuf.c. + * There should be an equivalent in the kernel mbuf code, + * but there does not appear to be one yet. + * + * Differs from m_append() in that additional mbufs are + * allocated with cluster size MJUMPAGESIZE, and filled + * accordingly. + * + * Return 1 if able to complete the job; otherwise 0. + */ +static int +hv_m_append(struct mbuf *m0, int len, c_caddr_t cp) +{ + struct mbuf *m, *n; + int remainder, space; + + for (m = m0; m->m_next != NULL; m = m->m_next) + ; + remainder = len; + space = M_TRAILINGSPACE(m); + if (space > 0) { + /* + * Copy into available space. + */ + if (space > remainder) + space = remainder; + bcopy(cp, mtod(m, caddr_t) + m->m_len, space); + m->m_len += space; + cp += space; + remainder -= space; + } + while (remainder > 0) { + /* + * Allocate a new mbuf; could check space + * and allocate a cluster instead. + */ + n = m_getjcl(M_DONTWAIT, m->m_type, 0, MJUMPAGESIZE); + if (n == NULL) + break; + n->m_len = min(MJUMPAGESIZE, remainder); + bcopy(cp, mtod(n, caddr_t), n->m_len); + cp += n->m_len; + remainder -= n->m_len; + m->m_next = n; + m = n; + } + if (m0->m_flags & M_PKTHDR) + m0->m_pkthdr.len += len - remainder; + + return (remainder == 0); +} + + +/* + * Called when we receive a data packet from the "wire" on the + * specified device + * + * Note: This is no longer used as a callback + */ +int +netvsc_recv(struct hv_device *device_ctx, netvsc_packet *packet) +{ + hn_softc_t *sc = (hn_softc_t *)device_get_softc(device_ctx->device); + struct mbuf *m_new; + struct ifnet *ifp = sc->hn_ifp; + int size; + int i; + + if (sc == NULL) { + return (0); /* TODO: KYS how can this be! */ + } + + ifp = sc->arpcom.ac_ifp; + + if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) { + return (0); + } + + /* + * Bail out if packet contains more data than configured MTU. + */ + if (packet->tot_data_buf_len > (ifp->if_mtu + ETHER_HDR_LEN)) { + return (0); + } + + /* + * Get an mbuf with a cluster. For packets 2K or less, + * get a standard 2K cluster. For anything larger, get a + * 4K cluster. Any buffers larger than 4K can cause problems + * if looped around to the Hyper-V TX channel, so avoid them. + */ + size = MCLBYTES; + + if (packet->tot_data_buf_len > MCLBYTES) { + /* 4096 */ + size = MJUMPAGESIZE; + } + + m_new = m_getjcl(M_DONTWAIT, MT_DATA, M_PKTHDR, size); + + if (m_new == NULL) + return (0); + + /* + * Remove trailing junk from RX data buffer. + * Fixme: This will not work for multiple Hyper-V RX buffers. + * Fortunately, the channel gathers all RX data into one buffer. + * + * L2 frame length, with L2 header, not including CRC + */ + packet->page_buffers[0].length = packet->tot_data_buf_len; + + /* + * Copy the received packet to one or more mbufs. + * The copy is required since the memory pointed to by netvsc_packet + * cannot be deallocated + */ + for (i=0; i < packet->page_buf_count; i++) { + /* Shift virtual page number to form virtual page address */ + uint8_t *vaddr = (uint8_t *) + (packet->page_buffers[i].pfn << PAGE_SHIFT); + + hv_m_append(m_new, packet->page_buffers[i].length, + vaddr + packet->page_buffers[i].offset); + } + + m_new->m_pkthdr.rcvif = ifp; + + if ((packet->vlan_tci != 0) && + (ifp->if_capenable & IFCAP_VLAN_HWTAGGING) != 0) { + m_new->m_pkthdr.ether_vtag = packet->vlan_tci; + m_new->m_flags |= M_VLANTAG; + } + + /* + * Note: Moved RX completion back to hv_nv_on_receive() so all + * messages (not just data messages) will trigger a response. + */ + + ifp->if_ipackets++; + + /* We're not holding the lock here, so don't release it */ + (*ifp->if_input)(ifp, m_new); + + return (0); +} + +/* + * Standard ioctl entry point. Called when the user wants to configure + * the interface. + */ +static int +hn_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) +{ + hn_softc_t *sc = ifp->if_softc; + struct ifreq *ifr = (struct ifreq *)data; + netvsc_device_info device_info; + struct hv_device *hn_dev; + int mask, error = 0; + + switch(cmd) { + + case SIOCSIFADDR: + case SIOCGIFADDR: + error = ether_ioctl(ifp, cmd, data); + break; + case SIOCSIFMTU: + hn_dev = vmbus_get_devctx(sc->hn_dev); + + NV_LOCK(sc); + + if (ifr->ifr_mtu > NETVSC_MAX_CONFIGURABLE_MTU) { + error = EINVAL; + NV_UNLOCK(sc); + break; + } + /* Obtain and record requested MTU */ + ifp->if_mtu = ifr->ifr_mtu; + + /* + * We must remove and add back the device to cause the new + * MTU to take effect. This includes tearing down, but not + * deleting the channel, then bringing it back up. + */ + error = hv_rf_on_device_remove(hn_dev, HV_RF_NV_RETAIN_CHANNEL); + if (error) { + NV_UNLOCK(sc); + break; + } + error = hv_rf_on_device_add(hn_dev, &device_info); + if (error) { + NV_UNLOCK(sc); + break; + } + + hn_ifinit_locked(sc); + + NV_UNLOCK(sc); + break; + case SIOCSIFFLAGS: + NV_LOCK(sc); + if (ifp->if_flags & IFF_UP) { + /* + * If only the state of the PROMISC flag changed, + * then just use the 'set promisc mode' command + * instead of reinitializing the entire NIC. Doing + * a full re-init means reloading the firmware and + * waiting for it to start up, which may take a + * second or two. + */ +#ifdef notyet + /* Fixme: Promiscuous mode? */ + /* No promiscuous mode with Xen */ + if (ifp->if_drv_flags & IFF_DRV_RUNNING && + ifp->if_flags & IFF_PROMISC && + !(sc->hn_if_flags & IFF_PROMISC)) { + /* do something here for Hyper-V */ + ; +/* XN_SETBIT(sc, XN_RX_MODE, */ +/* XN_RXMODE_RX_PROMISC); */ + } else if (ifp->if_drv_flags & IFF_DRV_RUNNING && + !(ifp->if_flags & IFF_PROMISC) && + sc->hn_if_flags & IFF_PROMISC) { + /* do something here for Hyper-V */ + ; +/* XN_CLRBIT(sc, XN_RX_MODE, */ +/* XN_RXMODE_RX_PROMISC); */ + } else +#endif + hn_ifinit_locked(sc); + } else { + if (ifp->if_drv_flags & IFF_DRV_RUNNING) { + hn_stop(sc); + } + } + sc->hn_if_flags = ifp->if_flags; + NV_UNLOCK(sc); + error = 0; + break; + case SIOCSIFCAP: + mask = ifr->ifr_reqcap ^ ifp->if_capenable; + if (mask & IFCAP_HWCSUM) { + if (IFCAP_HWCSUM & ifp->if_capenable) { + ifp->if_capenable &= ~IFCAP_HWCSUM; + } else { + ifp->if_capenable |= IFCAP_HWCSUM; + } + } + error = 0; + break; + case SIOCADDMULTI: + case SIOCDELMULTI: +#ifdef notyet + /* Fixme: Multicast mode? */ + if (ifp->if_drv_flags & IFF_DRV_RUNNING) { + NV_LOCK(sc); + netvsc_setmulti(sc); + NV_UNLOCK(sc); + error = 0; + } +#endif + /* FALLTHROUGH */ + case SIOCSIFMEDIA: + case SIOCGIFMEDIA: + error = EINVAL; + break; + default: + error = ether_ioctl(ifp, cmd, data); + break; + } + + return (error); +} + +/* + * + */ +static void +hn_stop(hn_softc_t *sc) +{ + struct ifnet *ifp; + int ret; + struct hv_device *device_ctx = vmbus_get_devctx(sc->hn_dev); + + NV_LOCK_ASSERT(sc); + ifp = sc->hn_ifp; + + printf(" Closing Device ...\n"); + + ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE); + sc->hn_initdone = 0; + + ret = hv_rf_on_close(device_ctx); +} + +/* + * FreeBSD transmit entry point + */ +static void +hn_start(struct ifnet *ifp) +{ + hn_softc_t *sc; + + sc = ifp->if_softc; + NV_LOCK(sc); + hn_start_locked(ifp); + NV_UNLOCK(sc); +} + +/* + * + */ +static void +hn_ifinit_locked(hn_softc_t *sc) +{ + struct ifnet *ifp; + struct hv_device *device_ctx = vmbus_get_devctx(sc->hn_dev); + int ret; + + NV_LOCK_ASSERT(sc); + + ifp = sc->hn_ifp; + + if (ifp->if_drv_flags & IFF_DRV_RUNNING) { + return; + } + + hv_promisc_mode = 1; + + ret = hv_rf_on_open(device_ctx); + if (ret != 0) { + return; + } else { + sc->hn_initdone = 1; + } + ifp->if_drv_flags |= IFF_DRV_RUNNING; + ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; +} + +/* + * + */ +static void +hn_ifinit(void *xsc) +{ + hn_softc_t *sc = xsc; + + NV_LOCK(sc); + hn_ifinit_locked(sc); + NV_UNLOCK(sc); +} + +#ifdef LATER +/* + * + */ +static void +hn_watchdog(struct ifnet *ifp) +{ + hn_softc_t *sc; + sc = ifp->if_softc; + + printf("hn%d: watchdog timeout -- resetting\n", sc->hn_unit); + hn_ifinit(sc); /*???*/ + ifp->if_oerrors++; +} +#endif + +static device_method_t netvsc_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, netvsc_probe), + DEVMETHOD(device_attach, netvsc_attach), + DEVMETHOD(device_detach, netvsc_detach), + DEVMETHOD(device_shutdown, netvsc_shutdown), + + { 0, 0 } +}; + +static driver_t netvsc_driver = { + NETVSC_DEVNAME, + netvsc_methods, + sizeof(hn_softc_t) +}; + +static devclass_t netvsc_devclass; + +DRIVER_MODULE(hn, vmbus, netvsc_driver, netvsc_devclass, 0, 0); +MODULE_VERSION(hn, 1); +MODULE_DEPEND(hn, vmbus, 1, 1, 1); +SYSINIT(netvsc_initx, SI_SUB_KTHREAD_IDLE, SI_ORDER_MIDDLE + 1, netvsc_init, + NULL); + diff --git a/sys/dev/hyperv/netvsc/hv_rndis.h b/sys/dev/hyperv/netvsc/hv_rndis.h new file mode 100644 index 00000000000..819cab5a729 --- /dev/null +++ b/sys/dev/hyperv/netvsc/hv_rndis.h @@ -0,0 +1,911 @@ +/*- + * Copyright (c) 2009-2012 Microsoft Corp. + * Copyright (c) 2010-2012 Citrix Inc. + * Copyright (c) 2012 NetApp Inc. + * 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 unmodified, 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#ifndef __HV_RNDIS_H__ +#define __HV_RNDIS_H__ + + +/* + * NDIS protocol version numbers + */ +#define NDIS_VERSION_5_0 0x00050000 +#define NDIS_VERSION_5_1 0x00050001 +#define NDIS_VERSION_6_0 0x00060000 +#define NDIS_VERSION (NDIS_VERSION_5_1) + +/* + * Status codes + */ + +#define STATUS_SUCCESS (0x00000000L) +#define STATUS_UNSUCCESSFUL (0xC0000001L) +#define STATUS_PENDING (0x00000103L) +#define STATUS_INSUFFICIENT_RESOURCES (0xC000009AL) +#define STATUS_BUFFER_OVERFLOW (0x80000005L) +#define STATUS_NOT_SUPPORTED (0xC00000BBL) + +#define RNDIS_STATUS_SUCCESS (STATUS_SUCCESS) +#define RNDIS_STATUS_PENDING (STATUS_PENDING) +#define RNDIS_STATUS_NOT_RECOGNIZED (0x00010001L) +#define RNDIS_STATUS_NOT_COPIED (0x00010002L) +#define RNDIS_STATUS_NOT_ACCEPTED (0x00010003L) +#define RNDIS_STATUS_CALL_ACTIVE (0x00010007L) + +#define RNDIS_STATUS_ONLINE (0x40010003L) +#define RNDIS_STATUS_RESET_START (0x40010004L) +#define RNDIS_STATUS_RESET_END (0x40010005L) +#define RNDIS_STATUS_RING_STATUS (0x40010006L) +#define RNDIS_STATUS_CLOSED (0x40010007L) +#define RNDIS_STATUS_WAN_LINE_UP (0x40010008L) +#define RNDIS_STATUS_WAN_LINE_DOWN (0x40010009L) +#define RNDIS_STATUS_WAN_FRAGMENT (0x4001000AL) +#define RNDIS_STATUS_MEDIA_CONNECT (0x4001000BL) +#define RNDIS_STATUS_MEDIA_DISCONNECT (0x4001000CL) +#define RNDIS_STATUS_HARDWARE_LINE_UP (0x4001000DL) +#define RNDIS_STATUS_HARDWARE_LINE_DOWN (0x4001000EL) +#define RNDIS_STATUS_INTERFACE_UP (0x4001000FL) +#define RNDIS_STATUS_INTERFACE_DOWN (0x40010010L) +#define RNDIS_STATUS_MEDIA_BUSY (0x40010011L) +#define RNDIS_STATUS_MEDIA_SPECIFIC_INDICATION (0x40010012L) +#define RNDIS_STATUS_WW_INDICATION RNDIS_STATUS_MEDIA_SPECIFIC_INDICATION +#define RNDIS_STATUS_LINK_SPEED_CHANGE (0x40010013L) + +#define RNDIS_STATUS_NOT_RESETTABLE (0x80010001L) +#define RNDIS_STATUS_SOFT_ERRORS (0x80010003L) +#define RNDIS_STATUS_HARD_ERRORS (0x80010004L) +#define RNDIS_STATUS_BUFFER_OVERFLOW (STATUS_BUFFER_OVERFLOW) + +#define RNDIS_STATUS_FAILURE (STATUS_UNSUCCESSFUL) +#define RNDIS_STATUS_RESOURCES (STATUS_INSUFFICIENT_RESOURCES) +#define RNDIS_STATUS_CLOSING (0xC0010002L) +#define RNDIS_STATUS_BAD_VERSION (0xC0010004L) +#define RNDIS_STATUS_BAD_CHARACTERISTICS (0xC0010005L) +#define RNDIS_STATUS_ADAPTER_NOT_FOUND (0xC0010006L) +#define RNDIS_STATUS_OPEN_FAILED (0xC0010007L) +#define RNDIS_STATUS_DEVICE_FAILED (0xC0010008L) +#define RNDIS_STATUS_MULTICAST_FULL (0xC0010009L) +#define RNDIS_STATUS_MULTICAST_EXISTS (0xC001000AL) +#define RNDIS_STATUS_MULTICAST_NOT_FOUND (0xC001000BL) +#define RNDIS_STATUS_REQUEST_ABORTED (0xC001000CL) +#define RNDIS_STATUS_RESET_IN_PROGRESS (0xC001000DL) +#define RNDIS_STATUS_CLOSING_INDICATING (0xC001000EL) +#define RNDIS_STATUS_NOT_SUPPORTED (STATUS_NOT_SUPPORTED) +#define RNDIS_STATUS_INVALID_PACKET (0xC001000FL) +#define RNDIS_STATUS_OPEN_LIST_FULL (0xC0010010L) +#define RNDIS_STATUS_ADAPTER_NOT_READY (0xC0010011L) +#define RNDIS_STATUS_ADAPTER_NOT_OPEN (0xC0010012L) +#define RNDIS_STATUS_NOT_INDICATING (0xC0010013L) +#define RNDIS_STATUS_INVALID_LENGTH (0xC0010014L) +#define RNDIS_STATUS_INVALID_DATA (0xC0010015L) +#define RNDIS_STATUS_BUFFER_TOO_SHORT (0xC0010016L) +#define RNDIS_STATUS_INVALID_OID (0xC0010017L) +#define RNDIS_STATUS_ADAPTER_REMOVED (0xC0010018L) +#define RNDIS_STATUS_UNSUPPORTED_MEDIA (0xC0010019L) +#define RNDIS_STATUS_GROUP_ADDRESS_IN_USE (0xC001001AL) +#define RNDIS_STATUS_FILE_NOT_FOUND (0xC001001BL) +#define RNDIS_STATUS_ERROR_READING_FILE (0xC001001CL) +#define RNDIS_STATUS_ALREADY_MAPPED (0xC001001DL) +#define RNDIS_STATUS_RESOURCE_CONFLICT (0xC001001EL) +#define RNDIS_STATUS_NO_CABLE (0xC001001FL) + +#define RNDIS_STATUS_INVALID_SAP (0xC0010020L) +#define RNDIS_STATUS_SAP_IN_USE (0xC0010021L) +#define RNDIS_STATUS_INVALID_ADDRESS (0xC0010022L) +#define RNDIS_STATUS_VC_NOT_ACTIVATED (0xC0010023L) +#define RNDIS_STATUS_DEST_OUT_OF_ORDER (0xC0010024L) +#define RNDIS_STATUS_VC_NOT_AVAILABLE (0xC0010025L) +#define RNDIS_STATUS_CELLRATE_NOT_AVAILABLE (0xC0010026L) +#define RNDIS_STATUS_INCOMPATABLE_QOS (0xC0010027L) +#define RNDIS_STATUS_AAL_PARAMS_UNSUPPORTED (0xC0010028L) +#define RNDIS_STATUS_NO_ROUTE_TO_DESTINATION (0xC0010029L) + +#define RNDIS_STATUS_TOKEN_RING_OPEN_ERROR (0xC0011000L) + + +/* + * Object Identifiers used by NdisRequest Query/Set Information + */ + +/* + * General Objects + */ + +#define RNDIS_OID_GEN_SUPPORTED_LIST 0x00010101 +#define RNDIS_OID_GEN_HARDWARE_STATUS 0x00010102 +#define RNDIS_OID_GEN_MEDIA_SUPPORTED 0x00010103 +#define RNDIS_OID_GEN_MEDIA_IN_USE 0x00010104 +#define RNDIS_OID_GEN_MAXIMUM_LOOKAHEAD 0x00010105 +#define RNDIS_OID_GEN_MAXIMUM_FRAME_SIZE 0x00010106 +#define RNDIS_OID_GEN_LINK_SPEED 0x00010107 +#define RNDIS_OID_GEN_TRANSMIT_BUFFER_SPACE 0x00010108 +#define RNDIS_OID_GEN_RECEIVE_BUFFER_SPACE 0x00010109 +#define RNDIS_OID_GEN_TRANSMIT_BLOCK_SIZE 0x0001010A +#define RNDIS_OID_GEN_RECEIVE_BLOCK_SIZE 0x0001010B +#define RNDIS_OID_GEN_VENDOR_ID 0x0001010C +#define RNDIS_OID_GEN_VENDOR_DESCRIPTION 0x0001010D +#define RNDIS_OID_GEN_CURRENT_PACKET_FILTER 0x0001010E +#define RNDIS_OID_GEN_CURRENT_LOOKAHEAD 0x0001010F +#define RNDIS_OID_GEN_DRIVER_VERSION 0x00010110 +#define RNDIS_OID_GEN_MAXIMUM_TOTAL_SIZE 0x00010111 +#define RNDIS_OID_GEN_PROTOCOL_OPTIONS 0x00010112 +#define RNDIS_OID_GEN_MAC_OPTIONS 0x00010113 +#define RNDIS_OID_GEN_MEDIA_CONNECT_STATUS 0x00010114 +#define RNDIS_OID_GEN_MAXIMUM_SEND_PACKETS 0x00010115 +#define RNDIS_OID_GEN_VENDOR_DRIVER_VERSION 0x00010116 +#define RNDIS_OID_GEN_NETWORK_LAYER_ADDRESSES 0x00010118 +#define RNDIS_OID_GEN_TRANSPORT_HEADER_OFFSET 0x00010119 +#define RNDIS_OID_GEN_MACHINE_NAME 0x0001021A +#define RNDIS_OID_GEN_RNDIS_CONFIG_PARAMETER 0x0001021B + +#define RNDIS_OID_GEN_XMIT_OK 0x00020101 +#define RNDIS_OID_GEN_RCV_OK 0x00020102 +#define RNDIS_OID_GEN_XMIT_ERROR 0x00020103 +#define RNDIS_OID_GEN_RCV_ERROR 0x00020104 +#define RNDIS_OID_GEN_RCV_NO_BUFFER 0x00020105 + +#define RNDIS_OID_GEN_DIRECTED_BYTES_XMIT 0x00020201 +#define RNDIS_OID_GEN_DIRECTED_FRAMES_XMIT 0x00020202 +#define RNDIS_OID_GEN_MULTICAST_BYTES_XMIT 0x00020203 +#define RNDIS_OID_GEN_MULTICAST_FRAMES_XMIT 0x00020204 +#define RNDIS_OID_GEN_BROADCAST_BYTES_XMIT 0x00020205 +#define RNDIS_OID_GEN_BROADCAST_FRAMES_XMIT 0x00020206 +#define RNDIS_OID_GEN_DIRECTED_BYTES_RCV 0x00020207 +#define RNDIS_OID_GEN_DIRECTED_FRAMES_RCV 0x00020208 +#define RNDIS_OID_GEN_MULTICAST_BYTES_RCV 0x00020209 +#define RNDIS_OID_GEN_MULTICAST_FRAMES_RCV 0x0002020A +#define RNDIS_OID_GEN_BROADCAST_BYTES_RCV 0x0002020B +#define RNDIS_OID_GEN_BROADCAST_FRAMES_RCV 0x0002020C + +#define RNDIS_OID_GEN_RCV_CRC_ERROR 0x0002020D +#define RNDIS_OID_GEN_TRANSMIT_QUEUE_LENGTH 0x0002020E + +#define RNDIS_OID_GEN_GET_TIME_CAPS 0x0002020F +#define RNDIS_OID_GEN_GET_NETCARD_TIME 0x00020210 + +/* + * These are connection-oriented general OIDs. + * These replace the above OIDs for connection-oriented media. + */ +#define RNDIS_OID_GEN_CO_SUPPORTED_LIST 0x00010101 +#define RNDIS_OID_GEN_CO_HARDWARE_STATUS 0x00010102 +#define RNDIS_OID_GEN_CO_MEDIA_SUPPORTED 0x00010103 +#define RNDIS_OID_GEN_CO_MEDIA_IN_USE 0x00010104 +#define RNDIS_OID_GEN_CO_LINK_SPEED 0x00010105 +#define RNDIS_OID_GEN_CO_VENDOR_ID 0x00010106 +#define RNDIS_OID_GEN_CO_VENDOR_DESCRIPTION 0x00010107 +#define RNDIS_OID_GEN_CO_DRIVER_VERSION 0x00010108 +#define RNDIS_OID_GEN_CO_PROTOCOL_OPTIONS 0x00010109 +#define RNDIS_OID_GEN_CO_MAC_OPTIONS 0x0001010A +#define RNDIS_OID_GEN_CO_MEDIA_CONNECT_STATUS 0x0001010B +#define RNDIS_OID_GEN_CO_VENDOR_DRIVER_VERSION 0x0001010C +#define RNDIS_OID_GEN_CO_MINIMUM_LINK_SPEED 0x0001010D + +#define RNDIS_OID_GEN_CO_GET_TIME_CAPS 0x00010201 +#define RNDIS_OID_GEN_CO_GET_NETCARD_TIME 0x00010202 + +/* + * These are connection-oriented statistics OIDs. + */ +#define RNDIS_OID_GEN_CO_XMIT_PDUS_OK 0x00020101 +#define RNDIS_OID_GEN_CO_RCV_PDUS_OK 0x00020102 +#define RNDIS_OID_GEN_CO_XMIT_PDUS_ERROR 0x00020103 +#define RNDIS_OID_GEN_CO_RCV_PDUS_ERROR 0x00020104 +#define RNDIS_OID_GEN_CO_RCV_PDUS_NO_BUFFER 0x00020105 + + +#define RNDIS_OID_GEN_CO_RCV_CRC_ERROR 0x00020201 +#define RNDIS_OID_GEN_CO_TRANSMIT_QUEUE_LENGTH 0x00020202 +#define RNDIS_OID_GEN_CO_BYTES_XMIT 0x00020203 +#define RNDIS_OID_GEN_CO_BYTES_RCV 0x00020204 +#define RNDIS_OID_GEN_CO_BYTES_XMIT_OUTSTANDING 0x00020205 +#define RNDIS_OID_GEN_CO_NETCARD_LOAD 0x00020206 + +/* + * These are objects for Connection-oriented media call-managers. + */ +#define RNDIS_OID_CO_ADD_PVC 0xFF000001 +#define RNDIS_OID_CO_DELETE_PVC 0xFF000002 +#define RNDIS_OID_CO_GET_CALL_INFORMATION 0xFF000003 +#define RNDIS_OID_CO_ADD_ADDRESS 0xFF000004 +#define RNDIS_OID_CO_DELETE_ADDRESS 0xFF000005 +#define RNDIS_OID_CO_GET_ADDRESSES 0xFF000006 +#define RNDIS_OID_CO_ADDRESS_CHANGE 0xFF000007 +#define RNDIS_OID_CO_SIGNALING_ENABLED 0xFF000008 +#define RNDIS_OID_CO_SIGNALING_DISABLED 0xFF000009 + + +/* + * 802.3 Objects (Ethernet) + */ + +#define RNDIS_OID_802_3_PERMANENT_ADDRESS 0x01010101 +#define RNDIS_OID_802_3_CURRENT_ADDRESS 0x01010102 +#define RNDIS_OID_802_3_MULTICAST_LIST 0x01010103 +#define RNDIS_OID_802_3_MAXIMUM_LIST_SIZE 0x01010104 +#define RNDIS_OID_802_3_MAC_OPTIONS 0x01010105 + +/* + * + */ +#define NDIS_802_3_MAC_OPTION_PRIORITY 0x00000001 + +#define RNDIS_OID_802_3_RCV_ERROR_ALIGNMENT 0x01020101 +#define RNDIS_OID_802_3_XMIT_ONE_COLLISION 0x01020102 +#define RNDIS_OID_802_3_XMIT_MORE_COLLISIONS 0x01020103 + +#define RNDIS_OID_802_3_XMIT_DEFERRED 0x01020201 +#define RNDIS_OID_802_3_XMIT_MAX_COLLISIONS 0x01020202 +#define RNDIS_OID_802_3_RCV_OVERRUN 0x01020203 +#define RNDIS_OID_802_3_XMIT_UNDERRUN 0x01020204 +#define RNDIS_OID_802_3_XMIT_HEARTBEAT_FAILURE 0x01020205 +#define RNDIS_OID_802_3_XMIT_TIMES_CRS_LOST 0x01020206 +#define RNDIS_OID_802_3_XMIT_LATE_COLLISIONS 0x01020207 + + +/* + * RNDIS MP custom OID for test + */ +#define OID_RNDISMP_GET_RECEIVE_BUFFERS 0xFFA0C90D // Query only + + +/* + * Remote NDIS message types + */ +#define REMOTE_NDIS_PACKET_MSG 0x00000001 +#define REMOTE_NDIS_INITIALIZE_MSG 0x00000002 +#define REMOTE_NDIS_HALT_MSG 0x00000003 +#define REMOTE_NDIS_QUERY_MSG 0x00000004 +#define REMOTE_NDIS_SET_MSG 0x00000005 +#define REMOTE_NDIS_RESET_MSG 0x00000006 +#define REMOTE_NDIS_INDICATE_STATUS_MSG 0x00000007 +#define REMOTE_NDIS_KEEPALIVE_MSG 0x00000008 + +#define REMOTE_CONDIS_MP_CREATE_VC_MSG 0x00008001 +#define REMOTE_CONDIS_MP_DELETE_VC_MSG 0x00008002 +#define REMOTE_CONDIS_MP_ACTIVATE_VC_MSG 0x00008005 +#define REMOTE_CONDIS_MP_DEACTIVATE_VC_MSG 0x00008006 +#define REMOTE_CONDIS_INDICATE_STATUS_MSG 0x00008007 + +/* + * Remote NDIS message completion types + */ +#define REMOTE_NDIS_INITIALIZE_CMPLT 0x80000002 +#define REMOTE_NDIS_QUERY_CMPLT 0x80000004 +#define REMOTE_NDIS_SET_CMPLT 0x80000005 +#define REMOTE_NDIS_RESET_CMPLT 0x80000006 +#define REMOTE_NDIS_KEEPALIVE_CMPLT 0x80000008 + +#define REMOTE_CONDIS_MP_CREATE_VC_CMPLT 0x80008001 +#define REMOTE_CONDIS_MP_DELETE_VC_CMPLT 0x80008002 +#define REMOTE_CONDIS_MP_ACTIVATE_VC_CMPLT 0x80008005 +#define REMOTE_CONDIS_MP_DEACTIVATE_VC_CMPLT 0x80008006 + +/* + * Reserved message type for private communication between lower-layer + * host driver and remote device, if necessary. + */ +#define REMOTE_NDIS_BUS_MSG 0xff000001 + +/* + * Defines for DeviceFlags in rndis_initialize_complete + */ +#define RNDIS_DF_CONNECTIONLESS 0x00000001 +#define RNDIS_DF_CONNECTION_ORIENTED 0x00000002 +#define RNDIS_DF_RAW_DATA 0x00000004 + +/* + * Remote NDIS medium types. + */ +#define RNDIS_MEDIUM_802_3 0x00000000 +#define RNDIS_MEDIUM_802_5 0x00000001 +#define RNDIS_MEDIUM_FDDI 0x00000002 +#define RNDIS_MEDIUM_WAN 0x00000003 +#define RNDIS_MEDIUM_LOCAL_TALK 0x00000004 +#define RNDIS_MEDIUM_ARCNET_RAW 0x00000006 +#define RNDIS_MEDIUM_ARCNET_878_2 0x00000007 +#define RNDIS_MEDIUM_ATM 0x00000008 +#define RNDIS_MEDIUM_WIRELESS_WAN 0x00000009 +#define RNDIS_MEDIUM_IRDA 0x0000000a +#define RNDIS_MEDIUM_CO_WAN 0x0000000b +/* Not a real medium, defined as an upper bound */ +#define RNDIS_MEDIUM_MAX 0x0000000d + +/* + * Remote NDIS medium connection states. + */ +#define RNDIS_MEDIA_STATE_CONNECTED 0x00000000 +#define RNDIS_MEDIA_STATE_DISCONNECTED 0x00000001 + +/* + * Remote NDIS version numbers + */ +#define RNDIS_MAJOR_VERSION 0x00000001 +#define RNDIS_MINOR_VERSION 0x00000000 + +/* + * NdisInitialize message + */ +typedef struct rndis_initialize_request_ { + /* RNDIS request ID */ + uint32_t request_id; + uint32_t major_version; + uint32_t minor_version; + uint32_t max_xfer_size; +} rndis_initialize_request; + +/* + * Response to NdisInitialize + */ +typedef struct rndis_initialize_complete_ { + /* RNDIS request ID */ + uint32_t request_id; + /* RNDIS status */ + uint32_t status; + uint32_t major_version; + uint32_t minor_version; + uint32_t device_flags; + /* RNDIS medium */ + uint32_t medium; + uint32_t max_pkts_per_msg; + uint32_t max_xfer_size; + uint32_t pkt_align_factor; + uint32_t af_list_offset; + uint32_t af_list_size; +} rndis_initialize_complete; + +/* + * Call manager devices only: Information about an address family + * supported by the device is appended to the response to NdisInitialize. + */ +typedef struct rndis_co_address_family_ { + /* RNDIS AF */ + uint32_t address_family; + uint32_t major_version; + uint32_t minor_version; +} rndis_co_address_family; + +/* + * NdisHalt message + */ +typedef struct rndis_halt_request_ { + /* RNDIS request ID */ + uint32_t request_id; +} rndis_halt_request; + +/* + * NdisQueryRequest message + */ +typedef struct rndis_query_request_ { + /* RNDIS request ID */ + uint32_t request_id; + /* RNDIS OID */ + uint32_t oid; + uint32_t info_buffer_length; + uint32_t info_buffer_offset; + /* RNDIS handle */ + uint32_t device_vc_handle; +} rndis_query_request; + +/* + * Response to NdisQueryRequest + */ +typedef struct rndis_query_complete_ { + /* RNDIS request ID */ + uint32_t request_id; + /* RNDIS status */ + uint32_t status; + uint32_t info_buffer_length; + uint32_t info_buffer_offset; +} rndis_query_complete; + +/* + * NdisSetRequest message + */ +typedef struct rndis_set_request_ { + /* RNDIS request ID */ + uint32_t request_id; + /* RNDIS OID */ + uint32_t oid; + uint32_t info_buffer_length; + uint32_t info_buffer_offset; + /* RNDIS handle */ + uint32_t device_vc_handle; +} rndis_set_request; + +/* + * Response to NdisSetRequest + */ +typedef struct rndis_set_complete_ { + /* RNDIS request ID */ + uint32_t request_id; + /* RNDIS status */ + uint32_t status; +} rndis_set_complete; + +/* + * NdisReset message + */ +typedef struct rndis_reset_request_ { + uint32_t reserved; +} rndis_reset_request; + +/* + * Response to NdisReset + */ +typedef struct rndis_reset_complete_ { + /* RNDIS status */ + uint32_t status; + uint32_t addressing_reset; +} rndis_reset_complete; + +/* + * NdisMIndicateStatus message + */ +typedef struct rndis_indicate_status_ { + /* RNDIS status */ + uint32_t status; + uint32_t status_buf_length; + uint32_t status_buf_offset; +} rndis_indicate_status; + +/* + * Diagnostic information passed as the status buffer in + * rndis_indicate_status messages signifying error conditions. + */ +typedef struct rndis_diagnostic_info_ { + /* RNDIS status */ + uint32_t diag_status; + uint32_t error_offset; +} rndis_diagnostic_info; + +/* + * NdisKeepAlive message + */ +typedef struct rndis_keepalive_request_ { + /* RNDIS request ID */ + uint32_t request_id; +} rndis_keepalive_request; + +/* + * Response to NdisKeepAlive + */ +typedef struct rndis_keepalive_complete_ { + /* RNDIS request ID */ + uint32_t request_id; + /* RNDIS status */ + uint32_t status; +} rndis_keepalive_complete; + +/* + * Data message. All offset fields contain byte offsets from the beginning + * of the rndis_packet structure. All length fields are in bytes. + * VcHandle is set to 0 for connectionless data, otherwise it + * contains the VC handle. + */ +typedef struct rndis_packet_ { + uint32_t data_offset; + uint32_t data_length; + uint32_t oob_data_offset; + uint32_t oob_data_length; + uint32_t num_oob_data_elements; + uint32_t per_pkt_info_offset; + uint32_t per_pkt_info_length; + /* RNDIS handle */ + uint32_t vc_handle; + uint32_t reserved; +} rndis_packet; + +typedef struct rndis_packet_ex_ { + uint32_t data_offset; + uint32_t data_length; + uint32_t oob_data_offset; + uint32_t oob_data_length; + uint32_t num_oob_data_elements; + uint32_t per_pkt_info_offset; + uint32_t per_pkt_info_length; + /* RNDIS handle */ + uint32_t vc_handle; + uint32_t reserved; + uint64_t data_buf_id; + uint32_t data_buf_offset; + uint64_t next_header_buf_id; + uint32_t next_header_byte_offset; + uint32_t next_header_byte_count; +} rndis_packet_ex; + +/* + * Optional Out of Band data associated with a Data message. + */ +typedef struct rndis_oobd_ { + uint32_t size; + /* RNDIS class ID */ + uint32_t type; + uint32_t class_info_offset; +} rndis_oobd; + +/* + * Packet extension field contents associated with a Data message. + */ +typedef struct rndis_per_packet_info_ { + uint32_t size; + uint32_t type; + uint32_t per_packet_info_offset; +} rndis_per_packet_info; + +typedef enum ndis_per_pkt_infotype_ { + tcpip_chksum_info, + ipsec_info, + tcp_large_send_info, + classification_handle_info, + ndis_reserved, + sgl_info, + ieee_8021q_info, + original_pkt_info, + pkt_cancel_id, + original_netbuf_list, + cached_netbuf_list, + short_pkt_padding_info, + max_perpkt_info +} ndis_per_pkt_infotype; + +typedef struct ndis_8021q_info_ { + union { + struct { + uint32_t user_pri : 3; /* User Priority */ + uint32_t cfi : 1; /* Canonical Format ID */ + uint32_t vlan_id : 12; + uint32_t reserved : 16; + } s1; + uint32_t value; + } u1; +} ndis_8021q_info; + +/* + * Format of Information buffer passed in a SetRequest for the OID + * OID_GEN_RNDIS_CONFIG_PARAMETER. + */ +typedef struct rndis_config_parameter_info_ { + uint32_t parameter_name_offset; + uint32_t parameter_name_length; + uint32_t parameter_type; + uint32_t parameter_value_offset; + uint32_t parameter_value_length; +} rndis_config_parameter_info; + +/* + * Values for ParameterType in rndis_config_parameter_info + */ +#define RNDIS_CONFIG_PARAM_TYPE_INTEGER 0 +#define RNDIS_CONFIG_PARAM_TYPE_STRING 2 + + +/* + * CONDIS Miniport messages for connection oriented devices + * that do not implement a call manager. + */ + +/* + * CoNdisMiniportCreateVc message + */ +typedef struct rcondis_mp_create_vc_ { + /* RNDIS request ID */ + uint32_t request_id; + /* RNDIS handle */ + uint32_t ndis_vc_handle; +} rcondis_mp_create_vc; + +/* + * Response to CoNdisMiniportCreateVc + */ +typedef struct rcondis_mp_create_vc_complete_ { + /* RNDIS request ID */ + uint32_t request_id; + /* RNDIS handle */ + uint32_t device_vc_handle; + /* RNDIS status */ + uint32_t status; +} rcondis_mp_create_vc_complete; + +/* + * CoNdisMiniportDeleteVc message + */ +typedef struct rcondis_mp_delete_vc_ { + /* RNDIS request ID */ + uint32_t request_id; + /* RNDIS handle */ + uint32_t device_vc_handle; +} rcondis_mp_delete_vc; + +/* + * Response to CoNdisMiniportDeleteVc + */ +typedef struct rcondis_mp_delete_vc_complete_ { + /* RNDIS request ID */ + uint32_t request_id; + /* RNDIS status */ + uint32_t status; +} rcondis_mp_delete_vc_complete; + +/* + * CoNdisMiniportQueryRequest message + */ +typedef struct rcondis_mp_query_request_ { + /* RNDIS request ID */ + uint32_t request_id; + /* RNDIS request type */ + uint32_t request_type; + /* RNDIS OID */ + uint32_t oid; + /* RNDIS handle */ + uint32_t device_vc_handle; + uint32_t info_buf_length; + uint32_t info_buf_offset; +} rcondis_mp_query_request; + +/* + * CoNdisMiniportSetRequest message + */ +typedef struct rcondis_mp_set_request_ { + /* RNDIS request ID */ + uint32_t request_id; + /* RNDIS request type */ + uint32_t request_type; + /* RNDIS OID */ + uint32_t oid; + /* RNDIS handle */ + uint32_t device_vc_handle; + uint32_t info_buf_length; + uint32_t info_buf_offset; +} rcondis_mp_set_request; + +/* + * CoNdisIndicateStatus message + */ +typedef struct rcondis_indicate_status_ { + /* RNDIS handle */ + uint32_t ndis_vc_handle; + /* RNDIS status */ + uint32_t status; + uint32_t status_buf_length; + uint32_t status_buf_offset; +} rcondis_indicate_status; + +/* + * CONDIS Call/VC parameters + */ + +typedef struct rcondis_specific_parameters_ { + uint32_t parameter_type; + uint32_t parameter_length; + uint32_t parameter_offset; +} rcondis_specific_parameters; + +typedef struct rcondis_media_parameters_ { + uint32_t flags; + uint32_t reserved1; + uint32_t reserved2; + rcondis_specific_parameters media_specific; +} rcondis_media_parameters; + +typedef struct rndis_flowspec_ { + uint32_t token_rate; + uint32_t token_bucket_size; + uint32_t peak_bandwidth; + uint32_t latency; + uint32_t delay_variation; + uint32_t service_type; + uint32_t max_sdu_size; + uint32_t minimum_policed_size; +} rndis_flowspec; + +typedef struct rcondis_call_manager_parameters_ { + rndis_flowspec transmit; + rndis_flowspec receive; + rcondis_specific_parameters call_mgr_specific; +} rcondis_call_manager_parameters; + +/* + * CoNdisMiniportActivateVc message + */ +typedef struct rcondis_mp_activate_vc_request_ { + /* RNDIS request ID */ + uint32_t request_id; + uint32_t flags; + /* RNDIS handle */ + uint32_t device_vc_handle; + uint32_t media_params_offset; + uint32_t media_params_length; + uint32_t call_mgr_params_offset; + uint32_t call_mgr_params_length; +} rcondis_mp_activate_vc_request; + +/* + * Response to CoNdisMiniportActivateVc + */ +typedef struct rcondis_mp_activate_vc_complete_ { + /* RNDIS request ID */ + uint32_t request_id; + /* RNDIS status */ + uint32_t status; +} rcondis_mp_activate_vc_complete; + +/* + * CoNdisMiniportDeactivateVc message + */ +typedef struct rcondis_mp_deactivate_vc_request_ { + /* RNDIS request ID */ + uint32_t request_id; + uint32_t flags; + /* RNDIS handle */ + uint32_t device_vc_handle; +} rcondis_mp_deactivate_vc_request; + +/* + * Response to CoNdisMiniportDeactivateVc + */ +typedef struct rcondis_mp_deactivate_vc_complete_ { + /* RNDIS request ID */ + uint32_t request_id; + /* RNDIS status */ + uint32_t status; +} rcondis_mp_deactivate_vc_complete; + +/* + * union with all of the RNDIS messages + */ +typedef union rndis_msg_container_ { + rndis_packet packet; + rndis_initialize_request init_request; + rndis_halt_request halt_request; + rndis_query_request query_request; + rndis_set_request set_request; + rndis_reset_request reset_request; + rndis_keepalive_request keepalive_request; + rndis_indicate_status indicate_status; + rndis_initialize_complete init_complete; + rndis_query_complete query_complete; + rndis_set_complete set_complete; + rndis_reset_complete reset_complete; + rndis_keepalive_complete keepalive_complete; + rcondis_mp_create_vc co_miniport_create_vc; + rcondis_mp_delete_vc co_miniport_delete_vc; + rcondis_indicate_status co_miniport_status; + rcondis_mp_activate_vc_request co_miniport_activate_vc; + rcondis_mp_deactivate_vc_request co_miniport_deactivate_vc; + rcondis_mp_create_vc_complete co_miniport_create_vc_complete; + rcondis_mp_delete_vc_complete co_miniport_delete_vc_complete; + rcondis_mp_activate_vc_complete co_miniport_activate_vc_complete; + rcondis_mp_deactivate_vc_complete co_miniport_deactivate_vc_complete; + rndis_packet_ex packet_ex; +} rndis_msg_container; + +/* + * Remote NDIS message format + */ +typedef struct rndis_msg_ { + uint32_t ndis_msg_type; + + /* + * Total length of this message, from the beginning + * of the rndis_msg struct, in bytes. + */ + uint32_t msg_len; + + /* Actual message */ + rndis_msg_container msg; +} rndis_msg; + + +/* + * Handy macros + */ + +/* + * get the size of an RNDIS message. Pass in the message type, + * rndis_set_request, rndis_packet for example + */ +#define RNDIS_MESSAGE_SIZE(message) \ + (sizeof(message) + (sizeof(rndis_msg) - sizeof(rndis_msg_container))) + +/* + * get pointer to info buffer with message pointer + */ +#define MESSAGE_TO_INFO_BUFFER(message) \ + (((PUCHAR)(message)) + message->InformationBufferOffset) + +/* + * get pointer to status buffer with message pointer + */ +#define MESSAGE_TO_STATUS_BUFFER(message) \ + (((PUCHAR)(message)) + message->StatusBufferOffset) + +/* + * get pointer to OOBD buffer with message pointer + */ +#define MESSAGE_TO_OOBD_BUFFER(message) \ + (((PUCHAR)(message)) + message->OOBDataOffset) + +/* + * get pointer to data buffer with message pointer + */ +#define MESSAGE_TO_DATA_BUFFER(message) \ + (((PUCHAR)(message)) + message->PerPacketInfoOffset) + +/* + * get pointer to contained message from NDIS_MESSAGE pointer + */ +#define RNDIS_MESSAGE_PTR_TO_MESSAGE_PTR(rndis_message) \ + ((void *) &rndis_message->Message) + +/* + * get pointer to contained message from NDIS_MESSAGE pointer + */ +#define RNDIS_MESSAGE_RAW_PTR_TO_MESSAGE_PTR(rndis_message) \ + ((void *) rndis_message) + + + +/* + * Structures used in OID_RNDISMP_GET_RECEIVE_BUFFERS + */ + +#define RNDISMP_RECEIVE_BUFFER_ELEM_FLAG_VMQ_RECEIVE_BUFFER 0x00000001 + +typedef struct rndismp_rx_buf_elem_ { + uint32_t flags; + uint32_t length; + uint64_t rx_buf_id; + uint32_t gpadl_handle; + void *rx_buf; +} rndismp_rx_buf_elem; + +typedef struct rndismp_rx_bufs_info_ { + uint32_t num_rx_bufs; + rndismp_rx_buf_elem rx_buf_elems[1]; +} rndismp_rx_bufs_info; + + + +#define RNDIS_HEADER_SIZE (sizeof(rndis_msg) - sizeof(rndis_msg_container)) + +#define NDIS_PACKET_TYPE_DIRECTED 0x00000001 +#define NDIS_PACKET_TYPE_MULTICAST 0x00000002 +#define NDIS_PACKET_TYPE_ALL_MULTICAST 0x00000004 +#define NDIS_PACKET_TYPE_BROADCAST 0x00000008 +#define NDIS_PACKET_TYPE_SOURCE_ROUTING 0x00000010 +#define NDIS_PACKET_TYPE_PROMISCUOUS 0x00000020 +#define NDIS_PACKET_TYPE_SMT 0x00000040 +#define NDIS_PACKET_TYPE_ALL_LOCAL 0x00000080 +#define NDIS_PACKET_TYPE_GROUP 0x00000100 +#define NDIS_PACKET_TYPE_ALL_FUNCTIONAL 0x00000200 +#define NDIS_PACKET_TYPE_FUNCTIONAL 0x00000400 +#define NDIS_PACKET_TYPE_MAC_FRAME 0x00000800 + + +#endif /* __HV_RNDIS_H__ */ + diff --git a/sys/dev/hyperv/netvsc/hv_rndis_filter.c b/sys/dev/hyperv/netvsc/hv_rndis_filter.c new file mode 100644 index 00000000000..691cf7ed51b --- /dev/null +++ b/sys/dev/hyperv/netvsc/hv_rndis_filter.c @@ -0,0 +1,929 @@ +/*- + * Copyright (c) 2009-2012 Microsoft Corp. + * Copyright (c) 2010-2012 Citrix Inc. + * Copyright (c) 2012 NetApp Inc. + * 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 unmodified, 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "hv_net_vsc.h" +#include "hv_rndis.h" +#include "hv_rndis_filter.h" + + +/* + * Forward declarations + */ +static int hv_rf_send_request(rndis_device *device, rndis_request *request, + uint32_t message_type); +static void hv_rf_receive_response(rndis_device *device, rndis_msg *response); +static void hv_rf_receive_indicate_status(rndis_device *device, + rndis_msg *response); +static void hv_rf_receive_data(rndis_device *device, rndis_msg *message, + netvsc_packet *pkt); +static int hv_rf_query_device(rndis_device *device, uint32_t oid, + void *result, uint32_t *result_size); +static inline int hv_rf_query_device_mac(rndis_device *device); +static inline int hv_rf_query_device_link_status(rndis_device *device); +static int hv_rf_set_packet_filter(rndis_device *device, uint32_t new_filter); +static int hv_rf_init_device(rndis_device *device); +static int hv_rf_open_device(rndis_device *device); +static int hv_rf_close_device(rndis_device *device); +static void hv_rf_on_send_completion(void *context); +static void hv_rf_on_send_request_completion(void *context); +static void hv_rf_on_send_request_halt_completion(void *context); + + +/* + * Allow module_param to work and override to switch to promiscuous mode. + */ +static inline rndis_device * +hv_get_rndis_device(void) +{ + rndis_device *device; + + device = malloc(sizeof(rndis_device), M_DEVBUF, M_NOWAIT | M_ZERO); + if (device == NULL) { + return (NULL); + } + + mtx_init(&device->req_lock, "HV-FRL", NULL, MTX_SPIN | MTX_RECURSE); + + /* Same effect as STAILQ_HEAD_INITIALIZER() static initializer */ + STAILQ_INIT(&device->myrequest_list); + + device->state = RNDIS_DEV_UNINITIALIZED; + + return (device); +} + +/* + * + */ +static inline void +hv_put_rndis_device(rndis_device *device) +{ + mtx_destroy(&device->req_lock); + free(device, M_DEVBUF); +} + +/* + * + */ +static inline rndis_request * +hv_rndis_request(rndis_device *device, uint32_t message_type, + uint32_t message_length) +{ + rndis_request *request; + rndis_msg *rndis_mesg; + rndis_set_request *set; + + request = malloc(sizeof(rndis_request), M_DEVBUF, M_NOWAIT | M_ZERO); + if (request == NULL) { + return (NULL); + } + + sema_init(&request->wait_sema, 0, "rndis sema"); + + rndis_mesg = &request->request_msg; + rndis_mesg->ndis_msg_type = message_type; + rndis_mesg->msg_len = message_length; + + /* + * Set the request id. This field is always after the rndis header + * for request/response packet types so we just use the set_request + * as a template. + */ + set = &rndis_mesg->msg.set_request; + set->request_id = atomic_fetchadd_int(&device->new_request_id, 1); + /* Increment to get the new value (call above returns old value) */ + set->request_id += 1; + + /* Add to the request list */ + mtx_lock_spin(&device->req_lock); + STAILQ_INSERT_TAIL(&device->myrequest_list, request, mylist_entry); + mtx_unlock_spin(&device->req_lock); + + return (request); +} + +/* + * + */ +static inline void +hv_put_rndis_request(rndis_device *device, rndis_request *request) +{ + mtx_lock_spin(&device->req_lock); + /* Fixme: Has O(n) performance */ + /* + * XXXKYS: Use Doubly linked lists. + */ + STAILQ_REMOVE(&device->myrequest_list, request, rndis_request_, + mylist_entry); + mtx_unlock_spin(&device->req_lock); + + sema_destroy(&request->wait_sema); + free(request, M_DEVBUF); +} + +/* + * + */ +static int +hv_rf_send_request(rndis_device *device, rndis_request *request, + uint32_t message_type) +{ + int ret; + netvsc_packet *packet; + + /* Set up the packet to send it */ + packet = &request->pkt; + + packet->is_data_pkt = FALSE; + packet->tot_data_buf_len = request->request_msg.msg_len; + packet->page_buf_count = 1; + + packet->page_buffers[0].pfn = + hv_get_phys_addr(&request->request_msg) >> PAGE_SHIFT; + packet->page_buffers[0].length = request->request_msg.msg_len; + packet->page_buffers[0].offset = + (unsigned long)&request->request_msg & (PAGE_SIZE - 1); + + packet->compl.send.send_completion_context = request; /* packet */ + if (message_type != REMOTE_NDIS_HALT_MSG) { + packet->compl.send.on_send_completion = + hv_rf_on_send_request_completion; + } else { + packet->compl.send.on_send_completion = + hv_rf_on_send_request_halt_completion; + } + packet->compl.send.send_completion_tid = (unsigned long)device; + + ret = hv_nv_on_send(device->net_dev->dev, packet); + + return (ret); +} + +/* + * RNDIS filter receive response + */ +static void +hv_rf_receive_response(rndis_device *device, rndis_msg *response) +{ + rndis_request *request = NULL; + rndis_request *next_request; + boolean_t found = FALSE; + + mtx_lock_spin(&device->req_lock); + request = STAILQ_FIRST(&device->myrequest_list); + while (request != NULL) { + /* + * All request/response message contains request_id as the + * first field + */ + if (request->request_msg.msg.init_request.request_id == + response->msg.init_complete.request_id) { + found = TRUE; + break; + } + next_request = STAILQ_NEXT(request, mylist_entry); + request = next_request; + } + mtx_unlock_spin(&device->req_lock); + + if (found) { + if (response->msg_len <= sizeof(rndis_msg)) { + memcpy(&request->response_msg, response, + response->msg_len); + } else { + if (response->ndis_msg_type == REMOTE_NDIS_RESET_CMPLT) { + /* Does not have a request id field */ + request->response_msg.msg.reset_complete.status = + STATUS_BUFFER_OVERFLOW; + } else { + request->response_msg.msg.init_complete.status = + STATUS_BUFFER_OVERFLOW; + } + } + + sema_post(&request->wait_sema); + } +} + +/* + * RNDIS filter receive indicate status + */ +static void +hv_rf_receive_indicate_status(rndis_device *device, rndis_msg *response) +{ + rndis_indicate_status *indicate = &response->msg.indicate_status; + + if (indicate->status == RNDIS_STATUS_MEDIA_CONNECT) { + netvsc_linkstatus_callback(device->net_dev->dev, 1); + } else if (indicate->status == RNDIS_STATUS_MEDIA_DISCONNECT) { + netvsc_linkstatus_callback(device->net_dev->dev, 0); + } else { + /* TODO: */ + } +} + +/* + * RNDIS filter receive data + */ +static void +hv_rf_receive_data(rndis_device *device, rndis_msg *message, netvsc_packet *pkt) +{ + rndis_packet *rndis_pkt; + rndis_per_packet_info *rppi; + ndis_8021q_info *rppi_vlan_info; + uint32_t data_offset; + + rndis_pkt = &message->msg.packet; + + /* + * Fixme: Handle multiple rndis pkt msgs that may be enclosed in this + * netvsc packet (ie tot_data_buf_len != message_length) + */ + + /* Remove rndis header, then pass data packet up the stack */ + data_offset = RNDIS_HEADER_SIZE + rndis_pkt->data_offset; + + /* L2 frame length, with L2 header, not including CRC */ + pkt->tot_data_buf_len = rndis_pkt->data_length; + pkt->page_buffers[0].offset += data_offset; + /* Buffer length now L2 frame length plus trailing junk */ + pkt->page_buffers[0].length -= data_offset; + + pkt->is_data_pkt = TRUE; + + pkt->vlan_tci = 0; + + /* + * Read the VLAN ID if supplied by the Hyper-V infrastructure. + * Let higher-level driver code decide if it wants to use it. + * Ignore CFI, priority for now as FreeBSD does not support these. + */ + if (rndis_pkt->per_pkt_info_offset != 0) { + /* rppi struct exists; compute its address */ + rppi = (rndis_per_packet_info *)((uint8_t *)rndis_pkt + + rndis_pkt->per_pkt_info_offset); + /* if VLAN ppi struct, get the VLAN ID */ + if (rppi->type == ieee_8021q_info) { + rppi_vlan_info = (ndis_8021q_info *)((uint8_t *)rppi + + rppi->per_packet_info_offset); + pkt->vlan_tci = rppi_vlan_info->u1.s1.vlan_id; + } + } + + netvsc_recv(device->net_dev->dev, pkt); +} + +/* + * RNDIS filter on receive + */ +int +hv_rf_on_receive(struct hv_device *device, netvsc_packet *pkt) +{ + hn_softc_t *sc = device_get_softc(device->device); + netvsc_dev *net_dev = sc->net_dev; + rndis_device *rndis_dev; + rndis_msg rndis_mesg; + rndis_msg *rndis_hdr; + + /* Make sure the rndis device state is initialized */ + if (net_dev->extension == NULL) + return (ENODEV); + + rndis_dev = (rndis_device *)net_dev->extension; + if (rndis_dev->state == RNDIS_DEV_UNINITIALIZED) + return (EINVAL); + + /* Shift virtual page number to form virtual page address */ + rndis_hdr = (rndis_msg *)(pkt->page_buffers[0].pfn << PAGE_SHIFT); + + rndis_hdr = (void *)((unsigned long)rndis_hdr + + pkt->page_buffers[0].offset); + + /* + * Make sure we got a valid rndis message + * Fixme: There seems to be a bug in set completion msg where + * its msg_len is 16 bytes but the byte_count field in the + * xfer page range shows 52 bytes + */ +#if 0 + if (pkt->tot_data_buf_len != rndis_hdr->msg_len) { + DPRINT_ERR(NETVSC, "invalid rndis message? (expected %u " + "bytes got %u)... dropping this message!", + rndis_hdr->msg_len, pkt->tot_data_buf_len); + DPRINT_EXIT(NETVSC); + + return (-1); + } +#endif + + memcpy(&rndis_mesg, rndis_hdr, + (rndis_hdr->msg_len > sizeof(rndis_msg)) ? + sizeof(rndis_msg) : rndis_hdr->msg_len); + + switch (rndis_mesg.ndis_msg_type) { + + /* data message */ + case REMOTE_NDIS_PACKET_MSG: + hv_rf_receive_data(rndis_dev, &rndis_mesg, pkt); + break; + /* completion messages */ + case REMOTE_NDIS_INITIALIZE_CMPLT: + case REMOTE_NDIS_QUERY_CMPLT: + case REMOTE_NDIS_SET_CMPLT: + case REMOTE_NDIS_RESET_CMPLT: + case REMOTE_NDIS_KEEPALIVE_CMPLT: + hv_rf_receive_response(rndis_dev, &rndis_mesg); + break; + /* notification message */ + case REMOTE_NDIS_INDICATE_STATUS_MSG: + hv_rf_receive_indicate_status(rndis_dev, &rndis_mesg); + break; + default: + printf("hv_rf_on_receive(): Unknown msg_type 0x%x\n", + rndis_mesg.ndis_msg_type); + break; + } + + return (0); +} + +/* + * RNDIS filter query device + */ +static int +hv_rf_query_device(rndis_device *device, uint32_t oid, void *result, + uint32_t *result_size) +{ + rndis_request *request; + uint32_t in_result_size = *result_size; + rndis_query_request *query; + rndis_query_complete *query_complete; + int ret = 0; + + *result_size = 0; + request = hv_rndis_request(device, REMOTE_NDIS_QUERY_MSG, + RNDIS_MESSAGE_SIZE(rndis_query_request)); + if (request == NULL) { + ret = -1; + goto cleanup; + } + + /* Set up the rndis query */ + query = &request->request_msg.msg.query_request; + query->oid = oid; + query->info_buffer_offset = sizeof(rndis_query_request); + query->info_buffer_length = 0; + query->device_vc_handle = 0; + + ret = hv_rf_send_request(device, request, REMOTE_NDIS_QUERY_MSG); + if (ret != 0) { + /* Fixme: printf added */ + printf("RNDISFILTER request failed to Send!\n"); + goto cleanup; + } + + sema_wait(&request->wait_sema); + + /* Copy the response back */ + query_complete = &request->response_msg.msg.query_complete; + + if (query_complete->info_buffer_length > in_result_size) { + ret = EINVAL; + goto cleanup; + } + + memcpy(result, (void *)((unsigned long)query_complete + + query_complete->info_buffer_offset), + query_complete->info_buffer_length); + + *result_size = query_complete->info_buffer_length; + +cleanup: + if (request != NULL) + hv_put_rndis_request(device, request); + + return (ret); +} + +/* + * RNDIS filter query device MAC address + */ +static inline int +hv_rf_query_device_mac(rndis_device *device) +{ + uint32_t size = HW_MACADDR_LEN; + + return (hv_rf_query_device(device, + RNDIS_OID_802_3_PERMANENT_ADDRESS, device->hw_mac_addr, &size)); +} + +/* + * RNDIS filter query device link status + */ +static inline int +hv_rf_query_device_link_status(rndis_device *device) +{ + uint32_t size = sizeof(uint32_t); + + return (hv_rf_query_device(device, + RNDIS_OID_GEN_MEDIA_CONNECT_STATUS, &device->link_status, &size)); +} + +/* + * RNDIS filter set packet filter + * Sends an rndis request with the new filter, then waits for a response + * from the host. + * Returns zero on success, non-zero on failure. + */ +static int +hv_rf_set_packet_filter(rndis_device *device, uint32_t new_filter) +{ + rndis_request *request; + rndis_set_request *set; + rndis_set_complete *set_complete; + uint32_t status; + int ret; + + request = hv_rndis_request(device, REMOTE_NDIS_SET_MSG, + RNDIS_MESSAGE_SIZE(rndis_set_request) + sizeof(uint32_t)); + if (request == NULL) { + ret = -1; + goto cleanup; + } + + /* Set up the rndis set */ + set = &request->request_msg.msg.set_request; + set->oid = RNDIS_OID_GEN_CURRENT_PACKET_FILTER; + set->info_buffer_length = sizeof(uint32_t); + set->info_buffer_offset = sizeof(rndis_set_request); + + memcpy((void *)((unsigned long)set + sizeof(rndis_set_request)), + &new_filter, sizeof(uint32_t)); + + ret = hv_rf_send_request(device, request, REMOTE_NDIS_SET_MSG); + if (ret != 0) { + goto cleanup; + } + + /* + * Wait for the response from the host. Another thread will signal + * us when the response has arrived. In the failure case, + * sema_timedwait() returns a non-zero status after waiting 5 seconds. + */ + ret = sema_timedwait(&request->wait_sema, 500); + if (ret == 0) { + /* Response received, check status */ + set_complete = &request->response_msg.msg.set_complete; + status = set_complete->status; + if (status != RNDIS_STATUS_SUCCESS) { + /* Bad response status, return error */ + ret = -2; + } + } else { + /* + * We cannot deallocate the request since we may still + * receive a send completion for it. + */ + goto exit; + } + +cleanup: + if (request != NULL) { + hv_put_rndis_request(device, request); + } +exit: + return (ret); +} + +/* + * RNDIS filter init device + */ +static int +hv_rf_init_device(rndis_device *device) +{ + rndis_request *request; + rndis_initialize_request *init; + rndis_initialize_complete *init_complete; + uint32_t status; + int ret; + + request = hv_rndis_request(device, REMOTE_NDIS_INITIALIZE_MSG, + RNDIS_MESSAGE_SIZE(rndis_initialize_request)); + if (!request) { + ret = -1; + goto cleanup; + } + + /* Set up the rndis set */ + init = &request->request_msg.msg.init_request; + init->major_version = RNDIS_MAJOR_VERSION; + init->minor_version = RNDIS_MINOR_VERSION; + /* + * Per the RNDIS document, this should be set to the max MTU + * plus the header size. However, 2048 works fine, so leaving + * it as is. + */ + init->max_xfer_size = 2048; + + device->state = RNDIS_DEV_INITIALIZING; + + ret = hv_rf_send_request(device, request, REMOTE_NDIS_INITIALIZE_MSG); + if (ret != 0) { + device->state = RNDIS_DEV_UNINITIALIZED; + goto cleanup; + } + + sema_wait(&request->wait_sema); + + init_complete = &request->response_msg.msg.init_complete; + status = init_complete->status; + if (status == RNDIS_STATUS_SUCCESS) { + device->state = RNDIS_DEV_INITIALIZED; + ret = 0; + } else { + device->state = RNDIS_DEV_UNINITIALIZED; + ret = -1; + } + +cleanup: + if (request) { + hv_put_rndis_request(device, request); + } + + return (ret); +} + +#define HALT_COMPLETION_WAIT_COUNT 25 + +/* + * RNDIS filter halt device + */ +static int +hv_rf_halt_device(rndis_device *device) +{ + rndis_request *request; + rndis_halt_request *halt; + int i, ret; + + /* Attempt to do a rndis device halt */ + request = hv_rndis_request(device, REMOTE_NDIS_HALT_MSG, + RNDIS_MESSAGE_SIZE(rndis_halt_request)); + if (request == NULL) { + return (-1); + } + + /* initialize "poor man's semaphore" */ + request->halt_complete_flag = 0; + + /* Set up the rndis set */ + halt = &request->request_msg.msg.halt_request; + halt->request_id = atomic_fetchadd_int(&device->new_request_id, 1); + /* Increment to get the new value (call above returns old value) */ + halt->request_id += 1; + + ret = hv_rf_send_request(device, request, REMOTE_NDIS_HALT_MSG); + if (ret != 0) { + return (-1); + } + + /* + * Wait for halt response from halt callback. We must wait for + * the transaction response before freeing the request and other + * resources. + */ + for (i=HALT_COMPLETION_WAIT_COUNT; i > 0; i--) { + if (request->halt_complete_flag != 0) { + break; + } + DELAY(400); + } + if (i == 0) { + return (-1); + } + + device->state = RNDIS_DEV_UNINITIALIZED; + + if (request != NULL) { + hv_put_rndis_request(device, request); + } + + return (0); +} + +/* + * RNDIS filter open device + */ +static int +hv_rf_open_device(rndis_device *device) +{ + int ret; + + if (device->state != RNDIS_DEV_INITIALIZED) { + return (0); + } + + if (hv_promisc_mode != 1) { + ret = hv_rf_set_packet_filter(device, + NDIS_PACKET_TYPE_BROADCAST | + NDIS_PACKET_TYPE_ALL_MULTICAST | + NDIS_PACKET_TYPE_DIRECTED); + } else { + ret = hv_rf_set_packet_filter(device, + NDIS_PACKET_TYPE_PROMISCUOUS); + } + + if (ret == 0) { + device->state = RNDIS_DEV_DATAINITIALIZED; + } + + return (ret); +} + +/* + * RNDIS filter close device + */ +static int +hv_rf_close_device(rndis_device *device) +{ + int ret; + + if (device->state != RNDIS_DEV_DATAINITIALIZED) { + return (0); + } + + ret = hv_rf_set_packet_filter(device, 0); + if (ret == 0) { + device->state = RNDIS_DEV_INITIALIZED; + } + + return (ret); +} + +/* + * RNDIS filter on device add + */ +int +hv_rf_on_device_add(struct hv_device *device, void *additl_info) +{ + int ret; + netvsc_dev *net_dev; + rndis_device *rndis_dev; + netvsc_device_info *dev_info = (netvsc_device_info *)additl_info; + + rndis_dev = hv_get_rndis_device(); + if (rndis_dev == NULL) { + return (ENOMEM); + } + + /* + * Let the inner driver handle this first to create the netvsc channel + * NOTE! Once the channel is created, we may get a receive callback + * (hv_rf_on_receive()) before this call is completed. + * Note: Earlier code used a function pointer here. + */ + net_dev = hv_nv_on_device_add(device, additl_info); + if (!net_dev) { + hv_put_rndis_device(rndis_dev); + + return (ENOMEM); + } + + /* + * Initialize the rndis device + */ + + net_dev->extension = rndis_dev; + rndis_dev->net_dev = net_dev; + + /* Send the rndis initialization message */ + ret = hv_rf_init_device(rndis_dev); + if (ret != 0) { + /* + * TODO: If rndis init failed, we will need to shut down + * the channel + */ + } + + /* Get the mac address */ + ret = hv_rf_query_device_mac(rndis_dev); + if (ret != 0) { + /* TODO: shut down rndis device and the channel */ + } + + memcpy(dev_info->mac_addr, rndis_dev->hw_mac_addr, HW_MACADDR_LEN); + + hv_rf_query_device_link_status(rndis_dev); + + dev_info->link_state = rndis_dev->link_status; + + return (ret); +} + +/* + * RNDIS filter on device remove + */ +int +hv_rf_on_device_remove(struct hv_device *device, boolean_t destroy_channel) +{ + hn_softc_t *sc = device_get_softc(device->device); + netvsc_dev *net_dev = sc->net_dev; + rndis_device *rndis_dev = (rndis_device *)net_dev->extension; + int ret; + + /* Halt and release the rndis device */ + ret = hv_rf_halt_device(rndis_dev); + + hv_put_rndis_device(rndis_dev); + net_dev->extension = NULL; + + /* Pass control to inner driver to remove the device */ + ret |= hv_nv_on_device_remove(device, destroy_channel); + + return (ret); +} + +/* + * RNDIS filter on open + */ +int +hv_rf_on_open(struct hv_device *device) +{ + hn_softc_t *sc = device_get_softc(device->device); + netvsc_dev *net_dev = sc->net_dev; + + return (hv_rf_open_device((rndis_device *)net_dev->extension)); +} + +/* + * RNDIS filter on close + */ +int +hv_rf_on_close(struct hv_device *device) +{ + hn_softc_t *sc = device_get_softc(device->device); + netvsc_dev *net_dev = sc->net_dev; + + return (hv_rf_close_device((rndis_device *)net_dev->extension)); +} + +/* + * RNDIS filter on send + */ +int +hv_rf_on_send(struct hv_device *device, netvsc_packet *pkt) +{ + rndis_filter_packet *filter_pkt; + rndis_msg *rndis_mesg; + rndis_packet *rndis_pkt; + rndis_per_packet_info *rppi; + ndis_8021q_info *rppi_vlan_info; + uint32_t rndis_msg_size; + int ret = 0; + + /* Add the rndis header */ + filter_pkt = (rndis_filter_packet *)pkt->extension; + + memset(filter_pkt, 0, sizeof(rndis_filter_packet)); + + rndis_mesg = &filter_pkt->message; + rndis_msg_size = RNDIS_MESSAGE_SIZE(rndis_packet); + + if (pkt->vlan_tci != 0) { + rndis_msg_size += sizeof(rndis_per_packet_info) + + sizeof(ndis_8021q_info); + } + + rndis_mesg->ndis_msg_type = REMOTE_NDIS_PACKET_MSG; + rndis_mesg->msg_len = pkt->tot_data_buf_len + rndis_msg_size; + + rndis_pkt = &rndis_mesg->msg.packet; + rndis_pkt->data_offset = sizeof(rndis_packet); + rndis_pkt->data_length = pkt->tot_data_buf_len; + + pkt->is_data_pkt = TRUE; + pkt->page_buffers[0].pfn = hv_get_phys_addr(rndis_mesg) >> PAGE_SHIFT; + pkt->page_buffers[0].offset = + (unsigned long)rndis_mesg & (PAGE_SIZE - 1); + pkt->page_buffers[0].length = rndis_msg_size; + + /* Save the packet context */ + filter_pkt->completion_context = + pkt->compl.send.send_completion_context; + + /* Use ours */ + pkt->compl.send.on_send_completion = hv_rf_on_send_completion; + pkt->compl.send.send_completion_context = filter_pkt; + + /* + * If there is a VLAN tag, we need to set up some additional + * fields so the Hyper-V infrastructure will stuff the VLAN tag + * into the frame. + */ + if (pkt->vlan_tci != 0) { + /* Move data offset past end of rppi + VLAN structs */ + rndis_pkt->data_offset += sizeof(rndis_per_packet_info) + + sizeof(ndis_8021q_info); + + /* must be set when we have rppi, VLAN info */ + rndis_pkt->per_pkt_info_offset = sizeof(rndis_packet); + rndis_pkt->per_pkt_info_length = sizeof(rndis_per_packet_info) + + sizeof(ndis_8021q_info); + + /* rppi immediately follows rndis_pkt */ + rppi = (rndis_per_packet_info *)(rndis_pkt + 1); + rppi->size = sizeof(rndis_per_packet_info) + + sizeof(ndis_8021q_info); + rppi->type = ieee_8021q_info; + rppi->per_packet_info_offset = sizeof(rndis_per_packet_info); + + /* VLAN info immediately follows rppi struct */ + rppi_vlan_info = (ndis_8021q_info *)(rppi + 1); + /* FreeBSD does not support CFI or priority */ + rppi_vlan_info->u1.s1.vlan_id = pkt->vlan_tci & 0xfff; + } + + /* + * Invoke netvsc send. If return status is bad, the caller now + * resets the context pointers before retrying. + */ + ret = hv_nv_on_send(device, pkt); + + return (ret); +} + +/* + * RNDIS filter on send completion callback + */ +static void +hv_rf_on_send_completion(void *context) +{ + rndis_filter_packet *filter_pkt = (rndis_filter_packet *)context; + + /* Pass it back to the original handler */ + netvsc_xmit_completion(filter_pkt->completion_context); +} + +/* + * RNDIS filter on send request completion callback + */ +static void +hv_rf_on_send_request_completion(void *context) +{ +} + +/* + * RNDIS filter on send request (halt only) completion callback + */ +static void +hv_rf_on_send_request_halt_completion(void *context) +{ + rndis_request *request = context; + + /* + * Notify hv_rf_halt_device() about halt completion. + * The halt code must wait for completion before freeing + * the transaction resources. + */ + request->halt_complete_flag = 1; +} + diff --git a/sys/dev/hyperv/netvsc/hv_rndis_filter.h b/sys/dev/hyperv/netvsc/hv_rndis_filter.h new file mode 100644 index 00000000000..edbb3476b32 --- /dev/null +++ b/sys/dev/hyperv/netvsc/hv_rndis_filter.h @@ -0,0 +1,116 @@ +/*- + * Copyright (c) 2009-2012 Microsoft Corp. + * Copyright (c) 2010-2012 Citrix Inc. + * Copyright (c) 2012 NetApp Inc. + * 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 unmodified, 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#ifndef __HV_RNDIS_FILTER_H__ +#define __HV_RNDIS_FILTER_H__ + + +/* + * Defines + */ + +/* Destroy or preserve channel on filter/netvsc teardown */ +#define HV_RF_NV_DESTROY_CHANNEL TRUE +#define HV_RF_NV_RETAIN_CHANNEL FALSE + +/* + * Number of page buffers to reserve for the RNDIS filter packet in the + * transmitted message. + */ +#define HV_RF_NUM_TX_RESERVED_PAGE_BUFS 1 + + +/* + * Data types + */ + +typedef enum { + RNDIS_DEV_UNINITIALIZED = 0, + RNDIS_DEV_INITIALIZING, + RNDIS_DEV_INITIALIZED, + RNDIS_DEV_DATAINITIALIZED, +} rndis_device_state; + +typedef struct rndis_request_ { + STAILQ_ENTRY(rndis_request_) mylist_entry; + struct sema wait_sema; + + /* + * Fixme: We assumed a fixed size response here. If we do ever + * need to handle a bigger response, we can either define a max + * response message or add a response buffer variable above this field + */ + rndis_msg response_msg; + + /* Simplify allocation by having a netvsc packet inline */ + netvsc_packet pkt; + hv_vmbus_page_buffer buffer; + /* Fixme: We assumed a fixed size request here. */ + rndis_msg request_msg; + /* Fixme: Poor man's semaphore. */ + uint32_t halt_complete_flag; +} rndis_request; + +typedef struct rndis_device_ { + netvsc_dev *net_dev; + + rndis_device_state state; + uint32_t link_status; + uint32_t new_request_id; + + struct mtx req_lock; + + STAILQ_HEAD(RQ, rndis_request_) myrequest_list; + + uint8_t hw_mac_addr[HW_MACADDR_LEN]; +} rndis_device; + +typedef struct rndis_filter_packet_ { + void *completion_context; + /* No longer used */ + pfn_on_send_rx_completion on_completion; + + rndis_msg message; +} rndis_filter_packet; + + +/* + * Externs + */ + +extern int hv_rf_on_receive(struct hv_device *device, netvsc_packet *pkt); +extern int hv_rf_on_device_add(struct hv_device *device, void *additl_info); +extern int hv_rf_on_device_remove(struct hv_device *device, + boolean_t destroy_channel); +extern int hv_rf_on_open(struct hv_device *device); +extern int hv_rf_on_close(struct hv_device *device); +extern int hv_rf_on_send(struct hv_device *device, netvsc_packet *pkt); + + +#endif /* __HV_RNDIS_FILTER_H__ */ + diff --git a/sys/dev/hyperv/stordisengage/hv_ata_pci_disengage.c b/sys/dev/hyperv/stordisengage/hv_ata_pci_disengage.c new file mode 100644 index 00000000000..9e5cf55c80e --- /dev/null +++ b/sys/dev/hyperv/stordisengage/hv_ata_pci_disengage.c @@ -0,0 +1,204 @@ +/*- + * Copyright (c) 1998 - 2008 Søren Schmidt + * 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, + * without modification, immediately at the beginning of the file. + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ +/*- + * Copyright (c) 2009-2013 Microsoft Corp. + * Copyright (c) 2012 NetApp Inc. + * Copyright (c) 2012 Citrix Inc. + * 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 unmodified, 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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 +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define HV_X64_MSR_GUEST_OS_ID 0x40000000 +#define HV_X64_CPUID_MIN 0x40000005 +#define HV_X64_CPUID_MAX 0x4000ffff + +/* prototypes */ +static int hv_ata_pci_probe(device_t dev); +static int hv_ata_pci_attach(device_t dev); +static int hv_ata_pci_detach(device_t dev); + +static int hv_check_for_hyper_v(void); + +/* + * generic PCI ATA device probe + */ +static int +hv_ata_pci_probe(device_t dev) +{ + int ata_disk_enable; + + ata_disk_enable = 0; + + /* + * Don't probe if not running in a Hyper-V environment + */ + if (!hv_check_for_hyper_v()) + return (ENXIO); + + if (bootverbose) + device_printf(dev, + "hv_ata_pci_probe dev_class/subslcass = %d, %d\n", + pci_get_class(dev), pci_get_subclass(dev)); + + /* is this a storage class device ? */ + if (pci_get_class(dev) != PCIC_STORAGE) + return (ENXIO); + + /* is this an IDE/ATA type device ? */ + if (pci_get_subclass(dev) != PCIS_STORAGE_IDE) + return (ENXIO); + + if(bootverbose) + device_printf(dev, + "Hyper-V probe for disabling ATA-PCI, emulated driver\n"); + + /* + * On Hyper-V the default is to use the enlightened driver for + * IDE disks. However, if the user wishes to use the native + * ATA driver, the environment variable + * hw_ata.disk_enable must be explicitly set to 1. + */ + if (getenv_int("hw.ata.disk_enable", &ata_disk_enable)) { + if(bootverbose) + device_printf(dev, + "hw.ata.disk_enable flag is disabling Hyper-V" + " ATA driver support\n"); + return (ENXIO); + } + + if (bootverbose) + device_printf(dev, "Hyper-V ATA storage driver enabled.\n"); + + return (BUS_PROBE_VENDOR); +} + +static int +hv_ata_pci_attach(device_t dev) +{ + + return (0); +} + +static int +hv_ata_pci_detach(device_t dev) +{ + + return (0); +} + +/** +* Detect Hyper-V and enable fast IDE +* via enlighted storage driver +*/ +static int +hv_check_for_hyper_v(void) +{ + u_int regs[4]; + int hyper_v_detected; + + hyper_v_detected = 0; + do_cpuid(1, regs); + if (regs[2] & 0x80000000) { + /* + * if(a hypervisor is detected) + * make sure this really is Hyper-V + */ + do_cpuid(HV_X64_MSR_GUEST_OS_ID, regs); + hyper_v_detected = + regs[0] >= HV_X64_CPUID_MIN && + regs[0] <= HV_X64_CPUID_MAX && + !memcmp("Microsoft Hv", ®s[1], 12); + } + + return (hyper_v_detected); +} + +static device_method_t hv_ata_pci_methods[] = { + /* device interface */ + DEVMETHOD(device_probe, hv_ata_pci_probe), + DEVMETHOD(device_attach, hv_ata_pci_attach), + DEVMETHOD(device_detach, hv_ata_pci_detach), + DEVMETHOD(device_shutdown, bus_generic_shutdown), + + DEVMETHOD_END +}; + +devclass_t hv_ata_pci_devclass; + +static driver_t hv_ata_pci_disengage_driver = { + "pciata-disable", + hv_ata_pci_methods, + sizeof(struct ata_pci_controller), +}; + +DRIVER_MODULE(atapci_dis, pci, hv_ata_pci_disengage_driver, + hv_ata_pci_devclass, NULL, NULL); +MODULE_VERSION(atapci_dis, 1); +MODULE_DEPEND(atapci_dis, ata, 1, 1, 1); diff --git a/sys/dev/hyperv/storvsc/hv_storvsc_drv_freebsd.c b/sys/dev/hyperv/storvsc/hv_storvsc_drv_freebsd.c new file mode 100644 index 00000000000..c89f42e0fee --- /dev/null +++ b/sys/dev/hyperv/storvsc/hv_storvsc_drv_freebsd.c @@ -0,0 +1,1478 @@ +/*- + * Copyright (c) 2009-2012 Microsoft Corp. + * Copyright (c) 2012 NetApp Inc. + * Copyright (c) 2012 Citrix Inc. + * 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 unmodified, 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +/** + * StorVSC driver for Hyper-V. This driver presents a SCSI HBA interface + * to the Comman Access Method (CAM) layer. CAM control blocks (CCBs) are + * converted into VSCSI protocol messages which are delivered to the parent + * partition StorVSP driver over the Hyper-V VMBUS. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#include +#include "hv_vstorage.h" + +#define STORVSC_RINGBUFFER_SIZE (20*PAGE_SIZE) +#define STORVSC_MAX_LUNS_PER_TARGET (64) +#define STORVSC_MAX_IO_REQUESTS (STORVSC_MAX_LUNS_PER_TARGET * 2) +#define BLKVSC_MAX_IDE_DISKS_PER_TARGET (1) +#define BLKVSC_MAX_IO_REQUESTS STORVSC_MAX_IO_REQUESTS +#define STORVSC_MAX_TARGETS (1) + +struct storvsc_softc; + +enum storvsc_request_type { + WRITE_TYPE, + READ_TYPE, + UNKNOWN_TYPE +}; + +struct hv_storvsc_request { + LIST_ENTRY(hv_storvsc_request) link; + struct vstor_packet vstor_packet; + hv_vmbus_multipage_buffer data_buf; + void *sense_data; + uint8_t sense_info_len; + uint8_t retries; + union ccb *ccb; + struct storvsc_softc *softc; + struct callout callout; + struct sema synch_sema; /*Synchronize the request/response if needed */ +}; + +struct storvsc_softc { + struct hv_device *hs_dev; + LIST_HEAD(, hv_storvsc_request) hs_free_list; + struct mtx hs_lock; + struct storvsc_driver_props *hs_drv_props; + int hs_unit; + uint32_t hs_frozen; + struct cam_sim *hs_sim; + struct cam_path *hs_path; + uint32_t hs_num_out_reqs; + boolean_t hs_destroy; + boolean_t hs_drain_notify; + struct sema hs_drain_sema; + struct hv_storvsc_request hs_init_req; + struct hv_storvsc_request hs_reset_req; +}; + + +/** + * HyperV storvsc timeout testing cases: + * a. IO returned after first timeout; + * b. IO returned after second timeout and queue freeze; + * c. IO returned while timer handler is running + * The first can be tested by "sg_senddiag -vv /dev/daX", + * and the second and third can be done by + * "sg_wr_mode -v -p 08 -c 0,1a -m 0,ff /dev/daX". + */ +#define HVS_TIMEOUT_TEST 0 + +/* + * Bus/adapter reset functionality on the Hyper-V host is + * buggy and it will be disabled until + * it can be further tested. + */ +#define HVS_HOST_RESET 0 + +struct storvsc_driver_props { + char *drv_name; + char *drv_desc; + uint8_t drv_max_luns_per_target; + uint8_t drv_max_ios_per_target; + uint32_t drv_ringbuffer_size; +}; + +enum hv_storage_type { + DRIVER_BLKVSC, + DRIVER_STORVSC, + DRIVER_UNKNOWN +}; + +#define HS_MAX_ADAPTERS 10 + +/* {ba6163d9-04a1-4d29-b605-72e2ffb1dc7f} */ +static const hv_guid gStorVscDeviceType={ + .data = {0xd9, 0x63, 0x61, 0xba, 0xa1, 0x04, 0x29, 0x4d, + 0xb6, 0x05, 0x72, 0xe2, 0xff, 0xb1, 0xdc, 0x7f} +}; + +/* {32412632-86cb-44a2-9b5c-50d1417354f5} */ +static const hv_guid gBlkVscDeviceType={ + .data = {0x32, 0x26, 0x41, 0x32, 0xcb, 0x86, 0xa2, 0x44, + 0x9b, 0x5c, 0x50, 0xd1, 0x41, 0x73, 0x54, 0xf5} +}; + +static struct storvsc_driver_props g_drv_props_table[] = { + {"blkvsc", "Hyper-V IDE Storage Interface", + BLKVSC_MAX_IDE_DISKS_PER_TARGET, BLKVSC_MAX_IO_REQUESTS, + STORVSC_RINGBUFFER_SIZE}, + {"storvsc", "Hyper-V SCSI Storage Interface", + STORVSC_MAX_LUNS_PER_TARGET, STORVSC_MAX_IO_REQUESTS, + STORVSC_RINGBUFFER_SIZE} +}; + +static struct storvsc_softc *hs_softc[HS_MAX_ADAPTERS]; + +/* static functions */ +static int storvsc_probe(device_t dev); +static int storvsc_attach(device_t dev); +static int storvsc_detach(device_t dev); +static void storvsc_poll(struct cam_sim * sim); +static void storvsc_action(struct cam_sim * sim, union ccb * ccb); +static void scan_for_luns(struct storvsc_softc * sc); +static void create_storvsc_request(union ccb *ccb, struct hv_storvsc_request *reqp); +static void storvsc_free_request(struct storvsc_softc *sc, struct hv_storvsc_request *reqp); +static enum hv_storage_type storvsc_get_storage_type(device_t dev); +static void hv_storvsc_on_channel_callback(void *context); +static void hv_storvsc_on_iocompletion( struct storvsc_softc *sc, + struct vstor_packet *vstor_packet, + struct hv_storvsc_request *request); +static int hv_storvsc_connect_vsp(struct hv_device *device); +static void storvsc_io_done(struct hv_storvsc_request *reqp); + +static device_method_t storvsc_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, storvsc_probe), + DEVMETHOD(device_attach, storvsc_attach), + DEVMETHOD(device_detach, storvsc_detach), + DEVMETHOD(device_shutdown, bus_generic_shutdown), + DEVMETHOD_END +}; + +static driver_t storvsc_driver = { + "storvsc", storvsc_methods, sizeof(struct storvsc_softc), +}; + +static devclass_t storvsc_devclass; +DRIVER_MODULE(storvsc, vmbus, storvsc_driver, storvsc_devclass, 0, 0); +MODULE_VERSION(storvsc, 1); +MODULE_DEPEND(storvsc, vmbus, 1, 1, 1); + + +/** + * The host is capable of sending messages to us that are + * completely unsolicited. So, we need to address the race + * condition where we may be in the process of unloading the + * driver when the host may send us an unsolicited message. + * We address this issue by implementing a sequentially + * consistent protocol: + * + * 1. Channel callback is invoked while holding the the channel lock + * and an unloading driver will reset the channel callback under + * the protection of this channel lock. + * + * 2. To ensure bounded wait time for unloading a driver, we don't + * permit outgoing traffic once the device is marked as being + * destroyed. + * + * 3. Once the device is marked as being destroyed, we only + * permit incoming traffic to properly account for + * packets already sent out. + */ +static inline struct storvsc_softc * +get_stor_device(struct hv_device *device, + boolean_t outbound) +{ + struct storvsc_softc *sc; + + sc = device_get_softc(device->device); + if (sc == NULL) { + return NULL; + } + + if (outbound) { + /* + * Here we permit outgoing I/O only + * if the device is not being destroyed. + */ + + if (sc->hs_destroy) { + sc = NULL; + } + } else { + /* + * inbound case; if being destroyed + * only permit to account for + * messages already sent out. + */ + if (sc->hs_destroy && (sc->hs_num_out_reqs == 0)) { + sc = NULL; + } + } + return sc; +} + +/** + * @brief initialize channel connection to parent partition + * + * @param dev a Hyper-V device pointer + * @returns 0 on success, non-zero error on failure + */ +static int +hv_storvsc_channel_init(struct hv_device *dev) +{ + int ret = 0; + struct hv_storvsc_request *request; + struct vstor_packet *vstor_packet; + struct storvsc_softc *sc; + + sc = get_stor_device(dev, TRUE); + if (sc == NULL) { + return ENODEV; + } + + request = &sc->hs_init_req; + memset(request, 0, sizeof(struct hv_storvsc_request)); + vstor_packet = &request->vstor_packet; + request->softc = sc; + + /** + * Initiate the vsc/vsp initialization protocol on the open channel + */ + sema_init(&request->synch_sema, 0, ("stor_synch_sema")); + + vstor_packet->operation = VSTOR_OPERATION_BEGININITIALIZATION; + vstor_packet->flags = REQUEST_COMPLETION_FLAG; + + + ret = hv_vmbus_channel_send_packet( + dev->channel, + vstor_packet, + sizeof(struct vstor_packet), + (uint64_t)request, + HV_VMBUS_PACKET_TYPE_DATA_IN_BAND, + HV_VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); + + if (ret != 0) { + goto cleanup; + } + + ret = sema_timedwait(&request->synch_sema, 500); /* KYS 5 seconds */ + + if (ret != 0) { + goto cleanup; + } + + if (vstor_packet->operation != VSTOR_OPERATION_COMPLETEIO || + vstor_packet->status != 0) { + goto cleanup; + } + + /* reuse the packet for version range supported */ + + memset(vstor_packet, 0, sizeof(struct vstor_packet)); + vstor_packet->operation = VSTOR_OPERATION_QUERYPROTOCOLVERSION; + vstor_packet->flags = REQUEST_COMPLETION_FLAG; + + vstor_packet->version.major_minor = VMSTOR_PROTOCOL_VERSION_CURRENT; + + /* revision is only significant for Windows guests */ + vstor_packet->version.revision = 0; + + ret = hv_vmbus_channel_send_packet( + dev->channel, + vstor_packet, + sizeof(struct vstor_packet), + (uint64_t)request, + HV_VMBUS_PACKET_TYPE_DATA_IN_BAND, + HV_VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); + + if (ret != 0) { + goto cleanup; + } + + ret = sema_timedwait(&request->synch_sema, 500); /* KYS 5 seconds */ + + if (ret) { + goto cleanup; + } + + /* TODO: Check returned version */ + if (vstor_packet->operation != VSTOR_OPERATION_COMPLETEIO || + vstor_packet->status != 0) { + goto cleanup; + } + + /** + * Query channel properties + */ + memset(vstor_packet, 0, sizeof(struct vstor_packet)); + vstor_packet->operation = VSTOR_OPERATION_QUERYPROPERTIES; + vstor_packet->flags = REQUEST_COMPLETION_FLAG; + + ret = hv_vmbus_channel_send_packet( + dev->channel, + vstor_packet, + sizeof(struct vstor_packet), + (uint64_t)request, + HV_VMBUS_PACKET_TYPE_DATA_IN_BAND, + HV_VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); + + if ( ret != 0) { + goto cleanup; + } + + ret = sema_timedwait(&request->synch_sema, 500); /* KYS 5 seconds */ + + if (ret != 0) { + goto cleanup; + } + + /* TODO: Check returned version */ + if (vstor_packet->operation != VSTOR_OPERATION_COMPLETEIO || + vstor_packet->status != 0) { + goto cleanup; + } + + memset(vstor_packet, 0, sizeof(struct vstor_packet)); + vstor_packet->operation = VSTOR_OPERATION_ENDINITIALIZATION; + vstor_packet->flags = REQUEST_COMPLETION_FLAG; + + ret = hv_vmbus_channel_send_packet( + dev->channel, + vstor_packet, + sizeof(struct vstor_packet), + (uint64_t)request, + HV_VMBUS_PACKET_TYPE_DATA_IN_BAND, + HV_VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); + + if (ret != 0) { + goto cleanup; + } + + ret = sema_timedwait(&request->synch_sema, 500); /* KYS 5 seconds */ + + if (ret != 0) { + goto cleanup; + } + + if (vstor_packet->operation != VSTOR_OPERATION_COMPLETEIO || + vstor_packet->status != 0) { + goto cleanup; + } + +cleanup: + sema_destroy(&request->synch_sema); + return (ret); +} + +/** + * @brief Open channel connection to paraent partition StorVSP driver + * + * Open and initialize channel connection to parent partition StorVSP driver. + * + * @param pointer to a Hyper-V device + * @returns 0 on success, non-zero error on failure + */ +static int +hv_storvsc_connect_vsp(struct hv_device *dev) +{ + int ret = 0; + struct vmstor_chan_props props; + struct storvsc_softc *sc; + + sc = device_get_softc(dev->device); + + memset(&props, 0, sizeof(struct vmstor_chan_props)); + + /* + * Open the channel + */ + + ret = hv_vmbus_channel_open( + dev->channel, + sc->hs_drv_props->drv_ringbuffer_size, + sc->hs_drv_props->drv_ringbuffer_size, + (void *)&props, + sizeof(struct vmstor_chan_props), + hv_storvsc_on_channel_callback, + dev); + + + if (ret != 0) { + return ret; + } + + ret = hv_storvsc_channel_init(dev); + + return (ret); +} + +#if HVS_HOST_RESET +static int +hv_storvsc_host_reset(struct hv_device *dev) +{ + int ret = 0; + struct storvsc_softc *sc; + + struct hv_storvsc_request *request; + struct vstor_packet *vstor_packet; + + sc = get_stor_device(dev, TRUE); + if (sc == NULL) { + return ENODEV; + } + + request = &sc->hs_reset_req; + request->softc = sc; + vstor_packet = &request->vstor_packet; + + sema_init(&request->synch_sema, 0, "stor synch sema"); + + vstor_packet->operation = VSTOR_OPERATION_RESETBUS; + vstor_packet->flags = REQUEST_COMPLETION_FLAG; + + ret = hv_vmbus_channel_send_packet(dev->channel, + vstor_packet, + sizeof(struct vstor_packet), + (uint64_t)&sc->hs_reset_req, + HV_VMBUS_PACKET_TYPE_DATA_IN_BAND, + HV_VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); + + if (ret != 0) { + goto cleanup; + } + + ret = sema_timedwait(&request->synch_sema, 500); /* KYS 5 seconds */ + + if (ret) { + goto cleanup; + } + + + /* + * At this point, all outstanding requests in the adapter + * should have been flushed out and return to us + */ + +cleanup: + sema_destroy(&request->synch_sema); + return (ret); +} +#endif /* HVS_HOST_RESET */ + +/** + * @brief Function to initiate an I/O request + * + * @param device Hyper-V device pointer + * @param request pointer to a request structure + * @returns 0 on success, non-zero error on failure + */ +static int +hv_storvsc_io_request(struct hv_device *device, + struct hv_storvsc_request *request) +{ + struct storvsc_softc *sc; + struct vstor_packet *vstor_packet = &request->vstor_packet; + int ret = 0; + + sc = get_stor_device(device, TRUE); + + if (sc == NULL) { + return ENODEV; + } + + vstor_packet->flags |= REQUEST_COMPLETION_FLAG; + + vstor_packet->vm_srb.length = sizeof(struct vmscsi_req); + + vstor_packet->vm_srb.sense_info_len = SENSE_BUFFER_SIZE; + + vstor_packet->vm_srb.transfer_len = request->data_buf.length; + + vstor_packet->operation = VSTOR_OPERATION_EXECUTESRB; + + + mtx_unlock(&request->softc->hs_lock); + if (request->data_buf.length) { + ret = hv_vmbus_channel_send_packet_multipagebuffer( + device->channel, + &request->data_buf, + vstor_packet, + sizeof(struct vstor_packet), + (uint64_t)request); + + } else { + ret = hv_vmbus_channel_send_packet( + device->channel, + vstor_packet, + sizeof(struct vstor_packet), + (uint64_t)request, + HV_VMBUS_PACKET_TYPE_DATA_IN_BAND, + HV_VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); + } + mtx_lock(&request->softc->hs_lock); + + if (ret != 0) { + printf("Unable to send packet %p ret %d", vstor_packet, ret); + } else { + atomic_add_int(&sc->hs_num_out_reqs, 1); + } + + return (ret); +} + + +/** + * Process IO_COMPLETION_OPERATION and ready + * the result to be completed for upper layer + * processing by the CAM layer. + */ +static void +hv_storvsc_on_iocompletion(struct storvsc_softc *sc, + struct vstor_packet *vstor_packet, + struct hv_storvsc_request *request) +{ + struct vmscsi_req *vm_srb; + + vm_srb = &vstor_packet->vm_srb; + + request->sense_info_len = 0; + if (((vm_srb->scsi_status & 0xFF) == SCSI_STATUS_CHECK_COND) && + (vm_srb->srb_status & SRB_STATUS_AUTOSENSE_VALID)) { + /* Autosense data available */ + + KASSERT(vm_srb->sense_info_len <= request->sense_info_len, + ("vm_srb->sense_info_len <= " + "request->sense_info_len")); + + memcpy(request->sense_data, vm_srb->sense_data, + vm_srb->sense_info_len); + + request->sense_info_len = vm_srb->sense_info_len; + } + + /* Complete request by passing to the CAM layer */ + storvsc_io_done(request); + atomic_subtract_int(&sc->hs_num_out_reqs, 1); + if (sc->hs_drain_notify && (sc->hs_num_out_reqs == 0)) { + sema_post(&sc->hs_drain_sema); + } +} + +static void +hv_storvsc_on_channel_callback(void *context) +{ + int ret = 0; + struct hv_device *device = (struct hv_device *)context; + struct storvsc_softc *sc; + uint32_t bytes_recvd; + uint64_t request_id; + uint8_t packet[roundup2(sizeof(struct vstor_packet), 8)]; + struct hv_storvsc_request *request; + struct vstor_packet *vstor_packet; + + sc = get_stor_device(device, FALSE); + if (sc == NULL) { + return; + } + + KASSERT(device, ("device")); + + ret = hv_vmbus_channel_recv_packet( + device->channel, + packet, + roundup2(sizeof(struct vstor_packet), 8), + &bytes_recvd, + &request_id); + + while ((ret == 0) && (bytes_recvd > 0)) { + request = (struct hv_storvsc_request *)request_id; + KASSERT(request, ("request")); + + if ((request == &sc->hs_init_req) || + (request == &sc->hs_reset_req)) { + memcpy(&request->vstor_packet, packet, + sizeof(struct vstor_packet)); + sema_post(&request->synch_sema); + } else { + vstor_packet = (struct vstor_packet *)packet; + switch(vstor_packet->operation) { + case VSTOR_OPERATION_COMPLETEIO: + hv_storvsc_on_iocompletion(sc, + vstor_packet, request); + break; + case VSTOR_OPERATION_REMOVEDEVICE: + /* TODO: implement */ + break; + default: + break; + } + } + ret = hv_vmbus_channel_recv_packet( + device->channel, + packet, + roundup2(sizeof(struct vstor_packet), 8), + &bytes_recvd, + &request_id); + } +} + +/** + * @brief callback function for completing a single LUN scan + * + * This function is responsible for waking up the executer of + * the scan LUN CCB action (cam_periph_runccb.) cam_periph_ccbwait + * sleeps on the mutex being signaled. + * + * @param periph a pointer to a CAM peripheral + * @param done_ccb pointer to CAM control block + */ +static void +storvsc_xptdone(struct cam_periph *periph, union ccb *done_ccb) +{ + wakeup(&done_ccb->ccb_h.cbfcnp); +} + +/** + * @brief scan for attached logical unit numbers (LUNs) + * + * In Hyper-V there is no backend changed device operation which + * presents FreeBSD with a list of devices to connect. The result is + * that we have to scan for a list of luns in the storvsc_attach() + * routine. There is only one SCSI target, so scan for the maximum + * number of luns. + * + * @param pointer to softc + */ +static void +scan_for_luns(struct storvsc_softc *sc) +{ + union ccb *request_ccb; + struct cam_path *path = sc->hs_path; + struct cam_path *my_path = NULL; + cam_status status; + int lun_nb = 0; + int error; + + request_ccb = malloc(sizeof(union ccb), M_CAMXPT, M_WAITOK); + my_path = malloc(sizeof(*my_path), M_CAMXPT, M_WAITOK); + + mtx_lock(&sc->hs_lock); + do { + /* + * Scan the next LUN. Reuse path and ccb structs. + */ + bzero(my_path, sizeof(*my_path)); + bzero(request_ccb, sizeof(*request_ccb)); + status = xpt_compile_path(my_path, + xpt_periph, + path->bus->path_id, + 0, + lun_nb); + + if (status != CAM_REQ_CMP) { + mtx_unlock(&sc->hs_lock); + xpt_print(path, "scan_for_lunYYY: can't compile" + " path, 0x%p can't continue\n", + sc->hs_path); + free(request_ccb, M_CAMXPT); + free(my_path, M_CAMXPT); + return; + } + + xpt_setup_ccb(&request_ccb->ccb_h, my_path, 5); + request_ccb->ccb_h.func_code = XPT_SCAN_LUN; + request_ccb->ccb_h.cbfcnp = storvsc_xptdone; + request_ccb->crcn.flags = CAM_FLAG_NONE; + + error = cam_periph_runccb(request_ccb, NULL, + CAM_FLAG_NONE, 0, NULL); + KASSERT(error == 0, ("cam_periph_runccb failed %d\n", error)); + xpt_release_path(my_path); + } while ( ++lun_nb < sc->hs_drv_props->drv_max_luns_per_target); + mtx_unlock(&sc->hs_lock); + free(request_ccb, M_CAMXPT); + free(my_path, M_CAMXPT); +} + +/** + * @brief StorVSC probe function + * + * Device probe function. Returns 0 if the input device is a StorVSC + * device. Otherwise, a ENXIO is returned. If the input device is + * for BlkVSC (paravirtual IDE) device and this support is disabled in + * favor of the emulated ATA/IDE device, return ENXIO. + * + * @param a device + * @returns 0 on success, ENXIO if not a matcing StorVSC device + */ +static int +storvsc_probe(device_t dev) +{ + int ata_disk_enable = 0; + int ret = ENXIO; + + switch (storvsc_get_storage_type(dev)) { + case DRIVER_BLKVSC: + if(bootverbose) + device_printf(dev, "DRIVER_BLKVSC-Emulated ATA/IDE probe\n"); + if (!getenv_int("hw.ata.disk_enable", &ata_disk_enable)) { + if(bootverbose) + device_printf(dev, + "Enlightened ATA/IDE detected\n"); + ret = 0; + } else if(bootverbose) + device_printf(dev, "Emulated ATA/IDE set (hw.ata.disk_enable set)\n"); + break; + case DRIVER_STORVSC: + if(bootverbose) + device_printf(dev, "Enlightened SCSI device detected\n"); + ret = 0; + break; + default: + ret = ENXIO; + } + return (ret); +} + +/** + * @brief StorVSC attach function + * + * Function responsible for allocating per-device structures, + * setting up CAM interfaces and scanning for available LUNs to + * be used for SCSI device peripherals. + * + * @param a device + * @returns 0 on success or an error on failure + */ +static int +storvsc_attach(device_t dev) +{ + struct hv_device *hv_dev = vmbus_get_devctx(dev); + enum hv_storage_type stor_type; + struct storvsc_softc *sc; + struct cam_devq *devq; + int ret, i; + struct hv_storvsc_request *reqp; + struct root_hold_token *root_mount_token = NULL; + + /* + * We need to serialize storvsc attach calls. + */ + root_mount_token = root_mount_hold("storvsc"); + + sc = device_get_softc(dev); + if (sc == NULL) { + ret = ENOMEM; + goto cleanup; + } + + stor_type = storvsc_get_storage_type(dev); + + if (stor_type == DRIVER_UNKNOWN) { + ret = ENODEV; + goto cleanup; + } + + bzero(sc, sizeof(struct storvsc_softc)); + + /* fill in driver specific properties */ + sc->hs_drv_props = &g_drv_props_table[stor_type]; + + /* fill in device specific properties */ + sc->hs_unit = device_get_unit(dev); + sc->hs_dev = hv_dev; + device_set_desc(dev, g_drv_props_table[stor_type].drv_desc); + + LIST_INIT(&sc->hs_free_list); + mtx_init(&sc->hs_lock, "hvslck", NULL, MTX_DEF); + + for (i = 0; i < sc->hs_drv_props->drv_max_ios_per_target; ++i) { + reqp = malloc(sizeof(struct hv_storvsc_request), + M_DEVBUF, M_WAITOK|M_ZERO); + reqp->softc = sc; + + LIST_INSERT_HEAD(&sc->hs_free_list, reqp, link); + } + + sc->hs_destroy = FALSE; + sc->hs_drain_notify = FALSE; + sema_init(&sc->hs_drain_sema, 0, "Store Drain Sema"); + + ret = hv_storvsc_connect_vsp(hv_dev); + if (ret != 0) { + goto cleanup; + } + + /* + * Create the device queue. + * Hyper-V maps each target to one SCSI HBA + */ + devq = cam_simq_alloc(sc->hs_drv_props->drv_max_ios_per_target); + if (devq == NULL) { + device_printf(dev, "Failed to alloc device queue\n"); + ret = ENOMEM; + goto cleanup; + } + + sc->hs_sim = cam_sim_alloc(storvsc_action, + storvsc_poll, + sc->hs_drv_props->drv_name, + sc, + sc->hs_unit, + &sc->hs_lock, 1, + sc->hs_drv_props->drv_max_ios_per_target, + devq); + + if (sc->hs_sim == NULL) { + device_printf(dev, "Failed to alloc sim\n"); + cam_simq_free(devq); + ret = ENOMEM; + goto cleanup; + } + + mtx_lock(&sc->hs_lock); + /* bus_id is set to 0, need to get it from VMBUS channel query? */ + if (xpt_bus_register(sc->hs_sim, dev, 0) != CAM_SUCCESS) { + cam_sim_free(sc->hs_sim, /*free_devq*/TRUE); + mtx_unlock(&sc->hs_lock); + device_printf(dev, "Unable to register SCSI bus\n"); + ret = ENXIO; + goto cleanup; + } + + if (xpt_create_path(&sc->hs_path, /*periph*/NULL, + cam_sim_path(sc->hs_sim), + CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD) != CAM_REQ_CMP) { + xpt_bus_deregister(cam_sim_path(sc->hs_sim)); + cam_sim_free(sc->hs_sim, /*free_devq*/TRUE); + mtx_unlock(&sc->hs_lock); + device_printf(dev, "Unable to create path\n"); + ret = ENXIO; + goto cleanup; + } + + mtx_unlock(&sc->hs_lock); + scan_for_luns(sc); + for (i = 0; (hs_softc[i] != NULL) && (i < HS_MAX_ADAPTERS); i++); + KASSERT(i < HS_MAX_ADAPTERS, ("storvsc_attach: hs_softc full\n")); + hs_softc[i] = sc; + + root_mount_rel(root_mount_token); + return (0); + + +cleanup: + root_mount_rel(root_mount_token); + while (!LIST_EMPTY(&sc->hs_free_list)) { + reqp = LIST_FIRST(&sc->hs_free_list); + LIST_REMOVE(reqp, link); + free(reqp, M_DEVBUF); + } + return (ret); +} + +/** + * @brief StorVSC device detach function + * + * This function is responsible for safely detaching a + * StorVSC device. This includes waiting for inbound responses + * to complete and freeing associated per-device structures. + * + * @param dev a device + * returns 0 on success + */ +static int +storvsc_detach(device_t dev) +{ + struct storvsc_softc *sc = device_get_softc(dev); + struct hv_storvsc_request *reqp = NULL; + struct hv_device *hv_device = vmbus_get_devctx(dev); + + mtx_lock(&hv_device->channel->inbound_lock); + sc->hs_destroy = TRUE; + mtx_unlock(&hv_device->channel->inbound_lock); + + /* + * At this point, all outbound traffic should be disabled. We + * only allow inbound traffic (responses) to proceed so that + * outstanding requests can be completed. + */ + + sc->hs_drain_notify = TRUE; + sema_wait(&sc->hs_drain_sema); + sc->hs_drain_notify = FALSE; + + /* + * Since we have already drained, we don't need to busy wait. + * The call to close the channel will reset the callback + * under the protection of the incoming channel lock. + */ + + hv_vmbus_channel_close(hv_device->channel); + + mtx_lock(&sc->hs_lock); + while (!LIST_EMPTY(&sc->hs_free_list)) { + reqp = LIST_FIRST(&sc->hs_free_list); + LIST_REMOVE(reqp, link); + + free(reqp, M_DEVBUF); + } + mtx_unlock(&sc->hs_lock); + return (0); +} + +#if HVS_TIMEOUT_TEST +/** + * @brief unit test for timed out operations + * + * This function provides unit testing capability to simulate + * timed out operations. Recompilation with HV_TIMEOUT_TEST=1 + * is required. + * + * @param reqp pointer to a request structure + * @param opcode SCSI operation being performed + * @param wait if 1, wait for I/O to complete + */ +static void +storvsc_timeout_test(struct hv_storvsc_request *reqp, + uint8_t opcode, int wait) +{ + int ret; + union ccb *ccb = reqp->ccb; + struct storvsc_softc *sc = reqp->softc; + + if (reqp->vstor_packet.vm_srb.cdb[0] != opcode) { + return; + } + + if (wait) { + mtx_lock(&reqp->event.mtx); + } + ret = hv_storvsc_io_request(sc->hs_dev, reqp); + if (ret != 0) { + if (wait) { + mtx_unlock(&reqp->event.mtx); + } + printf("%s: io_request failed with %d.\n", + __func__, ret); + ccb->ccb_h.status = CAM_PROVIDE_FAIL; + mtx_lock(&sc->hs_lock); + storvsc_free_request(sc, reqp); + xpt_done(ccb); + mtx_unlock(&sc->hs_lock); + return; + } + + if (wait) { + xpt_print(ccb->ccb_h.path, + "%u: %s: waiting for IO return.\n", + ticks, __func__); + ret = cv_timedwait(&reqp->event.cv, &reqp->event.mtx, 60*hz); + mtx_unlock(&reqp->event.mtx); + xpt_print(ccb->ccb_h.path, "%u: %s: %s.\n", + ticks, __func__, (ret == 0)? + "IO return detected" : + "IO return not detected"); + /* + * Now both the timer handler and io done are running + * simultaneously. We want to confirm the io done always + * finishes after the timer handler exits. So reqp used by + * timer handler is not freed or stale. Do busy loop for + * another 1/10 second to make sure io done does + * wait for the timer handler to complete. + */ + DELAY(100*1000); + mtx_lock(&sc->hs_lock); + xpt_print(ccb->ccb_h.path, + "%u: %s: finishing, queue frozen %d, " + "ccb status 0x%x scsi_status 0x%x.\n", + ticks, __func__, sc->hs_frozen, + ccb->ccb_h.status, + ccb->csio.scsi_status); + mtx_unlock(&sc->hs_lock); + } +} +#endif /* HVS_TIMEOUT_TEST */ + +/** + * @brief timeout handler for requests + * + * This function is called as a result of a callout expiring. + * + * @param arg pointer to a request + */ +static void +storvsc_timeout(void *arg) +{ + struct hv_storvsc_request *reqp = arg; + struct storvsc_softc *sc = reqp->softc; + union ccb *ccb = reqp->ccb; + + if (reqp->retries == 0) { + mtx_lock(&sc->hs_lock); + xpt_print(ccb->ccb_h.path, + "%u: IO timed out (req=0x%p), wait for another %u secs.\n", + ticks, reqp, ccb->ccb_h.timeout / 1000); + cam_error_print(ccb, CAM_ESF_ALL, CAM_EPF_ALL); + mtx_unlock(&sc->hs_lock); + + reqp->retries++; + callout_reset(&reqp->callout, + (ccb->ccb_h.timeout * hz) / 1000, + storvsc_timeout, reqp); +#if HVS_TIMEOUT_TEST + storvsc_timeout_test(reqp, SEND_DIAGNOSTIC, 0); +#endif + return; + } + + mtx_lock(&sc->hs_lock); + xpt_print(ccb->ccb_h.path, + "%u: IO (reqp = 0x%p) did not return for %u seconds, %s.\n", + ticks, reqp, ccb->ccb_h.timeout * (reqp->retries+1) / 1000, + (sc->hs_frozen == 0)? + "freezing the queue" : "the queue is already frozen"); + if (sc->hs_frozen == 0) { + sc->hs_frozen = 1; + xpt_freeze_simq(xpt_path_sim(ccb->ccb_h.path), 1); + } + mtx_unlock(&sc->hs_lock); + +#if HVS_TIMEOUT_TEST + storvsc_timeout_test(reqp, MODE_SELECT_10, 1); +#endif +} + +/** + * @brief StorVSC device poll function + * + * This function is responsible for servicing requests when + * interrupts are disabled (i.e when we are dumping core.) + * + * @param sim a pointer to a CAM SCSI interface module + */ +static void +storvsc_poll(struct cam_sim *sim) +{ + struct storvsc_softc *sc = cam_sim_softc(sim); + + mtx_assert(&sc->hs_lock, MA_OWNED); + mtx_unlock(&sc->hs_lock); + hv_storvsc_on_channel_callback(sc->hs_dev); + mtx_lock(&sc->hs_lock); +} + +/** + * @brief StorVSC device action function + * + * This function is responsible for handling SCSI operations which + * are passed from the CAM layer. The requests are in the form of + * CAM control blocks which indicate the action being performed. + * Not all actions require converting the request to a VSCSI protocol + * message - these actions can be responded to by this driver. + * Requests which are destined for a backend storage device are converted + * to a VSCSI protocol message and sent on the channel connection associated + * with this device. + * + * @param sim pointer to a CAM SCSI interface module + * @param ccb pointer to a CAM control block + */ +static void +storvsc_action(struct cam_sim *sim, union ccb *ccb) +{ + struct storvsc_softc *sc = cam_sim_softc(sim); + int res; + + mtx_assert(&sc->hs_lock, MA_OWNED); + switch (ccb->ccb_h.func_code) { + case XPT_PATH_INQ: { + struct ccb_pathinq *cpi = &ccb->cpi; + + cpi->version_num = 1; + cpi->hba_inquiry = PI_TAG_ABLE|PI_SDTR_ABLE; + cpi->target_sprt = 0; + cpi->hba_misc = PIM_NOBUSRESET; + cpi->hba_eng_cnt = 0; + cpi->max_target = STORVSC_MAX_TARGETS; + cpi->max_lun = sc->hs_drv_props->drv_max_luns_per_target; + cpi->initiator_id = 0; + cpi->bus_id = cam_sim_bus(sim); + cpi->base_transfer_speed = 300000; + cpi->transport = XPORT_SAS; + cpi->transport_version = 0; + cpi->protocol = PROTO_SCSI; + cpi->protocol_version = SCSI_REV_SPC2; + strncpy(cpi->sim_vid, "FreeBSD", SIM_IDLEN); + strncpy(cpi->hba_vid, sc->hs_drv_props->drv_name, HBA_IDLEN); + strncpy(cpi->dev_name, cam_sim_name(sim), DEV_IDLEN); + cpi->unit_number = cam_sim_unit(sim); + + ccb->ccb_h.status = CAM_REQ_CMP; + xpt_done(ccb); + return; + } + case XPT_GET_TRAN_SETTINGS: { + struct ccb_trans_settings *cts = &ccb->cts; + + cts->transport = XPORT_SAS; + cts->transport_version = 0; + cts->protocol = PROTO_SCSI; + cts->protocol_version = SCSI_REV_SPC2; + + /* enable tag queuing and disconnected mode */ + cts->proto_specific.valid = CTS_SCSI_VALID_TQ; + cts->proto_specific.scsi.valid = CTS_SCSI_VALID_TQ; + cts->proto_specific.scsi.flags = CTS_SCSI_FLAGS_TAG_ENB; + cts->xport_specific.valid = CTS_SPI_VALID_DISC; + cts->xport_specific.spi.flags = CTS_SPI_FLAGS_DISC_ENB; + + ccb->ccb_h.status = CAM_REQ_CMP; + xpt_done(ccb); + return; + } + case XPT_SET_TRAN_SETTINGS: { + ccb->ccb_h.status = CAM_REQ_CMP; + xpt_done(ccb); + return; + } + case XPT_CALC_GEOMETRY:{ + cam_calc_geometry(&ccb->ccg, 1); + xpt_done(ccb); + return; + } + case XPT_RESET_BUS: + case XPT_RESET_DEV:{ +#if HVS_HOST_RESET + if ((res = hv_storvsc_host_reset(sc->hs_dev)) != 0) { + xpt_print(ccb->ccb_h.path, + "hv_storvsc_host_reset failed with %d\n", res); + ccb->ccb_h.status = CAM_PROVIDE_FAIL; + xpt_done(ccb); + return; + } + ccb->ccb_h.status = CAM_REQ_CMP; + xpt_done(ccb); + return; +#else + xpt_print(ccb->ccb_h.path, + "%s reset not supported.\n", + (ccb->ccb_h.func_code == XPT_RESET_BUS)? + "bus" : "dev"); + ccb->ccb_h.status = CAM_REQ_INVALID; + xpt_done(ccb); + return; +#endif /* HVS_HOST_RESET */ + } + case XPT_SCSI_IO: + case XPT_IMMED_NOTIFY: { + struct hv_storvsc_request *reqp = NULL; + + if (ccb->csio.cdb_len == 0) { + panic("cdl_len is 0\n"); + } + + if (LIST_EMPTY(&sc->hs_free_list)) { + ccb->ccb_h.status = CAM_REQUEUE_REQ; + if (sc->hs_frozen == 0) { + sc->hs_frozen = 1; + xpt_freeze_simq(sim, /* count*/1); + } + xpt_done(ccb); + return; + } + + reqp = LIST_FIRST(&sc->hs_free_list); + LIST_REMOVE(reqp, link); + + bzero(reqp, sizeof(struct hv_storvsc_request)); + reqp->softc = sc; + + ccb->ccb_h.status |= CAM_SIM_QUEUED; + create_storvsc_request(ccb, reqp); + + if (ccb->ccb_h.timeout != CAM_TIME_INFINITY) { + callout_init(&reqp->callout, CALLOUT_MPSAFE); + callout_reset(&reqp->callout, + (ccb->ccb_h.timeout * hz) / 1000, + storvsc_timeout, reqp); +#if HVS_TIMEOUT_TEST + cv_init(&reqp->event.cv, "storvsc timeout cv"); + mtx_init(&reqp->event.mtx, "storvsc timeout mutex", + NULL, MTX_DEF); + switch (reqp->vstor_packet.vm_srb.cdb[0]) { + case MODE_SELECT_10: + case SEND_DIAGNOSTIC: + /* To have timer send the request. */ + return; + default: + break; + } +#endif /* HVS_TIMEOUT_TEST */ + } + + if ((res = hv_storvsc_io_request(sc->hs_dev, reqp)) != 0) { + xpt_print(ccb->ccb_h.path, + "hv_storvsc_io_request failed with %d\n", res); + ccb->ccb_h.status = CAM_PROVIDE_FAIL; + storvsc_free_request(sc, reqp); + xpt_done(ccb); + return; + } + return; + } + + default: + ccb->ccb_h.status = CAM_REQ_INVALID; + xpt_done(ccb); + return; + } +} + +/** + * @brief Fill in a request structure based on a CAM control block + * + * Fills in a request structure based on the contents of a CAM control + * block. The request structure holds the payload information for + * VSCSI protocol request. + * + * @param ccb pointer to a CAM contorl block + * @param reqp pointer to a request structure + */ +static void +create_storvsc_request(union ccb *ccb, struct hv_storvsc_request *reqp) +{ + struct ccb_scsiio *csio = &ccb->csio; + uint64_t phys_addr; + uint32_t bytes_to_copy = 0; + uint32_t pfn_num = 0; + uint32_t pfn; + + /* refer to struct vmscsi_req for meanings of these two fields */ + reqp->vstor_packet.vm_srb.port = + cam_sim_unit(xpt_path_sim(ccb->ccb_h.path)); + reqp->vstor_packet.vm_srb.path_id = + cam_sim_bus(xpt_path_sim(ccb->ccb_h.path)); + + reqp->vstor_packet.vm_srb.target_id = ccb->ccb_h.target_id; + reqp->vstor_packet.vm_srb.lun = ccb->ccb_h.target_lun; + + reqp->vstor_packet.vm_srb.cdb_len = csio->cdb_len; + if(ccb->ccb_h.flags & CAM_CDB_POINTER) { + memcpy(&reqp->vstor_packet.vm_srb.cdb, csio->cdb_io.cdb_ptr, + csio->cdb_len); + } else { + memcpy(&reqp->vstor_packet.vm_srb.cdb, csio->cdb_io.cdb_bytes, + csio->cdb_len); + } + + switch (ccb->ccb_h.flags & CAM_DIR_MASK) { + case CAM_DIR_OUT: + reqp->vstor_packet.vm_srb.data_in = WRITE_TYPE; + break; + case CAM_DIR_IN: + reqp->vstor_packet.vm_srb.data_in = READ_TYPE; + break; + case CAM_DIR_NONE: + reqp->vstor_packet.vm_srb.data_in = UNKNOWN_TYPE; + break; + default: + reqp->vstor_packet.vm_srb.data_in = UNKNOWN_TYPE; + break; + } + + reqp->sense_data = &csio->sense_data; + reqp->sense_info_len = csio->sense_len; + + reqp->ccb = ccb; + /* + KASSERT((ccb->ccb_h.flags & CAM_SCATTER_VALID) == 0, + ("ccb is scatter gather valid\n")); + */ + if (csio->dxfer_len != 0) { + reqp->data_buf.length = csio->dxfer_len; + bytes_to_copy = csio->dxfer_len; + phys_addr = vtophys(csio->data_ptr); + reqp->data_buf.offset = phys_addr - trunc_page(phys_addr); + } + + while (bytes_to_copy != 0) { + int bytes, page_offset; + phys_addr = vtophys(&csio->data_ptr[reqp->data_buf.length - + bytes_to_copy]); + pfn = phys_addr >> PAGE_SHIFT; + reqp->data_buf.pfn_array[pfn_num] = pfn; + page_offset = phys_addr - trunc_page(phys_addr); + + bytes = min(PAGE_SIZE - page_offset, bytes_to_copy); + + bytes_to_copy -= bytes; + pfn_num++; + } +} + +/** + * @brief completion function before returning to CAM + * + * I/O process has been completed and the result needs + * to be passed to the CAM layer. + * Free resources related to this request. + * + * @param reqp pointer to a request structure + */ +static void +storvsc_io_done(struct hv_storvsc_request *reqp) +{ + union ccb *ccb = reqp->ccb; + struct ccb_scsiio *csio = &ccb->csio; + struct storvsc_softc *sc = reqp->softc; + struct vmscsi_req *vm_srb = &reqp->vstor_packet.vm_srb; + + if (reqp->retries > 0) { + mtx_lock(&sc->hs_lock); +#if HVS_TIMEOUT_TEST + xpt_print(ccb->ccb_h.path, + "%u: IO returned after timeout, " + "waking up timer handler if any.\n", ticks); + mtx_lock(&reqp->event.mtx); + cv_signal(&reqp->event.cv); + mtx_unlock(&reqp->event.mtx); +#endif + reqp->retries = 0; + xpt_print(ccb->ccb_h.path, + "%u: IO returned after timeout, " + "stopping timer if any.\n", ticks); + mtx_unlock(&sc->hs_lock); + } + + /* + * callout_drain() will wait for the timer handler to finish + * if it is running. So we don't need any lock to synchronize + * between this routine and the timer handler. + * Note that we need to make sure reqp is not freed when timer + * handler is using or will use it. + */ + if (ccb->ccb_h.timeout != CAM_TIME_INFINITY) { + callout_drain(&reqp->callout); + } + + ccb->ccb_h.status &= ~CAM_SIM_QUEUED; + ccb->ccb_h.status &= ~CAM_STATUS_MASK; + if (vm_srb->scsi_status == SCSI_STATUS_OK) { + ccb->ccb_h.status |= CAM_REQ_CMP; + } else { + mtx_lock(&sc->hs_lock); + xpt_print(ccb->ccb_h.path, + "srovsc scsi_status = %d\n", + vm_srb->scsi_status); + mtx_unlock(&sc->hs_lock); + ccb->ccb_h.status |= CAM_SCSI_STATUS_ERROR; + } + + ccb->csio.scsi_status = (vm_srb->scsi_status & 0xFF); + ccb->csio.resid = ccb->csio.dxfer_len - vm_srb->transfer_len; + + if (reqp->sense_info_len != 0) { + csio->sense_resid = csio->sense_len - reqp->sense_info_len; + ccb->ccb_h.status |= CAM_AUTOSNS_VALID; + } + + mtx_lock(&sc->hs_lock); + if (reqp->softc->hs_frozen == 1) { + xpt_print(ccb->ccb_h.path, + "%u: storvsc unfreezing softc 0x%p.\n", + ticks, reqp->softc); + ccb->ccb_h.status |= CAM_RELEASE_SIMQ; + reqp->softc->hs_frozen = 0; + } + storvsc_free_request(sc, reqp); + xpt_done(ccb); + mtx_unlock(&sc->hs_lock); +} + +/** + * @brief Free a request structure + * + * Free a request structure by returning it to the free list + * + * @param sc pointer to a softc + * @param reqp pointer to a request structure + */ +static void +storvsc_free_request(struct storvsc_softc *sc, struct hv_storvsc_request *reqp) +{ + + LIST_INSERT_HEAD(&sc->hs_free_list, reqp, link); +} + +/** + * @brief Determine type of storage device from GUID + * + * Using the type GUID, determine if this is a StorVSC (paravirtual + * SCSI or BlkVSC (paravirtual IDE) device. + * + * @param dev a device + * returns an enum + */ +static enum hv_storage_type +storvsc_get_storage_type(device_t dev) +{ + const char *p = vmbus_get_type(dev); + + if (!memcmp(p, &gBlkVscDeviceType, sizeof(hv_guid))) { + return DRIVER_BLKVSC; + } else if (!memcmp(p, &gStorVscDeviceType, sizeof(hv_guid))) { + return DRIVER_STORVSC; + } + return (DRIVER_UNKNOWN); +} + diff --git a/sys/dev/hyperv/storvsc/hv_vstorage.h b/sys/dev/hyperv/storvsc/hv_vstorage.h new file mode 100644 index 00000000000..d01d0841147 --- /dev/null +++ b/sys/dev/hyperv/storvsc/hv_vstorage.h @@ -0,0 +1,231 @@ +/*- + * Copyright (c) 2009-2012 Microsoft Corp. + * Copyright (c) 2012 NetApp Inc. + * Copyright (c) 2012 Citrix Inc. + * 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 unmodified, 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#ifndef __HV_VSTORAGE_H__ +#define __HV_VSTORAGE_H__ + +/* + * Major/minor macros. Minor version is in LSB, meaning that earlier flat + * version numbers will be interpreted as "0.x" (i.e., 1 becomes 0.1). + */ + +#define VMSTOR_PROTOCOL_MAJOR(VERSION_) (((VERSION_) >> 8) & 0xff) +#define VMSTOR_PROTOCOL_MINOR(VERSION_) (((VERSION_) ) & 0xff) +#define VMSTOR_PROTOCOL_VERSION(MAJOR_, MINOR_) ((((MAJOR_) & 0xff) << 8) | \ + (((MINOR_) & 0xff) )) + +/* + * Invalid version. + */ +#define VMSTOR_INVALID_PROTOCOL_VERSION -1 + +/* + * Version history: + * V1 Beta 0.1 + * V1 RC < 2008/1/31 1.0 + * V1 RC > 2008/1/31 2.0 + */ + +#define VMSTOR_PROTOCOL_VERSION_CURRENT VMSTOR_PROTOCOL_VERSION(2, 0) + +/** + * Packet structure ops describing virtual storage requests. + */ +enum vstor_packet_ops { + VSTOR_OPERATION_COMPLETEIO = 1, + VSTOR_OPERATION_REMOVEDEVICE = 2, + VSTOR_OPERATION_EXECUTESRB = 3, + VSTOR_OPERATION_RESETLUN = 4, + VSTOR_OPERATION_RESETADAPTER = 5, + VSTOR_OPERATION_RESETBUS = 6, + VSTOR_OPERATION_BEGININITIALIZATION = 7, + VSTOR_OPERATION_ENDINITIALIZATION = 8, + VSTOR_OPERATION_QUERYPROTOCOLVERSION = 9, + VSTOR_OPERATION_QUERYPROPERTIES = 10, + VSTOR_OPERATION_MAXIMUM = 10 +}; + + +/* + * Platform neutral description of a scsi request - + * this remains the same across the write regardless of 32/64 bit + * note: it's patterned off the Windows DDK SCSI_PASS_THROUGH structure + */ + +#define CDB16GENERIC_LENGTH 0x10 +#define SENSE_BUFFER_SIZE 0x12 +#define MAX_DATA_BUFFER_LENGTH_WITH_PADDING 0x14 + +struct vmscsi_req { + uint16_t length; + uint8_t srb_status; + uint8_t scsi_status; + + /* HBA number, set to the order number detected by initiator. */ + uint8_t port; + /* SCSI bus number or bus_id, different from CAM's path_id. */ + uint8_t path_id; + + uint8_t target_id; + uint8_t lun; + + uint8_t cdb_len; + uint8_t sense_info_len; + uint8_t data_in; + uint8_t reserved; + + uint32_t transfer_len; + + union { + uint8_t cdb[CDB16GENERIC_LENGTH]; + + uint8_t sense_data[SENSE_BUFFER_SIZE]; + + uint8_t reserved_array[MAX_DATA_BUFFER_LENGTH_WITH_PADDING]; + }; + +} __packed; + +/** + * This structure is sent during the initialization phase to get the different + * properties of the channel. + */ + +struct vmstor_chan_props { + uint16_t proto_ver; + uint8_t path_id; + uint8_t target_id; + + /** + * Note: port number is only really known on the client side + */ + uint32_t port; + uint32_t flags; + uint32_t max_transfer_bytes; + + /** + * This id is unique for each channel and will correspond with + * vendor specific data in the inquiry_ata + */ + uint64_t unique_id; + +} __packed; + +/** + * This structure is sent during the storage protocol negotiations. + */ + +struct vmstor_proto_ver +{ + /** + * Major (MSW) and minor (LSW) version numbers. + */ + uint16_t major_minor; + + uint16_t revision; /* always zero */ +} __packed; + +/** + * Channel Property Flags + */ + +#define STORAGE_CHANNEL_REMOVABLE_FLAG 0x1 +#define STORAGE_CHANNEL_EMULATED_IDE_FLAG 0x2 + + +struct vstor_packet { + /** + * Requested operation type + */ + enum vstor_packet_ops operation; + + /* + * Flags - see below for values + */ + uint32_t flags; + + /** + * Status of the request returned from the server side. + */ + uint32_t status; + + union + { + /** + * Structure used to forward SCSI commands from the client to + * the server. + */ + struct vmscsi_req vm_srb; + + /** + * Structure used to query channel properties. + */ + struct vmstor_chan_props chan_props; + + /** + * Used during version negotiations. + */ + struct vmstor_proto_ver version; + }; + +} __packed; + + +/** + * SRB (SCSI Request Block) Status Codes + */ +#define SRB_STATUS_PENDING 0x00 +#define SRB_STATUS_SUCCESS 0x01 +#define SRB_STATUS_ABORTED 0x02 +#define SRB_STATUS_ABORT_FAILED 0x03 +#define SRB_STATUS_ERROR 0x04 +#define SRB_STATUS_BUSY 0x05 + +/** + * SRB Status Masks (can be combined with above status codes) + */ +#define SRB_STATUS_QUEUE_FROZEN 0x40 +#define SRB_STATUS_AUTOSENSE_VALID 0x80 + + +/** + * Packet flags + */ + +/** + * This flag indicates that the server should send back a completion for this + * packet. + */ +#define REQUEST_COMPLETION_FLAG 0x1 + +/** + * This is the set of flags that the vsc can set in any packets it sends + */ +#define VSC_LEGAL_FLAGS (REQUEST_COMPLETION_FLAG) + +#endif /* __HV_VSTORAGE_H__ */ diff --git a/sys/dev/hyperv/utilities/hv_kvp.h b/sys/dev/hyperv/utilities/hv_kvp.h new file mode 100644 index 00000000000..f7dccf77df2 --- /dev/null +++ b/sys/dev/hyperv/utilities/hv_kvp.h @@ -0,0 +1,285 @@ +/*- + * Copyright (c) 2009-2012 Microsoft Corp. + * Copyright (c) 2012 NetApp Inc. + * Copyright (c) 2012 Citrix Inc. + * 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 unmodified, 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#ifndef _KVP_H +#define _KVP_H + +/* + * An implementation of HyperV key value pair (KVP) functionality for FreeBSD + * + */ + +/* + * Maximum value size - used for both key names and value data, and includes + * any applicable NULL terminators. + * + * Note: This limit is somewhat arbitrary, but falls easily within what is + * supported for all native guests (back to Win 2000) and what is reasonable + * for the IC KVP exchange functionality. Note that Windows Me/98/95 are + * limited to 255 character key names. + * + * MSDN recommends not storing data values larger than 2048 bytes in the + * registry. + * + * Note: This value is used in defining the KVP exchange message - this value + * cannot be modified without affecting the message size and compatibility. + */ + +/* + * bytes, including any null terminators + */ +#define HV_KVP_EXCHANGE_MAX_VALUE_SIZE (2048) + + +/* + * Maximum key size - the registry limit for the length of an entry name + * is 256 characters, including the null terminator + */ + +#define HV_KVP_EXCHANGE_MAX_KEY_SIZE (512) + +/* + * In FreeBSD, we implement the KVP functionality in two components: + * 1) The kernel component which is packaged as part of the hv_utils driver + * is responsible for communicating with the host and responsible for + * implementing the host/guest protocol. 2) A user level daemon that is + * responsible for data gathering. + * + * Host/Guest Protocol: The host iterates over an index and expects the guest + * to assign a key name to the index and also return the value corresponding to + * the key. The host will have atmost one KVP transaction outstanding at any + * given point in time. The host side iteration stops when the guest returns + * an error. Microsoft has specified the following mapping of key names to + * host specified index: + * + * Index Key Name + * 0 FullyQualifiedDomainName + * 1 IntegrationServicesVersion + * 2 NetworkAddressIPv4 + * 3 NetworkAddressIPv6 + * 4 OSBuildNumber + * 5 OSName + * 6 OSMajorVersion + * 7 OSMinorVersion + * 8 OSVersion + * 9 ProcessorArchitecture + * + * The Windows host expects the Key Name and Key Value to be encoded in utf16. + * + * Guest Kernel/KVP Daemon Protocol: As noted earlier, we implement all of the + * data gathering functionality in a user mode daemon. The user level daemon + * is also responsible for binding the key name to the index as well. The + * kernel and user-level daemon communicate using a connector channel. + * + * The user mode component first registers with the + * the kernel component. Subsequently, the kernel component requests, data + * for the specified keys. In response to this message the user mode component + * fills in the value corresponding to the specified key. We overload the + * sequence field in the cn_msg header to define our KVP message types. + * + * + * The kernel component simply acts as a conduit for communication between the + * Windows host and the user-level daemon. The kernel component passes up the + * index received from the Host to the user-level daemon. If the index is + * valid (supported), the corresponding key as well as its + * value (both are strings) is returned. If the index is invalid + * (not supported), a NULL key string is returned. + */ + + +/* + * Registry value types. + */ + +#define HV_REG_SZ 1 +#define HV_REG_U32 4 +#define HV_REG_U64 8 + + +/* + * Daemon code not supporting IP injection (legacy daemon). + */ + +#define HV_KVP_OP_REGISTER 4 + +/* + * Daemon code supporting IP injection. + * The KVP opcode field is used to communicate the + * registration information; so define a namespace that + * will be distinct from the host defined KVP opcode. + */ + +#define KVP_OP_REGISTER1 100 + +enum hv_kvp_exchg_op { + HV_KVP_OP_GET = 0, + HV_KVP_OP_SET, + HV_KVP_OP_DELETE, + HV_KVP_OP_ENUMERATE, + HV_KVP_OP_GET_IP_INFO, + HV_KVP_OP_SET_IP_INFO, + HV_KVP_OP_COUNT /* Number of operations, must be last. */ +}; + +enum hv_kvp_exchg_pool { + HV_KVP_POOL_EXTERNAL = 0, + HV_KVP_POOL_GUEST, + HV_KVP_POOL_AUTO, + HV_KVP_POOL_AUTO_EXTERNAL, + HV_KVP_POOL_AUTO_INTERNAL, + HV_KVP_POOL_COUNT /* Number of pools, must be last. */ +}; + +/* + * Some Hyper-V status codes. + */ +#define HV_KVP_S_OK 0x00000000 +#define HV_KVP_E_FAIL 0x80004005 +#define HV_KVP_S_CONT 0x80070103 +#define HV_ERROR_NOT_SUPPORTED 0x80070032 +#define HV_ERROR_MACHINE_LOCKED 0x800704F7 +#define HV_ERROR_DEVICE_NOT_CONNECTED 0x8007048F +#define HV_INVALIDARG 0x80070057 +#define HV_KVP_GUID_NOTFOUND 0x80041002 + +#define ADDR_FAMILY_NONE 0x00 +#define ADDR_FAMILY_IPV4 0x01 +#define ADDR_FAMILY_IPV6 0x02 + +#define MAX_ADAPTER_ID_SIZE 128 +#define MAX_IP_ADDR_SIZE 1024 +#define MAX_GATEWAY_SIZE 512 + + +struct hv_kvp_ipaddr_value { + uint16_t adapter_id[MAX_ADAPTER_ID_SIZE]; + uint8_t addr_family; + uint8_t dhcp_enabled; + uint16_t ip_addr[MAX_IP_ADDR_SIZE]; + uint16_t sub_net[MAX_IP_ADDR_SIZE]; + uint16_t gate_way[MAX_GATEWAY_SIZE]; + uint16_t dns_addr[MAX_IP_ADDR_SIZE]; +} __attribute__((packed)); + + +struct hv_kvp_hdr { + uint8_t operation; + uint8_t pool; + uint16_t pad; +} __attribute__((packed)); + +struct hv_kvp_exchg_msg_value { + uint32_t value_type; + uint32_t key_size; + uint32_t value_size; + uint8_t key[HV_KVP_EXCHANGE_MAX_KEY_SIZE]; + union { + uint8_t value[HV_KVP_EXCHANGE_MAX_VALUE_SIZE]; + uint32_t value_u32; + uint64_t value_u64; + } msg_value; +} __attribute__((packed)); + +struct hv_kvp_msg_enumerate { + uint32_t index; + struct hv_kvp_exchg_msg_value data; +} __attribute__((packed)); + +struct hv_kvp_msg_get { + struct hv_kvp_exchg_msg_value data; +} __attribute__((packed)); + +struct hv_kvp_msg_set { + struct hv_kvp_exchg_msg_value data; +} __attribute__((packed)); + +struct hv_kvp_msg_delete { + uint32_t key_size; + uint8_t key[HV_KVP_EXCHANGE_MAX_KEY_SIZE]; +} __attribute__((packed)); + +struct hv_kvp_register { + uint8_t version[HV_KVP_EXCHANGE_MAX_KEY_SIZE]; +} __attribute__((packed)); + +struct hv_kvp_msg { + union { + struct hv_kvp_hdr kvp_hdr; + int error; + } hdr; + union { + struct hv_kvp_msg_get kvp_get; + struct hv_kvp_msg_set kvp_set; + struct hv_kvp_msg_delete kvp_delete; + struct hv_kvp_msg_enumerate kvp_enum_data; + struct hv_kvp_ipaddr_value kvp_ip_val; + struct hv_kvp_register kvp_register; + } body; +} __attribute__((packed)); + +struct hv_kvp_ip_msg { + uint8_t operation; + uint8_t pool; + struct hv_kvp_ipaddr_value kvp_ip_val; +} __attribute__((packed)); + +#define BSD_SOC_PATH "/etc/hyperv/socket" + +#define HV_SHUT_DOWN 0 +#define HV_TIME_SYNCH 1 +#define HV_HEART_BEAT 2 +#define HV_KVP 3 +#define HV_MAX_UTIL_SERVICES 4 + +#define HV_WLTIMEDELTA 116444736000000000L /* in 100ns unit */ +#define HV_ICTIMESYNCFLAG_PROBE 0 +#define HV_ICTIMESYNCFLAG_SYNC 1 +#define HV_ICTIMESYNCFLAG_SAMPLE 2 +#define HV_NANO_SEC_PER_SEC 1000000000 + +typedef struct hv_vmbus_service { + hv_guid guid; /* Hyper-V GUID */ + char* name; /* name of service */ + boolean_t enabled; /* service enabled */ + hv_work_queue* work_queue; /* background work queue */ + + // + // function to initialize service + // + int (*init)(struct hv_vmbus_service *); + + // + // function to process Hyper-V messages + // + void (*callback)(void *); +} hv_vmbus_service; + +extern uint8_t* receive_buffer[]; +extern hv_vmbus_service service_table[]; + +#endif /* _KVP_H */ diff --git a/sys/dev/hyperv/utilities/hv_util.c b/sys/dev/hyperv/utilities/hv_util.c new file mode 100644 index 00000000000..e86cbd78498 --- /dev/null +++ b/sys/dev/hyperv/utilities/hv_util.c @@ -0,0 +1,474 @@ +/*- + * Copyright (c) 2009-2012 Microsoft Corp. + * Copyright (c) 2012 NetApp Inc. + * Copyright (c) 2012 Citrix Inc. + * 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 unmodified, 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +/** + * A common driver for all hyper-V util services. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "hv_kvp.h" + +/* Time Sync data */ +typedef struct { + uint64_t data; +} time_sync_data; + +static void hv_shutdown_cb(void *context); +static void hv_heartbeat_cb(void *context); +static void hv_timesync_cb(void *context); + +static int hv_timesync_init(hv_vmbus_service *serv); + +/** + * Note: GUID codes below are predefined by the host hypervisor + * (Hyper-V and Azure)interface and required for correct operation. + */ +hv_vmbus_service service_table[] = { + /* Shutdown Service */ + { .guid.data = {0x31, 0x60, 0x0B, 0X0E, 0x13, 0x52, 0x34, 0x49, + 0x81, 0x8B, 0x38, 0XD9, 0x0C, 0xED, 0x39, 0xDB}, + .name = "Hyper-V Shutdown Service\n", + .enabled = TRUE, + .callback = hv_shutdown_cb, + }, + + /* Time Synch Service */ + { .guid.data = {0x30, 0xe6, 0x27, 0x95, 0xae, 0xd0, 0x7b, 0x49, + 0xad, 0xce, 0xe8, 0x0a, 0xb0, 0x17, 0x5c, 0xaf}, + .name = "Hyper-V Time Synch Service\n", + .enabled = TRUE, + .init = hv_timesync_init, + .callback = hv_timesync_cb, + }, + + /* Heartbeat Service */ + { .guid.data = {0x39, 0x4f, 0x16, 0x57, 0x15, 0x91, 0x78, 0x4e, + 0xab, 0x55, 0x38, 0x2f, 0x3b, 0xd5, 0x42, 0x2d}, + .name = "Hyper-V Heartbeat Service\n", + .enabled = TRUE, + .callback = hv_heartbeat_cb, + }, +}; + +/* + * Receive buffer pointers. There is one buffer per utility service. The + * buffer is allocated during attach(). + */ +uint8_t *receive_buffer[HV_MAX_UTIL_SERVICES]; + +struct hv_ictimesync_data { + uint64_t parenttime; + uint64_t childtime; + uint64_t roundtriptime; + uint8_t flags; +} __packed; + +static int +hv_timesync_init(hv_vmbus_service *serv) +{ + + serv->work_queue = hv_work_queue_create("Time Sync"); + if (serv->work_queue == NULL) + return (ENOMEM); + return (0); +} + +static void +hv_negotiate_version( + struct hv_vmbus_icmsg_hdr* icmsghdrp, + struct hv_vmbus_icmsg_negotiate* negop, + uint8_t* buf) +{ + icmsghdrp->icmsgsize = 0x10; + + negop = (struct hv_vmbus_icmsg_negotiate *)&buf[ + sizeof(struct hv_vmbus_pipe_hdr) + + sizeof(struct hv_vmbus_icmsg_hdr)]; + + if (negop->icframe_vercnt >= 2 && + negop->icversion_data[1].major == 3) { + negop->icversion_data[0].major = 3; + negop->icversion_data[0].minor = 0; + negop->icversion_data[1].major = 3; + negop->icversion_data[1].minor = 0; + } else { + negop->icversion_data[0].major = 1; + negop->icversion_data[0].minor = 0; + negop->icversion_data[1].major = 1; + negop->icversion_data[1].minor = 0; + } + + negop->icframe_vercnt = 1; + negop->icmsg_vercnt = 1; +} + + +/** + * Set host time based on time sync message from host + */ +static void +hv_set_host_time(void *context) +{ + time_sync_data *time_msg = context; + uint64_t hosttime = time_msg->data; + struct timespec guest_ts, host_ts; + uint64_t host_tns; + int64_t diff; + int error; + + host_tns = (hosttime - HV_WLTIMEDELTA) * 100; + host_ts.tv_sec = (time_t)(host_tns/HV_NANO_SEC_PER_SEC); + host_ts.tv_nsec = (long)(host_tns%HV_NANO_SEC_PER_SEC); + + nanotime(&guest_ts); + + diff = (int64_t)host_ts.tv_sec - (int64_t)guest_ts.tv_sec; + + /* + * If host differs by 5 seconds then make the guest catch up + */ + if (diff > 5 || diff < -5) { + error = kern_clock_settime(curthread, CLOCK_REALTIME, + &host_ts); + } + + /* + * Free the hosttime that was allocated in hv_adj_guesttime() + */ + free(time_msg, M_DEVBUF); +} + +/** + * @brief Synchronize time with host after reboot, restore, etc. + * + * ICTIMESYNCFLAG_SYNC flag bit indicates reboot, restore events of the VM. + * After reboot the flag ICTIMESYNCFLAG_SYNC is included in the first time + * message after the timesync channel is opened. Since the hv_utils module is + * loaded after hv_vmbus, the first message is usually missed. The other + * thing is, systime is automatically set to emulated hardware clock which may + * not be UTC time or in the same time zone. So, to override these effects, we + * use the first 50 time samples for initial system time setting. + */ +static inline +void hv_adj_guesttime(uint64_t hosttime, uint8_t flags) +{ + time_sync_data* time_msg; + + time_msg = malloc(sizeof(time_sync_data), M_DEVBUF, M_NOWAIT); + + if (time_msg == NULL) + return; + + time_msg->data = hosttime; + + if ((flags & HV_ICTIMESYNCFLAG_SYNC) != 0) { + hv_queue_work_item(service_table[HV_TIME_SYNCH].work_queue, + hv_set_host_time, time_msg); + } else if ((flags & HV_ICTIMESYNCFLAG_SAMPLE) != 0) { + hv_queue_work_item(service_table[HV_TIME_SYNCH].work_queue, + hv_set_host_time, time_msg); + } else { + free(time_msg, M_DEVBUF); + } +} + +/** + * Time Sync Channel message handler + */ +static void +hv_timesync_cb(void *context) +{ + hv_vmbus_channel* channel = context; + hv_vmbus_icmsg_hdr* icmsghdrp; + uint32_t recvlen; + uint64_t requestId; + int ret; + uint8_t* time_buf; + struct hv_ictimesync_data* timedatap; + + time_buf = receive_buffer[HV_TIME_SYNCH]; + + ret = hv_vmbus_channel_recv_packet(channel, time_buf, + PAGE_SIZE, &recvlen, &requestId); + + if ((ret == 0) && recvlen > 0) { + icmsghdrp = (struct hv_vmbus_icmsg_hdr *) &time_buf[ + sizeof(struct hv_vmbus_pipe_hdr)]; + + if (icmsghdrp->icmsgtype == HV_ICMSGTYPE_NEGOTIATE) { + hv_negotiate_version(icmsghdrp, NULL, time_buf); + } else { + timedatap = (struct hv_ictimesync_data *) &time_buf[ + sizeof(struct hv_vmbus_pipe_hdr) + + sizeof(struct hv_vmbus_icmsg_hdr)]; + hv_adj_guesttime(timedatap->parenttime, timedatap->flags); + } + + icmsghdrp->icflags = HV_ICMSGHDRFLAG_TRANSACTION + | HV_ICMSGHDRFLAG_RESPONSE; + + hv_vmbus_channel_send_packet(channel, time_buf, + recvlen, requestId, + HV_VMBUS_PACKET_TYPE_DATA_IN_BAND, 0); + } +} + +/** + * Shutdown + */ +static void +hv_shutdown_cb(void *context) +{ + uint8_t* buf; + hv_vmbus_channel* channel = context; + uint8_t execute_shutdown = 0; + hv_vmbus_icmsg_hdr* icmsghdrp; + uint32_t recv_len; + uint64_t request_id; + int ret; + hv_vmbus_shutdown_msg_data* shutdown_msg; + + buf = receive_buffer[HV_SHUT_DOWN]; + + ret = hv_vmbus_channel_recv_packet(channel, buf, PAGE_SIZE, + &recv_len, &request_id); + + if ((ret == 0) && recv_len > 0) { + + icmsghdrp = (struct hv_vmbus_icmsg_hdr *) + &buf[sizeof(struct hv_vmbus_pipe_hdr)]; + + if (icmsghdrp->icmsgtype == HV_ICMSGTYPE_NEGOTIATE) { + hv_negotiate_version(icmsghdrp, NULL, buf); + + } else { + shutdown_msg = + (struct hv_vmbus_shutdown_msg_data *) + &buf[sizeof(struct hv_vmbus_pipe_hdr) + + sizeof(struct hv_vmbus_icmsg_hdr)]; + + switch (shutdown_msg->flags) { + case 0: + case 1: + icmsghdrp->status = HV_S_OK; + execute_shutdown = 1; + if(bootverbose) + printf("Shutdown request received -" + " graceful shutdown initiated\n"); + break; + default: + icmsghdrp->status = HV_E_FAIL; + execute_shutdown = 0; + printf("Shutdown request received -" + " Invalid request\n"); + break; + } + } + + icmsghdrp->icflags = HV_ICMSGHDRFLAG_TRANSACTION | + HV_ICMSGHDRFLAG_RESPONSE; + + hv_vmbus_channel_send_packet(channel, buf, + recv_len, request_id, + HV_VMBUS_PACKET_TYPE_DATA_IN_BAND, 0); + } + + if (execute_shutdown) + shutdown_nice(RB_POWEROFF); +} + +/** + * Process heartbeat message + */ +static void +hv_heartbeat_cb(void *context) +{ + uint8_t* buf; + hv_vmbus_channel* channel = context; + uint32_t recvlen; + uint64_t requestid; + int ret; + + struct hv_vmbus_heartbeat_msg_data* heartbeat_msg; + struct hv_vmbus_icmsg_hdr* icmsghdrp; + + buf = receive_buffer[HV_HEART_BEAT]; + + ret = hv_vmbus_channel_recv_packet(channel, buf, PAGE_SIZE, &recvlen, + &requestid); + + if ((ret == 0) && recvlen > 0) { + + icmsghdrp = (struct hv_vmbus_icmsg_hdr *) + &buf[sizeof(struct hv_vmbus_pipe_hdr)]; + + if (icmsghdrp->icmsgtype == HV_ICMSGTYPE_NEGOTIATE) { + hv_negotiate_version(icmsghdrp, NULL, buf); + + } else { + heartbeat_msg = + (struct hv_vmbus_heartbeat_msg_data *) + &buf[sizeof(struct hv_vmbus_pipe_hdr) + + sizeof(struct hv_vmbus_icmsg_hdr)]; + + heartbeat_msg->seq_num += 1; + } + + icmsghdrp->icflags = HV_ICMSGHDRFLAG_TRANSACTION | + HV_ICMSGHDRFLAG_RESPONSE; + + hv_vmbus_channel_send_packet(channel, buf, recvlen, requestid, + HV_VMBUS_PACKET_TYPE_DATA_IN_BAND, 0); + } +} + + +static int +hv_util_probe(device_t dev) +{ + int i; + int rtn_value = ENXIO; + + for (i = 0; i < HV_MAX_UTIL_SERVICES; i++) { + const char *p = vmbus_get_type(dev); + if (service_table[i].enabled && !memcmp(p, &service_table[i].guid, sizeof(hv_guid))) { + device_set_softc(dev, (void *) (&service_table[i])); + rtn_value = 0; + } + } + + return rtn_value; +} + +static int +hv_util_attach(device_t dev) +{ + struct hv_device* hv_dev; + struct hv_vmbus_service* service; + int ret; + size_t receive_buffer_offset; + + hv_dev = vmbus_get_devctx(dev); + service = device_get_softc(dev); + receive_buffer_offset = service - &service_table[0]; + device_printf(dev, "Hyper-V Service attaching: %s\n", service->name); + receive_buffer[receive_buffer_offset] = + malloc(4 * PAGE_SIZE, M_DEVBUF, M_WAITOK | M_ZERO); + + if (service->init != NULL) { + ret = service->init(service); + if (ret) { + ret = ENODEV; + goto error0; + } + } + + ret = hv_vmbus_channel_open(hv_dev->channel, 4 * PAGE_SIZE, + 4 * PAGE_SIZE, NULL, 0, + service->callback, hv_dev->channel); + + if (ret) + goto error0; + + return (0); + + error0: + + free(receive_buffer[receive_buffer_offset], M_DEVBUF); + receive_buffer[receive_buffer_offset] = NULL; + + return (ret); +} + +static int +hv_util_detach(device_t dev) +{ + struct hv_device* hv_dev; + struct hv_vmbus_service* service; + size_t receive_buffer_offset; + + hv_dev = vmbus_get_devctx(dev); + + hv_vmbus_channel_close(hv_dev->channel); + service = device_get_softc(dev); + receive_buffer_offset = service - &service_table[0]; + + if (service->work_queue != NULL) + hv_work_queue_close(service->work_queue); + + free(receive_buffer[receive_buffer_offset], M_DEVBUF); + receive_buffer[receive_buffer_offset] = NULL; + + return (0); +} + +static void hv_util_init(void) +{ +} + +static int hv_util_modevent(module_t mod, int event, void *arg) +{ + switch (event) { + case MOD_LOAD: + break; + case MOD_UNLOAD: + break; + default: + break; + } + return (0); +} + +static device_method_t util_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, hv_util_probe), + DEVMETHOD(device_attach, hv_util_attach), + DEVMETHOD(device_detach, hv_util_detach), + DEVMETHOD(device_shutdown, bus_generic_shutdown), + { 0, 0 } } +; + +static driver_t util_driver = { "hyperv-utils", util_methods, 0 }; + +static devclass_t util_devclass; + +DRIVER_MODULE(hv_utils, vmbus, util_driver, util_devclass, hv_util_modevent, 0); +MODULE_VERSION(hv_utils, 1); +MODULE_DEPEND(hv_utils, vmbus, 1, 1, 1); + +SYSINIT(hv_util_initx, SI_SUB_KTHREAD_IDLE, SI_ORDER_MIDDLE + 1, + hv_util_init, NULL); diff --git a/sys/dev/hyperv/vmbus/hv_channel.c b/sys/dev/hyperv/vmbus/hv_channel.c new file mode 100644 index 00000000000..17dfd761123 --- /dev/null +++ b/sys/dev/hyperv/vmbus/hv_channel.c @@ -0,0 +1,842 @@ +/*- + * Copyright (c) 2009-2012 Microsoft Corp. + * Copyright (c) 2012 NetApp Inc. + * Copyright (c) 2012 Citrix Inc. + * 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 unmodified, 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "hv_vmbus_priv.h" + +static int vmbus_channel_create_gpadl_header( + /* must be phys and virt contiguous*/ + void* contig_buffer, + /* page-size multiple */ + uint32_t size, + hv_vmbus_channel_msg_info** msg_info, + uint32_t* message_count); + +static void vmbus_channel_set_event(hv_vmbus_channel* channel); + +/** + * @brief Trigger an event notification on the specified channel + */ +static void +vmbus_channel_set_event(hv_vmbus_channel *channel) +{ + hv_vmbus_monitor_page *monitor_page; + + if (channel->offer_msg.monitor_allocated) { + /* Each uint32_t represents 32 channels */ + synch_set_bit((channel->offer_msg.child_rel_id & 31), + ((uint32_t *)hv_vmbus_g_connection.send_interrupt_page + + ((channel->offer_msg.child_rel_id >> 5)))); + + monitor_page = (hv_vmbus_monitor_page *) + hv_vmbus_g_connection.monitor_pages; + + monitor_page++; /* Get the child to parent monitor page */ + + synch_set_bit(channel->monitor_bit, + (uint32_t *)&monitor_page-> + trigger_group[channel->monitor_group].pending); + } else { + hv_vmbus_set_event(channel->offer_msg.child_rel_id); + } + +} + +/** + * @brief Open the specified channel + */ +int +hv_vmbus_channel_open( + hv_vmbus_channel* new_channel, + uint32_t send_ring_buffer_size, + uint32_t recv_ring_buffer_size, + void* user_data, + uint32_t user_data_len, + hv_vmbus_pfn_channel_callback pfn_on_channel_callback, + void* context) +{ + + int ret = 0; + void *in, *out; + hv_vmbus_channel_open_channel* open_msg; + hv_vmbus_channel_msg_info* open_info; + + new_channel->on_channel_callback = pfn_on_channel_callback; + new_channel->channel_callback_context = context; + + /* Allocate the ring buffer */ + out = contigmalloc((send_ring_buffer_size + recv_ring_buffer_size), + M_DEVBUF, M_ZERO, 0UL, BUS_SPACE_MAXADDR, PAGE_SIZE, 0); + KASSERT(out != NULL, + ("Error VMBUS: contigmalloc failed to allocate Ring Buffer!")); + if (out == NULL) + return (ENOMEM); + + in = ((uint8_t *) out + send_ring_buffer_size); + + new_channel->ring_buffer_pages = out; + new_channel->ring_buffer_page_count = (send_ring_buffer_size + + recv_ring_buffer_size) >> PAGE_SHIFT; + + hv_vmbus_ring_buffer_init( + &new_channel->outbound, + out, + send_ring_buffer_size); + + hv_vmbus_ring_buffer_init( + &new_channel->inbound, + in, + recv_ring_buffer_size); + + /** + * Establish the gpadl for the ring buffer + */ + new_channel->ring_buffer_gpadl_handle = 0; + + ret = hv_vmbus_channel_establish_gpadl(new_channel, + new_channel->outbound.ring_buffer, + send_ring_buffer_size + recv_ring_buffer_size, + &new_channel->ring_buffer_gpadl_handle); + + /** + * Create and init the channel open message + */ + open_info = (hv_vmbus_channel_msg_info*) malloc( + sizeof(hv_vmbus_channel_msg_info) + + sizeof(hv_vmbus_channel_open_channel), + M_DEVBUF, + M_NOWAIT); + KASSERT(open_info != NULL, + ("Error VMBUS: malloc failed to allocate Open Channel message!")); + + if (open_info == NULL) + return (ENOMEM); + + sema_init(&open_info->wait_sema, 0, "Open Info Sema"); + + open_msg = (hv_vmbus_channel_open_channel*) open_info->msg; + open_msg->header.message_type = HV_CHANNEL_MESSAGE_OPEN_CHANNEL; + open_msg->open_id = new_channel->offer_msg.child_rel_id; + open_msg->child_rel_id = new_channel->offer_msg.child_rel_id; + open_msg->ring_buffer_gpadl_handle = + new_channel->ring_buffer_gpadl_handle; + open_msg->downstream_ring_buffer_page_offset = send_ring_buffer_size + >> PAGE_SHIFT; + open_msg->server_context_area_gpadl_handle = 0; + + if (user_data_len) + memcpy(open_msg->user_data, user_data, user_data_len); + + mtx_lock_spin(&hv_vmbus_g_connection.channel_msg_lock); + TAILQ_INSERT_TAIL( + &hv_vmbus_g_connection.channel_msg_anchor, + open_info, + msg_list_entry); + mtx_unlock_spin(&hv_vmbus_g_connection.channel_msg_lock); + + ret = hv_vmbus_post_message( + open_msg, sizeof(hv_vmbus_channel_open_channel)); + + if (ret != 0) + goto cleanup; + + ret = sema_timedwait(&open_info->wait_sema, 500); /* KYS 5 seconds */ + + if (ret) + goto cleanup; + + if (open_info->response.open_result.status == 0) { + if(bootverbose) + printf("VMBUS: channel <%p> open success.\n", new_channel); + } else { + if(bootverbose) + printf("Error VMBUS: channel <%p> open failed - %d!\n", + new_channel, open_info->response.open_result.status); + } + + cleanup: + mtx_lock_spin(&hv_vmbus_g_connection.channel_msg_lock); + TAILQ_REMOVE( + &hv_vmbus_g_connection.channel_msg_anchor, + open_info, + msg_list_entry); + mtx_unlock_spin(&hv_vmbus_g_connection.channel_msg_lock); + sema_destroy(&open_info->wait_sema); + free(open_info, M_DEVBUF); + + return (ret); +} + +/** + * @brief Create a gpadl for the specified buffer + */ +static int +vmbus_channel_create_gpadl_header( + void* contig_buffer, + uint32_t size, /* page-size multiple */ + hv_vmbus_channel_msg_info** msg_info, + uint32_t* message_count) +{ + int i; + int page_count; + unsigned long long pfn; + uint32_t msg_size; + hv_vmbus_channel_gpadl_header* gpa_header; + hv_vmbus_channel_gpadl_body* gpadl_body; + hv_vmbus_channel_msg_info* msg_header; + hv_vmbus_channel_msg_info* msg_body; + + int pfnSum, pfnCount, pfnLeft, pfnCurr, pfnSize; + + page_count = size >> PAGE_SHIFT; + pfn = hv_get_phys_addr(contig_buffer) >> PAGE_SHIFT; + + /*do we need a gpadl body msg */ + pfnSize = HV_MAX_SIZE_CHANNEL_MESSAGE + - sizeof(hv_vmbus_channel_gpadl_header) + - sizeof(hv_gpa_range); + pfnCount = pfnSize / sizeof(uint64_t); + + if (page_count > pfnCount) { /* if(we need a gpadl body) */ + /* fill in the header */ + msg_size = sizeof(hv_vmbus_channel_msg_info) + + sizeof(hv_vmbus_channel_gpadl_header) + + sizeof(hv_gpa_range) + + pfnCount * sizeof(uint64_t); + msg_header = malloc(msg_size, M_DEVBUF, M_NOWAIT | M_ZERO); + KASSERT( + msg_header != NULL, + ("Error VMBUS: malloc failed to allocate Gpadl Message!")); + if (msg_header == NULL) + return (ENOMEM); + + TAILQ_INIT(&msg_header->sub_msg_list_anchor); + msg_header->message_size = msg_size; + + gpa_header = (hv_vmbus_channel_gpadl_header*) msg_header->msg; + gpa_header->range_count = 1; + gpa_header->range_buf_len = sizeof(hv_gpa_range) + + page_count * sizeof(uint64_t); + gpa_header->range[0].byte_offset = 0; + gpa_header->range[0].byte_count = size; + for (i = 0; i < pfnCount; i++) { + gpa_header->range[0].pfn_array[i] = pfn + i; + } + *msg_info = msg_header; + *message_count = 1; + + pfnSum = pfnCount; + pfnLeft = page_count - pfnCount; + + /* + * figure out how many pfns we can fit + */ + pfnSize = HV_MAX_SIZE_CHANNEL_MESSAGE + - sizeof(hv_vmbus_channel_gpadl_body); + pfnCount = pfnSize / sizeof(uint64_t); + + /* + * fill in the body + */ + while (pfnLeft) { + if (pfnLeft > pfnCount) { + pfnCurr = pfnCount; + } else { + pfnCurr = pfnLeft; + } + + msg_size = sizeof(hv_vmbus_channel_msg_info) + + sizeof(hv_vmbus_channel_gpadl_body) + + pfnCurr * sizeof(uint64_t); + msg_body = malloc(msg_size, M_DEVBUF, M_NOWAIT | M_ZERO); + KASSERT( + msg_body != NULL, + ("Error VMBUS: malloc failed to allocate Gpadl msg_body!")); + if (msg_body == NULL) + return (ENOMEM); + + msg_body->message_size = msg_size; + (*message_count)++; + gpadl_body = + (hv_vmbus_channel_gpadl_body*) msg_body->msg; + /* + * gpadl_body->gpadl = kbuffer; + */ + for (i = 0; i < pfnCurr; i++) { + gpadl_body->pfn[i] = pfn + pfnSum + i; + } + + TAILQ_INSERT_TAIL( + &msg_header->sub_msg_list_anchor, + msg_body, + msg_list_entry); + pfnSum += pfnCurr; + pfnLeft -= pfnCurr; + } + } else { /* else everything fits in a header */ + + msg_size = sizeof(hv_vmbus_channel_msg_info) + + sizeof(hv_vmbus_channel_gpadl_header) + + sizeof(hv_gpa_range) + + page_count * sizeof(uint64_t); + msg_header = malloc(msg_size, M_DEVBUF, M_NOWAIT | M_ZERO); + KASSERT( + msg_header != NULL, + ("Error VMBUS: malloc failed to allocate Gpadl Message!")); + if (msg_header == NULL) + return (ENOMEM); + + msg_header->message_size = msg_size; + + gpa_header = (hv_vmbus_channel_gpadl_header*) msg_header->msg; + gpa_header->range_count = 1; + gpa_header->range_buf_len = sizeof(hv_gpa_range) + + page_count * sizeof(uint64_t); + gpa_header->range[0].byte_offset = 0; + gpa_header->range[0].byte_count = size; + for (i = 0; i < page_count; i++) { + gpa_header->range[0].pfn_array[i] = pfn + i; + } + + *msg_info = msg_header; + *message_count = 1; + } + + return (0); +} + +/** + * @brief Establish a GPADL for the specified buffer + */ +int +hv_vmbus_channel_establish_gpadl( + hv_vmbus_channel* channel, + void* contig_buffer, + uint32_t size, /* page-size multiple */ + uint32_t* gpadl_handle) + +{ + int ret = 0; + hv_vmbus_channel_gpadl_header* gpadl_msg; + hv_vmbus_channel_gpadl_body* gpadl_body; + hv_vmbus_channel_msg_info* msg_info; + hv_vmbus_channel_msg_info* sub_msg_info; + uint32_t msg_count; + hv_vmbus_channel_msg_info* curr; + uint32_t next_gpadl_handle; + + next_gpadl_handle = hv_vmbus_g_connection.next_gpadl_handle; + atomic_add_int((int*) &hv_vmbus_g_connection.next_gpadl_handle, 1); + + ret = vmbus_channel_create_gpadl_header( + contig_buffer, size, &msg_info, &msg_count); + + if(ret != 0) { /* if(allocation failed) return immediately */ + /* reverse atomic_add_int above */ + atomic_subtract_int((int*) + &hv_vmbus_g_connection.next_gpadl_handle, 1); + return ret; + } + + sema_init(&msg_info->wait_sema, 0, "Open Info Sema"); + gpadl_msg = (hv_vmbus_channel_gpadl_header*) msg_info->msg; + gpadl_msg->header.message_type = HV_CHANNEL_MESSAGEL_GPADL_HEADER; + gpadl_msg->child_rel_id = channel->offer_msg.child_rel_id; + gpadl_msg->gpadl = next_gpadl_handle; + + mtx_lock_spin(&hv_vmbus_g_connection.channel_msg_lock); + TAILQ_INSERT_TAIL( + &hv_vmbus_g_connection.channel_msg_anchor, + msg_info, + msg_list_entry); + + mtx_unlock_spin(&hv_vmbus_g_connection.channel_msg_lock); + + ret = hv_vmbus_post_message( + gpadl_msg, + msg_info->message_size - + (uint32_t) sizeof(hv_vmbus_channel_msg_info)); + + if (ret != 0) + goto cleanup; + + if (msg_count > 1) { + TAILQ_FOREACH(curr, + &msg_info->sub_msg_list_anchor, msg_list_entry) { + sub_msg_info = curr; + gpadl_body = + (hv_vmbus_channel_gpadl_body*) sub_msg_info->msg; + + gpadl_body->header.message_type = + HV_CHANNEL_MESSAGE_GPADL_BODY; + gpadl_body->gpadl = next_gpadl_handle; + + ret = hv_vmbus_post_message( + gpadl_body, + sub_msg_info->message_size + - (uint32_t) sizeof(hv_vmbus_channel_msg_info)); + /* if (the post message failed) give up and clean up */ + if(ret != 0) + goto cleanup; + } + } + + ret = sema_timedwait(&msg_info->wait_sema, 500); /* KYS 5 seconds*/ + if (ret != 0) + goto cleanup; + + *gpadl_handle = gpadl_msg->gpadl; + +cleanup: + + mtx_lock_spin(&hv_vmbus_g_connection.channel_msg_lock); + TAILQ_REMOVE(&hv_vmbus_g_connection.channel_msg_anchor, + msg_info, msg_list_entry); + mtx_unlock_spin(&hv_vmbus_g_connection.channel_msg_lock); + + sema_destroy(&msg_info->wait_sema); + free(msg_info, M_DEVBUF); + + return (ret); +} + +/** + * @brief Teardown the specified GPADL handle + */ +int +hv_vmbus_channel_teardown_gpdal( + hv_vmbus_channel* channel, + uint32_t gpadl_handle) +{ + int ret = 0; + hv_vmbus_channel_gpadl_teardown* msg; + hv_vmbus_channel_msg_info* info; + + info = (hv_vmbus_channel_msg_info *) + malloc( sizeof(hv_vmbus_channel_msg_info) + + sizeof(hv_vmbus_channel_gpadl_teardown), + M_DEVBUF, M_NOWAIT); + KASSERT(info != NULL, + ("Error VMBUS: malloc failed to allocate Gpadl Teardown Msg!")); + if (info == NULL) { + ret = ENOMEM; + goto cleanup; + } + + sema_init(&info->wait_sema, 0, "Open Info Sema"); + + msg = (hv_vmbus_channel_gpadl_teardown*) info->msg; + + msg->header.message_type = HV_CHANNEL_MESSAGE_GPADL_TEARDOWN; + msg->child_rel_id = channel->offer_msg.child_rel_id; + msg->gpadl = gpadl_handle; + + mtx_lock_spin(&hv_vmbus_g_connection.channel_msg_lock); + TAILQ_INSERT_TAIL(&hv_vmbus_g_connection.channel_msg_anchor, + info, msg_list_entry); + mtx_unlock_spin(&hv_vmbus_g_connection.channel_msg_lock); + + ret = hv_vmbus_post_message(msg, + sizeof(hv_vmbus_channel_gpadl_teardown)); + if (ret != 0) + goto cleanup; + + ret = sema_timedwait(&info->wait_sema, 500); /* KYS 5 seconds */ + +cleanup: + /* + * Received a torndown response + */ + mtx_lock_spin(&hv_vmbus_g_connection.channel_msg_lock); + TAILQ_REMOVE(&hv_vmbus_g_connection.channel_msg_anchor, + info, msg_list_entry); + mtx_unlock_spin(&hv_vmbus_g_connection.channel_msg_lock); + sema_destroy(&info->wait_sema); + free(info, M_DEVBUF); + + return (ret); +} + +/** + * @brief Close the specified channel + */ +void +hv_vmbus_channel_close(hv_vmbus_channel *channel) +{ + int ret = 0; + hv_vmbus_channel_close_channel* msg; + hv_vmbus_channel_msg_info* info; + + mtx_lock(&channel->inbound_lock); + channel->on_channel_callback = NULL; + mtx_unlock(&channel->inbound_lock); + + /** + * Send a closing message + */ + info = (hv_vmbus_channel_msg_info *) + malloc( sizeof(hv_vmbus_channel_msg_info) + + sizeof(hv_vmbus_channel_close_channel), + M_DEVBUF, M_NOWAIT); + KASSERT(info != NULL, ("VMBUS: malloc failed hv_vmbus_channel_close!")); + if(info == NULL) + return; + + msg = (hv_vmbus_channel_close_channel*) info->msg; + msg->header.message_type = HV_CHANNEL_MESSAGE_CLOSE_CHANNEL; + msg->child_rel_id = channel->offer_msg.child_rel_id; + + ret = hv_vmbus_post_message( + msg, sizeof(hv_vmbus_channel_close_channel)); + + /* Tear down the gpadl for the channel's ring buffer */ + if (channel->ring_buffer_gpadl_handle) { + hv_vmbus_channel_teardown_gpdal(channel, + channel->ring_buffer_gpadl_handle); + } + + /* TODO: Send a msg to release the childRelId */ + + /* cleanup the ring buffers for this channel */ + hv_ring_buffer_cleanup(&channel->outbound); + hv_ring_buffer_cleanup(&channel->inbound); + + contigfree( + channel->ring_buffer_pages, + channel->ring_buffer_page_count, + M_DEVBUF); + + free(info, M_DEVBUF); + + /* + * If we are closing the channel during an error path in + * opening the channel, don't free the channel + * since the caller will free the channel + */ + if (channel->state == HV_CHANNEL_OPEN_STATE) { + mtx_lock_spin(&hv_vmbus_g_connection.channel_lock); + TAILQ_REMOVE( + &hv_vmbus_g_connection.channel_anchor, + channel, + list_entry); + mtx_unlock_spin(&hv_vmbus_g_connection.channel_lock); + + hv_vmbus_free_vmbus_channel(channel); + } + +} + +/** + * @brief Send the specified buffer on the given channel + */ +int +hv_vmbus_channel_send_packet( + hv_vmbus_channel* channel, + void* buffer, + uint32_t buffer_len, + uint64_t request_id, + hv_vmbus_packet_type type, + uint32_t flags) +{ + int ret = 0; + hv_vm_packet_descriptor desc; + uint32_t packet_len; + uint64_t aligned_data; + uint32_t packet_len_aligned; + hv_vmbus_sg_buffer_list buffer_list[3]; + + packet_len = sizeof(hv_vm_packet_descriptor) + buffer_len; + packet_len_aligned = HV_ALIGN_UP(packet_len, sizeof(uint64_t)); + aligned_data = 0; + + /* Setup the descriptor */ + desc.type = type; /* HV_VMBUS_PACKET_TYPE_DATA_IN_BAND; */ + desc.flags = flags; /* HV_VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED */ + /* in 8-bytes granularity */ + desc.data_offset8 = sizeof(hv_vm_packet_descriptor) >> 3; + desc.length8 = (uint16_t) (packet_len_aligned >> 3); + desc.transaction_id = request_id; + + buffer_list[0].data = &desc; + buffer_list[0].length = sizeof(hv_vm_packet_descriptor); + + buffer_list[1].data = buffer; + buffer_list[1].length = buffer_len; + + buffer_list[2].data = &aligned_data; + buffer_list[2].length = packet_len_aligned - packet_len; + + ret = hv_ring_buffer_write(&channel->outbound, buffer_list, 3); + + /* TODO: We should determine if this is optional */ + if (ret == 0 + && !hv_vmbus_get_ring_buffer_interrupt_mask( + &channel->outbound)) { + vmbus_channel_set_event(channel); + } + + return (ret); +} + +/** + * @brief Send a range of single-page buffer packets using + * a GPADL Direct packet type + */ +int +hv_vmbus_channel_send_packet_pagebuffer( + hv_vmbus_channel* channel, + hv_vmbus_page_buffer page_buffers[], + uint32_t page_count, + void* buffer, + uint32_t buffer_len, + uint64_t request_id) +{ + + int ret = 0; + int i = 0; + uint32_t packet_len; + uint32_t packetLen_aligned; + hv_vmbus_sg_buffer_list buffer_list[3]; + hv_vmbus_channel_packet_page_buffer desc; + uint32_t descSize; + uint64_t alignedData = 0; + + if (page_count > HV_MAX_PAGE_BUFFER_COUNT) + return (EINVAL); + + /* + * Adjust the size down since hv_vmbus_channel_packet_page_buffer + * is the largest size we support + */ + descSize = sizeof(hv_vmbus_channel_packet_page_buffer) - + ((HV_MAX_PAGE_BUFFER_COUNT - page_count) * + sizeof(hv_vmbus_page_buffer)); + packet_len = descSize + buffer_len; + packetLen_aligned = HV_ALIGN_UP(packet_len, sizeof(uint64_t)); + + /* Setup the descriptor */ + desc.type = HV_VMBUS_PACKET_TYPE_DATA_USING_GPA_DIRECT; + desc.flags = HV_VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED; + desc.data_offset8 = descSize >> 3; /* in 8-bytes granularity */ + desc.length8 = (uint16_t) (packetLen_aligned >> 3); + desc.transaction_id = request_id; + desc.range_count = page_count; + + for (i = 0; i < page_count; i++) { + desc.range[i].length = page_buffers[i].length; + desc.range[i].offset = page_buffers[i].offset; + desc.range[i].pfn = page_buffers[i].pfn; + } + + buffer_list[0].data = &desc; + buffer_list[0].length = descSize; + + buffer_list[1].data = buffer; + buffer_list[1].length = buffer_len; + + buffer_list[2].data = &alignedData; + buffer_list[2].length = packetLen_aligned - packet_len; + + ret = hv_ring_buffer_write(&channel->outbound, buffer_list, 3); + + /* TODO: We should determine if this is optional */ + if (ret == 0 && + !hv_vmbus_get_ring_buffer_interrupt_mask(&channel->outbound)) { + vmbus_channel_set_event(channel); + } + + return (ret); +} + +/** + * @brief Send a multi-page buffer packet using a GPADL Direct packet type + */ +int +hv_vmbus_channel_send_packet_multipagebuffer( + hv_vmbus_channel* channel, + hv_vmbus_multipage_buffer* multi_page_buffer, + void* buffer, + uint32_t buffer_len, + uint64_t request_id) +{ + + int ret = 0; + uint32_t desc_size; + uint32_t packet_len; + uint32_t packet_len_aligned; + uint32_t pfn_count; + uint64_t aligned_data = 0; + hv_vmbus_sg_buffer_list buffer_list[3]; + hv_vmbus_channel_packet_multipage_buffer desc; + + pfn_count = + HV_NUM_PAGES_SPANNED( + multi_page_buffer->offset, + multi_page_buffer->length); + + if ((pfn_count == 0) || (pfn_count > HV_MAX_MULTIPAGE_BUFFER_COUNT)) + return (EINVAL); + /* + * Adjust the size down since hv_vmbus_channel_packet_multipage_buffer + * is the largest size we support + */ + desc_size = + sizeof(hv_vmbus_channel_packet_multipage_buffer) - + ((HV_MAX_MULTIPAGE_BUFFER_COUNT - pfn_count) * + sizeof(uint64_t)); + packet_len = desc_size + buffer_len; + packet_len_aligned = HV_ALIGN_UP(packet_len, sizeof(uint64_t)); + + /* + * Setup the descriptor + */ + desc.type = HV_VMBUS_PACKET_TYPE_DATA_USING_GPA_DIRECT; + desc.flags = HV_VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED; + desc.data_offset8 = desc_size >> 3; /* in 8-bytes granularity */ + desc.length8 = (uint16_t) (packet_len_aligned >> 3); + desc.transaction_id = request_id; + desc.range_count = 1; + + desc.range.length = multi_page_buffer->length; + desc.range.offset = multi_page_buffer->offset; + + memcpy(desc.range.pfn_array, multi_page_buffer->pfn_array, + pfn_count * sizeof(uint64_t)); + + buffer_list[0].data = &desc; + buffer_list[0].length = desc_size; + + buffer_list[1].data = buffer; + buffer_list[1].length = buffer_len; + + buffer_list[2].data = &aligned_data; + buffer_list[2].length = packet_len_aligned - packet_len; + + ret = hv_ring_buffer_write(&channel->outbound, buffer_list, 3); + + /* TODO: We should determine if this is optional */ + if (ret == 0 && + !hv_vmbus_get_ring_buffer_interrupt_mask(&channel->outbound)) { + vmbus_channel_set_event(channel); + } + + return (ret); +} + +/** + * @brief Retrieve the user packet on the specified channel + */ +int +hv_vmbus_channel_recv_packet( + hv_vmbus_channel* channel, + void* Buffer, + uint32_t buffer_len, + uint32_t* buffer_actual_len, + uint64_t* request_id) +{ + int ret; + uint32_t user_len; + uint32_t packet_len; + hv_vm_packet_descriptor desc; + + *buffer_actual_len = 0; + *request_id = 0; + + ret = hv_ring_buffer_peek(&channel->inbound, &desc, + sizeof(hv_vm_packet_descriptor)); + if (ret != 0) + return (0); + + packet_len = desc.length8 << 3; + user_len = packet_len - (desc.data_offset8 << 3); + + *buffer_actual_len = user_len; + + if (user_len > buffer_len) + return (EINVAL); + + *request_id = desc.transaction_id; + + /* Copy over the packet to the user buffer */ + ret = hv_ring_buffer_read(&channel->inbound, Buffer, user_len, + (desc.data_offset8 << 3)); + + return (0); +} + +/** + * @brief Retrieve the raw packet on the specified channel + */ +int +hv_vmbus_channel_recv_packet_raw( + hv_vmbus_channel* channel, + void* buffer, + uint32_t buffer_len, + uint32_t* buffer_actual_len, + uint64_t* request_id) +{ + int ret; + uint32_t packetLen; + uint32_t userLen; + hv_vm_packet_descriptor desc; + + *buffer_actual_len = 0; + *request_id = 0; + + ret = hv_ring_buffer_peek( + &channel->inbound, &desc, + sizeof(hv_vm_packet_descriptor)); + + if (ret != 0) + return (0); + + packetLen = desc.length8 << 3; + userLen = packetLen - (desc.data_offset8 << 3); + + *buffer_actual_len = packetLen; + + if (packetLen > buffer_len) + return (ENOBUFS); + + *request_id = desc.transaction_id; + + /* Copy over the entire packet to the user buffer */ + ret = hv_ring_buffer_read(&channel->inbound, buffer, packetLen, 0); + + return (0); +} diff --git a/sys/dev/hyperv/vmbus/hv_channel_mgmt.c b/sys/dev/hyperv/vmbus/hv_channel_mgmt.c new file mode 100644 index 00000000000..011e305709e --- /dev/null +++ b/sys/dev/hyperv/vmbus/hv_channel_mgmt.c @@ -0,0 +1,680 @@ +/*- + * Copyright (c) 2009-2012 Microsoft Corp. + * Copyright (c) 2012 NetApp Inc. + * Copyright (c) 2012 Citrix Inc. + * 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 unmodified, 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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 +#include + +#include "hv_vmbus_priv.h" + +typedef void (*hv_pfn_channel_msg_handler)(hv_vmbus_channel_msg_header* msg); + +typedef struct hv_vmbus_channel_msg_table_entry { + hv_vmbus_channel_msg_type messageType; + hv_pfn_channel_msg_handler messageHandler; +} hv_vmbus_channel_msg_table_entry; + +/* + * Internal functions + */ + +static void vmbus_channel_on_offer(hv_vmbus_channel_msg_header* hdr); +static void vmbus_channel_on_open_result(hv_vmbus_channel_msg_header* hdr); +static void vmbus_channel_on_offer_rescind(hv_vmbus_channel_msg_header* hdr); +static void vmbus_channel_on_gpadl_created(hv_vmbus_channel_msg_header* hdr); +static void vmbus_channel_on_gpadl_torndown(hv_vmbus_channel_msg_header* hdr); +static void vmbus_channel_on_offers_delivered(hv_vmbus_channel_msg_header* hdr); +static void vmbus_channel_on_version_response(hv_vmbus_channel_msg_header* hdr); +static void vmbus_channel_process_offer(void *context); + +/** + * Channel message dispatch table + */ +hv_vmbus_channel_msg_table_entry + g_channel_message_table[HV_CHANNEL_MESSAGE_COUNT] = { + { HV_CHANNEL_MESSAGE_INVALID, NULL }, + { HV_CHANNEL_MESSAGE_OFFER_CHANNEL, vmbus_channel_on_offer }, + { HV_CHANNEL_MESSAGE_RESCIND_CHANNEL_OFFER, + vmbus_channel_on_offer_rescind }, + { HV_CHANNEL_MESSAGE_REQUEST_OFFERS, NULL }, + { HV_CHANNEL_MESSAGE_ALL_OFFERS_DELIVERED, + vmbus_channel_on_offers_delivered }, + { HV_CHANNEL_MESSAGE_OPEN_CHANNEL, NULL }, + { HV_CHANNEL_MESSAGE_OPEN_CHANNEL_RESULT, + vmbus_channel_on_open_result }, + { HV_CHANNEL_MESSAGE_CLOSE_CHANNEL, NULL }, + { HV_CHANNEL_MESSAGEL_GPADL_HEADER, NULL }, + { HV_CHANNEL_MESSAGE_GPADL_BODY, NULL }, + { HV_CHANNEL_MESSAGE_GPADL_CREATED, + vmbus_channel_on_gpadl_created }, + { HV_CHANNEL_MESSAGE_GPADL_TEARDOWN, NULL }, + { HV_CHANNEL_MESSAGE_GPADL_TORNDOWN, + vmbus_channel_on_gpadl_torndown }, + { HV_CHANNEL_MESSAGE_REL_ID_RELEASED, NULL }, + { HV_CHANNEL_MESSAGE_INITIATED_CONTACT, NULL }, + { HV_CHANNEL_MESSAGE_VERSION_RESPONSE, + vmbus_channel_on_version_response }, + { HV_CHANNEL_MESSAGE_UNLOAD, NULL } +}; + + +/** + * Implementation of the work abstraction. + */ +static void +work_item_callback(void *work, int pending) +{ + struct hv_work_item *w = (struct hv_work_item *)work; + + /* + * Serialize work execution. + */ + if (w->wq->work_sema != NULL) { + sema_wait(w->wq->work_sema); + } + + w->callback(w->context); + + if (w->wq->work_sema != NULL) { + sema_post(w->wq->work_sema); + } + + free(w, M_DEVBUF); +} + +struct hv_work_queue* +hv_work_queue_create(char* name) +{ + static unsigned int qid = 0; + char qname[64]; + int pri; + struct hv_work_queue* wq; + + wq = malloc(sizeof(struct hv_work_queue), M_DEVBUF, M_NOWAIT | M_ZERO); + KASSERT(wq != NULL, ("Error VMBUS: Failed to allocate work_queue\n")); + if (wq == NULL) + return (NULL); + + /* + * We use work abstraction to handle messages + * coming from the host and these are typically offers. + * Some FreeBsd drivers appear to have a concurrency issue + * where probe/attach needs to be serialized. We ensure that + * by having only one thread process work elements in a + * specific queue by serializing work execution. + * + */ + if (strcmp(name, "vmbusQ") == 0) { + pri = PI_DISK; + } else { /* control */ + pri = PI_NET; + /* + * Initialize semaphore for this queue by pointing + * to the globale semaphore used for synchronizing all + * control messages. + */ + wq->work_sema = &hv_vmbus_g_connection.control_sema; + } + + sprintf(qname, "hv_%s_%u", name, qid); + + /* + * Fixme: FreeBSD 8.2 has a different prototype for + * taskqueue_create(), and for certain other taskqueue functions. + * We need to research the implications of these changes. + * Fixme: Not sure when the changes were introduced. + */ + wq->queue = taskqueue_create(qname, M_NOWAIT, taskqueue_thread_enqueue, + &wq->queue + #if __FreeBSD_version < 800000 + , &wq->proc + #endif + ); + + if (wq->queue == NULL) { + free(wq, M_DEVBUF); + return (NULL); + } + + if (taskqueue_start_threads(&wq->queue, 1, pri, "%s taskq", qname)) { + taskqueue_free(wq->queue); + free(wq, M_DEVBUF); + return (NULL); + } + + qid++; + + return (wq); +} + +void +hv_work_queue_close(struct hv_work_queue *wq) +{ + /* + * KYS: Need to drain the taskqueue + * before we close the hv_work_queue. + */ + /*KYS: taskqueue_drain(wq->tq, ); */ + taskqueue_free(wq->queue); + free(wq, M_DEVBUF); +} + +/** + * @brief Create work item + */ +int +hv_queue_work_item( + struct hv_work_queue *wq, + void (*callback)(void *), void *context) +{ + struct hv_work_item *w = malloc(sizeof(struct hv_work_item), + M_DEVBUF, M_NOWAIT | M_ZERO); + KASSERT(w != NULL, ("Error VMBUS: Failed to allocate WorkItem\n")); + if (w == NULL) + return (ENOMEM); + + w->callback = callback; + w->context = context; + w->wq = wq; + + TASK_INIT(&w->work, 0, work_item_callback, w); + + return (taskqueue_enqueue(wq->queue, &w->work)); +} + +/** + * @brief Rescind the offer by initiating a device removal + */ +static void +vmbus_channel_process_rescind_offer(void *context) +{ + hv_vmbus_channel* channel = (hv_vmbus_channel*) context; + hv_vmbus_child_device_unregister(channel->device); +} + +/** + * @brief Allocate and initialize a vmbus channel object + */ +hv_vmbus_channel* +hv_vmbus_allocate_channel(void) +{ + hv_vmbus_channel* channel; + + channel = (hv_vmbus_channel*) malloc( + sizeof(hv_vmbus_channel), + M_DEVBUF, + M_NOWAIT | M_ZERO); + KASSERT(channel != NULL, ("Error VMBUS: Failed to allocate channel!")); + if (channel == NULL) + return (NULL); + + mtx_init(&channel->inbound_lock, "channel inbound", NULL, MTX_DEF); + + channel->control_work_queue = hv_work_queue_create("control"); + + if (channel->control_work_queue == NULL) { + mtx_destroy(&channel->inbound_lock); + free(channel, M_DEVBUF); + return (NULL); + } + + return (channel); +} + +/** + * @brief Release the vmbus channel object itself + */ +static inline void +ReleaseVmbusChannel(void *context) +{ + hv_vmbus_channel* channel = (hv_vmbus_channel*) context; + hv_work_queue_close(channel->control_work_queue); + free(channel, M_DEVBUF); +} + +/** + * @brief Release the resources used by the vmbus channel object + */ +void +hv_vmbus_free_vmbus_channel(hv_vmbus_channel* channel) +{ + mtx_destroy(&channel->inbound_lock); + /* + * We have to release the channel's workqueue/thread in + * the vmbus's workqueue/thread context + * ie we can't destroy ourselves + */ + hv_queue_work_item(hv_vmbus_g_connection.work_queue, + ReleaseVmbusChannel, (void *) channel); +} + +/** + * @brief Process the offer by creating a channel/device + * associated with this offer + */ +static void +vmbus_channel_process_offer(void *context) +{ + int ret; + hv_vmbus_channel* new_channel; + boolean_t f_new; + hv_vmbus_channel* channel; + + new_channel = (hv_vmbus_channel*) context; + f_new = TRUE; + channel = NULL; + + /* + * Make sure this is a new offer + */ + mtx_lock_spin(&hv_vmbus_g_connection.channel_lock); + + TAILQ_FOREACH(channel, &hv_vmbus_g_connection.channel_anchor, + list_entry) + { + if (!memcmp( + &channel->offer_msg.offer.interface_type, + &new_channel->offer_msg.offer.interface_type, + sizeof(hv_guid)) + && !memcmp( + &channel->offer_msg.offer.interface_instance, + &new_channel->offer_msg.offer.interface_instance, + sizeof(hv_guid))) { + f_new = FALSE; + break; + } + } + + if (f_new) { + /* Insert at tail */ + TAILQ_INSERT_TAIL( + &hv_vmbus_g_connection.channel_anchor, + new_channel, + list_entry); + } + mtx_unlock_spin(&hv_vmbus_g_connection.channel_lock); + + if (!f_new) { + hv_vmbus_free_vmbus_channel(new_channel); + return; + } + + /* + * Start the process of binding this offer to the driver + * (We need to set the device field before calling + * hv_vmbus_child_device_add()) + */ + new_channel->device = hv_vmbus_child_device_create( + new_channel->offer_msg.offer.interface_type, + new_channel->offer_msg.offer.interface_instance, new_channel); + + /* + * TODO - the HV_CHANNEL_OPEN_STATE flag should not be set below + * but in the "open" channel request. The ret != 0 logic below + * doesn't take into account that a channel + * may have been opened successfully + */ + + /* + * Add the new device to the bus. This will kick off device-driver + * binding which eventually invokes the device driver's AddDevice() + * method. + */ + ret = hv_vmbus_child_device_register(new_channel->device); + if (ret != 0) { + mtx_lock_spin(&hv_vmbus_g_connection.channel_lock); + TAILQ_REMOVE( + &hv_vmbus_g_connection.channel_anchor, + new_channel, + list_entry); + mtx_unlock_spin(&hv_vmbus_g_connection.channel_lock); + hv_vmbus_free_vmbus_channel(new_channel); + } else { + /* + * This state is used to indicate a successful open + * so that when we do close the channel normally, + * we can clean up properly + */ + new_channel->state = HV_CHANNEL_OPEN_STATE; + + } +} + +/** + * @brief Handler for channel offers from Hyper-V/Azure + * + * Handler for channel offers from vmbus in parent partition. We ignore + * all offers except network and storage offers. For each network and storage + * offers, we create a channel object and queue a work item to the channel + * object to process the offer synchronously + */ +static void +vmbus_channel_on_offer(hv_vmbus_channel_msg_header* hdr) +{ + hv_vmbus_channel_offer_channel* offer; + hv_vmbus_channel* new_channel; + + offer = (hv_vmbus_channel_offer_channel*) hdr; + + hv_guid *guidType; + hv_guid *guidInstance; + + guidType = &offer->offer.interface_type; + guidInstance = &offer->offer.interface_instance; + + /* Allocate the channel object and save this offer */ + new_channel = hv_vmbus_allocate_channel(); + if (new_channel == NULL) + return; + + memcpy(&new_channel->offer_msg, offer, + sizeof(hv_vmbus_channel_offer_channel)); + new_channel->monitor_group = (uint8_t) offer->monitor_id / 32; + new_channel->monitor_bit = (uint8_t) offer->monitor_id % 32; + + /* TODO: Make sure the offer comes from our parent partition */ + hv_queue_work_item( + new_channel->control_work_queue, + vmbus_channel_process_offer, + new_channel); +} + +/** + * @brief Rescind offer handler. + * + * We queue a work item to process this offer + * synchronously + */ +static void +vmbus_channel_on_offer_rescind(hv_vmbus_channel_msg_header* hdr) +{ + hv_vmbus_channel_rescind_offer* rescind; + hv_vmbus_channel* channel; + + rescind = (hv_vmbus_channel_rescind_offer*) hdr; + + channel = hv_vmbus_get_channel_from_rel_id(rescind->child_rel_id); + if (channel == NULL) + return; + + hv_queue_work_item(channel->control_work_queue, + vmbus_channel_process_rescind_offer, channel); +} + +/** + * + * @brief Invoked when all offers have been delivered. + */ +static void +vmbus_channel_on_offers_delivered(hv_vmbus_channel_msg_header* hdr) +{ +} + +/** + * @brief Open result handler. + * + * This is invoked when we received a response + * to our channel open request. Find the matching request, copy the + * response and signal the requesting thread. + */ +static void +vmbus_channel_on_open_result(hv_vmbus_channel_msg_header* hdr) +{ + hv_vmbus_channel_open_result* result; + hv_vmbus_channel_msg_info* msg_info; + hv_vmbus_channel_msg_header* requestHeader; + hv_vmbus_channel_open_channel* openMsg; + + result = (hv_vmbus_channel_open_result*) hdr; + + /* + * Find the open msg, copy the result and signal/unblock the wait event + */ + mtx_lock_spin(&hv_vmbus_g_connection.channel_msg_lock); + + TAILQ_FOREACH(msg_info, &hv_vmbus_g_connection.channel_msg_anchor, + msg_list_entry) { + requestHeader = (hv_vmbus_channel_msg_header*) msg_info->msg; + + if (requestHeader->message_type == + HV_CHANNEL_MESSAGE_OPEN_CHANNEL) { + openMsg = (hv_vmbus_channel_open_channel*) msg_info->msg; + if (openMsg->child_rel_id == result->child_rel_id + && openMsg->open_id == result->open_id) { + memcpy(&msg_info->response.open_result, result, + sizeof(hv_vmbus_channel_open_result)); + sema_post(&msg_info->wait_sema); + break; + } + } + } + mtx_unlock_spin(&hv_vmbus_g_connection.channel_msg_lock); + +} + +/** + * @brief GPADL created handler. + * + * This is invoked when we received a response + * to our gpadl create request. Find the matching request, copy the + * response and signal the requesting thread. + */ +static void +vmbus_channel_on_gpadl_created(hv_vmbus_channel_msg_header* hdr) +{ + hv_vmbus_channel_gpadl_created* gpadl_created; + hv_vmbus_channel_msg_info* msg_info; + hv_vmbus_channel_msg_header* request_header; + hv_vmbus_channel_gpadl_header* gpadl_header; + + gpadl_created = (hv_vmbus_channel_gpadl_created*) hdr; + + /* Find the establish msg, copy the result and signal/unblock + * the wait event + */ + mtx_lock_spin(&hv_vmbus_g_connection.channel_msg_lock); + TAILQ_FOREACH(msg_info, &hv_vmbus_g_connection.channel_msg_anchor, + msg_list_entry) { + request_header = (hv_vmbus_channel_msg_header*) msg_info->msg; + if (request_header->message_type == + HV_CHANNEL_MESSAGEL_GPADL_HEADER) { + gpadl_header = + (hv_vmbus_channel_gpadl_header*) request_header; + + if ((gpadl_created->child_rel_id == gpadl_header->child_rel_id) + && (gpadl_created->gpadl == gpadl_header->gpadl)) { + memcpy(&msg_info->response.gpadl_created, + gpadl_created, + sizeof(hv_vmbus_channel_gpadl_created)); + sema_post(&msg_info->wait_sema); + break; + } + } + } + mtx_unlock_spin(&hv_vmbus_g_connection.channel_msg_lock); +} + +/** + * @brief GPADL torndown handler. + * + * This is invoked when we received a respons + * to our gpadl teardown request. Find the matching request, copy the + * response and signal the requesting thread + */ +static void +vmbus_channel_on_gpadl_torndown(hv_vmbus_channel_msg_header* hdr) +{ + hv_vmbus_channel_gpadl_torndown* gpadl_torndown; + hv_vmbus_channel_msg_info* msg_info; + hv_vmbus_channel_msg_header* requestHeader; + hv_vmbus_channel_gpadl_teardown* gpadlTeardown; + + gpadl_torndown = (hv_vmbus_channel_gpadl_torndown*)hdr; + + /* + * Find the open msg, copy the result and signal/unblock the + * wait event. + */ + + mtx_lock_spin(&hv_vmbus_g_connection.channel_msg_lock); + + TAILQ_FOREACH(msg_info, &hv_vmbus_g_connection.channel_msg_anchor, + msg_list_entry) { + requestHeader = (hv_vmbus_channel_msg_header*) msg_info->msg; + + if (requestHeader->message_type + == HV_CHANNEL_MESSAGE_GPADL_TEARDOWN) { + gpadlTeardown = + (hv_vmbus_channel_gpadl_teardown*) requestHeader; + + if (gpadl_torndown->gpadl == gpadlTeardown->gpadl) { + memcpy(&msg_info->response.gpadl_torndown, + gpadl_torndown, + sizeof(hv_vmbus_channel_gpadl_torndown)); + sema_post(&msg_info->wait_sema); + break; + } + } + } + mtx_unlock_spin(&hv_vmbus_g_connection.channel_msg_lock); +} + +/** + * @brief Version response handler. + * + * This is invoked when we received a response + * to our initiate contact request. Find the matching request, copy th + * response and signal the requesting thread. + */ +static void +vmbus_channel_on_version_response(hv_vmbus_channel_msg_header* hdr) +{ + hv_vmbus_channel_msg_info* msg_info; + hv_vmbus_channel_msg_header* requestHeader; + hv_vmbus_channel_initiate_contact* initiate; + hv_vmbus_channel_version_response* versionResponse; + + versionResponse = (hv_vmbus_channel_version_response*)hdr; + + mtx_lock_spin(&hv_vmbus_g_connection.channel_msg_lock); + TAILQ_FOREACH(msg_info, &hv_vmbus_g_connection.channel_msg_anchor, + msg_list_entry) { + requestHeader = (hv_vmbus_channel_msg_header*) msg_info->msg; + if (requestHeader->message_type + == HV_CHANNEL_MESSAGE_INITIATED_CONTACT) { + initiate = + (hv_vmbus_channel_initiate_contact*) requestHeader; + memcpy(&msg_info->response.version_response, + versionResponse, + sizeof(hv_vmbus_channel_version_response)); + sema_post(&msg_info->wait_sema); + } + } + mtx_unlock_spin(&hv_vmbus_g_connection.channel_msg_lock); + +} + +/** + * @brief Handler for channel protocol messages. + * + * This is invoked in the vmbus worker thread context. + */ +void +hv_vmbus_on_channel_message(void *context) +{ + hv_vmbus_message* msg; + hv_vmbus_channel_msg_header* hdr; + int size; + + msg = (hv_vmbus_message*) context; + hdr = (hv_vmbus_channel_msg_header*) msg->u.payload; + size = msg->header.payload_size; + + if (hdr->message_type >= HV_CHANNEL_MESSAGE_COUNT) { + free(msg, M_DEVBUF); + return; + } + + if (g_channel_message_table[hdr->message_type].messageHandler) { + g_channel_message_table[hdr->message_type].messageHandler(hdr); + } + + /* Free the msg that was allocated in VmbusOnMsgDPC() */ + free(msg, M_DEVBUF); +} + +/** + * @brief Send a request to get all our pending offers. + */ +int +hv_vmbus_request_channel_offers(void) +{ + int ret; + hv_vmbus_channel_msg_header* msg; + hv_vmbus_channel_msg_info* msg_info; + + msg_info = (hv_vmbus_channel_msg_info *) + malloc(sizeof(hv_vmbus_channel_msg_info) + + sizeof(hv_vmbus_channel_msg_header), M_DEVBUF, M_NOWAIT); + + if (msg_info == NULL) { + if(bootverbose) + printf("Error VMBUS: malloc failed for Request Offers\n"); + return (ENOMEM); + } + + msg = (hv_vmbus_channel_msg_header*) msg_info->msg; + msg->message_type = HV_CHANNEL_MESSAGE_REQUEST_OFFERS; + + ret = hv_vmbus_post_message(msg, sizeof(hv_vmbus_channel_msg_header)); + + if (msg_info) + free(msg_info, M_DEVBUF); + + return (ret); +} + +/** + * @brief Release channels that are unattached/unconnected (i.e., no drivers associated) + */ +void +hv_vmbus_release_unattached_channels(void) +{ + hv_vmbus_channel *channel; + + mtx_lock_spin(&hv_vmbus_g_connection.channel_lock); + + while (!TAILQ_EMPTY(&hv_vmbus_g_connection.channel_anchor)) { + channel = TAILQ_FIRST(&hv_vmbus_g_connection.channel_anchor); + TAILQ_REMOVE(&hv_vmbus_g_connection.channel_anchor, + channel, list_entry); + + hv_vmbus_child_device_unregister(channel->device); + hv_vmbus_free_vmbus_channel(channel); + } + mtx_unlock_spin(&hv_vmbus_g_connection.channel_lock); +} diff --git a/sys/dev/hyperv/vmbus/hv_connection.c b/sys/dev/hyperv/vmbus/hv_connection.c new file mode 100644 index 00000000000..c8e0b48ac65 --- /dev/null +++ b/sys/dev/hyperv/vmbus/hv_connection.c @@ -0,0 +1,431 @@ +/*- + * Copyright (c) 2009-2012 Microsoft Corp. + * Copyright (c) 2012 NetApp Inc. + * Copyright (c) 2012 Citrix Inc. + * 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 unmodified, 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include "hv_vmbus_priv.h" + +/* + * Globals + */ +hv_vmbus_connection hv_vmbus_g_connection = + { .connect_state = HV_DISCONNECTED, + .next_gpadl_handle = 0xE1E10, }; + +/** + * Send a connect request on the partition service connection + */ +int +hv_vmbus_connect(void) { + int ret = 0; + hv_vmbus_channel_msg_info* msg_info = NULL; + hv_vmbus_channel_initiate_contact* msg; + + /** + * Make sure we are not connecting or connected + */ + if (hv_vmbus_g_connection.connect_state != HV_DISCONNECTED) { + return (-1); + } + + /** + * Initialize the vmbus connection + */ + hv_vmbus_g_connection.connect_state = HV_CONNECTING; + hv_vmbus_g_connection.work_queue = hv_work_queue_create("vmbusQ"); + sema_init(&hv_vmbus_g_connection.control_sema, 1, "control_sema"); + + TAILQ_INIT(&hv_vmbus_g_connection.channel_msg_anchor); + mtx_init(&hv_vmbus_g_connection.channel_msg_lock, "vmbus channel msg", + NULL, MTX_SPIN); + + TAILQ_INIT(&hv_vmbus_g_connection.channel_anchor); + mtx_init(&hv_vmbus_g_connection.channel_lock, "vmbus channel", + NULL, MTX_SPIN); + + /** + * Setup the vmbus event connection for channel interrupt abstraction + * stuff + */ + hv_vmbus_g_connection.interrupt_page = contigmalloc( + PAGE_SIZE, M_DEVBUF, + M_NOWAIT | M_ZERO, 0UL, + BUS_SPACE_MAXADDR, + PAGE_SIZE, 0); + KASSERT(hv_vmbus_g_connection.interrupt_page != NULL, + ("Error VMBUS: malloc failed to allocate Channel" + " Request Event message!")); + if (hv_vmbus_g_connection.interrupt_page == NULL) { + ret = ENOMEM; + goto cleanup; + } + + hv_vmbus_g_connection.recv_interrupt_page = + hv_vmbus_g_connection.interrupt_page; + + hv_vmbus_g_connection.send_interrupt_page = + ((uint8_t *) hv_vmbus_g_connection.interrupt_page + + (PAGE_SIZE >> 1)); + + /** + * Set up the monitor notification facility. The 1st page for + * parent->child and the 2nd page for child->parent + */ + hv_vmbus_g_connection.monitor_pages = contigmalloc( + 2 * PAGE_SIZE, + M_DEVBUF, + M_NOWAIT | M_ZERO, + 0UL, + BUS_SPACE_MAXADDR, + PAGE_SIZE, + 0); + KASSERT(hv_vmbus_g_connection.monitor_pages != NULL, + ("Error VMBUS: malloc failed to allocate Monitor Pages!")); + if (hv_vmbus_g_connection.monitor_pages == NULL) { + ret = ENOMEM; + goto cleanup; + } + + msg_info = (hv_vmbus_channel_msg_info*) + malloc(sizeof(hv_vmbus_channel_msg_info) + + sizeof(hv_vmbus_channel_initiate_contact), + M_DEVBUF, M_NOWAIT | M_ZERO); + KASSERT(msg_info != NULL, + ("Error VMBUS: malloc failed for Initiate Contact message!")); + if (msg_info == NULL) { + ret = ENOMEM; + goto cleanup; + } + + sema_init(&msg_info->wait_sema, 0, "Msg Info Sema"); + msg = (hv_vmbus_channel_initiate_contact*) msg_info->msg; + + msg->header.message_type = HV_CHANNEL_MESSAGE_INITIATED_CONTACT; + msg->vmbus_version_requested = HV_VMBUS_REVISION_NUMBER; + + msg->interrupt_page = hv_get_phys_addr( + hv_vmbus_g_connection.interrupt_page); + + msg->monitor_page_1 = hv_get_phys_addr( + hv_vmbus_g_connection.monitor_pages); + + msg->monitor_page_2 = + hv_get_phys_addr( + ((uint8_t *) hv_vmbus_g_connection.monitor_pages + + PAGE_SIZE)); + + /** + * Add to list before we send the request since we may receive the + * response before returning from this routine + */ + mtx_lock_spin(&hv_vmbus_g_connection.channel_msg_lock); + + TAILQ_INSERT_TAIL( + &hv_vmbus_g_connection.channel_msg_anchor, + msg_info, + msg_list_entry); + + mtx_unlock_spin(&hv_vmbus_g_connection.channel_msg_lock); + + ret = hv_vmbus_post_message( + msg, + sizeof(hv_vmbus_channel_initiate_contact)); + + if (ret != 0) { + mtx_lock_spin(&hv_vmbus_g_connection.channel_msg_lock); + TAILQ_REMOVE( + &hv_vmbus_g_connection.channel_msg_anchor, + msg_info, + msg_list_entry); + mtx_unlock_spin(&hv_vmbus_g_connection.channel_msg_lock); + goto cleanup; + } + + /** + * Wait for the connection response + */ + ret = sema_timedwait(&msg_info->wait_sema, 500); /* KYS 5 seconds */ + + mtx_lock_spin(&hv_vmbus_g_connection.channel_msg_lock); + TAILQ_REMOVE( + &hv_vmbus_g_connection.channel_msg_anchor, + msg_info, + msg_list_entry); + mtx_unlock_spin(&hv_vmbus_g_connection.channel_msg_lock); + + /** + * Check if successful + */ + if (msg_info->response.version_response.version_supported) { + hv_vmbus_g_connection.connect_state = HV_CONNECTED; + } else { + ret = ECONNREFUSED; + goto cleanup; + } + + sema_destroy(&msg_info->wait_sema); + free(msg_info, M_DEVBUF); + + return (0); + + /* + * Cleanup after failure! + */ + cleanup: + + hv_vmbus_g_connection.connect_state = HV_DISCONNECTED; + + hv_work_queue_close(hv_vmbus_g_connection.work_queue); + sema_destroy(&hv_vmbus_g_connection.control_sema); + mtx_destroy(&hv_vmbus_g_connection.channel_lock); + mtx_destroy(&hv_vmbus_g_connection.channel_msg_lock); + + if (hv_vmbus_g_connection.interrupt_page != NULL) { + contigfree( + hv_vmbus_g_connection.interrupt_page, + PAGE_SIZE, + M_DEVBUF); + hv_vmbus_g_connection.interrupt_page = NULL; + } + + if (hv_vmbus_g_connection.monitor_pages != NULL) { + contigfree( + hv_vmbus_g_connection.monitor_pages, + 2 * PAGE_SIZE, + M_DEVBUF); + hv_vmbus_g_connection.monitor_pages = NULL; + } + + if (msg_info) { + sema_destroy(&msg_info->wait_sema); + free(msg_info, M_DEVBUF); + } + + return (ret); +} + +/** + * Send a disconnect request on the partition service connection + */ +int +hv_vmbus_disconnect(void) { + int ret = 0; + hv_vmbus_channel_unload* msg; + + msg = malloc(sizeof(hv_vmbus_channel_unload), + M_DEVBUF, M_NOWAIT | M_ZERO); + KASSERT(msg != NULL, + ("Error VMBUS: malloc failed to allocate Channel Unload Msg!")); + if (msg == NULL) + return (ENOMEM); + + msg->message_type = HV_CHANNEL_MESSAGE_UNLOAD; + + ret = hv_vmbus_post_message(msg, sizeof(hv_vmbus_channel_unload)); + + + contigfree(hv_vmbus_g_connection.interrupt_page, PAGE_SIZE, M_DEVBUF); + + mtx_destroy(&hv_vmbus_g_connection.channel_msg_lock); + + hv_work_queue_close(hv_vmbus_g_connection.work_queue); + sema_destroy(&hv_vmbus_g_connection.control_sema); + + hv_vmbus_g_connection.connect_state = HV_DISCONNECTED; + + free(msg, M_DEVBUF); + + return (ret); +} + +/** + * Get the channel object given its child relative id (ie channel id) + */ +hv_vmbus_channel* +hv_vmbus_get_channel_from_rel_id(uint32_t rel_id) { + + hv_vmbus_channel* channel; + hv_vmbus_channel* foundChannel = NULL; + + /* + * TODO: + * Consider optimization where relids are stored in a fixed size array + * and channels are accessed without the need to take this lock or search + * the list. + */ + mtx_lock_spin(&hv_vmbus_g_connection.channel_lock); + TAILQ_FOREACH(channel, + &hv_vmbus_g_connection.channel_anchor, list_entry) { + + if (channel->offer_msg.child_rel_id == rel_id) { + foundChannel = channel; + break; + } + } + mtx_unlock_spin(&hv_vmbus_g_connection.channel_lock); + + return (foundChannel); +} + +/** + * Process a channel event notification + */ +static void +VmbusProcessChannelEvent(uint32_t relid) +{ + hv_vmbus_channel* channel; + + /** + * Find the channel based on this relid and invokes + * the channel callback to process the event + */ + + channel = hv_vmbus_get_channel_from_rel_id(relid); + + if (channel == NULL) { + return; + } + /** + * To deal with the race condition where we might + * receive a packet while the relevant driver is + * being unloaded, dispatch the callback while + * holding the channel lock. The unloading driver + * will acquire the same channel lock to set the + * callback to NULL. This closes the window. + */ + + mtx_lock(&channel->inbound_lock); + if (channel->on_channel_callback != NULL) { + channel->on_channel_callback(channel->channel_callback_context); + } + mtx_unlock(&channel->inbound_lock); +} + +/** + * Handler for events + */ +void +hv_vmbus_on_events(void *arg) +{ + int dword; + int bit; + int rel_id; + int maxdword = HV_MAX_NUM_CHANNELS_SUPPORTED >> 5; + /* int maxdword = PAGE_SIZE >> 3; */ + + /* + * receive size is 1/2 page and divide that by 4 bytes + */ + + uint32_t* recv_interrupt_page = + hv_vmbus_g_connection.recv_interrupt_page; + + /* + * Check events + */ + if (recv_interrupt_page != NULL) { + for (dword = 0; dword < maxdword; dword++) { + if (recv_interrupt_page[dword]) { + for (bit = 0; bit < 32; bit++) { + if (synch_test_and_clear_bit(bit, + (uint32_t *) &recv_interrupt_page[dword])) { + rel_id = (dword << 5) + bit; + if (rel_id == 0) { + /* + * Special case - + * vmbus channel protocol msg. + */ + continue; + } else { + VmbusProcessChannelEvent(rel_id); + + } + } + } + } + } + } + + return; +} + +/** + * Send a msg on the vmbus's message connection + */ +int hv_vmbus_post_message(void *buffer, size_t bufferLen) { + int ret = 0; + hv_vmbus_connection_id connId; + unsigned retries = 0; + + /* NetScaler delays from previous code were consolidated here */ + static int delayAmount[] = {100, 100, 100, 500, 500, 5000, 5000, 5000}; + + /* for(each entry in delayAmount) try to post message, + * delay a little bit before retrying + */ + for (retries = 0; + retries < sizeof(delayAmount)/sizeof(delayAmount[0]); retries++) { + connId.as_uint32_t = 0; + connId.u.id = HV_VMBUS_MESSAGE_CONNECTION_ID; + ret = hv_vmbus_post_msg_via_msg_ipc(connId, 1, buffer, bufferLen); + if (ret != HV_STATUS_INSUFFICIENT_BUFFERS) + break; + /* TODO: KYS We should use a blocking wait call */ + DELAY(delayAmount[retries]); + } + + KASSERT(ret == 0, ("Error VMBUS: Message Post Failed\n")); + + return (ret); +} + +/** + * Send an event notification to the parent + */ +int +hv_vmbus_set_event(uint32_t child_rel_id) { + int ret = 0; + + /* Each uint32_t represents 32 channels */ + + synch_set_bit(child_rel_id & 31, + (((uint32_t *)hv_vmbus_g_connection.send_interrupt_page + + (child_rel_id >> 5)))); + ret = hv_vmbus_signal_event(); + + return (ret); +} + diff --git a/sys/dev/hyperv/vmbus/hv_hv.c b/sys/dev/hyperv/vmbus/hv_hv.c new file mode 100644 index 00000000000..e3f3ae0870f --- /dev/null +++ b/sys/dev/hyperv/vmbus/hv_hv.c @@ -0,0 +1,494 @@ +/*- + * Copyright (c) 2009-2012 Microsoft Corp. + * Copyright (c) 2012 NetApp Inc. + * Copyright (c) 2012 Citrix Inc. + * 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 unmodified, 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +/** + * Implements low-level interactions with Hypver-V/Azure + */ + +#include +#include +#include +#include +#include +#include +#include +#include + + +#include "hv_vmbus_priv.h" + +#define HV_X64_MSR_GUEST_OS_ID 0x40000000 + +#define HV_X64_CPUID_MIN 0x40000005 +#define HV_X64_CPUID_MAX 0x4000ffff +#define HV_X64_MSR_TIME_REF_COUNT 0x40000020 + +#define HV_NANOSECONDS_PER_SEC 1000000000L + + +static u_int hv_get_timecount(struct timecounter *tc); + +static inline void do_cpuid_inline(unsigned int op, unsigned int *eax, + unsigned int *ebx, unsigned int *ecx, unsigned int *edx) { + __asm__ __volatile__("cpuid" : "=a" (*eax), "=b" (*ebx), "=c" (*ecx), + "=d" (*edx) : "0" (op), "c" (ecx)); +} + +/** + * Globals + */ +hv_vmbus_context hv_vmbus_g_context = { + .syn_ic_initialized = FALSE, + .hypercall_page = NULL, + .signal_event_param = NULL, + .signal_event_buffer = NULL, +}; + +static struct timecounter hv_timecounter = { + hv_get_timecount, 0, ~0u, HV_NANOSECONDS_PER_SEC/100, "Hyper-V", HV_NANOSECONDS_PER_SEC/100 +}; + +static u_int +hv_get_timecount(struct timecounter *tc) +{ + u_int now = rdmsr(HV_X64_MSR_TIME_REF_COUNT); + return (now); +} + +/** + * @brief Query the cpuid for presence of windows hypervisor + */ +int +hv_vmbus_query_hypervisor_presence(void) +{ + u_int regs[4]; + int hyper_v_detected = 0; + do_cpuid(1, regs); + if (regs[2] & 0x80000000) { /* if(a hypervisor is detected) */ + /* make sure this really is Hyper-V */ + /* we look at the CPUID info */ + do_cpuid(HV_X64_MSR_GUEST_OS_ID, regs); + hyper_v_detected = + regs[0] >= HV_X64_CPUID_MIN && + regs[0] <= HV_X64_CPUID_MAX && + !memcmp("Microsoft Hv", ®s[1], 12); + } + return (hyper_v_detected); +} + +/** + * @brief Get version of the windows hypervisor + */ +static int +hv_vmbus_get_hypervisor_version(void) +{ + unsigned int eax; + unsigned int ebx; + unsigned int ecx; + unsigned int edx; + unsigned int maxLeaf; + unsigned int op; + + /* + * Its assumed that this is called after confirming that + * Viridian is present + * Query id and revision. + */ + eax = 0; + ebx = 0; + ecx = 0; + edx = 0; + op = HV_CPU_ID_FUNCTION_HV_VENDOR_AND_MAX_FUNCTION; + do_cpuid_inline(op, &eax, &ebx, &ecx, &edx); + + maxLeaf = eax; + eax = 0; + ebx = 0; + ecx = 0; + edx = 0; + op = HV_CPU_ID_FUNCTION_HV_INTERFACE; + do_cpuid_inline(op, &eax, &ebx, &ecx, &edx); + + if (maxLeaf >= HV_CPU_ID_FUNCTION_MS_HV_VERSION) { + eax = 0; + ebx = 0; + ecx = 0; + edx = 0; + op = HV_CPU_ID_FUNCTION_MS_HV_VERSION; + do_cpuid_inline(op, &eax, &ebx, &ecx, &edx); + } + return (maxLeaf); +} + +/** + * @brief Invoke the specified hypercall + */ +static uint64_t +hv_vmbus_do_hypercall(uint64_t control, void* input, void* output) +{ +#ifdef __x86_64__ + uint64_t hv_status = 0; + uint64_t input_address = (input) ? hv_get_phys_addr(input) : 0; + uint64_t output_address = (output) ? hv_get_phys_addr(output) : 0; + volatile void* hypercall_page = hv_vmbus_g_context.hypercall_page; + + __asm__ __volatile__ ("mov %0, %%r8" : : "r" (output_address): "r8"); + __asm__ __volatile__ ("call *%3" : "=a"(hv_status): + "c" (control), "d" (input_address), + "m" (hypercall_page)); + return (hv_status); +#else + uint32_t control_high = control >> 32; + uint32_t control_low = control & 0xFFFFFFFF; + uint32_t hv_status_high = 1; + uint32_t hv_status_low = 1; + uint64_t input_address = (input) ? hv_get_phys_addr(input) : 0; + uint32_t input_address_high = input_address >> 32; + uint32_t input_address_low = input_address & 0xFFFFFFFF; + uint64_t output_address = (output) ? hv_get_phys_addr(output) : 0; + uint32_t output_address_high = output_address >> 32; + uint32_t output_address_low = output_address & 0xFFFFFFFF; + volatile void* hypercall_page = hv_vmbus_g_context.hypercall_page; + + __asm__ __volatile__ ("call *%8" : "=d"(hv_status_high), + "=a"(hv_status_low) : "d" (control_high), + "a" (control_low), "b" (input_address_high), + "c" (input_address_low), + "D"(output_address_high), + "S"(output_address_low), "m" (hypercall_page)); + return (hv_status_low | ((uint64_t)hv_status_high << 32)); +#endif /* __x86_64__ */ +} + +/** + * @brief Main initialization routine. + * + * This routine must be called + * before any other routines in here are called + */ +int +hv_vmbus_init(void) +{ + int max_leaf; + hv_vmbus_x64_msr_hypercall_contents hypercall_msr; + void* virt_addr = 0; + + memset( + hv_vmbus_g_context.syn_ic_event_page, + 0, + sizeof(hv_vmbus_handle) * MAXCPU); + + memset( + hv_vmbus_g_context.syn_ic_msg_page, + 0, + sizeof(hv_vmbus_handle) * MAXCPU); + + if (!hv_vmbus_query_hypervisor_presence()) + goto cleanup; + + max_leaf = hv_vmbus_get_hypervisor_version(); + + /* + * Write our OS info + */ + uint64_t os_guest_info = HV_FREEBSD_GUEST_ID; + wrmsr(HV_X64_MSR_GUEST_OS_ID, os_guest_info); + hv_vmbus_g_context.guest_id = os_guest_info; + + /* + * See if the hypercall page is already set + */ + hypercall_msr.as_uint64_t = rdmsr(HV_X64_MSR_HYPERCALL); + virt_addr = malloc(PAGE_SIZE, M_DEVBUF, M_NOWAIT | M_ZERO); + KASSERT(virt_addr != NULL, + ("Error VMBUS: malloc failed to allocate page during init!")); + if (virt_addr == NULL) + goto cleanup; + + hypercall_msr.enable = 1; + hypercall_msr.guest_physical_address = + (hv_get_phys_addr(virt_addr) >> PAGE_SHIFT); + wrmsr(HV_X64_MSR_HYPERCALL, hypercall_msr.as_uint64_t); + + /* + * Confirm that hypercall page did get set up + */ + hypercall_msr.as_uint64_t = 0; + hypercall_msr.as_uint64_t = rdmsr(HV_X64_MSR_HYPERCALL); + + if (!hypercall_msr.enable) + goto cleanup; + + hv_vmbus_g_context.hypercall_page = virt_addr; + + /* + * Setup the global signal event param for the signal event hypercall + */ + hv_vmbus_g_context.signal_event_buffer = + malloc(sizeof(hv_vmbus_input_signal_event_buffer), M_DEVBUF, + M_ZERO | M_NOWAIT); + KASSERT(hv_vmbus_g_context.signal_event_buffer != NULL, + ("Error VMBUS: Failed to allocate signal_event_buffer\n")); + if (hv_vmbus_g_context.signal_event_buffer == NULL) + goto cleanup; + + hv_vmbus_g_context.signal_event_param = + (hv_vmbus_input_signal_event*) + (HV_ALIGN_UP((unsigned long) + hv_vmbus_g_context.signal_event_buffer, + HV_HYPERCALL_PARAM_ALIGN)); + hv_vmbus_g_context.signal_event_param->connection_id.as_uint32_t = 0; + hv_vmbus_g_context.signal_event_param->connection_id.u.id = + HV_VMBUS_EVENT_CONNECTION_ID; + hv_vmbus_g_context.signal_event_param->flag_number = 0; + hv_vmbus_g_context.signal_event_param->rsvd_z = 0; + + tc_init(&hv_timecounter); /* register virtual timecount */ + + return (0); + + cleanup: + if (virt_addr != NULL) { + if (hypercall_msr.enable) { + hypercall_msr.as_uint64_t = 0; + wrmsr(HV_X64_MSR_HYPERCALL, + hypercall_msr.as_uint64_t); + } + + free(virt_addr, M_DEVBUF); + } + return (ENOTSUP); +} + +/** + * @brief Cleanup routine, called normally during driver unloading or exiting + */ +void +hv_vmbus_cleanup(void) +{ + hv_vmbus_x64_msr_hypercall_contents hypercall_msr; + + if (hv_vmbus_g_context.signal_event_buffer != NULL) { + free(hv_vmbus_g_context.signal_event_buffer, M_DEVBUF); + hv_vmbus_g_context.signal_event_buffer = NULL; + hv_vmbus_g_context.signal_event_param = NULL; + } + + if (hv_vmbus_g_context.guest_id == HV_FREEBSD_GUEST_ID) { + if (hv_vmbus_g_context.hypercall_page != NULL) { + hypercall_msr.as_uint64_t = 0; + wrmsr(HV_X64_MSR_HYPERCALL, + hypercall_msr.as_uint64_t); + free(hv_vmbus_g_context.hypercall_page, M_DEVBUF); + hv_vmbus_g_context.hypercall_page = NULL; + } + } +} + +/** + * @brief Post a message using the hypervisor message IPC. + * (This involves a hypercall.) + */ +hv_vmbus_status +hv_vmbus_post_msg_via_msg_ipc( + hv_vmbus_connection_id connection_id, + hv_vmbus_msg_type message_type, + void* payload, + size_t payload_size) +{ + struct alignedinput { + uint64_t alignment8; + hv_vmbus_input_post_message msg; + }; + + hv_vmbus_input_post_message* aligned_msg; + hv_vmbus_status status; + size_t addr; + + if (payload_size > HV_MESSAGE_PAYLOAD_BYTE_COUNT) + return (EMSGSIZE); + + addr = (size_t) malloc(sizeof(struct alignedinput), M_DEVBUF, + M_ZERO | M_NOWAIT); + KASSERT(addr != 0, + ("Error VMBUS: malloc failed to allocate message buffer!")); + if (addr == 0) + return (ENOMEM); + + aligned_msg = (hv_vmbus_input_post_message*) + (HV_ALIGN_UP(addr, HV_HYPERCALL_PARAM_ALIGN)); + + aligned_msg->connection_id = connection_id; + aligned_msg->message_type = message_type; + aligned_msg->payload_size = payload_size; + memcpy((void*) aligned_msg->payload, payload, payload_size); + + status = hv_vmbus_do_hypercall( + HV_CALL_POST_MESSAGE, aligned_msg, 0) & 0xFFFF; + + free((void *) addr, M_DEVBUF); + return (status); +} + +/** + * @brief Signal an event on the specified connection using the hypervisor + * event IPC. (This involves a hypercall.) + */ +hv_vmbus_status +hv_vmbus_signal_event() +{ + hv_vmbus_status status; + + status = hv_vmbus_do_hypercall( + HV_CALL_SIGNAL_EVENT, + hv_vmbus_g_context.signal_event_param, + 0) & 0xFFFF; + + return (status); +} + +/** + * @brief hv_vmbus_synic_init + */ +void +hv_vmbus_synic_init(void *arg) + +{ + int cpu; + hv_vmbus_synic_simp simp; + hv_vmbus_synic_siefp siefp; + hv_vmbus_synic_scontrol sctrl; + hv_vmbus_synic_sint shared_sint; + uint64_t version; + hv_setup_args* setup_args = (hv_setup_args *)arg; + + cpu = PCPU_GET(cpuid); + + if (hv_vmbus_g_context.hypercall_page == NULL) + return; + + /* + * KYS: Looks like we can only initialize on cpu0; don't we support + * SMP guests? + * + * TODO: Need to add SMP support for FreeBSD V9 + */ + + if (cpu != 0) + return; + + /* + * TODO: Check the version + */ + version = rdmsr(HV_X64_MSR_SVERSION); + + + hv_vmbus_g_context.syn_ic_msg_page[cpu] = setup_args->page_buffers[0]; + hv_vmbus_g_context.syn_ic_event_page[cpu] = setup_args->page_buffers[1]; + + /* + * Setup the Synic's message page + */ + + simp.as_uint64_t = rdmsr(HV_X64_MSR_SIMP); + simp.simp_enabled = 1; + simp.base_simp_gpa = ((hv_get_phys_addr( + hv_vmbus_g_context.syn_ic_msg_page[cpu])) >> PAGE_SHIFT); + + wrmsr(HV_X64_MSR_SIMP, simp.as_uint64_t); + + /* + * Setup the Synic's event page + */ + siefp.as_uint64_t = rdmsr(HV_X64_MSR_SIEFP); + siefp.siefp_enabled = 1; + siefp.base_siefp_gpa = ((hv_get_phys_addr( + hv_vmbus_g_context.syn_ic_event_page[cpu])) >> PAGE_SHIFT); + + wrmsr(HV_X64_MSR_SIEFP, siefp.as_uint64_t); + + /*HV_SHARED_SINT_IDT_VECTOR + 0x20; */ + shared_sint.vector = setup_args->vector; + shared_sint.masked = FALSE; + shared_sint.auto_eoi = FALSE; + + wrmsr(HV_X64_MSR_SINT0 + HV_VMBUS_MESSAGE_SINT, + shared_sint.as_uint64_t); + + /* Enable the global synic bit */ + sctrl.as_uint64_t = rdmsr(HV_X64_MSR_SCONTROL); + sctrl.enable = 1; + + wrmsr(HV_X64_MSR_SCONTROL, sctrl.as_uint64_t); + + hv_vmbus_g_context.syn_ic_initialized = TRUE; + + return; +} + +/** + * @brief Cleanup routine for hv_vmbus_synic_init() + */ +void hv_vmbus_synic_cleanup(void *arg) +{ + hv_vmbus_synic_sint shared_sint; + hv_vmbus_synic_simp simp; + hv_vmbus_synic_siefp siefp; + int cpu = PCPU_GET(cpuid); + + if (!hv_vmbus_g_context.syn_ic_initialized) + return; + + if (cpu != 0) + return; /* TODO: XXXKYS: SMP? */ + + shared_sint.as_uint64_t = rdmsr( + HV_X64_MSR_SINT0 + HV_VMBUS_MESSAGE_SINT); + + shared_sint.masked = 1; + + /* + * Disable the interrupt + */ + wrmsr( + HV_X64_MSR_SINT0 + HV_VMBUS_MESSAGE_SINT, + shared_sint.as_uint64_t); + + simp.as_uint64_t = rdmsr(HV_X64_MSR_SIMP); + simp.simp_enabled = 0; + simp.base_simp_gpa = 0; + + wrmsr(HV_X64_MSR_SIMP, simp.as_uint64_t); + + siefp.as_uint64_t = rdmsr(HV_X64_MSR_SIEFP); + siefp.siefp_enabled = 0; + siefp.base_siefp_gpa = 0; + + wrmsr(HV_X64_MSR_SIEFP, siefp.as_uint64_t); +} + diff --git a/sys/dev/hyperv/vmbus/hv_ring_buffer.c b/sys/dev/hyperv/vmbus/hv_ring_buffer.c new file mode 100644 index 00000000000..f7c1965c833 --- /dev/null +++ b/sys/dev/hyperv/vmbus/hv_ring_buffer.c @@ -0,0 +1,440 @@ +/*- + * Copyright (c) 2009-2012 Microsoft Corp. + * Copyright (c) 2012 NetApp Inc. + * Copyright (c) 2012 Citrix Inc. + * 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 unmodified, 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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 +#include +#include + +#include "hv_vmbus_priv.h" + +/* Amount of space to write to */ +#define HV_BYTES_AVAIL_TO_WRITE(r, w, z) ((w) >= (r))? \ + ((z) - ((w) - (r))):((r) - (w)) + +/** + * @brief Get number of bytes available to read and to write to + * for the specified ring buffer + */ +static inline void +get_ring_buffer_avail_bytes( + hv_vmbus_ring_buffer_info* rbi, + uint32_t* read, + uint32_t* write) +{ + uint32_t read_loc, write_loc; + + /* + * Capture the read/write indices before they changed + */ + read_loc = rbi->ring_buffer->read_index; + write_loc = rbi->ring_buffer->write_index; + + *write = HV_BYTES_AVAIL_TO_WRITE( + read_loc, write_loc, rbi->ring_data_size); + *read = rbi->ring_data_size - *write; +} + +/** + * @brief Get the next write location for the specified ring buffer + */ +static inline uint32_t +get_next_write_location(hv_vmbus_ring_buffer_info* ring_info) +{ + uint32_t next = ring_info->ring_buffer->write_index; + return (next); +} + +/** + * @brief Set the next write location for the specified ring buffer + */ +static inline void +set_next_write_location( + hv_vmbus_ring_buffer_info* ring_info, + uint32_t next_write_location) +{ + ring_info->ring_buffer->write_index = next_write_location; +} + +/** + * @brief Get the next read location for the specified ring buffer + */ +static inline uint32_t +get_next_read_location(hv_vmbus_ring_buffer_info* ring_info) +{ + uint32_t next = ring_info->ring_buffer->read_index; + return (next); +} + +/** + * @brief Get the next read location + offset for the specified ring buffer. + * This allows the caller to skip. + */ +static inline uint32_t +get_next_read_location_with_offset( + hv_vmbus_ring_buffer_info* ring_info, + uint32_t offset) +{ + uint32_t next = ring_info->ring_buffer->read_index; + next += offset; + next %= ring_info->ring_data_size; + return (next); +} + +/** + * @brief Set the next read location for the specified ring buffer + */ +static inline void +set_next_read_location( + hv_vmbus_ring_buffer_info* ring_info, + uint32_t next_read_location) +{ + ring_info->ring_buffer->read_index = next_read_location; +} + +/** + * @brief Get the start of the ring buffer + */ +static inline void * +get_ring_buffer(hv_vmbus_ring_buffer_info* ring_info) +{ + return (void *) ring_info->ring_buffer->buffer; +} + +/** + * @brief Get the size of the ring buffer. + */ +static inline uint32_t +get_ring_buffer_size(hv_vmbus_ring_buffer_info* ring_info) +{ + return ring_info->ring_data_size; +} + +/** + * Get the read and write indices as uint64_t of the specified ring buffer. + */ +static inline uint64_t +get_ring_buffer_indices(hv_vmbus_ring_buffer_info* ring_info) +{ + return (uint64_t) ring_info->ring_buffer->write_index << 32; +} + +static uint32_t copy_to_ring_buffer( + hv_vmbus_ring_buffer_info* ring_info, + uint32_t start_write_offset, + char* src, + uint32_t src_len); + +static uint32_t copy_from_ring_buffer( + hv_vmbus_ring_buffer_info* ring_info, + char* dest, + uint32_t dest_len, + uint32_t start_read_offset); + + +/** + * @brief Get the interrupt mask for the specified ring buffer. + */ +uint32_t +hv_vmbus_get_ring_buffer_interrupt_mask(hv_vmbus_ring_buffer_info *rbi) +{ + return rbi->ring_buffer->interrupt_mask; +} + +/** + * @brief Initialize the ring buffer. + */ +int +hv_vmbus_ring_buffer_init( + hv_vmbus_ring_buffer_info* ring_info, + void* buffer, + uint32_t buffer_len) +{ + memset(ring_info, 0, sizeof(hv_vmbus_ring_buffer_info)); + + ring_info->ring_buffer = (hv_vmbus_ring_buffer*) buffer; + ring_info->ring_buffer->read_index = + ring_info->ring_buffer->write_index = 0; + + ring_info->ring_size = buffer_len; + ring_info->ring_data_size = buffer_len - sizeof(hv_vmbus_ring_buffer); + + mtx_init(&ring_info->ring_lock, "vmbus ring buffer", NULL, MTX_SPIN); + + return (0); +} + +/** + * @brief Cleanup the ring buffer. + */ +void hv_ring_buffer_cleanup(hv_vmbus_ring_buffer_info* ring_info) +{ + mtx_destroy(&ring_info->ring_lock); +} + +/** + * @brief Write to the ring buffer. + */ +int +hv_ring_buffer_write( + hv_vmbus_ring_buffer_info* out_ring_info, + hv_vmbus_sg_buffer_list sg_buffers[], + uint32_t sg_buffer_count) +{ + int i = 0; + uint32_t byte_avail_to_write; + uint32_t byte_avail_to_read; + uint32_t total_bytes_to_write = 0; + + volatile uint32_t next_write_location; + uint64_t prev_indices = 0; + + for (i = 0; i < sg_buffer_count; i++) { + total_bytes_to_write += sg_buffers[i].length; + } + + total_bytes_to_write += sizeof(uint64_t); + + mtx_lock_spin(&out_ring_info->ring_lock); + + get_ring_buffer_avail_bytes(out_ring_info, &byte_avail_to_read, + &byte_avail_to_write); + + /* + * If there is only room for the packet, assume it is full. + * Otherwise, the next time around, we think the ring buffer + * is empty since the read index == write index + */ + + if (byte_avail_to_write <= total_bytes_to_write) { + + mtx_unlock_spin(&out_ring_info->ring_lock); + return (EAGAIN); + } + + /* + * Write to the ring buffer + */ + next_write_location = get_next_write_location(out_ring_info); + + for (i = 0; i < sg_buffer_count; i++) { + next_write_location = copy_to_ring_buffer(out_ring_info, + next_write_location, (char *) sg_buffers[i].data, + sg_buffers[i].length); + } + + /* + * Set previous packet start + */ + prev_indices = get_ring_buffer_indices(out_ring_info); + + next_write_location = copy_to_ring_buffer( + out_ring_info, next_write_location, + (char *) &prev_indices, sizeof(uint64_t)); + + /* + * Make sure we flush all writes before updating the writeIndex + */ + wmb(); + + /* + * Now, update the write location + */ + set_next_write_location(out_ring_info, next_write_location); + + mtx_unlock_spin(&out_ring_info->ring_lock); + + return (0); +} + +/** + * @brief Read without advancing the read index. + */ +int +hv_ring_buffer_peek( + hv_vmbus_ring_buffer_info* in_ring_info, + void* buffer, + uint32_t buffer_len) +{ + uint32_t bytesAvailToWrite; + uint32_t bytesAvailToRead; + uint32_t nextReadLocation = 0; + + mtx_lock_spin(&in_ring_info->ring_lock); + + get_ring_buffer_avail_bytes(in_ring_info, &bytesAvailToRead, + &bytesAvailToWrite); + + /* + * Make sure there is something to read + */ + if (bytesAvailToRead < buffer_len) { + mtx_unlock_spin(&in_ring_info->ring_lock); + return (EAGAIN); + } + + /* + * Convert to byte offset + */ + nextReadLocation = get_next_read_location(in_ring_info); + + nextReadLocation = copy_from_ring_buffer( + in_ring_info, (char *)buffer, buffer_len, nextReadLocation); + + mtx_unlock_spin(&in_ring_info->ring_lock); + + return (0); +} + +/** + * @brief Read and advance the read index. + */ +int +hv_ring_buffer_read( + hv_vmbus_ring_buffer_info* in_ring_info, + void* buffer, + uint32_t buffer_len, + uint32_t offset) +{ + uint32_t bytes_avail_to_write; + uint32_t bytes_avail_to_read; + uint32_t next_read_location = 0; + uint64_t prev_indices = 0; + + if (buffer_len <= 0) + return (EINVAL); + + mtx_lock_spin(&in_ring_info->ring_lock); + + get_ring_buffer_avail_bytes( + in_ring_info, &bytes_avail_to_read, + &bytes_avail_to_write); + + /* + * Make sure there is something to read + */ + if (bytes_avail_to_read < buffer_len) { + mtx_unlock_spin(&in_ring_info->ring_lock); + return (EAGAIN); + } + + next_read_location = get_next_read_location_with_offset( + in_ring_info, + offset); + + next_read_location = copy_from_ring_buffer( + in_ring_info, + (char *) buffer, + buffer_len, + next_read_location); + + next_read_location = copy_from_ring_buffer( + in_ring_info, + (char *) &prev_indices, + sizeof(uint64_t), + next_read_location); + + /* + * Make sure all reads are done before we update the read index since + * the writer may start writing to the read area once the read index + * is updated. + */ + wmb(); + + /* + * Update the read index + */ + set_next_read_location(in_ring_info, next_read_location); + + mtx_unlock_spin(&in_ring_info->ring_lock); + + return (0); +} + +/** + * @brief Helper routine to copy from source to ring buffer. + * + * Assume there is enough room. Handles wrap-around in dest case only! + */ +uint32_t +copy_to_ring_buffer( + hv_vmbus_ring_buffer_info* ring_info, + uint32_t start_write_offset, + char* src, + uint32_t src_len) +{ + char *ring_buffer = get_ring_buffer(ring_info); + uint32_t ring_buffer_size = get_ring_buffer_size(ring_info); + uint32_t fragLen; + + if (src_len > ring_buffer_size - start_write_offset) { + /* wrap-around detected! */ + fragLen = ring_buffer_size - start_write_offset; + memcpy(ring_buffer + start_write_offset, src, fragLen); + memcpy(ring_buffer, src + fragLen, src_len - fragLen); + } else { + memcpy(ring_buffer + start_write_offset, src, src_len); + } + + start_write_offset += src_len; + start_write_offset %= ring_buffer_size; + + return (start_write_offset); +} + +/** + * @brief Helper routine to copy to source from ring buffer. + * + * Assume there is enough room. Handles wrap-around in src case only! + */ +uint32_t +copy_from_ring_buffer( + hv_vmbus_ring_buffer_info* ring_info, + char* dest, + uint32_t dest_len, + uint32_t start_read_offset) +{ + uint32_t fragLen; + char *ring_buffer = get_ring_buffer(ring_info); + uint32_t ring_buffer_size = get_ring_buffer_size(ring_info); + + if (dest_len > ring_buffer_size - start_read_offset) { + /* wrap-around detected at the src */ + fragLen = ring_buffer_size - start_read_offset; + memcpy(dest, ring_buffer + start_read_offset, fragLen); + memcpy(dest + fragLen, ring_buffer, dest_len - fragLen); + } else { + memcpy(dest, ring_buffer + start_read_offset, dest_len); + } + + start_read_offset += dest_len; + start_read_offset %= ring_buffer_size; + + return (start_read_offset); +} + diff --git a/sys/dev/hyperv/vmbus/hv_vmbus_drv_freebsd.c b/sys/dev/hyperv/vmbus/hv_vmbus_drv_freebsd.c new file mode 100644 index 00000000000..08c61123b2d --- /dev/null +++ b/sys/dev/hyperv/vmbus/hv_vmbus_drv_freebsd.c @@ -0,0 +1,602 @@ +/*- + * Copyright (c) 2009-2012 Microsoft Corp. + * Copyright (c) 2012 NetApp Inc. + * Copyright (c) 2012 Citrix Inc. + * 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 unmodified, 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +/* + * VM Bus Driver Implementation + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include "hv_vmbus_priv.h" + + +#define VMBUS_IRQ 0x5 + +static struct intr_event *hv_msg_intr_event; +static struct intr_event *hv_event_intr_event; +static void *msg_swintr; +static void *event_swintr; +static device_t vmbus_devp; +static void *vmbus_cookiep; +static int vmbus_rid; +struct resource *intr_res; +static int vmbus_irq = VMBUS_IRQ; +static int vmbus_inited; +static hv_setup_args setup_args; /* only CPU 0 supported at this time */ + +/** + * @brief Software interrupt thread routine to handle channel messages from + * the hypervisor. + */ +static void +vmbus_msg_swintr(void *dummy) +{ + int cpu; + void* page_addr; + hv_vmbus_message* msg; + hv_vmbus_message* copied; + + cpu = PCPU_GET(cpuid); + page_addr = hv_vmbus_g_context.syn_ic_msg_page[cpu]; + msg = (hv_vmbus_message*) page_addr + HV_VMBUS_MESSAGE_SINT; + + for (;;) { + if (msg->header.message_type == HV_MESSAGE_TYPE_NONE) { + break; /* no message */ + } else { + copied = malloc(sizeof(hv_vmbus_message), + M_DEVBUF, M_NOWAIT); + KASSERT(copied != NULL, + ("Error VMBUS: malloc failed to allocate" + " hv_vmbus_message!")); + if (copied == NULL) + continue; + memcpy(copied, msg, sizeof(hv_vmbus_message)); + hv_queue_work_item(hv_vmbus_g_connection.work_queue, + hv_vmbus_on_channel_message, copied); + } + + msg->header.message_type = HV_MESSAGE_TYPE_NONE; + + /* + * Make sure the write to message_type (ie set to + * HV_MESSAGE_TYPE_NONE) happens before we read the + * message_pending and EOMing. Otherwise, the EOMing will + * not deliver any more messages + * since there is no empty slot + */ + wmb(); + + if (msg->header.message_flags.message_pending) { + /* + * This will cause message queue rescan to possibly + * deliver another msg from the hypervisor + */ + wrmsr(HV_X64_MSR_EOM, 0); + } + } +} + +/** + * @brief Interrupt filter routine for VMBUS. + * + * The purpose of this routine is to determine the type of VMBUS protocol + * message to process - an event or a channel message. + * As this is an interrupt filter routine, the function runs in a very + * restricted envinronment. From the manpage for bus_setup_intr(9) + * + * In this restricted environment, care must be taken to account for all + * races. A careful analysis of races should be done as well. It is gener- + * ally cheaper to take an extra interrupt, for example, than to protect + * variables with spinlocks. Read, modify, write cycles of hardware regis- + * ters need to be carefully analyzed if other threads are accessing the + * same registers. + */ +static int +hv_vmbus_isr(void *unused) +{ + int cpu; + hv_vmbus_message* msg; + hv_vmbus_synic_event_flags* event; + void* page_addr; + + cpu = PCPU_GET(cpuid); + /* (Temporary limit) */ + KASSERT(cpu == 0, ("hv_vmbus_isr: Interrupt on CPU other than zero")); + + /* + * The Windows team has advised that we check for events + * before checking for messages. This is the way they do it + * in Windows when running as a guest in Hyper-V + */ + + page_addr = hv_vmbus_g_context.syn_ic_event_page[cpu]; + event = (hv_vmbus_synic_event_flags*) + page_addr + HV_VMBUS_MESSAGE_SINT; + + /* Since we are a child, we only need to check bit 0 */ + if (synch_test_and_clear_bit(0, &event->flags32[0])) { + swi_sched(event_swintr, 0); + } + + /* Check if there are actual msgs to be process */ + page_addr = hv_vmbus_g_context.syn_ic_msg_page[cpu]; + msg = (hv_vmbus_message*) page_addr + HV_VMBUS_MESSAGE_SINT; + + if (msg->header.message_type != HV_MESSAGE_TYPE_NONE) { + swi_sched(msg_swintr, 0); + } + + return FILTER_HANDLED; +} + +static int +vmbus_read_ivar( + device_t dev, + device_t child, + int index, + uintptr_t* result) +{ + struct hv_device *child_dev_ctx = device_get_ivars(child); + + switch (index) { + + case HV_VMBUS_IVAR_TYPE: + *result = (uintptr_t) &child_dev_ctx->class_id; + return (0); + case HV_VMBUS_IVAR_INSTANCE: + *result = (uintptr_t) &child_dev_ctx->device_id; + return (0); + case HV_VMBUS_IVAR_DEVCTX: + *result = (uintptr_t) child_dev_ctx; + return (0); + case HV_VMBUS_IVAR_NODE: + *result = (uintptr_t) child_dev_ctx->device; + return (0); + } + return (ENOENT); +} + +static int +vmbus_write_ivar( + device_t dev, + device_t child, + int index, + uintptr_t value) +{ + switch (index) { + + case HV_VMBUS_IVAR_TYPE: + case HV_VMBUS_IVAR_INSTANCE: + case HV_VMBUS_IVAR_DEVCTX: + case HV_VMBUS_IVAR_NODE: + /* read-only */ + return (EINVAL); + } + return (ENOENT); +} + +struct hv_device* +hv_vmbus_child_device_create( + hv_guid type, + hv_guid instance, + hv_vmbus_channel* channel) +{ + hv_device* child_dev; + + /* + * Allocate the new child device + */ + child_dev = malloc(sizeof(hv_device), M_DEVBUF, + M_NOWAIT | M_ZERO); + KASSERT(child_dev != NULL, + ("Error VMBUS: malloc failed to allocate hv_device!")); + + if (child_dev == NULL) + return (NULL); + + child_dev->channel = channel; + memcpy(&child_dev->class_id, &type, sizeof(hv_guid)); + memcpy(&child_dev->device_id, &instance, sizeof(hv_guid)); + + return (child_dev); +} + +static void +print_dev_guid(struct hv_device *dev) +{ + int i; + unsigned char guid_name[100]; + for (i = 0; i < 32; i += 2) + sprintf(&guid_name[i], "%02x", dev->class_id.data[i / 2]); + if(bootverbose) + printf("VMBUS: Class ID: %s\n", guid_name); +} + +int +hv_vmbus_child_device_register(struct hv_device *child_dev) +{ + device_t child; + int ret = 0; + + print_dev_guid(child_dev); + + + child = device_add_child(vmbus_devp, NULL, -1); + child_dev->device = child; + device_set_ivars(child, child_dev); + + mtx_lock(&Giant); + ret = device_probe_and_attach(child); + mtx_unlock(&Giant); + + return (0); +} + +int +hv_vmbus_child_device_unregister(struct hv_device *child_dev) +{ + int ret = 0; + /* + * XXXKYS: Ensure that this is the opposite of + * device_add_child() + */ + mtx_lock(&Giant); + ret = device_delete_child(vmbus_devp, child_dev->device); + mtx_unlock(&Giant); + return(ret); +} + +static void vmbus_identify(driver_t *driver, device_t parent) { + BUS_ADD_CHILD(parent, 0, "vmbus", 0); + if (device_find_child(parent, "vmbus", 0) == NULL) { + BUS_ADD_CHILD(parent, 0, "vmbus", 0); + } +} + +static int +vmbus_probe(device_t dev) { + if(bootverbose) + device_printf(dev, "VMBUS: probe\n"); + + if (!hv_vmbus_query_hypervisor_presence()) + return (ENXIO); + + device_set_desc(dev, "Vmbus Devices"); + + return (0); +} + +/** + * @brief Main vmbus driver initialization routine. + * + * Here, we + * - initialize the vmbus driver context + * - setup various driver entry points + * - invoke the vmbus hv main init routine + * - get the irq resource + * - invoke the vmbus to add the vmbus root device + * - setup the vmbus root device + * - retrieve the channel offers + */ +static int +vmbus_bus_init(void) +{ + struct ioapic_intsrc { + struct intsrc io_intsrc; + u_int io_irq; + u_int io_intpin:8; + u_int io_vector:8; + u_int io_cpu:8; + u_int io_activehi:1; + u_int io_edgetrigger:1; + u_int io_masked:1; + int io_bus:4; + uint32_t io_lowreg; + }; + int i, ret; + unsigned int vector = 0; + struct intsrc *isrc; + struct ioapic_intsrc *intpin; + + if (vmbus_inited) + return (0); + + vmbus_inited = 1; + + ret = hv_vmbus_init(); + + if (ret) { + if(bootverbose) + printf("Error VMBUS: Hypervisor Initialization Failed!\n"); + return (ret); + } + + ret = swi_add(&hv_msg_intr_event, "hv_msg", vmbus_msg_swintr, + NULL, SWI_CLOCK, 0, &msg_swintr); + + if (ret) + goto cleanup; + + /* + * Message SW interrupt handler checks a per-CPU page and + * thus the thread needs to be bound to CPU-0 - which is where + * all interrupts are processed. + */ + ret = intr_event_bind(hv_msg_intr_event, 0); + + if (ret) + goto cleanup1; + + ret = swi_add(&hv_event_intr_event, "hv_event", hv_vmbus_on_events, + NULL, SWI_CLOCK, 0, &event_swintr); + + if (ret) + goto cleanup1; + + intr_res = bus_alloc_resource(vmbus_devp, + SYS_RES_IRQ, &vmbus_rid, vmbus_irq, vmbus_irq, 1, RF_ACTIVE); + + if (intr_res == NULL) { + ret = ENOMEM; /* XXXKYS: Need a better errno */ + goto cleanup2; + } + + /* + * Setup interrupt filter handler + */ + ret = bus_setup_intr(vmbus_devp, intr_res, + INTR_TYPE_NET | INTR_MPSAFE, hv_vmbus_isr, NULL, + NULL, &vmbus_cookiep); + + if (ret != 0) + goto cleanup3; + + ret = bus_bind_intr(vmbus_devp, intr_res, 0); + if (ret != 0) + goto cleanup4; + + isrc = intr_lookup_source(vmbus_irq); + if ((isrc == NULL) || (isrc->is_event == NULL)) { + ret = EINVAL; + goto cleanup4; + } + + /* vector = isrc->is_event->ie_vector; */ + intpin = (struct ioapic_intsrc *)isrc; + vector = intpin->io_vector; + + if(bootverbose) + printf("VMBUS: irq 0x%x vector 0x%x\n", vmbus_irq, vector); + + /** + * Notify the hypervisor of our irq. + */ + setup_args.vector = vector; + for(i = 0; i < 2; i++) { + setup_args.page_buffers[i] = + malloc(PAGE_SIZE, M_DEVBUF, M_NOWAIT | M_ZERO); + if (setup_args.page_buffers[i] == NULL) { + KASSERT(setup_args.page_buffers[i] != NULL, + ("Error VMBUS: malloc failed!")); + if (i > 0) + free(setup_args.page_buffers[0], M_DEVBUF); + goto cleanup4; + } + } + + /* only CPU #0 supported at this time */ + smp_rendezvous(NULL, hv_vmbus_synic_init, NULL, &setup_args); + + /* + * Connect to VMBus in the root partition + */ + ret = hv_vmbus_connect(); + + if (ret != 0) + goto cleanup4; + + hv_vmbus_request_channel_offers(); + return (ret); + + cleanup4: + + /* + * remove swi, bus and intr resource + */ + bus_teardown_intr(vmbus_devp, intr_res, vmbus_cookiep); + + cleanup3: + bus_release_resource(vmbus_devp, SYS_RES_IRQ, vmbus_rid, intr_res); + + cleanup2: + swi_remove(event_swintr); + + cleanup1: + swi_remove(msg_swintr); + + cleanup: + hv_vmbus_cleanup(); + + return (ret); +} + +static int +vmbus_attach(device_t dev) +{ + if(bootverbose) + device_printf(dev, "VMBUS: attach dev: %p\n", dev); + vmbus_devp = dev; + + /* + * If the system has already booted and thread + * scheduling is possible indicated by the global + * cold set to zero, we just call the driver + * initialization directly. + */ + if (!cold) + vmbus_bus_init(); + + return (0); +} + +static void +vmbus_init(void) +{ + /* + * If the system has already booted and thread + * scheduling is possible indicated by the global + * cold set to zero, we just call the driver + * initialization directly. + */ + if (!cold) + vmbus_bus_init(); +} + +static void +vmbus_bus_exit(void) +{ + int i; + + hv_vmbus_release_unattached_channels(); + hv_vmbus_disconnect(); + + smp_rendezvous(NULL, hv_vmbus_synic_cleanup, NULL, NULL); + + for(i = 0; i < 2; i++) { + if (setup_args.page_buffers[i] != 0) + free(setup_args.page_buffers[i], M_DEVBUF); + } + + hv_vmbus_cleanup(); + + /* remove swi, bus and intr resource */ + bus_teardown_intr(vmbus_devp, intr_res, vmbus_cookiep); + + bus_release_resource(vmbus_devp, SYS_RES_IRQ, vmbus_rid, intr_res); + + swi_remove(msg_swintr); + swi_remove(event_swintr); + + return; +} + +static void +vmbus_exit(void) +{ + vmbus_bus_exit(); +} + +static int +vmbus_detach(device_t dev) +{ + vmbus_exit(); + return (0); +} + +static void +vmbus_mod_load(void) +{ + if(bootverbose) + printf("VMBUS: load\n"); +} + +static void +vmbus_mod_unload(void) +{ + if(bootverbose) + printf("VMBUS: unload\n"); +} + +static int +vmbus_modevent(module_t mod, int what, void *arg) +{ + switch (what) { + + case MOD_LOAD: + vmbus_mod_load(); + break; + case MOD_UNLOAD: + vmbus_mod_unload(); + break; + } + + return (0); +} + +static device_method_t vmbus_methods[] = { + /** Device interface */ + DEVMETHOD(device_identify, vmbus_identify), + DEVMETHOD(device_probe, vmbus_probe), + DEVMETHOD(device_attach, vmbus_attach), + DEVMETHOD(device_detach, vmbus_detach), + DEVMETHOD(device_shutdown, bus_generic_shutdown), + DEVMETHOD(device_suspend, bus_generic_suspend), + DEVMETHOD(device_resume, bus_generic_resume), + + /** Bus interface */ + DEVMETHOD(bus_add_child, bus_generic_add_child), + DEVMETHOD(bus_print_child, bus_generic_print_child), + DEVMETHOD(bus_read_ivar, vmbus_read_ivar), + DEVMETHOD(bus_write_ivar, vmbus_write_ivar), + + { 0, 0 } }; + +static char driver_name[] = "vmbus"; +static driver_t vmbus_driver = { driver_name, vmbus_methods,0, }; + + +devclass_t vmbus_devclass; + +DRIVER_MODULE(vmbus, nexus, vmbus_driver, vmbus_devclass, vmbus_modevent, 0); +MODULE_VERSION(vmbus,1); + +/* TODO: We want to be earlier than SI_SUB_VFS */ +SYSINIT(vmb_init, SI_SUB_VFS, SI_ORDER_MIDDLE, vmbus_init, NULL); + diff --git a/sys/dev/hyperv/vmbus/hv_vmbus_priv.h b/sys/dev/hyperv/vmbus/hv_vmbus_priv.h new file mode 100644 index 00000000000..4607d507f27 --- /dev/null +++ b/sys/dev/hyperv/vmbus/hv_vmbus_priv.h @@ -0,0 +1,722 @@ +/*- + * Copyright (c) 2009-2012 Microsoft Corp. + * Copyright (c) 2012 NetApp Inc. + * Copyright (c) 2012 Citrix Inc. + * 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 unmodified, 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#ifndef __HYPERV_PRIV_H__ +#define __HYPERV_PRIV_H__ + +#include +#include +#include +#include + +#include + + +/* + * Status codes for hypervisor operations. + */ + +typedef uint16_t hv_vmbus_status; + +#define HV_MESSAGE_SIZE (256) +#define HV_MESSAGE_PAYLOAD_BYTE_COUNT (240) +#define HV_MESSAGE_PAYLOAD_QWORD_COUNT (30) +#define HV_ANY_VP (0xFFFFFFFF) + +/* + * Synthetic interrupt controller flag constants. + */ + +#define HV_EVENT_FLAGS_COUNT (256 * 8) +#define HV_EVENT_FLAGS_BYTE_COUNT (256) +#define HV_EVENT_FLAGS_DWORD_COUNT (256 / sizeof(uint32_t)) + +/* + * MessageId: HV_STATUS_INSUFFICIENT_BUFFERS + * MessageText: + * You did not supply enough message buffers to send a message. + */ + +#define HV_STATUS_INSUFFICIENT_BUFFERS ((uint16_t)0x0013) + +typedef void (*hv_vmbus_channel_callback)(void *context); + +typedef struct { + void* data; + uint32_t length; +} hv_vmbus_sg_buffer_list; + +typedef struct { + uint32_t current_interrupt_mask; + uint32_t current_read_index; + uint32_t current_write_index; + uint32_t bytes_avail_to_read; + uint32_t bytes_avail_to_write; +} hv_vmbus_ring_buffer_debug_info; + +typedef struct { + uint32_t rel_id; + hv_vmbus_channel_state state; + hv_guid interface_type; + hv_guid interface_instance; + uint32_t monitor_id; + uint32_t server_monitor_pending; + uint32_t server_monitor_latency; + uint32_t server_monitor_connection_id; + uint32_t client_monitor_pending; + uint32_t client_monitor_latency; + uint32_t client_monitor_connection_id; + hv_vmbus_ring_buffer_debug_info inbound; + hv_vmbus_ring_buffer_debug_info outbound; +} hv_vmbus_channel_debug_info; + +typedef union { + hv_vmbus_channel_version_supported version_supported; + hv_vmbus_channel_open_result open_result; + hv_vmbus_channel_gpadl_torndown gpadl_torndown; + hv_vmbus_channel_gpadl_created gpadl_created; + hv_vmbus_channel_version_response version_response; +} hv_vmbus_channel_msg_response; + +/* + * Represents each channel msg on the vmbus connection + * This is a variable-size data structure depending on + * the msg type itself + */ +typedef struct hv_vmbus_channel_msg_info { + /* + * Bookkeeping stuff + */ + TAILQ_ENTRY(hv_vmbus_channel_msg_info) msg_list_entry; + /* + * So far, this is only used to handle + * gpadl body message + */ + TAILQ_HEAD(, hv_vmbus_channel_msg_info) sub_msg_list_anchor; + /* + * Synchronize the request/response if + * needed. + * KYS: Use a semaphore for now. + * Not perf critical. + */ + struct sema wait_sema; + hv_vmbus_channel_msg_response response; + uint32_t message_size; + /** + * The channel message that goes out on + * the "wire". It will contain at + * minimum the + * hv_vmbus_channel_msg_header + * header. + */ + unsigned char msg[0]; +} hv_vmbus_channel_msg_info; + +/* + * The format must be the same as hv_vm_data_gpa_direct + */ +typedef struct hv_vmbus_channel_packet_page_buffer { + uint16_t type; + uint16_t data_offset8; + uint16_t length8; + uint16_t flags; + uint64_t transaction_id; + uint32_t reserved; + uint32_t range_count; + hv_vmbus_page_buffer range[HV_MAX_PAGE_BUFFER_COUNT]; +} __packed hv_vmbus_channel_packet_page_buffer; + +/* + * The format must be the same as hv_vm_data_gpa_direct + */ +typedef struct hv_vmbus_channel_packet_multipage_buffer { + uint16_t type; + uint16_t data_offset8; + uint16_t length8; + uint16_t flags; + uint64_t transaction_id; + uint32_t reserved; + uint32_t range_count; /* Always 1 in this case */ + hv_vmbus_multipage_buffer range; +} __packed hv_vmbus_channel_packet_multipage_buffer; + +enum { + HV_VMBUS_MESSAGE_CONNECTION_ID = 1, + HV_VMBUS_MESSAGE_PORT_ID = 1, + HV_VMBUS_EVENT_CONNECTION_ID = 2, + HV_VMBUS_EVENT_PORT_ID = 2, + HV_VMBUS_MONITOR_CONNECTION_ID = 3, + HV_VMBUS_MONITOR_PORT_ID = 3, + HV_VMBUS_MESSAGE_SINT = 2 +}; + +#define HV_PRESENT_BIT 0x80000000 + +#define HV_HYPERCALL_PARAM_ALIGN sizeof(uint64_t) + +/* + * Connection identifier type + */ +typedef union { + uint32_t as_uint32_t; + struct { + uint32_t id:24; + uint32_t reserved:8; + } u; + +} __packed hv_vmbus_connection_id; + +/* + * Definition of the hv_vmbus_signal_event hypercall input structure + */ +typedef struct { + hv_vmbus_connection_id connection_id; + uint16_t flag_number; + uint16_t rsvd_z; +} __packed hv_vmbus_input_signal_event; + +typedef struct { + uint64_t align8; + hv_vmbus_input_signal_event event; +} __packed hv_vmbus_input_signal_event_buffer; + +typedef struct { + uint64_t guest_id; + void* hypercall_page; + hv_bool_uint8_t syn_ic_initialized; + /* + * This is used as an input param to HV_CALL_SIGNAL_EVENT hypercall. + * The input param is immutable in our usage and + * must be dynamic mem (vs stack or global). + */ + hv_vmbus_input_signal_event_buffer *signal_event_buffer; + /* + * 8-bytes aligned of the buffer above + */ + hv_vmbus_input_signal_event *signal_event_param; + + hv_vmbus_handle syn_ic_msg_page[MAXCPU]; + hv_vmbus_handle syn_ic_event_page[MAXCPU]; +} hv_vmbus_context; + +/* + * Define hypervisor message types + */ +typedef enum { + + HV_MESSAGE_TYPE_NONE = 0x00000000, + + /* + * Memory access messages + */ + HV_MESSAGE_TYPE_UNMAPPED_GPA = 0x80000000, + HV_MESSAGE_TYPE_GPA_INTERCEPT = 0x80000001, + + /* + * Timer notification messages + */ + HV_MESSAGE_TIMER_EXPIRED = 0x80000010, + + /* + * Error messages + */ + HV_MESSAGE_TYPE_INVALID_VP_REGISTER_VALUE = 0x80000020, + HV_MESSAGE_TYPE_UNRECOVERABLE_EXCEPTION = 0x80000021, + HV_MESSAGE_TYPE_UNSUPPORTED_FEATURE = 0x80000022, + + /* + * Trace buffer complete messages + */ + HV_MESSAGE_TYPE_EVENT_LOG_BUFFER_COMPLETE = 0x80000040, + + /* + * Platform-specific processor intercept messages + */ + HV_MESSAGE_TYPE_X64_IO_PORT_INTERCEPT = 0x80010000, + HV_MESSAGE_TYPE_X64_MSR_INTERCEPT = 0x80010001, + HV_MESSAGE_TYPE_X64_CPU_INTERCEPT = 0x80010002, + HV_MESSAGE_TYPE_X64_EXCEPTION_INTERCEPT = 0x80010003, + HV_MESSAGE_TYPE_X64_APIC_EOI = 0x80010004, + HV_MESSAGE_TYPE_X64_LEGACY_FP_ERROR = 0x80010005 + +} hv_vmbus_msg_type; + +/* + * Define port identifier type + */ +typedef union _hv_vmbus_port_id { + uint32_t as_uint32_t; + struct { + uint32_t id:24; + uint32_t reserved:8; + } u ; +} hv_vmbus_port_id; + +/* + * Define synthetic interrupt controller message flag + */ +typedef union { + uint8_t as_uint8_t; + struct { + uint8_t message_pending:1; + uint8_t reserved:7; + }; +} hv_vmbus_msg_flags; + +typedef uint64_t hv_vmbus_partition_id; + +/* + * Define synthetic interrupt controller message header + */ +typedef struct { + hv_vmbus_msg_type message_type; + uint8_t payload_size; + hv_vmbus_msg_flags message_flags; + uint8_t reserved[2]; + union { + hv_vmbus_partition_id sender; + hv_vmbus_port_id port; + } u; +} hv_vmbus_msg_header; + +/* + * Define synthetic interrupt controller message format + */ +typedef struct { + hv_vmbus_msg_header header; + union { + uint64_t payload[HV_MESSAGE_PAYLOAD_QWORD_COUNT]; + } u ; +} hv_vmbus_message; + +/* + * Maximum channels is determined by the size of the interrupt + * page which is PAGE_SIZE. 1/2 of PAGE_SIZE is for + * send endpoint interrupt and the other is receive + * endpoint interrupt. + * + * Note: (PAGE_SIZE >> 1) << 3 allocates 16348 channels + */ +#define HV_MAX_NUM_CHANNELS (PAGE_SIZE >> 1) << 3 + +/* + * (The value here must be in multiple of 32) + */ +#define HV_MAX_NUM_CHANNELS_SUPPORTED 256 + +/* + * VM Bus connection states + */ +typedef enum { + HV_DISCONNECTED, + HV_CONNECTING, + HV_CONNECTED, + HV_DISCONNECTING +} hv_vmbus_connect_state; + +#define HV_MAX_SIZE_CHANNEL_MESSAGE HV_MESSAGE_PAYLOAD_BYTE_COUNT + + +typedef struct { + hv_vmbus_connect_state connect_state; + uint32_t next_gpadl_handle; + /** + * Represents channel interrupts. Each bit position + * represents a channel. + * When a channel sends an interrupt via VMBUS, it + * finds its bit in the send_interrupt_page, set it and + * calls Hv to generate a port event. The other end + * receives the port event and parse the + * recv_interrupt_page to see which bit is set + */ + void *interrupt_page; + void *send_interrupt_page; + void *recv_interrupt_page; + /* + * 2 pages - 1st page for parent->child + * notification and 2nd is child->parent + * notification + */ + void *monitor_pages; + TAILQ_HEAD(, hv_vmbus_channel_msg_info) channel_msg_anchor; + struct mtx channel_msg_lock; + /** + * List of channels + */ + TAILQ_HEAD(, hv_vmbus_channel) channel_anchor; + struct mtx channel_lock; + + hv_vmbus_handle work_queue; + struct sema control_sema; +} hv_vmbus_connection; + +/* + * Declare the MSR used to identify the guest OS + */ +#define HV_X64_MSR_GUEST_OS_ID 0x40000000 + +typedef union { + uint64_t as_uint64_t; + struct { + uint64_t build_number : 16; + uint64_t service_version : 8; /* Service Pack, etc. */ + uint64_t minor_version : 8; + uint64_t major_version : 8; + /* + * HV_GUEST_OS_MICROSOFT_IDS (If Vendor=MS) + * HV_GUEST_OS_VENDOR + */ + uint64_t os_id : 8; + uint64_t vendor_id : 16; + }; +} hv_vmbus_x64_msr_guest_os_id_contents; + +/* + * Declare the MSR used to setup pages used to communicate with the hypervisor + */ +#define HV_X64_MSR_HYPERCALL 0x40000001 + +typedef union { + uint64_t as_uint64_t; + struct { + uint64_t enable :1; + uint64_t reserved :11; + uint64_t guest_physical_address :52; + }; +} hv_vmbus_x64_msr_hypercall_contents; + +typedef union { + uint32_t as_uint32_t; + struct { + uint32_t group_enable :4; + uint32_t rsvd_z :28; + }; +} hv_vmbus_monitor_trigger_state; + +typedef union { + uint64_t as_uint64_t; + struct { + uint32_t pending; + uint32_t armed; + }; +} hv_vmbus_monitor_trigger_group; + +typedef struct { + hv_vmbus_connection_id connection_id; + uint16_t flag_number; + uint16_t rsvd_z; +} hv_vmbus_monitor_parameter; + +/* + * hv_vmbus_monitor_page Layout + * ------------------------------------------------------ + * | 0 | trigger_state (4 bytes) | Rsvd1 (4 bytes) | + * | 8 | trigger_group[0] | + * | 10 | trigger_group[1] | + * | 18 | trigger_group[2] | + * | 20 | trigger_group[3] | + * | 28 | Rsvd2[0] | + * | 30 | Rsvd2[1] | + * | 38 | Rsvd2[2] | + * | 40 | next_check_time[0][0] | next_check_time[0][1] | + * | ... | + * | 240 | latency[0][0..3] | + * | 340 | Rsvz3[0] | + * | 440 | parameter[0][0] | + * | 448 | parameter[0][1] | + * | ... | + * | 840 | Rsvd4[0] | + * ------------------------------------------------------ + */ + +typedef struct { + hv_vmbus_monitor_trigger_state trigger_state; + uint32_t rsvd_z1; + + hv_vmbus_monitor_trigger_group trigger_group[4]; + uint64_t rsvd_z2[3]; + + int32_t next_check_time[4][32]; + + uint16_t latency[4][32]; + uint64_t rsvd_z3[32]; + + hv_vmbus_monitor_parameter parameter[4][32]; + + uint8_t rsvd_z4[1984]; +} hv_vmbus_monitor_page; + +/* + * The below CPUID leaves are present if VersionAndFeatures.HypervisorPresent + * is set by CPUID(HV_CPU_ID_FUNCTION_VERSION_AND_FEATURES). + */ +typedef enum { + HV_CPU_ID_FUNCTION_VERSION_AND_FEATURES = 0x00000001, + HV_CPU_ID_FUNCTION_HV_VENDOR_AND_MAX_FUNCTION = 0x40000000, + HV_CPU_ID_FUNCTION_HV_INTERFACE = 0x40000001, + /* + * The remaining functions depend on the value + * of hv_cpu_id_function_interface + */ + HV_CPU_ID_FUNCTION_MS_HV_VERSION = 0x40000002, + HV_CPU_ID_FUNCTION_MS_HV_FEATURES = 0x40000003, + HV_CPU_ID_FUNCTION_MS_HV_ENLIGHTENMENT_INFORMATION = 0x40000004, + HV_CPU_ID_FUNCTION_MS_HV_IMPLEMENTATION_LIMITS = 0x40000005 + +} hv_vmbus_cpuid_function; + +/* + * Define the format of the SIMP register + */ +typedef union { + uint64_t as_uint64_t; + struct { + uint64_t simp_enabled : 1; + uint64_t preserved : 11; + uint64_t base_simp_gpa : 52; + }; +} hv_vmbus_synic_simp; + +/* + * Define the format of the SIEFP register + */ +typedef union { + uint64_t as_uint64_t; + struct { + uint64_t siefp_enabled : 1; + uint64_t preserved : 11; + uint64_t base_siefp_gpa : 52; + }; +} hv_vmbus_synic_siefp; + +/* + * Define synthetic interrupt source + */ +typedef union { + uint64_t as_uint64_t; + struct { + uint64_t vector : 8; + uint64_t reserved1 : 8; + uint64_t masked : 1; + uint64_t auto_eoi : 1; + uint64_t reserved2 : 46; + }; +} hv_vmbus_synic_sint; + +/* + * Define syn_ic control register + */ +typedef union _hv_vmbus_synic_scontrol { + uint64_t as_uint64_t; + struct { + uint64_t enable : 1; + uint64_t reserved : 63; + }; +} hv_vmbus_synic_scontrol; + +/* + * Define the hv_vmbus_post_message hypercall input structure + */ +typedef struct { + hv_vmbus_connection_id connection_id; + uint32_t reserved; + hv_vmbus_msg_type message_type; + uint32_t payload_size; + uint64_t payload[HV_MESSAGE_PAYLOAD_QWORD_COUNT]; +} hv_vmbus_input_post_message; + +/* + * Define the synthetic interrupt controller event flags format + */ +typedef union { + uint8_t flags8[HV_EVENT_FLAGS_BYTE_COUNT]; + uint32_t flags32[HV_EVENT_FLAGS_DWORD_COUNT]; +} hv_vmbus_synic_event_flags; + + +/* + * Define synthetic interrupt controller model specific registers + */ +#define HV_X64_MSR_SCONTROL (0x40000080) +#define HV_X64_MSR_SVERSION (0x40000081) +#define HV_X64_MSR_SIEFP (0x40000082) +#define HV_X64_MSR_SIMP (0x40000083) +#define HV_X64_MSR_EOM (0x40000084) + +#define HV_X64_MSR_SINT0 (0x40000090) +#define HV_X64_MSR_SINT1 (0x40000091) +#define HV_X64_MSR_SINT2 (0x40000092) +#define HV_X64_MSR_SINT3 (0x40000093) +#define HV_X64_MSR_SINT4 (0x40000094) +#define HV_X64_MSR_SINT5 (0x40000095) +#define HV_X64_MSR_SINT6 (0x40000096) +#define HV_X64_MSR_SINT7 (0x40000097) +#define HV_X64_MSR_SINT8 (0x40000098) +#define HV_X64_MSR_SINT9 (0x40000099) +#define HV_X64_MSR_SINT10 (0x4000009A) +#define HV_X64_MSR_SINT11 (0x4000009B) +#define HV_X64_MSR_SINT12 (0x4000009C) +#define HV_X64_MSR_SINT13 (0x4000009D) +#define HV_X64_MSR_SINT14 (0x4000009E) +#define HV_X64_MSR_SINT15 (0x4000009F) + +/* + * Declare the various hypercall operations + */ +typedef enum { + HV_CALL_POST_MESSAGE = 0x005c, + HV_CALL_SIGNAL_EVENT = 0x005d, +} hv_vmbus_call_code; + +/** + * Global variables + */ + +extern hv_vmbus_context hv_vmbus_g_context; +extern hv_vmbus_connection hv_vmbus_g_connection; + + +/* + * Private, VM Bus functions + */ + +int hv_vmbus_ring_buffer_init( + hv_vmbus_ring_buffer_info *ring_info, + void *buffer, + uint32_t buffer_len); + +void hv_ring_buffer_cleanup( + hv_vmbus_ring_buffer_info *ring_info); + +int hv_ring_buffer_write( + hv_vmbus_ring_buffer_info *ring_info, + hv_vmbus_sg_buffer_list sg_buffers[], + uint32_t sg_buff_count); + +int hv_ring_buffer_peek( + hv_vmbus_ring_buffer_info *ring_info, + void *buffer, + uint32_t buffer_len); + +int hv_ring_buffer_read( + hv_vmbus_ring_buffer_info *ring_info, + void *buffer, + uint32_t buffer_len, + uint32_t offset); + +uint32_t hv_vmbus_get_ring_buffer_interrupt_mask( + hv_vmbus_ring_buffer_info *ring_info); + +void hv_vmbus_dump_ring_info( + hv_vmbus_ring_buffer_info *ring_info, + char *prefix); + +hv_vmbus_channel* hv_vmbus_allocate_channel(void); +void hv_vmbus_free_vmbus_channel(hv_vmbus_channel *channel); +void hv_vmbus_on_channel_message(void *context); +int hv_vmbus_request_channel_offers(void); +void hv_vmbus_release_unattached_channels(void); +int hv_vmbus_init(void); +void hv_vmbus_cleanup(void); + +uint16_t hv_vmbus_post_msg_via_msg_ipc( + hv_vmbus_connection_id connection_id, + hv_vmbus_msg_type message_type, + void *payload, + size_t payload_size); + +uint16_t hv_vmbus_signal_event(void); +void hv_vmbus_synic_init(void *irq_arg); +void hv_vmbus_synic_cleanup(void *arg); +int hv_vmbus_query_hypervisor_presence(void); + +struct hv_device* hv_vmbus_child_device_create( + hv_guid device_type, + hv_guid device_instance, + hv_vmbus_channel *channel); + +int hv_vmbus_child_device_register( + struct hv_device *child_dev); +int hv_vmbus_child_device_unregister( + struct hv_device *child_dev); +hv_vmbus_channel* hv_vmbus_get_channel_from_rel_id(uint32_t rel_id); + +/** + * Connection interfaces + */ +int hv_vmbus_connect(void); +int hv_vmbus_disconnect(void); +int hv_vmbus_post_message(void *buffer, size_t buf_size); +int hv_vmbus_set_event(uint32_t child_rel_id); +void hv_vmbus_on_events(void *); + + +/* + * The guest OS needs to register the guest ID with the hypervisor. + * The guest ID is a 64 bit entity and the structure of this ID is + * specified in the Hyper-V specification: + * + * http://msdn.microsoft.com/en-us/library/windows/ + * hardware/ff542653%28v=vs.85%29.aspx + * + * While the current guideline does not specify how FreeBSD guest ID(s) + * need to be generated, our plan is to publish the guidelines for + * FreeBSD and other guest operating systems that currently are hosted + * on Hyper-V. The implementation here conforms to this yet + * unpublished guidelines. + * + * Bit(s) + * 63 - Indicates if the OS is Open Source or not; 1 is Open Source + * 62:56 - Os Type; Linux is 0x100, FreeBSD is 0x200 + * 55:48 - Distro specific identification + * 47:16 - FreeBSD kernel version number + * 15:0 - Distro specific identification + * + */ + +#define HV_FREEBSD_VENDOR_ID 0x8200 +#define HV_FREEBSD_GUEST_ID hv_generate_guest_id(0,0) + +static inline uint64_t hv_generate_guest_id( + uint8_t distro_id_part1, + uint16_t distro_id_part2) +{ + uint64_t guest_id; + guest_id = (((uint64_t)HV_FREEBSD_VENDOR_ID) << 48); + guest_id |= (((uint64_t)(distro_id_part1)) << 48); + guest_id |= (((uint64_t)(__FreeBSD_version)) << 16); /* in param.h */ + guest_id |= ((uint64_t)(distro_id_part2)); + return guest_id; +} + +typedef struct { + unsigned int vector; + void *page_buffers[2]; +} hv_setup_args; + +#endif /* __HYPERV_PRIV_H__ */ diff --git a/sys/dev/if_ndis/if_ndis.c b/sys/dev/if_ndis/if_ndis.c index a62b9138abc..01388c90cd6 100644 --- a/sys/dev/if_ndis/if_ndis.c +++ b/sys/dev/if_ndis/if_ndis.c @@ -1401,7 +1401,7 @@ ndis_rxeof(adapter, packets, pktcnt) p = packets[i]; if (p->np_oob.npo_status == NDIS_STATUS_SUCCESS) { p->np_refcnt++; - ndis_return_packet(p, block); + (void)ndis_return_packet(NULL ,p, block); } } return; @@ -1414,7 +1414,7 @@ ndis_rxeof(adapter, packets, pktcnt) if (ndis_ptom(&m0, p)) { device_printf(sc->ndis_dev, "ptom failed\n"); if (p->np_oob.npo_status == NDIS_STATUS_SUCCESS) - ndis_return_packet(p, block); + (void)ndis_return_packet(NULL, p, block); } else { #ifdef notdef if (p->np_oob.npo_status == NDIS_STATUS_RESOURCES) { diff --git a/sys/dev/iicbus/if_ic.c b/sys/dev/iicbus/if_ic.c index 6579663d956..530cf648e7a 100644 --- a/sys/dev/iicbus/if_ic.c +++ b/sys/dev/iicbus/if_ic.c @@ -204,7 +204,6 @@ icioctl(struct ifnet *ifp, u_long cmd, caddr_t data) switch (cmd) { - case SIOCSIFDSTADDR: case SIOCAIFADDR: case SIOCSIFADDR: if (ifa->ifa_addr->sa_family != AF_INET) diff --git a/sys/dev/iir/iir.c b/sys/dev/iir/iir.c index 4c907f01494..684fce3298e 100644 --- a/sys/dev/iir/iir.c +++ b/sys/dev/iir/iir.c @@ -399,7 +399,7 @@ iir_init(struct gdt_softc *gdt) gdt->oem_name[7]='\0'; } else { /* Old method, based on PCI ID */ - if (gdt->sc_vendor == INTEL_VENDOR_ID) + if (gdt->sc_vendor == INTEL_VENDOR_ID_IIR) strcpy(gdt->oem_name,"Intel "); else strcpy(gdt->oem_name,"ICP "); @@ -1374,7 +1374,7 @@ iir_action( struct cam_sim *sim, union ccb *ccb ) (bus == gdt->sc_virt_bus ? 127 : gdt->sc_bus_id[bus]); cpi->base_transfer_speed = 3300; strncpy(cpi->sim_vid, "FreeBSD", SIM_IDLEN); - if (gdt->sc_vendor == INTEL_VENDOR_ID) + if (gdt->sc_vendor == INTEL_VENDOR_ID_IIR) strncpy(cpi->hba_vid, "Intel Corp.", HBA_IDLEN); else strncpy(cpi->hba_vid, "ICP vortex ", HBA_IDLEN); diff --git a/sys/dev/iir/iir.h b/sys/dev/iir/iir.h index dca493d6b84..dbae7d2a80f 100644 --- a/sys/dev/iir/iir.h +++ b/sys/dev/iir/iir.h @@ -63,7 +63,7 @@ #define GDT_DEVICE_ID_MAX 0x2ff #define GDT_DEVICE_ID_NEWRX 0x300 -#define INTEL_VENDOR_ID 0x8086 +#define INTEL_VENDOR_ID_IIR 0x8086 #define INTEL_DEVICE_ID_IIR 0x600 #define GDT_MAXBUS 6 /* XXX Why not 5? */ diff --git a/sys/dev/iir/iir_ctrl.c b/sys/dev/iir/iir_ctrl.c index 5f9f3d597ce..94b18ddffae 100644 --- a/sys/dev/iir/iir_ctrl.c +++ b/sys/dev/iir/iir_ctrl.c @@ -273,7 +273,7 @@ iir_ioctl(struct cdev *dev, u_long cmd, caddr_t cmdarg, int flags, struct thread return (ENXIO); /* only RP controllers */ p->ext_type = 0x6000 | gdt->sc_device; - if (gdt->sc_vendor == INTEL_VENDOR_ID) { + if (gdt->sc_vendor == INTEL_VENDOR_ID_IIR) { p->oem_id = OEM_ID_INTEL; p->type = 0xfd; /* new -> subdevice into ext_type */ diff --git a/sys/dev/iir/iir_pci.c b/sys/dev/iir/iir_pci.c index 7baee86711b..ec54d49b4cb 100644 --- a/sys/dev/iir/iir_pci.c +++ b/sys/dev/iir/iir_pci.c @@ -163,7 +163,7 @@ MODULE_DEPEND(iir, cam, 1, 1, 1); static int iir_pci_probe(device_t dev) { - if (pci_get_vendor(dev) == INTEL_VENDOR_ID && + if (pci_get_vendor(dev) == INTEL_VENDOR_ID_IIR && pci_get_device(dev) == INTEL_DEVICE_ID_IIR) { device_set_desc(dev, "Intel Integrated RAID Controller"); return (BUS_PROBE_DEFAULT); diff --git a/sys/dev/ipmi/ipmi_linux.c b/sys/dev/ipmi/ipmi_linux.c index 430bd085894..b6b38f22b16 100644 --- a/sys/dev/ipmi/ipmi_linux.c +++ b/sys/dev/ipmi/ipmi_linux.c @@ -89,11 +89,13 @@ MODULE_DEPEND(ipmi_linux, linux, 1, 1, 1); static int ipmi_linux_ioctl(struct thread *td, struct linux_ioctl_args *args) { + cap_rights_t rights; struct file *fp; u_long cmd; int error; - if ((error = fget(td, args->fd, CAP_IOCTL, &fp)) != 0) + error = fget(td, args->fd, cap_rights_init(&rights, CAP_IOCTL), &fp); + if (error != 0) return (error); cmd = args->cmd; diff --git a/sys/dev/ips/ips_pci.c b/sys/dev/ips/ips_pci.c index d39f4acbf65..df917ed3729 100644 --- a/sys/dev/ips/ips_pci.c +++ b/sys/dev/ips/ips_pci.c @@ -59,7 +59,6 @@ static int ips_pci_probe(device_t dev) static int ips_pci_attach(device_t dev) { - u_int32_t command; ips_softc_t *sc; @@ -95,22 +94,18 @@ static int ips_pci_attach(device_t dev) } else goto error; /* make sure busmastering is on */ - command = pci_read_config(dev, PCIR_COMMAND, 1); - command |= PCIM_CMD_BUSMASTEREN; - pci_write_config(dev, PCIR_COMMAND, command, 1); + pci_enable_busmaster(dev); /* seting up io space */ sc->iores = NULL; - if(command & PCIM_CMD_MEMEN){ - PRINTF(10, "trying MEMIO\n"); - if(pci_get_device(dev) == IPS_COPPERHEAD_DEVICE_ID) - sc->rid = PCIR_BAR(1); - else - sc->rid = PCIR_BAR(0); - sc->iotype = SYS_RES_MEMORY; - sc->iores = bus_alloc_resource_any(dev, sc->iotype, - &sc->rid, RF_ACTIVE); - } - if(!sc->iores && command & PCIM_CMD_PORTEN){ + PRINTF(10, "trying MEMIO\n"); + if(pci_get_device(dev) == IPS_COPPERHEAD_DEVICE_ID) + sc->rid = PCIR_BAR(1); + else + sc->rid = PCIR_BAR(0); + sc->iotype = SYS_RES_MEMORY; + sc->iores = bus_alloc_resource_any(dev, sc->iotype, &sc->rid, + RF_ACTIVE); + if(!sc->iores){ PRINTF(10, "trying PORTIO\n"); sc->rid = PCIR_BAR(0); sc->iotype = SYS_RES_IOPORT; diff --git a/sys/dev/iscsi/icl.c b/sys/dev/iscsi/icl.c new file mode 100644 index 00000000000..eb9cf4efcd3 --- /dev/null +++ b/sys/dev/iscsi/icl.c @@ -0,0 +1,1292 @@ +/*- + * Copyright (c) 2012 The FreeBSD Foundation + * All rights reserved. + * + * This software was developed by Edward Tomasz Napierala under sponsorship + * from the FreeBSD Foundation. + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + * $FreeBSD$ + */ + +/* + * iSCSI Common Layer. It's used by both the initiator and target to send + * and receive iSCSI PDUs. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "icl.h" +#include "iscsi_proto.h" + +SYSCTL_NODE(_kern, OID_AUTO, icl, CTLFLAG_RD, 0, "iSCSI Common Layer"); +static int debug = 1; +TUNABLE_INT("kern.icl.debug", &debug); +SYSCTL_INT(_kern_icl, OID_AUTO, debug, CTLFLAG_RW, + &debug, 1, "Enable debug messages"); +static int partial_receive_len = 1 * 1024; /* XXX: More? */ +TUNABLE_INT("kern.icl.partial_receive_len", &partial_receive_len); +SYSCTL_INT(_kern_icl, OID_AUTO, partial_receive_len, CTLFLAG_RW, + &partial_receive_len, 1 * 1024, "Minimum read size for partially received " + "data segment"); + +static uma_zone_t icl_conn_zone; +static uma_zone_t icl_pdu_zone; + +static volatile u_int icl_ncons; + +#define ICL_DEBUG(X, ...) \ + if (debug > 1) { \ + printf("%s: " X "\n", __func__, ## __VA_ARGS__);\ + } while (0) + +#define ICL_WARN(X, ...) \ + if (debug > 0) { \ + printf("WARNING: %s: " X "\n", \ + __func__, ## __VA_ARGS__); \ + } while (0) + +#define ICL_CONN_LOCK(X) mtx_lock(&X->ic_lock) +#define ICL_CONN_UNLOCK(X) mtx_unlock(&X->ic_lock) +#define ICL_CONN_LOCK_ASSERT(X) mtx_assert(&X->ic_lock, MA_OWNED) + +static void +icl_conn_fail(struct icl_conn *ic) +{ + if (ic->ic_socket == NULL) + return; + + /* + * XXX + */ + ic->ic_socket->so_error = EDOOFUS; + (ic->ic_error)(ic); +} + +static struct mbuf * +icl_conn_receive(struct icl_conn *ic, size_t len) +{ + struct uio uio; + struct socket *so; + struct mbuf *m; + int error, flags; + + so = ic->ic_socket; + + memset(&uio, 0, sizeof(uio)); + uio.uio_resid = len; + + flags = MSG_DONTWAIT; + error = soreceive(so, NULL, &uio, &m, NULL, &flags); + if (error != 0) { + ICL_DEBUG("soreceive error %d", error); + return (NULL); + } + if (uio.uio_resid != 0) { + m_freem(m); + ICL_DEBUG("short read"); + return (NULL); + } + + return (m); +} + +static struct icl_pdu * +icl_pdu_new(struct icl_conn *ic, int flags) +{ + struct icl_pdu *ip; + + refcount_acquire(&ic->ic_outstanding_pdus); + ip = uma_zalloc(icl_pdu_zone, flags | M_ZERO); + if (ip == NULL) { + ICL_WARN("failed to allocate %zd bytes", sizeof(*ip)); + refcount_release(&ic->ic_outstanding_pdus); + return (NULL); + } + + ip->ip_conn = ic; + + return (ip); +} + +void +icl_pdu_free(struct icl_pdu *ip) +{ + struct icl_conn *ic; + + ic = ip->ip_conn; + + m_freem(ip->ip_bhs_mbuf); + m_freem(ip->ip_ahs_mbuf); + m_freem(ip->ip_data_mbuf); + uma_zfree(icl_pdu_zone, ip); + refcount_release(&ic->ic_outstanding_pdus); +} + +/* + * Allocate icl_pdu with empty BHS to fill up by the caller. + */ +struct icl_pdu * +icl_pdu_new_bhs(struct icl_conn *ic, int flags) +{ + struct icl_pdu *ip; + + ip = icl_pdu_new(ic, flags); + if (ip == NULL) + return (NULL); + + ip->ip_bhs_mbuf = m_getm2(NULL, sizeof(struct iscsi_bhs), + flags, MT_DATA, M_PKTHDR); + if (ip->ip_bhs_mbuf == NULL) { + ICL_WARN("failed to allocate %zd bytes", sizeof(*ip)); + icl_pdu_free(ip); + return (NULL); + } + ip->ip_bhs = mtod(ip->ip_bhs_mbuf, struct iscsi_bhs *); + memset(ip->ip_bhs, 0, sizeof(struct iscsi_bhs)); + ip->ip_bhs_mbuf->m_len = sizeof(struct iscsi_bhs); + + return (ip); +} + +static int +icl_pdu_ahs_length(const struct icl_pdu *request) +{ + + return (request->ip_bhs->bhs_total_ahs_len * 4); +} + +size_t +icl_pdu_data_segment_length(const struct icl_pdu *request) +{ + uint32_t len = 0; + + len += request->ip_bhs->bhs_data_segment_len[0]; + len <<= 8; + len += request->ip_bhs->bhs_data_segment_len[1]; + len <<= 8; + len += request->ip_bhs->bhs_data_segment_len[2]; + + return (len); +} + +static void +icl_pdu_set_data_segment_length(struct icl_pdu *response, uint32_t len) +{ + + response->ip_bhs->bhs_data_segment_len[2] = len; + response->ip_bhs->bhs_data_segment_len[1] = len >> 8; + response->ip_bhs->bhs_data_segment_len[0] = len >> 16; +} + +static size_t +icl_pdu_padding(const struct icl_pdu *ip) +{ + + if ((ip->ip_data_len % 4) != 0) + return (4 - (ip->ip_data_len % 4)); + + return (0); +} + +static size_t +icl_pdu_size(const struct icl_pdu *response) +{ + size_t len; + + KASSERT(response->ip_ahs_len == 0, ("responding with AHS")); + + len = sizeof(struct iscsi_bhs) + response->ip_data_len + + icl_pdu_padding(response); + if (response->ip_conn->ic_header_crc32c) + len += ISCSI_HEADER_DIGEST_SIZE; + if (response->ip_conn->ic_data_crc32c) + len += ISCSI_DATA_DIGEST_SIZE; + + return (len); +} + +static int +icl_pdu_receive_bhs(struct icl_pdu *request, size_t *availablep) +{ + struct mbuf *m; + + m = icl_conn_receive(request->ip_conn, sizeof(struct iscsi_bhs)); + if (m == NULL) { + ICL_DEBUG("failed to receive BHS"); + return (-1); + } + + request->ip_bhs_mbuf = m_pullup(m, sizeof(struct iscsi_bhs)); + if (request->ip_bhs_mbuf == NULL) { + ICL_WARN("m_pullup failed"); + return (-1); + } + request->ip_bhs = mtod(request->ip_bhs_mbuf, struct iscsi_bhs *); + + /* + * XXX: For architectures with strict alignment requirements + * we may need to allocate ip_bhs and copy the data into it. + * For some reason, though, not doing this doesn't seem + * to cause problems; tested on sparc64. + */ + + *availablep -= sizeof(struct iscsi_bhs); + return (0); +} + +static int +icl_pdu_receive_ahs(struct icl_pdu *request, size_t *availablep) +{ + + request->ip_ahs_len = icl_pdu_ahs_length(request); + if (request->ip_ahs_len == 0) + return (0); + + request->ip_ahs_mbuf = icl_conn_receive(request->ip_conn, + request->ip_ahs_len); + if (request->ip_ahs_mbuf == NULL) { + ICL_DEBUG("failed to receive AHS"); + return (-1); + } + + *availablep -= request->ip_ahs_len; + return (0); +} + +static uint32_t +icl_mbuf_to_crc32c(const struct mbuf *m0) +{ + uint32_t digest = 0xffffffff; + const struct mbuf *m; + + for (m = m0; m != NULL; m = m->m_next) + digest = calculate_crc32c(digest, + mtod(m, const void *), m->m_len); + + digest = digest ^ 0xffffffff; + + return (digest); +} + +static int +icl_pdu_check_header_digest(struct icl_pdu *request, size_t *availablep) +{ + struct mbuf *m; + uint32_t received_digest, valid_digest; + + if (request->ip_conn->ic_header_crc32c == false) + return (0); + + m = icl_conn_receive(request->ip_conn, ISCSI_HEADER_DIGEST_SIZE); + if (m == NULL) { + ICL_DEBUG("failed to receive header digest"); + return (-1); + } + + CTASSERT(sizeof(received_digest) == ISCSI_HEADER_DIGEST_SIZE); + memcpy(&received_digest, mtod(m, void *), ISCSI_HEADER_DIGEST_SIZE); + m_freem(m); + + *availablep -= ISCSI_HEADER_DIGEST_SIZE; + + /* + * XXX: Handle AHS. + */ + valid_digest = icl_mbuf_to_crc32c(request->ip_bhs_mbuf); + if (received_digest != valid_digest) { + ICL_WARN("header digest check failed; got 0x%x, " + "should be 0x%x", received_digest, valid_digest); + return (-1); + } + + return (0); +} + +/* + * Return the number of bytes that should be waiting in the receive socket + * before icl_pdu_receive_data_segment() gets called. + */ +static size_t +icl_pdu_data_segment_receive_len(const struct icl_pdu *request) +{ + size_t len; + + len = icl_pdu_data_segment_length(request); + if (len == 0) + return (0); + + /* + * Account for the parts of data segment already read from + * the socket buffer. + */ + KASSERT(len > request->ip_data_len, ("len <= request->ip_data_len")); + len -= request->ip_data_len; + + /* + * Don't always wait for the full data segment to be delivered + * to the socket; this might badly affect performance due to + * TCP window scaling. + */ + if (len > partial_receive_len) { +#if 0 + ICL_DEBUG("need %zd bytes of data, limiting to %zd", + len, partial_receive_len)); +#endif + len = partial_receive_len; + + return (len); + } + + /* + * Account for padding. Note that due to the way code is written, + * the icl_pdu_receive_data_segment() must always receive padding + * along with the last part of data segment, because it would be + * impossible to tell whether we've already received the full data + * segment including padding, or without it. + */ + if ((len % 4) != 0) + len += 4 - (len % 4); + +#if 0 + ICL_DEBUG("need %zd bytes of data", len)); +#endif + + return (len); +} + +static int +icl_pdu_receive_data_segment(struct icl_pdu *request, + size_t *availablep, bool *more_neededp) +{ + struct icl_conn *ic; + size_t len, padding = 0; + struct mbuf *m; + + ic = request->ip_conn; + + *more_neededp = false; + ic->ic_receive_len = 0; + + len = icl_pdu_data_segment_length(request); + if (len == 0) + return (0); + + if ((len % 4) != 0) + padding = 4 - (len % 4); + + /* + * Account for already received parts of data segment. + */ + KASSERT(len > request->ip_data_len, ("len <= request->ip_data_len")); + len -= request->ip_data_len; + + if (len + padding > *availablep) { + /* + * Not enough data in the socket buffer. Receive as much + * as we can. Don't receive padding, since, obviously, it's + * not the end of data segment yet. + */ +#if 0 + ICL_DEBUG("limited from %zd to %zd", + len + padding, *availablep - padding)); +#endif + len = *availablep - padding; + *more_neededp = true; + padding = 0; + } + + /* + * Must not try to receive padding without at least one byte + * of actual data segment. + */ + if (len > 0) { + m = icl_conn_receive(request->ip_conn, len + padding); + if (m == NULL) { + ICL_DEBUG("failed to receive data segment"); + return (-1); + } + + if (request->ip_data_mbuf == NULL) + request->ip_data_mbuf = m; + else + m_cat(request->ip_data_mbuf, m); + + request->ip_data_len += len; + *availablep -= len + padding; + } else + ICL_DEBUG("len 0"); + + if (*more_neededp) + ic->ic_receive_len = + icl_pdu_data_segment_receive_len(request); + + return (0); +} + +static int +icl_pdu_check_data_digest(struct icl_pdu *request, size_t *availablep) +{ + struct mbuf *m; + uint32_t received_digest, valid_digest; + + if (request->ip_conn->ic_data_crc32c == false) + return (0); + + if (request->ip_data_len == 0) + return (0); + + m = icl_conn_receive(request->ip_conn, ISCSI_DATA_DIGEST_SIZE); + if (m == NULL) { + ICL_DEBUG("failed to receive data digest"); + return (-1); + } + + CTASSERT(sizeof(received_digest) == ISCSI_DATA_DIGEST_SIZE); + memcpy(&received_digest, mtod(m, void *), ISCSI_DATA_DIGEST_SIZE); + m_freem(m); + + *availablep -= ISCSI_DATA_DIGEST_SIZE; + + /* + * Note that ip_data_mbuf also contains padding; since digest + * calculation is supposed to include that, we iterate over + * the entire ip_data_mbuf chain, not just ip_data_len bytes of it. + */ + valid_digest = icl_mbuf_to_crc32c(request->ip_data_mbuf); + if (received_digest != valid_digest) { + ICL_WARN("data digest check failed; got 0x%x, " + "should be 0x%x", received_digest, valid_digest); + return (-1); + } + + return (0); +} + +/* + * Somewhat contrary to the name, this attempts to receive only one + * "part" of PDU at a time; call it repeatedly until it returns non-NULL. + */ +static struct icl_pdu * +icl_conn_receive_pdu(struct icl_conn *ic, size_t *availablep) +{ + struct icl_pdu *request; + struct socket *so; + size_t len; + int error; + bool more_needed; + + so = ic->ic_socket; + + if (ic->ic_receive_state == ICL_CONN_STATE_BHS) { + KASSERT(ic->ic_receive_pdu == NULL, + ("ic->ic_receive_pdu != NULL")); + request = icl_pdu_new(ic, M_NOWAIT); + if (request == NULL) { + ICL_DEBUG("failed to allocate PDU; " + "dropping connection"); + icl_conn_fail(ic); + return (NULL); + } + ic->ic_receive_pdu = request; + } else { + KASSERT(ic->ic_receive_pdu != NULL, + ("ic->ic_receive_pdu == NULL")); + request = ic->ic_receive_pdu; + } + + if (*availablep < ic->ic_receive_len) { +#if 0 + ICL_DEBUG("not enough data; need %zd, " + "have %zd", ic->ic_receive_len, *availablep); +#endif + return (NULL); + } + + switch (ic->ic_receive_state) { + case ICL_CONN_STATE_BHS: + //ICL_DEBUG("receiving BHS"); + error = icl_pdu_receive_bhs(request, availablep); + if (error != 0) { + ICL_DEBUG("failed to receive BHS; " + "dropping connection"); + break; + } + + /* + * We don't enforce any limit for AHS length; + * its length is stored in 8 bit field. + */ + + len = icl_pdu_data_segment_length(request); + if (len > ic->ic_max_data_segment_length) { + ICL_WARN("received data segment " + "length %zd is larger than negotiated " + "MaxDataSegmentLength %zd; " + "dropping connection", + len, ic->ic_max_data_segment_length); + break; + } + + ic->ic_receive_state = ICL_CONN_STATE_AHS; + ic->ic_receive_len = icl_pdu_ahs_length(request); + break; + + case ICL_CONN_STATE_AHS: + //ICL_DEBUG("receiving AHS"); + error = icl_pdu_receive_ahs(request, availablep); + if (error != 0) { + ICL_DEBUG("failed to receive AHS; " + "dropping connection"); + break; + } + ic->ic_receive_state = ICL_CONN_STATE_HEADER_DIGEST; + if (ic->ic_header_crc32c == false) + ic->ic_receive_len = 0; + else + ic->ic_receive_len = ISCSI_HEADER_DIGEST_SIZE; + break; + + case ICL_CONN_STATE_HEADER_DIGEST: + //ICL_DEBUG("receiving header digest"); + error = icl_pdu_check_header_digest(request, availablep); + if (error != 0) { + ICL_DEBUG("header digest failed; " + "dropping connection"); + break; + } + + ic->ic_receive_state = ICL_CONN_STATE_DATA; + ic->ic_receive_len = + icl_pdu_data_segment_receive_len(request); + break; + + case ICL_CONN_STATE_DATA: + //ICL_DEBUG("receiving data segment"); + error = icl_pdu_receive_data_segment(request, availablep, + &more_needed); + if (error != 0) { + ICL_DEBUG("failed to receive data segment;" + "dropping connection"); + break; + } + + if (more_needed) + break; + + ic->ic_receive_state = ICL_CONN_STATE_DATA_DIGEST; + if (ic->ic_data_crc32c == false) + ic->ic_receive_len = 0; + else + ic->ic_receive_len = ISCSI_DATA_DIGEST_SIZE; + break; + + case ICL_CONN_STATE_DATA_DIGEST: + //ICL_DEBUG("receiving data digest"); + error = icl_pdu_check_data_digest(request, availablep); + if (error != 0) { + ICL_DEBUG("data digest failed; " + "dropping connection"); + break; + } + + /* + * We've received complete PDU; reset the receive state machine + * and return the PDU. + */ + ic->ic_receive_state = ICL_CONN_STATE_BHS; + ic->ic_receive_len = sizeof(struct iscsi_bhs); + ic->ic_receive_pdu = NULL; + return (request); + + default: + panic("invalid ic_receive_state %d\n", ic->ic_receive_state); + } + + if (error != 0) { + icl_pdu_free(request); + icl_conn_fail(ic); + } + + return (NULL); +} + +static void +icl_conn_receive_pdus(struct icl_conn *ic, size_t available) +{ + struct icl_pdu *response; + struct socket *so; + + so = ic->ic_socket; + + /* + * This can never happen; we're careful to only mess with ic->ic_socket + * pointer when the send/receive threads are not running. + */ + KASSERT(so != NULL, ("NULL socket")); + + for (;;) { + if (ic->ic_disconnecting) + return; + + if (so->so_error != 0) { + ICL_DEBUG("connection error %d; " + "dropping connection", so->so_error); + icl_conn_fail(ic); + return; + } + + /* + * Loop until we have a complete PDU or there is not enough + * data in the socket buffer. + */ + if (available < ic->ic_receive_len) { +#if 0 + ICL_DEBUG("not enough data; have %zd, " + "need %zd", available, + ic->ic_receive_len); +#endif + return; + } + + response = icl_conn_receive_pdu(ic, &available); + if (response == NULL) + continue; + + if (response->ip_ahs_len > 0) { + ICL_WARN("received PDU with unsupported " + "AHS; opcode 0x%x; dropping connection", + response->ip_bhs->bhs_opcode); + icl_pdu_free(response); + icl_conn_fail(ic); + return; + } + + (ic->ic_receive)(response); + } +} + +static void +icl_receive_thread(void *arg) +{ + struct icl_conn *ic; + size_t available; + struct socket *so; + + ic = arg; + so = ic->ic_socket; + + ICL_CONN_LOCK(ic); + ic->ic_receive_running = true; + ICL_CONN_UNLOCK(ic); + + for (;;) { + if (ic->ic_disconnecting) { + //ICL_DEBUG("terminating"); + ICL_CONN_LOCK(ic); + ic->ic_receive_running = false; + ICL_CONN_UNLOCK(ic); + kthread_exit(); + return; + } + + SOCKBUF_LOCK(&so->so_rcv); + available = so->so_rcv.sb_cc; + if (available < ic->ic_receive_len) { + so->so_rcv.sb_lowat = ic->ic_receive_len; + cv_wait(&ic->ic_receive_cv, &so->so_rcv.sb_mtx); + } + SOCKBUF_UNLOCK(&so->so_rcv); + + icl_conn_receive_pdus(ic, available); + } +} + +static int +icl_soupcall_receive(struct socket *so, void *arg, int waitflag) +{ + struct icl_conn *ic; + + ic = arg; + cv_signal(&ic->ic_receive_cv); + return (SU_OK); +} + +static int +icl_pdu_send(struct icl_pdu *request) +{ + size_t padding, pdu_len; + uint32_t digest, zero = 0; + int error, ok; + struct socket *so; + struct icl_conn *ic; + + ic = request->ip_conn; + so = request->ip_conn->ic_socket; + + ICL_CONN_LOCK_ASSERT(ic); + + icl_pdu_set_data_segment_length(request, request->ip_data_len); + + pdu_len = icl_pdu_size(request); + + if (ic->ic_header_crc32c) { + digest = icl_mbuf_to_crc32c(request->ip_bhs_mbuf); + ok = m_append(request->ip_bhs_mbuf, sizeof(digest), + (void *)&digest); + if (ok != 1) { + ICL_WARN("failed to append header digest"); + return (1); + } + } + + if (request->ip_data_len != 0) { + padding = icl_pdu_padding(request); + if (padding > 0) { + ok = m_append(request->ip_data_mbuf, padding, + (void *)&zero); + if (ok != 1) { + ICL_WARN("failed to append padding"); + return (1); + } + } + + if (ic->ic_data_crc32c) { + digest = icl_mbuf_to_crc32c(request->ip_data_mbuf); + + ok = m_append(request->ip_data_mbuf, sizeof(digest), + (void *)&digest); + if (ok != 1) { + ICL_WARN("failed to append header digest"); + return (1); + } + } + + m_cat(request->ip_bhs_mbuf, request->ip_data_mbuf); + request->ip_data_mbuf = NULL; + } + + request->ip_bhs_mbuf->m_pkthdr.len = pdu_len; + + error = sosend(so, NULL, NULL, request->ip_bhs_mbuf, + NULL, MSG_DONTWAIT, curthread); + request->ip_bhs_mbuf = NULL; /* Sosend consumes the mbuf. */ + if (error != 0) { + ICL_DEBUG("sosend error %d", error); + return (error); + } + + return (0); +} + +static void +icl_conn_send_pdus(struct icl_conn *ic) +{ + struct icl_pdu *request; + struct socket *so; + size_t available, size; + int error; + + ICL_CONN_LOCK_ASSERT(ic); + + so = ic->ic_socket; + + SOCKBUF_LOCK(&so->so_snd); + available = sbspace(&so->so_snd); + SOCKBUF_UNLOCK(&so->so_snd); + + while (!TAILQ_EMPTY(&ic->ic_to_send)) { + if (ic->ic_disconnecting) + return; + + request = TAILQ_FIRST(&ic->ic_to_send); + size = icl_pdu_size(request); + if (available < size) { + /* + * Set the low watermark on the socket, + * to avoid waking up until there is enough + * space. + */ + SOCKBUF_LOCK(&so->so_snd); + so->so_snd.sb_lowat = size; + SOCKBUF_UNLOCK(&so->so_snd); +#if 1 + ICL_DEBUG("no space to send; " + "have %zd, need %zd", + available, size); +#endif + return; + } + available -= size; + TAILQ_REMOVE(&ic->ic_to_send, request, ip_next); + error = icl_pdu_send(request); + if (error != 0) { + ICL_DEBUG("failed to send PDU; " + "dropping connection"); + icl_conn_fail(ic); + return; + } + icl_pdu_free(request); + } +} + +static void +icl_send_thread(void *arg) +{ + struct icl_conn *ic; + + ic = arg; + + ICL_CONN_LOCK(ic); + ic->ic_send_running = true; + ICL_CONN_UNLOCK(ic); + + for (;;) { + ICL_CONN_LOCK(ic); + if (ic->ic_disconnecting) { + //ICL_DEBUG("terminating"); + ic->ic_send_running = false; + ICL_CONN_UNLOCK(ic); + kthread_exit(); + return; + } + if (TAILQ_EMPTY(&ic->ic_to_send)) + cv_wait(&ic->ic_send_cv, &ic->ic_lock); + icl_conn_send_pdus(ic); + ICL_CONN_UNLOCK(ic); + } +} + +static int +icl_soupcall_send(struct socket *so, void *arg, int waitflag) +{ + struct icl_conn *ic; + + ic = arg; + cv_signal(&ic->ic_send_cv); + return (SU_OK); +} + +int +icl_pdu_append_data(struct icl_pdu *request, const void *addr, size_t len, int flags) +{ + struct mbuf *mb, *newmb; + size_t copylen, off = 0; + + KASSERT(len > 0, ("len == 0")); + + newmb = m_getm2(NULL, len, flags, MT_DATA, M_PKTHDR); + if (newmb == NULL) { + ICL_WARN("failed to allocate mbuf for %zd bytes", len); + return (ENOMEM); + } + + for (mb = newmb; mb != NULL; mb = mb->m_next) { + copylen = min(M_TRAILINGSPACE(mb), len - off); + memcpy(mtod(mb, char *), (const char *)addr + off, copylen); + mb->m_len = copylen; + off += copylen; + } + KASSERT(off == len, ("%s: off != len", __func__)); + + if (request->ip_data_mbuf == NULL) { + request->ip_data_mbuf = newmb; + request->ip_data_len = len; + } else { + m_cat(request->ip_data_mbuf, newmb); + request->ip_data_len += len; + } + + return (0); +} + +void +icl_pdu_get_data(struct icl_pdu *ip, size_t off, void *addr, size_t len) +{ + + m_copydata(ip->ip_data_mbuf, off, len, addr); +} + +void +icl_pdu_queue(struct icl_pdu *ip) +{ + struct icl_conn *ic; + + ic = ip->ip_conn; + + ICL_CONN_LOCK(ic); + if (ic->ic_disconnecting || ic->ic_socket == NULL) { + ICL_DEBUG("icl_pdu_queue on closed connection"); + ICL_CONN_UNLOCK(ic); + return; + } + TAILQ_INSERT_TAIL(&ic->ic_to_send, ip, ip_next); + ICL_CONN_UNLOCK(ic); + cv_signal(&ic->ic_send_cv); +} + +struct icl_conn * +icl_conn_new(void) +{ + struct icl_conn *ic; + + refcount_acquire(&icl_ncons); + + ic = uma_zalloc(icl_conn_zone, M_WAITOK | M_ZERO); + + TAILQ_INIT(&ic->ic_to_send); + mtx_init(&ic->ic_lock, "icl_lock", NULL, MTX_DEF); + cv_init(&ic->ic_send_cv, "icl_tx"); + cv_init(&ic->ic_receive_cv, "icl_rx"); + refcount_init(&ic->ic_outstanding_pdus, 0); + ic->ic_max_data_segment_length = ICL_MAX_DATA_SEGMENT_LENGTH; + + return (ic); +} + +void +icl_conn_free(struct icl_conn *ic) +{ + + mtx_destroy(&ic->ic_lock); + cv_destroy(&ic->ic_send_cv); + cv_destroy(&ic->ic_receive_cv); + uma_zfree(icl_conn_zone, ic); + refcount_release(&icl_ncons); +} + +static int +icl_conn_start(struct icl_conn *ic) +{ + size_t bufsize; + struct sockopt opt; + int error, one = 1; + + ICL_CONN_LOCK(ic); + + /* + * XXX: Ugly hack. + */ + if (ic->ic_socket == NULL) { + ICL_CONN_UNLOCK(ic); + return (EINVAL); + } + + ic->ic_receive_state = ICL_CONN_STATE_BHS; + ic->ic_receive_len = sizeof(struct iscsi_bhs); + ic->ic_disconnecting = false; + + ICL_CONN_UNLOCK(ic); + + /* + * Use max available sockbuf size for sending. Do it manually + * instead of sbreserve(9) to work around resource limits. + * + * XXX: This kind of sucks. On one hand, we don't currently support + * sending a part of data segment; we always do it in one piece, + * so we have to make sure it can fit in the socket buffer. + * Once I've implemented partial send, we'll get rid of this + * and use autoscaling. + */ + bufsize = (sizeof(struct iscsi_bhs) + + ic->ic_max_data_segment_length) * 8; + error = soreserve(ic->ic_socket, bufsize, bufsize); + if (error != 0) { + ICL_WARN("soreserve failed with error %d", error); + icl_conn_close(ic); + return (error); + } + + /* + * Disable Nagle. + */ + bzero(&opt, sizeof(opt)); + opt.sopt_dir = SOPT_SET; + opt.sopt_level = IPPROTO_TCP; + opt.sopt_name = TCP_NODELAY; + opt.sopt_val = &one; + opt.sopt_valsize = sizeof(one); + error = sosetopt(ic->ic_socket, &opt); + if (error != 0) { + ICL_WARN("disabling TCP_NODELAY failed with error %d", error); + icl_conn_close(ic); + return (error); + } + + /* + * Start threads. + */ + error = kthread_add(icl_send_thread, ic, NULL, NULL, 0, 0, "icltx"); + if (error != 0) { + ICL_WARN("kthread_add(9) failed with error %d", error); + icl_conn_close(ic); + return (error); + } + + error = kthread_add(icl_receive_thread, ic, NULL, NULL, 0, 0, "iclrx"); + if (error != 0) { + ICL_WARN("kthread_add(9) failed with error %d", error); + icl_conn_close(ic); + return (error); + } + + /* + * Register socket upcall, to get notified about incoming PDUs + * and free space to send outgoing ones. + */ + SOCKBUF_LOCK(&ic->ic_socket->so_snd); + soupcall_set(ic->ic_socket, SO_SND, icl_soupcall_send, ic); + SOCKBUF_UNLOCK(&ic->ic_socket->so_snd); + SOCKBUF_LOCK(&ic->ic_socket->so_rcv); + soupcall_set(ic->ic_socket, SO_RCV, icl_soupcall_receive, ic); + SOCKBUF_UNLOCK(&ic->ic_socket->so_rcv); + + return (0); +} + +int +icl_conn_handoff(struct icl_conn *ic, int fd) +{ + struct file *fp; + struct socket *so; + cap_rights_t rights; + int error; + + /* + * Steal the socket from userland. + */ + error = fget(curthread, fd, + cap_rights_init(&rights, CAP_SOCK_CLIENT), &fp); + if (error != 0) + return (error); + if (fp->f_type != DTYPE_SOCKET) { + fdrop(fp, curthread); + return (EINVAL); + } + so = fp->f_data; + if (so->so_type != SOCK_STREAM) { + fdrop(fp, curthread); + return (EINVAL); + } + + ICL_CONN_LOCK(ic); + + if (ic->ic_socket != NULL) { + ICL_CONN_UNLOCK(ic); + fdrop(fp, curthread); + return (EBUSY); + } + + ic->ic_socket = fp->f_data; + fp->f_ops = &badfileops; + fp->f_data = NULL; + fdrop(fp, curthread); + ICL_CONN_UNLOCK(ic); + + error = icl_conn_start(ic); + + return (error); +} + +void +icl_conn_shutdown(struct icl_conn *ic) +{ + + ICL_CONN_LOCK(ic); + if (ic->ic_socket == NULL) { + ICL_CONN_UNLOCK(ic); + return; + } + ICL_CONN_UNLOCK(ic); + + soshutdown(ic->ic_socket, SHUT_RDWR); +} + +void +icl_conn_close(struct icl_conn *ic) +{ + struct icl_pdu *pdu; + + ICL_CONN_LOCK(ic); + if (ic->ic_socket == NULL) { + ICL_CONN_UNLOCK(ic); + return; + } + + ic->ic_disconnecting = true; + + /* + * Wake up the threads, so they can properly terminate. + */ + cv_signal(&ic->ic_receive_cv); + cv_signal(&ic->ic_send_cv); + while (ic->ic_receive_running || ic->ic_send_running) { + //ICL_DEBUG("waiting for send/receive threads to terminate"); + ICL_CONN_UNLOCK(ic); + cv_signal(&ic->ic_receive_cv); + cv_signal(&ic->ic_send_cv); + pause("icl_close", 1 * hz); + ICL_CONN_LOCK(ic); + } + //ICL_DEBUG("send/receive threads terminated"); + + soclose(ic->ic_socket); + ic->ic_socket = NULL; + + if (ic->ic_receive_pdu != NULL) { + //ICL_DEBUG("freeing partially received PDU"); + icl_pdu_free(ic->ic_receive_pdu); + ic->ic_receive_pdu = NULL; + } + + /* + * Remove any outstanding PDUs from the send queue. + */ + while (!TAILQ_EMPTY(&ic->ic_to_send)) { + pdu = TAILQ_FIRST(&ic->ic_to_send); + TAILQ_REMOVE(&ic->ic_to_send, pdu, ip_next); + icl_pdu_free(pdu); + } + + KASSERT(TAILQ_EMPTY(&ic->ic_to_send), + ("destroying session with non-empty send queue")); + /* + * XXX + */ +#if 0 + KASSERT(ic->ic_outstanding_pdus == 0, + ("destroying session with %d outstanding PDUs", + ic->ic_outstanding_pdus)); +#endif + ICL_CONN_UNLOCK(ic); +} + +bool +icl_conn_connected(struct icl_conn *ic) +{ + + ICL_CONN_LOCK(ic); + if (ic->ic_socket == NULL) { + ICL_CONN_UNLOCK(ic); + return (false); + } + if (ic->ic_socket->so_error != 0) { + ICL_CONN_UNLOCK(ic); + return (false); + } + ICL_CONN_UNLOCK(ic); + return (true); +} + +#ifdef ICL_KERNEL_PROXY +int +icl_conn_handoff_sock(struct icl_conn *ic, struct socket *so) +{ + int error; + + if (so->so_type != SOCK_STREAM) + return (EINVAL); + + ICL_CONN_LOCK(ic); + if (ic->ic_socket != NULL) { + ICL_CONN_UNLOCK(ic); + return (EBUSY); + } + ic->ic_socket = so; + ICL_CONN_UNLOCK(ic); + + error = icl_conn_start(ic); + + return (error); +} +#endif /* ICL_KERNEL_PROXY */ + +static int +icl_unload(void) +{ + + if (icl_ncons != 0) + return (EBUSY); + + uma_zdestroy(icl_conn_zone); + uma_zdestroy(icl_pdu_zone); + + return (0); +} + +static void +icl_load(void) +{ + + icl_conn_zone = uma_zcreate("icl_conn", + sizeof(struct icl_conn), NULL, NULL, NULL, NULL, + UMA_ALIGN_PTR, UMA_ZONE_NOFREE); + icl_pdu_zone = uma_zcreate("icl_pdu", + sizeof(struct icl_pdu), NULL, NULL, NULL, NULL, + UMA_ALIGN_PTR, UMA_ZONE_NOFREE); + + refcount_init(&icl_ncons, 0); +} + +static int +icl_modevent(module_t mod, int what, void *arg) +{ + + switch (what) { + case MOD_LOAD: + icl_load(); + return (0); + case MOD_UNLOAD: + return (icl_unload()); + default: + return (EINVAL); + } +} + +moduledata_t icl_data = { + "icl", + icl_modevent, + 0 +}; + +DECLARE_MODULE(icl, icl_data, SI_SUB_DRIVERS, SI_ORDER_FIRST); +MODULE_VERSION(icl, 1); diff --git a/sys/dev/iscsi/icl.h b/sys/dev/iscsi/icl.h new file mode 100644 index 00000000000..87a56526956 --- /dev/null +++ b/sys/dev/iscsi/icl.h @@ -0,0 +1,151 @@ +/*- + * Copyright (c) 2012 The FreeBSD Foundation + * All rights reserved. + * + * This software was developed by Edward Tomasz Napierala under sponsorship + * from the FreeBSD Foundation. + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + * $FreeBSD$ + */ + +#ifndef ICL_H +#define ICL_H + +/* + * iSCSI Common Layer. It's used by both the initiator and target to send + * and receive iSCSI PDUs. + */ + +struct icl_conn; + +struct icl_pdu { + TAILQ_ENTRY(icl_pdu) ip_next; + struct icl_conn *ip_conn; + struct iscsi_bhs *ip_bhs; + struct mbuf *ip_bhs_mbuf; + size_t ip_ahs_len; + struct mbuf *ip_ahs_mbuf; + size_t ip_data_len; + struct mbuf *ip_data_mbuf; + + /* + * User (initiator or provider) private fields. + */ + uint32_t ip_prv0; + uint32_t ip_prv1; + uint32_t ip_prv2; +}; + +struct icl_pdu *icl_pdu_new_bhs(struct icl_conn *ic, int flags); +size_t icl_pdu_data_segment_length(const struct icl_pdu *ip); +int icl_pdu_append_data(struct icl_pdu *ip, const void *addr, size_t len, int flags); +void icl_pdu_get_data(struct icl_pdu *ip, size_t off, void *addr, size_t len); +void icl_pdu_queue(struct icl_pdu *ip); +void icl_pdu_free(struct icl_pdu *ip); + +#define ICL_CONN_STATE_INVALID 0 +#define ICL_CONN_STATE_BHS 1 +#define ICL_CONN_STATE_AHS 2 +#define ICL_CONN_STATE_HEADER_DIGEST 3 +#define ICL_CONN_STATE_DATA 4 +#define ICL_CONN_STATE_DATA_DIGEST 5 + +#define ICL_MAX_DATA_SEGMENT_LENGTH (128 * 1024) + +struct icl_conn { + struct mtx ic_lock; + struct socket *ic_socket; + volatile u_int ic_outstanding_pdus; + TAILQ_HEAD(, icl_pdu) ic_to_send; + size_t ic_receive_len; + int ic_receive_state; + struct icl_pdu *ic_receive_pdu; + struct cv ic_send_cv; + struct cv ic_receive_cv; + bool ic_header_crc32c; + bool ic_data_crc32c; + bool ic_send_running; + bool ic_receive_running; + size_t ic_max_data_segment_length; + bool ic_disconnecting; + bool ic_iser; + + void (*ic_receive)(struct icl_pdu *); + void (*ic_error)(struct icl_conn *); + + /* + * User (initiator or provider) private fields. + */ + void *ic_prv0; +}; + +struct icl_conn *icl_conn_new(void); +void icl_conn_free(struct icl_conn *ic); +int icl_conn_handoff(struct icl_conn *ic, int fd); +void icl_conn_shutdown(struct icl_conn *ic); +void icl_conn_close(struct icl_conn *ic); +bool icl_conn_connected(struct icl_conn *ic); + +#ifdef ICL_KERNEL_PROXY + +struct sockaddr; +struct icl_listen; + +struct icl_listen_sock { + TAILQ_ENTRY(icl_listen_sock) ils_next; + struct icl_listen *ils_listen; + struct socket *ils_socket; + bool ils_running; + bool ils_disconnecting; +}; + +struct icl_listen { + TAILQ_HEAD(, icl_listen_sock) il_sockets; + struct sx il_lock; + void (*il_accept)(struct socket *); +}; + +/* + * Initiator part. + */ +int icl_conn_connect(struct icl_conn *ic, bool rdma, + int domain, int socktype, int protocol, + struct sockaddr *from_sa, struct sockaddr *to_sa); +/* + * Target part. + */ +struct icl_listen *icl_listen_new(void (*accept_cb)(struct socket *)); +void icl_listen_free(struct icl_listen *il); +int icl_listen_add(struct icl_listen *il, bool rdma, int domain, + int socktype, int protocol, struct sockaddr *sa); +int icl_listen_remove(struct icl_listen *il, struct sockaddr *sa); + +/* + * This one is not a public API; only to be used by icl_proxy.c. + */ +int icl_conn_handoff_sock(struct icl_conn *ic, struct socket *so); + +#endif /* ICL_KERNEL_PROXY */ + +#endif /* !ICL_H */ diff --git a/sys/dev/iscsi/icl_proxy.c b/sys/dev/iscsi/icl_proxy.c new file mode 100644 index 00000000000..41e64c352a9 --- /dev/null +++ b/sys/dev/iscsi/icl_proxy.c @@ -0,0 +1,397 @@ +/*- + * Copyright (c) 2012 The FreeBSD Foundation + * All rights reserved. + * + * This software was developed by Edward Tomasz Napierala under sponsorship + * from the FreeBSD Foundation. + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + * $FreeBSD$ + */ +/*- + * Copyright (c) 1982, 1986, 1989, 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * sendfile(2) and related extensions: + * Copyright (c) 1998, David Greenman. 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. + * 4. 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. + * + * @(#)uipc_syscalls.c 8.4 (Berkeley) 2/21/94 + */ + +/* + * iSCSI Common Layer, kernel proxy part. + */ + +#ifdef ICL_KERNEL_PROXY + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "icl.h" + +static int debug = 1; + +#define ICL_DEBUG(X, ...) \ + if (debug > 1) { \ + printf("%s: " X "\n", __func__, ## __VA_ARGS__);\ + } while (0) + +#define ICL_WARN(X, ...) \ + if (debug > 0) { \ + printf("WARNING: %s: " X "\n", \ + __func__, ## __VA_ARGS__); \ + } while (0) + +static MALLOC_DEFINE(M_ICL_PROXY, "ICL_PROXY", "iSCSI common layer proxy"); + +#ifdef ICL_RDMA +static int icl_conn_connect_rdma(struct icl_conn *ic, int domain, int socktype, + int protocol, struct sockaddr *from_sa, struct sockaddr *to_sa); +static int icl_listen_add_rdma(struct icl_listen *il, int domain, int socktype, int protocol, + struct sockaddr *sa); +#endif /* ICL_RDMA */ + +static int +icl_conn_connect_tcp(struct icl_conn *ic, int domain, int socktype, + int protocol, struct sockaddr *from_sa, struct sockaddr *to_sa) +{ + struct socket *so; + int error; + int interrupted = 0; + + error = socreate(domain, &so, socktype, protocol, + curthread->td_ucred, curthread); + if (error != 0) + return (error); + + if (from_sa != NULL) { + error = sobind(so, from_sa, curthread); + if (error != 0) { + soclose(so); + return (error); + } + } + + error = soconnect(so, to_sa, curthread); + if (error != 0) { + soclose(so); + return (error); + } + + SOCK_LOCK(so); + while ((so->so_state & SS_ISCONNECTING) && so->so_error == 0) { + error = msleep(&so->so_timeo, SOCK_MTX(so), PSOCK | PCATCH, + "icl_connect", 0); + if (error) { + if (error == EINTR || error == ERESTART) + interrupted = 1; + break; + } + } + if (error == 0) { + error = so->so_error; + so->so_error = 0; + } + SOCK_UNLOCK(so); + + if (error != 0) { + soclose(so); + return (error); + } + + error = icl_conn_handoff_sock(ic, so); + if (error != 0) + soclose(so); + + return (error); +} + +int +icl_conn_connect(struct icl_conn *ic, bool rdma, int domain, int socktype, + int protocol, struct sockaddr *from_sa, struct sockaddr *to_sa) +{ + + if (rdma) { +#ifdef ICL_RDMA + return (icl_conn_connect_rdma(ic, domain, socktype, protocol, from_sa, to_sa)); +#else + ICL_DEBUG("RDMA not supported"); + return (EOPNOTSUPP); +#endif + } + + return (icl_conn_connect_tcp(ic, domain, socktype, protocol, from_sa, to_sa)); +} + +struct icl_listen * +icl_listen_new(void (*accept_cb)(struct socket *)) +{ + struct icl_listen *il; + + il = malloc(sizeof(*il), M_ICL_PROXY, M_ZERO | M_WAITOK); + TAILQ_INIT(&il->il_sockets); + sx_init(&il->il_lock, "icl_listen"); + il->il_accept = accept_cb; + + return (il); +} + +void +icl_listen_free(struct icl_listen *il) +{ + struct icl_listen_sock *ils; + + sx_xlock(&il->il_lock); + while (!TAILQ_EMPTY(&il->il_sockets)) { + ils = TAILQ_FIRST(&il->il_sockets); + while (ils->ils_running) { + ICL_DEBUG("waiting for accept thread to terminate"); + sx_xunlock(&il->il_lock); + ils->ils_disconnecting = true; + wakeup(&ils->ils_socket->so_timeo); + pause("icl_unlisten", 1 * hz); + sx_xlock(&il->il_lock); + } + + TAILQ_REMOVE(&il->il_sockets, ils, ils_next); + soclose(ils->ils_socket); + free(ils, M_ICL_PROXY); + } + sx_xunlock(&il->il_lock); + + free(il, M_ICL_PROXY); +} + +/* + * XXX: Doing accept in a separate thread in each socket might not be the best way + * to do stuff, but it's pretty clean and debuggable - and you probably won't + * have hundreds of listening sockets anyway. + */ +static void +icl_accept_thread(void *arg) +{ + struct icl_listen_sock *ils; + struct socket *head, *so; + struct sockaddr *sa; + int error; + + ils = arg; + head = ils->ils_socket; + + ils->ils_running = true; + + for (;;) { + ACCEPT_LOCK(); + while (TAILQ_EMPTY(&head->so_comp) && head->so_error == 0 && ils->ils_disconnecting == false) { + if (head->so_rcv.sb_state & SBS_CANTRCVMORE) { + head->so_error = ECONNABORTED; + break; + } + error = msleep(&head->so_timeo, &accept_mtx, PSOCK | PCATCH, + "accept", 0); + if (error) { + ACCEPT_UNLOCK(); + ICL_WARN("msleep failed with error %d", error); + continue; + } + if (ils->ils_disconnecting) { + ACCEPT_UNLOCK(); + ICL_DEBUG("terminating"); + ils->ils_running = false; + kthread_exit(); + return; + } + } + if (head->so_error) { + error = head->so_error; + head->so_error = 0; + ACCEPT_UNLOCK(); + ICL_WARN("socket error %d", error); + continue; + } + so = TAILQ_FIRST(&head->so_comp); + KASSERT(so != NULL, ("NULL so")); + KASSERT(!(so->so_qstate & SQ_INCOMP), ("accept1: so SQ_INCOMP")); + KASSERT(so->so_qstate & SQ_COMP, ("accept1: so not SQ_COMP")); + + /* + * Before changing the flags on the socket, we have to bump the + * reference count. Otherwise, if the protocol calls sofree(), + * the socket will be released due to a zero refcount. + */ + SOCK_LOCK(so); /* soref() and so_state update */ + soref(so); /* file descriptor reference */ + + TAILQ_REMOVE(&head->so_comp, so, so_list); + head->so_qlen--; + so->so_state |= (head->so_state & SS_NBIO); + so->so_qstate &= ~SQ_COMP; + so->so_head = NULL; + + SOCK_UNLOCK(so); + ACCEPT_UNLOCK(); + + sa = NULL; + error = soaccept(so, &sa); + if (error != 0) { + ICL_WARN("soaccept error %d", error); + if (sa != NULL) + free(sa, M_SONAME); + soclose(so); + } + + (ils->ils_listen->il_accept)(so); + } +} + +static int +icl_listen_add_tcp(struct icl_listen *il, int domain, int socktype, int protocol, + struct sockaddr *sa) +{ + struct icl_listen_sock *ils; + struct socket *so; + struct sockopt sopt; + int error, one = 1; + + error = socreate(domain, &so, socktype, protocol, + curthread->td_ucred, curthread); + if (error != 0) { + ICL_WARN("socreate failed with error %d", error); + return (error); + } + + sopt.sopt_dir = SOPT_SET; + sopt.sopt_level = SOL_SOCKET; + sopt.sopt_name = SO_REUSEADDR; + sopt.sopt_val = &one; + sopt.sopt_valsize = sizeof(one); + sopt.sopt_td = NULL; + error = sosetopt(so, &sopt); + if (error != 0) { + ICL_WARN("failed to set SO_REUSEADDR with error %d", error); + soclose(so); + return (error); + } + + error = sobind(so, sa, curthread); + if (error != 0) { + ICL_WARN("sobind failed with error %d", error); + soclose(so); + return (error); + } + + error = solisten(so, -1, curthread); + if (error != 0) { + ICL_WARN("solisten failed with error %d", error); + soclose(so); + return (error); + } + + ils = malloc(sizeof(*ils), M_ICL_PROXY, M_ZERO | M_WAITOK); + ils->ils_listen = il; + ils->ils_socket = so; + + error = kthread_add(icl_accept_thread, ils, NULL, NULL, 0, 0, "iclacc"); + if (error != 0) { + ICL_WARN("kthread_add failed with error %d", error); + soclose(so); + free(ils, M_ICL_PROXY); + + return (error); + } + + sx_xlock(&il->il_lock); + TAILQ_INSERT_TAIL(&il->il_sockets, ils, ils_next); + sx_xunlock(&il->il_lock); + + return (0); +} + +int +icl_listen_add(struct icl_listen *il, bool rdma, int domain, int socktype, int protocol, + struct sockaddr *sa) +{ + + if (rdma) { +#ifndef ICL_RDMA + ICL_DEBUG("RDMA not supported"); + return (EOPNOTSUPP); +#else + return (icl_listen_add_rdma(il, domain, socktype, protocol, sa)); +#endif + } + + + return (icl_listen_add_tcp(il, domain, socktype, protocol, sa)); +} + +int +icl_listen_remove(struct icl_listen *il, struct sockaddr *sa) +{ + + /* + * XXX + */ + + return (EOPNOTSUPP); +} + +#endif /* ICL_KERNEL_PROXY */ diff --git a/sys/dev/iscsi/iscsi.c b/sys/dev/iscsi/iscsi.c new file mode 100644 index 00000000000..0b3d565fbbf --- /dev/null +++ b/sys/dev/iscsi/iscsi.c @@ -0,0 +1,2125 @@ +/*- + * Copyright (c) 2012 The FreeBSD Foundation + * All rights reserved. + * + * This software was developed by Edward Tomasz Napierala under sponsorship + * from the FreeBSD Foundation. + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + * $FreeBSD$ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "iscsi_ioctl.h" +#include "iscsi.h" +#include "icl.h" +#include "iscsi_proto.h" + +#ifdef ICL_KERNEL_PROXY +#include +#endif + +/* + * XXX: This is global so the iscsi_unload() can access it. + * Think about how to do this properly. + */ +static struct iscsi_softc *sc; + +SYSCTL_NODE(_kern, OID_AUTO, iscsi, CTLFLAG_RD, 0, "iSCSI initiator"); +static int debug = 1; +TUNABLE_INT("kern.iscsi.debug", &debug); +SYSCTL_INT(_kern_iscsi, OID_AUTO, debug, CTLFLAG_RW, + &debug, 2, "Enable debug messages"); +static int ping_timeout = 5; +TUNABLE_INT("kern.iscsi.ping_timeout", &ping_timeout); +SYSCTL_INT(_kern_iscsi, OID_AUTO, ping_timeout, CTLFLAG_RW, &ping_timeout, + 5, "Timeout for ping (NOP-Out) requests, in seconds"); +static int iscsid_timeout = 60; +TUNABLE_INT("kern.iscsi.iscsid_timeout", &iscsid_timeout); +SYSCTL_INT(_kern_iscsi, OID_AUTO, iscsid_timeout, CTLFLAG_RW, &iscsid_timeout, + 60, "Time to wait for iscsid(8) to handle reconnection, in seconds"); +static int login_timeout = 60; +TUNABLE_INT("kern.iscsi.login_timeout", &login_timeout); +SYSCTL_INT(_kern_iscsi, OID_AUTO, login_timeout, CTLFLAG_RW, &login_timeout, + 60, "Time to wait for iscsid(8) to finish Login Phase, in seconds"); +static int maxtags = 255; +TUNABLE_INT("kern.iscsi.maxtags", &maxtags); +SYSCTL_INT(_kern_iscsi, OID_AUTO, maxtags, CTLFLAG_RW, &maxtags, + 255, "Max number of IO requests queued"); + +static MALLOC_DEFINE(M_ISCSI, "iSCSI", "iSCSI initiator"); +static uma_zone_t iscsi_outstanding_zone; + +#define CONN_SESSION(X) ((struct iscsi_session *)X->ic_prv0) +#define PDU_SESSION(X) (CONN_SESSION(X->ip_conn)) + +#define ISCSI_DEBUG(X, ...) \ + if (debug > 1) { \ + printf("%s: " X "\n", __func__, ## __VA_ARGS__);\ + } while (0) + +#define ISCSI_WARN(X, ...) \ + if (debug > 0) { \ + printf("WARNING: %s: " X "\n", \ + __func__, ## __VA_ARGS__); \ + } while (0) + +#define ISCSI_SESSION_DEBUG(S, X, ...) \ + if (debug > 1) { \ + printf("%s: %s (%s): " X "\n", \ + __func__, S->is_conf.isc_target_addr, \ + S->is_conf.isc_target, ## __VA_ARGS__); \ + } while (0) + +#define ISCSI_SESSION_WARN(S, X, ...) \ + if (debug > 0) { \ + printf("WARNING: %s (%s): " X "\n", \ + S->is_conf.isc_target_addr, \ + S->is_conf.isc_target, ## __VA_ARGS__); \ + } while (0) + +#define ISCSI_SESSION_LOCK(X) mtx_lock(&X->is_lock) +#define ISCSI_SESSION_UNLOCK(X) mtx_unlock(&X->is_lock) +#define ISCSI_SESSION_LOCK_ASSERT(X) mtx_assert(&X->is_lock, MA_OWNED) + +static int iscsi_ioctl(struct cdev *dev, u_long cmd, caddr_t arg, + int mode, struct thread *td); + +static struct cdevsw iscsi_cdevsw = { + .d_version = D_VERSION, + .d_ioctl = iscsi_ioctl, + .d_name = "iscsi", +}; + +static void iscsi_pdu_queue_locked(struct icl_pdu *request); +static void iscsi_pdu_queue(struct icl_pdu *request); +static void iscsi_pdu_update_statsn(const struct icl_pdu *response); +static void iscsi_pdu_handle_nop_in(struct icl_pdu *response); +static void iscsi_pdu_handle_scsi_response(struct icl_pdu *response); +static void iscsi_pdu_handle_data_in(struct icl_pdu *response); +static void iscsi_pdu_handle_logout_response(struct icl_pdu *response); +static void iscsi_pdu_handle_r2t(struct icl_pdu *response); +static void iscsi_pdu_handle_async_message(struct icl_pdu *response); +static void iscsi_pdu_handle_reject(struct icl_pdu *response); +static void iscsi_session_reconnect(struct iscsi_session *is); +static void iscsi_session_terminate(struct iscsi_session *is); +static void iscsi_action(struct cam_sim *sim, union ccb *ccb); +static void iscsi_poll(struct cam_sim *sim); +static struct iscsi_outstanding *iscsi_outstanding_find(struct iscsi_session *is, + uint32_t initiator_task_tag); +static int iscsi_outstanding_add(struct iscsi_session *is, + uint32_t initiator_task_tag, union ccb *ccb); +static void iscsi_outstanding_remove(struct iscsi_session *is, + struct iscsi_outstanding *io); + +static bool +iscsi_pdu_prepare(struct icl_pdu *request) +{ + struct iscsi_session *is; + struct iscsi_bhs_scsi_command *bhssc; + + is = PDU_SESSION(request); + + ISCSI_SESSION_LOCK_ASSERT(is); + + /* + * We're only using fields common for all the request + * (initiator -> target) PDUs. + */ + bhssc = (struct iscsi_bhs_scsi_command *)request->ip_bhs; + + /* + * Data-Out PDU does not contain CmdSN. + */ + if (bhssc->bhssc_opcode != ISCSI_BHS_OPCODE_SCSI_DATA_OUT) { + if (is->is_cmdsn > is->is_maxcmdsn && + (bhssc->bhssc_opcode & ISCSI_BHS_OPCODE_IMMEDIATE) == 0) { + /* + * Current MaxCmdSN prevents us from sending any more + * SCSI Command PDUs to the target; postpone the PDU. + * It will get resent by either iscsi_pdu_queue(), + * or by maintenance thread. + */ +#if 0 + ISCSI_SESSION_DEBUG(is, "postponing send, CmdSN %d, ExpCmdSN %d, MaxCmdSN %d, opcode 0x%x", + is->is_cmdsn, is->is_expcmdsn, is->is_maxcmdsn, bhssc->bhssc_opcode); +#endif + return (true); + } + bhssc->bhssc_cmdsn = htonl(is->is_cmdsn); + if ((bhssc->bhssc_opcode & ISCSI_BHS_OPCODE_IMMEDIATE) == 0) + is->is_cmdsn++; + } + bhssc->bhssc_expstatsn = htonl(is->is_statsn + 1); + + return (false); +} + +static void +iscsi_session_send_postponed(struct iscsi_session *is) +{ + struct icl_pdu *request; + bool postpone; + + ISCSI_SESSION_LOCK_ASSERT(is); + + while (!TAILQ_EMPTY(&is->is_postponed)) { + request = TAILQ_FIRST(&is->is_postponed); + postpone = iscsi_pdu_prepare(request); + if (postpone) + break; + TAILQ_REMOVE(&is->is_postponed, request, ip_next); + icl_pdu_queue(request); + } +} + +static void +iscsi_pdu_queue_locked(struct icl_pdu *request) +{ + struct iscsi_session *is; + bool postpone; + + is = PDU_SESSION(request); + ISCSI_SESSION_LOCK_ASSERT(is); + iscsi_session_send_postponed(is); + postpone = iscsi_pdu_prepare(request); + if (postpone) { + TAILQ_INSERT_TAIL(&is->is_postponed, request, ip_next); + return; + } + icl_pdu_queue(request); +} + +static void +iscsi_pdu_queue(struct icl_pdu *request) +{ + struct iscsi_session *is; + + is = PDU_SESSION(request); + ISCSI_SESSION_LOCK(is); + iscsi_pdu_queue_locked(request); + ISCSI_SESSION_UNLOCK(is); +} + +static void +iscsi_session_logout(struct iscsi_session *is) +{ + struct icl_pdu *request; + struct iscsi_bhs_logout_request *bhslr; + + request = icl_pdu_new_bhs(is->is_conn, M_NOWAIT); + if (request == NULL) + return; + + bhslr = (struct iscsi_bhs_logout_request *)request->ip_bhs; + bhslr->bhslr_opcode = ISCSI_BHS_OPCODE_LOGOUT_REQUEST; + bhslr->bhslr_reason = BHSLR_REASON_CLOSE_SESSION; + iscsi_pdu_queue_locked(request); +} + +static void +iscsi_session_terminate_tasks(struct iscsi_session *is, bool requeue) +{ + struct iscsi_outstanding *io, *tmp; + + ISCSI_SESSION_LOCK_ASSERT(is); + + TAILQ_FOREACH_SAFE(io, &is->is_outstanding, io_next, tmp) { + if (requeue) { + io->io_ccb->ccb_h.status &= ~CAM_SIM_QUEUED; + io->io_ccb->ccb_h.status |= CAM_REQUEUE_REQ; + } else { + io->io_ccb->ccb_h.status = CAM_REQ_ABORTED; + } + + if ((io->io_ccb->ccb_h.status & CAM_DEV_QFRZN) == 0) { + xpt_freeze_devq(io->io_ccb->ccb_h.path, 1); + ISCSI_SESSION_DEBUG(is, "freezing devq"); + } + io->io_ccb->ccb_h.status |= CAM_DEV_QFRZN; + xpt_done(io->io_ccb); + iscsi_outstanding_remove(is, io); + } +} + +static void +iscsi_maintenance_thread_reconnect(struct iscsi_session *is) +{ + struct icl_pdu *pdu; + + icl_conn_shutdown(is->is_conn); + icl_conn_close(is->is_conn); + + ISCSI_SESSION_LOCK(is); + +#ifdef ICL_KERNEL_PROXY + if (is->is_login_pdu != NULL) { + icl_pdu_free(is->is_login_pdu); + is->is_login_pdu = NULL; + } + cv_signal(&is->is_login_cv); +#endif + + /* + * Don't queue any new PDUs. + */ + if (is->is_sim != NULL && is->is_simq_frozen == false) { + ISCSI_SESSION_DEBUG(is, "freezing"); + xpt_freeze_simq(is->is_sim, 1); + is->is_simq_frozen = true; + } + + /* + * Remove postponed PDUs. + */ + while (!TAILQ_EMPTY(&is->is_postponed)) { + pdu = TAILQ_FIRST(&is->is_postponed); + TAILQ_REMOVE(&is->is_postponed, pdu, ip_next); + icl_pdu_free(pdu); + } + + /* + * Terminate SCSI tasks, asking CAM to requeue them. + */ + //ISCSI_SESSION_DEBUG(is, "terminating tasks"); + iscsi_session_terminate_tasks(is, true); + + KASSERT(TAILQ_EMPTY(&is->is_outstanding), + ("destroying session with active tasks")); + KASSERT(TAILQ_EMPTY(&is->is_postponed), + ("destroying session with postponed PDUs")); + + /* + * Request immediate reconnection from iscsid(8). + */ + //ISCSI_SESSION_DEBUG(is, "waking up iscsid(8)"); + is->is_connected = false; + is->is_reconnecting = false; + is->is_login_phase = false; + is->is_waiting_for_iscsid = true; + strlcpy(is->is_reason, "Waiting for iscsid(8)", sizeof(is->is_reason)); + is->is_timeout = 0; + ISCSI_SESSION_UNLOCK(is); + cv_signal(&is->is_softc->sc_cv); +} + +static void +iscsi_maintenance_thread_terminate(struct iscsi_session *is) +{ + struct iscsi_softc *sc; + struct icl_pdu *pdu; + + sc = is->is_softc; + sx_xlock(&sc->sc_lock); + TAILQ_REMOVE(&sc->sc_sessions, is, is_next); + sx_xunlock(&sc->sc_lock); + + icl_conn_close(is->is_conn); + + ISCSI_SESSION_LOCK(is); + + KASSERT(is->is_terminating, ("is_terminating == false")); + +#ifdef ICL_KERNEL_PROXY + if (is->is_login_pdu != NULL) { + icl_pdu_free(is->is_login_pdu); + is->is_login_pdu = NULL; + } + cv_signal(&is->is_login_cv); +#endif + + /* + * Don't queue any new PDUs. + */ + callout_drain(&is->is_callout); + if (is->is_sim != NULL && is->is_simq_frozen == false) { + ISCSI_SESSION_DEBUG(is, "freezing"); + xpt_freeze_simq(is->is_sim, 1); + is->is_simq_frozen = true; + } + + /* + * Remove postponed PDUs. + */ + while (!TAILQ_EMPTY(&is->is_postponed)) { + pdu = TAILQ_FIRST(&is->is_postponed); + TAILQ_REMOVE(&is->is_postponed, pdu, ip_next); + icl_pdu_free(pdu); + } + + /* + * Forcibly terminate SCSI tasks. + */ + ISCSI_SESSION_DEBUG(is, "terminating tasks"); + iscsi_session_terminate_tasks(is, false); + + /* + * Deregister CAM. + */ + if (is->is_sim != NULL) { + ISCSI_SESSION_DEBUG(is, "deregistering SIM"); + xpt_async(AC_LOST_DEVICE, is->is_path, NULL); + + if (is->is_simq_frozen) { + xpt_release_simq(is->is_sim, 1); + is->is_simq_frozen = false; + } + + xpt_free_path(is->is_path); + xpt_bus_deregister(cam_sim_path(is->is_sim)); + cam_sim_free(is->is_sim, TRUE /*free_devq*/); + is->is_sim = NULL; + } + + KASSERT(TAILQ_EMPTY(&is->is_outstanding), + ("destroying session with active tasks")); + KASSERT(TAILQ_EMPTY(&is->is_postponed), + ("destroying session with postponed PDUs")); + + ISCSI_SESSION_UNLOCK(is); + + icl_conn_free(is->is_conn); + mtx_destroy(&is->is_lock); + cv_destroy(&is->is_maintenance_cv); +#ifdef ICL_KERNEL_PROXY + cv_destroy(&is->is_login_cv); +#endif + ISCSI_SESSION_DEBUG(is, "terminated"); + free(is, M_ISCSI); + + /* + * The iscsi_unload() routine might be waiting. + */ + cv_signal(&sc->sc_cv); +} + +static void +iscsi_maintenance_thread(void *arg) +{ + struct iscsi_session *is; + + is = arg; + + for (;;) { + ISCSI_SESSION_LOCK(is); + if (is->is_reconnecting == false && + is->is_terminating == false && + TAILQ_EMPTY(&is->is_postponed)) + cv_wait(&is->is_maintenance_cv, &is->is_lock); + + if (is->is_reconnecting) { + ISCSI_SESSION_UNLOCK(is); + iscsi_maintenance_thread_reconnect(is); + continue; + } + + if (is->is_terminating) { + ISCSI_SESSION_UNLOCK(is); + iscsi_maintenance_thread_terminate(is); + kthread_exit(); + return; + } + + iscsi_session_send_postponed(is); + ISCSI_SESSION_UNLOCK(is); + } +} + +static void +iscsi_session_reconnect(struct iscsi_session *is) +{ + + /* + * XXX: We can't use locking here, because + * it's being called from various contexts. + * Hope it doesn't break anything. + */ + if (is->is_reconnecting) + return; + + is->is_reconnecting = true; + cv_signal(&is->is_maintenance_cv); +} + +static void +iscsi_session_terminate(struct iscsi_session *is) +{ + if (is->is_terminating) + return; + + is->is_terminating = true; + +#if 0 + iscsi_session_logout(is); +#endif + cv_signal(&is->is_maintenance_cv); +} + +static void +iscsi_callout(void *context) +{ + struct icl_pdu *request; + struct iscsi_bhs_nop_out *bhsno; + struct iscsi_session *is; + bool reconnect_needed = false; + + is = context; + + if (is->is_terminating) + return; + + callout_schedule(&is->is_callout, 1 * hz); + + ISCSI_SESSION_LOCK(is); + is->is_timeout++; + + if (is->is_waiting_for_iscsid) { + if (is->is_timeout > iscsid_timeout) { + ISCSI_SESSION_WARN(is, "timed out waiting for iscsid(8) " + "for %d seconds; reconnecting", + is->is_timeout); + reconnect_needed = true; + } + goto out; + } + + if (is->is_login_phase) { + if (is->is_timeout > login_timeout) { + ISCSI_SESSION_WARN(is, "login timed out after %d seconds; " + "reconnecting", is->is_timeout); + reconnect_needed = true; + } + goto out; + } + + if (is->is_timeout >= ping_timeout) { + ISCSI_SESSION_WARN(is, "no ping reply (NOP-In) after %d seconds; " + "reconnecting", ping_timeout); + reconnect_needed = true; + goto out; + } + + ISCSI_SESSION_UNLOCK(is); + + /* + * If the ping was reset less than one second ago - which means + * that we've received some PDU during the last second - assume + * the traffic flows correctly and don't bother sending a NOP-Out. + * + * (It's 2 - one for one second, and one for incrementing is_timeout + * earlier in this routine.) + */ + if (is->is_timeout < 2) + return; + + request = icl_pdu_new_bhs(is->is_conn, M_WAITOK); + bhsno = (struct iscsi_bhs_nop_out *)request->ip_bhs; + bhsno->bhsno_opcode = ISCSI_BHS_OPCODE_NOP_OUT | + ISCSI_BHS_OPCODE_IMMEDIATE; + bhsno->bhsno_flags = 0x80; + bhsno->bhsno_target_transfer_tag = 0xffffffff; + iscsi_pdu_queue(request); + return; + +out: + ISCSI_SESSION_UNLOCK(is); + + if (reconnect_needed) + iscsi_session_reconnect(is); +} + +static void +iscsi_pdu_update_statsn(const struct icl_pdu *response) +{ + const struct iscsi_bhs_data_in *bhsdi; + struct iscsi_session *is; + uint32_t expcmdsn, maxcmdsn; + + is = PDU_SESSION(response); + + ISCSI_SESSION_LOCK_ASSERT(is); + + /* + * We're only using fields common for all the response + * (target -> initiator) PDUs. + */ + bhsdi = (const struct iscsi_bhs_data_in *)response->ip_bhs; + /* + * Ok, I lied. In case of Data-In, "The fields StatSN, Status, + * and Residual Count only have meaningful content if the S bit + * is set to 1", so we also need to check the bit specific for + * Data-In PDU. + */ + if (bhsdi->bhsdi_opcode != ISCSI_BHS_OPCODE_SCSI_DATA_IN || + (bhsdi->bhsdi_flags & BHSDI_FLAGS_S) != 0) { + if (ntohl(bhsdi->bhsdi_statsn) < is->is_statsn) { + ISCSI_SESSION_WARN(is, + "PDU StatSN %d >= session StatSN %d, opcode 0x%x", + is->is_statsn, ntohl(bhsdi->bhsdi_statsn), + bhsdi->bhsdi_opcode); + } + is->is_statsn = ntohl(bhsdi->bhsdi_statsn); + } + + expcmdsn = ntohl(bhsdi->bhsdi_expcmdsn); + maxcmdsn = ntohl(bhsdi->bhsdi_maxcmdsn); + + /* + * XXX: Compare using Serial Arithmetic Sense. + */ + if (maxcmdsn + 1 < expcmdsn) { + ISCSI_SESSION_DEBUG(is, "PDU MaxCmdSN %d + 1 < PDU ExpCmdSN %d; ignoring", + maxcmdsn, expcmdsn); + } else { + if (maxcmdsn > is->is_maxcmdsn) { + is->is_maxcmdsn = maxcmdsn; + + /* + * Command window increased; kick the maintanance thread + * to send out postponed commands. + */ + if (!TAILQ_EMPTY(&is->is_postponed)) + cv_signal(&is->is_maintenance_cv); + } else if (maxcmdsn < is->is_maxcmdsn) { + ISCSI_SESSION_DEBUG(is, "PDU MaxCmdSN %d < session MaxCmdSN %d; ignoring", + maxcmdsn, is->is_maxcmdsn); + } + + if (expcmdsn > is->is_expcmdsn) { + is->is_expcmdsn = expcmdsn; + } else if (expcmdsn < is->is_expcmdsn) { + ISCSI_SESSION_DEBUG(is, "PDU ExpCmdSN %d < session ExpCmdSN %d; ignoring", + expcmdsn, is->is_expcmdsn); + } + } + + /* + * Every incoming PDU - not just NOP-In - resets the ping timer. + * The purpose of the timeout is to reset the connection when it stalls; + * we don't want this to happen when NOP-In or NOP-Out ends up delayed + * in some queue. + */ + is->is_timeout = 0; +} + +static void +iscsi_receive_callback(struct icl_pdu *response) +{ + struct iscsi_session *is; + + is = PDU_SESSION(response); + + ISCSI_SESSION_LOCK(is); + +#ifdef ICL_KERNEL_PROXY + if (is->is_login_phase) { + if (is->is_login_pdu == NULL) + is->is_login_pdu = response; + else + icl_pdu_free(response); + ISCSI_SESSION_UNLOCK(is); + cv_signal(&is->is_login_cv); + return; + } +#endif + + iscsi_pdu_update_statsn(response); + + /* + * The handling routine is responsible for freeing the PDU + * when it's no longer needed. + */ + switch (response->ip_bhs->bhs_opcode) { + case ISCSI_BHS_OPCODE_NOP_IN: + iscsi_pdu_handle_nop_in(response); + break; + case ISCSI_BHS_OPCODE_SCSI_RESPONSE: + iscsi_pdu_handle_scsi_response(response); + break; + case ISCSI_BHS_OPCODE_SCSI_DATA_IN: + iscsi_pdu_handle_data_in(response); + break; + case ISCSI_BHS_OPCODE_LOGOUT_RESPONSE: + iscsi_pdu_handle_logout_response(response); + break; + case ISCSI_BHS_OPCODE_R2T: + iscsi_pdu_handle_r2t(response); + break; + case ISCSI_BHS_OPCODE_ASYNC_MESSAGE: + iscsi_pdu_handle_async_message(response); + break; + case ISCSI_BHS_OPCODE_REJECT: + iscsi_pdu_handle_reject(response); + break; + default: + ISCSI_SESSION_WARN(is, "received PDU with unsupported " + "opcode 0x%x; reconnecting", + response->ip_bhs->bhs_opcode); + iscsi_session_reconnect(is); + icl_pdu_free(response); + } + + ISCSI_SESSION_UNLOCK(is); +} + +static void +iscsi_error_callback(struct icl_conn *ic) +{ + struct iscsi_session *is; + + is = CONN_SESSION(ic); + + ISCSI_SESSION_WARN(is, "connection error; reconnecting"); + iscsi_session_reconnect(is); +} + +static void +iscsi_pdu_handle_nop_in(struct icl_pdu *response) +{ + struct iscsi_bhs_nop_out *bhsno; + struct iscsi_bhs_nop_in *bhsni; + struct icl_pdu *request; + + bhsni = (struct iscsi_bhs_nop_in *)response->ip_bhs; + + if (bhsni->bhsni_target_transfer_tag == 0xffffffff) { + /* + * Nothing to do; iscsi_pdu_update_statsn() already + * zeroed the timeout. + */ + icl_pdu_free(response); + return; + } + + request = icl_pdu_new_bhs(response->ip_conn, M_NOWAIT); + if (request == NULL) { + icl_pdu_free(response); + return; + } + bhsno = (struct iscsi_bhs_nop_out *)request->ip_bhs; + bhsno->bhsno_opcode = ISCSI_BHS_OPCODE_NOP_OUT | + ISCSI_BHS_OPCODE_IMMEDIATE; + bhsno->bhsno_flags = 0x80; + bhsno->bhsno_initiator_task_tag = 0xffffffff; /* XXX */ + bhsno->bhsno_target_transfer_tag = bhsni->bhsni_target_transfer_tag; + + request->ip_data_len = response->ip_data_len; + request->ip_data_mbuf = response->ip_data_mbuf; + response->ip_data_len = 0; + response->ip_data_mbuf = NULL; + + icl_pdu_free(response); + iscsi_pdu_queue_locked(request); +} + +static void +iscsi_pdu_handle_scsi_response(struct icl_pdu *response) +{ + struct iscsi_bhs_scsi_response *bhssr; + struct iscsi_outstanding *io; + struct iscsi_session *is; + struct ccb_scsiio *csio; + size_t data_segment_len; + uint16_t sense_len; + + is = PDU_SESSION(response); + + bhssr = (struct iscsi_bhs_scsi_response *)response->ip_bhs; + io = iscsi_outstanding_find(is, bhssr->bhssr_initiator_task_tag); + if (io == NULL) { + ISCSI_SESSION_WARN(is, "bad itt 0x%x", bhssr->bhssr_initiator_task_tag); + icl_pdu_free(response); + iscsi_session_reconnect(is); + return; + } + + if (bhssr->bhssr_response != BHSSR_RESPONSE_COMMAND_COMPLETED) { + ISCSI_SESSION_WARN(is, "service response 0x%x", bhssr->bhssr_response); + if ((io->io_ccb->ccb_h.status & CAM_DEV_QFRZN) == 0) { + xpt_freeze_devq(io->io_ccb->ccb_h.path, 1); + ISCSI_SESSION_DEBUG(is, "freezing devq"); + } + io->io_ccb->ccb_h.status = CAM_REQ_CMP_ERR | CAM_DEV_QFRZN; + } else if (bhssr->bhssr_status == 0) { + io->io_ccb->ccb_h.status = CAM_REQ_CMP; + } else { + if ((io->io_ccb->ccb_h.status & CAM_DEV_QFRZN) == 0) { + xpt_freeze_devq(io->io_ccb->ccb_h.path, 1); + ISCSI_SESSION_DEBUG(is, "freezing devq"); + } + io->io_ccb->ccb_h.status = CAM_SCSI_STATUS_ERROR | CAM_DEV_QFRZN; + io->io_ccb->csio.scsi_status = bhssr->bhssr_status; + } + + if (bhssr->bhssr_flags & BHSSR_FLAGS_RESIDUAL_OVERFLOW) { + ISCSI_SESSION_WARN(is, "target indicated residual overflow"); + icl_pdu_free(response); + iscsi_session_reconnect(is); + return; + } + + csio = &io->io_ccb->csio; + + data_segment_len = icl_pdu_data_segment_length(response); + if (data_segment_len > 0) { + if (data_segment_len < sizeof(sense_len)) { + ISCSI_SESSION_WARN(is, "truncated data segment (%zd bytes)", + data_segment_len); + if ((io->io_ccb->ccb_h.status & CAM_DEV_QFRZN) == 0) { + xpt_freeze_devq(io->io_ccb->ccb_h.path, 1); + ISCSI_SESSION_DEBUG(is, "freezing devq"); + } + io->io_ccb->ccb_h.status = CAM_REQ_CMP_ERR | CAM_DEV_QFRZN; + goto out; + } + icl_pdu_get_data(response, 0, &sense_len, sizeof(sense_len)); + sense_len = ntohs(sense_len); +#if 0 + ISCSI_SESSION_DEBUG(is, "sense_len %d, data len %zd", + sense_len, data_segment_len); +#endif + if (sizeof(sense_len) + sense_len > data_segment_len) { + ISCSI_SESSION_WARN(is, "truncated data segment " + "(%zd bytes, should be %zd)", + data_segment_len, sizeof(sense_len) + sense_len); + if ((io->io_ccb->ccb_h.status & CAM_DEV_QFRZN) == 0) { + xpt_freeze_devq(io->io_ccb->ccb_h.path, 1); + ISCSI_SESSION_DEBUG(is, "freezing devq"); + } + io->io_ccb->ccb_h.status = CAM_REQ_CMP_ERR | CAM_DEV_QFRZN; + goto out; + } else if (sizeof(sense_len) + sense_len < data_segment_len) + ISCSI_SESSION_WARN(is, "oversize data segment " + "(%zd bytes, should be %zd)", + data_segment_len, sizeof(sense_len) + sense_len); + if (sense_len > csio->sense_len) { + ISCSI_SESSION_DEBUG(is, "truncating sense from %d to %d", + sense_len, csio->sense_len); + sense_len = csio->sense_len; + } + icl_pdu_get_data(response, sizeof(sense_len), &csio->sense_data, sense_len); + csio->sense_resid = csio->sense_len - sense_len; + io->io_ccb->ccb_h.status |= CAM_AUTOSNS_VALID; + } + +out: + if (bhssr->bhssr_flags & BHSSR_FLAGS_RESIDUAL_UNDERFLOW) + csio->resid = ntohl(bhssr->bhssr_residual_count); + + if ((csio->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_IN) { + KASSERT(io->io_received <= csio->dxfer_len, + ("io->io_received > csio->dxfer_len")); + if (io->io_received < csio->dxfer_len) { + if (csio->resid != csio->dxfer_len - io->io_received) { + ISCSI_SESSION_WARN(is, "underflow mismatch: " + "target indicates %d, we calculated %zd", + csio->resid, + csio->dxfer_len - io->io_received); + } + csio->resid = csio->dxfer_len - io->io_received; + } + } + + xpt_done(io->io_ccb); + iscsi_outstanding_remove(is, io); + icl_pdu_free(response); +} + +static void +iscsi_pdu_handle_data_in(struct icl_pdu *response) +{ + struct iscsi_bhs_data_in *bhsdi; + struct iscsi_outstanding *io; + struct iscsi_session *is; + struct ccb_scsiio *csio; + size_t data_segment_len; + + is = PDU_SESSION(response); + bhsdi = (struct iscsi_bhs_data_in *)response->ip_bhs; + io = iscsi_outstanding_find(is, bhsdi->bhsdi_initiator_task_tag); + if (io == NULL) { + ISCSI_SESSION_WARN(is, "bad itt 0x%x", bhsdi->bhsdi_initiator_task_tag); + icl_pdu_free(response); + iscsi_session_reconnect(is); + return; + } + + data_segment_len = icl_pdu_data_segment_length(response); + if (data_segment_len == 0) { + /* + * "The sending of 0 length data segments should be avoided, + * but initiators and targets MUST be able to properly receive + * 0 length data segments." + */ + icl_pdu_free(response); + return; + } + + /* + * We need to track this for security reasons - without it, malicious target + * could respond to SCSI READ without sending Data-In PDUs, which would result + * in read operation on the initiator side returning random kernel data. + */ + if (ntohl(bhsdi->bhsdi_buffer_offset) != io->io_received) { + ISCSI_SESSION_WARN(is, "data out of order; expected offset %zd, got %zd", + io->io_received, (size_t)ntohl(bhsdi->bhsdi_buffer_offset)); + icl_pdu_free(response); + iscsi_session_reconnect(is); + return; + } + + csio = &io->io_ccb->csio; + + if (ntohl(bhsdi->bhsdi_buffer_offset) + data_segment_len > + csio->dxfer_len) { + ISCSI_SESSION_WARN(is, "oversize data segment (%zd bytes " + "at offset %d, buffer is %d)", + data_segment_len, ntohl(bhsdi->bhsdi_buffer_offset), + csio->dxfer_len); + icl_pdu_free(response); + iscsi_session_reconnect(is); + return; + } + + icl_pdu_get_data(response, 0, csio->data_ptr + ntohl(bhsdi->bhsdi_buffer_offset), data_segment_len); + io->io_received += data_segment_len; + + /* + * XXX: Check DataSN. + * XXX: Check F. + */ + if (bhsdi->bhsdi_flags & BHSDI_FLAGS_S) { + //ISCSI_SESSION_DEBUG(is, "got S flag; status 0x%x", bhsdi->bhsdi_status); + if (bhsdi->bhsdi_status == 0) { + io->io_ccb->ccb_h.status = CAM_REQ_CMP; + } else { + if ((io->io_ccb->ccb_h.status & CAM_DEV_QFRZN) == 0) { + xpt_freeze_devq(io->io_ccb->ccb_h.path, 1); + ISCSI_SESSION_DEBUG(is, "freezing devq"); + } + io->io_ccb->ccb_h.status = CAM_SCSI_STATUS_ERROR | CAM_DEV_QFRZN; + csio->scsi_status = bhsdi->bhsdi_status; + } + xpt_done(io->io_ccb); + iscsi_outstanding_remove(is, io); + } + + icl_pdu_free(response); +} + +static void +iscsi_pdu_handle_logout_response(struct icl_pdu *response) +{ + + ISCSI_SESSION_DEBUG(PDU_SESSION(response), "logout response"); + icl_pdu_free(response); +} + +static void +iscsi_pdu_handle_r2t(struct icl_pdu *response) +{ + struct icl_pdu *request; + struct iscsi_session *is; + struct iscsi_bhs_r2t *bhsr2t; + struct iscsi_bhs_data_out *bhsdo; + struct iscsi_outstanding *io; + struct ccb_scsiio *csio; + size_t off, len, total_len; + int error; + + is = PDU_SESSION(response); + + bhsr2t = (struct iscsi_bhs_r2t *)response->ip_bhs; + io = iscsi_outstanding_find(is, bhsr2t->bhsr2t_initiator_task_tag); + if (io == NULL) { + ISCSI_SESSION_WARN(is, "bad itt 0x%x; reconnecting", + bhsr2t->bhsr2t_initiator_task_tag); + icl_pdu_free(response); + iscsi_session_reconnect(is); + return; + } + + csio = &io->io_ccb->csio; + + if ((csio->ccb_h.flags & CAM_DIR_MASK) != CAM_DIR_OUT) { + ISCSI_SESSION_WARN(is, "received R2T for read command; reconnecting"); + icl_pdu_free(response); + iscsi_session_reconnect(is); + return; + } + + /* + * XXX: Verify R2TSN. + */ + + io->io_datasn = 0; + off = ntohl(bhsr2t->bhsr2t_buffer_offset); + total_len = ntohl(bhsr2t->bhsr2t_desired_data_transfer_length); + + //ISCSI_SESSION_DEBUG(is, "r2t; off %zd, len %zd", off, total_len); + + for (;;) { + len = total_len; + + if (len > is->is_max_data_segment_length) + len = is->is_max_data_segment_length; + + if (off + len > csio->dxfer_len) { + ISCSI_SESSION_WARN(is, "bad off %zd, len %d", + off + len, csio->dxfer_len); + icl_pdu_free(response); + iscsi_session_reconnect(is); + return; + } + + request = icl_pdu_new_bhs(response->ip_conn, M_NOWAIT); + if (request == NULL) { + icl_pdu_free(response); + iscsi_session_reconnect(is); + return; + } + + bhsdo = (struct iscsi_bhs_data_out *)request->ip_bhs; + bhsdo->bhsdo_opcode = ISCSI_BHS_OPCODE_SCSI_DATA_OUT; + bhsdo->bhsdo_lun = bhsr2t->bhsr2t_lun; + bhsdo->bhsdo_initiator_task_tag = + bhsr2t->bhsr2t_initiator_task_tag; + bhsdo->bhsdo_target_transfer_tag = + bhsr2t->bhsr2t_target_transfer_tag; + bhsdo->bhsdo_datasn = htonl(io->io_datasn++); + bhsdo->bhsdo_buffer_offset = htonl(off); + error = icl_pdu_append_data(request, csio->data_ptr + off, len, M_NOWAIT); + if (error != 0) { + icl_pdu_free(request); + icl_pdu_free(response); + iscsi_session_reconnect(is); + return; + } + + off += len; + total_len -= len; + + if (total_len == 0) { + bhsdo->bhsdo_flags |= BHSDO_FLAGS_F; + //ISCSI_SESSION_DEBUG(is, "setting F, off %zd", off); + } else { + //ISCSI_SESSION_DEBUG(is, "not finished, off %zd", off); + } + + iscsi_pdu_queue_locked(request); + + if (total_len == 0) + break; + } + + icl_pdu_free(response); +} + +static void +iscsi_pdu_handle_async_message(struct icl_pdu *response) +{ + struct iscsi_bhs_asynchronous_message *bhsam; + struct iscsi_session *is; + + is = PDU_SESSION(response); + bhsam = (struct iscsi_bhs_asynchronous_message *)response->ip_bhs; + switch (bhsam->bhsam_async_event) { + case BHSAM_EVENT_TARGET_REQUESTS_LOGOUT: + ISCSI_SESSION_WARN(is, "target requests logout; removing session"); + iscsi_session_logout(is); + iscsi_session_terminate(is); + break; + case BHSAM_EVENT_TARGET_TERMINATES_CONNECTION: + ISCSI_SESSION_WARN(is, "target indicates it will drop drop the connection"); + break; + case BHSAM_EVENT_TARGET_TERMINATES_SESSION: + ISCSI_SESSION_WARN(is, "target indicates it will drop drop the session"); + break; + default: + /* + * XXX: Technically, we're obligated to also handle + * parameter renegotiation. + */ + ISCSI_SESSION_WARN(is, "ignoring AsyncEvent %d", bhsam->bhsam_async_event); + break; + } + + icl_pdu_free(response); +} + +static void +iscsi_pdu_handle_reject(struct icl_pdu *response) +{ + struct iscsi_bhs_reject *bhsr; + struct iscsi_session *is; + + is = PDU_SESSION(response); + bhsr = (struct iscsi_bhs_reject *)response->ip_bhs; + ISCSI_SESSION_WARN(is, "received Reject PDU, reason 0x%x; protocol error?", + bhsr->bhsr_reason); + + icl_pdu_free(response); +} + +static int +iscsi_ioctl_daemon_wait(struct iscsi_softc *sc, + struct iscsi_daemon_request *request) +{ + struct iscsi_session *is; + int error; + + sx_slock(&sc->sc_lock); + for (;;) { + TAILQ_FOREACH(is, &sc->sc_sessions, is_next) { + if (is->is_waiting_for_iscsid) + break; + } + + if (is == NULL) { + /* + * No session requires attention from iscsid(8); wait. + */ + error = cv_wait_sig(&sc->sc_cv, &sc->sc_lock); + if (error != 0) { + sx_sunlock(&sc->sc_lock); + return (error); + } + continue; + } + + ISCSI_SESSION_LOCK(is); + is->is_waiting_for_iscsid = false; + is->is_login_phase = true; + is->is_reason[0] = '\0'; + ISCSI_SESSION_UNLOCK(is); + + request->idr_session_id = is->is_id; + memcpy(&request->idr_conf, &is->is_conf, + sizeof(request->idr_conf)); + + sx_sunlock(&sc->sc_lock); + return (0); + } +} + +static int +iscsi_ioctl_daemon_handoff(struct iscsi_softc *sc, + struct iscsi_daemon_handoff *handoff) +{ + struct iscsi_session *is; + int error; + + sx_slock(&sc->sc_lock); + + /* + * Find the session to hand off socket to. + */ + TAILQ_FOREACH(is, &sc->sc_sessions, is_next) { + if (is->is_id == handoff->idh_session_id) + break; + } + if (is == NULL) { + sx_sunlock(&sc->sc_lock); + return (ESRCH); + } + ISCSI_SESSION_LOCK(is); + if (is->is_conf.isc_discovery || is->is_terminating) { + ISCSI_SESSION_UNLOCK(is); + sx_sunlock(&sc->sc_lock); + return (EINVAL); + } + + strlcpy(is->is_target_alias, handoff->idh_target_alias, + sizeof(is->is_target_alias)); + memcpy(is->is_isid, handoff->idh_isid, sizeof(is->is_isid)); + is->is_statsn = handoff->idh_statsn; + is->is_initial_r2t = handoff->idh_initial_r2t; + is->is_immediate_data = handoff->idh_immediate_data; + is->is_max_data_segment_length = handoff->idh_max_data_segment_length; + is->is_max_burst_length = handoff->idh_max_burst_length; + is->is_first_burst_length = handoff->idh_first_burst_length; + + if (handoff->idh_header_digest == ISCSI_DIGEST_CRC32C) + is->is_conn->ic_header_crc32c = true; + else + is->is_conn->ic_header_crc32c = false; + if (handoff->idh_data_digest == ISCSI_DIGEST_CRC32C) + is->is_conn->ic_data_crc32c = true; + else + is->is_conn->ic_data_crc32c = false; + + is->is_cmdsn = 0; + is->is_expcmdsn = 1; + is->is_maxcmdsn = 1; + is->is_waiting_for_iscsid = false; + is->is_login_phase = false; + is->is_timeout = 0; + is->is_connected = true; + is->is_reason[0] = '\0'; + + ISCSI_SESSION_UNLOCK(is); + +#ifndef ICL_KERNEL_PROXY + error = icl_conn_handoff(is->is_conn, handoff->idh_socket); + if (error != 0) { + sx_sunlock(&sc->sc_lock); + iscsi_session_terminate(is); + return (error); + } +#endif + + sx_sunlock(&sc->sc_lock); + + if (is->is_sim != NULL) { + /* + * When reconnecting, there already is SIM allocated for the session. + */ + KASSERT(is->is_simq_frozen, ("reconnect without frozen simq")); + ISCSI_SESSION_LOCK(is); + ISCSI_SESSION_DEBUG(is, "releasing"); + xpt_release_simq(is->is_sim, 1); + is->is_simq_frozen = false; + ISCSI_SESSION_UNLOCK(is); + + } else { + ISCSI_SESSION_LOCK(is); + is->is_devq = cam_simq_alloc(maxtags); + if (is->is_devq == NULL) { + ISCSI_SESSION_WARN(is, "failed to allocate simq"); + iscsi_session_terminate(is); + return (ENOMEM); + } + + is->is_sim = cam_sim_alloc(iscsi_action, iscsi_poll, "iscsi", + is, is->is_id /* unit */, &is->is_lock, + maxtags, maxtags, is->is_devq); + if (is->is_sim == NULL) { + ISCSI_SESSION_UNLOCK(is); + ISCSI_SESSION_WARN(is, "failed to allocate SIM"); + cam_simq_free(is->is_devq); + iscsi_session_terminate(is); + return (ENOMEM); + } + + error = xpt_bus_register(is->is_sim, NULL, 0); + if (error != 0) { + ISCSI_SESSION_UNLOCK(is); + ISCSI_SESSION_WARN(is, "failed to register bus"); + iscsi_session_terminate(is); + return (ENOMEM); + } + + error = xpt_create_path(&is->is_path, /*periph*/NULL, + cam_sim_path(is->is_sim), CAM_TARGET_WILDCARD, + CAM_LUN_WILDCARD); + if (error != CAM_REQ_CMP) { + ISCSI_SESSION_UNLOCK(is); + ISCSI_SESSION_WARN(is, "failed to create path"); + iscsi_session_terminate(is); + return (ENOMEM); + } + ISCSI_SESSION_UNLOCK(is); + } + + return (0); +} + +static int +iscsi_ioctl_daemon_fail(struct iscsi_softc *sc, + struct iscsi_daemon_fail *fail) +{ + struct iscsi_session *is; + + sx_slock(&sc->sc_lock); + + TAILQ_FOREACH(is, &sc->sc_sessions, is_next) { + if (is->is_id == fail->idf_session_id) + break; + } + if (is == NULL) { + sx_sunlock(&sc->sc_lock); + return (ESRCH); + } + ISCSI_SESSION_LOCK(is); + ISCSI_SESSION_DEBUG(is, "iscsid(8) failed: %s", + fail->idf_reason); + strlcpy(is->is_reason, fail->idf_reason, sizeof(is->is_reason)); + //is->is_waiting_for_iscsid = false; + //is->is_login_phase = true; + //iscsi_session_reconnect(is); + ISCSI_SESSION_UNLOCK(is); + sx_sunlock(&sc->sc_lock); + + return (0); +} + +#ifdef ICL_KERNEL_PROXY +static int +iscsi_ioctl_daemon_connect(struct iscsi_softc *sc, + struct iscsi_daemon_connect *idc) +{ + struct iscsi_session *is; + struct sockaddr *from_sa, *to_sa; + int error; + + sx_slock(&sc->sc_lock); + TAILQ_FOREACH(is, &sc->sc_sessions, is_next) { + if (is->is_id == idc->idc_session_id) + break; + } + if (is == NULL) { + sx_sunlock(&sc->sc_lock); + return (ESRCH); + } + sx_sunlock(&sc->sc_lock); + + if (idc->idc_from_addrlen > 0) { + error = getsockaddr(&from_sa, (void *)idc->idc_from_addr, idc->idc_from_addrlen); + if (error != 0) + return (error); + } else { + from_sa = NULL; + } + error = getsockaddr(&to_sa, (void *)idc->idc_to_addr, idc->idc_to_addrlen); + if (error != 0) { + free(from_sa, M_SONAME); + return (error); + } + + ISCSI_SESSION_LOCK(is); + is->is_waiting_for_iscsid = false; + is->is_login_phase = true; + is->is_timeout = 0; + ISCSI_SESSION_UNLOCK(is); + + error = icl_conn_connect(is->is_conn, idc->idc_iser, idc->idc_domain, + idc->idc_socktype, idc->idc_protocol, from_sa, to_sa); + free(from_sa, M_SONAME); + free(to_sa, M_SONAME); + + /* + * Digests are always disabled during login phase. + */ + is->is_conn->ic_header_crc32c = false; + is->is_conn->ic_data_crc32c = false; + + return (error); +} + +static int +iscsi_ioctl_daemon_send(struct iscsi_softc *sc, + struct iscsi_daemon_send *ids) +{ + struct iscsi_session *is; + struct icl_pdu *ip; + size_t datalen; + void *data; + int error; + + sx_slock(&sc->sc_lock); + TAILQ_FOREACH(is, &sc->sc_sessions, is_next) { + if (is->is_id == ids->ids_session_id) + break; + } + if (is == NULL) { + sx_sunlock(&sc->sc_lock); + return (ESRCH); + } + sx_sunlock(&sc->sc_lock); + + if (is->is_login_phase == false) + return (EBUSY); + + if (is->is_terminating || is->is_reconnecting) + return (EIO); + + datalen = ids->ids_data_segment_len; + if (datalen > ISCSI_MAX_DATA_SEGMENT_LENGTH) + return (EINVAL); + if (datalen > 0) { + data = malloc(datalen, M_ISCSI, M_WAITOK); + error = copyin(ids->ids_data_segment, data, datalen); + if (error != 0) { + free(data, M_ISCSI); + return (error); + } + } + + ip = icl_pdu_new_bhs(is->is_conn, M_WAITOK); + memcpy(ip->ip_bhs, ids->ids_bhs, sizeof(*ip->ip_bhs)); + if (datalen > 0) { + error = icl_pdu_append_data(ip, data, datalen, M_WAITOK); + KASSERT(error == 0, ("icl_pdu_append_data(..., M_WAITOK) failed")); + free(data, M_ISCSI); + } + icl_pdu_queue(ip); + + return (0); +} + +static int +iscsi_ioctl_daemon_receive(struct iscsi_softc *sc, + struct iscsi_daemon_receive *idr) +{ + struct iscsi_session *is; + struct icl_pdu *ip; + void *data; + + sx_slock(&sc->sc_lock); + TAILQ_FOREACH(is, &sc->sc_sessions, is_next) { + if (is->is_id == idr->idr_session_id) + break; + } + if (is == NULL) { + sx_sunlock(&sc->sc_lock); + return (ESRCH); + } + sx_sunlock(&sc->sc_lock); + + if (is->is_login_phase == false) + return (EBUSY); + + ISCSI_SESSION_LOCK(is); + while (is->is_login_pdu == NULL && + is->is_terminating == false && + is->is_reconnecting == false) + cv_wait(&is->is_login_cv, &is->is_lock); + if (is->is_terminating || is->is_reconnecting) { + ISCSI_SESSION_UNLOCK(is); + return (EIO); + } + ip = is->is_login_pdu; + is->is_login_pdu = NULL; + ISCSI_SESSION_UNLOCK(is); + + if (ip->ip_data_len > idr->idr_data_segment_len) { + icl_pdu_free(ip); + return (EMSGSIZE); + } + + copyout(ip->ip_bhs, idr->idr_bhs, sizeof(*ip->ip_bhs)); + if (ip->ip_data_len > 0) { + data = malloc(ip->ip_data_len, M_ISCSI, M_WAITOK); + icl_pdu_get_data(ip, 0, data, ip->ip_data_len); + copyout(data, idr->idr_data_segment, ip->ip_data_len); + free(data, M_ISCSI); + } + + icl_pdu_free(ip); + + return (0); +} + +static int +iscsi_ioctl_daemon_close(struct iscsi_softc *sc, + struct iscsi_daemon_close *idc) +{ + struct iscsi_session *is; + + sx_slock(&sc->sc_lock); + TAILQ_FOREACH(is, &sc->sc_sessions, is_next) { + if (is->is_id == idc->idc_session_id) + break; + } + if (is == NULL) { + sx_sunlock(&sc->sc_lock); + return (ESRCH); + } + sx_sunlock(&sc->sc_lock); + + iscsi_session_reconnect(is); + + return (0); +} +#endif /* ICL_KERNEL_PROXY */ + +static void +iscsi_sanitize_session_conf(struct iscsi_session_conf *isc) +{ + /* + * Just make sure all the fields are null-terminated. + * + * XXX: This is not particularly secure. We should + * create our own conf and then copy in relevant + * fields. + */ + isc->isc_initiator[ISCSI_NAME_LEN - 1] = '\0'; + isc->isc_initiator_addr[ISCSI_ADDR_LEN - 1] = '\0'; + isc->isc_initiator_alias[ISCSI_ALIAS_LEN - 1] = '\0'; + isc->isc_target[ISCSI_NAME_LEN - 1] = '\0'; + isc->isc_target_addr[ISCSI_ADDR_LEN - 1] = '\0'; + isc->isc_user[ISCSI_NAME_LEN - 1] = '\0'; + isc->isc_secret[ISCSI_SECRET_LEN - 1] = '\0'; + isc->isc_mutual_user[ISCSI_NAME_LEN - 1] = '\0'; + isc->isc_mutual_secret[ISCSI_SECRET_LEN - 1] = '\0'; +} + +static int +iscsi_ioctl_session_add(struct iscsi_softc *sc, struct iscsi_session_add *isa) +{ + struct iscsi_session *is; + const struct iscsi_session *is2; + int error; + + iscsi_sanitize_session_conf(&isa->isa_conf); + + is = malloc(sizeof(*is), M_ISCSI, M_ZERO | M_WAITOK); + memcpy(&is->is_conf, &isa->isa_conf, sizeof(is->is_conf)); + + if (is->is_conf.isc_initiator[0] == '\0' || + is->is_conf.isc_target_addr[0] == '\0') { + free(is, M_ISCSI); + return (EINVAL); + } + + if ((is->is_conf.isc_discovery != 0 && is->is_conf.isc_target[0] != 0) || + (is->is_conf.isc_discovery == 0 && is->is_conf.isc_target[0] == 0)) { + free(is, M_ISCSI); + return (EINVAL); + } + + sx_xlock(&sc->sc_lock); + + /* + * Prevent duplicates. + */ + TAILQ_FOREACH(is2, &sc->sc_sessions, is_next) { + if (!!is->is_conf.isc_discovery != + !!is2->is_conf.isc_discovery) + continue; + + if (strcmp(is->is_conf.isc_target_addr, + is2->is_conf.isc_target_addr) != 0) + continue; + + if (is->is_conf.isc_discovery == 0 && + strcmp(is->is_conf.isc_target, + is2->is_conf.isc_target) != 0) + continue; + + sx_xunlock(&sc->sc_lock); + free(is, M_ISCSI); + return (EBUSY); + } + + is->is_conn = icl_conn_new(); + is->is_conn->ic_receive = iscsi_receive_callback; + is->is_conn->ic_error = iscsi_error_callback; + is->is_conn->ic_prv0 = is; + TAILQ_INIT(&is->is_outstanding); + TAILQ_INIT(&is->is_postponed); + mtx_init(&is->is_lock, "iscsi_lock", NULL, MTX_DEF); + cv_init(&is->is_maintenance_cv, "iscsi_mt"); +#ifdef ICL_KERNEL_PROXY + cv_init(&is->is_login_cv, "iscsi_login"); +#endif + + is->is_softc = sc; + sc->sc_last_session_id++; + is->is_id = sc->sc_last_session_id; + callout_init(&is->is_callout, 1); + callout_reset(&is->is_callout, 1 * hz, iscsi_callout, is); + TAILQ_INSERT_TAIL(&sc->sc_sessions, is, is_next); + + error = kthread_add(iscsi_maintenance_thread, is, NULL, NULL, 0, 0, "iscsimt"); + if (error != 0) { + ISCSI_SESSION_WARN(is, "kthread_add(9) failed with error %d", error); + return (error); + } + + /* + * Trigger immediate reconnection. + */ + is->is_waiting_for_iscsid = true; + strlcpy(is->is_reason, "Waiting for iscsid(8)", sizeof(is->is_reason)); + cv_signal(&sc->sc_cv); + + sx_xunlock(&sc->sc_lock); + + return (0); +} + +static bool +iscsi_session_conf_matches(unsigned int id1, const struct iscsi_session_conf *c1, + unsigned int id2, const struct iscsi_session_conf *c2) +{ + if (id2 == 0 && c2->isc_target[0] == '\0' && + c2->isc_target_addr[0] == '\0') + return (true); + if (id2 != 0 && id2 == id1) + return (true); + if (c2->isc_target[0] != '\0' && + strcmp(c1->isc_target, c2->isc_target) == 0) + return (true); + if (c2->isc_target_addr[0] != '\0' && + strcmp(c1->isc_target_addr, c2->isc_target_addr) == 0) + return (true); + return (false); +} + +static int +iscsi_ioctl_session_remove(struct iscsi_softc *sc, + struct iscsi_session_remove *isr) +{ + struct iscsi_session *is, *tmp; + bool found = false; + + iscsi_sanitize_session_conf(&isr->isr_conf); + + sx_xlock(&sc->sc_lock); + TAILQ_FOREACH_SAFE(is, &sc->sc_sessions, is_next, tmp) { + ISCSI_SESSION_LOCK(is); + if (iscsi_session_conf_matches(is->is_id, &is->is_conf, + isr->isr_session_id, &isr->isr_conf)) { + found = true; + iscsi_session_logout(is); + iscsi_session_terminate(is); + } + ISCSI_SESSION_UNLOCK(is); + } + sx_xunlock(&sc->sc_lock); + + if (!found) + return (ESRCH); + + return (0); +} + +static int +iscsi_ioctl_session_list(struct iscsi_softc *sc, struct iscsi_session_list *isl) +{ + int error; + unsigned int i = 0; + struct iscsi_session *is; + struct iscsi_session_state iss; + + sx_slock(&sc->sc_lock); + TAILQ_FOREACH(is, &sc->sc_sessions, is_next) { + if (i >= isl->isl_nentries) { + sx_sunlock(&sc->sc_lock); + return (EMSGSIZE); + } + memset(&iss, 0, sizeof(iss)); + memcpy(&iss.iss_conf, &is->is_conf, sizeof(iss.iss_conf)); + iss.iss_id = is->is_id; + strlcpy(iss.iss_target_alias, is->is_target_alias, sizeof(iss.iss_target_alias)); + strlcpy(iss.iss_reason, is->is_reason, sizeof(iss.iss_reason)); + + if (is->is_conn->ic_header_crc32c) + iss.iss_header_digest = ISCSI_DIGEST_CRC32C; + else + iss.iss_header_digest = ISCSI_DIGEST_NONE; + + if (is->is_conn->ic_data_crc32c) + iss.iss_data_digest = ISCSI_DIGEST_CRC32C; + else + iss.iss_data_digest = ISCSI_DIGEST_NONE; + + iss.iss_max_data_segment_length = is->is_max_data_segment_length; + iss.iss_immediate_data = is->is_immediate_data; + iss.iss_connected = is->is_connected; + + error = copyout(&iss, isl->isl_pstates + i, sizeof(iss)); + if (error != 0) { + sx_sunlock(&sc->sc_lock); + return (error); + } + i++; + } + sx_sunlock(&sc->sc_lock); + + isl->isl_nentries = i; + + return (0); +} + +static int +iscsi_ioctl(struct cdev *dev, u_long cmd, caddr_t arg, int mode, + struct thread *td) +{ + struct iscsi_softc *sc; + + sc = dev->si_drv1; + + switch (cmd) { + case ISCSIDWAIT: + return (iscsi_ioctl_daemon_wait(sc, + (struct iscsi_daemon_request *)arg)); + case ISCSIDHANDOFF: + return (iscsi_ioctl_daemon_handoff(sc, + (struct iscsi_daemon_handoff *)arg)); + case ISCSIDFAIL: + return (iscsi_ioctl_daemon_fail(sc, + (struct iscsi_daemon_fail *)arg)); +#ifdef ICL_KERNEL_PROXY + case ISCSIDCONNECT: + return (iscsi_ioctl_daemon_connect(sc, + (struct iscsi_daemon_connect *)arg)); + case ISCSIDSEND: + return (iscsi_ioctl_daemon_send(sc, + (struct iscsi_daemon_send *)arg)); + case ISCSIDRECEIVE: + return (iscsi_ioctl_daemon_receive(sc, + (struct iscsi_daemon_receive *)arg)); + case ISCSIDCLOSE: + return (iscsi_ioctl_daemon_close(sc, + (struct iscsi_daemon_close *)arg)); +#endif /* ICL_KERNEL_PROXY */ + case ISCSISADD: + return (iscsi_ioctl_session_add(sc, + (struct iscsi_session_add *)arg)); + case ISCSISREMOVE: + return (iscsi_ioctl_session_remove(sc, + (struct iscsi_session_remove *)arg)); + case ISCSISLIST: + return (iscsi_ioctl_session_list(sc, + (struct iscsi_session_list *)arg)); + default: + return (EINVAL); + } +} + +static uint64_t +iscsi_encode_lun(uint32_t lun) +{ + uint8_t encoded[8]; + uint64_t result; + + memset(encoded, 0, sizeof(encoded)); + + if (lun < 256) { + /* + * Peripheral device addressing. + */ + encoded[1] = lun; + } else if (lun < 16384) { + /* + * Flat space addressing. + */ + encoded[0] = 0x40; + encoded[0] |= (lun >> 8) & 0x3f; + encoded[1] = lun & 0xff; + } else { + /* + * Extended flat space addressing. + */ + encoded[0] = 0xd2; + encoded[1] = lun >> 16; + encoded[2] = lun >> 8; + encoded[3] = lun; + } + + memcpy(&result, encoded, sizeof(result)); + return (result); +} + +static struct iscsi_outstanding * +iscsi_outstanding_find(struct iscsi_session *is, uint32_t initiator_task_tag) +{ + struct iscsi_outstanding *io; + + ISCSI_SESSION_LOCK_ASSERT(is); + + TAILQ_FOREACH(io, &is->is_outstanding, io_next) { + if (io->io_initiator_task_tag == initiator_task_tag) + return (io); + } + return (NULL); +} + +static int +iscsi_outstanding_add(struct iscsi_session *is, + uint32_t initiator_task_tag, union ccb *ccb) +{ + struct iscsi_outstanding *io; + + ISCSI_SESSION_LOCK_ASSERT(is); + + KASSERT(iscsi_outstanding_find(is, initiator_task_tag) == NULL, + ("initiator_task_tag 0x%x already added", initiator_task_tag)); + + io = uma_zalloc(iscsi_outstanding_zone, M_NOWAIT | M_ZERO); + if (io == NULL) { + ISCSI_SESSION_WARN(is, "failed to allocate %zd bytes", sizeof(*io)); + return (ENOMEM); + } + io->io_initiator_task_tag = initiator_task_tag; + io->io_ccb = ccb; + TAILQ_INSERT_TAIL(&is->is_outstanding, io, io_next); + return (0); +} + +static void +iscsi_outstanding_remove(struct iscsi_session *is, struct iscsi_outstanding *io) +{ + + ISCSI_SESSION_LOCK_ASSERT(is); + + TAILQ_REMOVE(&is->is_outstanding, io, io_next); + uma_zfree(iscsi_outstanding_zone, io); +} + +static void +iscsi_action_scsiio(struct iscsi_session *is, union ccb *ccb) +{ + struct icl_pdu *request; + struct iscsi_bhs_scsi_command *bhssc; + struct ccb_scsiio *csio; + size_t len; + int error; + + ISCSI_SESSION_LOCK_ASSERT(is); + +#if 0 + KASSERT(is->is_login_phase == false, ("%s called during Login Phase", __func__)); +#else + if (is->is_login_phase) { + ISCSI_SESSION_DEBUG(is, "called during login phase"); + if ((ccb->ccb_h.status & CAM_DEV_QFRZN) == 0) { + xpt_freeze_devq(ccb->ccb_h.path, 1); + ISCSI_SESSION_DEBUG(is, "freezing devq"); + } + ccb->ccb_h.status = CAM_REQ_ABORTED | CAM_DEV_QFRZN; + xpt_done(ccb); + return; + } +#endif + + request = icl_pdu_new_bhs(is->is_conn, M_NOWAIT); + if (request == NULL) { + if ((ccb->ccb_h.status & CAM_DEV_QFRZN) == 0) { + xpt_freeze_devq(ccb->ccb_h.path, 1); + ISCSI_SESSION_DEBUG(is, "freezing devq"); + } + ccb->ccb_h.status = CAM_RESRC_UNAVAIL | CAM_DEV_QFRZN; + xpt_done(ccb); + return; + } + + csio = &ccb->csio; + bhssc = (struct iscsi_bhs_scsi_command *)request->ip_bhs; + bhssc->bhssc_opcode = ISCSI_BHS_OPCODE_SCSI_COMMAND; + bhssc->bhssc_flags |= BHSSC_FLAGS_F; + switch (csio->ccb_h.flags & CAM_DIR_MASK) { + case CAM_DIR_IN: + bhssc->bhssc_flags |= BHSSC_FLAGS_R; + break; + case CAM_DIR_OUT: + bhssc->bhssc_flags |= BHSSC_FLAGS_W; + break; + } + + switch (csio->tag_action) { + case MSG_HEAD_OF_Q_TAG: + bhssc->bhssc_flags |= BHSSC_FLAGS_ATTR_HOQ; + break; + break; + case MSG_ORDERED_Q_TAG: + bhssc->bhssc_flags |= BHSSC_FLAGS_ATTR_ORDERED; + break; + case MSG_ACA_TASK: + bhssc->bhssc_flags |= BHSSC_FLAGS_ATTR_ACA; + break; + case CAM_TAG_ACTION_NONE: + case MSG_SIMPLE_Q_TAG: + default: + bhssc->bhssc_flags |= BHSSC_FLAGS_ATTR_SIMPLE; + break; + } + + bhssc->bhssc_lun = iscsi_encode_lun(csio->ccb_h.target_lun); + bhssc->bhssc_initiator_task_tag = is->is_initiator_task_tag; + is->is_initiator_task_tag++; + bhssc->bhssc_expected_data_transfer_length = htonl(csio->dxfer_len); + KASSERT(csio->cdb_len <= sizeof(bhssc->bhssc_cdb), + ("unsupported CDB size %zd", (size_t)csio->cdb_len)); + + if (csio->ccb_h.flags & CAM_CDB_POINTER) + memcpy(&bhssc->bhssc_cdb, csio->cdb_io.cdb_ptr, csio->cdb_len); + else + memcpy(&bhssc->bhssc_cdb, csio->cdb_io.cdb_bytes, csio->cdb_len); + + error = iscsi_outstanding_add(is, bhssc->bhssc_initiator_task_tag, ccb); + if (error != 0) { + icl_pdu_free(request); + if ((ccb->ccb_h.status & CAM_DEV_QFRZN) == 0) { + xpt_freeze_devq(ccb->ccb_h.path, 1); + ISCSI_SESSION_DEBUG(is, "freezing devq"); + } + ccb->ccb_h.status = CAM_RESRC_UNAVAIL | CAM_DEV_QFRZN; + xpt_done(ccb); + return; + } + + if (is->is_immediate_data && + (csio->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_OUT) { + len = csio->dxfer_len; + //ISCSI_SESSION_DEBUG(is, "adding %zd of immediate data", len); + if (len > is->is_first_burst_length) { + ISCSI_SESSION_DEBUG(is, "len %zd -> %zd", len, is->is_first_burst_length); + len = is->is_first_burst_length; + } + + error = icl_pdu_append_data(request, csio->data_ptr, len, M_NOWAIT); + if (error != 0) { + icl_pdu_free(request); + if ((ccb->ccb_h.status & CAM_DEV_QFRZN) == 0) { + xpt_freeze_devq(ccb->ccb_h.path, 1); + ISCSI_SESSION_DEBUG(is, "freezing devq"); + } + ccb->ccb_h.status = CAM_RESRC_UNAVAIL | CAM_DEV_QFRZN; + xpt_done(ccb); + return; + } + } + iscsi_pdu_queue_locked(request); +} + +static void +iscsi_action(struct cam_sim *sim, union ccb *ccb) +{ + struct iscsi_session *is; + + is = cam_sim_softc(sim); + + ISCSI_SESSION_LOCK_ASSERT(is); + + if (is->is_terminating) { + ISCSI_SESSION_DEBUG(is, "called during termination"); + ccb->ccb_h.status = CAM_DEV_NOT_THERE; + xpt_done(ccb); + return; + } + + switch (ccb->ccb_h.func_code) { + case XPT_PATH_INQ: + { + struct ccb_pathinq *cpi = &ccb->cpi; + + cpi->version_num = 1; + cpi->hba_inquiry = PI_TAG_ABLE; + cpi->target_sprt = 0; + //cpi->hba_misc = PIM_NOBUSRESET; + cpi->hba_misc = 0; + cpi->hba_eng_cnt = 0; + cpi->max_target = 0; + cpi->max_lun = 255; + //cpi->initiator_id = 0; /* XXX */ + cpi->initiator_id = 64; /* XXX */ + strlcpy(cpi->sim_vid, "FreeBSD", SIM_IDLEN); + strlcpy(cpi->hba_vid, "iSCSI", HBA_IDLEN); + strlcpy(cpi->dev_name, cam_sim_name(sim), DEV_IDLEN); + cpi->unit_number = cam_sim_unit(sim); + cpi->bus_id = cam_sim_bus(sim); + cpi->base_transfer_speed = 150000; /* XXX */ + cpi->transport = XPORT_ISCSI; + cpi->transport_version = 0; + cpi->protocol = PROTO_SCSI; + cpi->protocol_version = SCSI_REV_SPC3; + cpi->maxio = MAXPHYS; + cpi->ccb_h.status = CAM_REQ_CMP; + break; + } + case XPT_CALC_GEOMETRY: + cam_calc_geometry(&ccb->ccg, /*extended*/1); + ccb->ccb_h.status = CAM_REQ_CMP; + break; +#if 0 + /* + * XXX: What's the point? + */ + case XPT_RESET_BUS: + case XPT_ABORT: + case XPT_TERM_IO: + ISCSI_SESSION_DEBUG(is, "faking success for reset, abort, or term_io"); + ccb->ccb_h.status = CAM_REQ_CMP; + break; +#endif + case XPT_SCSI_IO: + iscsi_action_scsiio(is, ccb); + return; + default: +#if 0 + ISCSI_SESSION_DEBUG(is, "got unsupported code 0x%x", ccb->ccb_h.func_code); +#endif + ccb->ccb_h.status = CAM_FUNC_NOTAVAIL; + break; + } + xpt_done(ccb); +} + +static void +iscsi_poll(struct cam_sim *sim) +{ + + KASSERT(0, ("%s: you're not supposed to be here", __func__)); +} + +static void +iscsi_shutdown(struct iscsi_softc *sc) +{ + struct iscsi_session *is; + + ISCSI_DEBUG("removing all sessions due to shutdown"); + + sx_slock(&sc->sc_lock); + TAILQ_FOREACH(is, &sc->sc_sessions, is_next) + iscsi_session_terminate(is); + sx_sunlock(&sc->sc_lock); +} + +static int +iscsi_load(void) +{ + int error; + + sc = malloc(sizeof(*sc), M_ISCSI, M_ZERO | M_WAITOK); + sx_init(&sc->sc_lock, "iscsi"); + TAILQ_INIT(&sc->sc_sessions); + cv_init(&sc->sc_cv, "iscsi_cv"); + + iscsi_outstanding_zone = uma_zcreate("iscsi_outstanding", + sizeof(struct iscsi_outstanding), NULL, NULL, NULL, NULL, + UMA_ALIGN_PTR, UMA_ZONE_NOFREE); + + error = make_dev_p(MAKEDEV_CHECKNAME, &sc->sc_cdev, &iscsi_cdevsw, + NULL, UID_ROOT, GID_WHEEL, 0600, "iscsi"); + if (error != 0) { + ISCSI_WARN("failed to create device node, error %d", error); + sx_destroy(&sc->sc_lock); + cv_destroy(&sc->sc_cv); + uma_zdestroy(iscsi_outstanding_zone); + free(sc, M_ISCSI); + return (error); + } + sc->sc_cdev->si_drv1 = sc; + + /* + * XXX: For some reason this doesn't do its job; active sessions still hang out there + * after final sync, making the reboot effectively hang. + */ + sc->sc_shutdown_eh = EVENTHANDLER_REGISTER(shutdown_post_sync, iscsi_shutdown, sc, SHUTDOWN_PRI_DEFAULT); + + return (0); +} + +static int +iscsi_unload(void) +{ + /* + * XXX: kldunload hangs on "devdrn". + */ + struct iscsi_session *is, *tmp; + + ISCSI_DEBUG("removing device node"); + destroy_dev(sc->sc_cdev); + ISCSI_DEBUG("device node removed"); + + EVENTHANDLER_DEREGISTER(shutdown_post_sync, sc->sc_shutdown_eh); + + sx_slock(&sc->sc_lock); + TAILQ_FOREACH_SAFE(is, &sc->sc_sessions, is_next, tmp) + iscsi_session_terminate(is); + while(!TAILQ_EMPTY(&sc->sc_sessions)) { + ISCSI_DEBUG("waiting for sessions to terminate"); + cv_wait(&sc->sc_cv, &sc->sc_lock); + } + ISCSI_DEBUG("all sessions terminated"); + sx_sunlock(&sc->sc_lock); + + uma_zdestroy(iscsi_outstanding_zone); + sx_destroy(&sc->sc_lock); + cv_destroy(&sc->sc_cv); + free(sc, M_ISCSI); + return (0); +} + +static int +iscsi_quiesce(void) +{ + sx_slock(&sc->sc_lock); + if (!TAILQ_EMPTY(&sc->sc_sessions)) { + sx_sunlock(&sc->sc_lock); + return (EBUSY); + } + sx_sunlock(&sc->sc_lock); + return (0); +} + +static int +iscsi_modevent(module_t mod, int what, void *arg) +{ + int error; + + switch (what) { + case MOD_LOAD: + error = iscsi_load(); + break; + case MOD_UNLOAD: + error = iscsi_unload(); + break; + case MOD_QUIESCE: + error = iscsi_quiesce(); + break; + default: + error = EINVAL; + break; + } + return (error); +} + +moduledata_t iscsi_data = { + "iscsi", + iscsi_modevent, + 0 +}; + +DECLARE_MODULE(iscsi, iscsi_data, SI_SUB_DRIVERS, SI_ORDER_MIDDLE); +MODULE_DEPEND(iscsi, cam, 1, 1, 1); +MODULE_DEPEND(iscsi, icl, 1, 1, 1); diff --git a/sys/dev/iscsi/iscsi.h b/sys/dev/iscsi/iscsi.h new file mode 100644 index 00000000000..dd78852b471 --- /dev/null +++ b/sys/dev/iscsi/iscsi.h @@ -0,0 +1,135 @@ +/*- + * Copyright (c) 2012 The FreeBSD Foundation + * All rights reserved. + * + * This software was developed by Edward Tomasz Napierala under sponsorship + * from the FreeBSD Foundation. + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + * $FreeBSD$ + */ + +#ifndef ISCSI_H +#define ISCSI_H + +struct iscsi_softc; +struct icl_conn; + +#define ISCSI_NAME_LEN 224 /* 223 bytes, by RFC 3720, + '\0' */ +#define ISCSI_ADDR_LEN 47 /* INET6_ADDRSTRLEN + '\0' */ +#define ISCSI_SECRET_LEN 17 /* 16 + '\0' */ + +struct iscsi_outstanding { + TAILQ_ENTRY(iscsi_outstanding) io_next; + union ccb *io_ccb; + size_t io_received; + uint32_t io_initiator_task_tag; + uint32_t io_datasn; +}; + +struct iscsi_session { + TAILQ_ENTRY(iscsi_session) is_next; + + struct icl_conn *is_conn; + struct mtx is_lock; + + uint32_t is_statsn; + uint32_t is_cmdsn; + uint32_t is_expcmdsn; + uint32_t is_maxcmdsn; + uint32_t is_initiator_task_tag; + int is_header_digest; + int is_data_digest; + int is_initial_r2t; + size_t is_max_burst_length; + size_t is_first_burst_length; + uint8_t is_isid[6]; + bool is_immediate_data; + size_t is_max_data_segment_length; + char is_target_alias[ISCSI_ALIAS_LEN]; + + TAILQ_HEAD(, iscsi_outstanding) is_outstanding; + TAILQ_HEAD(, icl_pdu) is_postponed; + + struct callout is_callout; + unsigned int is_timeout; + + /* + * XXX: This could be rewritten using a single variable, + * but somehow it results in uglier code. + */ + /* + * We're waiting for iscsid(8); after iscsid_timeout + * expires, kernel will wake up an iscsid(8) to handle + * the session. + */ + bool is_waiting_for_iscsid; + + /* + * Some iscsid(8) instance is handling the session; + * after login_timeout expires, kernel will wake up + * another iscsid(8) to handle the session. + */ + bool is_login_phase; + + /* + * We're in the process of removing the iSCSI session. + */ + bool is_terminating; + + /* + * We're waiting for the maintenance thread to do some + * reconnection tasks. + */ + bool is_reconnecting; + + bool is_connected; + + struct cam_devq *is_devq; + struct cam_sim *is_sim; + struct cam_path *is_path; + struct cv is_maintenance_cv; + struct iscsi_softc *is_softc; + unsigned int is_id; + struct iscsi_session_conf is_conf; + bool is_simq_frozen; + + char is_reason[ISCSI_REASON_LEN]; + +#ifdef ICL_KERNEL_PROXY + struct cv is_login_cv;; + struct icl_pdu *is_login_pdu; +#endif +}; + +struct iscsi_softc { + device_t sc_dev; + struct sx sc_lock; + struct cdev *sc_cdev; + TAILQ_HEAD(, iscsi_session) sc_sessions; + struct cv sc_cv; + unsigned int sc_last_session_id; + eventhandler_tag sc_shutdown_eh; +}; + +#endif /* !ISCSI_H */ diff --git a/sys/dev/iscsi/iscsi_ioctl.h b/sys/dev/iscsi/iscsi_ioctl.h new file mode 100644 index 00000000000..61c48ccceea --- /dev/null +++ b/sys/dev/iscsi/iscsi_ioctl.h @@ -0,0 +1,201 @@ +/*- + * Copyright (c) 2012 The FreeBSD Foundation + * All rights reserved. + * + * This software was developed by Edward Tomasz Napierala under sponsorship + * from the FreeBSD Foundation. + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + * $FreeBSD$ + */ + +#ifndef ISCSI_IOCTL_H +#define ISCSI_IOCTL_H + +#ifdef ICL_KERNEL_PROXY +#include +#endif + +#define ISCSI_PATH "/dev/iscsi" +#define ISCSI_MAX_DATA_SEGMENT_LENGTH (128 * 1024) + +#define ISCSI_NAME_LEN 224 /* 223 bytes, by RFC 3720, + '\0' */ +#define ISCSI_ADDR_LEN 47 /* INET6_ADDRSTRLEN + '\0' */ +#define ISCSI_ALIAS_LEN 256 /* XXX: Where did it come from? */ +#define ISCSI_SECRET_LEN 17 /* 16 + '\0' */ +#define ISCSI_REASON_LEN 64 + +#define ISCSI_DIGEST_NONE 0 +#define ISCSI_DIGEST_CRC32C 1 + +/* + * Session configuration, set when adding the session. + */ +struct iscsi_session_conf { + char isc_initiator[ISCSI_NAME_LEN]; + char isc_initiator_addr[ISCSI_ADDR_LEN]; + char isc_initiator_alias[ISCSI_ALIAS_LEN]; + char isc_target[ISCSI_NAME_LEN]; + char isc_target_addr[ISCSI_ADDR_LEN]; + char isc_user[ISCSI_NAME_LEN]; + char isc_secret[ISCSI_SECRET_LEN]; + char isc_mutual_user[ISCSI_NAME_LEN]; + char isc_mutual_secret[ISCSI_SECRET_LEN]; + int isc_discovery; + int isc_header_digest; + int isc_data_digest; + int isc_iser; + int isc_spare[4]; +}; + +/* + * Session state, negotiated by iscsid(8) and queried by iscsictl(8). + */ +struct iscsi_session_state { + struct iscsi_session_conf iss_conf; + unsigned int iss_id; + char iss_target_alias[ISCSI_ALIAS_LEN]; + int iss_header_digest; + int iss_data_digest; + int iss_max_data_segment_length; + int iss_immediate_data; + int iss_connected; + char iss_reason[ISCSI_REASON_LEN]; + int iss_spare[4]; +}; + +/* + * For use with iscsid(8). + */ + +struct iscsi_daemon_request { + unsigned int idr_session_id; + struct iscsi_session_conf idr_conf; + int idr_spare[4]; +}; + +struct iscsi_daemon_handoff { + unsigned int idh_session_id; + int idh_socket; + char idh_target_alias[ISCSI_ALIAS_LEN]; + uint8_t idh_isid[6]; + uint32_t idh_statsn; + int idh_header_digest; + int idh_data_digest; + int idh_initial_r2t; + int idh_immediate_data; + size_t idh_max_data_segment_length; + size_t idh_max_burst_length; + size_t idh_first_burst_length; +}; + +struct iscsi_daemon_fail { + unsigned int idf_session_id; + char idf_reason[ISCSI_REASON_LEN]; +}; + +#define ISCSIDWAIT _IOR('I', 0x01, struct iscsi_daemon_request) +#define ISCSIDHANDOFF _IOW('I', 0x02, struct iscsi_daemon_handoff) +#define ISCSIDFAIL _IOW('I', 0x03, struct iscsi_daemon_fail) + +#ifdef ICL_KERNEL_PROXY + +/* + * When ICL_KERNEL_PROXY is not defined, the iscsid(8) is responsible + * for creating the socket, connecting, performing Login Phase using + * socked in the usual userspace way, and then passing the socket file + * descriptor to the kernel part using ISCSIDHANDOFF. + * + * When ICL_KERNEL_PROXY is defined, the iscsid(8) creates the session + * using ISCSICONNECT, performs Login Phase using ISCSISEND/ISCSIRECEIVE + * instead of read(2)/write(2), and then calls ISCSIDHANDOFF with + * idh_socket set to 0. + * + * The purpose of ICL_KERNEL_PROXY is to workaround the fact that, + * at this time, it's not possible to do iWARP (RDMA) in userspace. + */ + +struct iscsi_daemon_connect { + int idc_session_id; + int idc_iser; + int idc_domain; + int idc_socktype; + int idc_protocol; + struct sockaddr *idc_from_addr; + socklen_t idc_from_addrlen; + struct sockaddr *idc_to_addr; + socklen_t idc_to_addrlen; +}; + +struct iscsi_daemon_send { + int ids_session_id; + void *ids_bhs; + size_t ids_spare; + void *ids_spare2; + size_t ids_data_segment_len; + void *ids_data_segment; +}; + +struct iscsi_daemon_receive { + int idr_session_id; + void *idr_bhs; + size_t idr_spare; + void *idr_spare2; + size_t idr_data_segment_len; + void *idr_data_segment; +}; + +struct iscsi_daemon_close { + int idc_session_id; +}; + +#define ISCSIDCONNECT _IOWR('I', 0x04, struct iscsi_daemon_connect) +#define ISCSIDSEND _IOWR('I', 0x05, struct iscsi_daemon_send) +#define ISCSIDRECEIVE _IOWR('I', 0x06, struct iscsi_daemon_receive) +#define ISCSIDCLOSE _IOWR('I', 0x07, struct iscsi_daemon_close) + +#endif /* ICL_KERNEL_PROXY */ + +/* + * For use with iscsictl(8). + */ + +struct iscsi_session_add { + struct iscsi_session_conf isa_conf; +}; + +struct iscsi_session_remove { + unsigned int isr_session_id; + struct iscsi_session_conf isr_conf; +}; + +struct iscsi_session_list { + unsigned int isl_nentries; + struct iscsi_session_state *isl_pstates; +}; + +#define ISCSISADD _IOW('I', 0x11, struct iscsi_session_add) +#define ISCSISREMOVE _IOW('I', 0x12, struct iscsi_session_remove) +#define ISCSISLIST _IOWR('I', 0x13, struct iscsi_session_list) + +#endif /* !ISCSI_IOCTL_H */ diff --git a/sys/dev/iscsi/iscsi_proto.h b/sys/dev/iscsi/iscsi_proto.h new file mode 100644 index 00000000000..97d73a7a074 --- /dev/null +++ b/sys/dev/iscsi/iscsi_proto.h @@ -0,0 +1,439 @@ +/*- + * Copyright (c) 2012 The FreeBSD Foundation + * All rights reserved. + * + * This software was developed by Edward Tomasz Napierala under sponsorship + * from the FreeBSD Foundation. + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + * $FreeBSD$ + */ + +#ifndef ISCSI_PROTO_H +#define ISCSI_PROTO_H + +#ifndef CTASSERT +#define CTASSERT(x) _CTASSERT(x, __LINE__) +#define _CTASSERT(x, y) __CTASSERT(x, y) +#define __CTASSERT(x, y) typedef char __assert_ ## y [(x) ? 1 : -1] +#endif + +#define ISCSI_BHS_SIZE 48 +#define ISCSI_HEADER_DIGEST_SIZE 4 +#define ISCSI_DATA_DIGEST_SIZE 4 + +#define ISCSI_BHS_OPCODE_IMMEDIATE 0x40 + +#define ISCSI_BHS_OPCODE_NOP_OUT 0x00 +#define ISCSI_BHS_OPCODE_SCSI_COMMAND 0x01 +#define ISCSI_BHS_OPCODE_TASK_REQUEST 0x02 +#define ISCSI_BHS_OPCODE_LOGIN_REQUEST 0x03 +#define ISCSI_BHS_OPCODE_TEXT_REQUEST 0x04 +#define ISCSI_BHS_OPCODE_SCSI_DATA_OUT 0x05 +#define ISCSI_BHS_OPCODE_LOGOUT_REQUEST 0x06 + +#define ISCSI_BHS_OPCODE_NOP_IN 0x20 +#define ISCSI_BHS_OPCODE_SCSI_RESPONSE 0x21 +#define ISCSI_BHS_OPCODE_TASK_RESPONSE 0x22 +#define ISCSI_BHS_OPCODE_LOGIN_RESPONSE 0x23 +#define ISCSI_BHS_OPCODE_TEXT_RESPONSE 0x24 +#define ISCSI_BHS_OPCODE_SCSI_DATA_IN 0x25 +#define ISCSI_BHS_OPCODE_LOGOUT_RESPONSE 0x26 +#define ISCSI_BHS_OPCODE_R2T 0x31 +#define ISCSI_BHS_OPCODE_ASYNC_MESSAGE 0x32 +#define ISCSI_BHS_OPCODE_REJECT 0x3f + +struct iscsi_bhs { + uint8_t bhs_opcode; + uint8_t bhs_opcode_specific1[3]; + uint8_t bhs_total_ahs_len; + uint8_t bhs_data_segment_len[3]; + uint64_t bhs_lun; + uint8_t bhs_inititator_task_tag[4]; + uint8_t bhs_opcode_specific4[28]; +}; +CTASSERT(sizeof(struct iscsi_bhs) == ISCSI_BHS_SIZE); + +#define BHSSC_FLAGS_F 0x80 +#define BHSSC_FLAGS_R 0x40 +#define BHSSC_FLAGS_W 0x20 +#define BHSSC_FLAGS_ATTR 0x07 + +#define BHSSC_FLAGS_ATTR_UNTAGGED 0 +#define BHSSC_FLAGS_ATTR_SIMPLE 1 +#define BHSSC_FLAGS_ATTR_ORDERED 2 +#define BHSSC_FLAGS_ATTR_HOQ 3 +#define BHSSC_FLAGS_ATTR_ACA 4 + +struct iscsi_bhs_scsi_command { + uint8_t bhssc_opcode; + uint8_t bhssc_flags; + uint8_t bhssc_reserved[2]; + uint8_t bhssc_total_ahs_len; + uint8_t bhssc_data_segment_len[3]; + uint64_t bhssc_lun; + uint32_t bhssc_initiator_task_tag; + uint32_t bhssc_expected_data_transfer_length; + uint32_t bhssc_cmdsn; + uint32_t bhssc_expstatsn; + uint8_t bhssc_cdb[16]; +}; +CTASSERT(sizeof(struct iscsi_bhs_scsi_command) == ISCSI_BHS_SIZE); + +#define BHSSR_FLAGS_RESIDUAL_UNDERFLOW 0x02 +#define BHSSR_FLAGS_RESIDUAL_OVERFLOW 0x04 + +#define BHSSR_RESPONSE_COMMAND_COMPLETED 0x00 + +struct iscsi_bhs_scsi_response { + uint8_t bhssr_opcode; + uint8_t bhssr_flags; + uint8_t bhssr_response; + uint8_t bhssr_status; + uint8_t bhssr_total_ahs_len; + uint8_t bhssr_data_segment_len[3]; + uint64_t bhssr_reserved; + uint32_t bhssr_initiator_task_tag; + uint32_t bhssr_snack_tag; + uint32_t bhssr_statsn; + uint32_t bhssr_expcmdsn; + uint32_t bhssr_maxcmdsn; + uint32_t bhssr_expdatasn; + uint32_t bhssr_bidirectional_read_residual_count; + uint32_t bhssr_residual_count; +}; +CTASSERT(sizeof(struct iscsi_bhs_scsi_response) == ISCSI_BHS_SIZE); + +#define BHSTMR_FUNCTION_ABORT_TASK 1 +#define BHSTMR_FUNCTION_ABORT_TASK_SET 2 +#define BHSTMR_FUNCTION_CLEAR_ACA 3 +#define BHSTMR_FUNCTION_CLEAR_TASK_SET 4 +#define BHSTMR_FUNCTION_LOGICAL_UNIT_RESET 5 +#define BHSTMR_FUNCTION_TARGET_WARM_RESET 6 +#define BHSTMR_FUNCTION_TARGET_COLD_RESET 7 +#define BHSTMR_FUNCTION_TASK_REASSIGN 8 + +struct iscsi_bhs_task_management_request { + uint8_t bhstmr_opcode; + uint8_t bhstmr_function; + uint8_t bhstmr_reserved[2]; + uint8_t bhstmr_total_ahs_len; + uint8_t bhstmr_data_segment_len[3]; + uint64_t bhstmr_lun; + uint32_t bhstmr_initiator_task_tag; + uint32_t bhstmr_referenced_task_tag; + uint32_t bhstmr_cmdsn; + uint32_t bhstmr_expstatsn; + uint32_t bhstmr_refcmdsn; + uint32_t bhstmr_expdatasn; + uint64_t bhstmr_reserved2; +}; +CTASSERT(sizeof(struct iscsi_bhs_task_management_request) == ISCSI_BHS_SIZE); + +#define BHSTMR_RESPONSE_FUNCTION_COMPLETE 0 +#define BHSTMR_RESPONSE_FUNCTION_NOT_SUPPORTED 5 + +struct iscsi_bhs_task_management_response { + uint8_t bhstmr_opcode; + uint8_t bhstmr_flags; + uint8_t bhstmr_response; + uint8_t bhstmr_reserved; + uint8_t bhstmr_total_ahs_len; + uint8_t bhstmr_data_segment_len[3]; + uint64_t bhstmr_reserved2; + uint32_t bhstmr_initiator_task_tag; + uint32_t bhstmr_reserved3; + uint32_t bhstmr_statsn; + uint32_t bhstmr_expcmdsn; + uint32_t bhstmr_maxcmdsn; + uint8_t bhstmr_reserved4[12]; +}; +CTASSERT(sizeof(struct iscsi_bhs_task_management_response) == ISCSI_BHS_SIZE); + +#define BHSLR_FLAGS_TRANSIT 0x80 +#define BHSLR_FLAGS_CONTINUE 0x40 + +#define BHSLR_STAGE_SECURITY_NEGOTIATION 0 +#define BHSLR_STAGE_OPERATIONAL_NEGOTIATION 1 +#define BHSLR_STAGE_FULL_FEATURE_PHASE 3 /* Yes, 3. */ + +struct iscsi_bhs_login_request { + uint8_t bhslr_opcode; + uint8_t bhslr_flags; + uint8_t bhslr_version_max; + uint8_t bhslr_version_min; + uint8_t bhslr_total_ahs_len; + uint8_t bhslr_data_segment_len[3]; + uint8_t bhslr_isid[6]; + uint16_t bhslr_tsih; + uint32_t bhslr_initiator_task_tag; + uint16_t bhslr_cid; + uint16_t bhslr_reserved; + uint32_t bhslr_cmdsn; + uint32_t bhslr_expstatsn; + uint8_t bhslr_reserved2[16]; +}; +CTASSERT(sizeof(struct iscsi_bhs_login_request) == ISCSI_BHS_SIZE); + +struct iscsi_bhs_login_response { + uint8_t bhslr_opcode; + uint8_t bhslr_flags; + uint8_t bhslr_version_max; + uint8_t bhslr_version_active; + uint8_t bhslr_total_ahs_len; + uint8_t bhslr_data_segment_len[3]; + uint8_t bhslr_isid[6]; + uint16_t bhslr_tsih; + uint32_t bhslr_initiator_task_tag; + uint32_t bhslr_reserved; + uint32_t bhslr_statsn; + uint32_t bhslr_expcmdsn; + uint32_t bhslr_maxcmdsn; + uint8_t bhslr_status_class; + uint8_t bhslr_status_detail; + uint16_t bhslr_reserved2; + uint8_t bhslr_reserved3[8]; +}; +CTASSERT(sizeof(struct iscsi_bhs_login_response) == ISCSI_BHS_SIZE); + +#define BHSTR_FLAGS_FINAL 0x80 +#define BHSTR_FLAGS_CONTINUE 0x40 + +struct iscsi_bhs_text_request { + uint8_t bhstr_opcode; + uint8_t bhstr_flags; + uint16_t bhstr_reserved; + uint8_t bhstr_total_ahs_len; + uint8_t bhstr_data_segment_len[3]; + uint64_t bhstr_lun; + uint32_t bhstr_initiator_task_tag; + uint32_t bhstr_target_transfer_tag; + uint32_t bhstr_cmdsn; + uint32_t bhstr_expstatsn; + uint8_t bhstr_reserved2[16]; +}; +CTASSERT(sizeof(struct iscsi_bhs_text_request) == ISCSI_BHS_SIZE); + +struct iscsi_bhs_text_response { + uint8_t bhstr_opcode; + uint8_t bhstr_flags; + uint16_t bhstr_reserved; + uint8_t bhstr_total_ahs_len; + uint8_t bhstr_data_segment_len[3]; + uint64_t bhstr_lun; + uint32_t bhstr_initiator_task_tag; + uint32_t bhstr_target_transfer_tag; + uint32_t bhstr_statsn; + uint32_t bhstr_expcmdsn; + uint32_t bhstr_maxcmdsn; + uint8_t bhstr_reserved2[12]; +}; +CTASSERT(sizeof(struct iscsi_bhs_text_response) == ISCSI_BHS_SIZE); + +#define BHSDO_FLAGS_F 0x80 + +struct iscsi_bhs_data_out { + uint8_t bhsdo_opcode; + uint8_t bhsdo_flags; + uint8_t bhsdo_reserved[2]; + uint8_t bhsdo_total_ahs_len; + uint8_t bhsdo_data_segment_len[3]; + uint64_t bhsdo_lun; + uint32_t bhsdo_initiator_task_tag; + uint32_t bhsdo_target_transfer_tag; + uint32_t bhsdo_reserved2; + uint32_t bhsdo_expstatsn; + uint32_t bhsdo_reserved3; + uint32_t bhsdo_datasn; + uint32_t bhsdo_buffer_offset; + uint32_t bhsdo_reserved4; +}; +CTASSERT(sizeof(struct iscsi_bhs_data_out) == ISCSI_BHS_SIZE); + +#define BHSDI_FLAGS_F 0x80 +#define BHSDI_FLAGS_A 0x40 +#define BHSDI_FLAGS_O 0x04 +#define BHSDI_FLAGS_U 0x02 +#define BHSDI_FLAGS_S 0x01 + +struct iscsi_bhs_data_in { + uint8_t bhsdi_opcode; + uint8_t bhsdi_flags; + uint8_t bhsdi_reserved; + uint8_t bhsdi_status; + uint8_t bhsdi_total_ahs_len; + uint8_t bhsdi_data_segment_len[3]; + uint64_t bhsdi_lun; + uint32_t bhsdi_initiator_task_tag; + uint32_t bhsdi_target_transfer_tag; + uint32_t bhsdi_statsn; + uint32_t bhsdi_expcmdsn; + uint32_t bhsdi_maxcmdsn; + uint32_t bhsdi_datasn; + uint32_t bhsdi_buffer_offset; + uint32_t bhsdi_residual_count; +}; +CTASSERT(sizeof(struct iscsi_bhs_data_in) == ISCSI_BHS_SIZE); + +struct iscsi_bhs_r2t { + uint8_t bhsr2t_opcode; + uint8_t bhsr2t_flags; + uint16_t bhsr2t_reserved; + uint8_t bhsr2t_total_ahs_len; + uint8_t bhsr2t_data_segment_len[3]; + uint64_t bhsr2t_lun; + uint32_t bhsr2t_initiator_task_tag; + uint32_t bhsr2t_target_transfer_tag; + uint32_t bhsr2t_statsn; + uint32_t bhsr2t_expcmdsn; + uint32_t bhsr2t_maxcmdsn; + uint32_t bhsr2t_r2tsn; + uint32_t bhsr2t_buffer_offset; + uint32_t bhsr2t_desired_data_transfer_length; +}; +CTASSERT(sizeof(struct iscsi_bhs_r2t) == ISCSI_BHS_SIZE); + +struct iscsi_bhs_nop_out { + uint8_t bhsno_opcode; + uint8_t bhsno_flags; + uint16_t bhsno_reserved; + uint8_t bhsno_total_ahs_len; + uint8_t bhsno_data_segment_len[3]; + uint64_t bhsno_lun; + uint32_t bhsno_initiator_task_tag; + uint32_t bhsno_target_transfer_tag; + uint32_t bhsno_cmdsn; + uint32_t bhsno_expstatsn; + uint8_t bhsno_reserved2[16]; +}; +CTASSERT(sizeof(struct iscsi_bhs_nop_out) == ISCSI_BHS_SIZE); + +struct iscsi_bhs_nop_in { + uint8_t bhsni_opcode; + uint8_t bhsni_flags; + uint16_t bhsni_reserved; + uint8_t bhsni_total_ahs_len; + uint8_t bhsni_data_segment_len[3]; + uint64_t bhsni_lun; + uint32_t bhsni_initiator_task_tag; + uint32_t bhsni_target_transfer_tag; + uint32_t bhsni_statsn; + uint32_t bhsni_expcmdsn; + uint32_t bhsni_maxcmdsn; + uint8_t bhsno_reserved2[12]; +}; +CTASSERT(sizeof(struct iscsi_bhs_nop_in) == ISCSI_BHS_SIZE); + +#define BHSLR_REASON_CLOSE_SESSION 0 +#define BHSLR_REASON_CLOSE_CONNECTION 1 +#define BHSLR_REASON_REMOVE_FOR_RECOVERY 2 + +struct iscsi_bhs_logout_request { + uint8_t bhslr_opcode; + uint8_t bhslr_reason; + uint16_t bhslr_reserved; + uint8_t bhslr_total_ahs_len; + uint8_t bhslr_data_segment_len[3]; + uint64_t bhslr_reserved2; + uint32_t bhslr_initiator_task_tag; + uint16_t bhslr_cid; + uint16_t bhslr_reserved3; + uint32_t bhslr_cmdsn; + uint32_t bhslr_expstatsn; + uint8_t bhslr_reserved4[16]; +}; +CTASSERT(sizeof(struct iscsi_bhs_logout_request) == ISCSI_BHS_SIZE); + +#define BHSLR_RESPONSE_CLOSED_SUCCESSFULLY 0 +#define BHSLR_RESPONSE_RECOVERY_NOT_SUPPORTED 2 + +struct iscsi_bhs_logout_response { + uint8_t bhslr_opcode; + uint8_t bhslr_flags; + uint8_t bhslr_response; + uint8_t bhslr_reserved; + uint8_t bhslr_total_ahs_len; + uint8_t bhslr_data_segment_len[3]; + uint64_t bhslr_reserved2; + uint32_t bhslr_initiator_task_tag; + uint32_t bhslr_reserved3; + uint32_t bhslr_statsn; + uint32_t bhslr_expcmdsn; + uint32_t bhslr_maxcmdsn; + uint32_t bhslr_reserved4; + uint16_t bhslr_time2wait; + uint16_t bhslr_time2retain; + uint32_t bhslr_reserved5; +}; +CTASSERT(sizeof(struct iscsi_bhs_logout_response) == ISCSI_BHS_SIZE); + +#define BHSAM_EVENT_TARGET_REQUESTS_LOGOUT 1 +#define BHSAM_EVENT_TARGET_TERMINATES_CONNECTION 2 +#define BHSAM_EVENT_TARGET_TERMINATES_SESSION 3 + +struct iscsi_bhs_asynchronous_message { + uint8_t bhsam_opcode; + uint8_t bhsam_flags; + uint16_t bhsam_reserved; + uint8_t bhsam_total_ahs_len; + uint8_t bhsam_data_segment_len[3]; + uint64_t bhsam_lun; + uint32_t bhsam_0xffffffff; + uint32_t bhsam_reserved2; + uint32_t bhsam_statsn; + uint32_t bhsam_expcmdsn; + uint32_t bhsam_maxcmdsn; + uint8_t bhsam_async_event; + uint8_t bhsam_async_vcode; + uint16_t bhsam_parameter1; + uint16_t bhsam_parameter2; + uint16_t bhsam_parameter3; + uint32_t bhsam_reserved3; +}; +CTASSERT(sizeof(struct iscsi_bhs_asynchronous_message) == ISCSI_BHS_SIZE); + +#define BHSSR_REASON_DATA_DIGEST_ERROR 0x02 +#define BHSSR_PROTOCOL_ERROR 0x04 +#define BHSSR_COMMAND_NOT_SUPPORTED 0x05 +#define BHSSR_INVALID_PDU_FIELD 0x09 + +struct iscsi_bhs_reject { + uint8_t bhsr_opcode; + uint8_t bhsr_flags; + uint8_t bhsr_reason; + uint8_t bhsr_reserved; + uint8_t bhsr_total_ahs_len; + uint8_t bhsr_data_segment_len[3]; + uint64_t bhsr_reserved2; + uint32_t bhsr_0xffffffff; + uint32_t bhsr_reserved3; + uint32_t bhsr_statsn; + uint32_t bhsr_expcmdsn; + uint32_t bhsr_maxcmdsn; + uint32_t bhsr_datasn_r2tsn; + uint32_t bhsr_reserved4; + uint32_t bhsr_reserved5; +}; +CTASSERT(sizeof(struct iscsi_bhs_reject) == ISCSI_BHS_SIZE); + +#endif /* !ISCSI_PROTO_H */ diff --git a/sys/dev/iscsi/initiator/isc_cam.c b/sys/dev/iscsi_initiator/isc_cam.c similarity index 99% rename from sys/dev/iscsi/initiator/isc_cam.c rename to sys/dev/iscsi_initiator/isc_cam.c index ee0171c09bc..c3ddd26802a 100644 --- a/sys/dev/iscsi/initiator/isc_cam.c +++ b/sys/dev/iscsi_initiator/isc_cam.c @@ -53,8 +53,8 @@ __FBSDID("$FreeBSD$"); #include #include -#include -#include +#include +#include static void _inq(struct cam_sim *sim, union ccb *ccb) diff --git a/sys/dev/iscsi/initiator/isc_sm.c b/sys/dev/iscsi_initiator/isc_sm.c similarity index 99% rename from sys/dev/iscsi/initiator/isc_sm.c rename to sys/dev/iscsi_initiator/isc_sm.c index 7f47e9fdaa0..84f809ad22d 100644 --- a/sys/dev/iscsi/initiator/isc_sm.c +++ b/sys/dev/iscsi_initiator/isc_sm.c @@ -62,8 +62,8 @@ __FBSDID("$FreeBSD$"); #include #include -#include -#include +#include +#include static void _async(isc_session_t *sp, pduq_t *pq) diff --git a/sys/dev/iscsi/initiator/isc_soc.c b/sys/dev/iscsi_initiator/isc_soc.c similarity index 99% rename from sys/dev/iscsi/initiator/isc_soc.c rename to sys/dev/iscsi_initiator/isc_soc.c index 947aadce075..a08ecfad37a 100644 --- a/sys/dev/iscsi/initiator/isc_soc.c +++ b/sys/dev/iscsi_initiator/isc_soc.c @@ -56,8 +56,8 @@ __FBSDID("$FreeBSD$"); #include #include -#include -#include +#include +#include #ifndef NO_USE_MBUF #define USE_MBUF @@ -68,8 +68,8 @@ static int ou_refcnt = 0; /* | function for freeing external storage for mbuf */ -static void -ext_free(void *a, void *b) +static int +ext_free(struct mbuf *m, void *a, void *b) { pduq_t *pq = b; @@ -78,6 +78,7 @@ ext_free(void *a, void *b) free(pq->buf, M_ISCSIBUF); pq->buf = NULL; } + return (EXT_FREE_OK); } int diff --git a/sys/dev/iscsi/initiator/isc_subr.c b/sys/dev/iscsi_initiator/isc_subr.c similarity index 99% rename from sys/dev/iscsi/initiator/isc_subr.c rename to sys/dev/iscsi_initiator/isc_subr.c index d7f0d2754dc..677c5f1674c 100644 --- a/sys/dev/iscsi/initiator/isc_subr.c +++ b/sys/dev/iscsi_initiator/isc_subr.c @@ -55,8 +55,8 @@ __FBSDID("$FreeBSD$"); #include #include -#include -#include +#include +#include static MALLOC_DEFINE(M_ISC, "iSC", "iSCSI driver options"); diff --git a/sys/dev/iscsi/initiator/iscsi.c b/sys/dev/iscsi_initiator/iscsi.c similarity index 97% rename from sys/dev/iscsi/initiator/iscsi.c rename to sys/dev/iscsi_initiator/iscsi.c index 3737b7fdabb..6e06718aabf 100644 --- a/sys/dev/iscsi/initiator/iscsi.c +++ b/sys/dev/iscsi_initiator/iscsi.c @@ -58,8 +58,8 @@ __FBSDID("$FreeBSD$"); #include #include -#include -#include +#include +#include static char *iscsi_driver_version = "2.3.1"; static struct isc_softc *isc; @@ -382,16 +382,19 @@ i_ping(struct cdev *dev) static int i_setsoc(isc_session_t *sp, int fd, struct thread *td) { + cap_rights_t rights; int error = 0; if(sp->soc != NULL) isc_stop_receiver(sp); - error = fget(td, fd, CAP_SOCK_CLIENT, &sp->fp); + error = fget(td, fd, cap_rights_init(&rights, CAP_SOCK_CLIENT), &sp->fp); if(error) return error; - if((error = fgetsock(td, fd, CAP_SOCK_CLIENT, &sp->soc, 0)) == 0) { + error = fgetsock(td, fd, cap_rights_init(&rights, CAP_SOCK_CLIENT), + &sp->soc, 0); + if(error == 0) { sp->td = td; isc_start_receiver(sp); } @@ -855,7 +858,7 @@ iscsi_modevent(module_t mod, int what, void *arg) } moduledata_t iscsi_mod = { - "iscsi", + "iscsi_initiator", (modeventhand_t) iscsi_modevent, 0 }; @@ -875,5 +878,5 @@ iscsi_rootconf(void) SYSINIT(cpu_rootconf1, SI_SUB_ROOT_CONF, SI_ORDER_FIRST, iscsi_rootconf, NULL) #endif -DECLARE_MODULE(iscsi, iscsi_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE); -MODULE_DEPEND(iscsi, cam, 1, 1, 1); +DECLARE_MODULE(iscsi_initiator, iscsi_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE); +MODULE_DEPEND(iscsi_initiator, cam, 1, 1, 1); diff --git a/sys/dev/iscsi/initiator/iscsi.h b/sys/dev/iscsi_initiator/iscsi.h similarity index 100% rename from sys/dev/iscsi/initiator/iscsi.h rename to sys/dev/iscsi_initiator/iscsi.h diff --git a/sys/dev/iscsi/initiator/iscsi_subr.c b/sys/dev/iscsi_initiator/iscsi_subr.c similarity index 99% rename from sys/dev/iscsi/initiator/iscsi_subr.c rename to sys/dev/iscsi_initiator/iscsi_subr.c index 013b16e1247..b22ed831207 100644 --- a/sys/dev/iscsi/initiator/iscsi_subr.c +++ b/sys/dev/iscsi_initiator/iscsi_subr.c @@ -53,8 +53,8 @@ __FBSDID("$FreeBSD$"); #include #include -#include -#include +#include +#include /* | Interface to the SCSI layer diff --git a/sys/dev/iscsi/initiator/iscsivar.h b/sys/dev/iscsi_initiator/iscsivar.h similarity index 100% rename from sys/dev/iscsi/initiator/iscsivar.h rename to sys/dev/iscsi_initiator/iscsivar.h diff --git a/sys/dev/isp/isp_freebsd.c b/sys/dev/isp/isp_freebsd.c index 167d39d3bd7..2d0d64d6bb9 100644 --- a/sys/dev/isp/isp_freebsd.c +++ b/sys/dev/isp/isp_freebsd.c @@ -5445,6 +5445,11 @@ isp_action(struct cam_sim *sim, union ccb *ccb) cpi->max_target = ISP_MAX_TARGETS(isp) - 1; cpi->max_lun = ISP_MAX_LUNS(isp) - 1; cpi->bus_id = cam_sim_bus(sim); + if (isp->isp_osinfo.sixtyfourbit) + cpi->maxio = (ISP_NSEG64_MAX - 1) * PAGE_SIZE; + else + cpi->maxio = (ISP_NSEG_MAX - 1) * PAGE_SIZE; + bus = cam_sim_bus(xpt_path_sim(cpi->ccb_h.path)); if (IS_FC(isp)) { fcparam *fcp = FCPARAM(isp, bus); diff --git a/sys/dev/isp/isp_pci.c b/sys/dev/isp/isp_pci.c index 795f4324c7f..782178c3ff8 100644 --- a/sys/dev/isp/isp_pci.c +++ b/sys/dev/isp/isp_pci.c @@ -706,13 +706,10 @@ isp_pci_attach(device_t dev) pcs->irq = pcs->regs = NULL; pcs->rgd = pcs->rtp = pcs->iqd = 0; - cmd = pci_read_config(dev, PCIR_COMMAND, 2); - if (cmd & m1) { - pcs->rtp = (m1 == PCIM_CMD_MEMEN)? SYS_RES_MEMORY : SYS_RES_IOPORT; - pcs->rgd = (m1 == PCIM_CMD_MEMEN)? MEM_MAP_REG : IO_MAP_REG; - pcs->regs = bus_alloc_resource_any(dev, pcs->rtp, &pcs->rgd, RF_ACTIVE); - } - if (pcs->regs == NULL && (cmd & m2)) { + pcs->rtp = (m1 == PCIM_CMD_MEMEN)? SYS_RES_MEMORY : SYS_RES_IOPORT; + pcs->rgd = (m1 == PCIM_CMD_MEMEN)? MEM_MAP_REG : IO_MAP_REG; + pcs->regs = bus_alloc_resource_any(dev, pcs->rtp, &pcs->rgd, RF_ACTIVE); + if (pcs->regs == NULL) { pcs->rtp = (m2 == PCIM_CMD_MEMEN)? SYS_RES_MEMORY : SYS_RES_IOPORT; pcs->rgd = (m2 == PCIM_CMD_MEMEN)? MEM_MAP_REG : IO_MAP_REG; pcs->regs = bus_alloc_resource_any(dev, pcs->rtp, &pcs->rgd, RF_ACTIVE); @@ -891,6 +888,7 @@ isp_pci_attach(device_t dev) /* * Make sure that SERR, PERR, WRITE INVALIDATE and BUSMASTER are set. */ + cmd = pci_read_config(dev, PCIR_COMMAND, 2); cmd |= PCIM_CMD_SEREN | PCIM_CMD_PERRESPEN | PCIM_CMD_BUSMASTEREN | PCIM_CMD_INVEN; if (IS_2300(isp)) { /* per QLogic errata */ cmd &= ~PCIM_CMD_INVEN; diff --git a/sys/dev/iwn/if_iwn.c b/sys/dev/iwn/if_iwn.c index faf7ec605b3..14fb002864b 100644 --- a/sys/dev/iwn/if_iwn.c +++ b/sys/dev/iwn/if_iwn.c @@ -1,4 +1,6 @@ /*- + * Copyright (c) 2013 Cedric GROSS + * Copyright (c) 2011 Intel Corporation * Copyright (c) 2007-2009 * Damien Bergamini * Copyright (c) 2008 @@ -481,12 +483,12 @@ iwn_attach(device_t dev) pci_write_config(dev, 0x41, 0, 1); /* Hardware bug workaround. */ - reg = pci_read_config(dev, PCIR_COMMAND, 1); + reg = pci_read_config(dev, PCIR_COMMAND, 2); if (reg & PCIM_CMD_INTxDIS) { DPRINTF(sc, IWN_DEBUG_RESET, "%s: PCIe INTx Disable set\n", __func__); reg &= ~PCIM_CMD_INTxDIS; - pci_write_config(dev, PCIR_COMMAND, reg, 1); + pci_write_config(dev, PCIR_COMMAND, reg, 2); } /* Enable bus-mastering. */ @@ -521,6 +523,7 @@ iwn_attach(device_t dev) /* Read hardware revision and attach. */ sc->hw_type = (IWN_READ(sc, IWN_HW_REV) >> IWN_HW_REV_TYPE_SHIFT) & IWN_HW_REV_TYPE_MASK; + sc->subdevice_id = pci_get_subdevice(dev); if (sc->hw_type == IWN_HW_REV_TYPE_4965) error = iwn4965_attach(sc, pci_get_device(dev)); else @@ -638,6 +641,20 @@ iwn_attach(device_t dev) if (sc->sc_flags & IWN_FLAG_HAS_11N) { ic->ic_rxstream = sc->nrxchains; ic->ic_txstream = sc->ntxchains; + + /* + * The NICs we currently support cap out at 2x2 support + * separate from the chains being used. + * + * This is a total hack to work around that until some + * per-device method is implemented to return the + * actual stream support. + */ + if (ic->ic_rxstream > 2) + ic->ic_rxstream = 2; + if (ic->ic_txstream > 2) + ic->ic_txstream = 2; + ic->ic_htcaps = IEEE80211_HTCAP_SMPS_OFF /* SMPS mode disabled */ | IEEE80211_HTCAP_SHORTGI20 /* short GI in 20MHz */ @@ -894,19 +911,27 @@ iwn_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ], int unit, { struct iwn_vap *ivp; struct ieee80211vap *vap; + uint8_t mac1[IEEE80211_ADDR_LEN]; + struct iwn_softc *sc = ic->ic_ifp->if_softc; if (!TAILQ_EMPTY(&ic->ic_vaps)) /* only one at a time */ return NULL; + + IEEE80211_ADDR_COPY(mac1, mac); + ivp = (struct iwn_vap *) malloc(sizeof(struct iwn_vap), M_80211_VAP, M_NOWAIT | M_ZERO); if (ivp == NULL) return NULL; vap = &ivp->iv_vap; - ieee80211_vap_setup(ic, vap, name, unit, opmode, flags, bssid, mac); + ieee80211_vap_setup(ic, vap, name, unit, opmode, flags, bssid, mac1); + ivp->ctx = IWN_RXON_BSS_CTX; + IEEE80211_ADDR_COPY(ivp->macaddr, mac1); vap->iv_bmissthreshold = 10; /* override default */ /* Override with driver methods. */ ivp->iv_newstate = vap->iv_newstate; vap->iv_newstate = iwn_newstate; + sc->ivap[IWN_RXON_BSS_CTX] = vap; ieee80211_ratectl_init(vap); /* Complete setup. */ @@ -2325,6 +2350,8 @@ iwn_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) IWN_LOCK(sc); callout_stop(&sc->calib_to); + sc->rxon = &sc->rx_on[IWN_RXON_BSS_CTX]; + switch (nstate) { case IEEE80211_S_ASSOC: if (vap->iv_state != IEEE80211_S_RUN) @@ -2338,8 +2365,8 @@ iwn_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) * !AUTH -> AUTH transition requires state reset to handle * reassociations correctly. */ - sc->rxon.associd = 0; - sc->rxon.filter &= ~htole32(IWN_FILTER_BSS); + sc->rxon->associd = 0; + sc->rxon->filter &= ~htole32(IWN_FILTER_BSS); sc->calib.state = IWN_CALIB_STATE_INIT; if ((error = iwn_auth(sc, vap)) != 0) { @@ -2641,11 +2668,7 @@ iwn_rx_compressed_ba(struct iwn_softc *sc, struct iwn_rx_desc *desc, KASSERT(ni != NULL, ("no node")); KASSERT(m != NULL, ("no mbuf")); - if (m->m_flags & M_TXCB) - ieee80211_process_callback(ni, m, 1); - - m_freem(m); - ieee80211_free_node(ni); + ieee80211_tx_complete(ni, m, 1); txq->queued--; txq->read = (txq->read + 1) % IWN_TX_RING_COUNT; @@ -2907,29 +2930,6 @@ iwn_tx_done(struct iwn_softc *sc, struct iwn_rx_desc *desc, int ackfailcnt, ni = data->ni, data->ni = NULL; vap = ni->ni_vap; - if (m->m_flags & M_TXCB) { - /* - * Channels marked for "radar" require traffic to be received - * to unlock before we can transmit. Until traffic is seen - * any attempt to transmit is returned immediately with status - * set to IWN_TX_FAIL_TX_LOCKED. Unfortunately this can easily - * happen on first authenticate after scanning. To workaround - * this we ignore a failure of this sort in AUTH state so the - * 802.11 layer will fall back to using a timeout to wait for - * the AUTH reply. This allows the firmware time to see - * traffic so a subsequent retry of AUTH succeeds. It's - * unclear why the firmware does not maintain state for - * channels recently visited as this would allow immediate - * use of the channel after a scan (where we see traffic). - */ - if (status == IWN_TX_FAIL_TX_LOCKED && - ni->ni_vap->iv_state == IEEE80211_S_AUTH) - ieee80211_process_callback(ni, m, 0); - else - ieee80211_process_callback(ni, m, - (status & IWN_TX_FAIL) != 0); - } - /* * Update rate control statistics for the node. */ @@ -2942,8 +2942,27 @@ iwn_tx_done(struct iwn_softc *sc, struct iwn_rx_desc *desc, int ackfailcnt, ieee80211_ratectl_tx_complete(vap, ni, IEEE80211_RATECTL_TX_SUCCESS, &ackfailcnt, NULL); } - m_freem(m); - ieee80211_free_node(ni); + + /* + * Channels marked for "radar" require traffic to be received + * to unlock before we can transmit. Until traffic is seen + * any attempt to transmit is returned immediately with status + * set to IWN_TX_FAIL_TX_LOCKED. Unfortunately this can easily + * happen on first authenticate after scanning. To workaround + * this we ignore a failure of this sort in AUTH state so the + * 802.11 layer will fall back to using a timeout to wait for + * the AUTH reply. This allows the firmware time to see + * traffic so a subsequent retry of AUTH succeeds. It's + * unclear why the firmware does not maintain state for + * channels recently visited as this would allow immediate + * use of the channel after a scan (where we see traffic). + */ + if (status == IWN_TX_FAIL_TX_LOCKED && + ni->ni_vap->iv_state == IEEE80211_S_AUTH) + ieee80211_tx_complete(ni, m, 0); + else + ieee80211_tx_complete(ni, m, + (status & IWN_TX_FAIL) != 0); sc->sc_tx_timer = 0; if (--ring->queued < IWN_TX_RING_LOMARK) { @@ -3064,11 +3083,7 @@ iwn_ampdu_tx_done(struct iwn_softc *sc, int qid, int idx, int nframes, KASSERT(ni != NULL, ("no node")); KASSERT(m != NULL, ("no mbuf")); - if (m->m_flags & M_TXCB) - ieee80211_process_callback(ni, m, 1); - - m_freem(m); - ieee80211_free_node(ni); + ieee80211_tx_complete(ni, m, 1); ring->queued--; ring->read = (ring->read + 1) % IWN_TX_RING_COUNT; @@ -4353,6 +4368,8 @@ iwn_add_broadcast_node(struct iwn_softc *sc, int async) DPRINTF(sc, IWN_DEBUG_TRACE, "->%s begin\n", __func__); + sc->rxon = &sc->rx_on[IWN_RXON_BSS_CTX]; + memset(&node, 0, sizeof node); IEEE80211_ADDR_COPY(node.macaddr, ifp->if_broadcastaddr); node.id = sc->broadcast_id; @@ -4536,8 +4553,9 @@ iwn4965_set_txpower(struct iwn_softc *sc, struct ieee80211_channel *ch, int i, c, grp, maxpwr; uint8_t chan; + sc->rxon = &sc->rx_on[IWN_RXON_BSS_CTX]; /* Retrieve current channel from last RXON. */ - chan = sc->rxon.chan; + chan = sc->rxon->chan; DPRINTF(sc, IWN_DEBUG_RESET, "setting TX power for channel %d\n", chan); @@ -4896,8 +4914,8 @@ iwn_collect_noise(struct iwn_softc *sc, #ifdef notyet /* XXX Disable RX chains with no antennas connected. */ - sc->rxon.rxchain = htole16(IWN_RXCHAIN_SEL(sc->chainmask)); - (void)iwn_cmd(sc, IWN_CMD_RXON, &sc->rxon, sc->rxonsz, 1); + sc->rxon->rxchain = htole16(IWN_RXCHAIN_SEL(sc->chainmask)); + (void)iwn_cmd(sc, IWN_CMD_RXON, sc->rxon, sc->rxonsz, 1); #endif /* Enable power-saving mode if requested by user. */ @@ -5342,7 +5360,7 @@ iwn_send_advanced_btcoex(struct iwn_softc *sc) return error; /* Force BT state machine change. */ - memset(&btprot, 0, sizeof btprio); + memset(&btprot, 0, sizeof btprot); btprot.open = 1; btprot.type = 1; error = iwn_cmd(sc, IWN_CMD_BT_COEX_PROT, &btprot, sizeof(btprot), 1); @@ -5426,39 +5444,40 @@ iwn_config(struct iwn_softc *sc) } /* Set mode, channel, RX filter and enable RX. */ - memset(&sc->rxon, 0, sizeof (struct iwn_rxon)); - IEEE80211_ADDR_COPY(sc->rxon.myaddr, IF_LLADDR(ifp)); - IEEE80211_ADDR_COPY(sc->rxon.wlap, IF_LLADDR(ifp)); - sc->rxon.chan = ieee80211_chan2ieee(ic, ic->ic_curchan); - sc->rxon.flags = htole32(IWN_RXON_TSF | IWN_RXON_CTS_TO_SELF); + sc->rxon = &sc->rx_on[IWN_RXON_BSS_CTX]; + memset(sc->rxon, 0, sizeof (struct iwn_rxon)); + IEEE80211_ADDR_COPY(sc->rxon->myaddr, IF_LLADDR(ifp)); + IEEE80211_ADDR_COPY(sc->rxon->wlap, IF_LLADDR(ifp)); + sc->rxon->chan = ieee80211_chan2ieee(ic, ic->ic_curchan); + sc->rxon->flags = htole32(IWN_RXON_TSF | IWN_RXON_CTS_TO_SELF); if (IEEE80211_IS_CHAN_2GHZ(ic->ic_curchan)) - sc->rxon.flags |= htole32(IWN_RXON_AUTO | IWN_RXON_24GHZ); + sc->rxon->flags |= htole32(IWN_RXON_AUTO | IWN_RXON_24GHZ); switch (ic->ic_opmode) { case IEEE80211_M_STA: - sc->rxon.mode = IWN_MODE_STA; - sc->rxon.filter = htole32(IWN_FILTER_MULTICAST); + sc->rxon->mode = IWN_MODE_STA; + sc->rxon->filter = htole32(IWN_FILTER_MULTICAST); break; case IEEE80211_M_MONITOR: - sc->rxon.mode = IWN_MODE_MONITOR; - sc->rxon.filter = htole32(IWN_FILTER_MULTICAST | + sc->rxon->mode = IWN_MODE_MONITOR; + sc->rxon->filter = htole32(IWN_FILTER_MULTICAST | IWN_FILTER_CTL | IWN_FILTER_PROMISC); break; default: /* Should not get there. */ break; } - sc->rxon.cck_mask = 0x0f; /* not yet negotiated */ - sc->rxon.ofdm_mask = 0xff; /* not yet negotiated */ - sc->rxon.ht_single_mask = 0xff; - sc->rxon.ht_dual_mask = 0xff; - sc->rxon.ht_triple_mask = 0xff; + sc->rxon->cck_mask = 0x0f; /* not yet negotiated */ + sc->rxon->ofdm_mask = 0xff; /* not yet negotiated */ + sc->rxon->ht_single_mask = 0xff; + sc->rxon->ht_dual_mask = 0xff; + sc->rxon->ht_triple_mask = 0xff; rxchain = IWN_RXCHAIN_VALID(sc->rxchainmask) | IWN_RXCHAIN_MIMO_COUNT(2) | IWN_RXCHAIN_IDLE_COUNT(2); - sc->rxon.rxchain = htole16(rxchain); + sc->rxon->rxchain = htole16(rxchain); DPRINTF(sc, IWN_DEBUG_RESET, "%s: setting configuration\n", __func__); - error = iwn_cmd(sc, IWN_CMD_RXON, &sc->rxon, sc->rxonsz, 0); + error = iwn_cmd(sc, IWN_CMD_RXON, sc->rxon, sc->rxonsz, 0); if (error != 0) { device_printf(sc->sc_dev, "%s: RXON command failed\n", __func__); @@ -5529,6 +5548,7 @@ iwn_scan(struct iwn_softc *sc) DPRINTF(sc, IWN_DEBUG_TRACE, "->%s begin\n", __func__); + sc->rxon = &sc->rx_on[IWN_RXON_BSS_CTX]; buf = malloc(IWN_SCAN_MAXSZ, M_DEVBUF, M_NOWAIT | M_ZERO); if (buf == NULL) { device_printf(sc->sc_dev, @@ -5570,7 +5590,7 @@ iwn_scan(struct iwn_softc *sc) } else { hdr->flags = htole32(IWN_RXON_24GHZ | IWN_RXON_AUTO); if (sc->hw_type == IWN_HW_REV_TYPE_4965 && - sc->rxon.associd && sc->rxon.chan > 14) + sc->rxon->associd && sc->rxon->chan > 14) tx->rate = htole32(0xd); else { /* Send probe requests at 1Mbps. */ @@ -5629,7 +5649,7 @@ iwn_scan(struct iwn_softc *sc) } else if (IEEE80211_IS_CHAN_5GHZ(c)) { chan->rf_gain = 0x3b; chan->active = htole16(24); - if (sc->rxon.associd) + if (sc->rxon->associd) chan->passive = htole16(78); else chan->passive = htole16(110); @@ -5642,7 +5662,7 @@ iwn_scan(struct iwn_softc *sc) } else { chan->rf_gain = 0x28; chan->active = htole16(36); - if (sc->rxon.associd) + if (sc->rxon->associd) chan->passive = htole16(88); else chan->passive = htole16(120); @@ -5681,31 +5701,32 @@ iwn_auth(struct iwn_softc *sc, struct ieee80211vap *vap) DPRINTF(sc, IWN_DEBUG_TRACE, "->%s begin\n", __func__); + sc->rxon = &sc->rx_on[IWN_RXON_BSS_CTX]; /* Update adapter configuration. */ - IEEE80211_ADDR_COPY(sc->rxon.bssid, ni->ni_bssid); - sc->rxon.chan = ieee80211_chan2ieee(ic, ni->ni_chan); - sc->rxon.flags = htole32(IWN_RXON_TSF | IWN_RXON_CTS_TO_SELF); + IEEE80211_ADDR_COPY(sc->rxon->bssid, ni->ni_bssid); + sc->rxon->chan = ieee80211_chan2ieee(ic, ni->ni_chan); + sc->rxon->flags = htole32(IWN_RXON_TSF | IWN_RXON_CTS_TO_SELF); if (IEEE80211_IS_CHAN_2GHZ(ni->ni_chan)) - sc->rxon.flags |= htole32(IWN_RXON_AUTO | IWN_RXON_24GHZ); + sc->rxon->flags |= htole32(IWN_RXON_AUTO | IWN_RXON_24GHZ); if (ic->ic_flags & IEEE80211_F_SHSLOT) - sc->rxon.flags |= htole32(IWN_RXON_SHSLOT); + sc->rxon->flags |= htole32(IWN_RXON_SHSLOT); if (ic->ic_flags & IEEE80211_F_SHPREAMBLE) - sc->rxon.flags |= htole32(IWN_RXON_SHPREAMBLE); + sc->rxon->flags |= htole32(IWN_RXON_SHPREAMBLE); if (IEEE80211_IS_CHAN_A(ni->ni_chan)) { - sc->rxon.cck_mask = 0; - sc->rxon.ofdm_mask = 0x15; + sc->rxon->cck_mask = 0; + sc->rxon->ofdm_mask = 0x15; } else if (IEEE80211_IS_CHAN_B(ni->ni_chan)) { - sc->rxon.cck_mask = 0x03; - sc->rxon.ofdm_mask = 0; + sc->rxon->cck_mask = 0x03; + sc->rxon->ofdm_mask = 0; } else { /* Assume 802.11b/g. */ - sc->rxon.cck_mask = 0x0f; - sc->rxon.ofdm_mask = 0x15; + sc->rxon->cck_mask = 0x0f; + sc->rxon->ofdm_mask = 0x15; } DPRINTF(sc, IWN_DEBUG_STATE, "rxon chan %d flags %x cck %x ofdm %x\n", - sc->rxon.chan, sc->rxon.flags, sc->rxon.cck_mask, - sc->rxon.ofdm_mask); - error = iwn_cmd(sc, IWN_CMD_RXON, &sc->rxon, sc->rxonsz, 1); + sc->rxon->chan, sc->rxon->flags, sc->rxon->cck_mask, + sc->rxon->ofdm_mask); + error = iwn_cmd(sc, IWN_CMD_RXON, sc->rxon, sc->rxonsz, 1); if (error != 0) { device_printf(sc->sc_dev, "%s: RXON command failed, error %d\n", __func__, error); @@ -5747,6 +5768,7 @@ iwn_run(struct iwn_softc *sc, struct ieee80211vap *vap) DPRINTF(sc, IWN_DEBUG_TRACE, "->%s begin\n", __func__); + sc->rxon = &sc->rx_on[IWN_RXON_BSS_CTX]; if (ic->ic_opmode == IEEE80211_M_MONITOR) { /* Link LED blinks while monitoring. */ iwn_set_led(sc, IWN_LED_LINK, 5, 5); @@ -5759,26 +5781,26 @@ iwn_run(struct iwn_softc *sc, struct ieee80211vap *vap) } /* Update adapter configuration. */ - IEEE80211_ADDR_COPY(sc->rxon.bssid, ni->ni_bssid); - sc->rxon.associd = htole16(IEEE80211_AID(ni->ni_associd)); - sc->rxon.chan = ieee80211_chan2ieee(ic, ni->ni_chan); - sc->rxon.flags = htole32(IWN_RXON_TSF | IWN_RXON_CTS_TO_SELF); + IEEE80211_ADDR_COPY(sc->rxon->bssid, ni->ni_bssid); + sc->rxon->associd = htole16(IEEE80211_AID(ni->ni_associd)); + sc->rxon->chan = ieee80211_chan2ieee(ic, ni->ni_chan); + sc->rxon->flags = htole32(IWN_RXON_TSF | IWN_RXON_CTS_TO_SELF); if (IEEE80211_IS_CHAN_2GHZ(ni->ni_chan)) - sc->rxon.flags |= htole32(IWN_RXON_AUTO | IWN_RXON_24GHZ); + sc->rxon->flags |= htole32(IWN_RXON_AUTO | IWN_RXON_24GHZ); if (ic->ic_flags & IEEE80211_F_SHSLOT) - sc->rxon.flags |= htole32(IWN_RXON_SHSLOT); + sc->rxon->flags |= htole32(IWN_RXON_SHSLOT); if (ic->ic_flags & IEEE80211_F_SHPREAMBLE) - sc->rxon.flags |= htole32(IWN_RXON_SHPREAMBLE); + sc->rxon->flags |= htole32(IWN_RXON_SHPREAMBLE); if (IEEE80211_IS_CHAN_A(ni->ni_chan)) { - sc->rxon.cck_mask = 0; - sc->rxon.ofdm_mask = 0x15; + sc->rxon->cck_mask = 0; + sc->rxon->ofdm_mask = 0x15; } else if (IEEE80211_IS_CHAN_B(ni->ni_chan)) { - sc->rxon.cck_mask = 0x03; - sc->rxon.ofdm_mask = 0; + sc->rxon->cck_mask = 0x03; + sc->rxon->ofdm_mask = 0; } else { /* Assume 802.11b/g. */ - sc->rxon.cck_mask = 0x0f; - sc->rxon.ofdm_mask = 0x15; + sc->rxon->cck_mask = 0x0f; + sc->rxon->ofdm_mask = 0x15; } if (IEEE80211_IS_CHAN_HT(ni->ni_chan)) { htflags |= IWN_RXON_HT_PROTMODE(ic->ic_curhtprotmode); @@ -5795,11 +5817,11 @@ iwn_run(struct iwn_softc *sc, struct ieee80211vap *vap) if (IEEE80211_IS_CHAN_HT40D(ni->ni_chan)) htflags |= IWN_RXON_HT_HT40MINUS; } - sc->rxon.flags |= htole32(htflags); - sc->rxon.filter |= htole32(IWN_FILTER_BSS); + sc->rxon->flags |= htole32(htflags); + sc->rxon->filter |= htole32(IWN_FILTER_BSS); DPRINTF(sc, IWN_DEBUG_STATE, "rxon chan %d flags %x\n", - sc->rxon.chan, sc->rxon.flags); - error = iwn_cmd(sc, IWN_CMD_RXON, &sc->rxon, sc->rxonsz, 1); + sc->rxon->chan, sc->rxon->flags); + error = iwn_cmd(sc, IWN_CMD_RXON, sc->rxon, sc->rxonsz, 1); if (error != 0) { device_printf(sc->sc_dev, "%s: could not update configuration, error %d\n", __func__, @@ -6781,6 +6803,27 @@ iwn_read_firmware_tlv(struct iwn_softc *sc, struct iwn_fw_info *fw, sc->noise_gain = tmp + 1; } break; + case IWN_FW_TLV_PAN: + sc->sc_flags |= IWN_FLAG_PAN_SUPPORT; + DPRINTF(sc, IWN_DEBUG_RESET, + "PAN Support found: %d\n", 1); + break; + case IWN_FW_TLV_FLAGS : + sc->tlv_feature_flags = htole32(*ptr); + break; + case IWN_FW_TLV_PBREQ_MAXLEN: + case IWN_FW_TLV_RUNT_EVTLOG_PTR: + case IWN_FW_TLV_RUNT_EVTLOG_SIZE: + case IWN_FW_TLV_RUNT_ERRLOG_PTR: + case IWN_FW_TLV_INIT_EVTLOG_PTR: + case IWN_FW_TLV_INIT_EVTLOG_SIZE: + case IWN_FW_TLV_INIT_ERRLOG_PTR: + case IWN_FW_TLV_WOWLAN_INST: + case IWN_FW_TLV_WOWLAN_DATA: + DPRINTF(sc, IWN_DEBUG_RESET, + "TLV type %d reconized but not handled\n", + le16toh(tlv->type)); + break; default: DPRINTF(sc, IWN_DEBUG_RESET, "TLV type %d not handled\n", le16toh(tlv->type)); diff --git a/sys/dev/iwn/if_iwnvar.h b/sys/dev/iwn/if_iwnvar.h index 0bcc19bd0e6..d1c0b76ab92 100644 --- a/sys/dev/iwn/if_iwnvar.h +++ b/sys/dev/iwn/if_iwnvar.h @@ -2,6 +2,8 @@ /* $OpenBSD: if_iwnvar.h,v 1.18 2010/04/30 16:06:46 damien Exp $ */ /*- + * Copyright (c) 2013 Cedric GROSS + * Copyright (c) 2011 Intel Corporation * Copyright (c) 2007, 2008 * Damien Bergamini * Copyright (c) 2008 Sam Leffler, Errno Consulting @@ -18,6 +20,38 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +enum iwn_rxon_ctx_id { + IWN_RXON_BSS_CTX, + IWN_RXON_PAN_CTX, + IWN_NUM_RXON_CTX +}; + +struct iwn_pan_slot { + uint16_t time; + uint8_t type; + uint8_t reserved; +} __packed; + +struct iwn_pan_params_cmd { + uint16_t flags; +#define IWN_PAN_PARAMS_FLG_SLOTTED_MODE (1 << 3) + + uint8_t reserved; + uint8_t num_slots; + struct iwn_pan_slot slots[10]; +} __packed; + +struct iwn_led_mode +{ + uint8_t led_cur_mode; + uint64_t led_cur_bt; + uint64_t led_last_bt; + uint64_t led_cur_tpt; + uint64_t led_last_tpt; + uint64_t led_bt_diff; + int led_cur_time; + int led_last_time; +}; struct iwn_rx_radiotap_header { struct ieee80211_radiotap_header wr_ihdr; @@ -191,6 +225,10 @@ struct iwn_vap { int (*iv_newstate)(struct ieee80211vap *, enum ieee80211_state, int); + int ctx; + int beacon_int; + uint8_t macaddr[IEEE80211_ADDR_LEN]; + }; #define IWN_VAP(_vap) ((struct iwn_vap *)(_vap)) @@ -210,8 +248,11 @@ struct iwn_softc { #define IWN_FLAG_HAS_11N (1 << 6) #define IWN_FLAG_ENH_SENS (1 << 7) #define IWN_FLAG_ADV_BTCOEX (1 << 8) +#define IWN_FLAG_PAN_SUPPORT (1 << 9) uint8_t hw_type; + /* subdevice_id used to adjust configuration */ + uint16_t subdevice_id; struct iwn_ops ops; const char *fwname; @@ -272,7 +313,7 @@ struct iwn_softc { int calib_cnt; struct iwn_calib_state calib; struct callout watchdog_to; - + struct callout ct_kill_exit_to; struct iwn_fw_info fw; struct iwn_calib_info calibcmd[5]; uint32_t errptr; @@ -280,7 +321,12 @@ struct iwn_softc { struct iwn_rx_stat last_rx_stat; int last_rx_valid; struct iwn_ucode_info ucode_info; - struct iwn_rxon rxon; + struct iwn_rxon rx_on[IWN_NUM_RXON_CTX]; + struct iwn_rxon *rxon; + int ctx; + struct ieee80211vap *ivap[IWN_NUM_RXON_CTX]; + + uint8_t uc_scan_progress; uint32_t rawtemp; int temp; int noise; @@ -295,11 +341,14 @@ struct iwn_softc { char eeprom_domain[4]; uint32_t eeprom_crystal; int16_t eeprom_temp; + int16_t eeprom_temp_high; int16_t eeprom_voltage; int8_t maxpwr2GHz; int8_t maxpwr5GHz; int8_t maxpwr[IEEE80211_CHAN_MAX]; + uint32_t tlv_feature_flags; + int32_t temp_off; uint32_t int_mask; uint8_t ntxchains; @@ -309,6 +358,7 @@ struct iwn_softc { uint8_t chainmask; int sc_tx_timer; + int sc_scan_timer; struct ieee80211_tx_ampdu *qid2tap[IWN5000_NTXQUEUES]; @@ -323,9 +373,22 @@ struct iwn_softc { void (*sc_addba_stop)(struct ieee80211_node *, struct ieee80211_tx_ampdu *); + struct iwn_led_mode sc_led; struct iwn_rx_radiotap_header sc_rxtap; struct iwn_tx_radiotap_header sc_txtap; + + /* The power save level originally configured by user */ + int desired_pwrsave_level; + + /* + * The current power save level, this may differ from the + * configured value due to thermal throttling etc. + */ + int current_pwrsave_level; + + /* For specifique params */ + struct iwn_base_params *base_params; }; #define IWN_LOCK_INIT(_sc) \ diff --git a/sys/dev/ixgb/if_ixgb.c b/sys/dev/ixgb/if_ixgb.c index 6e466969c4e..2e62c1c1572 100644 --- a/sys/dev/ixgb/if_ixgb.c +++ b/sys/dev/ixgb/if_ixgb.c @@ -1210,15 +1210,9 @@ ixgb_identify_hardware(struct adapter * adapter) device_t dev = adapter->dev; /* Make sure our PCI config space has the necessary stuff set */ + pci_enable_busmaster(dev); adapter->hw.pci_cmd_word = pci_read_config(dev, PCIR_COMMAND, 2); - if (!((adapter->hw.pci_cmd_word & PCIM_CMD_BUSMASTEREN) && - (adapter->hw.pci_cmd_word & PCIM_CMD_MEMEN))) { - device_printf(dev, - "Memory Access and/or Bus Master bits were not set!\n"); - adapter->hw.pci_cmd_word |= - (PCIM_CMD_BUSMASTEREN | PCIM_CMD_MEMEN); - pci_write_config(dev, PCIR_COMMAND, adapter->hw.pci_cmd_word, 2); - } + /* Save off the information about this board */ adapter->hw.vendor_id = pci_get_vendor(dev); adapter->hw.device_id = pci_get_device(dev); diff --git a/sys/dev/ixgbe/ixgbe.c b/sys/dev/ixgbe/ixgbe.c index ca4aa660ad9..b65df7221b7 100644 --- a/sys/dev/ixgbe/ixgbe.c +++ b/sys/dev/ixgbe/ixgbe.c @@ -2456,12 +2456,18 @@ ixgbe_setup_msix(struct adapter *adapter) msgs, want); goto msi; } - if (pci_alloc_msix(dev, &msgs) == 0) { + if ((pci_alloc_msix(dev, &msgs) == 0) && (msgs == want)) { device_printf(adapter->dev, "Using MSIX interrupts with %d vectors\n", msgs); adapter->num_queues = queues; return (msgs); } + /* + ** If MSIX alloc failed or provided us with + ** less than needed, free and fall through to MSI + */ + pci_release_msi(dev); + msi: if (adapter->msix_mem != NULL) { bus_release_resource(dev, SYS_RES_MEMORY, @@ -4619,7 +4625,7 @@ ixgbe_rx_checksum(u32 staterr, struct mbuf * mp, u32 ptype) mp->m_pkthdr.csum_flags = 0; } if (status & IXGBE_RXD_STAT_L4CS) { - u16 type = (CSUM_DATA_VALID | CSUM_PSEUDO_HDR); + u64 type = (CSUM_DATA_VALID | CSUM_PSEUDO_HDR); #if __FreeBSD_version >= 800000 if (sctp) type = CSUM_SCTP_VALID; diff --git a/sys/dev/ixgbe/ixv.c b/sys/dev/ixgbe/ixv.c index 20131aa41cb..a471da91074 100644 --- a/sys/dev/ixgbe/ixv.c +++ b/sys/dev/ixgbe/ixv.c @@ -1561,14 +1561,8 @@ ixv_identify_hardware(struct adapter *adapter) ** Make sure BUSMASTER is set, on a VM under ** KVM it may not be and will break things. */ + pci_enable_busmaster(dev); pci_cmd_word = pci_read_config(dev, PCIR_COMMAND, 2); - if (!((pci_cmd_word & PCIM_CMD_BUSMASTEREN) && - (pci_cmd_word & PCIM_CMD_MEMEN))) { - INIT_DEBUGOUT("Memory Access and/or Bus Master " - "bits were not set!\n"); - pci_cmd_word |= (PCIM_CMD_BUSMASTEREN | PCIM_CMD_MEMEN); - pci_write_config(dev, PCIR_COMMAND, pci_cmd_word, 2); - } /* Save off the information about this board */ adapter->hw.vendor_id = pci_get_vendor(dev); @@ -1704,11 +1698,13 @@ ixv_setup_msix(struct adapter *adapter) ** plus an additional for mailbox. */ want = 2; - if (pci_alloc_msix(dev, &want) == 0) { + if ((pci_alloc_msix(dev, &want) == 0) && (want == 2)) { device_printf(adapter->dev, "Using MSIX interrupts with %d vectors\n", want); return (want); } + /* Release in case alloc was insufficient */ + pci_release_msi(dev); out: if (adapter->msix_mem != NULL) { bus_release_resource(dev, SYS_RES_MEMORY, @@ -3548,7 +3544,7 @@ ixv_rx_checksum(u32 staterr, struct mbuf * mp, u32 ptype) mp->m_pkthdr.csum_flags = 0; } if (status & IXGBE_RXD_STAT_L4CS) { - u16 type = (CSUM_DATA_VALID | CSUM_PSEUDO_HDR); + u64 type = (CSUM_DATA_VALID | CSUM_PSEUDO_HDR); #if __FreeBSD_version >= 800000 if (sctp) type = CSUM_SCTP_VALID; diff --git a/sys/dev/jme/if_jme.c b/sys/dev/jme/if_jme.c index 832acef0e5c..9bdb229807d 100644 --- a/sys/dev/jme/if_jme.c +++ b/sys/dev/jme/if_jme.c @@ -1690,7 +1690,7 @@ jme_encap(struct jme_softc *sc, struct mbuf **m_head) struct mbuf *m; bus_dma_segment_t txsegs[JME_MAXTXSEGS]; int error, i, nsegs, prod; - uint32_t cflags, tso_segsz; + uint32_t cflags, tsosegsz; JME_LOCK_ASSERT(sc); @@ -1808,10 +1808,10 @@ jme_encap(struct jme_softc *sc, struct mbuf **m_head) m = *m_head; cflags = 0; - tso_segsz = 0; + tsosegsz = 0; /* Configure checksum offload and TSO. */ if ((m->m_pkthdr.csum_flags & CSUM_TSO) != 0) { - tso_segsz = (uint32_t)m->m_pkthdr.tso_segsz << + tsosegsz = (uint32_t)m->m_pkthdr.tso_segsz << JME_TD_MSS_SHIFT; cflags |= JME_TD_TSO; } else { @@ -1830,7 +1830,7 @@ jme_encap(struct jme_softc *sc, struct mbuf **m_head) desc = &sc->jme_rdata.jme_tx_ring[prod]; desc->flags = htole32(cflags); - desc->buflen = htole32(tso_segsz); + desc->buflen = htole32(tsosegsz); desc->addr_hi = htole32(m->m_pkthdr.len); desc->addr_lo = 0; sc->jme_cdata.jme_tx_cnt++; diff --git a/sys/dev/lge/if_lge.c b/sys/dev/lge/if_lge.c index d9948672d30..5fc763d0109 100644 --- a/sys/dev/lge/if_lge.c +++ b/sys/dev/lge/if_lge.c @@ -122,7 +122,7 @@ static int lge_detach(device_t); static int lge_alloc_jumbo_mem(struct lge_softc *); static void lge_free_jumbo_mem(struct lge_softc *); static void *lge_jalloc(struct lge_softc *); -static void lge_jfree(void *, void *); +static int lge_jfree(struct mbuf *, void *, void *); static int lge_newbuf(struct lge_softc *, struct lge_rx_desc *, struct mbuf *); static int lge_encap(struct lge_softc *, struct mbuf *, u_int32_t *); @@ -846,10 +846,8 @@ lge_jalloc(sc) /* * Release a jumbo buffer. */ -static void -lge_jfree(buf, args) - void *buf; - void *args; +static int +lge_jfree(struct mbuf *m, void *buf, void *args) { struct lge_softc *sc; int i; @@ -875,7 +873,7 @@ lge_jfree(buf, args) SLIST_REMOVE_HEAD(&sc->lge_jinuse_listhead, jpool_entries); SLIST_INSERT_HEAD(&sc->lge_jfree_listhead, entry, jpool_entries); - return; + return (EXT_FREE_OK); } /* diff --git a/sys/dev/lmc/if_lmc.c b/sys/dev/lmc/if_lmc.c index 49d0a5009c6..d42aca1de3a 100644 --- a/sys/dev/lmc/if_lmc.c +++ b/sys/dev/lmc/if_lmc.c @@ -4480,7 +4480,6 @@ lmc_raw_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) # if (defined(__FreeBSD__) && defined(DEVICE_POLLING)) /* XXX necessary? */ case SIOCSIFCAP: # endif - case SIOCSIFDSTADDR: case SIOCAIFADDR: case SIOCSIFFLAGS: #if 0 diff --git a/sys/dev/md/md.c b/sys/dev/md/md.c index 57c5b571485..f0d1aec0d91 100644 --- a/sys/dev/md/md.c +++ b/sys/dev/md/md.c @@ -826,15 +826,14 @@ mdstart_swap(struct md_s *sc, struct bio *bp) vm_object_pip_add(sc->object, 1); for (i = bp->bio_offset / PAGE_SIZE; i <= lastp; i++) { len = ((i == lastp) ? lastend : PAGE_SIZE) - offs; - m = vm_page_grab(sc->object, i, VM_ALLOC_NORMAL | - VM_ALLOC_RETRY); + m = vm_page_grab(sc->object, i, VM_ALLOC_SYSTEM); if (bp->bio_cmd == BIO_READ) { if (m->valid == VM_PAGE_BITS_ALL) rv = VM_PAGER_OK; else rv = vm_pager_get_pages(sc->object, &m, 1, 0); if (rv == VM_PAGER_ERROR) { - vm_page_wakeup(m); + vm_page_xunbusy(m); break; } else if (rv == VM_PAGER_FAIL) { /* @@ -859,7 +858,7 @@ mdstart_swap(struct md_s *sc, struct bio *bp) else rv = VM_PAGER_OK; if (rv == VM_PAGER_ERROR) { - vm_page_wakeup(m); + vm_page_xunbusy(m); break; } if ((bp->bio_flags & BIO_UNMAPPED) != 0) { @@ -875,7 +874,7 @@ mdstart_swap(struct md_s *sc, struct bio *bp) else rv = VM_PAGER_OK; if (rv == VM_PAGER_ERROR) { - vm_page_wakeup(m); + vm_page_xunbusy(m); break; } if (len != PAGE_SIZE) { @@ -885,7 +884,7 @@ mdstart_swap(struct md_s *sc, struct bio *bp) } else vm_pager_page_unswapped(m); } - vm_page_wakeup(m); + vm_page_xunbusy(m); vm_page_lock(m); if (bp->bio_cmd == BIO_DELETE && len == PAGE_SIZE) vm_page_free(m); diff --git a/sys/dev/mfi/mfi_cam.c b/sys/dev/mfi/mfi_cam.c index b6ceb57f286..e21b089fabf 100644 --- a/sys/dev/mfi/mfi_cam.c +++ b/sys/dev/mfi/mfi_cam.c @@ -308,17 +308,16 @@ mfip_cam_rescan(struct mfi_softc *sc, uint32_t tid) return; } camsc->state = MFIP_STATE_RESCAN; - mtx_unlock(&sc->mfi_io_lock); ccb = xpt_alloc_ccb_nowait(); if (ccb == NULL) { + mtx_unlock(&sc->mfi_io_lock); device_printf(sc->mfi_dev, "Cannot allocate ccb for bus rescan.\n"); return; } sim = camsc->sim; - mtx_lock(&sc->mfi_io_lock); if (xpt_create_path(&ccb->ccb_h.path, NULL, cam_sim_path(sim), tid, CAM_LUN_WILDCARD) != CAM_REQ_CMP) { xpt_free_ccb(ccb); @@ -327,11 +326,8 @@ mfip_cam_rescan(struct mfi_softc *sc, uint32_t tid) "Cannot create path for bus rescan.\n"); return; } - mtx_unlock(&sc->mfi_io_lock); - xpt_rescan(ccb); - mtx_lock(&sc->mfi_io_lock); camsc->state = MFIP_STATE_NONE; mtx_unlock(&sc->mfi_io_lock); } diff --git a/sys/dev/mfi/mfi_linux.c b/sys/dev/mfi/mfi_linux.c index 3328a6650cd..429d49600c0 100644 --- a/sys/dev/mfi/mfi_linux.c +++ b/sys/dev/mfi/mfi_linux.c @@ -84,6 +84,7 @@ MODULE_DEPEND(mfi, linux, 1, 1, 1); static int mfi_linux_ioctl(struct thread *p, struct linux_ioctl_args *args) { + cap_rights_t rights; struct file *fp; int error; u_long cmd = args->cmd; @@ -97,7 +98,8 @@ mfi_linux_ioctl(struct thread *p, struct linux_ioctl_args *args) break; } - if ((error = fget(p, args->fd, CAP_IOCTL, &fp)) != 0) + error = fget(p, args->fd, cap_rights_init(&rights, CAP_IOCTL), &fp); + if (error != 0) return (error); error = fo_ioctl(fp, cmd, (caddr_t)args->arg, p->td_ucred, p); fdrop(fp, p); diff --git a/sys/dev/mfi/mfi_pci.c b/sys/dev/mfi/mfi_pci.c index 5f757da9632..657289343ac 100644 --- a/sys/dev/mfi/mfi_pci.c +++ b/sys/dev/mfi/mfi_pci.c @@ -187,7 +187,6 @@ mfi_pci_attach(device_t dev) { struct mfi_softc *sc; struct mfi_ident *m; - uint32_t command; int count, error; sc = device_get_softc(dev); @@ -196,19 +195,8 @@ mfi_pci_attach(device_t dev) m = mfi_find_ident(dev); sc->mfi_flags = m->flags; - /* Verify that the adapter can be set up in PCI space */ - command = pci_read_config(dev, PCIR_COMMAND, 2); - command |= PCIM_CMD_BUSMASTEREN; - pci_write_config(dev, PCIR_COMMAND, command, 2); - command = pci_read_config(dev, PCIR_COMMAND, 2); - if ((command & PCIM_CMD_BUSMASTEREN) == 0) { - device_printf(dev, "Can't enable PCI busmaster\n"); - return (ENXIO); - } - if ((command & PCIM_CMD_MEMEN) == 0) { - device_printf(dev, "PCI memory window not available\n"); - return (ENXIO); - } + /* Ensure busmastering is enabled */ + pci_enable_busmaster(dev); /* Allocate PCI registers */ if ((sc->mfi_flags & MFI_FLAGS_1064R) || diff --git a/sys/dev/mfi/mfi_syspd.c b/sys/dev/mfi/mfi_syspd.c index 739b8662196..e9587dd74d3 100644 --- a/sys/dev/mfi/mfi_syspd.c +++ b/sys/dev/mfi/mfi_syspd.c @@ -126,7 +126,8 @@ mfi_syspd_attach(device_t dev) sectors / (1024 * 1024 / secsize), sectors, sc->pd_id); sc->pd_disk = disk_alloc(); sc->pd_disk->d_drv1 = sc; - sc->pd_disk->d_maxsize = sc->pd_controller->mfi_max_io * secsize; + sc->pd_disk->d_maxsize = min(sc->pd_controller->mfi_max_io * secsize, + (sc->pd_controller->mfi_max_sge - 1) * PAGE_SIZE); sc->pd_disk->d_name = "mfisyspd"; sc->pd_disk->d_open = mfi_syspd_open; sc->pd_disk->d_close = mfi_syspd_close; diff --git a/sys/dev/mge/if_mge.c b/sys/dev/mge/if_mge.c index 3195b239988..2f89aadad48 100644 --- a/sys/dev/mge/if_mge.c +++ b/sys/dev/mge/if_mge.c @@ -1703,9 +1703,7 @@ mge_offload_setup_descriptor(struct mge_softc *sc, struct mge_desc_wrapper *dw) ip = (struct ip *)(m0->m_data + ehlen); cmd_status |= MGE_TX_IP_HDR_SIZE(ip->ip_hl); - - if ((m0->m_flags & M_FRAG) == 0) - cmd_status |= MGE_TX_NOT_FRAGMENT; + cmd_status |= MGE_TX_NOT_FRAGMENT; } if (csum_flags & CSUM_IP) diff --git a/sys/dev/mly/mly.c b/sys/dev/mly/mly.c index a5c568b1043..a58b21b8f08 100644 --- a/sys/dev/mly/mly.c +++ b/sys/dev/mly/mly.c @@ -333,7 +333,6 @@ static int mly_pci_attach(struct mly_softc *sc) { int i, error; - u_int32_t command; debug_called(1); @@ -342,21 +341,8 @@ mly_pci_attach(struct mly_softc *sc) /* * Verify that the adapter is correctly set up in PCI space. - * - * XXX we shouldn't do this; the PCI code should. */ - command = pci_read_config(sc->mly_dev, PCIR_COMMAND, 2); - command |= PCIM_CMD_BUSMASTEREN; - pci_write_config(sc->mly_dev, PCIR_COMMAND, command, 2); - command = pci_read_config(sc->mly_dev, PCIR_COMMAND, 2); - if (!(command & PCIM_CMD_BUSMASTEREN)) { - mly_printf(sc, "can't enable busmaster feature\n"); - goto fail; - } - if ((command & PCIM_CMD_MEMEN) == 0) { - mly_printf(sc, "memory window not available\n"); - goto fail; - } + pci_enable_busmaster(sc->mly_dev); /* * Allocate the PCI register window. diff --git a/sys/dev/mmc/mmc.c b/sys/dev/mmc/mmc.c index f101e659965..2ca08753388 100644 --- a/sys/dev/mmc/mmc.c +++ b/sys/dev/mmc/mmc.c @@ -106,6 +106,8 @@ struct mmc_ivars { #define CMD_RETRIES 3 +#define CARD_ID_FREQUENCY 400000 /* Spec requires 400kHz max during ID phase. */ + static SYSCTL_NODE(_hw, OID_AUTO, mmc, CTLFLAG_RD, NULL, "mmc driver"); static int mmc_debug; @@ -391,8 +393,9 @@ mmc_wait_for_req(struct mmc_softc *sc, struct mmc_request *req) while ((req->flags & MMC_REQ_DONE) == 0) msleep(req, &sc->sc_mtx, 0, "mmcreq", 0); MMC_UNLOCK(sc); - if (mmc_debug > 2 || (mmc_debug > 1 && req->cmd->error)) - device_printf(sc->dev, "RESULT: %d\n", req->cmd->error); + if (mmc_debug > 2 || (mmc_debug > 0 && req->cmd->error != MMC_ERR_NONE)) + device_printf(sc->dev, "CMD%d RESULT: %d\n", + req->cmd->opcode, req->cmd->error); return (0); } @@ -408,14 +411,21 @@ static int mmc_wait_for_cmd(struct mmc_softc *sc, struct mmc_command *cmd, int retries) { struct mmc_request mreq; + int err; - memset(&mreq, 0, sizeof(mreq)); - memset(cmd->resp, 0, sizeof(cmd->resp)); - cmd->retries = retries; - cmd->mrq = &mreq; - mreq.cmd = cmd; - mmc_wait_for_req(sc, &mreq); - return (cmd->error); + do { + memset(&mreq, 0, sizeof(mreq)); + memset(cmd->resp, 0, sizeof(cmd->resp)); + cmd->retries = 0; /* Retries done here, not in hardware. */ + cmd->mrq = &mreq; + mreq.cmd = cmd; + if (mmc_wait_for_req(sc, &mreq) != 0) + err = MMC_ERR_FAILED; + else + err = cmd->error; + } while (err != MMC_ERR_NONE && retries-- > 0); + + return (err); } static int @@ -423,24 +433,28 @@ mmc_wait_for_app_cmd(struct mmc_softc *sc, uint32_t rca, struct mmc_command *cmd, int retries) { struct mmc_command appcmd; - int err = MMC_ERR_NONE, i; + int err; - for (i = 0; i <= retries; i++) { + do { + memset(&appcmd, 0, sizeof(appcmd)); appcmd.opcode = MMC_APP_CMD; appcmd.arg = rca << 16; appcmd.flags = MMC_RSP_R1 | MMC_CMD_AC; appcmd.data = NULL; - mmc_wait_for_cmd(sc, &appcmd, 0); - err = appcmd.error; - if (err != MMC_ERR_NONE) - continue; - if (!(appcmd.resp[0] & R1_APP_CMD)) - return MMC_ERR_FAILED; - mmc_wait_for_cmd(sc, cmd, 0); - err = cmd->error; - if (err == MMC_ERR_NONE) - break; - } + if (mmc_wait_for_cmd(sc, &appcmd, 0) != 0) + err = MMC_ERR_FAILED; + else + err = appcmd.error; + if (err == MMC_ERR_NONE) { + if (!(appcmd.resp[0] & R1_APP_CMD)) + err = MMC_ERR_FAILED; + else if (mmc_wait_for_cmd(sc, cmd, 0) != 0) + err = MMC_ERR_FAILED; + else + err = cmd->error; + } + } while (err != MMC_ERR_NONE && retries-- > 0); + return (err); } @@ -459,8 +473,6 @@ mmc_wait_for_command(struct mmc_softc *sc, uint32_t opcode, err = mmc_wait_for_cmd(sc, &cmd, retries); if (err) return (err); - if (cmd.error) - return (cmd.error); if (resp) { if (flags & MMC_RSP_136) memcpy(resp, cmd.resp, 4 * sizeof(uint32_t)); @@ -486,7 +498,7 @@ mmc_idle_cards(struct mmc_softc *sc) cmd.arg = 0; cmd.flags = MMC_RSP_NONE | MMC_CMD_BC; cmd.data = NULL; - mmc_wait_for_cmd(sc, &cmd, 0); + mmc_wait_for_cmd(sc, &cmd, CMD_RETRIES); mmc_ms_delay(1); mmcbr_set_chip_select(dev, cs_dontcare); @@ -579,7 +591,7 @@ mmc_power_up(struct mmc_softc *sc) mmcbr_update_ios(dev); mmc_ms_delay(1); - mmcbr_set_clock(dev, mmcbr_get_f_min(sc->dev)); + mmcbr_set_clock(dev, CARD_ID_FREQUENCY); mmcbr_set_timing(dev, bus_timing_normal); mmcbr_set_power_mode(dev, power_on); mmcbr_update_ios(dev); @@ -616,6 +628,7 @@ mmc_switch(struct mmc_softc *sc, uint8_t set, uint8_t index, uint8_t value) struct mmc_command cmd; int err; + memset(&cmd, 0, sizeof(cmd)); cmd.opcode = MMC_SWITCH_FUNC; cmd.arg = (MMC_SWITCH_FUNC_WR << 24) | (index << 16) | @@ -623,7 +636,7 @@ mmc_switch(struct mmc_softc *sc, uint8_t set, uint8_t index, uint8_t value) set; cmd.flags = MMC_RSP_R1B | MMC_CMD_AC; cmd.data = NULL; - err = mmc_wait_for_cmd(sc, &cmd, 0); + err = mmc_wait_for_cmd(sc, &cmd, CMD_RETRIES); return (err); } @@ -635,8 +648,8 @@ mmc_sd_switch(struct mmc_softc *sc, uint8_t mode, uint8_t grp, uint8_t value, struct mmc_command cmd; struct mmc_data data; - memset(&cmd, 0, sizeof(struct mmc_command)); - memset(&data, 0, sizeof(struct mmc_data)); + memset(&cmd, 0, sizeof(cmd)); + memset(&data, 0, sizeof(data)); memset(res, 0, 64); cmd.opcode = SD_SWITCH_FUNC; @@ -663,14 +676,14 @@ mmc_set_card_bus_width(struct mmc_softc *sc, uint16_t rca, int width) uint8_t value; if (mmcbr_get_mode(sc->dev) == mode_sd) { - memset(&cmd, 0, sizeof(struct mmc_command)); + memset(&cmd, 0, sizeof(cmd)); cmd.opcode = ACMD_SET_CLR_CARD_DETECT; cmd.flags = MMC_RSP_R1 | MMC_CMD_AC; cmd.arg = SD_CLR_CARD_DETECT; err = mmc_wait_for_app_cmd(sc, rca, &cmd, CMD_RETRIES); if (err != 0) return (err); - memset(&cmd, 0, sizeof(struct mmc_command)); + memset(&cmd, 0, sizeof(cmd)); cmd.opcode = ACMD_SET_BUS_WIDTH; cmd.flags = MMC_RSP_R1 | MMC_CMD_AC; switch (width) { @@ -746,6 +759,8 @@ mmc_test_bus_width(struct mmc_softc *sc) mmcbr_set_bus_width(sc->dev, bus_width_8); mmcbr_update_ios(sc->dev); + memset(&cmd, 0, sizeof(cmd)); + memset(&data, 0, sizeof(data)); cmd.opcode = MMC_BUSTEST_W; cmd.arg = 0; cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC; @@ -756,6 +771,8 @@ mmc_test_bus_width(struct mmc_softc *sc) data.flags = MMC_DATA_WRITE; mmc_wait_for_cmd(sc, &cmd, 0); + memset(&cmd, 0, sizeof(cmd)); + memset(&data, 0, sizeof(data)); cmd.opcode = MMC_BUSTEST_R; cmd.arg = 0; cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC; @@ -777,6 +794,8 @@ mmc_test_bus_width(struct mmc_softc *sc) mmcbr_set_bus_width(sc->dev, bus_width_4); mmcbr_update_ios(sc->dev); + memset(&cmd, 0, sizeof(cmd)); + memset(&data, 0, sizeof(data)); cmd.opcode = MMC_BUSTEST_W; cmd.arg = 0; cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC; @@ -787,6 +806,8 @@ mmc_test_bus_width(struct mmc_softc *sc) data.flags = MMC_DATA_WRITE; mmc_wait_for_cmd(sc, &cmd, 0); + memset(&cmd, 0, sizeof(cmd)); + memset(&data, 0, sizeof(data)); cmd.opcode = MMC_BUSTEST_R; cmd.arg = 0; cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC; @@ -1048,11 +1069,12 @@ mmc_all_send_cid(struct mmc_softc *sc, uint32_t *rawcid) struct mmc_command cmd; int err; + memset(&cmd, 0, sizeof(cmd)); cmd.opcode = MMC_ALL_SEND_CID; cmd.arg = 0; cmd.flags = MMC_RSP_R2 | MMC_CMD_BCR; cmd.data = NULL; - err = mmc_wait_for_cmd(sc, &cmd, 0); + err = mmc_wait_for_cmd(sc, &cmd, CMD_RETRIES); memcpy(rawcid, cmd.resp, 4 * sizeof(uint32_t)); return (err); } @@ -1063,11 +1085,12 @@ mmc_send_csd(struct mmc_softc *sc, uint16_t rca, uint32_t *rawcsd) struct mmc_command cmd; int err; + memset(&cmd, 0, sizeof(cmd)); cmd.opcode = MMC_SEND_CSD; cmd.arg = rca << 16; cmd.flags = MMC_RSP_R2 | MMC_CMD_BCR; cmd.data = NULL; - err = mmc_wait_for_cmd(sc, &cmd, 0); + err = mmc_wait_for_cmd(sc, &cmd, CMD_RETRIES); memcpy(rawcsd, cmd.resp, 4 * sizeof(uint32_t)); return (err); } @@ -1079,8 +1102,8 @@ mmc_app_send_scr(struct mmc_softc *sc, uint16_t rca, uint32_t *rawscr) struct mmc_command cmd; struct mmc_data data; - memset(&cmd, 0, sizeof(struct mmc_command)); - memset(&data, 0, sizeof(struct mmc_data)); + memset(&cmd, 0, sizeof(cmd)); + memset(&data, 0, sizeof(data)); memset(rawscr, 0, 8); cmd.opcode = ACMD_SEND_SCR; @@ -1105,8 +1128,8 @@ mmc_send_ext_csd(struct mmc_softc *sc, uint8_t *rawextcsd) struct mmc_command cmd; struct mmc_data data; - memset(&cmd, 0, sizeof(struct mmc_command)); - memset(&data, 0, sizeof(struct mmc_data)); + memset(&cmd, 0, sizeof(cmd)); + memset(&data, 0, sizeof(data)); memset(rawextcsd, 0, 512); cmd.opcode = MMC_SEND_EXT_CSD; @@ -1129,8 +1152,8 @@ mmc_app_sd_status(struct mmc_softc *sc, uint16_t rca, uint32_t *rawsdstatus) struct mmc_command cmd; struct mmc_data data; - memset(&cmd, 0, sizeof(struct mmc_command)); - memset(&data, 0, sizeof(struct mmc_data)); + memset(&cmd, 0, sizeof(cmd)); + memset(&data, 0, sizeof(data)); memset(rawsdstatus, 0, 64); cmd.opcode = ACMD_SD_STATUS; @@ -1154,11 +1177,12 @@ mmc_set_relative_addr(struct mmc_softc *sc, uint16_t resp) struct mmc_command cmd; int err; + memset(&cmd, 0, sizeof(cmd)); cmd.opcode = MMC_SET_RELATIVE_ADDR; cmd.arg = resp << 16; cmd.flags = MMC_RSP_R6 | MMC_CMD_BCR; cmd.data = NULL; - err = mmc_wait_for_cmd(sc, &cmd, 0); + err = mmc_wait_for_cmd(sc, &cmd, CMD_RETRIES); return (err); } @@ -1168,11 +1192,12 @@ mmc_send_relative_addr(struct mmc_softc *sc, uint32_t *resp) struct mmc_command cmd; int err; + memset(&cmd, 0, sizeof(cmd)); cmd.opcode = SD_SEND_RELATIVE_ADDR; cmd.arg = 0; cmd.flags = MMC_RSP_R6 | MMC_CMD_BCR; cmd.data = NULL; - err = mmc_wait_for_cmd(sc, &cmd, 0); + err = mmc_wait_for_cmd(sc, &cmd, CMD_RETRIES); *resp = cmd.resp[0]; return (err); } @@ -1183,11 +1208,12 @@ mmc_send_status(struct mmc_softc *sc, uint16_t rca, uint32_t *status) struct mmc_command cmd; int err; + memset(&cmd, 0, sizeof(cmd)); cmd.opcode = MMC_SEND_STATUS; cmd.arg = rca << 16; cmd.flags = MMC_RSP_R1 | MMC_CMD_AC; cmd.data = NULL; - err = mmc_wait_for_cmd(sc, &cmd, 0); + err = mmc_wait_for_cmd(sc, &cmd, CMD_RETRIES); *status = cmd.resp[0]; return (err); } @@ -1198,18 +1224,19 @@ mmc_set_blocklen(struct mmc_softc *sc, uint32_t len) struct mmc_command cmd; int err; + memset(&cmd, 0, sizeof(cmd)); cmd.opcode = MMC_SET_BLOCKLEN; cmd.arg = len; cmd.flags = MMC_RSP_R1 | MMC_CMD_AC; cmd.data = NULL; - err = mmc_wait_for_cmd(sc, &cmd, 0); + err = mmc_wait_for_cmd(sc, &cmd, CMD_RETRIES); return (err); } static void mmc_log_card(device_t dev, struct mmc_ivars *ivar, int newcard) { - device_printf(dev, "Card at relative address %d%s:\n", + device_printf(dev, "Card at relative address 0x%04x%s:\n", ivar->rca, newcard ? " added" : ""); device_printf(dev, " card: %s\n", ivar->card_id_string); device_printf(dev, " bus: %ubit, %uMHz%s\n", @@ -1534,7 +1561,7 @@ mmc_go_discovery(struct mmc_softc *sc) mmc_idle_cards(sc); } else { mmcbr_set_bus_mode(dev, opendrain); - mmcbr_set_clock(dev, mmcbr_get_f_min(dev)); + mmcbr_set_clock(dev, CARD_ID_FREQUENCY); mmcbr_update_ios(dev); /* XXX recompute vdd based on new cards? */ } @@ -1572,11 +1599,10 @@ static int mmc_calculate_clock(struct mmc_softc *sc) { int max_dtr, max_hs_dtr, max_timing; - int nkid, i, f_min, f_max; + int nkid, i, f_max; device_t *kids; struct mmc_ivars *ivar; - f_min = mmcbr_get_f_min(sc->dev); f_max = mmcbr_get_f_max(sc->dev); max_dtr = max_hs_dtr = f_max; if ((mmcbr_get_caps(sc->dev) & MMC_CAP_HSPEED)) @@ -1735,3 +1761,4 @@ DRIVER_MODULE(mmc, at91_mci, mmc_driver, mmc_devclass, NULL, NULL); DRIVER_MODULE(mmc, sdhci_pci, mmc_driver, mmc_devclass, NULL, NULL); DRIVER_MODULE(mmc, sdhci_bcm, mmc_driver, mmc_devclass, NULL, NULL); DRIVER_MODULE(mmc, sdhci_fdt, mmc_driver, mmc_devclass, NULL, NULL); +DRIVER_MODULE(mmc, sdhci_ti, mmc_driver, mmc_devclass, NULL, NULL); diff --git a/sys/dev/mmc/mmcsd.c b/sys/dev/mmc/mmcsd.c index c28a5d3ec65..f24537e7920 100644 --- a/sys/dev/mmc/mmcsd.c +++ b/sys/dev/mmc/mmcsd.c @@ -328,6 +328,7 @@ mmcsd_rw(struct mmcsd_softc *sc, struct bio *bp) memset(&req, 0, sizeof(req)); memset(&cmd, 0, sizeof(cmd)); memset(&stop, 0, sizeof(stop)); + memset(&data, 0, sizeof(data)); cmd.mrq = &req; req.cmd = &cmd; cmd.data = &data; diff --git a/sys/dev/mn/if_mn.c b/sys/dev/mn/if_mn.c index 239aa0fc2b8..63ae5f602eb 100644 --- a/sys/dev/mn/if_mn.c +++ b/sys/dev/mn/if_mn.c @@ -1346,9 +1346,9 @@ mn_attach (device_t self) return(ENXIO); } - u = pci_read_config(self, PCIR_COMMAND, 1); + u = pci_read_config(self, PCIR_COMMAND, 2); printf("%x\n", u); - pci_write_config(self, PCIR_COMMAND, u | PCIM_CMD_PERRESPEN | PCIM_CMD_BUSMASTEREN | PCIM_CMD_MEMEN, 1); + pci_write_config(self, PCIR_COMMAND, u | PCIM_CMD_PERRESPEN | PCIM_CMD_BUSMASTEREN, 2); #if 0 pci_write_config(self, PCIR_COMMAND, 0x02800046, 4); #endif diff --git a/sys/dev/mps/mps.c b/sys/dev/mps/mps.c index b6c549aee24..3cfc5eb438e 100644 --- a/sys/dev/mps/mps.c +++ b/sys/dev/mps/mps.c @@ -123,6 +123,9 @@ typedef union _reply_descriptor { } u; }reply_descriptor,address_descriptor; +/* Rate limit chain-fail messages to 1 per minute */ +static struct timeval mps_chainfail_interval = { 60, 0 }; + /* * sleep_flag can be either CAN_SLEEP or NO_SLEEP. * If this function is called from process context, it can sleep @@ -1371,6 +1374,11 @@ mps_get_tunables(struct mps_softc *sc) snprintf(tmpstr, sizeof(tmpstr), "dev.mps.%d.max_chains", device_get_unit(sc->mps_dev)); TUNABLE_INT_FETCH(tmpstr, &sc->max_chains); + + bzero(sc->exclude_ids, sizeof(sc->exclude_ids)); + snprintf(tmpstr, sizeof(tmpstr), "dev.mps.%d.exclude_ids", + device_get_unit(sc->mps_dev)); + TUNABLE_STR_FETCH(tmpstr, sc->exclude_ids, sizeof(sc->exclude_ids)); } static void @@ -1462,6 +1470,7 @@ mps_attach(struct mps_softc *sc) mtx_init(&sc->mps_mtx, "MPT2SAS lock", NULL, MTX_DEF); callout_init_mtx(&sc->periodic, &sc->mps_mtx, 0); TAILQ_INIT(&sc->event_list); + timevalclear(&sc->lastfail); if ((error = mps_transition_ready(sc)) != 0) { mps_printf(sc, "%s failed to transition ready\n", __func__); @@ -2408,8 +2417,9 @@ mps_data_cb(void *arg, bus_dma_segment_t *segs, int nsegs, int error) sflags, nsegs - i); if (error != 0) { /* Resource shortage, roll back! */ - mps_dprint(sc, MPS_INFO, "Out of chain frames, " - "consider increasing hw.mps.max_chains.\n"); + if (ratecheck(&sc->lastfail, &mps_chainfail_interval)) + mps_dprint(sc, MPS_INFO, "Out of chain frames, " + "consider increasing hw.mps.max_chains.\n"); cm->cm_flags |= MPS_CM_FLAGS_CHAIN_FAILED; mps_complete_command(sc, cm); return; diff --git a/sys/dev/mps/mps_pci.c b/sys/dev/mps/mps_pci.c index e64acdc83c4..5417ae6aad8 100644 --- a/sys/dev/mps/mps_pci.c +++ b/sys/dev/mps/mps_pci.c @@ -183,7 +183,6 @@ mps_pci_attach(device_t dev) { struct mps_softc *sc; struct mps_ident *m; - uint16_t command; int error; sc = device_get_softc(dev); @@ -193,18 +192,7 @@ mps_pci_attach(device_t dev) sc->mps_flags = m->flags; /* Twiddle basic PCI config bits for a sanity check */ - command = pci_read_config(dev, PCIR_COMMAND, 2); - command |= PCIM_CMD_BUSMASTEREN; - pci_write_config(dev, PCIR_COMMAND, command, 2); - command = pci_read_config(dev, PCIR_COMMAND, 2); - if ((command & PCIM_CMD_BUSMASTEREN) == 0) { - mps_printf(sc, "Cannot enable PCI busmaster\n"); - return (ENXIO); - } - if ((command & PCIM_CMD_MEMEN) == 0) { - mps_printf(sc, "PCI memory window not available\n"); - return (ENXIO); - } + pci_enable_busmaster(dev); /* Allocate the System Interface Register Set */ sc->mps_regs_rid = PCIR_BAR(1); diff --git a/sys/dev/mps/mps_sas.c b/sys/dev/mps/mps_sas.c index 9a4964a500a..b6ee542a5f4 100644 --- a/sys/dev/mps/mps_sas.c +++ b/sys/dev/mps/mps_sas.c @@ -307,6 +307,10 @@ mpssas_log_command(struct mps_command *cm, u_int level, const char *fmt, ...) if (cm == NULL) return; + /* No need to be in here if debugging isn't enabled */ + if ((cm->cm_sc->mps_debug & level) == 0) + return; + sbuf_new(&sb, str, sizeof(str), 0); va_start(ap, fmt); @@ -2099,7 +2103,7 @@ mpssas_scsiio_complete(struct mps_softc *sc, struct mps_command *cm) cm->cm_targ->completed++; cm->cm_targ->outstanding--; TAILQ_REMOVE(&cm->cm_targ->commands, cm, cm_link); - ccb->ccb_h.status |= ~(CAM_STATUS_MASK | CAM_SIM_QUEUED); + ccb->ccb_h.status &= ~(CAM_STATUS_MASK | CAM_SIM_QUEUED); if (cm->cm_state == MPS_CM_STATE_TIMEDOUT) { TAILQ_REMOVE(&cm->cm_targ->timedout_commands, cm, cm_recovery); @@ -2141,7 +2145,7 @@ mpssas_scsiio_complete(struct mps_softc *sc, struct mps_command *cm) * because there can be no reply when we haven't actually * gone out to the hardware. */ - ccb->ccb_h.status |= CAM_REQUEUE_REQ; + ccb->ccb_h.status = CAM_REQUEUE_REQ; /* * Currently the only error included in the mask is @@ -3553,3 +3557,20 @@ mpssas_portenable_complete(struct mps_softc *sc, struct mps_command *cm) xpt_release_simq(sassc->sim, 1); } +int +mpssas_check_id(struct mpssas_softc *sassc, int id) +{ + struct mps_softc *sc = sassc->sc; + char *ids; + char *name; + + ids = &sc->exclude_ids[0]; + while((name = strsep(&ids, ",")) != NULL) { + if (name[0] == '\0') + continue; + if (strtol(name, NULL, 0) == (long)id) + return (1); + } + + return (0); +} diff --git a/sys/dev/mps/mps_sas.h b/sys/dev/mps/mps_sas.h index 662a29a0095..f6a08204992 100644 --- a/sys/dev/mps/mps_sas.h +++ b/sys/dev/mps/mps_sas.h @@ -158,3 +158,4 @@ void mpssas_startup_decrement(struct mpssas_softc *sassc); struct mps_command * mpssas_alloc_tm(struct mps_softc *sc); void mpssas_free_tm(struct mps_softc *sc, struct mps_command *tm); void mpssas_firmware_event_work(void *arg, int pending); +int mpssas_check_id(struct mpssas_softc *sassc, int id); diff --git a/sys/dev/mps/mps_sas_lsi.c b/sys/dev/mps/mps_sas_lsi.c index 85d6d1fd5f1..c219be14bb1 100644 --- a/sys/dev/mps/mps_sas_lsi.c +++ b/sys/dev/mps/mps_sas_lsi.c @@ -669,6 +669,13 @@ mpssas_add_device(struct mps_softc *sc, u16 handle, u8 linkrate){ error = ENXIO; goto out; } + + if (mpssas_check_id(sassc, id) != 0) { + device_printf(sc->mps_dev, "Excluding target id %d\n", id); + error = ENXIO; + goto out; + } + mps_dprint(sc, MPS_MAPPING, "SAS Address from SAS device page0 = %jx\n", sas_address); targ = &sassc->targets[id]; @@ -891,7 +898,9 @@ mpssas_volume_add(struct mps_softc *sc, u16 handle) free(lun, M_MPT2); } SLIST_INIT(&targ->luns); +#if __FreeBSD_version < 1000039 if ((sassc->flags & MPSSAS_IN_STARTUP) == 0) +#endif mpssas_rescan_target(sc, targ); mps_dprint(sc, MPS_MAPPING, "RAID target id %d added (WWID = 0x%jx)\n", targ->tid, wwid); diff --git a/sys/dev/mps/mpsvar.h b/sys/dev/mps/mpsvar.h index 5f8a015d8f2..14015fa0d6d 100644 --- a/sys/dev/mps/mpsvar.h +++ b/sys/dev/mps/mpsvar.h @@ -414,6 +414,9 @@ struct mps_softc { uint16_t DD_block_exponent; uint64_t DD_max_lba; struct mps_column_map DD_column_map[MPS_MAX_DISKS_IN_VOL]; + + char exclude_ids[80]; + struct timeval lastfail; }; struct mps_config_params { diff --git a/sys/dev/mpt/mpt_pci.c b/sys/dev/mpt/mpt_pci.c index d6a1d383ea7..74c52920a54 100644 --- a/sys/dev/mpt/mpt_pci.c +++ b/sys/dev/mpt/mpt_pci.c @@ -389,16 +389,11 @@ mpt_pci_attach(device_t dev) /* Print INFO level (if any) if bootverbose is set */ mpt->verbose += (bootverbose != 0)? 1 : 0; } - /* Make sure memory access decoders are enabled */ - cmd = pci_read_config(dev, PCIR_COMMAND, 2); - if ((cmd & PCIM_CMD_MEMEN) == 0) { - device_printf(dev, "Memory accesses disabled"); - return (ENXIO); - } /* * Make sure that SERR, PERR, WRITE INVALIDATE and BUSMASTER are set. */ + cmd = pci_read_config(dev, PCIR_COMMAND, 2); cmd |= PCIM_CMD_SERRESPEN | PCIM_CMD_PERRESPEN | PCIM_CMD_BUSMASTEREN | PCIM_CMD_MWRICEN; diff --git a/sys/dev/mwl/if_mwl.c b/sys/dev/mwl/if_mwl.c index 1d2b6e38b01..bf669620b12 100644 --- a/sys/dev/mwl/if_mwl.c +++ b/sys/dev/mwl/if_mwl.c @@ -2621,8 +2621,8 @@ mwl_rxbuf_init(struct mwl_softc *sc, struct mwl_rxbuf *bf) return 0; } -static void -mwl_ext_free(void *data, void *arg) +static int +mwl_ext_free(struct mbuf *m, void *data, void *arg) { struct mwl_softc *sc = arg; @@ -2637,6 +2637,7 @@ mwl_ext_free(void *data, void *arg) sc->sc_rxblocked = 0; mwl_hal_intrset(sc->sc_mh, sc->sc_imask); } + return (EXT_FREE_OK); } struct mwl_frame_bar { diff --git a/sys/dev/mwl/if_mwl_pci.c b/sys/dev/mwl/if_mwl_pci.c index d800a5470c2..d4d00dc95f3 100644 --- a/sys/dev/mwl/if_mwl_pci.c +++ b/sys/dev/mwl/if_mwl_pci.c @@ -120,29 +120,6 @@ mwl_pci_probe(device_t dev) return ENXIO; } -static u_int32_t -mwl_pci_setup(device_t dev) -{ - u_int32_t cmd; - - /* - * Enable memory mapping and bus mastering. - */ - cmd = pci_read_config(dev, PCIR_COMMAND, 4); - cmd |= PCIM_CMD_MEMEN | PCIM_CMD_BUSMASTEREN; - pci_write_config(dev, PCIR_COMMAND, cmd, 4); - cmd = pci_read_config(dev, PCIR_COMMAND, 4); - if ((cmd & PCIM_CMD_MEMEN) == 0) { - device_printf(dev, "failed to enable memory mapping\n"); - return 0; - } - if ((cmd & PCIM_CMD_BUSMASTEREN) == 0) { - device_printf(dev, "failed to enable bus mastering\n"); - return 0; - } - return 1; -} - static int mwl_pci_attach(device_t dev) { @@ -152,11 +129,8 @@ mwl_pci_attach(device_t dev) sc->sc_dev = dev; - /* - * Enable memory mapping and bus mastering. - */ - if (!mwl_pci_setup(dev)) - return 0; + pci_enable_busmaster(dev); + /* * Setup memory-mapping of PCI registers. */ @@ -285,8 +259,7 @@ mwl_pci_resume(device_t dev) { struct mwl_pci_softc *psc = device_get_softc(dev); - if (!mwl_pci_setup(dev)) - return ENXIO; + pci_enable_busmaster(dev); mwl_resume(&psc->sc_sc); diff --git a/sys/dev/mxge/if_mxge.c b/sys/dev/mxge/if_mxge.c index 4dcea0f9eb8..c7064f555d4 100644 --- a/sys/dev/mxge/if_mxge.c +++ b/sys/dev/mxge/if_mxge.c @@ -3827,7 +3827,7 @@ mxge_setup_cfg_space(mxge_softc_t *sc) { device_t dev = sc->dev; int reg; - uint16_t cmd, lnk, pectl; + uint16_t lnk, pectl; /* find the PCIe link width and set max read request to 4KB*/ if (pci_find_cap(dev, PCIY_EXPRESS, ®) == 0) { @@ -3847,9 +3847,6 @@ mxge_setup_cfg_space(mxge_softc_t *sc) /* Enable DMA and Memory space access */ pci_enable_busmaster(dev); - cmd = pci_read_config(dev, PCIR_COMMAND, 2); - cmd |= PCIM_CMD_MEMEN; - pci_write_config(dev, PCIR_COMMAND, cmd, 2); } static uint32_t diff --git a/sys/dev/nfe/if_nfe.c b/sys/dev/nfe/if_nfe.c index 13099e9fa9e..3bcda25f9aa 100644 --- a/sys/dev/nfe/if_nfe.c +++ b/sys/dev/nfe/if_nfe.c @@ -2390,7 +2390,7 @@ nfe_encap(struct nfe_softc *sc, struct mbuf **m_head) bus_dmamap_t map; bus_dma_segment_t segs[NFE_MAX_SCATTER]; int error, i, nsegs, prod, si; - uint32_t tso_segsz; + uint32_t tsosegsz; uint16_t cflags, flags; struct mbuf *m; @@ -2429,9 +2429,9 @@ nfe_encap(struct nfe_softc *sc, struct mbuf **m_head) m = *m_head; cflags = flags = 0; - tso_segsz = 0; + tsosegsz = 0; if ((m->m_pkthdr.csum_flags & CSUM_TSO) != 0) { - tso_segsz = (uint32_t)m->m_pkthdr.tso_segsz << + tsosegsz = (uint32_t)m->m_pkthdr.tso_segsz << NFE_TX_TSO_SHIFT; cflags &= ~(NFE_TX_IP_CSUM | NFE_TX_TCP_UDP_CSUM); cflags |= NFE_TX_TSO; @@ -2482,14 +2482,14 @@ nfe_encap(struct nfe_softc *sc, struct mbuf **m_head) if ((m->m_flags & M_VLANTAG) != 0) desc64->vtag = htole32(NFE_TX_VTAG | m->m_pkthdr.ether_vtag); - if (tso_segsz != 0) { + if (tsosegsz != 0) { /* * XXX * The following indicates the descriptor element * is a 32bit quantity. */ - desc64->length |= htole16((uint16_t)tso_segsz); - desc64->flags |= htole16(tso_segsz >> 16); + desc64->length |= htole16((uint16_t)tsosegsz); + desc64->flags |= htole16(tsosegsz >> 16); } /* * finally, set the valid/checksum/TSO bit in the first @@ -2502,14 +2502,14 @@ nfe_encap(struct nfe_softc *sc, struct mbuf **m_head) else desc32->flags |= htole16(NFE_TX_LASTFRAG_V1); desc32 = &sc->txq.desc32[si]; - if (tso_segsz != 0) { + if (tsosegsz != 0) { /* * XXX * The following indicates the descriptor element * is a 32bit quantity. */ - desc32->length |= htole16((uint16_t)tso_segsz); - desc32->flags |= htole16(tso_segsz >> 16); + desc32->length |= htole16((uint16_t)tsosegsz); + desc32->flags |= htole16(tsosegsz >> 16); } /* * finally, set the valid/checksum/TSO bit in the first @@ -3260,7 +3260,7 @@ nfe_stats_update(struct nfe_softc *sc) if ((sc->nfe_flags & NFE_MIB_V3) != 0) { stats->tx_unicast += NFE_READ(sc, NFE_TX_UNICAST); stats->tx_multicast += NFE_READ(sc, NFE_TX_MULTICAST); - stats->rx_broadcast += NFE_READ(sc, NFE_TX_BROADCAST); + stats->tx_broadcast += NFE_READ(sc, NFE_TX_BROADCAST); } } diff --git a/sys/dev/ntb/if_ntb/if_ntb.c b/sys/dev/ntb/if_ntb/if_ntb.c index 55b19c5dd43..e86ed53b3bb 100644 --- a/sys/dev/ntb/if_ntb/if_ntb.c +++ b/sys/dev/ntb/if_ntb/if_ntb.c @@ -104,7 +104,7 @@ struct ntb_transport_qp { bool client_ready; bool qp_link; - uint8_t qp_num; /* Only 64 QP's are allowed. 0-63 */ + uint8_t qp_num; /* Only 64 QPs are allowed. 0-63 */ struct ntb_rx_info *rx_info; struct ntb_rx_info *remote_rx_info; @@ -279,14 +279,14 @@ ntb_handle_module_events(struct module *m, int what, void *arg) return (err); } -static moduledata_t ntb_transport_mod = { - "ntb_transport", +static moduledata_t if_ntb_mod = { + "if_ntb", ntb_handle_module_events, NULL }; -DECLARE_MODULE(ntb_transport, ntb_transport_mod, SI_SUB_KLD, SI_ORDER_ANY); -MODULE_DEPEND(ntb_transport, ntb_hw, 1, 1, 1); +DECLARE_MODULE(if_ntb, if_ntb_mod, SI_SUB_KLD, SI_ORDER_ANY); +MODULE_DEPEND(if_ntb, ntb_hw, 1, 1, 1); static int ntb_setup_interface() @@ -297,7 +297,7 @@ ntb_setup_interface() net_softc.ntb = devclass_get_softc(devclass_find("ntb_hw"), 0); if (net_softc.ntb == NULL) { - printf("ntb: Can't find devclass\n"); + printf("ntb: Cannot find devclass\n"); return (ENXIO); } @@ -334,14 +334,19 @@ ntb_setup_interface() static int ntb_teardown_interface() { - struct ifnet *ifp = net_softc.ifp; - ntb_transport_link_down(net_softc.qp); + if (net_softc.qp != NULL) + ntb_transport_link_down(net_softc.qp); - ether_ifdetach(ifp); - if_free(ifp); - ntb_transport_free_queue(net_softc.qp); - ntb_transport_free(&net_softc); + if (net_softc.ifp != NULL) { + ether_ifdetach(net_softc.ifp); + if_free(net_softc.ifp); + } + + if (net_softc.qp != NULL) { + ntb_transport_free_queue(net_softc.qp); + ntb_transport_free(&net_softc); + } return (0); } @@ -405,7 +410,7 @@ ntb_start(struct ifnet *ifp) m_length(m_head, NULL)); if (rc != 0) { CTR1(KTR_NTB, - "TX: couldn't tx mbuf %p. Returning to snd q", + "TX: could not tx mbuf %p. Returning to snd q", m_head); if (rc == EAGAIN) { ifp->if_drv_flags |= IFF_DRV_OACTIVE; @@ -475,8 +480,11 @@ ntb_transport_init(struct ntb_softc *ntb) if (rc != 0) goto err; - if (ntb_query_link_status(ntb)) + if (ntb_query_link_status(ntb)) { + if (bootverbose) + device_printf(ntb_get_device(ntb), "link up\n"); callout_reset(&nt->link_work, 0, ntb_transport_link_work, nt); + } return (0); @@ -497,7 +505,7 @@ ntb_transport_free(void *transport) callout_drain(&nt->link_work); - /* verify that all the qp's are freed */ + /* verify that all the qps are freed */ for (i = 0; i < nt->max_qps; i++) if (!test_bit(i, &nt->qp_bitmap)) ntb_transport_free_queue(&nt->qps[i]); @@ -673,6 +681,8 @@ ntb_transport_link_up(struct ntb_transport_qp *qp) return; qp->client_ready = NTB_LINK_UP; + if (bootverbose) + device_printf(ntb_get_device(qp->ntb), "qp client ready\n"); if (qp->transport->transport_link == NTB_LINK_UP) callout_reset(&qp->link_work, 0, ntb_qp_link_work, qp); @@ -709,7 +719,7 @@ ntb_transport_tx_enqueue(struct ntb_transport_qp *qp, void *cb, void *data, entry = ntb_list_rm(&qp->ntb_tx_free_q_lock, &qp->tx_free_q); if (entry == NULL) { - CTR0(KTR_NTB, "TX: couldn't get entry from tx_free_q"); + CTR0(KTR_NTB, "TX: could not get entry from tx_free_q"); return (ENOMEM); } CTR1(KTR_NTB, "TX: got entry %p from tx_free_q", entry); @@ -988,9 +998,13 @@ ntb_transport_event_callback(void *data, enum ntb_hw_event event) switch (event) { case NTB_EVENT_HW_LINK_UP: + if (bootverbose) + device_printf(ntb_get_device(nt->ntb), "HW link up\n"); callout_reset(&nt->link_work, 0, ntb_transport_link_work, nt); break; case NTB_EVENT_HW_LINK_DOWN: + if (bootverbose) + device_printf(ntb_get_device(nt->ntb), "HW link down\n"); ntb_transport_link_cleanup(nt); break; default: @@ -1071,6 +1085,8 @@ ntb_transport_link_work(void *arg) return; nt->transport_link = NTB_LINK_UP; + if (bootverbose) + device_printf(ntb_get_device(ntb), "transport link up\n"); for (i = 0; i < nt->max_qps; i++) { qp = &nt->qps[i]; @@ -1176,6 +1192,8 @@ ntb_qp_link_work(void *arg) qp->qp_link = NTB_LINK_UP; if (qp->event_handler != NULL) qp->event_handler(qp->cb_data, NTB_LINK_UP); + if (bootverbose) + device_printf(ntb_get_device(ntb), "qp link up\n"); } else if (nt->transport_link == NTB_LINK_UP) { callout_reset(&qp->link_work, NTB_LINK_DOWN_TIMEOUT * hz / 1000, ntb_qp_link_work, qp); diff --git a/sys/dev/ntb/ntb_hw/ntb_hw.c b/sys/dev/ntb/ntb_hw/ntb_hw.c index 72314dd5f27..019f2a77c8d 100644 --- a/sys/dev/ntb/ntb_hw/ntb_hw.c +++ b/sys/dev/ntb/ntb_hw/ntb_hw.c @@ -76,10 +76,18 @@ enum ntb_device_type { NTB_SOC }; +/* Device features and workarounds */ +#define HAS_FEATURE(feature) \ + ((ntb->features & (feature)) != 0) + +#define NTB_BAR_SIZE_4K (1 << 0) +#define NTB_REGS_THRU_MW (1 << 1) + struct ntb_hw_info { uint32_t device_id; - enum ntb_device_type type; const char *desc; + enum ntb_device_type type; + uint64_t features; }; struct ntb_pci_bar_info { @@ -108,6 +116,7 @@ struct ntb_db_cb { struct ntb_softc { device_t device; enum ntb_device_type type; + uint64_t features; struct ntb_pci_bar_info bar_info[NTB_MAX_BARS]; struct ntb_int_info int_info[MAX_MSIX_INTERRUPTS]; @@ -145,26 +154,31 @@ struct ntb_softc { uint8_t link_speed; }; -#define ntb_reg_read(SIZE, offset) \ - bus_space_read_ ## SIZE (ntb->bar_info[NTB_CONFIG_BAR].pci_bus_tag, \ - ntb->bar_info[NTB_CONFIG_BAR].pci_bus_handle, (offset)) +#define ntb_bar_read(SIZE, bar, offset) \ + bus_space_read_ ## SIZE (ntb->bar_info[(bar)].pci_bus_tag, \ + ntb->bar_info[(bar)].pci_bus_handle, (offset)) +#define ntb_bar_write(SIZE, bar, offset, val) \ + bus_space_write_ ## SIZE (ntb->bar_info[(bar)].pci_bus_tag, \ + ntb->bar_info[(bar)].pci_bus_handle, (offset), (val)) +#define ntb_reg_read(SIZE, offset) ntb_bar_read(SIZE, NTB_CONFIG_BAR, offset) #define ntb_reg_write(SIZE, offset, val) \ - bus_space_write_ ## SIZE (ntb->bar_info[NTB_CONFIG_BAR].pci_bus_tag, \ - ntb->bar_info[NTB_CONFIG_BAR].pci_bus_handle, (offset), (val)) + ntb_bar_write(SIZE, NTB_CONFIG_BAR, offset, val) +#define ntb_mw_read(SIZE, offset) ntb_bar_read(SIZE, NTB_B2B_BAR_2, offset) +#define ntb_mw_write(SIZE, offset, val) \ + ntb_bar_write(SIZE, NTB_B2B_BAR_2, offset, val) -#define ntb_read_1(offset) ntb_reg_read(1, (offset)) -#define ntb_read_2(offset) ntb_reg_read(2, (offset)) -#define ntb_read_4(offset) ntb_reg_read(4, (offset)) -#define ntb_read_8(offset) ntb_reg_read(8, (offset)) -#define ntb_write_1(offset, val) ntb_reg_write(1, (offset), (val)) -#define ntb_write_2(offset, val) ntb_reg_write(2, (offset), (val)) -#define ntb_write_4(offset, val) ntb_reg_write(4, (offset), (val)) -#define ntb_write_8(offset, val) ntb_reg_write(8, (offset), (val)) +typedef int (*bar_map_strategy)(struct ntb_softc *ntb, + struct ntb_pci_bar_info *bar); static int ntb_probe(device_t device); static int ntb_attach(device_t device); static int ntb_detach(device_t device); -static int ntb_map_pci_bar(struct ntb_softc *ntb); +static int ntb_map_pci_bars(struct ntb_softc *ntb); +static int map_pci_bar(struct ntb_softc *ntb, bar_map_strategy strategy, + struct ntb_pci_bar_info *bar); +static int map_mmr_bar(struct ntb_softc *ntb, struct ntb_pci_bar_info *bar); +static int map_memory_window_bar(struct ntb_softc *ntb, + struct ntb_pci_bar_info *bar); static void ntb_unmap_pci_bar(struct ntb_softc *ntb); static int ntb_setup_interrupts(struct ntb_softc *ntb); static void ntb_teardown_interrupts(struct ntb_softc *ntb); @@ -178,17 +192,21 @@ static struct ntb_hw_info *ntb_get_device_info(uint32_t device_id); static int ntb_initialize_hw(struct ntb_softc *ntb); static int ntb_setup_xeon(struct ntb_softc *ntb); static int ntb_setup_soc(struct ntb_softc *ntb); +static void configure_soc_secondary_side_bars(struct ntb_softc *ntb); +static void configure_xeon_secondary_side_bars(struct ntb_softc *ntb); static void ntb_handle_heartbeat(void *arg); static void ntb_handle_link_event(struct ntb_softc *ntb, int link_state); static void recover_soc_link(void *arg); static int ntb_check_link_status(struct ntb_softc *ntb); -static bool is_bar_for_data_transfer(int bar_num); +static void save_bar_parameters(struct ntb_pci_bar_info *bar); static struct ntb_hw_info pci_ids[] = { - { 0x3C0D8086, NTB_XEON, "Xeon E5/Core i7 Non-Transparent Bridge B2B" }, - { 0x0C4E8086, NTB_SOC, "Atom Processor S1200 NTB Primary B2B" }, - { 0x0E0D8086, NTB_XEON, "Xeon E5 V2 Non-Transparent Bridge B2B" }, - { 0x00000000, NTB_SOC, NULL } + { 0x3C0D8086, "Xeon E5/Core i7 Non-Transparent Bridge B2B", NTB_XEON, + NTB_REGS_THRU_MW }, + { 0x0C4E8086, "Atom Processor S1200 NTB Primary B2B", NTB_SOC, 0 }, + { 0x0E0D8086, "Xeon E5 V2 Non-Transparent Bridge B2B", NTB_XEON, + NTB_REGS_THRU_MW | NTB_BAR_SIZE_4K }, + { 0x00000000, NULL, NTB_SOC, 0 } }; /* @@ -245,12 +263,13 @@ ntb_attach(device_t device) ntb->device = device; ntb->type = p->type; + ntb->features = p->features; /* Heartbeat timer for NTB_SOC since there is no link interrupt */ callout_init(&ntb->heartbeat_timer, CALLOUT_MPSAFE); callout_init(&ntb->lr_timer, CALLOUT_MPSAFE); - DETACH_ON_ERROR(ntb_map_pci_bar(ntb)); + DETACH_ON_ERROR(ntb_map_pci_bars(ntb)); DETACH_ON_ERROR(ntb_initialize_hw(ntb)); DETACH_ON_ERROR(ntb_setup_interrupts(ntb)); @@ -273,59 +292,122 @@ ntb_detach(device_t device) } static int -ntb_map_pci_bar(struct ntb_softc *ntb) +ntb_map_pci_bars(struct ntb_softc *ntb) { - struct ntb_pci_bar_info *current_bar; - int rc, i; + int rc; ntb->bar_info[NTB_CONFIG_BAR].pci_resource_id = PCIR_BAR(0); + rc = map_pci_bar(ntb, map_mmr_bar, &ntb->bar_info[NTB_CONFIG_BAR]); + if (rc != 0) + return rc; + ntb->bar_info[NTB_B2B_BAR_1].pci_resource_id = PCIR_BAR(2); + rc = map_pci_bar(ntb, map_memory_window_bar, + &ntb->bar_info[NTB_B2B_BAR_1]); + if (rc != 0) + return rc; + ntb->bar_info[NTB_B2B_BAR_2].pci_resource_id = PCIR_BAR(4); + if (HAS_FEATURE(NTB_REGS_THRU_MW)) + rc = map_pci_bar(ntb, map_mmr_bar, + &ntb->bar_info[NTB_B2B_BAR_2]); + else + rc = map_pci_bar(ntb, map_memory_window_bar, + &ntb->bar_info[NTB_B2B_BAR_2]); + if (rc != 0) + return rc; - for (i = 0; i< NTB_MAX_BARS; i++) { - current_bar = &ntb->bar_info[i]; - current_bar->pci_resource = - bus_alloc_resource(ntb->device, - SYS_RES_MEMORY, - ¤t_bar->pci_resource_id, 0, ~0, 1, - RF_ACTIVE); + return (0); +} - if (current_bar->pci_resource == NULL) { - device_printf(ntb->device, - "unable to allocate pci resource\n"); - return (ENXIO); +static int +map_pci_bar(struct ntb_softc *ntb, bar_map_strategy strategy, + struct ntb_pci_bar_info *bar) +{ + int rc; + + rc = strategy(ntb, bar); + if (rc != 0) { + device_printf(ntb->device, + "unable to allocate pci resource\n"); + } else { + device_printf(ntb->device, + "Bar size = %lx, v %p, p %p\n", + bar->size, bar->vbase, + (void *)(bar->pbase)); + } + return (rc); +} + +static int +map_mmr_bar(struct ntb_softc *ntb, struct ntb_pci_bar_info *bar) +{ + + bar->pci_resource = bus_alloc_resource_any(ntb->device, SYS_RES_MEMORY, + &bar->pci_resource_id, RF_ACTIVE); + + if (bar->pci_resource == NULL) + return (ENXIO); + else { + save_bar_parameters(bar); + return (0); + } +} + +static int +map_memory_window_bar(struct ntb_softc *ntb, struct ntb_pci_bar_info *bar) +{ + int rc; + uint8_t bar_size_bits = 0; + + bar->pci_resource = bus_alloc_resource_any(ntb->device, + SYS_RES_MEMORY, &bar->pci_resource_id, RF_ACTIVE); + + if (bar->pci_resource == NULL) + return (ENXIO); + else { + save_bar_parameters(bar); + /* + * Ivytown NTB BAR sizes are misreported by the hardware due to + * a hardware issue. To work around this, query the size it + * should be configured to by the device and modify the resource + * to correspond to this new size. The BIOS on systems with this + * problem is required to provide enough address space to allow + * the driver to make this change safely. + * + * Ideally I could have just specified the size when I allocated + * the resource like: + * bus_alloc_resource(ntb->device, + * SYS_RES_MEMORY, &bar->pci_resource_id, 0ul, ~0ul, + * 1ul << bar_size_bits, RF_ACTIVE); + * but the PCI driver does not honor the size in this call, so + * we have to modify it after the fact. + */ + if (HAS_FEATURE(NTB_BAR_SIZE_4K)) { + if (bar->pci_resource_id == PCIR_BAR(2)) + bar_size_bits = pci_read_config(ntb->device, + XEON_PBAR23SZ_OFFSET, 1); + else + bar_size_bits = pci_read_config(ntb->device, + XEON_PBAR45SZ_OFFSET, 1); + rc = bus_adjust_resource(ntb->device, SYS_RES_MEMORY, + bar->pci_resource, bar->pbase, + bar->pbase + (1ul << bar_size_bits) - 1); + if (rc != 0 ) { + device_printf(ntb->device, + "unable to resize bar\n"); + return (rc); + } else + save_bar_parameters(bar); } - else { - current_bar->pci_bus_tag = - rman_get_bustag(current_bar->pci_resource); - current_bar->pci_bus_handle = - rman_get_bushandle(current_bar->pci_resource); - current_bar->pbase = - rman_get_start(current_bar->pci_resource); - current_bar->size = - rman_get_size(current_bar->pci_resource); - current_bar->vbase = - rman_get_virtual(current_bar->pci_resource); - if (is_bar_for_data_transfer(i)) { - /* - * Mark bar region as write combining to improve - * performance. - */ - rc = pmap_change_attr( - (vm_offset_t)current_bar->vbase, - current_bar->size, - VM_MEMATTR_WRITE_COMBINING); - if (rc != 0) { - device_printf(ntb->device, - "Couldn't mark bar as" - " WRITE_COMBINING\n"); - return (rc); - } - } - device_printf(ntb->device, - "Bar size = %lx, v %p, p %p\n", - current_bar->size, current_bar->vbase, - (void *)(current_bar->pbase)); + + /* Mark bar region as write combining to improve performance. */ + rc = pmap_change_attr((vm_offset_t)bar->vbase, bar->size, + VM_MEMATTR_WRITE_COMBINING); + if (rc != 0) { + device_printf(ntb->device, "unable to mark bar as" + " WRITE_COMBINING\n"); + return (rc); } } return (0); @@ -361,9 +443,9 @@ ntb_setup_interrupts(struct ntb_softc *ntb) * Interrupt. The rest will be unmasked as callbacks are registered. */ if (ntb->type == NTB_SOC) - ntb_write_8(ntb->reg_ofs.pdb_mask, ~0); + ntb_reg_write(8, ntb->reg_ofs.pdb_mask, ~0); else - ntb_write_2(ntb->reg_ofs.pdb_mask, + ntb_reg_write(2, ntb->reg_ofs.pdb_mask, ~(1 << ntb->limits.max_db_bits)); num_vectors = MIN(pci_msix_count(ntb->device), @@ -393,7 +475,8 @@ ntb_setup_interrupts(struct ntb_softc *ntb) int_arg = &ntb->db_cb[i]; } else { if (i == num_vectors - 1) { - interrupt_handler = handle_xeon_event_irq; + interrupt_handler = + handle_xeon_event_irq; int_arg = ntb; } else { interrupt_handler = @@ -413,8 +496,8 @@ ntb_setup_interrupts(struct ntb_softc *ntb) } else { ntb->int_info[0].rid = 0; - ntb->int_info[0].res = bus_alloc_resource_any(ntb->device, SYS_RES_IRQ, - &ntb->int_info[0].rid, RF_SHAREABLE|RF_ACTIVE); + ntb->int_info[0].res = bus_alloc_resource_any(ntb->device, + SYS_RES_IRQ, &ntb->int_info[0].rid, RF_SHAREABLE|RF_ACTIVE); interrupt_handler = ntb_handle_legacy_interrupt; if (ntb->int_info[0].res == NULL) { device_printf(ntb->device, @@ -463,7 +546,7 @@ handle_soc_irq(void *arg) struct ntb_db_cb *db_cb = arg; struct ntb_softc *ntb = db_cb->ntb; - ntb_write_8(ntb->reg_ofs.pdb, (uint64_t) 1 << db_cb->db_num); + ntb_reg_write(8, ntb->reg_ofs.pdb, (uint64_t) 1 << db_cb->db_num); if (db_cb->callback != NULL) db_cb->callback(db_cb->data, db_cb->db_num); @@ -481,7 +564,7 @@ handle_xeon_irq(void *arg) * vectors, with the 4th having a single bit for link * interrupts. */ - ntb_write_2(ntb->reg_ofs.pdb, + ntb_reg_write(2, ntb->reg_ofs.pdb, ((1 << ntb->bits_per_vector) - 1) << (db_cb->db_num * ntb->bits_per_vector)); @@ -501,7 +584,7 @@ handle_xeon_event_irq(void *arg) device_printf(ntb->device, "Error determining link status\n"); /* bit 15 is always the link bit */ - ntb_write_2(ntb->reg_ofs.pdb, 1 << ntb->limits.max_db_bits); + ntb_reg_write(2, ntb->reg_ofs.pdb, 1 << ntb->limits.max_db_bits); } static void @@ -513,7 +596,7 @@ ntb_handle_legacy_interrupt(void *arg) uint16_t pdb16; if (ntb->type == NTB_SOC) { - pdb64 = ntb_read_8(ntb->reg_ofs.pdb); + pdb64 = ntb_reg_read(8, ntb->reg_ofs.pdb); while (pdb64) { i = ffs(pdb64); @@ -521,7 +604,7 @@ ntb_handle_legacy_interrupt(void *arg) handle_soc_irq(&ntb->db_cb[i]); } } else { - pdb16 = ntb_read_2(ntb->reg_ofs.pdb); + pdb16 = ntb_reg_read(2, ntb->reg_ofs.pdb); if ((pdb16 & XEON_DB_HW_LINK) != 0) { handle_xeon_event_irq(ntb); @@ -634,10 +717,15 @@ ntb_setup_xeon(struct ntb_softc *ntb) ntb->limits.msix_cnt = XEON_MSIX_CNT; ntb->bits_per_vector = XEON_DB_BITS_PER_VEC; + configure_xeon_secondary_side_bars(ntb); /* Enable Bus Master and Memory Space on the secondary side */ - ntb_write_2(ntb->reg_ofs.spci_cmd, + ntb_reg_write(2, ntb->reg_ofs.spci_cmd, PCIM_CMD_MEMEN | PCIM_CMD_BUSMASTEREN); + /* Enable link training */ + ntb_reg_write(4, ntb->reg_ofs.lnk_cntl, + NTB_CNTL_BAR23_SNOOP | NTB_CNTL_BAR45_SNOOP); + return (0); } @@ -698,49 +786,63 @@ ntb_setup_soc(struct ntb_softc *ntb) */ pci_write_config(ntb->device, 0xFC, 0x4, 4); - /* - * Some BIOSes aren't filling out the XLAT offsets. - * Check and correct the issue. - */ - if (ntb->dev_type == NTB_DEV_USD) { - if (ntb_read_8(SOC_PBAR2XLAT_OFFSET) == 0) - ntb_write_8(SOC_PBAR2XLAT_OFFSET, - SOC_PBAR2XLAT_USD_ADDR); - - if (ntb_read_8(SOC_PBAR4XLAT_OFFSET) == 0) - ntb_write_8(SOC_PBAR4XLAT_OFFSET, - SOC_PBAR4XLAT_USD_ADDR); - - if (ntb_read_8(SOC_MBAR23_OFFSET) == 0xC) - ntb_write_8(SOC_MBAR23_OFFSET, SOC_MBAR23_USD_ADDR); - - if (ntb_read_8(SOC_MBAR45_OFFSET) == 0xC) - ntb_write_8(SOC_MBAR45_OFFSET, SOC_MBAR45_USD_ADDR); - } else { - if (ntb_read_8(SOC_PBAR2XLAT_OFFSET) == 0) - ntb_write_8(SOC_PBAR2XLAT_OFFSET, - SOC_PBAR2XLAT_DSD_ADDR); - - if (ntb_read_8(SOC_PBAR4XLAT_OFFSET) == 0) - ntb_write_8(SOC_PBAR4XLAT_OFFSET, - SOC_PBAR4XLAT_DSD_ADDR); - - if (ntb_read_8(SOC_MBAR23_OFFSET) == 0xC) - ntb_write_8(SOC_MBAR23_OFFSET, SOC_MBAR23_DSD_ADDR); - - if (ntb_read_8(SOC_MBAR45_OFFSET) == 0xC) - ntb_write_8(SOC_MBAR45_OFFSET, SOC_MBAR45_DSD_ADDR); - } + configure_soc_secondary_side_bars(ntb); /* Enable Bus Master and Memory Space on the secondary side */ - ntb_write_2(ntb->reg_ofs.spci_cmd, + ntb_reg_write(2, ntb->reg_ofs.spci_cmd, PCIM_CMD_MEMEN | PCIM_CMD_BUSMASTEREN); callout_reset(&ntb->heartbeat_timer, 0, ntb_handle_heartbeat, ntb); return (0); } -/* SOC doesn't have link status interrupt, poll on that platform */ +static void +configure_soc_secondary_side_bars(struct ntb_softc *ntb) +{ + + if (ntb->dev_type == NTB_DEV_USD) { + ntb_reg_write(8, SOC_PBAR2XLAT_OFFSET, PBAR2XLAT_USD_ADDR); + ntb_reg_write(8, SOC_PBAR4XLAT_OFFSET, PBAR4XLAT_USD_ADDR); + ntb_reg_write(8, SOC_MBAR23_OFFSET, MBAR23_USD_ADDR); + ntb_reg_write(8, SOC_MBAR45_OFFSET, MBAR45_USD_ADDR); + } else { + ntb_reg_write(8, SOC_PBAR2XLAT_OFFSET, PBAR2XLAT_DSD_ADDR); + ntb_reg_write(8, SOC_PBAR4XLAT_OFFSET, PBAR4XLAT_DSD_ADDR); + ntb_reg_write(8, SOC_MBAR23_OFFSET, MBAR23_DSD_ADDR); + ntb_reg_write(8, SOC_MBAR45_OFFSET, MBAR45_DSD_ADDR); + } +} + +static void +configure_xeon_secondary_side_bars(struct ntb_softc *ntb) +{ + + if (ntb->dev_type == NTB_DEV_USD) { + ntb_reg_write(8, XEON_PBAR2XLAT_OFFSET, PBAR2XLAT_USD_ADDR); + if (HAS_FEATURE(NTB_REGS_THRU_MW)) + ntb_reg_write(8, XEON_PBAR4XLAT_OFFSET, + MBAR01_DSD_ADDR); + else + ntb_reg_write(8, XEON_PBAR4XLAT_OFFSET, + PBAR4XLAT_USD_ADDR); + ntb_reg_write(8, XEON_SBAR0BASE_OFFSET, MBAR01_USD_ADDR); + ntb_reg_write(8, XEON_SBAR2BASE_OFFSET, MBAR23_USD_ADDR); + ntb_reg_write(8, XEON_SBAR4BASE_OFFSET, MBAR45_USD_ADDR); + } else { + ntb_reg_write(8, XEON_PBAR2XLAT_OFFSET, PBAR2XLAT_DSD_ADDR); + if (HAS_FEATURE(NTB_REGS_THRU_MW)) + ntb_reg_write(8, XEON_PBAR4XLAT_OFFSET, + MBAR01_USD_ADDR); + else + ntb_reg_write(8, XEON_PBAR4XLAT_OFFSET, + PBAR4XLAT_DSD_ADDR); + ntb_reg_write(8, XEON_SBAR0BASE_OFFSET, MBAR01_DSD_ADDR); + ntb_reg_write(8, XEON_SBAR2BASE_OFFSET, MBAR23_DSD_ADDR); + ntb_reg_write(8, XEON_SBAR4BASE_OFFSET, MBAR45_DSD_ADDR); + } +} + +/* SOC does not have link status interrupt, poll on that platform */ static void ntb_handle_heartbeat(void *arg) { @@ -753,7 +855,7 @@ ntb_handle_heartbeat(void *arg) "Error determining link status\n"); /* Check to see if a link error is the cause of the link down */ if (ntb->link_status == NTB_LINK_DOWN) { - status32 = ntb_read_4(SOC_LTSSMSTATEJMP_OFFSET); + status32 = ntb_reg_read(4, SOC_LTSSMSTATEJMP_OFFSET); if ((status32 & SOC_LTSSMSTATEJMP_FORCEDETECT) != 0) { callout_reset(&ntb->lr_timer, 0, recover_soc_link, ntb); @@ -771,37 +873,37 @@ soc_perform_link_restart(struct ntb_softc *ntb) uint32_t status; /* Driver resets the NTB ModPhy lanes - magic! */ - ntb_write_1(SOC_MODPHY_PCSREG6, 0xe0); - ntb_write_1(SOC_MODPHY_PCSREG4, 0x40); - ntb_write_1(SOC_MODPHY_PCSREG4, 0x60); - ntb_write_1(SOC_MODPHY_PCSREG6, 0x60); + ntb_reg_write(1, SOC_MODPHY_PCSREG6, 0xe0); + ntb_reg_write(1, SOC_MODPHY_PCSREG4, 0x40); + ntb_reg_write(1, SOC_MODPHY_PCSREG4, 0x60); + ntb_reg_write(1, SOC_MODPHY_PCSREG6, 0x60); /* Driver waits 100ms to allow the NTB ModPhy to settle */ pause("ModPhy", hz / 10); /* Clear AER Errors, write to clear */ - status = ntb_read_4(SOC_ERRCORSTS_OFFSET); + status = ntb_reg_read(4, SOC_ERRCORSTS_OFFSET); status &= PCIM_AER_COR_REPLAY_ROLLOVER; - ntb_write_4(SOC_ERRCORSTS_OFFSET, status); + ntb_reg_write(4, SOC_ERRCORSTS_OFFSET, status); /* Clear unexpected electrical idle event in LTSSM, write to clear */ - status = ntb_read_4(SOC_LTSSMERRSTS0_OFFSET); + status = ntb_reg_read(4, SOC_LTSSMERRSTS0_OFFSET); status |= SOC_LTSSMERRSTS0_UNEXPECTEDEI; - ntb_write_4(SOC_LTSSMERRSTS0_OFFSET, status); + ntb_reg_write(4, SOC_LTSSMERRSTS0_OFFSET, status); /* Clear DeSkew Buffer error, write to clear */ - status = ntb_read_4(SOC_DESKEWSTS_OFFSET); + status = ntb_reg_read(4, SOC_DESKEWSTS_OFFSET); status |= SOC_DESKEWSTS_DBERR; - ntb_write_4(SOC_DESKEWSTS_OFFSET, status); + ntb_reg_write(4, SOC_DESKEWSTS_OFFSET, status); - status = ntb_read_4(SOC_IBSTERRRCRVSTS0_OFFSET); + status = ntb_reg_read(4, SOC_IBSTERRRCRVSTS0_OFFSET); status &= SOC_IBIST_ERR_OFLOW; - ntb_write_4(SOC_IBSTERRRCRVSTS0_OFFSET, status); + ntb_reg_write(4, SOC_IBSTERRRCRVSTS0_OFFSET, status); /* Releases the NTB state machine to allow the link to retrain */ - status = ntb_read_4(SOC_LTSSMSTATEJMP_OFFSET); + status = ntb_reg_read(4, SOC_LTSSMSTATEJMP_OFFSET); status &= ~SOC_LTSSMSTATEJMP_FORCEDETECT; - ntb_write_4(SOC_LTSSMSTATEJMP_OFFSET, status); + ntb_reg_write(4, SOC_LTSSMSTATEJMP_OFFSET, status); } static void @@ -819,7 +921,7 @@ ntb_handle_link_event(struct ntb_softc *ntb, int link_state) event = NTB_EVENT_HW_LINK_UP; if (ntb->type == NTB_SOC) - status = ntb_read_2(ntb->reg_ofs.lnk_stat); + status = ntb_reg_read(2, ntb->reg_ofs.lnk_stat); else status = pci_read_config(ntb->device, XEON_LINK_STATUS_OFFSET, 2); @@ -833,7 +935,7 @@ ntb_handle_link_event(struct ntb_softc *ntb, int link_state) device_printf(ntb->device, "Link Down\n"); ntb->link_status = NTB_LINK_DOWN; event = NTB_EVENT_HW_LINK_DOWN; - /* Don't modify link width/speed, we need it in link recovery */ + /* Do not modify link width/speed, we need it in link recovery */ } /* notify the upper layer if we have an event change */ @@ -852,15 +954,15 @@ recover_soc_link(void *arg) soc_perform_link_restart(ntb); pause("Link", SOC_LINK_RECOVERY_TIME * hz / 1000); - status32 = ntb_read_4(SOC_LTSSMSTATEJMP_OFFSET); + status32 = ntb_reg_read(4, SOC_LTSSMSTATEJMP_OFFSET); if ((status32 & SOC_LTSSMSTATEJMP_FORCEDETECT) != 0) goto retry; - status32 = ntb_read_4(SOC_IBSTERRRCRVSTS0_OFFSET); + status32 = ntb_reg_read(4, SOC_IBSTERRRCRVSTS0_OFFSET); if ((status32 & SOC_IBIST_ERR_OFLOW) != 0) goto retry; - status16 = ntb_read_2(ntb->reg_ofs.lnk_stat); + status16 = ntb_reg_read(2, ntb->reg_ofs.lnk_stat); width = (status16 & NTB_LINK_WIDTH_MASK) >> 4; speed = (status16 & NTB_LINK_SPEED_MASK); if (ntb->link_width != width || ntb->link_speed != speed) @@ -883,7 +985,7 @@ ntb_check_link_status(struct ntb_softc *ntb) uint16_t status; if (ntb->type == NTB_SOC) { - ntb_cntl = ntb_read_4(ntb->reg_ofs.lnk_cntl); + ntb_cntl = ntb_reg_read(4, ntb->reg_ofs.lnk_cntl); if ((ntb_cntl & SOC_CNTL_LINK_DOWN) != 0) link_state = NTB_LINK_DOWN; else @@ -965,9 +1067,9 @@ ntb_register_db_callback(struct ntb_softc *ntb, unsigned int idx, void *data, ntb->db_cb[idx].data = data; /* unmask interrupt */ - mask = ntb_read_2(ntb->reg_ofs.pdb_mask); + mask = ntb_reg_read(2, ntb->reg_ofs.pdb_mask); mask &= ~(1 << (idx * ntb->bits_per_vector)); - ntb_write_2(ntb->reg_ofs.pdb_mask, mask); + ntb_reg_write(2, ntb->reg_ofs.pdb_mask, mask); return (0); } @@ -988,9 +1090,9 @@ ntb_unregister_db_callback(struct ntb_softc *ntb, unsigned int idx) if (idx >= ntb->allocated_interrupts || !ntb->db_cb[idx].callback) return; - mask = ntb_read_2(ntb->reg_ofs.pdb_mask); + mask = ntb_reg_read(2, ntb->reg_ofs.pdb_mask); mask |= 1 << (idx * ntb->bits_per_vector); - ntb_write_2(ntb->reg_ofs.pdb_mask, mask); + ntb_reg_write(2, ntb->reg_ofs.pdb_mask, mask); ntb->db_cb[idx].callback = NULL; } @@ -1091,7 +1193,7 @@ ntb_write_local_spad(struct ntb_softc *ntb, unsigned int idx, uint32_t val) if (idx >= ntb->limits.max_spads) return (EINVAL); - ntb_write_4(ntb->reg_ofs.spad_local + idx * 4, val); + ntb_reg_write(4, ntb->reg_ofs.spad_local + idx * 4, val); return (0); } @@ -1114,7 +1216,7 @@ ntb_read_local_spad(struct ntb_softc *ntb, unsigned int idx, uint32_t *val) if (idx >= ntb->limits.max_spads) return (EINVAL); - *val = ntb_read_4(ntb->reg_ofs.spad_local + idx * 4); + *val = ntb_reg_read(4, ntb->reg_ofs.spad_local + idx * 4); return (0); } @@ -1137,7 +1239,10 @@ ntb_write_remote_spad(struct ntb_softc *ntb, unsigned int idx, uint32_t val) if (idx >= ntb->limits.max_spads) return (EINVAL); - ntb_write_4(ntb->reg_ofs.spad_remote + idx * 4, val); + if (HAS_FEATURE(NTB_REGS_THRU_MW)) + ntb_mw_write(4, XEON_SHADOW_SPAD_OFFSET + idx * 4, val); + else + ntb_reg_write(4, ntb->reg_ofs.spad_remote + idx * 4, val); return (0); } @@ -1160,7 +1265,10 @@ ntb_read_remote_spad(struct ntb_softc *ntb, unsigned int idx, uint32_t *val) if (idx >= ntb->limits.max_spads) return (EINVAL); - *val = ntb_read_4(ntb->reg_ofs.spad_remote + idx * 4); + if (HAS_FEATURE(NTB_REGS_THRU_MW)) + *val = ntb_mw_read(4, XEON_SHADOW_SPAD_OFFSET + idx * 4); + else + *val = ntb_reg_read(4, ntb->reg_ofs.spad_remote + idx * 4); return (0); } @@ -1233,10 +1341,10 @@ ntb_set_mw_addr(struct ntb_softc *ntb, unsigned int mw, uint64_t addr) switch (NTB_MW_TO_BAR(mw)) { case NTB_B2B_BAR_1: - ntb_write_8(ntb->reg_ofs.sbar2_xlat, addr); + ntb_reg_write(8, ntb->reg_ofs.sbar2_xlat, addr); break; case NTB_B2B_BAR_2: - ntb_write_8(ntb->reg_ofs.sbar4_xlat, addr); + ntb_reg_write(8, ntb->reg_ofs.sbar4_xlat, addr); break; } } @@ -1256,11 +1364,16 @@ ntb_ring_sdb(struct ntb_softc *ntb, unsigned int db) { if (ntb->type == NTB_SOC) - ntb_write_8(ntb->reg_ofs.sdb, (uint64_t) 1 << db); + ntb_reg_write(8, ntb->reg_ofs.sdb, (uint64_t) 1 << db); else - ntb_write_2(ntb->reg_ofs.sdb, - ((1 << ntb->bits_per_vector) - 1) << - (db * ntb->bits_per_vector)); + if (HAS_FEATURE(NTB_REGS_THRU_MW)) + ntb_mw_write(2, XEON_SHADOW_PDOORBELL_OFFSET, + ((1 << ntb->bits_per_vector) - 1) << + (db * ntb->bits_per_vector)); + else + ntb_reg_write(2, ntb->reg_ofs.sdb, + ((1 << ntb->bits_per_vector) - 1) << + (db * ntb->bits_per_vector)); } /** @@ -1278,11 +1391,24 @@ ntb_query_link_status(struct ntb_softc *ntb) return (ntb->link_status == NTB_LINK_UP); } -static bool -is_bar_for_data_transfer(int bar_num) +static void +save_bar_parameters(struct ntb_pci_bar_info *bar) { - if ((bar_num > NTB_CONFIG_BAR) && (bar_num < NTB_MAX_BARS)) - return true; - else - return false; + bar->pci_bus_tag = + rman_get_bustag(bar->pci_resource); + bar->pci_bus_handle = + rman_get_bushandle(bar->pci_resource); + bar->pbase = + rman_get_start(bar->pci_resource); + bar->size = + rman_get_size(bar->pci_resource); + bar->vbase = + rman_get_virtual(bar->pci_resource); + +} + +device_t ntb_get_device(struct ntb_softc *ntb) +{ + + return (ntb->device); } diff --git a/sys/dev/ntb/ntb_hw/ntb_hw.h b/sys/dev/ntb/ntb_hw/ntb_hw.h index 4f44031a024..c6c1274d17b 100644 --- a/sys/dev/ntb/ntb_hw/ntb_hw.h +++ b/sys/dev/ntb/ntb_hw/ntb_hw.h @@ -69,5 +69,6 @@ u_long ntb_get_mw_size(struct ntb_softc *ntb, unsigned int mw); void ntb_set_mw_addr(struct ntb_softc *ntb, unsigned int mw, uint64_t addr); void ntb_ring_sdb(struct ntb_softc *ntb, unsigned int db); bool ntb_query_link_status(struct ntb_softc *ntb); +device_t ntb_get_device(struct ntb_softc *ntb); #endif /* _NTB_HW_H_ */ diff --git a/sys/dev/ntb/ntb_hw/ntb_regs.h b/sys/dev/ntb/ntb_hw/ntb_regs.h index 34ad779632f..bd55a59184b 100644 --- a/sys/dev/ntb/ntb_hw/ntb_regs.h +++ b/sys/dev/ntb/ntb_hw/ntb_regs.h @@ -39,14 +39,14 @@ #define XEON_MAX_SPADS 16 #define XEON_MAX_COMPAT_SPADS 8 /* Reserve the uppermost bit for link interrupt */ -#define XEON_MAX_DB_BITS 15 +#define XEON_MAX_DB_BITS 15 #define XEON_DB_BITS_PER_VEC 5 #define XEON_DB_HW_LINK 0x8000 #define XEON_PCICMD_OFFSET 0x0504 #define XEON_DEVCTRL_OFFSET 0x0598 -#define XEON_LINK_STATUS_OFFSET 0x01A2 +#define XEON_LINK_STATUS_OFFSET 0x01a2 #define XEON_PBAR2LMT_OFFSET 0x0000 #define XEON_PBAR4LMT_OFFSET 0x0008 @@ -60,13 +60,13 @@ #define XEON_SBAR2BASE_OFFSET 0x0048 #define XEON_SBAR4BASE_OFFSET 0x0050 #define XEON_NTBCNTL_OFFSET 0x0058 -#define XEON_SBDF_OFFSET 0x005C +#define XEON_SBDF_OFFSET 0x005c #define XEON_PDOORBELL_OFFSET 0x0060 #define XEON_PDBMSK_OFFSET 0x0062 #define XEON_SDOORBELL_OFFSET 0x0064 #define XEON_SDBMSK_OFFSET 0x0066 #define XEON_USMEMMISS 0x0070 -#define XEON_SPAD_OFFSET 0x0080 +#define XEON_SPAD_OFFSET 0x0080 #define XEON_SPADSEMA4_OFFSET 0x00c0 #define XEON_WCCNTRL_OFFSET 0x00e0 #define XEON_B2B_SPAD_OFFSET 0x0100 @@ -105,7 +105,7 @@ #define SOC_MODPHY_PCSREG4 0x1c004 #define SOC_MODPHY_PCSREG6 0x1c006 -#define SOC_IP_BASE 0xC000 +#define SOC_IP_BASE 0xc000 #define SOC_DESKEWSTS_OFFSET (SOC_IP_BASE + 0x3024) #define SOC_LTSSMERRSTS0_OFFSET (SOC_IP_BASE + 0x3180) #define SOC_LTSSMSTATEJMP_OFFSET (SOC_IP_BASE + 0x3040) @@ -114,13 +114,15 @@ #define SOC_DESKEWSTS_DBERR (1 << 15) #define SOC_LTSSMERRSTS0_UNEXPECTEDEI (1 << 20) #define SOC_LTSSMSTATEJMP_FORCEDETECT (1 << 2) -#define SOC_IBIST_ERR_OFLOW 0x7FFF7FFF +#define SOC_IBIST_ERR_OFLOW 0x7fff7fff #define NTB_CNTL_BAR23_SNOOP (1 << 2) #define NTB_CNTL_BAR45_SNOOP (1 << 6) #define SOC_CNTL_LINK_DOWN (1 << 16) -#define NTB_PPD_OFFSET 0x00D4 +#define XEON_PBAR23SZ_OFFSET 0x00d0 +#define XEON_PBAR45SZ_OFFSET 0x00d1 +#define NTB_PPD_OFFSET 0x00d4 #define XEON_PPD_CONN_TYPE 0x0003 #define XEON_PPD_DEV_TYPE 0x0010 #define SOC_PPD_INIT_LINK 0x0008 @@ -134,13 +136,19 @@ #define NTB_DEV_DSD 1 #define NTB_DEV_USD 0 -#define SOC_PBAR2XLAT_USD_ADDR 0x0000004000000000 -#define SOC_PBAR4XLAT_USD_ADDR 0x0000008000000000 -#define SOC_MBAR23_USD_ADDR 0x000000410000000C -#define SOC_MBAR45_USD_ADDR 0x000000810000000C -#define SOC_PBAR2XLAT_DSD_ADDR 0x0000004100000000 -#define SOC_PBAR4XLAT_DSD_ADDR 0x0000008100000000 -#define SOC_MBAR23_DSD_ADDR 0x000000400000000C -#define SOC_MBAR45_DSD_ADDR 0x000000800000000C +#define PBAR2XLAT_USD_ADDR 0x0000004000000000 +#define PBAR4XLAT_USD_ADDR 0x0000008000000000 +#define MBAR01_USD_ADDR 0x000000210000000c +#define MBAR23_USD_ADDR 0x000000410000000c +#define MBAR45_USD_ADDR 0x000000810000000c +#define PBAR2XLAT_DSD_ADDR 0x0000004100000000 +#define PBAR4XLAT_DSD_ADDR 0x0000008100000000 +#define MBAR01_DSD_ADDR 0x000000200000000c +#define MBAR23_DSD_ADDR 0x000000400000000c +#define MBAR45_DSD_ADDR 0x000000800000000c + +/* XEON Shadowed MMIO Space */ +#define XEON_SHADOW_PDOORBELL_OFFSET 0x60 +#define XEON_SHADOW_SPAD_OFFSET 0x80 #endif /* _NTB_REGS_H_ */ diff --git a/sys/dev/nvme/nvme.c b/sys/dev/nvme/nvme.c index eacd0cc4997..ec8d592349c 100644 --- a/sys/dev/nvme/nvme.c +++ b/sys/dev/nvme/nvme.c @@ -157,30 +157,14 @@ nvme_shutdown(void) { device_t *devlist; struct nvme_controller *ctrlr; - union cc_register cc; - union csts_register csts; int dev, devcount; if (devclass_get_devices(nvme_devclass, &devlist, &devcount)) return; for (dev = 0; dev < devcount; dev++) { - /* - * Only notify controller of shutdown when a real shutdown is - * in process, not when a module unload occurs. It seems at - * least some controllers (Chatham at least) don't let you - * re-enable the controller after shutdown notification has - * been received. - */ ctrlr = DEVICE2SOFTC(devlist[dev]); - cc.raw = nvme_mmio_read_4(ctrlr, cc); - cc.bits.shn = NVME_SHN_NORMAL; - nvme_mmio_write_4(ctrlr, cc, cc.raw); - csts.raw = nvme_mmio_read_4(ctrlr, csts); - while (csts.bits.shst != NVME_SHST_COMPLETE) { - DELAY(5); - csts.raw = nvme_mmio_read_4(ctrlr, csts); - } + nvme_ctrlr_shutdown(ctrlr); } free(devlist, M_TEMP); @@ -294,6 +278,15 @@ nvme_notify_consumer(struct nvme_consumer *cons) else ctrlr_cookie = NULL; ctrlr->cons_cookie[cons->id] = ctrlr_cookie; + if (ctrlr->is_failed) { + if (cons->fail_fn != NULL) + (*cons->fail_fn)(ctrlr_cookie); + /* + * Do not notify consumers about the namespaces of a + * failed controller. + */ + continue; + } for (ns_idx = 0; ns_idx < ctrlr->cdata.nn; ns_idx++) { ns = &ctrlr->ns[ns_idx]; if (cons->ns_fn != NULL) diff --git a/sys/dev/nvme/nvme.h b/sys/dev/nvme/nvme.h index 9df75da046b..f904933a20a 100644 --- a/sys/dev/nvme/nvme.h +++ b/sys/dev/nvme/nvme.h @@ -170,27 +170,30 @@ struct nvme_registers union cap_lo_register cap_lo; union cap_hi_register cap_hi; - uint32_t vs; /* version */ - uint32_t intms; /* interrupt mask set */ - uint32_t intmc; /* interrupt mask clear */ + uint32_t vs; /* version */ + uint32_t intms; /* interrupt mask set */ + uint32_t intmc; /* interrupt mask clear */ /** controller configuration */ union cc_register cc; - uint32_t reserved1; - uint32_t csts; /* controller status */ - uint32_t reserved2; + uint32_t reserved1; + + /** controller status */ + union csts_register csts; + + uint32_t reserved2; /** admin queue attributes */ union aqa_register aqa; - uint64_t asq; /* admin submission queue base addr */ - uint64_t acq; /* admin completion queue base addr */ - uint32_t reserved3[0x3f2]; + uint64_t asq; /* admin submission queue base addr */ + uint64_t acq; /* admin completion queue base addr */ + uint32_t reserved3[0x3f2]; struct { - uint32_t sq_tdbl; /* submission queue tail doorbell */ - uint32_t cq_hdbl; /* completion queue head doorbell */ + uint32_t sq_tdbl; /* submission queue tail doorbell */ + uint32_t cq_hdbl; /* completion queue head doorbell */ } doorbell[1] __packed; } __packed; diff --git a/sys/dev/nvme/nvme_ctrlr.c b/sys/dev/nvme/nvme_ctrlr.c index 1338f153225..48457820540 100644 --- a/sys/dev/nvme/nvme_ctrlr.c +++ b/sys/dev/nvme/nvme_ctrlr.c @@ -1117,6 +1117,21 @@ nvme_ctrlr_destruct(struct nvme_controller *ctrlr, device_t dev) { int i; + /* + * Notify the controller of a shutdown, even though this is due to + * a driver unload, not a system shutdown (this path is not invoked + * during shutdown). This ensures the controller receives a + * shutdown notification in case the system is shutdown before + * reloading the driver. + * + * Chatham does not let you re-enable the controller after shutdown + * notification has been received, so do not send it in this case. + * This is OK because Chatham does not depend on the shutdown + * notification anyways. + */ + if (pci_get_devid(ctrlr->dev) != CHATHAM_PCI_ID) + nvme_ctrlr_shutdown(ctrlr); + nvme_ctrlr_disable(ctrlr); taskqueue_free(ctrlr->taskqueue); @@ -1162,6 +1177,26 @@ nvme_ctrlr_destruct(struct nvme_controller *ctrlr, device_t dev) pci_release_msi(dev); } +void +nvme_ctrlr_shutdown(struct nvme_controller *ctrlr) +{ + union cc_register cc; + union csts_register csts; + int ticks = 0; + + cc.raw = nvme_mmio_read_4(ctrlr, cc); + cc.bits.shn = NVME_SHN_NORMAL; + nvme_mmio_write_4(ctrlr, cc, cc.raw); + csts.raw = nvme_mmio_read_4(ctrlr, csts); + while ((csts.bits.shst != NVME_SHST_COMPLETE) && (ticks++ < 5*hz)) { + pause("nvme shn", 1); + csts.raw = nvme_mmio_read_4(ctrlr, csts); + } + if (csts.bits.shst != NVME_SHST_COMPLETE) + nvme_printf(ctrlr, "did not complete shutdown within 5 seconds " + "of notification\n"); +} + void nvme_ctrlr_submit_admin_request(struct nvme_controller *ctrlr, struct nvme_request *req) diff --git a/sys/dev/nvme/nvme_ns.c b/sys/dev/nvme/nvme_ns.c index fb31852bec3..5fa3ba0b1cc 100644 --- a/sys/dev/nvme/nvme_ns.c +++ b/sys/dev/nvme/nvme_ns.c @@ -133,11 +133,7 @@ nvme_ns_strategy(struct bio *bp) static struct cdevsw nvme_ns_cdevsw = { .d_version = D_VERSION, -#ifdef NVME_UNMAPPED_BIO_SUPPORT - .d_flags = D_DISK | D_UNMAPPED_IO, -#else .d_flags = D_DISK, -#endif .d_read = physread, .d_write = physwrite, .d_open = nvme_ns_open, @@ -348,6 +344,9 @@ nvme_ns_construct(struct nvme_namespace *ns, uint16_t id, NULL, UID_ROOT, GID_WHEEL, 0600, "nvme%dns%d", device_get_unit(ctrlr->dev), ns->id); #endif +#ifdef NVME_UNMAPPED_BIO_SUPPORT + ns->cdev->si_flags |= SI_UNMAPPED; +#endif if (ns->cdev != NULL) ns->cdev->si_drv1 = ns; diff --git a/sys/dev/nvme/nvme_private.h b/sys/dev/nvme/nvme_private.h index 10643f22178..f0f44537976 100644 --- a/sys/dev/nvme/nvme_private.h +++ b/sys/dev/nvme/nvme_private.h @@ -433,6 +433,7 @@ void nvme_completion_poll_cb(void *arg, const struct nvme_completion *cpl); int nvme_ctrlr_construct(struct nvme_controller *ctrlr, device_t dev); void nvme_ctrlr_destruct(struct nvme_controller *ctrlr, device_t dev); +void nvme_ctrlr_shutdown(struct nvme_controller *ctrlr); int nvme_ctrlr_hw_reset(struct nvme_controller *ctrlr); void nvme_ctrlr_reset(struct nvme_controller *ctrlr); /* ctrlr defined as void * to allow use with config_intrhook. */ diff --git a/sys/dev/ofw/ofw_bus_subr.c b/sys/dev/ofw/ofw_bus_subr.c index a1bd8b329e3..9b9dee4d162 100644 --- a/sys/dev/ofw/ofw_bus_subr.c +++ b/sys/dev/ofw/ofw_bus_subr.c @@ -300,7 +300,7 @@ ofw_bus_search_intrmap(void *intr, int intrsz, void *regs, int physsz, i = imapsz; while (i > 0) { bcopy(mptr + physsz + intrsz, &parent, sizeof(parent)); - if (OF_searchprop(parent, "#interrupt-cells", + if (OF_searchprop(OF_xref_phandle(parent), "#interrupt-cells", &pintrsz, sizeof(pintrsz)) == -1) pintrsz = 1; /* default */ pintrsz *= sizeof(pcell_t); diff --git a/sys/dev/ofw/ofw_console.c b/sys/dev/ofw/ofw_console.c index e71d5c19eb8..01f86bf7fe8 100644 --- a/sys/dev/ofw/ofw_console.c +++ b/sys/dev/ofw/ofw_console.c @@ -88,17 +88,19 @@ cn_drvinit(void *unused) if (ofw_consdev.cn_pri != CN_DEAD && ofw_consdev.cn_name[0] != '\0') { - if ((options = OF_finddevice("/options")) == -1 || - OF_getprop(options, "output-device", output, - sizeof(output)) == -1) - return; + tp = tty_alloc(&ofw_ttydevsw, NULL); + tty_makedev(tp, NULL, "%s", "ofwcons"); + /* * XXX: This is a hack and it may result in two /dev/ttya * XXX: devices on platforms where the sab driver works. */ - tp = tty_alloc(&ofw_ttydevsw, NULL); - tty_makedev(tp, NULL, "%s", output); - tty_makealias(tp, "ofwcons"); + if ((options = OF_finddevice("/options")) == -1 || + OF_getprop(options, "output-device", output, + sizeof(output)) == -1) + return; + if (strlen(output) > 0) + tty_makealias(tp, output); } } diff --git a/sys/dev/ofw/openfirm.c b/sys/dev/ofw/openfirm.c index d63edcebff9..0760854079c 100644 --- a/sys/dev/ofw/openfirm.c +++ b/sys/dev/ofw/openfirm.c @@ -386,6 +386,48 @@ OF_package_to_path(phandle_t package, char *buf, size_t len) return (OFW_PACKAGE_TO_PATH(ofw_obj, package, buf, len)); } +/* Look up effective phandle (see FDT/PAPR spec) */ +static phandle_t +OF_child_xref_phandle(phandle_t parent, phandle_t xref) +{ + phandle_t child, rxref; + + /* + * Recursively descend from parent, looking for a node with a property + * named either "phandle", "ibm,phandle", or "linux,phandle" that + * matches the xref we are looking for. + */ + + for (child = OF_child(parent); child != 0; child = OF_peer(child)) { + rxref = OF_child_xref_phandle(child, xref); + if (rxref != -1) + return (rxref); + + if (OF_getprop(child, "phandle", &rxref, sizeof(rxref)) == -1 && + OF_getprop(child, "ibm,phandle", &rxref, + sizeof(rxref)) == -1 && OF_getprop(child, + "linux,phandle", &rxref, sizeof(rxref)) == -1) + continue; + + if (rxref == xref) + return (child); + } + + return (-1); +} + +phandle_t +OF_xref_phandle(phandle_t xref) +{ + phandle_t node; + + node = OF_child_xref_phandle(OF_peer(0), xref); + if (node == -1) + return (xref); + + return (node); +} + /* Call the method in the scope of a given instance. */ int OF_call_method(const char *method, ihandle_t instance, int nargs, int nreturns, diff --git a/sys/dev/ofw/openfirm.h b/sys/dev/ofw/openfirm.h index 17a0b872b6d..e6ede1d6a9b 100644 --- a/sys/dev/ofw/openfirm.h +++ b/sys/dev/ofw/openfirm.h @@ -118,6 +118,14 @@ ssize_t OF_canon(const char *path, char *buf, size_t len); phandle_t OF_finddevice(const char *path); ssize_t OF_package_to_path(phandle_t node, char *buf, size_t len); +/* + * Some OF implementations (IBM, FDT) have a concept of effective phandles + * used for device-tree cross-references. Given one of these, returns the + * real phandle. If one can't be found (or running on OF implementations + * without this property), returns its input. + */ +phandle_t OF_xref_phandle(phandle_t xref); + /* Device I/O functions */ ihandle_t OF_open(const char *path); void OF_close(ihandle_t instance); diff --git a/sys/dev/patm/if_patm.c b/sys/dev/patm/if_patm.c index 279afde1d44..7c0eefa8339 100644 --- a/sys/dev/patm/if_patm.c +++ b/sys/dev/patm/if_patm.c @@ -319,7 +319,7 @@ patm_stop(struct patm_softc *sc) for (i = 0; i < IDT_TSQE_TAG_SPACE; i++) { if ((m = scd->on_card[i]) != NULL) { scd->on_card[i] = 0; - map = m->m_pkthdr.header; + map = m->m_pkthdr.PH_loc.ptr; bus_dmamap_unload(sc->tx_tag, map->map); SLIST_INSERT_HEAD(&sc->tx_maps_free, map, link); diff --git a/sys/dev/patm/if_patm_tx.c b/sys/dev/patm/if_patm_tx.c index 81a1efceb71..f17657d0269 100644 --- a/sys/dev/patm/if_patm_tx.c +++ b/sys/dev/patm/if_patm_tx.c @@ -373,7 +373,7 @@ patm_start(struct ifnet *ifp) } /* save data */ - m->m_pkthdr.header = vcc; + m->m_pkthdr.PH_loc.ptr = vcc; /* try to put it on the channels queue */ if (_IF_QFULL(&vcc->scd->q)) { @@ -473,7 +473,7 @@ patm_launch(struct patm_softc *sc, struct patm_scd *scd) if (m == NULL) break; - a.vcc = m->m_pkthdr.header; + a.vcc = m->m_pkthdr.PH_loc.ptr; /* we must know the number of segments beforehand - count * this may actually give a wrong number of segments for @@ -499,7 +499,7 @@ patm_launch(struct patm_softc *sc, struct patm_scd *scd) } /* load the map */ - m->m_pkthdr.header = map; + m->m_pkthdr.PH_loc.ptr = map; a.mbuf = m; /* handle AAL_RAW */ @@ -690,7 +690,7 @@ patm_tx(struct patm_softc *sc, u_int stamp, u_int status) scd->on_card[last] = NULL; patm_debug(sc, TX, "ok tag=%x", last); - map = m->m_pkthdr.header; + map = m->m_pkthdr.PH_loc.ptr; scd->space += m->m_pkthdr.csum_data; bus_dmamap_sync(sc->tx_tag, map->map, diff --git a/sys/dev/pccbb/pccbb_pci.c b/sys/dev/pccbb/pccbb_pci.c index 542dea6f2d9..0b02286f97c 100644 --- a/sys/dev/pccbb/pccbb_pci.c +++ b/sys/dev/pccbb/pccbb_pci.c @@ -471,10 +471,7 @@ cbb_chipinit(struct cbb_softc *sc) pci_write_config(sc->dev, PCIR_SUBBUS_2, sc->subbus, 1); /* Enable memory access */ - PCI_MASK_CONFIG(sc->dev, PCIR_COMMAND, - | PCIM_CMD_MEMEN - | PCIM_CMD_PORTEN - | PCIM_CMD_BUSMASTEREN, 2); + pci_enable_busmaster(sc->dev); /* disable Legacy IO */ switch (sc->chipset) { diff --git a/sys/dev/pci/fixup_pci.c b/sys/dev/pci/fixup_pci.c index 13fc4b15ffc..3a40a7ab418 100644 --- a/sys/dev/pci/fixup_pci.c +++ b/sys/dev/pci/fixup_pci.c @@ -92,13 +92,13 @@ fixwsc_natoma(device_t dev) pmccfg = pci_read_config(dev, 0x50, 2); #if defined(SMP) if (pmccfg & 0x8000) { - printf("Correcting Natoma config for SMP\n"); + device_printf(dev, "correcting Natoma config for SMP\n"); pmccfg &= ~0x8000; pci_write_config(dev, 0x50, pmccfg, 2); } #else if ((pmccfg & 0x8000) == 0) { - printf("Correcting Natoma config for non-SMP\n"); + device_printf(dev, "correcting Natoma config for non-SMP\n"); pmccfg |= 0x8000; pci_write_config(dev, 0x50, pmccfg, 2); } @@ -132,7 +132,8 @@ fixc1_nforce2(device_t dev) pci_get_function(dev) == 0) { val = pci_read_config(dev, 0x6c, 4); if (val & 0x000e0000) { - printf("Correcting nForce2 C1 CPU disconnect hangs\n"); + device_printf(dev, + "correcting nForce2 C1 CPU disconnect hangs\n"); val &= ~0x000e0000; pci_write_config(dev, 0x6c, val, 4); } diff --git a/sys/dev/pci/pcivar.h b/sys/dev/pci/pcivar.h index f4c6f51007b..d733e3b6ced 100644 --- a/sys/dev/pci/pcivar.h +++ b/sys/dev/pci/pcivar.h @@ -517,4 +517,11 @@ extern uint32_t pci_generation; struct pci_map *pci_find_bar(device_t dev, int reg); int pci_bar_enabled(device_t dev, struct pci_map *pm); +#define VGA_PCI_BIOS_SHADOW_ADDR 0xC0000 +#define VGA_PCI_BIOS_SHADOW_SIZE 131072 + +int vga_pci_is_boot_display(device_t dev); +void * vga_pci_map_bios(device_t dev, size_t *size); +void vga_pci_unmap_bios(device_t dev, void *bios); + #endif /* _PCIVAR_H_ */ diff --git a/sys/dev/pci/vga_pci.c b/sys/dev/pci/vga_pci.c index dc3237c790e..d7ff20f3d4d 100644 --- a/sys/dev/pci/vga_pci.c +++ b/sys/dev/pci/vga_pci.c @@ -46,6 +46,11 @@ __FBSDID("$FreeBSD$"); #include #include +#if defined(__amd64__) || defined(__i386__) || defined(__ia64__) +#include +#include +#endif + #include #include @@ -62,11 +67,95 @@ struct vga_pci_softc { SYSCTL_DECL(_hw_pci); +static struct vga_resource *lookup_res(struct vga_pci_softc *sc, int rid); +static struct resource *vga_pci_alloc_resource(device_t dev, device_t child, + int type, int *rid, u_long start, u_long end, u_long count, u_int flags); +static int vga_pci_release_resource(device_t dev, device_t child, int type, + int rid, struct resource *r); + int vga_pci_default_unit = -1; TUNABLE_INT("hw.pci.default_vgapci_unit", &vga_pci_default_unit); SYSCTL_INT(_hw_pci, OID_AUTO, default_vgapci_unit, CTLFLAG_RDTUN, &vga_pci_default_unit, -1, "Default VGA-compatible display"); +int +vga_pci_is_boot_display(device_t dev) +{ + + /* + * Return true if the given device is the default display used + * at boot time. + */ + return ( + (pci_get_class(dev) == PCIC_DISPLAY || + (pci_get_class(dev) == PCIC_OLD && + pci_get_subclass(dev) == PCIS_OLD_VGA)) && + device_get_unit(dev) == vga_pci_default_unit); +} + +void * +vga_pci_map_bios(device_t dev, size_t *size) +{ + int rid; + struct resource *res; + +#if defined(__amd64__) || defined(__i386__) || defined(__ia64__) + if (vga_pci_is_boot_display(dev)) { + /* + * On x86, the System BIOS copy the default display + * device's Video BIOS at a fixed location in system + * memory (0xC0000, 128 kBytes long) at boot time. + * + * We use this copy for the default boot device, because + * the original ROM may not be valid after boot. + */ + + *size = VGA_PCI_BIOS_SHADOW_SIZE; + return (pmap_mapbios(VGA_PCI_BIOS_SHADOW_ADDR, *size)); + } +#endif + + rid = PCIR_BIOS; + res = vga_pci_alloc_resource(dev, NULL, SYS_RES_MEMORY, &rid, 0ul, + ~0ul, 1, RF_ACTIVE); + if (res == NULL) { + return (NULL); + } + + *size = rman_get_size(res); + return (rman_get_virtual(res)); +} + +void +vga_pci_unmap_bios(device_t dev, void *bios) +{ + struct vga_resource *vr; + + if (bios == NULL) { + return; + } + +#if defined(__amd64__) || defined(__i386__) || defined(__ia64__) + if (vga_pci_is_boot_display(dev)) { + /* We mapped the BIOS shadow copy located at 0xC0000. */ + pmap_unmapdev((vm_offset_t)bios, VGA_PCI_BIOS_SHADOW_SIZE); + + return; + } +#endif + + /* + * Look up the PCIR_BIOS resource in our softc. It should match + * the address we returned previously. + */ + vr = lookup_res(device_get_softc(dev), PCIR_BIOS); + KASSERT(vr->vr_res != NULL, ("vga_pci_unmap_bios: bios not mapped")); + KASSERT(rman_get_virtual(vr->vr_res) == bios, + ("vga_pci_unmap_bios: mismatch")); + vga_pci_release_resource(dev, NULL, SYS_RES_MEMORY, PCIR_BIOS, + vr->vr_res); +} + static int vga_pci_probe(device_t dev) { diff --git a/sys/dev/ppbus/if_plip.c b/sys/dev/ppbus/if_plip.c index ceb4bce4250..60fc2128392 100644 --- a/sys/dev/ppbus/if_plip.c +++ b/sys/dev/ppbus/if_plip.c @@ -410,7 +410,6 @@ lpioctl(struct ifnet *ifp, u_long cmd, caddr_t data) int error; switch (cmd) { - case SIOCSIFDSTADDR: case SIOCAIFADDR: case SIOCSIFADDR: if (ifa->ifa_addr->sa_family != AF_INET) diff --git a/sys/dev/pst/pst-pci.c b/sys/dev/pst/pst-pci.c index 7070e6b2510..8ed8fed3b8e 100644 --- a/sys/dev/pst/pst-pci.c +++ b/sys/dev/pst/pst-pci.c @@ -88,9 +88,7 @@ iop_pci_attach(device_t dev) RF_SHAREABLE | RF_ACTIVE); /* now setup the infrastructure to talk to the device */ - pci_write_config(dev, PCIR_COMMAND, - pci_read_config(dev, PCIR_COMMAND, 1) | - PCIM_CMD_MEMEN | PCIM_CMD_BUSMASTEREN, 1); + pci_enable_busmaster(dev); sc->ibase = rman_get_virtual(sc->r_mem); sc->reg = (struct i2o_registers *)sc->ibase; diff --git a/sys/dev/qlxgb/qla_hw.c b/sys/dev/qlxgb/qla_hw.c index c866cf5824d..ea20b635a45 100644 --- a/sys/dev/qlxgb/qla_hw.c +++ b/sys/dev/qlxgb/qla_hw.c @@ -1000,9 +1000,9 @@ qla_hw_send(qla_host_t *ha, bus_dma_segment_t *segs, int nsegs, (mp->m_pkthdr.len > ha->max_frame_size)){ /* TBD: copy into private buffer and send it */ device_printf(dev, - "%s: (nsegs[%d, %d, 0x%x] > Q8_TX_MAX_SEGMENTS)\n", + "%s: (nsegs[%d, %d, 0x%b] > Q8_TX_MAX_SEGMENTS)\n", __func__, nsegs, mp->m_pkthdr.len, - mp->m_pkthdr.csum_flags); + (int)mp->m_pkthdr.csum_flags, CSUM_BITS); qla_dump_buf8(ha, "qla_hw_send: wrong pkt", mtod(mp, char *), mp->m_len); return (EINVAL); diff --git a/sys/dev/qlxgbe/ql_misc.c b/sys/dev/qlxgbe/ql_misc.c index 6c7292ff1d7..ef0f4a75784 100644 --- a/sys/dev/qlxgbe/ql_misc.c +++ b/sys/dev/qlxgbe/ql_misc.c @@ -321,7 +321,7 @@ qla_get_fdt(qla_host_t *ha) } while ((count < 10000) && (data32 != 0x6)); - if (count == 0 && data32 != 0x6) { + if (data32 != 0x6) { qla_sem_unlock(ha, Q8_FLASH_UNLOCK); device_printf(ha->pci_dev, "%s: Poll Q8_FLASH_STATUS failed\n", @@ -401,7 +401,7 @@ qla_flash_write_enable(qla_host_t *ha, int enable) } while ((count < 10000) && (data32 != 0x6)); - if (count == 0 && data32 != 0x6) { + if (data32 != 0x6) { device_printf(ha->pci_dev, "%s: Poll Q8_FLASH_STATUS failed\n", __func__); @@ -432,7 +432,7 @@ qla_erase_flash_sector(qla_host_t *ha, uint32_t start) } while (((count++) < 1000) && (data32 != 0x6)); - if (count == 0 && data32 != 0x6) { + if (data32 != 0x6) { device_printf(ha->pci_dev, "%s: Poll Q8_FLASH_STATUS failed\n", __func__); @@ -479,7 +479,7 @@ qla_erase_flash_sector(qla_host_t *ha, uint32_t start) } while (((count++) < 1000) && (data32 != 0x6)); - if (count == 0 && data32 != 0x6) { + if (data32 != 0x6) { device_printf(ha->pci_dev, "%s: Poll Q8_FLASH_STATUS failed\n", __func__); @@ -575,7 +575,7 @@ qla_wr_flash32(qla_host_t *ha, uint32_t off, uint32_t *data) } while ((count < 10000) && (data32 != 0x6)); - if (count == 0 && data32 != 0x6) { + if (data32 != 0x6) { device_printf(ha->pci_dev, "%s: Poll Q8_FLASH_STATUS failed\n", __func__); diff --git a/sys/dev/qlxgbe/ql_os.c b/sys/dev/qlxgbe/ql_os.c index c6fcae31b30..700c4a81381 100644 --- a/sys/dev/qlxgbe/ql_os.c +++ b/sys/dev/qlxgbe/ql_os.c @@ -1642,8 +1642,6 @@ qla_error_recovery(void *context, int pending) QLA_UNLOCK(ha, __func__); - ql_minidump(ha); - if ((ha->pci_func & 0x1) == 0) { if (!ha->msg_from_peer) { @@ -1656,6 +1654,8 @@ qla_error_recovery(void *context, int pending) ha->msg_from_peer = 0; + ql_minidump(ha); + (void) ql_init_hw(ha); qla_free_xmt_bufs(ha); qla_free_rcv_bufs(ha); diff --git a/sys/dev/random/harvest.c b/sys/dev/random/harvest.c index a1ff8dde4c6..473e429317a 100644 --- a/sys/dev/random/harvest.c +++ b/sys/dev/random/harvest.c @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2000-2004 Mark R V Murray + * Copyright (c) 2000-2013 Mark R V Murray * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -60,7 +60,7 @@ static int (*read_func)(void *, int) = read_random_phony; /* Initialise the harvester at load time */ void -random_yarrow_init_harvester(void (*reaper)(u_int64_t, const void *, u_int, +randomdev_init_harvester(void (*reaper)(u_int64_t, const void *, u_int, u_int, u_int, enum esource), int (*reader)(void *, int)) { reap_func = reaper; @@ -69,7 +69,7 @@ random_yarrow_init_harvester(void (*reaper)(u_int64_t, const void *, u_int, /* Deinitialise the harvester at unload time */ void -random_yarrow_deinit_harvester(void) +randomdev_deinit_harvester(void) { reap_func = NULL; read_func = read_random_phony; diff --git a/sys/dev/random/hash.c b/sys/dev/random/hash.c index 611f866c424..cf0feaa66ef 100644 --- a/sys/dev/random/hash.c +++ b/sys/dev/random/hash.c @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2000-2004 Mark R V Murray + * Copyright (c) 2000-2013 Mark R V Murray * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -36,46 +36,46 @@ __FBSDID("$FreeBSD$"); #include -/* initialise the hash */ +/* Initialise the hash */ void -yarrow_hash_init(struct yarrowhash *context) +randomdev_hash_init(struct randomdev_hash *context) { SHA256_Init(&context->sha); } -/* iterate the hash */ +/* Iterate the hash */ void -yarrow_hash_iterate(struct yarrowhash *context, void *data, size_t size) +randomdev_hash_iterate(struct randomdev_hash *context, void *data, size_t size) { SHA256_Update(&context->sha, data, size); } -/* Conclude by returning the hash in the supplied /buf/ which must be +/* Conclude by returning the hash in the supplied <*buf> which must be * KEYSIZE bytes long. */ void -yarrow_hash_finish(struct yarrowhash *context, void *buf) +randomdev_hash_finish(struct randomdev_hash *context, void *buf) { SHA256_Final(buf, &context->sha); } /* Initialise the encryption routine by setting up the key schedule - * from the supplied /data/ which must be KEYSIZE bytes of binary - * data. + * from the supplied <*data> which must be KEYSIZE bytes of binary + * data. Use CBC mode for better avalanche. */ void -yarrow_encrypt_init(struct yarrowkey *context, void *data) +randomdev_encrypt_init(struct randomdev_key *context, void *data) { rijndael_cipherInit(&context->cipher, MODE_CBC, NULL); rijndael_makeKey(&context->key, DIR_ENCRYPT, KEYSIZE*8, data); } /* Encrypt the supplied data using the key schedule preset in the context. - * KEYSIZE bytes are encrypted from /d_in/ to /d_out/. + * bytes are encrypted from <*d_in> to <*d_out>. must be + * a multiple of BLOCKSIZE. */ void -yarrow_encrypt(struct yarrowkey *context, void *d_in, void *d_out) +randomdev_encrypt(struct randomdev_key *context, void *d_in, void *d_out, unsigned length) { - rijndael_blockEncrypt(&context->cipher, &context->key, d_in, - KEYSIZE*8, d_out); + rijndael_blockEncrypt(&context->cipher, &context->key, d_in, length*8, d_out); } diff --git a/sys/dev/random/hash.h b/sys/dev/random/hash.h index 8580d145d68..62f116daa00 100644 --- a/sys/dev/random/hash.h +++ b/sys/dev/random/hash.h @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2000-2004 Mark R V Murray + * Copyright (c) 2000-2013 Mark R V Murray * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -26,19 +26,20 @@ * $FreeBSD$ */ -#define KEYSIZE 32 /* (in bytes) 32 bytes == 256 bits */ +#define KEYSIZE 32 /* (in bytes) == 256 bits */ +#define BLOCKSIZE 16 /* (in bytes) == 128 bits */ -struct yarrowhash { /* Big! Make static! */ +struct randomdev_hash { /* Big! Make static! */ SHA256_CTX sha; }; -struct yarrowkey { /* Big! Make static! */ +struct randomdev_key { /* Big! Make static! */ keyInstance key; /* Key schedule */ cipherInstance cipher; /* Rijndael internal */ }; -void yarrow_hash_init(struct yarrowhash *); -void yarrow_hash_iterate(struct yarrowhash *, void *, size_t); -void yarrow_hash_finish(struct yarrowhash *, void *); -void yarrow_encrypt_init(struct yarrowkey *, void *); -void yarrow_encrypt(struct yarrowkey *context, void *, void *); +void randomdev_hash_init(struct randomdev_hash *); +void randomdev_hash_iterate(struct randomdev_hash *, void *, size_t); +void randomdev_hash_finish(struct randomdev_hash *, void *); +void randomdev_encrypt_init(struct randomdev_key *, void *); +void randomdev_encrypt(struct randomdev_key *context, void *, void *, unsigned); diff --git a/sys/dev/random/ivy.c b/sys/dev/random/ivy.c index f81c148800b..07c87123acc 100644 --- a/sys/dev/random/ivy.c +++ b/sys/dev/random/ivy.c @@ -1,4 +1,5 @@ /*- + * Copyright (c) 2013 David E. O'Brien * Copyright (c) 2012 Konstantin Belousov * All rights reserved. * @@ -28,16 +29,19 @@ #include __FBSDID("$FreeBSD$"); -#include "opt_cpu.h" - -#ifdef RDRAND_RNG - #include #include +#include #include +#include #include #include #include + +#include +#include + +#include #include #define RETRY_COUNT 10 @@ -46,7 +50,7 @@ static void random_ivy_init(void); static void random_ivy_deinit(void); static int random_ivy_read(void *, int); -struct random_systat random_ivy = { +struct random_adaptor random_ivy = { .ident = "Hardware, Intel IvyBridge+ RNG", .init = random_ivy_init, .deinit = random_ivy_deinit, @@ -114,4 +118,28 @@ random_ivy_read(void *buf, int c) return (c - count); } +static int +rdrand_modevent(module_t mod, int type, void *unused) +{ + + switch (type) { + case MOD_LOAD: + if (cpu_feature2 & CPUID2_RDRAND) { + random_adaptor_register("rdrand", &random_ivy); + EVENTHANDLER_INVOKE(random_adaptor_attach, &random_ivy); + return (0); + } else { +#ifndef KLD_MODULE + if (bootverbose) #endif + printf( + "%s: RDRAND feature is not present on this CPU\n", + random_ivy.ident); + return (0); + } + } + + return (EINVAL); +} + +RANDOM_ADAPTOR_MODULE(random_rdrand, rdrand_modevent, 1); diff --git a/sys/dev/random/nehemiah.c b/sys/dev/random/nehemiah.c index f3afa89fcf8..1b4416eb19c 100644 --- a/sys/dev/random/nehemiah.c +++ b/sys/dev/random/nehemiah.c @@ -1,4 +1,5 @@ /*- + * Copyright (c) 2013 David E. O'Brien * Copyright (c) 2004 Mark R V Murray * All rights reserved. * @@ -28,19 +29,20 @@ #include __FBSDID("$FreeBSD$"); -#include "opt_cpu.h" - -#ifdef PADLOCK_RNG - #include #include #include #include +#include #include #include +#include #include +#include +#include +#include #include #define RANDOM_BLOCK_SIZE 256 @@ -50,7 +52,7 @@ static void random_nehemiah_init(void); static void random_nehemiah_deinit(void); static int random_nehemiah_read(void *, int); -struct random_systat random_nehemiah = { +struct random_adaptor random_nehemiah = { .ident = "Hardware, VIA Nehemiah", .init = random_nehemiah_init, .deinit = random_nehemiah_deinit, @@ -208,4 +210,29 @@ random_nehemiah_read(void *buf, int c) return (c); } +static int +nehemiah_modevent(module_t mod, int type, void *unused) +{ + + switch (type) { + case MOD_LOAD: + if (via_feature_rng & VIA_HAS_RNG) { + random_adaptor_register("nehemiah", &random_nehemiah); + EVENTHANDLER_INVOKE(random_adaptor_attach, + &random_nehemiah); + return (0); + } else { +#ifndef KLD_MODULE + if (bootverbose) #endif + printf( + "%s: VIA RNG feature is not present on this CPU\n", + random_nehemiah.ident); + return (0); + } + } + + return (EINVAL); +} + +RANDOM_ADAPTOR_MODULE(nehemiah, nehemiah_modevent, 1); diff --git a/sys/dev/random/pseudo_rng.c b/sys/dev/random/pseudo_rng.c new file mode 100644 index 00000000000..15cc7b3785d --- /dev/null +++ b/sys/dev/random/pseudo_rng.c @@ -0,0 +1,125 @@ +/*- + * Copyright (c) 2013 Arthur Mesh + * 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 + * in this position and unchanged. + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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 +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include + +#include +#include + +static struct mtx pseudo_random_block_mtx; + +/* Used to fake out unused random calls in random_adaptor */ +void +random_null_func(void) +{ +} + +static int +pseudo_random_block_read(void *buf __unused, int c __unused) +{ + + mtx_lock(&pseudo_random_block_mtx); + + printf("random(4) device is blocking.\n"); + msleep(pseudo_random_block_read, &pseudo_random_block_mtx, 0, + "block", 0); + + mtx_unlock(&pseudo_random_block_mtx); + + return (0); +} + +static void +pseudo_random_block_init(void) +{ + + mtx_init(&pseudo_random_block_mtx, "sleep mtx for random_block", + NULL, MTX_DEF); +} + +static void +pseudo_random_block_deinit(void) +{ + + mtx_destroy(&pseudo_random_block_mtx); +} + +struct random_adaptor pseudo_random_block = { + .ident = "pseudo-RNG that always blocks", + .init = pseudo_random_block_init, + .deinit = pseudo_random_block_deinit, + .read = pseudo_random_block_read, + .write = (random_write_func_t *)random_null_func, + .reseed = (random_reseed_func_t *)random_null_func, + .seeded = 1, +}; + +static int +pseudo_random_panic_read(void *buf, int c) +{ + + panic("Insert a witty panic msg in here."); + + return (0); +} + +struct random_adaptor pseudo_random_panic = { + .ident = "pseudo-RNG that always panics on first read(2)", + .init = (random_init_func_t *)random_null_func, + .deinit = (random_deinit_func_t *)random_null_func, + .read = pseudo_random_panic_read, + .write = (random_write_func_t *)random_null_func, + .reseed = (random_reseed_func_t *)random_null_func, + .seeded = 1, +}; + +static int +pseudo_random_modevent(module_t mod, int type, void *unused) +{ + + switch (type) { + case MOD_LOAD: + random_adaptor_register("block", &pseudo_random_block); + EVENTHANDLER_INVOKE(random_adaptor_attach, + &pseudo_random_block); + + random_adaptor_register("panic", &pseudo_random_panic); + + return (0); + } + + return (EINVAL); +} + +RANDOM_ADAPTOR_MODULE(pseudo, pseudo_random_modevent, 1); diff --git a/sys/dev/random/random_adaptors.c b/sys/dev/random/random_adaptors.c new file mode 100644 index 00000000000..43f55f22fe4 --- /dev/null +++ b/sys/dev/random/random_adaptors.c @@ -0,0 +1,279 @@ +/*- + * Copyright (c) 2013 Arthur Mesh + * Copyright (c) 2013 David E. O'Brien + * Copyright (c) 2004 Mark R V Murray + * 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 + * in this position and unchanged. + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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 +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +LIST_HEAD(adaptors_head, random_adaptors); +static struct adaptors_head adaptors = LIST_HEAD_INITIALIZER(adaptors); +static struct sx adaptors_lock; /* need a sleepable lock */ + +/* List for the dynamic sysctls */ +static struct sysctl_ctx_list random_clist; + +struct random_adaptor *random_adaptor; + +MALLOC_DEFINE(M_RANDOM_ADAPTORS, "random_adaptors", "Random adaptors buffers"); + +int +random_adaptor_register(const char *name, struct random_adaptor *rsp) +{ + struct random_adaptors *rpp; + + KASSERT(name != NULL && rsp != NULL, ("invalid input to %s", __func__)); + + rpp = malloc(sizeof(struct random_adaptors), M_RANDOM_ADAPTORS, + M_WAITOK); + rpp->name = name; + rpp->rsp = rsp; + + sx_xlock(&adaptors_lock); + LIST_INSERT_HEAD(&adaptors, rpp, entries); + sx_xunlock(&adaptors_lock); + + return (0); +} + +struct random_adaptor * +random_adaptor_get(const char *name) +{ + struct random_adaptors *rpp; + struct random_adaptor *rsp; + + rsp = NULL; + + sx_slock(&adaptors_lock); + + LIST_FOREACH(rpp, &adaptors, entries) + if (strcmp(rpp->name, name) == 0) + rsp = rpp->rsp; + + sx_sunlock(&adaptors_lock); + + return (rsp); +} + +/* + * In the past, the logic of the random_adaptor selection was inverted, such + * that hardware RNGs would be chosen unless disabled. This routine is here to + * preserve that functionality to avoid folks losing their hardware RNGs by + * upgrading to newer kernel. + */ +static void +random_adaptor_choose_legacy(struct random_adaptor **adaptor) +{ + struct random_adaptor *tmp; + int enable; + + /* Then go looking for hardware */ + enable = 1; + TUNABLE_INT_FETCH("hw.nehemiah_rng_enable", &enable); + if (enable && (tmp = random_adaptor_get("nehemiah"))) + *adaptor = tmp; + + enable = 1; + TUNABLE_INT_FETCH("hw.ivy_rng_enable", &enable); + if (enable && (tmp = random_adaptor_get("rdrand"))) + *adaptor = tmp; +} + +/* + * Walk a list of registered random(4) adaptors and pick the last non-selected + * one. + * + * If none are selected, use yarrow if available. + */ +void +random_adaptor_choose(struct random_adaptor **adaptor) +{ + char rngs[128], *token, *cp; + struct random_adaptors *rpp; + + KASSERT(adaptor != NULL, ("pre-conditions failed")); + + *adaptor = NULL; + + random_adaptor_choose_legacy(adaptor); + + if (*adaptor != NULL) + return; + + if (TUNABLE_STR_FETCH("rngs_want", rngs, sizeof(rngs))) { + cp = rngs; + + while ((token = strsep(&cp, ",")) != NULL) { + if ((*adaptor = random_adaptor_get(token)) != NULL) + break; + else if (bootverbose) + printf( + "%s random adaptor is not available, skipping\n", + token); + } + } + + if (*adaptor == NULL) { + /* + * Either no RNGs are prefered via rngs_want tunable, or + * no prefered RNGs are registered. + * Fallback to Yarrow. + */ + *adaptor = random_adaptor_get("yarrow"); + + if (*adaptor == NULL) { + /* + * Yarrow doesn't seem to be available. + * Fallback to the first thing that's on the list of + * available RNGs. + */ + sx_slock(&adaptors_lock); + + rpp = LIST_FIRST(&adaptors); + if (rpp != NULL) + *adaptor = rpp->rsp; + + sx_sunlock(&adaptors_lock); + } + + if (bootverbose && *adaptor) + printf("Falling back to <%s> random adaptor\n", + (*adaptor)->ident); + } +} + +static void +random_adaptors_deinit(void *unused) +{ + + sx_destroy(&adaptors_lock); + sysctl_ctx_free(&random_clist); +} + +static int +random_sysctl_adaptors_handler(SYSCTL_HANDLER_ARGS) +{ + struct random_adaptors *rpp; + int error, count; + + count = error = 0; + + sx_slock(&adaptors_lock); + + if (LIST_EMPTY(&adaptors)) { + error = SYSCTL_OUT(req, "", 0); + } else { + + LIST_FOREACH(rpp, &adaptors, entries) { + + error = SYSCTL_OUT(req, ",", count++ ? 1 : 0); + + if (error) + break; + + error = SYSCTL_OUT(req, rpp->name, strlen(rpp->name)); + + if (error) + break; + } + } + + sx_sunlock(&adaptors_lock); + + return (error); +} + +static int +random_sysctl_active_adaptor_handler(SYSCTL_HANDLER_ARGS) +{ + struct random_adaptor *rsp; + struct random_adaptors *rpp; + const char *name; + int error; + + name = NULL; + rsp = random_adaptor; + + if (rsp != NULL) { + sx_slock(&adaptors_lock); + + LIST_FOREACH(rpp, &adaptors, entries) { + if (rpp->rsp == rsp) + name = rpp->name; + } + + sx_sunlock(&adaptors_lock); + } + + if (rsp == NULL || name == NULL) { + error = SYSCTL_OUT(req, "", 0); + } else { + error = SYSCTL_OUT(req, name, strlen(name)); + } + + return (error); +} + +static void +random_adaptors_init(void *unused) +{ + + SYSCTL_PROC(_kern_random, OID_AUTO, adaptors, + CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, + NULL, 0, random_sysctl_adaptors_handler, "", + "Random Number Generator adaptors"); + + SYSCTL_PROC(_kern_random, OID_AUTO, active_adaptor, + CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, + NULL, 0, random_sysctl_active_adaptor_handler, "", + "Active Random Number Generator Adaptor"); + + sx_init(&adaptors_lock, "random_adaptors"); +} + +SYSCTL_NODE(_kern, OID_AUTO, random, CTLFLAG_RW, 0, "Random Number Generator"); + +SYSINIT(random_adaptors, SI_SUB_DRIVERS, SI_ORDER_FIRST, random_adaptors_init, + NULL); +SYSUNINIT(random_adaptors, SI_SUB_DRIVERS, SI_ORDER_FIRST, + random_adaptors_deinit, NULL); diff --git a/sys/dev/random/random_adaptors.h b/sys/dev/random/random_adaptors.h new file mode 100644 index 00000000000..fa5f7c82883 --- /dev/null +++ b/sys/dev/random/random_adaptors.h @@ -0,0 +1,69 @@ +/*- + * Copyright (c) 2013 Arthur Mesh + * 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 + * in this position and unchanged. + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + * + * $FreeBSD$ + */ + +#ifndef __RANDOM_ADAPTORS_H__ +#define __RANDOM_ADAPTORS_H__ + +#include + +struct random_adaptors { + LIST_ENTRY(random_adaptors) entries; /* list of providers */ + const char *name; /* name of random adaptor */ + struct random_adaptor *rsp; +}; + +struct random_adaptor *random_adaptor_get(const char *); +int random_adaptor_register(const char *, struct random_adaptor *); +void random_adaptor_choose(struct random_adaptor **); + +extern struct random_adaptor *random_adaptor; + +/* + * random_adaptor's should be registered prior to + * random module (SI_SUB_DRIVERS/SI_ORDER_MIDDLE) + */ +#define RANDOM_ADAPTOR_MODULE(name, modevent, ver) \ + static moduledata_t name##_mod = { \ + #name, \ + modevent, \ + 0 \ + }; \ + DECLARE_MODULE(name, name##_mod, SI_SUB_DRIVERS, \ + SI_ORDER_SECOND); \ + MODULE_VERSION(name, ver); \ + MODULE_DEPEND(name, random, 1, 1, 1); + +typedef void (*random_adaptor_attach_hook)(void *, struct random_adaptor *); +EVENTHANDLER_DECLARE(random_adaptor_attach, random_adaptor_attach_hook); + +/* kern.random sysctls */ +#ifdef SYSCTL_DECL /* from sysctl.h */ +SYSCTL_DECL(_kern_random); +#endif /* SYSCTL_DECL */ + +#endif /* __RANDOM_ADAPTORS_H__ */ diff --git a/sys/dev/random/random_harvestq.c b/sys/dev/random/random_harvestq.c new file mode 100644 index 00000000000..fc87cde0068 --- /dev/null +++ b/sys/dev/random/random_harvestq.c @@ -0,0 +1,251 @@ +/*- + * Copyright (c) 2013 Arthur Mesh + * Copyright (c) 2000-2009 Mark R V Murray + * Copyright (c) 2004 Robert N. M. Watson + * 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 + * in this position and unchanged. + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + * + * $FreeBSD$ + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "random_harvestq.h" + +#define RANDOM_FIFO_MAX 256 /* How many events to queue up */ + +MALLOC_DEFINE(M_ENTROPY, "entropy", "Entropy harvesting buffers"); + +/* + * The harvest mutex protects the consistency of the entropy fifos and + * empty fifo. + */ +struct mtx harvest_mtx; + +/* Lockable FIFO queue holding entropy buffers */ +struct entropyfifo { + int count; + STAILQ_HEAD(harvestlist, harvest) head; +}; + +/* Empty entropy buffers */ +static struct entropyfifo emptyfifo; + +#define EMPTYBUFFERS 1024 + +/* Harvested entropy */ +static struct entropyfifo harvestfifo[ENTROPYSOURCE]; + +/* <0 to end the kthread, 0 to let it run, 1 to flush the harvest queues */ +int random_kthread_control = 0; + +static struct proc *random_kthread_proc; + +static void +random_kthread(void *arg) +{ + STAILQ_HEAD(, harvest) local_queue; + struct harvest *event = NULL; + int local_count; + enum esource source; + event_proc_f func = arg; + + STAILQ_INIT(&local_queue); + local_count = 0; + + /* Process until told to stop */ + mtx_lock_spin(&harvest_mtx); + for (; random_kthread_control >= 0;) { + + /* Cycle through all the entropy sources */ + for (source = RANDOM_START; source < ENTROPYSOURCE; source++) { + /* + * Drain entropy source records into a thread-local + * queue for processing while not holding the mutex. + */ + STAILQ_CONCAT(&local_queue, &harvestfifo[source].head); + local_count += harvestfifo[source].count; + harvestfifo[source].count = 0; + } + + /* + * Deal with events, if any, dropping the mutex as we process + * each event. Then push the events back into the empty + * fifo. + */ + if (!STAILQ_EMPTY(&local_queue)) { + mtx_unlock_spin(&harvest_mtx); + STAILQ_FOREACH(event, &local_queue, next) + func(event); + mtx_lock_spin(&harvest_mtx); + STAILQ_CONCAT(&emptyfifo.head, &local_queue); + emptyfifo.count += local_count; + local_count = 0; + } + + KASSERT(local_count == 0, ("random_kthread: local_count %d", + local_count)); + + /* + * If a queue flush was commanded, it has now happened, + * and we can mark this by resetting the command. + */ + if (random_kthread_control == 1) + random_kthread_control = 0; + + /* Work done, so don't belabour the issue */ + msleep_spin_sbt(&random_kthread_control, &harvest_mtx, + "-", SBT_1S / 10, 0, C_PREL(1)); + + } + mtx_unlock_spin(&harvest_mtx); + + random_set_wakeup_exit(&random_kthread_control); + /* NOTREACHED */ +} + +void +random_harvestq_init(event_proc_f cb) +{ + int error, i; + struct harvest *np; + enum esource e; + + /* Initialise the harvest fifos */ + STAILQ_INIT(&emptyfifo.head); + emptyfifo.count = 0; + for (i = 0; i < EMPTYBUFFERS; i++) { + np = malloc(sizeof(struct harvest), M_ENTROPY, M_WAITOK); + STAILQ_INSERT_TAIL(&emptyfifo.head, np, next); + } + for (e = RANDOM_START; e < ENTROPYSOURCE; e++) { + STAILQ_INIT(&harvestfifo[e].head); + harvestfifo[e].count = 0; + } + + mtx_init(&harvest_mtx, "entropy harvest mutex", NULL, MTX_SPIN); + + + /* Start the hash/reseed thread */ + error = kproc_create(random_kthread, cb, + &random_kthread_proc, RFHIGHPID, 0, "rand_harvestq"); /* RANDOM_CSPRNG_NAME */ + + if (error != 0) + panic("Cannot create entropy maintenance thread."); +} + +void +random_harvestq_deinit(void) +{ + struct harvest *np; + enum esource e; + + /* Destroy the harvest fifos */ + while (!STAILQ_EMPTY(&emptyfifo.head)) { + np = STAILQ_FIRST(&emptyfifo.head); + STAILQ_REMOVE_HEAD(&emptyfifo.head, next); + free(np, M_ENTROPY); + } + for (e = RANDOM_START; e < ENTROPYSOURCE; e++) { + while (!STAILQ_EMPTY(&harvestfifo[e].head)) { + np = STAILQ_FIRST(&harvestfifo[e].head); + STAILQ_REMOVE_HEAD(&harvestfifo[e].head, next); + free(np, M_ENTROPY); + } + } + + mtx_destroy(&harvest_mtx); +} + +/* + * Entropy harvesting routine. This is supposed to be fast; do + * not do anything slow in here! + */ +void +random_harvestq_internal(u_int64_t somecounter, const void *entropy, + u_int count, u_int bits, u_int frac, enum esource origin) +{ + struct harvest *event; + + KASSERT(origin >= RANDOM_START && origin <= RANDOM_PURE, + ("random_harvest_internal: origin %d invalid\n", origin)); + + /* Lockless read to avoid lock operations if fifo is full. */ + if (harvestfifo[origin].count >= RANDOM_FIFO_MAX) + return; + + mtx_lock_spin(&harvest_mtx); + + /* + * Don't make the harvest queues too big - help to prevent low-grade + * entropy swamping + */ + if (harvestfifo[origin].count < RANDOM_FIFO_MAX) { + event = STAILQ_FIRST(&emptyfifo.head); + if (event != NULL) { + /* Add the harvested data to the fifo */ + STAILQ_REMOVE_HEAD(&emptyfifo.head, next); + harvestfifo[origin].count++; + event->somecounter = somecounter; + event->size = count; + event->bits = bits; + event->frac = frac; + event->source = origin; + + /* XXXX Come back and make this dynamic! */ + count = MIN(count, HARVESTSIZE); + memcpy(event->entropy, entropy, count); + +#if 0 + { + int i; + printf("Harvest:%16jX ", event->somecounter); + for (i = 0; i < event->size; i++) + printf("%02X", event->entropy[i]); + for (; i < 16; i++) + printf(" "); + printf(" %2d 0x%2X.%03X %02X\n", event->size, event->bits, event->frac, event->source); + } +#endif + + STAILQ_INSERT_TAIL(&harvestfifo[origin].head, + event, next); + } + } + mtx_unlock_spin(&harvest_mtx); +} + diff --git a/sys/i386/include/xen/xen_clock_util.h b/sys/dev/random/random_harvestq.h similarity index 73% rename from sys/i386/include/xen/xen_clock_util.h rename to sys/dev/random/random_harvestq.h index ecb8e2ccbf9..9766c7728c4 100644 --- a/sys/i386/include/xen/xen_clock_util.h +++ b/sys/dev/random/random_harvestq.h @@ -1,13 +1,13 @@ -/* - * - * Copyright (c) 2009 Adrian Chadd +/*- + * Copyright (c) 2013 Arthur Mesh * 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. + * notice, this list of conditions and the following disclaimer + * in this position and unchanged. * 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. @@ -23,14 +23,19 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * - * * $FreeBSD$ */ -#ifndef __XEN_CLOCK_UTIL_H__ -#define __XEN_CLOCK_UTIL_H__ +#ifndef __RANDOM_HARVEST_H__ +#define __RANDOM_HARVEST_H__ -extern void xen_fetch_wallclock(struct timespec *ts); -extern void xen_fetch_uptime(struct timespec *ts); +typedef void (*event_proc_f)(struct harvest *event); -#endif /* __XEN_CLOCK_UTIL_H__ */ +void random_harvestq_init(event_proc_f); +void random_harvestq_deinit(void); +void random_harvestq_internal(u_int64_t, const void *, + u_int, u_int, u_int, enum esource); + +extern int random_kthread_control; + +#endif /* __RANDOM_HARVEST_H__ */ diff --git a/sys/dev/random/randomdev.c b/sys/dev/random/randomdev.c index 18b17d833d9..0f10e6c627f 100644 --- a/sys/dev/random/randomdev.c +++ b/sys/dev/random/randomdev.c @@ -1,4 +1,5 @@ /*- + * Copyright (c) 2013 Arthur Mesh * Copyright (c) 2000-2004 Mark R V Murray * All rights reserved. * @@ -50,6 +51,7 @@ __FBSDID("$FreeBSD$"); #include #include +#include #include #define RANDOM_MINOR 0 @@ -70,17 +72,12 @@ static struct cdevsw random_cdevsw = { .d_name = "random", }; -struct random_systat *random_systat; +static eventhandler_tag attach_tag; +static int random_inited; /* For use with make_dev(9)/destroy_dev(9). */ static struct cdev *random_dev; -/* Used to fake out unused random calls in random_systat */ -void -random_null_func(void) -{ -} - /* ARGSUSED */ static int random_close(struct cdev *dev __unused, int flags, int fmt __unused, @@ -88,8 +85,8 @@ random_close(struct cdev *dev __unused, int flags, int fmt __unused, { if ((flags & FWRITE) && (priv_check(td, PRIV_RANDOM_RESEED) == 0) && (securelevel_gt(td->td_ucred, 0) == 0)) { - (*random_systat->reseed)(); - random_systat->seeded = 1; + (*random_adaptor->reseed)(); + random_adaptor->seeded = 1; arc4rand(NULL, 0, 1); /* Reseed arc4random as well. */ } @@ -104,8 +101,8 @@ random_read(struct cdev *dev __unused, struct uio *uio, int flag) void *random_buf; /* Blocking logic */ - if (!random_systat->seeded) - error = (*random_systat->block)(flag); + if (!random_adaptor->seeded) + error = (*random_adaptor->block)(flag); /* The actual read */ if (!error) { @@ -114,7 +111,7 @@ random_read(struct cdev *dev __unused, struct uio *uio, int flag) while (uio->uio_resid > 0 && !error) { c = MIN(uio->uio_resid, PAGE_SIZE); - c = (*random_systat->read)(random_buf, c); + c = (*random_adaptor->read)(random_buf, c); error = uiomove(random_buf, c, uio); } @@ -139,7 +136,7 @@ random_write(struct cdev *dev __unused, struct uio *uio, int flag __unused) error = uiomove(random_buf, c, uio); if (error) break; - (*random_systat->write)(random_buf, c); + (*random_adaptor->write)(random_buf, c); } free(random_buf, M_TEMP); @@ -172,14 +169,37 @@ random_poll(struct cdev *dev __unused, int events, struct thread *td) int revents = 0; if (events & (POLLIN | POLLRDNORM)) { - if (random_systat->seeded) + if (random_adaptor->seeded) revents = events & (POLLIN | POLLRDNORM); else - revents = (*random_systat->poll) (events,td); + revents = (*random_adaptor->poll) (events,td); } return (revents); } +static void +random_initialize(void *p, struct random_adaptor *s) +{ + if (random_inited) { + printf("random: <%s> already initialized\n", + random_adaptor->ident); + return; + } + + random_adaptor = s; + + (s->init)(); + + printf("random: <%s> initialized\n", s->ident); + + random_dev = make_dev_credf(MAKEDEV_ETERNAL_KLD, &random_cdevsw, + RANDOM_MINOR, NULL, UID_ROOT, GID_WHEEL, 0666, "random"); + make_dev_alias(random_dev, "urandom"); /* XXX Deprecated */ + + /* mark random(4) as initialized, to avoid being called again */ + random_inited = 1; +} + /* ARGSUSED */ static int random_modevent(module_t mod __unused, int type, void *data __unused) @@ -188,23 +208,29 @@ random_modevent(module_t mod __unused, int type, void *data __unused) switch (type) { case MOD_LOAD: - random_ident_hardware(&random_systat); - (*random_systat->init)(); + random_adaptor_choose(&random_adaptor); - if (bootverbose) - printf("random: \n", - random_systat->ident); - - random_dev = make_dev_credf(MAKEDEV_ETERNAL_KLD, &random_cdevsw, - RANDOM_MINOR, NULL, UID_ROOT, GID_WHEEL, 0666, "random"); - make_dev_alias(random_dev, "urandom"); /* XXX Deprecated */ + if (random_adaptor == NULL) { + printf( + "random: No random adaptor attached, postponing initialization\n"); + attach_tag = EVENTHANDLER_REGISTER(random_adaptor_attach, + random_initialize, NULL, EVENTHANDLER_PRI_ANY); + } else { + random_initialize(NULL, random_adaptor); + } break; case MOD_UNLOAD: - (*random_systat->deinit)(); - - destroy_dev(random_dev); + if (random_adaptor != NULL) { + (*random_adaptor->deinit)(); + destroy_dev(random_dev); + } + /* Unregister the event handler */ + if (attach_tag != NULL) { + EVENTHANDLER_DEREGISTER(random_adaptor_attach, + attach_tag); + } break; diff --git a/sys/dev/random/randomdev.h b/sys/dev/random/randomdev.h index deb68319233..75b2c198090 100644 --- a/sys/dev/random/randomdev.h +++ b/sys/dev/random/randomdev.h @@ -38,7 +38,7 @@ typedef void random_write_func_t(void *, int); typedef int random_poll_func_t(int, struct thread *); typedef void random_reseed_func_t(void); -struct random_systat { +struct random_adaptor { struct selinfo rsel; const char *ident; int seeded; @@ -51,7 +51,5 @@ struct random_systat { random_reseed_func_t *reseed; }; -extern struct random_systat *random_systat; - -extern void random_ident_hardware(struct random_systat **); +extern void random_ident_hardware(struct random_adaptor **); extern void random_null_func(void); diff --git a/sys/dev/random/randomdev_soft.c b/sys/dev/random/randomdev_soft.c index ac482144133..61afb1b76a7 100644 --- a/sys/dev/random/randomdev_soft.c +++ b/sys/dev/random/randomdev_soft.c @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2000-2009 Mark R V Murray + * Copyright (c) 2000-2013 Mark R V Murray * Copyright (c) 2004 Robert N. M. Watson * All rights reserved. * @@ -26,21 +26,24 @@ * */ +#if !defined(YARROW_RNG) && !defined(FORTUNA_RNG) +#define YARROW_RNG +#elif defined(YARROW_RNG) && defined(FORTUNA_RNG) +#error "Must define either YARROW_RNG or FORTUNA_RNG" +#endif + #include __FBSDID("$FreeBSD$"); #include #include -#include -#include #include #include -#include #include #include +#include #include #include -#include #include #include #include @@ -50,60 +53,57 @@ __FBSDID("$FreeBSD$"); #include #include +#include #include #include +#if defined(YARROW_RNG) +#include +#endif +#if defined(FORTUNA_RNG) +#include +#endif -#define RANDOM_FIFO_MAX 256 /* How many events to queue up */ +#include "random_harvestq.h" -static void random_kthread(void *); -static void -random_harvest_internal(u_int64_t, const void *, u_int, - u_int, u_int, enum esource); -static int random_yarrow_poll(int event,struct thread *td); -static int random_yarrow_block(int flag); -static void random_yarrow_flush_reseed(void); +static int randomdev_poll(int event, struct thread *td); +static int randomdev_block(int flag); +static void randomdev_flush_reseed(void); -struct random_systat random_yarrow = { +#if defined(YARROW_RNG) +static struct random_adaptor random_context = { .ident = "Software, Yarrow", - .init = random_yarrow_init, - .deinit = random_yarrow_deinit, - .block = random_yarrow_block, + .init = randomdev_init, + .deinit = randomdev_deinit, + .block = randomdev_block, .read = random_yarrow_read, - .write = random_yarrow_write, - .poll = random_yarrow_poll, - .reseed = random_yarrow_flush_reseed, + .write = randomdev_write, + .poll = randomdev_poll, + .reseed = randomdev_flush_reseed, .seeded = 1, }; +#define RANDOM_MODULE_NAME yarrow +#define RANDOM_CSPRNG_NAME "yarrow" +#endif -MALLOC_DEFINE(M_ENTROPY, "entropy", "Entropy harvesting buffers"); - -/* - * The harvest mutex protects the consistency of the entropy fifos and - * empty fifo. - */ -struct mtx harvest_mtx; - -/* Lockable FIFO queue holding entropy buffers */ -struct entropyfifo { - int count; - STAILQ_HEAD(harvestlist, harvest) head; +#if defined(FORTUNA_RNG) +static struct random_adaptor random_context = { + .ident = "Software, Fortuna", + .init = randomdev_init, + .deinit = randomdev_deinit, + .block = randomdev_block, + .read = random_fortuna_read, + .write = randomdev_write, + .poll = randomdev_poll, + .reseed = randomdev_flush_reseed, + .seeded = 1, }; +#define RANDOM_MODULE_NAME fortuna +#define RANDOM_CSPRNG_NAME "fortuna" -/* Empty entropy buffers */ -static struct entropyfifo emptyfifo; - -#define EMPTYBUFFERS 1024 - -/* Harvested entropy */ -static struct entropyfifo harvestfifo[ENTROPYSOURCE]; - -/* <0 to end the kthread, 0 to let it run, 1 to flush the harvest queues */ -static int random_kthread_control = 0; - -static struct proc *random_kthread_proc; +#endif /* List for the dynamic sysctls */ -struct sysctl_ctx_list random_clist; +static struct sysctl_ctx_list random_clist; /* ARGSUSED */ static int @@ -114,31 +114,27 @@ random_check_boolean(SYSCTL_HANDLER_ARGS) return sysctl_handle_int(oidp, oidp->oid_arg1, oidp->oid_arg2, req); } -/* ARGSUSED */ void -random_yarrow_init(void) +randomdev_init(void) { - int error, i; - struct harvest *np; - struct sysctl_oid *random_o, *random_sys_o, *random_sys_harvest_o; - enum esource e; + struct sysctl_oid *random_sys_o, *random_sys_harvest_o; - random_o = SYSCTL_ADD_NODE(&random_clist, - SYSCTL_STATIC_CHILDREN(_kern), - OID_AUTO, "random", CTLFLAG_RW, 0, - "Software Random Number Generator"); - - random_yarrow_init_alg(&random_clist, random_o); +#if defined(YARROW_RNG) + random_yarrow_init_alg(&random_clist); +#endif +#if defined(FORTUNA_RNG) + random_fortuna_init_alg(&random_clist); +#endif random_sys_o = SYSCTL_ADD_NODE(&random_clist, - SYSCTL_CHILDREN(random_o), + SYSCTL_STATIC_CHILDREN(_kern_random), OID_AUTO, "sys", CTLFLAG_RW, 0, "Entropy Device Parameters"); SYSCTL_ADD_PROC(&random_clist, SYSCTL_CHILDREN(random_sys_o), OID_AUTO, "seeded", CTLTYPE_INT | CTLFLAG_RW, - &random_systat->seeded, 1, random_check_boolean, "I", + &random_context.seeded, 1, random_check_boolean, "I", "Seeded State"); random_sys_harvest_o = SYSCTL_ADD_NODE(&random_clist, @@ -159,7 +155,7 @@ random_yarrow_init(void) SYSCTL_ADD_PROC(&random_clist, SYSCTL_CHILDREN(random_sys_harvest_o), OID_AUTO, "interrupt", CTLTYPE_INT | CTLFLAG_RW, - &harvest.interrupt, 1, random_check_boolean, "I", + &harvest.interrupt, 0, random_check_boolean, "I", "Harvest IRQ entropy"); SYSCTL_ADD_PROC(&random_clist, SYSCTL_CHILDREN(random_sys_harvest_o), @@ -167,40 +163,18 @@ random_yarrow_init(void) &harvest.swi, 0, random_check_boolean, "I", "Harvest SWI entropy"); - /* Initialise the harvest fifos */ - STAILQ_INIT(&emptyfifo.head); - emptyfifo.count = 0; - for (i = 0; i < EMPTYBUFFERS; i++) { - np = malloc(sizeof(struct harvest), M_ENTROPY, M_WAITOK); - STAILQ_INSERT_TAIL(&emptyfifo.head, np, next); - } - for (e = RANDOM_START; e < ENTROPYSOURCE; e++) { - STAILQ_INIT(&harvestfifo[e].head); - harvestfifo[e].count = 0; - } - - mtx_init(&harvest_mtx, "entropy harvest mutex", NULL, MTX_SPIN); - - /* Start the hash/reseed thread */ - error = kproc_create(random_kthread, NULL, - &random_kthread_proc, RFHIGHPID, 0, "yarrow"); - if (error != 0) - panic("Cannot create entropy maintenance thread."); + random_harvestq_init(random_process_event); /* Register the randomness harvesting routine */ - random_yarrow_init_harvester(random_harvest_internal, - random_yarrow_read); + randomdev_init_harvester(random_harvestq_internal, + random_context.read); } -/* ARGSUSED */ void -random_yarrow_deinit(void) +randomdev_deinit(void) { - struct harvest *np; - enum esource e; - /* Deregister the randomness harvesting routine */ - random_yarrow_deinit_harvester(); + randomdev_deinit_harvester(); /* * Command the hash/reseed thread to end and wait for it to finish @@ -208,140 +182,18 @@ random_yarrow_deinit(void) random_kthread_control = -1; tsleep((void *)&random_kthread_control, 0, "term", 0); - /* Destroy the harvest fifos */ - while (!STAILQ_EMPTY(&emptyfifo.head)) { - np = STAILQ_FIRST(&emptyfifo.head); - STAILQ_REMOVE_HEAD(&emptyfifo.head, next); - free(np, M_ENTROPY); - } - for (e = RANDOM_START; e < ENTROPYSOURCE; e++) { - while (!STAILQ_EMPTY(&harvestfifo[e].head)) { - np = STAILQ_FIRST(&harvestfifo[e].head); - STAILQ_REMOVE_HEAD(&harvestfifo[e].head, next); - free(np, M_ENTROPY); - } - } - +#if defined(YARROW_RNG) random_yarrow_deinit_alg(); - - mtx_destroy(&harvest_mtx); +#endif +#if defined(FORTUNA_RNG) + random_fortuna_deinit_alg(); +#endif sysctl_ctx_free(&random_clist); } -/* ARGSUSED */ -static void -random_kthread(void *arg __unused) -{ - STAILQ_HEAD(, harvest) local_queue; - struct harvest *event = NULL; - int local_count; - enum esource source; - - STAILQ_INIT(&local_queue); - local_count = 0; - - /* Process until told to stop */ - mtx_lock_spin(&harvest_mtx); - for (; random_kthread_control >= 0;) { - - /* Cycle through all the entropy sources */ - for (source = RANDOM_START; source < ENTROPYSOURCE; source++) { - /* - * Drain entropy source records into a thread-local - * queue for processing while not holding the mutex. - */ - STAILQ_CONCAT(&local_queue, &harvestfifo[source].head); - local_count += harvestfifo[source].count; - harvestfifo[source].count = 0; - } - - /* - * Deal with events, if any, dropping the mutex as we process - * each event. Then push the events back into the empty - * fifo. - */ - if (!STAILQ_EMPTY(&local_queue)) { - mtx_unlock_spin(&harvest_mtx); - STAILQ_FOREACH(event, &local_queue, next) - random_process_event(event); - mtx_lock_spin(&harvest_mtx); - STAILQ_CONCAT(&emptyfifo.head, &local_queue); - emptyfifo.count += local_count; - local_count = 0; - } - - KASSERT(local_count == 0, ("random_kthread: local_count %d", - local_count)); - - /* - * If a queue flush was commanded, it has now happened, - * and we can mark this by resetting the command. - */ - if (random_kthread_control == 1) - random_kthread_control = 0; - - /* Work done, so don't belabour the issue */ - msleep_spin_sbt(&random_kthread_control, &harvest_mtx, - "-", SBT_1S / 10, 0, C_PREL(1)); - - } - mtx_unlock_spin(&harvest_mtx); - - random_set_wakeup_exit(&random_kthread_control); - /* NOTREACHED */ -} - -/* Entropy harvesting routine. This is supposed to be fast; do - * not do anything slow in here! - */ -static void -random_harvest_internal(u_int64_t somecounter, const void *entropy, - u_int count, u_int bits, u_int frac, enum esource origin) -{ - struct harvest *event; - - KASSERT(origin == RANDOM_START || origin == RANDOM_WRITE || - origin == RANDOM_KEYBOARD || origin == RANDOM_MOUSE || - origin == RANDOM_NET || origin == RANDOM_INTERRUPT || - origin == RANDOM_PURE, - ("random_harvest_internal: origin %d invalid\n", origin)); - - /* Lockless read to avoid lock operations if fifo is full. */ - if (harvestfifo[origin].count >= RANDOM_FIFO_MAX) - return; - - mtx_lock_spin(&harvest_mtx); - - /* - * Don't make the harvest queues too big - help to prevent low-grade - * entropy swamping - */ - if (harvestfifo[origin].count < RANDOM_FIFO_MAX) { - event = STAILQ_FIRST(&emptyfifo.head); - if (event != NULL) { - /* Add the harvested data to the fifo */ - STAILQ_REMOVE_HEAD(&emptyfifo.head, next); - harvestfifo[origin].count++; - event->somecounter = somecounter; - event->size = count; - event->bits = bits; - event->frac = frac; - event->source = origin; - - /* XXXX Come back and make this dynamic! */ - count = MIN(count, HARVESTSIZE); - memcpy(event->entropy, entropy, count); - - STAILQ_INSERT_TAIL(&harvestfifo[origin].head, - event, next); - } - } - mtx_unlock_spin(&harvest_mtx); -} - void -random_yarrow_write(void *buf, int count) +randomdev_write(void *buf, int count) { int i; u_int chunk; @@ -354,52 +206,52 @@ random_yarrow_write(void *buf, int count) chunk = HARVESTSIZE; if (i + chunk >= count) chunk = (u_int)(count - i); - random_harvest_internal(get_cyclecount(), (char *)buf + i, + random_harvestq_internal(get_cyclecount(), (char *)buf + i, chunk, 0, 0, RANDOM_WRITE); } } void -random_yarrow_unblock(void) +randomdev_unblock(void) { - if (!random_systat->seeded) { - random_systat->seeded = 1; - selwakeuppri(&random_systat->rsel, PUSER); - wakeup(random_systat); + if (!random_context.seeded) { + random_context.seeded = 1; + selwakeuppri(&random_context.rsel, PUSER); + wakeup(&random_context); } (void)atomic_cmpset_int(&arc4rand_iniseed_state, ARC4_ENTR_NONE, ARC4_ENTR_HAVE); } static int -random_yarrow_poll(int events, struct thread *td) +randomdev_poll(int events, struct thread *td) { int revents = 0; mtx_lock(&random_reseed_mtx); - if (random_systat->seeded) + if (random_context.seeded) revents = events & (POLLIN | POLLRDNORM); else - selrecord(td, &random_systat->rsel); + selrecord(td, &random_context.rsel); mtx_unlock(&random_reseed_mtx); return revents; } static int -random_yarrow_block(int flag) +randomdev_block(int flag) { int error = 0; mtx_lock(&random_reseed_mtx); /* Blocking logic */ - while (!random_systat->seeded && !error) { + while (!random_context.seeded && !error) { if (flag & O_NONBLOCK) error = EWOULDBLOCK; else { printf("Entropy device is blocking.\n"); - error = msleep(random_systat, + error = msleep(&random_context, &random_reseed_mtx, PUSER | PCATCH, "block", 0); } @@ -411,12 +263,45 @@ random_yarrow_block(int flag) /* Helper routine to perform explicit reseeds */ static void -random_yarrow_flush_reseed(void) +randomdev_flush_reseed(void) { /* Command a entropy queue flush and wait for it to finish */ random_kthread_control = 1; while (random_kthread_control) pause("-", hz / 10); +#if defined(YARROW_RNG) random_yarrow_reseed(); +#endif +#if defined(FORTUNA_RNG) + random_fortuna_reseed(); +#endif } + +static int +randomdev_modevent(module_t mod, int type, void *unused) +{ + + switch (type) { + case MOD_LOAD: + random_adaptor_register(RANDOM_CSPRNG_NAME, &random_context); + /* + * For statically built kernels that contain both device + * random and options PADLOCK_RNG/RDRAND_RNG/etc.., + * this event handler will do nothing, since the random + * driver-specific handlers are loaded after these HW + * consumers, and hence hasn't yet registered for this event. + * + * In case where both the random driver and RNG's are built + * as seperate modules, random.ko is loaded prior to *_rng.ko's + * (by dependency). This event handler is there to delay + * creation of /dev/{u,}random and attachment of this *_rng.ko. + */ + EVENTHANDLER_INVOKE(random_adaptor_attach, &random_context); + return (0); + } + + return (EINVAL); +} + +RANDOM_ADAPTOR_MODULE(RANDOM_MODULE_NAME, randomdev_modevent, 1); diff --git a/sys/dev/random/randomdev_soft.h b/sys/dev/random/randomdev_soft.h index 489d45a3c5f..c92a5a8e072 100644 --- a/sys/dev/random/randomdev_soft.h +++ b/sys/dev/random/randomdev_soft.h @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2000-2004 Mark R V Murray + * Copyright (c) 2000-2013 Mark R V Murray * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -35,9 +35,6 @@ * an enum in sys/random.h */ -/* Cryptographic block size in bits */ -#define BLOCKSIZE 256 - /* The ring size _MUST_ be a power of 2 */ #define HARVEST_RING_SIZE 1024 /* harvest ring buffer size */ #define HARVEST_RING_MASK (HARVEST_RING_SIZE - 1) @@ -51,34 +48,28 @@ MALLOC_DECLARE(M_ENTROPY); */ struct harvest { uintmax_t somecounter; /* fast counter for clock jitter */ - u_char entropy[HARVESTSIZE]; /* the harvested entropy */ + uint8_t entropy[HARVESTSIZE]; /* the harvested entropy */ u_int size, bits, frac; /* stats about the entropy */ enum esource source; /* stats about the entropy */ STAILQ_ENTRY(harvest) next; /* next item on the list */ }; -void random_yarrow_init(void); -void random_yarrow_deinit(void); +void randomdev_init(void); +void randomdev_deinit(void); -int random_yarrow_read(void *, int); -void random_yarrow_write(void *, int); +void randomdev_write(void *, int); -void random_yarrow_init_harvester(void (*)(u_int64_t, const void *, u_int, +void randomdev_init_harvester(void (*)(u_int64_t, const void *, u_int, u_int, u_int, enum esource), int (*)(void *, int)); -void random_yarrow_deinit_harvester(void); +void randomdev_deinit_harvester(void); void random_set_wakeup_exit(void *); void random_process_event(struct harvest *event); -void random_yarrow_reseed(void); -void random_yarrow_unblock(void); +void randomdev_unblock(void); -void random_yarrow_init_alg(struct sysctl_ctx_list *, struct sysctl_oid *); -void random_yarrow_deinit_alg(void); - -extern struct random_systat random_yarrow; extern struct mtx random_reseed_mtx; -/* If this was c++, this would be a template */ +/* If this was C++, the macro below would be a template */ #define RANDOM_CHECK_UINT(name, min, max) \ static int \ random_check_uint_##name(SYSCTL_HANDLER_ARGS) \ diff --git a/sys/dev/random/yarrow.c b/sys/dev/random/yarrow.c index 6f631bb1113..21913f924f6 100644 --- a/sys/dev/random/yarrow.c +++ b/sys/dev/random/yarrow.c @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2000-2004 Mark R V Murray + * Copyright (c) 2000-2013 Mark R V Murray * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -41,24 +41,72 @@ __FBSDID("$FreeBSD$"); #include #include +#include #include #include +#define TIMEBIN 16 /* max value for Pt/t */ + +#define FAST 0 +#define SLOW 1 + +/* This is the beastie that needs protecting. It contains all of the + * state that we are excited about. + * Exactly one is instantiated. + */ +static struct random_state { + union { + uint8_t byte[BLOCKSIZE]; + uint64_t qword[BLOCKSIZE/sizeof(uint64_t)]; + } counter; /* C */ + struct randomdev_key key; /* K */ + u_int gengateinterval; /* Pg */ + u_int bins; /* Pt/t */ + u_int outputblocks; /* count output blocks for gates */ + u_int slowoverthresh; /* slow pool overthreshhold reseed count */ + struct pool { + struct source { + u_int bits; /* estimated bits of entropy */ + u_int frac; /* fractional bits of entropy + (given as 1024/n) */ + } source[ENTROPYSOURCE]; + u_int thresh; /* pool reseed threshhold */ + struct randomdev_hash hash; /* accumulated entropy */ + } pool[2]; /* pool[0] is fast, pool[1] is slow */ + u_int which; /* toggle - sets the current insertion pool */ +} random_state; + RANDOM_CHECK_UINT(gengateinterval, 4, 64); RANDOM_CHECK_UINT(bins, 2, 16); -RANDOM_CHECK_UINT(fastthresh, BLOCKSIZE/4, BLOCKSIZE); -RANDOM_CHECK_UINT(slowthresh, BLOCKSIZE/4, BLOCKSIZE); +RANDOM_CHECK_UINT(fastthresh, (BLOCKSIZE*8)/4, (BLOCKSIZE*8)); /* Bit counts */ +RANDOM_CHECK_UINT(slowthresh, (BLOCKSIZE*8)/4, (BLOCKSIZE*8)); /* Bit counts */ RANDOM_CHECK_UINT(slowoverthresh, 1, 5); -/* Structure holding the entropy state */ -static struct random_state random_state; - static void generator_gate(void); static void reseed(u_int); /* The reseed thread mutex */ struct mtx random_reseed_mtx; +/* 128-bit C = 0 */ +/* Nothing to see here, folks, just an ugly mess. */ +static void +clear_counter(void) +{ + random_state.counter.qword[0] = 0UL; + random_state.counter.qword[1] = 0UL; +} + +/* 128-bit C = C + 1 */ +/* Nothing to see here, folks, just an ugly mess. */ +static void +increment_counter(void) +{ + random_state.counter.qword[0]++; + if (!random_state.counter.qword[0]) + random_state.counter.qword[1]++; +} + /* Process a single stochastic event off the harvest queue */ void random_process_event(struct harvest *event) @@ -70,13 +118,13 @@ random_process_event(struct harvest *event) /* Unpack the event into the appropriate source accumulator */ pl = random_state.which; source = &random_state.pool[pl].source[event->source]; - yarrow_hash_iterate(&random_state.pool[pl].hash, event->entropy, + randomdev_hash_iterate(&random_state.pool[pl].hash, event->entropy, sizeof(event->entropy)); - yarrow_hash_iterate(&random_state.pool[pl].hash, &event->somecounter, + randomdev_hash_iterate(&random_state.pool[pl].hash, &event->somecounter, sizeof(event->somecounter)); source->frac += event->frac; - source->bits += event->bits + source->frac/1024; - source->frac %= 1024; + source->bits += event->bits + (source->frac >> 12); /* bits + frac/0x1000 */ + source->frac &= 0xFFF; /* Keep the fractional bits */ /* Count the over-threshold sources in each pool */ for (pl = 0; pl < 2; pl++) { @@ -101,7 +149,7 @@ random_process_event(struct harvest *event) } void -random_yarrow_init_alg(struct sysctl_ctx_list *clist, struct sysctl_oid *in_o) +random_yarrow_init_alg(struct sysctl_ctx_list *clist) { int i; struct sysctl_oid *random_yarrow_o; @@ -110,7 +158,7 @@ random_yarrow_init_alg(struct sysctl_ctx_list *clist, struct sysctl_oid *in_o) * have a very good clue about what they do! */ random_yarrow_o = SYSCTL_ADD_NODE(clist, - SYSCTL_CHILDREN(in_o), + SYSCTL_STATIC_CHILDREN(_kern_random), OID_AUTO, "yarrow", CTLFLAG_RW, 0, "Yarrow Parameters"); @@ -131,14 +179,14 @@ random_yarrow_init_alg(struct sysctl_ctx_list *clist, struct sysctl_oid *in_o) SYSCTL_ADD_PROC(clist, SYSCTL_CHILDREN(random_yarrow_o), OID_AUTO, "fastthresh", CTLTYPE_INT|CTLFLAG_RW, - &random_state.pool[0].thresh, (3*BLOCKSIZE)/4, + &random_state.pool[0].thresh, (3*(BLOCKSIZE*8))/4, random_check_uint_fastthresh, "I", "Fast reseed threshold"); SYSCTL_ADD_PROC(clist, SYSCTL_CHILDREN(random_yarrow_o), OID_AUTO, "slowthresh", CTLTYPE_INT|CTLFLAG_RW, - &random_state.pool[1].thresh, BLOCKSIZE, + &random_state.pool[1].thresh, (BLOCKSIZE*8), random_check_uint_slowthresh, "I", "Slow reseed threshold"); @@ -151,21 +199,20 @@ random_yarrow_init_alg(struct sysctl_ctx_list *clist, struct sysctl_oid *in_o) random_state.gengateinterval = 10; random_state.bins = 10; - random_state.pool[0].thresh = (3*BLOCKSIZE)/4; - random_state.pool[1].thresh = BLOCKSIZE; + random_state.pool[0].thresh = (3*(BLOCKSIZE*8))/4; + random_state.pool[1].thresh = (BLOCKSIZE*8); random_state.slowoverthresh = 2; random_state.which = FAST; /* Initialise the fast and slow entropy pools */ for (i = 0; i < 2; i++) - yarrow_hash_init(&random_state.pool[i].hash); + randomdev_hash_init(&random_state.pool[i].hash); /* Clear the counter */ - for (i = 0; i < 4; i++) - random_state.counter[i] = 0; + clear_counter(); /* Set up a lock for the reseed process */ - mtx_init(&random_reseed_mtx, "random reseed", NULL, MTX_DEF); + mtx_init(&random_reseed_mtx, "Yarrow reseed", NULL, MTX_DEF); } void @@ -180,10 +227,10 @@ reseed(u_int fastslow) /* Interrupt-context stack is a limited resource; make large * structures static. */ - static u_char v[TIMEBIN][KEYSIZE]; /* v[i] */ - static struct yarrowhash context; - u_char hash[KEYSIZE]; /* h' */ - u_char temp[KEYSIZE]; + static uint8_t v[TIMEBIN][KEYSIZE]; /* v[i] */ + static struct randomdev_hash context; + uint8_t hash[KEYSIZE]; /* h' */ + uint8_t temp[KEYSIZE]; u_int i; enum esource j; @@ -192,15 +239,15 @@ reseed(u_int fastslow) /* 1. Hash the accumulated entropy into v[0] */ - yarrow_hash_init(&context); + randomdev_hash_init(&context); /* Feed the slow pool hash in if slow */ if (fastslow == SLOW) - yarrow_hash_iterate(&context, + randomdev_hash_iterate(&context, &random_state.pool[SLOW].hash, - sizeof(struct yarrowhash)); - yarrow_hash_iterate(&context, - &random_state.pool[FAST].hash, sizeof(struct yarrowhash)); - yarrow_hash_finish(&context, v[0]); + sizeof(struct randomdev_hash)); + randomdev_hash_iterate(&context, + &random_state.pool[FAST].hash, sizeof(struct randomdev_hash)); + randomdev_hash_finish(&context, v[0]); /* 2. Compute hash values for all v. _Supposed_ to be computationally * intensive. @@ -209,34 +256,33 @@ reseed(u_int fastslow) if (random_state.bins > TIMEBIN) random_state.bins = TIMEBIN; for (i = 1; i < random_state.bins; i++) { - yarrow_hash_init(&context); + randomdev_hash_init(&context); /* v[i] #= h(v[i - 1]) */ - yarrow_hash_iterate(&context, v[i - 1], KEYSIZE); + randomdev_hash_iterate(&context, v[i - 1], KEYSIZE); /* v[i] #= h(v[0]) */ - yarrow_hash_iterate(&context, v[0], KEYSIZE); + randomdev_hash_iterate(&context, v[0], KEYSIZE); /* v[i] #= h(i) */ - yarrow_hash_iterate(&context, &i, sizeof(u_int)); + randomdev_hash_iterate(&context, &i, sizeof(u_int)); /* Return the hashval */ - yarrow_hash_finish(&context, v[i]); + randomdev_hash_finish(&context, v[i]); } /* 3. Compute a new key; h' is the identity function here; * it is not being ignored! */ - yarrow_hash_init(&context); - yarrow_hash_iterate(&context, &random_state.key, KEYSIZE); + randomdev_hash_init(&context); + randomdev_hash_iterate(&context, &random_state.key, KEYSIZE); for (i = 1; i < random_state.bins; i++) - yarrow_hash_iterate(&context, &v[i], KEYSIZE); - yarrow_hash_finish(&context, temp); - yarrow_encrypt_init(&random_state.key, temp); + randomdev_hash_iterate(&context, &v[i], KEYSIZE); + randomdev_hash_finish(&context, temp); + randomdev_encrypt_init(&random_state.key, temp); /* 4. Recompute the counter */ - for (i = 0; i < 4; i++) - random_state.counter[i] = 0; - yarrow_encrypt(&random_state.key, random_state.counter, temp); - memcpy(random_state.counter, temp, sizeof(random_state.counter)); + clear_counter(); + randomdev_encrypt(&random_state.key, random_state.counter.byte, temp, BLOCKSIZE); + memcpy(random_state.counter.byte, temp, BLOCKSIZE); /* 5. Reset entropy estimate accumulators to zero */ @@ -257,7 +303,7 @@ reseed(u_int fastslow) /* XXX Not done here yet */ /* Unblock the device if it was blocked due to being unseeded */ - random_yarrow_unblock(); + randomdev_unblock(); /* Release the reseed mutex */ mtx_unlock(&random_reseed_mtx); @@ -269,7 +315,7 @@ random_yarrow_read(void *buf, int count) { static int cur = 0; static int gate = 1; - static u_char genval[KEYSIZE]; + static uint8_t genval[KEYSIZE]; size_t tomove; int i; int retval; @@ -282,16 +328,14 @@ random_yarrow_read(void *buf, int count) random_state.outputblocks = 0; gate = 0; } - if (count > 0 && (size_t)count >= sizeof(random_state.counter)) { + if (count > 0 && (size_t)count >= BLOCKSIZE) { retval = 0; - for (i = 0; i < count; i += (int)sizeof(random_state.counter)) { - random_state.counter[0]++; - yarrow_encrypt(&random_state.key, random_state.counter, - genval); - tomove = min(count - i, sizeof(random_state.counter)); + for (i = 0; i < count; i += BLOCKSIZE) { + increment_counter(); + randomdev_encrypt(&random_state.key, random_state.counter.byte, genval, BLOCKSIZE); + tomove = MIN(count - i, BLOCKSIZE); memcpy((char *)buf + i, genval, tomove); - if (++random_state.outputblocks >= - random_state.gengateinterval) { + if (++random_state.outputblocks >= random_state.gengateinterval) { generator_gate(); random_state.outputblocks = 0; } @@ -301,13 +345,11 @@ random_yarrow_read(void *buf, int count) } else { if (!cur) { - random_state.counter[0]++; - yarrow_encrypt(&random_state.key, random_state.counter, - genval); + increment_counter(); + randomdev_encrypt(&random_state.key, random_state.counter.byte, genval, BLOCKSIZE); memcpy(buf, genval, (size_t)count); - cur = (int)sizeof(random_state.counter) - count; - if (++random_state.outputblocks >= - random_state.gengateinterval) { + cur = BLOCKSIZE - count; + if (++random_state.outputblocks >= random_state.gengateinterval) { generator_gate(); random_state.outputblocks = 0; } @@ -315,9 +357,7 @@ random_yarrow_read(void *buf, int count) } else { retval = MIN(cur, count); - memcpy(buf, - &genval[(int)sizeof(random_state.counter) - cur], - (size_t)retval); + memcpy(buf, &genval[BLOCKSIZE - cur], (size_t)retval); cur -= retval; } } @@ -329,17 +369,15 @@ static void generator_gate(void) { u_int i; - u_char temp[KEYSIZE]; + uint8_t temp[KEYSIZE]; - for (i = 0; i < KEYSIZE; i += sizeof(random_state.counter)) { - random_state.counter[0]++; - yarrow_encrypt(&random_state.key, random_state.counter, - &(temp[i])); + for (i = 0; i < KEYSIZE; i += BLOCKSIZE) { + increment_counter(); + randomdev_encrypt(&random_state.key, random_state.counter.byte, temp + i, BLOCKSIZE); } - yarrow_encrypt_init(&random_state.key, temp); + randomdev_encrypt_init(&random_state.key, temp); memset((void *)temp, 0, KEYSIZE); - } /* Helper routine to perform explicit reseeds */ diff --git a/sys/dev/random/yarrow.h b/sys/dev/random/yarrow.h index 558354d27f7..0bde5b582d3 100644 --- a/sys/dev/random/yarrow.h +++ b/sys/dev/random/yarrow.h @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2000-2004 Mark R V Murray + * Copyright (c) 2000-2013 Mark R V Murray * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -26,34 +26,7 @@ * $FreeBSD$ */ -/* This contains Yarrow-specific declarations. - * See http://www.counterpane.com/yarrow.html - */ - -#define TIMEBIN 16 /* max value for Pt/t */ - -#define FAST 0 -#define SLOW 1 - -/* This is the beastie that needs protecting. It contains all of the - * state that we are excited about. - * Exactly one will be instantiated. - */ -struct random_state { - u_int64_t counter[4]; /* C - 256 bits */ - struct yarrowkey key; /* K */ - u_int gengateinterval; /* Pg */ - u_int bins; /* Pt/t */ - u_int outputblocks; /* count output blocks for gates */ - u_int slowoverthresh; /* slow pool overthreshhold reseed count */ - struct pool { - struct source { - u_int bits; /* estimated bits of entropy */ - u_int frac; /* fractional bits of entropy - (given as 1024/n) */ - } source[ENTROPYSOURCE]; - u_int thresh; /* pool reseed threshhold */ - struct yarrowhash hash; /* accumulated entropy */ - } pool[2]; /* pool[0] is fast, pool[1] is slow */ - u_int which; /* toggle - sets the current insertion pool */ -}; +void random_yarrow_init_alg(struct sysctl_ctx_list *); +void random_yarrow_deinit_alg(void); +int random_yarrow_read(void *, int); +void random_yarrow_reseed(void); diff --git a/sys/dev/rndtest/rndtest.c b/sys/dev/rndtest/rndtest.c index 13ecdd6b010..3c42972c36f 100644 --- a/sys/dev/rndtest/rndtest.c +++ b/sys/dev/rndtest/rndtest.c @@ -152,7 +152,7 @@ rndtest_harvest(struct rndtest_state *rsp, void *buf, u_int len) for (len /= sizeof (u_int32_t); len; len--) add_true_randomness(*p++); #else - random_harvest(buf, len, len*NBBY, 0, RANDOM_PURE); + random_harvest(buf, len, len*NBBY/2, 0, RANDOM_PURE); #endif } } diff --git a/sys/dev/rp/rp_pci.c b/sys/dev/rp/rp_pci.c index cbd55167801..e7b98325bd0 100644 --- a/sys/dev/rp/rp_pci.c +++ b/sys/dev/rp/rp_pci.c @@ -151,7 +151,6 @@ rp_pciattach(device_t dev) CONTROLLER_t *ctlp; int unit; int retval; - u_int32_t stcmd; ctlp = device_get_softc(dev); bzero(ctlp, sizeof(*ctlp)); @@ -161,13 +160,6 @@ rp_pciattach(device_t dev) ctlp->aiop2off = rp_pci_aiop2off; ctlp->ctlmask = rp_pci_ctlmask; - /* Wake up the device. */ - stcmd = pci_read_config(dev, PCIR_COMMAND, 4); - if ((stcmd & PCIM_CMD_PORTEN) == 0) { - stcmd |= (PCIM_CMD_PORTEN); - pci_write_config(dev, PCIR_COMMAND, 4, stcmd); - } - /* The IO ports of AIOPs for a PCI controller are continuous. */ ctlp->io_num = 1; ctlp->io_rid = malloc(sizeof(*(ctlp->io_rid)) * ctlp->io_num, M_DEVBUF, M_NOWAIT | M_ZERO); diff --git a/sys/dev/safe/safe.c b/sys/dev/safe/safe.c index 396a93ccd9d..721b9f4b2b6 100644 --- a/sys/dev/safe/safe.c +++ b/sys/dev/safe/safe.c @@ -211,7 +211,7 @@ safe_partname(struct safe_softc *sc) static void default_harvest(struct rndtest_state *rsp, void *buf, u_int count) { - random_harvest(buf, count, count*NBBY, 0, RANDOM_PURE); + random_harvest(buf, count, count*NBBY/2, 0, RANDOM_PURE); } #endif /* SAFE_NO_RNG */ @@ -220,28 +220,15 @@ safe_attach(device_t dev) { struct safe_softc *sc = device_get_softc(dev); u_int32_t raddr; - u_int32_t cmd, i, devinfo; + u_int32_t i, devinfo; int rid; bzero(sc, sizeof (*sc)); sc->sc_dev = dev; /* XXX handle power management */ - - cmd = pci_read_config(dev, PCIR_COMMAND, 4); - cmd |= PCIM_CMD_MEMEN | PCIM_CMD_BUSMASTEREN; - pci_write_config(dev, PCIR_COMMAND, cmd, 4); - cmd = pci_read_config(dev, PCIR_COMMAND, 4); - if (!(cmd & PCIM_CMD_MEMEN)) { - device_printf(dev, "failed to enable memory mapping\n"); - goto bad; - } - - if (!(cmd & PCIM_CMD_BUSMASTEREN)) { - device_printf(dev, "failed to enable bus mastering\n"); - goto bad; - } + pci_enable_busmaster(dev); /* * Setup memory-mapping of PCI registers. diff --git a/sys/dev/sdhci/sdhci.c b/sys/dev/sdhci/sdhci.c index 15199f01956..c9c3ae90e96 100644 --- a/sys/dev/sdhci/sdhci.c +++ b/sys/dev/sdhci/sdhci.c @@ -238,6 +238,11 @@ sdhci_set_clock(struct sdhci_slot *slot, uint32_t clock) /* If no clock requested - left it so. */ if (clock == 0) return; + + /* Recalculate timeout clock frequency based on the new sd clock. */ + if (slot->quirks & SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK) + slot->timeout_clk = slot->clock / 1000; + if (slot->version < SDHCI_SPEC_300) { /* Looking for highest freq <= clock. */ res = slot->max_clk; @@ -345,7 +350,7 @@ sdhci_read_block_pio(struct sdhci_slot *slot) /* If we are too fast, broken controllers return zeroes. */ if (slot->quirks & SDHCI_QUIRK_BROKEN_TIMINGS) DELAY(10); - /* Handle unalligned and alligned buffer cases. */ + /* Handle unaligned and aligned buffer cases. */ if ((intptr_t)buffer & 3) { while (left > 3) { data = RD4(slot, SDHCI_BUFFER); @@ -385,7 +390,7 @@ sdhci_write_block_pio(struct sdhci_slot *slot) left = min(512, slot->curcmd->data->len - slot->offset); slot->offset += left; - /* Handle unalligned and alligned buffer cases. */ + /* Handle unaligned and aligned buffer cases. */ if ((intptr_t)buffer & 3) { while (left > 3) { data = buffer[0] + @@ -472,7 +477,7 @@ sdhci_card_task(void *arg, int pending) int sdhci_init_slot(device_t dev, struct sdhci_slot *slot, int num) { - uint32_t caps; + uint32_t caps, freq; int err; SDHCI_LOCK_INIT(slot); @@ -522,17 +527,23 @@ sdhci_init_slot(device_t dev, struct sdhci_slot *slot, int num) caps = RD4(slot, SDHCI_CAPABILITIES); /* Calculate base clock frequency. */ if (slot->version >= SDHCI_SPEC_300) - slot->max_clk = (caps & SDHCI_CLOCK_V3_BASE_MASK) - >> SDHCI_CLOCK_BASE_SHIFT; + freq = (caps & SDHCI_CLOCK_V3_BASE_MASK) >> + SDHCI_CLOCK_BASE_SHIFT; else - slot->max_clk = (caps & SDHCI_CLOCK_BASE_MASK) - >> SDHCI_CLOCK_BASE_SHIFT; + freq = (caps & SDHCI_CLOCK_BASE_MASK) >> + SDHCI_CLOCK_BASE_SHIFT; + if (freq != 0) + slot->max_clk = freq * 1000000; + /* + * If the frequency wasn't in the capabilities and the hardware driver + * hasn't already set max_clk we're probably not going to work right + * with an assumption, so complain about it. + */ if (slot->max_clk == 0) { - slot->max_clk = SDHCI_DEFAULT_MAX_FREQ; + slot->max_clk = SDHCI_DEFAULT_MAX_FREQ * 1000000; device_printf(dev, "Hardware doesn't specify base clock " "frequency, using %dMHz as default.\n", SDHCI_DEFAULT_MAX_FREQ); } - slot->max_clk *= 1000000; /* Calculate timeout clock frequency. */ if (slot->quirks & SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK) { slot->timeout_clk = slot->max_clk / 1000; @@ -542,10 +553,15 @@ sdhci_init_slot(device_t dev, struct sdhci_slot *slot, int num) if (caps & SDHCI_TIMEOUT_CLK_UNIT) slot->timeout_clk *= 1000; } - + /* + * If the frequency wasn't in the capabilities and the hardware driver + * hasn't already set timeout_clk we'll probably work okay using the + * max timeout, but still mention it. + */ if (slot->timeout_clk == 0) { device_printf(dev, "Hardware doesn't specify timeout clock " - "frequency.\n"); + "frequency, setting BROKEN_TIMEOUT quirk.\n"); + slot->quirks |= SDHCI_QUIRK_BROKEN_TIMEOUT_VAL; } slot->host.f_min = SDHCI_MIN_FREQ(slot->bus, slot); @@ -829,8 +845,13 @@ sdhci_finish_command(struct sdhci_slot *slot) uint8_t extra = 0; for (i = 0; i < 4; i++) { uint32_t val = RD4(slot, SDHCI_RESPONSE + i * 4); - slot->curcmd->resp[3 - i] = (val << 8) + extra; - extra = val >> 24; + if (slot->quirks & SDHCI_QUIRK_DONT_SHIFT_RESPONSE) + slot->curcmd->resp[3 - i] = val; + else { + slot->curcmd->resp[3 - i] = + (val << 8) | extra; + extra = val >> 24; + } } } else slot->curcmd->resp[0] = RD4(slot, SDHCI_RESPONSE); @@ -855,24 +876,22 @@ sdhci_start_data(struct sdhci_slot *slot, struct mmc_data *data) /* Calculate and set data timeout.*/ /* XXX: We should have this from mmc layer, now assume 1 sec. */ - target_timeout = 1000000; - div = 0; - current_timeout = (1 << 13) * 1000 / slot->timeout_clk; - while (current_timeout < target_timeout) { - div++; - current_timeout <<= 1; - if (div >= 0xF) - break; - } - /* Compensate for an off-by-one error in the CaFe chip.*/ - if (slot->quirks & SDHCI_QUIRK_INCR_TIMEOUT_CONTROL) - div++; - if (div >= 0xF) { - slot_printf(slot, "Timeout too large!\n"); + if (slot->quirks & SDHCI_QUIRK_BROKEN_TIMEOUT_VAL) { div = 0xE; + } else { + target_timeout = 1000000; + div = 0; + current_timeout = (1 << 13) * 1000 / slot->timeout_clk; + while (current_timeout < target_timeout && div < 0xE) { + ++div; + current_timeout <<= 1; + } + /* Compensate for an off-by-one error in the CaFe chip.*/ + if (div < 0xE && + (slot->quirks & SDHCI_QUIRK_INCR_TIMEOUT_CONTROL)) { + ++div; + } } - if (slot->quirks & SDHCI_QUIRK_BROKEN_TIMEOUT_VAL) - div = 0xE; WR1(slot, SDHCI_TIMEOUT_CONTROL, div); if (data == NULL) @@ -892,11 +911,14 @@ sdhci_start_data(struct sdhci_slot *slot, struct mmc_data *data) /* Load DMA buffer. */ if (slot->flags & SDHCI_USE_DMA) { if (data->flags & MMC_DATA_READ) - bus_dmamap_sync(slot->dmatag, slot->dmamap, BUS_DMASYNC_PREREAD); + bus_dmamap_sync(slot->dmatag, slot->dmamap, + BUS_DMASYNC_PREREAD); else { memcpy(slot->dmamem, data->data, - (data->len < DMA_BLOCK_SIZE)?data->len:DMA_BLOCK_SIZE); - bus_dmamap_sync(slot->dmatag, slot->dmamap, BUS_DMASYNC_PREWRITE); + (data->len < DMA_BLOCK_SIZE) ? + data->len : DMA_BLOCK_SIZE); + bus_dmamap_sync(slot->dmatag, slot->dmamap, + BUS_DMASYNC_PREWRITE); } WR4(slot, SDHCI_DMA_ADDRESS, slot->paddr); /* Interrupt aggregation: Mask border interrupt @@ -923,7 +945,7 @@ sdhci_finish_data(struct sdhci_slot *slot) slot->data_done = 1; /* Interrupt aggregation: Restore command interrupt. - * Auxillary restore point for the case when data interrupt + * Auxiliary restore point for the case when data interrupt * happened first. */ if (!slot->cmd_done) { WR4(slot, SDHCI_SIGNAL_ENABLE, @@ -933,11 +955,13 @@ sdhci_finish_data(struct sdhci_slot *slot) if (slot->flags & SDHCI_USE_DMA) { if (data->flags & MMC_DATA_READ) { size_t left = data->len - slot->offset; - bus_dmamap_sync(slot->dmatag, slot->dmamap, BUS_DMASYNC_POSTREAD); + bus_dmamap_sync(slot->dmatag, slot->dmamap, + BUS_DMASYNC_POSTREAD); memcpy((u_char*)data->data + slot->offset, slot->dmamem, (left < DMA_BLOCK_SIZE)?left:DMA_BLOCK_SIZE); } else - bus_dmamap_sync(slot->dmatag, slot->dmamap, BUS_DMASYNC_POSTWRITE); + bus_dmamap_sync(slot->dmatag, slot->dmamap, + BUS_DMASYNC_POSTWRITE); } /* If there was error - reset the host. */ if (slot->curcmd->error) { diff --git a/sys/dev/sdhci/sdhci.h b/sys/dev/sdhci/sdhci.h index 5c467786ece..d13640933f4 100644 --- a/sys/dev/sdhci/sdhci.h +++ b/sys/dev/sdhci/sdhci.h @@ -57,6 +57,8 @@ #define SDHCI_QUIRK_BROKEN_TIMEOUT_VAL (1<<11) /* SDHCI_CAPABILITIES is invalid */ #define SDHCI_QUIRK_MISSING_CAPS (1<<12) +/* Hardware shifts the 136-bit response, don't do it in software. */ +#define SDHCI_QUIRK_DONT_SHIFT_RESPONSE (1<<13) /* * Controller registers @@ -120,6 +122,8 @@ #define SDHCI_CTRL_SDMA 0x08 #define SDHCI_CTRL_ADMA2 0x10 #define SDHCI_CTRL_ADMA264 0x18 +#define SDHCI_CTRL_DMA_MASK 0x18 +#define SDHCI_CTRL_8BITBUS 0x20 #define SDHCI_CTRL_CARD_DET 0x40 #define SDHCI_CTRL_FORCE_CARD 0x80 @@ -195,6 +199,7 @@ #define SDHCI_CLOCK_BASE_SHIFT 8 #define SDHCI_MAX_BLOCK_MASK 0x00030000 #define SDHCI_MAX_BLOCK_SHIFT 16 +#define SDHCI_CAN_DO_8BITBUS 0x00040000 #define SDHCI_CAN_DO_ADMA2 0x00080000 #define SDHCI_CAN_DO_HISPD 0x00200000 #define SDHCI_CAN_DO_DMA 0x00400000 diff --git a/sys/dev/sfxge/sfxge_rx.c b/sys/dev/sfxge/sfxge_rx.c index 2aa8a3e27d8..7209c382ede 100644 --- a/sys/dev/sfxge/sfxge_rx.c +++ b/sys/dev/sfxge/sfxge_rx.c @@ -282,7 +282,6 @@ static void __sfxge_rx_deliver(struct sfxge_softc *sc, struct mbuf *m) struct ifnet *ifp = sc->ifnet; m->m_pkthdr.rcvif = ifp; - m->m_pkthdr.header = m->m_data; m->m_pkthdr.csum_data = 0xffff; ifp->if_input(ifp, m); } diff --git a/sys/dev/sound/pci/als4000.c b/sys/dev/sound/pci/als4000.c index bde95e7ccbc..0cd51e3457b 100644 --- a/sys/dev/sound/pci/als4000.c +++ b/sys/dev/sound/pci/als4000.c @@ -806,16 +806,13 @@ static int als_pci_attach(device_t dev) { struct sc_info *sc; - u_int32_t data; char status[SND_STATUSLEN]; sc = malloc(sizeof(*sc), M_DEVBUF, M_WAITOK | M_ZERO); sc->lock = snd_mtxcreate(device_get_nameunit(dev), "snd_als4000 softc"); sc->dev = dev; - data = pci_read_config(dev, PCIR_COMMAND, 2); - data |= (PCIM_CMD_PORTEN | PCIM_CMD_MEMEN | PCIM_CMD_BUSMASTEREN); - pci_write_config(dev, PCIR_COMMAND, data, 2); + pci_enable_busmaster(dev); /* * By default the power to the various components on the * ALS4000 is entirely controlled by the pci powerstate. We diff --git a/sys/dev/sound/pci/aureal.c b/sys/dev/sound/pci/aureal.c index 37e1c458472..67af07570f7 100644 --- a/sys/dev/sound/pci/aureal.c +++ b/sys/dev/sound/pci/aureal.c @@ -550,7 +550,6 @@ au_pci_probe(device_t dev) static int au_pci_attach(device_t dev) { - u_int32_t data; struct au_info *au; int type[10]; int regid[10]; @@ -565,10 +564,7 @@ au_pci_attach(device_t dev) au = malloc(sizeof(*au), M_DEVBUF, M_WAITOK | M_ZERO); au->unit = device_get_unit(dev); - data = pci_read_config(dev, PCIR_COMMAND, 2); - data |= (PCIM_CMD_PORTEN|PCIM_CMD_MEMEN|PCIM_CMD_BUSMASTEREN); - pci_write_config(dev, PCIR_COMMAND, data, 2); - data = pci_read_config(dev, PCIR_COMMAND, 2); + pci_enable_busmaster(dev); j=0; /* XXX dfr: is this strictly necessary? */ diff --git a/sys/dev/sound/pci/cmi.c b/sys/dev/sound/pci/cmi.c index 2b747e15503..6075a923ebe 100644 --- a/sys/dev/sound/pci/cmi.c +++ b/sys/dev/sound/pci/cmi.c @@ -935,15 +935,11 @@ static int cmi_attach(device_t dev) { struct sc_info *sc; - u_int32_t data; char status[SND_STATUSLEN]; sc = malloc(sizeof(*sc), M_DEVBUF, M_WAITOK | M_ZERO); sc->lock = snd_mtxcreate(device_get_nameunit(dev), "snd_cmi softc"); - data = pci_read_config(dev, PCIR_COMMAND, 2); - data |= (PCIM_CMD_PORTEN|PCIM_CMD_BUSMASTEREN); - pci_write_config(dev, PCIR_COMMAND, data, 2); - data = pci_read_config(dev, PCIR_COMMAND, 2); + pci_enable_busmaster(dev); sc->dev = dev; sc->regid = PCIR_BAR(0); diff --git a/sys/dev/sound/pci/cs4281.c b/sys/dev/sound/pci/cs4281.c index e533eeefe70..6e1b17d9818 100644 --- a/sys/dev/sound/pci/cs4281.c +++ b/sys/dev/sound/pci/cs4281.c @@ -760,16 +760,13 @@ cs4281_pci_attach(device_t dev) { struct sc_info *sc; struct ac97_info *codec = NULL; - u_int32_t data; char status[SND_STATUSLEN]; sc = malloc(sizeof(*sc), M_DEVBUF, M_WAITOK | M_ZERO); sc->dev = dev; sc->type = pci_get_devid(dev); - data = pci_read_config(dev, PCIR_COMMAND, 2); - data |= (PCIM_CMD_PORTEN | PCIM_CMD_MEMEN | PCIM_CMD_BUSMASTEREN); - pci_write_config(dev, PCIR_COMMAND, data, 2); + pci_enable_busmaster(dev); #if __FreeBSD_version > 500000 if (pci_get_powerstate(dev) != PCI_POWERSTATE_D0) { diff --git a/sys/dev/sound/pci/csa.c b/sys/dev/sound/pci/csa.c index 7fa0b1369cc..4a766e78853 100644 --- a/sys/dev/sound/pci/csa.c +++ b/sys/dev/sound/pci/csa.c @@ -242,7 +242,6 @@ csa_probe(device_t dev) static int csa_attach(device_t dev) { - u_int32_t stcmd; sc_p scp; csa_res *resp; struct sndcard_func *func; @@ -254,12 +253,7 @@ csa_attach(device_t dev) bzero(scp, sizeof(*scp)); scp->dev = dev; - /* Wake up the device. */ - stcmd = pci_read_config(dev, PCIR_COMMAND, 2); - if ((stcmd & PCIM_CMD_MEMEN) == 0 || (stcmd & PCIM_CMD_BUSMASTEREN) == 0) { - stcmd |= (PCIM_CMD_MEMEN | PCIM_CMD_BUSMASTEREN); - pci_write_config(dev, PCIR_COMMAND, stcmd, 2); - } + pci_enable_busmaster(dev); /* Allocate the resources. */ resp = &scp->res; diff --git a/sys/dev/sound/pci/ds1.c b/sys/dev/sound/pci/ds1.c index 16626b50e63..302271a2eb2 100644 --- a/sys/dev/sound/pci/ds1.c +++ b/sys/dev/sound/pci/ds1.c @@ -941,7 +941,6 @@ ds_pci_probe(device_t dev) static int ds_pci_attach(device_t dev) { - u_int32_t data; u_int32_t subdev, i; struct sc_info *sc; struct ac97_info *codec = NULL; @@ -954,10 +953,7 @@ ds_pci_attach(device_t dev) sc->type = ds_finddev(pci_get_devid(dev), subdev); sc->rev = pci_get_revid(dev); - data = pci_read_config(dev, PCIR_COMMAND, 2); - data |= (PCIM_CMD_PORTEN|PCIM_CMD_MEMEN|PCIM_CMD_BUSMASTEREN); - pci_write_config(dev, PCIR_COMMAND, data, 2); - data = pci_read_config(dev, PCIR_COMMAND, 2); + pci_enable_busmaster(dev); sc->regid = PCIR_BAR(0); sc->reg = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->regid, diff --git a/sys/dev/sound/pci/emu10k1.c b/sys/dev/sound/pci/emu10k1.c index 0aaeb4b374e..026c721bee1 100644 --- a/sys/dev/sound/pci/emu10k1.c +++ b/sys/dev/sound/pci/emu10k1.c @@ -2067,7 +2067,6 @@ emu_pci_attach(device_t dev) { struct ac97_info *codec = NULL; struct sc_info *sc; - u_int32_t data; int i, gotmic; char status[SND_STATUSLEN]; @@ -2081,10 +2080,7 @@ emu_pci_attach(device_t dev) sc->nchans = sc->audigy ? 8 : 4; sc->addrmask = sc->audigy ? EMU_A_PTR_ADDR_MASK : EMU_PTR_ADDR_MASK; - data = pci_read_config(dev, PCIR_COMMAND, 2); - data |= (PCIM_CMD_PORTEN | PCIM_CMD_BUSMASTEREN); - pci_write_config(dev, PCIR_COMMAND, data, 2); - data = pci_read_config(dev, PCIR_COMMAND, 2); + pci_enable_busmaster(dev); i = PCIR_BAR(0); sc->reg = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &i, RF_ACTIVE); diff --git a/sys/dev/sound/pci/emu10kx.c b/sys/dev/sound/pci/emu10kx.c index 4503c05581d..cca4997a29f 100644 --- a/sys/dev/sound/pci/emu10kx.c +++ b/sys/dev/sound/pci/emu10kx.c @@ -3040,7 +3040,6 @@ emu_pci_attach(device_t dev) #if 0 struct emu_midiinfo *midiinfo; #endif - uint32_t data; int i; int device_flags; char status[255]; @@ -3182,11 +3181,6 @@ emu_pci_attach(device_t dev) if (sc->opcode_shift == 0) goto bad; - data = pci_read_config(dev, PCIR_COMMAND, 2); - data |= (PCIM_CMD_PORTEN | PCIM_CMD_BUSMASTEREN); - pci_write_config(dev, PCIR_COMMAND, data, 2); - data = pci_read_config(dev, PCIR_COMMAND, 2); - pci_enable_busmaster(dev); i = PCIR_BAR(0); diff --git a/sys/dev/sound/pci/envy24.c b/sys/dev/sound/pci/envy24.c index 1c59765e723..6c23e28be83 100644 --- a/sys/dev/sound/pci/envy24.c +++ b/sys/dev/sound/pci/envy24.c @@ -2547,7 +2547,6 @@ envy24_alloc_resource(struct sc_info *sc) static int envy24_pci_attach(device_t dev) { - u_int32_t data; struct sc_info *sc; char status[SND_STATUSLEN]; int err = 0; @@ -2567,10 +2566,7 @@ envy24_pci_attach(device_t dev) sc->dev = dev; /* initialize PCI interface */ - data = pci_read_config(dev, PCIR_COMMAND, 2); - data |= (PCIM_CMD_PORTEN | PCIM_CMD_BUSMASTEREN); - pci_write_config(dev, PCIR_COMMAND, data, 2); - data = pci_read_config(dev, PCIR_COMMAND, 2); + pci_enable_busmaster(dev); /* allocate resources */ err = envy24_alloc_resource(sc); diff --git a/sys/dev/sound/pci/envy24ht.c b/sys/dev/sound/pci/envy24ht.c index b0c138daf22..7372e8a69ba 100644 --- a/sys/dev/sound/pci/envy24ht.c +++ b/sys/dev/sound/pci/envy24ht.c @@ -2450,7 +2450,6 @@ envy24ht_alloc_resource(struct sc_info *sc) static int envy24ht_pci_attach(device_t dev) { - u_int32_t data; struct sc_info *sc; char status[SND_STATUSLEN]; int err = 0; @@ -2471,10 +2470,7 @@ envy24ht_pci_attach(device_t dev) sc->dev = dev; /* initialize PCI interface */ - data = pci_read_config(dev, PCIR_COMMAND, 2); - data |= (PCIM_CMD_PORTEN | PCIM_CMD_BUSMASTEREN); - pci_write_config(dev, PCIR_COMMAND, data, 2); - data = pci_read_config(dev, PCIR_COMMAND, 2); + pci_enable_busmaster(dev); /* allocate resources */ err = envy24ht_alloc_resource(sc); diff --git a/sys/dev/sound/pci/es137x.c b/sys/dev/sound/pci/es137x.c index f4e24af6dfc..42d0450d434 100644 --- a/sys/dev/sound/pci/es137x.c +++ b/sys/dev/sound/pci/es137x.c @@ -1704,7 +1704,6 @@ es_init_sysctls(device_t dev) static int es_pci_attach(device_t dev) { - uint32_t data; struct es_info *es = NULL; int mapped, i, numplay, dac_cfg; char status[SND_STATUSLEN]; @@ -1719,11 +1718,7 @@ es_pci_attach(device_t dev) mapped = 0; pci_enable_busmaster(dev); - data = pci_read_config(dev, PCIR_COMMAND, 2); - data |= (PCIM_CMD_PORTEN|PCIM_CMD_MEMEN); - pci_write_config(dev, PCIR_COMMAND, data, 2); - data = pci_read_config(dev, PCIR_COMMAND, 2); - if (mapped == 0 && (data & PCIM_CMD_MEMEN)) { + if (mapped == 0) { es->regid = MEM_MAP_REG; es->regtype = SYS_RES_MEMORY; es->reg = bus_alloc_resource_any(dev, es->regtype, &es->regid, @@ -1731,7 +1726,7 @@ es_pci_attach(device_t dev) if (es->reg) mapped++; } - if (mapped == 0 && (data & PCIM_CMD_PORTEN)) { + if (mapped == 0) { es->regid = PCIR_BAR(0); es->regtype = SYS_RES_IOPORT; es->reg = bus_alloc_resource_any(dev, es->regtype, &es->regid, diff --git a/sys/dev/sound/pci/fm801.c b/sys/dev/sound/pci/fm801.c index ccfef36ebe0..969d53261ea 100644 --- a/sys/dev/sound/pci/fm801.c +++ b/sys/dev/sound/pci/fm801.c @@ -573,7 +573,6 @@ fm801_init(struct fm801_info *fm801) static int fm801_pci_attach(device_t dev) { - u_int32_t data; struct ac97_info *codec = 0; struct fm801_info *fm801; int i; @@ -583,10 +582,7 @@ fm801_pci_attach(device_t dev) fm801 = malloc(sizeof(*fm801), M_DEVBUF, M_WAITOK | M_ZERO); fm801->type = pci_get_devid(dev); - data = pci_read_config(dev, PCIR_COMMAND, 2); - data |= (PCIM_CMD_PORTEN|PCIM_CMD_MEMEN|PCIM_CMD_BUSMASTEREN); - pci_write_config(dev, PCIR_COMMAND, data, 2); - data = pci_read_config(dev, PCIR_COMMAND, 2); + pci_enable_busmaster(dev); for (i = 0; (mapped == 0) && (i < PCI_MAXMAPS_0); i++) { fm801->regid = PCIR_BAR(i); diff --git a/sys/dev/sound/pci/hdspe.c b/sys/dev/sound/pci/hdspe.c index fa576b64dc6..68a17f4f191 100644 --- a/sys/dev/sound/pci/hdspe.c +++ b/sys/dev/sound/pci/hdspe.c @@ -242,20 +242,6 @@ hdspe_probe(device_t dev) return (ENXIO); } -static int -set_pci_config(device_t dev) -{ - uint32_t data; - - pci_enable_busmaster(dev); - - data = pci_get_revid(dev); - data |= PCIM_CMD_PORTEN; - pci_write_config(dev, PCIR_COMMAND, data, 2); - - return 0; -} - static int hdspe_init(struct sc_info *sc) { @@ -307,13 +293,12 @@ hdspe_attach(device_t dev) device_printf(dev, "hdspe_attach()\n"); #endif - set_pci_config(dev); - sc = device_get_softc(dev); sc->lock = snd_mtxcreate(device_get_nameunit(dev), "snd_hdspe softc"); sc->dev = dev; + pci_enable_busmaster(dev); rev = pci_get_revid(dev); switch (rev) { case PCI_REVISION_AIO: diff --git a/sys/dev/sound/pci/maestro.c b/sys/dev/sound/pci/maestro.c index 66567fc5711..965d1c2267e 100644 --- a/sys/dev/sound/pci/maestro.c +++ b/sys/dev/sound/pci/maestro.c @@ -1844,15 +1844,10 @@ agg_attach(device_t dev) ess->curpwr = PCI_POWERSTATE_D3; pci_set_powerstate(dev, PCI_POWERSTATE_D0); - data = pci_read_config(dev, PCIR_COMMAND, 2); - data |= (PCIM_CMD_PORTEN|PCIM_CMD_BUSMASTEREN); - pci_write_config(dev, PCIR_COMMAND, data, 2); - data = pci_read_config(dev, PCIR_COMMAND, 2); + pci_enable_busmaster(dev); /* Allocate resources. */ - if (data & PCIM_CMD_PORTEN) - reg = bus_alloc_resource_any(dev, SYS_RES_IOPORT, ®id, - RF_ACTIVE); + reg = bus_alloc_resource_any(dev, SYS_RES_IOPORT, ®id, RF_ACTIVE); if (reg != NULL) { ess->reg = reg; ess->regid = regid; diff --git a/sys/dev/sound/pci/maestro3.c b/sys/dev/sound/pci/maestro3.c index 41e71cd137c..20a9bda7045 100644 --- a/sys/dev/sound/pci/maestro3.c +++ b/sys/dev/sound/pci/maestro3.c @@ -1317,7 +1317,6 @@ m3_pci_attach(device_t dev) { struct sc_info *sc; struct ac97_info *codec = NULL; - u_int32_t data; char status[SND_STATUSLEN]; struct m3_card_type *card; int i, len, dacn, adcn; @@ -1351,9 +1350,7 @@ m3_pci_attach(device_t dev) adcn = M3_RCHANS; - data = pci_read_config(dev, PCIR_COMMAND, 2); - data |= (PCIM_CMD_PORTEN | PCIM_CMD_MEMEN | PCIM_CMD_BUSMASTEREN); - pci_write_config(dev, PCIR_COMMAND, data, 2); + pci_enable_busmaster(dev); sc->regid = PCIR_BAR(0); sc->regtype = SYS_RES_MEMORY; diff --git a/sys/dev/sound/pci/neomagic.c b/sys/dev/sound/pci/neomagic.c index f46e3e1b00b..71c1bf14eb8 100644 --- a/sys/dev/sound/pci/neomagic.c +++ b/sys/dev/sound/pci/neomagic.c @@ -599,7 +599,7 @@ nm_pci_probe(device_t dev) { struct sc_info *sc = NULL; char *s = NULL; - u_int32_t subdev, i, data; + u_int32_t subdev, i; subdev = (pci_get_subdevice(dev) << 16) | pci_get_subvendor(dev); switch (pci_get_devid(dev)) { @@ -616,11 +616,6 @@ nm_pci_probe(device_t dev) return ENXIO; } - data = pci_read_config(dev, PCIR_COMMAND, 2); - pci_write_config(dev, PCIR_COMMAND, data | - PCIM_CMD_PORTEN | PCIM_CMD_MEMEN | - PCIM_CMD_BUSMASTEREN, 2); - sc->regid = PCIR_BAR(1); sc->reg = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->regid, @@ -628,7 +623,6 @@ nm_pci_probe(device_t dev) if (!sc->reg) { device_printf(dev, "unable to map register space\n"); - pci_write_config(dev, PCIR_COMMAND, data, 2); free(sc, M_DEVBUF); return ENXIO; } @@ -645,7 +639,6 @@ nm_pci_probe(device_t dev) DEB(device_printf(dev, "subdev = 0x%x - badcard?\n", subdev)); } - pci_write_config(dev, PCIR_COMMAND, data, 2); bus_release_resource(dev, SYS_RES_MEMORY, sc->regid, sc->reg); free(sc, M_DEVBUF); @@ -670,7 +663,6 @@ nm_pci_probe(device_t dev) static int nm_pci_attach(device_t dev) { - u_int32_t data; struct sc_info *sc; struct ac97_info *codec = 0; char status[SND_STATUSLEN]; @@ -679,10 +671,7 @@ nm_pci_attach(device_t dev) sc->dev = dev; sc->type = pci_get_devid(dev); - data = pci_read_config(dev, PCIR_COMMAND, 2); - data |= (PCIM_CMD_PORTEN|PCIM_CMD_MEMEN|PCIM_CMD_BUSMASTEREN); - pci_write_config(dev, PCIR_COMMAND, data, 2); - data = pci_read_config(dev, PCIR_COMMAND, 2); + pci_enable_busmaster(dev); sc->bufid = PCIR_BAR(0); sc->buf = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->bufid, diff --git a/sys/dev/sound/pci/solo.c b/sys/dev/sound/pci/solo.c index ba9202029cc..534d8106cb2 100644 --- a/sys/dev/sound/pci/solo.c +++ b/sys/dev/sound/pci/solo.c @@ -949,15 +949,9 @@ static int ess_resume(device_t dev) { uint16_t ddma; - uint32_t data; struct ess_info *sc = pcm_getdevinfo(dev); ess_lock(sc); - data = pci_read_config(dev, PCIR_COMMAND, 2); - data |= PCIM_CMD_PORTEN | PCIM_CMD_BUSMASTEREN; - pci_write_config(dev, PCIR_COMMAND, data, 2); - data = pci_read_config(dev, PCIR_COMMAND, 2); - ddma = rman_get_start(sc->vc) | 1; pci_write_config(dev, ESS_PCI_LEGACYCONTROL, 0x805f, 2); pci_write_config(dev, ESS_PCI_DDMACONTROL, ddma, 2); @@ -988,13 +982,9 @@ ess_attach(device_t dev) struct ess_info *sc; char status[SND_STATUSLEN]; u_int16_t ddma; - u_int32_t data; sc = malloc(sizeof(*sc), M_DEVBUF, M_WAITOK | M_ZERO); - data = pci_read_config(dev, PCIR_COMMAND, 2); - data |= PCIM_CMD_PORTEN | PCIM_CMD_BUSMASTEREN; - pci_write_config(dev, PCIR_COMMAND, data, 2); - data = pci_read_config(dev, PCIR_COMMAND, 2); + pci_enable_busmaster(dev); if (ess_alloc_resources(sc, dev)) goto no; diff --git a/sys/dev/sound/pci/t4dwave.c b/sys/dev/sound/pci/t4dwave.c index 8d9c4c64e9a..ef488906e46 100644 --- a/sys/dev/sound/pci/t4dwave.c +++ b/sys/dev/sound/pci/t4dwave.c @@ -822,7 +822,6 @@ tr_pci_probe(device_t dev) static int tr_pci_attach(device_t dev) { - u_int32_t data; struct tr_info *tr; struct ac97_info *codec = 0; bus_addr_t lowaddr; @@ -831,6 +830,7 @@ tr_pci_attach(device_t dev) #ifdef __sparc64__ device_t *children; int nchildren; + u_int32_t data; #endif tr = malloc(sizeof(*tr), M_DEVBUF, M_WAITOK | M_ZERO); @@ -857,10 +857,7 @@ tr_pci_attach(device_t dev) } } - data = pci_read_config(dev, PCIR_COMMAND, 2); - data |= (PCIM_CMD_PORTEN|PCIM_CMD_MEMEN|PCIM_CMD_BUSMASTEREN); - pci_write_config(dev, PCIR_COMMAND, data, 2); - data = pci_read_config(dev, PCIR_COMMAND, 2); + pci_enable_busmaster(dev); tr->regid = PCIR_BAR(0); tr->regtype = SYS_RES_IOPORT; diff --git a/sys/dev/sound/pci/via82c686.c b/sys/dev/sound/pci/via82c686.c index 4f336bf572c..28198a61f79 100644 --- a/sys/dev/sound/pci/via82c686.c +++ b/sys/dev/sound/pci/via82c686.c @@ -485,11 +485,7 @@ via_attach(device_t dev) via->lock = snd_mtxcreate(device_get_nameunit(dev), "snd_via82c686 softc"); - /* Get resources */ - data = pci_read_config(dev, PCIR_COMMAND, 2); - data |= (PCIM_CMD_PORTEN | PCIM_CMD_BUSMASTEREN); - pci_write_config(dev, PCIR_COMMAND, data, 2); - data = pci_read_config(dev, PCIR_COMMAND, 2); + pci_enable_busmaster(dev); /* Wake up and reset AC97 if necessary */ data = pci_read_config(dev, VIA_AC97STATUS, 1); diff --git a/sys/dev/sound/pci/vibes.c b/sys/dev/sound/pci/vibes.c index d3915758cb1..733e0d8eb9e 100644 --- a/sys/dev/sound/pci/vibes.c +++ b/sys/dev/sound/pci/vibes.c @@ -728,10 +728,7 @@ sv_attach(device_t dev) { sc = malloc(sizeof(*sc), M_DEVBUF, M_WAITOK | M_ZERO); sc->dev = dev; - data = pci_read_config(dev, PCIR_COMMAND, 2); - data |= (PCIM_CMD_PORTEN|PCIM_CMD_BUSMASTEREN); - pci_write_config(dev, PCIR_COMMAND, data, 2); - data = pci_read_config(dev, PCIR_COMMAND, 2); + pci_enable_busmaster(dev); #if __FreeBSD_version > 500000 if (pci_get_powerstate(dev) != PCI_POWERSTATE_D0) { diff --git a/sys/dev/stge/if_stge.c b/sys/dev/stge/if_stge.c index 96860a88827..3cc45b35051 100644 --- a/sys/dev/stge/if_stge.c +++ b/sys/dev/stge/if_stge.c @@ -453,11 +453,11 @@ stge_attach(device_t dev) pci_enable_busmaster(dev); cmd = pci_read_config(dev, PCIR_COMMAND, 2); val = pci_read_config(dev, PCIR_BAR(1), 4); - if ((val & 0x01) != 0) + if (PCI_BAR_IO(val)) sc->sc_spec = stge_res_spec_mem; else { val = pci_read_config(dev, PCIR_BAR(0), 4); - if ((val & 0x01) == 0) { + if (!PCI_BAR_IO(val)) { device_printf(sc->sc_dev, "couldn't locate IO BAR\n"); error = ENXIO; goto fail; diff --git a/sys/dev/streams/streams.c b/sys/dev/streams/streams.c index ad2817f5ca6..3ddbcc7563a 100644 --- a/sys/dev/streams/streams.c +++ b/sys/dev/streams/streams.c @@ -98,6 +98,7 @@ static struct fileops svr4_netops = { .fo_close = svr4_soo_close, .fo_chmod = invfo_chmod, .fo_chown = invfo_chown, + .fo_sendfile = invfo_sendfile, }; static struct cdevsw streams_cdevsw = { diff --git a/sys/dev/sym/sym_hipd.c b/sys/dev/sym/sym_hipd.c index 8dc7361c596..3ace75b2214 100644 --- a/sys/dev/sym/sym_hipd.c +++ b/sys/dev/sym/sym_hipd.c @@ -8528,11 +8528,9 @@ sym_pci_attach(device_t dev) /* * Alloc/get/map/retrieve everything that deals with MMIO. */ - if ((command & PCIM_CMD_MEMEN) != 0) { - int regs_id = SYM_PCI_MMIO; - np->mmio_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, - ®s_id, RF_ACTIVE); - } + i = SYM_PCI_MMIO; + np->mmio_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &i, + RF_ACTIVE); if (!np->mmio_res) { device_printf(dev, "failed to allocate MMIO resources\n"); goto attach_failed; @@ -8555,11 +8553,8 @@ sym_pci_attach(device_t dev) * User want us to use normal IO with PCI. * Alloc/get/map/retrieve everything that deals with IO. */ - if ((command & PCI_COMMAND_IO_ENABLE) != 0) { - int regs_id = SYM_PCI_IO; - np->io_res = bus_alloc_resource_any(dev, SYS_RES_IOPORT, - ®s_id, RF_ACTIVE); - } + i = SYM_PCI_IO; + np->io_res = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &i, RF_ACTIVE); if (!np->io_res) { device_printf(dev, "failed to allocate IO resources\n"); goto attach_failed; @@ -8571,8 +8566,7 @@ sym_pci_attach(device_t dev) * If the chip has RAM. * Alloc/get/map/retrieve the corresponding resources. */ - if ((np->features & (FE_RAM|FE_RAM8K)) && - (command & PCIM_CMD_MEMEN) != 0) { + if (np->features & (FE_RAM|FE_RAM8K)) { int regs_id = SYM_PCI_RAM; if (np->features & FE_64BIT) regs_id = SYM_PCI_RAM64; diff --git a/sys/dev/tdfx/tdfx_linux.c b/sys/dev/tdfx/tdfx_linux.c index 0b769f01a24..fa39ab1903b 100644 --- a/sys/dev/tdfx/tdfx_linux.c +++ b/sys/dev/tdfx/tdfx_linux.c @@ -45,6 +45,7 @@ LINUX_IOCTL_SET(tdfx, LINUX_IOCTL_TDFX_MIN, LINUX_IOCTL_TDFX_MAX); static int linux_ioctl_tdfx(struct thread *td, struct linux_ioctl_args* args) { + cap_rights_t rights; int error = 0; u_long cmd = args->cmd & 0xffff; @@ -54,7 +55,8 @@ linux_ioctl_tdfx(struct thread *td, struct linux_ioctl_args* args) struct file *fp; - if ((error = fget(td, args->fd, CAP_IOCTL, &fp)) != 0) + error = fget(td, args->fd, cap_rights_init(&rights, CAP_IOCTL), &fp); + if (error != 0) return (error); /* We simply copy the data and send it right to ioctl */ copyin((caddr_t)args->arg, &d_pio, sizeof(d_pio)); diff --git a/sys/dev/tdfx/tdfx_pci.c b/sys/dev/tdfx/tdfx_pci.c index 0992e5fbd79..918857a8abb 100644 --- a/sys/dev/tdfx/tdfx_pci.c +++ b/sys/dev/tdfx/tdfx_pci.c @@ -145,7 +145,6 @@ tdfx_attach(device_t dev) { * small, whole number. */ struct tdfx_softc *tdfx_info; - u_long val; /* rid value tells bus_alloc_resource where to find the addresses of ports or * of memory ranges in the PCI config space*/ int rid = PCIR_BAR(0); @@ -153,12 +152,6 @@ tdfx_attach(device_t dev) { /* Increment the card counter (for the ioctl code) */ tdfx_count++; - /* Enable MemMap on Voodoo */ - val = pci_read_config(dev, PCIR_COMMAND, 2); - val |= (PCIM_CMD_MEMEN); - pci_write_config(dev, PCIR_COMMAND, val, 2); - val = pci_read_config(dev, PCIR_COMMAND, 2); - /* Fill the soft config struct with info about this device*/ tdfx_info = device_get_softc(dev); tdfx_info->dev = dev; diff --git a/sys/dev/ti/if_ti.c b/sys/dev/ti/if_ti.c index f8142b47601..36cf0477ca1 100644 --- a/sys/dev/ti/if_ti.c +++ b/sys/dev/ti/if_ti.c @@ -3158,24 +3158,6 @@ ti_start_locked(struct ifnet *ifp) if (m_head == NULL) break; - /* - * XXX - * safety overkill. If this is a fragmented packet chain - * with delayed TCP/UDP checksums, then only encapsulate - * it if we have enough descriptors to handle the entire - * chain at once. - * (paranoia -- may not actually be needed) - */ - if (m_head->m_flags & M_FIRSTFRAG && - m_head->m_pkthdr.csum_flags & (CSUM_DELAY_DATA)) { - if ((TI_TX_RING_CNT - sc->ti_txcnt) < - m_head->m_pkthdr.csum_data + 16) { - IFQ_DRV_PREPEND(&ifp->if_snd, m_head); - ifp->if_drv_flags |= IFF_DRV_OACTIVE; - break; - } - } - /* * Pack the data into the transmit ring. If we * don't have room, set the OACTIVE flag and wait diff --git a/sys/dev/twa/tw_osl_freebsd.c b/sys/dev/twa/tw_osl_freebsd.c index b2284f89892..8b5626723f2 100644 --- a/sys/dev/twa/tw_osl_freebsd.c +++ b/sys/dev/twa/tw_osl_freebsd.c @@ -284,7 +284,6 @@ static TW_INT32 twa_attach(device_t dev) { struct twa_softc *sc = device_get_softc(dev); - TW_UINT32 command; TW_INT32 bar_num; TW_INT32 bar0_offset; TW_INT32 bar_size; @@ -323,22 +322,8 @@ twa_attach(device_t dev) OID_AUTO, "driver_version", CTLFLAG_RD, TW_OSL_DRIVER_VERSION_STRING, 0, "TWA driver version"); - /* Make sure we are going to be able to talk to this board. */ - command = pci_read_config(dev, PCIR_COMMAND, 2); - if ((command & PCIM_CMD_PORTEN) == 0) { - tw_osli_printf(sc, "error = %d", - TW_CL_SEVERITY_ERROR_STRING, - TW_CL_MESSAGE_SOURCE_FREEBSD_DRIVER, - 0x2001, - "Register window not available", - ENXIO); - tw_osli_free_resources(sc); - return(ENXIO); - } - /* Force the busmaster enable bit on, in case the BIOS forgot. */ - command |= PCIM_CMD_BUSMASTEREN; - pci_write_config(dev, PCIR_COMMAND, command, 2); + pci_enable_busmaster(dev); /* Allocate the PCI register window. */ if ((error = tw_cl_get_pci_bar_info(sc->device_id, TW_CL_BAR_TYPE_MEM, diff --git a/sys/dev/tws/tws.c b/sys/dev/tws/tws.c index 87a7d2014ce..409631f28b4 100644 --- a/sys/dev/tws/tws.c +++ b/sys/dev/tws/tws.c @@ -183,7 +183,7 @@ static int tws_attach(device_t dev) { struct tws_softc *sc = device_get_softc(dev); - u_int32_t cmd, bar; + u_int32_t bar; int error=0,i; /* no tracing yet */ @@ -224,14 +224,7 @@ tws_attach(device_t dev) OID_AUTO, "driver_version", CTLFLAG_RD, TWS_DRIVER_VERSION_STRING, 0, "TWS driver version"); - cmd = pci_read_config(dev, PCIR_COMMAND, 2); - if ( (cmd & PCIM_CMD_PORTEN) == 0) { - tws_log(sc, PCI_COMMAND_READ); - goto attach_fail_1; - } - /* Force the busmaster enable bit on. */ - cmd |= PCIM_CMD_BUSMASTEREN; - pci_write_config(dev, PCIR_COMMAND, cmd, 2); + pci_enable_busmaster(dev); bar = pci_read_config(dev, TWS_PCI_BAR0, 4); TWS_TRACE_DEBUG(sc, "bar0 ", bar, 0); @@ -461,13 +454,9 @@ static int tws_setup_irq(struct tws_softc *sc) { int messages; - u_int16_t cmd; - cmd = pci_read_config(sc->tws_dev, PCIR_COMMAND, 2); switch(sc->intr_type) { case TWS_INTx : - cmd = cmd & ~0x0400; - pci_write_config(sc->tws_dev, PCIR_COMMAND, cmd, 2); sc->irqs = 1; sc->irq_res_id[0] = 0; sc->irq_res[0] = bus_alloc_resource_any(sc->tws_dev, SYS_RES_IRQ, @@ -479,8 +468,6 @@ tws_setup_irq(struct tws_softc *sc) device_printf(sc->tws_dev, "Using legacy INTx\n"); break; case TWS_MSI : - cmd = cmd | 0x0400; - pci_write_config(sc->tws_dev, PCIR_COMMAND, cmd, 2); sc->irqs = 1; sc->irq_res_id[0] = 1; messages = 1; diff --git a/sys/dev/uart/uart.h b/sys/dev/uart/uart.h index fb0f6996dd8..840d783b405 100644 --- a/sys/dev/uart/uart.h +++ b/sys/dev/uart/uart.h @@ -74,6 +74,7 @@ extern struct uart_class uart_z8530_class __attribute__((weak)); extern struct uart_class uart_lpc_class __attribute__((weak)); extern struct uart_class uart_pl011_class __attribute__((weak)); extern struct uart_class uart_cdnc_class __attribute__((weak)); +extern struct uart_class uart_ti8250_class __attribute__((weak)); #ifdef PC98 struct uart_class *uart_pc98_getdev(u_long port); diff --git a/sys/dev/uart/uart_bus_fdt.c b/sys/dev/uart/uart_bus_fdt.c index 4f6ac9bd22e..da84f26de48 100644 --- a/sys/dev/uart/uart_bus_fdt.c +++ b/sys/dev/uart/uart_bus_fdt.c @@ -101,9 +101,7 @@ uart_fdt_probe(device_t dev) int err; sc = device_get_softc(dev); - if (ofw_bus_is_compatible(dev, "ns16550")) - sc->sc_class = &uart_ns8250_class; - else if (ofw_bus_is_compatible(dev, "lpc,uart")) + if (ofw_bus_is_compatible(dev, "lpc,uart")) sc->sc_class = &uart_lpc_class; else if (ofw_bus_is_compatible(dev, "fsl,imx-uart")) sc->sc_class = &uart_imx_class; @@ -113,6 +111,10 @@ uart_fdt_probe(device_t dev) sc->sc_class = &uart_s3c2410_class; else if (ofw_bus_is_compatible(dev, "cadence,uart")) sc->sc_class = &uart_cdnc_class; + else if (ofw_bus_is_compatible(dev, "ti,ns16550")) + sc->sc_class = &uart_ti8250_class; + else if (ofw_bus_is_compatible(dev, "ns16550")) + sc->sc_class = &uart_ns8250_class; else return (ENXIO); diff --git a/sys/dev/uart/uart_core.c b/sys/dev/uart/uart_core.c index c2f53b8e266..17dabd7d801 100644 --- a/sys/dev/uart/uart_core.c +++ b/sys/dev/uart/uart_core.c @@ -537,7 +537,7 @@ uart_bus_attach(device_t dev) sep = ", "; } if (sc->sc_polled) { - printf("%spolled mode", sep); + printf("%spolled mode (%dHz)", sep, uart_poll_freq); sep = ", "; } printf("\n"); diff --git a/sys/dev/uart/uart_cpu_fdt.c b/sys/dev/uart/uart_cpu_fdt.c index 0052388d375..230c3632c3a 100644 --- a/sys/dev/uart/uart_cpu_fdt.c +++ b/sys/dev/uart/uart_cpu_fdt.c @@ -141,18 +141,20 @@ uart_cpu_getdev(int devtype, struct uart_devinfo *di) */ if (fdt_is_compatible(node, "fsl,imx-uart")) class = &uart_imx_class; - if (fdt_is_compatible(node, "quicc")) + else if (fdt_is_compatible(node, "quicc")) class = &uart_quicc_class; - if (fdt_is_compatible(node, "lpc")) + else if (fdt_is_compatible(node, "lpc")) class = &uart_lpc_class; - if (fdt_is_compatible(node, "ns16550")) - class = &uart_ns8250_class; - if (fdt_is_compatible(node, "arm,pl011")) + else if (fdt_is_compatible(node, "arm,pl011")) class = &uart_pl011_class; - if (fdt_is_compatible(node, "exynos")) + else if (fdt_is_compatible(node, "exynos")) class = &uart_s3c2410_class; - if (fdt_is_compatible(node, "cadence,uart")) + else if (fdt_is_compatible(node, "cadence,uart")) class = &uart_cdnc_class; + else if (fdt_is_compatible(node, "ti,ns16550")) + class = &uart_ti8250_class; + else if (fdt_is_compatible(node, "ns16550")) + class = &uart_ns8250_class; di->bas.chan = 0; di->bas.regshft = (u_int)shift; diff --git a/sys/dev/uart/uart_dev_ns8250.c b/sys/dev/uart/uart_dev_ns8250.c index 8eca8f0f4dd..211d1132186 100644 --- a/sys/dev/uart/uart_dev_ns8250.c +++ b/sys/dev/uart/uart_dev_ns8250.c @@ -46,6 +46,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include @@ -239,7 +240,7 @@ static void ns8250_putc(struct uart_bas *bas, int); static int ns8250_rxready(struct uart_bas *bas); static int ns8250_getc(struct uart_bas *bas, struct mtx *); -static struct uart_ops uart_ns8250_ops = { +struct uart_ops uart_ns8250_ops = { .probe = ns8250_probe, .init = ns8250_init, .term = ns8250_term, @@ -352,32 +353,6 @@ ns8250_getc(struct uart_bas *bas, struct mtx *hwmtx) return (c); } -/* - * High-level UART interface. - */ -struct ns8250_softc { - struct uart_softc base; - uint8_t fcr; - uint8_t ier; - uint8_t mcr; - - uint8_t ier_mask; - uint8_t ier_rxbits; - uint8_t busy_detect; -}; - -static int ns8250_bus_attach(struct uart_softc *); -static int ns8250_bus_detach(struct uart_softc *); -static int ns8250_bus_flush(struct uart_softc *, int); -static int ns8250_bus_getsig(struct uart_softc *); -static int ns8250_bus_ioctl(struct uart_softc *, int, intptr_t); -static int ns8250_bus_ipend(struct uart_softc *); -static int ns8250_bus_param(struct uart_softc *, int, int, int, int); -static int ns8250_bus_probe(struct uart_softc *); -static int ns8250_bus_receive(struct uart_softc *); -static int ns8250_bus_setsig(struct uart_softc *, int); -static int ns8250_bus_transmit(struct uart_softc *); - static kobj_method_t ns8250_methods[] = { KOBJMETHOD(uart_attach, ns8250_bus_attach), KOBJMETHOD(uart_detach, ns8250_bus_detach), @@ -409,7 +384,7 @@ struct uart_class uart_ns8250_class = { i = (i & s) ? (i & ~s) | d : i; \ } -static int +int ns8250_bus_attach(struct uart_softc *sc) { struct ns8250_softc *ns8250 = (struct ns8250_softc*)sc; @@ -478,11 +453,23 @@ ns8250_bus_attach(struct uart_softc *sc) ns8250->ier |= ns8250->ier_rxbits; uart_setreg(bas, REG_IER, ns8250->ier); uart_barrier(bas); - + + /* + * Timing of the H/W access was changed with r253161 of uart_core.c + * It has been observed that an ITE IT8513E would signal a break + * condition with pretty much every character it received, unless + * it had enough time to settle between ns8250_bus_attach() and + * ns8250_bus_ipend() -- which it accidentally had before r253161. + * It's not understood why the UART chip behaves this way and it + * could very well be that the DELAY make the H/W work in the same + * accidental manner as before. More analysis is warranted, but + * at least now we fixed a known regression. + */ + DELAY(200); return (0); } -static int +int ns8250_bus_detach(struct uart_softc *sc) { struct ns8250_softc *ns8250; @@ -498,7 +485,7 @@ ns8250_bus_detach(struct uart_softc *sc) return (0); } -static int +int ns8250_bus_flush(struct uart_softc *sc, int what) { struct ns8250_softc *ns8250 = (struct ns8250_softc*)sc; @@ -518,7 +505,7 @@ ns8250_bus_flush(struct uart_softc *sc, int what) return (error); } -static int +int ns8250_bus_getsig(struct uart_softc *sc) { uint32_t new, old, sig; @@ -539,7 +526,7 @@ ns8250_bus_getsig(struct uart_softc *sc) return (sig); } -static int +int ns8250_bus_ioctl(struct uart_softc *sc, int request, intptr_t data) { struct uart_bas *bas; @@ -612,7 +599,7 @@ ns8250_bus_ioctl(struct uart_softc *sc, int request, intptr_t data) return (error); } -static int +int ns8250_bus_ipend(struct uart_softc *sc) { struct uart_bas *bas; @@ -656,7 +643,7 @@ ns8250_bus_ipend(struct uart_softc *sc) return (ipend); } -static int +int ns8250_bus_param(struct uart_softc *sc, int baudrate, int databits, int stopbits, int parity) { @@ -670,7 +657,7 @@ ns8250_bus_param(struct uart_softc *sc, int baudrate, int databits, return (error); } -static int +int ns8250_bus_probe(struct uart_softc *sc) { struct ns8250_softc *ns8250; @@ -820,7 +807,7 @@ ns8250_bus_probe(struct uart_softc *sc) return (0); } -static int +int ns8250_bus_receive(struct uart_softc *sc) { struct uart_bas *bas; @@ -853,7 +840,7 @@ ns8250_bus_receive(struct uart_softc *sc) return (0); } -static int +int ns8250_bus_setsig(struct uart_softc *sc, int sig) { struct ns8250_softc *ns8250 = (struct ns8250_softc*)sc; @@ -885,7 +872,7 @@ ns8250_bus_setsig(struct uart_softc *sc, int sig) return (0); } -static int +int ns8250_bus_transmit(struct uart_softc *sc) { struct ns8250_softc *ns8250 = (struct ns8250_softc*)sc; diff --git a/sys/dev/uart/uart_dev_ns8250.h b/sys/dev/uart/uart_dev_ns8250.h new file mode 100644 index 00000000000..39f4a0f2780 --- /dev/null +++ b/sys/dev/uart/uart_dev_ns8250.h @@ -0,0 +1,60 @@ +/*- + * Copyright (c) 2003 Marcel Moolenaar + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + * + * $FreeBSD$ + */ + +#ifndef _DEV_UART_DEV_NS8250_H_ +#define _DEV_UART_DEV_NS8250_H_ + +/* + * High-level UART interface. + */ +struct ns8250_softc { + struct uart_softc base; + uint8_t fcr; + uint8_t ier; + uint8_t mcr; + + uint8_t ier_mask; + uint8_t ier_rxbits; + uint8_t busy_detect; +}; + +extern struct uart_ops uart_ns8250_ops; + +int ns8250_bus_attach(struct uart_softc *); +int ns8250_bus_detach(struct uart_softc *); +int ns8250_bus_flush(struct uart_softc *, int); +int ns8250_bus_getsig(struct uart_softc *); +int ns8250_bus_ioctl(struct uart_softc *, int, intptr_t); +int ns8250_bus_ipend(struct uart_softc *); +int ns8250_bus_param(struct uart_softc *, int, int, int, int); +int ns8250_bus_probe(struct uart_softc *); +int ns8250_bus_receive(struct uart_softc *); +int ns8250_bus_setsig(struct uart_softc *, int); +int ns8250_bus_transmit(struct uart_softc *); + +#endif /* _DEV_UART_DEV_NS8250_H_ */ diff --git a/sys/dev/uart/uart_dev_ti8250.c b/sys/dev/uart/uart_dev_ti8250.c new file mode 100644 index 00000000000..12d059a8321 --- /dev/null +++ b/sys/dev/uart/uart_dev_ti8250.c @@ -0,0 +1,141 @@ +/*- + * Copyright (c) 2013 Ian Lepore + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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 "opt_platform.h" + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include "uart_if.h" + +/* + * High-level UART interface. + */ +struct ti8250_softc { + struct ns8250_softc ns8250_base; + /*uint32_t mystuff;*/ +}; + +#define MDR1_REG 8 +#define MDR1_MODE_UART 0 +#define MDR1_MODE_DISABLE 7 +#define SYSCC_REG 15 +#define SYSCC_SOFTRESET (1 << 1) +#define SYSS_REG 16 +#define SYSS_STATUS_RESETDONE (1 << 0) + +static int +ti8250_bus_probe(struct uart_softc *sc) +{ + int status; + int devid; + clk_ident_t clkid; + pcell_t prop; + phandle_t node; + + /* + * Get the device id from FDT. If it's not there we can't turn on the + * right clocks, so bail, unless we're doing unit 0. We assume that's + * the serial console, whose clock isn't controllable anyway, and we + * sure don't want to break the console because of a config error. + */ + node = ofw_bus_get_node(sc->sc_dev); + if ((OF_getprop(node, "uart-device-id", &prop, sizeof(prop))) <= 0) { + device_printf(sc->sc_dev, + "missing uart-device-id attribute in FDT\n"); + if (device_get_unit(sc->sc_dev) != 0) + return (ENXIO); + devid = 0; + } else + devid = fdt32_to_cpu(prop); + + /* Enable clocks for this device. We can't continue if that fails. */ + clkid = UART0_CLK + devid; + if ((status = ti_prcm_clk_enable(clkid)) != 0) + return (status); + + /* + * Set the hardware to disabled mode, do a full device reset, then set + * it to uart mode. Most devices will be reset-and-disabled already, + * but you never know what a bootloader might have done. + */ + uart_setreg(&sc->sc_bas, MDR1_REG, MDR1_MODE_DISABLE); + uart_setreg(&sc->sc_bas, SYSCC_REG, SYSCC_SOFTRESET); + while (uart_getreg(&sc->sc_bas, SYSS_REG) & SYSS_STATUS_RESETDONE) + continue; + uart_setreg(&sc->sc_bas, MDR1_REG, MDR1_MODE_UART); + + status = ns8250_bus_probe(sc); + if (status == 0) + device_set_desc(sc->sc_dev, "TI UART (16550 compatible)"); + + return (status); +} + +static kobj_method_t ti8250_methods[] = { + KOBJMETHOD(uart_probe, ti8250_bus_probe), + + KOBJMETHOD(uart_attach, ns8250_bus_attach), + KOBJMETHOD(uart_detach, ns8250_bus_detach), + KOBJMETHOD(uart_flush, ns8250_bus_flush), + KOBJMETHOD(uart_getsig, ns8250_bus_getsig), + KOBJMETHOD(uart_ioctl, ns8250_bus_ioctl), + KOBJMETHOD(uart_ipend, ns8250_bus_ipend), + KOBJMETHOD(uart_param, ns8250_bus_param), + KOBJMETHOD(uart_receive, ns8250_bus_receive), + KOBJMETHOD(uart_setsig, ns8250_bus_setsig), + KOBJMETHOD(uart_transmit, ns8250_bus_transmit), + KOBJMETHOD_END +}; + +struct uart_class uart_ti8250_class = { + "ti8250", + ti8250_methods, + sizeof(struct ti8250_softc), + .uc_ops = &uart_ns8250_ops, + .uc_range = 0x88, + .uc_rclk = 48000000 +}; + diff --git a/sys/dev/ubsec/ubsec.c b/sys/dev/ubsec/ubsec.c index 40e6d5eccc2..0e1b7cb5df6 100644 --- a/sys/dev/ubsec/ubsec.c +++ b/sys/dev/ubsec/ubsec.c @@ -259,7 +259,7 @@ ubsec_partname(struct ubsec_softc *sc) static void default_harvest(struct rndtest_state *rsp, void *buf, u_int count) { - random_harvest(buf, count, count*NBBY, 0, RANDOM_PURE); + random_harvest(buf, count, count*NBBY/2, 0, RANDOM_PURE); } static int @@ -267,7 +267,7 @@ ubsec_attach(device_t dev) { struct ubsec_softc *sc = device_get_softc(dev); struct ubsec_dma *dmap; - u_int32_t cmd, i; + u_int32_t i; int rid; bzero(sc, sizeof (*sc)); @@ -312,20 +312,7 @@ ubsec_attach(device_t dev) UBS_FLAGS_LONGCTX | UBS_FLAGS_HWNORM | UBS_FLAGS_BIGKEY; } - cmd = pci_read_config(dev, PCIR_COMMAND, 4); - cmd |= PCIM_CMD_MEMEN | PCIM_CMD_BUSMASTEREN; - pci_write_config(dev, PCIR_COMMAND, cmd, 4); - cmd = pci_read_config(dev, PCIR_COMMAND, 4); - - if (!(cmd & PCIM_CMD_MEMEN)) { - device_printf(dev, "failed to enable memory mapping\n"); - goto bad; - } - - if (!(cmd & PCIM_CMD_BUSMASTEREN)) { - device_printf(dev, "failed to enable bus mastering\n"); - goto bad; - } + pci_enable_busmaster(dev); /* * Setup memory-mapping of PCI registers. diff --git a/sys/dev/usb/controller/dwc_otg.c b/sys/dev/usb/controller/dwc_otg.c index d08b3d90c94..7c3b881bafd 100644 --- a/sys/dev/usb/controller/dwc_otg.c +++ b/sys/dev/usb/controller/dwc_otg.c @@ -3968,7 +3968,6 @@ done: static void dwc_otg_xfer_setup(struct usb_setup_params *parm) { - const struct usb_hw_ep_profile *pf; struct usb_xfer *xfer; void *last_obj; uint32_t ntd; @@ -4011,16 +4010,21 @@ dwc_otg_xfer_setup(struct usb_setup_params *parm) */ last_obj = NULL; - /* - * get profile stuff - */ ep_no = xfer->endpointno & UE_ADDR; - dwc_otg_get_hw_ep_profile(parm->udev, &pf, ep_no); - if (pf == NULL) { - /* should not happen */ - parm->err = USB_ERR_INVAL; - return; + /* + * Check for a valid endpoint profile in USB device mode: + */ + if (xfer->flags_int.usb_mode == USB_MODE_DEVICE) { + const struct usb_hw_ep_profile *pf; + + dwc_otg_get_hw_ep_profile(parm->udev, &pf, ep_no); + + if (pf == NULL) { + /* should not happen */ + parm->err = USB_ERR_INVAL; + return; + } } /* align data */ diff --git a/sys/dev/usb/controller/ehci.c b/sys/dev/usb/controller/ehci.c index 51b012b25c3..b8aa2ad2747 100644 --- a/sys/dev/usb/controller/ehci.c +++ b/sys/dev/usb/controller/ehci.c @@ -259,7 +259,7 @@ ehci_init_sub(struct ehci_softc *sc) DPRINTF("HCC uses 64-bit structures\n"); /* MUST clear segment register if 64 bit capable */ - EWRITE4(sc, EHCI_CTRLDSSEGMENT, 0); + EOWRITE4(sc, EHCI_CTRLDSSEGMENT, 0); } usbd_get_page(&sc->sc_hw.pframes_pc, 0, &buf_res); diff --git a/sys/dev/usb/controller/ehci_pci.c b/sys/dev/usb/controller/ehci_pci.c index e2fdd546fa3..6914f5094d3 100644 --- a/sys/dev/usb/controller/ehci_pci.c +++ b/sys/dev/usb/controller/ehci_pci.c @@ -165,7 +165,7 @@ ehci_pci_match(device_t self) case 0x00e810de: return "NVIDIA nForce3 250 USB 2.0 controller"; case 0x005b10de: - return "NVIDIA nForce4 USB 2.0 controller"; + return "NVIDIA nForce CK804 USB 2.0 controller"; case 0x036d10de: return "NVIDIA nForce MCP55 USB 2.0 controller"; case 0x03f210de: diff --git a/sys/dev/usb/controller/musb_otg.c b/sys/dev/usb/controller/musb_otg.c index 5113d7ac9bc..9b2974f267c 100644 --- a/sys/dev/usb/controller/musb_otg.c +++ b/sys/dev/usb/controller/musb_otg.c @@ -4026,7 +4026,6 @@ done: static void musbotg_xfer_setup(struct usb_setup_params *parm) { - const struct usb_hw_ep_profile *pf; struct musbotg_softc *sc; struct usb_xfer *xfer; void *last_obj; @@ -4088,12 +4087,14 @@ musbotg_xfer_setup(struct usb_setup_params *parm) */ last_obj = NULL; - /* - * get profile stuff - */ - if (ntd) { + ep_no = xfer->endpointno & UE_ADDR; + + /* + * Check for a valid endpoint profile in USB device mode: + */ + if (xfer->flags_int.usb_mode == USB_MODE_DEVICE) { + const struct usb_hw_ep_profile *pf; - ep_no = xfer->endpointno & UE_ADDR; musbotg_get_hw_ep_profile(parm->udev, &pf, ep_no); if (pf == NULL) { @@ -4101,9 +4102,6 @@ musbotg_xfer_setup(struct usb_setup_params *parm) parm->err = USB_ERR_INVAL; return; } - } else { - ep_no = 0; - pf = NULL; } /* align data */ diff --git a/sys/dev/usb/controller/ohci_pci.c b/sys/dev/usb/controller/ohci_pci.c index 7b6589ad797..36ca677b768 100644 --- a/sys/dev/usb/controller/ohci_pci.c +++ b/sys/dev/usb/controller/ohci_pci.c @@ -154,6 +154,8 @@ ohci_pci_match(device_t self) case 0x00d710de: return ("nVidia nForce3 USB Controller"); + case 0x005a10de: + return ("nVidia nForce CK804 USB Controller"); case 0x036c10de: return ("nVidia nForce MCP55 USB Controller"); case 0x03f110de: diff --git a/sys/dev/usb/controller/xhci.c b/sys/dev/usb/controller/xhci.c index 6c9a6101f64..ec8b75a4d40 100644 --- a/sys/dev/usb/controller/xhci.c +++ b/sys/dev/usb/controller/xhci.c @@ -87,12 +87,18 @@ ((struct xhci_softc *)(((uint8_t *)(bus)) - \ ((uint8_t *)&(((struct xhci_softc *)0)->sc_bus)))) +static SYSCTL_NODE(_hw_usb, OID_AUTO, xhci, CTLFLAG_RW, 0, "USB XHCI"); + +static int xhcistreams; +SYSCTL_INT(_hw_usb_xhci, OID_AUTO, streams, CTLFLAG_RW | CTLFLAG_TUN, + &xhcistreams, 0, "Set to enable streams mode support"); +TUNABLE_INT("hw.usb.xhci.streams", &xhcistreams); + #ifdef USB_DEBUG static int xhcidebug; static int xhciroute; static int xhcipolling; -static SYSCTL_NODE(_hw_usb, OID_AUTO, xhci, CTLFLAG_RW, 0, "USB XHCI"); SYSCTL_INT(_hw_usb_xhci, OID_AUTO, debug, CTLFLAG_RW | CTLFLAG_TUN, &xhcidebug, 0, "Debug level"); TUNABLE_INT("hw.usb.xhci.debug", &xhcidebug); @@ -1474,7 +1480,6 @@ void xhci_interrupt(struct xhci_softc *sc) { uint32_t status; - uint32_t iman; USB_BUS_LOCK(&sc->sc_bus); @@ -1489,15 +1494,6 @@ xhci_interrupt(struct xhci_softc *sc) DPRINTFN(16, "real interrupt (status=0x%08x)\n", status); if (status & XHCI_STS_EINT) { - - /* acknowledge pending event */ - iman = XREAD4(sc, runt, XHCI_IMAN(0)); - - /* reset interrupt */ - XWRITE4(sc, runt, XHCI_IMAN(0), iman); - - DPRINTFN(16, "real interrupt (iman=0x%08x)\n", iman); - /* check for event(s) */ xhci_interrupt_poll(sc); } @@ -4127,7 +4123,8 @@ xhci_set_endpoint_mode(struct usb_device *udev, struct usb_endpoint *ep, case USB_EP_MODE_DEFAULT: return (0); case USB_EP_MODE_STREAMS: - if ((ep->edesc->bmAttributes & UE_XFERTYPE) != UE_BULK || + if (xhcistreams == 0 || + (ep->edesc->bmAttributes & UE_XFERTYPE) != UE_BULK || udev->speed != USB_SPEED_SUPER) return (USB_ERR_INVAL); return (0); diff --git a/sys/dev/usb/input/ukbd.c b/sys/dev/usb/input/ukbd.c index 770c8fc704a..dd4b3295694 100644 --- a/sys/dev/usb/input/ukbd.c +++ b/sys/dev/usb/input/ukbd.c @@ -1130,8 +1130,12 @@ ukbd_parse_hid(struct ukbd_softc *sc, const uint8_t *ptr, uint32_t len) HID_USAGE2(HUP_KEYBOARD, 0x00), hid_input, 0, &sc->sc_loc_events, &flags, &sc->sc_id_events)) { - sc->sc_flags |= UKBD_FLAG_EVENTS; - DPRINTFN(1, "Found keyboard events\n"); + if (flags & HIO_VARIABLE) { + DPRINTFN(1, "Ignoring keyboard event control\n"); + } else { + sc->sc_flags |= UKBD_FLAG_EVENTS; + DPRINTFN(1, "Found keyboard event array\n"); + } } /* figure out leds on keyboard */ diff --git a/sys/dev/usb/net/if_udav.c b/sys/dev/usb/net/if_udav.c index 2e7c2716395..4fd39cc48cf 100644 --- a/sys/dev/usb/net/if_udav.c +++ b/sys/dev/usb/net/if_udav.c @@ -34,7 +34,7 @@ /* * DM9601(DAVICOM USB to Ethernet MAC Controller with Integrated 10/100 PHY) * The spec can be found at the following url. - * http://www.davicom.com.tw/big5/download/Data%20Sheet/DM9601-DS-P01-930914.pdf + * http://ptm2.cc.utu.fi/ftp/network/cards/DM9601/From_NET/DM9601-DS-P01-930914.pdf */ /* diff --git a/sys/dev/usb/net/if_usie.c b/sys/dev/usb/net/if_usie.c index 67f23b49232..9ca1ad61901 100644 --- a/sys/dev/usb/net/if_usie.c +++ b/sys/dev/usb/net/if_usie.c @@ -1324,7 +1324,6 @@ usie_if_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) break; case SIOCSIFADDR: - case SIOCSIFDSTADDR: break; default: diff --git a/sys/dev/usb/net/uhso.c b/sys/dev/usb/net/uhso.c index c56e1641cb3..c9d97577d2b 100644 --- a/sys/dev/usb/net/uhso.c +++ b/sys/dev/usb/net/uhso.c @@ -1854,7 +1854,6 @@ uhso_if_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) } break; case SIOCSIFADDR: - case SIOCSIFDSTADDR: case SIOCADDMULTI: case SIOCDELMULTI: break; diff --git a/sys/dev/usb/serial/uftdi.c b/sys/dev/usb/serial/uftdi.c index bc971eccce8..439cd8f0d06 100644 --- a/sys/dev/usb/serial/uftdi.c +++ b/sys/dev/usb/serial/uftdi.c @@ -243,6 +243,7 @@ static const STRUCT_USB_HOST_ID uftdi_devs[] = { UFTDI_DEV(FALCOM, TWIST, UFTDI_TYPE_8U232AM), UFTDI_DEV(FIC, NEO1973_DEBUG, UFTDI_TYPE_AUTO | UFTDI_FLAG_JTAG), UFTDI_DEV(FIC, NEO1973_DEBUG, UFTDI_TYPE_AUTO | UFTDI_FLAG_JTAG), + UFTDI_DEV(FTDI, 232EX, UFTDI_TYPE_AUTO), UFTDI_DEV(FTDI, 232H, UFTDI_TYPE_AUTO), UFTDI_DEV(FTDI, 232RL, UFTDI_TYPE_AUTO), UFTDI_DEV(FTDI, 4N_GALAXY_DE_1, UFTDI_TYPE_AUTO), diff --git a/sys/dev/usb/storage/umass.c b/sys/dev/usb/storage/umass.c index e2ff0ef6dda..2e6051dd6c1 100644 --- a/sys/dev/usb/storage/umass.c +++ b/sys/dev/usb/storage/umass.c @@ -1321,10 +1321,12 @@ umass_t_bbb_command_callback(struct usb_xfer *xfer, usb_error_t error) } sc->cbw.bCDBLength = sc->sc_transfer.cmd_len; + /* copy SCSI command data */ memcpy(sc->cbw.CBWCDB, sc->sc_transfer.cmd_data, sc->sc_transfer.cmd_len); - memset(sc->sc_transfer.cmd_data + + /* clear remaining command area */ + memset(sc->cbw.CBWCDB + sc->sc_transfer.cmd_len, 0, sizeof(sc->cbw.CBWCDB) - sc->sc_transfer.cmd_len); diff --git a/sys/dev/usb/usb_compat_linux.c b/sys/dev/usb/usb_compat_linux.c index a42f6364673..56caf0678a6 100644 --- a/sys/dev/usb/usb_compat_linux.c +++ b/sys/dev/usb/usb_compat_linux.c @@ -48,7 +48,6 @@ #include #include -#include #include #include diff --git a/sys/dev/usb/usb_hub.c b/sys/dev/usb/usb_hub.c index f82ff9baa93..46b7d8d540e 100644 --- a/sys/dev/usb/usb_hub.c +++ b/sys/dev/usb/usb_hub.c @@ -53,7 +53,6 @@ #include #include -#include #include #include @@ -2084,7 +2083,8 @@ usbd_transfer_power_ref(struct usb_xfer *xfer, int val) static uint8_t usb_peer_should_wakeup(struct usb_device *udev) { - return ((udev->power_mode == USB_POWER_MODE_ON) || + return (((udev->power_mode == USB_POWER_MODE_ON) && + (udev->flags.usb_mode == USB_MODE_HOST)) || (udev->driver_added_refcount != udev->bus->driver_added_refcount) || (udev->re_enumerate_wait != 0) || (udev->pwr_save.type_refs[UE_ISOCHRONOUS] != 0) || diff --git a/sys/dev/usb/usb_ioctl.h b/sys/dev/usb/usb_ioctl.h index 4b7850a1a65..277ba19ca3c 100644 --- a/sys/dev/usb/usb_ioctl.h +++ b/sys/dev/usb/usb_ioctl.h @@ -31,6 +31,7 @@ #ifndef USB_GLOBAL_INCLUDE_FILE #include +#include /* Building "kdump" depends on these includes */ @@ -43,6 +44,16 @@ #define USB_GENERIC_NAME "ugen" #define USB_TEMPLATE_SYSCTL "hw.usb.template" /* integer type */ +/* + * Align IOCTL structures to hide differences when running 32-bit + * programs under 64-bit kernels: + */ +#ifdef COMPAT_32BIT +#define USB_IOCTL_STRUCT_ALIGN(n) __aligned(n) +#else +#define USB_IOCTL_STRUCT_ALIGN(n) +#endif + /* Definition of valid template sysctl values */ enum { @@ -64,7 +75,7 @@ struct usb_read_dir { #endif uint32_t urd_startentry; uint32_t urd_maxlen; -}; +} USB_IOCTL_STRUCT_ALIGN(8); struct usb_ctl_request { #ifdef COMPAT_32BIT @@ -76,12 +87,12 @@ struct usb_ctl_request { uint16_t ucr_actlen; /* actual length transferred */ uint8_t ucr_addr; /* zero - currently not used */ struct usb_device_request ucr_request; -}; +} USB_IOCTL_STRUCT_ALIGN(8); struct usb_alt_interface { uint8_t uai_interface_index; uint8_t uai_alt_index; -}; +} USB_IOCTL_STRUCT_ALIGN(1); struct usb_gen_descriptor { #ifdef COMPAT_32BIT @@ -100,7 +111,7 @@ struct usb_gen_descriptor { uint8_t ugd_endpt_index; uint8_t ugd_report_type; uint8_t reserved[8]; -}; +} USB_IOCTL_STRUCT_ALIGN(8); struct usb_device_info { uint16_t udi_productNo; @@ -129,7 +140,7 @@ struct usb_device_info { char udi_vendor[128]; char udi_serial[64]; char udi_release[8]; -}; +} USB_IOCTL_STRUCT_ALIGN(2); #define USB_DEVICE_PORT_PATH_MAX 32 @@ -138,24 +149,24 @@ struct usb_device_port_path { uint8_t udp_index; /* which device index */ uint8_t udp_port_level; /* how many levels: 0, 1, 2 ... */ uint8_t udp_port_no[USB_DEVICE_PORT_PATH_MAX]; -}; +} USB_IOCTL_STRUCT_ALIGN(1); struct usb_device_stats { uint32_t uds_requests_ok[4]; /* Indexed by transfer type UE_XXX */ uint32_t uds_requests_fail[4]; /* Indexed by transfer type UE_XXX */ -}; +} USB_IOCTL_STRUCT_ALIGN(4); struct usb_fs_start { uint8_t ep_index; -}; +} USB_IOCTL_STRUCT_ALIGN(1); struct usb_fs_stop { uint8_t ep_index; -}; +} USB_IOCTL_STRUCT_ALIGN(1); struct usb_fs_complete { uint8_t ep_index; -}; +} USB_IOCTL_STRUCT_ALIGN(1); /* This structure is used for all endpoint types */ struct usb_fs_endpoint { @@ -188,7 +199,7 @@ struct usb_fs_endpoint { /* timeout value for no timeout */ #define USB_FS_TIMEOUT_NONE 0 int status; /* see USB_ERR_XXX */ -}; +} USB_IOCTL_STRUCT_ALIGN(8); struct usb_fs_init { /* userland pointer to endpoints structure */ @@ -199,11 +210,11 @@ struct usb_fs_init { #endif /* maximum number of endpoints */ uint8_t ep_index_max; -}; +} USB_IOCTL_STRUCT_ALIGN(8); struct usb_fs_uninit { uint8_t dummy; /* zero */ -}; +} USB_IOCTL_STRUCT_ALIGN(1); struct usb_fs_open { #define USB_FS_MAX_BUFSIZE (1 << 18) @@ -215,20 +226,20 @@ struct usb_fs_open { uint8_t dev_index; /* currently unused */ uint8_t ep_index; uint8_t ep_no; /* bEndpointNumber */ -}; +} USB_IOCTL_STRUCT_ALIGN(4); struct usb_fs_open_stream { struct usb_fs_open fs_open; - uint16_t stream_id; -}; + uint16_t stream_id; /* stream ID */ +} USB_IOCTL_STRUCT_ALIGN(4); struct usb_fs_close { uint8_t ep_index; -}; +} USB_IOCTL_STRUCT_ALIGN(1); struct usb_fs_clear_stall_sync { uint8_t ep_index; -}; +} USB_IOCTL_STRUCT_ALIGN(1); struct usb_gen_quirk { uint16_t index; /* Quirk Index */ @@ -238,11 +249,11 @@ struct usb_gen_quirk { uint16_t bcdDeviceHigh; /* High Device Revision */ uint16_t reserved[2]; /* - * String version of quirk including terminating zero. See UQ_XXX in - * "usb_quirk.h". + * String version of quirk including terminating zero. See + * UQ_XXX in "usb_quirk.h". */ char quirkname[64 - 14]; -}; +} USB_IOCTL_STRUCT_ALIGN(2); /* USB controller */ #define USB_REQUEST _IOWR('U', 1, struct usb_ctl_request) diff --git a/sys/dev/usb/usb_request.c b/sys/dev/usb/usb_request.c index 0a82462148d..cecc16724c5 100644 --- a/sys/dev/usb/usb_request.c +++ b/sys/dev/usb/usb_request.c @@ -51,7 +51,6 @@ #include #include #include -#include #include #define USB_DEBUG_VAR usb_debug diff --git a/sys/dev/usb/usbdevs b/sys/dev/usb/usbdevs index 8dee4f00d8c..e6a32aecf9d 100644 --- a/sys/dev/usb/usbdevs +++ b/sys/dev/usb/usbdevs @@ -515,11 +515,13 @@ vendor USR 0x0baf U.S. Robotics vendor AMBIT 0x0bb2 Ambit Microsystems vendor HTC 0x0bb4 HTC vendor REALTEK 0x0bda Realtek +vendor ERICSSON2 0x0bdb Ericsson vendor MEI 0x0bed MEI vendor ADDONICS2 0x0bf6 Addonics Technology vendor FSC 0x0bf8 Fujitsu Siemens Computers vendor AGATE 0x0c08 Agate Technologies vendor DMI 0x0c0b DMI +vendor CANYON 0x0c10 Canyon vendor ICOM 0x0c26 Icom Inc. vendor GNOTOMETRICS 0x0c33 GN Otometrics vendor CHICONY2 0x0c45 Chicony @@ -1545,6 +1547,7 @@ product DLINK DWLG122 0x3c00 DWL-G122 b1 Wireless Adapter product DLINK DUBE100B1 0x3c05 DUB-E100 rev B1 product DLINK RT2870 0x3c09 RT2870 product DLINK RT3072 0x3c0a RT3072 +product DLINK DWA127 0x3c1b DWA-127 Wireless Adapter product DLINK DSB650C 0x4000 10Mbps Ethernet product DLINK DSB650TX1 0x4001 10/100 Ethernet product DLINK DSB650TX 0x4002 10/100 Ethernet @@ -1774,6 +1777,7 @@ product FTDI SERIAL_8U232AM4 0x6004 8U232AM Serial product FTDI SERIAL_232RL 0x6006 FT232RL Serial product FTDI SERIAL_2232C 0x6010 FT2232C Dual port Serial product FTDI 232H 0x6014 FTDI compatible adapter +product FTDI 232EX 0x6015 FTDI compatible adapter product FTDI SERIAL_2232D 0x9e90 FT2232D Dual port Serial product FTDI SERIAL_4232H 0x6011 FT4232H Quad port Serial product FTDI BEAGLEBONE 0xa6d0 BeagleBone diff --git a/sys/dev/usb/wlan/if_run.c b/sys/dev/usb/wlan/if_run.c index aed07a205e4..5b4587fd6c1 100644 --- a/sys/dev/usb/wlan/if_run.c +++ b/sys/dev/usb/wlan/if_run.c @@ -171,6 +171,7 @@ static const STRUCT_USB_HOST_ID run_devs[] = { RUN_DEV(CYBERTAN, RT2870), RUN_DEV(DLINK, RT2870), RUN_DEV(DLINK, RT3072), + RUN_DEV(DLINK, DWA127), RUN_DEV(DLINK2, DWA130), RUN_DEV(DLINK2, RT2870_1), RUN_DEV(DLINK2, RT2870_2), diff --git a/sys/dev/virtio/balloon/virtio_balloon.c b/sys/dev/virtio/balloon/virtio_balloon.c index 9a87cf24d5e..a90a653901d 100644 --- a/sys/dev/virtio/balloon/virtio_balloon.c +++ b/sys/dev/virtio/balloon/virtio_balloon.c @@ -334,7 +334,7 @@ vtballoon_inflate(struct vtballoon_softc *sc, int npages) KASSERT(m->queue == PQ_NONE, ("%s: allocated page %p on queue", __func__, m)); - TAILQ_INSERT_TAIL(&sc->vtballoon_pages, m, pageq); + TAILQ_INSERT_TAIL(&sc->vtballoon_pages, m, plinks.q); } if (i > 0) @@ -362,8 +362,8 @@ vtballoon_deflate(struct vtballoon_softc *sc, int npages) sc->vtballoon_page_frames[i] = VM_PAGE_TO_PHYS(m) >> VIRTIO_BALLOON_PFN_SHIFT; - TAILQ_REMOVE(&sc->vtballoon_pages, m, pageq); - TAILQ_INSERT_TAIL(&free_pages, m, pageq); + TAILQ_REMOVE(&sc->vtballoon_pages, m, plinks.q); + TAILQ_INSERT_TAIL(&free_pages, m, plinks.q); } if (i > 0) { @@ -371,7 +371,7 @@ vtballoon_deflate(struct vtballoon_softc *sc, int npages) vtballoon_send_page_frames(sc, vq, i); while ((m = TAILQ_FIRST(&free_pages)) != NULL) { - TAILQ_REMOVE(&free_pages, m, pageq); + TAILQ_REMOVE(&free_pages, m, plinks.q); vtballoon_free_page(sc, m); } } diff --git a/sys/dev/virtio/network/if_vtnet.c b/sys/dev/virtio/network/if_vtnet.c index 89604d1b26b..93c60075ebc 100644 --- a/sys/dev/virtio/network/if_vtnet.c +++ b/sys/dev/virtio/network/if_vtnet.c @@ -29,10 +29,6 @@ #include __FBSDID("$FreeBSD$"); -#ifdef HAVE_KERNEL_OPTION_HEADERS -#include "opt_device_polling.h" -#endif - #include #include #include @@ -46,6 +42,9 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include +#include +#include #include @@ -63,6 +62,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include #include @@ -79,6 +79,9 @@ __FBSDID("$FreeBSD$"); #include "virtio_if.h" +#include "opt_inet.h" +#include "opt_inet6.h" + static int vtnet_modevent(module_t, int, void *); static int vtnet_probe(device_t); @@ -87,82 +90,139 @@ static int vtnet_detach(device_t); static int vtnet_suspend(device_t); static int vtnet_resume(device_t); static int vtnet_shutdown(device_t); +static int vtnet_attach_completed(device_t); static int vtnet_config_change(device_t); static void vtnet_negotiate_features(struct vtnet_softc *); +static void vtnet_setup_features(struct vtnet_softc *); +static int vtnet_init_rxq(struct vtnet_softc *, int); +static int vtnet_init_txq(struct vtnet_softc *, int); +static int vtnet_alloc_rxtx_queues(struct vtnet_softc *); +static void vtnet_free_rxtx_queues(struct vtnet_softc *); +static int vtnet_alloc_rx_filters(struct vtnet_softc *); +static void vtnet_free_rx_filters(struct vtnet_softc *); static int vtnet_alloc_virtqueues(struct vtnet_softc *); -static void vtnet_get_hwaddr(struct vtnet_softc *); -static void vtnet_set_hwaddr(struct vtnet_softc *); -static int vtnet_is_link_up(struct vtnet_softc *); -static void vtnet_update_link_status(struct vtnet_softc *); -static void vtnet_watchdog(struct vtnet_softc *); +static int vtnet_setup_interface(struct vtnet_softc *); static int vtnet_change_mtu(struct vtnet_softc *, int); static int vtnet_ioctl(struct ifnet *, u_long, caddr_t); -static int vtnet_init_rx_vq(struct vtnet_softc *); -static void vtnet_free_rx_mbufs(struct vtnet_softc *); -static void vtnet_free_tx_mbufs(struct vtnet_softc *); -static void vtnet_free_ctrl_vq(struct vtnet_softc *); +static int vtnet_rxq_populate(struct vtnet_rxq *); +static void vtnet_rxq_free_mbufs(struct vtnet_rxq *); +static struct mbuf * + vtnet_rx_alloc_buf(struct vtnet_softc *, int , struct mbuf **); +static int vtnet_rxq_replace_lro_nomgr_buf(struct vtnet_rxq *, + struct mbuf *, int); +static int vtnet_rxq_replace_buf(struct vtnet_rxq *, struct mbuf *, int); +static int vtnet_rxq_enqueue_buf(struct vtnet_rxq *, struct mbuf *); +static int vtnet_rxq_new_buf(struct vtnet_rxq *); +static int vtnet_rxq_csum(struct vtnet_rxq *, struct mbuf *, + struct virtio_net_hdr *); +static void vtnet_rxq_discard_merged_bufs(struct vtnet_rxq *, int); +static void vtnet_rxq_discard_buf(struct vtnet_rxq *, struct mbuf *); +static int vtnet_rxq_merged_eof(struct vtnet_rxq *, struct mbuf *, int); +static void vtnet_rxq_input(struct vtnet_rxq *, struct mbuf *, + struct virtio_net_hdr *); +static int vtnet_rxq_eof(struct vtnet_rxq *); +static void vtnet_rx_vq_intr(void *); +static void vtnet_rxq_tq_intr(void *, int); -#ifdef DEVICE_POLLING -static poll_handler_t vtnet_poll; +static void vtnet_txq_free_mbufs(struct vtnet_txq *); +static int vtnet_txq_offload_ctx(struct vtnet_txq *, struct mbuf *, + int *, int *, int *); +static int vtnet_txq_offload_tso(struct vtnet_txq *, struct mbuf *, int, + int, struct virtio_net_hdr *); +static struct mbuf * + vtnet_txq_offload(struct vtnet_txq *, struct mbuf *, + struct virtio_net_hdr *); +static int vtnet_txq_enqueue_buf(struct vtnet_txq *, struct mbuf **, + struct vtnet_tx_header *); +static int vtnet_txq_encap(struct vtnet_txq *, struct mbuf **); +#ifdef VTNET_LEGACY_TX +static void vtnet_start_locked(struct vtnet_txq *, struct ifnet *); +static void vtnet_start(struct ifnet *); +#else +static int vtnet_txq_mq_start_locked(struct vtnet_txq *, struct mbuf *); +static int vtnet_txq_mq_start(struct ifnet *, struct mbuf *); +static void vtnet_txq_tq_deferred(void *, int); +#endif +static void vtnet_txq_tq_intr(void *, int); +static void vtnet_txq_eof(struct vtnet_txq *); +static void vtnet_tx_vq_intr(void *); +static void vtnet_tx_start_all(struct vtnet_softc *); + +#ifndef VTNET_LEGACY_TX +static void vtnet_qflush(struct ifnet *); #endif -static struct mbuf * vtnet_alloc_rxbuf(struct vtnet_softc *, int, - struct mbuf **); -static int vtnet_replace_rxbuf(struct vtnet_softc *, - struct mbuf *, int); -static int vtnet_newbuf(struct vtnet_softc *); -static void vtnet_discard_merged_rxbuf(struct vtnet_softc *, int); -static void vtnet_discard_rxbuf(struct vtnet_softc *, struct mbuf *); -static int vtnet_enqueue_rxbuf(struct vtnet_softc *, struct mbuf *); -static void vtnet_vlan_tag_remove(struct mbuf *); -static int vtnet_rx_csum(struct vtnet_softc *, struct mbuf *, - struct virtio_net_hdr *); -static int vtnet_rxeof_merged(struct vtnet_softc *, struct mbuf *, int); -static int vtnet_rxeof(struct vtnet_softc *, int, int *); -static void vtnet_rx_vq_intr(void *); - -static void vtnet_txeof(struct vtnet_softc *); -static struct mbuf * vtnet_tx_offload(struct vtnet_softc *, struct mbuf *, - struct virtio_net_hdr *); -static int vtnet_enqueue_txbuf(struct vtnet_softc *, struct mbuf **, - struct vtnet_tx_header *); -static int vtnet_encap(struct vtnet_softc *, struct mbuf **); -static void vtnet_start_locked(struct ifnet *); -static void vtnet_start(struct ifnet *); +static int vtnet_watchdog(struct vtnet_txq *); +static void vtnet_rxq_accum_stats(struct vtnet_rxq *, + struct vtnet_rxq_stats *); +static void vtnet_txq_accum_stats(struct vtnet_txq *, + struct vtnet_txq_stats *); +static void vtnet_accumulate_stats(struct vtnet_softc *); static void vtnet_tick(void *); -static void vtnet_tx_vq_intr(void *); +static void vtnet_start_taskqueues(struct vtnet_softc *); +static void vtnet_free_taskqueues(struct vtnet_softc *); +static void vtnet_drain_taskqueues(struct vtnet_softc *); + +static void vtnet_drain_rxtx_queues(struct vtnet_softc *); +static void vtnet_stop_rendezvous(struct vtnet_softc *); static void vtnet_stop(struct vtnet_softc *); +static int vtnet_virtio_reinit(struct vtnet_softc *); +static void vtnet_init_rx_filters(struct vtnet_softc *); +static int vtnet_init_rx_queues(struct vtnet_softc *); +static int vtnet_init_tx_queues(struct vtnet_softc *); +static int vtnet_init_rxtx_queues(struct vtnet_softc *); +static void vtnet_set_active_vq_pairs(struct vtnet_softc *); static int vtnet_reinit(struct vtnet_softc *); static void vtnet_init_locked(struct vtnet_softc *); static void vtnet_init(void *); +static void vtnet_free_ctrl_vq(struct vtnet_softc *); static void vtnet_exec_ctrl_cmd(struct vtnet_softc *, void *, struct sglist *, int, int); - -static void vtnet_rx_filter(struct vtnet_softc *sc); +static int vtnet_ctrl_mac_cmd(struct vtnet_softc *, uint8_t *); +static int vtnet_ctrl_mq_cmd(struct vtnet_softc *, uint16_t); static int vtnet_ctrl_rx_cmd(struct vtnet_softc *, int, int); static int vtnet_set_promisc(struct vtnet_softc *, int); static int vtnet_set_allmulti(struct vtnet_softc *, int); +static void vtnet_attach_disable_promisc(struct vtnet_softc *); +static void vtnet_rx_filter(struct vtnet_softc *); static void vtnet_rx_filter_mac(struct vtnet_softc *); - static int vtnet_exec_vlan_filter(struct vtnet_softc *, int, uint16_t); static void vtnet_rx_filter_vlan(struct vtnet_softc *); -static void vtnet_set_vlan_filter(struct vtnet_softc *, int, uint16_t); +static void vtnet_update_vlan_filter(struct vtnet_softc *, int, uint16_t); static void vtnet_register_vlan(void *, struct ifnet *, uint16_t); static void vtnet_unregister_vlan(void *, struct ifnet *, uint16_t); +static int vtnet_is_link_up(struct vtnet_softc *); +static void vtnet_update_link_status(struct vtnet_softc *); static int vtnet_ifmedia_upd(struct ifnet *); static void vtnet_ifmedia_sts(struct ifnet *, struct ifmediareq *); +static void vtnet_get_hwaddr(struct vtnet_softc *); +static void vtnet_set_hwaddr(struct vtnet_softc *); +static void vtnet_vlan_tag_remove(struct mbuf *); -static void vtnet_add_statistics(struct vtnet_softc *); +static void vtnet_setup_rxq_sysctl(struct sysctl_ctx_list *, + struct sysctl_oid_list *, struct vtnet_rxq *); +static void vtnet_setup_txq_sysctl(struct sysctl_ctx_list *, + struct sysctl_oid_list *, struct vtnet_txq *); +static void vtnet_setup_queue_sysctl(struct vtnet_softc *); +static void vtnet_setup_sysctl(struct vtnet_softc *); -static int vtnet_enable_rx_intr(struct vtnet_softc *); -static int vtnet_enable_tx_intr(struct vtnet_softc *); -static void vtnet_disable_rx_intr(struct vtnet_softc *); -static void vtnet_disable_tx_intr(struct vtnet_softc *); +static int vtnet_rxq_enable_intr(struct vtnet_rxq *); +static void vtnet_rxq_disable_intr(struct vtnet_rxq *); +static int vtnet_txq_enable_intr(struct vtnet_txq *); +static void vtnet_txq_disable_intr(struct vtnet_txq *); +static void vtnet_enable_rx_interrupts(struct vtnet_softc *); +static void vtnet_enable_tx_interrupts(struct vtnet_softc *); +static void vtnet_enable_interrupts(struct vtnet_softc *); +static void vtnet_disable_rx_interrupts(struct vtnet_softc *); +static void vtnet_disable_tx_interrupts(struct vtnet_softc *); +static void vtnet_disable_interrupts(struct vtnet_softc *); + +static int vtnet_tunable_int(struct vtnet_softc *, const char *, int); /* Tunables. */ static int vtnet_csum_disable = 0; @@ -171,16 +231,25 @@ static int vtnet_tso_disable = 0; TUNABLE_INT("hw.vtnet.tso_disable", &vtnet_tso_disable); static int vtnet_lro_disable = 0; TUNABLE_INT("hw.vtnet.lro_disable", &vtnet_lro_disable); +static int vtnet_mq_disable = 0; +TUNABLE_INT("hw.vtnet.mq_disable", &vtnet_mq_disable); +static int vtnet_mq_max_pairs = 0; +TUNABLE_INT("hw.vtnet.mq_max_pairs", &vtnet_mq_max_pairs); +static int vtnet_rx_process_limit = 512; +TUNABLE_INT("hw.vtnet.rx_process_limit", &vtnet_rx_process_limit); /* - * Reducing the number of transmit completed interrupts can - * improve performance. To do so, the define below keeps the - * Tx vq interrupt disabled and adds calls to vtnet_txeof() - * in the start and watchdog paths. The price to pay for this - * is the m_free'ing of transmitted mbufs may be delayed until - * the watchdog fires. + * Reducing the number of transmit completed interrupts can improve + * performance. To do so, the define below keeps the Tx vq interrupt + * disabled and adds calls to vtnet_txeof() in the start and watchdog + * paths. The price to pay for this is the m_free'ing of transmitted + * mbufs may be delayed until the watchdog fires. + * + * BMV: Reintroduce this later as a run-time option, if it makes + * sense after the EVENT_IDX feature is supported. + * + * #define VTNET_TX_INTR_MODERATION */ -#define VTNET_TX_INTR_MODERATION static uma_zone_t vtnet_tx_header_zone; @@ -203,21 +272,25 @@ static struct virtio_feature_desc vtnet_feature_desc[] = { { VIRTIO_NET_F_CTRL_RX, "RxMode" }, { VIRTIO_NET_F_CTRL_VLAN, "VLanFilter" }, { VIRTIO_NET_F_CTRL_RX_EXTRA, "RxModeExtra" }, + { VIRTIO_NET_F_GUEST_ANNOUNCE, "GuestAnnounce" }, + { VIRTIO_NET_F_MQ, "Multiqueue" }, + { VIRTIO_NET_F_CTRL_MAC_ADDR, "SetMacAddress" }, { 0, NULL } }; static device_method_t vtnet_methods[] = { /* Device methods. */ - DEVMETHOD(device_probe, vtnet_probe), - DEVMETHOD(device_attach, vtnet_attach), - DEVMETHOD(device_detach, vtnet_detach), - DEVMETHOD(device_suspend, vtnet_suspend), - DEVMETHOD(device_resume, vtnet_resume), - DEVMETHOD(device_shutdown, vtnet_shutdown), + DEVMETHOD(device_probe, vtnet_probe), + DEVMETHOD(device_attach, vtnet_attach), + DEVMETHOD(device_detach, vtnet_detach), + DEVMETHOD(device_suspend, vtnet_suspend), + DEVMETHOD(device_resume, vtnet_resume), + DEVMETHOD(device_shutdown, vtnet_shutdown), /* VirtIO methods. */ - DEVMETHOD(virtio_config_change, vtnet_config_change), + DEVMETHOD(virtio_attach_completed, vtnet_attach_completed), + DEVMETHOD(virtio_config_change, vtnet_config_change), DEVMETHOD_END }; @@ -282,56 +355,31 @@ static int vtnet_attach(device_t dev) { struct vtnet_softc *sc; - struct ifnet *ifp; - int tx_size, error; + int error; sc = device_get_softc(dev); sc->vtnet_dev = dev; - VTNET_LOCK_INIT(sc); - callout_init_mtx(&sc->vtnet_tick_ch, VTNET_MTX(sc), 0); - - ifmedia_init(&sc->vtnet_media, IFM_IMASK, vtnet_ifmedia_upd, - vtnet_ifmedia_sts); - ifmedia_add(&sc->vtnet_media, VTNET_MEDIATYPE, 0, NULL); - ifmedia_set(&sc->vtnet_media, VTNET_MEDIATYPE); - - vtnet_add_statistics(sc); - + /* Register our feature descriptions. */ virtio_set_feature_desc(dev, vtnet_feature_desc); - vtnet_negotiate_features(sc); - if (virtio_with_feature(dev, VIRTIO_NET_F_MRG_RXBUF)) { - sc->vtnet_flags |= VTNET_FLAG_MRG_RXBUFS; - sc->vtnet_hdr_size = sizeof(struct virtio_net_hdr_mrg_rxbuf); - } else - sc->vtnet_hdr_size = sizeof(struct virtio_net_hdr); + VTNET_CORE_LOCK_INIT(sc); + callout_init_mtx(&sc->vtnet_tick_ch, VTNET_CORE_MTX(sc), 0); - sc->vtnet_rx_mbuf_size = MCLBYTES; - sc->vtnet_rx_mbuf_count = VTNET_NEEDED_RX_MBUFS(sc); + vtnet_setup_sysctl(sc); + vtnet_setup_features(sc); - if (virtio_with_feature(dev, VIRTIO_NET_F_CTRL_VQ)) { - sc->vtnet_flags |= VTNET_FLAG_CTRL_VQ; - - if (virtio_with_feature(dev, VIRTIO_NET_F_CTRL_RX)) { - sc->vtnet_mac_filter = malloc( - sizeof(struct vtnet_mac_filter), M_DEVBUF, - M_NOWAIT | M_ZERO); - if (sc->vtnet_mac_filter == NULL) { - device_printf(dev, - "cannot allocate mac filter table\n"); - error = ENOMEM; - goto fail; - } - - sc->vtnet_flags |= VTNET_FLAG_CTRL_RX; - } - - if (virtio_with_feature(dev, VIRTIO_NET_F_CTRL_VLAN)) - sc->vtnet_flags |= VTNET_FLAG_VLAN_FILTER; + error = vtnet_alloc_rx_filters(sc); + if (error) { + device_printf(dev, "cannot allocate Rx filters\n"); + goto fail; } - vtnet_get_hwaddr(sc); + error = vtnet_alloc_rxtx_queues(sc); + if (error) { + device_printf(dev, "cannot allocate queues\n"); + goto fail; + } error = vtnet_alloc_virtqueues(sc); if (error) { @@ -339,28 +387,534 @@ vtnet_attach(device_t dev) goto fail; } - ifp = sc->vtnet_ifp = if_alloc(IFT_ETHER); - if (ifp == NULL) { - device_printf(dev, "cannot allocate ifnet structure\n"); - error = ENOSPC; + error = vtnet_setup_interface(sc); + if (error) { + device_printf(dev, "cannot setup interface\n"); goto fail; } - ifp->if_softc = sc; + error = virtio_setup_intr(dev, INTR_TYPE_NET); + if (error) { + device_printf(dev, "cannot setup virtqueue interrupts\n"); + /* BMV: This will crash if during boot! */ + ether_ifdetach(sc->vtnet_ifp); + goto fail; + } + + vtnet_start_taskqueues(sc); + +fail: + if (error) + vtnet_detach(dev); + + return (error); +} + +static int +vtnet_detach(device_t dev) +{ + struct vtnet_softc *sc; + struct ifnet *ifp; + + sc = device_get_softc(dev); + ifp = sc->vtnet_ifp; + + if (device_is_attached(dev)) { + VTNET_CORE_LOCK(sc); + vtnet_stop(sc); + VTNET_CORE_UNLOCK(sc); + + callout_drain(&sc->vtnet_tick_ch); + vtnet_drain_taskqueues(sc); + + ether_ifdetach(ifp); + } + + vtnet_free_taskqueues(sc); + + if (sc->vtnet_vlan_attach != NULL) { + EVENTHANDLER_DEREGISTER(vlan_config, sc->vtnet_vlan_attach); + sc->vtnet_vlan_attach = NULL; + } + if (sc->vtnet_vlan_detach != NULL) { + EVENTHANDLER_DEREGISTER(vlan_unconfg, sc->vtnet_vlan_detach); + sc->vtnet_vlan_detach = NULL; + } + + ifmedia_removeall(&sc->vtnet_media); + + if (ifp != NULL) { + if_free(ifp); + sc->vtnet_ifp = NULL; + } + + vtnet_free_rxtx_queues(sc); + vtnet_free_rx_filters(sc); + + if (sc->vtnet_ctrl_vq != NULL) + vtnet_free_ctrl_vq(sc); + + VTNET_CORE_LOCK_DESTROY(sc); + + return (0); +} + +static int +vtnet_suspend(device_t dev) +{ + struct vtnet_softc *sc; + + sc = device_get_softc(dev); + + VTNET_CORE_LOCK(sc); + vtnet_stop(sc); + sc->vtnet_flags |= VTNET_FLAG_SUSPENDED; + VTNET_CORE_UNLOCK(sc); + + return (0); +} + +static int +vtnet_resume(device_t dev) +{ + struct vtnet_softc *sc; + struct ifnet *ifp; + + sc = device_get_softc(dev); + ifp = sc->vtnet_ifp; + + VTNET_CORE_LOCK(sc); + if (ifp->if_flags & IFF_UP) + vtnet_init_locked(sc); + sc->vtnet_flags &= ~VTNET_FLAG_SUSPENDED; + VTNET_CORE_UNLOCK(sc); + + return (0); +} + +static int +vtnet_shutdown(device_t dev) +{ + + /* + * Suspend already does all of what we need to + * do here; we just never expect to be resumed. + */ + return (vtnet_suspend(dev)); +} + +static int +vtnet_attach_completed(device_t dev) +{ + + vtnet_attach_disable_promisc(device_get_softc(dev)); + + return (0); +} + +static int +vtnet_config_change(device_t dev) +{ + struct vtnet_softc *sc; + + sc = device_get_softc(dev); + + VTNET_CORE_LOCK(sc); + vtnet_update_link_status(sc); + if (sc->vtnet_link_active != 0) + vtnet_tx_start_all(sc); + VTNET_CORE_UNLOCK(sc); + + return (0); +} + +static void +vtnet_negotiate_features(struct vtnet_softc *sc) +{ + device_t dev; + uint64_t mask, features; + + dev = sc->vtnet_dev; + mask = 0; + + /* + * TSO and LRO are only available when their corresponding checksum + * offload feature is also negotiated. + */ + if (vtnet_tunable_int(sc, "csum_disable", vtnet_csum_disable)) { + mask |= VIRTIO_NET_F_CSUM | VIRTIO_NET_F_GUEST_CSUM; + mask |= VTNET_TSO_FEATURES | VTNET_LRO_FEATURES; + } + if (vtnet_tunable_int(sc, "tso_disable", vtnet_tso_disable)) + mask |= VTNET_TSO_FEATURES; + if (vtnet_tunable_int(sc, "lro_disable", vtnet_lro_disable)) + mask |= VTNET_LRO_FEATURES; + if (vtnet_tunable_int(sc, "mq_disable", vtnet_mq_disable)) + mask |= VIRTIO_NET_F_MQ; +#ifdef VTNET_LEGACY_TX + mask |= VIRTIO_NET_F_MQ; +#endif + + features = VTNET_FEATURES & ~mask; + sc->vtnet_features = virtio_negotiate_features(dev, features); + + if (virtio_with_feature(dev, VTNET_LRO_FEATURES) == 0) + return; + if (virtio_with_feature(dev, VIRTIO_NET_F_MRG_RXBUF)) + return; + + /* + * LRO without mergeable buffers requires special care. This is not + * ideal because every receive buffer must be large enough to hold + * the maximum TCP packet, the Ethernet header, and the header. This + * requires up to 34 descriptors with MCLBYTES clusters. If we do + * not have indirect descriptors, LRO is disabled since the virtqueue + * will not contain very many receive buffers. + */ + if (virtio_with_feature(dev, VIRTIO_RING_F_INDIRECT_DESC) == 0) { + device_printf(dev, + "LRO disabled due to both mergeable buffers and indirect " + "descriptors not negotiated\n"); + + features &= ~VTNET_LRO_FEATURES; + sc->vtnet_features = virtio_negotiate_features(dev, features); + } else + sc->vtnet_flags |= VTNET_FLAG_LRO_NOMRG; +} + +static void +vtnet_setup_features(struct vtnet_softc *sc) +{ + device_t dev; + int max_pairs, max; + + dev = sc->vtnet_dev; + + vtnet_negotiate_features(sc); + + if (virtio_with_feature(dev, VIRTIO_RING_F_EVENT_IDX)) + sc->vtnet_flags |= VTNET_FLAG_EVENT_IDX; + + if (virtio_with_feature(dev, VIRTIO_NET_F_MAC)) { + /* This feature should always be negotiated. */ + sc->vtnet_flags |= VTNET_FLAG_MAC; + } + + if (virtio_with_feature(dev, VIRTIO_NET_F_MRG_RXBUF)) { + sc->vtnet_flags |= VTNET_FLAG_MRG_RXBUFS; + sc->vtnet_hdr_size = sizeof(struct virtio_net_hdr_mrg_rxbuf); + } else + sc->vtnet_hdr_size = sizeof(struct virtio_net_hdr); + + if (virtio_with_feature(dev, VIRTIO_NET_F_CTRL_VQ)) { + sc->vtnet_flags |= VTNET_FLAG_CTRL_VQ; + + if (virtio_with_feature(dev, VIRTIO_NET_F_CTRL_RX)) + sc->vtnet_flags |= VTNET_FLAG_CTRL_RX; + if (virtio_with_feature(dev, VIRTIO_NET_F_CTRL_VLAN)) + sc->vtnet_flags |= VTNET_FLAG_VLAN_FILTER; + if (virtio_with_feature(dev, VIRTIO_NET_F_CTRL_MAC_ADDR)) + sc->vtnet_flags |= VTNET_FLAG_CTRL_MAC; + } + + if (virtio_with_feature(dev, VIRTIO_NET_F_MQ) && + sc->vtnet_flags & VTNET_FLAG_CTRL_VQ) { + max_pairs = virtio_read_dev_config_2(dev, + offsetof(struct virtio_net_config, max_virtqueue_pairs)); + if (max_pairs < VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MIN || + max_pairs > VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MAX) + max_pairs = 1; + } else + max_pairs = 1; + + if (max_pairs > 1) { + /* + * Limit the maximum number of queue pairs to the number of + * CPUs or the configured maximum. The actual number of + * queues that get used may be less. + */ + max = vtnet_tunable_int(sc, "mq_max_pairs", vtnet_mq_max_pairs); + if (max > 0 && max_pairs > max) + max_pairs = max; + if (max_pairs > mp_ncpus) + max_pairs = mp_ncpus; + if (max_pairs > VTNET_MAX_QUEUE_PAIRS) + max_pairs = VTNET_MAX_QUEUE_PAIRS; + if (max_pairs > 1) + sc->vtnet_flags |= VTNET_FLAG_MULTIQ; + } + + sc->vtnet_max_vq_pairs = max_pairs; +} + +static int +vtnet_init_rxq(struct vtnet_softc *sc, int id) +{ + struct vtnet_rxq *rxq; + + rxq = &sc->vtnet_rxqs[id]; + + snprintf(rxq->vtnrx_name, sizeof(rxq->vtnrx_name), "%s-rx%d", + device_get_nameunit(sc->vtnet_dev), id); + mtx_init(&rxq->vtnrx_mtx, rxq->vtnrx_name, NULL, MTX_DEF); + + rxq->vtnrx_sc = sc; + rxq->vtnrx_id = id; + + TASK_INIT(&rxq->vtnrx_intrtask, 0, vtnet_rxq_tq_intr, rxq); + rxq->vtnrx_tq = taskqueue_create(rxq->vtnrx_name, M_NOWAIT, + taskqueue_thread_enqueue, &rxq->vtnrx_tq); + + return (rxq->vtnrx_tq == NULL ? ENOMEM : 0); +} + +static int +vtnet_init_txq(struct vtnet_softc *sc, int id) +{ + struct vtnet_txq *txq; + + txq = &sc->vtnet_txqs[id]; + + snprintf(txq->vtntx_name, sizeof(txq->vtntx_name), "%s-tx%d", + device_get_nameunit(sc->vtnet_dev), id); + mtx_init(&txq->vtntx_mtx, txq->vtntx_name, NULL, MTX_DEF); + + txq->vtntx_sc = sc; + txq->vtntx_id = id; + +#ifndef VTNET_LEGACY_TX + txq->vtntx_br = buf_ring_alloc(VTNET_DEFAULT_BUFRING_SIZE, M_DEVBUF, + M_NOWAIT, &txq->vtntx_mtx); + if (txq->vtntx_br == NULL) + return (ENOMEM); + + TASK_INIT(&txq->vtntx_defrtask, 0, vtnet_txq_tq_deferred, txq); +#endif + TASK_INIT(&txq->vtntx_intrtask, 0, vtnet_txq_tq_intr, txq); + txq->vtntx_tq = taskqueue_create(txq->vtntx_name, M_NOWAIT, + taskqueue_thread_enqueue, &txq->vtntx_tq); + if (txq->vtntx_tq == NULL) + return (ENOMEM); + + return (0); +} + +static int +vtnet_alloc_rxtx_queues(struct vtnet_softc *sc) +{ + int i, npairs, error; + + npairs = sc->vtnet_max_vq_pairs; + + sc->vtnet_rxqs = malloc(sizeof(struct vtnet_rxq) * npairs, M_DEVBUF, + M_NOWAIT | M_ZERO); + sc->vtnet_txqs = malloc(sizeof(struct vtnet_txq) * npairs, M_DEVBUF, + M_NOWAIT | M_ZERO); + if (sc->vtnet_rxqs == NULL || sc->vtnet_txqs == NULL) + return (ENOMEM); + + for (i = 0; i < npairs; i++) { + error = vtnet_init_rxq(sc, i); + if (error) + return (error); + error = vtnet_init_txq(sc, i); + if (error) + return (error); + } + + vtnet_setup_queue_sysctl(sc); + + return (0); +} + +static void +vtnet_destroy_rxq(struct vtnet_rxq *rxq) +{ + + rxq->vtnrx_sc = NULL; + rxq->vtnrx_id = -1; + + if (mtx_initialized(&rxq->vtnrx_mtx) != 0) + mtx_destroy(&rxq->vtnrx_mtx); +} + +static void +vtnet_destroy_txq(struct vtnet_txq *txq) +{ + + txq->vtntx_sc = NULL; + txq->vtntx_id = -1; + +#ifndef VTNET_LEGACY_TX + if (txq->vtntx_br != NULL) { + buf_ring_free(txq->vtntx_br, M_DEVBUF); + txq->vtntx_br = NULL; + } +#endif + + if (mtx_initialized(&txq->vtntx_mtx) != 0) + mtx_destroy(&txq->vtntx_mtx); +} + +static void +vtnet_free_rxtx_queues(struct vtnet_softc *sc) +{ + int i; + + if (sc->vtnet_rxqs != NULL) { + for (i = 0; i < sc->vtnet_max_vq_pairs; i++) + vtnet_destroy_rxq(&sc->vtnet_rxqs[i]); + free(sc->vtnet_rxqs, M_DEVBUF); + sc->vtnet_rxqs = NULL; + } + + if (sc->vtnet_txqs != NULL) { + for (i = 0; i < sc->vtnet_max_vq_pairs; i++) + vtnet_destroy_txq(&sc->vtnet_txqs[i]); + free(sc->vtnet_txqs, M_DEVBUF); + sc->vtnet_txqs = NULL; + } +} + +static int +vtnet_alloc_rx_filters(struct vtnet_softc *sc) +{ + + if (sc->vtnet_flags & VTNET_FLAG_CTRL_RX) { + sc->vtnet_mac_filter = malloc(sizeof(struct vtnet_mac_filter), + M_DEVBUF, M_NOWAIT | M_ZERO); + if (sc->vtnet_mac_filter == NULL) + return (ENOMEM); + } + + if (sc->vtnet_flags & VTNET_FLAG_VLAN_FILTER) { + sc->vtnet_vlan_filter = malloc(sizeof(uint32_t) * + VTNET_VLAN_FILTER_NWORDS, M_DEVBUF, M_NOWAIT | M_ZERO); + if (sc->vtnet_vlan_filter == NULL) + return (ENOMEM); + } + + return (0); +} + +static void +vtnet_free_rx_filters(struct vtnet_softc *sc) +{ + + if (sc->vtnet_mac_filter != NULL) { + free(sc->vtnet_mac_filter, M_DEVBUF); + sc->vtnet_mac_filter = NULL; + } + + if (sc->vtnet_vlan_filter != NULL) { + free(sc->vtnet_vlan_filter, M_DEVBUF); + sc->vtnet_vlan_filter = NULL; + } +} + +static int +vtnet_alloc_virtqueues(struct vtnet_softc *sc) +{ + device_t dev; + struct vq_alloc_info *info; + struct vtnet_rxq *rxq; + struct vtnet_txq *txq; + int i, idx, flags, nvqs, rxsegs, error; + + dev = sc->vtnet_dev; + flags = 0; + + /* + * Indirect descriptors are not needed for the Rx virtqueue when + * mergeable buffers are negotiated. The header is placed inline + * with the data, not in a separate descriptor, and mbuf clusters + * are always physically contiguous. + */ + if (sc->vtnet_flags & VTNET_FLAG_MRG_RXBUFS) + rxsegs = 0; + else if (sc->vtnet_flags & VTNET_FLAG_LRO_NOMRG) + rxsegs = VTNET_MAX_RX_SEGS; + else + rxsegs = VTNET_MIN_RX_SEGS; + + nvqs = sc->vtnet_max_vq_pairs * 2; + if (sc->vtnet_flags & VTNET_FLAG_CTRL_VQ) + nvqs++; + + info = malloc(sizeof(struct vq_alloc_info) * nvqs , M_TEMP, M_NOWAIT); + if (info == NULL) + return (ENOMEM); + + for (i = 0, idx = 0; i < sc->vtnet_max_vq_pairs; i++, idx+=2) { + rxq = &sc->vtnet_rxqs[i]; + VQ_ALLOC_INFO_INIT(&info[idx], rxsegs, + vtnet_rx_vq_intr, rxq, &rxq->vtnrx_vq, + "%s-%d rx", device_get_nameunit(dev), rxq->vtnrx_id); + + txq = &sc->vtnet_txqs[i]; + VQ_ALLOC_INFO_INIT(&info[idx+1], VTNET_MAX_TX_SEGS, + vtnet_tx_vq_intr, txq, &txq->vtntx_vq, + "%s-%d tx", device_get_nameunit(dev), txq->vtntx_id); + } + + if (sc->vtnet_flags & VTNET_FLAG_CTRL_VQ) { + VQ_ALLOC_INFO_INIT(&info[idx], 0, NULL, NULL, + &sc->vtnet_ctrl_vq, "%s ctrl", device_get_nameunit(dev)); + } + + /* + * Enable interrupt binding if this is multiqueue. This only matters + * when per-vq MSIX is available. + */ + if (sc->vtnet_flags & VTNET_FLAG_MULTIQ) + flags |= 0; + + error = virtio_alloc_virtqueues(dev, flags, nvqs, info); + free(info, M_TEMP); + + return (error); +} + +static int +vtnet_setup_interface(struct vtnet_softc *sc) +{ + device_t dev; + struct ifnet *ifp; + int limit; + + dev = sc->vtnet_dev; + + ifp = sc->vtnet_ifp = if_alloc(IFT_ETHER); + if (ifp == NULL) { + device_printf(dev, "cannot allocate ifnet structure\n"); + return (ENOSPC); + } + if_initname(ifp, device_get_name(dev), device_get_unit(dev)); + if_initbaudrate(ifp, IF_Gbps(10)); /* Approx. */ + ifp->if_softc = sc; ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; ifp->if_init = vtnet_init; - ifp->if_start = vtnet_start; ifp->if_ioctl = vtnet_ioctl; - sc->vtnet_rx_size = virtqueue_size(sc->vtnet_rx_vq); - sc->vtnet_rx_process_limit = sc->vtnet_rx_size; - - tx_size = virtqueue_size(sc->vtnet_tx_vq); - sc->vtnet_tx_size = tx_size; - IFQ_SET_MAXLEN(&ifp->if_snd, tx_size - 1); - ifp->if_snd.ifq_drv_maxlen = tx_size - 1; +#ifndef VTNET_LEGACY_TX + ifp->if_transmit = vtnet_txq_mq_start; + ifp->if_qflush = vtnet_qflush; +#else + struct virtqueue *vq = sc->vtnet_txqs[0].vtntx_vq; + ifp->if_start = vtnet_start; + IFQ_SET_MAXLEN(&ifp->if_snd, virtqueue_size(vq) - 1); + ifp->if_snd.ifq_drv_maxlen = virtqueue_size(vq) - 1; IFQ_SET_READY(&ifp->if_snd); +#endif + + ifmedia_init(&sc->vtnet_media, IFM_IMASK, vtnet_ifmedia_upd, + vtnet_ifmedia_sts); + ifmedia_add(&sc->vtnet_media, VTNET_MEDIATYPE, 0, NULL); + ifmedia_set(&sc->vtnet_media, VTNET_MEDIATYPE); + + /* Read (or generate) the MAC address for the adapter. */ + vtnet_get_hwaddr(sc); ether_ifattach(ifp, sc->vtnet_hwaddr); @@ -372,26 +926,26 @@ vtnet_attach(device_t dev) ifp->if_capabilities |= IFCAP_JUMBO_MTU | IFCAP_VLAN_MTU; if (virtio_with_feature(dev, VIRTIO_NET_F_CSUM)) { - ifp->if_capabilities |= IFCAP_TXCSUM; + ifp->if_capabilities |= IFCAP_TXCSUM | IFCAP_TXCSUM_IPV6; + + if (virtio_with_feature(dev, VIRTIO_NET_F_GSO)) { + ifp->if_capabilities |= IFCAP_TSO4 | IFCAP_TSO6; + sc->vtnet_flags |= VTNET_FLAG_TSO_ECN; + } else { + if (virtio_with_feature(dev, VIRTIO_NET_F_HOST_TSO4)) + ifp->if_capabilities |= IFCAP_TSO4; + if (virtio_with_feature(dev, VIRTIO_NET_F_HOST_TSO6)) + ifp->if_capabilities |= IFCAP_TSO6; + if (virtio_with_feature(dev, VIRTIO_NET_F_HOST_ECN)) + sc->vtnet_flags |= VTNET_FLAG_TSO_ECN; + } - if (virtio_with_feature(dev, VIRTIO_NET_F_HOST_TSO4)) - ifp->if_capabilities |= IFCAP_TSO4; - if (virtio_with_feature(dev, VIRTIO_NET_F_HOST_TSO6)) - ifp->if_capabilities |= IFCAP_TSO6; if (ifp->if_capabilities & IFCAP_TSO) ifp->if_capabilities |= IFCAP_VLAN_HWTSO; - - if (virtio_with_feature(dev, VIRTIO_NET_F_HOST_ECN)) - sc->vtnet_flags |= VTNET_FLAG_TSO_ECN; } - if (virtio_with_feature(dev, VIRTIO_NET_F_GUEST_CSUM)) { - ifp->if_capabilities |= IFCAP_RXCSUM; - - if (virtio_with_feature(dev, VIRTIO_NET_F_GUEST_TSO4) || - virtio_with_feature(dev, VIRTIO_NET_F_GUEST_TSO6)) - ifp->if_capabilities |= IFCAP_LRO; - } + if (virtio_with_feature(dev, VIRTIO_NET_F_GUEST_CSUM)) + ifp->if_capabilities |= IFCAP_RXCSUM | IFCAP_RXCSUM_IPV6; if (ifp->if_capabilities & IFCAP_HWCSUM) { /* @@ -410,6 +964,12 @@ vtnet_attach(device_t dev) * Capabilities after here are not enabled by default. */ + if (ifp->if_capabilities & IFCAP_RXCSUM) { + if (virtio_with_feature(dev, VIRTIO_NET_F_GUEST_TSO4) || + virtio_with_feature(dev, VIRTIO_NET_F_GUEST_TSO6)) + ifp->if_capabilities |= IFCAP_LRO; + } + if (sc->vtnet_flags & VTNET_FLAG_VLAN_FILTER) { ifp->if_capabilities |= IFCAP_VLAN_HWFILTER; @@ -419,349 +979,54 @@ vtnet_attach(device_t dev) vtnet_unregister_vlan, sc, EVENTHANDLER_PRI_FIRST); } -#ifdef DEVICE_POLLING - ifp->if_capabilities |= IFCAP_POLLING; -#endif + limit = vtnet_tunable_int(sc, "rx_process_limit", + vtnet_rx_process_limit); + if (limit < 0) + limit = INT_MAX; + sc->vtnet_rx_process_limit = limit; - error = virtio_setup_intr(dev, INTR_TYPE_NET); - if (error) { - device_printf(dev, "cannot setup virtqueue interrupts\n"); - ether_ifdetach(ifp); - goto fail; - } + return (0); +} + +static int +vtnet_change_mtu(struct vtnet_softc *sc, int new_mtu) +{ + struct ifnet *ifp; + int frame_size, clsize; + + ifp = sc->vtnet_ifp; + + if (new_mtu < ETHERMIN || new_mtu > VTNET_MAX_MTU) + return (EINVAL); + + frame_size = sc->vtnet_hdr_size + sizeof(struct ether_vlan_header) + + new_mtu; /* - * Device defaults to promiscuous mode for backwards - * compatibility. Turn it off if possible. + * Based on the new MTU (and hence frame size) determine which + * cluster size is most appropriate for the receive queues. */ - if (sc->vtnet_flags & VTNET_FLAG_CTRL_RX) { - VTNET_LOCK(sc); - if (vtnet_set_promisc(sc, 0) != 0) { - ifp->if_flags |= IFF_PROMISC; - device_printf(dev, - "cannot disable promiscuous mode\n"); - } - VTNET_UNLOCK(sc); + if (frame_size <= MCLBYTES) { + clsize = MCLBYTES; + } else if ((sc->vtnet_flags & VTNET_FLAG_MRG_RXBUFS) == 0) { + /* Avoid going past 9K jumbos. */ + if (frame_size > MJUM9BYTES) + return (EINVAL); + clsize = MJUM9BYTES; } else - ifp->if_flags |= IFF_PROMISC; + clsize = MJUMPAGESIZE; -fail: - if (error) - vtnet_detach(dev); + ifp->if_mtu = new_mtu; + sc->vtnet_rx_new_clsize = clsize; - return (error); -} - -static int -vtnet_detach(device_t dev) -{ - struct vtnet_softc *sc; - struct ifnet *ifp; - - sc = device_get_softc(dev); - ifp = sc->vtnet_ifp; - - KASSERT(mtx_initialized(VTNET_MTX(sc)), - ("vtnet mutex not initialized")); - -#ifdef DEVICE_POLLING - if (ifp != NULL && ifp->if_capenable & IFCAP_POLLING) - ether_poll_deregister(ifp); -#endif - - if (device_is_attached(dev)) { - VTNET_LOCK(sc); - vtnet_stop(sc); - VTNET_UNLOCK(sc); - - callout_drain(&sc->vtnet_tick_ch); - - ether_ifdetach(ifp); - } - - if (sc->vtnet_vlan_attach != NULL) { - EVENTHANDLER_DEREGISTER(vlan_config, sc->vtnet_vlan_attach); - sc->vtnet_vlan_attach = NULL; - } - if (sc->vtnet_vlan_detach != NULL) { - EVENTHANDLER_DEREGISTER(vlan_unconfg, sc->vtnet_vlan_detach); - sc->vtnet_vlan_detach = NULL; - } - - if (sc->vtnet_mac_filter != NULL) { - free(sc->vtnet_mac_filter, M_DEVBUF); - sc->vtnet_mac_filter = NULL; - } - - if (ifp != NULL) { - if_free(ifp); - sc->vtnet_ifp = NULL; - } - - if (sc->vtnet_rx_vq != NULL) - vtnet_free_rx_mbufs(sc); - if (sc->vtnet_tx_vq != NULL) - vtnet_free_tx_mbufs(sc); - if (sc->vtnet_ctrl_vq != NULL) - vtnet_free_ctrl_vq(sc); - - ifmedia_removeall(&sc->vtnet_media); - VTNET_LOCK_DESTROY(sc); - - return (0); -} - -static int -vtnet_suspend(device_t dev) -{ - struct vtnet_softc *sc; - - sc = device_get_softc(dev); - - VTNET_LOCK(sc); - vtnet_stop(sc); - sc->vtnet_flags |= VTNET_FLAG_SUSPENDED; - VTNET_UNLOCK(sc); - - return (0); -} - -static int -vtnet_resume(device_t dev) -{ - struct vtnet_softc *sc; - struct ifnet *ifp; - - sc = device_get_softc(dev); - ifp = sc->vtnet_ifp; - - VTNET_LOCK(sc); - if (ifp->if_flags & IFF_UP) + if (ifp->if_drv_flags & IFF_DRV_RUNNING) { + ifp->if_drv_flags &= ~IFF_DRV_RUNNING; vtnet_init_locked(sc); - sc->vtnet_flags &= ~VTNET_FLAG_SUSPENDED; - VTNET_UNLOCK(sc); + } return (0); } -static int -vtnet_shutdown(device_t dev) -{ - - /* - * Suspend already does all of what we need to - * do here; we just never expect to be resumed. - */ - return (vtnet_suspend(dev)); -} - -static int -vtnet_config_change(device_t dev) -{ - struct vtnet_softc *sc; - - sc = device_get_softc(dev); - - VTNET_LOCK(sc); - vtnet_update_link_status(sc); - VTNET_UNLOCK(sc); - - return (0); -} - -static void -vtnet_negotiate_features(struct vtnet_softc *sc) -{ - device_t dev; - uint64_t mask, features; - - dev = sc->vtnet_dev; - mask = 0; - - if (vtnet_csum_disable) - mask |= VIRTIO_NET_F_CSUM | VIRTIO_NET_F_GUEST_CSUM; - - /* - * TSO and LRO are only available when their corresponding - * checksum offload feature is also negotiated. - */ - - if (vtnet_csum_disable || vtnet_tso_disable) - mask |= VIRTIO_NET_F_HOST_TSO4 | VIRTIO_NET_F_HOST_TSO6 | - VIRTIO_NET_F_HOST_ECN; - - if (vtnet_csum_disable || vtnet_lro_disable) - mask |= VTNET_LRO_FEATURES; - - features = VTNET_FEATURES & ~mask; -#ifdef VTNET_TX_INTR_MODERATION - features |= VIRTIO_F_NOTIFY_ON_EMPTY; -#endif - sc->vtnet_features = virtio_negotiate_features(dev, features); - - if (virtio_with_feature(dev, VIRTIO_NET_F_MRG_RXBUF) == 0 && - virtio_with_feature(dev, VTNET_LRO_FEATURES)) { - /* - * LRO without mergeable buffers requires special care. This - * is not ideal because every receive buffer must be large - * enough to hold the maximum TCP packet, the Ethernet header, - * and the vtnet_rx_header. This requires up to 34 descriptors - * when using MCLBYTES clusters. If we do not have indirect - * descriptors, LRO is disabled since the virtqueue will not - * be able to contain very many receive buffers. - */ - if (virtio_with_feature(dev, - VIRTIO_RING_F_INDIRECT_DESC) == 0) { - device_printf(dev, - "LRO disabled due to lack of both mergeable " - "buffers and indirect descriptors\n"); - - sc->vtnet_features = virtio_negotiate_features(dev, - features & ~VTNET_LRO_FEATURES); - } else - sc->vtnet_flags |= VTNET_FLAG_LRO_NOMRG; - } -} - -static int -vtnet_alloc_virtqueues(struct vtnet_softc *sc) -{ - device_t dev; - struct vq_alloc_info vq_info[3]; - int nvqs, rxsegs; - - dev = sc->vtnet_dev; - nvqs = 2; - - /* - * Indirect descriptors are not needed for the Rx - * virtqueue when mergeable buffers are negotiated. - * The header is placed inline with the data, not - * in a separate descriptor, and mbuf clusters are - * always physically contiguous. - */ - if ((sc->vtnet_flags & VTNET_FLAG_MRG_RXBUFS) == 0) { - rxsegs = sc->vtnet_flags & VTNET_FLAG_LRO_NOMRG ? - VTNET_MAX_RX_SEGS : VTNET_MIN_RX_SEGS; - } else - rxsegs = 0; - - VQ_ALLOC_INFO_INIT(&vq_info[0], rxsegs, - vtnet_rx_vq_intr, sc, &sc->vtnet_rx_vq, - "%s receive", device_get_nameunit(dev)); - - VQ_ALLOC_INFO_INIT(&vq_info[1], VTNET_MAX_TX_SEGS, - vtnet_tx_vq_intr, sc, &sc->vtnet_tx_vq, - "%s transmit", device_get_nameunit(dev)); - - if (sc->vtnet_flags & VTNET_FLAG_CTRL_VQ) { - nvqs++; - - VQ_ALLOC_INFO_INIT(&vq_info[2], 0, NULL, NULL, - &sc->vtnet_ctrl_vq, "%s control", - device_get_nameunit(dev)); - } - - return (virtio_alloc_virtqueues(dev, 0, nvqs, vq_info)); -} - -static void -vtnet_get_hwaddr(struct vtnet_softc *sc) -{ - device_t dev; - - dev = sc->vtnet_dev; - - if (virtio_with_feature(dev, VIRTIO_NET_F_MAC)) { - virtio_read_device_config(dev, - offsetof(struct virtio_net_config, mac), - sc->vtnet_hwaddr, ETHER_ADDR_LEN); - } else { - /* Generate random locally administered unicast address. */ - sc->vtnet_hwaddr[0] = 0xB2; - arc4rand(&sc->vtnet_hwaddr[1], ETHER_ADDR_LEN - 1, 0); - - vtnet_set_hwaddr(sc); - } -} - -static void -vtnet_set_hwaddr(struct vtnet_softc *sc) -{ - device_t dev; - - dev = sc->vtnet_dev; - - virtio_write_device_config(dev, - offsetof(struct virtio_net_config, mac), - sc->vtnet_hwaddr, ETHER_ADDR_LEN); -} - -static int -vtnet_is_link_up(struct vtnet_softc *sc) -{ - device_t dev; - struct ifnet *ifp; - uint16_t status; - - dev = sc->vtnet_dev; - ifp = sc->vtnet_ifp; - - VTNET_LOCK_ASSERT(sc); - - if ((ifp->if_capenable & IFCAP_LINKSTATE) == 0) - return (1); - - status = virtio_read_dev_config_2(dev, - offsetof(struct virtio_net_config, status)); - - return ((status & VIRTIO_NET_S_LINK_UP) != 0); -} - -static void -vtnet_update_link_status(struct vtnet_softc *sc) -{ - struct ifnet *ifp; - int link; - - ifp = sc->vtnet_ifp; - - link = vtnet_is_link_up(sc); - - if (link && ((sc->vtnet_flags & VTNET_FLAG_LINK) == 0)) { - sc->vtnet_flags |= VTNET_FLAG_LINK; - if_link_state_change(ifp, LINK_STATE_UP); - if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd)) - vtnet_start_locked(ifp); - } else if (!link && (sc->vtnet_flags & VTNET_FLAG_LINK)) { - sc->vtnet_flags &= ~VTNET_FLAG_LINK; - if_link_state_change(ifp, LINK_STATE_DOWN); - } -} - -static void -vtnet_watchdog(struct vtnet_softc *sc) -{ - struct ifnet *ifp; - - ifp = sc->vtnet_ifp; - -#ifdef VTNET_TX_INTR_MODERATION - vtnet_txeof(sc); -#endif - - if (sc->vtnet_watchdog_timer == 0 || --sc->vtnet_watchdog_timer) - return; - - if_printf(ifp, "watchdog timeout -- resetting\n"); -#ifdef VTNET_DEBUG - virtqueue_dump(sc->vtnet_tx_vq); -#endif - ifp->if_oerrors++; - ifp->if_drv_flags &= ~IFF_DRV_RUNNING; - vtnet_init_locked(sc); -} - static int vtnet_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) { @@ -771,22 +1036,19 @@ vtnet_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) sc = ifp->if_softc; ifr = (struct ifreq *) data; - reinit = 0; error = 0; switch (cmd) { case SIOCSIFMTU: - if (ifr->ifr_mtu < ETHERMIN || ifr->ifr_mtu > VTNET_MAX_MTU) - error = EINVAL; - else if (ifp->if_mtu != ifr->ifr_mtu) { - VTNET_LOCK(sc); + if (ifp->if_mtu != ifr->ifr_mtu) { + VTNET_CORE_LOCK(sc); error = vtnet_change_mtu(sc, ifr->ifr_mtu); - VTNET_UNLOCK(sc); + VTNET_CORE_UNLOCK(sc); } break; case SIOCSIFFLAGS: - VTNET_LOCK(sc); + VTNET_CORE_LOCK(sc); if ((ifp->if_flags & IFF_UP) == 0) { if (ifp->if_drv_flags & IFF_DRV_RUNNING) vtnet_stop(sc); @@ -803,16 +1065,17 @@ vtnet_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) if (error == 0) sc->vtnet_if_flags = ifp->if_flags; - VTNET_UNLOCK(sc); + VTNET_CORE_UNLOCK(sc); break; case SIOCADDMULTI: case SIOCDELMULTI: - VTNET_LOCK(sc); - if ((sc->vtnet_flags & VTNET_FLAG_CTRL_RX) && - (ifp->if_drv_flags & IFF_DRV_RUNNING)) + if ((sc->vtnet_flags & VTNET_FLAG_CTRL_RX) == 0) + break; + VTNET_CORE_LOCK(sc); + if (ifp->if_drv_flags & IFF_DRV_RUNNING) vtnet_rx_filter_mac(sc); - VTNET_UNLOCK(sc); + VTNET_CORE_UNLOCK(sc); break; case SIOCSIFMEDIA: @@ -821,68 +1084,36 @@ vtnet_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) break; case SIOCSIFCAP: + VTNET_CORE_LOCK(sc); mask = ifr->ifr_reqcap ^ ifp->if_capenable; -#ifdef DEVICE_POLLING - if (mask & IFCAP_POLLING) { - if (ifr->ifr_reqcap & IFCAP_POLLING) { - error = ether_poll_register(vtnet_poll, ifp); - if (error) - break; - - VTNET_LOCK(sc); - vtnet_disable_rx_intr(sc); - vtnet_disable_tx_intr(sc); - ifp->if_capenable |= IFCAP_POLLING; - VTNET_UNLOCK(sc); - } else { - error = ether_poll_deregister(ifp); - - /* Enable interrupts even in error case. */ - VTNET_LOCK(sc); - vtnet_enable_tx_intr(sc); - vtnet_enable_rx_intr(sc); - ifp->if_capenable &= ~IFCAP_POLLING; - VTNET_UNLOCK(sc); - } - } -#endif - VTNET_LOCK(sc); - - if (mask & IFCAP_TXCSUM) { + if (mask & IFCAP_TXCSUM) ifp->if_capenable ^= IFCAP_TXCSUM; - if (ifp->if_capenable & IFCAP_TXCSUM) - ifp->if_hwassist |= VTNET_CSUM_OFFLOAD; - else - ifp->if_hwassist &= ~VTNET_CSUM_OFFLOAD; - } - - if (mask & IFCAP_TSO4) { + if (mask & IFCAP_TXCSUM_IPV6) + ifp->if_capenable ^= IFCAP_TXCSUM_IPV6; + if (mask & IFCAP_TSO4) ifp->if_capenable ^= IFCAP_TSO4; - if (ifp->if_capenable & IFCAP_TSO4) - ifp->if_hwassist |= CSUM_TSO; - else - ifp->if_hwassist &= ~CSUM_TSO; - } + if (mask & IFCAP_TSO6) + ifp->if_capenable ^= IFCAP_TSO6; - if (mask & IFCAP_RXCSUM) { - ifp->if_capenable ^= IFCAP_RXCSUM; + if (mask & (IFCAP_RXCSUM | IFCAP_RXCSUM_IPV6 | IFCAP_LRO | + IFCAP_VLAN_HWFILTER)) { + /* These Rx features require us to renegotiate. */ reinit = 1; - } - if (mask & IFCAP_LRO) { - ifp->if_capenable ^= IFCAP_LRO; - reinit = 1; - } - - if (mask & IFCAP_VLAN_HWFILTER) { - ifp->if_capenable ^= IFCAP_VLAN_HWFILTER; - reinit = 1; - } + if (mask & IFCAP_RXCSUM) + ifp->if_capenable ^= IFCAP_RXCSUM; + if (mask & IFCAP_RXCSUM_IPV6) + ifp->if_capenable ^= IFCAP_RXCSUM_IPV6; + if (mask & IFCAP_LRO) + ifp->if_capenable ^= IFCAP_LRO; + if (mask & IFCAP_VLAN_HWFILTER) + ifp->if_capenable ^= IFCAP_VLAN_HWFILTER; + } else + reinit = 0; if (mask & IFCAP_VLAN_HWTSO) ifp->if_capenable ^= IFCAP_VLAN_HWTSO; - if (mask & IFCAP_VLAN_HWTAGGING) ifp->if_capenable ^= IFCAP_VLAN_HWTAGGING; @@ -890,9 +1121,10 @@ vtnet_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) ifp->if_drv_flags &= ~IFF_DRV_RUNNING; vtnet_init_locked(sc); } + + VTNET_CORE_UNLOCK(sc); VLAN_CAPABILITIES(ifp); - VTNET_UNLOCK(sc); break; default: @@ -900,80 +1132,32 @@ vtnet_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) break; } - VTNET_LOCK_ASSERT_NOTOWNED(sc); + VTNET_CORE_LOCK_ASSERT_NOTOWNED(sc); return (error); } static int -vtnet_change_mtu(struct vtnet_softc *sc, int new_mtu) -{ - struct ifnet *ifp; - int new_frame_size, clsize; - - ifp = sc->vtnet_ifp; - - if ((sc->vtnet_flags & VTNET_FLAG_MRG_RXBUFS) == 0) { - new_frame_size = sizeof(struct vtnet_rx_header) + - sizeof(struct ether_vlan_header) + new_mtu; - - if (new_frame_size > MJUM9BYTES) - return (EINVAL); - - if (new_frame_size <= MCLBYTES) - clsize = MCLBYTES; - else - clsize = MJUM9BYTES; - } else { - new_frame_size = sizeof(struct virtio_net_hdr_mrg_rxbuf) + - sizeof(struct ether_vlan_header) + new_mtu; - - if (new_frame_size <= MCLBYTES) - clsize = MCLBYTES; - else - clsize = MJUMPAGESIZE; - } - - sc->vtnet_rx_mbuf_size = clsize; - sc->vtnet_rx_mbuf_count = VTNET_NEEDED_RX_MBUFS(sc); - KASSERT(sc->vtnet_rx_mbuf_count < VTNET_MAX_RX_SEGS, - ("too many rx mbufs: %d", sc->vtnet_rx_mbuf_count)); - - ifp->if_mtu = new_mtu; - - if (ifp->if_drv_flags & IFF_DRV_RUNNING) { - ifp->if_drv_flags &= ~IFF_DRV_RUNNING; - vtnet_init_locked(sc); - } - - return (0); -} - -static int -vtnet_init_rx_vq(struct vtnet_softc *sc) +vtnet_rxq_populate(struct vtnet_rxq *rxq) { struct virtqueue *vq; int nbufs, error; - vq = sc->vtnet_rx_vq; - nbufs = 0; + vq = rxq->vtnrx_vq; error = ENOSPC; - while (!virtqueue_full(vq)) { - if ((error = vtnet_newbuf(sc)) != 0) + for (nbufs = 0; !virtqueue_full(vq); nbufs++) { + error = vtnet_rxq_new_buf(rxq); + if (error) break; - nbufs++; } if (nbufs > 0) { virtqueue_notify(vq); - /* * EMSGSIZE signifies the virtqueue did not have enough * entries available to hold the last mbuf. This is not - * an error. We should not get ENOSPC since we check if - * the virtqueue is full before attempting to add a - * buffer. + * an error. */ if (error == EMSGSIZE) error = 0; @@ -983,86 +1167,32 @@ vtnet_init_rx_vq(struct vtnet_softc *sc) } static void -vtnet_free_rx_mbufs(struct vtnet_softc *sc) +vtnet_rxq_free_mbufs(struct vtnet_rxq *rxq) { struct virtqueue *vq; struct mbuf *m; int last; - vq = sc->vtnet_rx_vq; + vq = rxq->vtnrx_vq; last = 0; while ((m = virtqueue_drain(vq, &last)) != NULL) m_freem(m); - KASSERT(virtqueue_empty(vq), ("mbufs remaining in Rx Vq")); + KASSERT(virtqueue_empty(vq), + ("%s: mbufs remaining in rx queue %p", __func__, rxq)); } -static void -vtnet_free_tx_mbufs(struct vtnet_softc *sc) -{ - struct virtqueue *vq; - struct vtnet_tx_header *txhdr; - int last; - - vq = sc->vtnet_tx_vq; - last = 0; - - while ((txhdr = virtqueue_drain(vq, &last)) != NULL) { - m_freem(txhdr->vth_mbuf); - uma_zfree(vtnet_tx_header_zone, txhdr); - } - - KASSERT(virtqueue_empty(vq), ("mbufs remaining in Tx Vq")); -} - -static void -vtnet_free_ctrl_vq(struct vtnet_softc *sc) -{ - - /* - * The control virtqueue is only polled, therefore - * it should already be empty. - */ - KASSERT(virtqueue_empty(sc->vtnet_ctrl_vq), - ("Ctrl Vq not empty")); -} - -#ifdef DEVICE_POLLING -static int -vtnet_poll(struct ifnet *ifp, enum poll_cmd cmd, int count) -{ - struct vtnet_softc *sc; - int rx_done; - - sc = ifp->if_softc; - rx_done = 0; - - VTNET_LOCK(sc); - if (ifp->if_drv_flags & IFF_DRV_RUNNING) { - if (cmd == POLL_AND_CHECK_STATUS) - vtnet_update_link_status(sc); - - if (virtqueue_nused(sc->vtnet_rx_vq) > 0) - vtnet_rxeof(sc, count, &rx_done); - - vtnet_txeof(sc); - if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd)) - vtnet_start_locked(ifp); - } - VTNET_UNLOCK(sc); - - return (rx_done); -} -#endif /* DEVICE_POLLING */ - static struct mbuf * -vtnet_alloc_rxbuf(struct vtnet_softc *sc, int nbufs, struct mbuf **m_tailp) +vtnet_rx_alloc_buf(struct vtnet_softc *sc, int nbufs, struct mbuf **m_tailp) { struct mbuf *m_head, *m_tail, *m; int i, clsize; - clsize = sc->vtnet_rx_mbuf_size; + clsize = sc->vtnet_rx_clsize; + + KASSERT(nbufs == 1 || sc->vtnet_flags & VTNET_FLAG_LRO_NOMRG, + ("%s: chained mbuf %d request without LRO_NOMRG", __func__, nbufs)); m_head = m_getjcl(M_NOWAIT, MT_DATA, M_PKTHDR, clsize); if (m_head == NULL) @@ -1071,19 +1201,15 @@ vtnet_alloc_rxbuf(struct vtnet_softc *sc, int nbufs, struct mbuf **m_tailp) m_head->m_len = clsize; m_tail = m_head; - if (nbufs > 1) { - KASSERT(sc->vtnet_flags & VTNET_FLAG_LRO_NOMRG, - ("chained Rx mbuf requested without LRO_NOMRG")); + /* Allocate the rest of the chain. */ + for (i = 1; i < nbufs; i++) { + m = m_getjcl(M_NOWAIT, MT_DATA, 0, clsize); + if (m == NULL) + goto fail; - for (i = 1; i < nbufs; i++) { - m = m_getjcl(M_NOWAIT, MT_DATA, 0, clsize); - if (m == NULL) - goto fail; - - m->m_len = clsize; - m_tail->m_next = m; - m_tail = m; - } + m->m_len = clsize; + m_tail->m_next = m; + m_tail = m; } if (m_tailp != NULL) @@ -1098,43 +1224,48 @@ fail: return (NULL); } +/* + * Slow path for when LRO without mergeable buffers is negotiated. + */ static int -vtnet_replace_rxbuf(struct vtnet_softc *sc, struct mbuf *m0, int len0) +vtnet_rxq_replace_lro_nomgr_buf(struct vtnet_rxq *rxq, struct mbuf *m0, + int len0) { + struct vtnet_softc *sc; struct mbuf *m, *m_prev; struct mbuf *m_new, *m_tail; int len, clsize, nreplace, error; - m = m0; - m_prev = NULL; - len = len0; + sc = rxq->vtnrx_sc; + clsize = sc->vtnet_rx_clsize; + m_prev = NULL; m_tail = NULL; - clsize = sc->vtnet_rx_mbuf_size; nreplace = 0; - KASSERT(sc->vtnet_flags & VTNET_FLAG_LRO_NOMRG || - m->m_next == NULL, ("chained Rx mbuf without LRO_NOMRG")); + m = m0; + len = len0; /* - * Since LRO_NOMRG mbuf chains are so large, we want to avoid - * allocating an entire chain for each received frame. When - * the received frame's length is less than that of the chain, - * the unused mbufs are reassigned to the new chain. + * Since these mbuf chains are so large, we avoid allocating an + * entire replacement chain if possible. When the received frame + * did not consume the entire chain, the unused mbufs are moved + * to the replacement chain. */ while (len > 0) { /* - * Something is seriously wrong if we received - * a frame larger than the mbuf chain. Drop it. + * Something is seriously wrong if we received a frame + * larger than the chain. Drop it. */ if (m == NULL) { sc->vtnet_stats.rx_frame_too_large++; return (EMSGSIZE); } + /* We always allocate the same cluster size. */ KASSERT(m->m_len == clsize, - ("mbuf length not expected cluster size: %d", - m->m_len)); + ("%s: mbuf size %d is not the cluster size %d", + __func__, m->m_len, clsize)); m->m_len = MIN(m->m_len, len); len -= m->m_len; @@ -1144,27 +1275,26 @@ vtnet_replace_rxbuf(struct vtnet_softc *sc, struct mbuf *m0, int len0) nreplace++; } - KASSERT(m_prev != NULL, ("m_prev == NULL")); - KASSERT(nreplace <= sc->vtnet_rx_mbuf_count, - ("too many replacement mbufs: %d/%d", nreplace, - sc->vtnet_rx_mbuf_count)); + KASSERT(nreplace <= sc->vtnet_rx_nmbufs, + ("%s: too many replacement mbufs %d max %d", __func__, nreplace, + sc->vtnet_rx_nmbufs)); - m_new = vtnet_alloc_rxbuf(sc, nreplace, &m_tail); + m_new = vtnet_rx_alloc_buf(sc, nreplace, &m_tail); if (m_new == NULL) { m_prev->m_len = clsize; return (ENOBUFS); } /* - * Move unused mbufs, if any, from the original chain - * onto the end of the new chain. + * Move any unused mbufs from the received chain onto the end + * of the new chain. */ if (m_prev->m_next != NULL) { m_tail->m_next = m_prev->m_next; m_prev->m_next = NULL; } - error = vtnet_enqueue_rxbuf(sc, m_new); + error = vtnet_rxq_enqueue_buf(rxq, m_new); if (error) { /* * BAD! We could not enqueue the replacement mbuf chain. We @@ -1189,343 +1319,321 @@ vtnet_replace_rxbuf(struct vtnet_softc *sc, struct mbuf *m0, int len0) } static int -vtnet_newbuf(struct vtnet_softc *sc) +vtnet_rxq_replace_buf(struct vtnet_rxq *rxq, struct mbuf *m, int len) { + struct vtnet_softc *sc; + struct mbuf *m_new; + int error; + + sc = rxq->vtnrx_sc; + + KASSERT(sc->vtnet_flags & VTNET_FLAG_LRO_NOMRG || m->m_next == NULL, + ("%s: chained mbuf without LRO_NOMRG", __func__)); + + if (m->m_next == NULL) { + /* Fast-path for the common case of just one mbuf. */ + if (m->m_len < len) + return (EINVAL); + + m_new = vtnet_rx_alloc_buf(sc, 1, NULL); + if (m_new == NULL) + return (ENOBUFS); + + error = vtnet_rxq_enqueue_buf(rxq, m_new); + if (error) { + /* + * The new mbuf is suppose to be an identical + * copy of the one just dequeued so this is an + * unexpected error. + */ + m_freem(m_new); + sc->vtnet_stats.rx_enq_replacement_failed++; + } else + m->m_len = len; + } else + error = vtnet_rxq_replace_lro_nomgr_buf(rxq, m, len); + + return (error); +} + +static int +vtnet_rxq_enqueue_buf(struct vtnet_rxq *rxq, struct mbuf *m) +{ + struct sglist sg; + struct sglist_seg segs[VTNET_MAX_RX_SEGS]; + struct vtnet_softc *sc; + struct vtnet_rx_header *rxhdr; + uint8_t *mdata; + int offset, error; + + sc = rxq->vtnrx_sc; + mdata = mtod(m, uint8_t *); + + VTNET_RXQ_LOCK_ASSERT(rxq); + KASSERT(sc->vtnet_flags & VTNET_FLAG_LRO_NOMRG || m->m_next == NULL, + ("%s: chained mbuf without LRO_NOMRG", __func__)); + KASSERT(m->m_len == sc->vtnet_rx_clsize, + ("%s: unexpected cluster size %d/%d", __func__, m->m_len, + sc->vtnet_rx_clsize)); + + sglist_init(&sg, VTNET_MAX_RX_SEGS, segs); + if ((sc->vtnet_flags & VTNET_FLAG_MRG_RXBUFS) == 0) { + MPASS(sc->vtnet_hdr_size == sizeof(struct virtio_net_hdr)); + rxhdr = (struct vtnet_rx_header *) mdata; + sglist_append(&sg, &rxhdr->vrh_hdr, sc->vtnet_hdr_size); + offset = sizeof(struct vtnet_rx_header); + } else + offset = 0; + + sglist_append(&sg, mdata + offset, m->m_len - offset); + if (m->m_next != NULL) { + error = sglist_append_mbuf(&sg, m->m_next); + MPASS(error == 0); + } + + error = virtqueue_enqueue(rxq->vtnrx_vq, m, &sg, 0, sg.sg_nseg); + + return (error); +} + +static int +vtnet_rxq_new_buf(struct vtnet_rxq *rxq) +{ + struct vtnet_softc *sc; struct mbuf *m; int error; - m = vtnet_alloc_rxbuf(sc, sc->vtnet_rx_mbuf_count, NULL); + sc = rxq->vtnrx_sc; + + m = vtnet_rx_alloc_buf(sc, sc->vtnet_rx_nmbufs, NULL); if (m == NULL) return (ENOBUFS); - error = vtnet_enqueue_rxbuf(sc, m); + error = vtnet_rxq_enqueue_buf(rxq, m); if (error) m_freem(m); return (error); } -static void -vtnet_discard_merged_rxbuf(struct vtnet_softc *sc, int nbufs) -{ - struct virtqueue *vq; - struct mbuf *m; - - vq = sc->vtnet_rx_vq; - - while (--nbufs > 0) { - if ((m = virtqueue_dequeue(vq, NULL)) == NULL) - break; - vtnet_discard_rxbuf(sc, m); - } -} - -static void -vtnet_discard_rxbuf(struct vtnet_softc *sc, struct mbuf *m) -{ - int error; - - /* - * Requeue the discarded mbuf. This should always be - * successful since it was just dequeued. - */ - error = vtnet_enqueue_rxbuf(sc, m); - KASSERT(error == 0, ("cannot requeue discarded mbuf")); -} - -static int -vtnet_enqueue_rxbuf(struct vtnet_softc *sc, struct mbuf *m) -{ - struct sglist sg; - struct sglist_seg segs[VTNET_MAX_RX_SEGS]; - struct vtnet_rx_header *rxhdr; - struct virtio_net_hdr *hdr; - uint8_t *mdata; - int offset, error; - - VTNET_LOCK_ASSERT(sc); - KASSERT(sc->vtnet_flags & VTNET_FLAG_LRO_NOMRG || - m->m_next == NULL, ("chained Rx mbuf without LRO_NOMRG")); - - sglist_init(&sg, VTNET_MAX_RX_SEGS, segs); - - mdata = mtod(m, uint8_t *); - offset = 0; - - if ((sc->vtnet_flags & VTNET_FLAG_MRG_RXBUFS) == 0) { - rxhdr = (struct vtnet_rx_header *) mdata; - hdr = &rxhdr->vrh_hdr; - offset += sizeof(struct vtnet_rx_header); - - error = sglist_append(&sg, hdr, sc->vtnet_hdr_size); - KASSERT(error == 0, ("cannot add header to sglist")); - } - - error = sglist_append(&sg, mdata + offset, m->m_len - offset); - if (error) - return (error); - - if (m->m_next != NULL) { - error = sglist_append_mbuf(&sg, m->m_next); - if (error) - return (error); - } - - return (virtqueue_enqueue(sc->vtnet_rx_vq, m, &sg, 0, sg.sg_nseg)); -} - -static void -vtnet_vlan_tag_remove(struct mbuf *m) -{ - struct ether_vlan_header *evl; - - evl = mtod(m, struct ether_vlan_header *); - - m->m_pkthdr.ether_vtag = ntohs(evl->evl_tag); - m->m_flags |= M_VLANTAG; - - /* Strip the 802.1Q header. */ - bcopy((char *) evl, (char *) evl + ETHER_VLAN_ENCAP_LEN, - ETHER_HDR_LEN - ETHER_TYPE_LEN); - m_adj(m, ETHER_VLAN_ENCAP_LEN); -} - -#ifdef notyet -static int -vtnet_rx_csum(struct vtnet_softc *sc, struct mbuf *m, - struct virtio_net_hdr *hdr) -{ - struct ether_header *eh; - struct ether_vlan_header *evh; - struct ip *ip; - struct ip6_hdr *ip6; - struct udphdr *udp; - int ip_offset, csum_start, csum_offset, hlen; - uint16_t eth_type; - uint8_t ip_proto; - - /* - * Convert the VirtIO checksum interface to FreeBSD's interface. - * The host only provides us with the offset at which to start - * checksumming, and the offset from that to place the completed - * checksum. While this maps well with how Linux does checksums, - * for FreeBSD, we must parse the received packet in order to set - * the appropriate CSUM_* flags. - */ - - /* - * Every mbuf added to the receive virtqueue is always at least - * MCLBYTES big, so assume something is amiss if the first mbuf - * does not contain both the Ethernet and protocol headers. - */ - ip_offset = sizeof(struct ether_header); - if (m->m_len < ip_offset) - return (1); - - eh = mtod(m, struct ether_header *); - eth_type = ntohs(eh->ether_type); - if (eth_type == ETHERTYPE_VLAN) { - ip_offset = sizeof(struct ether_vlan_header); - if (m->m_len < ip_offset) - return (1); - evh = mtod(m, struct ether_vlan_header *); - eth_type = ntohs(evh->evl_proto); - } - - switch (eth_type) { - case ETHERTYPE_IP: - if (m->m_len < ip_offset + sizeof(struct ip)) - return (1); - - ip = (struct ip *)(mtod(m, uint8_t *) + ip_offset); - /* Sanity check the IP header. */ - if (ip->ip_v != IPVERSION) - return (1); - hlen = ip->ip_hl << 2; - if (hlen < sizeof(struct ip)) - return (1); - if (ntohs(ip->ip_len) < hlen) - return (1); - if (ntohs(ip->ip_len) != (m->m_pkthdr.len - ip_offset)) - return (1); - - ip_proto = ip->ip_p; - csum_start = ip_offset + hlen; - break; - - case ETHERTYPE_IPV6: - if (m->m_len < ip_offset + sizeof(struct ip6_hdr)) - return (1); - - /* - * XXX FreeBSD does not handle any IPv6 checksum offloading - * at the moment. - */ - - ip6 = (struct ip6_hdr *)(mtod(m, uint8_t *) + ip_offset); - /* XXX Assume no extension headers are present. */ - ip_proto = ip6->ip6_nxt; - csum_start = ip_offset + sizeof(struct ip6_hdr); - break; - - default: - sc->vtnet_stats.rx_csum_bad_ethtype++; - return (1); - } - - /* Assume checksum begins right after the IP header. */ - if (hdr->csum_start != csum_start) { - sc->vtnet_stats.rx_csum_bad_start++; - return (1); - } - - switch (ip_proto) { - case IPPROTO_TCP: - csum_offset = offsetof(struct tcphdr, th_sum); - break; - - case IPPROTO_UDP: - csum_offset = offsetof(struct udphdr, uh_sum); - break; - - case IPPROTO_SCTP: - csum_offset = offsetof(struct sctphdr, checksum); - break; - - default: - sc->vtnet_stats.rx_csum_bad_ipproto++; - return (1); - } - - if (hdr->csum_offset != csum_offset) { - sc->vtnet_stats.rx_csum_bad_offset++; - return (1); - } - - /* - * The IP header checksum is almost certainly valid but I'm - * uncertain if that is guaranteed. - * - * m->m_pkthdr.csum_flags |= CSUM_IP_CHECKED | CSUM_IP_VALID; - */ - - switch (ip_proto) { - case IPPROTO_UDP: - if (m->m_len < csum_start + sizeof(struct udphdr)) - return (1); - - udp = (struct udphdr *)(mtod(m, uint8_t *) + csum_start); - if (udp->uh_sum == 0) - return (0); - - /* FALLTHROUGH */ - - case IPPROTO_TCP: - m->m_pkthdr.csum_flags |= CSUM_DATA_VALID | CSUM_PSEUDO_HDR; - m->m_pkthdr.csum_data = 0xFFFF; - break; - - case IPPROTO_SCTP: - m->m_pkthdr.csum_flags |= CSUM_SCTP_VALID; - break; - } - - sc->vtnet_stats.rx_csum_offloaded++; - - return (0); -} -#endif - /* - * Alternative method of doing receive checksum offloading. Rather - * than parsing the received frame down to the IP header, use the - * csum_offset to determine which CSUM_* flags are appropriate. We - * can get by with doing this only because the checksum offsets are - * unique for the things we care about. + * Use the checksum offset in the VirtIO header to set the + * correct CSUM_* flags. */ static int -vtnet_rx_csum(struct vtnet_softc *sc, struct mbuf *m, - struct virtio_net_hdr *hdr) +vtnet_rxq_csum_by_offset(struct vtnet_rxq *rxq, struct mbuf *m, + uint16_t eth_type, int ip_start, struct virtio_net_hdr *hdr) { - struct ether_header *eh; - struct ether_vlan_header *evh; - struct udphdr *udp; - int csum_len; - uint16_t eth_type; + struct vtnet_softc *sc; +#if defined(INET) || defined(INET6) + int offset = hdr->csum_start + hdr->csum_offset; +#endif - csum_len = hdr->csum_start + hdr->csum_offset; + sc = rxq->vtnrx_sc; - if (csum_len < sizeof(struct ether_header) + sizeof(struct ip)) - return (1); - if (m->m_len < csum_len) - return (1); - - eh = mtod(m, struct ether_header *); - eth_type = ntohs(eh->ether_type); - if (eth_type == ETHERTYPE_VLAN) { - evh = mtod(m, struct ether_vlan_header *); - eth_type = ntohs(evh->evl_proto); - } - - if (eth_type != ETHERTYPE_IP && eth_type != ETHERTYPE_IPV6) { + /* Only do a basic sanity check on the offset. */ + switch (eth_type) { +#if defined(INET) + case ETHERTYPE_IP: + if (__predict_false(offset < ip_start + sizeof(struct ip))) + return (1); + break; +#endif +#if defined(INET6) + case ETHERTYPE_IPV6: + if (__predict_false(offset < ip_start + sizeof(struct ip6_hdr))) + return (1); + break; +#endif + default: sc->vtnet_stats.rx_csum_bad_ethtype++; return (1); } - /* Use the offset to determine the appropriate CSUM_* flags. */ + /* + * Use the offset to determine the appropriate CSUM_* flags. This is + * a bit dirty, but we can get by with it since the checksum offsets + * happen to be different. We assume the host host does not do IPv4 + * header checksum offloading. + */ switch (hdr->csum_offset) { case offsetof(struct udphdr, uh_sum): - if (m->m_len < hdr->csum_start + sizeof(struct udphdr)) - return (1); - udp = (struct udphdr *)(mtod(m, uint8_t *) + hdr->csum_start); - if (udp->uh_sum == 0) - return (0); - - /* FALLTHROUGH */ - case offsetof(struct tcphdr, th_sum): m->m_pkthdr.csum_flags |= CSUM_DATA_VALID | CSUM_PSEUDO_HDR; m->m_pkthdr.csum_data = 0xFFFF; break; - case offsetof(struct sctphdr, checksum): m->m_pkthdr.csum_flags |= CSUM_SCTP_VALID; break; - default: sc->vtnet_stats.rx_csum_bad_offset++; return (1); } - sc->vtnet_stats.rx_csum_offloaded++; - return (0); } static int -vtnet_rxeof_merged(struct vtnet_softc *sc, struct mbuf *m_head, int nbufs) +vtnet_rxq_csum_by_parse(struct vtnet_rxq *rxq, struct mbuf *m, + uint16_t eth_type, int ip_start, struct virtio_net_hdr *hdr) { + struct vtnet_softc *sc; + int offset, proto; + + sc = rxq->vtnrx_sc; + + switch (eth_type) { +#if defined(INET) + case ETHERTYPE_IP: { + struct ip *ip; + if (__predict_false(m->m_len < ip_start + sizeof(struct ip))) + return (1); + ip = (struct ip *)(m->m_data + ip_start); + proto = ip->ip_p; + offset = ip_start + (ip->ip_hl << 2); + break; + } +#endif +#if defined(INET6) + case ETHERTYPE_IPV6: + if (__predict_false(m->m_len < ip_start + + sizeof(struct ip6_hdr))) + return (1); + offset = ip6_lasthdr(m, ip_start, IPPROTO_IPV6, &proto); + if (__predict_false(offset < 0)) + return (1); + break; +#endif + default: + sc->vtnet_stats.rx_csum_bad_ethtype++; + return (1); + } + + switch (proto) { + case IPPROTO_TCP: + if (__predict_false(m->m_len < offset + sizeof(struct tcphdr))) + return (1); + m->m_pkthdr.csum_flags |= CSUM_DATA_VALID | CSUM_PSEUDO_HDR; + m->m_pkthdr.csum_data = 0xFFFF; + break; + case IPPROTO_UDP: + if (__predict_false(m->m_len < offset + sizeof(struct udphdr))) + return (1); + m->m_pkthdr.csum_flags |= CSUM_DATA_VALID | CSUM_PSEUDO_HDR; + m->m_pkthdr.csum_data = 0xFFFF; + break; + case IPPROTO_SCTP: + if (__predict_false(m->m_len < offset + sizeof(struct sctphdr))) + return (1); + m->m_pkthdr.csum_flags |= CSUM_SCTP_VALID; + break; + default: + /* + * For the remaining protocols, FreeBSD does not support + * checksum offloading, so the checksum will be recomputed. + */ +#if 0 + if_printf(sc->vtnet_ifp, "cksum offload of unsupported " + "protocol eth_type=%#x proto=%d csum_start=%d " + "csum_offset=%d\n", __func__, eth_type, proto, + hdr->csum_start, hdr->csum_offset); +#endif + break; + } + + return (0); +} + +/* + * Set the appropriate CSUM_* flags. Unfortunately, the information + * provided is not directly useful to us. The VirtIO header gives the + * offset of the checksum, which is all Linux needs, but this is not + * how FreeBSD does things. We are forced to peek inside the packet + * a bit. + * + * It would be nice if VirtIO gave us the L4 protocol or if FreeBSD + * could accept the offsets and let the stack figure it out. + */ +static int +vtnet_rxq_csum(struct vtnet_rxq *rxq, struct mbuf *m, + struct virtio_net_hdr *hdr) +{ + struct ether_header *eh; + struct ether_vlan_header *evh; + uint16_t eth_type; + int offset, error; + + eh = mtod(m, struct ether_header *); + eth_type = ntohs(eh->ether_type); + if (eth_type == ETHERTYPE_VLAN) { + /* BMV: We should handle nested VLAN tags too. */ + evh = mtod(m, struct ether_vlan_header *); + eth_type = ntohs(evh->evl_proto); + offset = sizeof(struct ether_vlan_header); + } else + offset = sizeof(struct ether_header); + + if (hdr->flags & VIRTIO_NET_HDR_F_NEEDS_CSUM) + error = vtnet_rxq_csum_by_offset(rxq, m, eth_type, offset, hdr); + else + error = vtnet_rxq_csum_by_parse(rxq, m, eth_type, offset, hdr); + + return (error); +} + +static void +vtnet_rxq_discard_merged_bufs(struct vtnet_rxq *rxq, int nbufs) +{ + struct mbuf *m; + + while (--nbufs > 0) { + m = virtqueue_dequeue(rxq->vtnrx_vq, NULL); + if (m == NULL) + break; + vtnet_rxq_discard_buf(rxq, m); + } +} + +static void +vtnet_rxq_discard_buf(struct vtnet_rxq *rxq, struct mbuf *m) +{ + int error; + + /* + * Requeue the discarded mbuf. This should always be successful + * since it was just dequeued. + */ + error = vtnet_rxq_enqueue_buf(rxq, m); + KASSERT(error == 0, + ("%s: cannot requeue discarded mbuf %d", __func__, error)); +} + +static int +vtnet_rxq_merged_eof(struct vtnet_rxq *rxq, struct mbuf *m_head, int nbufs) +{ + struct vtnet_softc *sc; struct ifnet *ifp; struct virtqueue *vq; struct mbuf *m, *m_tail; int len; + sc = rxq->vtnrx_sc; + vq = rxq->vtnrx_vq; ifp = sc->vtnet_ifp; - vq = sc->vtnet_rx_vq; m_tail = m_head; while (--nbufs > 0) { m = virtqueue_dequeue(vq, &len); if (m == NULL) { - ifp->if_ierrors++; + rxq->vtnrx_stats.vrxs_ierrors++; goto fail; } - if (vtnet_newbuf(sc) != 0) { - ifp->if_iqdrops++; - vtnet_discard_rxbuf(sc, m); + if (vtnet_rxq_new_buf(rxq) != 0) { + rxq->vtnrx_stats.vrxs_iqdrops++; + vtnet_rxq_discard_buf(rxq, m); if (nbufs > 1) - vtnet_discard_merged_rxbuf(sc, nbufs); + vtnet_rxq_discard_merged_bufs(rxq, nbufs); goto fail; } @@ -1549,35 +1657,83 @@ fail: return (1); } -static int -vtnet_rxeof(struct vtnet_softc *sc, int count, int *rx_npktsp) +static void +vtnet_rxq_input(struct vtnet_rxq *rxq, struct mbuf *m, + struct virtio_net_hdr *hdr) { - struct virtio_net_hdr lhdr; + struct vtnet_softc *sc; + struct ifnet *ifp; + struct ether_header *eh; + + sc = rxq->vtnrx_sc; + ifp = sc->vtnet_ifp; + + if (ifp->if_capenable & IFCAP_VLAN_HWTAGGING) { + eh = mtod(m, struct ether_header *); + if (eh->ether_type == htons(ETHERTYPE_VLAN)) { + vtnet_vlan_tag_remove(m); + /* + * With the 802.1Q header removed, update the + * checksum starting location accordingly. + */ + if (hdr->flags & VIRTIO_NET_HDR_F_NEEDS_CSUM) + hdr->csum_start -= ETHER_VLAN_ENCAP_LEN; + } + } + + m->m_pkthdr.flowid = rxq->vtnrx_id; + m->m_flags |= M_FLOWID; + + /* + * BMV: FreeBSD does not have the UNNECESSARY and PARTIAL checksum + * distinction that Linux does. Need to reevaluate if performing + * offloading for the NEEDS_CSUM case is really appropriate. + */ + if (hdr->flags & (VIRTIO_NET_HDR_F_NEEDS_CSUM | + VIRTIO_NET_HDR_F_DATA_VALID)) { + if (vtnet_rxq_csum(rxq, m, hdr) == 0) + rxq->vtnrx_stats.vrxs_csum++; + else + rxq->vtnrx_stats.vrxs_csum_failed++; + } + + rxq->vtnrx_stats.vrxs_ipackets++; + rxq->vtnrx_stats.vrxs_ibytes += m->m_pkthdr.len; + + /* VTNET_RXQ_UNLOCK(rxq); */ + (*ifp->if_input)(ifp, m); + /* VTNET_RXQ_LOCK(rxq); */ +} + +static int +vtnet_rxq_eof(struct vtnet_rxq *rxq) +{ + struct virtio_net_hdr lhdr, *hdr; + struct vtnet_softc *sc; struct ifnet *ifp; struct virtqueue *vq; struct mbuf *m; - struct ether_header *eh; - struct virtio_net_hdr *hdr; struct virtio_net_hdr_mrg_rxbuf *mhdr; - int len, deq, nbufs, adjsz, rx_npkts; + int len, deq, nbufs, adjsz, count; + sc = rxq->vtnrx_sc; + vq = rxq->vtnrx_vq; ifp = sc->vtnet_ifp; - vq = sc->vtnet_rx_vq; hdr = &lhdr; deq = 0; - rx_npkts = 0; + count = sc->vtnet_rx_process_limit; - VTNET_LOCK_ASSERT(sc); + VTNET_RXQ_LOCK_ASSERT(rxq); - while (--count >= 0) { + while (count-- > 0) { m = virtqueue_dequeue(vq, &len); if (m == NULL) break; deq++; if (len < sc->vtnet_hdr_size + ETHER_HDR_LEN) { - ifp->if_ierrors++; - vtnet_discard_rxbuf(sc, m); + rxq->vtnrx_stats.vrxs_ierrors++; + vtnet_rxq_discard_buf(rxq, m); continue; } @@ -1585,8 +1741,8 @@ vtnet_rxeof(struct vtnet_softc *sc, int count, int *rx_npktsp) nbufs = 1; adjsz = sizeof(struct vtnet_rx_header); /* - * Account for our pad between the header and - * the actual start of the frame. + * Account for our pad inserted between the header + * and the actual start of the frame. */ len += VTNET_RX_HEADER_PAD; } else { @@ -1595,11 +1751,11 @@ vtnet_rxeof(struct vtnet_softc *sc, int count, int *rx_npktsp) adjsz = sizeof(struct virtio_net_hdr_mrg_rxbuf); } - if (vtnet_replace_rxbuf(sc, m, len) != 0) { - ifp->if_iqdrops++; - vtnet_discard_rxbuf(sc, m); + if (vtnet_rxq_replace_buf(rxq, m, len) != 0) { + rxq->vtnrx_stats.vrxs_iqdrops++; + vtnet_rxq_discard_buf(rxq, m); if (nbufs > 1) - vtnet_discard_merged_rxbuf(sc, nbufs); + vtnet_rxq_discard_merged_bufs(rxq, nbufs); continue; } @@ -1608,263 +1764,297 @@ vtnet_rxeof(struct vtnet_softc *sc, int count, int *rx_npktsp) m->m_pkthdr.csum_flags = 0; if (nbufs > 1) { - if (vtnet_rxeof_merged(sc, m, nbufs) != 0) + /* Dequeue the rest of chain. */ + if (vtnet_rxq_merged_eof(rxq, m, nbufs) != 0) continue; } - ifp->if_ipackets++; - /* * Save copy of header before we strip it. For both mergeable - * and non-mergeable, the VirtIO header is placed first in the - * mbuf's data. We no longer need num_buffers, so always use a - * virtio_net_hdr. + * and non-mergeable, the header is at the beginning of the + * mbuf data. We no longer need num_buffers, so always use a + * regular header. + * + * BMV: Is this memcpy() expensive? We know the mbuf data is + * still valid even after the m_adj(). */ memcpy(hdr, mtod(m, void *), sizeof(struct virtio_net_hdr)); m_adj(m, adjsz); - if (ifp->if_capenable & IFCAP_VLAN_HWTAGGING) { - eh = mtod(m, struct ether_header *); - if (eh->ether_type == htons(ETHERTYPE_VLAN)) { - vtnet_vlan_tag_remove(m); - - /* - * With the 802.1Q header removed, update the - * checksum starting location accordingly. - */ - if (hdr->flags & VIRTIO_NET_HDR_F_NEEDS_CSUM) - hdr->csum_start -= - ETHER_VLAN_ENCAP_LEN; - } - } - - if (ifp->if_capenable & IFCAP_RXCSUM && - hdr->flags & VIRTIO_NET_HDR_F_NEEDS_CSUM) { - if (vtnet_rx_csum(sc, m, hdr) != 0) - sc->vtnet_stats.rx_csum_failed++; - } - - VTNET_UNLOCK(sc); - rx_npkts++; - (*ifp->if_input)(ifp, m); - VTNET_LOCK(sc); - - /* - * The interface may have been stopped while we were - * passing the packet up the network stack. - */ - if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) - break; + vtnet_rxq_input(rxq, m, hdr); } if (deq > 0) virtqueue_notify(vq); - if (rx_npktsp != NULL) - *rx_npktsp = rx_npkts; - return (count > 0 ? 0 : EAGAIN); } static void -vtnet_rx_vq_intr(void *xsc) +vtnet_rx_vq_intr(void *xrxq) { struct vtnet_softc *sc; + struct vtnet_rxq *rxq; struct ifnet *ifp; - int more; + int tries, more; - sc = xsc; + rxq = xrxq; + sc = rxq->vtnrx_sc; ifp = sc->vtnet_ifp; + tries = 0; + + if (__predict_false(rxq->vtnrx_id >= sc->vtnet_act_vq_pairs)) { + /* + * Ignore this interrupt. Either this is a spurious interrupt + * or multiqueue without per-VQ MSIX so every queue needs to + * be polled (a brain dead configuration we could try harder + * to avoid). + */ + vtnet_rxq_disable_intr(rxq); + return; + } again: - VTNET_LOCK(sc); - -#ifdef DEVICE_POLLING - if (ifp->if_capenable & IFCAP_POLLING) { - VTNET_UNLOCK(sc); - return; - } -#endif + VTNET_RXQ_LOCK(rxq); if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) { - vtnet_enable_rx_intr(sc); - VTNET_UNLOCK(sc); + VTNET_RXQ_UNLOCK(rxq); return; } - more = vtnet_rxeof(sc, sc->vtnet_rx_process_limit, NULL); - if (more || vtnet_enable_rx_intr(sc) != 0) { + more = vtnet_rxq_eof(rxq); + if (more || vtnet_rxq_enable_intr(rxq) != 0) { if (!more) - vtnet_disable_rx_intr(sc); - sc->vtnet_stats.rx_task_rescheduled++; - VTNET_UNLOCK(sc); - goto again; - } - - VTNET_UNLOCK(sc); + vtnet_rxq_disable_intr(rxq); + /* + * This is an occasional condition or race (when !more), + * so retry a few times before scheduling the taskqueue. + */ + rxq->vtnrx_stats.vrxs_rescheduled++; + VTNET_RXQ_UNLOCK(rxq); + if (tries++ < VTNET_INTR_DISABLE_RETRIES) + goto again; + taskqueue_enqueue(rxq->vtnrx_tq, &rxq->vtnrx_intrtask); + } else + VTNET_RXQ_UNLOCK(rxq); } static void -vtnet_txeof(struct vtnet_softc *sc) +vtnet_rxq_tq_intr(void *xrxq, int pending) +{ + struct vtnet_softc *sc; + struct vtnet_rxq *rxq; + struct ifnet *ifp; + int more; + + rxq = xrxq; + sc = rxq->vtnrx_sc; + ifp = sc->vtnet_ifp; + + VTNET_RXQ_LOCK(rxq); + + if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) { + VTNET_RXQ_UNLOCK(rxq); + return; + } + + more = vtnet_rxq_eof(rxq); + if (more || vtnet_rxq_enable_intr(rxq) != 0) { + if (!more) + vtnet_rxq_disable_intr(rxq); + rxq->vtnrx_stats.vrxs_rescheduled++; + taskqueue_enqueue(rxq->vtnrx_tq, &rxq->vtnrx_intrtask); + } + + VTNET_RXQ_UNLOCK(rxq); +} + +static void +vtnet_txq_free_mbufs(struct vtnet_txq *txq) { struct virtqueue *vq; - struct ifnet *ifp; struct vtnet_tx_header *txhdr; - int deq; + int last; - vq = sc->vtnet_tx_vq; - ifp = sc->vtnet_ifp; - deq = 0; + vq = txq->vtntx_vq; + last = 0; - VTNET_LOCK_ASSERT(sc); - - while ((txhdr = virtqueue_dequeue(vq, NULL)) != NULL) { - deq++; - ifp->if_opackets++; + while ((txhdr = virtqueue_drain(vq, &last)) != NULL) { m_freem(txhdr->vth_mbuf); uma_zfree(vtnet_tx_header_zone, txhdr); } - if (deq > 0) { - ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; - if (virtqueue_empty(vq)) - sc->vtnet_watchdog_timer = 0; - } + KASSERT(virtqueue_empty(vq), + ("%s: mbufs remaining in tx queue %p", __func__, txq)); } -static struct mbuf * -vtnet_tx_offload(struct vtnet_softc *sc, struct mbuf *m, - struct virtio_net_hdr *hdr) +/* + * BMV: Much of this can go away once we finally have offsets in + * the mbuf packet header. Bug andre@. + */ +static int +vtnet_txq_offload_ctx(struct vtnet_txq *txq, struct mbuf *m, + int *etype, int *proto, int *start) { - struct ifnet *ifp; - struct ether_header *eh; + struct vtnet_softc *sc; struct ether_vlan_header *evh; - struct ip *ip; - struct ip6_hdr *ip6; - struct tcphdr *tcp; - int ip_offset; - uint16_t eth_type, csum_start; - uint8_t ip_proto, gso_type; + int offset; - ifp = sc->vtnet_ifp; + sc = txq->vtntx_sc; - ip_offset = sizeof(struct ether_header); - if (m->m_len < ip_offset) { - if ((m = m_pullup(m, ip_offset)) == NULL) - return (NULL); + evh = mtod(m, struct ether_vlan_header *); + if (evh->evl_encap_proto == htons(ETHERTYPE_VLAN)) { + /* BMV: We should handle nested VLAN tags too. */ + *etype = ntohs(evh->evl_proto); + offset = sizeof(struct ether_vlan_header); + } else { + *etype = ntohs(evh->evl_encap_proto); + offset = sizeof(struct ether_header); } - eh = mtod(m, struct ether_header *); - eth_type = ntohs(eh->ether_type); - if (eth_type == ETHERTYPE_VLAN) { - ip_offset = sizeof(struct ether_vlan_header); - if (m->m_len < ip_offset) { - if ((m = m_pullup(m, ip_offset)) == NULL) - return (NULL); - } - evh = mtod(m, struct ether_vlan_header *); - eth_type = ntohs(evh->evl_proto); - } - - switch (eth_type) { - case ETHERTYPE_IP: - if (m->m_len < ip_offset + sizeof(struct ip)) { - m = m_pullup(m, ip_offset + sizeof(struct ip)); - if (m == NULL) - return (NULL); - } - - ip = (struct ip *)(mtod(m, uint8_t *) + ip_offset); - ip_proto = ip->ip_p; - csum_start = ip_offset + (ip->ip_hl << 2); - gso_type = VIRTIO_NET_HDR_GSO_TCPV4; + switch (*etype) { +#if defined(INET) + case ETHERTYPE_IP: { + struct ip *ip, iphdr; + if (__predict_false(m->m_len < offset + sizeof(struct ip))) { + m_copydata(m, offset, sizeof(struct ip), + (caddr_t) &iphdr); + ip = &iphdr; + } else + ip = (struct ip *)(m->m_data + offset); + *proto = ip->ip_p; + *start = offset + (ip->ip_hl << 2); break; - + } +#endif +#if defined(INET6) case ETHERTYPE_IPV6: - if (m->m_len < ip_offset + sizeof(struct ip6_hdr)) { - m = m_pullup(m, ip_offset + sizeof(struct ip6_hdr)); - if (m == NULL) - return (NULL); - } - - ip6 = (struct ip6_hdr *)(mtod(m, uint8_t *) + ip_offset); - /* - * XXX Assume no extension headers are present. Presently, - * this will always be true in the case of TSO, and FreeBSD - * does not perform checksum offloading of IPv6 yet. - */ - ip_proto = ip6->ip6_nxt; - csum_start = ip_offset + sizeof(struct ip6_hdr); - gso_type = VIRTIO_NET_HDR_GSO_TCPV6; + *proto = -1; + *start = ip6_lasthdr(m, offset, IPPROTO_IPV6, proto); + /* Assert the network stack sent us a valid packet. */ + KASSERT(*start > offset, + ("%s: mbuf %p start %d offset %d proto %d", __func__, m, + *start, offset, *proto)); break; - +#endif default: - return (m); + sc->vtnet_stats.tx_csum_bad_ethtype++; + return (EINVAL); } - if (m->m_pkthdr.csum_flags & VTNET_CSUM_OFFLOAD) { - hdr->flags |= VIRTIO_NET_HDR_F_NEEDS_CSUM; - hdr->csum_start = csum_start; - hdr->csum_offset = m->m_pkthdr.csum_data; - - sc->vtnet_stats.tx_csum_offloaded++; - } - - if (m->m_pkthdr.csum_flags & CSUM_TSO) { - if (ip_proto != IPPROTO_TCP) - return (m); - - if (m->m_len < csum_start + sizeof(struct tcphdr)) { - m = m_pullup(m, csum_start + sizeof(struct tcphdr)); - if (m == NULL) - return (NULL); - } - - tcp = (struct tcphdr *)(mtod(m, uint8_t *) + csum_start); - hdr->gso_type = gso_type; - hdr->hdr_len = csum_start + (tcp->th_off << 2); - hdr->gso_size = m->m_pkthdr.tso_segsz; - - if (tcp->th_flags & TH_CWR) { - /* - * Drop if we did not negotiate VIRTIO_NET_F_HOST_ECN. - * ECN support is only configurable globally with the - * net.inet.tcp.ecn.enable sysctl knob. - */ - if ((sc->vtnet_flags & VTNET_FLAG_TSO_ECN) == 0) { - if_printf(ifp, "TSO with ECN not supported " - "by host\n"); - m_freem(m); - return (NULL); - } - - hdr->flags |= VIRTIO_NET_HDR_GSO_ECN; - } - - sc->vtnet_stats.tx_tso_offloaded++; - } - - return (m); + return (0); } static int -vtnet_enqueue_txbuf(struct vtnet_softc *sc, struct mbuf **m_head, +vtnet_txq_offload_tso(struct vtnet_txq *txq, struct mbuf *m, int eth_type, + int offset, struct virtio_net_hdr *hdr) +{ + static struct timeval lastecn; + static int curecn; + struct vtnet_softc *sc; + struct tcphdr *tcp, tcphdr; + + sc = txq->vtntx_sc; + + if (__predict_false(m->m_len < offset + sizeof(struct tcphdr))) { + m_copydata(m, offset, sizeof(struct tcphdr), (caddr_t) &tcphdr); + tcp = &tcphdr; + } else + tcp = (struct tcphdr *)(m->m_data + offset); + + hdr->hdr_len = offset + (tcp->th_off << 2); + hdr->gso_size = m->m_pkthdr.tso_segsz; + hdr->gso_type = eth_type == ETHERTYPE_IP ? VIRTIO_NET_HDR_GSO_TCPV4 : + VIRTIO_NET_HDR_GSO_TCPV6; + + if (tcp->th_flags & TH_CWR) { + /* + * Drop if VIRTIO_NET_F_HOST_ECN was not negotiated. In FreeBSD, + * ECN support is not on a per-interface basis, but globally via + * the net.inet.tcp.ecn.enable sysctl knob. The default is off. + */ + if ((sc->vtnet_flags & VTNET_FLAG_TSO_ECN) == 0) { + if (ppsratecheck(&lastecn, &curecn, 1)) + if_printf(sc->vtnet_ifp, + "TSO with ECN not negotiated with host\n"); + return (ENOTSUP); + } + hdr->gso_type |= VIRTIO_NET_HDR_GSO_ECN; + } + + txq->vtntx_stats.vtxs_tso++; + + return (0); +} + +static struct mbuf * +vtnet_txq_offload(struct vtnet_txq *txq, struct mbuf *m, + struct virtio_net_hdr *hdr) +{ + struct vtnet_softc *sc; + int flags, etype, csum_start, proto, error; + + sc = txq->vtntx_sc; + flags = m->m_pkthdr.csum_flags; + + error = vtnet_txq_offload_ctx(txq, m, &etype, &proto, &csum_start); + if (error) + goto drop; + + if ((etype == ETHERTYPE_IP && flags & VTNET_CSUM_OFFLOAD) || + (etype == ETHERTYPE_IPV6 && flags & VTNET_CSUM_OFFLOAD_IPV6)) { + /* + * We could compare the IP protocol vs the CSUM_ flag too, + * but that really should not be necessary. + */ + hdr->flags |= VIRTIO_NET_HDR_F_NEEDS_CSUM; + hdr->csum_start = csum_start; + hdr->csum_offset = m->m_pkthdr.csum_data; + txq->vtntx_stats.vtxs_csum++; + } + + if (flags & CSUM_TSO) { + if (__predict_false(proto != IPPROTO_TCP)) { + /* Likely failed to correctly parse the mbuf. */ + sc->vtnet_stats.tx_tso_not_tcp++; + goto drop; + } + + KASSERT(hdr->flags & VIRTIO_NET_HDR_F_NEEDS_CSUM, + ("%s: mbuf %p TSO without checksum offload", __func__, m)); + + error = vtnet_txq_offload_tso(txq, m, etype, csum_start, hdr); + if (error) + goto drop; + } + + return (m); + +drop: + m_freem(m); + return (NULL); +} + +static int +vtnet_txq_enqueue_buf(struct vtnet_txq *txq, struct mbuf **m_head, struct vtnet_tx_header *txhdr) { struct sglist sg; struct sglist_seg segs[VTNET_MAX_TX_SEGS]; + struct vtnet_softc *sc; struct virtqueue *vq; struct mbuf *m; int collapsed, error; - vq = sc->vtnet_tx_vq; + vq = txq->vtntx_vq; + sc = txq->vtntx_sc; m = *m_head; collapsed = 0; sglist_init(&sg, VTNET_MAX_TX_SEGS, segs); error = sglist_append(&sg, &txhdr->vth_uhdr, sc->vtnet_hdr_size); KASSERT(error == 0 && sg.sg_nseg == 1, - ("%s: cannot add header to sglist error %d", __func__, error)); + ("%s: error %d adding header to sglist", __func__, error)); again: error = sglist_append_mbuf(&sg, m); @@ -1878,12 +2068,14 @@ again: *m_head = m; collapsed = 1; + txq->vtntx_stats.vtxs_collapsed++; goto again; } txhdr->vth_mbuf = m; + error = virtqueue_enqueue(vq, txhdr, &sg, sg.sg_nseg, 0); - return (virtqueue_enqueue(vq, txhdr, &sg, sg.sg_nseg, 0)); + return (error); fail: m_freem(*m_head); @@ -1893,28 +2085,29 @@ fail: } static int -vtnet_encap(struct vtnet_softc *sc, struct mbuf **m_head) +vtnet_txq_encap(struct vtnet_txq *txq, struct mbuf **m_head) { + struct vtnet_softc *sc; struct vtnet_tx_header *txhdr; struct virtio_net_hdr *hdr; struct mbuf *m; int error; + sc = txq->vtntx_sc; m = *m_head; M_ASSERTPKTHDR(m); txhdr = uma_zalloc(vtnet_tx_header_zone, M_NOWAIT | M_ZERO); if (txhdr == NULL) { - *m_head = NULL; m_freem(m); + *m_head = NULL; return (ENOMEM); } /* - * Always use the non-mergeable header to simplify things. When - * the mergeable feature is negotiated, the num_buffers field - * must be set to zero. We use vtnet_hdr_size later to enqueue - * the correct header size to the host. + * Always use the non-mergeable header, regardless if the feature + * was negotiated. For transmit, num_buffers is always zero. The + * vtnet_hdr_size is used to enqueue the correct header size. */ hdr = &txhdr->vth_uhdr.hdr; @@ -1927,72 +2120,57 @@ vtnet_encap(struct vtnet_softc *sc, struct mbuf **m_head) m->m_flags &= ~M_VLANTAG; } - if (m->m_pkthdr.csum_flags != 0) { - m = vtnet_tx_offload(sc, m, hdr); + if (m->m_pkthdr.csum_flags & VTNET_CSUM_ALL_OFFLOAD) { + m = vtnet_txq_offload(txq, m, hdr); if ((*m_head = m) == NULL) { error = ENOBUFS; goto fail; } } - error = vtnet_enqueue_txbuf(sc, m_head, txhdr); + error = vtnet_txq_enqueue_buf(txq, m_head, txhdr); + if (error == 0) + return (0); + fail: - if (error) - uma_zfree(vtnet_tx_header_zone, txhdr); + uma_zfree(vtnet_tx_header_zone, txhdr); return (error); } -static void -vtnet_start(struct ifnet *ifp) -{ - struct vtnet_softc *sc; - - sc = ifp->if_softc; - - VTNET_LOCK(sc); - vtnet_start_locked(ifp); - VTNET_UNLOCK(sc); -} +#ifdef VTNET_LEGACY_TX static void -vtnet_start_locked(struct ifnet *ifp) +vtnet_start_locked(struct vtnet_txq *txq, struct ifnet *ifp) { struct vtnet_softc *sc; struct virtqueue *vq; struct mbuf *m0; int enq; - sc = ifp->if_softc; - vq = sc->vtnet_tx_vq; + sc = txq->vtntx_sc; + vq = txq->vtntx_vq; enq = 0; - VTNET_LOCK_ASSERT(sc); + VTNET_TXQ_LOCK_ASSERT(txq); - if ((ifp->if_drv_flags & (IFF_DRV_RUNNING | IFF_DRV_OACTIVE)) != - IFF_DRV_RUNNING || ((sc->vtnet_flags & VTNET_FLAG_LINK) == 0)) + if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0 || + sc->vtnet_link_active == 0) return; -#ifdef VTNET_TX_INTR_MODERATION - if (virtqueue_nused(vq) >= sc->vtnet_tx_size / 2) - vtnet_txeof(sc); -#endif + vtnet_txq_eof(txq); while (!IFQ_DRV_IS_EMPTY(&ifp->if_snd)) { - if (virtqueue_full(vq)) { - ifp->if_drv_flags |= IFF_DRV_OACTIVE; + if (virtqueue_full(vq)) break; - } IFQ_DRV_DEQUEUE(&ifp->if_snd, m0); if (m0 == NULL) break; - if (vtnet_encap(sc, &m0) != 0) { - if (m0 == NULL) - break; - IFQ_DRV_PREPEND(&ifp->if_snd, m0); - ifp->if_drv_flags |= IFF_DRV_OACTIVE; + if (vtnet_txq_encap(txq, &m0) != 0) { + if (m0 != NULL) + IFQ_DRV_PREPEND(&ifp->if_snd, m0); break; } @@ -2002,65 +2180,533 @@ vtnet_start_locked(struct ifnet *ifp) if (enq > 0) { virtqueue_notify(vq); - sc->vtnet_watchdog_timer = VTNET_WATCHDOG_TIMEOUT; + txq->vtntx_watchdog = VTNET_TX_TIMEOUT; } } +static void +vtnet_start(struct ifnet *ifp) +{ + struct vtnet_softc *sc; + struct vtnet_txq *txq; + + sc = ifp->if_softc; + txq = &sc->vtnet_txqs[0]; + + VTNET_TXQ_LOCK(txq); + vtnet_start_locked(txq, ifp); + VTNET_TXQ_UNLOCK(txq); +} + +#else /* !VTNET_LEGACY_TX */ + +static int +vtnet_txq_mq_start_locked(struct vtnet_txq *txq, struct mbuf *m) +{ + struct vtnet_softc *sc; + struct virtqueue *vq; + struct buf_ring *br; + struct ifnet *ifp; + int enq, error; + + sc = txq->vtntx_sc; + vq = txq->vtntx_vq; + br = txq->vtntx_br; + ifp = sc->vtnet_ifp; + enq = 0; + error = 0; + + VTNET_TXQ_LOCK_ASSERT(txq); + + if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0 || + sc->vtnet_link_active == 0) { + if (m != NULL) + error = drbr_enqueue(ifp, br, m); + return (error); + } + + if (m != NULL) { + error = drbr_enqueue(ifp, br, m); + if (error) + return (error); + } + + vtnet_txq_eof(txq); + + while ((m = drbr_peek(ifp, br)) != NULL) { + error = vtnet_txq_encap(txq, &m); + if (error) { + if (m != NULL) + drbr_putback(ifp, br, m); + else + drbr_advance(ifp, br); + break; + } + drbr_advance(ifp, br); + + enq++; + ETHER_BPF_MTAP(ifp, m); + } + + if (enq > 0) { + virtqueue_notify(vq); + txq->vtntx_watchdog = VTNET_TX_TIMEOUT; + } + + return (error); +} + +static int +vtnet_txq_mq_start(struct ifnet *ifp, struct mbuf *m) +{ + struct vtnet_softc *sc; + struct vtnet_txq *txq; + int i, npairs, error; + + sc = ifp->if_softc; + npairs = sc->vtnet_act_vq_pairs; + + if (m->m_flags & M_FLOWID) + i = m->m_pkthdr.flowid % npairs; + else + i = curcpu % npairs; + + txq = &sc->vtnet_txqs[i]; + + if (VTNET_TXQ_TRYLOCK(txq) != 0) { + error = vtnet_txq_mq_start_locked(txq, m); + VTNET_TXQ_UNLOCK(txq); + } else { + error = drbr_enqueue(ifp, txq->vtntx_br, m); + taskqueue_enqueue(txq->vtntx_tq, &txq->vtntx_defrtask); + } + + return (error); +} + +static void +vtnet_txq_tq_deferred(void *xtxq, int pending) +{ + struct vtnet_softc *sc; + struct vtnet_txq *txq; + + txq = xtxq; + sc = txq->vtntx_sc; + + VTNET_TXQ_LOCK(txq); + if (!drbr_empty(sc->vtnet_ifp, txq->vtntx_br)) + vtnet_txq_mq_start_locked(txq, NULL); + VTNET_TXQ_UNLOCK(txq); +} + +#endif /* VTNET_LEGACY_TX */ + +static void +vtnet_txq_tq_intr(void *xtxq, int pending) +{ + struct vtnet_softc *sc; + struct vtnet_txq *txq; + struct ifnet *ifp; + + txq = xtxq; + sc = txq->vtntx_sc; + ifp = sc->vtnet_ifp; + + VTNET_TXQ_LOCK(txq); + + if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) { + VTNET_TXQ_UNLOCK(txq); + return; + } + + vtnet_txq_eof(txq); + +#ifdef VTNET_LEGACY_TX + if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd)) + vtnet_start_locked(txq, ifp); +#else + if (!drbr_empty(ifp, txq->vtntx_br)) + vtnet_txq_mq_start_locked(txq, NULL); +#endif + + if (vtnet_txq_enable_intr(txq) != 0) { + vtnet_txq_disable_intr(txq); + txq->vtntx_stats.vtxs_rescheduled++; + taskqueue_enqueue(txq->vtntx_tq, &txq->vtntx_intrtask); + } + + VTNET_TXQ_UNLOCK(txq); +} + +static void +vtnet_txq_eof(struct vtnet_txq *txq) +{ + struct virtqueue *vq; + struct vtnet_tx_header *txhdr; + struct mbuf *m; + + vq = txq->vtntx_vq; + VTNET_TXQ_LOCK_ASSERT(txq); + + while ((txhdr = virtqueue_dequeue(vq, NULL)) != NULL) { + m = txhdr->vth_mbuf; + + txq->vtntx_stats.vtxs_opackets++; + txq->vtntx_stats.vtxs_obytes += m->m_pkthdr.len; + if (m->m_flags & M_MCAST) + txq->vtntx_stats.vtxs_omcasts++; + + m_freem(m); + uma_zfree(vtnet_tx_header_zone, txhdr); + } + + if (virtqueue_empty(vq)) + txq->vtntx_watchdog = 0; +} + +static void +vtnet_tx_vq_intr(void *xtxq) +{ + struct vtnet_softc *sc; + struct vtnet_txq *txq; + struct ifnet *ifp; + int tries; + + txq = xtxq; + sc = txq->vtntx_sc; + ifp = sc->vtnet_ifp; + tries = 0; + + if (__predict_false(txq->vtntx_id >= sc->vtnet_act_vq_pairs)) { + /* + * Ignore this interrupt. Either this is a spurious interrupt + * or multiqueue without per-VQ MSIX so every queue needs to + * be polled (a brain dead configuration we could try harder + * to avoid). + */ + vtnet_txq_disable_intr(txq); + return; + } + +again: + VTNET_TXQ_LOCK(txq); + + if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) { + VTNET_TXQ_UNLOCK(txq); + return; + } + + vtnet_txq_eof(txq); + +#ifdef VTNET_LEGACY_TX + if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd)) + vtnet_start_locked(txq, ifp); +#else + if (!drbr_empty(ifp, txq->vtntx_br)) + vtnet_txq_mq_start_locked(txq, NULL); +#endif + + if (vtnet_txq_enable_intr(txq) != 0) { + vtnet_txq_disable_intr(txq); + /* + * This is an occasional race, so retry a few times + * before scheduling the taskqueue. + */ + VTNET_TXQ_UNLOCK(txq); + if (tries++ < VTNET_INTR_DISABLE_RETRIES) + goto again; + txq->vtntx_stats.vtxs_rescheduled++; + taskqueue_enqueue(txq->vtntx_tq, &txq->vtntx_intrtask); + } else + VTNET_TXQ_UNLOCK(txq); +} + +static void +vtnet_tx_start_all(struct vtnet_softc *sc) +{ + struct ifnet *ifp; + struct vtnet_txq *txq; + int i; + + ifp = sc->vtnet_ifp; + VTNET_CORE_LOCK_ASSERT(sc); + + for (i = 0; i < sc->vtnet_act_vq_pairs; i++) { + txq = &sc->vtnet_txqs[i]; + + VTNET_TXQ_LOCK(txq); +#ifdef VTNET_LEGACY_TX + if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd)) + vtnet_start_locked(txq, ifp); +#else + if (!drbr_empty(ifp, txq->vtntx_br)) + vtnet_txq_mq_start_locked(txq, NULL); +#endif + VTNET_TXQ_UNLOCK(txq); + } +} + +#ifndef VTNET_LEGACY_TX +static void +vtnet_qflush(struct ifnet *ifp) +{ + struct vtnet_softc *sc; + struct vtnet_txq *txq; + struct mbuf *m; + int i; + + sc = ifp->if_softc; + + for (i = 0; i < sc->vtnet_act_vq_pairs; i++) { + txq = &sc->vtnet_txqs[i]; + + VTNET_TXQ_LOCK(txq); + while ((m = buf_ring_dequeue_sc(txq->vtntx_br)) != NULL) + m_freem(m); + VTNET_TXQ_UNLOCK(txq); + } + + if_qflush(ifp); +} +#endif + +static int +vtnet_watchdog(struct vtnet_txq *txq) +{ + struct vtnet_softc *sc; + + sc = txq->vtntx_sc; + + VTNET_TXQ_LOCK(txq); + if (sc->vtnet_flags & VTNET_FLAG_EVENT_IDX) + vtnet_txq_eof(txq); + if (txq->vtntx_watchdog == 0 || --txq->vtntx_watchdog) { + VTNET_TXQ_UNLOCK(txq); + return (0); + } + VTNET_TXQ_UNLOCK(txq); + + if_printf(sc->vtnet_ifp, "watchdog timeout on queue %d\n", + txq->vtntx_id); + return (1); +} + +static void +vtnet_rxq_accum_stats(struct vtnet_rxq *rxq, struct vtnet_rxq_stats *accum) +{ + struct vtnet_rxq_stats *st; + + st = &rxq->vtnrx_stats; + + accum->vrxs_ipackets += st->vrxs_ipackets; + accum->vrxs_ibytes += st->vrxs_ibytes; + accum->vrxs_iqdrops += st->vrxs_iqdrops; + accum->vrxs_csum += st->vrxs_csum; + accum->vrxs_csum_failed += st->vrxs_csum_failed; + accum->vrxs_rescheduled += st->vrxs_rescheduled; +} + +static void +vtnet_txq_accum_stats(struct vtnet_txq *txq, struct vtnet_txq_stats *accum) +{ + struct vtnet_txq_stats *st; + + st = &txq->vtntx_stats; + + accum->vtxs_opackets += st->vtxs_opackets; + accum->vtxs_obytes += st->vtxs_obytes; + accum->vtxs_csum += st->vtxs_csum; + accum->vtxs_tso += st->vtxs_tso; + accum->vtxs_collapsed += st->vtxs_collapsed; + accum->vtxs_rescheduled += st->vtxs_rescheduled; +} + +static void +vtnet_accumulate_stats(struct vtnet_softc *sc) +{ + struct ifnet *ifp; + struct vtnet_statistics *st; + struct vtnet_rxq_stats rxaccum; + struct vtnet_txq_stats txaccum; + int i; + + ifp = sc->vtnet_ifp; + st = &sc->vtnet_stats; + bzero(&rxaccum, sizeof(struct vtnet_rxq_stats)); + bzero(&txaccum, sizeof(struct vtnet_txq_stats)); + + for (i = 0; i < sc->vtnet_max_vq_pairs; i++) { + vtnet_rxq_accum_stats(&sc->vtnet_rxqs[i], &rxaccum); + vtnet_txq_accum_stats(&sc->vtnet_txqs[i], &txaccum); + } + + st->rx_csum_offloaded = rxaccum.vrxs_csum; + st->rx_csum_failed = rxaccum.vrxs_csum_failed; + st->rx_task_rescheduled = rxaccum.vrxs_rescheduled; + st->tx_csum_offloaded = txaccum.vtxs_csum; + st->tx_tso_offloaded = txaccum.vtxs_tso; + st->tx_task_rescheduled = txaccum.vtxs_rescheduled; + + /* + * With the exception of if_ierrors, these ifnet statistics are + * only updated in the driver, so just set them to our accumulated + * values. if_ierrors is updated in ether_input() for malformed + * frames that we should have already discarded. + */ + ifp->if_ipackets = rxaccum.vrxs_ipackets; + ifp->if_iqdrops = rxaccum.vrxs_iqdrops; + ifp->if_ierrors = rxaccum.vrxs_ierrors; + ifp->if_opackets = txaccum.vtxs_opackets; +#ifndef VTNET_LEGACY_TX + ifp->if_obytes = txaccum.vtxs_obytes; + ifp->if_omcasts = txaccum.vtxs_omcasts; +#endif +} + static void vtnet_tick(void *xsc) -{ - struct vtnet_softc *sc; - - sc = xsc; - - VTNET_LOCK_ASSERT(sc); -#ifdef VTNET_DEBUG - virtqueue_dump(sc->vtnet_rx_vq); - virtqueue_dump(sc->vtnet_tx_vq); -#endif - - vtnet_watchdog(sc); - callout_reset(&sc->vtnet_tick_ch, hz, vtnet_tick, sc); -} - -static void -vtnet_tx_vq_intr(void *xsc) { struct vtnet_softc *sc; struct ifnet *ifp; + int i, timedout; sc = xsc; ifp = sc->vtnet_ifp; + timedout = 0; -again: - VTNET_LOCK(sc); + VTNET_CORE_LOCK_ASSERT(sc); + vtnet_accumulate_stats(sc); -#ifdef DEVICE_POLLING - if (ifp->if_capenable & IFCAP_POLLING) { - VTNET_UNLOCK(sc); - return; + for (i = 0; i < sc->vtnet_act_vq_pairs; i++) + timedout |= vtnet_watchdog(&sc->vtnet_txqs[i]); + + if (timedout != 0) { + ifp->if_drv_flags &= ~IFF_DRV_RUNNING; + vtnet_init_locked(sc); + } else + callout_schedule(&sc->vtnet_tick_ch, hz); +} + +static void +vtnet_start_taskqueues(struct vtnet_softc *sc) +{ + device_t dev; + struct vtnet_rxq *rxq; + struct vtnet_txq *txq; + int i, error; + + dev = sc->vtnet_dev; + + /* + * Errors here are very difficult to recover from - we cannot + * easily fail because, if this is during boot, we will hang + * when freeing any successfully started taskqueues because + * the scheduler isn't up yet. + * + * Most drivers just ignore the return value - it only fails + * with ENOMEM so an error is not likely. + */ + for (i = 0; i < sc->vtnet_max_vq_pairs; i++) { + rxq = &sc->vtnet_rxqs[i]; + error = taskqueue_start_threads(&rxq->vtnrx_tq, 1, PI_NET, + "%s rxq %d", device_get_nameunit(dev), rxq->vtnrx_id); + if (error) { + device_printf(dev, "failed to start rx taskq %d\n", + rxq->vtnrx_id); + } + + txq = &sc->vtnet_txqs[i]; + error = taskqueue_start_threads(&txq->vtntx_tq, 1, PI_NET, + "%s txq %d", device_get_nameunit(dev), txq->vtntx_id); + if (error) { + device_printf(dev, "failed to start tx taskq %d\n", + txq->vtntx_id); + } } +} + +static void +vtnet_free_taskqueues(struct vtnet_softc *sc) +{ + struct vtnet_rxq *rxq; + struct vtnet_txq *txq; + int i; + + for (i = 0; i < sc->vtnet_max_vq_pairs; i++) { + rxq = &sc->vtnet_rxqs[i]; + if (rxq->vtnrx_tq != NULL) { + taskqueue_free(rxq->vtnrx_tq); + rxq->vtnrx_vq = NULL; + } + + txq = &sc->vtnet_txqs[i]; + if (txq->vtntx_tq != NULL) { + taskqueue_free(txq->vtntx_tq); + txq->vtntx_tq = NULL; + } + } +} + +static void +vtnet_drain_taskqueues(struct vtnet_softc *sc) +{ + struct vtnet_rxq *rxq; + struct vtnet_txq *txq; + int i; + + for (i = 0; i < sc->vtnet_max_vq_pairs; i++) { + rxq = &sc->vtnet_rxqs[i]; + if (rxq->vtnrx_tq != NULL) + taskqueue_drain(rxq->vtnrx_tq, &rxq->vtnrx_intrtask); + + txq = &sc->vtnet_txqs[i]; + if (txq->vtntx_tq != NULL) { + taskqueue_drain(txq->vtntx_tq, &txq->vtntx_intrtask); +#ifndef VTNET_LEGACY_TX + taskqueue_drain(txq->vtntx_tq, &txq->vtntx_defrtask); #endif - - if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) { - vtnet_enable_tx_intr(sc); - VTNET_UNLOCK(sc); - return; + } } +} - vtnet_txeof(sc); +static void +vtnet_drain_rxtx_queues(struct vtnet_softc *sc) +{ + struct vtnet_rxq *rxq; + struct vtnet_txq *txq; + int i; - if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd)) - vtnet_start_locked(ifp); + for (i = 0; i < sc->vtnet_act_vq_pairs; i++) { + rxq = &sc->vtnet_rxqs[i]; + vtnet_rxq_free_mbufs(rxq); - if (vtnet_enable_tx_intr(sc) != 0) { - vtnet_disable_tx_intr(sc); - sc->vtnet_stats.tx_task_rescheduled++; - VTNET_UNLOCK(sc); - goto again; + txq = &sc->vtnet_txqs[i]; + vtnet_txq_free_mbufs(txq); } +} - VTNET_UNLOCK(sc); +static void +vtnet_stop_rendezvous(struct vtnet_softc *sc) +{ + struct vtnet_rxq *rxq; + struct vtnet_txq *txq; + int i; + + /* + * Lock and unlock the per-queue mutex so we known the stop + * state is visible. Doing only the active queues should be + * sufficient, but it does not cost much extra to do all the + * queues. Note we hold the core mutex here too. + */ + for (i = 0; i < sc->vtnet_max_vq_pairs; i++) { + rxq = &sc->vtnet_rxqs[i]; + VTNET_RXQ_LOCK(rxq); + VTNET_RXQ_UNLOCK(rxq); + + txq = &sc->vtnet_txqs[i]; + VTNET_TXQ_LOCK(txq); + VTNET_TXQ_UNLOCK(txq); + } } static void @@ -2072,46 +2718,60 @@ vtnet_stop(struct vtnet_softc *sc) dev = sc->vtnet_dev; ifp = sc->vtnet_ifp; - VTNET_LOCK_ASSERT(sc); + VTNET_CORE_LOCK_ASSERT(sc); - sc->vtnet_watchdog_timer = 0; + ifp->if_drv_flags &= ~IFF_DRV_RUNNING; + sc->vtnet_link_active = 0; callout_stop(&sc->vtnet_tick_ch); - ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE); - vtnet_disable_rx_intr(sc); - vtnet_disable_tx_intr(sc); + /* Only advisory. */ + vtnet_disable_interrupts(sc); /* - * Stop the host VirtIO adapter. Note this will reset the host - * adapter's state back to the pre-initialized state, so in - * order to make the device usable again, we must drive it - * through virtio_reinit() and virtio_reinit_complete(). + * Stop the host adapter. This resets it to the pre-initialized + * state. It will not generate any interrupts until after it is + * reinitialized. */ virtio_stop(dev); + vtnet_stop_rendezvous(sc); - sc->vtnet_flags &= ~VTNET_FLAG_LINK; - - vtnet_free_rx_mbufs(sc); - vtnet_free_tx_mbufs(sc); + /* Free any mbufs left in the virtqueues. */ + vtnet_drain_rxtx_queues(sc); } static int -vtnet_reinit(struct vtnet_softc *sc) +vtnet_virtio_reinit(struct vtnet_softc *sc) { + device_t dev; struct ifnet *ifp; uint64_t features; + int mask, error; + dev = sc->vtnet_dev; ifp = sc->vtnet_ifp; features = sc->vtnet_features; + mask = 0; +#if defined(INET) + mask |= IFCAP_RXCSUM; +#endif +#if defined (INET6) + mask |= IFCAP_RXCSUM_IPV6; +#endif + /* * Re-negotiate with the host, removing any disabled receive * features. Transmit features are disabled only on our side * via if_capenable and if_hwassist. */ - if (ifp->if_capabilities & IFCAP_RXCSUM) { - if ((ifp->if_capenable & IFCAP_RXCSUM) == 0) + if (ifp->if_capabilities & mask) { + /* + * We require both IPv4 and IPv6 offloading to be enabled + * in order to negotiated it: VirtIO does not distinguish + * between the two. + */ + if ((ifp->if_capenable & mask) != mask) features &= ~VIRTIO_NET_F_GUEST_CSUM; } @@ -2125,11 +2785,136 @@ vtnet_reinit(struct vtnet_softc *sc) features &= ~VIRTIO_NET_F_CTRL_VLAN; } - return (virtio_reinit(sc->vtnet_dev, features)); + error = virtio_reinit(dev, features); + if (error) + device_printf(dev, "virtio reinit error %d\n", error); + + return (error); } static void -vtnet_init_locked(struct vtnet_softc *sc) +vtnet_init_rx_filters(struct vtnet_softc *sc) +{ + struct ifnet *ifp; + + ifp = sc->vtnet_ifp; + + if (sc->vtnet_flags & VTNET_FLAG_CTRL_RX) { + /* Restore promiscuous and all-multicast modes. */ + vtnet_rx_filter(sc); + /* Restore filtered MAC addresses. */ + vtnet_rx_filter_mac(sc); + } + + if (ifp->if_capenable & IFCAP_VLAN_HWFILTER) + vtnet_rx_filter_vlan(sc); +} + +static int +vtnet_init_rx_queues(struct vtnet_softc *sc) +{ + device_t dev; + struct vtnet_rxq *rxq; + int i, clsize, error; + + dev = sc->vtnet_dev; + + /* + * Use the new cluster size if one has been set (via a MTU + * change). Otherwise, use the standard 2K clusters. + * + * BMV: It might make sense to use page sized clusters as + * the default (depending on the features negotiated). + */ + if (sc->vtnet_rx_new_clsize != 0) { + clsize = sc->vtnet_rx_new_clsize; + sc->vtnet_rx_new_clsize = 0; + } else + clsize = MCLBYTES; + + sc->vtnet_rx_clsize = clsize; + sc->vtnet_rx_nmbufs = VTNET_NEEDED_RX_MBUFS(sc, clsize); + + /* The first segment is reserved for the header. */ + KASSERT(sc->vtnet_rx_nmbufs < VTNET_MAX_RX_SEGS, + ("%s: too many rx mbufs %d", __func__, sc->vtnet_rx_nmbufs)); + + for (i = 0; i < sc->vtnet_act_vq_pairs; i++) { + rxq = &sc->vtnet_rxqs[i]; + + /* Hold the lock to satisfy asserts. */ + VTNET_RXQ_LOCK(rxq); + error = vtnet_rxq_populate(rxq); + VTNET_RXQ_UNLOCK(rxq); + + if (error) { + device_printf(dev, + "cannot allocate mbufs for Rx queue %d\n", i); + return (error); + } + } + + return (0); +} + +static int +vtnet_init_tx_queues(struct vtnet_softc *sc) +{ + struct vtnet_txq *txq; + int i; + + for (i = 0; i < sc->vtnet_act_vq_pairs; i++) { + txq = &sc->vtnet_txqs[i]; + txq->vtntx_watchdog = 0; + } + + return (0); +} + +static int +vtnet_init_rxtx_queues(struct vtnet_softc *sc) +{ + int error; + + error = vtnet_init_rx_queues(sc); + if (error) + return (error); + + error = vtnet_init_tx_queues(sc); + if (error) + return (error); + + return (0); +} + +static void +vtnet_set_active_vq_pairs(struct vtnet_softc *sc) +{ + device_t dev; + int npairs; + + dev = sc->vtnet_dev; + + if ((sc->vtnet_flags & VTNET_FLAG_MULTIQ) == 0) { + MPASS(sc->vtnet_max_vq_pairs == 1); + sc->vtnet_act_vq_pairs = 1; + return; + } + + /* BMV: Just use the maximum configured for now. */ + npairs = sc->vtnet_max_vq_pairs; + + if (vtnet_ctrl_mq_cmd(sc, npairs) != 0) { + device_printf(dev, + "cannot set active queue pairs to %d\n", npairs); + npairs = 1; + } + + sc->vtnet_act_vq_pairs = npairs; +} + +static int +vtnet_reinit(struct vtnet_softc *sc) { device_t dev; struct ifnet *ifp; @@ -2138,73 +2923,67 @@ vtnet_init_locked(struct vtnet_softc *sc) dev = sc->vtnet_dev; ifp = sc->vtnet_ifp; - VTNET_LOCK_ASSERT(sc); - - if (ifp->if_drv_flags & IFF_DRV_RUNNING) - return; - - /* Stop host's adapter, cancel any pending I/O. */ - vtnet_stop(sc); - - /* Reinitialize the host device. */ - error = vtnet_reinit(sc); - if (error) { - device_printf(dev, - "reinitialization failed, stopping device...\n"); - vtnet_stop(sc); - return; - } - - /* Update host with assigned MAC address. */ + /* Use the current MAC address. */ bcopy(IF_LLADDR(ifp), sc->vtnet_hwaddr, ETHER_ADDR_LEN); vtnet_set_hwaddr(sc); + vtnet_set_active_vq_pairs(sc); + ifp->if_hwassist = 0; if (ifp->if_capenable & IFCAP_TXCSUM) ifp->if_hwassist |= VTNET_CSUM_OFFLOAD; + if (ifp->if_capenable & IFCAP_TXCSUM_IPV6) + ifp->if_hwassist |= VTNET_CSUM_OFFLOAD_IPV6; if (ifp->if_capenable & IFCAP_TSO4) ifp->if_hwassist |= CSUM_TSO; + if (ifp->if_capenable & IFCAP_TSO6) + ifp->if_hwassist |= CSUM_TSO; /* No CSUM_TSO_IPV6. */ - error = vtnet_init_rx_vq(sc); - if (error) { - device_printf(dev, - "cannot allocate mbufs for Rx virtqueue\n"); - vtnet_stop(sc); - return; - } + if (sc->vtnet_flags & VTNET_FLAG_CTRL_VQ) + vtnet_init_rx_filters(sc); - if (sc->vtnet_flags & VTNET_FLAG_CTRL_VQ) { - if (sc->vtnet_flags & VTNET_FLAG_CTRL_RX) { - /* Restore promiscuous and all-multicast modes. */ - vtnet_rx_filter(sc); - - /* Restore filtered MAC addresses. */ - vtnet_rx_filter_mac(sc); - } - - /* Restore VLAN filters. */ - if (ifp->if_capenable & IFCAP_VLAN_HWFILTER) - vtnet_rx_filter_vlan(sc); - } - -#ifdef DEVICE_POLLING - if (ifp->if_capenable & IFCAP_POLLING) { - vtnet_disable_rx_intr(sc); - vtnet_disable_tx_intr(sc); - } else -#endif - { - vtnet_enable_rx_intr(sc); - vtnet_enable_tx_intr(sc); - } + error = vtnet_init_rxtx_queues(sc); + if (error) + return (error); + vtnet_enable_interrupts(sc); ifp->if_drv_flags |= IFF_DRV_RUNNING; - ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; + + return (0); +} + +static void +vtnet_init_locked(struct vtnet_softc *sc) +{ + device_t dev; + struct ifnet *ifp; + + dev = sc->vtnet_dev; + ifp = sc->vtnet_ifp; + + VTNET_CORE_LOCK_ASSERT(sc); + + if (ifp->if_drv_flags & IFF_DRV_RUNNING) + return; + + vtnet_stop(sc); + + /* Reinitialize with the host. */ + if (vtnet_virtio_reinit(sc) != 0) + goto fail; + + if (vtnet_reinit(sc) != 0) + goto fail; virtio_reinit_complete(dev); vtnet_update_link_status(sc); callout_reset(&sc->vtnet_tick_ch, hz, vtnet_tick, sc); + + return; + +fail: + vtnet_stop(sc); } static void @@ -2214,9 +2993,24 @@ vtnet_init(void *xsc) sc = xsc; - VTNET_LOCK(sc); + VTNET_CORE_LOCK(sc); vtnet_init_locked(sc); - VTNET_UNLOCK(sc); + VTNET_CORE_UNLOCK(sc); +} + +static void +vtnet_free_ctrl_vq(struct vtnet_softc *sc) +{ + struct virtqueue *vq; + + vq = sc->vtnet_ctrl_vq; + + /* + * The control virtqueue is only polled and therefore it should + * already be empty. + */ + KASSERT(virtqueue_empty(vq), + ("%s: ctrl vq %p not empty", __func__, vq)); } static void @@ -2224,87 +3018,117 @@ vtnet_exec_ctrl_cmd(struct vtnet_softc *sc, void *cookie, struct sglist *sg, int readable, int writable) { struct virtqueue *vq; - void *c; vq = sc->vtnet_ctrl_vq; - VTNET_LOCK_ASSERT(sc); + VTNET_CORE_LOCK_ASSERT(sc); KASSERT(sc->vtnet_flags & VTNET_FLAG_CTRL_VQ, - ("no control virtqueue")); - KASSERT(virtqueue_empty(vq), - ("control command already enqueued")); + ("%s: CTRL_VQ feature not negotiated", __func__)); + if (!virtqueue_empty(vq)) + return; if (virtqueue_enqueue(vq, cookie, sg, readable, writable) != 0) return; - virtqueue_notify(vq); - /* - * Poll until the command is complete. Previously, we would - * sleep until the control virtqueue interrupt handler woke - * us up, but dropping the VTNET_MTX leads to serialization - * difficulties. - * - * Furthermore, it appears QEMU/KVM only allocates three MSIX - * vectors. Two of those vectors are needed for the Rx and Tx - * virtqueues. We do not support sharing both a Vq and config - * changed notification on the same MSIX vector. + * Poll for the response, but the command is likely already + * done when we return from the notify. */ - c = virtqueue_poll(vq, NULL); - KASSERT(c == cookie, ("unexpected control command response")); + virtqueue_notify(vq); + virtqueue_poll(vq, NULL); } -static void -vtnet_rx_filter(struct vtnet_softc *sc) +static int +vtnet_ctrl_mac_cmd(struct vtnet_softc *sc, uint8_t *hwaddr) { - device_t dev; - struct ifnet *ifp; + struct virtio_net_ctrl_hdr hdr; + struct sglist_seg segs[3]; + struct sglist sg; + uint8_t ack; + int error; - dev = sc->vtnet_dev; - ifp = sc->vtnet_ifp; + hdr.class = VIRTIO_NET_CTRL_MAC; + hdr.cmd = VIRTIO_NET_CTRL_MAC_ADDR_SET; + ack = VIRTIO_NET_ERR; - VTNET_LOCK_ASSERT(sc); - KASSERT(sc->vtnet_flags & VTNET_FLAG_CTRL_RX, - ("CTRL_RX feature not negotiated")); + sglist_init(&sg, 3, segs); + error = 0; + error |= sglist_append(&sg, &hdr, sizeof(struct virtio_net_ctrl_hdr)); + error |= sglist_append(&sg, hwaddr, ETHER_ADDR_LEN); + error |= sglist_append(&sg, &ack, sizeof(uint8_t)); + KASSERT(error == 0 && sg.sg_nseg == 3, + ("%s: error %d adding set MAC msg to sglist", __func__, error)); - if (vtnet_set_promisc(sc, ifp->if_flags & IFF_PROMISC) != 0) - device_printf(dev, "cannot %s promiscuous mode\n", - ifp->if_flags & IFF_PROMISC ? "enable" : "disable"); + vtnet_exec_ctrl_cmd(sc, &ack, &sg, sg.sg_nseg - 1, 1); - if (vtnet_set_allmulti(sc, ifp->if_flags & IFF_ALLMULTI) != 0) - device_printf(dev, "cannot %s all-multicast mode\n", - ifp->if_flags & IFF_ALLMULTI ? "enable" : "disable"); + return (ack == VIRTIO_NET_OK ? 0 : EIO); +} + +static int +vtnet_ctrl_mq_cmd(struct vtnet_softc *sc, uint16_t npairs) +{ + struct sglist_seg segs[3]; + struct sglist sg; + struct { + struct virtio_net_ctrl_hdr hdr; + uint8_t pad1; + struct virtio_net_ctrl_mq mq; + uint8_t pad2; + uint8_t ack; + } s; + int error; + + s.hdr.class = VIRTIO_NET_CTRL_MQ; + s.hdr.cmd = VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET; + s.mq.virtqueue_pairs = npairs; + s.ack = VIRTIO_NET_ERR; + + sglist_init(&sg, 3, segs); + error = 0; + error |= sglist_append(&sg, &s.hdr, sizeof(struct virtio_net_ctrl_hdr)); + error |= sglist_append(&sg, &s.mq, sizeof(struct virtio_net_ctrl_mq)); + error |= sglist_append(&sg, &s.ack, sizeof(uint8_t)); + KASSERT(error == 0 && sg.sg_nseg == 3, + ("%s: error %d adding MQ message to sglist", __func__, error)); + + vtnet_exec_ctrl_cmd(sc, &s.ack, &sg, sg.sg_nseg - 1, 1); + + return (s.ack == VIRTIO_NET_OK ? 0 : EIO); } static int vtnet_ctrl_rx_cmd(struct vtnet_softc *sc, int cmd, int on) { - struct virtio_net_ctrl_hdr hdr; struct sglist_seg segs[3]; struct sglist sg; - uint8_t onoff, ack; + struct { + struct virtio_net_ctrl_hdr hdr; + uint8_t pad1; + uint8_t onoff; + uint8_t pad2; + uint8_t ack; + } s; int error; - if ((sc->vtnet_flags & VTNET_FLAG_CTRL_RX) == 0) - return (ENOTSUP); + KASSERT(sc->vtnet_flags & VTNET_FLAG_CTRL_RX, + ("%s: CTRL_RX feature not negotiated", __func__)); - error = 0; - - hdr.class = VIRTIO_NET_CTRL_RX; - hdr.cmd = cmd; - onoff = !!on; - ack = VIRTIO_NET_ERR; + s.hdr.class = VIRTIO_NET_CTRL_RX; + s.hdr.cmd = cmd; + s.onoff = !!on; + s.ack = VIRTIO_NET_ERR; sglist_init(&sg, 3, segs); - error |= sglist_append(&sg, &hdr, sizeof(struct virtio_net_ctrl_hdr)); - error |= sglist_append(&sg, &onoff, sizeof(uint8_t)); - error |= sglist_append(&sg, &ack, sizeof(uint8_t)); + error = 0; + error |= sglist_append(&sg, &s.hdr, sizeof(struct virtio_net_ctrl_hdr)); + error |= sglist_append(&sg, &s.onoff, sizeof(uint8_t)); + error |= sglist_append(&sg, &s.ack, sizeof(uint8_t)); KASSERT(error == 0 && sg.sg_nseg == 3, - ("error adding Rx filter message to sglist")); + ("%s: error %d adding Rx message to sglist", __func__, error)); - vtnet_exec_ctrl_cmd(sc, &ack, &sg, sg.sg_nseg - 1, 1); + vtnet_exec_ctrl_cmd(sc, &s.ack, &sg, sg.sg_nseg - 1, 1); - return (ack == VIRTIO_NET_OK ? 0 : EIO); + return (s.ack == VIRTIO_NET_OK ? 0 : EIO); } static int @@ -2321,6 +3145,48 @@ vtnet_set_allmulti(struct vtnet_softc *sc, int on) return (vtnet_ctrl_rx_cmd(sc, VIRTIO_NET_CTRL_RX_ALLMULTI, on)); } +/* + * The device defaults to promiscuous mode for backwards compatibility. + * Turn it off at attach time if possible. + */ +static void +vtnet_attach_disable_promisc(struct vtnet_softc *sc) +{ + struct ifnet *ifp; + + ifp = sc->vtnet_ifp; + + VTNET_CORE_LOCK(sc); + if ((sc->vtnet_flags & VTNET_FLAG_CTRL_RX) == 0) { + ifp->if_flags |= IFF_PROMISC; + } else if (vtnet_set_promisc(sc, 0) != 0) { + ifp->if_flags |= IFF_PROMISC; + device_printf(sc->vtnet_dev, + "cannot disable default promiscuous mode\n"); + } + VTNET_CORE_UNLOCK(sc); +} + +static void +vtnet_rx_filter(struct vtnet_softc *sc) +{ + device_t dev; + struct ifnet *ifp; + + dev = sc->vtnet_dev; + ifp = sc->vtnet_ifp; + + VTNET_CORE_LOCK_ASSERT(sc); + + if (vtnet_set_promisc(sc, ifp->if_flags & IFF_PROMISC) != 0) + device_printf(dev, "cannot %s promiscuous mode\n", + ifp->if_flags & IFF_PROMISC ? "enable" : "disable"); + + if (vtnet_set_allmulti(sc, ifp->if_flags & IFF_ALLMULTI) != 0) + device_printf(dev, "cannot %s all-multicast mode\n", + ifp->if_flags & IFF_ALLMULTI ? "enable" : "disable"); +} + static void vtnet_rx_filter_mac(struct vtnet_softc *sc) { @@ -2340,19 +3206,23 @@ vtnet_rx_filter_mac(struct vtnet_softc *sc) mcnt = 0; promisc = 0; allmulti = 0; - error = 0; - VTNET_LOCK_ASSERT(sc); + VTNET_CORE_LOCK_ASSERT(sc); KASSERT(sc->vtnet_flags & VTNET_FLAG_CTRL_RX, - ("CTRL_RX feature not negotiated")); + ("%s: CTRL_RX feature not negotiated", __func__)); /* Unicast MAC addresses: */ if_addr_rlock(ifp); TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { if (ifa->ifa_addr->sa_family != AF_LINK) continue; - else if (ucnt == VTNET_MAX_MAC_ENTRIES) + else if (memcmp(LLADDR((struct sockaddr_dl *)ifa->ifa_addr), + sc->vtnet_hwaddr, ETHER_ADDR_LEN) == 0) + continue; + else if (ucnt == VTNET_MAX_MAC_ENTRIES) { + promisc = 1; break; + } bcopy(LLADDR((struct sockaddr_dl *)ifa->ifa_addr), &filter->vmf_unicast.macs[ucnt], ETHER_ADDR_LEN); @@ -2360,10 +3230,8 @@ vtnet_rx_filter_mac(struct vtnet_softc *sc) } if_addr_runlock(ifp); - if (ucnt >= VTNET_MAX_MAC_ENTRIES) { - promisc = 1; + if (promisc != 0) { filter->vmf_unicast.nentries = 0; - if_printf(ifp, "more than %d MAC addresses assigned, " "falling back to promiscuous mode\n", VTNET_MAX_MAC_ENTRIES); @@ -2375,8 +3243,10 @@ vtnet_rx_filter_mac(struct vtnet_softc *sc) TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { if (ifma->ifma_addr->sa_family != AF_LINK) continue; - else if (mcnt == VTNET_MAX_MAC_ENTRIES) + else if (mcnt == VTNET_MAX_MAC_ENTRIES) { + allmulti = 1; break; + } bcopy(LLADDR((struct sockaddr_dl *)ifma->ifma_addr), &filter->vmf_multicast.macs[mcnt], ETHER_ADDR_LEN); @@ -2384,17 +3254,15 @@ vtnet_rx_filter_mac(struct vtnet_softc *sc) } if_maddr_runlock(ifp); - if (mcnt >= VTNET_MAX_MAC_ENTRIES) { - allmulti = 1; + if (allmulti != 0) { filter->vmf_multicast.nentries = 0; - if_printf(ifp, "more than %d multicast MAC addresses " "assigned, falling back to all-multicast mode\n", VTNET_MAX_MAC_ENTRIES); } else filter->vmf_multicast.nentries = mcnt; - if (promisc && allmulti) + if (promisc != 0 && allmulti != 0) goto out; hdr.class = VIRTIO_NET_CTRL_MAC; @@ -2402,6 +3270,7 @@ vtnet_rx_filter_mac(struct vtnet_softc *sc) ack = VIRTIO_NET_ERR; sglist_init(&sg, 4, segs); + error = 0; error |= sglist_append(&sg, &hdr, sizeof(struct virtio_net_ctrl_hdr)); error |= sglist_append(&sg, &filter->vmf_unicast, sizeof(uint32_t) + filter->vmf_unicast.nentries * ETHER_ADDR_LEN); @@ -2409,7 +3278,7 @@ vtnet_rx_filter_mac(struct vtnet_softc *sc) sizeof(uint32_t) + filter->vmf_multicast.nentries * ETHER_ADDR_LEN); error |= sglist_append(&sg, &ack, sizeof(uint8_t)); KASSERT(error == 0 && sg.sg_nseg == 4, - ("error adding MAC filtering message to sglist")); + ("%s: error %d adding MAC filter msg to sglist", __func__, error)); vtnet_exec_ctrl_cmd(sc, &ack, &sg, sg.sg_nseg - 1, 1); @@ -2417,111 +3286,99 @@ vtnet_rx_filter_mac(struct vtnet_softc *sc) if_printf(ifp, "error setting host MAC filter table\n"); out: - if (promisc) - if (vtnet_set_promisc(sc, 1) != 0) - if_printf(ifp, "cannot enable promiscuous mode\n"); - if (allmulti) - if (vtnet_set_allmulti(sc, 1) != 0) - if_printf(ifp, "cannot enable all-multicast mode\n"); + if (promisc != 0 && vtnet_set_promisc(sc, 1) != 0) + if_printf(ifp, "cannot enable promiscuous mode\n"); + if (allmulti != 0 && vtnet_set_allmulti(sc, 1) != 0) + if_printf(ifp, "cannot enable all-multicast mode\n"); } static int vtnet_exec_vlan_filter(struct vtnet_softc *sc, int add, uint16_t tag) { - struct virtio_net_ctrl_hdr hdr; struct sglist_seg segs[3]; struct sglist sg; - uint8_t ack; + struct { + struct virtio_net_ctrl_hdr hdr; + uint8_t pad1; + uint16_t tag; + uint8_t pad2; + uint8_t ack; + } s; int error; - hdr.class = VIRTIO_NET_CTRL_VLAN; - hdr.cmd = add ? VIRTIO_NET_CTRL_VLAN_ADD : VIRTIO_NET_CTRL_VLAN_DEL; - ack = VIRTIO_NET_ERR; - error = 0; + s.hdr.class = VIRTIO_NET_CTRL_VLAN; + s.hdr.cmd = add ? VIRTIO_NET_CTRL_VLAN_ADD : VIRTIO_NET_CTRL_VLAN_DEL; + s.tag = tag; + s.ack = VIRTIO_NET_ERR; sglist_init(&sg, 3, segs); - error |= sglist_append(&sg, &hdr, sizeof(struct virtio_net_ctrl_hdr)); - error |= sglist_append(&sg, &tag, sizeof(uint16_t)); - error |= sglist_append(&sg, &ack, sizeof(uint8_t)); + error = 0; + error |= sglist_append(&sg, &s.hdr, sizeof(struct virtio_net_ctrl_hdr)); + error |= sglist_append(&sg, &s.tag, sizeof(uint16_t)); + error |= sglist_append(&sg, &s.ack, sizeof(uint8_t)); KASSERT(error == 0 && sg.sg_nseg == 3, - ("error adding VLAN control message to sglist")); + ("%s: error %d adding VLAN message to sglist", __func__, error)); - vtnet_exec_ctrl_cmd(sc, &ack, &sg, sg.sg_nseg - 1, 1); + vtnet_exec_ctrl_cmd(sc, &s.ack, &sg, sg.sg_nseg - 1, 1); - return (ack == VIRTIO_NET_OK ? 0 : EIO); + return (s.ack == VIRTIO_NET_OK ? 0 : EIO); } static void vtnet_rx_filter_vlan(struct vtnet_softc *sc) { - device_t dev; - uint32_t w, mask; + uint32_t w; uint16_t tag; - int i, nvlans, error; + int i, bit; - VTNET_LOCK_ASSERT(sc); + VTNET_CORE_LOCK_ASSERT(sc); KASSERT(sc->vtnet_flags & VTNET_FLAG_VLAN_FILTER, - ("VLAN_FILTER feature not negotiated")); + ("%s: VLAN_FILTER feature not negotiated", __func__)); - dev = sc->vtnet_dev; - nvlans = sc->vtnet_nvlans; - error = 0; + /* Enable the filter for each configured VLAN. */ + for (i = 0; i < VTNET_VLAN_FILTER_NWORDS; i++) { + w = sc->vtnet_vlan_filter[i]; - /* Enable filtering for each configured VLAN. */ - for (i = 0; i < VTNET_VLAN_SHADOW_SIZE && nvlans > 0; i++) { - w = sc->vtnet_vlan_shadow[i]; - for (mask = 1, tag = i * 32; w != 0; mask <<= 1, tag++) { - if ((w & mask) != 0) { - w &= ~mask; - nvlans--; - if (vtnet_exec_vlan_filter(sc, 1, tag) != 0) - error++; + while ((bit = ffs(w) - 1) != -1) { + w &= ~(1 << bit); + tag = sizeof(w) * CHAR_BIT * i + bit; + + if (vtnet_exec_vlan_filter(sc, 1, tag) != 0) { + device_printf(sc->vtnet_dev, + "cannot enable VLAN %d filter\n", tag); } } } - - KASSERT(nvlans == 0, ("VLAN count incorrect")); - if (error) - device_printf(dev, "cannot restore VLAN filter table\n"); } static void -vtnet_set_vlan_filter(struct vtnet_softc *sc, int add, uint16_t tag) +vtnet_update_vlan_filter(struct vtnet_softc *sc, int add, uint16_t tag) { struct ifnet *ifp; int idx, bit; - KASSERT(sc->vtnet_flags & VTNET_FLAG_VLAN_FILTER, - ("VLAN_FILTER feature not negotiated")); - - if ((tag == 0) || (tag > 4095)) - return; - ifp = sc->vtnet_ifp; idx = (tag >> 5) & 0x7F; bit = tag & 0x1F; - VTNET_LOCK(sc); + if (tag == 0 || tag > 4095) + return; - /* Update shadow VLAN table. */ - if (add) { - sc->vtnet_nvlans++; - sc->vtnet_vlan_shadow[idx] |= (1 << bit); - } else { - sc->vtnet_nvlans--; - sc->vtnet_vlan_shadow[idx] &= ~(1 << bit); + VTNET_CORE_LOCK(sc); + + if (add) + sc->vtnet_vlan_filter[idx] |= (1 << bit); + else + sc->vtnet_vlan_filter[idx] &= ~(1 << bit); + + if (ifp->if_capenable & IFCAP_VLAN_HWFILTER && + vtnet_exec_vlan_filter(sc, add, tag) != 0) { + device_printf(sc->vtnet_dev, + "cannot %s VLAN %d %s the host filter table\n", + add ? "add" : "remove", tag, add ? "to" : "from"); } - if (ifp->if_capenable & IFCAP_VLAN_HWFILTER) { - if (vtnet_exec_vlan_filter(sc, add, tag) != 0) { - device_printf(sc->vtnet_dev, - "cannot %s VLAN %d %s the host filter table\n", - add ? "add" : "remove", tag, - add ? "to" : "from"); - } - } - - VTNET_UNLOCK(sc); + VTNET_CORE_UNLOCK(sc); } static void @@ -2531,7 +3388,7 @@ vtnet_register_vlan(void *arg, struct ifnet *ifp, uint16_t tag) if (ifp->if_softc != arg) return; - vtnet_set_vlan_filter(arg, 1, tag); + vtnet_update_vlan_filter(arg, 1, tag); } static void @@ -2541,7 +3398,47 @@ vtnet_unregister_vlan(void *arg, struct ifnet *ifp, uint16_t tag) if (ifp->if_softc != arg) return; - vtnet_set_vlan_filter(arg, 0, tag); + vtnet_update_vlan_filter(arg, 0, tag); +} + +static int +vtnet_is_link_up(struct vtnet_softc *sc) +{ + device_t dev; + struct ifnet *ifp; + uint16_t status; + + dev = sc->vtnet_dev; + ifp = sc->vtnet_ifp; + + if ((ifp->if_capabilities & IFCAP_LINKSTATE) == 0) + status = VIRTIO_NET_S_LINK_UP; + else + status = virtio_read_dev_config_2(dev, + offsetof(struct virtio_net_config, status)); + + return ((status & VIRTIO_NET_S_LINK_UP) != 0); +} + +static void +vtnet_update_link_status(struct vtnet_softc *sc) +{ + struct ifnet *ifp; + int link; + + ifp = sc->vtnet_ifp; + + VTNET_CORE_LOCK_ASSERT(sc); + link = vtnet_is_link_up(sc); + + /* Notify if the link status has changed. */ + if (link != 0 && sc->vtnet_link_active == 0) { + sc->vtnet_link_active = 1; + if_link_state_change(ifp, LINK_STATE_UP); + } else if (link == 0 && sc->vtnet_link_active != 0) { + sc->vtnet_link_active = 0; + if_link_state_change(ifp, LINK_STATE_DOWN); + } } static int @@ -2569,112 +3466,334 @@ vtnet_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr) ifmr->ifm_status = IFM_AVALID; ifmr->ifm_active = IFM_ETHER; - VTNET_LOCK(sc); + VTNET_CORE_LOCK(sc); if (vtnet_is_link_up(sc) != 0) { ifmr->ifm_status |= IFM_ACTIVE; ifmr->ifm_active |= VTNET_MEDIATYPE; } else ifmr->ifm_active |= IFM_NONE; - VTNET_UNLOCK(sc); + VTNET_CORE_UNLOCK(sc); } static void -vtnet_add_statistics(struct vtnet_softc *sc) +vtnet_set_hwaddr(struct vtnet_softc *sc) { device_t dev; - struct vtnet_statistics *stats; - struct sysctl_ctx_list *ctx; - struct sysctl_oid *tree; - struct sysctl_oid_list *child; dev = sc->vtnet_dev; - stats = &sc->vtnet_stats; + + if (sc->vtnet_flags & VTNET_FLAG_CTRL_MAC) { + if (vtnet_ctrl_mac_cmd(sc, sc->vtnet_hwaddr) != 0) + device_printf(dev, "unable to set MAC address\n"); + } else if (sc->vtnet_flags & VTNET_FLAG_MAC) { + virtio_write_device_config(dev, + offsetof(struct virtio_net_config, mac), + sc->vtnet_hwaddr, ETHER_ADDR_LEN); + } +} + +static void +vtnet_get_hwaddr(struct vtnet_softc *sc) +{ + device_t dev; + + dev = sc->vtnet_dev; + + if ((sc->vtnet_flags & VTNET_FLAG_MAC) == 0) { + /* + * Generate a random locally administered unicast address. + * + * It would be nice to generate the same MAC address across + * reboots, but it seems all the hosts currently available + * support the MAC feature, so this isn't too important. + */ + sc->vtnet_hwaddr[0] = 0xB2; + arc4rand(&sc->vtnet_hwaddr[1], ETHER_ADDR_LEN - 1, 0); + vtnet_set_hwaddr(sc); + return; + } + + virtio_read_device_config(dev, offsetof(struct virtio_net_config, mac), + sc->vtnet_hwaddr, ETHER_ADDR_LEN); +} + +static void +vtnet_vlan_tag_remove(struct mbuf *m) +{ + struct ether_vlan_header *evh; + + evh = mtod(m, struct ether_vlan_header *); + m->m_pkthdr.ether_vtag = ntohs(evh->evl_tag); + m->m_flags |= M_VLANTAG; + + /* Strip the 802.1Q header. */ + bcopy((char *) evh, (char *) evh + ETHER_VLAN_ENCAP_LEN, + ETHER_HDR_LEN - ETHER_TYPE_LEN); + m_adj(m, ETHER_VLAN_ENCAP_LEN); +} + +static void +vtnet_setup_rxq_sysctl(struct sysctl_ctx_list *ctx, + struct sysctl_oid_list *child, struct vtnet_rxq *rxq) +{ + struct sysctl_oid *node; + struct sysctl_oid_list *list; + struct vtnet_rxq_stats *stats; + char namebuf[16]; + + snprintf(namebuf, sizeof(namebuf), "rxq%d", rxq->vtnrx_id); + node = SYSCTL_ADD_NODE(ctx, child, OID_AUTO, namebuf, + CTLFLAG_RD, NULL, "Receive Queue"); + list = SYSCTL_CHILDREN(node); + + stats = &rxq->vtnrx_stats; + + SYSCTL_ADD_UQUAD(ctx, list, OID_AUTO, "ipackets", CTLFLAG_RD, + &stats->vrxs_ipackets, "Receive packets"); + SYSCTL_ADD_UQUAD(ctx, list, OID_AUTO, "ibytes", CTLFLAG_RD, + &stats->vrxs_ibytes, "Receive bytes"); + SYSCTL_ADD_UQUAD(ctx, list, OID_AUTO, "iqdrops", CTLFLAG_RD, + &stats->vrxs_iqdrops, "Receive drops"); + SYSCTL_ADD_UQUAD(ctx, list, OID_AUTO, "ierrors", CTLFLAG_RD, + &stats->vrxs_ierrors, "Receive errors"); + SYSCTL_ADD_UQUAD(ctx, list, OID_AUTO, "csum", CTLFLAG_RD, + &stats->vrxs_csum, "Receive checksum offloaded"); + SYSCTL_ADD_UQUAD(ctx, list, OID_AUTO, "csum_failed", CTLFLAG_RD, + &stats->vrxs_csum_failed, "Receive checksum offload failed"); + SYSCTL_ADD_UQUAD(ctx, list, OID_AUTO, "rescheduled", CTLFLAG_RD, + &stats->vrxs_rescheduled, + "Receive interrupt handler rescheduled"); +} + +static void +vtnet_setup_txq_sysctl(struct sysctl_ctx_list *ctx, + struct sysctl_oid_list *child, struct vtnet_txq *txq) +{ + struct sysctl_oid *node; + struct sysctl_oid_list *list; + struct vtnet_txq_stats *stats; + char namebuf[16]; + + snprintf(namebuf, sizeof(namebuf), "txq%d", txq->vtntx_id); + node = SYSCTL_ADD_NODE(ctx, child, OID_AUTO, namebuf, + CTLFLAG_RD, NULL, "Transmit Queue"); + list = SYSCTL_CHILDREN(node); + + stats = &txq->vtntx_stats; + + SYSCTL_ADD_UQUAD(ctx, list, OID_AUTO, "opackets", CTLFLAG_RD, + &stats->vtxs_opackets, "Transmit packets"); + SYSCTL_ADD_UQUAD(ctx, list, OID_AUTO, "obytes", CTLFLAG_RD, + &stats->vtxs_obytes, "Transmit bytes"); + SYSCTL_ADD_UQUAD(ctx, list, OID_AUTO, "omcasts", CTLFLAG_RD, + &stats->vtxs_omcasts, "Transmit multicasts"); + SYSCTL_ADD_UQUAD(ctx, list, OID_AUTO, "csum", CTLFLAG_RD, + &stats->vtxs_csum, "Transmit checksum offloaded"); + SYSCTL_ADD_UQUAD(ctx, list, OID_AUTO, "tso", CTLFLAG_RD, + &stats->vtxs_tso, "Transmit segmentation offloaded"); + SYSCTL_ADD_UQUAD(ctx, list, OID_AUTO, "collapsed", CTLFLAG_RD, + &stats->vtxs_collapsed, "Transmit mbufs collapsed"); + SYSCTL_ADD_UQUAD(ctx, list, OID_AUTO, "rescheduled", CTLFLAG_RD, + &stats->vtxs_rescheduled, + "Transmit interrupt handler rescheduled"); +} + +static void +vtnet_setup_queue_sysctl(struct vtnet_softc *sc) +{ + device_t dev; + struct sysctl_ctx_list *ctx; + struct sysctl_oid *tree; + struct sysctl_oid_list *child; + int i; + + dev = sc->vtnet_dev; ctx = device_get_sysctl_ctx(dev); tree = device_get_sysctl_tree(dev); child = SYSCTL_CHILDREN(tree); - SYSCTL_ADD_ULONG(ctx, child, OID_AUTO, "mbuf_alloc_failed", + for (i = 0; i < sc->vtnet_max_vq_pairs; i++) { + vtnet_setup_rxq_sysctl(ctx, child, &sc->vtnet_rxqs[i]); + vtnet_setup_txq_sysctl(ctx, child, &sc->vtnet_txqs[i]); + } +} + +static void +vtnet_setup_stat_sysctl(struct sysctl_ctx_list *ctx, + struct sysctl_oid_list *child, struct vtnet_softc *sc) +{ + struct vtnet_statistics *stats; + + stats = &sc->vtnet_stats; + + SYSCTL_ADD_UQUAD(ctx, child, OID_AUTO, "mbuf_alloc_failed", CTLFLAG_RD, &stats->mbuf_alloc_failed, "Mbuf cluster allocation failures"); - SYSCTL_ADD_ULONG(ctx, child, OID_AUTO, "rx_frame_too_large", + SYSCTL_ADD_UQUAD(ctx, child, OID_AUTO, "rx_frame_too_large", CTLFLAG_RD, &stats->rx_frame_too_large, "Received frame larger than the mbuf chain"); - SYSCTL_ADD_ULONG(ctx, child, OID_AUTO, "rx_enq_replacement_failed", + SYSCTL_ADD_UQUAD(ctx, child, OID_AUTO, "rx_enq_replacement_failed", CTLFLAG_RD, &stats->rx_enq_replacement_failed, "Enqueuing the replacement receive mbuf failed"); - SYSCTL_ADD_ULONG(ctx, child, OID_AUTO, "rx_mergeable_failed", + SYSCTL_ADD_UQUAD(ctx, child, OID_AUTO, "rx_mergeable_failed", CTLFLAG_RD, &stats->rx_mergeable_failed, "Mergeable buffers receive failures"); - SYSCTL_ADD_ULONG(ctx, child, OID_AUTO, "rx_csum_bad_ethtype", + SYSCTL_ADD_UQUAD(ctx, child, OID_AUTO, "rx_csum_bad_ethtype", CTLFLAG_RD, &stats->rx_csum_bad_ethtype, "Received checksum offloaded buffer with unsupported " "Ethernet type"); - SYSCTL_ADD_ULONG(ctx, child, OID_AUTO, "rx_csum_bad_start", - CTLFLAG_RD, &stats->rx_csum_bad_start, - "Received checksum offloaded buffer with incorrect start offset"); - SYSCTL_ADD_ULONG(ctx, child, OID_AUTO, "rx_csum_bad_ipproto", + SYSCTL_ADD_UQUAD(ctx, child, OID_AUTO, "rx_csum_bad_ipproto", CTLFLAG_RD, &stats->rx_csum_bad_ipproto, "Received checksum offloaded buffer with incorrect IP protocol"); - SYSCTL_ADD_ULONG(ctx, child, OID_AUTO, "rx_csum_bad_offset", + SYSCTL_ADD_UQUAD(ctx, child, OID_AUTO, "rx_csum_bad_offset", CTLFLAG_RD, &stats->rx_csum_bad_offset, "Received checksum offloaded buffer with incorrect offset"); - SYSCTL_ADD_ULONG(ctx, child, OID_AUTO, "rx_csum_failed", + SYSCTL_ADD_UQUAD(ctx, child, OID_AUTO, "rx_csum_bad_proto", + CTLFLAG_RD, &stats->rx_csum_bad_proto, + "Received checksum offloaded buffer with incorrect protocol"); + SYSCTL_ADD_UQUAD(ctx, child, OID_AUTO, "rx_csum_failed", CTLFLAG_RD, &stats->rx_csum_failed, "Received buffer checksum offload failed"); - SYSCTL_ADD_ULONG(ctx, child, OID_AUTO, "rx_csum_offloaded", + SYSCTL_ADD_UQUAD(ctx, child, OID_AUTO, "rx_csum_offloaded", CTLFLAG_RD, &stats->rx_csum_offloaded, "Received buffer checksum offload succeeded"); - SYSCTL_ADD_ULONG(ctx, child, OID_AUTO, "rx_task_rescheduled", + SYSCTL_ADD_UQUAD(ctx, child, OID_AUTO, "rx_task_rescheduled", CTLFLAG_RD, &stats->rx_task_rescheduled, "Times the receive interrupt task rescheduled itself"); - SYSCTL_ADD_ULONG(ctx, child, OID_AUTO, "tx_csum_offloaded", - CTLFLAG_RD, &stats->tx_csum_offloaded, - "Offloaded checksum of transmitted buffer"); - SYSCTL_ADD_ULONG(ctx, child, OID_AUTO, "tx_tso_offloaded", - CTLFLAG_RD, &stats->tx_tso_offloaded, - "Segmentation offload of transmitted buffer"); - SYSCTL_ADD_ULONG(ctx, child, OID_AUTO, "tx_csum_bad_ethtype", + SYSCTL_ADD_UQUAD(ctx, child, OID_AUTO, "tx_csum_bad_ethtype", CTLFLAG_RD, &stats->tx_csum_bad_ethtype, "Aborted transmit of checksum offloaded buffer with unknown " "Ethernet type"); - SYSCTL_ADD_ULONG(ctx, child, OID_AUTO, "tx_tso_bad_ethtype", + SYSCTL_ADD_UQUAD(ctx, child, OID_AUTO, "tx_tso_bad_ethtype", CTLFLAG_RD, &stats->tx_tso_bad_ethtype, "Aborted transmit of TSO buffer with unknown Ethernet type"); - SYSCTL_ADD_ULONG(ctx, child, OID_AUTO, "tx_task_rescheduled", + SYSCTL_ADD_UQUAD(ctx, child, OID_AUTO, "tx_tso_not_tcp", + CTLFLAG_RD, &stats->tx_tso_not_tcp, + "Aborted transmit of TSO buffer with non TCP protocol"); + SYSCTL_ADD_UQUAD(ctx, child, OID_AUTO, "tx_csum_offloaded", + CTLFLAG_RD, &stats->tx_csum_offloaded, + "Offloaded checksum of transmitted buffer"); + SYSCTL_ADD_UQUAD(ctx, child, OID_AUTO, "tx_tso_offloaded", + CTLFLAG_RD, &stats->tx_tso_offloaded, + "Segmentation offload of transmitted buffer"); + SYSCTL_ADD_UQUAD(ctx, child, OID_AUTO, "tx_task_rescheduled", CTLFLAG_RD, &stats->tx_task_rescheduled, "Times the transmit interrupt task rescheduled itself"); } -static int -vtnet_enable_rx_intr(struct vtnet_softc *sc) -{ - - return (virtqueue_enable_intr(sc->vtnet_rx_vq)); -} - static void -vtnet_disable_rx_intr(struct vtnet_softc *sc) +vtnet_setup_sysctl(struct vtnet_softc *sc) { + device_t dev; + struct sysctl_ctx_list *ctx; + struct sysctl_oid *tree; + struct sysctl_oid_list *child; - virtqueue_disable_intr(sc->vtnet_rx_vq); + dev = sc->vtnet_dev; + ctx = device_get_sysctl_ctx(dev); + tree = device_get_sysctl_tree(dev); + child = SYSCTL_CHILDREN(tree); + + SYSCTL_ADD_INT(ctx, child, OID_AUTO, "max_vq_pairs", + CTLFLAG_RD, &sc->vtnet_max_vq_pairs, 0, + "Maximum number of supported virtqueue pairs"); + SYSCTL_ADD_INT(ctx, child, OID_AUTO, "act_vq_pairs", + CTLFLAG_RD, &sc->vtnet_act_vq_pairs, 0, + "Number of active virtqueue pairs"); + + vtnet_setup_stat_sysctl(ctx, child, sc); } static int -vtnet_enable_tx_intr(struct vtnet_softc *sc) +vtnet_rxq_enable_intr(struct vtnet_rxq *rxq) { -#ifdef VTNET_TX_INTR_MODERATION - return (0); -#else - return (virtqueue_enable_intr(sc->vtnet_tx_vq)); -#endif + return (virtqueue_enable_intr(rxq->vtnrx_vq)); } static void -vtnet_disable_tx_intr(struct vtnet_softc *sc) +vtnet_rxq_disable_intr(struct vtnet_rxq *rxq) { - virtqueue_disable_intr(sc->vtnet_tx_vq); + virtqueue_disable_intr(rxq->vtnrx_vq); +} + +static int +vtnet_txq_enable_intr(struct vtnet_txq *txq) +{ + + return (virtqueue_postpone_intr(txq->vtntx_vq, VQ_POSTPONE_LONG)); +} + +static void +vtnet_txq_disable_intr(struct vtnet_txq *txq) +{ + + virtqueue_disable_intr(txq->vtntx_vq); +} + +static void +vtnet_enable_rx_interrupts(struct vtnet_softc *sc) +{ + int i; + + for (i = 0; i < sc->vtnet_act_vq_pairs; i++) + vtnet_rxq_enable_intr(&sc->vtnet_rxqs[i]); +} + +static void +vtnet_enable_tx_interrupts(struct vtnet_softc *sc) +{ + int i; + + for (i = 0; i < sc->vtnet_act_vq_pairs; i++) + vtnet_txq_enable_intr(&sc->vtnet_txqs[i]); +} + +static void +vtnet_enable_interrupts(struct vtnet_softc *sc) +{ + + vtnet_enable_rx_interrupts(sc); + vtnet_enable_tx_interrupts(sc); +} + +static void +vtnet_disable_rx_interrupts(struct vtnet_softc *sc) +{ + int i; + + for (i = 0; i < sc->vtnet_act_vq_pairs; i++) + vtnet_rxq_disable_intr(&sc->vtnet_rxqs[i]); +} + +static void +vtnet_disable_tx_interrupts(struct vtnet_softc *sc) +{ + int i; + + for (i = 0; i < sc->vtnet_act_vq_pairs; i++) + vtnet_txq_disable_intr(&sc->vtnet_txqs[i]); +} + +static void +vtnet_disable_interrupts(struct vtnet_softc *sc) +{ + + vtnet_disable_rx_interrupts(sc); + vtnet_disable_tx_interrupts(sc); +} + +static int +vtnet_tunable_int(struct vtnet_softc *sc, const char *knob, int def) +{ + char path[64]; + + snprintf(path, sizeof(path), + "hw.vtnet.%d.%s", device_get_unit(sc->vtnet_dev), knob); + TUNABLE_INT_FETCH(path, &def); + + return (def); } diff --git a/sys/dev/virtio/network/if_vtnetvar.h b/sys/dev/virtio/network/if_vtnetvar.h index d870436e454..7f04a93fa12 100644 --- a/sys/dev/virtio/network/if_vtnetvar.h +++ b/sys/dev/virtio/network/if_vtnetvar.h @@ -29,84 +29,167 @@ #ifndef _IF_VTNETVAR_H #define _IF_VTNETVAR_H +struct vtnet_softc; + struct vtnet_statistics { - unsigned long mbuf_alloc_failed; + uint64_t mbuf_alloc_failed; - unsigned long rx_frame_too_large; - unsigned long rx_enq_replacement_failed; - unsigned long rx_mergeable_failed; - unsigned long rx_csum_bad_ethtype; - unsigned long rx_csum_bad_start; - unsigned long rx_csum_bad_ipproto; - unsigned long rx_csum_bad_offset; - unsigned long rx_csum_failed; - unsigned long rx_csum_offloaded; - unsigned long rx_task_rescheduled; + uint64_t rx_frame_too_large; + uint64_t rx_enq_replacement_failed; + uint64_t rx_mergeable_failed; + uint64_t rx_csum_bad_ethtype; + uint64_t rx_csum_bad_ipproto; + uint64_t rx_csum_bad_offset; + uint64_t rx_csum_bad_proto; + uint64_t tx_csum_bad_ethtype; + uint64_t tx_tso_bad_ethtype; + uint64_t tx_tso_not_tcp; - unsigned long tx_csum_offloaded; - unsigned long tx_tso_offloaded; - unsigned long tx_csum_bad_ethtype; - unsigned long tx_tso_bad_ethtype; - unsigned long tx_task_rescheduled; + /* + * These are accumulated from each Rx/Tx queue. + */ + uint64_t rx_csum_failed; + uint64_t rx_csum_offloaded; + uint64_t rx_task_rescheduled; + uint64_t tx_csum_offloaded; + uint64_t tx_tso_offloaded; + uint64_t tx_task_rescheduled; }; +struct vtnet_rxq_stats { + uint64_t vrxs_ipackets; /* if_ipackets */ + uint64_t vrxs_ibytes; /* if_ibytes */ + uint64_t vrxs_iqdrops; /* if_iqdrops */ + uint64_t vrxs_ierrors; /* if_ierrors */ + uint64_t vrxs_csum; + uint64_t vrxs_csum_failed; + uint64_t vrxs_rescheduled; +}; + +struct vtnet_rxq { + struct mtx vtnrx_mtx; + struct vtnet_softc *vtnrx_sc; + struct virtqueue *vtnrx_vq; + int vtnrx_id; + int vtnrx_process_limit; + struct vtnet_rxq_stats vtnrx_stats; + struct taskqueue *vtnrx_tq; + struct task vtnrx_intrtask; + char vtnrx_name[16]; +} __aligned(CACHE_LINE_SIZE); + +#define VTNET_RXQ_LOCK(_rxq) mtx_lock(&(_rxq)->vtnrx_mtx) +#define VTNET_RXQ_UNLOCK(_rxq) mtx_unlock(&(_rxq)->vtnrx_mtx) +#define VTNET_RXQ_LOCK_ASSERT(_rxq) \ + mtx_assert(&(_rxq)->vtnrx_mtx, MA_OWNED) +#define VTNET_RXQ_LOCK_ASSERT_NOTOWNED(_rxq) \ + mtx_assert(&(_rxq)->vtnrx_mtx, MA_NOTOWNED) + +struct vtnet_txq_stats { + uint64_t vtxs_opackets; /* if_opackets */ + uint64_t vtxs_obytes; /* if_obytes */ + uint64_t vtxs_omcasts; /* if_omcasts */ + uint64_t vtxs_csum; + uint64_t vtxs_tso; + uint64_t vtxs_collapsed; + uint64_t vtxs_rescheduled; +}; + +struct vtnet_txq { + struct mtx vtntx_mtx; + struct vtnet_softc *vtntx_sc; + struct virtqueue *vtntx_vq; +#ifndef VTNET_LEGACY_TX + struct buf_ring *vtntx_br; +#endif + int vtntx_id; + int vtntx_watchdog; + struct vtnet_txq_stats vtntx_stats; + struct taskqueue *vtntx_tq; + struct task vtntx_intrtask; +#ifndef VTNET_LEGACY_TX + struct task vtntx_defrtask; +#endif + char vtntx_name[16]; +} __aligned(CACHE_LINE_SIZE); + +#define VTNET_TXQ_LOCK(_txq) mtx_lock(&(_txq)->vtntx_mtx) +#define VTNET_TXQ_TRYLOCK(_txq) mtx_trylock(&(_txq)->vtntx_mtx) +#define VTNET_TXQ_UNLOCK(_txq) mtx_unlock(&(_txq)->vtntx_mtx) +#define VTNET_TXQ_LOCK_ASSERT(_txq) \ + mtx_assert(&(_txq)->vtntx_mtx, MA_OWNED) +#define VTNET_TXQ_LOCK_ASSERT_NOTOWNED(_txq) \ + mtx_assert(&(_txq)->vtntx_mtx, MA_NOTOWNED) + struct vtnet_softc { device_t vtnet_dev; struct ifnet *vtnet_ifp; - struct mtx vtnet_mtx; + struct vtnet_rxq *vtnet_rxqs; + struct vtnet_txq *vtnet_txqs; uint32_t vtnet_flags; -#define VTNET_FLAG_LINK 0x0001 -#define VTNET_FLAG_SUSPENDED 0x0002 +#define VTNET_FLAG_SUSPENDED 0x0001 +#define VTNET_FLAG_MAC 0x0002 #define VTNET_FLAG_CTRL_VQ 0x0004 #define VTNET_FLAG_CTRL_RX 0x0008 -#define VTNET_FLAG_VLAN_FILTER 0x0010 -#define VTNET_FLAG_TSO_ECN 0x0020 -#define VTNET_FLAG_MRG_RXBUFS 0x0040 -#define VTNET_FLAG_LRO_NOMRG 0x0080 - - struct virtqueue *vtnet_rx_vq; - struct virtqueue *vtnet_tx_vq; - struct virtqueue *vtnet_ctrl_vq; +#define VTNET_FLAG_CTRL_MAC 0x0010 +#define VTNET_FLAG_VLAN_FILTER 0x0020 +#define VTNET_FLAG_TSO_ECN 0x0040 +#define VTNET_FLAG_MRG_RXBUFS 0x0080 +#define VTNET_FLAG_LRO_NOMRG 0x0100 +#define VTNET_FLAG_MULTIQ 0x0200 +#define VTNET_FLAG_EVENT_IDX 0x0400 + int vtnet_link_active; int vtnet_hdr_size; - int vtnet_tx_size; - int vtnet_rx_size; int vtnet_rx_process_limit; - int vtnet_rx_mbuf_size; - int vtnet_rx_mbuf_count; + int vtnet_rx_nmbufs; + int vtnet_rx_clsize; + int vtnet_rx_new_clsize; int vtnet_if_flags; - int vtnet_watchdog_timer; + int vtnet_act_vq_pairs; + int vtnet_max_vq_pairs; + + struct virtqueue *vtnet_ctrl_vq; + struct vtnet_mac_filter *vtnet_mac_filter; + uint32_t *vtnet_vlan_filter; + uint64_t vtnet_features; - struct vtnet_statistics vtnet_stats; - struct callout vtnet_tick_ch; - + struct ifmedia vtnet_media; eventhandler_tag vtnet_vlan_attach; eventhandler_tag vtnet_vlan_detach; - struct ifmedia vtnet_media; - /* - * Fake media type; the host does not provide us with - * any real media information. - */ -#define VTNET_MEDIATYPE (IFM_ETHER | IFM_1000_T | IFM_FDX) - char vtnet_hwaddr[ETHER_ADDR_LEN]; - - struct vtnet_mac_filter *vtnet_mac_filter; - /* - * During reset, the host's VLAN filtering table is lost. The - * array below is used to restore all the VLANs configured on - * this interface after a reset. - */ -#define VTNET_VLAN_SHADOW_SIZE (4096 / 32) - int vtnet_nvlans; - uint32_t vtnet_vlan_shadow[VTNET_VLAN_SHADOW_SIZE]; - + struct mtx vtnet_mtx; char vtnet_mtx_name[16]; + char vtnet_hwaddr[ETHER_ADDR_LEN]; }; +/* + * Maximum number of queue pairs we will autoconfigure to. + */ +#define VTNET_MAX_QUEUE_PAIRS 8 + +/* + * Additional completed entries can appear in a virtqueue before we can + * reenable interrupts. Number of times to retry before scheduling the + * taskqueue to process the completed entries. + */ +#define VTNET_INTR_DISABLE_RETRIES 4 + +/* + * Fake the media type. The host does not provide us with any real media + * information. + */ +#define VTNET_MEDIATYPE (IFM_ETHER | IFM_10G_T | IFM_FDX) + +/* + * Number of words to allocate for the VLAN shadow table. There is one + * bit for each VLAN. + */ +#define VTNET_VLAN_FILTER_NWORDS (4096 / 32) + /* * When mergeable buffers are not negotiated, the vtnet_rx_header structure * below is placed at the beginning of the mbuf data. Use 4 bytes of pad to @@ -161,8 +244,12 @@ struct vtnet_mac_filter { */ CTASSERT(sizeof(struct vtnet_mac_filter) <= PAGE_SIZE); -#define VTNET_WATCHDOG_TIMEOUT 5 +#define VTNET_TX_TIMEOUT 5 #define VTNET_CSUM_OFFLOAD (CSUM_TCP | CSUM_UDP | CSUM_SCTP) +#define VTNET_CSUM_OFFLOAD_IPV6 (CSUM_TCP_IPV6 | CSUM_UDP_IPV6 | CSUM_SCTP_IPV6) + +#define VTNET_CSUM_ALL_OFFLOAD \ + (VTNET_CSUM_OFFLOAD | VTNET_CSUM_OFFLOAD_IPV6 | CSUM_TSO) /* Features desired/implemented by this driver. */ #define VTNET_FEATURES \ @@ -170,8 +257,10 @@ CTASSERT(sizeof(struct vtnet_mac_filter) <= PAGE_SIZE); VIRTIO_NET_F_STATUS | \ VIRTIO_NET_F_CTRL_VQ | \ VIRTIO_NET_F_CTRL_RX | \ + VIRTIO_NET_F_CTRL_MAC_ADDR | \ VIRTIO_NET_F_CTRL_VLAN | \ VIRTIO_NET_F_CSUM | \ + VIRTIO_NET_F_GSO | \ VIRTIO_NET_F_HOST_TSO4 | \ VIRTIO_NET_F_HOST_TSO6 | \ VIRTIO_NET_F_HOST_ECN | \ @@ -180,8 +269,17 @@ CTASSERT(sizeof(struct vtnet_mac_filter) <= PAGE_SIZE); VIRTIO_NET_F_GUEST_TSO6 | \ VIRTIO_NET_F_GUEST_ECN | \ VIRTIO_NET_F_MRG_RXBUF | \ + VIRTIO_NET_F_MQ | \ + VIRTIO_RING_F_EVENT_IDX | \ VIRTIO_RING_F_INDIRECT_DESC) +/* + * The VIRTIO_NET_F_HOST_TSO[46] features permit us to send the host + * frames larger than 1514 bytes. + */ +#define VTNET_TSO_FEATURES (VIRTIO_NET_F_GSO | VIRTIO_NET_F_HOST_TSO4 | \ + VIRTIO_NET_F_HOST_TSO6 | VIRTIO_NET_F_HOST_ECN) + /* * The VIRTIO_NET_F_GUEST_TSO[46] features permit the host to send us * frames larger than 1514 bytes. We do not yet support software LRO @@ -208,28 +306,35 @@ CTASSERT(sizeof(struct vtnet_mac_filter) <= PAGE_SIZE); CTASSERT(((VTNET_MAX_RX_SEGS - 1) * MCLBYTES) >= VTNET_MAX_RX_SIZE); CTASSERT(((VTNET_MAX_TX_SEGS - 1) * MCLBYTES) >= VTNET_MAX_MTU); +/* + * Number of slots in the Tx bufrings. This value matches most other + * multiqueue drivers. + */ +#define VTNET_DEFAULT_BUFRING_SIZE 4096 + /* * Determine how many mbufs are in each receive buffer. For LRO without * mergeable descriptors, we must allocate an mbuf chain large enough to * hold both the vtnet_rx_header and the maximum receivable data. */ -#define VTNET_NEEDED_RX_MBUFS(_sc) \ +#define VTNET_NEEDED_RX_MBUFS(_sc, _clsize) \ ((_sc)->vtnet_flags & VTNET_FLAG_LRO_NOMRG) == 0 ? 1 : \ howmany(sizeof(struct vtnet_rx_header) + VTNET_MAX_RX_SIZE, \ - (_sc)->vtnet_rx_mbuf_size) + (_clsize)) -#define VTNET_MTX(_sc) &(_sc)->vtnet_mtx -#define VTNET_LOCK(_sc) mtx_lock(VTNET_MTX((_sc))) -#define VTNET_UNLOCK(_sc) mtx_unlock(VTNET_MTX((_sc))) -#define VTNET_LOCK_DESTROY(_sc) mtx_destroy(VTNET_MTX((_sc))) -#define VTNET_LOCK_ASSERT(_sc) mtx_assert(VTNET_MTX((_sc)), MA_OWNED) -#define VTNET_LOCK_ASSERT_NOTOWNED(_sc) \ - mtx_assert(VTNET_MTX((_sc)), MA_NOTOWNED) +#define VTNET_CORE_MTX(_sc) &(_sc)->vtnet_mtx +#define VTNET_CORE_LOCK(_sc) mtx_lock(VTNET_CORE_MTX((_sc))) +#define VTNET_CORE_UNLOCK(_sc) mtx_unlock(VTNET_CORE_MTX((_sc))) +#define VTNET_CORE_LOCK_DESTROY(_sc) mtx_destroy(VTNET_CORE_MTX((_sc))) +#define VTNET_CORE_LOCK_ASSERT(_sc) \ + mtx_assert(VTNET_CORE_MTX((_sc)), MA_OWNED) +#define VTNET_CORE_LOCK_ASSERT_NOTOWNED(_sc) \ + mtx_assert(VTNET_CORE_MTX((_sc)), MA_NOTOWNED) -#define VTNET_LOCK_INIT(_sc) do { \ +#define VTNET_CORE_LOCK_INIT(_sc) do { \ snprintf((_sc)->vtnet_mtx_name, sizeof((_sc)->vtnet_mtx_name), \ "%s", device_get_nameunit((_sc)->vtnet_dev)); \ - mtx_init(VTNET_MTX((_sc)), (_sc)->vtnet_mtx_name, \ + mtx_init(VTNET_CORE_MTX((_sc)), (_sc)->vtnet_mtx_name, \ "VTNET Core Lock", MTX_DEF); \ } while (0) diff --git a/sys/dev/virtio/network/virtio_net.h b/sys/dev/virtio/network/virtio_net.h index 15a73ccbb8e..f4f9febf59a 100644 --- a/sys/dev/virtio/network/virtio_net.h +++ b/sys/dev/virtio/network/virtio_net.h @@ -50,14 +50,22 @@ #define VIRTIO_NET_F_CTRL_RX 0x40000 /* Control channel RX mode support */ #define VIRTIO_NET_F_CTRL_VLAN 0x80000 /* Control channel VLAN filtering */ #define VIRTIO_NET_F_CTRL_RX_EXTRA 0x100000 /* Extra RX mode control support */ +#define VIRTIO_NET_F_GUEST_ANNOUNCE 0x200000 /* Announce device on network */ +#define VIRTIO_NET_F_MQ 0x400000 /* Device supports RFS */ +#define VIRTIO_NET_F_CTRL_MAC_ADDR 0x800000 /* Set MAC address */ #define VIRTIO_NET_S_LINK_UP 1 /* Link is up */ struct virtio_net_config { /* The config defining mac address (if VIRTIO_NET_F_MAC) */ - uint8_t mac[ETHER_ADDR_LEN]; + uint8_t mac[ETHER_ADDR_LEN]; /* See VIRTIO_NET_F_STATUS and VIRTIO_NET_S_* above */ uint16_t status; + /* Maximum number of each of transmit and receive queues; + * see VIRTIO_NET_F_MQ and VIRTIO_NET_CTRL_MQ. + * Legal values are between 1 and 0x8000. + */ + uint16_t max_virtqueue_pairs; } __packed; /* @@ -66,6 +74,7 @@ struct virtio_net_config { */ struct virtio_net_hdr { #define VIRTIO_NET_HDR_F_NEEDS_CSUM 1 /* Use csum_start,csum_offset*/ +#define VIRTIO_NET_HDR_F_DATA_VALID 2 /* Csum is valid */ uint8_t flags; #define VIRTIO_NET_HDR_GSO_NONE 0 /* Not a GSO frame */ #define VIRTIO_NET_HDR_GSO_TCPV4 1 /* GSO frame, IPv4 TCP (TSO) */ @@ -100,8 +109,6 @@ struct virtio_net_ctrl_hdr { uint8_t cmd; } __packed; -typedef uint8_t virtio_net_ctrl_ack; - #define VIRTIO_NET_OK 0 #define VIRTIO_NET_ERR 1 @@ -134,6 +141,10 @@ typedef uint8_t virtio_net_ctrl_ack; * first sg list contains unicast addresses, the second is for multicast. * This functionality is present if the VIRTIO_NET_F_CTRL_RX feature * is available. + * + * The ADDR_SET command requests one out scatterlist, it contains a + * 6 bytes MAC address. This functionality is present if the + * VIRTIO_NET_F_CTRL_MAC_ADDR feature is available. */ struct virtio_net_ctrl_mac { uint32_t entries; @@ -142,6 +153,7 @@ struct virtio_net_ctrl_mac { #define VIRTIO_NET_CTRL_MAC 1 #define VIRTIO_NET_CTRL_MAC_TABLE_SET 0 +#define VIRTIO_NET_CTRL_MAC_ADDR_SET 1 /* * Control VLAN filtering @@ -156,4 +168,35 @@ struct virtio_net_ctrl_mac { #define VIRTIO_NET_CTRL_VLAN_ADD 0 #define VIRTIO_NET_CTRL_VLAN_DEL 1 +/* + * Control link announce acknowledgement + * + * The command VIRTIO_NET_CTRL_ANNOUNCE_ACK is used to indicate that + * driver has recevied the notification; device would clear the + * VIRTIO_NET_S_ANNOUNCE bit in the status field after it receives + * this command. + */ +#define VIRTIO_NET_CTRL_ANNOUNCE 3 +#define VIRTIO_NET_CTRL_ANNOUNCE_ACK 0 + +/* + * Control Receive Flow Steering + * + * The command VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET enables Receive Flow + * Steering, specifying the number of the transmit and receive queues + * that will be used. After the command is consumed and acked by the + * device, the device will not steer new packets on receive virtqueues + * other than specified nor read from transmit virtqueues other than + * specified. Accordingly, driver should not transmit new packets on + * virtqueues other than specified. + */ +struct virtio_net_ctrl_mq { + uint16_t virtqueue_pairs; +} __packed; + +#define VIRTIO_NET_CTRL_MQ 4 +#define VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET 0 +#define VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MIN 1 +#define VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MAX 0x8000 + #endif /* _VIRTIO_NET_H */ diff --git a/sys/dev/virtio/pci/virtio_pci.c b/sys/dev/virtio/pci/virtio_pci.c index b3df3d9c73b..dcd82ec03a3 100644 --- a/sys/dev/virtio/pci/virtio_pci.c +++ b/sys/dev/virtio/pci/virtio_pci.c @@ -757,8 +757,10 @@ vtpci_probe_and_attach_child(struct vtpci_softc *sc) vtpci_release_child_resources(sc); /* Reset status for future attempt. */ vtpci_set_status(dev, VIRTIO_CONFIG_STATUS_ACK); - } else + } else { vtpci_set_status(dev, VIRTIO_CONFIG_STATUS_DRIVER_OK); + VIRTIO_ATTACH_COMPLETED(child); + } } static int diff --git a/sys/dev/virtio/virtio_if.m b/sys/dev/virtio/virtio_if.m index 9a99d371f66..521f4b8973a 100644 --- a/sys/dev/virtio/virtio_if.m +++ b/sys/dev/virtio/virtio_if.m @@ -29,6 +29,18 @@ INTERFACE virtio; +CODE { + static int + virtio_default_attach_completed(device_t dev) + { + return (0); + } +}; + +METHOD int attach_completed { + device_t dev; +} DEFAULT virtio_default_attach_completed; + CODE { static int virtio_default_config_change(device_t dev) diff --git a/sys/dev/virtio/virtqueue.c b/sys/dev/virtio/virtqueue.c index a82426ea1b4..5eda6cd0323 100644 --- a/sys/dev/virtio/virtqueue.c +++ b/sys/dev/virtio/virtqueue.c @@ -127,7 +127,7 @@ static uint16_t vq_ring_enqueue_segments(struct virtqueue *, static int vq_ring_use_indirect(struct virtqueue *, int); static void vq_ring_enqueue_indirect(struct virtqueue *, void *, struct sglist *, int, int); -static int vq_ring_enable_interrupt(struct virtqueue *, uint16_t); +static int vq_ring_enable_interrupt(struct virtqueue *, uint16_t); static int vq_ring_must_notify_host(struct virtqueue *); static void vq_ring_notify_host(struct virtqueue *); static void vq_ring_free_chain(struct virtqueue *, uint16_t); @@ -440,28 +440,38 @@ virtqueue_enable_intr(struct virtqueue *vq) } int -virtqueue_postpone_intr(struct virtqueue *vq) +virtqueue_postpone_intr(struct virtqueue *vq, vq_postpone_t hint) { uint16_t ndesc, avail_idx; - /* - * Request the next interrupt be postponed until at least half - * of the available descriptors have been consumed. - */ avail_idx = vq->vq_ring.avail->idx; - ndesc = (uint16_t)(avail_idx - vq->vq_used_cons_idx) / 2; + ndesc = (uint16_t)(avail_idx - vq->vq_used_cons_idx); + + switch (hint) { + case VQ_POSTPONE_SHORT: + ndesc = ndesc / 4; + break; + case VQ_POSTPONE_LONG: + ndesc = (ndesc * 3) / 4; + break; + case VQ_POSTPONE_EMPTIED: + break; + } return (vq_ring_enable_interrupt(vq, ndesc)); } +/* + * Note this is only considered a hint to the host. + */ void virtqueue_disable_intr(struct virtqueue *vq) { - /* - * Note this is only considered a hint to the host. - */ - if ((vq->vq_flags & VIRTQUEUE_FLAG_EVENT_IDX) == 0) + if (vq->vq_flags & VIRTQUEUE_FLAG_EVENT_IDX) { + vring_used_event(&vq->vq_ring) = vq->vq_used_cons_idx - + vq->vq_nentries - 1; + } else vq->vq_ring.avail->flags |= VRING_AVAIL_F_NO_INTERRUPT; } diff --git a/sys/dev/virtio/virtqueue.h b/sys/dev/virtio/virtqueue.h index 128a10a79e2..0d4ed9468d2 100644 --- a/sys/dev/virtio/virtqueue.h +++ b/sys/dev/virtio/virtqueue.h @@ -41,6 +41,16 @@ struct sglist; /* Device callback for a virtqueue interrupt. */ typedef void virtqueue_intr_t(void *); +/* + * Hint on how long the next interrupt should be postponed. This is + * only used when the EVENT_IDX feature is negotiated. + */ +typedef enum { + VQ_POSTPONE_SHORT, + VQ_POSTPONE_LONG, + VQ_POSTPONE_EMPTIED /* Until all available desc are used. */ +} vq_postpone_t; + #define VIRTQUEUE_MAX_NAME_SZ 32 /* One for each virtqueue the device wishes to allocate. */ @@ -73,7 +83,7 @@ int virtqueue_reinit(struct virtqueue *vq, uint16_t size); int virtqueue_intr_filter(struct virtqueue *vq); void virtqueue_intr(struct virtqueue *vq); int virtqueue_enable_intr(struct virtqueue *vq); -int virtqueue_postpone_intr(struct virtqueue *vq); +int virtqueue_postpone_intr(struct virtqueue *vq, vq_postpone_t hint); void virtqueue_disable_intr(struct virtqueue *vq); /* Get physical address of the virtqueue ring. */ diff --git a/sys/dev/vkbd/vkbd.c b/sys/dev/vkbd/vkbd.c index 81418f2d433..fc5452a9827 100644 --- a/sys/dev/vkbd/vkbd.c +++ b/sys/dev/vkbd/vkbd.c @@ -186,14 +186,10 @@ vkbd_dev_clone(void *arg, struct ucred *cred, char *name, int namelen, return; /* don't recognize the name */ /* find any existing device, or allocate new unit number */ - if (clone_create(&vkbd_dev_clones, &vkbd_dev_cdevsw, &unit, dev, 0)) { - *dev = make_dev(&vkbd_dev_cdevsw, unit, - UID_ROOT, GID_WHEEL, 0600, DEVICE_NAME "%d", unit); - if (*dev != NULL) { - dev_ref(*dev); - (*dev)->si_flags |= SI_CHEAPCLONE; - } - } + if (clone_create(&vkbd_dev_clones, &vkbd_dev_cdevsw, &unit, dev, 0)) + *dev = make_dev_credf(MAKEDEV_REF, &vkbd_dev_cdevsw, unit, + cred, UID_ROOT, GID_WHEEL, 0600, DEVICE_NAME "%d", + unit); } /* Open device */ diff --git a/sys/dev/vmware/vmxnet3/if_vmx.c b/sys/dev/vmware/vmxnet3/if_vmx.c new file mode 100644 index 00000000000..1cab2d557d8 --- /dev/null +++ b/sys/dev/vmware/vmxnet3/if_vmx.c @@ -0,0 +1,3348 @@ +/*- + * Copyright (c) 2013 Tsubai Masanari + * Copyright (c) 2013 Bryan Venteicher + * + * 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. + * + * $OpenBSD: src/sys/dev/pci/if_vmx.c,v 1.11 2013/06/22 00:28:10 uebayasi Exp $ + */ + +/* Driver for VMware vmxnet3 virtual ethernet devices. */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include "if_vmxreg.h" +#include "if_vmxvar.h" + +#include "opt_inet.h" +#include "opt_inet6.h" + +/* Always enable for now - useful for queue hangs. */ +#define VMXNET3_DEBUG_SYSCTL + +#ifdef VMXNET3_FAILPOINTS +#include +static SYSCTL_NODE(DEBUG_FP, OID_AUTO, vmxnet3, CTLFLAG_RW, 0, + "vmxnet3 fail points"); +#define VMXNET3_FP _debug_fail_point_vmxnet3 +#endif + +static int vmxnet3_probe(device_t); +static int vmxnet3_attach(device_t); +static int vmxnet3_detach(device_t); +static int vmxnet3_shutdown(device_t); + +static int vmxnet3_alloc_resources(struct vmxnet3_softc *); +static void vmxnet3_free_resources(struct vmxnet3_softc *); +static int vmxnet3_check_version(struct vmxnet3_softc *); +static void vmxnet3_initial_config(struct vmxnet3_softc *); + +static int vmxnet3_alloc_msix_interrupts(struct vmxnet3_softc *); +static int vmxnet3_alloc_msi_interrupts(struct vmxnet3_softc *); +static int vmxnet3_alloc_legacy_interrupts(struct vmxnet3_softc *); +static int vmxnet3_alloc_interrupt(struct vmxnet3_softc *, int, int, + struct vmxnet3_interrupt *); +static int vmxnet3_alloc_intr_resources(struct vmxnet3_softc *); +static int vmxnet3_setup_msix_interrupts(struct vmxnet3_softc *); +static int vmxnet3_setup_legacy_interrupt(struct vmxnet3_softc *); +static int vmxnet3_setup_interrupts(struct vmxnet3_softc *); +static int vmxnet3_alloc_interrupts(struct vmxnet3_softc *); + +static void vmxnet3_free_interrupt(struct vmxnet3_softc *, + struct vmxnet3_interrupt *); +static void vmxnet3_free_interrupts(struct vmxnet3_softc *); + +static int vmxnet3_init_rxq(struct vmxnet3_softc *, int); +static int vmxnet3_init_txq(struct vmxnet3_softc *, int); +static int vmxnet3_alloc_rxtx_queues(struct vmxnet3_softc *); +static void vmxnet3_destroy_rxq(struct vmxnet3_rxqueue *); +static void vmxnet3_destroy_txq(struct vmxnet3_txqueue *); +static void vmxnet3_free_rxtx_queues(struct vmxnet3_softc *); + +static int vmxnet3_alloc_shared_data(struct vmxnet3_softc *); +static void vmxnet3_free_shared_data(struct vmxnet3_softc *); +static int vmxnet3_alloc_txq_data(struct vmxnet3_softc *); +static void vmxnet3_free_txq_data(struct vmxnet3_softc *); +static int vmxnet3_alloc_rxq_data(struct vmxnet3_softc *); +static void vmxnet3_free_rxq_data(struct vmxnet3_softc *); +static int vmxnet3_alloc_queue_data(struct vmxnet3_softc *); +static void vmxnet3_free_queue_data(struct vmxnet3_softc *); +static int vmxnet3_alloc_mcast_table(struct vmxnet3_softc *); +static void vmxnet3_init_shared_data(struct vmxnet3_softc *); +static void vmxnet3_reinit_interface(struct vmxnet3_softc *); +static void vmxnet3_reinit_shared_data(struct vmxnet3_softc *); +static int vmxnet3_alloc_data(struct vmxnet3_softc *); +static void vmxnet3_free_data(struct vmxnet3_softc *); +static int vmxnet3_setup_interface(struct vmxnet3_softc *); + +static void vmxnet3_evintr(struct vmxnet3_softc *); +static void vmxnet3_txq_eof(struct vmxnet3_txqueue *); +static void vmxnet3_rx_csum(struct vmxnet3_rxcompdesc *, struct mbuf *); +static int vmxnet3_newbuf(struct vmxnet3_softc *, struct vmxnet3_rxring *); +static void vmxnet3_rxq_eof_discard(struct vmxnet3_rxqueue *, + struct vmxnet3_rxring *, int); +static void vmxnet3_rxq_eof(struct vmxnet3_rxqueue *); +static void vmxnet3_legacy_intr(void *); +static void vmxnet3_txq_intr(void *); +static void vmxnet3_rxq_intr(void *); +static void vmxnet3_event_intr(void *); + +static void vmxnet3_txstop(struct vmxnet3_softc *, struct vmxnet3_txqueue *); +static void vmxnet3_rxstop(struct vmxnet3_softc *, struct vmxnet3_rxqueue *); +static void vmxnet3_stop(struct vmxnet3_softc *); + +static void vmxnet3_txinit(struct vmxnet3_softc *, struct vmxnet3_txqueue *); +static int vmxnet3_rxinit(struct vmxnet3_softc *, struct vmxnet3_rxqueue *); +static int vmxnet3_reinit_queues(struct vmxnet3_softc *); +static int vmxnet3_enable_device(struct vmxnet3_softc *); +static void vmxnet3_reinit_rxfilters(struct vmxnet3_softc *); +static int vmxnet3_reinit(struct vmxnet3_softc *); +static void vmxnet3_init_locked(struct vmxnet3_softc *); +static void vmxnet3_init(void *); + +static int vmxnet3_txq_offload_ctx(struct mbuf *, int *, int *, int *); +static int vmxnet3_txq_load_mbuf(struct vmxnet3_txqueue *, struct mbuf **, + bus_dmamap_t, bus_dma_segment_t [], int *); +static void vmxnet3_txq_unload_mbuf(struct vmxnet3_txqueue *, bus_dmamap_t); +static int vmxnet3_txq_encap(struct vmxnet3_txqueue *, struct mbuf **); +static void vmxnet3_start_locked(struct ifnet *); +static void vmxnet3_start(struct ifnet *); + +static void vmxnet3_update_vlan_filter(struct vmxnet3_softc *, int, + uint16_t); +static void vmxnet3_register_vlan(void *, struct ifnet *, uint16_t); +static void vmxnet3_unregister_vlan(void *, struct ifnet *, uint16_t); +static void vmxnet3_set_rxfilter(struct vmxnet3_softc *); +static int vmxnet3_change_mtu(struct vmxnet3_softc *, int); +static int vmxnet3_ioctl(struct ifnet *, u_long, caddr_t); + +static int vmxnet3_watchdog(struct vmxnet3_txqueue *); +static void vmxnet3_tick(void *); +static void vmxnet3_link_status(struct vmxnet3_softc *); +static void vmxnet3_media_status(struct ifnet *, struct ifmediareq *); +static int vmxnet3_media_change(struct ifnet *); +static void vmxnet3_set_lladdr(struct vmxnet3_softc *); +static void vmxnet3_get_lladdr(struct vmxnet3_softc *); + +static void vmxnet3_setup_txq_sysctl(struct vmxnet3_txqueue *, + struct sysctl_ctx_list *, struct sysctl_oid_list *); +static void vmxnet3_setup_rxq_sysctl(struct vmxnet3_rxqueue *, + struct sysctl_ctx_list *, struct sysctl_oid_list *); +static void vmxnet3_setup_queue_sysctl(struct vmxnet3_softc *, + struct sysctl_ctx_list *, struct sysctl_oid_list *); +static void vmxnet3_setup_sysctl(struct vmxnet3_softc *); + +static void vmxnet3_write_bar0(struct vmxnet3_softc *, bus_size_t, + uint32_t); +static uint32_t vmxnet3_read_bar1(struct vmxnet3_softc *, bus_size_t); +static void vmxnet3_write_bar1(struct vmxnet3_softc *, bus_size_t, + uint32_t); +static void vmxnet3_write_cmd(struct vmxnet3_softc *, uint32_t); +static uint32_t vmxnet3_read_cmd(struct vmxnet3_softc *, uint32_t); + +static void vmxnet3_enable_intr(struct vmxnet3_softc *, int); +static void vmxnet3_disable_intr(struct vmxnet3_softc *, int); +static void vmxnet3_enable_all_intrs(struct vmxnet3_softc *); +static void vmxnet3_disable_all_intrs(struct vmxnet3_softc *); + +static int vmxnet3_dma_malloc(struct vmxnet3_softc *, bus_size_t, + bus_size_t, struct vmxnet3_dma_alloc *); +static void vmxnet3_dma_free(struct vmxnet3_softc *, + struct vmxnet3_dma_alloc *); +static int vmxnet3_tunable_int(struct vmxnet3_softc *, + const char *, int); + +typedef enum { + VMXNET3_BARRIER_RD, + VMXNET3_BARRIER_WR, + VMXNET3_BARRIER_RDWR, +} vmxnet3_barrier_t; + +static void vmxnet3_barrier(struct vmxnet3_softc *, vmxnet3_barrier_t); + +/* Tunables. */ +static int vmxnet3_default_txndesc = VMXNET3_DEF_TX_NDESC; +TUNABLE_INT("hw.vmx.txndesc", &vmxnet3_default_txndesc); +static int vmxnet3_default_rxndesc = VMXNET3_DEF_RX_NDESC; +TUNABLE_INT("hw.vmx.rxndesc", &vmxnet3_default_rxndesc); + +static device_method_t vmxnet3_methods[] = { + /* Device interface. */ + DEVMETHOD(device_probe, vmxnet3_probe), + DEVMETHOD(device_attach, vmxnet3_attach), + DEVMETHOD(device_detach, vmxnet3_detach), + DEVMETHOD(device_shutdown, vmxnet3_shutdown), + + DEVMETHOD_END +}; + +static driver_t vmxnet3_driver = { + "vmx", vmxnet3_methods, sizeof(struct vmxnet3_softc) +}; + +static devclass_t vmxnet3_devclass; +DRIVER_MODULE(vmx, pci, vmxnet3_driver, vmxnet3_devclass, 0, 0); + +MODULE_DEPEND(vmx, pci, 1, 1, 1); +MODULE_DEPEND(vmx, ether, 1, 1, 1); + +#define VMXNET3_VMWARE_VENDOR_ID 0x15AD +#define VMXNET3_VMWARE_DEVICE_ID 0x07B0 + +static int +vmxnet3_probe(device_t dev) +{ + + if (pci_get_vendor(dev) == VMXNET3_VMWARE_VENDOR_ID && + pci_get_device(dev) == VMXNET3_VMWARE_DEVICE_ID) { + device_set_desc(dev, "VMware VMXNET3 Ethernet Adapter"); + return (BUS_PROBE_DEFAULT); + } + + return (ENXIO); +} + +static int +vmxnet3_attach(device_t dev) +{ + struct vmxnet3_softc *sc; + int error; + + sc = device_get_softc(dev); + sc->vmx_dev = dev; + + pci_enable_busmaster(dev); + + VMXNET3_CORE_LOCK_INIT(sc, device_get_nameunit(dev)); + callout_init_mtx(&sc->vmx_tick, &sc->vmx_mtx, 0); + + vmxnet3_initial_config(sc); + + error = vmxnet3_alloc_resources(sc); + if (error) + goto fail; + + error = vmxnet3_check_version(sc); + if (error) + goto fail; + + error = vmxnet3_alloc_rxtx_queues(sc); + if (error) + goto fail; + + error = vmxnet3_alloc_interrupts(sc); + if (error) + goto fail; + + error = vmxnet3_alloc_data(sc); + if (error) + goto fail; + + error = vmxnet3_setup_interface(sc); + if (error) + goto fail; + + error = vmxnet3_setup_interrupts(sc); + if (error) { + ether_ifdetach(sc->vmx_ifp); + device_printf(dev, "could not set up interrupt\n"); + goto fail; + } + + vmxnet3_setup_sysctl(sc); + vmxnet3_link_status(sc); + +fail: + if (error) + vmxnet3_detach(dev); + + return (error); +} + +static int +vmxnet3_detach(device_t dev) +{ + struct vmxnet3_softc *sc; + struct ifnet *ifp; + + sc = device_get_softc(dev); + ifp = sc->vmx_ifp; + + if (device_is_attached(dev)) { + ether_ifdetach(ifp); + VMXNET3_CORE_LOCK(sc); + vmxnet3_stop(sc); + VMXNET3_CORE_UNLOCK(sc); + callout_drain(&sc->vmx_tick); + } + + if (sc->vmx_vlan_attach != NULL) { + EVENTHANDLER_DEREGISTER(vlan_config, sc->vmx_vlan_attach); + sc->vmx_vlan_attach = NULL; + } + if (sc->vmx_vlan_detach != NULL) { + EVENTHANDLER_DEREGISTER(vlan_config, sc->vmx_vlan_detach); + sc->vmx_vlan_detach = NULL; + } + + vmxnet3_free_interrupts(sc); + + if (ifp != NULL) { + if_free(ifp); + sc->vmx_ifp = NULL; + } + + ifmedia_removeall(&sc->vmx_media); + + vmxnet3_free_data(sc); + vmxnet3_free_resources(sc); + vmxnet3_free_rxtx_queues(sc); + + VMXNET3_CORE_LOCK_DESTROY(sc); + + return (0); +} + +static int +vmxnet3_shutdown(device_t dev) +{ + + return (0); +} + +static int +vmxnet3_alloc_resources(struct vmxnet3_softc *sc) +{ + device_t dev; + int rid; + + dev = sc->vmx_dev; + + rid = PCIR_BAR(0); + sc->vmx_res0 = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, + RF_ACTIVE); + if (sc->vmx_res0 == NULL) { + device_printf(dev, + "could not map BAR0 memory\n"); + return (ENXIO); + } + + sc->vmx_iot0 = rman_get_bustag(sc->vmx_res0); + sc->vmx_ioh0 = rman_get_bushandle(sc->vmx_res0); + + rid = PCIR_BAR(1); + sc->vmx_res1 = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, + RF_ACTIVE); + if (sc->vmx_res1 == NULL) { + device_printf(dev, + "could not map BAR1 memory\n"); + return (ENXIO); + } + + sc->vmx_iot1 = rman_get_bustag(sc->vmx_res1); + sc->vmx_ioh1 = rman_get_bushandle(sc->vmx_res1); + + if (pci_find_cap(dev, PCIY_MSIX, NULL) == 0) { + rid = PCIR_BAR(2); + sc->vmx_msix_res = bus_alloc_resource_any(dev, + SYS_RES_MEMORY, &rid, RF_ACTIVE); + } + + if (sc->vmx_msix_res == NULL) + sc->vmx_flags |= VMXNET3_FLAG_NO_MSIX; + + return (0); +} + +static void +vmxnet3_free_resources(struct vmxnet3_softc *sc) +{ + device_t dev; + int rid; + + dev = sc->vmx_dev; + + if (sc->vmx_res0 != NULL) { + rid = PCIR_BAR(0); + bus_release_resource(dev, SYS_RES_MEMORY, rid, sc->vmx_res0); + sc->vmx_res0 = NULL; + } + + if (sc->vmx_res1 != NULL) { + rid = PCIR_BAR(1); + bus_release_resource(dev, SYS_RES_MEMORY, rid, sc->vmx_res1); + sc->vmx_res1 = NULL; + } + + if (sc->vmx_msix_res != NULL) { + rid = PCIR_BAR(2); + bus_release_resource(dev, SYS_RES_MEMORY, rid, + sc->vmx_msix_res); + sc->vmx_msix_res = NULL; + } +} + +static int +vmxnet3_check_version(struct vmxnet3_softc *sc) +{ + device_t dev; + uint32_t version; + + dev = sc->vmx_dev; + + version = vmxnet3_read_bar1(sc, VMXNET3_BAR1_VRRS); + if ((version & 0x01) == 0) { + device_printf(dev, "unsupported hardware version %#x\n", + version); + return (ENOTSUP); + } + vmxnet3_write_bar1(sc, VMXNET3_BAR1_VRRS, 1); + + version = vmxnet3_read_bar1(sc, VMXNET3_BAR1_UVRS); + if ((version & 0x01) == 0) { + device_printf(dev, "unsupported UPT version %#x\n", version); + return (ENOTSUP); + } + vmxnet3_write_bar1(sc, VMXNET3_BAR1_UVRS, 1); + + return (0); +} + +static void +vmxnet3_initial_config(struct vmxnet3_softc *sc) +{ + int ndesc; + + /* + * BMV Much of the work is already done, but this driver does + * not support multiqueue yet. + */ + sc->vmx_ntxqueues = VMXNET3_TX_QUEUES; + sc->vmx_nrxqueues = VMXNET3_RX_QUEUES; + + ndesc = vmxnet3_tunable_int(sc, "txd", vmxnet3_default_txndesc); + if (ndesc > VMXNET3_MAX_TX_NDESC || ndesc < VMXNET3_MIN_TX_NDESC) + ndesc = VMXNET3_DEF_TX_NDESC; + if (ndesc & VMXNET3_MASK_TX_NDESC) + ndesc &= ~VMXNET3_MASK_TX_NDESC; + sc->vmx_ntxdescs = ndesc; + + ndesc = vmxnet3_tunable_int(sc, "rxd", vmxnet3_default_rxndesc); + if (ndesc > VMXNET3_MAX_RX_NDESC || ndesc < VMXNET3_MIN_RX_NDESC) + ndesc = VMXNET3_DEF_RX_NDESC; + if (ndesc & VMXNET3_MASK_RX_NDESC) + ndesc &= ~VMXNET3_MASK_RX_NDESC; + sc->vmx_nrxdescs = ndesc; + sc->vmx_max_rxsegs = VMXNET3_MAX_RX_SEGS; +} + +static int +vmxnet3_alloc_msix_interrupts(struct vmxnet3_softc *sc) +{ + device_t dev; + int nmsix, cnt, required; + + dev = sc->vmx_dev; + + if (sc->vmx_flags & VMXNET3_FLAG_NO_MSIX) + return (1); + + /* Allocate an additional vector for the events interrupt. */ + required = sc->vmx_nrxqueues + sc->vmx_ntxqueues + 1; + + nmsix = pci_msix_count(dev); + if (nmsix < required) + return (1); + + cnt = required; + if (pci_alloc_msix(dev, &cnt) == 0 && cnt >= required) { + sc->vmx_nintrs = required; + return (0); + } else + pci_release_msi(dev); + + return (1); +} + +static int +vmxnet3_alloc_msi_interrupts(struct vmxnet3_softc *sc) +{ + device_t dev; + int nmsi, cnt, required; + + dev = sc->vmx_dev; + required = 1; + + nmsi = pci_msi_count(dev); + if (nmsi < required) + return (1); + + cnt = required; + if (pci_alloc_msi(dev, &cnt) == 0 && cnt >= required) { + sc->vmx_nintrs = 1; + return (0); + } else + pci_release_msi(dev); + + return (1); +} + +static int +vmxnet3_alloc_legacy_interrupts(struct vmxnet3_softc *sc) +{ + + sc->vmx_nintrs = 1; + return (0); +} + +static int +vmxnet3_alloc_interrupt(struct vmxnet3_softc *sc, int rid, int flags, + struct vmxnet3_interrupt *intr) +{ + struct resource *irq; + + irq = bus_alloc_resource_any(sc->vmx_dev, SYS_RES_IRQ, &rid, flags); + if (irq == NULL) + return (ENXIO); + + intr->vmxi_irq = irq; + intr->vmxi_rid = rid; + + return (0); +} + +static int +vmxnet3_alloc_intr_resources(struct vmxnet3_softc *sc) +{ + int i, rid, flags, error; + + rid = 0; + flags = RF_ACTIVE; + + if (sc->vmx_intr_type == VMXNET3_IT_LEGACY) + flags |= RF_SHAREABLE; + else + rid = 1; + + for (i = 0; i < sc->vmx_nintrs; i++, rid++) { + error = vmxnet3_alloc_interrupt(sc, rid, flags, + &sc->vmx_intrs[i]); + if (error) + return (error); + } + + return (0); +} + +/* + * NOTE: We only support the simple case of each Rx and Tx queue on its + * own MSIX vector. This is good enough until we support mulitqueue. + */ +static int +vmxnet3_setup_msix_interrupts(struct vmxnet3_softc *sc) +{ + device_t dev; + struct vmxnet3_txqueue *txq; + struct vmxnet3_rxqueue *rxq; + struct vmxnet3_interrupt *intr; + enum intr_type type; + int i, error; + + dev = sc->vmx_dev; + intr = &sc->vmx_intrs[0]; + type = INTR_TYPE_NET | INTR_MPSAFE; + + for (i = 0; i < sc->vmx_ntxqueues; i++, intr++) { + txq = &sc->vmx_txq[i]; + error = bus_setup_intr(dev, intr->vmxi_irq, type, NULL, + vmxnet3_txq_intr, txq, &intr->vmxi_handler); + if (error) + return (error); + txq->vxtxq_intr_idx = intr->vmxi_rid - 1; + } + + for (i = 0; i < sc->vmx_nrxqueues; i++, intr++) { + rxq = &sc->vmx_rxq[i]; + error = bus_setup_intr(dev, intr->vmxi_irq, type, NULL, + vmxnet3_rxq_intr, rxq, &intr->vmxi_handler); + if (error) + return (error); + rxq->vxrxq_intr_idx = intr->vmxi_rid - 1; + } + + error = bus_setup_intr(dev, intr->vmxi_irq, type, NULL, + vmxnet3_event_intr, sc, &intr->vmxi_handler); + if (error) + return (error); + sc->vmx_event_intr_idx = intr->vmxi_rid - 1; + + return (0); +} + +static int +vmxnet3_setup_legacy_interrupt(struct vmxnet3_softc *sc) +{ + struct vmxnet3_interrupt *intr; + int i, error; + + intr = &sc->vmx_intrs[0]; + error = bus_setup_intr(sc->vmx_dev, intr->vmxi_irq, + INTR_TYPE_NET | INTR_MPSAFE, NULL, vmxnet3_legacy_intr, sc, + &intr->vmxi_handler); + + for (i = 0; i < sc->vmx_ntxqueues; i++) + sc->vmx_txq[i].vxtxq_intr_idx = 0; + for (i = 0; i < sc->vmx_nrxqueues; i++) + sc->vmx_rxq[i].vxrxq_intr_idx = 0; + sc->vmx_event_intr_idx = 0; + + return (error); +} + +/* + * XXX BMV Should probably reorganize the attach and just do + * this in vmxnet3_init_shared_data(). + */ +static void +vmxnet3_set_interrupt_idx(struct vmxnet3_softc *sc) +{ + struct vmxnet3_txqueue *txq; + struct vmxnet3_txq_shared *txs; + struct vmxnet3_rxqueue *rxq; + struct vmxnet3_rxq_shared *rxs; + int i; + + sc->vmx_ds->evintr = sc->vmx_event_intr_idx; + + for (i = 0; i < sc->vmx_ntxqueues; i++) { + txq = &sc->vmx_txq[i]; + txs = txq->vxtxq_ts; + txs->intr_idx = txq->vxtxq_intr_idx; + } + + for (i = 0; i < sc->vmx_nrxqueues; i++) { + rxq = &sc->vmx_rxq[i]; + rxs = rxq->vxrxq_rs; + rxs->intr_idx = rxq->vxrxq_intr_idx; + } +} + +static int +vmxnet3_setup_interrupts(struct vmxnet3_softc *sc) +{ + int error; + + error = vmxnet3_alloc_intr_resources(sc); + if (error) + return (error); + + switch (sc->vmx_intr_type) { + case VMXNET3_IT_MSIX: + error = vmxnet3_setup_msix_interrupts(sc); + break; + case VMXNET3_IT_MSI: + case VMXNET3_IT_LEGACY: + error = vmxnet3_setup_legacy_interrupt(sc); + break; + default: + panic("%s: invalid interrupt type %d", __func__, + sc->vmx_intr_type); + } + + if (error == 0) + vmxnet3_set_interrupt_idx(sc); + + return (error); +} + +static int +vmxnet3_alloc_interrupts(struct vmxnet3_softc *sc) +{ + device_t dev; + uint32_t config; + int error; + + dev = sc->vmx_dev; + config = vmxnet3_read_cmd(sc, VMXNET3_CMD_GET_INTRCFG); + + sc->vmx_intr_type = config & 0x03; + sc->vmx_intr_mask_mode = (config >> 2) & 0x03; + + switch (sc->vmx_intr_type) { + case VMXNET3_IT_AUTO: + sc->vmx_intr_type = VMXNET3_IT_MSIX; + /* FALLTHROUGH */ + case VMXNET3_IT_MSIX: + error = vmxnet3_alloc_msix_interrupts(sc); + if (error == 0) + break; + sc->vmx_intr_type = VMXNET3_IT_MSI; + /* FALLTHROUGH */ + case VMXNET3_IT_MSI: + error = vmxnet3_alloc_msi_interrupts(sc); + if (error == 0) + break; + sc->vmx_intr_type = VMXNET3_IT_LEGACY; + /* FALLTHROUGH */ + case VMXNET3_IT_LEGACY: + error = vmxnet3_alloc_legacy_interrupts(sc); + if (error == 0) + break; + /* FALLTHROUGH */ + default: + sc->vmx_intr_type = -1; + device_printf(dev, "cannot allocate any interrupt resources\n"); + return (ENXIO); + } + + return (error); +} + +static void +vmxnet3_free_interrupt(struct vmxnet3_softc *sc, + struct vmxnet3_interrupt *intr) +{ + device_t dev; + + dev = sc->vmx_dev; + + if (intr->vmxi_handler != NULL) { + bus_teardown_intr(dev, intr->vmxi_irq, intr->vmxi_handler); + intr->vmxi_handler = NULL; + } + + if (intr->vmxi_irq != NULL) { + bus_release_resource(dev, SYS_RES_IRQ, intr->vmxi_rid, + intr->vmxi_irq); + intr->vmxi_irq = NULL; + intr->vmxi_rid = -1; + } +} + +static void +vmxnet3_free_interrupts(struct vmxnet3_softc *sc) +{ + int i; + + for (i = 0; i < sc->vmx_nintrs; i++) + vmxnet3_free_interrupt(sc, &sc->vmx_intrs[i]); + + if (sc->vmx_intr_type == VMXNET3_IT_MSI || + sc->vmx_intr_type == VMXNET3_IT_MSIX) + pci_release_msi(sc->vmx_dev); +} + +static int +vmxnet3_init_rxq(struct vmxnet3_softc *sc, int q) +{ + struct vmxnet3_rxqueue *rxq; + struct vmxnet3_rxring *rxr; + int i; + + rxq = &sc->vmx_rxq[q]; + + snprintf(rxq->vxrxq_name, sizeof(rxq->vxrxq_name), "%s-rx%d", + device_get_nameunit(sc->vmx_dev), q); + mtx_init(&rxq->vxrxq_mtx, rxq->vxrxq_name, NULL, MTX_DEF); + + rxq->vxrxq_sc = sc; + rxq->vxrxq_id = q; + + for (i = 0; i < VMXNET3_RXRINGS_PERQ; i++) { + rxr = &rxq->vxrxq_cmd_ring[i]; + rxr->vxrxr_rid = i; + rxr->vxrxr_ndesc = sc->vmx_nrxdescs; + rxr->vxrxr_rxbuf = malloc(rxr->vxrxr_ndesc * + sizeof(struct vmxnet3_rxbuf), M_DEVBUF, M_NOWAIT | M_ZERO); + if (rxr->vxrxr_rxbuf == NULL) + return (ENOMEM); + + rxq->vxrxq_comp_ring.vxcr_ndesc += sc->vmx_nrxdescs; + } + + return (0); +} + +static int +vmxnet3_init_txq(struct vmxnet3_softc *sc, int q) +{ + struct vmxnet3_txqueue *txq; + struct vmxnet3_txring *txr; + + txq = &sc->vmx_txq[q]; + txr = &txq->vxtxq_cmd_ring; + + snprintf(txq->vxtxq_name, sizeof(txq->vxtxq_name), "%s-tx%d", + device_get_nameunit(sc->vmx_dev), q); + mtx_init(&txq->vxtxq_mtx, txq->vxtxq_name, NULL, MTX_DEF); + + txq->vxtxq_sc = sc; + txq->vxtxq_id = q; + + txr->vxtxr_ndesc = sc->vmx_ntxdescs; + txr->vxtxr_txbuf = malloc(txr->vxtxr_ndesc * + sizeof(struct vmxnet3_txbuf), M_DEVBUF, M_NOWAIT | M_ZERO); + if (txr->vxtxr_txbuf == NULL) + return (ENOMEM); + + txq->vxtxq_comp_ring.vxcr_ndesc = sc->vmx_ntxdescs; + + return (0); +} + +static int +vmxnet3_alloc_rxtx_queues(struct vmxnet3_softc *sc) +{ + int i, error; + + sc->vmx_rxq = malloc(sizeof(struct vmxnet3_rxqueue) * + sc->vmx_nrxqueues, M_DEVBUF, M_NOWAIT | M_ZERO); + sc->vmx_txq = malloc(sizeof(struct vmxnet3_txqueue) * + sc->vmx_ntxqueues, M_DEVBUF, M_NOWAIT | M_ZERO); + if (sc->vmx_rxq == NULL || sc->vmx_txq == NULL) + return (ENOMEM); + + for (i = 0; i < sc->vmx_nrxqueues; i++) { + error = vmxnet3_init_rxq(sc, i); + if (error) + return (error); + } + + for (i = 0; i < sc->vmx_ntxqueues; i++) { + error = vmxnet3_init_txq(sc, i); + if (error) + return (error); + } + + return (0); +} + +static void +vmxnet3_destroy_rxq(struct vmxnet3_rxqueue *rxq) +{ + struct vmxnet3_rxring *rxr; + int i; + + rxq->vxrxq_sc = NULL; + rxq->vxrxq_id = -1; + + for (i = 0; i < VMXNET3_RXRINGS_PERQ; i++) { + rxr = &rxq->vxrxq_cmd_ring[i]; + + if (rxr->vxrxr_rxbuf != NULL) { + free(rxr->vxrxr_rxbuf, M_DEVBUF); + rxr->vxrxr_rxbuf = NULL; + } + } + + if (mtx_initialized(&rxq->vxrxq_mtx) != 0) + mtx_destroy(&rxq->vxrxq_mtx); +} + +static void +vmxnet3_destroy_txq(struct vmxnet3_txqueue *txq) +{ + struct vmxnet3_txring *txr; + + txr = &txq->vxtxq_cmd_ring; + + txq->vxtxq_sc = NULL; + txq->vxtxq_id = -1; + + if (txr->vxtxr_txbuf != NULL) { + free(txr->vxtxr_txbuf, M_DEVBUF); + txr->vxtxr_txbuf = NULL; + } + + if (mtx_initialized(&txq->vxtxq_mtx) != 0) + mtx_destroy(&txq->vxtxq_mtx); +} + +static void +vmxnet3_free_rxtx_queues(struct vmxnet3_softc *sc) +{ + int i; + + if (sc->vmx_rxq != NULL) { + for (i = 0; i < sc->vmx_nrxqueues; i++) + vmxnet3_destroy_rxq(&sc->vmx_rxq[i]); + free(sc->vmx_rxq, M_DEVBUF); + sc->vmx_rxq = NULL; + } + + if (sc->vmx_txq != NULL) { + for (i = 0; i < sc->vmx_ntxqueues; i++) + vmxnet3_destroy_txq(&sc->vmx_txq[i]); + free(sc->vmx_txq, M_DEVBUF); + sc->vmx_txq = NULL; + } +} + +static int +vmxnet3_alloc_shared_data(struct vmxnet3_softc *sc) +{ + device_t dev; + uint8_t *kva; + size_t size; + int i, error; + + dev = sc->vmx_dev; + + size = sizeof(struct vmxnet3_driver_shared); + error = vmxnet3_dma_malloc(sc, size, 1, &sc->vmx_ds_dma); + if (error) { + device_printf(dev, "cannot alloc shared memory\n"); + return (error); + } + sc->vmx_ds = (struct vmxnet3_driver_shared *) sc->vmx_ds_dma.dma_vaddr; + + size = sc->vmx_ntxqueues * sizeof(struct vmxnet3_txq_shared) + + sc->vmx_nrxqueues * sizeof(struct vmxnet3_rxq_shared); + error = vmxnet3_dma_malloc(sc, size, 128, &sc->vmx_qs_dma); + if (error) { + device_printf(dev, "cannot alloc queue shared memory\n"); + return (error); + } + sc->vmx_qs = (void *) sc->vmx_qs_dma.dma_vaddr; + kva = sc->vmx_qs; + + for (i = 0; i < sc->vmx_ntxqueues; i++) { + sc->vmx_txq[i].vxtxq_ts = (struct vmxnet3_txq_shared *) kva; + kva += sizeof(struct vmxnet3_txq_shared); + } + for (i = 0; i < sc->vmx_nrxqueues; i++) { + sc->vmx_rxq[i].vxrxq_rs = (struct vmxnet3_rxq_shared *) kva; + kva += sizeof(struct vmxnet3_rxq_shared); + } + + return (0); +} + +static void +vmxnet3_free_shared_data(struct vmxnet3_softc *sc) +{ + + if (sc->vmx_qs != NULL) { + vmxnet3_dma_free(sc, &sc->vmx_qs_dma); + sc->vmx_qs = NULL; + } + + if (sc->vmx_ds != NULL) { + vmxnet3_dma_free(sc, &sc->vmx_ds_dma); + sc->vmx_ds = NULL; + } +} + +static int +vmxnet3_alloc_txq_data(struct vmxnet3_softc *sc) +{ + device_t dev; + struct vmxnet3_txqueue *txq; + struct vmxnet3_txring *txr; + struct vmxnet3_comp_ring *txc; + size_t descsz, compsz; + int i, q, error; + + dev = sc->vmx_dev; + + for (q = 0; q < sc->vmx_ntxqueues; q++) { + txq = &sc->vmx_txq[q]; + txr = &txq->vxtxq_cmd_ring; + txc = &txq->vxtxq_comp_ring; + + descsz = txr->vxtxr_ndesc * sizeof(struct vmxnet3_txdesc); + compsz = txr->vxtxr_ndesc * sizeof(struct vmxnet3_txcompdesc); + + error = bus_dma_tag_create(bus_get_dma_tag(dev), + 1, 0, /* alignment, boundary */ + BUS_SPACE_MAXADDR, /* lowaddr */ + BUS_SPACE_MAXADDR, /* highaddr */ + NULL, NULL, /* filter, filterarg */ + VMXNET3_TSO_MAXSIZE, /* maxsize */ + VMXNET3_TX_MAXSEGS, /* nsegments */ + VMXNET3_TX_MAXSEGSIZE, /* maxsegsize */ + 0, /* flags */ + NULL, NULL, /* lockfunc, lockarg */ + &txr->vxtxr_txtag); + if (error) { + device_printf(dev, + "unable to create Tx buffer tag for queue %d\n", q); + return (error); + } + + error = vmxnet3_dma_malloc(sc, descsz, 512, &txr->vxtxr_dma); + if (error) { + device_printf(dev, "cannot alloc Tx descriptors for " + "queue %d error %d\n", q, error); + return (error); + } + txr->vxtxr_txd = + (struct vmxnet3_txdesc *) txr->vxtxr_dma.dma_vaddr; + + error = vmxnet3_dma_malloc(sc, compsz, 512, &txc->vxcr_dma); + if (error) { + device_printf(dev, "cannot alloc Tx comp descriptors " + "for queue %d error %d\n", q, error); + return (error); + } + txc->vxcr_u.txcd = + (struct vmxnet3_txcompdesc *) txc->vxcr_dma.dma_vaddr; + + for (i = 0; i < txr->vxtxr_ndesc; i++) { + error = bus_dmamap_create(txr->vxtxr_txtag, 0, + &txr->vxtxr_txbuf[i].vtxb_dmamap); + if (error) { + device_printf(dev, "unable to create Tx buf " + "dmamap for queue %d idx %d\n", q, i); + return (error); + } + } + } + + return (0); +} + +static void +vmxnet3_free_txq_data(struct vmxnet3_softc *sc) +{ + device_t dev; + struct vmxnet3_txqueue *txq; + struct vmxnet3_txring *txr; + struct vmxnet3_comp_ring *txc; + struct vmxnet3_txbuf *txb; + int i, q; + + dev = sc->vmx_dev; + + for (q = 0; q < sc->vmx_ntxqueues; q++) { + txq = &sc->vmx_txq[q]; + txr = &txq->vxtxq_cmd_ring; + txc = &txq->vxtxq_comp_ring; + + for (i = 0; i < txr->vxtxr_ndesc; i++) { + txb = &txr->vxtxr_txbuf[i]; + if (txb->vtxb_dmamap != NULL) { + bus_dmamap_destroy(txr->vxtxr_txtag, + txb->vtxb_dmamap); + txb->vtxb_dmamap = NULL; + } + } + + if (txc->vxcr_u.txcd != NULL) { + vmxnet3_dma_free(sc, &txc->vxcr_dma); + txc->vxcr_u.txcd = NULL; + } + + if (txr->vxtxr_txd != NULL) { + vmxnet3_dma_free(sc, &txr->vxtxr_dma); + txr->vxtxr_txd = NULL; + } + + if (txr->vxtxr_txtag != NULL) { + bus_dma_tag_destroy(txr->vxtxr_txtag); + txr->vxtxr_txtag = NULL; + } + } +} + +static int +vmxnet3_alloc_rxq_data(struct vmxnet3_softc *sc) +{ + device_t dev; + struct vmxnet3_rxqueue *rxq; + struct vmxnet3_rxring *rxr; + struct vmxnet3_comp_ring *rxc; + int descsz, compsz; + int i, j, q, error; + + dev = sc->vmx_dev; + + for (q = 0; q < sc->vmx_nrxqueues; q++) { + rxq = &sc->vmx_rxq[q]; + rxc = &rxq->vxrxq_comp_ring; + compsz = 0; + + for (i = 0; i < VMXNET3_RXRINGS_PERQ; i++) { + rxr = &rxq->vxrxq_cmd_ring[i]; + + descsz = rxr->vxrxr_ndesc * + sizeof(struct vmxnet3_rxdesc); + compsz += rxr->vxrxr_ndesc * + sizeof(struct vmxnet3_rxcompdesc); + + error = bus_dma_tag_create(bus_get_dma_tag(dev), + 1, 0, /* alignment, boundary */ + BUS_SPACE_MAXADDR, /* lowaddr */ + BUS_SPACE_MAXADDR, /* highaddr */ + NULL, NULL, /* filter, filterarg */ + MJUMPAGESIZE, /* maxsize */ + 1, /* nsegments */ + MJUMPAGESIZE, /* maxsegsize */ + 0, /* flags */ + NULL, NULL, /* lockfunc, lockarg */ + &rxr->vxrxr_rxtag); + if (error) { + device_printf(dev, + "unable to create Rx buffer tag for " + "queue %d\n", q); + return (error); + } + + error = vmxnet3_dma_malloc(sc, descsz, 512, + &rxr->vxrxr_dma); + if (error) { + device_printf(dev, "cannot allocate Rx " + "descriptors for queue %d/%d error %d\n", + i, q, error); + return (error); + } + rxr->vxrxr_rxd = + (struct vmxnet3_rxdesc *) rxr->vxrxr_dma.dma_vaddr; + } + + error = vmxnet3_dma_malloc(sc, compsz, 512, &rxc->vxcr_dma); + if (error) { + device_printf(dev, "cannot alloc Rx comp descriptors " + "for queue %d error %d\n", q, error); + return (error); + } + rxc->vxcr_u.rxcd = + (struct vmxnet3_rxcompdesc *) rxc->vxcr_dma.dma_vaddr; + + for (i = 0; i < VMXNET3_RXRINGS_PERQ; i++) { + rxr = &rxq->vxrxq_cmd_ring[i]; + + error = bus_dmamap_create(rxr->vxrxr_rxtag, 0, + &rxr->vxrxr_spare_dmap); + if (error) { + device_printf(dev, "unable to create spare " + "dmamap for queue %d/%d error %d\n", + q, i, error); + return (error); + } + + for (j = 0; j < rxr->vxrxr_ndesc; j++) { + error = bus_dmamap_create(rxr->vxrxr_rxtag, 0, + &rxr->vxrxr_rxbuf[j].vrxb_dmamap); + if (error) { + device_printf(dev, "unable to create " + "dmamap for queue %d/%d slot %d " + "error %d\n", + q, i, j, error); + return (error); + } + } + } + } + + return (0); +} + +static void +vmxnet3_free_rxq_data(struct vmxnet3_softc *sc) +{ + device_t dev; + struct vmxnet3_rxqueue *rxq; + struct vmxnet3_rxring *rxr; + struct vmxnet3_comp_ring *rxc; + struct vmxnet3_rxbuf *rxb; + int i, j, q; + + dev = sc->vmx_dev; + + for (q = 0; q < sc->vmx_nrxqueues; q++) { + rxq = &sc->vmx_rxq[q]; + rxc = &rxq->vxrxq_comp_ring; + + for (i = 0; i < VMXNET3_RXRINGS_PERQ; i++) { + rxr = &rxq->vxrxq_cmd_ring[i]; + + if (rxr->vxrxr_spare_dmap != NULL) { + bus_dmamap_destroy(rxr->vxrxr_rxtag, + rxr->vxrxr_spare_dmap); + rxr->vxrxr_spare_dmap = NULL; + } + + for (j = 0; j < rxr->vxrxr_ndesc; j++) { + rxb = &rxr->vxrxr_rxbuf[j]; + if (rxb->vrxb_dmamap != NULL) { + bus_dmamap_destroy(rxr->vxrxr_rxtag, + rxb->vrxb_dmamap); + rxb->vrxb_dmamap = NULL; + } + } + } + + if (rxc->vxcr_u.rxcd != NULL) { + vmxnet3_dma_free(sc, &rxc->vxcr_dma); + rxc->vxcr_u.rxcd = NULL; + } + + for (i = 0; i < VMXNET3_RXRINGS_PERQ; i++) { + rxr = &rxq->vxrxq_cmd_ring[i]; + + if (rxr->vxrxr_rxd != NULL) { + vmxnet3_dma_free(sc, &rxr->vxrxr_dma); + rxr->vxrxr_rxd = NULL; + } + + if (rxr->vxrxr_rxtag != NULL) { + bus_dma_tag_destroy(rxr->vxrxr_rxtag); + rxr->vxrxr_rxtag = NULL; + } + } + } +} + +static int +vmxnet3_alloc_queue_data(struct vmxnet3_softc *sc) +{ + int error; + + error = vmxnet3_alloc_txq_data(sc); + if (error) + return (error); + + error = vmxnet3_alloc_rxq_data(sc); + if (error) + return (error); + + return (0); +} + +static void +vmxnet3_free_queue_data(struct vmxnet3_softc *sc) +{ + + if (sc->vmx_rxq != NULL) + vmxnet3_free_rxq_data(sc); + + if (sc->vmx_txq != NULL) + vmxnet3_free_txq_data(sc); +} + +static int +vmxnet3_alloc_mcast_table(struct vmxnet3_softc *sc) +{ + int error; + + error = vmxnet3_dma_malloc(sc, VMXNET3_MULTICAST_MAX * ETHER_ADDR_LEN, + 32, &sc->vmx_mcast_dma); + if (error) + device_printf(sc->vmx_dev, "unable to alloc multicast table\n"); + else + sc->vmx_mcast = sc->vmx_mcast_dma.dma_vaddr; + + return (error); +} + +static void +vmxnet3_free_mcast_table(struct vmxnet3_softc *sc) +{ + + if (sc->vmx_mcast != NULL) { + vmxnet3_dma_free(sc, &sc->vmx_mcast_dma); + sc->vmx_mcast = NULL; + } +} + +static void +vmxnet3_init_shared_data(struct vmxnet3_softc *sc) +{ + struct vmxnet3_driver_shared *ds; + struct vmxnet3_txqueue *txq; + struct vmxnet3_txq_shared *txs; + struct vmxnet3_rxqueue *rxq; + struct vmxnet3_rxq_shared *rxs; + int i; + + ds = sc->vmx_ds; + + /* + * Initialize fields of the shared data that remains the same across + * reinits. Note the shared data is zero'd when allocated. + */ + + ds->magic = VMXNET3_REV1_MAGIC; + + /* DriverInfo */ + ds->version = VMXNET3_DRIVER_VERSION; + ds->guest = VMXNET3_GOS_FREEBSD | VMXNET3_GUEST_OS_VERSION | +#ifdef __LP64__ + VMXNET3_GOS_64BIT; +#else + VMXNET3_GOS_32BIT; +#endif + ds->vmxnet3_revision = 1; + ds->upt_version = 1; + + /* Misc. conf */ + ds->driver_data = vtophys(sc); + ds->driver_data_len = sizeof(struct vmxnet3_softc); + ds->queue_shared = sc->vmx_qs_dma.dma_paddr; + ds->queue_shared_len = sc->vmx_qs_dma.dma_size; + ds->nrxsg_max = sc->vmx_max_rxsegs; + + /* Interrupt control. */ + ds->automask = sc->vmx_intr_mask_mode == VMXNET3_IMM_AUTO; + ds->nintr = sc->vmx_nintrs; + ds->evintr = sc->vmx_event_intr_idx; + ds->ictrl = VMXNET3_ICTRL_DISABLE_ALL; + + for (i = 0; i < sc->vmx_nintrs; i++) + ds->modlevel[i] = UPT1_IMOD_ADAPTIVE; + + /* Receive filter. */ + ds->mcast_table = sc->vmx_mcast_dma.dma_paddr; + ds->mcast_tablelen = sc->vmx_mcast_dma.dma_size; + + /* Tx queues */ + for (i = 0; i < sc->vmx_ntxqueues; i++) { + txq = &sc->vmx_txq[i]; + txs = txq->vxtxq_ts; + + txs->cmd_ring = txq->vxtxq_cmd_ring.vxtxr_dma.dma_paddr; + txs->cmd_ring_len = txq->vxtxq_cmd_ring.vxtxr_ndesc; + txs->comp_ring = txq->vxtxq_comp_ring.vxcr_dma.dma_paddr; + txs->comp_ring_len = txq->vxtxq_comp_ring.vxcr_ndesc; + txs->driver_data = vtophys(txq); + txs->driver_data_len = sizeof(struct vmxnet3_txqueue); + } + + /* Rx queues */ + for (i = 0; i < sc->vmx_nrxqueues; i++) { + rxq = &sc->vmx_rxq[i]; + rxs = rxq->vxrxq_rs; + + rxs->cmd_ring[0] = rxq->vxrxq_cmd_ring[0].vxrxr_dma.dma_paddr; + rxs->cmd_ring_len[0] = rxq->vxrxq_cmd_ring[0].vxrxr_ndesc; + rxs->cmd_ring[1] = rxq->vxrxq_cmd_ring[1].vxrxr_dma.dma_paddr; + rxs->cmd_ring_len[1] = rxq->vxrxq_cmd_ring[1].vxrxr_ndesc; + rxs->comp_ring = rxq->vxrxq_comp_ring.vxcr_dma.dma_paddr; + rxs->comp_ring_len = rxq->vxrxq_comp_ring.vxcr_ndesc; + rxs->driver_data = vtophys(rxq); + rxs->driver_data_len = sizeof(struct vmxnet3_rxqueue); + } +} + +static void +vmxnet3_reinit_interface(struct vmxnet3_softc *sc) +{ + struct ifnet *ifp; + + ifp = sc->vmx_ifp; + + /* Use the current MAC address. */ + bcopy(IF_LLADDR(sc->vmx_ifp), sc->vmx_lladdr, ETHER_ADDR_LEN); + vmxnet3_set_lladdr(sc); + + ifp->if_hwassist = 0; + if (ifp->if_capenable & IFCAP_TXCSUM) + ifp->if_hwassist |= VMXNET3_CSUM_OFFLOAD; + if (ifp->if_capenable & IFCAP_TXCSUM_IPV6) + ifp->if_hwassist |= VMXNET3_CSUM_OFFLOAD_IPV6; + if (ifp->if_capenable & IFCAP_TSO4) + ifp->if_hwassist |= CSUM_TSO; + if (ifp->if_capenable & IFCAP_TSO6) + ifp->if_hwassist |= CSUM_TSO; /* No CSUM_TSO_IPV6. */ +} + +static void +vmxnet3_reinit_shared_data(struct vmxnet3_softc *sc) +{ + struct ifnet *ifp; + struct vmxnet3_driver_shared *ds; + + ifp = sc->vmx_ifp; + ds = sc->vmx_ds; + + ds->upt_features = 0; + if (ifp->if_capenable & (IFCAP_RXCSUM | IFCAP_RXCSUM_IPV6)) + ds->upt_features |= UPT1_F_CSUM; + if (ifp->if_capenable & IFCAP_VLAN_HWTAGGING) + ds->upt_features |= UPT1_F_VLAN; + if (ifp->if_capenable & IFCAP_LRO) + ds->upt_features |= UPT1_F_LRO; + + ds->mtu = ifp->if_mtu; + ds->ntxqueue = sc->vmx_ntxqueues; + ds->nrxqueue = sc->vmx_nrxqueues; + + vmxnet3_write_bar1(sc, VMXNET3_BAR1_DSL, sc->vmx_ds_dma.dma_paddr); + vmxnet3_write_bar1(sc, VMXNET3_BAR1_DSH, + (uint64_t) sc->vmx_ds_dma.dma_paddr >> 32); +} + +static int +vmxnet3_alloc_data(struct vmxnet3_softc *sc) +{ + int error; + + error = vmxnet3_alloc_shared_data(sc); + if (error) + return (error); + + error = vmxnet3_alloc_queue_data(sc); + if (error) + return (error); + + error = vmxnet3_alloc_mcast_table(sc); + if (error) + return (error); + + vmxnet3_init_shared_data(sc); + + return (0); +} + +static void +vmxnet3_free_data(struct vmxnet3_softc *sc) +{ + + vmxnet3_free_mcast_table(sc); + vmxnet3_free_queue_data(sc); + vmxnet3_free_shared_data(sc); +} + +static int +vmxnet3_setup_interface(struct vmxnet3_softc *sc) +{ + device_t dev; + struct ifnet *ifp; + + dev = sc->vmx_dev; + + ifp = sc->vmx_ifp = if_alloc(IFT_ETHER); + if (ifp == NULL) { + device_printf(dev, "cannot allocate ifnet structure\n"); + return (ENOSPC); + } + + if_initname(ifp, device_get_name(dev), device_get_unit(dev)); +#if __FreeBSD_version < 1000025 + ifp->if_baudrate = 1000000000; +#else + if_initbaudrate(ifp, IF_Gbps(10)); +#endif + ifp->if_softc = sc; + ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; + ifp->if_init = vmxnet3_init; + ifp->if_ioctl = vmxnet3_ioctl; + ifp->if_start = vmxnet3_start; + ifp->if_snd.ifq_drv_maxlen = sc->vmx_ntxdescs - 1; + IFQ_SET_MAXLEN(&ifp->if_snd, sc->vmx_ntxdescs - 1); + IFQ_SET_READY(&ifp->if_snd); + + vmxnet3_get_lladdr(sc); + ether_ifattach(ifp, sc->vmx_lladdr); + + ifp->if_capabilities |= IFCAP_RXCSUM | IFCAP_TXCSUM; + ifp->if_capabilities |= IFCAP_RXCSUM_IPV6 | IFCAP_TXCSUM_IPV6; + ifp->if_capabilities |= IFCAP_TSO4 | IFCAP_TSO6; + ifp->if_capabilities |= IFCAP_VLAN_MTU | IFCAP_VLAN_HWTAGGING | + IFCAP_VLAN_HWCSUM; + ifp->if_capenable = ifp->if_capabilities; + + /* These capabilities are not enabled by default. */ + ifp->if_capabilities |= IFCAP_LRO | IFCAP_VLAN_HWFILTER; + + sc->vmx_vlan_attach = EVENTHANDLER_REGISTER(vlan_config, + vmxnet3_register_vlan, sc, EVENTHANDLER_PRI_FIRST); + sc->vmx_vlan_detach = EVENTHANDLER_REGISTER(vlan_config, + vmxnet3_unregister_vlan, sc, EVENTHANDLER_PRI_FIRST); + + ifmedia_init(&sc->vmx_media, 0, vmxnet3_media_change, + vmxnet3_media_status); + ifmedia_add(&sc->vmx_media, IFM_ETHER | IFM_AUTO, 0, NULL); + ifmedia_set(&sc->vmx_media, IFM_ETHER | IFM_AUTO); + + return (0); +} + +static void +vmxnet3_evintr(struct vmxnet3_softc *sc) +{ + device_t dev; + struct ifnet *ifp; + struct vmxnet3_txq_shared *ts; + struct vmxnet3_rxq_shared *rs; + uint32_t event; + int reset; + + dev = sc->vmx_dev; + ifp = sc->vmx_ifp; + reset = 0; + + VMXNET3_CORE_LOCK(sc); + + /* Clear events. */ + event = sc->vmx_ds->event; + vmxnet3_write_bar1(sc, VMXNET3_BAR1_EVENT, event); + + if (event & VMXNET3_EVENT_LINK) + vmxnet3_link_status(sc); + + if (event & (VMXNET3_EVENT_TQERROR | VMXNET3_EVENT_RQERROR)) { + reset = 1; + vmxnet3_read_cmd(sc, VMXNET3_CMD_GET_STATUS); + ts = sc->vmx_txq[0].vxtxq_ts; + if (ts->stopped != 0) + device_printf(dev, "Tx queue error %#x\n", ts->error); + rs = sc->vmx_rxq[0].vxrxq_rs; + if (rs->stopped != 0) + device_printf(dev, "Rx queue error %#x\n", rs->error); + device_printf(dev, "Rx/Tx queue error event ... resetting\n"); + } + + if (event & VMXNET3_EVENT_DIC) + device_printf(dev, "device implementation change event\n"); + if (event & VMXNET3_EVENT_DEBUG) + device_printf(dev, "debug event\n"); + + if (reset != 0) { + ifp->if_drv_flags &= ~IFF_DRV_RUNNING; + vmxnet3_init_locked(sc); + } + + VMXNET3_CORE_UNLOCK(sc); +} + +static void +vmxnet3_txq_eof(struct vmxnet3_txqueue *txq) +{ + struct vmxnet3_softc *sc; + struct ifnet *ifp; + struct vmxnet3_txring *txr; + struct vmxnet3_comp_ring *txc; + struct vmxnet3_txcompdesc *txcd; + struct vmxnet3_txbuf *txb; + u_int sop; + + sc = txq->vxtxq_sc; + ifp = sc->vmx_ifp; + txr = &txq->vxtxq_cmd_ring; + txc = &txq->vxtxq_comp_ring; + + VMXNET3_TXQ_LOCK_ASSERT(txq); + + for (;;) { + txcd = &txc->vxcr_u.txcd[txc->vxcr_next]; + if (txcd->gen != txc->vxcr_gen) + break; + vmxnet3_barrier(sc, VMXNET3_BARRIER_RD); + + if (++txc->vxcr_next == txc->vxcr_ndesc) { + txc->vxcr_next = 0; + txc->vxcr_gen ^= 1; + } + + sop = txr->vxtxr_next; + txb = &txr->vxtxr_txbuf[sop]; + + if (txb->vtxb_m != NULL) { + bus_dmamap_sync(txr->vxtxr_txtag, txb->vtxb_dmamap, + BUS_DMASYNC_POSTWRITE); + bus_dmamap_unload(txr->vxtxr_txtag, txb->vtxb_dmamap); + + m_freem(txb->vtxb_m); + txb->vtxb_m = NULL; + + ifp->if_opackets++; + } + + txr->vxtxr_next = (txcd->eop_idx + 1) % txr->vxtxr_ndesc; + } + + if (txr->vxtxr_head == txr->vxtxr_next) + txq->vxtxq_watchdog = 0; +} + +static int +vmxnet3_newbuf(struct vmxnet3_softc *sc, struct vmxnet3_rxring *rxr) +{ + struct ifnet *ifp; + struct mbuf *m; + struct vmxnet3_rxdesc *rxd; + struct vmxnet3_rxbuf *rxb; + bus_dma_tag_t tag; + bus_dmamap_t dmap; + bus_dma_segment_t segs[1]; + int idx, clsize, btype, flags, nsegs, error; + + ifp = sc->vmx_ifp; + tag = rxr->vxrxr_rxtag; + dmap = rxr->vxrxr_spare_dmap; + idx = rxr->vxrxr_fill; + rxd = &rxr->vxrxr_rxd[idx]; + rxb = &rxr->vxrxr_rxbuf[idx]; + +#ifdef VMXNET3_FAILPOINTS + KFAIL_POINT_CODE(VMXNET3_FP, newbuf, return ENOBUFS); + if (rxr->vxrxr_rid != 0) + KFAIL_POINT_CODE(VMXNET3_FP, newbuf_body_only, return ENOBUFS); +#endif + + if (rxr->vxrxr_rid == 0 && (idx % sc->vmx_rx_max_chain) == 0) { + flags = M_PKTHDR; + clsize = MCLBYTES; + btype = VMXNET3_BTYPE_HEAD; + } else { +#if __FreeBSD_version < 902001 + /* + * These mbufs will never be used for the start of a frame. + * Roughly prior to branching releng/9.2, the load_mbuf_sg() + * required the mbuf to always be a packet header. Avoid + * unnecessary mbuf initialization in newer versions where + * that is not the case. + */ + flags = M_PKTHDR; +#else + flags = 0; +#endif + clsize = MJUMPAGESIZE; + btype = VMXNET3_BTYPE_BODY; + } + + m = m_getjcl(M_NOWAIT, MT_DATA, flags, clsize); + if (m == NULL) { + sc->vmx_stats.vmst_mgetcl_failed++; + return (ENOBUFS); + } + + if (btype == VMXNET3_BTYPE_HEAD) { + m->m_len = m->m_pkthdr.len = clsize; + m_adj(m, ETHER_ALIGN); + } else + m->m_len = clsize; + + error = bus_dmamap_load_mbuf_sg(tag, dmap, m, &segs[0], &nsegs, + BUS_DMA_NOWAIT); + if (error) { + m_freem(m); + sc->vmx_stats.vmst_mbuf_load_failed++; + return (error); + } + KASSERT(nsegs == 1, + ("%s: mbuf %p with too many segments %d", __func__, m, nsegs)); +#if __FreeBSD_version < 902001 + if (btype == VMXNET3_BTYPE_BODY) + m->m_flags &= ~M_PKTHDR; +#endif + + if (rxb->vrxb_m != NULL) { + bus_dmamap_sync(tag, rxb->vrxb_dmamap, BUS_DMASYNC_POSTREAD); + bus_dmamap_unload(tag, rxb->vrxb_dmamap); + } + + rxr->vxrxr_spare_dmap = rxb->vrxb_dmamap; + rxb->vrxb_dmamap = dmap; + rxb->vrxb_m = m; + + rxd->addr = segs[0].ds_addr; + rxd->len = segs[0].ds_len; + rxd->btype = btype; + rxd->gen = rxr->vxrxr_gen; + + vmxnet3_rxr_increment_fill(rxr); + return (0); +} + +static void +vmxnet3_rxq_eof_discard(struct vmxnet3_rxqueue *rxq, + struct vmxnet3_rxring *rxr, int idx) +{ + struct vmxnet3_rxdesc *rxd; + + rxd = &rxr->vxrxr_rxd[idx]; + rxd->gen = rxr->vxrxr_gen; + vmxnet3_rxr_increment_fill(rxr); +} + +static void +vmxnet3_rxq_discard_chain(struct vmxnet3_rxqueue *rxq) +{ + struct vmxnet3_softc *sc; + struct vmxnet3_rxring *rxr; + struct vmxnet3_comp_ring *rxc; + struct vmxnet3_rxcompdesc *rxcd; + int idx, eof; + + sc = rxq->vxrxq_sc; + rxc = &rxq->vxrxq_comp_ring; + + do { + rxcd = &rxc->vxcr_u.rxcd[rxc->vxcr_next]; + if (rxcd->gen != rxc->vxcr_gen) + break; /* Not expected. */ + vmxnet3_barrier(sc, VMXNET3_BARRIER_RD); + + if (++rxc->vxcr_next == rxc->vxcr_ndesc) { + rxc->vxcr_next = 0; + rxc->vxcr_gen ^= 1; + } + + idx = rxcd->rxd_idx; + eof = rxcd->eop; + if (rxcd->qid < sc->vmx_nrxqueues) + rxr = &rxq->vxrxq_cmd_ring[0]; + else + rxr = &rxq->vxrxq_cmd_ring[1]; + vmxnet3_rxq_eof_discard(rxq, rxr, idx); + } while (!eof); +} + +static void +vmxnet3_rx_csum(struct vmxnet3_rxcompdesc *rxcd, struct mbuf *m) +{ + + if (rxcd->ipv4) { + m->m_pkthdr.csum_flags |= CSUM_IP_CHECKED; + if (rxcd->ipcsum_ok) + m->m_pkthdr.csum_flags |= CSUM_IP_VALID; + } + + if (!rxcd->fragment) { + if (rxcd->csum_ok && (rxcd->tcp || rxcd->udp)) { + m->m_pkthdr.csum_flags |= CSUM_DATA_VALID | + CSUM_PSEUDO_HDR; + m->m_pkthdr.csum_data = 0xFFFF; + } + } +} + +static void +vmxnet3_rxq_input(struct vmxnet3_rxqueue *rxq, + struct vmxnet3_rxcompdesc *rxcd, struct mbuf *m) +{ + struct vmxnet3_softc *sc; + struct ifnet *ifp; + + sc = rxq->vxrxq_sc; + ifp = sc->vmx_ifp; + + if (rxcd->error) { + ifp->if_ierrors++; + m_freem(m); + return; + } + + if (!rxcd->no_csum) + vmxnet3_rx_csum(rxcd, m); + if (rxcd->vlan) { + m->m_flags |= M_VLANTAG; + m->m_pkthdr.ether_vtag = rxcd->vtag; + } + + ifp->if_ipackets++; + VMXNET3_RXQ_UNLOCK(rxq); + (*ifp->if_input)(ifp, m); + VMXNET3_RXQ_LOCK(rxq); +} + +static void +vmxnet3_rxq_eof(struct vmxnet3_rxqueue *rxq) +{ + struct vmxnet3_softc *sc; + struct ifnet *ifp; + struct vmxnet3_rxring *rxr; + struct vmxnet3_comp_ring *rxc; + struct vmxnet3_rxdesc *rxd; + struct vmxnet3_rxcompdesc *rxcd; + struct mbuf *m, *m_head, *m_tail; + int idx, length; + + sc = rxq->vxrxq_sc; + ifp = sc->vmx_ifp; + rxc = &rxq->vxrxq_comp_ring; + m_head = m_tail = NULL; + + VMXNET3_RXQ_LOCK_ASSERT(rxq); + + if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) + return; + + for (;;) { + rxcd = &rxc->vxcr_u.rxcd[rxc->vxcr_next]; + if (rxcd->gen != rxc->vxcr_gen) + break; + vmxnet3_barrier(sc, VMXNET3_BARRIER_RD); + + if (++rxc->vxcr_next == rxc->vxcr_ndesc) { + rxc->vxcr_next = 0; + rxc->vxcr_gen ^= 1; + } + + idx = rxcd->rxd_idx; + length = rxcd->len; + if (rxcd->qid < sc->vmx_nrxqueues) + rxr = &rxq->vxrxq_cmd_ring[0]; + else + rxr = &rxq->vxrxq_cmd_ring[1]; + rxd = &rxr->vxrxr_rxd[idx]; + + m = rxr->vxrxr_rxbuf[idx].vrxb_m; + KASSERT(m != NULL, ("%s: queue %d idx %d without mbuf", + __func__, rxcd->qid, idx)); + + /* + * The host may skip descriptors. We detect this when this + * descriptor does not match the previous fill index. Catch + * up with the host now. + */ + if (__predict_false(rxr->vxrxr_fill != idx)) { + while (rxr->vxrxr_fill != idx) { + rxr->vxrxr_rxd[rxr->vxrxr_fill].gen = + rxr->vxrxr_gen; + vmxnet3_rxr_increment_fill(rxr); + } + } + + if (rxcd->sop) { + KASSERT(rxd->btype == VMXNET3_BTYPE_HEAD, + ("%s: start of frame w/o head buffer", __func__)); + KASSERT(rxr == &rxq->vxrxq_cmd_ring[0], + ("%s: start of frame not in ring 0", __func__)); + KASSERT((idx % sc->vmx_rx_max_chain) == 0, + ("%s: start of frame at unexcepted index %d (%d)", + __func__, idx, sc->vmx_rx_max_chain)); + KASSERT(m_head == NULL, + ("%s: duplicate start of frame?", __func__)); + + if (length == 0) { + /* Just ignore this descriptor. */ + vmxnet3_rxq_eof_discard(rxq, rxr, idx); + goto nextp; + } + + if (vmxnet3_newbuf(sc, rxr) != 0) { + ifp->if_iqdrops++; + vmxnet3_rxq_eof_discard(rxq, rxr, idx); + if (!rxcd->eop) + vmxnet3_rxq_discard_chain(rxq); + goto nextp; + } + + m->m_pkthdr.rcvif = ifp; + m->m_pkthdr.len = m->m_len = length; + m->m_pkthdr.csum_flags = 0; + m_head = m_tail = m; + + } else { + KASSERT(rxd->btype == VMXNET3_BTYPE_BODY, + ("%s: non start of frame w/o body buffer", __func__)); + KASSERT(m_head != NULL, + ("%s: frame not started?", __func__)); + + if (vmxnet3_newbuf(sc, rxr) != 0) { + ifp->if_iqdrops++; + vmxnet3_rxq_eof_discard(rxq, rxr, idx); + if (!rxcd->eop) + vmxnet3_rxq_discard_chain(rxq); + m_freem(m_head); + m_head = m_tail = NULL; + goto nextp; + } + + m->m_len = length; + m_head->m_pkthdr.len += length; + m_tail->m_next = m; + m_tail = m; + } + + if (rxcd->eop) { + vmxnet3_rxq_input(rxq, rxcd, m_head); + m_head = m_tail = NULL; + + /* Must recheck after dropping the Rx lock. */ + if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) + break; + } + +nextp: + if (__predict_false(rxq->vxrxq_rs->update_rxhead)) { + int qid = rxcd->qid; + bus_size_t r; + + idx = (idx + 1) % rxr->vxrxr_ndesc; + if (qid >= sc->vmx_nrxqueues) { + qid -= sc->vmx_nrxqueues; + r = VMXNET3_BAR0_RXH2(qid); + } else + r = VMXNET3_BAR0_RXH1(qid); + vmxnet3_write_bar0(sc, r, idx); + } + } +} + +static void +vmxnet3_legacy_intr(void *xsc) +{ + struct vmxnet3_softc *sc; + struct vmxnet3_rxqueue *rxq; + struct vmxnet3_txqueue *txq; + struct ifnet *ifp; + + sc = xsc; + rxq = &sc->vmx_rxq[0]; + txq = &sc->vmx_txq[0]; + ifp = sc->vmx_ifp; + + if (sc->vmx_intr_type == VMXNET3_IT_LEGACY) { + if (vmxnet3_read_bar1(sc, VMXNET3_BAR1_INTR) == 0) + return; + } + if (sc->vmx_intr_mask_mode == VMXNET3_IMM_ACTIVE) + vmxnet3_disable_all_intrs(sc); + + if (sc->vmx_ds->event != 0) + vmxnet3_evintr(sc); + + VMXNET3_RXQ_LOCK(rxq); + vmxnet3_rxq_eof(rxq); + VMXNET3_RXQ_UNLOCK(rxq); + + VMXNET3_TXQ_LOCK(txq); + vmxnet3_txq_eof(txq); + if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd)) + vmxnet3_start_locked(ifp); + VMXNET3_TXQ_UNLOCK(txq); + + vmxnet3_enable_all_intrs(sc); +} + +static void +vmxnet3_txq_intr(void *xtxq) +{ + struct vmxnet3_softc *sc; + struct vmxnet3_txqueue *txq; + struct ifnet *ifp; + + txq = xtxq; + sc = txq->vxtxq_sc; + ifp = sc->vmx_ifp; + + if (sc->vmx_intr_mask_mode == VMXNET3_IMM_ACTIVE) + vmxnet3_disable_intr(sc, txq->vxtxq_intr_idx); + + VMXNET3_TXQ_LOCK(txq); + vmxnet3_txq_eof(txq); + if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd)) + vmxnet3_start_locked(ifp); + VMXNET3_TXQ_UNLOCK(txq); + + vmxnet3_enable_intr(sc, txq->vxtxq_intr_idx); +} + +static void +vmxnet3_rxq_intr(void *xrxq) +{ + struct vmxnet3_softc *sc; + struct vmxnet3_rxqueue *rxq; + + rxq = xrxq; + sc = rxq->vxrxq_sc; + + if (sc->vmx_intr_mask_mode == VMXNET3_IMM_ACTIVE) + vmxnet3_disable_intr(sc, rxq->vxrxq_intr_idx); + + VMXNET3_RXQ_LOCK(rxq); + vmxnet3_rxq_eof(rxq); + VMXNET3_RXQ_UNLOCK(rxq); + + vmxnet3_enable_intr(sc, rxq->vxrxq_intr_idx); +} + +static void +vmxnet3_event_intr(void *xsc) +{ + struct vmxnet3_softc *sc; + + sc = xsc; + + if (sc->vmx_intr_mask_mode == VMXNET3_IMM_ACTIVE) + vmxnet3_disable_intr(sc, sc->vmx_event_intr_idx); + + if (sc->vmx_ds->event != 0) + vmxnet3_evintr(sc); + + vmxnet3_enable_intr(sc, sc->vmx_event_intr_idx); +} + +static void +vmxnet3_txstop(struct vmxnet3_softc *sc, struct vmxnet3_txqueue *txq) +{ + struct vmxnet3_txring *txr; + struct vmxnet3_txbuf *txb; + int i; + + txr = &txq->vxtxq_cmd_ring; + + for (i = 0; i < txr->vxtxr_ndesc; i++) { + txb = &txr->vxtxr_txbuf[i]; + + if (txb->vtxb_m == NULL) + continue; + + bus_dmamap_sync(txr->vxtxr_txtag, txb->vtxb_dmamap, + BUS_DMASYNC_POSTWRITE); + bus_dmamap_unload(txr->vxtxr_txtag, txb->vtxb_dmamap); + m_freem(txb->vtxb_m); + txb->vtxb_m = NULL; + } +} + +static void +vmxnet3_rxstop(struct vmxnet3_softc *sc, struct vmxnet3_rxqueue *rxq) +{ + struct vmxnet3_rxring *rxr; + struct vmxnet3_rxbuf *rxb; + int i, j; + + for (i = 0; i < VMXNET3_RXRINGS_PERQ; i++) { + rxr = &rxq->vxrxq_cmd_ring[i]; + + for (j = 0; j < rxr->vxrxr_ndesc; j++) { + rxb = &rxr->vxrxr_rxbuf[j]; + + if (rxb->vrxb_m == NULL) + continue; + bus_dmamap_sync(rxr->vxrxr_rxtag, rxb->vrxb_dmamap, + BUS_DMASYNC_POSTREAD); + bus_dmamap_unload(rxr->vxrxr_rxtag, rxb->vrxb_dmamap); + m_freem(rxb->vrxb_m); + rxb->vrxb_m = NULL; + } + } +} + +static void +vmxnet3_stop_rendezvous(struct vmxnet3_softc *sc) +{ + struct vmxnet3_rxqueue *rxq; + struct vmxnet3_txqueue *txq; + int i; + + for (i = 0; i < sc->vmx_nrxqueues; i++) { + rxq = &sc->vmx_rxq[i]; + VMXNET3_RXQ_LOCK(rxq); + VMXNET3_RXQ_UNLOCK(rxq); + } + + for (i = 0; i < sc->vmx_ntxqueues; i++) { + txq = &sc->vmx_txq[i]; + VMXNET3_TXQ_LOCK(txq); + VMXNET3_TXQ_UNLOCK(txq); + } +} + +static void +vmxnet3_stop(struct vmxnet3_softc *sc) +{ + struct ifnet *ifp; + int q; + + ifp = sc->vmx_ifp; + VMXNET3_CORE_LOCK_ASSERT(sc); + + ifp->if_drv_flags &= ~IFF_DRV_RUNNING; + sc->vmx_link_active = 0; + callout_stop(&sc->vmx_tick); + + /* Disable interrupts. */ + vmxnet3_disable_all_intrs(sc); + vmxnet3_write_cmd(sc, VMXNET3_CMD_DISABLE); + + vmxnet3_stop_rendezvous(sc); + + for (q = 0; q < sc->vmx_ntxqueues; q++) + vmxnet3_txstop(sc, &sc->vmx_txq[q]); + for (q = 0; q < sc->vmx_nrxqueues; q++) + vmxnet3_rxstop(sc, &sc->vmx_rxq[q]); + + vmxnet3_write_cmd(sc, VMXNET3_CMD_RESET); +} + +static void +vmxnet3_txinit(struct vmxnet3_softc *sc, struct vmxnet3_txqueue *txq) +{ + struct vmxnet3_txring *txr; + struct vmxnet3_comp_ring *txc; + + txr = &txq->vxtxq_cmd_ring; + txr->vxtxr_head = 0; + txr->vxtxr_next = 0; + txr->vxtxr_gen = VMXNET3_INIT_GEN; + bzero(txr->vxtxr_txd, + txr->vxtxr_ndesc * sizeof(struct vmxnet3_txdesc)); + + txc = &txq->vxtxq_comp_ring; + txc->vxcr_next = 0; + txc->vxcr_gen = VMXNET3_INIT_GEN; + bzero(txc->vxcr_u.txcd, + txc->vxcr_ndesc * sizeof(struct vmxnet3_txcompdesc)); +} + +static int +vmxnet3_rxinit(struct vmxnet3_softc *sc, struct vmxnet3_rxqueue *rxq) +{ + struct ifnet *ifp; + struct vmxnet3_rxring *rxr; + struct vmxnet3_comp_ring *rxc; + int i, populate, idx, frame_size, error; + + ifp = sc->vmx_ifp; + frame_size = ETHER_ALIGN + sizeof(struct ether_vlan_header) + + ifp->if_mtu; + + /* + * If the MTU causes us to exceed what a regular sized cluster can + * handle, we allocate a second MJUMPAGESIZE cluster after it in + * ring 0. If in use, ring 1 always contains MJUMPAGESIZE clusters. + * + * Keep rx_max_chain a divisor of the maximum Rx ring size to make + * our life easier. We do not support changing the ring size after + * the attach. + */ + if (frame_size <= MCLBYTES) + sc->vmx_rx_max_chain = 1; + else + sc->vmx_rx_max_chain = 2; + + /* + * Only populate ring 1 if the configuration will take advantage + * of it. That is either when LRO is enabled or the frame size + * exceeds what ring 0 can contain. + */ + if ((ifp->if_capenable & IFCAP_LRO) == 0 && + frame_size <= MCLBYTES + MJUMPAGESIZE) + populate = 1; + else + populate = VMXNET3_RXRINGS_PERQ; + + for (i = 0; i < populate; i++) { + rxr = &rxq->vxrxq_cmd_ring[i]; + rxr->vxrxr_fill = 0; + rxr->vxrxr_gen = VMXNET3_INIT_GEN; + bzero(rxr->vxrxr_rxd, + rxr->vxrxr_ndesc * sizeof(struct vmxnet3_rxdesc)); + + for (idx = 0; idx < rxr->vxrxr_ndesc; idx++) { + error = vmxnet3_newbuf(sc, rxr); + if (error) + return (error); + } + } + + for (/**/; i < VMXNET3_RXRINGS_PERQ; i++) { + rxr = &rxq->vxrxq_cmd_ring[i]; + rxr->vxrxr_fill = 0; + rxr->vxrxr_gen = 0; + bzero(rxr->vxrxr_rxd, + rxr->vxrxr_ndesc * sizeof(struct vmxnet3_rxdesc)); + } + + rxc = &rxq->vxrxq_comp_ring; + rxc->vxcr_next = 0; + rxc->vxcr_gen = VMXNET3_INIT_GEN; + bzero(rxc->vxcr_u.rxcd, + rxc->vxcr_ndesc * sizeof(struct vmxnet3_rxcompdesc)); + + return (0); +} + +static int +vmxnet3_reinit_queues(struct vmxnet3_softc *sc) +{ + device_t dev; + int q, error; + + dev = sc->vmx_dev; + + for (q = 0; q < sc->vmx_ntxqueues; q++) + vmxnet3_txinit(sc, &sc->vmx_txq[q]); + + for (q = 0; q < sc->vmx_nrxqueues; q++) { + error = vmxnet3_rxinit(sc, &sc->vmx_rxq[q]); + if (error) { + device_printf(dev, "cannot populate Rx queue %d\n", q); + return (error); + } + } + + return (0); +} + +static int +vmxnet3_enable_device(struct vmxnet3_softc *sc) +{ + int q; + + if (vmxnet3_read_cmd(sc, VMXNET3_CMD_ENABLE) != 0) { + device_printf(sc->vmx_dev, "device enable command failed!\n"); + return (1); + } + + /* Reset the Rx queue heads. */ + for (q = 0; q < sc->vmx_nrxqueues; q++) { + vmxnet3_write_bar0(sc, VMXNET3_BAR0_RXH1(q), 0); + vmxnet3_write_bar0(sc, VMXNET3_BAR0_RXH2(q), 0); + } + + return (0); +} + +static void +vmxnet3_reinit_rxfilters(struct vmxnet3_softc *sc) +{ + struct ifnet *ifp; + + ifp = sc->vmx_ifp; + + vmxnet3_set_rxfilter(sc); + + if (ifp->if_capenable & IFCAP_VLAN_HWFILTER) + bcopy(sc->vmx_vlan_filter, sc->vmx_ds->vlan_filter, + sizeof(sc->vmx_ds->vlan_filter)); + else + bzero(sc->vmx_ds->vlan_filter, + sizeof(sc->vmx_ds->vlan_filter)); + vmxnet3_write_cmd(sc, VMXNET3_CMD_VLAN_FILTER); +} + +static int +vmxnet3_reinit(struct vmxnet3_softc *sc) +{ + + vmxnet3_reinit_interface(sc); + vmxnet3_reinit_shared_data(sc); + + if (vmxnet3_reinit_queues(sc) != 0) + return (ENXIO); + + if (vmxnet3_enable_device(sc) != 0) + return (ENXIO); + + vmxnet3_reinit_rxfilters(sc); + + return (0); +} + +static void +vmxnet3_init_locked(struct vmxnet3_softc *sc) +{ + struct ifnet *ifp; + + ifp = sc->vmx_ifp; + + if (ifp->if_drv_flags & IFF_DRV_RUNNING) + return; + + vmxnet3_stop(sc); + + if (vmxnet3_reinit(sc) != 0) { + vmxnet3_stop(sc); + return; + } + + ifp->if_drv_flags |= IFF_DRV_RUNNING; + vmxnet3_link_status(sc); + + vmxnet3_enable_all_intrs(sc); + callout_reset(&sc->vmx_tick, hz, vmxnet3_tick, sc); +} + +static void +vmxnet3_init(void *xsc) +{ + struct vmxnet3_softc *sc; + + sc = xsc; + + VMXNET3_CORE_LOCK(sc); + vmxnet3_init_locked(sc); + VMXNET3_CORE_UNLOCK(sc); +} + +/* + * BMV: Much of this can go away once we finally have offsets in + * the mbuf packet header. Bug andre@. + */ +static int +vmxnet3_txq_offload_ctx(struct mbuf *m, int *etype, int *proto, int *start) +{ + struct ether_vlan_header *evh; + int offset; + + evh = mtod(m, struct ether_vlan_header *); + if (evh->evl_encap_proto == htons(ETHERTYPE_VLAN)) { + /* BMV: We should handle nested VLAN tags too. */ + *etype = ntohs(evh->evl_proto); + offset = sizeof(struct ether_vlan_header); + } else { + *etype = ntohs(evh->evl_encap_proto); + offset = sizeof(struct ether_header); + } + + switch (*etype) { +#if defined(INET) + case ETHERTYPE_IP: { + struct ip *ip, iphdr; + if (__predict_false(m->m_len < offset + sizeof(struct ip))) { + m_copydata(m, offset, sizeof(struct ip), + (caddr_t) &iphdr); + ip = &iphdr; + } else + ip = (struct ip *)(m->m_data + offset); + *proto = ip->ip_p; + *start = offset + (ip->ip_hl << 2); + break; + } +#endif +#if defined(INET6) + case ETHERTYPE_IPV6: + *proto = -1; + *start = ip6_lasthdr(m, offset, IPPROTO_IPV6, proto); + /* Assert the network stack sent us a valid packet. */ + KASSERT(*start > offset, + ("%s: mbuf %p start %d offset %d proto %d", __func__, m, + *start, offset, *proto)); + break; +#endif + default: + return (EINVAL); + } + + if (m->m_pkthdr.csum_flags & CSUM_TSO) { + struct tcphdr *tcp, tcphdr; + + if (__predict_false(*proto != IPPROTO_TCP)) { + /* Likely failed to correctly parse the mbuf. */ + return (EINVAL); + } + + if (m->m_len < *start + sizeof(struct tcphdr)) { + m_copydata(m, offset, sizeof(struct tcphdr), + (caddr_t) &tcphdr); + tcp = &tcphdr; + } else + tcp = (struct tcphdr *)(m->m_data + *start); + + /* + * For TSO, the size of the protocol header is also + * included in the descriptor header size. + */ + *start += (tcp->th_off << 2); + } + + return (0); +} + +static int +vmxnet3_txq_load_mbuf(struct vmxnet3_txqueue *txq, struct mbuf **m0, + bus_dmamap_t dmap, bus_dma_segment_t segs[], int *nsegs) +{ + struct vmxnet3_txring *txr; + struct mbuf *m; + bus_dma_tag_t tag; + int maxsegs, error; + + txr = &txq->vxtxq_cmd_ring; + m = *m0; + tag = txr->vxtxr_txtag; + maxsegs = VMXNET3_TX_MAXSEGS; + + error = bus_dmamap_load_mbuf_sg(tag, dmap, m, segs, nsegs, 0); + if (error == 0 || error != EFBIG) + return (error); + + m = m_collapse(m, M_NOWAIT, maxsegs); + if (m != NULL) { + *m0 = m; + error = bus_dmamap_load_mbuf_sg(tag, dmap, m, segs, nsegs, 0); + } else + error = ENOBUFS; + + if (error) { + m_freem(*m0); + *m0 = NULL; + } else + txq->vxtxq_sc->vmx_stats.vmst_collapsed++; + + return (error); +} + +static void +vmxnet3_txq_unload_mbuf(struct vmxnet3_txqueue *txq, bus_dmamap_t dmap) +{ + struct vmxnet3_txring *txr; + + txr = &txq->vxtxq_cmd_ring; + bus_dmamap_unload(txr->vxtxr_txtag, dmap); +} + +static int +vmxnet3_txq_encap(struct vmxnet3_txqueue *txq, struct mbuf **m0) +{ + struct vmxnet3_softc *sc; + struct ifnet *ifp; + struct vmxnet3_txring *txr; + struct vmxnet3_txdesc *txd, *sop; + struct mbuf *m; + bus_dmamap_t dmap; + bus_dma_segment_t segs[VMXNET3_TX_MAXSEGS]; + int i, gen, nsegs, etype, proto, start, error; + + sc = txq->vxtxq_sc; + ifp = sc->vmx_ifp; + start = 0; + txd = NULL; + txr = &txq->vxtxq_cmd_ring; + dmap = txr->vxtxr_txbuf[txr->vxtxr_head].vtxb_dmamap; + + error = vmxnet3_txq_load_mbuf(txq, m0, dmap, segs, &nsegs); + if (error) + return (error); + + m = *m0; + M_ASSERTPKTHDR(m); + KASSERT(nsegs <= VMXNET3_TX_MAXSEGS, + ("%s: mbuf %p with too many segments %d", __func__, m, nsegs)); + + if (VMXNET3_TXRING_AVAIL(txr) < nsegs) { + txq->vxtxq_stats.vtxrs_full++; + vmxnet3_txq_unload_mbuf(txq, dmap); + return (ENOSPC); + } else if (m->m_pkthdr.csum_flags & VMXNET3_CSUM_ALL_OFFLOAD) { + error = vmxnet3_txq_offload_ctx(m, &etype, &proto, &start); + if (error) { + txq->vxtxq_stats.vtxrs_offload_failed++; + vmxnet3_txq_unload_mbuf(txq, dmap); + m_freem(m); + *m0 = NULL; + return (error); + } + } + + txr->vxtxr_txbuf[txr->vxtxr_head].vtxb_m = m = *m0; + sop = &txr->vxtxr_txd[txr->vxtxr_head]; + gen = txr->vxtxr_gen ^ 1; /* Owned by cpu (yet) */ + + for (i = 0; i < nsegs; i++) { + txd = &txr->vxtxr_txd[txr->vxtxr_head]; + + txd->addr = segs[i].ds_addr; + txd->len = segs[i].ds_len; + txd->gen = gen; + txd->dtype = 0; + txd->offload_mode = VMXNET3_OM_NONE; + txd->offload_pos = 0; + txd->hlen = 0; + txd->eop = 0; + txd->compreq = 0; + txd->vtag_mode = 0; + txd->vtag = 0; + + if (++txr->vxtxr_head == txr->vxtxr_ndesc) { + txr->vxtxr_head = 0; + txr->vxtxr_gen ^= 1; + } + gen = txr->vxtxr_gen; + } + txd->eop = 1; + txd->compreq = 1; + + if (m->m_flags & M_VLANTAG) { + sop->vtag_mode = 1; + sop->vtag = m->m_pkthdr.ether_vtag; + } + + if (m->m_pkthdr.csum_flags & CSUM_TSO) { + sop->offload_mode = VMXNET3_OM_TSO; + sop->hlen = start; + sop->offload_pos = m->m_pkthdr.tso_segsz; + } else if (m->m_pkthdr.csum_flags & (VMXNET3_CSUM_OFFLOAD | + VMXNET3_CSUM_OFFLOAD_IPV6)) { + sop->offload_mode = VMXNET3_OM_CSUM; + sop->hlen = start; + sop->offload_pos = start + m->m_pkthdr.csum_data; + } + + /* Finally, change the ownership. */ + vmxnet3_barrier(sc, VMXNET3_BARRIER_WR); + sop->gen ^= 1; + + if (++txq->vxtxq_ts->npending >= txq->vxtxq_ts->intr_threshold) { + txq->vxtxq_ts->npending = 0; + vmxnet3_write_bar0(sc, VMXNET3_BAR0_TXH(txq->vxtxq_id), + txr->vxtxr_head); + } + + return (0); +} + +static void +vmxnet3_start_locked(struct ifnet *ifp) +{ + struct vmxnet3_softc *sc; + struct vmxnet3_txqueue *txq; + struct vmxnet3_txring *txr; + struct mbuf *m_head; + int tx, avail; + + sc = ifp->if_softc; + txq = &sc->vmx_txq[0]; + txr = &txq->vxtxq_cmd_ring; + tx = 0; + + VMXNET3_TXQ_LOCK_ASSERT(txq); + + if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0 || + sc->vmx_link_active == 0) + return; + + while (!IFQ_DRV_IS_EMPTY(&ifp->if_snd)) { + if ((avail = VMXNET3_TXRING_AVAIL(txr)) < 2) + break; + + IFQ_DRV_DEQUEUE(&ifp->if_snd, m_head); + if (m_head == NULL) + break; + + /* Assume worse case if this mbuf is the head of a chain. */ + if (m_head->m_next != NULL && avail < VMXNET3_TX_MAXSEGS) { + IFQ_DRV_PREPEND(&ifp->if_snd, m_head); + break; + } + + if (vmxnet3_txq_encap(txq, &m_head) != 0) { + if (m_head != NULL) + IFQ_DRV_PREPEND(&ifp->if_snd, m_head); + break; + } + + tx++; + ETHER_BPF_MTAP(ifp, m_head); + } + + if (tx > 0) { + if (txq->vxtxq_ts->npending > 0) { + txq->vxtxq_ts->npending = 0; + vmxnet3_write_bar0(sc, VMXNET3_BAR0_TXH(txq->vxtxq_id), + txr->vxtxr_head); + } + txq->vxtxq_watchdog = VMXNET3_WATCHDOG_TIMEOUT; + } +} + +static void +vmxnet3_start(struct ifnet *ifp) +{ + struct vmxnet3_softc *sc; + struct vmxnet3_txqueue *txq; + + sc = ifp->if_softc; + txq = &sc->vmx_txq[0]; + + VMXNET3_TXQ_LOCK(txq); + vmxnet3_start_locked(ifp); + VMXNET3_TXQ_UNLOCK(txq); +} + +static void +vmxnet3_update_vlan_filter(struct vmxnet3_softc *sc, int add, uint16_t tag) +{ + struct ifnet *ifp; + int idx, bit; + + ifp = sc->vmx_ifp; + idx = (tag >> 5) & 0x7F; + bit = tag & 0x1F; + + if (tag == 0 || tag > 4095) + return; + + VMXNET3_CORE_LOCK(sc); + + /* Update our private VLAN bitvector. */ + if (add) + sc->vmx_vlan_filter[idx] |= (1 << bit); + else + sc->vmx_vlan_filter[idx] &= ~(1 << bit); + + if (ifp->if_capenable & IFCAP_VLAN_HWFILTER) { + if (add) + sc->vmx_ds->vlan_filter[idx] |= (1 << bit); + else + sc->vmx_ds->vlan_filter[idx] &= ~(1 << bit); + vmxnet3_write_cmd(sc, VMXNET3_CMD_VLAN_FILTER); + } + + VMXNET3_CORE_UNLOCK(sc); +} + +static void +vmxnet3_register_vlan(void *arg, struct ifnet *ifp, uint16_t tag) +{ + + if (ifp->if_softc == arg) + vmxnet3_update_vlan_filter(arg, 1, tag); +} + +static void +vmxnet3_unregister_vlan(void *arg, struct ifnet *ifp, uint16_t tag) +{ + + if (ifp->if_softc == arg) + vmxnet3_update_vlan_filter(arg, 0, tag); +} + +static void +vmxnet3_set_rxfilter(struct vmxnet3_softc *sc) +{ + struct ifnet *ifp; + struct vmxnet3_driver_shared *ds; + struct ifmultiaddr *ifma; + u_int mode; + + ifp = sc->vmx_ifp; + ds = sc->vmx_ds; + + mode = VMXNET3_RXMODE_UCAST; + if (ifp->if_flags & IFF_BROADCAST) + mode |= VMXNET3_RXMODE_BCAST; + if (ifp->if_flags & IFF_PROMISC) + mode |= VMXNET3_RXMODE_PROMISC; + if (ifp->if_flags & IFF_ALLMULTI) + mode |= VMXNET3_RXMODE_ALLMULTI; + else { + int cnt = 0, overflow = 0; + + if_maddr_rlock(ifp); + TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { + if (ifma->ifma_addr->sa_family != AF_LINK) + continue; + else if (cnt == VMXNET3_MULTICAST_MAX) { + overflow = 1; + break; + } + + bcopy(LLADDR((struct sockaddr_dl *)ifma->ifma_addr), + &sc->vmx_mcast[cnt*ETHER_ADDR_LEN], ETHER_ADDR_LEN); + cnt++; + } + if_maddr_runlock(ifp); + + if (overflow != 0) { + cnt = 0; + mode |= VMXNET3_RXMODE_ALLMULTI; + } else if (cnt > 0) + mode |= VMXNET3_RXMODE_MCAST; + ds->mcast_tablelen = cnt * ETHER_ADDR_LEN; + } + + ds->rxmode = mode; + + vmxnet3_write_cmd(sc, VMXNET3_CMD_SET_FILTER); + vmxnet3_write_cmd(sc, VMXNET3_CMD_SET_RXMODE); +} + +static int +vmxnet3_change_mtu(struct vmxnet3_softc *sc, int mtu) +{ + struct ifnet *ifp; + + ifp = sc->vmx_ifp; + + if (mtu < VMXNET3_MIN_MTU || mtu > VMXNET3_MAX_MTU) + return (EINVAL); + + ifp->if_mtu = mtu; + + if (ifp->if_drv_flags & IFF_DRV_RUNNING) { + ifp->if_drv_flags &= ~IFF_DRV_RUNNING; + vmxnet3_init_locked(sc); + } + + return (0); +} + +static int +vmxnet3_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) +{ + struct vmxnet3_softc *sc; + struct ifreq *ifr; + int reinit, mask, error; + + sc = ifp->if_softc; + ifr = (struct ifreq *) data; + error = 0; + + switch (cmd) { + case SIOCSIFMTU: + if (ifp->if_mtu != ifr->ifr_mtu) { + VMXNET3_CORE_LOCK(sc); + error = vmxnet3_change_mtu(sc, ifr->ifr_mtu); + VMXNET3_CORE_UNLOCK(sc); + } + break; + + case SIOCSIFFLAGS: + VMXNET3_CORE_LOCK(sc); + if (ifp->if_flags & IFF_UP) { + if ((ifp->if_drv_flags & IFF_DRV_RUNNING)) { + if ((ifp->if_flags ^ sc->vmx_if_flags) & + (IFF_PROMISC | IFF_ALLMULTI)) { + vmxnet3_set_rxfilter(sc); + } + } else + vmxnet3_init_locked(sc); + } else { + if (ifp->if_drv_flags & IFF_DRV_RUNNING) + vmxnet3_stop(sc); + } + sc->vmx_if_flags = ifp->if_flags; + VMXNET3_CORE_UNLOCK(sc); + break; + + case SIOCADDMULTI: + case SIOCDELMULTI: + VMXNET3_CORE_LOCK(sc); + if (ifp->if_drv_flags & IFF_DRV_RUNNING) + vmxnet3_set_rxfilter(sc); + VMXNET3_CORE_UNLOCK(sc); + break; + + case SIOCSIFMEDIA: + case SIOCGIFMEDIA: + error = ifmedia_ioctl(ifp, ifr, &sc->vmx_media, cmd); + break; + + case SIOCSIFCAP: + VMXNET3_CORE_LOCK(sc); + mask = ifr->ifr_reqcap ^ ifp->if_capenable; + + if (mask & IFCAP_TXCSUM) + ifp->if_capenable ^= IFCAP_TXCSUM; + if (mask & IFCAP_TXCSUM_IPV6) + ifp->if_capenable ^= IFCAP_TXCSUM_IPV6; + if (mask & IFCAP_TSO4) + ifp->if_capenable ^= IFCAP_TSO4; + if (mask & IFCAP_TSO6) + ifp->if_capenable ^= IFCAP_TSO6; + + if (mask & (IFCAP_RXCSUM | IFCAP_RXCSUM_IPV6 | IFCAP_LRO | + IFCAP_VLAN_HWTAGGING | IFCAP_VLAN_HWFILTER)) { + /* Changing these features requires us to reinit. */ + reinit = 1; + + if (mask & IFCAP_RXCSUM) + ifp->if_capenable ^= IFCAP_RXCSUM; + if (mask & IFCAP_RXCSUM_IPV6) + ifp->if_capenable ^= IFCAP_RXCSUM_IPV6; + if (mask & IFCAP_LRO) + ifp->if_capenable ^= IFCAP_LRO; + if (mask & IFCAP_VLAN_HWTAGGING) + ifp->if_capenable ^= IFCAP_VLAN_HWTAGGING; + if (mask & IFCAP_VLAN_HWFILTER) + ifp->if_capenable ^= IFCAP_VLAN_HWFILTER; + } else + reinit = 0; + + if (mask & IFCAP_VLAN_HWTSO) + ifp->if_capenable ^= IFCAP_VLAN_HWTSO; + + if (reinit && (ifp->if_drv_flags & IFF_DRV_RUNNING)) { + ifp->if_drv_flags &= ~IFF_DRV_RUNNING; + vmxnet3_init_locked(sc); + } + + VMXNET3_CORE_UNLOCK(sc); + VLAN_CAPABILITIES(ifp); + break; + + default: + error = ether_ioctl(ifp, cmd, data); + break; + } + + VMXNET3_CORE_LOCK_ASSERT_NOTOWNED(sc); + + return (error); +} + +static int +vmxnet3_watchdog(struct vmxnet3_txqueue *txq) +{ + struct vmxnet3_softc *sc; + + sc = txq->vxtxq_sc; + + VMXNET3_TXQ_LOCK(txq); + if (txq->vxtxq_watchdog == 0 || --txq->vxtxq_watchdog) { + VMXNET3_TXQ_UNLOCK(txq); + return (0); + } + VMXNET3_TXQ_UNLOCK(txq); + + if_printf(sc->vmx_ifp, "watchdog timeout on queue %d\n", + txq->vxtxq_id); + return (1); +} + +static void +vmxnet3_refresh_stats(struct vmxnet3_softc *sc) +{ + + vmxnet3_write_cmd(sc, VMXNET3_CMD_GET_STATS); +} + +static void +vmxnet3_tick(void *xsc) +{ + struct vmxnet3_softc *sc; + struct ifnet *ifp; + int i, timedout; + + sc = xsc; + ifp = sc->vmx_ifp; + timedout = 0; + + VMXNET3_CORE_LOCK_ASSERT(sc); + vmxnet3_refresh_stats(sc); + + for (i = 0; i < sc->vmx_ntxqueues; i++) + timedout |= vmxnet3_watchdog(&sc->vmx_txq[i]); + + if (timedout != 0) { + ifp->if_drv_flags &= ~IFF_DRV_RUNNING; + vmxnet3_init_locked(sc); + } else + callout_reset(&sc->vmx_tick, hz, vmxnet3_tick, sc); +} + +static int +vmxnet3_link_is_up(struct vmxnet3_softc *sc) +{ + uint32_t status; + + /* Also update the link speed while here. */ + status = vmxnet3_read_cmd(sc, VMXNET3_CMD_GET_LINK); + sc->vmx_link_speed = status >> 16; + return !!(status & 0x1); +} + +static void +vmxnet3_link_status(struct vmxnet3_softc *sc) +{ + struct ifnet *ifp; + int link; + + ifp = sc->vmx_ifp; + link = vmxnet3_link_is_up(sc); + + if (link != 0 && sc->vmx_link_active == 0) { + sc->vmx_link_active = 1; + if_link_state_change(ifp, LINK_STATE_UP); + } else if (link == 0 && sc->vmx_link_active != 0) { + sc->vmx_link_active = 0; + if_link_state_change(ifp, LINK_STATE_DOWN); + } +} + +static void +vmxnet3_media_status(struct ifnet *ifp, struct ifmediareq *ifmr) +{ + struct vmxnet3_softc *sc; + + sc = ifp->if_softc; + + ifmr->ifm_active = IFM_ETHER | IFM_AUTO; + ifmr->ifm_status = IFM_AVALID; + + VMXNET3_CORE_LOCK(sc); + if (vmxnet3_link_is_up(sc) != 0) + ifmr->ifm_status |= IFM_ACTIVE; + else + ifmr->ifm_status |= IFM_NONE; + VMXNET3_CORE_UNLOCK(sc); +} + +static int +vmxnet3_media_change(struct ifnet *ifp) +{ + + /* Ignore. */ + return (0); +} + +static void +vmxnet3_set_lladdr(struct vmxnet3_softc *sc) +{ + uint32_t ml, mh; + + ml = sc->vmx_lladdr[0]; + ml |= sc->vmx_lladdr[1] << 8; + ml |= sc->vmx_lladdr[2] << 16; + ml |= sc->vmx_lladdr[3] << 24; + vmxnet3_write_bar1(sc, VMXNET3_BAR1_MACL, ml); + + mh = sc->vmx_lladdr[4]; + mh |= sc->vmx_lladdr[5] << 8; + vmxnet3_write_bar1(sc, VMXNET3_BAR1_MACH, mh); +} + +static void +vmxnet3_get_lladdr(struct vmxnet3_softc *sc) +{ + uint32_t ml, mh; + + ml = vmxnet3_read_cmd(sc, VMXNET3_CMD_GET_MACL); + mh = vmxnet3_read_cmd(sc, VMXNET3_CMD_GET_MACH); + + sc->vmx_lladdr[0] = ml; + sc->vmx_lladdr[1] = ml >> 8; + sc->vmx_lladdr[2] = ml >> 16; + sc->vmx_lladdr[3] = ml >> 24; + sc->vmx_lladdr[4] = mh; + sc->vmx_lladdr[5] = mh >> 8; +} + +static void +vmxnet3_setup_txq_sysctl(struct vmxnet3_txqueue *txq, + struct sysctl_ctx_list *ctx, struct sysctl_oid_list *child) +{ + struct sysctl_oid *node, *txsnode; + struct sysctl_oid_list *list, *txslist; + struct vmxnet3_txq_stats *stats; + struct UPT1_TxStats *txstats; + char namebuf[16]; + + stats = &txq->vxtxq_stats; + txstats = &txq->vxtxq_ts->stats; + + snprintf(namebuf, sizeof(namebuf), "txq%d", txq->vxtxq_id); + node = SYSCTL_ADD_NODE(ctx, child, OID_AUTO, namebuf, CTLFLAG_RD, + NULL, "Transmit Queue"); + txq->vxtxq_sysctl = list = SYSCTL_CHILDREN(node); + + SYSCTL_ADD_UQUAD(ctx, list, OID_AUTO, "ringfull", CTLFLAG_RD, + &stats->vtxrs_full, "Tx ring full"); + SYSCTL_ADD_UQUAD(ctx, list, OID_AUTO, "offload_failed", CTLFLAG_RD, + &stats->vtxrs_offload_failed, "Tx checksum offload failed"); + + /* + * Add statistics reported by the host. These are updated once + * per second. + */ + txsnode = SYSCTL_ADD_NODE(ctx, list, OID_AUTO, "hstats", CTLFLAG_RD, + NULL, "Host Statistics"); + txslist = SYSCTL_CHILDREN(txsnode); + SYSCTL_ADD_UQUAD(ctx, txslist, OID_AUTO, "tso_packets", CTLFLAG_RD, + &txstats->TSO_packets, "TSO packets"); + SYSCTL_ADD_UQUAD(ctx, txslist, OID_AUTO, "tso_bytes", CTLFLAG_RD, + &txstats->TSO_bytes, "TSO bytes"); + SYSCTL_ADD_UQUAD(ctx, txslist, OID_AUTO, "ucast_packets", CTLFLAG_RD, + &txstats->ucast_packets, "Unicast packets"); + SYSCTL_ADD_UQUAD(ctx, txslist, OID_AUTO, "unicast_bytes", CTLFLAG_RD, + &txstats->ucast_bytes, "Unicast bytes"); + SYSCTL_ADD_UQUAD(ctx, txslist, OID_AUTO, "mcast_packets", CTLFLAG_RD, + &txstats->mcast_packets, "Multicast packets"); + SYSCTL_ADD_UQUAD(ctx, txslist, OID_AUTO, "mcast_bytes", CTLFLAG_RD, + &txstats->mcast_bytes, "Multicast bytes"); + SYSCTL_ADD_UQUAD(ctx, txslist, OID_AUTO, "error", CTLFLAG_RD, + &txstats->error, "Errors"); + SYSCTL_ADD_UQUAD(ctx, txslist, OID_AUTO, "discard", CTLFLAG_RD, + &txstats->discard, "Discards"); +} + +static void +vmxnet3_setup_rxq_sysctl(struct vmxnet3_rxqueue *rxq, + struct sysctl_ctx_list *ctx, struct sysctl_oid_list *child) +{ + struct sysctl_oid *node, *rxsnode; + struct sysctl_oid_list *list, *rxslist; + struct vmxnet3_rxq_stats *stats; + struct UPT1_RxStats *rxstats; + char namebuf[16]; + + stats = &rxq->vxrxq_stats; + rxstats = &rxq->vxrxq_rs->stats; + + snprintf(namebuf, sizeof(namebuf), "rxq%d", rxq->vxrxq_id); + node = SYSCTL_ADD_NODE(ctx, child, OID_AUTO, namebuf, CTLFLAG_RD, + NULL, "Receive Queue"); + rxq->vxrxq_sysctl = list = SYSCTL_CHILDREN(node); + + /* + * Add statistics reported by the host. These are updated once + * per second. + */ + rxsnode = SYSCTL_ADD_NODE(ctx, list, OID_AUTO, "hstats", CTLFLAG_RD, + NULL, "Host Statistics"); + rxslist = SYSCTL_CHILDREN(rxsnode); + SYSCTL_ADD_UQUAD(ctx, rxslist, OID_AUTO, "lro_packets", CTLFLAG_RD, + &rxstats->LRO_packets, "LRO packets"); + SYSCTL_ADD_UQUAD(ctx, rxslist, OID_AUTO, "lro_bytes", CTLFLAG_RD, + &rxstats->LRO_bytes, "LRO bytes"); + SYSCTL_ADD_UQUAD(ctx, rxslist, OID_AUTO, "ucast_packets", CTLFLAG_RD, + &rxstats->ucast_packets, "Unicast packets"); + SYSCTL_ADD_UQUAD(ctx, rxslist, OID_AUTO, "unicast_bytes", CTLFLAG_RD, + &rxstats->ucast_bytes, "Unicast bytes"); + SYSCTL_ADD_UQUAD(ctx, rxslist, OID_AUTO, "mcast_packets", CTLFLAG_RD, + &rxstats->mcast_packets, "Multicast packets"); + SYSCTL_ADD_UQUAD(ctx, rxslist, OID_AUTO, "mcast_bytes", CTLFLAG_RD, + &rxstats->mcast_bytes, "Multicast bytes"); + SYSCTL_ADD_UQUAD(ctx, rxslist, OID_AUTO, "bcast_packets", CTLFLAG_RD, + &rxstats->bcast_packets, "Broadcast packets"); + SYSCTL_ADD_UQUAD(ctx, rxslist, OID_AUTO, "bcast_bytes", CTLFLAG_RD, + &rxstats->bcast_bytes, "Broadcast bytes"); + SYSCTL_ADD_UQUAD(ctx, rxslist, OID_AUTO, "nobuffer", CTLFLAG_RD, + &rxstats->nobuffer, "No buffer"); + SYSCTL_ADD_UQUAD(ctx, rxslist, OID_AUTO, "error", CTLFLAG_RD, + &rxstats->error, "Errors"); +} + +#ifdef VMXNET3_DEBUG_SYSCTL +static void +vmxnet3_setup_debug_sysctl(struct vmxnet3_softc *sc, + struct sysctl_ctx_list *ctx, struct sysctl_oid_list *child) +{ + struct sysctl_oid *node; + struct sysctl_oid_list *list; + int i; + + for (i = 0; i < sc->vmx_ntxqueues; i++) { + struct vmxnet3_txqueue *txq = &sc->vmx_txq[i]; + + node = SYSCTL_ADD_NODE(ctx, txq->vxtxq_sysctl, OID_AUTO, + "debug", CTLFLAG_RD, NULL, ""); + list = SYSCTL_CHILDREN(node); + + SYSCTL_ADD_UINT(ctx, list, OID_AUTO, "cmd_head", CTLFLAG_RD, + &txq->vxtxq_cmd_ring.vxtxr_head, 0, ""); + SYSCTL_ADD_UINT(ctx, list, OID_AUTO, "cmd_next", CTLFLAG_RD, + &txq->vxtxq_cmd_ring.vxtxr_next, 0, ""); + SYSCTL_ADD_UINT(ctx, list, OID_AUTO, "cmd_ndesc", CTLFLAG_RD, + &txq->vxtxq_cmd_ring.vxtxr_ndesc, 0, ""); + SYSCTL_ADD_INT(ctx, list, OID_AUTO, "cmd_gen", CTLFLAG_RD, + &txq->vxtxq_cmd_ring.vxtxr_gen, 0, ""); + SYSCTL_ADD_UINT(ctx, list, OID_AUTO, "comp_next", CTLFLAG_RD, + &txq->vxtxq_comp_ring.vxcr_next, 0, ""); + SYSCTL_ADD_UINT(ctx, list, OID_AUTO, "comp_ndesc", CTLFLAG_RD, + &txq->vxtxq_comp_ring.vxcr_ndesc, 0,""); + SYSCTL_ADD_INT(ctx, list, OID_AUTO, "comp_gen", CTLFLAG_RD, + &txq->vxtxq_comp_ring.vxcr_gen, 0, ""); + } + + for (i = 0; i < sc->vmx_nrxqueues; i++) { + struct vmxnet3_rxqueue *rxq = &sc->vmx_rxq[i]; + + node = SYSCTL_ADD_NODE(ctx, rxq->vxrxq_sysctl, OID_AUTO, + "debug", CTLFLAG_RD, NULL, ""); + list = SYSCTL_CHILDREN(node); + + SYSCTL_ADD_UINT(ctx, list, OID_AUTO, "cmd0_fill", CTLFLAG_RD, + &rxq->vxrxq_cmd_ring[0].vxrxr_fill, 0, ""); + SYSCTL_ADD_UINT(ctx, list, OID_AUTO, "cmd0_ndesc", CTLFLAG_RD, + &rxq->vxrxq_cmd_ring[0].vxrxr_ndesc, 0, ""); + SYSCTL_ADD_INT(ctx, list, OID_AUTO, "cmd0_gen", CTLFLAG_RD, + &rxq->vxrxq_cmd_ring[0].vxrxr_gen, 0, ""); + SYSCTL_ADD_UINT(ctx, list, OID_AUTO, "cmd1_fill", CTLFLAG_RD, + &rxq->vxrxq_cmd_ring[1].vxrxr_fill, 0, ""); + SYSCTL_ADD_UINT(ctx, list, OID_AUTO, "cmd1_ndesc", CTLFLAG_RD, + &rxq->vxrxq_cmd_ring[1].vxrxr_ndesc, 0, ""); + SYSCTL_ADD_INT(ctx, list, OID_AUTO, "cmd1_gen", CTLFLAG_RD, + &rxq->vxrxq_cmd_ring[1].vxrxr_gen, 0, ""); + SYSCTL_ADD_UINT(ctx, list, OID_AUTO, "comp_next", CTLFLAG_RD, + &rxq->vxrxq_comp_ring.vxcr_next, 0, ""); + SYSCTL_ADD_UINT(ctx, list, OID_AUTO, "comp_ndesc", CTLFLAG_RD, + &rxq->vxrxq_comp_ring.vxcr_ndesc, 0,""); + SYSCTL_ADD_INT(ctx, list, OID_AUTO, "comp_gen", CTLFLAG_RD, + &rxq->vxrxq_comp_ring.vxcr_gen, 0, ""); + } +} +#endif + +static void +vmxnet3_setup_queue_sysctl(struct vmxnet3_softc *sc, + struct sysctl_ctx_list *ctx, struct sysctl_oid_list *child) +{ + int i; + + for (i = 0; i < sc->vmx_ntxqueues; i++) + vmxnet3_setup_txq_sysctl(&sc->vmx_txq[i], ctx, child); + for (i = 0; i < sc->vmx_nrxqueues; i++) + vmxnet3_setup_rxq_sysctl(&sc->vmx_rxq[i], ctx, child); + +#ifdef VMXNET3_DEBUG_SYSCTL + vmxnet3_setup_debug_sysctl(sc, ctx, child); +#endif +} + +static void +vmxnet3_setup_sysctl(struct vmxnet3_softc *sc) +{ + device_t dev; + struct vmxnet3_statistics *stats; + struct sysctl_ctx_list *ctx; + struct sysctl_oid *tree; + struct sysctl_oid_list *child; + + dev = sc->vmx_dev; + ctx = device_get_sysctl_ctx(dev); + tree = device_get_sysctl_tree(dev); + child = SYSCTL_CHILDREN(tree); + + SYSCTL_ADD_INT(ctx, child, OID_AUTO, "ntxqueues", CTLFLAG_RD, + &sc->vmx_ntxqueues, 0, "Number of Tx queues"); + SYSCTL_ADD_INT(ctx, child, OID_AUTO, "nrxqueues", CTLFLAG_RD, + &sc->vmx_nrxqueues, 0, "Number of Rx queues"); + + stats = &sc->vmx_stats; + SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "collapsed", CTLFLAG_RD, + &stats->vmst_collapsed, 0, "Tx mbuf chains collapsed"); + SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "mgetcl_failed", CTLFLAG_RD, + &stats->vmst_mgetcl_failed, 0, "mbuf cluster allocation failed"); + SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "mbuf_load_failed", CTLFLAG_RD, + &stats->vmst_mbuf_load_failed, 0, "mbuf load segments failed"); + + vmxnet3_setup_queue_sysctl(sc, ctx, child); +} + +static void +vmxnet3_write_bar0(struct vmxnet3_softc *sc, bus_size_t r, uint32_t v) +{ + + bus_space_write_4(sc->vmx_iot0, sc->vmx_ioh0, r, v); +} + +static uint32_t +vmxnet3_read_bar1(struct vmxnet3_softc *sc, bus_size_t r) +{ + + return (bus_space_read_4(sc->vmx_iot1, sc->vmx_ioh1, r)); +} + +static void +vmxnet3_write_bar1(struct vmxnet3_softc *sc, bus_size_t r, uint32_t v) +{ + + bus_space_write_4(sc->vmx_iot1, sc->vmx_ioh1, r, v); +} + +static void +vmxnet3_write_cmd(struct vmxnet3_softc *sc, uint32_t cmd) +{ + + vmxnet3_write_bar1(sc, VMXNET3_BAR1_CMD, cmd); +} + +static uint32_t +vmxnet3_read_cmd(struct vmxnet3_softc *sc, uint32_t cmd) +{ + + vmxnet3_write_cmd(sc, cmd); + bus_space_barrier(sc->vmx_iot1, sc->vmx_ioh1, 0, 0, + BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE); + return (vmxnet3_read_bar1(sc, VMXNET3_BAR1_CMD)); +} + +static void +vmxnet3_enable_intr(struct vmxnet3_softc *sc, int irq) +{ + + vmxnet3_write_bar0(sc, VMXNET3_BAR0_IMASK(irq), 0); +} + +static void +vmxnet3_disable_intr(struct vmxnet3_softc *sc, int irq) +{ + + vmxnet3_write_bar0(sc, VMXNET3_BAR0_IMASK(irq), 1); +} + +static void +vmxnet3_enable_all_intrs(struct vmxnet3_softc *sc) +{ + int i; + + sc->vmx_ds->ictrl &= ~VMXNET3_ICTRL_DISABLE_ALL; + for (i = 0; i < sc->vmx_nintrs; i++) + vmxnet3_enable_intr(sc, i); +} + +static void +vmxnet3_disable_all_intrs(struct vmxnet3_softc *sc) +{ + int i; + + sc->vmx_ds->ictrl |= VMXNET3_ICTRL_DISABLE_ALL; + for (i = 0; i < sc->vmx_nintrs; i++) + vmxnet3_disable_intr(sc, i); +} + +static void +vmxnet3_dmamap_cb(void *arg, bus_dma_segment_t *segs, int nseg, int error) +{ + bus_addr_t *baddr = arg; + + if (error == 0) + *baddr = segs->ds_addr; +} + +static int +vmxnet3_dma_malloc(struct vmxnet3_softc *sc, bus_size_t size, bus_size_t align, + struct vmxnet3_dma_alloc *dma) +{ + device_t dev; + int error; + + dev = sc->vmx_dev; + bzero(dma, sizeof(struct vmxnet3_dma_alloc)); + + error = bus_dma_tag_create(bus_get_dma_tag(dev), + align, 0, /* alignment, bounds */ + BUS_SPACE_MAXADDR, /* lowaddr */ + BUS_SPACE_MAXADDR, /* highaddr */ + NULL, NULL, /* filter, filterarg */ + size, /* maxsize */ + 1, /* nsegments */ + size, /* maxsegsize */ + BUS_DMA_ALLOCNOW, /* flags */ + NULL, /* lockfunc */ + NULL, /* lockfuncarg */ + &dma->dma_tag); + if (error) { + device_printf(dev, "bus_dma_tag_create failed: %d\n", error); + goto fail; + } + + error = bus_dmamem_alloc(dma->dma_tag, (void **)&dma->dma_vaddr, + BUS_DMA_ZERO | BUS_DMA_NOWAIT, &dma->dma_map); + if (error) { + device_printf(dev, "bus_dmamem_alloc failed: %d\n", error); + goto fail; + } + + error = bus_dmamap_load(dma->dma_tag, dma->dma_map, dma->dma_vaddr, + size, vmxnet3_dmamap_cb, &dma->dma_paddr, BUS_DMA_NOWAIT); + if (error) { + device_printf(dev, "bus_dmamap_load failed: %d\n", error); + goto fail; + } + + dma->dma_size = size; + +fail: + if (error) + vmxnet3_dma_free(sc, dma); + + return (error); +} + +static void +vmxnet3_dma_free(struct vmxnet3_softc *sc, struct vmxnet3_dma_alloc *dma) +{ + + if (dma->dma_tag != NULL) { + if (dma->dma_map != NULL) { + bus_dmamap_sync(dma->dma_tag, dma->dma_map, + BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); + bus_dmamap_unload(dma->dma_tag, dma->dma_map); + } + + if (dma->dma_vaddr != NULL) { + bus_dmamem_free(dma->dma_tag, dma->dma_vaddr, + dma->dma_map); + } + + bus_dma_tag_destroy(dma->dma_tag); + } + bzero(dma, sizeof(struct vmxnet3_dma_alloc)); +} + +static int +vmxnet3_tunable_int(struct vmxnet3_softc *sc, const char *knob, int def) +{ + char path[64]; + + snprintf(path, sizeof(path), + "hw.vmx.%d.%s", device_get_unit(sc->vmx_dev), knob); + TUNABLE_INT_FETCH(path, &def); + + return (def); +} + +/* + * Since this is a purely paravirtualized device, we do not have + * to worry about DMA coherency. But at times, we must make sure + * both the compiler and CPU do not reorder memory operations. + */ +static inline void +vmxnet3_barrier(struct vmxnet3_softc *sc, vmxnet3_barrier_t type) +{ + + switch (type) { + case VMXNET3_BARRIER_RD: + rmb(); + break; + case VMXNET3_BARRIER_WR: + wmb(); + break; + case VMXNET3_BARRIER_RDWR: + mb(); + break; + default: + panic("%s: bad barrier type %d", __func__, type); + } +} diff --git a/sys/dev/vmware/vmxnet3/if_vmxreg.h b/sys/dev/vmware/vmxnet3/if_vmxreg.h new file mode 100644 index 00000000000..8b554b5579f --- /dev/null +++ b/sys/dev/vmware/vmxnet3/if_vmxreg.h @@ -0,0 +1,316 @@ +/*- + * Copyright (c) 2013 Tsubai Masanari + * + * 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. + * + * $OpenBSD: src/sys/dev/pci/if_vmxreg.h,v 1.2 2013/06/12 01:07:33 uebayasi Exp $ + * + * $FreeBSD$ + */ + +#ifndef _IF_VMXREG_H +#define _IF_VMXREG_H + +struct UPT1_TxStats { + uint64_t TSO_packets; + uint64_t TSO_bytes; + uint64_t ucast_packets; + uint64_t ucast_bytes; + uint64_t mcast_packets; + uint64_t mcast_bytes; + uint64_t bcast_packets; + uint64_t bcast_bytes; + uint64_t error; + uint64_t discard; +} __packed; + +struct UPT1_RxStats { + uint64_t LRO_packets; + uint64_t LRO_bytes; + uint64_t ucast_packets; + uint64_t ucast_bytes; + uint64_t mcast_packets; + uint64_t mcast_bytes; + uint64_t bcast_packets; + uint64_t bcast_bytes; + uint64_t nobuffer; + uint64_t error; +} __packed; + +/* Interrupt moderation levels */ +#define UPT1_IMOD_NONE 0 /* No moderation */ +#define UPT1_IMOD_HIGHEST 7 /* Least interrupts */ +#define UPT1_IMOD_ADAPTIVE 8 /* Adaptive interrupt moderation */ + +/* Hardware features */ +#define UPT1_F_CSUM 0x0001 /* Rx checksum verification */ +#define UPT1_F_RSS 0x0002 /* Receive side scaling */ +#define UPT1_F_VLAN 0x0004 /* VLAN tag stripping */ +#define UPT1_F_LRO 0x0008 /* Large receive offloading */ + +#define VMXNET3_BAR0_IMASK(irq) (0x000 + (irq) * 8) /* Interrupt mask */ +#define VMXNET3_BAR0_TXH(q) (0x600 + (q) * 8) /* Tx head */ +#define VMXNET3_BAR0_RXH1(q) (0x800 + (q) * 8) /* Ring1 Rx head */ +#define VMXNET3_BAR0_RXH2(q) (0xA00 + (q) * 8) /* Ring2 Rx head */ +#define VMXNET3_BAR1_VRRS 0x000 /* VMXNET3 revision report selection */ +#define VMXNET3_BAR1_UVRS 0x008 /* UPT version report selection */ +#define VMXNET3_BAR1_DSL 0x010 /* Driver shared address low */ +#define VMXNET3_BAR1_DSH 0x018 /* Driver shared address high */ +#define VMXNET3_BAR1_CMD 0x020 /* Command */ +#define VMXNET3_BAR1_MACL 0x028 /* MAC address low */ +#define VMXNET3_BAR1_MACH 0x030 /* MAC address high */ +#define VMXNET3_BAR1_INTR 0x038 /* Interrupt status */ +#define VMXNET3_BAR1_EVENT 0x040 /* Event status */ + +#define VMXNET3_CMD_ENABLE 0xCAFE0000 /* Enable VMXNET3 */ +#define VMXNET3_CMD_DISABLE 0xCAFE0001 /* Disable VMXNET3 */ +#define VMXNET3_CMD_RESET 0xCAFE0002 /* Reset device */ +#define VMXNET3_CMD_SET_RXMODE 0xCAFE0003 /* Set interface flags */ +#define VMXNET3_CMD_SET_FILTER 0xCAFE0004 /* Set address filter */ +#define VMXNET3_CMD_VLAN_FILTER 0xCAFE0005 /* Set VLAN filter */ +#define VMXNET3_CMD_GET_STATUS 0xF00D0000 /* Get queue errors */ +#define VMXNET3_CMD_GET_STATS 0xF00D0001 /* Get queue statistics */ +#define VMXNET3_CMD_GET_LINK 0xF00D0002 /* Get link status */ +#define VMXNET3_CMD_GET_MACL 0xF00D0003 /* Get MAC address low */ +#define VMXNET3_CMD_GET_MACH 0xF00D0004 /* Get MAC address high */ +#define VMXNET3_CMD_GET_INTRCFG 0xF00D0008 /* Get interrupt config */ + +#define VMXNET3_DMADESC_ALIGN 128 +#define VMXNET3_INIT_GEN 1 + +struct vmxnet3_txdesc { + uint64_t addr; + + uint32_t len:14; + uint32_t gen:1; /* Generation */ + uint32_t pad1:1; + uint32_t dtype:1; /* Descriptor type */ + uint32_t pad2:1; + uint32_t offload_pos:14; /* Offloading position */ + + uint32_t hlen:10; /* Header len */ + uint32_t offload_mode:2; /* Offloading mode */ + uint32_t eop:1; /* End of packet */ + uint32_t compreq:1; /* Completion request */ + uint32_t pad3:1; + uint32_t vtag_mode:1; /* VLAN tag insertion mode */ + uint32_t vtag:16; /* VLAN tag */ +} __packed; + +/* Offloading modes */ +#define VMXNET3_OM_NONE 0 +#define VMXNET3_OM_CSUM 2 +#define VMXNET3_OM_TSO 3 + +struct vmxnet3_txcompdesc { + uint32_t eop_idx:12; /* EOP index in Tx ring */ + uint32_t pad1:20; + + uint32_t pad2:32; + uint32_t pad3:32; + + uint32_t rsvd:24; + uint32_t type:7; + uint32_t gen:1; +} __packed; + +struct vmxnet3_rxdesc { + uint64_t addr; + + uint32_t len:14; + uint32_t btype:1; /* Buffer type */ + uint32_t dtype:1; /* Descriptor type */ + uint32_t rsvd:15; + uint32_t gen:1; + + uint32_t pad1:32; +} __packed; + +/* Buffer types */ +#define VMXNET3_BTYPE_HEAD 0 /* Head only */ +#define VMXNET3_BTYPE_BODY 1 /* Body only */ + +struct vmxnet3_rxcompdesc { + uint32_t rxd_idx:12; /* Rx descriptor index */ + uint32_t pad1:2; + uint32_t eop:1; /* End of packet */ + uint32_t sop:1; /* Start of packet */ + uint32_t qid:10; + uint32_t rss_type:4; + uint32_t no_csum:1; /* No checksum calculated */ + uint32_t pad2:1; + + uint32_t rss_hash:32; /* RSS hash value */ + + uint32_t len:14; + uint32_t error:1; + uint32_t vlan:1; /* 802.1Q VLAN frame */ + uint32_t vtag:16; /* VLAN tag */ + + uint32_t csum:16; + uint32_t csum_ok:1; /* TCP/UDP checksum ok */ + uint32_t udp:1; + uint32_t tcp:1; + uint32_t ipcsum_ok:1; /* IP checksum OK */ + uint32_t ipv6:1; + uint32_t ipv4:1; + uint32_t fragment:1; /* IP fragment */ + uint32_t fcs:1; /* Frame CRC correct */ + uint32_t type:7; + uint32_t gen:1; +} __packed; + +#define VMXNET3_REV1_MAGIC 0XBABEFEE1 + +#define VMXNET3_GOS_UNKNOWN 0x00 +#define VMXNET3_GOS_LINUX 0x04 +#define VMXNET3_GOS_WINDOWS 0x08 +#define VMXNET3_GOS_SOLARIS 0x0C +#define VMXNET3_GOS_FREEBSD 0x10 +#define VMXNET3_GOS_PXE 0x14 + +#define VMXNET3_GOS_32BIT 0x01 +#define VMXNET3_GOS_64BIT 0x02 + +#define VMXNET3_MAX_TX_QUEUES 8 +#define VMXNET3_MAX_RX_QUEUES 16 +#define VMXNET3_MAX_INTRS \ + (VMXNET3_MAX_TX_QUEUES + VMXNET3_MAX_RX_QUEUES + 1) + +#define VMXNET3_ICTRL_DISABLE_ALL 0x01 + +#define VMXNET3_RXMODE_UCAST 0x01 +#define VMXNET3_RXMODE_MCAST 0x02 +#define VMXNET3_RXMODE_BCAST 0x04 +#define VMXNET3_RXMODE_ALLMULTI 0x08 +#define VMXNET3_RXMODE_PROMISC 0x10 + +#define VMXNET3_EVENT_RQERROR 0x01 +#define VMXNET3_EVENT_TQERROR 0x02 +#define VMXNET3_EVENT_LINK 0x04 +#define VMXNET3_EVENT_DIC 0x08 +#define VMXNET3_EVENT_DEBUG 0x10 + +#define VMXNET3_MIN_MTU 60 +#define VMXNET3_MAX_MTU 9000 + +/* Interrupt mask mode. */ +#define VMXNET3_IMM_AUTO 0x00 +#define VMXNET3_IMM_ACTIVE 0x01 +#define VMXNET3_IMM_LAZY 0x02 + +/* Interrupt type. */ +#define VMXNET3_IT_AUTO 0x00 +#define VMXNET3_IT_LEGACY 0x01 +#define VMXNET3_IT_MSI 0x02 +#define VMXNET3_IT_MSIX 0x03 + +struct vmxnet3_driver_shared { + uint32_t magic; + uint32_t pad1; + + /* Misc. control */ + uint32_t version; /* Driver version */ + uint32_t guest; /* Guest OS */ + uint32_t vmxnet3_revision; /* Supported VMXNET3 revision */ + uint32_t upt_version; /* Supported UPT version */ + uint64_t upt_features; + uint64_t driver_data; + uint64_t queue_shared; + uint32_t driver_data_len; + uint32_t queue_shared_len; + uint32_t mtu; + uint16_t nrxsg_max; + uint8_t ntxqueue; + uint8_t nrxqueue; + uint32_t reserved1[4]; + + /* Interrupt control */ + uint8_t automask; + uint8_t nintr; + uint8_t evintr; + uint8_t modlevel[VMXNET3_MAX_INTRS]; + uint32_t ictrl; + uint32_t reserved2[2]; + + /* Receive filter parameters */ + uint32_t rxmode; + uint16_t mcast_tablelen; + uint16_t pad2; + uint64_t mcast_table; + uint32_t vlan_filter[4096 / 32]; + + struct { + uint32_t version; + uint32_t len; + uint64_t paddr; + } rss, pm, plugin; + + uint32_t event; + uint32_t reserved3[5]; +} __packed; + +struct vmxnet3_txq_shared { + /* Control */ + uint32_t npending; + uint32_t intr_threshold; + uint64_t reserved1; + + /* Config */ + uint64_t cmd_ring; + uint64_t data_ring; + uint64_t comp_ring; + uint64_t driver_data; + uint64_t reserved2; + uint32_t cmd_ring_len; + uint32_t data_ring_len; + uint32_t comp_ring_len; + uint32_t driver_data_len; + uint8_t intr_idx; + uint8_t pad1[7]; + + /* Queue status */ + uint8_t stopped; + uint8_t pad2[3]; + uint32_t error; + + struct UPT1_TxStats stats; + + uint8_t pad3[88]; +} __packed; + +struct vmxnet3_rxq_shared { + uint8_t update_rxhead; + uint8_t pad1[7]; + uint64_t reserved1; + + uint64_t cmd_ring[2]; + uint64_t comp_ring; + uint64_t driver_data; + uint64_t reserved2; + uint32_t cmd_ring_len[2]; + uint32_t comp_ring_len; + uint32_t driver_data_len; + uint8_t intr_idx; + uint8_t pad2[7]; + + uint8_t stopped; + uint8_t pad3[3]; + uint32_t error; + + struct UPT1_RxStats stats; + + uint8_t pad4[88]; +} __packed; + +#endif /* _IF_VMXREG_H */ diff --git a/sys/dev/vmware/vmxnet3/if_vmxvar.h b/sys/dev/vmware/vmxnet3/if_vmxvar.h new file mode 100644 index 00000000000..2e7d02d316b --- /dev/null +++ b/sys/dev/vmware/vmxnet3/if_vmxvar.h @@ -0,0 +1,326 @@ +/*- + * Copyright (c) 2013 Tsubai Masanari + * Copyright (c) 2013 Bryan Venteicher + * + * 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. + * + * $FreeBSD$ + */ + +#ifndef _IF_VMXVAR_H +#define _IF_VMXVAR_H + +struct vmxnet3_softc; + +struct vmxnet3_dma_alloc { + bus_addr_t dma_paddr; + caddr_t dma_vaddr; + bus_dma_tag_t dma_tag; + bus_dmamap_t dma_map; + bus_size_t dma_size; +}; + +/* + * The number of Rx/Tx queues this driver supports. + */ +#define VMXNET3_RX_QUEUES 1 +#define VMXNET3_TX_QUEUES 1 + +/* + * The number of Rx rings in each Rx queue. + */ +#define VMXNET3_RXRINGS_PERQ 2 + +/* + * The number of descriptors in each Rx/Tx ring. + */ +#define VMXNET3_DEF_TX_NDESC 512 +#define VMXNET3_MAX_TX_NDESC 4096 +#define VMXNET3_MIN_TX_NDESC 32 +#define VMXNET3_MASK_TX_NDESC 0x1F +#define VMXNET3_DEF_RX_NDESC 256 +#define VMXNET3_MAX_RX_NDESC 2048 +#define VMXNET3_MIN_RX_NDESC 32 +#define VMXNET3_MASK_RX_NDESC 0x1F + +#define VMXNET3_MAX_TX_NCOMPDESC VMXNET3_MAX_TX_NDESC +#define VMXNET3_MAX_RX_NCOMPDESC \ + (VMXNET3_MAX_RX_NDESC * VMXNET3_RXRINGS_PERQ) + +struct vmxnet3_txbuf { + bus_dmamap_t vtxb_dmamap; + struct mbuf *vtxb_m; +}; + +struct vmxnet3_txring { + struct vmxnet3_txbuf *vxtxr_txbuf; + u_int vxtxr_head; + u_int vxtxr_next; + u_int vxtxr_ndesc; + int vxtxr_gen; + bus_dma_tag_t vxtxr_txtag; + struct vmxnet3_txdesc *vxtxr_txd; + struct vmxnet3_dma_alloc vxtxr_dma; +}; + +static inline int +VMXNET3_TXRING_AVAIL(struct vmxnet3_txring *txr) +{ + int avail = txr->vxtxr_next - txr->vxtxr_head - 1; + return (avail < 0 ? txr->vxtxr_ndesc + avail : avail); +} + +struct vmxnet3_rxbuf { + bus_dmamap_t vrxb_dmamap; + struct mbuf *vrxb_m; +}; + +struct vmxnet3_rxring { + struct vmxnet3_rxbuf *vxrxr_rxbuf; + struct vmxnet3_rxdesc *vxrxr_rxd; + u_int vxrxr_fill; + u_int vxrxr_ndesc; + int vxrxr_gen; + int vxrxr_rid; + bus_dma_tag_t vxrxr_rxtag; + struct vmxnet3_dma_alloc vxrxr_dma; + bus_dmamap_t vxrxr_spare_dmap; +}; + +static inline void +vmxnet3_rxr_increment_fill(struct vmxnet3_rxring *rxr) +{ + + if (++rxr->vxrxr_fill == rxr->vxrxr_ndesc) { + rxr->vxrxr_fill = 0; + rxr->vxrxr_gen ^= 1; + } +} + +struct vmxnet3_comp_ring { + union { + struct vmxnet3_txcompdesc *txcd; + struct vmxnet3_rxcompdesc *rxcd; + } vxcr_u; + u_int vxcr_next; + u_int vxcr_ndesc; + int vxcr_gen; + struct vmxnet3_dma_alloc vxcr_dma; +}; + +struct vmxnet3_txq_stats { + uint64_t vtxrs_full; + uint64_t vtxrs_offload_failed; +}; + +struct vmxnet3_txqueue { + struct mtx vxtxq_mtx; + struct vmxnet3_softc *vxtxq_sc; + int vxtxq_id; + int vxtxq_intr_idx; + int vxtxq_watchdog; + struct vmxnet3_txring vxtxq_cmd_ring; + struct vmxnet3_comp_ring vxtxq_comp_ring; + struct vmxnet3_txq_stats vxtxq_stats; + struct vmxnet3_txq_shared *vxtxq_ts; + struct sysctl_oid_list *vxtxq_sysctl; + char vxtxq_name[16]; +}; + +#define VMXNET3_TXQ_LOCK(_txq) mtx_lock(&(_txq)->vxtxq_mtx) +#define VMXNET3_TXQ_TRYLOCK(_txq) mtx_trylock(&(_txq)->vxtxq_mtx) +#define VMXNET3_TXQ_UNLOCK(_txq) mtx_unlock(&(_txq)->vxtxq_mtx) +#define VMXNET3_TXQ_LOCK_ASSERT(_txq) \ + mtx_assert(&(_txq)->vxtxq_mtx, MA_OWNED) +#define VMXNET3_TXQ_LOCK_ASSERT_NOTOWNED(_txq) \ + mtx_assert(&(_txq)->vxtxq_mtx, MA_NOTOWNED) + +struct vmxnet3_rxq_stats { + +}; + +struct vmxnet3_rxqueue { + struct mtx vxrxq_mtx; + struct vmxnet3_softc *vxrxq_sc; + int vxrxq_id; + int vxrxq_intr_idx; + struct vmxnet3_rxring vxrxq_cmd_ring[VMXNET3_RXRINGS_PERQ]; + struct vmxnet3_comp_ring vxrxq_comp_ring; + struct vmxnet3_rxq_stats vxrxq_stats; + struct vmxnet3_rxq_shared *vxrxq_rs; + struct sysctl_oid_list *vxrxq_sysctl; + char vxrxq_name[16]; +}; + +#define VMXNET3_RXQ_LOCK(_rxq) mtx_lock(&(_rxq)->vxrxq_mtx) +#define VMXNET3_RXQ_UNLOCK(_rxq) mtx_unlock(&(_rxq)->vxrxq_mtx) +#define VMXNET3_RXQ_LOCK_ASSERT(_rxq) \ + mtx_assert(&(_rxq)->vxrxq_mtx, MA_OWNED) +#define VMXNET3_RXQ_LOCK_ASSERT_NOTOWNED(_rxq) \ + mtx_assert(&(_rxq)->vxrxq_mtx, MA_NOTOWNED) + +struct vmxnet3_statistics { + uint32_t vmst_collapsed; + uint32_t vmst_mgetcl_failed; + uint32_t vmst_mbuf_load_failed; + +}; + +struct vmxnet3_interrupt { + struct resource *vmxi_irq; + int vmxi_rid; + void *vmxi_handler; +}; + +struct vmxnet3_softc { + device_t vmx_dev; + struct ifnet *vmx_ifp; + struct vmxnet3_driver_shared *vmx_ds; + uint32_t vmx_flags; +#define VMXNET3_FLAG_NO_MSIX 0x0001 + + struct vmxnet3_rxqueue *vmx_rxq; + struct vmxnet3_txqueue *vmx_txq; + + struct resource *vmx_res0; + bus_space_tag_t vmx_iot0; + bus_space_handle_t vmx_ioh0; + struct resource *vmx_res1; + bus_space_tag_t vmx_iot1; + bus_space_handle_t vmx_ioh1; + struct resource *vmx_msix_res; + + int vmx_link_active; + int vmx_link_speed; + int vmx_if_flags; + int vmx_ntxqueues; + int vmx_nrxqueues; + int vmx_ntxdescs; + int vmx_nrxdescs; + int vmx_max_rxsegs; + int vmx_rx_max_chain; + + struct vmxnet3_statistics vmx_stats; + + int vmx_intr_type; + int vmx_intr_mask_mode; + int vmx_event_intr_idx; + int vmx_nintrs; + struct vmxnet3_interrupt vmx_intrs[VMXNET3_MAX_INTRS]; + + struct mtx vmx_mtx; + uint8_t *vmx_mcast; + void *vmx_qs; + struct callout vmx_tick; + struct vmxnet3_dma_alloc vmx_ds_dma; + struct vmxnet3_dma_alloc vmx_qs_dma; + struct vmxnet3_dma_alloc vmx_mcast_dma; + struct ifmedia vmx_media; + eventhandler_tag vmx_vlan_attach; + eventhandler_tag vmx_vlan_detach; + uint32_t vmx_vlan_filter[4096/32]; + uint8_t vmx_lladdr[ETHER_ADDR_LEN]; +}; + +#define VMXNET3_CORE_LOCK_INIT(_sc, _name) \ + mtx_init(&(_sc)->vmx_mtx, _name, "VMXNET3 Lock", MTX_DEF) +#define VMXNET3_CORE_LOCK_DESTROY(_sc) mtx_destroy(&(_sc)->vmx_mtx) +#define VMXNET3_CORE_LOCK(_sc) mtx_lock(&(_sc)->vmx_mtx) +#define VMXNET3_CORE_UNLOCK(_sc) mtx_unlock(&(_sc)->vmx_mtx) +#define VMXNET3_CORE_LOCK_ASSERT(_sc) mtx_assert(&(_sc)->vmx_mtx, MA_OWNED) +#define VMXNET3_CORE_LOCK_ASSERT_NOTOWNED(_sc) \ + mtx_assert(&(_sc)->vmx_mtx, MA_NOTOWNED) + +/* + * Our driver version we report to the hypervisor; we just keep + * this value constant. + */ +#define VMXNET3_DRIVER_VERSION 0x00010000 + +/* + * Convert the FreeBSD version in to something the hypervisor + * understands. This is apparently what VMware's driver reports + * so mimic it even though it probably is not required. + */ +#define VMXNET3_GUEST_OS_VERSION \ + (((__FreeBSD_version / 100000) << 14) | \ + (((__FreeBSD_version / 1000) % 100) << 6 ) | \ + (((__FreeBSD_version / 100) % 10) << 30) | \ + ((__FreeBSD_version % 100) << 22)) + +/* + * Max descriptors per Tx packet. We must limit the size of the + * any TSO packets based on the number of segments. + */ +#define VMXNET3_TX_MAXSEGS 32 +#define VMXNET3_TSO_MAXSIZE 65550 + +/* + * Maximum support Tx segments size. The length field in the + * Tx descriptor is 14 bits. + */ +#define VMXNET3_TX_MAXSEGSIZE (1 << 14) + +/* + * The maximum number of Rx segments we accept. When LRO is enabled, + * this allows us to receive the maximum sized frame with one MCLBYTES + * cluster followed by 16 MJUMPAGESIZE clusters. + */ +#define VMXNET3_MAX_RX_SEGS 17 + +/* + * Predetermined size of the multicast MACs filter table. If the + * number of multicast addresses exceeds this size, then the + * ALL_MULTI mode is use instead. + */ +#define VMXNET3_MULTICAST_MAX 32 + +/* + * Our Tx watchdog timeout. + */ +#define VMXNET3_WATCHDOG_TIMEOUT 5 + +/* + * IP protocols that we can perform Tx checksum offloading of. + */ +#define VMXNET3_CSUM_OFFLOAD (CSUM_TCP | CSUM_UDP) +#define VMXNET3_CSUM_OFFLOAD_IPV6 (CSUM_TCP_IPV6 | CSUM_UDP_IPV6) + +#define VMXNET3_CSUM_ALL_OFFLOAD \ + (VMXNET3_CSUM_OFFLOAD | VMXNET3_CSUM_OFFLOAD_IPV6 | CSUM_TSO) + +/* + * Compat macros to keep this driver compiling on old releases. + */ + +#if !defined(SYSCTL_ADD_UQUAD) +#define SYSCTL_ADD_UQUAD SYSCTL_ADD_QUAD +#endif + +#if !defined(IFCAP_TXCSUM_IPV6) +#define IFCAP_TXCSUM_IPV6 0 +#endif + +#if !defined(IFCAP_RXCSUM_IPV6) +#define IFCAP_RXCSUM_IPV6 0 +#endif + +#if !defined(CSUM_TCP_IPV6) +#define CSUM_TCP_IPV6 0 +#endif + +#if !defined(CSUM_UDP_IPV6) +#define CSUM_UDP_IPV6 0 +#endif + +#endif /* _IF_VMXVAR_H */ diff --git a/sys/dev/wb/if_wb.c b/sys/dev/wb/if_wb.c index 8bc28eabcf0..f96441c5e4d 100644 --- a/sys/dev/wb/if_wb.c +++ b/sys/dev/wb/if_wb.c @@ -142,7 +142,7 @@ static int wb_probe(device_t); static int wb_attach(device_t); static int wb_detach(device_t); -static void wb_bfree(void *addr, void *args); +static int wb_bfree(struct mbuf *, void *addr, void *args); static int wb_newbuf(struct wb_softc *, struct wb_chain_onefrag *, struct mbuf *); static int wb_encap(struct wb_softc *, struct wb_chain *, struct mbuf *); @@ -822,12 +822,11 @@ wb_list_rx_init(sc) return(0); } -static void -wb_bfree(buf, args) - void *buf; - void *args; +static int +wb_bfree(struct mbuf *m, void *buf, void *args) { + return (EXT_FREE_OK); } /* diff --git a/sys/dev/wi/if_wi_pci.c b/sys/dev/wi/if_wi_pci.c index 958ba7ef497..30cdbc40ed9 100644 --- a/sys/dev/wi/if_wi_pci.c +++ b/sys/dev/wi/if_wi_pci.c @@ -137,23 +137,13 @@ static int wi_pci_attach(device_t dev) { struct wi_softc *sc; - u_int32_t command, wanted; + u_int32_t command; u_int16_t reg; int error; int timeout; sc = device_get_softc(dev); - command = pci_read_config(dev, PCIR_COMMAND, 4); - wanted = PCIM_CMD_PORTEN|PCIM_CMD_MEMEN; - command |= wanted; - pci_write_config(dev, PCIR_COMMAND, command, 4); - command = pci_read_config(dev, PCIR_COMMAND, 4); - if ((command & wanted) != wanted) { - device_printf(dev, "wi_pci_attach() failed to enable pci!\n"); - return (ENXIO); - } - if (sc->wi_bus_type != WI_BUS_PCI_NATIVE) { error = wi_alloc(dev, WI_PCI_IORES); if (error) diff --git a/sys/dev/xen/balloon/balloon.c b/sys/dev/xen/balloon/balloon.c index d4f94ab9ee0..9021abb1172 100644 --- a/sys/dev/xen/balloon/balloon.c +++ b/sys/dev/xen/balloon/balloon.c @@ -40,15 +40,16 @@ __FBSDID("$FreeBSD$"); #include #include -#include -#include -#include -#include -#include - #include #include +#include +#include +#include +#include + +#include + static MALLOC_DEFINE(M_BALLOON, "Balloon", "Xen Balloon Driver"); struct mtx balloon_mutex; diff --git a/sys/dev/xen/blkback/blkback.c b/sys/dev/xen/blkback/blkback.c index 9c92309d324..21fbb41f77f 100644 --- a/sys/dev/xen/blkback/blkback.c +++ b/sys/dev/xen/blkback/blkback.c @@ -70,14 +70,13 @@ __FBSDID("$FreeBSD$"); #include #include -#include #include #include #include +#include #include -#include #include #include @@ -231,7 +230,7 @@ struct xbb_xen_reqlist { int num_children; /** - * Number of I/O requests dispatched to the backend. + * Number of I/O requests still pending on the backend. */ int pendcnt; @@ -327,13 +326,6 @@ struct xbb_xen_req { */ int nr_512b_sectors; - /** - * The number of struct bio requests still outstanding for this - * request on the backend device. This field is only used for - * device (rather than file) backed I/O. - */ - int pendcnt; - /** * BLKIF_OP code for this request. */ @@ -682,7 +674,7 @@ struct xbb_softc { blkif_back_rings_t rings; /** IRQ mapping for the communication ring event channel. */ - int irq; + xen_intr_handle_t xen_intr_handle; /** * \brief Backend access mode flags (e.g. write, or read-only). @@ -1240,6 +1232,8 @@ xbb_get_resources(struct xbb_softc *xbb, struct xbb_xen_reqlist **reqlist, nreq->reqlist = *reqlist; nreq->req_ring_idx = ring_idx; + nreq->id = ring_req->id; + nreq->operation = ring_req->operation; if (xbb->abi != BLKIF_PROTOCOL_NATIVE) { bcopy(ring_req, &nreq->ring_req_storage, sizeof(*ring_req)); @@ -1347,7 +1341,7 @@ xbb_send_response(struct xbb_softc *xbb, struct xbb_xen_req *req, int status) taskqueue_enqueue(xbb->io_taskqueue, &xbb->io_task); if (notify) - notify_remote_via_irq(xbb->irq); + xen_intr_signal(xbb->xen_intr_handle); } /** @@ -1609,15 +1603,14 @@ xbb_dispatch_io(struct xbb_softc *xbb, struct xbb_xen_reqlist *reqlist) req_ring_idx = nreq->req_ring_idx; nr_sects = 0; nseg = ring_req->nr_segments; - nreq->id = ring_req->id; nreq->nr_pages = nseg; nreq->nr_512b_sectors = 0; req_seg_idx = 0; sg = NULL; /* Check that number of segments is sane. */ - if (unlikely(nseg == 0) - || unlikely(nseg > xbb->max_request_segments)) { + if (__predict_false(nseg == 0) + || __predict_false(nseg > xbb->max_request_segments)) { DPRINTF("Bad number of segments in request (%d)\n", nseg); reqlist->status = BLKIF_RSP_ERROR; @@ -1734,7 +1727,7 @@ xbb_dispatch_io(struct xbb_softc *xbb, struct xbb_xen_reqlist *reqlist) for (seg_idx = 0, map = xbb->maps; seg_idx < reqlist->nr_segments; seg_idx++, map++){ - if (unlikely(map->status != 0)) { + if (__predict_false(map->status != 0)) { DPRINTF("invalid buffer -- could not remap " "it (%d)\n", map->status); DPRINTF("Mapping(%d): Host Addr 0x%lx, flags " @@ -2026,14 +2019,16 @@ xbb_run_queue(void *context, int pending) * \param arg Callback argument registerd during event channel * binding - the xbb_softc for this instance. */ -static void -xbb_intr(void *arg) +static int +xbb_filter(void *arg) { struct xbb_softc *xbb; - /* Defer to kernel thread. */ + /* Defer to taskqueue thread. */ xbb = (struct xbb_softc *)arg; taskqueue_enqueue(xbb->io_taskqueue, &xbb->io_task); + + return (FILTER_HANDLED); } SDT_PROVIDER_DEFINE(xbb); @@ -2061,7 +2056,6 @@ xbb_dispatch_dev(struct xbb_softc *xbb, struct xbb_xen_reqlist *reqlist, { struct xbb_dev_data *dev_data; struct bio *bios[XBB_MAX_SEGMENTS_PER_REQLIST]; - struct xbb_xen_req *nreq; off_t bio_offset; struct bio *bio; struct xbb_sg *xbb_sg; @@ -2079,9 +2073,8 @@ xbb_dispatch_dev(struct xbb_softc *xbb, struct xbb_xen_reqlist *reqlist, bio_idx = 0; if (operation == BIO_FLUSH) { - nreq = STAILQ_FIRST(&reqlist->contig_req_list); bio = g_new_bio(); - if (unlikely(bio == NULL)) { + if (__predict_false(bio == NULL)) { DPRINTF("Unable to allocate bio for BIO_FLUSH\n"); error = ENOMEM; return (error); @@ -2093,10 +2086,10 @@ xbb_dispatch_dev(struct xbb_softc *xbb, struct xbb_xen_reqlist *reqlist, bio->bio_offset = 0; bio->bio_data = 0; bio->bio_done = xbb_bio_done; - bio->bio_caller1 = nreq; + bio->bio_caller1 = reqlist; bio->bio_pblkno = 0; - nreq->pendcnt = 1; + reqlist->pendcnt = 1; SDT_PROBE1(xbb, kernel, xbb_dispatch_dev, flush, device_get_unit(xbb->dev)); @@ -2143,7 +2136,7 @@ xbb_dispatch_dev(struct xbb_softc *xbb, struct xbb_xen_reqlist *reqlist, } bio = bios[nbio++] = g_new_bio(); - if (unlikely(bio == NULL)) { + if (__predict_false(bio == NULL)) { error = ENOMEM; goto fail_free_bios; } @@ -2775,7 +2768,7 @@ xbb_free_communication_mem(struct xbb_softc *xbb) { if (xbb->kva != 0) { #ifndef XENHVM - kmem_free(kernel_map, xbb->kva, xbb->kva_size); + kva_free(xbb->kva, xbb->kva_size); #else if (xbb->pseudo_phys_res != NULL) { bus_release_resource(xbb->dev, SYS_RES_MEMORY, @@ -2811,10 +2804,7 @@ xbb_disconnect(struct xbb_softc *xbb) if ((xbb->flags & XBBF_RING_CONNECTED) == 0) return (0); - if (xbb->irq != 0) { - unbind_from_irqhandler(xbb->irq); - xbb->irq = 0; - } + xen_intr_unbind(&xbb->xen_intr_handle); mtx_unlock(&xbb->lock); taskqueue_drain(xbb->io_taskqueue, &xbb->io_task); @@ -2966,13 +2956,14 @@ xbb_connect_ring(struct xbb_softc *xbb) xbb->flags |= XBBF_RING_CONNECTED; - error = - bind_interdomain_evtchn_to_irqhandler(xbb->otherend_id, - xbb->ring_config.evtchn, - device_get_nameunit(xbb->dev), - xbb_intr, /*arg*/xbb, - INTR_TYPE_BIO | INTR_MPSAFE, - &xbb->irq); + error = xen_intr_bind_remote_port(xbb->dev, + xbb->otherend_id, + xbb->ring_config.evtchn, + xbb_filter, + /*ithread_handler*/NULL, + /*arg*/xbb, + INTR_TYPE_BIO | INTR_MPSAFE, + &xbb->xen_intr_handle); if (error) { (void)xbb_disconnect(xbb); xenbus_dev_fatal(xbb->dev, error, "binding event channel"); @@ -3014,7 +3005,7 @@ xbb_alloc_communication_mem(struct xbb_softc *xbb) device_get_nameunit(xbb->dev), xbb->kva_size, xbb->reqlist_kva_size); #ifndef XENHVM - xbb->kva = kmem_alloc_nofault(kernel_map, xbb->kva_size); + xbb->kva = kva_alloc(xbb->kva_size); if (xbb->kva == 0) return (ENOMEM); xbb->gnt_base_addr = xbb->kva; @@ -3791,9 +3782,10 @@ xbb_attach(device_t dev) * Create a taskqueue for doing work that must occur from a * thread context. */ - xbb->io_taskqueue = taskqueue_create(device_get_nameunit(dev), M_NOWAIT, - taskqueue_thread_enqueue, - /*context*/&xbb->io_taskqueue); + xbb->io_taskqueue = taskqueue_create_fast(device_get_nameunit(dev), + M_NOWAIT, + taskqueue_thread_enqueue, + /*contxt*/&xbb->io_taskqueue); if (xbb->io_taskqueue == NULL) { xbb_attach_failed(xbb, error, "Unable to create taskqueue"); return (ENOMEM); diff --git a/sys/dev/xen/blkfront/blkfront.c b/sys/dev/xen/blkfront/blkfront.c index 9f1b08297f3..feb79a1eb86 100644 --- a/sys/dev/xen/blkfront/blkfront.c +++ b/sys/dev/xen/blkfront/blkfront.c @@ -51,19 +51,17 @@ __FBSDID("$FreeBSD$"); #include #include -#include -#include -#include -#include - +#include #include #include -#include #include #include #include #include +#include +#include + #include #include @@ -139,7 +137,7 @@ xbd_flush_requests(struct xbd_softc *sc) RING_PUSH_REQUESTS_AND_CHECK_NOTIFY(&sc->xbd_ring, notify); if (notify) - notify_remote_via_irq(sc->xbd_irq); + xen_intr_signal(sc->xen_intr_handle); } static void @@ -310,7 +308,7 @@ xbd_bio_command(struct xbd_softc *sc) struct xbd_command *cm; struct bio *bp; - if (unlikely(sc->xbd_state != XBD_STATE_CONNECTED)) + if (__predict_false(sc->xbd_state != XBD_STATE_CONNECTED)) return (NULL); bp = xbd_dequeue_bio(sc); @@ -437,7 +435,7 @@ xbd_bio_complete(struct xbd_softc *sc, struct xbd_command *cm) bp = cm->cm_bp; - if (unlikely(cm->cm_status != BLKIF_RSP_OKAY)) { + if (__predict_false(cm->cm_status != BLKIF_RSP_OKAY)) { disk_err(bp, "disk error" , -1, 0); printf(" status: %x\n", cm->cm_status); bp->bio_flags |= BIO_ERROR; @@ -470,7 +468,7 @@ xbd_int(void *xsc) mtx_lock(&sc->xbd_io_lock); - if (unlikely(sc->xbd_state == XBD_STATE_DISCONNECTED)) { + if (__predict_false(sc->xbd_state == XBD_STATE_DISCONNECTED)) { mtx_unlock(&sc->xbd_io_lock); return; } @@ -531,7 +529,7 @@ xbd_int(void *xsc) xbd_startio(sc); - if (unlikely(sc->xbd_state == XBD_STATE_SUSPENDED)) + if (__predict_false(sc->xbd_state == XBD_STATE_SUSPENDED)) wakeup(&sc->xbd_cm_q[XBD_Q_BUSY]); mtx_unlock(&sc->xbd_io_lock); @@ -782,13 +780,12 @@ xbd_alloc_ring(struct xbd_softc *sc) } } - error = bind_listening_port_to_irqhandler( - xenbus_get_otherend_id(sc->xbd_dev), - "xbd", (driver_intr_t *)xbd_int, sc, - INTR_TYPE_BIO | INTR_MPSAFE, &sc->xbd_irq); + error = xen_intr_alloc_and_bind_local_port(sc->xbd_dev, + xenbus_get_otherend_id(sc->xbd_dev), NULL, xbd_int, sc, + INTR_TYPE_BIO | INTR_MPSAFE, &sc->xen_intr_handle); if (error) { xenbus_dev_fatal(sc->xbd_dev, error, - "bind_evtchn_to_irqhandler failed"); + "xen_intr_alloc_and_bind_local_port failed"); return (error); } @@ -877,10 +874,6 @@ xbd_setup_sysctl(struct xbd_softc *xbd) "max_requests", CTLFLAG_RD, &xbd->xbd_max_requests, -1, "maximum outstanding requests (negotiated)"); - SYSCTL_ADD_UINT(sysctl_ctx, children, OID_AUTO, - "max_requests", CTLFLAG_RD, &xbd->xbd_max_requests, -1, - "maximum outstanding requests (negotiated)"); - SYSCTL_ADD_UINT(sysctl_ctx, children, OID_AUTO, "max_request_segments", CTLFLAG_RD, &xbd->xbd_max_request_segments, 0, @@ -1046,10 +1039,8 @@ xbd_free(struct xbd_softc *sc) xbd_initq_cm(sc, XBD_Q_COMPLETE); } - if (sc->xbd_irq) { - unbind_from_irqhandler(sc->xbd_irq); - sc->xbd_irq = 0; - } + xen_intr_unbind(&sc->xen_intr_handle); + } /*--------------------------- State Change Handlers --------------------------*/ @@ -1281,7 +1272,7 @@ xbd_initialize(struct xbd_softc *sc) } error = xs_printf(XST_NIL, node_path, "event-channel", - "%u", irq_to_evtchn_port(sc->xbd_irq)); + "%u", xen_intr_port(sc->xen_intr_handle)); if (error) { xenbus_dev_fatal(sc->xbd_dev, error, "writing %s/event-channel", @@ -1418,6 +1409,9 @@ xbd_attach(device_t dev) /* FIXME: Use dynamic device id if this is not set. */ error = xs_scanf(XST_NIL, xenbus_get_node(dev), "virtual-device", NULL, "%" PRIu32, &vdevice); + if (error) + error = xs_scanf(XST_NIL, xenbus_get_node(dev), + "virtual-device-ext", NULL, "%" PRIu32, &vdevice); if (error) { xenbus_dev_fatal(dev, error, "reading virtual-device"); device_printf(dev, "Couldn't determine virtual device.\n"); diff --git a/sys/dev/xen/blkfront/block.h b/sys/dev/xen/blkfront/block.h index 0f7d6cb124d..9c803bc2eac 100644 --- a/sys/dev/xen/blkfront/block.h +++ b/sys/dev/xen/blkfront/block.h @@ -179,7 +179,7 @@ struct xbd_softc { uint32_t xbd_max_request_size; grant_ref_t xbd_ring_ref[XBD_MAX_RING_PAGES]; blkif_front_ring_t xbd_ring; - unsigned int xbd_irq; + xen_intr_handle_t xen_intr_handle; struct gnttab_free_callback xbd_callback; xbd_cm_q_t xbd_cm_q[XBD_Q_COUNT]; bus_dma_tag_t xbd_io_dmat; diff --git a/sys/dev/xen/console/console.c b/sys/dev/xen/console/console.c index 6281bf2165e..65a0e7dad96 100644 --- a/sys/dev/xen/console/console.c +++ b/sys/dev/xen/console/console.c @@ -15,7 +15,7 @@ __FBSDID("$FreeBSD$"); #include #include #include -#include +#include #include #include #include @@ -71,6 +71,8 @@ static char rbuf[RBUF_SIZE]; static int rc, rp; static unsigned int cnsl_evt_reg; static unsigned int wc, wp; /* write_cons, write_prod */ +xen_intr_handle_t xen_intr_handle; +device_t xencons_dev; #ifdef KDB static int xc_altbrk; @@ -232,6 +234,7 @@ xc_attach(device_t dev) { int error; + xencons_dev = dev; xccons = tty_alloc(&xc_ttydevsw, NULL); tty_makedev(xccons, NULL, "xc%r", 0); @@ -243,15 +246,10 @@ xc_attach(device_t dev) callout_reset(&xc_callout, XC_POLLTIME, xc_timeout, xccons); if (xen_start_info->flags & SIF_INITDOMAIN) { - error = bind_virq_to_irqhandler( - VIRQ_CONSOLE, - 0, - "console", - NULL, - xencons_priv_interrupt, NULL, - INTR_TYPE_TTY, NULL); - - KASSERT(error >= 0, ("can't register console interrupt")); + error = xen_intr_bind_virq(dev, VIRQ_CONSOLE, 0, NULL, + xencons_priv_interrupt, NULL, + INTR_TYPE_TTY, &xen_intr_handle); + KASSERT(error >= 0, ("can't register console interrupt")); } /* register handler to flush console on shutdown */ diff --git a/sys/dev/xen/console/xencons_ring.c b/sys/dev/xen/console/xencons_ring.c index 077d286f3fe..3701551ea10 100644 --- a/sys/dev/xen/console/xencons_ring.c +++ b/sys/dev/xen/console/xencons_ring.c @@ -16,7 +16,8 @@ __FBSDID("$FreeBSD$"); #include #include -#include + +#include #include #include #include @@ -30,9 +31,10 @@ __FBSDID("$FreeBSD$"); #include #define console_evtchn console.domU.evtchn -static unsigned int console_irq; +xen_intr_handle_t console_handle; extern char *console_page; extern struct mtx cn_mtx; +extern device_t xencons_dev; static inline struct xencons_interface * xencons_interface(void) @@ -74,7 +76,7 @@ xencons_ring_send(const char *data, unsigned len) wmb(); intf->out_prod = prod; - notify_remote_via_evtchn(xen_start_info->console_evtchn); + xen_intr_signal(console_handle); return sent; @@ -106,7 +108,7 @@ xencons_handle_input(void *unused) intf->in_cons = cons; CN_LOCK(cn_mtx); - notify_remote_via_evtchn(xen_start_info->console_evtchn); + xen_intr_signal(console_handle); xencons_tx(); CN_UNLOCK(cn_mtx); @@ -126,9 +128,9 @@ xencons_ring_init(void) if (!xen_start_info->console_evtchn) return 0; - err = bind_caller_port_to_irqhandler(xen_start_info->console_evtchn, - "xencons", xencons_handle_input, NULL, - INTR_TYPE_MISC | INTR_MPSAFE, &console_irq); + err = xen_intr_bind_local_port(xencons_dev, + xen_start_info->console_evtchn, NULL, xencons_handle_input, NULL, + INTR_TYPE_MISC | INTR_MPSAFE, &console_handle); if (err) { return err; } @@ -146,7 +148,7 @@ xencons_suspend(void) if (!xen_start_info->console_evtchn) return; - unbind_from_irqhandler(console_irq); + xen_intr_unbind(&console_handle); } void diff --git a/sys/dev/xen/control/control.c b/sys/dev/xen/control/control.c index 18f42bbe653..649f2816040 100644 --- a/sys/dev/xen/control/control.c +++ b/sys/dev/xen/control/control.c @@ -128,12 +128,13 @@ __FBSDID("$FreeBSD$"); #include #include -#include +#include #include #include #include +#include #include #include #include @@ -144,6 +145,9 @@ __FBSDID("$FreeBSD$"); #include +#include +#include + /*--------------------------- Forward Declarations --------------------------*/ /** Function signature for shutdown event handlers. */ typedef void (xctrl_shutdown_handler_t)(void); @@ -242,6 +246,7 @@ xctrl_suspend() xencons_suspend(); gnttab_suspend(); + intr_suspend(); max_pfn = HYPERVISOR_shared_info->arch.max_pfn; @@ -282,7 +287,7 @@ xctrl_suspend() HYPERVISOR_shared_info->arch.max_pfn = max_pfn; gnttab_resume(); - irq_resume(); + intr_resume(); local_irq_enable(); xencons_resume(); @@ -352,13 +357,11 @@ xctrl_suspend() * Prevent any races with evtchn_interrupt() handler. */ disable_intr(); - irq_suspend(); + intr_suspend(); suspend_cancelled = HYPERVISOR_suspend(0); - if (suspend_cancelled) - irq_resume(); - else - xenpci_resume(); + + intr_resume(); /* * Re-enable interrupts and put the scheduler back to normal. diff --git a/sys/dev/xen/netback/netback.c b/sys/dev/xen/netback/netback.c index 4c78113106e..8f0286c31f0 100644 --- a/sys/dev/xen/netback/netback.c +++ b/sys/dev/xen/netback/netback.c @@ -79,14 +79,15 @@ __FBSDID("$FreeBSD$"); #include #include -#include -#include -#include +#include +#include #include #include #include +#include + /*--------------------------- Compile-time Tunables --------------------------*/ /*---------------------------------- Macros ----------------------------------*/ @@ -433,8 +434,8 @@ struct xnb_softc { /** Xen device handle.*/ long handle; - /** IRQ mapping for the communication ring event channel. */ - int irq; + /** Handle to the communication ring event channel. */ + xen_intr_handle_t xen_intr_handle; /** * \brief Cached value of the front-end's domain id. @@ -587,14 +588,14 @@ xnb_dump_mbuf(const struct mbuf *m) if (m->m_flags & M_PKTHDR) { printf(" flowid=%10d, csum_flags=%#8x, csum_data=%#8x, " "tso_segsz=%5hd\n", - m->m_pkthdr.flowid, m->m_pkthdr.csum_flags, + m->m_pkthdr.flowid, (int)m->m_pkthdr.csum_flags, m->m_pkthdr.csum_data, m->m_pkthdr.tso_segsz); - printf(" rcvif=%16p, header=%18p, len=%19d\n", - m->m_pkthdr.rcvif, m->m_pkthdr.header, m->m_pkthdr.len); + printf(" rcvif=%16p, len=%19d\n", + m->m_pkthdr.rcvif, m->m_pkthdr.len); } printf(" m_next=%16p, m_nextpk=%16p, m_data=%16p\n", m->m_next, m->m_nextpkt, m->m_data); - printf(" m_len=%17d, m_flags=%#15x, m_type=%18hd\n", + printf(" m_len=%17d, m_flags=%#15x, m_type=%18u\n", m->m_len, m->m_flags, m->m_type); len = m->m_len; @@ -621,7 +622,7 @@ xnb_free_communication_mem(struct xnb_softc *xnb) { if (xnb->kva != 0) { #ifndef XENHVM - kmem_free(kernel_map, xnb->kva, xnb->kva_size); + kva_free(xnb->kva, xnb->kva_size); #else if (xnb->pseudo_phys_res != NULL) { bus_release_resource(xnb->dev, SYS_RES_MEMORY, @@ -647,10 +648,7 @@ xnb_disconnect(struct xnb_softc *xnb) int error; int i; - if (xnb->irq != 0) { - unbind_from_irqhandler(xnb->irq); - xnb->irq = 0; - } + xen_intr_unbind(xnb->xen_intr_handle); /* * We may still have another thread currently processing requests. We @@ -773,13 +771,13 @@ xnb_connect_comms(struct xnb_softc *xnb) xnb->flags |= XNBF_RING_CONNECTED; - error = - bind_interdomain_evtchn_to_irqhandler(xnb->otherend_id, - xnb->evtchn, - device_get_nameunit(xnb->dev), - xnb_intr, /*arg*/xnb, - INTR_TYPE_BIO | INTR_MPSAFE, - &xnb->irq); + error = xen_intr_bind_remote_port(xnb->dev, + xnb->otherend_id, + xnb->evtchn, + /*filter*/NULL, + xnb_intr, /*arg*/xnb, + INTR_TYPE_BIO | INTR_MPSAFE, + &xnb->xen_intr_handle); if (error != 0) { (void)xnb_disconnect(xnb); xenbus_dev_fatal(xnb->dev, error, "binding event channel"); @@ -811,7 +809,7 @@ xnb_alloc_communication_mem(struct xnb_softc *xnb) xnb->kva_size += xnb->ring_configs[i].ring_pages * PAGE_SIZE; } #ifndef XENHVM - xnb->kva = kmem_alloc_nofault(kernel_map, xnb->kva_size); + xnb->kva = kva_alloc(xnb->kva_size); if (xnb->kva == 0) return (ENOMEM); xnb->gnt_base_addr = xnb->kva; @@ -1448,7 +1446,7 @@ xnb_intr(void *arg) RING_PUSH_RESPONSES_AND_CHECK_NOTIFY(txb, notify); if (notify != 0) - notify_remote_via_irq(xnb->irq); + xen_intr_signal(xnb->xen_intr_handle); txb->sring->req_event = txb->req_cons + 1; xen_mb(); @@ -2361,7 +2359,7 @@ xnb_start_locked(struct ifnet *ifp) RING_PUSH_RESPONSES_AND_CHECK_NOTIFY(rxb, notify); if ((notify != 0) || (out_of_space != 0)) - notify_remote_via_irq(xnb->irq); + xen_intr_signal(xnb->xen_intr_handle); rxb->sring->req_event = req_prod_local + 1; xen_mb(); } while (rxb->sring->req_prod != req_prod_local) ; diff --git a/sys/dev/xen/netfront/netfront.c b/sys/dev/xen/netfront/netfront.c index 68b3bf9be82..f9c72e6fc49 100644 --- a/sys/dev/xen/netfront/netfront.c +++ b/sys/dev/xen/netfront/netfront.c @@ -76,17 +76,16 @@ __FBSDID("$FreeBSD$"); #include -#include -#include -#include +#include #include #include -#include #include #include #include #include +#include + #include #include "xenbus_if.h" @@ -257,8 +256,7 @@ struct netfront_info { struct mtx rx_lock; struct mtx sc_lock; - u_int handle; - u_int irq; + xen_intr_handle_t xen_intr_handle; u_int copying_receiver; u_int carrier; u_int maxfrags; @@ -547,7 +545,8 @@ talk_to_backend(device_t dev, struct netfront_info *info) goto abort_transaction; } err = xs_printf(xst, node, - "event-channel", "%u", irq_to_evtchn_port(info->irq)); + "event-channel", "%u", + xen_intr_port(info->xen_intr_handle)); if (err) { message = "writing event-channel"; goto abort_transaction; @@ -609,7 +608,6 @@ setup_device(device_t dev, struct netfront_info *info) info->rx_ring_ref = GRANT_REF_INVALID; info->rx.sring = NULL; info->tx.sring = NULL; - info->irq = 0; txs = (netif_tx_sring_t *)malloc(PAGE_SIZE, M_DEVBUF, M_NOWAIT|M_ZERO); if (!txs) { @@ -636,12 +634,13 @@ setup_device(device_t dev, struct netfront_info *info) if (error) goto fail; - error = bind_listening_port_to_irqhandler(xenbus_get_otherend_id(dev), - "xn", xn_intr, info, INTR_TYPE_NET | INTR_MPSAFE, &info->irq); + error = xen_intr_alloc_and_bind_local_port(dev, + xenbus_get_otherend_id(dev), /*filter*/NULL, xn_intr, info, + INTR_TYPE_NET | INTR_MPSAFE | INTR_ENTROPY, &info->xen_intr_handle); if (error) { xenbus_dev_fatal(dev, error, - "bind_evtchn_to_irqhandler failed"); + "xen_intr_alloc_and_bind_local_port failed"); goto fail; } @@ -806,7 +805,7 @@ network_alloc_rx_buffers(struct netfront_info *sc) req_prod = sc->rx.req_prod_pvt; - if (unlikely(sc->carrier == 0)) + if (__predict_false(sc->carrier == 0)) return; /* @@ -946,7 +945,7 @@ refill: /* Zap PTEs and give away pages in one big multicall. */ (void)HYPERVISOR_multicall(sc->rx_mcl, i+1); - if (unlikely(sc->rx_mcl[i].result != i || + if (__predict_false(sc->rx_mcl[i].result != i || HYPERVISOR_memory_op(XENMEM_decrease_reservation, &reservation) != i)) panic("%s: unable to reduce memory " @@ -961,7 +960,7 @@ refill: push: RING_PUSH_REQUESTS_AND_CHECK_NOTIFY(&sc->rx, notify); if (notify) - notify_remote_via_irq(sc->irq); + xen_intr_signal(sc->xen_intr_handle); } static void @@ -1003,7 +1002,7 @@ xn_rxeof(struct netfront_info *np) err = xennet_get_responses(np, &rinfo, rp, &i, &m, &pages_flipped); - if (unlikely(err)) { + if (__predict_false(err)) { if (m) mbufq_tail(&errq, m); np->stats.rx_errors++; @@ -1151,7 +1150,7 @@ xn_txeof(struct netfront_info *np) */ if (!m->m_next) ifp->if_opackets++; - if (unlikely(gnttab_query_foreign_access( + if (__predict_false(gnttab_query_foreign_access( np->grant_tx_ref[id]) != 0)) { panic("%s: grant id %u still in use by the " "backend", __func__, id); @@ -1249,7 +1248,7 @@ xennet_get_extras(struct netfront_info *np, struct mbuf *m; grant_ref_t ref; - if (unlikely(*cons + 1 == rp)) { + if (__predict_false(*cons + 1 == rp)) { #if 0 if (net_ratelimit()) WPRINTK("Missing extra info\n"); @@ -1261,7 +1260,7 @@ xennet_get_extras(struct netfront_info *np, extra = (struct netif_extra_info *) RING_GET_RESPONSE(&np->rx, ++(*cons)); - if (unlikely(!extra->type || + if (__predict_false(!extra->type || extra->type >= XEN_NETIF_EXTRA_TYPE_MAX)) { #if 0 if (net_ratelimit()) @@ -1317,7 +1316,7 @@ xennet_get_responses(struct netfront_info *np, DPRINTK("rx->status=%hd rx->offset=%hu frags=%u\n", rx->status, rx->offset, frags); #endif - if (unlikely(rx->status < 0 || + if (__predict_false(rx->status < 0 || rx->offset + rx->status > PAGE_SIZE)) { #if 0 @@ -1679,7 +1678,7 @@ xn_start_locked(struct ifnet *ifp) RING_PUSH_REQUESTS_AND_CHECK_NOTIFY(&sc->tx, notify); if (notify) - notify_remote_via_irq(sc->irq); + xen_intr_signal(sc->xen_intr_handle); if (RING_FULL(&sc->tx)) { sc->tx_full = 1; @@ -1961,7 +1960,7 @@ network_connect(struct netfront_info *np) * packets. */ netfront_carrier_on(np); - notify_remote_via_irq(np->irq); + xen_intr_signal(np->xen_intr_handle); XN_TX_LOCK(np); xn_txeof(np); XN_TX_UNLOCK(np); @@ -2050,8 +2049,9 @@ xn_configure_features(struct netfront_info *np) return (err); } -/** Create a network device. - * @param handle device handle +/** + * Create a network device. + * @param dev Newbus device representing this virtual NIC. */ int create_netdev(device_t dev) @@ -2198,10 +2198,7 @@ netif_disconnect_backend(struct netfront_info *info) free_ring(&info->tx_ring_ref, &info->tx.sring); free_ring(&info->rx_ring_ref, &info->rx.sring); - if (info->irq) - unbind_from_irqhandler(info->irq); - - info->irq = 0; + xen_intr_unbind(&info->xen_intr_handle); } static void diff --git a/sys/dev/xen/timer/timer.c b/sys/dev/xen/timer/timer.c new file mode 100644 index 00000000000..9c8db59b895 --- /dev/null +++ b/sys/dev/xen/timer/timer.c @@ -0,0 +1,608 @@ +/** + * Copyright (c) 2009 Adrian Chadd + * Copyright (c) 2012 Spectra Logic Corporation + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + */ + +/** + * \file dev/xen/timer/timer.c + * \brief A timer driver for the Xen hypervisor's PV clock. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "clock_if.h" + +static devclass_t xentimer_devclass; + +#define NSEC_IN_SEC 1000000000ULL +#define NSEC_IN_USEC 1000ULL +/* 18446744073 = int(2^64 / NSEC_IN_SC) = 1 ns in 64-bit fractions */ +#define FRAC_IN_NSEC 18446744073LL + +/* Xen timers may fire up to 100us off */ +#define XENTIMER_MIN_PERIOD_IN_NSEC 100*NSEC_IN_USEC +#define XENCLOCK_RESOLUTION 10000000 + +#define ETIME 62 /* Xen "bad time" error */ + +#define XENTIMER_QUALITY 950 + +struct xentimer_pcpu_data { + uint64_t timer; + uint64_t last_processed; + void *irq_handle; +}; + +DPCPU_DEFINE(struct xentimer_pcpu_data, xentimer_pcpu); + +DPCPU_DECLARE(struct vcpu_info *, vcpu_info); + +struct xentimer_softc { + device_t dev; + struct timecounter tc; + struct eventtimer et; +}; + +/* Last time; this guarantees a monotonically increasing clock. */ +volatile uint64_t xen_timer_last_time = 0; + +static void +xentimer_identify(driver_t *driver, device_t parent) +{ + if (!xen_domain()) + return; + + /* Handle all Xen PV timers in one device instance. */ + if (devclass_get_device(xentimer_devclass, 0)) + return; + + BUS_ADD_CHILD(parent, 0, "xen_et", 0); +} + +static int +xentimer_probe(device_t dev) +{ + KASSERT((xen_domain()), ("Trying to use Xen timer on bare metal")); + /* + * In order to attach, this driver requires the following: + * - Vector callback support by the hypervisor, in order to deliver + * timer interrupts to the correct CPU for CPUs other than 0. + * - Access to the hypervisor shared info page, in order to look up + * each VCPU's timer information and the Xen wallclock time. + * - The hypervisor must say its PV clock is "safe" to use. + * - The hypervisor must support VCPUOP hypercalls. + * - The maximum number of CPUs supported by FreeBSD must not exceed + * the number of VCPUs supported by the hypervisor. + */ +#define XTREQUIRES(condition, reason...) \ + if (!(condition)) { \ + device_printf(dev, ## reason); \ + device_detach(dev); \ + return (ENXIO); \ + } + + if (xen_hvm_domain()) { + XTREQUIRES(xen_vector_callback_enabled, + "vector callbacks unavailable\n"); + XTREQUIRES(xen_feature(XENFEAT_hvm_safe_pvclock), + "HVM safe pvclock unavailable\n"); + } + XTREQUIRES(HYPERVISOR_shared_info != NULL, + "shared info page unavailable\n"); + XTREQUIRES(HYPERVISOR_vcpu_op(VCPUOP_stop_periodic_timer, 0, NULL) == 0, + "VCPUOPs interface unavailable\n"); +#undef XTREQUIRES + device_set_desc(dev, "Xen PV Clock"); + return (0); +} + +/* + * Scale a 64-bit delta by scaling and multiplying by a 32-bit fraction, + * yielding a 64-bit result. + */ +static inline uint64_t +scale_delta(uint64_t delta, uint32_t mul_frac, int shift) +{ + uint64_t product; + + if (shift < 0) + delta >>= -shift; + else + delta <<= shift; + +#if defined(__i386__) + { + uint32_t tmp1, tmp2; + + /** + * For i386, the formula looks like: + * + * lower = (mul_frac * (delta & UINT_MAX)) >> 32 + * upper = mul_frac * (delta >> 32) + * product = lower + upper + */ + __asm__ ( + "mul %5 ; " + "mov %4,%%eax ; " + "mov %%edx,%4 ; " + "mul %5 ; " + "xor %5,%5 ; " + "add %4,%%eax ; " + "adc %5,%%edx ; " + : "=A" (product), "=r" (tmp1), "=r" (tmp2) + : "a" ((uint32_t)delta), "1" ((uint32_t)(delta >> 32)), + "2" (mul_frac) ); + } +#elif defined(__amd64__) + { + unsigned long tmp; + + __asm__ ( + "mulq %[mul_frac] ; shrd $32, %[hi], %[lo]" + : [lo]"=a" (product), [hi]"=d" (tmp) + : "0" (delta), [mul_frac]"rm"((uint64_t)mul_frac)); + } +#else +#error "xentimer: unsupported architecture" +#endif + + return (product); +} + +static uint64_t +get_nsec_offset(struct vcpu_time_info *tinfo) +{ + + return (scale_delta(rdtsc() - tinfo->tsc_timestamp, + tinfo->tsc_to_system_mul, tinfo->tsc_shift)); +} + +/* + * Read the current hypervisor system uptime value from Xen. + * See for a description of how this works. + */ +static uint32_t +xen_fetch_vcpu_tinfo(struct vcpu_time_info *dst, struct vcpu_time_info *src) +{ + + do { + dst->version = src->version; + rmb(); + dst->tsc_timestamp = src->tsc_timestamp; + dst->system_time = src->system_time; + dst->tsc_to_system_mul = src->tsc_to_system_mul; + dst->tsc_shift = src->tsc_shift; + rmb(); + } while ((src->version & 1) | (dst->version ^ src->version)); + + return (dst->version); +} + +/** + * \brief Get the current time, in nanoseconds, since the hypervisor booted. + * + * \note This function returns the current CPU's idea of this value, unless + * it happens to be less than another CPU's previously determined value. + */ +static uint64_t +xen_fetch_vcpu_time(void) +{ + struct vcpu_time_info dst; + struct vcpu_time_info *src; + uint32_t pre_version; + uint64_t now; + volatile uint64_t last; + struct vcpu_info *vcpu = DPCPU_GET(vcpu_info); + + src = &vcpu->time; + + critical_enter(); + do { + pre_version = xen_fetch_vcpu_tinfo(&dst, src); + barrier(); + now = dst.system_time + get_nsec_offset(&dst); + barrier(); + } while (pre_version != src->version); + + /* + * Enforce a monotonically increasing clock time across all + * VCPUs. If our time is too old, use the last time and return. + * Otherwise, try to update the last time. + */ + do { + last = xen_timer_last_time; + if (last > now) { + now = last; + break; + } + } while (!atomic_cmpset_64(&xen_timer_last_time, last, now)); + + critical_exit(); + + return (now); +} + +static uint32_t +xentimer_get_timecount(struct timecounter *tc) +{ + + return ((uint32_t)xen_fetch_vcpu_time() & UINT_MAX); +} + +/** + * \brief Fetch the hypervisor boot time, known as the "Xen wallclock". + * + * \param ts Timespec to store the current stable value. + * \param version Pointer to store the corresponding wallclock version. + * + * \note This value is updated when Domain-0 shifts its clock to follow + * clock drift, e.g. as detected by NTP. + */ +static void +xen_fetch_wallclock(struct timespec *ts) +{ + shared_info_t *src = HYPERVISOR_shared_info; + uint32_t version = 0; + + do { + version = src->wc_version; + rmb(); + ts->tv_sec = src->wc_sec; + ts->tv_nsec = src->wc_nsec; + rmb(); + } while ((src->wc_version & 1) | (version ^ src->wc_version)); +} + +static void +xen_fetch_uptime(struct timespec *ts) +{ + uint64_t uptime = xen_fetch_vcpu_time(); + ts->tv_sec = uptime / NSEC_IN_SEC; + ts->tv_nsec = uptime % NSEC_IN_SEC; +} + +static int +xentimer_settime(device_t dev __unused, struct timespec *ts) +{ + /* + * Don't return EINVAL here; just silently fail if the domain isn't + * privileged enough to set the TOD. + */ + return(0); +} + +/** + * \brief Return current time according to the Xen Hypervisor wallclock. + * + * \param dev Xentimer device. + * \param ts Pointer to store the wallclock time. + * + * \note The Xen time structures document the hypervisor start time and the + * uptime-since-hypervisor-start (in nsec.) They need to be combined + * in order to calculate a TOD clock. + */ +static int +xentimer_gettime(device_t dev, struct timespec *ts) +{ + struct timespec u_ts; + + timespecclear(ts); + xen_fetch_wallclock(ts); + xen_fetch_uptime(&u_ts); + timespecadd(ts, &u_ts); + + return(0); +} + +/** + * \brief Handle a timer interrupt for the Xen PV timer driver. + * + * \param arg Xen timer driver softc that is expecting the interrupt. + */ +static int +xentimer_intr(void *arg) +{ + struct xentimer_softc *sc = (struct xentimer_softc *)arg; + struct xentimer_pcpu_data *pcpu = DPCPU_PTR(xentimer_pcpu); + + pcpu->last_processed = xen_fetch_vcpu_time(); + if (pcpu->timer != 0 && sc->et.et_active) + sc->et.et_event_cb(&sc->et, sc->et.et_arg); + + return (FILTER_HANDLED); +} + +static int +xentimer_vcpu_start_timer(int vcpu, uint64_t next_time) +{ + struct vcpu_set_singleshot_timer single; + + single.timeout_abs_ns = next_time; + single.flags = VCPU_SSHOTTMR_future; + return (HYPERVISOR_vcpu_op(VCPUOP_set_singleshot_timer, vcpu, &single)); +} + +static int +xentimer_vcpu_stop_timer(int vcpu) +{ + + return (HYPERVISOR_vcpu_op(VCPUOP_stop_singleshot_timer, vcpu, NULL)); +} + +/** + * \brief Set the next oneshot time for the current CPU. + * + * \param et Xen timer driver event timer to schedule on. + * \param first Delta to the next time to schedule the interrupt for. + * \param period Not used. + * + * \note See eventtimers(9) for more information. + * \note + * + * \returns 0 + */ +static int +xentimer_et_start(struct eventtimer *et, + sbintime_t first, sbintime_t period) +{ + int error = 0, i = 0; + struct xentimer_softc *sc = et->et_priv; + int cpu = PCPU_GET(acpi_id); + struct xentimer_pcpu_data *pcpu = DPCPU_PTR(xentimer_pcpu); + uint64_t first_in_ns, next_time; + + /* See sbttots() for this formula. */ + first_in_ns = (((first >> 32) * NSEC_IN_SEC) + + (((uint64_t)NSEC_IN_SEC * (uint32_t)first) >> 32)); + + /* + * Retry any timer scheduling failures, where the hypervisor + * returns -ETIME. Sometimes even a 100us timer period isn't large + * enough, but larger period instances are relatively uncommon. + * + * XXX Remove the panics once et_start() and its consumers are + * equipped to deal with start failures. + */ + do { + if (++i == 60) + panic("can't schedule timer"); + next_time = xen_fetch_vcpu_time() + first_in_ns; + error = xentimer_vcpu_start_timer(cpu, next_time); + } while (error == -ETIME); + + if (error) + panic("%s: Error %d setting singleshot timer to %"PRIu64"\n", + device_get_nameunit(sc->dev), error, next_time); + + pcpu->timer = next_time; + return (error); +} + +/** + * \brief Cancel the event timer's currently running timer, if any. + */ +static int +xentimer_et_stop(struct eventtimer *et) +{ + int cpu = PCPU_GET(acpi_id); + struct xentimer_pcpu_data *pcpu = DPCPU_PTR(xentimer_pcpu); + + pcpu->timer = 0; + return (xentimer_vcpu_stop_timer(cpu)); +} + +/** + * \brief Attach a Xen PV timer driver instance. + * + * \param dev Bus device object to attach. + * + * \note + * \returns EINVAL + */ +static int +xentimer_attach(device_t dev) +{ + struct xentimer_softc *sc = device_get_softc(dev); + int error, i; + + sc->dev = dev; + + /* Bind an event channel to a VIRQ on each VCPU. */ + CPU_FOREACH(i) { + struct xentimer_pcpu_data *pcpu = DPCPU_ID_PTR(i, xentimer_pcpu); + + error = HYPERVISOR_vcpu_op(VCPUOP_stop_periodic_timer, i, NULL); + if (error) { + device_printf(dev, "Error disabling Xen periodic timer " + "on CPU %d\n", i); + return (error); + } + + error = xen_intr_bind_virq(dev, VIRQ_TIMER, i, xentimer_intr, + NULL, sc, INTR_TYPE_CLK, &pcpu->irq_handle); + if (error) { + device_printf(dev, "Error %d binding VIRQ_TIMER " + "to VCPU %d\n", error, i); + return (error); + } + xen_intr_describe(pcpu->irq_handle, "c%d", i); + } + + /* Register the event timer. */ + sc->et.et_name = "XENTIMER"; + sc->et.et_quality = XENTIMER_QUALITY; + sc->et.et_flags = ET_FLAGS_ONESHOT | ET_FLAGS_PERCPU; + sc->et.et_frequency = NSEC_IN_SEC; + /* See tstosbt() for this formula */ + sc->et.et_min_period = (XENTIMER_MIN_PERIOD_IN_NSEC * + (((uint64_t)1 << 63) / 500000000) >> 32); + sc->et.et_max_period = ((sbintime_t)4 << 32); + sc->et.et_start = xentimer_et_start; + sc->et.et_stop = xentimer_et_stop; + sc->et.et_priv = sc; + et_register(&sc->et); + + /* Register the timecounter. */ + sc->tc.tc_name = "XENTIMER"; + sc->tc.tc_quality = XENTIMER_QUALITY; + /* + * The underlying resolution is in nanoseconds, since the timer info + * scales TSC frequencies using a fraction that represents time in + * terms of nanoseconds. + */ + sc->tc.tc_frequency = NSEC_IN_SEC; + sc->tc.tc_counter_mask = ~0u; + sc->tc.tc_get_timecount = xentimer_get_timecount; + sc->tc.tc_priv = sc; + tc_init(&sc->tc); + + /* Register the Hypervisor wall clock */ + clock_register(dev, XENCLOCK_RESOLUTION); + + return (0); +} + +static int +xentimer_detach(device_t dev) +{ + + /* Implement Xen PV clock teardown - XXX see hpet_detach ? */ + /* If possible: + * 1. need to deregister timecounter + * 2. need to deregister event timer + * 3. need to deregister virtual IRQ event channels + */ + return (EBUSY); +} + +/** + * The following device methods are disabled because they wouldn't work + * properly. + */ +#ifdef NOTYET +static int +xentimer_resume(device_t dev) +{ + struct xentimer_softc *sc = device_get_softc(dev); + int error = 0; + int i; + + device_printf(sc->dev, "%s", __func__); + CPU_FOREACH(i) { + struct xentimer_pcpu_data *pcpu = DPCPU_ID_PTR(i, xentimer_pcpu); + + /* Skip inactive timers. */ + if (pcpu->timer == 0) + continue; + + /* + * XXX This won't actually work, because Xen requires that + * singleshot timers be set while running on the given CPU. + */ + error = xentimer_vcpu_start_timer(i, pcpu->timer); + if (error == -ETIME) { + /* Event time has already passed; process. */ + xentimer_intr(sc); + } else if (error != 0) { + panic("%s: error %d restarting vcpu %d\n", + __func__, error, i); + } + } + + return (error); +} + +static int +xentimer_suspend(device_t dev) +{ + struct xentimer_softc *sc = device_get_softc(dev); + int error = 0; + int i; + + device_printf(sc->dev, "%s", __func__); + CPU_FOREACH(i) { + struct xentimer_pcpu_data *pcpu = DPCPU_ID_PTR(i, xentimer_pcpu); + + /* Skip inactive timers. */ + if (pcpu->timer == 0) + continue; + error = xentimer_vcpu_stop_timer(i); + if (error) + panic("Error %d stopping VCPU %d timer\n", error, i); + } + + return (error); +} +#endif + +static device_method_t xentimer_methods[] = { + DEVMETHOD(device_identify, xentimer_identify), + DEVMETHOD(device_probe, xentimer_probe), + DEVMETHOD(device_attach, xentimer_attach), + DEVMETHOD(device_detach, xentimer_detach), +#ifdef NOTYET + DEVMETHOD(device_suspend, xentimer_suspend), + DEVMETHOD(device_resume, xentimer_resume), +#endif + /* clock interface */ + DEVMETHOD(clock_gettime, xentimer_gettime), + DEVMETHOD(clock_settime, xentimer_settime), + DEVMETHOD_END +}; + +static driver_t xentimer_driver = { + "xen_et", + xentimer_methods, + sizeof(struct xentimer_softc), +}; + +DRIVER_MODULE(xentimer, nexus, xentimer_driver, xentimer_devclass, 0, 0); +MODULE_DEPEND(xentimer, nexus, 1, 1, 1); diff --git a/sys/dev/xen/xenpci/evtchn.c b/sys/dev/xen/xenpci/evtchn.c deleted file mode 100644 index 2d9dd6d664d..00000000000 --- a/sys/dev/xen/xenpci/evtchn.c +++ /dev/null @@ -1,467 +0,0 @@ -/****************************************************************************** - * evtchn.c - * - * A simplified event channel for para-drivers in unmodified linux - * - * Copyright (c) 2002-2005, K A Fraser - * Copyright (c) 2005, Intel Corporation - * - * This file may be distributed separately from the Linux kernel, or - * incorporated into other software packages, subject to the following license: - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this source file (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 -__FBSDID("$FreeBSD$"); - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include - -#if defined(__i386__) -#define __ffs(word) (ffs(word) - 1) -#elif defined(__amd64__) -static inline unsigned long __ffs(unsigned long word) -{ - __asm__("bsfq %1,%0" - :"=r" (word) - :"rm" (word)); /* XXXRW: why no "cc"? */ - return word; -} -#else -#error "evtchn: unsupported architecture" -#endif - -#define is_valid_evtchn(x) ((x) != 0) -#define evtchn_from_irq(x) (irq_evtchn[irq].evtchn) - -static struct { - struct mtx lock; - driver_intr_t *handler; - void *arg; - int evtchn; - int close:1; /* close on unbind_from_irqhandler()? */ - int inuse:1; - int in_handler:1; - int mpsafe:1; -} irq_evtchn[256]; -static int evtchn_to_irq[NR_EVENT_CHANNELS] = { - [0 ... NR_EVENT_CHANNELS-1] = -1 }; - -static struct mtx irq_alloc_lock; -static device_t xenpci_device; - -#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0])) - -static unsigned int -alloc_xen_irq(void) -{ - static int warned; - unsigned int irq; - - mtx_lock(&irq_alloc_lock); - - for (irq = 1; irq < ARRAY_SIZE(irq_evtchn); irq++) { - if (irq_evtchn[irq].inuse) - continue; - irq_evtchn[irq].inuse = 1; - mtx_unlock(&irq_alloc_lock); - return irq; - } - - if (!warned) { - warned = 1; - printf("alloc_xen_irq: No available IRQ to bind to: " - "increase irq_evtchn[] size in evtchn.c.\n"); - } - - mtx_unlock(&irq_alloc_lock); - - return -ENOSPC; -} - -static void -free_xen_irq(int irq) -{ - - mtx_lock(&irq_alloc_lock); - irq_evtchn[irq].inuse = 0; - mtx_unlock(&irq_alloc_lock); -} - -int -irq_to_evtchn_port(int irq) -{ - - return irq_evtchn[irq].evtchn; -} - -void -mask_evtchn(int port) -{ - shared_info_t *s = HYPERVISOR_shared_info; - - synch_set_bit(port, &s->evtchn_mask[0]); -} - -void -unmask_evtchn(int port) -{ - evtchn_unmask_t op = { .port = port }; - - HYPERVISOR_event_channel_op(EVTCHNOP_unmask, &op); -} - -int -bind_listening_port_to_irqhandler(unsigned int remote_domain, - const char *devname, driver_intr_t handler, void *arg, - unsigned long irqflags, unsigned int *irqp) -{ - struct evtchn_alloc_unbound alloc_unbound; - unsigned int irq; - int error; - - irq = alloc_xen_irq(); - if (irq < 0) - return irq; - - mtx_lock(&irq_evtchn[irq].lock); - - alloc_unbound.dom = DOMID_SELF; - alloc_unbound.remote_dom = remote_domain; - error = HYPERVISOR_event_channel_op(EVTCHNOP_alloc_unbound, - &alloc_unbound); - if (error) { - mtx_unlock(&irq_evtchn[irq].lock); - free_xen_irq(irq); - return (-error); - } - - irq_evtchn[irq].handler = handler; - irq_evtchn[irq].arg = arg; - irq_evtchn[irq].evtchn = alloc_unbound.port; - irq_evtchn[irq].close = 1; - irq_evtchn[irq].mpsafe = (irqflags & INTR_MPSAFE) != 0; - - evtchn_to_irq[alloc_unbound.port] = irq; - - unmask_evtchn(alloc_unbound.port); - - mtx_unlock(&irq_evtchn[irq].lock); - - if (irqp) - *irqp = irq; - return (0); -} - -int -bind_interdomain_evtchn_to_irqhandler(unsigned int remote_domain, - unsigned int remote_port, const char *devname, driver_intr_t handler, - void *arg, unsigned long irqflags, unsigned int *irqp) -{ - struct evtchn_bind_interdomain bind_interdomain; - unsigned int irq; - int error; - - irq = alloc_xen_irq(); - if (irq < 0) - return irq; - - mtx_lock(&irq_evtchn[irq].lock); - - bind_interdomain.remote_dom = remote_domain; - bind_interdomain.remote_port = remote_port; - error = HYPERVISOR_event_channel_op(EVTCHNOP_bind_interdomain, - &bind_interdomain); - if (error) { - mtx_unlock(&irq_evtchn[irq].lock); - free_xen_irq(irq); - return (-error); - } - - irq_evtchn[irq].handler = handler; - irq_evtchn[irq].arg = arg; - irq_evtchn[irq].evtchn = bind_interdomain.local_port; - irq_evtchn[irq].close = 1; - irq_evtchn[irq].mpsafe = (irqflags & INTR_MPSAFE) != 0; - - evtchn_to_irq[bind_interdomain.local_port] = irq; - - unmask_evtchn(bind_interdomain.local_port); - - mtx_unlock(&irq_evtchn[irq].lock); - - if (irqp) - *irqp = irq; - return (0); -} - - -int -bind_caller_port_to_irqhandler(unsigned int caller_port, - const char *devname, driver_intr_t handler, void *arg, - unsigned long irqflags, unsigned int *irqp) -{ - unsigned int irq; - - irq = alloc_xen_irq(); - if (irq < 0) - return irq; - - mtx_lock(&irq_evtchn[irq].lock); - - irq_evtchn[irq].handler = handler; - irq_evtchn[irq].arg = arg; - irq_evtchn[irq].evtchn = caller_port; - irq_evtchn[irq].close = 0; - irq_evtchn[irq].mpsafe = (irqflags & INTR_MPSAFE) != 0; - - evtchn_to_irq[caller_port] = irq; - - unmask_evtchn(caller_port); - - mtx_unlock(&irq_evtchn[irq].lock); - - if (irqp) - *irqp = irq; - return (0); -} - -void -unbind_from_irqhandler(unsigned int irq) -{ - int evtchn; - - mtx_lock(&irq_evtchn[irq].lock); - - evtchn = evtchn_from_irq(irq); - - if (is_valid_evtchn(evtchn)) { - evtchn_to_irq[evtchn] = -1; - mask_evtchn(evtchn); - if (irq_evtchn[irq].close) { - struct evtchn_close close = { .port = evtchn }; - if (HYPERVISOR_event_channel_op(EVTCHNOP_close, &close)) - panic("EVTCHNOP_close failed"); - } - } - - irq_evtchn[irq].handler = NULL; - irq_evtchn[irq].evtchn = 0; - - mtx_unlock(&irq_evtchn[irq].lock); - - while (irq_evtchn[irq].in_handler) - cpu_relax(); - - free_xen_irq(irq); -} - -void notify_remote_via_irq(int irq) -{ - int evtchn; - - evtchn = evtchn_from_irq(irq); - if (is_valid_evtchn(evtchn)) - notify_remote_via_evtchn(evtchn); -} - -static inline unsigned long active_evtchns(unsigned int cpu, shared_info_t *sh, - unsigned int idx) -{ - return (sh->evtchn_pending[idx] & ~sh->evtchn_mask[idx]); -} - -static void -evtchn_interrupt(void *arg) -{ - unsigned int l1i, l2i, port; - unsigned long masked_l1, masked_l2; - /* XXX: All events are bound to vcpu0 but irq may be redirected. */ - int cpu = 0; /*smp_processor_id();*/ - driver_intr_t *handler; - void *handler_arg; - int irq, handler_mpsafe; - shared_info_t *s = HYPERVISOR_shared_info; - vcpu_info_t *v = &s->vcpu_info[cpu]; - struct pcpu *pc = pcpu_find(cpu); - unsigned long l1, l2; - - v->evtchn_upcall_pending = 0; - -#if 0 -#ifndef CONFIG_X86 /* No need for a barrier -- XCHG is a barrier on x86. */ - /* Clear master flag /before/ clearing selector flag. */ - wmb(); -#endif -#endif - - l1 = atomic_readandclear_long(&v->evtchn_pending_sel); - - l1i = pc->pc_last_processed_l1i; - l2i = pc->pc_last_processed_l2i; - - while (l1 != 0) { - - l1i = (l1i + 1) % LONG_BIT; - masked_l1 = l1 & ((~0UL) << l1i); - - if (masked_l1 == 0) { /* if we masked out all events, wrap around to the beginning */ - l1i = LONG_BIT - 1; - l2i = LONG_BIT - 1; - continue; - } - l1i = __ffs(masked_l1); - - do { - l2 = active_evtchns(cpu, s, l1i); - - l2i = (l2i + 1) % LONG_BIT; - masked_l2 = l2 & ((~0UL) << l2i); - - if (masked_l2 == 0) { /* if we masked out all events, move on */ - l2i = LONG_BIT - 1; - break; - } - l2i = __ffs(masked_l2); - - /* process port */ - port = (l1i * LONG_BIT) + l2i; - synch_clear_bit(port, &s->evtchn_pending[0]); - - irq = evtchn_to_irq[port]; - if (irq < 0) - continue; - - mtx_lock(&irq_evtchn[irq].lock); - handler = irq_evtchn[irq].handler; - handler_arg = irq_evtchn[irq].arg; - handler_mpsafe = irq_evtchn[irq].mpsafe; - if (unlikely(handler == NULL)) { - printf("Xen IRQ%d (port %d) has no handler!\n", - irq, port); - mtx_unlock(&irq_evtchn[irq].lock); - continue; - } - irq_evtchn[irq].in_handler = 1; - mtx_unlock(&irq_evtchn[irq].lock); - - //local_irq_enable(); - if (!handler_mpsafe) - mtx_lock(&Giant); - handler(handler_arg); - if (!handler_mpsafe) - mtx_unlock(&Giant); - //local_irq_disable(); - - mtx_lock(&irq_evtchn[irq].lock); - irq_evtchn[irq].in_handler = 0; - mtx_unlock(&irq_evtchn[irq].lock); - - /* if this is the final port processed, we'll pick up here+1 next time */ - pc->pc_last_processed_l1i = l1i; - pc->pc_last_processed_l2i = l2i; - - } while (l2i != LONG_BIT - 1); - - l2 = active_evtchns(cpu, s, l1i); - if (l2 == 0) /* we handled all ports, so we can clear the selector bit */ - l1 &= ~(1UL << l1i); - } -} - -void -irq_suspend(void) -{ - struct xenpci_softc *scp = device_get_softc(xenpci_device); - - /* - * Take our interrupt handler out of the list of handlers - * that can handle this irq. - */ - if (scp->intr_cookie != NULL) { - if (BUS_TEARDOWN_INTR(device_get_parent(xenpci_device), - xenpci_device, scp->res_irq, scp->intr_cookie) != 0) - printf("intr teardown failed.. continuing\n"); - scp->intr_cookie = NULL; - } -} - -void -irq_resume(void) -{ - struct xenpci_softc *scp = device_get_softc(xenpci_device); - int evtchn, irq; - - for (evtchn = 0; evtchn < NR_EVENT_CHANNELS; evtchn++) { - mask_evtchn(evtchn); - evtchn_to_irq[evtchn] = -1; - } - - for (irq = 0; irq < ARRAY_SIZE(irq_evtchn); irq++) - irq_evtchn[irq].evtchn = 0; - - BUS_SETUP_INTR(device_get_parent(xenpci_device), - xenpci_device, scp->res_irq, INTR_TYPE_MISC, - NULL, evtchn_interrupt, NULL, &scp->intr_cookie); -} - -int -xenpci_irq_init(device_t device, struct xenpci_softc *scp) -{ - int irq, cpu; - int error; - - mtx_init(&irq_alloc_lock, "xen-irq-lock", NULL, MTX_DEF); - - for (irq = 0; irq < ARRAY_SIZE(irq_evtchn); irq++) - mtx_init(&irq_evtchn[irq].lock, "irq-evtchn", NULL, MTX_DEF); - - for (cpu = 0; cpu < mp_ncpus; cpu++) { - pcpu_find(cpu)->pc_last_processed_l1i = LONG_BIT - 1; - pcpu_find(cpu)->pc_last_processed_l2i = LONG_BIT - 1; - } - - error = BUS_SETUP_INTR(device_get_parent(device), device, - scp->res_irq, INTR_MPSAFE|INTR_TYPE_MISC, NULL, evtchn_interrupt, - NULL, &scp->intr_cookie); - if (error) - return (error); - - xenpci_device = device; - - return (0); -} diff --git a/sys/dev/xen/xenpci/xenpci.c b/sys/dev/xen/xenpci/xenpci.c index f4c9f73d686..0b1762df03b 100644 --- a/sys/dev/xen/xenpci/xenpci.c +++ b/sys/dev/xen/xenpci/xenpci.c @@ -32,40 +32,25 @@ __FBSDID("$FreeBSD$"); #include #include #include -#include -#include -#include #include #include #include #include -#include + +#include #include #include -#include -#include -#include -#include +#include #include #include -#include -#include -#include -#include - #include -/* - * These variables are used by the rest of the kernel to access the - * hypervisor. - */ -char *hypercall_stubs; -shared_info_t *HYPERVISOR_shared_info; -static vm_paddr_t shared_info_pa; +extern void xen_intr_handle_upcall(struct trapframe *trap_frame); + static device_t nexus; /* @@ -73,102 +58,41 @@ static device_t nexus; */ static devclass_t xenpci_devclass; -/* - * Return the CPUID base address for Xen functions. - */ -static uint32_t -xenpci_cpuid_base(void) +static int +xenpci_intr_filter(void *trap_frame) { - uint32_t base, regs[4]; - - for (base = 0x40000000; base < 0x40010000; base += 0x100) { - do_cpuid(base, regs); - if (!memcmp("XenVMMXenVMM", ®s[1], 12) - && (regs[0] - base) >= 2) - return (base); - } - return (0); + xen_intr_handle_upcall(trap_frame); + return (FILTER_HANDLED); } -/* - * Allocate and fill in the hypcall page. - */ static int -xenpci_init_hypercall_stubs(device_t dev, struct xenpci_softc * scp) +xenpci_irq_init(device_t device, struct xenpci_softc *scp) { - uint32_t base, regs[4]; - int i; + int error; - base = xenpci_cpuid_base(); - if (!base) { - device_printf(dev, "Xen platform device but not Xen VMM\n"); - return (EINVAL); - } - - if (bootverbose) { - do_cpuid(base + 1, regs); - device_printf(dev, "Xen version %d.%d.\n", - regs[0] >> 16, regs[0] & 0xffff); - } + error = BUS_SETUP_INTR(device_get_parent(device), device, + scp->res_irq, INTR_MPSAFE|INTR_TYPE_MISC, + xenpci_intr_filter, NULL, /*trap_frame*/NULL, + &scp->intr_cookie); + if (error) + return error; /* - * Find the hypercall pages. + * When using the PCI event delivery callback we cannot assign + * events to specific vCPUs, so all events are delivered to vCPU#0 by + * Xen. Since the PCI interrupt can fire on any CPU by default, we + * need to bind it to vCPU#0 in order to ensure that + * xen_intr_handle_upcall always gets called on vCPU#0. */ - do_cpuid(base + 2, regs); - - hypercall_stubs = malloc(regs[0] * PAGE_SIZE, M_TEMP, M_WAITOK); - - for (i = 0; i < regs[0]; i++) { - wrmsr(regs[1], vtophys(hypercall_stubs + i * PAGE_SIZE) + i); - } + error = BUS_BIND_INTR(device_get_parent(device), device, + scp->res_irq, 0); + if (error) + return error; + xen_hvm_set_callback(device); return (0); } -/* - * After a resume, re-initialise the hypercall page. - */ -static void -xenpci_resume_hypercall_stubs(device_t dev, struct xenpci_softc * scp) -{ - uint32_t base, regs[4]; - int i; - - base = xenpci_cpuid_base(); - - do_cpuid(base + 2, regs); - for (i = 0; i < regs[0]; i++) { - wrmsr(regs[1], vtophys(hypercall_stubs + i * PAGE_SIZE) + i); - } -} - -/* - * Tell the hypervisor how to contact us for event channel callbacks. - */ -static void -xenpci_set_callback(device_t dev) -{ - int irq; - uint64_t callback; - struct xen_hvm_param xhp; - - irq = pci_get_irq(dev); - if (irq < 16) { - callback = irq; - } else { - callback = (pci_get_intpin(dev) - 1) & 3; - callback |= pci_get_slot(dev) << 11; - callback |= 1ull << 56; - } - - xhp.domid = DOMID_SELF; - xhp.index = HVM_PARAM_CALLBACK_IRQ; - xhp.value = callback; - if (HYPERVISOR_hvm_op(HVMOP_set_param, &xhp)) - panic("Can't set evtchn callback"); -} - - /* * Deallocate anything allocated by xenpci_allocate_resources. */ @@ -292,35 +216,6 @@ xenpci_deactivate_resource(device_t dev, device_t child, int type, return (BUS_DEACTIVATE_RESOURCE(nexus, child, type, rid, r)); } -/* - * Called very early in the resume sequence - reinitialise the various - * bits of Xen machinery including the hypercall page and the shared - * info page. - */ -void -xenpci_resume() -{ - device_t dev = devclass_get_device(xenpci_devclass, 0); - struct xenpci_softc *scp = device_get_softc(dev); - struct xen_add_to_physmap xatp; - - xenpci_resume_hypercall_stubs(dev, scp); - - xatp.domid = DOMID_SELF; - xatp.idx = 0; - xatp.space = XENMAPSPACE_shared_info; - xatp.gpfn = shared_info_pa >> PAGE_SHIFT; - if (HYPERVISOR_memory_op(XENMEM_add_to_physmap, &xatp)) - panic("HYPERVISOR_memory_op failed"); - - pmap_kenter((vm_offset_t) HYPERVISOR_shared_info, shared_info_pa); - - xenpci_set_callback(dev); - - gnttab_resume(); - irq_resume(); -} - /* * Probe - just check device ID. */ @@ -341,11 +236,9 @@ xenpci_probe(device_t dev) static int xenpci_attach(device_t dev) { - int error; struct xenpci_softc *scp = device_get_softc(dev); - struct xen_add_to_physmap xatp; - vm_offset_t shared_va; devclass_t dc; + int error; /* * Find and record nexus0. Since we are not really on the @@ -365,33 +258,15 @@ xenpci_attach(device_t dev) goto errexit; } - error = xenpci_init_hypercall_stubs(dev, scp); - if (error) { - device_printf(dev, "xenpci_init_hypercall_stubs failed(%d).\n", - error); - goto errexit; - } - - setup_xen_features(); - - xenpci_alloc_space_int(scp, PAGE_SIZE, &shared_info_pa); - - xatp.domid = DOMID_SELF; - xatp.idx = 0; - xatp.space = XENMAPSPACE_shared_info; - xatp.gpfn = shared_info_pa >> PAGE_SHIFT; - if (HYPERVISOR_memory_op(XENMEM_add_to_physmap, &xatp)) - panic("HYPERVISOR_memory_op failed"); - - shared_va = kmem_alloc_nofault(kernel_map, PAGE_SIZE); - pmap_kenter(shared_va, shared_info_pa); - HYPERVISOR_shared_info = (void *) shared_va; - /* * Hook the irq up to evtchn */ - xenpci_irq_init(dev, scp); - xenpci_set_callback(dev); + error = xenpci_irq_init(dev, scp); + if (error) { + device_printf(dev, "xenpci_irq_init failed(%d).\n", + error); + goto errexit; + } return (bus_generic_attach(dev)); @@ -431,13 +306,42 @@ xenpci_detach(device_t dev) return (xenpci_deallocate_resources(dev)); } +static int +xenpci_suspend(device_t dev) +{ + struct xenpci_softc *scp = device_get_softc(dev); + device_t parent = device_get_parent(dev); + + if (scp->intr_cookie != NULL) { + if (BUS_TEARDOWN_INTR(parent, dev, scp->res_irq, + scp->intr_cookie) != 0) + printf("intr teardown failed.. continuing\n"); + scp->intr_cookie = NULL; + } + + return (bus_generic_suspend(dev)); +} + +static int +xenpci_resume(device_t dev) +{ + struct xenpci_softc *scp = device_get_softc(dev); + device_t parent = device_get_parent(dev); + + BUS_SETUP_INTR(parent, dev, scp->res_irq, + INTR_MPSAFE|INTR_TYPE_MISC, xenpci_intr_filter, NULL, + /*trap_frame*/NULL, &scp->intr_cookie); + xen_hvm_set_callback(dev); + return (bus_generic_resume(dev)); +} + static device_method_t xenpci_methods[] = { /* Device interface */ DEVMETHOD(device_probe, xenpci_probe), DEVMETHOD(device_attach, xenpci_attach), DEVMETHOD(device_detach, xenpci_detach), - DEVMETHOD(device_suspend, bus_generic_suspend), - DEVMETHOD(device_resume, bus_generic_resume), + DEVMETHOD(device_suspend, xenpci_suspend), + DEVMETHOD(device_resume, xenpci_resume), /* Bus interface */ DEVMETHOD(bus_add_child, bus_generic_add_child), diff --git a/sys/dev/xen/xenpci/xenpcivar.h b/sys/dev/xen/xenpci/xenpcivar.h index a57c080b31d..527a291c880 100644 --- a/sys/dev/xen/xenpci/xenpcivar.h +++ b/sys/dev/xen/xenpci/xenpcivar.h @@ -22,6 +22,8 @@ * 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. + * + * $FreeBSD$ */ /* @@ -38,7 +40,4 @@ struct xenpci_softc { vm_paddr_t phys_next; /* next page from mem range */ }; -extern int xenpci_irq_init(device_t device, struct xenpci_softc *scp); extern int xenpci_alloc_space(size_t sz, vm_paddr_t *pa); -extern void xenpci_resume(void); -extern void xen_suspend(void); diff --git a/sys/fs/devfs/devfs_vnops.c b/sys/fs/devfs/devfs_vnops.c index 7da9b11af2a..711a7eafb7e 100644 --- a/sys/fs/devfs/devfs_vnops.c +++ b/sys/fs/devfs/devfs_vnops.c @@ -1696,6 +1696,8 @@ static struct fileops devfs_ops_f = { .fo_close = devfs_close_f, .fo_chmod = vn_chmod, .fo_chown = vn_chown, + .fo_sendfile = vn_sendfile, + .fo_seek = vn_seek, .fo_flags = DFLAG_PASSABLE | DFLAG_SEEKABLE }; diff --git a/sys/fs/ext2fs/ext2_alloc.c b/sys/fs/ext2fs/ext2_alloc.c index 9bc47146279..b8ea159916c 100644 --- a/sys/fs/ext2fs/ext2_alloc.c +++ b/sys/fs/ext2fs/ext2_alloc.c @@ -80,8 +80,8 @@ static daddr_t ext2_mapsearch(struct m_ext2fs *, char *, daddr_t); * available block is located. */ int -ext2_alloc(struct inode *ip, int32_t lbn, int32_t bpref, int size, - struct ucred *cred, int32_t *bnp) +ext2_alloc(struct inode *ip, daddr_t lbn, e4fs_daddr_t bpref, int size, + struct ucred *cred, e4fs_daddr_t *bnp) { struct m_ext2fs *fs; struct ext2mount *ump; @@ -166,7 +166,8 @@ ext2_reallocblks(struct vop_reallocblks_args *ap) struct cluster_save *buflist; struct indir start_ap[NIADDR + 1], end_ap[NIADDR + 1], *idp; e2fs_lbn_t start_lbn, end_lbn; - int32_t soff, newblk, blkno; + int soff; + e2fs_daddr_t newblk, blkno; int i, len, start_lvl, end_lvl, pref, ssize; if (doreallocblks == 0) @@ -250,7 +251,7 @@ ext2_reallocblks(struct vop_reallocblks_args *ap) /* * Search the block map looking for an allocation of the desired size. */ - if ((newblk = (int32_t)ext2_hashalloc(ip, dtog(fs, pref), pref, + if ((newblk = (e2fs_daddr_t)ext2_hashalloc(ip, dtog(fs, pref), pref, len, ext2_clusteralloc)) == 0){ EXT2_UNLOCK(ump); goto fail; @@ -550,9 +551,9 @@ ext2_dirpref(struct inode *pip) * of the above. Then, blocknr tells us the number of the block * that will hold the pointer */ -int32_t -ext2_blkpref(struct inode *ip, e2fs_lbn_t lbn, int indx, int32_t *bap, - int32_t blocknr) +e4fs_daddr_t +ext2_blkpref(struct inode *ip, e2fs_lbn_t lbn, int indx, e2fs_daddr_t *bap, + e2fs_daddr_t blocknr) { int tmp; mtx_assert(EXT2_MTX(ip->i_ump), MA_OWNED); @@ -575,7 +576,7 @@ ext2_blkpref(struct inode *ip, e2fs_lbn_t lbn, int indx, int32_t *bap, follow the rule that a block should be allocated near its inode */ return blocknr ? blocknr : - (int32_t)(ip->i_block_group * + (e2fs_daddr_t)(ip->i_block_group * EXT2_BLOCKS_PER_GROUP(ip->i_e2fs)) + ip->i_e2fs->e2fs->e2fs_first_dblock; } @@ -955,7 +956,7 @@ gotit: * */ void -ext2_blkfree(struct inode *ip, int32_t bno, long size) +ext2_blkfree(struct inode *ip, e4fs_daddr_t bno, long size) { struct m_ext2fs *fs; struct buf *bp; diff --git a/sys/fs/ext2fs/ext2_balloc.c b/sys/fs/ext2fs/ext2_balloc.c index d1efc685e3f..9e48f5663e7 100644 --- a/sys/fs/ext2fs/ext2_balloc.c +++ b/sys/fs/ext2fs/ext2_balloc.c @@ -65,8 +65,8 @@ ext2_balloc(struct inode *ip, e2fs_lbn_t lbn, int size, struct ucred *cred, struct buf *bp, *nbp; struct vnode *vp = ITOV(ip); struct indir indirs[NIADDR + 2]; - uint32_t nb, newb; - int32_t *bap, pref; + e4fs_daddr_t nb, newb; + e2fs_daddr_t *bap, pref; int osize, nsize, num, i, error; *bpp = NULL; @@ -195,7 +195,7 @@ ext2_balloc(struct inode *ip, e2fs_lbn_t lbn, int size, struct ucred *cred, brelse(bp); return (error); } - bap = (int32_t *)bp->b_data; + bap = (e2fs_daddr_t *)bp->b_data; nb = bap[indirs[i].in_off]; if (i == num) break; diff --git a/sys/fs/ext2fs/ext2_bmap.c b/sys/fs/ext2fs/ext2_bmap.c index 86a197361c6..29cd7af64ea 100644 --- a/sys/fs/ext2fs/ext2_bmap.c +++ b/sys/fs/ext2fs/ext2_bmap.c @@ -46,10 +46,14 @@ #include #include +#include #include +#include #include #include +static int ext4_bmapext(struct vnode *, int32_t, int64_t *, int *, int *); + /* * Bmap converts the logical block number of a file to its physical block * number on the disk. The conversion is done by using the logical block @@ -58,7 +62,7 @@ int ext2_bmap(struct vop_bmap_args *ap) { - int32_t blkno; + daddr_t blkno; int error; /* @@ -70,12 +74,56 @@ ext2_bmap(struct vop_bmap_args *ap) if (ap->a_bnp == NULL) return (0); - error = ext2_bmaparray(ap->a_vp, ap->a_bn, &blkno, - ap->a_runp, ap->a_runb); + if (VTOI(ap->a_vp)->i_flags & EXT4_EXTENTS) + error = ext4_bmapext(ap->a_vp, ap->a_bn, &blkno, + ap->a_runp, ap->a_runb); + else + error = ext2_bmaparray(ap->a_vp, ap->a_bn, &blkno, + ap->a_runp, ap->a_runb); *ap->a_bnp = blkno; return (error); } +/* + * This function converts the logical block number of a file to + * its physical block number on the disk within ext4 extents. + */ +static int +ext4_bmapext(struct vnode *vp, int32_t bn, int64_t *bnp, int *runp, int *runb) +{ + struct inode *ip; + struct m_ext2fs *fs; + struct ext4_extent *ep; + struct ext4_extent_path path; + daddr_t lbn; + + ip = VTOI(vp); + fs = ip->i_e2fs; + lbn = bn; + + /* + * TODO: need to implement read ahead to improve the performance. + */ + if (runp != NULL) + *runp = 0; + + if (runb != NULL) + *runb = 0; + + ext4_ext_find_extent(fs, ip, lbn, &path); + ep = path.ep_ext; + if (ep == NULL) + return (EIO); + + *bnp = fsbtodb(fs, lbn - ep->e_blk + + (ep->e_start_lo | (daddr_t)ep->e_start_hi << 32)); + + if (*bnp == 0) + *bnp = -1; + + return (0); +} + /* * Indirect blocks are now on the vnode for the file. They are given negative * logical block numbers. Indirect blocks are addressed by the negative @@ -91,7 +139,7 @@ ext2_bmap(struct vop_bmap_args *ap) */ int -ext2_bmaparray(struct vnode *vp, int32_t bn, int32_t *bnp, int *runp, int *runb) +ext2_bmaparray(struct vnode *vp, daddr_t bn, daddr_t *bnp, int *runp, int *runb) { struct inode *ip; struct buf *bp; @@ -134,7 +182,7 @@ ext2_bmaparray(struct vnode *vp, int32_t bn, int32_t *bnp, int *runp, int *runb) if (*bnp == 0) { *bnp = -1; } else if (runp) { - int32_t bnb = bn; + daddr_t bnb = bn; for (++bn; bn < NDADDR && *runp < maxrun && is_sequential(ump, ip->i_db[bn - 1], ip->i_db[bn]); ++bn, ++*runp); @@ -142,7 +190,7 @@ ext2_bmaparray(struct vnode *vp, int32_t bn, int32_t *bnp, int *runp, int *runb) if (runb && (bn > 0)) { for (--bn; (bn >= 0) && (*runb < maxrun) && is_sequential(ump, ip->i_db[bn], - ip->i_db[bn+1]); + ip->i_db[bn + 1]); --bn, ++*runb); } } @@ -191,19 +239,20 @@ ext2_bmaparray(struct vnode *vp, int32_t bn, int32_t *bnp, int *runp, int *runb) } } - daddr = ((int32_t *)bp->b_data)[ap->in_off]; + daddr = ((e2fs_daddr_t *)bp->b_data)[ap->in_off]; if (num == 1 && daddr && runp) { for (bn = ap->in_off + 1; bn < MNINDIR(ump) && *runp < maxrun && is_sequential(ump, - ((int32_t *)bp->b_data)[bn - 1], - ((int32_t *)bp->b_data)[bn]); + ((e2fs_daddr_t *)bp->b_data)[bn - 1], + ((e2fs_daddr_t *)bp->b_data)[bn]); ++bn, ++*runp); bn = ap->in_off; if (runb && bn) { for (--bn; bn >= 0 && *runb < maxrun && - is_sequential(ump, ((int32_t *)bp->b_data)[bn], - ((int32_t *)bp->b_data)[bn+1]); + is_sequential(ump, + ((e2fs_daddr_t *)bp->b_data)[bn], + ((e2fs_daddr_t *)bp->b_data)[bn + 1]); --bn, ++*runb); } } @@ -239,7 +288,7 @@ ext2_bmaparray(struct vnode *vp, int32_t bn, int32_t *bnp, int *runp, int *runb) * once with the offset into the page itself. */ int -ext2_getlbns(struct vnode *vp, int32_t bn, struct indir *ap, int *nump) +ext2_getlbns(struct vnode *vp, daddr_t bn, struct indir *ap, int *nump) { long blockcnt; e2fs_lbn_t metalbn, realbn; diff --git a/sys/fs/ext2fs/ext2_dinode.h b/sys/fs/ext2fs/ext2_dinode.h index 1526cb5bf3c..e492a0858c1 100644 --- a/sys/fs/ext2fs/ext2_dinode.h +++ b/sys/fs/ext2fs/ext2_dinode.h @@ -79,6 +79,8 @@ #define E2DI_HAS_XTIME(ip) (EXT2_HAS_RO_COMPAT_FEATURE(ip->i_e2fs, \ EXT2F_ROCOMPAT_EXTRA_ISIZE)) +#define E2DI_HAS_HUGE_FILE(ip) (EXT2_HAS_RO_COMPAT_FEATURE(ip->i_e2fs, \ + EXT2F_ROCOMPAT_HUGE_FILE)) /* * Constants relative to the data blocks diff --git a/sys/fs/ext2fs/ext2_extents.c b/sys/fs/ext2fs/ext2_extents.c new file mode 100644 index 00000000000..26e6a222cc9 --- /dev/null +++ b/sys/fs/ext2fs/ext2_extents.c @@ -0,0 +1,177 @@ +/*- + * Copyright (c) 2010 Zheng Liu + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + * $FreeBSD$ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +static void ext4_ext_binsearch_index(struct inode *ip, struct ext4_extent_path + *path, daddr_t lbn) +{ + struct ext4_extent_header *ehp = path->ep_header; + struct ext4_extent_index *l, *r, *m; + + l = (struct ext4_extent_index *)(char *)(ehp + 1); + r = (struct ext4_extent_index *)(char *)(ehp + 1) + ehp->eh_ecount - 1; + while (l <= r) { + m = l + (r - l) / 2; + if (lbn < m->ei_blk) + r = m - 1; + else + l = m + 1; + } + + path->ep_index = l - 1; +} + +static void +ext4_ext_binsearch(struct inode *ip, struct ext4_extent_path *path, daddr_t lbn) +{ + struct ext4_extent_header *ehp = path->ep_header; + struct ext4_extent *l, *r, *m; + + if (ehp->eh_ecount == 0) + return; + + l = (struct ext4_extent *)(char *)(ehp + 1); + r = (struct ext4_extent *)(char *)(ehp + 1) + ehp->eh_ecount - 1; + while (l <= r) { + m = l + (r - l) / 2; + if (lbn < m->e_blk) + r = m - 1; + else + l = m + 1; + } + + path->ep_ext = l - 1; +} + +/* + * Find a block in ext4 extent cache. + */ +int +ext4_ext_in_cache(struct inode *ip, daddr_t lbn, struct ext4_extent *ep) +{ + struct ext4_extent_cache *ecp; + int ret = EXT4_EXT_CACHE_NO; + + ecp = &ip->i_ext_cache; + + /* cache is invalid */ + if (ecp->ec_type == EXT4_EXT_CACHE_NO) + return (ret); + + if (lbn >= ecp->ec_blk && lbn < ecp->ec_blk + ecp->ec_len) { + ep->e_blk = ecp->ec_blk; + ep->e_start_lo = ecp->ec_start & 0xffffffff; + ep->e_start_hi = ecp->ec_start >> 32 & 0xffff; + ep->e_len = ecp->ec_len; + ret = ecp->ec_type; + } + return (ret); +} + +/* + * Put an ext4_extent structure in ext4 cache. + */ +void +ext4_ext_put_cache(struct inode *ip, struct ext4_extent *ep, int type) +{ + struct ext4_extent_cache *ecp; + + ecp = &ip->i_ext_cache; + ecp->ec_type = type; + ecp->ec_blk = ep->e_blk; + ecp->ec_len = ep->e_len; + ecp->ec_start = (daddr_t)ep->e_start_hi << 32 | ep->e_start_lo; +} + +/* + * Find an extent. + */ +struct ext4_extent_path * +ext4_ext_find_extent(struct m_ext2fs *fs, struct inode *ip, + daddr_t lbn, struct ext4_extent_path *path) +{ + struct vnode *vp; + struct ext4_extent_header *ehp; + uint16_t i; + int error, size; + daddr_t nblk; + + vp = ITOV(ip); + ehp = (struct ext4_extent_header *)(char *)ip->i_db; + + if (ehp->eh_magic != EXT4_EXT_MAGIC) + return (NULL); + + path->ep_header = ehp; + + for (i = ehp->eh_depth; i != 0; --i) { + ext4_ext_binsearch_index(ip, path, lbn); + path->ep_depth = 0; + path->ep_ext = NULL; + + nblk = (daddr_t)path->ep_index->ei_leaf_hi << 32 | + path->ep_index->ei_leaf_lo; + size = blksize(fs, ip, nblk); + if (path->ep_bp != NULL) { + brelse(path->ep_bp); + path->ep_bp = NULL; + } + error = bread(ip->i_devvp, fsbtodb(fs, nblk), size, NOCRED, + &path->ep_bp); + if (error) { + brelse(path->ep_bp); + path->ep_bp = NULL; + return (NULL); + } + ehp = (struct ext4_extent_header *)path->ep_bp->b_data; + path->ep_header = ehp; + } + + path->ep_depth = i; + path->ep_ext = NULL; + path->ep_index = NULL; + + ext4_ext_binsearch(ip, path, lbn); + return (path); +} diff --git a/sys/fs/ext2fs/ext2_extents.h b/sys/fs/ext2fs/ext2_extents.h new file mode 100644 index 00000000000..89aedc5fead --- /dev/null +++ b/sys/fs/ext2fs/ext2_extents.h @@ -0,0 +1,99 @@ +/*- + * Copyright (c) 2012, 2010 Zheng Liu + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + * $FreeBSD$ + */ +#ifndef _FS_EXT2FS_EXT2_EXTENTS_H_ +#define _FS_EXT2FS_EXT2_EXTENTS_H_ + +#include + +#define EXT4_EXT_MAGIC 0xf30a + +#define EXT4_EXT_CACHE_NO 0 +#define EXT4_EXT_CACHE_GAP 1 +#define EXT4_EXT_CACHE_IN 2 + +/* + * Ext4 file system extent on disk. + */ +struct ext4_extent { + uint32_t e_blk; /* first logical block */ + uint16_t e_len; /* number of blocks */ + uint16_t e_start_hi; /* high 16 bits of physical block */ + uint32_t e_start_lo; /* low 32 bits of physical block */ +}; + +/* + * Extent index on disk. + */ +struct ext4_extent_index { + uint32_t ei_blk; /* indexes logical blocks */ + uint32_t ei_leaf_lo; /* points to physical block of the + * next level */ + uint16_t ei_leaf_hi; /* high 16 bits of physical block */ + uint16_t ei_unused; +}; + +/* + * Extent tree header. + */ +struct ext4_extent_header { + uint16_t eh_magic; /* magic number: 0xf30a */ + uint16_t eh_ecount; /* number of valid entries */ + uint16_t eh_max; /* capacity of store in entries */ + uint16_t eh_depth; /* the depth of extent tree */ + uint32_t eh_gen; /* generation of extent tree */ +}; + +/* + * Save cached extent. + */ +struct ext4_extent_cache { + daddr_t ec_start; /* extent start */ + uint32_t ec_blk; /* logical block */ + uint32_t ec_len; + uint32_t ec_type; +}; + +/* + * Save path to some extent. + */ +struct ext4_extent_path { + uint16_t ep_depth; + struct buf *ep_bp; + struct ext4_extent *ep_ext; + struct ext4_extent_index *ep_index; + struct ext4_extent_header *ep_header; +}; + +struct inode; +struct m_ext2fs; +int ext4_ext_in_cache(struct inode *, daddr_t, struct ext4_extent *); +void ext4_ext_put_cache(struct inode *, struct ext4_extent *, int); +struct ext4_extent_path *ext4_ext_find_extent(struct m_ext2fs *fs, + struct inode *, daddr_t, struct ext4_extent_path *); + +#endif /* !_FS_EXT2FS_EXT2_EXTENTS_H_ */ diff --git a/sys/fs/ext2fs/ext2_extern.h b/sys/fs/ext2fs/ext2_extern.h index f9c87cb6782..b6a7dfca3c3 100644 --- a/sys/fs/ext2fs/ext2_extern.h +++ b/sys/fs/ext2fs/ext2_extern.h @@ -49,24 +49,24 @@ struct vfsconf; struct vnode; int ext2_add_entry(struct vnode *, struct ext2fs_direct_2 *); -int ext2_alloc(struct inode *, - int32_t, int32_t, int, struct ucred *, int32_t *); +int ext2_alloc(struct inode *, daddr_t, e4fs_daddr_t, int, + struct ucred *, e4fs_daddr_t *); int ext2_balloc(struct inode *, e2fs_lbn_t, int, struct ucred *, struct buf **, int); int ext2_blkatoff(struct vnode *, off_t, char **, struct buf **); -void ext2_blkfree(struct inode *, int32_t, long); -int32_t ext2_blkpref(struct inode *, e2fs_lbn_t, int, int32_t *, int32_t); +void ext2_blkfree(struct inode *, e4fs_daddr_t, long); +e4fs_daddr_t ext2_blkpref(struct inode *, e2fs_lbn_t, int, e2fs_daddr_t *, + e2fs_daddr_t); int ext2_bmap(struct vop_bmap_args *); -int ext2_bmaparray(struct vnode *, int32_t, int32_t *, int *, int *); +int ext2_bmaparray(struct vnode *, daddr_t, daddr_t *, int *, int *); void ext2_clusteracct(struct m_ext2fs *, char *, int, daddr_t, int); void ext2_dirbad(struct inode *ip, doff_t offset, char *how); void ext2_ei2i(struct ext2fs_dinode *, struct inode *); -int ext2_getlbns(struct vnode *, int32_t, struct indir *, int *); +int ext2_getlbns(struct vnode *, daddr_t, struct indir *, int *); void ext2_i2ei(struct inode *, struct ext2fs_dinode *); void ext2_itimes(struct vnode *vp); int ext2_reallocblks(struct vop_reallocblks_args *); int ext2_reclaim(struct vop_reclaim_args *); -void ext2_setblock(struct m_ext2fs *, u_char *, int32_t); int ext2_truncate(struct vnode *, off_t, int, struct ucred *, struct thread *); int ext2_update(struct vnode *, int); int ext2_valloc(struct vnode *, int, struct ucred *, struct vnode **); diff --git a/sys/fs/ext2fs/ext2_hash.c b/sys/fs/ext2fs/ext2_hash.c index 4d69f64444e..85849b0c728 100644 --- a/sys/fs/ext2fs/ext2_hash.c +++ b/sys/fs/ext2fs/ext2_hash.c @@ -73,7 +73,7 @@ /* * FF, GG, and HH are transformations for rounds 1, 2, and 3. - * Rotation is separated from addition to prevent recompuatation + * Rotation is separated from addition to prevent recomputation. */ #define FF(a, b, c, d, x, s) { \ (a) += F ((b), (c), (d)) + (x); \ diff --git a/sys/fs/ext2fs/ext2_htree.c b/sys/fs/ext2fs/ext2_htree.c index 0b5d9205b61..ff1e1a5ef61 100644 --- a/sys/fs/ext2fs/ext2_htree.c +++ b/sys/fs/ext2fs/ext2_htree.c @@ -89,10 +89,12 @@ static int ext2_htree_writebuf(struct ext2fs_htree_lookup_info *info); int ext2_htree_has_idx(struct inode *ip) { +#ifdef EXT2FS_HTREE if (EXT2_HAS_COMPAT_FEATURE(ip->i_e2fs, EXT2F_COMPAT_DIRHASHINDEX) && ip->i_flags & EXT4_INDEX) return (1); else +#endif return (0); } diff --git a/sys/fs/ext2fs/ext2_inode.c b/sys/fs/ext2fs/ext2_inode.c index d1e872c1fd7..11180f0447d 100644 --- a/sys/fs/ext2fs/ext2_inode.c +++ b/sys/fs/ext2fs/ext2_inode.c @@ -54,8 +54,8 @@ #include #include -static int ext2_indirtrunc(struct inode *, int32_t, int32_t, int32_t, int, - long *); +static int ext2_indirtrunc(struct inode *, daddr_t, daddr_t, + daddr_t, int, e4fs_daddr_t *); /* * Update the access, modified, and inode change times as specified by the @@ -119,7 +119,7 @@ ext2_truncate(struct vnode *vp, off_t length, int flags, struct ucred *cred, struct m_ext2fs *fs; struct buf *bp; int offset, size, level; - long count, nblocks, blocksreleased = 0; + e4fs_daddr_t count, nblocks, blocksreleased = 0; int error, i, allerror; off_t osize; @@ -356,16 +356,16 @@ done: */ static int -ext2_indirtrunc(struct inode *ip, int32_t lbn, int32_t dbn, int32_t lastbn, - int level, long *countp) +ext2_indirtrunc(struct inode *ip, daddr_t lbn, daddr_t dbn, + daddr_t lastbn, int level, e4fs_daddr_t *countp) { struct buf *bp; struct m_ext2fs *fs = ip->i_e2fs; struct vnode *vp; - int32_t *bap, *copy, nb, nlbn, last; - long blkcount, factor; - int i, nblocks, blocksreleased = 0; - int error = 0, allerror = 0; + e2fs_daddr_t *bap, *copy; + int i, nblocks, error = 0, allerror = 0; + e2fs_lbn_t nb, nlbn, last; + e4fs_daddr_t blkcount, factor, blocksreleased = 0; /* * Calculate index in current block of last @@ -405,11 +405,11 @@ ext2_indirtrunc(struct inode *ip, int32_t lbn, int32_t dbn, int32_t lastbn, return (error); } - bap = (int32_t *)bp->b_data; + bap = (e2fs_daddr_t *)bp->b_data; copy = malloc(fs->e2fs_bsize, M_TEMP, M_WAITOK); bcopy((caddr_t)bap, (caddr_t)copy, (u_int)fs->e2fs_bsize); bzero((caddr_t)&bap[last + 1], - (u_int)(NINDIR(fs) - (last + 1)) * sizeof(int32_t)); + (NINDIR(fs) - (last + 1)) * sizeof(e2fs_daddr_t)); if (last == -1) bp->b_flags |= B_INVAL; if (DOINGASYNC(vp)) { diff --git a/sys/fs/ext2fs/ext2_inode_cnv.c b/sys/fs/ext2fs/ext2_inode_cnv.c index cade4a6869d..c26784b3ffc 100644 --- a/sys/fs/ext2fs/ext2_inode_cnv.c +++ b/sys/fs/ext2fs/ext2_inode_cnv.c @@ -32,6 +32,7 @@ #include #include +#include #include #include #include @@ -44,22 +45,34 @@ void ext2_print_inode(struct inode *in) { int i; + struct ext4_extent_header *ehp; + struct ext4_extent *ep; printf( "Inode: %5ju", (uintmax_t)in->i_number); printf( /* "Inode: %5d" */ " Type: %10s Mode: 0x%o Flags: 0x%x Version: %d\n", "n/a", in->i_mode, in->i_flags, in->i_gen); - printf( "User: %5lu Group: %5lu Size: %lu\n", - (unsigned long)in->i_uid, (unsigned long)in->i_gid, - (unsigned long)in->i_size); - printf( "Links: %3d Blockcount: %d\n", - in->i_nlink, in->i_blocks); + printf("User: %5u Group: %5u Size: %ju\n", + in->i_uid, in->i_gid, (uintmax_t)in->i_size); + printf("Links: %3d Blockcount: %ju\n", + in->i_nlink, (uintmax_t)in->i_blocks); printf( "ctime: 0x%x", in->i_ctime); printf( "atime: 0x%x", in->i_atime); printf( "mtime: 0x%x", in->i_mtime); - printf( "BLOCKS: "); - for(i=0; i < (in->i_blocks <= 24 ? ((in->i_blocks+1)/2): 12); i++) - printf("%d ", in->i_db[i]); + if (E2DI_HAS_XTIME(in)) + printf("crtime %#x ", in->i_birthtime); + printf("BLOCKS:"); + for (i = 0; i < (in->i_blocks <= 24 ? (in->i_blocks + 1) / 2 : 12); i++) + printf(" %d", in->i_db[i]); + printf("\n"); + printf("Extents:\n"); + ehp = (struct ext4_extent_header *)in->i_db; + printf("Header (magic 0x%x entries %d max %d depth %d gen %d)\n", + ehp->eh_magic, ehp->eh_ecount, ehp->eh_max, ehp->eh_depth, + ehp->eh_gen); + ep = (struct ext4_extent *)(char *)(ehp + 1); + printf("Index (blk %d len %d start_lo %d start_hi %d)\n", ep->e_blk, + ep->e_len, ep->e_start_lo, ep->e_start_hi); printf("\n"); } @@ -96,6 +109,11 @@ ext2_ei2i(struct ext2fs_dinode *ei, struct inode *ip) ip->i_flags |= (ei->e2di_flags & EXT2_IMMUTABLE) ? SF_IMMUTABLE : 0; ip->i_flags |= (ei->e2di_flags & EXT2_NODUMP) ? UF_NODUMP : 0; ip->i_blocks = ei->e2di_nblock; + if (E2DI_HAS_HUGE_FILE(ip)) { + ip->i_blocks |= (uint64_t)ei->e2di_nblock_high << 32; + if (ei->e2di_flags & EXT4_HUGE_FILE) + ip->i_blocks = fsbtodb(ip->i_e2fs, ip->i_blocks); + } ip->i_gen = ei->e2di_gen; ip->i_uid = ei->e2di_uid; ip->i_gid = ei->e2di_gid; @@ -138,7 +156,8 @@ ext2_i2ei(struct inode *ip, struct ext2fs_dinode *ei) ei->e2di_flags |= (ip->i_flags & SF_APPEND) ? EXT2_APPEND: 0; ei->e2di_flags |= (ip->i_flags & SF_IMMUTABLE) ? EXT2_IMMUTABLE: 0; ei->e2di_flags |= (ip->i_flags & UF_NODUMP) ? EXT2_NODUMP: 0; - ei->e2di_nblock = ip->i_blocks; + ei->e2di_nblock = ip->i_blocks & 0xffffffff; + ei->e2di_nblock_high = ip->i_blocks >> 32 & 0xffff; ei->e2di_gen = ip->i_gen; ei->e2di_uid = ip->i_uid; ei->e2di_gid = ip->i_gid; diff --git a/sys/fs/ext2fs/ext2_lookup.c b/sys/fs/ext2fs/ext2_lookup.c index 990ed330217..d6075542fa8 100644 --- a/sys/fs/ext2fs/ext2_lookup.c +++ b/sys/fs/ext2fs/ext2_lookup.c @@ -884,6 +884,7 @@ ext2_direnter(struct inode *ip, struct vnode *dvp, struct componentname *cnp) bcopy(cnp->cn_nameptr, newdir.e2d_name, (unsigned)cnp->cn_namelen + 1); newentrysize = EXT2_DIR_REC_LEN(newdir.e2d_namlen); +#ifdef EXT2FS_HTREE if (ext2_htree_has_idx(dp)) { error = ext2_htree_add_entry(dvp, &newdir, cnp); if (error) { @@ -904,6 +905,7 @@ ext2_direnter(struct inode *ip, struct vnode *dvp, struct componentname *cnp) return ext2_htree_create_index(dvp, cnp, &newdir); } } +#endif /* EXT2FS_HTREE */ if (dp->i_count == 0) { /* diff --git a/sys/fs/ext2fs/ext2_subr.c b/sys/fs/ext2fs/ext2_subr.c index df6e430f0f0..ec10ea86dab 100644 --- a/sys/fs/ext2fs/ext2_subr.c +++ b/sys/fs/ext2fs/ext2_subr.c @@ -50,10 +50,11 @@ #include #include #include +#include +#include +#include #ifdef KDB -#include - void ext2_checkoverlap(struct buf *, struct inode *); #endif @@ -70,21 +71,63 @@ ext2_blkatoff(struct vnode *vp, off_t offset, char **res, struct buf **bpp) struct buf *bp; e2fs_lbn_t lbn; int bsize, error; + daddr_t newblk; + struct ext4_extent *ep; + struct ext4_extent_path path; ip = VTOI(vp); fs = ip->i_e2fs; lbn = lblkno(fs, offset); bsize = blksize(fs, ip, lbn); - *bpp = NULL; - if ((error = bread(vp, lbn, bsize, NOCRED, &bp)) != 0) { + + /* + * The EXT4_EXTENTS requires special treatment, otherwise we can + * fall back to the normal path. + */ + if (!(ip->i_flags & EXT4_EXTENTS)) + goto normal; + + memset(&path, 0, sizeof(path)); + if (ext4_ext_find_extent(fs, ip, lbn, &path) == NULL) + goto normal; + ep = path.ep_ext; + if (ep == NULL) + goto normal; + + newblk = lbn - ep->e_blk + + (ep->e_start_lo | (daddr_t)ep->e_start_hi << 32); + + if (path.ep_bp != NULL) { + brelse(path.ep_bp); + path.ep_bp = NULL; + } + error = bread(ip->i_devvp, fsbtodb(fs, newblk), bsize, NOCRED, &bp); + if (error != 0) { brelse(bp); return (error); } if (res) *res = (char *)bp->b_data + blkoff(fs, offset); + /* + * If EXT4_EXTENTS is enabled we would get a wrong offset so + * reset b_offset here. + */ + bp->b_offset = lbn * bsize; *bpp = bp; return (0); + +normal: + if (*bpp == NULL) { + if ((error = bread(vp, lbn, bsize, NOCRED, &bp)) != 0) { + brelse(bp); + return (error); + } + if (res) + *res = (char *)bp->b_data + blkoff(fs, offset); + *bpp = bp; + } + return (0); } #ifdef KDB @@ -92,7 +135,7 @@ void ext2_checkoverlap(struct buf *bp, struct inode *ip) { struct buf *ebp, *ep; - int32_t start, last; + e4fs_daddr_t start, last; struct vnode *vp; ebp = &buf[nbuf]; @@ -107,10 +150,10 @@ ext2_checkoverlap(struct buf *bp, struct inode *ip) ep->b_blkno + btodb(ep->b_bcount) <= start) continue; vprint("Disk overlap", vp); - (void)printf("\tstart %d, end %d overlap start %lld, end %ld\n", - start, last, (long long)ep->b_blkno, - (long)(ep->b_blkno + btodb(ep->b_bcount) - 1)); - panic("Disk buffer overlap"); + printf("\tstart %jd, end %jd overlap start %jd, end %jd\n", + (intmax_t)start, (intmax_t)last, (intmax_t)ep->b_blkno, + (intmax_t)(ep->b_blkno + btodb(ep->b_bcount) - 1)); + panic("ext2_checkoverlap: Disk buffer overlap"); } } #endif /* KDB */ diff --git a/sys/fs/ext2fs/ext2_vfsops.c b/sys/fs/ext2fs/ext2_vfsops.c index 1c8e1aa3dc1..b4683564bd8 100644 --- a/sys/fs/ext2fs/ext2_vfsops.c +++ b/sys/fs/ext2fs/ext2_vfsops.c @@ -397,9 +397,11 @@ compute_sb_data(struct vnode *devvp, struct ext2fs *es, if (es->e2fs_rev == E2FS_REV0 || !EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_LARGEFILE)) fs->e2fs_maxfilesize = 0x7fffffff; - else - fs->e2fs_maxfilesize = 0x7fffffffffffffff; - + else { + fs->e2fs_maxfilesize = 0xffffffffffff; + if (EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_HUGE_FILE)) + fs->e2fs_maxfilesize = 0x7fffffffffffffff; + } if (es->e4fs_flags & E2FS_UNSIGNED_HASH) { fs->e2fs_uhash = 3; } else if ((es->e4fs_flags & E2FS_SIGNED_HASH) == 0) { @@ -961,8 +963,12 @@ ext2_vget(struct mount *mp, ino_t ino, int flags, struct vnode **vpp) * Now we want to make sure that block pointers for unused * blocks are zeroed out - ext2_balloc depends on this * although for regular files and directories only + * + * If EXT4_EXTENTS flag is enabled, unused blocks aren't + * zeroed out because we could corrupt the extent tree. */ - if(S_ISDIR(ip->i_mode) || S_ISREG(ip->i_mode)) { + if (!(ip->i_flags & EXT4_EXTENTS) && + (S_ISDIR(ip->i_mode) || S_ISREG(ip->i_mode))) { used_blocks = (ip->i_size+fs->e2fs_bsize-1) / fs->e2fs_bsize; for (i = used_blocks; i < EXT2_NDIR_BLOCKS; i++) ip->i_db[i] = 0; diff --git a/sys/fs/ext2fs/ext2_vnops.c b/sys/fs/ext2fs/ext2_vnops.c index c2f8a8fe9f9..c5be7d06f89 100644 --- a/sys/fs/ext2fs/ext2_vnops.c +++ b/sys/fs/ext2fs/ext2_vnops.c @@ -84,6 +84,8 @@ static int ext2_makeinode(int mode, struct vnode *, struct vnode **, struct componentname *); static void ext2_itimes_locked(struct vnode *); +static int ext4_ext_read(struct vop_read_args *); +static int ext2_ind_read(struct vop_read_args *); static vop_access_t ext2_access; static int ext2_chmod(struct vnode *, int, struct ucred *, struct thread *); @@ -1327,7 +1329,7 @@ ext2_strategy(struct vop_strategy_args *ap) struct vnode *vp = ap->a_vp; struct inode *ip; struct bufobj *bo; - int32_t blkno; + daddr_t blkno; int error; ip = VTOI(vp); @@ -1604,6 +1606,29 @@ bad: */ static int ext2_read(struct vop_read_args *ap) +{ + struct vnode *vp; + struct inode *ip; + int error; + + vp = ap->a_vp; + ip = VTOI(vp); + + /*EXT4_EXT_LOCK(ip);*/ + if (ip->i_flags & EXT4_EXTENTS) + error = ext4_ext_read(ap); + else + error = ext2_ind_read(ap); + /*EXT4_EXT_UNLOCK(ip);*/ + return (error); +} + + +/* + * Vnode op for reading. + */ +static int +ext2_ind_read(struct vop_read_args *ap) { struct vnode *vp; struct inode *ip; @@ -1757,6 +1782,107 @@ ext2_ioctl(struct vop_ioctl_args *ap) } } +/* + * this function handles ext4 extents block mapping + */ +static int +ext4_ext_read(struct vop_read_args *ap) +{ + struct vnode *vp; + struct inode *ip; + struct uio *uio; + struct m_ext2fs *fs; + struct buf *bp; + struct ext4_extent nex, *ep; + struct ext4_extent_path path; + daddr_t lbn, newblk; + off_t bytesinfile; + int cache_type; + ssize_t orig_resid; + int error; + long size, xfersize, blkoffset; + + vp = ap->a_vp; + ip = VTOI(vp); + uio = ap->a_uio; + memset(&path, 0, sizeof(path)); + + orig_resid = uio->uio_resid; + KASSERT(orig_resid >= 0, ("%s: uio->uio_resid < 0", __func__)); + if (orig_resid == 0) + return (0); + KASSERT(uio->uio_offset >= 0, ("%s: uio->uio_offset < 0", __func__)); + fs = ip->i_e2fs; + if (uio->uio_offset < ip->i_size && uio->uio_offset >= fs->e2fs_maxfilesize) + return (EOVERFLOW); + + while (uio->uio_resid > 0) { + if ((bytesinfile = ip->i_size - uio->uio_offset) <= 0) + break; + lbn = lblkno(fs, uio->uio_offset); + size = blksize(fs, ip, lbn); + blkoffset = blkoff(fs, uio->uio_offset); + + xfersize = fs->e2fs_fsize - blkoffset; + xfersize = MIN(xfersize, uio->uio_resid); + xfersize = MIN(xfersize, bytesinfile); + + /* get block from ext4 extent cache */ + cache_type = ext4_ext_in_cache(ip, lbn, &nex); + switch (cache_type) { + case EXT4_EXT_CACHE_NO: + ext4_ext_find_extent(fs, ip, lbn, &path); + ep = path.ep_ext; + if (ep == NULL) + return (EIO); + + ext4_ext_put_cache(ip, ep, EXT4_EXT_CACHE_IN); + + newblk = lbn - ep->e_blk + (ep->e_start_lo | + (daddr_t)ep->e_start_hi << 32); + + if (path.ep_bp != NULL) { + brelse(path.ep_bp); + path.ep_bp = NULL; + } + break; + + case EXT4_EXT_CACHE_GAP: + /* block has not been allocated yet */ + return (0); + + case EXT4_EXT_CACHE_IN: + newblk = lbn - nex.e_blk + (nex.e_start_lo | + (daddr_t)nex.e_start_hi << 32); + break; + + default: + panic("%s: invalid cache type", __func__); + } + + error = bread(ip->i_devvp, fsbtodb(fs, newblk), size, NOCRED, &bp); + if (error) { + brelse(bp); + return (error); + } + + size -= bp->b_resid; + if (size < xfersize) { + if (size == 0) { + bqrelse(bp); + break; + } + xfersize = size; + } + error = uiomove(bp->b_data + blkoffset, (int)xfersize, uio); + bqrelse(bp); + if (error) + return (error); + } + + return (0); +} + /* * Vnode op for writing. */ diff --git a/sys/fs/ext2fs/ext2fs.h b/sys/fs/ext2fs/ext2fs.h index 7b16f0fcd40..b562287cac5 100644 --- a/sys/fs/ext2fs/ext2fs.h +++ b/sys/fs/ext2fs/ext2fs.h @@ -201,12 +201,18 @@ struct csum { * - EXT2F_ROCOMPAT_SPARSESUPER * - EXT2F_ROCOMPAT_LARGEFILE * - EXT2F_INCOMPAT_FTYPE + * + * We partially (read-only) support the following EXT4 features: + * - EXT2F_ROCOMPAT_HUGE_FILE + * - EXT2F_ROCOMPAT_EXTRA_ISIZE + * - EXT2F_INCOMPAT_EXTENTS */ #define EXT2F_COMPAT_SUPP 0x0000 #define EXT2F_ROCOMPAT_SUPP (EXT2F_ROCOMPAT_SPARSESUPER | \ EXT2F_ROCOMPAT_LARGEFILE | \ EXT2F_ROCOMPAT_EXTRA_ISIZE) -#define EXT2F_INCOMPAT_SUPP EXT2F_INCOMPAT_FTYPE +#define EXT2F_INCOMPAT_SUPP (EXT2F_INCOMPAT_FTYPE | \ + EXT2F_INCOMPAT_EXTENTS) /* Assume that user mode programs are passing in an ext2fs superblock, not * a kernel struct super_block. This will allow us to call the feature-test diff --git a/sys/fs/ext2fs/inode.h b/sys/fs/ext2fs/inode.h index d939987965e..e2016c51a02 100644 --- a/sys/fs/ext2fs/inode.h +++ b/sys/fs/ext2fs/inode.h @@ -38,9 +38,13 @@ #ifndef _FS_EXT2FS_INODE_H_ #define _FS_EXT2FS_INODE_H_ +#include #include +#include #include +#include + /* * This must agree with the definition in . */ @@ -50,9 +54,11 @@ #define NIADDR 3 /* Indirect addresses in inode. */ /* - * The size of physical and logical block numbers and time fields in UFS. + * The size of physical and logical block numbers in EXT2FS. */ -typedef int32_t e2fs_lbn_t; +typedef uint32_t e2fs_daddr_t; +typedef int64_t e2fs_lbn_t; +typedef int64_t e4fs_daddr_t; /* * The inode is used to describe each active (or recently active) file in the @@ -86,7 +92,10 @@ struct inode { /* Fields from struct dinode in UFS. */ uint16_t i_mode; /* IFMT, permissions; see below. */ int16_t i_nlink; /* File link count. */ + uint32_t i_uid; /* File owner. */ + uint32_t i_gid; /* File group. */ uint64_t i_size; /* File byte count. */ + uint64_t i_blocks; /* Blocks actually held. */ int32_t i_atime; /* Last access time. */ int32_t i_mtime; /* Last modified time. */ int32_t i_ctime; /* Last inode change time. */ @@ -95,13 +104,12 @@ struct inode { int32_t i_atimensec; /* Last access time. */ int32_t i_ctimensec; /* Last inode change time. */ int32_t i_birthnsec; /* Inode creation time. */ + uint32_t i_gen; /* Generation number. */ + uint32_t i_flags; /* Status flags (chflags). */ uint32_t i_db[NDADDR]; /* Direct disk blocks. */ uint32_t i_ib[NIADDR]; /* Indirect disk blocks. */ - uint32_t i_flags; /* Status flags (chflags). */ - uint32_t i_blocks; /* Blocks actually held. */ - uint32_t i_gen; /* Generation number. */ - uint32_t i_uid; /* File owner. */ - uint32_t i_gid; /* File group. */ + + struct ext4_extent_cache i_ext_cache; /* cache for ext4 extent */ }; /* diff --git a/sys/fs/fdescfs/fdesc_vnops.c b/sys/fs/fdescfs/fdesc_vnops.c index f18c0fc13f9..b976504353c 100644 --- a/sys/fs/fdescfs/fdesc_vnops.c +++ b/sys/fs/fdescfs/fdesc_vnops.c @@ -309,7 +309,7 @@ fdesc_lookup(ap) /* * No rights to check since 'fp' isn't actually used. */ - if ((error = fget(td, fd, 0, &fp)) != 0) + if ((error = fget(td, fd, NULL, &fp)) != 0) goto bad; /* Check if we're looking up ourselves. */ @@ -445,6 +445,7 @@ fdesc_setattr(ap) struct mount *mp; struct file *fp; struct thread *td = curthread; + cap_rights_t rights; unsigned fd; int error; @@ -459,7 +460,8 @@ fdesc_setattr(ap) /* * Allow setattr where there is an underlying vnode. */ - error = getvnode(td->td_proc->p_fd, fd, CAP_EXTATTR_SET, &fp); + error = getvnode(td->td_proc->p_fd, fd, + cap_rights_init(&rights, CAP_EXTATTR_SET), &fp); if (error) { /* * getvnode() returns EINVAL if the file descriptor is not diff --git a/sys/fs/fuse/fuse_vfsops.c b/sys/fs/fuse/fuse_vfsops.c index 639550a88c8..0b4f19b9ea4 100644 --- a/sys/fs/fuse/fuse_vfsops.c +++ b/sys/fs/fuse/fuse_vfsops.c @@ -220,6 +220,7 @@ fuse_vfsop_mount(struct mount *mp) struct file *fp, *fptmp; char *fspec, *subtype; struct vfsoptlist *opts; + cap_rights_t rights; subtype = NULL; max_read_set = 0; @@ -289,7 +290,7 @@ fuse_vfsop_mount(struct mount *mp) FS_DEBUG2G("mntopts 0x%jx\n", (uintmax_t)mntopts); - err = fget(td, fd, CAP_READ, &fp); + err = fget(td, fd, cap_rights_init(&rights, CAP_READ), &fp); if (err != 0) { FS_DEBUG("invalid or not opened device: data=%p\n", data); goto out; diff --git a/sys/fs/fuse/fuse_vnops.c b/sys/fs/fuse/fuse_vnops.c index 69c511dc16b..690d8d09d92 100644 --- a/sys/fs/fuse/fuse_vnops.c +++ b/sys/fs/fuse/fuse_vnops.c @@ -1854,36 +1854,8 @@ fuse_vnop_getpages(struct vop_getpages_args *ap) */ ; } - if (i != ap->a_reqpage) { - /* - * Whether or not to leave the page activated is up in - * the air, but we should put the page on a page queue - * somewhere (it already is in the object). Result: - * It appears that emperical results show that - * deactivating pages is best. - */ - - /* - * Just in case someone was asking for this page we - * now tell them that it is ok to use. - */ - if (!error) { - if (m->oflags & VPO_WANTED) { - fuse_vm_page_lock(m); - vm_page_activate(m); - fuse_vm_page_unlock(m); - } else { - fuse_vm_page_lock(m); - vm_page_deactivate(m); - fuse_vm_page_unlock(m); - } - vm_page_wakeup(m); - } else { - fuse_vm_page_lock(m); - vm_page_free(m); - fuse_vm_page_unlock(m); - } - } + if (i != ap->a_reqpage) + vm_page_readahead_finish(m); } fuse_vm_page_unlock_queues(); VM_OBJECT_WUNLOCK(vp->v_object); diff --git a/sys/fs/msdosfs/msdosfs_denode.c b/sys/fs/msdosfs/msdosfs_denode.c index 501604affa2..b2a51eafb00 100644 --- a/sys/fs/msdosfs/msdosfs_denode.c +++ b/sys/fs/msdosfs/msdosfs_denode.c @@ -304,8 +304,8 @@ deupdat(dep, waitfor) if ((dep->de_flag & DE_MODIFIED) == 0 && waitfor == 0) return (0); dep->de_flag &= ~DE_MODIFIED; - if (dep->de_Attributes & ATTR_DIRECTORY) - return (0); + if (DETOV(dep)->v_vflag & VV_ROOT) + return (EINVAL); if (dep->de_refcnt <= 0) return (0); error = readde(dep, &bp, &dirp); diff --git a/sys/fs/msdosfs/msdosfs_vnops.c b/sys/fs/msdosfs/msdosfs_vnops.c index da9a5dfc7a4..04cb3721e5b 100644 --- a/sys/fs/msdosfs/msdosfs_vnops.c +++ b/sys/fs/msdosfs/msdosfs_vnops.c @@ -172,8 +172,7 @@ msdosfs_create(ap) if (error) goto bad; - ndirent.de_Attributes = (ap->a_vap->va_mode & VWRITE) ? - ATTR_ARCHIVE : ATTR_ARCHIVE | ATTR_READONLY; + ndirent.de_Attributes = ATTR_ARCHIVE; ndirent.de_LowerCase = 0; ndirent.de_StartCluster = 0; ndirent.de_FileSize = 0; @@ -256,8 +255,7 @@ msdosfs_access(ap) mode_t file_mode; accmode_t accmode = ap->a_accmode; - file_mode = (S_IXUSR|S_IXGRP|S_IXOTH) | (S_IRUSR|S_IRGRP|S_IROTH) | - ((dep->de_Attributes & ATTR_READONLY) ? 0 : (S_IWUSR|S_IWGRP|S_IWOTH)); + file_mode = S_IRWXU|S_IRWXG|S_IRWXO; file_mode &= (vp->v_type == VDIR ? pmp->pm_dirmask : pmp->pm_mask); /* @@ -266,8 +264,8 @@ msdosfs_access(ap) */ if (accmode & VWRITE) { switch (vp->v_type) { - case VDIR: case VREG: + case VDIR: if (vp->v_mount->mnt_flag & MNT_RDONLY) return (EROFS); break; @@ -322,10 +320,7 @@ msdosfs_getattr(ap) else vap->va_fileid = (long)fileid; - if ((dep->de_Attributes & ATTR_READONLY) == 0) - mode = S_IRWXU|S_IRWXG|S_IRWXO; - else - mode = S_IRUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH; + mode = S_IRWXU|S_IRWXG|S_IRWXO; vap->va_mode = mode & (ap->a_vp->v_type == VDIR ? pmp->pm_dirmask : pmp->pm_mask); vap->va_uid = pmp->pm_uid; @@ -345,8 +340,14 @@ msdosfs_getattr(ap) vap->va_birthtime.tv_nsec = 0; } vap->va_flags = 0; - if ((dep->de_Attributes & ATTR_ARCHIVE) == 0) - vap->va_flags |= SF_ARCHIVED; + if (dep->de_Attributes & ATTR_ARCHIVE) + vap->va_flags |= UF_ARCHIVE; + if (dep->de_Attributes & ATTR_HIDDEN) + vap->va_flags |= UF_HIDDEN; + if (dep->de_Attributes & ATTR_READONLY) + vap->va_flags |= UF_READONLY; + if (dep->de_Attributes & ATTR_SYSTEM) + vap->va_flags |= UF_SYSTEM; vap->va_gen = 0; vap->va_blocksize = pmp->pm_bpcluster; vap->va_bytes = @@ -395,6 +396,18 @@ msdosfs_setattr(ap) #endif return (EINVAL); } + + /* + * We don't allow setting attributes on the root directory. + * The special case for the root directory is because before + * FAT32, the root directory didn't have an entry for itself + * (and was otherwise special). With FAT32, the root + * directory is not so special, but still doesn't have an + * entry for itself. + */ + if (vp->v_vflag & VV_ROOT) + return (EINVAL); + if (vap->va_flags != VNOVAL) { if (vp->v_mount->mnt_flag & MNT_RDONLY) return (EROFS); @@ -408,24 +421,29 @@ msdosfs_setattr(ap) * attributes. We ignored the access time and the * read and execute bits. We were strict for the other * attributes. - * - * Here we are strict, stricter than ufs in not allowing - * users to attempt to set SF_SETTABLE bits or anyone to - * set unsupported bits. However, we ignore attempts to - * set ATTR_ARCHIVE for directories `cp -pr' from a more - * sensible filesystem attempts it a lot. */ - if (vap->va_flags & SF_SETTABLE) { - error = priv_check_cred(cred, PRIV_VFS_SYSFLAGS, 0); - if (error) - return (error); - } - if (vap->va_flags & ~SF_ARCHIVED) + if (vap->va_flags & ~(UF_ARCHIVE | UF_HIDDEN | UF_READONLY | + UF_SYSTEM)) return EOPNOTSUPP; - if (vap->va_flags & SF_ARCHIVED) - dep->de_Attributes &= ~ATTR_ARCHIVE; - else if (!(dep->de_Attributes & ATTR_DIRECTORY)) + if (vap->va_flags & UF_ARCHIVE) dep->de_Attributes |= ATTR_ARCHIVE; + else + dep->de_Attributes &= ~ATTR_ARCHIVE; + if (vap->va_flags & UF_HIDDEN) + dep->de_Attributes |= ATTR_HIDDEN; + else + dep->de_Attributes &= ~ATTR_HIDDEN; + /* We don't allow changing the readonly bit on directories. */ + if (vp->v_type != VDIR) { + if (vap->va_flags & UF_READONLY) + dep->de_Attributes |= ATTR_READONLY; + else + dep->de_Attributes &= ~ATTR_READONLY; + } + if (vap->va_flags & UF_SYSTEM) + dep->de_Attributes |= ATTR_SYSTEM; + else + dep->de_Attributes &= ~ATTR_SYSTEM; dep->de_flag |= DE_MODIFIED; } @@ -489,21 +507,24 @@ msdosfs_setattr(ap) error = VOP_ACCESS(vp, VWRITE, cred, td); } else error = VOP_ACCESS(vp, VADMIN, cred, td); - if (vp->v_type != VDIR) { - if ((pmp->pm_flags & MSDOSFSMNT_NOWIN95) == 0 && - vap->va_atime.tv_sec != VNOVAL) { - dep->de_flag &= ~DE_ACCESS; - timespec2fattime(&vap->va_atime, 0, - &dep->de_ADate, NULL, NULL); - } - if (vap->va_mtime.tv_sec != VNOVAL) { - dep->de_flag &= ~DE_UPDATE; - timespec2fattime(&vap->va_mtime, 0, - &dep->de_MDate, &dep->de_MTime, NULL); - } - dep->de_Attributes |= ATTR_ARCHIVE; - dep->de_flag |= DE_MODIFIED; + if ((pmp->pm_flags & MSDOSFSMNT_NOWIN95) == 0 && + vap->va_atime.tv_sec != VNOVAL) { + dep->de_flag &= ~DE_ACCESS; + timespec2fattime(&vap->va_atime, 0, + &dep->de_ADate, NULL, NULL); } + if (vap->va_mtime.tv_sec != VNOVAL) { + dep->de_flag &= ~DE_UPDATE; + timespec2fattime(&vap->va_mtime, 0, + &dep->de_MDate, &dep->de_MTime, NULL); + } + /* + * We don't set the archive bit when modifying the time of + * a directory to emulate the Windows/DOS behavior. + */ + if (vp->v_type != VDIR) + dep->de_Attributes |= ATTR_ARCHIVE; + dep->de_flag |= DE_MODIFIED; } /* * DOS files only have the ability to have their writability diff --git a/sys/fs/nfs/nfs.h b/sys/fs/nfs/nfs.h index aaa7fb1caaa..5976934ca0a 100644 --- a/sys/fs/nfs/nfs.h +++ b/sys/fs/nfs/nfs.h @@ -335,11 +335,6 @@ struct nfsreferral { */ #define NFS_NFSSTATS 1 /* struct: struct nfsstats */ -#define FS_NFS_NAMES { \ - { 0, 0 }, \ - { "nfsstats", CTLTYPE_STRUCT }, \ -} - /* * Here is the definition of the attribute bits array and macros that * manipulate it. diff --git a/sys/fs/nfs/nfsport.h b/sys/fs/nfs/nfsport.h index f0e20f19623..cec50a65709 100644 --- a/sys/fs/nfs/nfsport.h +++ b/sys/fs/nfs/nfsport.h @@ -603,11 +603,6 @@ void nfsrvd_rcv(struct socket *, void *, int); #define NFSREQSPINLOCK extern struct mtx nfs_req_mutex #define NFSLOCKREQ() mtx_lock(&nfs_req_mutex) #define NFSUNLOCKREQ() mtx_unlock(&nfs_req_mutex) -#define NFSCACHEMUTEX extern struct mtx nfs_cache_mutex -#define NFSCACHEMUTEXPTR (&nfs_cache_mutex) -#define NFSLOCKCACHE() mtx_lock(&nfs_cache_mutex) -#define NFSUNLOCKCACHE() mtx_unlock(&nfs_cache_mutex) -#define NFSCACHELOCKREQUIRED() mtx_assert(&nfs_cache_mutex, MA_OWNED) #define NFSSOCKMUTEX extern struct mtx nfs_slock_mutex #define NFSSOCKMUTEXPTR (&nfs_slock_mutex) #define NFSLOCKSOCK() mtx_lock(&nfs_slock_mutex) diff --git a/sys/fs/nfs/nfsrvcache.h b/sys/fs/nfs/nfsrvcache.h index 30f757a0d9e..5c9dc57e136 100644 --- a/sys/fs/nfs/nfsrvcache.h +++ b/sys/fs/nfs/nfsrvcache.h @@ -41,8 +41,9 @@ #define NFSRVCACHE_MAX_SIZE 2048 #define NFSRVCACHE_MIN_SIZE 64 -#define NFSRVCACHE_HASHSIZE 20 +#define NFSRVCACHE_HASHSIZE 500 +/* Cache table entry. */ struct nfsrvcache { LIST_ENTRY(nfsrvcache) rc_hash; /* Hash chain */ TAILQ_ENTRY(nfsrvcache) rc_lru; /* UDP lru chain */ @@ -104,4 +105,11 @@ struct nfsrvcache { LIST_HEAD(nfsrvhashhead, nfsrvcache); +/* The fine-grained locked cache hash table for TCP. */ +struct nfsrchash_bucket { + struct mtx mtx; + char lock_name[16]; + struct nfsrvhashhead tbl; +}; + #endif /* _NFS_NFSRVCACHE_H_ */ diff --git a/sys/fs/nfsclient/nfs_clkrpc.c b/sys/fs/nfsclient/nfs_clkrpc.c index 8b0b2346759..502fec5f24a 100644 --- a/sys/fs/nfsclient/nfs_clkrpc.c +++ b/sys/fs/nfsclient/nfs_clkrpc.c @@ -278,17 +278,15 @@ nfsrvd_cbinit(int terminating) while (nfs_numnfscbd > 0) msleep(&nfs_numnfscbd, NFSDLOCKMUTEXPTR, PZERO, "nfscbdt", 0); - NFSD_UNLOCK(); - svcpool_destroy(nfscbd_pool); - nfscbd_pool = NULL; - } else - NFSD_UNLOCK(); + } - nfscbd_pool = svcpool_create("nfscbd", NULL); - nfscbd_pool->sp_rcache = NULL; - nfscbd_pool->sp_assign = NULL; - nfscbd_pool->sp_done = NULL; - - NFSD_LOCK(); + if (nfscbd_pool == NULL) { + NFSD_UNLOCK(); + nfscbd_pool = svcpool_create("nfscbd", NULL); + nfscbd_pool->sp_rcache = NULL; + nfscbd_pool->sp_assign = NULL; + nfscbd_pool->sp_done = NULL; + NFSD_LOCK(); + } } diff --git a/sys/fs/nfsclient/nfs_clport.c b/sys/fs/nfsclient/nfs_clport.c index d7b082b6ae8..b198d597698 100644 --- a/sys/fs/nfsclient/nfs_clport.c +++ b/sys/fs/nfsclient/nfs_clport.c @@ -1219,10 +1219,11 @@ nfssvc_nfscl(struct thread *td, struct nfssvc_args *uap) struct file *fp; struct nfscbd_args nfscbdarg; struct nfsd_nfscbd_args nfscbdarg2; - int error; struct nameidata nd; struct nfscl_dumpmntopts dumpmntopts; + cap_rights_t rights; char *buf; + int error; if (uap->flag & NFSSVC_CBADDSOCK) { error = copyin(uap->argp, (caddr_t)&nfscbdarg, sizeof(nfscbdarg)); @@ -1233,10 +1234,10 @@ nfssvc_nfscl(struct thread *td, struct nfssvc_args *uap) * pretend that we need them all. It is better to be too * careful than too reckless. */ - if ((error = fget(td, nfscbdarg.sock, CAP_SOCK_CLIENT, &fp)) - != 0) { + error = fget(td, nfscbdarg.sock, + cap_rights_init(&rights, CAP_SOCK_CLIENT), &fp); + if (error) return (error); - } if (fp->f_type != DTYPE_SOCKET) { fdrop(fp, td); return (EPERM); diff --git a/sys/fs/nfsclient/nfs_clvfsops.c b/sys/fs/nfsclient/nfs_clvfsops.c index 863c418d1b0..4a180c52ed6 100644 --- a/sys/fs/nfsclient/nfs_clvfsops.c +++ b/sys/fs/nfsclient/nfs_clvfsops.c @@ -120,6 +120,7 @@ static vfs_root_t nfs_root; static vfs_statfs_t nfs_statfs; static vfs_sync_t nfs_sync; static vfs_sysctl_t nfs_sysctl; +static vfs_purge_t nfs_purge; /* * nfs vfs operations. @@ -134,6 +135,7 @@ static struct vfsops nfs_vfsops = { .vfs_uninit = ncl_uninit, .vfs_unmount = nfs_unmount, .vfs_sysctl = nfs_sysctl, + .vfs_purge = nfs_purge, }; VFS_SET(nfs_vfsops, nfs, VFCF_NETWORK | VFCF_SBDRY); @@ -1675,6 +1677,19 @@ nfs_sysctl(struct mount *mp, fsctlop_t op, struct sysctl_req *req) return (0); } +/* + * Purge any RPCs in progress, so that they will all return errors. + * This allows dounmount() to continue as far as VFS_UNMOUNT() for a + * forced dismount. + */ +static void +nfs_purge(struct mount *mp) +{ + struct nfsmount *nmp = VFSTONFS(mp); + + newnfs_nmcancelreqs(nmp); +} + /* * Extract the information needed by the nlm from the nfs vnode. */ diff --git a/sys/fs/nfsserver/nfs_nfsdcache.c b/sys/fs/nfsserver/nfs_nfsdcache.c index b979b358d6b..32a053d4d14 100644 --- a/sys/fs/nfsserver/nfs_nfsdcache.c +++ b/sys/fs/nfsserver/nfs_nfsdcache.c @@ -160,15 +160,51 @@ __FBSDID("$FreeBSD$"); #include extern struct nfsstats newnfsstats; -NFSCACHEMUTEX; +extern struct mtx nfsrc_udpmtx; +extern struct nfsrchash_bucket nfsrchash_table[NFSRVCACHE_HASHSIZE]; int nfsrc_floodlevel = NFSRVCACHE_FLOODLEVEL, nfsrc_tcpsavedreplies = 0; #endif /* !APPLEKEXT */ -static int nfsrc_tcpnonidempotent = 1; -static int nfsrc_udphighwater = NFSRVCACHE_UDPHIGHWATER, nfsrc_udpcachesize = 0; +SYSCTL_DECL(_vfs_nfsd); + +static u_int nfsrc_tcphighwater = 0; +static int +sysctl_tcphighwater(SYSCTL_HANDLER_ARGS) +{ + int error, newhighwater; + + newhighwater = nfsrc_tcphighwater; + error = sysctl_handle_int(oidp, &newhighwater, 0, req); + if (error != 0 || req->newptr == NULL) + return (error); + if (newhighwater < 0) + return (EINVAL); + if (newhighwater >= nfsrc_floodlevel) + nfsrc_floodlevel = newhighwater + newhighwater / 5; + nfsrc_tcphighwater = newhighwater; + return (0); +} +SYSCTL_PROC(_vfs_nfsd, OID_AUTO, tcphighwater, CTLTYPE_UINT | CTLFLAG_RW, 0, + sizeof(nfsrc_tcphighwater), sysctl_tcphighwater, "IU", + "High water mark for TCP cache entries"); + +static u_int nfsrc_udphighwater = NFSRVCACHE_UDPHIGHWATER; +SYSCTL_UINT(_vfs_nfsd, OID_AUTO, udphighwater, CTLFLAG_RW, + &nfsrc_udphighwater, 0, + "High water mark for UDP cache entries"); +static u_int nfsrc_tcptimeout = NFSRVCACHE_TCPTIMEOUT; +SYSCTL_UINT(_vfs_nfsd, OID_AUTO, tcpcachetimeo, CTLFLAG_RW, + &nfsrc_tcptimeout, 0, + "Timeout for TCP entries in the DRC"); +static u_int nfsrc_tcpnonidempotent = 1; +SYSCTL_UINT(_vfs_nfsd, OID_AUTO, cachetcp, CTLFLAG_RW, + &nfsrc_tcpnonidempotent, 0, + "Enable the DRC for NFS over TCP"); + +static int nfsrc_udpcachesize = 0; static TAILQ_HEAD(, nfsrvcache) nfsrvudplru; -static struct nfsrvhashhead nfsrvhashtbl[NFSRVCACHE_HASHSIZE], - nfsrvudphashtbl[NFSRVCACHE_HASHSIZE]; +static struct nfsrvhashhead nfsrvudphashtbl[NFSRVCACHE_HASHSIZE]; + /* * and the reverse mapping from generic to Version 2 procedure numbers */ @@ -197,10 +233,11 @@ static int newnfsv2_procid[NFS_V3NPROCS] = { NFSV2PROC_NOOP, }; +#define nfsrc_hash(xid) (((xid) + ((xid) >> 24)) % NFSRVCACHE_HASHSIZE) #define NFSRCUDPHASH(xid) \ - (&nfsrvudphashtbl[((xid) + ((xid) >> 24)) % NFSRVCACHE_HASHSIZE]) + (&nfsrvudphashtbl[nfsrc_hash(xid)]) #define NFSRCHASH(xid) \ - (&nfsrvhashtbl[((xid) + ((xid) >> 24)) % NFSRVCACHE_HASHSIZE]) + (&nfsrchash_table[nfsrc_hash(xid)].tbl) #define TRUE 1 #define FALSE 0 #define NFSRVCACHE_CHECKLEN 100 @@ -250,6 +287,18 @@ static int nfsrc_activesocket(struct nfsrvcache *rp, u_int64_t, static int nfsrc_getlenandcksum(mbuf_t m1, u_int16_t *cksum); static void nfsrc_marksametcpconn(u_int64_t); +/* + * Return the correct mutex for this cache entry. + */ +static __inline struct mtx * +nfsrc_cachemutex(struct nfsrvcache *rp) +{ + + if ((rp->rc_flag & RC_UDP) != 0) + return (&nfsrc_udpmtx); + return (&nfsrchash_table[nfsrc_hash(rp->rc_xid)].mtx); +} + /* * Initialize the server request cache list */ @@ -264,7 +313,7 @@ nfsrvd_initcache(void) inited = 1; for (i = 0; i < NFSRVCACHE_HASHSIZE; i++) { LIST_INIT(&nfsrvudphashtbl[i]); - LIST_INIT(&nfsrvhashtbl[i]); + LIST_INIT(&nfsrchash_table[i].tbl); } TAILQ_INIT(&nfsrvudplru); nfsrc_tcpsavedreplies = 0; @@ -325,10 +374,12 @@ nfsrc_getudp(struct nfsrv_descript *nd, struct nfsrvcache *newrp) struct sockaddr_in6 *saddr6; struct nfsrvhashhead *hp; int ret = 0; + struct mtx *mutex; + mutex = nfsrc_cachemutex(newrp); hp = NFSRCUDPHASH(newrp->rc_xid); loop: - NFSLOCKCACHE(); + mtx_lock(mutex); LIST_FOREACH(rp, hp, rc_hash) { if (newrp->rc_xid == rp->rc_xid && newrp->rc_proc == rp->rc_proc && @@ -336,8 +387,8 @@ loop: nfsaddr_match(NETFAMILY(rp), &rp->rc_haddr, nd->nd_nam)) { if ((rp->rc_flag & RC_LOCKED) != 0) { rp->rc_flag |= RC_WANTED; - (void)mtx_sleep(rp, NFSCACHEMUTEXPTR, - (PZERO - 1) | PDROP, "nfsrc", 10 * hz); + (void)mtx_sleep(rp, mutex, (PZERO - 1) | PDROP, + "nfsrc", 10 * hz); goto loop; } if (rp->rc_flag == 0) @@ -347,14 +398,14 @@ loop: TAILQ_INSERT_TAIL(&nfsrvudplru, rp, rc_lru); if (rp->rc_flag & RC_INPROG) { newnfsstats.srvcache_inproghits++; - NFSUNLOCKCACHE(); + mtx_unlock(mutex); ret = RC_DROPIT; } else if (rp->rc_flag & RC_REPSTATUS) { /* * V2 only. */ newnfsstats.srvcache_nonidemdonehits++; - NFSUNLOCKCACHE(); + mtx_unlock(mutex); nfsrvd_rephead(nd); *(nd->nd_errp) = rp->rc_status; ret = RC_REPLY; @@ -362,7 +413,7 @@ loop: NFSRVCACHE_UDPTIMEOUT; } else if (rp->rc_flag & RC_REPMBUF) { newnfsstats.srvcache_nonidemdonehits++; - NFSUNLOCKCACHE(); + mtx_unlock(mutex); nd->nd_mreq = m_copym(rp->rc_reply, 0, M_COPYALL, M_WAITOK); ret = RC_REPLY; @@ -377,7 +428,7 @@ loop: } } newnfsstats.srvcache_misses++; - newnfsstats.srvcache_size++; + atomic_add_int(&newnfsstats.srvcache_size, 1); nfsrc_udpcachesize++; newrp->rc_flag |= RC_INPROG; @@ -392,7 +443,7 @@ loop: } LIST_INSERT_HEAD(hp, newrp, rc_hash); TAILQ_INSERT_TAIL(&nfsrvudplru, newrp, rc_lru); - NFSUNLOCKCACHE(); + mtx_unlock(mutex); nd->nd_rp = newrp; ret = RC_DOIT; @@ -410,12 +461,14 @@ nfsrvd_updatecache(struct nfsrv_descript *nd, struct socket *so) struct nfsrvcache *rp; struct nfsrvcache *retrp = NULL; mbuf_t m; + struct mtx *mutex; rp = nd->nd_rp; if (!rp) panic("nfsrvd_updatecache null rp"); nd->nd_rp = NULL; - NFSLOCKCACHE(); + mutex = nfsrc_cachemutex(rp); + mtx_lock(mutex); nfsrc_lock(rp); if (!(rp->rc_flag & RC_INPROG)) panic("nfsrvd_updatecache not inprog"); @@ -430,7 +483,7 @@ nfsrvd_updatecache(struct nfsrv_descript *nd, struct socket *so) */ if (nd->nd_repstat == NFSERR_REPLYFROMCACHE) { newnfsstats.srvcache_nonidemdonehits++; - NFSUNLOCKCACHE(); + mtx_unlock(mutex); nd->nd_repstat = 0; if (nd->nd_mreq) mbuf_freem(nd->nd_mreq); @@ -438,7 +491,7 @@ nfsrvd_updatecache(struct nfsrv_descript *nd, struct socket *so) panic("reply from cache"); nd->nd_mreq = m_copym(rp->rc_reply, 0, M_COPYALL, M_WAITOK); - rp->rc_timestamp = NFSD_MONOSEC + NFSRVCACHE_TCPTIMEOUT; + rp->rc_timestamp = NFSD_MONOSEC + nfsrc_tcptimeout; nfsrc_unlock(rp); goto out; } @@ -463,29 +516,28 @@ nfsrvd_updatecache(struct nfsrv_descript *nd, struct socket *so) nfsv2_repstat[newnfsv2_procid[nd->nd_procnum]]) { rp->rc_status = nd->nd_repstat; rp->rc_flag |= RC_REPSTATUS; - NFSUNLOCKCACHE(); + mtx_unlock(mutex); } else { if (!(rp->rc_flag & RC_UDP)) { - nfsrc_tcpsavedreplies++; + atomic_add_int(&nfsrc_tcpsavedreplies, 1); if (nfsrc_tcpsavedreplies > newnfsstats.srvcache_tcppeak) newnfsstats.srvcache_tcppeak = nfsrc_tcpsavedreplies; } - NFSUNLOCKCACHE(); + mtx_unlock(mutex); m = m_copym(nd->nd_mreq, 0, M_COPYALL, M_WAITOK); - NFSLOCKCACHE(); + mtx_lock(mutex); rp->rc_reply = m; rp->rc_flag |= RC_REPMBUF; - NFSUNLOCKCACHE(); + mtx_unlock(mutex); } if (rp->rc_flag & RC_UDP) { rp->rc_timestamp = NFSD_MONOSEC + NFSRVCACHE_UDPTIMEOUT; nfsrc_unlock(rp); } else { - rp->rc_timestamp = NFSD_MONOSEC + - NFSRVCACHE_TCPTIMEOUT; + rp->rc_timestamp = NFSD_MONOSEC + nfsrc_tcptimeout; if (rp->rc_refcnt > 0) nfsrc_unlock(rp); else @@ -493,7 +545,7 @@ nfsrvd_updatecache(struct nfsrv_descript *nd, struct socket *so) } } else { nfsrc_freecache(rp); - NFSUNLOCKCACHE(); + mtx_unlock(mutex); } out: @@ -509,14 +561,16 @@ out: APPLESTATIC void nfsrvd_delcache(struct nfsrvcache *rp) { + struct mtx *mutex; + mutex = nfsrc_cachemutex(rp); if (!(rp->rc_flag & RC_INPROG)) panic("nfsrvd_delcache not in prog"); - NFSLOCKCACHE(); + mtx_lock(mutex); rp->rc_flag &= ~RC_INPROG; if (rp->rc_refcnt == 0 && !(rp->rc_flag & RC_LOCKED)) nfsrc_freecache(rp); - NFSUNLOCKCACHE(); + mtx_unlock(mutex); } /* @@ -528,7 +582,9 @@ APPLESTATIC void nfsrvd_sentcache(struct nfsrvcache *rp, struct socket *so, int err) { tcp_seq tmp_seq; + struct mtx *mutex; + mutex = nfsrc_cachemutex(rp); if (!(rp->rc_flag & RC_LOCKED)) panic("nfsrvd_sentcache not locked"); if (!err) { @@ -537,10 +593,10 @@ nfsrvd_sentcache(struct nfsrvcache *rp, struct socket *so, int err) so->so_proto->pr_protocol != IPPROTO_TCP) panic("nfs sent cache"); if (nfsrv_getsockseqnum(so, &tmp_seq)) { - NFSLOCKCACHE(); + mtx_lock(mutex); rp->rc_tcpseq = tmp_seq; rp->rc_flag |= RC_TCPSEQ; - NFSUNLOCKCACHE(); + mtx_unlock(mutex); } } nfsrc_unlock(rp); @@ -559,11 +615,13 @@ nfsrc_gettcp(struct nfsrv_descript *nd, struct nfsrvcache *newrp) struct nfsrvcache *hitrp; struct nfsrvhashhead *hp, nfsrc_templist; int hit, ret = 0; + struct mtx *mutex; + mutex = nfsrc_cachemutex(newrp); hp = NFSRCHASH(newrp->rc_xid); newrp->rc_reqlen = nfsrc_getlenandcksum(nd->nd_mrep, &newrp->rc_cksum); tryagain: - NFSLOCKCACHE(); + mtx_lock(mutex); hit = 1; LIST_INIT(&nfsrc_templist); /* @@ -621,8 +679,8 @@ tryagain: rp = hitrp; if ((rp->rc_flag & RC_LOCKED) != 0) { rp->rc_flag |= RC_WANTED; - (void)mtx_sleep(rp, NFSCACHEMUTEXPTR, - (PZERO - 1) | PDROP, "nfsrc", 10 * hz); + (void)mtx_sleep(rp, mutex, (PZERO - 1) | PDROP, + "nfsrc", 10 * hz); goto tryagain; } if (rp->rc_flag == 0) @@ -630,7 +688,7 @@ tryagain: rp->rc_flag |= RC_LOCKED; if (rp->rc_flag & RC_INPROG) { newnfsstats.srvcache_inproghits++; - NFSUNLOCKCACHE(); + mtx_unlock(mutex); if (newrp->rc_sockref == rp->rc_sockref) nfsrc_marksametcpconn(rp->rc_sockref); ret = RC_DROPIT; @@ -639,24 +697,22 @@ tryagain: * V2 only. */ newnfsstats.srvcache_nonidemdonehits++; - NFSUNLOCKCACHE(); + mtx_unlock(mutex); if (newrp->rc_sockref == rp->rc_sockref) nfsrc_marksametcpconn(rp->rc_sockref); ret = RC_REPLY; nfsrvd_rephead(nd); *(nd->nd_errp) = rp->rc_status; - rp->rc_timestamp = NFSD_MONOSEC + - NFSRVCACHE_TCPTIMEOUT; + rp->rc_timestamp = NFSD_MONOSEC + nfsrc_tcptimeout; } else if (rp->rc_flag & RC_REPMBUF) { newnfsstats.srvcache_nonidemdonehits++; - NFSUNLOCKCACHE(); + mtx_unlock(mutex); if (newrp->rc_sockref == rp->rc_sockref) nfsrc_marksametcpconn(rp->rc_sockref); ret = RC_REPLY; nd->nd_mreq = m_copym(rp->rc_reply, 0, M_COPYALL, M_WAITOK); - rp->rc_timestamp = NFSD_MONOSEC + - NFSRVCACHE_TCPTIMEOUT; + rp->rc_timestamp = NFSD_MONOSEC + nfsrc_tcptimeout; } else { panic("nfs tcp cache1"); } @@ -665,7 +721,7 @@ tryagain: goto out; } newnfsstats.srvcache_misses++; - newnfsstats.srvcache_size++; + atomic_add_int(&newnfsstats.srvcache_size, 1); /* * For TCP, multiple entries for a key are allowed, so don't @@ -674,7 +730,7 @@ tryagain: newrp->rc_cachetime = NFSD_MONOSEC; newrp->rc_flag |= RC_INPROG; LIST_INSERT_HEAD(hp, newrp, rc_hash); - NFSUNLOCKCACHE(); + mtx_unlock(mutex); nd->nd_rp = newrp; ret = RC_DOIT; @@ -685,16 +741,17 @@ out: /* * Lock a cache entry. - * Also puts a mutex lock on the cache list. */ static void nfsrc_lock(struct nfsrvcache *rp) { - NFSCACHELOCKREQUIRED(); + struct mtx *mutex; + + mutex = nfsrc_cachemutex(rp); + mtx_assert(mutex, MA_OWNED); while ((rp->rc_flag & RC_LOCKED) != 0) { rp->rc_flag |= RC_WANTED; - (void)mtx_sleep(rp, NFSCACHEMUTEXPTR, PZERO - 1, - "nfsrc", 0); + (void)mtx_sleep(rp, mutex, PZERO - 1, "nfsrc", 0); } rp->rc_flag |= RC_LOCKED; } @@ -705,11 +762,13 @@ nfsrc_lock(struct nfsrvcache *rp) static void nfsrc_unlock(struct nfsrvcache *rp) { + struct mtx *mutex; - NFSLOCKCACHE(); + mutex = nfsrc_cachemutex(rp); + mtx_lock(mutex); rp->rc_flag &= ~RC_LOCKED; nfsrc_wanted(rp); - NFSUNLOCKCACHE(); + mtx_unlock(mutex); } /* @@ -732,7 +791,6 @@ static void nfsrc_freecache(struct nfsrvcache *rp) { - NFSCACHELOCKREQUIRED(); LIST_REMOVE(rp, rc_hash); if (rp->rc_flag & RC_UDP) { TAILQ_REMOVE(&nfsrvudplru, rp, rc_lru); @@ -742,10 +800,10 @@ nfsrc_freecache(struct nfsrvcache *rp) if (rp->rc_flag & RC_REPMBUF) { mbuf_freem(rp->rc_reply); if (!(rp->rc_flag & RC_UDP)) - nfsrc_tcpsavedreplies--; + atomic_add_int(&nfsrc_tcpsavedreplies, -1); } FREE((caddr_t)rp, M_NFSRVCACHE); - newnfsstats.srvcache_size--; + atomic_add_int(&newnfsstats.srvcache_size, -1); } /* @@ -757,20 +815,21 @@ nfsrvd_cleancache(void) struct nfsrvcache *rp, *nextrp; int i; - NFSLOCKCACHE(); for (i = 0; i < NFSRVCACHE_HASHSIZE; i++) { - LIST_FOREACH_SAFE(rp, &nfsrvhashtbl[i], rc_hash, nextrp) { + mtx_lock(&nfsrchash_table[i].mtx); + LIST_FOREACH_SAFE(rp, &nfsrchash_table[i].tbl, rc_hash, nextrp) nfsrc_freecache(rp); - } + mtx_unlock(&nfsrchash_table[i].mtx); } + mtx_lock(&nfsrc_udpmtx); for (i = 0; i < NFSRVCACHE_HASHSIZE; i++) { LIST_FOREACH_SAFE(rp, &nfsrvudphashtbl[i], rc_hash, nextrp) { nfsrc_freecache(rp); } } newnfsstats.srvcache_size = 0; + mtx_unlock(&nfsrc_udpmtx); nfsrc_tcpsavedreplies = 0; - NFSUNLOCKCACHE(); } /* @@ -780,28 +839,97 @@ static void nfsrc_trimcache(u_int64_t sockref, struct socket *so) { struct nfsrvcache *rp, *nextrp; - int i; + int i, j, k, time_histo[10]; + time_t thisstamp; + static time_t udp_lasttrim = 0, tcp_lasttrim = 0; + static int onethread = 0; - NFSLOCKCACHE(); - TAILQ_FOREACH_SAFE(rp, &nfsrvudplru, rc_lru, nextrp) { - if (!(rp->rc_flag & (RC_INPROG|RC_LOCKED|RC_WANTED)) - && rp->rc_refcnt == 0 - && ((rp->rc_flag & RC_REFCNT) || - NFSD_MONOSEC > rp->rc_timestamp || - nfsrc_udpcachesize > nfsrc_udphighwater)) - nfsrc_freecache(rp); - } - for (i = 0; i < NFSRVCACHE_HASHSIZE; i++) { - LIST_FOREACH_SAFE(rp, &nfsrvhashtbl[i], rc_hash, nextrp) { + if (atomic_cmpset_acq_int(&onethread, 0, 1) == 0) + return; + if (NFSD_MONOSEC != udp_lasttrim || + nfsrc_udpcachesize >= (nfsrc_udphighwater + + nfsrc_udphighwater / 2)) { + mtx_lock(&nfsrc_udpmtx); + udp_lasttrim = NFSD_MONOSEC; + TAILQ_FOREACH_SAFE(rp, &nfsrvudplru, rc_lru, nextrp) { if (!(rp->rc_flag & (RC_INPROG|RC_LOCKED|RC_WANTED)) && rp->rc_refcnt == 0 && ((rp->rc_flag & RC_REFCNT) || - NFSD_MONOSEC > rp->rc_timestamp || - nfsrc_activesocket(rp, sockref, so))) + udp_lasttrim > rp->rc_timestamp || + nfsrc_udpcachesize > nfsrc_udphighwater)) nfsrc_freecache(rp); } + mtx_unlock(&nfsrc_udpmtx); } - NFSUNLOCKCACHE(); + if (NFSD_MONOSEC != tcp_lasttrim || + nfsrc_tcpsavedreplies >= nfsrc_tcphighwater) { + for (i = 0; i < 10; i++) + time_histo[i] = 0; + for (i = 0; i < NFSRVCACHE_HASHSIZE; i++) { + mtx_lock(&nfsrchash_table[i].mtx); + if (i == 0) + tcp_lasttrim = NFSD_MONOSEC; + LIST_FOREACH_SAFE(rp, &nfsrchash_table[i].tbl, rc_hash, + nextrp) { + if (!(rp->rc_flag & + (RC_INPROG|RC_LOCKED|RC_WANTED)) + && rp->rc_refcnt == 0) { + /* + * The timestamps range from roughly the + * present (tcp_lasttrim) to the present + * + nfsrc_tcptimeout. Generate a simple + * histogram of where the timeouts fall. + */ + j = rp->rc_timestamp - tcp_lasttrim; + if (j >= nfsrc_tcptimeout) + j = nfsrc_tcptimeout - 1; + if (j < 0) + j = 0; + j = (j * 10 / nfsrc_tcptimeout) % 10; + time_histo[j]++; + if ((rp->rc_flag & RC_REFCNT) || + tcp_lasttrim > rp->rc_timestamp || + nfsrc_activesocket(rp, sockref, so)) + nfsrc_freecache(rp); + } + } + mtx_unlock(&nfsrchash_table[i].mtx); + } + j = nfsrc_tcphighwater / 5; /* 20% of it */ + if (j > 0 && (nfsrc_tcpsavedreplies + j) > nfsrc_tcphighwater) { + /* + * Trim some more with a smaller timeout of as little + * as 20% of nfsrc_tcptimeout to try and get below + * 80% of the nfsrc_tcphighwater. + */ + k = 0; + for (i = 0; i < 8; i++) { + k += time_histo[i]; + if (k > j) + break; + } + k = nfsrc_tcptimeout * (i + 1) / 10; + if (k < 1) + k = 1; + thisstamp = tcp_lasttrim + k; + for (i = 0; i < NFSRVCACHE_HASHSIZE; i++) { + mtx_lock(&nfsrchash_table[i].mtx); + LIST_FOREACH_SAFE(rp, &nfsrchash_table[i].tbl, + rc_hash, nextrp) { + if (!(rp->rc_flag & + (RC_INPROG|RC_LOCKED|RC_WANTED)) + && rp->rc_refcnt == 0 + && ((rp->rc_flag & RC_REFCNT) || + thisstamp > rp->rc_timestamp || + nfsrc_activesocket(rp, sockref, + so))) + nfsrc_freecache(rp); + } + mtx_unlock(&nfsrchash_table[i].mtx); + } + } + } + atomic_store_rel_int(&onethread, 0); } /* @@ -810,12 +938,14 @@ nfsrc_trimcache(u_int64_t sockref, struct socket *so) APPLESTATIC void nfsrvd_refcache(struct nfsrvcache *rp) { + struct mtx *mutex; - NFSLOCKCACHE(); + mutex = nfsrc_cachemutex(rp); + mtx_lock(mutex); if (rp->rc_refcnt < 0) panic("nfs cache refcnt"); rp->rc_refcnt++; - NFSUNLOCKCACHE(); + mtx_unlock(mutex); } /* @@ -824,14 +954,16 @@ nfsrvd_refcache(struct nfsrvcache *rp) APPLESTATIC void nfsrvd_derefcache(struct nfsrvcache *rp) { + struct mtx *mutex; - NFSLOCKCACHE(); + mutex = nfsrc_cachemutex(rp); + mtx_lock(mutex); if (rp->rc_refcnt <= 0) panic("nfs cache derefcnt"); rp->rc_refcnt--; if (rp->rc_refcnt == 0 && !(rp->rc_flag & (RC_LOCKED | RC_INPROG))) nfsrc_freecache(rp); - NFSUNLOCKCACHE(); + mtx_unlock(mutex); } /* diff --git a/sys/fs/nfsserver/nfs_nfsdport.c b/sys/fs/nfsserver/nfs_nfsdport.c index dfb13034f15..2f9d40a8f2e 100644 --- a/sys/fs/nfsserver/nfs_nfsdport.c +++ b/sys/fs/nfsserver/nfs_nfsdport.c @@ -60,7 +60,8 @@ extern SVCPOOL *nfsrvd_pool; extern struct nfsv4lock nfsd_suspend_lock; struct vfsoptlist nfsv4root_opt, nfsv4root_newopt; NFSDLOCKMUTEX; -struct mtx nfs_cache_mutex; +struct nfsrchash_bucket nfsrchash_table[NFSRVCACHE_HASHSIZE]; +struct mtx nfsrc_udpmtx; struct mtx nfs_v4root_mutex; struct nfsrvfh nfs_rootfh, nfs_pubfh; int nfs_pubfhset = 0, nfs_rootfhset = 0; @@ -3034,6 +3035,7 @@ nfssvc_nfsd(struct thread *td, struct nfssvc_args *uap) struct file *fp; struct nfsd_addsock_args sockarg; struct nfsd_nfsd_args nfsdarg; + cap_rights_t rights; int error; if (uap->flag & NFSSVC_NFSDADDSOCK) { @@ -3045,7 +3047,9 @@ nfssvc_nfsd(struct thread *td, struct nfssvc_args *uap) * pretend that we need them all. It is better to be too * careful than too reckless. */ - if ((error = fget(td, sockarg.sock, CAP_SOCK_SERVER, &fp)) != 0) + error = fget(td, sockarg.sock, + cap_rights_init(&rights, CAP_SOCK_SERVER), &fp); + if (error != 0) goto out; if (fp->f_type != DTYPE_SOCKET) { fdrop(fp, td); @@ -3278,7 +3282,7 @@ extern int (*nfsd_call_nfsd)(struct thread *, struct nfssvc_args *); static int nfsd_modevent(module_t mod, int type, void *data) { - int error = 0; + int error = 0, i; static int loaded = 0; switch (type) { @@ -3286,7 +3290,14 @@ nfsd_modevent(module_t mod, int type, void *data) if (loaded) goto out; newnfs_portinit(); - mtx_init(&nfs_cache_mutex, "nfs_cache_mutex", NULL, MTX_DEF); + for (i = 0; i < NFSRVCACHE_HASHSIZE; i++) { + snprintf(nfsrchash_table[i].lock_name, + sizeof(nfsrchash_table[i].lock_name), "nfsrc_tcp%d", + i); + mtx_init(&nfsrchash_table[i].mtx, + nfsrchash_table[i].lock_name, NULL, MTX_DEF); + } + mtx_init(&nfsrc_udpmtx, "nfs_udpcache_mutex", NULL, MTX_DEF); mtx_init(&nfs_v4root_mutex, "nfs_v4root_mutex", NULL, MTX_DEF); mtx_init(&nfsv4root_mnt.mnt_mtx, "struct mount mtx", NULL, MTX_DEF); @@ -3330,7 +3341,9 @@ nfsd_modevent(module_t mod, int type, void *data) svcpool_destroy(nfsrvd_pool); /* and get rid of the locks */ - mtx_destroy(&nfs_cache_mutex); + for (i = 0; i < NFSRVCACHE_HASHSIZE; i++) + mtx_destroy(&nfsrchash_table[i].mtx); + mtx_destroy(&nfsrc_udpmtx); mtx_destroy(&nfs_v4root_mutex); mtx_destroy(&nfsv4root_mnt.mnt_mtx); lockdestroy(&nfsv4root_mnt.mnt_explock); diff --git a/sys/fs/nullfs/null_vnops.c b/sys/fs/nullfs/null_vnops.c index 70402e35f1d..cf3762eda39 100644 --- a/sys/fs/nullfs/null_vnops.c +++ b/sys/fs/nullfs/null_vnops.c @@ -858,6 +858,15 @@ null_vptocnp(struct vop_vptocnp_args *ap) return (error); } +static int +null_link(struct vop_link_args *ap) +{ + + if (ap->a_tdvp->v_mount != ap->a_vp->v_mount) + return (EXDEV); + return (null_bypass((struct vop_generic_args *)ap)); +} + /* * Global vfs data structures */ @@ -871,6 +880,7 @@ struct vop_vector null_vnodeops = { .vop_getwritemount = null_getwritemount, .vop_inactive = null_inactive, .vop_islocked = vop_stdislocked, + .vop_link = null_link, .vop_lock1 = null_lock, .vop_lookup = null_lookup, .vop_open = null_open, diff --git a/sys/fs/smbfs/smbfs_node.c b/sys/fs/smbfs/smbfs_node.c index 1b55ff3d906..05d19e98ef9 100644 --- a/sys/fs/smbfs/smbfs_node.c +++ b/sys/fs/smbfs/smbfs_node.c @@ -370,10 +370,13 @@ smbfs_attr_cachelookup(struct vnode *vp, struct vattr *va) if (diff > 2) /* XXX should be configurable */ return ENOENT; va->va_type = vp->v_type; /* vnode type (for create) */ + va->va_flags = 0; /* flags defined for file */ if (vp->v_type == VREG) { va->va_mode = smp->sm_file_mode; /* files access mode and type */ - if (np->n_dosattr & SMB_FA_RDONLY) + if (np->n_dosattr & SMB_FA_RDONLY) { va->va_mode &= ~(S_IWUSR|S_IWGRP|S_IWOTH); + va->va_flags |= UF_READONLY; + } } else if (vp->v_type == VDIR) { va->va_mode = smp->sm_dir_mode; /* files access mode and type */ } else @@ -390,7 +393,15 @@ smbfs_attr_cachelookup(struct vnode *vp, struct vattr *va) va->va_mtime = np->n_mtime; va->va_atime = va->va_ctime = va->va_mtime; /* time file changed */ va->va_gen = VNOVAL; /* generation number of file */ - va->va_flags = 0; /* flags defined for file */ + if (np->n_dosattr & SMB_FA_HIDDEN) + va->va_flags |= UF_HIDDEN; + if (np->n_dosattr & SMB_FA_SYSTEM) + va->va_flags |= UF_SYSTEM; + /* + * We don't set the archive bit for directories. + */ + if ((vp->v_type != VDIR) && (np->n_dosattr & SMB_FA_ARCHIVE)) + va->va_flags |= UF_ARCHIVE; va->va_rdev = NODEV; /* device the special file represents */ va->va_bytes = va->va_size; /* bytes of disk space held by file */ va->va_filerev = 0; /* file modification number */ diff --git a/sys/fs/smbfs/smbfs_vnops.c b/sys/fs/smbfs/smbfs_vnops.c index ef1dc65989b..8ea11989944 100644 --- a/sys/fs/smbfs/smbfs_vnops.c +++ b/sys/fs/smbfs/smbfs_vnops.c @@ -305,16 +305,30 @@ smbfs_setattr(ap) int old_n_dosattr; SMBVDEBUG("\n"); - if (vap->va_flags != VNOVAL) - return EOPNOTSUPP; isreadonly = (vp->v_mount->mnt_flag & MNT_RDONLY); /* * Disallow write attempts if the filesystem is mounted read-only. */ if ((vap->va_uid != (uid_t)VNOVAL || vap->va_gid != (gid_t)VNOVAL || vap->va_atime.tv_sec != VNOVAL || vap->va_mtime.tv_sec != VNOVAL || - vap->va_mode != (mode_t)VNOVAL) && isreadonly) + vap->va_mode != (mode_t)VNOVAL || vap->va_flags != VNOVAL) && + isreadonly) return EROFS; + + /* + * We only support setting four flags. Don't allow setting others. + * + * We map UF_READONLY to SMB_FA_RDONLY, unlike the MacOS X version + * of this code, which maps both UF_IMMUTABLE AND SF_IMMUTABLE to + * SMB_FA_RDONLY. The immutable flags have different semantics + * than readonly, which is the reason for the difference. + */ + if (vap->va_flags != VNOVAL) { + if (vap->va_flags & ~(UF_HIDDEN|UF_SYSTEM|UF_ARCHIVE| + UF_READONLY)) + return EINVAL; + } + scred = smbfs_malloc_scred(); smb_makescred(scred, td, ap->a_cred); if (vap->va_size != VNOVAL) { @@ -353,12 +367,47 @@ smbfs_setattr(ap) goto out; } } - if (vap->va_mode != (mode_t)VNOVAL) { + if ((vap->va_flags != VNOVAL) || (vap->va_mode != (mode_t)VNOVAL)) { old_n_dosattr = np->n_dosattr; - if (vap->va_mode & S_IWUSR) - np->n_dosattr &= ~SMB_FA_RDONLY; - else - np->n_dosattr |= SMB_FA_RDONLY; + + if (vap->va_mode != (mode_t)VNOVAL) { + if (vap->va_mode & S_IWUSR) + np->n_dosattr &= ~SMB_FA_RDONLY; + else + np->n_dosattr |= SMB_FA_RDONLY; + } + + if (vap->va_flags != VNOVAL) { + if (vap->va_flags & UF_HIDDEN) + np->n_dosattr |= SMB_FA_HIDDEN; + else + np->n_dosattr &= ~SMB_FA_HIDDEN; + + if (vap->va_flags & UF_SYSTEM) + np->n_dosattr |= SMB_FA_SYSTEM; + else + np->n_dosattr &= ~SMB_FA_SYSTEM; + + if (vap->va_flags & UF_ARCHIVE) + np->n_dosattr |= SMB_FA_ARCHIVE; + else + np->n_dosattr &= ~SMB_FA_ARCHIVE; + + /* + * We only support setting the immutable / readonly + * bit for regular files. According to comments in + * the MacOS X version of this code, supporting the + * readonly bit on directories doesn't do the same + * thing in Windows as in Unix. + */ + if (vp->v_type == VREG) { + if (vap->va_flags & UF_READONLY) + np->n_dosattr |= SMB_FA_RDONLY; + else + np->n_dosattr &= ~SMB_FA_RDONLY; + } + } + if (np->n_dosattr != old_n_dosattr) { error = smbfs_smb_setpattr(np, np->n_dosattr, NULL, scred); if (error) diff --git a/sys/fs/tmpfs/tmpfs_subr.c b/sys/fs/tmpfs/tmpfs_subr.c index 4e94476e967..ec96936b076 100644 --- a/sys/fs/tmpfs/tmpfs_subr.c +++ b/sys/fs/tmpfs/tmpfs_subr.c @@ -1355,11 +1355,8 @@ tmpfs_reg_resize(struct vnode *vp, off_t newsize, boolean_t ignerr) retry: m = vm_page_lookup(uobj, idx); if (m != NULL) { - if ((m->oflags & VPO_BUSY) != 0 || - m->busy != 0) { - vm_page_sleep(m, "tmfssz"); + if (vm_page_sleep_if_busy(m, "tmfssz")) goto retry; - } MPASS(m->valid == VM_PAGE_BITS_ALL); } else if (vm_pager_has_page(uobj, idx, NULL, NULL)) { m = vm_page_alloc(uobj, idx, VM_ALLOC_NORMAL); @@ -1379,7 +1376,7 @@ retry: if (rv == VM_PAGER_OK) { vm_page_deactivate(m); vm_page_unlock(m); - vm_page_wakeup(m); + vm_page_xunbusy(m); } else { vm_page_free(m); vm_page_unlock(m); @@ -1436,9 +1433,10 @@ tmpfs_chflags(struct vnode *vp, u_long flags, struct ucred *cred, node = VP_TO_TMPFS_NODE(vp); - if ((flags & ~(UF_NODUMP | UF_IMMUTABLE | UF_APPEND | UF_OPAQUE | - UF_NOUNLINK | SF_ARCHIVED | SF_IMMUTABLE | SF_APPEND | - SF_NOUNLINK)) != 0) + if ((flags & ~(SF_APPEND | SF_ARCHIVED | SF_IMMUTABLE | SF_NOUNLINK | + UF_APPEND | UF_ARCHIVE | UF_HIDDEN | UF_IMMUTABLE | UF_NODUMP | + UF_NOUNLINK | UF_OFFLINE | UF_OPAQUE | UF_READONLY | UF_REPARSE | + UF_SPARSE | UF_SYSTEM)) != 0) return (EOPNOTSUPP); /* Disallow this operation if the file system is mounted read-only. */ diff --git a/sys/fs/tmpfs/tmpfs_vfsops.c b/sys/fs/tmpfs/tmpfs_vfsops.c index 4d55f51fbbc..57b9902d694 100644 --- a/sys/fs/tmpfs/tmpfs_vfsops.c +++ b/sys/fs/tmpfs/tmpfs_vfsops.c @@ -47,6 +47,8 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include +#include #include #include #include @@ -138,6 +140,7 @@ tmpfs_mount(struct mount *mp) sizeof(struct tmpfs_dirent) + sizeof(struct tmpfs_node)); struct tmpfs_mount *tmp; struct tmpfs_node *root; + struct thread *td = curthread; int error; /* Size counters. */ u_quad_t pages; @@ -150,6 +153,9 @@ tmpfs_mount(struct mount *mp) struct vattr va; + if (!prison_allow(td->td_ucred, PR_ALLOW_MOUNT_TMPFS)) + return (EPERM); + if (vfs_filteropt(mp->mnt_optnew, tmpfs_opts)) return (EINVAL); @@ -420,4 +426,4 @@ struct vfsops tmpfs_vfsops = { .vfs_statfs = tmpfs_statfs, .vfs_fhtovp = tmpfs_fhtovp, }; -VFS_SET(tmpfs_vfsops, tmpfs, 0); +VFS_SET(tmpfs_vfsops, tmpfs, VFCF_JAIL); diff --git a/sys/fs/tmpfs/tmpfs_vnops.c b/sys/fs/tmpfs/tmpfs_vnops.c index d867612a7bd..073a9fe98bc 100644 --- a/sys/fs/tmpfs/tmpfs_vnops.c +++ b/sys/fs/tmpfs/tmpfs_vnops.c @@ -438,230 +438,52 @@ tmpfs_setattr(struct vop_setattr_args *v) return error; } -static int -tmpfs_nocacheread(vm_object_t tobj, vm_pindex_t idx, - vm_offset_t offset, size_t tlen, struct uio *uio) -{ - vm_page_t m; - int error, rv; - - VM_OBJECT_WLOCK(tobj); - - /* - * Parallel reads of the page content from disk are prevented - * by VPO_BUSY. - * - * Although the tmpfs vnode lock is held here, it is - * nonetheless safe to sleep waiting for a free page. The - * pageout daemon does not need to acquire the tmpfs vnode - * lock to page out tobj's pages because tobj is a OBJT_SWAP - * type object. - */ - m = vm_page_grab(tobj, idx, VM_ALLOC_NORMAL | VM_ALLOC_RETRY | - VM_ALLOC_NOBUSY); - if (m->valid != VM_PAGE_BITS_ALL) { - vm_page_busy(m); - if (vm_pager_has_page(tobj, idx, NULL, NULL)) { - rv = vm_pager_get_pages(tobj, &m, 1, 0); - m = vm_page_lookup(tobj, idx); - if (m == NULL) { - printf( - "tmpfs: vm_obj %p idx %jd null lookup rv %d\n", - tobj, idx, rv); - VM_OBJECT_WUNLOCK(tobj); - return (EIO); - } - if (rv != VM_PAGER_OK) { - printf( - "tmpfs: vm_obj %p idx %jd valid %x pager error %d\n", - tobj, idx, m->valid, rv); - vm_page_lock(m); - vm_page_free(m); - vm_page_unlock(m); - VM_OBJECT_WUNLOCK(tobj); - return (EIO); - } - } else - vm_page_zero_invalid(m, TRUE); - vm_page_wakeup(m); - } - vm_page_lock(m); - vm_page_hold(m); - vm_page_unlock(m); - VM_OBJECT_WUNLOCK(tobj); - error = uiomove_fromphys(&m, offset, tlen, uio); - vm_page_lock(m); - vm_page_unhold(m); - if (m->queue == PQ_NONE) { - vm_page_deactivate(m); - } else { - /* Requeue to maintain LRU ordering. */ - vm_page_requeue(m); - } - vm_page_unlock(m); - - return (error); -} - static int tmpfs_read(struct vop_read_args *v) { - struct vnode *vp = v->a_vp; - struct uio *uio = v->a_uio; + struct vnode *vp; + struct uio *uio; struct tmpfs_node *node; - vm_object_t uobj; - size_t len; - int resid; - int error = 0; - vm_pindex_t idx; - vm_offset_t offset; - off_t addr; - size_t tlen; + vp = v->a_vp; + if (vp->v_type != VREG) + return (EISDIR); + uio = v->a_uio; + if (uio->uio_offset < 0) + return (EINVAL); node = VP_TO_TMPFS_NODE(vp); - - if (vp->v_type != VREG) { - error = EISDIR; - goto out; - } - - if (uio->uio_offset < 0) { - error = EINVAL; - goto out; - } - node->tn_status |= TMPFS_NODE_ACCESSED; - - uobj = node->tn_reg.tn_aobj; - while ((resid = uio->uio_resid) > 0) { - error = 0; - if (node->tn_size <= uio->uio_offset) - break; - len = MIN(node->tn_size - uio->uio_offset, resid); - if (len == 0) - break; - addr = uio->uio_offset; - idx = OFF_TO_IDX(addr); - offset = addr & PAGE_MASK; - tlen = MIN(PAGE_SIZE - offset, len); - error = tmpfs_nocacheread(uobj, idx, offset, tlen, uio); - if ((error != 0) || (resid == uio->uio_resid)) - break; - } - -out: - - return error; -} - -/* --------------------------------------------------------------------- */ - -static int -tmpfs_mappedwrite(vm_object_t tobj, size_t len, struct uio *uio) -{ - vm_pindex_t idx; - vm_page_t tpg; - vm_offset_t offset; - off_t addr; - size_t tlen; - int error, rv; - - error = 0; - - addr = uio->uio_offset; - idx = OFF_TO_IDX(addr); - offset = addr & PAGE_MASK; - tlen = MIN(PAGE_SIZE - offset, len); - - VM_OBJECT_WLOCK(tobj); - tpg = vm_page_grab(tobj, idx, VM_ALLOC_NORMAL | VM_ALLOC_NOBUSY | - VM_ALLOC_RETRY); - if (tpg->valid != VM_PAGE_BITS_ALL) { - vm_page_busy(tpg); - if (vm_pager_has_page(tobj, idx, NULL, NULL)) { - rv = vm_pager_get_pages(tobj, &tpg, 1, 0); - tpg = vm_page_lookup(tobj, idx); - if (tpg == NULL) { - printf( - "tmpfs: vm_obj %p idx %jd null lookup rv %d\n", - tobj, idx, rv); - VM_OBJECT_WUNLOCK(tobj); - return (EIO); - } - if (rv != VM_PAGER_OK) { - printf( - "tmpfs: vm_obj %p idx %jd valid %x pager error %d\n", - tobj, idx, tpg->valid, rv); - vm_page_lock(tpg); - vm_page_free(tpg); - vm_page_unlock(tpg); - VM_OBJECT_WUNLOCK(tobj); - return (EIO); - } - } else - vm_page_zero_invalid(tpg, TRUE); - vm_page_wakeup(tpg); - } - vm_page_lock(tpg); - vm_page_hold(tpg); - vm_page_unlock(tpg); - VM_OBJECT_WUNLOCK(tobj); - error = uiomove_fromphys(&tpg, offset, tlen, uio); - VM_OBJECT_WLOCK(tobj); - if (error == 0) - vm_page_dirty(tpg); - vm_page_lock(tpg); - vm_page_unhold(tpg); - if (tpg->queue == PQ_NONE) { - vm_page_deactivate(tpg); - } else { - /* Requeue to maintain LRU ordering. */ - vm_page_requeue(tpg); - } - vm_page_unlock(tpg); - VM_OBJECT_WUNLOCK(tobj); - - return (error); + return (uiomove_object(node->tn_reg.tn_aobj, node->tn_size, uio)); } static int tmpfs_write(struct vop_write_args *v) { - struct vnode *vp = v->a_vp; - struct uio *uio = v->a_uio; - int ioflag = v->a_ioflag; - - boolean_t extended; - int error = 0; - off_t oldsize; + struct vnode *vp; + struct uio *uio; struct tmpfs_node *node; - vm_object_t uobj; - size_t len; - int resid; + off_t oldsize; + int error, ioflag; + boolean_t extended; + vp = v->a_vp; + uio = v->a_uio; + ioflag = v->a_ioflag; + error = 0; node = VP_TO_TMPFS_NODE(vp); oldsize = node->tn_size; - if (uio->uio_offset < 0 || vp->v_type != VREG) { - error = EINVAL; - goto out; - } - - if (uio->uio_resid == 0) { - error = 0; - goto out; - } - + if (uio->uio_offset < 0 || vp->v_type != VREG) + return (EINVAL); + if (uio->uio_resid == 0) + return (0); if (ioflag & IO_APPEND) uio->uio_offset = node->tn_size; - if (uio->uio_offset + uio->uio_resid > VFS_TO_TMPFS(vp->v_mount)->tm_maxfilesize) return (EFBIG); - if (vn_rlimit_fsize(vp, uio, uio->uio_td)) return (EFBIG); - extended = uio->uio_offset + uio->uio_resid > node->tn_size; if (extended) { error = tmpfs_reg_resize(vp, uio->uio_offset + uio->uio_resid, @@ -670,26 +492,13 @@ tmpfs_write(struct vop_write_args *v) goto out; } - uobj = node->tn_reg.tn_aobj; - while ((resid = uio->uio_resid) > 0) { - if (node->tn_size <= uio->uio_offset) - break; - len = MIN(node->tn_size - uio->uio_offset, resid); - if (len == 0) - break; - error = tmpfs_mappedwrite(uobj, len, uio); - if ((error != 0) || (resid == uio->uio_resid)) - break; - } - + error = uiomove_object(node->tn_reg.tn_aobj, node->tn_size, uio); node->tn_status |= TMPFS_NODE_ACCESSED | TMPFS_NODE_MODIFIED | (extended ? TMPFS_NODE_CHANGED : 0); - if (node->tn_mode & (S_ISUID | S_ISGID)) { if (priv_check_cred(v->a_cred, PRIV_VFS_RETAINSUGID, 0)) node->tn_mode &= ~(S_ISUID | S_ISGID); } - if (error != 0) (void)tmpfs_reg_resize(vp, oldsize, TRUE); @@ -697,7 +506,7 @@ out: MPASS(IMPLIES(error == 0, uio->uio_resid == 0)); MPASS(IMPLIES(error != 0, oldsize == node->tn_size)); - return error; + return (error); } /* --------------------------------------------------------------------- */ diff --git a/sys/geom/eli/g_eli.c b/sys/geom/eli/g_eli.c index 6819fcc4424..18e3cc47585 100644 --- a/sys/geom/eli/g_eli.c +++ b/sys/geom/eli/g_eli.c @@ -621,21 +621,19 @@ end: * to close it when this situation occur. */ static void -g_eli_last_close(struct g_eli_softc *sc) +g_eli_last_close(void *arg, int flags __unused) { struct g_geom *gp; - struct g_provider *pp; - char ppname[64]; + char gpname[64]; int error; g_topology_assert(); - gp = sc->sc_geom; - pp = LIST_FIRST(&gp->provider); - strlcpy(ppname, pp->name, sizeof(ppname)); - error = g_eli_destroy(sc, TRUE); + gp = arg; + strlcpy(gpname, gp->name, sizeof(gpname)); + error = g_eli_destroy(gp->softc, TRUE); KASSERT(error == 0, ("Cannot detach %s on last close (error=%d).", - ppname, error)); - G_ELI_DEBUG(0, "Detached %s on last close.", ppname); + gpname, error)); + G_ELI_DEBUG(0, "Detached %s on last close.", gpname); } int @@ -665,7 +663,7 @@ g_eli_access(struct g_provider *pp, int dr, int dw, int de) */ if ((sc->sc_flags & G_ELI_FLAG_RW_DETACH) || (sc->sc_flags & G_ELI_FLAG_WOPEN)) { - g_eli_last_close(sc); + g_post_event(g_eli_last_close, gp, M_WAITOK, NULL); } return (0); } @@ -916,6 +914,10 @@ g_eli_destroy(struct g_eli_softc *sc, boolean_t force) if (force) { G_ELI_DEBUG(1, "Device %s is still open, so it " "cannot be definitely removed.", pp->name); + sc->sc_flags |= G_ELI_FLAG_RW_DETACH; + gp->access = g_eli_access; + g_wither_provider(pp, ENXIO); + return (EBUSY); } else { G_ELI_DEBUG(1, "Device %s is still open (r%dw%de%d).", pp->name, diff --git a/sys/geom/geom_dev.c b/sys/geom/geom_dev.c index f8b26802f75..17f24f8dcb2 100644 --- a/sys/geom/geom_dev.c +++ b/sys/geom/geom_dev.c @@ -79,7 +79,7 @@ static struct cdevsw g_dev_cdevsw = { .d_ioctl = g_dev_ioctl, .d_strategy = g_dev_strategy, .d_name = "g_dev", - .d_flags = D_DISK | D_TRACKCLOSE | D_UNMAPPED_IO, + .d_flags = D_DISK | D_TRACKCLOSE, }; static g_taste_t g_dev_taste; @@ -237,6 +237,7 @@ g_dev_taste(struct g_class *mp, struct g_provider *pp, int insist __unused) g_free(sc); return (NULL); } + dev->si_flags |= SI_UNMAPPED; sc->sc_dev = dev; /* Search for device alias name and create it if found. */ @@ -251,6 +252,7 @@ g_dev_taste(struct g_class *mp, struct g_provider *pp, int insist __unused) freeenv(val); make_dev_alias_p(MAKEDEV_CHECKNAME | MAKEDEV_WAITOK, &adev, dev, "%s", buf); + adev->si_flags |= SI_UNMAPPED; break; } } diff --git a/sys/geom/geom_disk.c b/sys/geom/geom_disk.c index 60cff73aecd..732b35dc015 100644 --- a/sys/geom/geom_disk.c +++ b/sys/geom/geom_disk.c @@ -460,6 +460,12 @@ g_disk_dumpconf(struct sbuf *sb, const char *indent, struct g_geom *gp, struct g if (dp->d_getattr(bp) == 0) sbuf_printf(sb, "%s%s\n", indent, buf); + bp->bio_attribute = "GEOM::lunname"; + bp->bio_length = DISK_IDENT_SIZE; + bp->bio_data = buf; + if (dp->d_getattr(bp) == 0) + sbuf_printf(sb, "%s%s\n", + indent, buf); g_destroy_bio(bp); g_free(buf); } else diff --git a/sys/geom/geom_pc98.c b/sys/geom/geom_pc98.c index 8e14f0998ec..42c9962583e 100644 --- a/sys/geom/geom_pc98.c +++ b/sys/geom/geom_pc98.c @@ -56,7 +56,7 @@ FEATURE(geom_pc98, "GEOM NEC PC9800 partitioning support"); struct g_pc98_softc { u_int fwsectors, fwheads, sectorsize; - int type[NDOSPART]; + int type[PC98_NPARTS]; u_char sec[8192]; }; @@ -84,8 +84,8 @@ static int g_pc98_modify(struct g_geom *gp, struct g_pc98_softc *ms, u_char *sec, int len __unused) { int i, error; - off_t s[NDOSPART], l[NDOSPART]; - struct pc98_partition dp[NDOSPART]; + off_t s[PC98_NPARTS], l[PC98_NPARTS]; + struct pc98_partition dp[PC98_NPARTS]; g_topology_assert(); @@ -114,11 +114,11 @@ g_pc98_modify(struct g_geom *gp, struct g_pc98_softc *ms, u_char *sec, int len _ return (EBUSY); #endif - for (i = 0; i < NDOSPART; i++) + for (i = 0; i < PC98_NPARTS; i++) pc98_partition_dec( sec + 512 + i * sizeof(struct pc98_partition), &dp[i]); - for (i = 0; i < NDOSPART; i++) { + for (i = 0; i < PC98_NPARTS; i++) { /* If start and end are identical it's bogus */ if (dp[i].dp_ssect == dp[i].dp_esect && dp[i].dp_shd == dp[i].dp_ehd && @@ -146,7 +146,7 @@ g_pc98_modify(struct g_geom *gp, struct g_pc98_softc *ms, u_char *sec, int len _ return (error); } - for (i = 0; i < NDOSPART; i++) { + for (i = 0; i < PC98_NPARTS; i++) { ms->type[i] = (dp[i].dp_sid << 8) | dp[i].dp_mid; g_slice_config(gp, i, G_SLICE_CONFIG_SET, s[i], l[i], ms->sectorsize, "%ss%d", gp->name, i + 1); @@ -269,7 +269,8 @@ g_pc98_taste(struct g_class *mp, struct g_provider *pp, int flags) if (flags == G_TF_NORMAL && !strcmp(pp->geom->class->name, PC98_CLASS_NAME)) return (NULL); - gp = g_slice_new(mp, NDOSPART, pp, &cp, &ms, sizeof *ms, g_pc98_start); + gp = g_slice_new(mp, PC98_NPARTS, pp, &cp, &ms, sizeof *ms, + g_pc98_start); if (gp == NULL) return (NULL); g_topology_unlock(); diff --git a/sys/geom/mirror/g_mirror.c b/sys/geom/mirror/g_mirror.c index f2d94dd561d..04233fec78e 100644 --- a/sys/geom/mirror/g_mirror.c +++ b/sys/geom/mirror/g_mirror.c @@ -2052,8 +2052,8 @@ g_mirror_launch_provider(struct g_mirror_softc *sc) } /* A provider underneath us doesn't support unmapped */ if ((dp->flags & G_PF_ACCEPT_UNMAPPED) == 0) { - G_MIRROR_DEBUG(0, "cancelling unmapped " - "because of %s\n", dp->name); + G_MIRROR_DEBUG(0, "Cancelling unmapped " + "because of %s.", dp->name); pp->flags &= ~G_PF_ACCEPT_UNMAPPED; } } diff --git a/sys/geom/part/g_part_gpt.c b/sys/geom/part/g_part_gpt.c index 12b0f8920b9..8634b1e9237 100644 --- a/sys/geom/part/g_part_gpt.c +++ b/sys/geom/part/g_part_gpt.c @@ -916,9 +916,10 @@ g_part_gpt_read(struct g_part_table *basetable, struct g_consumer *cp) basetable->gpt_first = table->hdr->hdr_lba_start; basetable->gpt_last = table->hdr->hdr_lba_end; - basetable->gpt_entries = table->hdr->hdr_entries; + basetable->gpt_entries = (table->hdr->hdr_lba_start - 2) * + pp->sectorsize / table->hdr->hdr_entsz; - for (index = basetable->gpt_entries - 1; index >= 0; index--) { + for (index = table->hdr->hdr_entries - 1; index >= 0; index--) { if (EQUUID(&tbl[index].ent_type, &gpt_uuid_unused)) continue; entry = (struct g_part_gpt_entry *)g_part_new_entry( diff --git a/sys/geom/part/g_part_ldm.c b/sys/geom/part/g_part_ldm.c index 81abc84365a..40c2eb877b0 100644 --- a/sys/geom/part/g_part_ldm.c +++ b/sys/geom/part/g_part_ldm.c @@ -339,8 +339,6 @@ static int g_part_ldm_read(struct g_part_table *, struct g_consumer *); static const char *g_part_ldm_type(struct g_part_table *, struct g_part_entry *, char *, size_t); static int g_part_ldm_write(struct g_part_table *, struct g_consumer *); -static int g_part_ldm_resize(struct g_part_table *, struct g_part_entry *, - struct g_part_parms *); static kobj_method_t g_part_ldm_methods[] = { KOBJMETHOD(g_part_add, g_part_ldm_add), @@ -350,7 +348,6 @@ static kobj_method_t g_part_ldm_methods[] = { KOBJMETHOD(g_part_dumpconf, g_part_ldm_dumpconf), KOBJMETHOD(g_part_dumpto, g_part_ldm_dumpto), KOBJMETHOD(g_part_modify, g_part_ldm_modify), - KOBJMETHOD(g_part_resize, g_part_ldm_resize), KOBJMETHOD(g_part_name, g_part_ldm_name), KOBJMETHOD(g_part_probe, g_part_ldm_probe), KOBJMETHOD(g_part_read, g_part_ldm_read), @@ -1206,14 +1203,6 @@ g_part_ldm_modify(struct g_part_table *basetable, return (ENOSYS); } -static int -g_part_ldm_resize(struct g_part_table *basetable, - struct g_part_entry *baseentry, struct g_part_parms *gpp) -{ - - return (ENOSYS); -} - static const char * g_part_ldm_name(struct g_part_table *table, struct g_part_entry *baseentry, char *buf, size_t bufsz) diff --git a/sys/geom/part/g_part_pc98.c b/sys/geom/part/g_part_pc98.c index 4d2b60c3644..d18d5cf5804 100644 --- a/sys/geom/part/g_part_pc98.c +++ b/sys/geom/part/g_part_pc98.c @@ -109,8 +109,8 @@ static struct g_part_scheme g_part_pc98_scheme = { g_part_pc98_methods, sizeof(struct g_part_pc98_table), .gps_entrysz = sizeof(struct g_part_pc98_entry), - .gps_minent = NDOSPART, - .gps_maxent = NDOSPART, + .gps_minent = PC98_NPARTS, + .gps_maxent = PC98_NPARTS, .gps_bootcodesz = BOOTSIZE, }; G_PART_SCHEME_DECLARE(g_part_pc98); @@ -259,7 +259,7 @@ g_part_pc98_create(struct g_part_table *basetable, struct g_part_parms *gpp) basetable->gpt_last = MIN(pp->mediasize / SECSIZE, UINT32_MAX) - 1; table = (struct g_part_pc98_table *)basetable; - le16enc(table->boot + DOSMAGICOFFSET, DOSMAGIC); + le16enc(table->boot + PC98_MAGICOFS, PC98_MAGIC); return (0); } @@ -396,8 +396,8 @@ g_part_pc98_probe(struct g_part_table *table, struct g_consumer *cp) /* We goto out on mismatch. */ res = ENXIO; - magic = le16dec(buf + DOSMAGICOFFSET); - if (magic != DOSMAGIC) + magic = le16dec(buf + PC98_MAGICOFS); + if (magic != PC98_MAGIC) goto out; sum = 0; @@ -408,8 +408,8 @@ g_part_pc98_probe(struct g_part_table *table, struct g_consumer *cp) goto out; } - for (index = 0; index < NDOSPART; index++) { - p = buf + SECSIZE + index * DOSPARTSIZE; + for (index = 0; index < PC98_NPARTS; index++) { + p = buf + SECSIZE + index * PC98_PARTSIZE; if (p[0] == 0 || p[1] == 0) /* !dp_mid || !dp_sid */ continue; scyl = le16dec(p + 10); @@ -456,8 +456,8 @@ g_part_pc98_read(struct g_part_table *basetable, struct g_consumer *cp) bcopy(buf + SECSIZE, table->table, sizeof(table->table)); bcopy(buf + SECSIZE*2, table->menu, sizeof(table->menu)); - for (index = NDOSPART - 1; index >= 0; index--) { - p = buf + SECSIZE + index * DOSPARTSIZE; + for (index = PC98_NPARTS - 1; index >= 0; index--) { + p = buf + SECSIZE + index * PC98_PARTSIZE; ent.dp_mid = p[0]; ent.dp_sid = p[1]; ent.dp_dum1 = p[2]; @@ -482,7 +482,7 @@ g_part_pc98_read(struct g_part_table *basetable, struct g_consumer *cp) entry->ent = ent; } - basetable->gpt_entries = NDOSPART; + basetable->gpt_entries = PC98_NPARTS; basetable->gpt_first = cyl; basetable->gpt_last = msize - 1; @@ -569,7 +569,7 @@ g_part_pc98_write(struct g_part_table *basetable, struct g_consumer *cp) table = (struct g_part_pc98_table *)basetable; baseentry = LIST_FIRST(&basetable->gpt_entry); for (index = 1; index <= basetable->gpt_entries; index++) { - p = table->table + (index - 1) * DOSPARTSIZE; + p = table->table + (index - 1) * PC98_PARTSIZE; entry = (baseentry != NULL && index == baseentry->gpe_index) ? (struct g_part_pc98_entry *)baseentry : NULL; if (entry != NULL && !baseentry->gpe_deleted) { @@ -589,7 +589,7 @@ g_part_pc98_write(struct g_part_table *basetable, struct g_consumer *cp) bcopy(entry->ent.dp_name, p + 16, sizeof(entry->ent.dp_name)); } else - bzero(p, DOSPARTSIZE); + bzero(p, PC98_PARTSIZE); if (entry != NULL) baseentry = LIST_NEXT(baseentry, gpe_entry); diff --git a/sys/geom/raid/g_raid.c b/sys/geom/raid/g_raid.c index fd6d69c7a04..41a1f9668cb 100644 --- a/sys/geom/raid/g_raid.c +++ b/sys/geom/raid/g_raid.c @@ -1863,6 +1863,11 @@ g_raid_access(struct g_provider *pp, int acr, int acw, int ace) error = ENXIO; goto out; } + /* Deny write opens for read-only volumes. */ + if (vol->v_read_only && acw > 0) { + error = EROFS; + goto out; + } if (dcw == 0) g_raid_clean(vol, dcw); vol->v_provider_open += acr + acw + ace; diff --git a/sys/geom/raid/g_raid.h b/sys/geom/raid/g_raid.h index 183edff67c2..993b1000b5f 100644 --- a/sys/geom/raid/g_raid.h +++ b/sys/geom/raid/g_raid.h @@ -306,6 +306,7 @@ struct g_raid_volume { int v_stopping; /* Volume is stopping */ int v_provider_open; /* Number of opens. */ int v_global_id; /* Global volume ID (rX). */ + int v_read_only; /* Volume is read-only. */ TAILQ_ENTRY(g_raid_volume) v_next; /* List of volumes entry. */ LIST_ENTRY(g_raid_volume) v_global_next; /* Global list entry. */ }; diff --git a/sys/geom/raid/tr_raid5.c b/sys/geom/raid/tr_raid5.c index 1931ff70ea7..6e54d168cea 100644 --- a/sys/geom/raid/tr_raid5.c +++ b/sys/geom/raid/tr_raid5.c @@ -106,7 +106,8 @@ g_raid_tr_taste_raid5(struct g_raid_tr_object *tr, struct g_raid_volume *vol) trs = (struct g_raid_tr_raid5_object *)tr; qual = tr->tro_volume->v_raid_level_qualifier; if (tr->tro_volume->v_raid_level == G_RAID_VOLUME_RL_RAID4 && - qual >= 0 && qual <= 1) { + (qual == G_RAID_VOLUME_RLQ_R4P0 || + qual == G_RAID_VOLUME_RLQ_R4PN)) { /* RAID4 */ } else if ((tr->tro_volume->v_raid_level == G_RAID_VOLUME_RL_RAID5 || tr->tro_volume->v_raid_level == G_RAID_VOLUME_RL_RAID5E || @@ -114,7 +115,10 @@ g_raid_tr_taste_raid5(struct g_raid_tr_object *tr, struct g_raid_volume *vol) tr->tro_volume->v_raid_level == G_RAID_VOLUME_RL_RAID5R || tr->tro_volume->v_raid_level == G_RAID_VOLUME_RL_RAID6 || tr->tro_volume->v_raid_level == G_RAID_VOLUME_RL_RAIDMDF) && - qual >= 0 && qual <= 3) { + (qual == G_RAID_VOLUME_RLQ_R5RA || + qual == G_RAID_VOLUME_RLQ_R5RS || + qual == G_RAID_VOLUME_RLQ_R5LA || + qual == G_RAID_VOLUME_RLQ_R5LS)) { /* RAID5/5E/5EE/5R/6/MDF */ } else return (G_RAID_TR_TASTE_FAIL); @@ -181,8 +185,9 @@ g_raid_tr_start_raid5(struct g_raid_tr_object *tr) struct g_raid_volume *vol; trs = (struct g_raid_tr_raid5_object *)tr; - vol = tr->tro_volume; trs->trso_starting = 0; + vol = tr->tro_volume; + vol->v_read_only = 1; g_raid_tr_update_state_raid5(vol, NULL); return (0); } diff --git a/sys/geom/zero/g_zero.c b/sys/geom/zero/g_zero.c index 1c2c54f37a0..311db54402b 100644 --- a/sys/geom/zero/g_zero.c +++ b/sys/geom/zero/g_zero.c @@ -41,16 +41,37 @@ __FBSDID("$FreeBSD$"); #define G_ZERO_CLASS_NAME "ZERO" +static int g_zero_clear_sysctl(SYSCTL_HANDLER_ARGS); + SYSCTL_DECL(_kern_geom); static SYSCTL_NODE(_kern_geom, OID_AUTO, zero, CTLFLAG_RW, 0, "GEOM_ZERO stuff"); static int g_zero_clear = 1; -SYSCTL_INT(_kern_geom_zero, OID_AUTO, clear, CTLFLAG_RW, &g_zero_clear, 0, - "Clear read data buffer"); +SYSCTL_PROC(_kern_geom_zero, OID_AUTO, clear, CTLTYPE_INT|CTLFLAG_RW, + &g_zero_clear, 0, g_zero_clear_sysctl, "I", "Clear read data buffer"); static int g_zero_byte = 0; SYSCTL_INT(_kern_geom_zero, OID_AUTO, byte, CTLFLAG_RW, &g_zero_byte, 0, "Byte (octet) value to clear the buffers with"); +static struct g_provider *gpp; + +static int +g_zero_clear_sysctl(SYSCTL_HANDLER_ARGS) +{ + int error; + + error = sysctl_handle_int(oidp, &g_zero_clear, 0, req); + if (error != 0 || req->newptr == NULL) + return (error); + if (gpp == NULL) + return (ENXIO); + if (g_zero_clear) + gpp->flags &= ~G_PF_ACCEPT_UNMAPPED; + else + gpp->flags |= G_PF_ACCEPT_UNMAPPED; + return (0); +} + static void g_zero_start(struct bio *bp) { @@ -58,7 +79,7 @@ g_zero_start(struct bio *bp) switch (bp->bio_cmd) { case BIO_READ: - if (g_zero_clear) + if (g_zero_clear && (bp->bio_flags & BIO_UNMAPPED) == 0) memset(bp->bio_data, g_zero_byte, bp->bio_length); /* FALLTHROUGH */ case BIO_DELETE: @@ -84,7 +105,9 @@ g_zero_init(struct g_class *mp) gp = g_new_geomf(mp, "gzero"); gp->start = g_zero_start; gp->access = g_std_access; - pp = g_new_providerf(gp, "%s", gp->name); + gpp = pp = g_new_providerf(gp, "%s", gp->name); + if (!g_zero_clear) + pp->flags |= G_PF_ACCEPT_UNMAPPED; pp->mediasize = 1152921504606846976LLU; pp->sectorsize = 512; g_error_provider(pp, 0); @@ -104,6 +127,7 @@ g_zero_destroy_geom(struct gctl_req *req __unused, struct g_class *mp __unused, return (0); if (pp->acr > 0 || pp->acw > 0 || pp->ace > 0) return (EBUSY); + gpp = NULL; g_wither_geom(gp, ENXIO); return (0); } diff --git a/sys/i386/conf/GENERIC b/sys/i386/conf/GENERIC index 93f23db86ae..334a654bff9 100644 --- a/sys/i386/conf/GENERIC +++ b/sys/i386/conf/GENERIC @@ -67,6 +67,7 @@ options HWPMC_HOOKS # Necessary kernel hooks for hwpmc(4) options AUDIT # Security event auditing options CAPABILITY_MODE # Capsicum capability mode options CAPABILITIES # Capsicum capabilities +options PROCDESC # Support for process descriptors options MAC # TrustedBSD MAC Framework options KDTRACE_HOOKS # Kernel DTrace hooks options DDB_CTF # Kernel ELF linker loads CTF data @@ -74,9 +75,8 @@ options INCLUDE_CONFIG_FILE # Include this file in kernel # Debugging support. Always need this: options KDB # Enable kernel debugger support. -# For minimum debugger support (stable branch) use: -#options KDB_TRACE # Print a stack trace for a panic. -# For full debugger support use this instead: +options KDB_TRACE # Print a stack trace for a panic. +# For full debugger support use (turn off in stable branch): options DDB # Support DDB. options GDB # Support remote GDB. options DEADLKRES # Enable the deadlock resolver @@ -307,8 +307,8 @@ device wpi # Intel 3945ABG wireless NICs. # Pseudo devices. device loop # Network loopback device random # Entropy device -options PADLOCK_RNG # VIA Padlock RNG -options RDRAND_RNG # Intel Bull Mountain RNG +device padlock_rng # VIA Padlock RNG +device rdrand_rng # Intel Bull Mountain RNG device ether # Ethernet support device vlan # 802.1Q VLAN support device tun # Packet tunnel. @@ -354,3 +354,6 @@ device vtnet # VirtIO Ethernet device device virtio_blk # VirtIO Block device device virtio_scsi # VirtIO SCSI device device virtio_balloon # VirtIO Memory Balloon device + +# VMware support +device vmx # VMware VMXNET3 Ethernet diff --git a/sys/i386/conf/NOTES b/sys/i386/conf/NOTES index 36744abdfdb..92be9473439 100644 --- a/sys/i386/conf/NOTES +++ b/sys/i386/conf/NOTES @@ -580,6 +580,7 @@ hint.mse.0.irq="5" # nfe: nVidia nForce MCP on-board Ethernet Networking (BSD open source) # nve: nVidia nForce MCP on-board Ethernet Networking # sbni: Granch SBNI12-xx ISA and PCI adapters +# vmx: VMware VMXNET3 Ethernet (BSD open source) # wl: Lucent Wavelan (ISA card only). # wpi: Intel 3945ABG Wireless LAN controller # Requires the wpi firmware module @@ -629,6 +630,7 @@ hint.sbni.0.at="isa" hint.sbni.0.port="0x210" hint.sbni.0.irq="0xefdead" hint.sbni.0.flags="0" +device vmx # VMware VMXNET3 Ethernet device wl hint.wl.0.at="isa" hint.wl.0.port="0x300" @@ -850,6 +852,8 @@ hint.spic.0.port="0x10a0" device asmc #device si device tpm +device padlock_rng # VIA Padlock RNG +device rdrand_rng # Intel Bull Mountain RNG # # Laptop/Notebook options: diff --git a/sys/i386/i386/apic_vector.s b/sys/i386/i386/apic_vector.s index 618436d830a..adedbc4999f 100644 --- a/sys/i386/i386/apic_vector.s +++ b/sys/i386/i386/apic_vector.s @@ -138,6 +138,25 @@ IDTVEC(errorint) MEXITCOUNT jmp doreti +#ifdef XENHVM +/* + * Xen event channel upcall interrupt handler. + * Only used when the hypervisor supports direct vector callbacks. + */ + .text + SUPERALIGN_TEXT +IDTVEC(xen_intr_upcall) + PUSH_FRAME + SET_KERNEL_SREGS + cld + FAKE_MCOUNT(TF_EIP(%esp)) + pushl %esp + call xen_intr_handle_upcall + add $4, %esp + MEXITCOUNT + jmp doreti +#endif + #ifdef SMP /* * Global address space TLB shootdown. diff --git a/sys/i386/i386/genassym.c b/sys/i386/i386/genassym.c index 2394c09f480..78e4a611190 100644 --- a/sys/i386/i386/genassym.c +++ b/sys/i386/i386/genassym.c @@ -246,9 +246,8 @@ ASSYM(BUS_SPACE_HANDLE_IAT, offsetof(struct bus_space_handle, bsh_iat)); #endif #ifdef XEN -#include ASSYM(PC_CR3, offsetof(struct pcpu, pc_cr3)); -ASSYM(HYPERVISOR_VIRT_START, __HYPERVISOR_VIRT_START); +ASSYM(XEN_HYPERVISOR_VIRT_START, HYPERVISOR_VIRT_START); #endif #ifdef HWPMC_HOOKS diff --git a/sys/i386/i386/initcpu.c b/sys/i386/i386/initcpu.c index 5f33897360c..71c57b25816 100644 --- a/sys/i386/i386/initcpu.c +++ b/sys/i386/i386/initcpu.c @@ -423,6 +423,19 @@ init_6x86(void) #endif /* I486_CPU */ #ifdef I586_CPU +/* + * Rise mP6 + */ +static void +init_rise(void) +{ + + /* + * The CMPXCHG8B instruction is always available but hidden. + */ + cpu_feature |= CPUID_CX8; +} + /* * IDT WinChip C6/2/2A/2B/3 * @@ -690,6 +703,9 @@ initializecpu(void) case CPU_VENDOR_TRANSMETA: init_transmeta(); break; + case CPU_VENDOR_RISE: + init_rise(); + break; } break; #endif diff --git a/sys/i386/i386/machdep.c b/sys/i386/i386/machdep.c index 51765e98a73..c43031644f8 100644 --- a/sys/i386/i386/machdep.c +++ b/sys/i386/i386/machdep.c @@ -160,9 +160,8 @@ uint32_t arch_i386_xbox_memsize = 0; #ifdef XEN /* XEN includes */ -#include +#include #include -#include #include #include #include @@ -1216,6 +1215,13 @@ cpu_est_clockrate(int cpu_id, uint64_t *rate) #ifdef XEN +static void +idle_block(void) +{ + + HYPERVISOR_sched_op(SCHEDOP_block, 0); +} + void cpu_halt(void) { @@ -1548,22 +1554,6 @@ idle_sysctl(SYSCTL_HANDLER_ARGS) SYSCTL_PROC(_machdep, OID_AUTO, idle, CTLTYPE_STRING | CTLFLAG_RW, 0, 0, idle_sysctl, "A", "currently selected idle function"); -uint64_t (*atomic_load_acq_64)(volatile uint64_t *) = - atomic_load_acq_64_i386; -void (*atomic_store_rel_64)(volatile uint64_t *, uint64_t) = - atomic_store_rel_64_i386; - -static void -cpu_probe_cmpxchg8b(void) -{ - - if ((cpu_feature & CPUID_CX8) != 0 || - cpu_vendor_id == CPU_VENDOR_RISE) { - atomic_load_acq_64 = atomic_load_acq_64_i586; - atomic_store_rel_64 = atomic_store_rel_64_i586; - } -} - /* * Reset registers to default values on exec. */ @@ -1975,6 +1965,9 @@ extern inthand_t IDTVEC(xmm), #ifdef KDTRACE_HOOKS IDTVEC(dtrace_ret), +#endif +#ifdef XENHVM + IDTVEC(xen_intr_upcall), #endif IDTVEC(lcall_syscall), IDTVEC(int0x80_syscall); @@ -2825,7 +2818,6 @@ init386(first) thread0.td_pcb->pcb_gsd = PCPU_GET(fsgs_gdt)[1]; cpu_probe_amdc1e(); - cpu_probe_cmpxchg8b(); } #else @@ -2965,6 +2957,10 @@ init386(first) setidt(IDT_DTRACE_RET, &IDTVEC(dtrace_ret), SDT_SYS386TGT, SEL_UPL, GSEL(GCODE_SEL, SEL_KPL)); #endif +#ifdef XENHVM + setidt(IDT_EVTCHN, &IDTVEC(xen_intr_upcall), SDT_SYS386IGT, SEL_UPL, + GSEL(GCODE_SEL, SEL_KPL)); +#endif r_idt.rd_limit = sizeof(idt0) - 1; r_idt.rd_base = (int) idt; @@ -3116,7 +3112,6 @@ init386(first) thread0.td_frame = &proc0_tf; cpu_probe_amdc1e(); - cpu_probe_cmpxchg8b(); #ifdef FDT x86_init_fdt(); @@ -3178,9 +3173,9 @@ f00f_hack(void *unused) printf("Intel Pentium detected, installing workaround for F00F bug\n"); - tmp = kmem_alloc(kernel_map, PAGE_SIZE * 2); + tmp = kmem_malloc(kernel_arena, PAGE_SIZE * 2, M_WAITOK | M_ZERO); if (tmp == 0) - panic("kmem_alloc returned 0"); + panic("kmem_malloc returned 0"); /* Put the problematic entry (#6) at the end of the lower page. */ new_idt = (struct gate_descriptor*) @@ -3189,9 +3184,7 @@ f00f_hack(void *unused) r_idt.rd_base = (u_int)new_idt; lidt(&r_idt); idt = new_idt; - if (vm_map_protect(kernel_map, tmp, tmp + PAGE_SIZE, - VM_PROT_READ, FALSE) != KERN_SUCCESS) - panic("vm_map_protect failed"); + pmap_protect(kernel_pmap, tmp, tmp + PAGE_SIZE, VM_PROT_READ); } #endif /* defined(I586_CPU) && !NO_F00F_HACK */ diff --git a/sys/i386/i386/mp_machdep.c b/sys/i386/i386/mp_machdep.c index 9b832ed2d21..60b36a24f97 100644 --- a/sys/i386/i386/mp_machdep.c +++ b/sys/i386/i386/mp_machdep.c @@ -81,6 +81,11 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include + +#ifdef XENHVM +#include +#endif #define WARMBOOT_TARGET 0 #define WARMBOOT_OFF (KERNBASE + 0x0467) @@ -166,6 +171,11 @@ u_long *ipi_lazypmap_counts[MAXCPU]; static u_long *ipi_hardclock_counts[MAXCPU]; #endif +/* Default cpu_ops implementation. */ +struct cpu_ops cpu_ops = { + .ipi_vectored = lapic_ipi_vectored +}; + /* * Local data and functions. */ @@ -747,6 +757,11 @@ init_secondary(void) /* set up SSE registers */ enable_sse(); +#ifdef XENHVM + /* register vcpu_info area */ + xen_hvm_init_cpu(); +#endif + #ifdef PAE /* Enable the PTE no-execute bit. */ if ((amd_feature & AMDID_NX) != 0) { @@ -959,8 +974,10 @@ start_all_aps(void) /* allocate and set up a boot stack data page */ bootstacks[cpu] = - (char *)kmem_alloc(kernel_map, KSTACK_PAGES * PAGE_SIZE); - dpcpu = (void *)kmem_alloc(kernel_map, DPCPU_SIZE); + (char *)kmem_malloc(kernel_arena, KSTACK_PAGES * PAGE_SIZE, + M_WAITOK | M_ZERO); + dpcpu = (void *)kmem_malloc(kernel_arena, DPCPU_SIZE, + M_WAITOK | M_ZERO); /* setup a vector to our boot code */ *((volatile u_short *) WARMBOOT_OFF) = WARMBOOT_TARGET; *((volatile u_short *) WARMBOOT_SEG) = (boot_address >> 4); @@ -1198,7 +1215,7 @@ ipi_send_cpu(int cpu, u_int ipi) if (old_pending) return; } - lapic_ipi_vectored(ipi, cpu_apic_ids[cpu]); + cpu_ops.ipi_vectored(ipi, cpu_apic_ids[cpu]); } /* @@ -1449,7 +1466,7 @@ ipi_all_but_self(u_int ipi) CPU_OR_ATOMIC(&ipi_nmi_pending, &other_cpus); CTR2(KTR_SMP, "%s: ipi: %x", __func__, ipi); - lapic_ipi_vectored(ipi, APIC_IPI_DEST_OTHERS); + cpu_ops.ipi_vectored(ipi, APIC_IPI_DEST_OTHERS); } int diff --git a/sys/i386/i386/pmap.c b/sys/i386/i386/pmap.c index 77b0235a97a..c9f327c3fd1 100644 --- a/sys/i386/i386/pmap.c +++ b/sys/i386/i386/pmap.c @@ -304,7 +304,7 @@ static boolean_t pmap_enter_pde(pmap_t pmap, vm_offset_t va, vm_page_t m, static vm_page_t pmap_enter_quick_locked(pmap_t pmap, vm_offset_t va, vm_page_t m, vm_prot_t prot, vm_page_t mpte); static void pmap_flush_page(vm_page_t m); -static void pmap_insert_pt_page(pmap_t pmap, vm_page_t mpte); +static int pmap_insert_pt_page(pmap_t pmap, vm_page_t mpte); static void pmap_fill_ptp(pt_entry_t *firstpte, pt_entry_t newpte); static boolean_t pmap_is_modified_pvh(struct md_page *pvh); static boolean_t pmap_is_referenced_pvh(struct md_page *pvh); @@ -317,12 +317,12 @@ static boolean_t pmap_protect_pde(pmap_t pmap, pd_entry_t *pde, vm_offset_t sva, vm_prot_t prot); static void pmap_pte_attr(pt_entry_t *pte, int cache_bits); static void pmap_remove_pde(pmap_t pmap, pd_entry_t *pdq, vm_offset_t sva, - vm_page_t *free); + struct spglist *free); static int pmap_remove_pte(pmap_t pmap, pt_entry_t *ptq, vm_offset_t sva, - vm_page_t *free); + struct spglist *free); static void pmap_remove_pt_page(pmap_t pmap, vm_page_t mpte); static void pmap_remove_page(struct pmap *pmap, vm_offset_t va, - vm_page_t *free); + struct spglist *free); static void pmap_remove_entry(struct pmap *pmap, vm_page_t m, vm_offset_t va); static void pmap_insert_entry(pmap_t pmap, vm_offset_t va, vm_page_t m); @@ -335,10 +335,10 @@ static void pmap_update_pde_invalidate(vm_offset_t va, pd_entry_t newpde); static vm_page_t pmap_allocpte(pmap_t pmap, vm_offset_t va, int flags); static vm_page_t _pmap_allocpte(pmap_t pmap, u_int ptepindex, int flags); -static void _pmap_unwire_ptp(pmap_t pmap, vm_page_t m, vm_page_t *free); +static void _pmap_unwire_ptp(pmap_t pmap, vm_page_t m, struct spglist *free); static pt_entry_t *pmap_pte_quick(pmap_t pmap, vm_offset_t va); static void pmap_pte_release(pt_entry_t *pte); -static int pmap_unuse_pt(pmap_t, vm_offset_t, vm_page_t *); +static int pmap_unuse_pt(pmap_t, vm_offset_t, struct spglist *); #ifdef PAE static void *pmap_pdpt_allocf(uma_zone_t zone, int bytes, u_int8_t *flags, int wait); #endif @@ -655,7 +655,7 @@ pmap_pdpt_allocf(uma_zone_t zone, int bytes, u_int8_t *flags, int wait) /* Inform UMA that this allocator uses kernel_map/object. */ *flags = UMA_SLAB_KERNEL; - return ((void *)kmem_alloc_contig(kernel_map, bytes, wait, 0x0ULL, + return ((void *)kmem_alloc_contig(kernel_arena, bytes, wait, 0x0ULL, 0xffffffffULL, 1, 0, VM_MEMATTR_DEFAULT)); } #endif @@ -783,13 +783,13 @@ pmap_init(void) */ s = (vm_size_t)(pv_npg * sizeof(struct md_page)); s = round_page(s); - pv_table = (struct md_page *)kmem_alloc(kernel_map, s); + pv_table = (struct md_page *)kmem_malloc(kernel_arena, s, + M_WAITOK | M_ZERO); for (i = 0; i < pv_npg; i++) TAILQ_INIT(&pv_table[i].pv_list); pv_maxchunks = MAX(pv_entry_max / _NPCPV, maxproc); - pv_chunkbase = (struct pv_chunk *)kmem_alloc_nofault(kernel_map, - PAGE_SIZE * pv_maxchunks); + pv_chunkbase = (struct pv_chunk *)kva_alloc(PAGE_SIZE * pv_maxchunks); if (pv_chunkbase == NULL) panic("pmap_init: not enough kvm for pv chunks"); pmap_ptelist_init(&pv_vafree, pv_chunkbase, pv_maxchunks); @@ -1568,14 +1568,12 @@ pmap_qremove(vm_offset_t sva, int count) * Page table page management routines..... ***************************************************/ static __inline void -pmap_free_zero_pages(vm_page_t free) +pmap_free_zero_pages(struct spglist *free) { vm_page_t m; - while (free != NULL) { - m = free; - free = (void *)m->object; - m->object = NULL; + while ((m = SLIST_FIRST(free)) != NULL) { + SLIST_REMOVE_HEAD(free, plinks.s.ss); /* Preserve the page's PG_ZERO setting. */ vm_page_free_toq(m); } @@ -1587,15 +1585,15 @@ pmap_free_zero_pages(vm_page_t free) * physical memory manager after the TLB has been updated. */ static __inline void -pmap_add_delayed_free_list(vm_page_t m, vm_page_t *free, boolean_t set_PG_ZERO) +pmap_add_delayed_free_list(vm_page_t m, struct spglist *free, + boolean_t set_PG_ZERO) { if (set_PG_ZERO) m->flags |= PG_ZERO; else m->flags &= ~PG_ZERO; - m->object = (void *)*free; - *free = m; + SLIST_INSERT_HEAD(free, m, plinks.s.ss); } /* @@ -1604,12 +1602,12 @@ pmap_add_delayed_free_list(vm_page_t m, vm_page_t *free, boolean_t set_PG_ZERO) * for mapping a distinct range of virtual addresses. The pmap's collection is * ordered by this virtual address range. */ -static __inline void +static __inline int pmap_insert_pt_page(pmap_t pmap, vm_page_t mpte) { PMAP_LOCK_ASSERT(pmap, MA_OWNED); - vm_radix_insert(&pmap->pm_root, mpte); + return (vm_radix_insert(&pmap->pm_root, mpte)); } /* @@ -1645,7 +1643,7 @@ pmap_remove_pt_page(pmap_t pmap, vm_page_t mpte) * page table page was unmapped and FALSE otherwise. */ static inline boolean_t -pmap_unwire_ptp(pmap_t pmap, vm_page_t m, vm_page_t *free) +pmap_unwire_ptp(pmap_t pmap, vm_page_t m, struct spglist *free) { --m->wire_count; @@ -1657,7 +1655,7 @@ pmap_unwire_ptp(pmap_t pmap, vm_page_t m, vm_page_t *free) } static void -_pmap_unwire_ptp(pmap_t pmap, vm_page_t m, vm_page_t *free) +_pmap_unwire_ptp(pmap_t pmap, vm_page_t m, struct spglist *free) { vm_offset_t pteva; @@ -1693,7 +1691,7 @@ _pmap_unwire_ptp(pmap_t pmap, vm_page_t m, vm_page_t *free) * conditionally free the page, and manage the hold/wire counts. */ static int -pmap_unuse_pt(pmap_t pmap, vm_offset_t va, vm_page_t *free) +pmap_unuse_pt(pmap_t pmap, vm_offset_t va, struct spglist *free) { pd_entry_t ptepde; vm_page_t mpte; @@ -1740,15 +1738,12 @@ pmap_pinit(pmap_t pmap) vm_paddr_t pa; int i; - PMAP_LOCK_INIT(pmap); - /* * No need to allocate page table space yet but we do need a valid * page directory table. */ if (pmap->pm_pdir == NULL) { - pmap->pm_pdir = (pd_entry_t *)kmem_alloc_nofault(kernel_map, - NBPTD); + pmap->pm_pdir = (pd_entry_t *)kva_alloc(NBPTD); if (pmap->pm_pdir == NULL) { PMAP_LOCK_DESTROY(pmap); return (0); @@ -2054,7 +2049,6 @@ pmap_release(pmap_t pmap) atomic_subtract_int(&cnt.v_wire_count, 1); vm_page_free_zero(m); } - PMAP_LOCK_DESTROY(pmap); } static int @@ -2194,16 +2188,18 @@ pmap_pv_reclaim(pmap_t locked_pmap) pt_entry_t *pte, tpte; pv_entry_t pv; vm_offset_t va; - vm_page_t free, m, m_pc; + vm_page_t m, m_pc; + struct spglist free; uint32_t inuse; int bit, field, freed; PMAP_LOCK_ASSERT(locked_pmap, MA_OWNED); pmap = NULL; - free = m_pc = NULL; + m_pc = NULL; + SLIST_INIT(&free); TAILQ_INIT(&newtail); while ((pc = TAILQ_FIRST(&pv_chunks)) != NULL && (pv_vafree == 0 || - free == NULL)) { + SLIST_EMPTY(&free))) { TAILQ_REMOVE(&pv_chunks, pc, pc_lru); if (pmap != pc->pc_pmap) { if (pmap != NULL) { @@ -2308,15 +2304,14 @@ out: if (pmap != locked_pmap) PMAP_UNLOCK(pmap); } - if (m_pc == NULL && pv_vafree != 0 && free != NULL) { - m_pc = free; - free = (void *)m_pc->object; - m_pc->object = NULL; + if (m_pc == NULL && pv_vafree != 0 && SLIST_EMPTY(&free)) { + m_pc = SLIST_FIRST(&free); + SLIST_REMOVE_HEAD(&free, plinks.s.ss); /* Recycle a freed page table page. */ m_pc->wire_count = 1; atomic_add_int(&cnt.v_wire_count, 1); } - pmap_free_zero_pages(free); + pmap_free_zero_pages(&free); return (m_pc); } @@ -2637,14 +2632,15 @@ pmap_demote_pde(pmap_t pmap, pd_entry_t *pde, vm_offset_t va) pd_entry_t newpde, oldpde; pt_entry_t *firstpte, newpte; vm_paddr_t mptepa; - vm_page_t free, mpte; + vm_page_t mpte; + struct spglist free; PMAP_LOCK_ASSERT(pmap, MA_OWNED); oldpde = *pde; KASSERT((oldpde & (PG_PS | PG_V)) == (PG_PS | PG_V), ("pmap_demote_pde: oldpde is missing PG_PS and/or PG_V")); - mpte = pmap_lookup_pt_page(pmap, va); - if (mpte != NULL) + if ((oldpde & PG_A) != 0 && (mpte = pmap_lookup_pt_page(pmap, va)) != + NULL) pmap_remove_pt_page(pmap, mpte); else { KASSERT((oldpde & PG_W) == 0, @@ -2659,10 +2655,10 @@ pmap_demote_pde(pmap_t pmap, pd_entry_t *pde, vm_offset_t va) if ((oldpde & PG_A) == 0 || (mpte = vm_page_alloc(NULL, va >> PDRSHIFT, VM_ALLOC_NOOBJ | VM_ALLOC_NORMAL | VM_ALLOC_WIRED)) == NULL) { - free = NULL; + SLIST_INIT(&free); pmap_remove_pde(pmap, pde, trunc_4mpage(va), &free); pmap_invalidate_page(pmap, trunc_4mpage(va)); - pmap_free_zero_pages(free); + pmap_free_zero_pages(&free); CTR2(KTR_PMAP, "pmap_demote_pde: failure for va %#x" " in pmap %p", va, pmap); return (FALSE); @@ -2815,7 +2811,7 @@ pmap_remove_kernel_pde(pmap_t pmap, pd_entry_t *pde, vm_offset_t va) */ static void pmap_remove_pde(pmap_t pmap, pd_entry_t *pdq, vm_offset_t sva, - vm_page_t *free) + struct spglist *free) { struct md_page *pvh; pd_entry_t oldpde; @@ -2871,7 +2867,8 @@ pmap_remove_pde(pmap_t pmap, pd_entry_t *pdq, vm_offset_t sva, * pmap_remove_pte: do the things to unmap a page in a process */ static int -pmap_remove_pte(pmap_t pmap, pt_entry_t *ptq, vm_offset_t va, vm_page_t *free) +pmap_remove_pte(pmap_t pmap, pt_entry_t *ptq, vm_offset_t va, + struct spglist *free) { pt_entry_t oldpte; vm_page_t m; @@ -2905,7 +2902,7 @@ pmap_remove_pte(pmap_t pmap, pt_entry_t *ptq, vm_offset_t va, vm_page_t *free) * Remove a single page from a process address space */ static void -pmap_remove_page(pmap_t pmap, vm_offset_t va, vm_page_t *free) +pmap_remove_page(pmap_t pmap, vm_offset_t va, struct spglist *free) { pt_entry_t *pte; @@ -2930,7 +2927,7 @@ pmap_remove(pmap_t pmap, vm_offset_t sva, vm_offset_t eva) vm_offset_t pdnxt; pd_entry_t ptpaddr; pt_entry_t *pte; - vm_page_t free = NULL; + struct spglist free; int anyvalid; /* @@ -2940,6 +2937,7 @@ pmap_remove(pmap_t pmap, vm_offset_t sva, vm_offset_t eva) return; anyvalid = 0; + SLIST_INIT(&free); rw_wlock(&pvh_global_lock); sched_pin(); @@ -3032,7 +3030,7 @@ out: pmap_invalidate_all(pmap); rw_wunlock(&pvh_global_lock); PMAP_UNLOCK(pmap); - pmap_free_zero_pages(free); + pmap_free_zero_pages(&free); } /* @@ -3057,11 +3055,11 @@ pmap_remove_all(vm_page_t m) pt_entry_t *pte, tpte; pd_entry_t *pde; vm_offset_t va; - vm_page_t free; + struct spglist free; KASSERT((m->oflags & VPO_UNMANAGED) == 0, ("pmap_remove_all: page %p is not managed", m)); - free = NULL; + SLIST_INIT(&free); rw_wlock(&pvh_global_lock); sched_pin(); if ((m->flags & PG_FICTITIOUS) != 0) @@ -3106,7 +3104,7 @@ small_mappings: vm_page_aflag_clear(m, PGA_WRITEABLE); sched_unpin(); rw_wunlock(&pvh_global_lock); - pmap_free_zero_pages(free); + pmap_free_zero_pages(&free); } /* @@ -3402,7 +3400,13 @@ setpte: ("pmap_promote_pde: page table page is out of range")); KASSERT(mpte->pindex == va >> PDRSHIFT, ("pmap_promote_pde: page table page's pindex is wrong")); - pmap_insert_pt_page(pmap, mpte); + if (pmap_insert_pt_page(pmap, mpte)) { + pmap_pde_p_failures++; + CTR2(KTR_PMAP, + "pmap_promote_pde: failure for va %#x in pmap %p", va, + pmap); + return; + } /* * Promote the pv entries. @@ -3460,7 +3464,7 @@ pmap_enter(pmap_t pmap, vm_offset_t va, vm_prot_t access, vm_page_t m, KASSERT(va < UPT_MIN_ADDRESS || va >= UPT_MAX_ADDRESS, ("pmap_enter: invalid to pmap_enter page table pages (va: 0x%x)", va)); - if ((m->oflags & (VPO_UNMANAGED | VPO_BUSY)) == 0) + if ((m->oflags & VPO_UNMANAGED) == 0 && !vm_page_xbusied(m)) VM_OBJECT_ASSERT_WLOCKED(m->object); mpte = NULL; @@ -3764,7 +3768,7 @@ pmap_enter_quick_locked(pmap_t pmap, vm_offset_t va, vm_page_t m, { pt_entry_t *pte; vm_paddr_t pa; - vm_page_t free; + struct spglist free; KASSERT(va < kmi.clean_sva || va >= kmi.clean_eva || (m->oflags & VPO_UNMANAGED) != 0, @@ -3833,10 +3837,10 @@ pmap_enter_quick_locked(pmap_t pmap, vm_offset_t va, vm_page_t m, if ((m->oflags & VPO_UNMANAGED) == 0 && !pmap_try_insert_pv_entry(pmap, va, m)) { if (mpte != NULL) { - free = NULL; + SLIST_INIT(&free); if (pmap_unwire_ptp(pmap, mpte, &free)) { pmap_invalidate_page(pmap, va); - pmap_free_zero_pages(free); + pmap_free_zero_pages(&free); } mpte = NULL; @@ -4019,7 +4023,7 @@ void pmap_copy(pmap_t dst_pmap, pmap_t src_pmap, vm_offset_t dst_addr, vm_size_t len, vm_offset_t src_addr) { - vm_page_t free; + struct spglist free; vm_offset_t addr; vm_offset_t end_addr = src_addr + len; vm_offset_t pdnxt; @@ -4058,6 +4062,8 @@ pmap_copy(pmap_t dst_pmap, pmap_t src_pmap, vm_offset_t dst_addr, vm_size_t len, continue; if (srcptepaddr & PG_PS) { + if ((addr & PDRMASK) != 0 || addr + NBPDR > end_addr) + continue; if (dst_pmap->pm_pdir[ptepindex] == 0 && ((srcptepaddr & PG_MANAGED) == 0 || pmap_pv_insert_pde(dst_pmap, addr, srcptepaddr & @@ -4102,12 +4108,12 @@ pmap_copy(pmap_t dst_pmap, pmap_t src_pmap, vm_offset_t dst_addr, vm_size_t len, PG_A); dst_pmap->pm_stats.resident_count++; } else { - free = NULL; + SLIST_INIT(&free); if (pmap_unwire_ptp(dst_pmap, dstmpte, &free)) { pmap_invalidate_page(dst_pmap, addr); - pmap_free_zero_pages(free); + pmap_free_zero_pages(&free); } goto out; } @@ -4414,11 +4420,11 @@ void pmap_remove_pages(pmap_t pmap) { pt_entry_t *pte, tpte; - vm_page_t free = NULL; vm_page_t m, mpte, mt; pv_entry_t pv; struct md_page *pvh; struct pv_chunk *pc, *npc; + struct spglist free; int field, idx; int32_t bit; uint32_t inuse, bitmask; @@ -4428,6 +4434,7 @@ pmap_remove_pages(pmap_t pmap) printf("warning: pmap_remove_pages called with non-current pmap\n"); return; } + SLIST_INIT(&free); rw_wlock(&pvh_global_lock); PMAP_LOCK(pmap); sched_pin(); @@ -4536,7 +4543,7 @@ pmap_remove_pages(pmap_t pmap) pmap_invalidate_all(pmap); rw_wunlock(&pvh_global_lock); PMAP_UNLOCK(pmap); - pmap_free_zero_pages(free); + pmap_free_zero_pages(&free); } /* @@ -4554,13 +4561,12 @@ pmap_is_modified(vm_page_t m) ("pmap_is_modified: page %p is not managed", m)); /* - * If the page is not VPO_BUSY, then PGA_WRITEABLE cannot be + * If the page is not exclusive busied, then PGA_WRITEABLE cannot be * concurrently set while the object is locked. Thus, if PGA_WRITEABLE * is clear, no PTEs can have PG_M set. */ VM_OBJECT_ASSERT_WLOCKED(m->object); - if ((m->oflags & VPO_BUSY) == 0 && - (m->aflags & PGA_WRITEABLE) == 0) + if (!vm_page_xbusied(m) && (m->aflags & PGA_WRITEABLE) == 0) return (FALSE); rw_wlock(&pvh_global_lock); rv = pmap_is_modified_pvh(&m->md) || @@ -4689,13 +4695,12 @@ pmap_remove_write(vm_page_t m) ("pmap_remove_write: page %p is not managed", m)); /* - * If the page is not VPO_BUSY, then PGA_WRITEABLE cannot be set by - * another thread while the object is locked. Thus, if PGA_WRITEABLE - * is clear, no page table entries need updating. + * If the page is not exclusive busied, then PGA_WRITEABLE cannot be + * set by another thread while the object is locked. Thus, + * if PGA_WRITEABLE is clear, no page table entries need updating. */ VM_OBJECT_ASSERT_WLOCKED(m->object); - if ((m->oflags & VPO_BUSY) == 0 && - (m->aflags & PGA_WRITEABLE) == 0) + if (!vm_page_xbusied(m) && (m->aflags & PGA_WRITEABLE) == 0) return; rw_wlock(&pvh_global_lock); sched_pin(); @@ -4741,6 +4746,8 @@ retry: rw_wunlock(&pvh_global_lock); } +#define PMAP_TS_REFERENCED_MAX 5 + /* * pmap_ts_referenced: * @@ -4757,79 +4764,200 @@ int pmap_ts_referenced(vm_page_t m) { struct md_page *pvh; - pv_entry_t pv, pvf, pvn; + pv_entry_t pv, pvf; pmap_t pmap; - pd_entry_t oldpde, *pde; + pd_entry_t *pde; pt_entry_t *pte; - vm_offset_t va; + vm_paddr_t pa; int rtval = 0; KASSERT((m->oflags & VPO_UNMANAGED) == 0, ("pmap_ts_referenced: page %p is not managed", m)); - pvh = pa_to_pvh(VM_PAGE_TO_PHYS(m)); + pa = VM_PAGE_TO_PHYS(m); + pvh = pa_to_pvh(pa); rw_wlock(&pvh_global_lock); sched_pin(); - if ((m->flags & PG_FICTITIOUS) != 0) + if ((m->flags & PG_FICTITIOUS) != 0 || + (pvf = TAILQ_FIRST(&pvh->pv_list)) == NULL) goto small_mappings; - TAILQ_FOREACH_SAFE(pv, &pvh->pv_list, pv_next, pvn) { - va = pv->pv_va; + pv = pvf; + do { pmap = PV_PMAP(pv); PMAP_LOCK(pmap); - pde = pmap_pde(pmap, va); - oldpde = *pde; - if ((oldpde & PG_A) != 0) { - if (pmap_demote_pde(pmap, pde, va)) { - if ((oldpde & PG_W) == 0) { - /* - * Remove the mapping to a single page - * so that a subsequent access may - * repromote. Since the underlying - * page table page is fully populated, - * this removal never frees a page - * table page. - */ - va += VM_PAGE_TO_PHYS(m) - (oldpde & - PG_PS_FRAME); - pmap_remove_page(pmap, va, NULL); - rtval++; - if (rtval > 4) { - PMAP_UNLOCK(pmap); - goto out; - } - } + pde = pmap_pde(pmap, pv->pv_va); + if ((*pde & PG_A) != 0) { + /* + * Since this reference bit is shared by either 1024 + * or 512 4KB pages, it should not be cleared every + * time it is tested. Apply a simple "hash" function + * on the physical page number, the virtual superpage + * number, and the pmap address to select one 4KB page + * out of the 1024 or 512 on which testing the + * reference bit will result in clearing that bit. + * This function is designed to avoid the selection of + * the same 4KB page for every 2- or 4MB page mapping. + * + * On demotion, a mapping that hasn't been referenced + * is simply destroyed. To avoid the possibility of a + * subsequent page fault on a demoted wired mapping, + * always leave its reference bit set. Moreover, + * since the superpage is wired, the current state of + * its reference bit won't affect page replacement. + */ + if ((((pa >> PAGE_SHIFT) ^ (pv->pv_va >> PDRSHIFT) ^ + (uintptr_t)pmap) & (NPTEPG - 1)) == 0 && + (*pde & PG_W) == 0) { + atomic_clear_int((u_int *)pde, PG_A); + pmap_invalidate_page(pmap, pv->pv_va); } + rtval++; } PMAP_UNLOCK(pmap); - } + /* Rotate the PV list if it has more than one entry. */ + if (TAILQ_NEXT(pv, pv_next) != NULL) { + TAILQ_REMOVE(&pvh->pv_list, pv, pv_next); + TAILQ_INSERT_TAIL(&pvh->pv_list, pv, pv_next); + } + if (rtval >= PMAP_TS_REFERENCED_MAX) + goto out; + } while ((pv = TAILQ_FIRST(&pvh->pv_list)) != pvf); small_mappings: - if ((pv = TAILQ_FIRST(&m->md.pv_list)) != NULL) { - pvf = pv; - do { - pvn = TAILQ_NEXT(pv, pv_next); + if ((pvf = TAILQ_FIRST(&m->md.pv_list)) == NULL) + goto out; + pv = pvf; + do { + pmap = PV_PMAP(pv); + PMAP_LOCK(pmap); + pde = pmap_pde(pmap, pv->pv_va); + KASSERT((*pde & PG_PS) == 0, + ("pmap_ts_referenced: found a 4mpage in page %p's pv list", + m)); + pte = pmap_pte_quick(pmap, pv->pv_va); + if ((*pte & PG_A) != 0) { + atomic_clear_int((u_int *)pte, PG_A); + pmap_invalidate_page(pmap, pv->pv_va); + rtval++; + } + PMAP_UNLOCK(pmap); + /* Rotate the PV list if it has more than one entry. */ + if (TAILQ_NEXT(pv, pv_next) != NULL) { TAILQ_REMOVE(&m->md.pv_list, pv, pv_next); TAILQ_INSERT_TAIL(&m->md.pv_list, pv, pv_next); - pmap = PV_PMAP(pv); - PMAP_LOCK(pmap); - pde = pmap_pde(pmap, pv->pv_va); - KASSERT((*pde & PG_PS) == 0, ("pmap_ts_referenced:" - " found a 4mpage in page %p's pv list", m)); - pte = pmap_pte_quick(pmap, pv->pv_va); - if ((*pte & PG_A) != 0) { - atomic_clear_int((u_int *)pte, PG_A); - pmap_invalidate_page(pmap, pv->pv_va); - rtval++; - if (rtval > 4) - pvn = NULL; - } - PMAP_UNLOCK(pmap); - } while ((pv = pvn) != NULL && pv != pvf); - } + } + } while ((pv = TAILQ_FIRST(&m->md.pv_list)) != pvf && rtval < + PMAP_TS_REFERENCED_MAX); out: sched_unpin(); rw_wunlock(&pvh_global_lock); return (rtval); } +/* + * Apply the given advice to the specified range of addresses within the + * given pmap. Depending on the advice, clear the referenced and/or + * modified flags in each mapping and set the mapped page's dirty field. + */ +void +pmap_advise(pmap_t pmap, vm_offset_t sva, vm_offset_t eva, int advice) +{ + pd_entry_t oldpde, *pde; + pt_entry_t *pte; + vm_offset_t pdnxt; + vm_page_t m; + boolean_t anychanged, pv_lists_locked; + + if (advice != MADV_DONTNEED && advice != MADV_FREE) + return; + if (pmap_is_current(pmap)) + pv_lists_locked = FALSE; + else { + pv_lists_locked = TRUE; +resume: + rw_wlock(&pvh_global_lock); + sched_pin(); + } + anychanged = FALSE; + PMAP_LOCK(pmap); + for (; sva < eva; sva = pdnxt) { + pdnxt = (sva + NBPDR) & ~PDRMASK; + if (pdnxt < sva) + pdnxt = eva; + pde = pmap_pde(pmap, sva); + oldpde = *pde; + if ((oldpde & PG_V) == 0) + continue; + else if ((oldpde & PG_PS) != 0) { + if ((oldpde & PG_MANAGED) == 0) + continue; + if (!pv_lists_locked) { + pv_lists_locked = TRUE; + if (!rw_try_wlock(&pvh_global_lock)) { + if (anychanged) + pmap_invalidate_all(pmap); + PMAP_UNLOCK(pmap); + goto resume; + } + sched_pin(); + } + if (!pmap_demote_pde(pmap, pde, sva)) { + /* + * The large page mapping was destroyed. + */ + continue; + } + + /* + * Unless the page mappings are wired, remove the + * mapping to a single page so that a subsequent + * access may repromote. Since the underlying page + * table page is fully populated, this removal never + * frees a page table page. + */ + if ((oldpde & PG_W) == 0) { + pte = pmap_pte_quick(pmap, sva); + KASSERT((*pte & PG_V) != 0, + ("pmap_advise: invalid PTE")); + pmap_remove_pte(pmap, pte, sva, NULL); + anychanged = TRUE; + } + } + if (pdnxt > eva) + pdnxt = eva; + for (pte = pmap_pte_quick(pmap, sva); sva != pdnxt; pte++, + sva += PAGE_SIZE) { + if ((*pte & (PG_MANAGED | PG_V)) != (PG_MANAGED | + PG_V)) + continue; + else if ((*pte & (PG_M | PG_RW)) == (PG_M | PG_RW)) { + if (advice == MADV_DONTNEED) { + /* + * Future calls to pmap_is_modified() + * can be avoided by making the page + * dirty now. + */ + m = PHYS_TO_VM_PAGE(*pte & PG_FRAME); + vm_page_dirty(m); + } + atomic_clear_int((u_int *)pte, PG_M | PG_A); + } else if ((*pte & PG_A) != 0) + atomic_clear_int((u_int *)pte, PG_A); + else + continue; + if ((*pte & PG_G) != 0) + pmap_invalidate_page(pmap, sva); + else + anychanged = TRUE; + } + } + if (anychanged) + pmap_invalidate_all(pmap); + if (pv_lists_locked) { + sched_unpin(); + rw_wunlock(&pvh_global_lock); + } + PMAP_UNLOCK(pmap); +} + /* * Clear the modify bits on the specified physical page. */ @@ -4846,13 +4974,13 @@ pmap_clear_modify(vm_page_t m) KASSERT((m->oflags & VPO_UNMANAGED) == 0, ("pmap_clear_modify: page %p is not managed", m)); VM_OBJECT_ASSERT_WLOCKED(m->object); - KASSERT((m->oflags & VPO_BUSY) == 0, - ("pmap_clear_modify: page %p is busy", m)); + KASSERT(!vm_page_xbusied(m), + ("pmap_clear_modify: page %p is exclusive busied", m)); /* * If the page is not PGA_WRITEABLE, then no PTEs can have PG_M set. * If the object containing the page is locked and the page is not - * VPO_BUSY, then PGA_WRITEABLE cannot be concurrently set. + * exclusive busied, then PGA_WRITEABLE cannot be concurrently set. */ if ((m->aflags & PGA_WRITEABLE) == 0) return; @@ -5044,7 +5172,7 @@ pmap_mapdev_attr(vm_paddr_t pa, vm_size_t size, int mode) if (pa < KERNLOAD && pa + size <= KERNLOAD) va = KERNBASE + pa; else - va = kmem_alloc_nofault(kernel_map, size); + va = kva_alloc(size); if (!va) panic("pmap_mapdev: Couldn't alloc kernel virtual memory"); @@ -5079,7 +5207,7 @@ pmap_unmapdev(vm_offset_t va, vm_size_t size) base = trunc_page(va); offset = va & PAGE_MASK; size = round_page(offset + size); - kmem_free(kernel_map, base, size); + kva_free(base, size); } /* diff --git a/sys/i386/i386/sys_machdep.c b/sys/i386/i386/sys_machdep.c index 00d74d33069..746f8463eb3 100644 --- a/sys/i386/i386/sys_machdep.c +++ b/sys/i386/i386/sys_machdep.c @@ -132,7 +132,7 @@ sysarch(td, uap) default: #ifdef KTRACE if (KTRPOINT(td, KTR_CAPFAIL)) - ktrcapfail(CAPFAIL_SYSCALL, 0, 0); + ktrcapfail(CAPFAIL_SYSCALL, NULL, NULL); #endif return (ECAPMODE); } @@ -164,7 +164,7 @@ sysarch(td, uap) break; case I386_SET_LDT: if (kargs.largs.descs != NULL) { - lp = (union descriptor *)kmem_malloc(kernel_map, + lp = (union descriptor *)kmem_malloc(kernel_arena, kargs.largs.num * sizeof(union descriptor), M_WAITOK); if (lp == NULL) { @@ -175,7 +175,7 @@ sysarch(td, uap) kargs.largs.num * sizeof(union descriptor)); if (error == 0) error = i386_set_ldt(td, &kargs.largs, lp); - kmem_free(kernel_map, (vm_offset_t)lp, + kmem_free(kernel_arena, (vm_offset_t)lp, kargs.largs.num * sizeof(union descriptor)); } else { error = i386_set_ldt(td, &kargs.largs, NULL); @@ -299,7 +299,7 @@ i386_extend_pcb(struct thread *td) 0 /* granularity */ }; - ext = (struct pcb_ext *)kmem_malloc(kernel_map, ctob(IOPAGES+1), + ext = (struct pcb_ext *)kmem_malloc(kernel_arena, ctob(IOPAGES+1), M_WAITOK); if (ext == 0) return (ENOMEM); @@ -473,7 +473,7 @@ user_ldt_alloc(struct mdproc *mdp, int len) M_SUBPROC, M_WAITOK); new_ldt->ldt_len = len = NEW_MAX_LD(len); - new_ldt->ldt_base = (caddr_t)kmem_malloc(kernel_map, + new_ldt->ldt_base = (caddr_t)kmem_malloc(kernel_arena, round_page(len * sizeof(union descriptor)), M_WAITOK); if (new_ldt->ldt_base == NULL) { free(new_ldt, M_SUBPROC); @@ -513,7 +513,7 @@ user_ldt_alloc(struct mdproc *mdp, int len) M_SUBPROC, M_WAITOK); new_ldt->ldt_len = len = NEW_MAX_LD(len); - new_ldt->ldt_base = (caddr_t)kmem_malloc(kernel_map, + new_ldt->ldt_base = (caddr_t)kmem_malloc(kernel_arena, len * sizeof(union descriptor), M_WAITOK); if (new_ldt->ldt_base == NULL) { free(new_ldt, M_SUBPROC); @@ -576,7 +576,7 @@ user_ldt_deref(struct proc_ldt *pldt) mtx_assert(&dt_lock, MA_OWNED); if (--pldt->ldt_refcnt == 0) { mtx_unlock_spin(&dt_lock); - kmem_free(kernel_map, (vm_offset_t)pldt->ldt_base, + kmem_free(kernel_arena, (vm_offset_t)pldt->ldt_base, pldt->ldt_len * sizeof(union descriptor)); free(pldt, M_SUBPROC); } else @@ -855,7 +855,7 @@ i386_ldt_grow(struct thread *td, int len) * free the new object and return. */ mtx_unlock_spin(&dt_lock); - kmem_free(kernel_map, + kmem_free(kernel_arena, (vm_offset_t)new_ldt->ldt_base, new_ldt->ldt_len * sizeof(union descriptor)); free(new_ldt, M_SUBPROC); @@ -889,7 +889,7 @@ i386_ldt_grow(struct thread *td, int len) mtx_unlock_spin(&dt_lock); #endif if (old_ldt_base != NULL_LDT_BASE) { - kmem_free(kernel_map, (vm_offset_t)old_ldt_base, + kmem_free(kernel_arena, (vm_offset_t)old_ldt_base, old_ldt_len * sizeof(union descriptor)); free(new_ldt, M_SUBPROC); } diff --git a/sys/i386/i386/vm_machdep.c b/sys/i386/i386/vm_machdep.c index 92e0f527c14..5d62b1c6fb6 100644 --- a/sys/i386/i386/vm_machdep.c +++ b/sys/i386/i386/vm_machdep.c @@ -355,7 +355,7 @@ cpu_thread_clean(struct thread *td) * XXX do we need to move the TSS off the allocated pages * before freeing them? (not done here) */ - kmem_free(kernel_map, (vm_offset_t)pcb->pcb_ext, + kva_free((vm_offset_t)pcb->pcb_ext, ctob(IOPAGES + 1)); pcb->pcb_ext = NULL; } @@ -751,7 +751,7 @@ sf_buf_init(void *arg) sf_buf_active = hashinit(nsfbufs, M_TEMP, &sf_buf_hashmask); TAILQ_INIT(&sf_buf_freelist); - sf_base = kmem_alloc_nofault(kernel_map, nsfbufs * PAGE_SIZE); + sf_base = kva_alloc(nsfbufs * PAGE_SIZE); sf_bufs = malloc(nsfbufs * sizeof(struct sf_buf), M_TEMP, M_NOWAIT | M_ZERO); for (i = 0; i < nsfbufs; i++) { diff --git a/sys/i386/ibcs2/ibcs2_fcntl.c b/sys/i386/ibcs2/ibcs2_fcntl.c index 2902da74bcd..d061b796874 100644 --- a/sys/i386/ibcs2/ibcs2_fcntl.c +++ b/sys/i386/ibcs2/ibcs2_fcntl.c @@ -201,10 +201,12 @@ ibcs2_open(td, uap) free(path, M_TEMP); PROC_LOCK(p); if (!ret && !noctty && SESS_LEADER(p) && !(p->p_flag & P_CONTROLT)) { + cap_rights_t rights; struct file *fp; int error; - error = fget(td, td->td_retval[0], CAP_IOCTL, &fp); + error = fget(td, td->td_retval[0], + cap_rights_init(&rights, CAP_IOCTL), &fp); PROC_UNLOCK(p); if (error) return (EBADF); diff --git a/sys/i386/ibcs2/ibcs2_ioctl.c b/sys/i386/ibcs2/ibcs2_ioctl.c index 83e68cd4519..03fa5f6218b 100644 --- a/sys/i386/ibcs2/ibcs2_ioctl.c +++ b/sys/i386/ibcs2/ibcs2_ioctl.c @@ -331,10 +331,12 @@ ibcs2_ioctl(td, uap) struct ibcs2_ioctl_args *uap; { struct proc *p = td->td_proc; + cap_rights_t rights; struct file *fp; int error; - if ((error = fget(td, uap->fd, CAP_IOCTL, &fp)) != 0) { + error = fget(td, uap->fd, cap_rights_init(&rights, CAP_IOCTL), &fp); + if (error != 0) { DPRINTF(("ibcs2_ioctl(%d): bad fd %d ", p->p_pid, uap->fd)); return EBADF; diff --git a/sys/i386/ibcs2/ibcs2_misc.c b/sys/i386/ibcs2/ibcs2_misc.c index 9f382aaf707..28cd83cbd5a 100644 --- a/sys/i386/ibcs2/ibcs2_misc.c +++ b/sys/i386/ibcs2/ibcs2_misc.c @@ -326,6 +326,7 @@ ibcs2_getdents(td, uap) register int len, reclen; /* BSD-format */ register caddr_t outp; /* iBCS2-format */ register int resid; /* iBCS2-format */ + cap_rights_t rights; struct file *fp; struct uio auio; struct iovec aiov; @@ -337,7 +338,9 @@ ibcs2_getdents(td, uap) #define BSD_DIRENT(cp) ((struct dirent *)(cp)) #define IBCS2_RECLEN(reclen) (reclen + sizeof(u_short)) - if ((error = getvnode(td->td_proc->p_fd, uap->fd, CAP_READ, &fp)) != 0) + error = getvnode(td->td_proc->p_fd, uap->fd, + cap_rights_init(&rights, CAP_READ), &fp); + if (error != 0) return (error); if ((fp->f_flag & FREAD) == 0) { fdrop(fp, td); @@ -478,6 +481,7 @@ ibcs2_read(td, uap) register int len, reclen; /* BSD-format */ register caddr_t outp; /* iBCS2-format */ register int resid; /* iBCS2-format */ + cap_rights_t rights; struct file *fp; struct uio auio; struct iovec aiov; @@ -490,8 +494,9 @@ ibcs2_read(td, uap) u_long *cookies = NULL, *cookiep; int ncookies; - if ((error = getvnode(td->td_proc->p_fd, uap->fd, CAP_READ, - &fp)) != 0) { + error = getvnode(td->td_proc->p_fd, uap->fd, + cap_rights_init(&rights, CAP_READ), &fp); + if (error != 0) { if (error == EINVAL) return sys_read(td, (struct read_args *)uap); else diff --git a/sys/i386/ibcs2/imgact_coff.c b/sys/i386/ibcs2/imgact_coff.c index b155ef99d8a..a7543d734de 100644 --- a/sys/i386/ibcs2/imgact_coff.c +++ b/sys/i386/ibcs2/imgact_coff.c @@ -128,7 +128,7 @@ load_coff_section(struct vmspace *vmspace, struct vnode *vp, vm_offset_t offset, if (map_len != 0) { error = vm_map_find(&vmspace->vm_map, NULL, 0, &map_addr, - map_len, VMFS_NO_SPACE, VM_PROT_ALL, VM_PROT_ALL, 0); + map_len, 0, VMFS_NO_SPACE, VM_PROT_ALL, VM_PROT_ALL, 0); if (error) return (vm_mmap_to_errno(error)); } @@ -146,7 +146,7 @@ load_coff_section(struct vmspace *vmspace, struct vnode *vp, vm_offset_t offset, error = copyout(data_buf, (caddr_t) map_addr, copy_len); - kmem_free_wakeup(exec_map, (vm_offset_t)data_buf, PAGE_SIZE); + kmap_free_wakeup(exec_map, (vm_offset_t)data_buf, PAGE_SIZE); return error; } @@ -280,7 +280,7 @@ coff_load_file(struct thread *td, char *name) error = 0; dealloc_and_fail: - kmem_free_wakeup(exec_map, (vm_offset_t)ptr, PAGE_SIZE); + kmap_free_wakeup(exec_map, (vm_offset_t)ptr, PAGE_SIZE); fail: VOP_UNLOCK(vp, 0); unlocked_fail: @@ -417,7 +417,7 @@ exec_coff_imgact(imgp) } free(libbuf, M_TEMP); } - kmem_free_wakeup(exec_map, (vm_offset_t)buf, len); + kmap_free_wakeup(exec_map, (vm_offset_t)buf, len); if (error) goto fail; } @@ -473,7 +473,7 @@ exec_coff_imgact(imgp) DPRINTF(("imgact: error = %d\n", error)); vm_map_find(&vmspace->vm_map, NULL, 0, - (vm_offset_t *)&hole, PAGE_SIZE, VMFS_NO_SPACE, + (vm_offset_t *)&hole, PAGE_SIZE, 0, VMFS_NO_SPACE, VM_PROT_ALL, VM_PROT_ALL, 0); DPRINTF(("IBCS2: start vm_dsize = 0x%x, vm_daddr = 0x%p end = 0x%p\n", ctob(vmspace->vm_dsize), vmspace->vm_daddr, diff --git a/sys/i386/include/acpica_machdep.h b/sys/i386/include/acpica_machdep.h index c4419f26f68..1ae3d5865db 100644 --- a/sys/i386/include/acpica_machdep.h +++ b/sys/i386/include/acpica_machdep.h @@ -1,100 +1,6 @@ /*- - * Copyright (c) 2002 Mitsuru IWASAKI - * 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. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. - * - * $FreeBSD$ + * This file is in the public domain. */ +/* $FreeBSD$ */ -/****************************************************************************** - * - * Name: acpica_machdep.h - arch-specific defines, etc. - * $Revision$ - * - *****************************************************************************/ - -#ifndef __ACPICA_MACHDEP_H__ -#define __ACPICA_MACHDEP_H__ - -#ifdef _KERNEL -/* - * Calling conventions: - * - * ACPI_SYSTEM_XFACE - Interfaces to host OS (handlers, threads) - * ACPI_EXTERNAL_XFACE - External ACPI interfaces - * ACPI_INTERNAL_XFACE - Internal ACPI interfaces - * ACPI_INTERNAL_VAR_XFACE - Internal variable-parameter list interfaces - */ -#define ACPI_SYSTEM_XFACE -#define ACPI_EXTERNAL_XFACE -#define ACPI_INTERNAL_XFACE -#define ACPI_INTERNAL_VAR_XFACE - -/* Asm macros */ - -#define ACPI_ASM_MACROS -#define BREAKPOINT3 -#define ACPI_DISABLE_IRQS() disable_intr() -#define ACPI_ENABLE_IRQS() enable_intr() - -#define ACPI_FLUSH_CPU_CACHE() wbinvd() - -/* Section 5.2.9.1: global lock acquire/release functions */ -extern int acpi_acquire_global_lock(uint32_t *lock); -extern int acpi_release_global_lock(uint32_t *lock); -#define ACPI_ACQUIRE_GLOBAL_LOCK(GLptr, Acq) do { \ - (Acq) = acpi_acquire_global_lock(&((GLptr)->GlobalLock)); \ -} while (0) -#define ACPI_RELEASE_GLOBAL_LOCK(GLptr, Acq) do { \ - (Acq) = acpi_release_global_lock(&((GLptr)->GlobalLock)); \ -} while (0) - -/*! [Begin] no source code translation - * - * Math helper asm macros - */ -#define asm __asm -#define ACPI_DIV_64_BY_32(n_hi, n_lo, d32, q32, r32) \ - asm("divl %2;" \ - :"=a"(q32), "=d"(r32) \ - :"r"(d32), \ - "0"(n_lo), "1"(n_hi)) - - -#define ACPI_SHIFT_RIGHT_64(n_hi, n_lo) \ - asm("shrl $1,%2;" \ - "rcrl $1,%3;" \ - :"=r"(n_hi), "=r"(n_lo) \ - :"0"(n_hi), "1"(n_lo)) - -/*! [End] no source code translation !*/ - -void acpi_SetDefaultIntrModel(int model); -void acpi_cpu_c1(void); -void *acpi_map_table(vm_paddr_t pa, const char *sig); -void acpi_unmap_table(void *table); -vm_paddr_t acpi_find_table(const char *sig); - -#endif /* _KERNEL */ - -#endif /* __ACPICA_MACHDEP_H__ */ +#include diff --git a/sys/i386/include/apicvar.h b/sys/i386/include/apicvar.h index a0e622e8785..5d1f52249de 100644 --- a/sys/i386/include/apicvar.h +++ b/sys/i386/include/apicvar.h @@ -226,6 +226,7 @@ int lapic_set_lvt_triggermode(u_int apic_id, u_int lvt, enum intr_trigger trigger); void lapic_set_tpr(u_int vector); void lapic_setup(int boot); +void xen_intr_handle_upcall(struct trapframe *frame); #endif /* !LOCORE */ #endif /* _MACHINE_APICVAR_H_ */ diff --git a/sys/i386/include/asm.h b/sys/i386/include/asm.h index 7ce3d57d42e..cc8bfe01219 100644 --- a/sys/i386/include/asm.h +++ b/sys/i386/include/asm.h @@ -49,11 +49,12 @@ popl %ebx #define PIC_PLT(x) x@PLT #define PIC_GOT(x) x@GOT(%ebx) +#define PIC_GOTOFF(x) x@GOTOFF(%ebx) #else #define PIC_PROLOGUE #define PIC_EPILOGUE #define PIC_PLT(x) x -#define PIC_GOT(x) x +#define PIC_GOTOFF(x) x #endif /* diff --git a/sys/i386/include/atomic.h b/sys/i386/include/atomic.h index 3b9d0019442..0156b5ba411 100644 --- a/sys/i386/include/atomic.h +++ b/sys/i386/include/atomic.h @@ -32,6 +32,11 @@ #error this file needs sys/cdefs.h as a prerequisite #endif +#ifdef _KERNEL +#include +#include +#endif + #define mb() __asm __volatile("lock; addl $0,(%%esp)" : : : "memory", "cc") #define wmb() __asm __volatile("lock; addl $0,(%%esp)" : : : "memory", "cc") #define rmb() __asm __volatile("lock; addl $0,(%%esp)" : : : "memory", "cc") @@ -54,12 +59,14 @@ * atomic_clear_int(P, V) (*(u_int *)(P) &= ~(V)) * atomic_add_int(P, V) (*(u_int *)(P) += (V)) * atomic_subtract_int(P, V) (*(u_int *)(P) -= (V)) + * atomic_swap_int(P, V) (return (*(u_int *)(P)); *(u_int *)(P) = (V);) * atomic_readandclear_int(P) (return (*(u_int *)(P)); *(u_int *)(P) = 0;) * * atomic_set_long(P, V) (*(u_long *)(P) |= (V)) * atomic_clear_long(P, V) (*(u_long *)(P) &= ~(V)) * atomic_add_long(P, V) (*(u_long *)(P) += (V)) * atomic_subtract_long(P, V) (*(u_long *)(P) -= (V)) + * atomic_swap_long(P, V) (return (*(u_long *)(P)); *(u_long *)(P) = (V);) * atomic_readandclear_long(P) (return (*(u_long *)(P)); *(u_long *)(P) = 0;) */ @@ -78,12 +85,18 @@ void atomic_##NAME##_barr_##TYPE(volatile u_##TYPE *p, u_##TYPE v) int atomic_cmpset_int(volatile u_int *dst, u_int expect, u_int src); u_int atomic_fetchadd_int(volatile u_int *p, u_int v); +int atomic_testandset_int(volatile u_int *p, u_int v); #define ATOMIC_LOAD(TYPE, LOP) \ u_##TYPE atomic_load_acq_##TYPE(volatile u_##TYPE *p) #define ATOMIC_STORE(TYPE) \ void atomic_store_rel_##TYPE(volatile u_##TYPE *p, u_##TYPE v) +int atomic_cmpset_64(volatile uint64_t *, uint64_t, uint64_t); +uint64_t atomic_load_acq_64(volatile uint64_t *); +void atomic_store_rel_64(volatile uint64_t *, uint64_t); +uint64_t atomic_swap_64(volatile uint64_t *, uint64_t); + #else /* !KLD_MODULE && __GNUCLIKE_ASM */ /* @@ -106,8 +119,8 @@ static __inline void \ atomic_##NAME##_##TYPE(volatile u_##TYPE *p, u_##TYPE v)\ { \ __asm __volatile(MPLOCKED OP \ - : "=m" (*p) \ - : CONS (V), "m" (*p) \ + : "+m" (*p) \ + : CONS (V) \ : "cc"); \ } \ \ @@ -115,93 +128,12 @@ static __inline void \ atomic_##NAME##_barr_##TYPE(volatile u_##TYPE *p, u_##TYPE v)\ { \ __asm __volatile(MPLOCKED OP \ - : "=m" (*p) \ - : CONS (V), "m" (*p) \ + : "+m" (*p) \ + : CONS (V) \ : "memory", "cc"); \ } \ struct __hack -#if defined(_KERNEL) && !defined(WANT_FUNCTIONS) - -/* I486 does not support SMP or CMPXCHG8B. */ -static __inline uint64_t -atomic_load_acq_64_i386(volatile uint64_t *p) -{ - volatile uint32_t *high, *low; - uint64_t res; - - low = (volatile uint32_t *)p; - high = (volatile uint32_t *)p + 1; - __asm __volatile( - " pushfl ; " - " cli ; " - " movl %1,%%eax ; " - " movl %2,%%edx ; " - " popfl" - : "=&A" (res) /* 0 */ - : "m" (*low), /* 1 */ - "m" (*high) /* 2 */ - : "memory"); - - return (res); -} - -static __inline void -atomic_store_rel_64_i386(volatile uint64_t *p, uint64_t v) -{ - volatile uint32_t *high, *low; - - low = (volatile uint32_t *)p; - high = (volatile uint32_t *)p + 1; - __asm __volatile( - " pushfl ; " - " cli ; " - " movl %%eax,%0 ; " - " movl %%edx,%1 ; " - " popfl" - : "=m" (*low), /* 0 */ - "=m" (*high) /* 1 */ - : "A" (v) /* 2 */ - : "memory"); -} - -static __inline uint64_t -atomic_load_acq_64_i586(volatile uint64_t *p) -{ - uint64_t res; - - __asm __volatile( - " movl %%ebx,%%eax ; " - " movl %%ecx,%%edx ; " - " " MPLOCKED " " - " cmpxchg8b %2" - : "=&A" (res), /* 0 */ - "=m" (*p) /* 1 */ - : "m" (*p) /* 2 */ - : "memory", "cc"); - - return (res); -} - -static __inline void -atomic_store_rel_64_i586(volatile uint64_t *p, uint64_t v) -{ - - __asm __volatile( - " movl %%eax,%%ebx ; " - " movl %%edx,%%ecx ; " - "1: " - " " MPLOCKED " " - " cmpxchg8b %2 ; " - " jne 1b" - : "=m" (*p), /* 0 */ - "+A" (v) /* 1 */ - : "m" (*p) /* 2 */ - : "ebx", "ecx", "memory", "cc"); -} - -#endif /* _KERNEL && !WANT_FUNCTIONS */ - /* * Atomic compare and set, used by the mutex functions * @@ -220,7 +152,7 @@ atomic_cmpset_int(volatile u_int *dst, u_int expect, u_int src) __asm __volatile( " pushfl ; " " cli ; " - " cmpl %3,%4 ; " + " cmpl %3,%1 ; " " jne 1f ; " " movl %2,%1 ; " "1: " @@ -228,12 +160,10 @@ atomic_cmpset_int(volatile u_int *dst, u_int expect, u_int src) " popfl ; " "# atomic_cmpset_int" : "=q" (res), /* 0 */ - "=m" (*dst) /* 1 */ + "+m" (*dst) /* 1 */ : "r" (src), /* 2 */ - "r" (expect), /* 3 */ - "m" (*dst) /* 4 */ + "r" (expect) /* 3 */ : "memory"); - return (res); } @@ -246,17 +176,14 @@ atomic_cmpset_int(volatile u_int *dst, u_int expect, u_int src) __asm __volatile( " " MPLOCKED " " - " cmpxchgl %2,%1 ; " + " cmpxchgl %3,%1 ; " " sete %0 ; " - "1: " "# atomic_cmpset_int" - : "=a" (res), /* 0 */ - "=m" (*dst) /* 1 */ - : "r" (src), /* 2 */ - "a" (expect), /* 3 */ - "m" (*dst) /* 4 */ + : "=q" (res), /* 0 */ + "+m" (*dst), /* 1 */ + "+a" (expect) /* 2 */ + : "r" (src) /* 3 */ : "memory", "cc"); - return (res); } @@ -272,15 +199,31 @@ atomic_fetchadd_int(volatile u_int *p, u_int v) __asm __volatile( " " MPLOCKED " " - " xaddl %0, %1 ; " + " xaddl %0,%1 ; " "# atomic_fetchadd_int" - : "+r" (v), /* 0 (result) */ - "=m" (*p) /* 1 */ - : "m" (*p) /* 2 */ - : "cc"); + : "+r" (v), /* 0 */ + "+m" (*p) /* 1 */ + : : "cc"); return (v); } +static __inline int +atomic_testandset_int(volatile u_int *p, u_int v) +{ + u_char res; + + __asm __volatile( + " " MPLOCKED " " + " btsl %2,%1 ; " + " setc %0 ; " + "# atomic_testandset_int" + : "=q" (res), /* 0 */ + "+m" (*p) /* 1 */ + : "Ir" (v & 0x1f) /* 2 */ + : "cc"); + return (res); +} + /* * We assume that a = b will do atomic loads and stores. Due to the * IA32 memory model, a simple store guarantees release semantics. @@ -325,16 +268,227 @@ atomic_load_acq_##TYPE(volatile u_##TYPE *p) \ \ __asm __volatile(MPLOCKED LOP \ : "=a" (res), /* 0 */ \ - "=m" (*p) /* 1 */ \ - : "m" (*p) /* 2 */ \ - : "memory", "cc"); \ - \ + "+m" (*p) /* 1 */ \ + : : "memory", "cc"); \ return (res); \ } \ struct __hack #endif /* _KERNEL && !SMP */ +#ifdef _KERNEL + +#ifdef WANT_FUNCTIONS +int atomic_cmpset_64_i386(volatile uint64_t *, uint64_t, uint64_t); +int atomic_cmpset_64_i586(volatile uint64_t *, uint64_t, uint64_t); +uint64_t atomic_load_acq_64_i386(volatile uint64_t *); +uint64_t atomic_load_acq_64_i586(volatile uint64_t *); +void atomic_store_rel_64_i386(volatile uint64_t *, uint64_t); +void atomic_store_rel_64_i586(volatile uint64_t *, uint64_t); +uint64_t atomic_swap_64_i386(volatile uint64_t *, uint64_t); +uint64_t atomic_swap_64_i586(volatile uint64_t *, uint64_t); +#endif + +/* I486 does not support SMP or CMPXCHG8B. */ +static __inline int +atomic_cmpset_64_i386(volatile uint64_t *dst, uint64_t expect, uint64_t src) +{ + volatile uint32_t *p; + u_char res; + + p = (volatile uint32_t *)dst; + __asm __volatile( + " pushfl ; " + " cli ; " + " xorl %1,%%eax ; " + " xorl %2,%%edx ; " + " orl %%edx,%%eax ; " + " jne 1f ; " + " movl %4,%1 ; " + " movl %5,%2 ; " + "1: " + " sete %3 ; " + " popfl" + : "+A" (expect), /* 0 */ + "+m" (*p), /* 1 */ + "+m" (*(p + 1)), /* 2 */ + "=q" (res) /* 3 */ + : "r" ((uint32_t)src), /* 4 */ + "r" ((uint32_t)(src >> 32)) /* 5 */ + : "memory", "cc"); + return (res); +} + +static __inline uint64_t +atomic_load_acq_64_i386(volatile uint64_t *p) +{ + volatile uint32_t *q; + uint64_t res; + + q = (volatile uint32_t *)p; + __asm __volatile( + " pushfl ; " + " cli ; " + " movl %1,%%eax ; " + " movl %2,%%edx ; " + " popfl" + : "=&A" (res) /* 0 */ + : "m" (*q), /* 1 */ + "m" (*(q + 1)) /* 2 */ + : "memory"); + return (res); +} + +static __inline void +atomic_store_rel_64_i386(volatile uint64_t *p, uint64_t v) +{ + volatile uint32_t *q; + + q = (volatile uint32_t *)p; + __asm __volatile( + " pushfl ; " + " cli ; " + " movl %%eax,%0 ; " + " movl %%edx,%1 ; " + " popfl" + : "=m" (*q), /* 0 */ + "=m" (*(q + 1)) /* 1 */ + : "A" (v) /* 2 */ + : "memory"); +} + +static __inline uint64_t +atomic_swap_64_i386(volatile uint64_t *p, uint64_t v) +{ + volatile uint32_t *q; + uint64_t res; + + q = (volatile uint32_t *)p; + __asm __volatile( + " pushfl ; " + " cli ; " + " movl %1,%%eax ; " + " movl %2,%%edx ; " + " movl %4,%2 ; " + " movl %3,%1 ; " + " popfl" + : "=&A" (res), /* 0 */ + "+m" (*q), /* 1 */ + "+m" (*(q + 1)) /* 2 */ + : "r" ((uint32_t)v), /* 3 */ + "r" ((uint32_t)(v >> 32))); /* 4 */ + return (res); +} + +static __inline int +atomic_cmpset_64_i586(volatile uint64_t *dst, uint64_t expect, uint64_t src) +{ + u_char res; + + __asm __volatile( + " " MPLOCKED " " + " cmpxchg8b %1 ; " + " sete %0" + : "=q" (res), /* 0 */ + "+m" (*dst), /* 1 */ + "+A" (expect) /* 2 */ + : "b" ((uint32_t)src), /* 3 */ + "c" ((uint32_t)(src >> 32)) /* 4 */ + : "memory", "cc"); + return (res); +} + +static __inline uint64_t +atomic_load_acq_64_i586(volatile uint64_t *p) +{ + uint64_t res; + + __asm __volatile( + " movl %%ebx,%%eax ; " + " movl %%ecx,%%edx ; " + " " MPLOCKED " " + " cmpxchg8b %1" + : "=&A" (res), /* 0 */ + "+m" (*p) /* 1 */ + : : "memory", "cc"); + return (res); +} + +static __inline void +atomic_store_rel_64_i586(volatile uint64_t *p, uint64_t v) +{ + + __asm __volatile( + " movl %%eax,%%ebx ; " + " movl %%edx,%%ecx ; " + "1: " + " " MPLOCKED " " + " cmpxchg8b %0 ; " + " jne 1b" + : "+m" (*p), /* 0 */ + "+A" (v) /* 1 */ + : : "ebx", "ecx", "memory", "cc"); +} + +static __inline uint64_t +atomic_swap_64_i586(volatile uint64_t *p, uint64_t v) +{ + + __asm __volatile( + " movl %%eax,%%ebx ; " + " movl %%edx,%%ecx ; " + "1: " + " " MPLOCKED " " + " cmpxchg8b %0 ; " + " jne 1b" + : "+m" (*p), /* 0 */ + "+A" (v) /* 1 */ + : : "ebx", "ecx", "memory", "cc"); + return (v); +} + +static __inline int +atomic_cmpset_64(volatile uint64_t *dst, uint64_t expect, uint64_t src) +{ + + if ((cpu_feature & CPUID_CX8) == 0) + return (atomic_cmpset_64_i386(dst, expect, src)); + else + return (atomic_cmpset_64_i586(dst, expect, src)); +} + +static __inline uint64_t +atomic_load_acq_64(volatile uint64_t *p) +{ + + if ((cpu_feature & CPUID_CX8) == 0) + return (atomic_load_acq_64_i386(p)); + else + return (atomic_load_acq_64_i586(p)); +} + +static __inline void +atomic_store_rel_64(volatile uint64_t *p, uint64_t v) +{ + + if ((cpu_feature & CPUID_CX8) == 0) + atomic_store_rel_64_i386(p, v); + else + atomic_store_rel_64_i586(p, v); +} + +static __inline uint64_t +atomic_swap_64(volatile uint64_t *p, uint64_t v) +{ + + if ((cpu_feature & CPUID_CX8) == 0) + return (atomic_swap_64_i386(p, v)); + else + return (atomic_swap_64_i586(p, v)); +} + +#endif /* _KERNEL */ + #endif /* KLD_MODULE || !__GNUCLIKE_ASM */ ATOMIC_ASM(set, char, "orb %b1,%0", "iq", v); @@ -373,11 +527,6 @@ ATOMIC_STORE(long); #ifndef WANT_FUNCTIONS -#ifdef _KERNEL -extern uint64_t (*atomic_load_acq_64)(volatile uint64_t *); -extern void (*atomic_store_rel_64)(volatile uint64_t *, uint64_t); -#endif - static __inline int atomic_cmpset_long(volatile u_long *dst, u_long expect, u_long src) { @@ -393,45 +542,39 @@ atomic_fetchadd_long(volatile u_long *p, u_long v) return (atomic_fetchadd_int((volatile u_int *)p, (u_int)v)); } -/* Read the current value and store a zero in the destination. */ +static __inline int +atomic_testandset_long(volatile u_long *p, u_int v) +{ + + return (atomic_testandset_int((volatile u_int *)p, v)); +} + +/* Read the current value and store a new value in the destination. */ #ifdef __GNUCLIKE_ASM static __inline u_int -atomic_readandclear_int(volatile u_int *addr) +atomic_swap_int(volatile u_int *p, u_int v) { - u_int res; - res = 0; __asm __volatile( " xchgl %1,%0 ; " - "# atomic_readandclear_int" - : "+r" (res), /* 0 */ - "=m" (*addr) /* 1 */ - : "m" (*addr)); - - return (res); + "# atomic_swap_int" + : "+r" (v), /* 0 */ + "+m" (*p)); /* 1 */ + return (v); } static __inline u_long -atomic_readandclear_long(volatile u_long *addr) +atomic_swap_long(volatile u_long *p, u_long v) { - u_long res; - res = 0; - __asm __volatile( - " xchgl %1,%0 ; " - "# atomic_readandclear_long" - : "+r" (res), /* 0 */ - "=m" (*addr) /* 1 */ - : "m" (*addr)); - - return (res); + return (atomic_swap_int((volatile u_int *)p, (u_int)v)); } #else /* !__GNUCLIKE_ASM */ -u_int atomic_readandclear_int(volatile u_int *addr); -u_long atomic_readandclear_long(volatile u_long *addr); +u_int atomic_swap_int(volatile u_int *p, u_int v); +u_long atomic_swap_long(volatile u_long *p, u_long v); #endif /* __GNUCLIKE_ASM */ @@ -475,6 +618,9 @@ u_long atomic_readandclear_long(volatile u_long *addr); #define atomic_cmpset_acq_long atomic_cmpset_long #define atomic_cmpset_rel_long atomic_cmpset_long +#define atomic_readandclear_int(p) atomic_swap_int(p, 0) +#define atomic_readandclear_long(p) atomic_swap_long(p, 0) + /* Operations on 8-bit bytes. */ #define atomic_set_8 atomic_set_char #define atomic_set_acq_8 atomic_set_acq_char @@ -525,8 +671,10 @@ u_long atomic_readandclear_long(volatile u_long *addr); #define atomic_cmpset_32 atomic_cmpset_int #define atomic_cmpset_acq_32 atomic_cmpset_acq_int #define atomic_cmpset_rel_32 atomic_cmpset_rel_int +#define atomic_swap_32 atomic_swap_int #define atomic_readandclear_32 atomic_readandclear_int #define atomic_fetchadd_32 atomic_fetchadd_int +#define atomic_testandset_32 atomic_testandset_int /* Operations on pointers. */ #define atomic_set_ptr(p, v) \ @@ -565,6 +713,8 @@ u_long atomic_readandclear_long(volatile u_long *addr); #define atomic_cmpset_rel_ptr(dst, old, new) \ atomic_cmpset_rel_int((volatile u_int *)(dst), (u_int)(old), \ (u_int)(new)) +#define atomic_swap_ptr(p, v) \ + atomic_swap_int((volatile u_int *)(p), (u_int)(v)) #define atomic_readandclear_ptr(p) \ atomic_readandclear_int((volatile u_int *)(p)) diff --git a/sys/i386/include/cpu.h b/sys/i386/include/cpu.h index 5bc9f5c04c1..9655a153540 100644 --- a/sys/i386/include/cpu.h +++ b/sys/i386/include/cpu.h @@ -54,6 +54,17 @@ #define TRAPF_PC(framep) ((framep)->tf_eip) #ifdef _KERNEL +/* + * Struct containing pointers to CPU management functions whose + * implementation is run time selectable. Selection can be made, + * for example, based on detection of a particular CPU variant or + * hypervisor environment. + */ +struct cpu_ops { + void (*ipi_vectored)(u_int, int); +}; + +extern struct cpu_ops cpu_ops; extern char btext[]; extern char etext[]; diff --git a/sys/i386/include/intr_machdep.h b/sys/i386/include/intr_machdep.h index b3dd122301e..6bbe378a9e7 100644 --- a/sys/i386/include/intr_machdep.h +++ b/sys/i386/include/intr_machdep.h @@ -44,12 +44,30 @@ * allocate IDT vectors. * * The first 255 IRQs (0 - 254) are reserved for ISA IRQs and PCI intline IRQs. - * IRQ values beyond 256 are used by MSI. We leave 255 unused to avoid - * confusion since 255 is used in PCI to indicate an invalid IRQ. + * IRQ values from 256 to 767 are used by MSI. When running under the Xen + * Hypervisor, IRQ values from 768 to 4863 are available for binding to + * event channel events. We leave 255 unused to avoid confusion since 255 is + * used in PCI to indicate an invalid IRQ. */ #define NUM_MSI_INTS 512 #define FIRST_MSI_INT 256 -#define NUM_IO_INTS (FIRST_MSI_INT + NUM_MSI_INTS) +#ifdef XENHVM +#include +#define NUM_EVTCHN_INTS NR_EVENT_CHANNELS +#define FIRST_EVTCHN_INT \ + (FIRST_MSI_INT + NUM_MSI_INTS) +#define LAST_EVTCHN_INT \ + (FIRST_EVTCHN_INT + NUM_EVTCHN_INTS - 1) +#elif defined(XEN) +#include +#define NUM_EVTCHN_INTS NR_EVENT_CHANNELS +#define FIRST_EVTCHN_INT 0 +#define LAST_EVTCHN_INT \ + (FIRST_EVTCHN_INT + NUM_EVTCHN_INTS - 1) +#else /* !XEN && !XENHVM */ +#define NUM_EVTCHN_INTS 0 +#endif +#define NUM_IO_INTS (FIRST_MSI_INT + NUM_MSI_INTS + NUM_EVTCHN_INTS) /* * Default base address for MSI messages on x86 platforms. diff --git a/sys/i386/include/pcpu.h b/sys/i386/include/pcpu.h index 3606d12dd1b..60e50f8167b 100644 --- a/sys/i386/include/pcpu.h +++ b/sys/i386/include/pcpu.h @@ -44,15 +44,6 @@ * other processors" */ -#if defined(XEN) || defined(XENHVM) -#ifndef NR_VIRQS -#define NR_VIRQS 24 -#endif -#ifndef NR_IPIS -#define NR_IPIS 2 -#endif -#endif - #if defined(XEN) /* These are peridically updated in shared_info, and then copied here. */ @@ -71,21 +62,9 @@ struct shadow_time_info { vm_paddr_t *pc_pdir_shadow; \ uint64_t pc_processed_system_time; \ struct shadow_time_info pc_shadow_time; \ - int pc_resched_irq; \ - int pc_callfunc_irq; \ - int pc_virq_to_irq[NR_VIRQS]; \ - int pc_ipi_to_irq[NR_IPIS]; \ - char __pad[77] + char __pad[189] -#elif defined(XENHVM) - -#define PCPU_XEN_FIELDS \ - ; \ - unsigned int pc_last_processed_l1i; \ - unsigned int pc_last_processed_l2i; \ - char __pad[229] - -#else /* !XEN && !XENHVM */ +#else /* !XEN */ #define PCPU_XEN_FIELDS \ ; \ diff --git a/sys/i386/include/pmap.h b/sys/i386/include/pmap.h index 8c20e1b14a4..0303c604b52 100644 --- a/sys/i386/include/pmap.h +++ b/sys/i386/include/pmap.h @@ -213,7 +213,9 @@ extern pd_entry_t *IdlePTD; /* physical address of "Idle" state directory */ #if defined(XEN) #include -#include + +#include + #include #include @@ -326,98 +328,27 @@ pmap_kextract(vm_offset_t va) #if defined(PAE) && !defined(XEN) -#define pde_cmpset(pdep, old, new) \ - atomic_cmpset_64((pdep), (old), (new)) - -static __inline pt_entry_t -pte_load(pt_entry_t *ptep) -{ - pt_entry_t r; - - __asm __volatile( - "lock; cmpxchg8b %1" - : "=A" (r) - : "m" (*ptep), "a" (0), "d" (0), "b" (0), "c" (0)); - return (r); -} - -static __inline pt_entry_t -pte_load_store(pt_entry_t *ptep, pt_entry_t v) -{ - pt_entry_t r; - - r = *ptep; - __asm __volatile( - "1:\n" - "\tlock; cmpxchg8b %1\n" - "\tjnz 1b" - : "+A" (r) - : "m" (*ptep), "b" ((uint32_t)v), "c" ((uint32_t)(v >> 32))); - return (r); -} - -/* XXXRU move to atomic.h? */ -static __inline int -atomic_cmpset_64(volatile uint64_t *dst, uint64_t exp, uint64_t src) -{ - int64_t res = exp; - - __asm __volatile ( - " lock ; " - " cmpxchg8b %2 ; " - " setz %%al ; " - " movzbl %%al,%0 ; " - "# atomic_cmpset_64" - : "+A" (res), /* 0 (result) */ - "=m" (*dst) /* 1 */ - : "m" (*dst), /* 2 */ - "b" ((uint32_t)src), - "c" ((uint32_t)(src >> 32))); - - return (res); -} - -#define pte_load_clear(ptep) pte_load_store((ptep), (pt_entry_t)0ULL) - -#define pte_store(ptep, pte) pte_load_store((ptep), (pt_entry_t)pte) +#define pde_cmpset(pdep, old, new) atomic_cmpset_64_i586(pdep, old, new) +#define pte_load_store(ptep, pte) atomic_swap_64_i586(ptep, pte) +#define pte_load_clear(ptep) atomic_swap_64_i586(ptep, 0) +#define pte_store(ptep, pte) atomic_store_rel_64_i586(ptep, pte) extern pt_entry_t pg_nx; -#elif !defined(PAE) && !defined (XEN) +#elif !defined(PAE) && !defined(XEN) -#define pde_cmpset(pdep, old, new) \ - atomic_cmpset_int((pdep), (old), (new)) - -static __inline pt_entry_t -pte_load(pt_entry_t *ptep) -{ - pt_entry_t r; - - r = *ptep; - return (r); -} - -static __inline pt_entry_t -pte_load_store(pt_entry_t *ptep, pt_entry_t pte) -{ - __asm volatile("xchgl %0, %1" : "+m" (*ptep), "+r" (pte)); - return (pte); -} - -#define pte_load_clear(pte) atomic_readandclear_int(pte) - -static __inline void -pte_store(pt_entry_t *ptep, pt_entry_t pte) -{ - - *ptep = pte; -} +#define pde_cmpset(pdep, old, new) atomic_cmpset_int(pdep, old, new) +#define pte_load_store(ptep, pte) atomic_swap_int(ptep, pte) +#define pte_load_clear(ptep) atomic_swap_int(ptep, 0) +#define pte_store(ptep, pte) do { \ + *(u_int *)(ptep) = (u_int)(pte); \ +} while (0) #endif /* PAE */ -#define pte_clear(ptep) pte_store((ptep), (pt_entry_t)0ULL) +#define pte_clear(ptep) pte_store(ptep, 0) -#define pde_store(pdep, pde) pte_store((pdep), (pde)) +#define pde_store(pdep, pde) pte_store(pdep, pde) #endif /* _KERNEL */ diff --git a/sys/i386/include/sf_buf.h b/sys/i386/include/sf_buf.h index 415dcbb86e6..20296b3c153 100644 --- a/sys/i386/include/sf_buf.h +++ b/sys/i386/include/sf_buf.h @@ -45,6 +45,9 @@ struct sf_buf { #endif }; +struct sf_buf * sf_buf_alloc(struct vm_page *m, int flags); +void sf_buf_free(struct sf_buf *sf); + static __inline vm_offset_t sf_buf_kva(struct sf_buf *sf) { diff --git a/sys/i386/include/smp.h b/sys/i386/include/smp.h index 7c11820ddc6..43dad10c3b6 100644 --- a/sys/i386/include/smp.h +++ b/sys/i386/include/smp.h @@ -84,11 +84,6 @@ void smp_masked_invltlb(cpuset_t mask); #ifdef XEN void ipi_to_irq_init(void); - -#define RESCHEDULE_VECTOR 0 -#define CALL_FUNCTION_VECTOR 1 -#define NR_IPIS 2 - #endif #endif /* !LOCORE */ #endif /* SMP */ diff --git a/sys/i386/include/xen/xen-os.h b/sys/i386/include/xen/xen-os.h index 257202ec9f9..e15d6687006 100644 --- a/sys/i386/include/xen/xen-os.h +++ b/sys/i386/include/xen/xen-os.h @@ -1,75 +1,45 @@ -/****************************************************************************** - * os.h +/***************************************************************************** + * i386/xen/xen-os.h * - * random collection of macros and definition + * Random collection of macros and definition + * + * Copyright (c) 2003, 2004 Keir Fraser (on behalf of the Xen team) + * All rights reserved. + * + * 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. + * + * $FreeBSD$ */ -#ifndef _XEN_OS_H_ -#define _XEN_OS_H_ -#include +#ifndef _MACHINE_XEN_XEN_OS_H_ +#define _MACHINE_XEN_XEN_OS_H_ #ifdef PAE #define CONFIG_X86_PAE #endif -#ifdef LOCORE -#define __ASSEMBLY__ -#endif - -#if !defined(__XEN_INTERFACE_VERSION__) -#define __XEN_INTERFACE_VERSION__ 0x00030208 -#endif - -#define GRANT_REF_INVALID 0xffffffff - -#include - /* Everything below this point is not included by assembler (.S) files. */ #ifndef __ASSEMBLY__ /* Force a proper event-channel callback from Xen. */ void force_evtchn_callback(void); -#define likely(x) __builtin_expect((x),1) -#define unlikely(x) __builtin_expect((x),0) - -#ifndef vtophys -#include -#include -#include -#endif - -extern int gdtset; -#ifdef SMP -#include /* XXX for pcpu.h */ -#include /* XXX for PCPU_GET */ -static inline int -smp_processor_id(void) -{ - if (likely(gdtset)) - return PCPU_GET(cpuid); - return 0; -} - -#else -#define smp_processor_id() 0 -#endif - -#ifndef NULL -#define NULL (void *)0 -#endif - -#ifndef PANIC_IF -#define PANIC_IF(exp) if (unlikely(exp)) {printk("panic - %s: %s:%d\n",#exp, __FILE__, __LINE__); panic("%s: %s:%d", #exp, __FILE__, __LINE__);} -#endif - -extern shared_info_t *HYPERVISOR_shared_info; - -/* Somewhere in the middle of the GCC 2.96 development cycle, we implemented - a mechanism by which the user can annotate likely branch directions and - expect the blocks to be reordered appropriately. Define __builtin_expect - to nothing for earlier compilers. */ - /* REP NOP (PAUSE) is a good thing to insert into busy-wait loops. */ static inline void rep_nop(void) { @@ -77,28 +47,36 @@ static inline void rep_nop(void) } #define cpu_relax() rep_nop() +#ifndef XENHVM +void xc_printf(const char *fmt, ...); -#if __GNUC__ == 2 && __GNUC_MINOR__ < 96 -#define __builtin_expect(x, expected_value) (x) +#ifdef SMP +extern int gdtset; + +#include /* XXX for pcpu.h */ +#include /* XXX for PCPU_GET */ +static inline int +smp_processor_id(void) +{ + if (__predict_true(gdtset)) + return PCPU_GET(cpuid); + return 0; +} + +#else +#define smp_processor_id() 0 #endif -#define per_cpu(var, cpu) (pcpu_find((cpu))->pc_ ## var) +#ifndef PANIC_IF +#define PANIC_IF(exp) if (__predict_false(exp)) {printf("panic - %s: %s:%d\n",#exp, __FILE__, __LINE__); panic("%s: %s:%d", #exp, __FILE__, __LINE__);} +#endif -/* crude memory allocator for memory allocation early in - * boot +/* + * Crude memory allocator for memory allocation early in boot. */ void *bootmem_alloc(unsigned int size); void bootmem_free(void *ptr, unsigned int size); -#include - -void printk(const char *fmt, ...); - -/* some function prototypes */ -void trap_init(void); - -#ifndef XENHVM - /* * STI/CLI equivalents. These basically set and clear the virtual * event_enable flag in the shared_info structure. Note that when @@ -106,7 +84,6 @@ void trap_init(void); * We may therefore call into do_hypervisor_callback() directly. */ - #define __cli() \ do { \ vcpu_info_t *_vcpu; \ @@ -122,7 +99,7 @@ do { \ _vcpu = &HYPERVISOR_shared_info->vcpu_info[smp_processor_id()]; \ _vcpu->evtchn_upcall_mask = 0; \ barrier(); /* unmask then check (avoid races) */ \ - if ( unlikely(_vcpu->evtchn_upcall_pending) ) \ + if (__predict_false(_vcpu->evtchn_upcall_pending)) \ force_evtchn_callback(); \ } while (0) @@ -133,7 +110,7 @@ do { \ _vcpu = &HYPERVISOR_shared_info->vcpu_info[smp_processor_id()]; \ if ((_vcpu->evtchn_upcall_mask = (x)) == 0) { \ barrier(); /* unmask then check (avoid races) */ \ - if ( unlikely(_vcpu->evtchn_upcall_pending) ) \ + if (__predict_false(_vcpu->evtchn_upcall_pending)) \ force_evtchn_callback(); \ } \ } while (0) @@ -168,31 +145,7 @@ do { \ #define spin_lock_irqsave mtx_lock_irqsave #define spin_unlock_irqrestore mtx_unlock_irqrestore -#endif - -#ifndef xen_mb -#define xen_mb() mb() -#endif -#ifndef xen_rmb -#define xen_rmb() rmb() -#endif -#ifndef xen_wmb -#define xen_wmb() wmb() -#endif -#ifdef SMP -#define smp_mb() mb() -#define smp_rmb() rmb() -#define smp_wmb() wmb() -#define smp_read_barrier_depends() read_barrier_depends() -#define set_mb(var, value) do { xchg(&var, value); } while (0) -#else -#define smp_mb() barrier() -#define smp_rmb() barrier() -#define smp_wmb() barrier() -#define smp_read_barrier_depends() do { } while(0) -#define set_mb(var, value) do { var = value; barrier(); } while (0) -#endif - +#endif /* !XENHVM */ /* This is a barrier for the compiler only, NOT the processor! */ #define barrier() __asm__ __volatile__("": : :"memory") @@ -207,8 +160,6 @@ do { \ */ typedef struct { volatile int counter; } atomic_t; - - #define xen_xchg(ptr,v) \ ((__typeof__(*(ptr)))__xchg((unsigned long)(v),(ptr),sizeof(*(ptr)))) struct __xchg_dummy { unsigned long a[100]; }; @@ -335,33 +286,6 @@ static __inline__ void atomic_inc(atomic_t *v) #define rdtscll(val) \ __asm__ __volatile__("rdtsc" : "=A" (val)) - - -/* - * Kernel pointers have redundant information, so we can use a - * scheme where we can return either an error code or a dentry - * pointer with the same return value. - * - * This should be a per-architecture thing, to allow different - * error and pointer decisions. - */ -#define IS_ERR_VALUE(x) unlikely((x) > (unsigned long)-1000L) - -static inline void *ERR_PTR(long error) -{ - return (void *) error; -} - -static inline long PTR_ERR(const void *ptr) -{ - return (long) ptr; -} - -static inline long IS_ERR(const void *ptr) -{ - return IS_ERR_VALUE((unsigned long)ptr); -} - #endif /* !__ASSEMBLY__ */ -#endif /* _OS_H_ */ +#endif /* _MACHINE_XEN_XEN_OS_H_ */ diff --git a/sys/i386/include/xen/xenfunc.h b/sys/i386/include/xen/xenfunc.h index 47f04057aa6..f02ee1212e3 100644 --- a/sys/i386/include/xen/xenfunc.h +++ b/sys/i386/include/xen/xenfunc.h @@ -29,10 +29,14 @@ #ifndef _XEN_XENFUNC_H_ #define _XEN_XENFUNC_H_ -#include +#include #include + +#include + #include #include + #include #define BKPT __asm__("int3"); #define XPQ_CALL_DEPTH 5 diff --git a/sys/i386/include/xen/xenvar.h b/sys/i386/include/xen/xenvar.h index b1a0a4d8191..2742613249d 100644 --- a/sys/i386/include/xen/xenvar.h +++ b/sys/i386/include/xen/xenvar.h @@ -37,7 +37,8 @@ #define XPMAP 0x2 extern int xendebug_flags; #ifndef NOXENDEBUG -#define XENPRINTF printk +/* Print directly to the Xen console during debugging. */ +#define XENPRINTF xc_printf #else #define XENPRINTF printf #endif diff --git a/sys/i386/isa/npx.c b/sys/i386/isa/npx.c index 3b86d841866..622e5b7f15b 100644 --- a/sys/i386/isa/npx.c +++ b/sys/i386/isa/npx.c @@ -69,7 +69,7 @@ __FBSDID("$FreeBSD$"); #include #ifdef XEN -#include +#include #include #endif diff --git a/sys/i386/linux/imgact_linux.c b/sys/i386/linux/imgact_linux.c index d92621026bf..0a4114bc7b1 100644 --- a/sys/i386/linux/imgact_linux.c +++ b/sys/i386/linux/imgact_linux.c @@ -139,8 +139,8 @@ exec_linux_imgact(struct image_params *imgp) */ vmaddr = virtual_offset; error = vm_map_find(&vmspace->vm_map, NULL, 0, &vmaddr, - a_out->a_text + a_out->a_data + bss_size, FALSE, - VM_PROT_ALL, VM_PROT_ALL, 0); + a_out->a_text + a_out->a_data + bss_size, 0, VMFS_NO_SPACE, + VM_PROT_ALL, VM_PROT_ALL, 0); if (error) goto fail; @@ -204,7 +204,7 @@ exec_linux_imgact(struct image_params *imgp) if (bss_size != 0) { vmaddr = virtual_offset + a_out->a_text + a_out->a_data; error = vm_map_find(&vmspace->vm_map, NULL, 0, &vmaddr, - bss_size, FALSE, VM_PROT_ALL, VM_PROT_ALL, 0); + bss_size, 0, VMFS_NO_SPACE, VM_PROT_ALL, VM_PROT_ALL, 0); if (error) goto fail; #ifdef DEBUG diff --git a/sys/i386/linux/linux_machdep.c b/sys/i386/linux/linux_machdep.c index 14d18920b61..2d792046eb3 100644 --- a/sys/i386/linux/linux_machdep.c +++ b/sys/i386/linux/linux_machdep.c @@ -422,6 +422,7 @@ linux_mmap_common(struct thread *td, l_uintptr_t addr, l_size_t len, l_int prot, } */ bsd_args; int error; struct file *fp; + cap_rights_t rights; error = 0; bsd_args.flags = 0; @@ -473,7 +474,9 @@ linux_mmap_common(struct thread *td, l_uintptr_t addr, l_size_t len, l_int prot, * is done in the FreeBSD mmap(). */ - if ((error = fget(td, bsd_args.fd, CAP_MMAP, &fp)) != 0) + error = fget(td, bsd_args.fd, + cap_rights_init(&rights, CAP_MMAP), &fp); + if (error != 0) return (error); if (fp->f_type != DTYPE_VNODE) { fdrop(fp, td); diff --git a/sys/i386/linux/linux_proto.h b/sys/i386/linux/linux_proto.h index 9373d3e346b..912c60dfb94 100644 --- a/sys/i386/linux/linux_proto.h +++ b/sys/i386/linux/linux_proto.h @@ -3,7 +3,7 @@ * * DO NOT EDIT-- this file is automatically generated. * $FreeBSD$ - * created from FreeBSD: head/sys/i386/linux/syscalls.master 238917 2012-07-30 20:44:45Z jhb + * created from FreeBSD: head/sys/i386/linux/syscalls.master 255675 2013-09-18 18:48:33Z rdivacky */ #ifndef _LINUX_SYSPROTO_H_ @@ -15,6 +15,7 @@ #include #include #include +#include #include diff --git a/sys/i386/linux/linux_syscall.h b/sys/i386/linux/linux_syscall.h index c02e40b660b..bb76143f1c5 100644 --- a/sys/i386/linux/linux_syscall.h +++ b/sys/i386/linux/linux_syscall.h @@ -3,7 +3,7 @@ * * DO NOT EDIT-- this file is automatically generated. * $FreeBSD$ - * created from FreeBSD: head/sys/i386/linux/syscalls.master 238917 2012-07-30 20:44:45Z jhb + * created from FreeBSD: head/sys/i386/linux/syscalls.master 255675 2013-09-18 18:48:33Z rdivacky */ #define LINUX_SYS_exit 1 diff --git a/sys/i386/linux/linux_syscalls.c b/sys/i386/linux/linux_syscalls.c index 4e7646a19d5..38642f4ddb0 100644 --- a/sys/i386/linux/linux_syscalls.c +++ b/sys/i386/linux/linux_syscalls.c @@ -3,7 +3,7 @@ * * DO NOT EDIT-- this file is automatically generated. * $FreeBSD$ - * created from FreeBSD: head/sys/i386/linux/syscalls.master 238917 2012-07-30 20:44:45Z jhb + * created from FreeBSD: head/sys/i386/linux/syscalls.master 255675 2013-09-18 18:48:33Z rdivacky */ const char *linux_syscallnames[] = { diff --git a/sys/i386/linux/linux_sysent.c b/sys/i386/linux/linux_sysent.c index fc64fdda45b..0d9168aad06 100644 --- a/sys/i386/linux/linux_sysent.c +++ b/sys/i386/linux/linux_sysent.c @@ -3,7 +3,7 @@ * * DO NOT EDIT-- this file is automatically generated. * $FreeBSD$ - * created from FreeBSD: head/sys/i386/linux/syscalls.master 238917 2012-07-30 20:44:45Z jhb + * created from FreeBSD: head/sys/i386/linux/syscalls.master 255675 2013-09-18 18:48:33Z rdivacky */ #include diff --git a/sys/i386/pci/pci_cfgreg.c b/sys/i386/pci/pci_cfgreg.c index 14558d80ac1..bddaaa836c7 100644 --- a/sys/i386/pci/pci_cfgreg.c +++ b/sys/i386/pci/pci_cfgreg.c @@ -562,7 +562,7 @@ pcie_cfgregopen(uint64_t base, uint8_t minbus, uint8_t maxbus) if (pcie_array == NULL) return (0); - va = kmem_alloc_nofault(kernel_map, PCIE_CACHE * PAGE_SIZE); + va = kva_alloc(PCIE_CACHE * PAGE_SIZE); if (va == 0) { free(pcie_array, M_DEVBUF); return (0); diff --git a/sys/i386/xen/clock.c b/sys/i386/xen/clock.c index a10b5462b7a..075f5675cad 100644 --- a/sys/i386/xen/clock.c +++ b/sys/i386/xen/clock.c @@ -79,16 +79,15 @@ __FBSDID("$FreeBSD$"); #include #include -#include #include #include #include #include -#include +#include #include #include #include -#include +#include /* * 32-bit time_t's can't reach leap years before 1904 or after 2036, so we @@ -117,6 +116,7 @@ struct mtx clock_lock; mtx_init(&clock_lock, "clk", NULL, MTX_SPIN | MTX_NOPROFILE) #define RTC_LOCK mtx_lock_spin(&clock_lock) #define RTC_UNLOCK mtx_unlock_spin(&clock_lock) +#define NS_PER_TICK (1000000000ULL/hz) int adjkerntz; /* local offset from GMT in seconds */ int clkintr_pending; @@ -124,20 +124,10 @@ int pscnt = 1; int psdiv = 1; int wall_cmos_clock; u_int timer_freq = TIMER_FREQ; -static int independent_wallclock; -static int xen_disable_rtc_set; static u_long cyc2ns_scale; -static struct timespec shadow_tv; -static uint32_t shadow_tv_version; /* XXX: lazy locking */ static uint64_t processed_system_time; /* stime (ns) at last processing. */ -static const u_char daysinmonth[] = {31,28,31,30,31,30,31,31,30,31,30,31}; - -SYSCTL_INT(_machdep, OID_AUTO, independent_wallclock, - CTLFLAG_RW, &independent_wallclock, 0, ""); -SYSCTL_INT(_machdep, OID_AUTO, xen_disable_rtc_set, - CTLFLAG_RW, &xen_disable_rtc_set, 1, ""); - +extern volatile uint64_t xen_timer_last_time; #define do_div(n,base) ({ \ unsigned long __upper, __low, __high, __mod, __base; \ @@ -154,12 +144,6 @@ SYSCTL_INT(_machdep, OID_AUTO, xen_disable_rtc_set, }) -#define NS_PER_TICK (1000000000ULL/hz) - -#define rdtscll(val) \ - __asm__ __volatile__("rdtsc" : "=A" (val)) - - /* convert from cycles(64bits) => nanoseconds (64bits) * basic equation: * ns = cycles / (freq / ns_per_sec) @@ -182,201 +166,13 @@ static inline void set_cyc2ns_scale(unsigned long cpu_mhz) static inline unsigned long long cycles_2_ns(unsigned long long cyc) { - return (cyc * cyc2ns_scale) >> CYC2NS_SCALE_FACTOR; + return ((cyc * cyc2ns_scale) >> CYC2NS_SCALE_FACTOR); } -/* - * Scale a 64-bit delta by scaling and multiplying by a 32-bit fraction, - * yielding a 64-bit result. - */ -static inline uint64_t -scale_delta(uint64_t delta, uint32_t mul_frac, int shift) -{ - uint64_t product; - uint32_t tmp1, tmp2; - - if ( shift < 0 ) - delta >>= -shift; - else - delta <<= shift; - - __asm__ ( - "mul %5 ; " - "mov %4,%%eax ; " - "mov %%edx,%4 ; " - "mul %5 ; " - "xor %5,%5 ; " - "add %4,%%eax ; " - "adc %5,%%edx ; " - : "=A" (product), "=r" (tmp1), "=r" (tmp2) - : "a" ((uint32_t)delta), "1" ((uint32_t)(delta >> 32)), "2" (mul_frac) ); - - return product; -} - -static uint64_t -get_nsec_offset(struct shadow_time_info *shadow) -{ - uint64_t now, delta; - rdtscll(now); - delta = now - shadow->tsc_timestamp; - return scale_delta(delta, shadow->tsc_to_nsec_mul, shadow->tsc_shift); -} - -static void update_wallclock(void) -{ - shared_info_t *s = HYPERVISOR_shared_info; - - do { - shadow_tv_version = s->wc_version; - rmb(); - shadow_tv.tv_sec = s->wc_sec; - shadow_tv.tv_nsec = s->wc_nsec; - rmb(); - } - while ((s->wc_version & 1) | (shadow_tv_version ^ s->wc_version)); - -} - -static void -add_uptime_to_wallclock(void) -{ - struct timespec ut; - - xen_fetch_uptime(&ut); - timespecadd(&shadow_tv, &ut); -} - -/* - * Reads a consistent set of time-base values from Xen, into a shadow data - * area. Must be called with the xtime_lock held for writing. - */ -static void __get_time_values_from_xen(void) -{ - shared_info_t *s = HYPERVISOR_shared_info; - struct vcpu_time_info *src; - struct shadow_time_info *dst; - uint32_t pre_version, post_version; - - src = &s->vcpu_info[smp_processor_id()].time; - dst = &per_cpu(shadow_time, smp_processor_id()); - - spinlock_enter(); - do { - pre_version = dst->version = src->version; - rmb(); - dst->tsc_timestamp = src->tsc_timestamp; - dst->system_timestamp = src->system_time; - dst->tsc_to_nsec_mul = src->tsc_to_system_mul; - dst->tsc_shift = src->tsc_shift; - rmb(); - post_version = src->version; - } - while ((pre_version & 1) | (pre_version ^ post_version)); - - dst->tsc_to_usec_mul = dst->tsc_to_nsec_mul / 1000; - spinlock_exit(); -} - - -static inline int time_values_up_to_date(int cpu) -{ - struct vcpu_time_info *src; - struct shadow_time_info *dst; - - src = &HYPERVISOR_shared_info->vcpu_info[cpu].time; - dst = &per_cpu(shadow_time, cpu); - - rmb(); - return (dst->version == src->version); -} - -static unsigned xen_get_timecount(struct timecounter *tc); - -static struct timecounter xen_timecounter = { - xen_get_timecount, /* get_timecount */ - 0, /* no poll_pps */ - ~0u, /* counter_mask */ - 0, /* frequency */ - "ixen", /* name */ - 0 /* quality */ -}; - -static struct eventtimer xen_et; - -struct xen_et_state { - int mode; -#define MODE_STOP 0 -#define MODE_PERIODIC 1 -#define MODE_ONESHOT 2 - int64_t period; - int64_t next; -}; - -static DPCPU_DEFINE(struct xen_et_state, et_state); - -static int -clkintr(void *arg) -{ - int64_t now; - int cpu = smp_processor_id(); - struct shadow_time_info *shadow = &per_cpu(shadow_time, cpu); - struct xen_et_state *state = DPCPU_PTR(et_state); - - do { - __get_time_values_from_xen(); - now = shadow->system_timestamp + get_nsec_offset(shadow); - } while (!time_values_up_to_date(cpu)); - - /* Process elapsed ticks since last call. */ - processed_system_time = now; - if (state->mode == MODE_PERIODIC) { - while (now >= state->next) { - state->next += state->period; - if (xen_et.et_active) - xen_et.et_event_cb(&xen_et, xen_et.et_arg); - } - HYPERVISOR_set_timer_op(state->next + 50000); - } else if (state->mode == MODE_ONESHOT) { - if (xen_et.et_active) - xen_et.et_event_cb(&xen_et, xen_et.et_arg); - } - /* - * Take synchronised time from Xen once a minute if we're not - * synchronised ourselves, and we haven't chosen to keep an independent - * time base. - */ - - if (shadow_tv_version != HYPERVISOR_shared_info->wc_version && - !independent_wallclock) { - printf("[XEN] hypervisor wallclock nudged; nudging TOD.\n"); - update_wallclock(); - add_uptime_to_wallclock(); - tc_setclock(&shadow_tv); - } - - /* XXX TODO */ - return (FILTER_HANDLED); -} static uint32_t getit(void) { - struct shadow_time_info *shadow; - uint64_t time; - uint32_t local_time_version; - - shadow = &per_cpu(shadow_time, smp_processor_id()); - - do { - local_time_version = shadow->version; - barrier(); - time = shadow->system_timestamp + get_nsec_offset(shadow); - if (!time_values_up_to_date(smp_processor_id())) - __get_time_values_from_xen(/*cpu */); - barrier(); - } while (local_time_version != shadow->version); - - return (time); + return (xen_timer_last_time); } @@ -480,39 +276,13 @@ DELAY(int n) #endif } - -/* - * Restore all the timers non-atomically (XXX: should be atomically). - * - * This function is called from pmtimer_resume() to restore all the timers. - * This should not be necessary, but there are broken laptops that do not - * restore all the timers on resume. - */ -void -timer_restore(void) -{ - struct xen_et_state *state = DPCPU_PTR(et_state); - - /* Get timebases for new environment. */ - __get_time_values_from_xen(); - - /* Reset our own concept of passage of system time. */ - processed_system_time = per_cpu(shadow_time, 0).system_timestamp; - state->next = processed_system_time; -} - void startrtclock() { - unsigned long long alarm; uint64_t __cpu_khz; uint32_t cpu_khz; struct vcpu_time_info *info; - /* initialize xen values */ - __get_time_values_from_xen(); - processed_system_time = per_cpu(shadow_time, 0).system_timestamp; - __cpu_khz = 1000000ULL << 32; info = &HYPERVISOR_shared_info->vcpu_info[0].time; @@ -530,12 +300,6 @@ startrtclock() set_cyc2ns_scale(cpu_khz/1000); tsc_freq = cpu_khz * 1000; - - timer_freq = 1000000000LL; - xen_timecounter.tc_frequency = timer_freq >> 9; - tc_init(&xen_timecounter); - - rdtscll(alarm); } /* @@ -594,8 +358,10 @@ domu_resettodr(void) int s; dom0_op_t op; struct shadow_time_info *shadow; + struct pcpu *pc; - shadow = &per_cpu(shadow_time, smp_processor_id()); + pc = pcpu_find(smp_processor_id()); + shadow = &pc->pc_shadow_time; if (xen_disable_rtc_set) return; @@ -767,120 +533,20 @@ resettodr() } #endif -static int -xen_et_start(struct eventtimer *et, sbintime_t first, sbintime_t period) -{ - struct xen_et_state *state = DPCPU_PTR(et_state); - struct shadow_time_info *shadow; - int64_t fperiod; - - __get_time_values_from_xen(); - - if (period != 0) { - state->mode = MODE_PERIODIC; - state->period = (1000000000LLU * period) >> 32; - } else { - state->mode = MODE_ONESHOT; - state->period = 0; - } - if (first != 0) - fperiod = (1000000000LLU * first) >> 32; - else - fperiod = state->period; - - shadow = &per_cpu(shadow_time, smp_processor_id()); - state->next = shadow->system_timestamp + get_nsec_offset(shadow); - state->next += fperiod; - HYPERVISOR_set_timer_op(state->next + 50000); - return (0); -} - -static int -xen_et_stop(struct eventtimer *et) -{ - struct xen_et_state *state = DPCPU_PTR(et_state); - - state->mode = MODE_STOP; - HYPERVISOR_set_timer_op(0); - return (0); -} - /* * Start clocks running. */ void cpu_initclocks(void) { - unsigned int time_irq; - int error; - - HYPERVISOR_vcpu_op(VCPUOP_stop_periodic_timer, 0, NULL); - error = bind_virq_to_irqhandler(VIRQ_TIMER, 0, "cpu0:timer", - clkintr, NULL, NULL, INTR_TYPE_CLK, &time_irq); - if (error) - panic("failed to register clock interrupt\n"); - /* should fast clock be enabled ? */ - - bzero(&xen_et, sizeof(xen_et)); - xen_et.et_name = "ixen"; - xen_et.et_flags = ET_FLAGS_PERIODIC | ET_FLAGS_ONESHOT | - ET_FLAGS_PERCPU; - xen_et.et_quality = 600; - xen_et.et_frequency = 1000000000; - xen_et.et_min_period = 0x00400000LL; - xen_et.et_max_period = (0xfffffffeLLU << 32) / xen_et.et_frequency; - xen_et.et_start = xen_et_start; - xen_et.et_stop = xen_et_stop; - xen_et.et_priv = NULL; - et_register(&xen_et); - cpu_initclocks_bsp(); } -int -ap_cpu_initclocks(int cpu) -{ - char buf[MAXCOMLEN + 1]; - unsigned int time_irq; - int error; - - HYPERVISOR_vcpu_op(VCPUOP_stop_periodic_timer, cpu, NULL); - snprintf(buf, sizeof(buf), "cpu%d:timer", cpu); - error = bind_virq_to_irqhandler(VIRQ_TIMER, cpu, buf, - clkintr, NULL, NULL, INTR_TYPE_CLK, &time_irq); - if (error) - panic("failed to register clock interrupt\n"); - - return (0); -} - -static uint32_t -xen_get_timecount(struct timecounter *tc) -{ - uint64_t clk; - struct shadow_time_info *shadow; - shadow = &per_cpu(shadow_time, smp_processor_id()); - - __get_time_values_from_xen(); - - clk = shadow->system_timestamp + get_nsec_offset(shadow); - - return (uint32_t)(clk >> 9); - -} - /* Return system time offset by ticks */ uint64_t get_system_time(int ticks) { - return processed_system_time + (ticks * NS_PER_TICK); -} - -void -idle_block(void) -{ - - HYPERVISOR_sched_op(SCHEDOP_block, 0); + return (processed_system_time + (ticks * NS_PER_TICK)); } int diff --git a/sys/i386/xen/exception.s b/sys/i386/xen/exception.s index e965ffd026d..95f1c0e6703 100644 --- a/sys/i386/xen/exception.s +++ b/sys/i386/xen/exception.s @@ -168,7 +168,7 @@ call_evtchn_upcall: jb critical_region_fixup 10: pushl %esp - call evtchn_do_upcall + call xen_intr_handle_upcall addl $4,%esp /* diff --git a/sys/i386/xen/locore.s b/sys/i386/xen/locore.s index 59cdb5474e3..b67046e50ab 100644 --- a/sys/i386/xen/locore.s +++ b/sys/i386/xen/locore.s @@ -76,7 +76,7 @@ ELFNOTE(Xen, XEN_ELFNOTE_PADDR_OFFSET, .long, KERNBASE) ELFNOTE(Xen, XEN_ELFNOTE_ENTRY, .long, btext) ELFNOTE(Xen, XEN_ELFNOTE_HYPERCALL_PAGE, .long, hypercall_page) - ELFNOTE(Xen, XEN_ELFNOTE_HV_START_LOW, .long, HYPERVISOR_VIRT_START) + ELFNOTE(Xen, XEN_ELFNOTE_HV_START_LOW, .long, XEN_HYPERVISOR_VIRT_START) #if 0 ELFNOTE(Xen, XEN_ELFNOTE_FEATURES, .asciz, "writable_page_tables|writable_descriptor_tables|auto_translated_physmap|pae_pgdir_above_4gb|supervisor_mode_kernel") #endif diff --git a/sys/i386/xen/mp_machdep.c b/sys/i386/xen/mp_machdep.c index fdfa8124fd7..d6eb35b9ad2 100644 --- a/sys/i386/xen/mp_machdep.c +++ b/sys/i386/xen/mp_machdep.c @@ -85,26 +85,61 @@ __FBSDID("$FreeBSD$"); #include #include - - -#include +#include #include #include #include #include +/*---------------------------- Extern Declarations ---------------------------*/ +extern struct pcpu __pcpu[]; + +extern void Xhypervisor_callback(void); +extern void failsafe_callback(void); +extern void pmap_lazyfix_action(void); + +/*--------------------------- Forward Declarations ---------------------------*/ +static driver_filter_t smp_reschedule_interrupt; +static driver_filter_t smp_call_function_interrupt; +static void assign_cpu_ids(void); +static void set_interrupt_apic_ids(void); +static int start_all_aps(void); +static int start_ap(int apic_id); +static void release_aps(void *dummy); + +/*---------------------------------- Macros ----------------------------------*/ +#define IPI_TO_IDX(ipi) ((ipi) - APIC_IPI_INTS) + +/*-------------------------------- Local Types -------------------------------*/ +typedef void call_data_func_t(uintptr_t , uintptr_t); + +struct cpu_info { + int cpu_present:1; + int cpu_bsp:1; + int cpu_disabled:1; +}; + +struct xen_ipi_handler +{ + driver_filter_t *filter; + const char *description; +}; + +enum { + RESCHEDULE_VECTOR, + CALL_FUNCTION_VECTOR, +}; + +/*-------------------------------- Global Data -------------------------------*/ +static u_int hyperthreading_cpus; +static cpuset_t hyperthreading_cpus_mask; int mp_naps; /* # of Applications processors */ int boot_cpu_id = -1; /* designated BSP */ -extern struct pcpu __pcpu[]; - static int bootAP; static union descriptor *bootAPgdt; -static char resched_name[NR_CPUS][15]; -static char callfunc_name[NR_CPUS][15]; - /* Free these after use */ void *bootstacks[MAXCPU]; @@ -115,8 +150,6 @@ vm_offset_t smp_tlb_addr1; vm_offset_t smp_tlb_addr2; volatile int smp_tlb_wait; -typedef void call_data_func_t(uintptr_t , uintptr_t); - static u_int logical_cpus; static volatile cpuset_t ipi_nmi_pending; @@ -130,11 +163,7 @@ static volatile int aps_ready = 0; * Store data from cpu_add() until later in the boot when we actually setup * the APs. */ -struct cpu_info { - int cpu_present:1; - int cpu_bsp:1; - int cpu_disabled:1; -} static cpu_info[MAX_APIC_ID + 1]; +static struct cpu_info cpu_info[MAX_APIC_ID + 1]; int cpu_apic_ids[MAXCPU]; int apic_cpuids[MAX_APIC_ID + 1]; @@ -144,19 +173,17 @@ static volatile u_int cpu_ipi_pending[MAXCPU]; static int cpu_logical; static int cpu_cores; -static void assign_cpu_ids(void); -static void set_interrupt_apic_ids(void); -int start_all_aps(void); -static int start_ap(int apic_id); -static void release_aps(void *dummy); +static const struct xen_ipi_handler xen_ipis[] = +{ + [RESCHEDULE_VECTOR] = { smp_reschedule_interrupt, "resched" }, + [CALL_FUNCTION_VECTOR] = { smp_call_function_interrupt,"callfunc" } +}; -static u_int hyperthreading_cpus; -static cpuset_t hyperthreading_cpus_mask; - -extern void Xhypervisor_callback(void); -extern void failsafe_callback(void); -extern void pmap_lazyfix_action(void); +/*------------------------------- Per-CPU Data -------------------------------*/ +DPCPU_DEFINE(xen_intr_handle_t, ipi_handle[nitems(xen_ipis)]); +DPCPU_DEFINE(struct vcpu_info *, vcpu_info); +/*------------------------------ Implementation ------------------------------*/ struct cpu_group * cpu_topo(void) { @@ -355,12 +382,12 @@ iv_lazypmap(uintptr_t a, uintptr_t b) */ static call_data_func_t *ipi_vectors[6] = { - iv_rendezvous, - iv_invltlb, - iv_invlpg, - iv_invlrng, - iv_invlcache, - iv_lazypmap, + iv_rendezvous, + iv_invltlb, + iv_invlpg, + iv_invlrng, + iv_invlcache, + iv_lazypmap, }; /* @@ -414,10 +441,11 @@ smp_call_function_interrupt(void *unused) atomic_t *finished = &call_data->finished; /* We only handle function IPIs, not bitmap IPIs */ - if (call_data->func_id < APIC_IPI_INTS || call_data->func_id > IPI_BITMAP_VECTOR) + if (call_data->func_id < APIC_IPI_INTS || + call_data->func_id > IPI_BITMAP_VECTOR) panic("invalid function id %u", call_data->func_id); - func = ipi_vectors[call_data->func_id - APIC_IPI_INTS]; + func = ipi_vectors[IPI_TO_IDX(call_data->func_id)]; /* * Notify initiating CPU that I've grabbed the data and am * about to execute the function @@ -461,50 +489,46 @@ cpu_mp_announce(void) } static int -xen_smp_intr_init(unsigned int cpu) +xen_smp_cpu_init(unsigned int cpu) { - int rc; - unsigned int irq; - - per_cpu(resched_irq, cpu) = per_cpu(callfunc_irq, cpu) = -1; + xen_intr_handle_t *ipi_handle; + const struct xen_ipi_handler *ipi; + int idx, rc; - sprintf(resched_name[cpu], "resched%u", cpu); - rc = bind_ipi_to_irqhandler(RESCHEDULE_VECTOR, - cpu, - resched_name[cpu], - smp_reschedule_interrupt, - INTR_TYPE_TTY, &irq); + ipi_handle = DPCPU_ID_GET(cpu, ipi_handle); + for (ipi = xen_ipis, idx = 0; idx < nitems(xen_ipis); ipi++, idx++) { - printf("[XEN] IPI cpu=%d irq=%d vector=RESCHEDULE_VECTOR (%d)\n", - cpu, irq, RESCHEDULE_VECTOR); - - per_cpu(resched_irq, cpu) = irq; + /* + * The PCPU variable pc_device is not initialized on i386 PV, + * so we have to use the root_bus device in order to setup + * the IPIs. + */ + rc = xen_intr_alloc_and_bind_ipi(root_bus, cpu, + ipi->filter, INTR_TYPE_TTY, &ipi_handle[idx]); + if (rc != 0) { + printf("Unable to allocate a XEN IPI port. " + "Error %d\n", rc); + break; + } + xen_intr_describe(ipi_handle[idx], "%s", ipi->description); + } - sprintf(callfunc_name[cpu], "callfunc%u", cpu); - rc = bind_ipi_to_irqhandler(CALL_FUNCTION_VECTOR, - cpu, - callfunc_name[cpu], - smp_call_function_interrupt, - INTR_TYPE_TTY, &irq); - if (rc < 0) - goto fail; - per_cpu(callfunc_irq, cpu) = irq; + for (;idx < nitems(xen_ipis); idx++) + ipi_handle[idx] = NULL; - printf("[XEN] IPI cpu=%d irq=%d vector=CALL_FUNCTION_VECTOR (%d)\n", - cpu, irq, CALL_FUNCTION_VECTOR); + if (rc == 0) + return (0); - - if ((cpu != 0) && ((rc = ap_cpu_initclocks(cpu)) != 0)) - goto fail; + /* Either all are successfully mapped, or none at all. */ + for (idx = 0; idx < nitems(xen_ipis); idx++) { + if (ipi_handle[idx] == NULL) + continue; - return 0; + xen_intr_unbind(ipi_handle[idx]); + ipi_handle[idx] = NULL; + } - fail: - if (per_cpu(resched_irq, cpu) >= 0) - unbind_from_irqhandler(per_cpu(resched_irq, cpu)); - if (per_cpu(callfunc_irq, cpu) >= 0) - unbind_from_irqhandler(per_cpu(callfunc_irq, cpu)); - return rc; + return (rc); } static void @@ -513,7 +537,17 @@ xen_smp_intr_init_cpus(void *unused) int i; for (i = 0; i < mp_ncpus; i++) - xen_smp_intr_init(i); + xen_smp_cpu_init(i); +} + +static void +xen_smp_intr_setup_cpus(void *unused) +{ + int i; + + for (i = 0; i < mp_ncpus; i++) + DPCPU_ID_SET(i, vcpu_info, + &HYPERVISOR_shared_info->vcpu_info[i]); } #define MTOPSIZE (1<<(14 + PAGE_SHIFT)) @@ -746,7 +780,8 @@ start_all_aps(void) /* Get per-cpu data */ pc = &__pcpu[bootAP]; pcpu_init(pc, bootAP, sizeof(struct pcpu)); - dpcpu_init((void *)kmem_alloc(kernel_map, DPCPU_SIZE), bootAP); + dpcpu_init((void *)kmem_malloc(kernel_arena, DPCPU_SIZE, + M_WAITOK | M_ZERO), bootAP); pc->pc_apic_id = cpu_apic_ids[bootAP]; pc->pc_prvspace = pc; pc->pc_curthread = 0; @@ -787,7 +822,7 @@ start_all_aps(void) pmap_invalidate_range(kernel_pmap, 0, NKPT * NBPDR - 1); /* number of APs actually started */ - return mp_naps; + return (mp_naps); } extern uint8_t *pcpu_boot_stack; @@ -833,8 +868,8 @@ cpu_initialize_context(unsigned int cpu) pmap_zero_page(m[i]); } - boot_stack = kmem_alloc_nofault(kernel_map, PAGE_SIZE); - newPTD = kmem_alloc_nofault(kernel_map, NPGPTD * PAGE_SIZE); + boot_stack = kva_alloc(PAGE_SIZE); + newPTD = kva_alloc(NPGPTD * PAGE_SIZE); ma[0] = VM_PAGE_TO_MACH(m[0])|PG_V; #ifdef PAE @@ -856,7 +891,7 @@ cpu_initialize_context(unsigned int cpu) nkpt*sizeof(vm_paddr_t)); pmap_qremove(newPTD, 4); - kmem_free(kernel_map, newPTD, 4 * PAGE_SIZE); + kva_free(newPTD, 4 * PAGE_SIZE); /* * map actual idle stack to boot_stack */ @@ -892,7 +927,8 @@ cpu_initialize_context(unsigned int cpu) smp_trap_init(ctxt.trap_ctxt); ctxt.ldt_ents = 0; - ctxt.gdt_frames[0] = (uint32_t)((uint64_t)vtomach(bootAPgdt) >> PAGE_SHIFT); + ctxt.gdt_frames[0] = + (uint32_t)((uint64_t)vtomach(bootAPgdt) >> PAGE_SHIFT); ctxt.gdt_ents = 512; #ifdef __i386__ @@ -952,10 +988,17 @@ start_ap(int apic_id) /* Wait up to 5 seconds for it to start. */ for (ms = 0; ms < 5000; ms++) { if (mp_naps > cpus) - return 1; /* return SUCCESS */ + return (1); /* return SUCCESS */ DELAY(1000); } - return 0; /* return FAILURE */ + return (0); /* return FAILURE */ +} + +static void +ipi_pcpu(int cpu, u_int ipi) +{ + KASSERT((ipi <= nitems(xen_ipis)), ("invalid IPI")); + xen_intr_signal(DPCPU_ID_GET(cpu, ipi_handle[ipi])); } /* @@ -1011,7 +1054,8 @@ smp_tlb_shootdown(u_int vector, vm_offset_t addr1, vm_offset_t addr2) } static void -smp_targeted_tlb_shootdown(cpuset_t mask, u_int vector, vm_offset_t addr1, vm_offset_t addr2) +smp_targeted_tlb_shootdown(cpuset_t mask, u_int vector, vm_offset_t addr1, + vm_offset_t addr2) { int cpu, ncpu, othercpus; struct _call_data data; @@ -1245,5 +1289,5 @@ release_aps(void *dummy __unused) ia32_pause(); } SYSINIT(start_aps, SI_SUB_SMP, SI_ORDER_FIRST, release_aps, NULL); -SYSINIT(start_ipis, SI_SUB_INTR, SI_ORDER_ANY, xen_smp_intr_init_cpus, NULL); - +SYSINIT(start_ipis, SI_SUB_SMP, SI_ORDER_ANY, xen_smp_intr_init_cpus, NULL); +SYSINIT(start_cpu, SI_SUB_INTR, SI_ORDER_ANY, xen_smp_intr_setup_cpus, NULL); diff --git a/sys/i386/xen/mptable.c b/sys/i386/xen/mptable.c index 0c1efe849b4..74cb9ab1a51 100644 --- a/sys/i386/xen/mptable.c +++ b/sys/i386/xen/mptable.c @@ -40,7 +40,7 @@ __FBSDID("$FreeBSD$"); #include #include -#include +#include #include #include diff --git a/sys/i386/xen/pmap.c b/sys/i386/xen/pmap.c index 96988e2c52f..3abe7ef84e4 100644 --- a/sys/i386/xen/pmap.c +++ b/sys/i386/xen/pmap.c @@ -620,8 +620,7 @@ pmap_init(void) pv_entry_high_water = 9 * (pv_entry_max / 10); pv_maxchunks = MAX(pv_entry_max / _NPCPV, maxproc); - pv_chunkbase = (struct pv_chunk *)kmem_alloc_nofault(kernel_map, - PAGE_SIZE * pv_maxchunks); + pv_chunkbase = (struct pv_chunk *)kva_alloc(PAGE_SIZE * pv_maxchunks); if (pv_chunkbase == NULL) panic("pmap_init: not enough kvm for pv chunks"); pmap_ptelist_init(&pv_vafree, pv_chunkbase, pv_maxchunks); @@ -1453,15 +1452,12 @@ pmap_pinit(pmap_t pmap) mtx_lock(&createdelete_lock); #endif - PMAP_LOCK_INIT(pmap); - /* * No need to allocate page table space yet but we do need a valid * page directory table. */ if (pmap->pm_pdir == NULL) { - pmap->pm_pdir = (pd_entry_t *)kmem_alloc_nofault(kernel_map, - NBPTD); + pmap->pm_pdir = (pd_entry_t *)kva_alloc(NBPTD); if (pmap->pm_pdir == NULL) { PMAP_LOCK_DESTROY(pmap); #ifdef HAMFISTED_LOCKING @@ -1470,7 +1466,7 @@ pmap_pinit(pmap_t pmap) return (0); } #ifdef PAE - pmap->pm_pdpt = (pd_entry_t *)kmem_alloc_nofault(kernel_map, 1); + pmap->pm_pdpt = (pd_entry_t *)kva_alloc(1); #endif } @@ -1823,7 +1819,6 @@ pmap_release(pmap_t pmap) #ifdef PAE pmap_qremove((vm_offset_t)pmap->pm_pdpt, 1); #endif - PMAP_LOCK_DESTROY(pmap); #ifdef HAMFISTED_LOCKING mtx_unlock(&createdelete_lock); @@ -2667,7 +2662,7 @@ pmap_enter(pmap_t pmap, vm_offset_t va, vm_prot_t access, vm_page_t m, KASSERT(va < UPT_MIN_ADDRESS || va >= UPT_MAX_ADDRESS, ("pmap_enter: invalid to pmap_enter page table pages (va: 0x%x)", va)); - if ((m->oflags & (VPO_UNMANAGED | VPO_BUSY)) == 0) + if ((m->oflags & VPO_UNMANAGED) == 0 && !vm_page_xbusied(m)) VM_OBJECT_ASSERT_WLOCKED(m->object); mpte = NULL; @@ -3696,13 +3691,12 @@ pmap_is_modified(vm_page_t m) rv = FALSE; /* - * If the page is not VPO_BUSY, then PGA_WRITEABLE cannot be + * If the page is not exclusive busied, then PGA_WRITEABLE cannot be * concurrently set while the object is locked. Thus, if PGA_WRITEABLE * is clear, no PTEs can have PG_M set. */ VM_OBJECT_ASSERT_WLOCKED(m->object); - if ((m->oflags & VPO_BUSY) == 0 && - (m->aflags & PGA_WRITEABLE) == 0) + if (!vm_page_xbusied(m) && (m->aflags & PGA_WRITEABLE) == 0) return (rv); rw_wlock(&pvh_global_lock); sched_pin(); @@ -3827,13 +3821,12 @@ pmap_remove_write(vm_page_t m) ("pmap_remove_write: page %p is not managed", m)); /* - * If the page is not VPO_BUSY, then PGA_WRITEABLE cannot be set by - * another thread while the object is locked. Thus, if PGA_WRITEABLE - * is clear, no page table entries need updating. + * If the page is not exclusive busied, then PGA_WRITEABLE cannot be + * set by another thread while the object is locked. Thus, + * if PGA_WRITEABLE is clear, no page table entries need updating. */ VM_OBJECT_ASSERT_WLOCKED(m->object); - if ((m->oflags & VPO_BUSY) == 0 && - (m->aflags & PGA_WRITEABLE) == 0) + if (!vm_page_xbusied(m) && (m->aflags & PGA_WRITEABLE) == 0) return; rw_wlock(&pvh_global_lock); sched_pin(); @@ -3920,6 +3913,72 @@ pmap_ts_referenced(vm_page_t m) return (rtval); } +/* + * Apply the given advice to the specified range of addresses within the + * given pmap. Depending on the advice, clear the referenced and/or + * modified flags in each mapping and set the mapped page's dirty field. + */ +void +pmap_advise(pmap_t pmap, vm_offset_t sva, vm_offset_t eva, int advice) +{ + pd_entry_t oldpde; + pt_entry_t *pte; + vm_offset_t pdnxt; + vm_page_t m; + boolean_t anychanged; + + if (advice != MADV_DONTNEED && advice != MADV_FREE) + return; + anychanged = FALSE; + rw_wlock(&pvh_global_lock); + sched_pin(); + PMAP_LOCK(pmap); + for (; sva < eva; sva = pdnxt) { + pdnxt = (sva + NBPDR) & ~PDRMASK; + if (pdnxt < sva) + pdnxt = eva; + oldpde = pmap->pm_pdir[sva >> PDRSHIFT]; + if ((oldpde & (PG_PS | PG_V)) != PG_V) + continue; + if (pdnxt > eva) + pdnxt = eva; + for (pte = pmap_pte_quick(pmap, sva); sva != pdnxt; pte++, + sva += PAGE_SIZE) { + if ((*pte & (PG_MANAGED | PG_V)) != (PG_MANAGED | + PG_V)) + continue; + else if ((*pte & (PG_M | PG_RW)) == (PG_M | PG_RW)) { + if (advice == MADV_DONTNEED) { + /* + * Future calls to pmap_is_modified() + * can be avoided by making the page + * dirty now. + */ + m = PHYS_TO_VM_PAGE(xpmap_mtop(*pte) & + PG_FRAME); + vm_page_dirty(m); + } + PT_SET_VA_MA(pte, *pte & ~(PG_M | PG_A), TRUE); + } else if ((*pte & PG_A) != 0) + PT_SET_VA_MA(pte, *pte & ~PG_A, TRUE); + else + continue; + if ((*pte & PG_G) != 0) + pmap_invalidate_page(pmap, sva); + else + anychanged = TRUE; + } + } + PT_UPDATES_FLUSH(); + if (*PMAP1) + PT_SET_VA_MA(PMAP1, 0, TRUE); + if (anychanged) + pmap_invalidate_all(pmap); + sched_unpin(); + rw_wunlock(&pvh_global_lock); + PMAP_UNLOCK(pmap); +} + /* * Clear the modify bits on the specified physical page. */ @@ -3933,13 +3992,13 @@ pmap_clear_modify(vm_page_t m) KASSERT((m->oflags & VPO_UNMANAGED) == 0, ("pmap_clear_modify: page %p is not managed", m)); VM_OBJECT_ASSERT_WLOCKED(m->object); - KASSERT((m->oflags & VPO_BUSY) == 0, - ("pmap_clear_modify: page %p is busy", m)); + KASSERT(!vm_page_xbusied(m), + ("pmap_clear_modify: page %p is exclusive busied", m)); /* * If the page is not PGA_WRITEABLE, then no PTEs can have PG_M set. * If the object containing the page is locked and the page is not - * VPO_BUSY, then PGA_WRITEABLE cannot be concurrently set. + * exclusive busied, then PGA_WRITEABLE cannot be concurrently set. */ if ((m->aflags & PGA_WRITEABLE) == 0) return; @@ -4022,7 +4081,7 @@ pmap_mapdev_attr(vm_paddr_t pa, vm_size_t size, int mode) if (pa < KERNLOAD && pa + size <= KERNLOAD) va = KERNBASE + pa; else - va = kmem_alloc_nofault(kernel_map, size); + va = kva_alloc(size); if (!va) panic("pmap_mapdev: Couldn't alloc kernel virtual memory"); @@ -4057,7 +4116,7 @@ pmap_unmapdev(vm_offset_t va, vm_size_t size) base = trunc_page(va); offset = va & PAGE_MASK; size = round_page(offset + size); - kmem_free(kernel_map, base, size); + kva_free(base, size); } /* diff --git a/sys/i386/xen/xen_clock_util.c b/sys/i386/xen/xen_clock_util.c deleted file mode 100644 index c14a627a122..00000000000 --- a/sys/i386/xen/xen_clock_util.c +++ /dev/null @@ -1,101 +0,0 @@ -/*- - * Copyright (c) 2009 Adrian Chadd - * 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. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. - * - * $FreeBSD$ - */ - -#include -__FBSDID("$FreeBSD$"); - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -/* - * Read the current hypervisor start time (wall clock) from Xen. - */ -void -xen_fetch_wallclock(struct timespec *ts) -{ - shared_info_t *s = HYPERVISOR_shared_info; - uint32_t ts_version; - - do { - ts_version = s->wc_version; - rmb(); - ts->tv_sec = s->wc_sec; - ts->tv_nsec = s->wc_nsec; - rmb(); - } - while ((s->wc_version & 1) | (ts_version ^ s->wc_version)); -} - -/* - * Read the current hypervisor system uptime value from Xen. - */ -void -xen_fetch_uptime(struct timespec *ts) -{ - shared_info_t *s = HYPERVISOR_shared_info; - struct vcpu_time_info *src; - struct shadow_time_info dst; - uint32_t pre_version, post_version; - - src = &s->vcpu_info[smp_processor_id()].time; - - spinlock_enter(); - do { - pre_version = dst.version = src->version; - rmb(); - dst.system_timestamp = src->system_time; - rmb(); - post_version = src->version; - } - while ((pre_version & 1) | (pre_version ^ post_version)); - - spinlock_exit(); - - ts->tv_sec = dst.system_timestamp / 1000000000; - ts->tv_nsec = dst.system_timestamp % 1000000000; -} diff --git a/sys/i386/xen/xen_machdep.c b/sys/i386/xen/xen_machdep.c index 9b5edd384cf..7049be6d561 100644 --- a/sys/i386/xen/xen_machdep.c +++ b/sys/i386/xen/xen_machdep.c @@ -47,7 +47,7 @@ __FBSDID("$FreeBSD$"); #include #include -#include +#include #include #include @@ -96,6 +96,8 @@ xen_pfn_t *xen_pfn_to_mfn_frame_list[16]; xen_pfn_t *xen_pfn_to_mfn_frame_list_list; int preemptable, init_first; extern unsigned int avail_space; +int xen_vector_callback_enabled = 0; +enum xen_domain_type xen_domain_type = XEN_PV_DOMAIN; void ni_cli(void); void ni_sti(void); @@ -129,6 +131,12 @@ ni_sti(void) ); } +void +force_evtchn_callback(void) +{ + (void)HYPERVISOR_xen_version(0, NULL); +} + /* * Modify the cmd_line by converting ',' to NULLs so that it is in a format * suitable for the static env vars. @@ -141,7 +149,7 @@ xen_setbootenv(char *cmd_line) /* Skip leading spaces */ for (; *cmd_line == ' '; cmd_line++); - printk("xen_setbootenv(): cmd_line='%s'\n", cmd_line); + xc_printf("xen_setbootenv(): cmd_line='%s'\n", cmd_line); for (cmd_line_next = cmd_line; strsep(&cmd_line_next, ",") != NULL;); return cmd_line; @@ -177,16 +185,16 @@ xen_boothowto(char *envp) return howto; } -#define PRINTK_BUFSIZE 1024 +#define XC_PRINTF_BUFSIZE 1024 void -printk(const char *fmt, ...) +xc_printf(const char *fmt, ...) { __va_list ap; int retval; - static char buf[PRINTK_BUFSIZE]; + static char buf[XC_PRINTF_BUFSIZE]; va_start(ap, fmt); - retval = vsnprintf(buf, PRINTK_BUFSIZE - 1, fmt, ap); + retval = vsnprintf(buf, XC_PRINTF_BUFSIZE - 1, fmt, ap); va_end(ap); buf[retval] = 0; (void)HYPERVISOR_console_write(buf, retval); @@ -239,9 +247,10 @@ xen_dump_queue(void) if (_xpq_idx <= 1) return; - printk("xen_dump_queue(): %u entries\n", _xpq_idx); + xc_printf("xen_dump_queue(): %u entries\n", _xpq_idx); for (i = 0; i < _xpq_idx; i++) { - printk(" val: %llx ptr: %llx\n", XPQ_QUEUE[i].val, XPQ_QUEUE[i].ptr); + xc_printf(" val: %llx ptr: %llx\n", XPQ_QUEUE[i].val, + XPQ_QUEUE[i].ptr); } } #endif @@ -955,9 +964,10 @@ initvalues(start_info_t *startinfo) cur_space = xen_start_info->pt_base + (l3_pages + l2_pages + l1_pages + 1)*PAGE_SIZE; - printk("initvalues(): wooh - availmem=%x,%x\n", avail_space, cur_space); + xc_printf("initvalues(): wooh - availmem=%x,%x\n", avail_space, + cur_space); - printk("KERNBASE=%x,pt_base=%x, VTOPFN(base)=%x, nr_pt_frames=%x\n", + xc_printf("KERNBASE=%x,pt_base=%x, VTOPFN(base)=%x, nr_pt_frames=%x\n", KERNBASE,xen_start_info->pt_base, VTOPFN(xen_start_info->pt_base), xen_start_info->nr_pt_frames); xendebug_flags = 0; /* 0xffffffff; */ @@ -1007,7 +1017,7 @@ initvalues(start_info_t *startinfo) /* Map proc0's KSTACK */ proc0kstack = cur_space; cur_space += (KSTACK_PAGES * PAGE_SIZE); - printk("proc0kstack=%u\n", proc0kstack); + xc_printf("proc0kstack=%u\n", proc0kstack); /* vm86/bios stack */ cur_space += PAGE_SIZE; @@ -1106,18 +1116,18 @@ initvalues(start_info_t *startinfo) shinfo = xen_start_info->shared_info; PT_SET_MA(HYPERVISOR_shared_info, shinfo | PG_KERNEL); - printk("#4\n"); + xc_printf("#4\n"); xen_store_ma = (((vm_paddr_t)xen_start_info->store_mfn) << PAGE_SHIFT); PT_SET_MA(xen_store, xen_store_ma | PG_KERNEL); console_page_ma = (((vm_paddr_t)xen_start_info->console.domU.mfn) << PAGE_SHIFT); PT_SET_MA(console_page, console_page_ma | PG_KERNEL); - printk("#5\n"); + xc_printf("#5\n"); set_iopl.iopl = 1; PANIC_IF(HYPERVISOR_physdev_op(PHYSDEVOP_SET_IOPL, &set_iopl)); - printk("#6\n"); + xc_printf("#6\n"); #if 0 /* add page table for KERNBASE */ xen_queue_pt_update(IdlePTDma + KPTDI*sizeof(vm_paddr_t), @@ -1132,7 +1142,7 @@ initvalues(start_info_t *startinfo) #endif xen_flush_queue(); cur_space += PAGE_SIZE; - printk("#6\n"); + xc_printf("#6\n"); #endif /* 0 */ #ifdef notyet if (xen_start_info->flags & SIF_INITDOMAIN) { @@ -1150,13 +1160,13 @@ initvalues(start_info_t *startinfo) i < (((vm_offset_t)&etext) & ~PAGE_MASK); i += PAGE_SIZE) PT_SET_MA(i, VTOM(i) | PG_V | PG_A); - printk("#7\n"); + xc_printf("#7\n"); physfree = VTOP(cur_space); init_first = physfree >> PAGE_SHIFT; IdlePTD = (pd_entry_t *)VTOP(IdlePTD); IdlePDPT = (pd_entry_t *)VTOP(IdlePDPT); setup_xen_features(); - printk("#8, proc0kstack=%u\n", proc0kstack); + xc_printf("#8, proc0kstack=%u\n", proc0kstack); } @@ -1200,9 +1210,9 @@ HYPERVISOR_multicall(struct multicall_entry * call_list, int nr_calls) /* Check the results of individual hypercalls. */ for (i = 0; i < nr_calls; i++) - if (unlikely(call_list[i].result < 0)) + if (__predict_false(call_list[i].result < 0)) ret++; - if (unlikely(ret > 0)) + if (__predict_false(ret > 0)) panic("%d multicall(s) failed: cpu %d\n", ret, smp_processor_id()); diff --git a/sys/i386/xen/xen_rtc.c b/sys/i386/xen/xen_rtc.c deleted file mode 100644 index 8e1e0175959..00000000000 --- a/sys/i386/xen/xen_rtc.c +++ /dev/null @@ -1,144 +0,0 @@ -/*- - * Copyright (c) 2009 Adrian Chadd - * 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. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. - * - * $FreeBSD$ - */ - -#include -__FBSDID("$FreeBSD$"); - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "clock_if.h" - -static int -xen_rtc_probe(device_t dev) -{ - device_set_desc(dev, "Xen Hypervisor Clock"); - printf("[XEN] xen_rtc_probe: probing Hypervisor RTC clock\n"); - if (! HYPERVISOR_shared_info) { - device_printf(dev, "No hypervisor shared page found; RTC can not start.\n"); - return (EINVAL); - } - return (0); -} - -static int -xen_rtc_attach(device_t dev) -{ - printf("[XEN] xen_rtc_attach: attaching Hypervisor RTC clock\n"); - clock_register(dev, 1000000); - return(0); -} - -static int -xen_rtc_settime(device_t dev __unused, struct timespec *ts) -{ - device_printf(dev, "[XEN] xen_rtc_settime\n"); - /* - * Don't return EINVAL here; just silently fail if the domain isn't privileged enough - * to set the TOD. - */ - return(0); -} - -/* - * The Xen time structures document the hypervisor start time and the - * uptime-since-hypervisor-start (in nsec.) They need to be combined - * in order to calculate a TOD clock. - */ -static int -xen_rtc_gettime(device_t dev, struct timespec *ts) -{ - struct timespec w_ts, u_ts; - - device_printf(dev, "[XEN] xen_rtc_gettime\n"); - xen_fetch_wallclock(&w_ts); - device_printf(dev, "[XEN] xen_rtc_gettime: wallclock %ld sec; %ld nsec\n", (long int) w_ts.tv_sec, (long int) w_ts.tv_nsec); - xen_fetch_uptime(&u_ts); - device_printf(dev, "[XEN] xen_rtc_gettime: uptime %ld sec; %ld nsec\n", (long int) u_ts.tv_sec, (long int) u_ts.tv_nsec); - - timespecclear(ts); - timespecadd(ts, &w_ts); - timespecadd(ts, &u_ts); - - device_printf(dev, "[XEN] xen_rtc_gettime: TOD %ld sec; %ld nsec\n", (long int) ts->tv_sec, (long int) ts->tv_nsec); - - return(0); -} - -static void -xen_rtc_identify(driver_t *drv, device_t parent) -{ - BUS_ADD_CHILD(parent, 0, "rtc", 0); -} - -static device_method_t xen_rtc_methods[] = { - /* Device interface */ - DEVMETHOD(device_probe, xen_rtc_probe), - DEVMETHOD(device_attach, xen_rtc_attach), - DEVMETHOD(device_identify, xen_rtc_identify), - - DEVMETHOD(device_detach, bus_generic_detach), - DEVMETHOD(device_shutdown, bus_generic_shutdown), - - /* clock interface */ - DEVMETHOD(clock_gettime, xen_rtc_gettime), - DEVMETHOD(clock_settime, xen_rtc_settime), - - { 0, 0 } -}; - - -static driver_t xen_rtc_driver = { - "rtc", - xen_rtc_methods, - 0 -}; - -static devclass_t xen_rtc_devclass; - -DRIVER_MODULE(rtc, nexus, xen_rtc_driver, xen_rtc_devclass, 0, 0); diff --git a/sys/ia64/conf/GENERIC b/sys/ia64/conf/GENERIC index cb516172ba0..553c5d1fcef 100644 --- a/sys/ia64/conf/GENERIC +++ b/sys/ia64/conf/GENERIC @@ -26,8 +26,8 @@ ident GENERIC makeoptions DEBUG=-g # Build kernel with debug information. options AUDIT # Security event auditing -options CAPABILITY_MODE # Capsicum capability mode -options CAPABILITIES # Capsicum capabilities +options CAPABILITY_MODE # Capsicum capability mode +options CAPABILITIES # Capsicum capabilities options CD9660 # ISO 9660 Filesystem options COMPAT_FREEBSD7 # Compatible with FreeBSD7 options FFS # Berkeley Fast Filesystem @@ -46,6 +46,7 @@ options NFS_ROOT # NFS usable as root device options P1003_1B_SEMAPHORES # POSIX-style semaphores options PREEMPTION # Enable kernel thread preemption options PRINTF_BUFR_SIZE=128 # Printf buffering to limit interspersion +options PROCDESC # Support for process descriptors options PROCFS # Process filesystem (/proc) options PSEUDOFS # Pseudo-filesystem framework options SCHED_ULE # ULE scheduler @@ -65,9 +66,8 @@ options _KPOSIX_PRIORITY_SCHEDULING # Posix P1003_1B RT extensions # Debugging support. Always need this: options KDB # Enable kernel debugger support. -# For minimum debugger support (stable branch) use: -#options KDB_TRACE # Print a stack trace for a panic. -# For full debugger support use this instead: +options KDB_TRACE # Print a stack trace for a panic. +# For full debugger support use (turn off in stable branch): options DDB # Support DDB options GDB # Support remote GDB options DEADLKRES # Enable the deadlock resolver diff --git a/sys/ia64/ia32/ia32_signal.c b/sys/ia64/ia32/ia32_signal.c index 695a04bc1f6..d17f0604928 100644 --- a/sys/ia64/ia32/ia32_signal.c +++ b/sys/ia64/ia32/ia32_signal.c @@ -169,8 +169,8 @@ ia32_setregs(struct thread *td, struct image_params *imgp, u_long stack) * Build the GDT and LDT. */ gdt = sv->sv_usrstack; - vm_map_find(&vmspace->vm_map, 0, 0, &gdt, IA32_PAGE_SIZE << 1, 0, - VM_PROT_ALL, VM_PROT_ALL, 0); + vm_map_find(&vmspace->vm_map, NULL, 0, &gdt, IA32_PAGE_SIZE << 1, 0, + VMFS_NO_SPACE, VM_PROT_ALL, VM_PROT_ALL, 0); ldt = gdt + IA32_PAGE_SIZE; desc.sd_lolimit = 8*NLDT-1; diff --git a/sys/ia64/ia64/mp_machdep.c b/sys/ia64/ia64/mp_machdep.c index 8f924600c41..8e71b35e00a 100644 --- a/sys/ia64/ia64/mp_machdep.c +++ b/sys/ia64/ia64/mp_machdep.c @@ -304,7 +304,8 @@ cpu_mp_add(u_int acpi_id, u_int id, u_int eid) if (cpuid != 0) { pc = (struct pcpu *)malloc(sizeof(*pc), M_SMP, M_WAITOK); pcpu_init(pc, cpuid, sizeof(*pc)); - dpcpu = (void *)kmem_alloc(kernel_map, DPCPU_SIZE); + dpcpu = (void *)kmem_malloc(kernel_arena, DPCPU_SIZE, + M_WAITOK | M_ZERO); dpcpu_init(dpcpu, cpuid); } else pc = pcpup; diff --git a/sys/ia64/ia64/pmap.c b/sys/ia64/ia64/pmap.c index 44181848afe..442149f588b 100644 --- a/sys/ia64/ia64/pmap.c +++ b/sys/ia64/ia64/pmap.c @@ -622,6 +622,8 @@ pmap_free_rid(uint32_t rid) void pmap_pinit0(struct pmap *pmap) { + + PMAP_LOCK_INIT(pmap); /* kernel_pmap is the same as any other pmap. */ pmap_pinit(pmap); } @@ -635,7 +637,6 @@ pmap_pinit(struct pmap *pmap) { int i; - PMAP_LOCK_INIT(pmap); for (i = 0; i < IA64_VM_MINKERN_REGION; i++) pmap->pm_rid[i] = pmap_allocate_rid(); TAILQ_INIT(&pmap->pm_pvchunk); @@ -660,7 +661,6 @@ pmap_release(pmap_t pmap) for (i = 0; i < IA64_VM_MINKERN_REGION; i++) if (pmap->pm_rid[i]) pmap_free_rid(pmap->pm_rid[i]); - PMAP_LOCK_DESTROY(pmap); } /* @@ -1677,7 +1677,7 @@ pmap_enter(pmap_t pmap, vm_offset_t va, vm_prot_t access, vm_page_t m, va &= ~PAGE_MASK; KASSERT(va <= VM_MAX_KERNEL_ADDRESS, ("pmap_enter: toobig")); - KASSERT((m->oflags & (VPO_UNMANAGED | VPO_BUSY)) != 0, + KASSERT((m->oflags & VPO_UNMANAGED) != 0 || vm_page_xbusied(m), ("pmap_enter: page %p is not busy", m)); /* @@ -2234,13 +2234,12 @@ pmap_is_modified(vm_page_t m) rv = FALSE; /* - * If the page is not VPO_BUSY, then PGA_WRITEABLE cannot be + * If the page is not exclusive busied, then PGA_WRITEABLE cannot be * concurrently set while the object is locked. Thus, if PGA_WRITEABLE * is clear, no PTEs can be dirty. */ VM_OBJECT_ASSERT_WLOCKED(m->object); - if ((m->oflags & VPO_BUSY) == 0 && - (m->aflags & PGA_WRITEABLE) == 0) + if (!vm_page_xbusied(m) && (m->aflags & PGA_WRITEABLE) == 0) return (rv); rw_wlock(&pvh_global_lock); TAILQ_FOREACH(pv, &m->md.pv_list, pv_list) { @@ -2310,6 +2309,50 @@ pmap_is_referenced(vm_page_t m) return (rv); } +/* + * Apply the given advice to the specified range of addresses within the + * given pmap. Depending on the advice, clear the referenced and/or + * modified flags in each mapping and set the mapped page's dirty field. + */ +void +pmap_advise(pmap_t pmap, vm_offset_t sva, vm_offset_t eva, int advice) +{ + struct ia64_lpte *pte; + pmap_t oldpmap; + vm_page_t m; + + PMAP_LOCK(pmap); + oldpmap = pmap_switch(pmap); + for (; sva < eva; sva += PAGE_SIZE) { + /* If page is invalid, skip this page. */ + pte = pmap_find_vhpt(sva); + if (pte == NULL) + continue; + + /* If it isn't managed, skip it too. */ + if (!pmap_managed(pte)) + continue; + + /* Clear its modified and referenced bits. */ + if (pmap_dirty(pte)) { + if (advice == MADV_DONTNEED) { + /* + * Future calls to pmap_is_modified() can be + * avoided by making the page dirty now. + */ + m = PHYS_TO_VM_PAGE(pmap_ppn(pte)); + vm_page_dirty(m); + } + pmap_clear_dirty(pte); + } else if (!pmap_accessed(pte)) + continue; + pmap_clear_accessed(pte); + pmap_invalidate_page(sva); + } + pmap_switch(oldpmap); + PMAP_UNLOCK(pmap); +} + /* * Clear the modify bits on the specified physical page. */ @@ -2323,13 +2366,13 @@ pmap_clear_modify(vm_page_t m) KASSERT((m->oflags & VPO_UNMANAGED) == 0, ("pmap_clear_modify: page %p is not managed", m)); VM_OBJECT_ASSERT_WLOCKED(m->object); - KASSERT((m->oflags & VPO_BUSY) == 0, - ("pmap_clear_modify: page %p is busy", m)); + KASSERT(!vm_page_xbusied(m), + ("pmap_clear_modify: page %p is exclusive busied", m)); /* * If the page is not PGA_WRITEABLE, then no PTEs can be modified. * If the object containing the page is locked and the page is not - * VPO_BUSY, then PGA_WRITEABLE cannot be concurrently set. + * exclusive busied, then PGA_WRITEABLE cannot be concurrently set. */ if ((m->aflags & PGA_WRITEABLE) == 0) return; @@ -2396,13 +2439,12 @@ pmap_remove_write(vm_page_t m) ("pmap_remove_write: page %p is not managed", m)); /* - * If the page is not VPO_BUSY, then PGA_WRITEABLE cannot be set by - * another thread while the object is locked. Thus, if PGA_WRITEABLE - * is clear, no page table entries need updating. + * If the page is not exclusive busied, then PGA_WRITEABLE cannot be + * set by another thread while the object is locked. Thus, + * if PGA_WRITEABLE is clear, no page table entries need updating. */ VM_OBJECT_ASSERT_WLOCKED(m->object); - if ((m->oflags & VPO_BUSY) == 0 && - (m->aflags & PGA_WRITEABLE) == 0) + if (!vm_page_xbusied(m) && (m->aflags & PGA_WRITEABLE) == 0) return; rw_wlock(&pvh_global_lock); TAILQ_FOREACH(pv, &m->md.pv_list, pv_list) { diff --git a/sys/ia64/ia64/vm_machdep.c b/sys/ia64/ia64/vm_machdep.c index 09987fd7b77..186897ba855 100644 --- a/sys/ia64/ia64/vm_machdep.c +++ b/sys/ia64/ia64/vm_machdep.c @@ -79,7 +79,6 @@ #include #include #include -#include #include #include @@ -352,27 +351,6 @@ cpu_exit(struct thread *td) { } -/* - * Allocate an sf_buf for the given vm_page. On this machine, however, there - * is no sf_buf object. Instead, an opaque pointer to the given vm_page is - * returned. - */ -struct sf_buf * -sf_buf_alloc(struct vm_page *m, int pri) -{ - - return ((struct sf_buf *)m); -} - -/* - * Free the sf_buf. In fact, do nothing because there are no resources - * associated with the sf_buf. - */ -void -sf_buf_free(struct sf_buf *sf) -{ -} - /* * Software interrupt handler for queued VM system processing. */ diff --git a/sys/ia64/include/acpica_machdep.h b/sys/ia64/include/acpica_machdep.h index f64ab8f6c4b..2f861e847b3 100644 --- a/sys/ia64/include/acpica_machdep.h +++ b/sys/ia64/include/acpica_machdep.h @@ -60,9 +60,9 @@ #define ACPI_FLUSH_CPU_CACHE() /* XXX ia64_fc()? */ -/* Section 5.2.9.1: global lock acquire/release functions */ -extern int acpi_acquire_global_lock(uint32_t *lock); -extern int acpi_release_global_lock(uint32_t *lock); +/* Section 5.2.10.1: global lock acquire/release functions */ +int acpi_acquire_global_lock(volatile uint32_t *); +int acpi_release_global_lock(volatile uint32_t *); #define ACPI_ACQUIRE_GLOBAL_LOCK(GLptr, Acq) do { \ (Acq) = acpi_acquire_global_lock(&((GLptr)->GlobalLock)); \ } while (0) diff --git a/sys/ia64/include/sf_buf.h b/sys/ia64/include/sf_buf.h index 75bcdfa0dcd..44d0109b245 100644 --- a/sys/ia64/include/sf_buf.h +++ b/sys/ia64/include/sf_buf.h @@ -41,6 +41,18 @@ */ struct sf_buf; +static inline struct sf_buf * +sf_buf_alloc(struct vm_page *m, int pri) +{ + + return ((struct sf_buf *)m); +} + +static inline void +sf_buf_free(struct sf_buf *sf) +{ +} + static __inline vm_page_t sf_buf_page(struct sf_buf *sf) { diff --git a/sys/kern/capabilities.conf b/sys/kern/capabilities.conf index d2fa51c4ef8..7f68668a542 100644 --- a/sys/kern/capabilities.conf +++ b/sys/kern/capabilities.conf @@ -114,15 +114,14 @@ cap_fcntls_limit cap_getmode cap_ioctls_get cap_ioctls_limit -cap_new -cap_rights_get +__cap_rights_get cap_rights_limit ## ## Allow read-only clock operations. ## -clock_gettime clock_getres +clock_gettime ## ## Always allow file descriptor close(2). diff --git a/sys/kern/imgact_elf.c b/sys/kern/imgact_elf.c index 61a2aefe4e0..f6da68e6fea 100644 --- a/sys/kern/imgact_elf.c +++ b/sys/kern/imgact_elf.c @@ -417,8 +417,9 @@ __elfN(map_insert)(vm_map_t map, vm_object_t object, vm_ooffset_t offset, * The mapping is not page aligned. This means we have * to copy the data. Sigh. */ - rv = vm_map_find(map, NULL, 0, &start, end - start, - FALSE, prot | VM_PROT_WRITE, VM_PROT_ALL, 0); + rv = vm_map_find(map, NULL, 0, &start, end - start, 0, + VMFS_NO_SPACE, prot | VM_PROT_WRITE, VM_PROT_ALL, + 0); if (rv) return (rv); if (object == NULL) diff --git a/sys/kern/imgact_gzip.c b/sys/kern/imgact_gzip.c index 7c48ac66a0c..ab77a885524 100644 --- a/sys/kern/imgact_gzip.c +++ b/sys/kern/imgact_gzip.c @@ -137,7 +137,7 @@ exec_gzip_imgact(imgp) } if (igz.inbuf) - kmem_free_wakeup(exec_map, (vm_offset_t)igz.inbuf, PAGE_SIZE); + kmap_free_wakeup(exec_map, (vm_offset_t)igz.inbuf, PAGE_SIZE); if (igz.error || error) { printf("Output=%lu ", igz.output); printf("Inflate_error=%d igz.error=%d where=%d\n", @@ -269,12 +269,9 @@ do_aout_hdr(struct imgact_gzip * gz) */ vmaddr = gz->virtual_offset + gz->a_out.a_text + gz->a_out.a_data; - error = vm_map_find(&vmspace->vm_map, - NULL, - 0, - &vmaddr, - gz->bss_size, - FALSE, VM_PROT_ALL, VM_PROT_ALL, 0); + error = vm_map_find(&vmspace->vm_map, NULL, 0, &vmaddr, + gz->bss_size, 0, VMFS_NO_SPACE, VM_PROT_ALL, VM_PROT_ALL, + 0); if (error) { gz->where = __LINE__; return (error); @@ -310,7 +307,7 @@ NextByte(void *vp) return igz->inbuf[(igz->idx++) - igz->offset]; } if (igz->inbuf) - kmem_free_wakeup(exec_map, (vm_offset_t)igz->inbuf, PAGE_SIZE); + kmap_free_wakeup(exec_map, (vm_offset_t)igz->inbuf, PAGE_SIZE); igz->offset = igz->idx & ~PAGE_MASK; error = vm_mmap(exec_map, /* map */ diff --git a/sys/kern/init_main.c b/sys/kern/init_main.c index 1eb364745a1..0828e4803f9 100644 --- a/sys/kern/init_main.c +++ b/sys/kern/init_main.c @@ -455,15 +455,6 @@ proc0_init(void *dummy __unused) * Add scheduler specific parts to proc, thread as needed. */ schedinit(); /* scheduler gets its house in order */ - /* - * Initialize sleep queue hash table - */ - sleepinit(); - - /* - * additional VM structures - */ - vm_init2(); /* * Create process 0 (the swapper). @@ -718,8 +709,8 @@ start_init(void *dummy) * Need just enough stack to hold the faked-up "execve()" arguments. */ addr = p->p_sysent->sv_usrstack - PAGE_SIZE; - if (vm_map_find(&p->p_vmspace->vm_map, NULL, 0, &addr, PAGE_SIZE, - FALSE, VM_PROT_ALL, VM_PROT_ALL, 0) != 0) + if (vm_map_find(&p->p_vmspace->vm_map, NULL, 0, &addr, PAGE_SIZE, 0, + VMFS_NO_SPACE, VM_PROT_ALL, VM_PROT_ALL, 0) != 0) panic("init: couldn't allocate argument space"); p->p_vmspace->vm_maxsaddr = (caddr_t)addr; p->p_vmspace->vm_ssize = 1; diff --git a/sys/kern/init_sysent.c b/sys/kern/init_sysent.c index 0fcb9dfcf75..261b659cfcb 100644 --- a/sys/kern/init_sysent.c +++ b/sys/kern/init_sysent.c @@ -3,7 +3,7 @@ * * DO NOT EDIT-- this file is automatically generated. * $FreeBSD$ - * created from FreeBSD: head/sys/kern/syscalls.master 251526 2013-06-08 13:27:57Z glebius + * created from FreeBSD: head/sys/kern/syscalls.master 255490 2013-09-12 17:52:18Z jhb */ #include "opt_compat.h" @@ -548,8 +548,8 @@ struct sysent sysent[] = { { AS(msgctl_args), (sy_call_t *)lkmressys, AUE_NULL, NULL, 0, 0, 0, SY_THR_ABSENT }, /* 511 = msgctl */ { AS(shmctl_args), (sy_call_t *)lkmressys, AUE_NULL, NULL, 0, 0, 0, SY_THR_ABSENT }, /* 512 = shmctl */ { AS(lpathconf_args), (sy_call_t *)sys_lpathconf, AUE_LPATHCONF, NULL, 0, 0, 0, SY_THR_STATIC }, /* 513 = lpathconf */ - { AS(cap_new_args), (sy_call_t *)sys_cap_new, AUE_CAP_NEW, NULL, 0, 0, SYF_CAPENABLED, SY_THR_STATIC }, /* 514 = cap_new */ - { AS(cap_rights_get_args), (sy_call_t *)sys_cap_rights_get, AUE_CAP_RIGHTS_GET, NULL, 0, 0, SYF_CAPENABLED, SY_THR_STATIC }, /* 515 = cap_rights_get */ + { 0, (sy_call_t *)nosys, AUE_NULL, NULL, 0, 0, 0, SY_THR_ABSENT }, /* 514 = obsolete cap_new */ + { AS(__cap_rights_get_args), (sy_call_t *)sys___cap_rights_get, AUE_CAP_RIGHTS_GET, NULL, 0, 0, SYF_CAPENABLED, SY_THR_STATIC }, /* 515 = __cap_rights_get */ { 0, (sy_call_t *)sys_cap_enter, AUE_CAP_ENTER, NULL, 0, 0, SYF_CAPENABLED, SY_THR_STATIC }, /* 516 = cap_enter */ { AS(cap_getmode_args), (sy_call_t *)sys_cap_getmode, AUE_CAP_GETMODE, NULL, 0, 0, SYF_CAPENABLED, SY_THR_STATIC }, /* 517 = cap_getmode */ { AS(pdfork_args), (sy_call_t *)sys_pdfork, AUE_PDFORK, NULL, 0, 0, SYF_CAPENABLED, SY_THR_STATIC }, /* 518 = pdfork */ diff --git a/sys/kern/kern_descrip.c b/sys/kern/kern_descrip.c index fb264ba3935..9e9010f0f62 100644 --- a/sys/kern/kern_descrip.c +++ b/sys/kern/kern_descrip.c @@ -455,6 +455,7 @@ kern_fcntl(struct thread *td, int fd, int cmd, intptr_t arg) struct filedescent *fde; struct proc *p; struct vnode *vp; + cap_rights_t rights; int error, flg, tmp; u_int old, new; uint64_t bsize; @@ -515,7 +516,8 @@ kern_fcntl(struct thread *td, int fd, int cmd, intptr_t arg) break; case F_GETFL: - error = fget_unlocked(fdp, fd, CAP_FCNTL, F_GETFL, &fp, NULL); + error = fget_unlocked(fdp, fd, + cap_rights_init(&rights, CAP_FCNTL), F_GETFL, &fp, NULL); if (error != 0) break; td->td_retval[0] = OFLAGS(fp->f_flag); @@ -523,7 +525,8 @@ kern_fcntl(struct thread *td, int fd, int cmd, intptr_t arg) break; case F_SETFL: - error = fget_unlocked(fdp, fd, CAP_FCNTL, F_SETFL, &fp, NULL); + error = fget_unlocked(fdp, fd, + cap_rights_init(&rights, CAP_FCNTL), F_SETFL, &fp, NULL); if (error != 0) break; do { @@ -550,7 +553,8 @@ kern_fcntl(struct thread *td, int fd, int cmd, intptr_t arg) break; case F_GETOWN: - error = fget_unlocked(fdp, fd, CAP_FCNTL, F_GETOWN, &fp, NULL); + error = fget_unlocked(fdp, fd, + cap_rights_init(&rights, CAP_FCNTL), F_GETOWN, &fp, NULL); if (error != 0) break; error = fo_ioctl(fp, FIOGETOWN, &tmp, td->td_ucred, td); @@ -560,7 +564,8 @@ kern_fcntl(struct thread *td, int fd, int cmd, intptr_t arg) break; case F_SETOWN: - error = fget_unlocked(fdp, fd, CAP_FCNTL, F_SETOWN, &fp, NULL); + error = fget_unlocked(fdp, fd, + cap_rights_init(&rights, CAP_FCNTL), F_SETOWN, &fp, NULL); if (error != 0) break; tmp = arg; @@ -581,7 +586,8 @@ kern_fcntl(struct thread *td, int fd, int cmd, intptr_t arg) case F_SETLK: do_setlk: - error = fget_unlocked(fdp, fd, CAP_FLOCK, 0, &fp, NULL); + cap_rights_init(&rights, CAP_FLOCK); + error = fget_unlocked(fdp, fd, &rights, 0, &fp, NULL); if (error != 0) break; if (fp->f_type != DTYPE_VNODE) { @@ -670,7 +676,7 @@ kern_fcntl(struct thread *td, int fd, int cmd, intptr_t arg) * that the closing thread was a bit slower and that the * advisory lock succeeded before the close. */ - error = fget_unlocked(fdp, fd, 0, 0, &fp2, NULL); + error = fget_unlocked(fdp, fd, &rights, 0, &fp2, NULL); if (error != 0) { fdrop(fp, td); break; @@ -688,7 +694,8 @@ kern_fcntl(struct thread *td, int fd, int cmd, intptr_t arg) break; case F_GETLK: - error = fget_unlocked(fdp, fd, CAP_FLOCK, 0, &fp, NULL); + error = fget_unlocked(fdp, fd, + cap_rights_init(&rights, CAP_FLOCK), 0, &fp, NULL); if (error != 0) break; if (fp->f_type != DTYPE_VNODE) { @@ -726,7 +733,7 @@ kern_fcntl(struct thread *td, int fd, int cmd, intptr_t arg) arg = arg ? 128 * 1024: 0; /* FALLTHROUGH */ case F_READAHEAD: - error = fget_unlocked(fdp, fd, 0, 0, &fp, NULL); + error = fget_unlocked(fdp, fd, NULL, 0, &fp, NULL); if (error != 0) break; if (fp->f_type != DTYPE_VNODE) { @@ -1281,11 +1288,13 @@ int kern_fstat(struct thread *td, int fd, struct stat *sbp) { struct file *fp; + cap_rights_t rights; int error; AUDIT_ARG_FD(fd); - if ((error = fget(td, fd, CAP_FSTAT, &fp)) != 0) + error = fget(td, fd, cap_rights_init(&rights, CAP_FSTAT), &fp); + if (error != 0) return (error); AUDIT_ARG_FILE(td->td_proc, fp); @@ -1339,9 +1348,11 @@ sys_fpathconf(struct thread *td, struct fpathconf_args *uap) { struct file *fp; struct vnode *vp; + cap_rights_t rights; int error; - if ((error = fget(td, uap->fd, CAP_FPATHCONF, &fp)) != 0) + error = fget(td, uap->fd, cap_rights_init(&rights, CAP_FPATHCONF), &fp); + if (error != 0) return (error); /* If asynchronous I/O is available, it works for all descriptors. */ @@ -1417,7 +1428,7 @@ static void filecaps_fill(struct filecaps *fcaps) { - fcaps->fc_rights = CAP_ALL; + CAP_ALL(&fcaps->fc_rights); fcaps->fc_ioctls = NULL; fcaps->fc_nioctls = -1; fcaps->fc_fcntls = CAP_FCNTL_ALL; @@ -1441,16 +1452,18 @@ static void filecaps_validate(const struct filecaps *fcaps, const char *func) { - KASSERT((fcaps->fc_rights & ~CAP_MASK_VALID) == 0, + KASSERT(cap_rights_is_valid(&fcaps->fc_rights), ("%s: invalid rights", func)); KASSERT((fcaps->fc_fcntls & ~CAP_FCNTL_ALL) == 0, ("%s: invalid fcntls", func)); - KASSERT(fcaps->fc_fcntls == 0 || (fcaps->fc_rights & CAP_FCNTL) != 0, + KASSERT(fcaps->fc_fcntls == 0 || + cap_rights_is_set(&fcaps->fc_rights, CAP_FCNTL), ("%s: fcntls without CAP_FCNTL", func)); KASSERT(fcaps->fc_ioctls != NULL ? fcaps->fc_nioctls > 0 : (fcaps->fc_nioctls == -1 || fcaps->fc_nioctls == 0), ("%s: invalid ioctls", func)); - KASSERT(fcaps->fc_nioctls == 0 || (fcaps->fc_rights & CAP_IOCTL) != 0, + KASSERT(fcaps->fc_nioctls == 0 || + cap_rights_is_set(&fcaps->fc_rights, CAP_IOCTL), ("%s: ioctls without CAP_IOCTL", func)); } @@ -2285,7 +2298,7 @@ finit(struct file *fp, u_int flag, short type, void *data, struct fileops *ops) } int -fget_unlocked(struct filedesc *fdp, int fd, cap_rights_t needrights, +fget_unlocked(struct filedesc *fdp, int fd, cap_rights_t *needrightsp, int needfcntl, struct file **fpp, cap_rights_t *haverightsp) { struct file *fp; @@ -2310,14 +2323,16 @@ fget_unlocked(struct filedesc *fdp, int fd, cap_rights_t needrights, if (fp == NULL) return (EBADF); #ifdef CAPABILITIES - haverights = cap_rights(fdp, fd); - error = cap_check(haverights, needrights); - if (error != 0) - return (error); - if ((needrights & CAP_FCNTL) != 0) { - error = cap_fcntl_check(fdp, fd, needfcntl); + haverights = *cap_rights(fdp, fd); + if (needrightsp != NULL) { + error = cap_check(&haverights, needrightsp); if (error != 0) return (error); + if (cap_rights_is_set(needrightsp, CAP_FCNTL)) { + error = cap_fcntl_check(fdp, fd, needfcntl); + if (error != 0) + return (error); + } } #endif count = fp->f_count; @@ -2338,7 +2353,7 @@ fget_unlocked(struct filedesc *fdp, int fd, cap_rights_t needrights, #ifdef CAPABILITIES *haverightsp = haverights; #else - *haverightsp = CAP_ALL; + CAP_ALL(haverightsp); #endif } return (0); @@ -2359,19 +2374,23 @@ fget_unlocked(struct filedesc *fdp, int fd, cap_rights_t needrights, */ static __inline int _fget(struct thread *td, int fd, struct file **fpp, int flags, - cap_rights_t needrights, u_char *maxprotp) + cap_rights_t *needrightsp, u_char *maxprotp) { struct filedesc *fdp; struct file *fp; - cap_rights_t haverights; + cap_rights_t haverights, needrights; int error; *fpp = NULL; if (td == NULL || (fdp = td->td_proc->p_fd) == NULL) return (EBADF); + if (needrightsp != NULL) + needrights = *needrightsp; + else + cap_rights_init(&needrights); if (maxprotp != NULL) - needrights |= CAP_MMAP; - error = fget_unlocked(fdp, fd, needrights, 0, &fp, &haverights); + cap_rights_set(&needrights, CAP_MMAP); + error = fget_unlocked(fdp, fd, &needrights, 0, &fp, &haverights); if (error != 0) return (error); if (fp->f_ops == &badfileops) { @@ -2384,7 +2403,7 @@ _fget(struct thread *td, int fd, struct file **fpp, int flags, * If requested, convert capability rights to access flags. */ if (maxprotp != NULL) - *maxprotp = cap_rights_to_vmprot(haverights); + *maxprotp = cap_rights_to_vmprot(&haverights); #else /* !CAPABILITIES */ if (maxprotp != NULL) *maxprotp = VM_PROT_ALL; @@ -2421,32 +2440,32 @@ _fget(struct thread *td, int fd, struct file **fpp, int flags, } int -fget(struct thread *td, int fd, cap_rights_t rights, struct file **fpp) +fget(struct thread *td, int fd, cap_rights_t *rightsp, struct file **fpp) { - return(_fget(td, fd, fpp, 0, rights, NULL)); + return(_fget(td, fd, fpp, 0, rightsp, NULL)); } int -fget_mmap(struct thread *td, int fd, cap_rights_t rights, u_char *maxprotp, +fget_mmap(struct thread *td, int fd, cap_rights_t *rightsp, u_char *maxprotp, struct file **fpp) { - return (_fget(td, fd, fpp, 0, rights, maxprotp)); + return (_fget(td, fd, fpp, 0, rightsp, maxprotp)); } int -fget_read(struct thread *td, int fd, cap_rights_t rights, struct file **fpp) +fget_read(struct thread *td, int fd, cap_rights_t *rightsp, struct file **fpp) { - return(_fget(td, fd, fpp, FREAD, rights, NULL)); + return(_fget(td, fd, fpp, FREAD, rightsp, NULL)); } int -fget_write(struct thread *td, int fd, cap_rights_t rights, struct file **fpp) +fget_write(struct thread *td, int fd, cap_rights_t *rightsp, struct file **fpp) { - return (_fget(td, fd, fpp, FWRITE, rights, NULL)); + return (_fget(td, fd, fpp, FWRITE, rightsp, NULL)); } /* @@ -2457,15 +2476,15 @@ fget_write(struct thread *td, int fd, cap_rights_t rights, struct file **fpp) * XXX: what about the unused flags ? */ static __inline int -_fgetvp(struct thread *td, int fd, int flags, cap_rights_t needrights, +_fgetvp(struct thread *td, int fd, int flags, cap_rights_t *needrightsp, struct vnode **vpp) { struct file *fp; int error; *vpp = NULL; - error = _fget(td, fd, &fp, flags, needrights, NULL); - if (error) + error = _fget(td, fd, &fp, flags, needrightsp, NULL); + if (error != 0) return (error); if (fp->f_vnode == NULL) { error = EINVAL; @@ -2479,14 +2498,14 @@ _fgetvp(struct thread *td, int fd, int flags, cap_rights_t needrights, } int -fgetvp(struct thread *td, int fd, cap_rights_t rights, struct vnode **vpp) +fgetvp(struct thread *td, int fd, cap_rights_t *rightsp, struct vnode **vpp) { - return (_fgetvp(td, fd, 0, rights, vpp)); + return (_fgetvp(td, fd, 0, rightsp, vpp)); } int -fgetvp_rights(struct thread *td, int fd, cap_rights_t need, +fgetvp_rights(struct thread *td, int fd, cap_rights_t *needrightsp, struct filecaps *havecaps, struct vnode **vpp) { struct filedesc *fdp; @@ -2503,9 +2522,11 @@ fgetvp_rights(struct thread *td, int fd, cap_rights_t need, return (EBADF); #ifdef CAPABILITIES - error = cap_check(cap_rights(fdp, fd), need); - if (error != 0) - return (error); + if (needrightsp != NULL) { + error = cap_check(cap_rights(fdp, fd), needrightsp); + if (error != 0) + return (error); + } #endif if (fp->f_vnode == NULL) @@ -2519,26 +2540,26 @@ fgetvp_rights(struct thread *td, int fd, cap_rights_t need, } int -fgetvp_read(struct thread *td, int fd, cap_rights_t rights, struct vnode **vpp) +fgetvp_read(struct thread *td, int fd, cap_rights_t *rightsp, struct vnode **vpp) { - return (_fgetvp(td, fd, FREAD, rights, vpp)); + return (_fgetvp(td, fd, FREAD, rightsp, vpp)); } int -fgetvp_exec(struct thread *td, int fd, cap_rights_t rights, struct vnode **vpp) +fgetvp_exec(struct thread *td, int fd, cap_rights_t *rightsp, struct vnode **vpp) { - return (_fgetvp(td, fd, FEXEC, rights, vpp)); + return (_fgetvp(td, fd, FEXEC, rightsp, vpp)); } #ifdef notyet int -fgetvp_write(struct thread *td, int fd, cap_rights_t rights, +fgetvp_write(struct thread *td, int fd, cap_rights_t *rightsp, struct vnode **vpp) { - return (_fgetvp(td, fd, FWRITE, rights, vpp)); + return (_fgetvp(td, fd, FWRITE, rightsp, vpp)); } #endif @@ -2554,7 +2575,7 @@ fgetvp_write(struct thread *td, int fd, cap_rights_t rights, * during use. */ int -fgetsock(struct thread *td, int fd, cap_rights_t rights, struct socket **spp, +fgetsock(struct thread *td, int fd, cap_rights_t *rightsp, struct socket **spp, u_int *fflagp) { struct file *fp; @@ -2563,7 +2584,7 @@ fgetsock(struct thread *td, int fd, cap_rights_t rights, struct socket **spp, *spp = NULL; if (fflagp != NULL) *fflagp = 0; - if ((error = _fget(td, fd, &fp, 0, rights, NULL)) != 0) + if ((error = _fget(td, fd, &fp, 0, rightsp, NULL)) != 0) return (error); if (fp->f_type != DTYPE_SOCKET) { error = ENOTSOCK; @@ -2637,9 +2658,11 @@ sys_flock(struct thread *td, struct flock_args *uap) struct file *fp; struct vnode *vp; struct flock lf; + cap_rights_t rights; int error; - if ((error = fget(td, uap->fd, CAP_FLOCK, &fp)) != 0) + error = fget(td, uap->fd, cap_rights_init(&rights, CAP_FLOCK), &fp); + if (error != 0) return (error); if (fp->f_type != DTYPE_VNODE) { fdrop(fp, td); @@ -3185,7 +3208,7 @@ struct export_fd_buf { static int export_fd_to_sb(void *data, int type, int fd, int fflags, int refcnt, - int64_t offset, cap_rights_t fd_cap_rights, struct export_fd_buf *efbuf) + int64_t offset, cap_rights_t *rightsp, struct export_fd_buf *efbuf) { struct { int fflag; @@ -3259,7 +3282,10 @@ export_fd_to_sb(void *data, int type, int fd, int fflags, int refcnt, for (i = 0; i < NFFLAGS; i++) if (fflags & fflags_table[i].fflag) kif->kf_flags |= fflags_table[i].kf_fflag; - kif->kf_cap_rights = fd_cap_rights; + if (rightsp != NULL) + kif->kf_cap_rights = *rightsp; + else + cap_rights_init(&kif->kf_cap_rights); kif->kf_fd = fd; kif->kf_type = type; kif->kf_ref_count = refcnt; @@ -3302,7 +3328,7 @@ kern_proc_filedesc_out(struct proc *p, struct sbuf *sb, ssize_t maxlen) void *data; int error, i; int type, refcnt, fflags; - cap_rights_t fd_cap_rights; + cap_rights_t rights; PROC_LOCK_ASSERT(p, MA_OWNED); @@ -3329,13 +3355,13 @@ kern_proc_filedesc_out(struct proc *p, struct sbuf *sb, ssize_t maxlen) efbuf->remainder = maxlen; if (tracevp != NULL) export_fd_to_sb(tracevp, KF_TYPE_VNODE, KF_FD_TYPE_TRACE, - FREAD | FWRITE, -1, -1, 0, efbuf); + FREAD | FWRITE, -1, -1, NULL, efbuf); if (textvp != NULL) export_fd_to_sb(textvp, KF_TYPE_VNODE, KF_FD_TYPE_TEXT, - FREAD, -1, -1, 0, efbuf); + FREAD, -1, -1, NULL, efbuf); if (cttyvp != NULL) export_fd_to_sb(cttyvp, KF_TYPE_VNODE, KF_FD_TYPE_CTTY, - FREAD | FWRITE, -1, -1, 0, efbuf); + FREAD | FWRITE, -1, -1, NULL, efbuf); error = 0; if (fdp == NULL) goto fail; @@ -3346,30 +3372,30 @@ kern_proc_filedesc_out(struct proc *p, struct sbuf *sb, ssize_t maxlen) vref(fdp->fd_cdir); data = fdp->fd_cdir; export_fd_to_sb(data, KF_TYPE_VNODE, KF_FD_TYPE_CWD, - FREAD, -1, -1, 0, efbuf); + FREAD, -1, -1, NULL, efbuf); } /* root directory */ if (fdp->fd_rdir != NULL) { vref(fdp->fd_rdir); data = fdp->fd_rdir; export_fd_to_sb(data, KF_TYPE_VNODE, KF_FD_TYPE_ROOT, - FREAD, -1, -1, 0, efbuf); + FREAD, -1, -1, NULL, efbuf); } /* jail directory */ if (fdp->fd_jdir != NULL) { vref(fdp->fd_jdir); data = fdp->fd_jdir; export_fd_to_sb(data, KF_TYPE_VNODE, KF_FD_TYPE_JAIL, - FREAD, -1, -1, 0, efbuf); + FREAD, -1, -1, NULL, efbuf); } for (i = 0; i < fdp->fd_nfiles; i++) { if ((fp = fdp->fd_ofiles[i].fde_file) == NULL) continue; data = NULL; #ifdef CAPABILITIES - fd_cap_rights = cap_rights(fdp, i); + rights = *cap_rights(fdp, i); #else /* !CAPABILITIES */ - fd_cap_rights = 0; + cap_rights_init(&rights); #endif switch (fp->f_type) { case DTYPE_VNODE: @@ -3443,8 +3469,8 @@ kern_proc_filedesc_out(struct proc *p, struct sbuf *sb, ssize_t maxlen) * the loop continues. */ error = export_fd_to_sb(data, type, i, fflags, refcnt, - offset, fd_cap_rights, efbuf); - if (error) + offset, &rights, efbuf); + if (error != 0) break; } FILEDESC_SUNLOCK(fdp); @@ -3887,6 +3913,15 @@ badfo_chown(struct file *fp, uid_t uid, gid_t gid, struct ucred *active_cred, return (EBADF); } +static int +badfo_sendfile(struct file *fp, int sockfd, struct uio *hdr_uio, + struct uio *trl_uio, off_t offset, size_t nbytes, off_t *sent, int flags, + int kflags, struct thread *td) +{ + + return (EBADF); +} + struct fileops badfileops = { .fo_read = badfo_readwrite, .fo_write = badfo_readwrite, @@ -3898,6 +3933,7 @@ struct fileops badfileops = { .fo_close = badfo_close, .fo_chmod = badfo_chmod, .fo_chown = badfo_chown, + .fo_sendfile = badfo_sendfile, }; int @@ -3916,6 +3952,15 @@ invfo_chown(struct file *fp, uid_t uid, gid_t gid, struct ucred *active_cred, return (EINVAL); } +int +invfo_sendfile(struct file *fp, int sockfd, struct uio *hdr_uio, + struct uio *trl_uio, off_t offset, size_t nbytes, off_t *sent, int flags, + int kflags, struct thread *td) +{ + + return (EINVAL); +} + /*-------------------------------------------------------------------*/ /* diff --git a/sys/kern/kern_event.c b/sys/kern/kern_event.c index cd8b232f83f..85ea78cd6da 100644 --- a/sys/kern/kern_event.c +++ b/sys/kern/kern_event.c @@ -127,6 +127,7 @@ static struct fileops kqueueops = { .fo_close = kqueue_close, .fo_chmod = invfo_chmod, .fo_chown = invfo_chown, + .fo_sendfile = invfo_sendfile, }; static int knote_attach(struct knote *kn, struct kqueue *kq); @@ -431,8 +432,11 @@ filt_proc(struct knote *kn, long hint) if (!(kn->kn_status & KN_DETACHED)) knlist_remove_inevent(&p->p_klist, kn); kn->kn_flags |= (EV_EOF | EV_ONESHOT); - kn->kn_data = p->p_xstat; kn->kn_ptr.p_proc = NULL; + if (kn->kn_fflags & NOTE_EXIT) + kn->kn_data = p->p_xstat; + if (kn->kn_fflags == 0) + kn->kn_flags |= EV_DROP; return (1); } @@ -474,7 +478,7 @@ knote_fork(struct knlist *list, int pid) */ if ((kn->kn_sfflags & NOTE_TRACK) == 0) { kn->kn_status |= KN_HASKQLOCK; - if (kn->kn_fop->f_event(kn, NOTE_FORK | pid)) + if (kn->kn_fop->f_event(kn, NOTE_FORK)) KNOTE_ACTIVATE(kn, 1); kn->kn_status &= ~KN_HASKQLOCK; KQ_UNLOCK(kq); @@ -502,10 +506,10 @@ knote_fork(struct knlist *list, int pid) kev.data = kn->kn_id; /* parent */ kev.udata = kn->kn_kevent.udata;/* preserve udata */ error = kqueue_register(kq, &kev, NULL, 0); - if (kn->kn_fop->f_event(kn, NOTE_FORK | pid)) - KNOTE_ACTIVATE(kn, 0); if (error) kn->kn_fflags |= NOTE_TRACKERR; + if (kn->kn_fop->f_event(kn, NOTE_FORK)) + KNOTE_ACTIVATE(kn, 0); KQ_LOCK(kq); kn->kn_status &= ~KN_INFLUX; KQ_UNLOCK_FLUX(kq); @@ -561,7 +565,7 @@ filt_timerattach(struct knote *kn) memory_order_relaxed)); kn->kn_flags |= EV_CLEAR; /* automatically set */ - kn->kn_status &= ~KN_DETACHED; /* knlist_add usually sets it */ + kn->kn_status &= ~KN_DETACHED; /* knlist_add clears it */ calloutp = malloc(sizeof(*calloutp), M_KQUEUE, M_WAITOK); callout_init(calloutp, CALLOUT_MPSAFE); kn->kn_hook = calloutp; @@ -583,7 +587,7 @@ filt_timerdetach(struct knote *kn) free(calloutp, M_KQUEUE); old = atomic_fetch_sub_explicit(&kq_ncallouts, 1, memory_order_relaxed); KASSERT(old > 0, ("Number of callouts cannot become negative")); - kn->kn_status |= KN_DETACHED; /* knlist_remove usually clears it */ + kn->kn_status |= KN_DETACHED; /* knlist_remove sets it */ } static int @@ -703,7 +707,7 @@ sys_kqueue(struct thread *td, struct kqueue_args *uap) TASK_INIT(&kq->kq_task, 0, kqueue_task, kq); FILEDESC_XLOCK(fdp); - SLIST_INSERT_HEAD(&fdp->fd_kqlist, kq, kq_list); + TAILQ_INSERT_HEAD(&fdp->fd_kqlist, kq, kq_list); FILEDESC_XUNLOCK(fdp); finit(fp, FREAD | FWRITE, DTYPE_KQUEUE, kq, &kqueueops); @@ -820,9 +824,11 @@ kern_kevent(struct thread *td, int fd, int nchanges, int nevents, struct kevent *kevp, *changes; struct kqueue *kq; struct file *fp; + cap_rights_t rights; int i, n, nerrors, error; - if ((error = fget(td, fd, CAP_POST_EVENT, &fp)) != 0) + error = fget(td, fd, cap_rights_init(&rights, CAP_POST_EVENT), &fp); + if (error != 0) return (error); if ((error = kqueue_acquire(fp, &kq)) != 0) goto done_norel; @@ -960,6 +966,7 @@ kqueue_register(struct kqueue *kq, struct kevent *kev, struct thread *td, int wa struct filterops *fops; struct file *fp; struct knote *kn, *tkn; + cap_rights_t rights; int error, filt, event; int haskqglobal; @@ -978,7 +985,8 @@ kqueue_register(struct kqueue *kq, struct kevent *kev, struct thread *td, int wa findkn: if (fops->f_isfd) { KASSERT(td != NULL, ("td is NULL")); - error = fget(td, kev->ident, CAP_POLL_EVENT, &fp); + error = fget(td, kev->ident, + cap_rights_init(&rights, CAP_POLL_EVENT), &fp); if (error) goto done; @@ -1410,7 +1418,21 @@ retry: KASSERT((kn->kn_status & KN_INFLUX) == 0, ("KN_INFLUX set when not suppose to be")); - if ((kn->kn_flags & EV_ONESHOT) == EV_ONESHOT) { + if ((kn->kn_flags & EV_DROP) == EV_DROP) { + kn->kn_status &= ~KN_QUEUED; + kn->kn_status |= KN_INFLUX; + kq->kq_count--; + KQ_UNLOCK(kq); + /* + * We don't need to lock the list since we've marked + * it _INFLUX. + */ + if (!(kn->kn_status & KN_DETACHED)) + kn->kn_fop->f_detach(kn); + knote_drop(kn, td); + KQ_LOCK(kq); + continue; + } else if ((kn->kn_flags & EV_ONESHOT) == EV_ONESHOT) { kn->kn_status &= ~KN_QUEUED; kn->kn_status |= KN_INFLUX; kq->kq_count--; @@ -1449,7 +1471,7 @@ retry: *kevp = kn->kn_kevent; KQ_LOCK(kq); KQ_GLOBAL_UNLOCK(&kq_global, haskqglobal); - if (kn->kn_flags & (EV_CLEAR | EV_DISPATCH)) { + if (kn->kn_flags & (EV_CLEAR | EV_DISPATCH)) { /* * Manually clear knotes who weren't * 'touch'ed. @@ -1696,7 +1718,7 @@ kqueue_close(struct file *fp, struct thread *td) KQ_UNLOCK(kq); FILEDESC_XLOCK(fdp); - SLIST_REMOVE(&fdp->fd_kqlist, kq, kqueue, kq_list); + TAILQ_REMOVE(&fdp->fd_kqlist, kq, kq_list); FILEDESC_XUNLOCK(fdp); seldrain(&kq->kq_sel); @@ -1841,7 +1863,7 @@ knlist_remove_kq(struct knlist *knl, struct knote *kn, int knlislocked, int kqis } /* - * remove all knotes from a specified klist + * remove knote from the specified knlist */ void knlist_remove(struct knlist *knl, struct knote *kn, int islocked) @@ -1851,7 +1873,7 @@ knlist_remove(struct knlist *knl, struct knote *kn, int islocked) } /* - * remove knote from a specified klist while in f_event handler. + * remove knote from the specified knlist while in f_event handler. */ void knlist_remove_inevent(struct knlist *knl, struct knote *kn) @@ -1984,7 +2006,7 @@ knlist_destroy(struct knlist *knl) #ifdef INVARIANTS /* * if we run across this error, we need to find the offending - * driver and have it call knlist_clear. + * driver and have it call knlist_clear or knlist_delete. */ if (!SLIST_EMPTY(&knl->kl_list)) printf("WARNING: destroying knlist w/ knotes on it!\n"); @@ -2073,7 +2095,7 @@ knote_fdclose(struct thread *td, int fd) * We shouldn't have to worry about new kevents appearing on fd * since filedesc is locked. */ - SLIST_FOREACH(kq, &fdp->fd_kqlist, kq_list) { + TAILQ_FOREACH(kq, &fdp->fd_kqlist, kq_list) { KQ_LOCK(kq); again: @@ -2219,9 +2241,11 @@ kqfd_register(int fd, struct kevent *kev, struct thread *td, int waitok) { struct kqueue *kq; struct file *fp; + cap_rights_t rights; int error; - if ((error = fget(td, fd, CAP_POST_EVENT, &fp)) != 0) + error = fget(td, fd, cap_rights_init(&rights, CAP_POST_EVENT), &fp); + if (error != 0) return (error); if ((error = kqueue_acquire(fp, &kq)) != 0) goto noacquire; diff --git a/sys/kern/kern_exec.c b/sys/kern/kern_exec.c index c0e14359539..45f732b2f85 100644 --- a/sys/kern/kern_exec.c +++ b/sys/kern/kern_exec.c @@ -96,12 +96,9 @@ dtrace_execexit_func_t dtrace_fasttrap_exec; #endif SDT_PROVIDER_DECLARE(proc); -SDT_PROBE_DEFINE(proc, kernel, , exec, exec); -SDT_PROBE_ARGTYPE(proc, kernel, , exec, 0, "char *"); -SDT_PROBE_DEFINE(proc, kernel, , exec_failure, exec-failure); -SDT_PROBE_ARGTYPE(proc, kernel, , exec_failure, 0, "int"); -SDT_PROBE_DEFINE(proc, kernel, , exec_success, exec-success); -SDT_PROBE_ARGTYPE(proc, kernel, , exec_success, 0, "char *"); +SDT_PROBE_DEFINE1(proc, kernel, , exec, exec, "char *"); +SDT_PROBE_DEFINE1(proc, kernel, , exec_failure, exec-failure, "int"); +SDT_PROBE_DEFINE1(proc, kernel, , exec_success, exec-success, "char *"); MALLOC_DEFINE(M_PARGS, "proc-args", "Process arguments"); @@ -341,6 +338,7 @@ do_execve(td, args, mac_p) struct ucred *tracecred = NULL; #endif struct vnode *textvp = NULL, *binvp = NULL; + cap_rights_t rights; int credential_changing; int textset; #ifdef MAC @@ -441,7 +439,8 @@ interpret: /* * Descriptors opened only with O_EXEC or O_RDONLY are allowed. */ - error = fgetvp_exec(td, args->fd, CAP_FEXECVE, &binvp); + error = fgetvp_exec(td, args->fd, + cap_rights_init(&rights, CAP_FEXECVE), &binvp); if (error) goto exec_fail; vn_lock(binvp, LK_EXCLUSIVE | LK_RETRY); @@ -937,10 +936,8 @@ exec_map_first_page(imgp) object->pg_color = 0; } #endif - ma[0] = vm_page_grab(object, 0, VM_ALLOC_NORMAL | VM_ALLOC_NOBUSY | - VM_ALLOC_RETRY); + ma[0] = vm_page_grab(object, 0, VM_ALLOC_NORMAL); if (ma[0]->valid != VM_PAGE_BITS_ALL) { - vm_page_busy(ma[0]); initial_pagein = VM_INITIAL_PAGEIN; if (initial_pagein > object->size) initial_pagein = object->size; @@ -948,9 +945,8 @@ exec_map_first_page(imgp) if ((ma[i] = vm_page_next(ma[i - 1])) != NULL) { if (ma[i]->valid) break; - if ((ma[i]->oflags & VPO_BUSY) || ma[i]->busy) + if (vm_page_tryxbusy(ma[i])) break; - vm_page_busy(ma[i]); } else { ma[i] = vm_page_alloc(object, i, VM_ALLOC_NORMAL | VM_ALLOC_IFNOTCACHED); @@ -970,8 +966,8 @@ exec_map_first_page(imgp) VM_OBJECT_WUNLOCK(object); return (EIO); } - vm_page_wakeup(ma[0]); } + vm_page_xunbusy(ma[0]); vm_page_lock(ma[0]); vm_page_hold(ma[0]); vm_page_unlock(ma[0]); @@ -1192,7 +1188,7 @@ int exec_alloc_args(struct image_args *args) { - args->buf = (char *)kmem_alloc_wait(exec_map, PATH_MAX + ARG_MAX); + args->buf = (char *)kmap_alloc_wait(exec_map, PATH_MAX + ARG_MAX); return (args->buf != NULL ? 0 : ENOMEM); } @@ -1201,7 +1197,7 @@ exec_free_args(struct image_args *args) { if (args->buf != NULL) { - kmem_free_wakeup(exec_map, (vm_offset_t)args->buf, + kmap_free_wakeup(exec_map, (vm_offset_t)args->buf, PATH_MAX + ARG_MAX); args->buf = NULL; } diff --git a/sys/kern/kern_exit.c b/sys/kern/kern_exit.c index 6acfc63c429..f0be10ed624 100644 --- a/sys/kern/kern_exit.c +++ b/sys/kern/kern_exit.c @@ -94,8 +94,7 @@ dtrace_execexit_func_t dtrace_fasttrap_exit; #endif SDT_PROVIDER_DECLARE(proc); -SDT_PROBE_DEFINE(proc, kernel, , exit, exit); -SDT_PROBE_ARGTYPE(proc, kernel, , exit, 0, "int"); +SDT_PROBE_DEFINE1(proc, kernel, , exit, exit, "int"); /* Hook for NFS teardown procedure. */ void (*nlminfo_release_p)(struct proc *p); diff --git a/sys/kern/kern_fork.c b/sys/kern/kern_fork.c index 741665f4af6..9cd1da96b64 100644 --- a/sys/kern/kern_fork.c +++ b/sys/kern/kern_fork.c @@ -89,10 +89,8 @@ dtrace_fork_func_t dtrace_fasttrap_fork; #endif SDT_PROVIDER_DECLARE(proc); -SDT_PROBE_DEFINE(proc, kernel, , create, create); -SDT_PROBE_ARGTYPE(proc, kernel, , create, 0, "struct proc *"); -SDT_PROBE_ARGTYPE(proc, kernel, , create, 1, "struct proc *"); -SDT_PROBE_ARGTYPE(proc, kernel, , create, 2, "int"); +SDT_PROBE_DEFINE3(proc, kernel, , create, create, "struct proc *", + "struct proc *", "int"); #ifndef _SYS_SYSPROTO_H_ struct fork_args { diff --git a/sys/kern/kern_intr.c b/sys/kern/kern_intr.c index 63d84694a56..f4b04c3f71b 100644 --- a/sys/kern/kern_intr.c +++ b/sys/kern/kern_intr.c @@ -1147,7 +1147,7 @@ swi_sched(void *cookie, int flags) entropy.event = (uintptr_t)ih; entropy.td = curthread; random_harvest(&entropy, sizeof(entropy), 1, 0, - RANDOM_INTERRUPT); + RANDOM_SWI); } /* diff --git a/sys/kern/kern_jail.c b/sys/kern/kern_jail.c index d70a936d5aa..331b0e1a943 100644 --- a/sys/kern/kern_jail.c +++ b/sys/kern/kern_jail.c @@ -206,6 +206,7 @@ static char *pr_allow_names[] = { "allow.mount.nullfs", "allow.mount.zfs", "allow.mount.procfs", + "allow.mount.tmpfs", }; const size_t pr_allow_names_size = sizeof(pr_allow_names); @@ -221,6 +222,7 @@ static char *pr_allow_nonames[] = { "allow.mount.nonullfs", "allow.mount.nozfs", "allow.mount.noprocfs", + "allow.mount.notmpfs", }; const size_t pr_allow_nonames_size = sizeof(pr_allow_nonames); @@ -3883,6 +3885,13 @@ prison_priv_check(struct ucred *cred, int priv) case PRIV_VFS_SETGID: case PRIV_VFS_STAT: case PRIV_VFS_STICKYFILE: + + /* + * As in the non-jail case, non-root users are expected to be + * able to read kernel/phyiscal memory (provided /dev/[k]mem + * exists in the jail and they have permission to access it). + */ + case PRIV_KMEM_READ: return (0); /* @@ -4228,6 +4237,10 @@ SYSCTL_PROC(_security_jail, OID_AUTO, mount_procfs_allowed, CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, NULL, PR_ALLOW_MOUNT_PROCFS, sysctl_jail_default_allow, "I", "Processes in jail can mount the procfs file system"); +SYSCTL_PROC(_security_jail, OID_AUTO, mount_tmpfs_allowed, + CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, + NULL, PR_ALLOW_MOUNT_TMPFS, sysctl_jail_default_allow, "I", + "Processes in jail can mount the tmpfs file system"); SYSCTL_PROC(_security_jail, OID_AUTO, mount_zfs_allowed, CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, NULL, PR_ALLOW_MOUNT_ZFS, sysctl_jail_default_allow, "I", @@ -4380,6 +4393,8 @@ SYSCTL_JAIL_PARAM(_allow_mount, nullfs, CTLTYPE_INT | CTLFLAG_RW, "B", "Jail may mount the nullfs file system"); SYSCTL_JAIL_PARAM(_allow_mount, procfs, CTLTYPE_INT | CTLFLAG_RW, "B", "Jail may mount the procfs file system"); +SYSCTL_JAIL_PARAM(_allow_mount, tmpfs, CTLTYPE_INT | CTLFLAG_RW, + "B", "Jail may mount the tmpfs file system"); SYSCTL_JAIL_PARAM(_allow_mount, zfs, CTLTYPE_INT | CTLFLAG_RW, "B", "Jail may mount the zfs file system"); diff --git a/sys/kern/kern_kthread.c b/sys/kern/kern_kthread.c index d8bbebd8ca6..969c513eb8f 100644 --- a/sys/kern/kern_kthread.c +++ b/sys/kern/kern_kthread.c @@ -257,18 +257,17 @@ kthread_add(void (*func)(void *), void *arg, struct proc *p, panic("kthread_add called too soon"); /* If no process supplied, put it on proc0 */ - if (p == NULL) { + if (p == NULL) p = &proc0; - oldtd = &thread0; - } else { - oldtd = FIRST_THREAD_IN_PROC(p); - } /* Initialize our new td */ newtd = thread_alloc(pages); if (newtd == NULL) return (ENOMEM); + PROC_LOCK(p); + oldtd = FIRST_THREAD_IN_PROC(p); + bzero(&newtd->td_startzero, __rangeof(struct thread, td_startzero, td_endzero)); bcopy(&oldtd->td_startcopy, &newtd->td_startcopy, @@ -292,7 +291,6 @@ kthread_add(void (*func)(void *), void *arg, struct proc *p, newtd->td_ucred = crhold(p->p_ucred); /* this code almost the same as create_thread() in kern_thr.c */ - PROC_LOCK(p); p->p_flag |= P_HADTHREADS; thread_link(newtd, p); thread_lock(oldtd); diff --git a/sys/kern/kern_ktrace.c b/sys/kern/kern_ktrace.c index e512a33b2c9..d4b722a9172 100644 --- a/sys/kern/kern_ktrace.c +++ b/sys/kern/kern_ktrace.c @@ -37,6 +37,7 @@ __FBSDID("$FreeBSD$"); #include "opt_ktrace.h" #include +#include #include #include #include @@ -779,8 +780,8 @@ ktrstruct(name, data, datalen) void ktrcapfail(type, needed, held) enum ktr_cap_fail_type type; - cap_rights_t needed; - cap_rights_t held; + const cap_rights_t *needed; + const cap_rights_t *held; { struct thread *td = curthread; struct ktr_request *req; @@ -791,8 +792,14 @@ ktrcapfail(type, needed, held) return; kcf = &req->ktr_data.ktr_cap_fail; kcf->cap_type = type; - kcf->cap_needed = needed; - kcf->cap_held = held; + if (needed != NULL) + kcf->cap_needed = *needed; + else + cap_rights_init(&kcf->cap_needed); + if (held != NULL) + kcf->cap_held = *held; + else + cap_rights_init(&kcf->cap_held); ktr_enqueuerequest(td, req); ktrace_exit(td); } diff --git a/sys/kern/kern_linker.c b/sys/kern/kern_linker.c index b3ab4df35a2..7d322601c68 100644 --- a/sys/kern/kern_linker.c +++ b/sys/kern/kern_linker.c @@ -45,6 +45,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include #include @@ -70,17 +71,6 @@ SYSCTL_INT(_debug, OID_AUTO, kld_debug, CTLFLAG_RW | CTLFLAG_TUN, TUNABLE_INT("debug.kld_debug", &kld_debug); #endif -#define KLD_LOCK() sx_xlock(&kld_sx) -#define KLD_UNLOCK() sx_xunlock(&kld_sx) -#define KLD_DOWNGRADE() sx_downgrade(&kld_sx) -#define KLD_LOCK_READ() sx_slock(&kld_sx) -#define KLD_UNLOCK_READ() sx_sunlock(&kld_sx) -#define KLD_LOCKED() sx_xlocked(&kld_sx) -#define KLD_LOCK_ASSERT() do { \ - if (!cold) \ - sx_assert(&kld_sx, SX_XLOCKED); \ -} while (0) - /* * static char *linker_search_path(const char *name, struct mod_depend * *verinfo); @@ -120,7 +110,8 @@ static int linker_no_more_classes = 0; #define LINKER_GET_NEXT_FILE_ID(a) do { \ linker_file_t lftmp; \ \ - KLD_LOCK_ASSERT(); \ + if (!cold) \ + sx_assert(&kld_sx, SA_XLOCKED); \ retry: \ TAILQ_FOREACH(lftmp, &linker_files, link) { \ if (next_file_id == lftmp->id) { \ @@ -152,16 +143,6 @@ static int linker_load_module(const char *kldname, struct mod_depend *verinfo, struct linker_file **lfpp); static modlist_t modlist_lookup2(const char *name, struct mod_depend *verinfo); -static char * -linker_strdup(const char *str) -{ - char *result; - - if ((result = malloc((strlen(str) + 1), M_LINKER, M_WAITOK)) != NULL) - strcpy(result, str); - return (result); -} - static void linker_init(void *arg) { @@ -207,6 +188,8 @@ linker_file_sysinit(linker_file_t lf) KLD_DPF(FILE, ("linker_file_sysinit: calling SYSINITs for %s\n", lf->filename)); + sx_assert(&kld_sx, SA_XLOCKED); + if (linker_file_lookup_set(lf, "sysinit_set", &start, &stop, NULL) != 0) return; /* @@ -232,6 +215,7 @@ linker_file_sysinit(linker_file_t lf) * Traverse the (now) ordered list of system initialization tasks. * Perform each task, and continue on to the next task. */ + sx_xunlock(&kld_sx); mtx_lock(&Giant); for (sipp = start; sipp < stop; sipp++) { if ((*sipp)->subsystem == SI_SUB_DUMMY) @@ -241,6 +225,7 @@ linker_file_sysinit(linker_file_t lf) (*((*sipp)->func)) ((*sipp)->udata); } mtx_unlock(&Giant); + sx_xlock(&kld_sx); } static void @@ -251,6 +236,8 @@ linker_file_sysuninit(linker_file_t lf) KLD_DPF(FILE, ("linker_file_sysuninit: calling SYSUNINITs for %s\n", lf->filename)); + sx_assert(&kld_sx, SA_XLOCKED); + if (linker_file_lookup_set(lf, "sysuninit_set", &start, &stop, NULL) != 0) return; @@ -278,6 +265,7 @@ linker_file_sysuninit(linker_file_t lf) * Traverse the (now) ordered list of system initialization tasks. * Perform each task, and continue on to the next task. */ + sx_xunlock(&kld_sx); mtx_lock(&Giant); for (sipp = start; sipp < stop; sipp++) { if ((*sipp)->subsystem == SI_SUB_DUMMY) @@ -287,6 +275,7 @@ linker_file_sysuninit(linker_file_t lf) (*((*sipp)->func)) ((*sipp)->udata); } mtx_unlock(&Giant); + sx_xlock(&kld_sx); } static void @@ -298,13 +287,17 @@ linker_file_register_sysctls(linker_file_t lf) ("linker_file_register_sysctls: registering SYSCTLs for %s\n", lf->filename)); + sx_assert(&kld_sx, SA_XLOCKED); + if (linker_file_lookup_set(lf, "sysctl_set", &start, &stop, NULL) != 0) return; + sx_xunlock(&kld_sx); sysctl_lock(); for (oidp = start; oidp < stop; oidp++) sysctl_register_oid(*oidp); sysctl_unlock(); + sx_xlock(&kld_sx); } static void @@ -315,13 +308,17 @@ linker_file_unregister_sysctls(linker_file_t lf) KLD_DPF(FILE, ("linker_file_unregister_sysctls: unregistering SYSCTLs" " for %s\n", lf->filename)); + sx_assert(&kld_sx, SA_XLOCKED); + if (linker_file_lookup_set(lf, "sysctl_set", &start, &stop, NULL) != 0) return; + sx_xunlock(&kld_sx); sysctl_lock(); for (oidp = start; oidp < stop; oidp++) sysctl_unregister_oid(*oidp); sysctl_unlock(); + sx_xlock(&kld_sx); } static int @@ -334,6 +331,8 @@ linker_file_register_modules(linker_file_t lf) KLD_DPF(FILE, ("linker_file_register_modules: registering modules" " in %s\n", lf->filename)); + sx_assert(&kld_sx, SA_XLOCKED); + if (linker_file_lookup_set(lf, "modmetadata_set", &start, &stop, NULL) != 0) { /* @@ -369,7 +368,9 @@ static void linker_init_kernel_modules(void) { + sx_xlock(&kld_sx); linker_file_register_modules(linker_kernel_file); + sx_xunlock(&kld_sx); } SYSINIT(linker_kernel, SI_SUB_KLD, SI_ORDER_ANY, linker_init_kernel_modules, @@ -386,7 +387,7 @@ linker_load_file(const char *filename, linker_file_t *result) if (prison0.pr_securelevel > 0) return (EPERM); - KLD_LOCK_ASSERT(); + sx_assert(&kld_sx, SA_XLOCKED); lf = linker_find_file_by_name(filename); if (lf) { KLD_DPF(FILE, ("linker_load_file: file %s is already loaded," @@ -420,10 +421,8 @@ linker_load_file(const char *filename, linker_file_t *result) return (error); } modules = !TAILQ_EMPTY(&lf->modules); - KLD_UNLOCK(); linker_file_register_sysctls(lf); linker_file_sysinit(lf); - KLD_LOCK(); lf->flags |= LINKER_FILE_LINKED; /* @@ -474,16 +473,16 @@ linker_reference_module(const char *modname, struct mod_depend *verinfo, modlist_t mod; int error; - KLD_LOCK(); + sx_xlock(&kld_sx); if ((mod = modlist_lookup2(modname, verinfo)) != NULL) { *result = mod->container; (*result)->refs++; - KLD_UNLOCK(); + sx_xunlock(&kld_sx); return (0); } error = linker_load_module(NULL, modname, NULL, verinfo, result); - KLD_UNLOCK(); + sx_xunlock(&kld_sx); return (error); } @@ -494,13 +493,13 @@ linker_release_module(const char *modname, struct mod_depend *verinfo, modlist_t mod; int error; - KLD_LOCK(); + sx_xlock(&kld_sx); if (lf == NULL) { KASSERT(modname != NULL, ("linker_release_module: no file or name")); mod = modlist_lookup2(modname, verinfo); if (mod == NULL) { - KLD_UNLOCK(); + sx_xunlock(&kld_sx); return (ESRCH); } lf = mod->container; @@ -508,7 +507,7 @@ linker_release_module(const char *modname, struct mod_depend *verinfo, KASSERT(modname == NULL && verinfo == NULL, ("linker_release_module: both file and name")); error = linker_file_unload(lf, LINKER_UNLOAD_NORMAL); - KLD_UNLOCK(); + sx_xunlock(&kld_sx); return (error); } @@ -521,7 +520,7 @@ linker_find_file_by_name(const char *filename) koname = malloc(strlen(filename) + 4, M_LINKER, M_WAITOK); sprintf(koname, "%s.ko", filename); - KLD_LOCK_ASSERT(); + sx_assert(&kld_sx, SA_XLOCKED); TAILQ_FOREACH(lf, &linker_files, link) { if (strcmp(lf->filename, koname) == 0) break; @@ -537,7 +536,7 @@ linker_find_file_by_id(int fileid) { linker_file_t lf; - KLD_LOCK_ASSERT(); + sx_assert(&kld_sx, SA_XLOCKED); TAILQ_FOREACH(lf, &linker_files, link) if (lf->id == fileid && lf->flags & LINKER_FILE_LINKED) break; @@ -550,13 +549,13 @@ linker_file_foreach(linker_predicate_t *predicate, void *context) linker_file_t lf; int retval = 0; - KLD_LOCK(); + sx_xlock(&kld_sx); TAILQ_FOREACH(lf, &linker_files, link) { retval = predicate(lf, context); if (retval != 0) break; } - KLD_UNLOCK(); + sx_xunlock(&kld_sx); return (retval); } @@ -566,7 +565,8 @@ linker_make_file(const char *pathname, linker_class_t lc) linker_file_t lf; const char *filename; - KLD_LOCK_ASSERT(); + if (!cold) + sx_assert(&kld_sx, SA_XLOCKED); filename = linker_basename(pathname); KLD_DPF(FILE, ("linker_make_file: new file, filename='%s' for pathname='%s'\n", filename, pathname)); @@ -576,14 +576,12 @@ linker_make_file(const char *pathname, linker_class_t lc) lf->refs = 1; lf->userrefs = 0; lf->flags = 0; - lf->filename = linker_strdup(filename); - lf->pathname = linker_strdup(pathname); + lf->filename = strdup(filename, M_LINKER); + lf->pathname = strdup(pathname, M_LINKER); LINKER_GET_NEXT_FILE_ID(lf->id); lf->ndeps = 0; lf->deps = NULL; lf->loadcnt = ++loadcnt; - lf->sdt_probes = NULL; - lf->sdt_nprobes = 0; STAILQ_INIT(&lf->common); TAILQ_INIT(&lf->modules); TAILQ_INSERT_TAIL(&linker_files, lf, link); @@ -602,7 +600,7 @@ linker_file_unload(linker_file_t file, int flags) if (prison0.pr_securelevel > 0) return (EPERM); - KLD_LOCK_ASSERT(); + sx_assert(&kld_sx, SA_XLOCKED); KLD_DPF(FILE, ("linker_file_unload: lf->refs=%d\n", file->refs)); /* Easy case of just dropping a reference. */ @@ -675,10 +673,8 @@ linker_file_unload(linker_file_t file, int flags) */ if (file->flags & LINKER_FILE_LINKED) { file->flags &= ~LINKER_FILE_LINKED; - KLD_UNLOCK(); linker_file_sysuninit(file); linker_file_unregister_sysctls(file); - KLD_LOCK(); } TAILQ_REMOVE(&linker_files, file, link); @@ -717,7 +713,7 @@ linker_file_add_dependency(linker_file_t file, linker_file_t dep) { linker_file_t *newdeps; - KLD_LOCK_ASSERT(); + sx_assert(&kld_sx, SA_XLOCKED); newdeps = malloc((file->ndeps + 1) * sizeof(linker_file_t *), M_LINKER, M_WAITOK | M_ZERO); if (newdeps == NULL) @@ -747,15 +743,9 @@ int linker_file_lookup_set(linker_file_t file, const char *name, void *firstp, void *lastp, int *countp) { - int error, locked; - locked = KLD_LOCKED(); - if (!locked) - KLD_LOCK(); - error = LINKER_LOOKUP_SET(file, name, firstp, lastp, countp); - if (!locked) - KLD_UNLOCK(); - return (error); + sx_assert(&kld_sx, SA_LOCKED); + return (LINKER_LOOKUP_SET(file, name, firstp, lastp, countp)); } /* @@ -774,12 +764,12 @@ linker_file_lookup_symbol(linker_file_t file, const char *name, int deps) caddr_t sym; int locked; - locked = KLD_LOCKED(); + locked = sx_xlocked(&kld_sx); if (!locked) - KLD_LOCK(); + sx_xlock(&kld_sx); sym = linker_file_lookup_symbol_internal(file, name, deps); if (!locked) - KLD_UNLOCK(); + sx_xunlock(&kld_sx); return (sym); } @@ -793,7 +783,7 @@ linker_file_lookup_symbol_internal(linker_file_t file, const char *name, size_t common_size = 0; int i; - KLD_LOCK_ASSERT(); + sx_assert(&kld_sx, SA_XLOCKED); KLD_DPF(SYM, ("linker_file_lookup_symbol: file=%p, name=%s, deps=%d\n", file, name, deps)); @@ -993,9 +983,9 @@ linker_search_symbol_name(caddr_t value, char *buf, u_int buflen, { int error; - KLD_LOCK(); + sx_xlock(&kld_sx); error = linker_debug_search_symbol_name(value, buf, buflen, offset); - KLD_UNLOCK(); + sx_xunlock(&kld_sx); return (error); } @@ -1005,9 +995,6 @@ linker_search_symbol_name(caddr_t value, char *buf, u_int buflen, int kern_kldload(struct thread *td, const char *file, int *fileid) { -#ifdef HWPMC_HOOKS - struct pmckern_map_in pkm; -#endif const char *kldname, *modname; linker_file_t lf; int error; @@ -1037,24 +1024,19 @@ kern_kldload(struct thread *td, const char *file, int *fileid) modname = file; } - KLD_LOCK(); + sx_xlock(&kld_sx); error = linker_load_module(kldname, modname, NULL, NULL, &lf); if (error) { - KLD_UNLOCK(); + sx_xunlock(&kld_sx); goto done; } lf->userrefs++; if (fileid != NULL) *fileid = lf->id; -#ifdef HWPMC_HOOKS - KLD_DOWNGRADE(); - pkm.pm_file = lf->filename; - pkm.pm_address = (uintptr_t) lf->address; - PMC_CALL_HOOK(td, PMC_FN_KLD_LOAD, (void *) &pkm); - KLD_UNLOCK_READ(); -#else - KLD_UNLOCK(); -#endif + + sx_downgrade(&kld_sx); + EVENTHANDLER_INVOKE(kld_load, lf); + sx_sunlock(&kld_sx); done: CURVNET_RESTORE(); @@ -1083,10 +1065,10 @@ sys_kldload(struct thread *td, struct kldload_args *uap) int kern_kldunload(struct thread *td, int fileid, int flags) { -#ifdef HWPMC_HOOKS - struct pmckern_map_out pkm; -#endif linker_file_t lf; + char *filename = NULL; + caddr_t address; + size_t size; int error = 0; if ((error = securelevel_gt(td->td_ucred, 0)) != 0) @@ -1096,17 +1078,15 @@ kern_kldunload(struct thread *td, int fileid, int flags) return (error); CURVNET_SET(TD_TO_VNET(td)); - KLD_LOCK(); + sx_xlock(&kld_sx); lf = linker_find_file_by_id(fileid); if (lf) { KLD_DPF(FILE, ("kldunload: lf->userrefs=%d\n", lf->userrefs)); - /* Check if there are DTrace probes enabled on this file. */ - if (lf->nenabled > 0) { - printf("kldunload: attempt to unload file that has" - " DTrace probes enabled\n"); + EVENTHANDLER_INVOKE(kld_unload_try, lf, &error); + if (error != 0) error = EBUSY; - } else if (lf->userrefs == 0) { + else if (lf->userrefs == 0) { /* * XXX: maybe LINKER_UNLOAD_FORCE should override ? */ @@ -1114,11 +1094,11 @@ kern_kldunload(struct thread *td, int fileid, int flags) " loaded by the kernel\n"); error = EBUSY; } else { -#ifdef HWPMC_HOOKS - /* Save data needed by hwpmc(4) before unloading. */ - pkm.pm_address = (uintptr_t) lf->address; - pkm.pm_size = lf->size; -#endif + /* Save data needed for the kld_unload callbacks. */ + filename = strdup(lf->filename, M_TEMP); + address = lf->address; + size = lf->size; + lf->userrefs--; error = linker_file_unload(lf, flags); if (error) @@ -1127,16 +1107,14 @@ kern_kldunload(struct thread *td, int fileid, int flags) } else error = ENOENT; -#ifdef HWPMC_HOOKS if (error == 0) { - KLD_DOWNGRADE(); - PMC_CALL_HOOK(td, PMC_FN_KLD_UNLOAD, (void *) &pkm); - KLD_UNLOCK_READ(); + sx_downgrade(&kld_sx); + EVENTHANDLER_INVOKE(kld_unload, filename, address, size); + sx_sunlock(&kld_sx); } else - KLD_UNLOCK(); -#else - KLD_UNLOCK(); -#endif + sx_xunlock(&kld_sx); + free(filename, M_TEMP); + CURVNET_RESTORE(); return (error); } @@ -1179,13 +1157,13 @@ sys_kldfind(struct thread *td, struct kldfind_args *uap) goto out; filename = linker_basename(pathname); - KLD_LOCK(); + sx_xlock(&kld_sx); lf = linker_find_file_by_name(filename); if (lf) td->td_retval[0] = lf->id; else error = ENOENT; - KLD_UNLOCK(); + sx_xunlock(&kld_sx); out: free(pathname, M_TEMP); return (error); @@ -1203,7 +1181,7 @@ sys_kldnext(struct thread *td, struct kldnext_args *uap) return (error); #endif - KLD_LOCK(); + sx_xlock(&kld_sx); if (uap->fileid == 0) lf = TAILQ_FIRST(&linker_files); else { @@ -1224,7 +1202,7 @@ sys_kldnext(struct thread *td, struct kldnext_args *uap) else td->td_retval[0] = 0; out: - KLD_UNLOCK(); + sx_xunlock(&kld_sx); return (error); } @@ -1263,10 +1241,10 @@ kern_kldstat(struct thread *td, int fileid, struct kld_file_stat *stat) return (error); #endif - KLD_LOCK(); + sx_xlock(&kld_sx); lf = linker_find_file_by_id(fileid); if (lf == NULL) { - KLD_UNLOCK(); + sx_xunlock(&kld_sx); return (ENOENT); } @@ -1284,7 +1262,7 @@ kern_kldstat(struct thread *td, int fileid, struct kld_file_stat *stat) if (namelen > MAXPATHLEN) namelen = MAXPATHLEN; bcopy(lf->pathname, &stat->pathname[0], namelen); - KLD_UNLOCK(); + sx_xunlock(&kld_sx); td->td_retval[0] = 0; return (0); @@ -1303,7 +1281,7 @@ sys_kldfirstmod(struct thread *td, struct kldfirstmod_args *uap) return (error); #endif - KLD_LOCK(); + sx_xlock(&kld_sx); lf = linker_find_file_by_id(uap->fileid); if (lf) { MOD_SLOCK; @@ -1315,7 +1293,7 @@ sys_kldfirstmod(struct thread *td, struct kldfirstmod_args *uap) MOD_SUNLOCK; } else error = ENOENT; - KLD_UNLOCK(); + sx_xunlock(&kld_sx); return (error); } @@ -1343,7 +1321,7 @@ sys_kldsym(struct thread *td, struct kldsym_args *uap) symstr = malloc(MAXPATHLEN, M_TEMP, M_WAITOK); if ((error = copyinstr(lookup.symname, symstr, MAXPATHLEN, NULL)) != 0) goto out; - KLD_LOCK(); + sx_xlock(&kld_sx); if (uap->fileid != 0) { lf = linker_find_file_by_id(uap->fileid); if (lf == NULL) @@ -1369,7 +1347,7 @@ sys_kldsym(struct thread *td, struct kldsym_args *uap) if (lf == NULL) error = ENOENT; } - KLD_UNLOCK(); + sx_xunlock(&kld_sx); out: free(symstr, M_TEMP); return (error); @@ -1478,6 +1456,7 @@ linker_preload(void *arg) error = 0; modptr = NULL; + sx_xlock(&kld_sx); while ((modptr = preload_search_next_name(modptr)) != NULL) { modname = (char *)preload_search_info(modptr, MODINFO_NAME); modtype = (char *)preload_search_info(modptr, MODINFO_TYPE); @@ -1659,6 +1638,7 @@ fail: TAILQ_REMOVE(&depended_files, lf, loaded); linker_file_unload(lf, LINKER_UNLOAD_FORCE); } + sx_xunlock(&kld_sx); /* woohoo! we made it! */ } @@ -1918,7 +1898,7 @@ linker_search_kld(const char *name) /* qualified at all? */ if (strchr(name, '/')) - return (linker_strdup(name)); + return (strdup(name, M_LINKER)); /* traverse the linker path */ len = strlen(name); @@ -1958,7 +1938,7 @@ linker_hwpmc_list_objects(void) int i, nmappings; nmappings = 0; - KLD_LOCK_READ(); + sx_slock(&kld_sx); TAILQ_FOREACH(lf, &linker_files, link) nmappings++; @@ -1973,7 +1953,7 @@ linker_hwpmc_list_objects(void) kobase[i].pm_address = (uintptr_t)lf->address; i++; } - KLD_UNLOCK_READ(); + sx_sunlock(&kld_sx); KASSERT(i > 0, ("linker_hpwmc_list_objects: no kernel objects?")); @@ -1999,7 +1979,7 @@ linker_load_module(const char *kldname, const char *modname, char *pathname; int error; - KLD_LOCK_ASSERT(); + sx_assert(&kld_sx, SA_XLOCKED); if (modname == NULL) { /* * We have to load KLD @@ -2011,7 +1991,7 @@ linker_load_module(const char *kldname, const char *modname, if (modlist_lookup2(modname, verinfo) != NULL) return (EEXIST); if (kldname != NULL) - pathname = linker_strdup(kldname); + pathname = strdup(kldname, M_LINKER); else if (rootvnode == NULL) pathname = NULL; else @@ -2073,7 +2053,7 @@ linker_load_dependencies(linker_file_t lf) /* * All files are dependant on /kernel. */ - KLD_LOCK_ASSERT(); + sx_assert(&kld_sx, SA_XLOCKED); if (linker_kernel_file) { linker_kernel_file->refs++; error = linker_file_add_dependency(lf, linker_kernel_file); @@ -2165,16 +2145,16 @@ sysctl_kern_function_list(SYSCTL_HANDLER_ARGS) error = sysctl_wire_old_buffer(req, 0); if (error != 0) return (error); - KLD_LOCK(); + sx_xlock(&kld_sx); TAILQ_FOREACH(lf, &linker_files, link) { error = LINKER_EACH_FUNCTION_NAME(lf, sysctl_kern_function_list_iterate, req); if (error) { - KLD_UNLOCK(); + sx_xunlock(&kld_sx); return (error); } } - KLD_UNLOCK(); + sx_xunlock(&kld_sx); return (SYSCTL_OUT(req, "", 1)); } diff --git a/sys/kern/kern_malloc.c b/sys/kern/kern_malloc.c index 5e4a5020ef1..91164335737 100644 --- a/sys/kern/kern_malloc.c +++ b/sys/kern/kern_malloc.c @@ -62,9 +62,11 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include +#include #include #include #include @@ -113,12 +115,7 @@ MALLOC_DEFINE(M_TEMP, "temp", "misc temporary data buffers"); MALLOC_DEFINE(M_IP6OPT, "ip6opt", "IPv6 options"); MALLOC_DEFINE(M_IP6NDP, "ip6ndp", "IPv6 Neighbor Discovery"); -static void kmeminit(void *); -SYSINIT(kmem, SI_SUB_KMEM, SI_ORDER_FIRST, kmeminit, NULL); - static struct malloc_type *kmemstatistics; -static vm_offset_t kmembase; -static vm_offset_t kmemlimit; static int kmemcount; #define KMEM_ZSHIFT 4 @@ -203,12 +200,12 @@ SYSCTL_UINT(_vm, OID_AUTO, kmem_size_scale, CTLFLAG_RDTUN, &vm_kmem_size_scale, static int sysctl_kmem_map_size(SYSCTL_HANDLER_ARGS); SYSCTL_PROC(_vm, OID_AUTO, kmem_map_size, CTLFLAG_RD | CTLTYPE_ULONG | CTLFLAG_MPSAFE, NULL, 0, - sysctl_kmem_map_size, "LU", "Current kmem_map allocation size"); + sysctl_kmem_map_size, "LU", "Current kmem allocation size"); static int sysctl_kmem_map_free(SYSCTL_HANDLER_ARGS); SYSCTL_PROC(_vm, OID_AUTO, kmem_map_free, CTLFLAG_RD | CTLTYPE_ULONG | CTLFLAG_MPSAFE, NULL, 0, - sysctl_kmem_map_free, "LU", "Largest contiguous free range in kmem_map"); + sysctl_kmem_map_free, "LU", "Free space in kmem"); /* * The malloc_mtx protects the kmemstatistics linked list. @@ -253,7 +250,7 @@ sysctl_kmem_map_size(SYSCTL_HANDLER_ARGS) { u_long size; - size = kmem_map->size; + size = vmem_size(kmem_arena, VMEM_ALLOC); return (sysctl_handle_long(oidp, &size, 0, req)); } @@ -262,10 +259,7 @@ sysctl_kmem_map_free(SYSCTL_HANDLER_ARGS) { u_long size; - vm_map_lock_read(kmem_map); - size = kmem_map->root != NULL ? kmem_map->root->max_free : - kmem_map->max_offset - kmem_map->min_offset; - vm_map_unlock_read(kmem_map); + size = vmem_size(kmem_arena, VMEM_FREE); return (sysctl_handle_long(oidp, &size, 0, req)); } @@ -420,7 +414,7 @@ contigmalloc(unsigned long size, struct malloc_type *type, int flags, { void *ret; - ret = (void *)kmem_alloc_contig(kernel_map, size, flags, low, high, + ret = (void *)kmem_alloc_contig(kernel_arena, size, flags, low, high, alignment, boundary, VM_MEMATTR_DEFAULT); if (ret != NULL) malloc_type_allocated(type, round_page(size)); @@ -438,7 +432,7 @@ void contigfree(void *addr, unsigned long size, struct malloc_type *type) { - kmem_free(kernel_map, (vm_offset_t)addr, size); + kmem_free(kernel_arena, (vm_offset_t)addr, size); malloc_type_freed(type, round_page(size)); } @@ -681,18 +675,24 @@ reallocf(void *addr, unsigned long size, struct malloc_type *mtp, int flags) } /* - * Initialize the kernel memory allocator + * Wake the page daemon when we exhaust KVA. It will call the lowmem handler + * and uma_reclaim() callbacks in a context that is safe. */ -/* ARGSUSED*/ static void -kmeminit(void *dummy) +kmem_reclaim(vmem_t *vm, int flags) { - uint8_t indx; - u_long mem_size, tmp; - int i; - - mtx_init(&malloc_mtx, "malloc", NULL, MTX_DEF); + pagedaemon_wakeup(); +} + +/* + * Initialize the kernel memory arena. + */ +void +kmeminit(void) +{ + u_long mem_size, tmp; + /* * Try to auto-tune the kernel memory size, so that it is * more applicable for a wider range of machine sizes. The @@ -740,14 +740,15 @@ kmeminit(void *dummy) if (vm_kmem_size / 2 / PAGE_SIZE > mem_size) vm_kmem_size = 2 * mem_size * PAGE_SIZE; + vm_kmem_size = round_page(vm_kmem_size); #ifdef DEBUG_MEMGUARD tmp = memguard_fudge(vm_kmem_size, kernel_map); #else tmp = vm_kmem_size; #endif - kmem_map = kmem_suballoc(kernel_map, &kmembase, &kmemlimit, - tmp, TRUE); - kmem_map->system_map = 1; + vmem_init(kmem_arena, "kmem arena", kva_alloc(tmp), tmp, PAGE_SIZE, + 0, 0); + vmem_set_reclaim(kmem_arena, kmem_reclaim); #ifdef DEBUG_MEMGUARD /* @@ -755,8 +756,23 @@ kmeminit(void *dummy) * replacement allocator used for detecting tamper-after-free * scenarios as they occur. It is only used for debugging. */ - memguard_init(kmem_map); + memguard_init(kmem_arena); #endif +} + +/* + * Initialize the kernel memory allocator + */ +/* ARGSUSED*/ +static void +mallocinit(void *dummy) +{ + int i; + uint8_t indx; + + mtx_init(&malloc_mtx, "malloc", NULL, MTX_DEF); + + kmeminit(); uma_startup2(); @@ -787,6 +803,7 @@ kmeminit(void *dummy) } } +SYSINIT(kmem, SI_SUB_KMEM, SI_ORDER_FIRST, mallocinit, NULL); void malloc_init(void *data) diff --git a/sys/kern/kern_mbuf.c b/sys/kern/kern_mbuf.c index 9e85806b168..5d589425fff 100644 --- a/sys/kern/kern_mbuf.c +++ b/sys/kern/kern_mbuf.c @@ -106,7 +106,7 @@ int nmbjumbo16; /* limits number of 16k jumbo clusters */ static quad_t maxmbufmem; /* overall real memory limit for all mbufs */ SYSCTL_QUAD(_kern_ipc, OID_AUTO, maxmbufmem, CTLFLAG_RDTUN, &maxmbufmem, 0, - "Maximum real memory allocateable to various mbuf types"); + "Maximum real memory allocatable to various mbuf types"); /* * tunable_mbinit() has to be run before any mbuf allocations are done. @@ -121,8 +121,7 @@ tunable_mbinit(void *dummy) * available kernel memory (physical or kmem). * At most it can be 3/4 of available kernel memory. */ - realmem = qmin((quad_t)physmem * PAGE_SIZE, - vm_map_max(kmem_map) - vm_map_min(kmem_map)); + realmem = qmin((quad_t)physmem * PAGE_SIZE, vm_kmem_size); maxmbufmem = realmem / 2; TUNABLE_QUAD_FETCH("kern.ipc.maxmbufmem", &maxmbufmem); if (maxmbufmem > realmem / 4 * 3) @@ -395,7 +394,7 @@ mbuf_jumbo_alloc(uma_zone_t zone, int bytes, uint8_t *flags, int wait) /* Inform UMA that this allocator uses kernel_map/object. */ *flags = UMA_SLAB_KERNEL; - return ((void *)kmem_alloc_contig(kernel_map, bytes, wait, + return ((void *)kmem_alloc_contig(kernel_arena, bytes, wait, (vm_paddr_t)0, ~(vm_paddr_t)0, 1, 0, VM_MEMATTR_DEFAULT)); } @@ -411,18 +410,14 @@ mb_ctor_mbuf(void *mem, int size, void *arg, int how) { struct mbuf *m; struct mb_args *args; -#ifdef MAC int error; -#endif int flags; short type; #ifdef INVARIANTS trash_ctor(mem, size, arg, how); #endif - m = (struct mbuf *)mem; args = (struct mb_args *)arg; - flags = args->flags; type = args->type; /* @@ -432,32 +427,12 @@ mb_ctor_mbuf(void *mem, int size, void *arg, int how) if (type == MT_NOINIT) return (0); - m->m_next = NULL; - m->m_nextpkt = NULL; - m->m_len = 0; - m->m_flags = flags; - m->m_type = type; - if (flags & M_PKTHDR) { - m->m_data = m->m_pktdat; - m->m_pkthdr.rcvif = NULL; - m->m_pkthdr.header = NULL; - m->m_pkthdr.len = 0; - m->m_pkthdr.csum_flags = 0; - m->m_pkthdr.csum_data = 0; - m->m_pkthdr.tso_segsz = 0; - m->m_pkthdr.ether_vtag = 0; - m->m_pkthdr.flowid = 0; - m->m_pkthdr.fibnum = 0; - SLIST_INIT(&m->m_pkthdr.tags); -#ifdef MAC - /* If the label init fails, fail the alloc */ - error = mac_mbuf_init(m, how); - if (error) - return (error); -#endif - } else - m->m_data = m->m_dat; - return (0); + m = (struct mbuf *)mem; + flags = args->flags; + + error = m_init(m, NULL, size, how, type, flags); + + return (error); } /* @@ -472,7 +447,7 @@ mb_dtor_mbuf(void *mem, int size, void *arg) m = (struct mbuf *)mem; flags = (unsigned long)arg; - if ((flags & MB_NOTAGS) == 0 && (m->m_flags & M_PKTHDR) != 0) + if ((m->m_flags & M_PKTHDR) && !SLIST_EMPTY(&m->m_pkthdr.tags)) m_tag_delete_chain(m, NULL); KASSERT((m->m_flags & M_EXT) == 0, ("%s: M_EXT set", __func__)); KASSERT((m->m_flags & M_NOFREE) == 0, ("%s: M_NOFREE set", __func__)); @@ -572,6 +547,7 @@ mb_ctor_clust(void *mem, int size, void *arg, int how) m->m_ext.ext_arg2 = NULL; m->m_ext.ext_size = size; m->m_ext.ext_type = type; + m->m_ext.ext_flags = 0; m->m_ext.ref_cnt = refcnt; } @@ -643,10 +619,7 @@ mb_ctor_pack(void *mem, int size, void *arg, int how) { struct mbuf *m; struct mb_args *args; -#ifdef MAC - int error; -#endif - int flags; + int error, flags; short type; m = (struct mbuf *)mem; @@ -657,34 +630,14 @@ mb_ctor_pack(void *mem, int size, void *arg, int how) #ifdef INVARIANTS trash_ctor(m->m_ext.ext_buf, MCLBYTES, arg, how); #endif - m->m_next = NULL; - m->m_nextpkt = NULL; - m->m_data = m->m_ext.ext_buf; - m->m_len = 0; - m->m_flags = (flags | M_EXT); - m->m_type = type; - if (flags & M_PKTHDR) { - m->m_pkthdr.rcvif = NULL; - m->m_pkthdr.len = 0; - m->m_pkthdr.header = NULL; - m->m_pkthdr.csum_flags = 0; - m->m_pkthdr.csum_data = 0; - m->m_pkthdr.tso_segsz = 0; - m->m_pkthdr.ether_vtag = 0; - m->m_pkthdr.flowid = 0; - m->m_pkthdr.fibnum = 0; - SLIST_INIT(&m->m_pkthdr.tags); -#ifdef MAC - /* If the label init fails, fail the alloc */ - error = mac_mbuf_init(m, how); - if (error) - return (error); -#endif - } + error = m_init(m, NULL, size, how, type, flags); + /* m_ext is already initialized. */ + m->m_data = m->m_ext.ext_buf; + m->m_flags = (flags | M_EXT); - return (0); + return (error); } int @@ -694,16 +647,20 @@ m_pkthdr_init(struct mbuf *m, int how) int error; #endif m->m_data = m->m_pktdat; - SLIST_INIT(&m->m_pkthdr.tags); m->m_pkthdr.rcvif = NULL; - m->m_pkthdr.header = NULL; + SLIST_INIT(&m->m_pkthdr.tags); m->m_pkthdr.len = 0; m->m_pkthdr.flowid = 0; - m->m_pkthdr.fibnum = 0; m->m_pkthdr.csum_flags = 0; - m->m_pkthdr.csum_data = 0; - m->m_pkthdr.tso_segsz = 0; - m->m_pkthdr.ether_vtag = 0; + m->m_pkthdr.fibnum = 0; + m->m_pkthdr.cosqos = 0; + m->m_pkthdr.rsstype = 0; + m->m_pkthdr.l2hlen = 0; + m->m_pkthdr.l3hlen = 0; + m->m_pkthdr.l4hlen = 0; + m->m_pkthdr.l5hlen = 0; + m->m_pkthdr.PH_per.sixtyfour[0] = 0; + m->m_pkthdr.PH_loc.sixtyfour[0] = 0; #ifdef MAC /* If the label init fails, fail the alloc */ error = mac_mbuf_init(m, how); diff --git a/sys/kern/kern_mutex.c b/sys/kern/kern_mutex.c index a613da8950b..cd1ed7dbf4e 100644 --- a/sys/kern/kern_mutex.c +++ b/sys/kern/kern_mutex.c @@ -218,13 +218,14 @@ __mtx_lock_flags(volatile uintptr_t *c, int opts, const char *file, int line) KASSERT(LOCK_CLASS(&m->lock_object) == &lock_class_mtx_sleep, ("mtx_lock() of spin mutex %s @ %s:%d", m->lock_object.lo_name, file, line)); - WITNESS_CHECKORDER(&m->lock_object, opts | LOP_NEWORDER | LOP_EXCLUSIVE, - file, line, NULL); + WITNESS_CHECKORDER(&m->lock_object, (opts & ~MTX_RECURSE) | + LOP_NEWORDER | LOP_EXCLUSIVE, file, line, NULL); __mtx_lock(m, curthread, opts, file, line); LOCK_LOG_LOCK("LOCK", &m->lock_object, opts, m->mtx_recurse, file, line); - WITNESS_LOCK(&m->lock_object, opts | LOP_EXCLUSIVE, file, line); + WITNESS_LOCK(&m->lock_object, (opts & ~MTX_RECURSE) | LOP_EXCLUSIVE, + file, line); curthread->td_locks++; } @@ -271,9 +272,11 @@ __mtx_lock_spin_flags(volatile uintptr_t *c, int opts, const char *file, ("mtx_lock_spin() of sleep mutex %s @ %s:%d", m->lock_object.lo_name, file, line)); if (mtx_owned(m)) - KASSERT((m->lock_object.lo_flags & LO_RECURSABLE) != 0, + KASSERT((m->lock_object.lo_flags & LO_RECURSABLE) != 0 || + (opts & MTX_RECURSE) != 0, ("mtx_lock_spin: recursed on non-recursive mutex %s @ %s:%d\n", m->lock_object.lo_name, file, line)); + opts &= ~MTX_RECURSE; WITNESS_CHECKORDER(&m->lock_object, opts | LOP_NEWORDER | LOP_EXCLUSIVE, file, line, NULL); __mtx_lock_spin(m, curthread, opts, file, line); @@ -335,12 +338,14 @@ _mtx_trylock_flags_(volatile uintptr_t *c, int opts, const char *file, int line) ("mtx_trylock() of spin mutex %s @ %s:%d", m->lock_object.lo_name, file, line)); - if (mtx_owned(m) && (m->lock_object.lo_flags & LO_RECURSABLE) != 0) { + if (mtx_owned(m) && ((m->lock_object.lo_flags & LO_RECURSABLE) != 0 || + (opts & MTX_RECURSE) != 0)) { m->mtx_recurse++; atomic_set_ptr(&m->mtx_lock, MTX_RECURSED); rval = 1; } else rval = _mtx_obtain_lock(m, (uintptr_t)curthread); + opts &= ~MTX_RECURSE; LOCK_LOG_TRY("LOCK", &m->lock_object, opts, rval, file, line); if (rval) { @@ -391,15 +396,18 @@ __mtx_lock_sleep(volatile uintptr_t *c, uintptr_t tid, int opts, m = mtxlock2mtx(c); if (mtx_owned(m)) { - KASSERT((m->lock_object.lo_flags & LO_RECURSABLE) != 0, + KASSERT((m->lock_object.lo_flags & LO_RECURSABLE) != 0 || + (opts & MTX_RECURSE) != 0, ("_mtx_lock_sleep: recursed on non-recursive mutex %s @ %s:%d\n", m->lock_object.lo_name, file, line)); + opts &= ~MTX_RECURSE; m->mtx_recurse++; atomic_set_ptr(&m->mtx_lock, MTX_RECURSED); if (LOCK_LOG_TEST(&m->lock_object, opts)) CTR1(KTR_LOCK, "_mtx_lock_sleep: %p recursing", m); return; } + opts &= ~MTX_RECURSE; #ifdef HWPMC_HOOKS PMC_SOFT_CALL( , , lock, failed); diff --git a/sys/kern/kern_physio.c b/sys/kern/kern_physio.c index 1387c8e7945..b37b9f3074c 100644 --- a/sys/kern/kern_physio.c +++ b/sys/kern/kern_physio.c @@ -54,6 +54,33 @@ physio(struct cdev *dev, struct uio *uio, int ioflag) dev->si_iosize_max = DFLTPHYS; } + /* + * If the driver does not want I/O to be split, that means that we + * need to reject any requests that will not fit into one buffer. + */ + if (dev->si_flags & SI_NOSPLIT && + (uio->uio_resid > dev->si_iosize_max || uio->uio_resid > MAXPHYS || + uio->uio_iovcnt > 1)) { + /* + * Tell the user why his I/O was rejected. + */ + if (uio->uio_resid > dev->si_iosize_max) + uprintf("%s: request size=%zd > si_iosize_max=%d; " + "cannot split request\n", devtoname(dev), + uio->uio_resid, dev->si_iosize_max); + if (uio->uio_resid > MAXPHYS) + uprintf("%s: request size=%zd > MAXPHYS=%d; " + "cannot split request\n", devtoname(dev), + uio->uio_resid, MAXPHYS); + if (uio->uio_iovcnt > 1) + uprintf("%s: request vectors=%d > 1; " + "cannot split request\n", devtoname(dev), + uio->uio_iovcnt); + + error = EFBIG; + goto doerror; + } + for (i = 0; i < uio->uio_iovcnt; i++) { while (uio->uio_iov[i].iov_len) { bp->b_flags = 0; @@ -83,6 +110,17 @@ physio(struct cdev *dev, struct uio *uio, int ioflag) */ iolen = ((vm_offset_t) bp->b_data) & PAGE_MASK; if ((bp->b_bcount + iolen) > bp->b_kvasize) { + /* + * This device does not want I/O to be split. + */ + if (dev->si_flags & SI_NOSPLIT) { + uprintf("%s: request ptr %p is not " + "on a page boundary; cannot split " + "request\n", devtoname(dev), + bp->b_data); + error = EFBIG; + goto doerror; + } bp->b_bcount = bp->b_kvasize; if (iolen != 0) bp->b_bcount -= PAGE_SIZE; @@ -93,8 +131,7 @@ physio(struct cdev *dev, struct uio *uio, int ioflag) csw = dev->si_devsw; if (uio->uio_segflg == UIO_USERSPACE) { - if (csw != NULL && - (csw->d_flags & D_UNMAPPED_IO) != 0) + if (dev->si_flags & SI_UNMAPPED) mapped = 0; else mapped = 1; diff --git a/sys/kern/kern_poll.c b/sys/kern/kern_poll.c index a79161ece5c..349f338f48a 100644 --- a/sys/kern/kern_poll.c +++ b/sys/kern/kern_poll.c @@ -169,7 +169,7 @@ static int user_frac_sysctl(SYSCTL_HANDLER_ARGS) error = sysctl_handle_int(oidp, &val, 0, req); if (error || !req->newptr ) return (error); - if (val < 0 || val > 99) + if (val > 99) return (EINVAL); mtx_lock(&poll_mtx); diff --git a/sys/kern/kern_proc.c b/sys/kern/kern_proc.c index cb1ec9d4b36..3fa7a7f811d 100644 --- a/sys/kern/kern_proc.c +++ b/sys/kern/kern_proc.c @@ -92,33 +92,18 @@ __FBSDID("$FreeBSD$"); #endif SDT_PROVIDER_DEFINE(proc); -SDT_PROBE_DEFINE(proc, kernel, ctor, entry, entry); -SDT_PROBE_ARGTYPE(proc, kernel, ctor, entry, 0, "struct proc *"); -SDT_PROBE_ARGTYPE(proc, kernel, ctor, entry, 1, "int"); -SDT_PROBE_ARGTYPE(proc, kernel, ctor, entry, 2, "void *"); -SDT_PROBE_ARGTYPE(proc, kernel, ctor, entry, 3, "int"); -SDT_PROBE_DEFINE(proc, kernel, ctor, return, return); -SDT_PROBE_ARGTYPE(proc, kernel, ctor, return, 0, "struct proc *"); -SDT_PROBE_ARGTYPE(proc, kernel, ctor, return, 1, "int"); -SDT_PROBE_ARGTYPE(proc, kernel, ctor, return, 2, "void *"); -SDT_PROBE_ARGTYPE(proc, kernel, ctor, return, 3, "int"); -SDT_PROBE_DEFINE(proc, kernel, dtor, entry, entry); -SDT_PROBE_ARGTYPE(proc, kernel, dtor, entry, 0, "struct proc *"); -SDT_PROBE_ARGTYPE(proc, kernel, dtor, entry, 1, "int"); -SDT_PROBE_ARGTYPE(proc, kernel, dtor, entry, 2, "void *"); -SDT_PROBE_ARGTYPE(proc, kernel, dtor, entry, 3, "struct thread *"); -SDT_PROBE_DEFINE(proc, kernel, dtor, return, return); -SDT_PROBE_ARGTYPE(proc, kernel, dtor, return, 0, "struct proc *"); -SDT_PROBE_ARGTYPE(proc, kernel, dtor, return, 1, "int"); -SDT_PROBE_ARGTYPE(proc, kernel, dtor, return, 2, "void *"); -SDT_PROBE_DEFINE(proc, kernel, init, entry, entry); -SDT_PROBE_ARGTYPE(proc, kernel, init, entry, 0, "struct proc *"); -SDT_PROBE_ARGTYPE(proc, kernel, init, entry, 1, "int"); -SDT_PROBE_ARGTYPE(proc, kernel, init, entry, 2, "int"); -SDT_PROBE_DEFINE(proc, kernel, init, return, return); -SDT_PROBE_ARGTYPE(proc, kernel, init, return, 0, "struct proc *"); -SDT_PROBE_ARGTYPE(proc, kernel, init, return, 1, "int"); -SDT_PROBE_ARGTYPE(proc, kernel, init, return, 2, "int"); +SDT_PROBE_DEFINE4(proc, kernel, ctor, entry, entry, "struct proc *", "int", + "void *", "int"); +SDT_PROBE_DEFINE4(proc, kernel, ctor, return, return, "struct proc *", "int", + "void *", "int"); +SDT_PROBE_DEFINE4(proc, kernel, dtor, entry, entry, "struct proc *", "int", + "void *", "struct thread *"); +SDT_PROBE_DEFINE3(proc, kernel, dtor, return, return, "struct proc *", "int", + "void *"); +SDT_PROBE_DEFINE3(proc, kernel, init, entry, entry, "struct proc *", "int", + "int"); +SDT_PROBE_DEFINE3(proc, kernel, init, return, return, "struct proc *", "int", + "int"); MALLOC_DEFINE(M_PGRP, "pgrp", "process group header"); MALLOC_DEFINE(M_SESSION, "session", "session header"); @@ -877,6 +862,7 @@ fill_kinfo_proc_only(struct proc *p, struct kinfo_proc *kp) kp->ki_swtime = (ticks - p->p_swtick) / hz; kp->ki_pid = p->p_pid; kp->ki_nice = p->p_nice; + kp->ki_fibnum = p->p_fibnum; kp->ki_start = p->p_stats->p_start; timevaladd(&kp->ki_start, &boottime); PROC_SLOCK(p); @@ -1175,6 +1161,7 @@ freebsd32_kinfo_proc_out(const struct kinfo_proc *ki, struct kinfo_proc32 *ki32) bcopy(ki->ki_comm, ki32->ki_comm, COMMLEN + 1); bcopy(ki->ki_emul, ki32->ki_emul, KI_EMULNAMELEN + 1); bcopy(ki->ki_loginclass, ki32->ki_loginclass, LOGINCLASSLEN + 1); + CP(*ki, *ki32, ki_fibnum); CP(*ki, *ki32, ki_cr_flags); CP(*ki, *ki32, ki_jid); CP(*ki, *ki32, ki_numthreads); diff --git a/sys/kern/kern_rangelock.c b/sys/kern/kern_rangelock.c index 1b4dfd63966..1c0faa3c068 100644 --- a/sys/kern/kern_rangelock.c +++ b/sys/kern/kern_rangelock.c @@ -84,20 +84,14 @@ rangelock_destroy(struct rangelock *lock) } /* - * Verifies the supplied rl_q_entries for compatibility. Returns true - * if the rangelock queue entries are not compatible, false if they are. - * * Two entries are compatible if their ranges do not overlap, or both * entries are for read. */ static int -rangelock_incompatible(const struct rl_q_entry *e1, +ranges_overlap(const struct rl_q_entry *e1, const struct rl_q_entry *e2) { - if ((e1->rl_q_flags & RL_LOCK_TYPE_MASK) == RL_LOCK_READ && - (e2->rl_q_flags & RL_LOCK_TYPE_MASK) == RL_LOCK_READ) - return (0); if (e1->rl_q_start < e2->rl_q_end && e1->rl_q_end > e2->rl_q_start) return (1); return (0); @@ -109,30 +103,38 @@ rangelock_incompatible(const struct rl_q_entry *e1, static void rangelock_calc_block(struct rangelock *lock) { - struct rl_q_entry *entry, *entry1, *whead; + struct rl_q_entry *entry, *nextentry, *entry1; - if (lock->rl_currdep == TAILQ_FIRST(&lock->rl_waiters) && - lock->rl_currdep != NULL) - lock->rl_currdep = TAILQ_NEXT(lock->rl_currdep, rl_q_link); - for (entry = lock->rl_currdep; entry != NULL; - entry = TAILQ_NEXT(entry, rl_q_link)) { - TAILQ_FOREACH(entry1, &lock->rl_waiters, rl_q_link) { - if (rangelock_incompatible(entry, entry1)) - goto out; - if (entry1 == entry) - break; + for (entry = lock->rl_currdep; entry != NULL; entry = nextentry) { + nextentry = TAILQ_NEXT(entry, rl_q_link); + if (entry->rl_q_flags & RL_LOCK_READ) { + /* Reads must not overlap with granted writes. */ + for (entry1 = TAILQ_FIRST(&lock->rl_waiters); + !(entry1->rl_q_flags & RL_LOCK_READ); + entry1 = TAILQ_NEXT(entry1, rl_q_link)) { + if (ranges_overlap(entry, entry1)) + goto out; + } + } else { + /* Write must not overlap with any granted locks. */ + for (entry1 = TAILQ_FIRST(&lock->rl_waiters); + entry1 != entry; + entry1 = TAILQ_NEXT(entry1, rl_q_link)) { + if (ranges_overlap(entry, entry1)) + goto out; + } + + /* Move grantable write locks to the front. */ + TAILQ_REMOVE(&lock->rl_waiters, entry, rl_q_link); + TAILQ_INSERT_HEAD(&lock->rl_waiters, entry, rl_q_link); } + + /* Grant this lock. */ + entry->rl_q_flags |= RL_LOCK_GRANTED; + wakeup(entry); } out: lock->rl_currdep = entry; - TAILQ_FOREACH(whead, &lock->rl_waiters, rl_q_link) { - if (whead == lock->rl_currdep) - break; - if (!(whead->rl_q_flags & RL_LOCK_GRANTED)) { - whead->rl_q_flags |= RL_LOCK_GRANTED; - wakeup(whead); - } - } } static void diff --git a/sys/kern/kern_sdt.c b/sys/kern/kern_sdt.c index ee4addee0c1..c8e1940fbd7 100644 --- a/sys/kern/kern_sdt.c +++ b/sys/kern/kern_sdt.c @@ -23,317 +23,29 @@ * SUCH DAMAGE. * * $FreeBSD$ - * - * Backend for the Statically Defined Tracing (SDT) kernel support. This is - * required to allow a module to load even though DTrace kernel support may - * not be present. A module may be built with SDT probes in it which are - * registered and deregistered via SYSINIT/SYSUNINIT. - * */ #include "opt_kdtrace.h" -#include #include #include -#include -#include -#include -#include -#include #include /* - * This is the list of statically defined tracing providers. - */ -static TAILQ_HEAD(sdt_provider_list_head, sdt_provider) sdt_provider_list; - -/* - * Mutex to serialise access to the SDT provider list. - */ -static struct sx sdt_sx; - -/* - * Hook for the DTrace probe function. The 'sdt' provider will set this - * to dtrace_probe when it loads. + * Hook for the DTrace probe function. The SDT provider will set this to + * dtrace_probe() when it loads. */ sdt_probe_func_t sdt_probe_func = sdt_probe_stub; -static sdt_provider_listall_func_t sdt_provider_register_func = NULL; -static sdt_provider_listall_func_t sdt_provider_deregister_func = NULL; -static sdt_probe_listall_func_t sdt_probe_register_func = NULL; - -static void *sdt_provider_register_arg; -static void *sdt_provider_deregister_arg; -static void *sdt_probe_register_arg; - -static int sdt_provider_listall_locked(sdt_provider_listall_func_t, void *); - /* * This is a stub for probe calls in case kernel DTrace support isn't - * compiled in. It should never get called because there is no DTrace - * support to enable it. + * enabled. It should never get called because there is no DTrace support + * to enable it. */ void sdt_probe_stub(uint32_t id, uintptr_t arg0, uintptr_t arg1, uintptr_t arg2, uintptr_t arg3, uintptr_t arg4) { + printf("sdt_probe_stub: Why did this get called?\n"); } - -/* - * Called from SYSINIT to register a provider. - */ -void -sdt_provider_register(void *arg) -{ - struct sdt_provider *prov = arg; - - sx_xlock(&sdt_sx); - - TAILQ_INSERT_TAIL(&sdt_provider_list, prov, prov_entry); - - TAILQ_INIT(&prov->probe_list); - - if (sdt_provider_register_func != NULL) - sdt_provider_register_func(prov, sdt_provider_register_arg); - - sx_xunlock(&sdt_sx); -} - -/* - * Called from SYSUNINIT to de-register a provider. - */ -void -sdt_provider_deregister(void *arg) -{ - struct sdt_provider *prov = arg; - - sx_xlock(&sdt_sx); - - TAILQ_REMOVE(&sdt_provider_list, prov, prov_entry); - - if (sdt_provider_deregister_func != NULL) - sdt_provider_deregister_func(prov, sdt_provider_deregister_arg); - - sx_xunlock(&sdt_sx); -} - -/* - * Called from SYSINIT to register a statically defined trace probe. - */ -void -sdt_probe_register(void *arg) -{ - struct sdt_probe *probe = arg; - - /* - * Check the reference structure version. Only version 1 is - * supported at the moment. - */ - if (probe->version != sizeof(struct sdt_probe)) { - printf("%s:%s:%s has version %d when %d required\n", probe->mod, probe->func, probe->name, probe->version, (int) sizeof(struct sdt_probe)); - return; - } - - sx_xlock(&sdt_sx); - - TAILQ_INSERT_TAIL(&probe->prov->probe_list, probe, probe_entry); - - TAILQ_INIT(&probe->argtype_list); - - probe->state = SDT_INIT; - - if (sdt_probe_register_func != NULL) - sdt_probe_register_func(probe, sdt_provider_register_arg); - - sx_xunlock(&sdt_sx); -} - -/* - * Called from SYSUNINIT to de-register a statically defined trace probe. - */ -void -sdt_probe_deregister(void *arg) -{ - struct sdt_probe *probe = arg; - - sx_xlock(&sdt_sx); - - if (probe->state == SDT_INIT) { - TAILQ_REMOVE(&probe->prov->probe_list, probe, probe_entry); - probe->state = SDT_UNINIT; - } - - sx_xunlock(&sdt_sx); -} - -/* - * Called from SYSINIT to register a statically defined trace probe argument. - */ -void -sdt_argtype_register(void *arg) -{ - struct sdt_argtype *argtype = arg; - - sx_xlock(&sdt_sx); - - TAILQ_INSERT_TAIL(&argtype->probe->argtype_list, argtype, argtype_entry); - - argtype->probe->n_args++; - - sx_xunlock(&sdt_sx); -} - -/* - * Called from SYSUNINIT to de-register a statically defined trace probe argument. - */ -void -sdt_argtype_deregister(void *arg) -{ - struct sdt_argtype *argtype = arg; - - sx_xlock(&sdt_sx); - - TAILQ_REMOVE(&argtype->probe->argtype_list, argtype, argtype_entry); - - sx_xunlock(&sdt_sx); -} - -static void -sdt_init(void *arg) -{ - sx_init_flags(&sdt_sx, "Statically Defined Tracing", SX_NOWITNESS); - - TAILQ_INIT(&sdt_provider_list); -} - -SYSINIT(sdt, SI_SUB_KDTRACE, SI_ORDER_FIRST, sdt_init, NULL); - -static void -sdt_uninit(void *arg) -{ - sx_destroy(&sdt_sx); -} - -SYSUNINIT(sdt, SI_SUB_KDTRACE, SI_ORDER_FIRST, sdt_uninit, NULL); - -/* - * List statically defined tracing providers. - */ -int -sdt_provider_listall(sdt_provider_listall_func_t callback_func, void *arg) -{ - int error; - - sx_xlock(&sdt_sx); - error = sdt_provider_listall_locked(callback_func, arg); - sx_xunlock(&sdt_sx); - - return (error); -} - -static int -sdt_provider_listall_locked(sdt_provider_listall_func_t callback_func, - void *arg) -{ - int error = 0; - struct sdt_provider *prov; - - sx_assert(&sdt_sx, SX_XLOCKED); - - TAILQ_FOREACH(prov, &sdt_provider_list, prov_entry) { - if ((error = callback_func(prov, arg)) != 0) - break; - } - - return (error); -} - -/* - * List statically defined tracing probes. - */ -int -sdt_probe_listall(struct sdt_provider *prov, - sdt_probe_listall_func_t callback_func,void *arg) -{ - int error = 0; - int locked; - struct sdt_probe *probe; - - locked = sx_xlocked(&sdt_sx); - if (!locked) - sx_xlock(&sdt_sx); - - TAILQ_FOREACH(probe, &prov->probe_list, probe_entry) { - if ((error = callback_func(probe, arg)) != 0) - break; - } - - if (!locked) - sx_xunlock(&sdt_sx); - - return (error); -} - -/* - * List statically defined tracing probe arguments. - */ -int -sdt_argtype_listall(struct sdt_probe *probe, - sdt_argtype_listall_func_t callback_func,void *arg) -{ - int error = 0; - int locked; - struct sdt_argtype *argtype; - - locked = sx_xlocked(&sdt_sx); - if (!locked) - sx_xlock(&sdt_sx); - - TAILQ_FOREACH(argtype, &probe->argtype_list, argtype_entry) { - if ((error = callback_func(argtype, arg)) != 0) - break; - } - - if (!locked) - sx_xunlock(&sdt_sx); - - return (error); -} - -void sdt_register_callbacks(sdt_provider_listall_func_t register_prov, - void *reg_prov_arg, sdt_provider_listall_func_t deregister_prov, - void *dereg_prov_arg, sdt_probe_listall_func_t register_probe, - void * reg_probe_arg) -{ - - sx_xlock(&sdt_sx); - sdt_provider_register_func = register_prov; - sdt_provider_deregister_func = deregister_prov; - sdt_probe_register_func = register_probe; - - sdt_provider_register_arg = reg_prov_arg; - sdt_provider_deregister_arg = dereg_prov_arg; - sdt_probe_register_arg = reg_probe_arg; - - sdt_provider_listall_locked(register_prov, reg_prov_arg); - sx_xunlock(&sdt_sx); -} - -void sdt_deregister_callbacks(void) -{ - - sx_xlock(&sdt_sx); - sdt_provider_listall_locked(sdt_provider_deregister_func, - sdt_provider_deregister_arg); - - sdt_provider_register_func = NULL; - sdt_provider_deregister_func = NULL; - sdt_probe_register_func = NULL; - - sdt_provider_register_arg = NULL; - sdt_provider_deregister_arg = NULL; - sdt_probe_register_arg = NULL; - sx_xunlock(&sdt_sx); -} diff --git a/sys/kern/kern_sharedpage.c b/sys/kern/kern_sharedpage.c index 20b903865e2..fd619cd32e0 100644 --- a/sys/kern/kern_sharedpage.c +++ b/sys/kern/kern_sharedpage.c @@ -108,11 +108,10 @@ shared_page_init(void *dummy __unused) shared_page_obj = vm_pager_allocate(OBJT_PHYS, 0, PAGE_SIZE, VM_PROT_DEFAULT, 0, NULL); VM_OBJECT_WLOCK(shared_page_obj); - m = vm_page_grab(shared_page_obj, 0, VM_ALLOC_RETRY | VM_ALLOC_NOBUSY | - VM_ALLOC_ZERO); + m = vm_page_grab(shared_page_obj, 0, VM_ALLOC_NOBUSY | VM_ALLOC_ZERO); m->valid = VM_PAGE_BITS_ALL; VM_OBJECT_WUNLOCK(shared_page_obj); - addr = kmem_alloc_nofault(kernel_map, PAGE_SIZE); + addr = kva_alloc(PAGE_SIZE); pmap_qenter(addr, &m, 1); shared_page_mapping = (char *)addr; } diff --git a/sys/kern/kern_sig.c b/sys/kern/kern_sig.c index 09dc2ca583d..1797ebc7dd4 100644 --- a/sys/kern/kern_sig.c +++ b/sys/kern/kern_sig.c @@ -93,17 +93,12 @@ __FBSDID("$FreeBSD$"); #define ONSIG 32 /* NSIG for osig* syscalls. XXX. */ SDT_PROVIDER_DECLARE(proc); -SDT_PROBE_DEFINE(proc, kernel, , signal_send, signal-send); -SDT_PROBE_ARGTYPE(proc, kernel, , signal_send, 0, "struct thread *"); -SDT_PROBE_ARGTYPE(proc, kernel, , signal_send, 1, "struct proc *"); -SDT_PROBE_ARGTYPE(proc, kernel, , signal_send, 2, "int"); -SDT_PROBE_DEFINE(proc, kernel, , signal_clear, signal-clear); -SDT_PROBE_ARGTYPE(proc, kernel, , signal_clear, 0, "int"); -SDT_PROBE_ARGTYPE(proc, kernel, , signal_clear, 1, "ksiginfo_t *"); -SDT_PROBE_DEFINE(proc, kernel, , signal_discard, signal-discard); -SDT_PROBE_ARGTYPE(proc, kernel, , signal_discard, 0, "struct thread *"); -SDT_PROBE_ARGTYPE(proc, kernel, , signal_discard, 1, "struct proc *"); -SDT_PROBE_ARGTYPE(proc, kernel, , signal_discard, 2, "int"); +SDT_PROBE_DEFINE3(proc, kernel, , signal_send, signal-send, "struct thread *", + "struct proc *", "int"); +SDT_PROBE_DEFINE2(proc, kernel, , signal_clear, signal-clear, "int", + "ksiginfo_t *"); +SDT_PROBE_DEFINE3(proc, kernel, , signal_discard, signal-discard, + "struct thread *", "struct proc *", "int"); static int coredump(struct thread *); static int killpg1(struct thread *td, int sig, int pgid, int all, @@ -1731,6 +1726,7 @@ sys_pdkill(td, uap) { #ifdef PROCDESC struct proc *p; + cap_rights_t rights; int error; AUDIT_ARG_SIGNUM(uap->signum); @@ -1738,7 +1734,8 @@ sys_pdkill(td, uap) if ((u_int)uap->signum > _SIG_MAXSIG) return (EINVAL); - error = procdesc_find(td, uap->fd, CAP_PDKILL, &p); + error = procdesc_find(td, uap->fd, + cap_rights_init(&rights, CAP_PDKILL), &p); if (error) return (error); AUDIT_ARG_PROCESS(p); diff --git a/sys/kern/kern_synch.c b/sys/kern/kern_synch.c index fb9c9bfcf5c..b0e19082c91 100644 --- a/sys/kern/kern_synch.c +++ b/sys/kern/kern_synch.c @@ -121,14 +121,20 @@ SDT_PROBE_DEFINE(sched, , , schedctl_nopreempt, schedctl-nopreempt); SDT_PROBE_DEFINE(sched, , , schedctl_preempt, schedctl-preempt); SDT_PROBE_DEFINE(sched, , , schedctl_yield, schedctl-yield); -void -sleepinit(void) +static void +sleepinit(void *unused) { hogticks = (hz / 10) * 2; /* Default only. */ init_sleepqueues(); } +/* + * vmem tries to lock the sleepq mutexes when free'ing kva, so make sure + * it is available. + */ +SYSINIT(sleepinit, SI_SUB_KMEM, SI_ORDER_ANY, sleepinit, 0); + /* * General sleep call. Suspends the current thread until a wakeup is * performed on the specified identifier. The thread will then be made @@ -350,10 +356,7 @@ msleep_spin_sbt(void *ident, struct mtx *mtx, const char *wmesg, int pause_sbt(const char *wmesg, sbintime_t sbt, sbintime_t pr, int flags) { - int sbt_sec; - - sbt_sec = sbintime_getsec(sbt); - KASSERT(sbt_sec >= 0, ("pause: timo must be >= 0")); + KASSERT(sbt >= 0, ("pause: timeout must be >= 0")); /* silently convert invalid timeouts */ if (sbt == 0) @@ -364,11 +367,14 @@ pause_sbt(const char *wmesg, sbintime_t sbt, sbintime_t pr, int flags) * We delay one second at a time to avoid overflowing the * system specific DELAY() function(s): */ - while (sbt_sec > 0) { + while (sbt >= SBT_1S) { DELAY(1000000); - sbt_sec--; + sbt -= SBT_1S; } - DELAY((sbt & 0xffffffff) / SBT_1US); + /* Do the delay remainder, if any */ + sbt = (sbt + SBT_1US - 1) / SBT_1US; + if (sbt > 0) + DELAY(sbt); return (0); } return (_sleep(&pause_wchan[curcpu], NULL, 0, wmesg, sbt, pr, flags)); diff --git a/sys/kern/kern_sysctl.c b/sys/kern/kern_sysctl.c index 68bf453f8c1..416f85f9b01 100644 --- a/sys/kern/kern_sysctl.c +++ b/sys/kern/kern_sysctl.c @@ -412,8 +412,12 @@ sysctl_remove_oid_locked(struct sysctl_oid *oidp, int del, int recurse) if (oidp->oid_refcnt == 1) { SLIST_FOREACH_SAFE(p, SYSCTL_CHILDREN(oidp), oid_link, tmp) { - if (!recurse) + if (!recurse) { + printf("Warning: failed attempt to " + "remove oid %s with child %s\n", + oidp->oid_name, p->oid_name); return (ENOTEMPTY); + } error = sysctl_remove_oid_locked(p, del, recurse); if (error) diff --git a/sys/kern/kern_timeout.c b/sys/kern/kern_timeout.c index f2e27d425bf..e3580fcd40b 100644 --- a/sys/kern/kern_timeout.c +++ b/sys/kern/kern_timeout.c @@ -69,11 +69,9 @@ DPCPU_DECLARE(sbintime_t, hardclocktime); #endif SDT_PROVIDER_DEFINE(callout_execute); -SDT_PROBE_DEFINE(callout_execute, kernel, , callout_start, callout-start); -SDT_PROBE_ARGTYPE(callout_execute, kernel, , callout_start, 0, +SDT_PROBE_DEFINE1(callout_execute, kernel, , callout_start, callout-start, "struct callout *"); -SDT_PROBE_DEFINE(callout_execute, kernel, , callout_end, callout-end); -SDT_PROBE_ARGTYPE(callout_execute, kernel, , callout_end, 0, +SDT_PROBE_DEFINE1(callout_execute, kernel, , callout_end, callout-end, "struct callout *"); #ifdef CALLOUT_PROFILING diff --git a/sys/kern/link_elf.c b/sys/kern/link_elf.c index 647ae0b0956..631ba752b7d 100644 --- a/sys/kern/link_elf.c +++ b/sys/kern/link_elf.c @@ -702,16 +702,6 @@ link_elf_link_preload_finish(linker_file_t lf) int error; ef = (elf_file_t) lf; -#if 0 /* this will be more trouble than it's worth for now */ - for (dp = ef->dynamic; dp->d_tag != DT_NULL; dp++) { - if (dp->d_tag != DT_NEEDED) - continue; - modname = ef->strtab + dp->d_un.d_val; - error = linker_load_module(modname, lf); - if (error != 0) - goto out; - } -#endif error = relocate_file(ef); if (error != 0) return (error); @@ -901,7 +891,7 @@ link_elf_load_file(linker_class_t cls, const char* filename, } ef->address = (caddr_t) vm_map_min(kernel_map); error = vm_map_find(kernel_map, ef->object, 0, - (vm_offset_t *) &ef->address, mapsize, 1, + (vm_offset_t *) &ef->address, mapsize, 0, VMFS_OPTIMAL_SPACE, VM_PROT_ALL, VM_PROT_ALL, 0); if (error != 0) { vm_object_deallocate(ef->object); @@ -973,16 +963,6 @@ link_elf_load_file(linker_class_t cls, const char* filename, vn_lock(nd.ni_vp, LK_EXCLUSIVE | LK_RETRY); if (error != 0) goto out; -#if 0 /* this will be more trouble than it's worth for now */ - for (dp = ef->dynamic; dp->d_tag != DT_NULL; dp++) { - if (dp->d_tag != DT_NEEDED) - continue; - modname = ef->strtab + dp->d_un.d_val; - error = linker_load_module(modname, lf); - if (error != 0) - goto out; - } -#endif error = relocate_file(ef); if (error != 0) goto out; diff --git a/sys/kern/link_elf_obj.c b/sys/kern/link_elf_obj.c index a9208df3a65..0334779afac 100644 --- a/sys/kern/link_elf_obj.c +++ b/sys/kern/link_elf_obj.c @@ -689,7 +689,8 @@ link_elf_load_file(linker_class_t cls, const char *filename, mapbase = VM_MIN_KERNEL_ADDRESS; #endif error = vm_map_find(kernel_map, ef->object, 0, &mapbase, - round_page(mapsize), TRUE, VM_PROT_ALL, VM_PROT_ALL, FALSE); + round_page(mapsize), 0, VMFS_OPTIMAL_SPACE, VM_PROT_ALL, + VM_PROT_ALL, 0); if (error) { vm_object_deallocate(ef->object); ef->object = 0; diff --git a/sys/kern/makesyscalls.sh b/sys/kern/makesyscalls.sh index 21e60467ba8..4345e86d5c4 100644 --- a/sys/kern/makesyscalls.sh +++ b/sys/kern/makesyscalls.sh @@ -156,7 +156,8 @@ s/\$//g printf "#include \n" > sysarg printf "#include \n" > sysarg printf "#include \n" > sysarg - printf "#include \n\n" > sysarg + printf "#include \n" > sysarg + printf "#include \n\n" > sysarg printf "#include \n\n" > sysarg printf "struct proc;\n\n" > sysarg printf "struct thread;\n\n" > sysarg diff --git a/sys/kern/sched_ule.c b/sys/kern/sched_ule.c index 95105d80475..cba9d804a3a 100644 --- a/sys/kern/sched_ule.c +++ b/sys/kern/sched_ule.c @@ -667,10 +667,14 @@ cpu_search(const struct cpu_group *cg, struct cpu_search *low, } /* Iterate through the child CPU groups and then remaining CPUs. */ - for (i = cg->cg_children, cpu = mp_maxid; i >= 0; ) { + for (i = cg->cg_children, cpu = mp_maxid; ; ) { if (i == 0) { +#ifdef HAVE_INLINE_FFSL + cpu = CPU_FFS(&cpumask) - 1; +#else while (cpu >= 0 && !CPU_ISSET(cpu, &cpumask)) cpu--; +#endif if (cpu < 0) break; child = NULL; @@ -695,6 +699,7 @@ cpu_search(const struct cpu_group *cg, struct cpu_search *low, break; } } else { /* Handle child CPU. */ + CPU_CLR(cpu, &cpumask); tdq = TDQ_CPU(cpu); load = tdq->tdq_load * 256; rndptr = DPCPU_PTR(randomval); @@ -742,8 +747,11 @@ cpu_search(const struct cpu_group *cg, struct cpu_search *low, i--; if (i == 0 && CPU_EMPTY(&cpumask)) break; - } else + } +#ifndef HAVE_INLINE_FFSL + else cpu--; +#endif } return (total); } diff --git a/sys/kern/subr_bus.c b/sys/kern/subr_bus.c index 717ded44df4..b3b1852cada 100644 --- a/sys/kern/subr_bus.c +++ b/sys/kern/subr_bus.c @@ -2077,11 +2077,11 @@ device_probe_child(device_t dev, device_t child) if (best == NULL || result > pri) { /* * Probes that return BUS_PROBE_NOWILDCARD - * or lower only match when they are set - * in stone by the parent bus. + * or lower only match on devices whose + * driver was explicitly specified. */ if (result <= BUS_PROBE_NOWILDCARD && - child->flags & DF_WILDCARD) + !(child->flags & DF_FIXEDCLASS)) continue; best = dl; pri = result; diff --git a/sys/kern/subr_busdma_bufalloc.c b/sys/kern/subr_busdma_bufalloc.c index 9406d9593d6..a80a233f53b 100644 --- a/sys/kern/subr_busdma_bufalloc.c +++ b/sys/kern/subr_busdma_bufalloc.c @@ -152,10 +152,10 @@ busdma_bufalloc_alloc_uncacheable(uma_zone_t zone, int size, u_int8_t *pflag, { #ifdef VM_MEMATTR_UNCACHEABLE - /* Inform UMA that this allocator uses kernel_map/object. */ + /* Inform UMA that this allocator uses kernel_arena/object. */ *pflag = UMA_SLAB_KERNEL; - return ((void *)kmem_alloc_attr(kernel_map, size, wait, 0, + return ((void *)kmem_alloc_attr(kernel_arena, size, wait, 0, BUS_SPACE_MAXADDR, VM_MEMATTR_UNCACHEABLE)); #else @@ -169,6 +169,6 @@ void busdma_bufalloc_free_uncacheable(void *item, int size, u_int8_t pflag) { - kmem_free(kernel_map, (vm_offset_t)item, size); + kmem_free(kernel_arena, (vm_offset_t)item, size); } diff --git a/sys/kern/subr_capability.c b/sys/kern/subr_capability.c new file mode 100644 index 00000000000..61ace5ab6e4 --- /dev/null +++ b/sys/kern/subr_capability.c @@ -0,0 +1,298 @@ +/*- + * Copyright (c) 2013 FreeBSD Foundation + * All rights reserved. + * + * This software was developed by Pawel Jakub Dawidek under sponsorship from + * the FreeBSD Foundation. + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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 +__FBSDID("$FreeBSD$"); + +#ifdef _KERNEL +#include +#include +#include + +#include +#else /* !_KERNEL */ +#include +#include + +#include +#include +#include +#include +#include +#endif + +#ifdef _KERNEL +#define assert(exp) KASSERT((exp), ("%s:%u", __func__, __LINE__)) +#endif + +#define CAPARSIZE_MIN (CAP_RIGHTS_VERSION_00 + 2) +#define CAPARSIZE_MAX (CAP_RIGHTS_VERSION + 2) + +static __inline int +right_to_index(uint64_t right) +{ + static const int bit2idx[] = { + -1, 0, 1, -1, 2, -1, -1, -1, 3, -1, -1, -1, -1, -1, -1, -1, + 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 + }; + int idx; + + idx = CAPIDXBIT(right); + assert(idx >= 0 && idx < sizeof(bit2idx) / sizeof(bit2idx[0])); + return (bit2idx[idx]); +} + +static void +cap_rights_vset(cap_rights_t *rights, va_list ap) +{ + uint64_t right; + int i, n; + + assert(CAPVER(rights) == CAP_RIGHTS_VERSION_00); + + n = CAPARSIZE(rights); + assert(n >= CAPARSIZE_MIN && n <= CAPARSIZE_MAX); + + for (;;) { + right = (uint64_t)va_arg(ap, unsigned long long); + if (right == 0) + break; + assert(CAPRVER(right) == 0); + i = right_to_index(right); + assert(i >= 0); + assert(i < n); + assert(CAPIDXBIT(rights->cr_rights[i]) == CAPIDXBIT(right)); + rights->cr_rights[i] |= right; + assert(CAPIDXBIT(rights->cr_rights[i]) == CAPIDXBIT(right)); + } +} + +static void +cap_rights_vclear(cap_rights_t *rights, va_list ap) +{ + uint64_t right; + int i, n; + + assert(CAPVER(rights) == CAP_RIGHTS_VERSION_00); + + n = CAPARSIZE(rights); + assert(n >= CAPARSIZE_MIN && n <= CAPARSIZE_MAX); + + for (;;) { + right = (uint64_t)va_arg(ap, unsigned long long); + if (right == 0) + break; + assert(CAPRVER(right) == 0); + i = right_to_index(right); + assert(i >= 0); + assert(i < n); + assert(CAPIDXBIT(rights->cr_rights[i]) == CAPIDXBIT(right)); + rights->cr_rights[i] &= ~(right & 0x01FFFFFFFFFFFFFFULL); + assert(CAPIDXBIT(rights->cr_rights[i]) == CAPIDXBIT(right)); + } +} + +static bool +cap_rights_is_vset(const cap_rights_t *rights, va_list ap) +{ + uint64_t right; + int i, n; + + assert(CAPVER(rights) == CAP_RIGHTS_VERSION_00); + + n = CAPARSIZE(rights); + assert(n >= CAPARSIZE_MIN && n <= CAPARSIZE_MAX); + + for (;;) { + right = (uint64_t)va_arg(ap, unsigned long long); + if (right == 0) + break; + assert(CAPRVER(right) == 0); + i = right_to_index(right); + assert(i >= 0); + assert(i < n); + assert(CAPIDXBIT(rights->cr_rights[i]) == CAPIDXBIT(right)); + if ((rights->cr_rights[i] & right) != right) + return (false); + } + + return (true); +} + +cap_rights_t * +__cap_rights_init(int version, cap_rights_t *rights, ...) +{ + unsigned int n; + va_list ap; + + assert(version == CAP_RIGHTS_VERSION_00); + + n = version + 2; + assert(n >= CAPARSIZE_MIN && n <= CAPARSIZE_MAX); + memset(rights->cr_rights, 0, sizeof(rights->cr_rights[0]) * n); + CAP_NONE(rights); + va_start(ap, rights); + cap_rights_vset(rights, ap); + va_end(ap); + + return (rights); +} + +void +__cap_rights_set(cap_rights_t *rights, ...) +{ + va_list ap; + + assert(CAPVER(rights) == CAP_RIGHTS_VERSION_00); + + va_start(ap, rights); + cap_rights_vset(rights, ap); + va_end(ap); +} + +void +__cap_rights_clear(cap_rights_t *rights, ...) +{ + va_list ap; + + assert(CAPVER(rights) == CAP_RIGHTS_VERSION_00); + + va_start(ap, rights); + cap_rights_vclear(rights, ap); + va_end(ap); +} + +bool +__cap_rights_is_set(const cap_rights_t *rights, ...) +{ + va_list ap; + bool ret; + + assert(CAPVER(rights) == CAP_RIGHTS_VERSION_00); + + va_start(ap, rights); + ret = cap_rights_is_vset(rights, ap); + va_end(ap); + + return (ret); +} + +bool +cap_rights_is_valid(const cap_rights_t *rights) +{ + cap_rights_t allrights; + int i, j; + + if (CAPVER(rights) != CAP_RIGHTS_VERSION_00) + return (false); + if (CAPARSIZE(rights) < CAPARSIZE_MIN || + CAPARSIZE(rights) > CAPARSIZE_MAX) { + return (false); + } + CAP_ALL(&allrights); + if (!cap_rights_contains(&allrights, rights)) + return (false); + for (i = 0; i < CAPARSIZE(rights); i++) { + j = right_to_index(rights->cr_rights[i]); + if (i != j) + return (false); + if (i > 0) { + if (CAPRVER(rights->cr_rights[i]) != 0) + return (false); + } + } + + return (true); +} + +void +cap_rights_merge(cap_rights_t *dst, const cap_rights_t *src) +{ + unsigned int i, n; + + assert(CAPVER(dst) == CAP_RIGHTS_VERSION_00); + assert(CAPVER(src) == CAP_RIGHTS_VERSION_00); + assert(CAPVER(dst) == CAPVER(src)); + assert(cap_rights_is_valid(src)); + assert(cap_rights_is_valid(dst)); + + n = CAPARSIZE(dst); + assert(n >= CAPARSIZE_MIN && n <= CAPARSIZE_MAX); + + for (i = 0; i < n; i++) + dst->cr_rights[i] |= src->cr_rights[i]; + + assert(cap_rights_is_valid(src)); + assert(cap_rights_is_valid(dst)); +} + +void +cap_rights_remove(cap_rights_t *dst, const cap_rights_t *src) +{ + unsigned int i, n; + + assert(CAPVER(dst) == CAP_RIGHTS_VERSION_00); + assert(CAPVER(src) == CAP_RIGHTS_VERSION_00); + assert(CAPVER(dst) == CAPVER(src)); + assert(cap_rights_is_valid(src)); + assert(cap_rights_is_valid(dst)); + + n = CAPARSIZE(dst); + assert(n >= CAPARSIZE_MIN && n <= CAPARSIZE_MAX); + + for (i = 0; i < n; i++) { + dst->cr_rights[i] &= + ~(src->cr_rights[i] & 0x01FFFFFFFFFFFFFFULL); + } + + assert(cap_rights_is_valid(src)); + assert(cap_rights_is_valid(dst)); +} + +bool +cap_rights_contains(const cap_rights_t *big, const cap_rights_t *little) +{ + unsigned int i, n; + + assert(CAPVER(big) == CAP_RIGHTS_VERSION_00); + assert(CAPVER(little) == CAP_RIGHTS_VERSION_00); + assert(CAPVER(big) == CAPVER(little)); + + n = CAPARSIZE(big); + assert(n >= CAPARSIZE_MIN && n <= CAPARSIZE_MAX); + + for (i = 0; i < n; i++) { + if ((big->cr_rights[i] & little->cr_rights[i]) != + little->cr_rights[i]) { + return (false); + } + } + + return (true); +} diff --git a/sys/kern/subr_mbpool.c b/sys/kern/subr_mbpool.c index ab80a5e4572..0b8cda62428 100644 --- a/sys/kern/subr_mbpool.c +++ b/sys/kern/subr_mbpool.c @@ -40,6 +40,7 @@ __FBSDID("$FreeBSD$"); #include +#include #include MODULE_VERSION(libmbpool, 1); @@ -282,10 +283,12 @@ mbp_free(struct mbpool *p, void *ptr) /* * Mbuf system external mbuf free routine */ -void -mbp_ext_free(void *buf, void *arg) +int +mbp_ext_free(struct mbuf *m, void *buf, void *arg) { mbp_free(arg, buf); + + return (EXT_FREE_OK); } /* diff --git a/sys/kern/subr_prf.c b/sys/kern/subr_prf.c index d8e8e0582a7..93d8ed372f3 100644 --- a/sys/kern/subr_prf.c +++ b/sys/kern/subr_prf.c @@ -151,39 +151,47 @@ uprintf(const char *fmt, ...) PROC_LOCK(p); if ((p->p_flag & P_CONTROLT) == 0) { PROC_UNLOCK(p); - retval = 0; - goto out; + sx_sunlock(&proctree_lock); + return (0); } SESS_LOCK(p->p_session); pca.tty = p->p_session->s_ttyp; SESS_UNLOCK(p->p_session); PROC_UNLOCK(p); if (pca.tty == NULL) { - retval = 0; - goto out; + sx_sunlock(&proctree_lock); + return (0); } pca.flags = TOTTY; pca.p_bufr = NULL; va_start(ap, fmt); tty_lock(pca.tty); + sx_sunlock(&proctree_lock); retval = kvprintf(fmt, putchar, &pca, 10, ap); tty_unlock(pca.tty); va_end(ap); -out: - sx_sunlock(&proctree_lock); return (retval); } /* - * tprintf prints on the controlling terminal associated with the given - * session, possibly to the log as well. + * tprintf and vtprintf print on the controlling terminal associated with the + * given session, possibly to the log as well. */ void tprintf(struct proc *p, int pri, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + vtprintf(p, pri, fmt, ap); + va_end(ap); +} + +void +vtprintf(struct proc *p, int pri, const char *fmt, va_list ap) { struct tty *tp = NULL; int flags = 0; - va_list ap; struct putchar_arg pca; struct session *sess = NULL; @@ -208,17 +216,15 @@ tprintf(struct proc *p, int pri, const char *fmt, ...) pca.tty = tp; pca.flags = flags; pca.p_bufr = NULL; - va_start(ap, fmt); if (pca.tty != NULL) tty_lock(pca.tty); + sx_sunlock(&proctree_lock); kvprintf(fmt, putchar, &pca, 10, ap); if (pca.tty != NULL) tty_unlock(pca.tty); - va_end(ap); if (sess != NULL) sess_release(sess); msgbuftrigger = 1; - sx_sunlock(&proctree_lock); } /* diff --git a/sys/kern/subr_taskqueue.c b/sys/kern/subr_taskqueue.c index 521a5b18e7f..9c7bf411aeb 100644 --- a/sys/kern/subr_taskqueue.c +++ b/sys/kern/subr_taskqueue.c @@ -620,7 +620,6 @@ taskqueue_member(struct taskqueue *queue, struct thread *td) { int i, j, ret = 0; - TQ_LOCK(queue); for (i = 0, j = 0; ; i++) { if (queue->tq_threads[i] == NULL) continue; @@ -631,6 +630,5 @@ taskqueue_member(struct taskqueue *queue, struct thread *td) if (++j >= queue->tq_tcount) break; } - TQ_UNLOCK(queue); return (ret); } diff --git a/sys/kern/subr_uio.c b/sys/kern/subr_uio.c index 1ee265c7dde..13c678df464 100644 --- a/sys/kern/subr_uio.c +++ b/sys/kern/subr_uio.c @@ -37,8 +37,6 @@ #include __FBSDID("$FreeBSD$"); -#include "opt_zero.h" - #include #include #include @@ -56,80 +54,14 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include -#ifdef SOCKET_SEND_COW -#include -#endif SYSCTL_INT(_kern, KERN_IOV_MAX, iov_max, CTLFLAG_RD, NULL, UIO_MAXIOV, "Maximum number of elements in an I/O vector; sysconf(_SC_IOV_MAX)"); static int uiomove_faultflag(void *cp, int n, struct uio *uio, int nofault); -#ifdef SOCKET_SEND_COW -/* Declared in uipc_socket.c */ -extern int so_zero_copy_receive; - -/* - * Identify the physical page mapped at the given kernel virtual - * address. Insert this physical page into the given address space at - * the given virtual address, replacing the physical page, if any, - * that already exists there. - */ -static int -vm_pgmoveco(vm_map_t mapa, vm_offset_t kaddr, vm_offset_t uaddr) -{ - vm_map_t map = mapa; - vm_page_t kern_pg, user_pg; - vm_object_t uobject; - vm_map_entry_t entry; - vm_pindex_t upindex; - vm_prot_t prot; - boolean_t wired; - - KASSERT((uaddr & PAGE_MASK) == 0, - ("vm_pgmoveco: uaddr is not page aligned")); - - /* - * Herein the physical page is validated and dirtied. It is - * unwired in sf_buf_mext(). - */ - kern_pg = PHYS_TO_VM_PAGE(vtophys(kaddr)); - kern_pg->valid = VM_PAGE_BITS_ALL; - KASSERT(kern_pg->queue == PQ_NONE && kern_pg->wire_count == 1, - ("vm_pgmoveco: kern_pg is not correctly wired")); - - if ((vm_map_lookup(&map, uaddr, - VM_PROT_WRITE, &entry, &uobject, - &upindex, &prot, &wired)) != KERN_SUCCESS) { - return(EFAULT); - } - VM_OBJECT_WLOCK(uobject); -retry: - if ((user_pg = vm_page_lookup(uobject, upindex)) != NULL) { - if (vm_page_sleep_if_busy(user_pg, TRUE, "vm_pgmoveco")) - goto retry; - vm_page_lock(user_pg); - pmap_remove_all(user_pg); - vm_page_free(user_pg); - vm_page_unlock(user_pg); - } else { - /* - * Even if a physical page does not exist in the - * object chain's first object, a physical page from a - * backing object may be mapped read only. - */ - if (uobject->backing_object != NULL) - pmap_remove(map->pmap, uaddr, uaddr + PAGE_SIZE); - } - vm_page_insert(kern_pg, uobject, upindex); - vm_page_dirty(kern_pg); - VM_OBJECT_WUNLOCK(uobject); - vm_map_lookup_done(map, entry); - return(KERN_SUCCESS); -} -#endif /* SOCKET_SEND_COW */ - int copyin_nofault(const void *udaddr, void *kaddr, size_t len) { @@ -307,103 +239,6 @@ uiomove_frombuf(void *buf, int buflen, struct uio *uio) return (uiomove((char *)buf + offset, n, uio)); } -#ifdef SOCKET_RECV_PFLIP -/* - * Experimental support for zero-copy I/O - */ -static int -userspaceco(void *cp, u_int cnt, struct uio *uio, int disposable) -{ - struct iovec *iov; - int error; - - iov = uio->uio_iov; - if (uio->uio_rw == UIO_READ) { - if ((so_zero_copy_receive != 0) - && ((cnt & PAGE_MASK) == 0) - && ((((intptr_t) iov->iov_base) & PAGE_MASK) == 0) - && ((uio->uio_offset & PAGE_MASK) == 0) - && ((((intptr_t) cp) & PAGE_MASK) == 0) - && (disposable != 0)) { - /* SOCKET: use page-trading */ - /* - * We only want to call vm_pgmoveco() on - * disposeable pages, since it gives the - * kernel page to the userland process. - */ - error = vm_pgmoveco(&curproc->p_vmspace->vm_map, - (vm_offset_t)cp, (vm_offset_t)iov->iov_base); - - /* - * If we get an error back, attempt - * to use copyout() instead. The - * disposable page should be freed - * automatically if we weren't able to move - * it into userland. - */ - if (error != 0) - error = copyout(cp, iov->iov_base, cnt); - } else { - error = copyout(cp, iov->iov_base, cnt); - } - } else { - error = copyin(iov->iov_base, cp, cnt); - } - return (error); -} - -int -uiomoveco(void *cp, int n, struct uio *uio, int disposable) -{ - struct iovec *iov; - u_int cnt; - int error; - - KASSERT(uio->uio_rw == UIO_READ || uio->uio_rw == UIO_WRITE, - ("uiomoveco: mode")); - KASSERT(uio->uio_segflg != UIO_USERSPACE || uio->uio_td == curthread, - ("uiomoveco proc")); - - while (n > 0 && uio->uio_resid) { - iov = uio->uio_iov; - cnt = iov->iov_len; - if (cnt == 0) { - uio->uio_iov++; - uio->uio_iovcnt--; - continue; - } - if (cnt > n) - cnt = n; - - switch (uio->uio_segflg) { - - case UIO_USERSPACE: - maybe_yield(); - error = userspaceco(cp, cnt, uio, disposable); - if (error) - return (error); - break; - - case UIO_SYSSPACE: - if (uio->uio_rw == UIO_READ) - bcopy(cp, iov->iov_base, cnt); - else - bcopy(iov->iov_base, cp, cnt); - break; - case UIO_NOCOPY: - break; - } - iov->iov_base = (char *)iov->iov_base + cnt; - iov->iov_len -= cnt; - uio->uio_resid -= cnt; - uio->uio_offset += cnt; - cp = (char *)cp + cnt; - n -= cnt; - } - return (0); -} -#endif /* SOCKET_RECV_PFLIP */ - /* * Give next character to user as result of read. */ diff --git a/sys/kern/subr_unit.c b/sys/kern/subr_unit.c index 9cf17819e1d..3bf7aaf691b 100644 --- a/sys/kern/subr_unit.c +++ b/sys/kern/subr_unit.c @@ -68,8 +68,8 @@ */ #include -#include #include +#include #ifdef _KERNEL @@ -187,22 +187,6 @@ CTASSERT(sizeof(struct unr) == sizeof(struct unrb)); /* Number of bits in the bitmap */ #define NBITS ((int)sizeof(((struct unrb *)NULL)->map) * 8) -/* Header element for a unr number space. */ - -struct unrhdr { - TAILQ_HEAD(unrhd,unr) head; - u_int low; /* Lowest item */ - u_int high; /* Highest item */ - u_int busy; /* Count of allocated items */ - u_int alloc; /* Count of memory allocations */ - u_int first; /* items in allocated from start */ - u_int last; /* items free at end */ - struct mtx *mtx; - TAILQ_HEAD(unrfr,unr) ppfree; /* Items to be freed after mtx - lock dropped */ -}; - - #if defined(DIAGNOSTIC) || !defined(_KERNEL) /* * Consistency check function. @@ -315,20 +299,12 @@ clean_unrhdr(struct unrhdr *uh) mtx_unlock(uh->mtx); } -/* - * Allocate a new unrheader set. - * - * Highest and lowest valid values given as parameters. - */ - -struct unrhdr * -new_unrhdr(int low, int high, struct mtx *mutex) +void +init_unrhdr(struct unrhdr *uh, int low, int high, struct mtx *mutex) { - struct unrhdr *uh; KASSERT(low >= 0 && low <= high, ("UNR: use error: new_unrhdr(%d, %d)", low, high)); - uh = Malloc(sizeof *uh); if (mutex != NULL) uh->mtx = mutex; else @@ -340,6 +316,21 @@ new_unrhdr(int low, int high, struct mtx *mutex) uh->first = 0; uh->last = 1 + (high - low); check_unrhdr(uh, __LINE__); +} + +/* + * Allocate a new unrheader set. + * + * Highest and lowest valid values given as parameters. + */ + +struct unrhdr * +new_unrhdr(int low, int high, struct mtx *mutex) +{ + struct unrhdr *uh; + + uh = Malloc(sizeof *uh); + init_unrhdr(uh, low, high, mutex); return (uh); } diff --git a/sys/kern/subr_vmem.c b/sys/kern/subr_vmem.c index 6aa8ad3a75f..f3f3eecee93 100644 --- a/sys/kern/subr_vmem.c +++ b/sys/kern/subr_vmem.c @@ -54,13 +54,17 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include +#include "opt_vm.h" + #include #include #include #include +#include #include #include #include @@ -164,6 +168,9 @@ struct vmem_btag { #define BT_END(bt) ((bt)->bt_start + (bt)->bt_size - 1) #if defined(DIAGNOSTIC) +static int enable_vmem_check = 1; +SYSCTL_INT(_debug, OID_AUTO, vmem_check, CTLFLAG_RW, + &enable_vmem_check, 0, "Enable vmem check"); static void vmem_check(vmem_t *); #endif @@ -213,11 +220,20 @@ static LIST_HEAD(, vmem) vmem_list = LIST_HEAD_INITIALIZER(vmem_list); static uma_zone_t vmem_bt_zone; /* boot time arena storage. */ +static struct vmem kernel_arena_storage; +static struct vmem kmem_arena_storage; static struct vmem buffer_arena_storage; static struct vmem transient_arena_storage; +vmem_t *kernel_arena = &kernel_arena_storage; +vmem_t *kmem_arena = &kmem_arena_storage; vmem_t *buffer_arena = &buffer_arena_storage; vmem_t *transient_arena = &transient_arena_storage; +#ifdef DEBUG_MEMGUARD +static struct vmem memguard_arena_storage; +vmem_t *memguard_arena = &memguard_arena_storage; +#endif + /* * Fill the vmem's boundary tag cache. We guarantee that boundary tag * allocation will not fail once bt_fill() passes. To do so we cache @@ -230,6 +246,14 @@ bt_fill(vmem_t *vm, int flags) VMEM_ASSERT_LOCKED(vm); + /* + * Only allow the kmem arena to dip into reserve tags. It is the + * vmem where new tags come from. + */ + flags &= BT_FLAGS; + if (vm != kmem_arena) + flags &= ~M_USE_RESERVE; + /* * Loop until we meet the reserve. To minimize the lock shuffle * and prevent simultaneous fills we first try a NOWAIT regardless @@ -545,6 +569,77 @@ qc_drain(vmem_t *vm) zone_drain(vm->vm_qcache[i].qc_cache); } +#ifndef UMA_MD_SMALL_ALLOC + +static struct mtx_padalign vmem_bt_lock; + +/* + * vmem_bt_alloc: Allocate a new page of boundary tags. + * + * On architectures with uma_small_alloc there is no recursion; no address + * space need be allocated to allocate boundary tags. For the others, we + * must handle recursion. Boundary tags are necessary to allocate new + * boundary tags. + * + * UMA guarantees that enough tags are held in reserve to allocate a new + * page of kva. We dip into this reserve by specifying M_USE_RESERVE only + * when allocating the page to hold new boundary tags. In this way the + * reserve is automatically filled by the allocation that uses the reserve. + * + * We still have to guarantee that the new tags are allocated atomically since + * many threads may try concurrently. The bt_lock provides this guarantee. + * We convert WAITOK allocations to NOWAIT and then handle the blocking here + * on failure. It's ok to return NULL for a WAITOK allocation as UMA will + * loop again after checking to see if we lost the race to allocate. + * + * There is a small race between vmem_bt_alloc() returning the page and the + * zone lock being acquired to add the page to the zone. For WAITOK + * allocations we just pause briefly. NOWAIT may experience a transient + * failure. To alleviate this we permit a small number of simultaneous + * fills to proceed concurrently so NOWAIT is less likely to fail unless + * we are really out of KVA. + */ +static void * +vmem_bt_alloc(uma_zone_t zone, int bytes, uint8_t *pflag, int wait) +{ + vmem_addr_t addr; + + *pflag = UMA_SLAB_KMEM; + + /* + * Single thread boundary tag allocation so that the address space + * and memory are added in one atomic operation. + */ + mtx_lock(&vmem_bt_lock); + if (vmem_xalloc(kmem_arena, bytes, 0, 0, 0, VMEM_ADDR_MIN, + VMEM_ADDR_MAX, M_NOWAIT | M_NOVM | M_USE_RESERVE | M_BESTFIT, + &addr) == 0) { + if (kmem_back(kmem_object, addr, bytes, + M_NOWAIT | M_USE_RESERVE) == 0) { + mtx_unlock(&vmem_bt_lock); + return ((void *)addr); + } + vmem_xfree(kmem_arena, addr, bytes); + mtx_unlock(&vmem_bt_lock); + /* + * Out of memory, not address space. This may not even be + * possible due to M_USE_RESERVE page allocation. + */ + if (wait & M_WAITOK) + VM_WAIT; + return (NULL); + } + mtx_unlock(&vmem_bt_lock); + /* + * We're either out of address space or lost a fill race. + */ + if (wait & M_WAITOK) + pause("btalloc", 1); + + return (NULL); +} +#endif + void vmem_startup(void) { @@ -553,6 +648,17 @@ vmem_startup(void) vmem_bt_zone = uma_zcreate("vmem btag", sizeof(struct vmem_btag), NULL, NULL, NULL, NULL, UMA_ALIGN_PTR, UMA_ZONE_VM); +#ifndef UMA_MD_SMALL_ALLOC + mtx_init(&vmem_bt_lock, "btag lock", NULL, MTX_DEF); + uma_prealloc(vmem_bt_zone, BT_MAXALLOC); + /* + * Reserve enough tags to allocate new tags. We allow multiple + * CPUs to attempt to allocate new tags concurrently to limit + * false restarts in UMA. + */ + uma_zone_reserve(vmem_bt_zone, BT_MAXALLOC * (mp_ncpus + 1) / 2); + uma_zone_set_allocf(vmem_bt_zone, vmem_bt_alloc); +#endif } /* ---- rehash */ @@ -618,9 +724,11 @@ vmem_periodic(void *unused, int pending) LIST_FOREACH(vm, &vmem_list, vm_alllist) { #ifdef DIAGNOSTIC /* Convenient time to verify vmem state. */ - VMEM_LOCK(vm); - vmem_check(vm); - VMEM_UNLOCK(vm); + if (enable_vmem_check == 1) { + VMEM_LOCK(vm); + vmem_check(vm); + VMEM_UNLOCK(vm); + } #endif desired = 1 << flsl(vm->vm_nbusytag); desired = MIN(MAX(desired, VMEM_HASHSIZE_MIN), @@ -656,20 +764,21 @@ vmem_add1(vmem_t *vm, vmem_addr_t addr, vmem_size_t size, int type) bt_t *btfree; MPASS(type == BT_TYPE_SPAN || type == BT_TYPE_SPAN_STATIC); + MPASS((size & vm->vm_quantum_mask) == 0); btspan = bt_alloc(vm); btspan->bt_type = type; btspan->bt_start = addr; btspan->bt_size = size; + bt_insseg_tail(vm, btspan); btfree = bt_alloc(vm); btfree->bt_type = BT_TYPE_FREE; btfree->bt_start = addr; btfree->bt_size = size; - - bt_insseg_tail(vm, btspan); bt_insseg(vm, btfree, btspan); bt_insfree(vm, btfree); + vm->vm_size += size; } @@ -703,7 +812,7 @@ vmem_destroy1(vmem_t *vm) } static int -vmem_import(vmem_t *vm, vmem_size_t size, int flags) +vmem_import(vmem_t *vm, vmem_size_t size, vmem_size_t align, int flags) { vmem_addr_t addr; int error; @@ -711,6 +820,12 @@ vmem_import(vmem_t *vm, vmem_size_t size, int flags) if (vm->vm_importfn == NULL) return EINVAL; + /* + * To make sure we get a span that meets the alignment we double it + * and add the size to the tail. This slightly overestimates. + */ + if (align != vm->vm_quantum_mask + 1) + size = (align * 2) + size; size = roundup(size, vm->vm_import_quantum); /* @@ -1055,7 +1170,7 @@ vmem_xalloc(vmem_t *vm, const vmem_size_t size0, vmem_size_t align, * imported region. It is up to the user to specify the * import quantum such that it can satisfy any allocation. */ - if (vmem_import(vm, size, flags) == 0) + if (vmem_import(vm, size, align, flags) == 0) continue; /* diff --git a/sys/kern/subr_witness.c b/sys/kern/subr_witness.c index 3b4d7a2fd77..9d3040d68b8 100644 --- a/sys/kern/subr_witness.c +++ b/sys/kern/subr_witness.c @@ -135,7 +135,7 @@ __FBSDID("$FreeBSD$"); #define WITNESS_COUNT 1024 #define WITNESS_CHILDCOUNT (WITNESS_COUNT * 4) #define WITNESS_HASH_SIZE 251 /* Prime, gives load factor < 2 */ -#define WITNESS_PENDLIST 768 +#define WITNESS_PENDLIST 1024 /* Allocate 256 KB of stack data space */ #define WITNESS_LO_DATA_COUNT 2048 @@ -1138,18 +1138,12 @@ witness_checkorder(struct lock_object *lock, int flags, const char *file, iclass = LOCK_CLASS(interlock); lock1 = find_instance(lock_list, interlock); if (lock1 == NULL) - kassert_panic( - "interlock (%s) %s not locked while locking" - " %s @ %s:%d", + kassert_panic("interlock (%s) %s not locked @ %s:%d", iclass->lc_name, interlock->lo_name, - flags & LOP_EXCLUSIVE ? "exclusive" : "shared", fixup_filename(file), line); else if ((lock1->li_flags & LI_RECURSEMASK) != 0) - kassert_panic( - "interlock (%s) %s recursed while locking %s" - " @ %s:%d", + kassert_panic("interlock (%s) %s recursed @ %s:%d", iclass->lc_name, interlock->lo_name, - flags & LOP_EXCLUSIVE ? "exclusive" : "shared", fixup_filename(file), line); } diff --git a/sys/kern/sys_capability.c b/sys/kern/sys_capability.c index 399d0b114da..7a820179633 100644 --- a/sys/kern/sys_capability.c +++ b/sys/kern/sys_capability.c @@ -70,6 +70,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include #include @@ -147,16 +148,19 @@ FEATURE(security_capabilities, "Capsicum Capabilities"); MALLOC_DECLARE(M_FILECAPS); static inline int -_cap_check(cap_rights_t have, cap_rights_t need, enum ktr_cap_fail_type type) +_cap_check(const cap_rights_t *havep, const cap_rights_t *needp, + enum ktr_cap_fail_type type) { + int i; - - if ((need & ~have) != 0) { + for (i = 0; i < nitems(havep->cr_rights); i++) { + if (!cap_rights_contains(havep, needp)) { #ifdef KTRACE - if (KTRPOINT(curthread, KTR_CAPFAIL)) - ktrcapfail(type, need, have); + if (KTRPOINT(curthread, KTR_CAPFAIL)) + ktrcapfail(type, needp, havep); #endif - return (ENOTCAPABLE); + return (ENOTCAPABLE); + } } return (0); } @@ -165,26 +169,26 @@ _cap_check(cap_rights_t have, cap_rights_t need, enum ktr_cap_fail_type type) * Test whether a capability grants the requested rights. */ int -cap_check(cap_rights_t have, cap_rights_t need) +cap_check(const cap_rights_t *havep, const cap_rights_t *needp) { - return (_cap_check(have, need, CAPFAIL_NOTCAPABLE)); + return (_cap_check(havep, needp, CAPFAIL_NOTCAPABLE)); } /* * Convert capability rights into VM access flags. */ u_char -cap_rights_to_vmprot(cap_rights_t have) +cap_rights_to_vmprot(cap_rights_t *havep) { u_char maxprot; maxprot = VM_PROT_NONE; - if (have & CAP_MMAP_R) + if (cap_rights_is_set(havep, CAP_MMAP_R)) maxprot |= VM_PROT_READ; - if (have & CAP_MMAP_W) + if (cap_rights_is_set(havep, CAP_MMAP_W)) maxprot |= VM_PROT_WRITE; - if (have & CAP_MMAP_X) + if (cap_rights_is_set(havep, CAP_MMAP_X)) maxprot |= VM_PROT_EXECUTE; return (maxprot); @@ -195,11 +199,11 @@ cap_rights_to_vmprot(cap_rights_t have) * any other way, as we want to keep all capability permission evaluation in * this one file. */ -cap_rights_t +cap_rights_t * cap_rights(struct filedesc *fdp, int fd) { - return (fdp->fd_ofiles[fd].fde_rights); + return (&fdp->fd_ofiles[fd].fde_rights); } /* @@ -210,16 +214,41 @@ sys_cap_rights_limit(struct thread *td, struct cap_rights_limit_args *uap) { struct filedesc *fdp; cap_rights_t rights; - int error, fd; + int error, fd, version; + + cap_rights_init(&rights); + + error = copyin(uap->rightsp, &rights, sizeof(rights.cr_rights[0])); + if (error != 0) + return (error); + version = CAPVER(&rights); + if (version != CAP_RIGHTS_VERSION_00) + return (EINVAL); + + error = copyin(uap->rightsp, &rights, + sizeof(rights.cr_rights[0]) * CAPARSIZE(&rights)); + if (error != 0) + return (error); + /* Check for race. */ + if (CAPVER(&rights) != version) + return (EINVAL); + + if (!cap_rights_is_valid(&rights)) + return (EINVAL); + + if (version != CAP_RIGHTS_VERSION) { + rights.cr_rights[0] &= ~(0x3ULL << 62); + rights.cr_rights[0] |= ((uint64_t)CAP_RIGHTS_VERSION << 62); + } +#ifdef KTRACE + if (KTRPOINT(td, KTR_STRUCT)) + ktrcaprights(&rights); +#endif fd = uap->fd; - rights = uap->rights; AUDIT_ARG_FD(fd); - AUDIT_ARG_RIGHTS(rights); - - if ((rights & ~CAP_ALL) != 0) - return (EINVAL); + AUDIT_ARG_RIGHTS(&rights); fdp = td->td_proc->p_fd; FILEDESC_XLOCK(fdp); @@ -227,15 +256,15 @@ sys_cap_rights_limit(struct thread *td, struct cap_rights_limit_args *uap) FILEDESC_XUNLOCK(fdp); return (EBADF); } - error = _cap_check(cap_rights(fdp, fd), rights, CAPFAIL_INCREASE); + error = _cap_check(cap_rights(fdp, fd), &rights, CAPFAIL_INCREASE); if (error == 0) { fdp->fd_ofiles[fd].fde_rights = rights; - if ((rights & CAP_IOCTL) == 0) { + if (!cap_rights_is_set(&rights, CAP_IOCTL)) { free(fdp->fd_ofiles[fd].fde_ioctls, M_FILECAPS); fdp->fd_ofiles[fd].fde_ioctls = NULL; fdp->fd_ofiles[fd].fde_nioctls = 0; } - if ((rights & CAP_FCNTL) == 0) + if (!cap_rights_is_set(&rights, CAP_FCNTL)) fdp->fd_ofiles[fd].fde_fcntls = 0; } FILEDESC_XUNLOCK(fdp); @@ -246,11 +275,14 @@ sys_cap_rights_limit(struct thread *td, struct cap_rights_limit_args *uap) * System call to query the rights mask associated with a capability. */ int -sys_cap_rights_get(struct thread *td, struct cap_rights_get_args *uap) +sys___cap_rights_get(struct thread *td, struct __cap_rights_get_args *uap) { struct filedesc *fdp; cap_rights_t rights; - int fd; + int error, fd, i, n; + + if (uap->version != CAP_RIGHTS_VERSION_00) + return (EINVAL); fd = uap->fd; @@ -262,9 +294,26 @@ sys_cap_rights_get(struct thread *td, struct cap_rights_get_args *uap) FILEDESC_SUNLOCK(fdp); return (EBADF); } - rights = cap_rights(fdp, fd); + rights = *cap_rights(fdp, fd); FILEDESC_SUNLOCK(fdp); - return (copyout(&rights, uap->rightsp, sizeof(*uap->rightsp))); + n = uap->version + 2; + if (uap->version != CAPVER(&rights)) { + /* + * For older versions we need to check if the descriptor + * doesn't contain rights not understood by the caller. + * If it does, we have to return an error. + */ + for (i = n; i < CAPARSIZE(&rights); i++) { + if ((rights.cr_rights[i] & ~(0x7FULL << 57)) != 0) + return (EINVAL); + } + } + error = copyout(&rights, uap->rightsp, sizeof(rights.cr_rights[0]) * n); +#ifdef KTRACE + if (error == 0 && KTRPOINT(td, KTR_STRUCT)) + ktrcaprights(&rights); +#endif + return (error); } /* @@ -328,32 +377,14 @@ cap_ioctl_limit_check(struct filedesc *fdp, int fd, const u_long *cmds, } int -sys_cap_ioctls_limit(struct thread *td, struct cap_ioctls_limit_args *uap) +kern_cap_ioctls_limit(struct thread *td, int fd, u_long *cmds, size_t ncmds) { struct filedesc *fdp; - u_long *cmds, *ocmds; - size_t ncmds; - int error, fd; - - fd = uap->fd; - ncmds = uap->ncmds; + u_long *ocmds; + int error; AUDIT_ARG_FD(fd); - if (ncmds > 256) /* XXX: Is 256 sane? */ - return (EINVAL); - - if (ncmds == 0) { - cmds = NULL; - } else { - cmds = malloc(sizeof(cmds[0]) * ncmds, M_FILECAPS, M_WAITOK); - error = copyin(uap->cmds, cmds, sizeof(cmds[0]) * ncmds); - if (error != 0) { - free(cmds, M_FILECAPS); - return (error); - } - } - fdp = td->td_proc->p_fd; FILEDESC_XLOCK(fdp); @@ -378,6 +409,32 @@ out: return (error); } +int +sys_cap_ioctls_limit(struct thread *td, struct cap_ioctls_limit_args *uap) +{ + u_long *cmds; + size_t ncmds; + int error; + + ncmds = uap->ncmds; + + if (ncmds > 256) /* XXX: Is 256 sane? */ + return (EINVAL); + + if (ncmds == 0) { + cmds = NULL; + } else { + cmds = malloc(sizeof(cmds[0]) * ncmds, M_FILECAPS, M_WAITOK); + error = copyin(uap->cmds, cmds, sizeof(cmds[0]) * ncmds); + if (error != 0) { + free(cmds, M_FILECAPS); + return (error); + } + } + + return (kern_cap_ioctls_limit(td, uap->fd, cmds, ncmds)); +} + int sys_cap_ioctls_get(struct thread *td, struct cap_ioctls_get_args *uap) { @@ -504,65 +561,6 @@ sys_cap_fcntls_get(struct thread *td, struct cap_fcntls_get_args *uap) return (copyout(&rights, uap->fcntlrightsp, sizeof(rights))); } -/* - * For backward compatibility. - */ -int -sys_cap_new(struct thread *td, struct cap_new_args *uap) -{ - struct filedesc *fdp; - cap_rights_t rights; - register_t newfd; - int error, fd; - - fd = uap->fd; - rights = uap->rights; - - AUDIT_ARG_FD(fd); - AUDIT_ARG_RIGHTS(rights); - - if ((rights & ~CAP_ALL) != 0) - return (EINVAL); - - fdp = td->td_proc->p_fd; - FILEDESC_SLOCK(fdp); - if (fget_locked(fdp, fd) == NULL) { - FILEDESC_SUNLOCK(fdp); - return (EBADF); - } - error = _cap_check(cap_rights(fdp, fd), rights, CAPFAIL_INCREASE); - FILEDESC_SUNLOCK(fdp); - if (error != 0) - return (error); - - error = do_dup(td, 0, fd, 0, &newfd); - if (error != 0) - return (error); - - FILEDESC_XLOCK(fdp); - /* - * We don't really care about the race between checking capability - * rights for the source descriptor and now. If capability rights - * were ok at that earlier point, the process had this descriptor - * with those rights, so we don't increase them in security sense, - * the process might have done the cap_new(2) a bit earlier to get - * the same effect. - */ - fdp->fd_ofiles[newfd].fde_rights = rights; - if ((rights & CAP_IOCTL) == 0) { - free(fdp->fd_ofiles[newfd].fde_ioctls, M_FILECAPS); - fdp->fd_ofiles[newfd].fde_ioctls = NULL; - fdp->fd_ofiles[newfd].fde_nioctls = 0; - } - if ((rights & CAP_FCNTL) == 0) - fdp->fd_ofiles[newfd].fde_fcntls = 0; - FILEDESC_XUNLOCK(fdp); - - td->td_retval[0] = newfd; - - return (0); -} - #else /* !CAPABILITIES */ /* @@ -578,7 +576,7 @@ sys_cap_rights_limit(struct thread *td, struct cap_rights_limit_args *uap) } int -sys_cap_rights_get(struct thread *td, struct cap_rights_get_args *uap) +sys___cap_rights_get(struct thread *td, struct __cap_rights_get_args *uap) { return (ENOSYS); @@ -612,11 +610,4 @@ sys_cap_fcntls_get(struct thread *td, struct cap_fcntls_get_args *uap) return (ENOSYS); } -int -sys_cap_new(struct thread *td, struct cap_new_args *uap) -{ - - return (ENOSYS); -} - #endif /* CAPABILITIES */ diff --git a/sys/kern/sys_generic.c b/sys/kern/sys_generic.c index 44d1a899464..d4d6293034a 100644 --- a/sys/kern/sys_generic.c +++ b/sys/kern/sys_generic.c @@ -243,9 +243,10 @@ int kern_readv(struct thread *td, int fd, struct uio *auio) { struct file *fp; + cap_rights_t rights; int error; - error = fget_read(td, fd, CAP_READ, &fp); + error = fget_read(td, fd, cap_rights_init(&rights, CAP_READ), &fp); if (error) return (error); error = dofileread(td, fd, fp, auio, (off_t)-1, 0); @@ -286,9 +287,10 @@ kern_preadv(td, fd, auio, offset) off_t offset; { struct file *fp; + cap_rights_t rights; int error; - error = fget_read(td, fd, CAP_PREAD, &fp); + error = fget_read(td, fd, cap_rights_init(&rights, CAP_PREAD), &fp); if (error) return (error); if (!(fp->f_ops->fo_flags & DFLAG_SEEKABLE)) @@ -452,9 +454,10 @@ int kern_writev(struct thread *td, int fd, struct uio *auio) { struct file *fp; + cap_rights_t rights; int error; - error = fget_write(td, fd, CAP_WRITE, &fp); + error = fget_write(td, fd, cap_rights_init(&rights, CAP_WRITE), &fp); if (error) return (error); error = dofilewrite(td, fd, fp, auio, (off_t)-1, 0); @@ -495,9 +498,10 @@ kern_pwritev(td, fd, auio, offset) off_t offset; { struct file *fp; + cap_rights_t rights; int error; - error = fget_write(td, fd, CAP_PWRITE, &fp); + error = fget_write(td, fd, cap_rights_init(&rights, CAP_PWRITE), &fp); if (error) return (error); if (!(fp->f_ops->fo_flags & DFLAG_SEEKABLE)) @@ -575,12 +579,13 @@ kern_ftruncate(td, fd, length) off_t length; { struct file *fp; + cap_rights_t rights; int error; AUDIT_ARG_FD(fd); if (length < 0) return (EINVAL); - error = fget(td, fd, CAP_FTRUNCATE, &fp); + error = fget(td, fd, cap_rights_init(&rights, CAP_FTRUNCATE), &fp); if (error) return (error); AUDIT_ARG_FILE(td->td_proc, fp); @@ -705,6 +710,9 @@ kern_ioctl(struct thread *td, int fd, u_long com, caddr_t data) { struct file *fp; struct filedesc *fdp; +#ifndef CAPABILITIES + cap_rights_t rights; +#endif int error, tmp, locked; AUDIT_ARG_FD(fd); @@ -743,7 +751,8 @@ kern_ioctl(struct thread *td, int fd, u_long com, caddr_t data) locked = LA_UNLOCKED; } #else - if ((error = fget(td, fd, CAP_IOCTL, &fp)) != 0) { + error = fget(td, fd, cap_rights_init(&rights, CAP_IOCTL), &fp); + if (error != 0) { fp = NULL; goto out; } @@ -1180,8 +1189,10 @@ selsetbits(fd_mask **ibits, fd_mask **obits, int idx, fd_mask bit, int events) static __inline int getselfd_cap(struct filedesc *fdp, int fd, struct file **fpp) { + cap_rights_t rights; - return (fget_unlocked(fdp, fd, CAP_POLL_EVENT, 0, fpp, NULL)); + return (fget_unlocked(fdp, fd, cap_rights_init(&rights, CAP_POLL_EVENT), + 0, fpp, NULL)); } /* @@ -1357,6 +1368,9 @@ pollrescan(struct thread *td) struct filedesc *fdp; struct file *fp; struct pollfd *fd; +#ifdef CAPABILITIES + cap_rights_t rights; +#endif int n; n = 0; @@ -1373,7 +1387,8 @@ pollrescan(struct thread *td) fp = fdp->fd_ofiles[fd->fd].fde_file; #ifdef CAPABILITIES if (fp == NULL || - cap_check(cap_rights(fdp, fd->fd), CAP_POLL_EVENT) != 0) + cap_check(cap_rights(fdp, fd->fd), + cap_rights_init(&rights, CAP_POLL_EVENT)) != 0) #else if (fp == NULL) #endif @@ -1431,6 +1446,9 @@ pollscan(td, fds, nfd) { struct filedesc *fdp = td->td_proc->p_fd; struct file *fp; +#ifdef CAPABILITIES + cap_rights_t rights; +#endif int i, n = 0; FILEDESC_SLOCK(fdp); @@ -1445,7 +1463,7 @@ pollscan(td, fds, nfd) #ifdef CAPABILITIES if (fp == NULL || cap_check(cap_rights(fdp, fds->fd), - CAP_POLL_EVENT) != 0) + cap_rights_init(&rights, CAP_POLL_EVENT)) != 0) #else if (fp == NULL) #endif diff --git a/sys/kern/sys_pipe.c b/sys/kern/sys_pipe.c index 493fee5e275..6ba52e37c5f 100644 --- a/sys/kern/sys_pipe.c +++ b/sys/kern/sys_pipe.c @@ -164,6 +164,7 @@ struct fileops pipeops = { .fo_close = pipe_close, .fo_chmod = pipe_chmod, .fo_chown = pipe_chown, + .fo_sendfile = invfo_sendfile, .fo_flags = DFLAG_PASSABLE }; @@ -523,7 +524,7 @@ retry: buffer = (caddr_t) vm_map_min(pipe_map); error = vm_map_find(pipe_map, NULL, 0, - (vm_offset_t *) &buffer, size, 1, + (vm_offset_t *) &buffer, size, 0, VMFS_ANY_SPACE, VM_PROT_ALL, VM_PROT_ALL, 0); if (error != KERN_SUCCESS) { if ((cpipe->pipe_buffer.buffer == NULL) && diff --git a/sys/kern/sys_procdesc.c b/sys/kern/sys_procdesc.c index f40004182f5..4bafeabaab4 100644 --- a/sys/kern/sys_procdesc.c +++ b/sys/kern/sys_procdesc.c @@ -113,6 +113,7 @@ static struct fileops procdesc_ops = { .fo_close = procdesc_close, .fo_chmod = procdesc_chmod, .fo_chown = procdesc_chown, + .fo_sendfile = invfo_sendfile, .fo_flags = DFLAG_PASSABLE, }; @@ -137,14 +138,14 @@ SYSINIT(vfs, SI_SUB_VFS, SI_ORDER_ANY, procdesc_init, NULL); * died. */ int -procdesc_find(struct thread *td, int fd, cap_rights_t rights, +procdesc_find(struct thread *td, int fd, cap_rights_t *rightsp, struct proc **p) { struct procdesc *pd; struct file *fp; int error; - error = fget(td, fd, rights, &fp); + error = fget(td, fd, rightsp, &fp); if (error) return (error); if (fp->f_type != DTYPE_PROCDESC) { @@ -184,12 +185,12 @@ procdesc_pid(struct file *fp_procdesc) * Retrieve the PID associated with a process descriptor. */ int -kern_pdgetpid(struct thread *td, int fd, cap_rights_t rights, pid_t *pidp) +kern_pdgetpid(struct thread *td, int fd, cap_rights_t *rightsp, pid_t *pidp) { struct file *fp; int error; - error = fget(td, fd, rights, &fp); + error = fget(td, fd, rightsp, &fp); if (error) return (error); if (fp->f_type != DTYPE_PROCDESC) { @@ -208,11 +209,13 @@ out: int sys_pdgetpid(struct thread *td, struct pdgetpid_args *uap) { + cap_rights_t rights; pid_t pid; int error; AUDIT_ARG_FD(uap->fd); - error = kern_pdgetpid(td, uap->fd, CAP_PDGETPID, &pid); + error = kern_pdgetpid(td, uap->fd, + cap_rights_init(&rights, CAP_PDGETPID), &pid); if (error == 0) error = copyout(&pid, uap->pidp, sizeof(pid)); return (error); diff --git a/sys/kern/sys_socket.c b/sys/kern/sys_socket.c index cd6f655cc65..6a766afa971 100644 --- a/sys/kern/sys_socket.c +++ b/sys/kern/sys_socket.c @@ -66,6 +66,7 @@ struct fileops socketops = { .fo_close = soo_close, .fo_chmod = invfo_chmod, .fo_chown = invfo_chown, + .fo_sendfile = invfo_sendfile, .fo_flags = DFLAG_PASSABLE }; diff --git a/sys/kern/syscalls.c b/sys/kern/syscalls.c index 17f5448559d..fb6194e3e73 100644 --- a/sys/kern/syscalls.c +++ b/sys/kern/syscalls.c @@ -3,7 +3,7 @@ * * DO NOT EDIT-- this file is automatically generated. * $FreeBSD$ - * created from FreeBSD: head/sys/kern/syscalls.master 251526 2013-06-08 13:27:57Z glebius + * created from FreeBSD: head/sys/kern/syscalls.master 255490 2013-09-12 17:52:18Z jhb */ const char *syscallnames[] = { @@ -521,8 +521,8 @@ const char *syscallnames[] = { "msgctl", /* 511 = msgctl */ "shmctl", /* 512 = shmctl */ "lpathconf", /* 513 = lpathconf */ - "cap_new", /* 514 = cap_new */ - "cap_rights_get", /* 515 = cap_rights_get */ + "obs_cap_new", /* 514 = obsolete cap_new */ + "__cap_rights_get", /* 515 = __cap_rights_get */ "cap_enter", /* 516 = cap_enter */ "cap_getmode", /* 517 = cap_getmode */ "pdfork", /* 518 = pdfork */ diff --git a/sys/kern/syscalls.master b/sys/kern/syscalls.master index 789f95a5b4d..7df50ca2332 100644 --- a/sys/kern/syscalls.master +++ b/sys/kern/syscalls.master @@ -917,9 +917,9 @@ 512 AUE_SHMCTL NOSTD { int shmctl(int shmid, int cmd, \ struct shmid_ds *buf); } 513 AUE_LPATHCONF STD { int lpathconf(char *path, int name); } -514 AUE_CAP_NEW STD { int cap_new(int fd, uint64_t rights); } -515 AUE_CAP_RIGHTS_GET STD { int cap_rights_get(int fd, \ - uint64_t *rightsp); } +514 AUE_NULL OBSOL cap_new +515 AUE_CAP_RIGHTS_GET STD { int __cap_rights_get(int version, \ + int fd, cap_rights_t *rightsp); } 516 AUE_CAP_ENTER STD { int cap_enter(void); } 517 AUE_CAP_GETMODE STD { int cap_getmode(u_int *modep); } 518 AUE_PDFORK STD { int pdfork(int *fdp, int flags); } @@ -952,12 +952,12 @@ off_t offset, off_t len); } 531 AUE_NULL STD { int posix_fadvise(int fd, off_t offset, \ off_t len, int advice); } -532 AUE_WAIT6 STD { int wait6(int idtype, id_t id, \ +532 AUE_WAIT6 STD { int wait6(idtype_t idtype, id_t id, \ int *status, int options, \ struct __wrusage *wrusage, \ siginfo_t *info); } 533 AUE_CAP_RIGHTS_LIMIT STD { int cap_rights_limit(int fd, \ - uint64_t rights); } + cap_rights_t *rightsp); } 534 AUE_CAP_IOCTLS_LIMIT STD { int cap_ioctls_limit(int fd, \ const u_long *cmds, size_t ncmds); } 535 AUE_CAP_IOCTLS_GET STD { ssize_t cap_ioctls_get(int fd, \ diff --git a/sys/kern/systrace_args.c b/sys/kern/systrace_args.c index 59e046bd352..f3d4363be3a 100644 --- a/sys/kern/systrace_args.c +++ b/sys/kern/systrace_args.c @@ -3126,20 +3126,13 @@ systrace_args(int sysnum, void *params, uint64_t *uarg, int *n_args) *n_args = 2; break; } - /* cap_new */ - case 514: { - struct cap_new_args *p = params; - iarg[0] = p->fd; /* int */ - uarg[1] = p->rights; /* uint64_t */ - *n_args = 2; - break; - } - /* cap_rights_get */ + /* __cap_rights_get */ case 515: { - struct cap_rights_get_args *p = params; - iarg[0] = p->fd; /* int */ - uarg[1] = (intptr_t) p->rightsp; /* uint64_t * */ - *n_args = 2; + struct __cap_rights_get_args *p = params; + iarg[0] = p->version; /* int */ + iarg[1] = p->fd; /* int */ + uarg[2] = (intptr_t) p->rightsp; /* cap_rights_t * */ + *n_args = 3; break; } /* cap_enter */ @@ -3277,7 +3270,7 @@ systrace_args(int sysnum, void *params, uint64_t *uarg, int *n_args) /* wait6 */ case 532: { struct wait6_args *p = params; - iarg[0] = p->idtype; /* int */ + iarg[0] = p->idtype; /* idtype_t */ iarg[1] = p->id; /* id_t */ uarg[2] = (intptr_t) p->status; /* int * */ iarg[3] = p->options; /* int */ @@ -3290,7 +3283,7 @@ systrace_args(int sysnum, void *params, uint64_t *uarg, int *n_args) case 533: { struct cap_rights_limit_args *p = params; iarg[0] = p->fd; /* int */ - uarg[1] = p->rights; /* uint64_t */ + uarg[1] = (intptr_t) p->rightsp; /* cap_rights_t * */ *n_args = 2; break; } @@ -8561,27 +8554,17 @@ systrace_entry_setargdesc(int sysnum, int ndx, char *desc, size_t descsz) break; }; break; - /* cap_new */ - case 514: - switch(ndx) { - case 0: - p = "int"; - break; - case 1: - p = "uint64_t"; - break; - default: - break; - }; - break; - /* cap_rights_get */ + /* __cap_rights_get */ case 515: switch(ndx) { case 0: p = "int"; break; case 1: - p = "uint64_t *"; + p = "int"; + break; + case 2: + p = "cap_rights_t *"; break; default: break; @@ -8821,7 +8804,7 @@ systrace_entry_setargdesc(int sysnum, int ndx, char *desc, size_t descsz) case 532: switch(ndx) { case 0: - p = "int"; + p = "idtype_t"; break; case 1: p = "id_t"; @@ -8849,7 +8832,7 @@ systrace_entry_setargdesc(int sysnum, int ndx, char *desc, size_t descsz) p = "int"; break; case 1: - p = "uint64_t"; + p = "cap_rights_t *"; break; default: break; @@ -10818,12 +10801,7 @@ systrace_return_setargdesc(int sysnum, int ndx, char *desc, size_t descsz) if (ndx == 0 || ndx == 1) p = "int"; break; - /* cap_new */ - case 514: - if (ndx == 0 || ndx == 1) - p = "int"; - break; - /* cap_rights_get */ + /* __cap_rights_get */ case 515: if (ndx == 0 || ndx == 1) p = "int"; diff --git a/sys/kern/sysv_shm.c b/sys/kern/sysv_shm.c index 90f5d7764fd..ea44debd176 100644 --- a/sys/kern/sysv_shm.c +++ b/sys/kern/sysv_shm.c @@ -413,7 +413,7 @@ kern_shmat(td, shmid, shmaddr, shmflg) vm_object_reference(shmseg->object); rv = vm_map_find(&p->p_vmspace->vm_map, shmseg->object, - 0, &attach_va, size, (flags & MAP_FIXED) ? VMFS_NO_SPACE : + 0, &attach_va, size, 0, (flags & MAP_FIXED) ? VMFS_NO_SPACE : VMFS_OPTIMAL_SPACE, prot, prot, MAP_INHERIT_SHARE); if (rv != KERN_SUCCESS) { vm_object_deallocate(shmseg->object); diff --git a/sys/kern/tty.c b/sys/kern/tty.c index 02eccd7ca0f..4fce6072d65 100644 --- a/sys/kern/tty.c +++ b/sys/kern/tty.c @@ -1837,11 +1837,13 @@ ttyhook_register(struct tty **rtp, struct proc *p, int fd, struct cdev *dev; struct cdevsw *cdp; struct filedesc *fdp; + cap_rights_t rights; int error, ref; /* Validate the file descriptor. */ fdp = p->p_fd; - error = fget_unlocked(fdp, fd, CAP_TTYHOOK, 0, &fp, NULL); + error = fget_unlocked(fdp, fd, cap_rights_init(&rights, CAP_TTYHOOK), + 0, &fp, NULL); if (error != 0) return (error); if (fp->f_ops == &badfileops) { diff --git a/sys/kern/tty_pts.c b/sys/kern/tty_pts.c index d6a247793d7..8d2ac0324b3 100644 --- a/sys/kern/tty_pts.c +++ b/sys/kern/tty_pts.c @@ -599,6 +599,7 @@ static struct fileops ptsdev_ops = { .fo_close = ptsdev_close, .fo_chmod = invfo_chmod, .fo_chown = invfo_chown, + .fo_sendfile = invfo_sendfile, .fo_flags = DFLAG_PASSABLE, }; diff --git a/sys/kern/uipc_cow.c b/sys/kern/uipc_cow.c index 654b6d7095c..8a3a5ff0475 100644 --- a/sys/kern/uipc_cow.c +++ b/sys/kern/uipc_cow.c @@ -70,10 +70,10 @@ struct netsend_cow_stats { static struct netsend_cow_stats socow_stats; -static void socow_iodone(void *addr, void *args); +static int socow_iodone(struct mbuf *m, void *addr, void *args); -static void -socow_iodone(void *addr, void *args) +static int +socow_iodone(struct mbuf *m, void *addr, void *args) { struct sf_buf *sf; vm_page_t pp; @@ -94,6 +94,7 @@ socow_iodone(void *addr, void *args) vm_page_free(pp); vm_page_unlock(pp); socow_stats.iodone++; + return (EXT_FREE_OK); } int diff --git a/sys/kern/uipc_debug.c b/sys/kern/uipc_debug.c index 57f4017b4f3..128c64b1066 100644 --- a/sys/kern/uipc_debug.c +++ b/sys/kern/uipc_debug.c @@ -411,7 +411,7 @@ db_print_sockbuf(struct sockbuf *sb, const char *sockbufname, int indent) db_print_indent(indent); db_printf("sb_ctl: %u ", sb->sb_ctl); db_printf("sb_lowat: %d ", sb->sb_lowat); - db_printf("sb_timeo: %d\n", sb->sb_timeo); + db_printf("sb_timeo: %jd\n", sb->sb_timeo); db_print_indent(indent); db_printf("sb_flags: 0x%x (", sb->sb_flags); diff --git a/sys/kern/uipc_mbuf.c b/sys/kern/uipc_mbuf.c index f555adf36fc..8e278a47c0a 100644 --- a/sys/kern/uipc_mbuf.c +++ b/sys/kern/uipc_mbuf.c @@ -84,6 +84,14 @@ SYSCTL_INT(_kern_ipc, OID_AUTO, m_defragrandomfailures, CTLFLAG_RW, &m_defragrandomfailures, 0, ""); #endif +/* + * Ensure the correct size of various mbuf parameters. It could be off due + * to compiler-induced padding and alignment artifacts. + */ +CTASSERT(sizeof(struct mbuf) == MSIZE); +CTASSERT(MSIZE - offsetof(struct mbuf, m_dat) == MLEN); +CTASSERT(MSIZE - offsetof(struct mbuf, m_pktdat) == MHLEN); + /* * m_get2() allocates minimum mbuf that would fit "size" argument. */ @@ -247,8 +255,8 @@ m_freem(struct mbuf *mb) */ int m_extadd(struct mbuf *mb, caddr_t buf, u_int size, - void (*freef)(void *, void *), void *arg1, void *arg2, int flags, int type, - int wait) + int (*freef)(struct mbuf *, void *, void *), void *arg1, void *arg2, + int flags, int type, int wait) { KASSERT(type != EXT_CLUSTER, ("%s: EXT_CLUSTER not allowed", __func__)); @@ -267,6 +275,7 @@ m_extadd(struct mbuf *mb, caddr_t buf, u_int size, mb->m_ext.ext_arg1 = arg1; mb->m_ext.ext_arg2 = arg2; mb->m_ext.ext_type = type; + mb->m_ext.ext_flags = 0; return (0); } @@ -283,12 +292,11 @@ mb_free_ext(struct mbuf *m) KASSERT((m->m_flags & M_EXT) == M_EXT, ("%s: M_EXT not set", __func__)); KASSERT(m->m_ext.ref_cnt != NULL, ("%s: ref_cnt not set", __func__)); - /* * check if the header is embedded in the cluster - */ + */ skipmbuf = (m->m_flags & M_NOFREE); - + /* Free attached storage if this mbuf is the only reference to it. */ if (*(m->m_ext.ref_cnt) == 1 || atomic_fetchadd_int(m->m_ext.ref_cnt, -1) == 1) { @@ -321,7 +329,7 @@ mb_free_ext(struct mbuf *m) case EXT_EXTREF: KASSERT(m->m_ext.ext_free != NULL, ("%s: ext_free not set", __func__)); - (*(m->m_ext.ext_free))(m->m_ext.ext_arg1, + (void)(*(m->m_ext.ext_free))(m, m->m_ext.ext_arg1, m->m_ext.ext_arg2); break; default: @@ -343,6 +351,7 @@ mb_free_ext(struct mbuf *m) m->m_ext.ref_cnt = NULL; m->m_ext.ext_size = 0; m->m_ext.ext_type = 0; + m->m_ext.ext_flags = 0; m->m_flags &= ~M_EXT; uma_zfree(zone_mbuf, m); } @@ -369,6 +378,7 @@ mb_dupcl(struct mbuf *n, struct mbuf *m) n->m_ext.ext_size = m->m_ext.ext_size; n->m_ext.ref_cnt = m->m_ext.ref_cnt; n->m_ext.ext_type = m->m_ext.ext_type; + n->m_ext.ext_flags = m->m_ext.ext_flags; n->m_flags |= M_EXT; n->m_flags |= m->m_flags & M_RDONLY; } @@ -436,11 +446,6 @@ m_sanity(struct mbuf *m0, int sanitize) M_SANITY_ACTION("m_data outside mbuf data range right"); if ((caddr_t)m->m_data + m->m_len > b) M_SANITY_ACTION("m_data + m_len exeeds mbuf space"); - if ((m->m_flags & M_PKTHDR) && m->m_pkthdr.header) { - if ((caddr_t)m->m_pkthdr.header < a || - (caddr_t)m->m_pkthdr.header > b) - M_SANITY_ACTION("m_pkthdr.header outside mbuf data range"); - } /* m->m_nextpkt may only be set on first mbuf in chain. */ if (m != m0 && m->m_nextpkt != NULL) { @@ -744,7 +749,6 @@ m_copymdata(struct mbuf *m, struct mbuf *n, int off, int len, return NULL; bcopy(&buf, mm->m_ext.ext_buf, mm->m_len); mm->m_data = mm->m_ext.ext_buf; - mm->m_pkthdr.header = NULL; } if (prep && !(mm->m_flags & M_EXT) && len > M_LEADINGSPACE(mm)) { bcopy(mm->m_data, &buf, mm->m_len); @@ -755,7 +759,6 @@ m_copymdata(struct mbuf *m, struct mbuf *n, int off, int len, mm->m_ext.ext_size - mm->m_len, mm->m_len); mm->m_data = (caddr_t)mm->m_ext.ext_buf + mm->m_ext.ext_size - mm->m_len; - mm->m_pkthdr.header = NULL; } /* Append/prepend as many mbuf (clusters) as necessary to fit len. */ diff --git a/sys/kern/uipc_mqueue.c b/sys/kern/uipc_mqueue.c index 11acedb6134..fe7e88645b1 100644 --- a/sys/kern/uipc_mqueue.c +++ b/sys/kern/uipc_mqueue.c @@ -2046,7 +2046,7 @@ sys_kmq_open(struct thread *td, struct kmq_open_args *uap) struct mq_attr attr; int flags, error; - if ((uap->flags & O_ACCMODE) == O_ACCMODE) + if ((uap->flags & O_ACCMODE) == O_ACCMODE || uap->flags & O_EXEC) return (EINVAL); flags = FFLAGS(uap->flags); if ((flags & O_CREAT) != 0 && uap->attr != NULL) { @@ -2086,19 +2086,19 @@ sys_kmq_unlink(struct thread *td, struct kmq_unlink_args *uap) return (error); } -typedef int (*_fgetf)(struct thread *, int, cap_rights_t, struct file **); +typedef int (*_fgetf)(struct thread *, int, cap_rights_t *, struct file **); /* * Get message queue by giving file slot */ static int -_getmq(struct thread *td, int fd, cap_rights_t rights, _fgetf func, +_getmq(struct thread *td, int fd, cap_rights_t *rightsp, _fgetf func, struct file **fpp, struct mqfs_node **ppn, struct mqueue **pmq) { struct mqfs_node *pn; int error; - error = func(td, fd, rights, fpp); + error = func(td, fd, rightsp, fpp); if (error) return (error); if (&mqueueops != (*fpp)->f_ops) { @@ -2117,21 +2117,30 @@ static __inline int getmq(struct thread *td, int fd, struct file **fpp, struct mqfs_node **ppn, struct mqueue **pmq) { - return _getmq(td, fd, CAP_POLL_EVENT, fget, fpp, ppn, pmq); + cap_rights_t rights; + + return _getmq(td, fd, cap_rights_init(&rights, CAP_POLL_EVENT), fget, + fpp, ppn, pmq); } static __inline int getmq_read(struct thread *td, int fd, struct file **fpp, struct mqfs_node **ppn, struct mqueue **pmq) { - return _getmq(td, fd, CAP_READ, fget_read, fpp, ppn, pmq); + cap_rights_t rights; + + return _getmq(td, fd, cap_rights_init(&rights, CAP_READ), fget_read, + fpp, ppn, pmq); } static __inline int getmq_write(struct thread *td, int fd, struct file **fpp, struct mqfs_node **ppn, struct mqueue **pmq) { - return _getmq(td, fd, CAP_WRITE, fget_write, fpp, ppn, pmq); + cap_rights_t rights; + + return _getmq(td, fd, cap_rights_init(&rights, CAP_WRITE), fget_write, + fpp, ppn, pmq); } static int @@ -2238,6 +2247,9 @@ sys_kmq_timedsend(struct thread *td, struct kmq_timedsend_args *uap) static int kern_kmq_notify(struct thread *td, int mqd, struct sigevent *sigev) { +#ifdef CAPABILITIES + cap_rights_t rights; +#endif struct filedesc *fdp; struct proc *p; struct mqueue *mq; @@ -2269,7 +2281,8 @@ again: goto out; } #ifdef CAPABILITIES - error = cap_check(cap_rights(fdp, mqd), CAP_POLL_EVENT); + error = cap_check(cap_rights(fdp, mqd), + cap_rights_init(&rights, CAP_POLL_EVENT)); if (error) { FILEDESC_SUNLOCK(fdp); goto out; @@ -2597,7 +2610,8 @@ static struct fileops mqueueops = { .fo_stat = mqf_stat, .fo_chmod = mqf_chmod, .fo_chown = mqf_chown, - .fo_close = mqf_close + .fo_close = mqf_close, + .fo_sendfile = invfo_sendfile, }; static struct vop_vector mqfs_vnodeops = { @@ -2681,7 +2695,7 @@ freebsd32_kmq_open(struct thread *td, struct freebsd32_kmq_open_args *uap) struct mq_attr32 attr32; int flags, error; - if ((uap->flags & O_ACCMODE) == O_ACCMODE) + if ((uap->flags & O_ACCMODE) == O_ACCMODE || uap->flags & O_EXEC) return (EINVAL); flags = FFLAGS(uap->flags); if ((flags & O_CREAT) != 0 && uap->attr != NULL) { diff --git a/sys/kern/uipc_sem.c b/sys/kern/uipc_sem.c index a9f60f1e538..f641654e8a5 100644 --- a/sys/kern/uipc_sem.c +++ b/sys/kern/uipc_sem.c @@ -116,7 +116,7 @@ static int ksem_create(struct thread *td, const char *path, semid_t *semidp, mode_t mode, unsigned int value, int flags, int compat32); static void ksem_drop(struct ksem *ks); -static int ksem_get(struct thread *td, semid_t id, cap_rights_t rights, +static int ksem_get(struct thread *td, semid_t id, cap_rights_t *rightsp, struct file **fpp); static struct ksem *ksem_hold(struct ksem *ks); static void ksem_insert(char *path, Fnv32_t fnv, struct ksem *ks); @@ -149,6 +149,7 @@ static struct fileops ksem_ops = { .fo_close = ksem_closef, .fo_chmod = ksem_chmod, .fo_chown = ksem_chown, + .fo_sendfile = invfo_sendfile, .fo_flags = DFLAG_PASSABLE }; @@ -599,13 +600,14 @@ ksem_create(struct thread *td, const char *name, semid_t *semidp, mode_t mode, } static int -ksem_get(struct thread *td, semid_t id, cap_rights_t rights, struct file **fpp) +ksem_get(struct thread *td, semid_t id, cap_rights_t *rightsp, + struct file **fpp) { struct ksem *ks; struct file *fp; int error; - error = fget(td, id, rights, &fp); + error = fget(td, id, rightsp, &fp); if (error) return (EINVAL); if (fp->f_type != DTYPE_SEM) { @@ -719,11 +721,13 @@ struct ksem_post_args { int sys_ksem_post(struct thread *td, struct ksem_post_args *uap) { + cap_rights_t rights; struct file *fp; struct ksem *ks; int error; - error = ksem_get(td, uap->id, CAP_SEM_POST, &fp); + error = ksem_get(td, uap->id, + cap_rights_init(&rights, CAP_SEM_POST), &fp); if (error) return (error); ks = fp->f_data; @@ -808,12 +812,13 @@ kern_sem_wait(struct thread *td, semid_t id, int tryflag, { struct timespec ts1, ts2; struct timeval tv; + cap_rights_t rights; struct file *fp; struct ksem *ks; int error; DP((">>> kern_sem_wait entered! pid=%d\n", (int)td->td_proc->p_pid)); - error = ksem_get(td, id, CAP_SEM_WAIT, &fp); + error = ksem_get(td, id, cap_rights_init(&rights, CAP_SEM_WAIT), &fp); if (error) return (error); ks = fp->f_data; @@ -875,11 +880,13 @@ struct ksem_getvalue_args { int sys_ksem_getvalue(struct thread *td, struct ksem_getvalue_args *uap) { + cap_rights_t rights; struct file *fp; struct ksem *ks; int error, val; - error = ksem_get(td, uap->id, CAP_SEM_GETVALUE, &fp); + error = ksem_get(td, uap->id, + cap_rights_init(&rights, CAP_SEM_GETVALUE), &fp); if (error) return (error); ks = fp->f_data; diff --git a/sys/kern/uipc_shm.c b/sys/kern/uipc_shm.c index 496b771cae4..372b583a543 100644 --- a/sys/kern/uipc_shm.c +++ b/sys/kern/uipc_shm.c @@ -69,6 +69,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include @@ -119,6 +120,7 @@ static fo_stat_t shm_stat; static fo_close_t shm_close; static fo_chmod_t shm_chmod; static fo_chown_t shm_chown; +static fo_seek_t shm_seek; /* File descriptor operations. */ static struct fileops shm_ops = { @@ -132,25 +134,198 @@ static struct fileops shm_ops = { .fo_close = shm_close, .fo_chmod = shm_chmod, .fo_chown = shm_chown, - .fo_flags = DFLAG_PASSABLE + .fo_sendfile = vn_sendfile, + .fo_seek = shm_seek, + .fo_flags = DFLAG_PASSABLE | DFLAG_SEEKABLE }; FEATURE(posix_shm, "POSIX shared memory"); +static int +uiomove_object_page(vm_object_t obj, size_t len, struct uio *uio) +{ + vm_page_t m; + vm_pindex_t idx; + size_t tlen; + int error, offset, rv; + + idx = OFF_TO_IDX(uio->uio_offset); + offset = uio->uio_offset & PAGE_MASK; + tlen = MIN(PAGE_SIZE - offset, len); + + VM_OBJECT_WLOCK(obj); + + /* + * Parallel reads of the page content from disk are prevented + * by exclusive busy. + * + * Although the tmpfs vnode lock is held here, it is + * nonetheless safe to sleep waiting for a free page. The + * pageout daemon does not need to acquire the tmpfs vnode + * lock to page out tobj's pages because tobj is a OBJT_SWAP + * type object. + */ + m = vm_page_grab(obj, idx, VM_ALLOC_NORMAL); + if (m->valid != VM_PAGE_BITS_ALL) { + if (vm_pager_has_page(obj, idx, NULL, NULL)) { + rv = vm_pager_get_pages(obj, &m, 1, 0); + m = vm_page_lookup(obj, idx); + if (m == NULL) { + printf( + "uiomove_object: vm_obj %p idx %jd null lookup rv %d\n", + obj, idx, rv); + VM_OBJECT_WUNLOCK(obj); + return (EIO); + } + if (rv != VM_PAGER_OK) { + printf( + "uiomove_object: vm_obj %p idx %jd valid %x pager error %d\n", + obj, idx, m->valid, rv); + vm_page_lock(m); + vm_page_free(m); + vm_page_unlock(m); + VM_OBJECT_WUNLOCK(obj); + return (EIO); + } + } else + vm_page_zero_invalid(m, TRUE); + } + vm_page_xunbusy(m); + vm_page_lock(m); + vm_page_hold(m); + vm_page_unlock(m); + VM_OBJECT_WUNLOCK(obj); + error = uiomove_fromphys(&m, offset, tlen, uio); + if (uio->uio_rw == UIO_WRITE && error == 0) { + VM_OBJECT_WLOCK(obj); + vm_page_dirty(m); + VM_OBJECT_WUNLOCK(obj); + } + vm_page_lock(m); + vm_page_unhold(m); + if (m->queue == PQ_NONE) { + vm_page_deactivate(m); + } else { + /* Requeue to maintain LRU ordering. */ + vm_page_requeue(m); + } + vm_page_unlock(m); + + return (error); +} + +int +uiomove_object(vm_object_t obj, off_t obj_size, struct uio *uio) +{ + ssize_t resid; + size_t len; + int error; + + error = 0; + while ((resid = uio->uio_resid) > 0) { + if (obj_size <= uio->uio_offset) + break; + len = MIN(obj_size - uio->uio_offset, resid); + if (len == 0) + break; + error = uiomove_object_page(obj, len, uio); + if (error != 0 || resid == uio->uio_resid) + break; + } + return (error); +} + +static int +shm_seek(struct file *fp, off_t offset, int whence, struct thread *td) +{ + struct shmfd *shmfd; + off_t foffset; + int error; + + shmfd = fp->f_data; + foffset = foffset_lock(fp, 0); + error = 0; + switch (whence) { + case L_INCR: + if (foffset < 0 || + (offset > 0 && foffset > OFF_MAX - offset)) { + error = EOVERFLOW; + break; + } + offset += foffset; + break; + case L_XTND: + if (offset > 0 && shmfd->shm_size > OFF_MAX - offset) { + error = EOVERFLOW; + break; + } + offset += shmfd->shm_size; + break; + case L_SET: + break; + default: + error = EINVAL; + } + if (error == 0) { + if (offset < 0 || offset > shmfd->shm_size) + error = EINVAL; + else + *(off_t *)(td->td_retval) = offset; + } + foffset_unlock(fp, offset, error != 0 ? FOF_NOUPDATE : 0); + return (error); +} + static int shm_read(struct file *fp, struct uio *uio, struct ucred *active_cred, int flags, struct thread *td) { + struct shmfd *shmfd; + void *rl_cookie; + int error; - return (EOPNOTSUPP); + shmfd = fp->f_data; + foffset_lock_uio(fp, uio, flags); + rl_cookie = rangelock_rlock(&shmfd->shm_rl, uio->uio_offset, + uio->uio_offset + uio->uio_resid, &shmfd->shm_mtx); +#ifdef MAC + error = mac_posixshm_check_read(active_cred, fp->f_cred, shmfd); + if (error) + return (error); +#endif + error = uiomove_object(shmfd->shm_object, shmfd->shm_size, uio); + rangelock_unlock(&shmfd->shm_rl, rl_cookie, &shmfd->shm_mtx); + foffset_unlock_uio(fp, uio, flags); + return (error); } static int shm_write(struct file *fp, struct uio *uio, struct ucred *active_cred, int flags, struct thread *td) { + struct shmfd *shmfd; + void *rl_cookie; + int error; - return (EOPNOTSUPP); + shmfd = fp->f_data; +#ifdef MAC + error = mac_posixshm_check_write(active_cred, fp->f_cred, shmfd); + if (error) + return (error); +#endif + foffset_lock_uio(fp, uio, flags); + if ((flags & FOF_OFFSET) == 0) { + rl_cookie = rangelock_wlock(&shmfd->shm_rl, 0, OFF_MAX, + &shmfd->shm_mtx); + } else { + rl_cookie = rangelock_wlock(&shmfd->shm_rl, uio->uio_offset, + uio->uio_offset + uio->uio_resid, &shmfd->shm_mtx); + } + + error = uiomove_object(shmfd->shm_object, shmfd->shm_size, uio); + rangelock_unlock(&shmfd->shm_rl, rl_cookie, &shmfd->shm_mtx); + foffset_unlock_uio(fp, uio, flags); + return (error); } static int @@ -281,11 +456,8 @@ shm_dotruncate(struct shmfd *shmfd, off_t length) retry: m = vm_page_lookup(object, idx); if (m != NULL) { - if ((m->oflags & VPO_BUSY) != 0 || - m->busy != 0) { - vm_page_sleep(m, "shmtrc"); + if (vm_page_sleep_if_busy(m, "shmtrc")) goto retry; - } } else if (vm_pager_has_page(object, idx, NULL, NULL)) { m = vm_page_alloc(object, idx, VM_ALLOC_NORMAL); if (m == NULL) { @@ -305,7 +477,7 @@ retry: if (rv == VM_PAGER_OK) { vm_page_deactivate(m); vm_page_unlock(m); - vm_page_wakeup(m); + vm_page_xunbusy(m); } else { vm_page_free(m); vm_page_unlock(m); @@ -379,6 +551,8 @@ shm_alloc(struct ucred *ucred, mode_t mode) shmfd->shm_atime = shmfd->shm_mtime = shmfd->shm_ctime = shmfd->shm_birthtime; refcount_init(&shmfd->shm_refs, 1); + mtx_init(&shmfd->shm_mtx, "shmrl", NULL, MTX_DEF); + rangelock_init(&shmfd->shm_rl); #ifdef MAC mac_posixshm_init(shmfd); mac_posixshm_create(ucred, shmfd); @@ -403,6 +577,8 @@ shm_drop(struct shmfd *shmfd) #ifdef MAC mac_posixshm_destroy(shmfd); #endif + rangelock_destroy(&shmfd->shm_rl); + mtx_destroy(&shmfd->shm_mtx); vm_object_deallocate(shmfd->shm_object); free(shmfd, M_SHMFD); } @@ -778,7 +954,7 @@ shm_map(struct file *fp, size_t size, off_t offset, void **memp) ofs = offset & PAGE_MASK; offset = trunc_page(offset); size = round_page(size + ofs); - rv = vm_map_find(kernel_map, obj, offset, &kva, size, + rv = vm_map_find(kernel_map, obj, offset, &kva, size, 0, VMFS_OPTIMAL_SPACE, VM_PROT_READ | VM_PROT_WRITE, VM_PROT_READ | VM_PROT_WRITE, 0); if (rv == KERN_SUCCESS) { diff --git a/sys/kern/uipc_sockbuf.c b/sys/kern/uipc_sockbuf.c index a09dbc613fd..9fa8ae0aa7b 100644 --- a/sys/kern/uipc_sockbuf.c +++ b/sys/kern/uipc_sockbuf.c @@ -127,9 +127,9 @@ sbwait(struct sockbuf *sb) SOCKBUF_LOCK_ASSERT(sb); sb->sb_flags |= SB_WAIT; - return (msleep(&sb->sb_cc, &sb->sb_mtx, + return (msleep_sbt(&sb->sb_cc, &sb->sb_mtx, (sb->sb_flags & SB_NOINTR) ? PSOCK : PSOCK | PCATCH, "sbwait", - sb->sb_timeo)); + sb->sb_timeo, 0, 0)); } int diff --git a/sys/kern/uipc_socket.c b/sys/kern/uipc_socket.c index 9330bb4902a..66af1810513 100644 --- a/sys/kern/uipc_socket.c +++ b/sys/kern/uipc_socket.c @@ -105,7 +105,6 @@ __FBSDID("$FreeBSD$"); #include "opt_inet.h" #include "opt_inet6.h" -#include "opt_zero.h" #include "opt_compat.h" #include @@ -221,21 +220,6 @@ static int numopensockets; SYSCTL_INT(_kern_ipc, OID_AUTO, numopensockets, CTLFLAG_RD, &numopensockets, 0, "Number of open sockets"); -#if defined(SOCKET_SEND_COW) || defined(SOCKET_RECV_PFLIP) -SYSCTL_NODE(_kern_ipc, OID_AUTO, zero_copy, CTLFLAG_RD, 0, - "Zero copy controls"); -#ifdef SOCKET_RECV_PFLIP -int so_zero_copy_receive = 1; -SYSCTL_INT(_kern_ipc_zero_copy, OID_AUTO, receive, CTLFLAG_RW, - &so_zero_copy_receive, 0, "Enable zero copy receive"); -#endif -#ifdef SOCKET_SEND_COW -int so_zero_copy_send = 1; -SYSCTL_INT(_kern_ipc_zero_copy, OID_AUTO, send, CTLFLAG_RW, - &so_zero_copy_send, 0, "Enable zero copy send"); -#endif /* SOCKET_SEND_COW */ -#endif /* SOCKET_SEND_COW || SOCKET_RECV_PFLIP */ - /* * accept_mtx locks down per-socket fields relating to accept queues. See * socketvar.h for an annotation of the protected fields of struct socket. @@ -978,113 +962,6 @@ sodisconnect(struct socket *so) return (error); } -#ifdef SOCKET_SEND_COW -struct so_zerocopy_stats{ - int size_ok; - int align_ok; - int found_ifp; -}; -struct so_zerocopy_stats so_zerocp_stats = {0,0,0}; - -/* - * sosend_copyin() is only used if zero copy sockets are enabled. Otherwise - * sosend_dgram() and sosend_generic() use m_uiotombuf(). - * - * sosend_copyin() accepts a uio and prepares an mbuf chain holding part or - * all of the data referenced by the uio. If desired, it uses zero-copy. - * *space will be updated to reflect data copied in. - * - * NB: If atomic I/O is requested, the caller must already have checked that - * space can hold resid bytes. - * - * NB: In the event of an error, the caller may need to free the partial - * chain pointed to by *mpp. The contents of both *uio and *space may be - * modified even in the case of an error. - */ -static int -sosend_copyin(struct uio *uio, struct mbuf **retmp, int atomic, long *space, - int flags) -{ - struct mbuf *m, **mp, *top; - long len; - ssize_t resid; - int error; - int cow_send; - - *retmp = top = NULL; - mp = ⊤ - len = 0; - resid = uio->uio_resid; - error = 0; - do { - cow_send = 0; - if (resid >= MINCLSIZE) { - if (top == NULL) { - m = m_gethdr(M_WAITOK, MT_DATA); - m->m_pkthdr.len = 0; - m->m_pkthdr.rcvif = NULL; - } else - m = m_get(M_WAITOK, MT_DATA); - if (so_zero_copy_send && - resid >= PAGE_SIZE && - *space >= PAGE_SIZE && - uio->uio_iov->iov_len >= PAGE_SIZE) { - so_zerocp_stats.size_ok++; - so_zerocp_stats.align_ok++; - cow_send = socow_setup(m, uio); - len = cow_send; - } - if (!cow_send) { - m_clget(m, M_WAITOK); - len = min(min(MCLBYTES, resid), *space); - } - } else { - if (top == NULL) { - m = m_gethdr(M_WAITOK, MT_DATA); - m->m_pkthdr.len = 0; - m->m_pkthdr.rcvif = NULL; - - len = min(min(MHLEN, resid), *space); - /* - * For datagram protocols, leave room - * for protocol headers in first mbuf. - */ - if (atomic && m && len < MHLEN) - MH_ALIGN(m, len); - } else { - m = m_get(M_WAITOK, MT_DATA); - len = min(min(MLEN, resid), *space); - } - } - if (m == NULL) { - error = ENOBUFS; - goto out; - } - - *space -= len; - if (cow_send) - error = 0; - else - error = uiomove(mtod(m, void *), (int)len, uio); - resid = uio->uio_resid; - m->m_len = len; - *mp = m; - top->m_pkthdr.len += len; - if (error) - goto out; - mp = &m->m_next; - if (resid <= 0) { - if (flags & MSG_EOR) - top->m_flags |= M_EOR; - break; - } - } while (*space > 0 && atomic); -out: - *retmp = top; - return (error); -} -#endif /* SOCKET_SEND_COW */ - #define SBLOCKWAIT(f) (((f) & MSG_DONTWAIT) ? 0 : SBL_WAIT) int @@ -1094,9 +971,6 @@ sosend_dgram(struct socket *so, struct sockaddr *addr, struct uio *uio, long space; ssize_t resid; int clen = 0, error, dontroute; -#ifdef SOCKET_SEND_COW - int atomic = sosendallatonce(so) || top; -#endif KASSERT(so->so_type == SOCK_DGRAM, ("sosend_dgram: !SOCK_DGRAM")); KASSERT(so->so_proto->pr_flags & PR_ATOMIC, @@ -1179,11 +1053,6 @@ sosend_dgram(struct socket *so, struct sockaddr *addr, struct uio *uio, if (flags & MSG_EOR) top->m_flags |= M_EOR; } else { -#ifdef SOCKET_SEND_COW - error = sosend_copyin(uio, &top, atomic, &space, flags); - if (error) - goto out; -#else /* * Copy the data from userland into a mbuf chain. * If no data is to be copied in, a single empty mbuf @@ -1196,7 +1065,6 @@ sosend_dgram(struct socket *so, struct sockaddr *addr, struct uio *uio, goto out; } space -= resid - uio->uio_resid; -#endif /* SOCKET_SEND_COW */ resid = uio->uio_resid; } KASSERT(resid == 0, ("sosend_dgram: resid != 0")); @@ -1368,12 +1236,6 @@ restart: if (flags & MSG_EOR) top->m_flags |= M_EOR; } else { -#ifdef SOCKET_SEND_COW - error = sosend_copyin(uio, &top, atomic, - &space, flags); - if (error != 0) - goto release; -#else /* * Copy the data from userland into a mbuf * chain. If no data is to be copied in, @@ -1388,7 +1250,6 @@ restart: goto release; } space -= resid - uio->uio_resid; -#endif /* SOCKET_SEND_COW */ resid = uio->uio_resid; } if (dontroute) { @@ -1480,20 +1341,6 @@ soreceive_rcvoob(struct socket *so, struct uio *uio, int flags) if (error) goto bad; do { -#ifdef SOCKET_RECV_PFLIP - if (so_zero_copy_receive) { - int disposable; - - if ((m->m_flags & M_EXT) - && (m->m_ext.ext_type == EXT_DISPOSABLE)) - disposable = 1; - else - disposable = 0; - - error = uiomoveco(mtod(m, void *), - min(uio->uio_resid, m->m_len), uio, disposable); - } else -#endif /* SOCKET_RECV_PFLIP */ error = uiomove(mtod(m, void *), (int) min(uio->uio_resid, m->m_len), uio); m = m_free(m); @@ -1816,20 +1663,6 @@ dontblock: SBLASTRECORDCHK(&so->so_rcv); SBLASTMBUFCHK(&so->so_rcv); SOCKBUF_UNLOCK(&so->so_rcv); -#ifdef SOCKET_RECV_PFLIP - if (so_zero_copy_receive) { - int disposable; - - if ((m->m_flags & M_EXT) - && (m->m_ext.ext_type == EXT_DISPOSABLE)) - disposable = 1; - else - disposable = 0; - - error = uiomoveco(mtod(m, char *) + moff, - (int)len, uio, disposable); - } else -#endif /* SOCKET_RECV_PFLIP */ error = uiomove(mtod(m, char *) + moff, (int)len, uio); SOCKBUF_LOCK(&so->so_rcv); if (error) { @@ -2541,7 +2374,7 @@ sosetopt(struct socket *so, struct sockopt *sopt) int error, optval; struct linger l; struct timeval tv; - u_long val; + sbintime_t val; uint32_t val32; #ifdef MAC struct mac extmac; @@ -2698,22 +2531,12 @@ sosetopt(struct socket *so, struct sockopt *sopt) sizeof tv); if (error) goto bad; - - /* assert(hz > 0); */ - if (tv.tv_sec < 0 || tv.tv_sec > INT_MAX / hz || - tv.tv_usec < 0 || tv.tv_usec >= 1000000) { + if (tv.tv_sec < 0 || tv.tv_usec < 0 || + tv.tv_usec >= 1000000) { error = EDOM; goto bad; } - /* assert(tick > 0); */ - /* assert(ULONG_MAX - INT_MAX >= 1000000); */ - val = (u_long)(tv.tv_sec * hz) + tv.tv_usec / tick; - if (val > INT_MAX) { - error = EDOM; - goto bad; - } - if (val == 0 && tv.tv_usec != 0) - val = 1; + val = tvtosbt(tv); switch (sopt->sopt_name) { case SO_SNDTIMEO: @@ -2867,8 +2690,7 @@ integer: optval = (sopt->sopt_name == SO_SNDTIMEO ? so->so_snd.sb_timeo : so->so_rcv.sb_timeo); - tv.tv_sec = optval / hz; - tv.tv_usec = (optval % hz) * tick; + tv = sbttotv(optval); #ifdef COMPAT_FREEBSD32 if (SV_CURPROC_FLAG(SV_ILP32)) { struct timeval32 tv32; diff --git a/sys/kern/uipc_syscalls.c b/sys/kern/uipc_syscalls.c index c35ef16c24c..bf4b52a6315 100644 --- a/sys/kern/uipc_syscalls.c +++ b/sys/kern/uipc_syscalls.c @@ -45,6 +45,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include #include @@ -57,6 +58,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include #include @@ -86,7 +88,7 @@ __FBSDID("$FreeBSD$"); #include #include #include -#include +#include #include #include @@ -109,15 +111,17 @@ static int recvit(struct thread *td, int s, struct msghdr *mp, void *namelenp); static int accept1(struct thread *td, int s, struct sockaddr *uname, socklen_t *anamelen, int flags); -static int do_sendfile(struct thread *td, struct sendfile_args *uap, int compat); +static int do_sendfile(struct thread *td, struct sendfile_args *uap, + int compat); static int getsockname1(struct thread *td, struct getsockname_args *uap, int compat); static int getpeername1(struct thread *td, struct getpeername_args *uap, int compat); counter_u64_t sfstat[sizeof(struct sfstat) / sizeof(uint64_t)]; + /* - * NSFBUFS-related variables and associated sysctls + * sendfile(2)-related variables and associated sysctls */ int nsfbufs; int nsfbufspeak; @@ -155,19 +159,20 @@ sfstat_sysctl(SYSCTL_HANDLER_ARGS) } SYSCTL_PROC(_kern_ipc, OID_AUTO, sfstat, CTLTYPE_OPAQUE | CTLFLAG_RW, NULL, 0, sfstat_sysctl, "I", "sendfile statistics"); + /* * Convert a user file descriptor to a kernel file entry and check if required * capability rights are present. * A reference on the file entry is held upon returning. */ static int -getsock_cap(struct filedesc *fdp, int fd, cap_rights_t rights, +getsock_cap(struct filedesc *fdp, int fd, cap_rights_t *rightsp, struct file **fpp, u_int *fflagp) { struct file *fp; int error; - error = fget_unlocked(fdp, fd, rights, 0, &fp, NULL); + error = fget_unlocked(fdp, fd, rightsp, 0, &fp, NULL); if (error != 0) return (error); if (fp->f_type != DTYPE_SOCKET) { @@ -217,16 +222,16 @@ sys_socket(td, uap) #ifdef MAC error = mac_socket_check_create(td->td_ucred, uap->domain, type, uap->protocol); - if (error) + if (error != 0) return (error); #endif error = falloc(td, &fp, &fd, oflag); - if (error) + if (error != 0) return (error); /* An extra reference on `fp' has been held for us by falloc(). */ error = socreate(uap->domain, &so, type, uap->protocol, td->td_ucred, td); - if (error) { + if (error != 0) { fdclose(td->td_proc->p_fd, fp, fd, td); } else { finit(fp, FREAD | FWRITE | fflag, DTYPE_SOCKET, so, &socketops); @@ -264,12 +269,14 @@ kern_bindat(struct thread *td, int dirfd, int fd, struct sockaddr *sa) { struct socket *so; struct file *fp; + cap_rights_t rights; int error; AUDIT_ARG_FD(fd); AUDIT_ARG_SOCKADDR(td, dirfd, sa); - error = getsock_cap(td->td_proc->p_fd, fd, CAP_BIND, &fp, NULL); - if (error) + error = getsock_cap(td->td_proc->p_fd, fd, + cap_rights_init(&rights, CAP_BIND), &fp, NULL); + if (error != 0) return (error); so = fp->f_data; #ifdef KTRACE @@ -331,10 +338,12 @@ sys_listen(td, uap) { struct socket *so; struct file *fp; + cap_rights_t rights; int error; AUDIT_ARG_FD(uap->s); - error = getsock_cap(td->td_proc->p_fd, uap->s, CAP_LISTEN, &fp, NULL); + error = getsock_cap(td->td_proc->p_fd, uap->s, + cap_rights_init(&rights, CAP_LISTEN), &fp, NULL); if (error == 0) { so = fp->f_data; #ifdef MAC @@ -367,7 +376,7 @@ accept1(td, s, uname, anamelen, flags) return (kern_accept4(td, s, NULL, NULL, flags, NULL)); error = copyin(anamelen, &namelen, sizeof (namelen)); - if (error) + if (error != 0) return (error); error = kern_accept4(td, s, &name, &namelen, flags, &fp); @@ -376,7 +385,7 @@ accept1(td, s, uname, anamelen, flags) * return a namelen of zero for older code which might * ignore the return value from accept. */ - if (error) { + if (error != 0) { (void) copyout(&namelen, anamelen, sizeof(*anamelen)); return (error); } @@ -392,7 +401,7 @@ accept1(td, s, uname, anamelen, flags) if (error == 0) error = copyout(&namelen, anamelen, sizeof(namelen)); - if (error) + if (error != 0) fdclose(td->td_proc->p_fd, fp, td->td_retval[0], td); fdrop(fp, td); free(name, M_SONAME); @@ -413,20 +422,20 @@ kern_accept4(struct thread *td, int s, struct sockaddr **name, struct filedesc *fdp; struct file *headfp, *nfp = NULL; struct sockaddr *sa = NULL; - int error; struct socket *head, *so; - int fd; + cap_rights_t rights; u_int fflag; pid_t pgid; - int tmp; + int error, fd, tmp; - if (name) + if (name != NULL) *name = NULL; AUDIT_ARG_FD(s); fdp = td->td_proc->p_fd; - error = getsock_cap(fdp, s, CAP_ACCEPT, &headfp, &fflag); - if (error) + error = getsock_cap(fdp, s, cap_rights_init(&rights, CAP_ACCEPT), + &headfp, &fflag); + if (error != 0) return (error); head = headfp->f_data; if ((head->so_options & SO_ACCEPTCONN) == 0) { @@ -439,7 +448,7 @@ kern_accept4(struct thread *td, int s, struct sockaddr **name, goto done; #endif error = falloc(td, &nfp, &fd, (flags & SOCK_CLOEXEC) ? O_CLOEXEC : 0); - if (error) + if (error != 0) goto done; ACCEPT_LOCK(); if ((head->so_state & SS_NBIO) && TAILQ_EMPTY(&head->so_comp)) { @@ -454,7 +463,7 @@ kern_accept4(struct thread *td, int s, struct sockaddr **name, } error = msleep(&head->so_timeo, &accept_mtx, PSOCK | PCATCH, "accept", 0); - if (error) { + if (error != 0) { ACCEPT_UNLOCK(); goto noconnection; } @@ -513,7 +522,7 @@ kern_accept4(struct thread *td, int s, struct sockaddr **name, (void) fo_ioctl(nfp, FIOASYNC, &tmp, td->td_ucred, td); sa = 0; error = soaccept(so, &sa); - if (error) { + if (error != 0) { /* * return a namelen of zero for older code which might * ignore the return value from accept. @@ -540,14 +549,13 @@ kern_accept4(struct thread *td, int s, struct sockaddr **name, sa = NULL; } noconnection: - if (sa) - free(sa, M_SONAME); + free(sa, M_SONAME); /* * close the new descriptor, assuming someone hasn't ripped it * out from under us. */ - if (error) + if (error != 0) fdclose(fdp, nfp, fd, td); /* @@ -582,6 +590,7 @@ sys_accept4(td, uap) struct thread *td; struct accept4_args *uap; { + if (uap->flags & ~(SOCK_CLOEXEC | SOCK_NONBLOCK)) return (EINVAL); @@ -626,13 +635,14 @@ kern_connectat(struct thread *td, int dirfd, int fd, struct sockaddr *sa) { struct socket *so; struct file *fp; - int error; - int interrupted = 0; + cap_rights_t rights; + int error, interrupted = 0; AUDIT_ARG_FD(fd); AUDIT_ARG_SOCKADDR(td, dirfd, sa); - error = getsock_cap(td->td_proc->p_fd, fd, CAP_CONNECT, &fp, NULL); - if (error) + error = getsock_cap(td->td_proc->p_fd, fd, + cap_rights_init(&rights, CAP_CONNECT), &fp, NULL); + if (error != 0) return (error); so = fp->f_data; if (so->so_state & SS_ISCONNECTING) { @@ -645,14 +655,14 @@ kern_connectat(struct thread *td, int dirfd, int fd, struct sockaddr *sa) #endif #ifdef MAC error = mac_socket_check_connect(td->td_ucred, so, sa); - if (error) + if (error != 0) goto bad; #endif if (dirfd == AT_FDCWD) error = soconnect(so, sa, td); else error = soconnectat(dirfd, so, sa, td); - if (error) + if (error != 0) goto bad; if ((so->so_state & SS_NBIO) && (so->so_state & SS_ISCONNECTING)) { error = EINPROGRESS; @@ -662,7 +672,7 @@ kern_connectat(struct thread *td, int dirfd, int fd, struct sockaddr *sa) while ((so->so_state & SS_ISCONNECTING) && so->so_error == 0) { error = msleep(&so->so_timeo, SOCK_MTX(so), PSOCK | PCATCH, "connec", 0); - if (error) { + if (error != 0) { if (error == EINTR || error == ERESTART) interrupted = 1; break; @@ -737,35 +747,35 @@ kern_socketpair(struct thread *td, int domain, int type, int protocol, /* We might want to have a separate check for socket pairs. */ error = mac_socket_check_create(td->td_ucred, domain, type, protocol); - if (error) + if (error != 0) return (error); #endif error = socreate(domain, &so1, type, protocol, td->td_ucred, td); - if (error) + if (error != 0) return (error); error = socreate(domain, &so2, type, protocol, td->td_ucred, td); - if (error) + if (error != 0) goto free1; /* On success extra reference to `fp1' and 'fp2' is set by falloc. */ error = falloc(td, &fp1, &fd, oflag); - if (error) + if (error != 0) goto free2; rsv[0] = fd; fp1->f_data = so1; /* so1 already has ref count */ error = falloc(td, &fp2, &fd, oflag); - if (error) + if (error != 0) goto free3; fp2->f_data = so2; /* so2 already has ref count */ rsv[1] = fd; error = soconnect2(so1, so2); - if (error) + if (error != 0) goto free4; if (type == SOCK_DGRAM) { /* * Datagram socket connection is asymmetric. */ error = soconnect2(so2, so1); - if (error) + if (error != 0) goto free4; } finit(fp1, FREAD | FWRITE | fflag, DTYPE_SOCKET, fp1->f_data, @@ -801,10 +811,10 @@ sys_socketpair(struct thread *td, struct socketpair_args *uap) error = kern_socketpair(td, uap->domain, uap->type, uap->protocol, sv); - if (error) + if (error != 0) return (error); error = copyout(sv, uap->rsv, 2 * sizeof(int)); - if (error) { + if (error != 0) { (void)kern_close(td, sv[0]); (void)kern_close(td, sv[1]); } @@ -829,7 +839,7 @@ sendit(td, s, mp, flags) if (mp->msg_name != NULL) { error = getsockaddr(&to, mp->msg_name, mp->msg_namelen); - if (error) { + if (error != 0) { to = NULL; goto bad; } @@ -849,7 +859,7 @@ sendit(td, s, mp, flags) } error = sockargs(&control, mp->msg_control, mp->msg_controllen, MT_CONTROL); - if (error) + if (error != 0) goto bad; #ifdef COMPAT_OLDSOCK if (mp->msg_flags == MSG_COMPAT) { @@ -869,8 +879,7 @@ sendit(td, s, mp, flags) error = kern_sendit(td, s, mp, flags, control, UIO_USERSPACE); bad: - if (to) - free(to, M_SONAME); + free(to, M_SONAME); return (error); } @@ -887,21 +896,21 @@ kern_sendit(td, s, mp, flags, control, segflg) struct uio auio; struct iovec *iov; struct socket *so; - int i, error; - ssize_t len; cap_rights_t rights; #ifdef KTRACE struct uio *ktruio = NULL; #endif + ssize_t len; + int i, error; AUDIT_ARG_FD(s); - rights = CAP_SEND; + cap_rights_init(&rights, CAP_SEND); if (mp->msg_name != NULL) { AUDIT_ARG_SOCKADDR(td, AT_FDCWD, mp->msg_name); - rights |= CAP_CONNECT; + cap_rights_set(&rights, CAP_CONNECT); } - error = getsock_cap(td->td_proc->p_fd, s, rights, &fp, NULL); - if (error) + error = getsock_cap(td->td_proc->p_fd, s, &rights, &fp, NULL); + if (error != 0) return (error); so = (struct socket *)fp->f_data; @@ -913,11 +922,11 @@ kern_sendit(td, s, mp, flags, control, segflg) if (mp->msg_name != NULL) { error = mac_socket_check_connect(td->td_ucred, so, mp->msg_name); - if (error) + if (error != 0) goto bad; } error = mac_socket_check_send(td->td_ucred, so); - if (error) + if (error != 0) goto bad; #endif @@ -941,7 +950,7 @@ kern_sendit(td, s, mp, flags, control, segflg) #endif len = auio.uio_resid; error = sosend(so, mp->msg_name, &auio, 0, control, flags, td); - if (error) { + if (error != 0) { if (auio.uio_resid != len && (error == ERESTART || error == EINTR || error == EWOULDBLOCK)) error = 0; @@ -980,7 +989,6 @@ sys_sendto(td, uap) { struct msghdr msg; struct iovec aiov; - int error; msg.msg_name = uap->to; msg.msg_namelen = uap->tolen; @@ -992,8 +1000,7 @@ sys_sendto(td, uap) #endif aiov.iov_base = uap->buf; aiov.iov_len = uap->len; - error = sendit(td, uap->s, &msg, uap->flags); - return (error); + return (sendit(td, uap->s, &msg, uap->flags)); } #ifdef COMPAT_OLDSOCK @@ -1009,7 +1016,6 @@ osend(td, uap) { struct msghdr msg; struct iovec aiov; - int error; msg.msg_name = 0; msg.msg_namelen = 0; @@ -1019,8 +1025,7 @@ osend(td, uap) aiov.iov_len = uap->len; msg.msg_control = 0; msg.msg_flags = 0; - error = sendit(td, uap->s, &msg, uap->flags); - return (error); + return (sendit(td, uap->s, &msg, uap->flags)); } int @@ -1037,10 +1042,10 @@ osendmsg(td, uap) int error; error = copyin(uap->msg, &msg, sizeof (struct omsghdr)); - if (error) + if (error != 0) return (error); error = copyiniov(msg.msg_iov, msg.msg_iovlen, &iov, EMSGSIZE); - if (error) + if (error != 0) return (error); msg.msg_iov = iov; msg.msg_flags = MSG_COMPAT; @@ -1064,10 +1069,10 @@ sys_sendmsg(td, uap) int error; error = copyin(uap->msg, &msg, sizeof (msg)); - if (error) + if (error != 0) return (error); error = copyiniov(msg.msg_iov, msg.msg_iovlen, &iov, EMSGSIZE); - if (error) + if (error != 0) return (error); msg.msg_iov = iov; #ifdef COMPAT_OLDSOCK @@ -1088,30 +1093,31 @@ kern_recvit(td, s, mp, fromseg, controlp) { struct uio auio; struct iovec *iov; - int i; - ssize_t len; - int error; struct mbuf *m, *control = NULL; caddr_t ctlbuf; struct file *fp; struct socket *so; struct sockaddr *fromsa = NULL; + cap_rights_t rights; #ifdef KTRACE struct uio *ktruio = NULL; #endif + ssize_t len; + int error, i; if (controlp != NULL) *controlp = NULL; AUDIT_ARG_FD(s); - error = getsock_cap(td->td_proc->p_fd, s, CAP_RECV, &fp, NULL); - if (error) + error = getsock_cap(td->td_proc->p_fd, s, + cap_rights_init(&rights, CAP_RECV), &fp, NULL); + if (error != 0) return (error); so = fp->f_data; #ifdef MAC error = mac_socket_check_receive(td->td_ucred, so); - if (error) { + if (error != 0) { fdrop(fp, td); return (error); } @@ -1139,7 +1145,7 @@ kern_recvit(td, s, mp, fromseg, controlp) error = soreceive(so, &fromsa, &auio, NULL, (mp->msg_control || controlp) ? &control : NULL, &mp->msg_flags); - if (error) { + if (error != 0) { if (auio.uio_resid != len && (error == ERESTART || error == EINTR || error == EWOULDBLOCK)) error = 0; @@ -1152,7 +1158,7 @@ kern_recvit(td, s, mp, fromseg, controlp) ktrgenio(s, UIO_READ, ktruio, error); } #endif - if (error) + if (error != 0) goto out; td->td_retval[0] = len - auio.uio_resid; if (mp->msg_name) { @@ -1170,7 +1176,7 @@ kern_recvit(td, s, mp, fromseg, controlp) if (fromseg == UIO_USERSPACE) { error = copyout(fromsa, mp->msg_name, (unsigned)len); - if (error) + if (error != 0) goto out; } else bcopy(fromsa, mp->msg_name, len); @@ -1229,8 +1235,7 @@ out: if (fromsa && KTRPOINT(td, KTR_STRUCT)) ktrsockaddr(fromsa); #endif - if (fromsa) - free(fromsa, M_SONAME); + free(fromsa, M_SONAME); if (error == 0 && controlp != NULL) *controlp = control; @@ -1250,9 +1255,9 @@ recvit(td, s, mp, namelenp) int error; error = kern_recvit(td, s, mp, UIO_USERSPACE, NULL); - if (error) + if (error != 0) return (error); - if (namelenp) { + if (namelenp != NULL) { error = copyout(&mp->msg_namelen, namelenp, sizeof (socklen_t)); #ifdef COMPAT_OLDSOCK if (mp->msg_flags & MSG_COMPAT) @@ -1281,7 +1286,7 @@ sys_recvfrom(td, uap) if (uap->fromlenaddr) { error = copyin(uap->fromlenaddr, &msg.msg_namelen, sizeof (msg.msg_namelen)); - if (error) + if (error != 0) goto done2; } else { msg.msg_namelen = 0; @@ -1295,7 +1300,7 @@ sys_recvfrom(td, uap) msg.msg_flags = uap->flags; error = recvit(td, uap->s, &msg, uap->fromlenaddr); done2: - return(error); + return (error); } #ifdef COMPAT_OLDSOCK @@ -1323,7 +1328,6 @@ orecv(td, uap) { struct msghdr msg; struct iovec aiov; - int error; msg.msg_name = 0; msg.msg_namelen = 0; @@ -1333,8 +1337,7 @@ orecv(td, uap) aiov.iov_len = uap->len; msg.msg_control = 0; msg.msg_flags = uap->flags; - error = recvit(td, uap->s, &msg, NULL); - return (error); + return (recvit(td, uap->s, &msg, NULL)); } /* @@ -1356,10 +1359,10 @@ orecvmsg(td, uap) int error; error = copyin(uap->msg, &msg, sizeof (struct omsghdr)); - if (error) + if (error != 0) return (error); error = copyiniov(msg.msg_iov, msg.msg_iovlen, &iov, EMSGSIZE); - if (error) + if (error != 0) return (error); msg.msg_flags = uap->flags | MSG_COMPAT; msg.msg_iov = iov; @@ -1386,10 +1389,10 @@ sys_recvmsg(td, uap) int error; error = copyin(uap->msg, &msg, sizeof (msg)); - if (error) + if (error != 0) return (error); error = copyiniov(msg.msg_iov, msg.msg_iovlen, &iov, EMSGSIZE); - if (error) + if (error != 0) return (error); msg.msg_flags = uap->flags; #ifdef COMPAT_OLDSOCK @@ -1417,11 +1420,12 @@ sys_shutdown(td, uap) { struct socket *so; struct file *fp; + cap_rights_t rights; int error; AUDIT_ARG_FD(uap->s); - error = getsock_cap(td->td_proc->p_fd, uap->s, CAP_SHUTDOWN, &fp, - NULL); + error = getsock_cap(td->td_proc->p_fd, uap->s, + cap_rights_init(&rights, CAP_SHUTDOWN), &fp, NULL); if (error == 0) { so = fp->f_data; error = soshutdown(so, uap->how); @@ -1457,10 +1461,11 @@ kern_setsockopt(td, s, level, name, val, valseg, valsize) enum uio_seg valseg; socklen_t valsize; { - int error; struct socket *so; struct file *fp; struct sockopt sopt; + cap_rights_t rights; + int error; if (val == NULL && valsize != 0) return (EFAULT); @@ -1484,7 +1489,8 @@ kern_setsockopt(td, s, level, name, val, valseg, valsize) } AUDIT_ARG_FD(s); - error = getsock_cap(td->td_proc->p_fd, s, CAP_SETSOCKOPT, &fp, NULL); + error = getsock_cap(td->td_proc->p_fd, s, + cap_rights_init(&rights, CAP_SETSOCKOPT), &fp, NULL); if (error == 0) { so = fp->f_data; error = sosetopt(so, &sopt); @@ -1506,11 +1512,11 @@ sys_getsockopt(td, uap) } */ *uap; { socklen_t valsize; - int error; + int error; if (uap->val) { error = copyin(uap->avalsize, &valsize, sizeof (valsize)); - if (error) + if (error != 0) return (error); } @@ -1536,10 +1542,11 @@ kern_getsockopt(td, s, level, name, val, valseg, valsize) enum uio_seg valseg; socklen_t *valsize; { - int error; - struct socket *so; + struct socket *so; struct file *fp; - struct sockopt sopt; + struct sockopt sopt; + cap_rights_t rights; + int error; if (val == NULL) *valsize = 0; @@ -1563,7 +1570,8 @@ kern_getsockopt(td, s, level, name, val, valseg, valsize) } AUDIT_ARG_FD(s); - error = getsock_cap(td->td_proc->p_fd, s, CAP_GETSOCKOPT, &fp, NULL); + error = getsock_cap(td->td_proc->p_fd, s, + cap_rights_init(&rights, CAP_GETSOCKOPT), &fp, NULL); if (error == 0) { so = fp->f_data; error = sogetopt(so, &sopt); @@ -1592,11 +1600,11 @@ getsockname1(td, uap, compat) int error; error = copyin(uap->alen, &len, sizeof(len)); - if (error) + if (error != 0) return (error); error = kern_getsockname(td, uap->fdes, &sa, &len); - if (error) + if (error != 0) return (error); if (len != 0) { @@ -1618,19 +1626,21 @@ kern_getsockname(struct thread *td, int fd, struct sockaddr **sa, { struct socket *so; struct file *fp; + cap_rights_t rights; socklen_t len; int error; AUDIT_ARG_FD(fd); - error = getsock_cap(td->td_proc->p_fd, fd, CAP_GETSOCKNAME, &fp, NULL); - if (error) + error = getsock_cap(td->td_proc->p_fd, fd, + cap_rights_init(&rights, CAP_GETSOCKNAME), &fp, NULL); + if (error != 0) return (error); so = fp->f_data; *sa = NULL; CURVNET_SET(so->so_vnet); error = (*so->so_proto->pr_usrreqs->pru_sockaddr)(so, sa); CURVNET_RESTORE(); - if (error) + if (error != 0) goto bad; if (*sa == NULL) len = 0; @@ -1643,7 +1653,7 @@ kern_getsockname(struct thread *td, int fd, struct sockaddr **sa, #endif bad: fdrop(fp, td); - if (error && *sa) { + if (error != 0 && *sa != NULL) { free(*sa, M_SONAME); *sa = NULL; } @@ -1689,11 +1699,11 @@ getpeername1(td, uap, compat) int error; error = copyin(uap->alen, &len, sizeof (len)); - if (error) + if (error != 0) return (error); error = kern_getpeername(td, uap->fdes, &sa, &len); - if (error) + if (error != 0) return (error); if (len != 0) { @@ -1715,12 +1725,14 @@ kern_getpeername(struct thread *td, int fd, struct sockaddr **sa, { struct socket *so; struct file *fp; + cap_rights_t rights; socklen_t len; int error; AUDIT_ARG_FD(fd); - error = getsock_cap(td->td_proc->p_fd, fd, CAP_GETPEERNAME, &fp, NULL); - if (error) + error = getsock_cap(td->td_proc->p_fd, fd, + cap_rights_init(&rights, CAP_GETPEERNAME), &fp, NULL); + if (error != 0) return (error); so = fp->f_data; if ((so->so_state & (SS_ISCONNECTED|SS_ISCONFIRMING)) == 0) { @@ -1731,7 +1743,7 @@ kern_getpeername(struct thread *td, int fd, struct sockaddr **sa, CURVNET_SET(so->so_vnet); error = (*so->so_proto->pr_usrreqs->pru_peeraddr)(so, sa); CURVNET_RESTORE(); - if (error) + if (error != 0) goto bad; if (*sa == NULL) len = 0; @@ -1743,7 +1755,7 @@ kern_getpeername(struct thread *td, int fd, struct sockaddr **sa, ktrsockaddr(*sa); #endif bad: - if (error && *sa) { + if (error != 0 && *sa != NULL) { free(*sa, M_SONAME); *sa = NULL; } @@ -1795,7 +1807,7 @@ sockargs(mp, buf, buflen, type) m = m_get2(buflen, M_WAITOK, type, 0); m->m_len = buflen; error = copyin(buf, mtod(m, caddr_t), (u_int)buflen); - if (error) + if (error != 0) (void) m_free(m); else { *mp = m; @@ -1827,7 +1839,7 @@ getsockaddr(namp, uaddr, len) return (EINVAL); sa = malloc(len, M_SONAME, M_WAITOK); error = copyin(uaddr, sa, len); - if (error) { + if (error != 0) { free(sa, M_SONAME); } else { #if defined(COMPAT_OLDSOCK) && BYTE_ORDER != BIG_ENDIAN @@ -1840,8 +1852,6 @@ getsockaddr(namp, uaddr, len) return (error); } -#include - struct sendfile_sync { struct mtx mtx; struct cv cv; @@ -1851,8 +1861,8 @@ struct sendfile_sync { /* * Detach mapped page and release resources back to the system. */ -void -sf_buf_mext(void *addr, void *args) +int +sf_buf_mext(struct mbuf *mb, void *addr, void *args) { vm_page_t m; struct sendfile_sync *sfs; @@ -1870,13 +1880,14 @@ sf_buf_mext(void *addr, void *args) vm_page_free(m); vm_page_unlock(m); if (addr == NULL) - return; + return (EXT_FREE_OK); sfs = addr; mtx_lock(&sfs->mtx); KASSERT(sfs->count> 0, ("Sendfile sync botchup count == 0")); if (--sfs->count == 0) cv_signal(&sfs->cv); mtx_unlock(&sfs->mtx); + return (EXT_FREE_OK); } /* @@ -1902,33 +1913,54 @@ do_sendfile(struct thread *td, struct sendfile_args *uap, int compat) { struct sf_hdtr hdtr; struct uio *hdr_uio, *trl_uio; + struct file *fp; + cap_rights_t rights; int error; + /* + * File offset must be positive. If it goes beyond EOF + * we send only the header/trailer and no payload data. + */ + if (uap->offset < 0) + return (EINVAL); + hdr_uio = trl_uio = NULL; if (uap->hdtr != NULL) { error = copyin(uap->hdtr, &hdtr, sizeof(hdtr)); - if (error) + if (error != 0) goto out; if (hdtr.headers != NULL) { error = copyinuio(hdtr.headers, hdtr.hdr_cnt, &hdr_uio); - if (error) + if (error != 0) goto out; } if (hdtr.trailers != NULL) { error = copyinuio(hdtr.trailers, hdtr.trl_cnt, &trl_uio); - if (error) + if (error != 0) goto out; } } - error = kern_sendfile(td, uap, hdr_uio, trl_uio, compat); + AUDIT_ARG_FD(uap->fd); + + /* + * sendfile(2) can start at any offset within a file so we require + * CAP_READ+CAP_SEEK = CAP_PREAD. + */ + if ((error = fget_read(td, uap->fd, + cap_rights_init(&rights, CAP_PREAD), &fp)) != 0) { + goto out; + } + + error = fo_sendfile(fp, uap->s, hdr_uio, trl_uio, uap->offset, + uap->nbytes, uap->sbytes, uap->flags, compat ? SFK_COMPAT : 0, td); + fdrop(fp, td); + out: - if (hdr_uio) - free(hdr_uio, M_IOV); - if (trl_uio) - free(trl_uio, M_IOV); + free(hdr_uio, M_IOV); + free(trl_uio, M_IOV); return (error); } @@ -1950,103 +1982,251 @@ freebsd4_sendfile(struct thread *td, struct freebsd4_sendfile_args *uap) } #endif /* COMPAT_FREEBSD4 */ -int -kern_sendfile(struct thread *td, struct sendfile_args *uap, - struct uio *hdr_uio, struct uio *trl_uio, int compat) +static int +sendfile_readpage(vm_object_t obj, struct vnode *vp, int nd, + off_t off, int xfsize, int bsize, struct thread *td, vm_page_t *res) +{ + vm_page_t m; + vm_pindex_t pindex; + ssize_t resid; + int error, readahead, rv; + + pindex = OFF_TO_IDX(off); + VM_OBJECT_WLOCK(obj); + m = vm_page_grab(obj, pindex, (vp != NULL ? VM_ALLOC_NOBUSY | + VM_ALLOC_IGN_SBUSY : 0) | VM_ALLOC_WIRED | VM_ALLOC_NORMAL); + + /* + * Check if page is valid for what we need, otherwise initiate I/O. + * + * The non-zero nd argument prevents disk I/O, instead we + * return the caller what he specified in nd. In particular, + * if we already turned some pages into mbufs, nd == EAGAIN + * and the main function send them the pages before we come + * here again and block. + */ + if (m->valid != 0 && vm_page_is_valid(m, off & PAGE_MASK, xfsize)) { + if (vp == NULL) + vm_page_xunbusy(m); + VM_OBJECT_WUNLOCK(obj); + *res = m; + return (0); + } else if (nd != 0) { + if (vp == NULL) + vm_page_xunbusy(m); + error = nd; + goto free_page; + } + + /* + * Get the page from backing store. + */ + error = 0; + if (vp != NULL) { + VM_OBJECT_WUNLOCK(obj); + readahead = sfreadahead * MAXBSIZE; + + /* + * Use vn_rdwr() instead of the pager interface for + * the vnode, to allow the read-ahead. + * + * XXXMAC: Because we don't have fp->f_cred here, we + * pass in NOCRED. This is probably wrong, but is + * consistent with our original implementation. + */ + error = vn_rdwr(UIO_READ, vp, NULL, readahead, trunc_page(off), + UIO_NOCOPY, IO_NODELOCKED | IO_VMIO | ((readahead / + bsize) << IO_SEQSHIFT), td->td_ucred, NOCRED, &resid, td); + SFSTAT_INC(sf_iocnt); + VM_OBJECT_WLOCK(obj); + } else { + if (vm_pager_has_page(obj, pindex, NULL, NULL)) { + rv = vm_pager_get_pages(obj, &m, 1, 0); + SFSTAT_INC(sf_iocnt); + m = vm_page_lookup(obj, pindex); + if (m == NULL) + error = EIO; + else if (rv != VM_PAGER_OK) { + vm_page_lock(m); + vm_page_free(m); + vm_page_unlock(m); + m = NULL; + error = EIO; + } + } else { + pmap_zero_page(m); + m->valid = VM_PAGE_BITS_ALL; + m->dirty = 0; + } + if (m != NULL) + vm_page_xunbusy(m); + } + if (error == 0) { + *res = m; + } else if (m != NULL) { +free_page: + vm_page_lock(m); + vm_page_unwire(m, 0); + + /* + * See if anyone else might know about this page. If + * not and it is not valid, then free it. + */ + if (m->wire_count == 0 && m->valid == 0 && !vm_page_busied(m)) + vm_page_free(m); + vm_page_unlock(m); + } + KASSERT(error != 0 || (m->wire_count > 0 && + vm_page_is_valid(m, off & PAGE_MASK, xfsize)), + ("wrong page state m %p", m)); + VM_OBJECT_WUNLOCK(obj); + return (error); +} + +static int +sendfile_getobj(struct thread *td, struct file *fp, vm_object_t *obj_res, + struct vnode **vp_res, struct shmfd **shmfd_res, off_t *obj_size, + int *bsize) { - struct file *sock_fp; - struct vnode *vp; - struct vm_object *obj = NULL; - struct socket *so = NULL; - struct mbuf *m = NULL; - struct sf_buf *sf; - struct vm_page *pg; struct vattr va; - off_t off, xfsize, fsbytes = 0, sbytes = 0, rem = 0; - int error, hdrlen = 0, mnw = 0; - int bsize; - struct sendfile_sync *sfs = NULL; + vm_object_t obj; + struct vnode *vp; + struct shmfd *shmfd; + int error; + + vp = *vp_res = NULL; + obj = NULL; + shmfd = *shmfd_res = NULL; + *bsize = 0; /* * The file descriptor must be a regular file and have a * backing VM object. - * File offset must be positive. If it goes beyond EOF - * we send only the header/trailer and no payload data. */ - AUDIT_ARG_FD(uap->fd); - /* - * sendfile(2) can start at any offset within a file so we require - * CAP_READ+CAP_SEEK = CAP_PREAD. - */ - if ((error = fgetvp_read(td, uap->fd, CAP_PREAD, &vp)) != 0) - goto out; - vn_lock(vp, LK_SHARED | LK_RETRY); - if (vp->v_type == VREG) { - bsize = vp->v_mount->mnt_stat.f_iosize; - if (uap->nbytes == 0) { - error = VOP_GETATTR(vp, &va, td->td_ucred); - if (error != 0) { - VOP_UNLOCK(vp, 0); - obj = NULL; - goto out; - } - rem = va.va_size; - } else - rem = uap->nbytes; - obj = vp->v_object; - if (obj != NULL) { - /* - * Temporarily increase the backing VM - * object's reference count so that a forced - * reclamation of its vnode does not - * immediately destroy it. - */ - VM_OBJECT_WLOCK(obj); - if ((obj->flags & OBJ_DEAD) == 0) { - vm_object_reference_locked(obj); - VM_OBJECT_WUNLOCK(obj); - } else { - VM_OBJECT_WUNLOCK(obj); - obj = NULL; - } + if (fp->f_type == DTYPE_VNODE) { + vp = fp->f_vnode; + vn_lock(vp, LK_SHARED | LK_RETRY); + if (vp->v_type != VREG) { + error = EINVAL; + goto out; } - } else - bsize = 0; /* silence gcc */ - VOP_UNLOCK(vp, 0); - if (obj == NULL) { - error = EINVAL; - goto out; - } - if (uap->offset < 0) { + *bsize = vp->v_mount->mnt_stat.f_iosize; + error = VOP_GETATTR(vp, &va, td->td_ucred); + if (error != 0) + goto out; + *obj_size = va.va_size; + obj = vp->v_object; + if (obj == NULL) { + error = EINVAL; + goto out; + } + } else if (fp->f_type == DTYPE_SHM) { + shmfd = fp->f_data; + obj = shmfd->shm_object; + *obj_size = shmfd->shm_size; + } else { error = EINVAL; goto out; } + VM_OBJECT_WLOCK(obj); + if ((obj->flags & OBJ_DEAD) != 0) { + VM_OBJECT_WUNLOCK(obj); + error = EBADF; + goto out; + } + + /* + * Temporarily increase the backing VM object's reference + * count so that a forced reclamation of its vnode does not + * immediately destroy it. + */ + vm_object_reference_locked(obj); + VM_OBJECT_WUNLOCK(obj); + *obj_res = obj; + *vp_res = vp; + *shmfd_res = shmfd; + +out: + if (vp != NULL) + VOP_UNLOCK(vp, 0); + return (error); +} + +static int +kern_sendfile_getsock(struct thread *td, int s, struct file **sock_fp, + struct socket **so) +{ + cap_rights_t rights; + int error; + + *sock_fp = NULL; + *so = NULL; + /* * The socket must be a stream socket and connected. - * Remember if it a blocking or non-blocking socket. */ - if ((error = getsock_cap(td->td_proc->p_fd, uap->s, CAP_SEND, - &sock_fp, NULL)) != 0) + error = getsock_cap(td->td_proc->p_fd, s, cap_rights_init(&rights, + CAP_SEND), sock_fp, NULL); + if (error != 0) + return (error); + *so = (*sock_fp)->f_data; + if ((*so)->so_type != SOCK_STREAM) + return (EINVAL); + if (((*so)->so_state & SS_ISCONNECTED) == 0) + return (ENOTCONN); + return (0); +} + +int +vn_sendfile(struct file *fp, int sockfd, struct uio *hdr_uio, + struct uio *trl_uio, off_t offset, size_t nbytes, off_t *sent, int flags, + int kflags, struct thread *td) +{ + struct file *sock_fp; + struct vnode *vp; + struct vm_object *obj; + struct socket *so; + struct mbuf *m; + struct sf_buf *sf; + struct vm_page *pg; + struct shmfd *shmfd; + struct sendfile_sync *sfs; + struct vattr va; + off_t off, xfsize, fsbytes, sbytes, rem, obj_size; + int error, bsize, nd, hdrlen, mnw; + bool inflight_called; + + pg = NULL; + obj = NULL; + so = NULL; + m = NULL; + sfs = NULL; + fsbytes = sbytes = 0; + hdrlen = mnw = 0; + rem = nbytes; + obj_size = 0; + inflight_called = false; + + error = sendfile_getobj(td, fp, &obj, &vp, &shmfd, &obj_size, &bsize); + if (error != 0) + return (error); + if (rem == 0) + rem = obj_size; + + error = kern_sendfile_getsock(td, sockfd, &sock_fp, &so); + if (error != 0) goto out; - so = sock_fp->f_data; - if (so->so_type != SOCK_STREAM) { - error = EINVAL; - goto out; - } - if ((so->so_state & SS_ISCONNECTED) == 0) { - error = ENOTCONN; - goto out; - } + /* * Do not wait on memory allocations but return ENOMEM for * caller to retry later. * XXX: Experimental. */ - if (uap->flags & SF_MNOWAIT) + if (flags & SF_MNOWAIT) mnw = 1; - if (uap->flags & SF_SYNC) { + if (flags & SF_SYNC) { sfs = malloc(sizeof *sfs, M_TEMP, M_WAITOK | M_ZERO); mtx_init(&sfs->mtx, "sendfile", NULL, MTX_DEF); cv_init(&sfs->cv, "sendfile"); @@ -2054,7 +2234,7 @@ kern_sendfile(struct thread *td, struct sendfile_args *uap, #ifdef MAC error = mac_socket_check_send(td->td_ucred, so); - if (error) + if (error != 0) goto out; #endif @@ -2068,11 +2248,11 @@ kern_sendfile(struct thread *td, struct sendfile_args *uap, * the header. If compat is specified subtract the * header size from nbytes. */ - if (compat) { - if (uap->nbytes > hdr_uio->uio_resid) - uap->nbytes -= hdr_uio->uio_resid; + if (kflags & SFK_COMPAT) { + if (nbytes > hdr_uio->uio_resid) + nbytes -= hdr_uio->uio_resid; else - uap->nbytes = 0; + nbytes = 0; } m = m_uiotombuf(hdr_uio, (mnw ? M_NOWAIT : M_WAITOK), 0, 0, 0); @@ -2103,14 +2283,14 @@ kern_sendfile(struct thread *td, struct sendfile_args *uap, * The outer loop checks the state and available space of the socket * and takes care of the overall progress. */ - for (off = uap->offset; ; ) { + for (off = offset; ; ) { struct mbuf *mtail; int loopbytes; int space; int done; - if ((uap->nbytes != 0 && uap->nbytes == fsbytes) || - (uap->nbytes == 0 && va.va_size == fsbytes)) + if ((nbytes != 0 && nbytes == fsbytes) || + (nbytes == 0 && obj_size == fsbytes)) break; mtail = NULL; @@ -2169,7 +2349,7 @@ retry_space: * been interrupted by a signal. If we've sent anything * then return bytes sent, otherwise return the error. */ - if (error) { + if (error != 0) { SOCKBUF_UNLOCK(&so->so_snd); goto done; } @@ -2184,13 +2364,16 @@ retry_space: */ space -= hdrlen; - error = vn_lock(vp, LK_SHARED); - if (error != 0) - goto done; - error = VOP_GETATTR(vp, &va, td->td_ucred); - if (error != 0 || off >= va.va_size) { - VOP_UNLOCK(vp, 0); - goto done; + if (vp != NULL) { + error = vn_lock(vp, LK_SHARED); + if (error != 0) + goto done; + error = VOP_GETATTR(vp, &va, td->td_ucred); + if (error != 0 || off >= va.va_size) { + VOP_UNLOCK(vp, 0); + goto done; + } + obj_size = va.va_size; } /* @@ -2198,7 +2381,6 @@ retry_space: * dumped into socket buffer. */ while (space > loopbytes) { - vm_pindex_t pindex; vm_offset_t pgoff; struct mbuf *m0; @@ -2208,11 +2390,10 @@ retry_space: * or the passed in nbytes. */ pgoff = (vm_offset_t)(off & PAGE_MASK); - if (uap->nbytes) - rem = (uap->nbytes - fsbytes - loopbytes); - else - rem = va.va_size - - uap->offset - fsbytes - loopbytes; + rem = obj_size - offset; + if (nbytes != 0) + rem = omin(rem, nbytes); + rem -= fsbytes + loopbytes; xfsize = omin(PAGE_SIZE - pgoff, rem); xfsize = omin(space - loopbytes, xfsize); if (xfsize <= 0) { @@ -2224,58 +2405,15 @@ retry_space: * Attempt to look up the page. Allocate * if not found or wait and loop if busy. */ - pindex = OFF_TO_IDX(off); - VM_OBJECT_WLOCK(obj); - pg = vm_page_grab(obj, pindex, VM_ALLOC_NOBUSY | - VM_ALLOC_NORMAL | VM_ALLOC_WIRED | VM_ALLOC_RETRY); - - /* - * Check if page is valid for what we need, - * otherwise initiate I/O. - * If we already turned some pages into mbufs, - * send them off before we come here again and - * block. - */ - if (pg->valid && vm_page_is_valid(pg, pgoff, xfsize)) - VM_OBJECT_WUNLOCK(obj); - else if (m != NULL) - error = EAGAIN; /* send what we already got */ - else if (uap->flags & SF_NODISKIO) - error = EBUSY; - else { - ssize_t resid; - int readahead = sfreadahead * MAXBSIZE; - - VM_OBJECT_WUNLOCK(obj); - - /* - * Get the page from backing store. - * XXXMAC: Because we don't have fp->f_cred - * here, we pass in NOCRED. This is probably - * wrong, but is consistent with our original - * implementation. - */ - error = vn_rdwr(UIO_READ, vp, NULL, readahead, - trunc_page(off), UIO_NOCOPY, IO_NODELOCKED | - IO_VMIO | ((readahead / bsize) << IO_SEQSHIFT), - td->td_ucred, NOCRED, &resid, td); - SFSTAT_INC(sf_iocnt); - if (error) - VM_OBJECT_WLOCK(obj); - } - if (error) { - vm_page_lock(pg); - vm_page_unwire(pg, 0); - /* - * See if anyone else might know about - * this page. If not and it is not valid, - * then free it. - */ - if (pg->wire_count == 0 && pg->valid == 0 && - pg->busy == 0 && !(pg->oflags & VPO_BUSY)) - vm_page_free(pg); - vm_page_unlock(pg); - VM_OBJECT_WUNLOCK(obj); + if (m != NULL) + nd = EAGAIN; /* send what we already got */ + else if ((flags & SF_NODISKIO) != 0) + nd = EBUSY; + else + nd = 0; + error = sendfile_readpage(obj, vp, nd, off, + xfsize, bsize, td, &pg); + if (error != 0) { if (error == EAGAIN) error = 0; /* not a real error */ break; @@ -2297,7 +2435,7 @@ retry_space: vm_page_lock(pg); vm_page_unwire(pg, 0); KASSERT(pg->object != NULL, - ("kern_sendfile: object disappeared")); + ("%s: object disappeared", __func__)); vm_page_unlock(pg); if (m == NULL) error = (mnw ? EAGAIN : EINTR); @@ -2311,14 +2449,14 @@ retry_space: m0 = m_get((mnw ? M_NOWAIT : M_WAITOK), MT_DATA); if (m0 == NULL) { error = (mnw ? EAGAIN : ENOBUFS); - sf_buf_mext(NULL, sf); + (void)sf_buf_mext(NULL, NULL, sf); break; } if (m_extadd(m0, (caddr_t )sf_buf_kva(sf), PAGE_SIZE, sf_buf_mext, sfs, sf, M_RDONLY, EXT_SFBUF, (mnw ? M_NOWAIT : M_WAITOK)) != 0) { error = (mnw ? EAGAIN : ENOBUFS); - sf_buf_mext(NULL, sf); + (void)sf_buf_mext(NULL, NULL, sf); m_freem(m0); break; } @@ -2345,7 +2483,8 @@ retry_space: } } - VOP_UNLOCK(vp, 0); + if (vp != NULL) + VOP_UNLOCK(vp, 0); /* Add the buffer chain to the socket buffer. */ if (m != NULL) { @@ -2388,7 +2527,7 @@ retry_space: /* Quit outer loop on error or when we're done. */ if (done) break; - if (error) + if (error != 0) goto done; } @@ -2397,7 +2536,7 @@ retry_space: */ if (trl_uio != NULL) { sbunlock(&so->so_snd); - error = kern_writev(td, uap->s, trl_uio); + error = kern_writev(td, sockfd, trl_uio); if (error == 0) sbytes += td->td_retval[0]; goto out; @@ -2413,13 +2552,11 @@ out: if (error == 0) { td->td_retval[0] = 0; } - if (uap->sbytes != NULL) { - copyout(&sbytes, uap->sbytes, sizeof(off_t)); + if (sent != NULL) { + copyout(&sbytes, sent, sizeof(off_t)); } if (obj != NULL) vm_object_deallocate(obj); - if (vp != NULL) - vrele(vp); if (so) fdrop(sock_fp, td); if (m) @@ -2457,21 +2594,22 @@ sys_sctp_peeloff(td, uap) { #if (defined(INET) || defined(INET6)) && defined(SCTP) struct file *nfp = NULL; - int error; struct socket *head, *so; - int fd; + cap_rights_t rights; u_int fflag; + int error, fd; AUDIT_ARG_FD(uap->sd); - error = fgetsock(td, uap->sd, CAP_PEELOFF, &head, &fflag); - if (error) + error = fgetsock(td, uap->sd, cap_rights_init(&rights, CAP_PEELOFF), + &head, &fflag); + if (error != 0) goto done2; if (head->so_proto->pr_protocol != IPPROTO_SCTP) { error = EOPNOTSUPP; goto done; } error = sctp_can_peel_off(head, (sctp_assoc_t)uap->name); - if (error) + if (error != 0) goto done; /* * At this point we know we do have a assoc to pull @@ -2480,7 +2618,7 @@ sys_sctp_peeloff(td, uap) */ error = falloc(td, &nfp, &fd, 0); - if (error) + if (error != 0) goto done; td->td_retval[0] = fd; @@ -2510,7 +2648,7 @@ sys_sctp_peeloff(td, uap) ACCEPT_UNLOCK(); finit(nfp, fflag, DTYPE_SOCKET, so, &socketops); error = sctp_do_peeloff(head, so, (sctp_assoc_t)uap->name); - if (error) + if (error != 0) goto noconnection; if (head->so_sigio != NULL) fsetown(fgetown(&head->so_sigio), &so->so_sigio); @@ -2520,7 +2658,7 @@ noconnection: * close the new descriptor, assuming someone hasn't ripped it * out from under us. */ - if (error) + if (error != 0) fdclose(td->td_proc->p_fd, nfp, fd, td); /* @@ -2555,7 +2693,6 @@ sys_sctp_generic_sendmsg (td, uap) struct sctp_sndrcvinfo sinfo, *u_sinfo = NULL; struct socket *so; struct file *fp = NULL; - int error = 0, len; struct sockaddr *to = NULL; #ifdef KTRACE struct uio *ktruio = NULL; @@ -2563,27 +2700,28 @@ sys_sctp_generic_sendmsg (td, uap) struct uio auio; struct iovec iov[1]; cap_rights_t rights; + int error = 0, len; - if (uap->sinfo) { + if (uap->sinfo != NULL) { error = copyin(uap->sinfo, &sinfo, sizeof (sinfo)); - if (error) + if (error != 0) return (error); u_sinfo = &sinfo; } - rights = CAP_SEND; - if (uap->tolen) { + cap_rights_init(&rights, CAP_SEND); + if (uap->tolen != 0) { error = getsockaddr(&to, uap->to, uap->tolen); - if (error) { + if (error != 0) { to = NULL; goto sctp_bad2; } - rights |= CAP_CONNECT; + cap_rights_set(&rights, CAP_CONNECT); } AUDIT_ARG_FD(uap->sd); - error = getsock_cap(td->td_proc->p_fd, uap->sd, rights, &fp, NULL); - if (error) + error = getsock_cap(td->td_proc->p_fd, uap->sd, &rights, &fp, NULL); + if (error != 0) goto sctp_bad; #ifdef KTRACE if (to && (KTRPOINT(td, KTR_STRUCT))) @@ -2600,7 +2738,7 @@ sys_sctp_generic_sendmsg (td, uap) } #ifdef MAC error = mac_socket_check_send(td->td_ucred, so); - if (error) + if (error != 0) goto sctp_bad; #endif /* MAC */ @@ -2613,11 +2751,10 @@ sys_sctp_generic_sendmsg (td, uap) auio.uio_resid = 0; len = auio.uio_resid = uap->mlen; CURVNET_SET(so->so_vnet); - error = sctp_lower_sosend(so, to, &auio, - (struct mbuf *)NULL, (struct mbuf *)NULL, - uap->flags, u_sinfo, td); + error = sctp_lower_sosend(so, to, &auio, (struct mbuf *)NULL, + (struct mbuf *)NULL, uap->flags, u_sinfo, td); CURVNET_RESTORE(); - if (error) { + if (error != 0) { if (auio.uio_resid != len && (error == ERESTART || error == EINTR || error == EWOULDBLOCK)) error = 0; @@ -2638,11 +2775,10 @@ sys_sctp_generic_sendmsg (td, uap) } #endif /* KTRACE */ sctp_bad: - if (fp) + if (fp != NULL) fdrop(fp, td); sctp_bad2: - if (to) - free(to, M_SONAME); + free(to, M_SONAME); return (error); #else /* SCTP */ return (EOPNOTSUPP); @@ -2666,8 +2802,6 @@ sys_sctp_generic_sendmsg_iov(td, uap) struct sctp_sndrcvinfo sinfo, *u_sinfo = NULL; struct socket *so; struct file *fp = NULL; - int error=0, i; - ssize_t len; struct sockaddr *to = NULL; #ifdef KTRACE struct uio *ktruio = NULL; @@ -2675,26 +2809,28 @@ sys_sctp_generic_sendmsg_iov(td, uap) struct uio auio; struct iovec *iov, *tiov; cap_rights_t rights; + ssize_t len; + int error, i; - if (uap->sinfo) { + if (uap->sinfo != NULL) { error = copyin(uap->sinfo, &sinfo, sizeof (sinfo)); - if (error) + if (error != 0) return (error); u_sinfo = &sinfo; } - rights = CAP_SEND; - if (uap->tolen) { + cap_rights_init(&rights, CAP_SEND); + if (uap->tolen != 0) { error = getsockaddr(&to, uap->to, uap->tolen); - if (error) { + if (error != 0) { to = NULL; goto sctp_bad2; } - rights |= CAP_CONNECT; + cap_rights_set(&rights, CAP_CONNECT); } AUDIT_ARG_FD(uap->sd); - error = getsock_cap(td->td_proc->p_fd, uap->sd, rights, &fp, NULL); - if (error) + error = getsock_cap(td->td_proc->p_fd, uap->sd, &rights, &fp, NULL); + if (error != 0) goto sctp_bad1; #ifdef COMPAT_FREEBSD32 @@ -2704,7 +2840,7 @@ sys_sctp_generic_sendmsg_iov(td, uap) else #endif error = copyiniov(uap->iov, uap->iovlen, &iov, EMSGSIZE); - if (error) + if (error != 0) goto sctp_bad1; #ifdef KTRACE if (to && (KTRPOINT(td, KTR_STRUCT))) @@ -2718,7 +2854,7 @@ sys_sctp_generic_sendmsg_iov(td, uap) } #ifdef MAC error = mac_socket_check_send(td->td_ucred, so); - if (error) + if (error != 0) goto sctp_bad; #endif /* MAC */ @@ -2742,7 +2878,7 @@ sys_sctp_generic_sendmsg_iov(td, uap) (struct mbuf *)NULL, (struct mbuf *)NULL, uap->flags, u_sinfo, td); CURVNET_RESTORE(); - if (error) { + if (error != 0) { if (auio.uio_resid != len && (error == ERESTART || error == EINTR || error == EWOULDBLOCK)) error = 0; @@ -2765,11 +2901,10 @@ sys_sctp_generic_sendmsg_iov(td, uap) sctp_bad: free(iov, M_IOV); sctp_bad1: - if (fp) + if (fp != NULL) fdrop(fp, td); sctp_bad2: - if (to) - free(to, M_SONAME); + free(to, M_SONAME); return (error); #else /* SCTP */ return (EOPNOTSUPP); @@ -2797,19 +2932,18 @@ sys_sctp_generic_recvmsg(td, uap) struct socket *so; struct file *fp = NULL; struct sockaddr *fromsa; - int fromlen; - ssize_t len; - int i, msg_flags; - int error = 0; + cap_rights_t rights; #ifdef KTRACE struct uio *ktruio = NULL; #endif + ssize_t len; + int error, fromlen, i, msg_flags; AUDIT_ARG_FD(uap->sd); - error = getsock_cap(td->td_proc->p_fd, uap->sd, CAP_RECV, &fp, NULL); - if (error) { + error = getsock_cap(td->td_proc->p_fd, uap->sd, + cap_rights_init(&rights, CAP_RECV), &fp, NULL); + if (error != 0) return (error); - } #ifdef COMPAT_FREEBSD32 if (SV_CURPROC_FLAG(SV_ILP32)) error = freebsd32_copyiniov((struct iovec32 *)uap->iov, @@ -2817,7 +2951,7 @@ sys_sctp_generic_recvmsg(td, uap) else #endif error = copyiniov(uap->iov, uap->iovlen, &iov, EMSGSIZE); - if (error) + if (error != 0) goto out1; so = fp->f_data; @@ -2827,25 +2961,21 @@ sys_sctp_generic_recvmsg(td, uap) } #ifdef MAC error = mac_socket_check_receive(td->td_ucred, so); - if (error) { + if (error != 0) goto out; - } #endif /* MAC */ - if (uap->fromlenaddr) { - error = copyin(uap->fromlenaddr, - &fromlen, sizeof (fromlen)); - if (error) { + if (uap->fromlenaddr != NULL) { + error = copyin(uap->fromlenaddr, &fromlen, sizeof (fromlen)); + if (error != 0) goto out; - } } else { fromlen = 0; } if (uap->msg_flags) { error = copyin(uap->msg_flags, &msg_flags, sizeof (int)); - if (error) { + if (error != 0) goto out; - } } else { msg_flags = 0; } @@ -2876,7 +3006,7 @@ sys_sctp_generic_recvmsg(td, uap) fromsa, fromlen, &msg_flags, (struct sctp_sndrcvinfo *)&sinfo, 1); CURVNET_RESTORE(); - if (error) { + if (error != 0) { if (auio.uio_resid != len && (error == ERESTART || error == EINTR || error == EWOULDBLOCK)) error = 0; @@ -2890,7 +3020,7 @@ sys_sctp_generic_recvmsg(td, uap) ktrgenio(uap->sd, UIO_READ, ktruio, error); } #endif /* KTRACE */ - if (error) + if (error != 0) goto out; td->td_retval[0] = len - auio.uio_resid; @@ -2901,13 +3031,12 @@ sys_sctp_generic_recvmsg(td, uap) else { len = MIN(len, fromsa->sa_len); error = copyout(fromsa, uap->from, (size_t)len); - if (error) + if (error != 0) goto out; } error = copyout(&len, uap->fromlenaddr, sizeof (socklen_t)); - if (error) { + if (error != 0) goto out; - } } #ifdef KTRACE if (KTRPOINT(td, KTR_STRUCT)) @@ -2915,14 +3044,13 @@ sys_sctp_generic_recvmsg(td, uap) #endif if (uap->msg_flags) { error = copyout(&msg_flags, uap->msg_flags, sizeof (int)); - if (error) { + if (error != 0) goto out; - } } out: free(iov, M_IOV); out1: - if (fp) + if (fp != NULL) fdrop(fp, td); return (error); diff --git a/sys/kern/uipc_usrreq.c b/sys/kern/uipc_usrreq.c index 7a4db04130f..e1eee36e45e 100644 --- a/sys/kern/uipc_usrreq.c +++ b/sys/kern/uipc_usrreq.c @@ -325,6 +325,7 @@ static struct protosw localsw[] = { */ .pr_flags = PR_ADDR|PR_ATOMIC|PR_CONNREQUIRED|PR_WANTRCVD| PR_RIGHTS, + .pr_ctloutput = &uipc_ctloutput, .pr_usrreqs = &uipc_usrreqs_seqpacket, }, }; @@ -464,6 +465,7 @@ uipc_bindat(int fd, struct socket *so, struct sockaddr *nam, struct thread *td) struct unpcb *unp; struct vnode *vp; struct mount *mp; + cap_rights_t rights; char *buf; unp = sotounpcb(so); @@ -502,7 +504,7 @@ uipc_bindat(int fd, struct socket *so, struct sockaddr *nam, struct thread *td) restart: NDINIT_ATRIGHTS(&nd, CREATE, NOFOLLOW | LOCKPARENT | SAVENAME, - UIO_SYSSPACE, buf, fd, CAP_BINDAT, td); + UIO_SYSSPACE, buf, fd, cap_rights_init(&rights, CAP_BINDAT), td); /* SHOULD BE ABLE TO ADOPT EXISTING AND wakeup() ALA FIFO's */ error = namei(&nd); if (error) @@ -1276,10 +1278,11 @@ unp_connectat(int fd, struct socket *so, struct sockaddr *nam, struct vnode *vp; struct socket *so2, *so3; struct unpcb *unp, *unp2, *unp3; - int error, len; struct nameidata nd; char buf[SOCK_MAXADDRLEN]; struct sockaddr *sa; + cap_rights_t rights; + int error, len; UNP_LINK_WLOCK_ASSERT(); @@ -1305,7 +1308,7 @@ unp_connectat(int fd, struct socket *so, struct sockaddr *nam, sa = malloc(sizeof(struct sockaddr_un), M_SONAME, M_WAITOK); NDINIT_ATRIGHTS(&nd, LOOKUP, FOLLOW | LOCKSHARED | LOCKLEAF, - UIO_SYSSPACE, buf, fd, CAP_CONNECTAT, td); + UIO_SYSSPACE, buf, fd, cap_rights_init(&rights, CAP_CONNECTAT), td); error = namei(&nd); if (error) vp = NULL; diff --git a/sys/kern/vfs_acl.c b/sys/kern/vfs_acl.c index cc8f68228a3..362792b46dc 100644 --- a/sys/kern/vfs_acl.c +++ b/sys/kern/vfs_acl.c @@ -399,9 +399,11 @@ int sys___acl_get_fd(struct thread *td, struct __acl_get_fd_args *uap) { struct file *fp; + cap_rights_t rights; int error; - error = getvnode(td->td_proc->p_fd, uap->filedes, CAP_ACL_GET, &fp); + error = getvnode(td->td_proc->p_fd, uap->filedes, + cap_rights_init(&rights, CAP_ACL_GET), &fp); if (error == 0) { error = vacl_get_acl(td, fp->f_vnode, uap->type, uap->aclp); fdrop(fp, td); @@ -416,9 +418,11 @@ int sys___acl_set_fd(struct thread *td, struct __acl_set_fd_args *uap) { struct file *fp; + cap_rights_t rights; int error; - error = getvnode(td->td_proc->p_fd, uap->filedes, CAP_ACL_SET, &fp); + error = getvnode(td->td_proc->p_fd, uap->filedes, + cap_rights_init(&rights, CAP_ACL_SET), &fp); if (error == 0) { error = vacl_set_acl(td, fp->f_vnode, uap->type, uap->aclp); fdrop(fp, td); @@ -469,10 +473,11 @@ int sys___acl_delete_fd(struct thread *td, struct __acl_delete_fd_args *uap) { struct file *fp; + cap_rights_t rights; int error; - error = getvnode(td->td_proc->p_fd, uap->filedes, CAP_ACL_DELETE, - &fp); + error = getvnode(td->td_proc->p_fd, uap->filedes, + cap_rights_init(&rights, CAP_ACL_DELETE), &fp); if (error == 0) { error = vacl_delete(td, fp->f_vnode, uap->type); fdrop(fp, td); @@ -523,10 +528,11 @@ int sys___acl_aclcheck_fd(struct thread *td, struct __acl_aclcheck_fd_args *uap) { struct file *fp; + cap_rights_t rights; int error; - error = getvnode(td->td_proc->p_fd, uap->filedes, CAP_ACL_CHECK, - &fp); + error = getvnode(td->td_proc->p_fd, uap->filedes, + cap_rights_init(&rights, CAP_ACL_CHECK), &fp); if (error == 0) { error = vacl_aclcheck(td, fp->f_vnode, uap->type, uap->aclp); fdrop(fp, td); @@ -540,6 +546,9 @@ acl_alloc(int flags) struct acl *aclp; aclp = malloc(sizeof(*aclp), M_ACL, flags); + if (aclp == NULL) + return (NULL); + aclp->acl_maxcnt = ACL_MAX_ENTRIES; return (aclp); diff --git a/sys/kern/vfs_aio.c b/sys/kern/vfs_aio.c index f732d31c090..7f9f8815193 100644 --- a/sys/kern/vfs_aio.c +++ b/sys/kern/vfs_aio.c @@ -1375,7 +1375,7 @@ aio_qphysio(struct proc *p, struct aiocblist *aiocbe) /* * Bring buffer into kernel space. */ - if (vmapbuf(bp, (csw->d_flags & D_UNMAPPED_IO) == 0) < 0) { + if (vmapbuf(bp, (dev->si_flags & SI_UNMAPPED) == 0) < 0) { error = EFAULT; goto doerror; } @@ -1567,6 +1567,7 @@ aio_aqueue(struct thread *td, struct aiocb *job, struct aioliojob *lj, int type, struct aiocb_ops *ops) { struct proc *p = td->td_proc; + cap_rights_t rights; struct file *fp; struct socket *so; struct aiocblist *aiocbe, *cb; @@ -1647,19 +1648,21 @@ aio_aqueue(struct thread *td, struct aiocb *job, struct aioliojob *lj, fd = aiocbe->uaiocb.aio_fildes; switch (opcode) { case LIO_WRITE: - error = fget_write(td, fd, CAP_PWRITE, &fp); + error = fget_write(td, fd, + cap_rights_init(&rights, CAP_PWRITE), &fp); break; case LIO_READ: - error = fget_read(td, fd, CAP_PREAD, &fp); + error = fget_read(td, fd, + cap_rights_init(&rights, CAP_PREAD), &fp); break; case LIO_SYNC: - error = fget(td, fd, CAP_FSYNC, &fp); + error = fget(td, fd, cap_rights_init(&rights, CAP_FSYNC), &fp); break; case LIO_MLOCK: fp = NULL; break; case LIO_NOP: - error = fget(td, fd, CAP_NONE, &fp); + error = fget(td, fd, cap_rights_init(&rights), &fp); break; default: error = EINVAL; @@ -2047,7 +2050,7 @@ sys_aio_cancel(struct thread *td, struct aio_cancel_args *uap) struct vnode *vp; /* Lookup file object. */ - error = fget(td, uap->fd, 0, &fp); + error = fget(td, uap->fd, NULL, &fp); if (error) return (error); diff --git a/sys/kern/vfs_bio.c b/sys/kern/vfs_bio.c index 205e9b35094..ea8a002dbc4 100644 --- a/sys/kern/vfs_bio.c +++ b/sys/kern/vfs_bio.c @@ -108,7 +108,6 @@ static void vm_hold_load_pages(struct buf *bp, vm_offset_t from, static void vfs_page_set_valid(struct buf *bp, vm_ooffset_t off, vm_page_t m); static void vfs_page_set_validclean(struct buf *bp, vm_ooffset_t off, vm_page_t m); -static void vfs_drain_busy_pages(struct buf *bp); static void vfs_clean_pages_dirty_buf(struct buf *bp); static void vfs_setdirty_locked_object(struct buf *bp); static void vfs_vmio_release(struct buf *bp); @@ -584,7 +583,7 @@ vfs_buf_test_cache(struct buf *bp, vm_page_t m) { - VM_OBJECT_ASSERT_WLOCKED(m->object); + VM_OBJECT_ASSERT_LOCKED(m->object); if (bp->b_flags & B_CACHE) { int base = (foff + off) & PAGE_MASK; if (vm_page_is_valid(m, base, size) == 0) @@ -856,7 +855,7 @@ bufinit(void) bogus_page = vm_page_alloc(NULL, 0, VM_ALLOC_NOOBJ | VM_ALLOC_NORMAL | VM_ALLOC_WIRED); - unmapped_buf = (caddr_t)kmem_alloc_nofault(kernel_map, MAXPHYS); + unmapped_buf = (caddr_t)kva_alloc(MAXPHYS); } #ifdef INVARIANTS @@ -1694,6 +1693,12 @@ brelse(struct buf *bp) KASSERT(presid >= 0, ("brelse: extra page")); VM_OBJECT_WLOCK(obj); + while (vm_page_xbusied(m)) { + vm_page_lock(m); + VM_OBJECT_WUNLOCK(obj); + vm_page_busy_sleep(m, "mbncsh"); + VM_OBJECT_WLOCK(obj); + } if (pmap_page_wired_mappings(m) == 0) vm_page_set_invalid(m, poffset, presid); VM_OBJECT_WUNLOCK(obj); @@ -1852,26 +1857,19 @@ vfs_vmio_release(struct buf *bp) */ vm_page_lock(m); vm_page_unwire(m, 0); + /* - * We don't mess with busy pages, it is - * the responsibility of the process that - * busied the pages to deal with them. + * Might as well free the page if we can and it has + * no valid data. We also free the page if the + * buffer was used for direct I/O */ - if ((m->oflags & VPO_BUSY) == 0 && m->busy == 0 && - m->wire_count == 0) { - /* - * Might as well free the page if we can and it has - * no valid data. We also free the page if the - * buffer was used for direct I/O - */ - if ((bp->b_flags & B_ASYNC) == 0 && !m->valid) { + if ((bp->b_flags & B_ASYNC) == 0 && !m->valid) { + if (m->wire_count == 0 && !vm_page_busied(m)) vm_page_free(m); - } else if (bp->b_flags & B_DIRECT) { - vm_page_try_to_free(m); - } else if (buf_vm_page_count_severe()) { - vm_page_try_to_cache(m); - } - } + } else if (bp->b_flags & B_DIRECT) + vm_page_try_to_free(m); + else if (buf_vm_page_count_severe()) + vm_page_try_to_cache(m); vm_page_unlock(m); } VM_OBJECT_WUNLOCK(bp->b_bufobj->bo_object); @@ -3450,7 +3448,7 @@ allocbuf(struct buf *bp, int size) m = bp->b_pages[i]; KASSERT(m != bogus_page, ("allocbuf: bogus page found")); - while (vm_page_sleep_if_busy(m, TRUE, + while (vm_page_sleep_if_busy(m, "biodep")) continue; @@ -3489,15 +3487,15 @@ allocbuf(struct buf *bp, int size) * here could interfere with paging I/O, no * matter which process we are. * - * We can only test VPO_BUSY here. Blocking on - * m->busy might lead to a deadlock: - * vm_fault->getpages->cluster_read->allocbuf - * Thus, we specify VM_ALLOC_IGN_SBUSY. + * Only exclusive busy can be tested here. + * Blocking on shared busy might lead to + * deadlocks once allocbuf() is called after + * pages are vfs_busy_pages(). */ m = vm_page_grab(obj, OFF_TO_IDX(bp->b_offset) + bp->b_npages, VM_ALLOC_NOBUSY | VM_ALLOC_SYSTEM | VM_ALLOC_WIRED | - VM_ALLOC_RETRY | VM_ALLOC_IGN_SBUSY | + VM_ALLOC_IGN_SBUSY | VM_ALLOC_COUNT(desiredpages - bp->b_npages)); if (m->valid == 0) bp->b_flags &= ~B_CACHE; @@ -3852,7 +3850,7 @@ bufdone_finish(struct buf *bp) vfs_page_set_valid(bp, foff, m); } - vm_page_io_finish(m); + vm_page_sunbusy(m); vm_object_pip_subtract(obj, 1); foff = (foff + PAGE_SIZE) & ~(off_t)PAGE_MASK; iosize -= resid; @@ -3914,7 +3912,7 @@ vfs_unbusy_pages(struct buf *bp) BUF_CHECK_UNMAPPED(bp); } vm_object_pip_subtract(obj, 1); - vm_page_io_finish(m); + vm_page_sunbusy(m); } vm_object_pip_wakeupn(obj, 0); VM_OBJECT_WUNLOCK(obj); @@ -3987,10 +3985,10 @@ vfs_page_set_validclean(struct buf *bp, vm_ooffset_t off, vm_page_t m) } /* - * Ensure that all buffer pages are not busied by VPO_BUSY flag. If - * any page is busy, drain the flag. + * Ensure that all buffer pages are not exclusive busied. If any page is + * exclusive busy, drain it. */ -static void +void vfs_drain_busy_pages(struct buf *bp) { vm_page_t m; @@ -4000,22 +3998,26 @@ vfs_drain_busy_pages(struct buf *bp) last_busied = 0; for (i = 0; i < bp->b_npages; i++) { m = bp->b_pages[i]; - if ((m->oflags & VPO_BUSY) != 0) { + if (vm_page_xbusied(m)) { for (; last_busied < i; last_busied++) - vm_page_busy(bp->b_pages[last_busied]); - while ((m->oflags & VPO_BUSY) != 0) - vm_page_sleep(m, "vbpage"); + vm_page_sbusy(bp->b_pages[last_busied]); + while (vm_page_xbusied(m)) { + vm_page_lock(m); + VM_OBJECT_WUNLOCK(bp->b_bufobj->bo_object); + vm_page_busy_sleep(m, "vbpage"); + VM_OBJECT_WLOCK(bp->b_bufobj->bo_object); + } } } for (i = 0; i < last_busied; i++) - vm_page_wakeup(bp->b_pages[i]); + vm_page_sunbusy(bp->b_pages[i]); } /* * This routine is called before a device strategy routine. * It is used to tell the VM system that paging I/O is in * progress, and treat the pages associated with the buffer - * almost as being VPO_BUSY. Also the object paging_in_progress + * almost as being exclusive busy. Also the object paging_in_progress * flag is handled to make sure that the object doesn't become * inconsistant. * @@ -4048,7 +4050,7 @@ vfs_busy_pages(struct buf *bp, int clear_modify) if ((bp->b_flags & B_CLUSTER) == 0) { vm_object_pip_add(obj, 1); - vm_page_io_start(m); + vm_page_sbusy(m); } /* * When readying a buffer for a read ( i.e @@ -4268,7 +4270,7 @@ vm_hold_free_pages(struct buf *bp, int newbsize) for (index = newnpages; index < bp->b_npages; index++) { p = bp->b_pages[index]; bp->b_pages[index] = NULL; - if (p->busy != 0) + if (vm_page_sbusied(p)) printf("vm_hold_free_pages: blkno: %jd, lblkno: %jd\n", (intmax_t)bp->b_blkno, (intmax_t)bp->b_lblkno); p->wire_count--; diff --git a/sys/kern/vfs_cluster.c b/sys/kern/vfs_cluster.c index b280317ed5c..960108253e7 100644 --- a/sys/kern/vfs_cluster.c +++ b/sys/kern/vfs_cluster.c @@ -315,7 +315,7 @@ cluster_rbuild(struct vnode *vp, u_quad_t filesize, daddr_t lbn, daddr_t bn; off_t off; long tinc, tsize; - int i, inc, j, toff; + int i, inc, j, k, toff; KASSERT(size == vp->v_mount->mnt_stat.f_iosize, ("cluster_rbuild: size %ld != f_iosize %jd\n", @@ -378,7 +378,15 @@ cluster_rbuild(struct vnode *vp, u_quad_t filesize, daddr_t lbn, inc = btodb(size); bo = &vp->v_bufobj; for (bn = blkno, i = 0; i < run; ++i, bn += inc) { - if (i != 0) { + if (i == 0) { + VM_OBJECT_WLOCK(tbp->b_bufobj->bo_object); + vfs_drain_busy_pages(tbp); + vm_object_pip_add(tbp->b_bufobj->bo_object, + tbp->b_npages); + for (k = 0; k < tbp->b_npages; k++) + vm_page_sbusy(tbp->b_pages[k]); + VM_OBJECT_WUNLOCK(tbp->b_bufobj->bo_object); + } else { if ((bp->b_npages * PAGE_SIZE) + round_page(size) > vp->v_mount->mnt_iosize_max) { break; @@ -424,14 +432,23 @@ cluster_rbuild(struct vnode *vp, u_quad_t filesize, daddr_t lbn, if ((tbp->b_pages[j]->valid & vm_page_bits(toff, tinc)) != 0) break; + if (vm_page_xbusied(tbp->b_pages[j])) + break; + vm_object_pip_add(tbp->b_bufobj->bo_object, 1); + vm_page_sbusy(tbp->b_pages[j]); off += tinc; tsize -= tinc; } - VM_OBJECT_WUNLOCK(tbp->b_bufobj->bo_object); if (tsize > 0) { +clean_sbusy: + vm_object_pip_add(tbp->b_bufobj->bo_object, -j); + for (k = 0; k < j; k++) + vm_page_sunbusy(tbp->b_pages[k]); + VM_OBJECT_WUNLOCK(tbp->b_bufobj->bo_object); bqrelse(tbp); break; } + VM_OBJECT_WUNLOCK(tbp->b_bufobj->bo_object); /* * Set a read-ahead mark as appropriate @@ -451,8 +468,8 @@ cluster_rbuild(struct vnode *vp, u_quad_t filesize, daddr_t lbn, if (tbp->b_blkno == tbp->b_lblkno) { tbp->b_blkno = bn; } else if (tbp->b_blkno != bn) { - brelse(tbp); - break; + VM_OBJECT_WLOCK(tbp->b_bufobj->bo_object); + goto clean_sbusy; } } /* @@ -466,10 +483,8 @@ cluster_rbuild(struct vnode *vp, u_quad_t filesize, daddr_t lbn, for (j = 0; j < tbp->b_npages; j += 1) { vm_page_t m; m = tbp->b_pages[j]; - vm_page_io_start(m); - vm_object_pip_add(m->object, 1); if ((bp->b_npages == 0) || - (bp->b_pages[bp->b_npages-1] != m)) { + (bp->b_pages[bp->b_npages-1] != m)) { bp->b_pages[bp->b_npages] = m; bp->b_npages++; } @@ -822,7 +837,9 @@ cluster_wbuild(struct vnode *vp, long size, daddr_t start_lbn, int len, (tbp->b_bcount != tbp->b_bufsize) || (tbp->b_bcount != size) || (len == 1) || - ((bp = getpbuf(&cluster_pbuf_freecnt)) == NULL)) { + ((bp = (vp->v_vflag & VV_MD) != 0 ? + trypbuf(&cluster_pbuf_freecnt) : + getpbuf(&cluster_pbuf_freecnt)) == NULL)) { totalwritten += tbp->b_bufsize; bawrite(tbp); ++start_lbn; @@ -944,10 +961,12 @@ cluster_wbuild(struct vnode *vp, long size, daddr_t start_lbn, int len, vm_page_t m; VM_OBJECT_WLOCK(tbp->b_bufobj->bo_object); - if (i != 0) { /* if not first buffer */ + if (i == 0) { + vfs_drain_busy_pages(tbp); + } else { /* if not first buffer */ for (j = 0; j < tbp->b_npages; j += 1) { m = tbp->b_pages[j]; - if (m->oflags & VPO_BUSY) { + if (vm_page_xbusied(m)) { VM_OBJECT_WUNLOCK( tbp->b_object); bqrelse(tbp); @@ -957,7 +976,7 @@ cluster_wbuild(struct vnode *vp, long size, daddr_t start_lbn, int len, } for (j = 0; j < tbp->b_npages; j += 1) { m = tbp->b_pages[j]; - vm_page_io_start(m); + vm_page_sbusy(m); vm_object_pip_add(m->object, 1); if ((bp->b_npages == 0) || (bp->b_pages[bp->b_npages - 1] != m)) { diff --git a/sys/kern/vfs_extattr.c b/sys/kern/vfs_extattr.c index 700a70c2c5e..bc7b942525e 100644 --- a/sys/kern/vfs_extattr.c +++ b/sys/kern/vfs_extattr.c @@ -216,6 +216,7 @@ sys_extattr_set_fd(td, uap) { struct file *fp; char attrname[EXTATTR_MAXNAMELEN]; + cap_rights_t rights; int error; AUDIT_ARG_FD(uap->fd); @@ -225,7 +226,8 @@ sys_extattr_set_fd(td, uap) return (error); AUDIT_ARG_TEXT(attrname); - error = getvnode(td->td_proc->p_fd, uap->fd, CAP_EXTATTR_SET, &fp); + error = getvnode(td->td_proc->p_fd, uap->fd, + cap_rights_init(&rights, CAP_EXTATTR_SET), &fp); if (error) return (error); @@ -389,6 +391,7 @@ sys_extattr_get_fd(td, uap) { struct file *fp; char attrname[EXTATTR_MAXNAMELEN]; + cap_rights_t rights; int error; AUDIT_ARG_FD(uap->fd); @@ -398,7 +401,8 @@ sys_extattr_get_fd(td, uap) return (error); AUDIT_ARG_TEXT(attrname); - error = getvnode(td->td_proc->p_fd, uap->fd, CAP_EXTATTR_GET, &fp); + error = getvnode(td->td_proc->p_fd, uap->fd, + cap_rights_init(&rights, CAP_EXTATTR_GET), &fp); if (error) return (error); @@ -531,6 +535,7 @@ sys_extattr_delete_fd(td, uap) { struct file *fp; char attrname[EXTATTR_MAXNAMELEN]; + cap_rights_t rights; int error; AUDIT_ARG_FD(uap->fd); @@ -540,8 +545,8 @@ sys_extattr_delete_fd(td, uap) return (error); AUDIT_ARG_TEXT(attrname); - error = getvnode(td->td_proc->p_fd, uap->fd, CAP_EXTATTR_DELETE, - &fp); + error = getvnode(td->td_proc->p_fd, uap->fd, + cap_rights_init(&rights, CAP_EXTATTR_DELETE), &fp); if (error) return (error); @@ -687,11 +692,13 @@ sys_extattr_list_fd(td, uap) } */ *uap; { struct file *fp; + cap_rights_t rights; int error; AUDIT_ARG_FD(uap->fd); AUDIT_ARG_VALUE(uap->attrnamespace); - error = getvnode(td->td_proc->p_fd, uap->fd, CAP_EXTATTR_LIST, &fp); + error = getvnode(td->td_proc->p_fd, uap->fd, + cap_rights_init(&rights, CAP_EXTATTR_LIST), &fp); if (error) return (error); diff --git a/sys/kern/vfs_lookup.c b/sys/kern/vfs_lookup.c index 7fe19085dd4..0be0463e31b 100644 --- a/sys/kern/vfs_lookup.c +++ b/sys/kern/vfs_lookup.c @@ -178,7 +178,7 @@ namei(struct nameidata *ndp) if (ndp->ni_dirfd == AT_FDCWD) { #ifdef KTRACE if (KTRPOINT(td, KTR_CAPFAIL)) - ktrcapfail(CAPFAIL_LOOKUP, 0, 0); + ktrcapfail(CAPFAIL_LOOKUP, NULL, NULL); #endif error = ECAPMODE; } @@ -222,20 +222,26 @@ namei(struct nameidata *ndp) dp = ndp->ni_startdir; error = 0; } else if (ndp->ni_dirfd != AT_FDCWD) { + cap_rights_t rights; + + rights = ndp->ni_rightsneeded; + cap_rights_set(&rights, CAP_LOOKUP); + if (cnp->cn_flags & AUDITVNODE1) AUDIT_ARG_ATFD1(ndp->ni_dirfd); if (cnp->cn_flags & AUDITVNODE2) AUDIT_ARG_ATFD2(ndp->ni_dirfd); error = fgetvp_rights(td, ndp->ni_dirfd, - ndp->ni_rightsneeded | CAP_LOOKUP, - &ndp->ni_filecaps, &dp); + &rights, &ndp->ni_filecaps, &dp); #ifdef CAPABILITIES /* * If file descriptor doesn't have all rights, * all lookups relative to it must also be * strictly relative. */ - if (ndp->ni_filecaps.fc_rights != CAP_ALL || + CAP_ALL(&rights); + if (!cap_rights_contains(&ndp->ni_filecaps.fc_rights, + &rights) || ndp->ni_filecaps.fc_fcntls != CAP_FCNTL_ALL || ndp->ni_filecaps.fc_nioctls != -1) { ndp->ni_strictrelative = 1; @@ -278,7 +284,7 @@ namei(struct nameidata *ndp) if (ndp->ni_strictrelative != 0) { #ifdef KTRACE if (KTRPOINT(curthread, KTR_CAPFAIL)) - ktrcapfail(CAPFAIL_LOOKUP, 0, 0); + ktrcapfail(CAPFAIL_LOOKUP, NULL, NULL); #endif return (ENOTCAPABLE); } @@ -634,7 +640,7 @@ dirloop: if (ndp->ni_strictrelative != 0) { #ifdef KTRACE if (KTRPOINT(curthread, KTR_CAPFAIL)) - ktrcapfail(CAPFAIL_LOOKUP, 0, 0); + ktrcapfail(CAPFAIL_LOOKUP, NULL, NULL); #endif error = ENOTCAPABLE; goto bad; @@ -1059,6 +1065,27 @@ bad: return (error); } +void +NDINIT_ALL(struct nameidata *ndp, u_long op, u_long flags, enum uio_seg segflg, + const char *namep, int dirfd, struct vnode *startdir, cap_rights_t *rightsp, + struct thread *td) +{ + + ndp->ni_cnd.cn_nameiop = op; + ndp->ni_cnd.cn_flags = flags; + ndp->ni_segflg = segflg; + ndp->ni_dirp = namep; + ndp->ni_dirfd = dirfd; + ndp->ni_startdir = startdir; + ndp->ni_strictrelative = 0; + if (rightsp != NULL) + ndp->ni_rightsneeded = *rightsp; + else + cap_rights_init(&ndp->ni_rightsneeded); + filecaps_init(&ndp->ni_filecaps); + ndp->ni_cnd.cn_thread = td; +} + /* * Free data allocated by namei(); see namei(9) for details. */ diff --git a/sys/kern/vfs_mount.c b/sys/kern/vfs_mount.c index 493bb988883..8f92e10b69b 100644 --- a/sys/kern/vfs_mount.c +++ b/sys/kern/vfs_mount.c @@ -1269,8 +1269,16 @@ dounmount(mp, flags, td) } mp->mnt_kern_flag |= MNTK_UNMOUNT | MNTK_NOINSMNTQ; /* Allow filesystems to detect that a forced unmount is in progress. */ - if (flags & MNT_FORCE) + if (flags & MNT_FORCE) { mp->mnt_kern_flag |= MNTK_UNMOUNTF; + MNT_IUNLOCK(mp); + /* + * Must be done after setting MNTK_UNMOUNTF and before + * waiting for mnt_lockref to become 0. + */ + VFS_PURGE(mp); + MNT_ILOCK(mp); + } error = 0; if (mp->mnt_lockref) { mp->mnt_kern_flag |= MNTK_DRAINING; diff --git a/sys/kern/vfs_mountroot.c b/sys/kern/vfs_mountroot.c index e873cf93907..322fc9a00c2 100644 --- a/sys/kern/vfs_mountroot.c +++ b/sys/kern/vfs_mountroot.c @@ -710,7 +710,7 @@ parse_mount(char **conf) errmsg = malloc(ERRMSGL, M_TEMP, M_WAITOK | M_ZERO); if (vfs_byname(fs) == NULL) { - strlcpy(errmsg, "unknown file system", sizeof(errmsg)); + strlcpy(errmsg, "unknown file system", ERRMSGL); error = ENOENT; goto out; } diff --git a/sys/kern/vfs_subr.c b/sys/kern/vfs_subr.c index 6fb49ae7dbe..3cbc95f6593 100644 --- a/sys/kern/vfs_subr.c +++ b/sys/kern/vfs_subr.c @@ -2906,7 +2906,7 @@ vn_printf(struct vnode *vp, const char *fmt, ...) */ DB_SHOW_COMMAND(lockedvnods, lockedvnodes) { - struct mount *mp, *nmp; + struct mount *mp; struct vnode *vp; /* @@ -2916,14 +2916,11 @@ DB_SHOW_COMMAND(lockedvnods, lockedvnodes) * about that. */ db_printf("Locked vnodes\n"); - for (mp = TAILQ_FIRST(&mountlist); mp != NULL; mp = nmp) { - nmp = TAILQ_NEXT(mp, mnt_list); + TAILQ_FOREACH(mp, &mountlist, mnt_list) { TAILQ_FOREACH(vp, &mp->mnt_nvnodelist, v_nmntvnodes) { - if (vp->v_type != VMARKER && - VOP_ISLOCKED(vp)) + if (vp->v_type != VMARKER && VOP_ISLOCKED(vp)) vprint("", vp); } - nmp = TAILQ_NEXT(mp, mnt_list); } } diff --git a/sys/kern/vfs_syscalls.c b/sys/kern/vfs_syscalls.c index a004ea0d51e..4b82df8d7ef 100644 --- a/sys/kern/vfs_syscalls.c +++ b/sys/kern/vfs_syscalls.c @@ -92,12 +92,8 @@ __FBSDID("$FreeBSD$"); MALLOC_DEFINE(M_FADVISE, "fadvise", "posix_fadvise(2) information"); SDT_PROVIDER_DEFINE(vfs); -SDT_PROBE_DEFINE(vfs, , stat, mode, mode); -SDT_PROBE_ARGTYPE(vfs, , stat, mode, 0, "char *"); -SDT_PROBE_ARGTYPE(vfs, , stat, mode, 1, "int"); -SDT_PROBE_DEFINE(vfs, , stat, reg, reg); -SDT_PROBE_ARGTYPE(vfs, , stat, reg, 0, "char *"); -SDT_PROBE_ARGTYPE(vfs, , stat, reg, 1, "int"); +SDT_PROBE_DEFINE2(vfs, , stat, mode, mode, "char *", "int"); +SDT_PROBE_DEFINE2(vfs, , stat, reg, reg, "char *", "int"); static int chroot_refuse_vdir_fds(struct filedesc *fdp); static int getutimes(const struct timeval *, enum uio_seg, struct timespec *); @@ -185,8 +181,8 @@ sys_quotactl(td, uap) } */ *uap; { struct mount *mp; - int error; struct nameidata nd; + int error; AUDIT_ARG_CMD(uap->cmd); AUDIT_ARG_UID(uap->uid); @@ -202,7 +198,7 @@ sys_quotactl(td, uap) vput(nd.ni_vp); error = vfs_busy(mp, 0); vfs_rel(mp); - if (error) + if (error != 0) return (error); error = VFS_QUOTACTL(mp, uap->cmd, uap->uid, uap->arg); @@ -295,13 +291,13 @@ kern_statfs(struct thread *td, char *path, enum uio_seg pathseg, { struct mount *mp; struct statfs *sp, sb; - int error; struct nameidata nd; + int error; NDINIT(&nd, LOOKUP, FOLLOW | LOCKSHARED | LOCKLEAF | AUDITVNODE1, pathseg, path, td); error = namei(&nd); - if (error) + if (error != 0) return (error); mp = nd.ni_vp->v_mount; vfs_ref(mp); @@ -309,11 +305,11 @@ kern_statfs(struct thread *td, char *path, enum uio_seg pathseg, vput(nd.ni_vp); error = vfs_busy(mp, 0); vfs_rel(mp); - if (error) + if (error != 0) return (error); #ifdef MAC error = mac_mount_check_stat(td->td_ucred, mp); - if (error) + if (error != 0) goto out; #endif /* @@ -324,7 +320,7 @@ kern_statfs(struct thread *td, char *path, enum uio_seg pathseg, sp->f_namemax = NAME_MAX; sp->f_flags = mp->mnt_flag & MNT_VISFLAGMASK; error = VFS_STATFS(mp, sp); - if (error) + if (error != 0) goto out; if (priv_check(td, PRIV_VFS_GENERATION)) { bcopy(sp, &sb, sizeof(sb)); @@ -371,11 +367,13 @@ kern_fstatfs(struct thread *td, int fd, struct statfs *buf) struct mount *mp; struct statfs *sp, sb; struct vnode *vp; + cap_rights_t rights; int error; AUDIT_ARG_FD(fd); - error = getvnode(td->td_proc->p_fd, fd, CAP_FSTATFS, &fp); - if (error) + error = getvnode(td->td_proc->p_fd, fd, + cap_rights_init(&rights, CAP_FSTATFS), &fp); + if (error != 0) return (error); vp = fp->f_vnode; vn_lock(vp, LK_SHARED | LK_RETRY); @@ -393,11 +391,11 @@ kern_fstatfs(struct thread *td, int fd, struct statfs *buf) } error = vfs_busy(mp, 0); vfs_rel(mp); - if (error) + if (error != 0) return (error); #ifdef MAC error = mac_mount_check_stat(td->td_ucred, mp); - if (error) + if (error != 0) goto out; #endif /* @@ -408,7 +406,7 @@ kern_fstatfs(struct thread *td, int fd, struct statfs *buf) sp->f_namemax = NAME_MAX; sp->f_flags = mp->mnt_flag & MNT_VISFLAGMASK; error = VFS_STATFS(mp, sp); - if (error) + if (error != 0) goto out; if (priv_check(td, PRIV_VFS_GENERATION)) { bcopy(sp, &sb, sizeof(sb)); @@ -527,7 +525,7 @@ kern_getfsstat(struct thread *td, struct statfs **buf, size_t bufsize, bcopy(sp, sfsp, sizeof(*sp)); else /* if (bufseg == UIO_USERSPACE) */ { error = copyout(sp, sfsp, sizeof(*sp)); - if (error) { + if (error != 0) { vfs_unbusy(mp); return (error); } @@ -572,7 +570,7 @@ freebsd4_statfs(td, uap) int error; error = kern_statfs(td, uap->path, UIO_USERSPACE, &sf); - if (error) + if (error != 0) return (error); cvtstatfs(&sf, &osb); return (copyout(&osb, uap->buf, sizeof(osb))); @@ -600,7 +598,7 @@ freebsd4_fstatfs(td, uap) int error; error = kern_fstatfs(td, uap->fd, &sf); - if (error) + if (error != 0) return (error); cvtstatfs(&sf, &osb); return (copyout(&osb, uap->buf, sizeof(osb))); @@ -671,10 +669,10 @@ freebsd4_fhstatfs(td, uap) int error; error = copyin(uap->u_fhp, &fh, sizeof(fhandle_t)); - if (error) + if (error != 0) return (error); error = kern_fhstatfs(td, fh, &sf); - if (error) + if (error != 0) return (error); cvtstatfs(&sf, &osb); return (copyout(&osb, uap->buf, sizeof(osb))); @@ -734,10 +732,13 @@ sys_fchdir(td, uap) struct vnode *vp, *tdp, *vpold; struct mount *mp; struct file *fp; + cap_rights_t rights; int error; AUDIT_ARG_FD(uap->fd); - if ((error = getvnode(fdp, uap->fd, CAP_FCHDIR, &fp)) != 0) + error = getvnode(fdp, uap->fd, cap_rights_init(&rights, CAP_FCHDIR), + &fp); + if (error != 0) return (error); vp = fp->f_vnode; VREF(vp); @@ -750,12 +751,12 @@ sys_fchdir(td, uap) continue; error = VFS_ROOT(mp, LK_SHARED, &tdp); vfs_unbusy(mp); - if (error) + if (error != 0) break; vput(vp); vp = tdp; } - if (error) { + if (error != 0) { vput(vp); return (error); } @@ -791,9 +792,9 @@ int kern_chdir(struct thread *td, char *path, enum uio_seg pathseg) { register struct filedesc *fdp = td->td_proc->p_fd; - int error; struct nameidata nd; struct vnode *vp; + int error; NDINIT(&nd, LOOKUP, FOLLOW | LOCKSHARED | LOCKLEAF | AUDITVNODE1, pathseg, path, td); @@ -870,21 +871,23 @@ sys_chroot(td, uap) char *path; } */ *uap; { - int error; struct nameidata nd; + int error; error = priv_check(td, PRIV_VFS_CHROOT); - if (error) + if (error != 0) return (error); NDINIT(&nd, LOOKUP, FOLLOW | LOCKSHARED | LOCKLEAF | AUDITVNODE1, UIO_USERSPACE, uap->path, td); error = namei(&nd); - if (error) + if (error != 0) goto error; - if ((error = change_dir(nd.ni_vp, td)) != 0) + error = change_dir(nd.ni_vp, td); + if (error != 0) goto e_vunlock; #ifdef MAC - if ((error = mac_vnode_check_chroot(td->td_ucred, nd.ni_vp))) + error = mac_vnode_check_chroot(td->td_ucred, nd.ni_vp); + if (error != 0) goto e_vunlock; #endif VOP_UNLOCK(nd.ni_vp, 0); @@ -908,18 +911,19 @@ change_dir(vp, td) struct vnode *vp; struct thread *td; { +#ifdef MAC int error; +#endif ASSERT_VOP_LOCKED(vp, "change_dir(): vp not locked"); if (vp->v_type != VDIR) return (ENOTDIR); #ifdef MAC error = mac_vnode_check_chdir(td->td_ucred, vp); - if (error) + if (error != 0) return (error); #endif - error = VOP_ACCESS(vp, VEXEC, td->td_ucred, td); - return (error); + return (VOP_ACCESS(vp, VEXEC, td->td_ucred, td)); } /* @@ -941,7 +945,7 @@ change_root(vp, td) if (chroot_allow_open_directories == 0 || (chroot_allow_open_directories == 1 && fdp->fd_rdir != rootvnode)) { error = chroot_refuse_vdir_fds(fdp); - if (error) { + if (error != 0) { FILEDESC_XUNLOCK(fdp); return (error); } @@ -958,42 +962,39 @@ change_root(vp, td) return (0); } -static __inline cap_rights_t -flags_to_rights(int flags) +static __inline void +flags_to_rights(int flags, cap_rights_t *rightsp) { - cap_rights_t rights = 0; if (flags & O_EXEC) { - rights |= CAP_FEXECVE; + cap_rights_set(rightsp, CAP_FEXECVE); } else { switch ((flags & O_ACCMODE)) { case O_RDONLY: - rights |= CAP_READ; + cap_rights_set(rightsp, CAP_READ); break; case O_RDWR: - rights |= CAP_READ; + cap_rights_set(rightsp, CAP_READ); /* FALLTHROUGH */ case O_WRONLY: - rights |= CAP_WRITE; + cap_rights_set(rightsp, CAP_WRITE); if (!(flags & (O_APPEND | O_TRUNC))) - rights |= CAP_SEEK; + cap_rights_set(rightsp, CAP_SEEK); break; } } if (flags & O_CREAT) - rights |= CAP_CREATE; + cap_rights_set(rightsp, CAP_CREATE); if (flags & O_TRUNC) - rights |= CAP_FTRUNCATE; + cap_rights_set(rightsp, CAP_FTRUNCATE); if (flags & (O_SYNC | O_FSYNC)) - rights |= CAP_FSYNC; + cap_rights_set(rightsp, CAP_FSYNC); if (flags & (O_EXLOCK | O_SHLOCK)) - rights |= CAP_FLOCK; - - return (rights); + cap_rights_set(rightsp, CAP_FLOCK); } /* @@ -1052,15 +1053,17 @@ kern_openat(struct thread *td, int fd, char *path, enum uio_seg pathseg, struct filedesc *fdp = p->p_fd; struct file *fp; struct vnode *vp; - int cmode; - int indx = -1, error; struct nameidata nd; - cap_rights_t rights_needed = CAP_LOOKUP; + cap_rights_t rights; + int cmode, error, indx; + + indx = -1; AUDIT_ARG_FFLAGS(flags); AUDIT_ARG_MODE(mode); /* XXX: audit dirfd */ - rights_needed |= flags_to_rights(flags); + cap_rights_init(&rights, CAP_LOOKUP); + flags_to_rights(flags, &rights); /* * Only one of the O_EXEC, O_RDONLY, O_WRONLY and O_RDWR flags * may be specified. @@ -1078,7 +1081,7 @@ kern_openat(struct thread *td, int fd, char *path, enum uio_seg pathseg, * Allocate the file descriptor, but don't install a descriptor yet. */ error = falloc_noinstall(td, &fp); - if (error) + if (error != 0) return (error); /* * An extra reference on `fp' has been held for us by @@ -1086,12 +1089,12 @@ kern_openat(struct thread *td, int fd, char *path, enum uio_seg pathseg, */ /* Set the flags early so the finit in devfs can pick them up. */ fp->f_flag = flags & FMASK; - cmode = ((mode &~ fdp->fd_cmask) & ALLPERMS) &~ S_ISTXT; + cmode = ((mode & ~fdp->fd_cmask) & ALLPERMS) & ~S_ISTXT; NDINIT_ATRIGHTS(&nd, LOOKUP, FOLLOW | AUDITVNODE1, pathseg, path, fd, - rights_needed, td); + &rights, td); td->td_dupfd = -1; /* XXX check for fdopen */ error = vn_open(&nd, &flags, cmode, fp); - if (error) { + if (error != 0) { /* * If the vn_open replaced the method vector, something * wonderous happened deep below and we just pass it up @@ -1135,14 +1138,14 @@ kern_openat(struct thread *td, int fd, char *path, enum uio_seg pathseg, if (fp->f_ops == &badfileops) { KASSERT(vp->v_type != VFIFO, ("Unexpected fifo.")); fp->f_seqcount = 1; - finit(fp, (flags & FMASK) | (fp->f_flag & FHASLOCK), DTYPE_VNODE, - vp, &vnops); + finit(fp, (flags & FMASK) | (fp->f_flag & FHASLOCK), + DTYPE_VNODE, vp, &vnops); } VOP_UNLOCK(vp, 0); if (flags & O_TRUNC) { error = fo_truncate(fp, 0, td->td_ucred, td); - if (error) + if (error != 0) goto bad; } success: @@ -1259,9 +1262,9 @@ kern_mknodat(struct thread *td, int fd, char *path, enum uio_seg pathseg, struct vnode *vp; struct mount *mp; struct vattr vattr; - int error; - int whiteout = 0; struct nameidata nd; + cap_rights_t rights; + int error, whiteout = 0; AUDIT_ARG_MODE(mode); AUDIT_ARG_DEV(dev); @@ -1284,12 +1287,12 @@ kern_mknodat(struct thread *td, int fd, char *path, enum uio_seg pathseg, error = EINVAL; break; } - if (error) + if (error != 0) return (error); restart: bwillwrite(); NDINIT_ATRIGHTS(&nd, CREATE, LOCKPARENT | SAVENAME | AUDITVNODE1, - pathseg, path, fd, CAP_MKNODAT, td); + pathseg, path, fd, cap_rights_init(&rights, CAP_MKNODAT), td); if ((error = namei(&nd)) != 0) return (error); vp = nd.ni_vp; @@ -1337,7 +1340,7 @@ restart: error = mac_vnode_check_create(td->td_ucred, nd.ni_dvp, &nd.ni_cnd, &vattr); #endif - if (!error) { + if (error == 0) { if (whiteout) error = VOP_WHITEOUT(nd.ni_dvp, &nd.ni_cnd, CREATE); else { @@ -1402,14 +1405,15 @@ kern_mkfifoat(struct thread *td, int fd, char *path, enum uio_seg pathseg, { struct mount *mp; struct vattr vattr; - int error; struct nameidata nd; + cap_rights_t rights; + int error; AUDIT_ARG_MODE(mode); restart: bwillwrite(); NDINIT_ATRIGHTS(&nd, CREATE, LOCKPARENT | SAVENAME | AUDITVNODE1, - pathseg, path, fd, CAP_MKFIFOAT, td); + pathseg, path, fd, cap_rights_init(&rights, CAP_MKFIFOAT), td); if ((error = namei(&nd)) != 0) return (error); if (nd.ni_vp != NULL) { @@ -1434,7 +1438,7 @@ restart: #ifdef MAC error = mac_vnode_check_create(td->td_ucred, nd.ni_dvp, &nd.ni_cnd, &vattr); - if (error) + if (error != 0) goto out; #endif error = VOP_MKNOD(nd.ni_dvp, &nd.ni_vp, &nd.ni_cnd, &vattr); @@ -1518,13 +1522,13 @@ can_hardlink(struct vnode *vp, struct ucred *cred) if (hardlink_check_uid && cred->cr_uid != va.va_uid) { error = priv_check_cred(cred, PRIV_VFS_LINK, 0); - if (error) + if (error != 0) return (error); } if (hardlink_check_gid && !groupmember(va.va_gid, cred)) { error = priv_check_cred(cred, PRIV_VFS_LINK, 0); - if (error) + if (error != 0) return (error); } @@ -1545,6 +1549,7 @@ kern_linkat(struct thread *td, int fd1, int fd2, char *path1, char *path2, struct vnode *vp; struct mount *mp; struct nameidata nd; + cap_rights_t rights; int error; bwillwrite(); @@ -1563,7 +1568,7 @@ kern_linkat(struct thread *td, int fd1, int fd2, char *path1, char *path2, return (error); } NDINIT_ATRIGHTS(&nd, CREATE, LOCKPARENT | SAVENAME | AUDITVNODE2, - segflg, path2, fd2, CAP_LINKAT, td); + segflg, path2, fd2, cap_rights_init(&rights, CAP_LINKAT), td); if ((error = namei(&nd)) == 0) { if (nd.ni_vp != NULL) { if (nd.ni_dvp == nd.ni_vp) @@ -1642,8 +1647,9 @@ kern_symlinkat(struct thread *td, char *path1, int fd, char *path2, struct mount *mp; struct vattr vattr; char *syspath; - int error; struct nameidata nd; + int error; + cap_rights_t rights; if (segflg == UIO_SYSSPACE) { syspath = path1; @@ -1656,7 +1662,7 @@ kern_symlinkat(struct thread *td, char *path1, int fd, char *path2, restart: bwillwrite(); NDINIT_ATRIGHTS(&nd, CREATE, LOCKPARENT | SAVENAME | AUDITVNODE1, - segflg, path2, fd, CAP_SYMLINKAT, td); + segflg, path2, fd, cap_rights_init(&rights, CAP_SYMLINKAT), td); if ((error = namei(&nd)) != 0) goto out; if (nd.ni_vp) { @@ -1682,7 +1688,7 @@ restart: vattr.va_type = VLNK; error = mac_vnode_check_create(td->td_ucred, nd.ni_dvp, &nd.ni_cnd, &vattr); - if (error) + if (error != 0) goto out2; #endif error = VOP_SYMLINK(nd.ni_dvp, &nd.ni_vp, &nd.ni_cnd, &vattr, syspath); @@ -1710,16 +1716,16 @@ sys_undelete(td, uap) char *path; } */ *uap; { - int error; struct mount *mp; struct nameidata nd; + int error; restart: bwillwrite(); NDINIT(&nd, DELETE, LOCKPARENT | DOWHITEOUT | AUDITVNODE1, UIO_USERSPACE, uap->path, td); error = namei(&nd); - if (error) + if (error != 0) return (error); if (nd.ni_vp != NULLVP || !(nd.ni_cnd.cn_flags & ISWHITEOUT)) { @@ -1801,14 +1807,15 @@ kern_unlinkat(struct thread *td, int fd, char *path, enum uio_seg pathseg, { struct mount *mp; struct vnode *vp; - int error; struct nameidata nd; struct stat sb; + cap_rights_t rights; + int error; restart: bwillwrite(); NDINIT_ATRIGHTS(&nd, DELETE, LOCKPARENT | LOCKLEAF | AUDITVNODE1, - pathseg, path, fd, CAP_UNLINKAT, td); + pathseg, path, fd, cap_rights_init(&rights, CAP_UNLINKAT), td); if ((error = namei(&nd)) != 0) return (error == EINVAL ? EPERM : error); vp = nd.ni_vp; @@ -1843,7 +1850,7 @@ restart: #ifdef MAC error = mac_vnode_check_unlink(td->td_ucred, nd.ni_dvp, vp, &nd.ni_cnd); - if (error) + if (error != 0) goto out; #endif vfs_notify_upper(vp, VFS_NOTIFY_UPPER_UNLINK); @@ -1883,77 +1890,17 @@ sys_lseek(td, uap) int whence; } */ *uap; { - struct ucred *cred = td->td_ucred; struct file *fp; - struct vnode *vp; - struct vattr vattr; - off_t foffset, offset, size; - int error, noneg; + cap_rights_t rights; + int error; AUDIT_ARG_FD(uap->fd); - if ((error = fget(td, uap->fd, CAP_SEEK, &fp)) != 0) - return (error); - if (!(fp->f_ops->fo_flags & DFLAG_SEEKABLE)) { - fdrop(fp, td); - return (ESPIPE); - } - vp = fp->f_vnode; - foffset = foffset_lock(fp, 0); - noneg = (vp->v_type != VCHR); - offset = uap->offset; - switch (uap->whence) { - case L_INCR: - if (noneg && - (foffset < 0 || - (offset > 0 && foffset > OFF_MAX - offset))) { - error = EOVERFLOW; - break; - } - offset += foffset; - break; - case L_XTND: - vn_lock(vp, LK_SHARED | LK_RETRY); - error = VOP_GETATTR(vp, &vattr, cred); - VOP_UNLOCK(vp, 0); - if (error) - break; - - /* - * If the file references a disk device, then fetch - * the media size and use that to determine the ending - * offset. - */ - if (vattr.va_size == 0 && vp->v_type == VCHR && - fo_ioctl(fp, DIOCGMEDIASIZE, &size, cred, td) == 0) - vattr.va_size = size; - if (noneg && - (vattr.va_size > OFF_MAX || - (offset > 0 && vattr.va_size > OFF_MAX - offset))) { - error = EOVERFLOW; - break; - } - offset += vattr.va_size; - break; - case L_SET: - break; - case SEEK_DATA: - error = fo_ioctl(fp, FIOSEEKDATA, &offset, cred, td); - break; - case SEEK_HOLE: - error = fo_ioctl(fp, FIOSEEKHOLE, &offset, cred, td); - break; - default: - error = EINVAL; - } - if (error == 0 && noneg && offset < 0) - error = EINVAL; + error = fget(td, uap->fd, cap_rights_init(&rights, CAP_SEEK), &fp); if (error != 0) - goto drop; - VFS_KNOTE_UNLOCKED(vp, 0); - *(off_t *)(td->td_retval) = offset; -drop: + return (error); + error = (fp->f_ops->fo_flags & DFLAG_SEEKABLE) != 0 ? + fo_seek(fp, uap->offset, uap->whence, td) : ESPIPE; fdrop(fp, td); - foffset_unlock(fp, offset, error != 0 ? FOF_NOUPDATE : 0); return (error); } @@ -2015,8 +1962,8 @@ vn_access(vp, user_flags, cred, td) struct ucred *cred; struct thread *td; { - int error; accmode_t accmode; + int error; /* Flags == 0 means only check for existence. */ error = 0; @@ -2030,7 +1977,7 @@ vn_access(vp, user_flags, cred, td) accmode |= VEXEC; #ifdef MAC error = mac_vnode_check_access(cred, vp, accmode); - if (error) + if (error != 0) return (error); #endif if ((accmode & VWRITE) == 0 || (error = vn_writechk(vp)) == 0) @@ -2092,6 +2039,7 @@ kern_accessat(struct thread *td, int fd, char *path, enum uio_seg pathseg, struct ucred *cred, *tmpcred; struct vnode *vp; struct nameidata nd; + cap_rights_t rights; int error; /* @@ -2108,7 +2056,8 @@ kern_accessat(struct thread *td, int fd, char *path, enum uio_seg pathseg, cred = tmpcred = td->td_ucred; AUDIT_ARG_VALUE(amode); NDINIT_ATRIGHTS(&nd, LOOKUP, FOLLOW | LOCKSHARED | LOCKLEAF | - AUDITVNODE1, pathseg, path, fd, CAP_FSTAT, td); + AUDITVNODE1, pathseg, path, fd, cap_rights_init(&rights, CAP_FSTAT), + td); if ((error = namei(&nd)) != 0) goto out1; vp = nd.ni_vp; @@ -2175,11 +2124,10 @@ ostat(td, uap) int error; error = kern_stat(td, uap->path, UIO_USERSPACE, &sb); - if (error) + if (error != 0) return (error); cvtstat(&sb, &osb); - error = copyout(&osb, uap->ub, sizeof (osb)); - return (error); + return (copyout(&osb, uap->ub, sizeof (osb))); } /* @@ -2204,11 +2152,10 @@ olstat(td, uap) int error; error = kern_lstat(td, uap->path, UIO_USERSPACE, &sb); - if (error) + if (error != 0) return (error); cvtstat(&sb, &osb); - error = copyout(&osb, uap->ub, sizeof (osb)); - return (error); + return (copyout(&osb, uap->ub, sizeof (osb))); } /* @@ -2310,6 +2257,7 @@ kern_statat_vnhook(struct thread *td, int flag, int fd, char *path, { struct nameidata nd; struct stat sb; + cap_rights_t rights; int error; if (flag & ~AT_SYMLINK_NOFOLLOW) @@ -2317,12 +2265,12 @@ kern_statat_vnhook(struct thread *td, int flag, int fd, char *path, NDINIT_ATRIGHTS(&nd, LOOKUP, ((flag & AT_SYMLINK_NOFOLLOW) ? NOFOLLOW : FOLLOW) | LOCKSHARED | LOCKLEAF | AUDITVNODE1, pathseg, path, fd, - CAP_FSTAT, td); + cap_rights_init(&rights, CAP_FSTAT), td); if ((error = namei(&nd)) != 0) return (error); error = vn_stat(nd.ni_vp, &sb, td->td_ucred, NOCRED, td); - if (!error) { + if (error == 0) { SDT_PROBE(vfs, , stat, mode, path, sb.st_mode, 0, 0, 0); if (S_ISREG(sb.st_mode)) SDT_PROBE(vfs, , stat, reg, path, pathseg, 0, 0, 0); @@ -2331,7 +2279,7 @@ kern_statat_vnhook(struct thread *td, int flag, int fd, char *path, } NDFREE(&nd, NDF_ONLY_PNBUF); vput(nd.ni_vp); - if (error) + if (error != 0) return (error); *sbp = sb; #ifdef KTRACE @@ -2383,6 +2331,7 @@ cvtnstat(sb, nsb) struct stat *sb; struct nstat *nsb; { + bzero(nsb, sizeof *nsb); nsb->st_dev = sb->st_dev; nsb->st_ino = sb->st_ino; @@ -2421,11 +2370,10 @@ sys_nstat(td, uap) int error; error = kern_stat(td, uap->path, UIO_USERSPACE, &sb); - if (error) + if (error != 0) return (error); cvtnstat(&sb, &nsb); - error = copyout(&nsb, uap->ub, sizeof (nsb)); - return (error); + return (copyout(&nsb, uap->ub, sizeof (nsb))); } /* @@ -2450,11 +2398,10 @@ sys_nlstat(td, uap) int error; error = kern_lstat(td, uap->path, UIO_USERSPACE, &sb); - if (error) + if (error != 0) return (error); cvtnstat(&sb, &nsb); - error = copyout(&nsb, uap->ub, sizeof (nsb)); - return (error); + return (copyout(&nsb, uap->ub, sizeof (nsb))); } /* @@ -2574,8 +2521,8 @@ kern_readlinkat(struct thread *td, int fd, char *path, enum uio_seg pathseg, struct vnode *vp; struct iovec aiov; struct uio auio; - int error; struct nameidata nd; + int error; if (count > IOSIZE_MAX) return (EINVAL); @@ -2589,7 +2536,7 @@ kern_readlinkat(struct thread *td, int fd, char *path, enum uio_seg pathseg, vp = nd.ni_vp; #ifdef MAC error = mac_vnode_check_readlink(td->td_ucred, vp); - if (error) { + if (error != 0) { vput(vp); return (error); } @@ -2622,9 +2569,9 @@ setfflags(td, vp, flags) struct vnode *vp; u_long flags; { - int error; struct mount *mp; struct vattr vattr; + int error; /* We can't support the value matching VNOVAL. */ if (flags == VNOVAL) @@ -2638,7 +2585,7 @@ setfflags(td, vp, flags) */ if (vp->v_type == VCHR || vp->v_type == VBLK) { error = priv_check(td, PRIV_VFS_CHFLAGS_DEV); - if (error) + if (error != 0) return (error); } @@ -2729,12 +2676,13 @@ kern_chflagsat(struct thread *td, int fd, const char *path, enum uio_seg pathseg, u_long flags, int atflag) { struct nameidata nd; + cap_rights_t rights; int error, follow; AUDIT_ARG_FFLAGS(flags); follow = (atflag & AT_SYMLINK_NOFOLLOW) ? NOFOLLOW : FOLLOW; NDINIT_ATRIGHTS(&nd, LOOKUP, follow | AUDITVNODE1, pathseg, path, fd, - CAP_FCHFLAGS, td); + cap_rights_init(&rights, CAP_FCHFLAGS), td); if ((error = namei(&nd)) != 0) return (error); NDFREE(&nd, NDF_ONLY_PNBUF); @@ -2761,12 +2709,14 @@ sys_fchflags(td, uap) } */ *uap; { struct file *fp; + cap_rights_t rights; int error; AUDIT_ARG_FD(uap->fd); AUDIT_ARG_FFLAGS(uap->flags); - if ((error = getvnode(td->td_proc->p_fd, uap->fd, CAP_FCHFLAGS, - &fp)) != 0) + error = getvnode(td->td_proc->p_fd, uap->fd, + cap_rights_init(&rights, CAP_FCHFLAGS), &fp); + if (error != 0) return (error); #ifdef AUDIT vn_lock(fp->f_vnode, LK_SHARED | LK_RETRY); @@ -2788,9 +2738,9 @@ setfmode(td, cred, vp, mode) struct vnode *vp; int mode; { - int error; struct mount *mp; struct vattr vattr; + int error; if ((error = vn_start_write(vp, &mp, V_WAIT | PCATCH)) != 0) return (error); @@ -2883,14 +2833,14 @@ int kern_fchmodat(struct thread *td, int fd, char *path, enum uio_seg pathseg, mode_t mode, int flag) { - int error; struct nameidata nd; - int follow; + cap_rights_t rights; + int error, follow; AUDIT_ARG_MODE(mode); follow = (flag & AT_SYMLINK_NOFOLLOW) ? NOFOLLOW : FOLLOW; NDINIT_ATRIGHTS(&nd, LOOKUP, follow | AUDITVNODE1, pathseg, path, fd, - CAP_FCHMOD, td); + cap_rights_init(&rights, CAP_FCHMOD), td); if ((error = namei(&nd)) != 0) return (error); NDFREE(&nd, NDF_ONLY_PNBUF); @@ -2912,12 +2862,13 @@ int sys_fchmod(struct thread *td, struct fchmod_args *uap) { struct file *fp; + cap_rights_t rights; int error; AUDIT_ARG_FD(uap->fd); AUDIT_ARG_MODE(uap->mode); - error = fget(td, uap->fd, CAP_FCHMOD, &fp); + error = fget(td, uap->fd, cap_rights_init(&rights, CAP_FCHMOD), &fp); if (error != 0) return (error); error = fo_chmod(fp, uap->mode, td->td_ucred, td); @@ -2936,9 +2887,9 @@ setfown(td, cred, vp, uid, gid) uid_t uid; gid_t gid; { - int error; struct mount *mp; struct vattr vattr; + int error; if ((error = vn_start_write(vp, &mp, V_WAIT | PCATCH)) != 0) return (error); @@ -3015,12 +2966,13 @@ kern_fchownat(struct thread *td, int fd, char *path, enum uio_seg pathseg, int uid, int gid, int flag) { struct nameidata nd; + cap_rights_t rights; int error, follow; AUDIT_ARG_OWNER(uid, gid); follow = (flag & AT_SYMLINK_NOFOLLOW) ? NOFOLLOW : FOLLOW; NDINIT_ATRIGHTS(&nd, LOOKUP, follow | AUDITVNODE1, pathseg, path, fd, - CAP_FCHOWN, td); + cap_rights_init(&rights, CAP_FCHOWN), td); if ((error = namei(&nd)) != 0) return (error); @@ -3082,11 +3034,12 @@ sys_fchown(td, uap) } */ *uap; { struct file *fp; + cap_rights_t rights; int error; AUDIT_ARG_FD(uap->fd); AUDIT_ARG_OWNER(uap->uid, uap->gid); - error = fget(td, uap->fd, CAP_FCHOWN, &fp); + error = fget(td, uap->fd, cap_rights_init(&rights, CAP_FCHOWN), &fp); if (error != 0) return (error); error = fo_chown(fp, uap->uid, uap->gid, td->td_ucred, td); @@ -3139,9 +3092,9 @@ setutimes(td, vp, ts, numtimes, nullflag) int numtimes; int nullflag; { - int error, setbirthtime; struct mount *mp; struct vattr vattr; + int error, setbirthtime; if ((error = vn_start_write(vp, &mp, V_WAIT | PCATCH)) != 0) return (error); @@ -3221,12 +3174,13 @@ kern_utimesat(struct thread *td, int fd, char *path, enum uio_seg pathseg, { struct nameidata nd; struct timespec ts[2]; + cap_rights_t rights; int error; if ((error = getutimes(tptr, tptrseg, ts)) != 0) return (error); NDINIT_ATRIGHTS(&nd, LOOKUP, FOLLOW | AUDITVNODE1, pathseg, path, fd, - CAP_FUTIMES, td); + cap_rights_init(&rights, CAP_FUTIMES), td); if ((error = namei(&nd)) != 0) return (error); @@ -3263,8 +3217,8 @@ kern_lutimes(struct thread *td, char *path, enum uio_seg pathseg, struct timeval *tptr, enum uio_seg tptrseg) { struct timespec ts[2]; - int error; struct nameidata nd; + int error; if ((error = getutimes(tptr, tptrseg, ts)) != 0) return (error); @@ -3304,12 +3258,16 @@ kern_futimes(struct thread *td, int fd, struct timeval *tptr, { struct timespec ts[2]; struct file *fp; + cap_rights_t rights; int error; AUDIT_ARG_FD(fd); - if ((error = getutimes(tptr, tptrseg, ts)) != 0) + error = getutimes(tptr, tptrseg, ts); + if (error != 0) return (error); - if ((error = getvnode(td->td_proc->p_fd, fd, CAP_FUTIMES, &fp)) != 0) + error = getvnode(td->td_proc->p_fd, fd, + cap_rights_init(&rights, CAP_FUTIMES), &fp); + if (error != 0) return (error); #ifdef AUDIT vn_lock(fp->f_vnode, LK_SHARED | LK_RETRY); @@ -3456,13 +3414,17 @@ sys_fsync(td, uap) struct vnode *vp; struct mount *mp; struct file *fp; + cap_rights_t rights; int error, lock_flags; AUDIT_ARG_FD(uap->fd); - if ((error = getvnode(td->td_proc->p_fd, uap->fd, CAP_FSYNC, &fp)) != 0) + error = getvnode(td->td_proc->p_fd, uap->fd, + cap_rights_init(&rights, CAP_FSYNC), &fp); + if (error != 0) return (error); vp = fp->f_vnode; - if ((error = vn_start_write(vp, &mp, V_WAIT | PCATCH)) != 0) + error = vn_start_write(vp, &mp, V_WAIT | PCATCH); + if (error != 0) goto drop; if (MNT_SHARED_WRITES(mp) || ((mp == NULL) && MNT_SHARED_WRITES(vp->v_mount))) { @@ -3538,15 +3500,17 @@ kern_renameat(struct thread *td, int oldfd, char *old, int newfd, char *new, struct mount *mp = NULL; struct vnode *tvp, *fvp, *tdvp; struct nameidata fromnd, tond; + cap_rights_t rights; int error; bwillwrite(); #ifdef MAC NDINIT_ATRIGHTS(&fromnd, DELETE, LOCKPARENT | LOCKLEAF | SAVESTART | - AUDITVNODE1, pathseg, old, oldfd, CAP_RENAMEAT, td); + AUDITVNODE1, pathseg, old, oldfd, + cap_rights_init(&rights, CAP_RENAMEAT), td); #else NDINIT_ATRIGHTS(&fromnd, DELETE, WANTPARENT | SAVESTART | AUDITVNODE1, - pathseg, old, oldfd, CAP_RENAMEAT, td); + pathseg, old, oldfd, cap_rights_init(&rights, CAP_RENAMEAT), td); #endif if ((error = namei(&fromnd)) != 0) @@ -3568,7 +3532,8 @@ kern_renameat(struct thread *td, int oldfd, char *old, int newfd, char *new, goto out1; } NDINIT_ATRIGHTS(&tond, RENAME, LOCKPARENT | LOCKLEAF | NOCACHE | - SAVESTART | AUDITVNODE2, pathseg, new, newfd, CAP_LINKAT, td); + SAVESTART | AUDITVNODE2, pathseg, new, newfd, + cap_rights_init(&rights, CAP_LINKAT), td); if (fromnd.ni_vp->v_type == VDIR) tond.ni_cnd.cn_flags |= WILLBEDIR; if ((error = namei(&tond)) != 0) { @@ -3597,8 +3562,8 @@ kern_renameat(struct thread *td, int oldfd, char *old, int newfd, char *new, * If the target already exists we require CAP_UNLINKAT * from 'newfd'. */ - error = cap_check(tond.ni_filecaps.fc_rights, - CAP_UNLINKAT); + error = cap_check(&tond.ni_filecaps.fc_rights, + cap_rights_init(&rights, CAP_UNLINKAT)); if (error != 0) goto out; } @@ -3620,15 +3585,15 @@ kern_renameat(struct thread *td, int oldfd, char *old, int newfd, char *new, tond.ni_vp, fromnd.ni_dvp == tdvp, &tond.ni_cnd); #endif out: - if (!error) { + if (error == 0) { error = VOP_RENAME(fromnd.ni_dvp, fromnd.ni_vp, &fromnd.ni_cnd, - tond.ni_dvp, tond.ni_vp, &tond.ni_cnd); + tond.ni_dvp, tond.ni_vp, &tond.ni_cnd); NDFREE(&fromnd, NDF_ONLY_PNBUF); NDFREE(&tond, NDF_ONLY_PNBUF); } else { NDFREE(&fromnd, NDF_ONLY_PNBUF); NDFREE(&tond, NDF_ONLY_PNBUF); - if (tvp) + if (tvp != NULL) vput(tvp); if (tdvp == tvp) vrele(tdvp); @@ -3696,14 +3661,15 @@ kern_mkdirat(struct thread *td, int fd, char *path, enum uio_seg segflg, struct mount *mp; struct vnode *vp; struct vattr vattr; - int error; struct nameidata nd; + cap_rights_t rights; + int error; AUDIT_ARG_MODE(mode); restart: bwillwrite(); NDINIT_ATRIGHTS(&nd, CREATE, LOCKPARENT | SAVENAME | AUDITVNODE1, - segflg, path, fd, CAP_MKDIRAT, td); + segflg, path, fd, cap_rights_init(&rights, CAP_MKDIRAT), td); nd.ni_cnd.cn_flags |= WILLBEDIR; if ((error = namei(&nd)) != 0) return (error); @@ -3735,7 +3701,7 @@ restart: #ifdef MAC error = mac_vnode_check_create(td->td_ucred, nd.ni_dvp, &nd.ni_cnd, &vattr); - if (error) + if (error != 0) goto out; #endif error = VOP_MKDIR(nd.ni_dvp, &nd.ni_vp, &nd.ni_cnd, &vattr); @@ -3744,7 +3710,7 @@ out: #endif NDFREE(&nd, NDF_ONLY_PNBUF); vput(nd.ni_dvp); - if (!error) + if (error == 0) vput(nd.ni_vp); vn_finished_write(mp); return (error); @@ -3781,13 +3747,14 @@ kern_rmdirat(struct thread *td, int fd, char *path, enum uio_seg pathseg) { struct mount *mp; struct vnode *vp; - int error; struct nameidata nd; + cap_rights_t rights; + int error; restart: bwillwrite(); NDINIT_ATRIGHTS(&nd, DELETE, LOCKPARENT | LOCKLEAF | AUDITVNODE1, - pathseg, path, fd, CAP_UNLINKAT, td); + pathseg, path, fd, cap_rights_init(&rights, CAP_UNLINKAT), td); if ((error = namei(&nd)) != 0) return (error); vp = nd.ni_vp; @@ -3812,7 +3779,7 @@ restart: #ifdef MAC error = mac_vnode_check_unlink(td->td_ucred, nd.ni_dvp, vp, &nd.ni_cnd); - if (error) + if (error != 0) goto out; #endif if (vn_start_write(nd.ni_dvp, &mp, V_NOWAIT) != 0) { @@ -3872,6 +3839,7 @@ kern_ogetdirentries(struct thread *td, struct ogetdirentries_args *uap, struct uio auio, kuio; struct iovec aiov, kiov; struct dirent *dp, *edp; + cap_rights_t rights; caddr_t dirbuf; int error, eofflag, readcnt; long loff; @@ -3880,7 +3848,9 @@ kern_ogetdirentries(struct thread *td, struct ogetdirentries_args *uap, /* XXX arbitrary sanity limit on `count'. */ if (uap->count > 64 * 1024) return (EINVAL); - if ((error = getvnode(td->td_proc->p_fd, uap->fd, CAP_READ, &fp)) != 0) + error = getvnode(td->td_proc->p_fd, uap->fd, + cap_rights_init(&rights, CAP_READ), &fp); + if (error != 0) return (error); if ((fp->f_flag & FREAD) == 0) { fdrop(fp, td); @@ -3906,7 +3876,7 @@ unionread: loff = auio.uio_offset = foffset; #ifdef MAC error = mac_vnode_check_readdir(td->td_ucred, vp); - if (error) { + if (error != 0) { VOP_UNLOCK(vp, 0); foffset_unlock(fp, foffset, FOF_NOUPDATE); fdrop(fp, td); @@ -3964,7 +3934,7 @@ unionread: } free(dirbuf, M_TEMP); } - if (error) { + if (error != 0) { VOP_UNLOCK(vp, 0); foffset_unlock(fp, foffset, 0); fdrop(fp, td); @@ -4018,7 +3988,7 @@ sys_getdirentries(td, uap) error = kern_getdirentries(td, uap->fd, uap->buf, uap->count, &base, NULL, UIO_USERSPACE); - if (error) + if (error != 0) return (error); if (uap->basep != NULL) error = copyout(&base, uap->basep, sizeof(long)); @@ -4033,6 +4003,7 @@ kern_getdirentries(struct thread *td, int fd, char *buf, u_int count, struct file *fp; struct uio auio; struct iovec aiov; + cap_rights_t rights; long loff; int error, eofflag; off_t foffset; @@ -4041,7 +4012,9 @@ kern_getdirentries(struct thread *td, int fd, char *buf, u_int count, if (count > IOSIZE_MAX) return (EINVAL); auio.uio_resid = count; - if ((error = getvnode(td->td_proc->p_fd, fd, CAP_READ, &fp)) != 0) + error = getvnode(td->td_proc->p_fd, fd, + cap_rights_init(&rights, CAP_READ), &fp); + if (error != 0) return (error); if ((fp->f_flag & FREAD) == 0) { fdrop(fp, td); @@ -4071,7 +4044,7 @@ unionread: error = VOP_READDIR(vp, &auio, fp->f_cred, &eofflag, NULL, NULL); foffset = auio.uio_offset; - if (error) { + if (error != 0) { VOP_UNLOCK(vp, 0); goto fail; } @@ -4079,6 +4052,7 @@ unionread: (vp->v_vflag & VV_ROOT) && (vp->v_mount->mnt_flag & MNT_UNION)) { struct vnode *tvp = vp; + vp = vp->v_mount->mnt_vnodecovered; VREF(vp); fp->f_vnode = vp; @@ -4115,6 +4089,7 @@ sys_getdents(td, uap) } */ *uap; { struct getdirentries_args ap; + ap.fd = uap->fd; ap.buf = uap->buf; ap.count = uap->count; @@ -4165,8 +4140,8 @@ sys_revoke(td, uap) { struct vnode *vp; struct vattr vattr; - int error; struct nameidata nd; + int error; NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF | AUDITVNODE1, UIO_USERSPACE, uap->path, td); @@ -4180,15 +4155,15 @@ sys_revoke(td, uap) } #ifdef MAC error = mac_vnode_check_revoke(td->td_ucred, vp); - if (error) + if (error != 0) goto out; #endif error = VOP_GETATTR(vp, &vattr, td->td_ucred); - if (error) + if (error != 0) goto out; if (td->td_ucred->cr_uid != vattr.va_uid) { error = priv_check(td, PRIV_VFS_ADMIN); - if (error) + if (error != 0) goto out; } if (vcount(vp) > 1) @@ -4204,12 +4179,12 @@ out: * entry is held upon returning. */ int -getvnode(struct filedesc *fdp, int fd, cap_rights_t rights, struct file **fpp) +getvnode(struct filedesc *fdp, int fd, cap_rights_t *rightsp, struct file **fpp) { struct file *fp; int error; - error = fget_unlocked(fdp, fd, rights, 0, &fp, NULL); + error = fget_unlocked(fdp, fd, rightsp, 0, &fp, NULL); if (error != 0) return (error); @@ -4254,12 +4229,12 @@ sys_lgetfh(td, uap) int error; error = priv_check(td, PRIV_VFS_GETFH); - if (error) + if (error != 0) return (error); NDINIT(&nd, LOOKUP, NOFOLLOW | LOCKLEAF | AUDITVNODE1, UIO_USERSPACE, uap->fname, td); error = namei(&nd); - if (error) + if (error != 0) return (error); NDFREE(&nd, NDF_ONLY_PNBUF); vp = nd.ni_vp; @@ -4267,9 +4242,8 @@ sys_lgetfh(td, uap) fh.fh_fsid = vp->v_mount->mnt_stat.f_fsid; error = VOP_VPTOFH(vp, &fh.fh_fid); vput(vp); - if (error) - return (error); - error = copyout(&fh, uap->fhp, sizeof (fh)); + if (error == 0) + error = copyout(&fh, uap->fhp, sizeof (fh)); return (error); } @@ -4290,12 +4264,12 @@ sys_getfh(td, uap) int error; error = priv_check(td, PRIV_VFS_GETFH); - if (error) + if (error != 0) return (error); NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF | AUDITVNODE1, UIO_USERSPACE, uap->fname, td); error = namei(&nd); - if (error) + if (error != 0) return (error); NDFREE(&nd, NDF_ONLY_PNBUF); vp = nd.ni_vp; @@ -4303,9 +4277,8 @@ sys_getfh(td, uap) fh.fh_fsid = vp->v_mount->mnt_stat.f_fsid; error = VOP_VPTOFH(vp, &fh.fh_fid); vput(vp); - if (error) - return (error); - error = copyout(&fh, uap->fhp, sizeof (fh)); + if (error == 0) + error = copyout(&fh, uap->fhp, sizeof (fh)); return (error); } @@ -4338,7 +4311,7 @@ sys_fhopen(td, uap) int indx; error = priv_check(td, PRIV_VFS_FHOPEN); - if (error) + if (error != 0) return (error); indx = -1; fmode = FFLAGS(uap->flags); @@ -4346,7 +4319,7 @@ sys_fhopen(td, uap) if (((fmode & (FREAD | FWRITE)) == 0) || (fmode & O_CREAT)) return (EINVAL); error = copyin(uap->u_fhp, &fhp, sizeof(fhp)); - if (error) + if (error != 0) return(error); /* find the mount point */ mp = vfs_busyfs(&fhp.fh_fsid); @@ -4355,11 +4328,11 @@ sys_fhopen(td, uap) /* now give me my vnode, it gets returned to me locked */ error = VFS_FHTOVP(mp, &fhp.fh_fid, LK_EXCLUSIVE, &vp); vfs_unbusy(mp); - if (error) + if (error != 0) return (error); error = falloc_noinstall(td, &fp); - if (error) { + if (error != 0) { vput(vp); return (error); } @@ -4372,7 +4345,7 @@ sys_fhopen(td, uap) td->td_dupfd = -1; #endif error = vn_open_vnode(vp, fmode, td->td_ucred, td, fp); - if (error) { + if (error != 0) { KASSERT(fp->f_ops == &badfileops, ("VOP_OPEN in fhopen() set f_ops")); KASSERT(td->td_dupfd < 0, @@ -4389,9 +4362,9 @@ sys_fhopen(td, uap) finit(fp, (fmode & FMASK) | (fp->f_flag & FHASLOCK), DTYPE_VNODE, vp, &vnops); VOP_UNLOCK(vp, 0); - if (fmode & O_TRUNC) { + if ((fmode & O_TRUNC) != 0) { error = fo_truncate(fp, 0, td->td_ucred, td); - if (error) + if (error != 0) goto bad; } @@ -4427,9 +4400,8 @@ sys_fhstat(td, uap) if (error != 0) return (error); error = kern_fhstat(td, fh, &sb); - if (error != 0) - return (error); - error = copyout(&sb, uap->sb, sizeof(sb)); + if (error == 0) + error = copyout(&sb, uap->sb, sizeof(sb)); return (error); } @@ -4441,13 +4413,13 @@ kern_fhstat(struct thread *td, struct fhandle fh, struct stat *sb) int error; error = priv_check(td, PRIV_VFS_FHSTAT); - if (error) + if (error != 0) return (error); if ((mp = vfs_busyfs(&fh.fh_fsid)) == NULL) return (ESTALE); error = VFS_FHTOVP(mp, &fh.fh_fid, LK_EXCLUSIVE, &vp); vfs_unbusy(mp); - if (error) + if (error != 0) return (error); error = vn_stat(vp, sb, td->td_ucred, NOCRED, td); vput(vp); @@ -4476,10 +4448,10 @@ sys_fhstatfs(td, uap) int error; error = copyin(uap->u_fhp, &fh, sizeof(fhandle_t)); - if (error) + if (error != 0) return (error); error = kern_fhstatfs(td, fh, &sf); - if (error) + if (error != 0) return (error); return (copyout(&sf, uap->buf, sizeof(sf))); } @@ -4493,22 +4465,22 @@ kern_fhstatfs(struct thread *td, fhandle_t fh, struct statfs *buf) int error; error = priv_check(td, PRIV_VFS_FHSTATFS); - if (error) + if (error != 0) return (error); if ((mp = vfs_busyfs(&fh.fh_fsid)) == NULL) return (ESTALE); error = VFS_FHTOVP(mp, &fh.fh_fid, LK_EXCLUSIVE, &vp); - if (error) { + if (error != 0) { vfs_unbusy(mp); return (error); } vput(vp); error = prison_canseemount(td->td_ucred, mp); - if (error) + if (error != 0) goto out; #ifdef MAC error = mac_mount_check_stat(td->td_ucred, mp); - if (error) + if (error != 0) goto out; #endif /* @@ -4532,11 +4504,12 @@ kern_posix_fallocate(struct thread *td, int fd, off_t offset, off_t len) struct file *fp; struct mount *mp; struct vnode *vp; + cap_rights_t rights; off_t olen, ooffset; int error; fp = NULL; - error = fget(td, fd, CAP_WRITE, &fp); + error = fget(td, fd, cap_rights_init(&rights, CAP_WRITE), &fp); if (error != 0) goto out; @@ -4628,6 +4601,7 @@ kern_posix_fadvise(struct thread *td, int fd, off_t offset, off_t len, struct fadvise_info *fa, *new; struct file *fp; struct vnode *vp; + cap_rights_t rights; off_t end; int error; @@ -4648,7 +4622,7 @@ kern_posix_fadvise(struct thread *td, int fd, off_t offset, off_t len, return (EINVAL); } /* XXX: CAP_POSIX_FADVISE? */ - error = fget(td, fd, CAP_NONE, &fp); + error = fget(td, fd, cap_rights_init(&rights), &fp); if (error != 0) goto out; diff --git a/sys/kern/vfs_vnops.c b/sys/kern/vfs_vnops.c index 06e59f9c00d..00bd9982e84 100644 --- a/sys/kern/vfs_vnops.c +++ b/sys/kern/vfs_vnops.c @@ -45,6 +45,7 @@ __FBSDID("$FreeBSD$"); #include #include +#include #include #include #include @@ -100,6 +101,8 @@ struct fileops vnops = { .fo_close = vn_closefile, .fo_chmod = vn_chmod, .fo_chown = vn_chown, + .fo_sendfile = vn_sendfile, + .fo_seek = vn_seek, .fo_flags = DFLAG_PASSABLE | DFLAG_SEEKABLE }; @@ -264,6 +267,8 @@ vn_open_vnode(struct vnode *vp, int fmode, struct ucred *cred, return (error); } } + if (vp->v_type == VFIFO && VOP_ISLOCKED(vp) != LK_EXCLUSIVE) + vn_lock(vp, LK_UPGRADE | LK_RETRY); if ((error = VOP_OPEN(vp, fmode, cred, td, fp)) != 0) return (error); @@ -355,7 +360,7 @@ vn_close(vp, flags, file_cred, td) struct mount *mp; int error, lock_flags; - if (!(flags & FWRITE) && vp->v_mount != NULL && + if (vp->v_type != VFIFO && !(flags & FWRITE) && vp->v_mount != NULL && vp->v_mount->mnt_kern_flag & MNTK_EXTENDED_SHARED) lock_flags = LK_SHARED; else @@ -2009,3 +2014,72 @@ unlock: *off = noff; return (error); } + +int +vn_seek(struct file *fp, off_t offset, int whence, struct thread *td) +{ + struct ucred *cred; + struct vnode *vp; + struct vattr vattr; + off_t foffset, size; + int error, noneg; + + cred = td->td_ucred; + vp = fp->f_vnode; + foffset = foffset_lock(fp, 0); + noneg = (vp->v_type != VCHR); + error = 0; + switch (whence) { + case L_INCR: + if (noneg && + (foffset < 0 || + (offset > 0 && foffset > OFF_MAX - offset))) { + error = EOVERFLOW; + break; + } + offset += foffset; + break; + case L_XTND: + vn_lock(vp, LK_SHARED | LK_RETRY); + error = VOP_GETATTR(vp, &vattr, cred); + VOP_UNLOCK(vp, 0); + if (error) + break; + + /* + * If the file references a disk device, then fetch + * the media size and use that to determine the ending + * offset. + */ + if (vattr.va_size == 0 && vp->v_type == VCHR && + fo_ioctl(fp, DIOCGMEDIASIZE, &size, cred, td) == 0) + vattr.va_size = size; + if (noneg && + (vattr.va_size > OFF_MAX || + (offset > 0 && vattr.va_size > OFF_MAX - offset))) { + error = EOVERFLOW; + break; + } + offset += vattr.va_size; + break; + case L_SET: + break; + case SEEK_DATA: + error = fo_ioctl(fp, FIOSEEKDATA, &offset, cred, td); + break; + case SEEK_HOLE: + error = fo_ioctl(fp, FIOSEEKHOLE, &offset, cred, td); + break; + default: + error = EINVAL; + } + if (error == 0 && noneg && offset < 0) + error = EINVAL; + if (error != 0) + goto drop; + VFS_KNOTE_UNLOCKED(vp, 0); + *(off_t *)(td->td_retval) = offset; +drop: + foffset_unlock(fp, offset, error != 0 ? FOF_NOUPDATE : 0); + return (error); +} diff --git a/sys/mips/atheros/ar71xx_gpio.c b/sys/mips/atheros/ar71xx_gpio.c index 0fad2ec8053..c6933bc1b06 100644 --- a/sys/mips/atheros/ar71xx_gpio.c +++ b/sys/mips/atheros/ar71xx_gpio.c @@ -41,6 +41,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include @@ -156,6 +157,7 @@ ar71xx_gpio_pin_max(device_t dev, int *maxpin) case AR71XX_SOC_AR9342: case AR71XX_SOC_AR9344: *maxpin = AR934X_GPIO_COUNT - 1; + break; default: *maxpin = AR71XX_GPIO_PINS - 1; } @@ -417,7 +419,14 @@ ar71xx_gpio_attach(device_t dev) "pinon", &pinon) != 0) pinon = 0; device_printf(dev, "gpio pinmask=0x%x\n", mask); - for (i = 0, j = 0; j < maxpin; j++) { + for (j = 0; j <= maxpin; j++) { + if ((mask & (1 << j)) == 0) + continue; + sc->gpio_npins++; + } + sc->gpio_pins = malloc(sizeof(*sc->gpio_pins) * sc->gpio_npins, + M_DEVBUF, M_WAITOK | M_ZERO); + for (i = 0, j = 0; j <= maxpin; j++) { if ((mask & (1 << j)) == 0) continue; snprintf(sc->gpio_pins[i].gp_name, GPIOMAXNAME, @@ -428,7 +437,6 @@ ar71xx_gpio_attach(device_t dev) ar71xx_gpio_pin_configure(sc, &sc->gpio_pins[i], DEFAULT_CAPS); i++; } - sc->gpio_npins = i; for (i = 0; i < sc->gpio_npins; i++) { j = sc->gpio_pins[i].gp_pin; if ((pinon & (1 << j)) != 0) @@ -454,6 +462,7 @@ ar71xx_gpio_detach(device_t dev) bus_release_resource(dev, SYS_RES_MEMORY, sc->gpio_mem_rid, sc->gpio_mem_res); + free(sc->gpio_pins, M_DEVBUF); mtx_destroy(&sc->gpio_mtx); return(0); diff --git a/sys/mips/atheros/ar71xx_gpiovar.h b/sys/mips/atheros/ar71xx_gpiovar.h index 3489f5a7aea..a1c6e2f1c5a 100644 --- a/sys/mips/atheros/ar71xx_gpiovar.h +++ b/sys/mips/atheros/ar71xx_gpiovar.h @@ -64,7 +64,7 @@ struct ar71xx_gpio_softc { int gpio_irq_rid; void *gpio_ih; int gpio_npins; - struct gpio_pin gpio_pins[AR71XX_GPIO_PINS]; + struct gpio_pin *gpio_pins; }; #endif /* __AR71XX_GPIOVAR_H__ */ diff --git a/sys/mips/atheros/ar71xx_spi.c b/sys/mips/atheros/ar71xx_spi.c index 92cc3654d80..095f9306e4d 100644 --- a/sys/mips/atheros/ar71xx_spi.c +++ b/sys/mips/atheros/ar71xx_spi.c @@ -108,7 +108,7 @@ ar71xx_spi_attach(device_t dev) SPI_WRITE(sc, AR71XX_SPI_CTRL, 0x43); SPI_WRITE(sc, AR71XX_SPI_IO_CTRL, SPI_IO_CTRL_CSMASK); - device_add_child(dev, "spibus", 0); + device_add_child(dev, "spibus", -1); return (bus_generic_attach(dev)); } diff --git a/sys/mips/atheros/if_arge.c b/sys/mips/atheros/if_arge.c index 7e933df3263..aabab52187b 100644 --- a/sys/mips/atheros/if_arge.c +++ b/sys/mips/atheros/if_arge.c @@ -142,6 +142,7 @@ static int arge_resume(device_t); static int arge_rx_ring_init(struct arge_softc *); static void arge_rx_ring_free(struct arge_softc *sc); static int arge_tx_ring_init(struct arge_softc *); +static void arge_tx_ring_free(struct arge_softc *); #ifdef DEVICE_POLLING static int arge_poll(struct ifnet *, enum poll_cmd, int); #endif @@ -371,6 +372,7 @@ arge_attach(device_t dev) uint32_t hint; long eeprom_mac_addr = 0; int miicfg = 0; + int readascii = 0; sc = device_get_softc(dev); sc->arge_dev = dev; @@ -384,16 +386,28 @@ arge_attach(device_t dev) * Since multiple units seem to use this feature, include * a method of setting the MAC address based on an flash location * in CPU address space. + * + * Some vendors have decided to store the mac address as a literal + * string of 18 characters in xx:xx:xx:xx:xx:xx format instead of + * an array of numbers. Expose a hint to turn on this conversion + * feature via strtol() */ - if (sc->arge_mac_unit == 0 && - resource_long_value(device_get_name(dev), device_get_unit(dev), + if (resource_long_value(device_get_name(dev), device_get_unit(dev), "eeprommac", &eeprom_mac_addr) == 0) { int i; const char *mac = (const char *) MIPS_PHYS_TO_KSEG1(eeprom_mac_addr); device_printf(dev, "Overriding MAC from EEPROM\n"); - for (i = 0; i < 6; i++) { - ar711_base_mac[i] = mac[i]; + if (resource_int_value(device_get_name(dev), device_get_unit(dev), + "readascii", &readascii) == 0) { + device_printf(dev, "Vendor stores MAC in ASCII format\n"); + for (i = 0; i < 6; i++) { + ar711_base_mac[i] = strtol(&(mac[i*3]), NULL, 16); + } + } else { + for (i = 0; i < 6; i++) { + ar711_base_mac[i] = mac[i]; + } } } @@ -1006,7 +1020,8 @@ arge_init_locked(struct arge_softc *sc) ARGE_LOCK_ASSERT(sc); - arge_stop(sc); + if ((ifp->if_flags & IFF_UP) && (ifp->if_drv_flags & IFF_DRV_RUNNING)) + return; /* Init circular RX list. */ if (arge_rx_ring_init(sc) != 0) { @@ -1264,6 +1279,7 @@ arge_stop(struct arge_softc *sc) /* Flush FIFO and free any existing mbufs */ arge_flush_ddr(sc); arge_rx_ring_free(sc); + arge_tx_ring_free(sc); } @@ -1693,6 +1709,30 @@ arge_tx_ring_init(struct arge_softc *sc) return (0); } +/* + * Free the Tx ring, unload any pending dma transaction and free the mbuf. + */ +static void +arge_tx_ring_free(struct arge_softc *sc) +{ + struct arge_txdesc *txd; + int i; + + /* Free the Tx buffers. */ + for (i = 0; i < ARGE_TX_RING_COUNT; i++) { + txd = &sc->arge_cdata.arge_txdesc[i]; + if (txd->tx_dmamap) { + bus_dmamap_sync(sc->arge_cdata.arge_tx_tag, + txd->tx_dmamap, BUS_DMASYNC_POSTWRITE); + bus_dmamap_unload(sc->arge_cdata.arge_tx_tag, + txd->tx_dmamap); + } + if (txd->tx_m) + m_freem(txd->tx_m); + txd->tx_m = NULL; + } +} + /* * Initialize the RX descriptors and allocate mbufs for them. Note that * we arrange the descriptors in a closed ring, so that the last descriptor diff --git a/sys/mips/cavium/octeon_rnd.c b/sys/mips/cavium/octeon_rnd.c index 634a4fa86f0..298f06aee1d 100644 --- a/sys/mips/cavium/octeon_rnd.c +++ b/sys/mips/cavium/octeon_rnd.c @@ -131,7 +131,7 @@ octeon_rnd_harvest(void *arg) for (i = 0; i < OCTEON_RND_WORDS; i++) sc->sc_entropy[i] = cvmx_rng_get_random64(); random_harvest(sc->sc_entropy, sizeof sc->sc_entropy, - sizeof sc->sc_entropy * 8, 0, RANDOM_PURE); + (sizeof(sc->sc_entropy)*8)/2, 0, RANDOM_PURE); callout_reset(&sc->sc_callout, hz * 5, octeon_rnd_harvest, sc); } diff --git a/sys/mips/conf/DIR-825 b/sys/mips/conf/DIR-825 index 62189cfc285..7740d4dfa01 100644 --- a/sys/mips/conf/DIR-825 +++ b/sys/mips/conf/DIR-825 @@ -54,12 +54,15 @@ options NO_SYSCTL_DESCR device geom_map # to get access to the SPI flash partitions device geom_uncompress # compressed in-memory filesystem hackery! options GEOM_UNCOMPRESS +options GEOM_PART_GPT options ROOTDEVNAME=\"ufs:/dev/map/rootfs.uncompress\" options AR71XX_REALMEM=64*1024*1024 options AR71XX_ENV_UBOOT +options MSDOSFS # Read MSDOS filesystems; useful for USB/CF + # options MD_ROOT # options MD_ROOT_SIZE="6144" diff --git a/sys/mips/conf/DIR-825.hints b/sys/mips/conf/DIR-825.hints index 0fe0196de9b..2ffd1b70602 100644 --- a/sys/mips/conf/DIR-825.hints +++ b/sys/mips/conf/DIR-825.hints @@ -6,13 +6,14 @@ hint.arge.0.phymask=0x0 hint.arge.0.media=1000 hint.arge.0.fduplex=1 - -# XXX grab these from uboot? -# hint.arge.0.eeprommac=0x1f01fc00 +hint.arge.0.eeprommac=0x1f66ffa0 +hint.arge.0.readascii=1 hint.arge.1.phymask=0x0 hint.arge.1.media=1000 hint.arge.1.fduplex=1 +hint.arge.1.eeprommac=0x1f66ffb4 +hint.arge.1.readascii=1 # ath0 - slot 17 hint.pcib.0.bus.0.17.0.ath_fixup_addr=0x1f661000 diff --git a/sys/mips/conf/GXEMUL32 b/sys/mips/conf/GXEMUL32 new file mode 100644 index 00000000000..6bd756f20f7 --- /dev/null +++ b/sys/mips/conf/GXEMUL32 @@ -0,0 +1,61 @@ +# +# GXEMUL "oldtestmips" sample kernel configuration. +# +# $FreeBSD$ +# + +ident GXEMUL + +machine mips mips +cpu CPU_MIPS4KC + +options HZ=100 + +makeoptions KERNLOADADDR=0x80100000 + +include "../gxemul/std.gxemul" + +hints "GXEMUL.hints" #Default places to look for devices. + +makeoptions DEBUG=-g #Build kernel with gdb(1) debug symbols + +makeoptions MODULES_OVERRIDE="" + +options DDB +options KDB + +# Make an SMP-capable kernel by default +options SMP # Symmetric MultiProcessor Kernel + +options SCHED_ULE +options INET # InterNETworking +options INET6 # IPv6 communications protocols + +options FFS #Berkeley Fast Filesystem + +# Debugging for use in -current +#options DEADLKRES #Enable the deadlock resolver +options INVARIANTS #Enable calls of extra sanity checking +options INVARIANT_SUPPORT #Extra sanity checks of internal structures, required by INVARIANTS +#options WITNESS #Enable checks to detect deadlocks and cycles +#options WITNESS_SKIPSPIN #Don't run witness on spinlocks for speed + +options ROOTDEVNAME=\"ufs:gxemul_disk0\" + +device gxemul_cons +device gxemul_disk +device gxemul_ether + +# Pseudo devices. +device loop # Network loopback +device random # Entropy device +device ether # Ethernet support +device tun # Packet tunnel. +device md # Memory "disks" +device gif # IPv6 and IPv4 tunneling +device faith # IPv6-to-IPv4 relaying (translation) + +# The `bpf' device enables the Berkeley Packet Filter. +# Be aware of the administrative consequences of enabling this! +# Note that 'bpf' is required for DHCP. +device bpf # Berkeley packet filter diff --git a/sys/mips/conf/MALTA b/sys/mips/conf/MALTA index 54ece7421aa..ab9d38da44f 100644 --- a/sys/mips/conf/MALTA +++ b/sys/mips/conf/MALTA @@ -65,5 +65,6 @@ device loop device ether device le device miibus +device bpf device md device uart diff --git a/sys/mips/conf/MALTA64 b/sys/mips/conf/MALTA64 index 3f3ace0b6b0..0f5bfdf4c4e 100644 --- a/sys/mips/conf/MALTA64 +++ b/sys/mips/conf/MALTA64 @@ -31,6 +31,8 @@ makeoptions MODULES_OVERRIDE="" options TICK_USE_YAMON_FREQ=defined #options TICK_USE_MALTA_RTC=defined +makeoptions KERNLOADADDR=0xffffffff80100000 + include "../malta/std.malta" hints "MALTA.hints" #Default places to look for devices. @@ -66,4 +68,5 @@ device ether device le device miibus device md +device bpf device uart diff --git a/sys/mips/conf/PICOSTATION_M2HP b/sys/mips/conf/PICOSTATION_M2HP new file mode 100644 index 00000000000..0f5ac17a7f6 --- /dev/null +++ b/sys/mips/conf/PICOSTATION_M2HP @@ -0,0 +1,68 @@ +# +# Specific board setup for the Picostation M2 HP board. +# +# This board has the following hardware: +# +# + AR7241 CPU SoC +# + AR9287 Wifi +# + Integrated switch (XXX speed?) +# + 8MB flash +# + 32MB RAM +# + uboot environment + +# $FreeBSD$ + +include "AR724X_BASE" +ident "PICOSTATION_M2HP" +hints "PICOSTATION_M2HP.hints" + +options AR71XX_REALMEM=32*1024*1024 + +options AR71XX_ENV_UBOOT + +# Limit inlines +makeoptions INLINE_LIMIT=768 + +# We bite the performance overhead for now; the kernel won't +# fit if the mutexes are inlined. +options MUTEX_NOINLINE +options RWLOCK_NOINLINE +options SX_NOINLINE + +# There's no need to enable swapping on this platform. +options NO_SWAPPING + +# For DOS - enable if required +# options MSDOSFS + +# uncompress - to boot read-only lzma natively from flash +device geom_uncompress +options GEOM_UNCOMPRESS +options ROOTDEVNAME=\"ufs:/dev/map/rootfs.uncompress\" + +# Not enough space for these.. +nooptions INVARIANTS +nooptions INVARIANT_SUPPORT +nooptions WITNESS +nooptions WITNESS_SKIPSPIN +nooptions DEBUG_REDZONE +nooptions DEBUG_MEMGUARD + +# Used for the static uboot partition map +device geom_map + +# Options needed for the EEPROM based calibration/PCI configuration data. +options AR71XX_ATH_EEPROM # Fetch EEPROM/PCI config from flash +options ATH_EEPROM_FIRMWARE # Use EEPROM from flash +device firmware # Used by the above + +# Options required for miiproxy and mdiobus +options ARGE_MDIO # Export an MDIO bus separate from arge +device miiproxy # MDIO bus <-> MII PHY rendezvous + +device etherswitch +device arswitch + +# Enable GPIO +device gpio +device gpioled diff --git a/sys/mips/conf/PICOSTATION_M2HP.hints b/sys/mips/conf/PICOSTATION_M2HP.hints new file mode 100644 index 00000000000..bd17428ed02 --- /dev/null +++ b/sys/mips/conf/PICOSTATION_M2HP.hints @@ -0,0 +1,103 @@ +# $FreeBSD$ + +# arge1 MDIO bus +hint.argemdio.0.at="nexus0" +hint.argemdio.0.maddr=0x1a000000 +hint.argemdio.0.msize=0x1000 +hint.argemdio.0.order=0 + +# Override MAC Address with the one on EEPROM +hint.arge.0.eeprommac=0x1fff0000 + +# arge0: dedicated switch port; RMII; dedicated PHY 4 on switch, connected +# via internal switch MDIO bus. +hint.arge.0.media=100 # Map to 100/full +hint.arge.0.fduplex=1 # +hint.arge.0.phymask=0x10 # PHY4 +hint.arge.0.mdio=mdioproxy1 # .. off of the switch mdiobus + +# arge1: nail to 1000/full, RMII - connected to the switch +hint.arge.1.media=1000 # Map to 1000/full +hint.arge.1.fduplex=1 # +hint.arge.1.phymask=0x0 # no directly mapped PHYs + +# +# AR7240 switch config +# +hint.arswitch.0.at="mdio0" +hint.arswitch.0.is_7240=1 # We need to be explicitly told this +hint.arswitch.0.numphys=4 # 4 active switch PHYs (PHY 0 -> 3) +hint.arswitch.0.phy4cpu=1 # Yes, PHY 4 == dedicated PHY +hint.arswitch.0.is_rgmii=0 # No, not RGMII +hint.arswitch.0.is_gmii=0 # No, not GMII + +# ath0 hint - pcie slot 0 +hint.pcib.0.bus.0.0.0.ath_fixup_addr=0x1fff1000 +hint.pcib.0.bus.0.0.0.ath_fixup_size=4096 + +# ath +hint.ath.0.eeprom_firmware="pcib.0.bus.0.0.0.eeprom_firmware" + +# GPIO pins +# Pin 0: red led (sig1) +# Pin 1: yellow led (sig2) +# Pin 11: green len (sig3) +# Pin 7: green len (sig4) +# Pin 12: Reset switch +hint.gpio.0.pinmask=0x1883 + +# Signal leds +hint.gpioled.0.at="gpiobus0" +hint.gpioled.0.name="sig1" +hint.gpioled.0.pins=0x0001 # pin 0 +hint.gpioled.1.at="gpiobus0" +hint.gpioled.1.name="sig2" +hint.gpioled.1.pins=0x0002 # pin 1 +hint.gpioled.2.at="gpiobus0" +hint.gpioled.2.name="sig3" +hint.gpioled.2.pins=0x0800 # pin 11 +hint.gpioled.3.at="gpiobus0" +hint.gpioled.3.name="sig4" +hint.gpioled.3.pins=0x0080 # pin 7 + +# GEOM_MAP +# +# Picostation M2 HP +# +# mtdparts=ar7240-nor0:256k(u-boot),64k(u-boot-env),1024k(kernel),6528k(rootfs),256k(cfg),64k(EEPROM) + +hint.map.0.at="flash/spi0" +hint.map.0.start=0x00000000 +hint.map.0.end=0x00040000 # 256k u-boot +hint.map.0.name="u-boot" +hint.map.0.readonly=1 + +hint.map.1.at="flash/spi0" +hint.map.1.start=0x00040000 +hint.map.1.end=0x00050000 # 64k u-boot-env +hint.map.1.name="u-boot-env" +hint.map.1.readonly=1 + +hint.map.2.at="flash/spi0" +hint.map.2.start=0x00050000 +hint.map.2.end=0x00130000 # 896k kernel +hint.map.2.name="kernel" +hint.map.2.readonly=1 + +hint.map.3.at="flash/spi0" +hint.map.3.start=0x130000 +hint.map.3.end=0x007b0000 # 6656k rootfs +hint.map.3.name="rootfs" +hint.map.3.readonly=0 + +hint.map.4.at="flash/spi0" +hint.map.4.start=0x007b0000 +hint.map.4.end=0x007f0000 # 256k cfg +hint.map.4.name="cfg" +hint.map.4.readonly=0 + +hint.map.5.at="flash/spi0" +hint.map.5.start=0x007f0000 +hint.map.5.end=0x00800000 # 64k EEPROM +hint.map.5.name="eeprom" +hint.map.5.readonly=1 diff --git a/sys/mips/conf/ROUTERSTATION.hints b/sys/mips/conf/ROUTERSTATION.hints index be212800dcd..7b35b229af3 100644 --- a/sys/mips/conf/ROUTERSTATION.hints +++ b/sys/mips/conf/ROUTERSTATION.hints @@ -23,6 +23,24 @@ hint.arge.1.mdio=mdioproxy1 # .. off of the switch mdiobus hint.ukswitch.0.at="mdio0" hint.ukswitch.0.phymask=0x30000 +# Don't flip on anything that isn't already enabled. +# This includes leaving the SPI CS1/CS2 pins as GPIO pins as they're +# not used here. +hint.gpio.0.function_set=0x00000000 +hint.gpio.0.function_clear=0x00000000 + +# These are the GPIO LEDs and buttons which can be software controlled. +hint.gpio.0.pinmask=0x000000ff + +# GPIO 0: Pin 1 +# GPIO 1: Pin 2 +# GPIO 2: RF LED +# GPIO 3: Pin 3 +# GPIO 4: Pin 4 +# GPIO 5: Pin 5 +# GPIO 6: Pin 6 +# GPIO 7: Pin 7 + # RF led hint.gpioled.0.at="gpiobus0" hint.gpioled.0.name="rf" diff --git a/sys/mips/conf/TP-WN1043ND b/sys/mips/conf/TP-WN1043ND index 7e1bafb015b..5493d0f7cda 100644 --- a/sys/mips/conf/TP-WN1043ND +++ b/sys/mips/conf/TP-WN1043ND @@ -34,12 +34,26 @@ options MSDOSFS # redboot stuff. options AR71XX_ENV_UBOOT -# uzip - to boot natively from flash -device geom_uzip -options GEOM_UZIP +# uncompress - to boot natively from flash +device geom_uncompress +options GEOM_UNCOMPRESS # Used for the static uboot partition map device geom_map # Boot off of the rootfs, as defined in the geom_map setup. -options ROOTDEVNAME=\"ufs:map/rootfs.uzip\" +options ROOTDEVNAME=\"ufs:map/rootfs.uncompress\" + +# We bite the performance overhead for now; the kernel won't +# fit if the mutexes are inlined. +options MUTEX_NOINLINE +options RWLOCK_NOINLINE +options SX_NOINLINE + +# Remove everything we don't need. We need a _really_ small kernel! +nooptions INVARIANTS +nooptions INVARIANT_SUPPORT +nooptions WITNESS +nooptions WITNESS_SKIPSPIN +nooptions DEBUG_REDZONE +nooptions DEBUG_MEMGUARD diff --git a/sys/mips/conf/WZR-300HP b/sys/mips/conf/WZR-300HP new file mode 100644 index 00000000000..aff305e81f5 --- /dev/null +++ b/sys/mips/conf/WZR-300HP @@ -0,0 +1,57 @@ +# +# Specific board setup for the Buffalo Airstation WZR-300HP +# +# The WZR-300HP has the following hardware: +# +# + AR7242 CPU SoC +# + AR9280 5GHz 11n +# + AR8136 Gigabit switch +# + 2 m25ll128 based 16MB flash +# + 64MB RAM +# + uboot environment + +# $FreeBSD$ + +include "AR724X_BASE" +ident "WZR-300HP" +hints "WZR-300HP.hints" + +options AR71XX_REALMEM=64*1024*1024 + +options AR71XX_ENV_UBOOT + +options BOOTVERBOSE +# Don't include the SCSI/CAM strings in the default build +options SCSI_NO_SENSE_STRINGS +options SCSI_NO_OP_STRINGS + +# .. And no sysctl strings +options NO_SYSCTL_DESCR + +# GEOM modules +device geom_map # to get access to the SPI flash partitions +device geom_uncompress # compressed in-memory filesystem hackery! + +options ROOTDEVNAME=\"ufs:/dev/map/rootfs.uncompress\" + +# options MD_ROOT +# options MD_ROOT_SIZE="6144" + +options AR71XX_ATH_EEPROM # Fetch EEPROM/PCI config from flash +options ATH_EEPROM_FIRMWARE # Use EEPROM from flash +device firmware # Used by the above + +# Options required for miiproxy and mdiobus +options ARGE_MDIO # Export an MDIO bus separate from arge +device miiproxy # MDIO bus <-> MII PHY rendezvous + +device etherswitch +device arswitch + +# Enable GPIO +device gpio +device gpioled + +# hwpmc +device hwpmc_mips24k +device hwpmc diff --git a/sys/mips/conf/WZR-300HP.hints b/sys/mips/conf/WZR-300HP.hints new file mode 100644 index 00000000000..9a86803948f --- /dev/null +++ b/sys/mips/conf/WZR-300HP.hints @@ -0,0 +1,187 @@ +# $FreeBSD$ + +# arge0 is connected to the LAN side of the switch PHY. +# arge1 is connected to the single port WAN side of the switch PHY. + +# arge1 MDIO bus +hint.argemdio.0.at="nexus0" +hint.argemdio.0.maddr=0x1a000000 +hint.argemdio.0.msize=0x1000 +hint.argemdio.0.order=0 + +hint.arge.0.phymask=0x0 +hint.arge.0.media=1000 +hint.arge.0.fduplex=1 +hint.arge.0.eeprommac=0x1f05120c +hint.arge.0.mdio=mdioproxy1 # .. off of the switch mdiobus + + +# arge1: nail to 1000/full, RMII - connected to the switch +hint.arge.1.media=1000 # Map to 1000/full +hint.arge.1.fduplex=1 # +hint.arge.1.phymask=0x0 # no directly mapped PHYs + +# +# AR7240 switch config +# +hint.arswitch.0.at="mdio0" +hint.arswitch.0.is_7240=1 # We need to be explicitly told this +hint.arswitch.0.numphys=4 # 4 active switch PHYs (PHY 0 -> 3) +hint.arswitch.0.phy4cpu=1 # Yes, PHY 4 == dedicated PHY +hint.arswitch.0.is_rgmii=0 # No, not RGMII +hint.arswitch.0.is_gmii=0 # No, not GMII + +# ath0 - slot 0 +hint.pcib.0.bus.0.0.0.ath_fixup_addr=0x1f051000 +hint.pcib.0.bus.0.0.0.ath_fixup_size=4096 + +# .. and now, telling each ath(4) NIC where to find the firmware +# image. +hint.ath.0.eeprom_firmware="pcib.0.bus.0.0.0.eeprom_firmware" + +# Inherited from AR724X_BASE.hints +#hint.mx25l.0.at="spibus0" +#hint.mx25l.0.cs=0 +# This board has two 16 MB flash devices on difference Chip Select pins +hint.mx25l.1.at="spibus0" +hint.mx25l.1.cs=1 + + +# Geom MAP + +# The WRZ-300HP has 2 16MB flash part - HOWEVER, the 64k caldata isn't +# at the end of the flash. It's ~ 328KB into the flash image. + +# mtdparts=ar7240-nor0: +# 256k(u-boot) +# 64k(u-boot-env) +# 64k@320k(ART) +# 1152k@384k(uImage) +# 6592k@1536k(rootfs) +# 64k@8128k(properties) + +# Uboot lies like a lying liar. OpenWRT does this: +# [ 0.570000] Concatenating MTD devices: +# [ 0.570000] (0): "spi0.0" +# [ 0.570000] (1): "spi0.1" +# [ 0.580000] into device "flash" +# [ 0.580000] Creating 7 MTD partitions on "flash": +# [ 0.590000] 0x000000000000-0x000000040000 : "u-boot" +# [ 0.600000] 0x000000040000-0x000000050000 : "u-boot-env" +# [ 0.600000] 0x000000050000-0x000000060000 : "art" +# [ 0.610000] 0x000000060000-0x000000160000 : "kernel" +# [ 0.620000] 0x000000160000-0x000001ff0000 : "rootfs" +# [ 0.620000] mtd: partition "rootfs" set to be root filesystem +# [ 0.630000] mtd: partition "rootfs_data" created automatically, ofs=330000, len=1CC0000 +# [ 0.640000] 0x000000330000-0x000001ff0000 : "rootfs_data" +# [ 0.650000] 0x000001ff0000-0x000002000000 : "user_property" +# [ 0.650000] 0x000000060000-0x000001ff0000 : "firmware" + +hint.map.0.at="flash/spi0" +hint.map.0.start=0x00000000 +hint.map.0.end= 0x00040000 +hint.map.0.name="uboot" +hint.map.0.readonly=1 + +hint.map.1.at="flash/spi0" +hint.map.1.start=0x00040000 +hint.map.1.end= 0x00050000 # 64k u-boot-env +hint.map.1.name="u-boot-env" +hint.map.1.readonly=1 + +hint.map.2.at="flash/spi0" +hint.map.2.start=0x00050000 +hint.map.2.end= 0x00060000 # 64k ART +hint.map.2.name="ART" +hint.map.2.readonly=1 + +hint.map.3.at="flash/spi0" +hint.map.3.start=0x00060000 +hint.map.3.end= 0x00160000 +hint.map.3.name="kernel" +hint.map.3.readonly=1 + +hint.map.4.at="flash/spi0" +hint.map.4.start=0x00160000 +hint.map.4.end= 0x00FF0000 +hint.map.4.name="rootfs" +hint.map.4.readonly=1 + +#hint.map.5.at="flash/spi1" +hint.map.5.at="flash/spi0" +hint.map.5.start=0x00FF0000 +hint.map.5.end= 0x01000000 +hint.map.5.name="cfg" +hint.map.5.readonly=0 + +# GPIO specific configuration block + +#define GPIO_PIN_INPUT 0x0001 /* input direction */ +#define GPIO_PIN_OUTPUT 0x0002 /* output direction */ +#define GPIO_PIN_OPENDRAIN 0x0004 /* open-drain output */ +#define GPIO_PIN_PUSHPULL 0x0008 /* push-pull output */ +#define GPIO_PIN_TRISTATE 0x0010 /* output disabled */ +#define GPIO_PIN_PULLUP 0x0020 /* internal pull-up enabled */ +#define GPIO_PIN_PULLDOWN 0x0040 /* internal pull-down enabled */ +#define GPIO_PIN_INVIN 0x0080 /* invert input */ +#define GPIO_PIN_INVOUT 0x0100 /* invert output */ +#define GPIO_PIN_PULSATE 0x0200 /* pulsate in hardware */ + +# Pin 1 - SCK +# Pin 2 - SDA +# Pin 3 - test 2 +# Pin 4 - test 3 +# Pin 5 - USB (LED Blue) +# Pin 6 - test a +# Pin 7 - Security (LED Orange) +# Pin 8 - Router (LED Green) +# Pin 9 - Movie Engine On (LED Blue) +# Pin 10 - Movie Engine Off (LED Blue) +# Pin 11 - test a +# Pin 12 - test a +# Pin 13 - test a +# Pin 14 - USB Power (turn on by default) +# Pin 15 - test a +# Pin 16 - test a +# Pin 17 - diag (LED red) + +# Don't flip on anything that isn't already enabled. +# Force on USB power pin 14 +#hint.gpio.0.function_set=0x00000000 +#hint.gpio.0.function_clear=0x00000000 + +# These are the GPIO LEDs and buttons which can be software controlled. +hint.gpio.0.pinmask=0x000103D0 + +hint.gpio.0.pinon=0x00000000 + +hint.gpioiic.0.at="gpiobus0" +hint.gpioiic.0.pins=0x0003 +hint.gpioiic.0.sda=0 +hint.gpioiic.0.scl=1 + +# LEDs are configured separately and driven by the LED device +# usb tested good +hint.gpioled.0.at="gpiobus0" +hint.gpioled.0.name="blue-usb" +hint.gpioled.0.pins=0x00000010 + +hint.gpioled.1.at="gpiobus0" +hint.gpioled.1.name="orange-security" +hint.gpioled.1.pins=0x00000040 + +hint.gpioled.2.at="gpiobus0" +hint.gpioled.2.name="green-router" +hint.gpioled.2.pins=0x00000080 + +hint.gpioled.3.at="gpiobus0" +hint.gpioled.3.name="blue-movie-engine-on" +hint.gpioled.3.pins=0x00000100 + +hint.gpioled.4.at="gpiobus0" +hint.gpioled.4.name="blue-movie-engine-off" +hint.gpioled.4.pins=0x00000200 + +hint.gpioled.5.at="gpiobus0" +hint.gpioled.5.name="red-diag" +hint.gpioled.5.pins=0x00010000 diff --git a/sys/mips/gxemul/mpreg.h b/sys/mips/gxemul/mpreg.h index e09946d88e1..f562d07cb58 100644 --- a/sys/mips/gxemul/mpreg.h +++ b/sys/mips/gxemul/mpreg.h @@ -43,10 +43,17 @@ #define GXEMUL_MP_DEV_IPI_READ 0x00c0 #define GXEMUL_MP_DEV_CYCLES 0x00d0 +#ifdef _LP64 #define GXEMUL_MP_DEV_FUNCTION(f) \ (volatile uint64_t *)MIPS_PHYS_TO_DIRECT_UNCACHED(GXEMUL_MP_DEV_BASE + (f)) #define GXEMUL_MP_DEV_READ(f) \ (volatile uint64_t)*GXEMUL_MP_DEV_FUNCTION(f) +#else +#define GXEMUL_MP_DEV_FUNCTION(f) \ + (volatile uint32_t *)MIPS_PHYS_TO_DIRECT_UNCACHED(GXEMUL_MP_DEV_BASE + (f)) +#define GXEMUL_MP_DEV_READ(f) \ + (volatile uint32_t)*GXEMUL_MP_DEV_FUNCTION(f) +#endif #define GXEMUL_MP_DEV_WRITE(f, v) \ *GXEMUL_MP_DEV_FUNCTION(f) = (v) diff --git a/sys/mips/include/_stdint.h b/sys/mips/include/_stdint.h index 510b8eae32c..ebf6eb5fcbb 100644 --- a/sys/mips/include/_stdint.h +++ b/sys/mips/include/_stdint.h @@ -66,6 +66,7 @@ #if !defined(__cplusplus) || defined(__STDC_LIMIT_MACROS) +#ifndef __INT64_C #ifdef __mips_n64 #define __INT64_C(c) (c ## L) #define __UINT64_C(c) (c ## UL) @@ -73,6 +74,7 @@ #define __INT64_C(c) (c ## LL) #define __UINT64_C(c) (c ## ULL) #endif +#endif /* * ISO/IEC 9899:1999 diff --git a/sys/mips/include/sf_buf.h b/sys/mips/include/sf_buf.h index b9efaf0d8b2..e5d981f3b78 100644 --- a/sys/mips/include/sf_buf.h +++ b/sys/mips/include/sf_buf.h @@ -41,6 +41,18 @@ /* In 64 bit the whole memory is directly mapped */ struct sf_buf; +static inline struct sf_buf * +sf_buf_alloc(struct vm_page *m, int pri) +{ + + return ((struct sf_buf *)m); +} + +static inline void +sf_buf_free(struct sf_buf *sf) +{ +} + static __inline vm_offset_t sf_buf_kva(struct sf_buf *sf) { @@ -66,6 +78,9 @@ struct sf_buf { vm_offset_t kva; /* va of mapping */ }; +struct sf_buf * sf_buf_alloc(struct vm_page *m, int flags); +void sf_buf_free(struct sf_buf *sf); + static __inline vm_offset_t sf_buf_kva(struct sf_buf *sf) { diff --git a/sys/mips/malta/files.malta b/sys/mips/malta/files.malta index 82d9ca49b98..853ca135d04 100644 --- a/sys/mips/malta/files.malta +++ b/sys/mips/malta/files.malta @@ -1,6 +1,7 @@ # $FreeBSD$ mips/malta/gt.c standard mips/malta/gt_pci.c standard +mips/malta/gt_pci_bus_space.c standard mips/malta/obio.c optional uart mips/malta/uart_cpu_maltausart.c optional uart mips/malta/uart_bus_maltausart.c optional uart diff --git a/sys/mips/malta/gt_pci.c b/sys/mips/malta/gt_pci.c index 0e2012ee913..f9d8b83465c 100644 --- a/sys/mips/malta/gt_pci.c +++ b/sys/mips/malta/gt_pci.c @@ -46,6 +46,7 @@ __FBSDID("$FreeBSD$"); #include #include +#include #include #include #include @@ -74,6 +75,7 @@ __FBSDID("$FreeBSD$"); #include #include "pcib_if.h" +#include #define ICU_LEN 16 /* number of ISA IRQs */ @@ -91,6 +93,14 @@ __FBSDID("$FreeBSD$"); #define OCW3_POLL_IRQ(x) ((x) & 0x7f) #define OCW3_POLL_PENDING (1U << 7) +/* + * Galileo controller's registers are LE so convert to then + * to/from native byte order. We rely on boot loader or emulator + * to set "swap bytes" configuration correctly for us + */ +#define GT_PCI_DATA(v) htole32((v)) +#define GT_HOST_DATA(v) le32toh((v)) + struct gt_pci_softc; struct gt_pci_intr_cookie { @@ -266,8 +276,12 @@ gt_pci_attach(device_t dev) sc->sc_io = MIPS_PHYS_TO_KSEG1(MALTA_PCI0_IO_BASE); sc->sc_io_rman.rm_type = RMAN_ARRAY; sc->sc_io_rman.rm_descr = "GT64120 PCI I/O Ports"; + /* + * First 256 bytes are ISA's registers: e.g. i8259's + * So do not use them for general purpose PCI I/O window + */ if (rman_init(&sc->sc_io_rman) != 0 || - rman_manage_region(&sc->sc_io_rman, 0, 0xffff) != 0) { + rman_manage_region(&sc->sc_io_rman, 0x100, 0xffff) != 0) { panic("gt_pci_attach: failed to set up I/O rman"); } @@ -433,20 +447,20 @@ gt_pci_read_config(device_t dev, u_int bus, u_int slot, u_int func, u_int reg, return (uint32_t)(-1); /* Clear cause register bits. */ - GT_REGVAL(GT_INTR_CAUSE) = 0; - - GT_REGVAL(GT_PCI0_CFG_ADDR) = (1 << 31) | addr; - data = GT_REGVAL(GT_PCI0_CFG_DATA); + GT_REGVAL(GT_INTR_CAUSE) = GT_PCI_DATA(0); + GT_REGVAL(GT_PCI0_CFG_ADDR) = GT_PCI_DATA((1 << 31) | addr); + /* + * Galileo system controller is special + */ + if ((bus == 0) && (slot == 0)) + data = GT_PCI_DATA(GT_REGVAL(GT_PCI0_CFG_DATA)); + else + data = GT_REGVAL(GT_PCI0_CFG_DATA); /* Check for master abort. */ - if (GT_REGVAL(GT_INTR_CAUSE) & (GTIC_MASABORT0 | GTIC_TARABORT0)) + if (GT_HOST_DATA(GT_REGVAL(GT_INTR_CAUSE)) & (GTIC_MASABORT0 | GTIC_TARABORT0)) data = (uint32_t) -1; - /* - * XXX: We assume that words readed from GT chip are BE. - * Should we set the mode explicitly during chip - * Initialization? - */ switch(reg % 4) { case 3: @@ -503,11 +517,6 @@ gt_pci_write_config(device_t dev, u_int bus, u_int slot, u_int func, u_int reg, { reg_data = gt_pci_read_config(dev, bus, slot, func, reg, 4); - /* - * XXX: We assume that words readed from GT chip are BE. - * Should we set the mode explicitly during chip - * Initialization? - */ shift = 8 * (reg & 3); switch(bytes) @@ -544,10 +553,23 @@ gt_pci_write_config(device_t dev, u_int bus, u_int slot, u_int func, u_int reg, return; /* Clear cause register bits. */ - GT_REGVAL(GT_INTR_CAUSE) = 0; + GT_REGVAL(GT_INTR_CAUSE) = GT_PCI_DATA(0); + + GT_REGVAL(GT_PCI0_CFG_ADDR) = GT_PCI_DATA((1 << 31) | addr); + + /* + * Galileo system controller is special + */ + if ((bus == 0) && (slot == 0)) + GT_REGVAL(GT_PCI0_CFG_DATA) = GT_PCI_DATA(data); + else + GT_REGVAL(GT_PCI0_CFG_DATA) = data; + +#if 0 + printf("PCICONF_WRITE(%02x:%02x.%02x[%04x] -> %02x(%d)\n", + bus, slot, func, reg, data, bytes); +#endif - GT_REGVAL(GT_PCI0_CFG_ADDR) = (1 << 31) | addr; - GT_REGVAL(GT_PCI0_CFG_DATA) = data; } static int @@ -568,8 +590,10 @@ gt_pci_route_interrupt(device_t pcib, device_t dev, int pin) * PIIX4 IDE adapter. HW IRQ0 */ return 0; + case 11: /* Ethernet */ + return 10; default: - printf("No mapping for %d/%d/%d/%d\n", bus, device, func, pin); + device_printf(pcib, "no IRQ mapping for %d/%d/%d/%d\n", bus, device, func, pin); } return (0); @@ -612,7 +636,6 @@ gt_pci_alloc_resource(device_t bus, device_t child, int type, int *rid, struct gt_pci_softc *sc = device_get_softc(bus); struct resource *rv = NULL; struct rman *rm; - bus_space_tag_t bt = 0; bus_space_handle_t bh = 0; switch (type) { @@ -621,12 +644,10 @@ gt_pci_alloc_resource(device_t bus, device_t child, int type, int *rid, break; case SYS_RES_MEMORY: rm = &sc->sc_mem_rman; - bt = sc->sc_st; bh = sc->sc_mem; break; case SYS_RES_IOPORT: rm = &sc->sc_io_rman; - bt = sc->sc_st; bh = sc->sc_io; break; default: @@ -640,7 +661,7 @@ gt_pci_alloc_resource(device_t bus, device_t child, int type, int *rid, if (type != SYS_RES_IRQ) { bh += (rman_get_start(rv)); - rman_set_bustag(rv, bt); + rman_set_bustag(rv, gt_pci_bus_space); rman_set_bushandle(rv, bh); if (flags & RF_ACTIVE) { if (bus_activate_resource(child, type, *rid, rv)) { diff --git a/sys/mips/malta/gt_pci_bus_space.c b/sys/mips/malta/gt_pci_bus_space.c new file mode 100644 index 00000000000..63eaabe3ace --- /dev/null +++ b/sys/mips/malta/gt_pci_bus_space.c @@ -0,0 +1,409 @@ +/* $NetBSD: bus.h,v 1.12 1997/10/01 08:25:15 fvdl Exp $ */ +/*- + * $Id: bus.h,v 1.6 2007/08/09 11:23:32 katta Exp $ + * + * Copyright (c) 1996, 1997 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility, + * NASA Ames Research Center. + * + * 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. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. + */ + +/* + * Copyright (c) 1996 Charles M. Hannum. All rights reserved. + * Copyright (c) 1996 Christopher G. Demetriou. 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. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Christopher G. Demetriou + * for the NetBSD Project. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + * + * from: src/sys/alpha/include/bus.h,v 1.5 1999/08/28 00:38:40 peter + * $FreeBSD$ + */ +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +static bs_r_2_proto(gt_pci); +static bs_r_4_proto(gt_pci); +static bs_w_2_proto(gt_pci); +static bs_w_4_proto(gt_pci); +static bs_rm_2_proto(gt_pci); +static bs_rm_4_proto(gt_pci); +static bs_wm_2_proto(gt_pci); +static bs_wm_4_proto(gt_pci); +static bs_rr_2_proto(gt_pci); +static bs_rr_4_proto(gt_pci); +static bs_wr_2_proto(gt_pci); +static bs_wr_4_proto(gt_pci); +static bs_sm_2_proto(gt_pci); +static bs_sm_4_proto(gt_pci); +static bs_sr_2_proto(gt_pci); +static bs_sr_4_proto(gt_pci); + +static struct bus_space gt_pci_space = { + /* cookie */ + .bs_cookie = (void *) 0, + + /* mapping/unmapping */ + .bs_map = generic_bs_map, + .bs_unmap = generic_bs_unmap, + .bs_subregion = generic_bs_subregion, + + /* allocation/deallocation */ + .bs_alloc = generic_bs_alloc, + .bs_free = generic_bs_free, + + /* barrier */ + .bs_barrier = generic_bs_barrier, + + /* read (single) */ + .bs_r_1 = generic_bs_r_1, + .bs_r_2 = gt_pci_bs_r_2, + .bs_r_4 = gt_pci_bs_r_4, + .bs_r_8 = NULL, + + /* read multiple */ + .bs_rm_1 = generic_bs_rm_1, + .bs_rm_2 = gt_pci_bs_rm_2, + .bs_rm_4 = gt_pci_bs_rm_4, + .bs_rm_8 = NULL, + + /* read region */ + .bs_rr_1 = generic_bs_rr_1, + .bs_rr_2 = gt_pci_bs_rr_2, + .bs_rr_4 = gt_pci_bs_rr_4, + .bs_rr_8 = NULL, + + /* write (single) */ + .bs_w_1 = generic_bs_w_1, + .bs_w_2 = gt_pci_bs_w_2, + .bs_w_4 = gt_pci_bs_w_4, + .bs_w_8 = NULL, + + /* write multiple */ + .bs_wm_1 = generic_bs_wm_1, + .bs_wm_2 = gt_pci_bs_wm_2, + .bs_wm_4 = gt_pci_bs_wm_4, + .bs_wm_8 = NULL, + + /* write region */ + .bs_wr_1 = generic_bs_wr_1, + .bs_wr_2 = gt_pci_bs_wr_2, + .bs_wr_4 = gt_pci_bs_wr_4, + .bs_wr_8 = NULL, + + /* set multiple */ + .bs_sm_1 = generic_bs_sm_1, + .bs_sm_2 = gt_pci_bs_sm_2, + .bs_sm_4 = gt_pci_bs_sm_4, + .bs_sm_8 = NULL, + + /* set region */ + .bs_sr_1 = generic_bs_sr_1, + .bs_sr_2 = gt_pci_bs_sr_2, + .bs_sr_4 = gt_pci_bs_sr_4, + .bs_sr_8 = NULL, + + /* copy */ + .bs_c_1 = generic_bs_c_1, + .bs_c_2 = generic_bs_c_2, + .bs_c_4 = generic_bs_c_4, + .bs_c_8 = NULL, + + /* read (single) stream */ + .bs_r_1_s = generic_bs_r_1, + .bs_r_2_s = generic_bs_r_2, + .bs_r_4_s = generic_bs_r_4, + .bs_r_8_s = NULL, + + /* read multiple stream */ + .bs_rm_1_s = generic_bs_rm_1, + .bs_rm_2_s = generic_bs_rm_2, + .bs_rm_4_s = generic_bs_rm_4, + .bs_rm_8_s = NULL, + + /* read region stream */ + .bs_rr_1_s = generic_bs_rr_1, + .bs_rr_2_s = generic_bs_rr_2, + .bs_rr_4_s = generic_bs_rr_4, + .bs_rr_8_s = NULL, + + /* write (single) stream */ + .bs_w_1_s = generic_bs_w_1, + .bs_w_2_s = generic_bs_w_2, + .bs_w_4_s = generic_bs_w_4, + .bs_w_8_s = NULL, + + /* write multiple stream */ + .bs_wm_1_s = generic_bs_wm_1, + .bs_wm_2_s = generic_bs_wm_2, + .bs_wm_4_s = generic_bs_wm_4, + .bs_wm_8_s = NULL, + + /* write region stream */ + .bs_wr_1_s = generic_bs_wr_1, + .bs_wr_2_s = generic_bs_wr_2, + .bs_wr_4_s = generic_bs_wr_4, + .bs_wr_8_s = NULL, +}; + +#define rd16(a) le16toh(readw(a)) +#define rd32(a) le32toh(readl(a)) +#define wr16(a, v) writew(a, htole16(v)) +#define wr32(a, v) writel(a, htole32(v)) + +/* generic bus_space tag */ +bus_space_tag_t gt_pci_bus_space = >_pci_space; + +uint16_t +gt_pci_bs_r_2(void *t, bus_space_handle_t handle, + bus_size_t offset) +{ + + return (rd16(handle + offset)); +} + +uint32_t +gt_pci_bs_r_4(void *t, bus_space_handle_t handle, + bus_size_t offset) +{ + + return (rd32(handle + offset)); +} + +void +gt_pci_bs_rm_2(void *t, bus_space_handle_t bsh, + bus_size_t offset, uint16_t *addr, size_t count) +{ + bus_addr_t baddr = bsh + offset; + + while (count--) + *addr++ = rd16(baddr); +} + +void +gt_pci_bs_rm_4(void *t, bus_space_handle_t bsh, + bus_size_t offset, uint32_t *addr, size_t count) +{ + bus_addr_t baddr = bsh + offset; + + while (count--) + *addr++ = rd32(baddr); +} + +/* + * Read `count' 2 or 4 byte quantities from bus space + * described by tag/handle and starting at `offset' and copy into + * buffer provided. + */ +void +gt_pci_bs_rr_2(void *t, bus_space_handle_t bsh, + bus_size_t offset, uint16_t *addr, size_t count) +{ + bus_addr_t baddr = bsh + offset; + + while (count--) { + *addr++ = rd16(baddr); + baddr += 2; + } +} + +void +gt_pci_bs_rr_4(void *t, bus_space_handle_t bsh, + bus_size_t offset, uint32_t *addr, size_t count) +{ + bus_addr_t baddr = bsh + offset; + + while (count--) { + *addr++ = rd32(baddr); + baddr += 4; + } +} + +/* + * Write the 2 or 4 byte value `value' to bus space + * described by tag/handle/offset. + */ +void +gt_pci_bs_w_2(void *t, bus_space_handle_t bsh, + bus_size_t offset, uint16_t value) +{ + + wr16(bsh + offset, value); +} + +void +gt_pci_bs_w_4(void *t, bus_space_handle_t bsh, + bus_size_t offset, uint32_t value) +{ + + wr32(bsh + offset, value); +} + +/* + * Write `count' 2 or 4 byte quantities from the buffer + * provided to bus space described by tag/handle/offset. + */ +void +gt_pci_bs_wm_2(void *t, bus_space_handle_t bsh, + bus_size_t offset, const uint16_t *addr, size_t count) +{ + bus_addr_t baddr = bsh + offset; + + while (count--) + wr16(baddr, *addr++); +} + +void +gt_pci_bs_wm_4(void *t, bus_space_handle_t bsh, + bus_size_t offset, const uint32_t *addr, size_t count) +{ + bus_addr_t baddr = bsh + offset; + + while (count--) + wr32(baddr, *addr++); +} + +/* + * Write `count' 2 or 4 byte quantities from the buffer provided + * to bus space described by tag/handle starting at `offset'. + */ +void +gt_pci_bs_wr_2(void *t, bus_space_handle_t bsh, + bus_size_t offset, const uint16_t *addr, size_t count) +{ + bus_addr_t baddr = bsh + offset; + + while (count--) { + wr16(baddr, *addr++); + baddr += 2; + } +} + +void +gt_pci_bs_wr_4(void *t, bus_space_handle_t bsh, + bus_size_t offset, const uint32_t *addr, size_t count) +{ + bus_addr_t baddr = bsh + offset; + + while (count--) { + wr32(baddr, *addr++); + baddr += 4; + } +} + +/* + * Write the 2 or 4 byte value `val' to bus space described + * by tag/handle/offset `count' times. + */ +void +gt_pci_bs_sm_2(void *t, bus_space_handle_t bsh, + bus_size_t offset, uint16_t value, size_t count) +{ + bus_addr_t addr = bsh + offset; + + while (count--) + wr16(addr, value); +} + +void +gt_pci_bs_sm_4(void *t, bus_space_handle_t bsh, + bus_size_t offset, uint32_t value, size_t count) +{ + bus_addr_t addr = bsh + offset; + + while (count--) + wr32(addr, value); +} + +/* + * Write `count' 2 or 4 byte value `val' to bus space described + * by tag/handle starting at `offset'. + */ +void +gt_pci_bs_sr_2(void *t, bus_space_handle_t bsh, + bus_size_t offset, uint16_t value, size_t count) +{ + bus_addr_t addr = bsh + offset; + + for (; count != 0; count--, addr += 2) + wr16(addr, value); +} + +void +gt_pci_bs_sr_4(void *t, bus_space_handle_t bsh, + bus_size_t offset, uint32_t value, size_t count) +{ + bus_addr_t addr = bsh + offset; + + for (; count != 0; count--, addr += 4) + wr32(addr, value); +} diff --git a/sys/mips/malta/gt_pci_bus_space.h b/sys/mips/malta/gt_pci_bus_space.h new file mode 100644 index 00000000000..77392f544fc --- /dev/null +++ b/sys/mips/malta/gt_pci_bus_space.h @@ -0,0 +1,36 @@ +/*- + * Copyright (c) 2009, Oleksandr Tymoshenko + * 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 unmodified, 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + * $FreeBSD$ + * + */ + +#ifndef __GT_PCI_BUS_SPACEH__ +#define __GT_PCI_BUS_SPACEH__ + +extern bus_space_tag_t gt_pci_bus_space; + +#endif /* __GT_PCI_BUS_SPACEH__ */ diff --git a/sys/mips/malta/malta_machdep.c b/sys/mips/malta/malta_machdep.c index 3bb070be8f5..00f21516238 100644 --- a/sys/mips/malta/malta_machdep.c +++ b/sys/mips/malta/malta_machdep.c @@ -269,8 +269,8 @@ platform_start(__register_t a0, __register_t a1, __register_t a2, vm_offset_t kernend; uint64_t platform_counter_freq; int argc = a0; - char **argv = (char **)a1; - char **envp = (char **)a2; + int32_t *argv = (int32_t*)a1; + int32_t *envp = (int32_t*)a2; unsigned int memsize = a3; int i; @@ -289,15 +289,20 @@ platform_start(__register_t a0, __register_t a1, __register_t a2, printf("entry: platform_start()\n"); bootverbose = 1; + /* + * YAMON uses 32bit pointers to strings so + * convert them to proper type manually + */ if (bootverbose) { printf("cmd line: "); for (i = 0; i < argc; i++) - printf("%s ", argv[i]); + printf("%s ", (char*)(intptr_t)argv[i]); printf("\n"); printf("envp:\n"); for (i = 0; envp[i]; i += 2) - printf("\t%s = %s\n", envp[i], envp[i+1]); + printf("\t%s = %s\n", (char*)(intptr_t)envp[i], + (char*)(intptr_t)envp[i+1]); printf("memsize = %08x\n", memsize); } diff --git a/sys/mips/malta/yamon.c b/sys/mips/malta/yamon.c index 4e167cbb2ac..e669bfe273c 100644 --- a/sys/mips/malta/yamon.c +++ b/sys/mips/malta/yamon.c @@ -56,6 +56,7 @@ yamon_getcpufreq(void) uint32_t freq; int ret; + freq = 0; ret = YAMON_SYSCON_READ(SYSCON_BOARD_CPU_CLOCK_FREQ_ID, &freq, sizeof(freq)); if (ret != 0) diff --git a/sys/mips/mips/bcopy.S b/sys/mips/mips/bcopy.S new file mode 100644 index 00000000000..a7ac1f2aee3 --- /dev/null +++ b/sys/mips/mips/bcopy.S @@ -0,0 +1,286 @@ +/* $NetBSD: bcopy.S,v 1.3 2009/12/14 00:39:00 matt Exp $ */ + +/* + * Mach Operating System + * Copyright (c) 1993 Carnegie Mellon University + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR + * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie Mellon + * the rights to redistribute these changes. + */ + +/* + * File: mips_bcopy.s + * Author: Chris Maeda + * Date: June 1993 + * + * Fast copy routine. Derived from aligned_block_copy. + */ + + +#include +__FBSDID("$FreeBSD$"); + +#include + +#if defined(LIBC_SCCS) && !defined(lint) +#if 0 + ASMSTR("from: @(#)mips_bcopy.s 2.2 CMU 18/06/93") +#else + ASMSTR("$NetBSD: bcopy.S,v 1.3 2009/12/14 00:39:00 matt Exp $") +#endif +#endif /* LIBC_SCCS and not lint */ + +#ifdef __ABICALLS__ + .abicalls +#endif + +/* + * bcopy(caddr_t src, caddr_t dst, unsigned int len) + * + * a0 src address + * a1 dst address + * a2 length + */ + +#define SRCREG a0 +#define DSTREG a1 +#define SIZEREG a2 + +LEAF(memcpy) + .set noat + .set noreorder + + move v0, a0 + move a0, a1 + move a1, v0 + +ALEAF(bcopy) +ALEAF(ovbcopy) + /* + * Make sure we can copy forwards. + */ + sltu t0,SRCREG,DSTREG # t0 == SRCREG < DSTREG + bne t0,zero,6f # copy backwards + + /* + * There are four alignment cases (with frequency) + * (Based on measurements taken with a DECstation 5000/200 + * inside a Mach kernel.) + * + * aligned -> aligned (mostly) + * unaligned -> aligned (sometimes) + * aligned,unaligned -> unaligned (almost never) + * + * Note that we could add another case that checks if + * the destination and source are unaligned but the + * copy is alignable. eg if src and dest are both + * on a halfword boundary. + */ + andi t1,DSTREG,(SZREG-1) # get last bits of dest + bne t1,zero,3f # dest unaligned + andi t0,SRCREG,(SZREG-1) # get last bits of src + bne t0,zero,5f + + /* + * Forward aligned->aligned copy, 8 words at a time. + */ +98: + li AT,-(SZREG*8) + and t0,SIZEREG,AT # count truncated to multiples + PTR_ADDU a3,SRCREG,t0 # run fast loop up to this addr + sltu AT,SRCREG,a3 # any work to do? + beq AT,zero,2f + PTR_SUBU SIZEREG,t0 + + /* + * loop body + */ +1: # cp + REG_L t3,(0*SZREG)(SRCREG) + REG_L v1,(1*SZREG)(SRCREG) + REG_L t0,(2*SZREG)(SRCREG) + REG_L t1,(3*SZREG)(SRCREG) + PTR_ADDU SRCREG,SZREG*8 + REG_S t3,(0*SZREG)(DSTREG) + REG_S v1,(1*SZREG)(DSTREG) + REG_S t0,(2*SZREG)(DSTREG) + REG_S t1,(3*SZREG)(DSTREG) + REG_L t1,(-1*SZREG)(SRCREG) + REG_L t0,(-2*SZREG)(SRCREG) + REG_L v1,(-3*SZREG)(SRCREG) + REG_L t3,(-4*SZREG)(SRCREG) + PTR_ADDU DSTREG,SZREG*8 + REG_S t1,(-1*SZREG)(DSTREG) + REG_S t0,(-2*SZREG)(DSTREG) + REG_S v1,(-3*SZREG)(DSTREG) + bne SRCREG,a3,1b + REG_S t3,(-4*SZREG)(DSTREG) + + /* + * Copy a word at a time, no loop unrolling. + */ +2: # wordcopy + andi t2,SIZEREG,(SZREG-1) # get byte count / SZREG + PTR_SUBU t2,SIZEREG,t2 # t2 = words to copy * SZREG + beq t2,zero,3f + PTR_ADDU t0,SRCREG,t2 # stop at t0 + PTR_SUBU SIZEREG,SIZEREG,t2 +1: + REG_L t3,0(SRCREG) + PTR_ADDU SRCREG,SZREG + REG_S t3,0(DSTREG) + bne SRCREG,t0,1b + PTR_ADDU DSTREG,SZREG + +3: # bytecopy + beq SIZEREG,zero,4f # nothing left to do? + nop +1: + lb t3,0(SRCREG) + PTR_ADDU SRCREG,1 + sb t3,0(DSTREG) + PTR_SUBU SIZEREG,1 + bgtz SIZEREG,1b + PTR_ADDU DSTREG,1 + +4: # copydone + j ra + nop + + /* + * Copy from unaligned source to aligned dest. + */ +5: # destaligned + andi t0,SIZEREG,(SZREG-1) # t0 = bytecount mod SZREG + PTR_SUBU a3,SIZEREG,t0 # number of words to transfer + beq a3,zero,3b + nop + move SIZEREG,t0 # this many to do after we are done + PTR_ADDU a3,SRCREG,a3 # stop point + +1: + REG_LHI t3,0(SRCREG) + REG_LLO t3,SZREG-1(SRCREG) + PTR_ADDI SRCREG,SZREG + REG_S t3,0(DSTREG) + bne SRCREG,a3,1b + PTR_ADDI DSTREG,SZREG + + b 3b + nop + +6: # backcopy -- based on above + PTR_ADDU SRCREG,SIZEREG + PTR_ADDU DSTREG,SIZEREG + andi t1,DSTREG,SZREG-1 # get last 3 bits of dest + bne t1,zero,3f + andi t0,SRCREG,SZREG-1 # get last 3 bits of src + bne t0,zero,5f + + /* + * Forward aligned->aligned copy, 8*4 bytes at a time. + */ + li AT,(-8*SZREG) + and t0,SIZEREG,AT # count truncated to multiple of 32 + beq t0,zero,2f # any work to do? + PTR_SUBU SIZEREG,t0 + PTR_SUBU a3,SRCREG,t0 + + /* + * loop body + */ +1: # cp + REG_L t3,(-4*SZREG)(SRCREG) + REG_L v1,(-3*SZREG)(SRCREG) + REG_L t0,(-2*SZREG)(SRCREG) + REG_L t1,(-1*SZREG)(SRCREG) + PTR_SUBU SRCREG,8*SZREG + REG_S t3,(-4*SZREG)(DSTREG) + REG_S v1,(-3*SZREG)(DSTREG) + REG_S t0,(-2*SZREG)(DSTREG) + REG_S t1,(-1*SZREG)(DSTREG) + REG_L t1,(3*SZREG)(SRCREG) + REG_L t0,(2*SZREG)(SRCREG) + REG_L v1,(1*SZREG)(SRCREG) + REG_L t3,(0*SZREG)(SRCREG) + PTR_SUBU DSTREG,8*SZREG + REG_S t1,(3*SZREG)(DSTREG) + REG_S t0,(2*SZREG)(DSTREG) + REG_S v1,(1*SZREG)(DSTREG) + bne SRCREG,a3,1b + REG_S t3,(0*SZREG)(DSTREG) + + /* + * Copy a word at a time, no loop unrolling. + */ +2: # wordcopy + andi t2,SIZEREG,SZREG-1 # get byte count / 4 + PTR_SUBU t2,SIZEREG,t2 # t2 = number of words to copy + beq t2,zero,3f + PTR_SUBU t0,SRCREG,t2 # stop at t0 + PTR_SUBU SIZEREG,SIZEREG,t2 +1: + REG_L t3,-SZREG(SRCREG) + PTR_SUBU SRCREG,SZREG + REG_S t3,-SZREG(DSTREG) + bne SRCREG,t0,1b + PTR_SUBU DSTREG,SZREG + +3: # bytecopy + beq SIZEREG,zero,4f # nothing left to do? + nop +1: + lb t3,-1(SRCREG) + PTR_SUBU SRCREG,1 + sb t3,-1(DSTREG) + PTR_SUBU SIZEREG,1 + bgtz SIZEREG,1b + PTR_SUBU DSTREG,1 + +4: # copydone + j ra + nop + + /* + * Copy from unaligned source to aligned dest. + */ +5: # destaligned + andi t0,SIZEREG,SZREG-1 # t0 = bytecount mod 4 + PTR_SUBU a3,SIZEREG,t0 # number of words to transfer + beq a3,zero,3b + nop + move SIZEREG,t0 # this many to do after we are done + PTR_SUBU a3,SRCREG,a3 # stop point + +1: + REG_LHI t3,-SZREG(SRCREG) + REG_LLO t3,-1(SRCREG) + PTR_SUBU SRCREG,SZREG + REG_S t3,-SZREG(DSTREG) + bne SRCREG,a3,1b + PTR_SUBU DSTREG,SZREG + + b 3b + nop + + .set reorder + .set at +END(memcpy) diff --git a/sys/mips/mips/mp_machdep.c b/sys/mips/mips/mp_machdep.c index 88c2357a1a3..2a6bbb4be35 100644 --- a/sys/mips/mips/mp_machdep.c +++ b/sys/mips/mips/mp_machdep.c @@ -33,6 +33,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include #include @@ -182,7 +183,7 @@ start_ap(int cpuid) int cpus, ms; cpus = mp_naps; - dpcpu = (void *)kmem_alloc(kernel_map, DPCPU_SIZE); + dpcpu = (void *)kmem_malloc(kernel_arena, DPCPU_SIZE, M_WAITOK | M_ZERO); mips_sync(); diff --git a/sys/mips/mips/pmap.c b/sys/mips/mips/pmap.c index 3ef51865c6a..d1bf5ea00d5 100644 --- a/sys/mips/mips/pmap.c +++ b/sys/mips/mips/pmap.c @@ -1070,8 +1070,6 @@ pmap_pinit(pmap_t pmap) vm_page_t ptdpg; int i; - PMAP_LOCK_INIT(pmap); - /* * allocate the page directory page */ @@ -1231,7 +1229,6 @@ pmap_release(pmap_t pmap) ptdpg->wire_count--; atomic_subtract_int(&cnt.v_wire_count, 1); vm_page_free_zero(ptdpg); - PMAP_LOCK_DESTROY(pmap); } /* @@ -1917,7 +1914,6 @@ pmap_protect(pmap_t pmap, vm_offset_t sva, vm_offset_t eva, vm_prot_t prot) if (prot & VM_PROT_WRITE) return; - rw_wlock(&pvh_global_lock); PMAP_LOCK(pmap); for (; sva < eva; sva = va_next) { pdpe = pmap_segmap(pmap, sva); @@ -1983,7 +1979,6 @@ pmap_protect(pmap_t pmap, vm_offset_t sva, vm_offset_t eva, vm_prot_t prot) if (va != va_next) pmap_invalidate_range(pmap, va, sva); } - rw_wunlock(&pvh_global_lock); PMAP_UNLOCK(pmap); } @@ -2014,7 +2009,7 @@ pmap_enter(pmap_t pmap, vm_offset_t va, vm_prot_t access, vm_page_t m, KASSERT((m->oflags & VPO_UNMANAGED) != 0 || va < kmi.clean_sva || va >= kmi.clean_eva, ("pmap_enter: managed mapping within the clean submap")); - KASSERT((m->oflags & (VPO_UNMANAGED | VPO_BUSY)) != 0, + KASSERT((m->oflags & VPO_UNMANAGED) != 0 || vm_page_xbusied(m), ("pmap_enter: page %p is not busy", m)); pa = VM_PAGE_TO_PHYS(m); newpte = TLBLO_PA_TO_PFN(pa) | init_pte_prot(m, access, prot); @@ -2812,13 +2807,12 @@ pmap_remove_write(vm_page_t m) ("pmap_remove_write: page %p is not managed", m)); /* - * If the page is not VPO_BUSY, then PGA_WRITEABLE cannot be set by - * another thread while the object is locked. Thus, if PGA_WRITEABLE - * is clear, no page table entries need updating. + * If the page is not exclusive busied, then PGA_WRITEABLE cannot be + * set by another thread while the object is locked. Thus, + * if PGA_WRITEABLE is clear, no page table entries need updating. */ VM_OBJECT_ASSERT_WLOCKED(m->object); - if ((m->oflags & VPO_BUSY) == 0 && - (m->aflags & PGA_WRITEABLE) == 0) + if (!vm_page_xbusied(m) && (m->aflags & PGA_WRITEABLE) == 0) return; rw_wlock(&pvh_global_lock); TAILQ_FOREACH(pv, &m->md.pv_list, pv_list) { @@ -2878,13 +2872,12 @@ pmap_is_modified(vm_page_t m) ("pmap_is_modified: page %p is not managed", m)); /* - * If the page is not VPO_BUSY, then PGA_WRITEABLE cannot be + * If the page is not exclusive busied, then PGA_WRITEABLE cannot be * concurrently set while the object is locked. Thus, if PGA_WRITEABLE * is clear, no PTEs can have PTE_D set. */ VM_OBJECT_ASSERT_WLOCKED(m->object); - if ((m->oflags & VPO_BUSY) == 0 && - (m->aflags & PGA_WRITEABLE) == 0) + if (!vm_page_xbusied(m) && (m->aflags & PGA_WRITEABLE) == 0) return (FALSE); rw_wlock(&pvh_global_lock); rv = pmap_testbit(m, PTE_D); @@ -2918,6 +2911,95 @@ pmap_is_prefaultable(pmap_t pmap, vm_offset_t addr) return (rv); } +/* + * Apply the given advice to the specified range of addresses within the + * given pmap. Depending on the advice, clear the referenced and/or + * modified flags in each mapping and set the mapped page's dirty field. + */ +void +pmap_advise(pmap_t pmap, vm_offset_t sva, vm_offset_t eva, int advice) +{ + pd_entry_t *pde, *pdpe; + pt_entry_t *pte; + vm_offset_t va, va_next; + vm_paddr_t pa; + vm_page_t m; + + if (advice != MADV_DONTNEED && advice != MADV_FREE) + return; + rw_wlock(&pvh_global_lock); + PMAP_LOCK(pmap); + for (; sva < eva; sva = va_next) { + pdpe = pmap_segmap(pmap, sva); +#ifdef __mips_n64 + if (*pdpe == 0) { + va_next = (sva + NBSEG) & ~SEGMASK; + if (va_next < sva) + va_next = eva; + continue; + } +#endif + va_next = (sva + NBPDR) & ~PDRMASK; + if (va_next < sva) + va_next = eva; + + pde = pmap_pdpe_to_pde(pdpe, sva); + if (*pde == NULL) + continue; + + /* + * Limit our scan to either the end of the va represented + * by the current page table page, or to the end of the + * range being write protected. + */ + if (va_next > eva) + va_next = eva; + + va = va_next; + for (pte = pmap_pde_to_pte(pde, sva); sva != va_next; pte++, + sva += PAGE_SIZE) { + if (!pte_test(pte, PTE_MANAGED | PTE_V)) { + if (va != va_next) { + pmap_invalidate_range(pmap, va, sva); + va = va_next; + } + continue; + } + pa = TLBLO_PTE_TO_PA(*pte); + m = PHYS_TO_VM_PAGE(pa); + m->md.pv_flags &= ~PV_TABLE_REF; + if (pte_test(pte, PTE_D)) { + if (advice == MADV_DONTNEED) { + /* + * Future calls to pmap_is_modified() + * can be avoided by making the page + * dirty now. + */ + vm_page_dirty(m); + } else { + pte_clear(pte, PTE_D); + if (va == va_next) + va = sva; + } + } else { + /* + * Unless PTE_D is set, any TLB entries + * mapping "sva" don't allow write access, so + * they needn't be invalidated. + */ + if (va != va_next) { + pmap_invalidate_range(pmap, va, sva); + va = va_next; + } + } + } + if (va != va_next) + pmap_invalidate_range(pmap, va, sva); + } + rw_wunlock(&pvh_global_lock); + PMAP_UNLOCK(pmap); +} + /* * Clear the modify bits on the specified physical page. */ @@ -2931,13 +3013,13 @@ pmap_clear_modify(vm_page_t m) KASSERT((m->oflags & VPO_UNMANAGED) == 0, ("pmap_clear_modify: page %p is not managed", m)); VM_OBJECT_ASSERT_WLOCKED(m->object); - KASSERT((m->oflags & VPO_BUSY) == 0, - ("pmap_clear_modify: page %p is busy", m)); + KASSERT(!vm_page_xbusied(m), + ("pmap_clear_modify: page %p is exclusive busied", m)); /* * If the page is not PGA_WRITEABLE, then no PTEs can have PTE_D set. * If the object containing the page is locked and the page is not - * VPO_BUSY, then PGA_WRITEABLE cannot be concurrently set. + * write busied, then PGA_WRITEABLE cannot be concurrently set. */ if ((m->aflags & PGA_WRITEABLE) == 0) return; @@ -3015,7 +3097,7 @@ pmap_mapdev(vm_paddr_t pa, vm_size_t size) offset = pa & PAGE_MASK; size = roundup(size + offset, PAGE_SIZE); - va = kmem_alloc_nofault(kernel_map, size); + va = kva_alloc(size); if (!va) panic("pmap_mapdev: Couldn't alloc kernel virtual memory"); pa = trunc_page(pa); @@ -3043,7 +3125,7 @@ pmap_unmapdev(vm_offset_t va, vm_size_t size) base = trunc_page(va); offset = va & PAGE_MASK; size = roundup(size + offset, PAGE_SIZE); - kmem_free(kernel_map, base, size); + kva_free(base, size); #endif } @@ -3149,21 +3231,6 @@ pmap_align_superpage(vm_object_t object, vm_ooffset_t offset, *addr = ((*addr + SEGMASK) & ~SEGMASK) + superpage_offset; } -/* - * Increase the starting virtual address of the given mapping so - * that it is aligned to not be the second page in a TLB entry. - * This routine assumes that the length is appropriately-sized so - * that the allocation does not share a TLB entry at all if required. - */ -void -pmap_align_tlb(vm_offset_t *addr) -{ - if ((*addr & PAGE_SIZE) == 0) - return; - *addr += PAGE_SIZE; - return; -} - #ifdef DDB DB_SHOW_COMMAND(ptable, ddb_pid_dump) { diff --git a/sys/mips/mips/support.S b/sys/mips/mips/support.S index 7acebf02872..42800000ceb 100644 --- a/sys/mips/mips/support.S +++ b/sys/mips/mips/support.S @@ -506,98 +506,6 @@ LEAF(fswintrberr) li v0, -1 END(fswintrberr) -/* - * memcpy(to, from, len) - * {ov}bcopy(from, to, len) - */ -LEAF(memcpy) - .set noreorder - move v0, a0 # swap from and to - move a0, a1 - move a1, v0 -ALEAF(bcopy) -ALEAF(ovbcopy) - .set noreorder - PTR_ADDU t0, a0, a2 # t0 = end of s1 region - sltu t1, a1, t0 - sltu t2, a0, a1 - and t1, t1, t2 # t1 = true if from < to < (from+len) - beq t1, zero, forward # non overlapping, do forward copy - slt t2, a2, 12 # check for small copy - - ble a2, zero, 2f - PTR_ADDU t1, a1, a2 # t1 = end of to region -1: - lb v1, -1(t0) # copy bytes backwards, - PTR_SUBU t0, t0, 1 # doesnt happen often so do slow way - PTR_SUBU t1, t1, 1 - bne t0, a0, 1b - sb v1, 0(t1) -2: - j ra - nop -forward: - bne t2, zero, smallcpy # do a small bcopy - xor v1, a0, a1 # compare low two bits of addresses - and v1, v1, 3 - PTR_SUBU a3, zero, a1 # compute # bytes to word align address - beq v1, zero, aligned # addresses can be word aligned - and a3, a3, 3 - - beq a3, zero, 1f - PTR_SUBU a2, a2, a3 # subtract from remaining count - LWHI v1, 0(a0) # get next 4 bytes (unaligned) - LWLO v1, 3(a0) - PTR_ADDU a0, a0, a3 - SWHI v1, 0(a1) # store 1, 2, or 3 bytes to align a1 - PTR_ADDU a1, a1, a3 -1: - and v1, a2, 3 # compute number of words left - PTR_SUBU a3, a2, v1 - move a2, v1 - PTR_ADDU a3, a3, a0 # compute ending address -2: - LWHI v1, 0(a0) # copy words a0 unaligned, a1 aligned - LWLO v1, 3(a0) - PTR_ADDU a0, a0, 4 - sw v1, 0(a1) - PTR_ADDU a1, a1, 4 - bne a0, a3, 2b - nop # We have to do this mmu-bug. - b smallcpy - nop -aligned: - beq a3, zero, 1f - PTR_SUBU a2, a2, a3 # subtract from remaining count - LWHI v1, 0(a0) # copy 1, 2, or 3 bytes to align - PTR_ADDU a0, a0, a3 - SWHI v1, 0(a1) - PTR_ADDU a1, a1, a3 -1: - and v1, a2, 3 # compute number of whole words left - PTR_SUBU a3, a2, v1 - move a2, v1 - PTR_ADDU a3, a3, a0 # compute ending address -2: - lw v1, 0(a0) # copy words - PTR_ADDU a0, a0, 4 - sw v1, 0(a1) - bne a0, a3, 2b - PTR_ADDU a1, a1, 4 -smallcpy: - ble a2, zero, 2f - PTR_ADDU a3, a2, a0 # compute ending address -1: - lbu v1, 0(a0) # copy bytes - PTR_ADDU a0, a0, 1 - sb v1, 0(a1) - bne a0, a3, 1b - PTR_ADDU a1, a1, 1 # MMU BUG ? can not do -1(a1) at 0x80000000!! -2: - j ra - nop -END(memcpy) - /* * memset(void *s1, int c, int len) * NetBSD: memset.S,v 1.3 2001/10/16 15:40:53 uch Exp diff --git a/sys/mips/mips/vm_machdep.c b/sys/mips/mips/vm_machdep.c index 0323bb3d9b3..c42f6406f26 100644 --- a/sys/mips/mips/vm_machdep.c +++ b/sys/mips/mips/vm_machdep.c @@ -76,7 +76,9 @@ __FBSDID("$FreeBSD$"); #include #include +#ifndef __mips_n64 #include +#endif #ifndef NSFBUFS #define NSFBUFS (512 + maxusers * 16) @@ -514,7 +516,7 @@ sf_buf_init(void *arg) mtx_init(&sf_freelist.sf_lock, "sf_bufs list lock", NULL, MTX_DEF); SLIST_INIT(&sf_freelist.sf_head); - sf_base = kmem_alloc_nofault(kernel_map, nsfbufs * PAGE_SIZE); + sf_base = kva_alloc(nsfbufs * PAGE_SIZE); sf_bufs = malloc(nsfbufs * sizeof(struct sf_buf), M_TEMP, M_NOWAIT | M_ZERO); for (i = 0; i < nsfbufs; i++) { @@ -523,7 +525,6 @@ sf_buf_init(void *arg) } sf_buf_alloc_want = 0; } -#endif /* * Get an sf_buf from the freelist. Will block if none are available. @@ -531,7 +532,6 @@ sf_buf_init(void *arg) struct sf_buf * sf_buf_alloc(struct vm_page *m, int flags) { -#ifndef __mips_n64 struct sf_buf *sf; int error; @@ -560,9 +560,6 @@ sf_buf_alloc(struct vm_page *m, int flags) } mtx_unlock(&sf_freelist.sf_lock); return (sf); -#else - return ((struct sf_buf *)m); -#endif } /* @@ -571,7 +568,6 @@ sf_buf_alloc(struct vm_page *m, int flags) void sf_buf_free(struct sf_buf *sf) { -#ifndef __mips_n64 pmap_qremove(sf->kva, 1); mtx_lock(&sf_freelist.sf_lock); SLIST_INSERT_HEAD(&sf_freelist.sf_head, sf, free_list); @@ -579,8 +575,8 @@ sf_buf_free(struct sf_buf *sf) if (sf_buf_alloc_want > 0) wakeup(&sf_freelist); mtx_unlock(&sf_freelist.sf_lock); -#endif } +#endif /* !__mips_n64 */ /* * Software interrupt handler for queued VM system processing. diff --git a/sys/mips/nlm/board.c b/sys/mips/nlm/board.c index c6fd59d4921..e7fd92efe3c 100644 --- a/sys/mips/nlm/board.c +++ b/sys/mips/nlm/board.c @@ -280,15 +280,8 @@ nlm_setup_port_defaults(struct xlp_port_ivars *p) * 1 3 9 0 */ static void -nlm_board_get_phyaddr(int block, int port, int *mdio, int *phyaddr) +nlm_board_get_phyaddr(int block, int port, int *phyaddr) { - - /* XXXJC: this is a board feature, check for chip not proper */ - if (nlm_is_xlp3xx() || (nlm_is_xlp8xx() && block == 4)) - *mdio = 0; - else - *mdio = 1; - switch (block) { case 0: switch (port) { case 0: *phyaddr = 4; break; @@ -377,7 +370,7 @@ nlm_print_processor_info(void) * at run-time goes here */ static int -nlm_setup_xlp_board(void) +nlm_setup_xlp_board(int node) { struct xlp_board_info *boardp; struct xlp_node_info *nodep; @@ -385,17 +378,18 @@ nlm_setup_xlp_board(void) struct xlp_block_ivars *blockp; struct xlp_port_ivars *portp; uint64_t cpldbase, nae_pcibase; - int node, block, port, rv, dbtype, usecpld; + int block, port, rv, dbtype, usecpld = 0, evp = 0, svp = 0; uint8_t *b; /* start with a clean slate */ boardp = &xlp_board_info; - memset(boardp, 0, sizeof(xlp_board_info)); - boardp->nodemask = 0x1; /* only node 0 */ + if (boardp->nodemask == 0) + memset(boardp, 0, sizeof(xlp_board_info)); + boardp->nodemask |= (1 << node); nlm_print_processor_info(); b = board_eeprom_buf; - rv = nlm_board_eeprom_read(0, EEPROM_I2CBUS, EEPROM_I2CADDR, 0, b, + rv = nlm_board_eeprom_read(node, EEPROM_I2CBUS, EEPROM_I2CADDR, 0, b, EEPROM_SIZE); if (rv == 0) { board_eeprom_set = 1; @@ -409,88 +403,48 @@ nlm_setup_xlp_board(void) printf("Board Info: Error on EEPROM read (i2c@%d %#X).\n", EEPROM_I2CBUS, EEPROM_I2CADDR); + nae_pcibase = nlm_get_nae_pcibase(node); + nodep = &boardp->nodes[node]; + naep = &nodep->nae_ivars; + naep->node = node; - /* XXXJC: check for boards with right CPLD, for now - * 4xx PCI cards don't have CPLD with daughter - * card info */ - usecpld = !nlm_is_xlp4xx(); + /* frequency at which network block runs */ + naep->freq = 500; - for (node = 0; node < XLP_MAX_NODES; node++) { - if ((boardp->nodemask & (1 << node)) == 0) - continue; - nae_pcibase = nlm_get_nae_pcibase(node); - nodep = &boardp->nodes[node]; - naep = &nodep->nae_ivars; - naep->node = node; + /* CRC16 polynomial used for flow table generation */ + naep->flow_crc_poly = 0xffff; + naep->hw_parser_en = 1; + naep->prepad_en = 1; + naep->prepad_size = 3; /* size in 16 byte units */ + naep->ieee_1588_en = 1; - naep->nblocks = nae_num_complex(nae_pcibase); - /* 3xx chips lie shamelessly about this */ - if (nlm_is_xlp3xx()) - naep->nblocks = naep->nblocks - 1; - naep->blockmask = (1 << naep->nblocks) - 1; /* XXXJC: redundant */ - naep->xauimask = 0x0; /* set this based on daughter card */ - naep->sgmiimask = 0x0; /* set this based on daughter card */ - - /* frequency at which network block runs */ - naep->freq = 500; - - /* CRC16 polynomial used for flow table generation */ - naep->flow_crc_poly = 0xffff; - naep->hw_parser_en = 1; - naep->prepad_en = 1; - naep->prepad_size = 3; /* size in 16 byte units */ - - naep->ieee_1588_en = 1; - cpldbase = nlm_board_cpld_base(node, XLP_EVB_CPLD_CHIPSELECT); - - for (block = 0; block < naep->nblocks; block++) { - blockp = &naep->block_ivars[block]; - blockp->block = block; - if (usecpld) - dbtype = nlm_board_cpld_dboard_type(cpldbase, - block); - else - dbtype = DCARD_XAUI; /* default XAUI */ - - if (block == 4) { - /* management block 4 on 8xx */ - blockp->type = SGMIIC; - blockp->portmask = 0x3; - naep->sgmiimask |= (1 << block); - } else { - switch (dbtype) { - case DCARD_ILAKEN: - blockp->type = ILC; - blockp->portmask = 0x1; - naep->xauimask |= (1 << block); - break; - case DCARD_SGMII: - blockp->type = SGMIIC; - blockp->portmask = 0xf; - naep->sgmiimask |= (1 << block); - break; - case DCARD_XAUI: - default: - blockp->type = XAUIC; - blockp->portmask = 0x1; - naep->xauimask |= (1 << block); - break; - } - } - for (port = 0; port < PORTS_PER_CMPLX; port++) { - if ((blockp->portmask & (1 << port)) == 0) - continue; - portp = &blockp->port_ivars[port]; - nlm_board_get_phyaddr(block, port, - &portp->mdio_bus, &portp->phy_addr); - portp->port = port; - portp->block = block; - portp->node = node; - portp->type = blockp->type; - nlm_setup_port_defaults(portp); - } - } + naep->ilmask = 0x0; /* set this based on daughter card */ + naep->xauimask = 0x0; /* set this based on daughter card */ + naep->sgmiimask = 0x0; /* set this based on daughter card */ + naep->nblocks = nae_num_complex(nae_pcibase); + if (strncmp(&b[16], "PCIE", 4) == 0) { + usecpld = 0; /* XLP PCIe card */ + /* Broadcom's XLP PCIe card has the following + * blocks fixed. + * blk 0-XAUI, 1-XAUI, 4-SGMII(one port) */ + naep->blockmask = 0x13; + } else if (strncmp(&b[16], "MB-EVP", 6) == 0) { + usecpld = 1; /* XLP non-PCIe card which has CPLD */ + evp = 1; + naep->blockmask = (1 << naep->nblocks) - 1; + } else if ((strncmp(&b[16], "MB-S", 4) == 0) || + (strncmp(&b[16], "MB_S", 4) == 0)) { + usecpld = 1; /* XLP non-PCIe card which has CPLD */ + svp = 1; + /* 3xx chip reports one block extra which is a bug */ + naep->nblocks = naep->nblocks - 1; + naep->blockmask = (1 << naep->nblocks) - 1; + } else { + printf("ERROR!!! Board type:%7s didn't match any board" + " type we support\n", &b[16]); + return (-1); } + cpldbase = nlm_board_cpld_base(node, XLP_EVB_CPLD_CHIPSELECT); /* pretty print network config */ printf("Network config"); @@ -498,30 +452,86 @@ nlm_setup_xlp_board(void) printf("(from CPLD@%d):\n", XLP_EVB_CPLD_CHIPSELECT); else printf("(defaults):\n"); - for (node = 0; node < XLP_MAX_NODES; node++) { - if ((boardp->nodemask & (1 << node)) == 0) - continue; - nodep = &boardp->nodes[node]; - naep = &nodep->nae_ivars; - printf(" NAE@%d Blocks: ", node); - for (block = 0; block < naep->nblocks; block++) { - char *s = "???"; + printf(" NAE@%d Blocks: ", node); + for (block = 0; block < naep->nblocks; block++) { + char *s = "???"; - blockp = &naep->block_ivars[block]; - switch (blockp->type) { - case SGMIIC : s = "SGMII"; break; - case XAUIC : s = "XAUI"; break; - case ILC : s = "IL"; break; + if ((naep->blockmask & (1 << block)) == 0) + continue; + blockp = &naep->block_ivars[block]; + blockp->block = block; + if (usecpld) + dbtype = nlm_board_cpld_dboard_type(cpldbase, block); + else + dbtype = DCARD_XAUI; /* default XAUI */ + + /* XLP PCIe cards */ + if ((!evp && !svp) && ((block == 2) || (block == 3))) + dbtype = DCARD_NOT_PRSNT; + + if (block == 4) { + /* management block 4 on 8xx or XLP PCIe */ + blockp->type = SGMIIC; + if (evp) + blockp->portmask = 0x3; + else + blockp->portmask = 0x1; + naep->sgmiimask |= (1 << block); + } else { + switch (dbtype) { + case DCARD_ILAKEN: + blockp->type = ILC; + blockp->portmask = 0x1; + naep->ilmask |= (1 << block); + break; + case DCARD_SGMII: + blockp->type = SGMIIC; + blockp->portmask = 0xf; + naep->sgmiimask |= (1 << block); + break; + case DCARD_XAUI: + blockp->type = XAUIC; + blockp->portmask = 0x1; + naep->xauimask |= (1 << block); + break; + default: /* DCARD_NOT_PRSNT */ + blockp->type = UNKNOWN; + blockp->portmask = 0; + break; } - printf(" [%d %s]", block, s); } - printf("\n"); + if (blockp->type != UNKNOWN) { + for (port = 0; port < PORTS_PER_CMPLX; port++) { + if ((blockp->portmask & (1 << port)) == 0) + continue; + portp = &blockp->port_ivars[port]; + nlm_board_get_phyaddr(block, port, + &portp->phy_addr); + if (svp || (block == 4)) + portp->mdio_bus = 0; + else + portp->mdio_bus = 1; + portp->port = port; + portp->block = block; + portp->node = node; + portp->type = blockp->type; + nlm_setup_port_defaults(portp); + } + } + switch (blockp->type) { + case SGMIIC : s = "SGMII"; break; + case XAUIC : s = "XAUI"; break; + case ILC : s = "IL"; break; + } + printf(" [%d %s]", block, s); } + printf("\n"); return (0); } int nlm_board_info_setup(void) { - nlm_setup_xlp_board(); + if (nlm_setup_xlp_board(0) != 0) + return (-1); return (0); } diff --git a/sys/mips/nlm/board.h b/sys/mips/nlm/board.h index 859a65416f9..2f1b433cd3d 100644 --- a/sys/mips/nlm/board.h +++ b/sys/mips/nlm/board.h @@ -116,6 +116,7 @@ struct xlp_nae_ivars { int node; int nblocks; u_int blockmask; + u_int ilmask; u_int xauimask; u_int sgmiimask; int freq; diff --git a/sys/mips/nlm/board_cpld.c b/sys/mips/nlm/board_cpld.c index 883a32128c9..ac55c0b883a 100644 --- a/sys/mips/nlm/board_cpld.c +++ b/sys/mips/nlm/board_cpld.c @@ -55,13 +55,13 @@ int nlm_cpld_read(uint64_t base, int reg) uint16_t val; val = *(volatile uint16_t *)(long)(base + reg * 2); - return bswap16(val); + return le16toh(val); } static __inline void nlm_cpld_write(uint64_t base, int reg, uint16_t data) { - bswap16(data); + data = htole16(data); *(volatile uint16_t *)(long)(base + reg * 2) = data; } diff --git a/sys/mips/nlm/dev/net/nae.c b/sys/mips/nlm/dev/net/nae.c index d97b2903862..9f06372885e 100644 --- a/sys/mips/nlm/dev/net/nae.c +++ b/sys/mips/nlm/dev/net/nae.c @@ -59,31 +59,17 @@ nlm_nae_flush_free_fifo(uint64_t nae_base, int nblocks) } void -nlm_program_nae_parser_seq_fifo(uint64_t nae_base, int nblock, +nlm_program_nae_parser_seq_fifo(uint64_t nae_base, int maxports, struct nae_port_config *cfg) { uint32_t val; - int start = 0, size, i, j; - - for (i = 0; i < nblock; i++) { - for (j = 0; j < PORTS_PER_CMPLX; j++) { - if ((i == 4) && (j > 1)) - size = 0; - else - size = cfg[(i*4)+j].pseq_fifo_size; - start += size; - } - } - - for (j = 0; j < PORTS_PER_CMPLX; j++) { - if ((i == 4) && (j > 1)) - size = 0; - else - size = cfg[(i*4)+j].pseq_fifo_size; + int start = 0, size, i; + for (i = 0; i < maxports; i++) { + size = cfg[i].pseq_fifo_size; val = (((size & 0x1fff) << 17) | ((start & 0xfff) << 5) | - (((i * 4) + j) & 0x1f)); + (i & 0x1f)); nlm_write_nae_reg(nae_base, NAE_PARSER_SEQ_FIFO_CFG, val); start += size; } @@ -255,105 +241,66 @@ nlm_setup_flow_crc_poly(uint64_t nae_base, uint32_t poly) } void -nlm_setup_iface_fifo_cfg(uint64_t nae_base, int nblock, +nlm_setup_iface_fifo_cfg(uint64_t nae_base, int maxports, struct nae_port_config *cfg) { uint32_t reg; int fifo_xoff_thresh = 12; - int i, size, j; + int i, size; int cur_iface_start = 0; - for (i = 0; i < nblock; i++) { - for (j = 0; j < PORTS_PER_CMPLX; j++) { - if ((i == 4) && (j > 1)) - size = 0; - else - size = cfg[(i*4)+j].iface_fifo_size; - cur_iface_start += size; - } - } - - for (j = 0; j < PORTS_PER_CMPLX; j++) { - if ((i == 4) && (j > 1)) - size = 0; - else - size = cfg[(i*4)+j].iface_fifo_size; + for (i = 0; i < maxports; i++) { + size = cfg[i].iface_fifo_size; reg = ((fifo_xoff_thresh << 25) | ((size & 0x1ff) << 16) | ((cur_iface_start & 0xff) << 8) | - (((i * 4) + j) & 0x1f)); + (i & 0x1f)); nlm_write_nae_reg(nae_base, NAE_IFACE_FIFO_CFG, reg); cur_iface_start += size; } } void -nlm_setup_rx_base_config(uint64_t nae_base, int nblock, +nlm_setup_rx_base_config(uint64_t nae_base, int maxports, struct nae_port_config *cfg) { - uint32_t val, nc; int base = 0; - int i, j; + uint32_t val; + int i; int id; - for (i = 0; i < nblock; i++) { - for (j = 0; j < (PORTS_PER_CMPLX/2); j++) { - base += cfg[(i*4)+(2*j)].num_channels; - base += cfg[(i*4)+(2*j + 1)].num_channels; - } - } + for (i = 0; i < (maxports/2); i++) { + id = 0x12 + i; /* RX_IF_BASE_CONFIG0 */ - id = 0x12 + (i * 2); /* RX_IF_BASE_CONFIG0 */ - - for (j = 0; j < (PORTS_PER_CMPLX/2); j++) { val = (base & 0x3ff); - nc = cfg[(i*4)+(2*j)].num_channels; - base += nc; + base += cfg[(i * 2)].num_channels; val |= ((base & 0x3ff) << 16); - nc = cfg[(i*4)+(2*j + 1)].num_channels; - base += nc; + base += cfg[(i * 2) + 1].num_channels; - nlm_write_nae_reg(nae_base, NAE_REG(7, 0, (id+j)), val); + nlm_write_nae_reg(nae_base, NAE_REG(7, 0, id), val); } } void -nlm_setup_rx_buf_config(uint64_t nae_base, int nblock, +nlm_setup_rx_buf_config(uint64_t nae_base, int maxports, struct nae_port_config *cfg) { uint32_t val; - int i, sz, j, k; + int i, sz, k; int context = 0; int base = 0; - int nc = 0; - for (i = 0; i < nblock; i++) { - for (j = 0; j < PORTS_PER_CMPLX; j++) { - if ((i == 4) && (j > 1)) - nc = 0; - else - nc = cfg[(i*4)+j].num_channels; - for (k = 0; k < nc; k++) { - sz = cfg[(i*4)+j].rxbuf_size; - base += sz; - } - context += nc; - } - } - - for (j = 0; j < PORTS_PER_CMPLX; j++) { - if ((i == 4) && (j > 1)) - nc = 0; - else - nc = cfg[(i*4)+j].num_channels; - for (k = 0; k < nc; k++) { + for (i = 0; i < maxports; i++) { + if (cfg[i].type == UNKNOWN) + continue; + for (k = 0; k < cfg[i].num_channels; k++) { /* write index (context num) */ nlm_write_nae_reg(nae_base, NAE_RXBUF_BASE_DPTH_ADDR, (context+k)); /* write value (rx buf sizes) */ - sz = cfg[(i*4)+j].rxbuf_size; + sz = cfg[i].rxbuf_size; val = 0x80000000 | ((base << 2) & 0x3fff); /* base */ val |= (((sz << 2) & 0x3fff) << 16); /* size */ @@ -362,46 +309,29 @@ nlm_setup_rx_buf_config(uint64_t nae_base, int nblock, (0x7fffffff & val)); base += sz; } - context += nc; + context += cfg[i].num_channels; } } void -nlm_setup_freein_fifo_cfg(uint64_t nae_base, int nblock, - struct nae_port_config *cfg) +nlm_setup_freein_fifo_cfg(uint64_t nae_base, struct nae_port_config *cfg) { - int size, i, cp = 0; + int size, i; uint32_t reg; - int start = 0; + int start = 0, maxbufpool; - for (cp = 0 ; cp < nblock; cp++ ) { - for (i = 0; i < PORTS_PER_CMPLX; i++) { /* 4 interfaces */ - if ((cp == 4) && (i > 1)) - size = 0; - else { - /* Each entry represents 2 descs; hence division by 2 */ - size = cfg[(cp*4)+i].num_free_descs / 2; - } - if (size == 0) - size = 8; - start += size; - } - } - - for (i = 0; i < PORTS_PER_CMPLX; i++) { /* 4 interfaces */ - if ((cp == 4) && (i > 1)) - size = 0; - else { - /* Each entry represents 2 descs; hence division by 2 */ - size = cfg[(cp*4)+i].num_free_descs / 2; - } + if (nlm_is_xlp8xx()) + maxbufpool = MAX_FREE_FIFO_POOL_8XX; + else + maxbufpool = MAX_FREE_FIFO_POOL_3XX; + for (i = 0; i < maxbufpool; i++) { /* Each entry represents 2 descs; hence division by 2 */ + size = (cfg[i].num_free_descs / 2); if (size == 0) size = 8; - reg = ((size & 0x3ff ) << 20) | /* fcSize */ ((start & 0x1ff) << 8) | /* fcStart */ - (((cp * 4) + i) & 0x1f); + (i & 0x1f); nlm_write_nae_reg(nae_base, NAE_FREE_IN_FIFO_CFG, reg); start += size; diff --git a/sys/mips/nlm/dev/net/xaui.c b/sys/mips/nlm/dev/net/xaui.c index 8b4c6a9b9e7..3b18ef9ba9f 100644 --- a/sys/mips/nlm/dev/net/xaui.c +++ b/sys/mips/nlm/dev/net/xaui.c @@ -211,12 +211,10 @@ nlm_config_xaui(uint64_t nae_base, int nblock, nlm_write_nae_reg(nae_base, XAUI_CONFIG0(nblock), 0); /* Enable tx/rx frame */ - val = 0xF00010A8; + val = 0x000010A8; val |= XAUI_CONFIG_LENCHK; val |= XAUI_CONFIG_GENFCS; val |= XAUI_CONFIG_PAD_64; - val |= XAUI_CONFIG_TFEN; - val |= XAUI_CONFIG_RFEN; nlm_write_nae_reg(nae_base, XAUI_CONFIG1(nblock), val); /* write max frame length */ diff --git a/sys/mips/nlm/dev/net/xlpge.c b/sys/mips/nlm/dev/net/xlpge.c index 18a14b132d0..3ae114821f7 100644 --- a/sys/mips/nlm/dev/net/xlpge.c +++ b/sys/mips/nlm/dev/net/xlpge.c @@ -306,29 +306,12 @@ static int xlpnae_get_maxchannels(struct nlm_xlpnae_softc *sc) { int maxchans = 0; - int i, j, port = 0; + int i; - for (i = 0; i < sc->nblocks; i++) { - switch (sc->cmplx_type[i]) { - case SGMIIC: - for (j = 0; j < 4; j++) { /* 4 ports */ - if ((i == 4) && (j > 1)) - continue; - maxchans += sc->portcfg[port].num_channels; - port++; - } - break; - case XAUIC: - maxchans += sc->portcfg[port].num_channels; - port += 4; - break; - case ILC: - if (((i%2) == 0) && (i != 4)) { - maxchans += sc->portcfg[port].num_channels; - port += 4; - break; - } - } + for (i = 0; i < sc->max_ports; i++) { + if (sc->portcfg[i].type == UNKNOWN) + continue; + maxchans += sc->portcfg[i].num_channels; } return (maxchans); @@ -374,7 +357,7 @@ nlm_setup_interfaces(struct nlm_xlpnae_softc *sc) uint32_t cur_slot, cur_slot_base; uint32_t cur_flow_base, port, flow_mask; int max_channels; - int i, j, context; + int i, context; cur_slot = 0; cur_slot_base = 0; @@ -386,39 +369,13 @@ nlm_setup_interfaces(struct nlm_xlpnae_softc *sc) port = 0; context = 0; - for (i = 0; i < sc->nblocks; i++) { - switch (sc->cmplx_type[i]) { - case SGMIIC: - for (j = 0; j < 4; j++) { /* 4 ports */ - if ((i == 4) && (j > 1)) - continue; - nlm_setup_interface(sc, i, port, - cur_flow_base, flow_mask, - max_channels, context); - cur_flow_base += sc->per_port_num_flows; - context += sc->portcfg[port].num_channels; - port++; - } - break; - case XAUIC: - nlm_setup_interface(sc, i, port, cur_flow_base, - flow_mask, max_channels, context); - cur_flow_base += sc->per_port_num_flows; - context += sc->portcfg[port].num_channels; - port += 4; - break; - case ILC: - if (((i%2) == 0) && (i != 4)) { - nlm_setup_interface(sc, i, port, - cur_flow_base, flow_mask, - max_channels, context); - cur_flow_base += sc->per_port_num_flows; - context += sc->portcfg[port].num_channels; - port += 4; - } - break; - } - cur_slot_base++; + for (i = 0; i < sc->max_ports; i++) { + if (sc->portcfg[i].type == UNKNOWN) + continue; + nlm_setup_interface(sc, sc->portcfg[i].block, i, cur_flow_base, + flow_mask, max_channels, context); + cur_flow_base += sc->per_port_num_flows; + context += sc->portcfg[i].num_channels; } } @@ -481,8 +438,6 @@ nlm_xlpnae_init(int node, struct nlm_xlpnae_softc *sc) nlm_setup_interfaces(sc); nlm_config_poe(sc->poe_base, sc->poedv_base); - nlm_xlpnae_print_frin_desc_carving(sc); - if (sc->hw_parser_en) nlm_enable_hardware_parser(nae_base); @@ -530,6 +485,12 @@ nlm_setup_portcfg(struct nlm_xlpnae_softc *sc, struct xlp_nae_ivars *naep, bp = &(naep->block_ivars[block]); p = &(bp->port_ivars[port & 0x3]); + sc->portcfg[port].node = p->node; + sc->portcfg[port].block = p->block; + sc->portcfg[port].port = p->port; + sc->portcfg[port].type = p->type; + sc->portcfg[port].mdio_bus = p->mdio_bus; + sc->portcfg[port].phy_addr = p->phy_addr; sc->portcfg[port].loopback_mode = p->loopback_mode; sc->portcfg[port].num_channels = p->num_channels; if (p->free_desc_sizes != MCLBYTES) { @@ -584,7 +545,7 @@ nlm_xlpnae_attach(device_t dev) struct nlm_xlpnae_softc *sc; device_t tmpd; uint32_t dv[NUM_WORDS_PER_DV]; - int port, i, j, n, nchan, nblock, node, qstart, qnum; + int port, i, j, nchan, nblock, node, qstart, qnum; int offset, context, txq_base, rxvcbase; uint64_t poe_pcibase, nae_pcibase; @@ -598,6 +559,8 @@ nlm_xlpnae_attach(device_t dev) sc->poe_base = nlm_get_poe_regbase(sc->node); sc->poedv_base = nlm_get_poedv_regbase(sc->node); sc->portcfg = nae_port_config; + sc->blockmask = nae_ivars->blockmask; + sc->ilmask = nae_ivars->ilmask; sc->xauimask = nae_ivars->xauimask; sc->sgmiimask = nae_ivars->sgmiimask; sc->nblocks = nae_ivars->nblocks; @@ -615,9 +578,10 @@ nlm_xlpnae_attach(device_t dev) sc->ncontexts = nlm_read_reg(nae_pcibase, XLP_PCI_DEVINFO_REG5); sc->nucores = nlm_num_uengines(nae_pcibase); - /* Initialize the 1st four complexes from board config */ - for (nblock = 0; nblock < sc->nblocks; nblock++) + for (nblock = 0; nblock < sc->nblocks; nblock++) { sc->cmplx_type[nblock] = nae_ivars->block_ivars[nblock].type; + sc->portmask[nblock] = nae_ivars->block_ivars[nblock].portmask; + } for (i = 0; i < sc->ncontexts; i++) cntx2port[i] = 18; /* 18 is an invalid port */ @@ -627,6 +591,8 @@ nlm_xlpnae_attach(device_t dev) else sc->max_ports = sc->nblocks * PORTS_PER_CMPLX; + for (i = 0; i < sc->max_ports; i++) + sc->portcfg[i].type = UNKNOWN; /* Port Not Present */ /* * Now setup all internal fifo carvings based on * total number of ports in the system @@ -638,13 +604,15 @@ nlm_xlpnae_attach(device_t dev) txq_base = nlm_qidstart(nae_pcibase); rxvcbase = txq_base + sc->ncontexts; for (i = 0; i < sc->nblocks; i++) { - /* only 2 SGMII ports in last complex */ - n = (sc->cmplx_type[i] == SGMIIC && i == 4) ? 2 : 4; - for (j = 0; j < n; j++, port++) { - if (sc->cmplx_type[i] == XAUIC && j != 0) - continue; - if (sc->cmplx_type[i] == ILC && - (i != 0 || i != 2 || j != 0)) + uint32_t portmask; + + if ((nae_ivars->blockmask & (1 << i)) == 0) { + port += 4; + continue; + } + portmask = nae_ivars->block_ivars[i].portmask; + for (j = 0; j < PORTS_PER_CMPLX; j++, port++) { + if ((portmask & (1 << j)) == 0) continue; nlm_setup_portcfg(sc, nae_ivars, i, port); nchan = sc->portcfg[port].num_channels; @@ -687,31 +655,27 @@ nlm_xlpnae_attach(device_t dev) nlm_xlpnae_init(node, sc); - for (i = 0; i < sc->nblocks; i++) { + for (i = 0; i < sc->max_ports; i++) { char desc[32]; - struct xlp_block_ivars *bv; + int block, port; - if ((nae_ivars->blockmask & (1 << i)) == 0) + if (sc->portcfg[i].type == UNKNOWN) continue; - bv = &nae_ivars->block_ivars[i]; - for (j = 0; j < PORTS_PER_CMPLX; j++) { - int port = i * 4 + j; - - if ((bv->portmask & (1 << j)) == 0) - continue; - tmpd = device_add_child(dev, "xlpge", port); - device_set_ivars(tmpd, &(bv->port_ivars[j])); - sprintf(desc, "XLP NAE Port %d,%d", i, j); - device_set_desc_copy(tmpd, desc); - } - - nlm_setup_iface_fifo_cfg(sc->base, i, sc->portcfg); - nlm_setup_rx_base_config(sc->base, i, sc->portcfg); - nlm_setup_rx_buf_config(sc->base, i, sc->portcfg); - nlm_setup_freein_fifo_cfg(sc->base, i, sc->portcfg); - nlm_program_nae_parser_seq_fifo(sc->base, i, sc->portcfg); + block = sc->portcfg[i].block; + port = sc->portcfg[i].port; + tmpd = device_add_child(dev, "xlpge", i); + device_set_ivars(tmpd, + &(nae_ivars->block_ivars[block].port_ivars[port])); + sprintf(desc, "XLP NAE Port %d,%d", block, port); + device_set_desc_copy(tmpd, desc); } + nlm_setup_iface_fifo_cfg(sc->base, sc->max_ports, sc->portcfg); + nlm_setup_rx_base_config(sc->base, sc->max_ports, sc->portcfg); + nlm_setup_rx_buf_config(sc->base, sc->max_ports, sc->portcfg); + nlm_setup_freein_fifo_cfg(sc->base, sc->portcfg); + nlm_program_nae_parser_seq_fifo(sc->base, sc->max_ports, sc->portcfg); + nlm_xlpnae_print_frin_desc_carving(sc); bus_generic_probe(dev); bus_generic_attach(dev); diff --git a/sys/mips/nlm/dev/net/xlpge.h b/sys/mips/nlm/dev/net/xlpge.h index 883653d77eb..ccf9d124f4a 100644 --- a/sys/mips/nlm/dev/net/xlpge.h +++ b/sys/mips/nlm/dev/net/xlpge.h @@ -75,6 +75,9 @@ struct nlm_xlpnae_softc { /* NetIOR configs */ u_int cmplx_type[8]; /* XXXJC: redundant? */ struct nae_port_config *portcfg; + u_int blockmask; + u_int portmask[XLP_NAE_NBLOCKS]; + u_int ilmask; u_int xauimask; u_int sgmiimask; u_int hw_parser_en; diff --git a/sys/mips/nlm/hal/nae.h b/sys/mips/nlm/hal/nae.h index 0738bc72dc2..5ebddca06fc 100644 --- a/sys/mips/nlm/hal/nae.h +++ b/sys/mips/nlm/hal/nae.h @@ -473,6 +473,9 @@ #define XLP_MAX_PORTS 18 #define XLP_STORM_MAX_PORTS 8 +#define MAX_FREE_FIFO_POOL_8XX 20 +#define MAX_FREE_FIFO_POOL_3XX 9 + #if !defined(LOCORE) && !defined(__ASSEMBLY__) #define nlm_read_nae_reg(b, r) nlm_read_reg_xkphys(b, r) @@ -494,6 +497,7 @@ enum XLPNAE_TX_TYPE { }; enum nblock_type { + UNKNOWN = 0, /* DONT MAKE IT NON-ZERO */ SGMIIC = 1, XAUIC = 2, ILC = 3 @@ -550,6 +554,12 @@ nae_num_context(uint64_t nae_pcibase) /* per port config structure */ struct nae_port_config { + int node; /* node id (quickread) */ + int block; /* network block id (quickread) */ + int port; /* port id - among the 18 in XLP */ + int type; /* port type - see xlp_gmac_port_types */ + int mdio_bus; + int phy_addr; int num_channels; int num_free_descs; int free_desc_sizes; @@ -605,7 +615,7 @@ void nlm_setup_flow_crc_poly(uint64_t, uint32_t); void nlm_setup_iface_fifo_cfg(uint64_t, int, struct nae_port_config *); void nlm_setup_rx_base_config(uint64_t, int, struct nae_port_config *); void nlm_setup_rx_buf_config(uint64_t, int, struct nae_port_config *); -void nlm_setup_freein_fifo_cfg(uint64_t, int, struct nae_port_config *); +void nlm_setup_freein_fifo_cfg(uint64_t, struct nae_port_config *); int nlm_get_flow_mask(int); void nlm_program_flow_cfg(uint64_t, int, uint32_t, uint32_t); void xlp_ax_nae_lane_reset_txpll(uint64_t, int, int, int); diff --git a/sys/mips/nlm/hal/nlm_hal.c b/sys/mips/nlm/hal/nlm_hal.c index 00b79dd713b..fa4287e5def 100644 --- a/sys/mips/nlm/hal/nlm_hal.c +++ b/sys/mips/nlm/hal/nlm_hal.c @@ -75,7 +75,10 @@ nlm_get_device_frequency(uint64_t sysbase, int devtype) dfsdiv = ((div_val >> (devtype << 2)) & 0xf) + 1; spf = (pllctrl >> 3 & 0x7f) + 1; spr = (pllctrl >> 1 & 0x03) + 1; - extra_div = nlm_is_xlp8xx_ax() ? 1 : 2; + if (devtype == DFS_DEVICE_NAE && !nlm_is_xlp8xx_ax()) + extra_div = 2; + else + extra_div = 1; return ((400 * spf) / (3 * extra_div * spr * dfsdiv)); } diff --git a/sys/mips/nlm/hal/sys.h b/sys/mips/nlm/hal/sys.h index 2092d37d299..078e63d6912 100644 --- a/sys/mips/nlm/hal/sys.h +++ b/sys/mips/nlm/hal/sys.h @@ -95,9 +95,11 @@ #define SYS_UCO_S_ECC 0x38 #define SYS_UCO_M_ECC 0x39 #define SYS_UCO_ADDR 0x3a +#define SYS_PLL_DFS_BYP_CTRL 0x3a /* Bx stepping */ #define SYS_UCO_INSTR 0x3b #define SYS_MEM_BIST0 0x3c #define SYS_MEM_BIST1 0x3d +#define SYS_PLL_DFS_DIV_VALUE 0x3d /* Bx stepping */ #define SYS_MEM_BIST2 0x3e #define SYS_MEM_BIST3 0x3f #define SYS_MEM_BIST4 0x40 diff --git a/sys/mips/sibyte/sb_zbpci.c b/sys/mips/sibyte/sb_zbpci.c index f4df353c737..7852a41b6a1 100644 --- a/sys/mips/sibyte/sb_zbpci.c +++ b/sys/mips/sibyte/sb_zbpci.c @@ -137,7 +137,7 @@ zbpci_attach(device_t dev) /* * Allocate KVA for accessing PCI config space. */ - va = kmem_alloc_nofault(kernel_map, PAGE_SIZE * mp_ncpus); + va = kva_alloc(PAGE_SIZE * mp_ncpus); if (va == 0) { device_printf(dev, "Cannot allocate virtual addresses for " "config space access.\n"); diff --git a/sys/modules/Makefile b/sys/modules/Makefile index bd263e9a917..2161b8b407a 100644 --- a/sys/modules/Makefile +++ b/sys/modules/Makefile @@ -126,6 +126,7 @@ SUBDIR= \ ${_hptnr} \ ${_hptrr} \ hwpmc \ + ${_hyperv} \ ${_i2c} \ ${_ibcs2} \ ${_ichwd} \ @@ -160,6 +161,7 @@ SUBDIR= \ ${_ipwfw} \ ${_isci} \ iscsi \ + iscsi_initiator \ isp \ ${_ispfw} \ ${_iwi} \ @@ -342,6 +344,7 @@ SUBDIR= \ ${_viawd} \ vkbd \ ${_vmm} \ + ${_vmware} \ ${_vpo} \ vr \ vte \ @@ -368,6 +371,7 @@ SUBDIR= \ .if ${MACHINE_CPUARCH} == "i386" || ${MACHINE_CPUARCH} == "amd64" _filemon= filemon +_vmware= vmware .endif .if ${MACHINE_CPUARCH} != "powerpc" && ${MACHINE_CPUARCH} != "arm" && \ @@ -669,6 +673,7 @@ _hptmv= hptmv _hptnr= hptnr _hptrr= hptrr .endif +_hyperv= hyperv _i2c= i2c _ichwd= ichwd _ida= ida diff --git a/sys/modules/aesni/Makefile b/sys/modules/aesni/Makefile index 9e25a4684f5..26dbedc960e 100644 --- a/sys/modules/aesni/Makefile +++ b/sys/modules/aesni/Makefile @@ -3,8 +3,17 @@ .PATH: ${.CURDIR}/../../crypto/aesni KMOD= aesni -SRCS= aesni.c aesni_wrap.c -SRCS+= aesencdec_${MACHINE_CPUARCH}.S aeskeys_${MACHINE_CPUARCH}.S +SRCS= aesni.c +SRCS+= aeskeys_${MACHINE_CPUARCH}.S SRCS+= device_if.h bus_if.h opt_bus.h cryptodev_if.h +OBJS+= aesni_wrap.o + +# Remove -nostdinc so we can get the intrinsics. +aesni_wrap.o: aesni_wrap.c + ${CC} -c ${CFLAGS:C/^-O2$/-O3/:N-nostdinc} ${WERROR} ${PROF} \ + -mmmx -msse -maes ${.IMPSRC} + ${CTFCONVERT_CMD} + .include + diff --git a/sys/modules/cam/Makefile b/sys/modules/cam/Makefile index c2dec724d9b..c3ecf56af8f 100644 --- a/sys/modules/cam/Makefile +++ b/sys/modules/cam/Makefile @@ -13,6 +13,7 @@ SRCS+= opt_scsi.h SRCS+= opt_cd.h SRCS+= opt_pt.h SRCS+= opt_sa.h +SRCS+= opt_ses.h SRCS+= device_if.h bus_if.h vnode_if.h SRCS+= cam.c SRCS+= cam_compat.c diff --git a/sys/modules/ctl/Makefile b/sys/modules/ctl/Makefile index 5744b7d08cc..39a61f612ef 100644 --- a/sys/modules/ctl/Makefile +++ b/sys/modules/ctl/Makefile @@ -12,6 +12,7 @@ SRCS+= ctl_cmd_table.c SRCS+= ctl_frontend.c SRCS+= ctl_frontend_cam_sim.c SRCS+= ctl_frontend_internal.c +SRCS+= ctl_frontend_iscsi.c SRCS+= ctl_mem_pool.c SRCS+= ctl_scsi_all.c SRCS+= ctl_error.c @@ -23,4 +24,6 @@ SRCS+= vnode_if.h SRCS+= opt_cam.h SRCS+= opt_kdtrace.h +#CFLAGS+=-DICL_KERNEL_PROXY + .include diff --git a/sys/modules/cxgb/cxgb/Makefile b/sys/modules/cxgb/cxgb/Makefile index 8807eced4f2..d24ff08da46 100644 --- a/sys/modules/cxgb/cxgb/Makefile +++ b/sys/modules/cxgb/cxgb/Makefile @@ -10,7 +10,7 @@ SRCS= cxgb_mc5.c cxgb_vsc8211.c cxgb_ael1002.c cxgb_mv88e1xxx.c SRCS+= cxgb_xgmac.c cxgb_vsc7323.c cxgb_t3_hw.c cxgb_main.c cxgb_aq100x.c SRCS+= cxgb_sge.c cxgb_tn1010.c SRCS+= device_if.h bus_if.h pci_if.h -SRCS+= opt_inet.h opt_inet6.h opt_zero.h opt_sched.h +SRCS+= opt_inet.h opt_inet6.h opt_sched.h SRCS+= uipc_mvec.c CFLAGS+= -g -DDEFAULT_JUMBO -I${CXGB} diff --git a/sys/modules/drm2/Makefile b/sys/modules/drm2/Makefile index f8411e3df4a..62414dbd978 100644 --- a/sys/modules/drm2/Makefile +++ b/sys/modules/drm2/Makefile @@ -2,8 +2,26 @@ .include +.if ${MACHINE_CPUARCH} == "amd64" +_radeonkms= radeonkms +. if ${MK_SOURCELESS_UCODE} != "no" +_radeonkmsfw= radeonkmsfw +. endif +.endif + +.if ${MACHINE_CPUARCH} == "i386" +. if ${MACHINE} != "pc98" +_radeonkms= radeonkms +. if ${MK_SOURCELESS_UCODE} != "no" +_radeonkmsfw= radeonkmsfw +. endif +. endif +.endif + SUBDIR = \ drm2 \ - i915kms + i915kms \ + ${_radeonkms} \ + ${_radeonkmsfw} .include diff --git a/sys/modules/drm2/drm2/Makefile b/sys/modules/drm2/drm2/Makefile index 215a0ed51e5..acb2d331156 100644 --- a/sys/modules/drm2/drm2/Makefile +++ b/sys/modules/drm2/drm2/Makefile @@ -6,10 +6,12 @@ SRCS = \ drm_agpsupport.c \ drm_auth.c \ drm_bufs.c \ + drm_buffer.c \ drm_context.c \ drm_crtc.c \ drm_crtc_helper.c \ drm_dma.c \ + drm_dp_helper.c \ drm_dp_iic_helper.c \ drm_drawable.c \ drm_drv.c \ @@ -42,7 +44,8 @@ SRCS = \ ttm_execbuf_util.c \ ttm_memory.c \ ttm_page_alloc.c \ - ttm_bo_vm.c + ttm_bo_vm.c \ + ati_pcigart.c #ttm_agp_backend.c #ttm_page_alloc_dma.c diff --git a/sys/modules/drm2/radeonkms/Makefile b/sys/modules/drm2/radeonkms/Makefile new file mode 100644 index 00000000000..f8468f60e17 --- /dev/null +++ b/sys/modules/drm2/radeonkms/Makefile @@ -0,0 +1,107 @@ +# $FreeBSD$ + +.PATH: ${.CURDIR}/../../../dev/drm2/radeon + +KMOD = radeonkms +SRCS = \ + rn50_reg_safe.h \ + r100_reg_safe.h \ + r200_reg_safe.h \ + rv515_reg_safe.h \ + r300_reg_safe.h \ + r420_reg_safe.h \ + rs600_reg_safe.h \ + r600_reg_safe.h \ + evergreen_reg_safe.h \ + cayman_reg_safe.h +SRCS += \ + radeon_acpi.c \ + radeon_agp.c \ + radeon_asic.c \ + radeon_atombios.c \ + radeon_atpx_handler.c \ + radeon_benchmark.c \ + radeon_bios.c \ + radeon_clocks.c \ + radeon_combios.c \ + radeon_connectors.c \ + radeon_cp.c \ + radeon_cs.c \ + radeon_cursor.c \ + radeon_device.c \ + radeon_display.c \ + radeon_drv.c \ + radeon_encoders.c \ + radeon_fb.c \ + radeon_fence.c \ + radeon_gart.c \ + radeon_gem.c \ + radeon_i2c.c \ + radeon_irq.c \ + radeon_irq_kms.c \ + radeon_kms.c \ + radeon_legacy_crtc.c \ + radeon_legacy_encoders.c \ + radeon_legacy_tv.c \ + radeon_mem.c \ + radeon_object.c \ + radeon_pm.c \ + radeon_ring.c \ + radeon_sa.c \ + radeon_semaphore.c \ + radeon_state.c \ + radeon_test.c \ + radeon_ttm.c \ + atom.c \ + atombios_crtc.c \ + atombios_dp.c \ + atombios_encoders.c \ + atombios_i2c.c \ + r100.c \ + r200.c \ + r300.c \ + r300_cmdbuf.c \ + r420.c \ + rs400.c \ + rs600.c \ + rs690.c \ + rv515.c \ + r520.c \ + r600.c \ + r600_audio.c \ + r600_blit.c \ + r600_blit_kms.c \ + r600_blit_shaders.c \ + r600_cp.c \ + r600_cs.c \ + r600_hdmi.c \ + rv770.c \ + evergreen.c \ + evergreen_blit_kms.c \ + evergreen_blit_shaders.c \ + evergreen_cs.c \ + evergreen_hdmi.c \ + cayman_blit_shaders.c \ + ni.c \ + si.c \ + si_blit_shaders.c + +#radeon_ioc32.c +#radeon_prime.c +#--radeon_trace_points.c + +SRCS += \ + opt_acpi.h \ + opt_compat.h \ + opt_drm.h \ + acpi_if.h \ + bus_if.h \ + device_if.h \ + iicbb_if.h \ + iicbus_if.h \ + pci_if.h + +CFLAGS += -I${.CURDIR}/../../../dev/drm2/radeon \ + -fms-extensions + +.include diff --git a/sys/modules/drm2/radeonkmsfw/ARUBA_me/Makefile b/sys/modules/drm2/radeonkmsfw/ARUBA_me/Makefile new file mode 100644 index 00000000000..a73cf0689df --- /dev/null +++ b/sys/modules/drm2/radeonkmsfw/ARUBA_me/Makefile @@ -0,0 +1,6 @@ +# $FreeBSD$ + +KMOD= radeonkmsfw_ARUBA_me +IMG= ARUBA_me + +.include diff --git a/sys/modules/drm2/radeonkmsfw/ARUBA_pfp/Makefile b/sys/modules/drm2/radeonkmsfw/ARUBA_pfp/Makefile new file mode 100644 index 00000000000..83c2d2116bd --- /dev/null +++ b/sys/modules/drm2/radeonkmsfw/ARUBA_pfp/Makefile @@ -0,0 +1,6 @@ +# $FreeBSD$ + +KMOD= radeonkmsfw_ARUBA_pfp +IMG= ARUBA_pfp + +.include diff --git a/sys/modules/drm2/radeonkmsfw/ARUBA_rlc/Makefile b/sys/modules/drm2/radeonkmsfw/ARUBA_rlc/Makefile new file mode 100644 index 00000000000..95cafc2d0ce --- /dev/null +++ b/sys/modules/drm2/radeonkmsfw/ARUBA_rlc/Makefile @@ -0,0 +1,6 @@ +# $FreeBSD$ + +KMOD= radeonkmsfw_ARUBA_rlc +IMG= ARUBA_rlc + +.include diff --git a/sys/modules/drm2/radeonkmsfw/BARTS_mc/Makefile b/sys/modules/drm2/radeonkmsfw/BARTS_mc/Makefile new file mode 100644 index 00000000000..d8c530a5b22 --- /dev/null +++ b/sys/modules/drm2/radeonkmsfw/BARTS_mc/Makefile @@ -0,0 +1,6 @@ +# $FreeBSD$ + +KMOD= radeonkmsfw_BARTS_mc +IMG= BARTS_mc + +.include diff --git a/sys/modules/drm2/radeonkmsfw/BARTS_me/Makefile b/sys/modules/drm2/radeonkmsfw/BARTS_me/Makefile new file mode 100644 index 00000000000..86da68820e7 --- /dev/null +++ b/sys/modules/drm2/radeonkmsfw/BARTS_me/Makefile @@ -0,0 +1,6 @@ +# $FreeBSD$ + +KMOD= radeonkmsfw_BARTS_me +IMG= BARTS_me + +.include diff --git a/sys/modules/drm2/radeonkmsfw/BARTS_pfp/Makefile b/sys/modules/drm2/radeonkmsfw/BARTS_pfp/Makefile new file mode 100644 index 00000000000..690cd3240ea --- /dev/null +++ b/sys/modules/drm2/radeonkmsfw/BARTS_pfp/Makefile @@ -0,0 +1,6 @@ +# $FreeBSD$ + +KMOD= radeonkmsfw_BARTS_pfp +IMG= BARTS_pfp + +.include diff --git a/sys/modules/drm2/radeonkmsfw/BTC_rlc/Makefile b/sys/modules/drm2/radeonkmsfw/BTC_rlc/Makefile new file mode 100644 index 00000000000..900a2e959e0 --- /dev/null +++ b/sys/modules/drm2/radeonkmsfw/BTC_rlc/Makefile @@ -0,0 +1,6 @@ +# $FreeBSD$ + +KMOD= radeonkmsfw_BTC_rlc +IMG= BTC_rlc + +.include diff --git a/sys/modules/drm2/radeonkmsfw/CAICOS_mc/Makefile b/sys/modules/drm2/radeonkmsfw/CAICOS_mc/Makefile new file mode 100644 index 00000000000..9b0fe6692da --- /dev/null +++ b/sys/modules/drm2/radeonkmsfw/CAICOS_mc/Makefile @@ -0,0 +1,6 @@ +# $FreeBSD$ + +KMOD= radeonkmsfw_CAICOS_mc +IMG= CAICOS_mc + +.include diff --git a/sys/modules/drm2/radeonkmsfw/CAICOS_me/Makefile b/sys/modules/drm2/radeonkmsfw/CAICOS_me/Makefile new file mode 100644 index 00000000000..c3a167e6286 --- /dev/null +++ b/sys/modules/drm2/radeonkmsfw/CAICOS_me/Makefile @@ -0,0 +1,6 @@ +# $FreeBSD$ + +KMOD= radeonkmsfw_CAICOS_me +IMG= CAICOS_me + +.include diff --git a/sys/modules/drm2/radeonkmsfw/CAICOS_pfp/Makefile b/sys/modules/drm2/radeonkmsfw/CAICOS_pfp/Makefile new file mode 100644 index 00000000000..bbee7aa0b14 --- /dev/null +++ b/sys/modules/drm2/radeonkmsfw/CAICOS_pfp/Makefile @@ -0,0 +1,6 @@ +# $FreeBSD$ + +KMOD= radeonkmsfw_CAICOS_pfp +IMG= CAICOS_pfp + +.include diff --git a/sys/modules/drm2/radeonkmsfw/CAYMAN_mc/Makefile b/sys/modules/drm2/radeonkmsfw/CAYMAN_mc/Makefile new file mode 100644 index 00000000000..98a4a9a7cb8 --- /dev/null +++ b/sys/modules/drm2/radeonkmsfw/CAYMAN_mc/Makefile @@ -0,0 +1,6 @@ +# $FreeBSD$ + +KMOD= radeonkmsfw_CAYMAN_mc +IMG= CAYMAN_mc + +.include diff --git a/sys/modules/drm2/radeonkmsfw/CAYMAN_me/Makefile b/sys/modules/drm2/radeonkmsfw/CAYMAN_me/Makefile new file mode 100644 index 00000000000..8f2c708ce51 --- /dev/null +++ b/sys/modules/drm2/radeonkmsfw/CAYMAN_me/Makefile @@ -0,0 +1,6 @@ +# $FreeBSD$ + +KMOD= radeonkmsfw_CAYMAN_me +IMG= CAYMAN_me + +.include diff --git a/sys/modules/drm2/radeonkmsfw/CAYMAN_pfp/Makefile b/sys/modules/drm2/radeonkmsfw/CAYMAN_pfp/Makefile new file mode 100644 index 00000000000..6c9adfa0735 --- /dev/null +++ b/sys/modules/drm2/radeonkmsfw/CAYMAN_pfp/Makefile @@ -0,0 +1,6 @@ +# $FreeBSD$ + +KMOD= radeonkmsfw_CAYMAN_pfp +IMG= CAYMAN_pfp + +.include diff --git a/sys/modules/drm2/radeonkmsfw/CAYMAN_rlc/Makefile b/sys/modules/drm2/radeonkmsfw/CAYMAN_rlc/Makefile new file mode 100644 index 00000000000..9f8fc80797b --- /dev/null +++ b/sys/modules/drm2/radeonkmsfw/CAYMAN_rlc/Makefile @@ -0,0 +1,6 @@ +# $FreeBSD$ + +KMOD= radeonkmsfw_CAYMAN_rlc +IMG= CAYMAN_rlc + +.include diff --git a/sys/modules/drm2/radeonkmsfw/CEDAR_me/Makefile b/sys/modules/drm2/radeonkmsfw/CEDAR_me/Makefile new file mode 100644 index 00000000000..d693d79f02a --- /dev/null +++ b/sys/modules/drm2/radeonkmsfw/CEDAR_me/Makefile @@ -0,0 +1,6 @@ +# $FreeBSD$ + +KMOD= radeonkmsfw_CEDAR_me +IMG= CEDAR_me + +.include diff --git a/sys/modules/drm2/radeonkmsfw/CEDAR_pfp/Makefile b/sys/modules/drm2/radeonkmsfw/CEDAR_pfp/Makefile new file mode 100644 index 00000000000..cef6073f757 --- /dev/null +++ b/sys/modules/drm2/radeonkmsfw/CEDAR_pfp/Makefile @@ -0,0 +1,6 @@ +# $FreeBSD$ + +KMOD= radeonkmsfw_CEDAR_pfp +IMG= CEDAR_pfp + +.include diff --git a/sys/modules/drm2/radeonkmsfw/CEDAR_rlc/Makefile b/sys/modules/drm2/radeonkmsfw/CEDAR_rlc/Makefile new file mode 100644 index 00000000000..d316e467d18 --- /dev/null +++ b/sys/modules/drm2/radeonkmsfw/CEDAR_rlc/Makefile @@ -0,0 +1,6 @@ +# $FreeBSD$ + +KMOD= radeonkmsfw_CEDAR_rlc +IMG= CEDAR_rlc + +.include diff --git a/sys/modules/drm2/radeonkmsfw/CYPRESS_me/Makefile b/sys/modules/drm2/radeonkmsfw/CYPRESS_me/Makefile new file mode 100644 index 00000000000..78fe6c663e8 --- /dev/null +++ b/sys/modules/drm2/radeonkmsfw/CYPRESS_me/Makefile @@ -0,0 +1,6 @@ +# $FreeBSD$ + +KMOD= radeonkmsfw_CYPRESS_me +IMG= CYPRESS_me + +.include diff --git a/sys/modules/drm2/radeonkmsfw/CYPRESS_pfp/Makefile b/sys/modules/drm2/radeonkmsfw/CYPRESS_pfp/Makefile new file mode 100644 index 00000000000..7b3a6aa7d41 --- /dev/null +++ b/sys/modules/drm2/radeonkmsfw/CYPRESS_pfp/Makefile @@ -0,0 +1,6 @@ +# $FreeBSD$ + +KMOD= radeonkmsfw_CYPRESS_pfp +IMG= CYPRESS_pfp + +.include diff --git a/sys/modules/drm2/radeonkmsfw/CYPRESS_rlc/Makefile b/sys/modules/drm2/radeonkmsfw/CYPRESS_rlc/Makefile new file mode 100644 index 00000000000..a8475b071b4 --- /dev/null +++ b/sys/modules/drm2/radeonkmsfw/CYPRESS_rlc/Makefile @@ -0,0 +1,6 @@ +# $FreeBSD$ + +KMOD= radeonkmsfw_CYPRESS_rlc +IMG= CYPRESS_rlc + +.include diff --git a/sys/modules/drm2/radeonkmsfw/CYPRESS_uvd/Makefile b/sys/modules/drm2/radeonkmsfw/CYPRESS_uvd/Makefile new file mode 100644 index 00000000000..3779a09439a --- /dev/null +++ b/sys/modules/drm2/radeonkmsfw/CYPRESS_uvd/Makefile @@ -0,0 +1,6 @@ +# $FreeBSD$ + +KMOD= radeonkmsfw_CYPRESS_uvd +IMG= CYPRESS_uvd + +.include diff --git a/sys/modules/drm2/radeonkmsfw/HAINAN_ce/Makefile b/sys/modules/drm2/radeonkmsfw/HAINAN_ce/Makefile new file mode 100644 index 00000000000..1aad7cf7d1e --- /dev/null +++ b/sys/modules/drm2/radeonkmsfw/HAINAN_ce/Makefile @@ -0,0 +1,6 @@ +# $FreeBSD$ + +KMOD= radeonkmsfw_HAINAN_ce +IMG= HAINAN_ce + +.include diff --git a/sys/modules/drm2/radeonkmsfw/HAINAN_mc/Makefile b/sys/modules/drm2/radeonkmsfw/HAINAN_mc/Makefile new file mode 100644 index 00000000000..54727af4244 --- /dev/null +++ b/sys/modules/drm2/radeonkmsfw/HAINAN_mc/Makefile @@ -0,0 +1,6 @@ +# $FreeBSD$ + +KMOD= radeonkmsfw_HAINAN_mc +IMG= HAINAN_mc + +.include diff --git a/sys/modules/drm2/radeonkmsfw/HAINAN_me/Makefile b/sys/modules/drm2/radeonkmsfw/HAINAN_me/Makefile new file mode 100644 index 00000000000..6d065c0ec08 --- /dev/null +++ b/sys/modules/drm2/radeonkmsfw/HAINAN_me/Makefile @@ -0,0 +1,6 @@ +# $FreeBSD$ + +KMOD= radeonkmsfw_HAINAN_me +IMG= HAINAN_me + +.include diff --git a/sys/modules/drm2/radeonkmsfw/HAINAN_pfp/Makefile b/sys/modules/drm2/radeonkmsfw/HAINAN_pfp/Makefile new file mode 100644 index 00000000000..ccf883413c8 --- /dev/null +++ b/sys/modules/drm2/radeonkmsfw/HAINAN_pfp/Makefile @@ -0,0 +1,6 @@ +# $FreeBSD$ + +KMOD= radeonkmsfw_HAINAN_pfp +IMG= HAINAN_pfp + +.include diff --git a/sys/modules/drm2/radeonkmsfw/HAINAN_rlc/Makefile b/sys/modules/drm2/radeonkmsfw/HAINAN_rlc/Makefile new file mode 100644 index 00000000000..a930fe0b654 --- /dev/null +++ b/sys/modules/drm2/radeonkmsfw/HAINAN_rlc/Makefile @@ -0,0 +1,6 @@ +# $FreeBSD$ + +KMOD= radeonkmsfw_HAINAN_rlc +IMG= HAINAN_rlc + +.include diff --git a/sys/modules/drm2/radeonkmsfw/JUNIPER_me/Makefile b/sys/modules/drm2/radeonkmsfw/JUNIPER_me/Makefile new file mode 100644 index 00000000000..1d10e666619 --- /dev/null +++ b/sys/modules/drm2/radeonkmsfw/JUNIPER_me/Makefile @@ -0,0 +1,6 @@ +# $FreeBSD$ + +KMOD= radeonkmsfw_JUNIPER_me +IMG= JUNIPER_me + +.include diff --git a/sys/modules/drm2/radeonkmsfw/JUNIPER_pfp/Makefile b/sys/modules/drm2/radeonkmsfw/JUNIPER_pfp/Makefile new file mode 100644 index 00000000000..ef53b1b5b34 --- /dev/null +++ b/sys/modules/drm2/radeonkmsfw/JUNIPER_pfp/Makefile @@ -0,0 +1,6 @@ +# $FreeBSD$ + +KMOD= radeonkmsfw_JUNIPER_pfp +IMG= JUNIPER_pfp + +.include diff --git a/sys/modules/drm2/radeonkmsfw/JUNIPER_rlc/Makefile b/sys/modules/drm2/radeonkmsfw/JUNIPER_rlc/Makefile new file mode 100644 index 00000000000..3fa7388267e --- /dev/null +++ b/sys/modules/drm2/radeonkmsfw/JUNIPER_rlc/Makefile @@ -0,0 +1,6 @@ +# $FreeBSD$ + +KMOD= radeonkmsfw_JUNIPER_rlc +IMG= JUNIPER_rlc + +.include diff --git a/sys/modules/drm2/radeonkmsfw/Makefile b/sys/modules/drm2/radeonkmsfw/Makefile new file mode 100644 index 00000000000..167743c96e7 --- /dev/null +++ b/sys/modules/drm2/radeonkmsfw/Makefile @@ -0,0 +1,85 @@ +# $FreeBSD$ + +SUBDIR= \ + ARUBA_me \ + ARUBA_pfp \ + ARUBA_rlc \ + BARTS_mc \ + BARTS_me \ + BARTS_pfp \ + BTC_rlc \ + CAICOS_mc \ + CAICOS_me \ + CAICOS_pfp \ + CAYMAN_mc \ + CAYMAN_me \ + CAYMAN_pfp \ + CAYMAN_rlc \ + CEDAR_me \ + CEDAR_pfp \ + CEDAR_rlc \ + CYPRESS_me \ + CYPRESS_pfp \ + CYPRESS_rlc \ + JUNIPER_me \ + JUNIPER_pfp \ + JUNIPER_rlc \ + PALM_me \ + PALM_pfp \ + PITCAIRN_ce \ + PITCAIRN_mc \ + PITCAIRN_me \ + PITCAIRN_pfp \ + PITCAIRN_rlc \ + R100_cp \ + R200_cp \ + R300_cp \ + R420_cp \ + R520_cp \ + R600_me \ + R600_pfp \ + R600_rlc \ + R700_rlc \ + REDWOOD_me \ + REDWOOD_pfp \ + REDWOOD_rlc \ + RS600_cp \ + RS690_cp \ + RS780_me \ + RS780_pfp \ + RV610_me \ + RV610_pfp \ + RV620_me \ + RV620_pfp \ + RV630_me \ + RV630_pfp \ + RV635_me \ + RV635_pfp \ + RV670_me \ + RV670_pfp \ + RV710_me \ + RV710_pfp \ + RV730_me \ + RV730_pfp \ + RV770_me \ + RV770_pfp \ + SUMO2_me \ + SUMO2_pfp \ + SUMO_me \ + SUMO_pfp \ + SUMO_rlc \ + TAHITI_ce \ + TAHITI_mc \ + TAHITI_me \ + TAHITI_pfp \ + TAHITI_rlc \ + TURKS_mc \ + TURKS_me \ + TURKS_pfp \ + VERDE_ce \ + VERDE_mc \ + VERDE_me \ + VERDE_pfp \ + VERDE_rlc + +.include diff --git a/sys/modules/drm2/radeonkmsfw/Makefile.inc b/sys/modules/drm2/radeonkmsfw/Makefile.inc new file mode 100644 index 00000000000..f6035a19f87 --- /dev/null +++ b/sys/modules/drm2/radeonkmsfw/Makefile.inc @@ -0,0 +1,18 @@ +# $FreeBSD$ +# +# Common rules for building firmware. Note this gets auto-included +# by the subdir Makefile's as a consequence of included bsd.kmod.mk. + +_FIRM= ${IMG}.bin + +CLEANFILES+= ${_FIRM} + +FIRMWS= ${_FIRM}:${KMOD} + +# +# Note that a license ack is not needed for iwn. +# +#FIRMWARE_LICENSE= + +${_FIRM}: ${.CURDIR}/../../../../contrib/dev/drm2/radeonkmsfw/${_FIRM}.uu + uudecode -p $? > ${.TARGET} diff --git a/sys/modules/drm2/radeonkmsfw/OLAND_ce/Makefile b/sys/modules/drm2/radeonkmsfw/OLAND_ce/Makefile new file mode 100644 index 00000000000..0de1de49830 --- /dev/null +++ b/sys/modules/drm2/radeonkmsfw/OLAND_ce/Makefile @@ -0,0 +1,6 @@ +# $FreeBSD$ + +KMOD= radeonkmsfw_OLAND_ce +IMG= OLAND_ce + +.include diff --git a/sys/modules/drm2/radeonkmsfw/OLAND_mc/Makefile b/sys/modules/drm2/radeonkmsfw/OLAND_mc/Makefile new file mode 100644 index 00000000000..161bfc0a9a4 --- /dev/null +++ b/sys/modules/drm2/radeonkmsfw/OLAND_mc/Makefile @@ -0,0 +1,6 @@ +# $FreeBSD$ + +KMOD= radeonkmsfw_OLAND_mc +IMG= OLAND_mc + +.include diff --git a/sys/modules/drm2/radeonkmsfw/OLAND_me/Makefile b/sys/modules/drm2/radeonkmsfw/OLAND_me/Makefile new file mode 100644 index 00000000000..fbe07cb9de8 --- /dev/null +++ b/sys/modules/drm2/radeonkmsfw/OLAND_me/Makefile @@ -0,0 +1,6 @@ +# $FreeBSD$ + +KMOD= radeonkmsfw_OLAND_me +IMG= OLAND_me + +.include diff --git a/sys/modules/drm2/radeonkmsfw/OLAND_pfp/Makefile b/sys/modules/drm2/radeonkmsfw/OLAND_pfp/Makefile new file mode 100644 index 00000000000..193d75347c9 --- /dev/null +++ b/sys/modules/drm2/radeonkmsfw/OLAND_pfp/Makefile @@ -0,0 +1,6 @@ +# $FreeBSD$ + +KMOD= radeonkmsfw_OLAND_pfp +IMG= OLAND_pfp + +.include diff --git a/sys/modules/drm2/radeonkmsfw/OLAND_rlc/Makefile b/sys/modules/drm2/radeonkmsfw/OLAND_rlc/Makefile new file mode 100644 index 00000000000..2490b348bc5 --- /dev/null +++ b/sys/modules/drm2/radeonkmsfw/OLAND_rlc/Makefile @@ -0,0 +1,6 @@ +# $FreeBSD$ + +KMOD= radeonkmsfw_OLAND_rlc +IMG= OLAND_rlc + +.include diff --git a/sys/modules/drm2/radeonkmsfw/PALM_me/Makefile b/sys/modules/drm2/radeonkmsfw/PALM_me/Makefile new file mode 100644 index 00000000000..f73e1b8d1eb --- /dev/null +++ b/sys/modules/drm2/radeonkmsfw/PALM_me/Makefile @@ -0,0 +1,6 @@ +# $FreeBSD$ + +KMOD= radeonkmsfw_PALM_me +IMG= PALM_me + +.include diff --git a/sys/modules/drm2/radeonkmsfw/PALM_pfp/Makefile b/sys/modules/drm2/radeonkmsfw/PALM_pfp/Makefile new file mode 100644 index 00000000000..2e5b2e625f2 --- /dev/null +++ b/sys/modules/drm2/radeonkmsfw/PALM_pfp/Makefile @@ -0,0 +1,6 @@ +# $FreeBSD$ + +KMOD= radeonkmsfw_PALM_pfp +IMG= PALM_pfp + +.include diff --git a/sys/modules/drm2/radeonkmsfw/PITCAIRN_ce/Makefile b/sys/modules/drm2/radeonkmsfw/PITCAIRN_ce/Makefile new file mode 100644 index 00000000000..101d9a4970c --- /dev/null +++ b/sys/modules/drm2/radeonkmsfw/PITCAIRN_ce/Makefile @@ -0,0 +1,6 @@ +# $FreeBSD$ + +KMOD= radeonkmsfw_PITCAIRN_ce +IMG= PITCAIRN_ce + +.include diff --git a/sys/modules/drm2/radeonkmsfw/PITCAIRN_mc/Makefile b/sys/modules/drm2/radeonkmsfw/PITCAIRN_mc/Makefile new file mode 100644 index 00000000000..f1a86924a62 --- /dev/null +++ b/sys/modules/drm2/radeonkmsfw/PITCAIRN_mc/Makefile @@ -0,0 +1,6 @@ +# $FreeBSD$ + +KMOD= radeonkmsfw_PITCAIRN_mc +IMG= PITCAIRN_mc + +.include diff --git a/sys/modules/drm2/radeonkmsfw/PITCAIRN_me/Makefile b/sys/modules/drm2/radeonkmsfw/PITCAIRN_me/Makefile new file mode 100644 index 00000000000..b41d40f55dc --- /dev/null +++ b/sys/modules/drm2/radeonkmsfw/PITCAIRN_me/Makefile @@ -0,0 +1,6 @@ +# $FreeBSD$ + +KMOD= radeonkmsfw_PITCAIRN_me +IMG= PITCAIRN_me + +.include diff --git a/sys/modules/drm2/radeonkmsfw/PITCAIRN_pfp/Makefile b/sys/modules/drm2/radeonkmsfw/PITCAIRN_pfp/Makefile new file mode 100644 index 00000000000..d60d8b15a06 --- /dev/null +++ b/sys/modules/drm2/radeonkmsfw/PITCAIRN_pfp/Makefile @@ -0,0 +1,6 @@ +# $FreeBSD$ + +KMOD= radeonkmsfw_PITCAIRN_pfp +IMG= PITCAIRN_pfp + +.include diff --git a/sys/modules/drm2/radeonkmsfw/PITCAIRN_rlc/Makefile b/sys/modules/drm2/radeonkmsfw/PITCAIRN_rlc/Makefile new file mode 100644 index 00000000000..ff9d7c7e082 --- /dev/null +++ b/sys/modules/drm2/radeonkmsfw/PITCAIRN_rlc/Makefile @@ -0,0 +1,6 @@ +# $FreeBSD$ + +KMOD= radeonkmsfw_PITCAIRN_rlc +IMG= PITCAIRN_rlc + +.include diff --git a/sys/modules/drm2/radeonkmsfw/R100_cp/Makefile b/sys/modules/drm2/radeonkmsfw/R100_cp/Makefile new file mode 100644 index 00000000000..f5407dd3e8f --- /dev/null +++ b/sys/modules/drm2/radeonkmsfw/R100_cp/Makefile @@ -0,0 +1,6 @@ +# $FreeBSD$ + +KMOD= radeonkmsfw_R100_cp +IMG= R100_cp + +.include diff --git a/sys/modules/drm2/radeonkmsfw/R200_cp/Makefile b/sys/modules/drm2/radeonkmsfw/R200_cp/Makefile new file mode 100644 index 00000000000..bdfc803fae4 --- /dev/null +++ b/sys/modules/drm2/radeonkmsfw/R200_cp/Makefile @@ -0,0 +1,6 @@ +# $FreeBSD$ + +KMOD= radeonkmsfw_R200_cp +IMG= R200_cp + +.include diff --git a/sys/modules/drm2/radeonkmsfw/R300_cp/Makefile b/sys/modules/drm2/radeonkmsfw/R300_cp/Makefile new file mode 100644 index 00000000000..41e984b7bee --- /dev/null +++ b/sys/modules/drm2/radeonkmsfw/R300_cp/Makefile @@ -0,0 +1,6 @@ +# $FreeBSD$ + +KMOD= radeonkmsfw_R300_cp +IMG= R300_cp + +.include diff --git a/sys/modules/drm2/radeonkmsfw/R420_cp/Makefile b/sys/modules/drm2/radeonkmsfw/R420_cp/Makefile new file mode 100644 index 00000000000..18e67884e63 --- /dev/null +++ b/sys/modules/drm2/radeonkmsfw/R420_cp/Makefile @@ -0,0 +1,6 @@ +# $FreeBSD$ + +KMOD= radeonkmsfw_R420_cp +IMG= R420_cp + +.include diff --git a/sys/modules/drm2/radeonkmsfw/R520_cp/Makefile b/sys/modules/drm2/radeonkmsfw/R520_cp/Makefile new file mode 100644 index 00000000000..484283dd9d5 --- /dev/null +++ b/sys/modules/drm2/radeonkmsfw/R520_cp/Makefile @@ -0,0 +1,6 @@ +# $FreeBSD$ + +KMOD= radeonkmsfw_R520_cp +IMG= R520_cp + +.include diff --git a/sys/modules/drm2/radeonkmsfw/R600_me/Makefile b/sys/modules/drm2/radeonkmsfw/R600_me/Makefile new file mode 100644 index 00000000000..e42d1cdf01e --- /dev/null +++ b/sys/modules/drm2/radeonkmsfw/R600_me/Makefile @@ -0,0 +1,6 @@ +# $FreeBSD$ + +KMOD= radeonkmsfw_R600_me +IMG= R600_me + +.include diff --git a/sys/modules/drm2/radeonkmsfw/R600_pfp/Makefile b/sys/modules/drm2/radeonkmsfw/R600_pfp/Makefile new file mode 100644 index 00000000000..0cd74c61c6c --- /dev/null +++ b/sys/modules/drm2/radeonkmsfw/R600_pfp/Makefile @@ -0,0 +1,6 @@ +# $FreeBSD$ + +KMOD= radeonkmsfw_R600_pfp +IMG= R600_pfp + +.include diff --git a/sys/modules/drm2/radeonkmsfw/R600_rlc/Makefile b/sys/modules/drm2/radeonkmsfw/R600_rlc/Makefile new file mode 100644 index 00000000000..6cb5acf3335 --- /dev/null +++ b/sys/modules/drm2/radeonkmsfw/R600_rlc/Makefile @@ -0,0 +1,6 @@ +# $FreeBSD$ + +KMOD= radeonkmsfw_R600_rlc +IMG= R600_rlc + +.include diff --git a/sys/modules/drm2/radeonkmsfw/R700_rlc/Makefile b/sys/modules/drm2/radeonkmsfw/R700_rlc/Makefile new file mode 100644 index 00000000000..c6853f18ea7 --- /dev/null +++ b/sys/modules/drm2/radeonkmsfw/R700_rlc/Makefile @@ -0,0 +1,6 @@ +# $FreeBSD$ + +KMOD= radeonkmsfw_R700_rlc +IMG= R700_rlc + +.include diff --git a/sys/modules/drm2/radeonkmsfw/REDWOOD_me/Makefile b/sys/modules/drm2/radeonkmsfw/REDWOOD_me/Makefile new file mode 100644 index 00000000000..84019ba7058 --- /dev/null +++ b/sys/modules/drm2/radeonkmsfw/REDWOOD_me/Makefile @@ -0,0 +1,6 @@ +# $FreeBSD$ + +KMOD= radeonkmsfw_REDWOOD_me +IMG= REDWOOD_me + +.include diff --git a/sys/modules/drm2/radeonkmsfw/REDWOOD_pfp/Makefile b/sys/modules/drm2/radeonkmsfw/REDWOOD_pfp/Makefile new file mode 100644 index 00000000000..f175171d36e --- /dev/null +++ b/sys/modules/drm2/radeonkmsfw/REDWOOD_pfp/Makefile @@ -0,0 +1,6 @@ +# $FreeBSD$ + +KMOD= radeonkmsfw_REDWOOD_pfp +IMG= REDWOOD_pfp + +.include diff --git a/sys/modules/drm2/radeonkmsfw/REDWOOD_rlc/Makefile b/sys/modules/drm2/radeonkmsfw/REDWOOD_rlc/Makefile new file mode 100644 index 00000000000..2727422f91b --- /dev/null +++ b/sys/modules/drm2/radeonkmsfw/REDWOOD_rlc/Makefile @@ -0,0 +1,6 @@ +# $FreeBSD$ + +KMOD= radeonkmsfw_REDWOOD_rlc +IMG= REDWOOD_rlc + +.include diff --git a/sys/modules/drm2/radeonkmsfw/RS600_cp/Makefile b/sys/modules/drm2/radeonkmsfw/RS600_cp/Makefile new file mode 100644 index 00000000000..d33f0e322c3 --- /dev/null +++ b/sys/modules/drm2/radeonkmsfw/RS600_cp/Makefile @@ -0,0 +1,6 @@ +# $FreeBSD$ + +KMOD= radeonkmsfw_RS600_cp +IMG= RS600_cp + +.include diff --git a/sys/modules/drm2/radeonkmsfw/RS690_cp/Makefile b/sys/modules/drm2/radeonkmsfw/RS690_cp/Makefile new file mode 100644 index 00000000000..e295ced8983 --- /dev/null +++ b/sys/modules/drm2/radeonkmsfw/RS690_cp/Makefile @@ -0,0 +1,6 @@ +# $FreeBSD$ + +KMOD= radeonkmsfw_RS690_cp +IMG= RS690_cp + +.include diff --git a/sys/modules/drm2/radeonkmsfw/RS780_me/Makefile b/sys/modules/drm2/radeonkmsfw/RS780_me/Makefile new file mode 100644 index 00000000000..413bf580365 --- /dev/null +++ b/sys/modules/drm2/radeonkmsfw/RS780_me/Makefile @@ -0,0 +1,6 @@ +# $FreeBSD$ + +KMOD= radeonkmsfw_RS780_me +IMG= RS780_me + +.include diff --git a/sys/modules/drm2/radeonkmsfw/RS780_pfp/Makefile b/sys/modules/drm2/radeonkmsfw/RS780_pfp/Makefile new file mode 100644 index 00000000000..c6eb278a767 --- /dev/null +++ b/sys/modules/drm2/radeonkmsfw/RS780_pfp/Makefile @@ -0,0 +1,6 @@ +# $FreeBSD$ + +KMOD= radeonkmsfw_RS780_pfp +IMG= RS780_pfp + +.include diff --git a/sys/modules/drm2/radeonkmsfw/RV610_me/Makefile b/sys/modules/drm2/radeonkmsfw/RV610_me/Makefile new file mode 100644 index 00000000000..c44f8415b79 --- /dev/null +++ b/sys/modules/drm2/radeonkmsfw/RV610_me/Makefile @@ -0,0 +1,6 @@ +# $FreeBSD$ + +KMOD= radeonkmsfw_RV610_me +IMG= RV610_me + +.include diff --git a/sys/modules/drm2/radeonkmsfw/RV610_pfp/Makefile b/sys/modules/drm2/radeonkmsfw/RV610_pfp/Makefile new file mode 100644 index 00000000000..e6419fd033a --- /dev/null +++ b/sys/modules/drm2/radeonkmsfw/RV610_pfp/Makefile @@ -0,0 +1,6 @@ +# $FreeBSD$ + +KMOD= radeonkmsfw_RV610_pfp +IMG= RV610_pfp + +.include diff --git a/sys/modules/drm2/radeonkmsfw/RV620_me/Makefile b/sys/modules/drm2/radeonkmsfw/RV620_me/Makefile new file mode 100644 index 00000000000..997e913d268 --- /dev/null +++ b/sys/modules/drm2/radeonkmsfw/RV620_me/Makefile @@ -0,0 +1,6 @@ +# $FreeBSD$ + +KMOD= radeonkmsfw_RV620_me +IMG= RV620_me + +.include diff --git a/sys/modules/drm2/radeonkmsfw/RV620_pfp/Makefile b/sys/modules/drm2/radeonkmsfw/RV620_pfp/Makefile new file mode 100644 index 00000000000..476e486e942 --- /dev/null +++ b/sys/modules/drm2/radeonkmsfw/RV620_pfp/Makefile @@ -0,0 +1,6 @@ +# $FreeBSD$ + +KMOD= radeonkmsfw_RV620_pfp +IMG= RV620_pfp + +.include diff --git a/sys/modules/drm2/radeonkmsfw/RV630_me/Makefile b/sys/modules/drm2/radeonkmsfw/RV630_me/Makefile new file mode 100644 index 00000000000..0807570c4ee --- /dev/null +++ b/sys/modules/drm2/radeonkmsfw/RV630_me/Makefile @@ -0,0 +1,6 @@ +# $FreeBSD$ + +KMOD= radeonkmsfw_RV630_me +IMG= RV630_me + +.include diff --git a/sys/modules/drm2/radeonkmsfw/RV630_pfp/Makefile b/sys/modules/drm2/radeonkmsfw/RV630_pfp/Makefile new file mode 100644 index 00000000000..38c66dc888a --- /dev/null +++ b/sys/modules/drm2/radeonkmsfw/RV630_pfp/Makefile @@ -0,0 +1,6 @@ +# $FreeBSD$ + +KMOD= radeonkmsfw_RV630_pfp +IMG= RV630_pfp + +.include diff --git a/sys/modules/drm2/radeonkmsfw/RV635_me/Makefile b/sys/modules/drm2/radeonkmsfw/RV635_me/Makefile new file mode 100644 index 00000000000..440508e72b7 --- /dev/null +++ b/sys/modules/drm2/radeonkmsfw/RV635_me/Makefile @@ -0,0 +1,6 @@ +# $FreeBSD$ + +KMOD= radeonkmsfw_RV635_me +IMG= RV635_me + +.include diff --git a/sys/modules/drm2/radeonkmsfw/RV635_pfp/Makefile b/sys/modules/drm2/radeonkmsfw/RV635_pfp/Makefile new file mode 100644 index 00000000000..01fc97b3604 --- /dev/null +++ b/sys/modules/drm2/radeonkmsfw/RV635_pfp/Makefile @@ -0,0 +1,6 @@ +# $FreeBSD$ + +KMOD= radeonkmsfw_RV635_pfp +IMG= RV635_pfp + +.include diff --git a/sys/modules/drm2/radeonkmsfw/RV670_me/Makefile b/sys/modules/drm2/radeonkmsfw/RV670_me/Makefile new file mode 100644 index 00000000000..3c13ed1dd43 --- /dev/null +++ b/sys/modules/drm2/radeonkmsfw/RV670_me/Makefile @@ -0,0 +1,6 @@ +# $FreeBSD$ + +KMOD= radeonkmsfw_RV670_me +IMG= RV670_me + +.include diff --git a/sys/modules/drm2/radeonkmsfw/RV670_pfp/Makefile b/sys/modules/drm2/radeonkmsfw/RV670_pfp/Makefile new file mode 100644 index 00000000000..50aee8d7e8f --- /dev/null +++ b/sys/modules/drm2/radeonkmsfw/RV670_pfp/Makefile @@ -0,0 +1,6 @@ +# $FreeBSD$ + +KMOD= radeonkmsfw_RV670_pfp +IMG= RV670_pfp + +.include diff --git a/sys/modules/drm2/radeonkmsfw/RV710_me/Makefile b/sys/modules/drm2/radeonkmsfw/RV710_me/Makefile new file mode 100644 index 00000000000..f42c7adc3cd --- /dev/null +++ b/sys/modules/drm2/radeonkmsfw/RV710_me/Makefile @@ -0,0 +1,6 @@ +# $FreeBSD$ + +KMOD= radeonkmsfw_RV710_me +IMG= RV710_me + +.include diff --git a/sys/modules/drm2/radeonkmsfw/RV710_pfp/Makefile b/sys/modules/drm2/radeonkmsfw/RV710_pfp/Makefile new file mode 100644 index 00000000000..5acaf3ffe4b --- /dev/null +++ b/sys/modules/drm2/radeonkmsfw/RV710_pfp/Makefile @@ -0,0 +1,6 @@ +# $FreeBSD$ + +KMOD= radeonkmsfw_RV710_pfp +IMG= RV710_pfp + +.include diff --git a/sys/modules/drm2/radeonkmsfw/RV710_uvd/Makefile b/sys/modules/drm2/radeonkmsfw/RV710_uvd/Makefile new file mode 100644 index 00000000000..8146bc2cd2a --- /dev/null +++ b/sys/modules/drm2/radeonkmsfw/RV710_uvd/Makefile @@ -0,0 +1,6 @@ +# $FreeBSD$ + +KMOD= radeonkmsfw_RV710_uvd +IMG= RV710_uvd + +.include diff --git a/sys/modules/drm2/radeonkmsfw/RV730_me/Makefile b/sys/modules/drm2/radeonkmsfw/RV730_me/Makefile new file mode 100644 index 00000000000..9a0907de02b --- /dev/null +++ b/sys/modules/drm2/radeonkmsfw/RV730_me/Makefile @@ -0,0 +1,6 @@ +# $FreeBSD$ + +KMOD= radeonkmsfw_RV730_me +IMG= RV730_me + +.include diff --git a/sys/modules/drm2/radeonkmsfw/RV730_pfp/Makefile b/sys/modules/drm2/radeonkmsfw/RV730_pfp/Makefile new file mode 100644 index 00000000000..0ecb70cb6d9 --- /dev/null +++ b/sys/modules/drm2/radeonkmsfw/RV730_pfp/Makefile @@ -0,0 +1,6 @@ +# $FreeBSD$ + +KMOD= radeonkmsfw_RV730_pfp +IMG= RV730_pfp + +.include diff --git a/sys/modules/drm2/radeonkmsfw/RV770_me/Makefile b/sys/modules/drm2/radeonkmsfw/RV770_me/Makefile new file mode 100644 index 00000000000..85852a175d9 --- /dev/null +++ b/sys/modules/drm2/radeonkmsfw/RV770_me/Makefile @@ -0,0 +1,6 @@ +# $FreeBSD$ + +KMOD= radeonkmsfw_RV770_me +IMG= RV770_me + +.include diff --git a/sys/modules/drm2/radeonkmsfw/RV770_pfp/Makefile b/sys/modules/drm2/radeonkmsfw/RV770_pfp/Makefile new file mode 100644 index 00000000000..2eebdfabb18 --- /dev/null +++ b/sys/modules/drm2/radeonkmsfw/RV770_pfp/Makefile @@ -0,0 +1,6 @@ +# $FreeBSD$ + +KMOD= radeonkmsfw_RV770_pfp +IMG= RV770_pfp + +.include diff --git a/sys/modules/drm2/radeonkmsfw/SUMO2_me/Makefile b/sys/modules/drm2/radeonkmsfw/SUMO2_me/Makefile new file mode 100644 index 00000000000..2d6091fb212 --- /dev/null +++ b/sys/modules/drm2/radeonkmsfw/SUMO2_me/Makefile @@ -0,0 +1,6 @@ +# $FreeBSD$ + +KMOD= radeonkmsfw_SUMO2_me +IMG= SUMO2_me + +.include diff --git a/sys/modules/drm2/radeonkmsfw/SUMO2_pfp/Makefile b/sys/modules/drm2/radeonkmsfw/SUMO2_pfp/Makefile new file mode 100644 index 00000000000..2b99a5f5e54 --- /dev/null +++ b/sys/modules/drm2/radeonkmsfw/SUMO2_pfp/Makefile @@ -0,0 +1,6 @@ +# $FreeBSD$ + +KMOD= radeonkmsfw_SUMO2_pfp +IMG= SUMO2_pfp + +.include diff --git a/sys/modules/drm2/radeonkmsfw/SUMO_me/Makefile b/sys/modules/drm2/radeonkmsfw/SUMO_me/Makefile new file mode 100644 index 00000000000..e050d3e7e18 --- /dev/null +++ b/sys/modules/drm2/radeonkmsfw/SUMO_me/Makefile @@ -0,0 +1,6 @@ +# $FreeBSD$ + +KMOD= radeonkmsfw_SUMO_me +IMG= SUMO_me + +.include diff --git a/sys/modules/drm2/radeonkmsfw/SUMO_pfp/Makefile b/sys/modules/drm2/radeonkmsfw/SUMO_pfp/Makefile new file mode 100644 index 00000000000..2fc1ac2744c --- /dev/null +++ b/sys/modules/drm2/radeonkmsfw/SUMO_pfp/Makefile @@ -0,0 +1,6 @@ +# $FreeBSD$ + +KMOD= radeonkmsfw_SUMO_pfp +IMG= SUMO_pfp + +.include diff --git a/sys/modules/drm2/radeonkmsfw/SUMO_rlc/Makefile b/sys/modules/drm2/radeonkmsfw/SUMO_rlc/Makefile new file mode 100644 index 00000000000..4864b1f74b1 --- /dev/null +++ b/sys/modules/drm2/radeonkmsfw/SUMO_rlc/Makefile @@ -0,0 +1,6 @@ +# $FreeBSD$ + +KMOD= radeonkmsfw_SUMO_rlc +IMG= SUMO_rlc + +.include diff --git a/sys/modules/drm2/radeonkmsfw/SUMO_uvd/Makefile b/sys/modules/drm2/radeonkmsfw/SUMO_uvd/Makefile new file mode 100644 index 00000000000..694b226b86c --- /dev/null +++ b/sys/modules/drm2/radeonkmsfw/SUMO_uvd/Makefile @@ -0,0 +1,6 @@ +# $FreeBSD$ + +KMOD= radeonkmsfw_SUMO_uvd +IMG= SUMO_uvd + +.include diff --git a/sys/modules/drm2/radeonkmsfw/TAHITI_ce/Makefile b/sys/modules/drm2/radeonkmsfw/TAHITI_ce/Makefile new file mode 100644 index 00000000000..8b757069668 --- /dev/null +++ b/sys/modules/drm2/radeonkmsfw/TAHITI_ce/Makefile @@ -0,0 +1,6 @@ +# $FreeBSD$ + +KMOD= radeonkmsfw_TAHITI_ce +IMG= TAHITI_ce + +.include diff --git a/sys/modules/drm2/radeonkmsfw/TAHITI_mc/Makefile b/sys/modules/drm2/radeonkmsfw/TAHITI_mc/Makefile new file mode 100644 index 00000000000..af9227f15a0 --- /dev/null +++ b/sys/modules/drm2/radeonkmsfw/TAHITI_mc/Makefile @@ -0,0 +1,6 @@ +# $FreeBSD$ + +KMOD= radeonkmsfw_TAHITI_mc +IMG= TAHITI_mc + +.include diff --git a/sys/modules/drm2/radeonkmsfw/TAHITI_me/Makefile b/sys/modules/drm2/radeonkmsfw/TAHITI_me/Makefile new file mode 100644 index 00000000000..b6da4a99db2 --- /dev/null +++ b/sys/modules/drm2/radeonkmsfw/TAHITI_me/Makefile @@ -0,0 +1,6 @@ +# $FreeBSD$ + +KMOD= radeonkmsfw_TAHITI_me +IMG= TAHITI_me + +.include diff --git a/sys/modules/drm2/radeonkmsfw/TAHITI_pfp/Makefile b/sys/modules/drm2/radeonkmsfw/TAHITI_pfp/Makefile new file mode 100644 index 00000000000..0c5322d191e --- /dev/null +++ b/sys/modules/drm2/radeonkmsfw/TAHITI_pfp/Makefile @@ -0,0 +1,6 @@ +# $FreeBSD$ + +KMOD= radeonkmsfw_TAHITI_pfp +IMG= TAHITI_pfp + +.include diff --git a/sys/modules/drm2/radeonkmsfw/TAHITI_rlc/Makefile b/sys/modules/drm2/radeonkmsfw/TAHITI_rlc/Makefile new file mode 100644 index 00000000000..8af425f5df4 --- /dev/null +++ b/sys/modules/drm2/radeonkmsfw/TAHITI_rlc/Makefile @@ -0,0 +1,6 @@ +# $FreeBSD$ + +KMOD= radeonkmsfw_TAHITI_rlc +IMG= TAHITI_rlc + +.include diff --git a/sys/modules/drm2/radeonkmsfw/TAHITI_uvd/Makefile b/sys/modules/drm2/radeonkmsfw/TAHITI_uvd/Makefile new file mode 100644 index 00000000000..89834bf16b1 --- /dev/null +++ b/sys/modules/drm2/radeonkmsfw/TAHITI_uvd/Makefile @@ -0,0 +1,6 @@ +# $FreeBSD$ + +KMOD= radeonkmsfw_TAHITI_uvd +IMG= TAHITI_uvd + +.include diff --git a/sys/modules/drm2/radeonkmsfw/TURKS_mc/Makefile b/sys/modules/drm2/radeonkmsfw/TURKS_mc/Makefile new file mode 100644 index 00000000000..b317c78d864 --- /dev/null +++ b/sys/modules/drm2/radeonkmsfw/TURKS_mc/Makefile @@ -0,0 +1,6 @@ +# $FreeBSD$ + +KMOD= radeonkmsfw_TURKS_mc +IMG= TURKS_mc + +.include diff --git a/sys/modules/drm2/radeonkmsfw/TURKS_me/Makefile b/sys/modules/drm2/radeonkmsfw/TURKS_me/Makefile new file mode 100644 index 00000000000..e7c616cc5a5 --- /dev/null +++ b/sys/modules/drm2/radeonkmsfw/TURKS_me/Makefile @@ -0,0 +1,6 @@ +# $FreeBSD$ + +KMOD= radeonkmsfw_TURKS_me +IMG= TURKS_me + +.include diff --git a/sys/modules/drm2/radeonkmsfw/TURKS_pfp/Makefile b/sys/modules/drm2/radeonkmsfw/TURKS_pfp/Makefile new file mode 100644 index 00000000000..e42ce25f351 --- /dev/null +++ b/sys/modules/drm2/radeonkmsfw/TURKS_pfp/Makefile @@ -0,0 +1,6 @@ +# $FreeBSD$ + +KMOD= radeonkmsfw_TURKS_pfp +IMG= TURKS_pfp + +.include diff --git a/sys/modules/drm2/radeonkmsfw/VERDE_ce/Makefile b/sys/modules/drm2/radeonkmsfw/VERDE_ce/Makefile new file mode 100644 index 00000000000..d3b471f372a --- /dev/null +++ b/sys/modules/drm2/radeonkmsfw/VERDE_ce/Makefile @@ -0,0 +1,6 @@ +# $FreeBSD$ + +KMOD= radeonkmsfw_VERDE_ce +IMG= VERDE_ce + +.include diff --git a/sys/modules/drm2/radeonkmsfw/VERDE_mc/Makefile b/sys/modules/drm2/radeonkmsfw/VERDE_mc/Makefile new file mode 100644 index 00000000000..0f1b57cb646 --- /dev/null +++ b/sys/modules/drm2/radeonkmsfw/VERDE_mc/Makefile @@ -0,0 +1,6 @@ +# $FreeBSD$ + +KMOD= radeonkmsfw_VERDE_mc +IMG= VERDE_mc + +.include diff --git a/sys/modules/drm2/radeonkmsfw/VERDE_me/Makefile b/sys/modules/drm2/radeonkmsfw/VERDE_me/Makefile new file mode 100644 index 00000000000..a039c19a082 --- /dev/null +++ b/sys/modules/drm2/radeonkmsfw/VERDE_me/Makefile @@ -0,0 +1,6 @@ +# $FreeBSD$ + +KMOD= radeonkmsfw_VERDE_me +IMG= VERDE_me + +.include diff --git a/sys/modules/drm2/radeonkmsfw/VERDE_pfp/Makefile b/sys/modules/drm2/radeonkmsfw/VERDE_pfp/Makefile new file mode 100644 index 00000000000..4e73c0c7708 --- /dev/null +++ b/sys/modules/drm2/radeonkmsfw/VERDE_pfp/Makefile @@ -0,0 +1,6 @@ +# $FreeBSD$ + +KMOD= radeonkmsfw_VERDE_pfp +IMG= VERDE_pfp + +.include diff --git a/sys/modules/drm2/radeonkmsfw/VERDE_rlc/Makefile b/sys/modules/drm2/radeonkmsfw/VERDE_rlc/Makefile new file mode 100644 index 00000000000..a2e882a8af1 --- /dev/null +++ b/sys/modules/drm2/radeonkmsfw/VERDE_rlc/Makefile @@ -0,0 +1,6 @@ +# $FreeBSD$ + +KMOD= radeonkmsfw_VERDE_rlc +IMG= VERDE_rlc + +.include diff --git a/sys/modules/drm2/radeonkmsfw/gen-makefiles b/sys/modules/drm2/radeonkmsfw/gen-makefiles new file mode 100755 index 00000000000..4c364a763b0 --- /dev/null +++ b/sys/modules/drm2/radeonkmsfw/gen-makefiles @@ -0,0 +1,30 @@ +#!/bin/sh +# $FreeBSD$ + +set -e + +scriptdir=$(cd $(dirname $0) && pwd) +fwdir=$scriptdir/../../../contrib/dev/drm2/radeonkmsfw + +for dir in $scriptdir/*; do + if [ ! -d $dir ]; then + continue + fi + rm -rf $dir +done + +for file in $fwdir/*.uu; do + img=$(basename $file) + img=${img%.bin.uu} + echo "Image: $img" + + mkdir -p $scriptdir/$img + cat > $scriptdir/$img/Makefile < +EOF +done diff --git a/sys/modules/dtrace/Makefile b/sys/modules/dtrace/Makefile index f1f1f1eaed8..2fa7ecd4eae 100644 --- a/sys/modules/dtrace/Makefile +++ b/sys/modules/dtrace/Makefile @@ -22,7 +22,7 @@ SUBDIR+= fasttrap fbt systrace_linux32 .if ${MACHINE_CPUARCH} == "powerpc" SUBDIR+= fbt .endif -.if ${MACHINE_CPUARCH} == "amd64" +.if ${MACHINE_CPUARCH} == "amd64" || ${MACHINE_ARCH} == "powerpc64" SUBDIR+= systrace_freebsd32 .endif diff --git a/sys/modules/dtrace/sdt/Makefile b/sys/modules/dtrace/sdt/Makefile index 96605e11152..d50999a4e9a 100644 --- a/sys/modules/dtrace/sdt/Makefile +++ b/sys/modules/dtrace/sdt/Makefile @@ -4,7 +4,7 @@ KMOD= sdt SRCS= sdt.c -SRCS+= vnode_if.h +SRCS+= vnode_if.h opt_kdtrace.h CFLAGS+= -I${.CURDIR}/../../../cddl/compat/opensolaris \ -I${.CURDIR}/../../../cddl/contrib/opensolaris/uts/common \ diff --git a/sys/modules/ext2fs/Makefile b/sys/modules/ext2fs/Makefile index a38a63e245e..fc10ab0928b 100644 --- a/sys/modules/ext2fs/Makefile +++ b/sys/modules/ext2fs/Makefile @@ -3,8 +3,8 @@ .PATH: ${.CURDIR}/../../fs/ext2fs KMOD= ext2fs SRCS= opt_ddb.h opt_directio.h opt_quota.h opt_suiddir.h vnode_if.h \ - ext2_alloc.c ext2_balloc.c ext2_bmap.c ext2_hash.c ext2_htree.c \ - ext2_inode.c ext2_inode_cnv.c ext2_lookup.c ext2_subr.c ext2_vfsops.c \ - ext2_vnops.c + ext2_alloc.c ext2_balloc.c ext2_bmap.c ext2_extents.c ext2_hash.c \ + ext2_htree.c ext2_inode.c ext2_inode_cnv.c ext2_lookup.c ext2_subr.c \ + ext2_vfsops.c ext2_vnops.c .include diff --git a/sys/modules/hwpmc/Makefile b/sys/modules/hwpmc/Makefile index 8948805ceec..0ebf7a1b37d 100644 --- a/sys/modules/hwpmc/Makefile +++ b/sys/modules/hwpmc/Makefile @@ -29,7 +29,7 @@ SRCS+= hwpmc_ia64.c .endif .if ${MACHINE_CPUARCH} == "powerpc" -SRCS+= hwpmc_powerpc.c +SRCS+= hwpmc_powerpc.c hwpmc_mpc7xxx.c .endif .if ${MACHINE_CPUARCH} == "sparc64" diff --git a/sys/modules/hyperv/Makefile b/sys/modules/hyperv/Makefile new file mode 100644 index 00000000000..25b32e3a3cf --- /dev/null +++ b/sys/modules/hyperv/Makefile @@ -0,0 +1,5 @@ +# $FreeBSD$ + +SUBDIR = vmbus netvsc stordisengage storvsc utilities + +.include diff --git a/sys/modules/hyperv/netvsc/Makefile b/sys/modules/hyperv/netvsc/Makefile new file mode 100644 index 00000000000..b3685102eda --- /dev/null +++ b/sys/modules/hyperv/netvsc/Makefile @@ -0,0 +1,13 @@ +# $FreeBSD$ + +.PATH: ${.CURDIR}/../../../dev/hyperv/netvsc + +KMOD = hv_netvsc + +SRCS = hv_net_vsc.c \ + hv_netvsc_drv_freebsd.c \ + hv_rndis_filter.c + +CFLAGS += -I${.CURDIR}/../../../dev/hyperv/netvsc + +.include diff --git a/sys/modules/hyperv/stordisengage/Makefile b/sys/modules/hyperv/stordisengage/Makefile new file mode 100644 index 00000000000..d65c63c3413 --- /dev/null +++ b/sys/modules/hyperv/stordisengage/Makefile @@ -0,0 +1,9 @@ +# $FreeBSD$ + +.PATH: ${.CURDIR}/../../../dev/hyperv/stordisengage + +KMOD= hv_ata_pci_disengage + +SRCS= hv_ata_pci_disengage.c ata_if.h + +.include diff --git a/sys/modules/hyperv/storvsc/Makefile b/sys/modules/hyperv/storvsc/Makefile new file mode 100644 index 00000000000..5d19884a346 --- /dev/null +++ b/sys/modules/hyperv/storvsc/Makefile @@ -0,0 +1,14 @@ +# $FreeBSD$ + +.PATH: ${.CURDIR}/../../../dev/hyperv/storvsc + +KMOD= hv_storvsc + +SRCS = hv_storvsc_drv_freebsd.c \ + hv_vstorage.h + +CFLAGS+= -I${.CURDIR}/../../../dev/hyperv/include \ + -I${.CURDIR}/../../../dev/hyperv/vmbus \ + -I${.CURDIR}/../../../dev/hyperv/storvsc + +.include diff --git a/sys/modules/hyperv/utilities/Makefile b/sys/modules/hyperv/utilities/Makefile new file mode 100644 index 00000000000..ef0e338358c --- /dev/null +++ b/sys/modules/hyperv/utilities/Makefile @@ -0,0 +1,12 @@ +# $FreeBSD$ + +.PATH: ${.CURDIR}/../../../dev/hyperv/utilities + +KMOD= hv_utils + +SRCS = hv_util.c + +CFLAGS+= -I${.CURDIR}/../../../dev/hyperv/include \ + -I${.CURDIR}/../../../dev/hyperv/vmbus + +.include diff --git a/sys/modules/hyperv/vmbus/Makefile b/sys/modules/hyperv/vmbus/Makefile new file mode 100644 index 00000000000..df8464e0217 --- /dev/null +++ b/sys/modules/hyperv/vmbus/Makefile @@ -0,0 +1,20 @@ +# $FreeBSD$ + +.PATH: ${.CURDIR}/../../../dev/hyperv/vmbus \ + ${.CURDIR}/../../../dev/hyperv/utilities + +KMOD= hv_vmbus + +SRCS = hv_channel.c \ + hv_channel_mgmt.c \ + hv_connection.c \ + hv_hv.c \ + hv_ring_buffer.c \ + hv_vmbus_drv_freebsd.c \ + hv_vmbus_priv.h + +CFLAGS+= -I${.CURDIR}/../../../dev/hyperv/include \ + -I${.CURDIR}/../../../dev/hyperv/vmbus \ + -I${.CURDIR}/../../../dev/hyperv/utilities + +.include diff --git a/sys/modules/ip6_mroute_mod/Makefile b/sys/modules/ip6_mroute_mod/Makefile index 943f9ab9236..eb1c82395b0 100644 --- a/sys/modules/ip6_mroute_mod/Makefile +++ b/sys/modules/ip6_mroute_mod/Makefile @@ -7,7 +7,7 @@ KMOD= ip6_mroute SRCS= ip6_mroute.c -SRCS+= opt_inet6.h opt_mrouting.h +SRCS+= opt_inet6.h opt_kdtrace.h opt_mrouting.h .if !defined(KERNBUILDDIR) opt_inet6.h: diff --git a/sys/modules/ipfilter/Makefile b/sys/modules/ipfilter/Makefile index db780e9a679..6e6accf46ed 100644 --- a/sys/modules/ipfilter/Makefile +++ b/sys/modules/ipfilter/Makefile @@ -7,7 +7,8 @@ KMOD= ipl SRCS= mlfk_ipl.c ip_nat.c ip_frag.c ip_state.c ip_proxy.c ip_auth.c \ ip_log.c ip_fil_freebsd.c fil.c ip_lookup.c ip_pool.c ip_htable.c \ - ip_sync.c + ip_sync.c \ + ip_nat6.c ip_rules.c ip_scan.c ip_dstlist.c radix_ipf.c SRCS+= opt_bpf.h opt_inet6.h .if !defined(KERNBUILDDIR) diff --git a/sys/modules/iscsi/Makefile b/sys/modules/iscsi/Makefile index 625e3be9783..d072cfb3926 100644 --- a/sys/modules/iscsi/Makefile +++ b/sys/modules/iscsi/Makefile @@ -1,5 +1,25 @@ # $FreeBSD$ -SUBDIR= initiator +.PATH: ${.CURDIR}/../../dev/iscsi/ +KMOD= iscsi -.include +SRCS= iscsi.c +.if defined(ICL_RDMA) +SRCS+= icl_rdma.c +.else +SRCS+= icl.c +.endif +SRCS+= icl_proxy.c +SRCS+= opt_cam.h +SRCS+= bus_if.h +SRCS+= device_if.h + +# Those below are required for RDMA. +SRCS+= vnode_if.h +SRCS+= opt_inet.h +SRCS+= opt_inet6.h + +CFLAGS+= -I${.CURDIR}/../../ofed/include +#CFLAGS+=-DICL_KERNEL_PROXY + +.include diff --git a/sys/modules/iscsi/initiator/Makefile b/sys/modules/iscsi_initiator/Makefile similarity index 85% rename from sys/modules/iscsi/initiator/Makefile rename to sys/modules/iscsi_initiator/Makefile index 3295c7b6f51..0ae7aa4b973 100644 --- a/sys/modules/iscsi/initiator/Makefile +++ b/sys/modules/iscsi_initiator/Makefile @@ -1,6 +1,6 @@ # $FreeBSD$ -.PATH: ${.CURDIR}/../../../dev/iscsi/initiator +.PATH: ${.CURDIR}/../../dev/iscsi_initiator KMOD=iscsi_initiator SRCS= iscsi.h iscsivar.h diff --git a/sys/modules/iwnfw/Makefile b/sys/modules/iwnfw/Makefile index dc834605302..9075edb30c6 100644 --- a/sys/modules/iwnfw/Makefile +++ b/sys/modules/iwnfw/Makefile @@ -1,5 +1,5 @@ # $FreeBSD$ -SUBDIR= iwn1000 iwn4965 iwn5000 iwn5150 iwn6000 iwn6000g2a iwn6000g2b iwn6050 +SUBDIR= iwn1000 iwn4965 iwn5000 iwn5150 iwn6000 iwn6000g2a iwn6000g2b iwn6050 iwn2030 .include diff --git a/sys/modules/iwnfw/iwn2000/Makefile b/sys/modules/iwnfw/iwn2000/Makefile new file mode 100644 index 00000000000..920ae8def1b --- /dev/null +++ b/sys/modules/iwnfw/iwn2000/Makefile @@ -0,0 +1,6 @@ +# $FreeBSD$ + +KMOD= iwn2000fw +IMG= iwlwifi-2000-18.168.6.1 + +.include diff --git a/sys/modules/iwnfw/iwn2030/Makefile b/sys/modules/iwnfw/iwn2030/Makefile new file mode 100644 index 00000000000..0e92657048d --- /dev/null +++ b/sys/modules/iwnfw/iwn2030/Makefile @@ -0,0 +1,6 @@ +# $FreeBSD$ + +KMOD= iwn2030fw +IMG= iwnwifi-2030-18.168.6.1 + +.include diff --git a/sys/modules/iwnfw/iwn6000g2a/Makefile b/sys/modules/iwnfw/iwn6000g2a/Makefile index a39896cbda4..5a7142273d3 100644 --- a/sys/modules/iwnfw/iwn6000g2a/Makefile +++ b/sys/modules/iwnfw/iwn6000g2a/Makefile @@ -1,6 +1,6 @@ # $FreeBSD$ KMOD= iwn6000g2afw -IMG= iwlwifi-6000g2a-17.168.5.2 +IMG= iwlwifi-6000g2a-18.168.6.1 .include diff --git a/sys/modules/linux/Makefile b/sys/modules/linux/Makefile index 9d8dcf9346e..7ed6e989388 100644 --- a/sys/modules/linux/Makefile +++ b/sys/modules/linux/Makefile @@ -64,7 +64,3 @@ CFLAGS+= -DKTR .endif .include - -# XXX: clang integrated-as doesn't grok .codeNN directives yet -CFLAGS.linux32_locore.s= ${CLANG_NO_IAS} -CFLAGS+= ${CFLAGS.${.IMPSRC:T}} diff --git a/sys/modules/random/Makefile b/sys/modules/random/Makefile index ad14899e7ef..60b62afde1d 100644 --- a/sys/modules/random/Makefile +++ b/sys/modules/random/Makefile @@ -5,7 +5,7 @@ .PATH: ${.CURDIR}/../../crypto/sha2 KMOD= random -SRCS= randomdev.c probe.c +SRCS= randomdev.c .if ${MACHINE} == "amd64" || ${MACHINE} == "i386" SRCS+= nehemiah.c SRCS+= ivy.c diff --git a/sys/modules/rdma/Makefile b/sys/modules/rdma/Makefile index 83edf0939e4..f5d7255b309 100644 --- a/sys/modules/rdma/Makefile +++ b/sys/modules/rdma/Makefile @@ -1,9 +1,9 @@ # $FreeBSD$ -SUBDIR= addr -SUBDIR+= cma -SUBDIR+= iwcm -SUBDIR+= core +#SUBDIR= addr +#SUBDIR+= cma +#SUBDIR+= iwcm +#SUBDIR+= core SUBDIR+= krping .include diff --git a/sys/modules/send/Makefile b/sys/modules/send/Makefile index 1cb976b9b8d..06ea9e0d1ea 100644 --- a/sys/modules/send/Makefile +++ b/sys/modules/send/Makefile @@ -2,6 +2,6 @@ .PATH: ${.CURDIR}/../../netinet6 KMOD= send -SRCS= send.c +SRCS= send.c opt_kdtrace.h .include diff --git a/sys/modules/sfxge/Makefile b/sys/modules/sfxge/Makefile index f18e8adb01a..556ea5ea42b 100644 --- a/sys/modules/sfxge/Makefile +++ b/sys/modules/sfxge/Makefile @@ -5,7 +5,7 @@ KMOD= sfxge SFXGE= ${.CURDIR}/../../dev/sfxge SRCS= device_if.h bus_if.h pci_if.h -SRCS+= opt_inet.h opt_zero.h opt_sched.h +SRCS+= opt_inet.h opt_sched.h .PATH: ${.CURDIR}/../../dev/sfxge SRCS+= sfxge.c sfxge_dma.c sfxge_ev.c diff --git a/sys/modules/ti/Makefile b/sys/modules/ti/Makefile index 411cce0153a..7b87b6f3083 100644 --- a/sys/modules/ti/Makefile +++ b/sys/modules/ti/Makefile @@ -3,6 +3,6 @@ .PATH: ${.CURDIR}/../../dev/ti KMOD= if_ti -SRCS= if_ti.c device_if.h bus_if.h pci_if.h opt_ti.h opt_zero.h +SRCS= if_ti.c device_if.h bus_if.h pci_if.h opt_ti.h .include diff --git a/sys/modules/virtio/network/Makefile b/sys/modules/virtio/network/Makefile index 8463309c95e..f124d996342 100644 --- a/sys/modules/virtio/network/Makefile +++ b/sys/modules/virtio/network/Makefile @@ -23,14 +23,29 @@ # SUCH DAMAGE. # +.include + .PATH: ${.CURDIR}/../../../dev/virtio/network KMOD= if_vtnet SRCS= if_vtnet.c SRCS+= virtio_bus_if.h virtio_if.h SRCS+= bus_if.h device_if.h +SRCS+= opt_inet.h opt_inet6.h MFILES= kern/bus_if.m kern/device_if.m \ dev/virtio/virtio_bus_if.m dev/virtio/virtio_if.m +.if !defined(KERNBUILDDIR) +.if ${MK_INET_SUPPORT} != "no" +opt_inet.h: + @echo "#define INET 1" > ${.TARGET} +.endif + +.if ${MK_INET6_SUPPORT} != "no" +opt_inet6.h: + @echo "#define INET6 1" > ${.TARGET} +.endif +.endif + .include diff --git a/sys/modules/vmm/Makefile b/sys/modules/vmm/Makefile index b7bc9dee6c1..a7d44f94134 100644 --- a/sys/modules/vmm/Makefile +++ b/sys/modules/vmm/Makefile @@ -2,11 +2,11 @@ KMOD= vmm -SRCS= opt_ddb.h device_if.h bus_if.h pci_if.h +SRCS= opt_acpi.h opt_ddb.h device_if.h bus_if.h pci_if.h CFLAGS+= -DVMM_KEEP_STATS -DSMP -CFLAGS+= -I${.CURDIR}/../../amd64/vmm -CFLAGS+= -I${.CURDIR}/../../amd64/vmm/io +CFLAGS+= -I${.CURDIR}/../../amd64/vmm +CFLAGS+= -I${.CURDIR}/../../amd64/vmm/io CFLAGS+= -I${.CURDIR}/../../amd64/vmm/intel CFLAGS+= -I${.CURDIR}/../../amd64/vmm/amd diff --git a/sys/modules/vmware/Makefile b/sys/modules/vmware/Makefile new file mode 100644 index 00000000000..695d3440a18 --- /dev/null +++ b/sys/modules/vmware/Makefile @@ -0,0 +1,28 @@ +# +# $FreeBSD$ +# +# 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. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. +# + +SUBDIR= vmxnet3 + +.include diff --git a/sys/modules/vmware/vmxnet3/Makefile b/sys/modules/vmware/vmxnet3/Makefile new file mode 100644 index 00000000000..c84a382b8e2 --- /dev/null +++ b/sys/modules/vmware/vmxnet3/Makefile @@ -0,0 +1,46 @@ +# +# $FreeBSD$ +# +# 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. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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 + +.PATH: ${.CURDIR}/../../../dev/vmware/vmxnet3 + +KMOD= if_vmx +SRCS= if_vmx.c +SRCS+= bus_if.h device_if.h pci_if.h opt_inet.h opt_inet6.h + +.if !defined(KERNBUILDDIR) +.if ${MK_INET_SUPPORT} != "no" +opt_inet.h: + @echo "#define INET 1" > ${.TARGET} +.endif + +.if ${MK_INET6_SUPPORT} != "no" +opt_inet6.h: + @echo "#define INET6 1" > ${.TARGET} +.endif +.endif + +.include diff --git a/sys/net/if.c b/sys/net/if.c index 2cb3da013c2..0356ec7fbf4 100644 --- a/sys/net/if.c +++ b/sys/net/if.c @@ -2553,11 +2553,23 @@ ifioctl(struct socket *so, u_long cmd, caddr_t data, struct thread *td) CURVNET_RESTORE(); return (EOPNOTSUPP); } + + /* + * Pass the request on to the socket control method, and if the + * latter returns EOPNOTSUPP, directly to the interface. + * + * Make an exception for the legacy SIOCSIF* requests. Drivers + * trust SIOCSIFADDR et al to come from an already privileged + * layer, and do not perform any credentials checks or input + * validation. + */ #ifndef COMPAT_43 error = ((*so->so_proto->pr_usrreqs->pru_control)(so, cmd, data, ifp, td)); - if (error == EOPNOTSUPP && ifp != NULL && ifp->if_ioctl != NULL) + if (error == EOPNOTSUPP && ifp != NULL && ifp->if_ioctl != NULL && + cmd != SIOCSIFADDR && cmd != SIOCSIFBRDADDR && + cmd != SIOCSIFDSTADDR && cmd != SIOCSIFNETMASK) error = (*ifp->if_ioctl)(ifp, cmd, data); #else { @@ -2601,7 +2613,9 @@ ifioctl(struct socket *so, u_long cmd, caddr_t data, struct thread *td) data, ifp, td)); if (error == EOPNOTSUPP && ifp != NULL && - ifp->if_ioctl != NULL) + ifp->if_ioctl != NULL && + cmd != SIOCSIFADDR && cmd != SIOCSIFBRDADDR && + cmd != SIOCSIFDSTADDR && cmd != SIOCSIFNETMASK) error = (*ifp->if_ioctl)(ifp, cmd, data); switch (ocmd) { diff --git a/sys/net/if.h b/sys/net/if.h index 7aca535f9f7..ab98ec0b282 100644 --- a/sys/net/if.h +++ b/sys/net/if.h @@ -103,7 +103,7 @@ struct if_data { u_long ifi_omcasts; /* packets sent via multicast */ u_long ifi_iqdrops; /* dropped on input, this interface */ u_long ifi_noproto; /* destined for unsupported protocol */ - u_long ifi_hwassist; /* HW offload capabilities, see IFCAP */ + uint64_t ifi_hwassist; /* HW offload capabilities, see IFCAP */ time_t ifi_epoch; /* uptime at attach or stat reset */ struct timeval ifi_lastchange; /* time of last administrative change */ }; diff --git a/sys/net/if_ethersubr.c b/sys/net/if_ethersubr.c index 4f9672deb35..638b3647f6b 100644 --- a/sys/net/if_ethersubr.c +++ b/sys/net/if_ethersubr.c @@ -638,9 +638,8 @@ ether_input_internal(struct ifnet *ifp, struct mbuf *m) m->m_flags |= M_PROMISC; } - /* First chunk of an mbuf contains good entropy */ if (harvest.ethernet) - random_harvest(m, 16, 3, 0, RANDOM_NET); + random_harvest(&(m->m_data), 12, 3, 0, RANDOM_NET_ETHER); ether_demux(ifp, m); CURVNET_RESTORE(); @@ -774,7 +773,7 @@ ether_demux(struct ifnet *ifp, struct mbuf *m) * Strip off Ethernet header. */ m->m_flags &= ~M_VLANTAG; - m->m_flags &= ~(M_PROTOFLAGS); + m_clrprotoflags(m); m_adj(m, ETHER_HDR_LEN); /* diff --git a/sys/net/if_fddisubr.c b/sys/net/if_fddisubr.c index 5e7dbe62696..4f54dcb6780 100644 --- a/sys/net/if_fddisubr.c +++ b/sys/net/if_fddisubr.c @@ -390,7 +390,6 @@ fddi_input(ifp, m) goto dropanyway; } fh = mtod(m, struct fddi_header *); - m->m_pkthdr.header = (void *)fh; /* * Discard packet if interface is not up. diff --git a/sys/net/if_gif.c b/sys/net/if_gif.c index 874bbc00480..dfc2697e9af 100644 --- a/sys/net/if_gif.c +++ b/sys/net/if_gif.c @@ -687,9 +687,6 @@ gif_ioctl(ifp, cmd, data) ifp->if_flags |= IFF_UP; break; - case SIOCSIFDSTADDR: - break; - case SIOCADDMULTI: case SIOCDELMULTI: break; diff --git a/sys/net/if_gre.c b/sys/net/if_gre.c index df0fc8224bb..98090e54859 100644 --- a/sys/net/if_gre.c +++ b/sys/net/if_gre.c @@ -534,8 +534,6 @@ gre_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) case SIOCSIFADDR: ifp->if_flags |= IFF_UP; break; - case SIOCSIFDSTADDR: - break; case SIOCSIFFLAGS: /* * XXXRW: Isn't this priv_check() redundant to the ifnet diff --git a/sys/net/if_iso88025subr.c b/sys/net/if_iso88025subr.c index 63b210760e6..5975b282302 100644 --- a/sys/net/if_iso88025subr.c +++ b/sys/net/if_iso88025subr.c @@ -476,7 +476,6 @@ iso88025_input(ifp, m) goto dropanyway; } th = mtod(m, struct iso88025_header *); - m->m_pkthdr.header = (void *)th; /* * Discard packet if interface is not up. diff --git a/sys/net/if_lagg.c b/sys/net/if_lagg.c index a46e1f386ad..27bab876faf 100644 --- a/sys/net/if_lagg.c +++ b/sys/net/if_lagg.c @@ -37,7 +37,7 @@ __FBSDID("$FreeBSD$"); #include #include #include -#include +#include #include #include @@ -233,16 +233,17 @@ lagg_register_vlan(void *arg, struct ifnet *ifp, u_int16_t vtag) { struct lagg_softc *sc = ifp->if_softc; struct lagg_port *lp; + struct rm_priotracker tracker; if (ifp->if_softc != arg) /* Not our event */ return; - LAGG_RLOCK(sc); + LAGG_RLOCK(sc, &tracker); if (!SLIST_EMPTY(&sc->sc_ports)) { SLIST_FOREACH(lp, &sc->sc_ports, lp_entries) EVENTHANDLER_INVOKE(vlan_config, lp->lp_ifp, vtag); } - LAGG_RUNLOCK(sc); + LAGG_RUNLOCK(sc, &tracker); } /* @@ -254,16 +255,17 @@ lagg_unregister_vlan(void *arg, struct ifnet *ifp, u_int16_t vtag) { struct lagg_softc *sc = ifp->if_softc; struct lagg_port *lp; + struct rm_priotracker tracker; if (ifp->if_softc != arg) /* Not our event */ return; - LAGG_RLOCK(sc); + LAGG_RLOCK(sc, &tracker); if (!SLIST_EMPTY(&sc->sc_ports)) { SLIST_FOREACH(lp, &sc->sc_ports, lp_entries) EVENTHANDLER_INVOKE(vlan_unconfig, lp->lp_ifp, vtag); } - LAGG_RUNLOCK(sc); + LAGG_RUNLOCK(sc, &tracker); } static int @@ -322,9 +324,15 @@ lagg_clone_create(struct if_clone *ifc, int unit, caddr_t params) } } LAGG_LOCK_INIT(sc); + LAGG_CALLOUT_LOCK_INIT(sc); SLIST_INIT(&sc->sc_ports); TASK_INIT(&sc->sc_lladdr_task, 0, lagg_port_setlladdr, sc); - callout_init_rw(&sc->sc_callout, &sc->sc_mtx, CALLOUT_SHAREDLOCK); + + /* + * This uses the callout lock rather than the rmlock; one can't + * hold said rmlock during SWI. + */ + callout_init_mtx(&sc->sc_callout, &sc->sc_call_mtx, 0); /* Initialise pseudo media types */ ifmedia_init(&sc->sc_media, 0, lagg_media_change, @@ -389,7 +397,10 @@ lagg_clone_destroy(struct ifnet *ifp) ether_ifdetach(ifp); if_free(ifp); + /* This grabs sc_callout_mtx, serialising it correctly */ callout_drain(&sc->sc_callout); + + /* At this point it's drained; we can free this */ counter_u64_free(sc->sc_ipackets); counter_u64_free(sc->sc_opackets); counter_u64_free(sc->sc_ibytes); @@ -401,6 +412,7 @@ lagg_clone_destroy(struct ifnet *ifp) taskqueue_drain(taskqueue_swi, &sc->sc_lladdr_task); LAGG_LOCK_DESTROY(sc); + LAGG_CALLOUT_LOCK_DESTROY(sc); free(sc, M_DEVBUF); } @@ -764,6 +776,7 @@ lagg_port_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) struct lagg_softc *sc; struct lagg_port *lp = NULL; int error = 0; + struct rm_priotracker tracker; /* Should be checked by the caller */ if (ifp->if_type != IFT_IEEE8023ADLAG || @@ -778,15 +791,15 @@ lagg_port_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) break; } - LAGG_RLOCK(sc); + LAGG_RLOCK(sc, &tracker); if ((lp = ifp->if_lagg) == NULL || lp->lp_softc != sc) { error = ENOENT; - LAGG_RUNLOCK(sc); + LAGG_RUNLOCK(sc, &tracker); break; } lagg_port2req(lp, rp); - LAGG_RUNLOCK(sc); + LAGG_RUNLOCK(sc, &tracker); break; case SIOCSIFCAP: @@ -955,21 +968,22 @@ lagg_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) struct thread *td = curthread; char *buf, *outbuf; int count, buflen, len, error = 0; + struct rm_priotracker tracker; bzero(&rpbuf, sizeof(rpbuf)); switch (cmd) { case SIOCGLAGG: - LAGG_RLOCK(sc); + LAGG_RLOCK(sc, &tracker); count = 0; SLIST_FOREACH(lp, &sc->sc_ports, lp_entries) count++; buflen = count * sizeof(struct lagg_reqport); - LAGG_RUNLOCK(sc); + LAGG_RUNLOCK(sc, &tracker); outbuf = malloc(buflen, M_TEMP, M_WAITOK | M_ZERO); - LAGG_RLOCK(sc); + LAGG_RLOCK(sc, &tracker); ra->ra_proto = sc->sc_proto; if (sc->sc_req != NULL) (*sc->sc_req)(sc, (caddr_t)&ra->ra_psc); @@ -987,7 +1001,7 @@ lagg_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) buf += sizeof(rpbuf); len -= sizeof(rpbuf); } - LAGG_RUNLOCK(sc); + LAGG_RUNLOCK(sc, &tracker); ra->ra_ports = count; ra->ra_size = count * sizeof(rpbuf); error = copyout(outbuf, ra->ra_port, ra->ra_size); @@ -1065,16 +1079,16 @@ lagg_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) break; } - LAGG_RLOCK(sc); + LAGG_RLOCK(sc, &tracker); if ((lp = (struct lagg_port *)tpif->if_lagg) == NULL || lp->lp_softc != sc) { error = ENOENT; - LAGG_RUNLOCK(sc); + LAGG_RUNLOCK(sc, &tracker); break; } lagg_port2req(lp, rp); - LAGG_RUNLOCK(sc); + LAGG_RUNLOCK(sc, &tracker); break; case SIOCSLAGGPORT: error = priv_check(td, PRIV_NET_LAGG); @@ -1280,14 +1294,15 @@ lagg_transmit(struct ifnet *ifp, struct mbuf *m) { struct lagg_softc *sc = (struct lagg_softc *)ifp->if_softc; int error, len, mcast; + struct rm_priotracker tracker; len = m->m_pkthdr.len; mcast = (m->m_flags & (M_MCAST | M_BCAST)) ? 1 : 0; - LAGG_RLOCK(sc); + LAGG_RLOCK(sc, &tracker); /* We need a Tx algorithm and at least one port */ if (sc->sc_proto == LAGG_PROTO_NONE || sc->sc_count == 0) { - LAGG_RUNLOCK(sc); + LAGG_RUNLOCK(sc, &tracker); m_freem(m); ifp->if_oerrors++; return (ENXIO); @@ -1296,7 +1311,7 @@ lagg_transmit(struct ifnet *ifp, struct mbuf *m) ETHER_BPF_MTAP(ifp, m); error = (*sc->sc_start)(sc, m); - LAGG_RUNLOCK(sc); + LAGG_RUNLOCK(sc, &tracker); if (error == 0) { counter_u64_add(sc->sc_opackets, 1); @@ -1322,12 +1337,13 @@ lagg_input(struct ifnet *ifp, struct mbuf *m) struct lagg_port *lp = ifp->if_lagg; struct lagg_softc *sc = lp->lp_softc; struct ifnet *scifp = sc->sc_ifp; + struct rm_priotracker tracker; - LAGG_RLOCK(sc); + LAGG_RLOCK(sc, &tracker); if ((scifp->if_drv_flags & IFF_DRV_RUNNING) == 0 || (lp->lp_flags & LAGG_PORT_DISABLED) || sc->sc_proto == LAGG_PROTO_NONE) { - LAGG_RUNLOCK(sc); + LAGG_RUNLOCK(sc, &tracker); m_freem(m); return (NULL); } @@ -1346,7 +1362,7 @@ lagg_input(struct ifnet *ifp, struct mbuf *m) } } - LAGG_RUNLOCK(sc); + LAGG_RUNLOCK(sc, &tracker); return (m); } @@ -1367,16 +1383,17 @@ lagg_media_status(struct ifnet *ifp, struct ifmediareq *imr) { struct lagg_softc *sc = (struct lagg_softc *)ifp->if_softc; struct lagg_port *lp; + struct rm_priotracker tracker; imr->ifm_status = IFM_AVALID; imr->ifm_active = IFM_ETHER | IFM_AUTO; - LAGG_RLOCK(sc); + LAGG_RLOCK(sc, &tracker); SLIST_FOREACH(lp, &sc->sc_ports, lp_entries) { if (LAGG_PORTACTIVE(lp)) imr->ifm_status |= IFM_ACTIVE; } - LAGG_RUNLOCK(sc); + LAGG_RUNLOCK(sc, &tracker); } static void diff --git a/sys/net/if_lagg.h b/sys/net/if_lagg.h index a851f441b33..0db8da800e3 100644 --- a/sys/net/if_lagg.h +++ b/sys/net/if_lagg.h @@ -187,7 +187,8 @@ struct lagg_llq { struct lagg_softc { struct ifnet *sc_ifp; /* virtual interface */ - struct rwlock sc_mtx; + struct rmlock sc_mtx; + struct mtx sc_call_mtx; int sc_proto; /* lagg protocol */ u_int sc_count; /* number of ports */ u_int sc_active; /* active port count */ @@ -255,14 +256,19 @@ struct lagg_port { SLIST_ENTRY(lagg_port) lp_entries; }; -#define LAGG_LOCK_INIT(_sc) rw_init(&(_sc)->sc_mtx, "if_lagg rwlock") -#define LAGG_LOCK_DESTROY(_sc) rw_destroy(&(_sc)->sc_mtx) -#define LAGG_RLOCK(_sc) rw_rlock(&(_sc)->sc_mtx) -#define LAGG_WLOCK(_sc) rw_wlock(&(_sc)->sc_mtx) -#define LAGG_RUNLOCK(_sc) rw_runlock(&(_sc)->sc_mtx) -#define LAGG_WUNLOCK(_sc) rw_wunlock(&(_sc)->sc_mtx) -#define LAGG_RLOCK_ASSERT(_sc) rw_assert(&(_sc)->sc_mtx, RA_RLOCKED) -#define LAGG_WLOCK_ASSERT(_sc) rw_assert(&(_sc)->sc_mtx, RA_WLOCKED) +#define LAGG_LOCK_INIT(_sc) rm_init(&(_sc)->sc_mtx, "if_lagg rmlock") +#define LAGG_LOCK_DESTROY(_sc) rm_destroy(&(_sc)->sc_mtx) +#define LAGG_RLOCK(_sc, _p) rm_rlock(&(_sc)->sc_mtx, (_p)) +#define LAGG_WLOCK(_sc) rm_wlock(&(_sc)->sc_mtx) +#define LAGG_RUNLOCK(_sc, _p) rm_runlock(&(_sc)->sc_mtx, (_p)) +#define LAGG_WUNLOCK(_sc) rm_wunlock(&(_sc)->sc_mtx) +#define LAGG_RLOCK_ASSERT(_sc) rm_assert(&(_sc)->sc_mtx, RA_RLOCKED) +#define LAGG_WLOCK_ASSERT(_sc) rm_assert(&(_sc)->sc_mtx, RA_WLOCKED) + +#define LAGG_CALLOUT_LOCK_INIT(_sc) \ + mtx_init(&(_sc)->sc_call_mtx, "if_lagg callout mutex", NULL,\ + MTX_DEF) +#define LAGG_CALLOUT_LOCK_DESTROY(_sc) mtx_destroy(&(_sc)->sc_call_mtx) extern struct mbuf *(*lagg_input_p)(struct ifnet *, struct mbuf *); extern void (*lagg_linkstate_p)(struct ifnet *, int ); diff --git a/sys/net/if_llatbl.h b/sys/net/if_llatbl.h index 693ccd521fa..4f21fdea447 100644 --- a/sys/net/if_llatbl.h +++ b/sys/net/if_llatbl.h @@ -30,8 +30,6 @@ __FBSDID("$FreeBSD$"); #ifndef _NET_IF_LLATBL_H_ #define _NET_IF_LLATBL_H_ -#include "opt_ofed.h" - #include #include @@ -75,9 +73,7 @@ struct llentry { union { uint64_t mac_aligned; uint16_t mac16[3]; -#ifdef OFED uint8_t mac8[20]; /* IB needs 20 bytes. */ -#endif } ll_addr; /* XXX af-private? */ diff --git a/sys/net/if_pfsync.h b/sys/net/if_pfsync.h index 29d4b23ce22..7a72bbbee56 100644 --- a/sys/net/if_pfsync.h +++ b/sys/net/if_pfsync.h @@ -211,11 +211,6 @@ struct pfsync_tdb { #define PFSYNCCTL_STATS 1 /* PFSYNC stats */ #define PFSYNCCTL_MAXID 2 -#define PFSYNCCTL_NAMES { \ - { 0, 0 }, \ - { "stats", CTLTYPE_STRUCT }, \ -} - struct pfsyncstats { u_int64_t pfsyncs_ipackets; /* total input packets, IPv4 */ u_int64_t pfsyncs_ipackets6; /* total input packets, IPv6 */ diff --git a/sys/net/if_spppsubr.c b/sys/net/if_spppsubr.c index 72555459cd3..9fe713c3891 100644 --- a/sys/net/if_spppsubr.c +++ b/sys/net/if_spppsubr.c @@ -1200,7 +1200,6 @@ sppp_ioctl(struct ifnet *ifp, IOCTL_CMD_T cmd, void *data) rv = 0; switch (cmd) { case SIOCAIFADDR: - case SIOCSIFDSTADDR: break; case SIOCSIFADDR: diff --git a/sys/net/if_tap.c b/sys/net/if_tap.c index 45bd7d269b9..ad6f781b6e5 100644 --- a/sys/net/if_tap.c +++ b/sys/net/if_tap.c @@ -409,8 +409,6 @@ tapcreate(struct cdev *dev) const char *name = NULL; u_char eaddr[6]; - dev->si_flags &= ~SI_CHEAPCLONE; - /* allocate driver storage and create device */ tp = malloc(sizeof(*tp), M_TAP, M_WAITOK | M_ZERO); mtx_init(&tp->tap_mtx, "tap_mtx", NULL, MTX_DEF); diff --git a/sys/net/if_tun.c b/sys/net/if_tun.c index d88502493f1..f44bc0e5bf3 100644 --- a/sys/net/if_tun.c +++ b/sys/net/if_tun.c @@ -321,6 +321,7 @@ static moduledata_t tun_mod = { }; DECLARE_MODULE(if_tun, tun_mod, SI_SUB_PSEUDO, SI_ORDER_ANY); +MODULE_VERSION(if_tun, 1); static void tunstart(struct ifnet *ifp) @@ -360,8 +361,6 @@ tuncreate(const char *name, struct cdev *dev) struct tun_softc *sc; struct ifnet *ifp; - dev->si_flags &= ~SI_CHEAPCLONE; - sc = malloc(sizeof(*sc), M_TUN, M_WAITOK | M_ZERO); mtx_init(&sc->tun_mtx, "tun_mtx", NULL, MTX_DEF); cv_init(&sc->tun_cv, "tun_condvar"); @@ -553,10 +552,6 @@ tunifioctl(struct ifnet *ifp, u_long cmd, caddr_t data) tuninit(ifp); TUNDEBUG(ifp, "address set\n"); break; - case SIOCSIFDSTADDR: - tuninit(ifp); - TUNDEBUG(ifp, "destination address set\n"); - break; case SIOCSIFMTU: ifp->if_mtu = ifr->ifr_mtu; TUNDEBUG(ifp, "mtu set\n"); @@ -921,9 +916,8 @@ tunwrite(struct cdev *dev, struct uio *uio, int flag) m_freem(m); return (EAFNOSUPPORT); } - /* First chunk of an mbuf contains good junk */ if (harvest.point_to_point) - random_harvest(m, 16, 3, 0, RANDOM_NET); + random_harvest(&(m->m_data), 12, 3, 0, RANDOM_NET_TUN); ifp->if_ibytes += m->m_pkthdr.len; ifp->if_ipackets++; CURVNET_SET(ifp->if_vnet); diff --git a/sys/net/netisr.c b/sys/net/netisr.c index 534d80cce68..3045e95bb70 100644 --- a/sys/net/netisr.c +++ b/sys/net/netisr.c @@ -153,19 +153,6 @@ SYSCTL_PROC(_net_isr, OID_AUTO, dispatch, CTLTYPE_STRING | CTLFLAG_RW | CTLFLAG_TUN, 0, 0, sysctl_netisr_dispatch_policy, "A", "netisr dispatch policy"); -/* - * These sysctls were used in previous versions to control and export - * dispatch policy state. Now, we provide read-only export via them so that - * older netstat binaries work. At some point they can be garbage collected. - */ -static int netisr_direct_force; -SYSCTL_INT(_net_isr, OID_AUTO, direct_force, CTLFLAG_RD, - &netisr_direct_force, 0, "compat: force direct dispatch"); - -static int netisr_direct; -SYSCTL_INT(_net_isr, OID_AUTO, direct, CTLFLAG_RD, &netisr_direct, 0, - "compat: enable direct dispatch"); - /* * Allow the administrator to limit the number of threads (CPUs) to use for * netisr. We don't check netisr_maxthreads before creating the thread for @@ -338,32 +325,6 @@ netisr_dispatch_policy_from_str(const char *str, u_int *dispatch_policyp) return (EINVAL); } -static void -netisr_dispatch_policy_compat(void) -{ - - switch (netisr_dispatch_policy) { - case NETISR_DISPATCH_DEFERRED: - netisr_direct_force = 0; - netisr_direct = 0; - break; - - case NETISR_DISPATCH_HYBRID: - netisr_direct_force = 0; - netisr_direct = 1; - break; - - case NETISR_DISPATCH_DIRECT: - netisr_direct_force = 1; - netisr_direct = 1; - break; - - default: - panic("%s: unknown policy %u", __func__, - netisr_dispatch_policy); - } -} - static int sysctl_netisr_dispatch_policy(SYSCTL_HANDLER_ARGS) { @@ -379,10 +340,8 @@ sysctl_netisr_dispatch_policy(SYSCTL_HANDLER_ARGS) &dispatch_policy); if (error == 0 && dispatch_policy == NETISR_DISPATCH_DEFAULT) error = EINVAL; - if (error == 0) { + if (error == 0) netisr_dispatch_policy = dispatch_policy; - netisr_dispatch_policy_compat(); - } } return (error); } @@ -1199,10 +1158,9 @@ netisr_init(void *arg) &dispatch_policy); if (error == 0 && dispatch_policy == NETISR_DISPATCH_DEFAULT) error = EINVAL; - if (error == 0) { + if (error == 0) netisr_dispatch_policy = dispatch_policy; - netisr_dispatch_policy_compat(); - } else + else printf( "%s: invalid dispatch policy %s, using default\n", __func__, tmp); diff --git a/sys/net/pfil.c b/sys/net/pfil.c index 9939d72a15a..44373ee14bf 100644 --- a/sys/net/pfil.c +++ b/sys/net/pfil.c @@ -52,11 +52,9 @@ static struct mtx pfil_global_lock; MTX_SYSINIT(pfil_heads_lock, &pfil_global_lock, "pfil_head_list lock", MTX_DEF); -static int pfil_list_add(pfil_list_t *, struct packet_filter_hook *, int); - -static int pfil_list_remove(pfil_list_t *, - int (*)(void *, struct mbuf **, struct ifnet *, int, struct inpcb *), - void *); +static struct packet_filter_hook *pfil_chain_get(int, struct pfil_head *); +static int pfil_chain_add(pfil_chain_t *, struct packet_filter_hook *, int); +static int pfil_chain_remove(pfil_chain_t *, pfil_func_t, void *); LIST_HEAD(pfilheadhead, pfil_head); VNET_DEFINE(struct pfilheadhead, pfil_head_list); @@ -65,7 +63,7 @@ VNET_DEFINE(struct rmlock, pfil_lock); #define V_pfil_lock VNET(pfil_lock) /* - * pfil_run_hooks() runs the specified packet filter hooks. + * pfil_run_hooks() runs the specified packet filter hook chain. */ int pfil_run_hooks(struct pfil_head *ph, struct mbuf **mp, struct ifnet *ifp, @@ -78,8 +76,8 @@ pfil_run_hooks(struct pfil_head *ph, struct mbuf **mp, struct ifnet *ifp, PFIL_RLOCK(ph, &rmpt); KASSERT(ph->ph_nhooks >= 0, ("Pfil hook count dropped < 0")); - for (pfh = pfil_hook_get(dir, ph); pfh != NULL; - pfh = TAILQ_NEXT(pfh, pfil_link)) { + for (pfh = pfil_chain_get(dir, ph); pfh != NULL; + pfh = TAILQ_NEXT(pfh, pfil_chain)) { if (pfh->pfil_func != NULL) { rv = (*pfh->pfil_func)(pfh->pfil_arg, &m, ifp, dir, inp); @@ -92,6 +90,18 @@ pfil_run_hooks(struct pfil_head *ph, struct mbuf **mp, struct ifnet *ifp, return (rv); } +static struct packet_filter_hook * +pfil_chain_get(int dir, struct pfil_head *ph) +{ + + if (dir == PFIL_IN) + return (TAILQ_FIRST(&ph->ph_in)); + else if (dir == PFIL_OUT) + return (TAILQ_FIRST(&ph->ph_out)); + else + return (NULL); +} + /* * pfil_try_rlock() acquires rm reader lock for specified head * if this is immediately possible. @@ -153,6 +163,7 @@ pfil_wowned(struct pfil_head *ph) return (PFIL_WOWNED(ph)); } + /* * pfil_head_register() registers a pfil_head with the packet filter hook * mechanism. @@ -162,11 +173,11 @@ pfil_head_register(struct pfil_head *ph) { struct pfil_head *lph; - PFIL_LIST_LOCK(); + PFIL_HEADLIST_LOCK(); LIST_FOREACH(lph, &V_pfil_head_list, ph_list) { if (ph->ph_type == lph->ph_type && ph->ph_un.phu_val == lph->ph_un.phu_val) { - PFIL_LIST_UNLOCK(); + PFIL_HEADLIST_UNLOCK(); return (EEXIST); } } @@ -175,7 +186,7 @@ pfil_head_register(struct pfil_head *ph) TAILQ_INIT(&ph->ph_in); TAILQ_INIT(&ph->ph_out); LIST_INSERT_HEAD(&V_pfil_head_list, ph, ph_list); - PFIL_LIST_UNLOCK(); + PFIL_HEADLIST_UNLOCK(); return (0); } @@ -189,12 +200,12 @@ pfil_head_unregister(struct pfil_head *ph) { struct packet_filter_hook *pfh, *pfnext; - PFIL_LIST_LOCK(); + PFIL_HEADLIST_LOCK(); LIST_REMOVE(ph, ph_list); - PFIL_LIST_UNLOCK(); - TAILQ_FOREACH_SAFE(pfh, &ph->ph_in, pfil_link, pfnext) + PFIL_HEADLIST_UNLOCK(); + TAILQ_FOREACH_SAFE(pfh, &ph->ph_in, pfil_chain, pfnext) free(pfh, M_IFADDR); - TAILQ_FOREACH_SAFE(pfh, &ph->ph_out, pfil_link, pfnext) + TAILQ_FOREACH_SAFE(pfh, &ph->ph_out, pfil_chain, pfnext) free(pfh, M_IFADDR); PFIL_LOCK_DESTROY(ph); return (0); @@ -208,11 +219,11 @@ pfil_head_get(int type, u_long val) { struct pfil_head *ph; - PFIL_LIST_LOCK(); + PFIL_HEADLIST_LOCK(); LIST_FOREACH(ph, &V_pfil_head_list, ph_list) if (ph->ph_type == type && ph->ph_un.phu_val == val) break; - PFIL_LIST_UNLOCK(); + PFIL_HEADLIST_UNLOCK(); return (ph); } @@ -225,8 +236,7 @@ pfil_head_get(int type, u_long val) * PFIL_WAITOK OK to call malloc with M_WAITOK. */ int -pfil_add_hook(int (*func)(void *, struct mbuf **, struct ifnet *, int, - struct inpcb *), void *arg, int flags, struct pfil_head *ph) +pfil_add_hook(pfil_func_t func, void *arg, int flags, struct pfil_head *ph) { struct packet_filter_hook *pfh1 = NULL; struct packet_filter_hook *pfh2 = NULL; @@ -252,7 +262,7 @@ pfil_add_hook(int (*func)(void *, struct mbuf **, struct ifnet *, int, if (flags & PFIL_IN) { pfh1->pfil_func = func; pfh1->pfil_arg = arg; - err = pfil_list_add(&ph->ph_in, pfh1, flags & ~PFIL_OUT); + err = pfil_chain_add(&ph->ph_in, pfh1, flags & ~PFIL_OUT); if (err) goto locked_error; ph->ph_nhooks++; @@ -260,10 +270,10 @@ pfil_add_hook(int (*func)(void *, struct mbuf **, struct ifnet *, int, if (flags & PFIL_OUT) { pfh2->pfil_func = func; pfh2->pfil_arg = arg; - err = pfil_list_add(&ph->ph_out, pfh2, flags & ~PFIL_IN); + err = pfil_chain_add(&ph->ph_out, pfh2, flags & ~PFIL_IN); if (err) { if (flags & PFIL_IN) - pfil_list_remove(&ph->ph_in, func, arg); + pfil_chain_remove(&ph->ph_in, func, arg); goto locked_error; } ph->ph_nhooks++; @@ -282,22 +292,21 @@ error: /* * pfil_remove_hook removes a specific function from the packet filter hook - * list. + * chain. */ int -pfil_remove_hook(int (*func)(void *, struct mbuf **, struct ifnet *, int, - struct inpcb *), void *arg, int flags, struct pfil_head *ph) +pfil_remove_hook(pfil_func_t func, void *arg, int flags, struct pfil_head *ph) { int err = 0; PFIL_WLOCK(ph); if (flags & PFIL_IN) { - err = pfil_list_remove(&ph->ph_in, func, arg); + err = pfil_chain_remove(&ph->ph_in, func, arg); if (err == 0) ph->ph_nhooks--; } if ((err == 0) && (flags & PFIL_OUT)) { - err = pfil_list_remove(&ph->ph_out, func, arg); + err = pfil_chain_remove(&ph->ph_out, func, arg); if (err == 0) ph->ph_nhooks--; } @@ -305,15 +314,18 @@ pfil_remove_hook(int (*func)(void *, struct mbuf **, struct ifnet *, int, return (err); } +/* + * Internal: Add a new pfil hook into a hook chain. + */ static int -pfil_list_add(pfil_list_t *list, struct packet_filter_hook *pfh1, int flags) +pfil_chain_add(pfil_chain_t *chain, struct packet_filter_hook *pfh1, int flags) { struct packet_filter_hook *pfh; /* * First make sure the hook is not already there. */ - TAILQ_FOREACH(pfh, list, pfil_link) + TAILQ_FOREACH(pfh, chain, pfil_chain) if (pfh->pfil_func == pfh1->pfil_func && pfh->pfil_arg == pfh1->pfil_arg) return (EEXIST); @@ -323,26 +335,23 @@ pfil_list_add(pfil_list_t *list, struct packet_filter_hook *pfh1, int flags) * the same path is followed in or out of the kernel. */ if (flags & PFIL_IN) - TAILQ_INSERT_HEAD(list, pfh1, pfil_link); + TAILQ_INSERT_HEAD(chain, pfh1, pfil_chain); else - TAILQ_INSERT_TAIL(list, pfh1, pfil_link); + TAILQ_INSERT_TAIL(chain, pfh1, pfil_chain); return (0); } /* - * pfil_list_remove is an internal function that takes a function off the - * specified list. + * Internal: Remove a pfil hook from a hook chain. */ static int -pfil_list_remove(pfil_list_t *list, - int (*func)(void *, struct mbuf **, struct ifnet *, int, struct inpcb *), - void *arg) +pfil_chain_remove(pfil_chain_t *chain, pfil_func_t func, void *arg) { struct packet_filter_hook *pfh; - TAILQ_FOREACH(pfh, list, pfil_link) + TAILQ_FOREACH(pfh, chain, pfil_chain) if (pfh->pfil_func == func && pfh->pfil_arg == arg) { - TAILQ_REMOVE(list, pfh, pfil_link); + TAILQ_REMOVE(chain, pfh, pfil_chain); free(pfh, M_IFADDR); return (0); } @@ -369,7 +378,8 @@ static int vnet_pfil_uninit(const void *unused) { - /* XXX should panic if list is not empty */ + KASSERT(LIST_EMPTY(&V_pfil_head_list), + ("%s: pfil_head_list %p not empty", __func__, &V_pfil_head_list)); PFIL_LOCK_DESTROY_REAL(&V_pfil_lock); return (0); } diff --git a/sys/net/pfil.h b/sys/net/pfil.h index 9cdb4225f43..c9a1b65918e 100644 --- a/sys/net/pfil.h +++ b/sys/net/pfil.h @@ -43,15 +43,18 @@ struct mbuf; struct ifnet; struct inpcb; +typedef int (*pfil_func_t)(void *, struct mbuf **, struct ifnet *, int, + struct inpcb *); + /* * The packet filter hooks are designed for anything to call them to - * possibly intercept the packet. + * possibly intercept the packet. Multiple filter hooks are chained + * together and after each other in the specified order. */ struct packet_filter_hook { - TAILQ_ENTRY(packet_filter_hook) pfil_link; - int (*pfil_func)(void *, struct mbuf **, struct ifnet *, int, - struct inpcb *); - void *pfil_arg; + TAILQ_ENTRY(packet_filter_hook) pfil_chain; + pfil_func_t pfil_func; + void *pfil_arg; }; #define PFIL_IN 0x00000001 @@ -59,55 +62,62 @@ struct packet_filter_hook { #define PFIL_WAITOK 0x00000004 #define PFIL_ALL (PFIL_IN|PFIL_OUT) -typedef TAILQ_HEAD(pfil_list, packet_filter_hook) pfil_list_t; +typedef TAILQ_HEAD(pfil_chain, packet_filter_hook) pfil_chain_t; #define PFIL_TYPE_AF 1 /* key is AF_* type */ #define PFIL_TYPE_IFNET 2 /* key is ifnet pointer */ #define PFIL_FLAG_PRIVATE_LOCK 0x01 /* Personal lock instead of global */ +/* + * A pfil head is created by each protocol or packet intercept point. + * For packet is then run through the hook chain for inspection. + */ struct pfil_head { - pfil_list_t ph_in; - pfil_list_t ph_out; - int ph_type; - int ph_nhooks; + pfil_chain_t ph_in; + pfil_chain_t ph_out; + int ph_type; + int ph_nhooks; #if defined( __linux__ ) || defined( _WIN32 ) - rwlock_t ph_mtx; + rwlock_t ph_mtx; #else struct rmlock *ph_plock; /* Pointer to the used lock */ - struct rmlock ph_lock; /* Private lock storage */ - int flags; + struct rmlock ph_lock; /* Private lock storage */ + int flags; #endif union { - u_long phu_val; - void *phu_ptr; + u_long phu_val; + void *phu_ptr; } ph_un; -#define ph_af ph_un.phu_val -#define ph_ifnet ph_un.phu_ptr +#define ph_af ph_un.phu_val +#define ph_ifnet ph_un.phu_ptr LIST_ENTRY(pfil_head) ph_list; }; -int pfil_add_hook(int (*func)(void *, struct mbuf **, struct ifnet *, - int, struct inpcb *), void *, int, struct pfil_head *); -int pfil_remove_hook(int (*func)(void *, struct mbuf **, struct ifnet *, - int, struct inpcb *), void *, int, struct pfil_head *); +/* Public functions for pfil hook management by packet filters. */ +struct pfil_head *pfil_head_get(int, u_long); +int pfil_add_hook(pfil_func_t, void *, int, struct pfil_head *); +int pfil_remove_hook(pfil_func_t, void *, int, struct pfil_head *); +#define PFIL_HOOKED(p) ((p)->ph_nhooks > 0) + +/* Public functions to run the packet inspection by protocols. */ int pfil_run_hooks(struct pfil_head *, struct mbuf **, struct ifnet *, int, struct inpcb *inp); -struct rm_priotracker; /* Do not require including rmlock header */ -int pfil_try_rlock(struct pfil_head *, struct rm_priotracker *); -void pfil_rlock(struct pfil_head *, struct rm_priotracker *); -void pfil_runlock(struct pfil_head *, struct rm_priotracker *); -void pfil_wlock(struct pfil_head *); -void pfil_wunlock(struct pfil_head *); -int pfil_wowned(struct pfil_head *ph); - +/* Public functions for pfil head management by protocols. */ int pfil_head_register(struct pfil_head *); int pfil_head_unregister(struct pfil_head *); -struct pfil_head *pfil_head_get(int, u_long); +/* Public pfil locking functions for self managed locks by packet filters. */ +struct rm_priotracker; /* Do not require including rmlock header */ +int pfil_try_rlock(struct pfil_head *, struct rm_priotracker *); +void pfil_rlock(struct pfil_head *, struct rm_priotracker *); +void pfil_runlock(struct pfil_head *, struct rm_priotracker *); +void pfil_wlock(struct pfil_head *); +void pfil_wunlock(struct pfil_head *); +int pfil_wowned(struct pfil_head *ph); -#define PFIL_HOOKED(p) ((p)->ph_nhooks > 0) +/* Internal pfil locking functions. */ #define PFIL_LOCK_INIT_REAL(l, t) \ rm_init_flags(l, "PFil " t " rmlock", RM_RECURSE) #define PFIL_LOCK_DESTROY_REAL(l) \ @@ -123,25 +133,16 @@ struct pfil_head *pfil_head_get(int, u_long); if ((p)->flags & PFIL_FLAG_PRIVATE_LOCK) \ PFIL_LOCK_DESTROY_REAL((p)->ph_plock); \ } while (0) + #define PFIL_TRY_RLOCK(p, t) rm_try_rlock((p)->ph_plock, (t)) #define PFIL_RLOCK(p, t) rm_rlock((p)->ph_plock, (t)) #define PFIL_WLOCK(p) rm_wlock((p)->ph_plock) #define PFIL_RUNLOCK(p, t) rm_runlock((p)->ph_plock, (t)) #define PFIL_WUNLOCK(p) rm_wunlock((p)->ph_plock) #define PFIL_WOWNED(p) rm_wowned((p)->ph_plock) -#define PFIL_LIST_LOCK() mtx_lock(&pfil_global_lock) -#define PFIL_LIST_UNLOCK() mtx_unlock(&pfil_global_lock) -static __inline struct packet_filter_hook * -pfil_hook_get(int dir, struct pfil_head *ph) -{ - - if (dir == PFIL_IN) - return (TAILQ_FIRST(&ph->ph_in)); - else if (dir == PFIL_OUT) - return (TAILQ_FIRST(&ph->ph_out)); - else - return (NULL); -} +/* Internal locking macros for global/vnet pfil_head_list. */ +#define PFIL_HEADLIST_LOCK() mtx_lock(&pfil_global_lock) +#define PFIL_HEADLIST_UNLOCK() mtx_unlock(&pfil_global_lock) #endif /* _NET_PFIL_H_ */ diff --git a/sys/net/rtsock.c b/sys/net/rtsock.c index 479977c6c96..0b5d1c5ebc5 100644 --- a/sys/net/rtsock.c +++ b/sys/net/rtsock.c @@ -86,7 +86,7 @@ struct if_data32 { uint8_t ifi_hdrlen; uint8_t ifi_link_state; uint8_t ifi_vhid; - uint8_t ifi_spare_char2; + uint8_t ifi_baudrate_pf; uint8_t ifi_datalen; uint32_t ifi_mtu; uint32_t ifi_metric; @@ -1629,6 +1629,7 @@ copy_ifdata32(struct if_data *src, struct if_data32 *dst) CP(*src, *dst, ifi_hdrlen); CP(*src, *dst, ifi_link_state); CP(*src, *dst, ifi_vhid); + CP(*src, *dst, ifi_baudrate_pf); dst->ifi_datalen = sizeof(struct if_data32); CP(*src, *dst, ifi_mtu); CP(*src, *dst, ifi_metric); diff --git a/sys/net80211/ieee80211.c b/sys/net80211/ieee80211.c index 6b6987d69dc..db505abd2dc 100644 --- a/sys/net80211/ieee80211.c +++ b/sys/net80211/ieee80211.c @@ -241,9 +241,15 @@ null_transmit(struct ifnet *ifp, struct mbuf *m) return EACCES; /* XXX EIO/EPERM? */ } +#if __FreeBSD_version >= 1000031 static int null_output(struct ifnet *ifp, struct mbuf *m, const struct sockaddr *dst, struct route *ro) +#else +static int +null_output(struct ifnet *ifp, struct mbuf *m, + struct sockaddr *dst, struct route *ro) +#endif { if_printf(ifp, "discard raw packet\n"); return null_transmit(ifp, m); @@ -427,13 +433,10 @@ ieee80211_vap_setup(struct ieee80211com *ic, struct ieee80211vap *vap, if_initname(ifp, name, unit); ifp->if_softc = vap; /* back pointer */ ifp->if_flags = IFF_SIMPLEX | IFF_BROADCAST | IFF_MULTICAST; - ifp->if_start = ieee80211_start; + ifp->if_transmit = ieee80211_vap_transmit; + ifp->if_qflush = ieee80211_vap_qflush; ifp->if_ioctl = ieee80211_ioctl; ifp->if_init = ieee80211_init; - /* NB: input+output filled in by ether_ifattach */ - IFQ_SET_MAXLEN(&ifp->if_snd, ifqmaxlen); - ifp->if_snd.ifq_drv_maxlen = ifqmaxlen; - IFQ_SET_READY(&ifp->if_snd); vap->iv_ifp = ifp; vap->iv_ic = ic; @@ -1515,7 +1518,6 @@ findmedia(const struct ratemedia rates[], int n, u_int match) int ieee80211_rate2media(struct ieee80211com *ic, int rate, enum ieee80211_phymode mode) { -#define N(a) (sizeof(a) / sizeof(a[0])) static const struct ratemedia rates[] = { { 2 | IFM_IEEE80211_FH, IFM_IEEE80211_FH1 }, { 4 | IFM_IEEE80211_FH, IFM_IEEE80211_FH2 }, @@ -1636,7 +1638,7 @@ ieee80211_rate2media(struct ieee80211com *ic, int rate, enum ieee80211_phymode m if (mode == IEEE80211_MODE_11NA) { if (rate & IEEE80211_RATE_MCS) { rate &= ~IEEE80211_RATE_MCS; - m = findmedia(htrates, N(htrates), rate); + m = findmedia(htrates, nitems(htrates), rate); if (m != IFM_AUTO) return m | IFM_IEEE80211_11NA; } @@ -1644,7 +1646,7 @@ ieee80211_rate2media(struct ieee80211com *ic, int rate, enum ieee80211_phymode m /* NB: 12 is ambiguous, it will be treated as an MCS */ if (rate & IEEE80211_RATE_MCS) { rate &= ~IEEE80211_RATE_MCS; - m = findmedia(htrates, N(htrates), rate); + m = findmedia(htrates, nitems(htrates), rate); if (m != IFM_AUTO) return m | IFM_IEEE80211_11NG; } @@ -1657,31 +1659,32 @@ ieee80211_rate2media(struct ieee80211com *ic, int rate, enum ieee80211_phymode m case IEEE80211_MODE_11NA: case IEEE80211_MODE_TURBO_A: case IEEE80211_MODE_STURBO_A: - return findmedia(rates, N(rates), rate | IFM_IEEE80211_11A); + return findmedia(rates, nitems(rates), + rate | IFM_IEEE80211_11A); case IEEE80211_MODE_11B: - return findmedia(rates, N(rates), rate | IFM_IEEE80211_11B); + return findmedia(rates, nitems(rates), + rate | IFM_IEEE80211_11B); case IEEE80211_MODE_FH: - return findmedia(rates, N(rates), rate | IFM_IEEE80211_FH); + return findmedia(rates, nitems(rates), + rate | IFM_IEEE80211_FH); case IEEE80211_MODE_AUTO: /* NB: ic may be NULL for some drivers */ if (ic != NULL && ic->ic_phytype == IEEE80211_T_FH) - return findmedia(rates, N(rates), + return findmedia(rates, nitems(rates), rate | IFM_IEEE80211_FH); /* NB: hack, 11g matches both 11b+11a rates */ /* fall thru... */ case IEEE80211_MODE_11G: case IEEE80211_MODE_11NG: case IEEE80211_MODE_TURBO_G: - return findmedia(rates, N(rates), rate | IFM_IEEE80211_11G); + return findmedia(rates, nitems(rates), rate | IFM_IEEE80211_11G); } return IFM_AUTO; -#undef N } int ieee80211_media2rate(int mword) { -#define N(a) (sizeof(a) / sizeof(a[0])) static const int ieeerates[] = { -1, /* IFM_AUTO */ 0, /* IFM_MANUAL */ @@ -1709,9 +1712,8 @@ ieee80211_media2rate(int mword) 54, /* IFM_IEEE80211_OFDM27 */ -1, /* IFM_IEEE80211_MCS */ }; - return IFM_SUBTYPE(mword) < N(ieeerates) ? + return IFM_SUBTYPE(mword) < nitems(ieeerates) ? ieeerates[IFM_SUBTYPE(mword)] : 0; -#undef N } /* diff --git a/sys/net80211/ieee80211_action.c b/sys/net80211/ieee80211_action.c index e3576d6679a..a36df7c8b0b 100644 --- a/sys/net80211/ieee80211_action.c +++ b/sys/net80211/ieee80211_action.c @@ -80,37 +80,35 @@ static ieee80211_send_action_func *vendor_send_action[8] = { int ieee80211_send_action_register(int cat, int act, ieee80211_send_action_func *f) { -#define N(a) (sizeof(a) / sizeof(a[0])) switch (cat) { case IEEE80211_ACTION_CAT_BA: - if (act >= N(ba_send_action)) + if (act >= nitems(ba_send_action)) break; ba_send_action[act] = f; return 0; case IEEE80211_ACTION_CAT_HT: - if (act >= N(ht_send_action)) + if (act >= nitems(ht_send_action)) break; ht_send_action[act] = f; return 0; case IEEE80211_ACTION_CAT_SELF_PROT: - if (act >= N(meshpl_send_action)) + if (act >= nitems(meshpl_send_action)) break; meshpl_send_action[act] = f; return 0; case IEEE80211_ACTION_CAT_MESH: - if (act >= N(meshaction_send_action)) + if (act >= nitems(meshaction_send_action)) break; meshaction_send_action[act] = f; return 0; break; case IEEE80211_ACTION_CAT_VENDOR: - if (act >= N(vendor_send_action)) + if (act >= nitems(vendor_send_action)) break; vendor_send_action[act] = f; return 0; } return EINVAL; -#undef N } void @@ -122,33 +120,31 @@ ieee80211_send_action_unregister(int cat, int act) int ieee80211_send_action(struct ieee80211_node *ni, int cat, int act, void *sa) { -#define N(a) (sizeof(a) / sizeof(a[0])) ieee80211_send_action_func *f = send_inval; switch (cat) { case IEEE80211_ACTION_CAT_BA: - if (act < N(ba_send_action)) + if (act < nitems(ba_send_action)) f = ba_send_action[act]; break; case IEEE80211_ACTION_CAT_HT: - if (act < N(ht_send_action)) + if (act < nitems(ht_send_action)) f = ht_send_action[act]; break; case IEEE80211_ACTION_CAT_SELF_PROT: - if (act < N(meshpl_send_action)) + if (act < nitems(meshpl_send_action)) f = meshpl_send_action[act]; break; case IEEE80211_ACTION_CAT_MESH: - if (act < N(meshaction_send_action)) + if (act < nitems(meshaction_send_action)) f = meshaction_send_action[act]; break; case IEEE80211_ACTION_CAT_VENDOR: - if (act < N(vendor_send_action)) + if (act < nitems(vendor_send_action)) f = vendor_send_action[act]; break; } return f(ni, cat, act, sa); -#undef N } static int @@ -183,36 +179,34 @@ static ieee80211_recv_action_func *vendor_recv_action[8] = { int ieee80211_recv_action_register(int cat, int act, ieee80211_recv_action_func *f) { -#define N(a) (sizeof(a) / sizeof(a[0])) switch (cat) { case IEEE80211_ACTION_CAT_BA: - if (act >= N(ba_recv_action)) + if (act >= nitems(ba_recv_action)) break; ba_recv_action[act] = f; return 0; case IEEE80211_ACTION_CAT_HT: - if (act >= N(ht_recv_action)) + if (act >= nitems(ht_recv_action)) break; ht_recv_action[act] = f; return 0; case IEEE80211_ACTION_CAT_SELF_PROT: - if (act >= N(meshpl_recv_action)) + if (act >= nitems(meshpl_recv_action)) break; meshpl_recv_action[act] = f; return 0; case IEEE80211_ACTION_CAT_MESH: - if (act >= N(meshaction_recv_action)) + if (act >= nitems(meshaction_recv_action)) break; meshaction_recv_action[act] = f; return 0; case IEEE80211_ACTION_CAT_VENDOR: - if (act >= N(vendor_recv_action)) + if (act >= nitems(vendor_recv_action)) break; vendor_recv_action[act] = f; return 0; } return EINVAL; -#undef N } void @@ -226,7 +220,6 @@ ieee80211_recv_action(struct ieee80211_node *ni, const struct ieee80211_frame *wh, const uint8_t *frm, const uint8_t *efrm) { -#define N(a) (sizeof(a) / sizeof(a[0])) ieee80211_recv_action_func *f = recv_inval; struct ieee80211vap *vap = ni->ni_vap; const struct ieee80211_action *ia = @@ -234,15 +227,15 @@ ieee80211_recv_action(struct ieee80211_node *ni, switch (ia->ia_category) { case IEEE80211_ACTION_CAT_BA: - if (ia->ia_action < N(ba_recv_action)) + if (ia->ia_action < nitems(ba_recv_action)) f = ba_recv_action[ia->ia_action]; break; case IEEE80211_ACTION_CAT_HT: - if (ia->ia_action < N(ht_recv_action)) + if (ia->ia_action < nitems(ht_recv_action)) f = ht_recv_action[ia->ia_action]; break; case IEEE80211_ACTION_CAT_SELF_PROT: - if (ia->ia_action < N(meshpl_recv_action)) + if (ia->ia_action < nitems(meshpl_recv_action)) f = meshpl_recv_action[ia->ia_action]; break; case IEEE80211_ACTION_CAT_MESH: @@ -255,14 +248,13 @@ ieee80211_recv_action(struct ieee80211_node *ni, vap->iv_stats.is_mesh_nolink++; break; } - if (ia->ia_action < N(meshaction_recv_action)) + if (ia->ia_action < nitems(meshaction_recv_action)) f = meshaction_recv_action[ia->ia_action]; break; case IEEE80211_ACTION_CAT_VENDOR: - if (ia->ia_action < N(vendor_recv_action)) + if (ia->ia_action < nitems(vendor_recv_action)) f = vendor_recv_action[ia->ia_action]; break; } return f(ni, wh, frm, efrm); -#undef N } diff --git a/sys/net80211/ieee80211_freebsd.c b/sys/net80211/ieee80211_freebsd.c index 4415b62066a..b616c4453e8 100644 --- a/sys/net80211/ieee80211_freebsd.c +++ b/sys/net80211/ieee80211_freebsd.c @@ -511,7 +511,7 @@ ieee80211_process_callback(struct ieee80211_node *ni, * (the callers will first need modifying.) */ int -ieee80211_parent_transmit(struct ieee80211com *ic, +ieee80211_parent_xmitpkt(struct ieee80211com *ic, struct mbuf *m) { struct ifnet *parent = ic->ic_ifp; @@ -528,7 +528,7 @@ ieee80211_parent_transmit(struct ieee80211com *ic, * Transmit a frame to the VAP interface. */ int -ieee80211_vap_transmit(struct ieee80211vap *vap, struct mbuf *m) +ieee80211_vap_xmitpkt(struct ieee80211vap *vap, struct mbuf *m) { struct ifnet *ifp = vap->iv_ifp; @@ -809,7 +809,8 @@ static void bpf_track(void *arg, struct ifnet *ifp, int dlt, int attach) { /* NB: identify vap's by if_start */ - if (dlt == DLT_IEEE802_11_RADIO && ifp->if_start == ieee80211_start) { + if (dlt == DLT_IEEE802_11_RADIO && + ifp->if_transmit == ieee80211_vap_transmit) { struct ieee80211vap *vap = ifp->if_softc; /* * Track bpf radiotap listener state. We mark the vap diff --git a/sys/net80211/ieee80211_freebsd.h b/sys/net80211/ieee80211_freebsd.h index 3ea117bc895..711bac34823 100644 --- a/sys/net80211/ieee80211_freebsd.h +++ b/sys/net80211/ieee80211_freebsd.h @@ -234,9 +234,25 @@ struct mbuf *ieee80211_getmgtframe(uint8_t **frm, int headroom, int pktlen); #define M_FF M_PROTO6 /* fast frame */ #define M_TXCB M_PROTO7 /* do tx complete callback */ #define M_AMPDU_MPDU M_PROTO8 /* ok for A-MPDU aggregation */ + +/* + * FreeBSD-HEAD from 1000046 retired M_*FRAG* flags and turned them + * into header flags instead. So, we use the new protocol-specific + * flags. + * + * Earlier FreeBSD versions overload M_FRAG, M_FIRSTFRAG and M_LASTFRAG. + * + * XXX TODO: rename these fields so there are no namespace clashes! + */ +#if __FreeBSD_version >= 1000046 +#define M_FRAG M_PROTO9 /* frame fragmentation */ +#define M_FIRSTFRAG M_PROTO10 /* first frame fragment */ +#define M_LASTFRAG M_PROTO11 /* last frame fragment */ +#endif + #define M_80211_TX \ - (M_FRAG|M_FIRSTFRAG|M_LASTFRAG|M_ENCAP|M_EAPOL|M_PWR_SAV|\ - M_MORE_DATA|M_FF|M_TXCB|M_AMPDU_MPDU) + (M_ENCAP|M_EAPOL|M_PWR_SAV|M_MORE_DATA|M_FF|M_TXCB| \ + M_AMPDU_MPDU|M_FRAG|M_FIRSTFRAG|M_LASTFRAG) /* rx path usage */ #define M_AMPDU M_PROTO1 /* A-MPDU subframe */ @@ -246,17 +262,22 @@ struct mbuf *ieee80211_getmgtframe(uint8_t **frm, int headroom, int pktlen); #endif #define M_80211_RX (M_AMPDU|M_WEP|M_AMPDU_MPDU) +#if __FreeBSD_version >= 1000046 #define IEEE80211_MBUF_TX_FLAG_BITS \ - "\20\1M_EXT\2M_PKTHDR\3M_EOR\4M_RDONLY\5M_ENCAP\6M_WEP\7M_EAPOL" \ - "\10M_PWR_SAV\11M_MORE_DATA\12M_BCAST\13M_MCAST\14M_FRAG\15M_FIRSTFRAG" \ - "\16M_LASTFRAG\17M_SKIP_FIREWALL\20M_FREELIST\21M_VLANTAG\22M_PROMISC" \ - "\23M_NOFREE\24M_FF\25M_TXCB\26M_AMPDU_MPDU\27M_FLOWID" + M_FLAG_BITS \ + "\15M_ENCAP\17M_EAPOL\20M_PWR_SAV\21M_MORE_DATA\22M_FF\23M_TXCB" \ + "\24M_AMPDU_MPDU\25M_FRAG\26M_FIRSTFRAG\27M_LASTFRAG" +#else +/* There aren't any flag bits available for versions before this */ +/* XXX TODO: implement M_FLAG_BITS for this! */ +#define IEEE80211_MBUF_TX_FLAG_BITS \ + "\15M_ENCAP\17M_EAPOL\20M_PWR_SAV\21M_MORE_DATA\22M_FF\23M_TXCB" \ + "\24M_AMPDU_MPDU" +#endif #define IEEE80211_MBUF_RX_FLAG_BITS \ - "\20\1M_EXT\2M_PKTHDR\3M_EOR\4M_RDONLY\5M_AMPDU\6M_WEP\7M_PROTO3" \ - "\10M_PROTO4\11M_PROTO5\12M_BCAST\13M_MCAST\14M_FRAG\15M_FIRSTFRAG" \ - "\16M_LASTFRAG\17M_SKIP_FIREWALL\20M_FREELIST\21M_VLANTAG\22M_PROMISC" \ - "\23M_NOFREE\24M_PROTO6\25M_PROTO7\26M_AMPDU_MPDU\27M_FLOWID" + M_FLAG_BITS \ + "\15M_AMPDU\16M_WEP\24M_AMPDU_MPDU" /* * Store WME access control bits in the vlan tag. @@ -297,8 +318,8 @@ int ieee80211_add_callback(struct mbuf *m, void ieee80211_process_callback(struct ieee80211_node *, struct mbuf *, int); struct ieee80211com; -int ieee80211_parent_transmit(struct ieee80211com *, struct mbuf *); -int ieee80211_vap_transmit(struct ieee80211vap *, struct mbuf *); +int ieee80211_parent_xmitpkt(struct ieee80211com *, struct mbuf *); +int ieee80211_vap_xmitpkt(struct ieee80211vap *, struct mbuf *); void get_random_bytes(void *, size_t); diff --git a/sys/net80211/ieee80211_hostap.c b/sys/net80211/ieee80211_hostap.c index fe83ebb840d..93a9b27024b 100644 --- a/sys/net80211/ieee80211_hostap.c +++ b/sys/net80211/ieee80211_hostap.c @@ -355,7 +355,12 @@ hostap_deliver_data(struct ieee80211vap *vap, struct ifnet *ifp = vap->iv_ifp; /* clear driver/net80211 flags before passing up */ +#if __FreeBSD_version >= 1000046 + m->m_flags &= ~(M_MCAST | M_BCAST); + m_clrprotoflags(m); +#else m->m_flags &= ~(M_80211_RX | M_MCAST | M_BCAST); +#endif KASSERT(vap->iv_opmode == IEEE80211_M_HOSTAP, ("gack, opmode %d", vap->iv_opmode)); @@ -412,7 +417,7 @@ hostap_deliver_data(struct ieee80211vap *vap, if (mcopy != NULL) { int len, err; len = mcopy->m_pkthdr.len; - err = ieee80211_vap_transmit(vap, mcopy); + err = ieee80211_vap_xmitpkt(vap, mcopy); if (err) { /* NB: IFQ_HANDOFF reclaims mcopy */ } else { @@ -2322,13 +2327,13 @@ ieee80211_recv_pspoll(struct ieee80211_node *ni, struct mbuf *m0) /* * Do the right thing; if it's an encap'ed frame then - * call ieee80211_parent_transmit() (and free the ref) else - * call ieee80211_vap_transmit(). + * call ieee80211_parent_xmitpkt() (and free the ref) else + * call ieee80211_vap_xmitpkt(). */ if (m->m_flags & M_ENCAP) { - if (ieee80211_parent_transmit(ic, m) != 0) + if (ieee80211_parent_xmitpkt(ic, m) != 0) ieee80211_free_node(ni); } else { - (void) ieee80211_vap_transmit(vap, m); + (void) ieee80211_vap_xmitpkt(vap, m); } } diff --git a/sys/net80211/ieee80211_hwmp.c b/sys/net80211/ieee80211_hwmp.c index 8c481da5ab3..751f1e73773 100644 --- a/sys/net80211/ieee80211_hwmp.c +++ b/sys/net80211/ieee80211_hwmp.c @@ -1467,7 +1467,7 @@ hwmp_recv_prep(struct ieee80211vap *vap, struct ieee80211_node *ni, * If the mbuf has M_ENCAP set, ensure we free it. * Note that after if_transmit() is called, m is invalid. */ - (void) ieee80211_vap_transmit(vap, m); + (void) ieee80211_vap_xmitpkt(vap, m); } #undef IS_PROXY #undef PROXIED_BY_US diff --git a/sys/net80211/ieee80211_input.c b/sys/net80211/ieee80211_input.c index 91adc6c1ec1..5e956461ce8 100644 --- a/sys/net80211/ieee80211_input.c +++ b/sys/net80211/ieee80211_input.c @@ -250,7 +250,10 @@ ieee80211_deliver_data(struct ieee80211vap *vap, struct ifnet *ifp = vap->iv_ifp; /* clear driver/net80211 flags before passing up */ - m->m_flags &= ~(M_80211_RX | M_MCAST | M_BCAST); + m->m_flags &= ~(M_MCAST | M_BCAST); +#if __FreeBSD_version >= 1000046 + m_clrprotoflags(m); +#endif /* NB: see hostap_deliver_data, this path doesn't handle hostap */ KASSERT(vap->iv_opmode != IEEE80211_M_HOSTAP, ("gack, hostap")); diff --git a/sys/net80211/ieee80211_mesh.c b/sys/net80211/ieee80211_mesh.c index 417747b2ff4..1d03c1a820b 100644 --- a/sys/net80211/ieee80211_mesh.c +++ b/sys/net80211/ieee80211_mesh.c @@ -1243,7 +1243,7 @@ mesh_forward(struct ieee80211vap *vap, struct mbuf *m, * to do here; we'll have to re-think this soon. */ IEEE80211_TX_LOCK(ic); - err = ieee80211_parent_transmit(ic, mcopy); + err = ieee80211_parent_xmitpkt(ic, mcopy); IEEE80211_TX_UNLOCK(ic); if (err != 0) { /* NB: IFQ_HANDOFF reclaims mbuf */ diff --git a/sys/net80211/ieee80211_node.c b/sys/net80211/ieee80211_node.c index a81c4810436..41692558fd9 100644 --- a/sys/net80211/ieee80211_node.c +++ b/sys/net80211/ieee80211_node.c @@ -985,7 +985,6 @@ ieee80211_ies_expand(struct ieee80211_ies *ies) static void node_cleanup(struct ieee80211_node *ni) { -#define N(a) (sizeof(a)/sizeof(a[0])) struct ieee80211vap *vap = ni->ni_vap; struct ieee80211com *ic = ni->ni_ic; int i; @@ -1052,7 +1051,7 @@ node_cleanup(struct ieee80211_node *ni) * * XXX does this leave us open to inheriting old state? */ - for (i = 0; i < N(ni->ni_rxfrag); i++) + for (i = 0; i < nitems(ni->ni_rxfrag); i++) if (ni->ni_rxfrag[i] != NULL) { m_freem(ni->ni_rxfrag[i]); ni->ni_rxfrag[i] = NULL; @@ -1061,7 +1060,6 @@ node_cleanup(struct ieee80211_node *ni) * Must be careful here to remove any key map entry w/o a LOR. */ ieee80211_node_delucastkey(ni); -#undef N } static void diff --git a/sys/net80211/ieee80211_output.c b/sys/net80211/ieee80211_output.c index 3be382cf277..a7aa7d9e902 100644 --- a/sys/net80211/ieee80211_output.c +++ b/sys/net80211/ieee80211_output.c @@ -210,8 +210,7 @@ ieee80211_vap_pkt_send_dest(struct ieee80211vap *vap, struct mbuf *m, m = ieee80211_ff_check(ni, m); if (m == NULL) { /* NB: any ni ref held on stageq */ - /* XXX better status? */ - return (ENOBUFS); + return (0); } } #endif /* IEEE80211_SUPPORT_SUPERG */ @@ -236,7 +235,7 @@ ieee80211_vap_pkt_send_dest(struct ieee80211vap *vap, struct mbuf *m, return (ENOBUFS); } } - error = ieee80211_parent_transmit(ic, m); + error = ieee80211_parent_xmitpkt(ic, m); /* * Unlock at this point - no need to hold it across @@ -396,14 +395,16 @@ ieee80211_start_pkt(struct ieee80211vap *vap, struct mbuf *m) * Start method for vap's. All packets from the stack come * through here. We handle common processing of the packets * before dispatching them to the underlying device. + * + * if_transmit() requires that the mbuf be consumed by this call + * regardless of the return condition. */ -void -ieee80211_start(struct ifnet *ifp) +int +ieee80211_vap_transmit(struct ifnet *ifp, struct mbuf *m) { struct ieee80211vap *vap = ifp->if_softc; struct ieee80211com *ic = vap->iv_ic; struct ifnet *parent = ic->ic_ifp; - struct mbuf *m; /* NB: parent must be up and running */ if (!IFNET_IS_UP_RUNNING(parent)) { @@ -411,14 +412,16 @@ ieee80211_start(struct ifnet *ifp) "%s: ignore queue, parent %s not up+running\n", __func__, parent->if_xname); /* XXX stat */ - return; + m_freem(m); + return (EINVAL); } if (vap->iv_state == IEEE80211_S_SLEEP) { /* * In power save, wakeup device for transmit. */ ieee80211_new_state(vap, IEEE80211_S_RUN, 0); - return; + m_freem(m); + return (0); } /* * No data frames go out unless we're running. @@ -435,34 +438,36 @@ ieee80211_start(struct ifnet *ifp) __func__, ieee80211_state_name[vap->iv_state]); vap->iv_stats.is_tx_badstate++; IEEE80211_UNLOCK(ic); - IFQ_LOCK(&ifp->if_snd); ifp->if_drv_flags |= IFF_DRV_OACTIVE; - IFQ_UNLOCK(&ifp->if_snd); - return; + m_freem(m); + return (EINVAL); } IEEE80211_UNLOCK(ic); } - for (;;) { - IFQ_DEQUEUE(&ifp->if_snd, m); - if (m == NULL) - break; - /* - * Sanitize mbuf flags for net80211 use. We cannot - * clear M_PWR_SAV or M_MORE_DATA because these may - * be set for frames that are re-submitted from the - * power save queue. - * - * NB: This must be done before ieee80211_classify as - * it marks EAPOL in frames with M_EAPOL. - */ - m->m_flags &= ~(M_80211_TX - M_PWR_SAV - M_MORE_DATA); - /* - * Bump to the packet transmission path. - */ - (void) ieee80211_start_pkt(vap, m); - /* mbuf is consumed here */ - } + /* + * Sanitize mbuf flags for net80211 use. We cannot + * clear M_PWR_SAV or M_MORE_DATA because these may + * be set for frames that are re-submitted from the + * power save queue. + * + * NB: This must be done before ieee80211_classify as + * it marks EAPOL in frames with M_EAPOL. + */ + m->m_flags &= ~(M_80211_TX - M_PWR_SAV - M_MORE_DATA); + + /* + * Bump to the packet transmission path. + * The mbuf will be consumed here. + */ + return (ieee80211_start_pkt(vap, m)); +} + +void +ieee80211_vap_qflush(struct ifnet *ifp) +{ + + /* Empty for now */ } /* @@ -482,9 +487,15 @@ ieee80211_raw_output(struct ieee80211vap *vap, struct ieee80211_node *ni, * connect bpf write calls to the 802.11 layer for injecting * raw 802.11 frames. */ +#if __FreeBSD_version >= 1000031 int ieee80211_output(struct ifnet *ifp, struct mbuf *m, const struct sockaddr *dst, struct route *ro) +#else +int +ieee80211_output(struct ifnet *ifp, struct mbuf *m, + struct sockaddr *dst, struct route *ro) +#endif { #define senderr(e) do { error = (e); goto bad;} while (0) struct ieee80211_node *ni = NULL; @@ -494,9 +505,7 @@ ieee80211_output(struct ifnet *ifp, struct mbuf *m, int error; int ret; - IFQ_LOCK(&ifp->if_snd); if (ifp->if_drv_flags & IFF_DRV_OACTIVE) { - IFQ_UNLOCK(&ifp->if_snd); /* * Short-circuit requests if the vap is marked OACTIVE * as this can happen because a packet came down through @@ -507,7 +516,6 @@ ieee80211_output(struct ifnet *ifp, struct mbuf *m, */ senderr(ENETDOWN); } - IFQ_UNLOCK(&ifp->if_snd); vap = ifp->if_softc; ic = vap->iv_ic; /* @@ -3308,3 +3316,71 @@ ieee80211_beacon_update(struct ieee80211_node *ni, return len_changed; } + +/* + * Do Ethernet-LLC encapsulation for each payload in a fast frame + * tunnel encapsulation. The frame is assumed to have an Ethernet + * header at the front that must be stripped before prepending the + * LLC followed by the Ethernet header passed in (with an Ethernet + * type that specifies the payload size). + */ +struct mbuf * +ieee80211_ff_encap1(struct ieee80211vap *vap, struct mbuf *m, + const struct ether_header *eh) +{ + struct llc *llc; + uint16_t payload; + + /* XXX optimize by combining m_adj+M_PREPEND */ + m_adj(m, sizeof(struct ether_header) - sizeof(struct llc)); + llc = mtod(m, struct llc *); + llc->llc_dsap = llc->llc_ssap = LLC_SNAP_LSAP; + llc->llc_control = LLC_UI; + llc->llc_snap.org_code[0] = 0; + llc->llc_snap.org_code[1] = 0; + llc->llc_snap.org_code[2] = 0; + llc->llc_snap.ether_type = eh->ether_type; + payload = m->m_pkthdr.len; /* NB: w/o Ethernet header */ + + M_PREPEND(m, sizeof(struct ether_header), M_NOWAIT); + if (m == NULL) { /* XXX cannot happen */ + IEEE80211_DPRINTF(vap, IEEE80211_MSG_SUPERG, + "%s: no space for ether_header\n", __func__); + vap->iv_stats.is_tx_nobuf++; + return NULL; + } + ETHER_HEADER_COPY(mtod(m, void *), eh); + mtod(m, struct ether_header *)->ether_type = htons(payload); + return m; +} + +/* + * Complete an mbuf transmission. + * + * For now, this simply processes a completed frame after the + * driver has completed it's transmission and/or retransmission. + * It assumes the frame is an 802.11 encapsulated frame. + * + * Later on it will grow to become the exit path for a given frame + * from the driver and, depending upon how it's been encapsulated + * and already transmitted, it may end up doing A-MPDU retransmission, + * power save requeuing, etc. + * + * In order for the above to work, the driver entry point to this + * must not hold any driver locks. Thus, the driver needs to delay + * any actual mbuf completion until it can release said locks. + * + * This frees the mbuf and if the mbuf has a node reference, + * the node reference will be freed. + */ +void +ieee80211_tx_complete(struct ieee80211_node *ni, struct mbuf *m, int status) +{ + + if (ni != NULL) { + if (m->m_flags & M_TXCB) + ieee80211_process_callback(ni, m, status); + ieee80211_free_node(ni); + } + m_freem(m); +} diff --git a/sys/net80211/ieee80211_phy.c b/sys/net80211/ieee80211_phy.c index d1924d83dac..923266ca373 100644 --- a/sys/net80211/ieee80211_phy.c +++ b/sys/net80211/ieee80211_phy.c @@ -294,13 +294,12 @@ static struct ieee80211_rate_table ieee80211_11na_table = { static void ieee80211_setup_ratetable(struct ieee80211_rate_table *rt) { -#define N(a) (sizeof(a)/sizeof(a[0])) #define WLAN_CTRL_FRAME_SIZE \ (sizeof(struct ieee80211_frame_ack) + IEEE80211_CRC_LEN) int i; - for (i = 0; i < N(rt->rateCodeToIndex); i++) + for (i = 0; i < nitems(rt->rateCodeToIndex); i++) rt->rateCodeToIndex[i] = (uint8_t) -1; for (i = 0; i < rt->rateCount; i++) { uint8_t code = rt->info[i].dot11Rate; @@ -341,14 +340,12 @@ ieee80211_setup_ratetable(struct ieee80211_rate_table *rt) } #undef WLAN_CTRL_FRAME_SIZE -#undef N } /* Setup all rate tables */ static void ieee80211_phy_init(void) { -#define N(arr) (int)(sizeof(arr) / sizeof(arr[0])) static struct ieee80211_rate_table * const ratetables[] = { &ieee80211_half_table, &ieee80211_quarter_table, @@ -362,10 +359,9 @@ ieee80211_phy_init(void) }; int i; - for (i = 0; i < N(ratetables); ++i) + for (i = 0; i < nitems(ratetables); ++i) ieee80211_setup_ratetable(ratetables[i]); -#undef N } SYSINIT(wlan_phy, SI_SUB_DRIVERS, SI_ORDER_FIRST, ieee80211_phy_init, NULL); diff --git a/sys/net80211/ieee80211_power.c b/sys/net80211/ieee80211_power.c index 995aa884951..4542ec59da0 100644 --- a/sys/net80211/ieee80211_power.c +++ b/sys/net80211/ieee80211_power.c @@ -456,6 +456,7 @@ pwrsave_flushq(struct ieee80211_node *ni) while (parent_q != NULL) { m = parent_q; parent_q = m->m_nextpkt; + m->m_nextpkt = NULL; /* must be encapsulated */ KASSERT((m->m_flags & M_ENCAP), ("%s: parentq with non-M_ENCAP frame!\n", @@ -464,7 +465,7 @@ pwrsave_flushq(struct ieee80211_node *ni) * For encaped frames, we need to free the node * reference upon failure. */ - if (ieee80211_parent_transmit(ic, m) != 0) + if (ieee80211_parent_xmitpkt(ic, m) != 0) ieee80211_free_node(ni); } } @@ -474,9 +475,10 @@ pwrsave_flushq(struct ieee80211_node *ni) while (ifp_q != NULL) { m = ifp_q; ifp_q = m->m_nextpkt; + m->m_nextpkt = NULL; KASSERT((!(m->m_flags & M_ENCAP)), ("%s: vapq with M_ENCAP frame!\n", __func__)); - (void) ieee80211_vap_transmit(vap, m); + (void) ieee80211_vap_xmitpkt(vap, m); } } } diff --git a/sys/net80211/ieee80211_proto.c b/sys/net80211/ieee80211_proto.c index 3a2b5c72254..605c77102c6 100644 --- a/sys/net80211/ieee80211_proto.c +++ b/sys/net80211/ieee80211_proto.c @@ -662,13 +662,12 @@ ieee80211_set_shortslottime(struct ieee80211com *ic, int onoff) int ieee80211_iserp_rateset(const struct ieee80211_rateset *rs) { -#define N(a) (sizeof(a) / sizeof(a[0])) static const int rates[] = { 2, 4, 11, 22, 12, 24, 48 }; int i, j; - if (rs->rs_nrates < N(rates)) + if (rs->rs_nrates < nitems(rates)) return 0; - for (i = 0; i < N(rates); i++) { + for (i = 0; i < nitems(rates); i++) { for (j = 0; j < rs->rs_nrates; j++) { int r = rs->rs_rates[j] & IEEE80211_RATE_VAL; if (rates[i] == r) @@ -681,7 +680,6 @@ ieee80211_iserp_rateset(const struct ieee80211_rateset *rs) ; } return 1; -#undef N } /* @@ -1784,15 +1782,11 @@ ieee80211_newstate_cb(void *xvap, int npending) * Note this can also happen as a result of SLEEP->RUN * (i.e. coming out of power save mode). */ - IF_LOCK(&vap->iv_ifp->if_snd); vap->iv_ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; - IF_UNLOCK(&vap->iv_ifp->if_snd); /* - * XXX Kick-start a VAP queue - this should be a method, - * not if_start()! + * XXX TODO Kick-start a VAP queue - this should be a method! */ - if_start(vap->iv_ifp); /* bring up any vaps waiting on us */ wakeupwaiting(vap); @@ -1805,8 +1799,9 @@ ieee80211_newstate_cb(void *xvap, int npending) */ ieee80211_scan_flush(vap); - /* XXX NB: cast for altq */ - ieee80211_flush_ifq((struct ifqueue *)&ic->ic_ifp->if_snd, vap); + /* + * XXX TODO: ic/vap queue flush + */ } done: IEEE80211_UNLOCK(ic); diff --git a/sys/net80211/ieee80211_proto.h b/sys/net80211/ieee80211_proto.h index 94dd097649e..5ec845938ac 100644 --- a/sys/net80211/ieee80211_proto.h +++ b/sys/net80211/ieee80211_proto.h @@ -96,8 +96,13 @@ int ieee80211_mgmt_output(struct ieee80211_node *, struct mbuf *, int, struct ieee80211_bpf_params *); int ieee80211_raw_xmit(struct ieee80211_node *, struct mbuf *, const struct ieee80211_bpf_params *); +#if __FreeBSD_version >= 1000031 int ieee80211_output(struct ifnet *, struct mbuf *, const struct sockaddr *, struct route *ro); +#else +int ieee80211_output(struct ifnet *, struct mbuf *, + struct sockaddr *, struct route *ro); +#endif int ieee80211_vap_pkt_send_dest(struct ieee80211vap *, struct mbuf *, struct ieee80211_node *); int ieee80211_raw_output(struct ieee80211vap *, struct ieee80211_node *, @@ -105,7 +110,8 @@ int ieee80211_raw_output(struct ieee80211vap *, struct ieee80211_node *, void ieee80211_send_setup(struct ieee80211_node *, struct mbuf *, int, int, const uint8_t [IEEE80211_ADDR_LEN], const uint8_t [IEEE80211_ADDR_LEN], const uint8_t [IEEE80211_ADDR_LEN]); -void ieee80211_start(struct ifnet *ifp); +int ieee80211_vap_transmit(struct ifnet *ifp, struct mbuf *m); +void ieee80211_vap_qflush(struct ifnet *ifp); int ieee80211_send_nulldata(struct ieee80211_node *); int ieee80211_classify(struct ieee80211_node *, struct mbuf *m); struct mbuf *ieee80211_mbuf_adjust(struct ieee80211vap *, int, @@ -119,6 +125,11 @@ int ieee80211_send_probereq(struct ieee80211_node *ni, const uint8_t da[IEEE80211_ADDR_LEN], const uint8_t bssid[IEEE80211_ADDR_LEN], const uint8_t *ssid, size_t ssidlen); +struct mbuf * ieee80211_ff_encap1(struct ieee80211vap *, struct mbuf *, + const struct ether_header *); +void ieee80211_tx_complete(struct ieee80211_node *, + struct mbuf *, int); + /* * The formation of ProbeResponse frames requires guidance to * deal with legacy clients. When the client is identified as diff --git a/sys/net80211/ieee80211_scan_sta.c b/sys/net80211/ieee80211_scan_sta.c index 381facab7ae..c791ea1ef99 100644 --- a/sys/net80211/ieee80211_scan_sta.c +++ b/sys/net80211/ieee80211_scan_sta.c @@ -450,13 +450,12 @@ add_channels(struct ieee80211vap *vap, struct ieee80211_scan_state *ss, enum ieee80211_phymode mode, const uint16_t freq[], int nfreq) { -#define N(a) (sizeof(a) / sizeof(a[0])) struct ieee80211com *ic = vap->iv_ic; struct ieee80211_channel *c, *cg; u_int modeflags; int i; - KASSERT(mode < N(chanflags), ("Unexpected mode %u", mode)); + KASSERT(mode < nitems(chanflags), ("Unexpected mode %u", mode)); modeflags = chanflags[mode]; for (i = 0; i < nfreq; i++) { if (ss->ss_last >= IEEE80211_SCAN_MAX) @@ -476,7 +475,6 @@ add_channels(struct ieee80211vap *vap, } ss->ss_chans[ss->ss_last++] = c; } -#undef N } struct scanlist { diff --git a/sys/net80211/ieee80211_superg.c b/sys/net80211/ieee80211_superg.c index d8083c2c633..21f3b995c0e 100644 --- a/sys/net80211/ieee80211_superg.c +++ b/sys/net80211/ieee80211_superg.c @@ -329,43 +329,6 @@ ieee80211_ff_decap(struct ieee80211_node *ni, struct mbuf *m) #undef FF_LLC_SIZE } -/* - * Do Ethernet-LLC encapsulation for each payload in a fast frame - * tunnel encapsulation. The frame is assumed to have an Ethernet - * header at the front that must be stripped before prepending the - * LLC followed by the Ethernet header passed in (with an Ethernet - * type that specifies the payload size). - */ -static struct mbuf * -ff_encap1(struct ieee80211vap *vap, struct mbuf *m, - const struct ether_header *eh) -{ - struct llc *llc; - uint16_t payload; - - /* XXX optimize by combining m_adj+M_PREPEND */ - m_adj(m, sizeof(struct ether_header) - sizeof(struct llc)); - llc = mtod(m, struct llc *); - llc->llc_dsap = llc->llc_ssap = LLC_SNAP_LSAP; - llc->llc_control = LLC_UI; - llc->llc_snap.org_code[0] = 0; - llc->llc_snap.org_code[1] = 0; - llc->llc_snap.org_code[2] = 0; - llc->llc_snap.ether_type = eh->ether_type; - payload = m->m_pkthdr.len; /* NB: w/o Ethernet header */ - - M_PREPEND(m, sizeof(struct ether_header), M_NOWAIT); - if (m == NULL) { /* XXX cannot happen */ - IEEE80211_DPRINTF(vap, IEEE80211_MSG_SUPERG, - "%s: no space for ether_header\n", __func__); - vap->iv_stats.is_tx_nobuf++; - return NULL; - } - ETHER_HEADER_COPY(mtod(m, void *), eh); - mtod(m, struct ether_header *)->ether_type = htons(payload); - return m; -} - /* * Fast frame encapsulation. There must be two packets * chained with m_nextpkt. We do header adjustment for @@ -424,10 +387,10 @@ ieee80211_ff_encap(struct ieee80211vap *vap, struct mbuf *m1, int hdrspace, * Now do tunnel encapsulation. First, each * frame gets a standard encapsulation. */ - m1 = ff_encap1(vap, m1, &eh1); + m1 = ieee80211_ff_encap1(vap, m1, &eh1); if (m1 == NULL) goto bad; - m2 = ff_encap1(vap, m2, &eh2); + m2 = ieee80211_ff_encap1(vap, m2, &eh2); if (m2 == NULL) goto bad; @@ -511,7 +474,7 @@ ff_transmit(struct ieee80211_node *ni, struct mbuf *m) if (m != NULL) { struct ifnet *ifp = vap->iv_ifp; - error = ieee80211_parent_transmit(ic, m);; + error = ieee80211_parent_xmitpkt(ic, m);; if (error != 0) { /* NB: IFQ_HANDOFF reclaims mbuf */ ieee80211_free_node(ni); diff --git a/sys/net80211/ieee80211_tdma.c b/sys/net80211/ieee80211_tdma.c index ece82ff64b3..08acc3897c5 100644 --- a/sys/net80211/ieee80211_tdma.c +++ b/sys/net80211/ieee80211_tdma.c @@ -744,7 +744,7 @@ tdma_ioctl_get80211(struct ieee80211vap *vap, struct ieee80211req *ireq) struct ieee80211_tdma_state *ts = vap->iv_tdma; if ((vap->iv_caps & IEEE80211_C_TDMA) == 0) - return EOPNOTSUPP; + return ENOSYS; switch (ireq->i_type) { case IEEE80211_IOC_TDMA_SLOT: @@ -772,7 +772,7 @@ tdma_ioctl_set80211(struct ieee80211vap *vap, struct ieee80211req *ireq) struct ieee80211_tdma_state *ts = vap->iv_tdma; if ((vap->iv_caps & IEEE80211_C_TDMA) == 0) - return EOPNOTSUPP; + return ENOSYS; switch (ireq->i_type) { case IEEE80211_IOC_TDMA_SLOT: diff --git a/sys/net80211/ieee80211_var.h b/sys/net80211/ieee80211_var.h index 5c88924bf40..174fabca829 100644 --- a/sys/net80211/ieee80211_var.h +++ b/sys/net80211/ieee80211_var.h @@ -496,8 +496,13 @@ struct ieee80211vap { int (*iv_newstate)(struct ieee80211vap *, enum ieee80211_state, int); /* 802.3 output method for raw frame xmit */ +#if __FreeBSD_version >= 1000031 int (*iv_output)(struct ifnet *, struct mbuf *, const struct sockaddr *, struct route *); +#else + int (*iv_output)(struct ifnet *, struct mbuf *, + struct sockaddr *, struct route *); +#endif uint64_t iv_spare[6]; }; MALLOC_DECLARE(M_80211_VAP); diff --git a/sys/net80211/ieee80211_wds.c b/sys/net80211/ieee80211_wds.c index ad88a05eff5..8e0cb06e6ef 100644 --- a/sys/net80211/ieee80211_wds.c +++ b/sys/net80211/ieee80211_wds.c @@ -295,7 +295,7 @@ ieee80211_dwds_mcast(struct ieee80211vap *vap0, struct mbuf *m) mcopy->m_flags |= M_MCAST; mcopy->m_pkthdr.rcvif = (void *) ni; - err = ieee80211_parent_transmit(ic, mcopy); + err = ieee80211_parent_xmitpkt(ic, mcopy); if (err) { /* NB: IFQ_HANDOFF reclaims mbuf */ ifp->if_oerrors++; diff --git a/sys/netgraph/bluetooth/drivers/ubt/ng_ubt.c b/sys/netgraph/bluetooth/drivers/ubt/ng_ubt.c index b268409eda7..edbd32ccd5a 100644 --- a/sys/netgraph/bluetooth/drivers/ubt/ng_ubt.c +++ b/sys/netgraph/bluetooth/drivers/ubt/ng_ubt.c @@ -437,6 +437,67 @@ static const STRUCT_USB_HOST_ID ubt_devs[] = USB_IFACE_CLASS(UICLASS_VENDOR), USB_IFACE_SUBCLASS(UDSUBCLASS_RF), USB_IFACE_PROTOCOL(UDPROTO_BLUETOOTH) }, + + /* Apple-specific (Broadcom) devices */ + { USB_VENDOR(USB_VENDOR_APPLE), + USB_IFACE_CLASS(UICLASS_VENDOR), + USB_IFACE_SUBCLASS(UDSUBCLASS_RF), + USB_IFACE_PROTOCOL(UDPROTO_BLUETOOTH) }, + + /* Foxconn - Hon Hai */ + { USB_VENDOR(USB_VENDOR_FOXCONN), + USB_IFACE_CLASS(UICLASS_VENDOR), + USB_IFACE_SUBCLASS(UDSUBCLASS_RF), + USB_IFACE_PROTOCOL(UDPROTO_BLUETOOTH) }, + + /* MediaTek MT76x0E */ + { USB_VPI(USB_VENDOR_MEDIATEK, 0x763f, 0) }, + + /* Broadcom SoftSailing reporting vendor specific */ + { USB_VPI(USB_VENDOR_BROADCOM, 0x21e1, 0) }, + + /* Apple MacBookPro 7,1 */ + { USB_VPI(USB_VENDOR_APPLE, 0x8213, 0) }, + + /* Apple iMac11,1 */ + { USB_VPI(USB_VENDOR_APPLE, 0x8215, 0) }, + + /* Apple MacBookPro6,2 */ + { USB_VPI(USB_VENDOR_APPLE, 0x8218, 0) }, + + /* Apple MacBookAir3,1, MacBookAir3,2 */ + { USB_VPI(USB_VENDOR_APPLE, 0x821b, 0) }, + + /* Apple MacBookAir4,1 */ + { USB_VPI(USB_VENDOR_APPLE, 0x821f, 0) }, + + /* MacBookAir6,1 */ + { USB_VPI(USB_VENDOR_APPLE, 0x828f, 0) }, + + /* Apple MacBookPro8,2 */ + { USB_VPI(USB_VENDOR_APPLE, 0x821a, 0) }, + + /* Apple MacMini5,1 */ + { USB_VPI(USB_VENDOR_APPLE, 0x8281, 0) }, + + /* Bluetooth Ultraport Module from IBM */ + { USB_VPI(USB_VENDOR_TDK, 0x030a, 0) }, + + /* ALPS Modules with non-standard ID */ + { USB_VPI(USB_VENDOR_ALPS, 0x3001, 0) }, + { USB_VPI(USB_VENDOR_ALPS, 0x3002, 0) }, + + { USB_VPI(USB_VENDOR_ERICSSON2, 0x1002, 0) }, + + /* Canyon CN-BTU1 with HID interfaces */ + { USB_VPI(USB_VENDOR_CANYON, 0x0000, 0) }, + + /* Broadcom BCM20702A0 */ + { USB_VPI(USB_VENDOR_ASUS, 0x17b5, 0) }, + { USB_VPI(USB_VENDOR_ASUS, 0x17cb, 0) }, + { USB_VPI(USB_VENDOR_LITEON, 0x2003, 0) }, + { USB_VPI(USB_VENDOR_FOXCONN, 0xe042, 0) }, + { USB_VPI(USB_VENDOR_DELL, 0x8197, 0) }, }; /* diff --git a/sys/netgraph/netflow/ng_netflow.h b/sys/netgraph/netflow/ng_netflow.h index 875a75d2e82..0d708b7a883 100644 --- a/sys/netgraph/netflow/ng_netflow.h +++ b/sys/netgraph/netflow/ng_netflow.h @@ -416,7 +416,7 @@ struct netflow { * indexed by hash hash. Each hash element consist of tailqueue * head and mutex to protect this element. */ -#define CACHESIZE (65536*4) +#define CACHESIZE (65536*16) #define CACHELOWAT (CACHESIZE * 3/4) #define CACHEHIGHWAT (CACHESIZE * 9/10) uma_zone_t zone; diff --git a/sys/netgraph/ng_iface.c b/sys/netgraph/ng_iface.c index e6ac5085368..72fc16201df 100644 --- a/sys/netgraph/ng_iface.c +++ b/sys/netgraph/ng_iface.c @@ -774,9 +774,8 @@ ng_iface_rcvdata(hook_p hook, item_p item) m_freem(m); return (EAFNOSUPPORT); } - /* First chunk of an mbuf contains good junk */ if (harvest.point_to_point) - random_harvest(m, 16, 3, 0, RANDOM_NET); + random_harvest(&(m->m_data), 12, 3, 0, RANDOM_NET_NG); M_SETFIB(m, ifp->if_fib); netisr_dispatch(isr, m); return (0); diff --git a/sys/netinet/icmp_var.h b/sys/netinet/icmp_var.h index 809879db9d7..ae869e67e40 100644 --- a/sys/netinet/icmp_var.h +++ b/sys/netinet/icmp_var.h @@ -85,13 +85,6 @@ void kmod_icmpstat_inc(int statnum); #define ICMPCTL_ICMPLIM 3 #define ICMPCTL_MAXID 4 -#define ICMPCTL_NAMES { \ - { 0, 0 }, \ - { "maskrepl", CTLTYPE_INT }, \ - { "stats", CTLTYPE_STRUCT }, \ - { "icmplim", CTLTYPE_INT }, \ -} - #ifdef _KERNEL SYSCTL_DECL(_net_inet_icmp); diff --git a/sys/netinet/if_ether.c b/sys/netinet/if_ether.c index 675e0ddadc9..9660edc42d9 100644 --- a/sys/netinet/if_ether.c +++ b/sys/netinet/if_ether.c @@ -281,6 +281,7 @@ arprequest(struct ifnet *ifp, const struct in_addr *sip, sa.sa_family = AF_ARP; sa.sa_len = 2; m->m_flags |= M_BCAST; + m_clrprotoflags(m); /* Avoid confusing lower layers. */ (*ifp->if_output)(ifp, m, &sa, NULL); ARPSTAT_INC(txrequests); } @@ -784,6 +785,8 @@ match: for (; m_hold != NULL; m_hold = m_hold_next) { m_hold_next = m_hold->m_nextpkt; m_hold->m_nextpkt = NULL; + /* Avoid confusing lower layers. */ + m_clrprotoflags(m_hold); (*ifp->if_output)(ifp, m_hold, &sa, NULL); } } else @@ -888,6 +891,7 @@ reply: m->m_pkthdr.rcvif = NULL; sa.sa_family = AF_ARP; sa.sa_len = 2; + m_clrprotoflags(m); /* Avoid confusing lower layers. */ (*ifp->if_output)(ifp, m, &sa, NULL); ARPSTAT_INC(txreplies); return; diff --git a/sys/netinet/igmp.c b/sys/netinet/igmp.c index 725e4ec159e..9f31a191c75 100644 --- a/sys/netinet/igmp.c +++ b/sys/netinet/igmp.c @@ -289,7 +289,7 @@ igmp_save_context(struct mbuf *m, struct ifnet *ifp) { #ifdef VIMAGE - m->m_pkthdr.header = ifp->if_vnet; + m->m_pkthdr.PH_loc.ptr = ifp->if_vnet; #endif /* VIMAGE */ m->m_pkthdr.flowid = ifp->if_index; } @@ -298,7 +298,7 @@ static __inline void igmp_scrub_context(struct mbuf *m) { - m->m_pkthdr.header = NULL; + m->m_pkthdr.PH_loc.ptr = NULL; m->m_pkthdr.flowid = 0; } @@ -326,7 +326,7 @@ igmp_restore_context(struct mbuf *m) #ifdef notyet #if defined(VIMAGE) && defined(INVARIANTS) - KASSERT(curvnet == (m->m_pkthdr.header), + KASSERT(curvnet == (m->m_pkthdr.PH_loc.ptr), ("%s: called when curvnet was not restored", __func__)); #endif #endif @@ -3403,7 +3403,7 @@ igmp_intr(struct mbuf *m) * indexes to guard against interface detach, they are * unique to each VIMAGE and must be retrieved. */ - CURVNET_SET((struct vnet *)(m->m_pkthdr.header)); + CURVNET_SET((struct vnet *)(m->m_pkthdr.PH_loc.ptr)); ifindex = igmp_restore_context(m); /* @@ -3450,7 +3450,7 @@ igmp_intr(struct mbuf *m) } igmp_scrub_context(m0); - m->m_flags &= ~(M_PROTOFLAGS); + m_clrprotoflags(m); m0->m_pkthdr.rcvif = V_loif; #ifdef MAC mac_netinet_igmp_send(ifp, m0); diff --git a/sys/netinet/igmp_var.h b/sys/netinet/igmp_var.h index ca17158f1ae..cd10d7fb694 100644 --- a/sys/netinet/igmp_var.h +++ b/sys/netinet/igmp_var.h @@ -218,8 +218,4 @@ SYSCTL_DECL(_net_inet_igmp); #define IGMPCTL_STATS 1 /* statistics (read-only) */ #define IGMPCTL_MAXID 2 -#define IGMPCTL_NAMES { \ - { 0, 0 }, \ - { "stats", CTLTYPE_STRUCT } \ -} #endif diff --git a/sys/netinet/in.h b/sys/netinet/in.h index 9f3b294cd42..db9a59032d4 100644 --- a/sys/netinet/in.h +++ b/sys/netinet/in.h @@ -699,24 +699,6 @@ int getsourcefilter(int, uint32_t, struct sockaddr *, socklen_t, #define IPCTL_GIF_TTL 16 /* default TTL for gif encap packet */ #define IPCTL_MAXID 17 -#define IPCTL_NAMES { \ - { 0, 0 }, \ - { "forwarding", CTLTYPE_INT }, \ - { "redirect", CTLTYPE_INT }, \ - { "ttl", CTLTYPE_INT }, \ - { "mtu", CTLTYPE_INT }, \ - { "rtexpire", CTLTYPE_INT }, \ - { "rtminexpire", CTLTYPE_INT }, \ - { "rtmaxcache", CTLTYPE_INT }, \ - { "sourceroute", CTLTYPE_INT }, \ - { "directed-broadcast", CTLTYPE_INT }, \ - { "intr-queue-maxlen", CTLTYPE_INT }, \ - { "intr-queue-drops", CTLTYPE_INT }, \ - { "stats", CTLTYPE_STRUCT }, \ - { "accept_sourceroute", CTLTYPE_INT }, \ - { "fastforwarding", CTLTYPE_INT }, \ -} - #endif /* __BSD_VISIBLE */ #ifdef _KERNEL diff --git a/sys/netinet/in_kdtrace.c b/sys/netinet/in_kdtrace.c new file mode 100644 index 00000000000..85a5a4ead49 --- /dev/null +++ b/sys/netinet/in_kdtrace.c @@ -0,0 +1,127 @@ +/*- + * Copyright (c) 2013 Mark Johnston + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + * $FreeBSD$ + */ + +#include +__FBSDID("$FreeBSD$"); + +#include "opt_kdtrace.h" + +#include +#include +#include + +SDT_PROVIDER_DEFINE(ip); +SDT_PROVIDER_DEFINE(tcp); +SDT_PROVIDER_DEFINE(udp); + +SDT_PROBE_DEFINE6_XLATE(ip, , , receive, receive, + "void *", "pktinfo_t *", + "void *", "csinfo_t *", + "uint8_t *", "ipinfo_t *", + "struct ifnet *", "ifinfo_t *", + "struct ip *", "ipv4info_t *", + "struct ip6_hdr *", "ipv6info_t *"); + +SDT_PROBE_DEFINE6_XLATE(ip, , , send, send, + "void *", "pktinfo_t *", + "void *", "csinfo_t *", + "uint8_t *", "ipinfo_t *", + "struct ifnet *", "ifinfo_t *", + "struct ip *", "ipv4info_t *", + "struct ip6_hdr *", "ipv6info_t *"); + +SDT_PROBE_DEFINE5_XLATE(tcp, , , accept_established, accept-established, + "void *", "pktinfo_t *", + "struct tcpcb *", "csinfo_t *", + "uint8_t *", "ipinfo_t *", + "struct tcpcb *", "tcpsinfo_t *" , + "struct tcphdr *", "tcpinfo_t *"); + +SDT_PROBE_DEFINE5_XLATE(tcp, , , accept_refused, accept-refused, + "void *", "pktinfo_t *", + "struct tcpcb *", "csinfo_t *", + "uint8_t *", "ipinfo_t *", + "struct tcpcb *", "tcpsinfo_t *" , + "struct tcphdr *", "tcpinfo_t *"); + +SDT_PROBE_DEFINE5_XLATE(tcp, , , connect_established, connect-established, + "void *", "pktinfo_t *", + "struct tcpcb *", "csinfo_t *", + "uint8_t *", "ipinfo_t *", + "struct tcpcb *", "tcpsinfo_t *" , + "struct tcphdr *", "tcpinfo_t *"); + +SDT_PROBE_DEFINE5_XLATE(tcp, , , connect_refused, connect-refused, + "void *", "pktinfo_t *", + "struct tcpcb *", "csinfo_t *", + "uint8_t *", "ipinfo_t *", + "struct tcpcb *", "tcpsinfo_t *" , + "struct tcphdr *", "tcpinfo_t *"); + +SDT_PROBE_DEFINE5_XLATE(tcp, , , connect_request, connect-request, + "void *", "pktinfo_t *", + "struct tcpcb *", "csinfo_t *", + "uint8_t *", "ipinfo_t *", + "struct tcpcb *", "tcpsinfo_t *" , + "struct tcphdr *", "tcpinfo_t *"); + +SDT_PROBE_DEFINE5_XLATE(tcp, , , receive, receive, + "void *", "pktinfo_t *", + "struct tcpcb *", "csinfo_t *", + "uint8_t *", "ipinfo_t *", + "struct tcpcb *", "tcpsinfo_t *" , + "struct tcphdr *", "tcpinfo_t *"); + +SDT_PROBE_DEFINE5_XLATE(tcp, , , send, send, + "void *", "pktinfo_t *", + "struct tcpcb *", "csinfo_t *", + "uint8_t *", "ipinfo_t *", + "struct tcpcb *", "tcpsinfo_t *" , + "struct tcphdr *", "tcpinfo_t *"); + +SDT_PROBE_DEFINE6_XLATE(tcp, , , state_change, state-change, + "void *", "void *", + "struct tcpcb *", "csinfo_t *", + "void *", "void *", + "struct tcpcb *", "tcpsinfo_t *", + "void *", "void *", + "int", "tcplsinfo_t *"); + +SDT_PROBE_DEFINE5_XLATE(udp, , , receive, receive, + "void *", "pktinfo_t *", + "struct inpcb *", "csinfo_t *", + "uint8_t *", "ipinfo_t *", + "struct inpcb *", "udpsinfo_t *", + "struct udphdr *", "udpinfo_t *"); + +SDT_PROBE_DEFINE5_XLATE(udp, , , send, send, + "void *", "pktinfo_t *", + "struct inpcb *", "csinfo_t *", + "uint8_t *", "ipinfo_t *", + "struct inpcb *", "udpsinfo_t *", + "struct udphdr *", "udpinfo_t *"); diff --git a/sys/netinet/in_kdtrace.h b/sys/netinet/in_kdtrace.h new file mode 100644 index 00000000000..1759fac5128 --- /dev/null +++ b/sys/netinet/in_kdtrace.h @@ -0,0 +1,59 @@ +/*- + * Copyright (c) 2013 Mark Johnston + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + * $FreeBSD$ + */ + +#ifndef _SYS_IN_KDTRACE_H_ +#define _SYS_IN_KDTRACE_H_ + +#define IP_PROBE(probe, arg0, arg1, arg2, arg3, arg4, arg5) \ + SDT_PROBE6(ip, , , probe, arg0, arg1, arg2, arg3, arg4, arg5) +#define UDP_PROBE(probe, arg0, arg1, arg2, arg3, arg4) \ + SDT_PROBE5(udp, , , probe, arg0, arg1, arg2, arg3, arg4) +#define TCP_PROBE5(probe, arg0, arg1, arg2, arg3, arg4) \ + SDT_PROBE5(tcp, , , probe, arg0, arg1, arg2, arg3, arg4) +#define TCP_PROBE6(probe, arg0, arg1, arg2, arg3, arg4, arg5) \ + SDT_PROBE6(tcp, , , probe, arg0, arg1, arg2, arg3, arg4, arg5) + +SDT_PROVIDER_DECLARE(ip); +SDT_PROVIDER_DECLARE(tcp); +SDT_PROVIDER_DECLARE(udp); + +SDT_PROBE_DECLARE(ip, , , receive); +SDT_PROBE_DECLARE(ip, , , send); + +SDT_PROBE_DECLARE(tcp, , , accept_established); +SDT_PROBE_DECLARE(tcp, , , accept_refused); +SDT_PROBE_DECLARE(tcp, , , connect_established); +SDT_PROBE_DECLARE(tcp, , , connect_refused); +SDT_PROBE_DECLARE(tcp, , , connect_request); +SDT_PROBE_DECLARE(tcp, , , receive); +SDT_PROBE_DECLARE(tcp, , , send); +SDT_PROBE_DECLARE(tcp, , , state_change); + +SDT_PROBE_DECLARE(udp, , , receive); +SDT_PROBE_DECLARE(udp, , , send); + +#endif diff --git a/sys/netinet/in_mcast.c b/sys/netinet/in_mcast.c index 31f2fe1ca4f..8022c69a9ff 100644 --- a/sys/netinet/in_mcast.c +++ b/sys/netinet/in_mcast.c @@ -1648,6 +1648,8 @@ inp_get_source_filters(struct inpcb *inp, struct sockopt *sopt) * has asked for, but we always tell userland how big the * buffer really needs to be. */ + if (msfr.msfr_nsrcs > in_mcast_maxsocksrc) + msfr.msfr_nsrcs = in_mcast_maxsocksrc; tss = NULL; if (msfr.msfr_srcs != NULL && msfr.msfr_nsrcs > 0) { tss = malloc(sizeof(struct sockaddr_storage) * msfr.msfr_nsrcs, diff --git a/sys/netinet/ip_carp.c b/sys/netinet/ip_carp.c index 9228a8f628f..a170e34d32d 100644 --- a/sys/netinet/ip_carp.c +++ b/sys/netinet/ip_carp.c @@ -187,48 +187,59 @@ static int proto_reg[] = {-1, -1}; * dereferencing our function pointers. */ -static int carp_allow = 1; /* Accept incoming CARP packets. */ -static int carp_preempt = 0; /* Preempt slower nodes. */ -static int carp_log = 1; /* Log level. */ -static int carp_demotion = 0; /* Global advskew demotion. */ -static int carp_senderr_adj = CARP_MAXSKEW; /* Send error demotion factor */ -static int carp_ifdown_adj = CARP_MAXSKEW; /* Iface down demotion factor */ +/* Accept incoming CARP packets. */ +static VNET_DEFINE(int, carp_allow) = 1; +#define V_carp_allow VNET(carp_allow) + +/* Preempt slower nodes. */ +static VNET_DEFINE(int, carp_preempt) = 0; +#define V_carp_preempt VNET(carp_preempt) + +/* Log level. */ +static VNET_DEFINE(int, carp_log) = 1; +#define V_carp_log VNET(carp_log) + +/* Global advskew demotion. */ +static VNET_DEFINE(int, carp_demotion) = 0; +#define V_carp_demotion VNET(carp_demotion) + +/* Send error demotion factor. */ +static VNET_DEFINE(int, carp_senderr_adj) = CARP_MAXSKEW; +#define V_carp_senderr_adj VNET(carp_senderr_adj) + +/* Iface down demotion factor. */ +static VNET_DEFINE(int, carp_ifdown_adj) = CARP_MAXSKEW; +#define V_carp_ifdown_adj VNET(carp_ifdown_adj) + static int carp_demote_adj_sysctl(SYSCTL_HANDLER_ARGS); SYSCTL_NODE(_net_inet, IPPROTO_CARP, carp, CTLFLAG_RW, 0, "CARP"); -SYSCTL_INT(_net_inet_carp, OID_AUTO, allow, CTLFLAG_RW, &carp_allow, 0, - "Accept incoming CARP packets"); -SYSCTL_INT(_net_inet_carp, OID_AUTO, preempt, CTLFLAG_RW, &carp_preempt, 0, - "High-priority backup preemption mode"); -SYSCTL_INT(_net_inet_carp, OID_AUTO, log, CTLFLAG_RW, &carp_log, 0, - "CARP log level"); -SYSCTL_PROC(_net_inet_carp, OID_AUTO, demotion, CTLTYPE_INT|CTLFLAG_RW, +SYSCTL_VNET_INT(_net_inet_carp, OID_AUTO, allow, CTLFLAG_RW, + &VNET_NAME(carp_allow), 0, "Accept incoming CARP packets"); +SYSCTL_VNET_INT(_net_inet_carp, OID_AUTO, preempt, CTLFLAG_RW, + &VNET_NAME(carp_preempt), 0, "High-priority backup preemption mode"); +SYSCTL_VNET_INT(_net_inet_carp, OID_AUTO, log, CTLFLAG_RW, + &VNET_NAME(carp_log), 0, "CARP log level"); +SYSCTL_VNET_PROC(_net_inet_carp, OID_AUTO, demotion, CTLTYPE_INT|CTLFLAG_RW, 0, 0, carp_demote_adj_sysctl, "I", "Adjust demotion factor (skew of advskew)"); -SYSCTL_INT(_net_inet_carp, OID_AUTO, senderr_demotion_factor, CTLFLAG_RW, - &carp_senderr_adj, 0, "Send error demotion factor adjustment"); -SYSCTL_INT(_net_inet_carp, OID_AUTO, ifdown_demotion_factor, CTLFLAG_RW, - &carp_ifdown_adj, 0, "Interface down demotion factor adjustment"); +SYSCTL_VNET_INT(_net_inet_carp, OID_AUTO, senderr_demotion_factor, CTLFLAG_RW, + &VNET_NAME(carp_senderr_adj), 0, "Send error demotion factor adjustment"); +SYSCTL_VNET_INT(_net_inet_carp, OID_AUTO, ifdown_demotion_factor, CTLFLAG_RW, + &VNET_NAME(carp_ifdown_adj), 0, + "Interface down demotion factor adjustment"); + +VNET_PCPUSTAT_DEFINE(struct carpstats, carpstats); +VNET_PCPUSTAT_SYSINIT(carpstats); +VNET_PCPUSTAT_SYSUNINIT(carpstats); -static counter_u64_t carpstats[sizeof(struct carpstats) / sizeof(uint64_t)]; #define CARPSTATS_ADD(name, val) \ - counter_u64_add(carpstats[offsetof(struct carpstats, name) / \ + counter_u64_add(VNET(carpstats)[offsetof(struct carpstats, name) / \ sizeof(uint64_t)], (val)) #define CARPSTATS_INC(name) CARPSTATS_ADD(name, 1) -static int -carpstats_sysctl(SYSCTL_HANDLER_ARGS) -{ - struct carpstats s; - - COUNTER_ARRAY_COPY(carpstats, &s, sizeof(s) / sizeof(uint64_t)); - if (req->newptr) - COUNTER_ARRAY_ZERO(carpstats, sizeof(s) / sizeof(uint64_t)); - return (SYSCTL_OUT(req, &s, sizeof(s))); -} -SYSCTL_PROC(_net_inet_carp, OID_AUTO, stats, CTLTYPE_OPAQUE | CTLFLAG_RW, - NULL, 0, carpstats_sysctl, "I", - "CARP statistics (struct carpstats, netinet/ip_carp.h)"); +SYSCTL_VNET_PCPUSTAT(_net_inet_carp, OID_AUTO, stats, struct carpstats, + carpstats, "CARP statistics (struct carpstats, netinet/ip_carp.h)"); #define CARP_LOCK_INIT(sc) mtx_init(&(sc)->sc_mtx, "carp_softc", \ NULL, MTX_DEF) @@ -251,12 +262,12 @@ SYSCTL_PROC(_net_inet_carp, OID_AUTO, stats, CTLTYPE_OPAQUE | CTLFLAG_RW, } while (0) #define CARP_LOG(...) do { \ - if (carp_log > 0) \ + if (V_carp_log > 0) \ log(LOG_INFO, "carp: " __VA_ARGS__); \ } while (0) #define CARP_DEBUG(...) do { \ - if (carp_log > 1) \ + if (V_carp_log > 1) \ log(LOG_DEBUG, __VA_ARGS__); \ } while (0) @@ -277,8 +288,8 @@ SYSCTL_PROC(_net_inet_carp, OID_AUTO, stats, CTLTYPE_OPAQUE | CTLFLAG_RW, TAILQ_FOREACH((sc), &(ifp)->if_carp->cif_vrs, sc_list) #define DEMOTE_ADVSKEW(sc) \ - (((sc)->sc_advskew + carp_demotion > CARP_MAXSKEW) ? \ - CARP_MAXSKEW : ((sc)->sc_advskew + carp_demotion)) + (((sc)->sc_advskew + V_carp_demotion > CARP_MAXSKEW) ? \ + CARP_MAXSKEW : ((sc)->sc_advskew + V_carp_demotion)) static void carp_input_c(struct mbuf *, struct carp_header *, sa_family_t); static struct carp_softc @@ -430,7 +441,7 @@ carp_input(struct mbuf *m, int hlen) CARPSTATS_INC(carps_ipackets); - if (!carp_allow) { + if (!V_carp_allow) { m_freem(m); return; } @@ -513,7 +524,7 @@ carp6_input(struct mbuf **mp, int *offp, int proto) CARPSTATS_INC(carps_ipackets6); - if (!carp_allow) { + if (!V_carp_allow) { m_freem(m); return (IPPROTO_DONE); } @@ -647,7 +658,7 @@ carp_input_c(struct mbuf *m, struct carp_header *ch, sa_family_t af) * If we're pre-empting masters who advertise slower than us, * and this one claims to be slower, treat him as down. */ - if (carp_preempt && timevalcmp(&sc_tv, &ch_tv, <)) { + if (V_carp_preempt && timevalcmp(&sc_tv, &ch_tv, <)) { CARP_LOG("VHID %u@%s: BACKUP -> MASTER " "(preempting a slower master)\n", sc->sc_vhid, @@ -830,13 +841,14 @@ carp_send_ad_locked(struct carp_softc *sc) if (sc->sc_sendad_errors < INT_MAX) sc->sc_sendad_errors++; if (sc->sc_sendad_errors == CARP_SENDAD_MAX_ERRORS) - carp_demote_adj(carp_senderr_adj, "send error"); + carp_demote_adj(V_carp_senderr_adj, + "send error"); sc->sc_sendad_success = 0; } else { if (sc->sc_sendad_errors >= CARP_SENDAD_MAX_ERRORS) { if (++sc->sc_sendad_success >= CARP_SENDAD_MIN_SUCCESS) { - carp_demote_adj(-carp_senderr_adj, + carp_demote_adj(-V_carp_senderr_adj, "send ok"); sc->sc_sendad_errors = 0; } @@ -903,14 +915,14 @@ carp_send_ad_locked(struct carp_softc *sc) if (sc->sc_sendad_errors < INT_MAX) sc->sc_sendad_errors++; if (sc->sc_sendad_errors == CARP_SENDAD_MAX_ERRORS) - carp_demote_adj(carp_senderr_adj, + carp_demote_adj(V_carp_senderr_adj, "send6 error"); sc->sc_sendad_success = 0; } else { if (sc->sc_sendad_errors >= CARP_SENDAD_MAX_ERRORS) { if (++sc->sc_sendad_success >= CARP_SENDAD_MIN_SUCCESS) { - carp_demote_adj(-carp_senderr_adj, + carp_demote_adj(-V_carp_senderr_adj, "send6 ok"); sc->sc_sendad_errors = 0; } @@ -1541,7 +1553,7 @@ carp_destroy(struct carp_softc *sc) CARP_LOCK(sc); if (sc->sc_suppress) - carp_demote_adj(-carp_ifdown_adj, "vhid removed"); + carp_demote_adj(-V_carp_ifdown_adj, "vhid removed"); callout_drain(&sc->sc_ad_tmo); #ifdef INET callout_drain(&sc->sc_md_tmo); @@ -1593,12 +1605,12 @@ carp_free_if(struct carp_if *cif) IF_ADDR_WLOCK(ifp); ifp->if_carp = NULL; - if_rele(ifp); IF_ADDR_WUNLOCK(ifp); CIF_LOCK_DESTROY(cif); ifpromisc(ifp, 0); + if_rele(ifp); free(cif, M_CARP); } @@ -2006,13 +2018,13 @@ carp_sc_state(struct carp_softc *sc) carp_set_state(sc, INIT); carp_setrun(sc, 0); if (!sc->sc_suppress) - carp_demote_adj(carp_ifdown_adj, "interface down"); + carp_demote_adj(V_carp_ifdown_adj, "interface down"); sc->sc_suppress = 1; } else { carp_set_state(sc, INIT); carp_setrun(sc, 0); if (sc->sc_suppress) - carp_demote_adj(-carp_ifdown_adj, "interface up"); + carp_demote_adj(-V_carp_ifdown_adj, "interface up"); sc->sc_suppress = 0; } } @@ -2020,8 +2032,8 @@ carp_sc_state(struct carp_softc *sc) static void carp_demote_adj(int adj, char *reason) { - atomic_add_int(&carp_demotion, adj); - CARP_LOG("demoted by %d to %d (%s)\n", adj, carp_demotion, reason); + atomic_add_int(&V_carp_demotion, adj); + CARP_LOG("demoted by %d to %d (%s)\n", adj, V_carp_demotion, reason); taskqueue_enqueue(taskqueue_swi, &carp_sendall_task); } @@ -2030,7 +2042,7 @@ carp_demote_adj_sysctl(SYSCTL_HANDLER_ARGS) { int new, error; - new = carp_demotion; + new = V_carp_demotion; error = sysctl_handle_int(oidp, &new, 0, req); if (error || !req->newptr) return (error); @@ -2101,8 +2113,6 @@ carp_mod_cleanup(void) mtx_unlock(&carp_mtx); taskqueue_drain(taskqueue_swi, &carp_sendall_task); mtx_destroy(&carp_mtx); - COUNTER_ARRAY_FREE(carpstats, - sizeof(struct carpstats) / sizeof(uint64_t)); } static int @@ -2112,8 +2122,6 @@ carp_mod_load(void) mtx_init(&carp_mtx, "carp_mtx", NULL, MTX_DEF); LIST_INIT(&carp_list); - COUNTER_ARRAY_ALLOC(carpstats, - sizeof(struct carpstats) / sizeof(uint64_t), M_WAITOK); carp_get_vhid_p = carp_get_vhid; carp_forus_p = carp_forus; carp_output_p = carp_output; diff --git a/sys/netinet/ip_fastfwd.c b/sys/netinet/ip_fastfwd.c index 3a228ca81c0..7d8147566cd 100644 --- a/sys/netinet/ip_fastfwd.c +++ b/sys/netinet/ip_fastfwd.c @@ -78,6 +78,7 @@ __FBSDID("$FreeBSD$"); #include "opt_ipfw.h" #include "opt_ipstealth.h" +#include "opt_kdtrace.h" #include #include @@ -85,6 +86,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include @@ -97,6 +99,7 @@ __FBSDID("$FreeBSD$"); #include #include +#include #include #include #include @@ -524,9 +527,14 @@ passout: if (ip_len <= mtu || (ifp->if_hwassist & CSUM_FRAGMENT && (ip_off & IP_DF) == 0)) { + /* + * Avoid confusing lower layers. + */ + m_clrprotoflags(m); /* * Send off the packet via outgoing interface */ + IP_PROBE(send, NULL, NULL, ip, ifp, ip, NULL); error = (*ifp->if_output)(ifp, m, (struct sockaddr *)dst, &ro); } else { @@ -553,7 +561,12 @@ passout: do { m0 = m->m_nextpkt; m->m_nextpkt = NULL; + /* + * Avoid confusing lower layers. + */ + m_clrprotoflags(m); + IP_PROBE(send, NULL, NULL, ip, ifp, ip, NULL); error = (*ifp->if_output)(ifp, m, (struct sockaddr *)dst, &ro); if (error) diff --git a/sys/netinet/ip_input.c b/sys/netinet/ip_input.c index 219f362dcc9..c265d02b4e2 100644 --- a/sys/netinet/ip_input.c +++ b/sys/netinet/ip_input.c @@ -36,6 +36,7 @@ __FBSDID("$FreeBSD$"); #include "opt_ipfw.h" #include "opt_ipstealth.h" #include "opt_ipsec.h" +#include "opt_kdtrace.h" #include "opt_route.h" #include @@ -49,6 +50,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include @@ -63,6 +65,7 @@ __FBSDID("$FreeBSD$"); #include #include +#include #include #include #include @@ -360,6 +363,11 @@ ip_init(void) void ip_destroy(void) { + int i; + + if ((i = pfil_head_unregister(&V_inet_pfil_hook)) != 0) + printf("%s: WARNING: unable to unregister pfil hook, " + "error %d\n", __func__, i); /* Cleanup in_ifaddr hash table; should be empty. */ hashdestroy(V_in_ifaddrhashtbl, M_IFADDR, V_in_ifaddrhmask); @@ -429,6 +437,8 @@ ip_input(struct mbuf *m) ip = mtod(m, struct ip *); } + IP_PROBE(receive, NULL, NULL, ip, m->m_pkthdr.rcvif, ip, NULL); + /* 127/8 must not appear on wire - RFC1122 */ ifp = m->m_pkthdr.rcvif; if ((ntohl(ip->ip_dst.s_addr) >> IN_CLASSA_NSHIFT) == IN_LOOPBACKNET || @@ -911,9 +921,9 @@ found: IPSTAT_INC(ips_toosmall); /* XXX */ goto dropfrag; } - m->m_flags |= M_FRAG; + m->m_flags |= M_IP_FRAG; } else - m->m_flags &= ~M_FRAG; + m->m_flags &= ~M_IP_FRAG; ip->ip_off = htons(ntohs(ip->ip_off) << 3); /* @@ -921,7 +931,7 @@ found: * ip_reass() will return a different mbuf. */ IPSTAT_INC(ips_fragments); - m->m_pkthdr.header = ip; + m->m_pkthdr.PH_loc.ptr = ip; /* Previous ip_reass() started here. */ /* @@ -964,7 +974,7 @@ found: #endif } -#define GETIP(m) ((struct ip*)((m)->m_pkthdr.header)) +#define GETIP(m) ((struct ip*)((m)->m_pkthdr.PH_loc.ptr)) /* * Handle ECN by comparing this segment with the first one; @@ -1060,7 +1070,7 @@ found: next += ntohs(GETIP(q)->ip_len); } /* Make sure the last packet didn't have the IP_MF flag */ - if (p->m_flags & M_FRAG) { + if (p->m_flags & M_IP_FRAG) { if (fp->ipq_nfrags > V_maxfragsperpacket) { IPSTAT_ADD(ips_fragdropped, fp->ipq_nfrags); ip_freef(head, fp); diff --git a/sys/netinet/ip_mroute.c b/sys/netinet/ip_mroute.c index 23f1be7fb87..3a03defe04c 100644 --- a/sys/netinet/ip_mroute.c +++ b/sys/netinet/ip_mroute.c @@ -609,7 +609,7 @@ static void if_detached_event(void *arg __unused, struct ifnet *ifp) { vifi_t vifi; - int i; + u_long i; MROUTER_LOCK(); @@ -634,8 +634,8 @@ if_detached_event(void *arg __unused, struct ifnet *ifp) continue; for (i = 0; i < mfchashsize; i++) { struct mfc *rt, *nrt; - for (rt = LIST_FIRST(&V_mfchashtbl[i]); rt; rt = nrt) { - nrt = LIST_NEXT(rt, mfc_hash); + + LIST_FOREACH_SAFE(rt, &V_mfchashtbl[i], mfc_hash, nrt) { if (rt->mfc_parent == vifi) { expire_mfc(rt); } @@ -704,10 +704,9 @@ ip_mrouter_init(struct socket *so, int version) static int X_ip_mrouter_done(void) { - vifi_t vifi; - int i; struct ifnet *ifp; - struct ifreq ifr; + u_long i; + vifi_t vifi; MROUTER_LOCK(); @@ -732,11 +731,6 @@ X_ip_mrouter_done(void) for (vifi = 0; vifi < V_numvifs; vifi++) { if (!in_nullhost(V_viftable[vifi].v_lcl_addr) && !(V_viftable[vifi].v_flags & (VIFF_TUNNEL | VIFF_REGISTER))) { - struct sockaddr_in *so = (struct sockaddr_in *)&(ifr.ifr_addr); - - so->sin_len = sizeof(struct sockaddr_in); - so->sin_family = AF_INET; - so->sin_addr.s_addr = INADDR_ANY; ifp = V_viftable[vifi].v_ifp; if_allmulti(ifp, 0); } @@ -759,8 +753,8 @@ X_ip_mrouter_done(void) */ for (i = 0; i < mfchashsize; i++) { struct mfc *rt, *nrt; - for (rt = LIST_FIRST(&V_mfchashtbl[i]); rt; rt = nrt) { - nrt = LIST_NEXT(rt, mfc_hash); + + LIST_FOREACH_SAFE(rt, &V_mfchashtbl[i], mfc_hash, nrt) { expire_mfc(rt); } } @@ -803,7 +797,7 @@ set_assert(int i) int set_api_config(uint32_t *apival) { - int i; + u_long i; /* * We can set the API capabilities only if it is the first operation @@ -1439,7 +1433,7 @@ non_fatal: static void expire_upcalls(void *arg) { - int i; + u_long i; CURVNET_SET((struct vnet *) arg); @@ -1451,9 +1445,7 @@ expire_upcalls(void *arg) if (V_nexpire[i] == 0) continue; - for (rt = LIST_FIRST(&V_mfchashtbl[i]); rt; rt = nrt) { - nrt = LIST_NEXT(rt, mfc_hash); - + LIST_FOREACH_SAFE(rt, &V_mfchashtbl[i], mfc_hash, nrt) { if (TAILQ_EMPTY(&rt->mfc_stall)) continue; diff --git a/sys/netinet/ip_output.c b/sys/netinet/ip_output.c index d835bd2197b..464c85289e4 100644 --- a/sys/netinet/ip_output.c +++ b/sys/netinet/ip_output.c @@ -34,9 +34,10 @@ __FBSDID("$FreeBSD$"); #include "opt_ipfw.h" #include "opt_ipsec.h" -#include "opt_route.h" +#include "opt_kdtrace.h" #include "opt_mbuf_stress_test.h" #include "opt_mpath.h" +#include "opt_route.h" #include "opt_sctp.h" #include @@ -47,6 +48,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include #include @@ -64,6 +66,7 @@ __FBSDID("$FreeBSD$"); #include #include +#include #include #include #include @@ -621,7 +624,8 @@ passout: * Reset layer specific mbuf flags * to avoid confusing lower layers. */ - m->m_flags &= ~(M_PROTOFLAGS); + m_clrprotoflags(m); + IP_PROBE(send, NULL, NULL, ip, ifp, ip, NULL); error = (*ifp->if_output)(ifp, m, (const struct sockaddr *)gw, ro); goto done; @@ -654,8 +658,9 @@ passout: * Reset layer specific mbuf flags * to avoid confusing upper layers. */ - m->m_flags &= ~(M_PROTOFLAGS); + m_clrprotoflags(m); + IP_PROBE(send, NULL, NULL, ip, ifp, ip, NULL); error = (*ifp->if_output)(ifp, m, (const struct sockaddr *)gw, ro); } else @@ -784,7 +789,7 @@ smart_frag_failure: IPSTAT_INC(ips_odropped); goto done; } - m->m_flags |= (m0->m_flags & M_MCAST) | M_FRAG; + m->m_flags |= (m0->m_flags & M_MCAST); /* * In the first mbuf, leave room for the link header, then * copy the original IP header including options. The payload @@ -801,10 +806,9 @@ smart_frag_failure: m->m_len = mhlen; /* XXX do we need to add ip_off below ? */ mhip->ip_off = ((off - hlen) >> 3) + ip_off; - if (off + len >= ip_len) { /* last fragment */ + if (off + len >= ip_len) len = ip_len - off; - m->m_flags |= M_LASTFRAG; - } else + else mhip->ip_off |= IP_MF; mhip->ip_len = htons((u_short)(len + mhlen)); m->m_next = m_copym(m0, off, len, M_NOWAIT); @@ -831,10 +835,6 @@ smart_frag_failure: } IPSTAT_ADD(ips_ofragments, nfrags); - /* set first marker for fragment chain */ - m0->m_flags |= M_FIRSTFRAG | M_FRAG; - m0->m_pkthdr.csum_data = nfrags; - /* * Update first fragment by trimming what's been copied out * and updating header. diff --git a/sys/netinet/ip_var.h b/sys/netinet/ip_var.h index 1ac1bd28084..519f9cddb48 100644 --- a/sys/netinet/ip_var.h +++ b/sys/netinet/ip_var.h @@ -163,10 +163,13 @@ void kmod_ipstat_dec(int statnum); #define IP_ALLOWBROADCAST SO_BROADCAST /* 0x20 can send broadcast packets */ /* - * mbuf flag used by ip_fastfwd + * IPv4 protocol layer specific mbuf flags. */ #define M_FASTFWD_OURS M_PROTO1 /* changed dst to local */ #define M_IP_NEXTHOP M_PROTO2 /* explicit ip nexthop */ +#define M_SKIP_FIREWALL M_PROTO3 /* skip firewall processing, + keep in sync with IP6 */ +#define M_IP_FRAG M_PROTO4 /* fragment reassembly */ #ifdef __NO_STRICT_ALIGNMENT #define IP_HDR_ALIGNED_P(ip) 1 diff --git a/sys/netinet/pim_var.h b/sys/netinet/pim_var.h index 1fdfb108fe6..d082d9a3145 100644 --- a/sys/netinet/pim_var.h +++ b/sys/netinet/pim_var.h @@ -71,11 +71,6 @@ struct pimstat { #define PIMCTL_STATS 1 /* statistics (read-only) */ #define PIMCTL_MAXID 2 -#define PIMCTL_NAMES { \ - { 0, 0 }, \ - { "stats", CTLTYPE_STRUCT }, \ -} - #ifdef _KERNEL void pim_input(struct mbuf *, int); diff --git a/sys/netinet/sctp.h b/sys/netinet/sctp.h index 493e085d334..3310453ac7f 100644 --- a/sys/netinet/sctp.h +++ b/sys/netinet/sctp.h @@ -425,7 +425,7 @@ struct sctp_error_unrecognized_chunk { /* RFC4895 */ #define SCTP_AUTHENTICATION 0x0f /* EY nr_sack chunk id*/ -#define SCTP_NR_SELECTIVE_ACK 0x10 +#define SCTP_NR_SELECTIVE_ACK 0x10 /************0x40 series ***********/ /************0x80 series ***********/ /* RFC5061 */ @@ -509,38 +509,38 @@ struct sctp_error_unrecognized_chunk { /* * PCB Features (in sctp_features bitmask) */ -#define SCTP_PCB_FLAGS_DO_NOT_PMTUD 0x00000001 -#define SCTP_PCB_FLAGS_EXT_RCVINFO 0x00000002 /* deprecated */ -#define SCTP_PCB_FLAGS_DONOT_HEARTBEAT 0x00000004 -#define SCTP_PCB_FLAGS_FRAG_INTERLEAVE 0x00000008 -#define SCTP_PCB_FLAGS_INTERLEAVE_STRMS 0x00000010 -#define SCTP_PCB_FLAGS_DO_ASCONF 0x00000020 -#define SCTP_PCB_FLAGS_AUTO_ASCONF 0x00000040 -#define SCTP_PCB_FLAGS_ZERO_COPY_ACTIVE 0x00000080 +#define SCTP_PCB_FLAGS_DO_NOT_PMTUD 0x0000000000000001 +#define SCTP_PCB_FLAGS_EXT_RCVINFO 0x0000000000000002 /* deprecated */ +#define SCTP_PCB_FLAGS_DONOT_HEARTBEAT 0x0000000000000004 +#define SCTP_PCB_FLAGS_FRAG_INTERLEAVE 0x0000000000000008 +#define SCTP_PCB_FLAGS_INTERLEAVE_STRMS 0x0000000000000010 +#define SCTP_PCB_FLAGS_DO_ASCONF 0x0000000000000020 +#define SCTP_PCB_FLAGS_AUTO_ASCONF 0x0000000000000040 +#define SCTP_PCB_FLAGS_ZERO_COPY_ACTIVE 0x0000000000000080 /* socket options */ -#define SCTP_PCB_FLAGS_NODELAY 0x00000100 -#define SCTP_PCB_FLAGS_AUTOCLOSE 0x00000200 -#define SCTP_PCB_FLAGS_RECVDATAIOEVNT 0x00000400 /* deprecated */ -#define SCTP_PCB_FLAGS_RECVASSOCEVNT 0x00000800 -#define SCTP_PCB_FLAGS_RECVPADDREVNT 0x00001000 -#define SCTP_PCB_FLAGS_RECVPEERERR 0x00002000 -#define SCTP_PCB_FLAGS_RECVSENDFAILEVNT 0x00004000 /* deprecated */ -#define SCTP_PCB_FLAGS_RECVSHUTDOWNEVNT 0x00008000 -#define SCTP_PCB_FLAGS_ADAPTATIONEVNT 0x00010000 -#define SCTP_PCB_FLAGS_PDAPIEVNT 0x00020000 -#define SCTP_PCB_FLAGS_AUTHEVNT 0x00040000 -#define SCTP_PCB_FLAGS_STREAM_RESETEVNT 0x00080000 -#define SCTP_PCB_FLAGS_NO_FRAGMENT 0x00100000 -#define SCTP_PCB_FLAGS_EXPLICIT_EOR 0x00400000 -#define SCTP_PCB_FLAGS_NEEDS_MAPPED_V4 0x00800000 -#define SCTP_PCB_FLAGS_MULTIPLE_ASCONFS 0x01000000 -#define SCTP_PCB_FLAGS_PORTREUSE 0x02000000 -#define SCTP_PCB_FLAGS_DRYEVNT 0x04000000 -#define SCTP_PCB_FLAGS_RECVRCVINFO 0x08000000 -#define SCTP_PCB_FLAGS_RECVNXTINFO 0x10000000 -#define SCTP_PCB_FLAGS_ASSOC_RESETEVNT 0x20000000 -#define SCTP_PCB_FLAGS_STREAM_CHANGEEVNT 0x40000000 -#define SCTP_PCB_FLAGS_RECVNSENDFAILEVNT 0x80000000 +#define SCTP_PCB_FLAGS_NODELAY 0x0000000000000100 +#define SCTP_PCB_FLAGS_AUTOCLOSE 0x0000000000000200 +#define SCTP_PCB_FLAGS_RECVDATAIOEVNT 0x0000000000000400 /* deprecated */ +#define SCTP_PCB_FLAGS_RECVASSOCEVNT 0x0000000000000800 +#define SCTP_PCB_FLAGS_RECVPADDREVNT 0x0000000000001000 +#define SCTP_PCB_FLAGS_RECVPEERERR 0x0000000000002000 +#define SCTP_PCB_FLAGS_RECVSENDFAILEVNT 0x0000000000004000 /* deprecated */ +#define SCTP_PCB_FLAGS_RECVSHUTDOWNEVNT 0x0000000000008000 +#define SCTP_PCB_FLAGS_ADAPTATIONEVNT 0x0000000000010000 +#define SCTP_PCB_FLAGS_PDAPIEVNT 0x0000000000020000 +#define SCTP_PCB_FLAGS_AUTHEVNT 0x0000000000040000 +#define SCTP_PCB_FLAGS_STREAM_RESETEVNT 0x0000000000080000 +#define SCTP_PCB_FLAGS_NO_FRAGMENT 0x0000000000100000 +#define SCTP_PCB_FLAGS_EXPLICIT_EOR 0x0000000000400000 +#define SCTP_PCB_FLAGS_NEEDS_MAPPED_V4 0x0000000000800000 +#define SCTP_PCB_FLAGS_MULTIPLE_ASCONFS 0x0000000001000000 +#define SCTP_PCB_FLAGS_PORTREUSE 0x0000000002000000 +#define SCTP_PCB_FLAGS_DRYEVNT 0x0000000004000000 +#define SCTP_PCB_FLAGS_RECVRCVINFO 0x0000000008000000 +#define SCTP_PCB_FLAGS_RECVNXTINFO 0x0000000010000000 +#define SCTP_PCB_FLAGS_ASSOC_RESETEVNT 0x0000000020000000 +#define SCTP_PCB_FLAGS_STREAM_CHANGEEVNT 0x0000000040000000 +#define SCTP_PCB_FLAGS_RECVNSENDFAILEVNT 0x0000000080000000 /*- * mobility_features parameters (by micchie).Note diff --git a/sys/netinet/sctp_auth.c b/sys/netinet/sctp_auth.c index 42e263650f0..0048856fb27 100644 --- a/sys/netinet/sctp_auth.c +++ b/sys/netinet/sctp_auth.c @@ -703,15 +703,7 @@ sctp_auth_add_hmacid(sctp_hmaclist_t * list, uint16_t hmac_id) return (-1); } if ((hmac_id != SCTP_AUTH_HMAC_ID_SHA1) && -#ifdef HAVE_SHA224 - (hmac_id != SCTP_AUTH_HMAC_ID_SHA224) && -#endif -#ifdef HAVE_SHA2 - (hmac_id != SCTP_AUTH_HMAC_ID_SHA256) && - (hmac_id != SCTP_AUTH_HMAC_ID_SHA384) && - (hmac_id != SCTP_AUTH_HMAC_ID_SHA512) && -#endif - 1) { + (hmac_id != SCTP_AUTH_HMAC_ID_SHA256)) { return (-1); } /* Now is it already in the list */ @@ -754,8 +746,9 @@ sctp_default_supported_hmaclist(void) new_list = sctp_alloc_hmaclist(2); if (new_list == NULL) return (NULL); - (void)sctp_auth_add_hmacid(new_list, SCTP_AUTH_HMAC_ID_SHA1); + /* We prefer SHA256, so list it first */ (void)sctp_auth_add_hmacid(new_list, SCTP_AUTH_HMAC_ID_SHA256); + (void)sctp_auth_add_hmacid(new_list, SCTP_AUTH_HMAC_ID_SHA1); return (new_list); } @@ -811,19 +804,13 @@ int sctp_verify_hmac_param(struct sctp_auth_hmac_algo *hmacs, uint32_t num_hmacs) { uint32_t i; - uint16_t hmac_id; - uint32_t sha1_supported = 0; for (i = 0; i < num_hmacs; i++) { - hmac_id = ntohs(hmacs->hmac_ids[i]); - if (hmac_id == SCTP_AUTH_HMAC_ID_SHA1) - sha1_supported = 1; + if (ntohs(hmacs->hmac_ids[i]) == SCTP_AUTH_HMAC_ID_SHA1) { + return (0); + } } - /* all HMAC id's are supported */ - if (sha1_supported == 0) - return (-1); - else - return (0); + return (-1); } sctp_authinfo_t * @@ -877,18 +864,8 @@ sctp_get_hmac_digest_len(uint16_t hmac_algo) switch (hmac_algo) { case SCTP_AUTH_HMAC_ID_SHA1: return (SCTP_AUTH_DIGEST_LEN_SHA1); -#ifdef HAVE_SHA224 - case SCTP_AUTH_HMAC_ID_SHA224: - return (SCTP_AUTH_DIGEST_LEN_SHA224); -#endif -#ifdef HAVE_SHA2 case SCTP_AUTH_HMAC_ID_SHA256: return (SCTP_AUTH_DIGEST_LEN_SHA256); - case SCTP_AUTH_HMAC_ID_SHA384: - return (SCTP_AUTH_DIGEST_LEN_SHA384); - case SCTP_AUTH_HMAC_ID_SHA512: - return (SCTP_AUTH_DIGEST_LEN_SHA512); -#endif default: /* unknown HMAC algorithm: can't do anything */ return (0); @@ -900,17 +877,9 @@ sctp_get_hmac_block_len(uint16_t hmac_algo) { switch (hmac_algo) { case SCTP_AUTH_HMAC_ID_SHA1: -#ifdef HAVE_SHA224 - case SCTP_AUTH_HMAC_ID_SHA224: -#endif return (64); -#ifdef HAVE_SHA2 case SCTP_AUTH_HMAC_ID_SHA256: return (64); - case SCTP_AUTH_HMAC_ID_SHA384: - case SCTP_AUTH_HMAC_ID_SHA512: - return (128); -#endif case SCTP_AUTH_HMAC_ID_RSVD: default: /* unknown HMAC algorithm: can't do anything */ @@ -923,23 +892,11 @@ sctp_hmac_init(uint16_t hmac_algo, sctp_hash_context_t * ctx) { switch (hmac_algo) { case SCTP_AUTH_HMAC_ID_SHA1: - SHA1_Init(&ctx->sha1); + SCTP_SHA1_INIT(&ctx->sha1); break; -#ifdef HAVE_SHA224 - case SCTP_AUTH_HMAC_ID_SHA224: - break; -#endif -#ifdef HAVE_SHA2 case SCTP_AUTH_HMAC_ID_SHA256: - SHA256_Init(&ctx->sha256); + SCTP_SHA256_INIT(&ctx->sha256); break; - case SCTP_AUTH_HMAC_ID_SHA384: - SHA384_Init(&ctx->sha384); - break; - case SCTP_AUTH_HMAC_ID_SHA512: - SHA512_Init(&ctx->sha512); - break; -#endif case SCTP_AUTH_HMAC_ID_RSVD: default: /* unknown HMAC algorithm: can't do anything */ @@ -953,23 +910,11 @@ sctp_hmac_update(uint16_t hmac_algo, sctp_hash_context_t * ctx, { switch (hmac_algo) { case SCTP_AUTH_HMAC_ID_SHA1: - SHA1_Update(&ctx->sha1, text, textlen); + SCTP_SHA1_UPDATE(&ctx->sha1, text, textlen); break; -#ifdef HAVE_SHA224 - case SCTP_AUTH_HMAC_ID_SHA224: - break; -#endif -#ifdef HAVE_SHA2 case SCTP_AUTH_HMAC_ID_SHA256: - SHA256_Update(&ctx->sha256, text, textlen); + SCTP_SHA256_UPDATE(&ctx->sha256, text, textlen); break; - case SCTP_AUTH_HMAC_ID_SHA384: - SHA384_Update(&ctx->sha384, text, textlen); - break; - case SCTP_AUTH_HMAC_ID_SHA512: - SHA512_Update(&ctx->sha512, text, textlen); - break; -#endif case SCTP_AUTH_HMAC_ID_RSVD: default: /* unknown HMAC algorithm: can't do anything */ @@ -983,24 +928,11 @@ sctp_hmac_final(uint16_t hmac_algo, sctp_hash_context_t * ctx, { switch (hmac_algo) { case SCTP_AUTH_HMAC_ID_SHA1: - SHA1_Final(digest, &ctx->sha1); + SCTP_SHA1_FINAL(digest, &ctx->sha1); break; -#ifdef HAVE_SHA224 - case SCTP_AUTH_HMAC_ID_SHA224: - break; -#endif -#ifdef HAVE_SHA2 case SCTP_AUTH_HMAC_ID_SHA256: - SHA256_Final(digest, &ctx->sha256); + SCTP_SHA256_FINAL(digest, &ctx->sha256); break; - case SCTP_AUTH_HMAC_ID_SHA384: - /* SHA384 is truncated SHA512 */ - SHA384_Final(digest, &ctx->sha384); - break; - case SCTP_AUTH_HMAC_ID_SHA512: - SHA512_Final(digest, &ctx->sha512); - break; -#endif case SCTP_AUTH_HMAC_ID_RSVD: default: /* unknown HMAC algorithm: can't do anything */ diff --git a/sys/netinet/sctp_auth.h b/sys/netinet/sctp_auth.h index eac89f6fca8..154bc13d01c 100644 --- a/sys/netinet/sctp_auth.h +++ b/sys/netinet/sctp_auth.h @@ -36,14 +36,12 @@ __FBSDID("$FreeBSD$"); #ifndef _NETINET_SCTP_AUTH_H_ #define _NETINET_SCTP_AUTH_H_ +#include /* digest lengths */ #define SCTP_AUTH_DIGEST_LEN_SHA1 20 -#define SCTP_AUTH_DIGEST_LEN_SHA224 28 #define SCTP_AUTH_DIGEST_LEN_SHA256 32 -#define SCTP_AUTH_DIGEST_LEN_SHA384 48 -#define SCTP_AUTH_DIGEST_LEN_SHA512 64 -#define SCTP_AUTH_DIGEST_LEN_MAX 64 +#define SCTP_AUTH_DIGEST_LEN_MAX SCTP_AUTH_DIGEST_LEN_SHA256 /* random sizes */ #define SCTP_AUTH_RANDOM_SIZE_DEFAULT 32 @@ -52,12 +50,8 @@ __FBSDID("$FreeBSD$"); /* union of all supported HMAC algorithm contexts */ typedef union sctp_hash_context { - SHA1_CTX sha1; -#ifdef HAVE_SHA2 - SHA256_CTX sha256; - SHA384_CTX sha384; - SHA512_CTX sha512; -#endif + SCTP_SHA1_CTX sha1; + SCTP_SHA256_CTX sha256; } sctp_hash_context_t; typedef struct sctp_key { diff --git a/sys/netinet/sctp_dtrace_define.h b/sys/netinet/sctp_dtrace_define.h index 5247b02dc9b..c460c0bf349 100644 --- a/sys/netinet/sctp_dtrace_define.h +++ b/sys/netinet/sctp_dtrace_define.h @@ -45,189 +45,132 @@ SDT_PROVIDER_DEFINE(sctp); /* Cwnd probe - tracks changes in the congestion window on a netp */ /********************************************************/ /* Initial */ -SDT_PROBE_DEFINE(sctp, cwnd, net, init, init); -/* The Vtag for this end */ -SDT_PROBE_ARGTYPE(sctp, cwnd, net, init, 0, "uint32_t"); -/* The port number of the local side << 16 | port number of remote - * in network byte order. - */ -SDT_PROBE_ARGTYPE(sctp, cwnd, net, init, 1, "uint32_t"); -/* The pointer to the struct sctp_nets * changing */ -SDT_PROBE_ARGTYPE(sctp, cwnd, net, init, 2, "uintptr_t"); -/* The old value of the cwnd */ -SDT_PROBE_ARGTYPE(sctp, cwnd, net, init, 3, "int"); -/* The new value of the cwnd */ -SDT_PROBE_ARGTYPE(sctp, cwnd, net, init, 4, "int"); - +SDT_PROBE_DEFINE5(sctp, cwnd, net, init, init, + "uint32_t", /* The Vtag for this end */ + "uint32_t", /* + * The port number of the local side << 16 | port number + * of remote in network byte order. + */ + "uintptr_t", /* The pointer to the struct sctp_nets * changing */ + "int", /* The old value of the cwnd */ + "int"); /* The new value of the cwnd */ /* ACK-INCREASE */ -SDT_PROBE_DEFINE(sctp, cwnd, net, ack, ack); -/* The Vtag for this end */ -SDT_PROBE_ARGTYPE(sctp, cwnd, net, ack, 0, "uint32_t"); -/* The port number of the local side << 16 | port number of remote - * in network byte order. - */ -SDT_PROBE_ARGTYPE(sctp, cwnd, net, ack, 1, "uint32_t"); -/* The pointer to the struct sctp_nets * changing */ -SDT_PROBE_ARGTYPE(sctp, cwnd, net, ack, 2, "uintptr_t"); -/* The old value of the cwnd */ -SDT_PROBE_ARGTYPE(sctp, cwnd, net, ack, 3, "int"); -/* The new value of the cwnd */ -SDT_PROBE_ARGTYPE(sctp, cwnd, net, ack, 4, "int"); - +SDT_PROBE_DEFINE5(sctp, cwnd, net, ack, ack, + "uint32_t", /* The Vtag for this end */ + "uint32_t", /* + * The port number of the local side << 16 | port number + * of remote in network byte order. + */ + "uintptr_t", /* The pointer to the struct sctp_nets * changing */ + "int", /* The old value of the cwnd */ + "int"); /* The new value of the cwnd */ /* ACK-INCREASE */ -SDT_PROBE_DEFINE(sctp, cwnd, net, rttvar, rttvar); -/* The Vtag << 32 | localport << 16 | remoteport */ -SDT_PROBE_ARGTYPE(sctp, cwnd, net, rttvar, 0, "uint64_t"); -/* obw | nbw */ -SDT_PROBE_ARGTYPE(sctp, cwnd, net, rttvar, 1, "uint64_t"); -/* bwrtt | newrtt */ -SDT_PROBE_ARGTYPE(sctp, cwnd, net, rttvar, 2, "uint64_t"); -/* flight */ -SDT_PROBE_ARGTYPE(sctp, cwnd, net, rttvar, 3, "uint64_t"); -/* (cwnd << 32) | point << 16 | retval(0/1) */ -SDT_PROBE_ARGTYPE(sctp, cwnd, net, rttvar, 4, "uint64_t"); - - -SDT_PROBE_DEFINE(sctp, cwnd, net, rttstep, rttstep); -/* The Vtag << 32 | localport << 16 | remoteport */ -SDT_PROBE_ARGTYPE(sctp, cwnd, net, rttstep, 0, "uint64_t"); -/* obw | nbw */ -SDT_PROBE_ARGTYPE(sctp, cwnd, net, rttstep, 1, "uint64_t"); -/* bwrtt | nrtt */ -SDT_PROBE_ARGTYPE(sctp, cwnd, net, rttstep, 2, "uint64_t"); -/* cwnd_saved | stepcnt << 16 | oldstep */ -SDT_PROBE_ARGTYPE(sctp, cwnd, net, rttstep, 3, "uint64_t"); -/* (cwnd << 32) | point << 16 | retval(0/1) */ -SDT_PROBE_ARGTYPE(sctp, cwnd, net, rttstep, 4, "uint64_t"); +SDT_PROBE_DEFINE5(sctp, cwnd, net, rttvar, rttvar, + "uint64_t", /* The Vtag << 32 | localport << 16 | remoteport */ + "uint64_t", /* obw | nbw */ + "uint64_t", /* bwrtt | newrtt */ + "uint64_t", /* flight */ + "uint64_t"); /* (cwnd << 32) | point << 16 | retval(0/1) */ +SDT_PROBE_DEFINE5(sctp, cwnd, net, rttstep, rttstep, + "uint64_t", /* The Vtag << 32 | localport << 16 | remoteport */ + "uint64_t", /* obw | nbw */ + "uint64_t", /* bwrtt | newrtt */ + "uint64_t", /* flight */ + "uint64_t"); /* (cwnd << 32) | point << 16 | retval(0/1) */ /* FastRetransmit-DECREASE */ -SDT_PROBE_DEFINE(sctp, cwnd, net, fr, fr); -/* The Vtag for this end */ -SDT_PROBE_ARGTYPE(sctp, cwnd, net, fr, 0, "uint32_t"); -/* The port number of the local side << 16 | port number of remote - * in network byte order. - */ -SDT_PROBE_ARGTYPE(sctp, cwnd, net, fr, 1, "uint32_t"); -/* The pointer to the struct sctp_nets * changing */ -SDT_PROBE_ARGTYPE(sctp, cwnd, net, fr, 2, "uintptr_t"); -/* The old value of the cwnd */ -SDT_PROBE_ARGTYPE(sctp, cwnd, net, fr, 3, "int"); -/* The new value of the cwnd */ -SDT_PROBE_ARGTYPE(sctp, cwnd, net, fr, 4, "int"); - +SDT_PROBE_DEFINE5(sctp, cwnd, net, fr, fr, + "uint32_t", /* The Vtag for this end */ + "uint32_t", /* + * The port number of the local side << 16 | port number + * of remote in network byte order. + */ + "uintptr_t", /* The pointer to the struct sctp_nets * changing */ + "int", /* The old value of the cwnd */ + "int"); /* The new value of the cwnd */ /* TimeOut-DECREASE */ -SDT_PROBE_DEFINE(sctp, cwnd, net, to, to); -/* The Vtag for this end */ -SDT_PROBE_ARGTYPE(sctp, cwnd, net, to, 0, "uint32_t"); -/* The port number of the local side << 16 | port number of remote - * in network byte order. - */ -SDT_PROBE_ARGTYPE(sctp, cwnd, net, to, 1, "uint32_t"); -/* The pointer to the struct sctp_nets * changing */ -SDT_PROBE_ARGTYPE(sctp, cwnd, net, to, 2, "uintptr_t"); -/* The old value of the cwnd */ -SDT_PROBE_ARGTYPE(sctp, cwnd, net, to, 3, "int"); -/* The new value of the cwnd */ -SDT_PROBE_ARGTYPE(sctp, cwnd, net, to, 4, "int"); - +SDT_PROBE_DEFINE5(sctp, cwnd, net, to, to, + "uint32_t", /* The Vtag for this end */ + "uint32_t", /* + * The port number of the local side << 16 | port number + * of remote in network byte order. + */ + "uintptr_t", /* The pointer to the struct sctp_nets * changing */ + "int", /* The old value of the cwnd */ + "int"); /* The new value of the cwnd */ /* BurstLimit-DECREASE */ -SDT_PROBE_DEFINE(sctp, cwnd, net, bl, bl); -/* The Vtag for this end */ -SDT_PROBE_ARGTYPE(sctp, cwnd, net, bl, 0, "uint32_t"); -/* The port number of the local side << 16 | port number of remote - * in network byte order. - */ -SDT_PROBE_ARGTYPE(sctp, cwnd, net, bl, 1, "uint32_t"); -/* The pointer to the struct sctp_nets * changing */ -SDT_PROBE_ARGTYPE(sctp, cwnd, net, bl, 2, "uintptr_t"); -/* The old value of the cwnd */ -SDT_PROBE_ARGTYPE(sctp, cwnd, net, bl, 3, "int"); -/* The new value of the cwnd */ -SDT_PROBE_ARGTYPE(sctp, cwnd, net, bl, 4, "int"); - +SDT_PROBE_DEFINE5(sctp, cwnd, net, bl, bl, + "uint32_t", /* The Vtag for this end */ + "uint32_t", /* + * The port number of the local side << 16 | port number + * of remote in network byte order. + */ + "uintptr_t", /* The pointer to the struct sctp_nets * changing */ + "int", /* The old value of the cwnd */ + "int"); /* The new value of the cwnd */ /* ECN-DECREASE */ -SDT_PROBE_DEFINE(sctp, cwnd, net, ecn, ecn); -/* The Vtag for this end */ -SDT_PROBE_ARGTYPE(sctp, cwnd, net, ecn, 0, "uint32_t"); -/* The port number of the local side << 16 | port number of remote - * in network byte order. - */ -SDT_PROBE_ARGTYPE(sctp, cwnd, net, ecn, 1, "uint32_t"); -/* The pointer to the struct sctp_nets * changing */ -SDT_PROBE_ARGTYPE(sctp, cwnd, net, ecn, 2, "uintptr_t"); -/* The old value of the cwnd */ -SDT_PROBE_ARGTYPE(sctp, cwnd, net, ecn, 3, "int"); -/* The new value of the cwnd */ -SDT_PROBE_ARGTYPE(sctp, cwnd, net, ecn, 4, "int"); - +SDT_PROBE_DEFINE5(sctp, cwnd, net, ecn, ecn, + "uint32_t", /* The Vtag for this end */ + "uint32_t", /* + * The port number of the local side << 16 | port number + * of remote in network byte order. + */ + "uintptr_t", /* The pointer to the struct sctp_nets * changing */ + "int", /* The old value of the cwnd */ + "int"); /* The new value of the cwnd */ /* PacketDrop-DECREASE */ -SDT_PROBE_DEFINE(sctp, cwnd, net, pd, pd); -/* The Vtag for this end */ -SDT_PROBE_ARGTYPE(sctp, cwnd, net, pd, 0, "uint32_t"); -/* The port number of the local side << 16 | port number of remote - * in network byte order. - */ -SDT_PROBE_ARGTYPE(sctp, cwnd, net, pd, 1, "uint32_t"); -/* The pointer to the struct sctp_nets * changing */ -SDT_PROBE_ARGTYPE(sctp, cwnd, net, pd, 2, "uintptr_t"); -/* The old value of the cwnd */ -SDT_PROBE_ARGTYPE(sctp, cwnd, net, pd, 3, "int"); -/* The new value of the cwnd */ -SDT_PROBE_ARGTYPE(sctp, cwnd, net, pd, 4, "int"); - - +SDT_PROBE_DEFINE5(sctp, cwnd, net, pd, pd, + "uint32_t", /* The Vtag for this end */ + "uint32_t", /* + * The port number of the local side << 16 | port number + * of remote in network byte order. + */ + "uintptr_t", /* The pointer to the struct sctp_nets * changing */ + "int", /* The old value of the cwnd */ + "int"); /* The new value of the cwnd */ /********************************************************/ /* Rwnd probe - tracks changes in the receiver window for an assoc */ /********************************************************/ -SDT_PROBE_DEFINE(sctp, rwnd, assoc, val, val); -/* The Vtag for this end */ -SDT_PROBE_ARGTYPE(sctp, rwnd, assoc, val, 0, "uint32_t"); -/* The port number of the local side << 16 | port number of remote - * in network byte order. - */ -SDT_PROBE_ARGTYPE(sctp, rwnd, assoc, val, 1, "uint32_t"); -/* The up/down amount */ -SDT_PROBE_ARGTYPE(sctp, rwnd, assoc, val, 2, "int"); -/* The new value of the cwnd */ -SDT_PROBE_ARGTYPE(sctp, rwnd, assoc, val, 3, "int"); +SDT_PROBE_DEFINE4(sctp, rwnd, assoc, val, val, + "uint32_t", /* The Vtag for this end */ + "uint32_t", /* + * The port number of the local side << 16 | port number + * of remote in network byte order. + */ + "int", /* The up/down amount */ + "int"); /* The new value of the cwnd */ /********************************************************/ /* flight probe - tracks changes in the flight size on a net or assoc */ /********************************************************/ -SDT_PROBE_DEFINE(sctp, flightsize, net, val, val); -/* The Vtag for this end */ -SDT_PROBE_ARGTYPE(sctp, flightsize, net, val, 0, "uint32_t"); -/* The port number of the local side << 16 | port number of remote - * in network byte order. - */ -SDT_PROBE_ARGTYPE(sctp, flightsize, net, val, 1, "uint32_t"); -/* The pointer to the struct sctp_nets * changing */ -SDT_PROBE_ARGTYPE(sctp, flightsize, net, val, 2, "uintptr_t"); -/* The up/down amount */ -SDT_PROBE_ARGTYPE(sctp, flightsize, net, val, 3, "int"); -/* The new value of the cwnd */ -SDT_PROBE_ARGTYPE(sctp, flightsize, net, val, 4, "int"); +SDT_PROBE_DEFINE5(sctp, flightsize, net, val, val, + "uint32_t", /* The Vtag for this end */ + "uint32_t", /* + * The port number of the local side << 16 | port number + * of remote in network byte order. + */ + "uintptr_t", /* The pointer to the struct sctp_nets * changing */ + "int", /* The up/down amount */ + "int"); /* The new value of the cwnd */ + /********************************************************/ /* The total flight version */ /********************************************************/ -SDT_PROBE_DEFINE(sctp, flightsize, assoc, val, val); -/* The Vtag for this end */ -SDT_PROBE_ARGTYPE(sctp, flightsize, assoc, val, 0, "uint32_t"); -/* The port number of the local side << 16 | port number of remote - * in network byte order. - */ -SDT_PROBE_ARGTYPE(sctp, flightsize, assoc, val, 1, "uint32_t"); -/* The up/down amount */ -SDT_PROBE_ARGTYPE(sctp, flightsize, assoc, val, 2, "int"); -/* The new value of the cwnd */ -SDT_PROBE_ARGTYPE(sctp, flightsize, assoc, val, 3, "int"); +SDT_PROBE_DEFINE4(sctp, flightsize, assoc, val, val, + "uint32_t", /* The Vtag for this end */ + "uint32_t", /* + * The port number of the local side << 16 | port number + * of remote in network byte order. + */ + "int", /* The up/down amount */ + "int"); /* The new value of the cwnd */ #endif diff --git a/sys/netinet/sctp_indata.c b/sys/netinet/sctp_indata.c index 7bad54c1f6f..26fcf852095 100644 --- a/sys/netinet/sctp_indata.c +++ b/sys/netinet/sctp_indata.c @@ -789,13 +789,12 @@ doit_again: * but should we? */ if (stcb->sctp_socket) { - pd_point = min(SCTP_SB_LIMIT_RCV(stcb->sctp_socket), + pd_point = min(SCTP_SB_LIMIT_RCV(stcb->sctp_socket) >> SCTP_PARTIAL_DELIVERY_SHIFT, stcb->sctp_ep->partial_delivery_point); } else { pd_point = stcb->sctp_ep->partial_delivery_point; } if (sctp_is_all_msg_on_reasm(asoc, &tsize) || (tsize >= pd_point)) { - /* * Yes, we setup to start reception, by * backing down the TSN just in case we @@ -1417,7 +1416,6 @@ sctp_does_tsn_belong_to_reasm(struct sctp_association *asoc, return (0); } - static int sctp_process_a_data_chunk(struct sctp_tcb *stcb, struct sctp_association *asoc, struct mbuf **m, int offset, struct sctp_data_chunk *ch, int chk_length, @@ -2492,7 +2490,7 @@ doit_again: * delivery queue and something can be delivered. */ if (stcb->sctp_socket) { - pd_point = min(SCTP_SB_LIMIT_RCV(stcb->sctp_socket), + pd_point = min(SCTP_SB_LIMIT_RCV(stcb->sctp_socket) >> SCTP_PARTIAL_DELIVERY_SHIFT, stcb->sctp_ep->partial_delivery_point); } else { pd_point = stcb->sctp_ep->partial_delivery_point; @@ -4719,7 +4717,7 @@ sctp_handle_sack(struct mbuf *m, int offset_seg, int offset_dup, } } TAILQ_REMOVE(&asoc->sent_queue, tp1, sctp_next); - if (tp1->pr_sctp_on) { + if (PR_SCTP_ENABLED(tp1->flags)) { if (asoc->pr_sctp_cnt != 0) asoc->pr_sctp_cnt--; } diff --git a/sys/netinet/sctp_input.c b/sys/netinet/sctp_input.c index 6ee71260265..287c3ede2c3 100644 --- a/sys/netinet/sctp_input.c +++ b/sys/netinet/sctp_input.c @@ -6023,10 +6023,10 @@ sctp_input_with_port(struct mbuf *i_pak, int off, uint16_t port) } #endif SCTPDBG(SCTP_DEBUG_CRCOFFLOAD, - "sctp_input(): Packet of length %d received on %s with csum_flags 0x%x.\n", + "sctp_input(): Packet of length %d received on %s with csum_flags 0x%b.\n", m->m_pkthdr.len, if_name(m->m_pkthdr.rcvif), - m->m_pkthdr.csum_flags); + (int)m->m_pkthdr.csum_flags, CSUM_BITS); if (m->m_flags & M_FLOWID) { mflowid = m->m_pkthdr.flowid; use_mflowid = 1; diff --git a/sys/netinet/sctp_os_bsd.h b/sys/netinet/sctp_os_bsd.h index 9cb77b688c2..2159bbc295a 100644 --- a/sys/netinet/sctp_os_bsd.h +++ b/sys/netinet/sctp_os_bsd.h @@ -104,6 +104,9 @@ __FBSDID("$FreeBSD$"); #include +#include +#include + #ifndef in6pcb #define in6pcb inpcb #endif @@ -428,6 +431,11 @@ typedef struct rtentry sctp_rtentry_t; /* This is re-pulse ourselves for sendbuf */ #define SCTP_ZERO_COPY_SENDQ_EVENT(inp, so) +/* + * SCTP protocol specific mbuf flags. + */ +#define M_NOTIFICATION M_PROTO1 /* SCTP notification */ + /* * IP output routines */ @@ -439,12 +447,14 @@ typedef struct rtentry sctp_rtentry_t; local_stcb->sctp_ep && \ local_stcb->sctp_ep->sctp_socket) \ o_flgs |= local_stcb->sctp_ep->sctp_socket->so_options & SO_DONTROUTE; \ + m_clrprotoflags(o_pak); \ result = ip_output(o_pak, NULL, ro, o_flgs, 0, NULL); \ } #define SCTP_IP6_OUTPUT(result, o_pak, ro, ifp, stcb, vrf_id) \ { \ struct sctp_tcb *local_stcb = stcb; \ + m_clrprotoflags(o_pak); \ if (local_stcb && local_stcb->sctp_ep) \ result = ip6_output(o_pak, \ ((struct in6pcb *)(local_stcb->sctp_ep))->in6p_outputopts, \ @@ -461,23 +471,18 @@ sctp_get_mbuf_for_msg(unsigned int space_needed, /* * SCTP AUTH */ -#define HAVE_SHA2 - #define SCTP_READ_RANDOM(buf, len) read_random(buf, len) -#ifdef USE_SCTP_SHA1 -#include -#else -#include /* map standard crypto API names */ -#define SHA1_Init SHA1Init -#define SHA1_Update SHA1Update -#define SHA1_Final(x,y) SHA1Final((caddr_t)x, y) -#endif +#define SCTP_SHA1_CTX SHA1_CTX +#define SCTP_SHA1_INIT SHA1Init +#define SCTP_SHA1_UPDATE SHA1Update +#define SCTP_SHA1_FINAL(x,y) SHA1Final((caddr_t)x, y) -#if defined(HAVE_SHA2) -#include -#endif +#define SCTP_SHA256_CTX SHA256_CTX +#define SCTP_SHA256_INIT SHA256_Init +#define SCTP_SHA256_UPDATE SHA256_Update +#define SCTP_SHA256_FINAL(x,y) SHA256_Final((caddr_t)x, y) #endif diff --git a/sys/netinet/sctp_output.c b/sys/netinet/sctp_output.c index e07bb05a5af..34edf5b0e60 100644 --- a/sys/netinet/sctp_output.c +++ b/sys/netinet/sctp_output.c @@ -3561,7 +3561,7 @@ sctp_process_cmsgs_for_init(struct sctp_tcb *stcb, struct mbuf *control, int *er static struct sctp_tcb * sctp_findassociation_cmsgs(struct sctp_inpcb **inp_p, - in_port_t port, + uint16_t port, struct mbuf *control, struct sctp_nets **net_p, int *error) @@ -5406,6 +5406,14 @@ do_a_abort: } SCTP_BUF_LEN(m) = sizeof(struct sctp_init_chunk); + /* + * We might not overwrite the identification[] completely and on + * some platforms time_entered will contain some padding. Therefore + * zero out the cookie to avoid putting uninitialized memory on the + * wire. + */ + memset(&stc, 0, sizeof(struct sctp_state_cookie)); + /* the time I built cookie */ (void)SCTP_GETTIME_TIMEVAL(&stc.time_entered); @@ -6059,7 +6067,6 @@ sctp_get_frag_point(struct sctp_tcb *stcb, static void sctp_set_prsctp_policy(struct sctp_stream_queue_pending *sp) { - sp->pr_sctp_on = 0; /* * We assume that the user wants PR_SCTP_TTL if the user provides a * positive lifetime but does not specify any PR_SCTP policy. This @@ -6069,7 +6076,6 @@ sctp_set_prsctp_policy(struct sctp_stream_queue_pending *sp) */ if (PR_SCTP_ENABLED(sp->sinfo_flags)) { sp->act_flags |= PR_SCTP_POLICY(sp->sinfo_flags); - sp->pr_sctp_on = 1; } else { return; } @@ -6406,7 +6412,7 @@ sctp_sendall_iterator(struct sctp_inpcb *inp, struct sctp_tcb *stcb, void *ptr, /* TSNH */ return; } - if ((ca->m) && ca->sndlen) { + if (ca->sndlen > 0) { m = SCTP_M_COPYM(ca->m, 0, M_COPYALL, M_NOWAIT); if (m == NULL) { /* can't copy so we are done */ @@ -6435,38 +6441,40 @@ sctp_sendall_iterator(struct sctp_inpcb *inp, struct sctp_tcb *stcb, void *ptr, } if (ca->sndrcv.sinfo_flags & SCTP_ABORT) { /* Abort this assoc with m as the user defined reason */ - if (m) { + if (m != NULL) { + SCTP_BUF_PREPEND(m, sizeof(struct sctp_paramhdr), M_NOWAIT); + } else { + m = sctp_get_mbuf_for_msg(sizeof(struct sctp_paramhdr), + 0, M_NOWAIT, 1, MT_DATA); + SCTP_BUF_LEN(m) = sizeof(struct sctp_paramhdr); + } + if (m != NULL) { struct sctp_paramhdr *ph; - SCTP_BUF_PREPEND(m, sizeof(struct sctp_paramhdr), M_NOWAIT); - if (m) { - ph = mtod(m, struct sctp_paramhdr *); - ph->param_type = htons(SCTP_CAUSE_USER_INITIATED_ABT); - ph->param_length = htons(sizeof(struct sctp_paramhdr) + ca->sndlen); - } - /* - * We add one here to keep the assoc from - * dis-appearing on us. - */ - atomic_add_int(&stcb->asoc.refcnt, 1); - sctp_abort_an_association(inp, stcb, m, SCTP_SO_NOT_LOCKED); - /* - * sctp_abort_an_association calls sctp_free_asoc() - * free association will NOT free it since we - * incremented the refcnt .. we do this to prevent - * it being freed and things getting tricky since we - * could end up (from free_asoc) calling inpcb_free - * which would get a recursive lock call to the - * iterator lock.. But as a consequence of that the - * stcb will return to us un-locked.. since - * free_asoc returns with either no TCB or the TCB - * unlocked, we must relock.. to unlock in the - * iterator timer :-0 - */ - SCTP_TCB_LOCK(stcb); - atomic_add_int(&stcb->asoc.refcnt, -1); - goto no_chunk_output; + ph = mtod(m, struct sctp_paramhdr *); + ph->param_type = htons(SCTP_CAUSE_USER_INITIATED_ABT); + ph->param_length = htons(sizeof(struct sctp_paramhdr) + ca->sndlen); } + /* + * We add one here to keep the assoc from dis-appearing on + * us. + */ + atomic_add_int(&stcb->asoc.refcnt, 1); + sctp_abort_an_association(inp, stcb, m, SCTP_SO_NOT_LOCKED); + /* + * sctp_abort_an_association calls sctp_free_asoc() free + * association will NOT free it since we incremented the + * refcnt .. we do this to prevent it being freed and things + * getting tricky since we could end up (from free_asoc) + * calling inpcb_free which would get a recursive lock call + * to the iterator lock.. But as a consequence of that the + * stcb will return to us un-locked.. since free_asoc + * returns with either no TCB or the TCB unlocked, we must + * relock.. to unlock in the iterator timer :-0 + */ + SCTP_TCB_LOCK(stcb); + atomic_add_int(&stcb->asoc.refcnt, -1); + goto no_chunk_output; } else { if (m) { ret = sctp_msg_append(stcb, net, m, @@ -6560,8 +6568,7 @@ sctp_sendall_iterator(struct sctp_inpcb *inp, struct sctp_tcb *stcb, void *ptr, if ((sctp_is_feature_off(inp, SCTP_PCB_FLAGS_NODELAY)) && (stcb->asoc.total_flight > 0) && - (un_sent < (int)(stcb->asoc.smallest_mtu - SCTP_MIN_OVERHEAD)) - ) { + (un_sent < (int)(stcb->asoc.smallest_mtu - SCTP_MIN_OVERHEAD))) { do_chunk_output = 0; } if (do_chunk_output) @@ -6690,13 +6697,10 @@ sctp_sendall(struct sctp_inpcb *inp, struct uio *uio, struct mbuf *m, /* Gather the length of the send */ struct mbuf *mat; - mat = m; ca->sndlen = 0; - while (m) { - ca->sndlen += SCTP_BUF_LEN(m); - m = SCTP_BUF_NEXT(m); + for (mat = m; mat; mat = SCTP_BUF_NEXT(mat)) { + ca->sndlen += SCTP_BUF_LEN(mat); } - ca->m = mat; } ret = sctp_initiate_iterator(NULL, sctp_sendall_iterator, NULL, SCTP_PCB_ANY_FLAGS, SCTP_PCB_ANY_FEATURES, @@ -7417,13 +7421,8 @@ dont_do_it: } chk->send_size += pads; } - /* We only re-set the policy if it is on */ - if (sp->pr_sctp_on) { - sctp_set_prsctp_policy(sp); + if (PR_SCTP_ENABLED(chk->flags)) { asoc->pr_sctp_cnt++; - chk->pr_sctp_on = 1; - } else { - chk->pr_sctp_on = 0; } if (sp->msg_is_complete && (sp->length == 0) && (sp->sender_all_done)) { /* All done pull and kill the message */ @@ -7613,7 +7612,7 @@ sctp_med_chunk_output(struct sctp_inpcb *inp, #endif ) { - /* + /** * Ok this is the generic chunk service queue. we must do the * following: - Service the stream queue that is next, moving any * message (note I must get a complete message i.e. FIRST/MIDDLE and diff --git a/sys/netinet/sctp_pcb.h b/sys/netinet/sctp_pcb.h index 91807c7cd90..8045765c4c5 100644 --- a/sys/netinet/sctp_pcb.h +++ b/sys/netinet/sctp_pcb.h @@ -388,8 +388,8 @@ struct sctp_inpcb { /* back pointer to our socket */ struct socket *sctp_socket; + uint64_t sctp_features; /* Feature flags */ uint32_t sctp_flags; /* INP state flag set */ - uint32_t sctp_features; /* Feature flags */ uint32_t sctp_mobility_features; /* Mobility Feature flags */ struct sctp_pcb sctp_ep;/* SCTP ep data */ /* head of the hash of all associations */ diff --git a/sys/netinet/sctp_structs.h b/sys/netinet/sctp_structs.h index bc18f0e8315..a8b86c62040 100644 --- a/sys/netinet/sctp_structs.h +++ b/sys/netinet/sctp_structs.h @@ -446,7 +446,6 @@ struct sctp_tmit_chunk { uint8_t do_rtt; uint8_t book_size_scale; uint8_t no_fr_allowed; - uint8_t pr_sctp_on; uint8_t copy_by_ref; uint8_t window_probe; }; @@ -522,7 +521,6 @@ struct sctp_stream_queue_pending { uint8_t holds_key_ref; uint8_t msg_is_complete; uint8_t some_taken; - uint8_t pr_sctp_on; uint8_t sender_all_done; uint8_t put_last_out; uint8_t discard_rest; @@ -1205,7 +1203,7 @@ struct sctp_association { /* JRS 5/21/07 - CMT PF variable */ uint8_t sctp_cmt_pf; uint8_t use_precise_time; - uint32_t sctp_features; + uint64_t sctp_features; uint16_t port; /* remote UDP encapsulation port */ /* * The mapping array is used to track out of order sequences above diff --git a/sys/netinet/sctp_timer.c b/sys/netinet/sctp_timer.c index a09ba34ff75..833c94a062a 100644 --- a/sys/netinet/sctp_timer.c +++ b/sys/netinet/sctp_timer.c @@ -446,7 +446,7 @@ sctp_recover_sent_list(struct sctp_tcb *stcb) } } TAILQ_REMOVE(&asoc->sent_queue, chk, sctp_next); - if (chk->pr_sctp_on) { + if (PR_SCTP_ENABLED(chk->flags)) { if (asoc->pr_sctp_cnt != 0) asoc->pr_sctp_cnt--; } diff --git a/sys/netinet/sctp_uio.h b/sys/netinet/sctp_uio.h index 794c10b022d..129539170cb 100644 --- a/sys/netinet/sctp_uio.h +++ b/sys/netinet/sctp_uio.h @@ -662,10 +662,6 @@ struct sctp_hmacalgo { #define SCTP_AUTH_HMAC_ID_RSVD 0x0000 #define SCTP_AUTH_HMAC_ID_SHA1 0x0001 /* default, mandatory */ #define SCTP_AUTH_HMAC_ID_SHA256 0x0003 -#define SCTP_AUTH_HMAC_ID_SHA224 0x0004 -#define SCTP_AUTH_HMAC_ID_SHA384 0x0005 -#define SCTP_AUTH_HMAC_ID_SHA512 0x0006 - /* SCTP_AUTH_ACTIVE_KEY / SCTP_AUTH_DELETE_KEY */ struct sctp_authkeyid { @@ -1149,7 +1145,7 @@ union sctp_sockstore { struct xsctp_inpcb { uint32_t last; uint32_t flags; - uint32_t features; + uint64_t features; uint32_t total_sends; uint32_t total_recvs; uint32_t total_nospaces; @@ -1157,7 +1153,7 @@ struct xsctp_inpcb { uint16_t local_port; uint16_t qlen; uint16_t maxqlen; - uint32_t extra_padding[32]; /* future */ + uint32_t extra_padding[31]; /* future */ }; struct xsctp_tcb { diff --git a/sys/netinet/sctputil.c b/sys/netinet/sctputil.c index bdc3c90257d..c101118b992 100644 --- a/sys/netinet/sctputil.c +++ b/sys/netinet/sctputil.c @@ -4833,7 +4833,6 @@ sctp_release_pr_sctp_chunk(struct sctp_tcb *stcb, struct sctp_tmit_chunk *tp1, atomic_add_int(&chk->whoTo->ref_count, 1); chk->rec.data.TSN_seq = atomic_fetchadd_int(&stcb->asoc.sending_seq, 1); stcb->asoc.pr_sctp_cnt++; - chk->pr_sctp_on = 1; TAILQ_INSERT_TAIL(&stcb->asoc.sent_queue, chk, sctp_next); stcb->asoc.sent_queue_cnt++; stcb->asoc.pr_sctp_cnt++; diff --git a/sys/netinet/tcp_input.c b/sys/netinet/tcp_input.c index 99bbbd3c41b..0d7eb193349 100644 --- a/sys/netinet/tcp_input.c +++ b/sys/netinet/tcp_input.c @@ -54,6 +54,7 @@ __FBSDID("$FreeBSD$"); #include "opt_inet.h" #include "opt_inet6.h" #include "opt_ipsec.h" +#include "opt_kdtrace.h" #include "opt_tcpdebug.h" #include @@ -63,6 +64,7 @@ __FBSDID("$FreeBSD$"); #include #include /* for proc0 declaration */ #include +#include #include #include #include @@ -82,6 +84,7 @@ __FBSDID("$FreeBSD$"); #include #include +#include #include #include #include @@ -688,7 +691,10 @@ tcp_input(struct mbuf *m, int off0) bzero(ipov->ih_x1, sizeof(ipov->ih_x1)); ipov->ih_len = htons(tlen); th->th_sum = in_cksum(m, len); + /* Reset length for SDT probes. */ + ip->ip_len = htons(tlen + off0); } + if (th->th_sum) { TCPSTAT_INC(tcps_rcvbadsum); goto drop; @@ -1384,6 +1390,8 @@ relocked: } #endif + TCP_PROBE5(receive, NULL, tp, m->m_data, tp, th); + /* * Segment belongs to a connection in SYN_SENT, ESTABLISHED or later * state. tcp_do_segment() always consumes the mbuf chain, unlocks @@ -1394,6 +1402,8 @@ relocked: return; dropwithreset: + TCP_PROBE5(receive, NULL, tp, m->m_data, tp, th); + if (ti_locked == TI_WLOCKED) { INP_INFO_WUNLOCK(&V_tcbinfo); ti_locked = TI_UNLOCKED; @@ -1415,6 +1425,9 @@ dropwithreset: goto drop; dropunlock: + if (m != NULL) + TCP_PROBE5(receive, NULL, tp, m->m_data, tp, th); + if (ti_locked == TI_WLOCKED) { INP_INFO_WUNLOCK(&V_tcbinfo); ti_locked = TI_UNLOCKED; @@ -1910,8 +1923,11 @@ tcp_do_segment(struct mbuf *m, struct tcphdr *th, struct socket *so, rstreason = BANDLIM_UNLIMITED; goto dropwithreset; } - if ((thflags & (TH_ACK|TH_RST)) == (TH_ACK|TH_RST)) + if ((thflags & (TH_ACK|TH_RST)) == (TH_ACK|TH_RST)) { + TCP_PROBE5(connect_refused, NULL, tp, m->m_data, tp, + th); tp = tcp_drop(tp, ECONNREFUSED); + } if (thflags & TH_RST) goto drop; if (!(thflags & TH_SYN)) @@ -1956,11 +1972,13 @@ tcp_do_segment(struct mbuf *m, struct tcphdr *th, struct socket *so, */ tp->t_starttime = ticks; if (tp->t_flags & TF_NEEDFIN) { - tp->t_state = TCPS_FIN_WAIT_1; + tcp_state_change(tp, TCPS_FIN_WAIT_1); tp->t_flags &= ~TF_NEEDFIN; thflags &= ~TH_SYN; } else { - tp->t_state = TCPS_ESTABLISHED; + tcp_state_change(tp, TCPS_ESTABLISHED); + TCP_PROBE5(connect_established, NULL, tp, + m->m_data, tp, th); cc_conn_init(tp); tcp_timer_activate(tp, TT_KEEP, TP_KEEPIDLE(tp)); @@ -1978,7 +1996,7 @@ tcp_do_segment(struct mbuf *m, struct tcphdr *th, struct socket *so, */ tp->t_flags |= (TF_ACKNOW | TF_NEEDSYN); tcp_timer_activate(tp, TT_REXMT, 0); - tp->t_state = TCPS_SYN_RECEIVED; + tcp_state_change(tp, TCPS_SYN_RECEIVED); } KASSERT(ti_locked == TI_WLOCKED, ("%s: trimthenstep6: " @@ -2116,7 +2134,7 @@ tcp_do_segment(struct mbuf *m, struct tcphdr *th, struct socket *so, ti_locked)); INP_INFO_WLOCK_ASSERT(&V_tcbinfo); - tp->t_state = TCPS_CLOSED; + tcp_state_change(tp, TCPS_CLOSED); TCPSTAT_INC(tcps_drops); tp = tcp_close(tp); break; @@ -2361,10 +2379,12 @@ tcp_do_segment(struct mbuf *m, struct tcphdr *th, struct socket *so, */ tp->t_starttime = ticks; if (tp->t_flags & TF_NEEDFIN) { - tp->t_state = TCPS_FIN_WAIT_1; + tcp_state_change(tp, TCPS_FIN_WAIT_1); tp->t_flags &= ~TF_NEEDFIN; } else { - tp->t_state = TCPS_ESTABLISHED; + tcp_state_change(tp, TCPS_ESTABLISHED); + TCP_PROBE5(accept_established, NULL, tp, m->m_data, tp, + th); cc_conn_init(tp); tcp_timer_activate(tp, TT_KEEP, TP_KEEPIDLE(tp)); } @@ -2752,7 +2772,7 @@ process_ACK: tcp_finwait2_timeout : TP_MAXIDLE(tp))); } - tp->t_state = TCPS_FIN_WAIT_2; + tcp_state_change(tp, TCPS_FIN_WAIT_2); } break; @@ -2978,7 +2998,7 @@ dodata: /* XXX */ tp->t_starttime = ticks; /* FALLTHROUGH */ case TCPS_ESTABLISHED: - tp->t_state = TCPS_CLOSE_WAIT; + tcp_state_change(tp, TCPS_CLOSE_WAIT); break; /* @@ -2986,7 +3006,7 @@ dodata: /* XXX */ * enter the CLOSING state. */ case TCPS_FIN_WAIT_1: - tp->t_state = TCPS_CLOSING; + tcp_state_change(tp, TCPS_CLOSING); break; /* diff --git a/sys/netinet/tcp_lro.c b/sys/netinet/tcp_lro.c index 032d47c2fe7..63a6bbafc87 100644 --- a/sys/netinet/tcp_lro.c +++ b/sys/netinet/tcp_lro.c @@ -193,6 +193,25 @@ tcp_lro_rx_csum_fixup(struct lro_entry *le, void *l3hdr, struct tcphdr *th, } #endif +void +tcp_lro_flush_inactive(struct lro_ctrl *lc, const struct timeval *timeout) +{ + struct lro_entry *le, *le_tmp; + struct timeval tv; + + if (SLIST_EMPTY(&lc->lro_active)) + return; + + getmicrotime(&tv); + timevalsub(&tv, timeout); + SLIST_FOREACH_SAFE(le, &lc->lro_active, next, le_tmp) { + if (timevalcmp(&tv, &le->mtime, >=)) { + SLIST_REMOVE(&lc->lro_active, le, lro_entry, next); + tcp_lro_flush(lc, le); + } + } +} + void tcp_lro_flush(struct lro_ctrl *lc, struct lro_entry *le) { @@ -543,7 +562,8 @@ tcp_lro_rx(struct lro_ctrl *lc, struct mbuf *m, uint32_t csum) if (le->p_len > (65535 - lc->ifp->if_mtu)) { SLIST_REMOVE(&lc->lro_active, le, lro_entry, next); tcp_lro_flush(lc, le); - } + } else + getmicrotime(&le->mtime); return (0); } @@ -556,6 +576,7 @@ tcp_lro_rx(struct lro_ctrl *lc, struct mbuf *m, uint32_t csum) le = SLIST_FIRST(&lc->lro_free); SLIST_REMOVE_HEAD(&lc->lro_free, next); SLIST_INSERT_HEAD(&lc->lro_active, le, next); + getmicrotime(&le->mtime); /* Start filling in details. */ switch (eh_type) { diff --git a/sys/netinet/tcp_lro.h b/sys/netinet/tcp_lro.h index b3a501798aa..ab6d74ac900 100644 --- a/sys/netinet/tcp_lro.h +++ b/sys/netinet/tcp_lro.h @@ -30,6 +30,8 @@ #ifndef _TCP_LRO_H_ #define _TCP_LRO_H_ +#include + struct lro_entry { SLIST_ENTRY(lro_entry) next; @@ -59,6 +61,7 @@ struct lro_entry uint32_t tsecr; uint16_t window; uint16_t timestamp; /* flag, not a TCP hdr field. */ + struct timeval mtime; }; SLIST_HEAD(lro_head, lro_entry); @@ -83,6 +86,7 @@ struct lro_ctrl { int tcp_lro_init(struct lro_ctrl *); void tcp_lro_free(struct lro_ctrl *); +void tcp_lro_flush_inactive(struct lro_ctrl *, const struct timeval *); void tcp_lro_flush(struct lro_ctrl *, struct lro_entry *); int tcp_lro_rx(struct lro_ctrl *, struct mbuf *, uint32_t); diff --git a/sys/netinet/tcp_output.c b/sys/netinet/tcp_output.c index b4342f3b7b3..00d541596b0 100644 --- a/sys/netinet/tcp_output.c +++ b/sys/netinet/tcp_output.c @@ -35,6 +35,7 @@ __FBSDID("$FreeBSD$"); #include "opt_inet.h" #include "opt_inet6.h" #include "opt_ipsec.h" +#include "opt_kdtrace.h" #include "opt_tcpdebug.h" #include @@ -46,6 +47,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include #include @@ -56,6 +58,7 @@ __FBSDID("$FreeBSD$"); #include #include +#include #include #include #include @@ -1174,6 +1177,18 @@ send: */ ip6->ip6_hlim = in6_selecthlim(tp->t_inpcb, NULL); + /* + * Set the packet size here for the benefit of DTrace probes. + * ip6_output() will set it properly; it's supposed to include + * the option header lengths as well. + */ + ip6->ip6_plen = htons(m->m_pkthdr.len - sizeof(*ip6)); + + if (tp->t_state == TCPS_SYN_SENT) + TCP_PROBE5(connect_request, NULL, tp, ip6, tp, th); + + TCP_PROBE5(send, NULL, tp, ip6, tp, th); + /* TODO: IPv6 IP6TOS_ECT bit on */ error = ip6_output(m, tp->t_inpcb->in6p_outputopts, &ro, ((so->so_options & SO_DONTROUTE) ? IP_ROUTETOIF : 0), @@ -1208,6 +1223,11 @@ send: if (V_path_mtu_discovery && tp->t_maxopd > V_tcp_minmss) ip->ip_off |= htons(IP_DF); + if (tp->t_state == TCPS_SYN_SENT) + TCP_PROBE5(connect_request, NULL, tp, ip, tp, th); + + TCP_PROBE5(send, NULL, tp, ip, tp, th); + error = ip_output(m, tp->t_inpcb->inp_options, &ro, ((so->so_options & SO_DONTROUTE) ? IP_ROUTETOIF : 0), 0, tp->t_inpcb); diff --git a/sys/netinet/tcp_subr.c b/sys/netinet/tcp_subr.c index c466bf470c7..5d37b504707 100644 --- a/sys/netinet/tcp_subr.c +++ b/sys/netinet/tcp_subr.c @@ -36,6 +36,7 @@ __FBSDID("$FreeBSD$"); #include "opt_inet.h" #include "opt_inet6.h" #include "opt_ipsec.h" +#include "opt_kdtrace.h" #include "opt_tcpdebug.h" #include @@ -53,6 +54,7 @@ __FBSDID("$FreeBSD$"); #endif #include #include +#include #include #include #include @@ -66,6 +68,7 @@ __FBSDID("$FreeBSD$"); #include #include +#include #include #include #include @@ -632,8 +635,8 @@ tcp_respond(struct tcpcb *tp, void *ipgen, struct tcphdr *th, struct mbuf *m, ip6->ip6_flow = 0; ip6->ip6_vfc = IPV6_VERSION; ip6->ip6_nxt = IPPROTO_TCP; - ip6->ip6_plen = 0; /* Set in ip6_output(). */ tlen += sizeof (struct ip6_hdr) + sizeof (struct tcphdr); + ip6->ip6_plen = htons(tlen - sizeof(*ip6)); } #endif #if defined(INET) && defined(INET6) @@ -702,6 +705,10 @@ tcp_respond(struct tcpcb *tp, void *ipgen, struct tcphdr *th, struct mbuf *m, if (tp == NULL || (inp->inp_socket->so_options & SO_DEBUG)) tcp_trace(TA_OUTPUT, 0, tp, mtod(m, void *), th, 0); #endif + if (flags & TH_RST) + TCP_PROBE5(accept_refused, NULL, NULL, m->m_data, tp, nth); + + TCP_PROBE5(send, NULL, tp, m->m_data, tp, nth); #ifdef INET6 if (isipv6) (void) ip6_output(m, NULL, NULL, ipflags, NULL, NULL, inp); @@ -882,7 +889,7 @@ tcp_drop(struct tcpcb *tp, int errno) INP_WLOCK_ASSERT(tp->t_inpcb); if (TCPS_HAVERCVDSYN(tp->t_state)) { - tp->t_state = TCPS_CLOSED; + tcp_state_change(tp, TCPS_CLOSED); (void) tcp_output(tp); TCPSTAT_INC(tcps_drops); } else @@ -2376,3 +2383,19 @@ tcp_log_addr(struct in_conninfo *inc, struct tcphdr *th, void *ip4hdr, panic("%s: string too long", __func__); return (s); } + +/* + * A subroutine which makes it easy to track TCP state changes with DTrace. + * This function shouldn't be called for t_state initializations that don't + * correspond to actual TCP state transitions. + */ +void +tcp_state_change(struct tcpcb *tp, int newstate) +{ +#if defined(KDTRACE_HOOKS) + int pstate = tp->t_state; +#endif + + tp->t_state = newstate; + TCP_PROBE6(state_change, NULL, tp, NULL, tp, NULL, pstate); +} diff --git a/sys/netinet/tcp_syncache.c b/sys/netinet/tcp_syncache.c index cd7b424e0b1..1c27d7a0218 100644 --- a/sys/netinet/tcp_syncache.c +++ b/sys/netinet/tcp_syncache.c @@ -835,7 +835,7 @@ syncache_socket(struct syncache *sc, struct socket *lso, struct mbuf *m) #endif /* INET */ INP_HASH_WUNLOCK(&V_tcbinfo); tp = intotcpcb(inp); - tp->t_state = TCPS_SYN_RECEIVED; + tcp_state_change(tp, TCPS_SYN_RECEIVED); tp->iss = sc->sc_iss; tp->irs = sc->sc_irs; tcp_rcvseqinit(tp); diff --git a/sys/netinet/tcp_usrreq.c b/sys/netinet/tcp_usrreq.c index ef0aad1639e..88755e739dd 100644 --- a/sys/netinet/tcp_usrreq.c +++ b/sys/netinet/tcp_usrreq.c @@ -367,7 +367,7 @@ tcp_usr_listen(struct socket *so, int backlog, struct thread *td) error = in_pcbbind(inp, (struct sockaddr *)0, td->td_ucred); INP_HASH_WUNLOCK(&V_tcbinfo); if (error == 0) { - tp->t_state = TCPS_LISTEN; + tcp_state_change(tp, TCPS_LISTEN); solisten_proto(so, backlog); #ifdef TCP_OFFLOAD if ((so->so_options & SO_NO_OFFLOAD) == 0) @@ -412,7 +412,7 @@ tcp6_usr_listen(struct socket *so, int backlog, struct thread *td) } INP_HASH_WUNLOCK(&V_tcbinfo); if (error == 0) { - tp->t_state = TCPS_LISTEN; + tcp_state_change(tp, TCPS_LISTEN); solisten_proto(so, backlog); #ifdef TCP_OFFLOAD if ((so->so_options & SO_NO_OFFLOAD) == 0) @@ -1152,7 +1152,7 @@ tcp_connect(struct tcpcb *tp, struct sockaddr *nam, struct thread *td) soisconnecting(so); TCPSTAT_INC(tcps_connattempt); - tp->t_state = TCPS_SYN_SENT; + tcp_state_change(tp, TCPS_SYN_SENT); tp->iss = tcp_new_isn(tp); tcp_sendseqinit(tp); @@ -1224,7 +1224,7 @@ tcp6_connect(struct tcpcb *tp, struct sockaddr *nam, struct thread *td) soisconnecting(so); TCPSTAT_INC(tcps_connattempt); - tp->t_state = TCPS_SYN_SENT; + tcp_state_change(tp, TCPS_SYN_SENT); tp->iss = tcp_new_isn(tp); tcp_sendseqinit(tp); @@ -1704,7 +1704,7 @@ tcp_usrclosed(struct tcpcb *tp) #endif /* FALLTHROUGH */ case TCPS_CLOSED: - tp->t_state = TCPS_CLOSED; + tcp_state_change(tp, TCPS_CLOSED); tp = tcp_close(tp); /* * tcp_close() should never return NULL here as the socket is @@ -1720,11 +1720,11 @@ tcp_usrclosed(struct tcpcb *tp) break; case TCPS_ESTABLISHED: - tp->t_state = TCPS_FIN_WAIT_1; + tcp_state_change(tp, TCPS_FIN_WAIT_1); break; case TCPS_CLOSE_WAIT: - tp->t_state = TCPS_LAST_ACK; + tcp_state_change(tp, TCPS_LAST_ACK); break; } if (tp->t_state >= TCPS_FIN_WAIT_2) { diff --git a/sys/netinet/tcp_var.h b/sys/netinet/tcp_var.h index cfba5d9ccda..aaaa4a44e1f 100644 --- a/sys/netinet/tcp_var.h +++ b/sys/netinet/tcp_var.h @@ -593,24 +593,6 @@ struct xtcpcb { #define TCPCTL_MAXID 16 #define TCPCTL_FINWAIT2_TIMEOUT 17 -#define TCPCTL_NAMES { \ - { 0, 0 }, \ - { "rfc1323", CTLTYPE_INT }, \ - { "mssdflt", CTLTYPE_INT }, \ - { "stats", CTLTYPE_STRUCT }, \ - { "rttdflt", CTLTYPE_INT }, \ - { "keepidle", CTLTYPE_INT }, \ - { "keepintvl", CTLTYPE_INT }, \ - { "sendspace", CTLTYPE_INT }, \ - { "recvspace", CTLTYPE_INT }, \ - { "keepinit", CTLTYPE_INT }, \ - { "pcblist", CTLTYPE_STRUCT }, \ - { "delacktime", CTLTYPE_INT }, \ - { "v6mssdflt", CTLTYPE_INT }, \ - { "maxid", CTLTYPE_INT }, \ -} - - #ifdef _KERNEL #ifdef SYSCTL_DECL SYSCTL_DECL(_net_inet_tcp); @@ -701,6 +683,7 @@ struct inpcb * struct tcpcb * tcp_newtcpcb(struct inpcb *); int tcp_output(struct tcpcb *); +void tcp_state_change(struct tcpcb *, int); void tcp_respond(struct tcpcb *, void *, struct tcphdr *, struct mbuf *, tcp_seq, tcp_seq, int); void tcp_tw_init(void); diff --git a/sys/netinet/udp_usrreq.c b/sys/netinet/udp_usrreq.c index afd11e68c01..de4957de78c 100644 --- a/sys/netinet/udp_usrreq.c +++ b/sys/netinet/udp_usrreq.c @@ -42,6 +42,7 @@ __FBSDID("$FreeBSD$"); #include "opt_inet.h" #include "opt_inet6.h" #include "opt_ipsec.h" +#include "opt_kdtrace.h" #include #include @@ -54,6 +55,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include #include @@ -68,6 +70,7 @@ __FBSDID("$FreeBSD$"); #include #include +#include #include #include #include @@ -616,6 +619,8 @@ udp_input(struct mbuf *m, int off) m_freem(m); return; } + + UDP_PROBE(receive, NULL, inp, ip, inp, uh); udp_append(inp, ip, m, iphlen, &udp_in); INP_RUNLOCK(inp); return; @@ -1193,6 +1198,7 @@ udp_output(struct inpcb *inp, struct mbuf *m, struct sockaddr *addr, */ ui = mtod(m, struct udpiphdr *); bzero(ui->ui_x1, sizeof(ui->ui_x1)); /* XXX still needed? */ + ui->ui_v = IPVERSION << 4; ui->ui_pr = IPPROTO_UDP; ui->ui_src = laddr; ui->ui_dst = faddr; @@ -1243,6 +1249,7 @@ udp_output(struct inpcb *inp, struct mbuf *m, struct sockaddr *addr, INP_HASH_WUNLOCK(&V_udbinfo); else if (unlock_udbinfo == UH_RLOCKED) INP_HASH_RUNLOCK(&V_udbinfo); + UDP_PROBE(send, NULL, inp, &ui->ui_i, inp, &ui->ui_u); error = ip_output(m, inp->inp_options, NULL, ipflags, inp->inp_moptions, inp); if (unlock_udbinfo == UH_WLOCKED) diff --git a/sys/netinet/udp_var.h b/sys/netinet/udp_var.h index 0c26b88cd43..91a98f6c2b8 100644 --- a/sys/netinet/udp_var.h +++ b/sys/netinet/udp_var.h @@ -42,6 +42,7 @@ struct udpiphdr { struct udphdr ui_u; /* udp header */ }; #define ui_x1 ui_i.ih_x1 +#define ui_v ui_i.ih_x1[0] #define ui_pr ui_i.ih_pr #define ui_len ui_i.ih_len #define ui_src ui_i.ih_src @@ -123,15 +124,6 @@ void kmod_udpstat_inc(int statnum); #define UDPCTL_PCBLIST 5 /* list of PCBs for UDP sockets */ #define UDPCTL_MAXID 6 -#define UDPCTL_NAMES { \ - { 0, 0 }, \ - { "checksum", CTLTYPE_INT }, \ - { "stats", CTLTYPE_STRUCT }, \ - { "maxdgram", CTLTYPE_INT }, \ - { "recvspace", CTLTYPE_INT }, \ - { "pcblist", CTLTYPE_STRUCT }, \ -} - #ifdef _KERNEL SYSCTL_DECL(_net_inet_udp); diff --git a/sys/netinet6/in6.c b/sys/netinet6/in6.c index 9d9993091c7..e5c62df81a2 100644 --- a/sys/netinet6/in6.c +++ b/sys/netinet6/in6.c @@ -431,6 +431,18 @@ in6_control(struct socket *so, u_long cmd, caddr_t data, case SIOCGIFSTAT_ICMP6: sa6 = &ifr->ifr_addr; break; + case SIOCSIFADDR: + case SIOCSIFBRDADDR: + case SIOCSIFDSTADDR: + case SIOCSIFNETMASK: + /* + * Although we should pass any non-INET6 ioctl requests + * down to driver, we filter some legacy INET requests. + * Drivers trust SIOCSIFADDR et al to come from an already + * privileged layer, and do not perform any credentials + * checks or input validation. + */ + return (EINVAL); default: sa6 = NULL; break; diff --git a/sys/netinet6/in6_mcast.c b/sys/netinet6/in6_mcast.c index ce23aa8c34c..fca48b5c6d6 100644 --- a/sys/netinet6/in6_mcast.c +++ b/sys/netinet6/in6_mcast.c @@ -1625,6 +1625,8 @@ in6p_get_source_filters(struct inpcb *inp, struct sockopt *sopt) * has asked for, but we always tell userland how big the * buffer really needs to be. */ + if (msfr.msfr_nsrcs > in6_mcast_maxsocksrc) + msfr.msfr_nsrcs = in6_mcast_maxsocksrc; tss = NULL; if (msfr.msfr_srcs != NULL && msfr.msfr_nsrcs > 0) { tss = malloc(sizeof(struct sockaddr_storage) * msfr.msfr_nsrcs, diff --git a/sys/netinet6/ip6_input.c b/sys/netinet6/ip6_input.c index 02bbf75b1b8..a94d5c22c50 100644 --- a/sys/netinet6/ip6_input.c +++ b/sys/netinet6/ip6_input.c @@ -67,6 +67,7 @@ __FBSDID("$FreeBSD$"); #include "opt_inet6.h" #include "opt_ipfw.h" #include "opt_ipsec.h" +#include "opt_kdtrace.h" #include "opt_route.h" #include @@ -76,6 +77,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include #include @@ -92,6 +94,7 @@ __FBSDID("$FreeBSD$"); #include #include +#include #include #include #include @@ -304,7 +307,11 @@ ip6proto_unregister(short ip6proto) void ip6_destroy() { + int i; + if ((i = pfil_head_unregister(&V_inet6_pfil_hook)) != 0) + printf("%s: WARNING: unable to unregister pfil hook, " + "error %d\n", __func__, i); hashdestroy(V_in6_ifaddrhashtbl, M_IFADDR, V_in6_ifaddrhmask); nd6_destroy(); callout_drain(&V_in6_tmpaddrtimer_ch); @@ -538,6 +545,8 @@ ip6_input(struct mbuf *m) IP6STAT_INC(ip6s_nxthist[ip6->ip6_nxt]); + IP_PROBE(receive, NULL, NULL, ip6, m->m_pkthdr.rcvif, NULL, ip6); + /* * Check against address spoofing/corruption. */ diff --git a/sys/netinet6/ip6_mroute.c b/sys/netinet6/ip6_mroute.c index 194aaf5d3d6..36d0d6f183d 100644 --- a/sys/netinet6/ip6_mroute.c +++ b/sys/netinet6/ip6_mroute.c @@ -82,6 +82,7 @@ __FBSDID("$FreeBSD$"); #include "opt_inet6.h" +#include "opt_kdtrace.h" #include #include @@ -93,6 +94,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include #include @@ -114,6 +116,7 @@ __FBSDID("$FreeBSD$"); #include #include +#include #include #include #include @@ -573,7 +576,7 @@ int X_ip6_mrouter_done(void) { mifi_t mifi; - int i; + u_long i; struct mf6c *rt; struct rtdetq *rte; @@ -1338,7 +1341,7 @@ expire_upcalls(void *unused) { struct rtdetq *rte; struct mf6c *mfc, **nptr; - int i; + u_long i; MFC6_LOCK(); for (i = 0; i < MF6CTBLSIZ; i++) { @@ -1644,10 +1647,13 @@ phyint_send(struct ip6_hdr *ip6, struct mif6 *mifp, struct mbuf *m) dst6.sin6_len = sizeof(struct sockaddr_in6); dst6.sin6_family = AF_INET6; dst6.sin6_addr = ip6->ip6_dst; + + IP_PROBE(send, NULL, NULL, ip6, ifp, NULL, ip6); /* * We just call if_output instead of nd6_output here, since * we need no ND for a multicast forwarded packet...right? */ + m_clrprotoflags(m); /* Avoid confusing lower layers. */ error = (*ifp->if_output)(ifp, mb_copy, (struct sockaddr *)&dst6, NULL); #ifdef MRT6DEBUG diff --git a/sys/netinet6/ip6_output.c b/sys/netinet6/ip6_output.c index 5df3572b9bd..0d55b66290f 100644 --- a/sys/netinet6/ip6_output.c +++ b/sys/netinet6/ip6_output.c @@ -196,8 +196,8 @@ in6_delayed_cksum(struct mbuf *m, uint32_t plen, u_short offset) if (offset + sizeof(u_short) > m->m_len) { printf("%s: delayed m_pullup, m->len: %d plen %u off %u " - "csum_flags=0x%04x\n", __func__, m->m_len, plen, offset, - m->m_pkthdr.csum_flags); + "csum_flags=%b\n", __func__, m->m_len, plen, offset, + (int)m->m_pkthdr.csum_flags, CSUM_BITS); /* * XXX this should not happen, but if it does, the correct * behavior may be to insert the checksum in the appropriate diff --git a/sys/netinet6/ip6_var.h b/sys/netinet6/ip6_var.h index a02928c3d8d..1c22cc98100 100644 --- a/sys/netinet6/ip6_var.h +++ b/sys/netinet6/ip6_var.h @@ -293,7 +293,12 @@ struct ip6aux { #define IPV6_FORWARDING 0x02 /* most of IPv6 header exists */ #define IPV6_MINMTU 0x04 /* use minimum MTU (IPV6_USE_MIN_MTU) */ -#define M_IP6_NEXTHOP M_PROTO7 /* explicit ip nexthop */ +/* + * IPv6 protocol layer specific mbuf flags. + */ +#define M_IP6_NEXTHOP M_PROTO2 /* explicit ip nexthop */ +#define M_SKIP_FIREWALL M_PROTO3 /* skip firewall processing, + keep in sync with IPv4 */ #ifdef __NO_STRICT_ALIGNMENT #define IP6_HDR_ALIGNED_P(ip) 1 diff --git a/sys/netinet6/mld6.c b/sys/netinet6/mld6.c index 560e8d63e62..77b19bfb980 100644 --- a/sys/netinet6/mld6.c +++ b/sys/netinet6/mld6.c @@ -275,7 +275,7 @@ mld_save_context(struct mbuf *m, struct ifnet *ifp) { #ifdef VIMAGE - m->m_pkthdr.header = ifp->if_vnet; + m->m_pkthdr.PH_loc.ptr = ifp->if_vnet; #endif /* VIMAGE */ m->m_pkthdr.flowid = ifp->if_index; } @@ -284,7 +284,7 @@ static __inline void mld_scrub_context(struct mbuf *m) { - m->m_pkthdr.header = NULL; + m->m_pkthdr.PH_loc.ptr = NULL; m->m_pkthdr.flowid = 0; } @@ -300,7 +300,7 @@ mld_restore_context(struct mbuf *m) { #if defined(VIMAGE) && defined(INVARIANTS) - KASSERT(curvnet == m->m_pkthdr.header, + KASSERT(curvnet == m->m_pkthdr.PH_loc.ptr, ("%s: called when curvnet was not restored", __func__)); #endif return (m->m_pkthdr.flowid); @@ -3098,7 +3098,7 @@ mld_dispatch_packet(struct mbuf *m) } mld_scrub_context(m0); - m->m_flags &= ~(M_PROTOFLAGS); + m_clrprotoflags(m); m0->m_pkthdr.rcvif = V_loif; ip6 = mtod(m0, struct ip6_hdr *); diff --git a/sys/netinet6/nd6.c b/sys/netinet6/nd6.c index 5250f9346b4..e76121d3d4b 100644 --- a/sys/netinet6/nd6.c +++ b/sys/netinet6/nd6.c @@ -34,6 +34,7 @@ __FBSDID("$FreeBSD$"); #include "opt_inet.h" #include "opt_inet6.h" +#include "opt_kdtrace.h" #include #include @@ -50,6 +51,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include @@ -62,6 +64,7 @@ __FBSDID("$FreeBSD$"); #include #include +#include #include #define L3_ADDR_SIN6(le) ((struct sockaddr_in6 *) L3_ADDR(le)) #include @@ -1508,7 +1511,11 @@ nd6_ioctl(u_long cmd, caddr_t data, struct ifnet *ifp) nbi->state = ln->ln_state; nbi->asked = ln->la_asked; nbi->isrouter = ln->ln_router; - nbi->expire = ln->la_expire + (time_second - time_uptime); + if (ln->la_expire == 0) + nbi->expire = 0; + else + nbi->expire = ln->la_expire + + (time_second - time_uptime); LLE_RUNLOCK(ln); break; } @@ -2078,8 +2085,9 @@ nd6_output_lle(struct ifnet *ifp, struct ifnet *origifp, struct mbuf *m0, } return (error); } - /* Reset layer specific mbuf flags to avoid confusing lower layers. */ - m->m_flags &= ~(M_PROTOFLAGS); + m_clrprotoflags(m); /* Avoid confusing lower layers. */ + IP_PROBE(send, NULL, NULL, mtod(m, struct ip6_hdr *), ifp, NULL, + mtod(m, struct ip6_hdr *)); if ((ifp->if_flags & IFF_LOOPBACK) != 0) { return ((*ifp->if_output)(origifp, m, (struct sockaddr *)dst, NULL)); diff --git a/sys/netinet6/pim6_var.h b/sys/netinet6/pim6_var.h index 4157e84f8db..4842208f174 100644 --- a/sys/netinet6/pim6_var.h +++ b/sys/netinet6/pim6_var.h @@ -61,8 +61,4 @@ int pim6_input(struct mbuf **, int*, int); #define PIM6CTL_STATS 1 /* statistics (read-only) */ #define PIM6CTL_MAXID 2 -#define PIM6CTL_NAMES { \ - { 0, 0 }, \ - { 0, 0 }, \ -} #endif /* _NETINET6_PIM6_VAR_H_ */ diff --git a/sys/netinet6/sctp6_usrreq.c b/sys/netinet6/sctp6_usrreq.c index 9489c2146ba..511b0e83173 100644 --- a/sys/netinet6/sctp6_usrreq.c +++ b/sys/netinet6/sctp6_usrreq.c @@ -109,10 +109,10 @@ sctp6_input_with_port(struct mbuf **i_pak, int *offp, uint16_t port) } #endif SCTPDBG(SCTP_DEBUG_CRCOFFLOAD, - "sctp6_input(): Packet of length %d received on %s with csum_flags 0x%x.\n", + "sctp6_input(): Packet of length %d received on %s with csum_flags 0x%b.\n", m->m_pkthdr.len, if_name(m->m_pkthdr.rcvif), - m->m_pkthdr.csum_flags); + (int)m->m_pkthdr.csum_flags, CSUM_BITS); if (m->m_flags & M_FLOWID) { mflowid = m->m_pkthdr.flowid; use_mflowid = 1; diff --git a/sys/netinet6/send.c b/sys/netinet6/send.c index 5624366533d..aa6ec673fcd 100644 --- a/sys/netinet6/send.c +++ b/sys/netinet6/send.c @@ -27,12 +27,15 @@ #include __FBSDID("$FreeBSD$"); +#include "opt_kdtrace.h" + #include #include #include #include #include #include +#include #include #include #include @@ -46,6 +49,7 @@ __FBSDID("$FreeBSD$"); #include #include +#include #include #include #include @@ -180,6 +184,10 @@ send_output(struct mbuf *m, struct ifnet *ifp, int direction) dst.sin6_len = sizeof(dst); dst.sin6_addr = ip6->ip6_dst; + m_clrprotoflags(m); /* Avoid confusing lower layers. */ + + IP_PROBE(send, NULL, NULL, ip6, ifp, NULL, ip6); + /* * Output the packet as nd6.c:nd6_output_lle() would do. * The mbuf is always consumed, so we do not have to care diff --git a/sys/netinet6/udp6_usrreq.c b/sys/netinet6/udp6_usrreq.c index 6aadae10d47..7df0db75377 100644 --- a/sys/netinet6/udp6_usrreq.c +++ b/sys/netinet6/udp6_usrreq.c @@ -73,6 +73,7 @@ __FBSDID("$FreeBSD$"); #include "opt_inet6.h" #include "opt_ipfw.h" #include "opt_ipsec.h" +#include "opt_kdtrace.h" #include #include @@ -82,6 +83,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include #include @@ -95,6 +97,7 @@ __FBSDID("$FreeBSD$"); #include #include +#include #include #include #include @@ -377,6 +380,7 @@ udp6_input(struct mbuf **mp, int *offp, int proto) INP_RLOCK(last); INP_INFO_RUNLOCK(&V_udbinfo); up = intoudpcb(last); + UDP_PROBE(receive, NULL, last, ip6, last, uh); if (up->u_tun_func == NULL) { udp6_append(last, m, off, &fromsa); } else { @@ -455,6 +459,7 @@ udp6_input(struct mbuf **mp, int *offp, int proto) } INP_RLOCK_ASSERT(inp); up = intoudpcb(inp); + UDP_PROBE(receive, NULL, inp, ip6, inp, uh); if (up->u_tun_func == NULL) { udp6_append(inp, m, off, &fromsa); } else { @@ -771,9 +776,7 @@ udp6_output(struct inpcb *inp, struct mbuf *m, struct sockaddr *addr6, ip6->ip6_flow = inp->inp_flow & IPV6_FLOWINFO_MASK; ip6->ip6_vfc &= ~IPV6_VERSION_MASK; ip6->ip6_vfc |= IPV6_VERSION; -#if 0 /* ip6_plen will be filled in ip6_output. */ ip6->ip6_plen = htons((u_short)plen); -#endif ip6->ip6_nxt = IPPROTO_UDP; ip6->ip6_hlim = in6_selecthlim(inp, NULL); ip6->ip6_src = *laddr; @@ -785,6 +788,7 @@ udp6_output(struct inpcb *inp, struct mbuf *m, struct sockaddr *addr6, flags = 0; + UDP_PROBE(send, NULL, inp, ip6, inp, udp6); UDPSTAT_INC(udps_opackets); error = ip6_output(m, optp, NULL, flags, inp->in6p_moptions, NULL, inp); diff --git a/sys/netipsec/ipsec.h b/sys/netipsec/ipsec.h index 0b928761bda..240083ab659 100644 --- a/sys/netipsec/ipsec.h +++ b/sys/netipsec/ipsec.h @@ -264,40 +264,6 @@ struct ipsecstat { #define IPSECCTL_ESP_RANDPAD 13 #define IPSECCTL_MAXID 14 -#define IPSECCTL_NAMES { \ - { 0, 0 }, \ - { 0, 0 }, \ - { "def_policy", CTLTYPE_INT }, \ - { "esp_trans_deflev", CTLTYPE_INT }, \ - { "esp_net_deflev", CTLTYPE_INT }, \ - { "ah_trans_deflev", CTLTYPE_INT }, \ - { "ah_net_deflev", CTLTYPE_INT }, \ - { 0, 0 }, \ - { "ah_cleartos", CTLTYPE_INT }, \ - { "ah_offsetmask", CTLTYPE_INT }, \ - { "dfbit", CTLTYPE_INT }, \ - { "ecn", CTLTYPE_INT }, \ - { "debug", CTLTYPE_INT }, \ - { "esp_randpad", CTLTYPE_INT }, \ -} - -#define IPSEC6CTL_NAMES { \ - { 0, 0 }, \ - { 0, 0 }, \ - { "def_policy", CTLTYPE_INT }, \ - { "esp_trans_deflev", CTLTYPE_INT }, \ - { "esp_net_deflev", CTLTYPE_INT }, \ - { "ah_trans_deflev", CTLTYPE_INT }, \ - { "ah_net_deflev", CTLTYPE_INT }, \ - { 0, 0 }, \ - { 0, 0 }, \ - { 0, 0 }, \ - { 0, 0 }, \ - { "ecn", CTLTYPE_INT }, \ - { "debug", CTLTYPE_INT }, \ - { "esp_randpad", CTLTYPE_INT }, \ -} - #ifdef _KERNEL #include diff --git a/sys/netipsec/key_var.h b/sys/netipsec/key_var.h index edf232d86cc..2601ece8fdc 100644 --- a/sys/netipsec/key_var.h +++ b/sys/netipsec/key_var.h @@ -48,22 +48,6 @@ #define KEYCTL_PREFERED_OLDSA 12 #define KEYCTL_MAXID 13 -#define KEYCTL_NAMES { \ - { 0, 0 }, \ - { "debug", CTLTYPE_INT }, \ - { "spi_try", CTLTYPE_INT }, \ - { "spi_min_value", CTLTYPE_INT }, \ - { "spi_max_value", CTLTYPE_INT }, \ - { "random_int", CTLTYPE_INT }, \ - { "larval_lifetime", CTLTYPE_INT }, \ - { "blockacq_count", CTLTYPE_INT }, \ - { "blockacq_lifetime", CTLTYPE_INT }, \ - { "esp_keymin", CTLTYPE_INT }, \ - { "esp_auth", CTLTYPE_INT }, \ - { "ah_keymin", CTLTYPE_INT }, \ - { "prefered_oldsa", CTLTYPE_INT }, \ -} - #ifdef _KERNEL #define _ARRAYLEN(p) (sizeof(p)/sizeof(p[0])) #define _KEYLEN(key) ((u_int)((key)->bits >> 3)) diff --git a/sys/netnatm/natm.c b/sys/netnatm/natm.c index 681da9cd059..c6116501553 100644 --- a/sys/netnatm/natm.c +++ b/sys/netnatm/natm.c @@ -339,6 +339,21 @@ natm_usr_control(struct socket *so, u_long cmd, caddr_t arg, npcb = (struct natmpcb *)so->so_pcb; KASSERT(npcb != NULL, ("natm_usr_control: npcb == NULL")); + switch (cmd) { + case SIOCSIFADDR: + case SIOCSIFBRDADDR: + case SIOCSIFDSTADDR: + case SIOCSIFNETMASK: + /* + * Although we should pass any non-ATM ioctl requests + * down to driver, we filter some legacy INET requests. + * Drivers trust SIOCSIFADDR et al to come from an already + * privileged layer, and do not perform any credentials + * checks or input validation. + */ + return (EINVAL); + } + if (ifp == NULL || ifp->if_ioctl == NULL) return (EOPNOTSUPP); return ((*ifp->if_ioctl)(ifp, cmd, arg)); diff --git a/sys/netpfil/ipfw/ip_dummynet.c b/sys/netpfil/ipfw/ip_dummynet.c index 429f2f11dae..4de2156b20d 100644 --- a/sys/netpfil/ipfw/ip_dummynet.c +++ b/sys/netpfil/ipfw/ip_dummynet.c @@ -82,13 +82,15 @@ dummynet(void *arg) { (void)arg; /* UNUSED */ - taskqueue_enqueue(dn_tq, &dn_task); + taskqueue_enqueue_fast(dn_tq, &dn_task); } void dn_reschedule(void) { - callout_reset(&dn_timeout, 1, dummynet, NULL); + + callout_reset_sbt(&dn_timeout, tick_sbt, 0, dummynet, NULL, + C_HARDCLOCK | C_DIRECT_EXEC); } /*----- end of callout hooks -----*/ @@ -2159,12 +2161,12 @@ ip_dn_init(void) DN_LOCK_INIT(); TASK_INIT(&dn_task, 0, dummynet_task, curvnet); - dn_tq = taskqueue_create("dummynet", M_WAITOK, + dn_tq = taskqueue_create_fast("dummynet", M_WAITOK, taskqueue_thread_enqueue, &dn_tq); taskqueue_start_threads(&dn_tq, 1, PI_NET, "dummynet"); callout_init(&dn_timeout, CALLOUT_MPSAFE); - callout_reset(&dn_timeout, 1, dummynet, NULL); + dn_reschedule(); /* Initialize curr_time adjustment mechanics. */ getmicrouptime(&dn_cfg.prev_t); diff --git a/sys/netpfil/ipfw/ip_fw2.c b/sys/netpfil/ipfw/ip_fw2.c index 6317013307a..128afad3ad6 100644 --- a/sys/netpfil/ipfw/ip_fw2.c +++ b/sys/netpfil/ipfw/ip_fw2.c @@ -142,6 +142,8 @@ VNET_DEFINE(int, verbose_limit); /* layer3_chain contains the list of rules for layer 3 */ VNET_DEFINE(struct ip_fw_chain, layer3_chain); +VNET_DEFINE(int, ipfw_nat_ready) = 0; + ipfw_nat_t *ipfw_nat_ptr = NULL; struct cfg_nat *(*lookup_nat_ptr)(struct nat_list *, int); ipfw_nat_cfg_t *ipfw_nat_cfg_ptr; diff --git a/sys/netpfil/ipfw/ip_fw_nat.c b/sys/netpfil/ipfw/ip_fw_nat.c index 84852dbfc01..155eddd4cf1 100644 --- a/sys/netpfil/ipfw/ip_fw_nat.c +++ b/sys/netpfil/ipfw/ip_fw_nat.c @@ -53,8 +53,7 @@ __FBSDID("$FreeBSD$"); #include /* XXX for in_cksum */ -static VNET_DEFINE(eventhandler_tag, ifaddr_event_tag); -#define V_ifaddr_event_tag VNET(ifaddr_event_tag) +static eventhandler_tag ifaddr_event_tag; static void ifaddr_change(void *arg __unused, struct ifnet *ifp) @@ -63,6 +62,8 @@ ifaddr_change(void *arg __unused, struct ifnet *ifp) struct ifaddr *ifa; struct ip_fw_chain *chain; + KASSERT(curvnet == ifp->if_vnet, + ("curvnet(%p) differs from iface vnet(%p)", curvnet, ifp->if_vnet)); chain = &V_layer3_chain; IPFW_WLOCK(chain); /* Check every nat entry... */ @@ -589,26 +590,16 @@ ipfw_nat_get_log(struct sockopt *sopt) return(0); } -static void -ipfw_nat_init(void) +static int +vnet_ipfw_nat_init(const void *arg __unused) { - IPFW_WLOCK(&V_layer3_chain); - /* init ipfw hooks */ - ipfw_nat_ptr = ipfw_nat; - lookup_nat_ptr = lookup_nat; - ipfw_nat_cfg_ptr = ipfw_nat_cfg; - ipfw_nat_del_ptr = ipfw_nat_del; - ipfw_nat_get_cfg_ptr = ipfw_nat_get_cfg; - ipfw_nat_get_log_ptr = ipfw_nat_get_log; - IPFW_WUNLOCK(&V_layer3_chain); - V_ifaddr_event_tag = EVENTHANDLER_REGISTER( - ifaddr_event, ifaddr_change, - NULL, EVENTHANDLER_PRI_ANY); + V_ipfw_nat_ready = 1; + return (0); } -static void -ipfw_nat_destroy(void) +static int +vnet_ipfw_nat_uninit(const void *arg __unused) { struct cfg_nat *ptr, *ptr_temp; struct ip_fw_chain *chain; @@ -621,8 +612,33 @@ ipfw_nat_destroy(void) LibAliasUninit(ptr->lib); free(ptr, M_IPFW); } - EVENTHANDLER_DEREGISTER(ifaddr_event, V_ifaddr_event_tag); flush_nat_ptrs(chain, -1 /* flush all */); + V_ipfw_nat_ready = 0; + IPFW_WUNLOCK(chain); + return (0); +} + +static void +ipfw_nat_init(void) +{ + + /* init ipfw hooks */ + ipfw_nat_ptr = ipfw_nat; + lookup_nat_ptr = lookup_nat; + ipfw_nat_cfg_ptr = ipfw_nat_cfg; + ipfw_nat_del_ptr = ipfw_nat_del; + ipfw_nat_get_cfg_ptr = ipfw_nat_get_cfg; + ipfw_nat_get_log_ptr = ipfw_nat_get_log; + + ifaddr_event_tag = EVENTHANDLER_REGISTER(ifaddr_event, ifaddr_change, + NULL, EVENTHANDLER_PRI_ANY); +} + +static void +ipfw_nat_destroy(void) +{ + + EVENTHANDLER_DEREGISTER(ifaddr_event, ifaddr_event_tag); /* deregister ipfw_nat */ ipfw_nat_ptr = NULL; lookup_nat_ptr = NULL; @@ -630,7 +646,6 @@ ipfw_nat_destroy(void) ipfw_nat_del_ptr = NULL; ipfw_nat_get_cfg_ptr = NULL; ipfw_nat_get_log_ptr = NULL; - IPFW_WUNLOCK(chain); } static int @@ -640,11 +655,9 @@ ipfw_nat_modevent(module_t mod, int type, void *unused) switch (type) { case MOD_LOAD: - ipfw_nat_init(); break; case MOD_UNLOAD: - ipfw_nat_destroy(); break; default: @@ -660,8 +673,25 @@ static moduledata_t ipfw_nat_mod = { 0 }; -DECLARE_MODULE(ipfw_nat, ipfw_nat_mod, SI_SUB_PROTO_IFATTACHDOMAIN, SI_ORDER_ANY); +/* Define startup order. */ +#define IPFW_NAT_SI_SUB_FIREWALL SI_SUB_PROTO_IFATTACHDOMAIN +#define IPFW_NAT_MODEVENT_ORDER (SI_ORDER_ANY - 255) +#define IPFW_NAT_MODULE_ORDER (IPFW_NAT_MODEVENT_ORDER + 1) +#define IPFW_NAT_VNET_ORDER (IPFW_NAT_MODEVENT_ORDER + 2) + +DECLARE_MODULE(ipfw_nat, ipfw_nat_mod, IPFW_NAT_SI_SUB_FIREWALL, SI_ORDER_ANY); MODULE_DEPEND(ipfw_nat, libalias, 1, 1, 1); MODULE_DEPEND(ipfw_nat, ipfw, 2, 2, 2); MODULE_VERSION(ipfw_nat, 1); + +SYSINIT(ipfw_nat_init, IPFW_NAT_SI_SUB_FIREWALL, IPFW_NAT_MODULE_ORDER, + ipfw_nat_init, NULL); +VNET_SYSINIT(vnet_ipfw_nat_init, IPFW_NAT_SI_SUB_FIREWALL, IPFW_NAT_VNET_ORDER, + vnet_ipfw_nat_init, NULL); + +SYSUNINIT(ipfw_nat_destroy, IPFW_NAT_SI_SUB_FIREWALL, IPFW_NAT_MODULE_ORDER, + ipfw_nat_destroy, NULL); +VNET_SYSUNINIT(vnet_ipfw_nat_uninit, IPFW_NAT_SI_SUB_FIREWALL, + IPFW_NAT_VNET_ORDER, vnet_ipfw_nat_uninit, NULL); + /* end of file */ diff --git a/sys/netpfil/ipfw/ip_fw_private.h b/sys/netpfil/ipfw/ip_fw_private.h index a41cdf55a36..a8d7eea4edf 100644 --- a/sys/netpfil/ipfw/ip_fw_private.h +++ b/sys/netpfil/ipfw/ip_fw_private.h @@ -327,9 +327,11 @@ extern struct cfg_nat *(*lookup_nat_ptr)(struct nat_list *, int); typedef int ipfw_nat_t(struct ip_fw_args *, struct cfg_nat *, struct mbuf *); typedef int ipfw_nat_cfg_t(struct sockopt *); -extern ipfw_nat_t *ipfw_nat_ptr; -#define IPFW_NAT_LOADED (ipfw_nat_ptr != NULL) +VNET_DECLARE(int, ipfw_nat_ready); +#define V_ipfw_nat_ready VNET(ipfw_nat_ready) +#define IPFW_NAT_LOADED (V_ipfw_nat_ready) +extern ipfw_nat_t *ipfw_nat_ptr; extern ipfw_nat_cfg_t *ipfw_nat_cfg_ptr; extern ipfw_nat_cfg_t *ipfw_nat_del_ptr; extern ipfw_nat_cfg_t *ipfw_nat_get_cfg_ptr; diff --git a/sys/netpfil/pf/pf.c b/sys/netpfil/pf/pf.c index 8fa1c908274..2de8c40fa4e 100644 --- a/sys/netpfil/pf/pf.c +++ b/sys/netpfil/pf/pf.c @@ -5310,7 +5310,7 @@ pf_route(struct mbuf **m, struct pf_rule *r, int dir, struct ifnet *oifp, ip->ip_sum = in_cksum(m0, ip->ip_hl << 2); m0->m_pkthdr.csum_flags &= ~CSUM_IP; } - m0->m_flags &= ~(M_PROTOFLAGS); + m_clrprotoflags(m0); /* Avoid confusing lower layers. */ error = (*ifp->if_output)(ifp, m0, sintosa(&dst), NULL); goto done; } @@ -5335,7 +5335,7 @@ pf_route(struct mbuf **m, struct pf_rule *r, int dir, struct ifnet *oifp, m1 = m0->m_nextpkt; m0->m_nextpkt = NULL; if (error == 0) { - m0->m_flags &= ~(M_PROTOFLAGS); + m_clrprotoflags(m0); error = (*ifp->if_output)(ifp, m0, sintosa(&dst), NULL); } else m_freem(m0); diff --git a/sys/netpfil/pf/pf_lb.c b/sys/netpfil/pf/pf_lb.c index c82a1211301..f870bf46735 100644 --- a/sys/netpfil/pf/pf_lb.c +++ b/sys/netpfil/pf/pf_lb.c @@ -58,10 +58,9 @@ static struct pf_rule *pf_match_translation(struct pf_pdesc *, struct mbuf *, int, int, struct pfi_kif *, struct pf_addr *, u_int16_t, struct pf_addr *, uint16_t, int, struct pf_anchor_stackframe *); -static int pf_get_sport(sa_family_t, u_int8_t, struct pf_rule *, - struct pf_addr *, struct pf_addr *, u_int16_t, - struct pf_addr *, u_int16_t*, u_int16_t, u_int16_t, - struct pf_src_node **); +static int pf_get_sport(sa_family_t, uint8_t, struct pf_rule *, + struct pf_addr *, uint16_t, struct pf_addr *, uint16_t, struct pf_addr *, + uint16_t *, uint16_t, uint16_t, struct pf_src_node **); #define mix(a,b,c) \ do { \ @@ -210,13 +209,13 @@ pf_match_translation(struct pf_pdesc *pd, struct mbuf *m, int off, static int pf_get_sport(sa_family_t af, u_int8_t proto, struct pf_rule *r, - struct pf_addr *saddr, struct pf_addr *daddr, u_int16_t dport, - struct pf_addr *naddr, u_int16_t *nport, u_int16_t low, u_int16_t high, - struct pf_src_node **sn) + struct pf_addr *saddr, uint16_t sport, struct pf_addr *daddr, + uint16_t dport, struct pf_addr *naddr, uint16_t *nport, uint16_t low, + uint16_t high, struct pf_src_node **sn) { struct pf_state_key_cmp key; struct pf_addr init_addr; - u_int16_t cut; + uint16_t cut; bzero(&init_addr, sizeof(init_addr)); if (pf_map_addr(af, r, saddr, naddr, &init_addr, sn)) @@ -227,34 +226,38 @@ pf_get_sport(sa_family_t af, u_int8_t proto, struct pf_rule *r, high = 65535; } + bzero(&key, sizeof(key)); + key.af = af; + key.proto = proto; + key.port[0] = dport; + PF_ACPY(&key.addr[0], daddr, key.af); + do { - key.af = af; - key.proto = proto; - PF_ACPY(&key.addr[1], daddr, key.af); - PF_ACPY(&key.addr[0], naddr, key.af); - key.port[1] = dport; + PF_ACPY(&key.addr[1], naddr, key.af); /* * port search; start random, step; * similar 2 portloop in in_pcbbind */ if (!(proto == IPPROTO_TCP || proto == IPPROTO_UDP || - proto == IPPROTO_ICMP)) { - key.port[0] = dport; - if (pf_find_state_all(&key, PF_IN, NULL) == NULL) - return (0); - } else if (low == 0 && high == 0) { - key.port[0] = *nport; - if (pf_find_state_all(&key, PF_IN, NULL) == NULL) + proto == IPPROTO_ICMP) || (low == 0 && high == 0)) { + /* + * XXX bug: icmp states don't use the id on both sides. + * (traceroute -I through nat) + */ + key.port[1] = sport; + if (pf_find_state_all(&key, PF_IN, NULL) == NULL) { + *nport = sport; return (0); + } } else if (low == high) { - key.port[0] = htons(low); + key.port[1] = htons(low); if (pf_find_state_all(&key, PF_IN, NULL) == NULL) { *nport = htons(low); return (0); } } else { - u_int16_t tmp; + uint16_t tmp; if (low > high) { tmp = low; @@ -265,7 +268,7 @@ pf_get_sport(sa_family_t af, u_int8_t proto, struct pf_rule *r, cut = htonl(arc4random()) % (1 + high - low) + low; /* low <= cut <= high */ for (tmp = cut; tmp <= high; ++(tmp)) { - key.port[0] = htons(tmp); + key.port[1] = htons(tmp); if (pf_find_state_all(&key, PF_IN, NULL) == NULL) { *nport = htons(tmp); @@ -273,7 +276,7 @@ pf_get_sport(sa_family_t af, u_int8_t proto, struct pf_rule *r, } } for (tmp = cut - 1; tmp >= low; --(tmp)) { - key.port[0] = htons(tmp); + key.port[1] = htons(tmp); if (pf_find_state_all(&key, PF_IN, NULL) == NULL) { *nport = htons(tmp); @@ -551,8 +554,8 @@ pf_get_translation(struct pf_pdesc *pd, struct mbuf *m, int off, int direction, switch (r->action) { case PF_NAT: - if (pf_get_sport(pd->af, pd->proto, r, saddr, daddr, dport, - naddr, nport, r->rpool.proxy_port[0], + if (pf_get_sport(pd->af, pd->proto, r, saddr, sport, daddr, + dport, naddr, nport, r->rpool.proxy_port[0], r->rpool.proxy_port[1], sn)) { DPFPRINTF(PF_DEBUG_MISC, ("pf: NAT proxy port allocation (%u-%u) failed\n", diff --git a/sys/netsmb/smb_dev.c b/sys/netsmb/smb_dev.c index 0efb282ad02..279ae6716e6 100644 --- a/sys/netsmb/smb_dev.c +++ b/sys/netsmb/smb_dev.c @@ -378,6 +378,7 @@ int smb_dev2share(int fd, int mode, struct smb_cred *scred, struct smb_share **sspp, struct smb_dev **ssdp) { + cap_rights_t rights; struct file *fp, *fptmp; struct smb_dev *sdp; struct smb_share *ssp; @@ -385,7 +386,7 @@ smb_dev2share(int fd, int mode, struct smb_cred *scred, int error; td = curthread; - error = fget(td, fd, CAP_READ, &fp); + error = fget(td, fd, cap_rights_init(&rights, CAP_READ), &fp); if (error) return (error); fptmp = td->td_fpop; diff --git a/sys/nfsserver/nfs_srvkrpc.c b/sys/nfsserver/nfs_srvkrpc.c index db69df95cbc..85003b7b1be 100644 --- a/sys/nfsserver/nfs_srvkrpc.c +++ b/sys/nfsserver/nfs_srvkrpc.c @@ -168,6 +168,7 @@ nfssvc_nfsserver(struct thread *td, struct nfssvc_args *uap) struct file *fp; struct nfsd_addsock_args addsockarg; struct nfsd_nfsd_args nfsdarg; + cap_rights_t rights; int error; if (uap->flag & NFSSVC_ADDSOCK) { @@ -175,7 +176,8 @@ nfssvc_nfsserver(struct thread *td, struct nfssvc_args *uap) sizeof(addsockarg)); if (error) return (error); - error = fget(td, addsockarg.sock, CAP_SOCK_SERVER, &fp); + error = fget(td, addsockarg.sock, + cap_rights_init(&rights, CAP_SOCK_SERVER), &fp); if (error) return (error); if (fp->f_type != DTYPE_SOCKET) { diff --git a/sys/nlm/nlm_prot_impl.c b/sys/nlm/nlm_prot_impl.c index dca03006704..387b0090578 100644 --- a/sys/nlm/nlm_prot_impl.c +++ b/sys/nlm/nlm_prot_impl.c @@ -132,6 +132,11 @@ static time_t nlm_grace_threshold; */ static time_t nlm_next_idle_check; +/* + * A flag to indicate the server is already running. + */ +static int nlm_is_running; + /* * A socket to use for RPC - shared by all IPv4 RPC clients. */ @@ -1526,51 +1531,51 @@ nlm_server_main(int addr_count, char **addrs) struct nlm_waiting_lock *nw; vop_advlock_t *old_nfs_advlock; vop_reclaim_t *old_nfs_reclaim; - int v4_used; -#ifdef INET6 - int v6_used; -#endif - if (nlm_socket) { + if (nlm_is_running != 0) { NLM_ERR("NLM: can't start server - " "it appears to be running already\n"); return (EPERM); } - memset(&opt, 0, sizeof(opt)); + if (nlm_socket == NULL) { + memset(&opt, 0, sizeof(opt)); - nlm_socket = NULL; - error = socreate(AF_INET, &nlm_socket, SOCK_DGRAM, 0, - td->td_ucred, td); - if (error) { - NLM_ERR("NLM: can't create IPv4 socket - error %d\n", error); - return (error); - } - opt.sopt_dir = SOPT_SET; - opt.sopt_level = IPPROTO_IP; - opt.sopt_name = IP_PORTRANGE; - portlow = IP_PORTRANGE_LOW; - opt.sopt_val = &portlow; - opt.sopt_valsize = sizeof(portlow); - sosetopt(nlm_socket, &opt); + error = socreate(AF_INET, &nlm_socket, SOCK_DGRAM, 0, + td->td_ucred, td); + if (error) { + NLM_ERR("NLM: can't create IPv4 socket - error %d\n", + error); + return (error); + } + opt.sopt_dir = SOPT_SET; + opt.sopt_level = IPPROTO_IP; + opt.sopt_name = IP_PORTRANGE; + portlow = IP_PORTRANGE_LOW; + opt.sopt_val = &portlow; + opt.sopt_valsize = sizeof(portlow); + sosetopt(nlm_socket, &opt); #ifdef INET6 - nlm_socket6 = NULL; - error = socreate(AF_INET6, &nlm_socket6, SOCK_DGRAM, 0, - td->td_ucred, td); - if (error) { - NLM_ERR("NLM: can't create IPv6 socket - error %d\n", error); - goto out; - return (error); - } - opt.sopt_dir = SOPT_SET; - opt.sopt_level = IPPROTO_IPV6; - opt.sopt_name = IPV6_PORTRANGE; - portlow = IPV6_PORTRANGE_LOW; - opt.sopt_val = &portlow; - opt.sopt_valsize = sizeof(portlow); - sosetopt(nlm_socket6, &opt); + nlm_socket6 = NULL; + error = socreate(AF_INET6, &nlm_socket6, SOCK_DGRAM, 0, + td->td_ucred, td); + if (error) { + NLM_ERR("NLM: can't create IPv6 socket - error %d\n", + error); + soclose(nlm_socket); + nlm_socket = NULL; + return (error); + } + opt.sopt_dir = SOPT_SET; + opt.sopt_level = IPPROTO_IPV6; + opt.sopt_name = IPV6_PORTRANGE; + portlow = IPV6_PORTRANGE_LOW; + opt.sopt_val = &portlow; + opt.sopt_valsize = sizeof(portlow); + sosetopt(nlm_socket6, &opt); #endif + } nlm_auth = authunix_create(curthread->td_ucred); @@ -1622,6 +1627,7 @@ nlm_server_main(int addr_count, char **addrs) error = EINVAL; goto out; } + nlm_is_running = 1; NLM_DEBUG(1, "NLM: local NSM state is %d\n", smstat.state); nlm_nsm_state = smstat.state; @@ -1638,6 +1644,7 @@ nlm_server_main(int addr_count, char **addrs) nfs_reclaim_p = old_nfs_reclaim; out: + nlm_is_running = 0; if (pool) svcpool_destroy(pool); @@ -1661,13 +1668,8 @@ out: * nlm_hosts to the host (which may remove it from the list * and free it). After this phase, the only entries in the * nlm_host list should be from other threads performing - * client lock requests. We arrange to defer closing the - * sockets until the last RPC client handle is released. + * client lock requests. */ - v4_used = 0; -#ifdef INET6 - v6_used = 0; -#endif mtx_lock(&nlm_global_lock); TAILQ_FOREACH(nw, &nlm_waiting_locks, nw_link) { wakeup(nw); @@ -1678,43 +1680,10 @@ out: nlm_host_release(host); mtx_lock(&nlm_global_lock); } - TAILQ_FOREACH_SAFE(host, &nlm_hosts, nh_link, nhost) { - mtx_lock(&host->nh_lock); - if (host->nh_srvrpc.nr_client - || host->nh_clntrpc.nr_client) { - if (host->nh_addr.ss_family == AF_INET) - v4_used++; -#ifdef INET6 - if (host->nh_addr.ss_family == AF_INET6) - v6_used++; -#endif - /* - * Note that the rpc over udp code copes - * correctly with the fact that a socket may - * be used by many rpc handles. - */ - if (host->nh_srvrpc.nr_client) - CLNT_CONTROL(host->nh_srvrpc.nr_client, - CLSET_FD_CLOSE, 0); - if (host->nh_clntrpc.nr_client) - CLNT_CONTROL(host->nh_clntrpc.nr_client, - CLSET_FD_CLOSE, 0); - } - mtx_unlock(&host->nh_lock); - } mtx_unlock(&nlm_global_lock); AUTH_DESTROY(nlm_auth); - if (!v4_used) - soclose(nlm_socket); - nlm_socket = NULL; -#ifdef INET6 - if (!v6_used) - soclose(nlm_socket6); - nlm_socket6 = NULL; -#endif - return (error); } @@ -2435,7 +2404,15 @@ static int nfslockd_modevent(module_t mod, int type, void *data) { - return (0); + switch (type) { + case MOD_LOAD: + return (0); + case MOD_UNLOAD: + /* The NLM module cannot be safely unloaded. */ + /* FALLTHROUGH */ + default: + return (EOPNOTSUPP); + } } static moduledata_t nfslockd_mod = { "nfslockd", diff --git a/sys/ofed/drivers/infiniband/ulp/ipoib/ipoib_main.c b/sys/ofed/drivers/infiniband/ulp/ipoib/ipoib_main.c index ba3447faf37..9081e135042 100644 --- a/sys/ofed/drivers/infiniband/ulp/ipoib/ipoib_main.c +++ b/sys/ofed/drivers/infiniband/ulp/ipoib/ipoib_main.c @@ -1073,6 +1073,8 @@ ipoib_remove_one(struct ib_device *device) if (rdma_port_get_link_layer(device, priv->port) != IB_LINK_LAYER_INFINIBAND) continue; + ipoib_stop(priv); + ib_unregister_event_handler(&priv->event_handler); /* dev_change_flags(priv->dev, priv->dev->flags & ~IFF_UP); */ @@ -1442,7 +1444,7 @@ ipoib_input(struct ifnet *ifp, struct mbuf *m) * Strip off Infiniband header. */ m->m_flags &= ~M_VLANTAG; - m->m_flags &= ~(M_PROTOFLAGS); + m_clrprotoflags(m); m_adj(m, IPOIB_HEADER_LEN); if (IPOIB_IS_MULTICAST(eh->hwaddr)) { diff --git a/sys/ofed/drivers/net/mlx4/en_frag.c b/sys/ofed/drivers/net/mlx4/en_frag.c index 57c6368fd09..c8429e4b12d 100644 --- a/sys/ofed/drivers/net/mlx4/en_frag.c +++ b/sys/ofed/drivers/net/mlx4/en_frag.c @@ -87,7 +87,7 @@ static void flush_session(struct mlx4_en_priv *priv, u16 more) { struct mbuf *mb = session->fragments; - struct ip *iph = mb->m_pkthdr.header; + struct ip *iph = mb->m_pkthdr.PH_loc.ptr; struct net_device *dev = mb->m_pkthdr.rcvif; /* Update IP length and checksum */ @@ -132,7 +132,7 @@ int mlx4_en_rx_frags(struct mlx4_en_priv *priv, struct mlx4_en_rx_ring *ring, u16 offset; iph = (struct ip *)(mtod(mb, char *) + ETHER_HDR_LEN); - mb->m_pkthdr.header = iph; + mb->m_pkthdr.PH_loc.ptr = iph; ip_len = ntohs(iph->ip_len); ip_hlen = iph->ip_hl * 4; data_len = ip_len - ip_hlen; diff --git a/sys/ofed/include/linux/bitops.h b/sys/ofed/include/linux/bitops.h index 4305a3a7636..658c32e4674 100644 --- a/sys/ofed/include/linux/bitops.h +++ b/sys/ofed/include/linux/bitops.h @@ -272,22 +272,25 @@ bitmap_empty(unsigned long *addr, int size) return (1); } -#define NBINT (NBBY * sizeof(int)) +#define NBLONG (NBBY * sizeof(long)) #define set_bit(i, a) \ - atomic_set_int(&((volatile int *)(a))[(i)/NBINT], 1 << (i) % NBINT) + atomic_set_long(&((volatile long *)(a))[(i)/NBLONG], 1 << (i) % NBLONG) #define clear_bit(i, a) \ - atomic_clear_int(&((volatile int *)(a))[(i)/NBINT], 1 << (i) % NBINT) + atomic_clear_long(&((volatile long *)(a))[(i)/NBLONG], 1 << (i) % NBLONG) #define test_bit(i, a) \ - !!(atomic_load_acq_int(&((volatile int *)(a))[(i)/NBINT]) & 1 << ((i) % NBINT)) + !!(atomic_load_acq_long(&((volatile long *)(a))[(i)/NBLONG]) & \ + 1 << ((i) % NBLONG)) static inline long test_and_clear_bit(long bit, long *var) { long val; + var += bit / (sizeof(long) * NBBY); + bit %= sizeof(long) * NBBY; bit = 1 << bit; do { val = *(volatile long *)var; @@ -301,6 +304,8 @@ test_and_set_bit(long bit, long *var) { long val; + var += bit / (sizeof(long) * NBBY); + bit %= sizeof(long) * NBBY; bit = 1 << bit; do { val = *(volatile long *)var; diff --git a/sys/ofed/include/linux/dma-mapping.h b/sys/ofed/include/linux/dma-mapping.h index c6535243214..0f0ad9d279a 100644 --- a/sys/ofed/include/linux/dma-mapping.h +++ b/sys/ofed/include/linux/dma-mapping.h @@ -130,7 +130,7 @@ dma_alloc_coherent(struct device *dev, size_t size, dma_addr_t *dma_handle, else high = BUS_SPACE_MAXADDR_32BIT; align = PAGE_SIZE << get_order(size); - mem = (void *)kmem_alloc_contig(kmem_map, size, flag, 0, high, align, + mem = (void *)kmem_alloc_contig(kmem_arena, size, flag, 0, high, align, 0, VM_MEMATTR_DEFAULT); if (mem) *dma_handle = vtophys(mem); @@ -144,7 +144,7 @@ dma_free_coherent(struct device *dev, size_t size, void *cpu_addr, dma_addr_t dma_handle) { - kmem_free(kmem_map, (vm_offset_t)cpu_addr, size); + kmem_free(kmem_arena, (vm_offset_t)cpu_addr, size); } /* XXX This only works with no iommu. */ diff --git a/sys/ofed/include/linux/file.h b/sys/ofed/include/linux/file.h index b9bd8b15429..bb9d58d50f8 100644 --- a/sys/ofed/include/linux/file.h +++ b/sys/ofed/include/linux/file.h @@ -47,8 +47,10 @@ linux_fget(unsigned int fd) { struct file *file; - if (fget_unlocked(curthread->td_proc->p_fd, fd, 0, 0, &file, NULL) != 0) + if (fget_unlocked(curthread->td_proc->p_fd, fd, NULL, 0, &file, + NULL) != 0) { return (NULL); + } return (struct linux_file *)file->f_data; } @@ -70,8 +72,10 @@ put_unused_fd(unsigned int fd) { struct file *file; - if (fget_unlocked(curthread->td_proc->p_fd, fd, 0, 0, &file, NULL) != 0) + if (fget_unlocked(curthread->td_proc->p_fd, fd, NULL, 0, &file, + NULL) != 0) { return; + } fdclose(curthread->td_proc->p_fd, file, fd, curthread); } @@ -80,8 +84,10 @@ fd_install(unsigned int fd, struct linux_file *filp) { struct file *file; - if (fget_unlocked(curthread->td_proc->p_fd, fd, 0, 0, &file, NULL) != 0) + if (fget_unlocked(curthread->td_proc->p_fd, fd, NULL, 0, &file, + NULL) != 0) { file = NULL; + } filp->_file = file; finit(file, filp->f_mode, DTYPE_DEV, filp, &linuxfileops); } diff --git a/sys/ofed/include/linux/gfp.h b/sys/ofed/include/linux/gfp.h index 8c36c15d886..8d2b228c6a9 100644 --- a/sys/ofed/include/linux/gfp.h +++ b/sys/ofed/include/linux/gfp.h @@ -65,7 +65,7 @@ static inline unsigned long _get_page(gfp_t mask) { - return kmem_malloc(kmem_map, PAGE_SIZE, mask); + return kmem_malloc(kmem_arena, PAGE_SIZE, mask); } #define get_zeroed_page(mask) _get_page((mask) | M_ZERO) @@ -78,7 +78,7 @@ free_page(unsigned long page) if (page == 0) return; - kmem_free(kmem_map, page, PAGE_SIZE); + kmem_free(kmem_arena, page, PAGE_SIZE); } static inline void @@ -88,7 +88,7 @@ __free_page(struct page *m) if (m->object != kmem_object) panic("__free_page: Freed page %p not allocated via wrappers.", m); - kmem_free(kmem_map, (vm_offset_t)page_address(m), PAGE_SIZE); + kmem_free(kmem_arena, (vm_offset_t)page_address(m), PAGE_SIZE); } static inline void @@ -99,7 +99,7 @@ __free_pages(void *p, unsigned int order) if (p == 0) return; size = PAGE_SIZE << order; - kmem_free(kmem_map, (vm_offset_t)p, size); + kmem_free(kmem_arena, (vm_offset_t)p, size); } /* @@ -114,7 +114,7 @@ alloc_pages(gfp_t gfp_mask, unsigned int order) size_t size; size = PAGE_SIZE << order; - page = kmem_alloc_contig(kmem_map, size, gfp_mask, 0, -1, + page = kmem_alloc_contig(kmem_arena, size, gfp_mask, 0, -1, size, 0, VM_MEMATTR_DEFAULT); if (page == 0) return (NULL); diff --git a/sys/ofed/include/linux/linux_compat.c b/sys/ofed/include/linux/linux_compat.c index 7167b1c583e..4dbdad92665 100644 --- a/sys/ofed/include/linux/linux_compat.c +++ b/sys/ofed/include/linux/linux_compat.c @@ -565,6 +565,7 @@ struct fileops linuxfileops = { .fo_ioctl = linux_file_ioctl, .fo_chmod = invfo_chmod, .fo_chown = invfo_chown, + .fo_sendfile = invfo_sendfile, }; /* @@ -647,7 +648,7 @@ vmap(struct page **pages, unsigned int count, unsigned long flags, int prot) size_t size; size = count * PAGE_SIZE; - off = kmem_alloc_nofault(kernel_map, size); + off = kva_alloc(size); if (off == 0) return (NULL); vmmap_add((void *)off, size); @@ -665,7 +666,7 @@ vunmap(void *addr) if (vmmap == NULL) return; pmap_qremove((vm_offset_t)addr, vmmap->vm_size / PAGE_SIZE); - kmem_free(kernel_map, (vm_offset_t)addr, vmmap->vm_size); + kva_free((vm_offset_t)addr, vmmap->vm_size); kfree(vmmap); } diff --git a/sys/ofed/include/linux/net.h b/sys/ofed/include/linux/net.h index f47acf938fe..f84dee20919 100644 --- a/sys/ofed/include/linux/net.h +++ b/sys/ofed/include/linux/net.h @@ -44,7 +44,7 @@ static inline int sock_getname(struct socket *so, struct sockaddr *addr, int *sockaddr_len, int peer) { - struct sockaddr **nam; + struct sockaddr *nam; int error; nam = NULL; @@ -52,15 +52,15 @@ sock_getname(struct socket *so, struct sockaddr *addr, int *sockaddr_len, if ((so->so_state & (SS_ISCONNECTED|SS_ISCONFIRMING)) == 0) return (-ENOTCONN); - error = (*so->so_proto->pr_usrreqs->pru_peeraddr)(so, nam); + error = (*so->so_proto->pr_usrreqs->pru_peeraddr)(so, &nam); } else - error = (*so->so_proto->pr_usrreqs->pru_sockaddr)(so, nam); + error = (*so->so_proto->pr_usrreqs->pru_sockaddr)(so, &nam); if (error) return (-error); - *addr = **nam; + *addr = *nam; *sockaddr_len = addr->sa_len; - free(*nam, M_SONAME); + free(nam, M_SONAME); return (0); } diff --git a/sys/ofed/include/linux/page.h b/sys/ofed/include/linux/page.h index 9e152014cee..748014cc9d8 100644 --- a/sys/ofed/include/linux/page.h +++ b/sys/ofed/include/linux/page.h @@ -32,6 +32,7 @@ #include +#include #include #include diff --git a/sys/ofed/include/linux/sysfs.h b/sys/ofed/include/linux/sysfs.h index 4a763c8287d..c60a2b904e6 100644 --- a/sys/ofed/include/linux/sysfs.h +++ b/sys/ofed/include/linux/sysfs.h @@ -97,11 +97,14 @@ sysctl_handle_attr(SYSCTL_HANDLER_ARGS) error = -len; if (error != EIO) goto out; + buf[0] = '\0'; + } else if (len) { + len--; + if (len >= PAGE_SIZE) + len = PAGE_SIZE - 1; + /* Trim trailing newline. */ + buf[len] = '\0'; } - - /* Trim trailing newline. */ - len--; - buf[len] = '\0'; } /* Leave one trailing byte to append a newline. */ diff --git a/sys/ofed/include/rdma/sdp_socket.h b/sys/ofed/include/rdma/sdp_socket.h index 902dc974434..6b075892968 100644 --- a/sys/ofed/include/rdma/sdp_socket.h +++ b/sys/ofed/include/rdma/sdp_socket.h @@ -3,10 +3,12 @@ #ifndef SDP_SOCKET_H #define SDP_SOCKET_H +#ifndef __FreeBSD__ #ifndef AF_INET_SDP #define AF_INET_SDP 27 #define PF_INET_SDP AF_INET_SDP #endif +#endif #ifndef SDP_ZCOPY_THRESH #define SDP_ZCOPY_THRESH 80 diff --git a/sys/opencrypto/cryptodev.c b/sys/opencrypto/cryptodev.c index 2bc0e1f74d9..44bfa5c650d 100644 --- a/sys/opencrypto/cryptodev.c +++ b/sys/opencrypto/cryptodev.c @@ -304,6 +304,7 @@ static struct fileops cryptofops = { .fo_close = cryptof_close, .fo_chmod = invfo_chmod, .fo_chown = invfo_chown, + .fo_sendfile = invfo_sendfile, }; static struct csession *csefind(struct fcrypt *, u_int); diff --git a/sys/pc98/conf/GENERIC b/sys/pc98/conf/GENERIC index 7386c1f737d..e5f5e125d4c 100644 --- a/sys/pc98/conf/GENERIC +++ b/sys/pc98/conf/GENERIC @@ -65,6 +65,7 @@ options HWPMC_HOOKS # Necessary kernel hooks for hwpmc(4) options AUDIT # Security event auditing options CAPABILITY_MODE # Capsicum capability mode options CAPABILITIES # Capsicum capabilities +options PROCDESC # Support for process descriptors options MAC # TrustedBSD MAC Framework options INCLUDE_CONFIG_FILE # Include this file in kernel options KDB # Kernel debugger related code diff --git a/sys/pc98/pc98/machdep.c b/sys/pc98/pc98/machdep.c index 7a6c951935a..443518697cb 100644 --- a/sys/pc98/pc98/machdep.c +++ b/sys/pc98/pc98/machdep.c @@ -1359,21 +1359,6 @@ idle_sysctl(SYSCTL_HANDLER_ARGS) SYSCTL_PROC(_machdep, OID_AUTO, idle, CTLTYPE_STRING | CTLFLAG_RW, 0, 0, idle_sysctl, "A", "currently selected idle function"); -uint64_t (*atomic_load_acq_64)(volatile uint64_t *) = - atomic_load_acq_64_i386; -void (*atomic_store_rel_64)(volatile uint64_t *, uint64_t) = - atomic_store_rel_64_i386; - -static void -cpu_probe_cmpxchg8b(void) -{ - - if ((cpu_feature & CPUID_CX8) != 0) { - atomic_load_acq_64 = atomic_load_acq_64_i586; - atomic_store_rel_64 = atomic_store_rel_64_i586; - } -} - /* * Reset registers to default values on exec. */ @@ -2422,8 +2407,6 @@ init386(first) thread0.td_pcb->pcb_cr3 = (int)IdlePTD; thread0.td_pcb->pcb_ext = 0; thread0.td_frame = &proc0_tf; - - cpu_probe_cmpxchg8b(); } void @@ -2479,7 +2462,7 @@ f00f_hack(void *unused) printf("Intel Pentium detected, installing workaround for F00F bug\n"); - tmp = kmem_alloc(kernel_map, PAGE_SIZE * 2); + tmp = kmem_malloc(kernel_arena, PAGE_SIZE * 2, M_WAITOK | M_ZERO); if (tmp == 0) panic("kmem_alloc returned 0"); @@ -2490,9 +2473,7 @@ f00f_hack(void *unused) r_idt.rd_base = (u_int)new_idt; lidt(&r_idt); idt = new_idt; - if (vm_map_protect(kernel_map, tmp, tmp + PAGE_SIZE, - VM_PROT_READ, FALSE) != KERN_SUCCESS) - panic("vm_map_protect failed"); + pmap_protect(kernel_pmap, tmp, tmp + PAGE_SIZE, VM_PROT_READ); } #endif /* defined(I586_CPU) && !NO_F00F_HACK */ diff --git a/sys/pci/ncr.c b/sys/pci/ncr.c index 2dfba6128c3..d13769cc631 100644 --- a/sys/pci/ncr.c +++ b/sys/pci/ncr.c @@ -3622,8 +3622,8 @@ ncr_attach (device_t dev) pci_write_config(dev, PCIR_CACHELNSZ, cachelnsz, 1); } - if (!(command & (1<<4))) { - command |= (1<<4); + if (!(command & PCIM_CMD_MWRICEN)) { + command |= PCIM_CMD_MWRICEN; printf("%s: setting PCI command write and invalidate.\n", ncr_name(np)); pci_write_config(dev, PCIR_COMMAND, command, 2); diff --git a/sys/powerpc/aim/mmu_oea.c b/sys/powerpc/aim/mmu_oea.c index 538642df4a3..f4e9d9bbbe8 100644 --- a/sys/powerpc/aim/mmu_oea.c +++ b/sys/powerpc/aim/mmu_oea.c @@ -1158,7 +1158,7 @@ moea_enter_locked(pmap_t pmap, vm_offset_t va, vm_page_t m, vm_prot_t prot, if (pmap_bootstrapped) rw_assert(&pvh_global_lock, RA_WLOCKED); PMAP_LOCK_ASSERT(pmap, MA_OWNED); - if ((m->oflags & (VPO_UNMANAGED | VPO_BUSY)) == 0) + if ((m->oflags & VPO_UNMANAGED) == 0 && !vm_page_xbusied(m)) VM_OBJECT_ASSERT_LOCKED(m->object); /* XXX change the pvo head for fake pages */ @@ -1326,13 +1326,12 @@ moea_is_modified(mmu_t mmu, vm_page_t m) ("moea_is_modified: page %p is not managed", m)); /* - * If the page is not VPO_BUSY, then PGA_WRITEABLE cannot be + * If the page is not exclusive busied, then PGA_WRITEABLE cannot be * concurrently set while the object is locked. Thus, if PGA_WRITEABLE * is clear, no PTEs can have PTE_CHG set. */ VM_OBJECT_ASSERT_WLOCKED(m->object); - if ((m->oflags & VPO_BUSY) == 0 && - (m->aflags & PGA_WRITEABLE) == 0) + if (!vm_page_xbusied(m) && (m->aflags & PGA_WRITEABLE) == 0) return (FALSE); rw_wlock(&pvh_global_lock); rv = moea_query_bit(m, PTE_CHG); @@ -1371,13 +1370,13 @@ moea_clear_modify(mmu_t mmu, vm_page_t m) KASSERT((m->oflags & VPO_UNMANAGED) == 0, ("moea_clear_modify: page %p is not managed", m)); VM_OBJECT_ASSERT_WLOCKED(m->object); - KASSERT((m->oflags & VPO_BUSY) == 0, - ("moea_clear_modify: page %p is busy", m)); + KASSERT(!vm_page_xbusied(m), + ("moea_clear_modify: page %p is exclusive busy", m)); /* * If the page is not PGA_WRITEABLE, then no PTEs can have PTE_CHG * set. If the object containing the page is locked and the page is - * not VPO_BUSY, then PGA_WRITEABLE cannot be concurrently set. + * not exclusive busied, then PGA_WRITEABLE cannot be concurrently set. */ if ((m->aflags & PGA_WRITEABLE) == 0) return; @@ -1401,13 +1400,12 @@ moea_remove_write(mmu_t mmu, vm_page_t m) ("moea_remove_write: page %p is not managed", m)); /* - * If the page is not VPO_BUSY, then PGA_WRITEABLE cannot be set by - * another thread while the object is locked. Thus, if PGA_WRITEABLE - * is clear, no page table entries need updating. + * If the page is not exclusive busied, then PGA_WRITEABLE cannot be + * set by another thread while the object is locked. Thus, + * if PGA_WRITEABLE is clear, no page table entries need updating. */ VM_OBJECT_ASSERT_WLOCKED(m->object); - if ((m->oflags & VPO_BUSY) == 0 && - (m->aflags & PGA_WRITEABLE) == 0) + if (!vm_page_xbusied(m) && (m->aflags & PGA_WRITEABLE) == 0) return; rw_wlock(&pvh_global_lock); lo = moea_attr_fetch(m); @@ -1657,7 +1655,6 @@ moea_pinit(mmu_t mmu, pmap_t pmap) u_int entropy; KASSERT((int)pmap < VM_MIN_KERNEL_ADDRESS, ("moea_pinit: virt pmap")); - PMAP_LOCK_INIT(pmap); RB_INIT(&pmap->pmap_pvo); entropy = 0; @@ -1721,6 +1718,7 @@ void moea_pinit0(mmu_t mmu, pmap_t pm) { + PMAP_LOCK_INIT(pm); moea_pinit(mmu, pm); bzero(&pm->pm_stats, sizeof(pm->pm_stats)); } @@ -1826,7 +1824,6 @@ moea_release(mmu_t mmu, pmap_t pmap) idx /= VSID_NBPW; moea_vsid_bitmap[idx] &= ~mask; mtx_unlock(&moea_vsid_mutex); - PMAP_LOCK_DESTROY(pmap); } /* @@ -2591,7 +2588,7 @@ moea_mapdev_attr(mmu_t mmu, vm_offset_t pa, vm_size_t size, vm_memattr_t ma) return ((void *) pa); } - va = kmem_alloc_nofault(kernel_map, size); + va = kva_alloc(size); if (!va) panic("moea_mapdev: Couldn't alloc kernel virtual memory"); @@ -2619,7 +2616,7 @@ moea_unmapdev(mmu_t mmu, vm_offset_t va, vm_size_t size) base = trunc_page(va); offset = va & PAGE_MASK; size = roundup(offset + size, PAGE_SIZE); - kmem_free(kernel_map, base, size); + kva_free(base, size); } } diff --git a/sys/powerpc/aim/mmu_oea64.c b/sys/powerpc/aim/mmu_oea64.c index 5f3e4e0017d..3913b4778ac 100644 --- a/sys/powerpc/aim/mmu_oea64.c +++ b/sys/powerpc/aim/mmu_oea64.c @@ -263,7 +263,7 @@ uintptr_t moea64_scratchpage_pte[2]; struct mtx moea64_scratchpage_mtx; uint64_t moea64_large_page_mask = 0; -int moea64_large_page_size = 0; +uint64_t moea64_large_page_size = 0; int moea64_large_page_shift = 0; /* @@ -546,12 +546,9 @@ moea64_probe_large_page(void) powerpc_sync(); isync(); /* FALLTHROUGH */ - case IBMCELLBE: + default: moea64_large_page_size = 0x1000000; /* 16 MB */ moea64_large_page_shift = 24; - break; - default: - moea64_large_page_size = 0; } moea64_large_page_mask = moea64_large_page_size - 1; @@ -1260,7 +1257,7 @@ moea64_enter(mmu_t mmu, pmap_t pmap, vm_offset_t va, vm_page_t m, pvo_flags = PVO_MANAGED; } - if ((m->oflags & (VPO_UNMANAGED | VPO_BUSY)) == 0) + if ((m->oflags & VPO_UNMANAGED) == 0 && !vm_page_xbusied(m)) VM_OBJECT_ASSERT_LOCKED(m->object); /* XXX change the pvo head for fake pages */ @@ -1522,13 +1519,12 @@ moea64_is_modified(mmu_t mmu, vm_page_t m) ("moea64_is_modified: page %p is not managed", m)); /* - * If the page is not VPO_BUSY, then PGA_WRITEABLE cannot be + * If the page is not exclusive busied, then PGA_WRITEABLE cannot be * concurrently set while the object is locked. Thus, if PGA_WRITEABLE * is clear, no PTEs can have LPTE_CHG set. */ - VM_OBJECT_ASSERT_WLOCKED(m->object); - if ((m->oflags & VPO_BUSY) == 0 && - (m->aflags & PGA_WRITEABLE) == 0) + VM_OBJECT_ASSERT_LOCKED(m->object); + if (!vm_page_xbusied(m) && (m->aflags & PGA_WRITEABLE) == 0) return (FALSE); return (moea64_query_bit(mmu, m, LPTE_CHG)); } @@ -1562,13 +1558,13 @@ moea64_clear_modify(mmu_t mmu, vm_page_t m) KASSERT((m->oflags & VPO_UNMANAGED) == 0, ("moea64_clear_modify: page %p is not managed", m)); VM_OBJECT_ASSERT_WLOCKED(m->object); - KASSERT((m->oflags & VPO_BUSY) == 0, - ("moea64_clear_modify: page %p is busy", m)); + KASSERT(!vm_page_xbusied(m), + ("moea64_clear_modify: page %p is exclusive busied", m)); /* * If the page is not PGA_WRITEABLE, then no PTEs can have LPTE_CHG * set. If the object containing the page is locked and the page is - * not VPO_BUSY, then PGA_WRITEABLE cannot be concurrently set. + * not exclusive busied, then PGA_WRITEABLE cannot be concurrently set. */ if ((m->aflags & PGA_WRITEABLE) == 0) return; @@ -1590,13 +1586,12 @@ moea64_remove_write(mmu_t mmu, vm_page_t m) ("moea64_remove_write: page %p is not managed", m)); /* - * If the page is not VPO_BUSY, then PGA_WRITEABLE cannot be set by - * another thread while the object is locked. Thus, if PGA_WRITEABLE - * is clear, no page table entries need updating. + * If the page is not exclusive busied, then PGA_WRITEABLE cannot be + * set by another thread while the object is locked. Thus, + * if PGA_WRITEABLE is clear, no page table entries need updating. */ VM_OBJECT_ASSERT_WLOCKED(m->object); - if ((m->oflags & VPO_BUSY) == 0 && - (m->aflags & PGA_WRITEABLE) == 0) + if (!vm_page_xbusied(m) && (m->aflags & PGA_WRITEABLE) == 0) return; powerpc_sync(); LOCK_TABLE_RD(); @@ -1881,7 +1876,7 @@ moea64_get_unique_vsid(void) { void moea64_pinit(mmu_t mmu, pmap_t pmap) { - PMAP_LOCK_INIT(pmap); + RB_INIT(&pmap->pmap_pvo); pmap->pm_slb_tree_root = slb_alloc_tree(); @@ -1895,7 +1890,6 @@ moea64_pinit(mmu_t mmu, pmap_t pmap) int i; uint32_t hash; - PMAP_LOCK_INIT(pmap); RB_INIT(&pmap->pmap_pvo); if (pmap_bootstrapped) @@ -1922,6 +1916,8 @@ moea64_pinit(mmu_t mmu, pmap_t pmap) void moea64_pinit0(mmu_t mmu, pmap_t pm) { + + PMAP_LOCK_INIT(pm); moea64_pinit(mmu, pm); bzero(&pm->pm_stats, sizeof(pm->pm_stats)); } @@ -2076,8 +2072,6 @@ moea64_release(mmu_t mmu, pmap_t pmap) moea64_release_vsid(VSID_TO_HASH(pmap->pm_sr[0])); #endif - - PMAP_LOCK_DESTROY(pmap); } /* @@ -2544,7 +2538,7 @@ moea64_mapdev_attr(mmu_t mmu, vm_offset_t pa, vm_size_t size, vm_memattr_t ma) offset = pa & PAGE_MASK; size = roundup2(offset + size, PAGE_SIZE); - va = kmem_alloc_nofault(kernel_map, size); + va = kva_alloc(size); if (!va) panic("moea64_mapdev: Couldn't alloc kernel virtual memory"); @@ -2575,7 +2569,7 @@ moea64_unmapdev(mmu_t mmu, vm_offset_t va, vm_size_t size) offset = va & PAGE_MASK; size = roundup2(offset + size, PAGE_SIZE); - kmem_free(kernel_map, base, size); + kva_free(base, size); } void diff --git a/sys/powerpc/aim/mmu_oea64.h b/sys/powerpc/aim/mmu_oea64.h index 101181da5ba..111d04d6c20 100644 --- a/sys/powerpc/aim/mmu_oea64.h +++ b/sys/powerpc/aim/mmu_oea64.h @@ -70,6 +70,7 @@ extern u_int moea64_pte_overflow; extern struct pvo_head *moea64_pvo_table; extern int moea64_large_page_shift; +extern uint64_t moea64_large_page_size; extern u_int moea64_pteg_count; extern u_int moea64_pteg_mask; diff --git a/sys/powerpc/aim/nexus.c b/sys/powerpc/aim/nexus.c index 9a6bf470b8e..109ec52ae6f 100644 --- a/sys/powerpc/aim/nexus.c +++ b/sys/powerpc/aim/nexus.c @@ -195,7 +195,8 @@ static driver_t nexus_driver = { static devclass_t nexus_devclass; -DRIVER_MODULE(nexus, root, nexus_driver, nexus_devclass, 0, 0); +EARLY_DRIVER_MODULE(nexus, root, nexus_driver, nexus_devclass, 0, 0, + BUS_PASS_BUS); static int nexus_probe(device_t dev) @@ -216,7 +217,7 @@ nexus_attach(device_t dev) sc = device_get_softc(dev); start = 0; - end = MAX_PICS*INTR_VECTORS - 1; + end = ~0; sc->sc_rman.rm_start = start; sc->sc_rman.rm_end = end; diff --git a/sys/powerpc/aim/trap.c b/sys/powerpc/aim/trap.c index 97f4ca1ceb2..2edbbfa7e3f 100644 --- a/sys/powerpc/aim/trap.c +++ b/sys/powerpc/aim/trap.c @@ -197,13 +197,11 @@ trap(struct trapframe *frame) #ifdef HWPMC_HOOKS if (type == EXC_PERF && (pmc_intr != NULL)) { -#ifdef notyet - (*pmc_intr)(PCPU_GET(cpuid), frame); - if (!user) + (*pmc_intr)(PCPU_GET(cpuid), frame); + if (user) + userret(td, frame); return; -#endif } - else #endif #ifdef KDTRACE_HOOKS /* @@ -316,9 +314,11 @@ trap(struct trapframe *frame) if (*(uintptr_t *)frame->srr0 == 0x7c810808) { if (dtrace_invop_jump_addr != NULL) { dtrace_invop_jump_addr(frame); + return; } } } + break; #endif #ifdef __powerpc64__ case EXC_DSE: diff --git a/sys/powerpc/aim/vm_machdep.c b/sys/powerpc/aim/vm_machdep.c index 785f22ae9ae..1790ce38d75 100644 --- a/sys/powerpc/aim/vm_machdep.c +++ b/sys/powerpc/aim/vm_machdep.c @@ -187,6 +187,7 @@ cpu_fork(struct thread *td1, struct proc *p2, struct thread *td2, int flags) cf->cf_arg1 = (register_t)tf; pcb->pcb_sp = (register_t)cf; + KASSERT(pcb->pcb_sp % 16 == 0, ("stack misaligned")); #ifdef __powerpc64__ pcb->pcb_lr = ((register_t *)fork_trampoline)[0]; pcb->pcb_toc = ((register_t *)fork_trampoline)[1]; @@ -253,7 +254,7 @@ sf_buf_init(void *arg) sf_buf_active = hashinit(nsfbufs, M_TEMP, &sf_buf_hashmask); TAILQ_INIT(&sf_buf_freelist); - sf_base = kmem_alloc_nofault(kernel_map, nsfbufs * PAGE_SIZE); + sf_base = kva_alloc(nsfbufs * PAGE_SIZE); sf_bufs = malloc(nsfbufs * sizeof(struct sf_buf), M_TEMP, M_NOWAIT | M_ZERO); for (i = 0; i < nsfbufs; i++) { diff --git a/sys/powerpc/booke/pmap.c b/sys/powerpc/booke/pmap.c index 7e3c29ee9b7..d5c64ed3290 100644 --- a/sys/powerpc/booke/pmap.c +++ b/sys/powerpc/booke/pmap.c @@ -1477,6 +1477,7 @@ static void mmu_booke_pinit0(mmu_t mmu, pmap_t pmap) { + PMAP_LOCK_INIT(pmap); mmu_booke_pinit(mmu, pmap); PCPU_SET(curpmap, pmap); } @@ -1495,7 +1496,6 @@ mmu_booke_pinit(mmu_t mmu, pmap_t pmap) KASSERT((pmap != kernel_pmap), ("pmap_pinit: initializing kernel_pmap")); - PMAP_LOCK_INIT(pmap); for (i = 0; i < MAXCPU; i++) pmap->pm_tid[i] = TID_NONE; CPU_ZERO(&kernel_pmap->pm_active); @@ -1516,8 +1516,6 @@ mmu_booke_release(mmu_t mmu, pmap_t pmap) KASSERT(pmap->pm_stats.resident_count == 0, ("pmap_release: pmap resident count %ld != 0", pmap->pm_stats.resident_count)); - - PMAP_LOCK_DESTROY(pmap); } /* @@ -1563,7 +1561,7 @@ mmu_booke_enter_locked(mmu_t mmu, pmap_t pmap, vm_offset_t va, vm_page_t m, KASSERT((va <= VM_MAXUSER_ADDRESS), ("mmu_booke_enter_locked: user pmap, non user va")); } - if ((m->oflags & (VPO_UNMANAGED | VPO_BUSY)) == 0) + if ((m->oflags & VPO_UNMANAGED) == 0 && !vm_page_xbusied(m)) VM_OBJECT_ASSERT_LOCKED(m->object); PMAP_LOCK_ASSERT(pmap, MA_OWNED); @@ -1959,13 +1957,12 @@ mmu_booke_remove_write(mmu_t mmu, vm_page_t m) ("mmu_booke_remove_write: page %p is not managed", m)); /* - * If the page is not VPO_BUSY, then PGA_WRITEABLE cannot be set by - * another thread while the object is locked. Thus, if PGA_WRITEABLE - * is clear, no page table entries need updating. + * If the page is not exclusive busied, then PGA_WRITEABLE cannot be + * set by another thread while the object is locked. Thus, + * if PGA_WRITEABLE is clear, no page table entries need updating. */ VM_OBJECT_ASSERT_WLOCKED(m->object); - if ((m->oflags & VPO_BUSY) == 0 && - (m->aflags & PGA_WRITEABLE) == 0) + if (!vm_page_xbusied(m) && (m->aflags & PGA_WRITEABLE) == 0) return; rw_wlock(&pvh_global_lock); TAILQ_FOREACH(pv, &m->md.pv_list, pv_link) { @@ -2204,13 +2201,12 @@ mmu_booke_is_modified(mmu_t mmu, vm_page_t m) rv = FALSE; /* - * If the page is not VPO_BUSY, then PGA_WRITEABLE cannot be + * If the page is not exclusive busied, then PGA_WRITEABLE cannot be * concurrently set while the object is locked. Thus, if PGA_WRITEABLE * is clear, no PTEs can be modified. */ VM_OBJECT_ASSERT_WLOCKED(m->object); - if ((m->oflags & VPO_BUSY) == 0 && - (m->aflags & PGA_WRITEABLE) == 0) + if (!vm_page_xbusied(m) && (m->aflags & PGA_WRITEABLE) == 0) return (rv); rw_wlock(&pvh_global_lock); TAILQ_FOREACH(pv, &m->md.pv_list, pv_link) { @@ -2281,13 +2277,13 @@ mmu_booke_clear_modify(mmu_t mmu, vm_page_t m) KASSERT((m->oflags & VPO_UNMANAGED) == 0, ("mmu_booke_clear_modify: page %p is not managed", m)); VM_OBJECT_ASSERT_WLOCKED(m->object); - KASSERT((m->oflags & VPO_BUSY) == 0, - ("mmu_booke_clear_modify: page %p is busy", m)); + KASSERT(!vm_page_xbusied(m), + ("mmu_booke_clear_modify: page %p is exclusive busied", m)); /* * If the page is not PG_AWRITEABLE, then no PTEs can be modified. * If the object containing the page is locked and the page is not - * VPO_BUSY, then PG_AWRITEABLE cannot be concurrently set. + * exclusive busied, then PG_AWRITEABLE cannot be concurrently set. */ if ((m->aflags & PGA_WRITEABLE) == 0) return; @@ -2681,7 +2677,7 @@ mmu_booke_unmapdev(mmu_t mmu, vm_offset_t va, vm_size_t size) base = trunc_page(va); offset = va & PAGE_MASK; size = roundup(offset + size, PAGE_SIZE); - kmem_free(kernel_map, base, size); + kva_free(base, size); } } diff --git a/sys/powerpc/booke/vm_machdep.c b/sys/powerpc/booke/vm_machdep.c index 3303794a557..f3fcf21dcb0 100644 --- a/sys/powerpc/booke/vm_machdep.c +++ b/sys/powerpc/booke/vm_machdep.c @@ -260,7 +260,7 @@ sf_buf_init(void *arg) sf_buf_active = hashinit(nsfbufs, M_TEMP, &sf_buf_hashmask); TAILQ_INIT(&sf_buf_freelist); - sf_base = kmem_alloc_nofault(kernel_map, nsfbufs * PAGE_SIZE); + sf_base = kva_alloc(nsfbufs * PAGE_SIZE); sf_bufs = malloc(nsfbufs * sizeof(struct sf_buf), M_TEMP, M_NOWAIT | M_ZERO); for (i = 0; i < nsfbufs; i++) { diff --git a/sys/powerpc/conf/DEFAULTS b/sys/powerpc/conf/DEFAULTS index 9d933b3a69e..da8e7fb6d19 100644 --- a/sys/powerpc/conf/DEFAULTS +++ b/sys/powerpc/conf/DEFAULTS @@ -9,6 +9,7 @@ device mem # Memory and kernel memory devices # UART chips on this platform device uart_ns8250 +options GEOM_PART_BSD options GEOM_PART_MBR options NEW_PCIB diff --git a/sys/powerpc/conf/GENERIC b/sys/powerpc/conf/GENERIC index d91e00da48b..59ff68f0bac 100644 --- a/sys/powerpc/conf/GENERIC +++ b/sys/powerpc/conf/GENERIC @@ -30,6 +30,7 @@ makeoptions WITH_CTF=1 options POWERMAC #NewWorld Apple PowerMacs options PSIM #GDB PSIM ppc simulator options MAMBO #IBM Mambo Full System Simulator +options PSERIES #PAPR-compliant systems options SCHED_ULE #ULE scheduler options PREEMPTION #Enable kernel thread preemption @@ -69,6 +70,7 @@ options HWPMC_HOOKS # Necessary kernel hooks for hwpmc(4) options AUDIT # Security event auditing options CAPABILITY_MODE # Capsicum capability mode options CAPABILITIES # Capsicum capabilities +options PROCDESC # Support for process descriptors options MAC # TrustedBSD MAC Framework options KDTRACE_HOOKS # Kernel DTrace hooks options DDB_CTF # Kernel ELF linker loads CTF data @@ -76,9 +78,8 @@ options INCLUDE_CONFIG_FILE # Include this file in kernel # Debugging support. Always need this: options KDB # Enable kernel debugger support. -# For minimum debugger support (stable branch) use: -#options KDB_TRACE # Print a stack trace for a panic. -# For full debugger support use this instead: +options KDB_TRACE # Print a stack trace for a panic. +# For full debugger support use (turn off in stable branch): options DDB #Support DDB #options DEADLKRES #Enable the deadlock resolver options INVARIANTS #Enable calls of extra sanity checking diff --git a/sys/powerpc/conf/GENERIC64 b/sys/powerpc/conf/GENERIC64 index 1cdf1953648..7ac3e10bfaf 100644 --- a/sys/powerpc/conf/GENERIC64 +++ b/sys/powerpc/conf/GENERIC64 @@ -24,11 +24,13 @@ ident GENERIC machine powerpc powerpc64 makeoptions DEBUG=-g #Build kernel with gdb(1) debug symbols +makeoptions WITH_CTF=1 # Platform support options POWERMAC #NewWorld Apple PowerMacs options PS3 #Sony Playstation 3 options MAMBO #IBM Mambo Full System Simulator +options PSERIES #PAPR-compliant systems (e.g. IBM p) options SCHED_ULE #ULE scheduler options PREEMPTION #Enable kernel thread preemption @@ -67,13 +69,14 @@ options _KPOSIX_PRIORITY_SCHEDULING #Posix P1003_1B real-time extensions options HWPMC_HOOKS # Necessary kernel hooks for hwpmc(4) options AUDIT # Security event auditing options MAC # TrustedBSD MAC Framework +options KDTRACE_HOOKS # Kernel DTrace hooks +options DDB_CTF # Kernel ELF linker loads CTF data options INCLUDE_CONFIG_FILE # Include this file in kernel # Debugging support. Always need this: options KDB # Enable kernel debugger support. -# For minimum debugger support (stable branch) use: -#options KDB_TRACE # Print a stack trace for a panic. -# For full debugger support use this instead: +options KDB_TRACE # Print a stack trace for a panic. +# For full debugger support use (turn off in stable branch): options DDB #Support DDB #options DEADLKRES #Enable the deadlock resolver options INVARIANTS #Enable calls of extra sanity checking @@ -129,6 +132,9 @@ device uart device uart_z8530 # Ethernet hardware +device em # Intel PRO/1000 Gigabit Ethernet Family +device igb # Intel PRO/1000 PCIE Server Gigabit Family +device ixgbe # Intel PRO/10GbE PCIE Ethernet Family device glc # Sony Playstation 3 Ethernet # PCI Ethernet NICs that use the common MII bus controller code. @@ -137,6 +143,8 @@ device bge # Broadcom BCM570xx Gigabit Ethernet device gem # Sun GEM/Sun ERI/Apple GMAC device dc # DEC/Intel 21143 and various workalikes device fxp # Intel EtherExpress PRO/100B (82557, 82558) +device re # RealTek 8139C+/8169/8169S/8110S +device rl # RealTek 8129/8139 # Pseudo devices. device loop # Network loopback diff --git a/sys/powerpc/include/_stdint.h b/sys/powerpc/include/_stdint.h index 6ad1fd257d4..9928a1a0196 100644 --- a/sys/powerpc/include/_stdint.h +++ b/sys/powerpc/include/_stdint.h @@ -65,6 +65,7 @@ #if !defined(__cplusplus) || defined(__STDC_LIMIT_MACROS) +#ifndef __INT64_C #ifdef __LP64__ #define __INT64_C(c) (c ## L) #define __UINT64_C(c) (c ## UL) @@ -72,6 +73,7 @@ #define __INT64_C(c) (c ## LL) #define __UINT64_C(c) (c ## ULL) #endif +#endif /* * ISO/IEC 9899:1999 diff --git a/sys/powerpc/include/frame.h b/sys/powerpc/include/frame.h index 196cb6fb8af..d2100a104a2 100644 --- a/sys/powerpc/include/frame.h +++ b/sys/powerpc/include/frame.h @@ -94,6 +94,7 @@ struct callframe { register_t cf_func; register_t cf_arg0; register_t cf_arg1; + register_t _padding; /* Maintain 16-byte alignment */ }; #else struct callframe { @@ -102,6 +103,7 @@ struct callframe { register_t cf_func; register_t cf_arg0; register_t cf_arg1; + register_t _padding; /* Maintain 16-byte alignment */ }; #endif diff --git a/sys/powerpc/include/param.h b/sys/powerpc/include/param.h index 25283258abd..7e7c8f153cb 100644 --- a/sys/powerpc/include/param.h +++ b/sys/powerpc/include/param.h @@ -69,7 +69,7 @@ #if defined(SMP) || defined(KLD_MODULE) #ifndef MAXCPU -#define MAXCPU 8 +#define MAXCPU 32 #endif #else #define MAXCPU 1 diff --git a/sys/powerpc/include/platform.h b/sys/powerpc/include/platform.h index 48ea0e60325..d93088c9b82 100644 --- a/sys/powerpc/include/platform.h +++ b/sys/powerpc/include/platform.h @@ -52,6 +52,7 @@ int platform_smp_first_cpu(struct cpuref *); int platform_smp_next_cpu(struct cpuref *); int platform_smp_get_bsp(struct cpuref *); int platform_smp_start_cpu(struct pcpu *); +void platform_smp_ap_init(void); const char *installed_platform(void); void platform_probe_and_attach(void); diff --git a/sys/powerpc/include/pmc_mdep.h b/sys/powerpc/include/pmc_mdep.h index 34563686610..678852b6233 100644 --- a/sys/powerpc/include/pmc_mdep.h +++ b/sys/powerpc/include/pmc_mdep.h @@ -7,7 +7,8 @@ #ifndef _MACHINE_PMC_MDEP_H_ #define _MACHINE_PMC_MDEP_H_ -#define PMC_MDEP_CLASS_INDEX_PPC7450 1 +#define PMC_MDEP_CLASS_INDEX_PPC7450 0 +#define PMC_MDEP_CLASS_INDEX_PPC970 0 union pmc_md_op_pmcallocate { uint64_t __pad[4]; diff --git a/sys/powerpc/include/pte.h b/sys/powerpc/include/pte.h index b5d2ecfdbfa..85f169c18c7 100644 --- a/sys/powerpc/include/pte.h +++ b/sys/powerpc/include/pte.h @@ -96,8 +96,9 @@ struct lpteg { #define LPTE_VSID_SHIFT 12 #define LPTE_AVPN_MASK 0xFFFFFFFFFFFFFF80ULL #define LPTE_API 0x0000000000000F80ULL -#define LPTE_LOCKED 0x0000000000000040ULL -#define LPTE_WIRED 0x0000000000000008ULL +#define LPTE_SWBITS 0x0000000000000078ULL +#define LPTE_WIRED 0x0000000000000010ULL +#define LPTE_LOCKED 0x0000000000000008ULL #define LPTE_BIG 0x0000000000000004ULL /* 4kb/16Mb page */ #define LPTE_HID 0x0000000000000002ULL #define LPTE_VALID 0x0000000000000001ULL diff --git a/sys/powerpc/include/sf_buf.h b/sys/powerpc/include/sf_buf.h index 7ddb981bdc3..f8a5936f805 100644 --- a/sys/powerpc/include/sf_buf.h +++ b/sys/powerpc/include/sf_buf.h @@ -45,6 +45,9 @@ struct sf_buf { int ref_count; /* usage of this mapping */ }; +struct sf_buf * sf_buf_alloc(struct vm_page *m, int flags); +void sf_buf_free(struct sf_buf *sf); + /* * On 32-bit OEA, the only purpose for which sf_buf is used is to implement * an opaque pointer required by the machine-independent parts of the kernel. diff --git a/sys/powerpc/include/spr.h b/sys/powerpc/include/spr.h index 601b62649e8..f99b67f0655 100644 --- a/sys/powerpc/include/spr.h +++ b/sys/powerpc/include/spr.h @@ -168,6 +168,8 @@ #define IBMPOWER3PLUS 0x0041 #define IBM970MP 0x0044 #define IBM970GX 0x0045 +#define IBMPOWER7PLUS 0x004a +#define IBMPOWER8 0x004b #define MPC860 0x0050 #define IBMCELLBE 0x0070 #define MPC8240 0x0081 diff --git a/sys/powerpc/ofw/ofw_cpu.c b/sys/powerpc/ofw/ofw_cpu.c index 6fb1126ae3c..6cc64841cf2 100644 --- a/sys/powerpc/ofw/ofw_cpu.c +++ b/sys/powerpc/ofw/ofw_cpu.c @@ -158,7 +158,7 @@ static device_method_t ofw_cpu_methods[] = { static driver_t ofw_cpu_driver = { "cpu", ofw_cpu_methods, - 0 + sizeof(struct ofw_cpu_softc) }; static devclass_t ofw_cpu_devclass; diff --git a/sys/powerpc/ofw/ofw_pcibus.c b/sys/powerpc/ofw/ofw_pcibus.c index c3dfed00b93..744b7ac7237 100644 --- a/sys/powerpc/ofw/ofw_pcibus.c +++ b/sys/powerpc/ofw/ofw_pcibus.c @@ -101,6 +101,9 @@ DRIVER_MODULE(ofw_pcibus, pcib, ofw_pcibus_driver, pci_devclass, 0, 0); MODULE_VERSION(ofw_pcibus, 1); MODULE_DEPEND(ofw_pcibus, pci, 1, 1, 1); +static int ofw_devices_only = 0; +TUNABLE_INT("hw.pci.ofw_devices_only", &ofw_devices_only); + static int ofw_pcibus_probe(device_t dev) { @@ -109,7 +112,7 @@ ofw_pcibus_probe(device_t dev) return (ENXIO); device_set_desc(dev, "OFW PCI bus"); - return (0); + return (BUS_PROBE_DEFAULT); } static int @@ -137,7 +140,8 @@ ofw_pcibus_attach(device_t dev) * functions on multi-function cards. */ - ofw_pcibus_enum_bus(dev, domain, busno); + if (!ofw_devices_only) + ofw_pcibus_enum_bus(dev, domain, busno); return (bus_generic_attach(dev)); } @@ -210,11 +214,12 @@ ofw_pcibus_enum_devtree(device_t dev, u_int domain, u_int busno) icells = 1; OF_getprop(child, "interrupt-parent", &iparent, sizeof(iparent)); - OF_getprop(iparent, "#interrupt-cells", &icells, - sizeof(icells)); - - if (iparent != 0) + if (iparent != 0) { + OF_getprop(OF_xref_phandle(iparent), + "#interrupt-cells", &icells, + sizeof(icells)); intr[0] = MAP_IRQ(iparent, intr[0]); + } if (iparent != 0 && icells > 1) { powerpc_config_intr(intr[0], diff --git a/sys/powerpc/ofw/ofw_syscons.c b/sys/powerpc/ofw/ofw_syscons.c index 84ba302f61c..1c1c4c12c2a 100644 --- a/sys/powerpc/ofw/ofw_syscons.c +++ b/sys/powerpc/ofw/ofw_syscons.c @@ -264,6 +264,10 @@ ofwfb_configure(int flags) } else return (0); + if (OF_getproplen(node, "height") != sizeof(sc->sc_height) || + OF_getproplen(node, "width") != sizeof(sc->sc_width)) + return (0); + sc->sc_depth = depth; sc->sc_node = node; sc->sc_console = 1; @@ -278,6 +282,8 @@ ofwfb_configure(int flags) * * XXX We assume #address-cells is 1 at this point. */ + if (OF_getproplen(node, "address") != sizeof(fb_phys)) + return (0); OF_getprop(node, "address", &fb_phys, sizeof(fb_phys)); bus_space_map(&bs_be_tag, fb_phys, sc->sc_height * sc->sc_stride, diff --git a/sys/powerpc/ofw/rtas.c b/sys/powerpc/ofw/rtas.c index c6d89691a5d..34b7acfa3a6 100644 --- a/sys/powerpc/ofw/rtas.c +++ b/sys/powerpc/ofw/rtas.c @@ -93,7 +93,7 @@ rtas_setup(void *junk) return; } - mtx_init(&rtas_mtx, "RTAS", MTX_DEF, 0); + mtx_init(&rtas_mtx, "RTAS", NULL, MTX_SPIN); /* RTAS must be called with everything turned off in MSR */ rtasmsr = mfmsr(); @@ -208,7 +208,7 @@ rtas_call_method(cell_t token, int nargs, int nreturns, ...) args.token = token; va_start(ap, nreturns); - mtx_lock(&rtas_mtx); + mtx_lock_spin(&rtas_mtx); rtas_bounce_offset = 0; args.nargs = nargs; @@ -232,7 +232,7 @@ rtas_call_method(cell_t token, int nargs, int nreturns, ...) __asm __volatile ("sync"); rtas_real_unmap(argsptr, &args, sizeof(args)); - mtx_unlock(&rtas_mtx); + mtx_unlock_spin(&rtas_mtx); if (result < 0) return (result); diff --git a/sys/powerpc/powermac/atibl.c b/sys/powerpc/powermac/atibl.c index fff76d082b3..f4ac9b0e6c9 100644 --- a/sys/powerpc/powermac/atibl.c +++ b/sys/powerpc/powermac/atibl.c @@ -86,6 +86,8 @@ DRIVER_MODULE(atibl, vgapci, atibl_driver, atibl_devclass, 0, 0); static void atibl_identify(driver_t *driver, device_t parent) { + if (OF_finddevice("mac-io/backlight") == -1) + return; if (device_find_child(parent, "backlight", -1) == NULL) device_add_child(parent, "backlight", -1); } diff --git a/sys/powerpc/powermac/kiic.c b/sys/powerpc/powermac/kiic.c index 0b13190aa09..7a60ba2a7c9 100644 --- a/sys/powerpc/powermac/kiic.c +++ b/sys/powerpc/powermac/kiic.c @@ -420,7 +420,7 @@ kiic_transfer(device_t dev, struct iic_msg *msgs, uint32_t nmsgs) device_printf(sc->sc_dev, "I2C error\n"); sc->sc_flags = 0; mtx_unlock(&sc->sc_mutex); - return (-1); + return (EIO); } } diff --git a/sys/powerpc/powermac/nvbl.c b/sys/powerpc/powermac/nvbl.c index 1f898815ab4..033f972652a 100644 --- a/sys/powerpc/powermac/nvbl.c +++ b/sys/powerpc/powermac/nvbl.c @@ -82,6 +82,8 @@ DRIVER_MODULE(nvbl, vgapci, nvbl_driver, nvbl_devclass, 0, 0); static void nvbl_identify(driver_t *driver, device_t parent) { + if (OF_finddevice("mac-io/backlight") == -1) + return; if (device_find_child(parent, "backlight", -1) == NULL) device_add_child(parent, "backlight", -1); } diff --git a/sys/powerpc/powermac/platform_powermac.c b/sys/powerpc/powermac/platform_powermac.c index 52d5c4a3c52..e21a48cbe18 100644 --- a/sys/powerpc/powermac/platform_powermac.c +++ b/sys/powerpc/powermac/platform_powermac.c @@ -91,8 +91,22 @@ PLATFORM_DEF(powermac_platform); static int powermac_probe(platform_t plat) { - if (OF_finddevice("/memory") != -1 || OF_finddevice("/memory@0") != -1) - return (BUS_PROBE_GENERIC); + char compat[255]; + ssize_t compatlen; + char *curstr; + phandle_t root; + + root = OF_peer(0); + if (root == 0) + return (ENXIO); + + compatlen = OF_getprop(root, "compatible", compat, sizeof(compat)); + + for (curstr = compat; curstr < compat + compatlen; + curstr += strlen(curstr) + 1) { + if (strncmp(curstr, "MacRISC", 7) == 0) + return (BUS_PROBE_SPECIFIC); + } return (ENXIO); } diff --git a/sys/powerpc/powerpc/busdma_machdep.c b/sys/powerpc/powerpc/busdma_machdep.c index 07508fba4a0..144b7f9b730 100644 --- a/sys/powerpc/powerpc/busdma_machdep.c +++ b/sys/powerpc/powerpc/busdma_machdep.c @@ -532,7 +532,7 @@ bus_dmamem_alloc(bus_dma_tag_t dmat, void** vaddr, int flags, * multi-seg allocations yet though. * XXX Certain AGP hardware does. */ - *vaddr = (void *)kmem_alloc_contig(kernel_map, dmat->maxsize, + *vaddr = (void *)kmem_alloc_contig(kmem_arena, dmat->maxsize, mflags, 0ul, dmat->lowaddr, dmat->alignment ? dmat->alignment : 1ul, dmat->boundary, attr); (*mapp)->contigalloc = 1; @@ -560,7 +560,7 @@ bus_dmamem_free(bus_dma_tag_t dmat, void *vaddr, bus_dmamap_t map) if (!map->contigalloc) free(vaddr, M_DEVBUF); else - kmem_free(kernel_map, (vm_offset_t)vaddr, dmat->maxsize); + kmem_free(kmem_arena, (vm_offset_t)vaddr, dmat->maxsize); bus_dmamap_destroy(dmat, map); CTR3(KTR_BUSDMA, "%s: tag %p flags 0x%x", __func__, dmat, dmat->flags); } @@ -844,16 +844,19 @@ _bus_dmamap_complete(bus_dma_tag_t dmat, bus_dmamap_t map, bus_dma_segment_t *segs, int nsegs, int error) { + map->nsegs = nsegs; if (segs != NULL) memcpy(map->segments, segs, map->nsegs*sizeof(segs[0])); - else - segs = map->segments; - map->nsegs = nsegs; if (dmat->iommu != NULL) IOMMU_MAP(dmat->iommu, map->segments, &map->nsegs, dmat->lowaddr, dmat->highaddr, dmat->alignment, dmat->boundary, dmat->iommu_cookie); + if (segs != NULL) + memcpy(segs, map->segments, map->nsegs*sizeof(segs[0])); + else + segs = map->segments; + return (segs); } diff --git a/sys/powerpc/powerpc/cpu.c b/sys/powerpc/powerpc/cpu.c index d67f359e41c..d7f8e2d4d05 100644 --- a/sys/powerpc/powerpc/cpu.c +++ b/sys/powerpc/powerpc/cpu.c @@ -127,6 +127,26 @@ static const struct cputab models[] = { { "IBM PowerPC 970MP", IBM970MP, REVFMT_MAJMIN, PPC_FEATURE_64 | PPC_FEATURE_HAS_ALTIVEC | PPC_FEATURE_HAS_FPU, cpu_970_setup }, + { "IBM POWER4", IBMPOWER4, REVFMT_MAJMIN, + PPC_FEATURE_64 | PPC_FEATURE_HAS_FPU, NULL }, + { "IBM POWER4+", IBMPOWER4PLUS, REVFMT_MAJMIN, + PPC_FEATURE_64 | PPC_FEATURE_HAS_FPU, NULL }, + { "IBM POWER5", IBMPOWER5, REVFMT_MAJMIN, + PPC_FEATURE_64 | PPC_FEATURE_HAS_FPU, NULL }, + { "IBM POWER5+", IBMPOWER5PLUS, REVFMT_MAJMIN, + PPC_FEATURE_64 | PPC_FEATURE_HAS_FPU, NULL }, + { "IBM POWER6", IBMPOWER6, REVFMT_MAJMIN, + PPC_FEATURE_64 | PPC_FEATURE_HAS_ALTIVEC | PPC_FEATURE_HAS_FPU, + NULL }, + { "IBM POWER7", IBMPOWER7, REVFMT_MAJMIN, + PPC_FEATURE_64 | PPC_FEATURE_HAS_ALTIVEC | PPC_FEATURE_HAS_FPU, + NULL }, + { "IBM POWER7+", IBMPOWER7PLUS, REVFMT_MAJMIN, + PPC_FEATURE_64 | PPC_FEATURE_HAS_ALTIVEC | PPC_FEATURE_HAS_FPU, + NULL }, + { "IBM POWER8", IBMPOWER8, REVFMT_MAJMIN, + PPC_FEATURE_64 | PPC_FEATURE_HAS_ALTIVEC | PPC_FEATURE_HAS_FPU, + NULL }, { "Motorola PowerPC 7400", MPC7400, REVFMT_MAJMIN, PPC_FEATURE_HAS_ALTIVEC | PPC_FEATURE_HAS_FPU, cpu_6xx_setup }, { "Motorola PowerPC 7410", MPC7410, REVFMT_MAJMIN, diff --git a/sys/powerpc/powerpc/intr_machdep.c b/sys/powerpc/powerpc/intr_machdep.c index abfe9050779..539d3f6467b 100644 --- a/sys/powerpc/powerpc/intr_machdep.c +++ b/sys/powerpc/powerpc/intr_machdep.c @@ -176,7 +176,7 @@ intrcnt_add(const char *name, u_long **countp) static struct powerpc_intr * intr_lookup(u_int irq) { - char intrname[8]; + char intrname[16]; struct powerpc_intr *i, *iscan; int vector; diff --git a/sys/powerpc/powerpc/mmu_if.m b/sys/powerpc/powerpc/mmu_if.m index 0382bd8ba0a..f9f37cb6f6e 100644 --- a/sys/powerpc/powerpc/mmu_if.m +++ b/sys/powerpc/powerpc/mmu_if.m @@ -132,6 +132,25 @@ CODE { }; +/** + * @brief Apply the given advice to the specified range of addresses within + * the given pmap. Depending on the advice, clear the referenced and/or + * modified flags in each mapping and set the mapped page's dirty field. + * + * @param _pmap physical map + * @param _start virtual range start + * @param _end virtual range end + * @param _advice advice to apply + */ +METHOD void advise { + mmu_t _mmu; + pmap_t _pmap; + vm_offset_t _start; + vm_offset_t _end; + int _advice; +}; + + /** * @brief Change the wiring attribute for the page in the given physical * map and virtual address. diff --git a/sys/powerpc/powerpc/mp_machdep.c b/sys/powerpc/powerpc/mp_machdep.c index db20a6fba17..6835d0c84ad 100644 --- a/sys/powerpc/powerpc/mp_machdep.c +++ b/sys/powerpc/powerpc/mp_machdep.c @@ -34,6 +34,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include #include @@ -90,6 +91,9 @@ machdep_ap_bootstrap(void) #endif decr_ap_init(); + /* Give platform code a chance to do anything necessary */ + platform_smp_ap_init(); + /* Serialize console output and AP count increment */ mtx_lock_spin(&ap_boot_mtx); ap_awake++; @@ -163,7 +167,8 @@ cpu_mp_start(void) void *dpcpu; pc = &__pcpu[cpu.cr_cpuid]; - dpcpu = (void *)kmem_alloc(kernel_map, DPCPU_SIZE); + dpcpu = (void *)kmem_malloc(kernel_arena, DPCPU_SIZE, + M_WAITOK | M_ZERO); pcpu_init(pc, cpu.cr_cpuid, sizeof(*pc)); dpcpu_init(dpcpu, cpu.cr_cpuid); } else { diff --git a/sys/powerpc/powerpc/platform.c b/sys/powerpc/powerpc/platform.c index 169bbc199e1..6f7e7b56115 100644 --- a/sys/powerpc/powerpc/platform.c +++ b/sys/powerpc/powerpc/platform.c @@ -141,6 +141,12 @@ platform_smp_start_cpu(struct pcpu *cpu) return (PLATFORM_SMP_START_CPU(plat_obj, cpu)); } +void +platform_smp_ap_init() +{ + PLATFORM_SMP_AP_INIT(plat_obj); +} + #ifdef SMP struct cpu_group * cpu_topo(void) diff --git a/sys/powerpc/powerpc/platform_if.m b/sys/powerpc/powerpc/platform_if.m index 94383c327d6..cd878e02b6c 100644 --- a/sys/powerpc/powerpc/platform_if.m +++ b/sys/powerpc/powerpc/platform_if.m @@ -80,6 +80,10 @@ CODE { { return (VM_MAX_ADDRESS); } + static void platform_null_smp_ap_init(platform_t plat) + { + return; + } }; /** @@ -184,6 +188,14 @@ METHOD int smp_start_cpu { struct pcpu *_cpu; }; +/** + * @brief Start a CPU + * + */ +METHOD void smp_ap_init { + platform_t _plat; +} DEFAULT platform_null_smp_ap_init; + /** * @brief Return SMP topology */ diff --git a/sys/powerpc/powerpc/pmap_dispatch.c b/sys/powerpc/powerpc/pmap_dispatch.c index 7fd98f4b9b2..24e6076b2aa 100644 --- a/sys/powerpc/powerpc/pmap_dispatch.c +++ b/sys/powerpc/powerpc/pmap_dispatch.c @@ -90,6 +90,15 @@ RB_GENERATE(pvo_tree, pvo_entry, pvo_plink, pvo_vaddr_compare); #endif +void +pmap_advise(pmap_t pmap, vm_offset_t start, vm_offset_t end, int advice) +{ + + CTR5(KTR_PMAP, "%s(%p, %#x, %#x, %d)", __func__, pmap, start, end, + advice); + MMU_ADVISE(mmu_obj, pmap, start, end, advice); +} + void pmap_change_wiring(pmap_t pmap, vm_offset_t va, boolean_t wired) { diff --git a/sys/powerpc/pseries/mmu_phyp.c b/sys/powerpc/pseries/mmu_phyp.c new file mode 100644 index 00000000000..454bb125d86 --- /dev/null +++ b/sys/powerpc/pseries/mmu_phyp.c @@ -0,0 +1,420 @@ +/* + * Copyright (C) 2010 Andreas Tobler + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 TOOLS GMBH 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 +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "mmu_if.h" +#include "moea64_if.h" + +#include "phyp-hvcall.h" + +extern int n_slbs; + +/* + * Kernel MMU interface + */ + +static void mphyp_bootstrap(mmu_t mmup, vm_offset_t kernelstart, + vm_offset_t kernelend); +static void mphyp_cpu_bootstrap(mmu_t mmup, int ap); +static void mphyp_pte_synch(mmu_t, uintptr_t pt, struct lpte *pvo_pt); +static void mphyp_pte_clear(mmu_t, uintptr_t pt, struct lpte *pvo_pt, + uint64_t vpn, u_int64_t ptebit); +static void mphyp_pte_unset(mmu_t, uintptr_t pt, struct lpte *pvo_pt, + uint64_t vpn); +static void mphyp_pte_change(mmu_t, uintptr_t pt, struct lpte *pvo_pt, + uint64_t vpn); +static int mphyp_pte_insert(mmu_t, u_int ptegidx, struct lpte *pvo_pt); +static uintptr_t mphyp_pvo_to_pte(mmu_t, const struct pvo_entry *pvo); + +#define VSID_HASH_MASK 0x0000007fffffffffULL + + +static mmu_method_t mphyp_methods[] = { + MMUMETHOD(mmu_bootstrap, mphyp_bootstrap), + MMUMETHOD(mmu_cpu_bootstrap, mphyp_cpu_bootstrap), + + MMUMETHOD(moea64_pte_synch, mphyp_pte_synch), + MMUMETHOD(moea64_pte_clear, mphyp_pte_clear), + MMUMETHOD(moea64_pte_unset, mphyp_pte_unset), + MMUMETHOD(moea64_pte_change, mphyp_pte_change), + MMUMETHOD(moea64_pte_insert, mphyp_pte_insert), + MMUMETHOD(moea64_pvo_to_pte, mphyp_pvo_to_pte), + + { 0, 0 } +}; + +MMU_DEF_INHERIT(pseries_mmu, "mmu_phyp", mphyp_methods, 0, oea64_mmu); + +static void +mphyp_bootstrap(mmu_t mmup, vm_offset_t kernelstart, vm_offset_t kernelend) +{ + uint64_t final_pteg_count = 0; + char buf[8]; + uint32_t prop[2]; + uint32_t nptlp, shift = 0, slb_encoding = 0; + phandle_t dev, node, root; + int idx, len, res; + + moea64_early_bootstrap(mmup, kernelstart, kernelend); + + root = OF_peer(0); + + dev = OF_child(root); + while (dev != 0) { + res = OF_getprop(dev, "name", buf, sizeof(buf)); + if (res > 0 && strcmp(buf, "cpus") == 0) + break; + dev = OF_peer(dev); + } + + node = OF_child(dev); + + while (node != 0) { + res = OF_getprop(node, "device_type", buf, sizeof(buf)); + if (res > 0 && strcmp(buf, "cpu") == 0) + break; + node = OF_peer(node); + } + + res = OF_getprop(node, "ibm,pft-size", prop, sizeof(prop)); + if (res <= 0) + panic("mmu_phyp: unknown PFT size"); + final_pteg_count = 1 << prop[1]; + res = OF_getprop(node, "ibm,slb-size", prop, sizeof(prop[0])); + if (res > 0) + n_slbs = prop[0]; + + moea64_pteg_count = final_pteg_count / sizeof(struct lpteg); + + /* + * Scan the large page size property for PAPR compatible machines. + * See PAPR D.5 Changes to Section 5.1.4, 'CPU Node Properties' + * for the encoding of the property. + */ + + len = OF_getproplen(node, "ibm,segment-page-sizes"); + if (len > 0) { + /* + * We have to use a variable length array on the stack + * since we have very limited stack space. + */ + cell_t arr[len/sizeof(cell_t)]; + res = OF_getprop(node, "ibm,segment-page-sizes", &arr, + sizeof(arr)); + len /= 4; + idx = 0; + while (len > 0) { + shift = arr[idx]; + slb_encoding = arr[idx + 1]; + nptlp = arr[idx + 2]; + idx += 3; + len -= 3; + while (len > 0 && nptlp) { + idx += 2; + len -= 2; + nptlp--; + } + } + moea64_large_page_shift = shift; + moea64_large_page_size = 1 << shift; + } + + moea64_mid_bootstrap(mmup, kernelstart, kernelend); + moea64_late_bootstrap(mmup, kernelstart, kernelend); +} + +static void +mphyp_cpu_bootstrap(mmu_t mmup, int ap) +{ + struct slb *slb = PCPU_GET(slb); + register_t seg0; + int i; + + /* + * Install kernel SLB entries + */ + + __asm __volatile ("slbia"); + __asm __volatile ("slbmfee %0,%1; slbie %0;" : "=r"(seg0) : "r"(0)); + for (i = 0; i < 64; i++) { + if (!(slb[i].slbe & SLBE_VALID)) + continue; + + __asm __volatile ("slbmte %0, %1" :: + "r"(slb[i].slbv), "r"(slb[i].slbe)); + } +} + +static void +mphyp_pte_synch(mmu_t mmu, uintptr_t slot, struct lpte *pvo_pt) +{ + struct lpte pte; + uint64_t junk; + + phyp_pft_hcall(H_READ, 0, slot, 0, 0, &pte.pte_hi, &pte.pte_lo, + &junk); + + pvo_pt->pte_lo |= pte.pte_lo & (LPTE_CHG | LPTE_REF); +} + +static void +mphyp_pte_clear(mmu_t mmu, uintptr_t slot, struct lpte *pvo_pt, uint64_t vpn, + u_int64_t ptebit) +{ + + if (ptebit & LPTE_CHG) + phyp_hcall(H_CLEAR_MOD, 0, slot); + if (ptebit & LPTE_REF) + phyp_hcall(H_CLEAR_REF, 0, slot); +} + +static void +mphyp_pte_unset(mmu_t mmu, uintptr_t slot, struct lpte *pvo_pt, uint64_t vpn) +{ + + /* XXX: last argument can check the VPN -- set flag to enable */ + phyp_hcall(H_REMOVE, 0, slot, vpn); +} + +static void +mphyp_pte_change(mmu_t mmu, uintptr_t slot, struct lpte *pvo_pt, uint64_t vpn) +{ + struct lpte evicted; + uint64_t index, junk; + int64_t result; + + /* + * NB: this is protected by the global table lock, so this two-step + * is safe, except for the scratch-page case. No CPUs on which we run + * this code should be using scratch pages. + */ + KASSERT(!(pvo_pt->pte_hi & LPTE_LOCKED), + ("Locked pages not supported on PHYP")); + + /* XXX: optimization using H_PROTECT for common case? */ + result = phyp_hcall(H_REMOVE, 0, slot, vpn); + if (result != H_SUCCESS) + panic("mphyp_pte_change() invalidation failure: %ld\n", result); + result = phyp_pft_hcall(H_ENTER, H_EXACT, slot, pvo_pt->pte_hi, + pvo_pt->pte_lo, &index, &evicted.pte_lo, &junk); + if (result != H_SUCCESS) + panic("mphyp_pte_change() insertion failure: %ld\n", result); +} + +static __inline int +mphyp_pte_spillable_ident(u_int ptegidx, struct lpte *to_evict) +{ + uint64_t slot, junk, k; + struct lpte pt; + int i, j; + + /* Start at a random slot */ + i = mftb() % 8; + k = -1; + for (j = 0; j < 8; j++) { + slot = (ptegidx << 3) + (i + j) % 8; + phyp_pft_hcall(H_READ, 0, slot, 0, 0, &pt.pte_hi, &pt.pte_lo, + &junk); + + if (pt.pte_hi & LPTE_SWBITS) + continue; + + /* This is a candidate, so remember it */ + k = slot; + + /* Try to get a page that has not been used lately */ + if (!(pt.pte_lo & LPTE_REF)) { + memcpy(to_evict, &pt, sizeof(struct lpte)); + return (k); + } + } + + phyp_pft_hcall(H_READ, 0, slot, 0, 0, &to_evict->pte_hi, + &to_evict->pte_lo, &junk); + return (k); +} + +static int +mphyp_pte_insert(mmu_t mmu, u_int ptegidx, struct lpte *pvo_pt) +{ + int64_t result; + struct lpte evicted; + struct pvo_entry *pvo; + uint64_t index, junk; + u_int pteg_bktidx; + + /* Check for locked pages, which we can't support on this system */ + KASSERT(!(pvo_pt->pte_hi & LPTE_LOCKED), + ("Locked pages not supported on PHYP")); + + /* Initialize PTE */ + pvo_pt->pte_hi |= LPTE_VALID; + pvo_pt->pte_hi &= ~LPTE_HID; + evicted.pte_hi = 0; + + /* + * First try primary hash. + */ + pteg_bktidx = ptegidx; + result = phyp_pft_hcall(H_ENTER, 0, pteg_bktidx << 3, pvo_pt->pte_hi, + pvo_pt->pte_lo, &index, &evicted.pte_lo, &junk); + if (result == H_SUCCESS) + return (index & 0x07); + KASSERT(result == H_PTEG_FULL, ("Page insertion error: %ld " + "(ptegidx: %#x/%#x, PTE %#lx/%#lx", result, ptegidx, + moea64_pteg_count, pvo_pt->pte_hi, pvo_pt->pte_lo)); + + /* + * Next try secondary hash. + */ + pteg_bktidx ^= moea64_pteg_mask; + pvo_pt->pte_hi |= LPTE_HID; + result = phyp_pft_hcall(H_ENTER, 0, pteg_bktidx << 3, + pvo_pt->pte_hi, pvo_pt->pte_lo, &index, &evicted.pte_lo, &junk); + if (result == H_SUCCESS) + return (index & 0x07); + KASSERT(result == H_PTEG_FULL, ("Secondary page insertion error: %ld", + result)); + + /* + * Out of luck. Find a PTE to sacrifice. + */ + pteg_bktidx = ptegidx; + index = mphyp_pte_spillable_ident(pteg_bktidx, &evicted); + if (index == -1L) { + pteg_bktidx ^= moea64_pteg_mask; + index = mphyp_pte_spillable_ident(pteg_bktidx, &evicted); + } + + if (index == -1L) { + /* No freeable slots in either PTEG? We're hosed. */ + panic("mphyp_pte_insert: overflow"); + return (-1); + } + + if (pteg_bktidx == ptegidx) + pvo_pt->pte_hi &= ~LPTE_HID; + else + pvo_pt->pte_hi |= LPTE_HID; + + /* + * Synchronize the sacrifice PTE with its PVO, then mark both + * invalid. The PVO will be reused when/if the VM system comes + * here after a fault. + */ + + if (evicted.pte_hi & LPTE_HID) + pteg_bktidx ^= moea64_pteg_mask; /* PTEs indexed by primary */ + + LIST_FOREACH(pvo, &moea64_pvo_table[pteg_bktidx], pvo_olink) { + if (pvo->pvo_pte.lpte.pte_hi == evicted.pte_hi) { + KASSERT(pvo->pvo_pte.lpte.pte_hi & LPTE_VALID, + ("Invalid PVO for valid PTE!")); + phyp_hcall(H_REMOVE, 0, index, 0); + PVO_PTEGIDX_CLR(pvo); + moea64_pte_overflow++; + break; + } + } + + KASSERT(pvo->pvo_pte.lpte.pte_hi == evicted.pte_hi, + ("Unable to find PVO for spilled PTE")); + + /* + * Set the new PTE. + */ + result = phyp_pft_hcall(H_ENTER, H_EXACT, index, pvo_pt->pte_hi, + pvo_pt->pte_lo, &index, &evicted.pte_lo, &junk); + if (result == H_SUCCESS) + return (index & 0x07); + + panic("Page replacement error: %ld", result); + return (-1); +} + +static __inline u_int +va_to_pteg(uint64_t vsid, vm_offset_t addr, int large) +{ + uint64_t hash; + int shift; + + shift = large ? moea64_large_page_shift : ADDR_PIDX_SHFT; + hash = (vsid & VSID_HASH_MASK) ^ (((uint64_t)addr & ADDR_PIDX) >> + shift); + return (hash & moea64_pteg_mask); +} + +static uintptr_t +mphyp_pvo_to_pte(mmu_t mmu, const struct pvo_entry *pvo) +{ + uint64_t vsid; + u_int ptegidx; + + /* If the PTEG index is not set, then there is no page table entry */ + if (!PVO_PTEGIDX_ISSET(pvo)) + return (-1); + + vsid = PVO_VSID(pvo); + ptegidx = va_to_pteg(vsid, PVO_VADDR(pvo), pvo->pvo_vaddr & PVO_LARGE); + + /* + * We can find the actual pte entry without searching by grabbing + * the PTEG index from 3 unused bits in pvo_vaddr and by + * noticing the HID bit. + */ + if (pvo->pvo_pte.lpte.pte_hi & LPTE_HID) + ptegidx ^= moea64_pteg_mask; + + return ((ptegidx << 3) | PVO_PTEGIDX_GET(pvo)); +} + diff --git a/sys/powerpc/pseries/phyp-hvcall.S b/sys/powerpc/pseries/phyp-hvcall.S new file mode 100644 index 00000000000..66bf97293df --- /dev/null +++ b/sys/powerpc/pseries/phyp-hvcall.S @@ -0,0 +1,68 @@ +/*- + * Copyright (C) 2010 Andreas Tobler + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 TOOLS GMBH 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. + * + * $FreeBSD$ + */ +#include + +/* Hypervisor entry call. */ +#define hc .long 0x44000022 + +/* + * Simple HV calls take the same arguments, with the same ABI, as this + * C function + */ +ASENTRY(phyp_hcall) + mflr %r0 + std %r0,16(%r1) + hc /* invoke the hypervisor */ + ld %r0,16(%r1) + mtlr %r0 + blr /* return r3 = status */ + +/* + * PFT HV calls take a special ABI (see PAPR 14.5.4.1) + * + * r3-r7 arguments passed unchanged, r8-r10 are addresses of return values + * HV takes the same r3-r7, but returns values in r3, r4-r6 + */ +ASENTRY(phyp_pft_hcall) + mflr %r0 + std %r0,16(%r1) + stdu %r1,-80(%r1) + std %r8,48(%r1) /* save arguments */ + std %r9,56(%r1) + std %r10,64(%r1) + hc /* invoke the hypervisor */ + ld %r11,48(%r1) /* store results */ + std %r4,0(%r11) + ld %r11,56(%r1) + std %r5,0(%r11) + ld %r11,64(%r1) + std %r6,0(%r11) + ld %r1,0(%r1) /* exit */ + ld %r0,16(%r1) + mtlr %r0 + blr /* return r3 = status */ + diff --git a/sys/powerpc/pseries/phyp-hvcall.h b/sys/powerpc/pseries/phyp-hvcall.h new file mode 100644 index 00000000000..c6b87836925 --- /dev/null +++ b/sys/powerpc/pseries/phyp-hvcall.h @@ -0,0 +1,305 @@ +/*- + * Copyright (C) 2010 Andreas Tobler + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 TOOLS GMBH 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. + * + * $FreeBSD$ + */ + +#ifndef _PSERIES_PHYP_HVCALL_H_ +#define _PSERIES_PHYP_HVCALL_H_ + +/* Information taken from: Power.org PAPR, Version 2.4 (December 7, 2009). */ + +#include + +/* Return codes. */ + +#define H_SUCCESS 0 +#define H_BUSY 1 /* Hardware Busy -- Retry Later. */ +#define H_CLOSED 2 /* Virtual I/O connection is closed. */ +#define H_NOT_AVAILABLE 3 +#define H_CONSTRAINED 4 /* The request called for resources in excess of + the maximum allowed. The resultant allocation + was constrained to maximum allowed. */ +#define H_PARTIAL 5 /* The request completed only partially successful. + Parameters were valid but some specific hcall + function condition prevented fully completing the + architected function, see the specific hcall + definition for possible reasons. */ +#define H_IN_PROGRESS 14 +#define H_PAGE_REGISTERED 15 +#define H_PARTIAL_STORE 16 +#define H_PENDING 17 +#define H_CONTINUE 18 + +#define H_LONG_BUSY_ORDER_1_MS 9900 /* This return code is identical to + H_BUSY, but with the added bonus of a + hint to the partition OS. If the + partition OS can delay for 1 + millisecond, the hcall will likely + succeed on a new hcall with no further + busy return codes. If the partition OS + cannot handle a delay, they are + certainly free to immediately turn + around and try again. */ +#define H_LONG_BUSY_ORDER_10_MS 9901 /* Similar to H_LONG_BUSY_ORDER_1_MS, but + the hint is 10mSec wait this time. */ + +#define H_LONG_BUSY_ORDER_100_MS 9902 /* Similar to H_LONG_BUSY_ORDER_1_MS, but + the hint is 100mSec wait this time. */ + +#define H_LONG_BUSY_ORDER_1_S 9903 /* Similar to H_LONG_BUSY_ORDER_1_MS, but + the hint is 1Sec wait this time. */ +#define H_LONG_BUSY_ORDER_10_S 9904 /* Similar to H_LONG_BUSY_ORDER_1_MS, but + the hint is 10Sec wait this time. */ +#define H_LONG_BUSY_ORDER_100_S 9905 /* Similar to H_LONG_BUSY_ORDER_1_MS, but + the hint is 100Sec wait this time. */ + +#define H_HARDWARE -1 /* Error. */ +#define H_FUNCTION -2 /* Not supported. */ +#define H_PRIVILEGE -3 /* Caller not in privileged mode. */ +#define H_PARAMETER -4 /* Outside valid range for partition or conflicting. */ +#define H_BAD_MODE -5 /* Illegal MSR value. */ +#define H_PTEG_FULL -6 /* The requested pteg was full. */ +#define H_NOT_FOUND -7 /* The requested entitiy was not found. */ +#define H_RESERVED_DABR -8 /* The requested address is reserved by the + hypervisor on this processor. */ +#define H_NOMEM -9 +#define H_AUTHORITY -10 /* The caller did not have authority to perform the + function. */ +#define H_PERMISSION -11 /* The mapping specified by the request does not + allow for the requested transfer. */ +#define H_DROPPED -12 /* One or more packets could not be delivered to + their requested destinations. */ +#define H_S_PARM -13 /* The source parameter is illegal. */ +#define H_D_PARM -14 /* The destination parameter is illegal. */ +#define H_R_PARM -15 /* The remote TCE mapping is illegal. */ +#define H_RESOURCE -16 /* One or more required resources are in use. */ +#define H_ADAPTER_PARM -17 /* Invalid adapter. */ +#define H_RH_PARM -18 /* Resource not valid or logical partition + conflicting. */ +#define H_RCQ_PARM -19 /* RCQ not valid or logical partition conflicting. */ +#define H_SCQ_PARM -20 /* SCQ not valid or logical partition conflicting. */ +#define H_EQ_PARM -21 /* EQ not valid or logical partition conflicting. */ +#define H_RT_PARM -22 /* Invalid resource type. */ +#define H_ST_PARM -23 /* Invalid service type. */ +#define H_SIGT_PARM -24 /* Invalid signalling type. */ +#define H_TOKEN_PARM -25 /* Invalid token. */ +#define H_MLENGTH_PARM -27 /* Invalid memory length. */ +#define H_MEM_PARM -28 /* Invalid memory I/O virtual address. */ +#define H_MEM_ACCESS_PARM -29 /* Invalid memory access control. */ +#define H_ATTR_PARM -30 /* Invalid attribute value. */ +#define H_PORT_PARM -31 /* Invalid port number. */ +#define H_MCG_PARM -32 /* Invalid multicast group. */ +#define H_VL_PARM -33 /* Invalid virtual lane. */ +#define H_TSIZE_PARM -34 /* Invalid trace size. */ +#define H_TRACE_PARM -35 /* Invalid trace buffer. */ +#define H_MASK_PARM -37 /* Invalid mask value. */ +#define H_MCG_FULL -38 /* Multicast attachments exceeded. */ +#define H_ALIAS_EXIST -39 /* Alias QP already defined. */ +#define H_P_COUNTER -40 /* Invalid counter specification. */ +#define H_TABLE_FULL -41 /* Resource page table full. */ +#define H_ALT_TABLE -42 /* Alternate table already exists / alternate page + table not available. */ +#define H_MR_CONDITION -43 /* Invalid memory region condition. */ +#define H_NOT_ENOUGH_RESOURCES -44 /* Insufficient resources. */ +#define H_R_STATE -45 /* Invalid resource state condition or sequencing + error. */ +#define H_RESCINDED -46 +#define H_ABORTED -54 +#define H_P2 -55 +#define H_P3 -56 +#define H_P4 -57 +#define H_P5 -58 +#define H_P6 -59 +#define H_P7 -60 +#define H_P8 -61 +#define H_P9 -62 +#define H_NOOP -63 +#define H_TOO_BIG -64 + +#define H_UNSUPPORTED -67 /* Parameter value outside of the range supported + by this implementation. */ + +/* Flags. */ +/* Table 168. Page Frame Table Access flags field definition. */ +#define H_EXACT (1UL<<(63-24)) +#define H_R_XLATE (1UL<<(63-25)) +#define H_READ_4 (1UL<<(63-26)) + +/* Table 178. CMO Page Usage State flags Definition. */ +#define H_PAGE_STATE_CHANGE (1UL<<(63-28)) +#define H_PAGE_UNUSED ((1UL<<(63-29)) | (1UL<<(63-30))) +#define H_PAGE_SET_UNUSED (H_PAGE_STATE_CHANGE | H_PAGE_UNUSED) +#define H_PAGE_SET_LOANED (H_PAGE_SET_UNUSED | (1UL<<(63-31))) +#define H_PAGE_SET_ACTIVE H_PAGE_STATE_CHANGE + +/* Table 168. Page Frame Table Access flags field definition. */ +#define H_AVPN (1UL<<(63-32)) +#define H_ANDCOND (1UL<<(63-33)) + +#define H_ICACHE_INVALIDATE (1UL<<(63-40)) +#define H_ICACHE_SYNCHRONIZE (1UL<<(63-41)) + +#define H_ZERO_PAGE (1UL<<(63-48)) +#define H_COPY_PAGE (1UL<<(63-49)) + +#define H_N (1UL<<(63-61)) +#define H_PP1 (1UL<<(63-62)) +#define H_PP2 (1UL<<(63-63)) + +/* pSeries hypervisor opcodes. */ +#define H_REMOVE 0x04 +#define H_ENTER 0x08 +#define H_READ 0x0c +#define H_CLEAR_MOD 0x10 +#define H_CLEAR_REF 0x14 +#define H_PROTECT 0x18 +#define H_GET_TCE 0x1c +#define H_PUT_TCE 0x20 +#define H_SET_SPRG0 0x24 +#define H_SET_DABR 0x28 +#define H_PAGE_INIT 0x2c +#define H_SET_ASR 0x30 +#define H_ASR_ON 0x34 +#define H_ASR_OFF 0x38 +#define H_LOGICAL_CI_LOAD 0x3c +#define H_LOGICAL_CI_STORE 0x40 +#define H_LOGICAL_CACHE_LOAD 0x44 +#define H_LOGICAL_CACHE_STORE 0x48 +#define H_LOGICAL_ICBI 0x4c +#define H_LOGICAL_DCBF 0x50 +#define H_GET_TERM_CHAR 0x54 +#define H_PUT_TERM_CHAR 0x58 +#define H_REAL_TO_LOGICAL 0x5c +#define H_HYPERVISOR_DATA 0x60 +#define H_EOI 0x64 +#define H_CPPR 0x68 +#define H_IPI 0x6c +#define H_IPOLL 0x70 +#define H_XIRR 0x74 +#define H_MIGRATE_DMA 0x78 +#define H_PERFMON 0x7c +#define H_REGISTER_VPA 0xdc +#define H_CEDE 0xe0 +#define H_CONFER 0xe4 +#define H_PROD 0xe8 +#define H_GET_PPP 0xec +#define H_SET_PPP 0xf0 +#define H_PURR 0xf4 +#define H_PIC 0xf8 +#define H_REG_CRQ 0xfc +#define H_FREE_CRQ 0x100 +#define H_VIO_SIGNAL 0x104 +#define H_SEND_CRQ 0x108 +#define H_PUT_RTCE 0x10c +#define H_COPY_RDMA 0x110 +#define H_REGISTER_LOGICAL_LAN 0x114 +#define H_FREE_LOGICAL_LAN 0x118 +#define H_ADD_LOGICAL_LAN_BUFFER 0x11c +#define H_SEND_LOGICAL_LAN 0x120 +#define H_BULK_REMOVE 0x124 +#define H_WRITE_RDMA 0x128 +#define H_READ_RDMA 0x12c +#define H_MULTICAST_CTRL 0x130 +#define H_SET_XDABR 0x134 +#define H_STUFF_TCE 0x138 +#define H_PUT_TCE_INDIRECT 0x13c +#define H_PUT_RTCE_INDIRECT 0x140 +#define H_CHANGE_LOGICAL_LAN_MAC 0x14c +#define H_VTERM_PARTNER_INFO 0x150 +#define H_REGISTER_VTERM 0x154 +#define H_FREE_VTERM 0x158 +/* Reserved .... +#define H_RESET_EVENTS 0x15c +#define H_ALLOC_RESOURCE 0x160 +#define H_FREE_RESOURCE 0x164 +#define H_MODIFY_QP 0x168 +#define H_QUERY_QP 0x16c +#define H_REREGISTER_PMR 0x170 +#define H_REGISTER_SMR 0x174 +#define H_QUERY_MR 0x178 +#define H_QUERY_MW 0x17c +#define H_QUERY_HCA 0x180 +#define H_QUERY_PORT 0x184 +#define H_MODIFY_PORT 0x188 +#define H_DEFINE_AQP1 0x18c +#define H_GET_TRACE_BUFFER 0x190 +#define H_DEFINE_AQP0 0x194 +#define H_RESIZE_MR 0x198 +#define H_ATTACH_MCQP 0x19c +#define H_DETACH_MCQP 0x1a0 +#define H_CREATE_RPT 0x1a4 +#define H_REMOVE_RPT 0x1a8 +#define H_REGISTER_RPAGES 0x1ac +#define H_DISABLE_AND_GETC 0x1b0 +#define H_ERROR_DATA 0x1b4 +#define H_GET_HCA_INFO 0x1b8 +#define H_GET_PERF_COUNT 0x1bc +#define H_MANAGE_TRACE 0x1c0 +.... */ +#define H_FREE_LOGICAL_LAN_BUFFER 0x1d4 +#define H_POLL_PENDING 0x1d8 +/* Reserved .... +#define H_QUERY_INT_STATE 0x1e4 +.... */ +#define H_LIOBN_ATTRIBUTES 0x240 +#define H_ILLAN_ATTRIBUTES 0x244 +#define H_REMOVE_RTCE 0x24c +/* Reserved ... +#define H_MODIFY_HEA_QP 0x250 +#define H_QUERY_HEA_QP 0x254 +#define H_QUERY_HEA 0x258 +#define H_QUERY_HEA_PORT 0x25c +#define H_MODIFY_HEA_PORT 0x260 +#define H_REG_BCMC 0x264 +#define H_DEREG_BCMC 0x268 +#define H_REGISTER_HEA_RPAGES 0x26c +#define H_DISABLE_AND_GET_HEA 0x270 +#define H_GET_HEA_INFO 0x274 +#define H_ALLOC_HEA_RESOURCE 0x278 +#define H_ADD_CONN 0x284 +#define H_DEL_CONN 0x288 +... */ +#define H_JOIN 0x298 +#define H_DONOR_OPERATION 0x29c +#define H_VASI_SIGNAL 0x2a0 +#define H_VASI_STATE 0x2a4 +#define H_VIOCTL 0x2a8 +#define H_VRMASD 0x2ac +#define H_ENABLE_CRQ 0x2b0 +/* Reserved ... +#define H_GET_EM_PARMS 0x2b8 +... */ +#define H_VPM_STAT 0x2bc +#define H_SET_MPP 0x2d0 +#define H_GET_MPP 0x2d4 +#define MAX_HCALL_OPCODE H_GET_MPP + +int64_t phyp_hcall(uint64_t opcode, ...); +int64_t phyp_pft_hcall(uint64_t opcode, uint64_t flags, uint64_t pteidx, + uint64_t pte_hi, uint64_t pte_lo, uint64_t *pteidx_out, uint64_t *ptelo_out, + uint64_t *r6); + +#endif /* _PSERIES_PHYP_HVCALL_H_ */ + diff --git a/sys/powerpc/pseries/phyp_console.c b/sys/powerpc/pseries/phyp_console.c new file mode 100644 index 00000000000..8f66f146957 --- /dev/null +++ b/sys/powerpc/pseries/phyp_console.c @@ -0,0 +1,420 @@ +/*- + * Copyright (C) 2011 by Nathan Whitehorn. 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 TOOLS GMBH 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 +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "phyp-hvcall.h" +#include "uart_if.h" + +struct uart_phyp_softc { + device_t dev; + phandle_t node; + int vtermid; + + struct tty *tp; + struct resource *irqres; + int irqrid; + struct callout callout; + void *sc_icookie; + int polltime; + + struct mtx sc_mtx; + int protocol; + + union { + uint64_t u64[2]; + char str[16]; + } phyp_inbuf; + uint64_t inbuflen; + uint8_t outseqno; +}; + +static struct uart_phyp_softc *console_sc = NULL; +#if defined(KDB) +static int alt_break_state; +#endif + +enum { + HVTERM1, HVTERMPROT +}; + +#define VS_DATA_PACKET_HEADER 0xff +#define VS_CONTROL_PACKET_HEADER 0xfe +#define VSV_SET_MODEM_CTL 0x01 +#define VSV_MODEM_CTL_UPDATE 0x02 +#define VSV_RENEGOTIATE_CONNECTION 0x03 +#define VS_QUERY_PACKET_HEADER 0xfd +#define VSV_SEND_VERSION_NUMBER 0x01 +#define VSV_SEND_MODEM_CTL_STATUS 0x02 +#define VS_QUERY_RESPONSE_PACKET_HEADER 0xfc + +static int uart_phyp_probe(device_t dev); +static int uart_phyp_attach(device_t dev); +static void uart_phyp_intr(void *v); + +static device_method_t uart_phyp_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, uart_phyp_probe), + DEVMETHOD(device_attach, uart_phyp_attach), + + DEVMETHOD_END +}; + +static driver_t uart_phyp_driver = { + "uart", + uart_phyp_methods, + sizeof(struct uart_phyp_softc), +}; + +DRIVER_MODULE(uart_phyp, vdevice, uart_phyp_driver, uart_devclass, 0, 0); + +static cn_probe_t uart_phyp_cnprobe; +static cn_init_t uart_phyp_cninit; +static cn_term_t uart_phyp_cnterm; +static cn_getc_t uart_phyp_cngetc; +static cn_putc_t uart_phyp_cnputc; +static cn_grab_t uart_phyp_cngrab; +static cn_ungrab_t uart_phyp_cnungrab; + +CONSOLE_DRIVER(uart_phyp); + +static void uart_phyp_ttyoutwakeup(struct tty *tp); + +static struct ttydevsw uart_phyp_tty_class = { + .tsw_flags = TF_INITLOCK|TF_CALLOUT, + .tsw_outwakeup = uart_phyp_ttyoutwakeup, +}; + +static int +uart_phyp_probe_node(struct uart_phyp_softc *sc) +{ + phandle_t node = sc->node; + uint32_t reg; + char buf[64]; + + sc->inbuflen = 0; + sc->outseqno = 0; + + if (OF_getprop(node, "name", buf, sizeof(buf)) <= 0) + return (ENXIO); + if (strcmp(buf, "vty") != 0) + return (ENXIO); + + if (OF_getprop(node, "device_type", buf, sizeof(buf)) <= 0) + return (ENXIO); + if (strcmp(buf, "serial") != 0) + return (ENXIO); + + reg = -1; + OF_getprop(node, "reg", ®, sizeof(reg)); + if (reg == -1) + return (ENXIO); + sc->node = node; + + if (OF_getprop(node, "compatible", buf, sizeof(buf)) <= 0) + return (ENXIO); + if (strcmp(buf, "hvterm1") == 0) { + sc->protocol = HVTERM1; + return (0); + } else if (strcmp(buf, "hvterm-protocol") == 0) { + sc->protocol = HVTERMPROT; + return (0); + } + + return (ENXIO); +} + +static int +uart_phyp_probe(device_t dev) +{ + const char *name; + struct uart_phyp_softc sc; + int err; + + name = ofw_bus_get_name(dev); + if (name == NULL || strcmp(name, "vty") != 0) + return (ENXIO); + + sc.node = ofw_bus_get_node(dev); + err = uart_phyp_probe_node(&sc); + if (err != 0) + return (err); + + device_set_desc(dev, "POWER Hypervisor Virtual Serial Port"); + + return (err); +} + +static void +uart_phyp_cnprobe(struct consdev *cp) +{ + char buf[64]; + ihandle_t stdout; + phandle_t input, opts, chosen; + static struct uart_phyp_softc sc; + + if ((opts = OF_finddevice("/options")) == -1) + goto fail; + if ((chosen = OF_finddevice("/chosen")) == -1) + goto fail; + + /* Check if OF has an active stdin/stdout */ + input = -1; + if (OF_getprop(chosen, "stdout", &stdout, + sizeof(stdout)) == sizeof(stdout) && stdout != 0) + input = OF_instance_to_package(stdout); + if (input == -1) + goto fail; + + if (OF_getprop(input, "device_type", buf, sizeof(buf)) == -1) + goto fail; + if (strcmp(buf, "serial") != 0) + goto fail; + + sc.node = input; + if (uart_phyp_probe_node(&sc) != 0) + goto fail; + mtx_init(&sc.sc_mtx, "uart_phyp", NULL, MTX_SPIN | MTX_QUIET | + MTX_NOWITNESS); + + cp->cn_pri = CN_NORMAL; + console_sc = ≻ + return; + +fail: + cp->cn_pri = CN_DEAD; + return; +} + +static int +uart_phyp_attach(device_t dev) +{ + struct uart_phyp_softc *sc; + int unit; + + sc = device_get_softc(dev); + sc->dev = dev; + sc->node = ofw_bus_get_node(dev); + uart_phyp_probe_node(sc); + + unit = device_get_unit(dev); + sc->tp = tty_alloc(&uart_phyp_tty_class, sc); + mtx_init(&sc->sc_mtx, device_get_nameunit(dev), NULL, + MTX_SPIN | MTX_QUIET | MTX_NOWITNESS); + + if (console_sc != NULL && console_sc->vtermid == sc->vtermid) { + sc->outseqno = console_sc->outseqno; + console_sc = sc; + sprintf(uart_phyp_consdev.cn_name, "ttyu%r", unit); + tty_init_console(sc->tp, 0); + } + + sc->irqrid = 0; +#ifdef NOTYET + sc->irqres = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->irqrid, + RF_ACTIVE | RF_SHAREABLE); +#else + sc->irqres = NULL; +#endif + if (sc->irqres != NULL) { + bus_setup_intr(dev, sc->irqres, INTR_TYPE_TTY | INTR_MPSAFE, + NULL, uart_phyp_intr, sc, &sc->sc_icookie); + } else { + callout_init(&sc->callout, CALLOUT_MPSAFE); + sc->polltime = hz / 20; + if (sc->polltime < 1) + sc->polltime = 1; + callout_reset(&sc->callout, sc->polltime, uart_phyp_intr, sc); + } + + tty_makedev(sc->tp, NULL, "u%r", unit); + + return (0); +} + +static void +uart_phyp_cninit(struct consdev *cp) +{ + + strcpy(cp->cn_name, "phypcons"); +} + +static void +uart_phyp_cnterm(struct consdev *cp) +{ +} + +static int +uart_phyp_get(struct uart_phyp_softc *sc, void *buffer, size_t bufsize) +{ + int err; + + uart_lock(&sc->sc_mtx); + if (sc->inbuflen == 0) { + err = phyp_pft_hcall(H_GET_TERM_CHAR, sc->vtermid, + 0, 0, 0, &sc->inbuflen, &sc->phyp_inbuf.u64[0], + &sc->phyp_inbuf.u64[1]); + if (err != H_SUCCESS) { + uart_unlock(&sc->sc_mtx); + return (-1); + } + } + + if (sc->inbuflen == 0) { + uart_unlock(&sc->sc_mtx); + return (0); + } + + if (bufsize > sc->inbuflen) + bufsize = sc->inbuflen; + memcpy(buffer, sc->phyp_inbuf.str, bufsize); + sc->inbuflen -= bufsize; + if (sc->inbuflen > 0) + memmove(&sc->phyp_inbuf.str[0], &sc->phyp_inbuf.str[bufsize], + sc->inbuflen); + + uart_unlock(&sc->sc_mtx); + return (bufsize); +} + +static int +uart_phyp_put(struct uart_phyp_softc *sc, void *buffer, size_t bufsize) +{ + uint16_t seqno; + uint64_t len = 0; + union { + uint64_t u64; + char bytes[8]; + } cbuf; + + uart_lock(&sc->sc_mtx); + switch (sc->protocol) { + case HVTERM1: + if (bufsize > 8) + bufsize = 8; + memcpy(&cbuf, buffer, bufsize); + len = bufsize; + break; + case HVTERMPROT: + if (bufsize > 4) + bufsize = 4; + seqno = sc->outseqno++; + cbuf.bytes[0] = VS_DATA_PACKET_HEADER; + cbuf.bytes[1] = 4 + bufsize; /* total length */ + cbuf.bytes[2] = (seqno >> 8) & 0xff; + cbuf.bytes[3] = seqno & 0xff; + memcpy(&cbuf.bytes[4], buffer, bufsize); + len = 4 + bufsize; + break; + } + phyp_hcall(H_PUT_TERM_CHAR, sc->vtermid, len, cbuf.u64, 0); + uart_unlock(&sc->sc_mtx); + + return (bufsize); +} + +static int +uart_phyp_cngetc(struct consdev *cp) +{ + unsigned char c; + int retval; + + retval = uart_phyp_get(console_sc, &c, 1); + if (retval != 1) + return (-1); +#if defined(KDB) + kdb_alt_break(c, &alt_break_state); +#endif + + return (c); +} + +static void +uart_phyp_cnputc(struct consdev *cp, int c) +{ + unsigned char ch = c; + uart_phyp_put(console_sc, &ch, 1); +} + +static void +uart_phyp_cngrab(struct consdev *cp) +{ +} + +static void +uart_phyp_cnungrab(struct consdev *cp) +{ +} + +static void +uart_phyp_ttyoutwakeup(struct tty *tp) +{ + struct uart_phyp_softc *sc; + char buffer[8]; + int len; + + sc = tty_softc(tp); + + while ((len = ttydisc_getc(tp, buffer, sizeof(buffer))) != 0) + uart_phyp_put(sc, buffer, len); +} + +static void +uart_phyp_intr(void *v) +{ + struct uart_phyp_softc *sc = v; + struct tty *tp = sc->tp; + unsigned char c; + int len; + + tty_lock(tp); + while ((len = uart_phyp_get(sc, &c, 1)) > 0) + ttydisc_rint(tp, c, 0); + ttydisc_rint_done(tp); + tty_unlock(tp); + + if (sc->irqres == NULL) + callout_reset(&sc->callout, sc->polltime, uart_phyp_intr, sc); +} + diff --git a/sys/powerpc/pseries/platform_chrp.c b/sys/powerpc/pseries/platform_chrp.c new file mode 100644 index 00000000000..e86ed84ffb7 --- /dev/null +++ b/sys/powerpc/pseries/platform_chrp.c @@ -0,0 +1,411 @@ +/*- + * Copyright (c) 2008 Marcel Moolenaar + * Copyright (c) 2009 Nathan Whitehorn + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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 +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "platform_if.h" + +#ifdef SMP +extern void *ap_pcpu; +#endif + +#ifdef __powerpc64__ +static uint8_t splpar_vpa[640] __aligned(64); +#endif + +static vm_offset_t realmaxaddr = VM_MAX_ADDRESS; + +static int chrp_probe(platform_t); +static int chrp_attach(platform_t); +void chrp_mem_regions(platform_t, struct mem_region **phys, int *physsz, + struct mem_region **avail, int *availsz); +static vm_offset_t chrp_real_maxaddr(platform_t); +static u_long chrp_timebase_freq(platform_t, struct cpuref *cpuref); +static int chrp_smp_first_cpu(platform_t, struct cpuref *cpuref); +static int chrp_smp_next_cpu(platform_t, struct cpuref *cpuref); +static int chrp_smp_get_bsp(platform_t, struct cpuref *cpuref); +static void chrp_smp_ap_init(platform_t); +#ifdef SMP +static int chrp_smp_start_cpu(platform_t, struct pcpu *cpu); +static struct cpu_group *chrp_smp_topo(platform_t plat); +#endif +static void chrp_reset(platform_t); +#ifdef __powerpc64__ +#include "phyp-hvcall.h" +static void phyp_cpu_idle(sbintime_t sbt); +#endif + +static platform_method_t chrp_methods[] = { + PLATFORMMETHOD(platform_probe, chrp_probe), + PLATFORMMETHOD(platform_attach, chrp_attach), + PLATFORMMETHOD(platform_mem_regions, chrp_mem_regions), + PLATFORMMETHOD(platform_real_maxaddr, chrp_real_maxaddr), + PLATFORMMETHOD(platform_timebase_freq, chrp_timebase_freq), + + PLATFORMMETHOD(platform_smp_ap_init, chrp_smp_ap_init), + PLATFORMMETHOD(platform_smp_first_cpu, chrp_smp_first_cpu), + PLATFORMMETHOD(platform_smp_next_cpu, chrp_smp_next_cpu), + PLATFORMMETHOD(platform_smp_get_bsp, chrp_smp_get_bsp), +#ifdef SMP + PLATFORMMETHOD(platform_smp_start_cpu, chrp_smp_start_cpu), + PLATFORMMETHOD(platform_smp_topo, chrp_smp_topo), +#endif + + PLATFORMMETHOD(platform_reset, chrp_reset), + + { 0, 0 } +}; + +static platform_def_t chrp_platform = { + "chrp", + chrp_methods, + 0 +}; + +PLATFORM_DEF(chrp_platform); + +static int +chrp_probe(platform_t plat) +{ + if (OF_finddevice("/memory") != -1 || OF_finddevice("/memory@0") != -1) + return (BUS_PROBE_GENERIC); + + return (ENXIO); +} + +static int +chrp_attach(platform_t plat) +{ +#ifdef __powerpc64__ + /* XXX: check for /rtas/ibm,hypertas-functions? */ + if (!(mfmsr() & PSL_HV)) { + struct mem_region *phys, *avail; + int nphys, navail; + mem_regions(&phys, &nphys, &avail, &navail); + realmaxaddr = phys[0].mr_size; + + pmap_mmu_install("mmu_phyp", BUS_PROBE_SPECIFIC); + cpu_idle_hook = phyp_cpu_idle; + + /* Set up important VPA fields */ + bzero(splpar_vpa, sizeof(splpar_vpa)); + splpar_vpa[4] = (uint8_t)((sizeof(splpar_vpa) >> 8) & 0xff); + splpar_vpa[5] = (uint8_t)(sizeof(splpar_vpa) & 0xff); + splpar_vpa[0xba] = 1; /* Maintain FPRs */ + splpar_vpa[0xbb] = 1; /* Maintain PMCs */ + splpar_vpa[0xfc] = 0xff; /* Maintain full SLB */ + splpar_vpa[0xfd] = 0xff; + splpar_vpa[0xff] = 1; /* Maintain Altivec */ + mb(); + + /* Set up hypervisor CPU stuff */ + chrp_smp_ap_init(plat); + } +#endif + + return (0); +} + +void +chrp_mem_regions(platform_t plat, struct mem_region **phys, int *physsz, + struct mem_region **avail, int *availsz) +{ + ofw_mem_regions(phys,physsz,avail,availsz); +} + +static vm_offset_t +chrp_real_maxaddr(platform_t plat) +{ + return (realmaxaddr); +} + +static u_long +chrp_timebase_freq(platform_t plat, struct cpuref *cpuref) +{ + phandle_t phandle; + int32_t ticks = -1; + + phandle = cpuref->cr_hwref; + + OF_getprop(phandle, "timebase-frequency", &ticks, sizeof(ticks)); + + if (ticks <= 0) + panic("Unable to determine timebase frequency!"); + + return (ticks); +} + +static int +chrp_smp_first_cpu(platform_t plat, struct cpuref *cpuref) +{ + char buf[8]; + phandle_t cpu, dev, root; + int res, cpuid; + + root = OF_peer(0); + + dev = OF_child(root); + while (dev != 0) { + res = OF_getprop(dev, "name", buf, sizeof(buf)); + if (res > 0 && strcmp(buf, "cpus") == 0) + break; + dev = OF_peer(dev); + } + if (dev == 0) { + /* + * psim doesn't have a name property on the /cpus node, + * but it can be found directly + */ + dev = OF_finddevice("/cpus"); + if (dev == 0) + return (ENOENT); + } + + cpu = OF_child(dev); + + while (cpu != 0) { + res = OF_getprop(cpu, "device_type", buf, sizeof(buf)); + if (res > 0 && strcmp(buf, "cpu") == 0) + break; + cpu = OF_peer(cpu); + } + if (cpu == 0) + return (ENOENT); + + cpuref->cr_hwref = cpu; + res = OF_getprop(cpu, "ibm,ppc-interrupt-server#s", &cpuid, + sizeof(cpuid)); + if (res <= 0) + res = OF_getprop(cpu, "reg", &cpuid, sizeof(cpuid)); + if (res <= 0) + cpuid = 0; + cpuref->cr_cpuid = cpuid; + + return (0); +} + +static int +chrp_smp_next_cpu(platform_t plat, struct cpuref *cpuref) +{ + char buf[8]; + phandle_t cpu; + int i, res, cpuid; + + /* Check for whether it should be the next thread */ + res = OF_getproplen(cpuref->cr_hwref, "ibm,ppc-interrupt-server#s"); + if (res > 0) { + cell_t interrupt_servers[res/sizeof(cell_t)]; + OF_getprop(cpuref->cr_hwref, "ibm,ppc-interrupt-server#s", + interrupt_servers, res); + for (i = 0; i < res/sizeof(cell_t) - 1; i++) { + if (interrupt_servers[i] == cpuref->cr_cpuid) { + cpuref->cr_cpuid = interrupt_servers[i+1]; + return (0); + } + } + } + + /* Next CPU core/package */ + cpu = OF_peer(cpuref->cr_hwref); + while (cpu != 0) { + res = OF_getprop(cpu, "device_type", buf, sizeof(buf)); + if (res > 0 && strcmp(buf, "cpu") == 0) + break; + cpu = OF_peer(cpu); + } + if (cpu == 0) + return (ENOENT); + + cpuref->cr_hwref = cpu; + res = OF_getprop(cpu, "ibm,ppc-interrupt-server#s", &cpuid, + sizeof(cpuid)); + if (res <= 0) + res = OF_getprop(cpu, "reg", &cpuid, sizeof(cpuid)); + if (res <= 0) + cpuid = 0; + cpuref->cr_cpuid = cpuid; + + return (0); +} + +static int +chrp_smp_get_bsp(platform_t plat, struct cpuref *cpuref) +{ + ihandle_t inst; + phandle_t bsp, chosen; + int res, cpuid; + + chosen = OF_finddevice("/chosen"); + if (chosen == 0) + return (ENXIO); + + res = OF_getprop(chosen, "cpu", &inst, sizeof(inst)); + if (res < 0) + return (ENXIO); + + bsp = OF_instance_to_package(inst); + + /* Pick the primary thread. Can it be any other? */ + cpuref->cr_hwref = bsp; + res = OF_getprop(bsp, "ibm,ppc-interrupt-server#s", &cpuid, + sizeof(cpuid)); + if (res <= 0) + res = OF_getprop(bsp, "reg", &cpuid, sizeof(cpuid)); + if (res <= 0) + cpuid = 0; + cpuref->cr_cpuid = cpuid; + + return (0); +} + +#ifdef SMP +static int +chrp_smp_start_cpu(platform_t plat, struct pcpu *pc) +{ + cell_t start_cpu; + int result, err, timeout; + + if (!rtas_exists()) { + printf("RTAS uninitialized: unable to start AP %d\n", + pc->pc_cpuid); + return (ENXIO); + } + + start_cpu = rtas_token_lookup("start-cpu"); + if (start_cpu == -1) { + printf("RTAS unknown method: unable to start AP %d\n", + pc->pc_cpuid); + return (ENXIO); + } + + ap_pcpu = pc; + powerpc_sync(); + + result = rtas_call_method(start_cpu, 3, 1, pc->pc_cpuid, EXC_RST, pc, + &err); + if (result < 0 || err != 0) { + printf("RTAS error (%d/%d): unable to start AP %d\n", + result, err, pc->pc_cpuid); + return (ENXIO); + } + + timeout = 10000; + while (!pc->pc_awake && timeout--) + DELAY(100); + + return ((pc->pc_awake) ? 0 : EBUSY); +} + +static struct cpu_group * +chrp_smp_topo(platform_t plat) +{ + struct pcpu *pc, *last_pc; + int i, ncores, ncpus; + + ncores = ncpus = 0; + last_pc = NULL; + for (i = 0; i <= mp_maxid; i++) { + pc = pcpu_find(i); + if (pc == NULL) + continue; + if (last_pc == NULL || pc->pc_hwref != last_pc->pc_hwref) + ncores++; + last_pc = pc; + ncpus++; + } + + if (ncpus % ncores != 0) { + printf("WARNING: Irregular SMP topology. Performance may be " + "suboptimal (%d CPUS, %d cores)\n", ncpus, ncores); + return (smp_topo_none()); + } + + /* Don't do anything fancier for non-threaded SMP */ + if (ncpus == ncores) + return (smp_topo_none()); + + return (smp_topo_1level(CG_SHARE_L1, ncpus / ncores, CG_FLAG_SMT)); +} +#endif + +static void +chrp_reset(platform_t platform) +{ + OF_reboot(); +} + +#ifdef __powerpc64__ +static void +phyp_cpu_idle(sbintime_t sbt) +{ + #ifdef NOTYET /* Causes hangs on QEMU */ + phyp_hcall(H_CEDE); + #endif +} + +static void +chrp_smp_ap_init(platform_t platform) +{ + if (!(mfmsr() & PSL_HV)) { + /* Set interrupt priority */ + phyp_hcall(H_CPPR, 0xff); + + /* Register VPA */ + phyp_hcall(H_REGISTER_VPA, 1UL, PCPU_GET(cpuid), splpar_vpa); + } +} +#else +static void +chrp_smp_ap_init(platform_t platform) +{ +} +#endif + diff --git a/sys/powerpc/pseries/plpar_iommu.c b/sys/powerpc/pseries/plpar_iommu.c new file mode 100644 index 00000000000..3b67a3888e8 --- /dev/null +++ b/sys/powerpc/pseries/plpar_iommu.c @@ -0,0 +1,239 @@ +/*- + * Copyright (c) 2013, Nathan Whitehorn + * 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 unmodified, 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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 +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include + +MALLOC_DEFINE(M_PHYPIOMMU, "iommu", "IOMMU data for PAPR LPARs"); + +struct papr_iommu_map { + uint32_t iobn; + vmem_t *vmem; + struct papr_iommu_map *next; +}; + +static SLIST_HEAD(iommu_maps, iommu_map) iommu_map_head = + SLIST_HEAD_INITIALIZER(iommu_map_head); +static int papr_supports_stuff_tce = -1; + +struct iommu_map { + uint32_t iobn; + vmem_t *vmem; + + SLIST_ENTRY(iommu_map) entries; +}; + +struct dma_window { + struct iommu_map *map; + bus_addr_t start; + bus_addr_t end; +}; + +int +phyp_iommu_set_dma_tag(device_t dev, device_t child, bus_dma_tag_t tag) +{ + device_t p; + phandle_t node; + cell_t dma_acells, dma_scells, dmawindow[5]; + struct iommu_map *i; + + for (p = child; p != NULL; p = device_get_parent(p)) { + if (ofw_bus_has_prop(p, "ibm,my-dma-window")) + break; + if (ofw_bus_has_prop(p, "ibm,dma-window")) + break; + } + + if (p == NULL) + return (ENXIO); + + node = ofw_bus_get_node(p); + if (OF_getprop(node, "ibm,#dma-size-cells", &dma_scells, + sizeof(cell_t)) <= 0) + OF_searchprop(node, "#size-cells", &dma_scells, sizeof(cell_t)); + if (OF_getprop(node, "ibm,#dma-address-cells", &dma_acells, + sizeof(cell_t)) <= 0) + OF_searchprop(node, "#address-cells", &dma_acells, + sizeof(cell_t)); + + if (ofw_bus_has_prop(p, "ibm,my-dma-window")) + OF_getprop(node, "ibm,my-dma-window", dmawindow, + sizeof(cell_t)*(dma_scells + dma_acells + 1)); + else + OF_getprop(node, "ibm,dma-window", dmawindow, + sizeof(cell_t)*(dma_scells + dma_acells + 1)); + + struct dma_window *window = malloc(sizeof(struct dma_window), + M_PHYPIOMMU, M_WAITOK); + if (dma_acells == 1) + window->start = dmawindow[1]; + else + window->start = ((uint64_t)(dmawindow[1]) << 32) | dmawindow[2]; + if (dma_scells == 1) + window->end = window->start + dmawindow[dma_acells + 1]; + else + window->end = window->start + + (((uint64_t)(dmawindow[dma_acells + 1]) << 32) | + dmawindow[dma_acells + 2]); + + window->map = NULL; + SLIST_FOREACH(i, &iommu_map_head, entries) { + if (i->iobn == dmawindow[0]) { + window->map = i; + break; + } + } + + if (window->map == NULL) { + window->map = malloc(sizeof(struct iommu_map), M_PHYPIOMMU, + M_WAITOK); + window->map->iobn = dmawindow[0]; + /* + * Allocate IOMMU range beginning at PAGE_SIZE. Some drivers + * (em(4), for example) do not like getting mappings at 0. + */ + window->map->vmem = vmem_create("IOMMU mappings", PAGE_SIZE, + trunc_page(VMEM_ADDR_MAX) - PAGE_SIZE, PAGE_SIZE, 0, + M_BESTFIT | M_NOWAIT); + } + + /* + * Check experimentally whether we can use H_STUFF_TCE. It is required + * by the spec but some firmware (e.g. QEMU) does not actually support + * it + */ + if (papr_supports_stuff_tce == -1) + papr_supports_stuff_tce = !(phyp_hcall(H_STUFF_TCE, + window->map->iobn, 0, 0, 0) == H_FUNCTION); + + bus_dma_tag_set_iommu(tag, dev, window); + + return (0); +} + +int +phyp_iommu_map(device_t dev, bus_dma_segment_t *segs, int *nsegs, + bus_addr_t min, bus_addr_t max, bus_size_t alignment, bus_addr_t boundary, + void *cookie) +{ + struct dma_window *window = cookie; + bus_addr_t minaddr, maxaddr; + bus_addr_t alloced; + bus_size_t allocsize; + int error, i, j; + uint64_t tce; + minaddr = window->start; + maxaddr = window->end; + + /* XXX: handle exclusion range in a more useful way */ + if (min < maxaddr) + maxaddr = min; + + /* XXX: consolidate segs? */ + for (i = 0; i < *nsegs; i++) { + allocsize = round_page(segs[i].ds_len + + (segs[i].ds_addr & PAGE_MASK)); + error = vmem_xalloc(window->map->vmem, allocsize, + (alignment < PAGE_SIZE) ? PAGE_SIZE : alignment, 0, + boundary, minaddr, maxaddr, M_BESTFIT | M_NOWAIT, &alloced); + if (error != 0) { + panic("VMEM failure: %d\n", error); + return (error); + } + KASSERT(alloced % PAGE_SIZE == 0, ("Alloc not page aligned")); + KASSERT((alloced + (segs[i].ds_addr & PAGE_MASK)) % + alignment == 0, + ("Allocated segment does not match alignment constraint")); + + tce = trunc_page(segs[i].ds_addr); + tce |= 0x3; /* read/write */ + if (papr_supports_stuff_tce) { + error = phyp_hcall(H_STUFF_TCE, window->map->iobn, + alloced, tce, allocsize/PAGE_SIZE); + } else { + for (j = 0; j < allocsize; j += PAGE_SIZE) + error = phyp_hcall(H_PUT_TCE, window->map->iobn, + alloced + j, tce + j); + } + + segs[i].ds_addr = alloced + (segs[i].ds_addr & PAGE_MASK); + KASSERT(segs[i].ds_addr > 0, ("Address needs to be positive")); + KASSERT(segs[i].ds_addr + segs[i].ds_len < maxaddr, + ("Address not in range")); + if (error < 0) { + panic("IOMMU mapping error: %d\n", error); + return (ENOMEM); + } + } + + return (0); +} + +int +phyp_iommu_unmap(device_t dev, bus_dma_segment_t *segs, int nsegs, void *cookie) +{ + struct dma_window *window = cookie; + bus_addr_t pageround; + bus_size_t roundedsize; + int i; + bus_addr_t j; + + for (i = 0; i < nsegs; i++) { + pageround = trunc_page(segs[i].ds_addr); + roundedsize = round_page(segs[i].ds_len + + (segs[i].ds_addr & PAGE_MASK)); + + if (papr_supports_stuff_tce) { + phyp_hcall(H_STUFF_TCE, window->map->iobn, pageround, 0, + roundedsize/PAGE_SIZE); + } else { + for (j = 0; j < roundedsize; j += PAGE_SIZE) + phyp_hcall(H_PUT_TCE, window->map->iobn, + pageround + j, 0); + } + + vmem_xfree(window->map->vmem, pageround, roundedsize); + } + + return (0); +} + diff --git a/sys/powerpc/pseries/plpar_iommu.h b/sys/powerpc/pseries/plpar_iommu.h new file mode 100644 index 00000000000..cdd315abfb5 --- /dev/null +++ b/sys/powerpc/pseries/plpar_iommu.h @@ -0,0 +1,43 @@ +/*- + * Copyright (c) 2013, Nathan Whitehorn + * 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 unmodified, 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + * + * $FreeBSD$ + */ + +#ifndef _PSERIES_PHYP_IOMMU_H_ +#define _PSERIES_PHYP_IOMMU_H_ + +#include +#include + +int phyp_iommu_set_dma_tag(device_t dev, device_t child, bus_dma_tag_t tag); +int phyp_iommu_map(device_t dev, bus_dma_segment_t *segs, int *nsegs, + bus_addr_t min, bus_addr_t max, bus_size_t alignment, bus_addr_t boundary, + void *cookie); +int phyp_iommu_unmap(device_t dev, bus_dma_segment_t *segs, int nsegs, + void *cookie); + +#endif + diff --git a/sys/powerpc/pseries/rtas_dev.c b/sys/powerpc/pseries/rtas_dev.c new file mode 100644 index 00000000000..36cb1999bfc --- /dev/null +++ b/sys/powerpc/pseries/rtas_dev.c @@ -0,0 +1,173 @@ +/*- + * Copyright (c) 2011 Nathan Whitehorn + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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 +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include "clock_if.h" + +static int rtasdev_probe(device_t); +static int rtasdev_attach(device_t); +/* clock interface */ +static int rtas_gettime(device_t dev, struct timespec *ts); +static int rtas_settime(device_t dev, struct timespec *ts); + +static void rtas_shutdown(void *arg, int howto); + +static device_method_t rtasdev_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, rtasdev_probe), + DEVMETHOD(device_attach, rtasdev_attach), + + /* clock interface */ + DEVMETHOD(clock_gettime, rtas_gettime), + DEVMETHOD(clock_settime, rtas_settime), + + { 0, 0 }, +}; + +static driver_t rtasdev_driver = { + "rtas", + rtasdev_methods, + 0 +}; + +static devclass_t rtasdev_devclass; + +DRIVER_MODULE(rtasdev, nexus, rtasdev_driver, rtasdev_devclass, 0, 0); + +static int +rtasdev_probe(device_t dev) +{ + const char *name = ofw_bus_get_name(dev); + + if (strcmp(name, "rtas") != 0) + return (ENXIO); + if (!rtas_exists()) + return (ENXIO); + + device_set_desc(dev, "Run-Time Abstraction Services"); + return (0); +} + +static int +rtasdev_attach(device_t dev) +{ + if (rtas_token_lookup("get-time-of-day") != -1) + clock_register(dev, 2000); + + EVENTHANDLER_REGISTER(shutdown_final, rtas_shutdown, NULL, + SHUTDOWN_PRI_LAST); + + return (0); +} + +static int +rtas_gettime(device_t dev, struct timespec *ts) { + struct clocktime ct; + cell_t tod[8]; + cell_t token; + int error; + + token = rtas_token_lookup("get-time-of-day"); + if (token == -1) + return (ENXIO); + error = rtas_call_method(token, 0, 8, &tod[0], &tod[1], &tod[2], + &tod[3], &tod[4], &tod[5], &tod[6], &tod[7]); + if (error < 0) + return (ENXIO); + if (tod[0] != 0) + return ((tod[0] == -1) ? ENXIO : EAGAIN); + + ct.year = tod[1]; + ct.mon = tod[2]; + ct.day = tod[3]; + ct.hour = tod[4]; + ct.min = tod[5]; + ct.sec = tod[6]; + ct.nsec = tod[7]; + + return (clock_ct_to_ts(&ct, ts)); +} + +static int +rtas_settime(device_t dev, struct timespec *ts) +{ + struct clocktime ct; + cell_t token, status; + int error; + + token = rtas_token_lookup("set-time-of-day"); + if (token == -1) + return (ENXIO); + + clock_ts_to_ct(ts, &ct); + error = rtas_call_method(token, 7, 1, ct.year, ct.mon, ct.day, ct.hour, + ct.min, ct.sec, ct.nsec, &status); + if (error < 0) + return (ENXIO); + if (status != 0) + return (((int)status < 0) ? ENXIO : EAGAIN); + + return (0); +} + +static void +rtas_shutdown(void *arg, int howto) +{ + cell_t token, status; + + if (howto & RB_HALT) { + token = rtas_token_lookup("power-off"); + if (token == -1) + return; + + rtas_call_method(token, 2, 1, 0, 0, &status); + } else { + token = rtas_token_lookup("system-reboot"); + if (token == -1) + return; + + rtas_call_method(token, 0, 1, &status); + } +} + diff --git a/sys/powerpc/pseries/rtas_pci.c b/sys/powerpc/pseries/rtas_pci.c new file mode 100644 index 00000000000..f3e9a29a655 --- /dev/null +++ b/sys/powerpc/pseries/rtas_pci.c @@ -0,0 +1,236 @@ +/*- + * Copyright (c) 2011 Nathan Whitehorn + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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 +__FBSDID("$FreeBSD$"); +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include +#include + +#include "pcib_if.h" +#include "iommu_if.h" + +/* + * Device interface. + */ +static int rtaspci_probe(device_t); +static int rtaspci_attach(device_t); + +/* + * pcib interface. + */ +static u_int32_t rtaspci_read_config(device_t, u_int, u_int, u_int, + u_int, int); +static void rtaspci_write_config(device_t, u_int, u_int, u_int, + u_int, u_int32_t, int); + +/* + * IOMMU LPAR interface + */ +static bus_dma_tag_t rtaspci_get_dma_tag(device_t dev, device_t child); + +/* + * Driver methods. + */ +static device_method_t rtaspci_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, rtaspci_probe), + DEVMETHOD(device_attach, rtaspci_attach), + + /* pcib interface */ + DEVMETHOD(pcib_read_config, rtaspci_read_config), + DEVMETHOD(pcib_write_config, rtaspci_write_config), + + /* IOMMU functions */ + DEVMETHOD(bus_get_dma_tag, rtaspci_get_dma_tag), +#ifdef __powerpc64__ + DEVMETHOD(iommu_map, phyp_iommu_map), + DEVMETHOD(iommu_unmap, phyp_iommu_unmap), +#endif + + DEVMETHOD_END +}; + +struct rtaspci_softc { + struct ofw_pci_softc pci_sc; + bus_dma_tag_t dma_tag; + + cell_t read_pci_config, write_pci_config; + cell_t ex_read_pci_config, ex_write_pci_config; + int sc_extended_config; +}; + +static devclass_t rtaspci_devclass; +DEFINE_CLASS_1(pcib, rtaspci_driver, rtaspci_methods, + sizeof(struct rtaspci_softc), ofw_pci_driver); +DRIVER_MODULE(rtaspci, nexus, rtaspci_driver, rtaspci_devclass, 0, 0); + +static int +rtaspci_probe(device_t dev) +{ + const char *type; + + if (!rtas_exists()) + return (ENXIO); + + type = ofw_bus_get_type(dev); + + if (OF_getproplen(ofw_bus_get_node(dev), "used-by-rtas") < 0) + return (ENXIO); + if (type == NULL || strcmp(type, "pci") != 0) + return (ENXIO); + + device_set_desc(dev, "RTAS Host-PCI bridge"); + return (BUS_PROBE_GENERIC); +} + +static int +rtaspci_attach(device_t dev) +{ + struct rtaspci_softc *sc; + + sc = device_get_softc(dev); + + sc->read_pci_config = rtas_token_lookup("read-pci-config"); + sc->write_pci_config = rtas_token_lookup("write-pci-config"); + sc->ex_read_pci_config = rtas_token_lookup("ibm,read-pci-config"); + sc->ex_write_pci_config = rtas_token_lookup("ibm,write-pci-config"); + + sc->sc_extended_config = 0; + OF_getprop(ofw_bus_get_node(dev), "ibm,pci-config-space-type", + &sc->sc_extended_config, sizeof(sc->sc_extended_config)); + + bus_dma_tag_create(bus_get_dma_tag(dev), + 1, 0, BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR, + NULL, NULL, BUS_SPACE_MAXSIZE, BUS_SPACE_UNRESTRICTED, + BUS_SPACE_MAXSIZE, 0, NULL, NULL, &sc->dma_tag); +#ifdef __powerpc64__ + if (!(mfmsr() & PSL_HV)) + phyp_iommu_set_dma_tag(dev, dev, sc->dma_tag); +#endif + + return (ofw_pci_attach(dev)); +} + +static uint32_t +rtaspci_read_config(device_t dev, u_int bus, u_int slot, u_int func, u_int reg, + int width) +{ + struct rtaspci_softc *sc; + uint32_t retval = 0xffffffff; + uint32_t config_addr; + int error, pcierror; + + sc = device_get_softc(dev); + + config_addr = ((bus & 0xff) << 16) | ((slot & 0x1f) << 11) | + ((func & 0x7) << 8) | (reg & 0xff); + if (sc->sc_extended_config) + config_addr |= (reg & 0xf00) << 16; + + if (sc->ex_read_pci_config != -1) + error = rtas_call_method(sc->ex_read_pci_config, 4, 2, + config_addr, sc->pci_sc.sc_pcir.phys_hi, + sc->pci_sc.sc_pcir.phys_mid, width, &pcierror, &retval); + else + error = rtas_call_method(sc->read_pci_config, 2, 2, + config_addr, width, &pcierror, &retval); + + /* Sign-extend output */ + switch (width) { + case 1: + retval = (int32_t)(int8_t)(retval); + break; + case 2: + retval = (int32_t)(int16_t)(retval); + break; + } + + if (error < 0 || pcierror != 0) + retval = 0xffffffff; + + return (retval); +} + +static void +rtaspci_write_config(device_t dev, u_int bus, u_int slot, u_int func, + u_int reg, uint32_t val, int width) +{ + struct rtaspci_softc *sc; + uint32_t config_addr; + int pcierror; + + sc = device_get_softc(dev); + + config_addr = ((bus & 0xff) << 16) | ((slot & 0x1f) << 11) | + ((func & 0x7) << 8) | (reg & 0xff); + if (sc->sc_extended_config) + config_addr |= (reg & 0xf00) << 16; + + if (sc->ex_write_pci_config != -1) + rtas_call_method(sc->ex_write_pci_config, 5, 1, config_addr, + sc->pci_sc.sc_pcir.phys_hi, sc->pci_sc.sc_pcir.phys_mid, + width, val, &pcierror); + else + rtas_call_method(sc->write_pci_config, 3, 1, config_addr, + width, val, &pcierror); +} + +static bus_dma_tag_t +rtaspci_get_dma_tag(device_t dev, device_t child) +{ + struct rtaspci_softc *sc; + + sc = device_get_softc(dev); + return (sc->dma_tag); +} + diff --git a/sys/powerpc/pseries/vdevice.c b/sys/powerpc/pseries/vdevice.c new file mode 100644 index 00000000000..45511062cd3 --- /dev/null +++ b/sys/powerpc/pseries/vdevice.c @@ -0,0 +1,202 @@ +/*- + * Copyright (c) 2011 Nathan Whitehorn + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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 +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +static int vdevice_probe(device_t); +static int vdevice_attach(device_t); +static const struct ofw_bus_devinfo *vdevice_get_devinfo(device_t dev, + device_t child); +static int vdevice_print_child(device_t dev, device_t child); +static struct resource_list *vdevice_get_resource_list (device_t, device_t); + +/* + * VDevice devinfo + */ +struct vdevice_devinfo { + struct ofw_bus_devinfo mdi_obdinfo; + struct resource_list mdi_resources; +}; + +static device_method_t vdevice_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, vdevice_probe), + DEVMETHOD(device_attach, vdevice_attach), + + /* Bus interface */ + DEVMETHOD(bus_add_child, bus_generic_add_child), + DEVMETHOD(bus_child_pnpinfo_str, ofw_bus_gen_child_pnpinfo_str), + DEVMETHOD(bus_print_child, vdevice_print_child), + DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), + DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), + DEVMETHOD(bus_alloc_resource, bus_generic_rl_alloc_resource), + DEVMETHOD(bus_release_resource, bus_generic_rl_release_resource), + DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), + DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), + DEVMETHOD(bus_get_resource_list, vdevice_get_resource_list), + + /* ofw_bus interface */ + DEVMETHOD(ofw_bus_get_devinfo, vdevice_get_devinfo), + DEVMETHOD(ofw_bus_get_compat, ofw_bus_gen_get_compat), + DEVMETHOD(ofw_bus_get_model, ofw_bus_gen_get_model), + DEVMETHOD(ofw_bus_get_name, ofw_bus_gen_get_name), + DEVMETHOD(ofw_bus_get_node, ofw_bus_gen_get_node), + DEVMETHOD(ofw_bus_get_type, ofw_bus_gen_get_type), + + DEVMETHOD_END +}; + +static driver_t vdevice_driver = { + "vdevice", + vdevice_methods, + 0 +}; + +static devclass_t vdevice_devclass; + +DRIVER_MODULE(vdevice, nexus, vdevice_driver, vdevice_devclass, 0, 0); + +static int +vdevice_probe(device_t dev) +{ + const char *name; + + name = ofw_bus_get_name(dev); + + if (name == NULL || strcmp(name, "vdevice") != 0) + return (ENXIO); + + if (!ofw_bus_is_compatible(dev, "IBM,vdevice")) + return (ENXIO); + + device_set_desc(dev, "POWER Hypervisor Virtual Device Root"); + + return (0); +} + +static int +vdevice_attach(device_t dev) +{ + phandle_t root, child; + device_t cdev; + int icells, i, nintr, *intr; + phandle_t iparent; + struct vdevice_devinfo *dinfo; + + root = ofw_bus_get_node(dev); + + for (child = OF_child(root); child != 0; child = OF_peer(child)) { + dinfo = malloc(sizeof(*dinfo), M_DEVBUF, M_WAITOK | M_ZERO); + + if (ofw_bus_gen_setup_devinfo(&dinfo->mdi_obdinfo, + child) != 0) { + free(dinfo, M_DEVBUF); + continue; + } + resource_list_init(&dinfo->mdi_resources); + + if (OF_searchprop(child, "#interrupt-cells", &icells, + sizeof(icells)) <= 0) + icells = 2; + if (OF_getprop(child, "interrupt-parent", &iparent, + sizeof(iparent)) <= 0) + iparent = -1; + nintr = OF_getprop_alloc(child, "interrupts", sizeof(*intr), + (void **)&intr); + if (nintr > 0) { + for (i = 0; i < nintr; i += icells) { + u_int irq = intr[i]; + if (iparent != -1) + irq = MAP_IRQ(iparent, intr[i]); + + resource_list_add(&dinfo->mdi_resources, + SYS_RES_IRQ, i, irq, irq, i); + } + } + + cdev = device_add_child(dev, NULL, -1); + if (cdev == NULL) { + device_printf(dev, "<%s>: device_add_child failed\n", + dinfo->mdi_obdinfo.obd_name); + ofw_bus_gen_destroy_devinfo(&dinfo->mdi_obdinfo); + free(dinfo, M_DEVBUF); + continue; + } + device_set_ivars(cdev, dinfo); + } + + return (bus_generic_attach(dev)); +} + +static const struct ofw_bus_devinfo * +vdevice_get_devinfo(device_t dev, device_t child) +{ + return (device_get_ivars(child)); +} + +static int +vdevice_print_child(device_t dev, device_t child) +{ + struct vdevice_devinfo *dinfo; + struct resource_list *rl; + int retval = 0; + + dinfo = device_get_ivars(child); + rl = &dinfo->mdi_resources; + + retval += bus_print_child_header(dev, child); + + retval += resource_list_print_type(rl, "irq", SYS_RES_IRQ, "%ld"); + + retval += bus_print_child_footer(dev, child); + + return (retval); +} + +static struct resource_list * +vdevice_get_resource_list (device_t dev, device_t child) +{ + struct vdevice_devinfo *dinfo; + + dinfo = device_get_ivars(child); + return (&dinfo->mdi_resources); +} + diff --git a/sys/powerpc/pseries/xics.c b/sys/powerpc/pseries/xics.c new file mode 100644 index 00000000000..7bfbb3d5df7 --- /dev/null +++ b/sys/powerpc/pseries/xics.c @@ -0,0 +1,323 @@ +/*- + * Copyright 2011 Nathan Whitehorn + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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 +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include "phyp-hvcall.h" +#include "pic_if.h" + +#define XICP_PRIORITY 5 /* Random non-zero number */ +#define XICP_IPI 2 +#define MAX_XICP_IRQS (1<<24) /* 24-bit XIRR field */ + +static int xicp_probe(device_t); +static int xicp_attach(device_t); +static int xics_probe(device_t); +static int xics_attach(device_t); + +static void xicp_bind(device_t dev, u_int irq, cpuset_t cpumask); +static void xicp_dispatch(device_t, struct trapframe *); +static void xicp_enable(device_t, u_int, u_int); +static void xicp_eoi(device_t, u_int); +static void xicp_ipi(device_t, u_int); +static void xicp_mask(device_t, u_int); +static void xicp_unmask(device_t, u_int); + +static device_method_t xicp_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, xicp_probe), + DEVMETHOD(device_attach, xicp_attach), + + /* PIC interface */ + DEVMETHOD(pic_bind, xicp_bind), + DEVMETHOD(pic_dispatch, xicp_dispatch), + DEVMETHOD(pic_enable, xicp_enable), + DEVMETHOD(pic_eoi, xicp_eoi), + DEVMETHOD(pic_ipi, xicp_ipi), + DEVMETHOD(pic_mask, xicp_mask), + DEVMETHOD(pic_unmask, xicp_unmask), + + { 0, 0 }, +}; + +static device_method_t xics_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, xics_probe), + DEVMETHOD(device_attach, xics_attach), + + { 0, 0 }, +}; + +struct xicp_softc { + struct mtx sc_mtx; + + int ibm_int_on; + int ibm_int_off; + int ibm_get_xive; + int ibm_set_xive; + + /* XXX: inefficient -- hash table? tree? */ + struct { + int irq; + int vector; + } intvecs[256]; + int nintvecs; +}; + +static driver_t xicp_driver = { + "xicp", + xicp_methods, + sizeof(struct xicp_softc) +}; + +static driver_t xics_driver = { + "xics", + xics_methods, + 0 +}; + +static devclass_t xicp_devclass; +static devclass_t xics_devclass; + +EARLY_DRIVER_MODULE(xicp, nexus, xicp_driver, xicp_devclass, 0, 0, + BUS_PASS_INTERRUPT-1); +EARLY_DRIVER_MODULE(xics, nexus, xics_driver, xics_devclass, 0, 0, + BUS_PASS_INTERRUPT); + +static int +xicp_probe(device_t dev) +{ + if (ofw_bus_get_name(dev) == NULL || strcmp(ofw_bus_get_name(dev), + "interrupt-controller") != 0) + return (ENXIO); + + if (!ofw_bus_is_compatible(dev, "ibm,ppc-xicp")) + return (ENXIO); + + device_set_desc(dev, "PAPR virtual interrupt controller"); + return (BUS_PROBE_GENERIC); +} + +static int +xics_probe(device_t dev) +{ + if (ofw_bus_get_name(dev) == NULL || strcmp(ofw_bus_get_name(dev), + "interrupt-controller") != 0) + return (ENXIO); + + if (!ofw_bus_is_compatible(dev, "ibm,ppc-xics")) + return (ENXIO); + + device_set_desc(dev, "PAPR virtual interrupt source"); + return (BUS_PROBE_GENERIC); +} + +static int +xicp_attach(device_t dev) +{ + struct xicp_softc *sc = device_get_softc(dev); + phandle_t phandle = ofw_bus_get_node(dev); + + mtx_init(&sc->sc_mtx, "XICP", NULL, MTX_DEF); + sc->nintvecs = 0; + + sc->ibm_int_on = rtas_token_lookup("ibm,int-on"); + sc->ibm_int_off = rtas_token_lookup("ibm,int-off"); + sc->ibm_set_xive = rtas_token_lookup("ibm,set-xive"); + sc->ibm_get_xive = rtas_token_lookup("ibm,get-xive"); + + if (OF_getproplen(phandle, "ibm,phandle") > 0) + OF_getprop(phandle, "ibm,phandle", &phandle, sizeof(phandle)); + + powerpc_register_pic(dev, phandle, MAX_XICP_IRQS, + 1 /* Number of IPIs */, FALSE); + root_pic = dev; + + return (0); +} + +static int +xics_attach(device_t dev) +{ + phandle_t phandle = ofw_bus_get_node(dev); + + if (OF_getproplen(phandle, "ibm,phandle") > 0) + OF_getprop(phandle, "ibm,phandle", &phandle, sizeof(phandle)); + + /* The XICP (root PIC) will handle all our interrupts */ + powerpc_register_pic(root_pic, phandle, MAX_XICP_IRQS, + 1 /* Number of IPIs */, FALSE); + + return (0); +} + +/* + * PIC I/F methods. + */ + +static void +xicp_bind(device_t dev, u_int irq, cpuset_t cpumask) +{ + struct xicp_softc *sc = device_get_softc(dev); + cell_t status, cpu; + + /* + * This doesn't appear to actually support affinity groups, so just + * use the first CPU. + */ + CPU_FOREACH(cpu) + if (CPU_ISSET(cpu, &cpumask)) break; + + rtas_call_method(sc->ibm_set_xive, 3, 1, irq, cpu, XICP_PRIORITY, + &status); +} + +static void +xicp_dispatch(device_t dev, struct trapframe *tf) +{ + struct xicp_softc *sc; + uint64_t xirr, junk; + int i; + + sc = device_get_softc(dev); + for (;;) { + /* Return value in R4, use the PFT call */ + phyp_pft_hcall(H_XIRR, 0, 0, 0, 0, &xirr, &junk, &junk); + xirr &= 0x00ffffff; + + if (xirr == 0) { /* No more pending interrupts? */ + phyp_hcall(H_CPPR, (uint64_t)0xff); + break; + } + if (xirr == XICP_IPI) { /* Magic number for IPIs */ + xirr = MAX_XICP_IRQS; /* Map to FreeBSD magic */ + phyp_hcall(H_IPI, (uint64_t)(PCPU_GET(cpuid)), + 0xff); /* Clear IPI */ + } + + /* XXX: super inefficient */ + for (i = 0; i < sc->nintvecs; i++) { + if (sc->intvecs[i].irq == xirr) + break; + } + + KASSERT(i < sc->nintvecs, ("Unmapped XIRR")); + powerpc_dispatch_intr(sc->intvecs[i].vector, tf); + } +} + +static void +xicp_enable(device_t dev, u_int irq, u_int vector) +{ + struct xicp_softc *sc; + cell_t status, cpu; + + sc = device_get_softc(dev); + + KASSERT(sc->nintvecs + 1 < sizeof(sc->intvecs)/sizeof(sc->intvecs[0]), + ("Too many XICP interrupts")); + + mtx_lock(&sc->sc_mtx); + sc->intvecs[sc->nintvecs].irq = irq; + sc->intvecs[sc->nintvecs].vector = vector; + mb(); + sc->nintvecs++; + mtx_unlock(&sc->sc_mtx); + + /* IPIs are also enabled */ + if (irq == MAX_XICP_IRQS) + return; + + /* Bind to this CPU to start: distrib. ID is last entry in gserver# */ + cpu = PCPU_GET(cpuid); + rtas_call_method(sc->ibm_set_xive, 3, 1, irq, cpu, XICP_PRIORITY, + &status); + xicp_unmask(dev, irq); +} + +static void +xicp_eoi(device_t dev, u_int irq) +{ + uint64_t xirr; + + if (irq == MAX_XICP_IRQS) /* Remap IPI interrupt to internal value */ + irq = XICP_IPI; + xirr = irq | (XICP_PRIORITY << 24); + + phyp_hcall(H_EOI, xirr); +} + +static void +xicp_ipi(device_t dev, u_int cpu) +{ + + phyp_hcall(H_IPI, (uint64_t)cpu, XICP_PRIORITY); +} + +static void +xicp_mask(device_t dev, u_int irq) +{ + struct xicp_softc *sc = device_get_softc(dev); + cell_t status; + + if (irq == MAX_XICP_IRQS) + return; + + rtas_call_method(sc->ibm_int_off, 1, 1, irq, &status); +} + +static void +xicp_unmask(device_t dev, u_int irq) +{ + struct xicp_softc *sc = device_get_softc(dev); + cell_t status; + + if (irq == MAX_XICP_IRQS) + return; + + rtas_call_method(sc->ibm_int_on, 1, 1, irq, &status); +} + diff --git a/sys/rpc/clnt_dg.c b/sys/rpc/clnt_dg.c index a658de9eba5..4c1fe8c3ef2 100644 --- a/sys/rpc/clnt_dg.c +++ b/sys/rpc/clnt_dg.c @@ -682,6 +682,7 @@ get_reply: next_sendtime += retransmit_time; goto send_again; } + cu->cu_sent += CWNDSCALE; TAILQ_INSERT_TAIL(&cs->cs_pending, cr, cr_link); } @@ -733,6 +734,7 @@ got_reply: */ XDR_DESTROY(&xdrs); mtx_lock(&cs->cs_lock); + cu->cu_sent += CWNDSCALE; TAILQ_INSERT_TAIL(&cs->cs_pending, cr, cr_link); cr->cr_mrep = NULL; diff --git a/sys/security/audit/audit.h b/sys/security/audit/audit.h index dd55875be5b..559d571cbc4 100644 --- a/sys/security/audit/audit.h +++ b/sys/security/audit/audit.h @@ -114,7 +114,7 @@ void audit_arg_auditon(union auditon_udata *udata); void audit_arg_file(struct proc *p, struct file *fp); void audit_arg_argv(char *argv, int argc, int length); void audit_arg_envv(char *envv, int envc, int length); -void audit_arg_rights(cap_rights_t rights); +void audit_arg_rights(cap_rights_t *rightsp); void audit_arg_fcntl_rights(uint32_t fcntlrights); void audit_sysclose(struct thread *td, int fd); void audit_cred_copy(struct ucred *src, struct ucred *dest); diff --git a/sys/security/audit/audit_arg.c b/sys/security/audit/audit_arg.c index 4927be0d134..2e86842ba43 100644 --- a/sys/security/audit/audit_arg.c +++ b/sys/security/audit/audit_arg.c @@ -861,7 +861,7 @@ audit_arg_envv(char *envv, int envc, int length) } void -audit_arg_rights(cap_rights_t rights) +audit_arg_rights(cap_rights_t *rightsp) { struct kaudit_record *ar; @@ -869,7 +869,7 @@ audit_arg_rights(cap_rights_t rights) if (ar == NULL) return; - ar->k_ar.ar_arg_rights = rights; + ar->k_ar.ar_arg_rights = *rightsp; ARG_SET_VALID(ar, ARG_RIGHTS); } diff --git a/sys/security/audit/audit_bsm.c b/sys/security/audit/audit_bsm.c index 03b3c23e5c4..9f29eceb3ad 100644 --- a/sys/security/audit/audit_bsm.c +++ b/sys/security/audit/audit_bsm.c @@ -1611,14 +1611,13 @@ kaudit_to_bsm(struct kaudit_record *kar, struct au_record **pau) } break; - case AUE_CAP_NEW: case AUE_CAP_RIGHTS_LIMIT: /* * XXXRW/XXXJA: Would be nice to audit socket/etc information. */ FD_VNODE1_TOKENS; if (ARG_IS_VALID(kar, ARG_RIGHTS)) { - tok = au_to_arg64(2, "rights", ar->ar_arg_rights); + tok = au_to_rights(&ar->ar_arg_rights); kau_write(rec, tok); } break; diff --git a/sys/security/audit/audit_bsm_klib.c b/sys/security/audit/audit_bsm_klib.c index 5f5d58bdff2..d06c7705eaf 100644 --- a/sys/security/audit/audit_bsm_klib.c +++ b/sys/security/audit/audit_bsm_klib.c @@ -496,7 +496,7 @@ audit_canon_path(struct thread *td, int dirfd, char *path, char *cpath) vhold(cvnp); } else { /* XXX: fgetvp() that vhold()s vnode instead of vref()ing it would be better */ - error = fgetvp(td, dirfd, 0, &cvnp); + error = fgetvp(td, dirfd, NULL, &cvnp); if (error) { cpath[0] = '\0'; if (rvnp != NULL) diff --git a/sys/security/audit/audit_pipe.c b/sys/security/audit/audit_pipe.c index dc0df3e8ae4..b2919753e07 100644 --- a/sys/security/audit/audit_pipe.c +++ b/sys/security/audit/audit_pipe.c @@ -672,14 +672,9 @@ audit_pipe_clone(void *arg, struct ucred *cred, char *name, int namelen, return; i = clone_create(&audit_pipe_clones, &audit_pipe_cdevsw, &u, dev, 0); - if (i) { - *dev = make_dev(&audit_pipe_cdevsw, u, UID_ROOT, - GID_WHEEL, 0600, "%s%d", AUDIT_PIPE_NAME, u); - if (*dev != NULL) { - dev_ref(*dev); - (*dev)->si_flags |= SI_CHEAPCLONE; - } - } + if (i) + *dev = make_dev_credf(MAKEDEV_REF, &audit_pipe_cdevsw, u, cred, + UID_ROOT, GID_WHEEL, 0600, "%s%d", AUDIT_PIPE_NAME, u); } /* diff --git a/sys/security/audit/audit_private.h b/sys/security/audit/audit_private.h index e23ba087ae1..b5c373ae53e 100644 --- a/sys/security/audit/audit_private.h +++ b/sys/security/audit/audit_private.h @@ -41,6 +41,7 @@ #error "no user-serviceable parts inside" #endif +#include #include #include #include diff --git a/sys/security/audit/bsm_token.c b/sys/security/audit/bsm_token.c index 6d0d67fcff1..763d59721eb 100644 --- a/sys/security/audit/bsm_token.c +++ b/sys/security/audit/bsm_token.c @@ -835,6 +835,22 @@ au_to_process_ex(au_id_t auid, uid_t euid, gid_t egid, uid_t ruid, tid)); } +token_t * +au_to_rights(cap_rights_t *rightsp) +{ + token_t *t; + u_char *dptr; + int i; + + GET_TOKEN_AREA(t, dptr, sizeof(u_char) + sizeof(*rightsp)); + + ADD_U_CHAR(dptr, AUT_RIGHTS); + for (i = 0; i < nitems(rightsp->cr_rights); i++) + ADD_U_INT64(dptr, rightsp->cr_rights[i]); + + return (t); +} + /* * token ID 1 byte * error status 1 byte diff --git a/sys/security/mac/mac_framework.h b/sys/security/mac/mac_framework.h index 92aedea4ef7..77cb8bcb60c 100644 --- a/sys/security/mac/mac_framework.h +++ b/sys/security/mac/mac_framework.h @@ -243,6 +243,8 @@ int mac_posixshm_check_mmap(struct ucred *cred, struct shmfd *shmfd, int prot, int flags); int mac_posixshm_check_open(struct ucred *cred, struct shmfd *shmfd, accmode_t accmode); +int mac_posixshm_check_read(struct ucred *active_cred, + struct ucred *file_cred, struct shmfd *shmfd); int mac_posixshm_check_setmode(struct ucred *cred, struct shmfd *shmfd, mode_t mode); int mac_posixshm_check_setowner(struct ucred *cred, struct shmfd *shmfd, @@ -252,6 +254,8 @@ int mac_posixshm_check_stat(struct ucred *active_cred, int mac_posixshm_check_truncate(struct ucred *active_cred, struct ucred *file_cred, struct shmfd *shmfd); int mac_posixshm_check_unlink(struct ucred *cred, struct shmfd *shmfd); +int mac_posixshm_check_write(struct ucred *active_cred, + struct ucred *file_cred, struct shmfd *shmfd); void mac_posixshm_create(struct ucred *cred, struct shmfd *shmfd); void mac_posixshm_destroy(struct shmfd *); void mac_posixshm_init(struct shmfd *); diff --git a/sys/security/mac/mac_policy.h b/sys/security/mac/mac_policy.h index 090dc405810..dadadb5828b 100644 --- a/sys/security/mac/mac_policy.h +++ b/sys/security/mac/mac_policy.h @@ -363,6 +363,9 @@ typedef int (*mpo_posixshm_check_mmap_t)(struct ucred *cred, typedef int (*mpo_posixshm_check_open_t)(struct ucred *cred, struct shmfd *shmfd, struct label *shmlabel, accmode_t accmode); +typedef int (*mpo_posixshm_check_read_t)(struct ucred *active_cred, + struct ucred *file_cred, struct shmfd *shmfd, + struct label *shmlabel); typedef int (*mpo_posixshm_check_setmode_t)(struct ucred *cred, struct shmfd *shmfd, struct label *shmlabel, mode_t mode); @@ -377,6 +380,9 @@ typedef int (*mpo_posixshm_check_truncate_t)(struct ucred *active_cred, struct label *shmlabel); typedef int (*mpo_posixshm_check_unlink_t)(struct ucred *cred, struct shmfd *shmfd, struct label *shmlabel); +typedef int (*mpo_posixshm_check_write_t)(struct ucred *active_cred, + struct ucred *file_cred, struct shmfd *shmfd, + struct label *shmlabel); typedef void (*mpo_posixshm_create_t)(struct ucred *cred, struct shmfd *shmfd, struct label *shmlabel); typedef void (*mpo_posixshm_destroy_label_t)(struct label *label); @@ -818,11 +824,13 @@ struct mac_policy_ops { mpo_posixshm_check_create_t mpo_posixshm_check_create; mpo_posixshm_check_mmap_t mpo_posixshm_check_mmap; mpo_posixshm_check_open_t mpo_posixshm_check_open; + mpo_posixshm_check_read_t mpo_posixshm_check_read; mpo_posixshm_check_setmode_t mpo_posixshm_check_setmode; mpo_posixshm_check_setowner_t mpo_posixshm_check_setowner; mpo_posixshm_check_stat_t mpo_posixshm_check_stat; mpo_posixshm_check_truncate_t mpo_posixshm_check_truncate; mpo_posixshm_check_unlink_t mpo_posixshm_check_unlink; + mpo_posixshm_check_write_t mpo_posixshm_check_write; mpo_posixshm_create_t mpo_posixshm_create; mpo_posixshm_destroy_label_t mpo_posixshm_destroy_label; mpo_posixshm_init_label_t mpo_posixshm_init_label; diff --git a/sys/security/mac/mac_posix_shm.c b/sys/security/mac/mac_posix_shm.c index d5d15fc4248..1202d46303a 100644 --- a/sys/security/mac/mac_posix_shm.c +++ b/sys/security/mac/mac_posix_shm.c @@ -228,3 +228,37 @@ mac_posixshm_check_setowner(struct ucred *cred, struct shmfd *shmfd, uid_t uid, return (error); } + +MAC_CHECK_PROBE_DEFINE3(posixshm_check_read, "struct ucred *", + "struct ucred *", "struct shmfd *"); + +int +mac_posixshm_check_read(struct ucred *active_cred, struct ucred *file_cred, + struct shmfd *shmfd) +{ + int error; + + MAC_POLICY_CHECK_NOSLEEP(posixshm_check_read, active_cred, + file_cred, shmfd, shmfd->shm_label); + MAC_CHECK_PROBE3(posixshm_check_read, error, active_cred, + file_cred, shmfd); + + return (error); +} + +MAC_CHECK_PROBE_DEFINE3(posixshm_check_write, "struct ucred *", + "struct ucred *", "struct shmfd *"); + +int +mac_posixshm_check_write(struct ucred *active_cred, struct ucred *file_cred, + struct shmfd *shmfd) +{ + int error; + + MAC_POLICY_CHECK_NOSLEEP(posixshm_check_write, active_cred, + file_cred, shmfd, shmfd->shm_label); + MAC_CHECK_PROBE3(posixshm_check_write, error, active_cred, + file_cred, shmfd); + + return (error); +} diff --git a/sys/security/mac/mac_syscalls.c b/sys/security/mac/mac_syscalls.c index ff55ec924d3..64055869767 100644 --- a/sys/security/mac/mac_syscalls.c +++ b/sys/security/mac/mac_syscalls.c @@ -229,6 +229,7 @@ sys___mac_get_fd(struct thread *td, struct __mac_get_fd_args *uap) struct vnode *vp; struct pipe *pipe; struct socket *so; + cap_rights_t rights; short label_type; int error; @@ -248,7 +249,7 @@ sys___mac_get_fd(struct thread *td, struct __mac_get_fd_args *uap) } buffer = malloc(mac.m_buflen, M_MACTEMP, M_WAITOK | M_ZERO); - error = fget(td, uap->fd, CAP_MAC_GET, &fp); + error = fget(td, uap->fd, cap_rights_init(&rights, CAP_MAC_GET), &fp); if (error) goto out; @@ -425,6 +426,7 @@ sys___mac_set_fd(struct thread *td, struct __mac_set_fd_args *uap) struct mount *mp; struct vnode *vp; struct mac mac; + cap_rights_t rights; char *buffer; int error; @@ -443,7 +445,7 @@ sys___mac_set_fd(struct thread *td, struct __mac_set_fd_args *uap) return (error); } - error = fget(td, uap->fd, CAP_MAC_SET, &fp); + error = fget(td, uap->fd, cap_rights_init(&rights, CAP_MAC_SET), &fp); if (error) goto out; diff --git a/sys/security/mac_biba/mac_biba.c b/sys/security/mac_biba/mac_biba.c index aa37fc79fa3..4216fea4147 100644 --- a/sys/security/mac_biba/mac_biba.c +++ b/sys/security/mac_biba/mac_biba.c @@ -1758,6 +1758,24 @@ biba_posixshm_check_open(struct ucred *cred, struct shmfd *shmfd, return (0); } +static int +biba_posixshm_check_read(struct ucred *active_cred, struct ucred *file_cred, + struct shmfd *vp, struct label *shmlabel) +{ + struct mac_biba *subj, *obj; + + if (!biba_enabled || !revocation_enabled) + return (0); + + subj = SLOT(active_cred->cr_label); + obj = SLOT(shmlabel); + + if (!biba_dominate_effective(obj, subj)) + return (EACCES); + + return (0); +} + static int biba_posixshm_check_setmode(struct ucred *cred, struct shmfd *shmfd, struct label *shmlabel, mode_t mode) @@ -1848,6 +1866,24 @@ biba_posixshm_check_unlink(struct ucred *cred, struct shmfd *shmfd, return (0); } +static int +biba_posixshm_check_write(struct ucred *active_cred, struct ucred *file_cred, + struct shmfd *vp, struct label *shmlabel) +{ + struct mac_biba *subj, *obj; + + if (!biba_enabled || !revocation_enabled) + return (0); + + subj = SLOT(active_cred->cr_label); + obj = SLOT(shmlabel); + + if (!biba_dominate_effective(obj, subj)) + return (EACCES); + + return (0); +} + static void biba_posixshm_create(struct ucred *cred, struct shmfd *shmfd, struct label *shmlabel) @@ -3657,11 +3693,13 @@ static struct mac_policy_ops mac_biba_ops = .mpo_posixshm_check_mmap = biba_posixshm_check_mmap, .mpo_posixshm_check_open = biba_posixshm_check_open, + .mpo_posixshm_check_read = biba_posixshm_check_read, .mpo_posixshm_check_setmode = biba_posixshm_check_setmode, .mpo_posixshm_check_setowner = biba_posixshm_check_setowner, .mpo_posixshm_check_stat = biba_posixshm_check_stat, .mpo_posixshm_check_truncate = biba_posixshm_check_truncate, .mpo_posixshm_check_unlink = biba_posixshm_check_unlink, + .mpo_posixshm_check_write = biba_posixshm_check_write, .mpo_posixshm_create = biba_posixshm_create, .mpo_posixshm_destroy_label = biba_destroy_label, .mpo_posixshm_init_label = biba_init_label, diff --git a/sys/security/mac_mls/mac_mls.c b/sys/security/mac_mls/mac_mls.c index ff9084e870f..8980f500fbb 100644 --- a/sys/security/mac_mls/mac_mls.c +++ b/sys/security/mac_mls/mac_mls.c @@ -1650,6 +1650,24 @@ mls_posixshm_check_open(struct ucred *cred, struct shmfd *shmfd, return (0); } +static int +mls_posixshm_check_read(struct ucred *active_cred, struct ucred *file_cred, + struct shmfd *shm, struct label *shmlabel) +{ + struct mac_mls *subj, *obj; + + if (!mls_enabled || !revocation_enabled) + return (0); + + subj = SLOT(active_cred->cr_label); + obj = SLOT(shmlabel); + + if (!mls_dominate_effective(subj, obj)) + return (EACCES); + + return (0); +} + static int mls_posixshm_check_setmode(struct ucred *cred, struct shmfd *shmfd, struct label *shmlabel, mode_t mode) @@ -1740,6 +1758,24 @@ mls_posixshm_check_unlink(struct ucred *cred, struct shmfd *shmfd, return (0); } +static int +mls_posixshm_check_write(struct ucred *active_cred, struct ucred *file_cred, + struct shmfd *shm, struct label *shmlabel) +{ + struct mac_mls *subj, *obj; + + if (!mls_enabled || !revocation_enabled) + return (0); + + subj = SLOT(active_cred->cr_label); + obj = SLOT(shmlabel); + + if (!mls_dominate_effective(subj, obj)) + return (EACCES); + + return (0); +} + static void mls_posixshm_create(struct ucred *cred, struct shmfd *shmfd, struct label *shmlabel) @@ -3280,11 +3316,13 @@ static struct mac_policy_ops mls_ops = .mpo_posixshm_check_mmap = mls_posixshm_check_mmap, .mpo_posixshm_check_open = mls_posixshm_check_open, + .mpo_posixshm_check_read = mls_posixshm_check_read, .mpo_posixshm_check_setmode = mls_posixshm_check_setmode, .mpo_posixshm_check_setowner = mls_posixshm_check_setowner, .mpo_posixshm_check_stat = mls_posixshm_check_stat, .mpo_posixshm_check_truncate = mls_posixshm_check_truncate, .mpo_posixshm_check_unlink = mls_posixshm_check_unlink, + .mpo_posixshm_check_write = mls_posixshm_check_write, .mpo_posixshm_create = mls_posixshm_create, .mpo_posixshm_destroy_label = mls_destroy_label, .mpo_posixshm_init_label = mls_init_label, diff --git a/sys/security/mac_stub/mac_stub.c b/sys/security/mac_stub/mac_stub.c index ed25d232238..54a67537d31 100644 --- a/sys/security/mac_stub/mac_stub.c +++ b/sys/security/mac_stub/mac_stub.c @@ -756,6 +756,14 @@ stub_posixshm_check_open(struct ucred *cred, struct shmfd *shmfd, return (0); } +static int +stub_posixshm_check_read(struct ucred *active_cred, struct ucred *file_cred, + struct shmfd *shm, struct label *shmlabel) +{ + + return (0); +} + static int stub_posixshm_check_setmode(struct ucred *cred, struct shmfd *shmfd, struct label *shmlabel, mode_t mode) @@ -796,6 +804,14 @@ stub_posixshm_check_unlink(struct ucred *cred, struct shmfd *shmfd, return (0); } +static int +stub_posixshm_check_write(struct ucred *active_cred, struct ucred *file_cred, + struct shmfd *shm, struct label *shmlabel) +{ + + return (0); +} + static void stub_posixshm_create(struct ucred *cred, struct shmfd *shmfd, struct label *shmlabel) @@ -1782,11 +1798,13 @@ static struct mac_policy_ops stub_ops = .mpo_posixshm_check_create = stub_posixshm_check_create, .mpo_posixshm_check_mmap = stub_posixshm_check_mmap, .mpo_posixshm_check_open = stub_posixshm_check_open, + .mpo_posixshm_check_read = stub_posixshm_check_read, .mpo_posixshm_check_setmode = stub_posixshm_check_setmode, .mpo_posixshm_check_setowner = stub_posixshm_check_setowner, .mpo_posixshm_check_stat = stub_posixshm_check_stat, .mpo_posixshm_check_truncate = stub_posixshm_check_truncate, .mpo_posixshm_check_unlink = stub_posixshm_check_unlink, + .mpo_posixshm_check_write = stub_posixshm_check_write, .mpo_posixshm_create = stub_posixshm_create, .mpo_posixshm_destroy_label = stub_destroy_label, .mpo_posixshm_init_label = stub_init_label, diff --git a/sys/security/mac_test/mac_test.c b/sys/security/mac_test/mac_test.c index 1781798e582..4d48dfd4bbb 100644 --- a/sys/security/mac_test/mac_test.c +++ b/sys/security/mac_test/mac_test.c @@ -1423,6 +1423,21 @@ test_posixshm_check_open(struct ucred *cred, struct shmfd *shmfd, return (0); } +COUNTER_DECL(posixshm_check_read); +static int +test_posixshm_check_read(struct ucred *active_cred, + struct ucred *file_cred, struct shmfd *shm, struct label *shmlabel) +{ + + LABEL_CHECK(active_cred->cr_label, MAGIC_CRED); + if (file_cred != NULL) + LABEL_CHECK(file_cred->cr_label, MAGIC_CRED); + LABEL_CHECK(shmlabel, MAGIC_POSIX_SHM); + COUNTER_INC(posixshm_check_read); + + return (0); +} + COUNTER_DECL(posixshm_check_setmode); static int test_posixshm_check_setmode(struct ucred *cred, struct shmfd *shmfd, @@ -1485,6 +1500,21 @@ test_posixshm_check_unlink(struct ucred *cred, struct shmfd *shmfd, return (0); } +COUNTER_DECL(posixshm_check_write); +static int +test_posixshm_check_write(struct ucred *active_cred, + struct ucred *file_cred, struct shmfd *shm, struct label *shmlabel) +{ + + LABEL_CHECK(active_cred->cr_label, MAGIC_CRED); + if (file_cred != NULL) + LABEL_CHECK(file_cred->cr_label, MAGIC_CRED); + LABEL_CHECK(shmlabel, MAGIC_POSIX_SHM); + COUNTER_INC(posixshm_check_write); + + return (0); +} + COUNTER_DECL(posixshm_create); static void test_posixshm_create(struct ucred *cred, struct shmfd *shmfd, @@ -3114,11 +3144,13 @@ static struct mac_policy_ops test_ops = .mpo_posixshm_check_create = test_posixshm_check_create, .mpo_posixshm_check_mmap = test_posixshm_check_mmap, .mpo_posixshm_check_open = test_posixshm_check_open, + .mpo_posixshm_check_read = test_posixshm_check_read, .mpo_posixshm_check_setmode = test_posixshm_check_setmode, .mpo_posixshm_check_setowner = test_posixshm_check_setowner, .mpo_posixshm_check_stat = test_posixshm_check_stat, .mpo_posixshm_check_truncate = test_posixshm_check_truncate, .mpo_posixshm_check_unlink = test_posixshm_check_unlink, + .mpo_posixshm_check_write = test_posixshm_check_write, .mpo_posixshm_create = test_posixshm_create, .mpo_posixshm_destroy_label = test_posixshm_destroy_label, .mpo_posixshm_init_label = test_posixshm_init_label, diff --git a/sys/sparc64/conf/GENERIC b/sys/sparc64/conf/GENERIC index ad8f42990ec..2d826519b9e 100644 --- a/sys/sparc64/conf/GENERIC +++ b/sys/sparc64/conf/GENERIC @@ -63,14 +63,14 @@ options HWPMC_HOOKS # Necessary kernel hooks for hwpmc(4) options AUDIT # Security event auditing options CAPABILITY_MODE # Capsicum capability mode options CAPABILITIES # Capsicum capabilities +options PROCDESC # Support for process descriptors options MAC # TrustedBSD MAC Framework options INCLUDE_CONFIG_FILE # Include this file in kernel # Debugging support. Always need this: options KDB # Enable kernel debugger support. -# For minimum debugger support (stable branch) use: -#options KDB_TRACE # Print a stack trace for a panic. -# For full debugger support use this instead: +options KDB_TRACE # Print a stack trace for a panic. +# For full debugger support use (turn off in stable branch): options DDB # Support DDB. options GDB # Support remote GDB. options DEADLKRES # Enable the deadlock resolver diff --git a/sys/sparc64/include/sf_buf.h b/sys/sparc64/include/sf_buf.h index b6ee1cc20b6..ebbbea8389c 100644 --- a/sys/sparc64/include/sf_buf.h +++ b/sys/sparc64/include/sf_buf.h @@ -39,6 +39,9 @@ struct sf_buf { vm_offset_t kva; /* va of mapping */ }; +struct sf_buf * sf_buf_alloc(struct vm_page *m, int flags); +void sf_buf_free(struct sf_buf *sf); + static __inline vm_offset_t sf_buf_kva(struct sf_buf *sf) { diff --git a/sys/sparc64/sparc64/bus_machdep.c b/sys/sparc64/sparc64/bus_machdep.c index 7f5e76b9363..415f43dafbf 100644 --- a/sys/sparc64/sparc64/bus_machdep.c +++ b/sys/sparc64/sparc64/bus_machdep.c @@ -655,7 +655,7 @@ sparc64_bus_mem_map(bus_space_tag_t tag, bus_addr_t addr, bus_size_t size, if (vaddr != 0L) sva = trunc_page(vaddr); else { - if ((sva = kmem_alloc_nofault(kernel_map, size)) == 0) + if ((sva = kva_alloc(size)) == 0) panic("%s: cannot allocate virtual memory", __func__); } @@ -701,7 +701,7 @@ sparc64_bus_mem_unmap(bus_space_tag_t tag, bus_space_handle_t handle, for (va = sva; va < endva; va += PAGE_SIZE) pmap_kremove_flags(va); tlb_range_demap(kernel_pmap, sva, sva + size - 1); - kmem_free(kernel_map, sva, size); + kva_free(sva, size); return (0); } diff --git a/sys/sparc64/sparc64/genassym.c b/sys/sparc64/sparc64/genassym.c index df31805a09a..0b4a10ce8c5 100644 --- a/sys/sparc64/sparc64/genassym.c +++ b/sys/sparc64/sparc64/genassym.c @@ -39,6 +39,7 @@ __FBSDID("$FreeBSD$"); #include #include +#include #include #include #include diff --git a/sys/sparc64/sparc64/mem.c b/sys/sparc64/sparc64/mem.c index 68c397e2f3f..b78f7050a00 100644 --- a/sys/sparc64/sparc64/mem.c +++ b/sys/sparc64/sparc64/mem.c @@ -137,8 +137,11 @@ memrw(struct cdev *dev, struct uio *uio, int flags) if (ova == 0) { if (dcache_color_ignore == 0) colors = DCACHE_COLORS; - ova = kmem_alloc_wait(kernel_map, - PAGE_SIZE * colors); + ova = kva_alloc(PAGE_SIZE * colors); + if (ova == 0) { + error = ENOMEM; + break; + } } if (colors != 1 && m->md.color != -1) va = ova + m->md.color * PAGE_SIZE; @@ -179,6 +182,6 @@ memrw(struct cdev *dev, struct uio *uio, int flags) /* else panic! */ } if (ova != 0) - kmem_free_wakeup(kernel_map, ova, PAGE_SIZE * colors); + kva_free(ova, PAGE_SIZE * colors); return (error); } diff --git a/sys/sparc64/sparc64/mp_machdep.c b/sys/sparc64/sparc64/mp_machdep.c index fccfb6f6ae6..8d2282ebdb4 100644 --- a/sys/sparc64/sparc64/mp_machdep.c +++ b/sys/sparc64/sparc64/mp_machdep.c @@ -336,10 +336,12 @@ ap_start(phandle_t node, u_int mid, u_int cpu_impl) cpuid_to_mid[cpuid] = mid; cpu_identify(csa->csa_ver, clock, cpuid); - va = kmem_alloc(kernel_map, PCPU_PAGES * PAGE_SIZE); + va = kmem_malloc(kernel_arena, PCPU_PAGES * PAGE_SIZE, + M_WAITOK | M_ZERO); pc = (struct pcpu *)(va + (PCPU_PAGES * PAGE_SIZE)) - 1; pcpu_init(pc, cpuid, sizeof(*pc)); - dpcpu_init((void *)kmem_alloc(kernel_map, DPCPU_SIZE), cpuid); + dpcpu_init((void *)kmem_malloc(kernel_arena, DPCPU_SIZE, + M_WAITOK | M_ZERO), cpuid); pc->pc_addr = va; pc->pc_clock = clock; pc->pc_impl = cpu_impl; diff --git a/sys/sparc64/sparc64/pmap.c b/sys/sparc64/sparc64/pmap.c index 0b1e6b21a3c..7e63c6caa57 100644 --- a/sys/sparc64/sparc64/pmap.c +++ b/sys/sparc64/sparc64/pmap.c @@ -786,7 +786,7 @@ pmap_init(void) continue; if (addr < VM_MIN_PROM_ADDRESS || addr > VM_MAX_PROM_ADDRESS) continue; - result = vm_map_find(kernel_map, NULL, 0, &addr, size, + result = vm_map_find(kernel_map, NULL, 0, &addr, size, 0, VMFS_NO_SPACE, VM_PROT_ALL, VM_PROT_ALL, MAP_NOFAULT); if (result != KERN_SUCCESS || addr != translations[i].om_start) panic("pmap_init: vm_map_find"); @@ -1204,14 +1204,11 @@ pmap_pinit(pmap_t pm) vm_page_t m; int i; - PMAP_LOCK_INIT(pm); - /* * Allocate KVA space for the TSB. */ if (pm->pm_tsb == NULL) { - pm->pm_tsb = (struct tte *)kmem_alloc_nofault(kernel_map, - TSB_BSIZE); + pm->pm_tsb = (struct tte *)kva_alloc(TSB_BSIZE); if (pm->pm_tsb == NULL) { PMAP_LOCK_DESTROY(pm); return (0); @@ -1231,7 +1228,7 @@ pmap_pinit(pmap_t pm) VM_OBJECT_WLOCK(pm->pm_tsb_obj); for (i = 0; i < TSB_PAGES; i++) { m = vm_page_grab(pm->pm_tsb_obj, i, VM_ALLOC_NOBUSY | - VM_ALLOC_RETRY | VM_ALLOC_WIRED | VM_ALLOC_ZERO); + VM_ALLOC_WIRED | VM_ALLOC_ZERO); m->valid = VM_PAGE_BITS_ALL; m->md.pmap = pm; ma[i] = m; @@ -1300,7 +1297,6 @@ pmap_release(pmap_t pm) vm_page_free_zero(m); } VM_OBJECT_WUNLOCK(obj); - PMAP_LOCK_DESTROY(pm); } /* @@ -1493,7 +1489,7 @@ pmap_enter_locked(pmap_t pm, vm_offset_t va, vm_page_t m, vm_prot_t prot, rw_assert(&tte_list_global_lock, RA_WLOCKED); PMAP_LOCK_ASSERT(pm, MA_OWNED); - if ((m->oflags & (VPO_UNMANAGED | VPO_BUSY)) == 0) + if ((m->oflags & VPO_UNMANAGED) == 0 && !vm_page_xbusied(m)) VM_OBJECT_ASSERT_LOCKED(m->object); PMAP_STATS_INC(pmap_nenter); pa = VM_PAGE_TO_PHYS(m); @@ -2067,13 +2063,12 @@ pmap_is_modified(vm_page_t m) rv = FALSE; /* - * If the page is not VPO_BUSY, then PGA_WRITEABLE cannot be + * If the page is not exclusive busied, then PGA_WRITEABLE cannot be * concurrently set while the object is locked. Thus, if PGA_WRITEABLE * is clear, no TTEs can have TD_W set. */ VM_OBJECT_ASSERT_WLOCKED(m->object); - if ((m->oflags & VPO_BUSY) == 0 && - (m->aflags & PGA_WRITEABLE) == 0) + if (!vm_page_xbusied(m) && (m->aflags & PGA_WRITEABLE) == 0) return (rv); rw_wlock(&tte_list_global_lock); TAILQ_FOREACH(tp, &m->md.tte_list, tte_link) { @@ -2131,6 +2126,14 @@ pmap_is_referenced(vm_page_t m) return (rv); } +/* + * This function is advisory. + */ +void +pmap_advise(pmap_t pmap, vm_offset_t sva, vm_offset_t eva, int advice) +{ +} + void pmap_clear_modify(vm_page_t m) { @@ -2140,13 +2143,13 @@ pmap_clear_modify(vm_page_t m) KASSERT((m->oflags & VPO_UNMANAGED) == 0, ("pmap_clear_modify: page %p is not managed", m)); VM_OBJECT_ASSERT_WLOCKED(m->object); - KASSERT((m->oflags & VPO_BUSY) == 0, - ("pmap_clear_modify: page %p is busy", m)); + KASSERT(!vm_page_xbusied(m), + ("pmap_clear_modify: page %p is exclusive busied", m)); /* * If the page is not PGA_WRITEABLE, then no TTEs can have TD_W set. * If the object containing the page is locked and the page is not - * VPO_BUSY, then PGA_WRITEABLE cannot be concurrently set. + * exclusive busied, then PGA_WRITEABLE cannot be concurrently set. */ if ((m->aflags & PGA_WRITEABLE) == 0) return; @@ -2190,13 +2193,12 @@ pmap_remove_write(vm_page_t m) ("pmap_remove_write: page %p is not managed", m)); /* - * If the page is not VPO_BUSY, then PGA_WRITEABLE cannot be set by - * another thread while the object is locked. Thus, if PGA_WRITEABLE - * is clear, no page table entries need updating. + * If the page is not exclusive busied, then PGA_WRITEABLE cannot be + * set by another thread while the object is locked. Thus, + * if PGA_WRITEABLE is clear, no page table entries need updating. */ VM_OBJECT_ASSERT_WLOCKED(m->object); - if ((m->oflags & VPO_BUSY) == 0 && - (m->aflags & PGA_WRITEABLE) == 0) + if (!vm_page_xbusied(m) && (m->aflags & PGA_WRITEABLE) == 0) return; rw_wlock(&tte_list_global_lock); TAILQ_FOREACH(tp, &m->md.tte_list, tte_link) { diff --git a/sys/sparc64/sparc64/sys_machdep.c b/sys/sparc64/sparc64/sys_machdep.c index c8e8694af6f..9c9163f221f 100644 --- a/sys/sparc64/sparc64/sys_machdep.c +++ b/sys/sparc64/sparc64/sys_machdep.c @@ -71,7 +71,7 @@ sysarch(struct thread *td, struct sysarch_args *uap) default: #ifdef KTRACE if (KTRPOINT(td, KTR_CAPFAIL)) - ktrcapfail(CAPFAIL_SYSCALL, 0, 0); + ktrcapfail(CAPFAIL_SYSCALL, NULL, NULL); #endif return (ECAPMODE); } diff --git a/sys/sparc64/sparc64/vm_machdep.c b/sys/sparc64/sparc64/vm_machdep.c index 8cec001e185..261c131dbe5 100644 --- a/sys/sparc64/sparc64/vm_machdep.c +++ b/sys/sparc64/sparc64/vm_machdep.c @@ -421,7 +421,7 @@ sf_buf_init(void *arg) mtx_init(&sf_freelist.sf_lock, "sf_bufs list lock", NULL, MTX_DEF); SLIST_INIT(&sf_freelist.sf_head); - sf_base = kmem_alloc_nofault(kernel_map, nsfbufs * PAGE_SIZE); + sf_base = kva_alloc(nsfbufs * PAGE_SIZE); sf_bufs = malloc(nsfbufs * sizeof(struct sf_buf), M_TEMP, M_NOWAIT | M_ZERO); for (i = 0; i < nsfbufs; i++) { diff --git a/sys/sys/_types.h b/sys/sys/_types.h index 34d1edbd8ea..ffef9d8b9ab 100644 --- a/sys/sys/_types.h +++ b/sys/sys/_types.h @@ -38,7 +38,6 @@ typedef __uint32_t __blksize_t; /* file block size */ typedef __int64_t __blkcnt_t; /* file block count */ typedef __int32_t __clockid_t; /* clock_gettime()... */ -typedef __uint64_t __cap_rights_t; /* capability rights */ typedef __uint32_t __fflags_t; /* file flags */ typedef __uint64_t __fsblkcnt_t; typedef __uint64_t __fsfilcnt_t; diff --git a/sys/sys/_unrhdr.h b/sys/sys/_unrhdr.h new file mode 100644 index 00000000000..f3c25d165d8 --- /dev/null +++ b/sys/sys/_unrhdr.h @@ -0,0 +1,51 @@ +/*- + * Copyright (c) 2004 Poul-Henning Kamp + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + * $FreeBSD$ + */ + +#ifndef _SYS_UNRHDR_H +#define _SYS_UNRHDR_H + +#include + +struct mtx; + +/* Header element for a unr number space. */ + +struct unrhdr { + TAILQ_HEAD(unrhd,unr) head; + u_int low; /* Lowest item */ + u_int high; /* Highest item */ + u_int busy; /* Count of allocated items */ + u_int alloc; /* Count of memory allocations */ + u_int first; /* items in allocated from start */ + u_int last; /* items free at end */ + struct mtx *mtx; + TAILQ_HEAD(unrfr,unr) ppfree; /* Items to be freed after mtx + lock dropped */ +}; + +#endif diff --git a/sys/sys/bitset.h b/sys/sys/bitset.h index dee5542d68b..7c24ecd0443 100644 --- a/sys/sys/bitset.h +++ b/sys/sys/bitset.h @@ -135,7 +135,14 @@ atomic_set_long(&(p)->__bits[__bitset_word(_s, n)], \ __bitset_mask((_s), n)) -/* Convenience functions catering special cases. */ +/* Convenience functions catering special cases. */ +#define BIT_AND_ATOMIC(_s, d, s) do { \ + __size_t __i; \ + for (__i = 0; __i < __bitset_words((_s)); __i++) \ + atomic_clear_long(&(d)->__bits[__i], \ + ~(s)->__bits[__i]); \ +} while (0) + #define BIT_OR_ATOMIC(_s, d, s) do { \ __size_t __i; \ for (__i = 0; __i < __bitset_words((_s)); __i++) \ diff --git a/sys/sys/buf.h b/sys/sys/buf.h index ffa66762224..c384e187493 100644 --- a/sys/sys/buf.h +++ b/sys/sys/buf.h @@ -501,6 +501,7 @@ void bufstrategy(struct bufobj *, struct buf *); void brelse(struct buf *); void bqrelse(struct buf *); int vfs_bio_awrite(struct buf *); +void vfs_drain_busy_pages(struct buf *bp); struct buf * getpbuf(int *); struct buf *incore(struct bufobj *, daddr_t); struct buf *gbincore(struct bufobj *, daddr_t); diff --git a/sys/sys/callout.h b/sys/sys/callout.h index 9e3eb903112..f5c75ecc25e 100644 --- a/sys/sys/callout.h +++ b/sys/sys/callout.h @@ -71,6 +71,9 @@ void _callout_init_lock(struct callout *, struct lock_object *, int); #define callout_init_mtx(c, mtx, flags) \ _callout_init_lock((c), ((mtx) != NULL) ? &(mtx)->lock_object : \ NULL, (flags)) +#define callout_init_rm(c, rm, flags) \ + _callout_init_lock((c), ((rm != NULL) ? &(rm)->lock_object : \ + NULL, (flags)) #define callout_init_rw(c, rw, flags) \ _callout_init_lock((c), ((rw) != NULL) ? &(rw)->lock_object : \ NULL, (flags)) diff --git a/sys/sys/capability.h b/sys/sys/capability.h index ec63de7ff9e..e5b9ec721c9 100644 --- a/sys/sys/capability.h +++ b/sys/sys/capability.h @@ -42,9 +42,16 @@ #include #include +#include #include #include +#ifndef _KERNEL +#include +#endif + +#define CAPRIGHT(idx, bit) ((1ULL << (57 + (idx))) | (bit)) + /* * Possible rights on capabilities. * @@ -59,29 +66,31 @@ * involve reads or writes depending a great deal on context. */ -#define CAP_NONE 0x0000000000000000ULL +/* INDEX 0 */ /* * General file I/O. */ /* Allows for openat(O_RDONLY), read(2), readv(2). */ -#define CAP_READ 0x0000000000000001ULL +#define CAP_READ CAPRIGHT(0, 0x0000000000000001ULL) /* Allows for openat(O_WRONLY | O_APPEND), write(2), writev(2). */ -#define CAP_WRITE 0x0000000000000002ULL +#define CAP_WRITE CAPRIGHT(0, 0x0000000000000002ULL) +/* Allows for lseek(fd, 0, SEEK_CUR). */ +#define CAP_SEEK_TELL CAPRIGHT(0, 0x0000000000000004ULL) /* Allows for lseek(2). */ -#define CAP_SEEK 0x0000000000000080ULL +#define CAP_SEEK (CAP_SEEK_TELL | 0x0000000000000008ULL) /* Allows for pread(2), preadv(2). */ #define CAP_PREAD (CAP_SEEK | CAP_READ) /* Allows for openat(O_WRONLY) (without O_APPEND), pwrite(2), pwritev(2). */ #define CAP_PWRITE (CAP_SEEK | CAP_WRITE) /* Allows for mmap(PROT_NONE). */ -#define CAP_MMAP 0x0000000000000004ULL +#define CAP_MMAP CAPRIGHT(0, 0x0000000000000010ULL) /* Allows for mmap(PROT_READ). */ #define CAP_MMAP_R (CAP_MMAP | CAP_SEEK | CAP_READ) /* Allows for mmap(PROT_WRITE). */ #define CAP_MMAP_W (CAP_MMAP | CAP_SEEK | CAP_WRITE) /* Allows for mmap(PROT_EXEC). */ -#define CAP_MMAP_X (CAP_MMAP | CAP_SEEK | 0x0000000000000008ULL) +#define CAP_MMAP_X (CAP_MMAP | CAP_SEEK | 0x0000000000000020ULL) /* Allows for mmap(PROT_READ | PROT_WRITE). */ #define CAP_MMAP_RW (CAP_MMAP_R | CAP_MMAP_W) /* Allows for mmap(PROT_READ | PROT_EXEC). */ @@ -91,67 +100,67 @@ /* Allows for mmap(PROT_READ | PROT_WRITE | PROT_EXEC). */ #define CAP_MMAP_RWX (CAP_MMAP_R | CAP_MMAP_W | CAP_MMAP_X) /* Allows for openat(O_CREAT). */ -#define CAP_CREATE 0x0000000000080000ULL +#define CAP_CREATE CAPRIGHT(0, 0x0000000000000040ULL) /* Allows for openat(O_EXEC) and fexecve(2) in turn. */ -#define CAP_FEXECVE 0x0000000000000010ULL +#define CAP_FEXECVE CAPRIGHT(0, 0x0000000000000080ULL) /* Allows for openat(O_SYNC), openat(O_FSYNC), fsync(2). */ -#define CAP_FSYNC 0x0000000000000020ULL +#define CAP_FSYNC CAPRIGHT(0, 0x0000000000000100ULL) /* Allows for openat(O_TRUNC), ftruncate(2). */ -#define CAP_FTRUNCATE 0x0000000000000040ULL - -/* VFS methods. */ -#define CAP_FCHDIR 0x0000000000000200ULL -#define CAP_FCHFLAGS 0x0000000000000100ULL -#define CAP_CHFLAGSAT CAP_FCHFLAGS -#define CAP_FCHMOD 0x0000000000000400ULL -#define CAP_FCHMODAT CAP_FCHMOD -#define CAP_FCHOWN 0x0000000000000800ULL -#define CAP_FCHOWNAT CAP_FCHOWN -#define CAP_FCNTL 0x0000000000001000ULL -#define CAP_FLOCK 0x0000000000004000ULL -#define CAP_FPATHCONF 0x0000000000002000ULL -#define CAP_FSCK 0x0000000000008000ULL -#define CAP_FSTAT 0x0000000000010000ULL -#define CAP_FSTATAT CAP_FSTAT -#define CAP_FSTATFS 0x0000000000020000ULL -#define CAP_FUTIMES 0x0000000000040000ULL -#define CAP_FUTIMESAT CAP_FUTIMES -#define CAP_LINKAT 0x0000000000400000ULL -#define CAP_MKDIRAT 0x0000000000200000ULL -#define CAP_MKFIFOAT 0x0000000000800000ULL -#define CAP_MKNODAT 0x0080000000000000ULL -#define CAP_RENAMEAT 0x0200000000000000ULL -#define CAP_SYMLINKAT 0x0100000000000000ULL -#define CAP_UNLINKAT 0x0000000000100000ULL +#define CAP_FTRUNCATE CAPRIGHT(0, 0x0000000000000200ULL) /* Lookups - used to constrain *at() calls. */ -#define CAP_LOOKUP 0x0000000001000000ULL +#define CAP_LOOKUP CAPRIGHT(0, 0x0000000000000400ULL) + +/* VFS methods. */ +#define CAP_FCHDIR CAPRIGHT(0, 0x0000000000000800ULL) +#define CAP_FCHFLAGS CAPRIGHT(0, 0x0000000000001000ULL) +#define CAP_CHFLAGSAT (CAP_FCHFLAGS | CAP_LOOKUP) +#define CAP_FCHMOD CAPRIGHT(0, 0x0000000000002000ULL) +#define CAP_FCHMODAT (CAP_FCHMOD | CAP_LOOKUP) +#define CAP_FCHOWN CAPRIGHT(0, 0x0000000000004000ULL) +#define CAP_FCHOWNAT (CAP_FCHOWN | CAP_LOOKUP) +#define CAP_FCNTL CAPRIGHT(0, 0x0000000000008000ULL) +#define CAP_FLOCK CAPRIGHT(0, 0x0000000000010000ULL) +#define CAP_FPATHCONF CAPRIGHT(0, 0x0000000000020000ULL) +#define CAP_FSCK CAPRIGHT(0, 0x0000000000040000ULL) +#define CAP_FSTAT CAPRIGHT(0, 0x0000000000080000ULL) +#define CAP_FSTATAT (CAP_FSTAT | CAP_LOOKUP) +#define CAP_FSTATFS CAPRIGHT(0, 0x0000000000100000ULL) +#define CAP_FUTIMES CAPRIGHT(0, 0x0000000000200000ULL) +#define CAP_FUTIMESAT (CAP_FUTIMES | CAP_LOOKUP) +#define CAP_LINKAT CAPRIGHT(0, 0x0000000000400000ULL) +#define CAP_MKDIRAT CAPRIGHT(0, 0x0000000000800000ULL) +#define CAP_MKFIFOAT CAPRIGHT(0, 0x0000000001000000ULL) +#define CAP_MKNODAT CAPRIGHT(0, 0x0000000002000000ULL) +#define CAP_RENAMEAT CAPRIGHT(0, 0x0000000004000000ULL) +#define CAP_SYMLINKAT CAPRIGHT(0, 0x0000000008000000ULL) +#define CAP_UNLINKAT CAPRIGHT(0, 0x0000000010000000ULL) /* Extended attributes. */ -#define CAP_EXTATTR_DELETE 0x0000000002000000ULL -#define CAP_EXTATTR_GET 0x0000000004000000ULL -#define CAP_EXTATTR_LIST 0x0000000008000000ULL -#define CAP_EXTATTR_SET 0x0000000010000000ULL +#define CAP_EXTATTR_DELETE CAPRIGHT(0, 0x0000000020000000ULL) +#define CAP_EXTATTR_GET CAPRIGHT(0, 0x0000000040000000ULL) +#define CAP_EXTATTR_LIST CAPRIGHT(0, 0x0000000080000000ULL) +#define CAP_EXTATTR_SET CAPRIGHT(0, 0x0000000100000000ULL) /* Access Control Lists. */ -#define CAP_ACL_CHECK 0x0000000020000000ULL -#define CAP_ACL_DELETE 0x0000000040000000ULL -#define CAP_ACL_GET 0x0000000080000000ULL -#define CAP_ACL_SET 0x0000000100000000ULL +#define CAP_ACL_CHECK CAPRIGHT(0, 0x0000000200000000ULL) +#define CAP_ACL_DELETE CAPRIGHT(0, 0x0000000400000000ULL) +#define CAP_ACL_GET CAPRIGHT(0, 0x0000000800000000ULL) +#define CAP_ACL_SET CAPRIGHT(0, 0x0000001000000000ULL) /* Socket operations. */ -#define CAP_ACCEPT 0x0000000200000000ULL -#define CAP_BIND 0x0000000400000000ULL -#define CAP_CONNECT 0x0000000800000000ULL -#define CAP_GETPEERNAME 0x0000001000000000ULL -#define CAP_GETSOCKNAME 0x0000002000000000ULL -#define CAP_GETSOCKOPT 0x0000004000000000ULL -#define CAP_LISTEN 0x0000008000000000ULL -#define CAP_PEELOFF 0x0000010000000000ULL +#define CAP_ACCEPT CAPRIGHT(0, 0x0000002000000000ULL) +#define CAP_BIND CAPRIGHT(0, 0x0000004000000000ULL) +#define CAP_CONNECT CAPRIGHT(0, 0x0000008000000000ULL) +#define CAP_GETPEERNAME CAPRIGHT(0, 0x0000010000000000ULL) +#define CAP_GETSOCKNAME CAPRIGHT(0, 0x0000020000000000ULL) +#define CAP_GETSOCKOPT CAPRIGHT(0, 0x0000040000000000ULL) +#define CAP_LISTEN CAPRIGHT(0, 0x0000080000000000ULL) +#define CAP_PEELOFF CAPRIGHT(0, 0x0000100000000000ULL) #define CAP_RECV CAP_READ #define CAP_SEND CAP_WRITE -#define CAP_SETSOCKOPT 0x0000020000000000ULL -#define CAP_SHUTDOWN 0x0000040000000000ULL +#define CAP_SETSOCKOPT CAPRIGHT(0, 0x0000200000000000ULL) +#define CAP_SHUTDOWN CAPRIGHT(0, 0x0000400000000000ULL) #define CAP_SOCK_CLIENT \ (CAP_CONNECT | CAP_GETPEERNAME | CAP_GETSOCKNAME | CAP_GETSOCKOPT | \ @@ -161,56 +170,69 @@ CAP_GETSOCKOPT | CAP_LISTEN | CAP_PEELOFF | CAP_RECV | CAP_SEND | \ CAP_SETSOCKOPT | CAP_SHUTDOWN) +/* All used bits for index 0. */ +#define CAP_ALL0 CAPRIGHT(0, 0x00007FFFFFFFFFFFULL) + +/* Available bits for index 0. */ +#define CAP_UNUSED0_48 CAPRIGHT(0, 0x0000800000000000ULL) +/* ... */ +#define CAP_UNUSED0_57 CAPRIGHT(0, 0x0100000000000000ULL) + +/* INDEX 1 */ + /* Mandatory Access Control. */ -#define CAP_MAC_GET 0x0000080000000000ULL -#define CAP_MAC_SET 0x0000100000000000ULL +#define CAP_MAC_GET CAPRIGHT(1, 0x0000000000000001ULL) +#define CAP_MAC_SET CAPRIGHT(1, 0x0000000000000002ULL) /* Methods on semaphores. */ -#define CAP_SEM_GETVALUE 0x0000200000000000ULL -#define CAP_SEM_POST 0x0000400000000000ULL -#define CAP_SEM_WAIT 0x0000800000000000ULL +#define CAP_SEM_GETVALUE CAPRIGHT(1, 0x0000000000000004ULL) +#define CAP_SEM_POST CAPRIGHT(1, 0x0000000000000008ULL) +#define CAP_SEM_WAIT CAPRIGHT(1, 0x0000000000000010ULL) /* kqueue events. */ -#define CAP_POLL_EVENT 0x0001000000000000ULL -#define CAP_POST_EVENT 0x0002000000000000ULL +#define CAP_POLL_EVENT CAPRIGHT(1, 0x0000000000000020ULL) +#define CAP_POST_EVENT CAPRIGHT(1, 0x0000000000000040ULL) /* Strange and powerful rights that should not be given lightly. */ -#define CAP_IOCTL 0x0004000000000000ULL -#define CAP_TTYHOOK 0x0008000000000000ULL +#define CAP_IOCTL CAPRIGHT(1, 0x0000000000000080ULL) +#define CAP_TTYHOOK CAPRIGHT(1, 0x0000000000000100ULL) /* Process management via process descriptors. */ -#define CAP_PDGETPID 0x0010000000000000ULL -#define CAP_PDWAIT 0x0020000000000000ULL -#define CAP_PDKILL 0x0040000000000000ULL +#define CAP_PDGETPID CAPRIGHT(1, 0x0000000000000200ULL) +#define CAP_PDWAIT CAPRIGHT(1, 0x0000000000000400ULL) +#define CAP_PDKILL CAPRIGHT(1, 0x0000000000000800ULL) /* * Rights that allow to use bindat(2) and connectat(2) syscalls on a * directory descriptor. */ -#define CAP_BINDAT 0x0400000000000000ULL -#define CAP_CONNECTAT 0x0800000000000000ULL +#define CAP_BINDAT CAPRIGHT(1, 0x0000000000001000ULL) +#define CAP_CONNECTAT CAPRIGHT(1, 0x0000000000002000ULL) -/* The mask of all valid method rights. */ -#define CAP_MASK_VALID 0x0fffffffffffffffULL -#define CAP_ALL CAP_MASK_VALID +/* All used bits for index 1. */ +#define CAP_ALL1 CAPRIGHT(1, 0x0000000000003FFFULL) -/* Available bits. */ -#define CAP_UNUSED3 0x1000000000000000ULL -#define CAP_UNUSED2 0x2000000000000000ULL -#define CAP_UNUSED1 0x4000000000000000ULL -#define CAP_UNUSED0 0x8000000000000000ULL +/* Available bits for index 1. */ +#define CAP_UNUSED1_15 CAPRIGHT(1, 0x0000000000004000ULL) +/* ... */ +#define CAP_UNUSED1_57 CAPRIGHT(1, 0x0100000000000000ULL) -/* - * The following defines are provided for backward API compatibility and - * should not be used in new code. - */ -#define CAP_MAPEXEC CAP_MMAP_X -#define CAP_DELETE CAP_UNLINKAT -#define CAP_MKDIR CAP_MKDIRAT -#define CAP_RMDIR CAP_UNLINKAT -#define CAP_MKFIFO CAP_MKFIFOAT -#define CAP_MKNOD CAP_MKNODAT -#define CAP_SOCK_ALL (CAP_SOCK_CLIENT | CAP_SOCK_SERVER) +#define CAP_ALL(rights) do { \ + (rights)->cr_rights[0] = \ + ((uint64_t)CAP_RIGHTS_VERSION << 62) | CAP_ALL0; \ + (rights)->cr_rights[1] = CAP_ALL1; \ +} while (0) + +#define CAP_NONE(rights) do { \ + (rights)->cr_rights[0] = \ + ((uint64_t)CAP_RIGHTS_VERSION << 62) | CAPRIGHT(0, 0ULL); \ + (rights)->cr_rights[1] = CAPRIGHT(1, 0ULL); \ +} while (0) + +#define CAPRVER(right) ((int)((right) >> 62)) +#define CAPVER(rights) CAPRVER((rights)->cr_rights[0]) +#define CAPARSIZE(rights) (CAPVER(rights) + 2) +#define CAPIDXBIT(right) ((int)(((right) >> 57) & 0x1F)) /* * Allowed fcntl(2) commands. @@ -230,6 +252,27 @@ #define CAP_IOCTLS_ALL SSIZE_MAX +#define cap_rights_init(...) \ + __cap_rights_init(CAP_RIGHTS_VERSION, __VA_ARGS__, 0ULL) +cap_rights_t *__cap_rights_init(int version, cap_rights_t *rights, ...); + +#define cap_rights_set(rights, ...) \ + __cap_rights_set((rights), __VA_ARGS__, 0ULL) +void __cap_rights_set(cap_rights_t *rights, ...); + +#define cap_rights_clear(rights, ...) \ + __cap_rights_clear((rights), __VA_ARGS__, 0ULL) +void __cap_rights_clear(cap_rights_t *rights, ...); + +#define cap_rights_is_set(rights, ...) \ + __cap_rights_is_set((rights), __VA_ARGS__, 0ULL) +bool __cap_rights_is_set(const cap_rights_t *rights, ...); + +bool cap_rights_is_valid(const cap_rights_t *rights); +void cap_rights_merge(cap_rights_t *dst, const cap_rights_t *src); +void cap_rights_remove(cap_rights_t *dst, const cap_rights_t *src); +bool cap_rights_contains(const cap_rights_t *big, const cap_rights_t *little); + #ifdef _KERNEL #include @@ -241,17 +284,17 @@ struct filedesc; /* * Test whether a capability grants the requested rights. */ -int cap_check(cap_rights_t have, cap_rights_t need); +int cap_check(const cap_rights_t *havep, const cap_rights_t *needp); /* * Convert capability rights into VM access flags. */ -u_char cap_rights_to_vmprot(cap_rights_t have); +u_char cap_rights_to_vmprot(cap_rights_t *havep); /* * For the purposes of procstat(1) and similar tools, allow kern_descrip.c to * extract the rights from a capability. */ -cap_rights_t cap_rights(struct filedesc *fdp, int fd); +cap_rights_t *cap_rights(struct filedesc *fdp, int fd); int cap_ioctl_check(struct filedesc *fdp, int fd, u_long cmd); int cap_fcntl_check(struct filedesc *fdp, int fd, int cmd); @@ -259,18 +302,11 @@ int cap_fcntl_check(struct filedesc *fdp, int fd, int cmd); #else /* !_KERNEL */ __BEGIN_DECLS -#include - /* * cap_enter(): Cause the process to enter capability mode, which will * prevent it from directly accessing global namespaces. System calls will * be limited to process-local, process-inherited, or file descriptor * operations. If already in capability mode, a no-op. - * - * Currently, process-inherited operations are not properly handled -- in - * particular, we're interested in things like waitpid(2), kill(2), etc, - * being properly constrained. One possible solution is to introduce process - * descriptors. */ int cap_enter(void); @@ -288,11 +324,12 @@ int cap_getmode(u_int *modep); /* * Limits capability rights for the given descriptor (CAP_*). */ -int cap_rights_limit(int fd, cap_rights_t rights); +int cap_rights_limit(int fd, const cap_rights_t *rights); /* - * Returns bitmask of capability rights for the given descriptor. + * Returns capability rights for the given descriptor. */ -int cap_rights_get(int fd, cap_rights_t *rightsp); +#define cap_rights_get(fd, rights) __cap_rights_get(CAP_RIGHTS_VERSION, (fd), (rights)) +int __cap_rights_get(int version, int fd, cap_rights_t *rightsp); /* * Limits allowed ioctls for the given descriptor. */ @@ -312,10 +349,6 @@ int cap_fcntls_limit(int fd, uint32_t fcntlrights); */ int cap_fcntls_get(int fd, uint32_t *fcntlrightsp); -/* For backward compatibility. */ -int cap_new(int fd, cap_rights_t rights); -#define cap_getrights(fd, rightsp) cap_rights_get((fd), (rightsp)) - __END_DECLS #endif /* !_KERNEL */ diff --git a/sys/sys/caprights.h b/sys/sys/caprights.h new file mode 100644 index 00000000000..eb8e454f1de --- /dev/null +++ b/sys/sys/caprights.h @@ -0,0 +1,61 @@ +/*- + * Copyright (c) 2013 FreeBSD Foundation + * All rights reserved. + * + * This software was developed by Pawel Jakub Dawidek under sponsorship from + * the FreeBSD Foundation. + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + * $FreeBSD$ + */ + +#ifndef _SYS_CAPRIGHTS_H_ +#define _SYS_CAPRIGHTS_H_ + +/* + * The top two bits in the first element of the cr_rights[] array contain + * total number of elements in the array - 2. This means if those two bits are + * equal to 0, we have 2 array elements. + * The top two bits in all remaining array elements should be 0. + * The next five bits contain array index. Only one bit is used and bit position + * in this five-bits range defines array index. This means there can be at most + * five array elements. + */ +#define CAP_RIGHTS_VERSION_00 0 +/* +#define CAP_RIGHTS_VERSION_01 1 +#define CAP_RIGHTS_VERSION_02 2 +#define CAP_RIGHTS_VERSION_03 3 +*/ +#define CAP_RIGHTS_VERSION CAP_RIGHTS_VERSION_00 + +struct cap_rights { + uint64_t cr_rights[CAP_RIGHTS_VERSION + 2]; +}; + +#ifndef _CAP_RIGHTS_T_DECLARED +#define _CAP_RIGHTS_T_DECLARED +typedef struct cap_rights cap_rights_t; +#endif + +#endif /* !_SYS_CAPRIGHTS_H_ */ diff --git a/sys/sys/conf.h b/sys/sys/conf.h index 5a579b4d233..e9a2f55ee89 100644 --- a/sys/sys/conf.h +++ b/sys/sys/conf.h @@ -61,6 +61,8 @@ struct cdev { #define SI_CHILD 0x0010 /* child of another struct cdev **/ #define SI_DUMPDEV 0x0080 /* is kernel dumpdev */ #define SI_CLONELIST 0x0200 /* on a clone list */ +#define SI_UNMAPPED 0x0400 /* can handle unmapped I/O */ +#define SI_NOSPLIT 0x0800 /* I/O should not be split up */ struct timespec si_atime; struct timespec si_ctime; struct timespec si_mtime; @@ -167,7 +169,6 @@ typedef int dumper_t( #define D_MMAP_ANON 0x00100000 /* special treatment in vm_mmap.c */ #define D_NEEDGIANT 0x00400000 /* driver want Giant */ #define D_NEEDMINOR 0x00800000 /* driver uses clone_create() */ -#define D_UNMAPPED_IO 0x01000000 /* d_strategy can accept unmapped IO */ /* * Version numbers. diff --git a/sys/sys/cpuset.h b/sys/sys/cpuset.h index fc078d303bb..e1ee37d020d 100644 --- a/sys/sys/cpuset.h +++ b/sys/sys/cpuset.h @@ -55,6 +55,7 @@ #define CPU_NAND(d, s) BIT_NAND(CPU_SETSIZE, d, s) #define CPU_CLR_ATOMIC(n, p) BIT_CLR_ATOMIC(CPU_SETSIZE, n, p) #define CPU_SET_ATOMIC(n, p) BIT_SET_ATOMIC(CPU_SETSIZE, n, p) +#define CPU_AND_ATOMIC(n, p) BIT_AND_ATOMIC(CPU_SETSIZE, n, p) #define CPU_OR_ATOMIC(d, s) BIT_OR_ATOMIC(CPU_SETSIZE, d, s) #define CPU_COPY_STORE_REL(f, t) BIT_COPY_STORE_REL(CPU_SETSIZE, f, t) #define CPU_FFS(p) BIT_FFS(CPU_SETSIZE, p) diff --git a/sys/sys/diskpc98.h b/sys/sys/diskpc98.h index 66bda90a126..aa0bb0a8f21 100644 --- a/sys/sys/diskpc98.h +++ b/sys/sys/diskpc98.h @@ -35,15 +35,12 @@ #include -#define DOSBBSECTOR 0 /* DOS boot block relative sector number */ -#undef DOSPARTOFF -#define DOSPARTOFF 0 -#undef DOSPARTSIZE -#define DOSPARTSIZE 32 -#undef NDOSPART -#define NDOSPART 16 -#define DOSMAGICOFFSET 510 -#define DOSMAGIC 0xAA55 +#define PC98_BBSECTOR 1 /* DOS boot block relative sector number */ +#define PC98_PARTOFF 0 +#define PC98_PARTSIZE 32 +#define PC98_NPARTS 16 +#define PC98_MAGICOFS 510 +#define PC98_MAGIC 0xAA55 #define PC98_MID_BOOTABLE 0x80 #define PC98_MID_MASK 0x7f @@ -55,8 +52,7 @@ #define DOSMID_386BSD (PC98_MID_386BSD | PC98_MID_BOOTABLE) #define DOSSID_386BSD (PC98_SID_386BSD | PC98_SID_ACTIVE) -#undef DOSPTYP_386BSD -#define DOSPTYP_386BSD (DOSSID_386BSD << 8 | DOSMID_386BSD) +#define PC98_PTYP_386BSD (DOSSID_386BSD << 8 | DOSMID_386BSD) struct pc98_partition { unsigned char dp_mid; @@ -75,7 +71,7 @@ struct pc98_partition { unsigned char dp_name[16]; }; #ifdef CTASSERT -CTASSERT(sizeof (struct pc98_partition) == DOSPARTSIZE); +CTASSERT(sizeof (struct pc98_partition) == PC98_PARTSIZE); #endif void pc98_partition_dec(void const *pp, struct pc98_partition *d); diff --git a/sys/sys/event.h b/sys/sys/event.h index 707fbed7001..03bd7b90cfa 100644 --- a/sys/sys/event.h +++ b/sys/sys/event.h @@ -76,6 +76,7 @@ struct kevent { #define EV_DISPATCH 0x0080 /* disable event after reporting */ #define EV_SYSFLAGS 0xF000 /* reserved by system */ +#define EV_DROP 0x1000 /* note should be dropped */ #define EV_FLAG1 0x2000 /* filter-specific flag */ /* returned values */ @@ -134,7 +135,7 @@ struct kevent { struct knote; SLIST_HEAD(klist, knote); struct kqueue; -SLIST_HEAD(kqlist, kqueue); +TAILQ_HEAD(kqlist, kqueue); struct knlist { struct klist kl_list; void (*kl_lock)(void *); /* lock function */ diff --git a/sys/sys/eventhandler.h b/sys/sys/eventhandler.h index 02fbeaa816d..f1d06b56ae3 100644 --- a/sys/sys/eventhandler.h +++ b/sys/sys/eventhandler.h @@ -266,4 +266,13 @@ EVENTHANDLER_DECLARE(nmbclusters_change, uma_zone_chfn); EVENTHANDLER_DECLARE(nmbufs_change, uma_zone_chfn); EVENTHANDLER_DECLARE(maxsockets_change, uma_zone_chfn); +/* Kernel linker file load and unload events */ +struct linker_file; +typedef void (*kld_load_fn)(void *, struct linker_file *); +typedef void (*kld_unload_fn)(void *, const char *, caddr_t, size_t); +typedef void (*kld_unload_try_fn)(void *, struct linker_file *, int *); +EVENTHANDLER_DECLARE(kld_load, kld_load_fn); +EVENTHANDLER_DECLARE(kld_unload, kld_unload_fn); +EVENTHANDLER_DECLARE(kld_unload_try, kld_unload_try_fn); + #endif /* SYS_EVENTHANDLER_H */ diff --git a/sys/sys/eventvar.h b/sys/sys/eventvar.h index bdb3adf5727..ed1067f1015 100644 --- a/sys/sys/eventvar.h +++ b/sys/sys/eventvar.h @@ -41,7 +41,7 @@ struct kqueue { struct mtx kq_lock; int kq_refcnt; - SLIST_ENTRY(kqueue) kq_list; + TAILQ_ENTRY(kqueue) kq_list; TAILQ_HEAD(, knote) kq_head; /* list of pending event */ int kq_count; /* number of pending events */ struct selinfo kq_sel; diff --git a/sys/sys/fcntl.h b/sys/sys/fcntl.h index 8ecb3c99d9f..3461f8bdd9e 100644 --- a/sys/sys/fcntl.h +++ b/sys/sys/fcntl.h @@ -136,8 +136,8 @@ typedef __pid_t pid_t; #ifdef _KERNEL /* convert from open() flags to/from fflags; convert O_RD/WR to FREAD/FWRITE */ -#define FFLAGS(oflags) ((oflags) + 1) -#define OFLAGS(fflags) ((fflags) - 1) +#define FFLAGS(oflags) ((oflags) & O_EXEC ? (oflags) : (oflags) + 1) +#define OFLAGS(fflags) ((fflags) & O_EXEC ? (fflags) : (fflags) - 1) /* bits to save after open */ #define FMASK (FREAD|FWRITE|FAPPEND|FASYNC|FFSYNC|FNONBLOCK|O_DIRECT|FEXEC) diff --git a/sys/sys/file.h b/sys/sys/file.h index cfdc1d898fe..7b373f0d709 100644 --- a/sys/sys/file.h +++ b/sys/sys/file.h @@ -105,6 +105,11 @@ typedef int fo_chmod_t(struct file *fp, mode_t mode, struct ucred *active_cred, struct thread *td); typedef int fo_chown_t(struct file *fp, uid_t uid, gid_t gid, struct ucred *active_cred, struct thread *td); +typedef int fo_sendfile_t(struct file *fp, int sockfd, struct uio *hdr_uio, + struct uio *trl_uio, off_t offset, size_t nbytes, + off_t *sent, int flags, int kflags, struct thread *td); +typedef int fo_seek_t(struct file *fp, off_t offset, int whence, + struct thread *td); typedef int fo_flags_t; struct fileops { @@ -118,6 +123,8 @@ struct fileops { fo_close_t *fo_close; fo_chmod_t *fo_chmod; fo_chown_t *fo_chown; + fo_sendfile_t *fo_sendfile; + fo_seek_t *fo_seek; fo_flags_t fo_flags; /* DFLAG_* below */ }; @@ -210,12 +217,12 @@ extern int maxfiles; /* kernel limit on number of open files */ extern int maxfilesperproc; /* per process limit on number of open files */ extern volatile int openfiles; /* actual number of open files */ -int fget(struct thread *td, int fd, cap_rights_t rights, struct file **fpp); -int fget_mmap(struct thread *td, int fd, cap_rights_t rights, +int fget(struct thread *td, int fd, cap_rights_t *rightsp, struct file **fpp); +int fget_mmap(struct thread *td, int fd, cap_rights_t *rightsp, u_char *maxprotp, struct file **fpp); -int fget_read(struct thread *td, int fd, cap_rights_t rights, +int fget_read(struct thread *td, int fd, cap_rights_t *rightsp, struct file **fpp); -int fget_write(struct thread *td, int fd, cap_rights_t rights, +int fget_write(struct thread *td, int fd, cap_rights_t *rightsp, struct file **fpp); int _fdrop(struct file *fp, struct thread *td); @@ -235,19 +242,24 @@ fo_close_t soo_close; fo_chmod_t invfo_chmod; fo_chown_t invfo_chown; +fo_sendfile_t invfo_sendfile; + +fo_sendfile_t vn_sendfile; +fo_seek_t vn_seek; void finit(struct file *, u_int, short, void *, struct fileops *); -int fgetvp(struct thread *td, int fd, cap_rights_t rights, struct vnode **vpp); -int fgetvp_exec(struct thread *td, int fd, cap_rights_t rights, +int fgetvp(struct thread *td, int fd, cap_rights_t *rightsp, struct vnode **vpp); -int fgetvp_rights(struct thread *td, int fd, cap_rights_t need, +int fgetvp_exec(struct thread *td, int fd, cap_rights_t *rightsp, + struct vnode **vpp); +int fgetvp_rights(struct thread *td, int fd, cap_rights_t *needrightsp, struct filecaps *havecaps, struct vnode **vpp); -int fgetvp_read(struct thread *td, int fd, cap_rights_t rights, +int fgetvp_read(struct thread *td, int fd, cap_rights_t *rightsp, struct vnode **vpp); -int fgetvp_write(struct thread *td, int fd, cap_rights_t rights, +int fgetvp_write(struct thread *td, int fd, cap_rights_t *rightsp, struct vnode **vpp); -int fgetsock(struct thread *td, int fd, cap_rights_t rights, +int fgetsock(struct thread *td, int fd, cap_rights_t *rightsp, struct socket **spp, u_int *fflagp); void fputsock(struct socket *sp); @@ -273,6 +285,7 @@ static __inline fo_stat_t fo_stat; static __inline fo_close_t fo_close; static __inline fo_chmod_t fo_chmod; static __inline fo_chown_t fo_chown; +static __inline fo_sendfile_t fo_sendfile; static __inline int fo_read(struct file *fp, struct uio *uio, struct ucred *active_cred, @@ -352,6 +365,23 @@ fo_chown(struct file *fp, uid_t uid, gid_t gid, struct ucred *active_cred, return ((*fp->f_ops->fo_chown)(fp, uid, gid, active_cred, td)); } +static __inline int +fo_sendfile(struct file *fp, int sockfd, struct uio *hdr_uio, + struct uio *trl_uio, off_t offset, size_t nbytes, off_t *sent, int flags, + int kflags, struct thread *td) +{ + + return ((*fp->f_ops->fo_sendfile)(fp, sockfd, hdr_uio, trl_uio, offset, + nbytes, sent, flags, kflags, td)); +} + +static __inline int +fo_seek(struct file *fp, off_t offset, int whence, struct thread *td) +{ + + return ((*fp->f_ops->fo_seek)(fp, offset, whence, td)); +} + #endif /* _KERNEL */ #endif /* !SYS_FILE_H */ diff --git a/sys/sys/filedesc.h b/sys/sys/filedesc.h index 1e51a9cfad4..968ceffe9ff 100644 --- a/sys/sys/filedesc.h +++ b/sys/sys/filedesc.h @@ -33,6 +33,7 @@ #ifndef _SYS_FILEDESC_H_ #define _SYS_FILEDESC_H_ +#include #include #include #include @@ -43,9 +44,9 @@ struct filecaps { cap_rights_t fc_rights; /* per-descriptor capability rights */ - uint32_t fc_fcntls; /* per-descriptor allowed fcntls */ u_long *fc_ioctls; /* per-descriptor allowed ioctls */ int16_t fc_nioctls; /* fc_ioctls array size */ + uint32_t fc_fcntls; /* per-descriptor allowed fcntls */ }; struct filedescent { @@ -108,10 +109,6 @@ struct filedesc_to_leader { #ifdef _KERNEL -#include /* CTASSERT() */ - -CTASSERT(sizeof(cap_rights_t) == sizeof(uint64_t)); - /* Flags for do_dup() */ #define DUP_FIXED 0x1 /* Force fixed allocation. */ #define DUP_FCNTL 0x2 /* fcntl()-style errors. */ @@ -163,13 +160,13 @@ struct filedesc *fdshare(struct filedesc *fdp); struct filedesc_to_leader * filedesc_to_leader_alloc(struct filedesc_to_leader *old, struct filedesc *fdp, struct proc *leader); -int getvnode(struct filedesc *fdp, int fd, cap_rights_t rights, +int getvnode(struct filedesc *fdp, int fd, cap_rights_t *rightsp, struct file **fpp); void mountcheckdirs(struct vnode *olddp, struct vnode *newdp); void setugidsafety(struct thread *td); /* Return a referenced file from an unlocked descriptor. */ -int fget_unlocked(struct filedesc *fdp, int fd, cap_rights_t needrights, +int fget_unlocked(struct filedesc *fdp, int fd, cap_rights_t *needrightsp, int needfcntl, struct file **fpp, cap_rights_t *haverightsp); /* Requires a FILEDESC_{S,X}LOCK held and returns without a ref. */ diff --git a/sys/sys/jail.h b/sys/sys/jail.h index a934aacb19f..a82a4995106 100644 --- a/sys/sys/jail.h +++ b/sys/sys/jail.h @@ -227,7 +227,8 @@ struct prison_racct { #define PR_ALLOW_MOUNT_NULLFS 0x0100 #define PR_ALLOW_MOUNT_ZFS 0x0200 #define PR_ALLOW_MOUNT_PROCFS 0x0400 -#define PR_ALLOW_ALL 0x07ff +#define PR_ALLOW_MOUNT_TMPFS 0x0800 +#define PR_ALLOW_ALL 0x0fff /* * OSD methods diff --git a/sys/sys/kernel.h b/sys/sys/kernel.h index 687b42caf09..68e0858ac61 100644 --- a/sys/sys/kernel.h +++ b/sys/sys/kernel.h @@ -96,6 +96,11 @@ enum sysinit_sub_id { SI_SUB_VM = 0x1000000, /* virtual memory system init*/ SI_SUB_KMEM = 0x1800000, /* kernel memory*/ SI_SUB_KVM_RSRC = 0x1A00000, /* kvm operational limits*/ + SI_SUB_HYPERVISOR = 0x1A40000, /* + * Hypervisor detection and + * virtualization support + * setup. + */ SI_SUB_WITNESS = 0x1A80000, /* witness initialization */ SI_SUB_MTX_POOL_DYNAMIC = 0x1AC0000, /* dynamic mutex pool */ SI_SUB_LOCK = 0x1B00000, /* various locks */ diff --git a/sys/sys/ktrace.h b/sys/sys/ktrace.h index b3e6be8a7fe..e9cfa6ef5b8 100644 --- a/sys/sys/ktrace.h +++ b/sys/sys/ktrace.h @@ -33,6 +33,8 @@ #ifndef _SYS_KTRACE_H_ #define _SYS_KTRACE_H_ +#include + /* * operations to ktrace system call (KTROP(op)) */ @@ -264,7 +266,10 @@ void ktrprocexit(struct thread *); void ktrprocfork(struct proc *, struct proc *); void ktruserret(struct thread *); void ktrstruct(const char *, void *, size_t); -void ktrcapfail(enum ktr_cap_fail_type, cap_rights_t, cap_rights_t); +void ktrcapfail(enum ktr_cap_fail_type, const cap_rights_t *, + const cap_rights_t *); +#define ktrcaprights(s) \ + ktrstruct("caprights", (s), sizeof(cap_rights_t)) #define ktrsockaddr(s) \ ktrstruct("sockaddr", (s), ((struct sockaddr *)(s))->sa_len) #define ktrstat(s) \ diff --git a/sys/sys/linker.h b/sys/sys/linker.h index 8ea140f7788..b2942f20624 100644 --- a/sys/sys/linker.h +++ b/sys/sys/linker.h @@ -92,10 +92,6 @@ struct linker_file { */ int nenabled; /* number of enabled probes. */ int fbt_nentries; /* number of fbt entries created. */ - void *sdt_probes; - int sdt_nentries; - size_t sdt_nprobes; - size_t sdt_size; }; /* diff --git a/sys/sys/mbpool.h b/sys/sys/mbpool.h index f292f29721a..585b66d4864 100644 --- a/sys/sys/mbpool.h +++ b/sys/sys/mbpool.h @@ -69,7 +69,7 @@ void *mbp_alloc(struct mbpool *, bus_addr_t *, uint32_t *); void mbp_free(struct mbpool *, void *); /* free a chunk that is an external mbuf */ -void mbp_ext_free(void *, void *); +int mbp_ext_free(struct mbuf *, void *, void *); /* free all buffers that are marked to be on the card */ void mbp_card_free(struct mbpool *); diff --git a/sys/sys/mbuf.h b/sys/sys/mbuf.h index cef9f04b347..bc2924ad656 100644 --- a/sys/sys/mbuf.h +++ b/sys/sys/mbuf.h @@ -53,6 +53,10 @@ * externally and attach it to the mbuf in a way similar to that of mbuf * clusters. * + * NB: These calculation do not take actual compiler-induced alignment and + * padding inside the complete struct mbuf into account. Appropriate + * attention is required when changing members of struct mbuf. + * * MLEN is data length in a normal mbuf. * MHLEN is data length in an mbuf with pktheader. * MINCLSIZE is a smallest amount of data that should be put into cluster. @@ -67,8 +71,10 @@ * type: * * mtod(m, t) -- Convert mbuf pointer to data pointer of correct type. + * mtodo(m, o) -- Same as above but with offset 'o' into data. */ #define mtod(m, t) ((t)((m)->m_data)) +#define mtodo(m, o) ((void *)(((m)->m_data) + (o))) /* * Argument structure passed to UMA routines during mbuf and packet @@ -80,23 +86,21 @@ struct mb_args { }; #endif /* _KERNEL */ -#if defined(__LP64__) -#define M_HDR_PAD 6 -#else -#define M_HDR_PAD 2 -#endif - /* * Header present at the beginning of every mbuf. + * Size ILP32: 24 + * LP64: 32 */ struct m_hdr { struct mbuf *mh_next; /* next buffer in chain */ struct mbuf *mh_nextpkt; /* next chain in queue/record */ caddr_t mh_data; /* location of data */ - int mh_len; /* amount of data in this mbuf */ - int mh_flags; /* flags; see below */ - short mh_type; /* type of data in this mbuf */ - uint8_t pad[M_HDR_PAD];/* word align */ + int32_t mh_len; /* amount of data in this mbuf */ + uint32_t mh_type:8, /* type of data in this mbuf */ + mh_flags:24; /* flags; see below */ +#if !defined(__LP64__) + uint32_t mh_pad; /* pad for 64bit alignment */ +#endif }; /* @@ -112,42 +116,66 @@ struct m_tag { /* * Record/packet header in first mbuf of chain; valid only if M_PKTHDR is set. + * Size ILP32: 48 + * LP64: 56 */ struct pkthdr { struct ifnet *rcvif; /* rcv interface */ - /* variables for ip and tcp reassembly */ - void *header; /* pointer to packet header */ - int len; /* total packet length */ - uint32_t flowid; /* packet's 4-tuple system - * flow identifier - */ - /* variables for hardware checksum */ - int csum_flags; /* flags regarding checksum */ - int csum_data; /* data field used by csum routines */ - u_int16_t tso_segsz; /* TSO segment size */ - union { - u_int16_t vt_vtag; /* Ethernet 802.1p+q vlan tag */ - u_int16_t vt_nrecs; /* # of IGMPv3 records in this chain */ - } PH_vt; - u_int16_t fibnum; /* this packet should use this fib */ - u_int16_t pad2; /* align to 32 bits */ SLIST_HEAD(packet_tags, m_tag) tags; /* list of packet tags */ + int32_t len; /* total packet length */ + + /* Layer crossing persistent information. */ + uint32_t flowid; /* packet's 4-tuple system */ + uint64_t csum_flags; /* checksum and offload features */ + uint16_t fibnum; /* this packet should use this fib */ + uint8_t cosqos; /* class/quality of service */ + uint8_t rsstype; /* hash type */ + uint8_t l2hlen; /* layer 2 header length */ + uint8_t l3hlen; /* layer 3 header length */ + uint8_t l4hlen; /* layer 4 header length */ + uint8_t l5hlen; /* layer 5 header length */ + union { + uint8_t eigth[8]; + uint16_t sixteen[4]; + uint32_t thirtytwo[2]; + uint64_t sixtyfour[1]; + uintptr_t unintptr[1]; + void *ptr; + } PH_per; + + /* Layer specific non-persistent local storage for reassembly, etc. */ + union { + uint8_t eigth[8]; + uint16_t sixteen[4]; + uint32_t thirtytwo[2]; + uint64_t sixtyfour[1]; + uintptr_t unintptr[1]; + void *ptr; + } PH_loc; }; -#define ether_vtag PH_vt.vt_vtag +#define ether_vtag PH_per.sixteen[0] +#define PH_vt PH_per +#define vt_nrecs sixteen[0] +#define tso_segsz PH_per.sixteen[1] +#define csum_phsum PH_per.sixteen[2] +#define csum_data PH_per.thirtytwo[1] /* * Description of external storage mapped into mbuf; valid only if M_EXT is * set. + * Size ILP32: 28 + * LP64: 48 */ struct m_ext { + volatile u_int *ref_cnt; /* pointer to ref count info */ caddr_t ext_buf; /* start of buffer */ - void (*ext_free) /* free routine if not the usual */ - (void *, void *); + uint32_t ext_size; /* size of buffer, for ext_free */ + uint32_t ext_type:8, /* type of external storage */ + ext_flags:24; /* external storage mbuf flags */ + int (*ext_free) /* free routine if not the usual */ + (struct mbuf *, void *, void *); void *ext_arg1; /* optional argument pointer */ void *ext_arg2; /* optional argument pointer */ - u_int ext_size; /* size of buffer, for ext_free */ - volatile u_int *ref_cnt; /* pointer to ref count info */ - int ext_type; /* type of external storage */ }; /* @@ -180,40 +208,60 @@ struct mbuf { #define m_dat M_dat.M_databuf /* - * mbuf flags. + * mbuf flags of global significance and layer crossing. + * Those of only protocol/layer specific significance are to be mapped + * to M_PROTO[1-12] and cleared at layer handoff boundaries. + * NB: Limited to the lower 24 bits. */ #define M_EXT 0x00000001 /* has associated external storage */ #define M_PKTHDR 0x00000002 /* start of record */ #define M_EOR 0x00000004 /* end of record */ #define M_RDONLY 0x00000008 /* associated data is marked read-only */ -#define M_PROTO1 0x00000010 /* protocol-specific */ -#define M_PROTO2 0x00000020 /* protocol-specific */ -#define M_PROTO3 0x00000040 /* protocol-specific */ -#define M_PROTO4 0x00000080 /* protocol-specific */ -#define M_PROTO5 0x00000100 /* protocol-specific */ -#define M_BCAST 0x00000200 /* send/received as link-level broadcast */ -#define M_MCAST 0x00000400 /* send/received as link-level multicast */ -#define M_FRAG 0x00000800 /* packet is a fragment of a larger packet */ -#define M_FIRSTFRAG 0x00001000 /* packet is first fragment */ -#define M_LASTFRAG 0x00002000 /* packet is last fragment */ -#define M_SKIP_FIREWALL 0x00004000 /* skip firewall processing */ - /* 0x00008000 free */ -#define M_VLANTAG 0x00010000 /* ether_vtag is valid */ -#define M_PROMISC 0x00020000 /* packet was not for us */ -#define M_NOFREE 0x00040000 /* do not free mbuf, embedded in cluster */ -#define M_PROTO6 0x00080000 /* protocol-specific */ -#define M_PROTO7 0x00100000 /* protocol-specific */ -#define M_PROTO8 0x00200000 /* protocol-specific */ -#define M_FLOWID 0x00400000 /* deprecated: flowid is valid */ -#define M_HASHTYPEBITS 0x0F000000 /* mask of bits holding flowid hash type */ +#define M_BCAST 0x00000010 /* send/received as link-level broadcast */ +#define M_MCAST 0x00000020 /* send/received as link-level multicast */ +#define M_PROMISC 0x00000040 /* packet was not for us */ +#define M_VLANTAG 0x00000080 /* ether_vtag is valid */ +#define M_FLOWID 0x00000100 /* deprecated: flowid is valid */ +#define M_NOFREE 0x00000200 /* do not free mbuf, embedded in cluster */ -#define M_NOTIFICATION M_PROTO5 /* SCTP notification */ +#define M_PROTO1 0x00001000 /* protocol-specific */ +#define M_PROTO2 0x00002000 /* protocol-specific */ +#define M_PROTO3 0x00004000 /* protocol-specific */ +#define M_PROTO4 0x00008000 /* protocol-specific */ +#define M_PROTO5 0x00010000 /* protocol-specific */ +#define M_PROTO6 0x00020000 /* protocol-specific */ +#define M_PROTO7 0x00040000 /* protocol-specific */ +#define M_PROTO8 0x00080000 /* protocol-specific */ +#define M_PROTO9 0x00100000 /* protocol-specific */ +#define M_PROTO10 0x00200000 /* protocol-specific */ +#define M_PROTO11 0x00400000 /* protocol-specific */ +#define M_PROTO12 0x00800000 /* protocol-specific */ /* * Flags to purge when crossing layers. */ #define M_PROTOFLAGS \ - (M_PROTO1|M_PROTO2|M_PROTO3|M_PROTO4|M_PROTO5|M_PROTO6|M_PROTO7|M_PROTO8) + (M_PROTO1|M_PROTO2|M_PROTO3|M_PROTO4|M_PROTO5|M_PROTO6|M_PROTO7|M_PROTO8|\ + M_PROTO9|M_PROTO10|M_PROTO11|M_PROTO12) + +/* + * Flags preserved when copying m_pkthdr. + */ +#define M_COPYFLAGS \ + (M_PKTHDR|M_EOR|M_RDONLY|M_BCAST|M_MCAST|M_VLANTAG|M_PROMISC| \ + M_PROTOFLAGS) + +/* + * Mbuf flag description for use with printf(9) %b identifier. + */ +#define M_FLAG_BITS \ + "\20\1M_EXT\2M_PKTHDR\3M_EOR\4M_RDONLY\5M_BCAST\6M_MCAST" \ + "\7M_PROMISC\10M_VLANTAG\11M_FLOWID" +#define M_FLAG_PROTOBITS \ + "\15M_PROTO1\16M_PROTO2\17M_PROTO3\20M_PROTO4\21M_PROTO5" \ + "\22M_PROTO6\23M_PROTO7\24M_PROTO8\25M_PROTO9\26M_PROTO10" \ + "\27M_PROTO11\30M_PROTO12" +#define M_FLAG_PRINTF (M_FLAG_BITS M_FLAG_PROTOBITS) /* * Network interface cards are able to hash protocol fields (such as IPv4 @@ -230,34 +278,49 @@ struct mbuf { * that provide an opaque flow identifier, allowing for ordering and * distribution without explicit affinity. */ -#define M_HASHTYPE_SHIFT 24 -#define M_HASHTYPE_NONE 0x0 -#define M_HASHTYPE_RSS_IPV4 0x1 /* IPv4 2-tuple */ -#define M_HASHTYPE_RSS_TCP_IPV4 0x2 /* TCPv4 4-tuple */ -#define M_HASHTYPE_RSS_IPV6 0x3 /* IPv6 2-tuple */ -#define M_HASHTYPE_RSS_TCP_IPV6 0x4 /* TCPv6 4-tuple */ -#define M_HASHTYPE_RSS_IPV6_EX 0x5 /* IPv6 2-tuple + ext hdrs */ -#define M_HASHTYPE_RSS_TCP_IPV6_EX 0x6 /* TCPv6 4-tiple + ext hdrs */ -#define M_HASHTYPE_OPAQUE 0xf /* ordering, not affinity */ +#define M_HASHTYPE_NONE 0 +#define M_HASHTYPE_RSS_IPV4 1 /* IPv4 2-tuple */ +#define M_HASHTYPE_RSS_TCP_IPV4 2 /* TCPv4 4-tuple */ +#define M_HASHTYPE_RSS_IPV6 3 /* IPv6 2-tuple */ +#define M_HASHTYPE_RSS_TCP_IPV6 4 /* TCPv6 4-tuple */ +#define M_HASHTYPE_RSS_IPV6_EX 5 /* IPv6 2-tuple + ext hdrs */ +#define M_HASHTYPE_RSS_TCP_IPV6_EX 6 /* TCPv6 4-tiple + ext hdrs */ +#define M_HASHTYPE_OPAQUE 255 /* ordering, not affinity */ -#define M_HASHTYPE_CLEAR(m) (m)->m_flags &= ~(M_HASHTYPEBITS) -#define M_HASHTYPE_GET(m) (((m)->m_flags & M_HASHTYPEBITS) >> \ - M_HASHTYPE_SHIFT) -#define M_HASHTYPE_SET(m, v) do { \ - (m)->m_flags &= ~M_HASHTYPEBITS; \ - (m)->m_flags |= ((v) << M_HASHTYPE_SHIFT); \ -} while (0) +#define M_HASHTYPE_CLEAR(m) ((m)->m_pkthdr.rsstype = 0) +#define M_HASHTYPE_GET(m) ((m)->m_pkthdr.rsstype) +#define M_HASHTYPE_SET(m, v) ((m)->m_pkthdr.rsstype = (v)) #define M_HASHTYPE_TEST(m, v) (M_HASHTYPE_GET(m) == (v)) /* - * Flags preserved when copying m_pkthdr. + * COS/QOS class and quality of service tags. + * It uses DSCP code points as base. */ -#define M_COPYFLAGS \ - (M_PKTHDR|M_EOR|M_RDONLY|M_PROTOFLAGS|M_SKIP_FIREWALL|M_BCAST|M_MCAST|\ - M_FRAG|M_FIRSTFRAG|M_LASTFRAG|M_VLANTAG|M_PROMISC|M_HASHTYPEBITS) +#define QOS_DSCP_CS0 0x00 +#define QOS_DSCP_DEF QOS_DSCP_CS0 +#define QOS_DSCP_CS1 0x20 +#define QOS_DSCP_AF11 0x28 +#define QOS_DSCP_AF12 0x30 +#define QOS_DSCP_AF13 0x38 +#define QOS_DSCP_CS2 0x40 +#define QOS_DSCP_AF21 0x48 +#define QOS_DSCP_AF22 0x50 +#define QOS_DSCP_AF23 0x58 +#define QOS_DSCP_CS3 0x60 +#define QOS_DSCP_AF31 0x68 +#define QOS_DSCP_AF32 0x70 +#define QOS_DSCP_AF33 0x78 +#define QOS_DSCP_CS4 0x80 +#define QOS_DSCP_AF41 0x88 +#define QOS_DSCP_AF42 0x90 +#define QOS_DSCP_AF43 0x98 +#define QOS_DSCP_CS5 0xa0 +#define QOS_DSCP_EF 0xb8 +#define QOS_DSCP_CS6 0xc0 +#define QOS_DSCP_CS7 0xe0 /* - * External buffer types: identify ext_buf type. + * External mbuf storage buffer types. */ #define EXT_CLUSTER 1 /* mbuf cluster */ #define EXT_SFBUF 2 /* sendfile(2)'s sf_bufs */ @@ -266,47 +329,135 @@ struct mbuf { #define EXT_JUMBO16 5 /* jumbo cluster 16184 bytes */ #define EXT_PACKET 6 /* mbuf+cluster from packet zone */ #define EXT_MBUF 7 /* external mbuf reference (M_IOVEC) */ -#define EXT_NET_DRV 100 /* custom ext_buf provided by net driver(s) */ -#define EXT_MOD_TYPE 200 /* custom module's ext_buf type */ -#define EXT_DISPOSABLE 300 /* can throw this buffer away w/page flipping */ -#define EXT_EXTREF 400 /* has externally maintained ref_cnt ptr */ + +#define EXT_VENDOR1 224 /* for vendor-internal use */ +#define EXT_VENDOR2 225 /* for vendor-internal use */ +#define EXT_VENDOR3 226 /* for vendor-internal use */ +#define EXT_VENDOR4 227 /* for vendor-internal use */ + +#define EXT_EXP1 244 /* for experimental use */ +#define EXT_EXP2 245 /* for experimental use */ +#define EXT_EXP3 246 /* for experimental use */ +#define EXT_EXP4 247 /* for experimental use */ + +#define EXT_NET_DRV 252 /* custom ext_buf provided by net driver(s) */ +#define EXT_MOD_TYPE 253 /* custom module's ext_buf type */ +#define EXT_DISPOSABLE 254 /* can throw this buffer away w/page flipping */ +#define EXT_EXTREF 255 /* has externally maintained ref_cnt ptr */ /* - * Flags indicating hw checksum support and sw checksum requirements. This - * field can be directly tested against if_data.ifi_hwassist. + * Flags for external mbuf buffer types. + * NB: limited to the lower 24 bits. */ -#define CSUM_IP 0x0001 /* will csum IP */ -#define CSUM_TCP 0x0002 /* will csum TCP */ -#define CSUM_UDP 0x0004 /* will csum UDP */ -#define CSUM_FRAGMENT 0x0010 /* will do IP fragmentation */ -#define CSUM_TSO 0x0020 /* will do TSO */ -#define CSUM_SCTP 0x0040 /* will csum SCTP */ -#define CSUM_SCTP_IPV6 0x0080 /* will csum IPv6/SCTP */ +#define EXT_FLAG_EMBREF 0x000001 /* embedded ref_cnt, notyet */ +#define EXT_FLAG_EXTREF 0x000002 /* external ref_cnt, notyet */ +#define EXT_FLAG_NOFREE 0x000010 /* don't free mbuf to pool, notyet */ -#define CSUM_IP_CHECKED 0x0100 /* did csum IP */ -#define CSUM_IP_VALID 0x0200 /* ... the csum is valid */ -#define CSUM_DATA_VALID 0x0400 /* csum_data field is valid */ -#define CSUM_PSEUDO_HDR 0x0800 /* csum_data has pseudo hdr */ -#define CSUM_SCTP_VALID 0x1000 /* SCTP checksum is valid */ -#define CSUM_UDP_IPV6 0x2000 /* will csum IPv6/UDP */ -#define CSUM_TCP_IPV6 0x4000 /* will csum IPv6/TCP */ -/* CSUM_TSO_IPV6 0x8000 will do IPv6/TSO */ +#define EXT_FLAG_VENDOR1 0x010000 /* for vendor-internal use */ +#define EXT_FLAG_VENDOR2 0x020000 /* for vendor-internal use */ +#define EXT_FLAG_VENDOR3 0x040000 /* for vendor-internal use */ +#define EXT_FLAG_VENDOR4 0x080000 /* for vendor-internal use */ -/* CSUM_FRAGMENT_IPV6 0x10000 will do IPv6 fragementation */ - -#define CSUM_DELAY_DATA_IPV6 (CSUM_TCP_IPV6 | CSUM_UDP_IPV6) -#define CSUM_DATA_VALID_IPV6 CSUM_DATA_VALID - -#define CSUM_DELAY_DATA (CSUM_TCP | CSUM_UDP) -#define CSUM_DELAY_IP (CSUM_IP) /* Only v4, no v6 IP hdr csum */ +#define EXT_FLAG_EXP1 0x100000 /* for experimental use */ +#define EXT_FLAG_EXP2 0x200000 /* for experimental use */ +#define EXT_FLAG_EXP3 0x400000 /* for experimental use */ +#define EXT_FLAG_EXP4 0x800000 /* for experimental use */ /* - * mbuf types. + * EXT flag description for use with printf(9) %b identifier. + */ +#define EXT_FLAG_BITS \ + "\20\1EXT_FLAG_EMBREF\2EXT_FLAG_EXTREF\5EXT_FLAG_NOFREE" \ + "\21EXT_FLAG_VENDOR1\22EXT_FLAG_VENDOR2\23EXT_FLAG_VENDOR3" \ + "\24EXT_FLAG_VENDOR4\25EXT_FLAG_EXP1\26EXT_FLAG_EXP2\27EXT_FLAG_EXP3" \ + "\30EXT_FLAG_EXP4" + +/* + * Return values for (*ext_free). + */ +#define EXT_FREE_OK 0 /* Normal return */ + +/* + * Flags indicating checksum, segmentation and other offload work to be + * done, or already done, by hardware or lower layers. It is split into + * separate inbound and outbound flags. + * + * Outbound flags that are set by upper protocol layers requesting lower + * layers, or ideally the hardware, to perform these offloading tasks. + * For outbound packets this field and its flags can be directly tested + * against if_data.ifi_hwassist. + */ +#define CSUM_IP 0x00000001 /* IP header checksum offload */ +#define CSUM_IP_UDP 0x00000002 /* UDP checksum offload */ +#define CSUM_IP_TCP 0x00000004 /* TCP checksum offload */ +#define CSUM_IP_SCTP 0x00000008 /* SCTP checksum offload */ +#define CSUM_IP_TSO 0x00000010 /* TCP segmentation offload */ +#define CSUM_IP_ISCSI 0x00000020 /* iSCSI checksum offload */ + +#define CSUM_IP6_UDP 0x00000200 /* UDP checksum offload */ +#define CSUM_IP6_TCP 0x00000400 /* TCP checksum offload */ +#define CSUM_IP6_SCTP 0x00000800 /* SCTP checksum offload */ +#define CSUM_IP6_TSO 0x00001000 /* TCP segmentation offload */ +#define CSUM_IP6_ISCSI 0x00002000 /* iSCSI checksum offload */ + +/* Inbound checksum support where the checksum was verified by hardware. */ +#define CSUM_L3_CALC 0x01000000 /* calculated layer 3 csum */ +#define CSUM_L3_VALID 0x02000000 /* checksum is correct */ +#define CSUM_L4_CALC 0x04000000 /* calculated layer 4 csum */ +#define CSUM_L4_VALID 0x08000000 /* checksum is correct */ +#define CSUM_L5_CALC 0x10000000 /* calculated layer 5 csum */ +#define CSUM_L5_VALID 0x20000000 /* checksum is correct */ +#define CSUM_COALESED 0x40000000 /* contains merged segments */ + +/* + * CSUM flag description for use with printf(9) %b identifier. + */ +#define CSUM_BITS \ + "\20\1CSUM_IP\2CSUM_IP_UDP\3CSUM_IP_TCP\4CSUM_IP_SCTP\5CSUM_IP_TSO" \ + "\6CSUM_IP_ISCSI" \ + "\12CSUM_IP6_UDP\13CSUM_IP6_TCP\14CSUM_IP6_SCTP\15CSUM_IP6_TSO" \ + "\16CSUM_IP6_ISCSI" \ + "\31CSUM_L3_CALC\32CSUM_L3_VALID\33CSUM_L4_CALC\34CSUM_L4_VALID" \ + "\35CSUM_L5_CALC\36CSUM_L5_VALID\37CSUM_COALESED" + +/* CSUM flags compatibility mappings. */ +#define CSUM_IP_CHECKED CSUM_L3_CALC +#define CSUM_IP_VALID CSUM_L3_VALID +#define CSUM_DATA_VALID CSUM_L4_VALID +#define CSUM_PSEUDO_HDR CSUM_L4_CALC +#define CSUM_SCTP_VALID CSUM_L4_VALID +#define CSUM_DELAY_DATA (CSUM_TCP|CSUM_UDP) +#define CSUM_DELAY_IP CSUM_IP /* Only v4, no v6 IP hdr csum */ +#define CSUM_DELAY_DATA_IPV6 (CSUM_TCP_IPV6|CSUM_UDP_IPV6) +#define CSUM_DATA_VALID_IPV6 CSUM_DATA_VALID +#define CSUM_TCP CSUM_IP_TCP +#define CSUM_UDP CSUM_IP_UDP +#define CSUM_SCTP CSUM_IP_SCTP +#define CSUM_TSO (CSUM_IP_TSO|CSUM_IP6_TSO) +#define CSUM_UDP_IPV6 CSUM_IP6_UDP +#define CSUM_TCP_IPV6 CSUM_IP6_TCP +#define CSUM_SCTP_IPV6 CSUM_IP6_SCTP +#define CSUM_FRAGMENT 0x0 /* Unused */ + +/* + * mbuf types describing the content of the mbuf (including external storage). */ #define MT_NOTMBUF 0 /* USED INTERNALLY ONLY! Object is not mbuf */ #define MT_DATA 1 /* dynamic (data) allocation */ #define MT_HEADER MT_DATA /* packet header, use M_PKTHDR instead */ + +#define MT_VENDOR1 4 /* for vendor-internal use */ +#define MT_VENDOR2 5 /* for vendor-internal use */ +#define MT_VENDOR3 6 /* for vendor-internal use */ +#define MT_VENDOR4 7 /* for vendor-internal use */ + #define MT_SONAME 8 /* socket name */ + +#define MT_EXP1 9 /* for experimental use */ +#define MT_EXP2 10 /* for experimental use */ +#define MT_EXP3 11 /* for experimental use */ +#define MT_EXP4 12 /* for experimental use */ + #define MT_CONTROL 14 /* extra-data protocol message */ #define MT_OOBDATA 15 /* expedited data */ #define MT_NTYPES 16 /* number of mbuf types for mbtypes[] */ @@ -314,8 +465,6 @@ struct mbuf { #define MT_NOINIT 255 /* Not a type but a flag to allocate a non-initialized mbuf */ -#define MB_NOTAGS 0x1UL /* no tags attached to mbuf */ - /* * Compatibility with historic mbuf allocator. */ @@ -396,6 +545,28 @@ m_gettype(int size) return (type); } +/* + * Associated an external reference counted buffer with an mbuf. + */ +static __inline void +m_extaddref(struct mbuf *m, caddr_t buf, u_int size, u_int *ref_cnt, + int (*freef)(struct mbuf *, void *, void *), void *arg1, void *arg2) +{ + + KASSERT(ref_cnt != NULL, ("%s: ref_cnt not provided", __func__)); + + atomic_add_int(ref_cnt, 1); + m->m_flags |= M_EXT; + m->m_ext.ext_buf = buf; + m->m_ext.ref_cnt = ref_cnt; + m->m_data = m->m_ext.ext_buf; + m->m_ext.ext_size = size; + m->m_ext.ext_free = freef; + m->m_ext.ext_arg1 = arg1; + m->m_ext.ext_arg2 = arg2; + m->m_ext.ext_type = EXT_EXTREF; +} + static __inline uma_zone_t m_getzone(int size) { @@ -497,29 +668,6 @@ m_getcl(int how, short type, int flags) return (uma_zalloc_arg(zone_pack, &args, how)); } -static __inline void -m_free_fast(struct mbuf *m) -{ -#ifdef INVARIANTS - if (m->m_flags & M_PKTHDR) - KASSERT(SLIST_EMPTY(&m->m_pkthdr.tags), ("doing fast free of mbuf with tags")); -#endif - - uma_zfree_arg(zone_mbuf, m, (void *)MB_NOTAGS); -} - -static __inline struct mbuf * -m_free(struct mbuf *m) -{ - struct mbuf *n = m->m_next; - - if (m->m_flags & M_EXT) - mb_free_ext(m); - else if ((m->m_flags & M_NOFREE) == 0) - uma_zfree(zone_mbuf, m); - return (n); -} - static __inline void m_clget(struct mbuf *m, int how) { @@ -593,6 +741,7 @@ m_cljset(struct mbuf *m, void *cl, int type) m->m_ext.ext_free = m->m_ext.ext_arg1 = m->m_ext.ext_arg2 = NULL; m->m_ext.ext_size = size; m->m_ext.ext_type = type; + m->m_ext.ext_flags = 0; m->m_ext.ref_cnt = uma_find_refcnt(zone, cl); m->m_flags |= M_EXT; @@ -605,6 +754,13 @@ m_chtype(struct mbuf *m, short new_type) m->m_type = new_type; } +static __inline void +m_clrprotoflags(struct mbuf *m) +{ + + m->m_flags &= ~M_PROTOFLAGS; +} + static __inline struct mbuf * m_last(struct mbuf *m) { @@ -761,7 +917,8 @@ int m_apply(struct mbuf *, int, int, int m_append(struct mbuf *, int, c_caddr_t); void m_cat(struct mbuf *, struct mbuf *); int m_extadd(struct mbuf *, caddr_t, u_int, - void (*)(void *, void *), void *, void *, int, int, int); + int (*)(struct mbuf *, void *, void *), void *, void *, + int, int, int); struct mbuf *m_collapse(struct mbuf *, int, int); void m_copyback(struct mbuf *, int, int, c_caddr_t); void m_copydata(const struct mbuf *, int, int, caddr_t); @@ -977,6 +1134,20 @@ m_tag_find(struct mbuf *m, int type, struct m_tag *start) m_tag_locate(m, MTAG_ABI_COMPAT, type, start)); } +static __inline struct mbuf * +m_free(struct mbuf *m) +{ + struct mbuf *n = m->m_next; + + if ((m->m_flags & (M_PKTHDR|M_NOFREE)) == (M_PKTHDR|M_NOFREE)) + m_tag_delete_chain(m, NULL); + if (m->m_flags & M_EXT) + mb_free_ext(m); + else if ((m->m_flags & M_NOFREE) == 0) + uma_zfree(zone_mbuf, m); + return (n); +} + static int inline rt_m_getfib(struct mbuf *m) { diff --git a/sys/sys/mman.h b/sys/sys/mman.h index a178d7cb3d8..eaea818d151 100644 --- a/sys/sys/mman.h +++ b/sys/sys/mman.h @@ -91,6 +91,20 @@ */ #define MAP_NOCORE 0x00020000 /* dont include these pages in a coredump */ #define MAP_PREFAULT_READ 0x00040000 /* prefault mapping for reading */ +#ifdef __LP64__ +#define MAP_32BIT 0x00080000 /* map in the low 2GB of address space */ +#endif + +/* + * Request specific alignment (n == log2 of the desired alignment). + * + * MAP_ALIGNED_SUPER requests optimal superpage alignment, but does + * not enforce a specific alignment. + */ +#define MAP_ALIGNED(n) ((n) << MAP_ALIGNMENT_SHIFT) +#define MAP_ALIGNMENT_SHIFT 24 +#define MAP_ALIGNMENT_MASK MAP_ALIGNED(0xff) +#define MAP_ALIGNED_SUPER MAP_ALIGNED(1) /* align on a superpage */ #endif /* __BSD_VISIBLE */ #if __POSIX_VISIBLE >= 199309 @@ -179,6 +193,10 @@ typedef __size_t size_t; #endif #if defined(_KERNEL) || defined(_WANT_FILE) +#include +#include +#include +#include #include struct file; @@ -203,6 +221,9 @@ struct shmfd { struct label *shm_label; /* MAC label */ const char *shm_path; + + struct rangelock shm_rl; + struct mtx shm_mtx; }; #endif diff --git a/sys/sys/mount.h b/sys/sys/mount.h index a953dae420c..8f9445154b0 100644 --- a/sys/sys/mount.h +++ b/sys/sys/mount.h @@ -609,6 +609,7 @@ typedef int vfs_sysctl_t(struct mount *mp, fsctlop_t op, struct sysctl_req *req); typedef void vfs_susp_clean_t(struct mount *mp); typedef void vfs_notify_lowervp_t(struct mount *mp, struct vnode *lowervp); +typedef void vfs_purge_t(struct mount *mp); struct vfsops { vfs_mount_t *vfs_mount; @@ -628,6 +629,8 @@ struct vfsops { vfs_susp_clean_t *vfs_susp_clean; vfs_notify_lowervp_t *vfs_reclaim_lowervp; vfs_notify_lowervp_t *vfs_unlink_lowervp; + vfs_purge_t *vfs_purge; + vfs_mount_t *vfs_spare[6]; /* spares for ABI compat */ }; vfs_statfs_t __vfs_statfs; @@ -756,6 +759,14 @@ vfs_statfs_t __vfs_statfs; } \ } while (0) +#define VFS_PURGE(MP) do { \ + if (*(MP)->mnt_op->vfs_purge != NULL) { \ + VFS_PROLOGUE(MP); \ + (*(MP)->mnt_op->vfs_purge)(MP); \ + VFS_EPILOGUE(MP); \ + } \ +} while (0) + #define VFS_KNOTE_LOCKED(vp, hint) do \ { \ if (((vp)->v_vflag & VV_NOKNOTE) == 0) \ diff --git a/sys/sys/mouse.h b/sys/sys/mouse.h index e0b70ffdb52..e03bc40cdfd 100644 --- a/sys/sys/mouse.h +++ b/sys/sys/mouse.h @@ -107,6 +107,9 @@ typedef struct synapticshw { int capMultiFinger; int capPalmDetect; int capPassthrough; + int capMiddle; + int nExtendedButtons; + int nExtendedQueries; } synapticshw_t; /* iftype */ diff --git a/sys/sys/namei.h b/sys/sys/namei.h index a9992f43c68..f1b1223b6ca 100644 --- a/sys/sys/namei.h +++ b/sys/sys/namei.h @@ -33,6 +33,7 @@ #ifndef _SYS_NAMEI_H_ #define _SYS_NAMEI_H_ +#include #include #include #include @@ -158,32 +159,14 @@ struct nameidata { NDINIT_ALL(ndp, op, flags, segflg, namep, AT_FDCWD, NULL, 0, td) #define NDINIT_AT(ndp, op, flags, segflg, namep, dirfd, td) \ NDINIT_ALL(ndp, op, flags, segflg, namep, dirfd, NULL, 0, td) -#define NDINIT_ATRIGHTS(ndp, op, flags, segflg, namep, dirfd, rights, td) \ - NDINIT_ALL(ndp, op, flags, segflg, namep, dirfd, NULL, rights, td) +#define NDINIT_ATRIGHTS(ndp, op, flags, segflg, namep, dirfd, rightsp, td) \ + NDINIT_ALL(ndp, op, flags, segflg, namep, dirfd, NULL, rightsp, td) #define NDINIT_ATVP(ndp, op, flags, segflg, namep, vp, td) \ NDINIT_ALL(ndp, op, flags, segflg, namep, AT_FDCWD, vp, 0, td) -static __inline void -NDINIT_ALL(struct nameidata *ndp, - u_long op, u_long flags, - enum uio_seg segflg, - const char *namep, - int dirfd, - struct vnode *startdir, - cap_rights_t rights, - struct thread *td) -{ - ndp->ni_cnd.cn_nameiop = op; - ndp->ni_cnd.cn_flags = flags; - ndp->ni_segflg = segflg; - ndp->ni_dirp = namep; - ndp->ni_dirfd = dirfd; - ndp->ni_startdir = startdir; - ndp->ni_strictrelative = 0; - ndp->ni_rightsneeded = rights; - filecaps_init(&ndp->ni_filecaps); - ndp->ni_cnd.cn_thread = td; -} +void NDINIT_ALL(struct nameidata *ndp, u_long op, u_long flags, + enum uio_seg segflg, const char *namep, int dirfd, struct vnode *startdir, + cap_rights_t *rightsp, struct thread *td); #define NDF_NO_DVP_RELE 0x00000001 #define NDF_NO_DVP_UNLOCK 0x00000002 diff --git a/sys/sys/param.h b/sys/sys/param.h index d425fdc6fa7..b29ecb39231 100644 --- a/sys/sys/param.h +++ b/sys/sys/param.h @@ -58,7 +58,7 @@ * in the range 5 to 9. */ #undef __FreeBSD_version -#define __FreeBSD_version 1000041 /* Master, propagated to newvers */ +#define __FreeBSD_version 1000055 /* Master, propagated to newvers */ /* * __FreeBSD_kernel__ indicates that this system uses the kernel of FreeBSD, diff --git a/sys/sys/pmckern.h b/sys/sys/pmckern.h index e3e18a6fdb7..70b9b3f9c3c 100644 --- a/sys/sys/pmckern.h +++ b/sys/sys/pmckern.h @@ -51,8 +51,8 @@ #define PMC_FN_CSW_IN 2 #define PMC_FN_CSW_OUT 3 #define PMC_FN_DO_SAMPLES 4 -#define PMC_FN_KLD_LOAD 5 -#define PMC_FN_KLD_UNLOAD 6 +#define PMC_FN_UNUSED1 5 +#define PMC_FN_UNUSED2 6 #define PMC_FN_MMAP 7 #define PMC_FN_MUNMAP 8 #define PMC_FN_USER_CALLCHAIN 9 diff --git a/sys/sys/proc.h b/sys/sys/proc.h index 0ef225a9826..2f03152de69 100644 --- a/sys/sys/proc.h +++ b/sys/sys/proc.h @@ -892,7 +892,6 @@ int setrunnable(struct thread *); void setsugid(struct proc *p); int should_yield(void); int sigonstack(size_t sp); -void sleepinit(void); void stopevent(struct proc *, u_int, u_int); struct thread *tdfind(lwpid_t, pid_t); void threadinit(void); diff --git a/sys/sys/procdesc.h b/sys/sys/procdesc.h index cc8b7166f63..4d77a056403 100644 --- a/sys/sys/procdesc.h +++ b/sys/sys/procdesc.h @@ -92,8 +92,8 @@ struct procdesc { * In-kernel interfaces to process descriptors. */ int procdesc_exit(struct proc *); -int procdesc_find(struct thread *, int fd, cap_rights_t, struct proc **); -int kern_pdgetpid(struct thread *, int fd, cap_rights_t, pid_t *pidp); +int procdesc_find(struct thread *, int fd, cap_rights_t *, struct proc **); +int kern_pdgetpid(struct thread *, int fd, cap_rights_t *, pid_t *pidp); void procdesc_new(struct proc *, int); void procdesc_finit(struct procdesc *, struct file *); pid_t procdesc_pid(struct file *); diff --git a/sys/sys/random.h b/sys/sys/random.h index 5cf1611e359..2f86c82e854 100644 --- a/sys/sys/random.h +++ b/sys/sys/random.h @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2000 Mark R. V. Murray + * Copyright (c) 2000-2013 Mark R. V. Murray * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -42,8 +42,11 @@ enum esource { RANDOM_WRITE = 0, RANDOM_KEYBOARD, RANDOM_MOUSE, - RANDOM_NET, + RANDOM_NET_TUN, + RANDOM_NET_ETHER, + RANDOM_NET_NG, RANDOM_INTERRUPT, + RANDOM_SWI, RANDOM_PURE, ENTROPYSOURCE }; @@ -57,6 +60,7 @@ struct harvest_select { int point_to_point; int interrupt; int swi; + int namei; }; extern struct harvest_select harvest; diff --git a/sys/sys/rangelock.h b/sys/sys/rangelock.h index fb0d2523eee..2a904c098d8 100644 --- a/sys/sys/rangelock.h +++ b/sys/sys/rangelock.h @@ -48,9 +48,13 @@ struct rl_q_entry; * Access to the structure itself is synchronized with the externally * supplied mutex. * - * rl_waiters is the queue of lock requests in the order of arrival. + * rl_waiters is the queue containing in order (a) granted write lock + * requests, (b) granted read lock requests, and (c) in order of arrival, + * lock requests which cannot be granted yet. + * * rl_currdep is the first lock request that cannot be granted now due - * to the preceding requests conflicting with it. + * to the preceding requests conflicting with it (i.e., it points to + * position (c) in the list above). */ struct rangelock { TAILQ_HEAD(, rl_q_entry) rl_waiters; diff --git a/sys/sys/sdt.h b/sys/sys/sdt.h index 9c3684151fd..820f4bba0ca 100644 --- a/sys/sys/sdt.h +++ b/sys/sys/sdt.h @@ -77,6 +77,9 @@ #else /* _KERNEL */ +#include +#include + #ifndef KDTRACE_HOOKS #define SDT_PROVIDER_DEFINE(prov) @@ -84,7 +87,7 @@ #define SDT_PROBE_DEFINE(prov, mod, func, name, sname) #define SDT_PROBE_DECLARE(prov, mod, func, name) #define SDT_PROBE(prov, mod, func, name, arg0, arg1, arg2, arg3, arg4) -#define SDT_PROBE_ARGTYPE(prov, mod, func, name, num, type) +#define SDT_PROBE_ARGTYPE(prov, mod, func, name, num, type, xtype) #define SDT_PROBE_DEFINE0(prov, mod, func, name, sname) #define SDT_PROBE_DEFINE1(prov, mod, func, name, sname, arg0) @@ -107,87 +110,44 @@ #define SDT_PROBE7(prov, mod, func, name, arg0, arg1, arg2, arg3, arg4, arg5, \ arg6) +#define SDT_PROBE_DEFINE0_XLATE(prov, mod, func, name, sname) +#define SDT_PROBE_DEFINE1_XLATE(prov, mod, func, name, sname, arg0, xarg0) +#define SDT_PROBE_DEFINE2_XLATE(prov, mod, func, name, sname, arg0, xarg0, \ + arg1, xarg1) +#define SDT_PROBE_DEFINE3_XLATE(prov, mod, func, name, sname, arg0, xarg0, \ + arg1, xarg1, arg2, xarg2) +#define SDT_PROBE_DEFINE4_XLATE(prov, mod, func, name, sname, arg0, xarg0, \ + arg1, xarg1, arg2, xarg2, arg3, xarg3) +#define SDT_PROBE_DEFINE5_XLATE(prov, mod, func, name, sname, arg0, xarg0, \ + arg1, xarg1, arg2, xarg2, arg3, xarg3, arg4, xarg4) +#define SDT_PROBE_DEFINE6_XLATE(prov, mod, func, name, sname, arg0, xarg0, \ + arg1, xarg1, arg2, xarg2, arg3, xarg3, arg4, xarg4, arg5, xarg5) +#define SDT_PROBE_DEFINE7_XLATE(prov, mod, func, name, sname, arg0, xarg0, \ + arg1, xarg1, arg2, xarg2, arg3, xarg3, arg4, xarg4, arg5, xarg5, arg6, \ + xarg6) + #else -/* - * This type definition must match that of dtrace_probe. It is defined this - * way to avoid having to rely on CDDL code. - */ -typedef void (*sdt_probe_func_t)(u_int32_t, uintptr_t arg0, uintptr_t arg1, - uintptr_t arg2, uintptr_t arg3, uintptr_t arg4); - -/* - * The hook for the probe function. See kern_sdt.c which defaults this to - * it's own stub. The 'sdt' provider will set it to dtrace_probe when it - * loads. - */ -extern sdt_probe_func_t sdt_probe_func; - -typedef enum { - SDT_UNINIT = 1, - SDT_INIT, -} sdt_state_t; - -struct sdt_probe; -struct sdt_provider; - -struct sdt_argtype { - int ndx; /* Argument index. */ - const char *type; /* Argument type string. */ - TAILQ_ENTRY(sdt_argtype) - argtype_entry; /* Argument type list entry. */ - struct sdt_probe - *probe; /* Ptr to the probe structure. */ -}; - -struct sdt_probe { - int version; /* Set to sizeof(struct sdt_ref). */ - sdt_state_t state; - struct sdt_provider - *prov; /* Ptr to the provider structure. */ - TAILQ_ENTRY(sdt_probe) - probe_entry; /* SDT probe list entry. */ - TAILQ_HEAD(argtype_list_head, sdt_argtype) argtype_list; - const char *mod; - const char *func; - const char *name; - id_t id; /* DTrace probe ID. */ - int n_args; /* Number of arguments. */ -}; - -struct sdt_provider { - const char *name; /* Provider name. */ - TAILQ_ENTRY(sdt_provider) - prov_entry; /* SDT provider list entry. */ - TAILQ_HEAD(probe_list_head, sdt_probe) probe_list; - uintptr_t id; /* DTrace provider ID. */ -}; +SET_DECLARE(sdt_providers_set, struct sdt_provider); +SET_DECLARE(sdt_probes_set, struct sdt_probe); +SET_DECLARE(sdt_argtypes_set, struct sdt_argtype); #define SDT_PROVIDER_DEFINE(prov) \ struct sdt_provider sdt_provider_##prov[1] = { \ - { #prov, { NULL, NULL }, { NULL, NULL } } \ + { #prov, { NULL, NULL }, { NULL, NULL }, 0, 0 } \ }; \ - SYSINIT(sdt_provider_##prov##_init, SI_SUB_KDTRACE, \ - SI_ORDER_SECOND, sdt_provider_register, \ - sdt_provider_##prov ); \ - SYSUNINIT(sdt_provider_##prov##_uninit, SI_SUB_KDTRACE, \ - SI_ORDER_SECOND, sdt_provider_deregister, \ - sdt_provider_##prov ) + DATA_SET(sdt_providers_set, sdt_provider_##prov); #define SDT_PROVIDER_DECLARE(prov) \ extern struct sdt_provider sdt_provider_##prov[1] #define SDT_PROBE_DEFINE(prov, mod, func, name, sname) \ struct sdt_probe sdt_##prov##_##mod##_##func##_##name[1] = { \ - { sizeof(struct sdt_probe), 0, sdt_provider_##prov, \ - { NULL, NULL }, { NULL, NULL }, #mod, #func, #sname, 0, 0 } \ + { sizeof(struct sdt_probe), sdt_provider_##prov, \ + { NULL, NULL }, { NULL, NULL }, #mod, #func, #sname, 0, 0, \ + NULL } \ }; \ - SYSINIT(sdt_##prov##_##mod##_##func##_##name##_init, SI_SUB_KDTRACE, \ - SI_ORDER_SECOND + 1, sdt_probe_register, \ - sdt_##prov##_##mod##_##func##_##name ); \ - SYSUNINIT(sdt_##prov##_##mod##_##func##_##name##_uninit, \ - SI_SUB_KDTRACE, SI_ORDER_SECOND + 1, sdt_probe_deregister, \ - sdt_##prov##_##mod##_##func##_##name ) + DATA_SET(sdt_probes_set, sdt_##prov##_##mod##_##func##_##name); #define SDT_PROBE_DECLARE(prov, mod, func, name) \ extern struct sdt_probe sdt_##prov##_##mod##_##func##_##name[1] @@ -199,71 +159,125 @@ struct sdt_provider { (uintptr_t) arg3, (uintptr_t) arg4); \ } while (0) -#define SDT_PROBE_ARGTYPE(prov, mod, func, name, num, type) \ +#define SDT_PROBE_ARGTYPE(prov, mod, func, name, num, type, xtype) \ static struct sdt_argtype sdt_##prov##_##mod##_##func##_##name##num[1] \ - = { { num, type, { NULL, NULL }, \ + = { { num, type, xtype, { NULL, NULL }, \ sdt_##prov##_##mod##_##func##_##name } \ }; \ - SYSINIT(sdt_##prov##_##mod##_##func##_##name##num##_init, \ - SI_SUB_KDTRACE, SI_ORDER_SECOND + 2, sdt_argtype_register, \ - sdt_##prov##_##mod##_##func##_##name##num ); \ - SYSUNINIT(sdt_##prov##_##mod##_##func##_##name##num##_uninit, \ - SI_SUB_KDTRACE, SI_ORDER_SECOND + 2, sdt_argtype_deregister, \ - sdt_##prov##_##mod##_##func##_##name##num ) + DATA_SET(sdt_argtypes_set, sdt_##prov##_##mod##_##func##_##name##num); #define SDT_PROBE_DEFINE0(prov, mod, func, name, sname) \ SDT_PROBE_DEFINE(prov, mod, func, name, sname) #define SDT_PROBE_DEFINE1(prov, mod, func, name, sname, arg0) \ SDT_PROBE_DEFINE(prov, mod, func, name, sname); \ - SDT_PROBE_ARGTYPE(prov, mod, func, name, 0, arg0) + SDT_PROBE_ARGTYPE(prov, mod, func, name, 0, arg0, NULL) #define SDT_PROBE_DEFINE2(prov, mod, func, name, sname, arg0, arg1) \ SDT_PROBE_DEFINE(prov, mod, func, name, sname); \ - SDT_PROBE_ARGTYPE(prov, mod, func, name, 0, arg0); \ - SDT_PROBE_ARGTYPE(prov, mod, func, name, 1, arg1) + SDT_PROBE_ARGTYPE(prov, mod, func, name, 0, arg0, NULL); \ + SDT_PROBE_ARGTYPE(prov, mod, func, name, 1, arg1, NULL) #define SDT_PROBE_DEFINE3(prov, mod, func, name, sname, arg0, arg1, arg2)\ SDT_PROBE_DEFINE(prov, mod, func, name, sname); \ - SDT_PROBE_ARGTYPE(prov, mod, func, name, 0, arg0); \ - SDT_PROBE_ARGTYPE(prov, mod, func, name, 1, arg1); \ - SDT_PROBE_ARGTYPE(prov, mod, func, name, 2, arg2) + SDT_PROBE_ARGTYPE(prov, mod, func, name, 0, arg0, NULL); \ + SDT_PROBE_ARGTYPE(prov, mod, func, name, 1, arg1, NULL); \ + SDT_PROBE_ARGTYPE(prov, mod, func, name, 2, arg2, NULL) #define SDT_PROBE_DEFINE4(prov, mod, func, name, sname, arg0, arg1, arg2, arg3) \ SDT_PROBE_DEFINE(prov, mod, func, name, sname); \ - SDT_PROBE_ARGTYPE(prov, mod, func, name, 0, arg0); \ - SDT_PROBE_ARGTYPE(prov, mod, func, name, 1, arg1); \ - SDT_PROBE_ARGTYPE(prov, mod, func, name, 2, arg2); \ - SDT_PROBE_ARGTYPE(prov, mod, func, name, 3, arg3) + SDT_PROBE_ARGTYPE(prov, mod, func, name, 0, arg0, NULL); \ + SDT_PROBE_ARGTYPE(prov, mod, func, name, 1, arg1, NULL); \ + SDT_PROBE_ARGTYPE(prov, mod, func, name, 2, arg2, NULL); \ + SDT_PROBE_ARGTYPE(prov, mod, func, name, 3, arg3, NULL) #define SDT_PROBE_DEFINE5(prov, mod, func, name, sname, arg0, arg1, arg2, arg3, arg4) \ SDT_PROBE_DEFINE(prov, mod, func, name, sname); \ - SDT_PROBE_ARGTYPE(prov, mod, func, name, 0, arg0); \ - SDT_PROBE_ARGTYPE(prov, mod, func, name, 1, arg1); \ - SDT_PROBE_ARGTYPE(prov, mod, func, name, 2, arg2); \ - SDT_PROBE_ARGTYPE(prov, mod, func, name, 3, arg3); \ - SDT_PROBE_ARGTYPE(prov, mod, func, name, 4, arg4) + SDT_PROBE_ARGTYPE(prov, mod, func, name, 0, arg0, NULL); \ + SDT_PROBE_ARGTYPE(prov, mod, func, name, 1, arg1, NULL); \ + SDT_PROBE_ARGTYPE(prov, mod, func, name, 2, arg2, NULL); \ + SDT_PROBE_ARGTYPE(prov, mod, func, name, 3, arg3, NULL); \ + SDT_PROBE_ARGTYPE(prov, mod, func, name, 4, arg4, NULL) #define SDT_PROBE_DEFINE6(prov, mod, func, name, sname, arg0, arg1, arg2, arg3,\ arg4, arg5) \ SDT_PROBE_DEFINE(prov, mod, func, name, sname); \ - SDT_PROBE_ARGTYPE(prov, mod, func, name, 0, arg0); \ - SDT_PROBE_ARGTYPE(prov, mod, func, name, 1, arg1); \ - SDT_PROBE_ARGTYPE(prov, mod, func, name, 2, arg2); \ - SDT_PROBE_ARGTYPE(prov, mod, func, name, 3, arg3); \ - SDT_PROBE_ARGTYPE(prov, mod, func, name, 4, arg4); \ - SDT_PROBE_ARGTYPE(prov, mod, func, name, 5, arg5); + SDT_PROBE_ARGTYPE(prov, mod, func, name, 0, arg0, NULL); \ + SDT_PROBE_ARGTYPE(prov, mod, func, name, 1, arg1, NULL); \ + SDT_PROBE_ARGTYPE(prov, mod, func, name, 2, arg2, NULL); \ + SDT_PROBE_ARGTYPE(prov, mod, func, name, 3, arg3, NULL); \ + SDT_PROBE_ARGTYPE(prov, mod, func, name, 4, arg4, NULL); \ + SDT_PROBE_ARGTYPE(prov, mod, func, name, 5, arg5, NULL) #define SDT_PROBE_DEFINE7(prov, mod, func, name, sname, arg0, arg1, arg2, arg3,\ arg4, arg5, arg6) \ SDT_PROBE_DEFINE(prov, mod, func, name, sname); \ - SDT_PROBE_ARGTYPE(prov, mod, func, name, 0, arg0); \ - SDT_PROBE_ARGTYPE(prov, mod, func, name, 1, arg1); \ - SDT_PROBE_ARGTYPE(prov, mod, func, name, 2, arg2); \ - SDT_PROBE_ARGTYPE(prov, mod, func, name, 3, arg3); \ - SDT_PROBE_ARGTYPE(prov, mod, func, name, 4, arg4); \ - SDT_PROBE_ARGTYPE(prov, mod, func, name, 5, arg5); \ - SDT_PROBE_ARGTYPE(prov, mod, func, name, 6, arg6); + SDT_PROBE_ARGTYPE(prov, mod, func, name, 0, arg0, NULL); \ + SDT_PROBE_ARGTYPE(prov, mod, func, name, 1, arg1, NULL); \ + SDT_PROBE_ARGTYPE(prov, mod, func, name, 2, arg2, NULL); \ + SDT_PROBE_ARGTYPE(prov, mod, func, name, 3, arg3, NULL); \ + SDT_PROBE_ARGTYPE(prov, mod, func, name, 4, arg4, NULL); \ + SDT_PROBE_ARGTYPE(prov, mod, func, name, 5, arg5, NULL); \ + SDT_PROBE_ARGTYPE(prov, mod, func, name, 6, arg6, NULL) + +#define SDT_PROBE_DEFINE0_XLATE(prov, mod, func, name, sname) \ + SDT_PROBE_DEFINE(prov, mod, func, name, sname) + +#define SDT_PROBE_DEFINE1_XLATE(prov, mod, func, name, sname, arg0, xarg0) \ + SDT_PROBE_DEFINE(prov, mod, func, name, sname); \ + SDT_PROBE_ARGTYPE(prov, mod, func, name, 0, arg0, xarg0) + +#define SDT_PROBE_DEFINE2_XLATE(prov, mod, func, name, sname, arg0, xarg0, \ + arg1, xarg1) \ + SDT_PROBE_DEFINE(prov, mod, func, name, sname); \ + SDT_PROBE_ARGTYPE(prov, mod, func, name, 0, arg0, xarg0); \ + SDT_PROBE_ARGTYPE(prov, mod, func, name, 1, arg1, xarg1) + +#define SDT_PROBE_DEFINE3_XLATE(prov, mod, func, name, sname, arg0, xarg0, \ + arg1, xarg1, arg2, xarg2) \ + SDT_PROBE_DEFINE(prov, mod, func, name, sname); \ + SDT_PROBE_ARGTYPE(prov, mod, func, name, 0, arg0, xarg0); \ + SDT_PROBE_ARGTYPE(prov, mod, func, name, 1, arg1, xarg1); \ + SDT_PROBE_ARGTYPE(prov, mod, func, name, 2, arg2, xarg2) + +#define SDT_PROBE_DEFINE4_XLATE(prov, mod, func, name, sname, arg0, xarg0, \ + arg1, xarg1, arg2, xarg2, arg3, xarg3) \ + SDT_PROBE_DEFINE(prov, mod, func, name, sname); \ + SDT_PROBE_ARGTYPE(prov, mod, func, name, 0, arg0, xarg0); \ + SDT_PROBE_ARGTYPE(prov, mod, func, name, 1, arg1, xarg1); \ + SDT_PROBE_ARGTYPE(prov, mod, func, name, 2, arg2, xarg2); \ + SDT_PROBE_ARGTYPE(prov, mod, func, name, 3, arg3, xarg3) + +#define SDT_PROBE_DEFINE5_XLATE(prov, mod, func, name, sname, arg0, xarg0, \ + arg1, xarg1, arg2, xarg2, arg3, xarg3, arg4, xarg4) \ + SDT_PROBE_DEFINE(prov, mod, func, name, sname); \ + SDT_PROBE_ARGTYPE(prov, mod, func, name, 0, arg0, xarg0); \ + SDT_PROBE_ARGTYPE(prov, mod, func, name, 1, arg1, xarg1); \ + SDT_PROBE_ARGTYPE(prov, mod, func, name, 2, arg2, xarg2); \ + SDT_PROBE_ARGTYPE(prov, mod, func, name, 3, arg3, xarg3); \ + SDT_PROBE_ARGTYPE(prov, mod, func, name, 4, arg4, xarg4) + +#define SDT_PROBE_DEFINE6_XLATE(prov, mod, func, name, sname, arg0, xarg0, \ + arg1, xarg1, arg2, xarg2, arg3, xarg3, arg4, xarg4, arg5, xarg5) \ + SDT_PROBE_DEFINE(prov, mod, func, name, sname); \ + SDT_PROBE_ARGTYPE(prov, mod, func, name, 0, arg0, xarg0); \ + SDT_PROBE_ARGTYPE(prov, mod, func, name, 1, arg1, xarg1); \ + SDT_PROBE_ARGTYPE(prov, mod, func, name, 2, arg2, xarg2); \ + SDT_PROBE_ARGTYPE(prov, mod, func, name, 3, arg3, xarg3); \ + SDT_PROBE_ARGTYPE(prov, mod, func, name, 4, arg4, xarg4); \ + SDT_PROBE_ARGTYPE(prov, mod, func, name, 5, arg5, xarg5) + +#define SDT_PROBE_DEFINE7_XLATE(prov, mod, func, name, sname, arg0, xarg0, \ + arg1, xarg1, arg2, xarg2, arg3, xarg3, arg4, xarg4, arg5, xarg5, arg6, \ + xarg6) \ + SDT_PROBE_DEFINE(prov, mod, func, name, sname); \ + SDT_PROBE_ARGTYPE(prov, mod, func, name, 0, arg0, xarg0); \ + SDT_PROBE_ARGTYPE(prov, mod, func, name, 1, arg1, xarg1); \ + SDT_PROBE_ARGTYPE(prov, mod, func, name, 2, arg2, xarg2); \ + SDT_PROBE_ARGTYPE(prov, mod, func, name, 3, arg3, xarg3); \ + SDT_PROBE_ARGTYPE(prov, mod, func, name, 4, arg4, xarg4); \ + SDT_PROBE_ARGTYPE(prov, mod, func, name, 5, arg5, xarg5); \ + SDT_PROBE_ARGTYPE(prov, mod, func, name, 6, arg6, xarg6) #define SDT_PROBE0(prov, mod, func, name) \ SDT_PROBE(prov, mod, func, name, 0, 0, 0, 0, 0) @@ -299,28 +313,59 @@ struct sdt_provider { (uintptr_t)arg6); \ } while (0) -typedef int (*sdt_argtype_listall_func_t)(struct sdt_argtype *, void *); -typedef int (*sdt_probe_listall_func_t)(struct sdt_probe *, void *); -typedef int (*sdt_provider_listall_func_t)(struct sdt_provider *, void *); - -void sdt_argtype_deregister(void *); -void sdt_argtype_register(void *); -void sdt_probe_deregister(void *); -void sdt_probe_register(void *); -void sdt_provider_deregister(void *); -void sdt_provider_register(void *); -void sdt_probe_stub(u_int32_t, uintptr_t arg0, uintptr_t arg1, uintptr_t arg2, - uintptr_t arg3, uintptr_t arg4); -int sdt_argtype_listall(struct sdt_probe *, sdt_argtype_listall_func_t, void *); -int sdt_probe_listall(struct sdt_provider *, sdt_probe_listall_func_t, void *); -int sdt_provider_listall(sdt_provider_listall_func_t,void *); - -void sdt_register_callbacks(sdt_provider_listall_func_t, void *, - sdt_provider_listall_func_t, void *, sdt_probe_listall_func_t, void *); -void sdt_deregister_callbacks(void); - #endif /* KDTRACE_HOOKS */ +/* + * This type definition must match that of dtrace_probe. It is defined this + * way to avoid having to rely on CDDL code. + */ +typedef void (*sdt_probe_func_t)(uint32_t, uintptr_t arg0, uintptr_t arg1, + uintptr_t arg2, uintptr_t arg3, uintptr_t arg4); + +/* + * The 'sdt' provider will set it to dtrace_probe when it loads. + */ +extern sdt_probe_func_t sdt_probe_func; + +struct sdt_probe; +struct sdt_provider; +struct linker_file; + +struct sdt_argtype { + int ndx; /* Argument index. */ + const char *type; /* Argument type string. */ + const char *xtype; /* Translated argument type. */ + TAILQ_ENTRY(sdt_argtype) + argtype_entry; /* Argument type list entry. */ + struct sdt_probe *probe; /* Ptr to the probe structure. */ +}; + +struct sdt_probe { + int version; /* Set to sizeof(struct sdt_probe). */ + struct sdt_provider *prov; /* Ptr to the provider structure. */ + TAILQ_ENTRY(sdt_probe) + probe_entry; /* SDT probe list entry. */ + TAILQ_HEAD(argtype_list_head, sdt_argtype) argtype_list; + const char *mod; + const char *func; + const char *name; + id_t id; /* DTrace probe ID. */ + int n_args; /* Number of arguments. */ + struct linker_file *sdtp_lf; /* Module in which we're defined. */ +}; + +struct sdt_provider { + char *name; /* Provider name. */ + TAILQ_ENTRY(sdt_provider) + prov_entry; /* SDT provider list entry. */ + TAILQ_HEAD(probe_list_head, sdt_probe) probe_list; + uintptr_t id; /* DTrace provider ID. */ + int sdt_refs; /* Number of module references. */ +}; + +void sdt_probe_stub(uint32_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, + uintptr_t); + #endif /* _KERNEL */ #endif /* _SYS_SDT_H */ diff --git a/sys/sys/sf_buf.h b/sys/sys/sf_buf.h index 1b62fd32fff..2e85bc87a9e 100644 --- a/sys/sys/sf_buf.h +++ b/sys/sys/sf_buf.h @@ -54,7 +54,9 @@ struct sfstat { /* sendfile statistics */ #ifdef _KERNEL #include +#include #include +struct mbuf; /* for sf_buf_mext() */ extern counter_u64_t sfstat[sizeof(struct sfstat) / sizeof(uint64_t)]; #define SFSTAT_ADD(name, val) \ @@ -63,9 +65,6 @@ extern counter_u64_t sfstat[sizeof(struct sfstat) / sizeof(uint64_t)]; #define SFSTAT_INC(name) SFSTAT_ADD(name, 1) #endif /* _KERNEL */ -struct sf_buf * - sf_buf_alloc(struct vm_page *m, int flags); -void sf_buf_free(struct sf_buf *sf); -void sf_buf_mext(void *addr, void *args); +int sf_buf_mext(struct mbuf *mb, void *addr, void *args); #endif /* !_SYS_SF_BUF_H_ */ diff --git a/sys/sys/sockbuf.h b/sys/sys/sockbuf.h index bfccd74f6f7..402a8f0749f 100644 --- a/sys/sys/sockbuf.h +++ b/sys/sys/sockbuf.h @@ -97,7 +97,7 @@ struct sockbuf { u_int sb_mbmax; /* (c/d) max chars of mbufs to use */ u_int sb_ctl; /* (c/d) non-data chars in buffer */ int sb_lowat; /* (c/d) low water mark */ - int sb_timeo; /* (c/d) timeout for read/write */ + sbintime_t sb_timeo; /* (c/d) timeout for read/write */ short sb_flags; /* (c/d) flags, see below */ int (*sb_upcall)(struct socket *, void *, int); /* (c/d) */ void *sb_upcallarg; /* (c/d) */ diff --git a/sys/sys/socket.h b/sys/sys/socket.h index 0ff04f5438c..952454aea00 100644 --- a/sys/sys/socket.h +++ b/sys/sys/socket.h @@ -230,7 +230,9 @@ struct accept_filter_arg { #define AF_ARP 35 #define AF_BLUETOOTH 36 /* Bluetooth sockets */ #define AF_IEEE80211 37 /* IEEE 802.11 protocol */ -#define AF_MAX 38 +#define AF_INET_SDP 40 /* OFED Socket Direct Protocol ipv4 */ +#define AF_INET6_SDP 42 /* OFED Socket Direct Protocol ipv6 */ +#define AF_MAX 42 /* * When allocating a new AF_ constant, please only allocate * even numbered constants for FreeBSD until 134 as odd numbered AF_ @@ -353,6 +355,8 @@ struct sockproto { #define PF_ARP AF_ARP #define PF_BLUETOOTH AF_BLUETOOTH #define PF_IEEE80211 AF_IEEE80211 +#define PF_INET_SDP AF_INET_SDP +#define PF_INET6_SDP AF_INET6_SDP #define PF_MAX AF_MAX @@ -366,44 +370,6 @@ struct sockproto { */ #define NET_MAXID AF_MAX -#define CTL_NET_NAMES { \ - { 0, 0 }, \ - { "unix", CTLTYPE_NODE }, \ - { "inet", CTLTYPE_NODE }, \ - { "implink", CTLTYPE_NODE }, \ - { "pup", CTLTYPE_NODE }, \ - { "chaos", CTLTYPE_NODE }, \ - { "xerox_ns", CTLTYPE_NODE }, \ - { "iso", CTLTYPE_NODE }, \ - { "emca", CTLTYPE_NODE }, \ - { "datakit", CTLTYPE_NODE }, \ - { "ccitt", CTLTYPE_NODE }, \ - { "ibm_sna", CTLTYPE_NODE }, \ - { "decnet", CTLTYPE_NODE }, \ - { "dec_dli", CTLTYPE_NODE }, \ - { "lat", CTLTYPE_NODE }, \ - { "hylink", CTLTYPE_NODE }, \ - { "appletalk", CTLTYPE_NODE }, \ - { "route", CTLTYPE_NODE }, \ - { "link_layer", CTLTYPE_NODE }, \ - { "xtp", CTLTYPE_NODE }, \ - { "coip", CTLTYPE_NODE }, \ - { "cnt", CTLTYPE_NODE }, \ - { "rtip", CTLTYPE_NODE }, \ - { "ipx", CTLTYPE_NODE }, \ - { "sip", CTLTYPE_NODE }, \ - { "pip", CTLTYPE_NODE }, \ - { "isdn", CTLTYPE_NODE }, \ - { "key", CTLTYPE_NODE }, \ - { "inet6", CTLTYPE_NODE }, \ - { "natm", CTLTYPE_NODE }, \ - { "atm", CTLTYPE_NODE }, \ - { "hdrcomplete", CTLTYPE_NODE }, \ - { "netgraph", CTLTYPE_NODE }, \ - { "snp", CTLTYPE_NODE }, \ - { "scp", CTLTYPE_NODE }, \ -} - /* * PF_ROUTE - Routing table * @@ -420,14 +386,6 @@ struct sockproto { * versions of msghdr structs. */ #define NET_RT_MAXID 6 -#define CTL_NET_RT_NAMES { \ - { 0, 0 }, \ - { "dump", CTLTYPE_STRUCT }, \ - { "flags", CTLTYPE_STRUCT }, \ - { "iflist", CTLTYPE_STRUCT }, \ - { "ifmalist", CTLTYPE_STRUCT }, \ - { "iflistl", CTLTYPE_STRUCT }, \ -} #endif /* __BSD_VISIBLE */ /* @@ -624,7 +582,11 @@ struct sf_hdtr { #define SF_NODISKIO 0x00000001 #define SF_MNOWAIT 0x00000002 #define SF_SYNC 0x00000004 -#endif + +#ifdef _KERNEL +#define SFK_COMPAT 0x00000001 +#endif /* _KERNEL */ +#endif /* __BSD_VISIBLE */ #ifndef _KERNEL diff --git a/sys/sys/socketvar.h b/sys/sys/socketvar.h index 89dec187cc9..90903406156 100644 --- a/sys/sys/socketvar.h +++ b/sys/sys/socketvar.h @@ -325,7 +325,6 @@ int soconnect(struct socket *so, struct sockaddr *nam, struct thread *td); int soconnectat(int fd, struct socket *so, struct sockaddr *nam, struct thread *td); int soconnect2(struct socket *so1, struct socket *so2); -int socow_setup(struct mbuf *m0, struct uio *uio); int socreate(int dom, struct socket **aso, int type, int proto, struct ucred *cred, struct thread *td); int sodisconnect(struct socket *so); diff --git a/sys/sys/stat.h b/sys/sys/stat.h index f430a9a1de9..0f3284f9791 100644 --- a/sys/sys/stat.h +++ b/sys/sys/stat.h @@ -265,8 +265,26 @@ struct nstat { #define UF_NODUMP 0x00000001 /* do not dump file */ #define UF_IMMUTABLE 0x00000002 /* file may not be changed */ #define UF_APPEND 0x00000004 /* writes to file may only append */ -#define UF_OPAQUE 0x00000008 /* directory is opaque wrt. union */ -#define UF_NOUNLINK 0x00000010 /* file may not be removed or renamed */ +#define UF_OPAQUE 0x00000008 /* directory is opaque wrt. union */ +#define UF_NOUNLINK 0x00000010 /* file may not be removed or renamed */ +/* + * These two bits are defined in MacOS X. They are not currently used in + * FreeBSD. + */ +#if 0 +#define UF_COMPRESSED 0x00000020 /* file is compressed */ +#define UF_TRACKED 0x00000040 /* renames and deletes are tracked */ +#endif + +#define UF_SYSTEM 0x00000080 /* Windows system file bit */ +#define UF_SPARSE 0x00000100 /* sparse file */ +#define UF_OFFLINE 0x00000200 /* file is offline */ +#define UF_REPARSE 0x00000400 /* Windows reparse point file bit */ +#define UF_ARCHIVE 0x00000800 /* file needs to be archived */ +#define UF_READONLY 0x00001000 /* Windows readonly file bit */ +/* This is the same as the MacOS X definition of UF_HIDDEN. */ +#define UF_HIDDEN 0x00008000 /* file is hidden */ + /* * Super-user changeable flags. */ diff --git a/sys/sys/stdatomic.h b/sys/sys/stdatomic.h index 054ded6d817..b55d3888c26 100644 --- a/sys/sys/stdatomic.h +++ b/sys/sys/stdatomic.h @@ -288,8 +288,8 @@ typedef _Atomic(__uintmax_t) atomic_uintmax_t; __typeof__(expected) __ep = (expected); \ __typeof__(*__ep) __e = *__ep; \ (void)(success); (void)(failure); \ - (*__ep = __sync_val_compare_and_swap(&(object)->__val, \ - __e, desired)) == __e; \ + (_Bool)((*__ep = __sync_val_compare_and_swap(&(object)->__val, \ + __e, desired)) == __e); \ }) #define atomic_compare_exchange_weak_explicit(object, expected, \ desired, success, failure) \ @@ -381,11 +381,7 @@ static __inline _Bool atomic_flag_test_and_set_explicit(volatile atomic_flag *__object, memory_order __order) { - _Bool __expected; - - __expected = 0; - return (atomic_compare_exchange_strong_explicit(&__object->__flag, - &__expected, 1, __order, __order)); + return (atomic_exchange_explicit(&__object->__flag, 1, __order)); } static __inline void diff --git a/sys/sys/syscall.h b/sys/sys/syscall.h index b3214164a74..e7b9b1329d5 100644 --- a/sys/sys/syscall.h +++ b/sys/sys/syscall.h @@ -3,7 +3,7 @@ * * DO NOT EDIT-- this file is automatically generated. * $FreeBSD$ - * created from FreeBSD: head/sys/kern/syscalls.master 251526 2013-06-08 13:27:57Z glebius + * created from FreeBSD: head/sys/kern/syscalls.master 255490 2013-09-12 17:52:18Z jhb */ #define SYS_syscall 0 @@ -434,8 +434,8 @@ #define SYS_msgctl 511 #define SYS_shmctl 512 #define SYS_lpathconf 513 -#define SYS_cap_new 514 -#define SYS_cap_rights_get 515 + /* 514 is obsolete cap_new */ +#define SYS___cap_rights_get 515 #define SYS_cap_enter 516 #define SYS_cap_getmode 517 #define SYS_pdfork 518 diff --git a/sys/sys/syscall.mk b/sys/sys/syscall.mk index df7d19f9a53..73834607a8b 100644 --- a/sys/sys/syscall.mk +++ b/sys/sys/syscall.mk @@ -1,7 +1,7 @@ # FreeBSD system call names. # DO NOT EDIT-- this file is automatically generated. # $FreeBSD$ -# created from FreeBSD: head/sys/kern/syscalls.master 251526 2013-06-08 13:27:57Z glebius +# created from FreeBSD: head/sys/kern/syscalls.master 255490 2013-09-12 17:52:18Z jhb MIASM = \ syscall.o \ exit.o \ @@ -383,8 +383,7 @@ MIASM = \ msgctl.o \ shmctl.o \ lpathconf.o \ - cap_new.o \ - cap_rights_get.o \ + __cap_rights_get.o \ cap_enter.o \ cap_getmode.o \ pdfork.o \ diff --git a/sys/sys/syscallsubr.h b/sys/sys/syscallsubr.h index e18c7354ca1..17f2b97db9c 100644 --- a/sys/sys/syscallsubr.h +++ b/sys/sys/syscallsubr.h @@ -71,6 +71,8 @@ int kern_adjtime(struct thread *td, struct timeval *delta, int kern_alternate_path(struct thread *td, const char *prefix, const char *path, enum uio_seg pathseg, char **pathbuf, int create, int dirfd); int kern_bind(struct thread *td, int fd, struct sockaddr *sa); +int kern_cap_ioctls_limit(struct thread *td, int fd, u_long *cmds, + size_t ncmds); int kern_chdir(struct thread *td, char *path, enum uio_seg pathseg); int kern_chmod(struct thread *td, char *path, enum uio_seg pathseg, int mode); diff --git a/sys/sys/sysctl.h b/sys/sys/sysctl.h index 2fdeed22fb5..64292baccc3 100644 --- a/sys/sys/sysctl.h +++ b/sys/sys/sysctl.h @@ -48,7 +48,7 @@ struct thread; * respective subsystem header files. */ -#define CTL_MAXNAME 24 /* largest number of components supported */ +#define CTL_MAXNAME 24 /* largest number of components supported */ /* * Each subsystem defined by sysctl defines a list of variables @@ -59,10 +59,10 @@ struct thread; */ struct ctlname { char *ctl_name; /* subsystem name */ - int ctl_type; /* type of name */ + int ctl_type; /* type of name */ }; -#define CTLTYPE 0xf /* Mask for the type */ +#define CTLTYPE 0xf /* mask for the type */ #define CTLTYPE_NODE 1 /* name is a node */ #define CTLTYPE_INT 2 /* name describes an integer */ #define CTLTYPE_STRING 3 /* name describes a string */ @@ -74,35 +74,35 @@ struct ctlname { #define CTLTYPE_ULONG 8 /* name describes an unsigned long */ #define CTLTYPE_U64 9 /* name describes an unsigned 64-bit number */ -#define CTLFLAG_RD 0x80000000 /* Allow reads of variable */ -#define CTLFLAG_WR 0x40000000 /* Allow writes to the variable */ -#define CTLFLAG_RW (CTLFLAG_RD|CTLFLAG_WR) -#define CTLFLAG_ANYBODY 0x10000000 /* All users can set this var */ -#define CTLFLAG_SECURE 0x08000000 /* Permit set only if securelevel<=0 */ -#define CTLFLAG_PRISON 0x04000000 /* Prisoned roots can fiddle */ -#define CTLFLAG_DYN 0x02000000 /* Dynamic oid - can be freed */ -#define CTLFLAG_SKIP 0x01000000 /* Skip this sysctl when listing */ -#define CTLMASK_SECURE 0x00F00000 /* Secure level */ -#define CTLFLAG_TUN 0x00080000 /* Tunable variable */ +#define CTLFLAG_RD 0x80000000 /* Allow reads of variable */ +#define CTLFLAG_WR 0x40000000 /* Allow writes to the variable */ +#define CTLFLAG_RW (CTLFLAG_RD|CTLFLAG_WR) +#define CTLFLAG_ANYBODY 0x10000000 /* All users can set this var */ +#define CTLFLAG_SECURE 0x08000000 /* Permit set only if securelevel<=0 */ +#define CTLFLAG_PRISON 0x04000000 /* Prisoned roots can fiddle */ +#define CTLFLAG_DYN 0x02000000 /* Dynamic oid - can be freed */ +#define CTLFLAG_SKIP 0x01000000 /* Skip this sysctl when listing */ +#define CTLMASK_SECURE 0x00F00000 /* Secure level */ +#define CTLFLAG_TUN 0x00080000 /* Tunable variable */ #define CTLFLAG_RDTUN (CTLFLAG_RD|CTLFLAG_TUN) #define CTLFLAG_RWTUN (CTLFLAG_RW|CTLFLAG_TUN) -#define CTLFLAG_MPSAFE 0x00040000 /* Handler is MP safe */ -#define CTLFLAG_VNET 0x00020000 /* Prisons with vnet can fiddle */ -#define CTLFLAG_DYING 0x00010000 /* oid is being removed */ -#define CTLFLAG_CAPRD 0x00008000 /* Can be read in capability mode */ -#define CTLFLAG_CAPWR 0x00004000 /* Can be written in capability mode */ -#define CTLFLAG_STATS 0x00002000 /* Statistics, not a tuneable */ -#define CTLFLAG_CAPRW (CTLFLAG_CAPRD|CTLFLAG_CAPWR) +#define CTLFLAG_MPSAFE 0x00040000 /* Handler is MP safe */ +#define CTLFLAG_VNET 0x00020000 /* Prisons with vnet can fiddle */ +#define CTLFLAG_DYING 0x00010000 /* Oid is being removed */ +#define CTLFLAG_CAPRD 0x00008000 /* Can be read in capability mode */ +#define CTLFLAG_CAPWR 0x00004000 /* Can be written in capability mode */ +#define CTLFLAG_STATS 0x00002000 /* Statistics, not a tuneable */ +#define CTLFLAG_CAPRW (CTLFLAG_CAPRD|CTLFLAG_CAPWR) /* - * Secure level. Note that CTLFLAG_SECURE == CTLFLAG_SECURE1. + * Secure level. Note that CTLFLAG_SECURE == CTLFLAG_SECURE1. * * Secure when the securelevel is raised to at least N. */ -#define CTLSHIFT_SECURE 20 -#define CTLFLAG_SECURE1 (CTLFLAG_SECURE | (0 << CTLSHIFT_SECURE)) -#define CTLFLAG_SECURE2 (CTLFLAG_SECURE | (1 << CTLSHIFT_SECURE)) -#define CTLFLAG_SECURE3 (CTLFLAG_SECURE | (2 << CTLSHIFT_SECURE)) +#define CTLSHIFT_SECURE 20 +#define CTLFLAG_SECURE1 (CTLFLAG_SECURE | (0 << CTLSHIFT_SECURE)) +#define CTLFLAG_SECURE2 (CTLFLAG_SECURE | (1 << CTLSHIFT_SECURE)) +#define CTLFLAG_SECURE3 (CTLFLAG_SECURE | (2 << CTLSHIFT_SECURE)) /* * USE THIS instead of a hardwired number from the categories below @@ -110,19 +110,19 @@ struct ctlname { * technology. This is the way nearly all new sysctl variables should * be implemented. * e.g. SYSCTL_INT(_parent, OID_AUTO, name, CTLFLAG_RW, &variable, 0, ""); - */ -#define OID_AUTO (-1) + */ +#define OID_AUTO (-1) /* * The starting number for dynamically-assigned entries. WARNING! * ALL static sysctl entries should have numbers LESS than this! */ -#define CTL_AUTO_START 0x100 +#define CTL_AUTO_START 0x100 #ifdef _KERNEL #include -#define SYSCTL_HANDLER_ARGS struct sysctl_oid *oidp, void *arg1, \ +#define SYSCTL_HANDLER_ARGS struct sysctl_oid *oidp, void *arg1, \ intptr_t arg2, struct sysctl_req *req /* definitions for sysctl_req 'lock' member */ @@ -130,8 +130,8 @@ struct ctlname { #define REQ_WIRED 2 /* definitions for sysctl_req 'flags' member */ -#if defined(__amd64__) || defined(__ia64__) || defined(__powerpc64__) || \ - (defined(__mips__) && defined(__mips_n64)) +#if defined(__amd64__) || defined(__ia64__) || defined(__powerpc64__) ||\ + (defined(__mips__) && defined(__mips_n64)) #define SCTL_MASK32 1 /* 32 bit emulation */ #endif @@ -141,17 +141,17 @@ struct ctlname { */ struct sysctl_req { struct thread *td; /* used for access checking */ - int lock; /* wiring state */ + int lock; /* wiring state */ void *oldptr; - size_t oldlen; - size_t oldidx; + size_t oldlen; + size_t oldidx; int (*oldfunc)(struct sysctl_req *, const void *, size_t); void *newptr; - size_t newlen; - size_t newidx; + size_t newlen; + size_t newidx; int (*newfunc)(struct sysctl_req *, void *, size_t); - size_t validlen; - int flags; + size_t validlen; + int flags; }; SLIST_HEAD(sysctl_oid_list, sysctl_oid); @@ -163,20 +163,20 @@ SLIST_HEAD(sysctl_oid_list, sysctl_oid); struct sysctl_oid { struct sysctl_oid_list *oid_parent; SLIST_ENTRY(sysctl_oid) oid_link; - int oid_number; - u_int oid_kind; + int oid_number; + u_int oid_kind; void *oid_arg1; - intptr_t oid_arg2; + intptr_t oid_arg2; const char *oid_name; - int (*oid_handler)(SYSCTL_HANDLER_ARGS); + int (*oid_handler)(SYSCTL_HANDLER_ARGS); const char *oid_fmt; - int oid_refcnt; - u_int oid_running; + int oid_refcnt; + u_int oid_running; const char *oid_descr; }; -#define SYSCTL_IN(r, p, l) (r->newfunc)(r, p, l) -#define SYSCTL_OUT(r, p, l) (r->oldfunc)(r, p, l) +#define SYSCTL_IN(r, p, l) (r->newfunc)(r, p, l) +#define SYSCTL_OUT(r, p, l) (r->oldfunc)(r, p, l) int sysctl_handle_int(SYSCTL_HANDLER_ARGS); int sysctl_msec_to_ticks(SYSCTL_HANDLER_ARGS); @@ -197,18 +197,16 @@ void sysctl_register_oid(struct sysctl_oid *oidp); void sysctl_unregister_oid(struct sysctl_oid *oidp); /* Declare a static oid to allow child oids to be added to it. */ -#define SYSCTL_DECL(name) \ +#define SYSCTL_DECL(name) \ extern struct sysctl_oid_list sysctl_##name##_children -/* Hide these in macros */ -#define SYSCTL_CHILDREN(oid_ptr) (struct sysctl_oid_list *) \ - (oid_ptr)->oid_arg1 -#define SYSCTL_CHILDREN_SET(oid_ptr, val) \ - (oid_ptr)->oid_arg1 = (val); -#define SYSCTL_STATIC_CHILDREN(oid_name) \ - (&sysctl_##oid_name##_children) +/* Hide these in macros. */ +#define SYSCTL_CHILDREN(oid_ptr) \ + (struct sysctl_oid_list *)(oid_ptr)->oid_arg1 +#define SYSCTL_CHILDREN_SET(oid_ptr, val) (oid_ptr)->oid_arg1 = (val) +#define SYSCTL_STATIC_CHILDREN(oid_name) (&sysctl_##oid_name##_children) -/* === Structs and macros related to context handling === */ +/* === Structs and macros related to context handling. === */ /* All dynamically created sysctls can be tracked in a context list. */ struct sysctl_ctx_entry { @@ -218,7 +216,7 @@ struct sysctl_ctx_entry { TAILQ_HEAD(sysctl_ctx_list, sysctl_ctx_entry); -#define SYSCTL_NODE_CHILDREN(parent, name) \ +#define SYSCTL_NODE_CHILDREN(parent, name) \ sysctl_##parent##_##name##_children /* @@ -269,37 +267,48 @@ SYSCTL_ALLOWED_TYPES(UINT64, uint64_t *a; unsigned long long *b; ); struct __hack #ifndef NO_SYSCTL_DESCR -#define __DESCR(d) d +#define __DESCR(d) d #else -#define __DESCR(d) "" +#define __DESCR(d) "" #endif /* This constructs a "raw" MIB oid. */ -#define SYSCTL_OID(parent, nbr, name, kind, a1, a2, handler, fmt, descr) \ - static struct sysctl_oid sysctl__##parent##_##name = { \ - &sysctl_##parent##_children, { NULL }, nbr, kind, \ - a1, a2, #name, handler, fmt, 0, 0, __DESCR(descr) }; \ +#define SYSCTL_OID(parent, nbr, name, kind, a1, a2, handler, fmt, descr)\ + static struct sysctl_oid sysctl__##parent##_##name = { \ + &sysctl_##parent##_children, \ + { NULL }, \ + nbr, \ + kind, \ + a1, \ + a2, \ + #name, \ + handler, \ + fmt, \ + 0, \ + 0, \ + __DESCR(descr) \ + }; \ DATA_SET(sysctl_set, sysctl__##parent##_##name) -#define SYSCTL_ADD_OID(ctx, parent, nbr, name, kind, a1, a2, handler, fmt, descr) \ +#define SYSCTL_ADD_OID(ctx, parent, nbr, name, kind, a1, a2, handler, fmt, descr) \ sysctl_add_oid(ctx, parent, nbr, name, kind, a1, a2, handler, fmt, __DESCR(descr)) /* This constructs a node from which other oids can hang. */ -#define SYSCTL_NODE(parent, nbr, name, access, handler, descr) \ +#define SYSCTL_NODE(parent, nbr, name, access, handler, descr) \ struct sysctl_oid_list SYSCTL_NODE_CHILDREN(parent, name); \ SYSCTL_OID(parent, nbr, name, CTLTYPE_NODE|(access), \ (void*)&SYSCTL_NODE_CHILDREN(parent, name), 0, handler, "N", descr) -#define SYSCTL_ADD_NODE(ctx, parent, nbr, name, access, handler, descr) \ +#define SYSCTL_ADD_NODE(ctx, parent, nbr, name, access, handler, descr) \ sysctl_add_oid(ctx, parent, nbr, name, CTLTYPE_NODE|(access), \ NULL, 0, handler, "N", __DESCR(descr)) /* Oid for a string. len can be 0 to indicate '\0' termination. */ -#define SYSCTL_STRING(parent, nbr, name, access, arg, len, descr) \ +#define SYSCTL_STRING(parent, nbr, name, access, arg, len, descr) \ SYSCTL_OID(parent, nbr, name, CTLTYPE_STRING|(access), \ arg, len, sysctl_handle_string, "A", descr) -#define SYSCTL_ADD_STRING(ctx, parent, nbr, name, access, arg, len, descr) \ +#define SYSCTL_ADD_STRING(ctx, parent, nbr, name, access, arg, len, descr) \ sysctl_add_oid(ctx, parent, nbr, name, CTLTYPE_STRING|(access), \ arg, len, sysctl_handle_string, "A", __DESCR(descr)) @@ -380,7 +389,7 @@ SYSCTL_ALLOWED_TYPES(UINT64, uint64_t *a; unsigned long long *b; ); SYSCTL_ADD_ASSERT_TYPE(UINT64, ptr), 0, \ sysctl_handle_64, "QU", __DESCR(descr)) -/* Oid for a 64-bin unsigned counter(9). The pointer must be non NULL. */ +/* Oid for a 64-bit unsigned counter(9). The pointer must be non NULL. */ #define SYSCTL_COUNTER_U64(parent, nbr, name, access, ptr, val, descr) \ SYSCTL_ASSERT_TYPE(UINT64, ptr, parent, name); \ SYSCTL_OID(parent, nbr, name, \ @@ -394,31 +403,31 @@ SYSCTL_ALLOWED_TYPES(UINT64, uint64_t *a; unsigned long long *b; ); sysctl_handle_counter_u64, "QU", __DESCR(descr)) /* Oid for an opaque object. Specified by a pointer and a length. */ -#define SYSCTL_OPAQUE(parent, nbr, name, access, ptr, len, fmt, descr) \ +#define SYSCTL_OPAQUE(parent, nbr, name, access, ptr, len, fmt, descr) \ SYSCTL_OID(parent, nbr, name, CTLTYPE_OPAQUE|(access), \ ptr, len, sysctl_handle_opaque, fmt, descr) -#define SYSCTL_ADD_OPAQUE(ctx, parent, nbr, name, access, ptr, len, fmt, descr)\ +#define SYSCTL_ADD_OPAQUE(ctx, parent, nbr, name, access, ptr, len, fmt, descr)\ sysctl_add_oid(ctx, parent, nbr, name, CTLTYPE_OPAQUE|(access), \ ptr, len, sysctl_handle_opaque, fmt, __DESCR(descr)) /* Oid for a struct. Specified by a pointer and a type. */ -#define SYSCTL_STRUCT(parent, nbr, name, access, ptr, type, descr) \ +#define SYSCTL_STRUCT(parent, nbr, name, access, ptr, type, descr) \ SYSCTL_OID(parent, nbr, name, CTLTYPE_OPAQUE|(access), \ ptr, sizeof(struct type), sysctl_handle_opaque, \ "S," #type, descr) -#define SYSCTL_ADD_STRUCT(ctx, parent, nbr, name, access, ptr, type, descr) \ +#define SYSCTL_ADD_STRUCT(ctx, parent, nbr, name, access, ptr, type, descr) \ sysctl_add_oid(ctx, parent, nbr, name, CTLTYPE_OPAQUE|(access), \ ptr, sizeof(struct type), sysctl_handle_opaque, "S," #type, __DESCR(descr)) /* Oid for a procedure. Specified by a pointer and an arg. */ -#define SYSCTL_PROC(parent, nbr, name, access, ptr, arg, handler, fmt, descr) \ +#define SYSCTL_PROC(parent, nbr, name, access, ptr, arg, handler, fmt, descr) \ CTASSERT(((access) & CTLTYPE) != 0); \ SYSCTL_OID(parent, nbr, name, (access), \ ptr, arg, handler, fmt, descr) -#define SYSCTL_ADD_PROC(ctx, parent, nbr, name, access, ptr, arg, handler, fmt, descr) \ +#define SYSCTL_ADD_PROC(ctx, parent, nbr, name, access, ptr, arg, handler, fmt, descr) \ sysctl_add_oid(ctx, parent, nbr, name, (access), \ ptr, arg, handler, fmt, __DESCR(descr)) @@ -428,7 +437,7 @@ SYSCTL_ALLOWED_TYPES(UINT64, uint64_t *a; unsigned long long *b; ); */ #define FEATURE(name, desc) \ SYSCTL_INT(_kern_features, OID_AUTO, name, CTLFLAG_RD | CTLFLAG_CAPRD, \ - 0, 1, desc) + NULL, 1, desc) #endif /* _KERNEL */ @@ -447,31 +456,18 @@ SYSCTL_ALLOWED_TYPES(UINT64, uint64_t *a; unsigned long long *b; ); #define CTL_P1003_1B 9 /* POSIX 1003.1B */ #define CTL_MAXID 10 /* number of valid top-level ids */ -#define CTL_NAMES { \ - { 0, 0 }, \ - { "kern", CTLTYPE_NODE }, \ - { "vm", CTLTYPE_NODE }, \ - { "vfs", CTLTYPE_NODE }, \ - { "net", CTLTYPE_NODE }, \ - { "debug", CTLTYPE_NODE }, \ - { "hw", CTLTYPE_NODE }, \ - { "machdep", CTLTYPE_NODE }, \ - { "user", CTLTYPE_NODE }, \ - { "p1003_1b", CTLTYPE_NODE }, \ -} - /* * CTL_KERN identifiers */ -#define KERN_OSTYPE 1 /* string: system version */ -#define KERN_OSRELEASE 2 /* string: system release */ -#define KERN_OSREV 3 /* int: system revision */ -#define KERN_VERSION 4 /* string: compile time info */ -#define KERN_MAXVNODES 5 /* int: max vnodes */ -#define KERN_MAXPROC 6 /* int: max processes */ -#define KERN_MAXFILES 7 /* int: max open files */ -#define KERN_ARGMAX 8 /* int: max arguments to exec */ -#define KERN_SECURELVL 9 /* int: system security level */ +#define KERN_OSTYPE 1 /* string: system version */ +#define KERN_OSRELEASE 2 /* string: system release */ +#define KERN_OSREV 3 /* int: system revision */ +#define KERN_VERSION 4 /* string: compile time info */ +#define KERN_MAXVNODES 5 /* int: max vnodes */ +#define KERN_MAXPROC 6 /* int: max processes */ +#define KERN_MAXFILES 7 /* int: max open files */ +#define KERN_ARGMAX 8 /* int: max arguments to exec */ +#define KERN_SECURELVL 9 /* int: system security level */ #define KERN_HOSTNAME 10 /* string: hostname */ #define KERN_HOSTID 11 /* int: host identifier */ #define KERN_CLOCKRATE 12 /* struct: struct clockrate */ @@ -484,14 +480,14 @@ SYSCTL_ALLOWED_TYPES(UINT64, uint64_t *a; unsigned long long *b; ); #define KERN_JOB_CONTROL 19 /* int: is job control available */ #define KERN_SAVED_IDS 20 /* int: saved set-user/group-ID */ #define KERN_BOOTTIME 21 /* struct: time kernel was booted */ -#define KERN_NISDOMAINNAME 22 /* string: YP domain name */ -#define KERN_UPDATEINTERVAL 23 /* int: update process sleep time */ -#define KERN_OSRELDATE 24 /* int: kernel release date */ -#define KERN_NTP_PLL 25 /* node: NTP PLL control */ +#define KERN_NISDOMAINNAME 22 /* string: YP domain name */ +#define KERN_UPDATEINTERVAL 23 /* int: update process sleep time */ +#define KERN_OSRELDATE 24 /* int: kernel release date */ +#define KERN_NTP_PLL 25 /* node: NTP PLL control */ #define KERN_BOOTFILE 26 /* string: name of booted kernel */ #define KERN_MAXFILESPERPROC 27 /* int: max open files per proc */ -#define KERN_MAXPROCPERUID 28 /* int: max processes per uid */ -#define KERN_DUMPDEV 29 /* struct cdev *: device to dump on */ +#define KERN_MAXPROCPERUID 28 /* int: max processes per uid */ +#define KERN_DUMPDEV 29 /* struct cdev *: device to dump on */ #define KERN_IPC 30 /* node: anything related to IPC */ #define KERN_DUMMY 31 /* unused */ #define KERN_PS_STRINGS 32 /* int: address of PS_STRINGS */ @@ -501,58 +497,10 @@ SYSCTL_ALLOWED_TYPES(UINT64, uint64_t *a; unsigned long long *b; ); #define KERN_HOSTUUID 36 /* string: host UUID identifier */ #define KERN_ARND 37 /* int: from arc4rand() */ #define KERN_MAXID 38 /* number of valid kern ids */ - -#define CTL_KERN_NAMES { \ - { 0, 0 }, \ - { "ostype", CTLTYPE_STRING }, \ - { "osrelease", CTLTYPE_STRING }, \ - { "osrevision", CTLTYPE_INT }, \ - { "version", CTLTYPE_STRING }, \ - { "maxvnodes", CTLTYPE_INT }, \ - { "maxproc", CTLTYPE_INT }, \ - { "maxfiles", CTLTYPE_INT }, \ - { "argmax", CTLTYPE_INT }, \ - { "securelevel", CTLTYPE_INT }, \ - { "hostname", CTLTYPE_STRING }, \ - { "hostid", CTLTYPE_UINT }, \ - { "clockrate", CTLTYPE_STRUCT }, \ - { "vnode", CTLTYPE_STRUCT }, \ - { "proc", CTLTYPE_STRUCT }, \ - { "file", CTLTYPE_STRUCT }, \ - { "profiling", CTLTYPE_NODE }, \ - { "posix1version", CTLTYPE_INT }, \ - { "ngroups", CTLTYPE_INT }, \ - { "job_control", CTLTYPE_INT }, \ - { "saved_ids", CTLTYPE_INT }, \ - { "boottime", CTLTYPE_STRUCT }, \ - { "nisdomainname", CTLTYPE_STRING }, \ - { "update", CTLTYPE_INT }, \ - { "osreldate", CTLTYPE_INT }, \ - { "ntp_pll", CTLTYPE_NODE }, \ - { "bootfile", CTLTYPE_STRING }, \ - { "maxfilesperproc", CTLTYPE_INT }, \ - { "maxprocperuid", CTLTYPE_INT }, \ - { "ipc", CTLTYPE_NODE }, \ - { "dummy", CTLTYPE_INT }, \ - { "ps_strings", CTLTYPE_INT }, \ - { "usrstack", CTLTYPE_INT }, \ - { "logsigexit", CTLTYPE_INT }, \ - { "iov_max", CTLTYPE_INT }, \ - { "hostuuid", CTLTYPE_STRING }, \ - { "arc4rand", CTLTYPE_OPAQUE }, \ -} - -/* - * CTL_VFS identifiers - */ -#define CTL_VFS_NAMES { \ - { "vfsconf", CTLTYPE_STRUCT }, \ -} - /* * KERN_PROC subtypes */ -#define KERN_PROC_ALL 0 /* everything */ +#define KERN_PROC_ALL 0 /* everything */ #define KERN_PROC_PID 1 /* by process id */ #define KERN_PROC_PGRP 2 /* by process group id */ #define KERN_PROC_SESSION 3 /* by session of pid */ @@ -586,7 +534,7 @@ SYSCTL_ALLOWED_TYPES(UINT64, uint64_t *a; unsigned long long *b; ); /* * KERN_IPC identifiers */ -#define KIPC_MAXSOCKBUF 1 /* int: max size of a socket buffer */ +#define KIPC_MAXSOCKBUF 1 /* int: max size of a socket buffer */ #define KIPC_SOCKBUF_WASTE 2 /* int: wastage factor in sockbuf */ #define KIPC_SOMAXCONN 3 /* int: max length of connection q */ #define KIPC_MAX_LINKHDR 4 /* int: max length of link header */ @@ -606,27 +554,11 @@ SYSCTL_ALLOWED_TYPES(UINT64, uint64_t *a; unsigned long long *b; ); #define HW_PAGESIZE 7 /* int: software page size */ #define HW_DISKNAMES 8 /* strings: disk drive names */ #define HW_DISKSTATS 9 /* struct: diskstats[] */ -#define HW_FLOATINGPT 10 /* int: has HW floating point? */ -#define HW_MACHINE_ARCH 11 /* string: machine architecture */ +#define HW_FLOATINGPT 10 /* int: has HW floating point? */ +#define HW_MACHINE_ARCH 11 /* string: machine architecture */ #define HW_REALMEM 12 /* int: 'real' memory */ #define HW_MAXID 13 /* number of valid hw ids */ -#define CTL_HW_NAMES { \ - { 0, 0 }, \ - { "machine", CTLTYPE_STRING }, \ - { "model", CTLTYPE_STRING }, \ - { "ncpu", CTLTYPE_INT }, \ - { "byteorder", CTLTYPE_INT }, \ - { "physmem", CTLTYPE_ULONG }, \ - { "usermem", CTLTYPE_ULONG }, \ - { "pagesize", CTLTYPE_INT }, \ - { "disknames", CTLTYPE_STRUCT }, \ - { "diskstats", CTLTYPE_STRUCT }, \ - { "floatingpoint", CTLTYPE_INT }, \ - { "machine_arch", CTLTYPE_STRING }, \ - { "realmem", CTLTYPE_ULONG }, \ -} - /* * CTL_USER definitions */ @@ -652,86 +584,33 @@ SYSCTL_ALLOWED_TYPES(UINT64, uint64_t *a; unsigned long long *b; ); #define USER_TZNAME_MAX 20 /* int: POSIX2_TZNAME_MAX */ #define USER_MAXID 21 /* number of valid user ids */ -#define CTL_USER_NAMES { \ - { 0, 0 }, \ - { "cs_path", CTLTYPE_STRING }, \ - { "bc_base_max", CTLTYPE_INT }, \ - { "bc_dim_max", CTLTYPE_INT }, \ - { "bc_scale_max", CTLTYPE_INT }, \ - { "bc_string_max", CTLTYPE_INT }, \ - { "coll_weights_max", CTLTYPE_INT }, \ - { "expr_nest_max", CTLTYPE_INT }, \ - { "line_max", CTLTYPE_INT }, \ - { "re_dup_max", CTLTYPE_INT }, \ - { "posix2_version", CTLTYPE_INT }, \ - { "posix2_c_bind", CTLTYPE_INT }, \ - { "posix2_c_dev", CTLTYPE_INT }, \ - { "posix2_char_term", CTLTYPE_INT }, \ - { "posix2_fort_dev", CTLTYPE_INT }, \ - { "posix2_fort_run", CTLTYPE_INT }, \ - { "posix2_localedef", CTLTYPE_INT }, \ - { "posix2_sw_dev", CTLTYPE_INT }, \ - { "posix2_upe", CTLTYPE_INT }, \ - { "stream_max", CTLTYPE_INT }, \ - { "tzname_max", CTLTYPE_INT }, \ -} +#define CTL_P1003_1B_ASYNCHRONOUS_IO 1 /* boolean */ +#define CTL_P1003_1B_MAPPED_FILES 2 /* boolean */ +#define CTL_P1003_1B_MEMLOCK 3 /* boolean */ +#define CTL_P1003_1B_MEMLOCK_RANGE 4 /* boolean */ +#define CTL_P1003_1B_MEMORY_PROTECTION 5 /* boolean */ +#define CTL_P1003_1B_MESSAGE_PASSING 6 /* boolean */ +#define CTL_P1003_1B_PRIORITIZED_IO 7 /* boolean */ +#define CTL_P1003_1B_PRIORITY_SCHEDULING 8 /* boolean */ +#define CTL_P1003_1B_REALTIME_SIGNALS 9 /* boolean */ +#define CTL_P1003_1B_SEMAPHORES 10 /* boolean */ +#define CTL_P1003_1B_FSYNC 11 /* boolean */ +#define CTL_P1003_1B_SHARED_MEMORY_OBJECTS 12 /* boolean */ +#define CTL_P1003_1B_SYNCHRONIZED_IO 13 /* boolean */ +#define CTL_P1003_1B_TIMERS 14 /* boolean */ +#define CTL_P1003_1B_AIO_LISTIO_MAX 15 /* int */ +#define CTL_P1003_1B_AIO_MAX 16 /* int */ +#define CTL_P1003_1B_AIO_PRIO_DELTA_MAX 17 /* int */ +#define CTL_P1003_1B_DELAYTIMER_MAX 18 /* int */ +#define CTL_P1003_1B_MQ_OPEN_MAX 19 /* int */ +#define CTL_P1003_1B_PAGESIZE 20 /* int */ +#define CTL_P1003_1B_RTSIG_MAX 21 /* int */ +#define CTL_P1003_1B_SEM_NSEMS_MAX 22 /* int */ +#define CTL_P1003_1B_SEM_VALUE_MAX 23 /* int */ +#define CTL_P1003_1B_SIGQUEUE_MAX 24 /* int */ +#define CTL_P1003_1B_TIMER_MAX 25 /* int */ -#define CTL_P1003_1B_ASYNCHRONOUS_IO 1 /* boolean */ -#define CTL_P1003_1B_MAPPED_FILES 2 /* boolean */ -#define CTL_P1003_1B_MEMLOCK 3 /* boolean */ -#define CTL_P1003_1B_MEMLOCK_RANGE 4 /* boolean */ -#define CTL_P1003_1B_MEMORY_PROTECTION 5 /* boolean */ -#define CTL_P1003_1B_MESSAGE_PASSING 6 /* boolean */ -#define CTL_P1003_1B_PRIORITIZED_IO 7 /* boolean */ -#define CTL_P1003_1B_PRIORITY_SCHEDULING 8 /* boolean */ -#define CTL_P1003_1B_REALTIME_SIGNALS 9 /* boolean */ -#define CTL_P1003_1B_SEMAPHORES 10 /* boolean */ -#define CTL_P1003_1B_FSYNC 11 /* boolean */ -#define CTL_P1003_1B_SHARED_MEMORY_OBJECTS 12 /* boolean */ -#define CTL_P1003_1B_SYNCHRONIZED_IO 13 /* boolean */ -#define CTL_P1003_1B_TIMERS 14 /* boolean */ -#define CTL_P1003_1B_AIO_LISTIO_MAX 15 /* int */ -#define CTL_P1003_1B_AIO_MAX 16 /* int */ -#define CTL_P1003_1B_AIO_PRIO_DELTA_MAX 17 /* int */ -#define CTL_P1003_1B_DELAYTIMER_MAX 18 /* int */ -#define CTL_P1003_1B_MQ_OPEN_MAX 19 /* int */ -#define CTL_P1003_1B_PAGESIZE 20 /* int */ -#define CTL_P1003_1B_RTSIG_MAX 21 /* int */ -#define CTL_P1003_1B_SEM_NSEMS_MAX 22 /* int */ -#define CTL_P1003_1B_SEM_VALUE_MAX 23 /* int */ -#define CTL_P1003_1B_SIGQUEUE_MAX 24 /* int */ -#define CTL_P1003_1B_TIMER_MAX 25 /* int */ - -#define CTL_P1003_1B_MAXID 26 - -#define CTL_P1003_1B_NAMES { \ - { 0, 0 }, \ - { "asynchronous_io", CTLTYPE_INT }, \ - { "mapped_files", CTLTYPE_INT }, \ - { "memlock", CTLTYPE_INT }, \ - { "memlock_range", CTLTYPE_INT }, \ - { "memory_protection", CTLTYPE_INT }, \ - { "message_passing", CTLTYPE_INT }, \ - { "prioritized_io", CTLTYPE_INT }, \ - { "priority_scheduling", CTLTYPE_INT }, \ - { "realtime_signals", CTLTYPE_INT }, \ - { "semaphores", CTLTYPE_INT }, \ - { "fsync", CTLTYPE_INT }, \ - { "shared_memory_objects", CTLTYPE_INT }, \ - { "synchronized_io", CTLTYPE_INT }, \ - { "timers", CTLTYPE_INT }, \ - { "aio_listio_max", CTLTYPE_INT }, \ - { "aio_max", CTLTYPE_INT }, \ - { "aio_prio_delta_max", CTLTYPE_INT }, \ - { "delaytimer_max", CTLTYPE_INT }, \ - { "mq_open_max", CTLTYPE_INT }, \ - { "pagesize", CTLTYPE_INT }, \ - { "rtsig_max", CTLTYPE_INT }, \ - { "nsems_max", CTLTYPE_INT }, \ - { "sem_value_max", CTLTYPE_INT }, \ - { "sigqueue_max", CTLTYPE_INT }, \ - { "timer_max", CTLTYPE_INT }, \ -} +#define CTL_P1003_1B_MAXID 26 #ifdef _KERNEL @@ -772,43 +651,42 @@ extern char kern_ident[]; /* Dynamic oid handling */ struct sysctl_oid *sysctl_add_oid(struct sysctl_ctx_list *clist, - struct sysctl_oid_list *parent, int nbr, const char *name, - int kind, void *arg1, intptr_t arg2, - int (*handler) (SYSCTL_HANDLER_ARGS), - const char *fmt, const char *descr); + struct sysctl_oid_list *parent, int nbr, const char *name, int kind, + void *arg1, intptr_t arg2, int (*handler)(SYSCTL_HANDLER_ARGS), + const char *fmt, const char *descr); int sysctl_remove_name(struct sysctl_oid *parent, const char *name, int del, - int recurse); + int recurse); void sysctl_rename_oid(struct sysctl_oid *oidp, const char *name); int sysctl_move_oid(struct sysctl_oid *oidp, - struct sysctl_oid_list *parent); + struct sysctl_oid_list *parent); int sysctl_remove_oid(struct sysctl_oid *oidp, int del, int recurse); int sysctl_ctx_init(struct sysctl_ctx_list *clist); int sysctl_ctx_free(struct sysctl_ctx_list *clist); struct sysctl_ctx_entry *sysctl_ctx_entry_add(struct sysctl_ctx_list *clist, - struct sysctl_oid *oidp); + struct sysctl_oid *oidp); struct sysctl_ctx_entry *sysctl_ctx_entry_find(struct sysctl_ctx_list *clist, - struct sysctl_oid *oidp); + struct sysctl_oid *oidp); int sysctl_ctx_entry_del(struct sysctl_ctx_list *clist, - struct sysctl_oid *oidp); + struct sysctl_oid *oidp); int kernel_sysctl(struct thread *td, int *name, u_int namelen, void *old, - size_t *oldlenp, void *new, size_t newlen, - size_t *retval, int flags); -int kernel_sysctlbyname(struct thread *td, char *name, - void *old, size_t *oldlenp, void *new, size_t newlen, - size_t *retval, int flags); + size_t *oldlenp, void *new, size_t newlen, size_t *retval, + int flags); +int kernel_sysctlbyname(struct thread *td, char *name, void *old, + size_t *oldlenp, void *new, size_t newlen, size_t *retval, + int flags); int userland_sysctl(struct thread *td, int *name, u_int namelen, void *old, - size_t *oldlenp, int inkernel, void *new, size_t newlen, - size_t *retval, int flags); + size_t *oldlenp, int inkernel, void *new, size_t newlen, + size_t *retval, int flags); int sysctl_find_oid(int *name, u_int namelen, struct sysctl_oid **noid, - int *nindx, struct sysctl_req *req); + int *nindx, struct sysctl_req *req); void sysctl_lock(void); void sysctl_unlock(void); int sysctl_wire_old_buffer(struct sysctl_req *req, size_t len); struct sbuf; -struct sbuf *sbuf_new_for_sysctl(struct sbuf *, char *, int, - struct sysctl_req *); +struct sbuf *sbuf_new_for_sysctl(struct sbuf *, char *, int, + struct sysctl_req *); #else /* !_KERNEL */ #include diff --git a/sys/sys/sysproto.h b/sys/sys/sysproto.h index 6a54152c2b6..897a2985686 100644 --- a/sys/sys/sysproto.h +++ b/sys/sys/sysproto.h @@ -3,7 +3,7 @@ * * DO NOT EDIT-- this file is automatically generated. * $FreeBSD$ - * created from FreeBSD: head/sys/kern/syscalls.master 251526 2013-06-08 13:27:57Z glebius + * created from FreeBSD: head/sys/kern/syscalls.master 255490 2013-09-12 17:52:18Z jhb */ #ifndef _SYS_SYSPROTO_H_ @@ -15,6 +15,7 @@ #include #include #include +#include #include @@ -1672,13 +1673,10 @@ struct lpathconf_args { char path_l_[PADL_(char *)]; char * path; char path_r_[PADR_(char *)]; char name_l_[PADL_(int)]; int name; char name_r_[PADR_(int)]; }; -struct cap_new_args { +struct __cap_rights_get_args { + char version_l_[PADL_(int)]; int version; char version_r_[PADR_(int)]; char fd_l_[PADL_(int)]; int fd; char fd_r_[PADR_(int)]; - char rights_l_[PADL_(uint64_t)]; uint64_t rights; char rights_r_[PADR_(uint64_t)]; -}; -struct cap_rights_get_args { - char fd_l_[PADL_(int)]; int fd; char fd_r_[PADR_(int)]; - char rightsp_l_[PADL_(uint64_t *)]; uint64_t * rightsp; char rightsp_r_[PADR_(uint64_t *)]; + char rightsp_l_[PADL_(cap_rights_t *)]; cap_rights_t * rightsp; char rightsp_r_[PADR_(cap_rights_t *)]; }; struct cap_enter_args { register_t dummy; @@ -1755,7 +1753,7 @@ struct posix_fadvise_args { char advice_l_[PADL_(int)]; int advice; char advice_r_[PADR_(int)]; }; struct wait6_args { - char idtype_l_[PADL_(int)]; int idtype; char idtype_r_[PADR_(int)]; + char idtype_l_[PADL_(idtype_t)]; idtype_t idtype; char idtype_r_[PADR_(idtype_t)]; char id_l_[PADL_(id_t)]; id_t id; char id_r_[PADR_(id_t)]; char status_l_[PADL_(int *)]; int * status; char status_r_[PADR_(int *)]; char options_l_[PADL_(int)]; int options; char options_r_[PADR_(int)]; @@ -1764,7 +1762,7 @@ struct wait6_args { }; struct cap_rights_limit_args { char fd_l_[PADL_(int)]; int fd; char fd_r_[PADR_(int)]; - char rights_l_[PADL_(uint64_t)]; uint64_t rights; char rights_r_[PADR_(uint64_t)]; + char rightsp_l_[PADL_(cap_rights_t *)]; cap_rights_t * rightsp; char rightsp_r_[PADR_(cap_rights_t *)]; }; struct cap_ioctls_limit_args { char fd_l_[PADL_(int)]; int fd; char fd_r_[PADR_(int)]; @@ -2179,8 +2177,7 @@ int sys___semctl(struct thread *, struct __semctl_args *); int sys_msgctl(struct thread *, struct msgctl_args *); int sys_shmctl(struct thread *, struct shmctl_args *); int sys_lpathconf(struct thread *, struct lpathconf_args *); -int sys_cap_new(struct thread *, struct cap_new_args *); -int sys_cap_rights_get(struct thread *, struct cap_rights_get_args *); +int sys___cap_rights_get(struct thread *, struct __cap_rights_get_args *); int sys_cap_enter(struct thread *, struct cap_enter_args *); int sys_cap_getmode(struct thread *, struct cap_getmode_args *); int sys_pdfork(struct thread *, struct pdfork_args *); @@ -2886,8 +2883,7 @@ int freebsd7_shmctl(struct thread *, struct freebsd7_shmctl_args *); #define SYS_AUE_msgctl AUE_MSGCTL #define SYS_AUE_shmctl AUE_SHMCTL #define SYS_AUE_lpathconf AUE_LPATHCONF -#define SYS_AUE_cap_new AUE_CAP_NEW -#define SYS_AUE_cap_rights_get AUE_CAP_RIGHTS_GET +#define SYS_AUE___cap_rights_get AUE_CAP_RIGHTS_GET #define SYS_AUE_cap_enter AUE_CAP_ENTER #define SYS_AUE_cap_getmode AUE_CAP_GETMODE #define SYS_AUE_pdfork AUE_PDFORK diff --git a/sys/sys/systm.h b/sys/sys/systm.h index 4887d71f567..f097e83c1f0 100644 --- a/sys/sys/systm.h +++ b/sys/sys/systm.h @@ -212,6 +212,7 @@ u_long strtoul(const char *, char **, int) __nonnull(1); quad_t strtoq(const char *, char **, int) __nonnull(1); u_quad_t strtouq(const char *, char **, int) __nonnull(1); void tprintf(struct proc *p, int pri, const char *, ...) __printflike(3, 4); +void vtprintf(struct proc *, int, const char *, __va_list) __printflike(3, 0); void hexdump(const void *ptr, int length, const char *hdr, int flags); #define HD_COLUMN_MASK 0xff #define HD_DELIM_MASK 0xff00 @@ -396,6 +397,7 @@ int root_mounted(void); */ struct unrhdr; struct unrhdr *new_unrhdr(int low, int high, struct mtx *mutex); +void init_unrhdr(struct unrhdr *uh, int low, int high, struct mtx *mutex); void delete_unrhdr(struct unrhdr *uh); void clean_unrhdr(struct unrhdr *uh); void clean_unrhdrl(struct unrhdr *uh); diff --git a/sys/sys/time.h b/sys/sys/time.h index d260cc79030..82a7db1436c 100644 --- a/sys/sys/time.h +++ b/sys/sys/time.h @@ -56,64 +56,64 @@ struct bintime { }; static __inline void -bintime_addx(struct bintime *bt, uint64_t x) +bintime_addx(struct bintime *_bt, uint64_t _x) { - uint64_t u; + uint64_t _u; - u = bt->frac; - bt->frac += x; - if (u > bt->frac) - bt->sec++; + _u = _bt->frac; + _bt->frac += _x; + if (_u > _bt->frac) + _bt->sec++; } static __inline void -bintime_add(struct bintime *bt, const struct bintime *bt2) +bintime_add(struct bintime *_bt, const struct bintime *_bt2) { - uint64_t u; + uint64_t _u; - u = bt->frac; - bt->frac += bt2->frac; - if (u > bt->frac) - bt->sec++; - bt->sec += bt2->sec; + _u = _bt->frac; + _bt->frac += _bt2->frac; + if (_u > _bt->frac) + _bt->sec++; + _bt->sec += _bt2->sec; } static __inline void -bintime_sub(struct bintime *bt, const struct bintime *bt2) +bintime_sub(struct bintime *_bt, const struct bintime *_bt2) { - uint64_t u; + uint64_t _u; - u = bt->frac; - bt->frac -= bt2->frac; - if (u < bt->frac) - bt->sec--; - bt->sec -= bt2->sec; + _u = _bt->frac; + _bt->frac -= _bt2->frac; + if (_u < _bt->frac) + _bt->sec--; + _bt->sec -= _bt2->sec; } static __inline void -bintime_mul(struct bintime *bt, u_int x) +bintime_mul(struct bintime *_bt, u_int _x) { - uint64_t p1, p2; + uint64_t _p1, _p2; - p1 = (bt->frac & 0xffffffffull) * x; - p2 = (bt->frac >> 32) * x + (p1 >> 32); - bt->sec *= x; - bt->sec += (p2 >> 32); - bt->frac = (p2 << 32) | (p1 & 0xffffffffull); + _p1 = (_bt->frac & 0xffffffffull) * _x; + _p2 = (_bt->frac >> 32) * _x + (_p1 >> 32); + _bt->sec *= _x; + _bt->sec += (_p2 >> 32); + _bt->frac = (_p2 << 32) | (_p1 & 0xffffffffull); } static __inline void -bintime_shift(struct bintime *__bt, int __exp) +bintime_shift(struct bintime *_bt, int _exp) { - if (__exp > 0) { - __bt->sec <<= __exp; - __bt->sec |= __bt->frac >> (64 - __exp); - __bt->frac <<= __exp; - } else if (__exp < 0) { - __bt->frac >>= -__exp; - __bt->frac |= (uint64_t)__bt->sec << (64 + __exp); - __bt->sec >>= -__exp; + if (_exp > 0) { + _bt->sec <<= _exp; + _bt->sec |= _bt->frac >> (64 - _exp); + _bt->frac <<= _exp; + } else if (_exp < 0) { + _bt->frac >>= -_exp; + _bt->frac |= (uint64_t)_bt->sec << (64 + _exp); + _bt->sec >>= -_exp; } } @@ -131,27 +131,27 @@ bintime_shift(struct bintime *__bt, int __exp) #define SBT_1NS (SBT_1S / 1000000000) static __inline int -sbintime_getsec(sbintime_t sbt) +sbintime_getsec(sbintime_t _sbt) { - return (sbt >> 32); + return (_sbt >> 32); } static __inline sbintime_t -bttosbt(const struct bintime bt) +bttosbt(const struct bintime _bt) { - return (((sbintime_t)bt.sec << 32) + (bt.frac >> 32)); + return (((sbintime_t)_bt.sec << 32) + (_bt.frac >> 32)); } static __inline struct bintime -sbttobt(sbintime_t sbt) +sbttobt(sbintime_t _sbt) { - struct bintime bt; + struct bintime _bt; - bt.sec = sbt >> 32; - bt.frac = sbt << 32; - return (bt); + _bt.sec = _sbt >> 32; + _bt.frac = _sbt << 32; + return (_bt); } /*- @@ -169,73 +169,74 @@ sbttobt(sbintime_t sbt) */ static __inline void -bintime2timespec(const struct bintime *bt, struct timespec *ts) +bintime2timespec(const struct bintime *_bt, struct timespec *_ts) { - ts->tv_sec = bt->sec; - ts->tv_nsec = ((uint64_t)1000000000 * (uint32_t)(bt->frac >> 32)) >> 32; + _ts->tv_sec = _bt->sec; + _ts->tv_nsec = ((uint64_t)1000000000 * + (uint32_t)(_bt->frac >> 32)) >> 32; } static __inline void -timespec2bintime(const struct timespec *ts, struct bintime *bt) +timespec2bintime(const struct timespec *_ts, struct bintime *_bt) { - bt->sec = ts->tv_sec; + _bt->sec = _ts->tv_sec; /* 18446744073 = int(2^64 / 1000000000) */ - bt->frac = ts->tv_nsec * (uint64_t)18446744073LL; + _bt->frac = _ts->tv_nsec * (uint64_t)18446744073LL; } static __inline void -bintime2timeval(const struct bintime *bt, struct timeval *tv) +bintime2timeval(const struct bintime *_bt, struct timeval *_tv) { - tv->tv_sec = bt->sec; - tv->tv_usec = ((uint64_t)1000000 * (uint32_t)(bt->frac >> 32)) >> 32; + _tv->tv_sec = _bt->sec; + _tv->tv_usec = ((uint64_t)1000000 * (uint32_t)(_bt->frac >> 32)) >> 32; } static __inline void -timeval2bintime(const struct timeval *tv, struct bintime *bt) +timeval2bintime(const struct timeval *_tv, struct bintime *_bt) { - bt->sec = tv->tv_sec; + _bt->sec = _tv->tv_sec; /* 18446744073709 = int(2^64 / 1000000) */ - bt->frac = tv->tv_usec * (uint64_t)18446744073709LL; + _bt->frac = _tv->tv_usec * (uint64_t)18446744073709LL; } static __inline struct timespec -sbttots(sbintime_t sbt) +sbttots(sbintime_t _sbt) { - struct timespec ts; + struct timespec _ts; - ts.tv_sec = sbt >> 32; - ts.tv_nsec = ((uint64_t)1000000000 * (uint32_t)sbt) >> 32; - return (ts); + _ts.tv_sec = _sbt >> 32; + _ts.tv_nsec = ((uint64_t)1000000000 * (uint32_t)_sbt) >> 32; + return (_ts); } static __inline sbintime_t -tstosbt(struct timespec ts) +tstosbt(struct timespec _ts) { - return (((sbintime_t)ts.tv_sec << 32) + - (ts.tv_nsec * (((uint64_t)1 << 63) / 500000000) >> 32)); + return (((sbintime_t)_ts.tv_sec << 32) + + (_ts.tv_nsec * (((uint64_t)1 << 63) / 500000000) >> 32)); } static __inline struct timeval -sbttotv(sbintime_t sbt) +sbttotv(sbintime_t _sbt) { - struct timeval tv; + struct timeval _tv; - tv.tv_sec = sbt >> 32; - tv.tv_usec = ((uint64_t)1000000 * (uint32_t)sbt) >> 32; - return (tv); + _tv.tv_sec = _sbt >> 32; + _tv.tv_usec = ((uint64_t)1000000 * (uint32_t)_sbt) >> 32; + return (_tv); } static __inline sbintime_t -tvtosbt(struct timeval tv) +tvtosbt(struct timeval _tv) { - return (((sbintime_t)tv.tv_sec << 32) + - (tv.tv_usec * (((uint64_t)1 << 63) / 500000) >> 32)); + return (((sbintime_t)_tv.tv_sec << 32) + + (_tv.tv_usec * (((uint64_t)1 << 63) / 500000) >> 32)); } #endif /* __BSD_VISIBLE */ @@ -411,10 +412,10 @@ void microuptime(struct timeval *tvp); static __inline sbintime_t sbinuptime(void) { - struct bintime bt; + struct bintime _bt; - binuptime(&bt); - return (bttosbt(bt)); + binuptime(&_bt); + return (bttosbt(_bt)); } void bintime(struct bintime *bt); @@ -428,10 +429,10 @@ void getmicrouptime(struct timeval *tvp); static __inline sbintime_t getsbinuptime(void) { - struct bintime bt; + struct bintime _bt; - getbinuptime(&bt); - return (bttosbt(bt)); + getbinuptime(&_bt); + return (bttosbt(_bt)); } void getbintime(struct bintime *bt); diff --git a/sys/sys/types.h b/sys/sys/types.h index cc0bca8f33b..03eaa6002ef 100644 --- a/sys/sys/types.h +++ b/sys/sys/types.h @@ -88,8 +88,6 @@ typedef __blkcnt_t blkcnt_t; #define _BLKCNT_T_DECLARED #endif -typedef __cap_rights_t cap_rights_t; - #ifndef _CLOCK_T_DECLARED typedef __clock_t clock_t; #define _CLOCK_T_DECLARED @@ -234,6 +232,13 @@ typedef __useconds_t useconds_t; /* microseconds (unsigned) */ #define _USECONDS_T_DECLARED #endif +#ifndef _CAP_RIGHTS_T_DECLARED +#define _CAP_RIGHTS_T_DECLARED +struct cap_rights; + +typedef struct cap_rights cap_rights_t; +#endif + typedef __vm_offset_t vm_offset_t; typedef __vm_ooffset_t vm_ooffset_t; typedef __vm_paddr_t vm_paddr_t; diff --git a/sys/sys/uio.h b/sys/sys/uio.h index 8a594f36dbb..271a2f7a9f0 100644 --- a/sys/sys/uio.h +++ b/sys/sys/uio.h @@ -103,7 +103,7 @@ int uiomove_frombuf(void *buf, int buflen, struct uio *uio); int uiomove_fromphys(struct vm_page *ma[], vm_offset_t offset, int n, struct uio *uio); int uiomove_nofault(void *cp, int n, struct uio *uio); -int uiomoveco(void *cp, int n, struct uio *uio, int disposable); +int uiomove_object(struct vm_object *obj, off_t obj_size, struct uio *uio); #else /* !_KERNEL */ diff --git a/sys/sys/user.h b/sys/sys/user.h index aa81dc9c60b..349003d73da 100644 --- a/sys/sys/user.h +++ b/sys/sys/user.h @@ -61,6 +61,7 @@ #ifndef _SYS_SOCKET_VAR_H_ #include #endif +#include /* * KERN_PROC subtype ops return arrays of selected proc structure entries: @@ -83,7 +84,7 @@ * it in two places: function fill_kinfo_proc in sys/kern/kern_proc.c and * function kvm_proclist in lib/libkvm/kvm_proc.c . */ -#define KI_NSPARE_INT 9 +#define KI_NSPARE_INT 8 #define KI_NSPARE_LONG 12 #define KI_NSPARE_PTR 6 @@ -186,6 +187,7 @@ struct kinfo_proc { */ char ki_sparestrings[50]; /* spare string space */ int ki_spareints[KI_NSPARE_INT]; /* spare room for growth */ + int ki_fibnum; /* Default FIB number */ u_int ki_cr_flags; /* Credential flags */ int ki_jid; /* Process jail ID */ int ki_numthreads; /* XXXKSE number of threads in total */ @@ -317,7 +319,7 @@ struct kinfo_ofile { }; #if defined(__amd64__) || defined(__i386__) -#define KINFO_FILE_SIZE 1392 +#define KINFO_FILE_SIZE 1424 #endif struct kinfo_file { @@ -388,6 +390,7 @@ struct kinfo_file { uint16_t kf_pad1; /* Round to 32 bit alignment. */ int _kf_ispare0; /* Space for more stuff. */ cap_rights_t kf_cap_rights; /* Capability rights. */ + uint64_t _kf_cap_spare[3]; /* Space for future cap_rights_t. */ int _kf_ispare[4]; /* Space for more stuff. */ /* Truncated before copyout in sysctl */ char kf_path[PATH_MAX]; /* Path to file, if any. */ diff --git a/sys/sys/vmmeter.h b/sys/sys/vmmeter.h index 59b430d5320..d2ad920a406 100644 --- a/sys/sys/vmmeter.h +++ b/sys/sys/vmmeter.h @@ -98,7 +98,7 @@ struct vmmeter { u_int v_inactive_count; /* (q) pages inactive */ u_int v_cache_count; /* (f) pages on cache queue */ u_int v_cache_min; /* (c) min pages desired on cache queue */ - u_int v_cache_max; /* (c) max pages in cached obj */ + u_int v_cache_max; /* (c) max pages in cached obj (unused) */ u_int v_pageout_free_min; /* (c) min pages reserved for kernel */ u_int v_interrupt_free_min; /* (c) reserved pages for int code */ u_int v_free_severe; /* (c) severe page depletion point */ @@ -118,6 +118,8 @@ struct vmmeter { extern struct vmmeter cnt; +extern int vm_pageout_wakeup_thresh; + /* * Return TRUE if we are under our severe low-free-pages threshold * @@ -170,10 +172,7 @@ static __inline int vm_paging_target(void) { - return ( - (cnt.v_free_target + cnt.v_cache_min) - - (cnt.v_free_count + cnt.v_cache_count) - ); + return (cnt.v_free_target - (cnt.v_free_count + cnt.v_cache_count)); } /* @@ -184,10 +183,7 @@ static __inline int vm_paging_needed(void) { - return ( - (cnt.v_free_reserved + cnt.v_cache_min) > - (cnt.v_free_count + cnt.v_cache_count) - ); + return (cnt.v_free_count + cnt.v_cache_count < vm_pageout_wakeup_thresh); } #endif diff --git a/sys/sys/wait.h b/sys/sys/wait.h index d71e8a07355..098830e3ef8 100644 --- a/sys/sys/wait.h +++ b/sys/sys/wait.h @@ -53,7 +53,7 @@ #define _WSTOPPED 0177 /* _WSTATUS if process is stopped */ #define WIFSTOPPED(x) (_WSTATUS(x) == _WSTOPPED) #define WSTOPSIG(x) (_W_INT(x) >> 8) -#define WIFSIGNALED(x) (_WSTATUS(x) != _WSTOPPED && _WSTATUS(x) != 0) +#define WIFSIGNALED(x) (_WSTATUS(x) != _WSTOPPED && _WSTATUS(x) != 0 && (x) != 0x13) #define WTERMSIG(x) (_WSTATUS(x)) #define WIFEXITED(x) (_WSTATUS(x) == 0) #define WEXITSTATUS(x) (_W_INT(x) >> 8) diff --git a/sys/ufs/ffs/ffs_alloc.c b/sys/ufs/ffs/ffs_alloc.c index cf1d953af41..19e880c6354 100644 --- a/sys/ufs/ffs/ffs_alloc.c +++ b/sys/ufs/ffs/ffs_alloc.c @@ -516,7 +516,13 @@ ffs_reallocblks_ufs1(ap) ip = VTOI(vp); fs = ip->i_fs; ump = ip->i_ump; - if (fs->fs_contigsumsize <= 0) + /* + * If we are not tracking block clusters or if we have less than 2% + * free blocks left, then do not attempt to cluster. Running with + * less than 5% free block reserve is not recommended and those that + * choose to do so do not expect to have good file layout. + */ + if (fs->fs_contigsumsize <= 0 || freespace(fs, 2) < 0) return (ENOSPC); buflist = ap->a_buflist; len = buflist->bs_nchildren; @@ -737,7 +743,13 @@ ffs_reallocblks_ufs2(ap) ip = VTOI(vp); fs = ip->i_fs; ump = ip->i_ump; - if (fs->fs_contigsumsize <= 0) + /* + * If we are not tracking block clusters or if we have less than 2% + * free blocks left, then do not attempt to cluster. Running with + * less than 5% free block reserve is not recommended and those that + * choose to do so do not expect to have good file layout. + */ + if (fs->fs_contigsumsize <= 0 || freespace(fs, 2) < 0) return (ENOSPC); buflist = ap->a_buflist; len = buflist->bs_nchildren; @@ -1174,7 +1186,7 @@ ffs_dirpref(pip) if (fs->fs_contigdirs[cg] < maxcontigdirs) return ((ino_t)(fs->fs_ipg * cg)); } - for (cg = prefcg - 1; cg >= 0; cg--) + for (cg = 0; cg < prefcg; cg++) if (fs->fs_cs(fs, cg).cs_ndir < maxndir && fs->fs_cs(fs, cg).cs_nifree >= minifree && fs->fs_cs(fs, cg).cs_nbfree >= minbfree) { @@ -1187,7 +1199,7 @@ ffs_dirpref(pip) for (cg = prefcg; cg < fs->fs_ncg; cg++) if (fs->fs_cs(fs, cg).cs_nifree >= avgifree) return ((ino_t)(fs->fs_ipg * cg)); - for (cg = prefcg - 1; cg >= 0; cg--) + for (cg = 0; cg < prefcg; cg++) if (fs->fs_cs(fs, cg).cs_nifree >= avgifree) break; return ((ino_t)(fs->fs_ipg * cg)); @@ -2697,6 +2709,7 @@ sysctl_ffs_fsck(SYSCTL_HANDLER_ARGS) long blkcnt, blksize; struct filedesc *fdp; struct file *fp, *vfp; + cap_rights_t rights; int filetype, error; static struct fileops *origops, bufferedops; @@ -2706,8 +2719,8 @@ sysctl_ffs_fsck(SYSCTL_HANDLER_ARGS) return (error); if (cmd.version != FFS_CMD_VERSION) return (ERPCMISMATCH); - if ((error = getvnode(td->td_proc->p_fd, cmd.handle, CAP_FSCK, - &fp)) != 0) + if ((error = getvnode(td->td_proc->p_fd, cmd.handle, + cap_rights_init(&rights, CAP_FSCK), &fp)) != 0) return (error); vp = fp->f_data; if (vp->v_type != VREG && vp->v_type != VDIR) { @@ -3021,7 +3034,7 @@ sysctl_ffs_fsck(SYSCTL_HANDLER_ARGS) } #endif /* DEBUG */ if ((error = getvnode(td->td_proc->p_fd, cmd.value, - CAP_FSCK, &vfp)) != 0) + cap_rights_init(&rights, CAP_FSCK), &vfp)) != 0) break; if (vfp->f_vnode->v_type != VCHR) { fdrop(vfp, td); diff --git a/sys/ufs/ufs/ufs_dirhash.c b/sys/ufs/ufs/ufs_dirhash.c index b51fdfff129..5c299237858 100644 --- a/sys/ufs/ufs/ufs_dirhash.c +++ b/sys/ufs/ufs/ufs_dirhash.c @@ -85,7 +85,7 @@ SYSCTL_INT(_vfs_ufs, OID_AUTO, dirhash_docheck, CTLFLAG_RW, &ufs_dirhashcheck, static int ufs_dirhashlowmemcount = 0; SYSCTL_INT(_vfs_ufs, OID_AUTO, dirhash_lowmemcount, CTLFLAG_RD, &ufs_dirhashlowmemcount, 0, "number of times low memory hook called"); -static int ufs_dirhashreclaimage = 5; +static int ufs_dirhashreclaimage = 60; SYSCTL_INT(_vfs_ufs, OID_AUTO, dirhash_reclaimage, CTLFLAG_RW, &ufs_dirhashreclaimage, 0, "max time in seconds of hash inactivity before deletion in low VM events"); diff --git a/sys/ufs/ufs/ufs_vnops.c b/sys/ufs/ufs/ufs_vnops.c index 729e96780bf..56e24a30065 100644 --- a/sys/ufs/ufs/ufs_vnops.c +++ b/sys/ufs/ufs/ufs_vnops.c @@ -528,9 +528,11 @@ ufs_setattr(ap) return (EINVAL); } if (vap->va_flags != VNOVAL) { - if ((vap->va_flags & ~(UF_NODUMP | UF_IMMUTABLE | UF_APPEND | - UF_OPAQUE | UF_NOUNLINK | SF_ARCHIVED | SF_IMMUTABLE | - SF_APPEND | SF_NOUNLINK | SF_SNAPSHOT)) != 0) + if ((vap->va_flags & ~(SF_APPEND | SF_ARCHIVED | SF_IMMUTABLE | + SF_NOUNLINK | SF_SNAPSHOT | UF_APPEND | UF_ARCHIVE | + UF_HIDDEN | UF_IMMUTABLE | UF_NODUMP | UF_NOUNLINK | + UF_OFFLINE | UF_OPAQUE | UF_READONLY | UF_REPARSE | + UF_SPARSE | UF_SYSTEM)) != 0) return (EOPNOTSUPP); if (vp->v_mount->mnt_flag & MNT_RDONLY) return (EROFS); diff --git a/sys/vm/_vm_radix.h b/sys/vm/_vm_radix.h index f06646240b2..1d06d0aac26 100644 --- a/sys/vm/_vm_radix.h +++ b/sys/vm/_vm_radix.h @@ -36,8 +36,12 @@ */ struct vm_radix { uintptr_t rt_root; + uint8_t rt_flags; }; +#define RT_INSERT_INPROG 0x01 +#define RT_TRIE_MODIFIED 0x02 + #ifdef _KERNEL static __inline boolean_t diff --git a/sys/vm/device_pager.c b/sys/vm/device_pager.c index fd2066458cf..8113f0394b3 100644 --- a/sys/vm/device_pager.c +++ b/sys/vm/device_pager.c @@ -226,7 +226,7 @@ dev_pager_free_page(vm_object_t object, vm_page_t m) KASSERT((object->type == OBJT_DEVICE && (m->oflags & VPO_UNMANAGED) != 0), ("Managed device or page obj %p m %p", object, m)); - TAILQ_REMOVE(&object->un_pager.devp.devp_pglist, m, pageq); + TAILQ_REMOVE(&object->un_pager.devp.devp_pglist, m, plinks.q); vm_page_putfake(m); } @@ -281,7 +281,7 @@ dev_pager_getpages(vm_object_t object, vm_page_t *ma, int count, int reqpage) ("Wrong page type %p %p", ma[reqpage], object)); if (object->type == OBJT_DEVICE) { TAILQ_INSERT_TAIL(&object->un_pager.devp.devp_pglist, - ma[reqpage], pageq); + ma[reqpage], plinks.q); } } @@ -348,11 +348,12 @@ old_dev_pager_fault(vm_object_t object, vm_ooffset_t offset, int prot, */ page = vm_page_getfake(paddr, memattr); VM_OBJECT_WLOCK(object); + if (vm_page_replace(page, object, (*mres)->pindex) != *mres) + panic("old_dev_pager_fault: invalid page replacement"); vm_page_lock(*mres); vm_page_free(*mres); vm_page_unlock(*mres); *mres = page; - vm_page_insert(page, object, pidx); } page->valid = VM_PAGE_BITS_ALL; return (VM_PAGER_OK); diff --git a/sys/vm/memguard.c b/sys/vm/memguard.c index b1740c37e5e..167c2230177 100644 --- a/sys/vm/memguard.c +++ b/sys/vm/memguard.c @@ -48,6 +48,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include @@ -55,6 +56,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include #include @@ -99,8 +101,8 @@ SYSCTL_PROC(_vm_memguard, OID_AUTO, desc, CTLTYPE_STRING | CTLFLAG_RW | CTLFLAG_MPSAFE, 0, 0, memguard_sysctl_desc, "A", "Short description of memory type to monitor"); -static vm_map_t memguard_map = NULL; static vm_offset_t memguard_cursor; +static vm_offset_t memguard_base; static vm_size_t memguard_mapsize; static vm_size_t memguard_physlimit; static u_long memguard_wasted; @@ -112,7 +114,7 @@ static u_long memguard_fail_pgs; SYSCTL_ULONG(_vm_memguard, OID_AUTO, cursor, CTLFLAG_RD, &memguard_cursor, 0, "MemGuard cursor"); SYSCTL_ULONG(_vm_memguard, OID_AUTO, mapsize, CTLFLAG_RD, - &memguard_mapsize, 0, "MemGuard private vm_map size"); + &memguard_mapsize, 0, "MemGuard private arena size"); SYSCTL_ULONG(_vm_memguard, OID_AUTO, phys_limit, CTLFLAG_RD, &memguard_physlimit, 0, "Limit on MemGuard memory consumption"); SYSCTL_ULONG(_vm_memguard, OID_AUTO, wasted, CTLFLAG_RD, @@ -200,21 +202,18 @@ memguard_fudge(unsigned long km_size, const struct vm_map *parent_map) * out of a single VM map (contiguous chunk of address space). */ void -memguard_init(vm_map_t parent_map) +memguard_init(vmem_t *parent) { - vm_offset_t base, limit; + vm_offset_t base; - memguard_map = kmem_suballoc(parent_map, &base, &limit, - memguard_mapsize, FALSE); - memguard_map->system_map = 1; - KASSERT(memguard_mapsize == limit - base, - ("Expected %lu, got %lu", (u_long)memguard_mapsize, - (u_long)(limit - base))); + vmem_alloc(parent, memguard_mapsize, M_BESTFIT | M_WAITOK, &base); + vmem_init(memguard_arena, "memguard arena", base, memguard_mapsize, + PAGE_SIZE, 0, M_WAITOK); memguard_cursor = base; + memguard_base = base; printf("MEMGUARD DEBUGGING ALLOCATOR INITIALIZED:\n"); printf("\tMEMGUARD map base: 0x%lx\n", (u_long)base); - printf("\tMEMGUARD map limit: 0x%lx\n", (u_long)limit); printf("\tMEMGUARD map size: %jd KBytes\n", (uintmax_t)memguard_mapsize >> 10); } @@ -230,11 +229,13 @@ memguard_sysinit(void) parent = SYSCTL_STATIC_CHILDREN(_vm_memguard); SYSCTL_ADD_ULONG(NULL, parent, OID_AUTO, "mapstart", CTLFLAG_RD, - &memguard_map->min_offset, "MemGuard KVA base"); + &memguard_base, "MemGuard KVA base"); SYSCTL_ADD_ULONG(NULL, parent, OID_AUTO, "maplimit", CTLFLAG_RD, - &memguard_map->max_offset, "MemGuard KVA end"); + &memguard_mapsize, "MemGuard KVA size"); +#if 0 SYSCTL_ADD_ULONG(NULL, parent, OID_AUTO, "mapused", CTLFLAG_RD, &memguard_map->size, "MemGuard KVA used"); +#endif } SYSINIT(memguard, SI_SUB_KLD, SI_ORDER_ANY, memguard_sysinit, NULL); @@ -260,7 +261,22 @@ v2sizep(vm_offset_t va) p = PHYS_TO_VM_PAGE(pa); KASSERT(p->wire_count != 0 && p->queue == PQ_NONE, ("MEMGUARD: Expected wired page %p in vtomgfifo!", p)); - return ((u_long *)&p->pageq.tqe_next); + return (&p->plinks.memguard.p); +} + +static u_long * +v2sizev(vm_offset_t va) +{ + vm_paddr_t pa; + struct vm_page *p; + + pa = pmap_kextract(va); + if (pa == 0) + panic("MemGuard detected double-free of %p", (void *)va); + p = PHYS_TO_VM_PAGE(pa); + KASSERT(p->wire_count != 0 && p->queue == PQ_NONE, + ("MEMGUARD: Expected wired page %p in vtomgfifo!", p)); + return (&p->plinks.memguard.v); } /* @@ -289,14 +305,13 @@ memguard_alloc(unsigned long req_size, int flags) if (do_guard) size_v += 2 * PAGE_SIZE; - vm_map_lock(memguard_map); /* * When we pass our memory limit, reject sub-page allocations. * Page-size and larger allocations will use the same amount * of physical memory whether we allocate or hand off to * uma_large_alloc(), so keep those. */ - if (memguard_map->size >= memguard_physlimit && + if (vmem_size(memguard_arena, VMEM_ALLOC) >= memguard_physlimit && req_size < PAGE_SIZE) { addr = (vm_offset_t)NULL; memguard_fail_pgs++; @@ -313,33 +328,35 @@ memguard_alloc(unsigned long req_size, int flags) * map, unless vm_map_findspace() is tweaked. */ for (;;) { - rv = vm_map_findspace(memguard_map, memguard_cursor, - size_v, &addr); - if (rv == KERN_SUCCESS) + if (vmem_xalloc(memguard_arena, size_v, 0, 0, 0, + memguard_cursor, VMEM_ADDR_MAX, + M_BESTFIT | M_NOWAIT, &addr) == 0) break; /* * The map has no space. This may be due to * fragmentation, or because the cursor is near the * end of the map. */ - if (memguard_cursor == vm_map_min(memguard_map)) { + if (memguard_cursor == memguard_base) { memguard_fail_kva++; addr = (vm_offset_t)NULL; goto out; } memguard_wrap++; - memguard_cursor = vm_map_min(memguard_map); + memguard_cursor = memguard_base; } if (do_guard) addr += PAGE_SIZE; - rv = kmem_back(memguard_map, addr, size_p, flags); + rv = kmem_back(kmem_object, addr, size_p, flags); if (rv != KERN_SUCCESS) { + vmem_xfree(memguard_arena, addr, size_v); memguard_fail_pgs++; addr = (vm_offset_t)NULL; goto out; } - memguard_cursor = addr + size_p; + memguard_cursor = addr + size_v; *v2sizep(trunc_page(addr)) = req_size; + *v2sizev(trunc_page(addr)) = size_v; memguard_succ++; if (req_size < PAGE_SIZE) { memguard_wasted += (PAGE_SIZE - req_size); @@ -354,7 +371,6 @@ memguard_alloc(unsigned long req_size, int flags) } } out: - vm_map_unlock(memguard_map); return ((void *)addr); } @@ -363,7 +379,7 @@ is_memguard_addr(void *addr) { vm_offset_t a = (vm_offset_t)(uintptr_t)addr; - return (a >= memguard_map->min_offset && a < memguard_map->max_offset); + return (a >= memguard_base && a < memguard_base + memguard_mapsize); } /* @@ -373,12 +389,13 @@ void memguard_free(void *ptr) { vm_offset_t addr; - u_long req_size, size; + u_long req_size, size, sizev; char *temp; int i; addr = trunc_page((uintptr_t)ptr); req_size = *v2sizep(addr); + sizev = *v2sizev(addr); size = round_page(req_size); /* @@ -400,11 +417,12 @@ memguard_free(void *ptr) * vm_map lock to serialize updates to memguard_wasted, since * we had the lock at increment. */ - vm_map_lock(memguard_map); + kmem_unback(kmem_object, addr, size); + if (sizev > size) + addr -= PAGE_SIZE; + vmem_xfree(memguard_arena, addr, sizev); if (req_size < PAGE_SIZE) memguard_wasted -= (PAGE_SIZE - req_size); - (void)vm_map_delete(memguard_map, addr, addr + size); - vm_map_unlock(memguard_map); } /* diff --git a/sys/vm/memguard.h b/sys/vm/memguard.h index 9ec4ffd16a2..9e99e9877bd 100644 --- a/sys/vm/memguard.h +++ b/sys/vm/memguard.h @@ -33,10 +33,11 @@ struct malloc_type; struct vm_map; +struct vmem; #ifdef DEBUG_MEMGUARD unsigned long memguard_fudge(unsigned long, const struct vm_map *); -void memguard_init(struct vm_map *); +void memguard_init(struct vmem *); void *memguard_alloc(unsigned long, int); void *memguard_realloc(void *, unsigned long, struct malloc_type *, int); void memguard_free(void *); diff --git a/sys/vm/phys_pager.c b/sys/vm/phys_pager.c index 7b9f7b25446..9e9800665dc 100644 --- a/sys/vm/phys_pager.c +++ b/sys/vm/phys_pager.c @@ -38,6 +38,7 @@ __FBSDID("$FreeBSD$"); #include #include +#include #include #include #include @@ -152,10 +153,12 @@ phys_pager_getpages(vm_object_t object, vm_page_t *m, int count, int reqpage) KASSERT(m[i]->dirty == 0, ("phys_pager_getpages: dirty page %p", m[i])); /* The requested page must remain busy, the others not. */ - if (i == reqpage) + if (i == reqpage) { + vm_page_lock(m[i]); vm_page_flash(m[i]); - else - vm_page_wakeup(m[i]); + vm_page_unlock(m[i]); + } else + vm_page_xunbusy(m[i]); } return (VM_PAGER_OK); } diff --git a/sys/vm/pmap.h b/sys/vm/pmap.h index c64a549ea63..911298f9534 100644 --- a/sys/vm/pmap.h +++ b/sys/vm/pmap.h @@ -98,11 +98,10 @@ struct thread; extern vm_offset_t kernel_vm_end; void pmap_activate(struct thread *td); +void pmap_advise(pmap_t pmap, vm_offset_t sva, vm_offset_t eva, + int advice); void pmap_align_superpage(vm_object_t, vm_ooffset_t, vm_offset_t *, vm_size_t); -#if defined(__mips__) -void pmap_align_tlb(vm_offset_t *); -#endif void pmap_change_wiring(pmap_t, vm_offset_t, boolean_t); void pmap_clear_modify(vm_page_t m); void pmap_clear_reference(vm_page_t m); diff --git a/sys/vm/sg_pager.c b/sys/vm/sg_pager.c index 76cae682ea2..08e8fc7d206 100644 --- a/sys/vm/sg_pager.c +++ b/sys/vm/sg_pager.c @@ -124,7 +124,7 @@ sg_pager_dealloc(vm_object_t object) * Free up our fake pages. */ while ((m = TAILQ_FIRST(&object->un_pager.sgp.sgp_pglist)) != 0) { - TAILQ_REMOVE(&object->un_pager.sgp.sgp_pglist, m, pageq); + TAILQ_REMOVE(&object->un_pager.sgp.sgp_pglist, m, plinks.q); vm_page_putfake(m); } @@ -182,15 +182,17 @@ sg_pager_getpages(vm_object_t object, vm_page_t *m, int count, int reqpage) /* Construct a new fake page. */ page = vm_page_getfake(paddr, memattr); VM_OBJECT_WLOCK(object); - TAILQ_INSERT_TAIL(&object->un_pager.sgp.sgp_pglist, page, pageq); + TAILQ_INSERT_TAIL(&object->un_pager.sgp.sgp_pglist, page, plinks.q); /* Free the original pages and insert this fake page into the object. */ for (i = 0; i < count; i++) { + if (i == reqpage && + vm_page_replace(page, object, offset) != m[i]) + panic("sg_pager_getpages: invalid place replacement"); vm_page_lock(m[i]); vm_page_free(m[i]); vm_page_unlock(m[i]); } - vm_page_insert(page, object, offset); m[reqpage] = page; page->valid = VM_PAGE_BITS_ALL; diff --git a/sys/vm/swap_pager.c b/sys/vm/swap_pager.c index 319811ab7b0..54ebf9d0691 100644 --- a/sys/vm/swap_pager.c +++ b/sys/vm/swap_pager.c @@ -1219,9 +1219,10 @@ swap_pager_getpages(vm_object_t object, vm_page_t *m, int count, int reqpage) */ VM_OBJECT_WLOCK(object); while ((mreq->oflags & VPO_SWAPINPROG) != 0) { - mreq->oflags |= VPO_WANTED; + mreq->oflags |= VPO_SWAPSLEEP; PCPU_INC(cnt.v_intrans); - if (VM_OBJECT_SLEEP(object, mreq, PSWP, "swread", hz * 20)) { + if (VM_OBJECT_SLEEP(object, &object->paging_in_progress, PSWP, + "swread", hz * 20)) { printf( "swap_pager: indefinite wait buffer: bufobj: %p, blkno: %jd, size: %ld\n", bp->b_bufobj, (intmax_t)bp->b_blkno, bp->b_bcount); @@ -1459,12 +1460,6 @@ swap_pager_putpages(vm_object_t object, vm_page_t *m, int count, * Completion routine for asynchronous reads and writes from/to swap. * Also called manually by synchronous code to finish up a bp. * - * For READ operations, the pages are VPO_BUSY'd. For WRITE operations, - * the pages are vm_page_t->busy'd. For READ operations, we VPO_BUSY - * unbusy all pages except the 'main' request page. For WRITE - * operations, we vm_page_t->busy'd unbusy all pages ( we can do this - * because we marked them all VM_PAGER_PEND on return from putpages ). - * * This routine may not sleep. */ static void @@ -1514,6 +1509,10 @@ swp_pager_async_iodone(struct buf *bp) vm_page_t m = bp->b_pages[i]; m->oflags &= ~VPO_SWAPINPROG; + if (m->oflags & VPO_SWAPSLEEP) { + m->oflags &= ~VPO_SWAPSLEEP; + wakeup(&object->paging_in_progress); + } if (bp->b_ioflags & BIO_ERROR) { /* @@ -1542,8 +1541,11 @@ swp_pager_async_iodone(struct buf *bp) m->valid = 0; if (i != bp->b_pager.pg_reqpage) swp_pager_free_nrpage(m); - else + else { + vm_page_lock(m); vm_page_flash(m); + vm_page_unlock(m); + } /* * If i == bp->b_pager.pg_reqpage, do not wake * the page up. The caller needs to. @@ -1558,7 +1560,7 @@ swp_pager_async_iodone(struct buf *bp) vm_page_lock(m); vm_page_activate(m); vm_page_unlock(m); - vm_page_io_finish(m); + vm_page_sunbusy(m); } } else if (bp->b_iocmd == BIO_READ) { /* @@ -1575,7 +1577,7 @@ swp_pager_async_iodone(struct buf *bp) * Note that the requested page, reqpage, is left * busied, but we still have to wake it up. The * other pages are released (unbusied) by - * vm_page_wakeup(). + * vm_page_xunbusy(). */ KASSERT(!pmap_page_is_mapped(m), ("swp_pager_async_iodone: page %p is mapped", m)); @@ -1595,9 +1597,12 @@ swp_pager_async_iodone(struct buf *bp) vm_page_lock(m); vm_page_deactivate(m); vm_page_unlock(m); - vm_page_wakeup(m); - } else + vm_page_xunbusy(m); + } else { + vm_page_lock(m); vm_page_flash(m); + vm_page_unlock(m); + } } else { /* * For write success, clear the dirty @@ -1608,7 +1613,7 @@ swp_pager_async_iodone(struct buf *bp) ("swp_pager_async_iodone: page %p is not write" " protected", m)); vm_page_undirty(m); - vm_page_io_finish(m); + vm_page_sunbusy(m); if (vm_page_count_severe()) { vm_page_lock(m); vm_page_try_to_cache(m); @@ -1706,19 +1711,18 @@ swp_pager_force_pagein(vm_object_t object, vm_pindex_t pindex) vm_page_t m; vm_object_pip_add(object, 1); - m = vm_page_grab(object, pindex, VM_ALLOC_NORMAL | VM_ALLOC_RETRY | - VM_ALLOC_NOBUSY); + m = vm_page_grab(object, pindex, VM_ALLOC_NORMAL); if (m->valid == VM_PAGE_BITS_ALL) { vm_object_pip_subtract(object, 1); vm_page_dirty(m); vm_page_lock(m); vm_page_activate(m); vm_page_unlock(m); + vm_page_xunbusy(m); vm_pager_page_unswapped(m); return; } - vm_page_busy(m); if (swap_pager_getpages(object, &m, 1, 0) != VM_PAGER_OK) panic("swap_pager_force_pagein: read from swap failed");/*XXX*/ vm_object_pip_subtract(object, 1); @@ -1726,7 +1730,7 @@ swp_pager_force_pagein(vm_object_t object, vm_pindex_t pindex) vm_page_lock(m); vm_page_deactivate(m); vm_page_unlock(m); - vm_page_wakeup(m); + vm_page_xunbusy(m); vm_pager_page_unswapped(m); } diff --git a/sys/vm/uma_core.c b/sys/vm/uma_core.c index 900209ee2ae..e39654ea067 100644 --- a/sys/vm/uma_core.c +++ b/sys/vm/uma_core.c @@ -718,18 +718,6 @@ keg_free_slab(uma_keg_t keg, uma_slab_t slab, int start) keg->uk_fini(slab->us_data + (keg->uk_rsize * i), keg->uk_size); } - if (keg->uk_flags & UMA_ZONE_VTOSLAB) { - vm_object_t obj; - - if (flags & UMA_SLAB_KMEM) - obj = kmem_object; - else if (flags & UMA_SLAB_KERNEL) - obj = kernel_object; - else - obj = NULL; - for (i = 0; i < keg->uk_ppera; i++) - vsetobj((vm_offset_t)mem + (i * PAGE_SIZE), obj); - } if (keg->uk_flags & UMA_ZONE_OFFPAGE) zone_free_item(keg->uk_slabzone, slab, NULL, SKIP_NONE); #ifdef UMA_DEBUG @@ -792,7 +780,7 @@ finished: while ((slab = SLIST_FIRST(&freeslabs)) != NULL) { SLIST_REMOVE(&freeslabs, slab, uma_slab, us_hlink); - keg_free_slab(keg, slab, 0); + keg_free_slab(keg, slab, keg->uk_ipers); } } @@ -1015,7 +1003,7 @@ page_alloc(uma_zone_t zone, int bytes, uint8_t *pflag, int wait) void *p; /* Returned page */ *pflag = UMA_SLAB_KMEM; - p = (void *) kmem_malloc(kmem_map, bytes, wait); + p = (void *) kmem_malloc(kmem_arena, bytes, wait); return (p); } @@ -1097,16 +1085,16 @@ noobj_alloc(uma_zone_t zone, int bytes, uint8_t *flags, int wait) static void page_free(void *mem, int size, uint8_t flags) { - vm_map_t map; + struct vmem *vmem; if (flags & UMA_SLAB_KMEM) - map = kmem_map; + vmem = kmem_arena; else if (flags & UMA_SLAB_KERNEL) - map = kernel_map; + vmem = kernel_arena; else panic("UMA: page_free used with invalid flags %d", flags); - kmem_free(map, (vm_offset_t)mem, size); + kmem_free(vmem, (vm_offset_t)mem, size); } /* @@ -2983,7 +2971,7 @@ uma_zone_reserve_kva(uma_zone_t zone, int count) #else if (1) { #endif - kva = kmem_alloc_nofault(kernel_map, pages * UMA_SLAB_SIZE); + kva = kva_alloc(pages * UMA_SLAB_SIZE); if (kva == 0) return (0); } else @@ -3112,7 +3100,7 @@ uma_large_malloc(int size, int wait) void uma_large_free(uma_slab_t slab) { - vsetobj((vm_offset_t)slab->us_data, kmem_object); + page_free(slab->us_data, slab->us_size, slab->us_flags); zone_free_item(slabzone, slab, NULL, SKIP_NONE); } diff --git a/sys/vm/uma_int.h b/sys/vm/uma_int.h index 9aa05609114..00e8519e07c 100644 --- a/sys/vm/uma_int.h +++ b/sys/vm/uma_int.h @@ -404,15 +404,9 @@ static __inline uma_slab_t vtoslab(vm_offset_t va) { vm_page_t p; - uma_slab_t slab; p = PHYS_TO_VM_PAGE(pmap_kextract(va)); - slab = (uma_slab_t )p->object; - - if (p->flags & PG_SLAB) - return (slab); - else - return (NULL); + return ((uma_slab_t)p->plinks.s.pv); } static __inline void @@ -421,18 +415,7 @@ vsetslab(vm_offset_t va, uma_slab_t slab) vm_page_t p; p = PHYS_TO_VM_PAGE(pmap_kextract(va)); - p->object = (vm_object_t)slab; - p->flags |= PG_SLAB; -} - -static __inline void -vsetobj(vm_offset_t va, vm_object_t obj) -{ - vm_page_t p; - - p = PHYS_TO_VM_PAGE(pmap_kextract(va)); - p->object = obj; - p->flags &= ~PG_SLAB; + p->plinks.s.pv = slab; } /* diff --git a/sys/vm/vm_extern.h b/sys/vm/vm_extern.h index 4a2dc04a9f0..fee55d04394 100644 --- a/sys/vm/vm_extern.h +++ b/sys/vm/vm_extern.h @@ -36,27 +36,40 @@ struct proc; struct vmspace; struct vnode; +struct vmem; #ifdef _KERNEL -int kernacc(void *, int, int); -vm_offset_t kmem_alloc(vm_map_t, vm_size_t); -vm_offset_t kmem_alloc_attr(vm_map_t map, vm_size_t size, int flags, +/* These operate on kernel virtual addresses only. */ +vm_offset_t kva_alloc(vm_size_t); +void kva_free(vm_offset_t, vm_size_t); + +/* These operate on pageable virtual addresses. */ +vm_offset_t kmap_alloc_wait(vm_map_t, vm_size_t); +void kmap_free_wakeup(vm_map_t, vm_offset_t, vm_size_t); + +/* These operate on virtual addresses backed by memory. */ +vm_offset_t kmem_alloc_attr(struct vmem *, vm_size_t size, int flags, vm_paddr_t low, vm_paddr_t high, vm_memattr_t memattr); -vm_offset_t kmem_alloc_contig(vm_map_t map, vm_size_t size, int flags, +vm_offset_t kmem_alloc_contig(struct vmem *, vm_size_t size, int flags, vm_paddr_t low, vm_paddr_t high, u_long alignment, vm_paddr_t boundary, vm_memattr_t memattr); -vm_offset_t kmem_alloc_nofault(vm_map_t, vm_size_t); -vm_offset_t kmem_alloc_nofault_space(vm_map_t, vm_size_t, int); -vm_offset_t kmem_alloc_wait(vm_map_t, vm_size_t); -void kmem_free(vm_map_t, vm_offset_t, vm_size_t); -void kmem_free_wakeup(vm_map_t, vm_offset_t, vm_size_t); -void kmem_init(vm_offset_t, vm_offset_t); -vm_offset_t kmem_malloc(vm_map_t map, vm_size_t size, int flags); -int kmem_back(vm_map_t, vm_offset_t, vm_size_t, int); +vm_offset_t kmem_malloc(struct vmem *, vm_size_t size, int flags); +void kmem_free(struct vmem *, vm_offset_t, vm_size_t); + +/* This provides memory for previously allocated address space. */ +int kmem_back(vm_object_t, vm_offset_t, vm_size_t, int); +void kmem_unback(vm_object_t, vm_offset_t, vm_size_t); + +/* Bootstrapping. */ vm_map_t kmem_suballoc(vm_map_t, vm_offset_t *, vm_offset_t *, vm_size_t, boolean_t); +void kmem_init(vm_offset_t, vm_offset_t); +void kmem_init_zero_region(void); +void kmeminit(void); + void swapout_procs(int); +int kernacc(void *, int, int); int useracc(void *, int, int); int vm_fault(vm_map_t, vm_offset_t, vm_prot_t, int); void vm_fault_copy_entry(vm_map_t, vm_map_t, vm_map_entry_t, vm_map_entry_t, diff --git a/sys/vm/vm_fault.c b/sys/vm/vm_fault.c index f7f1889ca61..26ad9f30864 100644 --- a/sys/vm/vm_fault.c +++ b/sys/vm/vm_fault.c @@ -141,7 +141,7 @@ static inline void release_page(struct faultstate *fs) { - vm_page_wakeup(fs->m); + vm_page_xunbusy(fs->m); vm_page_lock(fs->m); vm_page_deactivate(fs->m); vm_page_unlock(fs->m); @@ -333,48 +333,29 @@ RetryFault:; */ fs.m = vm_page_lookup(fs.object, fs.pindex); if (fs.m != NULL) { - /* - * check for page-based copy on write. - * We check fs.object == fs.first_object so - * as to ensure the legacy COW mechanism is - * used when the page in question is part of - * a shadow object. Otherwise, vm_page_cowfault() - * removes the page from the backing object, - * which is not what we want. - */ - vm_page_lock(fs.m); - if ((fs.m->cow) && - (fault_type & VM_PROT_WRITE) && - (fs.object == fs.first_object)) { - vm_page_cowfault(fs.m); - unlock_and_deallocate(&fs); - goto RetryFault; - } - /* * Wait/Retry if the page is busy. We have to do this - * if the page is busy via either VPO_BUSY or - * vm_page_t->busy because the vm_pager may be using - * vm_page_t->busy for pageouts ( and even pageins if - * it is the vnode pager ), and we could end up trying - * to pagein and pageout the same page simultaneously. + * if the page is either exclusive or shared busy + * because the vm_pager may be using read busy for + * pageouts (and even pageins if it is the vnode + * pager), and we could end up trying to pagein and + * pageout the same page simultaneously. * * We can theoretically allow the busy case on a read * fault if the page is marked valid, but since such * pages are typically already pmap'd, putting that * special case in might be more effort then it is * worth. We cannot under any circumstances mess - * around with a vm_page_t->busy page except, perhaps, + * around with a shared busied page except, perhaps, * to pmap it. */ - if ((fs.m->oflags & VPO_BUSY) || fs.m->busy) { + if (vm_page_busied(fs.m)) { /* * Reference the page before unlocking and * sleeping so that the page daemon is less * likely to reclaim it. */ vm_page_aflag_set(fs.m, PGA_REFERENCED); - vm_page_unlock(fs.m); if (fs.object != fs.first_object) { if (!VM_OBJECT_TRYWLOCK( fs.first_object)) { @@ -392,8 +373,7 @@ RetryFault:; unlock_map(&fs); if (fs.m == vm_page_lookup(fs.object, fs.pindex)) { - vm_page_sleep_if_busy(fs.m, TRUE, - "vmpfw"); + vm_page_sleep_if_busy(fs.m, "vmpfw"); } vm_object_pip_wakeup(fs.object); VM_OBJECT_WUNLOCK(fs.object); @@ -401,6 +381,7 @@ RetryFault:; vm_object_deallocate(fs.first_object); goto RetryFault; } + vm_page_lock(fs.m); vm_page_remque(fs.m); vm_page_unlock(fs.m); @@ -410,7 +391,7 @@ RetryFault:; * (readable), jump to readrest, else break-out ( we * found the page ). */ - vm_page_busy(fs.m); + vm_page_xbusy(fs.m); if (fs.m->valid != VM_PAGE_BITS_ALL) goto readrest; break; @@ -516,7 +497,7 @@ readrest: /* * Call the pager to retrieve the data, if any, after * releasing the lock on the map. We hold a ref on - * fs.object and the pages are VPO_BUSY'd. + * fs.object and the pages are exclusive busied. */ unlock_map(&fs); @@ -565,7 +546,7 @@ vnode_locked: * return value is the index into the marray for the * vm_page_t passed to the routine. * - * fs.m plus the additional pages are VPO_BUSY'd. + * fs.m plus the additional pages are exclusive busied. */ faultcount = vm_fault_additional_pages( fs.m, behind, ahead, marray, &reqpage); @@ -691,8 +672,7 @@ vnode_locked: } } - KASSERT((fs.m->oflags & VPO_BUSY) != 0, - ("vm_fault: not busy after main loop")); + vm_page_assert_xbusied(fs.m); /* * PAGE HAS BEEN FOUND. [Loop invariant still holds -- the object lock @@ -754,10 +734,12 @@ vnode_locked: * process'es object. The page is * automatically made dirty. */ - vm_page_lock(fs.m); - vm_page_rename(fs.m, fs.first_object, fs.first_pindex); - vm_page_unlock(fs.m); - vm_page_busy(fs.m); + if (vm_page_rename(fs.m, fs.first_object, + fs.first_pindex)) { + unlock_and_deallocate(&fs); + goto RetryFault; + } + vm_page_xbusy(fs.m); fs.first_m = fs.m; fs.m = NULL; PCPU_INC(cnt.v_cow_optim); @@ -905,11 +887,8 @@ vnode_locked: } } - /* - * Page had better still be busy - */ - KASSERT(fs.m->oflags & VPO_BUSY, - ("vm_fault: page %p not busy!", fs.m)); + vm_page_assert_xbusied(fs.m); + /* * Page must be completely valid or it is not fit to * map into user space. vm_pager_get_pages() ensures this. @@ -946,7 +925,7 @@ vnode_locked: vm_page_hold(fs.m); } vm_page_unlock(fs.m); - vm_page_wakeup(fs.m); + vm_page_xunbusy(fs.m); /* * Unlock everything, and return @@ -991,13 +970,12 @@ vm_fault_cache_behind(const struct faultstate *fs, int distance) if (pindex < OFF_TO_IDX(fs->entry->offset)) pindex = OFF_TO_IDX(fs->entry->offset); m = first_object != object ? fs->first_m : fs->m; - KASSERT((m->oflags & VPO_BUSY) != 0, - ("vm_fault_cache_behind: page %p is not busy", m)); + vm_page_assert_xbusied(m); m_prev = vm_page_prev(m); while ((m = m_prev) != NULL && m->pindex >= pindex && m->valid == VM_PAGE_BITS_ALL) { m_prev = vm_page_prev(m); - if (m->busy != 0 || (m->oflags & VPO_BUSY) != 0) + if (vm_page_busied(m)) continue; vm_page_lock(m); if (m->hold_count == 0 && m->wire_count == 0) { @@ -1378,7 +1356,7 @@ vm_fault_copy_entry(vm_map_t dst_map, vm_map_t src_map, vm_page_activate(dst_m); vm_page_unlock(dst_m); } - vm_page_wakeup(dst_m); + vm_page_xunbusy(dst_m); } VM_OBJECT_WUNLOCK(dst_object); if (upgrade) { diff --git a/sys/vm/vm_glue.c b/sys/vm/vm_glue.c index 948e2b3c0d2..4512039d0d9 100644 --- a/sys/vm/vm_glue.c +++ b/sys/vm/vm_glue.c @@ -67,6 +67,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include #include @@ -76,6 +77,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include #include @@ -231,10 +233,8 @@ vm_imgact_hold_page(vm_object_t object, vm_ooffset_t offset) VM_OBJECT_WLOCK(object); pindex = OFF_TO_IDX(offset); - m = vm_page_grab(object, pindex, VM_ALLOC_NORMAL | VM_ALLOC_RETRY | - VM_ALLOC_NOBUSY); + m = vm_page_grab(object, pindex, VM_ALLOC_NORMAL); if (m->valid != VM_PAGE_BITS_ALL) { - vm_page_busy(m); ma[0] = m; rv = vm_pager_get_pages(object, ma, 1, 0); m = vm_page_lookup(object, pindex); @@ -247,8 +247,8 @@ vm_imgact_hold_page(vm_object_t object, vm_ooffset_t offset) m = NULL; goto out; } - vm_page_wakeup(m); } + vm_page_xunbusy(m); vm_page_lock(m); vm_page_hold(m); vm_page_unlock(m); @@ -359,11 +359,13 @@ vm_thread_new(struct thread *td, int pages) * We need to align the kstack's mapped address to fit within * a single TLB entry. */ - ks = kmem_alloc_nofault_space(kernel_map, - (pages + KSTACK_GUARD_PAGES) * PAGE_SIZE, VMFS_TLB_ALIGNED_SPACE); + if (vmem_xalloc(kernel_arena, (pages + KSTACK_GUARD_PAGES) * PAGE_SIZE, + PAGE_SIZE * 2, 0, 0, VMEM_ADDR_MIN, VMEM_ADDR_MAX, + M_BESTFIT | M_NOWAIT, &ks)) { + ks = 0; + } #else - ks = kmem_alloc_nofault(kernel_map, - (pages + KSTACK_GUARD_PAGES) * PAGE_SIZE); + ks = kva_alloc((pages + KSTACK_GUARD_PAGES) * PAGE_SIZE); #endif if (ks == 0) { printf("vm_thread_new: kstack allocation failed\n"); @@ -393,7 +395,7 @@ vm_thread_new(struct thread *td, int pages) * Get a kernel stack page. */ m = vm_page_grab(ksobj, i, VM_ALLOC_NOBUSY | - VM_ALLOC_NORMAL | VM_ALLOC_RETRY | VM_ALLOC_WIRED); + VM_ALLOC_NORMAL | VM_ALLOC_WIRED); ma[i] = m; m->valid = VM_PAGE_BITS_ALL; } @@ -422,7 +424,7 @@ vm_thread_stack_dispose(vm_object_t ksobj, vm_offset_t ks, int pages) } VM_OBJECT_WUNLOCK(ksobj); vm_object_deallocate(ksobj); - kmem_free(kernel_map, ks - (KSTACK_GUARD_PAGES * PAGE_SIZE), + kva_free(ks - (KSTACK_GUARD_PAGES * PAGE_SIZE), (pages + KSTACK_GUARD_PAGES) * PAGE_SIZE); } @@ -525,17 +527,15 @@ vm_thread_swapin(struct thread *td) ksobj = td->td_kstack_obj; VM_OBJECT_WLOCK(ksobj); for (i = 0; i < pages; i++) - ma[i] = vm_page_grab(ksobj, i, VM_ALLOC_NORMAL | VM_ALLOC_RETRY | + ma[i] = vm_page_grab(ksobj, i, VM_ALLOC_NORMAL | VM_ALLOC_WIRED); for (i = 0; i < pages; i++) { if (ma[i]->valid != VM_PAGE_BITS_ALL) { - KASSERT(ma[i]->oflags & VPO_BUSY, - ("lost busy 1")); + vm_page_assert_xbusied(ma[i]); vm_object_pip_add(ksobj, 1); for (j = i + 1; j < pages; j++) { - KASSERT(ma[j]->valid == VM_PAGE_BITS_ALL || - (ma[j]->oflags & VPO_BUSY), - ("lost busy 2")); + if (ma[j]->valid != VM_PAGE_BITS_ALL) + vm_page_assert_xbusied(ma[j]); if (ma[j]->valid == VM_PAGE_BITS_ALL) break; } @@ -546,9 +546,9 @@ vm_thread_swapin(struct thread *td) vm_object_pip_wakeup(ksobj); for (k = i; k < j; k++) ma[k] = vm_page_lookup(ksobj, k); - vm_page_wakeup(ma[i]); - } else if (ma[i]->oflags & VPO_BUSY) - vm_page_wakeup(ma[i]); + vm_page_xunbusy(ma[i]); + } else if (vm_page_xbusied(ma[i])) + vm_page_xunbusy(ma[i]); } VM_OBJECT_WUNLOCK(ksobj); pmap_qenter(td->td_kstack, ma, pages); diff --git a/sys/vm/vm_init.c b/sys/vm/vm_init.c index 2c4bcb6b302..30faa5a2aeb 100644 --- a/sys/vm/vm_init.c +++ b/sys/vm/vm_init.c @@ -70,6 +70,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include #include @@ -100,6 +101,26 @@ SYSCTL_INT(_vm, OID_AUTO, exec_map_entries, CTLFLAG_RD, &exec_map_entries, 0, static void vm_mem_init(void *); SYSINIT(vm_mem, SI_SUB_VM, SI_ORDER_FIRST, vm_mem_init, NULL); +/* + * Import kva into the kernel arena. + */ +static int +kva_import(void *unused, vmem_size_t size, int flags, vmem_addr_t *addrp) +{ + vm_offset_t addr; + int result; + + addr = vm_map_min(kernel_map); + result = vm_map_find(kernel_map, NULL, 0, &addr, size, 0, + VMFS_SUPER_SPACE, VM_PROT_ALL, VM_PROT_ALL, MAP_NOFAULT); + if (result != KERN_SUCCESS) + return (ENOMEM); + + *addrp = addr; + + return (0); +} + /* * vm_init initializes the virtual memory system. * This is done only by the first cpu up. @@ -111,6 +132,7 @@ static void vm_mem_init(dummy) void *dummy; { + /* * Initializes resident memory structures. From here on, all physical * memory is accounted for, and we use only virtual addresses. @@ -125,6 +147,20 @@ vm_mem_init(dummy) vm_object_init(); vm_map_startup(); kmem_init(virtual_avail, virtual_end); + + /* + * Initialize the kernel_arena. This can grow on demand. + */ + vmem_init(kernel_arena, "kernel arena", 0, 0, PAGE_SIZE, 0, 0); + vmem_set_import(kernel_arena, kva_import, NULL, NULL, +#if VM_NRESERVLEVEL > 0 + 1 << (VM_LEVEL_0_ORDER + PAGE_SHIFT)); +#else + /* On non-superpage architectures want large import sizes. */ + PAGE_SIZE * 1024); +#endif + + kmem_init_zero_region(); pmap_init(); vm_pager_init(); } @@ -138,7 +174,6 @@ vm_ksubmap_init(struct kva_md_info *kmi) long physmem_est; vm_offset_t minaddr; vm_offset_t maxaddr; - vm_map_t clean_map; /* * Allocate space for system data structures. @@ -146,8 +181,6 @@ vm_ksubmap_init(struct kva_md_info *kmi) * As pages of kernel virtual memory are allocated, "v" is incremented. * As pages of memory are allocated and cleared, * "firstaddr" is incremented. - * An index into the kernel page table corresponding to the - * virtual memory address maintained in "v" is kept in "mapaddr". */ /* @@ -173,7 +206,8 @@ again: */ if (firstaddr == 0) { size = (vm_size_t)v; - firstaddr = kmem_alloc(kernel_map, round_page(size)); + firstaddr = kmem_malloc(kernel_arena, round_page(size), + M_ZERO | M_WAITOK); if (firstaddr == 0) panic("startup: no room for tables"); goto again; @@ -185,31 +219,49 @@ again: if ((vm_size_t)((char *)v - firstaddr) != size) panic("startup: table size inconsistency"); + /* + * Allocate the clean map to hold all of the paging and I/O virtual + * memory. + */ size = (long)nbuf * BKVASIZE + (long)nswbuf * MAXPHYS + (long)bio_transient_maxcnt * MAXPHYS; - clean_map = kmem_suballoc(kernel_map, &kmi->clean_sva, &kmi->clean_eva, - size, TRUE); + kmi->clean_sva = firstaddr = kva_alloc(size); + kmi->clean_eva = firstaddr + size; + /* + * Allocate the buffer arena. + */ size = (long)nbuf * BKVASIZE; - kmi->buffer_sva = kmem_alloc_nofault(clean_map, size); + kmi->buffer_sva = firstaddr; kmi->buffer_eva = kmi->buffer_sva + size; vmem_init(buffer_arena, "buffer arena", kmi->buffer_sva, size, PAGE_SIZE, 0, 0); + firstaddr += size; + /* + * Now swap kva. + */ + swapbkva = firstaddr; size = (long)nswbuf * MAXPHYS; - swapbkva = kmem_alloc_nofault(clean_map, size); - if (!swapbkva) - panic("Not enough clean_map VM space for pager buffers"); + firstaddr += size; + /* + * And optionally transient bio space. + */ if (bio_transient_maxcnt != 0) { size = (long)bio_transient_maxcnt * MAXPHYS; vmem_init(transient_arena, "transient arena", - kmem_alloc_nofault(clean_map, size), - size, PAGE_SIZE, 0, 0); + firstaddr, size, PAGE_SIZE, 0, 0); + firstaddr += size; } + if (firstaddr != kmi->clean_eva) + panic("Clean map calculation incorrect"); + + /* + * Allocate the pageable submaps. + */ exec_map = kmem_suballoc(kernel_map, &minaddr, &maxaddr, exec_map_entries * round_page(PATH_MAX + ARG_MAX), FALSE); pipe_map = kmem_suballoc(kernel_map, &minaddr, &maxaddr, maxpipekva, FALSE); } - diff --git a/sys/vm/vm_kern.c b/sys/vm/vm_kern.c index 42cd6991007..dd075c1569a 100644 --- a/sys/vm/vm_kern.c +++ b/sys/vm/vm_kern.c @@ -74,9 +74,11 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include +#include #include #include #include @@ -86,7 +88,6 @@ __FBSDID("$FreeBSD$"); #include vm_map_t kernel_map; -vm_map_t kmem_map; vm_map_t exec_map; vm_map_t pipe_map; @@ -105,7 +106,7 @@ SYSCTL_ULONG(_vm, OID_AUTO, max_kernel_address, CTLFLAG_RD, "Max kernel address"); /* - * kmem_alloc_nofault: + * kva_alloc: * * Allocate a virtual address range with no underlying object and * no initial mapping to physical memory. Any mapping from this @@ -114,94 +115,35 @@ SYSCTL_ULONG(_vm, OID_AUTO, max_kernel_address, CTLFLAG_RD, * a mapping on demand through vm_fault() will result in a panic. */ vm_offset_t -kmem_alloc_nofault(map, size) - vm_map_t map; +kva_alloc(size) vm_size_t size; { vm_offset_t addr; - int result; size = round_page(size); - addr = vm_map_min(map); - result = vm_map_find(map, NULL, 0, &addr, size, VMFS_ANY_SPACE, - VM_PROT_ALL, VM_PROT_ALL, MAP_NOFAULT); - if (result != KERN_SUCCESS) { + if (vmem_alloc(kernel_arena, size, M_BESTFIT | M_NOWAIT, &addr)) return (0); - } + return (addr); } /* - * kmem_alloc_nofault_space: + * kva_free: * - * Allocate a virtual address range with no underlying object and - * no initial mapping to physical memory within the specified - * address space. Any mapping from this range to physical memory - * must be explicitly created prior to its use, typically with - * pmap_qenter(). Any attempt to create a mapping on demand - * through vm_fault() will result in a panic. + * Release a region of kernel virtual memory allocated + * with kva_alloc, and return the physical pages + * associated with that region. + * + * This routine may not block on kernel maps. */ -vm_offset_t -kmem_alloc_nofault_space(map, size, find_space) - vm_map_t map; - vm_size_t size; - int find_space; -{ +void +kva_free(addr, size) vm_offset_t addr; - int result; - - size = round_page(size); - addr = vm_map_min(map); - result = vm_map_find(map, NULL, 0, &addr, size, find_space, - VM_PROT_ALL, VM_PROT_ALL, MAP_NOFAULT); - if (result != KERN_SUCCESS) { - return (0); - } - return (addr); -} - -/* - * Allocate wired-down memory in the kernel's address map - * or a submap. - */ -vm_offset_t -kmem_alloc(map, size) - vm_map_t map; vm_size_t size; { - vm_offset_t addr; - vm_offset_t offset; size = round_page(size); - - /* - * Use the kernel object for wired-down kernel pages. Assume that no - * region of the kernel object is referenced more than once. - */ - - /* - * Locate sufficient space in the map. This will give us the final - * virtual address for the new memory, and thus will tell us the - * offset within the kernel map. - */ - vm_map_lock(map); - if (vm_map_findspace(map, vm_map_min(map), size, &addr)) { - vm_map_unlock(map); - return (0); - } - offset = addr - VM_MIN_KERNEL_ADDRESS; - vm_object_reference(kernel_object); - vm_map_insert(map, kernel_object, offset, addr, addr + size, - VM_PROT_ALL, VM_PROT_ALL, 0); - vm_map_unlock(map); - - /* - * And finally, mark the data as non-pageable. - */ - (void) vm_map_wire(map, addr, addr + size, - VM_MAP_WIRE_SYSTEM|VM_MAP_WIRE_NOHOLES); - - return (addr); + vmem_free(kernel_arena, addr, size); } /* @@ -213,62 +155,57 @@ kmem_alloc(map, size) * given flags, then the pages are zeroed before they are mapped. */ vm_offset_t -kmem_alloc_attr(vm_map_t map, vm_size_t size, int flags, vm_paddr_t low, +kmem_alloc_attr(vmem_t *vmem, vm_size_t size, int flags, vm_paddr_t low, vm_paddr_t high, vm_memattr_t memattr) { - vm_object_t object = kernel_object; + vm_object_t object = vmem == kmem_arena ? kmem_object : kernel_object; vm_offset_t addr; - vm_ooffset_t end_offset, offset; + vm_ooffset_t offset; vm_page_t m; int pflags, tries; + int i; size = round_page(size); - vm_map_lock(map); - if (vm_map_findspace(map, vm_map_min(map), size, &addr)) { - vm_map_unlock(map); + if (vmem_alloc(vmem, size, M_BESTFIT | flags, &addr)) return (0); - } offset = addr - VM_MIN_KERNEL_ADDRESS; - vm_object_reference(object); - vm_map_insert(map, object, offset, addr, addr + size, VM_PROT_ALL, - VM_PROT_ALL, 0); - pflags = malloc2vm_flags(flags) | VM_ALLOC_NOBUSY; + pflags = malloc2vm_flags(flags) | VM_ALLOC_NOBUSY | VM_ALLOC_WIRED; VM_OBJECT_WLOCK(object); - end_offset = offset + size; - for (; offset < end_offset; offset += PAGE_SIZE) { + for (i = 0; i < size; i += PAGE_SIZE) { tries = 0; retry: - m = vm_page_alloc_contig(object, OFF_TO_IDX(offset), pflags, 1, - low, high, PAGE_SIZE, 0, memattr); + m = vm_page_alloc_contig(object, OFF_TO_IDX(offset + i), + pflags, 1, low, high, PAGE_SIZE, 0, memattr); if (m == NULL) { VM_OBJECT_WUNLOCK(object); if (tries < ((flags & M_NOWAIT) != 0 ? 1 : 3)) { - vm_map_unlock(map); vm_pageout_grow_cache(tries, low, high); - vm_map_lock(map); VM_OBJECT_WLOCK(object); tries++; goto retry; } - - /* - * Since the pages that were allocated by any previous - * iterations of this loop are not busy, they can be - * freed by vm_object_page_remove(), which is called - * by vm_map_delete(). + /* + * Unmap and free the pages. */ - vm_map_delete(map, addr, addr + size); - vm_map_unlock(map); + if (i != 0) + pmap_remove(kernel_pmap, addr, addr + i); + while (i != 0) { + i -= PAGE_SIZE; + m = vm_page_lookup(object, + OFF_TO_IDX(offset + i)); + vm_page_unwire(m, 0); + vm_page_free(m); + } + vmem_free(vmem, addr, size); return (0); } if ((flags & M_ZERO) && (m->flags & PG_ZERO) == 0) pmap_zero_page(m); m->valid = VM_PAGE_BITS_ALL; + pmap_enter(kernel_pmap, addr + i, VM_PROT_ALL, m, VM_PROT_ALL, + TRUE); } VM_OBJECT_WUNLOCK(object); - vm_map_unlock(map); - vm_map_wire(map, addr, addr + size, VM_MAP_WIRE_SYSTEM | - VM_MAP_WIRE_NOHOLES); return (addr); } @@ -281,27 +218,21 @@ retry: * mapped. */ vm_offset_t -kmem_alloc_contig(vm_map_t map, vm_size_t size, int flags, vm_paddr_t low, +kmem_alloc_contig(struct vmem *vmem, vm_size_t size, int flags, vm_paddr_t low, vm_paddr_t high, u_long alignment, vm_paddr_t boundary, vm_memattr_t memattr) { - vm_object_t object = kernel_object; - vm_offset_t addr; + vm_object_t object = vmem == kmem_arena ? kmem_object : kernel_object; + vm_offset_t addr, tmp; vm_ooffset_t offset; vm_page_t end_m, m; int pflags, tries; size = round_page(size); - vm_map_lock(map); - if (vm_map_findspace(map, vm_map_min(map), size, &addr)) { - vm_map_unlock(map); + if (vmem_alloc(vmem, size, flags | M_BESTFIT, &addr)) return (0); - } offset = addr - VM_MIN_KERNEL_ADDRESS; - vm_object_reference(object); - vm_map_insert(map, object, offset, addr, addr + size, VM_PROT_ALL, - VM_PROT_ALL, 0); - pflags = malloc2vm_flags(flags) | VM_ALLOC_NOBUSY; + pflags = malloc2vm_flags(flags) | VM_ALLOC_NOBUSY | VM_ALLOC_WIRED; VM_OBJECT_WLOCK(object); tries = 0; retry: @@ -310,49 +241,27 @@ retry: if (m == NULL) { VM_OBJECT_WUNLOCK(object); if (tries < ((flags & M_NOWAIT) != 0 ? 1 : 3)) { - vm_map_unlock(map); vm_pageout_grow_cache(tries, low, high); - vm_map_lock(map); VM_OBJECT_WLOCK(object); tries++; goto retry; } - vm_map_delete(map, addr, addr + size); - vm_map_unlock(map); + vmem_free(vmem, addr, size); return (0); } end_m = m + atop(size); + tmp = addr; for (; m < end_m; m++) { if ((flags & M_ZERO) && (m->flags & PG_ZERO) == 0) pmap_zero_page(m); m->valid = VM_PAGE_BITS_ALL; + pmap_enter(kernel_pmap, tmp, VM_PROT_ALL, m, VM_PROT_ALL, true); + tmp += PAGE_SIZE; } VM_OBJECT_WUNLOCK(object); - vm_map_unlock(map); - vm_map_wire(map, addr, addr + size, VM_MAP_WIRE_SYSTEM | - VM_MAP_WIRE_NOHOLES); return (addr); } -/* - * kmem_free: - * - * Release a region of kernel virtual memory allocated - * with kmem_alloc, and return the physical pages - * associated with that region. - * - * This routine may not block on kernel maps. - */ -void -kmem_free(map, addr, size) - vm_map_t map; - vm_offset_t addr; - vm_size_t size; -{ - - (void) vm_map_remove(map, trunc_page(addr), round_page(addr + size)); -} - /* * kmem_suballoc: * @@ -376,8 +285,8 @@ kmem_suballoc(vm_map_t parent, vm_offset_t *min, vm_offset_t *max, size = round_page(size); *min = vm_map_min(parent); - ret = vm_map_find(parent, NULL, 0, min, size, superpage_align ? - VMFS_ALIGNED_SPACE : VMFS_ANY_SPACE, VM_PROT_ALL, VM_PROT_ALL, + ret = vm_map_find(parent, NULL, 0, min, size, 0, superpage_align ? + VMFS_SUPER_SPACE : VMFS_ANY_SPACE, VM_PROT_ALL, VM_PROT_ALL, MAP_ACC_NO_CHARGE); if (ret != KERN_SUCCESS) panic("kmem_suballoc: bad status return of %d", ret); @@ -393,65 +302,25 @@ kmem_suballoc(vm_map_t parent, vm_offset_t *min, vm_offset_t *max, /* * kmem_malloc: * - * Allocate wired-down memory in the kernel's address map for the higher - * level kernel memory allocator (kern/kern_malloc.c). We cannot use - * kmem_alloc() because we may need to allocate memory at interrupt - * level where we cannot block (canwait == FALSE). - * - * This routine has its own private kernel submap (kmem_map) and object - * (kmem_object). This, combined with the fact that only malloc uses - * this routine, ensures that we will never block in map or object waits. - * - * We don't worry about expanding the map (adding entries) since entries - * for wired maps are statically allocated. - * - * `map' is ONLY allowed to be kmem_map or one of the mbuf submaps to - * which we never free. + * Allocate wired-down pages in the kernel's address space. */ vm_offset_t -kmem_malloc(map, size, flags) - vm_map_t map; - vm_size_t size; - int flags; +kmem_malloc(struct vmem *vmem, vm_size_t size, int flags) { vm_offset_t addr; - int i, rv; + int rv; size = round_page(size); - addr = vm_map_min(map); + if (vmem_alloc(vmem, size, flags | M_BESTFIT, &addr)) + return (0); - /* - * Locate sufficient space in the map. This will give us the final - * virtual address for the new memory, and thus will tell us the - * offset within the kernel map. - */ - vm_map_lock(map); - if (vm_map_findspace(map, vm_map_min(map), size, &addr)) { - vm_map_unlock(map); - if ((flags & M_NOWAIT) == 0) { - for (i = 0; i < 8; i++) { - EVENTHANDLER_INVOKE(vm_lowmem, 0); - uma_reclaim(); - vm_map_lock(map); - if (vm_map_findspace(map, vm_map_min(map), - size, &addr) == 0) { - break; - } - vm_map_unlock(map); - tsleep(&i, 0, "nokva", (hz / 4) * (i + 1)); - } - if (i == 8) { - panic("kmem_malloc(%ld): kmem_map too small: %ld total allocated", - (long)size, (long)map->size); - } - } else { - return (0); - } + rv = kmem_back((vmem == kmem_arena) ? kmem_object : kernel_object, + addr, size, flags); + if (rv != KERN_SUCCESS) { + vmem_free(vmem, addr, size); + return (0); } - - rv = kmem_back(map, addr, size, flags); - vm_map_unlock(map); - return (rv == KERN_SUCCESS ? addr : 0); + return (addr); } /* @@ -460,37 +329,22 @@ kmem_malloc(map, size, flags) * Allocate physical pages for the specified virtual address range. */ int -kmem_back(vm_map_t map, vm_offset_t addr, vm_size_t size, int flags) +kmem_back(vm_object_t object, vm_offset_t addr, vm_size_t size, int flags) { vm_offset_t offset, i; - vm_map_entry_t entry; vm_page_t m; int pflags; - boolean_t found; - KASSERT(vm_map_locked(map), ("kmem_back: map %p is not locked", map)); + KASSERT(object == kmem_object || object == kernel_object, + ("kmem_back: only supports kernel objects.")); + offset = addr - VM_MIN_KERNEL_ADDRESS; - vm_object_reference(kmem_object); - vm_map_insert(map, kmem_object, offset, addr, addr + size, - VM_PROT_ALL, VM_PROT_ALL, 0); + pflags = malloc2vm_flags(flags) | VM_ALLOC_NOBUSY | VM_ALLOC_WIRED; - /* - * Assert: vm_map_insert() will never be able to extend the - * previous entry so vm_map_lookup_entry() will find a new - * entry exactly corresponding to this address range and it - * will have wired_count == 0. - */ - found = vm_map_lookup_entry(map, addr, &entry); - KASSERT(found && entry->start == addr && entry->end == addr + size && - entry->wired_count == 0 && (entry->eflags & MAP_ENTRY_IN_TRANSITION) - == 0, ("kmem_back: entry not found or misaligned")); - - pflags = malloc2vm_flags(flags) | VM_ALLOC_WIRED; - - VM_OBJECT_WLOCK(kmem_object); + VM_OBJECT_WLOCK(object); for (i = 0; i < size; i += PAGE_SIZE) { retry: - m = vm_page_alloc(kmem_object, OFF_TO_IDX(offset + i), pflags); + m = vm_page_alloc(object, OFF_TO_IDX(offset + i), pflags); /* * Ran out of space, free everything up and return. Don't need @@ -499,79 +353,78 @@ retry: */ if (m == NULL) { if ((flags & M_NOWAIT) == 0) { - VM_OBJECT_WUNLOCK(kmem_object); - entry->eflags |= MAP_ENTRY_IN_TRANSITION; - vm_map_unlock(map); + VM_OBJECT_WUNLOCK(object); VM_WAIT; - vm_map_lock(map); - KASSERT( -(entry->eflags & (MAP_ENTRY_IN_TRANSITION | MAP_ENTRY_NEEDS_WAKEUP)) == - MAP_ENTRY_IN_TRANSITION, - ("kmem_back: volatile entry")); - entry->eflags &= ~MAP_ENTRY_IN_TRANSITION; - VM_OBJECT_WLOCK(kmem_object); + VM_OBJECT_WLOCK(object); goto retry; } /* - * Free the pages before removing the map entry. - * They are already marked busy. Calling - * vm_map_delete before the pages has been freed or - * unbusied will cause a deadlock. + * Unmap and free the pages. */ + if (i != 0) + pmap_remove(kernel_pmap, addr, addr + i); while (i != 0) { i -= PAGE_SIZE; - m = vm_page_lookup(kmem_object, + m = vm_page_lookup(object, OFF_TO_IDX(offset + i)); vm_page_unwire(m, 0); vm_page_free(m); } - VM_OBJECT_WUNLOCK(kmem_object); - vm_map_delete(map, addr, addr + size); + VM_OBJECT_WUNLOCK(object); return (KERN_NO_SPACE); } if (flags & M_ZERO && (m->flags & PG_ZERO) == 0) pmap_zero_page(m); - m->valid = VM_PAGE_BITS_ALL; KASSERT((m->oflags & VPO_UNMANAGED) != 0, ("kmem_malloc: page %p is managed", m)); - } - VM_OBJECT_WUNLOCK(kmem_object); - - /* - * Mark map entry as non-pageable. Repeat the assert. - */ - KASSERT(entry->start == addr && entry->end == addr + size && - entry->wired_count == 0, - ("kmem_back: entry not found or misaligned after allocation")); - entry->wired_count = 1; - - /* - * At this point, the kmem_object must be unlocked because - * vm_map_simplify_entry() calls vm_object_deallocate(), which - * locks the kmem_object. - */ - vm_map_simplify_entry(map, entry); - - /* - * Loop thru pages, entering them in the pmap. - */ - VM_OBJECT_WLOCK(kmem_object); - for (i = 0; i < size; i += PAGE_SIZE) { - m = vm_page_lookup(kmem_object, OFF_TO_IDX(offset + i)); - /* - * Because this is kernel_pmap, this call will not block. - */ + m->valid = VM_PAGE_BITS_ALL; pmap_enter(kernel_pmap, addr + i, VM_PROT_ALL, m, VM_PROT_ALL, TRUE); - vm_page_wakeup(m); } - VM_OBJECT_WUNLOCK(kmem_object); + VM_OBJECT_WUNLOCK(object); return (KERN_SUCCESS); } +void +kmem_unback(vm_object_t object, vm_offset_t addr, vm_size_t size) +{ + vm_page_t m; + vm_offset_t offset; + int i; + + KASSERT(object == kmem_object || object == kernel_object, + ("kmem_unback: only supports kernel objects.")); + + offset = addr - VM_MIN_KERNEL_ADDRESS; + VM_OBJECT_WLOCK(object); + pmap_remove(kernel_pmap, addr, addr + size); + for (i = 0; i < size; i += PAGE_SIZE) { + m = vm_page_lookup(object, OFF_TO_IDX(offset + i)); + vm_page_unwire(m, 0); + vm_page_free(m); + } + VM_OBJECT_WUNLOCK(object); +} + /* - * kmem_alloc_wait: + * kmem_free: + * + * Free memory allocated with kmem_malloc. The size must match the + * original allocation. + */ +void +kmem_free(struct vmem *vmem, vm_offset_t addr, vm_size_t size) +{ + + size = round_page(size); + kmem_unback((vmem == kmem_arena) ? kmem_object : kernel_object, + addr, size); + vmem_free(vmem, addr, size); +} + +/* + * kmap_alloc_wait: * * Allocates pageable memory from a sub-map of the kernel. If the submap * has no room, the caller sleeps waiting for more memory in the submap. @@ -579,7 +432,7 @@ retry: * This routine may block. */ vm_offset_t -kmem_alloc_wait(map, size) +kmap_alloc_wait(map, size) vm_map_t map; vm_size_t size; { @@ -613,13 +466,13 @@ kmem_alloc_wait(map, size) } /* - * kmem_free_wakeup: + * kmap_free_wakeup: * * Returns memory to a submap of the kernel, and wakes up any processes * waiting for memory in that map. */ void -kmem_free_wakeup(map, addr, size) +kmap_free_wakeup(map, addr, size) vm_map_t map; vm_offset_t addr; vm_size_t size; @@ -634,28 +487,25 @@ kmem_free_wakeup(map, addr, size) vm_map_unlock(map); } -static void +void kmem_init_zero_region(void) { vm_offset_t addr, i; vm_page_t m; - int error; /* * Map a single physical page of zeros to a larger virtual range. * This requires less looping in places that want large amounts of * zeros, while not using much more physical resources. */ - addr = kmem_alloc_nofault(kernel_map, ZERO_REGION_SIZE); + addr = kva_alloc(ZERO_REGION_SIZE); m = vm_page_alloc(NULL, 0, VM_ALLOC_NORMAL | VM_ALLOC_NOOBJ | VM_ALLOC_WIRED | VM_ALLOC_ZERO); if ((m->flags & PG_ZERO) == 0) pmap_zero_page(m); for (i = 0; i < ZERO_REGION_SIZE; i += PAGE_SIZE) pmap_qenter(addr + i, &m, 1); - error = vm_map_protect(kernel_map, addr, addr + ZERO_REGION_SIZE, - VM_PROT_READ, TRUE); - KASSERT(error == 0, ("error=%d", error)); + pmap_protect(kernel_pmap, addr, addr + ZERO_REGION_SIZE, VM_PROT_READ); zero_region = (const void *)addr; } @@ -688,8 +538,6 @@ kmem_init(start, end) start, VM_PROT_ALL, VM_PROT_ALL, MAP_NOFAULT); /* ... and ending with the completion of the above `insert' */ vm_map_unlock(m); - - kmem_init_zero_region(); } #ifdef DIAGNOSTIC diff --git a/sys/vm/vm_kern.h b/sys/vm/vm_kern.h index 1479e5f9283..aec3ebde74f 100644 --- a/sys/vm/vm_kern.h +++ b/sys/vm/vm_kern.h @@ -65,11 +65,13 @@ /* Kernel memory management definitions. */ extern vm_map_t kernel_map; -extern vm_map_t kmem_map; extern vm_map_t exec_map; extern vm_map_t pipe_map; +extern struct vmem *kernel_arena; +extern struct vmem *kmem_arena; extern struct vmem *buffer_arena; extern struct vmem *transient_arena; +extern struct vmem *memguard_arena; extern vm_offset_t swapbkva; extern u_long vm_kmem_size; diff --git a/sys/vm/vm_map.c b/sys/vm/vm_map.c index c43bce21b74..59906300618 100644 --- a/sys/vm/vm_map.c +++ b/sys/vm/vm_map.c @@ -197,9 +197,15 @@ vm_map_startup(void) kmapentzone = uma_zcreate("KMAP ENTRY", sizeof(struct vm_map_entry), NULL, NULL, NULL, NULL, UMA_ALIGN_PTR, UMA_ZONE_MTXCLASS | UMA_ZONE_VM); - uma_prealloc(kmapentzone, MAX_KMAPENT); mapentzone = uma_zcreate("MAP ENTRY", sizeof(struct vm_map_entry), NULL, NULL, NULL, NULL, UMA_ALIGN_PTR, 0); + vmspace_zone = uma_zcreate("VMSPACE", sizeof(struct vmspace), NULL, +#ifdef INVARIANTS + vmspace_zdtor, +#else + NULL, +#endif + vmspace_zinit, vmspace_zfini, UMA_ALIGN_PTR, UMA_ZONE_NOFREE); } static void @@ -220,6 +226,7 @@ vmspace_zinit(void *mem, int size, int flags) vm->vm_map.pmap = NULL; (void)vm_map_zinit(&vm->vm_map, sizeof(vm->vm_map), flags); + PMAP_LOCK_INIT(vmspace_pmap(vm)); return (0); } @@ -299,21 +306,6 @@ vmspace_alloc(min, max) return (vm); } -void -vm_init2(void) -{ - uma_zone_reserve_kva(kmapentzone, lmin(cnt.v_page_count, - (VM_MAX_KERNEL_ADDRESS - VM_MIN_KERNEL_ADDRESS) / PAGE_SIZE) / 8 + - maxproc * 2 + maxfiles); - vmspace_zone = uma_zcreate("VMSPACE", sizeof(struct vmspace), NULL, -#ifdef INVARIANTS - vmspace_zdtor, -#else - NULL, -#endif - vmspace_zinit, vmspace_zfini, UMA_ALIGN_PTR, UMA_ZONE_NOFREE); -} - static void vmspace_container_reset(struct proc *p) { @@ -1440,22 +1432,28 @@ vm_map_fixed(vm_map_t map, vm_object_t object, vm_ooffset_t offset, int vm_map_find(vm_map_t map, vm_object_t object, vm_ooffset_t offset, vm_offset_t *addr, /* IN/OUT */ - vm_size_t length, int find_space, vm_prot_t prot, - vm_prot_t max, int cow) + vm_size_t length, vm_offset_t max_addr, int find_space, + vm_prot_t prot, vm_prot_t max, int cow) { - vm_offset_t start, initial_addr; + vm_offset_t alignment, initial_addr, start; int result; if (find_space == VMFS_OPTIMAL_SPACE && (object == NULL || (object->flags & OBJ_COLORED) == 0)) - find_space = VMFS_ANY_SPACE; + find_space = VMFS_ANY_SPACE; + if (find_space >> 8 != 0) { + KASSERT((find_space & 0xff) == 0, ("bad VMFS flags")); + alignment = (vm_offset_t)1 << (find_space >> 8); + } else + alignment = 0; initial_addr = *addr; again: start = initial_addr; vm_map_lock(map); do { if (find_space != VMFS_NO_SPACE) { - if (vm_map_findspace(map, start, length, addr)) { + if (vm_map_findspace(map, start, length, addr) || + (max_addr != 0 && *addr + length > max_addr)) { vm_map_unlock(map); if (find_space == VMFS_OPTIMAL_SPACE) { find_space = VMFS_ANY_SPACE; @@ -1464,17 +1462,18 @@ again: return (KERN_NO_SPACE); } switch (find_space) { - case VMFS_ALIGNED_SPACE: + case VMFS_SUPER_SPACE: case VMFS_OPTIMAL_SPACE: pmap_align_superpage(object, offset, addr, length); break; -#ifdef VMFS_TLB_ALIGNED_SPACE - case VMFS_TLB_ALIGNED_SPACE: - pmap_align_tlb(addr); + case VMFS_ANY_SPACE: break; -#endif default: + if ((*addr & (alignment - 1)) != 0) { + *addr &= ~(alignment - 1); + *addr += alignment; + } break; } @@ -1482,11 +1481,8 @@ again: } result = vm_map_insert(map, object, offset, start, start + length, prot, max, cow); - } while (result == KERN_NO_SPACE && (find_space == VMFS_ALIGNED_SPACE || -#ifdef VMFS_TLB_ALIGNED_SPACE - find_space == VMFS_TLB_ALIGNED_SPACE || -#endif - find_space == VMFS_OPTIMAL_SPACE)); + } while (result == KERN_NO_SPACE && find_space != VMFS_NO_SPACE && + find_space != VMFS_ANY_SPACE); vm_map_unlock(map); return (result); } @@ -2130,7 +2126,7 @@ vm_map_madvise( (current != &map->header) && (current->start < end); current = current->next ) { - vm_offset_t useStart; + vm_offset_t useEnd, useStart; if (current->eflags & MAP_ENTRY_IS_SUB_MAP) continue; @@ -2138,17 +2134,34 @@ vm_map_madvise( pstart = OFF_TO_IDX(current->offset); pend = pstart + atop(current->end - current->start); useStart = current->start; + useEnd = current->end; if (current->start < start) { pstart += atop(start - current->start); useStart = start; } - if (current->end > end) + if (current->end > end) { pend -= atop(current->end - end); + useEnd = end; + } if (pstart >= pend) continue; + /* + * Perform the pmap_advise() before clearing + * PGA_REFERENCED in vm_page_advise(). Otherwise, a + * concurrent pmap operation, such as pmap_remove(), + * could clear a reference in the pmap and set + * PGA_REFERENCED on the page before the pmap_advise() + * had completed. Consequently, the page would appear + * referenced based upon an old reference that + * occurred before this pmap_advise() ran. + */ + if (behav == MADV_DONTNEED || behav == MADV_FREE) + pmap_advise(map->pmap, useStart, useEnd, + behav); + vm_object_madvise(current->object.vm_object, pstart, pend, behav); if (behav == MADV_WILLNEED) { diff --git a/sys/vm/vm_map.h b/sys/vm/vm_map.h index 824a9a0f6e8..850bf257c53 100644 --- a/sys/vm/vm_map.h +++ b/sys/vm/vm_map.h @@ -339,15 +339,16 @@ long vmspace_resident_count(struct vmspace *vmspace); #define VM_FAULT_READ_AHEAD_MAX min(atop(MAXPHYS) - 1, UINT8_MAX) /* - * The following "find_space" options are supported by vm_map_find() + * The following "find_space" options are supported by vm_map_find(). + * + * For VMFS_ALIGNED_SPACE, the desired alignment is specified to + * the macro argument as log base 2 of the desired alignment. */ #define VMFS_NO_SPACE 0 /* don't find; use the given range */ #define VMFS_ANY_SPACE 1 /* find a range with any alignment */ #define VMFS_OPTIMAL_SPACE 2 /* find a range with optimal alignment*/ -#define VMFS_ALIGNED_SPACE 3 /* find a superpage-aligned range */ -#if defined(__mips__) -#define VMFS_TLB_ALIGNED_SPACE 4 /* find a TLB entry aligned range */ -#endif +#define VMFS_SUPER_SPACE 3 /* find a superpage-aligned range */ +#define VMFS_ALIGNED_SPACE(x) ((x) << 8) /* find a range with fixed alignment */ /* * vm_map_wire and vm_map_unwire option flags @@ -365,7 +366,7 @@ boolean_t vm_map_check_protection (vm_map_t, vm_offset_t, vm_offset_t, vm_prot_t vm_map_t vm_map_create(pmap_t, vm_offset_t, vm_offset_t); int vm_map_delete(vm_map_t, vm_offset_t, vm_offset_t); int vm_map_find(vm_map_t, vm_object_t, vm_ooffset_t, vm_offset_t *, vm_size_t, - int, vm_prot_t, vm_prot_t, int); + vm_offset_t, int, vm_prot_t, vm_prot_t, int); int vm_map_fixed(vm_map_t, vm_object_t, vm_ooffset_t, vm_offset_t, vm_size_t, vm_prot_t, vm_prot_t, int); int vm_map_findspace (vm_map_t, vm_offset_t, vm_size_t, vm_offset_t *); @@ -387,7 +388,6 @@ int vm_map_submap (vm_map_t, vm_offset_t, vm_offset_t, vm_map_t); int vm_map_sync(vm_map_t, vm_offset_t, vm_offset_t, boolean_t, boolean_t); int vm_map_madvise (vm_map_t, vm_offset_t, vm_offset_t, int); void vm_map_simplify_entry (vm_map_t, vm_map_entry_t); -void vm_init2 (void); int vm_map_stack (vm_map_t, vm_offset_t, vm_size_t, vm_prot_t, vm_prot_t, int); int vm_map_growstack (struct proc *p, vm_offset_t addr); int vm_map_unwire(vm_map_t map, vm_offset_t start, vm_offset_t end, diff --git a/sys/vm/vm_mmap.c b/sys/vm/vm_mmap.c index 1b0809756c2..0f2d531941b 100644 --- a/sys/vm/vm_mmap.c +++ b/sys/vm/vm_mmap.c @@ -94,10 +94,8 @@ SYSCTL_INT(_vm, OID_AUTO, old_mlock, CTLFLAG_RW | CTLFLAG_TUN, &old_mlock, 0, "Do not apply RLIMIT_MEMLOCK on mlockall"); TUNABLE_INT("vm.old_mlock", &old_mlock); -#ifndef _SYS_SYSPROTO_H_ -struct sbrk_args { - int incr; -}; +#ifdef MAP_32BIT +#define MAP_32BIT_MAX_ADDR ((vm_offset_t)1 << 31) #endif static int vm_mmap_vnode(struct thread *, vm_size_t, vm_prot_t, vm_prot_t *, @@ -107,6 +105,12 @@ static int vm_mmap_cdev(struct thread *, vm_size_t, vm_prot_t, vm_prot_t *, static int vm_mmap_shm(struct thread *, vm_size_t, vm_prot_t, vm_prot_t *, int *, struct shmfd *, vm_ooffset_t, vm_object_t *); +#ifndef _SYS_SYSPROTO_H_ +struct sbrk_args { + int incr; +}; +#endif + /* * MPSAFE */ @@ -201,7 +205,7 @@ sys_mmap(td, uap) vm_prot_t cap_maxprot, prot, maxprot; void *handle; objtype_t handle_type; - int flags, error; + int align, error, flags; off_t pos; struct vmspace *vms = td->td_proc->p_vmspace; cap_rights_t rights; @@ -251,6 +255,13 @@ sys_mmap(td, uap) size += pageoff; /* low end... */ size = (vm_size_t) round_page(size); /* hi end */ + /* Ensure alignment is at least a page and fits in a pointer. */ + align = flags & MAP_ALIGNMENT_MASK; + if (align != 0 && align != MAP_ALIGNED_SUPER && + (align >> MAP_ALIGNMENT_SHIFT >= sizeof(void *) * NBBY || + align >> MAP_ALIGNMENT_SHIFT < PAGE_SHIFT)) + return (EINVAL); + /* * Check for illegal addresses. Watch out for address wrap... Note * that VM_*_ADDRESS are not constants due to casts (argh). @@ -271,6 +282,18 @@ sys_mmap(td, uap) return (EINVAL); if (addr + size < addr) return (EINVAL); +#ifdef MAP_32BIT + if (flags & MAP_32BIT && addr + size > MAP_32BIT_MAX_ADDR) + return (EINVAL); + } else if (flags & MAP_32BIT) { + /* + * For MAP_32BIT, override the hint if it is too high and + * do not bother moving the mapping past the heap (since + * the heap is usually above 2GB). + */ + if (addr + size > MAP_32BIT_MAX_ADDR) + addr = 0; +#endif } else { /* * XXX for non-fixed mappings where no hint is provided or @@ -304,17 +327,17 @@ sys_mmap(td, uap) * rights, but also return the maximum rights to be combined * with maxprot later. */ - rights = CAP_MMAP; + cap_rights_init(&rights, CAP_MMAP); if (prot & PROT_READ) - rights |= CAP_MMAP_R; + cap_rights_set(&rights, CAP_MMAP_R); if ((flags & MAP_SHARED) != 0) { if (prot & PROT_WRITE) - rights |= CAP_MMAP_W; + cap_rights_set(&rights, CAP_MMAP_W); } if (prot & PROT_EXEC) - rights |= CAP_MMAP_X; - if ((error = fget_mmap(td, uap->fd, rights, &cap_maxprot, - &fp)) != 0) + cap_rights_set(&rights, CAP_MMAP_X); + error = fget_mmap(td, uap->fd, &rights, &cap_maxprot, &fp); + if (error != 0) goto done; if (fp->f_type == DTYPE_SHM) { handle = fp->f_data; @@ -961,12 +984,12 @@ RestartScan: * the byte vector is zeroed for those skipped entries. */ while ((lastvecindex + 1) < vecindex) { + ++lastvecindex; error = subyte(vec + lastvecindex, 0); if (error) { error = EFAULT; goto done2; } - ++lastvecindex; } /* @@ -1002,12 +1025,12 @@ RestartScan: */ vecindex = OFF_TO_IDX(end - first_addr); while ((lastvecindex + 1) < vecindex) { + ++lastvecindex; error = subyte(vec + lastvecindex, 0); if (error) { error = EFAULT; goto done2; } - ++lastvecindex; } /* @@ -1490,7 +1513,7 @@ vm_mmap(vm_map_t map, vm_offset_t *addr, vm_size_t size, vm_prot_t prot, boolean_t fitit; vm_object_t object = NULL; struct thread *td = curthread; - int docow, error, rv; + int docow, error, findspace, rv; boolean_t writecounted; if (size == 0) @@ -1605,12 +1628,20 @@ vm_mmap(vm_map_t map, vm_offset_t *addr, vm_size_t size, vm_prot_t prot, if (flags & MAP_STACK) rv = vm_map_stack(map, *addr, size, prot, maxprot, docow | MAP_STACK_GROWS_DOWN); - else if (fitit) + else if (fitit) { + if ((flags & MAP_ALIGNMENT_MASK) == MAP_ALIGNED_SUPER) + findspace = VMFS_SUPER_SPACE; + else if ((flags & MAP_ALIGNMENT_MASK) != 0) + findspace = VMFS_ALIGNED_SPACE(flags >> + MAP_ALIGNMENT_SHIFT); + else + findspace = VMFS_OPTIMAL_SPACE; rv = vm_map_find(map, object, foff, addr, size, - object != NULL && object->type == OBJT_DEVICE ? - VMFS_ALIGNED_SPACE : VMFS_OPTIMAL_SPACE, prot, maxprot, - docow); - else +#ifdef MAP_32BIT + flags & MAP_32BIT ? MAP_32BIT_MAX_ADDR : +#endif + 0, findspace, prot, maxprot, docow); + } else rv = vm_map_fixed(map, object, foff, *addr, size, prot, maxprot, docow); diff --git a/sys/vm/vm_object.c b/sys/vm/vm_object.c index 1d128bfba34..9dea3a1cbd9 100644 --- a/sys/vm/vm_object.c +++ b/sys/vm/vm_object.c @@ -201,10 +201,12 @@ vm_object_zinit(void *mem, int size, int flags) /* These are true for any object that has been freed */ object->rtree.rt_root = 0; + object->rtree.rt_flags = 0; object->paging_in_progress = 0; object->resident_page_count = 0; object->shadow_count = 0; object->cache.rt_root = 0; + object->cache.rt_flags = 0; return (0); } @@ -295,7 +297,7 @@ vm_object_init(void) #else NULL, #endif - vm_object_zinit, NULL, UMA_ALIGN_PTR, UMA_ZONE_VM|UMA_ZONE_NOFREE); + vm_object_zinit, NULL, UMA_ALIGN_PTR, UMA_ZONE_NOFREE); vm_radix_init(); } @@ -744,8 +746,7 @@ vm_object_terminate(vm_object_t object) * the object, the page and object are reset to any empty state. */ TAILQ_FOREACH_SAFE(p, &object->memq, listq, p_next) { - KASSERT(!p->busy && (p->oflags & VPO_BUSY) == 0, - ("vm_object_terminate: freeing busy page %p", p)); + vm_page_assert_unbusied(p); vm_page_lock(p); /* * Optimize the page's removal from the object by resetting @@ -871,7 +872,7 @@ rescan: np = TAILQ_NEXT(p, listq); if (p->valid == 0) continue; - if (vm_page_sleep_if_busy(p, TRUE, "vpcwai")) { + if (vm_page_sleep_if_busy(p, "vpcwai")) { if (object->generation != curgeneration) { if ((flags & OBJPC_SYNC) != 0) goto rescan; @@ -939,7 +940,7 @@ vm_object_page_collect_flush(vm_object_t object, vm_page_t p, int pagerflags, for (tp = p; count < vm_pageout_page_count; count++) { tp = vm_page_next(tp); - if (tp == NULL || tp->busy != 0 || (tp->oflags & VPO_BUSY) != 0) + if (tp == NULL || vm_page_busied(tp)) break; if (!vm_object_page_remove_write(tp, flags, clearobjflags)) break; @@ -947,7 +948,7 @@ vm_object_page_collect_flush(vm_object_t object, vm_page_t p, int pagerflags, for (p_first = p; count < vm_pageout_page_count; count++) { tp = vm_page_prev(p_first); - if (tp == NULL || tp->busy != 0 || (tp->oflags & VPO_BUSY) != 0) + if (tp == NULL || vm_page_busied(tp)) break; if (!vm_object_page_remove_write(tp, flags, clearobjflags)) break; @@ -1156,7 +1157,7 @@ shadowlookup: ("vm_object_madvise: page %p is fictitious", m)); KASSERT((m->oflags & VPO_UNMANAGED) == 0, ("vm_object_madvise: page %p is not managed", m)); - if ((m->oflags & VPO_BUSY) || m->busy) { + if (vm_page_busied(m)) { if (advise == MADV_WILLNEED) { /* * Reference the page before unlocking and @@ -1165,11 +1166,10 @@ shadowlookup: */ vm_page_aflag_set(m, PGA_REFERENCED); } - vm_page_unlock(m); if (object != tobject) VM_OBJECT_WUNLOCK(object); - m->oflags |= VPO_WANTED; - VM_OBJECT_SLEEP(tobject, m, PDROP | PVM, "madvpo", 0); + VM_OBJECT_WUNLOCK(tobject); + vm_page_busy_sleep(m, "madvpo"); VM_OBJECT_WLOCK(object); goto relookup; } @@ -1344,10 +1344,22 @@ retry: * We do not have to VM_PROT_NONE the page as mappings should * not be changed by this operation. */ - if ((m->oflags & VPO_BUSY) || m->busy) { + if (vm_page_busied(m)) { VM_OBJECT_WUNLOCK(new_object); - m->oflags |= VPO_WANTED; - VM_OBJECT_SLEEP(orig_object, m, PVM, "spltwt", 0); + vm_page_lock(m); + VM_OBJECT_WUNLOCK(orig_object); + vm_page_busy_sleep(m, "spltwt"); + VM_OBJECT_WLOCK(orig_object); + VM_OBJECT_WLOCK(new_object); + goto retry; + } + + /* vm_page_rename() will handle dirty and cache. */ + if (vm_page_rename(m, new_object, idx)) { + VM_OBJECT_WUNLOCK(new_object); + VM_OBJECT_WUNLOCK(orig_object); + VM_WAIT; + VM_OBJECT_WLOCK(orig_object); VM_OBJECT_WLOCK(new_object); goto retry; } @@ -1366,12 +1378,8 @@ retry: */ vm_reserv_rename(m, new_object, orig_object, offidxstart); #endif - vm_page_lock(m); - vm_page_rename(m, new_object, idx); - vm_page_unlock(m); - /* page automatically made dirty by rename and cache handled */ if (orig_object->type == OBJT_SWAP) - vm_page_busy(m); + vm_page_xbusy(m); } if (orig_object->type == OBJT_SWAP) { /* @@ -1380,7 +1388,7 @@ retry: */ swap_pager_copy(orig_object, new_object, offidxstart, 0); TAILQ_FOREACH(m, &new_object->memq, listq) - vm_page_wakeup(m); + vm_page_xunbusy(m); /* * Transfer any cached pages from orig_object to new_object. @@ -1496,18 +1504,16 @@ vm_object_backing_scan(vm_object_t object, int op) vm_page_t pp; if (op & OBSC_COLLAPSE_NOWAIT) { - if ((p->oflags & VPO_BUSY) || - !p->valid || - p->busy) { + if (!p->valid || vm_page_busied(p)) { p = next; continue; } } else if (op & OBSC_COLLAPSE_WAIT) { - if ((p->oflags & VPO_BUSY) || p->busy) { + if (vm_page_busied(p)) { VM_OBJECT_WUNLOCK(object); - p->oflags |= VPO_WANTED; - VM_OBJECT_SLEEP(backing_object, p, - PDROP | PVM, "vmocol", 0); + vm_page_lock(p); + VM_OBJECT_WUNLOCK(backing_object); + vm_page_busy_sleep(p, "vmocol"); VM_OBJECT_WLOCK(object); VM_OBJECT_WLOCK(backing_object); /* @@ -1527,21 +1533,14 @@ vm_object_backing_scan(vm_object_t object, int op) ("vm_object_backing_scan: object mismatch") ); - /* - * Destroy any associated swap - */ - if (backing_object->type == OBJT_SWAP) { - swap_pager_freespace( - backing_object, - p->pindex, - 1 - ); - } - if ( p->pindex < backing_offset_index || new_pindex >= object->size ) { + if (backing_object->type == OBJT_SWAP) + swap_pager_freespace(backing_object, + p->pindex, 1); + /* * Page is out of the parent object's range, we * can simply destroy it. @@ -1563,6 +1562,10 @@ vm_object_backing_scan(vm_object_t object, int op) (op & OBSC_COLLAPSE_NOWAIT) != 0 && (pp != NULL && pp->valid == 0) ) { + if (backing_object->type == OBJT_SWAP) + swap_pager_freespace(backing_object, + p->pindex, 1); + /* * The page in the parent is not (yet) valid. * We don't know anything about the state of @@ -1581,6 +1584,10 @@ vm_object_backing_scan(vm_object_t object, int op) pp != NULL || vm_pager_has_page(object, new_pindex, NULL, NULL) ) { + if (backing_object->type == OBJT_SWAP) + swap_pager_freespace(backing_object, + p->pindex, 1); + /* * page already exists in parent OR swap exists * for this location in the parent. Destroy @@ -1600,6 +1607,31 @@ vm_object_backing_scan(vm_object_t object, int op) continue; } + /* + * Page does not exist in parent, rename the + * page from the backing object to the main object. + * + * If the page was mapped to a process, it can remain + * mapped through the rename. + * vm_page_rename() will handle dirty and cache. + */ + if (vm_page_rename(p, object, new_pindex)) { + if (op & OBSC_COLLAPSE_NOWAIT) { + p = next; + continue; + } + VM_OBJECT_WLOCK(backing_object); + VM_OBJECT_WUNLOCK(object); + VM_WAIT; + VM_OBJECT_WLOCK(object); + VM_OBJECT_WLOCK(backing_object); + p = TAILQ_FIRST(&backing_object->memq); + continue; + } + if (backing_object->type == OBJT_SWAP) + swap_pager_freespace(backing_object, p->pindex, + 1); + #if VM_NRESERVLEVEL > 0 /* * Rename the reservation. @@ -1607,18 +1639,6 @@ vm_object_backing_scan(vm_object_t object, int op) vm_reserv_rename(p, object, backing_object, backing_offset_index); #endif - - /* - * Page does not exist in parent, rename the - * page from the backing object to the main object. - * - * If the page was mapped to a process, it can remain - * mapped through the rename. - */ - vm_page_lock(p); - vm_page_rename(p, object, new_pindex); - vm_page_unlock(p); - /* page automatically made dirty by rename */ } p = next; } @@ -1890,6 +1910,12 @@ again: * not specified. */ vm_page_lock(p); + if (vm_page_xbusied(p)) { + VM_OBJECT_WUNLOCK(object); + vm_page_busy_sleep(p, "vmopax"); + VM_OBJECT_WLOCK(object); + goto again; + } if ((wirings = p->wire_count) != 0 && (wirings = pmap_page_wired_mappings(p)) != p->wire_count) { if ((options & (OBJPR_NOTWIRED | OBJPR_NOTMAPPED)) == @@ -1905,8 +1931,12 @@ again: } goto next; } - if (vm_page_sleep_if_busy(p, TRUE, "vmopar")) + if (vm_page_busied(p)) { + VM_OBJECT_WUNLOCK(object); + vm_page_busy_sleep(p, "vmopar"); + VM_OBJECT_WLOCK(object); goto again; + } KASSERT((p->flags & PG_FICTITIOUS) == 0, ("vm_object_page_remove: page %p is fictitious", p)); if ((options & OBJPR_CLEANONLY) != 0 && p->valid != 0) { @@ -2010,8 +2040,7 @@ vm_object_populate(vm_object_t object, vm_pindex_t start, vm_pindex_t end) VM_OBJECT_ASSERT_WLOCKED(object); for (pindex = start; pindex < end; pindex++) { - m = vm_page_grab(object, pindex, VM_ALLOC_NORMAL | - VM_ALLOC_RETRY); + m = vm_page_grab(object, pindex, VM_ALLOC_NORMAL); if (m->valid != VM_PAGE_BITS_ALL) { ma[0] = m; rv = vm_pager_get_pages(object, ma, 1, 0); @@ -2033,7 +2062,7 @@ vm_object_populate(vm_object_t object, vm_pindex_t start, vm_pindex_t end) if (pindex > start) { m = vm_page_lookup(object, start); while (m != NULL && m->pindex < pindex) { - vm_page_wakeup(m); + vm_page_xunbusy(m); m = TAILQ_NEXT(m, listq); } } diff --git a/sys/vm/vm_object.h b/sys/vm/vm_object.h index e083b720d4c..d59a9e61fdf 100644 --- a/sys/vm/vm_object.h +++ b/sys/vm/vm_object.h @@ -102,7 +102,7 @@ struct vm_object { TAILQ_ENTRY(vm_object) object_list; /* list of all objects */ LIST_HEAD(, vm_object) shadow_head; /* objects that this is a shadow for */ LIST_ENTRY(vm_object) shadow_list; /* chain of shadow objects */ - TAILQ_HEAD(, vm_page) memq; /* list of resident pages */ + TAILQ_HEAD(respgs, vm_page) memq; /* list of resident pages */ struct vm_radix rtree; /* root of the resident page radix trie*/ vm_pindex_t size; /* Object size */ int generation; /* generation ID */ diff --git a/sys/vm/vm_page.c b/sys/vm/vm_page.c index 4a167c17af1..696f4eebfef 100644 --- a/sys/vm/vm_page.c +++ b/sys/vm/vm_page.c @@ -64,8 +64,7 @@ * GENERAL RULES ON VM_PAGE MANIPULATION * * - A page queue lock is required when adding or removing a page from a - * page queue (vm_pagequeues[]), regardless of other locks or the - * busy state of a page. + * page queue regardless of other locks or the busy state of a page. * * * In general, no thread besides the page daemon can acquire or * hold more than one page queue lock at a time. @@ -124,20 +123,7 @@ __FBSDID("$FreeBSD$"); * page structure. */ -struct vm_pagequeue vm_pagequeues[PQ_COUNT] = { - [PQ_INACTIVE] = { - .pq_pl = TAILQ_HEAD_INITIALIZER( - vm_pagequeues[PQ_INACTIVE].pq_pl), - .pq_cnt = &cnt.v_inactive_count, - .pq_name = "vm inactive pagequeue" - }, - [PQ_ACTIVE] = { - .pq_pl = TAILQ_HEAD_INITIALIZER( - vm_pagequeues[PQ_ACTIVE].pq_pl), - .pq_cnt = &cnt.v_active_count, - .pq_name = "vm active pagequeue" - } -}; +struct vm_domain vm_dom[MAXMEMDOM]; struct mtx_padalign vm_page_queue_free_mtx; struct mtx_padalign pa_lock[PA_LOCK_COUNT]; @@ -159,11 +145,14 @@ SYSCTL_INT(_vm, OID_AUTO, tryrelock_restart, CTLFLAG_RD, static uma_zone_t fakepg_zone; static struct vnode *vm_page_alloc_init(vm_page_t m); +static void vm_page_cache_turn_free(vm_page_t m); static void vm_page_clear_dirty_mask(vm_page_t m, vm_page_bits_t pagebits); static void vm_page_enqueue(int queue, vm_page_t m); static void vm_page_init_fakepg(void *dummy); -static void vm_page_insert_after(vm_page_t m, vm_object_t object, +static int vm_page_insert_after(vm_page_t m, vm_object_t object, vm_pindex_t pindex, vm_page_t mpred); +static void vm_page_insert_radixdone(vm_page_t m, vm_object_t object, + vm_page_t mpred); SYSINIT(vm_page, SI_SUB_VM, SI_ORDER_SECOND, vm_page_init_fakepg, NULL); @@ -256,6 +245,33 @@ vm_page_blacklist_lookup(char *list, vm_paddr_t pa) return (0); } +static void +vm_page_domain_init(struct vm_domain *vmd) +{ + struct vm_pagequeue *pq; + int i; + + *__DECONST(char **, &vmd->vmd_pagequeues[PQ_INACTIVE].pq_name) = + "vm inactive pagequeue"; + *__DECONST(int **, &vmd->vmd_pagequeues[PQ_INACTIVE].pq_vcnt) = + &cnt.v_inactive_count; + *__DECONST(char **, &vmd->vmd_pagequeues[PQ_ACTIVE].pq_name) = + "vm active pagequeue"; + *__DECONST(int **, &vmd->vmd_pagequeues[PQ_ACTIVE].pq_vcnt) = + &cnt.v_active_count; + vmd->vmd_page_count = 0; + vmd->vmd_free_count = 0; + vmd->vmd_segs = 0; + vmd->vmd_oom = FALSE; + vmd->vmd_pass = 0; + for (i = 0; i < PQ_COUNT; i++) { + pq = &vmd->vmd_pagequeues[i]; + TAILQ_INIT(&pq->pq_pl); + mtx_init(&pq->pq_mutex, pq->pq_name, "vm pagequeue", + MTX_DEF | MTX_DUPOK); + } +} + /* * vm_page_startup: * @@ -319,8 +335,8 @@ vm_page_startup(vm_offset_t vaddr) mtx_init(&vm_page_queue_free_mtx, "vm page free queue", NULL, MTX_DEF); for (i = 0; i < PA_LOCK_COUNT; i++) mtx_init(&pa_lock[i], "vm page", NULL, MTX_DEF); - for (i = 0; i < PQ_COUNT; i++) - vm_pagequeue_init_lock(&vm_pagequeues[i]); + for (i = 0; i < vm_ndomains; i++) + vm_page_domain_init(&vm_dom[i]); /* * Allocate memory for use when boot strapping the kernel memory @@ -469,66 +485,174 @@ vm_page_reference(vm_page_t m) vm_page_aflag_set(m, PGA_REFERENCED); } -void -vm_page_busy(vm_page_t m) -{ - - VM_OBJECT_ASSERT_WLOCKED(m->object); - KASSERT((m->oflags & VPO_BUSY) == 0, - ("vm_page_busy: page already busy!!!")); - m->oflags |= VPO_BUSY; -} - /* - * vm_page_flash: + * vm_page_busy_downgrade: * - * wakeup anyone waiting for the page. + * Downgrade an exclusive busy page into a single shared busy page. */ void -vm_page_flash(vm_page_t m) +vm_page_busy_downgrade(vm_page_t m) { + u_int x; - VM_OBJECT_ASSERT_WLOCKED(m->object); - if (m->oflags & VPO_WANTED) { - m->oflags &= ~VPO_WANTED; - wakeup(m); + vm_page_assert_xbusied(m); + + for (;;) { + x = m->busy_lock; + x &= VPB_BIT_WAITERS; + if (atomic_cmpset_rel_int(&m->busy_lock, + VPB_SINGLE_EXCLUSIVER | x, VPB_SHARERS_WORD(1) | x)) + break; } } /* - * vm_page_wakeup: + * vm_page_sbusied: * - * clear the VPO_BUSY flag and wakeup anyone waiting for the - * page. + * Return a positive value if the page is shared busied, 0 otherwise. + */ +int +vm_page_sbusied(vm_page_t m) +{ + u_int x; + + x = m->busy_lock; + return ((x & VPB_BIT_SHARED) != 0 && x != VPB_UNBUSIED); +} + +/* + * vm_page_sunbusy: * + * Shared unbusy a page. */ void -vm_page_wakeup(vm_page_t m) +vm_page_sunbusy(vm_page_t m) { + u_int x; - VM_OBJECT_ASSERT_WLOCKED(m->object); - KASSERT(m->oflags & VPO_BUSY, ("vm_page_wakeup: page not busy!!!")); - m->oflags &= ~VPO_BUSY; - vm_page_flash(m); + vm_page_assert_sbusied(m); + + for (;;) { + x = m->busy_lock; + if (VPB_SHARERS(x) > 1) { + if (atomic_cmpset_int(&m->busy_lock, x, + x - VPB_ONE_SHARER)) + break; + continue; + } + if ((x & VPB_BIT_WAITERS) == 0) { + KASSERT(x == VPB_SHARERS_WORD(1), + ("vm_page_sunbusy: invalid lock state")); + if (atomic_cmpset_int(&m->busy_lock, + VPB_SHARERS_WORD(1), VPB_UNBUSIED)) + break; + continue; + } + KASSERT(x == (VPB_SHARERS_WORD(1) | VPB_BIT_WAITERS), + ("vm_page_sunbusy: invalid lock state for waiters")); + + vm_page_lock(m); + if (!atomic_cmpset_int(&m->busy_lock, x, VPB_UNBUSIED)) { + vm_page_unlock(m); + continue; + } + wakeup(m); + vm_page_unlock(m); + break; + } } +/* + * vm_page_busy_sleep: + * + * Sleep and release the page lock, using the page pointer as wchan. + * This is used to implement the hard-path of busying mechanism. + * + * The given page must be locked. + */ void -vm_page_io_start(vm_page_t m) +vm_page_busy_sleep(vm_page_t m, const char *wmesg) { + u_int x; - VM_OBJECT_ASSERT_WLOCKED(m->object); - m->busy++; + vm_page_lock_assert(m, MA_OWNED); + + x = m->busy_lock; + if (x == VPB_UNBUSIED) { + vm_page_unlock(m); + return; + } + if ((x & VPB_BIT_WAITERS) == 0 && + !atomic_cmpset_int(&m->busy_lock, x, x | VPB_BIT_WAITERS)) { + vm_page_unlock(m); + return; + } + msleep(m, vm_page_lockptr(m), PVM | PDROP, wmesg, 0); } +/* + * vm_page_trysbusy: + * + * Try to shared busy a page. + * If the operation succeeds 1 is returned otherwise 0. + * The operation never sleeps. + */ +int +vm_page_trysbusy(vm_page_t m) +{ + u_int x; + + for (;;) { + x = m->busy_lock; + if ((x & VPB_BIT_SHARED) == 0) + return (0); + if (atomic_cmpset_acq_int(&m->busy_lock, x, x + VPB_ONE_SHARER)) + return (1); + } +} + +/* + * vm_page_xunbusy_hard: + * + * Called after the first try the exclusive unbusy of a page failed. + * It is assumed that the waiters bit is on. + */ void -vm_page_io_finish(vm_page_t m) +vm_page_xunbusy_hard(vm_page_t m) { - VM_OBJECT_ASSERT_WLOCKED(m->object); - KASSERT(m->busy > 0, ("vm_page_io_finish: page %p is not busy", m)); - m->busy--; - if (m->busy == 0) - vm_page_flash(m); + vm_page_assert_xbusied(m); + + vm_page_lock(m); + atomic_store_rel_int(&m->busy_lock, VPB_UNBUSIED); + wakeup(m); + vm_page_unlock(m); +} + +/* + * vm_page_flash: + * + * Wakeup anyone waiting for the page. + * The ownership bits do not change. + * + * The given page must be locked. + */ +void +vm_page_flash(vm_page_t m) +{ + u_int x; + + vm_page_lock_assert(m, MA_OWNED); + + for (;;) { + x = m->busy_lock; + if ((x & VPB_BIT_WAITERS) == 0) + return; + if (atomic_cmpset_int(&m->busy_lock, x, + x & (~VPB_BIT_WAITERS))) + break; + } + wakeup(m); } /* @@ -550,8 +674,8 @@ vm_page_unhold(vm_page_t mem) { vm_page_lock_assert(mem, MA_OWNED); + KASSERT(mem->hold_count >= 1, ("vm_page_unhold: hold count < 0!!!")); --mem->hold_count; - KASSERT(mem->hold_count >= 0, ("vm_page_unhold: hold count < 0!!!")); if (mem->hold_count == 0 && (mem->flags & PG_UNHOLDFREE) != 0) vm_page_free_toq(mem); } @@ -643,7 +767,8 @@ vm_page_initfake(vm_page_t m, vm_paddr_t paddr, vm_memattr_t memattr) /* Fictitious pages don't use "segind". */ m->flags = PG_FICTITIOUS; /* Fictitious pages don't use "order" or "pool". */ - m->oflags = VPO_BUSY | VPO_UNMANAGED; + m->oflags = VPO_UNMANAGED; + m->busy_lock = VPB_SINGLE_EXCLUSIVER; m->wire_count = 1; pmap_page_init(m); memattr: @@ -723,16 +848,13 @@ vm_page_readahead_finish(vm_page_t m) * deactivating the page is usually the best choice, * unless the page is wanted by another thread. */ - if (m->oflags & VPO_WANTED) { - vm_page_lock(m); + vm_page_lock(m); + if ((m->busy_lock & VPB_BIT_WAITERS) != 0) vm_page_activate(m); - vm_page_unlock(m); - } else { - vm_page_lock(m); + else vm_page_deactivate(m); - vm_page_unlock(m); - } - vm_page_wakeup(m); + vm_page_unlock(m); + vm_page_xunbusy(m); } else { /* * Free the completely invalid page. Such page state @@ -747,29 +869,38 @@ vm_page_readahead_finish(vm_page_t m) } /* - * vm_page_sleep: + * vm_page_sleep_if_busy: * - * Sleep and release the page lock. + * Sleep and release the page queues lock if the page is busied. + * Returns TRUE if the thread slept. * - * The object containing the given page must be locked. + * The given page must be unlocked and object containing it must + * be locked. */ -void -vm_page_sleep(vm_page_t m, const char *msg) +int +vm_page_sleep_if_busy(vm_page_t m, const char *msg) { + vm_object_t obj; + vm_page_lock_assert(m, MA_NOTOWNED); VM_OBJECT_ASSERT_WLOCKED(m->object); - if (mtx_owned(vm_page_lockptr(m))) - vm_page_unlock(m); - /* - * It's possible that while we sleep, the page will get - * unbusied and freed. If we are holding the object - * lock, we will assume we hold a reference to the object - * such that even if m->object changes, we can re-lock - * it. - */ - m->oflags |= VPO_WANTED; - VM_OBJECT_SLEEP(m->object, m, PVM, msg, 0); + if (vm_page_busied(m)) { + /* + * The page-specific object must be cached because page + * identity can change during the sleep, causing the + * re-lock of a different object. + * It is assumed that a reference to the object is already + * held by the callers. + */ + obj = m->object; + vm_page_lock(m); + VM_OBJECT_WUNLOCK(obj); + vm_page_busy_sleep(m, msg); + VM_OBJECT_WLOCK(obj); + return (TRUE); + } + return (FALSE); } /* @@ -805,14 +936,14 @@ vm_page_dirty_KBI(vm_page_t m) * * The object must be locked. */ -void +int vm_page_insert(vm_page_t m, vm_object_t object, vm_pindex_t pindex) { vm_page_t mpred; VM_OBJECT_ASSERT_WLOCKED(object); mpred = vm_radix_lookup_le(&object->rtree, pindex); - vm_page_insert_after(m, object, pindex, mpred); + return (vm_page_insert_after(m, object, pindex, mpred)); } /* @@ -825,18 +956,19 @@ vm_page_insert(vm_page_t m, vm_object_t object, vm_pindex_t pindex) * * The object must be locked. */ -static void +static int vm_page_insert_after(vm_page_t m, vm_object_t object, vm_pindex_t pindex, vm_page_t mpred) { + vm_pindex_t sidx; + vm_object_t sobj; vm_page_t msucc; VM_OBJECT_ASSERT_WLOCKED(object); KASSERT(m->object == NULL, ("vm_page_insert_after: page already inserted")); if (mpred != NULL) { - KASSERT(mpred->object == object || - (mpred->flags & PG_SLAB) != 0, + KASSERT(mpred->object == object, ("vm_page_insert_after: object doesn't contain mpred")); KASSERT(mpred->pindex < pindex, ("vm_page_insert_after: mpred doesn't precede pindex")); @@ -850,17 +982,52 @@ vm_page_insert_after(vm_page_t m, vm_object_t object, vm_pindex_t pindex, /* * Record the object/offset pair in this page */ + sobj = m->object; + sidx = m->pindex; m->object = object; m->pindex = pindex; /* * Now link into the object's ordered list of backed pages. */ + if (vm_radix_insert(&object->rtree, m)) { + m->object = sobj; + m->pindex = sidx; + return (1); + } + vm_page_insert_radixdone(m, object, mpred); + return (0); +} + +/* + * vm_page_insert_radixdone: + * + * Complete page "m" insertion into the specified object after the + * radix trie hooking. + * + * The page "mpred" must precede the offset "m->pindex" within the + * specified object. + * + * The object must be locked. + */ +static void +vm_page_insert_radixdone(vm_page_t m, vm_object_t object, vm_page_t mpred) +{ + + VM_OBJECT_ASSERT_WLOCKED(object); + KASSERT(object != NULL && m->object == object, + ("vm_page_insert_radixdone: page %p has inconsistent object", m)); + if (mpred != NULL) { + KASSERT(mpred->object == object, + ("vm_page_insert_after: object doesn't contain mpred")); + KASSERT(mpred->pindex < m->pindex, + ("vm_page_insert_after: mpred doesn't precede pindex")); + } + if (mpred != NULL) TAILQ_INSERT_AFTER(&object->memq, mpred, m, listq); else TAILQ_INSERT_HEAD(&object->memq, m, listq); - vm_radix_insert(&object->rtree, m); /* * Show that the object has one more resident page. @@ -894,15 +1061,24 @@ void vm_page_remove(vm_page_t m) { vm_object_t object; + boolean_t lockacq; if ((m->oflags & VPO_UNMANAGED) == 0) vm_page_lock_assert(m, MA_OWNED); if ((object = m->object) == NULL) return; VM_OBJECT_ASSERT_WLOCKED(object); - if (m->oflags & VPO_BUSY) { - m->oflags &= ~VPO_BUSY; + if (vm_page_xbusied(m)) { + lockacq = FALSE; + if ((m->oflags & VPO_UNMANAGED) != 0 && + !mtx_owned(vm_page_lockptr(m))) { + lockacq = TRUE; + vm_page_lock(m); + } vm_page_flash(m); + atomic_store_rel_int(&m->busy_lock, VPB_UNBUSIED); + if (lockacq) + vm_page_unlock(m); } /* @@ -996,6 +1172,54 @@ vm_page_prev(vm_page_t m) return (prev); } +/* + * Uses the page mnew as a replacement for an existing page at index + * pindex which must be already present in the object. + * + * The existing page must not be on a paging queue. + */ +vm_page_t +vm_page_replace(vm_page_t mnew, vm_object_t object, vm_pindex_t pindex) +{ + vm_page_t mold, mpred; + + VM_OBJECT_ASSERT_WLOCKED(object); + + /* + * This function mostly follows vm_page_insert() and + * vm_page_remove() without the radix, object count and vnode + * dance. Double check such functions for more comments. + */ + mpred = vm_radix_lookup(&object->rtree, pindex); + KASSERT(mpred != NULL, + ("vm_page_replace: replacing page not present with pindex")); + mpred = TAILQ_PREV(mpred, respgs, listq); + if (mpred != NULL) + KASSERT(mpred->pindex < pindex, + ("vm_page_insert_after: mpred doesn't precede pindex")); + + mnew->object = object; + mnew->pindex = pindex; + mold = vm_radix_replace(&object->rtree, mnew, pindex); + KASSERT(mold->queue == PQ_NONE, + ("vm_page_replace: mold is on a paging queue")); + + /* Detach the old page from the resident tailq. */ + TAILQ_REMOVE(&object->memq, mold, listq); + + mold->object = NULL; + vm_page_xunbusy(mold); + + /* Insert the new page in the resident tailq. */ + if (mpred != NULL) + TAILQ_INSERT_AFTER(&object->memq, mpred, mnew, listq); + else + TAILQ_INSERT_HEAD(&object->memq, mnew, listq); + if (pmap_page_is_write_mapped(mnew)) + vm_object_set_writeable_dirty(object); + return (mold); +} + /* * vm_page_rename: * @@ -1014,15 +1238,47 @@ vm_page_prev(vm_page_t m) * or vm_page_dirty() will panic. Dirty pages are not allowed * on the cache. * - * The objects must be locked. The page must be locked if it is managed. + * The objects must be locked. */ -void +int vm_page_rename(vm_page_t m, vm_object_t new_object, vm_pindex_t new_pindex) { + vm_page_t mpred; + vm_pindex_t opidx; + VM_OBJECT_ASSERT_WLOCKED(new_object); + + mpred = vm_radix_lookup_le(&new_object->rtree, new_pindex); + KASSERT(mpred == NULL || mpred->pindex != new_pindex, + ("vm_page_rename: pindex already renamed")); + + /* + * Create a custom version of vm_page_insert() which does not depend + * by m_prev and can cheat on the implementation aspects of the + * function. + */ + opidx = m->pindex; + m->pindex = new_pindex; + if (vm_radix_insert(&new_object->rtree, m)) { + m->pindex = opidx; + return (1); + } + + /* + * The operation cannot fail anymore. The removal must happen before + * the listq iterator is tainted. + */ + m->pindex = opidx; + vm_page_lock(m); vm_page_remove(m); - vm_page_insert(m, new_object, new_pindex); + + /* Return back to the new pindex to complete vm_page_insert(). */ + m->pindex = new_pindex; + m->object = new_object; + vm_page_unlock(m); + vm_page_insert_radixdone(m, new_object, mpred); vm_page_dirty(m); + return (0); } /* @@ -1048,14 +1304,7 @@ vm_page_cache_free(vm_object_t object, vm_pindex_t start, vm_pindex_t end) if (end != 0 && m->pindex >= end) break; vm_radix_remove(&object->cache, m->pindex); - m->object = NULL; - m->valid = 0; - /* Clear PG_CACHED and set PG_FREE. */ - m->flags ^= PG_CACHED | PG_FREE; - KASSERT((m->flags & (PG_CACHED | PG_FREE)) == PG_FREE, - ("vm_page_cache_free: page %p has inconsistent flags", m)); - cnt.v_cache_count--; - cnt.v_free_count++; + vm_page_cache_turn_free(m); } empty = vm_radix_is_empty(&object->cache); mtx_unlock(&vm_page_queue_free_mtx); @@ -1135,7 +1384,8 @@ vm_page_cache_transfer(vm_object_t orig_object, vm_pindex_t offidxstart, /* Update the page's object and offset. */ m->object = new_object; m->pindex -= offidxstart; - vm_radix_insert(&new_object->cache, m); + if (vm_radix_insert(&new_object->cache, m)) + vm_page_cache_turn_free(m); } mtx_unlock(&vm_page_queue_free_mtx); } @@ -1171,8 +1421,7 @@ vm_page_is_cached(vm_object_t object, vm_pindex_t pindex) * vm_page_alloc: * * Allocate and return a page that is associated with the specified - * object and offset pair. By default, this page has the flag VPO_BUSY - * set. + * object and offset pair. By default, this page is exclusive busied. * * The caller must always specify an allocation class. * @@ -1187,10 +1436,11 @@ vm_page_is_cached(vm_object_t object, vm_pindex_t pindex) * VM_ALLOC_IFCACHED return page only if it is cached * VM_ALLOC_IFNOTCACHED return NULL, do not reactivate if the page * is cached - * VM_ALLOC_NOBUSY do not set the flag VPO_BUSY on the page + * VM_ALLOC_NOBUSY do not exclusive busy the page * VM_ALLOC_NODUMP do not include the page in a kernel core dump * VM_ALLOC_NOOBJ page is not associated with an object and - * should not have the flag VPO_BUSY set + * should not be exclusive busy + * VM_ALLOC_SBUSY shared busy the allocated page * VM_ALLOC_WIRED wire the allocated page * VM_ALLOC_ZERO prefer a zeroed page * @@ -1205,8 +1455,12 @@ vm_page_alloc(vm_object_t object, vm_pindex_t pindex, int req) int flags, req_class; mpred = 0; /* XXX: pacify gcc */ - KASSERT((object != NULL) == ((req & VM_ALLOC_NOOBJ) == 0), - ("vm_page_alloc: inconsistent object/req")); + KASSERT((object != NULL) == ((req & VM_ALLOC_NOOBJ) == 0) && + (object != NULL || (req & VM_ALLOC_SBUSY) == 0) && + ((req & (VM_ALLOC_NOBUSY | VM_ALLOC_SBUSY)) != + (VM_ALLOC_NOBUSY | VM_ALLOC_SBUSY)), + ("vm_page_alloc: inconsistent object(%p)/req(%x)", (void *)object, + req)); if (object != NULL) VM_OBJECT_ASSERT_WLOCKED(object); @@ -1223,7 +1477,13 @@ vm_page_alloc(vm_object_t object, vm_pindex_t pindex, int req) KASSERT(mpred == NULL || mpred->pindex != pindex, ("vm_page_alloc: pindex already allocated")); } - mtx_lock(&vm_page_queue_free_mtx); + + /* + * The page allocation request can came from consumers which already + * hold the free page queue mutex, like vm_page_insert() in + * vm_page_cache(). + */ + mtx_lock_flags(&vm_page_queue_free_mtx, MTX_RECURSE); if (cnt.v_free_count + cnt.v_cache_count > cnt.v_free_reserved || (req_class == VM_ALLOC_SYSTEM && cnt.v_free_count + cnt.v_cache_count > cnt.v_interrupt_free_min) || @@ -1287,7 +1547,8 @@ vm_page_alloc(vm_object_t object, vm_pindex_t pindex, int req) ("vm_page_alloc: page %p has unexpected queue %d", m, m->queue)); KASSERT(m->wire_count == 0, ("vm_page_alloc: page %p is wired", m)); KASSERT(m->hold_count == 0, ("vm_page_alloc: page %p is held", m)); - KASSERT(m->busy == 0, ("vm_page_alloc: page %p is busy", m)); + KASSERT(!vm_page_sbusied(m), + ("vm_page_alloc: page %p is busy", m)); KASSERT(m->dirty == 0, ("vm_page_alloc: page %p is dirty", m)); KASSERT(pmap_page_get_memattr(m) == VM_MEMATTR_DEFAULT, ("vm_page_alloc: page %p has unexpected memattr %d", m, @@ -1311,7 +1572,7 @@ vm_page_alloc(vm_object_t object, vm_pindex_t pindex, int req) ("vm_page_alloc: page %p is not free", m)); KASSERT(m->valid == 0, ("vm_page_alloc: free page %p is valid", m)); - cnt.v_free_count--; + vm_phys_freecnt_adj(m, -1); } /* @@ -1331,8 +1592,11 @@ vm_page_alloc(vm_object_t object, vm_pindex_t pindex, int req) m->aflags = 0; m->oflags = object == NULL || (object->flags & OBJ_UNMANAGED) != 0 ? VPO_UNMANAGED : 0; - if ((req & (VM_ALLOC_NOBUSY | VM_ALLOC_NOOBJ)) == 0) - m->oflags |= VPO_BUSY; + m->busy_lock = VPB_UNBUSIED; + if ((req & (VM_ALLOC_NOBUSY | VM_ALLOC_NOOBJ | VM_ALLOC_SBUSY)) == 0) + m->busy_lock = VPB_SINGLE_EXCLUSIVER; + if ((req & VM_ALLOC_SBUSY) != 0) + m->busy_lock = VPB_SHARERS_WORD(1); if (req & VM_ALLOC_WIRED) { /* * The page lock is not required for wiring a page until that @@ -1344,11 +1608,24 @@ vm_page_alloc(vm_object_t object, vm_pindex_t pindex, int req) m->act_count = 0; if (object != NULL) { + if (vm_page_insert_after(m, object, pindex, mpred)) { + /* See the comment below about hold count. */ + if (vp != NULL) + vdrop(vp); + pagedaemon_wakeup(); + if (req & VM_ALLOC_WIRED) { + atomic_subtract_int(&cnt.v_wire_count, 1); + m->wire_count = 0; + } + m->object = NULL; + vm_page_free(m); + return (NULL); + } + /* Ignore device objects; the pager sets "memattr" for them. */ if (object->memattr != VM_MEMATTR_DEFAULT && (object->flags & OBJ_FICTITIOUS) == 0) pmap_page_set_memattr(m, object->memattr); - vm_page_insert_after(m, object, pindex, mpred); } else m->pindex = pindex; @@ -1371,6 +1648,16 @@ vm_page_alloc(vm_object_t object, vm_pindex_t pindex, int req) return (m); } +static void +vm_page_alloc_contig_vdrop(struct spglist *lst) +{ + + while (!SLIST_EMPTY(lst)) { + vdrop((struct vnode *)SLIST_FIRST(lst)-> plinks.s.pv); + SLIST_REMOVE_HEAD(lst, plinks.s.ss); + } +} + /* * vm_page_alloc_contig: * @@ -1400,9 +1687,10 @@ vm_page_alloc(vm_object_t object, vm_pindex_t pindex, int req) * VM_ALLOC_INTERRUPT interrupt time request * * optional allocation flags: - * VM_ALLOC_NOBUSY do not set the flag VPO_BUSY on the page + * VM_ALLOC_NOBUSY do not exclusive busy the page * VM_ALLOC_NOOBJ page is not associated with an object and - * should not have the flag VPO_BUSY set + * should not be exclusive busy + * VM_ALLOC_SBUSY shared busy the allocated page * VM_ALLOC_WIRED wire the allocated page * VM_ALLOC_ZERO prefer a zeroed page * @@ -1414,12 +1702,17 @@ vm_page_alloc_contig(vm_object_t object, vm_pindex_t pindex, int req, vm_paddr_t boundary, vm_memattr_t memattr) { struct vnode *drop; - vm_page_t deferred_vdrop_list, m, m_ret; + struct spglist deferred_vdrop_list; + vm_page_t m, m_tmp, m_ret; u_int flags, oflags; int req_class; - KASSERT((object != NULL) == ((req & VM_ALLOC_NOOBJ) == 0), - ("vm_page_alloc_contig: inconsistent object/req")); + KASSERT((object != NULL) == ((req & VM_ALLOC_NOOBJ) == 0) && + (object != NULL || (req & VM_ALLOC_SBUSY) == 0) && + ((req & (VM_ALLOC_NOBUSY | VM_ALLOC_SBUSY)) != + (VM_ALLOC_NOBUSY | VM_ALLOC_SBUSY)), + ("vm_page_alloc: inconsistent object(%p)/req(%x)", (void *)object, + req)); if (object != NULL) { VM_OBJECT_ASSERT_WLOCKED(object); KASSERT(object->type == OBJT_PHYS, @@ -1435,7 +1728,7 @@ vm_page_alloc_contig(vm_object_t object, vm_pindex_t pindex, int req, if (curproc == pageproc && req_class != VM_ALLOC_INTERRUPT) req_class = VM_ALLOC_SYSTEM; - deferred_vdrop_list = NULL; + SLIST_INIT(&deferred_vdrop_list); mtx_lock(&vm_page_queue_free_mtx); if (cnt.v_free_count + cnt.v_cache_count >= npages + cnt.v_free_reserved || (req_class == VM_ALLOC_SYSTEM && @@ -1462,14 +1755,10 @@ retry: if (drop != NULL) { /* * Enqueue the vnode for deferred vdrop(). - * - * Once the pages are removed from the free - * page list, "pageq" can be safely abused to - * construct a short-lived list of vnodes. */ - m->pageq.tqe_prev = (void *)drop; - m->pageq.tqe_next = deferred_vdrop_list; - deferred_vdrop_list = m; + m->plinks.s.pv = drop; + SLIST_INSERT_HEAD(&deferred_vdrop_list, m, + plinks.s.ss); } } else { @@ -1495,8 +1784,6 @@ retry: atomic_add_int(&cnt.v_wire_count, npages); oflags = VPO_UNMANAGED; if (object != NULL) { - if ((req & VM_ALLOC_NOBUSY) == 0) - oflags |= VPO_BUSY; if (object->memattr != VM_MEMATTR_DEFAULT && memattr == VM_MEMATTR_DEFAULT) memattr = object->memattr; @@ -1504,22 +1791,43 @@ retry: for (m = m_ret; m < &m_ret[npages]; m++) { m->aflags = 0; m->flags = (m->flags | PG_NODUMP) & flags; + m->busy_lock = VPB_UNBUSIED; + if (object != NULL) { + if ((req & (VM_ALLOC_NOBUSY | VM_ALLOC_SBUSY)) == 0) + m->busy_lock = VPB_SINGLE_EXCLUSIVER; + if ((req & VM_ALLOC_SBUSY) != 0) + m->busy_lock = VPB_SHARERS_WORD(1); + } if ((req & VM_ALLOC_WIRED) != 0) m->wire_count = 1; /* Unmanaged pages don't use "act_count". */ m->oflags = oflags; + if (object != NULL) { + if (vm_page_insert(m, object, pindex)) { + vm_page_alloc_contig_vdrop( + &deferred_vdrop_list); + if (vm_paging_needed()) + pagedaemon_wakeup(); + if ((req & VM_ALLOC_WIRED) != 0) + atomic_subtract_int(&cnt.v_wire_count, + npages); + for (m_tmp = m, m = m_ret; + m < &m_ret[npages]; m++) { + if ((req & VM_ALLOC_WIRED) != 0) + m->wire_count = 0; + if (m >= m_tmp) + m->object = NULL; + vm_page_free(m); + } + return (NULL); + } + } else + m->pindex = pindex; if (memattr != VM_MEMATTR_DEFAULT) pmap_page_set_memattr(m, memattr); - if (object != NULL) - vm_page_insert(m, object, pindex); - else - m->pindex = pindex; pindex++; } - while (deferred_vdrop_list != NULL) { - vdrop((struct vnode *)deferred_vdrop_list->pageq.tqe_prev); - deferred_vdrop_list = deferred_vdrop_list->pageq.tqe_next; - } + vm_page_alloc_contig_vdrop(&deferred_vdrop_list); if (vm_paging_needed()) pagedaemon_wakeup(); return (m_ret); @@ -1546,7 +1854,7 @@ vm_page_alloc_init(vm_page_t m) ("vm_page_alloc_init: page %p is wired", m)); KASSERT(m->hold_count == 0, ("vm_page_alloc_init: page %p is held", m)); - KASSERT(m->busy == 0, + KASSERT(!vm_page_sbusied(m), ("vm_page_alloc_init: page %p is busy", m)); KASSERT(m->dirty == 0, ("vm_page_alloc_init: page %p is dirty", m)); @@ -1569,7 +1877,7 @@ vm_page_alloc_init(vm_page_t m) ("vm_page_alloc_init: page %p is not free", m)); KASSERT(m->valid == 0, ("vm_page_alloc_init: free page %p is valid", m)); - cnt.v_free_count--; + vm_phys_freecnt_adj(m, -1); if ((m->flags & PG_ZERO) != 0) vm_page_zero_count--; } @@ -1617,7 +1925,7 @@ vm_page_alloc_freelist(int flind, int req) /* * Do not allocate reserved pages unless the req has asked for it. */ - mtx_lock(&vm_page_queue_free_mtx); + mtx_lock_flags(&vm_page_queue_free_mtx, MTX_RECURSE); if (cnt.v_free_count + cnt.v_cache_count > cnt.v_free_reserved || (req_class == VM_ALLOC_SYSTEM && cnt.v_free_count + cnt.v_cache_count > cnt.v_interrupt_free_min) || @@ -1711,6 +2019,13 @@ vm_waitpfault(void) "pfault", 0); } +struct vm_pagequeue * +vm_page_pagequeue(vm_page_t m) +{ + + return (&vm_phys_domain(m)->vmd_pagequeues[m->queue]); +} + /* * vm_page_dequeue: * @@ -1726,11 +2041,11 @@ vm_page_dequeue(vm_page_t m) vm_page_lock_assert(m, MA_OWNED); KASSERT(m->queue != PQ_NONE, ("vm_page_dequeue: page %p is not queued", m)); - pq = &vm_pagequeues[m->queue]; + pq = vm_page_pagequeue(m); vm_pagequeue_lock(pq); m->queue = PQ_NONE; - TAILQ_REMOVE(&pq->pq_pl, m, pageq); - (*pq->pq_cnt)--; + TAILQ_REMOVE(&pq->pq_pl, m, plinks.q); + vm_pagequeue_cnt_dec(pq); vm_pagequeue_unlock(pq); } @@ -1747,11 +2062,11 @@ vm_page_dequeue_locked(vm_page_t m) struct vm_pagequeue *pq; vm_page_lock_assert(m, MA_OWNED); - pq = &vm_pagequeues[m->queue]; + pq = vm_page_pagequeue(m); vm_pagequeue_assert_locked(pq); m->queue = PQ_NONE; - TAILQ_REMOVE(&pq->pq_pl, m, pageq); - (*pq->pq_cnt)--; + TAILQ_REMOVE(&pq->pq_pl, m, plinks.q); + vm_pagequeue_cnt_dec(pq); } /* @@ -1767,11 +2082,11 @@ vm_page_enqueue(int queue, vm_page_t m) struct vm_pagequeue *pq; vm_page_lock_assert(m, MA_OWNED); - pq = &vm_pagequeues[queue]; + pq = &vm_phys_domain(m)->vmd_pagequeues[queue]; vm_pagequeue_lock(pq); m->queue = queue; - TAILQ_INSERT_TAIL(&pq->pq_pl, m, pageq); - ++*pq->pq_cnt; + TAILQ_INSERT_TAIL(&pq->pq_pl, m, plinks.q); + vm_pagequeue_cnt_inc(pq); vm_pagequeue_unlock(pq); } @@ -1790,10 +2105,10 @@ vm_page_requeue(vm_page_t m) vm_page_lock_assert(m, MA_OWNED); KASSERT(m->queue != PQ_NONE, ("vm_page_requeue: page %p is not queued", m)); - pq = &vm_pagequeues[m->queue]; + pq = vm_page_pagequeue(m); vm_pagequeue_lock(pq); - TAILQ_REMOVE(&pq->pq_pl, m, pageq); - TAILQ_INSERT_TAIL(&pq->pq_pl, m, pageq); + TAILQ_REMOVE(&pq->pq_pl, m, plinks.q); + TAILQ_INSERT_TAIL(&pq->pq_pl, m, plinks.q); vm_pagequeue_unlock(pq); } @@ -1811,10 +2126,10 @@ vm_page_requeue_locked(vm_page_t m) KASSERT(m->queue != PQ_NONE, ("vm_page_requeue_locked: page %p is not queued", m)); - pq = &vm_pagequeues[m->queue]; + pq = vm_page_pagequeue(m); vm_pagequeue_assert_locked(pq); - TAILQ_REMOVE(&pq->pq_pl, m, pageq); - TAILQ_INSERT_TAIL(&pq->pq_pl, m, pageq); + TAILQ_REMOVE(&pq->pq_pl, m, plinks.q); + TAILQ_INSERT_TAIL(&pq->pq_pl, m, plinks.q); } /* @@ -1882,6 +2197,28 @@ vm_page_free_wakeup(void) } } +/* + * Turn a cached page into a free page, by changing its attributes. + * Keep the statistics up-to-date. + * + * The free page queue must be locked. + */ +static void +vm_page_cache_turn_free(vm_page_t m) +{ + + mtx_assert(&vm_page_queue_free_mtx, MA_OWNED); + + m->object = NULL; + m->valid = 0; + /* Clear PG_CACHED and set PG_FREE. */ + m->flags ^= PG_CACHED | PG_FREE; + KASSERT((m->flags & (PG_CACHED | PG_FREE)) == PG_FREE, + ("vm_page_cache_free: page %p has inconsistent flags", m)); + cnt.v_cache_count--; + vm_phys_freecnt_adj(m, 1); +} + /* * vm_page_free_toq: * @@ -1905,7 +2242,7 @@ vm_page_free_toq(vm_page_t m) if (VM_PAGE_IS_FREE(m)) panic("vm_page_free: freeing free page %p", m); - else if (m->busy != 0) + else if (vm_page_sbusied(m)) panic("vm_page_free: freeing busy page %p", m); /* @@ -1948,7 +2285,7 @@ vm_page_free_toq(vm_page_t m) */ mtx_lock(&vm_page_queue_free_mtx); m->flags |= PG_FREE; - cnt.v_free_count++; + vm_phys_freecnt_adj(m, 1); #if VM_NRESERVLEVEL > 0 if (!vm_reserv_free_page(m)) #else @@ -2081,14 +2418,14 @@ _vm_page_deactivate(vm_page_t m, int athead) if (queue != PQ_NONE) vm_page_dequeue(m); m->flags &= ~PG_WINATCFLS; - pq = &vm_pagequeues[PQ_INACTIVE]; + pq = &vm_phys_domain(m)->vmd_pagequeues[PQ_INACTIVE]; vm_pagequeue_lock(pq); m->queue = PQ_INACTIVE; if (athead) - TAILQ_INSERT_HEAD(&pq->pq_pl, m, pageq); + TAILQ_INSERT_HEAD(&pq->pq_pl, m, plinks.q); else - TAILQ_INSERT_TAIL(&pq->pq_pl, m, pageq); - cnt.v_inactive_count++; + TAILQ_INSERT_TAIL(&pq->pq_pl, m, plinks.q); + vm_pagequeue_cnt_inc(pq); vm_pagequeue_unlock(pq); } } @@ -2116,8 +2453,8 @@ vm_page_try_to_cache(vm_page_t m) vm_page_lock_assert(m, MA_OWNED); VM_OBJECT_ASSERT_WLOCKED(m->object); - if (m->dirty || m->hold_count || m->busy || m->wire_count || - (m->oflags & (VPO_BUSY | VPO_UNMANAGED)) != 0) + if (m->dirty || m->hold_count || m->wire_count || + (m->oflags & VPO_UNMANAGED) != 0 || vm_page_busied(m)) return (0); pmap_remove_all(m); if (m->dirty) @@ -2139,8 +2476,8 @@ vm_page_try_to_free(vm_page_t m) vm_page_lock_assert(m, MA_OWNED); if (m->object != NULL) VM_OBJECT_ASSERT_WLOCKED(m->object); - if (m->dirty || m->hold_count || m->busy || m->wire_count || - (m->oflags & (VPO_BUSY | VPO_UNMANAGED)) != 0) + if (m->dirty || m->hold_count || m->wire_count || + (m->oflags & VPO_UNMANAGED) != 0 || vm_page_busied(m)) return (0); pmap_remove_all(m); if (m->dirty) @@ -2165,7 +2502,7 @@ vm_page_cache(vm_page_t m) vm_page_lock_assert(m, MA_OWNED); object = m->object; VM_OBJECT_ASSERT_WLOCKED(object); - if ((m->oflags & (VPO_UNMANAGED | VPO_BUSY)) || m->busy || + if (vm_page_busied(m) || (m->oflags & VPO_UNMANAGED) || m->hold_count || m->wire_count) panic("vm_page_cache: attempting to cache busy page"); KASSERT(!pmap_page_is_mapped(m), @@ -2184,7 +2521,6 @@ vm_page_cache(vm_page_t m) } KASSERT((m->flags & PG_CACHED) == 0, ("vm_page_cache: page %p is already cached", m)); - PCPU_INC(cnt.v_tcached); /* * Remove the page from the paging queues. @@ -2211,10 +2547,27 @@ vm_page_cache(vm_page_t m) */ m->flags &= ~PG_ZERO; mtx_lock(&vm_page_queue_free_mtx); + cache_was_empty = vm_radix_is_empty(&object->cache); + if (vm_radix_insert(&object->cache, m)) { + mtx_unlock(&vm_page_queue_free_mtx); + if (object->resident_page_count == 0) + vdrop(object->handle); + m->object = NULL; + vm_page_free(m); + return; + } + + /* + * The above call to vm_radix_insert() could reclaim the one pre- + * existing cached page from this object, resulting in a call to + * vdrop(). + */ + if (!cache_was_empty) + cache_was_empty = vm_radix_is_singleton(&object->cache); + m->flags |= PG_CACHED; cnt.v_cache_count++; - cache_was_empty = vm_radix_is_empty(&object->cache); - vm_radix_insert(&object->cache, m); + PCPU_INC(cnt.v_tcached); #if VM_NRESERVLEVEL > 0 if (!vm_reserv_free_page(m)) { #else @@ -2283,7 +2636,6 @@ vm_page_advise(vm_page_t m, int advice) * But we do make the page is freeable as we can without * actually taking the step of unmapping it. */ - pmap_clear_modify(m); m->dirty = 0; m->act_count = 0; } else if (advice != MADV_DONTNEED) @@ -2303,15 +2655,7 @@ vm_page_advise(vm_page_t m, int advice) /* * Clear any references to the page. Otherwise, the page daemon will * immediately reactivate the page. - * - * Perform the pmap_clear_reference() first. Otherwise, a concurrent - * pmap operation, such as pmap_remove(), could clear a reference in - * the pmap and set PGA_REFERENCED on the page before the - * pmap_clear_reference() had completed. Consequently, the page would - * appear referenced based upon an old reference that occurred before - * this function ran. */ - pmap_clear_reference(m); vm_page_aflag_clear(m, PGA_REFERENCED); if (advice != MADV_FREE && m->dirty == 0 && pmap_is_modified(m)) @@ -2339,9 +2683,6 @@ vm_page_advise(vm_page_t m, int advice) * to be in the object. If the page doesn't exist, first allocate it * and then conditionally zero it. * - * The caller must always specify the VM_ALLOC_RETRY flag. This is intended - * to facilitate its eventual removal. - * * This routine may sleep. * * The object must be locked on entry. The lock will, however, be released @@ -2351,21 +2692,27 @@ vm_page_t vm_page_grab(vm_object_t object, vm_pindex_t pindex, int allocflags) { vm_page_t m; + int sleep; VM_OBJECT_ASSERT_WLOCKED(object); - KASSERT((allocflags & VM_ALLOC_RETRY) != 0, - ("vm_page_grab: VM_ALLOC_RETRY is required")); + KASSERT((allocflags & VM_ALLOC_SBUSY) == 0 || + (allocflags & VM_ALLOC_IGN_SBUSY) != 0, + ("vm_page_grab: VM_ALLOC_SBUSY/VM_ALLOC_IGN_SBUSY mismatch")); retrylookup: if ((m = vm_page_lookup(object, pindex)) != NULL) { - if ((m->oflags & VPO_BUSY) != 0 || - ((allocflags & VM_ALLOC_IGN_SBUSY) == 0 && m->busy != 0)) { + sleep = (allocflags & VM_ALLOC_IGN_SBUSY) != 0 ? + vm_page_xbusied(m) : vm_page_busied(m); + if (sleep) { /* * Reference the page before unlocking and * sleeping so that the page daemon is less * likely to reclaim it. */ vm_page_aflag_set(m, PGA_REFERENCED); - vm_page_sleep(m, "pgrbwt"); + vm_page_lock(m); + VM_OBJECT_WUNLOCK(object); + vm_page_busy_sleep(m, "pgrbwt"); + VM_OBJECT_WLOCK(object); goto retrylookup; } else { if ((allocflags & VM_ALLOC_WIRED) != 0) { @@ -2373,13 +2720,15 @@ retrylookup: vm_page_wire(m); vm_page_unlock(m); } - if ((allocflags & VM_ALLOC_NOBUSY) == 0) - vm_page_busy(m); + if ((allocflags & + (VM_ALLOC_NOBUSY | VM_ALLOC_SBUSY)) == 0) + vm_page_xbusy(m); + if ((allocflags & VM_ALLOC_SBUSY) != 0) + vm_page_sbusy(m); return (m); } } - m = vm_page_alloc(object, pindex, allocflags & ~(VM_ALLOC_RETRY | - VM_ALLOC_IGN_SBUSY)); + m = vm_page_alloc(object, pindex, allocflags & ~VM_ALLOC_IGN_SBUSY); if (m == NULL) { VM_OBJECT_WUNLOCK(object); VM_WAIT; @@ -2482,12 +2831,12 @@ vm_page_clear_dirty_mask(vm_page_t m, vm_page_bits_t pagebits) #endif /* - * If the object is locked and the page is neither VPO_BUSY nor + * If the object is locked and the page is neither exclusive busy nor * write mapped, then the page's dirty field cannot possibly be * set by a concurrent pmap operation. */ VM_OBJECT_ASSERT_WLOCKED(m->object); - if ((m->oflags & VPO_BUSY) == 0 && !pmap_page_is_write_mapped(m)) + if (!vm_page_xbusied(m) && !pmap_page_is_write_mapped(m)) m->dirty &= ~pagebits; else { /* @@ -2629,12 +2978,19 @@ void vm_page_set_invalid(vm_page_t m, int base, int size) { vm_page_bits_t bits; + vm_object_t object; - VM_OBJECT_ASSERT_WLOCKED(m->object); - bits = vm_page_bits(base, size); + object = m->object; + VM_OBJECT_ASSERT_WLOCKED(object); + if (object->type == OBJT_VNODE && base == 0 && IDX_TO_OFF(m->pindex) + + size >= object->un_pager.vnp.vnp_size) + bits = VM_PAGE_BITS_ALL; + else + bits = vm_page_bits(base, size); if (m->valid == VM_PAGE_BITS_ALL && bits != 0) pmap_remove_all(m); - KASSERT(!pmap_page_is_mapped(m), + KASSERT((bits == 0 && m->valid == VM_PAGE_BITS_ALL) || + !pmap_page_is_mapped(m), ("vm_page_set_invalid: page %p is mapped", m)); m->valid &= ~bits; m->dirty &= ~bits; @@ -2696,7 +3052,7 @@ vm_page_is_valid(vm_page_t m, int base, int size) { vm_page_bits_t bits; - VM_OBJECT_ASSERT_WLOCKED(m->object); + VM_OBJECT_ASSERT_LOCKED(m->object); bits = vm_page_bits(base, size); return (m->valid != 0 && (m->valid & bits) == bits); } @@ -2750,105 +3106,6 @@ vm_page_lock_assert_KBI(vm_page_t m, int a, const char *file, int line) } #endif -int so_zerocp_fullpage = 0; - -/* - * Replace the given page with a copy. The copied page assumes - * the portion of the given page's "wire_count" that is not the - * responsibility of this copy-on-write mechanism. - * - * The object containing the given page must have a non-zero - * paging-in-progress count and be locked. - */ -void -vm_page_cowfault(vm_page_t m) -{ - vm_page_t mnew; - vm_object_t object; - vm_pindex_t pindex; - - vm_page_lock_assert(m, MA_OWNED); - object = m->object; - VM_OBJECT_ASSERT_WLOCKED(object); - KASSERT(object->paging_in_progress != 0, - ("vm_page_cowfault: object %p's paging-in-progress count is zero.", - object)); - pindex = m->pindex; - - retry_alloc: - pmap_remove_all(m); - vm_page_remove(m); - mnew = vm_page_alloc(object, pindex, VM_ALLOC_NORMAL | VM_ALLOC_NOBUSY); - if (mnew == NULL) { - vm_page_insert(m, object, pindex); - vm_page_unlock(m); - VM_OBJECT_WUNLOCK(object); - VM_WAIT; - VM_OBJECT_WLOCK(object); - if (m == vm_page_lookup(object, pindex)) { - vm_page_lock(m); - goto retry_alloc; - } else { - /* - * Page disappeared during the wait. - */ - return; - } - } - - if (m->cow == 0) { - /* - * check to see if we raced with an xmit complete when - * waiting to allocate a page. If so, put things back - * the way they were - */ - vm_page_unlock(m); - vm_page_lock(mnew); - vm_page_free(mnew); - vm_page_unlock(mnew); - vm_page_insert(m, object, pindex); - } else { /* clear COW & copy page */ - if (!so_zerocp_fullpage) - pmap_copy_page(m, mnew); - mnew->valid = VM_PAGE_BITS_ALL; - vm_page_dirty(mnew); - mnew->wire_count = m->wire_count - m->cow; - m->wire_count = m->cow; - vm_page_unlock(m); - } -} - -void -vm_page_cowclear(vm_page_t m) -{ - - vm_page_lock_assert(m, MA_OWNED); - if (m->cow) { - m->cow--; - /* - * let vm_fault add back write permission lazily - */ - } - /* - * sf_buf_free() will free the page, so we needn't do it here - */ -} - -int -vm_page_cowsetup(vm_page_t m) -{ - - vm_page_lock_assert(m, MA_OWNED); - if ((m->flags & PG_FICTITIOUS) != 0 || - (m->oflags & VPO_UNMANAGED) != 0 || - m->cow == USHRT_MAX - 1 || !VM_OBJECT_TRYWLOCK(m->object)) - return (EBUSY); - m->cow++; - pmap_remove_write(m); - VM_OBJECT_WUNLOCK(m->object); - return (0); -} - #ifdef INVARIANTS void vm_page_object_lock_assert(vm_page_t m) @@ -2856,12 +3113,11 @@ vm_page_object_lock_assert(vm_page_t m) /* * Certain of the page's fields may only be modified by the - * holder of the containing object's lock or the setter of the - * page's VPO_BUSY flag. Unfortunately, the setter of the - * VPO_BUSY flag is not recorded, and thus cannot be checked - * here. + * holder of the containing object's lock or the exclusive busy. + * holder. Unfortunately, the holder of the write busy is + * not recorded, and thus cannot be checked here. */ - if (m->object != NULL && (m->oflags & VPO_BUSY) == 0) + if (m->object != NULL && !vm_page_xbusied(m)) VM_OBJECT_ASSERT_WLOCKED(m->object); } #endif @@ -2888,18 +3144,20 @@ DB_SHOW_COMMAND(page, vm_page_print_page_info) DB_SHOW_COMMAND(pageq, vm_page_print_pageq_info) { - - db_printf("PQ_FREE:"); - db_printf(" %d", cnt.v_free_count); - db_printf("\n"); - - db_printf("PQ_CACHE:"); - db_printf(" %d", cnt.v_cache_count); - db_printf("\n"); + int dom; - db_printf("PQ_ACTIVE: %d, PQ_INACTIVE: %d\n", - *vm_pagequeues[PQ_ACTIVE].pq_cnt, - *vm_pagequeues[PQ_INACTIVE].pq_cnt); + db_printf("pq_free %d pq_cache %d\n", + cnt.v_free_count, cnt.v_cache_count); + for (dom = 0; dom < vm_ndomains; dom++) { + db_printf( + "dom %d page_cnt %d free %d pq_act %d pq_inact %d pass %d\n", + dom, + vm_dom[dom].vmd_page_count, + vm_dom[dom].vmd_free_count, + vm_dom[dom].vmd_pagequeues[PQ_ACTIVE].pq_cnt, + vm_dom[dom].vmd_pagequeues[PQ_INACTIVE].pq_cnt, + vm_dom[dom].vmd_pass); + } } DB_SHOW_COMMAND(pginfo, vm_page_print_pginfo) @@ -2919,9 +3177,9 @@ DB_SHOW_COMMAND(pginfo, vm_page_print_pginfo) m = (vm_page_t)addr; db_printf( "page %p obj %p pidx 0x%jx phys 0x%jx q %d hold %d wire %d\n" - " af 0x%x of 0x%x f 0x%x act %d busy %d valid 0x%x dirty 0x%x\n", + " af 0x%x of 0x%x f 0x%x act %d busy %x valid 0x%x dirty 0x%x\n", m, m->object, (uintmax_t)m->pindex, (uintmax_t)m->phys_addr, m->queue, m->hold_count, m->wire_count, m->aflags, m->oflags, - m->flags, m->act_count, m->busy, m->valid, m->dirty); + m->flags, m->act_count, m->busy_lock, m->valid, m->dirty); } #endif /* DDB */ diff --git a/sys/vm/vm_page.h b/sys/vm/vm_page.h index 4fe5d7e2190..7846702aa8d 100644 --- a/sys/vm/vm_page.h +++ b/sys/vm/vm_page.h @@ -126,26 +126,34 @@ typedef uint64_t vm_page_bits_t; #endif struct vm_page { - TAILQ_ENTRY(vm_page) pageq; /* page queue or free list (Q) */ - TAILQ_ENTRY(vm_page) listq; /* pages in same object (O) */ - - vm_object_t object; /* which object am I in (O,P)*/ + union { + TAILQ_ENTRY(vm_page) q; /* page queue or free list (Q) */ + struct { + SLIST_ENTRY(vm_page) ss; /* private slists */ + void *pv; + } s; + struct { + u_long p; + u_long v; + } memguard; + } plinks; + TAILQ_ENTRY(vm_page) listq; /* pages in same object (O) */ + vm_object_t object; /* which object am I in (O,P) */ vm_pindex_t pindex; /* offset into object (O,P) */ vm_paddr_t phys_addr; /* physical address of page */ struct md_page md; /* machine dependant stuff */ - uint8_t queue; /* page queue index (P,Q) */ - int8_t segind; - short hold_count; /* page hold count (P) */ - uint8_t order; /* index of the buddy queue */ - uint8_t pool; - u_short cow; /* page cow mapping count (P) */ u_int wire_count; /* wired down maps refs (P) */ + volatile u_int busy_lock; /* busy owners lock */ + uint16_t hold_count; /* page hold count (P) */ + uint16_t flags; /* page PG_* flags (P) */ uint8_t aflags; /* access is atomic */ uint8_t oflags; /* page VPO_* flags (O) */ - uint16_t flags; /* page PG_* flags (P) */ + uint8_t queue; /* page queue index (P,Q) */ + int8_t segind; + uint8_t order; /* index of the buddy queue */ + uint8_t pool; u_char act_count; /* page usage count (P) */ - u_char busy; /* page busy count (O) */ - /* NOTE that these must support one bit per DEV_BSIZE in a page!!! */ + /* NOTE that these must support one bit per DEV_BSIZE in a page */ /* so, on normal X86 kernels, they must be at least 8 bits wide */ vm_page_bits_t valid; /* map of valid DEV_BSIZE chunks (O) */ vm_page_bits_t dirty; /* map of dirty DEV_BSIZE chunks (M) */ @@ -165,34 +173,83 @@ struct vm_page { * mappings, and such pages are also not on any PQ queue. * */ -#define VPO_BUSY 0x01 /* page is in transit */ -#define VPO_WANTED 0x02 /* someone is waiting for page */ +#define VPO_UNUSED01 0x01 /* --available-- */ +#define VPO_SWAPSLEEP 0x02 /* waiting for swap to finish */ #define VPO_UNMANAGED 0x04 /* no PV management for page */ #define VPO_SWAPINPROG 0x08 /* swap I/O in progress on page */ #define VPO_NOSYNC 0x10 /* do not collect for syncer */ +/* + * Busy page implementation details. + * The algorithm is taken mostly by rwlock(9) and sx(9) locks implementation, + * even if the support for owner identity is removed because of size + * constraints. Checks on lock recursion are then not possible, while the + * lock assertions effectiveness is someway reduced. + */ +#define VPB_BIT_SHARED 0x01 +#define VPB_BIT_EXCLUSIVE 0x02 +#define VPB_BIT_WAITERS 0x04 +#define VPB_BIT_FLAGMASK \ + (VPB_BIT_SHARED | VPB_BIT_EXCLUSIVE | VPB_BIT_WAITERS) + +#define VPB_SHARERS_SHIFT 3 +#define VPB_SHARERS(x) \ + (((x) & ~VPB_BIT_FLAGMASK) >> VPB_SHARERS_SHIFT) +#define VPB_SHARERS_WORD(x) ((x) << VPB_SHARERS_SHIFT | VPB_BIT_SHARED) +#define VPB_ONE_SHARER (1 << VPB_SHARERS_SHIFT) + +#define VPB_SINGLE_EXCLUSIVER VPB_BIT_EXCLUSIVE + +#define VPB_UNBUSIED VPB_SHARERS_WORD(0) + #define PQ_NONE 255 #define PQ_INACTIVE 0 #define PQ_ACTIVE 1 #define PQ_COUNT 2 TAILQ_HEAD(pglist, vm_page); +SLIST_HEAD(spglist, vm_page); struct vm_pagequeue { struct mtx pq_mutex; struct pglist pq_pl; - int *const pq_cnt; - const char *const pq_name; + int pq_cnt; + int * const pq_vcnt; + const char * const pq_name; } __aligned(CACHE_LINE_SIZE); -extern struct vm_pagequeue vm_pagequeues[PQ_COUNT]; + +struct vm_domain { + struct vm_pagequeue vmd_pagequeues[PQ_COUNT]; + u_int vmd_page_count; + u_int vmd_free_count; + long vmd_segs; /* bitmask of the segments */ + boolean_t vmd_oom; + int vmd_pass; /* local pagedaemon pass */ + struct vm_page vmd_marker; /* marker for pagedaemon private use */ +}; + +extern struct vm_domain vm_dom[MAXMEMDOM]; #define vm_pagequeue_assert_locked(pq) mtx_assert(&(pq)->pq_mutex, MA_OWNED) -#define vm_pagequeue_init_lock(pq) mtx_init(&(pq)->pq_mutex, \ - (pq)->pq_name, "vm pagequeue", MTX_DEF | MTX_DUPOK); #define vm_pagequeue_lock(pq) mtx_lock(&(pq)->pq_mutex) #define vm_pagequeue_unlock(pq) mtx_unlock(&(pq)->pq_mutex) +#ifdef _KERNEL +static __inline void +vm_pagequeue_cnt_add(struct vm_pagequeue *pq, int addend) +{ + +#ifdef notyet + vm_pagequeue_assert_locked(pq); +#endif + pq->pq_cnt += addend; + atomic_add_int(pq->pq_vcnt, addend); +} +#define vm_pagequeue_cnt_inc(pq) vm_pagequeue_cnt_add((pq), 1) +#define vm_pagequeue_cnt_dec(pq) vm_pagequeue_cnt_add((pq), -1) +#endif /* _KERNEL */ + extern struct mtx_padalign vm_page_queue_free_mtx; extern struct mtx_padalign pa_lock[]; @@ -248,8 +305,9 @@ extern struct mtx_padalign pa_lock[]; * directly set this flag. They should call vm_page_reference() instead. * * PGA_WRITEABLE is set exclusively on managed pages by pmap_enter(). When it - * does so, the page must be VPO_BUSY. The MI VM layer must never access this - * flag directly. Instead, it should call pmap_page_is_write_mapped(). + * does so, the page must be exclusive busied. The MI VM layer must never + * access this flag directly. Instead, it should call + * pmap_page_is_write_mapped(). * * PGA_EXECUTABLE may be set by pmap routines, and indicates that a page has * at least one executable mapping. It is not consumed by the MI VM layer. @@ -267,7 +325,6 @@ extern struct mtx_padalign pa_lock[]; #define PG_FICTITIOUS 0x0004 /* physical page doesn't exist */ #define PG_ZERO 0x0008 /* page is zeroed */ #define PG_MARKER 0x0010 /* special queue marker page */ -#define PG_SLAB 0x0020 /* object pointer is actually a slab */ #define PG_WINATCFLS 0x0040 /* flush dirty page on inactive q */ #define PG_NODUMP 0x0080 /* don't include this page in a dump */ #define PG_UNHOLDFREE 0x0100 /* delayed free of a held page */ @@ -329,13 +386,13 @@ vm_page_t PHYS_TO_VM_PAGE(vm_paddr_t pa); /* page allocation flags: */ #define VM_ALLOC_WIRED 0x0020 /* non pageable */ #define VM_ALLOC_ZERO 0x0040 /* Try to obtain a zeroed page */ -#define VM_ALLOC_RETRY 0x0080 /* Mandatory with vm_page_grab() */ #define VM_ALLOC_NOOBJ 0x0100 /* No associated object */ #define VM_ALLOC_NOBUSY 0x0200 /* Do not busy the page */ #define VM_ALLOC_IFCACHED 0x0400 /* Fail if the page is not cached */ #define VM_ALLOC_IFNOTCACHED 0x0800 /* Fail if the page is cached */ #define VM_ALLOC_IGN_SBUSY 0x1000 /* vm_page_grab() only */ #define VM_ALLOC_NODUMP 0x2000 /* don't include in dump */ +#define VM_ALLOC_SBUSY 0x4000 /* Shared busy the page */ #define VM_ALLOC_COUNT_SHIFT 16 #define VM_ALLOC_COUNT(count) ((count) << VM_ALLOC_COUNT_SHIFT) @@ -359,15 +416,13 @@ malloc2vm_flags(int malloc_flags) } #endif -void vm_page_busy(vm_page_t m); +void vm_page_busy_downgrade(vm_page_t m); +void vm_page_busy_sleep(vm_page_t m, const char *msg); void vm_page_flash(vm_page_t m); -void vm_page_io_start(vm_page_t m); -void vm_page_io_finish(vm_page_t m); void vm_page_hold(vm_page_t mem); void vm_page_unhold(vm_page_t mem); void vm_page_free(vm_page_t m); void vm_page_free_zero(vm_page_t m); -void vm_page_wakeup(vm_page_t m); void vm_page_activate (vm_page_t); void vm_page_advise(vm_page_t m, int advice); @@ -388,26 +443,33 @@ void vm_page_dequeue_locked(vm_page_t m); vm_page_t vm_page_find_least(vm_object_t, vm_pindex_t); vm_page_t vm_page_getfake(vm_paddr_t paddr, vm_memattr_t memattr); void vm_page_initfake(vm_page_t m, vm_paddr_t paddr, vm_memattr_t memattr); -void vm_page_insert (vm_page_t, vm_object_t, vm_pindex_t); +int vm_page_insert (vm_page_t, vm_object_t, vm_pindex_t); boolean_t vm_page_is_cached(vm_object_t object, vm_pindex_t pindex); vm_page_t vm_page_lookup (vm_object_t, vm_pindex_t); vm_page_t vm_page_next(vm_page_t m); int vm_page_pa_tryrelock(pmap_t, vm_paddr_t, vm_paddr_t *); +struct vm_pagequeue *vm_page_pagequeue(vm_page_t m); vm_page_t vm_page_prev(vm_page_t m); void vm_page_putfake(vm_page_t m); void vm_page_readahead_finish(vm_page_t m); void vm_page_reference(vm_page_t m); void vm_page_remove (vm_page_t); -void vm_page_rename (vm_page_t, vm_object_t, vm_pindex_t); +int vm_page_rename (vm_page_t, vm_object_t, vm_pindex_t); +vm_page_t vm_page_replace(vm_page_t mnew, vm_object_t object, + vm_pindex_t pindex); void vm_page_requeue(vm_page_t m); void vm_page_requeue_locked(vm_page_t m); +int vm_page_sbusied(vm_page_t m); void vm_page_set_valid_range(vm_page_t m, int base, int size); -void vm_page_sleep(vm_page_t m, const char *msg); +int vm_page_sleep_if_busy(vm_page_t m, const char *msg); vm_offset_t vm_page_startup(vm_offset_t vaddr); +void vm_page_sunbusy(vm_page_t m); +int vm_page_trysbusy(vm_page_t m); void vm_page_unhold_pages(vm_page_t *ma, int count); void vm_page_unwire (vm_page_t, int); void vm_page_updatefake(vm_page_t m, vm_paddr_t paddr, vm_memattr_t memattr); void vm_page_wire (vm_page_t); +void vm_page_xunbusy_hard(vm_page_t m); void vm_page_set_validclean (vm_page_t, int, int); void vm_page_clear_dirty (vm_page_t, int, int); void vm_page_set_invalid (vm_page_t, int, int); @@ -417,9 +479,6 @@ vm_page_bits_t vm_page_bits(int base, int size); void vm_page_zero_invalid(vm_page_t m, boolean_t setvalid); void vm_page_free_toq(vm_page_t m); void vm_page_zero_idle_wakeup(void); -void vm_page_cowfault (vm_page_t); -int vm_page_cowsetup(vm_page_t); -void vm_page_cowclear (vm_page_t); void vm_page_dirty_KBI(vm_page_t m); void vm_page_lock_KBI(vm_page_t m, const char *file, int line); @@ -430,6 +489,48 @@ void vm_page_assert_locked_KBI(vm_page_t m, const char *file, int line); void vm_page_lock_assert_KBI(vm_page_t m, int a, const char *file, int line); #endif +#define vm_page_assert_sbusied(m) \ + KASSERT(vm_page_sbusied(m), \ + ("vm_page_assert_sbusied: page %p not shared busy @ %s:%d", \ + (void *)m, __FILE__, __LINE__)); + +#define vm_page_assert_unbusied(m) \ + KASSERT(!vm_page_busied(m), \ + ("vm_page_assert_unbusied: page %p busy @ %s:%d", \ + (void *)m, __FILE__, __LINE__)); + +#define vm_page_assert_xbusied(m) \ + KASSERT(vm_page_xbusied(m), \ + ("vm_page_assert_xbusied: page %p not exclusive busy @ %s:%d", \ + (void *)m, __FILE__, __LINE__)); + +#define vm_page_busied(m) \ + ((m)->busy_lock != VPB_UNBUSIED) + +#define vm_page_sbusy(m) do { \ + if (!vm_page_trysbusy(m)) \ + panic("%s: page %p failed shared busing", __func__, m); \ +} while (0) + +#define vm_page_tryxbusy(m) \ + (atomic_cmpset_acq_int(&m->busy_lock, VPB_UNBUSIED, \ + VPB_SINGLE_EXCLUSIVER)) + +#define vm_page_xbusied(m) \ + ((m->busy_lock & VPB_SINGLE_EXCLUSIVER) != 0) + +#define vm_page_xbusy(m) do { \ + if (!vm_page_tryxbusy(m)) \ + panic("%s: page %p failed exclusive busing", __func__, \ + m); \ +} while (0) + +#define vm_page_xunbusy(m) do { \ + if (!atomic_cmpset_rel_int(&(m)->busy_lock, \ + VPB_SINGLE_EXCLUSIVER, VPB_UNBUSIED)) \ + vm_page_xunbusy_hard(m); \ +} while (0) + #ifdef INVARIANTS void vm_page_object_lock_assert(vm_page_t m); #define VM_PAGE_OBJECT_LOCK_ASSERT(m) vm_page_object_lock_assert(m) @@ -484,11 +585,11 @@ vm_page_aflag_set(vm_page_t m, uint8_t bits) /* * The PGA_WRITEABLE flag can only be set if the page is managed and - * VPO_BUSY. Currently, this flag is only set by pmap_enter(). + * exclusive busied. Currently, this flag is only set by pmap_enter(). */ KASSERT((bits & PGA_WRITEABLE) == 0 || - (m->oflags & (VPO_UNMANAGED | VPO_BUSY)) == VPO_BUSY, - ("vm_page_aflag_set: PGA_WRITEABLE and !VPO_BUSY")); + ((m->oflags & VPO_UNMANAGED) == 0 && vm_page_xbusied(m)), + ("vm_page_aflag_set: PGA_WRITEABLE and not exclusive busy")); /* * Access the whole 32-bit word containing the aflags field with an @@ -543,27 +644,6 @@ vm_page_remque(vm_page_t m) vm_page_dequeue(m); } -/* - * vm_page_sleep_if_busy: - * - * Sleep and release the page queues lock if VPO_BUSY is set or, - * if also_m_busy is TRUE, busy is non-zero. Returns TRUE if the - * thread slept and the page queues lock was released. - * Otherwise, retains the page queues lock and returns FALSE. - * - * The object containing the given page must be locked. - */ -static __inline int -vm_page_sleep_if_busy(vm_page_t m, int also_m_busy, const char *msg) -{ - - if ((m->oflags & VPO_BUSY) || (also_m_busy && m->busy)) { - vm_page_sleep(m, msg); - return (TRUE); - } - return (FALSE); -} - /* * vm_page_undirty: * diff --git a/sys/vm/vm_pageout.c b/sys/vm/vm_pageout.c index 841820b4b2f..5660b561a0f 100644 --- a/sys/vm/vm_pageout.c +++ b/sys/vm/vm_pageout.c @@ -90,6 +90,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include #include @@ -103,6 +104,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include #include @@ -114,7 +116,8 @@ __FBSDID("$FreeBSD$"); /* the kernel process "vm_pageout"*/ static void vm_pageout(void); static int vm_pageout_clean(vm_page_t); -static void vm_pageout_scan(int pass); +static void vm_pageout_scan(struct vm_domain *vmd, int pass); +static void vm_pageout_mightbe_oom(struct vm_domain *vmd, int pass); struct proc *pageproc; @@ -143,6 +146,7 @@ SYSINIT(vmdaemon, SI_SUB_KTHREAD_VM, SI_ORDER_FIRST, kproc_start, &vm_kp); int vm_pages_needed; /* Event on which pageout daemon sleeps */ int vm_pageout_deficit; /* Estimated number of pages deficit */ int vm_pageout_pages_needed; /* flag saying that the pageout daemon needs pages */ +int vm_pageout_wakeup_thresh; #if !defined(NO_SWAPPING) static int vm_pageout_req_swapout; /* XXX */ @@ -152,13 +156,11 @@ static struct mtx vm_daemon_mtx; MTX_SYSINIT(vm_daemon, &vm_daemon_mtx, "vm daemon", MTX_DEF); #endif static int vm_max_launder = 32; -static int vm_pageout_stats_max; -static int vm_pageout_stats; -static int vm_pageout_stats_interval; -static int vm_pageout_full_stats; -static int vm_pageout_full_stats_interval; +static int vm_pageout_update_period; static int defer_swap_pageouts; static int disable_swap_pageouts; +static int lowmem_period = 10; +static int lowmem_ticks; #if defined(NO_SWAPPING) static int vm_swap_enabled = 0; @@ -168,23 +170,19 @@ static int vm_swap_enabled = 1; static int vm_swap_idle_enabled = 0; #endif +SYSCTL_INT(_vm, OID_AUTO, pageout_wakeup_thresh, + CTLFLAG_RW, &vm_pageout_wakeup_thresh, 0, + "free page threshold for waking up the pageout daemon"); + SYSCTL_INT(_vm, OID_AUTO, max_launder, CTLFLAG_RW, &vm_max_launder, 0, "Limit dirty flushes in pageout"); -SYSCTL_INT(_vm, OID_AUTO, pageout_stats_max, - CTLFLAG_RW, &vm_pageout_stats_max, 0, "Max pageout stats scan length"); - -SYSCTL_INT(_vm, OID_AUTO, pageout_stats, - CTLFLAG_RD, &vm_pageout_stats, 0, "Number of partial stats scans"); - -SYSCTL_INT(_vm, OID_AUTO, pageout_stats_interval, - CTLFLAG_RW, &vm_pageout_stats_interval, 0, "Interval for partial stats scan"); - -SYSCTL_INT(_vm, OID_AUTO, pageout_full_stats, - CTLFLAG_RD, &vm_pageout_full_stats, 0, "Number of full stats scans"); - -SYSCTL_INT(_vm, OID_AUTO, pageout_full_stats_interval, - CTLFLAG_RW, &vm_pageout_full_stats_interval, 0, "Interval for full stats scan"); +SYSCTL_INT(_vm, OID_AUTO, pageout_update_period, + CTLFLAG_RW, &vm_pageout_update_period, 0, + "Maximum active LRU update period"); + +SYSCTL_INT(_vm, OID_AUTO, lowmem_period, CTLFLAG_RW, &lowmem_period, 0, + "Low memory callback period"); #if defined(NO_SWAPPING) SYSCTL_INT(_vm, VM_SWAPPING_ENABLED, swap_enabled, @@ -216,20 +214,20 @@ SYSCTL_INT(_vm, OID_AUTO, max_wired, CTLFLAG_RW, &vm_page_max_wired, 0, "System-wide limit to wired page count"); static boolean_t vm_pageout_fallback_object_lock(vm_page_t, vm_page_t *); -static boolean_t vm_pageout_launder(int, int, vm_paddr_t, vm_paddr_t); +static boolean_t vm_pageout_launder(struct vm_pagequeue *pq, int, vm_paddr_t, + vm_paddr_t); #if !defined(NO_SWAPPING) static void vm_pageout_map_deactivate_pages(vm_map_t, long); static void vm_pageout_object_deactivate_pages(pmap_t, vm_object_t, long); static void vm_req_vmdaemon(int req); #endif static boolean_t vm_pageout_page_lock(vm_page_t, vm_page_t *); -static void vm_pageout_page_stats(void); /* * Initialize a dummy page for marking the caller's place in the specified * paging queue. In principle, this function only needs to set the flag - * PG_MARKER. Nonetheless, it sets the flag VPO_BUSY and initializes the hold - * count to one as safety precautions. + * PG_MARKER. Nonetheless, it wirte busies and initializes the hold count + * to one as safety precautions. */ static void vm_pageout_init_marker(vm_page_t marker, u_short queue) @@ -237,7 +235,7 @@ vm_pageout_init_marker(vm_page_t marker, u_short queue) bzero(marker, sizeof(*marker)); marker->flags = PG_MARKER; - marker->oflags = VPO_BUSY; + marker->busy_lock = VPB_SINGLE_EXCLUSIVER; marker->queue = queue; marker->hold_count = 1; } @@ -267,10 +265,10 @@ vm_pageout_fallback_object_lock(vm_page_t m, vm_page_t *next) queue = m->queue; vm_pageout_init_marker(&marker, queue); - pq = &vm_pagequeues[queue]; + pq = vm_page_pagequeue(m); object = m->object; - TAILQ_INSERT_AFTER(&pq->pq_pl, m, &marker, pageq); + TAILQ_INSERT_AFTER(&pq->pq_pl, m, &marker, plinks.q); vm_pagequeue_unlock(pq); vm_page_unlock(m); VM_OBJECT_WLOCK(object); @@ -278,11 +276,11 @@ vm_pageout_fallback_object_lock(vm_page_t m, vm_page_t *next) vm_pagequeue_lock(pq); /* Page queue might have changed. */ - *next = TAILQ_NEXT(&marker, pageq); + *next = TAILQ_NEXT(&marker, plinks.q); unchanged = (m->queue == queue && m->object == object && - &marker == TAILQ_NEXT(m, pageq)); - TAILQ_REMOVE(&pq->pq_pl, &marker, pageq); + &marker == TAILQ_NEXT(m, plinks.q)); + TAILQ_REMOVE(&pq->pq_pl, &marker, plinks.q); return (unchanged); } @@ -309,17 +307,17 @@ vm_pageout_page_lock(vm_page_t m, vm_page_t *next) queue = m->queue; vm_pageout_init_marker(&marker, queue); - pq = &vm_pagequeues[queue]; + pq = vm_page_pagequeue(m); - TAILQ_INSERT_AFTER(&pq->pq_pl, m, &marker, pageq); + TAILQ_INSERT_AFTER(&pq->pq_pl, m, &marker, plinks.q); vm_pagequeue_unlock(pq); vm_page_lock(m); vm_pagequeue_lock(pq); /* Page queue might have changed. */ - *next = TAILQ_NEXT(&marker, pageq); - unchanged = (m->queue == queue && &marker == TAILQ_NEXT(m, pageq)); - TAILQ_REMOVE(&pq->pq_pl, &marker, pageq); + *next = TAILQ_NEXT(&marker, plinks.q); + unchanged = (m->queue == queue && &marker == TAILQ_NEXT(m, plinks.q)); + TAILQ_REMOVE(&pq->pq_pl, &marker, plinks.q); return (unchanged); } @@ -357,8 +355,7 @@ vm_pageout_clean(vm_page_t m) /* * Can't clean the page if it's busy or held. */ - KASSERT(m->busy == 0 && (m->oflags & VPO_BUSY) == 0, - ("vm_pageout_clean: page %p is busy", m)); + vm_page_assert_unbusied(m); KASSERT(m->hold_count == 0, ("vm_pageout_clean: page %p is held", m)); vm_page_unlock(m); @@ -396,8 +393,7 @@ more: break; } - if ((p = vm_page_prev(pb)) == NULL || - (p->oflags & VPO_BUSY) != 0 || p->busy != 0) { + if ((p = vm_page_prev(pb)) == NULL || vm_page_busied(p)) { ib = 0; break; } @@ -426,8 +422,7 @@ more: pindex + is < object->size) { vm_page_t p; - if ((p = vm_page_next(ps)) == NULL || - (p->oflags & VPO_BUSY) != 0 || p->busy != 0) + if ((p = vm_page_next(ps)) == NULL || vm_page_busied(p)) break; vm_page_lock(p); vm_page_test_dirty(p); @@ -497,7 +492,7 @@ vm_pageout_flush(vm_page_t *mc, int count, int flags, int mreq, int *prunlen, KASSERT(mc[i]->valid == VM_PAGE_BITS_ALL, ("vm_pageout_flush: partially invalid page %p index %d/%d", mc[i], i, count)); - vm_page_io_start(mc[i]); + vm_page_sbusy(mc[i]); pmap_remove_write(mc[i]); } vm_object_pip_add(object, count); @@ -553,7 +548,7 @@ vm_pageout_flush(vm_page_t *mc, int count, int flags, int mreq, int *prunlen, */ if (pageout_status[i] != VM_PAGER_PEND) { vm_object_pip_wakeup(object); - vm_page_io_finish(mt); + vm_page_sunbusy(mt); if (vm_page_count_severe()) { vm_page_lock(mt); vm_page_try_to_cache(mt); @@ -567,21 +562,17 @@ vm_pageout_flush(vm_page_t *mc, int count, int flags, int mreq, int *prunlen, } static boolean_t -vm_pageout_launder(int queue, int tries, vm_paddr_t low, vm_paddr_t high) +vm_pageout_launder(struct vm_pagequeue *pq, int tries, vm_paddr_t low, + vm_paddr_t high) { struct mount *mp; - struct vm_pagequeue *pq; struct vnode *vp; vm_object_t object; vm_paddr_t pa; vm_page_t m, m_tmp, next; - pq = &vm_pagequeues[queue]; vm_pagequeue_lock(pq); - TAILQ_FOREACH_SAFE(m, &pq->pq_pl, pageq, next) { - KASSERT(m->queue == queue, - ("vm_pageout_launder: page %p's queue is not %d", m, - queue)); + TAILQ_FOREACH_SAFE(m, &pq->pq_pl, plinks.q, next) { if ((m->flags & PG_MARKER) != 0) continue; pa = VM_PAGE_TO_PHYS(m); @@ -594,8 +585,7 @@ vm_pageout_launder(int queue, int tries, vm_paddr_t low, vm_paddr_t high) object = m->object; if ((!VM_OBJECT_TRYWLOCK(object) && (!vm_pageout_fallback_object_lock(m, &next) || - m->hold_count != 0)) || (m->oflags & VPO_BUSY) != 0 || - m->busy != 0) { + m->hold_count != 0)) || vm_page_busied(m)) { vm_page_unlock(m); VM_OBJECT_WUNLOCK(object); continue; @@ -661,7 +651,8 @@ vm_pageout_launder(int queue, int tries, vm_paddr_t low, vm_paddr_t high) void vm_pageout_grow_cache(int tries, vm_paddr_t low, vm_paddr_t high) { - int actl, actmax, inactl, inactmax; + int actl, actmax, inactl, inactmax, dom, initial_dom; + static int start_dom = 0; if (tries > 0) { /* @@ -677,19 +668,55 @@ vm_pageout_grow_cache(int tries, vm_paddr_t low, vm_paddr_t high) */ uma_reclaim(); } + + /* + * Make the next scan start on the next domain. + */ + initial_dom = atomic_fetchadd_int(&start_dom, 1) % vm_ndomains; + inactl = 0; inactmax = cnt.v_inactive_count; actl = 0; actmax = tries < 2 ? 0 : cnt.v_active_count; + dom = initial_dom; + + /* + * Scan domains in round-robin order, first inactive queues, + * then active. Since domain usually owns large physically + * contiguous chunk of memory, it makes sense to completely + * exhaust one domain before switching to next, while growing + * the pool of contiguous physical pages. + * + * Do not even start launder a domain which cannot contain + * the specified address range, as indicated by segments + * constituting the domain. + */ again: - if (inactl < inactmax && vm_pageout_launder(PQ_INACTIVE, tries, low, - high)) { - inactl++; - goto again; + if (inactl < inactmax) { + if (vm_phys_domain_intersects(vm_dom[dom].vmd_segs, + low, high) && + vm_pageout_launder(&vm_dom[dom].vmd_pagequeues[PQ_INACTIVE], + tries, low, high)) { + inactl++; + goto again; + } + if (++dom == vm_ndomains) + dom = 0; + if (dom != initial_dom) + goto again; } - if (actl < actmax && vm_pageout_launder(PQ_ACTIVE, tries, low, high)) { - actl++; - goto again; + if (actl < actmax) { + if (vm_phys_domain_intersects(vm_dom[dom].vmd_segs, + low, high) && + vm_pageout_launder(&vm_dom[dom].vmd_pagequeues[PQ_ACTIVE], + tries, low, high)) { + actl++; + goto again; + } + if (++dom == vm_ndomains) + dom = 0; + if (dom != initial_dom) + goto again; } } @@ -730,7 +757,7 @@ vm_pageout_object_deactivate_pages(pmap_t pmap, vm_object_t first_object, TAILQ_FOREACH(p, &object->memq, listq) { if (pmap_resident_count(pmap) <= desired) goto unlock_return; - if ((p->oflags & VPO_BUSY) != 0 || p->busy != 0) + if (vm_page_busied(p)) continue; PCPU_INC(cnt.v_pdpages); vm_page_lock(p); @@ -859,12 +886,15 @@ vm_pageout_map_deactivate_pages(map, desired) /* * vm_pageout_scan does the dirty work for the pageout daemon. + * + * pass 0 - Update active LRU/deactivate pages + * pass 1 - Move inactive to cache or free + * pass 2 - Launder dirty pages */ static void -vm_pageout_scan(int pass) +vm_pageout_scan(struct vm_domain *vmd, int pass) { vm_page_t m, next; - struct vm_page marker; struct vm_pagequeue *pq; int page_shortage, maxscan, pcount; int addl_page_shortage; @@ -874,21 +904,28 @@ vm_pageout_scan(int pass) int maxlaunder; boolean_t queues_locked; - vm_pageout_init_marker(&marker, PQ_INACTIVE); - /* - * Decrease registered cache sizes. + * If we need to reclaim memory ask kernel caches to return + * some. We rate limit to avoid thrashing. */ - EVENTHANDLER_INVOKE(vm_lowmem, 0); - /* - * We do this explicitly after the caches have been drained above. - */ - uma_reclaim(); + if (vmd == &vm_dom[0] && pass > 0 && + lowmem_ticks + (lowmem_period * hz) < ticks) { + /* + * Decrease registered cache sizes. + */ + EVENTHANDLER_INVOKE(vm_lowmem, 0); + /* + * We do this explicitly after the caches have been + * drained above. + */ + uma_reclaim(); + lowmem_ticks = ticks; + } /* * The addl_page_shortage is the number of temporarily * stuck pages in the inactive queue. In other words, the - * number of pages from cnt.v_inactive_count that should be + * number of pages from the inactive count that should be * discounted in setting the target for the active queue scan. */ addl_page_shortage = atomic_readandclear_int(&vm_pageout_deficit); @@ -911,11 +948,9 @@ vm_pageout_scan(int pass) */ if ((maxlaunder = vm_max_launder) <= 1) maxlaunder = 1; - if (pass) + if (pass > 1) maxlaunder = 10000; - maxscan = cnt.v_inactive_count; - /* * Start scanning the inactive queue for pages we can move to the * cache or free. The scan will stop when the target is reached or @@ -923,7 +958,8 @@ vm_pageout_scan(int pass) * is not used to form decisions for the inactive queue, only for the * active queue. */ - pq = &vm_pagequeues[PQ_INACTIVE]; + pq = &vmd->vmd_pagequeues[PQ_INACTIVE]; + maxscan = pq->pq_cnt; vm_pagequeue_lock(pq); queues_locked = TRUE; for (m = TAILQ_FIRST(&pq->pq_pl); @@ -934,7 +970,7 @@ vm_pageout_scan(int pass) KASSERT(m->queue == PQ_INACTIVE, ("Inactive queue %p", m)); PCPU_INC(cnt.v_pdpages); - next = TAILQ_NEXT(m, pageq); + next = TAILQ_NEXT(m, plinks.q); /* * skip marker pages @@ -972,7 +1008,7 @@ vm_pageout_scan(int pass) * pages, because they may leave the inactive queue * shortly after page scan is finished. */ - if (m->busy != 0 || (m->oflags & VPO_BUSY) != 0) { + if (vm_page_busied(m)) { vm_page_unlock(m); VM_OBJECT_WUNLOCK(object); addl_page_shortage++; @@ -984,7 +1020,7 @@ vm_pageout_scan(int pass) * 'next' pointer. Use our marker to remember our * place. */ - TAILQ_INSERT_AFTER(&pq->pq_pl, m, &marker, pageq); + TAILQ_INSERT_AFTER(&pq->pq_pl, m, &vmd->vmd_marker, plinks.q); vm_pagequeue_unlock(pq); queues_locked = FALSE; @@ -1034,7 +1070,7 @@ vm_pageout_scan(int pass) /* * Held pages are essentially stuck in the * queue. So, they ought to be discounted - * from cnt.v_inactive_count. See the + * from the inactive count. See the * calculation of the page_shortage for the * loop over the active queue below. */ @@ -1068,7 +1104,7 @@ vm_pageout_scan(int pass) */ vm_page_cache(m); --page_shortage; - } else if ((m->flags & PG_WINATCFLS) == 0 && pass == 0) { + } else if ((m->flags & PG_WINATCFLS) == 0 && pass < 2) { /* * Dirty pages need to be paged out, but flushing * a page is extremely expensive verses freeing @@ -1178,7 +1214,7 @@ vm_pageout_scan(int pass) */ if (m->queue != PQ_INACTIVE || m->object != object || - TAILQ_NEXT(m, pageq) != &marker) { + TAILQ_NEXT(m, plinks.q) != &vmd->vmd_marker) { vm_page_unlock(m); if (object->flags & OBJ_MIGHTBEDIRTY) vnodes_skipped++; @@ -1191,7 +1227,7 @@ vm_pageout_scan(int pass) * page back onto the end of the queue so that * statistics are more correct if we don't. */ - if (m->busy || (m->oflags & VPO_BUSY)) { + if (vm_page_busied(m)) { vm_page_unlock(m); goto unlock_and_continue; } @@ -1248,8 +1284,8 @@ relock_queues: vm_pagequeue_lock(pq); queues_locked = TRUE; } - next = TAILQ_NEXT(&marker, pageq); - TAILQ_REMOVE(&pq->pq_pl, &marker, pageq); + next = TAILQ_NEXT(&vmd->vmd_marker, plinks.q); + TAILQ_REMOVE(&pq->pq_pl, &vmd->vmd_marker, plinks.q); } vm_pagequeue_unlock(pq); @@ -1257,25 +1293,33 @@ relock_queues: * Compute the number of pages we want to try to move from the * active queue to the inactive queue. */ + pq = &vmd->vmd_pagequeues[PQ_ACTIVE]; + vm_pagequeue_lock(pq); + pcount = pq->pq_cnt; page_shortage = vm_paging_target() + - cnt.v_inactive_target - cnt.v_inactive_count; + cnt.v_inactive_target - cnt.v_inactive_count; page_shortage += addl_page_shortage; + /* + * If we're just idle polling attempt to visit every + * active page within 'update_period' seconds. + */ + if (pass == 0 && vm_pageout_update_period != 0) { + pcount /= vm_pageout_update_period; + page_shortage = pcount; + } /* * Scan the active queue for things we can deactivate. We nominally * track the per-page activity counter and use it to locate * deactivation candidates. */ - pcount = cnt.v_active_count; - pq = &vm_pagequeues[PQ_ACTIVE]; - vm_pagequeue_lock(pq); m = TAILQ_FIRST(&pq->pq_pl); while ((m != NULL) && (pcount-- > 0) && (page_shortage > 0)) { KASSERT(m->queue == PQ_ACTIVE, ("vm_pageout_scan: page %p isn't active", m)); - next = TAILQ_NEXT(m, pageq); + next = TAILQ_NEXT(m, plinks.q); if ((m->flags & PG_MARKER) != 0) { m = next; continue; @@ -1289,27 +1333,6 @@ relock_queues: m = next; continue; } - object = m->object; - if (!VM_OBJECT_TRYWLOCK(object) && - !vm_pageout_fallback_object_lock(m, &next)) { - VM_OBJECT_WUNLOCK(object); - vm_page_unlock(m); - m = next; - continue; - } - - /* - * Don't deactivate pages that are busy. - */ - if ((m->busy != 0) || - (m->oflags & VPO_BUSY) || - (m->hold_count != 0)) { - vm_page_unlock(m); - VM_OBJECT_WUNLOCK(object); - vm_page_requeue_locked(m); - m = next; - continue; - } /* * The count for pagedaemon pages is done after checking the @@ -1325,7 +1348,15 @@ relock_queues: vm_page_aflag_clear(m, PGA_REFERENCED); act_delta += 1; } - if (object->ref_count != 0) + /* + * Unlocked object ref count check. Two races are possible. + * 1) The ref was transitioning to zero and we saw non-zero, + * the pmap bits will be checked unnecessarily. + * 2) The ref was transitioning to one and we saw zero. + * The page lock prevents a new reference to this page so + * we need not check the reference bits. + */ + if (m->object->ref_count != 0) act_delta += pmap_ts_referenced(m); /* @@ -1345,9 +1376,6 @@ relock_queues: * queue depending on usage. */ if (act_delta == 0) { - KASSERT(object->ref_count != 0 || - !pmap_page_is_mapped(m), - ("vm_pageout_scan: page %p is mapped", m)); /* Dequeue to avoid later lock recursion. */ vm_page_dequeue_locked(m); vm_page_deactivate(m); @@ -1355,7 +1383,6 @@ relock_queues: } else vm_page_requeue_locked(m); vm_page_unlock(m); - VM_OBJECT_WUNLOCK(object); m = next; } vm_pagequeue_unlock(pq); @@ -1393,12 +1420,54 @@ relock_queues: * chance to flush out dirty vnode-backed pages and to allow * active pages to be moved to the inactive queue and reclaimed. */ - if (pass != 0 && - ((swap_pager_avail < 64 && vm_page_count_min()) || - (swap_pager_full && vm_paging_target() > 0))) - vm_pageout_oom(VM_OOM_MEM); + vm_pageout_mightbe_oom(vmd, pass); } +static int vm_pageout_oom_vote; + +/* + * The pagedaemon threads randlomly select one to perform the + * OOM. Trying to kill processes before all pagedaemons + * failed to reach free target is premature. + */ +static void +vm_pageout_mightbe_oom(struct vm_domain *vmd, int pass) +{ + int old_vote; + + if (pass <= 1 || !((swap_pager_avail < 64 && vm_page_count_min()) || + (swap_pager_full && vm_paging_target() > 0))) { + if (vmd->vmd_oom) { + vmd->vmd_oom = FALSE; + atomic_subtract_int(&vm_pageout_oom_vote, 1); + } + return; + } + + if (vmd->vmd_oom) + return; + + vmd->vmd_oom = TRUE; + old_vote = atomic_fetchadd_int(&vm_pageout_oom_vote, 1); + if (old_vote != vm_ndomains - 1) + return; + + /* + * The current pagedaemon thread is the last in the quorum to + * start OOM. Initiate the selection and signaling of the + * victim. + */ + vm_pageout_oom(VM_OOM_MEM); + + /* + * After one round of OOM terror, recall our vote. On the + * next pass, current pagedaemon would vote again if the low + * memory condition is still there, due to vmd_oom being + * false. + */ + vmd->vmd_oom = FALSE; + atomic_subtract_int(&vm_pageout_oom_vote, 1); +} void vm_pageout_oom(int shortage) @@ -1494,121 +1563,67 @@ vm_pageout_oom(int shortage) } } -/* - * This routine tries to maintain the pseudo LRU active queue, - * so that during long periods of time where there is no paging, - * that some statistic accumulation still occurs. This code - * helps the situation where paging just starts to occur. - */ static void -vm_pageout_page_stats(void) +vm_pageout_worker(void *arg) { - struct vm_pagequeue *pq; - vm_object_t object; - vm_page_t m, next; - int pcount, tpcount; /* Number of pages to check */ - static int fullintervalcount = 0; - int page_shortage; + struct vm_domain *domain; + int domidx; - page_shortage = - (cnt.v_inactive_target + cnt.v_cache_max + cnt.v_free_min) - - (cnt.v_free_count + cnt.v_inactive_count + cnt.v_cache_count); + domidx = (uintptr_t)arg; + domain = &vm_dom[domidx]; - if (page_shortage <= 0) - return; + /* + * XXXKIB It could be useful to bind pageout daemon threads to + * the cores belonging to the domain, from which vm_page_array + * is allocated. + */ - pcount = cnt.v_active_count; - fullintervalcount += vm_pageout_stats_interval; - if (fullintervalcount < vm_pageout_full_stats_interval) { - vm_pageout_stats++; - tpcount = (int64_t)vm_pageout_stats_max * cnt.v_active_count / - cnt.v_page_count; - if (pcount > tpcount) - pcount = tpcount; - } else { - vm_pageout_full_stats++; - fullintervalcount = 0; - } - - pq = &vm_pagequeues[PQ_ACTIVE]; - vm_pagequeue_lock(pq); - m = TAILQ_FIRST(&pq->pq_pl); - while ((m != NULL) && (pcount-- > 0)) { - int actcount; - - KASSERT(m->queue == PQ_ACTIVE, - ("vm_pageout_page_stats: page %p isn't active", m)); - - next = TAILQ_NEXT(m, pageq); - if ((m->flags & PG_MARKER) != 0) { - m = next; - continue; - } - vm_page_lock_assert(m, MA_NOTOWNED); - if (!vm_pageout_page_lock(m, &next)) { - vm_page_unlock(m); - m = next; - continue; - } - object = m->object; - if (!VM_OBJECT_TRYWLOCK(object) && - !vm_pageout_fallback_object_lock(m, &next)) { - VM_OBJECT_WUNLOCK(object); - vm_page_unlock(m); - m = next; - continue; - } + KASSERT(domain->vmd_segs != 0, ("domain without segments")); + vm_pageout_init_marker(&domain->vmd_marker, PQ_INACTIVE); + /* + * The pageout daemon worker is never done, so loop forever. + */ + while (TRUE) { /* - * Don't deactivate pages that are busy. + * If we have enough free memory, wakeup waiters. Do + * not clear vm_pages_needed until we reach our target, + * otherwise we may be woken up over and over again and + * waste a lot of cpu. */ - if ((m->busy != 0) || - (m->oflags & VPO_BUSY) || - (m->hold_count != 0)) { - vm_page_unlock(m); - VM_OBJECT_WUNLOCK(object); - vm_page_requeue_locked(m); - m = next; - continue; + mtx_lock(&vm_page_queue_free_mtx); + if (vm_pages_needed && !vm_page_count_min()) { + if (!vm_paging_needed()) + vm_pages_needed = 0; + wakeup(&cnt.v_free_count); } - - actcount = 0; - if (m->aflags & PGA_REFERENCED) { - vm_page_aflag_clear(m, PGA_REFERENCED); - actcount += 1; - } - - actcount += pmap_ts_referenced(m); - if (actcount) { - m->act_count += ACT_ADVANCE + actcount; - if (m->act_count > ACT_MAX) - m->act_count = ACT_MAX; - vm_page_requeue_locked(m); + if (vm_pages_needed) { + /* + * Still not done, take a second pass without waiting + * (unlimited dirty cleaning), otherwise sleep a bit + * and try again. + */ + if (domain->vmd_pass > 1) + msleep(&vm_pages_needed, + &vm_page_queue_free_mtx, PVM, "psleep", + hz / 2); } else { - if (m->act_count == 0) { - /* - * We turn off page access, so that we have - * more accurate RSS stats. We don't do this - * in the normal page deactivation when the - * system is loaded VM wise, because the - * cost of the large number of page protect - * operations would be higher than the value - * of doing the operation. - */ - pmap_remove_all(m); - /* Dequeue to avoid later lock recursion. */ - vm_page_dequeue_locked(m); - vm_page_deactivate(m); - } else { - m->act_count -= min(m->act_count, ACT_DECLINE); - vm_page_requeue_locked(m); - } + /* + * Good enough, sleep until required to refresh + * stats. + */ + domain->vmd_pass = 0; + msleep(&vm_pages_needed, &vm_page_queue_free_mtx, + PVM, "psleep", hz); + } - vm_page_unlock(m); - VM_OBJECT_WUNLOCK(object); - m = next; + if (vm_pages_needed) { + cnt.v_pdwakeups++; + domain->vmd_pass++; + } + mtx_unlock(&vm_page_queue_free_mtx); + vm_pageout_scan(domain, domain->vmd_pass); } - vm_pagequeue_unlock(pq); } /* @@ -1617,7 +1632,9 @@ vm_pageout_page_stats(void) static void vm_pageout(void) { - int error, pass; +#if MAXMEMDOM > 1 + int error, i; +#endif /* * Initialize some paging parameters. @@ -1640,105 +1657,43 @@ vm_pageout(void) cnt.v_free_reserved = vm_pageout_page_count + cnt.v_pageout_free_min + (cnt.v_page_count / 768); cnt.v_free_severe = cnt.v_free_min / 2; + cnt.v_free_target = 4 * cnt.v_free_min + cnt.v_free_reserved; cnt.v_free_min += cnt.v_free_reserved; cnt.v_free_severe += cnt.v_free_reserved; - - /* - * v_free_target and v_cache_min control pageout hysteresis. Note - * that these are more a measure of the VM cache queue hysteresis - * then the VM free queue. Specifically, v_free_target is the - * high water mark (free+cache pages). - * - * v_free_reserved + v_cache_min (mostly means v_cache_min) is the - * low water mark, while v_free_min is the stop. v_cache_min must - * be big enough to handle memory needs while the pageout daemon - * is signalled and run to free more pages. - */ - if (cnt.v_free_count > 6144) - cnt.v_free_target = 4 * cnt.v_free_min + cnt.v_free_reserved; - else - cnt.v_free_target = 2 * cnt.v_free_min + cnt.v_free_reserved; - - if (cnt.v_free_count > 2048) { - cnt.v_cache_min = cnt.v_free_target; - cnt.v_cache_max = 2 * cnt.v_cache_min; - cnt.v_inactive_target = (3 * cnt.v_free_target) / 2; - } else { - cnt.v_cache_min = 0; - cnt.v_cache_max = 0; - cnt.v_inactive_target = cnt.v_free_count / 4; - } + cnt.v_inactive_target = (3 * cnt.v_free_target) / 2; if (cnt.v_inactive_target > cnt.v_free_count / 3) cnt.v_inactive_target = cnt.v_free_count / 3; + /* + * Set the default wakeup threshold to be 10% above the minimum + * page limit. This keeps the steady state out of shortfall. + */ + vm_pageout_wakeup_thresh = (cnt.v_free_min / 10) * 11; + + /* + * Set interval in seconds for active scan. We want to visit each + * page at least once every ten minutes. This is to prevent worst + * case paging behaviors with stale active LRU. + */ + if (vm_pageout_update_period == 0) + vm_pageout_update_period = 600; + /* XXX does not really belong here */ if (vm_page_max_wired == 0) vm_page_max_wired = cnt.v_free_count / 3; - if (vm_pageout_stats_max == 0) - vm_pageout_stats_max = cnt.v_free_target; - - /* - * Set interval in seconds for stats scan. - */ - if (vm_pageout_stats_interval == 0) - vm_pageout_stats_interval = 5; - if (vm_pageout_full_stats_interval == 0) - vm_pageout_full_stats_interval = vm_pageout_stats_interval * 4; - swap_pager_swap_init(); - pass = 0; - /* - * The pageout daemon is never done, so loop forever. - */ - while (TRUE) { - /* - * If we have enough free memory, wakeup waiters. Do - * not clear vm_pages_needed until we reach our target, - * otherwise we may be woken up over and over again and - * waste a lot of cpu. - */ - mtx_lock(&vm_page_queue_free_mtx); - if (vm_pages_needed && !vm_page_count_min()) { - if (!vm_paging_needed()) - vm_pages_needed = 0; - wakeup(&cnt.v_free_count); +#if MAXMEMDOM > 1 + for (i = 1; i < vm_ndomains; i++) { + error = kthread_add(vm_pageout_worker, (void *)(uintptr_t)i, + curproc, NULL, 0, 0, "dom%d", i); + if (error != 0) { + panic("starting pageout for domain %d, error %d\n", + i, error); } - if (vm_pages_needed) { - /* - * Still not done, take a second pass without waiting - * (unlimited dirty cleaning), otherwise sleep a bit - * and try again. - */ - ++pass; - if (pass > 1) - msleep(&vm_pages_needed, - &vm_page_queue_free_mtx, PVM, "psleep", - hz / 2); - } else { - /* - * Good enough, sleep & handle stats. Prime the pass - * for the next run. - */ - if (pass > 1) - pass = 1; - else - pass = 0; - error = msleep(&vm_pages_needed, - &vm_page_queue_free_mtx, PVM, "psleep", - vm_pageout_stats_interval * hz); - if (error && !vm_pages_needed) { - mtx_unlock(&vm_page_queue_free_mtx); - pass = 0; - vm_pageout_page_stats(); - continue; - } - } - if (vm_pages_needed) - cnt.v_pdwakeups++; - mtx_unlock(&vm_page_queue_free_mtx); - vm_pageout_scan(pass); } +#endif + vm_pageout_worker((uintptr_t)0); } /* diff --git a/sys/vm/vm_param.h b/sys/vm/vm_param.h index 22401fd8058..d19d1b9a2c9 100644 --- a/sys/vm/vm_param.h +++ b/sys/vm/vm_param.h @@ -86,21 +86,6 @@ #define VM_SWAPPING_ENABLED 11 /* swapping enabled */ #define VM_MAXID 12 /* number of valid vm ids */ -#define CTL_VM_NAMES { \ - { 0, 0 }, \ - { "vmtotal", CTLTYPE_STRUCT }, \ - { "loadavg", CTLTYPE_STRUCT }, \ - { "v_free_min", CTLTYPE_UINT }, \ - { "v_free_target", CTLTYPE_UINT }, \ - { "v_free_reserved", CTLTYPE_UINT }, \ - { "v_inactive_target", CTLTYPE_UINT }, \ - { "v_cache_min", CTLTYPE_UINT }, \ - { "v_cache_max", CTLTYPE_UINT }, \ - { "v_pageout_free_min", CTLTYPE_UINT}, \ - { "obsolete_pageout_algorithm", CTLTYPE_INT}, \ - { "swap_enabled", CTLTYPE_INT},\ -} - /* * Structure for swap device statistics */ diff --git a/sys/vm/vm_phys.c b/sys/vm/vm_phys.c index 66f3b0c540c..ce6dfd3c161 100644 --- a/sys/vm/vm_phys.c +++ b/sys/vm/vm_phys.c @@ -65,26 +65,15 @@ __FBSDID("$FreeBSD$"); #include #include -struct vm_freelist { - struct pglist pl; - int lcnt; -}; - -struct vm_phys_seg { - vm_paddr_t start; - vm_paddr_t end; - vm_page_t first_page; - int domain; - struct vm_freelist (*free_queues)[VM_NFREEPOOL][VM_NFREEORDER]; -}; +_Static_assert(sizeof(long) * NBBY >= VM_PHYSSEG_MAX, + "Too many physsegs."); struct mem_affinity *mem_affinity; int vm_ndomains = 1; -static struct vm_phys_seg vm_phys_segs[VM_PHYSSEG_MAX]; - -static int vm_phys_nsegs; +struct vm_phys_seg vm_phys_segs[VM_PHYSSEG_MAX]; +int vm_phys_nsegs; #define VM_PHYS_FICTITIOUS_NSEGS 8 static struct vm_phys_fictitious_seg { @@ -93,7 +82,7 @@ static struct vm_phys_fictitious_seg { vm_page_t first_page; } vm_phys_fictitious_segs[VM_PHYS_FICTITIOUS_NSEGS]; static struct mtx vm_phys_fictitious_reg_mtx; -MALLOC_DEFINE(M_FICT_PAGES, "", ""); +MALLOC_DEFINE(M_FICT_PAGES, "vm_fictitious", "Fictitious VM pages"); static struct vm_freelist vm_phys_free_queues[MAXMEMDOM][VM_NFREELIST][VM_NFREEPOOL][VM_NFREEORDER]; @@ -140,6 +129,22 @@ vm_rr_selectdomain(void) #endif } +boolean_t +vm_phys_domain_intersects(long mask, vm_paddr_t low, vm_paddr_t high) +{ + struct vm_phys_seg *s; + int idx; + + while ((idx = ffsl(mask)) != 0) { + idx--; /* ffsl counts from 1 */ + mask &= ~(1UL << idx); + s = &vm_phys_segs[idx]; + if (low < s->end && high > s->start) + return (TRUE); + } + return (FALSE); +} + /* * Outputs the state of the physical memory allocator, specifically, * the amount of physical memory in each free list. @@ -221,9 +226,9 @@ vm_freelist_add(struct vm_freelist *fl, vm_page_t m, int order, int tail) m->order = order; if (tail) - TAILQ_INSERT_TAIL(&fl[order].pl, m, pageq); + TAILQ_INSERT_TAIL(&fl[order].pl, m, plinks.q); else - TAILQ_INSERT_HEAD(&fl[order].pl, m, pageq); + TAILQ_INSERT_HEAD(&fl[order].pl, m, plinks.q); fl[order].lcnt++; } @@ -231,7 +236,7 @@ static void vm_freelist_rem(struct vm_freelist *fl, vm_page_t m, int order) { - TAILQ_REMOVE(&fl[order].pl, m, pageq); + TAILQ_REMOVE(&fl[order].pl, m, plinks.q); fl[order].lcnt--; m->order = VM_NFREEORDER; } @@ -378,12 +383,16 @@ void vm_phys_add_page(vm_paddr_t pa) { vm_page_t m; + struct vm_domain *vmd; cnt.v_page_count++; m = vm_phys_paddr_to_vm_page(pa); m->phys_addr = pa; m->queue = PQ_NONE; m->segind = vm_phys_paddr_to_segind(pa); + vmd = vm_phys_domain(m); + vmd->vmd_page_count++; + vmd->vmd_segs |= 1UL << m->segind; m->flags = PG_FREE; KASSERT(m->order == VM_NFREEORDER, ("vm_phys_add_page: page %p has unexpected order %d", @@ -391,7 +400,7 @@ vm_phys_add_page(vm_paddr_t pa) m->pool = VM_FREEPOOL_DEFAULT; pmap_page_init(m); mtx_lock(&vm_page_queue_free_mtx); - cnt.v_free_count++; + vm_phys_freecnt_adj(m, 1); vm_phys_free_pages(m, 0); mtx_unlock(&vm_page_queue_free_mtx); } @@ -559,7 +568,8 @@ vm_phys_fictitious_reg_range(vm_paddr_t start, vm_paddr_t end, } for (i = 0; i < page_count; i++) { vm_page_initfake(&fp[i], start + PAGE_SIZE * i, memattr); - fp[i].oflags &= ~(VPO_BUSY | VPO_UNMANAGED); + fp[i].oflags &= ~VPO_UNMANAGED; + fp[i].busy_lock = VPB_UNBUSIED; } mtx_lock(&vm_phys_fictitious_reg_mtx); for (segind = 0; segind < VM_PHYS_FICTITIOUS_NSEGS; segind++) { @@ -809,16 +819,16 @@ vm_phys_zero_pages_idle(void) fl = vm_phys_free_queues[domain][0][0]; mtx_assert(&vm_page_queue_free_mtx, MA_OWNED); for (;;) { - TAILQ_FOREACH_REVERSE(m, &fl[oind].pl, pglist, pageq) { + TAILQ_FOREACH_REVERSE(m, &fl[oind].pl, pglist, plinks.q) { for (m_tmp = m; m_tmp < &m[1 << oind]; m_tmp++) { if ((m_tmp->flags & (PG_CACHED | PG_ZERO)) == 0) { vm_phys_unfree_page(m_tmp); - cnt.v_free_count--; + vm_phys_freecnt_adj(m, -1); mtx_unlock(&vm_page_queue_free_mtx); pmap_zero_page_idle(m_tmp); m_tmp->flags |= PG_ZERO; mtx_lock(&vm_page_queue_free_mtx); - cnt.v_free_count++; + vm_phys_freecnt_adj(m, 1); vm_phys_free_pages(m_tmp, 0); vm_page_zero_count++; cnt_prezero++; @@ -879,7 +889,7 @@ restartdom: for (oind = min(order, VM_NFREEORDER - 1); oind < VM_NFREEORDER; oind++) { for (pind = 0; pind < VM_NFREEPOOL; pind++) { fl = &vm_phys_free_queues[domain][flind][pind][0]; - TAILQ_FOREACH(m_ret, &fl[oind].pl, pageq) { + TAILQ_FOREACH(m_ret, &fl[oind].pl, plinks.q) { /* * A free list may contain physical pages * from one or more segments. diff --git a/sys/vm/vm_phys.h b/sys/vm/vm_phys.h index 98128165ffd..f39943cd3d5 100644 --- a/sys/vm/vm_phys.h +++ b/sys/vm/vm_phys.h @@ -47,8 +47,23 @@ struct mem_affinity { int domain; }; +struct vm_freelist { + struct pglist pl; + int lcnt; +}; + +struct vm_phys_seg { + vm_paddr_t start; + vm_paddr_t end; + vm_page_t first_page; + int domain; + struct vm_freelist (*free_queues)[VM_NFREEPOOL][VM_NFREEORDER]; +}; + extern struct mem_affinity *mem_affinity; extern int vm_ndomains; +extern struct vm_phys_seg vm_phys_segs[]; +extern int vm_phys_nsegs; /* * The following functions are only to be used by the virtual memory system. @@ -58,6 +73,7 @@ vm_page_t vm_phys_alloc_contig(u_long npages, vm_paddr_t low, vm_paddr_t high, u_long alignment, vm_paddr_t boundary); vm_page_t vm_phys_alloc_freelist_pages(int flind, int pool, int order); vm_page_t vm_phys_alloc_pages(int pool, int order); +boolean_t vm_phys_domain_intersects(long mask, vm_paddr_t low, vm_paddr_t high); int vm_phys_fictitious_reg_range(vm_paddr_t start, vm_paddr_t end, vm_memattr_t memattr); void vm_phys_fictitious_unreg_range(vm_paddr_t start, vm_paddr_t end); @@ -70,5 +86,36 @@ void vm_phys_set_pool(int pool, vm_page_t m, int order); boolean_t vm_phys_unfree_page(vm_page_t m); boolean_t vm_phys_zero_pages_idle(void); +/* + * vm_phys_domain: + * + * Return the memory domain the page belongs to. + */ +static inline struct vm_domain * +vm_phys_domain(vm_page_t m) +{ +#if MAXMEMDOM > 1 + int domn, segind; + + /* XXXKIB try to assert that the page is managed */ + segind = m->segind; + KASSERT(segind < vm_phys_nsegs, ("segind %d m %p", segind, m)); + domn = vm_phys_segs[segind].domain; + KASSERT(domn < vm_ndomains, ("domain %d m %p", domn, m)); + return (&vm_dom[domn]); +#else + return (&vm_dom[0]); +#endif +} + +static inline void +vm_phys_freecnt_adj(vm_page_t m, int adj) +{ + + mtx_assert(&vm_page_queue_free_mtx, MA_OWNED); + cnt.v_free_count += adj; + vm_phys_domain(m)->vmd_free_count += adj; +} + #endif /* _KERNEL */ #endif /* !_VM_PHYS_H_ */ diff --git a/sys/vm/vm_radix.c b/sys/vm/vm_radix.c index 113a2269fe6..ff311b2d5b5 100644 --- a/sys/vm/vm_radix.c +++ b/sys/vm/vm_radix.c @@ -103,30 +103,16 @@ struct vm_radix_node { static uma_zone_t vm_radix_node_zone; /* - * Allocate a radix node. Pre-allocation should ensure that the request - * will always be satisfied. + * Allocate a radix node. */ static __inline struct vm_radix_node * vm_radix_node_get(vm_pindex_t owner, uint16_t count, uint16_t clevel) { struct vm_radix_node *rnode; - rnode = uma_zalloc(vm_radix_node_zone, M_NOWAIT); - - /* - * The required number of nodes should already be pre-allocated - * by vm_radix_prealloc(). However, UMA can hold a few nodes - * in per-CPU buckets, which will not be accessible by the - * current CPU. Thus, the allocation could return NULL when - * the pre-allocated pool is close to exhaustion. Anyway, - * in practice this should never occur because a new node - * is not always required for insert. Thus, the pre-allocated - * pool should have some extra pages that prevent this from - * becoming a problem. - */ + rnode = uma_zalloc(vm_radix_node_zone, M_NOWAIT | M_ZERO); if (rnode == NULL) - panic("%s: uma_zalloc() returned NULL for a new node", - __func__); + return (NULL); rnode->rn_owner = owner; rnode->rn_count = count; rnode->rn_clev = clevel; @@ -295,39 +281,30 @@ vm_radix_node_zone_dtor(void *mem, int size __unused, void *arg __unused) } #endif +#ifndef UMA_MD_SMALL_ALLOC /* - * Radix node zone initializer. - */ -static int -vm_radix_node_zone_init(void *mem, int size __unused, int flags __unused) -{ - struct vm_radix_node *rnode; - - rnode = mem; - memset(rnode->rn_child, 0, sizeof(rnode->rn_child)); - return (0); -} - -/* - * Pre-allocate intermediate nodes from the UMA slab zone. + * Reserve the KVA necessary to satisfy the node allocation. + * This is mandatory in architectures not supporting direct + * mapping as they will need otherwise to carve into the kernel maps for + * every node allocation, resulting into deadlocks for consumers already + * working with kernel maps. */ static void -vm_radix_prealloc(void *arg __unused) +vm_radix_reserve_kva(void *arg __unused) { - int nodes; /* * Calculate the number of reserved nodes, discounting the pages that * are needed to store them. */ - nodes = ((vm_paddr_t)cnt.v_page_count * PAGE_SIZE) / (PAGE_SIZE + - sizeof(struct vm_radix_node)); - if (!uma_zone_reserve_kva(vm_radix_node_zone, nodes)) - panic("%s: unable to create new zone", __func__); - uma_prealloc(vm_radix_node_zone, nodes); + if (!uma_zone_reserve_kva(vm_radix_node_zone, + ((vm_paddr_t)cnt.v_page_count * PAGE_SIZE) / (PAGE_SIZE + + sizeof(struct vm_radix_node)))) + panic("%s: unable to reserve KVA", __func__); } -SYSINIT(vm_radix_prealloc, SI_SUB_KMEM, SI_ORDER_SECOND, vm_radix_prealloc, - NULL); +SYSINIT(vm_radix_reserve_kva, SI_SUB_KMEM, SI_ORDER_SECOND, + vm_radix_reserve_kva, NULL); +#endif /* * Initialize the UMA slab zone. @@ -345,15 +322,14 @@ vm_radix_init(void) #else NULL, #endif - vm_radix_node_zone_init, NULL, VM_RADIX_PAD, UMA_ZONE_VM | - UMA_ZONE_NOFREE); + NULL, NULL, VM_RADIX_PAD, UMA_ZONE_VM); } /* * Inserts the key-value pair into the trie. * Panics if the key already exists. */ -void +int vm_radix_insert(struct vm_radix *rtree, vm_page_t page) { vm_pindex_t index, newind; @@ -365,6 +341,8 @@ vm_radix_insert(struct vm_radix *rtree, vm_page_t page) index = page->pindex; +restart: + /* * The owner of record for root is not really important because it * will never be used. @@ -372,7 +350,7 @@ vm_radix_insert(struct vm_radix *rtree, vm_page_t page) rnode = vm_radix_getroot(rtree); if (rnode == NULL) { rtree->rt_root = (uintptr_t)page | VM_RADIX_ISLEAF; - return; + return (0); } parentp = (void **)&rtree->rt_root; for (;;) { @@ -382,19 +360,43 @@ vm_radix_insert(struct vm_radix *rtree, vm_page_t page) panic("%s: key %jx is already present", __func__, (uintmax_t)index); clev = vm_radix_keydiff(m->pindex, index); + + /* + * During node allocation the trie that is being + * walked can be modified because of recursing radix + * trie operations. + * If this is the case, the recursing functions signal + * such situation and the insert operation must + * start from scratch again. + * The freed radix node will then be in the UMA + * caches very likely to avoid the same situation + * to happen. + */ + rtree->rt_flags |= RT_INSERT_INPROG; tmp = vm_radix_node_get(vm_radix_trimkey(index, clev + 1), 2, clev); + rtree->rt_flags &= ~RT_INSERT_INPROG; + if (tmp == NULL) { + rtree->rt_flags &= ~RT_TRIE_MODIFIED; + return (ENOMEM); + } + if ((rtree->rt_flags & RT_TRIE_MODIFIED) != 0) { + rtree->rt_flags &= ~RT_TRIE_MODIFIED; + tmp->rn_count = 0; + vm_radix_node_put(tmp); + goto restart; + } *parentp = tmp; vm_radix_addpage(tmp, index, clev, page); vm_radix_addpage(tmp, m->pindex, clev, m); - return; + return (0); } else if (vm_radix_keybarr(rnode, index)) break; slot = vm_radix_slot(index, rnode->rn_clev); if (rnode->rn_child[slot] == NULL) { rnode->rn_count++; vm_radix_addpage(rnode, index, rnode->rn_clev, page); - return; + return (0); } parentp = &rnode->rn_child[slot]; rnode = rnode->rn_child[slot]; @@ -407,12 +409,41 @@ vm_radix_insert(struct vm_radix *rtree, vm_page_t page) */ newind = rnode->rn_owner; clev = vm_radix_keydiff(newind, index); - tmp = vm_radix_node_get(vm_radix_trimkey(index, clev + 1), 2, - clev); + + /* See the comments above. */ + rtree->rt_flags |= RT_INSERT_INPROG; + tmp = vm_radix_node_get(vm_radix_trimkey(index, clev + 1), 2, clev); + rtree->rt_flags &= ~RT_INSERT_INPROG; + if (tmp == NULL) { + rtree->rt_flags &= ~RT_TRIE_MODIFIED; + return (ENOMEM); + } + if ((rtree->rt_flags & RT_TRIE_MODIFIED) != 0) { + rtree->rt_flags &= ~RT_TRIE_MODIFIED; + tmp->rn_count = 0; + vm_radix_node_put(tmp); + goto restart; + } *parentp = tmp; vm_radix_addpage(tmp, index, clev, page); slot = vm_radix_slot(newind, clev); tmp->rn_child[slot] = rnode; + return (0); +} + +/* + * Returns TRUE if the specified radix tree contains a single leaf and FALSE + * otherwise. + */ +boolean_t +vm_radix_is_singleton(struct vm_radix *rtree) +{ + struct vm_radix_node *rnode; + + rnode = vm_radix_getroot(rtree); + if (rnode == NULL) + return (FALSE); + return (vm_radix_isleaf(rnode)); } /* @@ -677,6 +708,20 @@ vm_radix_remove(struct vm_radix *rtree, vm_pindex_t index) vm_page_t m; int i, slot; + /* + * Detect if a page is going to be removed from a trie which is + * already undergoing another trie operation. + * Right now this is only possible for vm_radix_remove() recursing + * into vm_radix_insert(). + * If this is the case, the caller must be notified about this + * situation. It will also takecare to update the RT_TRIE_MODIFIED + * accordingly. + * The RT_TRIE_MODIFIED bit is set here because the remove operation + * will always succeed. + */ + if ((rtree->rt_flags & RT_INSERT_INPROG) != 0) + rtree->rt_flags |= RT_TRIE_MODIFIED; + rnode = vm_radix_getroot(rtree); if (vm_radix_isleaf(rnode)) { m = vm_radix_topage(rnode); @@ -731,6 +776,9 @@ vm_radix_reclaim_allnodes(struct vm_radix *rtree) { struct vm_radix_node *root; + KASSERT((rtree->rt_flags & RT_INSERT_INPROG) == 0, + ("vm_radix_reclaim_allnodes: unexpected trie recursion")); + root = vm_radix_getroot(rtree); if (root == NULL) return; @@ -739,6 +787,51 @@ vm_radix_reclaim_allnodes(struct vm_radix *rtree) vm_radix_reclaim_allnodes_int(root); } +/* + * Replace an existing page into the trie with another one. + * Panics if the replacing page is not present or if the new page has an + * invalid key. + */ +vm_page_t +vm_radix_replace(struct vm_radix *rtree, vm_page_t newpage, vm_pindex_t index) +{ + struct vm_radix_node *rnode; + vm_page_t m; + int slot; + + KASSERT(newpage->pindex == index, ("%s: newpage index invalid", + __func__)); + + rnode = vm_radix_getroot(rtree); + if (rnode == NULL) + panic("%s: replacing page on an empty trie", __func__); + if (vm_radix_isleaf(rnode)) { + m = vm_radix_topage(rnode); + if (m->pindex != index) + panic("%s: original replacing root key not found", + __func__); + rtree->rt_root = (uintptr_t)newpage | VM_RADIX_ISLEAF; + return (m); + } + for (;;) { + slot = vm_radix_slot(index, rnode->rn_clev); + if (vm_radix_isleaf(rnode->rn_child[slot])) { + m = vm_radix_topage(rnode->rn_child[slot]); + if (m->pindex == index) { + rnode->rn_child[slot] = + (void *)((uintptr_t)newpage | + VM_RADIX_ISLEAF); + return (m); + } else + break; + } else if (rnode->rn_child[slot] == NULL || + vm_radix_keybarr(rnode->rn_child[slot], index)) + break; + rnode = rnode->rn_child[slot]; + } + panic("%s: original replacing page not found", __func__); +} + #ifdef DDB /* * Show details about the given radix node. diff --git a/sys/vm/vm_radix.h b/sys/vm/vm_radix.h index 341425371ee..4491860027b 100644 --- a/sys/vm/vm_radix.h +++ b/sys/vm/vm_radix.h @@ -36,12 +36,15 @@ #ifdef _KERNEL void vm_radix_init(void); -void vm_radix_insert(struct vm_radix *rtree, vm_page_t page); +int vm_radix_insert(struct vm_radix *rtree, vm_page_t page); +boolean_t vm_radix_is_singleton(struct vm_radix *rtree); vm_page_t vm_radix_lookup(struct vm_radix *rtree, vm_pindex_t index); vm_page_t vm_radix_lookup_ge(struct vm_radix *rtree, vm_pindex_t index); vm_page_t vm_radix_lookup_le(struct vm_radix *rtree, vm_pindex_t index); void vm_radix_reclaim_allnodes(struct vm_radix *rtree); void vm_radix_remove(struct vm_radix *rtree, vm_pindex_t index); +vm_page_t vm_radix_replace(struct vm_radix *rtree, vm_page_t newpage, + vm_pindex_t index); #endif /* _KERNEL */ #endif /* !_VM_RADIX_H_ */ diff --git a/sys/vm/vm_reserv.c b/sys/vm/vm_reserv.c index c264a7644f7..6ca5642807f 100644 --- a/sys/vm/vm_reserv.c +++ b/sys/vm/vm_reserv.c @@ -502,8 +502,7 @@ vm_reserv_alloc_page(vm_object_t object, vm_pindex_t pindex, vm_page_t mpred) * Look for an existing reservation. */ if (mpred != NULL) { - KASSERT(mpred->object == object || - (mpred->flags & PG_SLAB) != 0, + KASSERT(mpred->object == object, ("vm_reserv_alloc_page: object doesn't contain mpred")); KASSERT(mpred->pindex < pindex, ("vm_reserv_alloc_page: mpred doesn't precede pindex")); diff --git a/sys/vm/vm_zeroidle.c b/sys/vm/vm_zeroidle.c index 6ba96e1c006..8c191c0d529 100644 --- a/sys/vm/vm_zeroidle.c +++ b/sys/vm/vm_zeroidle.c @@ -50,6 +50,7 @@ __FBSDID("$FreeBSD$"); #include #include +#include #include #include diff --git a/sys/vm/vnode_pager.c b/sys/vm/vnode_pager.c index 4c7da16261e..2e3d2c0095b 100644 --- a/sys/vm/vnode_pager.c +++ b/sys/vm/vnode_pager.c @@ -1135,8 +1135,7 @@ vnode_pager_generic_putpages(struct vnode *vp, vm_page_t *ma, int bytecount, * pmap operation. */ m = ma[ncount - 1]; - KASSERT(m->busy > 0, - ("vnode_pager_generic_putpages: page %p is not busy", m)); + vm_page_assert_sbusied(m); KASSERT(!pmap_page_is_write_mapped(m), ("vnode_pager_generic_putpages: page %p is not read-only", m)); vm_page_clear_dirty(m, pgoff, PAGE_SIZE - diff --git a/sys/x86/acpica/srat.c b/sys/x86/acpica/srat.c index 7ea715e6397..8b5082c9847 100644 --- a/sys/x86/acpica/srat.c +++ b/sys/x86/acpica/srat.c @@ -31,10 +31,14 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include +#include #include +#include #include #include #include +#include #include #include diff --git a/sys/x86/include/acpica_machdep.h b/sys/x86/include/acpica_machdep.h new file mode 100644 index 00000000000..47437868589 --- /dev/null +++ b/sys/x86/include/acpica_machdep.h @@ -0,0 +1,80 @@ +/*- + * Copyright (c) 2002 Mitsuru IWASAKI + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + * $FreeBSD$ + */ + +/****************************************************************************** + * + * Name: acpica_machdep.h - arch-specific defines, etc. + * $Revision$ + * + *****************************************************************************/ + +#ifndef __ACPICA_MACHDEP_H__ +#define __ACPICA_MACHDEP_H__ + +#ifdef _KERNEL +/* + * Calling conventions: + * + * ACPI_SYSTEM_XFACE - Interfaces to host OS (handlers, threads) + * ACPI_EXTERNAL_XFACE - External ACPI interfaces + * ACPI_INTERNAL_XFACE - Internal ACPI interfaces + * ACPI_INTERNAL_VAR_XFACE - Internal variable-parameter list interfaces + */ +#define ACPI_SYSTEM_XFACE +#define ACPI_EXTERNAL_XFACE +#define ACPI_INTERNAL_XFACE +#define ACPI_INTERNAL_VAR_XFACE + +/* Asm macros */ + +#define ACPI_ASM_MACROS +#define BREAKPOINT3 +#define ACPI_DISABLE_IRQS() disable_intr() +#define ACPI_ENABLE_IRQS() enable_intr() + +#define ACPI_FLUSH_CPU_CACHE() wbinvd() + +/* Section 5.2.10.1: global lock acquire/release functions */ +int acpi_acquire_global_lock(volatile uint32_t *); +int acpi_release_global_lock(volatile uint32_t *); +#define ACPI_ACQUIRE_GLOBAL_LOCK(GLptr, Acq) do { \ + (Acq) = acpi_acquire_global_lock(&((GLptr)->GlobalLock)); \ +} while (0) +#define ACPI_RELEASE_GLOBAL_LOCK(GLptr, Acq) do { \ + (Acq) = acpi_release_global_lock(&((GLptr)->GlobalLock)); \ +} while (0) + +void acpi_SetDefaultIntrModel(int model); +void acpi_cpu_c1(void); +void *acpi_map_table(vm_paddr_t pa, const char *sig); +void acpi_unmap_table(void *table); +vm_paddr_t acpi_find_table(const char *sig); + +#endif /* _KERNEL */ + +#endif /* __ACPICA_MACHDEP_H__ */ diff --git a/sys/x86/include/segments.h b/sys/x86/include/segments.h index 74066ef48e7..0d6a282c3c8 100644 --- a/sys/x86/include/segments.h +++ b/sys/x86/include/segments.h @@ -217,6 +217,7 @@ union descriptor { #define IDT_IO_INTS NRSVIDT /* Base of IDT entries for I/O interrupts. */ #define IDT_SYSCALL 0x80 /* System Call Interrupt Vector */ #define IDT_DTRACE_RET 0x92 /* DTrace pid provider Interrupt Vector */ +#define IDT_EVTCHN 0x93 /* Xen HVM Event Channel Interrupt Vector */ #if defined(__i386__) || defined(__ia64__) /* diff --git a/sys/x86/isa/clock.c b/sys/x86/isa/clock.c index 29ec02d456e..a12e17514a9 100644 --- a/sys/x86/isa/clock.c +++ b/sys/x86/isa/clock.c @@ -469,7 +469,7 @@ i8254_restore(void) if (attimer_sc != NULL) set_i8254_freq(attimer_sc->mode, attimer_sc->period); else - set_i8254_freq(0, 0); + set_i8254_freq(MODE_STOP, 0); } #ifndef __amd64__ @@ -504,7 +504,7 @@ i8254_init(void) if (pc98_machine_type & M_8M) i8254_freq = 1996800L; /* 1.9968 MHz */ #endif - set_i8254_freq(0, 0); + set_i8254_freq(MODE_STOP, 0); } void @@ -539,7 +539,7 @@ sysctl_machdep_i8254_freq(SYSCTL_HANDLER_ARGS) set_i8254_freq(attimer_sc->mode, attimer_sc->period); attimer_sc->tc.tc_frequency = freq; } else { - set_i8254_freq(0, 0); + set_i8254_freq(MODE_STOP, 0); } } return (error); @@ -715,7 +715,7 @@ attimer_attach(device_t dev) i8254_pending = i8254_intsrc->is_pic->pic_source_pending; resource_int_value(device_get_name(dev), device_get_unit(dev), "timecounter", &i8254_timecounter); - set_i8254_freq(0, 0); + set_i8254_freq(MODE_STOP, 0); if (i8254_timecounter) { sc->tc.tc_get_timecount = i8254_get_timecount; sc->tc.tc_counter_mask = 0xffff; diff --git a/sys/x86/x86/busdma_machdep.c b/sys/x86/x86/busdma_machdep.c index a4de15af391..0b5961683f3 100644 --- a/sys/x86/x86/busdma_machdep.c +++ b/sys/x86/x86/busdma_machdep.c @@ -544,11 +544,11 @@ bus_dmamem_alloc(bus_dma_tag_t dmat, void** vaddr, int flags, dmat->alignment <= PAGE_SIZE && (dmat->boundary == 0 || dmat->boundary >= dmat->lowaddr)) { /* Page-based multi-segment allocations allowed */ - *vaddr = (void *)kmem_alloc_attr(kernel_map, dmat->maxsize, + *vaddr = (void *)kmem_alloc_attr(kernel_arena, dmat->maxsize, mflags, 0ul, dmat->lowaddr, attr); *mapp = &contig_dmamap; } else { - *vaddr = (void *)kmem_alloc_contig(kernel_map, dmat->maxsize, + *vaddr = (void *)kmem_alloc_contig(kernel_arena, dmat->maxsize, mflags, 0ul, dmat->lowaddr, dmat->alignment ? dmat->alignment : 1ul, dmat->boundary, attr); *mapp = &contig_dmamap; @@ -582,7 +582,7 @@ bus_dmamem_free(bus_dma_tag_t dmat, void *vaddr, bus_dmamap_t map) if (map == NULL) free(vaddr, M_DEVBUF); else - kmem_free(kernel_map, (vm_offset_t)vaddr, dmat->maxsize); + kmem_free(kernel_arena, (vm_offset_t)vaddr, dmat->maxsize); CTR3(KTR_BUSDMA, "%s: tag %p flags 0x%x", __func__, dmat, dmat->flags); } diff --git a/sys/x86/x86/local_apic.c b/sys/x86/x86/local_apic.c index 42ffa48f736..ac651cdcd64 100644 --- a/sys/x86/x86/local_apic.c +++ b/sys/x86/x86/local_apic.c @@ -91,6 +91,7 @@ CTASSERT(IPI_STOP < APIC_SPURIOUS_INT); #define IRQ_TIMER (NUM_IO_INTS + 1) #define IRQ_SYSCALL (NUM_IO_INTS + 2) #define IRQ_DTRACE_RET (NUM_IO_INTS + 3) +#define IRQ_EVTCHN (NUM_IO_INTS + 4) /* * Support for local APICs. Local APICs manage interrupts on each @@ -313,6 +314,9 @@ lapic_create(u_int apic_id, int boot_cpu) lapics[apic_id].la_ioint_irqs[IDT_DTRACE_RET - APIC_IO_INTS] = IRQ_DTRACE_RET; #endif +#ifdef XENHVM + lapics[apic_id].la_ioint_irqs[IDT_EVTCHN - APIC_IO_INTS] = IRQ_EVTCHN; +#endif #ifdef SMP @@ -1136,6 +1140,10 @@ DB_SHOW_COMMAND(apic, db_show_apic) #ifdef KDTRACE_HOOKS if (irq == IRQ_DTRACE_RET) continue; +#endif +#ifdef XENHVM + if (irq == IRQ_EVTCHN) + continue; #endif db_printf("vec 0x%2x -> ", i + APIC_IO_INTS); if (irq == IRQ_TIMER) diff --git a/sys/x86/xen/hvm.c b/sys/x86/xen/hvm.c new file mode 100644 index 00000000000..0404fe9af08 --- /dev/null +++ b/sys/x86/xen/hvm.c @@ -0,0 +1,687 @@ +/* + * Copyright (c) 2008, 2013 Citrix Systems, Inc. + * Copyright (c) 2012 Spectra Logic Corporation + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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 +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +/*--------------------------- Forward Declarations ---------------------------*/ +static driver_filter_t xen_smp_rendezvous_action; +static driver_filter_t xen_invltlb; +static driver_filter_t xen_invlpg; +static driver_filter_t xen_invlrng; +static driver_filter_t xen_invlcache; +#ifdef __i386__ +static driver_filter_t xen_lazypmap; +#endif +static driver_filter_t xen_ipi_bitmap_handler; +static driver_filter_t xen_cpustop_handler; +static driver_filter_t xen_cpususpend_handler; +static driver_filter_t xen_cpustophard_handler; + +/*---------------------------- Extern Declarations ---------------------------*/ +/* Variables used by mp_machdep to perform the MMU related IPIs */ +extern volatile int smp_tlb_wait; +extern vm_offset_t smp_tlb_addr2; +#ifdef __i386__ +extern vm_offset_t smp_tlb_addr1; +#else +extern struct invpcid_descr smp_tlb_invpcid; +extern uint64_t pcid_cr3; +extern int invpcid_works; +extern int pmap_pcid_enabled; +extern pmap_t smp_tlb_pmap; +#endif + +#ifdef __i386__ +extern void pmap_lazyfix_action(void); +#endif + +/*---------------------------------- Macros ----------------------------------*/ +#define IPI_TO_IDX(ipi) ((ipi) - APIC_IPI_INTS) + +/*-------------------------------- Local Types -------------------------------*/ +struct xen_ipi_handler +{ + driver_filter_t *filter; + const char *description; +}; + +/*-------------------------------- Global Data -------------------------------*/ +enum xen_domain_type xen_domain_type = XEN_NATIVE; + +static MALLOC_DEFINE(M_XENHVM, "xen_hvm", "Xen HVM PV Support"); + +static struct xen_ipi_handler xen_ipis[] = +{ + [IPI_TO_IDX(IPI_RENDEZVOUS)] = { xen_smp_rendezvous_action, "r" }, + [IPI_TO_IDX(IPI_INVLTLB)] = { xen_invltlb, "itlb"}, + [IPI_TO_IDX(IPI_INVLPG)] = { xen_invlpg, "ipg" }, + [IPI_TO_IDX(IPI_INVLRNG)] = { xen_invlrng, "irg" }, + [IPI_TO_IDX(IPI_INVLCACHE)] = { xen_invlcache, "ic" }, +#ifdef __i386__ + [IPI_TO_IDX(IPI_LAZYPMAP)] = { xen_lazypmap, "lp" }, +#endif + [IPI_TO_IDX(IPI_BITMAP_VECTOR)] = { xen_ipi_bitmap_handler, "b" }, + [IPI_TO_IDX(IPI_STOP)] = { xen_cpustop_handler, "st" }, + [IPI_TO_IDX(IPI_SUSPEND)] = { xen_cpususpend_handler, "sp" }, + [IPI_TO_IDX(IPI_STOP_HARD)] = { xen_cpustophard_handler, "sth" }, +}; + +/** + * If non-zero, the hypervisor has been configured to use a direct + * IDT event callback for interrupt injection. + */ +int xen_vector_callback_enabled; + +/*------------------------------- Per-CPU Data -------------------------------*/ +DPCPU_DEFINE(struct vcpu_info, vcpu_local_info); +DPCPU_DEFINE(struct vcpu_info *, vcpu_info); +DPCPU_DEFINE(xen_intr_handle_t, ipi_handle[nitems(xen_ipis)]); + +/*------------------ Hypervisor Access Shared Memory Regions -----------------*/ +/** Hypercall table accessed via HYPERVISOR_*_op() methods. */ +char *hypercall_stubs; +shared_info_t *HYPERVISOR_shared_info; + +/*---------------------------- XEN PV IPI Handlers ---------------------------*/ +/* + * This are C clones of the ASM functions found in apic_vector.s + */ +static int +xen_ipi_bitmap_handler(void *arg) +{ + struct trapframe *frame; + + frame = arg; + ipi_bitmap_handler(*frame); + return (FILTER_HANDLED); +} + +static int +xen_smp_rendezvous_action(void *arg) +{ +#ifdef COUNT_IPIS + int cpu; + + cpu = PCPU_GET(cpuid); + (*ipi_rendezvous_counts[cpu])++; +#endif /* COUNT_IPIS */ + + smp_rendezvous_action(); + return (FILTER_HANDLED); +} + +static int +xen_invltlb(void *arg) +{ +#if defined(COUNT_XINVLTLB_HITS) || defined(COUNT_IPIS) + int cpu; + + cpu = PCPU_GET(cpuid); +#ifdef COUNT_XINVLTLB_HITS + xhits_gbl[cpu]++; +#endif /* COUNT_XINVLTLB_HITS */ +#ifdef COUNT_IPIS + (*ipi_invltlb_counts[cpu])++; +#endif /* COUNT_IPIS */ +#endif /* COUNT_XINVLTLB_HITS || COUNT_IPIS */ + + invltlb(); + atomic_add_int(&smp_tlb_wait, 1); + return (FILTER_HANDLED); +} + +#ifdef __amd64__ +static int +xen_invltlb_pcid(void *arg) +{ + uint64_t cr3; +#if defined(COUNT_XINVLTLB_HITS) || defined(COUNT_IPIS) + int cpu; + + cpu = PCPU_GET(cpuid); +#ifdef COUNT_XINVLTLB_HITS + xhits_gbl[cpu]++; +#endif /* COUNT_XINVLTLB_HITS */ +#ifdef COUNT_IPIS + (*ipi_invltlb_counts[cpu])++; +#endif /* COUNT_IPIS */ +#endif /* COUNT_XINVLTLB_HITS || COUNT_IPIS */ + + cr3 = rcr3(); + if (smp_tlb_invpcid.pcid != (uint64_t)-1 && + smp_tlb_invpcid.pcid != 0) { + + if (invpcid_works) { + invpcid(&smp_tlb_invpcid, INVPCID_CTX); + } else { + /* Otherwise reload %cr3 twice. */ + if (cr3 != pcid_cr3) { + load_cr3(pcid_cr3); + cr3 |= CR3_PCID_SAVE; + } + load_cr3(cr3); + } + } else { + invltlb_globpcid(); + } + if (smp_tlb_pmap != NULL) + CPU_CLR_ATOMIC(PCPU_GET(cpuid), &smp_tlb_pmap->pm_save); + + atomic_add_int(&smp_tlb_wait, 1); + return (FILTER_HANDLED); +} +#endif + +static int +xen_invlpg(void *arg) +{ +#if defined(COUNT_XINVLTLB_HITS) || defined(COUNT_IPIS) + int cpu; + + cpu = PCPU_GET(cpuid); +#ifdef COUNT_XINVLTLB_HITS + xhits_pg[cpu]++; +#endif /* COUNT_XINVLTLB_HITS */ +#ifdef COUNT_IPIS + (*ipi_invlpg_counts[cpu])++; +#endif /* COUNT_IPIS */ +#endif /* COUNT_XINVLTLB_HITS || COUNT_IPIS */ + +#ifdef __i386__ + invlpg(smp_tlb_addr1); +#else + invlpg(smp_tlb_invpcid.addr); +#endif + atomic_add_int(&smp_tlb_wait, 1); + return (FILTER_HANDLED); +} + +#ifdef __amd64__ +static int +xen_invlpg_pcid(void *arg) +{ +#if defined(COUNT_XINVLTLB_HITS) || defined(COUNT_IPIS) + int cpu; + + cpu = PCPU_GET(cpuid); +#ifdef COUNT_XINVLTLB_HITS + xhits_pg[cpu]++; +#endif /* COUNT_XINVLTLB_HITS */ +#ifdef COUNT_IPIS + (*ipi_invlpg_counts[cpu])++; +#endif /* COUNT_IPIS */ +#endif /* COUNT_XINVLTLB_HITS || COUNT_IPIS */ + + if (invpcid_works) { + invpcid(&smp_tlb_invpcid, INVPCID_ADDR); + } else if (smp_tlb_invpcid.pcid == 0) { + invlpg(smp_tlb_invpcid.addr); + } else if (smp_tlb_invpcid.pcid == (uint64_t)-1) { + invltlb_globpcid(); + } else { + uint64_t cr3; + + /* + * PCID supported, but INVPCID is not. + * Temporarily switch to the target address + * space and do INVLPG. + */ + cr3 = rcr3(); + if (cr3 != pcid_cr3) + load_cr3(pcid_cr3 | CR3_PCID_SAVE); + invlpg(smp_tlb_invpcid.addr); + load_cr3(cr3 | CR3_PCID_SAVE); + } + + atomic_add_int(&smp_tlb_wait, 1); + return (FILTER_HANDLED); +} +#endif + +static inline void +invlpg_range(vm_offset_t start, vm_offset_t end) +{ + do { + invlpg(start); + start += PAGE_SIZE; + } while (start < end); +} + +static int +xen_invlrng(void *arg) +{ + vm_offset_t addr; +#if defined(COUNT_XINVLTLB_HITS) || defined(COUNT_IPIS) + int cpu; + + cpu = PCPU_GET(cpuid); +#ifdef COUNT_XINVLTLB_HITS + xhits_rng[cpu]++; +#endif /* COUNT_XINVLTLB_HITS */ +#ifdef COUNT_IPIS + (*ipi_invlrng_counts[cpu])++; +#endif /* COUNT_IPIS */ +#endif /* COUNT_XINVLTLB_HITS || COUNT_IPIS */ + +#ifdef __i386__ + addr = smp_tlb_addr1; + invlpg_range(addr, smp_tlb_addr2); +#else + addr = smp_tlb_invpcid.addr; + if (pmap_pcid_enabled) { + if (invpcid_works) { + struct invpcid_descr d; + + d = smp_tlb_invpcid; + do { + invpcid(&d, INVPCID_ADDR); + d.addr += PAGE_SIZE; + } while (d.addr < smp_tlb_addr2); + } else if (smp_tlb_invpcid.pcid == 0) { + /* + * kernel pmap - use invlpg to invalidate + * global mapping. + */ + invlpg_range(addr, smp_tlb_addr2); + } else if (smp_tlb_invpcid.pcid != (uint64_t)-1) { + invltlb_globpcid(); + if (smp_tlb_pmap != NULL) { + CPU_CLR_ATOMIC(PCPU_GET(cpuid), + &smp_tlb_pmap->pm_save); + } + } else { + uint64_t cr3; + + cr3 = rcr3(); + if (cr3 != pcid_cr3) + load_cr3(pcid_cr3 | CR3_PCID_SAVE); + invlpg_range(addr, smp_tlb_addr2); + load_cr3(cr3 | CR3_PCID_SAVE); + } + } else { + invlpg_range(addr, smp_tlb_addr2); + } +#endif + + atomic_add_int(&smp_tlb_wait, 1); + return (FILTER_HANDLED); +} + +static int +xen_invlcache(void *arg) +{ +#ifdef COUNT_IPIS + int cpu = PCPU_GET(cpuid); + + cpu = PCPU_GET(cpuid); + (*ipi_invlcache_counts[cpu])++; +#endif /* COUNT_IPIS */ + + wbinvd(); + atomic_add_int(&smp_tlb_wait, 1); + return (FILTER_HANDLED); +} + +#ifdef __i386__ +static int +xen_lazypmap(void *arg) +{ + + pmap_lazyfix_action(); + return (FILTER_HANDLED); +} +#endif + +static int +xen_cpustop_handler(void *arg) +{ + + cpustop_handler(); + return (FILTER_HANDLED); +} + +static int +xen_cpususpend_handler(void *arg) +{ + + cpususpend_handler(); + return (FILTER_HANDLED); +} + +static int +xen_cpustophard_handler(void *arg) +{ + + ipi_nmi_handler(); + return (FILTER_HANDLED); +} + +/* Xen PV IPI sender */ +static void +xen_ipi_vectored(u_int vector, int dest) +{ + xen_intr_handle_t *ipi_handle; + int ipi_idx, to_cpu, self; + + ipi_idx = IPI_TO_IDX(vector); + if (ipi_idx > nitems(xen_ipis)) + panic("IPI out of range"); + + switch(dest) { + case APIC_IPI_DEST_SELF: + ipi_handle = DPCPU_GET(ipi_handle); + xen_intr_signal(ipi_handle[ipi_idx]); + break; + case APIC_IPI_DEST_ALL: + CPU_FOREACH(to_cpu) { + ipi_handle = DPCPU_ID_GET(to_cpu, ipi_handle); + xen_intr_signal(ipi_handle[ipi_idx]); + } + break; + case APIC_IPI_DEST_OTHERS: + self = PCPU_GET(cpuid); + CPU_FOREACH(to_cpu) { + if (to_cpu != self) { + ipi_handle = DPCPU_ID_GET(to_cpu, ipi_handle); + xen_intr_signal(ipi_handle[ipi_idx]); + } + } + break; + default: + to_cpu = apic_cpuid(dest); + ipi_handle = DPCPU_ID_GET(to_cpu, ipi_handle); + xen_intr_signal(ipi_handle[ipi_idx]); + break; + } +} + +static void +xen_cpu_ipi_init(int cpu) +{ + xen_intr_handle_t *ipi_handle; + const struct xen_ipi_handler *ipi; + device_t dev; + int idx, rc; + + ipi_handle = DPCPU_ID_GET(cpu, ipi_handle); + dev = pcpu_find(cpu)->pc_device; + KASSERT((dev != NULL), ("NULL pcpu device_t")); + + for (ipi = xen_ipis, idx = 0; idx < nitems(xen_ipis); ipi++, idx++) { + + if (ipi->filter == NULL) { + ipi_handle[idx] = NULL; + continue; + } + + rc = xen_intr_alloc_and_bind_ipi(dev, cpu, ipi->filter, + INTR_TYPE_TTY, &ipi_handle[idx]); + if (rc != 0) + panic("Unable to allocate a XEN IPI port"); + xen_intr_describe(ipi_handle[idx], "%s", ipi->description); + } +} + +static void +xen_init_ipis(void) +{ + int i; + + if (!xen_hvm_domain() || !xen_vector_callback_enabled) + return; + +#ifdef __amd64__ + if (pmap_pcid_enabled) { + xen_ipis[IPI_TO_IDX(IPI_INVLTLB)].filter = xen_invltlb_pcid; + xen_ipis[IPI_TO_IDX(IPI_INVLPG)].filter = xen_invlpg_pcid; + } +#endif + CPU_FOREACH(i) + xen_cpu_ipi_init(i); + + /* Set the xen pv ipi ops to replace the native ones */ + cpu_ops.ipi_vectored = xen_ipi_vectored; +} + +/*---------------------- XEN Hypervisor Probe and Setup ----------------------*/ +static uint32_t +xen_hvm_cpuid_base(void) +{ + uint32_t base, regs[4]; + + for (base = 0x40000000; base < 0x40010000; base += 0x100) { + do_cpuid(base, regs); + if (!memcmp("XenVMMXenVMM", ®s[1], 12) + && (regs[0] - base) >= 2) + return (base); + } + return (0); +} + +/* + * Allocate and fill in the hypcall page. + */ +static int +xen_hvm_init_hypercall_stubs(void) +{ + uint32_t base, regs[4]; + int i; + + base = xen_hvm_cpuid_base(); + if (base == 0) + return (ENXIO); + + if (hypercall_stubs == NULL) { + do_cpuid(base + 1, regs); + printf("XEN: Hypervisor version %d.%d detected.\n", + regs[0] >> 16, regs[0] & 0xffff); + } + + /* + * Find the hypercall pages. + */ + do_cpuid(base + 2, regs); + + if (hypercall_stubs == NULL) { + size_t call_region_size; + + call_region_size = regs[0] * PAGE_SIZE; + hypercall_stubs = malloc(call_region_size, M_XENHVM, M_NOWAIT); + if (hypercall_stubs == NULL) + panic("Unable to allocate Xen hypercall region"); + } + + for (i = 0; i < regs[0]; i++) + wrmsr(regs[1], vtophys(hypercall_stubs + i * PAGE_SIZE) + i); + + return (0); +} + +static void +xen_hvm_init_shared_info_page(void) +{ + struct xen_add_to_physmap xatp; + + if (HYPERVISOR_shared_info == NULL) { + HYPERVISOR_shared_info = malloc(PAGE_SIZE, M_XENHVM, M_NOWAIT); + if (HYPERVISOR_shared_info == NULL) + panic("Unable to allocate Xen shared info page"); + } + + xatp.domid = DOMID_SELF; + xatp.idx = 0; + xatp.space = XENMAPSPACE_shared_info; + xatp.gpfn = vtophys(HYPERVISOR_shared_info) >> PAGE_SHIFT; + if (HYPERVISOR_memory_op(XENMEM_add_to_physmap, &xatp)) + panic("HYPERVISOR_memory_op failed"); +} + +/* + * Tell the hypervisor how to contact us for event channel callbacks. + */ +void +xen_hvm_set_callback(device_t dev) +{ + struct xen_hvm_param xhp; + int irq; + + xhp.domid = DOMID_SELF; + xhp.index = HVM_PARAM_CALLBACK_IRQ; + if (xen_feature(XENFEAT_hvm_callback_vector) != 0) { + int error; + + xhp.value = HVM_CALLBACK_VECTOR(IDT_EVTCHN); + error = HYPERVISOR_hvm_op(HVMOP_set_param, &xhp); + if (error == 0) { + xen_vector_callback_enabled = 1; + return; + } + printf("Xen HVM callback vector registration failed (%d). " + "Falling back to emulated device interrupt\n", error); + } + xen_vector_callback_enabled = 0; + if (dev == NULL) { + /* + * Called from early boot or resume. + * xenpci will invoke us again later. + */ + return; + } + + irq = pci_get_irq(dev); + if (irq < 16) { + xhp.value = HVM_CALLBACK_GSI(irq); + } else { + u_int slot; + u_int pin; + + slot = pci_get_slot(dev); + pin = pci_get_intpin(dev) - 1; + xhp.value = HVM_CALLBACK_PCI_INTX(slot, pin); + } + + if (HYPERVISOR_hvm_op(HVMOP_set_param, &xhp) != 0) + panic("Can't set evtchn callback"); +} + +#define XEN_MAGIC_IOPORT 0x10 +enum { + XMI_MAGIC = 0x49d2, + XMI_UNPLUG_IDE_DISKS = 0x01, + XMI_UNPLUG_NICS = 0x02, + XMI_UNPLUG_IDE_EXCEPT_PRI_MASTER = 0x04 +}; + +static void +xen_hvm_disable_emulated_devices(void) +{ + if (inw(XEN_MAGIC_IOPORT) != XMI_MAGIC) + return; + + if (bootverbose) + printf("XEN: Disabling emulated block and network devices\n"); + outw(XEN_MAGIC_IOPORT, XMI_UNPLUG_IDE_DISKS|XMI_UNPLUG_NICS); +} + +void +xen_hvm_suspend(void) +{ +} + +void +xen_hvm_resume(void) +{ + + xen_hvm_init_hypercall_stubs(); + xen_hvm_init_shared_info_page(); +} + +static void +xen_hvm_init(void *dummy __unused) +{ + + if (xen_hvm_init_hypercall_stubs() != 0) + return; + + xen_domain_type = XEN_HVM_DOMAIN; + setup_xen_features(); + xen_hvm_init_shared_info_page(); + xen_hvm_set_callback(NULL); + xen_hvm_disable_emulated_devices(); +} + +void xen_hvm_init_cpu(void) +{ + struct vcpu_register_vcpu_info info; + struct vcpu_info *vcpu_info; + int cpu, rc; + + cpu = PCPU_GET(acpi_id); + vcpu_info = DPCPU_PTR(vcpu_local_info); + info.mfn = vtophys(vcpu_info) >> PAGE_SHIFT; + info.offset = vtophys(vcpu_info) - trunc_page(vtophys(vcpu_info)); + + rc = HYPERVISOR_vcpu_op(VCPUOP_register_vcpu_info, cpu, &info); + if (rc != 0) + DPCPU_SET(vcpu_info, &HYPERVISOR_shared_info->vcpu_info[cpu]); + else + DPCPU_SET(vcpu_info, vcpu_info); +} + +SYSINIT(xen_hvm_init, SI_SUB_HYPERVISOR, SI_ORDER_FIRST, xen_hvm_init, NULL); +SYSINIT(xen_init_ipis, SI_SUB_SMP, SI_ORDER_FIRST, xen_init_ipis, NULL); +SYSINIT(xen_hvm_init_cpu, SI_SUB_INTR, SI_ORDER_FIRST, xen_hvm_init_cpu, NULL); diff --git a/sys/x86/xen/xen_intr.c b/sys/x86/xen/xen_intr.c new file mode 100644 index 00000000000..54a6be67800 --- /dev/null +++ b/sys/x86/xen/xen_intr.c @@ -0,0 +1,1126 @@ +/****************************************************************************** + * xen_intr.c + * + * Xen event and interrupt services for x86 PV and HVM guests. + * + * Copyright (c) 2002-2005, K A Fraser + * Copyright (c) 2005, Intel Corporation + * Copyright (c) 2012, Spectra Logic Corporation + * + * This file may be distributed separately from the Linux kernel, or + * incorporated into other software packages, subject to the following license: + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this source file (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 +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include + +static MALLOC_DEFINE(M_XENINTR, "xen_intr", "Xen Interrupt Services"); + +/** + * Per-cpu event channel processing state. + */ +struct xen_intr_pcpu_data { + /** + * The last event channel bitmap section (level one bit) processed. + * This is used to ensure we scan all ports before + * servicing an already servied port again. + */ + u_int last_processed_l1i; + + /** + * The last event channel processed within the event channel + * bitmap being scanned. + */ + u_int last_processed_l2i; + + /** Pointer to this CPU's interrupt statistic counter. */ + u_long *evtchn_intrcnt; + + /** + * A bitmap of ports that can be serviced from this CPU. + * A set bit means interrupt handling is enabled. + */ + u_long evtchn_enabled[sizeof(u_long) * 8]; +}; + +/* + * Start the scan at port 0 by initializing the last scanned + * location as the highest numbered event channel port. + */ +DPCPU_DEFINE(struct xen_intr_pcpu_data, xen_intr_pcpu) = { + .last_processed_l1i = LONG_BIT - 1, + .last_processed_l2i = LONG_BIT - 1 +}; + +DPCPU_DECLARE(struct vcpu_info *, vcpu_info); + +#define is_valid_evtchn(x) ((x) != 0) + +struct xenisrc { + struct intsrc xi_intsrc; + enum evtchn_type xi_type; + int xi_cpu; /* VCPU for delivery. */ + int xi_vector; /* Global isrc vector number. */ + evtchn_port_t xi_port; + int xi_pirq; + int xi_virq; + u_int xi_close:1; /* close on unbind? */ + u_int xi_needs_eoi:1; + u_int xi_shared:1; /* Shared with other domains. */ +}; + +#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0])) + +static void xen_intr_suspend(struct pic *); +static void xen_intr_resume(struct pic *); +static void xen_intr_enable_source(struct intsrc *isrc); +static void xen_intr_disable_source(struct intsrc *isrc, int eoi); +static void xen_intr_eoi_source(struct intsrc *isrc); +static void xen_intr_enable_intr(struct intsrc *isrc); +static void xen_intr_disable_intr(struct intsrc *isrc); +static int xen_intr_vector(struct intsrc *isrc); +static int xen_intr_source_pending(struct intsrc *isrc); +static int xen_intr_config_intr(struct intsrc *isrc, + enum intr_trigger trig, enum intr_polarity pol); +static int xen_intr_assign_cpu(struct intsrc *isrc, u_int apic_id); + +static void xen_intr_pirq_enable_source(struct intsrc *isrc); +static void xen_intr_pirq_disable_source(struct intsrc *isrc, int eoi); +static void xen_intr_pirq_eoi_source(struct intsrc *isrc); +static void xen_intr_pirq_enable_intr(struct intsrc *isrc); + +/** + * PIC interface for all event channel port types except physical IRQs. + */ +struct pic xen_intr_pic = { + .pic_enable_source = xen_intr_enable_source, + .pic_disable_source = xen_intr_disable_source, + .pic_eoi_source = xen_intr_eoi_source, + .pic_enable_intr = xen_intr_enable_intr, + .pic_disable_intr = xen_intr_disable_intr, + .pic_vector = xen_intr_vector, + .pic_source_pending = xen_intr_source_pending, + .pic_suspend = xen_intr_suspend, + .pic_resume = xen_intr_resume, + .pic_config_intr = xen_intr_config_intr, + .pic_assign_cpu = xen_intr_assign_cpu +}; + +/** + * PIC interface for all event channel representing + * physical interrupt sources. + */ +struct pic xen_intr_pirq_pic = { + .pic_enable_source = xen_intr_pirq_enable_source, + .pic_disable_source = xen_intr_pirq_disable_source, + .pic_eoi_source = xen_intr_pirq_eoi_source, + .pic_enable_intr = xen_intr_pirq_enable_intr, + .pic_disable_intr = xen_intr_disable_intr, + .pic_vector = xen_intr_vector, + .pic_source_pending = xen_intr_source_pending, + .pic_suspend = xen_intr_suspend, + .pic_resume = xen_intr_resume, + .pic_config_intr = xen_intr_config_intr, + .pic_assign_cpu = xen_intr_assign_cpu +}; + +static struct mtx xen_intr_isrc_lock; +static int xen_intr_isrc_count; +static struct xenisrc *xen_intr_port_to_isrc[NR_EVENT_CHANNELS]; + +/*------------------------- Private Functions --------------------------------*/ +/** + * Disable signal delivery for an event channel port on the + * specified CPU. + * + * \param port The event channel port to mask. + * + * This API is used to manage the port<=>CPU binding of event + * channel handlers. + * + * \note This operation does not preclude reception of an event + * for this event channel on another CPU. To mask the + * event channel globally, use evtchn_mask(). + */ +static inline void +evtchn_cpu_mask_port(u_int cpu, evtchn_port_t port) +{ + struct xen_intr_pcpu_data *pcpu; + + pcpu = DPCPU_ID_PTR(cpu, xen_intr_pcpu); + clear_bit(port, pcpu->evtchn_enabled); +} + +/** + * Enable signal delivery for an event channel port on the + * specified CPU. + * + * \param port The event channel port to unmask. + * + * This API is used to manage the port<=>CPU binding of event + * channel handlers. + * + * \note This operation does not guarantee that event delivery + * is enabled for this event channel port. The port must + * also be globally enabled. See evtchn_unmask(). + */ +static inline void +evtchn_cpu_unmask_port(u_int cpu, evtchn_port_t port) +{ + struct xen_intr_pcpu_data *pcpu; + + pcpu = DPCPU_ID_PTR(cpu, xen_intr_pcpu); + set_bit(port, pcpu->evtchn_enabled); +} + +/** + * Allocate and register a per-cpu Xen upcall interrupt counter. + * + * \param cpu The cpu for which to register this interrupt count. + */ +static void +xen_intr_intrcnt_add(u_int cpu) +{ + char buf[MAXCOMLEN + 1]; + struct xen_intr_pcpu_data *pcpu; + + pcpu = DPCPU_ID_PTR(cpu, xen_intr_pcpu); + if (pcpu->evtchn_intrcnt != NULL) + return; + + snprintf(buf, sizeof(buf), "cpu%d:xen", cpu); + intrcnt_add(buf, &pcpu->evtchn_intrcnt); +} + +/** + * Search for an already allocated but currently unused Xen interrupt + * source object. + * + * \param type Restrict the search to interrupt sources of the given + * type. + * + * \return A pointer to a free Xen interrupt source object or NULL. + */ +static struct xenisrc * +xen_intr_find_unused_isrc(enum evtchn_type type) +{ + int isrc_idx; + + KASSERT(mtx_owned(&xen_intr_isrc_lock), ("Evtchn isrc lock not held")); + + for (isrc_idx = 0; isrc_idx < xen_intr_isrc_count; isrc_idx ++) { + struct xenisrc *isrc; + u_int vector; + + vector = FIRST_EVTCHN_INT + isrc_idx; + isrc = (struct xenisrc *)intr_lookup_source(vector); + if (isrc != NULL + && isrc->xi_type == EVTCHN_TYPE_UNBOUND) { + KASSERT(isrc->xi_intsrc.is_handlers == 0, + ("Free evtchn still has handlers")); + isrc->xi_type = type; + return (isrc); + } + } + return (NULL); +} + +/** + * Allocate a Xen interrupt source object. + * + * \param type The type of interrupt source to create. + * + * \return A pointer to a newly allocated Xen interrupt source + * object or NULL. + */ +static struct xenisrc * +xen_intr_alloc_isrc(enum evtchn_type type) +{ + static int warned; + struct xenisrc *isrc; + int vector; + + KASSERT(mtx_owned(&xen_intr_isrc_lock), ("Evtchn alloc lock not held")); + + if (xen_intr_isrc_count > NR_EVENT_CHANNELS) { + if (!warned) { + warned = 1; + printf("xen_intr_alloc: Event channels exhausted.\n"); + } + return (NULL); + } + vector = FIRST_EVTCHN_INT + xen_intr_isrc_count; + xen_intr_isrc_count++; + + mtx_unlock(&xen_intr_isrc_lock); + isrc = malloc(sizeof(*isrc), M_XENINTR, M_WAITOK | M_ZERO); + isrc->xi_intsrc.is_pic = &xen_intr_pic; + isrc->xi_vector = vector; + isrc->xi_type = type; + intr_register_source(&isrc->xi_intsrc); + mtx_lock(&xen_intr_isrc_lock); + + return (isrc); +} + +/** + * Attempt to free an active Xen interrupt source object. + * + * \param isrc The interrupt source object to release. + * + * \returns EBUSY if the source is still in use, otherwise 0. + */ +static int +xen_intr_release_isrc(struct xenisrc *isrc) +{ + + mtx_lock(&xen_intr_isrc_lock); + if (isrc->xi_intsrc.is_handlers != 0) { + mtx_unlock(&xen_intr_isrc_lock); + return (EBUSY); + } + evtchn_mask_port(isrc->xi_port); + evtchn_clear_port(isrc->xi_port); + + /* Rebind port to CPU 0. */ + evtchn_cpu_mask_port(isrc->xi_cpu, isrc->xi_port); + evtchn_cpu_unmask_port(0, isrc->xi_port); + + if (isrc->xi_close != 0) { + struct evtchn_close close = { .port = isrc->xi_port }; + if (HYPERVISOR_event_channel_op(EVTCHNOP_close, &close)) + panic("EVTCHNOP_close failed"); + } + + xen_intr_port_to_isrc[isrc->xi_port] = NULL; + isrc->xi_cpu = 0; + isrc->xi_type = EVTCHN_TYPE_UNBOUND; + isrc->xi_port = 0; + mtx_unlock(&xen_intr_isrc_lock); + return (0); +} + +/** + * Associate an interrupt handler with an already allocated local Xen + * event channel port. + * + * \param isrcp The returned Xen interrupt object associated with + * the specified local port. + * \param local_port The event channel to bind. + * \param type The event channel type of local_port. + * \param intr_owner The device making this bind request. + * \param filter An interrupt filter handler. Specify NULL + * to always dispatch to the ithread handler. + * \param handler An interrupt ithread handler. Optional (can + * specify NULL) if all necessary event actions + * are performed by filter. + * \param arg Argument to present to both filter and handler. + * \param irqflags Interrupt handler flags. See sys/bus.h. + * \param handlep Pointer to an opaque handle used to manage this + * registration. + * + * \returns 0 on success, otherwise an errno. + */ +static int +xen_intr_bind_isrc(struct xenisrc **isrcp, evtchn_port_t local_port, + enum evtchn_type type, device_t intr_owner, driver_filter_t filter, + driver_intr_t handler, void *arg, enum intr_type flags, + xen_intr_handle_t *port_handlep) +{ + struct xenisrc *isrc; + int error; + + *isrcp = NULL; + if (port_handlep == NULL) { + device_printf(intr_owner, + "xen_intr_bind_isrc: Bad event handle\n"); + return (EINVAL); + } + + mtx_lock(&xen_intr_isrc_lock); + isrc = xen_intr_find_unused_isrc(type); + if (isrc == NULL) { + isrc = xen_intr_alloc_isrc(type); + if (isrc == NULL) { + mtx_unlock(&xen_intr_isrc_lock); + return (ENOSPC); + } + } + isrc->xi_port = local_port; + xen_intr_port_to_isrc[local_port] = isrc; + mtx_unlock(&xen_intr_isrc_lock); + + error = intr_add_handler(device_get_nameunit(intr_owner), + isrc->xi_vector, filter, handler, arg, + flags|INTR_EXCL, port_handlep); + if (error != 0) { + device_printf(intr_owner, + "xen_intr_bind_irq: intr_add_handler failed\n"); + xen_intr_release_isrc(isrc); + return (error); + } + *isrcp = isrc; + return (0); +} + +/** + * Lookup a Xen interrupt source object given an interrupt binding handle. + * + * \param handle A handle initialized by a previous call to + * xen_intr_bind_isrc(). + * + * \returns A pointer to the Xen interrupt source object associated + * with the given interrupt handle. NULL if no association + * currently exists. + */ +static struct xenisrc * +xen_intr_isrc(xen_intr_handle_t handle) +{ + struct intr_handler *ih; + + ih = handle; + if (ih == NULL || ih->ih_event == NULL) + return (NULL); + + return (ih->ih_event->ie_source); +} + +/** + * Determine the event channel ports at the given section of the + * event port bitmap which have pending events for the given cpu. + * + * \param pcpu The Xen interrupt pcpu data for the cpu being querried. + * \param sh The Xen shared info area. + * \param idx The index of the section of the event channel bitmap to + * inspect. + * + * \returns A u_long with bits set for every event channel with pending + * events. + */ +static inline u_long +xen_intr_active_ports(struct xen_intr_pcpu_data *pcpu, shared_info_t *sh, + u_int idx) +{ + return (sh->evtchn_pending[idx] + & ~sh->evtchn_mask[idx] + & pcpu->evtchn_enabled[idx]); +} + +/** + * Interrupt handler for processing all Xen event channel events. + * + * \param trap_frame The trap frame context for the current interrupt. + */ +void +xen_intr_handle_upcall(struct trapframe *trap_frame) +{ + u_int l1i, l2i, port, cpu; + u_long masked_l1, masked_l2; + struct xenisrc *isrc; + shared_info_t *s; + vcpu_info_t *v; + struct xen_intr_pcpu_data *pc; + u_long l1, l2; + + /* + * Disable preemption in order to always check and fire events + * on the right vCPU + */ + critical_enter(); + + cpu = PCPU_GET(cpuid); + pc = DPCPU_PTR(xen_intr_pcpu); + s = HYPERVISOR_shared_info; + v = DPCPU_GET(vcpu_info); + + if (xen_hvm_domain() && !xen_vector_callback_enabled) { + KASSERT((cpu == 0), ("Fired PCI event callback on wrong CPU")); + } + + v->evtchn_upcall_pending = 0; + +#if 0 +#ifndef CONFIG_X86 /* No need for a barrier -- XCHG is a barrier on x86. */ + /* Clear master flag /before/ clearing selector flag. */ + wmb(); +#endif +#endif + + l1 = atomic_readandclear_long(&v->evtchn_pending_sel); + + l1i = pc->last_processed_l1i; + l2i = pc->last_processed_l2i; + (*pc->evtchn_intrcnt)++; + + while (l1 != 0) { + + l1i = (l1i + 1) % LONG_BIT; + masked_l1 = l1 & ((~0UL) << l1i); + + if (masked_l1 == 0) { + /* + * if we masked out all events, wrap around + * to the beginning. + */ + l1i = LONG_BIT - 1; + l2i = LONG_BIT - 1; + continue; + } + l1i = ffsl(masked_l1) - 1; + + do { + l2 = xen_intr_active_ports(pc, s, l1i); + + l2i = (l2i + 1) % LONG_BIT; + masked_l2 = l2 & ((~0UL) << l2i); + + if (masked_l2 == 0) { + /* if we masked out all events, move on */ + l2i = LONG_BIT - 1; + break; + } + l2i = ffsl(masked_l2) - 1; + + /* process port */ + port = (l1i * LONG_BIT) + l2i; + synch_clear_bit(port, &s->evtchn_pending[0]); + + isrc = xen_intr_port_to_isrc[port]; + if (__predict_false(isrc == NULL)) + continue; + + /* Make sure we are firing on the right vCPU */ + KASSERT((isrc->xi_cpu == PCPU_GET(cpuid)), + ("Received unexpected event on vCPU#%d, event bound to vCPU#%d", + PCPU_GET(cpuid), isrc->xi_cpu)); + + intr_execute_handlers(&isrc->xi_intsrc, trap_frame); + + /* + * If this is the final port processed, + * we'll pick up here+1 next time. + */ + pc->last_processed_l1i = l1i; + pc->last_processed_l2i = l2i; + + } while (l2i != LONG_BIT - 1); + + l2 = xen_intr_active_ports(pc, s, l1i); + if (l2 == 0) { + /* + * We handled all ports, so we can clear the + * selector bit. + */ + l1 &= ~(1UL << l1i); + } + } + critical_exit(); +} + +static int +xen_intr_init(void *dummy __unused) +{ + struct xen_intr_pcpu_data *pcpu; + int i; + + mtx_init(&xen_intr_isrc_lock, "xen-irq-lock", NULL, MTX_DEF); + + /* + * Register interrupt count manually as we aren't + * guaranteed to see a call to xen_intr_assign_cpu() + * before our first interrupt. Also set the per-cpu + * mask of CPU#0 to enable all, since by default + * all event channels are bound to CPU#0. + */ + CPU_FOREACH(i) { + pcpu = DPCPU_ID_PTR(i, xen_intr_pcpu); + memset(pcpu->evtchn_enabled, i == 0 ? ~0 : 0, + sizeof(pcpu->evtchn_enabled)); + xen_intr_intrcnt_add(i); + } + + intr_register_pic(&xen_intr_pic); + + return (0); +} +SYSINIT(xen_intr_init, SI_SUB_INTR, SI_ORDER_MIDDLE, xen_intr_init, NULL); + +/*--------------------------- Common PIC Functions ---------------------------*/ +/** + * Prepare this PIC for system suspension. + */ +static void +xen_intr_suspend(struct pic *unused) +{ +} + +/** + * Return this PIC to service after being suspended. + */ +static void +xen_intr_resume(struct pic *unused) +{ + u_int port; + + /* + * Mask events for all ports. They will be unmasked after + * drivers have re-registered their handlers. + */ + for (port = 0; port < NR_EVENT_CHANNELS; port++) + evtchn_mask_port(port); +} + +/** + * Disable a Xen interrupt source. + * + * \param isrc The interrupt source to disable. + */ +static void +xen_intr_disable_intr(struct intsrc *base_isrc) +{ + struct xenisrc *isrc = (struct xenisrc *)base_isrc; + + evtchn_mask_port(isrc->xi_port); +} + +/** + * Determine the global interrupt vector number for + * a Xen interrupt source. + * + * \param isrc The interrupt source to query. + * + * \return The vector number corresponding to the given interrupt source. + */ +static int +xen_intr_vector(struct intsrc *base_isrc) +{ + struct xenisrc *isrc = (struct xenisrc *)base_isrc; + + return (isrc->xi_vector); +} + +/** + * Determine whether or not interrupt events are pending on the + * the given interrupt source. + * + * \param isrc The interrupt source to query. + * + * \returns 0 if no events are pending, otherwise non-zero. + */ +static int +xen_intr_source_pending(struct intsrc *isrc) +{ + /* + * EventChannels are edge triggered and never masked. + * There can be no pending events. + */ + return (0); +} + +/** + * Perform configuration of an interrupt source. + * + * \param isrc The interrupt source to configure. + * \param trig Edge or level. + * \param pol Active high or low. + * + * \returns 0 if no events are pending, otherwise non-zero. + */ +static int +xen_intr_config_intr(struct intsrc *isrc, enum intr_trigger trig, + enum intr_polarity pol) +{ + /* Configuration is only possible via the evtchn apis. */ + return (ENODEV); +} + +/** + * Configure CPU affinity for interrupt source event delivery. + * + * \param isrc The interrupt source to configure. + * \param apic_id The apic id of the CPU for handling future events. + * + * \returns 0 if successful, otherwise an errno. + */ +static int +xen_intr_assign_cpu(struct intsrc *base_isrc, u_int apic_id) +{ + struct evtchn_bind_vcpu bind_vcpu; + struct xenisrc *isrc; + u_int to_cpu, acpi_id; + int error; + +#ifdef XENHVM + if (xen_vector_callback_enabled == 0) + return (EOPNOTSUPP); +#endif + + to_cpu = apic_cpuid(apic_id); + acpi_id = pcpu_find(to_cpu)->pc_acpi_id; + xen_intr_intrcnt_add(to_cpu); + + mtx_lock(&xen_intr_isrc_lock); + isrc = (struct xenisrc *)base_isrc; + if (!is_valid_evtchn(isrc->xi_port)) { + mtx_unlock(&xen_intr_isrc_lock); + return (EINVAL); + } + + if ((isrc->xi_type == EVTCHN_TYPE_VIRQ) || + (isrc->xi_type == EVTCHN_TYPE_IPI)) { + /* + * Virtual IRQs are associated with a cpu by + * the Hypervisor at evtchn_bind_virq time, so + * all we need to do is update the per-CPU masks. + */ + evtchn_cpu_mask_port(isrc->xi_cpu, isrc->xi_port); + isrc->xi_cpu = to_cpu; + evtchn_cpu_unmask_port(isrc->xi_cpu, isrc->xi_port); + mtx_unlock(&xen_intr_isrc_lock); + return (0); + } + + bind_vcpu.port = isrc->xi_port; + bind_vcpu.vcpu = acpi_id; + + /* + * Allow interrupts to be fielded on the new VCPU before + * we ask the hypervisor to deliver them there. + */ + evtchn_cpu_unmask_port(to_cpu, isrc->xi_port); + error = HYPERVISOR_event_channel_op(EVTCHNOP_bind_vcpu, &bind_vcpu); + if (isrc->xi_cpu != to_cpu) { + if (error == 0) { + /* Commit to new binding by removing the old one. */ + evtchn_cpu_mask_port(isrc->xi_cpu, isrc->xi_port); + isrc->xi_cpu = to_cpu; + } else { + /* Roll-back to previous binding. */ + evtchn_cpu_mask_port(to_cpu, isrc->xi_port); + } + } + mtx_unlock(&xen_intr_isrc_lock); + return (0); +} + +/*------------------- Virtual Interrupt Source PIC Functions -----------------*/ +/* + * Mask a level triggered interrupt source. + * + * \param isrc The interrupt source to mask (if necessary). + * \param eoi If non-zero, perform any necessary end-of-interrupt + * acknowledgements. + */ +static void +xen_intr_disable_source(struct intsrc *isrc, int eoi) +{ +} + +/* + * Unmask a level triggered interrupt source. + * + * \param isrc The interrupt source to unmask (if necessary). + */ +static void +xen_intr_enable_source(struct intsrc *isrc) +{ +} + +/* + * Perform any necessary end-of-interrupt acknowledgements. + * + * \param isrc The interrupt source to EOI. + */ +static void +xen_intr_eoi_source(struct intsrc *isrc) +{ +} + +/* + * Enable and unmask the interrupt source. + * + * \param isrc The interrupt source to enable. + */ +static void +xen_intr_enable_intr(struct intsrc *base_isrc) +{ + struct xenisrc *isrc = (struct xenisrc *)base_isrc; + + evtchn_unmask_port(isrc->xi_port); +} + +/*------------------ Physical Interrupt Source PIC Functions -----------------*/ +/* + * Mask a level triggered interrupt source. + * + * \param isrc The interrupt source to mask (if necessary). + * \param eoi If non-zero, perform any necessary end-of-interrupt + * acknowledgements. + */ +static void +xen_intr_pirq_disable_source(struct intsrc *base_isrc, int eoi) +{ + struct xenisrc *isrc; + + isrc = (struct xenisrc *)base_isrc; + evtchn_mask_port(isrc->xi_port); +} + +/* + * Unmask a level triggered interrupt source. + * + * \param isrc The interrupt source to unmask (if necessary). + */ +static void +xen_intr_pirq_enable_source(struct intsrc *base_isrc) +{ + struct xenisrc *isrc; + + isrc = (struct xenisrc *)base_isrc; + evtchn_unmask_port(isrc->xi_port); +} + +/* + * Perform any necessary end-of-interrupt acknowledgements. + * + * \param isrc The interrupt source to EOI. + */ +static void +xen_intr_pirq_eoi_source(struct intsrc *base_isrc) +{ + struct xenisrc *isrc; + + /* XXX Use shared page of flags for this. */ + isrc = (struct xenisrc *)base_isrc; + if (isrc->xi_needs_eoi != 0) { + struct physdev_eoi eoi = { .irq = isrc->xi_pirq }; + + (void)HYPERVISOR_physdev_op(PHYSDEVOP_eoi, &eoi); + } +} + +/* + * Enable and unmask the interrupt source. + * + * \param isrc The interrupt source to enable. + */ +static void +xen_intr_pirq_enable_intr(struct intsrc *isrc) +{ +} + +/*--------------------------- Public Functions -------------------------------*/ +/*------- API comments for these methods can be found in xen/xenintr.h -------*/ +int +xen_intr_bind_local_port(device_t dev, evtchn_port_t local_port, + driver_filter_t filter, driver_intr_t handler, void *arg, + enum intr_type flags, xen_intr_handle_t *port_handlep) +{ + struct xenisrc *isrc; + int error; + + error = xen_intr_bind_isrc(&isrc, local_port, EVTCHN_TYPE_PORT, dev, + filter, handler, arg, flags, port_handlep); + if (error != 0) + return (error); + + /* + * The Event Channel API didn't open this port, so it is not + * responsible for closing it automatically on unbind. + */ + isrc->xi_close = 0; + return (0); +} + +int +xen_intr_alloc_and_bind_local_port(device_t dev, u_int remote_domain, + driver_filter_t filter, driver_intr_t handler, void *arg, + enum intr_type flags, xen_intr_handle_t *port_handlep) +{ + struct xenisrc *isrc; + struct evtchn_alloc_unbound alloc_unbound; + int error; + + alloc_unbound.dom = DOMID_SELF; + alloc_unbound.remote_dom = remote_domain; + error = HYPERVISOR_event_channel_op(EVTCHNOP_alloc_unbound, + &alloc_unbound); + if (error != 0) { + /* + * XXX Trap Hypercall error code Linuxisms in + * the HYPERCALL layer. + */ + return (-error); + } + + error = xen_intr_bind_isrc(&isrc, alloc_unbound.port, EVTCHN_TYPE_PORT, + dev, filter, handler, arg, flags, + port_handlep); + if (error != 0) { + evtchn_close_t close = { .port = alloc_unbound.port }; + if (HYPERVISOR_event_channel_op(EVTCHNOP_close, &close)) + panic("EVTCHNOP_close failed"); + return (error); + } + + isrc->xi_close = 1; + return (0); +} + +int +xen_intr_bind_remote_port(device_t dev, u_int remote_domain, + u_int remote_port, driver_filter_t filter, driver_intr_t handler, + void *arg, enum intr_type flags, xen_intr_handle_t *port_handlep) +{ + struct xenisrc *isrc; + struct evtchn_bind_interdomain bind_interdomain; + int error; + + bind_interdomain.remote_dom = remote_domain; + bind_interdomain.remote_port = remote_port; + error = HYPERVISOR_event_channel_op(EVTCHNOP_bind_interdomain, + &bind_interdomain); + if (error != 0) { + /* + * XXX Trap Hypercall error code Linuxisms in + * the HYPERCALL layer. + */ + return (-error); + } + + error = xen_intr_bind_isrc(&isrc, bind_interdomain.local_port, + EVTCHN_TYPE_PORT, dev, filter, handler, + arg, flags, port_handlep); + if (error) { + evtchn_close_t close = { .port = bind_interdomain.local_port }; + if (HYPERVISOR_event_channel_op(EVTCHNOP_close, &close)) + panic("EVTCHNOP_close failed"); + return (error); + } + + /* + * The Event Channel API opened this port, so it is + * responsible for closing it automatically on unbind. + */ + isrc->xi_close = 1; + return (0); +} + +int +xen_intr_bind_virq(device_t dev, u_int virq, u_int cpu, + driver_filter_t filter, driver_intr_t handler, void *arg, + enum intr_type flags, xen_intr_handle_t *port_handlep) +{ + int acpi_id = pcpu_find(cpu)->pc_acpi_id; + struct xenisrc *isrc; + struct evtchn_bind_virq bind_virq = { .virq = virq, .vcpu = acpi_id }; + int error; + + /* Ensure the target CPU is ready to handle evtchn interrupts. */ + xen_intr_intrcnt_add(cpu); + + isrc = NULL; + error = HYPERVISOR_event_channel_op(EVTCHNOP_bind_virq, &bind_virq); + if (error != 0) { + /* + * XXX Trap Hypercall error code Linuxisms in + * the HYPERCALL layer. + */ + return (-error); + } + + error = xen_intr_bind_isrc(&isrc, bind_virq.port, EVTCHN_TYPE_VIRQ, dev, + filter, handler, arg, flags, port_handlep); + if (error == 0) + error = intr_event_bind(isrc->xi_intsrc.is_event, cpu); + + if (error != 0) { + evtchn_close_t close = { .port = bind_virq.port }; + + xen_intr_unbind(*port_handlep); + if (HYPERVISOR_event_channel_op(EVTCHNOP_close, &close)) + panic("EVTCHNOP_close failed"); + return (error); + } + + if (isrc->xi_cpu != cpu) { + /* + * Too early in the boot process for the generic interrupt + * code to perform the binding. Update our event channel + * masks manually so events can't fire on the wrong cpu + * during AP startup. + */ + xen_intr_assign_cpu(&isrc->xi_intsrc, cpu_apic_ids[cpu]); + } + + /* + * The Event Channel API opened this port, so it is + * responsible for closing it automatically on unbind. + */ + isrc->xi_close = 1; + return (0); +} + +int +xen_intr_alloc_and_bind_ipi(device_t dev, u_int cpu, + driver_filter_t filter, enum intr_type flags, + xen_intr_handle_t *port_handlep) +{ + int acpi_id = pcpu_find(cpu)->pc_acpi_id; + struct xenisrc *isrc; + struct evtchn_bind_ipi bind_ipi = { .vcpu = acpi_id }; + int error; + + /* Ensure the target CPU is ready to handle evtchn interrupts. */ + xen_intr_intrcnt_add(cpu); + + isrc = NULL; + error = HYPERVISOR_event_channel_op(EVTCHNOP_bind_ipi, &bind_ipi); + if (error != 0) { + /* + * XXX Trap Hypercall error code Linuxisms in + * the HYPERCALL layer. + */ + return (-error); + } + + error = xen_intr_bind_isrc(&isrc, bind_ipi.port, EVTCHN_TYPE_IPI, + dev, filter, NULL, NULL, flags, + port_handlep); + if (error == 0) + error = intr_event_bind(isrc->xi_intsrc.is_event, cpu); + + if (error != 0) { + evtchn_close_t close = { .port = bind_ipi.port }; + + xen_intr_unbind(*port_handlep); + if (HYPERVISOR_event_channel_op(EVTCHNOP_close, &close)) + panic("EVTCHNOP_close failed"); + return (error); + } + + if (isrc->xi_cpu != cpu) { + /* + * Too early in the boot process for the generic interrupt + * code to perform the binding. Update our event channel + * masks manually so events can't fire on the wrong cpu + * during AP startup. + */ + xen_intr_assign_cpu(&isrc->xi_intsrc, cpu_apic_ids[cpu]); + } + + /* + * The Event Channel API opened this port, so it is + * responsible for closing it automatically on unbind. + */ + isrc->xi_close = 1; + return (0); +} + +int +xen_intr_describe(xen_intr_handle_t port_handle, const char *fmt, ...) +{ + char descr[MAXCOMLEN + 1]; + struct xenisrc *isrc; + va_list ap; + + isrc = xen_intr_isrc(port_handle); + if (isrc == NULL) + return (EINVAL); + + va_start(ap, fmt); + vsnprintf(descr, sizeof(descr), fmt, ap); + va_end(ap); + return (intr_describe(isrc->xi_vector, port_handle, descr)); +} + +void +xen_intr_unbind(xen_intr_handle_t *port_handlep) +{ + struct intr_handler *handler; + struct xenisrc *isrc; + + handler = *port_handlep; + *port_handlep = NULL; + isrc = xen_intr_isrc(handler); + if (isrc == NULL) + return; + + intr_remove_handler(handler); + xen_intr_release_isrc(isrc); +} + +void +xen_intr_signal(xen_intr_handle_t handle) +{ + struct xenisrc *isrc; + + isrc = xen_intr_isrc(handle); + if (isrc != NULL) { + KASSERT(isrc->xi_type == EVTCHN_TYPE_PORT || + isrc->xi_type == EVTCHN_TYPE_IPI, + ("evtchn_signal on something other than a local port")); + struct evtchn_send send = { .port = isrc->xi_port }; + (void)HYPERVISOR_event_channel_op(EVTCHNOP_send, &send); + } +} + +evtchn_port_t +xen_intr_port(xen_intr_handle_t handle) +{ + struct xenisrc *isrc; + + isrc = xen_intr_isrc(handle); + if (isrc == NULL) + return (0); + + return (isrc->xi_port); +} diff --git a/sys/xen/evtchn.h b/sys/xen/evtchn.h index 721742f6de4..00fa67e0a4d 100644 --- a/sys/xen/evtchn.h +++ b/sys/xen/evtchn.h @@ -1,94 +1,87 @@ /****************************************************************************** * evtchn.h * - * Communication via Xen event channels. - * Also definitions for the device that demuxes notifications to userspace. + * Interface to /dev/xen/evtchn. * - * Copyright (c) 2004, K A Fraser + * Copyright (c) 2003-2005, K A Fraser + * + * This file may be distributed separately from the Linux kernel, or + * incorporated into other software packages, subject to the following license: + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this source file (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. * * $FreeBSD$ */ -#ifndef __ASM_EVTCHN_H__ -#define __ASM_EVTCHN_H__ -#include -#include -#include -#include +#ifndef __XEN_EVTCHN_H__ +#define __XEN_EVTCHN_H__ /* - * LOW-LEVEL DEFINITIONS + * Bind a fresh port to VIRQ @virq. */ +#define IOCTL_EVTCHN_BIND_VIRQ \ + _IOWR('E', 4, struct ioctl_evtchn_bind_virq) +struct ioctl_evtchn_bind_virq { + unsigned int virq; + unsigned int port; +}; /* - * Unlike notify_remote_via_evtchn(), this is safe to use across - * save/restore. Notifications on a broken connection are silently dropped. + * Bind a fresh port to remote <@remote_domain, @remote_port>. */ -void notify_remote_via_irq(int irq); - - -/* Entry point for notifications into Linux subsystems. */ -void evtchn_do_upcall(struct trapframe *frame); - -/* Entry point for notifications into the userland character device. */ -void evtchn_device_upcall(int port); - -void mask_evtchn(int port); - -void unmask_evtchn(int port); - -#ifdef SMP -void rebind_evtchn_to_cpu(int port, unsigned int cpu); -#else -#define rebind_evtchn_to_cpu(port, cpu) ((void)0) -#endif - -static inline -int test_and_set_evtchn_mask(int port) -{ - shared_info_t *s = HYPERVISOR_shared_info; - return synch_test_and_set_bit(port, s->evtchn_mask); -} - -static inline void -clear_evtchn(int port) -{ - shared_info_t *s = HYPERVISOR_shared_info; - synch_clear_bit(port, &s->evtchn_pending[0]); -} - -static inline void -notify_remote_via_evtchn(int port) -{ - struct evtchn_send send = { .port = port }; - (void)HYPERVISOR_event_channel_op(EVTCHNOP_send, &send); -} +#define IOCTL_EVTCHN_BIND_INTERDOMAIN \ + _IOWR('E', 5, struct ioctl_evtchn_bind_interdomain) +struct ioctl_evtchn_bind_interdomain { + unsigned int remote_domain, remote_port; + unsigned int port; +}; /* - * Use these to access the event channel underlying the IRQ handle returned - * by bind_*_to_irqhandler(). + * Allocate a fresh port for binding to @remote_domain. */ -int irq_to_evtchn_port(int irq); - -void ipi_pcpu(unsigned int cpu, int vector); +#define IOCTL_EVTCHN_BIND_UNBOUND_PORT \ + _IOWR('E', 6, struct ioctl_evtchn_bind_unbound_port) +struct ioctl_evtchn_bind_unbound_port { + unsigned int remote_domain; + unsigned int port; +}; /* - * CHARACTER-DEVICE DEFINITIONS + * Unbind previously allocated @port. */ +#define IOCTL_EVTCHN_UNBIND \ + _IOW('E', 7, struct ioctl_evtchn_unbind) +struct ioctl_evtchn_unbind { + unsigned int port; +}; -#define PORT_NORMAL 0x0000 -#define PORT_EXCEPTION 0x8000 -#define PORTIDX_MASK 0x7fff +/* + * Send event to previously allocated @port. + */ +#define IOCTL_EVTCHN_NOTIFY \ + _IOW('E', 8, struct ioctl_evtchn_notify) +struct ioctl_evtchn_notify { + unsigned int port; +}; -/* /dev/xen/evtchn resides at device number major=10, minor=200 */ -#define EVTCHN_MINOR 200 +/* Clear and reinitialise the event buffer. Clear error condition. */ +#define IOCTL_EVTCHN_RESET \ + _IO('E', 9) -/* /dev/xen/evtchn ioctls: */ -/* EVTCHN_RESET: Clear and reinit the event buffer. Clear error condition. */ -#define EVTCHN_RESET _IO('E', 1) -/* EVTCHN_BIND: Bind to the specified event-channel port. */ -#define EVTCHN_BIND _IO('E', 2) -/* EVTCHN_UNBIND: Unbind from the specified event-channel port. */ -#define EVTCHN_UNBIND _IO('E', 3) - -#endif /* __ASM_EVTCHN_H__ */ +#endif /* __XEN_EVTCHN_H__ */ diff --git a/sys/xen/evtchn/evtchn.c b/sys/xen/evtchn/evtchn.c deleted file mode 100644 index baff9aaa2c8..00000000000 --- a/sys/xen/evtchn/evtchn.c +++ /dev/null @@ -1,1141 +0,0 @@ -/****************************************************************************** - * evtchn.c - * - * Communication via Xen event channels. - * - * Copyright (c) 2002-2005, K A Fraser - * Copyright (c) 2005-2006 Kip Macy - */ - -#include -__FBSDID("$FreeBSD$"); - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -static inline unsigned long __ffs(unsigned long word) -{ - __asm__("bsfl %1,%0" - :"=r" (word) - :"rm" (word)); - return word; -} - -/* - * irq_mapping_update_lock: in order to allow an interrupt to occur in a critical - * section, to set pcpu->ipending (etc...) properly, we - * must be able to get the icu lock, so it can't be - * under witness. - */ -static struct mtx irq_mapping_update_lock; -MTX_SYSINIT(irq_mapping_update_lock, &irq_mapping_update_lock, "xp", MTX_SPIN); - -static struct xenpic *xp; -struct xenpic_intsrc { - struct intsrc xp_intsrc; - void *xp_cookie; - uint8_t xp_vector; - boolean_t xp_masked; -}; - -struct xenpic { - struct pic *xp_dynirq_pic; - struct pic *xp_pirq_pic; - uint16_t xp_numintr; - struct xenpic_intsrc xp_pins[0]; -}; - -#define TODO printf("%s: not implemented!\n", __func__) - -/* IRQ <-> event-channel mappings. */ -static int evtchn_to_irq[NR_EVENT_CHANNELS]; - -/* Packed IRQ information: binding type, sub-type index, and event channel. */ -static uint32_t irq_info[NR_IRQS]; -/* Binding types. */ -enum { - IRQT_UNBOUND, - IRQT_PIRQ, - IRQT_VIRQ, - IRQT_IPI, - IRQT_LOCAL_PORT, - IRQT_CALLER_PORT, - _IRQT_COUNT - -}; - - -#define _IRQT_BITS 4 -#define _EVTCHN_BITS 12 -#define _INDEX_BITS (32 - _IRQT_BITS - _EVTCHN_BITS) - -/* Constructor for packed IRQ information. */ -static inline uint32_t -mk_irq_info(uint32_t type, uint32_t index, uint32_t evtchn) -{ - - return ((type << (32 - _IRQT_BITS)) | (index << _EVTCHN_BITS) | evtchn); -} - -/* Constructor for packed IRQ information. */ - -/* Convenient shorthand for packed representation of an unbound IRQ. */ -#define IRQ_UNBOUND mk_irq_info(IRQT_UNBOUND, 0, 0) - -/* - * Accessors for packed IRQ information. - */ - -static inline unsigned int evtchn_from_irq(int irq) -{ - return irq_info[irq] & ((1U << _EVTCHN_BITS) - 1); -} - -static inline unsigned int index_from_irq(int irq) -{ - return (irq_info[irq] >> _EVTCHN_BITS) & ((1U << _INDEX_BITS) - 1); -} - -static inline unsigned int type_from_irq(int irq) -{ - return irq_info[irq] >> (32 - _IRQT_BITS); -} - - -/* IRQ <-> VIRQ mapping. */ - -/* IRQ <-> IPI mapping. */ -#ifndef NR_IPIS -#ifdef SMP -#error "NR_IPIS not defined" -#endif -#define NR_IPIS 1 -#endif - -/* Bitmap indicating which PIRQs require Xen to be notified on unmask. */ -static unsigned long pirq_needs_unmask_notify[NR_PIRQS/sizeof(unsigned long)]; - -/* Reference counts for bindings to IRQs. */ -static int irq_bindcount[NR_IRQS]; - -#define VALID_EVTCHN(_chn) ((_chn) != 0) - -#ifdef SMP - -static uint8_t cpu_evtchn[NR_EVENT_CHANNELS]; -static unsigned long cpu_evtchn_mask[XEN_LEGACY_MAX_VCPUS][NR_EVENT_CHANNELS/LONG_BIT]; - -#define active_evtchns(cpu,sh,idx) \ - ((sh)->evtchn_pending[idx] & \ - cpu_evtchn_mask[cpu][idx] & \ - ~(sh)->evtchn_mask[idx]) - -static void bind_evtchn_to_cpu(unsigned int chn, unsigned int cpu) -{ - clear_bit(chn, (unsigned long *)cpu_evtchn_mask[cpu_evtchn[chn]]); - set_bit(chn, (unsigned long *)cpu_evtchn_mask[cpu]); - cpu_evtchn[chn] = cpu; -} - -static void init_evtchn_cpu_bindings(void) -{ - /* By default all event channels notify CPU#0. */ - memset(cpu_evtchn, 0, sizeof(cpu_evtchn)); - memset(cpu_evtchn_mask[0], ~0, sizeof(cpu_evtchn_mask[0])); -} - -#define cpu_from_evtchn(evtchn) (cpu_evtchn[evtchn]) - -#else - -#define active_evtchns(cpu,sh,idx) \ - ((sh)->evtchn_pending[idx] & \ - ~(sh)->evtchn_mask[idx]) -#define bind_evtchn_to_cpu(chn,cpu) ((void)0) -#define init_evtchn_cpu_bindings() ((void)0) -#define cpu_from_evtchn(evtchn) (0) - -#endif - - -/* - * Force a proper event-channel callback from Xen after clearing the - * callback mask. We do this in a very simple manner, by making a call - * down into Xen. The pending flag will be checked by Xen on return. - */ -void force_evtchn_callback(void) -{ - (void)HYPERVISOR_xen_version(0, NULL); -} - -void -evtchn_do_upcall(struct trapframe *frame) -{ - unsigned long l1, l2; - unsigned int l1i, l2i, port; - int irq, cpu; - shared_info_t *s; - vcpu_info_t *vcpu_info; - - cpu = PCPU_GET(cpuid); - s = HYPERVISOR_shared_info; - vcpu_info = &s->vcpu_info[cpu]; - - vcpu_info->evtchn_upcall_pending = 0; - - /* NB. No need for a barrier here -- XCHG is a barrier on x86. */ - l1 = xen_xchg(&vcpu_info->evtchn_pending_sel, 0); - - while (l1 != 0) { - l1i = __ffs(l1); - l1 &= ~(1 << l1i); - - while ((l2 = active_evtchns(cpu, s, l1i)) != 0) { - l2i = __ffs(l2); - - port = (l1i * LONG_BIT) + l2i; - if ((irq = evtchn_to_irq[port]) != -1) { - struct intsrc *isrc = intr_lookup_source(irq); - /* - * ack - */ - mask_evtchn(port); - clear_evtchn(port); - - intr_execute_handlers(isrc, frame); - } else { - evtchn_device_upcall(port); - } - } - } -} - -/* - * Send an IPI from the current CPU to the destination CPU. - */ -void -ipi_pcpu(unsigned int cpu, int vector) -{ - int irq; - - irq = pcpu_find(cpu)->pc_ipi_to_irq[vector]; - - notify_remote_via_irq(irq); -} - -static int -find_unbound_irq(void) -{ - int dynirq, irq; - - for (dynirq = 0; dynirq < NR_IRQS; dynirq++) { - irq = dynirq_to_irq(dynirq); - if (irq_bindcount[irq] == 0) - break; - } - - if (irq == NR_IRQS) - panic("No available IRQ to bind to: increase NR_IRQS!\n"); - - return (irq); -} - -static int -bind_caller_port_to_irq(unsigned int caller_port, int * port) -{ - int irq; - - mtx_lock_spin(&irq_mapping_update_lock); - - if ((irq = evtchn_to_irq[caller_port]) == -1) { - if ((irq = find_unbound_irq()) < 0) - goto out; - - evtchn_to_irq[caller_port] = irq; - irq_info[irq] = mk_irq_info(IRQT_CALLER_PORT, 0, caller_port); - } - - irq_bindcount[irq]++; - *port = caller_port; - - out: - mtx_unlock_spin(&irq_mapping_update_lock); - return irq; -} - -static int -bind_local_port_to_irq(unsigned int local_port, int * port) -{ - int irq; - - mtx_lock_spin(&irq_mapping_update_lock); - - KASSERT(evtchn_to_irq[local_port] == -1, - ("evtchn_to_irq inconsistent")); - - if ((irq = find_unbound_irq()) < 0) { - struct evtchn_close close = { .port = local_port }; - HYPERVISOR_event_channel_op(EVTCHNOP_close, &close); - - goto out; - } - - evtchn_to_irq[local_port] = irq; - irq_info[irq] = mk_irq_info(IRQT_LOCAL_PORT, 0, local_port); - irq_bindcount[irq]++; - *port = local_port; - - out: - mtx_unlock_spin(&irq_mapping_update_lock); - return irq; -} - -static int -bind_listening_port_to_irq(unsigned int remote_domain, int * port) -{ - struct evtchn_alloc_unbound alloc_unbound; - int err; - - alloc_unbound.dom = DOMID_SELF; - alloc_unbound.remote_dom = remote_domain; - - err = HYPERVISOR_event_channel_op(EVTCHNOP_alloc_unbound, - &alloc_unbound); - - return err ? : bind_local_port_to_irq(alloc_unbound.port, port); -} - -static int -bind_interdomain_evtchn_to_irq(unsigned int remote_domain, - unsigned int remote_port, int * port) -{ - struct evtchn_bind_interdomain bind_interdomain; - int err; - - bind_interdomain.remote_dom = remote_domain; - bind_interdomain.remote_port = remote_port; - - err = HYPERVISOR_event_channel_op(EVTCHNOP_bind_interdomain, - &bind_interdomain); - - return err ? : bind_local_port_to_irq(bind_interdomain.local_port, port); -} - -static int -bind_virq_to_irq(unsigned int virq, unsigned int cpu, int * port) -{ - struct evtchn_bind_virq bind_virq; - int evtchn = 0, irq; - - mtx_lock_spin(&irq_mapping_update_lock); - - if ((irq = pcpu_find(cpu)->pc_virq_to_irq[virq]) == -1) { - if ((irq = find_unbound_irq()) < 0) - goto out; - - bind_virq.virq = virq; - bind_virq.vcpu = cpu; - HYPERVISOR_event_channel_op(EVTCHNOP_bind_virq, &bind_virq); - - evtchn = bind_virq.port; - - evtchn_to_irq[evtchn] = irq; - irq_info[irq] = mk_irq_info(IRQT_VIRQ, virq, evtchn); - - pcpu_find(cpu)->pc_virq_to_irq[virq] = irq; - - bind_evtchn_to_cpu(evtchn, cpu); - } - - irq_bindcount[irq]++; - *port = evtchn; -out: - mtx_unlock_spin(&irq_mapping_update_lock); - - return irq; -} - - -static int -bind_ipi_to_irq(unsigned int ipi, unsigned int cpu, int * port) -{ - struct evtchn_bind_ipi bind_ipi; - int irq; - int evtchn = 0; - - mtx_lock_spin(&irq_mapping_update_lock); - - if ((irq = pcpu_find(cpu)->pc_ipi_to_irq[ipi]) == -1) { - if ((irq = find_unbound_irq()) < 0) - goto out; - - bind_ipi.vcpu = cpu; - HYPERVISOR_event_channel_op(EVTCHNOP_bind_ipi, &bind_ipi); - evtchn = bind_ipi.port; - - evtchn_to_irq[evtchn] = irq; - irq_info[irq] = mk_irq_info(IRQT_IPI, ipi, evtchn); - - pcpu_find(cpu)->pc_ipi_to_irq[ipi] = irq; - - bind_evtchn_to_cpu(evtchn, cpu); - } - irq_bindcount[irq]++; - *port = evtchn; -out: - - mtx_unlock_spin(&irq_mapping_update_lock); - - return irq; -} - - -static void -unbind_from_irq(int irq) -{ - struct evtchn_close close; - int evtchn = evtchn_from_irq(irq); - int cpu; - - mtx_lock_spin(&irq_mapping_update_lock); - - if ((--irq_bindcount[irq] == 0) && VALID_EVTCHN(evtchn)) { - close.port = evtchn; - HYPERVISOR_event_channel_op(EVTCHNOP_close, &close); - - switch (type_from_irq(irq)) { - case IRQT_VIRQ: - cpu = cpu_from_evtchn(evtchn); - pcpu_find(cpu)->pc_virq_to_irq[index_from_irq(irq)] = -1; - break; - case IRQT_IPI: - cpu = cpu_from_evtchn(evtchn); - pcpu_find(cpu)->pc_ipi_to_irq[index_from_irq(irq)] = -1; - break; - default: - break; - } - - /* Closed ports are implicitly re-bound to VCPU0. */ - bind_evtchn_to_cpu(evtchn, 0); - - evtchn_to_irq[evtchn] = -1; - irq_info[irq] = IRQ_UNBOUND; - } - - mtx_unlock_spin(&irq_mapping_update_lock); -} - -int -bind_caller_port_to_irqhandler(unsigned int caller_port, - const char *devname, driver_intr_t handler, void *arg, - unsigned long irqflags, unsigned int *irqp) -{ - unsigned int irq; - int port = -1; - int error; - - irq = bind_caller_port_to_irq(caller_port, &port); - intr_register_source(&xp->xp_pins[irq].xp_intsrc); - error = intr_add_handler(devname, irq, NULL, handler, arg, irqflags, - &xp->xp_pins[irq].xp_cookie); - - if (error) { - unbind_from_irq(irq); - return (error); - } - if (port != -1) - unmask_evtchn(port); - - if (irqp) - *irqp = irq; - - return (0); -} - -int -bind_listening_port_to_irqhandler(unsigned int remote_domain, - const char *devname, driver_intr_t handler, void *arg, - unsigned long irqflags, unsigned int *irqp) -{ - unsigned int irq; - int port = -1; - int error; - - irq = bind_listening_port_to_irq(remote_domain, &port); - intr_register_source(&xp->xp_pins[irq].xp_intsrc); - error = intr_add_handler(devname, irq, NULL, handler, arg, irqflags, - &xp->xp_pins[irq].xp_cookie); - if (error) { - unbind_from_irq(irq); - return (error); - } - if (port != -1) - unmask_evtchn(port); - if (irqp) - *irqp = irq; - - return (0); -} - -int -bind_interdomain_evtchn_to_irqhandler(unsigned int remote_domain, - unsigned int remote_port, const char *devname, - driver_intr_t handler, void *arg, unsigned long irqflags, - unsigned int *irqp) -{ - unsigned int irq; - int port = -1; - int error; - - irq = bind_interdomain_evtchn_to_irq(remote_domain, remote_port, &port); - intr_register_source(&xp->xp_pins[irq].xp_intsrc); - error = intr_add_handler(devname, irq, NULL, handler, arg, - irqflags, &xp->xp_pins[irq].xp_cookie); - if (error) { - unbind_from_irq(irq); - return (error); - } - if (port != -1) - unmask_evtchn(port); - - if (irqp) - *irqp = irq; - return (0); -} - -int -bind_virq_to_irqhandler(unsigned int virq, unsigned int cpu, - const char *devname, driver_filter_t filter, driver_intr_t handler, - void *arg, unsigned long irqflags, unsigned int *irqp) -{ - unsigned int irq; - int port = -1; - int error; - - irq = bind_virq_to_irq(virq, cpu, &port); - intr_register_source(&xp->xp_pins[irq].xp_intsrc); - error = intr_add_handler(devname, irq, filter, handler, - arg, irqflags, &xp->xp_pins[irq].xp_cookie); - if (error) { - unbind_from_irq(irq); - return (error); - } - if (port != -1) - unmask_evtchn(port); - - if (irqp) - *irqp = irq; - return (0); -} - -int -bind_ipi_to_irqhandler(unsigned int ipi, unsigned int cpu, - const char *devname, driver_filter_t filter, - unsigned long irqflags, unsigned int *irqp) -{ - unsigned int irq; - int port = -1; - int error; - - irq = bind_ipi_to_irq(ipi, cpu, &port); - intr_register_source(&xp->xp_pins[irq].xp_intsrc); - error = intr_add_handler(devname, irq, filter, NULL, - NULL, irqflags, &xp->xp_pins[irq].xp_cookie); - if (error) { - unbind_from_irq(irq); - return (error); - } - if (port != -1) - unmask_evtchn(port); - - if (irqp) - *irqp = irq; - return (0); -} - -void -unbind_from_irqhandler(unsigned int irq) -{ - intr_remove_handler(xp->xp_pins[irq].xp_cookie); - unbind_from_irq(irq); -} - -#if 0 -/* Rebind an evtchn so that it gets delivered to a specific cpu */ -static void -rebind_irq_to_cpu(unsigned irq, unsigned tcpu) -{ - evtchn_op_t op = { .cmd = EVTCHNOP_bind_vcpu }; - int evtchn; - - mtx_lock_spin(&irq_mapping_update_lock); - - evtchn = evtchn_from_irq(irq); - if (!VALID_EVTCHN(evtchn)) { - mtx_unlock_spin(&irq_mapping_update_lock); - return; - } - - /* Send future instances of this interrupt to other vcpu. */ - bind_vcpu.port = evtchn; - bind_vcpu.vcpu = tcpu; - - /* - * If this fails, it usually just indicates that we're dealing with a - * virq or IPI channel, which don't actually need to be rebound. Ignore - * it, but don't do the xenlinux-level rebind in that case. - */ - if (HYPERVISOR_event_channel_op(&op) >= 0) - bind_evtchn_to_cpu(evtchn, tcpu); - - mtx_unlock_spin(&irq_mapping_update_lock); - -} - -static void set_affinity_irq(unsigned irq, cpumask_t dest) -{ - unsigned tcpu = ffs(dest) - 1; - rebind_irq_to_cpu(irq, tcpu); -} -#endif - -/* - * Interface to generic handling in intr_machdep.c - */ - - -/*------------ interrupt handling --------------------------------------*/ -#define TODO printf("%s: not implemented!\n", __func__) - - -static void xenpic_dynirq_enable_source(struct intsrc *isrc); -static void xenpic_dynirq_disable_source(struct intsrc *isrc, int); -static void xenpic_dynirq_eoi_source(struct intsrc *isrc); -static void xenpic_dynirq_enable_intr(struct intsrc *isrc); -static void xenpic_dynirq_disable_intr(struct intsrc *isrc); - -static void xenpic_pirq_enable_source(struct intsrc *isrc); -static void xenpic_pirq_disable_source(struct intsrc *isrc, int); -static void xenpic_pirq_eoi_source(struct intsrc *isrc); -static void xenpic_pirq_enable_intr(struct intsrc *isrc); - - -static int xenpic_vector(struct intsrc *isrc); -static int xenpic_source_pending(struct intsrc *isrc); -static void xenpic_suspend(struct pic* pic); -static void xenpic_resume(struct pic* pic); -static int xenpic_assign_cpu(struct intsrc *, u_int apic_id); - - -struct pic xenpic_dynirq_template = { - .pic_enable_source = xenpic_dynirq_enable_source, - .pic_disable_source = xenpic_dynirq_disable_source, - .pic_eoi_source = xenpic_dynirq_eoi_source, - .pic_enable_intr = xenpic_dynirq_enable_intr, - .pic_disable_intr = xenpic_dynirq_disable_intr, - .pic_vector = xenpic_vector, - .pic_source_pending = xenpic_source_pending, - .pic_suspend = xenpic_suspend, - .pic_resume = xenpic_resume -}; - -struct pic xenpic_pirq_template = { - .pic_enable_source = xenpic_pirq_enable_source, - .pic_disable_source = xenpic_pirq_disable_source, - .pic_eoi_source = xenpic_pirq_eoi_source, - .pic_enable_intr = xenpic_pirq_enable_intr, - .pic_vector = xenpic_vector, - .pic_source_pending = xenpic_source_pending, - .pic_suspend = xenpic_suspend, - .pic_resume = xenpic_resume, - .pic_assign_cpu = xenpic_assign_cpu -}; - - - -void -xenpic_dynirq_enable_source(struct intsrc *isrc) -{ - unsigned int irq; - struct xenpic_intsrc *xp; - - xp = (struct xenpic_intsrc *)isrc; - - mtx_lock_spin(&irq_mapping_update_lock); - if (xp->xp_masked) { - irq = xenpic_vector(isrc); - unmask_evtchn(evtchn_from_irq(irq)); - xp->xp_masked = FALSE; - } - mtx_unlock_spin(&irq_mapping_update_lock); -} - -static void -xenpic_dynirq_disable_source(struct intsrc *isrc, int foo) -{ - unsigned int irq; - struct xenpic_intsrc *xp; - - xp = (struct xenpic_intsrc *)isrc; - - mtx_lock_spin(&irq_mapping_update_lock); - if (!xp->xp_masked) { - irq = xenpic_vector(isrc); - mask_evtchn(evtchn_from_irq(irq)); - xp->xp_masked = TRUE; - } - mtx_unlock_spin(&irq_mapping_update_lock); -} - -static void -xenpic_dynirq_enable_intr(struct intsrc *isrc) -{ - unsigned int irq; - struct xenpic_intsrc *xp; - - xp = (struct xenpic_intsrc *)isrc; - mtx_lock_spin(&irq_mapping_update_lock); - xp->xp_masked = 0; - irq = xenpic_vector(isrc); - unmask_evtchn(evtchn_from_irq(irq)); - mtx_unlock_spin(&irq_mapping_update_lock); -} - -static void -xenpic_dynirq_disable_intr(struct intsrc *isrc) -{ - unsigned int irq; - struct xenpic_intsrc *xp; - - xp = (struct xenpic_intsrc *)isrc; - mtx_lock_spin(&irq_mapping_update_lock); - irq = xenpic_vector(isrc); - mask_evtchn(evtchn_from_irq(irq)); - xp->xp_masked = 1; - mtx_unlock_spin(&irq_mapping_update_lock); -} - -static void -xenpic_dynirq_eoi_source(struct intsrc *isrc) -{ - unsigned int irq; - struct xenpic_intsrc *xp; - - xp = (struct xenpic_intsrc *)isrc; - mtx_lock_spin(&irq_mapping_update_lock); - xp->xp_masked = 0; - irq = xenpic_vector(isrc); - unmask_evtchn(evtchn_from_irq(irq)); - mtx_unlock_spin(&irq_mapping_update_lock); -} - -static int -xenpic_vector(struct intsrc *isrc) -{ - struct xenpic_intsrc *pin; - - pin = (struct xenpic_intsrc *)isrc; - //printf("xenpic_vector(): isrc=%p,vector=%u\n", pin, pin->xp_vector); - - return (pin->xp_vector); -} - -static int -xenpic_source_pending(struct intsrc *isrc) -{ - struct xenpic_intsrc *pin = (struct xenpic_intsrc *)isrc; - - /* XXXEN: TODO */ - printf("xenpic_source_pending(): vector=%x,masked=%x\n", - pin->xp_vector, pin->xp_masked); - -/* notify_remote_via_evtchn(pin->xp_vector); // XXX RS: Is this correct? */ - return 0; -} - -static void -xenpic_suspend(struct pic* pic) -{ - TODO; -} - -static void -xenpic_resume(struct pic* pic) -{ - TODO; -} - -static int -xenpic_assign_cpu(struct intsrc *isrc, u_int apic_id) -{ - TODO; - return (EOPNOTSUPP); -} - -void -notify_remote_via_irq(int irq) -{ - int evtchn = evtchn_from_irq(irq); - - if (VALID_EVTCHN(evtchn)) - notify_remote_via_evtchn(evtchn); - else - panic("invalid evtchn %d", irq); -} - -/* required for support of physical devices */ -static inline void -pirq_unmask_notify(int pirq) -{ - struct physdev_eoi eoi = { .irq = pirq }; - - if (unlikely(test_bit(pirq, &pirq_needs_unmask_notify[0]))) { - (void)HYPERVISOR_physdev_op(PHYSDEVOP_eoi, &eoi); - } -} - -static inline void -pirq_query_unmask(int pirq) -{ - struct physdev_irq_status_query irq_status_query; - - irq_status_query.irq = pirq; - (void)HYPERVISOR_physdev_op(PHYSDEVOP_IRQ_STATUS_QUERY, &irq_status_query); - clear_bit(pirq, &pirq_needs_unmask_notify[0]); - if ( irq_status_query.flags & PHYSDEVOP_IRQ_NEEDS_UNMASK_NOTIFY ) - set_bit(pirq, &pirq_needs_unmask_notify[0]); -} - -/* - * On startup, if there is no action associated with the IRQ then we are - * probing. In this case we should not share with others as it will confuse us. - */ -#define probing_irq(_irq) (intr_lookup_source(irq) == NULL) - -static void -xenpic_pirq_enable_intr(struct intsrc *isrc) -{ - struct evtchn_bind_pirq bind_pirq; - int evtchn; - unsigned int irq; - - mtx_lock_spin(&irq_mapping_update_lock); - irq = xenpic_vector(isrc); - evtchn = evtchn_from_irq(irq); - - if (VALID_EVTCHN(evtchn)) - goto out; - - bind_pirq.pirq = irq; - /* NB. We are happy to share unless we are probing. */ - bind_pirq.flags = probing_irq(irq) ? 0 : BIND_PIRQ__WILL_SHARE; - - if (HYPERVISOR_event_channel_op(EVTCHNOP_bind_pirq, &bind_pirq) != 0) { -#ifndef XEN_PRIVILEGED_GUEST - panic("unexpected pirq call"); -#endif - if (!probing_irq(irq)) /* Some failures are expected when probing. */ - printf("Failed to obtain physical IRQ %d\n", irq); - mtx_unlock_spin(&irq_mapping_update_lock); - return; - } - evtchn = bind_pirq.port; - - pirq_query_unmask(irq_to_pirq(irq)); - - bind_evtchn_to_cpu(evtchn, 0); - evtchn_to_irq[evtchn] = irq; - irq_info[irq] = mk_irq_info(IRQT_PIRQ, irq, evtchn); - - out: - unmask_evtchn(evtchn); - pirq_unmask_notify(irq_to_pirq(irq)); - mtx_unlock_spin(&irq_mapping_update_lock); -} - -static void -xenpic_pirq_enable_source(struct intsrc *isrc) -{ - int evtchn; - unsigned int irq; - - mtx_lock_spin(&irq_mapping_update_lock); - irq = xenpic_vector(isrc); - evtchn = evtchn_from_irq(irq); - - if (!VALID_EVTCHN(evtchn)) - goto done; - - unmask_evtchn(evtchn); - pirq_unmask_notify(irq_to_pirq(irq)); - done: - mtx_unlock_spin(&irq_mapping_update_lock); -} - -static void -xenpic_pirq_disable_source(struct intsrc *isrc, int eoi) -{ - int evtchn; - unsigned int irq; - - mtx_lock_spin(&irq_mapping_update_lock); - irq = xenpic_vector(isrc); - evtchn = evtchn_from_irq(irq); - - if (!VALID_EVTCHN(evtchn)) - goto done; - - mask_evtchn(evtchn); - done: - mtx_unlock_spin(&irq_mapping_update_lock); -} - - -static void -xenpic_pirq_eoi_source(struct intsrc *isrc) -{ - int evtchn; - unsigned int irq; - - mtx_lock_spin(&irq_mapping_update_lock); - irq = xenpic_vector(isrc); - evtchn = evtchn_from_irq(irq); - - if (!VALID_EVTCHN(evtchn)) - goto done; - - unmask_evtchn(evtchn); - pirq_unmask_notify(irq_to_pirq(irq)); - done: - mtx_unlock_spin(&irq_mapping_update_lock); -} - -int -irq_to_evtchn_port(int irq) -{ - return evtchn_from_irq(irq); -} - -void -mask_evtchn(int port) -{ - shared_info_t *s = HYPERVISOR_shared_info; - synch_set_bit(port, &s->evtchn_mask[0]); -} - -void -unmask_evtchn(int port) -{ - shared_info_t *s = HYPERVISOR_shared_info; - unsigned int cpu = PCPU_GET(cpuid); - vcpu_info_t *vcpu_info = &s->vcpu_info[cpu]; - - /* Slow path (hypercall) if this is a non-local port. */ - if (unlikely(cpu != cpu_from_evtchn(port))) { - struct evtchn_unmask unmask = { .port = port }; - (void)HYPERVISOR_event_channel_op(EVTCHNOP_unmask, &unmask); - return; - } - - synch_clear_bit(port, &s->evtchn_mask); - - /* - * The following is basically the equivalent of 'hw_resend_irq'. Just - * like a real IO-APIC we 'lose the interrupt edge' if the channel is - * masked. - */ - if (synch_test_bit(port, &s->evtchn_pending) && - !synch_test_and_set_bit(port / LONG_BIT, - &vcpu_info->evtchn_pending_sel)) { - vcpu_info->evtchn_upcall_pending = 1; - if (!vcpu_info->evtchn_upcall_mask) - force_evtchn_callback(); - } -} - -void irq_resume(void) -{ - evtchn_op_t op; - int cpu, pirq, virq, ipi, irq, evtchn; - - struct evtchn_bind_virq bind_virq; - struct evtchn_bind_ipi bind_ipi; - - init_evtchn_cpu_bindings(); - - /* New event-channel space is not 'live' yet. */ - for (evtchn = 0; evtchn < NR_EVENT_CHANNELS; evtchn++) - mask_evtchn(evtchn); - - /* Check that no PIRQs are still bound. */ - for (pirq = 0; pirq < NR_PIRQS; pirq++) { - KASSERT(irq_info[pirq_to_irq(pirq)] == IRQ_UNBOUND, - ("pirq_to_irq inconsistent")); - } - - /* Secondary CPUs must have no VIRQ or IPI bindings. */ - for (cpu = 1; cpu < XEN_LEGACY_MAX_VCPUS; cpu++) { - for (virq = 0; virq < NR_VIRQS; virq++) { - KASSERT(pcpu_find(cpu)->pc_virq_to_irq[virq] == -1, - ("virq_to_irq inconsistent")); - } - for (ipi = 0; ipi < NR_IPIS; ipi++) { - KASSERT(pcpu_find(cpu)->pc_ipi_to_irq[ipi] == -1, - ("ipi_to_irq inconsistent")); - } - } - - /* No IRQ <-> event-channel mappings. */ - for (irq = 0; irq < NR_IRQS; irq++) - irq_info[irq] &= ~0xFFFF; /* zap event-channel binding */ - for (evtchn = 0; evtchn < NR_EVENT_CHANNELS; evtchn++) - evtchn_to_irq[evtchn] = -1; - - /* Primary CPU: rebind VIRQs automatically. */ - for (virq = 0; virq < NR_VIRQS; virq++) { - if ((irq = pcpu_find(0)->pc_virq_to_irq[virq]) == -1) - continue; - - KASSERT(irq_info[irq] == mk_irq_info(IRQT_VIRQ, virq, 0), - ("irq_info inconsistent")); - - /* Get a new binding from Xen. */ - bind_virq.virq = virq; - bind_virq.vcpu = 0; - HYPERVISOR_event_channel_op(EVTCHNOP_bind_virq, &bind_virq); - evtchn = bind_virq.port; - - /* Record the new mapping. */ - evtchn_to_irq[evtchn] = irq; - irq_info[irq] = mk_irq_info(IRQT_VIRQ, virq, evtchn); - - /* Ready for use. */ - unmask_evtchn(evtchn); - } - - /* Primary CPU: rebind IPIs automatically. */ - for (ipi = 0; ipi < NR_IPIS; ipi++) { - if ((irq = pcpu_find(0)->pc_ipi_to_irq[ipi]) == -1) - continue; - - KASSERT(irq_info[irq] == mk_irq_info(IRQT_IPI, ipi, 0), - ("irq_info inconsistent")); - - /* Get a new binding from Xen. */ - memset(&op, 0, sizeof(op)); - bind_ipi.vcpu = 0; - HYPERVISOR_event_channel_op(EVTCHNOP_bind_ipi, &bind_ipi); - evtchn = bind_ipi.port; - - /* Record the new mapping. */ - evtchn_to_irq[evtchn] = irq; - irq_info[irq] = mk_irq_info(IRQT_IPI, ipi, evtchn); - - /* Ready for use. */ - unmask_evtchn(evtchn); - } -} - -static void -evtchn_init(void *dummy __unused) -{ - int i, cpu; - struct xenpic_intsrc *pin, *tpin; - - - init_evtchn_cpu_bindings(); - - /* No VIRQ or IPI bindings. */ - for (cpu = 0; cpu < mp_ncpus; cpu++) { - for (i = 0; i < NR_VIRQS; i++) - pcpu_find(cpu)->pc_virq_to_irq[i] = -1; - for (i = 0; i < NR_IPIS; i++) - pcpu_find(cpu)->pc_ipi_to_irq[i] = -1; - } - - /* No event-channel -> IRQ mappings. */ - for (i = 0; i < NR_EVENT_CHANNELS; i++) { - evtchn_to_irq[i] = -1; - mask_evtchn(i); /* No event channels are 'live' right now. */ - } - - /* No IRQ -> event-channel mappings. */ - for (i = 0; i < NR_IRQS; i++) - irq_info[i] = IRQ_UNBOUND; - - xp = malloc(sizeof(struct xenpic) + NR_IRQS*sizeof(struct xenpic_intsrc), - M_DEVBUF, M_WAITOK); - - xp->xp_dynirq_pic = &xenpic_dynirq_template; - xp->xp_pirq_pic = &xenpic_pirq_template; - xp->xp_numintr = NR_IRQS; - bzero(xp->xp_pins, sizeof(struct xenpic_intsrc) * NR_IRQS); - - - /* We need to register our PIC's beforehand */ - if (intr_register_pic(&xenpic_pirq_template)) - panic("XEN: intr_register_pic() failure"); - if (intr_register_pic(&xenpic_dynirq_template)) - panic("XEN: intr_register_pic() failure"); - - /* - * Initialize the dynamic IRQ's - we initialize the structures, but - * we do not bind them (bind_evtchn_to_irqhandle() does this) - */ - pin = xp->xp_pins; - for (i = 0; i < NR_DYNIRQS; i++) { - /* Dynamic IRQ space is currently unbound. Zero the refcnts. */ - irq_bindcount[dynirq_to_irq(i)] = 0; - - tpin = &pin[dynirq_to_irq(i)]; - tpin->xp_intsrc.is_pic = xp->xp_dynirq_pic; - tpin->xp_vector = dynirq_to_irq(i); - - } - /* - * Now, we go ahead and claim every PIRQ there is. - */ - pin = xp->xp_pins; - for (i = 0; i < NR_PIRQS; i++) { - /* Dynamic IRQ space is currently unbound. Zero the refcnts. */ - irq_bindcount[pirq_to_irq(i)] = 0; - -#ifdef RTC_IRQ - /* If not domain 0, force our RTC driver to fail its probe. */ - if ((i == RTC_IRQ) && - !(xen_start_info->flags & SIF_INITDOMAIN)) - continue; -#endif - tpin = &pin[pirq_to_irq(i)]; - tpin->xp_intsrc.is_pic = xp->xp_pirq_pic; - tpin->xp_vector = pirq_to_irq(i); - - } -} - -SYSINIT(evtchn_init, SI_SUB_INTR, SI_ORDER_MIDDLE, evtchn_init, NULL); - diff --git a/sys/xen/evtchn/evtchn_dev.c b/sys/xen/evtchn/evtchn_dev.c index ab3bd173304..9da26421e52 100644 --- a/sys/xen/evtchn/evtchn_dev.c +++ b/sys/xen/evtchn/evtchn_dev.c @@ -22,28 +22,23 @@ __FBSDID("$FreeBSD$"); #include #include #include - -#include -#include -#include #include + +#include +#include +#include + +#include #include #include -#include -#include +#include typedef struct evtchn_sotfc { struct selinfo ev_rsel; } evtchn_softc_t; - -#ifdef linuxcrap -/* NB. This must be shared amongst drivers if more things go in /dev/xen */ -static devfs_handle_t xen_dev_dir; -#endif - /* Only one process may open /dev/xen/evtchn at any time. */ static unsigned long evtchn_dev_inuse; @@ -72,12 +67,12 @@ static d_close_t evtchn_close; void -evtchn_device_upcall(int port) +evtchn_device_upcall(evtchn_port_t port) { mtx_lock(&upcall_lock); - mask_evtchn(port); - clear_evtchn(port); + evtchn_mask_port(port); + evtchn_clear_port(port); if ( ring != NULL ) { if ( (ring_prod - ring_cons) < EVTCHN_RING_SIZE ) { @@ -208,7 +203,7 @@ evtchn_write(struct cdev *dev, struct uio *uio, int ioflag) mtx_lock_spin(&lock); for ( i = 0; i < (count/2); i++ ) if ( test_bit(kbuf[i], &bound_ports[0]) ) - unmask_evtchn(kbuf[i]); + evtchn_unmask_port(kbuf[i]); mtx_unlock_spin(&lock); rc = count; @@ -224,6 +219,7 @@ evtchn_ioctl(struct cdev *dev, unsigned long cmd, caddr_t arg, { int rc = 0; +#ifdef NOTYET mtx_lock_spin(&lock); switch ( cmd ) @@ -249,6 +245,7 @@ evtchn_ioctl(struct cdev *dev, unsigned long cmd, caddr_t arg, } mtx_unlock_spin(&lock); +#endif return rc; } @@ -309,7 +306,7 @@ evtchn_close(struct cdev *dev, int flag, int otyp, struct thread *td __unused) mtx_lock_spin(&lock); for ( i = 0; i < NR_EVENT_CHANNELS; i++ ) if ( synch_test_and_clear_bit(i, &bound_ports[0]) ) - mask_evtchn(i); + evtchn_mask_port(i); mtx_unlock_spin(&lock); evtchn_dev_inuse = 0; @@ -352,34 +349,6 @@ evtchn_dev_init(void *dummy __unused) evtchn_dev->si_drv1 = malloc(sizeof(evtchn_softc_t), M_DEVBUF, M_WAITOK); bzero(evtchn_dev->si_drv1, sizeof(evtchn_softc_t)); - /* XXX I don't think we need any of this rubbish */ -#if 0 - if ( err != 0 ) - { - printk(KERN_ALERT "Could not register /dev/misc/evtchn\n"); - return err; - } - - /* (DEVFS) create directory '/dev/xen'. */ - xen_dev_dir = devfs_mk_dir(NULL, "xen", NULL); - - /* (DEVFS) &link_dest[pos] == '../misc/evtchn'. */ - pos = devfs_generate_path(evtchn_miscdev.devfs_handle, - &link_dest[3], - sizeof(link_dest) - 3); - if ( pos >= 0 ) - strncpy(&link_dest[pos], "../", 3); - /* (DEVFS) symlink '/dev/xen/evtchn' -> '../misc/evtchn'. */ - (void)devfs_mk_symlink(xen_dev_dir, - "evtchn", - DEVFS_FL_DEFAULT, - &link_dest[pos], - &symlink_handle, - NULL); - - /* (DEVFS) automatically destroy the symlink with its destination. */ - devfs_auto_unregister(evtchn_miscdev.devfs_handle, symlink_handle); -#endif if (bootverbose) printf("Event-channel device installed.\n"); @@ -387,5 +356,3 @@ evtchn_dev_init(void *dummy __unused) } SYSINIT(evtchn_dev_init, SI_SUB_DRIVERS, SI_ORDER_FIRST, evtchn_dev_init, NULL); - - diff --git a/sys/xen/evtchn/evtchnvar.h b/sys/xen/evtchn/evtchnvar.h new file mode 100644 index 00000000000..8008d23297d --- /dev/null +++ b/sys/xen/evtchn/evtchnvar.h @@ -0,0 +1,105 @@ +/****************************************************************************** + * evtchn.h + * + * Data structures and definitions private to the FreeBSD implementation + * of the Xen event channel API. + * + * Copyright (c) 2004, K A Fraser + * Copyright (c) 2012, Spectra Logic Corporation + * + * This file may be distributed separately from the Linux kernel, or + * incorporated into other software packages, subject to the following license: + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this source file (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. + * + * $FreeBSD$ + */ + +#ifndef __XEN_EVTCHN_EVTCHNVAR_H__ +#define __XEN_EVTCHN_EVTCHNVAR_H__ + +#include +#include + +enum evtchn_type { + EVTCHN_TYPE_UNBOUND, + EVTCHN_TYPE_PIRQ, + EVTCHN_TYPE_VIRQ, + EVTCHN_TYPE_IPI, + EVTCHN_TYPE_PORT, + EVTCHN_TYPE_COUNT +}; + +/** Submit a port notification for delivery to a userland evtchn consumer */ +void evtchn_device_upcall(evtchn_port_t port); + +/** + * Disable signal delivery for an event channel port, returning its + * previous mask state. + * + * \param port The event channel port to query and mask. + * + * \returns 1 if event delivery was previously disabled. Otherwise 0. + */ +static inline int +evtchn_test_and_set_mask(evtchn_port_t port) +{ + shared_info_t *s = HYPERVISOR_shared_info; + return synch_test_and_set_bit(port, s->evtchn_mask); +} + +/** + * Clear any pending event for the given event channel port. + * + * \param port The event channel port to clear. + */ +static inline void +evtchn_clear_port(evtchn_port_t port) +{ + shared_info_t *s = HYPERVISOR_shared_info; + synch_clear_bit(port, &s->evtchn_pending[0]); +} + +/** + * Disable signal delivery for an event channel port. + * + * \param port The event channel port to mask. + */ +static inline void +evtchn_mask_port(evtchn_port_t port) +{ + shared_info_t *s = HYPERVISOR_shared_info; + + synch_set_bit(port, &s->evtchn_mask[0]); +} + +/** + * Enable signal delivery for an event channel port. + * + * \param port The event channel port to enable. + */ +static inline void +evtchn_unmask_port(evtchn_port_t port) +{ + evtchn_unmask_t op = { .port = port }; + + HYPERVISOR_event_channel_op(EVTCHNOP_unmask, &op); +} + +#endif /* __XEN_EVTCHN_EVTCHNVAR_H__ */ diff --git a/sys/xen/features.c b/sys/xen/features.c index f28fe049177..bbb28968935 100644 --- a/sys/xen/features.c +++ b/sys/xen/features.c @@ -4,7 +4,7 @@ __FBSDID("$FreeBSD$"); #include #include -#include +#include #include #include diff --git a/sys/xen/gnttab.c b/sys/xen/gnttab.c index 4ece182c31a..03c32b724fd 100644 --- a/sys/xen/gnttab.c +++ b/sys/xen/gnttab.c @@ -26,7 +26,7 @@ __FBSDID("$FreeBSD$"); #include #include -#include +#include #include #include @@ -108,7 +108,7 @@ do_free_callbacks(void) static inline void check_free_callbacks(void) { - if (unlikely(gnttab_free_callback_list != NULL)) + if (__predict_false(gnttab_free_callback_list != NULL)) do_free_callbacks(); } @@ -136,7 +136,7 @@ gnttab_grant_foreign_access(domid_t domid, unsigned long frame, int readonly, error = get_free_entries(1, &ref); - if (unlikely(error)) + if (__predict_false(error)) return (error); shared[ref].frame = frame; @@ -248,7 +248,7 @@ gnttab_grant_foreign_transfer(domid_t domid, unsigned long pfn, int error, ref; error = get_free_entries(1, &ref); - if (unlikely(error)) + if (__predict_false(error)) return (error); gnttab_grant_foreign_transfer_ref(ref, domid, pfn); @@ -341,7 +341,7 @@ gnttab_alloc_grant_references(uint16_t count, grant_ref_t *head) int ref, error; error = get_free_entries(count, &ref); - if (unlikely(error)) + if (__predict_false(error)) return (error); *head = ref; @@ -360,7 +360,7 @@ gnttab_claim_grant_reference(grant_ref_t *private_head) { grant_ref_t g = *private_head; - if (unlikely(g == GNTTAB_LIST_END)) + if (__predict_false(g == GNTTAB_LIST_END)) return (g); *private_head = gnttab_entry(g); return (g); @@ -527,8 +527,7 @@ gnttab_map(unsigned int start_idx, unsigned int end_idx) if (shared == NULL) { vm_offset_t area; - area = kmem_alloc_nofault(kernel_map, - PAGE_SIZE * max_nr_grant_frames()); + area = kva_alloc(PAGE_SIZE * max_nr_grant_frames()); KASSERT(area, ("can't allocate VM space for grant table")); shared = (grant_entry_t *)area; } @@ -590,8 +589,7 @@ gnttab_map(unsigned int start_idx, unsigned int end_idx) if (shared == NULL) { vm_offset_t area; - area = kmem_alloc_nofault(kernel_map, - PAGE_SIZE * max_nr_grant_frames()); + area = kva_alloc(PAGE_SIZE * max_nr_grant_frames()); KASSERT(area, ("can't allocate VM space for grant table")); shared = (grant_entry_t *)area; } diff --git a/sys/xen/gnttab.h b/sys/xen/gnttab.h index 1741ec33872..d81e965fc43 100644 --- a/sys/xen/gnttab.h +++ b/sys/xen/gnttab.h @@ -36,13 +36,12 @@ #ifndef __ASM_GNTTAB_H__ -#include - +#include #include -#include -#include #include +#include + #define GNTTAB_LIST_END GRANT_REF_INVALID struct gnttab_free_callback { diff --git a/sys/xen/hvm.h b/sys/xen/hvm.h index 338a107ab84..562aaf93ab0 100644 --- a/sys/xen/hvm.h +++ b/sys/xen/hvm.h @@ -23,6 +23,9 @@ #ifndef __XEN_HVM_H__ #define __XEN_HVM_H__ +#include +#include + #include /** @@ -91,4 +94,5 @@ enum { void xen_hvm_set_callback(device_t); void xen_hvm_suspend(void); void xen_hvm_resume(void); +void xen_hvm_init_cpu(void); #endif /* __XEN_HVM_H__ */ diff --git a/sys/xen/interface/event_channel.h b/sys/xen/interface/event_channel.h index 07ff32194b4..65d19113fc2 100644 --- a/sys/xen/interface/event_channel.h +++ b/sys/xen/interface/event_channel.h @@ -73,8 +73,11 @@ #define EVTCHNOP_reset 10 /* ` } */ +#ifndef __XEN_EVTCHN_PORT_DEFINED__ typedef uint32_t evtchn_port_t; DEFINE_XEN_GUEST_HANDLE(evtchn_port_t); +#define __XEN_EVTCHN_PORT_DEFINED__ 1 +#endif /* * EVTCHNOP_alloc_unbound: Allocate a port in domain and mark as diff --git a/sys/xen/xen-os.h b/sys/xen/xen-os.h new file mode 100644 index 00000000000..95e8c6a3a4b --- /dev/null +++ b/sys/xen/xen-os.h @@ -0,0 +1,95 @@ +/****************************************************************************** + * xen/xen-os.h + * + * Random collection of macros and definition + * + * Copyright (c) 2003, 2004 Keir Fraser (on behalf of the Xen team) + * All rights reserved. + * + * 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. + * + * $FreeBSD$ + */ + +#ifndef _XEN_XEN_OS_H_ +#define _XEN_XEN_OS_H_ + +#if !defined(__XEN_INTERFACE_VERSION__) +#define __XEN_INTERFACE_VERSION__ 0x00030208 +#endif + +#define GRANT_REF_INVALID 0xffffffff + +#ifdef LOCORE +#define __ASSEMBLY__ +#endif + +#include + +#include + +/* Everything below this point is not included by assembler (.S) files. */ +#ifndef __ASSEMBLY__ + +/* Force a proper event-channel callback from Xen. */ +void force_evtchn_callback(void); + +extern int gdtset; + +extern shared_info_t *HYPERVISOR_shared_info; + +enum xen_domain_type { + XEN_NATIVE, /* running on bare hardware */ + XEN_PV_DOMAIN, /* running in a PV domain */ + XEN_HVM_DOMAIN, /* running in a Xen hvm domain */ +}; + +extern enum xen_domain_type xen_domain_type; + +static inline int +xen_domain(void) +{ + return (xen_domain_type != XEN_NATIVE); +} + +static inline int +xen_pv_domain(void) +{ + return (xen_domain_type == XEN_PV_DOMAIN); +} + +static inline int +xen_hvm_domain(void) +{ + return (xen_domain_type == XEN_HVM_DOMAIN); +} + +#ifndef xen_mb +#define xen_mb() mb() +#endif +#ifndef xen_rmb +#define xen_rmb() rmb() +#endif +#ifndef xen_wmb +#define xen_wmb() wmb() +#endif + +#endif /* !__ASSEMBLY__ */ + +#endif /* _XEN_XEN_OS_H_ */ diff --git a/sys/xen/xen_intr.h b/sys/xen/xen_intr.h index 2e753e65ecb..3b339a5d12b 100644 --- a/sys/xen/xen_intr.h +++ b/sys/xen/xen_intr.h @@ -1,103 +1,215 @@ -/* -*- Mode:C; c-basic-offset:4; tab-width:4 -*- */ +/****************************************************************************** + * xen_intr.h + * + * APIs for managing Xen event channel, virtual IRQ, and physical IRQ + * notifications. + * + * Copyright (c) 2004, K A Fraser + * Copyright (c) 2012, Spectra Logic Corporation + * + * This file may be distributed separately from the Linux kernel, or + * incorporated into other software packages, subject to the following license: + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this source file (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. + * + * $FreeBSD$ + */ #ifndef _XEN_INTR_H_ #define _XEN_INTR_H_ -/* -* The flat IRQ space is divided into two regions: -* 1. A one-to-one mapping of real physical IRQs. This space is only used -* if we have physical device-access privilege. This region is at the -* start of the IRQ space so that existing device drivers do not need -* to be modified to translate physical IRQ numbers into our IRQ space. -* 3. A dynamic mapping of inter-domain and Xen-sourced virtual IRQs. These -* are bound using the provided bind/unbind functions. -* -* -* $FreeBSD$ -*/ +#ifndef __XEN_EVTCHN_PORT_DEFINED__ +typedef uint32_t evtchn_port_t; +DEFINE_XEN_GUEST_HANDLE(evtchn_port_t); +#define __XEN_EVTCHN_PORT_DEFINED__ 1 +#endif -#define PIRQ_BASE 0 -#define NR_PIRQS 128 +/** Registered Xen interrupt callback handle. */ +typedef void * xen_intr_handle_t; -#define DYNIRQ_BASE (PIRQ_BASE + NR_PIRQS) -#define NR_DYNIRQS 128 +/** If non-zero, the hypervisor has been configured to use a direct vector */ +extern int xen_vector_callback_enabled; -#define NR_IRQS (NR_PIRQS + NR_DYNIRQS) - -#define pirq_to_irq(_x) ((_x) + PIRQ_BASE) -#define irq_to_pirq(_x) ((_x) - PIRQ_BASE) - -#define dynirq_to_irq(_x) ((_x) + DYNIRQ_BASE) -#define irq_to_dynirq(_x) ((_x) - DYNIRQ_BASE) - -/* - * Dynamic binding of event channels and VIRQ sources to guest IRQ space. +/** + * Associate an already allocated local event channel port an interrupt + * handler. + * + * \param dev The device making this bind request. + * \param local_port The event channel to bind. + * \param filter An interrupt filter handler. Specify NULL + * to always dispatch to the ithread handler. + * \param handler An interrupt ithread handler. Optional (can + * specify NULL) if all necessary event actions + * are performed by filter. + * \param arg Argument to present to both filter and handler. + * \param irqflags Interrupt handler flags. See sys/bus.h. + * \param handlep Pointer to an opaque handle used to manage this + * registration. + * + * \returns 0 on success, otherwise an errno. */ +int xen_intr_bind_local_port(device_t dev, evtchn_port_t local_port, + driver_filter_t filter, driver_intr_t handler, void *arg, + enum intr_type irqflags, xen_intr_handle_t *handlep); -/* - * Bind a caller port event channel to an interrupt handler. If - * successful, the guest IRQ number is returned in *irqp. Return zero - * on success or errno otherwise. +/** + * Allocate a local event channel port, accessible by the specified + * remote/foreign domain and, if successful, associate the port with + * the specified interrupt handler. + * + * \param dev The device making this bind request. + * \param remote_domain Remote domain grant permission to signal the + * newly allocated local port. + * \param filter An interrupt filter handler. Specify NULL + * to always dispatch to the ithread handler. + * \param handler An interrupt ithread handler. Optional (can + * specify NULL) if all necessary event actions + * are performed by filter. + * \param arg Argument to present to both filter and handler. + * \param irqflags Interrupt handler flags. See sys/bus.h. + * \param handlep Pointer to an opaque handle used to manage this + * registration. + * + * \returns 0 on success, otherwise an errno. */ -extern int bind_caller_port_to_irqhandler(unsigned int caller_port, - const char *devname, driver_intr_t handler, void *arg, - unsigned long irqflags, unsigned int *irqp); +int xen_intr_alloc_and_bind_local_port(device_t dev, + u_int remote_domain, driver_filter_t filter, driver_intr_t handler, + void *arg, enum intr_type irqflags, xen_intr_handle_t *handlep); -/* - * Bind a listening port to an interrupt handler. If successful, the - * guest IRQ number is returned in *irqp. Return zero on success or - * errno otherwise. +/** + * Associate the specified interrupt handler with the remote event + * channel port specified by remote_domain and remote_port. + * + * \param dev The device making this bind request. + * \param remote_domain The domain peer for this event channel connection. + * \param remote_port Remote domain's local port number for this event + * channel port. + * \param filter An interrupt filter handler. Specify NULL + * to always dispatch to the ithread handler. + * \param handler An interrupt ithread handler. Optional (can + * specify NULL) if all necessary event actions + * are performed by filter. + * \param arg Argument to present to both filter and handler. + * \param irqflags Interrupt handler flags. See sys/bus.h. + * \param handlep Pointer to an opaque handle used to manage this + * registration. + * + * \returns 0 on success, otherwise an errno. */ -extern int bind_listening_port_to_irqhandler(unsigned int remote_domain, - const char *devname, driver_intr_t handler, void *arg, - unsigned long irqflags, unsigned int *irqp); +int xen_intr_bind_remote_port(device_t dev, u_int remote_domain, + evtchn_port_t remote_port, driver_filter_t filter, + driver_intr_t handler, void *arg, enum intr_type irqflags, + xen_intr_handle_t *handlep); -/* - * Bind a VIRQ to an interrupt handler. If successful, the guest IRQ - * number is returned in *irqp. Return zero on success or errno - * otherwise. +/** + * Associate the specified interrupt handler with the specified Xen + * virtual interrupt source. + * + * \param dev The device making this bind request. + * \param virq The Xen virtual IRQ number for the Xen interrupt + * source being hooked. + * \param cpu The cpu on which interrupt events should be delivered. + * \param filter An interrupt filter handler. Specify NULL + * to always dispatch to the ithread handler. + * \param handler An interrupt ithread handler. Optional (can + * specify NULL) if all necessary event actions + * are performed by filter. + * \param arg Argument to present to both filter and handler. + * \param irqflags Interrupt handler flags. See sys/bus.h. + * \param handlep Pointer to an opaque handle used to manage this + * registration. + * + * \returns 0 on success, otherwise an errno. */ -extern int bind_virq_to_irqhandler(unsigned int virq, unsigned int cpu, - const char *devname, driver_filter_t filter, driver_intr_t handler, - void *arg, unsigned long irqflags, unsigned int *irqp); +int xen_intr_bind_virq(device_t dev, u_int virq, u_int cpu, + driver_filter_t filter, driver_intr_t handler, + void *arg, enum intr_type irqflags, xen_intr_handle_t *handlep); -/* - * Bind an IPI to an interrupt handler. If successful, the guest - * IRQ number is returned in *irqp. Return zero on success or errno - * otherwise. +/** + * Allocate a local event channel port for servicing interprocessor + * interupts and, if successful, associate the port with the specified + * interrupt handler. + * + * \param dev The device making this bind request. + * \param cpu The cpu receiving the IPI. + * \param filter The interrupt filter servicing this IPI. + * \param irqflags Interrupt handler flags. See sys/bus.h. + * \param handlep Pointer to an opaque handle used to manage this + * registration. + * + * \returns 0 on success, otherwise an errno. */ -extern int bind_ipi_to_irqhandler(unsigned int ipi, unsigned int cpu, - const char *devname, driver_filter_t filter, - unsigned long irqflags, unsigned int *irqp); +int xen_intr_alloc_and_bind_ipi(device_t dev, u_int cpu, + driver_filter_t filter, enum intr_type irqflags, + xen_intr_handle_t *handlep); -/* - * Bind an interdomain event channel to an interrupt handler. If - * successful, the guest IRQ number is returned in *irqp. Return zero - * on success or errno otherwise. +/** + * Unbind an interrupt handler from its interrupt source. + * + * \param handlep A pointer to the opaque handle that was initialized + * at the time the interrupt source was bound. + * + * \returns 0 on success, otherwise an errno. + * + * \note The event channel, if any, that was allocated at bind time is + * closed upon successful return of this method. + * + * \note It is always safe to call xen_intr_unbind() on a handle that + * has been initilized to NULL. */ -extern int bind_interdomain_evtchn_to_irqhandler(unsigned int remote_domain, - unsigned int remote_port, const char *devname, - driver_intr_t handler, void *arg, - unsigned long irqflags, unsigned int *irqp); +void xen_intr_unbind(xen_intr_handle_t *handle); -/* - * Unbind an interrupt handler using the guest IRQ number returned - * when it was bound. +/** + * Add a description to an interrupt handler. + * + * \param handle The opaque handle that was initialized at the time + * the interrupt source was bound. + * + * \param fmt The sprintf compatible format string for the description, + * followed by optional sprintf arguments. + * + * \returns 0 on success, otherwise an errno. */ -extern void unbind_from_irqhandler(unsigned int irq); +int +xen_intr_describe(xen_intr_handle_t port_handle, const char *fmt, ...) + __attribute__((format(printf, 2, 3))); -static __inline__ int irq_cannonicalize(unsigned int irq) -{ - return (irq == 2) ? 9 : irq; -} +/** + * Signal the remote peer of an interrupt source associated with an + * event channel port. + * + * \param handle The opaque handle that was initialized at the time + * the interrupt source was bound. + * + * \note For xen interrupt sources other than event channel ports, + * this method takes no action. + */ +void xen_intr_signal(xen_intr_handle_t handle); -extern void disable_irq(unsigned int); -extern void disable_irq_nosync(unsigned int); -extern void enable_irq(unsigned int); - -extern void irq_suspend(void); -extern void irq_resume(void); - -extern void idle_block(void); -extern int ap_cpu_initclocks(int cpu); +/** + * Get the local event channel port number associated with this interrupt + * source. + * + * \param handle The opaque handle that was initialized at the time + * the interrupt source was bound. + * + * \returns 0 if the handle is invalid, otherwise positive port number. + */ +evtchn_port_t xen_intr_port(xen_intr_handle_t handle); #endif /* _XEN_INTR_H_ */ diff --git a/sys/xen/xenbus/xenbus.c b/sys/xen/xenbus/xenbus.c index 8887066c7e4..c59d4aec453 100644 --- a/sys/xen/xenbus/xenbus.c +++ b/sys/xen/xenbus/xenbus.c @@ -50,11 +50,12 @@ __FBSDID("$FreeBSD$"); #include #include -#include +#include #include #include #include #include + #include MALLOC_DEFINE(M_XENBUS, "xenbus", "XenBus Support"); @@ -222,42 +223,6 @@ xenbus_grant_ring(device_t dev, unsigned long ring_mfn, grant_ref_t *refp) return (0); } -int -xenbus_alloc_evtchn(device_t dev, evtchn_port_t *port) -{ - struct evtchn_alloc_unbound alloc_unbound; - int err; - - alloc_unbound.dom = DOMID_SELF; - alloc_unbound.remote_dom = xenbus_get_otherend_id(dev); - - err = HYPERVISOR_event_channel_op(EVTCHNOP_alloc_unbound, - &alloc_unbound); - - if (err) { - xenbus_dev_fatal(dev, -err, "allocating event channel"); - return (-err); - } - *port = alloc_unbound.port; - return (0); -} - -int -xenbus_free_evtchn(device_t dev, evtchn_port_t port) -{ - struct evtchn_close close; - int err; - - close.port = port; - - err = HYPERVISOR_event_channel_op(EVTCHNOP_close, &close); - if (err) { - xenbus_dev_error(dev, -err, "freeing event channel %d", port); - return (-err); - } - return (0); -} - XenbusState xenbus_read_driver_state(const char *path) { diff --git a/sys/xen/xenbus/xenbus_if.m b/sys/xen/xenbus/xenbus_if.m index 87d7c7fef9d..fd9ae51ce1c 100644 --- a/sys/xen/xenbus/xenbus_if.m +++ b/sys/xen/xenbus/xenbus_if.m @@ -29,7 +29,8 @@ #include #include -#include + +#include #include #include diff --git a/sys/xen/xenbus/xenbusb_front.c b/sys/xen/xenbus/xenbusb_front.c index 818e7f0a2cc..145d5271886 100644 --- a/sys/xen/xenbus/xenbusb_front.c +++ b/sys/xen/xenbus/xenbusb_front.c @@ -51,9 +51,9 @@ __FBSDID("$FreeBSD$"); #include #include -#include #include +#include #include #include #include diff --git a/sys/xen/xenbus/xenbusvar.h b/sys/xen/xenbus/xenbusvar.h index 1c730fb900a..ab5d01fa6b6 100644 --- a/sys/xen/xenbus/xenbusvar.h +++ b/sys/xen/xenbus/xenbusvar.h @@ -43,8 +43,8 @@ #include #include -#include +#include #include #include #include @@ -194,39 +194,6 @@ int xenbus_watch_path2(device_t dev, const char *path, */ int xenbus_grant_ring(device_t dev, unsigned long ring_mfn, grant_ref_t *refp); -/** - * Allocate an event channel for the given XenBus device. - * - * \param dev The device for which to allocate the event channel. - * \param port[out] The port identifier for the allocated event channel. - * - * \return On success, 0. Otherwise an errno value indicating the - * type of failure. - * - * A successfully allocated event channel should be free'd using - * xenbus_free_evtchn(). - * - * \note On error, \a dev will be switched to the XenbusStateClosing - * state and the returned error is saved in the per-device error node - * for \a dev in the XenStore. - */ -int xenbus_alloc_evtchn(device_t dev, evtchn_port_t *port); - -/** - * Free an existing event channel. - * - * \param dev The device which allocated this event channel. - * \param port The port identifier for the event channel to free. - * - * \return On success, 0. Otherwise an errno value indicating the - * type of failure. - * - * \note On error, \a dev will be switched to the XenbusStateClosing - * state and the returned error is saved in the per-device error node - * for \a dev in the XenStore. - */ -int xenbus_free_evtchn(device_t dev, evtchn_port_t port); - /** * Record the given errno, along with the given, printf-style, formatted * message in dev's device specific error node in the XenStore. diff --git a/sys/xen/xenstore/xenstore.c b/sys/xen/xenstore/xenstore.c index a07a26266e4..d404862ab55 100644 --- a/sys/xen/xenstore/xenstore.c +++ b/sys/xen/xenstore/xenstore.c @@ -49,10 +49,9 @@ __FBSDID("$FreeBSD$"); #include #include -#include #include -#include +#include #include #include #include @@ -244,8 +243,8 @@ struct xs_softc { */ int evtchn; - /** Interrupt number for our event channel. */ - u_int irq; + /** Handle for XenStore interrupts. */ + xen_intr_handle_t xen_intr_handle; /** * Interrupt driven config hook allowing us to defer @@ -505,11 +504,10 @@ xs_write_store(const void *tdata, unsigned len) xen_store->req_prod += avail; /* - * notify_remote_via_evtchn implies mb(). The other side - * will see the change to req_prod at the time of the - * interrupt. + * xen_intr_signal() implies mb(). The other side will see + * the change to req_prod at the time of the interrupt. */ - notify_remote_via_evtchn(xs.evtchn); + xen_intr_signal(xs.xen_intr_handle); } return (0); @@ -597,11 +595,10 @@ xs_read_store(void *tdata, unsigned len) xen_store->rsp_cons += avail; /* - * notify_remote_via_evtchn implies mb(). The producer - * will see the updated consumer index when the event - * is delivered. + * xen_intr_signal() implies mb(). The producer will see + * the updated consumer index when the event is delivered. */ - notify_remote_via_evtchn(xs.evtchn); + xen_intr_signal(xs.xen_intr_handle); } return (0); @@ -1068,11 +1065,11 @@ xs_init_comms(void) xen_store->rsp_cons = xen_store->rsp_prod; } - if (xs.irq) - unbind_from_irqhandler(xs.irq); + xen_intr_unbind(&xs.xen_intr_handle); - error = bind_caller_port_to_irqhandler(xs.evtchn, "xenstore", - xs_intr, NULL, INTR_TYPE_NET, &xs.irq); + error = xen_intr_bind_local_port(xs.xs_dev, xs.evtchn, + /*filter*/NULL, xs_intr, /*arg*/NULL, INTR_TYPE_NET|INTR_MPSAFE, + &xs.xen_intr_handle); if (error) { log(LOG_WARNING, "XENSTORE request irq failed %i\n", error); return (error); @@ -1168,7 +1165,6 @@ xs_attach(device_t dev) sx_init(&xs.suspend_mutex, "xenstore suspend"); mtx_init(&xs.registered_watches_lock, "watches", NULL, MTX_DEF); mtx_init(&xs.watch_events_lock, "watch events", NULL, MTX_DEF); - xs.irq = 0; /* Initialize the shared memory rings to talk to xenstored */ error = xs_init_comms(); diff --git a/sys/xen/xenstore/xenstore_dev.c b/sys/xen/xenstore/xenstore_dev.c index 1fa419795ed..e1b4091447a 100644 --- a/sys/xen/xenstore/xenstore_dev.c +++ b/sys/xen/xenstore/xenstore_dev.c @@ -44,7 +44,7 @@ __FBSDID("$FreeBSD$"); #include #include -#include +#include #include #include diff --git a/sys/xen/xenstore/xenstorevar.h b/sys/xen/xenstore/xenstorevar.h index 4a1382d8f74..208e5bf09b8 100644 --- a/sys/xen/xenstore/xenstorevar.h +++ b/sys/xen/xenstore/xenstorevar.h @@ -41,8 +41,8 @@ #include #include -#include +#include #include #include #include diff --git a/tools/build/mk/OptionalObsoleteFiles.inc b/tools/build/mk/OptionalObsoleteFiles.inc index 4c337b6955c..09a39acec6f 100644 --- a/tools/build/mk/OptionalObsoleteFiles.inc +++ b/tools/build/mk/OptionalObsoleteFiles.inc @@ -900,6 +900,11 @@ OLD_FILES+=usr/bin/CC OLD_FILES+=usr/bin/c++ OLD_FILES+=usr/bin/c++filt OLD_FILES+=usr/bin/g++ +OLD_FILES+=usr/libexec/cc1plus +.endif + +.if ${MK_GNUCXX} == no +OLD_FILES+=usr/bin/g++ OLD_FILES+=usr/include/c++/4.2/algorithm OLD_FILES+=usr/include/c++/4.2/backward/algo.h OLD_FILES+=usr/include/c++/4.2/backward/algobase.h @@ -1455,12 +1460,23 @@ OLD_FILES+=usr/include/c++/4.2/utility OLD_FILES+=usr/include/c++/4.2/valarray OLD_FILES+=usr/include/c++/4.2/vector OLD_FILES+=usr/lib/libstdc++.a -# Keep libs to allow bootstrapping g++(1) with gperf(1) -#OLD_LIBS+=usr/lib/libstdc++.so -#OLD_LIBS+=usr/lib/libstdc++.so.6 +OLD_FILES+=usr/lib/libstdc++.so +OLD_LIBS+=usr/lib/libstdc++.so.6 OLD_FILES+=usr/lib/libstdc++_p.a OLD_FILES+=usr/lib/libsupc++.a +OLD_FILES+=usr/lib/libsupc++.so +OLD_LIBS+=usr/lib/libsupc++.so.1 OLD_FILES+=usr/lib/libsupc++_p.a +.if ${TARGET_ARCH} == "amd64" || ${TARGET_ARCH} == "powerpc64" +OLD_FILES+=usr/lib32/libstdc++.a +OLD_FILES+=usr/lib32/libstdc++.so +OLD_LIBS+=usr/lib32/libstdc++.so.6 +OLD_FILES+=usr/lib32/libstdc++_p.a +OLD_FILES+=usr/lib32/libsupc++.a +OLD_FILES+=usr/lib32/libsupc++.so +OLD_LIBS+=usr/lib32/libsupc++.so.1 +OLD_FILES+=usr/lib32/libsupc++_p.a +.endif OLD_FILES+=usr/libexec/cc1plus .endif @@ -1552,13 +1568,17 @@ OLD_FILES+=usr/bin/gcc OLD_FILES+=usr/bin/gcov OLD_FILES+=usr/bin/gcpp .if ${TARGET_ARCH} == "amd64" || ${TARGET_ARCH} == "i386" +OLD_FILES+=usr/include/gcc/4.2/__wmmintrin_aes.h +OLD_FILES+=usr/include/gcc/4.2/__wmmintrin_pclmul.h +OLD_FILES+=usr/include/gcc/4.2/ammintrin.h OLD_FILES+=usr/include/gcc/4.2/emmintrin.h +OLD_FILES+=usr/include/gcc/4.2/mm3dnow.h OLD_FILES+=usr/include/gcc/4.2/mm_malloc.h OLD_FILES+=usr/include/gcc/4.2/mmintrin.h OLD_FILES+=usr/include/gcc/4.2/pmmintrin.h OLD_FILES+=usr/include/gcc/4.2/tmmintrin.h +OLD_FILES+=usr/include/gcc/4.2/wmmintrin.h OLD_FILES+=usr/include/gcc/4.2/xmmintrin.h -OLD_FILES+=usr/include/gcc/4.2/mm3dnow.h .elif ${TARGET_ARCH} == "ia64" OLD_FILES+=usr/include/gcc/4.2/ia64intrin.h .elif ${TARGET_ARCH} == "arm" @@ -1568,8 +1588,6 @@ OLD_FILES+=usr/include/gcc/4.2/altivec.h OLD_FILES+=usr/include/gcc/4.2/ppc-asm.h OLD_FILES+=usr/include/gcc/4.2/spe.h .endif -OLD_DIRS+=usr/include/gcc/4.2 -OLD_DIRS+=usr/include/gcc OLD_FILES+=usr/libexec/cc1 OLD_FILES+=usr/libexec/cc1plus OLD_FILES+=usr/share/info/cpp.info.gz @@ -2198,6 +2216,27 @@ OLD_FILES+=usr/share/man/man5/hesiod.conf.5.gz # to be filled in #.endif +.if ${MK_ICONV} == no +OLD_FILES+=usr/bin/iconv +OLD_FILES+=usr/bin/mkcsmapper +OLD_FILES+=usr/bin/mkesdb +OLD_FILES+=usr/include/_libiconv_compat.h +OLD_FILES+=usr/include/iconv.h +OLD_FILES+=usr/share/man/man1/iconv.1.gz +OLD_FILES+=usr/share/man/man1/mkcsmapper.1.gz +OLD_FILES+=usr/share/man/man1/mkesdb.1.gz +OLD_FILES+=usr/share/man/man3/__iconv.3.gz +OLD_FILES+=usr/share/man/man3/__iconv_free_list.3.gz +OLD_FILES+=usr/share/man/man3/__iconv_get_list.3.gz +OLD_FILES+=usr/share/man/man3/iconv.3.gz +OLD_FILES+=usr/share/man/man3/iconv_canonicalize.3.gz +OLD_FILES+=usr/share/man/man3/iconv_close.3.gz +OLD_FILES+=usr/share/man/man3/iconv_open.3.gz +OLD_FILES+=usr/share/man/man3/iconv_open_into.3.gz +OLD_FILES+=usr/share/man/man3/iconvctl.3.gz +OLD_FILES+=usr/share/man/man3/iconvlist.3.gz +.endif + .if ${MK_INET6} == no OLD_FILES+=sbin/ping6 OLD_FILES+=sbin/rtsol @@ -2507,6 +2546,14 @@ OLD_FILES+=usr/lib/pam_krb5.so OLD_LIBS+=usr/lib/pam_krb5.so.5 OLD_FILES+=usr/lib/pam_ksu.so OLD_LIBS+=usr/lib/pam_ksu.so.5 +OLD_FILES+=usr/lib/private/libheimipcc.a +OLD_FILES+=usr/lib/private/libheimipcc.so +OLD_LIBS+=usr/lib/private/libheimipcc.so.11 +OLD_FILES+=usr/lib/private/libheimipcc_p.a +OLD_FILES+=usr/lib/private/libheimipcs.a +OLD_FILES+=usr/lib/private/libheimipcs.so +OLD_LIBS+=usr/lib/private/libheimipcs.so.11 +OLD_FILES+=usr/lib/private/libheimipcs_p.a .if ${TARGET_ARCH} == "amd64" || ${TARGET_ARCH} == "powerpc64" OLD_FILES+=usr/lib32/libasn1.a OLD_FILES+=usr/lib32/libasn1.so @@ -2576,6 +2623,14 @@ OLD_FILES+=usr/lib32/pam_krb5.so OLD_LIBS+=usr/lib32/pam_krb5.so.5 OLD_FILES+=usr/lib32/pam_ksu.so OLD_LIBS+=usr/lib32/pam_ksu.so.5 +OLD_FILES+=usr/lib32/private/libheimipcc.a +OLD_FILES+=usr/lib32/private/libheimipcc.so +OLD_LIBS+=usr/lib32/private/libheimipcc.so.11 +OLD_FILES+=usr/lib32/private/libheimipcc_p.a +OLD_FILES+=usr/lib32/private/libheimipcs.a +OLD_FILES+=usr/lib32/private/libheimipcs.so +OLD_LIBS+=usr/lib32/private/libheimipcs.so.11 +OLD_FILES+=usr/lib32/private/libheimipcs_p.a .endif OLD_FILES+=usr/libexec/digest-service OLD_FILES+=usr/libexec/hprop @@ -3334,10 +3389,30 @@ OLD_FILES+=usr/share/man/man8/verify_krb5_conf.8.gz OLD_FILES+=usr/share/man/man8/verify_krb5_conf.8.gz .endif +.if ${MK_LDNS} == no +OLD_FILES+=usr/lib/private/libldns.a +OLD_FILES+=usr/lib/private/libldns.so +OLD_LIBS+=usr/lib/private/libldns.so.5 +OLD_FILES+=usr/lib/private/libldns_p.a +.if ${TARGET_ARCH} == "amd64" || ${TARGET_ARCH} == "powerpc64" +OLD_FILES+=usr/lib32/private/libldns.a +OLD_FILES+=usr/lib32/private/libldns.so +OLD_LIBS+=usr/lib32/private/libldns.so.5 +OLD_FILES+=usr/lib32/private/libldns_p.a +.endif +.endif + #.if ${MK_LIB32} == no # to be filled in #.endif +.if ${MK_LIBICONV_COMPAT} == no +OLD_FILES+=usr/lib/libiconv.a +OLD_FILES+=usr/lib/libiconv.so +OLD_FILES+=usr/lib/libiconv.so.3 +OLD_FILES+=usr/lib/libiconv_p.a +.endif + .if ${MK_LIBCPLUSPLUS} == no OLD_LIBS+=lib/libcxxrt.so.1 OLD_FILES+=usr/lib/libc++.a @@ -3653,7 +3728,27 @@ OLD_FILES+=usr/share/man/man8/ntptime.8.gz #.endif .if ${MK_OPENSSH} == no -OLD_FILES+=usr.bin/ssh-copy-id +OLD_FILES+=usr/bin/sftp +OLD_FILES+=usr/bin/ssh +OLD_FILES+=usr/bin/ssh-add +OLD_FILES+=usr/bin/ssh-agent +OLD_FILES+=usr/bin/ssh-copy-id +OLD_FILES+=usr/bin/ssh-keygen +OLD_FILES+=usr/bin/ssh-keyscan +OLD_FILES+=usr/lib/private/libssh.a +OLD_FILES+=usr/lib/private/libssh.so +OLD_LIBS+=usr/lib/private/libssh.so.5 +OLD_FILES+=usr/lib/private/libssh_p.a +.if ${TARGET_ARCH} == "amd64" || ${TARGET_ARCH} == "powerpc64" +OLD_FILES+=usr/lib32/private/libssh.a +OLD_FILES+=usr/lib32/private/libssh.so +OLD_LIBS+=usr/lib32/private/libssh.so.5 +OLD_FILES+=usr/lib32/private/libssh_p.a +.endif +OLD_FILES+=usr/libexec/sftp-server +OLD_FILES+=usr/libexec/ssh-keysign +OLD_FILES+=usr/libexec/ssh-pkcs11-helper +OLD_FILES+=usr/sbin/sshd .endif #.if ${MK_OPENSSL} == no @@ -3910,7 +4005,6 @@ OLD_FILES+=usr/lib/librt_p.a OLD_FILES+=usr/lib/libsbuf_p.a OLD_FILES+=usr/lib/libsdp_p.a OLD_FILES+=usr/lib/libsmb_p.a -OLD_FILES+=usr/lib/libssh_p.a OLD_FILES+=usr/lib/libssl_p.a OLD_FILES+=usr/lib/libstdc++_p.a OLD_FILES+=usr/lib/libsupc++_p.a @@ -3933,6 +4027,8 @@ OLD_FILES+=usr/lib/libwrap_p.a OLD_FILES+=usr/lib/liby_p.a OLD_FILES+=usr/lib/libypclnt_p.a OLD_FILES+=usr/lib/libz_p.a +OLD_FILES+=usr/lib/private/libldns_p.a +OLD_FILES+=usr/lib/private/libssh_p.a .endif .if ${MK_RCMDS} == no @@ -4271,6 +4367,24 @@ OLD_FILES+=usr/share/man/man8/telnetd.8.gz # to be filled in #.endif +.if ${MK_UNBOUND} == no +OLD_FILES+=usr/lib/private/libunbound.a +OLD_FILES+=usr/lib/private/libunbound.so +OLD_LIBS+=usr/lib/private/libunbound.so.5 +OLD_FILES+=usr/lib/private/libunbound_p.a +.if ${TARGET_ARCH} == "amd64" || ${TARGET_ARCH} == "powerpc64" +OLD_FILES+=usr/lib32/private/libunbound.a +OLD_FILES+=usr/lib32/private/libunbound.so +OLD_LIBS+=usr/lib32/private/libunbound.so.5 +OLD_FILES+=usr/lib32/private/libunbound_p.a +.endif +OLD_FILES+=usr/sbin/unbound +OLD_FILES+=usr/sbin/unbound-anchor +OLD_FILES+=usr/sbin/unbound-checkconf +OLD_FILES+=usr/sbin/unbound-control +OLD_FILES+=usr/sbin/unbound-control-setup +.endif + #.if ${MK_USB} == no # to be filled in #.endif @@ -4347,7 +4461,3 @@ OLD_FILES+=usr/bin/svnserve OLD_FILES+=usr/bin/svnsync OLD_FILES+=usr/bin/svnversion .endif - -.if ${MK_ICONV} == no -OLD_FILES+=usr/bin/iconv -.endif diff --git a/tools/build/options/WITHOUT_GCC b/tools/build/options/WITHOUT_GCC index 5717f64019e..7bb21f8b837 100644 --- a/tools/build/options/WITHOUT_GCC +++ b/tools/build/options/WITHOUT_GCC @@ -1,6 +1,2 @@ .\" $FreeBSD$ -Set to not install gcc and g++. -.Bf -symbolic -The option does not generally work for build targets, unless some alternative -toolchain is enabled. -.Ef +Set to not build and install gcc and g++. diff --git a/tools/build/options/WITHOUT_GNUCXX b/tools/build/options/WITHOUT_GNUCXX new file mode 100644 index 00000000000..ce10636009e --- /dev/null +++ b/tools/build/options/WITHOUT_GNUCXX @@ -0,0 +1,3 @@ +.\" $FreeBSD$ +Do not build the GNU C++ stack (g++, libstdc++). +This is the default on platforms where clang is the system compiler. diff --git a/tools/build/options/WITHOUT_ICONV b/tools/build/options/WITHOUT_ICONV new file mode 100644 index 00000000000..a43dd3a1691 --- /dev/null +++ b/tools/build/options/WITHOUT_ICONV @@ -0,0 +1,2 @@ +.\" $FreeBSD$ +Set to not build iconv as part of libc. diff --git a/tools/build/options/WITHOUT_LDNS b/tools/build/options/WITHOUT_LDNS index c56aecf8b1b..446d3e82e91 100644 --- a/tools/build/options/WITHOUT_LDNS +++ b/tools/build/options/WITHOUT_LDNS @@ -1,2 +1,2 @@ .\" $FreeBSD$ -Setting this variable will prevent LDNS from being built. +Setting this variable will prevent the LDNS library from being built. diff --git a/tools/build/options/WITHOUT_UNBOUND b/tools/build/options/WITHOUT_UNBOUND new file mode 100644 index 00000000000..1f39ea05634 --- /dev/null +++ b/tools/build/options/WITHOUT_UNBOUND @@ -0,0 +1,4 @@ +.\" $FreeBSD$ +Set to not build +.Xr unbound 8 +and related programs. diff --git a/tools/build/options/WITH_BSDCONFIG b/tools/build/options/WITH_BSDCONFIG deleted file mode 100644 index f3e6e25b660..00000000000 --- a/tools/build/options/WITH_BSDCONFIG +++ /dev/null @@ -1,2 +0,0 @@ -.\" $FreeBSD$ -Set to install bsdconfig(8), a BSD-licensed configuration/management utility. diff --git a/tools/build/options/WITH_GCC b/tools/build/options/WITH_GCC new file mode 100644 index 00000000000..c121fe5ec19 --- /dev/null +++ b/tools/build/options/WITH_GCC @@ -0,0 +1,2 @@ +.\" $FreeBSD$ +Set to build and install gcc and g++. diff --git a/tools/build/options/WITH_GNUCXX b/tools/build/options/WITH_GNUCXX new file mode 100644 index 00000000000..c49e7cd9e1c --- /dev/null +++ b/tools/build/options/WITH_GNUCXX @@ -0,0 +1,3 @@ +.\" $FreeBSD$ +Build the GNU C++ stack (g++, libstdc++). +This is the default on platforms where gcc is the system compiler. diff --git a/tools/build/options/WITH_GNU_PATCH b/tools/build/options/WITH_GNU_PATCH deleted file mode 100644 index d2a751f9296..00000000000 --- a/tools/build/options/WITH_GNU_PATCH +++ /dev/null @@ -1,2 +0,0 @@ -.\" $FreeBSD$ -Install GNU-licensed patch as 'patch' instead of BSD patch. diff --git a/tools/build/options/WITH_ICONV b/tools/build/options/WITH_ICONV deleted file mode 100644 index 7acbe44403f..00000000000 --- a/tools/build/options/WITH_ICONV +++ /dev/null @@ -1,2 +0,0 @@ -.\" $FreeBSD$ -Set to build iconv as part of libc. diff --git a/tools/build/options/WITH_LIBICONV_COMPAT b/tools/build/options/WITH_LIBICONV_COMPAT new file mode 100644 index 00000000000..f414798f672 --- /dev/null +++ b/tools/build/options/WITH_LIBICONV_COMPAT @@ -0,0 +1,2 @@ +.\" $FreeBSD$ +Set to build libiconv API and link time compatibility. diff --git a/tools/build/options/WITH_USB_GADGET_EXAMPLES b/tools/build/options/WITH_USB_GADGET_EXAMPLES new file mode 100644 index 00000000000..2ecbdb72c00 --- /dev/null +++ b/tools/build/options/WITH_USB_GADGET_EXAMPLES @@ -0,0 +1,2 @@ +.\" $FreeBSD$ +Set to build USB gadget kernel modules. diff --git a/tools/make_libdeps.sh b/tools/make_libdeps.sh index 3b59488e1cf..2f5b32b1928 100644 --- a/tools/make_libdeps.sh +++ b/tools/make_libdeps.sh @@ -28,6 +28,7 @@ export PATH=/bin:/usr/bin +LC_ALL=C # make sort deterministic FS=': ' # internal field separator LIBDEPENDS=./_libdeps # intermediate output file USRSRC=${1:-/usr/src} # source root @@ -64,7 +65,7 @@ genlibdepends() { ( cd ${USRSRC} - find ${LIBS} -mindepth 1 -name Makefile | + find -s ${LIBS} -mindepth 1 -name Makefile | xargs grep -l 'bsd\.lib\.mk' | while read makefile; do libdir=$(dirname ${makefile}) diff --git a/tools/regression/bin/sh/builtins/alias4.0 b/tools/regression/bin/sh/builtins/alias4.0 new file mode 100644 index 00000000000..3d5efeccf2c --- /dev/null +++ b/tools/regression/bin/sh/builtins/alias4.0 @@ -0,0 +1,4 @@ +# $FreeBSD$ + +unalias -a +alias -- diff --git a/tools/regression/bin/sh/builtins/jobid1.0 b/tools/regression/bin/sh/builtins/jobid1.0 new file mode 100644 index 00000000000..483fda20743 --- /dev/null +++ b/tools/regression/bin/sh/builtins/jobid1.0 @@ -0,0 +1,7 @@ +# $FreeBSD$ +# Non-standard builtin. + +: & +p1=$! +p2=$(jobid) +[ "${p1:?}" = "${p2:?}" ] diff --git a/tools/regression/bin/sh/builtins/jobid2.0 b/tools/regression/bin/sh/builtins/jobid2.0 new file mode 100644 index 00000000000..101831a2e4b --- /dev/null +++ b/tools/regression/bin/sh/builtins/jobid2.0 @@ -0,0 +1,9 @@ +# $FreeBSD$ + +: & +p1=$(jobid) +p2=$(jobid --) +p3=$(jobid %+) +p4=$(jobid -- %+) +[ "${p1:?}" = "${p2:?}" ] && [ "${p2:?}" = "${p3:?}" ] && +[ "${p3:?}" = "${p4:?}" ] && [ "${p4:?}" = "${p1:?}" ] diff --git a/tools/regression/bin/sh/builtins/local4.0 b/tools/regression/bin/sh/builtins/local4.0 new file mode 100644 index 00000000000..3955aaa12f1 --- /dev/null +++ b/tools/regression/bin/sh/builtins/local4.0 @@ -0,0 +1,12 @@ +# $FreeBSD$ + +f() { + local -- x + x=2 + [ "$x" = 2 ] +} +x=1 +f || exit 3 +[ "$x" = 1 ] || exit 3 +f || exit 3 +[ "$x" = 1 ] || exit 3 diff --git a/tools/regression/bin/sh/builtins/return8.0 b/tools/regression/bin/sh/builtins/return8.0 new file mode 100644 index 00000000000..f00e859a74c --- /dev/null +++ b/tools/regression/bin/sh/builtins/return8.0 @@ -0,0 +1,13 @@ +# $FreeBSD$ + +if [ "$1" = nested ]; then + return 17 +fi + +f() { + set -- nested + . "$0" + return $(($? ^ 1)) +} +f +exit $(($? ^ 16)) diff --git a/tools/regression/bin/sh/builtins/type3.0 b/tools/regression/bin/sh/builtins/type3.0 new file mode 100644 index 00000000000..87cccdd812c --- /dev/null +++ b/tools/regression/bin/sh/builtins/type3.0 @@ -0,0 +1,3 @@ +# $FreeBSD$ + +[ "$(type type)" = "$(type -- type)" ] diff --git a/tools/regression/bin/sh/expansion/arith13.0 b/tools/regression/bin/sh/expansion/arith13.0 new file mode 100644 index 00000000000..207e4881935 --- /dev/null +++ b/tools/regression/bin/sh/expansion/arith13.0 @@ -0,0 +1,6 @@ +# $FreeBSD$ +# Pre-increment and pre-decrement in arithmetic expansion are not in POSIX. +# Require either an error or a correct implementation. + +! (eval 'x=4; [ $((++x)) != 5 ] || [ $x != 5 ]') 2>/dev/null && +! (eval 'x=2; [ $((--x)) != 1 ] || [ $x != 1 ]') 2>/dev/null diff --git a/tools/regression/bin/sh/parser/empty-cmd1.0 b/tools/regression/bin/sh/parser/empty-cmd1.0 new file mode 100644 index 00000000000..f8b01e9c799 --- /dev/null +++ b/tools/regression/bin/sh/parser/empty-cmd1.0 @@ -0,0 +1,3 @@ +# $FreeBSD$ + +! (eval ': || f()') 2>/dev/null diff --git a/tools/regression/bin/sh/parser/only-redir2.0 b/tools/regression/bin/sh/parser/only-redir2.0 new file mode 100644 index 00000000000..b9e9501c202 --- /dev/null +++ b/tools/regression/bin/sh/parser/only-redir2.0 @@ -0,0 +1,2 @@ +# $FreeBSD$ + current maximum number * of open files limit work. + * Test #24: check if dup3(O_CLOEXEC) works. + * Test #25: check if dup3(O_CLOEXEC) returned a fd we asked for. + * Test #26: check if dup3(O_CLOEXEC) set close-on-exec flag for duped fd. + * Test #27: check if dup3(0) works. + * Test #28: check if dup3(0) returned a fd we asked for. + * Test #29: check if dup3(0) cleared close-on-exec flag for duped fd. + * Test #30: check if dup3(O_CLOEXEC) fails if oldfd == newfd. + * Test #31: check if dup3(0) fails if oldfd == newfd. + * Test #32: check if dup3(O_CLOEXEC) to a fd > current maximum number of + * open files limit work. */ #include @@ -74,7 +84,7 @@ main(int __unused argc, char __unused *argv[]) orgfd = getafile(); - printf("1..23\n"); + printf("1..32\n"); /* If dup(2) ever work? */ if ((fd1 = dup(orgfd)) < 0) @@ -297,5 +307,80 @@ main(int __unused argc, char __unused *argv[]) printf("ok %d - fcntl(F_DUP2FD_CLOEXEC) didn't bypass NOFILE limit\n", test); + /* Does dup3(O_CLOEXEC) ever work? */ + if ((fd2 = dup3(fd1, fd1 + 1, O_CLOEXEC)) < 0) + err(1, "dup3(O_CLOEXEC)"); + printf("ok %d - dup3(O_CLOEXEC) works\n", ++test); + + /* Do we get the right fd? */ + ++test; + if (fd2 != fd1 + 1) + printf( + "no ok %d - dup3(O_CLOEXEC) didn't give us the right fd\n", + test); + else + printf("ok %d - dup3(O_CLOEXEC) returned a correct fd\n", + test); + + /* Was close-on-exec set? */ + ++test; + if (fcntl(fd2, F_GETFD) != FD_CLOEXEC) + printf( + "not ok %d - dup3(O_CLOEXEC) didn't set close-on-exec\n", + test); + else + printf("ok %d - dup3(O_CLOEXEC) set close-on-exec\n", + test); + + /* Does dup3(0) ever work? */ + if ((fd2 = dup3(fd1, fd1 + 1, 0)) < 0) + err(1, "dup3(0)"); + printf("ok %d - dup3(0) works\n", ++test); + + /* Do we get the right fd? */ + ++test; + if (fd2 != fd1 + 1) + printf( + "no ok %d - dup3(0) didn't give us the right fd\n", + test); + else + printf("ok %d - dup3(0) returned a correct fd\n", + test); + + /* Was close-on-exec cleared? */ + ++test; + if (fcntl(fd2, F_GETFD) != 0) + printf( + "not ok %d - dup3(0) didn't clear close-on-exec\n", + test); + else + printf("ok %d - dup3(0) cleared close-on-exec\n", + test); + + /* dup3() does not allow duplicating to the same fd */ + ++test; + if (dup3(fd1, fd1, O_CLOEXEC) != -1) + printf( + "not ok %d - dup3(fd1, fd1, O_CLOEXEC) succeeded\n", test); + else + printf("ok %d - dup3(fd1, fd1, O_CLOEXEC) failed\n", test); + + ++test; + if (dup3(fd1, fd1, 0) != -1) + printf( + "not ok %d - dup3(fd1, fd1, 0) succeeded\n", test); + else + printf("ok %d - dup3(fd1, fd1, 0) failed\n", test); + + ++test; + if (getrlimit(RLIMIT_NOFILE, &rlp) < 0) + err(1, "getrlimit"); + if ((fd2 = dup3(fd1, rlp.rlim_cur + 1, O_CLOEXEC)) >= 0) + printf("not ok %d - dup3(O_CLOEXEC) bypassed NOFILE limit\n", + test); + else + printf("ok %d - dup3(O_CLOEXEC) didn't bypass NOFILE limit\n", + test); + return (0); } diff --git a/tools/regression/file/fcntlflags/Makefile b/tools/regression/file/fcntlflags/Makefile new file mode 100644 index 00000000000..9e7fc3e2ae6 --- /dev/null +++ b/tools/regression/file/fcntlflags/Makefile @@ -0,0 +1,7 @@ +# $FreeBSD$ + +PROG= fcntlflags +MAN= +WARNS?= 6 + +.include diff --git a/tools/regression/file/fcntlflags/fcntlflags.c b/tools/regression/file/fcntlflags/fcntlflags.c new file mode 100644 index 00000000000..bcb3b54d604 --- /dev/null +++ b/tools/regression/file/fcntlflags/fcntlflags.c @@ -0,0 +1,110 @@ +/*- + * Copyright (c) 2013 Jilles Tjoelker + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + * $FreeBSD$ + */ + +#include + +#include +#include +#include + +/* + * O_ACCMODE is currently defined incorrectly. This is what it should be. + * Various code depends on the incorrect value. + */ +#define CORRECT_O_ACCMODE (O_ACCMODE | O_EXEC) + +static int testnum; + +static void +subtests(const char *path, int omode, const char *omodetext) +{ + int fd, flags1, flags2, flags3; + + fd = open(path, omode); + if (fd == -1) + printf("not ok %d - open(\"%s\", %s) failed\n", + testnum++, path, omodetext); + else + printf("ok %d - open(\"%s\", %s) succeeded\n", + testnum++, path, omodetext); + flags1 = fcntl(fd, F_GETFL); + if (flags1 == -1) + printf("not ok %d - fcntl(F_GETFL) failed\n", testnum++); + else if ((flags1 & CORRECT_O_ACCMODE) == omode) + printf("ok %d - fcntl(F_GETFL) gave correct result\n", + testnum++); + else + printf("not ok %d - fcntl(F_GETFL) gave incorrect result " + "(%#x & %#x != %#x)\n", + testnum++, flags1, CORRECT_O_ACCMODE, omode); + if (fcntl(fd, F_SETFL, flags1) == -1) + printf("not ok %d - fcntl(F_SETFL) same flags failed\n", + testnum++); + else + printf("ok %d - fcntl(F_SETFL) same flags succeeded\n", + testnum++); + flags2 = fcntl(fd, F_GETFL); + if (flags2 == -1) + printf("not ok %d - fcntl(F_GETFL) failed\n", testnum++); + else if (flags2 == flags1) + printf("ok %d - fcntl(F_GETFL) gave same result\n", + testnum++); + else + printf("not ok %d - fcntl(F_SETFL) caused fcntl(F_GETFL) to " + "change from %#x to %#x\n", + testnum++, flags1, flags2); + if (fcntl(fd, F_SETFL, flags2 | O_NONBLOCK) == -1) + printf("not ok %d - fcntl(F_SETFL) O_NONBLOCK failed\n", + testnum++); + else + printf("ok %d - fcntl(F_SETFL) O_NONBLOCK succeeded\n", + testnum++); + flags3 = fcntl(fd, F_GETFL); + if (flags3 == -1) + printf("not ok %d - fcntl(F_GETFL) failed\n", testnum++); + else if (flags3 == (flags2 | O_NONBLOCK)) + printf("ok %d - fcntl(F_GETFL) gave expected result\n", + testnum++); + else + printf("not ok %d - fcntl(F_SETFL) gave unexpected result " + "(%#x != %#x)\n", + testnum++, flags3, flags2 | O_NONBLOCK); + (void)close(fd); +} + +int +main(int argc __unused, char **argv __unused) +{ + printf("1..24\n"); + testnum = 1; + subtests("/dev/null", O_RDONLY, "O_RDONLY"); + subtests("/dev/null", O_WRONLY, "O_WRONLY"); + subtests("/dev/null", O_RDWR, "O_RDWR"); + subtests("/bin/sh", O_EXEC, "O_EXEC"); + return (0); +} diff --git a/tools/regression/file/fcntlflags/fcntlflags.t b/tools/regression/file/fcntlflags/fcntlflags.t new file mode 100644 index 00000000000..8bdfd03be81 --- /dev/null +++ b/tools/regression/file/fcntlflags/fcntlflags.t @@ -0,0 +1,10 @@ +#!/bin/sh +# $FreeBSD$ + +cd `dirname $0` + +executable=`basename $0 .t` + +make $executable 2>&1 > /dev/null + +exec ./$executable diff --git a/tools/regression/iscsi/ctl.conf b/tools/regression/iscsi/ctl.conf new file mode 100644 index 00000000000..a6667cc8c34 --- /dev/null +++ b/tools/regression/iscsi/ctl.conf @@ -0,0 +1,35 @@ +auth-group meh { + chap user secretsecret +} + +portal-group meh { + listen 0.0.0.0 + discovery-auth-group no-authentication +} + +target iqn.2012-06.com.example:1 { + auth-group no-authentication + portal-group meh + lun 0 { + path /var/tmp/example_t1l0 + size 4G + } + lun 1 { + path /var/tmp/example_t1l1 + size 4G + } +} + +target iqn.2012-06.com.example:2 { + auth-group meh + portal-group meh + lun 0 { + path /var/tmp/example_t2l0 + size 4G + } + lun 1 { + path /var/tmp/example_t2l1 + size 4G + } +} + diff --git a/tools/regression/iscsi/initiator-instructions.txt b/tools/regression/iscsi/initiator-instructions.txt new file mode 100644 index 00000000000..11c3311cddb --- /dev/null +++ b/tools/regression/iscsi/initiator-instructions.txt @@ -0,0 +1,59 @@ +How to prepare initiator virtual machines for iSCSI target testing +------------------------------------------------------------------ + +1. Install operating systems. + + - FreeBSD: Use default settings for everything. Don't install + ports from the system installer, use "portsnap fetch extract" + after installation instead. + + - Fedora: Change the environment to "Minimal install". + + - Solaris: Use defaults. + +2. Install required software. + + - FreeBSD: install from ports, with 'make install BATCH=1': + benchmarks/bonnie++ + benchmarks/iozone + benchmarks/postmark + databases/postgresql92-server + databases/postgresql92-contrib + + - Fedora: + yum install btrfs-progs bonnie++ postgresql-server postgresql-contrib iscsi-initiator-utils + chkconfig iscsid on + chkconfig iscsi on + + After that, install iozone and postmark from source; they are not + provided by Fedora; download sites: + + http://www.iozone.org/src/current/iozone3_397.tar + http://www.gtlib.gatech.edu/pub/debian/pool/main/p/postmark/postmark_1.53.orig.tar.gz + + To build iozone, use "make linux". Copy the 'postmark' and 'iozone' binaries + to /usr/local/bin/. + + - Solaris: + Install gcc: + + pkg install gcc-45 + pkg install system/header + + After that, install bonnie++, iozone, and postmark from source; download sites: + + http://www.coker.com.au/bonnie++/experimental/bonnie++-1.97.tgz + http://www.iozone.org/src/current/iozone3_397.tar + http://www.gtlib.gatech.edu/pub/debian/pool/main/p/postmark/postmark_1.53.orig.tar.gz + + To build iozone, use "make Solaris10gcc". Copy the 'bonnie++', 'postmark', and 'iozone' + binaries to /usr/bin/. + + Fetch the binary PostgreSQL distribution from the link below and untar to /usr/postgres/: + + http://ftp.postgresql.org/pub/binary/v9.2.3/solaris/solaris11/i386/postgresql-9.2.3-S11.i386-32.tar.bz2 + +3. Run the test script. + + ./iscsi-test.sh + diff --git a/tools/regression/iscsi/iscsi-test.sh b/tools/regression/iscsi/iscsi-test.sh new file mode 100644 index 00000000000..6a9ea23bb8c --- /dev/null +++ b/tools/regression/iscsi/iscsi-test.sh @@ -0,0 +1,794 @@ +#!/bin/sh +# +# Copyright (c) 2012 The FreeBSD Foundation +# All rights reserved. +# +# This software was developed by Edward Tomasz Napierala under sponsorship +# from the FreeBSD Foundation. +# +# 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. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. +# +# $FreeBSD$ +# + +# +# This expects that the iSCSI server being tested is at $TARGETIP and exports +# two targets: $TARGET1 and $TARGET2; the former requiring no authentication, +# and the latter using CHAP with user $USER and secret $SECRET. Discovery +# must be permitted without authentication. Each target must contain exactly +# two LUNs, 4GB each. For example, ctl.conf(5) should look like this: +# +# auth-group meh { +# chap user secretsecret +# } +# +# portal-group meh { +# listen 0.0.0.0 +# discovery-auth-group no-authentication +# } +# +# target iqn.2012-06.com.example:1 { +# auth-group no-authentication +# portal-group meh +# lun 0 { +# path /var/tmp/example_t1l0 +# size 4G +# } +# lun 1 { +# path /var/tmp/example_t1l1 +# size 4G +# } +# } +# +# target iqn.2012-06.com.example:2 { +# auth-group meh +# portal-group meh +# lun 0 { +# path /var/tmp/example_t2l0 +# size 4G +# } +# lun 1 { +# path /var/tmp/example_t2l1 +# size 4G +# } +# } +# +# Remember to create the backing files (/var/tmp/example_t1l0 etcc) +# +# On the initiator, $MNTDIR will be used for testing. +# + +TARGETIP=192.168.56.101 +TARGET1=iqn.2012-06.com.example:1 +TARGET2=iqn.2012-06.com.example:2 +USER=user +SECRET=secretsecret +MNTDIR=/mnt +TMPDIR=/tmp + +die() { + echo "$*" + exit 1 +} + +case `uname` in + FreeBSD) + LUN0=/dev/da0 + LUN1=/dev/da1 + LUN2=/dev/da2 + LUN3=/dev/da3 + ZFSPOOL=iscsipool + ;; + Linux) + LUN0=/dev/sdb + LUN1=/dev/sdc + LUN2=/dev/sdd + LUN3=/dev/sde + ;; + SunOS) + # LUN names are being set later, during attach. + ZFSPOOL=iscsipool + ;; + *) + die "unsupported system" + ;; +esac + +check() { + echo "# $@" > /dev/stderr + $@ || die "$@ failed" +} + +banner() { + echo "Will try to attach to $TARGET1 and $TARGET2 on $TARGETIP," + echo "user $USER, secret $SECRET. Will use mountpoint $MNTDIR, temporary dir $TMPDIR," + if [ -n "$LUN0" ]; then + echo "scratch disks $LUN0, $LUN1, $LUN2, $LUN3." + else + echo "scratch disks unknown at this stage." + fi + echo + echo "This script is NOT safe to run on multiuser system." + echo + echo "Press ^C to interrupt; will proceed in 5 seconds." + sleep 5 +} + +test_discovery_freebsd_9() { + kldload iscsi_initiator + check iscontrol -dt $TARGETIP > $TMPDIR/discovered + cat $TMPDIR/discovered + echo "TargetName=$TARGET1" > $TMPDIR/expected + echo "TargetName=$TARGET2" >> $TMPDIR/expected + check cmp $TMPDIR/expected $TMPDIR/discovered + rm -f $TMPDIR/expected $TMPDIR/discovered +} + +test_discovery_freebsd() { + /etc/rc.d/iscsid onestart + check iscsictl -Ad $TARGETIP + sleep 1 + iscsictl | awk '{ print $1 }' | sort > $TMPDIR/discovered + printf "Target\n$TARGET1\n$TARGET2\n" | sort > $TMPDIR/expected + check cmp $TMPDIR/expected $TMPDIR/discovered + rm -f $TMPDIR/expected $TMPDIR/discovered + check iscsictl -Ra + sleep 1 +} + +test_discovery_linux() { + cat > /etc/iscsi/iscsid.conf << END + +discovery.sendtargets.auth.authmethod = None +node.startup = manual + +END + + check iscsiadm -m discovery -t sendtargets -p $TARGETIP > $TMPDIR/discovered + cat $TMPDIR/discovered + echo "$TARGETIP:3260,-1 $TARGET1" > $TMPDIR/expected + echo "$TARGETIP:3260,-1 $TARGET2" >> $TMPDIR/expected + check cmp $TMPDIR/expected $TMPDIR/discovered + rm -f $TMPDIR/expected $TMPDIR/discovered + +} + +test_discovery_solaris() { + check iscsiadm add discovery-address $TARGETIP + check iscsiadm modify discovery --sendtargets enable + check iscsiadm modify discovery --static enable + check iscsiadm list target | awk '/^Target/ { print $2 }' | sort > $TMPDIR/discovered + check iscsiadm remove discovery-address $TARGETIP + cat $TMPDIR/discovered + echo "$TARGET1" > $TMPDIR/expected + echo "$TARGET2" >> $TMPDIR/expected + check cmp $TMPDIR/expected $TMPDIR/discovered + rm -f $TMPDIR/expected $TMPDIR/discovered +} + +test_discovery() { + echo "*** discovery test ***" + case `uname` in + FreeBSD) + case `uname -r` in + 9*) + test_discovery_freebsd_9 + ;; + *) + test_discovery_freebsd + ;; + esac + ;; + Linux) + test_discovery_linux + ;; + SunOS) + test_discovery_solaris + ;; + *) + die "unsupported system" + ;; + esac +} + +test_attach_freebsd_9() { + [ ! -e LUN0 ] || die "$LUN0 already exists" + [ ! -e LUN1 ] || die "$LUN1 already exists" + [ ! -e LUN2 ] || die "$LUN2 already exists" + [ ! -e LUN3 ] || die "$LUN3 already exists" + + cat > $TMPDIR/iscsi.conf << END + +target1 { + TargetName = $TARGET1 + TargetAddress = $TARGETIP +} + +target2 { + TargetName = $TARGET2 + TargetAddress = $TARGETIP + AuthMethod = CHAP + chapIName = $USER + chapSecret = $SECRET +} + +END + check iscontrol -c $TMPDIR/iscsi.conf -n target1 + check iscontrol -c $TMPDIR/iscsi.conf -n target2 + + echo "Waiting 10 seconds for attach to complete." + sleep 10 + + [ -e $LUN0 ] || die "$LUN0 doesn't exist" + [ -e $LUN1 ] || die "$LUN1 doesn't exist" + [ -e $LUN2 ] || die "$LUN2 doesn't exist" + [ -e $LUN3 ] || die "$LUN3 doesn't exist" + + rm $TMPDIR/iscsi.conf +} + +test_attach_freebsd() { + [ ! -e LUN0 ] || die "$LUN0 already exists" + [ ! -e LUN1 ] || die "$LUN1 already exists" + [ ! -e LUN2 ] || die "$LUN2 already exists" + [ ! -e LUN3 ] || die "$LUN3 already exists" + + cat > $TMPDIR/iscsi.conf << END + +target1 { + TargetName = $TARGET1 + TargetAddress = $TARGETIP +} + +target2 { + TargetName = $TARGET2 + TargetAddress = $TARGETIP + AuthMethod = CHAP + chapIName = $USER + chapSecret = $SECRET +} + +END + check iscsictl -Ac $TMPDIR/iscsi.conf -n target1 + check iscsictl -Ac $TMPDIR/iscsi.conf -n target2 + + echo "Waiting 10 seconds for attach to complete." + sleep 10 + + [ -e $LUN0 ] || die "$LUN0 doesn't exist" + [ -e $LUN1 ] || die "$LUN1 doesn't exist" + [ -e $LUN2 ] || die "$LUN2 doesn't exist" + [ -e $LUN3 ] || die "$LUN3 doesn't exist" + + rm $TMPDIR/iscsi.conf +} + +test_attach_linux() { + check iscsiadm --mode node --targetname "$TARGET1" -p "$TARGETIP:3260" --op=update --name node.session.auth.authmethod --value=None + check iscsiadm --mode node --targetname "$TARGET1" -p "$TARGETIP:3260" --login + check iscsiadm --mode node --targetname "$TARGET2" -p "$TARGETIP:3260" --op=update --name node.session.auth.authmethod --value=CHAP + check iscsiadm --mode node --targetname "$TARGET2" -p "$TARGETIP:3260" --op=update --name node.session.auth.username --value="$USER" + check iscsiadm --mode node --targetname "$TARGET2" -p "$TARGETIP:3260" --op=update --name node.session.auth.password --value="$SECRET" + check iscsiadm --mode node --targetname "$TARGET2" -p "$TARGETIP:3260" --login +} + +test_attach_solaris() { + # XXX: How to enter the CHAP secret from the script? For now, + # just use the first target, and thus first two LUNs. + #check iscsiadm modify initiator-node --authentication CHAP + #check iscsiadm modify initiator-node --CHAP-name $USER + #check iscsiadm modify initiator-node --CHAP-secret $SECRET + iscsiadm add static-config $TARGET1,$TARGETIP + LUN0=`iscsiadm list target -S | awk '/OS Device Name/ { print $4 }' | sed -n 1p` + LUN1=`iscsiadm list target -S | awk '/OS Device Name/ { print $4 }' | sed -n 2p` + LUN0=`echo ${LUN0}2 | sed 's/rdsk/dsk/'` + LUN1=`echo ${LUN1}2 | sed 's/rdsk/dsk/'` + [ -n "$LUN0" -a -n "LUN1" ] || die "attach failed" + echo "Scratch disks: $LUN0, $LUN1" +} + +test_attach() { + echo "*** attach test ***" + case `uname` in + FreeBSD) + case `uname -r` in + 9*) + test_attach_freebsd_9 + ;; + *) + test_attach_freebsd + ;; + esac + ;; + Linux) + test_attach_linux + ;; + SunOS) + test_attach_solaris + ;; + *) + die "unsupported system" + ;; + esac +} + +test_newfs_freebsd_ufs() { + echo "*** UFS filesystem smoke test ***" + check newfs $LUN0 + check newfs $LUN1 + check newfs $LUN2 + check newfs $LUN3 + check fsck -t ufs $LUN0 + check fsck -t ufs $LUN1 + check fsck -t ufs $LUN2 + check fsck -t ufs $LUN3 +} + +test_newfs_freebsd_zfs() { + echo "*** ZFS filesystem smoke test ***" + check zpool create -f $ZFSPOOL $LUN0 $LUN1 $LUN2 $LUN3 + check zpool destroy -f $ZFSPOOL +} + +test_newfs_linux_ext4() { + echo "*** ext4 filesystem smoke test ***" + yes | check mkfs $LUN0 + yes | check mkfs $LUN1 + yes | check mkfs $LUN2 + yes | check mkfs $LUN3 + check fsck -f $LUN0 + check fsck -f $LUN1 + check fsck -f $LUN2 + check fsck -f $LUN3 +} + +test_newfs_linux_btrfs() { + echo "*** btrfs filesystem smoke test ***" + check mkfs.btrfs $LUN0 $LUN1 $LUN2 $LUN3 +} + + +test_newfs_solaris_ufs() { + echo "*** UFS filesystem smoke test ***" + yes | check newfs $LUN0 + yes | check newfs $LUN1 + check fsck -F ufs $LUN0 + check fsck -F ufs $LUN1 +} + +test_newfs_solaris_zfs() { + echo "*** ZFS filesystem smoke test ***" + check zpool create -f $ZFSPOOL $LUN0 $LUN1 + check zpool destroy -f $ZFSPOOL +} + +test_newfs() { + case `uname` in + FreeBSD) + test_newfs_freebsd_ufs + test_newfs_freebsd_zfs + ;; + Linux) + test_newfs_linux_ext4 + test_newfs_linux_btrfs + ;; + SunOS) + test_newfs_solaris_ufs + test_newfs_solaris_zfs + ;; + *) + die "unsupported system" + ;; + esac +} + +test_cp() { + echo "*** basic filesystem test ***" + case `uname` in + FreeBSD) + check newfs $LUN0 + check mount -t ufs $LUN0 $MNTDIR + check dd if=/dev/urandom of=$MNTDIR/1 bs=1m count=500 + check cp $MNTDIR/1 $MNTDIR/2 + check umount $MNTDIR + check fsck -t ufs $LUN0 + check mount -t ufs $LUN0 $MNTDIR + check cmp $MNTDIR/1 $MNTDIR/2 + check umount $MNTDIR + + check zpool create -f $ZFSPOOL $LUN0 $LUN1 $LUN2 $LUN3 + check dd if=/dev/urandom of=/$ZFSPOOL/1 bs=1m count=500 + check zpool scrub $ZFSPOOL + check cp /$ZFSPOOL/1 /$ZFSPOOL/2 + check cmp /$ZFSPOOL/1 /$ZFSPOOL/2 + check zpool destroy -f $ZFSPOOL + ;; + Linux) + yes | check mkfs $LUN0 + check mount $LUN0 $MNTDIR + check dd if=/dev/urandom of=$MNTDIR/1 bs=1M count=500 + check cp $MNTDIR/1 $MNTDIR/2 + check umount $MNTDIR + check fsck -f $LUN0 + check mount $LUN0 $MNTDIR + check cmp $MNTDIR/1 $MNTDIR/2 + check umount $MNTDIR + + check mkfs.btrfs $LUN0 $LUN1 $LUN2 $LUN3 + check mount $LUN0 $MNTDIR + check dd if=/dev/urandom of=$MNTDIR/1 bs=1M count=500 + check cp $MNTDIR/1 $MNTDIR/2 + check umount $MNTDIR + check mount $LUN0 $MNTDIR + check cmp $MNTDIR/1 $MNTDIR/2 + check umount $MNTDIR + ;; + SunOS) + yes | check newfs $LUN0 + check mount -F ufs $LUN0 $MNTDIR + check dd if=/dev/urandom of=$MNTDIR/1 bs=1024k count=500 + check cp $MNTDIR/1 $MNTDIR/2 + check umount $MNTDIR + check fsck -yF ufs $LUN0 + check mount -F ufs $LUN0 $MNTDIR + check cmp $MNTDIR/1 $MNTDIR/2 + check umount $MNTDIR + + check zpool create -f $ZFSPOOL $LUN0 $LUN1 + check dd if=/dev/urandom of=/$ZFSPOOL/1 bs=1024k count=500 + check zpool scrub $ZFSPOOL + check cp /$ZFSPOOL/1 /$ZFSPOOL/2 + check cmp /$ZFSPOOL/1 /$ZFSPOOL/2 + check zpool destroy -f $ZFSPOOL + ;; + *) + die "unsupported system" + ;; + esac +} + +test_bonnie() { + echo "*** bonnie++ ***" + case `uname` in + FreeBSD) + check newfs $LUN0 + check mount -t ufs $LUN0 $MNTDIR + check cd $MNTDIR + check bonnie++ -u root -s 2g -r 1g -n0 + check bonnie++ -u root -s 0 + check cd - + check umount $MNTDIR + check fsck -t ufs $LUN0 + + check zpool create -f $ZFSPOOL $LUN0 $LUN1 $LUN2 $LUN3 + check cd /$ZFSPOOL + check bonnie++ -u root -s 2g -r 1g -n0 + check bonnie++ -u root -s 0 + check cd - + check zpool destroy -f $ZFSPOOL + ;; + Linux) + yes | check mkfs $LUN0 + check mount $LUN0 $MNTDIR + check cd $MNTDIR + check bonnie++ -u root -s 2g -r 1g -n0 + check bonnie++ -u root -s 0 + check cd - + check umount $MNTDIR + check fsck -f $LUN0 + + check mkfs.btrfs $LUN0 $LUN1 $LUN2 $LUN3 + check mount $LUN0 $MNTDIR + check cd $MNTDIR + check bonnie++ -u root -s 2g -r 1g -n0 + check bonnie++ -u root -s 0 + check cd - + check umount $MNTDIR + ;; + SunOS) + yes | check newfs $LUN0 + check mount -F ufs $LUN0 $MNTDIR + check cd $MNTDIR + check bonnie++ -u root -s 2g -r 1g -n0 + check bonnie++ -u root -s 0 + check cd - + check umount $MNTDIR + check fsck -yF ufs $LUN0 + + check zpool create -f $ZFSPOOL $LUN0 $LUN1 + check cd /$ZFSPOOL + check bonnie++ -u root -s 2g -r 1g -n0 + check bonnie++ -u root -s 0 + check cd - + check zpool destroy -f $ZFSPOOL + ;; + *) + die "unsupported system" + ;; + esac +} + +test_iozone() { + echo "*** iozone ***" + case `uname` in + FreeBSD) + check newfs $LUN0 + check mount -t ufs $LUN0 $MNTDIR + check cd $MNTDIR + check iozone -a + check cd - + check umount $MNTDIR + check fsck -t ufs $LUN0 + + check zpool create -f $ZFSPOOL $LUN0 $LUN1 $LUN2 $LUN3 + check cd /$ZFSPOOL + check iozone -a + check cd - + check zpool destroy -f $ZFSPOOL + ;; + Linux) + yes | check mkfs $LUN0 + check mount $LUN0 $MNTDIR + check cd $MNTDIR + check iozone -a + check cd - + check umount $MNTDIR + check fsck -f $LUN0 + + check mkfs.btrfs $LUN0 $LUN1 $LUN2 $LUN3 + check mount $LUN0 $MNTDIR + check cd $MNTDIR + check iozone -a + check cd - + check umount $MNTDIR + ;; + SunOS) + yes | check newfs $LUN0 + check mount -F ufs $LUN0 $MNTDIR + check cd $MNTDIR + check iozone -a + check cd - + check umount $MNTDIR + check fsck -yF ufs $LUN0 + + check zpool create -f $ZFSPOOL $LUN0 $LUN1 + check cd /$ZFSPOOL + check iozone -a + check cd - + check zpool destroy -f $ZFSPOOL + ;; + *) + die "unsupported system" + ;; + esac + +} + +test_postmark() { + echo "*** postmark ***" + case `uname` in + FreeBSD) + check newfs $LUN0 + check mount -t ufs $LUN0 $MNTDIR + check cd $MNTDIR + printf "set number 10000\nrun\n" | check postmark + check cd - + check umount $MNTDIR + check fsck -t ufs $LUN0 + + check zpool create -f $ZFSPOOL $LUN0 $LUN1 $LUN2 $LUN3 + check cd /$ZFSPOOL + printf "set number 10000\nrun\n" | check postmark + check cd - + check zpool destroy -f $ZFSPOOL + ;; + Linux) + yes | check mkfs $LUN0 + check mount $LUN0 $MNTDIR + check cd $MNTDIR + printf "set number 10000\nrun\n" | check postmark + check cd - + check umount $MNTDIR + check fsck -f $LUN0 + + check mkfs.btrfs $LUN0 $LUN1 $LUN2 $LUN3 + check mount $LUN0 $MNTDIR + check cd $MNTDIR + printf "set number 10000\nrun\n" | check postmark + check cd - + check umount $MNTDIR + ;; + SunOS) + yes | check newfs $LUN0 + check mount -F ufs $LUN0 $MNTDIR + check cd $MNTDIR + printf "set number 10000\nrun\n" | check postmark + check cd - + check umount $MNTDIR + check fsck -yF ufs $LUN0 + + check zpool create -f $ZFSPOOL $LUN0 $LUN1 + check cd /$ZFSPOOL + printf "set number 10000\nrun\n" | check postmark + check cd - + check zpool destroy -f $ZFSPOOL + ;; + *) + die "unsupported system" + ;; + esac +} + +test_postgresql_freebsd() { + check newfs $LUN0 + check mount -t ufs $LUN0 $MNTDIR + check chown pgsql $MNTDIR + check chmod 755 $MNTDIR + check cd / + # XXX: How to make 'check' work here? + su -m pgsql -c "initdb -D $MNTDIR/db" + su -m pgsql -c "pg_ctl -D $MNTDIR/db -l /tmp/log start" + check sleep 10 + su -m pgsql -c "pgbench -i postgres" + su -m pgsql -c "pgbench -t 10000 postgres" + su -m pgsql -c "pg_ctl -D $MNTDIR/db stop" + check cd - + check umount $MNTDIR + check fsck -t ufs $LUN0 + + check zpool create -f $ZFSPOOL $LUN0 $LUN1 $LUN2 $LUN3 + check chown pgsql /$ZFSPOOL + check chmod 755 /$ZFSPOOL + check cd / + # XXX: How to make 'check' work here? + su -m pgsql -c "initdb -D /$ZFSPOOL/db" + su -m pgsql -c "pg_ctl -D /$ZFSPOOL/db -l /tmp/log start" + check sleep 10 + su -m pgsql -c "pgbench -i postgres" + su -m pgsql -c "pgbench -t 10000 postgres" + su -m pgsql -c "pg_ctl -D /$ZFSPOOL/db stop" + check cd - + check zpool destroy -f $ZFSPOOL +} + +test_postgresql_linux() { + yes | check mkfs $LUN0 + check mount $LUN0 $MNTDIR + check chown postgres $MNTDIR + check chmod 755 $MNTDIR + check cd / + # XXX: How to make 'check' work here? + su -m postgres -c "initdb -D $MNTDIR/db" + su -m postgres -c "pg_ctl -D $MNTDIR/db -l /tmp/log start" + check sleep 5 + su -m postgres -c "pgbench -i" + su -m postgres -c "pgbench -t 10000" + su -m postgres -c "pg_ctl -D $MNTDIR/db stop" + check cd - + check umount $MNTDIR + check fsck -f $LUN0 + + check mkfs.btrfs $LUN0 $LUN1 $LUN2 $LUN3 + check mount $LUN0 $MNTDIR + check chown postgres $MNTDIR + check chmod 755 $MNTDIR + check cd / + su -m postgres -c "initdb -D $MNTDIR/db" + su -m postgres -c "pg_ctl -D $MNTDIR/db -l /tmp/log start" + check sleep 5 + su -m postgres -c "pgbench -i" + su -m postgres -c "pgbench -t 10000" + su -m postgres -c "pg_ctl -D $MNTDIR/db stop" + check cd - + check umount $MNTDIR +} + +test_postgresql_solaris() { + PATH="$PATH:/usr/postgres/9.2-pgdg/bin" export PATH + yes | check newfs $LUN0 + check mount -F ufs $LUN0 $MNTDIR + check chown postgres $MNTDIR + check chmod 755 $MNTDIR + check cd / + # XXX: How to make 'check' work here? + su postgres -c "initdb -D $MNTDIR/db" + su postgres -c "pg_ctl -D $MNTDIR/db -l /tmp/log start" + check sleep 10 + su postgres -c "pgbench -i postgres" + su postgres -c "pgbench -t 10000 postgres" + su postgres -c "pg_ctl -D $MNTDIR/db stop" + check cd - + check umount $MNTDIR + check fsck -yF ufs $LUN0 + + check zpool create -f $ZFSPOOL $LUN0 $LUN1 $LUN2 $LUN3 + check chown postgres /$ZFSPOOL + check chmod 755 /$ZFSPOOL + check cd / + # XXX: How to make 'check' work here? + su postgres -c "initdb -D /$ZFSPOOL/db" + su postgres -c "pg_ctl -D /$ZFSPOOL/db -l /tmp/log start" + check sleep 10 + su postgres -c "pgbench -i postgres" + su postgres -c "pgbench -t 10000 postgres" + su postgres -c "pg_ctl -D /$ZFSPOOL/db stop" + check cd - + check zpool destroy -f $ZFSPOOL +} + +test_postgresql() { + echo "*** postgresql ***" + case `uname` in + FreeBSD) + test_postgresql_freebsd + ;; + Linux) + test_postgresql_linux + ;; + SunOS) + test_postgresql_solaris + ;; + *) + die "unsupported system" + ;; + esac +} + +test_detach() { + echo "*** detach ***" + case `uname` in + FreeBSD) + case `uname -r` in + 9*) + echo "*** detaching not supported on FreeBSD 9 ***" + echo "*** please reboot the initiator VM before trying to run this script again ***" + ;; + *) + check iscsictl -Ra + ;; + esac + ;; + Linux) + check iscsiadm -m node --logout + ;; + SunOS) + check iscsiadm remove static-config $TARGET1,$TARGETIP + ;; + *) + die "unsupported system" + ;; + esac +} + +banner +test_discovery +test_attach +test_newfs +test_cp +test_bonnie +test_iozone +test_postmark +test_postgresql +test_detach + +echo "*** done ***" + diff --git a/tools/regression/lib/libc/gen/test-fnmatch.c b/tools/regression/lib/libc/gen/test-fnmatch.c index 2544067c136..fd33574643e 100644 --- a/tools/regression/lib/libc/gen/test-fnmatch.c +++ b/tools/regression/lib/libc/gen/test-fnmatch.c @@ -135,6 +135,8 @@ struct testcase { "\\[", "\\[", 0, FNM_NOMATCH, "\\(", "\\(", 0, FNM_NOMATCH, "\\a", "\\a", 0, FNM_NOMATCH, + "\\", "\\", 0, FNM_NOMATCH, + "\\", "", 0, 0, "\\*", "\\*", FNM_NOESCAPE, 0, "\\?", "\\?", FNM_NOESCAPE, 0, "\\", "\\", FNM_NOESCAPE, 0, @@ -236,6 +238,8 @@ write_sh_tests(const char *progname, int num) if (strchr(t->pattern, '\'') != NULL || strchr(t->string, '\'') != NULL) continue; + if (t->flags == 0 && strcmp(t->pattern, "\\") == 0) + continue; if (num == 1 && t->flags == 0) printf("test%smatch '%s' '%s'\n", t->result == FNM_NOMATCH ? "no" : "", diff --git a/tools/regression/lib/libc/stdio/Makefile b/tools/regression/lib/libc/stdio/Makefile index f496f73f86e..4b252b91c57 100644 --- a/tools/regression/lib/libc/stdio/Makefile +++ b/tools/regression/lib/libc/stdio/Makefile @@ -1,8 +1,8 @@ # $FreeBSD$ -TESTS= test-fmemopen test-getdelim test-open_memstream test-open_wmemstream \ - test-perror test-print-positional test-printbasic test-printfloat \ - test-scanfloat +TESTS= test-fmemopen test-getdelim test-mkostemp test-open_memstream \ + test-open_wmemstream test-perror test-print-positional test-printbasic \ + test-printfloat test-scanfloat CFLAGS+= -lm .PHONY: tests diff --git a/tools/regression/lib/libc/stdio/test-fmemopen.t b/tools/regression/lib/libc/stdio/test-fmemopen.t index 8bdfd03be81..bd5157b5d8f 100644 --- a/tools/regression/lib/libc/stdio/test-fmemopen.t +++ b/tools/regression/lib/libc/stdio/test-fmemopen.t @@ -7,4 +7,9 @@ executable=`basename $0 .t` make $executable 2>&1 > /dev/null -exec ./$executable +echo 1..1 +if ./$executable; then + echo ok 1 - $executable successful +else + echo not ok 1 - $executable failed +fi diff --git a/tools/regression/lib/libc/stdio/test-fopen.c b/tools/regression/lib/libc/stdio/test-fopen.c new file mode 100644 index 00000000000..8605717dba5 --- /dev/null +++ b/tools/regression/lib/libc/stdio/test-fopen.c @@ -0,0 +1,113 @@ +/*- + * Copyright (c) 2013 Jilles Tjoelker + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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 +__FBSDID("$FreeBSD$"); + +#include +#include +#include + +/* + * O_ACCMODE is currently defined incorrectly. This is what it should be. + * Various code depends on the incorrect value. + */ +#define CORRECT_O_ACCMODE (O_ACCMODE | O_EXEC) + +static int testnum = 1; + +static void +runtest(const char *fname, const char *mode) +{ + FILE *fp; + int fd, flags, wantedflags; + + fp = fopen(fname, mode); + if (fp == NULL) { + printf("not ok %d - fopen(\"%s\", \"%s\") failed\n", + testnum++, fname, mode); + printf("not ok %d - FD_CLOEXEC # SKIP\n", + testnum++); + return; + } + fd = fileno(fp); + if (fd < 0) + printf("not ok %d - fileno() failed\n", testnum++); + else + printf("ok %d - fopen(\"%s\", \"%s\") and fileno() succeeded\n", + testnum++, fname, mode); + if (fcntl(fd, F_GETFD) == (strchr(mode, 'e') != NULL ? FD_CLOEXEC : 0)) + printf("ok %d - FD_CLOEXEC flag correct\n", testnum++); + else + printf("not ok %d - FD_CLOEXEC flag incorrect\n", testnum++); + flags = fcntl(fd, F_GETFL); + if (strchr(mode, '+')) + wantedflags = O_RDWR | (*mode == 'a' ? O_APPEND : 0); + else if (*mode == 'r') + wantedflags = O_RDONLY; + else if (*mode == 'w') + wantedflags = O_WRONLY; + else if (*mode == 'a') + wantedflags = O_WRONLY | O_APPEND; + else + wantedflags = -1; + if (wantedflags == -1) + printf("not ok %d - unrecognized mode\n", testnum++); + else if ((flags & (CORRECT_O_ACCMODE | O_APPEND)) == wantedflags) + printf("ok %d - correct access mode\n", testnum++); + else + printf("not ok %d - incorrect access mode\n", testnum++); + fclose(fp); +} + +/* + * Test program for fopen(). + */ +int +main(int argc, char *argv[]) +{ + printf("1..45\n"); + runtest("/dev/null", "r"); + runtest("/dev/null", "r+"); + runtest("/dev/null", "w"); + runtest("/dev/null", "w+"); + runtest("/dev/null", "a"); + runtest("/dev/null", "a+"); + runtest("/dev/null", "re"); + runtest("/dev/null", "r+e"); + runtest("/dev/null", "we"); + runtest("/dev/null", "w+e"); + runtest("/dev/null", "ae"); + runtest("/dev/null", "a+e"); + runtest("/dev/null", "re+"); + runtest("/dev/null", "we+"); + runtest("/dev/null", "ae+"); + + return 0; +} + +/* vim:ts=8:cin:sw=8 + * */ diff --git a/tools/regression/lib/libc/stdio/test-fopen.t b/tools/regression/lib/libc/stdio/test-fopen.t new file mode 100644 index 00000000000..8bdfd03be81 --- /dev/null +++ b/tools/regression/lib/libc/stdio/test-fopen.t @@ -0,0 +1,10 @@ +#!/bin/sh +# $FreeBSD$ + +cd `dirname $0` + +executable=`basename $0 .t` + +make $executable 2>&1 > /dev/null + +exec ./$executable diff --git a/tools/regression/lib/libc/stdio/test-mkostemp.c b/tools/regression/lib/libc/stdio/test-mkostemp.c new file mode 100644 index 00000000000..5f67c72be4a --- /dev/null +++ b/tools/regression/lib/libc/stdio/test-mkostemp.c @@ -0,0 +1,164 @@ +/*- + * Copyright (c) 2013 Jilles Tjoelker + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + */ + +/* + * Test program for mkostemp(). + */ + +#include +__FBSDID("$FreeBSD$"); + +#include + +#include +#include +#include +#include +#include +#include +#include + +static const char template[] = _PATH_TMP "mkostemp.XXXXXXXX"; +static int testnum; + +#define MISCFLAGS (O_APPEND | O_DIRECT | O_SHLOCK | O_EXLOCK | O_SYNC) + +static void +test_one(int oflags) +{ + char tmpf[sizeof(template)]; + struct stat st1, st2; + int fd; + + memcpy(tmpf, template, sizeof(tmpf)); + fd = mkostemp(tmpf, oflags); + if (fd < 0) { + printf("not ok %d - oflags=%#x " + "mkostemp() reported failure: %s\n", + testnum++, oflags, strerror(errno)); + return; + } + if (memcmp(tmpf, template, sizeof(tmpf) - 8 - 1) != 0) { + printf("not ok %d - oflags=%#x " + "returned pathname does not match template: %s\n", + testnum++, oflags, tmpf); + return; + } + do { + if (fcntl(fd, F_GETFD) != + (oflags & O_CLOEXEC ? FD_CLOEXEC : 0)) { + printf("not ok %d - oflags=%#x " + "close-on-exec flag incorrect\n", + testnum++, oflags); + break; + } + if ((fcntl(fd, F_GETFL) & MISCFLAGS) != (oflags & MISCFLAGS)) { + printf("not ok %d - oflags=%#x " + "open flags incorrect\n", + testnum++, oflags); + break; + } + if (stat(tmpf, &st1) == -1) { + printf("not ok %d - oflags=%#x " + "cannot stat returned pathname %s: %s\n", + testnum++, oflags, tmpf, strerror(errno)); + break; + } + if (fstat(fd, &st2) == -1) { + printf("not ok %d - oflags=%#x " + "cannot fstat returned fd %d: %s\n", + testnum++, oflags, fd, strerror(errno)); + break; + } + if (!S_ISREG(st1.st_mode) || (st1.st_mode & 0777) != 0600 || + st1.st_nlink != 1 || st1.st_size != 0) { + printf("not ok %d - oflags=%#x " + "named file attributes incorrect\n", + testnum++, oflags); + break; + } + if (!S_ISREG(st2.st_mode) || (st2.st_mode & 0777) != 0600 || + st2.st_nlink != 1 || st2.st_size != 0) { + printf("not ok %d - oflags=%#x " + "opened file attributes incorrect\n", + testnum++, oflags); + break; + } + if (st1.st_dev != st2.st_dev || st1.st_ino != st2.st_ino) { + printf("not ok %d - oflags=%#x " + "named and opened file do not match\n", + testnum++, oflags); + break; + } + (void)unlink(tmpf); + if (fstat(fd, &st2) == -1) + printf("not ok %d - oflags=%#x " + "cannot fstat returned fd %d again: %s\n", + testnum++, oflags, fd, strerror(errno)); + else if (st2.st_nlink != 0) + printf("not ok %d - oflags=%#x " + "st_nlink is not 0 after unlink\n", + testnum++, oflags); + else + printf("ok %d - oflags=%#x\n", testnum++, oflags); + (void)close(fd); + return; + } while (0); + (void)close(fd); + (void)unlink(tmpf); +} + +static void +test_badflags(void) +{ + char tmpf[sizeof(template)]; + + memcpy(tmpf, template, sizeof(tmpf)); + if (mkostemp(tmpf, O_CREAT) == -1) + printf("ok %d - mkostemp(O_CREAT) correctly failed\n", + testnum++); + else + printf("not ok %d - mkostemp(O_CREAT) wrongly succeeded\n", + testnum++); +} + +int +main(int argc, char *argv[]) +{ + int i; + const char *e; + + printf("1..5\n"); + testnum = 1; + + test_one(0); + test_one(O_CLOEXEC); + test_one(O_APPEND); + test_one(O_APPEND | O_CLOEXEC); + test_badflags(); + + return (0); +} diff --git a/tools/regression/lib/libc/stdio/test-mkostemp.t b/tools/regression/lib/libc/stdio/test-mkostemp.t new file mode 100644 index 00000000000..8bdfd03be81 --- /dev/null +++ b/tools/regression/lib/libc/stdio/test-mkostemp.t @@ -0,0 +1,10 @@ +#!/bin/sh +# $FreeBSD$ + +cd `dirname $0` + +executable=`basename $0 .t` + +make $executable 2>&1 > /dev/null + +exec ./$executable diff --git a/tools/regression/lib/libc/stdio/test-open_memstream.t b/tools/regression/lib/libc/stdio/test-open_memstream.t index 8bdfd03be81..bd5157b5d8f 100644 --- a/tools/regression/lib/libc/stdio/test-open_memstream.t +++ b/tools/regression/lib/libc/stdio/test-open_memstream.t @@ -7,4 +7,9 @@ executable=`basename $0 .t` make $executable 2>&1 > /dev/null -exec ./$executable +echo 1..1 +if ./$executable; then + echo ok 1 - $executable successful +else + echo not ok 1 - $executable failed +fi diff --git a/tools/regression/lib/libc/stdio/test-open_wmemstream.t b/tools/regression/lib/libc/stdio/test-open_wmemstream.t index 8bdfd03be81..bd5157b5d8f 100644 --- a/tools/regression/lib/libc/stdio/test-open_wmemstream.t +++ b/tools/regression/lib/libc/stdio/test-open_wmemstream.t @@ -7,4 +7,9 @@ executable=`basename $0 .t` make $executable 2>&1 > /dev/null -exec ./$executable +echo 1..1 +if ./$executable; then + echo ok 1 - $executable successful +else + echo not ok 1 - $executable failed +fi diff --git a/tools/regression/sockets/sendfile/sendfile.c b/tools/regression/sockets/sendfile/sendfile.c index bd8c0c55751..73033d75c15 100644 --- a/tools/regression/sockets/sendfile/sendfile.c +++ b/tools/regression/sockets/sendfile/sendfile.c @@ -74,12 +74,13 @@ struct sendfile_test { uint32_t hdr_length; uint32_t offset; uint32_t length; + uint32_t file_size; }; -int file_fd; -char path[PATH_MAX]; -int listen_socket; -int accept_socket; +static int file_fd; +static char path[PATH_MAX]; +static int listen_socket; +static int accept_socket; static int test_th(struct test_header *th, uint32_t *header_length, uint32_t *offset, uint32_t *length); @@ -92,6 +93,7 @@ static int new_test_socket(int *connect_socket); static void init_th(struct test_header *th, uint32_t header_length, uint32_t offset, uint32_t length); static int send_test(int connect_socket, struct sendfile_test); +static int write_test_file(size_t file_size); static void run_parent(void); static void cleanup(void); @@ -278,15 +280,12 @@ send_test(int connect_socket, struct sendfile_test test) if (len != 0) FAIL_ERR("lseek") - if (test.length == 0) { - struct stat st; - if (fstat(file_fd, &st) < 0) - FAIL_ERR("fstat") - length = st.st_size - test.offset; - } - else { + struct stat st; + if (fstat(file_fd, &st) < 0) + FAIL_ERR("fstat") + length = st.st_size - test.offset; + if (test.length > 0 && test.length < (uint32_t)length) length = test.length; - } init_th(&th, test.hdr_length, test.offset, length); @@ -336,17 +335,55 @@ send_test(int connect_socket, struct sendfile_test test) return (0); } +static int +write_test_file(size_t file_size) +{ + char *page_buffer; + ssize_t len; + static size_t current_file_size = 0; + + if (file_size == current_file_size) + return (0); + else if (file_size < current_file_size) { + if (ftruncate(file_fd, file_size) != 0) + FAIL_ERR("ftruncate"); + current_file_size = file_size; + return (0); + } + + page_buffer = malloc(file_size); + if (page_buffer == NULL) + FAIL_ERR("malloc") + bzero(page_buffer, file_size); + + len = write(file_fd, page_buffer, file_size); + if (len < 0) + FAIL_ERR("write") + + len = lseek(file_fd, 0, SEEK_SET); + if (len < 0) + FAIL_ERR("lseek") + if (len != 0) + FAIL("len != 0") + + free(page_buffer); + current_file_size = file_size; + return (0); +} + static void run_parent(void) { int connect_socket; int status; int test_num; + int test_count; int pid; + size_t desired_file_size = 0; const int pagesize = getpagesize(); - struct sendfile_test tests[10] = { + struct sendfile_test tests[] = { { .hdr_length = 0, .offset = 0, .length = 1 }, { .hdr_length = 0, .offset = 0, .length = pagesize }, { .hdr_length = 0, .offset = 1, .length = 1 }, @@ -356,12 +393,23 @@ run_parent(void) { .hdr_length = 0, .offset = 0, .length = 0 }, { .hdr_length = 0, .offset = pagesize, .length = 0 }, { .hdr_length = 0, .offset = 2*pagesize, .length = 0 }, - { .hdr_length = 0, .offset = TEST_PAGES*pagesize, .length = 0 } + { .hdr_length = 0, .offset = TEST_PAGES*pagesize, .length = 0 }, + { .hdr_length = 0, .offset = 0, .length = pagesize, + .file_size = 1 } }; - printf("1..10\n"); + test_count = sizeof(tests) / sizeof(tests[0]); + printf("1..%d\n", test_count); - for (test_num = 1; test_num <= 10; test_num++) { + for (test_num = 1; test_num <= test_count; test_num++) { + + desired_file_size = tests[test_num - 1].file_size; + if (desired_file_size == 0) + desired_file_size = TEST_PAGES * pagesize; + if (write_test_file(desired_file_size) != 0) { + printf("not ok %d\n", test_num); + continue; + } pid = fork(); if (pid == -1) { @@ -411,17 +459,11 @@ cleanup(void) int main(int argc, char *argv[]) { - char *page_buffer; int pagesize; - ssize_t len; *path = '\0'; pagesize = getpagesize(); - page_buffer = malloc(TEST_PAGES * pagesize); - if (page_buffer == NULL) - FAIL_ERR("malloc") - bzero(page_buffer, TEST_PAGES * pagesize); if (argc == 1) { snprintf(path, PATH_MAX, "/tmp/sendfile.XXXXXXXXXXXX"); @@ -439,16 +481,6 @@ main(int argc, char *argv[]) atexit(cleanup); - len = write(file_fd, page_buffer, TEST_PAGES * pagesize); - if (len < 0) - FAIL_ERR("write") - - len = lseek(file_fd, 0, SEEK_SET); - if (len < 0) - FAIL_ERR("lseek") - if (len != 0) - FAIL("len != 0") - run_parent(); return (0); } diff --git a/tools/test/posixshm/shm_test.c b/tools/test/posixshm/shm_test.c index 3ab47326dd4..0ec890a8578 100644 --- a/tools/test/posixshm/shm_test.c +++ b/tools/test/posixshm/shm_test.c @@ -21,7 +21,7 @@ * Signal handler which does nothing. */ static void -ignoreit(int sig) +ignoreit(int sig __unused) { ; } @@ -29,13 +29,13 @@ ignoreit(int sig) int main(int argc, char **argv) { - char buf[1024], *cp; - int desc, rv; + char buf[1024], *cp, c; + int error, desc, rv; long scval; sigset_t ss; struct sigaction sa; void *region; - size_t psize; + size_t i, psize; #ifndef _POSIX_SHARED_MEMORY_OBJECTS printf("_POSIX_SHARED_MEMORY_OBJECTS is undefined\n"); @@ -118,14 +118,29 @@ main(int argc, char **argv) sigemptyset(&ss); sigsuspend(&ss); - for (cp = region; cp < (char *)region + psize; cp++) + for (cp = region; cp < (char *)region + psize; cp++) { if (*cp != '\151') _exit(1); + } + if (lseek(desc, 0, SEEK_SET) == -1) + _exit(1); + for (i = 0; i < psize; i++) { + error = read(desc, &c, 1); + if (c != '\151') + _exit(1); + } _exit(0); } else { int status; - memset(region, '\151', psize); + memset(region, '\151', psize - 2); + error = pwrite(desc, region, 2, psize - 2); + if (error != 2) { + if (error >= 0) + errx(1, "short write %d", error); + else + err(1, "shmfd write"); + } kill(rv, SIGUSR1); waitpid(rv, &status, 0); diff --git a/tools/tools/README b/tools/tools/README index 1260683cbba..ef05ee017c4 100644 --- a/tools/tools/README +++ b/tools/tools/README @@ -18,6 +18,7 @@ cxgbetool A tool for the cxgbe(4) driver. cxgbtool A tool for the cxgb(4) driver. diffburst OBSOLETE: equivalent functionality is available via split -p. For example: "split -p ^diff < patchfile". See split(1). +drm Tools specific to the DRM/KMS device drivers. editing Editor modes and the like to help editing FreeBSD code. epfe Extract printing filter examples from printing.sgml. ether_reflect An Ethernet packet reflector for low level testing. diff --git a/tools/tools/bus_autoconf/bus_load_file.c b/tools/tools/bus_autoconf/bus_load_file.c index 527e5bce49b..8d82ac3f7e9 100644 --- a/tools/tools/bus_autoconf/bus_load_file.c +++ b/tools/tools/bus_autoconf/bus_load_file.c @@ -39,7 +39,7 @@ void load_file(const char *fname, uint8_t **pptr, uint32_t *plen) { uint8_t *ptr; - uint32_t len; + ssize_t len; off_t off; int f; diff --git a/tools/tools/drm/README b/tools/tools/drm/README new file mode 100644 index 00000000000..35660ed0827 --- /dev/null +++ b/tools/tools/drm/README @@ -0,0 +1,5 @@ +# $FreeBSD$ + +gen-drm_pciids Generate drm_pciids.h based on Linux' drm_pciids.h, FreeBSD's + drm_pciids.h and misc/pciids database. +radeon Tools specific to the Radeon device driver. diff --git a/tools/tools/drm/gen-drm_pciids b/tools/tools/drm/gen-drm_pciids new file mode 100755 index 00000000000..02ed5620e2f --- /dev/null +++ b/tools/tools/drm/gen-drm_pciids @@ -0,0 +1,183 @@ +#!/usr/bin/perl +# $FreeBSD$ + +use strict; +use warnings; +use utf8; + +use File::Basename; + +my $progname = basename($0); + +sub parse_linux_header ($) +{ + my ($header) = @_; + + open(my $fh, '<', $header) or die "Can't open Linux header: $!\n"; + + my $in_list = 0; + + my %pciids = (); + + my $current_vendor_define; + + while (my $line = <$fh>) { + if ($line =~ /^#define +([^ ]+) +/) { + $current_vendor_define = $1; + $pciids{$current_vendor_define} = {}; + } elsif ($line =~ /^\t\{0x([0-9a-fA-F]{4}), *0x([0-9a-fA-F]{4}),[^,]+,[^,]+,[^,]+,[^,]+, *([^}]+)\}/) { + my $vendor_id = uc($1); + my $device_id = uc($2); + my $flags = $3; + + $pciids{$current_vendor_define}{$device_id} = { + 'vendor_id' => $vendor_id, + 'flags' => $flags + }; + } + } + + close($fh); + + return %pciids; +} + +sub parse_freebsd_header ($) { + my ($header) = @_; + + open(my $fh, '<', $header) or die "Can't open FreeBSD header: $!\n"; + + my $in_list = 0; + + my %pciids = (); + + my $current_vendor_define; + + while (my $line = <$fh>) { + if ($line =~ /^#define +([^ ]+) +/) { + $current_vendor_define = $1; + $pciids{$current_vendor_define} = {}; + } elsif ($line =~ /^\t\{0x([0-9a-fA-F]{4}), *0x([0-9a-fA-F]{4}), *([^,]+), *"([^"]+)"\}/) { + my $vendor_id = uc($1); + my $device_id = uc($2); + my $flags = $3; + my $name = $4; + + $pciids{$current_vendor_define}{$device_id} = { + 'vendor_id' => $vendor_id, + 'flags' => $flags, + 'name' => $name + }; + } + } + + close($fh); + + return %pciids; +} + +sub parse_pciids_db ($) { + my ($header) = @_; + + open(my $fh, '<', $header) or die "Can't open PCI IDs database: $!\n"; + + my %pciids = (); + + my $current_vendor_id; + + while (my $line = <$fh>) { + if (!$line || $line =~ /^#/) { + next; + } + if ($line =~ /^([0-9a-fA-F]{4}) (.+)/) { + # Vendor ID & name. + my $vendor_id = uc($1); + my $vendor_name = $2; + $pciids{$vendor_id} = { + 'name' => $vendor_name, + 'devices' => {} + }; + + $current_vendor_id = $vendor_id; + } elsif ($line =~ /^\t([0-9a-fA-F]{4}) (.+)/) { + # Device ID & name. + my $device_id = uc($1); + my $device_name = $2; + $pciids{$current_vendor_id}{'devices'}{$device_id} = $device_name; + } + } + + close($fh); + + return %pciids; +} + +if (scalar(@ARGV) != 3) { + print STDERR "Syntax: $progname []\n"; + exit 1; +} + +my $linux_header = $ARGV[0]; +my $freebsd_header = $ARGV[1]; +my $pciids_db = $ARGV[2]; +my $only_vendor = $ARGV[3]; + +my %linux_pciids = parse_linux_header($linux_header); +my %freebsd_pciids = parse_freebsd_header($freebsd_header); +my %pciids_db = parse_pciids_db($pciids_db); + +print STDERR "Update FreeBSD's PCI IDs:\n"; +foreach my $vendor_define (sort keys(%linux_pciids)) { + if ($only_vendor && $vendor_define ne $only_vendor) { + print STDERR "(skip unwanted define: $vendor_define)\n"; + next; + } elsif (!$only_vendor && !exists($freebsd_pciids{$vendor_define})) { + print STDERR "(skip unsupport define: $vendor_define)\n"; + next; + } + + foreach my $device_id (sort keys(%{$linux_pciids{$vendor_define}})) { + my $vendor_id = $linux_pciids{$vendor_define}{$device_id}{'vendor_id'}; + + if (exists($freebsd_pciids{$vendor_define}{$device_id})) { + print STDERR " $vendor_define: $vendor_id:$device_id already in header\n"; + next; + } + + my $flags = $linux_pciids{$vendor_define}{$device_id}{'flags'}; + my $name = $pciids_db{$vendor_id}{'devices'}{$device_id} || "Unknown device name"; + print STDERR " $vendor_define: $vendor_id:$device_id is missing ($name)\n"; + $freebsd_pciids{$vendor_define}{$device_id} = { + 'vendor_id' => $vendor_id, + 'flags' => $flags, + 'name' => $name + }; + } +} + +print STDERR "\nWrite FreeBSD header to stdout...\n"; +print <<"EOF"; +/* + * \$FreeBSD\$ + */ + +/* + * Generated by $progname from: + * o previous FreeBSD's drm_pciids.h + * o Linux' drm_pciids.h + * o the PCI ID repository (http://pciids.sourceforge.net/) + * + * See tools/tools/drm/$progname. + */ +EOF +foreach my $vendor_define (sort keys(%freebsd_pciids)) { + print "\n#define $vendor_define \\\n"; + foreach my $device_id (sort keys(%{$freebsd_pciids{$vendor_define}})) { + my $vendor_id = $freebsd_pciids{$vendor_define}{$device_id}{'vendor_id'}; + my $flags = $freebsd_pciids{$vendor_define}{$device_id}{'flags'}; + my $name = $freebsd_pciids{$vendor_define}{$device_id}{'name'}; + + print "\t{0x$vendor_id, 0x$device_id, $flags, \"$name\"}, \\\n"; + } + print "\t{0, 0, 0, NULL}\n"; +} diff --git a/tools/tools/drm/radeon/README b/tools/tools/drm/radeon/README new file mode 100644 index 00000000000..a05878ebb9e --- /dev/null +++ b/tools/tools/drm/radeon/README @@ -0,0 +1,4 @@ +# $FreeBSD$ + +firmwares Tools to handle Radeon firmwares imported into the tree. +mkregtable Generate headers used to build the Radeon driver. diff --git a/tools/tools/drm/radeon/firmwares/README b/tools/tools/drm/radeon/firmwares/README new file mode 100644 index 00000000000..1a28c83941d --- /dev/null +++ b/tools/tools/drm/radeon/firmwares/README @@ -0,0 +1,9 @@ +# $FreeBSD$ + +To update firmwares: + + 1. Clone the following Git repository: + git://git.kernel.org/pub/scm/linux/kernel/git/firmware/linux-firmware.git + + 2. Run this script as: + ./encode-firmwares /path/to/linux-firmwares/radeon diff --git a/tools/tools/drm/radeon/firmwares/encode-firmwares b/tools/tools/drm/radeon/firmwares/encode-firmwares new file mode 100755 index 00000000000..e95c0d2393f --- /dev/null +++ b/tools/tools/drm/radeon/firmwares/encode-firmwares @@ -0,0 +1,17 @@ +#!/bin/sh +# $FreeBSD$ + +set -e + +scriptdir=$(cd $(dirname $0) && pwd) +fwdir="$scriptdir/../../../../../sys/contrib/dev/drm2/radeonkmsfw" +srcdir=$1 + +if [ -z "$srcdir" -o ! -d "$srcdir" ]; then + echo "Syntax: $(basename $0) " 1>&2 + exit +fi + +for file in "$srcdir"/*.bin; do + uuencode -o "$fwdir"/$(basename $file).uu $file $(basename $file) +done diff --git a/tools/tools/drm/radeon/mkregtable/Makefile b/tools/tools/drm/radeon/mkregtable/Makefile new file mode 100644 index 00000000000..b83cf5ca774 --- /dev/null +++ b/tools/tools/drm/radeon/mkregtable/Makefile @@ -0,0 +1,58 @@ +# $FreeBSD$ + +all: regtables + +PROG= mkregtable + +SRCS= mkregtable.c + +NO_MAN= + +MKREGTABLE= ${PROG} +KERNSRCDIR= ${.CURDIR}/../../../../../sys +REG_SRCS_DIR= ${KERNSRCDIR}/dev/drm2/radeon/reg_srcs +REG_DEST_DIR= ${KERNSRCDIR}/dev/drm2/radeon + +regtables: \ + ${REG_DEST_DIR}/rn50_reg_safe.h \ + ${REG_DEST_DIR}/r100_reg_safe.h \ + ${REG_DEST_DIR}/r200_reg_safe.h \ + ${REG_DEST_DIR}/rv515_reg_safe.h \ + ${REG_DEST_DIR}/r300_reg_safe.h \ + ${REG_DEST_DIR}/r420_reg_safe.h \ + ${REG_DEST_DIR}/rs600_reg_safe.h \ + ${REG_DEST_DIR}/r600_reg_safe.h \ + ${REG_DEST_DIR}/evergreen_reg_safe.h \ + ${REG_DEST_DIR}/cayman_reg_safe.h + +${REG_DEST_DIR}/rn50_reg_safe.h: ${REG_SRCS_DIR}/rn50 ${MKREGTABLE} + ./${MKREGTABLE} ${REG_SRCS_DIR}/rn50 > $@ + +${REG_DEST_DIR}/r100_reg_safe.h: ${REG_SRCS_DIR}/r100 ${MKREGTABLE} + ./${MKREGTABLE} ${REG_SRCS_DIR}/r100 > $@ + +${REG_DEST_DIR}/r200_reg_safe.h: ${REG_SRCS_DIR}/r200 ${MKREGTABLE} + ./${MKREGTABLE} ${REG_SRCS_DIR}/r200 > $@ + +${REG_DEST_DIR}/rv515_reg_safe.h: ${REG_SRCS_DIR}/rv515 ${MKREGTABLE} + ./${MKREGTABLE} ${REG_SRCS_DIR}/rv515 > $@ + +${REG_DEST_DIR}/r300_reg_safe.h: ${REG_SRCS_DIR}/r300 ${MKREGTABLE} + ./${MKREGTABLE} ${REG_SRCS_DIR}/r300 > $@ + +${REG_DEST_DIR}/r420_reg_safe.h: ${REG_SRCS_DIR}/r420 ${MKREGTABLE} + ./${MKREGTABLE} ${REG_SRCS_DIR}/r420 > $@ + +${REG_DEST_DIR}/rs600_reg_safe.h: ${REG_SRCS_DIR}/rs600 ${MKREGTABLE} + ./${MKREGTABLE} ${REG_SRCS_DIR}/rs600 > $@ + +${REG_DEST_DIR}/r600_reg_safe.h: ${REG_SRCS_DIR}/r600 ${MKREGTABLE} + ./${MKREGTABLE} ${REG_SRCS_DIR}/r600 > $@ + +${REG_DEST_DIR}/evergreen_reg_safe.h: ${REG_SRCS_DIR}/evergreen ${MKREGTABLE} + ./${MKREGTABLE} ${REG_SRCS_DIR}/evergreen > $@ + +${REG_DEST_DIR}/cayman_reg_safe.h: ${REG_SRCS_DIR}/cayman ${MKREGTABLE} + ./${MKREGTABLE} ${REG_SRCS_DIR}/cayman > $@ + +.include diff --git a/tools/tools/drm/radeon/mkregtable/README b/tools/tools/drm/radeon/mkregtable/README new file mode 100644 index 00000000000..12b86994b1b --- /dev/null +++ b/tools/tools/drm/radeon/mkregtable/README @@ -0,0 +1,7 @@ +# $FreeBSD$ + +"mkregtable" is a tool used to generate headers for the radeonkms +driver. Headers are regenerated by running "make" in this directory. + +Header source files are in sys/dev/drm2/radeon/reg_srcs. +Generated headers are placed in sys/dev/drm2/radeon. diff --git a/tools/tools/drm/radeon/mkregtable/mkregtable.c b/tools/tools/drm/radeon/mkregtable/mkregtable.c new file mode 100644 index 00000000000..51b8f909cd2 --- /dev/null +++ b/tools/tools/drm/radeon/mkregtable/mkregtable.c @@ -0,0 +1,733 @@ +/* utility to create the register check tables + * this includes inlined list.h safe for userspace. + * + * Copyright 2009 Jerome Glisse + * Copyright 2009 Red Hat Inc. + * + * Authors: + * Jerome Glisse + * Dave Airlie + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include + +#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) +/** + * container_of - cast a member of a structure out to the containing structure + * @ptr: the pointer to the member. + * @type: the type of the container struct this is embedded in. + * @member: the name of the member within the struct. + * + */ +#define container_of(ptr, type, member) ({ \ + const typeof(((type *)0)->member)*__mptr = (ptr); \ + (type *)((char *)__mptr - offsetof(type, member)); }) + +/* + * Simple doubly linked list implementation. + * + * Some of the internal functions ("__xxx") are useful when + * manipulating whole lists rather than single entries, as + * sometimes we already know the next/prev entries and we can + * generate better code by using them directly rather than + * using the generic single-entry routines. + */ + +struct list_head { + struct list_head *next, *prev; +}; + +#define LIST_HEAD_INIT(name) { &(name), &(name) } + +#define LIST_HEAD(name) \ + struct list_head name = LIST_HEAD_INIT(name) + +static inline void INIT_LIST_HEAD(struct list_head *list) +{ + list->next = list; + list->prev = list; +} + +/* + * Insert a new entry between two known consecutive entries. + * + * This is only for internal list manipulation where we know + * the prev/next entries already! + */ +#ifndef CONFIG_DEBUG_LIST +static inline void __list_add(struct list_head *new, + struct list_head *prev, struct list_head *next) +{ + next->prev = new; + new->next = next; + new->prev = prev; + prev->next = new; +} +#else +extern void __list_add(struct list_head *new, + struct list_head *prev, struct list_head *next); +#endif + +/** + * list_add - add a new entry + * @new: new entry to be added + * @head: list head to add it after + * + * Insert a new entry after the specified head. + * This is good for implementing stacks. + */ +static inline void list_add(struct list_head *new, struct list_head *head) +{ + __list_add(new, head, head->next); +} + +/** + * list_add_tail - add a new entry + * @new: new entry to be added + * @head: list head to add it before + * + * Insert a new entry before the specified head. + * This is useful for implementing queues. + */ +static inline void list_add_tail(struct list_head *new, struct list_head *head) +{ + __list_add(new, head->prev, head); +} + +/* + * Delete a list entry by making the prev/next entries + * point to each other. + * + * This is only for internal list manipulation where we know + * the prev/next entries already! + */ +static inline void __list_del(struct list_head *prev, struct list_head *next) +{ + next->prev = prev; + prev->next = next; +} + +/** + * list_del - deletes entry from list. + * @entry: the element to delete from the list. + * Note: list_empty() on entry does not return true after this, the entry is + * in an undefined state. + */ +#ifndef CONFIG_DEBUG_LIST +static inline void list_del(struct list_head *entry) +{ + __list_del(entry->prev, entry->next); + entry->next = (void *)0xDEADBEEF; + entry->prev = (void *)0xBEEFDEAD; +} +#else +extern void list_del(struct list_head *entry); +#endif + +/** + * list_replace - replace old entry by new one + * @old : the element to be replaced + * @new : the new element to insert + * + * If @old was empty, it will be overwritten. + */ +static inline void list_replace(struct list_head *old, struct list_head *new) +{ + new->next = old->next; + new->next->prev = new; + new->prev = old->prev; + new->prev->next = new; +} + +static inline void list_replace_init(struct list_head *old, + struct list_head *new) +{ + list_replace(old, new); + INIT_LIST_HEAD(old); +} + +/** + * list_del_init - deletes entry from list and reinitialize it. + * @entry: the element to delete from the list. + */ +static inline void list_del_init(struct list_head *entry) +{ + __list_del(entry->prev, entry->next); + INIT_LIST_HEAD(entry); +} + +/** + * list_move - delete from one list and add as another's head + * @list: the entry to move + * @head: the head that will precede our entry + */ +static inline void list_move(struct list_head *list, struct list_head *head) +{ + __list_del(list->prev, list->next); + list_add(list, head); +} + +/** + * list_move_tail - delete from one list and add as another's tail + * @list: the entry to move + * @head: the head that will follow our entry + */ +static inline void list_move_tail(struct list_head *list, + struct list_head *head) +{ + __list_del(list->prev, list->next); + list_add_tail(list, head); +} + +/** + * list_is_last - tests whether @list is the last entry in list @head + * @list: the entry to test + * @head: the head of the list + */ +static inline int list_is_last(const struct list_head *list, + const struct list_head *head) +{ + return list->next == head; +} + +/** + * list_empty - tests whether a list is empty + * @head: the list to test. + */ +static inline int list_empty(const struct list_head *head) +{ + return head->next == head; +} + +/** + * list_empty_careful - tests whether a list is empty and not being modified + * @head: the list to test + * + * Description: + * tests whether a list is empty _and_ checks that no other CPU might be + * in the process of modifying either member (next or prev) + * + * NOTE: using list_empty_careful() without synchronization + * can only be safe if the only activity that can happen + * to the list entry is list_del_init(). Eg. it cannot be used + * if another CPU could re-list_add() it. + */ +static inline int list_empty_careful(const struct list_head *head) +{ + struct list_head *next = head->next; + return (next == head) && (next == head->prev); +} + +/** + * list_is_singular - tests whether a list has just one entry. + * @head: the list to test. + */ +static inline int list_is_singular(const struct list_head *head) +{ + return !list_empty(head) && (head->next == head->prev); +} + +static inline void __list_cut_position(struct list_head *list, + struct list_head *head, + struct list_head *entry) +{ + struct list_head *new_first = entry->next; + list->next = head->next; + list->next->prev = list; + list->prev = entry; + entry->next = list; + head->next = new_first; + new_first->prev = head; +} + +/** + * list_cut_position - cut a list into two + * @list: a new list to add all removed entries + * @head: a list with entries + * @entry: an entry within head, could be the head itself + * and if so we won't cut the list + * + * This helper moves the initial part of @head, up to and + * including @entry, from @head to @list. You should + * pass on @entry an element you know is on @head. @list + * should be an empty list or a list you do not care about + * losing its data. + * + */ +static inline void list_cut_position(struct list_head *list, + struct list_head *head, + struct list_head *entry) +{ + if (list_empty(head)) + return; + if (list_is_singular(head) && (head->next != entry && head != entry)) + return; + if (entry == head) + INIT_LIST_HEAD(list); + else + __list_cut_position(list, head, entry); +} + +static inline void __list_splice(const struct list_head *list, + struct list_head *prev, struct list_head *next) +{ + struct list_head *first = list->next; + struct list_head *last = list->prev; + + first->prev = prev; + prev->next = first; + + last->next = next; + next->prev = last; +} + +/** + * list_splice - join two lists, this is designed for stacks + * @list: the new list to add. + * @head: the place to add it in the first list. + */ +static inline void list_splice(const struct list_head *list, + struct list_head *head) +{ + if (!list_empty(list)) + __list_splice(list, head, head->next); +} + +/** + * list_splice_tail - join two lists, each list being a queue + * @list: the new list to add. + * @head: the place to add it in the first list. + */ +static inline void list_splice_tail(struct list_head *list, + struct list_head *head) +{ + if (!list_empty(list)) + __list_splice(list, head->prev, head); +} + +/** + * list_splice_init - join two lists and reinitialise the emptied list. + * @list: the new list to add. + * @head: the place to add it in the first list. + * + * The list at @list is reinitialised + */ +static inline void list_splice_init(struct list_head *list, + struct list_head *head) +{ + if (!list_empty(list)) { + __list_splice(list, head, head->next); + INIT_LIST_HEAD(list); + } +} + +/** + * list_splice_tail_init - join two lists and reinitialise the emptied list + * @list: the new list to add. + * @head: the place to add it in the first list. + * + * Each of the lists is a queue. + * The list at @list is reinitialised + */ +static inline void list_splice_tail_init(struct list_head *list, + struct list_head *head) +{ + if (!list_empty(list)) { + __list_splice(list, head->prev, head); + INIT_LIST_HEAD(list); + } +} + +/** + * list_entry - get the struct for this entry + * @ptr: the &struct list_head pointer. + * @type: the type of the struct this is embedded in. + * @member: the name of the list_struct within the struct. + */ +#define list_entry(ptr, type, member) \ + container_of(ptr, type, member) + +/** + * list_first_entry - get the first element from a list + * @ptr: the list head to take the element from. + * @type: the type of the struct this is embedded in. + * @member: the name of the list_struct within the struct. + * + * Note, that list is expected to be not empty. + */ +#define list_first_entry(ptr, type, member) \ + list_entry((ptr)->next, type, member) + +/** + * list_for_each - iterate over a list + * @pos: the &struct list_head to use as a loop cursor. + * @head: the head for your list. + */ +#define list_for_each(pos, head) \ + for (pos = (head)->next; prefetch(pos->next), pos != (head); \ + pos = pos->next) + +/** + * __list_for_each - iterate over a list + * @pos: the &struct list_head to use as a loop cursor. + * @head: the head for your list. + * + * This variant differs from list_for_each() in that it's the + * simplest possible list iteration code, no prefetching is done. + * Use this for code that knows the list to be very short (empty + * or 1 entry) most of the time. + */ +#define __list_for_each(pos, head) \ + for (pos = (head)->next; pos != (head); pos = pos->next) + +/** + * list_for_each_prev - iterate over a list backwards + * @pos: the &struct list_head to use as a loop cursor. + * @head: the head for your list. + */ +#define list_for_each_prev(pos, head) \ + for (pos = (head)->prev; prefetch(pos->prev), pos != (head); \ + pos = pos->prev) + +/** + * list_for_each_safe - iterate over a list safe against removal of list entry + * @pos: the &struct list_head to use as a loop cursor. + * @n: another &struct list_head to use as temporary storage + * @head: the head for your list. + */ +#define list_for_each_safe(pos, n, head) \ + for (pos = (head)->next, n = pos->next; pos != (head); \ + pos = n, n = pos->next) + +/** + * list_for_each_prev_safe - iterate over a list backwards safe against removal of list entry + * @pos: the &struct list_head to use as a loop cursor. + * @n: another &struct list_head to use as temporary storage + * @head: the head for your list. + */ +#define list_for_each_prev_safe(pos, n, head) \ + for (pos = (head)->prev, n = pos->prev; \ + prefetch(pos->prev), pos != (head); \ + pos = n, n = pos->prev) + +/** + * list_for_each_entry - iterate over list of given type + * @pos: the type * to use as a loop cursor. + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + */ +#define list_for_each_entry(pos, head, member) \ + for (pos = list_entry((head)->next, typeof(*pos), member); \ + &pos->member != (head); \ + pos = list_entry(pos->member.next, typeof(*pos), member)) + +/** + * list_for_each_entry_reverse - iterate backwards over list of given type. + * @pos: the type * to use as a loop cursor. + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + */ +#define list_for_each_entry_reverse(pos, head, member) \ + for (pos = list_entry((head)->prev, typeof(*pos), member); \ + prefetch(pos->member.prev), &pos->member != (head); \ + pos = list_entry(pos->member.prev, typeof(*pos), member)) + +/** + * list_prepare_entry - prepare a pos entry for use in list_for_each_entry_continue() + * @pos: the type * to use as a start point + * @head: the head of the list + * @member: the name of the list_struct within the struct. + * + * Prepares a pos entry for use as a start point in list_for_each_entry_continue(). + */ +#define list_prepare_entry(pos, head, member) \ + ((pos) ? : list_entry(head, typeof(*pos), member)) + +/** + * list_for_each_entry_continue - continue iteration over list of given type + * @pos: the type * to use as a loop cursor. + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + * + * Continue to iterate over list of given type, continuing after + * the current position. + */ +#define list_for_each_entry_continue(pos, head, member) \ + for (pos = list_entry(pos->member.next, typeof(*pos), member); \ + prefetch(pos->member.next), &pos->member != (head); \ + pos = list_entry(pos->member.next, typeof(*pos), member)) + +/** + * list_for_each_entry_continue_reverse - iterate backwards from the given point + * @pos: the type * to use as a loop cursor. + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + * + * Start to iterate over list of given type backwards, continuing after + * the current position. + */ +#define list_for_each_entry_continue_reverse(pos, head, member) \ + for (pos = list_entry(pos->member.prev, typeof(*pos), member); \ + prefetch(pos->member.prev), &pos->member != (head); \ + pos = list_entry(pos->member.prev, typeof(*pos), member)) + +/** + * list_for_each_entry_from - iterate over list of given type from the current point + * @pos: the type * to use as a loop cursor. + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + * + * Iterate over list of given type, continuing from current position. + */ +#define list_for_each_entry_from(pos, head, member) \ + for (; prefetch(pos->member.next), &pos->member != (head); \ + pos = list_entry(pos->member.next, typeof(*pos), member)) + +/** + * list_for_each_entry_safe - iterate over list of given type safe against removal of list entry + * @pos: the type * to use as a loop cursor. + * @n: another type * to use as temporary storage + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + */ +#define list_for_each_entry_safe(pos, n, head, member) \ + for (pos = list_entry((head)->next, typeof(*pos), member), \ + n = list_entry(pos->member.next, typeof(*pos), member); \ + &pos->member != (head); \ + pos = n, n = list_entry(n->member.next, typeof(*n), member)) + +/** + * list_for_each_entry_safe_continue + * @pos: the type * to use as a loop cursor. + * @n: another type * to use as temporary storage + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + * + * Iterate over list of given type, continuing after current point, + * safe against removal of list entry. + */ +#define list_for_each_entry_safe_continue(pos, n, head, member) \ + for (pos = list_entry(pos->member.next, typeof(*pos), member), \ + n = list_entry(pos->member.next, typeof(*pos), member); \ + &pos->member != (head); \ + pos = n, n = list_entry(n->member.next, typeof(*n), member)) + +/** + * list_for_each_entry_safe_from + * @pos: the type * to use as a loop cursor. + * @n: another type * to use as temporary storage + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + * + * Iterate over list of given type from current point, safe against + * removal of list entry. + */ +#define list_for_each_entry_safe_from(pos, n, head, member) \ + for (n = list_entry(pos->member.next, typeof(*pos), member); \ + &pos->member != (head); \ + pos = n, n = list_entry(n->member.next, typeof(*n), member)) + +/** + * list_for_each_entry_safe_reverse + * @pos: the type * to use as a loop cursor. + * @n: another type * to use as temporary storage + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + * + * Iterate backwards over list of given type, safe against removal + * of list entry. + */ +#define list_for_each_entry_safe_reverse(pos, n, head, member) \ + for (pos = list_entry((head)->prev, typeof(*pos), member), \ + n = list_entry(pos->member.prev, typeof(*pos), member); \ + &pos->member != (head); \ + pos = n, n = list_entry(n->member.prev, typeof(*n), member)) + +struct offset { + struct list_head list; + unsigned offset; +}; + +struct table { + struct list_head offsets; + unsigned offset_max; + unsigned nentry; + unsigned *table; + char *gpu_prefix; +}; + +static struct offset *offset_new(unsigned o) +{ + struct offset *offset; + + offset = (struct offset *)malloc(sizeof(struct offset)); + if (offset) { + INIT_LIST_HEAD(&offset->list); + offset->offset = o; + } + return offset; +} + +static void table_offset_add(struct table *t, struct offset *offset) +{ + list_add_tail(&offset->list, &t->offsets); +} + +static void table_init(struct table *t) +{ + INIT_LIST_HEAD(&t->offsets); + t->offset_max = 0; + t->nentry = 0; + t->table = NULL; +} + +static void table_print(struct table *t) +{ + unsigned nlloop, i, j, n, c, id; + + nlloop = (t->nentry + 3) / 4; + c = t->nentry; + printf( + "#include \n" + "__FBSDID(\"$" "FreeBSD" "$\");\n" + "\n" + ); + printf("static const unsigned %s_reg_safe_bm[%d] = {\n", t->gpu_prefix, + t->nentry); + for (i = 0, id = 0; i < nlloop; i++) { + n = 4; + if (n > c) + n = c; + c -= n; + for (j = 0; j < n; j++) { + if (j == 0) + printf("\t"); + else + printf(" "); + printf("0x%08X,", t->table[id++]); + } + printf("\n"); + } + printf("};\n"); +} + +static int table_build(struct table *t) +{ + struct offset *offset; + unsigned i, m; + + t->nentry = ((t->offset_max >> 2) + 31) / 32; + t->table = (unsigned *)malloc(sizeof(unsigned) * t->nentry); + if (t->table == NULL) + return -1; + memset(t->table, 0xff, sizeof(unsigned) * t->nentry); + list_for_each_entry(offset, &t->offsets, list) { + i = (offset->offset >> 2) / 32; + m = (offset->offset >> 2) & 31; + m = 1 << m; + t->table[i] ^= m; + } + return 0; +} + +static char gpu_name[10]; +static int parser_auth(struct table *t, const char *filename) +{ + FILE *file; + regex_t mask_rex; + regmatch_t match[4]; + char buf[1024]; + size_t end; + int len; + int done = 0; + int r; + unsigned o; + struct offset *offset; + char last_reg_s[10]; + int last_reg; + + if (regcomp + (&mask_rex, "(0x[0-9a-fA-F]*) *([_a-zA-Z0-9]*)", REG_EXTENDED)) { + fprintf(stderr, "Failed to compile regular expression\n"); + return -1; + } + file = fopen(filename, "r"); + if (file == NULL) { + fprintf(stderr, "Failed to open: %s\n", filename); + return -1; + } + fseek(file, 0, SEEK_END); + end = ftell(file); + fseek(file, 0, SEEK_SET); + + /* get header */ + if (fgets(buf, 1024, file) == NULL) { + fclose(file); + return -1; + } + + /* first line will contain the last register + * and gpu name */ + sscanf(buf, "%s %s", gpu_name, last_reg_s); + t->gpu_prefix = gpu_name; + last_reg = strtol(last_reg_s, NULL, 16); + + do { + if (fgets(buf, 1024, file) == NULL) { + fclose(file); + return -1; + } + len = strlen(buf); + if (ftell(file) == end) + done = 1; + if (len) { + r = regexec(&mask_rex, buf, 4, match, 0); + if (r == REG_NOMATCH) { + } else if (r) { + fprintf(stderr, + "Error matching regular expression %d in %s\n", + r, filename); + fclose(file); + return -1; + } else { + buf[match[0].rm_eo] = 0; + buf[match[1].rm_eo] = 0; + buf[match[2].rm_eo] = 0; + o = strtol(&buf[match[1].rm_so], NULL, 16); + offset = offset_new(o); + table_offset_add(t, offset); + if (o > t->offset_max) + t->offset_max = o; + } + } + } while (!done); + fclose(file); + if (t->offset_max < last_reg) + t->offset_max = last_reg; + return table_build(t); +} + +int main(int argc, char *argv[]) +{ + struct table t; + + if (argc != 2) { + fprintf(stderr, "Usage: %s \n", argv[0]); + exit(1); + } + table_init(&t); + if (parser_auth(&t, argv[1])) { + fprintf(stderr, "Failed to parse file %s\n", argv[1]); + return -1; + } + table_print(&t); + return 0; +} diff --git a/tools/tools/ifinfo/ifinfo.c b/tools/tools/ifinfo/ifinfo.c index f5987f79d6a..bfc3863c496 100644 --- a/tools/tools/ifinfo/ifinfo.c +++ b/tools/tools/ifinfo/ifinfo.c @@ -169,8 +169,11 @@ printit(const struct ifmibdata *ifmd, const char *dname) ifmd->ifmd_data.ifi_physical)); printf("\taddress length: %d\n", ifmd->ifmd_data.ifi_addrlen); printf("\theader length: %d\n", ifmd->ifmd_data.ifi_hdrlen); - printf("\treceive spare char1: %u\n", ifmd->ifmd_data.ifi_spare_char1); - printf("\ttransmit spare char2: %u\n", ifmd->ifmd_data.ifi_spare_char2); + printf("\tlink state: %u\n", ifmd->ifmd_data.ifi_link_state); + printf("\tvhid: %u\n", ifmd->ifmd_data.ifi_vhid); + printf("\tbaudrate power factor: %u\n", + ifmd->ifmd_data.ifi_baudrate_pf); + printf("\tdatalen: %u\n", ifmd->ifmd_data.ifi_datalen); printf("\tmtu: %lu\n", ifmd->ifmd_data.ifi_mtu); printf("\tmetric: %lu\n", ifmd->ifmd_data.ifi_metric); printf("\tline rate: %lu bit/s\n", ifmd->ifmd_data.ifi_baudrate); @@ -186,6 +189,10 @@ printit(const struct ifmibdata *ifmd, const char *dname) printf("\tinput queue drops: %lu\n", ifmd->ifmd_data.ifi_iqdrops); printf("\tpackets for unknown protocol: %lu\n", ifmd->ifmd_data.ifi_noproto); + printf("\tHW offload capabilities: 0x%lx\n", + ifmd->ifmd_data.ifi_hwassist); + printf("\tuptime at attach or stat reset: %lu\n", + ifmd->ifmd_data.ifi_epoch); #ifdef notdef printf("\treceive timing: %lu usec\n", ifmd->ifmd_data.ifi_recvtiming); printf("\ttransmit timing: %lu usec\n", diff --git a/tools/tools/net80211/wlanstats/Makefile b/tools/tools/net80211/wlanstats/Makefile index da364952b13..48df17fc191 100644 --- a/tools/tools/net80211/wlanstats/Makefile +++ b/tools/tools/net80211/wlanstats/Makefile @@ -1,9 +1,14 @@ # $FreeBSD$ +.include + PROG= wlanstats BINDIR= /usr/local/bin NO_MAN= SRCS= statfoo.c wlanstats.c main.c +.if ${COMPILER_TYPE} == "clang" +CFLAGS+= -fbracket-depth=512 +.endif .include diff --git a/tools/tools/sysbuild/sysbuild.sh b/tools/tools/sysbuild/sysbuild.sh index 0b39fe2df47..ebe2c67552c 100644 --- a/tools/tools/sysbuild/sysbuild.sh +++ b/tools/tools/sysbuild/sysbuild.sh @@ -254,6 +254,13 @@ ports_build() ( fi fi + miss=`(cd $p ; make missing ${PORTS_OPTS}) || true` + + if [ "x${miss}" != "x" ] ; then + log_it "MISSING for $p:" $miss + continue + fi + log_it "build $pn ($p)" ( set +e diff --git a/tools/tools/sysdoc/sysdoc.sh b/tools/tools/sysdoc/sysdoc.sh index c428174c8c3..b07c53dd69b 100644 --- a/tools/tools/sysdoc/sysdoc.sh +++ b/tools/tools/sysdoc/sysdoc.sh @@ -88,7 +88,7 @@ EOF # tunables in our tunables.mdoc file and generate # the final 'inner circle' of our manual page. markup_create() { - sort < _names | \ + sort -u < _names | \ xargs -n 1 /bin/sh ./sysctl.sh \ > markup.file \ 2> tunables.TODO @@ -238,9 +238,13 @@ if [ -z "$LOCATION" ] ; && for x in `find $LOCATION -name '*.kld'` \ $LOCATION/kernel; \ do nm $x | \ - grep ' sysctl___' | uniq | \ - sed 's/sysctl___//g' | sed 's/_/./g' | \ - awk {'print $3'} > _names; + sed -n '/sysctl___/ { + 's/[\.a-z_]*sysctl___//g' + 's/_/./g' + p + }' | \ + awk {'print $3'} | \ + sort -u > _names; done; markup_create page_create diff --git a/tools/tools/sysdoc/tunables.mdoc b/tools/tools/sysdoc/tunables.mdoc index 8b426e66ac9..505c4a7d0f5 100644 --- a/tools/tools/sysdoc/tunables.mdoc +++ b/tools/tools/sysdoc/tunables.mdoc @@ -1092,6 +1092,13 @@ line programs. kern.quantum +--- +kern.random.adaptors +str + +Displays registered PRNG adaptors. +This is a read-only variable. + --- kern.random.sys.burst diff --git a/tools/tools/usbtest/Makefile b/tools/tools/usbtest/Makefile new file mode 100644 index 00000000000..1cc0c833e65 --- /dev/null +++ b/tools/tools/usbtest/Makefile @@ -0,0 +1,40 @@ +# +# $FreeBSD$ +# +# Copyright (c) 2013 Hans Petter Selasky. 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. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. +# +PROG= usbtest +MAN= +SRCS+= usbtest.c +SRCS+= usb_msc_test.c +SRCS+= usb_modem_test.c +SRCS+= usb_control_ep_test.c + +LDADD+= -lusb + +WARNS= 3 + +CFLAGS+= -I ${.CURDIR}/../../../sys/dev/usb/gadget + +.include diff --git a/tools/tools/usbtest/usb_control_ep_test.c b/tools/tools/usbtest/usb_control_ep_test.c new file mode 100644 index 00000000000..87c5fc16a83 --- /dev/null +++ b/tools/tools/usbtest/usb_control_ep_test.c @@ -0,0 +1,673 @@ +/* $FreeBSD$ */ +/*- + * Copyright (c) 2007-2010 Hans Petter Selasky. 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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 +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include +#include +#include + +#include "usbtest.h" + +static void +set_ctrl_ep_fail(int bus, int dev, int ds_fail, int ss_fail) +{ + int error; + + error = sysctlbyname("hw.usb.ctrl_bus_fail", NULL, NULL, + &bus, sizeof(bus)); + if (error != 0) + goto emissing; + + error = sysctlbyname("hw.usb.ctrl_dev_fail", NULL, NULL, + &dev, sizeof(dev)); + if (error != 0) + goto emissing; + + error = sysctlbyname("hw.usb.ctrl_ds_fail", NULL, NULL, + &ds_fail, sizeof(ds_fail)); + if (error != 0) + goto emissing; + + error = sysctlbyname("hw.usb.ctrl_ss_fail", NULL, NULL, + &ss_fail, sizeof(ss_fail)); + if (error != 0) + goto emissing; + return; + +emissing: + printf("Cannot set USB sysctl, missing USB_REQ_DEBUG option?\n"); +} + +void +usb_control_ep_error_test(uint16_t vid, uint16_t pid) +{ + struct LIBUSB20_CONTROL_SETUP_DECODED req; + struct libusb20_device *pdev; + uint8_t buffer[256]; + int error; + int fail = 0; + int bus; + int dev; + int cfg; + + pdev = find_usb_device(vid, pid); + if (pdev == NULL) { + printf("USB device not found\n"); + return; + } + error = libusb20_dev_open(pdev, 0); + if (error) { + printf("Could not open USB device\n"); + libusb20_dev_free(pdev); + return; + } + + bus = libusb20_dev_get_bus_number(pdev); + dev = libusb20_dev_get_address(pdev); + + for (cfg = 0; cfg != 255; cfg++) { + + LIBUSB20_INIT(LIBUSB20_CONTROL_SETUP, &req); + req.bmRequestType = 0x80; /* read */ + req.bRequest = 0x06; /* descriptor */ + req.wValue = 0x0200 | cfg; /* config descriptor */ + req.wIndex = 0; + req.wLength = 255; + + printf("Test #%d.1/3 ...\n", cfg); + + set_ctrl_ep_fail(-1,-1,0,0); + + error = libusb20_dev_request_sync(pdev, &req, buffer, + NULL, 1000, 0); + if (error != 0) { + printf("Last configuration index is: %d\n", cfg - 1); + break; + } + + printf("Test #%d.2/3 ...\n", cfg); + + set_ctrl_ep_fail(bus,dev,1,1); + + error = libusb20_dev_request_sync(pdev, &req, buffer, + NULL, 1000, 0); + + set_ctrl_ep_fail(-1,-1,0,0); + + error = libusb20_dev_request_sync(pdev, &req, buffer, + NULL, 1000, 0); + if (error != 0) { + printf("Cannot fetch descriptor (unexpected)\n"); + fail++; + } + + printf("Test #%d.3/3 ...\n", cfg); + + set_ctrl_ep_fail(bus,dev,0,1); + + error = libusb20_dev_request_sync(pdev, &req, buffer, + NULL, 1000, 0); + + set_ctrl_ep_fail(-1,-1,0,0); + + error = libusb20_dev_request_sync(pdev, &req, buffer, + NULL, 1000, 0); + if (error != 0) { + printf("Cannot fetch descriptor (unexpected)\n"); + fail++; + } + } + + libusb20_dev_close(pdev); + libusb20_dev_free(pdev); + + printf("Test completed detecting %d failures\nDone\n\n", fail); +} + +void +usb_get_string_desc_test(uint16_t vid, uint16_t pid) +{ + struct libusb20_device *pdev; + uint32_t x; + uint32_t y; + uint32_t valid; + uint8_t *buf; + int error; + + pdev = find_usb_device(vid, pid); + if (pdev == NULL) { + printf("USB device not found\n"); + return; + } + error = libusb20_dev_open(pdev, 0); + if (error) { + printf("Could not open USB device\n"); + libusb20_dev_free(pdev); + return; + } + buf = malloc(256); + if (buf == NULL) { + printf("Cannot allocate memory\n"); + libusb20_dev_free(pdev); + return; + } + valid = 0; + + printf("Starting string descriptor test for " + "VID=0x%04x PID=0x%04x\n", vid, pid); + + for (x = 0; x != 256; x++) { + + if (libusb20_dev_check_connected(pdev) != 0) { + printf("Device disconnected\n"); + break; + } + printf("%d .. ", (int)x); + + fflush(stdout); + + error = libusb20_dev_req_string_simple_sync(pdev, x, buf, 255); + + if (error == 0) { + printf("\nINDEX=%d, STRING='%s' (Default language)\n", (int)x, buf); + fflush(stdout); + } else { + continue; + } + + valid = 0; + + for (y = 0; y != 65536; y++) { + + if (libusb20_dev_check_connected(pdev) != 0) { + printf("Device disconnected\n"); + break; + } + error = libusb20_dev_req_string_sync(pdev, x, y, buf, 256); + if (error == 0) + valid++; + } + + printf("String at INDEX=%d responds to %d " + "languages\n", (int)x, (int)valid); + } + + printf("\nDone\n"); + + free(buf); + + libusb20_dev_free(pdev); +} + +void +usb_port_reset_test(uint16_t vid, uint16_t pid, uint32_t duration) +{ + struct timeval sub_tv; + struct timeval ref_tv; + struct timeval res_tv; + + struct libusb20_device *pdev; + + int error; + int iter; + int errcnt; + + time_t last_sec; + + /* sysctl() - no set config */ + + pdev = find_usb_device(vid, pid); + if (pdev == NULL) { + printf("USB device not found\n"); + return; + } + error = libusb20_dev_open(pdev, 0); + if (error) { + libusb20_dev_free(pdev); + printf("Could not open USB device\n"); + return; + } + iter = 0; + + errcnt = 0; + + gettimeofday(&ref_tv, 0); + + last_sec = ref_tv.tv_sec; + + while (1) { + + gettimeofday(&sub_tv, 0); + + if (last_sec != sub_tv.tv_sec) { + + printf("STATUS: ID=%u, ERR=%u\n", + (int)iter, (int)errcnt); + + fflush(stdout); + + last_sec = sub_tv.tv_sec; + } + timersub(&sub_tv, &ref_tv, &res_tv); + + if ((res_tv.tv_sec < 0) || (res_tv.tv_sec >= (int)duration)) + break; + + if (libusb20_dev_reset(pdev)) { + errcnt++; + usleep(50000); + } + if (libusb20_dev_check_connected(pdev) != 0) { + printf("Device disconnected\n"); + break; + } + iter++; + } + + libusb20_dev_reset(pdev); + + libusb20_dev_free(pdev); +} + +void +usb_set_config_test(uint16_t vid, uint16_t pid, uint32_t duration) +{ + struct libusb20_device *pdev; + struct LIBUSB20_DEVICE_DESC_DECODED *ddesc; + int x; + int error; + int failed; + int exp; + + pdev = find_usb_device(vid, pid); + if (pdev == NULL) { + printf("USB device not found\n"); + return; + } + error = libusb20_dev_open(pdev, 0); + if (error) { + printf("Could not open USB device\n"); + libusb20_dev_free(pdev); + return; + } + failed = 0; + + printf("Starting set config test for " + "VID=0x%04x PID=0x%04x\n", vid, pid); + + for (x = 255; x > -1; x--) { + + error = libusb20_dev_set_config_index(pdev, x); + if (error == 0) { + if (x == 255) { + printf("Unconfiguring USB device " + "was successful\n"); + } else { + printf("Setting configuration %d " + "was successful\n", x); + } + } else { + failed++; + } + } + + ddesc = libusb20_dev_get_device_desc(pdev); + if (ddesc != NULL) + exp = ddesc->bNumConfigurations + 1; + else + exp = 1; + + printf("\n\n" + "Set configuration summary\n" + "Valid count: %d/%d %s\n" + "Failed count: %d\n", + 256 - failed, exp, + (exp == (256 - failed)) ? "(expected)" : "(unexpected)", + failed); + + libusb20_dev_free(pdev); +} + +void +usb_get_descriptor_test(uint16_t vid, uint16_t pid, uint32_t duration) +{ + struct libusb20_device *pdev; + + pdev = find_usb_device(vid, pid); + if (pdev == NULL) { + printf("USB device not found\n"); + return; + } + libusb20_dev_free(pdev); +} + +void +usb_suspend_resume_test(uint16_t vid, uint16_t pid, uint32_t duration) +{ + struct timeval sub_tv; + struct timeval ref_tv; + struct timeval res_tv; + + struct libusb20_device *pdev; + + time_t last_sec; + + int iter; + int error; + int ptimo; + int errcnt; + int power_old; + + ptimo = 1; /* second(s) */ + + error = sysctlbyname("hw.usb.power_timeout", NULL, NULL, + &ptimo, sizeof(ptimo)); + + if (error != 0) { + printf("WARNING: Could not set power " + "timeout to 1 (error=%d) \n", errno); + } + pdev = find_usb_device(vid, pid); + if (pdev == NULL) { + printf("USB device not found\n"); + return; + } + error = libusb20_dev_open(pdev, 0); + if (error) { + printf("Could not open USB device\n"); + libusb20_dev_free(pdev); + return; + } + power_old = libusb20_dev_get_power_mode(pdev); + + printf("Starting suspend and resume " + "test for VID=0x%04x PID=0x%04x\n", vid, pid); + + iter = 0; + errcnt = 0; + + gettimeofday(&ref_tv, 0); + + last_sec = ref_tv.tv_sec; + + while (1) { + + if (libusb20_dev_check_connected(pdev) != 0) { + printf("Device disconnected\n"); + break; + } + gettimeofday(&sub_tv, 0); + + if (last_sec != sub_tv.tv_sec) { + + printf("STATUS: ID=%u, ERR=%u\n", + (int)iter, (int)errcnt); + + fflush(stdout); + + last_sec = sub_tv.tv_sec; + } + timersub(&sub_tv, &ref_tv, &res_tv); + + if ((res_tv.tv_sec < 0) || (res_tv.tv_sec >= (int)duration)) + break; + + error = libusb20_dev_set_power_mode(pdev, (iter & 1) ? + LIBUSB20_POWER_ON : LIBUSB20_POWER_SAVE); + + if (error) + errcnt++; + + /* wait before switching power mode */ + usleep(4100000 + + (((uint32_t)usb_ts_rand_noise()) % 2000000U)); + + iter++; + } + + /* restore default power mode */ + libusb20_dev_set_power_mode(pdev, power_old); + + libusb20_dev_free(pdev); +} + +void +usb_set_and_clear_stall_test(uint16_t vid, uint16_t pid) +{ + struct libusb20_device *pdev; + struct libusb20_transfer *pxfer; + + int iter; + int error; + int errcnt; + int ep; + + pdev = find_usb_device(vid, pid); + if (pdev == NULL) { + printf("USB device not found\n"); + return; + } + error = libusb20_dev_open(pdev, 1); + if (error) { + printf("Could not open USB device\n"); + libusb20_dev_free(pdev); + return; + } + printf("Starting set and clear stall test " + "for VID=0x%04x PID=0x%04x\n", vid, pid); + + iter = 0; + errcnt = 0; + + for (ep = 2; ep != 32; ep++) { + + struct LIBUSB20_CONTROL_SETUP_DECODED setup_set_stall; + struct LIBUSB20_CONTROL_SETUP_DECODED setup_get_status; + + uint8_t epno = ((ep / 2) | ((ep & 1) << 7)); + uint8_t buf[1]; + + LIBUSB20_INIT(LIBUSB20_CONTROL_SETUP, &setup_set_stall); + setup_set_stall.bmRequestType = 0x02; /* write endpoint */ + setup_set_stall.bRequest = 0x03; /* set feature */ + setup_set_stall.wValue = 0x00; /* UF_ENDPOINT_HALT */ + setup_set_stall.wIndex = epno; + setup_set_stall.wLength = 0; + + LIBUSB20_INIT(LIBUSB20_CONTROL_SETUP, &setup_get_status); + setup_get_status.bmRequestType = 0x82; /* read endpoint */ + setup_get_status.bRequest = 0x00; /* get status */ + setup_get_status.wValue = 0x00; + setup_get_status.wIndex = epno; + setup_get_status.wLength = 1; + + if (libusb20_dev_check_connected(pdev) != 0) { + printf("Device disconnected\n"); + break; + } + pxfer = libusb20_tr_get_pointer(pdev, 0); + + error = libusb20_tr_open(pxfer, 1, 1, epno); + + if (error != 0) { + printf("Endpoint 0x%02x does not exist " + "in current setting. (%s, ignored)\n", + epno, libusb20_strerror(error)); + continue; + } + printf("Stalling endpoint 0x%02x\n", epno); + + /* set stall */ + error = libusb20_dev_request_sync(pdev, + &setup_set_stall, NULL, NULL, 250, 0); + + if (error != 0) { + printf("Endpoint 0x%02x does not allow " + "setting of stall. (%s)\n", + epno, libusb20_strerror(error)); + errcnt++; + } + /* get EP status */ + buf[0] = 0; + error = libusb20_dev_request_sync(pdev, + &setup_get_status, buf, NULL, 250, 0); + + if (error != 0) { + printf("Endpoint 0x%02x does not allow " + "reading status. (%s)\n", + epno, libusb20_strerror(error)); + errcnt++; + } else { + if (!(buf[0] & 1)) { + printf("Endpoint 0x%02x status is " + "not set to stalled\n", epno); + errcnt++; + } + } + + buf[0] = 0; + error = libusb20_tr_bulk_intr_sync(pxfer, buf, 1, NULL, 250); + if (error != LIBUSB20_TRANSFER_STALL) { + printf("Endpoint 0x%02x does not appear to " + "have stalled. Missing stall PID!\n", epno); + errcnt++; + } + printf("Unstalling endpoint 0x%02x\n", epno); + + libusb20_tr_clear_stall_sync(pxfer); + + /* get EP status */ + buf[0] = 0; + error = libusb20_dev_request_sync(pdev, + &setup_get_status, buf, NULL, 250, 0); + + if (error != 0) { + printf("Endpoint 0x%02x does not allow " + "reading status. (%s)\n", + epno, libusb20_strerror(error)); + errcnt++; + } else { + if (buf[0] & 1) { + printf("Endpoint 0x%02x status is " + "still stalled\n", epno); + errcnt++; + } + } + + libusb20_tr_close(pxfer); + iter++; + } + + libusb20_dev_free(pdev); + + printf("\n" + "Test summary\n" + "============\n" + "Endpoints tested: %d\n" + "Errors: %d\n", iter, errcnt); +} + +void +usb_set_alt_interface_test(uint16_t vid, uint16_t pid) +{ + struct libusb20_device *pdev; + struct libusb20_config *config; + + int iter; + int error; + int errcnt; + int n; + int m; + + pdev = find_usb_device(vid, pid); + if (pdev == NULL) { + printf("USB device not found\n"); + return; + } + printf("Starting set alternate setting test " + "for VID=0x%04x PID=0x%04x\n", vid, pid); + + config = libusb20_dev_alloc_config(pdev, + libusb20_dev_get_config_index(pdev)); + if (config == NULL) { + printf("Could not get configuration descriptor\n"); + libusb20_dev_free(pdev); + return; + } + iter = 0; + errcnt = 0; + + for (n = 0; n != config->num_interface; n++) { + /* detach kernel driver */ + libusb20_dev_detach_kernel_driver(pdev, n); + + error = libusb20_dev_open(pdev, 0); + if (error) + printf("ERROR could not open device\n"); + + /* Try the alternate settings */ + for (m = 0; m != config->interface[n].num_altsetting; m++) { + + iter++; + + if (libusb20_dev_set_alt_index(pdev, n, m + 1)) { + printf("ERROR on interface %d alt %d\n", n, m + 1); + errcnt++; + } + } + + /* Restore to default */ + + iter++; + + if (libusb20_dev_set_alt_index(pdev, n, 0)) { + printf("ERROR on interface %d alt %d\n", n, 0); + errcnt++; + } + libusb20_dev_close(pdev); + } + + libusb20_dev_free(pdev); + + printf("\n" + "Test summary\n" + "============\n" + "Interfaces tested: %d\n" + "Errors: %d\n", iter, errcnt); +} diff --git a/tools/tools/usbtest/usb_modem_test.c b/tools/tools/usbtest/usb_modem_test.c new file mode 100644 index 00000000000..74406fc6b21 --- /dev/null +++ b/tools/tools/usbtest/usb_modem_test.c @@ -0,0 +1,585 @@ +/* $FreeBSD$ */ +/*- + * Copyright (c) 2007-2010 Hans Petter Selasky. 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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 +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include +#include +#include + +#include "usbtest.h" + +static struct modem { + struct libusb20_transfer *xfer_in; + struct libusb20_transfer *xfer_out; + struct libusb20_device *usb_dev; + + struct bps rx_bytes; + struct bps tx_bytes; + uint32_t c0; + uint32_t c1; + uint32_t out_state; + uint32_t in_last; + uint32_t in_synced; + uint32_t duration; + uint32_t errors; + + uint8_t use_vendor_specific; + uint8_t loop_data; + uint8_t modem_at_mode; + uint8_t data_stress_test; + uint8_t control_ep_test; + uint8_t usb_iface; + uint8_t random_tx_length; + uint8_t random_tx_delay; + +} modem; + +static void +set_defaults(struct modem *p) +{ + memset(p, 0, sizeof(*p)); + + p->data_stress_test = 1; + p->control_ep_test = 1; + p->duration = 60; /* seconds */ +} + +void +do_bps(const char *desc, struct bps *bps, uint32_t len) +{ + bps->bytes += len; +} + +static void +modem_out_state(uint8_t *buf) +{ + if (modem.modem_at_mode) { + switch (modem.out_state & 3) { + case 0: + *buf = 'A'; + break; + case 1: + *buf = 'T'; + break; + case 2: + *buf = '\r'; + break; + default: + *buf = '\n'; + modem.c0++; + break; + } + modem.out_state++; + } else { + *buf = modem.out_state; + modem.out_state++; + modem.out_state %= 255; + } +} + +static void +modem_in_state(uint8_t buf, uint32_t counter) +{ + if ((modem.in_last == 'O') && (buf == 'K')) { + modem.c1++; + modem.in_last = buf; + } else if (buf == modem.in_last) { + modem.c1++; + modem.in_last++; + modem.in_last %= 255; + if (modem.in_synced == 0) { + if (modem.errors < 64) { + printf("Got sync\n"); + } + modem.in_synced = 1; + } + } else { + if (modem.in_synced) { + if (modem.errors < 64) { + printf("Lost sync @ %d, 0x%02x != 0x%02x\n", + counter % 512, buf, modem.in_last); + } + modem.in_synced = 0; + modem.errors++; + } + modem.in_last = buf; + modem.in_last++; + modem.in_last %= 255; + } +} + +static void +modem_write(uint8_t *buf, uint32_t len) +{ + uint32_t n; + + for (n = 0; n != len; n++) { + modem_out_state(buf + n); + } + + do_bps("transmitted", &modem.tx_bytes, len); +} + +static void +modem_read(uint8_t *buf, uint32_t len) +{ + uint32_t n; + + for (n = 0; n != len; n++) { + modem_in_state(buf[n], n); + } + + do_bps("received", &modem.rx_bytes, len); +} + +static void +usb_modem_control_ep_test(struct modem *p, uint32_t duration, uint8_t flag) +{ + struct timeval sub_tv; + struct timeval ref_tv; + struct timeval res_tv; + struct LIBUSB20_CONTROL_SETUP_DECODED setup; + struct usb_cdc_abstract_state ast; + struct usb_cdc_line_state ls; + uint16_t feature = UCDC_ABSTRACT_STATE; + uint16_t state = UCDC_DATA_MULTIPLEXED; + uint8_t iface_no; + uint8_t buf[4]; + int id = 0; + int iter = 0; + + time_t last_sec; + + iface_no = p->usb_iface - 1; + + gettimeofday(&ref_tv, 0); + + last_sec = ref_tv.tv_sec; + + printf("\nTest=%d\n", (int)flag); + + while (1) { + + gettimeofday(&sub_tv, 0); + + if (last_sec != sub_tv.tv_sec) { + + printf("STATUS: ID=%u, COUNT=%u tests/sec ERR=%u\n", + (int)id, + (int)iter, + (int)p->errors); + + fflush(stdout); + + last_sec = sub_tv.tv_sec; + + id++; + + iter = 0; + } + timersub(&sub_tv, &ref_tv, &res_tv); + + if ((res_tv.tv_sec < 0) || (res_tv.tv_sec >= (int)duration)) + break; + + LIBUSB20_INIT(LIBUSB20_CONTROL_SETUP, &setup); + + if (flag & 1) { + setup.bmRequestType = UT_READ_CLASS_INTERFACE; + setup.bRequest = 0x03; + setup.wValue = 0x0001; + setup.wIndex = iface_no; + setup.wLength = 0x0002; + + if (libusb20_dev_request_sync(p->usb_dev, &setup, buf, NULL, 250, 0)) { + p->errors++; + } + } + if (flag & 2) { + setup.bmRequestType = UT_WRITE_CLASS_INTERFACE; + setup.bRequest = UCDC_SET_COMM_FEATURE; + setup.wValue = feature; + setup.wIndex = iface_no; + setup.wLength = UCDC_ABSTRACT_STATE_LENGTH; + USETW(ast.wState, state); + + if (libusb20_dev_request_sync(p->usb_dev, &setup, &ast, NULL, 250, 0)) { + p->errors++; + } + } + if (flag & 4) { + USETDW(ls.dwDTERate, 115200); + ls.bCharFormat = UCDC_STOP_BIT_1; + ls.bParityType = UCDC_PARITY_NONE; + ls.bDataBits = 8; + + setup.bmRequestType = UT_WRITE_CLASS_INTERFACE; + setup.bRequest = UCDC_SET_LINE_CODING; + setup.wValue = 0; + setup.wIndex = iface_no; + setup.wLength = sizeof(ls); + + if (libusb20_dev_request_sync(p->usb_dev, &setup, &ls, NULL, 250, 0)) { + p->errors++; + } + } + iter++; + } + + printf("\nModem control endpoint test done!\n"); +} + +static void +usb_modem_data_stress_test(struct modem *p, uint32_t duration) +{ + struct timeval sub_tv; + struct timeval ref_tv; + struct timeval res_tv; + + time_t last_sec; + + uint8_t in_pending = 0; + uint8_t in_ready = 0; + uint8_t out_pending = 0; + + uint32_t id = 0; + + uint32_t in_max; + uint32_t out_max; + uint32_t io_max; + + uint8_t *in_buffer = 0; + uint8_t *out_buffer = 0; + + gettimeofday(&ref_tv, 0); + + last_sec = ref_tv.tv_sec; + + printf("\n"); + + in_max = libusb20_tr_get_max_total_length(p->xfer_in); + out_max = libusb20_tr_get_max_total_length(p->xfer_out); + + /* get the smallest buffer size and use that */ + io_max = (in_max < out_max) ? in_max : out_max; + + if (in_max != out_max) + printf("WARNING: Buffer sizes are un-equal: %u vs %u\n", in_max, out_max); + + in_buffer = malloc(io_max); + if (in_buffer == NULL) + goto fail; + + out_buffer = malloc(io_max); + if (out_buffer == NULL) + goto fail; + + while (1) { + + gettimeofday(&sub_tv, 0); + + if (last_sec != sub_tv.tv_sec) { + + printf("STATUS: ID=%u, RX=%u bytes/sec, TX=%u bytes/sec, ERR=%d\n", + (int)id, + (int)p->rx_bytes.bytes, + (int)p->tx_bytes.bytes, + (int)p->errors); + + p->rx_bytes.bytes = 0; + p->tx_bytes.bytes = 0; + + fflush(stdout); + + last_sec = sub_tv.tv_sec; + + id++; + } + timersub(&sub_tv, &ref_tv, &res_tv); + + if ((res_tv.tv_sec < 0) || (res_tv.tv_sec >= (int)duration)) + break; + + libusb20_dev_process(p->usb_dev); + + if (!libusb20_tr_pending(p->xfer_in)) { + if (in_pending) { + if (libusb20_tr_get_status(p->xfer_in) == 0) { + modem_read(in_buffer, libusb20_tr_get_length(p->xfer_in, 0)); + } else { + p->errors++; + usleep(10000); + } + in_pending = 0; + in_ready = 1; + } + if (p->loop_data == 0) { + libusb20_tr_setup_bulk(p->xfer_in, in_buffer, io_max, 0); + libusb20_tr_start(p->xfer_in); + in_pending = 1; + in_ready = 0; + } + } + if (!libusb20_tr_pending(p->xfer_out)) { + + uint32_t len; + uint32_t dly; + + if (out_pending) { + if (libusb20_tr_get_status(p->xfer_out) != 0) { + p->errors++; + usleep(10000); + } + } + if (p->random_tx_length) { + len = ((uint32_t)usb_ts_rand_noise()) % ((uint32_t)io_max); + } else { + len = io_max; + } + + if (p->random_tx_delay) { + dly = ((uint32_t)usb_ts_rand_noise()) % 16000U; + } else { + dly = 0; + } + + if (p->loop_data != 0) { + if (in_ready != 0) { + len = libusb20_tr_get_length(p->xfer_in, 0); + memcpy(out_buffer, in_buffer, len); + in_ready = 0; + } else { + len = io_max + 1; + } + if (!libusb20_tr_pending(p->xfer_in)) { + libusb20_tr_setup_bulk(p->xfer_in, in_buffer, io_max, 0); + libusb20_tr_start(p->xfer_in); + in_pending = 1; + } + } else { + modem_write(out_buffer, len); + } + + if (len <= io_max) { + libusb20_tr_setup_bulk(p->xfer_out, out_buffer, len, 0); + + if (dly != 0) + usleep(dly); + + libusb20_tr_start(p->xfer_out); + + out_pending = 1; + } + } + libusb20_dev_wait_process(p->usb_dev, 500); + + if (libusb20_dev_check_connected(p->usb_dev) != 0) { + printf("Device disconnected\n"); + break; + } + } + + libusb20_tr_stop(p->xfer_in); + libusb20_tr_stop(p->xfer_out); + + printf("\nData stress test done!\n"); + +fail: + if (in_buffer) + free(in_buffer); + if (out_buffer) + free(out_buffer); +} + +static void +exec_host_modem_test(struct modem *p, uint16_t vid, uint16_t pid) +{ + struct libusb20_device *pdev; + + uint8_t ntest = 0; + uint8_t x; + uint8_t in_ep; + uint8_t out_ep; + uint8_t iface; + + int error; + + pdev = find_usb_device(vid, pid); + if (pdev == NULL) { + printf("USB device not found\n"); + return; + } + + if (p->use_vendor_specific) + find_usb_endpoints(pdev, 255, 255, 255, 0, &iface, &in_ep, &out_ep, 0); + else + find_usb_endpoints(pdev, 2, 2, 1, 0, &iface, &in_ep, &out_ep, 1); + + if ((in_ep == 0) || (out_ep == 0)) { + printf("Could not find USB endpoints\n"); + libusb20_dev_free(pdev); + return; + } + printf("Attaching to: %s @ iface %d\n", + libusb20_dev_get_desc(pdev), iface); + + if (libusb20_dev_open(pdev, 2)) { + printf("Could not open USB device\n"); + libusb20_dev_free(pdev); + return; + } + if (libusb20_dev_detach_kernel_driver(pdev, iface)) { + printf("WARNING: Could not detach kernel driver\n"); + } + p->xfer_in = libusb20_tr_get_pointer(pdev, 0); + error = libusb20_tr_open(p->xfer_in, 65536 / 4, 1, in_ep); + if (error) { + printf("Could not open USB endpoint %d\n", in_ep); + libusb20_dev_free(pdev); + return; + } + p->xfer_out = libusb20_tr_get_pointer(pdev, 1); + error = libusb20_tr_open(p->xfer_out, 65536 / 4, 1, out_ep); + if (error) { + printf("Could not open USB endpoint %d\n", out_ep); + libusb20_dev_free(pdev); + return; + } + p->usb_dev = pdev; + p->usb_iface = iface; + p->errors = 0; + + if (p->control_ep_test) + ntest += 7; + + if (p->data_stress_test) + ntest += 1; + + if (ntest == 0) { + printf("No tests selected\n"); + } else { + + if (p->control_ep_test) { + for (x = 1; x != 8; x++) { + usb_modem_control_ep_test(p, + (p->duration + ntest - 1) / ntest, x); + } + } + if (p->data_stress_test) { + usb_modem_data_stress_test(p, + (p->duration + ntest - 1) / ntest); + } + } + + printf("\nDone\n"); + + libusb20_dev_free(pdev); +} + +void +show_host_modem_test(uint8_t level, uint16_t vid, uint16_t pid, uint32_t duration) +{ + uint8_t retval; + + set_defaults(&modem); + + modem.duration = duration; + + while (1) { + + retval = usb_ts_show_menu(level, "Modem Test Parameters", + " 1) Execute Data Stress Test: <%s>\n" + " 2) Execute Modem Control Endpoint Test: <%s>\n" + " 3) Use random transmit length: <%s>\n" + " 4) Use random transmit delay: <%s> ms\n" + " 5) Use vendor specific interface: <%s>\n" + "10) Loop data: <%s>\n" + "13) Set test duration: <%d> seconds\n" + "20) Reset parameters\n" + "30) Start test (VID=0x%04x, PID=0x%04x)\n" + "40) Select another device\n" + " x) Return to previous menu \n", + (modem.data_stress_test ? "YES" : "NO"), + (modem.control_ep_test ? "YES" : "NO"), + (modem.random_tx_length ? "YES" : "NO"), + (modem.random_tx_delay ? "16" : "0"), + (modem.use_vendor_specific ? "YES" : "NO"), + (modem.loop_data ? "YES" : "NO"), + (int)(modem.duration), + (int)vid, (int)pid); + + switch (retval) { + case 0: + break; + case 1: + modem.data_stress_test ^= 1; + break; + case 2: + modem.control_ep_test ^= 1; + break; + case 3: + modem.random_tx_length ^= 1; + break; + case 4: + modem.random_tx_delay ^= 1; + break; + case 5: + modem.use_vendor_specific ^= 1; + modem.control_ep_test = 0; + break; + case 10: + modem.loop_data ^= 1; + break; + case 13: + modem.duration = get_integer(); + break; + case 20: + set_defaults(&modem); + break; + case 30: + exec_host_modem_test(&modem, vid, pid); + break; + case 40: + show_host_device_selection(level + 1, &vid, &pid); + break; + default: + return; + } + } +} diff --git a/tools/tools/usbtest/usb_msc_test.c b/tools/tools/usbtest/usb_msc_test.c new file mode 100644 index 00000000000..f98b5492bec --- /dev/null +++ b/tools/tools/usbtest/usb_msc_test.c @@ -0,0 +1,1290 @@ +/* $FreeBSD$ */ +/*- + * Copyright (c) 2007-2012 Hans Petter Selasky. 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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 +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include + +#include "usbtest.h" + +#include "usb_msc_test.h" + +/* Command Block Wrapper */ +typedef struct { + uDWord dCBWSignature; +#define CBWSIGNATURE 0x43425355 + uDWord dCBWTag; + uDWord dCBWDataTransferLength; + uByte bCBWFlags; +#define CBWFLAGS_OUT 0x00 +#define CBWFLAGS_IN 0x80 + uByte bCBWLUN; + uByte bCDBLength; +#define CBWCDBLENGTH 16 + uByte CBWCDB[CBWCDBLENGTH]; +} umass_bbb_cbw_t; + +#define UMASS_BBB_CBW_SIZE 31 + +/* Command Status Wrapper */ +typedef struct { + uDWord dCSWSignature; +#define CSWSIGNATURE 0x53425355 +#define CSWSIGNATURE_IMAGINATION_DBX1 0x43425355 +#define CSWSIGNATURE_OLYMPUS_C1 0x55425355 + uDWord dCSWTag; + uDWord dCSWDataResidue; + uByte bCSWStatus; +#define CSWSTATUS_GOOD 0x0 +#define CSWSTATUS_FAILED 0x1 +#define CSWSTATUS_PHASE 0x2 +} umass_bbb_csw_t; + +#define UMASS_BBB_CSW_SIZE 13 + +#define SC_READ_6 0x08 +#define SC_READ_10 0x28 +#define SC_READ_12 0xa8 +#define SC_WRITE_6 0x0a +#define SC_WRITE_10 0x2a +#define SC_WRITE_12 0xaa + +static struct stats { + uint64_t xfer_error; + uint64_t xfer_success; + uint64_t xfer_reset; + uint64_t xfer_rx_bytes; + uint64_t xfer_tx_bytes; + uint64_t data_error; +} stats; + +static uint32_t xfer_current_id; +static uint32_t xfer_wrapper_sig; +static uint32_t block_size = 512; + +static struct libusb20_transfer *xfer_in; +static struct libusb20_transfer *xfer_out; +static struct libusb20_device *usb_pdev; +static uint8_t usb_iface; +static int sense_recurse; + +/* + * SCSI commands sniffed off the wire - LUN maybe needs to be + * adjusted! Refer to "dev/usb/storage/ustorage_fs.c" for more + * information. + */ +static uint8_t mode_sense_6[0x6] = {0x1a, 0, 0x3f, 0, 0x0c}; +static uint8_t read_capacity[0xA] = {0x25,}; +static uint8_t request_sense[0xC] = {0x03, 0, 0, 0, 0x12}; +static uint8_t test_unit_ready[0x6] = {0}; +static uint8_t mode_page_inquiry[0x6] = {0x12, 1, 0x80, 0, 0xff, 0}; +static uint8_t request_invalid[0xC] = {0xEA, 0, 0, 0, 0}; +static uint8_t prevent_removal[0x6] = {0x1E, 0, 0, 0, 1}; +static uint8_t read_toc[0xA] = {0x43, 0x02, 0, 0, 0, 0xAA, 0, 0x0C}; + +#define TIMEOUT_FILTER(x) (x) + +static void usb_request_sense(uint8_t lun); + +static void +do_msc_reset(uint8_t lun) +{ + struct LIBUSB20_CONTROL_SETUP_DECODED setup; + + LIBUSB20_INIT(LIBUSB20_CONTROL_SETUP, &setup); + + setup.bmRequestType = LIBUSB20_REQUEST_TYPE_CLASS | + LIBUSB20_RECIPIENT_INTERFACE; + setup.bRequest = 0xFF; /* BBB reset */ + setup.wValue = 0; + setup.wIndex = usb_iface; + setup.wLength = 0; + + if (libusb20_dev_request_sync(usb_pdev, &setup, NULL, NULL, 5000, 0)) { + printf("ERROR: %s\n", __FUNCTION__); + stats.xfer_error++; + } + libusb20_tr_clear_stall_sync(xfer_in); + libusb20_tr_clear_stall_sync(xfer_out); + + stats.xfer_reset++; + + usb_request_sense(lun); +} + +static uint8_t +do_msc_cmd(uint8_t *pcmd, uint8_t cmdlen, void *pdata, uint32_t datalen, + uint8_t isread, uint8_t isshort, uint8_t lun, uint8_t flags) +{ + umass_bbb_cbw_t cbw; + umass_bbb_csw_t csw; + struct libusb20_transfer *xfer_io; + uint32_t actlen; + uint32_t timeout; + int error; + + memset(&cbw, 0, sizeof(cbw)); + + USETDW(cbw.dCBWSignature, xfer_wrapper_sig); + USETDW(cbw.dCBWTag, xfer_current_id); + xfer_current_id++; + USETDW(cbw.dCBWDataTransferLength, datalen); + cbw.bCBWFlags = (isread ? CBWFLAGS_IN : CBWFLAGS_OUT); + cbw.bCBWLUN = lun; + cbw.bCDBLength = cmdlen; + bcopy(pcmd, cbw.CBWCDB, cmdlen); + + actlen = 0; + + timeout = ((datalen + 299999) / 300000) * 1000; + timeout += 5000; + + if ((error = libusb20_tr_bulk_intr_sync(xfer_out, + &cbw, sizeof(cbw), &actlen, TIMEOUT_FILTER(1000)))) { + printf("ERROR: CBW reception: %d\n", error); + do_msc_reset(lun); + return (1); + } + if (actlen != sizeof(cbw)) { + printf("ERROR: CBW length: %d != %d\n", + actlen, (int)sizeof(cbw)); + do_msc_reset(lun); + return (1); + } + if (flags & 1) + datalen /= 2; + + if (datalen != 0) { + xfer_io = isread ? xfer_in : xfer_out; + + if ((error = libusb20_tr_bulk_intr_sync(xfer_io, + pdata, datalen, &actlen, TIMEOUT_FILTER(timeout)))) { + printf("ERROR: Data transfer: %d\n", error); + do_msc_reset(lun); + return (1); + } + if ((actlen != datalen) && (!isshort)) { + printf("ERROR: Short data: %d of %d bytes\n", + actlen, datalen); + do_msc_reset(lun); + return (1); + } + } + actlen = 0; + timeout = 8; + + do { + error = libusb20_tr_bulk_intr_sync(xfer_in, &csw, + sizeof(csw), &actlen, TIMEOUT_FILTER(1000)); + if (error) { + if (error == LIBUSB20_TRANSFER_TIMED_OUT) { + printf("TIMEOUT: Trying to get CSW again. " + "%d tries left.\n", timeout); + } else { + break; + } + } else { + break; + } + } while (--timeout); + + if (error) { + libusb20_tr_clear_stall_sync(xfer_in); + error = libusb20_tr_bulk_intr_sync(xfer_in, &csw, + sizeof(csw), &actlen, TIMEOUT_FILTER(1000)); + if (error) { + libusb20_tr_clear_stall_sync(xfer_in); + printf("ERROR: Could not read CSW: Stalled or " + "timeout (%d).\n", error); + do_msc_reset(lun); + return (1); + } + } + if (UGETDW(csw.dCSWSignature) != CSWSIGNATURE) { + printf("ERROR: Wrong CSW signature\n"); + do_msc_reset(lun); + return (1); + } + if (actlen != sizeof(csw)) { + printf("ERROR: Wrong CSW length: %d != %d\n", + actlen, (int)sizeof(csw)); + do_msc_reset(lun); + return (1); + } + if (csw.bCSWStatus != 0) { + printf("ERROR: CSW status: %d\n", (int)csw.bCSWStatus); + return (1); + } else { + stats.xfer_success++; + return (0); + } +} + +static uint8_t +do_read_10(uint32_t lba, uint32_t len, void *buf, uint8_t lun) +{ + static uint8_t cmd[10]; + uint8_t retval; + + cmd[0] = SC_READ_10; + + len /= block_size; + + cmd[2] = lba >> 24; + cmd[3] = lba >> 16; + cmd[4] = lba >> 8; + cmd[5] = lba >> 0; + + cmd[7] = len >> 8; + cmd[8] = len; + + retval = do_msc_cmd(cmd, 10, buf, len * block_size, 1, 0, lun, 0); + + if (retval) { + printf("ERROR: %s\n", __FUNCTION__); + stats.xfer_error++; + } + return (retval); +} + +static uint8_t +do_write_10(uint32_t lba, uint32_t len, void *buf, uint8_t lun) +{ + static uint8_t cmd[10]; + uint8_t retval; + uint8_t abort; + + cmd[0] = SC_WRITE_10; + + abort = len & 1; + + len /= block_size; + + cmd[2] = lba >> 24; + cmd[3] = lba >> 16; + cmd[4] = lba >> 8; + cmd[5] = lba >> 0; + + cmd[7] = len >> 8; + cmd[8] = len; + + retval = do_msc_cmd(cmd, 10, buf, (len * block_size), 0, 0, lun, abort); + + if (retval) { + printf("ERROR: %s\n", __FUNCTION__); + stats.xfer_error++; + } + return (retval); +} + +static void +do_io_test(struct usb_msc_params *p, uint8_t lun, uint32_t lba_max, + uint8_t *buffer, uint8_t *reference) +{ + uint32_t io_offset; + uint32_t io_size; + uint32_t temp; + uint8_t do_read; + uint8_t retval; + + switch (p->io_mode) { + case USB_MSC_IO_MODE_WRITE_ONLY: + do_read = 0; + break; + case USB_MSC_IO_MODE_READ_WRITE: + do_read = (usb_ts_rand_noise() & 1); + break; + default: + do_read = 1; + break; + } + + switch (p->io_offset) { + case USB_MSC_IO_OFF_RANDOM: + io_offset = usb_ts_rand_noise(); + break; + default: + io_offset = 0; + break; + } + + switch (p->io_delay) { + case USB_MSC_IO_DELAY_RANDOM_10MS: + usleep(((uint32_t)usb_ts_rand_noise()) % 10000U); + break; + case USB_MSC_IO_DELAY_RANDOM_100MS: + usleep(((uint32_t)usb_ts_rand_noise()) % 100000U); + break; + case USB_MSC_IO_DELAY_FIXED_10MS: + usleep(10000); + break; + case USB_MSC_IO_DELAY_FIXED_100MS: + usleep(100000); + break; + default: + break; + } + + switch (p->io_size) { + case USB_MSC_IO_SIZE_RANDOM: + io_size = ((uint32_t)usb_ts_rand_noise()) & 65535U; + break; + case USB_MSC_IO_SIZE_INCREASING: + io_size = (xfer_current_id & 65535U); + break; + case USB_MSC_IO_SIZE_FIXED_1BLK: + io_size = 1; + break; + case USB_MSC_IO_SIZE_FIXED_2BLK: + io_size = 2; + break; + case USB_MSC_IO_SIZE_FIXED_4BLK: + io_size = 4; + break; + case USB_MSC_IO_SIZE_FIXED_8BLK: + io_size = 8; + break; + case USB_MSC_IO_SIZE_FIXED_16BLK: + io_size = 16; + break; + case USB_MSC_IO_SIZE_FIXED_32BLK: + io_size = 32; + break; + case USB_MSC_IO_SIZE_FIXED_64BLK: + io_size = 64; + break; + case USB_MSC_IO_SIZE_FIXED_128BLK: + io_size = 128; + break; + case USB_MSC_IO_SIZE_FIXED_256BLK: + io_size = 256; + break; + case USB_MSC_IO_SIZE_FIXED_512BLK: + io_size = 512; + break; + case USB_MSC_IO_SIZE_FIXED_1024BLK: + io_size = 1024; + break; + default: + io_size = 1; + break; + } + + if (io_size == 0) + io_size = 1; + + io_offset %= lba_max; + + temp = (lba_max - io_offset); + + if (io_size > temp) + io_size = temp; + + if (do_read) { + retval = do_read_10(io_offset, io_size * block_size, + buffer + (io_offset * block_size), lun); + + if (retval == 0) { + if (bcmp(buffer + (io_offset * block_size), + reference + (io_offset * block_size), + io_size * block_size)) { + printf("ERROR: Data comparison failure\n"); + stats.data_error++; + retval = 1; + } + } + stats.xfer_rx_bytes += (io_size * block_size); + + } else { + + retval = do_write_10(io_offset, io_size * block_size, + reference + (io_offset * block_size), lun); + + stats.xfer_tx_bytes += (io_size * block_size); + } + + if ((stats.xfer_error + stats.data_error + + stats.xfer_reset) >= p->max_errors) { + printf("Maximum number of errors exceeded\n"); + p->done = 1; + } +} + +static void +usb_request_sense(uint8_t lun) +{ + uint8_t dummy_buf[255]; + + if (sense_recurse) + return; + + sense_recurse++; + + do_msc_cmd(request_sense, sizeof(request_sense), + dummy_buf, 255, 1, 1, lun, 0); + + sense_recurse--; +} + +static void +usb_msc_test(struct usb_msc_params *p) +{ + struct stats last_stat; + struct timeval sub_tv; + struct timeval ref_tv; + struct timeval res_tv; + uint8_t *buffer = NULL; + uint8_t *reference = NULL; + uint32_t dummy_buf[65536 / 4]; + uint32_t lba_max; + uint32_t x; + uint32_t y; + uint32_t capacity_lba; + uint32_t capacity_bs; + time_t last_sec; + uint8_t lun; + int tries; + + memset(&last_stat, 0, sizeof(last_stat)); + + switch (p->io_lun) { + case USB_MSC_IO_LUN_0: + lun = 0; + break; + case USB_MSC_IO_LUN_1: + lun = 1; + break; + case USB_MSC_IO_LUN_2: + lun = 2; + break; + case USB_MSC_IO_LUN_3: + lun = 3; + break; + default: + lun = 0; + break; + } + + p->done = 0; + + sense_recurse = p->try_sense_on_error ? 0 : 1; + + printf("Resetting device ...\n"); + + do_msc_reset(lun); + + printf("Testing SCSI commands ...\n"); + + if (p->try_all_lun) { + printf("Requesting sense from LUN 0..255 ... "); + for (x = y = 0; x != 256; x++) { + if (do_msc_cmd(mode_sense_6, sizeof(mode_sense_6), + dummy_buf, 255, 1, 1, x, 0)) + y++; + + if (libusb20_dev_check_connected(usb_pdev) != 0) { + printf(" disconnect "); + break; + } + } + printf("Passed=%d, Failed=%d\n", 256 - y, y); + } + do_msc_cmd(mode_sense_6, sizeof(mode_sense_6), + dummy_buf, 255, 1, 1, lun, 0); + do_msc_cmd(request_sense, sizeof(request_sense), + dummy_buf, 255, 1, 1, lun, 0); + + for (tries = 0; tries != 4; tries++) { + + memset(dummy_buf, 0, sizeof(dummy_buf)); + + if (do_msc_cmd(read_capacity, sizeof(read_capacity), + dummy_buf, 255, 1, 1, lun, 0) != 0) { + printf("Cannot read disk capacity (%u / 4)\n", tries); + if (tries == 3) + return; + usleep(50000); + continue; + } else { + break; + } + } + + capacity_lba = be32toh(dummy_buf[0]); + capacity_bs = be32toh(dummy_buf[1]); + + printf("Disk reports a capacity of LBA=%u and BS=%u\n", + capacity_lba, capacity_bs); + + block_size = capacity_bs; + + if (capacity_bs > 65535) { + printf("Blocksize is too big\n"); + return; + } + if (capacity_bs < 1) { + printf("Blocksize is too small\n"); + return; + } + if (capacity_bs != 512) + printf("INFO: Blocksize is not 512 bytes\n"); + + if (p->try_invalid_scsi_command) { + int status; + + for (tries = 0; tries != 4; tries++) { + + printf("Trying invalid SCSI command: "); + + status = do_msc_cmd(request_invalid, + sizeof(request_invalid), dummy_buf, + 255, 1, 1, lun, 0); + + printf("Result%s as expected\n", status ? "" : " NOT"); + + usleep(50000); + } + } + if (p->try_invalid_wrapper_block) { + int status; + + for (tries = 0; tries != 4; tries++) { + + printf("Trying invalid USB wrapper block signature: "); + + xfer_wrapper_sig = 0x55663322; + + status = do_msc_cmd(read_capacity, + sizeof(read_capacity), dummy_buf, + 255, 1, 1, lun, 0); + + printf("Result%s as expected\n", status ? "" : " NOT"); + + xfer_wrapper_sig = CBWSIGNATURE; + + usleep(50000); + } + } + do_msc_cmd(request_sense, sizeof(request_sense), dummy_buf, 255, 1, 1, lun, 0); + do_msc_cmd(read_capacity, sizeof(read_capacity), dummy_buf, 255, 1, 1, lun, 0); + do_msc_cmd(request_sense, sizeof(request_sense), dummy_buf, 255, 1, 1, lun, 0); + do_msc_cmd(test_unit_ready, sizeof(test_unit_ready), 0, 0, 1, 1, lun, 0); + do_msc_cmd(test_unit_ready, sizeof(test_unit_ready), 0, 0, 1, 1, lun, 0); + do_msc_cmd(request_sense, sizeof(request_sense), dummy_buf, 255, 1, 1, lun, 0); + do_msc_cmd(test_unit_ready, sizeof(test_unit_ready), 0, 0, 1, 1, lun, 0); + do_msc_cmd(request_sense, sizeof(request_sense), dummy_buf, 255, 1, 1, lun, 0); + do_msc_cmd(test_unit_ready, sizeof(test_unit_ready), 0, 0, 1, 1, lun, 0); + do_msc_cmd(test_unit_ready, sizeof(test_unit_ready), 0, 0, 1, 1, lun, 0); + do_msc_cmd(test_unit_ready, sizeof(test_unit_ready), 0, 0, 1, 1, lun, 0); + do_msc_cmd(request_sense, sizeof(request_sense), dummy_buf, 255, 1, 1, lun, 0); + do_msc_cmd(test_unit_ready, sizeof(test_unit_ready), 0, 0, 1, 1, lun, 0); + do_msc_cmd(read_capacity, sizeof(read_capacity), dummy_buf, 255, 1, 1, lun, 0); + do_msc_cmd(mode_page_inquiry, sizeof(mode_page_inquiry), dummy_buf, 255, 1, 1, lun, 0); + do_msc_cmd(mode_page_inquiry, sizeof(mode_page_inquiry), dummy_buf, 255, 1, 1, lun, 0); + + if (do_msc_cmd(prevent_removal, sizeof(prevent_removal), + 0, 0, 1, 1, lun, 0)) { + printf("INFO: Prevent medium removal failed\n"); + } + if (do_msc_cmd(read_toc, sizeof(read_toc), + dummy_buf, 255, 1, 1, lun, 0)) { + printf("INFO: Read Table Of Content failed\n"); + } + if (p->try_last_lba) { + + for (y = 0, x = (1UL << 31); x; x >>= 1) { + if (do_read_10(x | y, block_size, dummy_buf, lun) == 0) + y |= x; + } + + printf("Highest readable LBA: %u (%s), " + "Capacity is %u MBytes\n", y, + (capacity_lba != y) ? "WRONG" : "OK", + (int)((((uint64_t)(y) * (uint64_t)block_size) + + (uint64_t)block_size) / 1000000ULL)); + } else { + + y = capacity_lba; + + printf("Highest readable LBA: %u (not " + "verified), Capacity is %u MBytes\n", y, + (int)((((uint64_t)(y) * (uint64_t)block_size) + + (uint64_t)block_size) / 1000000ULL)); + } + + if (y != 0xFFFFFFFFU) + y++; + + lba_max = y; + + switch (p->io_area) { + case USB_MSC_IO_AREA_1MB: + lba_max = 1024; + break; + case USB_MSC_IO_AREA_16MB: + lba_max = 1024 * 16; + break; + case USB_MSC_IO_AREA_256MB: + lba_max = 1024 * 256; + break; + case USB_MSC_IO_AREA_COMPLETE: + default: + break; + } + + if (lba_max > 65535) + lba_max = 65535; + + printf("Highest testable LBA: %u\n", (int)lba_max); + + buffer = malloc(block_size * lba_max); + if (buffer == NULL) { + printf("ERROR: Could not allocate memory\n"); + goto fail; + } + reference = malloc(block_size * lba_max); + if (reference == NULL) { + printf("ERROR: Could not allocate memory\n"); + goto fail; + } +retry_read_init: + + printf("Setting up initial data pattern, " + "LBA limit = %u ... ", lba_max); + + switch (p->io_mode) { + case USB_MSC_IO_MODE_WRITE_ONCE_READ_ONLY: + case USB_MSC_IO_MODE_WRITE_ONLY: + case USB_MSC_IO_MODE_READ_WRITE: + + switch (p->io_pattern) { + case USB_MSC_IO_PATTERN_FIXED: + for (x = 0; x != (block_size * lba_max); x += 8) { + reference[x + 0] = x >> 24; + reference[x + 1] = x >> 16; + reference[x + 2] = x >> 8; + reference[x + 3] = x >> 0; + reference[x + 4] = 0xFF; + reference[x + 5] = 0x00; + reference[x + 6] = 0xFF; + reference[x + 7] = 0x00; + } + if (do_write_10(0, lba_max * block_size, + reference, lun)) { + printf("FAILED\n"); + lba_max /= 2; + if (lba_max) + goto retry_read_init; + goto fail; + } + printf("SUCCESS\n"); + break; + case USB_MSC_IO_PATTERN_RANDOM: + for (x = 0; x != (block_size * lba_max); x++) { + reference[x] = usb_ts_rand_noise() % 255U; + } + if (do_write_10(0, lba_max * block_size, + reference, lun)) { + printf("FAILED\n"); + lba_max /= 2; + if (lba_max) + goto retry_read_init; + goto fail; + } + printf("SUCCESS\n"); + break; + default: + if (do_read_10(0, lba_max * block_size, + reference, lun)) { + printf("FAILED\n"); + lba_max /= 2; + if (lba_max) + goto retry_read_init; + goto fail; + } + printf("SUCCESS\n"); + break; + } + break; + + default: + if (do_read_10(0, lba_max * block_size, reference, lun)) { + printf("FAILED\n"); + lba_max /= 2; + if (lba_max) + goto retry_read_init; + goto fail; + } + printf("SUCCESS\n"); + break; + } + + + if (p->try_abort_data_write) { + if (do_write_10(0, (2 * block_size) | 1, reference, lun)) + printf("Aborted data write failed (OK)!\n"); + else + printf("Aborted data write did not fail (ERROR)!\n"); + + if (do_read_10(0, (2 * block_size), reference, lun)) + printf("Post-aborted data read failed (ERROR)\n"); + else + printf("Post-aborted data read success (OK)!\n"); + } + printf("Starting test ...\n"); + + gettimeofday(&ref_tv, 0); + + last_sec = ref_tv.tv_sec; + + printf("\n"); + + while (1) { + + gettimeofday(&sub_tv, 0); + + if (last_sec != sub_tv.tv_sec) { + + printf("STATUS: ID=%u, RX=%u bytes/sec, " + "TX=%u bytes/sec, ERR=%u, RST=%u, DERR=%u\n", + (int)xfer_current_id, + (int)(stats.xfer_rx_bytes - + last_stat.xfer_rx_bytes), + (int)(stats.xfer_tx_bytes - + last_stat.xfer_tx_bytes), + (int)(stats.xfer_error), + (int)(stats.xfer_reset), + (int)(stats.data_error)); + + fflush(stdout); + + last_sec = sub_tv.tv_sec; + last_stat = stats; + } + timersub(&sub_tv, &ref_tv, &res_tv); + + if ((res_tv.tv_sec < 0) || (res_tv.tv_sec >= (int)p->duration)) + break; + + do_io_test(p, lun, lba_max, buffer, reference); + + if (libusb20_dev_check_connected(usb_pdev) != 0) { + printf("Device disconnected\n"); + break; + } + if (p->done) { + printf("Maximum number of errors exceeded\n"); + break; + } + } + + printf("\nTest done!\n"); + +fail: + if (buffer) + free(buffer); + if (reference) + free(reference); +} + +void +show_host_device_selection(uint8_t level, uint16_t *pvid, uint16_t *ppid) +{ + struct libusb20_backend *pbe; + struct libusb20_device *pdev; + struct LIBUSB20_DEVICE_DESC_DECODED *ddesc; + + uint16_t vid[USB_DEVICES_MAX]; + uint16_t pid[USB_DEVICES_MAX]; + + int index; + int sel; + + const char *ptr; + +top: + pbe = libusb20_be_alloc_default(); + pdev = NULL; + index = 0; + + printf("\n[] Select USB device:\n"); + + while ((pdev = libusb20_be_device_foreach(pbe, pdev))) { + + if (libusb20_dev_get_mode(pdev) != LIBUSB20_MODE_HOST) + continue; + + if (index < USB_DEVICES_MAX) { + ddesc = libusb20_dev_get_device_desc(pdev); + ptr = libusb20_dev_get_desc(pdev); + printf("%s%d) %s\n", indent[level], index, ptr); + vid[index] = ddesc->idVendor; + pid[index] = ddesc->idProduct; + index++; + } else { + break; + } + } + + printf("%sr) Refresh device list\n", indent[level]); + printf("%sx) Return to previous menu\n", indent[level]); + + /* release data */ + libusb20_be_free(pbe); + + sel = get_integer(); + + if (sel == -2) + goto top; + + if ((sel < 0) || (sel >= index)) { + *pvid = 0; + *ppid = 0; + return; + } + *pvid = vid[sel]; + *ppid = pid[sel]; +} + +struct libusb20_device * +find_usb_device(uint16_t vid, uint16_t pid) +{ + struct libusb20_backend *pbe = libusb20_be_alloc_default(); + struct libusb20_device *pdev = NULL; + struct LIBUSB20_DEVICE_DESC_DECODED *ddesc; + + while ((pdev = libusb20_be_device_foreach(pbe, pdev))) { + + if (libusb20_dev_get_mode(pdev) != LIBUSB20_MODE_HOST) + continue; + + ddesc = libusb20_dev_get_device_desc(pdev); + + if ((vid == ddesc->idVendor) && + (pid == ddesc->idProduct)) { + libusb20_be_dequeue_device(pbe, pdev); + break; + } + } + + /* release data */ + libusb20_be_free(pbe); + + return (pdev); +} + +void +find_usb_endpoints(struct libusb20_device *pdev, uint8_t class, + uint8_t subclass, uint8_t protocol, uint8_t alt_setting, + uint8_t *pif, uint8_t *in_ep, uint8_t *out_ep, uint8_t next_if) +{ + struct libusb20_config *pcfg; + struct libusb20_interface *iface; + struct libusb20_endpoint *ep; + uint8_t x; + uint8_t y; + uint8_t z; + + *in_ep = 0; + *out_ep = 0; + *pif = 0; + + pcfg = libusb20_dev_alloc_config(pdev, + libusb20_dev_get_config_index(pdev)); + + if (pcfg == NULL) + return; + + for (x = 0; x != pcfg->num_interface; x++) { + + y = alt_setting; + + iface = (pcfg->interface + x); + + if ((iface->desc.bInterfaceClass == class) && + (iface->desc.bInterfaceSubClass == subclass || + subclass == 255) && + (iface->desc.bInterfaceProtocol == protocol || + protocol == 255)) { + + if (next_if) { + x++; + if (x == pcfg->num_interface) + break; + iface = (pcfg->interface + x); + } + *pif = x; + + for (z = 0; z != iface->num_endpoints; z++) { + ep = iface->endpoints + z; + + /* BULK only */ + if ((ep->desc.bmAttributes & 3) != 2) + continue; + + if (ep->desc.bEndpointAddress & 0x80) + *in_ep = ep->desc.bEndpointAddress; + else + *out_ep = ep->desc.bEndpointAddress; + } + break; + } + } + + free(pcfg); +} + +static void +exec_host_msc_test(struct usb_msc_params *p, uint16_t vid, uint16_t pid) +{ + struct libusb20_device *pdev; + + uint8_t in_ep; + uint8_t out_ep; + uint8_t iface; + + int error; + + memset(&stats, 0, sizeof(stats)); + + xfer_current_id = 0; + xfer_wrapper_sig = CBWSIGNATURE; + + pdev = find_usb_device(vid, pid); + if (pdev == NULL) { + printf("USB device not found\n"); + return; + } + find_usb_endpoints(pdev, 8, 6, 0x50, 0, &iface, &in_ep, &out_ep, 0); + + if ((in_ep == 0) || (out_ep == 0)) { + printf("Could not find USB endpoints\n"); + libusb20_dev_free(pdev); + return; + } + printf("Attaching to: %s @ iface %d\n", + libusb20_dev_get_desc(pdev), iface); + + if (libusb20_dev_open(pdev, 2)) { + printf("Could not open USB device\n"); + libusb20_dev_free(pdev); + return; + } + if (libusb20_dev_detach_kernel_driver(pdev, iface)) { + printf("WARNING: Could not detach kernel driver\n"); + } + xfer_in = libusb20_tr_get_pointer(pdev, 0); + error = libusb20_tr_open(xfer_in, 65536, 1, in_ep); + if (error) { + printf("Could not open USB endpoint %d\n", in_ep); + libusb20_dev_free(pdev); + return; + } + xfer_out = libusb20_tr_get_pointer(pdev, 1); + error = libusb20_tr_open(xfer_out, 65536, 1, out_ep); + if (error) { + printf("Could not open USB endpoint %d\n", out_ep); + libusb20_dev_free(pdev); + return; + } + usb_pdev = pdev; + usb_iface = iface; + + usb_msc_test(p); + + libusb20_dev_free(pdev); +} + +static void +set_defaults(struct usb_msc_params *p) +{ + memset(p, 0, sizeof(*p)); + + p->duration = 60; /* seconds */ + p->try_invalid_scsi_command = 1; + p->try_invalid_wrapper_block = 1; + p->try_last_lba = 1; + p->max_errors = -1; +} + +static const char * +get_io_mode(const struct usb_msc_params *p) +{ + ; /* indent fix */ + switch (p->io_mode) { + case USB_MSC_IO_MODE_READ_ONLY: + return ("Read Only"); + case USB_MSC_IO_MODE_WRITE_ONCE_READ_ONLY: + return ("Write Once, Read Only"); + case USB_MSC_IO_MODE_WRITE_ONLY: + return ("Write Only"); + case USB_MSC_IO_MODE_READ_WRITE: + return ("Read and Write"); + default: + return ("Unknown"); + } +} + +static const char * +get_io_pattern(const struct usb_msc_params *p) +{ + ; /* indent fix */ + switch (p->io_pattern) { + case USB_MSC_IO_PATTERN_FIXED: + return ("Fixed"); + case USB_MSC_IO_PATTERN_RANDOM: + return ("Random"); + case USB_MSC_IO_PATTERN_PRESERVE: + return ("Preserve"); + default: + return ("Unknown"); + } +} + +static const char * +get_io_size(const struct usb_msc_params *p) +{ + ; /* indent fix */ + switch (p->io_size) { + case USB_MSC_IO_SIZE_RANDOM: + return ("Random"); + case USB_MSC_IO_SIZE_INCREASING: + return ("Increasing"); + case USB_MSC_IO_SIZE_FIXED_1BLK: + return ("Single block"); + case USB_MSC_IO_SIZE_FIXED_2BLK: + return ("2 blocks"); + case USB_MSC_IO_SIZE_FIXED_4BLK: + return ("4 blocks"); + case USB_MSC_IO_SIZE_FIXED_8BLK: + return ("8 blocks"); + case USB_MSC_IO_SIZE_FIXED_16BLK: + return ("16 blocks"); + case USB_MSC_IO_SIZE_FIXED_32BLK: + return ("32 blocks"); + case USB_MSC_IO_SIZE_FIXED_64BLK: + return ("64 blocks"); + case USB_MSC_IO_SIZE_FIXED_128BLK: + return ("128 blocks"); + case USB_MSC_IO_SIZE_FIXED_256BLK: + return ("256 blocks"); + case USB_MSC_IO_SIZE_FIXED_512BLK: + return ("512 blocks"); + case USB_MSC_IO_SIZE_FIXED_1024BLK: + return ("1024 blocks"); + default: + return ("Unknown"); + } +} + +static const char * +get_io_delay(const struct usb_msc_params *p) +{ + ; /* indent fix */ + switch (p->io_delay) { + case USB_MSC_IO_DELAY_NONE: + return ("None"); + case USB_MSC_IO_DELAY_RANDOM_10MS: + return ("Random 10ms"); + case USB_MSC_IO_DELAY_RANDOM_100MS: + return ("Random 100ms"); + case USB_MSC_IO_DELAY_FIXED_10MS: + return ("Fixed 10ms"); + case USB_MSC_IO_DELAY_FIXED_100MS: + return ("Fixed 100ms"); + default: + return ("Unknown"); + } +} + +static const char * +get_io_offset(const struct usb_msc_params *p) +{ + ; /* indent fix */ + switch (p->io_offset) { + case USB_MSC_IO_OFF_START_OF_DISK: + return ("Start Of Disk"); + case USB_MSC_IO_OFF_RANDOM: + return ("Random Offset"); + default: + return ("Unknown"); + } +} + +static const char * +get_io_area(const struct usb_msc_params *p) +{ + ; /* indent fix */ + switch (p->io_area) { + case USB_MSC_IO_AREA_COMPLETE: + return ("Complete Disk"); + case USB_MSC_IO_AREA_1MB: + return ("First MegaByte"); + case USB_MSC_IO_AREA_16MB: + return ("First 16 MegaBytes"); + case USB_MSC_IO_AREA_256MB: + return ("First 256 MegaBytes"); + default: + return ("Unknown"); + } +} + +void +show_host_msc_test(uint8_t level, uint16_t vid, + uint16_t pid, uint32_t duration) +{ + struct usb_msc_params params; + uint8_t retval; + + set_defaults(¶ms); + + params.duration = duration; + + while (1) { + + retval = usb_ts_show_menu(level, + "Mass Storage Test Parameters", + " 1) Toggle I/O mode: <%s>\n" + " 2) Toggle I/O size: <%s>\n" + " 3) Toggle I/O delay: <%s>\n" + " 4) Toggle I/O offset: <%s>\n" + " 5) Toggle I/O area: <%s>\n" + " 6) Toggle I/O pattern: <%s>\n" + " 7) Toggle try invalid SCSI command: <%s>\n" + " 8) Toggle try invalid wrapper block: <%s>\n" + " 9) Toggle try invalid MaxPacketSize: <%s>\n" + "10) Toggle try last Logical Block Address: <%s>\n" + "11) Toggle I/O lun: <%d>\n" + "12) Set maximum number of errors: <%d>\n" + "13) Set test duration: <%d> seconds\n" + "14) Toggle try aborted write transfer: <%s>\n" + "15) Toggle request sense on error: <%s>\n" + "16) Toggle try all LUN: <%s>\n" + "20) Reset parameters\n" + "30) Start test (VID=0x%04x, PID=0x%04x)\n" + "40) Select another device\n" + " x) Return to previous menu \n", + get_io_mode(¶ms), + get_io_size(¶ms), + get_io_delay(¶ms), + get_io_offset(¶ms), + get_io_area(¶ms), + get_io_pattern(¶ms), + (params.try_invalid_scsi_command ? "YES" : "NO"), + (params.try_invalid_wrapper_block ? "YES" : "NO"), + (params.try_invalid_max_packet_size ? "YES" : "NO"), + (params.try_last_lba ? "YES" : "NO"), + params.io_lun, + (int)params.max_errors, + (int)params.duration, + (params.try_abort_data_write ? "YES" : "NO"), + (params.try_sense_on_error ? "YES" : "NO"), + (params.try_all_lun ? "YES" : "NO"), + vid, pid); + switch (retval) { + case 0: + break; + case 1: + params.io_mode++; + params.io_mode %= USB_MSC_IO_MODE_MAX; + break; + case 2: + params.io_size++; + params.io_size %= USB_MSC_IO_SIZE_MAX; + break; + case 3: + params.io_delay++; + params.io_delay %= USB_MSC_IO_DELAY_MAX; + break; + case 4: + params.io_offset++; + params.io_offset %= USB_MSC_IO_OFF_MAX; + break; + case 5: + params.io_area++; + params.io_area %= USB_MSC_IO_AREA_MAX; + break; + case 6: + params.io_pattern++; + params.io_pattern %= USB_MSC_IO_PATTERN_MAX; + break; + case 7: + params.try_invalid_scsi_command ^= 1; + break; + case 8: + params.try_invalid_wrapper_block ^= 1; + break; + case 9: + params.try_invalid_max_packet_size ^= 1; + break; + case 10: + params.try_last_lba ^= 1; + break; + case 11: + params.io_lun++; + params.io_lun %= USB_MSC_IO_LUN_MAX; + break; + case 12: + params.max_errors = get_integer(); + break; + case 13: + params.duration = get_integer(); + break; + case 14: + params.try_abort_data_write ^= 1; + break; + case 15: + params.try_sense_on_error ^= 1; + break; + case 16: + params.try_all_lun ^= 1; + break; + case 20: + set_defaults(¶ms); + break; + case 30: + exec_host_msc_test(¶ms, vid, pid); + break; + case 40: + show_host_device_selection(level + 1, &vid, &pid); + break; + default: + return; + } + } +} diff --git a/tools/tools/usbtest/usb_msc_test.h b/tools/tools/usbtest/usb_msc_test.h new file mode 100644 index 00000000000..3af7b0869d2 --- /dev/null +++ b/tools/tools/usbtest/usb_msc_test.h @@ -0,0 +1,120 @@ +/* $FreeBSD$ */ +/*- + * Copyright (c) 2010 Hans Petter Selasky. 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + */ + +#ifndef _USB_MSC_TEST_H_ +#define _USB_MSC_TEST_H_ + +enum { + USB_MSC_IO_MODE_READ_ONLY, + USB_MSC_IO_MODE_WRITE_ONCE_READ_ONLY, + USB_MSC_IO_MODE_WRITE_ONLY, + USB_MSC_IO_MODE_READ_WRITE, + USB_MSC_IO_MODE_MAX, +}; + +enum { + USB_MSC_IO_PATTERN_FIXED, + USB_MSC_IO_PATTERN_RANDOM, + USB_MSC_IO_PATTERN_PRESERVE, + USB_MSC_IO_PATTERN_MAX, +}; + +enum { + USB_MSC_IO_SIZE_RANDOM, + USB_MSC_IO_SIZE_INCREASING, + USB_MSC_IO_SIZE_FIXED_1BLK, + USB_MSC_IO_SIZE_FIXED_2BLK, + USB_MSC_IO_SIZE_FIXED_4BLK, + USB_MSC_IO_SIZE_FIXED_8BLK, + USB_MSC_IO_SIZE_FIXED_16BLK, + USB_MSC_IO_SIZE_FIXED_32BLK, + USB_MSC_IO_SIZE_FIXED_64BLK, + USB_MSC_IO_SIZE_FIXED_128BLK, + USB_MSC_IO_SIZE_FIXED_256BLK, + USB_MSC_IO_SIZE_FIXED_512BLK, + USB_MSC_IO_SIZE_FIXED_1024BLK, + USB_MSC_IO_SIZE_MAX, +}; + +enum { + USB_MSC_IO_DELAY_NONE, + USB_MSC_IO_DELAY_RANDOM_10MS, + USB_MSC_IO_DELAY_RANDOM_100MS, + USB_MSC_IO_DELAY_FIXED_10MS, + USB_MSC_IO_DELAY_FIXED_100MS, + USB_MSC_IO_DELAY_MAX, +}; + +enum { + USB_MSC_IO_OFF_START_OF_DISK, + USB_MSC_IO_OFF_RANDOM, + USB_MSC_IO_OFF_MAX, +}; + +enum { + USB_MSC_IO_AREA_COMPLETE, + USB_MSC_IO_AREA_1MB, + USB_MSC_IO_AREA_16MB, + USB_MSC_IO_AREA_256MB, + USB_MSC_IO_AREA_MAX, +}; + +enum { + USB_MSC_IO_LUN_0, + USB_MSC_IO_LUN_1, + USB_MSC_IO_LUN_2, + USB_MSC_IO_LUN_3, + USB_MSC_IO_LUN_MAX, +}; + +struct usb_msc_params { + + uint32_t duration; + uint32_t max_errors; + + /* See "USB_MSC_XXX" enums */ + + uint8_t io_mode; + uint8_t io_size; + uint8_t io_delay; + uint8_t io_offset; + uint8_t io_area; + uint8_t io_pattern; + uint8_t io_lun; + + /* booleans */ + uint8_t try_invalid_scsi_command; + uint8_t try_invalid_wrapper_block; + uint8_t try_invalid_max_packet_size; + uint8_t try_last_lba; + uint8_t try_abort_data_write; + uint8_t try_sense_on_error; + uint8_t try_all_lun; + + uint8_t done; +}; + +#endif /* _USB_MSC_TEST_H_ */ diff --git a/tools/tools/usbtest/usbtest.c b/tools/tools/usbtest/usbtest.c new file mode 100644 index 00000000000..725b9ead971 --- /dev/null +++ b/tools/tools/usbtest/usbtest.c @@ -0,0 +1,809 @@ +/* $FreeBSD$ */ +/*- + * Copyright (c) 2010 Hans Petter Selasky. 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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 +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include "usbtest.h" + +#include +#include +#include +#include + +static uint8_t usb_ts_select[USB_TS_MAX_LEVELS]; + +const char *indent[USB_TS_MAX_LEVELS] = { + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", +}; + +/* a perceptual white noise generator (after HPS' invention) */ + +int32_t +usb_ts_rand_noise(void) +{ + uint32_t temp; + const uint32_t prime = 0xFFFF1D; + static uint32_t noise_rem = 1; + + if (noise_rem & 1) { + noise_rem += prime; + } + noise_rem /= 2; + + temp = noise_rem; + + /* unsigned to signed conversion */ + + temp ^= 0x800000; + if (temp & 0x800000) { + temp |= (-0x800000); + } + return temp; +} + +uint8_t +usb_ts_show_menu(uint8_t level, const char *title, const char *fmt,...) +{ + va_list args; + uint8_t x; + uint8_t retval; + char *pstr; + char buf[16]; + char menu[80 * 20]; + + va_start(args, fmt); + vsnprintf(menu, sizeof(menu), fmt, args); + va_end(args); + + printf("["); + + for (x = 0; x != level; x++) { + if ((x + 1) == level) + printf("%d", usb_ts_select[x]); + else + printf("%d.", usb_ts_select[x]); + } + + printf("] - %s:\n\n", title); + + x = 1; + for (pstr = menu; *pstr; pstr++) { + if (x != 0) { + printf("%s", indent[level]); + x = 0; + } + printf("%c", *pstr); + + if (*pstr == '\n') + x = 1; + } + + printf("\n>"); + + if (fgets(buf, sizeof(buf), stdin) == NULL) + err(1, "Cannot read input"); + + if (buf[0] == 'x') + retval = 255; + else + retval = atoi(buf); + + usb_ts_select[level] = retval; + + return (retval); +} + +void +get_string(char *ptr, int size) +{ + printf("\nEnter string>"); + + if (fgets(ptr, size, stdin) == NULL) + err(1, "Cannot read input"); + + ptr[size - 1] = 0; + + size = strlen(ptr); + + /* strip trailing newline, if any */ + if (size == 0) + return; + else if (ptr[size - 1] == '\n') + ptr[size - 1] = 0; +} + +int +get_integer(void) +{ + char buf[32]; + + printf("\nEnter integer value>"); + + if (fgets(buf, sizeof(buf), stdin) == NULL) + err(1, "Cannot read input"); + + if (strcmp(buf, "x\n") == 0) + return (-1); + if (strcmp(buf, "r\n") == 0) + return (-2); + + return ((int)strtol(buf, 0, 0)); +} + +static void +set_template(int template) +{ + int error; + + error = sysctlbyname("hw.usb.template", NULL, NULL, + &template, sizeof(template)); + + if (error != 0) { + printf("WARNING: Could not set USB template " + "to %d (error=%d)\n", template, errno); + } +} + +static void +show_default_audio_select(uint8_t level) +{ + int error; + int retval; + int mode = 0; + int pattern_interval = 128; + int throughput = 0; + size_t len; + char pattern[G_AUDIO_MAX_STRLEN] = {"0123456789abcdef"}; + + set_template(USB_TEMP_AUDIO); + + while (1) { + + error = sysctlbyname("hw.usb.g_audio.mode", NULL, NULL, + &mode, sizeof(mode)); + + if (error != 0) { + printf("WARNING: Could not set audio mode " + "to %d (error=%d)\n", mode, errno); + } + error = sysctlbyname("hw.usb.g_audio.pattern_interval", NULL, NULL, + &pattern_interval, sizeof(pattern_interval)); + + if (error != 0) { + printf("WARNING: Could not set pattern interval " + "to %d (error=%d)\n", pattern_interval, errno); + } + len = sizeof(throughput); + + error = sysctlbyname("hw.usb.g_audio.throughput", + &throughput, &len, 0, 0); + + if (error != 0) { + printf("WARNING: Could not get throughput " + "(error=%d)\n", errno); + } + error = sysctlbyname("hw.usb.g_audio.pattern", NULL, NULL, + &pattern, strlen(pattern)); + + if (error != 0) { + printf("WARNING: Could not set audio pattern " + "to '%s' (error=%d)\n", pattern, errno); + } + retval = usb_ts_show_menu(level, "Default Audio Settings", + "1) Set Silent mode %s\n" + "2) Set Dump mode %s\n" + "3) Set Loop mode %s\n" + "4) Set Pattern mode %s\n" + "5) Change DTMF pattern: '%s'\n" + "6) Change pattern advance interval: %d ms\n" + "x) Return to previous menu\n" + "s: Ready for enumeration\n" + "t: Throughput: %d bytes/second\n", + (mode == G_AUDIO_MODE_SILENT) ? "(selected)" : "", + (mode == G_AUDIO_MODE_DUMP) ? "(selected)" : "", + (mode == G_AUDIO_MODE_LOOP) ? "(selected)" : "", + (mode == G_AUDIO_MODE_PATTERN) ? "(selected)" : "", + pattern, pattern_interval, throughput); + + switch (retval) { + case 0: + break; + case 1: + mode = G_AUDIO_MODE_SILENT; + break; + case 2: + mode = G_AUDIO_MODE_DUMP; + break; + case 3: + mode = G_AUDIO_MODE_LOOP; + break; + case 4: + mode = G_AUDIO_MODE_PATTERN; + break; + case 5: + get_string(pattern, sizeof(pattern)); + break; + case 6: + pattern_interval = get_integer(); + break; + default: + return; + } + } +} + +static void +show_device_audio_select(uint8_t level) +{ + uint8_t retval; + + while (1) { + + retval = usb_ts_show_menu(level, "Select Audio Device Model", + "1) Generic Audio Device\n" + "x) Return to previous menu\n"); + + switch (retval) { + case 0: + break; + case 1: + show_default_audio_select(level + 1); + break; + default: + return; + } + } +} + +static void +show_device_msc_select(uint8_t level) +{ + set_template(USB_TEMP_MSC); +} + +static void +show_device_ethernet_select(uint8_t level) +{ + set_template(USB_TEMP_CDCE); +} + +static void +show_default_keyboard_select(uint8_t level) +{ + int error; + int retval; + int mode = 0; + int interval = 1023; + char pattern[G_KEYBOARD_MAX_STRLEN] = {"abcdefpattern"}; + + set_template(USB_TEMP_KBD); + + while (1) { + + error = sysctlbyname("hw.usb.g_keyboard.mode", NULL, NULL, + &mode, sizeof(mode)); + + if (error != 0) { + printf("WARNING: Could not set keyboard mode " + " to %d (error=%d) \n", mode, errno); + } + error = sysctlbyname("hw.usb.g_keyboard.key_press_interval", NULL, NULL, + &interval, sizeof(interval)); + + if (error != 0) { + printf("WARNING: Could not set key press interval " + "to %d (error=%d)\n", interval, errno); + } + error = sysctlbyname("hw.usb.g_keyboard.key_press_pattern", NULL, NULL, + &pattern, strlen(pattern)); + + if (error != 0) { + printf("WARNING: Could not set key pattern " + "to '%s' (error=%d)\n", pattern, errno); + } + retval = usb_ts_show_menu(level, "Default Keyboard Settings", + "1) Set silent mode %s\n" + "2) Set pattern mode %s\n" + "3) Change pattern: '%s'\n" + "4) Change key press interval: %d ms\n" + "x) Return to previous menu\n" + "s: Ready for enumeration\n", + (mode == G_KEYBOARD_MODE_SILENT) ? "(selected)" : "", + (mode == G_KEYBOARD_MODE_PATTERN) ? "(selected)" : "", + pattern, interval); + + switch (retval) { + case 0: + break; + case 1: + mode = G_KEYBOARD_MODE_SILENT; + break; + case 2: + mode = G_KEYBOARD_MODE_PATTERN; + break; + case 3: + get_string(pattern, sizeof(pattern)); + break; + case 4: + interval = get_integer(); + break; + default: + return; + } + } +} + +static void +show_device_keyboard_select(uint8_t level) +{ + uint8_t retval; + + while (1) { + + retval = usb_ts_show_menu(level, "Select Keyboard Model", + "1) Generic Keyboard \n" + "x) Return to previous menu \n"); + + switch (retval) { + case 0: + break; + case 1: + show_default_keyboard_select(level + 1); + break; + default: + return; + } + } +} + +static void +show_default_mouse_select(uint8_t level) +{ + int error; + int retval; + int mode = 0; + int cursor_interval = 128; + int cursor_radius = 75; + int button_interval = 0; + + set_template(USB_TEMP_MOUSE); + + while (1) { + + error = sysctlbyname("hw.usb.g_mouse.mode", NULL, NULL, + &mode, sizeof(mode)); + + if (error != 0) { + printf("WARNING: Could not set mouse mode " + "to %d (error=%d)\n", mode, errno); + } + error = sysctlbyname("hw.usb.g_mouse.cursor_update_interval", NULL, NULL, + &cursor_interval, sizeof(cursor_interval)); + + if (error != 0) { + printf("WARNING: Could not set cursor update interval " + "to %d (error=%d)\n", cursor_interval, errno); + } + error = sysctlbyname("hw.usb.g_mouse.button_press_interval", NULL, NULL, + &button_interval, sizeof(button_interval)); + + if (error != 0) { + printf("WARNING: Could not set button press interval " + "to %d (error=%d)\n", button_interval, errno); + } + error = sysctlbyname("hw.usb.g_mouse.cursor_radius", NULL, NULL, + &cursor_radius, sizeof(cursor_radius)); + + if (error != 0) { + printf("WARNING: Could not set cursor radius " + "to %d (error=%d)\n", cursor_radius, errno); + } + retval = usb_ts_show_menu(level, "Default Mouse Settings", + "1) Set Silent mode %s\n" + "2) Set Circle mode %s\n" + "3) Set Square mode %s\n" + "4) Set Spiral mode %s\n" + "5) Change cursor radius: %d pixels\n" + "6) Change cursor update interval: %d ms\n" + "7) Change button[0] press interval: %d ms\n" + "x) Return to previous menu\n" + "s: Ready for enumeration\n", + (mode == G_MOUSE_MODE_SILENT) ? "(selected)" : "", + (mode == G_MOUSE_MODE_CIRCLE) ? "(selected)" : "", + (mode == G_MOUSE_MODE_BOX) ? "(selected)" : "", + (mode == G_MOUSE_MODE_SPIRAL) ? "(selected)" : "", + cursor_radius, cursor_interval, button_interval); + + switch (retval) { + case 0: + break; + case 1: + mode = G_MOUSE_MODE_SILENT; + break; + case 2: + mode = G_MOUSE_MODE_CIRCLE; + break; + case 3: + mode = G_MOUSE_MODE_BOX; + break; + case 4: + mode = G_MOUSE_MODE_SPIRAL; + break; + case 5: + cursor_radius = get_integer(); + break; + case 6: + cursor_interval = get_integer(); + break; + case 7: + button_interval = get_integer(); + break; + default: + return; + } + } +} + +static void +show_device_mouse_select(uint8_t level) +{ + uint8_t retval; + + while (1) { + + retval = usb_ts_show_menu(level, "Select Mouse Model", + "1) Generic Mouse\n" + "x) Return to previous menu\n"); + + switch (retval) { + case 0: + break; + case 1: + show_default_mouse_select(level + 1); + break; + default: + return; + } + } +} + +static void +show_device_mtp_select(uint8_t level) +{ + set_template(USB_TEMP_MTP); +} + +static void +show_default_modem_select(uint8_t level) +{ + int error; + int retval; + int mode = 0; + int pattern_interval = 128; + int throughput = 0; + size_t len; + char pattern[G_MODEM_MAX_STRLEN] = {"abcdefpattern"}; + + set_template(USB_TEMP_MODEM); + + while (1) { + + error = sysctlbyname("hw.usb.g_modem.mode", NULL, NULL, + &mode, sizeof(mode)); + + if (error != 0) { + printf("WARNING: Could not set modem mode " + "to %d (error=%d)\n", mode, errno); + } + error = sysctlbyname("hw.usb.g_modem.pattern_interval", NULL, NULL, + &pattern_interval, sizeof(pattern_interval)); + + if (error != 0) { + printf("WARNING: Could not set pattern interval " + "to %d (error=%d)\n", pattern_interval, errno); + } + len = sizeof(throughput); + + error = sysctlbyname("hw.usb.g_modem.throughput", + &throughput, &len, 0, 0); + + if (error != 0) { + printf("WARNING: Could not get throughput " + "(error=%d)\n", errno); + } + error = sysctlbyname("hw.usb.g_modem.pattern", NULL, NULL, + &pattern, strlen(pattern)); + + if (error != 0) { + printf("WARNING: Could not set modem pattern " + "to '%s' (error=%d)\n", pattern, errno); + } + retval = usb_ts_show_menu(level, "Default Modem Settings", + "1) Set Silent mode %s\n" + "2) Set Dump mode %s\n" + "3) Set Loop mode %s\n" + "4) Set Pattern mode %s\n" + "5) Change test pattern: '%s'\n" + "6) Change data transmit interval: %d ms\n" + "x) Return to previous menu\n" + "s: Ready for enumeration\n" + "t: Throughput: %d bytes/second\n", + (mode == G_MODEM_MODE_SILENT) ? "(selected)" : "", + (mode == G_MODEM_MODE_DUMP) ? "(selected)" : "", + (mode == G_MODEM_MODE_LOOP) ? "(selected)" : "", + (mode == G_MODEM_MODE_PATTERN) ? "(selected)" : "", + pattern, pattern_interval, throughput); + + switch (retval) { + case 0: + break; + case 1: + mode = G_MODEM_MODE_SILENT; + break; + case 2: + mode = G_MODEM_MODE_DUMP; + break; + case 3: + mode = G_MODEM_MODE_LOOP; + break; + case 4: + mode = G_MODEM_MODE_PATTERN; + break; + case 5: + get_string(pattern, sizeof(pattern)); + break; + case 6: + pattern_interval = get_integer(); + break; + default: + return; + } + } +} + +static void +show_device_modem_select(uint8_t level) +{ + uint8_t retval; + + while (1) { + + retval = usb_ts_show_menu(level, "Select Modem Model", + "1) Generic Modem\n" + "x) Return to previous menu\n"); + + switch (retval) { + case 0: + break; + case 1: + show_default_modem_select(level + 1); + break; + default: + return; + } + } +} + +static void +show_device_generic_select(uint8_t level) +{ +} + +static void +show_device_select(uint8_t level) +{ + uint8_t retval; + + while (1) { + + retval = usb_ts_show_menu(level, "Select Device Mode Test Group", + "1) Audio (UAUDIO)\n" + "2) Mass Storage (MSC)\n" + "3) Ethernet (CDCE)\n" + "4) Keyboard Input Device (UKBD)\n" + "5) Mouse Input Device (UMS)\n" + "6) Message Transfer Protocol (MTP)\n" + "7) Modem (CDC)\n" + "8) Generic Endpoint Loopback (GENERIC)\n" + "x) Return to previous menu\n"); + + switch (retval) { + case 0: + break; + case 1: + show_device_audio_select(level + 1); + break; + case 2: + show_device_msc_select(level + 1); + break; + case 3: + show_device_ethernet_select(level + 1); + break; + case 4: + show_device_keyboard_select(level + 1); + break; + case 5: + show_device_mouse_select(level + 1); + break; + case 6: + show_device_mtp_select(level + 1); + break; + case 7: + show_device_modem_select(level + 1); + break; + case 8: + show_device_generic_select(level + 1); + break; + default: + return; + } + } +} + +static void +show_host_select(uint8_t level) +{ + int force_fs = 0; + int error; + uint32_t duration = 60; + + uint16_t dev_vid = 0; + uint16_t dev_pid = 0; + uint8_t retval; + + while (1) { + + error = sysctlbyname("hw.usb.ehci.no_hs", NULL, NULL, + &force_fs, sizeof(force_fs)); + + if (error != 0) { + printf("WARNING: Could not set non-FS mode " + "to %d (error=%d)\n", force_fs, errno); + } + retval = usb_ts_show_menu(level, "Select Host Mode Test (via LibUSB)", + " 1) Select USB device (VID=0x%04x, PID=0x%04x)\n" + " 2) Manually enter USB vendor and product ID\n" + " 3) Force FULL speed operation: <%s>\n" + " 4) Mass Storage (UMASS)\n" + " 5) Modem (UMODEM)\n" + "10) Start String Descriptor Test\n" + "11) Start Port Reset Test\n" + "12) Start Set Config Test\n" + "13) Start Get Descriptor Test\n" + "14) Start Suspend and Resume Test\n" + "15) Start Set and Clear Endpoint Stall Test\n" + "16) Start Set Alternate Interface Setting Test\n" + "17) Start Invalid Control Request Test\n" + "30) Duration: <%d> seconds\n" + "x) Return to previous menu\n", + dev_vid, dev_pid, + force_fs ? "YES" : "NO", + (int)duration); + + switch (retval) { + case 0: + break; + case 1: + show_host_device_selection(level + 1, &dev_vid, &dev_pid); + break; + case 2: + dev_vid = get_integer() & 0xFFFF; + dev_pid = get_integer() & 0xFFFF; + break; + case 3: + force_fs ^= 1; + break; + case 4: + show_host_msc_test(level + 1, dev_vid, dev_pid, duration); + break; + case 5: + show_host_modem_test(level + 1, dev_vid, dev_pid, duration); + break; + case 10: + usb_get_string_desc_test(dev_vid, dev_pid); + break; + case 11: + usb_port_reset_test(dev_vid, dev_pid, duration); + break; + case 12: + usb_set_config_test(dev_vid, dev_pid, duration); + break; + case 13: + usb_get_descriptor_test(dev_vid, dev_pid, duration); + break; + case 14: + usb_suspend_resume_test(dev_vid, dev_pid, duration); + break; + case 15: + usb_set_and_clear_stall_test(dev_vid, dev_pid); + break; + case 16: + usb_set_alt_interface_test(dev_vid, dev_pid); + break; + case 17: + usb_control_ep_error_test(dev_vid, dev_pid); + break; + case 30: + duration = get_integer(); + break; + default: + return; + } + } +} + +static void +show_mode_select(uint8_t level) +{ + uint8_t retval; + + while (1) { + + retval = usb_ts_show_menu(level, "Select Computer Mode", + "1) This computer is Running the Device Side\n" + "2) This computer is Running the Host Side\n" + "x) Return to previous menu\n"); + + switch (retval) { + case 0: + break; + case 1: + show_device_select(level + 1); + break; + case 2: + show_host_select(level + 1); + break; + default: + return; + } + } +} + +int +main(int argc, char **argv) +{ + show_mode_select(1); + + return (0); +} diff --git a/tools/tools/usbtest/usbtest.h b/tools/tools/usbtest/usbtest.h new file mode 100644 index 00000000000..3d6643cd58f --- /dev/null +++ b/tools/tools/usbtest/usbtest.h @@ -0,0 +1,62 @@ +/* $FreeBSD$ */ +/*- + * Copyright (c) 2010 Hans Petter Selasky. 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + */ + +#ifndef _USBTEST_H_ +#define _USBTEST_H_ + +#define USB_DEVICES_MAX 128 +#define USB_TS_MAX_LEVELS 8 + +struct libusb20_device; + +struct bps { + uint32_t bytes; + time_t time; +}; + +extern void usb_get_string_desc_test(uint16_t, uint16_t); +extern void usb_port_reset_test(uint16_t, uint16_t, uint32_t); +extern void usb_set_config_test(uint16_t, uint16_t, uint32_t); +extern void usb_get_descriptor_test(uint16_t, uint16_t, uint32_t); +extern void usb_control_ep_error_test(uint16_t, uint16_t); +extern void usb_set_and_clear_stall_test(uint16_t, uint16_t); +extern void usb_set_alt_interface_test(uint16_t, uint16_t); + +extern void usb_suspend_resume_test(uint16_t, uint16_t, uint32_t); +extern void do_bps(const char *, struct bps *, uint32_t len); +extern const char *indent[USB_TS_MAX_LEVELS]; +extern void show_host_msc_test(uint8_t, uint16_t, uint16_t, uint32_t); +extern void show_host_modem_test(uint8_t, uint16_t, uint16_t, uint32_t); +extern void show_host_device_selection(uint8_t, uint16_t *, uint16_t *); +extern struct libusb20_device *find_usb_device(uint16_t, uint16_t); +extern void find_usb_endpoints(struct libusb20_device *, uint8_t, uint8_t, + uint8_t, uint8_t, uint8_t *, uint8_t *, uint8_t *, uint8_t); +extern void get_string(char *, int); +extern int get_integer(void); +extern uint8_t usb_ts_show_menu(uint8_t, const char *, const char *,...); +extern int32_t usb_ts_rand_noise(void); + +#endif /* _USBTEST_H_ */ diff --git a/usr.bin/Makefile b/usr.bin/Makefile index 0e7edf2c9d5..8f4d920c101 100644 --- a/usr.bin/Makefile +++ b/usr.bin/Makefile @@ -68,6 +68,7 @@ SUBDIR= alias \ id \ ipcrm \ ipcs \ + iscsictl \ join \ jot \ ${_kdump} \ @@ -262,6 +263,10 @@ SUBDIR+= truss SUBDIR+= compile_et .endif +.if ${MK_LDNS_UTILS} != "no" +SUBDIR+= host +.endif + .if ${MK_LIBTHR} != "no" SUBDIR+= csup .endif @@ -366,7 +371,9 @@ SUBDIR+= users SUBDIR+= who .endif -.if ${MACHINE_ARCH} == "amd64" || ${MACHINE_ARCH} == "ia64" || ${MACHINE_ARCH} == "sparc64" || ${MACHINE_ARCH} == "i386" || ${MACHINE_ARCH} == "armv6" +.if ${MACHINE_ARCH} == "amd64" || ${MACHINE_ARCH} == "ia64" || \ + ${MACHINE_ARCH} == "sparc64" || ${MACHINE_ARCH} == "i386" || \ + (${MACHINE_ARCH} == "armv6" && ${COMPILER_TYPE} == "clang") .if ${MK_SVN} == "yes" || ${MK_SVNLITE} == "yes" SUBDIR+= svn .endif diff --git a/usr.bin/bmake/Makefile b/usr.bin/bmake/Makefile index 86414d43e90..ed3751e7864 100644 --- a/usr.bin/bmake/Makefile +++ b/usr.bin/bmake/Makefile @@ -14,10 +14,10 @@ CFLAGS+= -I${.CURDIR} CLEANDIRS+= FreeBSD CLEANFILES+= bootstrap -# $Id: Makefile,v 1.17 2013/07/30 19:13:53 sjg Exp $ +# $Id: Makefile,v 1.20 2013/09/04 15:42:03 sjg Exp $ # Base version on src date -MAKE_VERSION= 20130730 +MAKE_VERSION= 20130904 PROG?= ${.CURDIR:T} @@ -167,6 +167,7 @@ accept test: # override some simple things BINDIR= /usr/bin MANDIR= /usr/share/man/man +DEFAULT_SYS_PATH= /usr/share/mk # make sure we get this CFLAGS+= ${COPTS.${.IMPSRC:T}} diff --git a/usr.bin/bmake/unit-tests/Makefile b/usr.bin/bmake/unit-tests/Makefile index 4cbf6a2072a..be4cd2f77fe 100644 --- a/usr.bin/bmake/unit-tests/Makefile +++ b/usr.bin/bmake/unit-tests/Makefile @@ -5,9 +5,9 @@ SRCTOP?= ${.CURDIR:H:H:H} -# $Id: Makefile.in,v 1.43 2013/07/16 21:14:30 sjg Exp $ +# $Id: Makefile.in,v 1.44 2013/08/28 22:09:29 sjg Exp $ # -# $NetBSD: Makefile,v 1.37 2013/07/16 19:59:28 sjg Exp $ +# $NetBSD: Makefile,v 1.38 2013/08/28 21:56:50 sjg Exp $ # # Unit tests for make(1) # The main targets are: @@ -52,6 +52,7 @@ SUBFILES= \ phony-end \ posix \ qequals \ + sunshcmd \ sysv \ ternary \ unexport \ diff --git a/usr.bin/brandelf/brandelf.1 b/usr.bin/brandelf/brandelf.1 index 689f0cc4650..2b3ea7ee10e 100644 --- a/usr.bin/brandelf/brandelf.1 +++ b/usr.bin/brandelf/brandelf.1 @@ -1,5 +1,4 @@ -.\" Copyright (c) 1997 -.\" John-Mark Gurney. All rights reserved. +.\" Copyright 1997 John-Mark Gurney. All rights reserved. .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions @@ -9,9 +8,6 @@ .\" 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 author nor the names of any co-contributors -.\" may be used to endorse or promote products derived from this software -.\" without specific prior written permission. .\" .\" THIS SOFTWARE IS PROVIDED BY John-Mark Gurney AND CONTRIBUTORS ``AS IS'' .\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE diff --git a/usr.bin/calendar/Makefile b/usr.bin/calendar/Makefile index 79f8590d06c..c7bd5445df0 100644 --- a/usr.bin/calendar/Makefile +++ b/usr.bin/calendar/Makefile @@ -8,7 +8,7 @@ DPADD= ${LIBM} LDADD= -lm INTER= de_AT.ISO_8859-15 de_DE.ISO8859-1 fr_FR.ISO8859-1 \ hr_HR.ISO8859-2 hu_HU.ISO8859-2 pt_BR.ISO8859-1 \ - pt_BR.UTF-8 ru_RU.KOI8-R uk_UA.KOI8-U + pt_BR.UTF-8 ru_RU.KOI8-R ru_RU.UTF-8 uk_UA.KOI8-U DE_LINKS= de_DE.ISO8859-15 FR_LINKS= fr_FR.ISO8859-15 TEXTMODE?= 444 diff --git a/usr.bin/calendar/calendars/calendar.freebsd b/usr.bin/calendar/calendars/calendar.freebsd index 333eebc68e8..6cf03461a41 100644 --- a/usr.bin/calendar/calendars/calendar.freebsd +++ b/usr.bin/calendar/calendars/calendar.freebsd @@ -152,6 +152,7 @@ 05/10 Markus Brueffer born in Gronau, Nordrhein-Westfalen, Germany, 1977 05/11 Jesus Rodriguez born in Barcelona, Spain, 1972 05/11 Roman Kurakin born in Moscow, USSR, 1979 +05/11 Ulrich Spoerlein born in Schesslitz, Bayern, Germany, 1981 05/13 Pete Fritchman born in Lansdale, Pennsylvania, United States, 1983 05/14 Tatsumi Hosokawa born in Tokyo, Japan, 1968 05/14 Shigeyuku Fukushima born in Osaka, Japan, 1974 @@ -194,7 +195,7 @@ 06/07 Jimmy Olgeni born in Milano, Italy, 1976 06/07 Benjamin Close born in Adelaide, Australia, 1978 06/14 Josh Paetzel born in Minneapolis, Minnesota, United States, 1973 -06/17 Tilman Linneweh born in Weinheim, Baden-Wuertemberg, Germany, 1978 +06/17 Tilman Linneweh born in Weinheim, Baden-Wuerttemberg, Germany, 1978 06/18 Li-Wen Hsu born in Taipei, Taiwan, Republic of China, 1984 06/18 Roman Bogorodskiy born in Saratov, Russian Federation, 1986 06/19 Charlie Root born in Portland, Oregon, United States, 1993 diff --git a/usr.bin/calendar/calendars/ru_RU.UTF-8/calendar.all b/usr.bin/calendar/calendars/ru_RU.UTF-8/calendar.all new file mode 100644 index 00000000000..038bfb0f09d --- /dev/null +++ b/usr.bin/calendar/calendars/ru_RU.UTF-8/calendar.all @@ -0,0 +1,16 @@ +/* + * РуÑÑкий календарь + * + * $FreeBSD$ + */ + +#ifndef _ru_RU_UTF_8_all_ +#define _ru_RU_UTF_8_all_ + +#include +#include +#include +#include +#include + +#endif /* !_ru_RU_UTF_8_all_ */ diff --git a/usr.bin/calendar/calendars/ru_RU.UTF-8/calendar.common b/usr.bin/calendar/calendars/ru_RU.UTF-8/calendar.common new file mode 100644 index 00000000000..8c0a8231c7c --- /dev/null +++ b/usr.bin/calendar/calendars/ru_RU.UTF-8/calendar.common @@ -0,0 +1,105 @@ +/* + * РоÑÑийÑкие праздники + * + * $FreeBSD$ + */ + +#ifndef _ru_RU_UTF_8_common_ +#define _ru_RU_UTF_8_common_ + +LANG=ru_RU.UTF-8 + +12 Ñнв День работника прокуратуры +13 Ñнв День роÑÑийÑкой печати +14 Ñнв Старый Ðовый год +21 Ñнв День инженерных войÑк +25 Ñнв ТатьÑнин день. СтуденчеÑкий праздник + 8 фев День роÑÑийÑкой науки +10 фев День дипломатичеÑкого работника + 1 мар Ð’Ñемирный день гражданÑкой обороны +03/SunSecond День работников геодезии и картографии +11 мар День работника органов Ð½Ð°Ñ€ÐºÐ¾ÐºÐ¾Ð½Ñ‚Ñ€Ð¾Ð»Ñ +18 мар День налоговой полиции +03/SunThird День работников торговли, бытового обÑÐ»ÑƒÐ¶Ð¸Ð²Ð°Ð½Ð¸Ñ Ð½Ð°ÑÐµÐ»ÐµÐ½Ð¸Ñ Ð¸ жилищно-коммунального хозÑйÑтва +27 мар Международный день театра +27 мар День внутренних войÑк + 1 апр День Ñмеха + 2 апр День ÐµÐ´Ð¸Ð½ÐµÐ½Ð¸Ñ Ð½Ð°Ñ€Ð¾Ð´Ð¾Ð² +04/SunFirst День геолога +12 апр День коÑмонавтики +04/SunSecond День войÑк противовоздушной обороны +26 апр День памÑти погибших в радиационных авариÑÑ… и катаÑтрофах +30 апр День пожарной охраны + 7 май День радио +17 май Международный день телекоммуникаций +18 май Международный день музеев +24 май День ÑлавÑнÑкой пиÑьменноÑти и культуры +26 май День роÑÑийÑкого предпринимательÑтва +27 май ОбщероÑÑийÑкий день библиотек +28 май День пограничника +30 май День пожарной охраны +31 май День РоÑÑийÑкой Ðдвокатуры +05/SunLast День химика + 1 июн День защиты детей + 5 июн День Ñколога + 6 июн ПушкинÑкий день + 8 июн День Ñоциального работника +06/SunSecond День работников легкой промышленноÑти +06/SunThird День медицинÑкого работника +22 июн День памÑти и Ñкорби (Ðачало Великой ОтечеÑтвенной Войны, 1941 год) +27 июн День молодежи +29 июн День партизан и подпольщиков +06/SatLast День Ð¸Ð·Ð¾Ð±Ñ€ÐµÑ‚Ð°Ñ‚ÐµÐ»Ñ Ð¸ рационализатора +07/SunFirst День работников морÑкого и речного флота +07/SunSecond День рыбака +07/SunSecond День роÑÑийÑкой почты +07/SunThird День металлурга +07/SunLast День Военно-МорÑкого Флота +28 июл День ÐºÑ€ÐµÑ‰ÐµÐ½Ð¸Ñ Ð ÑƒÑи + 6 авг День железнодорожных войÑк +08/SunFirst День железнодорожника +12 авг День военно-воздушных Ñил +08/SunSecond День ÑÑ‚Ñ€Ð¾Ð¸Ñ‚ÐµÐ»Ñ +08/SunThird День Воздушного Флота +22 авг День гоÑударÑтвенного флага +27 авг День кино +08/SunLast День шахтера + 1 Ñен День знаний + 2 Ñен День роÑÑийÑкой гвардии + 3 Ñен День ÑолидарноÑти в борьбе Ñ Ñ‚ÐµÑ€Ñ€Ð¾Ñ€Ð¸Ð·Ð¼Ð¾Ð¼ + 4 Ñен День ÑпециалиÑта по Ñдерному обеÑпечению +09/SunFirst День работников нефтÑной и газовой промышленноÑти +09/SunSecond День танкиÑта +09/SunThird День работников леÑа +28 Ñен День работника атомной промышленноÑти +09/SunLast День машиноÑÑ‚Ñ€Ð¾Ð¸Ñ‚ÐµÐ»Ñ + 1 окт День пожилых людей + 1 окт День Ñухопутных войÑк + 4 окт День коÑмичеÑких войÑк + 5 окт День ÑƒÑ‡Ð¸Ñ‚ÐµÐ»Ñ +14 окт Международный день Ñтандартизации +10/SunSecond День работников ÑельÑкого хозÑйÑтва и перерабатывающей промышленноÑти +10/SunThird День работников дорожного хозÑйÑтва +24 окт Международный день ООР+25 окт День таможенника +30 окт День памÑти жертв политичеÑких репреÑÑий +10/SunLast День работников автомобильного транÑпорта + 7 Ð½Ð¾Ñ Ð”ÐµÐ½ÑŒ октÑбрьÑкой революции 1917 года + 9 Ð½Ð¾Ñ Ð’Ñемирный день качеÑтва +10 Ð½Ð¾Ñ Ð”ÐµÐ½ÑŒ милиции +16 Ð½Ð¾Ñ Ð”ÐµÐ½ÑŒ морÑкой пехоты +17 Ð½Ð¾Ñ ÐœÐµÐ¶Ð´ÑƒÐ½Ð°Ñ€Ð¾Ð´Ð½Ñ‹Ð¹ день Ñтудентов +19 Ð½Ð¾Ñ Ð”ÐµÐ½ÑŒ ракетных войÑк и артиллерии +21 Ð½Ð¾Ñ Ð”ÐµÐ½ÑŒ работников налоговых органов +26 Ð½Ð¾Ñ Ð’Ñемирный день информации +11/SunLast День матери + 1 дек Ð’Ñемирный день борьбы Ñо СПИДом + 3 дек День юриÑта + 9 дек День Героев ОтечеÑтва +12 дек День КонÑтитуции +17 дек День ракетных войÑк ÑтратегичеÑкого Ð½Ð°Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ +20 дек День работника органов безопаÑноÑти +22 дек День Ñнергетика +27 дек День ÑпаÑÐ°Ñ‚ÐµÐ»Ñ + +#endif /* !_ru_RU_UTF_8_common_ */ diff --git a/usr.bin/calendar/calendars/ru_RU.UTF-8/calendar.holiday b/usr.bin/calendar/calendars/ru_RU.UTF-8/calendar.holiday new file mode 100644 index 00000000000..13a7bc0523f --- /dev/null +++ b/usr.bin/calendar/calendars/ru_RU.UTF-8/calendar.holiday @@ -0,0 +1,25 @@ +/* + * РоÑÑийÑкие праздники (нерабочие "краÑные" дни) + * + * $FreeBSD$ + */ + +#ifndef _ru_RU_UTF_8_holiday_ +#define _ru_RU_UTF_8_holiday_ + +LANG=ru_RU.UTF-8 + + 1 Ñнв Ðовый год + 2 Ñнв Ðовогодние каникулы + 3 Ñнв Ðовогодние каникулы + 4 Ñнв Ðовогодние каникулы + 5 Ñнв Ðовогодние каникулы + 7 Ñнв РождеÑтво ХриÑтово +23 фев День защитника ОтечеÑтва + 8 мар Международный женÑкий день + 1 май Праздник ВеÑны и Труда + 9 май День Победы +12 июн День РоÑÑии + 4 Ð½Ð¾Ñ Ð”ÐµÐ½ÑŒ народного единÑтва + +#endif /* !_ru_RU_UTF_8_holiday_ */ diff --git a/usr.bin/calendar/calendars/ru_RU.UTF-8/calendar.military b/usr.bin/calendar/calendars/ru_RU.UTF-8/calendar.military new file mode 100644 index 00000000000..48f257c866b --- /dev/null +++ b/usr.bin/calendar/calendars/ru_RU.UTF-8/calendar.military @@ -0,0 +1,28 @@ +/* + * Дни воинÑкой Ñлавы РоÑÑии + * + * $FreeBSD$ + */ + +#ifndef _ru_RU_UTF_8_military_ +#define _ru_RU_UTF_8_military_ + +LANG=ru_RU.UTF-8 + +27 Ñнв День ÑнÑÑ‚Ð¸Ñ Ð±Ð»Ð¾ÐºÐ°Ð´Ñ‹ города Ленинграда (1944 год) + 2 фев День разгрома ÑоветÑкими войÑками немецко-фашиÑÑ‚Ñких войÑк в СталинградÑкой битве (1943 год) +23 фев День победы КраÑной Ðрмии над кайзеровÑкими войÑками Германии (1918 год) +18 апр День победы руÑÑких воинов кнÑÐ·Ñ ÐлекÑандра ÐевÑкого над немецкими рыцарÑми на ЧудÑком озере (Ледовое побоище, 1242 год) +10 июл День победы руÑÑкой армии под командованием Петра Первого над шведами в ПолтавÑком Ñражении (1709 год) + 9 авг День первой в роÑÑийÑкой иÑтории морÑкой победы руÑÑкого флота под командованием Петра Первого над шведами у мыÑа Гангут (1714 год) +23 авг День разгрома ÑоветÑкими войÑками немецко-фашиÑÑ‚Ñких войÑк в КурÑкой битве (1943 год) + 2 Ñен День Ð¾ÐºÐ¾Ð½Ñ‡Ð°Ð½Ð¸Ñ Ð’Ñ‚Ð¾Ñ€Ð¾Ð¹ мировой войны (1945 год) + 8 Ñен День БородинÑкого ÑÑ€Ð°Ð¶ÐµÐ½Ð¸Ñ Ñ€ÑƒÑÑкой армии под командованием М.И. Кутузова Ñ Ñ„Ñ€Ð°Ð½Ñ†ÑƒÐ·Ñкой армией (1812 год) +11 Ñен День победы руÑÑкой ÑÑкадры под командованием Ф.Ф. Ушакова над турецкой ÑÑкадрой у мыÑа Тендра (1790 год) +21 Ñен День победы руÑÑких полков во главе Ñ Ð²ÐµÐ»Ð¸ÐºÐ¸Ð¼ кнÑзем Дмитрием ДонÑким над монголо-татарÑкими войÑками в КуликовÑкой битве (1380 год) + 7 Ð½Ð¾Ñ Ð”ÐµÐ½ÑŒ оÑÐ²Ð¾Ð±Ð¾Ð¶Ð´ÐµÐ½Ð¸Ñ ÐœÐ¾Ñквы Ñилами народного Ð¾Ð¿Ð¾Ð»Ñ‡ÐµÐ½Ð¸Ñ Ð¿Ð¾Ð´ руководÑтвом Кузьмы Минина и Ð”Ð¼Ð¸Ñ‚Ñ€Ð¸Ñ ÐŸÐ¾Ð¶Ð°Ñ€Ñкого от польÑких интервентов (1612 год) + 1 дек День победы руÑÑкой ÑÑкадры под командованием П.С. Ðахимова над турецкой ÑÑкадрой у мыÑа Синоп (1853 год) + 5 дек День начала контрнаÑÑ‚ÑƒÐ¿Ð»ÐµÐ½Ð¸Ñ ÑоветÑких войÑк против немецко-фашиÑÑ‚Ñких войÑк в битве под МоÑквой (1941 год) +24 дек День взÑÑ‚Ð¸Ñ Ñ‚ÑƒÑ€ÐµÑ†ÐºÐ¾Ð¹ крепоÑти Измаил руÑÑкими войÑками под командованием Ð.Ð’. Суворова (1790 год) + +#endif /* !_ru_RU_UTF_8_military_ */ diff --git a/usr.bin/calendar/calendars/ru_RU.UTF-8/calendar.orthodox b/usr.bin/calendar/calendars/ru_RU.UTF-8/calendar.orthodox new file mode 100644 index 00000000000..f42c6aee7d5 --- /dev/null +++ b/usr.bin/calendar/calendars/ru_RU.UTF-8/calendar.orthodox @@ -0,0 +1,36 @@ +/* + * ПравоÑлавные праздники + * + * $FreeBSD$ + */ + +#ifndef _ru_RU_UTF_8_orthodox_ +#define _ru_RU_UTF_8_orthodox_ + +LANG=ru_RU.UTF-8 +Paskha=ПаÑха + +21 Ñен РождеÑтво ПреÑвÑтой Богородицы +27 Ñен Воздвижение КреÑта ГоÑÐ¿Ð¾Ð´Ð½Ñ +14 окт Покров ПреÑвÑтой Богородицы + 4 дек Введение во храм ПреÑвÑтой Богородицы + 7 Ñнв РождеÑтво ХриÑтово +14 Ñнв Обрезание ГоÑподне +19 Ñнв БогоÑвление или Крещение ГоÑподне +15 фев Сретение ГоÑподне +ПаÑха-48 Великий ПоÑÑ‚ +ПаÑха-7 Вход ГоÑподень в ИеруÑалим. Вербное ВоÑкреÑенье +ПаÑха-3 Великий Четверг +ПаÑха-2 СтраÑÑ‚Ð½Ð°Ñ ÐŸÑтница +ПаÑха ВоÑкреÑение ХриÑтово +ПаÑха+39 ВознеÑение +ПаÑха+49 День СвÑтой Троицы. ПÑтидеÑÑтница + 7 апр Благовещение ПреÑвÑтой Богородицы + 7 июл РождеÑтво Иоанна Предтечи +12 июл День ÑвÑтых первоверховных апоÑтолов Петра и Павла +19 авг Преображение ГоÑподне +28 авг УÑпение ПреÑвÑтой Богородицы +11 Ñен УÑекновение главы Иоанна Предтечи + +#endif /* !_ru_RU_UTF_8_orthodox_ */ + diff --git a/usr.bin/calendar/calendars/ru_RU.UTF-8/calendar.pagan b/usr.bin/calendar/calendars/ru_RU.UTF-8/calendar.pagan new file mode 100644 index 00000000000..144a649dc67 --- /dev/null +++ b/usr.bin/calendar/calendars/ru_RU.UTF-8/calendar.pagan @@ -0,0 +1,42 @@ +/* + * ЯзычеÑкие праздники + * + * $FreeBSD$ + */ + +#ifndef _ru_RU_UTF_8_pagan_ +#define _ru_RU_UTF_8_pagan_ + +LANG=ru_RU.UTF-8 +Paskha=ПаÑха + +21 дек* Зимнее ÑолнцеÑтоÑние +25 дек КолÑда (Ñдвинутое зимнее ÑолнцеÑтоÑние) + 6 Ñнв День ÐšÐ°Ñ‰ÐµÑ Ð¸ ВелеÑа +24 фев День ВелеÑа +29 фев День ÐšÐ°Ñ‰ÐµÑ + 1 мар День Марены +14 мар Ðовый Год, ОвÑень малый +ПаÑха-55 МаÑленица +ПаÑха+7 КраÑÐ½Ð°Ñ Ð“Ð¾Ñ€ÐºÐ° +ПаÑха+16 Радуница +20 мар* ВеÑенние равноденÑтвие + 7 апр День Марены (Ñдвинутое веÑеннее равноденÑтвие) + 6 май День Дажьбога, ОвÑень большой +22 май Ярилин День +15 июн День Триглава +21 июн* Летнее ÑолнцеÑтоÑние + 1 июл РуÑÐ°Ð»ÑŒÐ½Ð°Ñ ÐÐµÐ´ÐµÐ»Ñ + 7 июл Купала (Ñдвинутое летнее ÑолнцеÑтоÑние) +27 июл Отбор жертв Перуну, руÑалии + 2 авг Перунов День +21 авг День Стрибога +28 авг УÑпение Златогорки +14 Ñен День Волха Змеевича +22 Ñен* Поворот к зиме (оÑеннее равноденÑтвие) +10 Ð½Ð¾Ñ Ð”ÐµÐ½ÑŒ Макоши +21 Ð½Ð¾Ñ Ð”ÐµÐ½ÑŒ Сварога и Семаргла + 9 дек День Дажьбога и Марены + +#endif /* !_ru_RU_UTF_8_pagan_ */ + diff --git a/usr.bin/dig/Makefile b/usr.bin/dig/Makefile index ec11dc48352..b76a0cacc2b 100644 --- a/usr.bin/dig/Makefile +++ b/usr.bin/dig/Makefile @@ -20,7 +20,7 @@ CFLAGS+= -DWITH_IDN -I/usr/local/include CFLAGS+= -L/usr/local/lib -lidnkit -R/usr/local/lib -liconv .endif -WARNS?= 1 +WARNS?= 0 DPADD+= ${BIND_DPADD} ${CRYPTO_DPADD} ${PTHREAD_DPADD} LDADD+= ${BIND_LDADD} ${CRYPTO_LDADD} ${PTHREAD_LDADD} diff --git a/usr.bin/dtc/dtc.1 b/usr.bin/dtc/dtc.1 index 97247876c75..afb94f763e0 100644 --- a/usr.bin/dtc/dtc.1 +++ b/usr.bin/dtc/dtc.1 @@ -51,6 +51,7 @@ .Op Fl p Ar bytes .Op Fl V Ar blob_version .Op Fl W Ar [no-]checker_name +.Op Fl P Ar predefined_properties .Ar input_file .Sh DESCRIPTION The @@ -132,6 +133,22 @@ The ASCII representation of the FDT. .El .It Fl o Ar output_file The file to which to write the output. +.It Fl P Ar predefined_macro +Defines a macro, in the form +.Ar name=value +or +.Ar name +to be used for device tree source files that contain conditional components. +This tool supports two extensions to the standard to support conditional +compilation of device trees. +The first is an +.Ar /include/if [property]/ "file.dts" +directive that is allowed at the start of a file and which will only include +the specified file if it the specified property is passed with this flag. +The second is the +.Ar $NAME +format for property values. +These allow property value to be specified on the command line. .It Fl R Ar entries The number of empty reservation table entries to pad the table with. This is diff --git a/usr.bin/dtc/dtc.cc b/usr.bin/dtc/dtc.cc index cffb3eb3cc3..c04f0161680 100644 --- a/usr.bin/dtc/dtc.cc +++ b/usr.bin/dtc/dtc.cc @@ -100,7 +100,7 @@ main(int argc, char **argv) clock_t c0 = clock(); class device_tree tree; fdt::checking::check_manager checks; - const char *options = "hqI:O:o:V:d:R:S:p:b:fisvH:W:E:D"; + const char *options = "hqI:O:o:V:d:R:S:p:b:fisvH:W:E:DP:"; // Don't forget to update the man page if any more options are added. while ((ch = getopt(argc, argv, options)) != -1) @@ -267,6 +267,13 @@ main(int argc, char **argv) case 'p': tree.set_blob_padding(strtoll(optarg, 0, 10)); break; + case 'P': + if (!tree.parse_define(optarg)) + { + fprintf(stderr, "Invalid predefine value %s\n", + optarg); + } + break; default: fprintf(stderr, "Unknown option %c\n", ch); return EXIT_FAILURE; diff --git a/usr.bin/dtc/fdt.cc b/usr.bin/dtc/fdt.cc index 6f27c9cb0b5..eb944fca2cc 100644 --- a/usr.bin/dtc/fdt.cc +++ b/usr.bin/dtc/fdt.cc @@ -382,13 +382,45 @@ property::property(input_buffer &structs, input_buffer &strings) values.push_back(v); } -property::property(input_buffer &input, string k, string l) : key(k), label(l), - valid(true) +void property::parse_define(input_buffer &input, define_map *defines) +{ + input.consume('$'); + if (!defines) + { + input.parse_error("No predefined properties to match name\n"); + valid = false; + return; + } + string name = string::parse_property_name(input); + define_map::iterator found; + if ((name == string()) || + ((found = defines->find(name)) == defines->end())) + { + input.parse_error("Undefined property name\n"); + valid = false; + return; + } + values.push_back((*found).second->values[0]); +} + +property::property(input_buffer &input, + string k, + string l, + bool semicolonTerminated, + define_map *defines) : key(k), label(l), valid(true) { do { input.next_token(); switch (input[0]) { + case '$': + { + parse_define(input, defines); + if (valid) + { + break; + } + } default: input.parse_error("Invalid property value."); valid = false; @@ -412,7 +444,7 @@ property::property(input_buffer &input, string k, string l) : key(k), label(l), } input.next_token(); } while (input.consume(',')); - if (!input.consume(';')) + if (semicolonTerminated && !input.consume(';')) { input.parse_error("Expected ; at end of property"); valid = false; @@ -432,9 +464,10 @@ property::parse_dtb(input_buffer &structs, input_buffer &strings) } property* -property::parse(input_buffer &input, string key, string label) +property::parse(input_buffer &input, string key, string label, + bool semicolonTerminated, define_map *defines) { - property *p = new property(input, key, label); + property *p = new property(input, key, label, semicolonTerminated, defines); if (!p->valid) { delete p; @@ -591,7 +624,7 @@ node::node(input_buffer &structs, input_buffer &strings) : valid(true) return; } -node::node(input_buffer &input, string n, string l, string a) : +node::node(input_buffer &input, string n, string l, string a, define_map *defines) : label(l), name(n), unit_address(a), valid(true) { if (!input.consume('{')) @@ -628,7 +661,7 @@ node::node(input_buffer &input, string n, string l, string a) : if (input.consume('=')) { property *p= property::parse(input, child_name, - child_label); + child_label, true, defines); if (p == 0) { valid = false; @@ -641,7 +674,7 @@ node::node(input_buffer &input, string n, string l, string a) : else if (!is_property && input[0] == ('{')) { node *child = node::parse(input, child_name, - child_label, child_address); + child_label, child_address, defines); if (child) { children.push_back(child); @@ -693,9 +726,13 @@ node::sort() } node* -node::parse(input_buffer &input, string name, string label, string address) +node::parse(input_buffer &input, + string name, + string label, + string address, + define_map *defines) { - node *n = new node(input, name, label, address); + node *n = new node(input, name, label, address, defines); if (!n->valid) { delete n; @@ -1008,7 +1045,7 @@ device_tree::parse_roots(input_buffer &input, std::vector &roots) while (valid && input.consume('/')) { input.next_token(); - node *n = node::parse(input, string("", 1)); + node *n = node::parse(input, string("", 1), string(), string(), &defines); if (n) { roots.push_back(n); @@ -1241,6 +1278,18 @@ device_tree::parse_dts(const char *fn, FILE *depfile) input.next_token(); while(input.consume("/include/")) { + bool reallyInclude = true; + if (input.consume("if ")) + { + input.next_token(); + string name = string::parse_property_name(input); + // XXX: Error handling + if (defines.find(name) == defines.end()) + { + reallyInclude = false; + } + input.consume('/'); + } input.next_token(); if (!input.consume('"')) { @@ -1259,6 +1308,14 @@ device_tree::parse_dts(const char *fn, FILE *depfile) include_file[dir_length] = '/'; memcpy(include_file+dir_length+1, file, length); include_file[dir_length+length+1] = 0; + + input.consume(include_file+dir_length+1); + input.consume('"'); + if (!reallyInclude) + { + continue; + } + input_buffer *include_buffer = buffer_for_file(include_file); if (include_buffer == 0) @@ -1292,8 +1349,6 @@ device_tree::parse_dts(const char *fn, FILE *depfile) return; } input_buffer &include = *include_buffer; - input.consume(include_file+dir_length+1); - input.consume('"'); free((void*)include_file); if (!read_header) @@ -1361,6 +1416,33 @@ device_tree::~device_tree() delete buffers.back(); buffers.pop_back(); } + for (define_map::iterator i=defines.begin(), e=defines.end() ; + i!=e ; ++i) + { + delete i->second; + } +} + +bool device_tree::parse_define(const char *def) +{ + char *val = strchr(def, '='); + if (!val) + { + if (strlen(def) != 0) + { + string name(def); + defines[name]; + return true; + } + return false; + } + string name(def, val-def); + val++; + input_buffer in = input_buffer(val, strlen(val)); + property *p = property::parse(in, name, string(), false); + if (p) + defines[name] = p; + return p; } } // namespace fdt diff --git a/usr.bin/dtc/fdt.hh b/usr.bin/dtc/fdt.hh index b00ea48063a..3ac10847e5f 100644 --- a/usr.bin/dtc/fdt.hh +++ b/usr.bin/dtc/fdt.hh @@ -48,6 +48,8 @@ class string_table; namespace fdt { +class property; +typedef std::map define_map; /** * Properties may contain a number of different value, each with a different * label. This class encapsulates a single value. @@ -262,6 +264,10 @@ class property * follow their interpretation for compatibility. */ void parse_reference(input_buffer &input); + /** + * Parse a predefined macro definition for a property. + */ + void parse_define(input_buffer &input, define_map *defines); /** * Constructs a new property from two input buffers, pointing to the * struct and strings tables in the device tree blob, respectively. @@ -272,7 +278,11 @@ class property /** * Parses a new property from the input buffer. */ - property(input_buffer &input, string k, string l); + property(input_buffer &input, + string k, + string l, + bool terminated, + define_map *defines); public: /** * Creates an empty property. @@ -298,7 +308,9 @@ class property */ static property* parse(input_buffer &input, string key, - string label=string()); + string label=string(), + bool semicolonTerminated=true, + define_map *defines=0); /** * Iterator type used for accessing the values of a property. */ @@ -398,7 +410,7 @@ class node * node. The name, and optionally label and unit address, should have * already been parsed. */ - node(input_buffer &input, string n, string l, string a); + node(input_buffer &input, string n, string l, string a, define_map*); /** * Comparison function for properties, used when sorting the properties * vector. Orders the properties based on their names. @@ -476,7 +488,8 @@ class node static node* parse(input_buffer &input, string name, string label=string(), - string address=string()); + string address=string(), + define_map *defines=0); /** * Factory method for constructing a new node. Attempts to parse a * node in DTB format from the input, and returns it on success. On @@ -616,6 +629,10 @@ class device_tree * must be freed separately. */ std::vector include_paths; + /** + * Dictionary of predefined macros provided on the command line. + */ + define_map defines; /** * The default boot CPU, specified in the device tree header. */ @@ -773,6 +790,10 @@ class device_tree { blob_padding = p; } + /** + * Parses a predefined macro value. + */ + bool parse_define(const char *def); }; } // namespace fdt diff --git a/usr.bin/dtc/input_buffer.cc b/usr.bin/dtc/input_buffer.cc index 10b3a0018be..8f055fe61d7 100644 --- a/usr.bin/dtc/input_buffer.cc +++ b/usr.bin/dtc/input_buffer.cc @@ -216,7 +216,8 @@ input_buffer::parse_error(const char *msg) putc('\n', stderr); for (int i=0 ; i<(cursor-line_start) ; ++i) { - putc(' ', stderr); + char c = (buffer[i+line_start] == '\t') ? '\t' : ' '; + putc(c, stderr); } putc('^', stderr); putc('\n', stderr); diff --git a/usr.bin/host/Makefile b/usr.bin/host/Makefile index e2909e13782..25dfd2d1dff 100644 --- a/usr.bin/host/Makefile +++ b/usr.bin/host/Makefile @@ -1,5 +1,29 @@ # $FreeBSD$ +.include + +.if ${MK_LDNS_UTILS} != "no" + +LDNSDIR= ${.CURDIR}/../../contrib/ldns +LDNSHOSTDIR= ${.CURDIR}/../../contrib/ldns-host + +.PATH: ${LDNSHOSTDIR} + +PROG= host +SRCS= ldns-host.c +MAN= host.1 + +host.1: ldns-host.1 + sed -e 's/ldns-//gI' <${.ALLSRC} >${.TARGET} || \ + (rm -rf ${.TARGET} ; false) + +CFLAGS+= -I${LDNSDIR} +DPADD+= ${LIBLDNS} ${LIBCRYPTO} +LDADD+= -lldns -lcrypto +USEPRIVATELIB= ldns + +.else + BIND_DIR= ${.CURDIR}/../../contrib/bind9 LIB_BIND_REL= ../../lib/bind LIB_BIND_DIR= ${.CURDIR}/${LIB_BIND_REL} @@ -15,9 +39,11 @@ SRCS+= dighost.c host.c CFLAGS+= -I${SRCDIR}/include CFLAGS+= -I${BIND_DIR}/lib/isc/${ISC_ATOMIC_ARCH}/include -WARNS?= 1 +WARNS?= 0 DPADD+= ${BIND_DPADD} ${CRYPTO_DPADD} ${PTHREAD_DPADD} LDADD+= ${BIND_LDADD} ${CRYPTO_LDADD} ${PTHREAD_LDADD} +.endif + .include diff --git a/usr.bin/iscsictl/Makefile b/usr.bin/iscsictl/Makefile new file mode 100644 index 00000000000..9331ca57563 --- /dev/null +++ b/usr.bin/iscsictl/Makefile @@ -0,0 +1,19 @@ +# $FreeBSD$ + +PROG= iscsictl +SRCS= iscsictl.c periphs.c parse.y token.l y.tab.h +CFLAGS+= -I${.CURDIR} +CFLAGS+= -I${.CURDIR}/../../sys/dev/iscsi +MAN= iscsictl.8 + +DPADD= ${LIBCAM} ${LIBUTIL} +LDADD= -lcam -lfl -lutil + +YFLAGS+= -v +LFLAGS+= -i +CLEANFILES= y.tab.c y.tab.h y.output + +WARNS= 6 +NO_WMISSING_VARIABLE_DECLARATIONS= + +.include diff --git a/usr.bin/iscsictl/iscsictl.8 b/usr.bin/iscsictl/iscsictl.8 new file mode 100644 index 00000000000..03525df1238 --- /dev/null +++ b/usr.bin/iscsictl/iscsictl.8 @@ -0,0 +1,153 @@ +.\" Copyright (c) 2012 The FreeBSD Foundation +.\" All rights reserved. +.\" +.\" This software was developed by Edward Tomasz Napierala under sponsorship +.\" from the FreeBSD Foundation. +.\" +.\" 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. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHORS 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 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. +.\" +.\" $FreeBSD$ +.\" +.Dd September 20, 2012 +.Dt ISCSICTL 8 +.Os +.Sh NAME +.Nm iscsictl +.Nd iSCSI initiator management utility +.Sh SYNOPSIS +.Nm +.Fl A +.Fl h Ar host Fl t Ar target Op Fl u Ar user Fl s Ar secret +.Nm +.Fl A +.Fl d Ar discovery-host Op Fl u Ar user Fl s Ar secret +.Nm +.Fl A +.Fl a Op Fl c Ar path +.Nm +.Fl A +.Fl n Ar nickname Op Fl c Ar path +.Nm +.Fl R +.Op Fl h Ar host +.Op Fl t Ar target +.Nm +.Fl R +.Fl a +.Nm +.Fl R +.Fl n Ar nickname Op Fl c Ar path +.Nm +.Fl L +.Op Fl v +.Sh DESCRIPTION +The +.Nm +utility is used to configure the iSCSI initiator. +.Pp +The following options are available: +.Bl -tag -width ".Fl A" +.It Fl A +Add session. +.It Fl R +Remove session. +.It Fl L +List sessions. +.It Fl a +When adding, add all sessions defined in the configuration file. +When removing, remove all currently established sessions. +.It Fl c +Path to the configuration file. +The default is +.Pa /etc/iscsi.conf . +.It Fl d +Target host name or address used for SendTargets discovery. +When used, it will add a temporary discovery session. +After discovery is done, sessions will be added for each discovered target, +and the temporary discovery sesion will be removed. +.It Fl h +Target host name or address for statically defined targets. +.It Fl n +The "nickname" of session defined in the configuration file. +.It Fl t +Target name. +.It Fl v +Verbose mode. +.El +.Pp +Since connecting to the target is performed in background, non-zero +exit status does not mean that the session was successfully established. +Use +.Nm Fl L +to check the connection status. +The initiator notifies +.Xr devd 8 +when session gets connected or disconnected. +.Pp +Note that in order to the iSCSI initiator to be able to connect to a target, +the +.Xr iscsid 8 +daemon must be running. +.Pp +Also note that +.Fx +currently supports two different initiators: the old one, +.Xr iscsi_initiator 4 , +with its control utility +.Xr iscontrol 8 , +and the new one, +.Xr iscsi 4 , +with +.Nm +and +.Xr iscsid 8 . +The only thing the two have in common is the configuration file, +.Xr iscsi.conf 5 . +.Sh FILES +.Bl -tag -width ".Pa /etc/iscsi.conf" -compact +.It Pa /etc/iscsi.conf +iSCSI initiator configuration file. +.El +.Sh EXIT STATUS +The +.Nm +utility exits 0 on success, and >0 if an error occurs. +.Sh EXAMPLES +Attach to target qn.2012-06.com.example:target0, served by 192.168.1.1: +.Dl Nm Fl A Fl t Ar qn.2012-06.com.example:target0 Fl h Ar 192.168.1.1 +.Pp +Disconnect all iSCSI sessions: +.Dl Nm Fl Ra +.Sh SEE ALSO +.Xr iscsi.conf 5 , +.Xr iscsid 8 +.Sh HISTORY +The +.Nm +command appeared in +.Fx 10.0 . +.Sh AUTHORS +The +.Nm +was developed by +.An Edward Tomasz Napierala Aq trasz@FreeBSD.org +under sponsorship from the FreeBSD Foundation. diff --git a/usr.bin/iscsictl/iscsictl.c b/usr.bin/iscsictl/iscsictl.c new file mode 100644 index 00000000000..1088d69f03e --- /dev/null +++ b/usr.bin/iscsictl/iscsictl.c @@ -0,0 +1,742 @@ +/*- + * Copyright (c) 2012 The FreeBSD Foundation + * All rights reserved. + * + * This software was developed by Edward Tomasz Napierala under sponsorship + * from the FreeBSD Foundation. + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + * $FreeBSD$ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "iscsictl.h" + +struct conf * +conf_new(void) +{ + struct conf *conf; + + conf = calloc(1, sizeof(*conf)); + if (conf == NULL) + err(1, "calloc"); + + TAILQ_INIT(&conf->conf_targets); + + return (conf); +} + +struct target * +target_find(struct conf *conf, const char *nickname) +{ + struct target *targ; + + TAILQ_FOREACH(targ, &conf->conf_targets, t_next) { + if (targ->t_nickname != NULL && + strcasecmp(targ->t_nickname, nickname) == 0) + return (targ); + } + + return (NULL); +} + +struct target * +target_new(struct conf *conf) +{ + struct target *targ; + + targ = calloc(1, sizeof(*targ)); + if (targ == NULL) + err(1, "calloc"); + targ->t_conf = conf; + TAILQ_INSERT_TAIL(&conf->conf_targets, targ, t_next); + + return (targ); +} + +void +target_delete(struct target *targ) +{ + + TAILQ_REMOVE(&targ->t_conf->conf_targets, targ, t_next); + free(targ); +} + + +static char * +default_initiator_name(void) +{ + char *name; + size_t namelen; + int error; + + namelen = _POSIX_HOST_NAME_MAX + strlen(DEFAULT_IQN); + + name = calloc(1, namelen + 1); + if (name == NULL) + err(1, "calloc"); + strcpy(name, DEFAULT_IQN); + error = gethostname(name + strlen(DEFAULT_IQN), + namelen - strlen(DEFAULT_IQN)); + if (error != 0) + err(1, "gethostname"); + + return (name); +} + +static bool +valid_hex(const char ch) +{ + switch (ch) { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case 'a': + case 'A': + case 'b': + case 'B': + case 'c': + case 'C': + case 'd': + case 'D': + case 'e': + case 'E': + case 'f': + case 'F': + return (true); + default: + return (false); + } +} + +bool +valid_iscsi_name(const char *name) +{ + int i; + + if (strlen(name) >= MAX_NAME_LEN) { + warnx("overlong name for \"%s\"; max length allowed " + "by iSCSI specification is %d characters", + name, MAX_NAME_LEN); + return (false); + } + + /* + * In the cases below, we don't return an error, just in case the admin + * was right, and we're wrong. + */ + if (strncasecmp(name, "iqn.", strlen("iqn.")) == 0) { + for (i = strlen("iqn."); name[i] != '\0'; i++) { + /* + * XXX: We should verify UTF-8 normalisation, as defined + * by 3.2.6.2: iSCSI Name Encoding. + */ + if (isalnum(name[i])) + continue; + if (name[i] == '-' || name[i] == '.' || name[i] == ':') + continue; + warnx("invalid character \"%c\" in iSCSI name " + "\"%s\"; allowed characters are letters, digits, " + "'-', '.', and ':'", name[i], name); + break; + } + /* + * XXX: Check more stuff: valid date and a valid reversed domain. + */ + } else if (strncasecmp(name, "eui.", strlen("eui.")) == 0) { + if (strlen(name) != strlen("eui.") + 16) + warnx("invalid iSCSI name \"%s\"; the \"eui.\" " + "should be followed by exactly 16 hexadecimal " + "digits", name); + for (i = strlen("eui."); name[i] != '\0'; i++) { + if (!valid_hex(name[i])) { + warnx("invalid character \"%c\" in iSCSI " + "name \"%s\"; allowed characters are 1-9 " + "and A-F", name[i], name); + break; + } + } + } else if (strncasecmp(name, "naa.", strlen("naa.")) == 0) { + if (strlen(name) > strlen("naa.") + 32) + warnx("invalid iSCSI name \"%s\"; the \"naa.\" " + "should be followed by at most 32 hexadecimal " + "digits", name); + for (i = strlen("naa."); name[i] != '\0'; i++) { + if (!valid_hex(name[i])) { + warnx("invalid character \"%c\" in ISCSI " + "name \"%s\"; allowed characters are 1-9 " + "and A-F", name[i], name); + break; + } + } + } else { + warnx("invalid iSCSI name \"%s\"; should start with " + "either \".iqn\", \"eui.\", or \"naa.\"", + name); + } + return (true); +} + +void +conf_verify(struct conf *conf) +{ + struct target *targ; + + TAILQ_FOREACH(targ, &conf->conf_targets, t_next) { + assert(targ->t_nickname != NULL); + if (targ->t_session_type == SESSION_TYPE_UNSPECIFIED) + targ->t_session_type = SESSION_TYPE_NORMAL; + if (targ->t_session_type == SESSION_TYPE_NORMAL && + targ->t_name == NULL) + errx(1, "missing TargetName for target \"%s\"", + targ->t_nickname); + if (targ->t_session_type == SESSION_TYPE_DISCOVERY && + targ->t_name != NULL) + errx(1, "cannot specify TargetName for discovery " + "sessions for target \"%s\"", targ->t_nickname); + if (targ->t_name != NULL) { + if (valid_iscsi_name(targ->t_name) == false) + errx(1, "invalid target name \"%s\"", + targ->t_name); + } + if (targ->t_protocol == PROTOCOL_UNSPECIFIED) + targ->t_protocol = PROTOCOL_ISCSI; +#ifndef ICL_KERNEL_PROXY + if (targ->t_protocol == PROTOCOL_ISER) + errx(1, "iSER support requires ICL_KERNEL_PROXY; " + "see iscsi(4) for details"); +#endif + if (targ->t_address == NULL) + errx(1, "missing TargetAddress for target \"%s\"", + targ->t_nickname); + if (targ->t_initiator_name == NULL) + targ->t_initiator_name = default_initiator_name(); + if (valid_iscsi_name(targ->t_initiator_name) == false) + errx(1, "invalid initiator name \"%s\"", + targ->t_initiator_name); + if (targ->t_header_digest == DIGEST_UNSPECIFIED) + targ->t_header_digest = DIGEST_NONE; + if (targ->t_data_digest == DIGEST_UNSPECIFIED) + targ->t_data_digest = DIGEST_NONE; + if (targ->t_auth_method == AUTH_METHOD_UNSPECIFIED) { + if (targ->t_user != NULL || targ->t_secret != NULL || + targ->t_mutual_user != NULL || + targ->t_mutual_secret != NULL) + targ->t_auth_method = + AUTH_METHOD_CHAP; + else + targ->t_auth_method = + AUTH_METHOD_NONE; + } + if (targ->t_auth_method == AUTH_METHOD_CHAP) { + if (targ->t_user == NULL) { + errx(1, "missing chapIName for target \"%s\"", + targ->t_nickname); + } + if (targ->t_secret == NULL) + errx(1, "missing chapSecret for target \"%s\"", + targ->t_nickname); + if (targ->t_mutual_user != NULL || + targ->t_mutual_secret != NULL) { + if (targ->t_mutual_user == NULL) + errx(1, "missing tgtChapName for " + "target \"%s\"", targ->t_nickname); + if (targ->t_mutual_secret == NULL) + errx(1, "missing tgtChapSecret for " + "target \"%s\"", targ->t_nickname); + } + } + } +} + +static void +conf_from_target(struct iscsi_session_conf *conf, + const struct target *targ) +{ + memset(conf, 0, sizeof(*conf)); + + /* + * XXX: Check bounds and return error instead of silently truncating. + */ + if (targ->t_initiator_name != NULL) + strlcpy(conf->isc_initiator, targ->t_initiator_name, + sizeof(conf->isc_initiator)); + if (targ->t_initiator_address != NULL) + strlcpy(conf->isc_initiator_addr, targ->t_initiator_address, + sizeof(conf->isc_initiator_addr)); + if (targ->t_initiator_alias != NULL) + strlcpy(conf->isc_initiator_alias, targ->t_initiator_alias, + sizeof(conf->isc_initiator_alias)); + if (targ->t_name != NULL) + strlcpy(conf->isc_target, targ->t_name, + sizeof(conf->isc_target)); + if (targ->t_address != NULL) + strlcpy(conf->isc_target_addr, targ->t_address, + sizeof(conf->isc_target_addr)); + if (targ->t_user != NULL) + strlcpy(conf->isc_user, targ->t_user, + sizeof(conf->isc_user)); + if (targ->t_secret != NULL) + strlcpy(conf->isc_secret, targ->t_secret, + sizeof(conf->isc_secret)); + if (targ->t_mutual_user != NULL) + strlcpy(conf->isc_mutual_user, targ->t_mutual_user, + sizeof(conf->isc_mutual_user)); + if (targ->t_mutual_secret != NULL) + strlcpy(conf->isc_mutual_secret, targ->t_mutual_secret, + sizeof(conf->isc_mutual_secret)); + if (targ->t_session_type == SESSION_TYPE_DISCOVERY) + conf->isc_discovery = 1; + if (targ->t_protocol == PROTOCOL_ISER) + conf->isc_iser = 1; + if (targ->t_header_digest == DIGEST_CRC32C) + conf->isc_header_digest = ISCSI_DIGEST_CRC32C; + else + conf->isc_header_digest = ISCSI_DIGEST_NONE; + if (targ->t_data_digest == DIGEST_CRC32C) + conf->isc_data_digest = ISCSI_DIGEST_CRC32C; + else + conf->isc_data_digest = ISCSI_DIGEST_NONE; +} + +static int +kernel_add(int iscsi_fd, const struct target *targ) +{ + struct iscsi_session_add isa; + int error; + + memset(&isa, 0, sizeof(isa)); + conf_from_target(&isa.isa_conf, targ); + error = ioctl(iscsi_fd, ISCSISADD, &isa); + if (error != 0) + warn("ISCSISADD"); + return (error); +} + +static int +kernel_remove(int iscsi_fd, const struct target *targ) +{ + struct iscsi_session_remove isr; + int error; + + memset(&isr, 0, sizeof(isr)); + conf_from_target(&isr.isr_conf, targ); + error = ioctl(iscsi_fd, ISCSISREMOVE, &isr); + if (error != 0) + warn("ISCSISREMOVE"); + return (error); +} + +/* + * XXX: Add filtering. + */ +static int +kernel_list(int iscsi_fd, const struct target *targ __unused, + int verbose) +{ + struct iscsi_session_state *states = NULL; + const struct iscsi_session_state *state; + const struct iscsi_session_conf *conf; + struct iscsi_session_list isl; + unsigned int i, nentries = 1; + int error; + bool show_periphs; + + for (;;) { + states = realloc(states, + nentries * sizeof(struct iscsi_session_state)); + if (states == NULL) + err(1, "realloc"); + + memset(&isl, 0, sizeof(isl)); + isl.isl_nentries = nentries; + isl.isl_pstates = states; + + error = ioctl(iscsi_fd, ISCSISLIST, &isl); + if (error != 0 && errno == EMSGSIZE) { + nentries *= 4; + continue; + } + break; + } + if (error != 0) { + warn("ISCSISLIST"); + return (error); + } + + if (verbose != 0) { + for (i = 0; i < isl.isl_nentries; i++) { + state = &states[i]; + conf = &state->iss_conf; + + printf("Session ID: %d\n", state->iss_id); + printf("Initiator name: %s\n", conf->isc_initiator); + printf("Initiator addr: %s\n", + conf->isc_initiator_addr); + printf("Initiator alias: %s\n", + conf->isc_initiator_alias); + printf("Target name: %s\n", conf->isc_target); + printf("Target addr: %s\n", + conf->isc_target_addr); + printf("Target alias: %s\n", + state->iss_target_alias); + printf("User: %s\n", conf->isc_user); + printf("Secret: %s\n", conf->isc_secret); + printf("Mutual user: %s\n", + conf->isc_mutual_user); + printf("Mutual secret: %s\n", + conf->isc_mutual_secret); + printf("Session type: %s\n", + conf->isc_discovery ? "Discovery" : "Normal"); + printf("Session state: %s\n", + state->iss_connected ? + "Connected" : "Disconnected"); + printf("Failure reason: %s\n", state->iss_reason); + printf("Header digest: %s\n", + state->iss_header_digest == ISCSI_DIGEST_CRC32C ? + "CRC32C" : "None"); + printf("Data digest: %s\n", + state->iss_data_digest == ISCSI_DIGEST_CRC32C ? + "CRC32C" : "None"); + printf("DataSegmentLen: %d\n", + state->iss_max_data_segment_length); + printf("ImmediateData: %s\n", + state->iss_immediate_data ? "Yes" : "No"); + printf("iSER (RDMA): %s\n", + conf->isc_iser ? "Yes" : "No"); + printf("Device nodes: "); + print_periphs(state->iss_id); + printf("\n\n"); + } + } else { + printf("%-36s %-16s %s\n", + "Target name", "Target addr", "State"); + for (i = 0; i < isl.isl_nentries; i++) { + state = &states[i]; + conf = &state->iss_conf; + show_periphs = false; + + printf("%-36s %-16s ", + conf->isc_target, conf->isc_target_addr); + + if (state->iss_reason[0] != '\0') { + printf("%s\n", state->iss_reason); + } else { + if (conf->isc_discovery) { + printf("Discovery\n"); + } else if (state->iss_connected) { + printf("Connected: "); + print_periphs(state->iss_id); + printf("\n"); + } else { + printf("Disconnected\n"); + } + } + } + } + + return (0); +} + +static void +usage(void) +{ + + fprintf(stderr, "usage: iscsictl -A -h host -t target " + "[-u user -s secret]\n"); + fprintf(stderr, " iscsictl -A -d discovery-host " + "[-u user -s secret]\n"); + fprintf(stderr, " iscsictl -A -a [-c path]\n"); + fprintf(stderr, " iscsictl -A -n nickname [-c path]\n"); + fprintf(stderr, " iscsictl -R [-h host] [-t target]\n"); + fprintf(stderr, " iscsictl -R -a\n"); + fprintf(stderr, " iscsictl -R -n nickname [-c path]\n"); + fprintf(stderr, " iscsictl -L [-v]\n"); + exit(1); +} + +char * +checked_strdup(const char *s) +{ + char *c; + + c = strdup(s); + if (c == NULL) + err(1, "strdup"); + return (c); +} + +int +main(int argc, char **argv) +{ + int Aflag = 0, Rflag = 0, Lflag = 0, aflag = 0, vflag = 0; + const char *conf_path = DEFAULT_CONFIG_PATH; + char *nickname = NULL, *discovery_host = NULL, *host = NULL, + *target = NULL, *user = NULL, *secret = NULL; + int ch, error, iscsi_fd, retval, saved_errno; + int failed = 0; + struct conf *conf; + struct target *targ; + + while ((ch = getopt(argc, argv, "ARLac:d:n:h:t:u:s:v")) != -1) { + switch (ch) { + case 'A': + Aflag = 1; + break; + case 'R': + Rflag = 1; + break; + case 'L': + Lflag = 1; + break; + case 'a': + aflag = 1; + break; + case 'c': + conf_path = optarg; + break; + case 'd': + discovery_host = optarg; + break; + case 'n': + nickname = optarg; + break; + case 'h': + host = optarg; + break; + case 't': + target = optarg; + break; + case 'u': + user = optarg; + break; + case 's': + secret = optarg; + break; + case 'v': + vflag = 1; + break; + case '?': + default: + usage(); + } + } + argc -= optind; + if (argc != 0) + usage(); + + if (Aflag + Rflag + Lflag == 0) + Lflag = 1; + if (Aflag + Rflag + Lflag > 1) + errx(1, "at most one of -A, -R, or -L may be specified"); + + /* + * Note that we ignore unneccessary/inapplicable "-c" flag; so that + * people can do something like "alias ISCSICTL="iscsictl -c path" + * in shell scripts. + */ + if (Aflag != 0) { + if (aflag != 0) { + if (host != NULL) + errx(1, "-a and -h and mutually exclusive"); + if (target != NULL) + errx(1, "-a and -t and mutually exclusive"); + if (user != NULL) + errx(1, "-a and -u and mutually exclusive"); + if (secret != NULL) + errx(1, "-a and -s and mutually exclusive"); + if (nickname != NULL) + errx(1, "-a and -n and mutually exclusive"); + if (discovery_host != NULL) + errx(1, "-a and -d and mutually exclusive"); + } else if (nickname != NULL) { + if (host != NULL) + errx(1, "-n and -h and mutually exclusive"); + if (target != NULL) + errx(1, "-n and -t and mutually exclusive"); + if (user != NULL) + errx(1, "-n and -u and mutually exclusive"); + if (secret != NULL) + errx(1, "-n and -s and mutually exclusive"); + if (discovery_host != NULL) + errx(1, "-n and -d and mutually exclusive"); + } else if (discovery_host != NULL) { + if (host != NULL) + errx(1, "-d and -h and mutually exclusive"); + if (target != NULL) + errx(1, "-d and -t and mutually exclusive"); + } else { + if (target == NULL && host == NULL) + errx(1, "must specify -a, -n or -t/-h"); + + if (target != NULL && host == NULL) + errx(1, "-t must always be used with -h"); + if (host != NULL && target == NULL) + errx(1, "-h must always be used with -t"); + } + + if (user != NULL && secret == NULL) + errx(1, "-u must always be used with -s"); + if (secret != NULL && user == NULL) + errx(1, "-s must always be used with -u"); + + if (vflag != 0) + errx(1, "-v cannot be used with -A"); + + } else if (Rflag != 0) { + if (user != NULL) + errx(1, "-R and -u are mutually exclusive"); + if (secret != NULL) + errx(1, "-R and -s are mutually exclusive"); + if (discovery_host != NULL) + errx(1, "-R and -d are mutually exclusive"); + + if (aflag != 0) { + if (host != NULL) + errx(1, "-a and -h and mutually exclusive"); + if (target != NULL) + errx(1, "-a and -t and mutually exclusive"); + if (nickname != NULL) + errx(1, "-a and -n and mutually exclusive"); + } else if (nickname != NULL) { + if (host != NULL) + errx(1, "-n and -h and mutually exclusive"); + if (target != NULL) + errx(1, "-n and -t and mutually exclusive"); + } else if (host != NULL) { + if (target != NULL) + errx(1, "-h and -t and mutually exclusive"); + } else if (target != NULL) { + if (host != NULL) + errx(1, "-t and -h and mutually exclusive"); + } else + errx(1, "must specify either-a, -n, -t, or -h"); + + if (vflag != 0) + errx(1, "-v cannot be used with -R"); + + } else { + assert(Lflag != 0); + + if (host != NULL) + errx(1, "-L and -h and mutually exclusive"); + if (target != NULL) + errx(1, "-L and -t and mutually exclusive"); + if (user != NULL) + errx(1, "-L and -u and mutually exclusive"); + if (secret != NULL) + errx(1, "-L and -s and mutually exclusive"); + if (nickname != NULL) + errx(1, "-L and -n and mutually exclusive"); + if (discovery_host != NULL) + errx(1, "-L and -d and mutually exclusive"); + } + + iscsi_fd = open(ISCSI_PATH, O_RDWR); + if (iscsi_fd < 0 && errno == ENOENT) { + saved_errno = errno; + retval = kldload("iscsi"); + if (retval != -1) + iscsi_fd = open(ISCSI_PATH, O_RDWR); + else + errno = saved_errno; + } + if (iscsi_fd < 0) + err(1, "failed to open %s", ISCSI_PATH); + + if (Aflag != 0 && aflag != 0) { + conf = conf_new_from_file(conf_path); + + TAILQ_FOREACH(targ, &conf->conf_targets, t_next) + failed += kernel_add(iscsi_fd, targ); + } else if (nickname != NULL) { + conf = conf_new_from_file(conf_path); + targ = target_find(conf, nickname); + if (targ == NULL) + errx(1, "target %s not found in the configuration file", + nickname); + + if (Aflag != 0) + failed += kernel_add(iscsi_fd, targ); + else if (Rflag != 0) + failed += kernel_remove(iscsi_fd, targ); + else + failed += kernel_list(iscsi_fd, targ, vflag); + } else { + if (Aflag != 0 && target != NULL) { + if (valid_iscsi_name(target) == false) + errx(1, "invalid target name \"%s\"", target); + } + conf = conf_new(); + targ = target_new(conf); + targ->t_initiator_name = default_initiator_name(); + targ->t_header_digest = DIGEST_NONE; + targ->t_data_digest = DIGEST_NONE; + targ->t_name = target; + if (discovery_host != NULL) { + targ->t_session_type = SESSION_TYPE_DISCOVERY; + targ->t_address = discovery_host; + } else { + targ->t_session_type = SESSION_TYPE_NORMAL; + targ->t_address = host; + } + targ->t_user = user; + targ->t_secret = secret; + + if (Aflag != 0) + failed += kernel_add(iscsi_fd, targ); + else if (Rflag != 0) + failed += kernel_remove(iscsi_fd, targ); + else + failed += kernel_list(iscsi_fd, targ, vflag); + } + + error = close(iscsi_fd); + if (error != 0) + err(1, "close"); + + if (failed > 0) + return (1); + return (0); +} diff --git a/usr.bin/iscsictl/iscsictl.h b/usr.bin/iscsictl/iscsictl.h new file mode 100644 index 00000000000..e8d4768a88f --- /dev/null +++ b/usr.bin/iscsictl/iscsictl.h @@ -0,0 +1,116 @@ +/*- + * Copyright (c) 2012 The FreeBSD Foundation + * All rights reserved. + * + * This software was developed by Edward Tomasz Napierala under sponsorship + * from the FreeBSD Foundation. + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + * $FreeBSD$ + */ + +#ifndef ISCSICTL_H +#define ISCSICTL_H + +#include +#include +#include + +#define DEFAULT_CONFIG_PATH "/etc/iscsi.conf" +#define DEFAULT_IQN "iqn.1994-09.org.freebsd:" + +#define MAX_NAME_LEN 223 +#define MAX_DATA_SEGMENT_LENGTH 65536 + +#define AUTH_METHOD_UNSPECIFIED 0 +#define AUTH_METHOD_NONE 1 +#define AUTH_METHOD_CHAP 2 + +#define DIGEST_UNSPECIFIED 0 +#define DIGEST_NONE 1 +#define DIGEST_CRC32C 2 + +#define SESSION_TYPE_UNSPECIFIED 0 +#define SESSION_TYPE_NORMAL 1 +#define SESSION_TYPE_DISCOVERY 2 + +#define PROTOCOL_UNSPECIFIED 0 +#define PROTOCOL_ISCSI 1 +#define PROTOCOL_ISER 2 + +struct target { + TAILQ_ENTRY(target) t_next; + struct conf *t_conf; + char *t_nickname; + char *t_name; + char *t_address; + char *t_initiator_name; + char *t_initiator_address; + char *t_initiator_alias; + int t_header_digest; + int t_data_digest; + int t_auth_method; + int t_session_type; + int t_protocol; + char *t_user; + char *t_secret; + char *t_mutual_user; + char *t_mutual_secret; +}; + +struct conf { + TAILQ_HEAD(, target) conf_targets; +}; + +#define CONN_SESSION_TYPE_NONE 0 +#define CONN_SESSION_TYPE_DISCOVERY 1 +#define CONN_SESSION_TYPE_NORMAL 2 + +struct connection { + struct target *conn_target; + int conn_socket; + int conn_session_type; + uint32_t conn_cmdsn; + uint32_t conn_statsn; + size_t conn_max_data_segment_length; + size_t conn_max_burst_length; + size_t conn_max_outstanding_r2t; + int conn_header_digest; + int conn_data_digest; +}; + +struct conf *conf_new(void); +struct conf *conf_new_from_file(const char *path); +void conf_delete(struct conf *conf); +void conf_verify(struct conf *conf); + +struct target *target_new(struct conf *conf); +struct target *target_find(struct conf *conf, const char *nickname); +void target_delete(struct target *ic); + +void print_periphs(int session_id); + +char *checked_strdup(const char *); +bool valid_iscsi_name(const char *name); + +#endif /* !ISCSICTL_H */ diff --git a/usr.bin/iscsictl/parse.y b/usr.bin/iscsictl/parse.y new file mode 100644 index 00000000000..b44fc3bb35f --- /dev/null +++ b/usr.bin/iscsictl/parse.y @@ -0,0 +1,333 @@ +%{ +/*- + * Copyright (c) 2012 The FreeBSD Foundation + * All rights reserved. + * + * This software was developed by Edward Tomasz Napierala under sponsorship + * from the FreeBSD Foundation. + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + * $FreeBSD$ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "iscsictl.h" + +extern FILE *yyin; +extern char *yytext; +extern int lineno; + +static struct conf *conf; +static struct target *target; + +extern void yyerror(const char *); +extern int yylex(void); +extern void yyrestart(FILE *); + +%} + +%token AUTH_METHOD HEADER_DIGEST DATA_DIGEST TARGET_NAME TARGET_ADDRESS +%token INITIATOR_NAME INITIATOR_ADDRESS INITIATOR_ALIAS USER SECRET +%token MUTUAL_USER MUTUAL_SECRET SESSION_TYPE PROTOCOL IGNORED +%token EQUALS OPENING_BRACKET CLOSING_BRACKET + +%union +{ + char *str; +} + +%token STR + +%% + +statements: + | + statements target_statement + ; + +target_statement: STR OPENING_BRACKET target_entries CLOSING_BRACKET + { + if (target_find(conf, $1) != NULL) + errx(1, "duplicated target %s", $1); + target->t_nickname = $1; + target = target_new(conf); + } + ; + +target_entries: + | + target_entries target_entry + ; + +target_entry: + target_name_statement + | + target_address_statement + | + initiator_name_statement + | + initiator_address_statement + | + initiator_alias_statement + | + user_statement + | + secret_statement + | + mutual_user_statement + | + mutual_secret_statement + | + auth_method_statement + | + header_digest_statement + | + data_digest_statement + | + session_type_statement + | + protocol_statement + | + ignored_statement + ; + +target_name_statement: TARGET_NAME EQUALS STR + { + if (target->t_name != NULL) + errx(1, "duplicated TargetName at line %d", lineno + 1); + target->t_name = $3; + } + ; + +target_address_statement: TARGET_ADDRESS EQUALS STR + { + if (target->t_address != NULL) + errx(1, "duplicated TargetAddress at line %d", lineno + 1); + target->t_address = $3; + } + ; + +initiator_name_statement: INITIATOR_NAME EQUALS STR + { + if (target->t_initiator_name != NULL) + errx(1, "duplicated InitiatorName at line %d", lineno + 1); + target->t_initiator_name = $3; + } + ; + +initiator_address_statement: INITIATOR_ADDRESS EQUALS STR + { + if (target->t_initiator_address != NULL) + errx(1, "duplicated InitiatorAddress at line %d", lineno + 1); + target->t_initiator_address = $3; + } + ; + +initiator_alias_statement: INITIATOR_ALIAS EQUALS STR + { + if (target->t_initiator_alias != NULL) + errx(1, "duplicated InitiatorAlias at line %d", lineno + 1); + target->t_initiator_alias = $3; + } + ; + +user_statement: USER EQUALS STR + { + if (target->t_user != NULL) + errx(1, "duplicated chapIName at line %d", lineno + 1); + target->t_user = $3; + } + ; + +secret_statement: SECRET EQUALS STR + { + if (target->t_secret != NULL) + errx(1, "duplicated chapSecret at line %d", lineno + 1); + target->t_secret = $3; + } + ; + +mutual_user_statement: MUTUAL_USER EQUALS STR + { + if (target->t_mutual_user != NULL) + errx(1, "duplicated tgtChapName at line %d", lineno + 1); + target->t_mutual_user = $3; + } + ; + +mutual_secret_statement:MUTUAL_SECRET EQUALS STR + { + if (target->t_mutual_secret != NULL) + errx(1, "duplicated tgtChapSecret at line %d", lineno + 1); + target->t_mutual_secret = $3; + } + ; + +auth_method_statement: AUTH_METHOD EQUALS STR + { + if (target->t_auth_method != AUTH_METHOD_UNSPECIFIED) + errx(1, "duplicated AuthMethod at line %d", lineno + 1); + if (strcasecmp($3, "none") == 0) + target->t_auth_method = AUTH_METHOD_NONE; + else if (strcasecmp($3, "chap") == 0) + target->t_auth_method = AUTH_METHOD_CHAP; + else + errx(1, "invalid AuthMethod at line %d; " + "must be either \"none\" or \"CHAP\"", lineno + 1); + } + ; + +header_digest_statement: HEADER_DIGEST EQUALS STR + { + if (target->t_header_digest != DIGEST_UNSPECIFIED) + errx(1, "duplicated HeaderDigest at line %d", lineno + 1); + if (strcasecmp($3, "none") == 0) + target->t_header_digest = DIGEST_NONE; + else if (strcasecmp($3, "CRC32C") == 0) + target->t_header_digest = DIGEST_CRC32C; + else + errx(1, "invalid HeaderDigest at line %d; " + "must be either \"none\" or \"CRC32C\"", lineno + 1); + } + ; + +data_digest_statement: DATA_DIGEST EQUALS STR + { + if (target->t_data_digest != DIGEST_UNSPECIFIED) + errx(1, "duplicated DataDigest at line %d", lineno + 1); + if (strcasecmp($3, "none") == 0) + target->t_data_digest = DIGEST_NONE; + else if (strcasecmp($3, "CRC32C") == 0) + target->t_data_digest = DIGEST_CRC32C; + else + errx(1, "invalid DataDigest at line %d; " + "must be either \"none\" or \"CRC32C\"", lineno + 1); + } + ; + +session_type_statement: SESSION_TYPE EQUALS STR + { + if (target->t_session_type != SESSION_TYPE_UNSPECIFIED) + errx(1, "duplicated SessionType at line %d", lineno + 1); + if (strcasecmp($3, "normal") == 0) + target->t_session_type = SESSION_TYPE_NORMAL; + else if (strcasecmp($3, "discovery") == 0) + target->t_session_type = SESSION_TYPE_DISCOVERY; + else + errx(1, "invalid SessionType at line %d; " + "must be either \"normal\" or \"discovery\"", lineno + 1); + } + ; + +protocol_statement: PROTOCOL EQUALS STR + { + if (target->t_protocol != PROTOCOL_UNSPECIFIED) + errx(1, "duplicated protocol at line %d", lineno + 1); + if (strcasecmp($3, "iscsi") == 0) + target->t_protocol = PROTOCOL_ISCSI; + else if (strcasecmp($3, "iser") == 0) + target->t_protocol = PROTOCOL_ISER; + else + errx(1, "invalid protocol at line %d; " + "must be either \"iscsi\" or \"iser\"", lineno + 1); + } + ; + +ignored_statement: IGNORED EQUALS STR + { + warnx("obsolete statement ignored at line %d", lineno + 1); + } + ; + +%% + +void +yyerror(const char *str) +{ + + errx(1, "error in configuration file at line %d near '%s': %s", + lineno + 1, yytext, str); +} + +static void +check_perms(const char *path) +{ + struct stat sb; + int error; + + error = stat(path, &sb); + if (error != 0) { + warn("stat"); + return; + } + if (sb.st_mode & S_IWOTH) { + warnx("%s is world-writable", path); + } else if (sb.st_mode & S_IROTH) { + warnx("%s is world-readable", path); + } else if (sb.st_mode & S_IXOTH) { + /* + * Ok, this one doesn't matter, but still do it, + * just for consistency. + */ + warnx("%s is world-executable", path); + } + + /* + * XXX: Should we also check for owner != 0? + */ +} + +struct conf * +conf_new_from_file(const char *path) +{ + int error; + + conf = conf_new(); + target = target_new(conf); + + yyin = fopen(path, "r"); + if (yyin == NULL) + err(1, "unable to open configuration file %s", path); + check_perms(path); + lineno = 0; + yyrestart(yyin); + error = yyparse(); + assert(error == 0); + fclose(yyin); + + assert(target->t_nickname == NULL); + target_delete(target); + + conf_verify(conf); + + return (conf); +} diff --git a/usr.bin/iscsictl/periphs.c b/usr.bin/iscsictl/periphs.c new file mode 100644 index 00000000000..78b237f4aea --- /dev/null +++ b/usr.bin/iscsictl/periphs.c @@ -0,0 +1,186 @@ +/* + * Copyright (c) 1997-2007 Kenneth D. Merry + * Copyright (c) 2012 The FreeBSD Foundation + * All rights reserved. + * + * Portions of this software were developed by Edward Tomasz Napierala + * under sponsorship from the FreeBSD Foundation. + * + * 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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 +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "iscsictl.h" + +void +print_periphs(int session_id) +{ + union ccb ccb; + int bufsize, fd; + unsigned int i; + int skip_bus, skip_device; + + if ((fd = open(XPT_DEVICE, O_RDWR)) == -1) { + warn("couldn't open %s", XPT_DEVICE); + return; + } + + /* + * First, iterate over the whole list to find the bus. + */ + + bzero(&ccb, sizeof(union ccb)); + + ccb.ccb_h.path_id = CAM_XPT_PATH_ID; + ccb.ccb_h.target_id = CAM_TARGET_WILDCARD; + ccb.ccb_h.target_lun = CAM_LUN_WILDCARD; + + ccb.ccb_h.func_code = XPT_DEV_MATCH; + bufsize = sizeof(struct dev_match_result) * 100; + ccb.cdm.match_buf_len = bufsize; + ccb.cdm.matches = (struct dev_match_result *)malloc(bufsize); + if (ccb.cdm.matches == NULL) { + warnx("can't malloc memory for matches"); + close(fd); + return; + } + ccb.cdm.num_matches = 0; + + /* + * We fetch all nodes, since we display most of them in the default + * case, and all in the verbose case. + */ + ccb.cdm.num_patterns = 0; + ccb.cdm.pattern_buf_len = 0; + + /* + * We do the ioctl multiple times if necessary, in case there are + * more than 100 nodes in the EDT. + */ + do { + if (ioctl(fd, CAMIOCOMMAND, &ccb) == -1) { + warn("error sending CAMIOCOMMAND ioctl"); + break; + } + + if ((ccb.ccb_h.status != CAM_REQ_CMP) + || ((ccb.cdm.status != CAM_DEV_MATCH_LAST) + && (ccb.cdm.status != CAM_DEV_MATCH_MORE))) { + warnx("got CAM error %#x, CDM error %d\n", + ccb.ccb_h.status, ccb.cdm.status); + break; + } + + skip_bus = 1; + skip_device = 1; + + for (i = 0; i < ccb.cdm.num_matches; i++) { + switch (ccb.cdm.matches[i].type) { + case DEV_MATCH_BUS: { + struct bus_match_result *bus_result; + + bus_result = &ccb.cdm.matches[i].result.bus_result; + + skip_bus = 1; + + if (strcmp(bus_result->dev_name, "iscsi") != 0) { + //printf("not iscsi\n"); + continue; + } + + if ((int)bus_result->unit_number != session_id) { + //printf("wrong unit, %d != %d\n", bus_result->unit_number, session_id); + continue; + } + + skip_bus = 0; + } + case DEV_MATCH_DEVICE: { + skip_device = 1; + + if (skip_bus != 0) + continue; + + skip_device = 0; + + break; + } + case DEV_MATCH_PERIPH: { + struct periph_match_result *periph_result; + + periph_result = + &ccb.cdm.matches[i].result.periph_result; + + if (skip_device != 0) + continue; + + if (strcmp(periph_result->periph_name, "pass") == 0) + continue; + + fprintf(stdout, "%s%d ", + periph_result->periph_name, + periph_result->unit_number); + + break; + } + default: + fprintf(stdout, "unknown match type\n"); + break; + } + } + + } while ((ccb.ccb_h.status == CAM_REQ_CMP) + && (ccb.cdm.status == CAM_DEV_MATCH_MORE)); + + close(fd); +} + diff --git a/usr.bin/iscsictl/token.l b/usr.bin/iscsictl/token.l new file mode 100644 index 00000000000..f6c03aecc75 --- /dev/null +++ b/usr.bin/iscsictl/token.l @@ -0,0 +1,93 @@ +%{ +/*- + * Copyright (c) 2012 The FreeBSD Foundation + * All rights reserved. + * + * This software was developed by Edward Tomasz Napierala under sponsorship + * from the FreeBSD Foundation. + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + * $FreeBSD$ + */ + +#include +#include +#include + +#include "iscsictl.h" +#include "y.tab.h" + +int lineno; + +#define YY_DECL int yylex(void) +extern int yylex(void); + +%} + +%option noinput +%option nounput + +%% +HeaderDigest { return HEADER_DIGEST; } +DataDigest { return DATA_DIGEST; } +TargetName { return TARGET_NAME; } +TargetAddress { return TARGET_ADDRESS; } +InitiatorName { return INITIATOR_NAME; } +InitiatorAddress { return INITIATOR_ADDRESS; } +InitiatorAlias { return INITIATOR_ALIAS; } +chapIName { return USER; } +chapSecret { return SECRET; } +tgtChapName { return MUTUAL_USER; } +tgtChapSecret { return MUTUAL_SECRET; } +AuthMethod { return AUTH_METHOD; } +SessionType { return SESSION_TYPE; } +protocol { return PROTOCOL; } +port { return IGNORED; } +MaxConnections { return IGNORED; } +TargetAlias { return IGNORED; } +TargetPortalGroupTag { return IGNORED; } +InitialR2T { return IGNORED; } +ImmediateData { return IGNORED; } +MaxRecvDataSegmentLength { return IGNORED; } +MaxBurstLength { return IGNORED; } +FirstBurstLength { return IGNORED; } +DefaultTime2Wait { return IGNORED; } +DefaultTime2Retain { return IGNORED; } +MaxOutstandingR2T { return IGNORED; } +DataPDUInOrder { return IGNORED; } +DataSequenceInOrder { return IGNORED; } +ErrorRecoveryLevel { return IGNORED; } +tags { return IGNORED; } +maxluns { return IGNORED; } +sockbufsize { return IGNORED; } +chapDigest { return IGNORED; } +\"[^"]+\" { yylval.str = strndup(yytext + 1, + strlen(yytext) - 2); return STR; } +[a-zA-Z0-9\.\-_/\:\[\]]+ { yylval.str = strdup(yytext); return STR; } +\{ { return OPENING_BRACKET; } +\} { return CLOSING_BRACKET; } += { return EQUALS; } +#.*$ /* ignore comments */; +\n { lineno++; } +[ \t]+ /* ignore whitespace */; +%% diff --git a/usr.bin/kdump/kdump.c b/usr.bin/kdump/kdump.c index 0f1a26b6753..c49b45b6cc1 100644 --- a/usr.bin/kdump/kdump.c +++ b/usr.bin/kdump/kdump.c @@ -59,6 +59,7 @@ extern int errno; #include #include #include +#include #ifdef IPX #include #include @@ -103,6 +104,7 @@ void ktrcsw_old(struct ktr_csw_old *); void ktruser_malloc(unsigned char *); void ktruser_rtld(int, unsigned char *); void ktruser(int, unsigned char *); +void ktrcaprights(cap_rights_t *); void ktrsockaddr(struct sockaddr *); void ktrstat(struct stat *); void ktrstruct(char *, size_t); @@ -379,21 +381,21 @@ limitfd(int fd) cap_rights_t rights; unsigned long cmd; - rights = CAP_FSTAT; + cap_rights_init(&rights, CAP_FSTAT); cmd = -1; switch (fd) { case STDIN_FILENO: - rights |= CAP_READ; + cap_rights_set(&rights, CAP_READ); break; case STDOUT_FILENO: - rights |= CAP_IOCTL | CAP_WRITE; + cap_rights_set(&rights, CAP_IOCTL, CAP_WRITE); cmd = TIOCGETA; /* required by isatty(3) in printf(3) */ break; case STDERR_FILENO: - rights |= CAP_WRITE; + cap_rights_set(&rights, CAP_WRITE); if (!suppressdata) { - rights |= CAP_IOCTL; + cap_rights_set(&rights, CAP_IOCTL); cmd = TIOCGWINSZ; } break; @@ -401,7 +403,7 @@ limitfd(int fd) abort(); } - if (cap_rights_limit(fd, rights) < 0 && errno != ENOSYS) + if (cap_rights_limit(fd, &rights) < 0 && errno != ENOSYS) err(1, "unable to limit rights for descriptor %d", fd); if (cmd != -1 && cap_ioctls_limit(fd, &cmd, 1) < 0 && errno != ENOSYS) err(1, "unable to limit ioctls for descriptor %d", fd); @@ -605,6 +607,29 @@ ktrsyscall(struct ktr_syscall *ktr, u_int flags) char c = '('; if (fancy && (flags == 0 || (flags & SV_ABI_MASK) == SV_ABI_FREEBSD)) { + switch (ktr->ktr_code) { + case SYS_bindat: + case SYS_connectat: + case SYS_faccessat: + case SYS_fchmodat: + case SYS_fchownat: + case SYS_fstatat: + case SYS_futimesat: + case SYS_linkat: + case SYS_mkdirat: + case SYS_mkfifoat: + case SYS_mknodat: + case SYS_openat: + case SYS_readlinkat: + case SYS_renameat: + case SYS_unlinkat: + putchar('('); + atfdname(*ip, decimal); + c = ','; + ip++; + narg--; + break; + } switch (ktr->ktr_code) { case SYS_ioctl: { print_number(ip, narg, c); @@ -624,6 +649,7 @@ ktrsyscall(struct ktr_syscall *ktr, u_int flags) break; case SYS_access: case SYS_eaccess: + case SYS_faccessat: print_number(ip, narg, c); putchar(','); accessmodename(*ip); @@ -631,6 +657,7 @@ ktrsyscall(struct ktr_syscall *ktr, u_int flags) narg--; break; case SYS_open: + case SYS_openat: print_number(ip, narg, c); putchar(','); flagsandmodename(ip[0], ip[1], decimal); @@ -638,10 +665,32 @@ ktrsyscall(struct ktr_syscall *ktr, u_int flags) narg -= 2; break; case SYS_wait4: + print_number(ip, narg, c); + print_number(ip, narg, c); + /* + * A flags value of zero is valid for + * wait4() but not for wait6(), so + * handle zero special here. + */ + if (*ip == 0) { + print_number(ip, narg, c); + } else { + putchar(','); + wait6optname(*ip); + ip++; + narg--; + } + break; + case SYS_wait6: + putchar('('); + idtypename(*ip, decimal); + c = ','; + ip++; + narg--; print_number(ip, narg, c); print_number(ip, narg, c); putchar(','); - wait4optname(*ip); + wait6optname(*ip); ip++; narg--; break; @@ -655,6 +704,7 @@ ktrsyscall(struct ktr_syscall *ktr, u_int flags) narg--; break; case SYS_mknod: + case SYS_mknodat: print_number(ip, narg, c); putchar(','); modename(*ip); @@ -804,7 +854,7 @@ ktrsyscall(struct ktr_syscall *ktr, u_int flags) ip++; narg--; putchar(','); - socktypename(*ip); + socktypenamewithflags(*ip); ip++; narg--; if (sockdomain == PF_INET || @@ -860,7 +910,9 @@ ktrsyscall(struct ktr_syscall *ktr, u_int flags) narg--; break; case SYS_mkfifo: + case SYS_mkfifoat: case SYS_mkdir: + case SYS_mkdirat: print_number(ip, narg, c); putchar(','); modename(*ip); @@ -880,7 +932,7 @@ ktrsyscall(struct ktr_syscall *ktr, u_int flags) ip++; narg--; putchar(','); - socktypename(*ip); + socktypenamewithflags(*ip); ip++; narg--; c = ','; @@ -1083,34 +1135,14 @@ ktrsyscall(struct ktr_syscall *ktr, u_int flags) ip++; narg--; break; - case SYS_cap_new: - case SYS_cap_rights_limit: + case SYS_linkat: + case SYS_renameat: + case SYS_symlinkat: print_number(ip, narg, c); putchar(','); - arg = *ip; + atfdname(*ip, decimal); ip++; narg--; - /* - * Hack: the second argument is a - * cap_rights_t, which 64 bits wide, so on - * 32-bit systems, it is split between two - * registers. - * - * Since sizeof() is not evaluated by the - * preprocessor, we can't use an #ifdef, - * but the compiler will probably optimize - * the code out anyway. - */ - if (sizeof(cap_rights_t) > sizeof(register_t)) { -#if _BYTE_ORDER == _LITTLE_ENDIAN - arg = ((intmax_t)*ip << 32) + arg; -#else - arg = (arg << 32) + *ip; -#endif - ip++; - narg--; - } - capname(arg); break; case SYS_cap_fcntls_limit: print_number(ip, narg, c); @@ -1498,6 +1530,15 @@ ktruser(int len, unsigned char *p) printf("\n"); } +void +ktrcaprights(cap_rights_t *rightsp) +{ + + printf("cap_rights_t "); + capname(rightsp); + printf("\n"); +} + void ktrsockaddr(struct sockaddr *sa) { @@ -1675,6 +1716,7 @@ ktrstruct(char *buf, size_t buflen) char *name, *data; size_t namelen, datalen; int i; + cap_rights_t rights; struct stat sb; struct sockaddr_storage ss; @@ -1694,7 +1736,12 @@ ktrstruct(char *buf, size_t buflen) for (i = 0; i < (int)namelen; ++i) if (!isalpha(name[i])) goto invalid; - if (strcmp(name, "stat") == 0) { + if (strcmp(name, "caprights") == 0) { + if (datalen != sizeof(cap_rights_t)) + goto invalid; + memcpy(&rights, data, datalen); + ktrcaprights(&rights); + } else if (strcmp(name, "stat") == 0) { if (datalen != sizeof(struct stat)) goto invalid; memcpy(&sb, data, datalen); @@ -1721,16 +1768,16 @@ ktrcapfail(struct ktr_cap_fail *ktr) case CAPFAIL_NOTCAPABLE: /* operation on fd with insufficient capabilities */ printf("operation requires "); - capname((intmax_t)ktr->cap_needed); + capname(&ktr->cap_needed); printf(", process holds "); - capname((intmax_t)ktr->cap_held); + capname(&ktr->cap_held); break; case CAPFAIL_INCREASE: /* requested more capabilities than fd already has */ printf("attempt to increase capabilities from "); - capname((intmax_t)ktr->cap_held); + capname(&ktr->cap_held); printf(" to "); - capname((intmax_t)ktr->cap_needed); + capname(&ktr->cap_needed); break; case CAPFAIL_SYSCALL: /* called restricted syscall */ @@ -1742,9 +1789,9 @@ ktrcapfail(struct ktr_cap_fail *ktr) break; default: printf("unknown capability failure: "); - capname((intmax_t)ktr->cap_needed); + capname(&ktr->cap_needed); printf(" "); - capname((intmax_t)ktr->cap_held); + capname(&ktr->cap_held); break; } printf("\n"); diff --git a/usr.bin/kdump/mksubr b/usr.bin/kdump/mksubr index aed8291ff8c..7fd42b7df2e 100644 --- a/usr.bin/kdump/mksubr +++ b/usr.bin/kdump/mksubr @@ -208,6 +208,18 @@ cat <<_EOF_ print_or(#flag,orflag); } \\ while (0) +/* MANUAL */ +void +atfdname(int fd, int decimal) +{ + if (fd == AT_FDCWD) + printf("AT_FDCWD"); + else if (decimal) + printf("%d", fd); + else + printf("%#x", fd); +} + /* MANUAL */ extern char *signames[]; /* from kdump.c */ void @@ -315,6 +327,68 @@ flagsandmodename(int flags, int mode, int decimal) } } +/* MANUAL */ +void +idtypename(idtype_t idtype, int decimal) +{ + switch(idtype) { + case P_PID: + printf("P_PID"); + break; + case P_PPID: + printf("P_PPID"); + break; + case P_PGID: + printf("P_PGID"); + break; + case P_SID: + printf("P_SID"); + break; + case P_CID: + printf("P_CID"); + break; + case P_UID: + printf("P_UID"); + break; + case P_GID: + printf("P_GID"); + break; + case P_ALL: + printf("P_ALL"); + break; + case P_LWPID: + printf("P_LWPID"); + break; + case P_TASKID: + printf("P_TASKID"); + break; + case P_PROJID: + printf("P_PROJID"); + break; + case P_POOLID: + printf("P_POOLID"); + break; + case P_JAILID: + printf("P_JAILID"); + break; + case P_CTID: + printf("P_CTID"); + break; + case P_CPUID: + printf("P_CPUID"); + break; + case P_PSETID: + printf("P_PSETID"); + break; + default: + if (decimal) { + printf("%d", idtype); + } else { + printf("%#x", idtype); + } + } +} + /* * MANUAL * @@ -356,11 +430,23 @@ vmprotname (int type) if_print_or(type, VM_PROT_EXECUTE, or); if_print_or(type, VM_PROT_COPY, or); } + +/* + * MANUAL + */ +void +socktypenamewithflags(int type) +{ + if (type & SOCK_CLOEXEC) + printf("SOCK_CLOEXEC|"), type &= ~SOCK_CLOEXEC; + if (type & SOCK_NONBLOCK) + printf("SOCK_NONBLOCK|"), type &= ~SOCK_NONBLOCK; + socktypename(type); +} _EOF_ auto_or_type "accessmodename" "[A-Z]_OK[[:space:]]+0?x?[0-9A-Fa-f]+" "sys/unistd.h" auto_switch_type "acltypename" "ACL_TYPE_[A-Z4_]+[[:space:]]+0x[0-9]+" "sys/acl.h" -auto_or_type "capname" "CAP_[A-Z]+[[:space:]]+0x[01248]{16}ULL" "sys/capability.h" auto_or_type "capfcntlname" "CAP_FCNTL_[A-Z]+[[:space:]]+\(1" "sys/capability.h" auto_switch_type "extattrctlname" "EXTATTR_NAMESPACE_[A-Z]+[[:space:]]+0x[0-9]+" "sys/extattr.h" auto_switch_type "fadvisebehavname" "POSIX_FADV_[A-Z]+[[:space:]]+[0-9]+" "sys/fcntl.h" @@ -373,7 +459,6 @@ auto_switch_type "lio_listioname" "LIO_(NO)?WAIT[[:space:]]+[0-9]+" auto_switch_type "madvisebehavname" "_?MADV_[A-Z]+[[:space:]]+[0-9]+" "sys/mman.h" auto_switch_type "minheritname" "INHERIT_[A-Z]+[[:space:]]+[0-9]+" "sys/mman.h" auto_or_type "mlockallname" "MCL_[A-Z]+[[:space:]]+0x[0-9]+" "sys/mman.h" -auto_or_type "mmapflagsname" "MAP_[A-Z]+[[:space:]]+0x[0-9A-Fa-f]+" "sys/mman.h" auto_or_type "mmapprotname" "PROT_[A-Z]+[[:space:]]+0x[0-9A-Fa-f]+" "sys/mman.h" auto_or_type "modename" "S_[A-Z]+[[:space:]]+[0-6]{7}" "sys/stat.h" auto_or_type "mountflagsname" "MNT_[A-Z]+[[:space:]]+0x[0-9]+" "sys/mount.h" @@ -403,7 +488,7 @@ auto_switch_type "sockoptname" "SO_[A-Z]+[[:space:]]+0x[0-9]+" auto_switch_type "socktypename" "SOCK_[A-Z]+[[:space:]]+[1-9]+[0-9]*" "sys/socket.h" auto_or_type "thrcreateflagsname" "THR_[A-Z]+[[:space:]]+0x[0-9]+" "sys/thr.h" auto_switch_type "vmresultname" "KERN_[A-Z]+[[:space:]]+[0-9]+" "vm/vm_param.h" -auto_or_type "wait4optname" "W[A-Z]+[[:space:]]+[0-9]+" "sys/wait.h" +auto_or_type "wait6optname" "W[A-Z]+[[:space:]]+[0-9]+" "sys/wait.h" auto_switch_type "whencename" "SEEK_[A-Z]+[[:space:]]+[0-9]+" "sys/unistd.h" cat <<_EOF_ @@ -454,6 +539,44 @@ cat <<_EOF_ } } +/* + * AUTO - Special + * + * The MAP_ALIGNED flag requires special handling. + */ +void +mmapflagsname(int flags) +{ + int align; + int or = 0; + printf("%#x<", flags); +_EOF_ +egrep "^#[[:space:]]*define[[:space:]]+MAP_[A-Z_]+[[:space:]]+0x[0-9A-Fa-f]+[[:space:]]*" \ + $include_dir/sys/mman.h | grep -v MAP_ALIGNED | \ + awk '{ for (i = 1; i <= NF; i++) \ + if ($i ~ /define/) \ + break; \ + ++i; \ + printf "\tif (!((flags > 0) ^ ((%s) > 0)))\n\t\tif_print_or(flags, %s, or);\n", $i, $i }' +cat <<_EOF_ +#ifdef MAP_32BIT + if (!((flags > 0) ^ ((MAP_32BIT) > 0))) + if_print_or(flags, MAP_32BIT, or); +#endif + align = flags & MAP_ALIGNMENT_MASK; + if (align != 0) { + if (align == MAP_ALIGNED_SUPER) + print_or("MAP_ALIGNED_SUPER", or); + else { + print_or("MAP_ALIGNED", or); + printf("(%d)", align >> MAP_ALIGNMENT_SHIFT); + } + } + printf(">"); + if (or == 0) + printf("%d", flags); +} + /* * AUTO - Special * @@ -551,3 +674,26 @@ cat <<_EOF_ } } } + +_EOF_ +egrep '#define[[:space:]]+CAP_[A-Z_]+[[:space:]]+CAPRIGHT\([0-9],[[:space:]]+0x[0-9]{16}ULL\)' \ + $include_dir/sys/capability.h | \ + sed -E 's/[ ]+/ /g' | \ + awk -F '[ \(,\)]' ' + BEGIN { + printf "void\n" + printf "capname(const cap_rights_t *rightsp)\n" + printf "{\n" + printf "\tint comma = 0;\n\n" + printf "\tprintf(\"<\");\n" + } + { + printf "\tif ((rightsp->cr_rights[%s] & %s) == %s) {\n", $4, $2, $2 + printf "\t\tif (comma) printf(\",\"); else comma = 1;\n" + printf "\t\tprintf(\"%s\");\n", $2 + printf "\t}\n" + } + END { + printf "\tprintf(\">\");\n" + printf "}\n" + }' diff --git a/usr.bin/ldd/ldd.c b/usr.bin/ldd/ldd.c index 00c87975d85..bc4dbb6204a 100644 --- a/usr.bin/ldd/ldd.c +++ b/usr.bin/ldd/ldd.c @@ -49,12 +49,6 @@ __FBSDID("$FreeBSD$"); #include "extern.h" -#ifdef COMPAT_32BIT -#define LD_ "LD_32_" -#else -#define LD_ "LD_" -#endif - /* * 32-bit ELF data structures can only be used if the system header[s] declare * them. There is no official macro for determining whether they are declared, @@ -64,6 +58,16 @@ __FBSDID("$FreeBSD$"); #define ELF32_SUPPORTED #endif +#define LDD_SETENV(name, value, overwrite) do { \ + setenv("LD_" name, value, overwrite); \ + setenv("LD_32_" name, value, overwrite); \ +} while (0) + +#define LDD_UNSETENV(name) do { \ + unsetenv("LD_" name); \ + unsetenv("LD_32_" name); \ +} while (0) + static int is_executable(const char *fname, int fd, int *is_shlib, int *type); static void usage(void); @@ -82,7 +86,7 @@ execldd32(char *file, char *fmt1, char *fmt2, int aflag, int vflag) char *argv[8]; int i, rval, status; - unsetenv(LD_ "TRACE_LOADED_OBJECTS"); + LDD_UNSETENV("TRACE_LOADED_OBJECTS"); rval = 0; i = 0; argv[i++] = strdup(_PATH_LDD32); @@ -121,7 +125,7 @@ execldd32(char *file, char *fmt1, char *fmt2, int aflag, int vflag) } while (i--) free(argv[i]); - setenv(LD_ "TRACE_LOADED_OBJECTS", "yes", 1); + LDD_SETENV("TRACE_LOADED_OBJECTS", "yes", 1); return (rval); } #endif @@ -210,15 +214,15 @@ main(int argc, char *argv[]) } /* ld.so magic */ - setenv(LD_ "TRACE_LOADED_OBJECTS", "yes", 1); + LDD_SETENV("TRACE_LOADED_OBJECTS", "yes", 1); if (fmt1 != NULL) - setenv(LD_ "TRACE_LOADED_OBJECTS_FMT1", fmt1, 1); + LDD_SETENV("TRACE_LOADED_OBJECTS_FMT1", fmt1, 1); if (fmt2 != NULL) - setenv(LD_ "TRACE_LOADED_OBJECTS_FMT2", fmt2, 1); + LDD_SETENV("TRACE_LOADED_OBJECTS_FMT2", fmt2, 1); - setenv(LD_ "TRACE_LOADED_OBJECTS_PROGNAME", *argv, 1); + LDD_SETENV("TRACE_LOADED_OBJECTS_PROGNAME", *argv, 1); if (aflag) - setenv(LD_ "TRACE_LOADED_OBJECTS_ALL", "1", 1); + LDD_SETENV("TRACE_LOADED_OBJECTS_ALL", "1", 1); else if (fmt1 == NULL && fmt2 == NULL) /* Default formats */ printf("%s:\n", *argv); diff --git a/usr.bin/netstat/inet6.c b/usr.bin/netstat/inet6.c index a3bbc311c96..78383ac6a61 100644 --- a/usr.bin/netstat/inet6.c +++ b/usr.bin/netstat/inet6.c @@ -1120,12 +1120,17 @@ inet6print(struct in6_addr *in6, int port, const char *proto, int numeric) char * inet6name(struct in6_addr *in6p) { - char *cp; + struct sockaddr_in6 sin6; + char hbuf[NI_MAXHOST], *cp; static char line[50]; - struct hostent *hp; static char domain[MAXHOSTNAMELEN]; static int first = 1; + int flags, error; + if (IN6_IS_ADDR_UNSPECIFIED(in6p)) { + strcpy(line, "*"); + return (line); + } if (first && !numeric_addr) { first = 0; if (gethostname(domain, MAXHOSTNAMELEN) == 0 && @@ -1134,24 +1139,26 @@ inet6name(struct in6_addr *in6p) else domain[0] = 0; } - cp = 0; - if (!numeric_addr && !IN6_IS_ADDR_UNSPECIFIED(in6p)) { - hp = gethostbyaddr((char *)in6p, sizeof(*in6p), AF_INET6); - if (hp) { - if ((cp = strchr(hp->h_name, '.')) && - !strcmp(cp + 1, domain)) - *cp = 0; - cp = hp->h_name; - } - } - if (IN6_IS_ADDR_UNSPECIFIED(in6p)) - strcpy(line, "*"); - else if (cp) - strcpy(line, cp); - else + memset(&sin6, 0, sizeof(sin6)); + memcpy(&sin6.sin6_addr, in6p, sizeof(*in6p)); + sin6.sin6_family = AF_INET6; + /* XXX: in6p.s6_addr[2] can contain scopeid. */ + in6_fillscopeid(&sin6); + flags = (numeric_addr) ? NI_NUMERICHOST : 0; + error = getnameinfo((struct sockaddr *)&sin6, sizeof(sin6), hbuf, + sizeof(hbuf), NULL, 0, flags); + if (error == 0) { + if ((flags & NI_NUMERICHOST) == 0 && + (cp = strchr(hbuf, '.')) && + !strcmp(cp + 1, domain)) + *cp = 0; + strcpy(line, hbuf); + } else { + /* XXX: this should not happen. */ sprintf(line, "%s", - inet_ntop(AF_INET6, (void *)in6p, ntop_buf, + inet_ntop(AF_INET6, (void *)&sin6.sin6_addr, ntop_buf, sizeof(ntop_buf))); + } return (line); } #endif /*INET6*/ diff --git a/usr.bin/nslookup/Makefile b/usr.bin/nslookup/Makefile index b3ba117cf8a..4ed0e92584b 100644 --- a/usr.bin/nslookup/Makefile +++ b/usr.bin/nslookup/Makefile @@ -12,13 +12,13 @@ PROG= nslookup .PATH: ${SRCDIR} SRCS+= dighost.c nslookup.c -CFLAGS+= -I${SRCDIR}/include +CFLAGS+= -I${SRCDIR}/include -ledit CFLAGS+= -I${BIND_DIR}/lib/isc/${ISC_ATOMIC_ARCH}/include -DPADD+= ${BIND_DPADD} ${CRYPTO_DPADD} ${PTHREAD_DPADD} -LDADD+= ${BIND_LDADD} ${CRYPTO_LDADD} ${PTHREAD_LDADD} +DPADD+= ${BIND_DPADD} ${CRYPTO_DPADD} ${PTHREAD_DPADD} ${LIBEDIT} +LDADD+= ${BIND_LDADD} ${CRYPTO_LDADD} ${PTHREAD_LDADD} ${LIBEDIT} -WARNS?= 1 +WARNS?= 0 MANFILTER= sed -e "s@^host \[server\]@\\\fBhost\\\fR \\\fI[server]\\\fR@" diff --git a/usr.bin/nsupdate/Makefile b/usr.bin/nsupdate/Makefile index d5cb8f02c61..593d58923f6 100644 --- a/usr.bin/nsupdate/Makefile +++ b/usr.bin/nsupdate/Makefile @@ -12,12 +12,12 @@ PROG= nsupdate .PATH: ${SRCDIR} SRCS+= nsupdate.c -CFLAGS+= -I${SRCDIR}/include +CFLAGS+= -I${SRCDIR}/include -ledit CFLAGS+= -I${BIND_DIR}/lib/isc/${ISC_ATOMIC_ARCH}/include CFLAGS+= -DSESSION_KEYFILE=\"/var/run/named/session.key\" -DPADD+= ${BIND_DPADD} ${CRYPTO_DPADD} ${PTHREAD_DPADD} -LDADD+= ${BIND_LDADD} ${CRYPTO_LDADD} ${PTHREAD_LDADD} +DPADD+= ${BIND_DPADD} ${CRYPTO_DPADD} ${PTHREAD_DPADD} ${LIBEDIT} +LDADD+= ${BIND_LDADD} ${CRYPTO_LDADD} ${PTHREAD_LDADD} ${LIBEDIT} WARNS?= 1 diff --git a/usr.bin/patch/Makefile b/usr.bin/patch/Makefile index 18261f677df..a5f15c706a1 100644 --- a/usr.bin/patch/Makefile +++ b/usr.bin/patch/Makefile @@ -1,17 +1,7 @@ # $OpenBSD: Makefile,v 1.4 2005/05/16 15:22:46 espie Exp $ # $FreeBSD$ -.include - -.if ${MK_GNU_PATCH} == "yes" -PROG= bsdpatch -CLEANFILES+= bsdpatch.1 - -bsdpatch.1: patch.1 - cp ${.ALLSRC} ${.TARGET} -.else PROG= patch -.endif SRCS= backupfile.c inp.c mkpath.c patch.c pch.c util.c diff --git a/usr.bin/patch/pch.c b/usr.bin/patch/pch.c index c6224e7f234..eca12ad06f8 100644 --- a/usr.bin/patch/pch.c +++ b/usr.bin/patch/pch.c @@ -1516,15 +1516,12 @@ posix_name(const struct file_name *names, bool assume_exists) return path ? savestr(path) : NULL; } -/* - * Choose the name of the file to be patched based the "best" one - * available. - */ static char * -best_name(const struct file_name *names, bool assume_exists) +compare_names(const struct file_name *names, bool assume_exists, int phase) { size_t min_components, min_baselen, min_len, tmp; char *best = NULL; + char *path; int i; /* @@ -1536,47 +1533,43 @@ best_name(const struct file_name *names, bool assume_exists) */ min_components = min_baselen = min_len = SIZE_MAX; for (i = INDEX_FILE; i >= OLD_FILE; i--) { - if (names[i].path == NULL || - (!names[i].exists && !assume_exists)) + path = names[i].path; + if (path == NULL || + (phase == 1 && !names[i].exists && !assume_exists) || + (phase == 2 && checked_in(path) == NULL)) continue; - if ((tmp = num_components(names[i].path)) > min_components) + if ((tmp = num_components(path)) > min_components) continue; if (tmp < min_components) { min_components = tmp; - best = names[i].path; + best = path; } - if ((tmp = strlen(basename(names[i].path))) > min_baselen) + if ((tmp = strlen(basename(path))) > min_baselen) continue; if (tmp < min_baselen) { min_baselen = tmp; - best = names[i].path; + best = path; } - if ((tmp = strlen(names[i].path)) > min_len) + if ((tmp = strlen(path)) > min_len) continue; min_len = tmp; - best = names[i].path; + best = path; } + return best; +} + +/* + * Choose the name of the file to be patched based the "best" one + * available. + */ +static char * +best_name(const struct file_name *names, bool assume_exists) +{ + char *best; + + best = compare_names(names, assume_exists, 1); if (best == NULL) { - /* - * No files found, look for something we can checkout from - * RCS/SCCS dirs. Logic is identical to that above... - */ - min_components = min_baselen = min_len = SIZE_MAX; - for (i = INDEX_FILE; i >= OLD_FILE; i--) { - if (names[i].path == NULL || - checked_in(names[i].path) == NULL) - continue; - if ((tmp = num_components(names[i].path)) > min_components) - continue; - min_components = tmp; - if ((tmp = strlen(basename(names[i].path))) > min_baselen) - continue; - min_baselen = tmp; - if ((tmp = strlen(names[i].path)) > min_len) - continue; - min_len = tmp; - best = names[i].path; - } + best = compare_names(names, assume_exists, 2); /* * Still no match? Check to see if the diff could be creating * a new file. diff --git a/usr.bin/patch/util.c b/usr.bin/patch/util.c index 28a3cb0cf58..c19918b04e9 100644 --- a/usr.bin/patch/util.c +++ b/usr.bin/patch/util.c @@ -412,7 +412,7 @@ checked_in(char *file) void version(void) { - fprintf(stderr, "patch 2.0-12u8 FreeBSD\n"); + fprintf(stderr, "patch 2.0-12u9 FreeBSD\n"); my_exit(EXIT_SUCCESS); } diff --git a/usr.bin/procstat/procstat_files.c b/usr.bin/procstat/procstat_files.c index a3137db0852..d65c1ae9398 100644 --- a/usr.bin/procstat/procstat_files.c +++ b/usr.bin/procstat/procstat_files.c @@ -133,7 +133,7 @@ print_address(struct sockaddr_storage *ss) } static struct cap_desc { - cap_rights_t cd_right; + uint64_t cd_right; const char *cd_desc; } cap_desc[] = { /* General file I/O. */ @@ -244,14 +244,14 @@ static const u_int cap_desc_count = sizeof(cap_desc) / sizeof(cap_desc[0]); static u_int -width_capability(cap_rights_t rights) +width_capability(cap_rights_t *rightsp) { u_int count, i, width; count = 0; width = 0; for (i = 0; i < cap_desc_count; i++) { - if ((cap_desc[i].cd_right & ~rights) == 0) { + if (cap_rights_is_set(rightsp, cap_desc[i].cd_right)) { width += strlen(cap_desc[i].cd_desc); if (count) width++; @@ -262,20 +262,20 @@ width_capability(cap_rights_t rights) } static void -print_capability(cap_rights_t rights, u_int capwidth) +print_capability(cap_rights_t *rightsp, u_int capwidth) { u_int count, i, width; count = 0; width = 0; - for (i = width_capability(rights); i < capwidth; i++) { - if (rights || i != 0) + for (i = width_capability(rightsp); i < capwidth; i++) { + if (i != 0) printf(" "); else printf("-"); } for (i = 0; i < cap_desc_count; i++) { - if ((cap_desc[i].cd_right & ~rights) == 0) { + if (cap_rights_is_set(rightsp, cap_desc[i].cd_right)) { printf("%s%s", count ? "," : "", cap_desc[i].cd_desc); width += strlen(cap_desc[i].cd_desc); if (count) @@ -306,7 +306,7 @@ procstat_files(struct procstat *procstat, struct kinfo_proc *kipp) head = procstat_getfiles(procstat, kipp, 0); if (head != NULL && Cflag) { STAILQ_FOREACH(fst, head, next) { - width = width_capability(fst->fs_cap_rights); + width = width_capability(&fst->fs_cap_rights); if (width > capwidth) capwidth = width; } @@ -332,19 +332,19 @@ procstat_files(struct procstat *procstat, struct kinfo_proc *kipp) printf("%5d ", kipp->ki_pid); printf("%-16s ", kipp->ki_comm); if (fst->fs_uflags & PS_FST_UFLAG_CTTY) - printf("ctty "); + printf(" ctty "); else if (fst->fs_uflags & PS_FST_UFLAG_CDIR) - printf(" cwd "); + printf(" cwd "); else if (fst->fs_uflags & PS_FST_UFLAG_JAIL) - printf("jail "); + printf(" jail "); else if (fst->fs_uflags & PS_FST_UFLAG_RDIR) - printf("root "); + printf(" root "); else if (fst->fs_uflags & PS_FST_UFLAG_TEXT) - printf("text "); + printf(" text "); else if (fst->fs_uflags & PS_FST_UFLAG_TRACE) printf("trace "); else - printf("%4d ", fst->fs_fd); + printf("%5d ", fst->fs_fd); switch (fst->fs_type) { case PS_FST_TYPE_VNODE: @@ -460,7 +460,7 @@ procstat_files(struct procstat *procstat, struct kinfo_proc *kipp) printf("%7c ", '-'); } if (Cflag) { - print_capability(fst->fs_cap_rights, capwidth); + print_capability(&fst->fs_cap_rights, capwidth); printf(" "); } switch (fst->fs_type) { diff --git a/usr.bin/rwho/rwho.c b/usr.bin/rwho/rwho.c index bcb5adb8e91..8c985f0170d 100644 --- a/usr.bin/rwho/rwho.c +++ b/usr.bin/rwho/rwho.c @@ -93,6 +93,7 @@ main(int argc, char *argv[]) struct whod *w; struct whoent *we; struct myutmp *mp; + cap_rights_t rights; int f, n, i; int d_first; int dfd; @@ -124,7 +125,8 @@ main(int argc, char *argv[]) err(1, "opendir(%s)", _PATH_RWHODIR); dfd = dirfd(dirp); mp = myutmp; - if (cap_rights_limit(dfd, CAP_READ | CAP_LOOKUP) < 0 && errno != ENOSYS) + cap_rights_init(&rights, CAP_READ, CAP_LOOKUP); + if (cap_rights_limit(dfd, &rights) < 0 && errno != ENOSYS) err(1, "cap_rights_limit failed: %s", _PATH_RWHODIR); /* * Cache files required for time(3) and localtime(3) before entering @@ -135,13 +137,14 @@ main(int argc, char *argv[]) if (cap_enter() < 0 && errno != ENOSYS) err(1, "cap_enter"); (void) time(&now); + cap_rights_init(&rights, CAP_READ); while ((dp = readdir(dirp)) != NULL) { if (dp->d_ino == 0 || strncmp(dp->d_name, "whod.", 5) != 0) continue; f = openat(dfd, dp->d_name, O_RDONLY); if (f < 0) continue; - if (cap_rights_limit(f, CAP_READ) < 0 && errno != ENOSYS) + if (cap_rights_limit(f, &rights) < 0 && errno != ENOSYS) err(1, "cap_rights_limit failed: %s", dp->d_name); cc = read(f, (char *)&wd, sizeof(struct whod)); if (cc < WHDRSIZE) { diff --git a/usr.bin/su/su.1 b/usr.bin/su/su.1 index d9180e37a0b..9eb24d3f522 100644 --- a/usr.bin/su/su.1 +++ b/usr.bin/su/su.1 @@ -28,7 +28,7 @@ .\" @(#)su.1 8.2 (Berkeley) 4/18/94 .\" $FreeBSD$ .\" -.Dd June 6, 2011 +.Dd August 11, 2013 .Dt SU 1 .Os .Sh NAME @@ -37,8 +37,8 @@ .Sh SYNOPSIS .Nm .Op Fl -.Op Fl flms .Op Fl c Ar class +.Op Fl flms .Op Ar login Op Ar args .Sh DESCRIPTION The @@ -85,6 +85,11 @@ are also normally retained unless the target login has a user ID of 0. .Pp The options are as follows: .Bl -tag -width Ds +.It Fl c Ar class +Use the settings of the specified login class. +The login class must be defined in +.Xr login.conf 5 . +Only allowed for the super-user. .It Fl f If the invoked shell is .Xr csh 1 , @@ -139,9 +144,6 @@ is not sufficient to transition to the user's default MAC label. If the label cannot be set, .Nm will fail. -.It Fl c Ar class -Use the settings of the specified login class. -Only allowed for the super-user. .El .Pp The diff --git a/usr.bin/su/su.c b/usr.bin/su/su.c index 6557c343a0c..e721e62300a 100644 --- a/usr.bin/su/su.c +++ b/usr.bin/su/su.c @@ -373,6 +373,8 @@ main(int argc, char *argv[]) } lc = login_getclass(class); if (lc == NULL) + err(1, "login_getclass"); + if (lc->lc_class == NULL || strcmp(class, lc->lc_class) != 0) errx(1, "unknown class: %s", class); } diff --git a/usr.bin/truss/syscall.h b/usr.bin/truss/syscall.h index 94776a0dddb..ce7d2e94e4f 100644 --- a/usr.bin/truss/syscall.h +++ b/usr.bin/truss/syscall.h @@ -40,7 +40,7 @@ enum Argtype { None = 1, Hex, Octal, Int, Name, Ptr, Stat, Ioctl, Quad, Fd_set, Sigaction, Fcntl, Mprot, Mmapflags, Whence, Readlinkres, Umtx, Sigset, Sigprocmask, Kevent, Sockdomain, Socktype, Open, Fcntlflag, Rusage, BinString, Shutdown, Resource, Rlimit, Timeval2, - Pathconf, Rforkflags }; + Pathconf, Rforkflags, ExitStatus, Waitoptions, Idtype }; #define ARG_MASK 0xff #define OUT 0x100 diff --git a/usr.bin/truss/syscalls.c b/usr.bin/truss/syscalls.c index 6668d7ce083..5369decb44c 100644 --- a/usr.bin/truss/syscalls.c +++ b/usr.bin/truss/syscalls.c @@ -39,12 +39,13 @@ static const char rcsid[] = * arguments. */ -#include #include +#include #include #include #include #include +#include #include #include #include @@ -263,6 +264,12 @@ static struct syscall syscalls[] = { .args = { { Name , 0 } , { Name, 1 } } }, { .name = "posix_openpt", .ret_type = 1, .nargs = 1, .args = { { Open, 0 } } }, + { .name = "wait4", .ret_type = 1, .nargs = 4, + .args = { { Int, 0 }, { ExitStatus | OUT, 1 }, { Waitoptions, 2 }, + { Rusage | OUT, 3 } } }, + { .name = "wait6", .ret_type = 1, .nargs = 6, + .args = { { Idtype, 0 }, { Int, 1 }, { ExitStatus | OUT, 2 }, + { Waitoptions, 3 }, { Rusage | OUT, 4 }, { Ptr, 5 } } }, { .name = 0 }, }; @@ -296,7 +303,11 @@ static struct xlat mmap_flags[] = { X(MAP_SHARED) X(MAP_PRIVATE) X(MAP_FIXED) X(MAP_RENAME) X(MAP_NORESERVE) X(MAP_RESERVED0080) X(MAP_RESERVED0100) X(MAP_HASSEMAPHORE) X(MAP_STACK) X(MAP_NOSYNC) X(MAP_ANON) - X(MAP_NOCORE) XEND + X(MAP_NOCORE) X(MAP_PREFAULT_READ) +#ifdef MAP_32BIT + X(MAP_32BIT) +#endif + XEND }; static struct xlat mprot_flags[] = { @@ -377,6 +388,17 @@ static struct xlat rfork_flags[] = { X(RFSIGSHARE) X(RFTSIGZMB) X(RFLINUXTHPN) XEND }; +static struct xlat wait_options[] = { + X(WNOHANG) X(WUNTRACED) X(WCONTINUED) X(WNOWAIT) X(WEXITED) + X(WTRAPPED) XEND +}; + +static struct xlat idtype_arg[] = { + X(P_PID) X(P_PPID) X(P_PGID) X(P_SID) X(P_CID) X(P_UID) X(P_GID) + X(P_ALL) X(P_LWPID) X(P_TASKID) X(P_PROJID) X(P_POOLID) X(P_JAILID) + X(P_CTID) X(P_CPUID) X(P_PSETID) XEND +}; + #undef X #undef XEND @@ -533,6 +555,16 @@ get_string(pid_t pid, void *offset, int max) } } +static char * +strsig2(int sig) +{ + char *tmp; + + tmp = strsig(sig); + if (tmp == NULL) + asprintf(&tmp, "%d", sig); + return (tmp); +} /* * print_arg @@ -818,19 +850,14 @@ print_arg(struct syscall_args *sc, unsigned long *args, long retval, free(fds); break; } - case Signal: { - long sig; - - sig = args[sc->offset]; - tmp = strsig(sig); - if (tmp == NULL) - asprintf(&tmp, "%ld", sig); + case Signal: + tmp = strsig2(args[sc->offset]); break; - } case Sigset: { long sig; sigset_t ss; int i, used; + char *signame; sig = args[sc->offset]; if (get_struct(pid, (void *)args[sc->offset], (void *)&ss, @@ -841,8 +868,11 @@ print_arg(struct syscall_args *sc, unsigned long *args, long retval, tmp = malloc(sys_nsig * 8); /* 7 bytes avg per signal name */ used = 0; for (i = 1; i < sys_nsig; i++) { - if (sigismember(&ss, i)) - used += sprintf(tmp + used, "%s|", strsig(i)); + if (sigismember(&ss, i)) { + signame = strsig(i); + used += sprintf(tmp + used, "%s|", signame); + free(signame); + } } if (used) tmp[used-1] = 0; @@ -893,9 +923,41 @@ print_arg(struct syscall_args *sc, unsigned long *args, long retval, case Mprot: tmp = strdup(xlookup_bits(mprot_flags, args[sc->offset])); break; - case Mmapflags: - tmp = strdup(xlookup_bits(mmap_flags, args[sc->offset])); + case Mmapflags: { + char *base, *alignstr; + int align, flags; + + /* + * MAP_ALIGNED can't be handled by xlookup_bits(), so + * generate that string manually and prepend it to the + * string from xlookup_bits(). Have to be careful to + * avoid outputting MAP_ALIGNED|0 if MAP_ALIGNED is + * the only flag. + */ + flags = args[sc->offset] & ~MAP_ALIGNMENT_MASK; + align = args[sc->offset] & MAP_ALIGNMENT_MASK; + if (align != 0) { + if (align == MAP_ALIGNED_SUPER) + alignstr = strdup("MAP_ALIGNED_SUPER"); + else + asprintf(&alignstr, "MAP_ALIGNED(%d)", + align >> MAP_ALIGNMENT_SHIFT); + if (flags == 0) { + tmp = alignstr; + break; + } + } else + alignstr = NULL; + base = strdup(xlookup_bits(mmap_flags, flags)); + if (alignstr == NULL) { + tmp = base; + break; + } + asprintf(&tmp, "%s|%s", alignstr, base); + free(alignstr); + free(base); break; + } case Whence: tmp = strdup(xlookup(whence_arg, args[sc->offset])); break; @@ -1107,6 +1169,35 @@ print_arg(struct syscall_args *sc, unsigned long *args, long retval, asprintf(&tmp, "0x%lx", args[sc->offset]); break; } + case ExitStatus: { + char *signame; + int status; + signame = NULL; + if (get_struct(pid, (void *)args[sc->offset], &status, + sizeof(status)) != -1) { + if (WIFCONTINUED(status)) + tmp = strdup("{ CONTINUED }"); + else if (WIFEXITED(status)) + asprintf(&tmp, "{ EXITED,val=%d }", + WEXITSTATUS(status)); + else if (WIFSIGNALED(status)) + asprintf(&tmp, "{ SIGNALED,sig=%s%s }", + signame = strsig2(WTERMSIG(status)), + WCOREDUMP(status) ? ",cored" : ""); + else + asprintf(&tmp, "{ STOPPED,sig=%s }", + signame = strsig2(WTERMSIG(status))); + } else + asprintf(&tmp, "0x%lx", args[sc->offset]); + free(signame); + break; + } + case Waitoptions: + tmp = strdup(xlookup_bits(wait_options, args[sc->offset])); + break; + case Idtype: + tmp = strdup(xlookup(idtype_arg, args[sc->offset])); + break; default: errx(1, "Invalid argument type %d\n", sc->type & ARG_MASK); } diff --git a/usr.bin/uniq/uniq.c b/usr.bin/uniq/uniq.c index d34b0c0a5b3..8e7f40fd14c 100644 --- a/usr.bin/uniq/uniq.c +++ b/usr.bin/uniq/uniq.c @@ -145,20 +145,19 @@ main (int argc, char *argv[]) ofp = stdout; if (argc > 0 && strcmp(argv[0], "-") != 0) ifp = file(ifn = argv[0], "r"); - if (cap_rights_limit(fileno(ifp), CAP_FSTAT | CAP_READ) < 0 && - errno != ENOSYS) { + cap_rights_init(&rights, CAP_FSTAT, CAP_READ); + if (cap_rights_limit(fileno(ifp), &rights) < 0 && errno != ENOSYS) err(1, "unable to limit rights for %s", ifn); - } - rights = CAP_FSTAT | CAP_WRITE; + cap_rights_init(&rights, CAP_FSTAT, CAP_WRITE); if (argc > 1) ofp = file(argv[1], "w"); else - rights |= CAP_IOCTL; - if (cap_rights_limit(fileno(ofp), rights) < 0 && errno != ENOSYS) { + cap_rights_set(&rights, CAP_IOCTL); + if (cap_rights_limit(fileno(ofp), &rights) < 0 && errno != ENOSYS) { err(1, "unable to limit rights for %s", argc > 1 ? argv[1] : "stdout"); } - if ((rights & CAP_IOCTL) != 0) { + if (cap_rights_is_set(&rights, CAP_IOCTL)) { unsigned long cmd; cmd = TIOCGETA; /* required by isatty(3) in printf(3) */ diff --git a/usr.bin/vi/Makefile b/usr.bin/vi/Makefile index 048c35b6f01..033c4726a14 100644 --- a/usr.bin/vi/Makefile +++ b/usr.bin/vi/Makefile @@ -2,14 +2,12 @@ # $FreeBSD$ # +.include + SRCDIR= ${.CURDIR}/../../contrib/nvi +SUBDIR+= catalog -CFLAGS+= -DGTAGS - -#if using ncurses: -CFLAGS+= -DSYSV_CURSES - -WARNS?= 0 +WARNS?= 0 # some warn issues on 32 bit machines VI= nvi EX= nex @@ -17,6 +15,8 @@ VIEW= nview PROG= nvi +CFLAGS+= -D__REGEX_PRIVATE + LINKS= ${BINDIR}/${VI} ${BINDIR}/${EX} ${BINDIR}/${VI} ${BINDIR}/${VIEW} LINKS+= ${BINDIR}/${VI} ${BINDIR}/vi ${BINDIR}/${EX} ${BINDIR}/ex LINKS+= ${BINDIR}/${VI} ${BINDIR}/view @@ -25,34 +25,38 @@ MAN= ${SRCDIR}/docs/USD.doc/vi.man/vi.1 MLINKS+=vi.1 ex.1 vi.1 view.1 MLINKS+=vi.1 nex.1 vi.1 nview.1 vi.1 nvi.1 -CATALOGS= dutch english french german polish ru_RU.KOI8-R spanish swedish \ - uk_UA.KOI8-U -NLLINKS= nl_NL -ENLINKS= en_AU en_CA en_GB en_NZ en_US -FRLINKS= fr_BE fr_CA fr_CH fr_FR -DELINKS= de_AT de_CH de_DE -ESLINKS= es_ES -SVLINKS= sv_SE -PLLINKS= pl_PL - .PATH: ${SRCDIR}/common .PATH: ${SRCDIR}/ex .PATH: ${SRCDIR}/cl .PATH: ${SRCDIR}/vi +.PATH: ${SRCDIR}/regex -CFLAGS+=-I${.CURDIR} -I${SRCDIR} -I${SRCDIR}/include +CFLAGS+=-I${.CURDIR} -I${SRCDIR} -I${SRCDIR}/regex -DPADD= ${LIBNCURSES} -LDADD= -lncurses +DPADD= ${LIBUTIL} +LDADD= -lutil + +.if defined(RESCUE) || defined(RELEASE_CRUNCH) +DPADD+= ${LIBNCURSES} +LDADD+= -lncurses +.else +CFLAGS+= -DUSE_WIDECHAR +DPADD+= ${LIBNCURSESW} +LDADD+= -lncursesw +.endif + +.if ${MK_ICONV} == "yes" && !defined(RESCUE) && !defined(RELEASE_CRUNCH) +CFLAGS+= -DUSE_ICONV +.endif CLEANFILES+=${EX} # Vi curses sources -SRCS+= cl_bsd.c cl_funcs.c cl_main.c cl_read.c cl_screen.c cl_term.c +SRCS+= cl_funcs.c cl_main.c cl_read.c cl_screen.c cl_term.c # General sources. -SRCS+= cut.c delete.c exf.c key.c line.c log.c main.c mark.c msg.c options.c \ - options_f.c put.c screen.c search.c seq.c recover.c util.c +SRCS+= conv.c cut.c delete.c encoding.c exf.c key.c line.c log.c main.c mark.c msg.c options.c \ + options_f.c put.c recover.c screen.c search.c seq.c util.c # Ex source. SRCS+= ex.c ex_abbrev.c ex_append.c ex_args.c ex_argv.c ex_at.c ex_bang.c \ @@ -63,7 +67,7 @@ SRCS+= ex.c ex_abbrev.c ex_append.c ex_args.c ex_argv.c ex_at.c ex_bang.c \ ex_read.c ex_screen.c ex_script.c ex_set.c ex_shell.c \ ex_shift.c ex_source.c ex_stop.c ex_subst.c ex_tag.c \ ex_txt.c ex_undo.c ex_usage.c ex_util.c ex_version.c ex_visual.c \ - ex_write.c ex_yank.c ex_z.c ex_tcl.c ex_perl.c + ex_write.c ex_yank.c ex_z.c # Vi source. SRCS+= getc.c v_at.c v_ch.c v_cmd.c v_delete.c v_ex.c v_increment.c v_init.c \ @@ -75,38 +79,7 @@ SRCS+= getc.c v_at.c v_ch.c v_cmd.c v_delete.c v_ex.c v_increment.c v_init.c \ # Vi screen source. SRCS+= vs_line.c vs_msg.c vs_refresh.c vs_relative.c vs_smap.c vs_split.c -FILES= ${CATALOGS:S;^;${SRCDIR}/catalog/;} -FILESDIR= /usr/share/vi/catalog -SYMLINKS= -.for l in ${NLLINKS} -SYMLINKS+= dutch ${FILESDIR}/$l.ISO8859-1 -SYMLINKS+= dutch ${FILESDIR}/$l.ISO8859-15 -.endfor -.for l in ${ENLINKS} -SYMLINKS+= english ${FILESDIR}/$l.ISO8859-1 -SYMLINKS+= english ${FILESDIR}/$l.ISO8859-15 -SYMLINKS+= english ${FILESDIR}/$l.US-ASCII -.endfor -SYMLINKS+= english ${FILESDIR}/POSIX -SYMLINKS+= english ${FILESDIR}/C -.for l in ${FRLINKS} -SYMLINKS+= french ${FILESDIR}/$l.ISO8859-1 -SYMLINKS+= french ${FILESDIR}/$l.ISO8859-15 -.endfor -.for l in ${DELINKS} -SYMLINKS+= german ${FILESDIR}/$l.ISO8859-1 -SYMLINKS+= german ${FILESDIR}/$l.ISO8859-15 -.endfor -.for l in ${ESLINKS} -SYMLINKS+= spanish ${FILESDIR}/$l.ISO8859-1 -SYMLINKS+= spanish ${FILESDIR}/$l.ISO8859-15 -.endfor -.for l in ${SVLINKS} -SYMLINKS+= swedish ${FILESDIR}/$l.ISO8859-1 -SYMLINKS+= swedish ${FILESDIR}/$l.ISO8859-15 -.endfor -.for l in ${PLLINKS} -SYMLINKS+= polish ${FILESDIR}/$l.ISO8859-2 -.endfor +# Wide char regex +SRCS+= regcomp.c regerror.c regexec.c regfree.c .include diff --git a/usr.bin/vi/catalog/Makefile b/usr.bin/vi/catalog/Makefile new file mode 100644 index 00000000000..bd9db9f22e3 --- /dev/null +++ b/usr.bin/vi/catalog/Makefile @@ -0,0 +1,161 @@ +# $Id: Makefile,v 9.0 2012/10/19 15:13:11 zy Exp $ +# $FreeBSD$ + +.include + +V= ${.CURDIR}/../../../contrib/nvi + +CAT= dutch english french german polish ru_RU.KOI8-R spanish swedish \ + uk_UA.KOI8-U zh_CN.GB2312 + +SCAN= ${V}/cl/*.c ${V}/common/*.c ${V}/ex/*.c ${V}/vi/*.c + +.PATH: ${V}/catalog + +all: dump + +build-tools: dump + +# Helper since iconv is non trivial to make a build tool +utf8convert: +.for c in dutch french german spanish swedish + iconv -f ISO8859-1 -t UTF-8 $V/catalog/$c.base > $c.UTF-8.base +.endfor + iconv -f ISO8859-2 -t UTF-8 $V/catalog/polish.base > polish.UTF-8.base + iconv -f GB2312 -t UTF-8 $V/catalog/zh_CN.GB2312.base > zh_CN.UTF-8.base + iconv -f KOI8-R -t UTF-8 $V/catalog/ru_RU.KOI8-R.base > ru_RU.UTF-8.base + iconv -f KOI8-U -t UTF-8 $V/catalog/uk_UA.KOI8-U.base > uk_UA.UTF-8.base + +.for c in dutch french german polish spanish swedish zh_CN ru_RU uk_UA +CAT+= $c.UTF-8 +.endfor + +.for c in ${CAT} +${c}: ${c}.base + @echo "... $c"; \ + rm -f $c; \ + sort -u ${.ALLSRC} | \ + awk '{ \ + if ($$1 == 1) { \ + print "\nMESSAGE NUMBER 1 IS NOT LEGAL"; \ + exit 1; \ + } \ + if (++nline > $$1) { \ + print "DUPLICATE MESSAGE NUMBER " $$1; \ + exit 1; \ + } \ + print $0; \ + }' | \ + sed -e '1s/^/$$set 1~$$quote "~/; 1y/~/\n/' | \ + gencat $c /dev/stdin; \ + chmod 444 $c; \ + if grep DUPLICATE $c > /dev/null; then \ + grep DUPLICATE $@; \ + fi; \ + if grep 'NOT LEGAL' $c > /dev/null; then \ + grep 'NOT LEGAL' $@; \ + fi +.endfor + +CHK= dutch.check english.check french.check german.check \ + polish.check ru_RU.KOI8-R.check spanish.check swedish.check \ + uk_UA.KOI8-U.check zh_CN.GB2312.check +check: ${CHK} +.for c in ${CAT} +${c}.check: ${c}.base + @echo "... $c"; \ + f=${.ALLSRC:S;.base$;;}; \ + (echo "Unused message id's (this is okay):"; \ + awk '{ \ + while (++nline < $$1) \ + printf "%03d\n", nline; \ + }' < $$f.base; \ + echo =========================; \ + echo "MISSING ERROR MESSAGES (Please add!):"; \ + awk '{print $$1}' < $$f.base | sort -u > __ck1; \ + awk '{print $$1}' < english.base | sort -u > __ck2; \ + comm -13 __ck1 __ck2; \ + echo =========================; \ + echo "Extra error messages (just delete them):"; \ + comm -23 __ck1 __ck2; \ + echo =========================; \ + echo "MESSAGES WITH THE SAME MESSAGE ID's (FIX!):"; \ + for j in \ + `sed '/^$$/d' < $$f.base | LANG=C sort -u | \ + awk '{print $$1}' | uniq -d`; do \ + egrep $$j $$f.base; \ + done; \ + echo =========================; \ + echo "Duplicate messages, both id and message (this is okay):"; \ + sed '/^$$/d' < $$f.base | LANG=C sort | uniq -c | \ + awk '$$1 != 1 { print $$0 }' | sort -n; \ + echo =========================) > $c +.endfor + +english.base: dump ${SCAN} #Makefile + ./dump ${SCAN} |\ + sed -e '/|/!d' \ + -e 's/|/ "/' \ + -e 's/^"//' |\ + sort -nu > $@ + + +dump: dump.c + ${CC} -o dump ${.ALLSRC} + +CLEANFILES+= dump ${CAT} english.base *.check __ck1 __ck2 + +CATALOGS= ${CAT} +NLLINKS= nl_NL +ENLINKS= en_AU en_CA en_GB en_NZ en_US +FRLINKS= fr_BE fr_CA fr_CH fr_FR +DELINKS= de_AT de_CH de_DE +ESLINKS= es_ES +SVLINKS= sv_SE +PLLINKS= pl_PL + +FILES= ${CATALOGS} +FILESDIR= /usr/share/vi/catalog +SYMLINKS= +.for l in ${NLLINKS} +SYMLINKS+= dutch ${FILESDIR}/$l.ISO8859-1 +SYMLINKS+= dutch ${FILESDIR}/$l.ISO8859-15 +SYMLINKS+= dutch.UTF-8 ${FILESDIR}/$l.UTF-8 +.endfor +.for l in ${ENLINKS} +SYMLINKS+= english ${FILESDIR}/$l.ISO8859-1 +SYMLINKS+= english ${FILESDIR}/$l.ISO8859-15 +SYMLINKS+= english ${FILESDIR}/$l.US-ASCII +SYMLINKS+= english ${FILESDIR}/$l.UTF-8 +.endfor +SYMLINKS+= english ${FILESDIR}/POSIX +SYMLINKS+= english ${FILESDIR}/C +.for l in ${FRLINKS} +SYMLINKS+= french ${FILESDIR}/$l.ISO8859-1 +SYMLINKS+= french ${FILESDIR}/$l.ISO8859-15 +SYMLINKS+= french.UTF-8 ${FILESDIR}/$l.UTF-8 +.endfor +.for l in ${DELINKS} +SYMLINKS+= german ${FILESDIR}/$l.ISO8859-1 +SYMLINKS+= german ${FILESDIR}/$l.ISO8859-15 +SYMLINKS+= german.UTF-8 ${FILESDIR}/$l.UTF-8 +.endfor +.for l in ${ESLINKS} +SYMLINKS+= spanish ${FILESDIR}/$l.ISO8859-1 +SYMLINKS+= spanish ${FILESDIR}/$l.ISO8859-15 +SYMLINKS+= spanish.UTF-8 ${FILESDIR}/$l.UTF-8 +.endfor +.for l in ${SVLINKS} +SYMLINKS+= swedish ${FILESDIR}/$l.ISO8859-1 +SYMLINKS+= swedish ${FILESDIR}/$l.ISO8859-15 +SYMLINKS+= swedish.UTF-8 ${FILESDIR}/$l.UTF-8 +.endfor +.for l in ${PLLINKS} +SYMLINKS+= polish ${FILESDIR}/$l.ISO8859-2 +SYMLINKS+= polish.UTF-8 ${FILESDIR}/$l.UTF-8 +.endfor +SYMLINKS+= zh_CN.GB2312 ${FILESDIR}/zh_CN.GB18030 +SYMLINKS+= zh_CN.GB2312 ${FILESDIR}/zh_CN.GBK +SYMLINKS+= zh_CN.GB2312 ${FILESDIR}/zh_CN.eucCN + +.include diff --git a/usr.bin/vi/catalog/dutch.UTF-8.base b/usr.bin/vi/catalog/dutch.UTF-8.base new file mode 100644 index 00000000000..eca193c28c0 --- /dev/null +++ b/usr.bin/vi/catalog/dutch.UTF-8.base @@ -0,0 +1,306 @@ +002 "regel te lang" +003 "kan regel %lu niet verwijderen" +004 "kan niet toevoegen aan regel %lu" +005 "kan niet invoegen vooraan regel %lu" +006 "kan regel %lu niet opslaan" +007 "kan laatste regel niet lezen" +008 "Fout: kan regel %lu niet vinden" +009 "log bestand" +010 "Er vindt geen logging plaats, kan wijzigingen niet ongedaan maken" +011 "geen wijzigingen om ongedaan te maken" +012 "Er vindt geen logging plaats, kan wijzigingen niet ongedaan maken" +013 "Er vindt geen logging plaats, herhaling niet mogelijk" +014 "geen wijzigingen om te herhalen" +015 "%s/%d: schrijven naar log mislukt" +016 "Vi's standaard invoer en uitvoer moeten aan een terminal gekoppeld zijn" +017 "Merk %s: niet gezet" +018 "Merk %s: de regel is verwijderd" +019 "Merk %s: de cursor positie bestaat niet meer" +020 "Fout: " +021 "nieuw bestand" +022 "naam veranderd" +023 "gewijzigd" +024 "ongewijzigd" +025 "NIET BEVEILIGD" +026 "niet schrijfbaar" +027 "regel %lu uit %lu [%ld%%]" +028 "leeg bestand" +029 "regel %lu" +030 "Het bestand %s is geen message catalog" +031 "Niet in staat om de standaard %s optie in te stellen" +032 "Gebruik: %s" +033 "set: optie %s onbekend: 'set all' laat alle opties zien" +034 "set: [no]%s optie kan geen waarde hebben" +035 "set: %s optie moet een waarde hebben" +036 "set: %s optie: %s" +037 "set: %s optie: %s: getal is te groot" +038 "set: %s optie: %s is een ongeldige waarde" +039 "set: %s optie moet een waarde hebben" +040 "Te weinig kolommen op het scherm, minder dan %d" +041 "Aantal kolommen te groot, meer dan %d" +042 "Te weinig regels op het scherm, minder dan %d" +043 "Aantal regels te groot, meer dan %d" +044 "De lisp optie is niet ondersteund" +045 "messages niet uitgeschakeld: %s" +046 "messages niet geactiveerd: %s" +047 "De %s optie moet karakter paren bevatten" +053 "De standaard buffer is leeg" +054 "Buffer %s is leeg" +055 "Bestanden met newlines in de naam kunnen niet hersteld worden" +056 "Wijzigingen kunnen niet ongedaan gemaakt worden als deze sessie mislukt" +057 "Bestand wordt gecopieerd voor herstel..." +058 "Herstel mechanisme werkt niet: %s" +059 "Wijzigingen kunnen niet ongedaan gemaakt worden als deze sessie mislukt" +060 "Kon bestand niet veilig stellen: %s" +061 "Bestand wordt gecopieerd voor herstel..." +062 "Informatie met betrekking tot gebruiker nummer %u niet gevonden" +063 "Kan herstel bestand niet beveiligen" +064 "herstel buffer overgelopen" +065 "herstel bestand" +066 "%s: verminkt herstel bestand" +067 "%s: verminkt herstel bestand" +068 "U heeft geen bestand genaamd %s te herstellen" +069 "U kan eerdere versies van dit bestand herstellen" +070 "U kan nog meer bestanden herstellen" +071 "kan geen email versturen: %s" +072 "Bestand leeg; niets om te doorzoeken" +073 "Einde van het bestand bereikt zonder dat het patroon gevonden is" +074 "Geen vorig zoek patroon" +075 "Patroon niet gevonden" +076 "Begin van het bestand bereikt zonder dat het patroon gevonden is" +077 "Zoek-operatie omgeslagen" +078 "Bezig met zoeken..." +079 "Geen niet-printbaar karakter gevonden" +080 "Onbekend commando" +082 "Commando niet beschikbaar in ex mode" +083 "Aantal mag niet nul zijn" +084 "%s: ongeldige regel aanduiding" +085 "Interne fout in syntax tabel (%s: %s)" +086 "Gebruik: %s" +087 "%s: tijdelijke buffer niet vrijgegeven" +088 "Vlag offset voor regel 1" +089 "Vlag offset voorbij bestands einde" +090 "bestand/scherm veranderd tijdens uitvoeren van @ in een blok" +091 "bestand/scherm veranderd tijdens uitvoeren van globaal/v commando" +092 "Ex commando mislukt: rest van commando(s) genegeerd" +093 "Ex commando mislukt: gemappede toetsen genegeerd" +094 "Het tweede adres is kleiner dan het eerste" +095 "Geen merk naam opgegeven" +096 "\\ niet gevolgd door / of ?" +097 "Referentie aan een regel nummer kleiner dan 0" +098 "Het %s commando is onbekend" +099 "Adres waarde te groot" +100 "Adres waarde te klein" +101 "Ongeldige adres combinatie" +102 "Ongeldig adres: slechts %lu regels in het bestand aanwezig" +103 "Ongeldig adres: het bestand is leeg" +104 "Het %s commando staat het adres 0 niet toe" +105 "Geen afkortingen om weer te geven" +106 "Afkortingen moeten eindigen met een \"woord\" letter" +107 "Afkortingen mogen geen tabulaties of spaties bevatten" +108 "Afkortingen mogen geen woord/niet-woord karakters mengen, behalve aan het einde" +109 "\"%s\" is geen afkorting" +110 "Vi commando mislukt: gemappede toetsen genegeerd" +111 "Dit is het laatste bestand" +112 "Dit is het eerste bestand" +113 "Dit is het eerste bestand" +114 "lijst met bestanden is leeg" +115 "Geen voorgaand commando om \"!\" te vervangen" +116 "Geen bestandsnaam voor %%" +117 "Geen bestandsnaam voor #" +118 "Fout: execl: %s" +119 "I/O fout: %s" +120 "Bestand gewijzigd sinds laatste schrijfactie; schrijf het weg of gebruik ! om het te forceren" +121 "Kan uw home directory niet vinden" +122 "Nieuwe huidige directory: %s" +123 "Geen cut buffers aanwezig" +124 "Het %s commando kan niet gebruikt worden in een globaal of v commando" +125 "%s/%s: niet gelezen: noch U noch root is de eigenaar" +126 "%s/%s: niet gelezen: U bent niet de eigenaar" +127 "%s/%s: niet gelezen: kan gewijzigd worden door andere gebruikers" +128 "%s: niet gelezen: noch U noch root is de eigenaar" +129 "%s: niet gelezen: U bent niet de eigenaar" +130 "%s: niet gelezen: kan gewijzigd worden door andere gebruikers" +131 "Geen volgende regel om samen te voegen" +132 "Geen input map entries" +133 "Geen command map entries" +134 "Het %s karakter kan niet ge-remapped worden" +135 "\"%s\" is niet gemapped" +136 "Merk naam moet een enkel karakter zijn" +137 "%s bestaat al, niet weggeschreven; gebruik ! om het te forceren" +138 "Nieuw .exrc bestand: %s. " +139 "doel regel ligt in het blok" +140 "Het open commando vereist dat de open optie actief is" +141 "Het open commando is nog niet ondersteund" +142 "Kan dit bestand niet veilig stellen" +143 "Bestand veilig gesteld" +144 "%s resulteert in te veel bestandsnamen" +145 "Alleen echte bestanden en named pipes kunnen gelezen worden" +146 "%s: lees beveiliging niet beschikbaar" +147 "Bezig met lezen..." +148 "%s: %lu regels, %lu karakters" +149 "Geen achtergrond schermen aanwezig" +150 "Het script commando is alleen beschikbaar in vi mode" +151 "Geen comando om uit te voeren" +152 "shiftwidth optie op 0 gezet" +153 "Count te groot" +154 "Count te klein" +155 "Reguliere expressie opgegeven; r vlag heeft geen betekenis" +156 "De #, l en p vlaggen kunnen niet gecombineerd worden met de c vlag in vi mode" +157 "Geen match gevonden" +158 "Geen voorafgaande tag aanwezig" +159 "Minder dan %s elementen op de tags stapel; gebruik :display t[ags]" +160 "Geen bestand genaamd %s op de tags stapel; gebruik :display t[ags]" +161 "Kies Enter om door te gaan: " +162 "%s: tag niet gevonden" +163 "%s: verminkte tag in %s" +164 "%s: Het regel nummer van deze tag is voorbij het einde van het bestand" +165 "De tags stapel is leeg" +166 "%s: zoek patroon niet gevonden" +167 "%d andere bestanden te wijzigen" +168 "Buffer %s is leeg" +169 "Bevestig wijziging? [n]" +170 "Onderbroken" +171 "Geen voorafgaande buffer om uit te voeren" +172 "Geen vorige reguliere expressie" +173 "Het %s commando vereist dat er een bestand geladen is" +174 "Gebruik: %s" +175 "Het visual commando vereist dat de open optie actief is" +177 "Leeg bestand" +178 "Geen voorafgaand F, f, T of t zoek commando" +179 "%s niet gevonden" +180 "Geen voorafgaand bestand te bewerken" +181 "Cursor niet op een getal" +182 "Getal wordt te groot" +183 "Getal wordt te klein" +184 "Geen overeenkomstig karakter op deze regel" +185 "Overeenkomstig karakter niet gevonden" +186 "Geen karakters te vervangen" +187 "Geen ander scherm aanwezig" +188 "Karakters achter het zoek patroon, de regel offset, en/of het z commando" +189 "Geen voorafgaand zoek patroon" +190 "Zoekopdracht na omslag teruggekeerd op originele positie" +191 "Afkorting overschrijdt expansie limiet: karakters genegeerd" +192 "Ongeldig karakter; quote to enter" +193 "Reeds aan het begin van de invoer" +194 "Niet meer karakters te verwijderen" +195 "Verplaatsing voorbij het einde van het bestand" +196 "Verplaatsing voorbij het einde van de regel" +197 "Cursor niet verplaatst" +198 "Reeds aan het begin van het bestand" +199 "Verplaatsing voorbij het begin van het bestand" +200 "Reeds in de eerste kolom" +201 "Buffers moeten voor het commando opgegeven worden" +202 "Reeds bij het einde van het bestand" +203 "Reeds bij het einde van de regel" +204 "%s is geen vi commando" +205 "Gebruik: %s" +206 "Geen karakters te verwijderen" +207 "Het Q commando vereist de ex terminal interface" +208 "Geen commando om te herhalen" +209 "Het bestand is leeg" +210 "%s mag niet gebruikt worden als een verplaatsings commando" +211 "Al in commando mode" +212 "Cursor niet in een woord" +214 "Windows optie waarde is te groot, maximum is %u" +215 "Toevoegen" +216 "Veranderen" +217 "Commando" +218 "Invoegen" +219 "Vervangen" +220 "Verplaatsing voorbij het eind van het scherm" +221 "Verplaatsing voorbij het begin van het scherm" +222 "Scherm moet meer dan %d regels hebben om het te kunnen splitsen" +223 "Er zijn geen achtergrond schermen" +224 "Er is geen achtergrond scherm waarin U bestand %s aan het bewerken bent" +225 "U kan uw enige scherm niet in de achtergrond zetten" +226 "Het scherm kan slechts verkleind worden tot %d regels" +227 "Het scherm kan niet kleiner" +228 "Het scherm kan niet groter" +230 "Dit scherm kan niet gesuspend worden" +231 "Onderbroken: gemappede toetsen genegeerd" +232 "vi: tijdelijke buffer niet vrijgegeven" +233 "Deze terminal heeft geen %s toets" +234 "Er kan slechts een buffer opgegeven worden" +235 "Getal groter dan %lu" +236 "Onderbroken" +237 "Aanmaken van tijdelijk bestand is mislukt" +238 "Waarschuwing: %s is geen regulier bestand" +239 "%s is al geopend, bestand is in deze sessie niet schrijfbaar" +240 "%s: verwijdering mislukt" +241 "%s: sluiting mislukt" +242 "%s: verwijdering mislukt" +243 "%s: verwijdering mislukt" +244 "Bestand niet schrijfbaar, niet weggeschreven; gebruik ! om het te forceren" +245 "Bestand niet schrijfbaar, niet weggeschreven" +246 "%s bestaat al, niet weggeschreven; gebruik ! om het te forceren" +247 "%s bestaat al, niet weggeschreven" +248 "Gebruik ! om een incompleet bestand weg te schrijven" +249 "Bestand incompleet, niet weggeschreven" +250 "%s: bestand op disk nieuwer dan deze versie; gebruik ! om het te forceren" +251 "%s: bestand op disk nieuwer dan deze versie" +252 "%s: schrijf beveiliging niet beschikbaar" +253 "Bezig met schrijven..." +254 "%s: WAARSCHUWING: BESTAND INCOMPLEET" +255 "Reeds op de eerste tag van deze groep" +256 "%s: nieuw bestand: %lu regels, %lu karakters" +257 "%s: %lu regels, %lu karakters" +258 "%s resulteert in te veel bestandsnamen" +259 "%s: geen normaal bestand" +260 "%s: U bent niet de eigenaar" +261 "%s: kan gewijzigd worden door andere gebruikers" +262 "Bestand gewijzigd sinds laatste schrijfactie; schrijf het weg of gebruik ! om het te forceren" +263 "Bestand gewijzigd sinds laatste schrijfactie; schrijf het weg of gebruik :edit! om het te forceren" +264 "Bestand gewijzigd sinds laatste schrijfactie; schrijf het weg of gebruik ! om het te forceren" +265 "Tijdelijk bestand; exit negeert wijzigingen" +266 "Bestand niet schrijfbaar, wijzigingen niet automatisch weggeschreven" +267 "log opnieuw gestart" +268 "Bevestig? [ynq]" +269 "Druk op een toets om door te gaan: " +270 "Druk op een toets om door te gaan [: voor meer ex commandos]: " +271 "Druk op een toets om door te gaan [q om te stoppen]: " +272 "Deze vorm van %s vereist de ex terminal interface" +273 "Entering ex input mode." +274 "Commando mislukt, nog geen bestand geladen." +275 " doorgaan?" +276 "Onverwacht character event" +277 "Onverwacht end-of-file event" +278 "Geen match gevonden voor dit patroon" +279 "Onverwacht interrupt event" +280 "Onverwacht quit event" +281 "Onverwacht repaint event" +282 "Reeds op de laatste tag van deze groep" +283 "Het %s command vereist de ex terminal interface" +284 "Deze vorm van %s is niet ondersteund als de secure edit optie actief is" +285 "Onverwacht string event" +286 "Onverwacht timeout event" +287 "Onverwacht write event" +289 "Shell expansies zijn niet ondersteund als de secure edit optie actief is" +290 "Het %s commando is niet ondersteund als de secure edit optie actief is" +291 "set: %s mag niet uitgezet worden" +292 "Scherm te klein." +293 "toegevoegd" +294 "gewijzigd" +295 "verwijderd" +296 "samengevoegd" +297 "verplaatst" +298 "verschoven" +299 "gebufferd" +300 "regel" +301 "regels" +302 "Vi was niet geladen met een Tcl interpreter" +303 "Bestand gewijzigd sinds het de laatste keer weg is geschreven." +304 "Shell expansie mislukt" +305 "Geen %s edit optie opgegeven" +306 "Vi was niet geladen met een Perl interpreter" +307 "Geen ex commando om uit te voeren" +308 "Kies om commando uit te voeren, :q om te stoppen" +309 "Gebruik \"cscope help\" voor uitleg" +310 "Nog geen cscope connectie aanwezig" +311 "%s: onbekend zoek type: gebruik een van %s" +312 "%d: onbekende cscope sessie" +313 "set: de %s optie mag nooit aangezet worden" +314 "set: de %s optie mag nooit op 0 gezet worden" +315 "%s: toegevoegd: %lu regels, %lu karakters" +316 "Onverwacht resize event" +317 "%d bestanden te wijzigen" diff --git a/usr.bin/vi/catalog/french.UTF-8.base b/usr.bin/vi/catalog/french.UTF-8.base new file mode 100644 index 00000000000..eb6aad000f3 --- /dev/null +++ b/usr.bin/vi/catalog/french.UTF-8.base @@ -0,0 +1,306 @@ +002 "Dépassement de longueur de ligne" +003 "impossible de supprimer la ligne %lu" +004 "impossible d'ajouter à la ligne %lu" +005 "impossible d'insérer devant la ligne %lu" +006 "impossible de stocker la ligne %lu" +007 "impossible d'obtenir la dernière ligne" +008 "Erreur : impossible de récupérer la ligne %lu" +009 "Fichier journal" +010 "Aucune journalisation n'étant effectuée, impossible d'annuler" +011 "Aucune action à annuler" +012 "Aucune journalisation n'étant effectuée, impossible d'annuler" +013 "Aucune journalisation n'étant effectuée, reprise actualisée impossible" +014 "Aucune action à refaire" +015 "%s/%d : Erreur d'écriture de journal" +016 "L'entrée et la sortie Vi standards doivent être un terminal" +017 "Marque %s : non définie" +018 "Marque %s : la ligne a été supprimée" +019 "Marque %s : la position du curseur n'existe plus" +020 "Erreur : " +021 "nouveau fichier" +022 "le nom a changé" +023 "modifié" +024 "non modifié" +025 "DÉVERROUILLÉ" +026 "lecture seule" +027 "ligne %lu de %lu [%ld%%]" +028 "fichier vide" +029 "ligne %lu" +030 "Ce fichier %s n'est pas un catalogue de messages" +031 "Impossible de configurer option %s par défaut" +032 "Utilisation : %s" +033 "Définition : pas d'option %s : 'set all' donne toutes les valeurs optionnelles" +034 "Définition : option [no]%s ne prend pas de valeur" +035 "Définition : l'option %s n'est pas booléenne" +036 "Définition : option %s : %s" +037 "Définition : option %s : %s : Dépassement de valeur" +038 "Définition : option %s : %s n'est pas un nombre valide" +039 "Définition : l'option %s n'est pas booléenne" +040 "Les colonnes de l'écran sont trop petites, inférieures à %d" +041 "Les colonnes de l'écran sont trop grandes, supérieures à %d" +042 "Les lignes de l'écran sont trop courtes, inférieures à %d" +043 "Les lignes de l'écran sont trop longues, supérieures à %d" +044 "L'option lisp n'est pas implémentée" +045 "Les messages ne sont pas désactivés : %s" +046 "Les messages ne sont pas activés : %s" +047 "L'option de %s doit être en groupe de deux caractères" +053 "Le tampon par défaut est vide" +054 "Le tampon %s est vide" +055 "Les fichiers dont le nom contient des caractères de saut de ligne sont irrécupérables" +056 "Impossible de récupérer les modifications si la session échoue" +057 "Copie en cours du fichier pour récupération..." +058 "La préservation a échoué : %s" +059 "Impossible de récupérer les modifications si la session échoue" +060 "La sauvegarde du fichier a échoué : %s" +061 "Copie en cours du fichier pour récupération..." +062 "Les renseignements sur l'identité %u de l'utilisateur sont introuvables" +063 "Impossible de verrouiller le fichier de récupération" +064 "Débordement de tampon du fichier de récupération" +065 "Fichier de récupération" +066 "%s : Fichier de récupération malformé" +067 "%s : Fichier de récupération malformé" +068 "Aucun fichier nommé %s à récupérer, que vous puissiez lire" +069 "Il existe des versions récupérables antérieures à ce fichier" +070 "Vous avez d'autres fichiers à récupérer" +071 "pas d'envoi d'email : %s" +072 "Fichier vide, rien à rechercher" +073 "Fin de fichier atteinte sans trouver le motif" +074 "Pas de motif de recherche précédent" +075 "Motif introuvable" +076 "Début du fichier atteint sans trouver le motif" +077 "La recherche est revenue à son point de départ" +078 "Recherche en cours..." +079 "Caractère non-imprimable introuvable" +080 "Nom de commande inconnu" +082 "%s : Commande non disponible en ex mode" +083 "Le compteur ne peut être zéro" +084 "%s : mauvaise spécification de ligne" +085 "Erreur de tableau de syntaxe interne (%s: %s)" +086 "Utilisation : %s" +087 "%s : tampon temporaire non libéré" +088 "Décalage de drapeau hors de la ligne 1" +089 "Décalage de drapeau hors de la fin du fichier" +090 "@ avec plage, en cours d'exécution quand le fichier/l'écran a changé" +091 "Commande Global/v en cours d'exécution quand le fichier/l'écran a changé" +092 "La commande ex a échoué : commandes en attente abandonnées" +093 "La commande ex a échoué : les touches affectées sont abandonnées" +094 "La deuxième adresse est plus petite que la première" +095 "Aucun nom de marque fourni" +096 "\\ non suivi par / ou ?" +097 "Référence à un numéro de ligne inférieure à 0" +098 "La commande %s est inconnue" +099 "Dépassement de la valeur adresse" +100 "Dépassement négatif de la valeur adresse" +101 "Combinaison d'adresse non valide" +102 "Adresse non valide : il n'y a que %lu lignes dans ce fichier" +103 "Adresse non valide : le fichier est vide" +104 "La commande %s ne permet pas une adresse de 0" +105 "Pas d'abréviations à afficher" +106 "Les abréviations doivent finir par un caractère \"mot\"" +107 "Les abréviations ne peuvent pas contenir de tabulations ni d'espaces" +108 "Les abréviations ne peuvent pas contenir un mélange de caractères mot/non-mot, sauf à la fin" +109 "\"%s\" n'est pas une abréviation" +110 "La commande Vi a échoué : Les touches affectées ont été abandonnées" +111 "Plus de fichiers à éditer" +112 "Pas de fichiers précédents à éditer" +113 "Pas de fichiers précédents à rembobiner" +114 "Pas de liste de fichiers à afficher" +115 "Pas de commande précédente à remplacer \"!\"" +116 "Pas de nom de fichier à substituer à %%" +117 "Pas de nom de fichier à substituer à #" +118 "Erreur : execl : %s" +119 "Erreur E/S : %s" +120 "Fichier modifié depuis la dernière écriture complète ; écrire ou utiliser ! pour outrepasser" +121 "Impossible de trouver l'emplacement du répertoire d'origine" +122 "Nouveau répertoire en cours : %s" +123 "Pas de tampon de coupure à afficher" +124 "La commande %s ne peut pas être utilisée à l'intérieur d'une commande globale ou commande v" +125 "%s/%s : échec de source : ni vous ni le super-utilisateur n'êtes les propriétaires " +126 "%s/%s : échec de source : vous n'êtes pas le propriétaire" +127 "%s/%s : échec de source : peut être écrit par un utilisateur autre que le propriétaire" +128 "%s : échec de source : ni vous ni le super-utilisateur n'êtes les propriétaires" +129 "%s : échec de source : vous n'êtes pas le propriétaire" +130 "%s : échec de source : peut être écrit par un utilisateur autre que le propriétaire" +131 "Pas de lignes suivantes à joindre" +132 "Pas d'entrées de mappage d'entrée" +133 "Pas d'entrées de mappage de commandes" +134 "Le caractère %s ne peut pas être remappé" +135 "\"%s\" n'est pas actuellement mappé" +136 "Les noms de marque ne doivent avoir qu'un caractère" +137 "%s existe, non enregistré; utiliser ! pour outrepasser" +138 "Nouveau fichier exrc : %s" +139 "La ligne de destination est à l'intérieur de la plage à déplacer" +140 "La commande ouverte nécessite que l'option ouverte soit définie" +141 "La commande ouverte n'est pas encore implémentée" +142 "La préservation de ce fichier est impossible" +143 "Fichier préservé" +144 "%s: étendu dans trop de noms de fichiers" +145 "Vous ne pouvez lire que les fichiers standards et les canaux de transmission nommés" +146 "%s: Interdiction de lecture non disponible" +147 "Lecture en cours..." +148 "%s: %lu lignes, %lu caractères" +149 "Pas d'écrans d'arrière-plan à afficher" +150 "La commande script n'est disponible qu'en mode vi" +151 "Pas de commande à exécuter" +152 "Option de largeur de décalage définie sur 0" +153 "Compter dépassement" +154 "Compter dépassement négatif" +155 "Expression standard spécifiée; drapeau r superflu" +156 "Vous ne pouvez pas en mode vi, combiner les drapeaux #, l et p avec le drapeau c" +157 "Aucune correspondance trouvée" +158 "Aucune marque précédente entrée" +159 "Moins de %s entrées dans la pile de marques ; utilisez t[ags]" +160 "Pas de fichier %s vers lequel retourner dans la pile de marques ; utiliser : affichage t[ags]" +161 "Appuyez sur Entrée pour continuer :" +162 "%s : marque introuvable" +163 "%s : marque corrompue en %s" +164 "%s : le numéro de ligne de la marque dépasse la fin du fichier" +165 "La pile de marques est vide" +166 "%s : motif de recherche introuvable" +167 "%d fichiers supplémentaires à éditer" +168 "Le tampon %s est vide +169 "Confirmer les changements ? [n]" +170 "Interrompu" +171 "Pas de tampon précédent à exécuter" +172 "Pas d'expression standard précédente" +173 "La commande %s nécessite qu'un fichier ait déjà été lu en mémoire" +174 "Utilisation : %s" +175 "La commande visual nécessite que l'option open soit définie" +177 "Fichier vide" +178 "Pas de recherche précédente F, f, T ou t" +179 "%s introuvable" +180 "Pas de fichier précédent à éditer" +181 "Le curseur n'est pas dans un nombre" +182 "Le nombre obtenu est trop grand" +183 "Le nombre obtenu est trop petit" +184 "Pas de correspondance de caractère sur cette ligne" +185 "Caractère correspondant introuvable" +186 "Pas de caractères à remplacer" +187 "Pas d'autre écran vers lequel basculer" +188 "Caractères après la chaîne de recherche, décalage de ligne et/ou commande z" +189 "Pas de motif de recherche précédent" +190 "La recherche est revenue à son point de départ" +191 "L'abréviation a dépassé la limite de l'expansion : caractères abandonnés" +192 "Caractère non valide ; guillemet pour saisir" +193 "Déjà au début de l'insertion" +194 "Plus de caractères à effacer" +195 "Déplacement hors de fin de fichier" +196 "Déplacement hors de fin de ligne" +197 "Aucun mouvement de curseur n'a été effectué" +198 "Déjà au début du fichier" +199 "Déplacement hors du début du fichier" +200 "Déjà dans la première colonne" +201 "Les tampons doivent être spécifiés avant la commande" +202 "Déjà à la fin du fichier" +203 "Déjà à la fin de la ligne" +204 "%s n'est pas une commande vi" +205 "Utilisation : %s" +206 "Pas de caractères à supprimer" +207 "La commande Q nécessite une interface terminal ex" +208 "Pas de commande à répéter" +209 "Le fichier est vide" +210 "Vous ne pouvez pas utiliser %s comme commande de déplacement" +211 "Déjà en mode commande" +212 "Le curseur n'est pas dans un mot" +214 "Valeur optionnelle de fenêtre trop grande, maximum est %u" +215 "Ajouter" +216 "Changer" +217 "Commande" +218 "Insérer" +219 "Remplacer" +220 "Déplacement hors de la fin d'écran" +221 "Déplacement hors du début d'écran" +222 "L'écran doit être supérieur à %d lignes pour se fractionner" +223 "Il n'y a pas d'écran d'arrière-plan" +224 "Il n'y a pas d'écran d'arrière-plan qui édite un fichier nommé %s" +225 "Vous ne pouvez pas mettre à l'arrière-plan votre seul écran affiché" +226 "L'écran ne peut être réduit qu'à %d rangs" +227 "L'écran n'est pas auto-réductible" +228 "L'écran n'est pas auto-extensible" +230 "Vous ne pouvez pas mettre cet écran en attente" +231 "Interrompu : les touches affectées ont été abandonnées" +232 "vi : le tampon temporaire n' a pas été libéré" +233 "Ce terminal n'a pas de touche %s" +234 "Vous ne pouvez spécifier qu'un seul tampon" +235 "Nombre supérieur à %lu" +236 "Interrompu" +237 "Impossible de créer un fichier temporaire" +238 "Avertissement : %s n'est pas un fichier standard" +239 "%s déjà verrouillé, session en lecture seule" +240 "%s: supprimer" +241 "%s: fermer" +242 "%s: supprimer" +243 "%s: supprimer" +244 "Fichier en lecture seule, non écrit, utiliser ! pour outrepasser" +245 "Fichier en lecture seule, non écrit" +246 "%s existe, non écrit; utiliser ! pour outrepasser" +247 "%s existe, non écrit" +248 "Fichier partiel, non écrit; utiliser ! pour outrepasser" +249 "Fichier partiel, non écrit" +250 "%s: fichier modifié plus récemment que cet exemplaire; utiliser ! pour outrepasser" +251 "%s: fichier modifié plus récemment que cet exemplaire" +252 "%s: interdiction d'écriture non disponible" +253 "Ecriture en cours..." +254 "%s: AVERTISSEMENT : FICHIER TRONQUÉ" +255 "Première marque de ce groupe déjà atteinte" +256 "%s: nouveau fichier : %lu lignes, %lu caractères" +257 "%s: %lu lignes, %lu caractères" +258 "%s étendue à trop de noms de fichiers" +259 "%s: pas un fichier standard" +260 "%s: ne vous appartient pas" +261 "%s: accessible par un utilisateur autre que son propriétaire" +262 "Fichier modifé depuis la dernière écriture complète ; écrire ou utiliser ! pour outrepasser " +263 "Fichier modifé depuis la dernière écriture complète ; écrire ou utiliser :edit! pour outrepasser" +264 "Fichier modifé depuis la dernière écriture complète ; écrire ou utiliser ! pour outrepasser" +265 "Fichier temporaire ; quitter annulera les modifications" +266 "Fichier en lecture seule ; les modifications ne sont pas écrites automatiquement" +267 "Journal redémarré" +268 "confirmer ? [ynq]" +269 "Appuyez sur n'importe quelle touche pour continuer : " +270 "Appuyez sur n'importe quelle touche pour continuer [: pour entrer plus de commandes ex] : " +271 "Appuyez sur n'importe quelle touche pour continuer [q pour Quitter]: " +272 "Cette forme de %s nécessite l'interface de terminal ex" +273 "Entrée de mode entrée ex." +274 "La commande a échoué, aucun fichier n'a encore été lu." +275 "cont?" +276 "Evénement imprévu de caractère" +277 "Evénement imprévu de fin-de-fichier" +278 "Pas de correspondances pour cette requête" +279 "Evénement imprévu d'interruption" +280 "Evénement quitter imprévu" +281 "Evénement imprévu de rafraîchissement" +282 "La dernière marque de ce groupe a déjà été atteinte" +283 "La commande %s nécessite l'interface de terminal ex" +284 "Cette forme de %s n'est pas reconnue quand l'option d'édition protégée est activée" +285 "Evénement imprévu de chaîne" +286 "Evénement imprévu de délai imparti" +287 "Evénement d'écriture imprévu" +289 "Les expansions du shell ne sont pas reconnues quand l'option d'édition protégée est activée" +290 "La commande %s n'est pas reconnue quand l'option d'édition protégée est activée" +291 "Définition : l'option %s ne peut pas être désactivée" +292 "Affichage trop petit." +293 "ajouté" +294 "changé" +295 "supprimé" +296 "joint" +297 "déplacé" +298 "décalé" +299 "coupé" +300 "ligne" +301 "lignes" +302 "Vi n'a pas été chargé avec un interprétateur Tcl" +303 "Ficher modifié depuis le dernier enregistrement." +304 "L'expansion du shell a échoué" +305 "Pas d'option d'édition %s spécifiée" +306 "Vi n'a pas été chargé avec un interprétateur Perl" +307 "Pas de commande ex à exécuter" +308 "Entrez pour exécuter une commande, :q pour quitter" +309 "Utiliser \"cscope help\" pour obtenir de l'aide" +310 "Aucune connexion cscope n'est lancée" +311 "%s : type de recherche inconnu : utiliser un des %s" +312 "%d : Il n'existe pas de telle session cscope" +313 "Définition : l'option %s ne peut jamais être activée" +314 "Définition : l'option %s ne peut jamais être définie sur 0" +315 "%s: joints : %lu lignes, %lu caractères" +316 "événement imprévu de redimensionnement" +317 "%d fichiers à éditer" diff --git a/usr.bin/vi/catalog/german.UTF-8.base b/usr.bin/vi/catalog/german.UTF-8.base new file mode 100644 index 00000000000..956fd82fd9a --- /dev/null +++ b/usr.bin/vi/catalog/german.UTF-8.base @@ -0,0 +1,306 @@ +002 "Zeilenlängen-Überlauf" +003 "kann Zeile %lu nicht löschen" +004 "kann an Zeile %lu nicht anfügen" +005 "kann in Zeile %lu nicht einfügen" +006 "kann Zeile %lu nicht speichern" +007 "kann letzte Zeile nicht lesen" +008 "Fehler: kann Zeile %lu nicht wiederherstellen" +009 "Protokolldatei" +010 "Keine Protokollierung aktiv, Rückgängigmachen nicht möglich" +011 "Keine Änderungen rückgängig zu machen" +012 "Keine Protokollierung aktiv, Rückgängigmachen nicht möglich" +013 "Keine Protokollierung aktiv, Wiederholung von Änderungen nicht möglich" +014 "Keine Änderungen zu wiederholen" +015 "%s/%d: Protokollschreibfehler" +016 "Vi's Standardein- und -ausgabe muß ein Terminal sein" +017 "Marke %s: nicht gesetzt" +018 "Marke %s: die Zeile wurde gelöscht" +019 "Marke %s: Cursorposition existiert nicht mehr" +020 "Fehler: " +021 "neue Datei" +022 "Name geändert" +023 "geändert" +024 "nicht geändert" +025 "NICHT GELOCKT" +026 "nur zum Lesen" +027 "Zeile %lu von %lu [%ld%%]" +028 "leere Datei" +029 "Zeile %lu" +030 "Die Datei %s ist kein Meldungskatalog" +031 "Setzen der Voreinstellung für %s Option nicht möglich" +032 "Benutzung: %s" +033 "set: keine %s Option: 'set all' zeigt alle Optionen mit Werten an" +034 "set: der [no]%s Option kann kein Wert zugewiesen werden" +035 "set: %s ist keine Boolsche Option" +036 "set: %s Option: %s" +037 "set: %s Option: %s: Werte-Überlauf" +038 "set: %s Option: %s ist eine ungültige Zahl" +039 "set: %s ist keine Boolsche Option" +040 "Bildschirm hat zu wenig Spalten, weniger als %d" +041 "Bildschirm hat zu viele Spalten, mehr als %d" +042 "Bildschirm hat zu wenig Zeilen, weniger als %d" +043 "Bildschirm hat zu viele Zeilen, mehr als %d" +044 "Die lisp-Option ist nicht implementiert" +045 "Meldungen nicht abgeschaltet: %s" +046 "Meldungen nicht eingeschaltet: %s" +047 "Die %s-Option muß Gruppen zu zwei Zeichen enthalten" +053 "Der Standardpuffer ist leer" +054 "Puffer %s ist leer" +055 "Dateien mit newlines im Namen sind nicht wiederherstellbar" +056 "Änderungen nicht wiederherstellbar, falls die Editorsitzung schiefgeht" +057 "kopiere Datei für Wiederherstellung ..." +058 "Rettungsmechanismus funktioniert nicht: %s" +059 "Änderungen nicht wiederherstellbar, falls die Editorsitzung schiefgeht" +060 "Sicherung der Datei gescheitert: %s" +061 "kopiere Datei für Wiederherstellung ..." +062 "Informationen über den Benutzer mit id %u nicht gefunden" +063 "Wiederherstellungsdatei kann nicht gelockt werden" +064 "Puffer der Wiederherstellungsdatei übergelaufen" +065 "Wiederherstellungsdatei" +066 "%s: Wiederherstellungsdatei hat falsches Format" +067 "%s: Wiederherstellungsdatei hat falsches Format" +068 "Keine von Ihnen lesbaren Dateien mit Namen %s zum Wiederherstellen" +069 "Es gibt ältere Versionen dieser Datei von Ihnen zum Wiederherstellen" +070 "Sie haben noch andere Dateien zum Wiederherstellen" +071 "schicke keine email: %s" +072 "Datei leer; nichts zu suchen" +073 "Dateiende erreicht, ohne das Suchmuster zu finden" +074 "Kein altes Suchmuster" +075 "Suchmuster nicht gefunden" +076 "Dateianfang erreicht, ohne das Suchmuster zu finden" +077 "Suche beginnt von vorn" +078 "suche ..." +079 "Keine nichtdruckbaren Zeichen gefunden" +080 "Unbekannter Kommandoname" +082 "%s: Kommando im ex-Modus nicht verfügbar" +083 "Anzahl darf nicht Null sein" +084 "%s: falsche Zeilenangabe" +085 "Interner Syntaxtabellenfehler (%s: %s)" +086 "Benutzung: %s" +087 "%s: temporärer Puffer nicht freigegeben" +088 "Flagoffset vor Zeile 1" +089 "Flagoffset hinter dem Dateiende" +090 "@ mit Bereich lief, als Datei/Anzeige geändert wurde" +091 "globales oder v-Kommando lief, als Datei/Anzeige geändert wurde" +092 "Ex-Kommando mißlungen: restliche Kommandoeingabe ignoriert" +093 "Ex-Kommando mißlungen: umdefinierte Tasten ungültig" +094 "Die zweite Adresse ist kleiner als die erste" +095 "Kein Markenname angegeben" +096 "\\ ohne folgenden / oder ?" +097 "Referenz auf eine negative Zeilennummer" +098 "Das Kommando %s ist unbekannt" +099 "Adreßwert zu groß" +100 "Adreßwert zu klein" +101 "Ungültige Adreßkombination" +102 "Ungültige Adresse: nur %lu Zeilen in der Datei" +103 "Ungültige Adresse: die Datei ist leer" +104 "Das Kommando %s erlaubt keine Adresse 0" +105 "Keine Abkürzungen zum Anzeigen" +106 "Abkürzungen müssen mit einem \"Wort\"-Zeichen enden" +107 "Abkürzungen dürfen keine Tabulator- oder Leerzeichen enthalten" +108 "In Abkürzungen dürfen außer am Ende Wort- und Nicht-Wort-Zeichen nicht gemischt werden" +109 "\"%s\" ist keine Abkürzung" +110 "Vi Kommando mißlungen: umdefinierte Tasten ungültig" +111 "Keine weiteren Dateien zu editieren" +112 "Keine vorhergehenden Dateien zu editieren" +113 "Keine vorhergehenden Dateien für rewind" +114 "Keine Dateiliste zum Anzeigen" +115 "Kein vorhergehendes Kommando, um \"!\" zu ersetzen" +116 "Kein Dateiname für %%" +117 "Kein Dateiname für #" +118 "Fehler: execl: %s" +119 "E/A-Fehler: %s" +120 "Datei wurde seit dem letzten vollständigen Schreiben geändert; schreibe oder verwende ! zum ignorieren" +121 "Kann Homedirectory nicht bestimmen" +122 "Neues aktuelles Directory: %s" +123 "Keine Puffer anzuzeigen" +124 "Das Kommando %s kann nicht als Teil eines global oder v Kommandos verwendet werden" +125 "%s/%s: nicht gelesen: gehört weder Ihnen noch root" +126 "%s/%s: nicht gelesen: gehört nicht Ihnen" +127 "%s/%s: nicht gelesen: anderer Benutzer als Eigentümer hat Schreibrecht" +128 "%s: nicht gelesen: gehört weder Ihnen noch root" +129 "%s: nicht gelesen: gehört nicht Ihnen" +130 "%s: nicht gelesen: anderer Benutzer als Eigentümer hat Schreibrecht" +131 "Keine folgenden Zeilen zum Verbinden" +132 "Kein input-Map Eintrag" +133 "Kein command-Map Eintrag" +134 "Das %s Zeichen kann nicht umgemappt werden" +135 "\"%s\" ist momentan nicht gemappt" +136 "Markennamen müssen einzelne Buchstaben sein" +137 "%s existiert, nicht geschrieben; verwende ! zum Überschreiben" +138 "Neue .exrc Datei: %s. " +139 "Zielzeile ist innerhalb des Verschiebebereichs" +140 "Das open Kommando verlangt, daß die open Option gesetzt ist" +141 "Das open Kommando ist nocht nicht implementiert" +142 "Rettung dieser Datei nicht möglich" +143 "Datei gerettet" +144 "%s wurde in zu viele Dateinamen expandiert" +145 "Nur reguläre Dateien und named pipes können gelesen werden" +146 "%s: Locken zum Lesen war nicht möglich" +147 "lese ..." +148 "%s: %lu Zeilen, %lu Zeichen" +149 "Keine Hintergrundanzeigen vorhanden" +150 "Das script Kommando ist nur im vi Modus verfügbar" +151 "Kein Kommando auszuführen" +152 "shiftwidth Option auf 0 gesetzt" +153 "Anzahl-Überlauf" +154 "Anzahl-Unterlauf" +155 "Regulärer Ausdruck angegeben; r Flag bedeutungslos" +156 "Die #, l und p Flags können im vi Modus nicht mit dem c Flag kombiniert werden" +157 "Keine Übereinstimmung gefunden" +158 "Kein vorhergehender 'tag' angegeben" +159 "Weniger als %s Einträge auf dem 'tag'-Stack; verwende :display t[ags]" +160 "Keine Datei namens %s auf dem 'tag'-Stack; verwende :display t[ags]" +161 "Drücke Enter um fortzufahren: " +162 "%s: 'tag' nicht gefunden" +163 "%s: kaputter 'tag' in %s" +164 "%s: die Zeilennummer des 'tag' ist hinter dem Dateiende" +165 "Der 'tag' Stack ist leer" +166 "%s: Suchmuster nicht gefunden" +167 "%d weitere Dateien zu editieren" +168 "Puffer %s ist leer" +169 "Bestätige Änderung [n]" +170 "Unterbrochen." +171 "Kein vorhergehender Puffer zum Ausführen" +172 "Kein vorhergehender regulärer Ausdruck" +173 "Das Kommando %s verlangt, daß bereits eine Datei eingelesen wurde" +174 "Benutzung: %s" +175 "Das visual Kommando verlangt daß die open Option gesetzt ist" +177 "Leere Datei" +178 "Keine vorhergehende F, f, T oder t Suche" +179 "%s nicht gefunden" +180 "Keine vorhergehende Datei zu editieren" +181 "Cursor nicht auf einer Zahl" +182 "Sich ergebende Zahl ist zu groß" +183 "Sich ergebende Zahl ist zu klein" +184 "Kein korrespondierendes Zeichen in dieser Zeile" +185 "Korrespondierendes Zeichen nicht gefunden" +186 "Keine Zeichen zu ersetzen" +187 "Keine andere Anzeige zum Hinschalten" +188 "Zeichen nach Suchmuster, Zeilenoffset und/oder z Kommando" +189 "Kein altes Suchmuster" +190 "Suche zur ursprünglichen Position zurückgekehrt" +191 "Abkürzung überschreitet Expansionsgrenze: Zeichen weggelassen" +192 "Nicht erlaubtes Zeichen; maskiere zum Eingeben" +193 "Bereits am Anfang der Eingabe" +194 "Keine weiteren Zeichen zu löschen" +195 "Bewegung hinter das Dateiende" +196 "Bewegung hinter das Zeilenende" +197 "Keine Cursorbewegung gemacht" +198 "Bereits am Dateianfang" +199 "Bewegung vor den Dateianfang" +200 "Bereits in der ersten Spalte" +201 "Puffer sollen vor dem Kommando angegeben werden" +202 "Bereits am Dateiende" +203 "Bereits am Zeilenende" +204 "%s ist kein vi Kommando" +205 "Benutzung: %s" +206 "Keine Zeichen zu löschen" +207 "Das Q Kommando benötigt das ex Terminal Interface" +208 "Kein Kommando zu wiederholen" +209 "Die Datei ist leer" +210 "%s kann nicht als Bewegungskommando verwendet werden" +211 "Bereits im Kommando-Modus" +212 " Cursor nicht auf einem Wort" +214 "Wert der Window Option ist zu groß, max ist %u" +215 "Anhängen" +216 "Ändern" +217 "Kommando" +218 "Einfügen" +219 "Ersetzen" +220 "Bewegung hinter das Anzeigenende" +221 "Bewegung vor den Anzeigenanfang" +222 "Anzeige muß für Bildschirmteilung größer als %d sein" +223 "Keine Hintergrundanzeigen vorhanden" +224 "Es gibt keine Hintergrundanzeige, die eine Datei namens %s editiert" +225 "Die einzige dargestellte Anzeige kann nicht in den Hintergrund gebracht werden" +226 "Die Anzeige kann nur auf %d Zeilen verkleinert werden" +227 "Die Anzeige kann nicht verkleinert werden" +228 "Die Anzeige kann nicht vergrößert werden" +230 "Diese Anzeige kann nicht gestopt werden" +231 "Unterbrochen: umdefinierte Tasten ungültig" +232 "vi: temporärer Puffer nicht freigegeben" +233 "Dieses Terminal hat keine %s Taste" +234 "es kann nur ein Puffer angegeben werden" +235 "Zahl größer als %lu" +236 "Unterbrochen" +237 "Nicht möglich, temporäre Datei anzulegen" +238 "Warnung: %s ist keine reguläre Datei" +239 "%s ist bereits gelockt, Editorsitzung schreibgeschützt" +240 "%s: löschen" +241 "%s: schließen" +242 "%s: löschen" +243 "%s: löschen" +244 "Datei nicht schreibbar, nicht geschrieben; verwende ! zum Überschreiben" +245 "Datei nicht schreibbar, nicht geschrieben" +246 "%s existiert, nicht geschrieben; verwende ! zum Überschreiben" +247 "%s existiert, nicht geschrieben" +248 "Teil der Datei, nicht geschrieben; verwende ! zum Überschreiben" +249 "Teil der Datei, nicht geschrieben" +250 "%s: Datei wurde später als diese Kopie verändert; verwende ! zum Überschreiben" +251 "%s: Datei wurde später als diese Kopie verändert" +252 "%s: Locken zum Schreiben war nicht möglich" +253 "schreibe ..." +254 "%s: WARNUNG: DATEI ABGESCHNITTEN" +255 "Bereits am ersten 'tag' dieser Gruppe" +256 "%s: neue Datei: %lu Zeilen, %lu Zeichen" +257 "%s: %lu Zeilen, %lu Zeichen" +258 "%s wurde in zu viele Dateinamen expandiert" +259 "%s: keine reguläre Datei" +260 "%s: gehört nicht Ihnen" +261 "%s: anderer Benutzer als Eigentümer hat Zugriff" +262 "Datei wurde seit dem letzten vollständigen Schreiben geändert; schreibe oder verwende ! zum ignorieren" +263 "Datei wurde seit dem letzten vollständigen Schreiben geändert; schreibe oder verwende :edit! zum ignorieren" +264 "Datei wurde seit dem letzten vollständigen Schreiben geändert; schreibe oder verwende ! zum ignorieren" +265 "Datei ist temporär; beim Verlassen gehen die Änderungen verloren" +266 "Datei ist schreibgeschützt, Änderungen werden nicht automatisch geschrieben" +267 "Portokollierung neu begonnen" +268 "bestätigen [ynq]" +269 "Drücke beliebige Taste um fortzufahren" +270 "Drücke beliebige Taste um fortzufahren [: um weitere Kommandos einzugeben]: " +271 "Drücke beliebige Taste um fortzufahren [q zum Verlassen]: " +272 "Diese Form von %s benötigt das ex Terminal-Interface" +273 "Gehe in ex Eingabe-Modus.\n" +274 "Kommando schiefgelaufen, noch keine Datei eingelesen" +275 " weiter?" +276 "unerwartetes Zeichen - Ereignis" +277 "unerwartetes Dateiende - Ereignis" +278 "Keine Position zum Anspringen gefunden" +279 "unerwartetes Unterbrechungs - Ereignis" +280 "unerwartetes Verlassen - Ereignis" +281 "unerwartetes Wiederherstellungs - Ereignis" +282 "Bereits am letzten 'tag' dieser Gruppe" +283 "Das %s Kommando benötigt das ex Terminal-Interface" +284 "Diese Form von %s wird nicht unterstützt wenn die 'secure edit' - Option gesetzt ist" +285 "unerwartetes Zeichenketten - Ereignis" +286 "unerwartetes timeout - Ereignis" +287 "unerwartetes Schreibe - Ereignis" +289 "Shell Expandierungen nicht unterstützt wenn die 'secure edit' - Option gesetzt ist" +290 "Das %s Kommando wird nicht unterstützt wenn die 'secure edit' - Option gesetzt ist" +291 "set: %s kann nicht ausgeschaltet werden" +292 "Anzeige zu klein." +293 "angefügt" +294 "geändert" +295 "gelöscht" +296 "verbunden" +297 "verschoben" +298 "geschoben" +299 "in Puffer geschrieben" +300 "Zeile" +301 "Zeilen" +302 "Vi wurde nicht mit dem Tcl Interpreter gelinkt" +303 "Datei wurde seit dem letzten Schreiben verändert." +304 "Shell Expansion hat nicht geklappt" +305 "Es ist keine %s Editieroption angegeben" +306 "Vi wurde nicht mit einem Perl Interpreter geladen" +307 "Kein ex Kommando auszuführen" +308 "Drücke um ein Kommando auszuführen, :q zum verlassen" +309 "Verwende \"cscope help\" für Hilestellung" +310 "Keine cscope Verbindung aktiv" +311 "%s: unbekannter Suchtyp: verwende einen aus %s" +312 "%d: keine solche cscope Verbindung" +313 "set: die %s Option kann nicht eingeschaltet werden" +314 "set: die %s Option kann nicht auf 0 gesetzt werden" +315 "%s: angefügt: %lu Zeilen, %lu Zeichen" +316 "unerwartetes Größenveränderungs - Ereignis" +317 "%d Dateien zu edieren" diff --git a/usr.bin/vi/catalog/polish.UTF-8.base b/usr.bin/vi/catalog/polish.UTF-8.base new file mode 100644 index 00000000000..9648e78ced3 --- /dev/null +++ b/usr.bin/vi/catalog/polish.UTF-8.base @@ -0,0 +1,306 @@ +002 "Zbyt dÅ‚uga linia" +003 "nie można usunąć linii %lu" +004 "nie można dodać do linii %lu" +005 "nie można wstawić w linii %lu" +006 "nie można zachować linii %lu" +007 "nie można pobrać ostatniej linii" +008 "Błąd: nie można pobrać linii %lu" +009 "Plik logu" +010 "Brak logowania, anulowanie (undo) niemożliwe" +011 "Brak zmian do anulowania" +012 "Brak logowania, anulowanie (undo) niemożliwe" +013 "Brak logowania, nie można przeglÄ…dać do przodu" +014 "Brak zmian do powtórzenia" +015 "%s/%d: błąd zapisu do logu" +016 "Standardowe wejÅ›cie i wyjÅ›cie vi musi być terminalem" +017 "Znacznik %s: nie ustawiony" +018 "Znacznik %s: linia zostaÅ‚a usuniÄ™ta" +019 "Znacznik %s: pozycja kursora już nie istnieje" +020 "Błąd: " +021 "nowy plik" +022 "nazwa zostaÅ‚a zmieniona" +023 "zmieniony" +024 "nie zmieniony" +025 "ODBLOKOWANY" +026 "tylko do odczytu" +027 "linia %lu z %lu [%ld%%]" +028 "pusty plik" +029 "linia %lu" +030 "Plik %s nie jest katalogiem komunikatów" +031 "Nie można ustawić domyÅ›lnej opcji %s" +032 "Sposób użycia: %s" +033 "set: brak opcji %s: 'set all' wyÅ›wietla wartoÅ›ci opcji" +034 "set: opcja no]%s nie pobiera wartoÅ›ci" +035 "set: opcja %s nie jest zmiennÄ… boolowskÄ…" +036 "set: opcja %s: %s" +037 "set: opcja %s: %s: przepeÅ‚nienie" +038 "set: opcja %s: %s nie jest poprawnÄ… liczbÄ…" +039 "set: opcja %s nie jest zmiennÄ… boolowskÄ…" +040 "Zbyt maÅ‚a liczba kolumn ekranu, mniejsza niż %d" +041 "Zbyt duża liczba kolumn, wiÄ™ksza niż %d" +042 "Zbyt maÅ‚a liczba wierszy ekranu, mniejsza niż %d" +043 "Zbyt duża liczba wierszy ekranu, wiÄ™ksza niż %d" +044 "Opcja lisp nie jest zaimplementowana" +045 "komunikaty nie wyłączone: %s" +046 "komunikaty nie włączone: %s" +047 "Opcja %s musi skÅ‚adać siÄ™ z dwóch grup symboli" +053 "DomyÅ›lny bufor jest pusty" +054 "Bufor %s jest pusty" +055 "Pliki z symbolem nowego wiersza w nazwie nie mogÄ… być odtworzone" +056 "Zmiany nie do odtworzenia, jeÅ›li sesja zostanie przerwana" +057 "Tworzenie kopii zapasowej..." +058 "Błąd podczas zachowywania: %s" +059 "Zmiany nie do odtworzenia, jeÅ›li sesja zostanie przerwana" +060 "Błąd podczas tworzenia kopii zapasowej: %s" +061 "Tworzenie kopii zapasowej..." +062 "Nie znaleziono informacji o użytkowniku numer %u" +063 "Nie można zablokować kopii zapasowej" +064 "PrzepeÅ‚nienie bufora kopii zapasowej" +065 "Kopia zapasowa" +066 "%s: błędna kopia zapasowa" +067 "%s: błędna kopia zapasowa" +068 "Brak plików o nazwie %s, które mógÅ‚byÅ› odczytać, do odzyskania" +069 "IstniejÄ… starsze wersje tego pliku, które można odzyskać" +070 "IstniejÄ… inne pliki, które można odzyskać" +071 "nie wysyÅ‚am poczty: %s" +072 "Pusty plik; nie ma czego szukać" +073 "Dotarto do koÅ„ca pliku bez znalezienia szukanego wzorca" +074 "Brak poprzedniego wzorca poszukiwaÅ„" +075 "Nie znaleziono wzorca" +076 "Dotarto do poczÄ…tku pliku bez znalezienia szukanego wzorca" +077 "Poszukiwanie od poczÄ…tku" +078 "Szukam..." +079 "Nie znaleziono niedrukowalnych znaków" +080 "Nieznana nazwa polecenia" +082 "%s: polecenie niedostÄ™pne w trybie edytora ex" +083 "Liczba nie może być zerem" +084 "%s: błędny numer linii" +085 "WewnÄ™trzny błąd tablicy skÅ‚adni (%s: %s)" +086 "Sposób użycia: %s" +087 "%s: tymczasowy bufor nie zostaÅ‚ zwolniony" +088 "PrzesuniÄ™cie przed liniÄ™ 1" +089 "PrzesuniÄ™cie poza koniec pliku" +090 "@ with range running when the file/screen changed" +091 "Wykonywano polecenie globalne/wizualne podczas zmiany pliku/ekranu" +092 "Błąd polecenie ex: pozostaÅ‚e polecenia anulowane" +093 "Błąd polecenia ex: mapowanie klawiszy anulowane" +094 "Drugi adres jest mniejszy od pierwszego" +095 "Nie podano nazwy znacznika" +096 "\\ bez / lub ?" +097 "Odniesienie do linii o numerze mniejszym od 0" +098 "Nieznane polecenie %s" +099 "PrzepeÅ‚nienie wartoÅ›ci adresu" +100 "Niedomiar wartoÅ›ci adresu" +101 "Niedozwolona kombinacja adresu" +102 "Niedozwolony adres: jest tylko %lu linii w pliku" +103 "Niedozwolony adres: plik jest pusty" +104 "Polecenie %s nie zezwala na użycie adresu 0" +105 "Brak skrótów do wyÅ›wietlenia" +106 "Skróty muszÄ… siÄ™ koÅ„czyć symbolem \"sÅ‚owo\"" +107 "Skróty nie mogÄ… zawierać spacji lub tabulacji" +108 "Skróty nie mogÄ… przeplatać symboli sÅ‚owo/nie-sÅ‚owo, z wyjÄ…tkiem na koÅ„cu linii" +109 "\"%s\" nie jest skrótem" +110 "Błąd polecenia vi: mapowanie klawiszy odrzucone" +111 "Nie ma wiÄ™cej plików do edycji" +112 "Nie ma poprzednich plików do edycji" +113 "Nie ma poprzednich plików do przewiniÄ™cia wstecz" +114 "Nie ma listy plików do wyÅ›wietlenia" +115 "Nie ma poprzedniego polecenia do zastÄ…pienia \"!\"" +116 "Nie ma nazwy pliku do zastÄ…pienia %%" +117 "Nie ma nazwy pliku do zastÄ…pienia #" +118 "Błąd: execl: %s" +119 "Błąd I/O: %s" +120 "Plik zmodyfikowany od ostatniego zapisu; zapisz lub użyj ! żeby wymusić" +121 "Nie mozna znaleźć katalogu domowego (home)" +122 "Nowy katalog bieżący: %s" +123 "Nie ma buforów edycji do wyÅ›wietlenia" +124 "Polecenie %s nie może być użyte jako część polecenia globalnego lub wizualnego" +125 "%s/%s: nie wczytany: nie należy do ciebie ani do roota" +126 "%s/%s: nie wczytany: nie należy do ciebie" +127 "%s/%s: nie wczytany: inny użytkownik niż wÅ‚aÅ›ciciel ma prawo do zapisu" +128 "%s: nie wczytany: nie należy do ciebie ani do roota" +129 "%s: nie wczytany: nie należy do ciebie" +130 "%s: nie wczytany: inny uzytkownik niż wÅ‚aÅ›ciciel ma prawo do zapisu" +131 "Brak kolejnych linii do połączenia" +132 "Brak pozycji map wejÅ›ciowych" +133 "Brak pozycji map poleceÅ„" +134 "Znak %s nie może być przemapowany" +135 "\"%s\" obecnie nie jest mapowany" +136 "Nazwa znacznika musi być pojedyÅ„czÄ… literÄ…" +137 "%s istnieje, nie zapisany; użyj ! żeby wymusić" +138 "Nowy plik exrc: %s" +139 "Linia docelowa jest wewnÄ…trz przenoszonego obszaru" +140 "Polecenie open wymaga ustawienia opcji open" +141 "Polecenie open jest niezaimplementowane" +142 "Niemożliwe jest zachowanie tego pliku" +143 "Plik zachowany" +144 "%s: rozwija siÄ™ na zbyt wiele nazw plików" +145 "Tylko zwykÅ‚e pliki i nazwane łącza (named pipes) można czytać" +146 "%s: nie można zaÅ‚ożyć blokady do odczytu" +147 "Czytanie..." +148 "%s: %lu linii, %lu znaków" +149 "Brak ekranów w tle do wyÅ›wietlenia" +150 "Polecenie script dostÄ™pne jest tylko w trybie vi" +151 "Brak polecenia do wykonania" +152 "Opcja shiftwidth ustawiona na 0" +153 "PrzepeÅ‚nienie licznika" +154 "Niedomiar licznika" +155 "Podano wyrażenie regularne; flaga r nie ma znaczenia" +156 "Flagi #, l oraz p nie mogÄ… być łączone z flagÄ… c w trybie vi" +157 "Nie znaleziono wzorca" +158 "Brak poprzedniego znacznika" +159 "Mniej niż %s na stosie znaczników; użyj :display t[ags]" +160 "Brak pliku %s na stosie znaczników do powrotu; użyj :display t[ags]" +161 "NaciÅ›nij Enter: " +162 "%s: znacznik nie znaleziony" +163 "%s: błędny znacznik w %s" +164 "%s: numer linii znacznika wykracza poza koniec pliku" +165 "Stos znaczników jest pusty" +166 "%s: nie znaleziono szukanego wzorca" +167 "%d plików jeszcze do edycji" +168 "Bufor %s jest pusty" +169 "Potwierdzasz zmianÄ™? [n]" +170 "Przerwany" +171 "Brak poprzedniego bufora do wykonania" +172 "Brak poprzedniego wyrażenia regularnego" +173 "Polecenie %s wymaga uprzedniego wczytania pliku" +174 "Sposób użycia: %s" +175 "Polecenie wizualne wymaga ustawienia opcji open" +177 "Pusty plik" +178 "Brak poprzedniego szukania F, f, T lub t" +179 "%s nie znaleziono" +180 "Brak poprzedniego pliku do edycji" +181 "Kursor nie na liczbie" +182 "Liczba wynikowa zbyt duża" +183 "Liczba wynikowa zbyt maÅ‚a" +184 "Brak pasujÄ…cych znaków w tej linii" +185 "Nie znaleziono pasujÄ…cych znaków" +186 "Brak znaków do zamiany" +187 "Brak innego ekranu do przełączenia" +188 "Znaki poza napisem do szukania, przesuniÄ™ciem linii i/lub poleceniem z" +189 "Brak poprzedniego wzorca szukania" +190 "Szukanie wróciÅ‚o do pierwotnej pozycji" +191 "Skrót przekroczyÅ‚ limit rozwiniÄ™cia: część znaków odrzucono" +192 "Niedozwolony znak; zacytuj, żeby wprowadzić" +193 "Już na poczÄ…tku wstawiania" +194 "Brak kolejnych znaków do usuniÄ™cia" +195 "PrzesuniÄ™cie poza koniec pliku" +196 "PrzesuniÄ™cie poza koniec linii" +197 "Nie wykonano przesuniÄ™cia kursora" +198 "Już na poczÄ…tku pliku" +199 "PrzesuniÄ™cie przed poczÄ…tek pliku" +200 "Już w pierwszej kolumnie" +201 "Należy podać bufory przed poleceniem" +202 "Już na koÅ„cu pliku" +203 "Już na koÅ„cu linii" +204 "%s nie jest poleceniem vi" +205 "Sposób użycia: %s" +206 "Brak znaków do usuniÄ™cia" +207 "Polecenie Q wymaga interfejsu terminala ex" +208 "Brak polecenia do powtórzenia" +209 "Plik jest pusty" +210 "%s nie może być użyte jako polecenie przesuniÄ™cia" +211 "Już w trybie poleceÅ„" +212 "Kursor nie na sÅ‚owie" +214 "WartoÅ›ci opcji okna zbyt duże, max to %u" +215 "Dołącz" +216 "ZmieÅ„" +217 "Polecenie" +218 "Wstaw" +219 "ZamieÅ„" +220 "PrzesuniÄ™cie poza koniec ekranu" +221 "PrzesuniÄ™cie przed poczÄ…tek ekranu" +222 "Ekran musi mieć wiÄ™cej niż %d linii, żeby go podzielić" +223 "Brak ekranów w tle" +224 "Nie ma ekranu w tle z edycjÄ… pliku o nazwie %s" +225 "Nie można schować w tle jedynego ekranu edycyjnego" +226 "Można zmniejszyć ekran tylko do %d wierszy" +227 "Ekran nie może być zmniejszony" +228 "Ekran nie może być zwiÄ™kszony" +230 "Ekran nie może być zawieszony" +231 "Przerwano: odrzucono mapowane klawisze" +232 "vi: nie zwolniono bufora tymczasowego" +233 "Ten terminal nie posiada klawisza %s" +234 "Można podać tylko jeden bufor" +235 "Liczba wiÄ™ksza niż %lu" +236 "Przerwano" +237 "Nie można utworzyć pliku tymczasowego" +238 "Uwaga: %s nie jest zwykÅ‚ym plikiem" +239 "%s już zablokowany, sesja tylko do odczytu" +240 "%s: usuÅ„" +241 "%s: zamknij" +242 "%s: usuÅ„" +243 "%s: usuÅ„" +244 "Plik tylko do odczytu, nie zapisany; użyj ! żeby wymusić" +245 "Plik tylko do odczytu, nie zapisany" +246 "%s istnieje, nie zapisany; użyj ! żeby wymusić" +247 "%s istnieje, nie zapisany" +248 "Plik częściowy, nie zapisany; użyj ! żeby wymusić" +249 "Plik częściowy, nie zapisany" +250 "%s: plik zmodyfikowany później, niż ta kopia; użyj ! żeby wymusić" +251 "%s: plik zmodyfikowany później, niż ta kopia" +252 "%s: nie można zablokować do pisania" +253 "Zapisywanie..." +254 "%s: OSTRZEÅ»ENIE: PLIK ZOSTAÅ OBCIĘTY" +255 "Już przy pierwszym znaczniku z tej grupy" +256 "%s: nowy plik: %lu linii, %lu znaków" +257 "%s: %lu linii, %lu znaków" +258 "%s rozwija siÄ™ na zbyt wiele nazw plików" +259 "%s: nie jest zwykÅ‚ym plikiem" +260 "%s: nie jesteÅ› wÅ‚aÅ›cicielem" +261 "%s: dostÄ™pny dla użytkownika innego niż wÅ‚aÅ›ciciel" +262 "Plik zmodyfikowany od ostatniego zapisu; zapisz lub użyj ! żeby wymusić" +263 "Plik zmodyfikowany od ostatniego zapisu; zapisz lub użyj :edit! żeby wymusić" +264 "Plik zmodyfikowany od ostatniego zapisu; zapisz lub użyj ! żeby wymusić" +265 "To jest plik tymczasowy; wyjÅ›cie anuluje zmiany" +266 "Plik tylko do odczytu, nie ma autozapisu zmian" +267 "Ponownie rozpoczÄ™to logowanie" +268 "potwierdzasz? [ynq]" +269 "NaciÅ›nij dowolny klawisz: " +270 "NaciÅ›nij dowolny klawisz [: żeby podać nastÄ™pne polecenia ex]: " +271 "NaciÅ›nij dowolny klawisz [q żeby zakoÅ„czyć]: " +272 "Ta forma %s wymaga interfejsu terminala ex" +273 "WchodzÄ™ w tryb wprowadzania ex." +274 "Błąd polecenia, jeszcze nie wczytano pliku." +275 " dalej?" +276 "Nieoczekiwany symbol" +277 "Nieoczekiwany koniec pliku" +278 "Brak wzorców pasujÄ…cych do zapytania" +279 "Nieoczekiwane przerwanie" +280 "Nieoczekiwane polecenie koniec" +281 "Nieoczekiwane polecenie odÅ›wieżenia" +282 "Już przy ostatnim znaczniku z tej grupy" +283 "Polecenie %s wymaga interfejsu terminala ex" +284 "Ta forma %s jest niedostÄ™pna, kiedy ustawiona jest opcja bezpiecznej edycji" +285 "Nieoczekiwany ciÄ…g znaków" +286 "Nieoczekiwany timeout" +287 "Nieoczekiwane polecenie zapisu" +289 "RozwiniÄ™cia shella niedostÄ™pne w trybie bezpiecznej edycji" +290 "Polecenie %s jest niedostÄ™pne w trybie bezpiecznej edycji" +291 "set: opcji %s nie można wyłączyć" +292 "Ekran zbyt maÅ‚y." +293 "dodano" +294 "zmieniono" +295 "usuniÄ™to" +296 "połączono" +297 "przeniesiono" +298 "przesuniÄ™to" +299 "wczytano" +300 "linia" +301 "linii" +302 "Vi nie zostaÅ‚ uruchomiony z interpreterem Tcl" +303 "Plik zmodyfikowany od ostatniego zapisu" +304 "Błąd rozwiniÄ™cia shella" +305 "Brak opcji %s edycji" +306 "Vi nie zostaÅ‚ uruchomiony z interpreterem Perl" +307 "Brak polecenia ex do wykonania" +308 "NaciÅ›nij żeby wykonac polecenie, :q żeby wyjść" +309 "Użyj \"cscope help\" w celu uzyskania pomocy" +310 "Brak aktywnych połączeÅ„ cscope" +311 "%s: nieznany typ poszukiwaÅ„: użyj jednego z %s" +312 "%d: brak takiej sesji cscope" +313 "set: nie można nigdy włączyć opcji %s" +314 "set: opcja %s nie może nigdy mieć wartoÅ›ci 0" +315 "%s: dodano: %lu linii, %lu znaków" +316 "Nieoczekiwane polecenie zmiany rozmiaru" +317 "%d plików do edycji" diff --git a/usr.bin/vi/catalog/ru_RU.UTF-8.base b/usr.bin/vi/catalog/ru_RU.UTF-8.base new file mode 100644 index 00000000000..b358015544e --- /dev/null +++ b/usr.bin/vi/catalog/ru_RU.UTF-8.base @@ -0,0 +1,311 @@ +002 "Переполнение Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ Ð´Ð»Ð¸Ð½Ñ‹ Ñтроки" +003 "невозможно удалить Ñтроку %lu" +004 "невозможно добавить к Ñтроке %lu" +005 "невозможно вÑтавить в Ñтроку %lu" +006 "невозможно Ñохранить Ñтроку %lu" +007 "невозможно доÑтать поÑледнюю Ñтроку" +008 "Ошибка: не удалоÑÑŒ получить Ñтроку %lu" +009 "Файл запиÑей" +010 "ЗапиÑи не велиÑÑŒ, невозможно отменить поÑледнюю команду" +011 "Ðет изменений Ð´Ð»Ñ Ð¾Ñ‚Ð¼ÐµÐ½Ñ‹" +012 "ЗапиÑи не велиÑÑŒ, невозможно отменить поÑледнюю команду" +013 "ЗапиÑи не велиÑÑŒ, невозможно проÑмотреть вперед" +014 "Ðет изменений Ð´Ð»Ñ Ð¿ÐµÑ€ÐµÐ´ÐµÐ»ÐºÐ¸" +015 "%s/%d: ошибка при запиÑи протокола" +016 "Стандартный ввод/вывод Ð´Ð»Ñ VI должен быть терминал" +017 "Отметка %s: не уÑтановлена" +018 "Отметка %s: Ñтрока была удалена" +019 "Отметка %s: позиции курÑора больше не ÑущеÑтвует" +020 "Ошибка: " +021 "новый файл" +022 "Ð¸Ð¼Ñ Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¾" +023 "модифицировано" +024 "не модифицировано" +025 "РÐЗБЛОКИРОВÐÐО" +026 "только чтение" +027 "Ñтрока %lu из %lu [%ld%%]" +028 "пуÑтой файл" +029 "Ñтрока %lu" +030 "Файл %s не ÑвлÑетÑÑ ÐºÐ°Ñ‚Ð°Ð»Ð¾Ð³Ð¾Ð¼ Ñообщений" +031 "Ðевозможно уÑтановить опцию %s по умолчанию" +032 "ИÑпользование: %s" +033 "Опции %s нет: 'set all' показывает вÑе возможные опции" +034 "set: [no]%s не принимает такого значениÑ" +035 "set: %s Ð¾Ð¿Ñ†Ð¸Ñ Ð½Ðµ ÑвлÑетÑÑ Ð»Ð¾Ð³Ð¸Ñ‡ÐµÑкой" +036 "set: %s опциÑ: %s" +037 "set: %s опциÑ: %s: значение переполнениÑ" +038 "set: неправильное значение %s" +039 "set: %s Ð¾Ð¿Ñ†Ð¸Ñ Ð½Ðµ ÑвлÑетÑÑ Ð»Ð¾Ð³Ð¸Ñ‡ÐµÑкой" +040 "КоличеÑтво колонок Ñкрана Ñлишком мало, меньше чем %d" +041 "КоличеÑтво колонок Ñкрана Ñлишком велико, больше чем %d" +042 "КоличеÑтво Ñтрок Ñкрана Ñлишком мало, меньше чем %d" +043 "КоличеÑтво Ñтрок Ñкрана Ñлишком велико, больше чем %d" +044 "ÐžÐ¿Ñ†Ð¸Ñ lisp отÑутÑтвует" +045 "Ð¡Ð¾Ð¾Ð±Ñ‰ÐµÐ½Ð¸Ñ Ð½Ðµ выключены: %s" +046 "Ð¡Ð¾Ð¾Ð±Ñ‰ÐµÐ½Ð¸Ñ Ð½Ðµ включены: %s" +047 "ÐžÐ¿Ñ†Ð¸Ñ %s должна ÑоÑтоÑть из групп Ñ Ð´Ð²ÑƒÐ¼Ñ Ñимволами" +053 "Стартовый буфер пуÑÑ‚" +054 "Буфер %s пуÑÑ‚" +055 "Файлы Ñ Ñимволами перевода Ñтроки в имени не могут быть воÑÑтановлены" +056 "Ð˜Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ð½Ðµ Ñохранены при крахе ÑеÑÑии" +057 "Копирование файла Ð´Ð»Ñ Ð²Ð¾ÑÑтановлениÑ..." +058 "Сохранение не удалоÑÑŒ: %s" +059 "Ð˜Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ð½Ðµ ÑохранÑÑŽÑ‚ÑÑ Ð¿Ñ€Ð¸ обрыве ÑеÑÑии" +060 "Сохранение копии файла не удалоÑÑŒ: %s" +061 "Копирование файла Ð´Ð»Ñ Ð²Ð¾ÑÑтановлениÑ..." +062 "Информации на Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ %u не найдено" +063 "Ðевозможно защитить ÑпаÑенный файл" +064 "Буфер воÑÑтановленного файла переполнен" +065 "ВоÑÑтановленный файл" +066 "%s: не до конца воÑÑтановленный файл" +067 "%s: не до конца воÑÑтановленный файл" +068 "Файлов Ñ Ð¸Ð¼ÐµÐ½ÐµÐ¼ %s, которые Ð’Ñ‹ можете читать, не ÑущеÑтвует" +069 "ЕÑть Ñтарые верÑии файла, которые можно воÑÑтановить" +070 "СущеÑтвуют другие файлы, которые можно воÑÑтановить" +071 "е-mail не поÑлан: %s" +072 "Файл пуÑÑ‚ - иÑкать нечего" +073 "ДоÑтигнут конец файла без Ð½Ð°Ñ…Ð¾Ð¶Ð´ÐµÐ½Ð¸Ñ Ð¾Ð±Ñ€Ð°Ð·Ñ†Ð° поиÑка" +074 "Ðе задан образец поиÑка" +075 "Образец поиÑка не найден" +076 "ДоÑтупно начало файла без Ð½Ð°Ñ…Ð¾Ð¶Ð´ÐµÐ½Ð¸Ñ Ð¾Ð±Ñ€Ð°Ð·Ñ†Ð° поиÑка" +077 "ПоиÑк зациклен" +078 "ПоиÑк..." +079 "Ðепечатных Ñимволов не найдено" +080 "ÐеизвеÑÑ‚Ð½Ð°Ñ ÐºÐ¾Ð¼Ð°Ð½Ð´Ð°" +082 "%s: команда не доÑтупна в режиме ex" +083 "Счетчик не может быть нулем" +084 "%s: неправильное указание Ñтроки" +085 "ВнутреннÑÑ Ð¾ÑˆÐ¸Ð±ÐºÐ° в ÑинтакÑиÑе (%s: %s)" +086 "ИÑпользование: %s" +087 "%s: временный буфер не иÑпользован" +088 "Метка поÑтавлена перед Ñтрокой 1" +089 "Метка поÑтавлена поÑле конца файла" +090 "@ Ñ Ð´Ð¸Ð°Ð¿Ð°Ð·Ð¾Ð½Ð¾Ð¼ выполнÑетÑÑ ÐºÐ¾Ð³Ð´Ð° файл/Ñкран изменены" +091 "ГлобальнаÑ/v команда выполнÑетÑÑ ÐºÐ¾Ð³Ð´Ð° файл/Ñкран изменены" +092 "Команда ex не удалаÑÑŒ: поÑледующие команды забыты" +093 "Команда ex не удалаÑÑŒ: назначенные клавиши забыты" +094 "Второй Ð°Ð´Ñ€ÐµÑ Ð¼ÐµÐ½ÑŒÑˆÐµ чем первый" +095 "Ðе указано название отметки" +096 "\\ не завершаетÑÑ / или ?" +097 "СÑылка к Ñтроке Ñ Ð½Ð¾Ð¼ÐµÑ€Ð¾Ð¼ меньше 0" +098 "Команда %s неизвеÑтна" +099 "Переполнение Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ Ð°Ð´Ñ€ÐµÑа" +100 "Ðедобор Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ Ð°Ð´Ñ€ÐµÑа" +101 "ÐедопуÑÑ‚Ð¸Ð¼Ð°Ñ ÐºÐ¾Ð¼Ð±Ð¸Ð½Ð°Ñ†Ð¸Ñ Ð² адреÑе" +102 "Ðеправильный адреÑ: вÑего %lu Ñтрок в файле" +103 "Ðеправильный адреÑ: файл пуÑÑ‚" +104 "Команда %s не может иÑпользовать Ð°Ð´Ñ€ÐµÑ 0" +105 "Ðббревиатуры отÑутÑтвуют" +106 "Ðббревиатуры должны заканчиватьÑÑ Ñимволом \"Ñлов\"" +107 "Ðббревиатуры не могут Ñодержать Ñимволы табулÑции или пробелы" +108 "Ðббревиатуры не могут ÑочетатьÑÑ Ñ Ñимволами Ñлов/не-Ñлов, за иÑключением конца Ñтроки" +109 "\"%s\" не ÑвлÑетÑÑ Ð°Ð±Ð±Ñ€ÐµÐ²Ð¸Ð°Ñ‚ÑƒÑ€Ð¾Ð¹" +110 "Vi команда не удалаÑÑŒ: назначенные клавиши забыты" +111 "Файлов Ð´Ð»Ñ Ñ€ÐµÐ´Ð°ÐºÑ‚Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ Ð±Ð¾Ð»ÑŒÑˆÐµ нет" +112 "ОтÑутÑтвие предыдущего файла Ð´Ð»Ñ Ñ€ÐµÐ´Ð°ÐºÑ‚Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ" +113 "ОтÑутÑтвие предыдущего файла Ð´Ð»Ñ Ð¿Ñ€Ð¾Ñмотра назад" +114 "Ðет файлов" +115 "ОтÑутÑтвие предыдущей команды Ð´Ð»Ñ Ð·Ð°Ð¼ÐµÐ½Ñ‹ \"!\"" +116 "ОтÑутÑтвие замены Ð´Ð»Ñ %%" +117 "ОтÑутÑтвие замены Ð´Ð»Ñ #" +118 "Ошибка: execl: %s" +119 "Ошибка ввода/вывода: %s" +120 "Файл изменен Ñ Ð¼Ð¾Ð¼ÐµÐ½Ñ‚Ð° поÑледней полной запиÑи: иÑпользуйте ! Ð´Ð»Ñ Ð¾Ð±Ñ…Ð¾Ð´Ð°" +121 "Ðевозможно найти домашний каталог" +122 "Ðовый каталог: %s" +123 "Ðет вырезанных буферов" +124 "Команда %s не может быть иÑпользована внутри глобальной или v команды" +125 "%s/%s: не открыт: не принадлежит Вам или root-у" +126 "%s/%s: не открыт: не принадлежит Вам" +127 "%s/%s: не открыт: возможноÑть запиÑи у пользователÑ, не ÑвлÑющегоÑÑ Ð²Ð»Ð°Ð´ÐµÐ»ÑŒÑ†ÐµÐ¼" +128 "%s: не Ñчитан: не принадлежит Вам или root-у" +129 "%s: не Ñчитан: не принадлежит Вам" +130 "%s: не Ñчитан: возможноÑть запиÑи у пользователÑ, не ÑвлÑющегоÑÑ Ð²Ð»Ð°Ð´ÐµÐ»ÑŒÑ†ÐµÐ¼" +131 "ПоÑледующие Ñтроки отÑутÑтвуют" +132 "ОтÑутÑтвие параметров ввода" +133 "ОтÑутÑтвие параметров команды" +134 "Символ %s не может быть переназначен" +135 "\"%s\" на данный момент не назначен" +136 "Ð˜Ð¼Ñ Ð¼ÐµÑ‚ÐºÐ¸ должно быть одним Ñимволом" +137 "%s ÑущеÑтвует, не запиÑан; иÑпользуйте ! Ð´Ð»Ñ Ð¾Ð±Ñ…Ð¾Ð´Ð°" +138 "Ðовый файл exrc: %s" +139 "Строка переноÑа находитÑÑ Ð²Ð½ÑƒÑ‚Ñ€Ð¸ параметров переноÑа" +140 "Команда open подразумевает уÑтановку опции open" +141 "Команда open не реализована" +142 "Защита файла невозможна" +143 "Файл защищен" +144 "%s раÑширилÑÑ Ð² Ñлишком большое количеÑтво имен файлов" +145 "Только обычные файлы или именованные каналы могут быть прочитаны" +146 "%s: защита на чтение была недоÑтупна" +147 "Чтение..." +148 "%s: %lu Ñтрок, %lu Ñимволов" +149 "Ðет теневых окон" +150 "Команда script иÑпользуетÑÑ Ñ‚Ð¾Ð»ÑŒÐºÐ¾ в режиме vi" +151 "Ðет команды Ð´Ð»Ñ Ð¸ÑполнениÑ" +152 "ÐžÐ¿Ñ†Ð¸Ñ shiftwidth уÑтановлена на 0" +153 "Переполнение Ñчетчика" +154 "Цикл выполнен не до конца" +155 "Указано регулÑрное выражение: флаг r не нужен" +156 "Флаги #, l и p не могут быть объединены Ñ Ñ„Ð»Ð°Ð³Ð¾Ð¼ c в режиме vi" +157 "Совпадений нет" +158 "Метка отÑутÑтвует" +159 "Ð’ Ñтеке меток запиÑей меньше, чем %s, иÑпользуйте :display t[ags]" +160 "Файла Ñ Ð¸Ð¼ÐµÐ½ÐµÐ¼ %s в Ñтеке меток нет; иÑпользуйте :display t[ags]" +161 "Ðажмите Enter чтобы продолжить: " +162 "%s: метка не найдена" +163 "%s: Ð¿Ð»Ð¾Ñ…Ð°Ñ Ð¼ÐµÑ‚ÐºÐ° в %s" +164 "%s: номер Ñтроки метки вышел за конец файла" +165 "Стек меток пуÑÑ‚" +166 "%s: образец поиÑка не найден" +167 "еще %d файлов Ð´Ð»Ñ Ñ€ÐµÐ´Ð°ÐºÑ‚Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ" +168 "Буфер %s пуÑÑ‚" +169 "Подтвердить изменениÑ? [n]" +170 "Прервано" +171 "ОтÑутÑтвие буфера Ð´Ð»Ñ Ð¸ÑпользованиÑ" +172 "Ðет предыдущего регулÑрного выражениÑ" +173 "Команда %s подразумевает наличие прочтенного файла" +174 "ИÑпользование: %s" +175 "Команда visual подразумевает обÑзательную уÑтановку опции open" +177 "ПуÑтой файл" +178 "Ðет предыдущего поиÑка F, f, T, или t" +179 "%s не найдено" +180 "Ðет предыдущего файла Ð´Ð»Ñ Ñ€ÐµÐ´Ð°ÐºÑ‚Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ" +181 "КурÑор Ñтоит не на цифре" +182 "Полученное чиÑло Ñлишком велико" +183 "Полученное чиÑло Ñлишком мало" +184 "ПодходÑщего Ñимвола нет на Ñтой Ñтроке" +185 "ПодходÑщий Ñимвол не найден" +186 "Ðет Ñимволов Ð´Ð»Ñ ÑƒÐ´Ð°Ð»ÐµÐ½Ð¸Ñ" +187 "Другого Ñкрана не ÑущеÑтвует" +188 "Символы поÑле Ñтроки Ð´Ð»Ñ Ð¿Ð¾Ð¸Ñка, ÑÐ¼ÐµÑ‰ÐµÐ½Ð¸Ñ Ñтроки и/или команды z" +189 "Прошлый образец поиÑка отÑутÑтвует" +190 "ПоиÑк завершилÑÑ Ð½Ð° начальной позиции" +191 "Ðббревиатура превыÑила лимит раÑширениÑ: Ñимволы забыты" +192 "Символ неправилен; квотируйте Ð´Ð»Ñ Ð²Ð²Ð¾Ð´Ð°" +193 "Уже на начале вÑтавки" +194 "Ðет Ñимволов Ð´Ð»Ñ ÑƒÐ´Ð°Ð»ÐµÐ½Ð¸Ñ" +195 "Передвижение за конец файла" +196 "Передвижение за конец Ñтроки" +197 "Движение Ñтроки не Ñделано" +198 "Уже на начале файла" +199 "Движение курÑора за начало файла" +200 "Уже в первой колонке" +201 "Буферы должны быть указаны до Ð²Ñ‹Ð¿Ð¾Ð»Ð½ÐµÐ½Ð¸Ñ ÐºÐ¾Ð¼Ð°Ð½Ð´Ñ‹" +202 "Уже на конце файла" +203 "Уже на конце Ñтроки" +204 "%s не ÑвлÑетÑÑ ÐºÐ¾Ð¼Ð°Ð½Ð´Ð¾Ð¹ VI" +205 "ИÑпользование: %s" +206 "Ðет Ñимволов Ð´Ð»Ñ ÑƒÐ´Ð°Ð»ÐµÐ½Ð¸Ñ" +207 "Команда Q требует ex Ð¸Ð½Ñ‚ÐµÑ€Ñ„ÐµÐ¹Ñ Ñ‚ÐµÑ€Ð¼Ð¸Ð½Ð°Ð»Ð°" +208 "Ðет команды Ð´Ð»Ñ Ð¿Ð¾Ð²Ñ‚Ð¾Ñ€Ð°" +209 "Файл пуÑÑ‚" +210 "Команда %s не может быть иÑпользована как команда перемещениÑ" +211 "Уже в командном режиме" +212 "КурÑор находитÑÑ Ð½Ðµ на Ñлове" +214 "Значение опции windows Ñлишком велико, макÑимальное значение = %u" +215 "Добавить" +216 "Изменить" +217 "Команда" +218 "Ð’Ñтавить" +219 "Заменить" +220 "Движение курÑора за конец Ñкрана" +221 "Движение курÑора за начало Ñкрана" +222 "Ð”Ð»Ñ Ñ€Ð°Ð·Ð´ÐµÐ»ÐµÐ½Ð¸Ñ Ñкран должен быть больше чем Ñтроки %d" +223 "Теневых окон нет" +224 "Ðе ÑущеÑтвует теневого окна Ñ Ñ€ÐµÐ´Ð°ÐºÑ‚Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð¸ÐµÐ¼ файла %s" +225 "Ð’Ñ‹ не можете Ñделать единÑтвенное окно теневым" +226 "Экран может быть Ñжат" +227 "Экран не может быть Ñжат только до %d Ñтрок" +228 "Экран не может быть раÑширен" +230 "Этот Ñкран не может быть приоÑтановлен" +231 "Прервано: назначенные клавиши забыты" +232 "vi: временный буфер не оÑвобожден" +233 "Данный тип терминала не имеет клавиши %s" +234 "Может быть указан только один буфер" +235 "ЧиÑло больше чем %lu" +236 "Прервано" +237 "Ðевозможно Ñоздать временный файл" +238 "Внимание: %s Ñпециальный файл" +239 "%s уже заблокирован, доÑтупен только на чтение" +240 "%s: удален" +241 "%s: закрыт" +242 "%s: удален" +243 "%s: удален" +244 "Файл только Ð´Ð»Ñ Ñ‡Ñ‚ÐµÐ½Ð¸Ñ, не запиÑан: ИÑпользуйте ! Ð´Ð»Ñ Ð¾Ð±Ñ…Ð¾Ð´Ð°" +245 "Файл только Ð´Ð»Ñ Ñ‡Ñ‚ÐµÐ½Ð¸Ñ, не запиÑан" +246 "%s ÑущеÑтвует, не запиÑан; иÑпользуйте ! Ð´Ð»Ñ Ð¾Ð±Ñ…Ð¾Ð´Ð°" +247 "%s ÑущеÑтвует, не запиÑан" +248 "ИÑпользуйте ! Ð´Ð»Ñ Ñ‡Ð°Ñтичной запиÑи файла" +249 "ЧаÑть файла, файл не запиÑан" +250 "%s: Файл изменÑлÑÑ Ð¿Ð¾Ð·Ð´Ð½ÐµÐµ, чем Ð´Ð°Ð½Ð½Ð°Ñ ÐºÐ¾Ð¿Ð¸Ñ: иÑпользуйте ! Ð´Ð»Ñ Ð¾Ð±Ñ…Ð¾Ð´Ð°" +251 "%s: Файл изменÑлÑÑ Ð¿Ð¾Ð·Ð´Ð½ÐµÐµ, чем Ð´Ð°Ð½Ð½Ð°Ñ ÐºÐ¾Ð¿Ð¸Ñ" +252 "%s: защита на запиÑÑŒ была недоÑтупна" +253 "ЗапиÑÑŒ..." +254 "%s: Ð’ÐИМÐÐИЕ: ФÐЙЛ УСЕЧЕÐ" +255 "Уже приÑутÑтвует в первой метке данной группы" +256 "%s: новый файл: %lu Ñтрок, %lu Ñимволов" +257 "%s: %lu Ñтрок, %lu Ñимволов" +258 "%s раÑширилÑÑ Ð² Ñлишком большое количеÑтво имен файлов" +259 "%s: Ñпециальный файл" +260 "%s: не принадлежит Вам" +261 "%s: доÑтупен не только Вам" +262 "Файл изменен Ñо времени поÑледней запиÑи: Ñохраните или иÑпользуйте ! Ð´Ð»Ñ Ð¾Ð±Ñ…Ð¾Ð´Ð°" +263 "Файл изменен Ñо времени поÑледней запиÑи: Ñохраните или иÑпользуйте :edit Ð´Ð»Ñ Ð¾Ð±Ñ…Ð¾Ð´Ð°" +264 "Файл изменен Ñо времени поÑледней запиÑи: Ñохраните или иÑпользуйте ! Ð´Ð»Ñ Ð¾Ð±Ñ…Ð¾Ð´Ð°" +265 "Файл временный: выход Ñотрет любые изменениÑ" +266 "Файл только Ð´Ð»Ñ Ñ‡Ñ‚ÐµÐ½Ð¸Ñ, автозапиÑÑŒ не Ñработала" +267 "ЗапиÑи начаты заново" +268 "подтвердить? [ynq]" +269 "Ðажмите любую клавишу чтобы продолжить: " +270 "Ðажмите любую клавишу чтобы продолжить [: чтобы ввеÑти еще ex команды]: " +271 "Ðажмите любую клавишу чтобы продолжить [q чтобы выйти]: " +272 "Эта форма %s требует ex Ð¸Ð½Ñ‚ÐµÑ€Ñ„ÐµÐ¹Ñ Ñ‚ÐµÑ€Ð¼Ð¸Ð½Ð°Ð»Ð°" +273 "Вход в режим ввода ex." +274 "Команда не удалаÑÑŒ, файл еще не прочтен." +275 " прод?" +276 "Ðеожиданное Ñимвольное Ñобытие" +277 "Ðеожиданное Ñобытие 'конец файла'" +278 "По запроÑу Ñовпадений нет" +279 "Ðеожиданное Ñобытие прерываниÑ" +280 "Ðеожиданное Ñобытие выхода" +281 "Ðеожиданное Ñобытие перериÑовки" +282 "Уже приÑутÑтвует в поÑледней метке данной группы" +283 "Команда %s требует ex Ð¸Ð½Ñ‚ÐµÑ€Ñ„ÐµÐ¹Ñ Ñ‚ÐµÑ€Ð¼Ð¸Ð½Ð°Ð»Ð°" +284 "Эта форма %s не поддерживаетÑÑ Ð¿Ð¾ÐºÐ° уÑтановлена Ð¾Ð¿Ñ†Ð¸Ñ Ð±ÐµÐ·Ð¾Ð¿Ð°Ñного редактированиÑ" +285 "Ðеожиданное Ñтроковое Ñобытие" +286 "Ðеожиданное Ñобытие таймаута" +287 "Ðеожиданное Ñобытие запиÑи" +288 "Ð”Ð»Ñ Ñ€Ð°Ð·Ð´ÐµÐ»ÐµÐ½Ð¸Ñ Ñкран должен быть больше чем Ñтолбцы %d" +289 "Shell раÑÑˆÐ¸Ñ€ÐµÐ½Ð¸Ñ Ð½Ðµ поддерживаютÑÑ Ð¿Ð¾ÐºÐ° уÑтановлена Ð¾Ð¿Ñ†Ð¸Ñ Ð±ÐµÐ·Ð¾Ð¿Ð°Ñного редактированиÑ" +290 "Команда %s не поддерживаетÑÑ Ð¿Ð¾ÐºÐ° уÑтановлена Ð¾Ð¿Ñ†Ð¸Ñ Ð±ÐµÐ·Ð¾Ð¿Ð°Ñного редактированиÑ" +291 "set: Ð¾Ð¿Ñ†Ð¸Ñ %s не может быть выключена" +292 "Экран Ñлишком мал" +293 "добавлено" +294 "изменено" +295 "удалено" +296 "приÑоединено" +297 "перемещено" +298 "Ñмещено" +299 "Ñкопировано" +300 "Ñтрока" +301 "Ñтроки" +303 "Файл изменен Ñ Ð¼Ð¾Ð¼ÐµÐ½Ñ‚Ð° поÑледней запиÑи." +304 "Shell раÑширение не удалоÑÑŒ" +305 "Ðет указанной %s опции редактированиÑ" +307 "Ðет ex команды Ð´Ð»Ñ Ð²Ñ‹Ð¿Ð¾Ð»Ð½ÐµÐ½Ð¸Ñ" +308 "Введите Ð´Ð»Ñ Ð²Ñ‹Ð¿Ð¾Ð»Ð½ÐµÐ½Ð¸Ñ ÐºÐ¾Ð¼Ð°Ð½Ð´Ñ‹, :q чтобы выйти" +309 "ИÑпользуйте \"cscope help\" Ð´Ð»Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰Ð¸" +310 "Ðет запущенного cscope ÑоединениÑ" +311 "%s: неизвеÑтный тип поиÑка: иÑпользуйте один из %s" +312 "%d: нет такого cscope ÑеанÑа" +313 "set: %s Ð¾Ð¿Ñ†Ð¸Ñ Ð½Ð¸ÐºÐ¾Ð³Ð´Ð° не может быть включена" +314 "set: %s Ð¾Ð¿Ñ†Ð¸Ñ Ð½Ð¸ÐºÐ¾Ð³Ð´Ð° не может быть уÑтановлена в 0" +315 "%s: добавлено: %lu Ñтрок, %lu Ñимволов" +316 "Ðеожиданное Ñобытие Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ñ€Ð°Ð·Ð¼ÐµÑ€Ð°" +317 "%d файлов Ð´Ð»Ñ Ñ€ÐµÐ´Ð°ÐºÑ‚Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ" +319 "%d Ñкранов в фоновом режиме; иÑпользуйте :display чтобы перечиÑлить их" +320 "ÐеизвеÑÑ‚Ð½Ð°Ñ Ð¿Ð¾Ð·Ð¸Ñ†Ð¸Ñ ÐºÑƒÑ€Ñора" +321 "Преобразование кодировки файлов не поддерживаетÑÑ" +322 "Преобразование кодировки ввода не поддерживаетÑÑ" +323 "Ðеверный ввод. УÑечено." +324 "Ошибка Ð¿Ñ€ÐµÐ¾Ð±Ñ€Ð°Ð·Ð¾Ð²Ð°Ð½Ð¸Ñ Ð² Ñтроке %d" diff --git a/usr.bin/vi/catalog/spanish.UTF-8.base b/usr.bin/vi/catalog/spanish.UTF-8.base new file mode 100644 index 00000000000..4a3059a84ca --- /dev/null +++ b/usr.bin/vi/catalog/spanish.UTF-8.base @@ -0,0 +1,306 @@ +002 "Desbordamiento de longitud de línea" +003 "no se puede borrar la línea %lu" +004 "no se puede añadir la línea %lu" +005 "no se puede insertar en la línea %lu" +006 "no se puede guardar la línea %lu" +007 "no se puede obtener la última línea" +008 "Error: no se puede recuperar la línea %lu" +009 "Archivo de log" +010 "No se realiza log, no se puede deshacer" +011 "No hay cambios para deshacer" +012 "No se realiza log, no se puede deshacer" +013 "No se realiza log, no se puede remontar hacia adelante" +014 "No hay cambios para rehacer" +015 "%s/%d: error de log" +016 "La entrada y salida estándar de Vi debe ser una terminal" +017 "Marcar %s: no determinado" +018 "Marcar %s: se borró la línea" +019 "Marcar %s: la posición del cursor ya no existe" +020 "Error: " +021 "nuevo archivo" +022 "nombre cambiado" +023 "modificado" +024 "no modificado" +025 "DESTRABADO" +026 "lectura solamente" +027 "línea %lu de %lu [%ld%%]" +028 "archivo vacío" +029 "línea %lu" +030 "El archivo %s no es un catálogo de mensajes" +031 "No se puede determinar la opción por omisión %s" +032 "Uso: %s" +033 "determinar: no hay opción %s: 'determinar todo' establece todos los valores de opción" +034 "determinar: [no] hay opción %s no tiene valor" +035 "determinar: opción %s no es booleano" +036 "determinar: opción %s: %s" +037 "determinar: opción %s: %s: desbordamiento de valores" +038 "determinar: opción %s: %s es un número ilegal" +039 "determinar: opción %s no es booleano" +040 "Las columnas en pantalla son demasiado pequeñas, menos de %d" +041 "Las columnas en pantalla son demasiado grandes, más de %d" +042 "Las líneas en pantalla son demasiado pequeñas, menos de %d" +043 "Las líneas en pantalla son demasiado grandes, más de %d" +044 "La opción lisp no está implementada" +045 "mensajes no desconectados: %s" +046 "mensajes no conectados: %s" +047 "La opción de %s debe estar en dos grupos de caracteres" +053 "El buffer por omisión está vacío" +054 "El buffer %s está vacío" +055 "Los archivos con nuevas líneas en el nombre son irrecuperables" +056 "Las modificaciones no pueden recuperarse si la sesión falla" +057 "Copiando archivo para recuperación..." +058 "Preservación fracasada: %s" +059 "Las modificaciones no pueden recuperarse si la sesión falla" +060 "Archivo de respaldo fracasado: %s" +061 "Copiando archivo para recuperación..." +062 "Información sobre identificación del usuario %u no encontrada" +063 "No se puede trabar archivo de recuperación" +064 "Desbordamiento de buffer de archivo de recuperación" +065 "Archivo de recuperación" +066 "%s: archivo de recuperación defectuoso" +067 "%s: archivo de recuperación defectuoso" +068 "No hay archivos denominados %s, que usted pueda leer, para recuperar" +069 "Existen versiones más antiguas de este archivo que usted puede recuperar" +070 "Existen otros archivos que usted puede recuperar" +071 "no envía email: %s" +072 "Archivo vacío; no hay nada para buscar" +073 "Se alcanzó el final del archivo sin encontrar el patrón" +074 "No hay patrón anterior de búsqueda" +075 "No se encontró el patrón" +076 " Se alcanzó el principio del archivo sin encontrar el patrón" +077 "Búsqueda reiniciada" +078 "Buscando..." +079 "No se encontró ningún carácter no imprimible" +080 "Nombre de comando desconocido" +082 "%s: comando no disponible en modalidad ex" +083 "La cuenta no puede ser cero" +084 "%s: mala especificación de línea" +085 "Error interno de tabla de sintaxis (%s: %s)" +086 "Uso: %s" +087 "%s: buffer temporario no liberado" +088 "Desplazamiento de marcador a antes de la línea 1" +089 "Desplazamiento de marcador más allá del final del archivo" +090 "@ con rango que corre cuando se cambia el archivo/la pantalla" +091 "Comando global/v que corre cuando se cambia el archivo/la pantalla" +092 "Comando Ex fracasado: comandos pendientes descartados" +093 "Comando Ex fracasado: teclas mapeadas descartadas" +094 "La segunda dirección es más pequeña que la primera" +095 "No se suministra nombre de marca" +096 "\\ no es seguido por / o ?" +097 "Referencia a un número de línea menor que 0" +098 "El comando %s es desconocido" +099 "Desbordamiento de valor de dirección" +100 "Subdesbordamiento de valor de dirección" +101 "Combinación de dirección ilegal" +102 "Dirección ilegal: sólo %lu líneas en el archivo" +103 "Dirección ilegal: el archivo está vacío" +104 "El comando %s no permite una dirección de 0" +105 "No hay abreviaturas para visualizar" +106 "Las abreviaturas deben terminar con un carácter de \"palabra\" " +107 "Las abreviaturas no pueden contener tabs o espacios" +108 "Las abreviaturas no pueden mezclar caracteres palabra/no-palabra, salvo al final" +109 "\"%s\" no es una abreviatura" +110 "Comando Vi fracasado: teclas mapeadas descartadas" +111 "No hay más archivos para editar" +112 "No hay archivos anteriores para editar" +113 "No hay archivos anteriores para rebobinar" +114 "No hay lista de archivos para visualizar" +115 "No hay un comando anterior para reemplazar a \"!\"" +116 "No hay nombre de archivo para sustituir por %%" +117 "No hay nombre de archivo para sustituir por #" +118 "Error: execl: %s" +119 "Error de E/S: %s" +120 "Archivo modificado desde la última escritura completa; escribir o usar ! para alterar" +121 "No se puede encontrar la ubicación del directorio inicial" +122 "Nuevo directorio actual: %s" +123 "No hay buffers sueltos para visualizar" +124 "El comando %s no puede usarse como parte de un comando global o v" +125 "%s/%s: sin fuente: no le pertenece a usted o a raíz" +126 "%s/%s: sin fuente: no le pertenece a usted" +127 "%s/%s: sin fuente: puede ser escrito por un usuario que no sea el propietario" +128 "%s: sin fuente: no le pertenece a usted o a raíz" +129 "%s: sin fuente: no le pertenece a usted" +130 "%s: sin fuente: puede ser escrito por un usuario que no sea el propietario" +131 "No hay líneas siguientes para unir" +132 "No hay anotaciones de mapa de entrada" +133 "No hay anotaciones de mapa de comando" +134 "El carácter %s no puede remapearse" +135 "\"%s\" no está mapeado actualmente" +136 "Marca de nombres debe ser un sólo carácter" +137 "%s existe, no está escrito; usar ! para alterar" +138 "Nuevo archivo exrc: %s" +139 "La línea de destino se encuentra dentro del rango de movimiento" +140 "El comando abierto requiere que se determine la opción abierta" +141 "El comando abierto no se ha implementado aún" +142 "No es posible preservar este archivo" +143 "Archivo preservado" +144 "%s: expandido a demasiados nombres de archivo" +145 "Sólo pueden leerse los archivos regulares y los conductos nombrados" +146 "%s: traba de lectura no disponible" +147 "Leyendo..." +148 "%s: %lu líneas, %lu caracteres" +149 "No hay pantallas de fondo para mostrar" +150 "El comando de script sólo está disponible en modalidad vi" +151 "No hay comando para ejecutar" +152 "opción de ancho de desplazamiento en 0" +153 "Desbordamiento de cuenta" +154 "Subdesbordamiento de cuenta" +155 "Expresión regular especificada; marcador r no tiene significado" +156 "Los marcadores #, l y p no pueden combinarse con el marcador c en la modalidad vi" +157 "No se encontró coincidencia" +158 "No se ingresó un identificador anterior" +159 "Se encontraron menos de %s anotaciones en la pila de identificadores; usar :visualizar i[dentificadores]" +160 "No hay archivo %s en la pila de identificadores al que se pueda volver; usar :visualizar i[dentificadores]" +161 "Presionar Intro para continuar: " +162 "%s: no se encontró el identificador" +163 "%s: identificador corrompido en %s" +164 "%s: el número de línea del identificador es posterior al final del archivo" +165 "La pila de identificadores está vacía" +166 "%s: patrón de búsqueda no encontrado" +167 "%d archivos más para editar" +168 "El buffer %s está vacío" +169 "¿Confirmar cambio? [n]" +170 "Interrumpido" +171 "No hay buffer anterior para ejecutar" +172 "No hay expresión regular anterior" +173 "El comando %s requiere que se haya leído un archivo" +174 "Uso: %s" +175 "El comando visual requiere que se determine la opción abierta" +177 "Archivo vacío" +178 "No hay búsqueda F, f, T o t anterior" +179 "%s no se encontró" +180 "No hay archivo anterior para editar" +181 "El cursor no está en un número" +182 "El número resultante es demasiado grande" +183 " El número resultante es demasiado pequeño" +184 "No hay carácter coincidente en esta línea" +185 "No se encontró un carácter coincidente" +186 "No hay caracteres para reemplazar" +187 "No hay otra pantalla a la que se pueda pasar" +188 "Caracteres tras de cadena de búsqueda, desplazamiento de línea y/o comando z" +189 "No hay patrón anterior de búsqueda" +190 "Búsqueda vuelve a la posición inicial" +191 "Se superó el límite de expansión de abreviatura: se descartaron caracteres" +192 "Carácter ilegal; mencionar para entrar" +193 "Ya se encuentra al principio de la inserción" +194 "No hay más caracteres para borrar" +195 "Movimiento más allá del final del archivo" +196 "Movimiento más allá del final de la línea" +197 "No hay movimiento del cursor" +198 "Ya se encuentra al principio del archivo" +199 "Movimiento más allá del principio del archivo" +200 "Ya se encuentra en la primera columna" +201 "Los buffers deben especificarse antes del comando" +202 "Ya se encuentra al final del archivo" +203 "Ya se encuentra al final de la línea" +204 "%s no es un comando vi" +205 "Uso: %s" +206 "No hay caracteres para borrar" +207 "El comando Q requiere la interfase de terminal ex" +208 "No hay comando para repetir" +209 "El archivo está vacío" +210 "%s no puede usarse como comando de movimiento" +211 "Ya se encuentra en modalidad de comando" +212 "El cursor no se encuentra en una palabra" +214 "El valor de opción de Windows es demasiado grande, el máx. es %u" +215 "Añadir" +216 "Cambiar" +217 "Comando" +218 "Insertar" +219 "Reemplazar" +220 "El movimiento va más allá del final de la pantalla" +221 "El movimiento va más allá del principio de la pantalla" +222 "La pantalla debe tener más de %d líneas para dividirse" +223 "No hay pantallas de fondo" +224 "No hay pantalla de fondo editando un archivo denominado %s" +225 "No se puede poner fondo a la única pantalla que se visualiza" +226 "La pantalla sólo puede reducirse a %d hileras" +227 "La pantalla no puede reducirse" +228 "La pantalla no puede aumentarse" +230 "Esta pantalla no puede suspenderse" +231 "Interrumpido: teclas mapeadas descartadas" +232 "vi: buffer temporario no liberado" +233 "Esta terminal no tiene tecla %s" +234 "Sólo un buffer puede especificarse" +235 "Número mayor que %lu" +236 "Interrumpido" +237 "No se puede crear archivo temporario" +238 "Advertencia: %s no es un archivo regular" +239 "%s ya se encuentra trabado, la sesión es de lectura solamente" +240 "%s: eliminar" +241 "%s: cerrar" +242 "%s: eliminar" +243 "%s: eliminar" +244 "Archivo de lectura solamente, no escrito; usar ! para alterar" +245 " Archivo de lectura solamente, no escrito" +246 "%s existe, no escrito; usar ! para alterar" +247 "%s existe, no escrito" +248 "Archivo parcial, no escrito; usar ! para alterar" +249 "Archivo parcial, no escrito" +250 "%s: archivo modificado más recientemente que esta copia; usar ! para alterar" +251 "%s: archivo modificado más recientemente que esta copia" +252 "%s: la traba de escritura no estaba disponible" +253 "Escribiendo..." +254 "%s: ADVERTENCIA: ARCHIVO TRUNCADO" +255 "Ya se encuentra en el primer identificador de este grupo" +256 "%s: nuevo archivo: %lu líneas, %lu caracteres" +257 "%s: %lu líneas, %lu caracteres" +258 "%s expandido a demasiados nombres de archivos" +259 "%s: no es un archivo regular" +260 "%s: no le pertenece" +261 "%s: accesible por un usuario que no sea el propietario" +262 "Archivo modificado desde la última escritura completa; escribir o usar ! para alterar" +263 "Archivo modificado desde la última escritura completa; escribir o usar :editar! para alterar" +264 "Archivo modificado desde la última escritura completa; escribir o usar ! para alterar" +265 "El archivo es temporario; al salir se descartarán las modificaciones" +266 "Archivo de lectura solamente, las modificaciones no se autoescriben" +267 "Se reinició el log" +268 "confirmar? [snq]" +269 "Presionar cualquier tecla para continuar: " +270 "Presionar cualquier tecla para continuar [: para ingresar más comandos ex]: " +271 "Presionar cualquier tecla para continuar [q para salir]: " +272 "Esta forma de %s requiere la interfase terminal ex" +273 "Ingresando en la modalidad de entrada ex." +274 "Comando fracasado, no hay archivo leído aun." +275 " cont?" +276 "Evento inesperado de carácter" +277 "Evento inesperado de final de archivo" +278 "No hay coincidencias para consulta" +279 "Evento inesperado de interrupción" +280 "Evento inesperado de salida" +281 "Evento inesperado de repintura" +282 "Ya se encuentra en el último identificador de este grupo" +283 "El comando %s requiere la interfase terminal ex" +284 "Esta forma de %s no se encuentra soportada cuando se determina la opción de edición segura" +285 "Evento inesperado de cadena" +286 "Evento inesperado de tiempo excedido" +287 "Evento inesperado de escritura" +289 "Las expansiones de shell no se encuentran soportadas cuando se determina la opción de edición segura" +290 "El comando %s no se encuentra soportado cuando se determina la opción de edición segura" +291 "determinar: la opción %s puede no estar desconectada" +292 "El monitor es demasiado pequeño." +293 "agregado" +294 "cambiado" +295 "borrado" +296 "unido" +297 "movido" +298 "desplazado" +299 "arrancado" +300 "línea" +301 "líneas" +302 "Vi no se cargó con un intérprete Tcl" +303 "Archivo modificado desde la última escritura." +304 "Expansión de shell fracasada" +305 "No hay opción de edición %s especificada" +306 "Vi no se cargó con un intérprete Perl" +307 "No hay comando ex para ejecutar" +308 "Ingresar para ejecutar un comando, :q para salir" +309 "Usar \"cscope ayuda\" para obtener ayuda" +310 "No hay conexiones cscope corriendo" +311 "%s: tipo de búsqueda desconocido: usar uno de %s" +312 "%d: no existe esta sesión cscope" +313 "determinar: la opción %s no puede conectarse nunca" +314 "determinar: la opción %s no puede determinarse nunca en 0" +315 "%s: añadido: %lu líneas, %lu caracteres" +316 "Evento inesperado de modificación de tamaño" +317 "%d archivos para editar" diff --git a/usr.bin/vi/catalog/swedish.UTF-8.base b/usr.bin/vi/catalog/swedish.UTF-8.base new file mode 100644 index 00000000000..fc08dfc8cd2 --- /dev/null +++ b/usr.bin/vi/catalog/swedish.UTF-8.base @@ -0,0 +1,306 @@ +002 "För lÃ¥nga rader" +003 "kan inte ta bort rad %lu" +004 "kan inte lägga till pÃ¥ rad %lu" +005 "kan inte sätta in pÃ¥ rad %lu" +006 "kan inte lagra rad %lu" +007 "kan inte hämta sista raden" +008 "Fel: kan inte hämta rad %lu" +009 "Loggningsfil" +010 "Loggning utförs inte, Ã¥ngra är inte möjligt" +011 "Inga ändringar att Ã¥ngra" +012 "Loggning utförs inte, Ã¥ngra är inte möjligt" +013 "Loggning utförs inte, Ã¥ngra Ã¥ngra är inte möjligt" +014 "Inga ändringar att Ã¥tergöra" +015 "%s/%d: fel vid loggning" +016 "Vi:s standard in och ut mÃ¥ste gÃ¥ till en terminal" +017 "Markering %s: inte satt" +018 "Markering %s: raden har tagits bort" +019 "Markering %s: markörpositionen finns inte längre" +020 "Fel: " +021 "ny fil" +022 "namnet ändrades" +023 "ändrad" +024 "oförändrad" +025 "OLÃ…ST" +026 "inte skrivbar" +027 "rad %lu av %lu [%ld%%]" +028 "tom fil" +029 "rad %lu" +030 "Filen %s är ingen meddelandekatalog" +031 "Kan inte sätta standardvärde för %s flaggan" +032 "Användning: %s" +033 "set: %s är en okänd flagga: \"set all\" visar alla flaggor" +034 "set: [no]%s flaggan kan inte ges ett värde" +035 "set: %s flaggan är inte boleansk" +036 "set: %s flaggan: %s" +037 "set: %s flaggan: %s: för stort värde" +038 "set: %s flaggan: %s är ett otillÃ¥tet tal" +039 "set: %s flaggan är inte boleansk" +040 "Fönsterkolumnerna är för fÃ¥, mindre än %d" +041 "Fönsterkolumnerna är för mÃ¥nga, fler än %d" +042 "Fönsterraderna är för fÃ¥, mindre än %d" +043 "Fönsterraderna är för mÃ¥nga, fler än %d" +044 "Lisp flaggan är inte implementerad" +045 "meddelanden är inte avslagna: %s" +046 "meddelanden är inte pÃ¥slagna: %s" +047 "%s flaggan mÃ¥ste ges i teckengrupper om tvÃ¥" +053 "Standardbufferten är tom" +054 "Buffer %s är tom" +055 "Filer med radmatning i namnet kan inte Ã¥terskapas" +056 "Ändringar kan inte Ã¥terskapas om programmet kraschar" +057 "Kopierar filen för Ã¥terskapning..." +058 "Säkerhetskopiering misslyckades: %s" +059 "Ändringar kan inte Ã¥terskapas om programmet kraschar" +060 "Misslyckades att säkerhetskopiera filen: %s" +061 "Kopierar filen för Ã¥terskapning..." +062 "Kan inte hitta information om användaridentitet %u" +063 "Kan inte lÃ¥sa Ã¥terskapningsfilen" +064 "Ã…terskapningsfilens buffer överskriven" +065 "Ã…terskapningsfil" +066 "%s: Ã…terskapningsfilen är korrupt" +067 "%s: Ã…terskapningsfilen är korrupt" +068 "Det finns ingen fil %s, läsbar av dig, att Ã¥terskapa" +069 "Det finns äldre versioner av denna fil som du kan Ã¥terskapa" +070 "Det finns andra filer du kan Ã¥terskapa" +071 "skickar inte email: %s" +072 "Filen är tom; inget att söka i" +073 "Kom till slutet pÃ¥ filen utan att hitta söksträngen" +074 "Ingen tidigare söksträng" +075 "Hittar inte söksträngen" +076 "Kom till början av filen utan att hitta söksträngen" +077 "Sökningen slog runt" +078 "Söker..." +079 "Inga icke skrivbara tecken funna" +080 "Okänt kommandonamn" +082 "%s: kommandot är inte tillgängligt i \"ex\" läge" +083 "Talet fÃ¥r inte vara noll" +084 "%s: Ogiltig radspecifikation" +085 "Fel i intern syntaxtabell (%s: %s)" +086 "Användning: %s" +087 "%s: temporärbuffert inte frisläppt" +088 "Offset är före rad 1" +089 "Offset är efter slutet pÃ¥ filen" +090 "@ med intervall exekverades när filen/fönstret ändrades" +091 "Global/v kommando exekverades när filen/fönstret ändrades" +092 "Ex kommando misslyckades: efterföljande kommandon ignorerade" +093 "Ex kommando misslyckades: omdefinierade tangenter ignorerade" +094 "Den andra adressen är mindre än den första" +095 "Inget namn pÃ¥ markering givet" +096 "\\ följs inte av / eller ?" +097 "Referens till ett radnummer mindre än 0" +098 "%s kommandot är inte känt" +099 "Värdet pÃ¥ adressen är för stort" +100 "Värdet pÃ¥ adressen är för litet" +101 "OtillÃ¥ten adresskombination" +102 "OtillÃ¥ten adress: bara %lu rader finns i filen" +103 "OtillÃ¥ten adress: filen är tom" +104 "%s kommandot tillÃ¥ter inte en adress som är 0" +105 "Inga förkortningar att visa" +106 "Förkortningar mÃ¥ste sluta med ett \"ord\" tecken" +107 "Förkortningar kan inte innehÃ¥lla mellanslag eller tab" +108 "Förkortningar kan inte blanda \"ord\"/\"icke ord\" tecken, utom i slutet" +109 "\"%s\" är ingen förkortning" +110 "Vi kommando misslyckades: omdefinierade tangenter ignorerade" +111 "Inga fler filer att editera" +112 "Inga tidigare filer att editera" +113 "Inga tidigare filer att spela tillbaka" +114 "Ingen fillista att visa" +115 "Inget tidigare kommando att ersätta \"!\" med" +116 "Inget filnamn att ersätta %% med" +117 "Inget filnamn att ersätta # med" +118 "Fel: execl: %s" +119 "I/O fel: %s" +120 "Filen ändrad efter sista skrivning; spara eller använd !" +121 "Kan inte hitta hemkatalog" +122 "Ny nuvarande katalog: %s" +123 "Inga \"cut buffers\" att visa" +124 "%s kommandot kan inte används som del i ett \"global\" eller v kommando" +125 "%s/%s: inte läst: varken du eller root är ägare" +126 "%s/%s: inte läst: du är inte ägare" +127 "%s/%s: inte läst: skrivbar av annan än ägaren" +128 "%s: inte läst: varken du eller root är ägare" +129 "%s: inte läst: du är inte ägare" +130 "%s: inte läst: skrivbar av annan än ägaren" +131 "Ingen nästa rad att sätta ihop med" +132 "Det finns inget i inmatningsmappningen" +133 "Det finns inget i kommandomappningen" +134 "%s tecknet kan inte mappas om" +135 "\"%s\" är inte ommappat just nu" +136 "Namn pÃ¥ markeringar mÃ¥ste vara ett tecken lÃ¥nga" +137 "%s finns, inget sparat; använd ! för att spara" +138 "Ny exrc fil: %s" +139 "MÃ¥lraden ligger inne i omrÃ¥det som ska flyttas" +140 "Open kommandot kräver att open flaggan är satt" +141 "Open kommandot är inte implementerat ännu" +142 "Säkerhetskopiering av filen är inte möjligt" +143 "Filen säkerhetskopierad" +144 "%s expanderade till för mÃ¥nga filnamn" +145 "Endast vanliga filer och namngivna rör kan läsas" +146 "%s: läslÃ¥set är otillgängligt" +147 "Läser..." +148 "%s: %lu rader, %lu tecken" +149 "Inga bakgrundsfönster att visa" +150 "Script kommandot finns bara i \"vi\" läge" +151 "Inget kommando att exekvera" +152 "shiftwidth flaggan satt till 0" +153 "Talet har för stort värde" +154 "Talet har för litet värde" +155 "Reguljärt uttryck är givet; r flaggan är meningslös" +156 "#, l och p flaggorna kan inte kombineras med c flaggan i \"vi\" läge" +157 "Ingen matchande text funnen" +158 "Inget tidigare märke har givits" +159 "Det är färre än %s märken i stacken; använd :display t[ags]" +160 "Det finns ingen fil %s i märkesstacken; använd :display t[ags]" +161 "Tryck Enter för att fortsätta: " +162 "%s: märke inte funnet" +163 "%s: korrupt märke i %s" +164 "%s: märkets radnummer är bortom filslutet" +165 "Märkesstacken är tom" +166 "%s: söksträngen inte funnen" +167 "%d filer till att editera" +168 "Buffert %s är tom" +169 "Bekräfta ändring? [n]" +170 "Avbruten" +171 "Ingen tidigare buffert att exekvera" +172 "Inget tidigare reguljärt uttryck" +173 "%s kommandot kräver att en fil redan lästs in" +174 "Användning: %s" +175 "Visual kommandot kräver att open flaggan är satt" +177 "Tom fil" +178 "Ingen tidigare F, f, T eller t sökning" +179 "%s inte funnen" +180 "Ingen tidigare fil att editera" +181 "Markören är inte i ett tal" +182 "Det resulterande talet är för stort" +183 "Det resulterande talet är för litet" +184 "Inget matchande tecken pÃ¥ denna rad" +185 "Matchande tecken inte funnet" +186 "Det finns inga tecken att ersätta" +187 "Det finns inget fönster att byta till" +188 "Tecken efter söksträng, radoffset och/eller z kommandot" +189 "Ingen tidigare söksträng" +190 "Sökningen slog runt till ursprungliga positionen" +191 "Förkortning överskred expanderingsgränsen: tecken har tagits bort" +192 "Ogiltigt tecken; använd \"quote\" för att sätta in" +193 "Redan i början pÃ¥ insättningen" +194 "Inga fler tecken att ta bort" +195 "Försök att gÃ¥ bortom slutet pÃ¥ filen" +196 "Försök att gÃ¥ bortom slutet pÃ¥ raden" +197 "Ingen förflyttning gjord" +198 "Redan i början pÃ¥ filen" +199 "Försök att gÃ¥ före början pÃ¥ filen" +200 "Redan i första kolumnen" +201 "Buffertar mÃ¥ste anges före kommandot" +202 "Redan i slutet av filen" +203 "Redan pÃ¥ slutet av raden" +204 "%s är inte ett \"vi\" kommando" +205 "Användning: %s" +206 "Inga tecken att ta bort" +207 "Q kommandot kräver \"ex\" i terminalläge" +208 "Inget kommando att repetera" +209 "Filen är tom" +210 "%s kan inte användas som ett förflyttningskommando" +211 "Redan i kommando läge" +212 "Markören är inte i ett ord" +214 "Windows flaggans värde är för stor, största värde är %u" +215 "Lägg till" +216 "Ändra" +217 "Kommando" +218 "Sätt in" +219 "Ersätt" +220 "Förflyttning bortom fönsterslut" +221 "Förflyttning till före fönstrets början" +222 "Fönstret mÃ¥ste vara större än %d rader för delning" +223 "Det finns inga fönster i bakgrunden" +224 "Det finns inget fönster i bakgrunden som editerar filen %s" +225 "Du fÃ¥r inte sätta ditt enda synliga fönster i bakgrunden" +226 "Fönstret kan bara krympa till %d rader" +227 "Fönstret kan inte krympa" +228 "Fönstret kan inte växa" +230 "Detta fönster kan inte pausas" +231 "Avbrutet: omdefinierade tangenter ignorerade" +232 "vi: temporärbuffertar inte frisläppta" +233 "Denna terminal har ingen %s tangent" +234 "Endast en buffert kan anges" +235 "Talet är större än %lu" +236 "Avbrutet" +237 "Kan inte skapa temporär fil" +238 "Warning: %s är inte en normal fil" +239 "%s är redan lÃ¥st, detta blir en icke skrivbar session" +240 "%s: ta bort" +241 "%s: stäng" +242 "%s: ta bort" +243 "%s: ta bort" +244 "Ej skrivbar fil, filen inte sparad; använd ! för att skriva över" +245 "Ej skrivbar fil, filen inte sparad" +246 "%s finns, ej sparad; använd ! för att utföra operationen" +247 "%s finns, filen inte sparad" +248 "Ofullständig fil, filen inte sparad, använd ! för att skriva över" +249 "Ofullständig fil, filen inte sparad" +250 "%s: filen ändrad efter denna kopia togs; använd ! för att utföra operationen" +251 "%s: filen ändrad efter denna kopia togs" +252 "%s: skrivlÃ¥set är otillgängligt" +253 "Skriver..." +254 "%s: VARNING: FILEN TRUNKERAD" +255 "Redan vid första märket i denna grupp" +256 "%s: ny fil: %lu rader, %lu tecken" +257 "%s: %lu rader, %lu tecken" +258 "%s expanderade till för mÃ¥nga filnamn" +259 "%s är inte en normal fil" +260 "%s ägs inte av dig" +261 "%s är Ã¥tkomstbar av andra än ägaren" +262 "Filen har ändrats efter den sparats; spara eller använd !" +263 "Filen har ändrats efter den sparats; spara eller använd :edit!" +264 "Filen har ändrats efter den sparats; spara eller använd !" +265 "Filen är temporär; exit kastar bort ändringarna" +266 "Ej skrivbar fil, ändringar har inte automatsparats" +267 "Loggningen startar om" +268 "bekräfta? [ynq]" +269 "Tryck pÃ¥ en tangent för att fortsätta: " +270 "Tryck pÃ¥ en tangent för att fortsätta [: för att ge fler kommandon]: " +271 "Tryck pÃ¥ en tangent för att fortsätta [q för att avsluta]: " +272 "Den formen av %s kräver \"ex\" i terminalläge" +273 "GÃ¥r till \"ex\" inmatningsläge." +274 "Kommandot misslyckades, ingen fil inläst ännu." +275 " forts?" +276 "Oväntad teckenhändelse" +277 "Oväntad filslutshändelse" +278 "Sökningen hittade ingenting" +279 "Oväntad avbrottshändelse" +280 "Oväntad avslutningshändelse" +281 "Oväntad omritningshändelse" +282 "Redan vid sista märket i denna grupp" +283 "%s kommandot kräver \"ex\" i terminalläge" +284 "Den formen av %s är inte tillgänglig när secure edit flaggan är satt" +285 "Oväntad stränghändelse" +286 "Oväntad tidshändelse" +287 "Oväntad skrivhändelse" +289 "Skalexpansion är inte tillgänglig när secure edit flaggan är satt" +290 "%s kommandot är inte tillgänglig när secure edit flaggan är satt" +291 "set: %s kan inte slÃ¥s av" +292 "Fönstret för litet." +293 "tillagda" +294 "ändrade" +295 "borttagna" +296 "ihopsatta" +297 "flyttade" +298 "flyttade" +299 "inklistrade" +300 "rad" +301 "rader" +302 "Vi har inte länkats med en Tcl tolk" +303 "Filen har ändrats efter den sparats." +304 "Skalexpansion misslyckades" +305 "Ingen %s edit flagga given" +306 "Vi har inte länkats med en Perl tolk" +307 "Inga \"ex\" kommandon att exekvera" +308 "Tryck för att exekvera kommando, :q för att avsluta" +309 "Gör \"cscope help\" för hjälp" +310 "Inga cscope kopplingar körs" +311 "%s: okänd söktyp: använd en av %s" +312 "%d: ingen sÃ¥dan cscope session" +313 "set: %s flaggan fÃ¥r aldrig slÃ¥s pÃ¥" +314 "set: %s flaggan fÃ¥r aldrig sättas till 0" +315 "%s: tillagt: %lu rader, %lu tecken" +316 "Oväntad storleksändring" +317 "%d filer att editera" diff --git a/usr.bin/vi/catalog/uk_UA.UTF-8.base b/usr.bin/vi/catalog/uk_UA.UTF-8.base new file mode 100644 index 00000000000..b4d6887a011 --- /dev/null +++ b/usr.bin/vi/catalog/uk_UA.UTF-8.base @@ -0,0 +1,306 @@ +002 "ÐŸÐµÑ€ÐµÐ¿Ð¾Ð²Ð½ÐµÐ½Ð½Ñ Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ð´Ð¾Ð²Ð¶Ð¸Ð½Ð¸ Ñ€Ñдку" +003 "неможливо Ñтерти Ñ€Ñдок %lu" +004 "неможливо додати до Ñ€Ñдку %lu" +005 "неможливо вÑтавити в Ñ€Ñдок %lu" +006 "неможливо розміÑтити Ñ€Ñдок %lu" +007 "неможливо діÑтати оÑтанній Ñ€Ñдок" +008 "Помилка: неможливо отримати Ñ€Ñдок %lu" +009 "Файл запиÑів" +010 "ЗапиÑи не велиÑÑ, неможливо відмінити оÑтанню команду" +011 "Ðема чого відмінÑти" +012 "ЗапиÑи не велиÑÑ, неможливо відмінити оÑтанню команду" +013 "ЗапиÑи не велиÑÑ, неможливо продивитиÑÑ Ð²Ð¿ÐµÑ€ÐµÐ´" +014 "Ðема чого повторювати" +015 "%s/%d: помилка запиÑу протоколу" +016 "Стандартним введеннÑм/виведеннÑм Ð´Ð»Ñ vi має бути термінал" +017 "Мітка %s: не вÑтановлено" +018 "Мітка %s: Ñ€Ñдок Ñтерто" +019 "Мітка %s: позиції курÑору більше не Ñ–Ñнує" +020 "Помилка: " +021 "Ðовий файл" +022 "Iм'Ñ Ð·Ð¼Ñ–Ð½Ð¸Ð»Ð¾ÑÑŒ" +023 "змінений" +024 "не змінений" +025 "РO3БЛOКOÐ’ÐÐO" +026 "тільки Ð´Ð»Ñ Ñ‡Ð¸Ñ‚Ð°Ð½Ð½Ñ" +027 "Ñ€Ñдок %lu з %lu [%ld%%]" +028 "пуÑтий файл" +029 "Ñ€Ñдок %lu" +030 "Файл %s не Ñ” файлом повідомленнь" +031 "Ðеможливо вÑтановити опцію %s за змовчаннÑм" +032 "ВикориÑтаннÑ: %s" +033 "Опції %s немає: 'set all' показує вÑÑ– можливі опції" +034 "set: [no]%s не набуває такого значеннÑ" +035 "set: %s Ð¾Ð¿Ñ†Ñ–Ñ Ð½Ðµ Ñ” логічною" +036 "set: Ð¾Ð¿Ñ†Ñ–Ñ %s: %s" +037 "set: Ð¾Ð¿Ñ†Ñ–Ñ %s: %s: переповненнÑ" +038 "set: неправильне Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ %s" +039 "set: %s Ð¾Ð¿Ñ†Ñ–Ñ Ð½Ðµ Ñ” логічною" +040 "КількіÑть колонок на Ñкрані надто мала, менше ніж %d" +041 "КількіÑть колонок на Ñкрані надто велика, більше ніж %d" +042 "КількіÑть Ñ€Ñдків на Ñкрані надто мала, менше ніж %d" +043 "КількіÑть Ñ€Ñдків на Ñкрані надто велика, більше ніж %d" +044 "ÐžÐ¿Ñ†Ñ–Ñ lisp відÑутнÑ" +045 "ÐŸÐ¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½Ð½Ñ Ð½Ðµ вимкнені: %s" +046 "ÐŸÐ¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½Ð½Ñ Ð½Ðµ ввімкнені: %s" +047 "ÐžÐ¿Ñ†Ñ–Ñ %s повинна міÑтити групи з двох Ñимволів" +053 "Стартовий буфер порожній" +054 "Буфер %s порожній" +055 "Ðеможливо відновити файл, що міÑтить в імені Ñимволи Ð¿ÐµÑ€ÐµÐ²ÐµÐ´ÐµÐ½Ð½Ñ ÐºÐ°Ñ€ÐµÑ‚ÐºÐ¸" +056 "Зміни не можна буде відновити піÑÐ»Ñ ÐºÑ€Ð°Ñ…Ñƒ ÑеÑÑ–Ñ—" +057 "ÐšÐ¾Ð¿Ñ–ÑŽÐ²Ð°Ð½Ð½Ñ Ñ„Ð°Ð¹Ð»Ñƒ Ð´Ð»Ñ Ð²Ñ–Ð´Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ..." +058 "Ð—Ð±ÐµÑ€ÐµÐ¶ÐµÐ½Ð½Ñ Ð½Ðµ вдалоÑÑŒ: %s" +059 "Зміни не можна буде відновити піÑÐ»Ñ ÐºÑ€Ð°Ñ…Ñƒ ÑеÑÑ–Ñ—" +060 "Ð—Ð±ÐµÑ€ÐµÐ¶ÐµÐ½Ð½Ñ ÐºÐ¾Ð¿Ñ–Ñ— файлу не вдалоÑÑŒ: %s" +061 "ÐšÐ¾Ð¿Ñ–ÑŽÐ²Ð°Ð½Ð½Ñ Ñ„Ð°Ð¹Ð»Ñƒ Ð´Ð»Ñ Ð²Ñ–Ð´Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ..." +062 "Iнформацію про кориÑтувача %u не знайдено" +063 "Заблокувати відновленний файл неможливо" +064 "Буфер відновленого файлу переповнено" +065 "Відновленний файл" +066 "%s: файл відновленний не до кінцÑ" +067 "%s: файл відновленний не до кінцÑ" +068 "Ðе Ñ–Ñнуює файлів з іменем %s, Ñкі Ви можете прочитати" +069 "IÑнують Ñтарі верÑÑ–Ñ— файлу, Ñкі можна відновити" +070 "IÑнують інші файли, Ñкі можна відновити" +071 "e-mail не відіÑлано: %s" +072 "Файл пуÑтий - нема чого шукати" +073 "ДоÑÑнуто ÐºÑ–Ð½Ñ†Ñ Ñ„Ð°Ð¹Ð»Ñƒ без Ð·Ð½Ð°Ñ…Ð¾Ð¶Ð´ÐµÐ½Ð½Ñ Ð·Ñ€Ð°Ð·ÐºÑƒ пошуку" +074 "Ðе задано зразок пошуку" +075 "Зразок пошуку не знайдено" +076 "ДоÑÑнуто початку файлу без Ð·Ð½Ð°Ñ…Ð¾Ð¶Ð´ÐµÐ½Ð½Ñ Ð·Ñ€Ð°Ð·ÐºÑƒ пошуку" +077 "Пошук зациклено" +078 "Пошук..." +079 "Ðепечатних Ñимволів не знайдено" +080 "Ðевідома команда" +082 "%s: команда не доÑтупна в режимі ex" +083 "Лічильник не може бути нулем" +084 "%s: неправильне Ð²Ð¸Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ñ€Ñдку" +085 "Ð’Ð½ÑƒÑ‚Ñ€Ñ–ÑˆÐ½Ñ Ð¿Ð¾Ð¼Ð¸Ð»ÐºÐ° в ÑинтакÑиÑÑ– (%s: %s)" +086 "ВикориÑтаннÑ: %s" +087 "%s: тимчаÑовий буфер не викориÑтано" +088 "Мітку поÑтавлено перед першим Ñ€Ñдком" +089 "Мітку поÑтавлено піÑÐ»Ñ ÐºÑ–Ð½Ñ†Ñ Ñ„Ð°Ð¹Ð»Ñƒ" +090 "@ з діапазоном працює коли змінено файл/вікно" +091 "Команда Global/v працює коли змінено файл/вікно" +092 "Команда ex не вдалаÑÑŒ: наÑтупні команди з'ігноровано" +093 "Команда ex не вдалаÑÑŒ: відображені клавіші з'ігноровано" +094 "Друга адреÑа менше ніж перша" +095 "Ðе вказано ім'Ñ Ð¼Ñ–Ñ‚ÐºÐ¸" +096 "\\ не закінчуєтьÑÑ / чи ?" +097 "ПоÑÐ¸Ð»Ð°Ð½Ð½Ñ Ð½Ð° Ñ€Ñдок з номером, меншим ніж 0" +098 "Команда %s невідома" +099 "ÐŸÐµÑ€ÐµÐ¿Ð¾Ð²Ð½ÐµÐ½Ð½Ñ Ð»Ñ–Ñ‡Ð¸Ð»ÑŒÐ½Ð¸ÐºÐ° адреÑ" +100 "Ðедобір лічильника адреÑ" +101 "ÐедопуÑтима ÐºÐ¾Ð¼Ð±Ñ–Ð½Ð°Ñ†Ñ–Ñ Ð² адреÑÑ–" +102 "Ðеправильна адреÑа: вÑього %lu Ñ€Ñдків у файлі" +103 "Ðеправильна адреÑа: файл пуÑтий" +104 "Команда %s не може викориÑтовувати адреÑу 0" +105 "Ðемає абревіатур" +106 "Ðбревіатури повинні закінчуватиÑÑ Ñимволом \"Ñлів\"" +107 "Ð’ абревіатурі не можна викориÑтовувати Ñимволи табулÑції та пробіли" +108 "Ðбревіатури не можуть змішувати Ñимволи Ñлів/не-Ñлів, хіба що в кінці Ñ€Ñдку" +109 "\"%s\" не абревіатура" +110 "Команда Vi не вдалаÑÑŒ: відображені клавіші з'ігноровано" +111 "Файлів Ð´Ð»Ñ Ñ€ÐµÐ´Ð°Ð³ÑƒÐ²Ð°Ð½Ð½Ñ Ð±Ñ–Ð»ÑŒÑˆÐµ немає" +112 "Попереднього файлу Ð´Ð»Ñ Ñ€ÐµÐ´Ð°Ð³ÑƒÐ²Ð°Ð½Ð½Ñ Ð½ÐµÐ¼Ð°Ñ”" +113 "Попереднього файлу Ð´Ð»Ñ Ð¿ÐµÑ€ÐµÐ³Ð»Ñду немає" +114 "Ðемає файлів" +115 "Ðемає попередньої команди Ð´Ð»Ñ Ð·Ð°Ð¼Ñ–Ð½Ð¸ \"!\"" +116 "ВідÑутнє ім'Ñ Ñ„Ð°Ð¹Ð»Ñƒ Ð´Ð»Ñ Ð¿Ñ–Ð´Ð¼Ñ–Ð½Ð¸ %%" +117 "ВідÑутнє ім'Ñ Ñ„Ð°Ð¹Ð»Ñƒ Ð´Ð»Ñ Ð¿Ñ–Ð´Ð¼Ñ–Ð½Ð¸ #" +118 "Помилка: execl: %s" +119 "Помилка введеннÑ/виведеннÑ: %s" +120 "Файл модифіковано піÑÐ»Ñ Ð¾Ñтанньої команди повного запиÑу: збережіть чи викориÑтайте ! Ð´Ð»Ñ Ð¾Ð±Ñ…Ð¾Ð´Ñƒ" +121 "Ðеможливо знайти домашній каталог" +122 "Ðовий каталог: %s" +123 "Ðемає наповнених буферів" +124 "Команда %s не викориÑтовуєтьÑÑ Ñк чаÑтина команди global чи v" +125 "%s/%s: не відкрито: не належить Вам чи адмініÑтратору" +126 "%s/%s: не відкрито: не належить Вам" +127 "%s/%s: не відкрито: можливіÑть запиÑу кориÑтувачем, Ñкий не Ñ” влаÑником" +128 "%s: не відкрито: не належить Вам чи адмініÑтратору" +129 "%s: не відкрито: не належить Вам" +130 "%s: не відкрито: можливіÑть запиÑу кориÑтувачем, Ñкий не Ñ” влаÑником" +131 "Ðемає більше Ñ€Ñдків Ð´Ð»Ñ Ð¾Ð±'єднаннÑ" +132 "Ðемає параметрів введеннÑ" +133 "Ðемає параметрів команди" +134 "Символ %s не можна переназначити" +135 "\"%s\" зараз не переназначено" +136 "Iм'Ñ Ð¼Ñ–Ñ‚ÐºÐ¸ -- один Ñимвол" +137 "%s Ñ–Ñнує, не запиÑано; викориÑтайте ! Ð´Ð»Ñ Ð¾Ð±Ñ…Ð¾Ð´Ñƒ" +138 "Ðовий файл exrÑ: %s" +139 "РÑдок Ð¿Ñ€Ð¸Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ð·Ð°Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¾ вÑередині діапазону переміщеннÑ" +140 "Команда open вимагає вÑÑ‚Ð°Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Ð¾Ð¿Ñ†Ñ–Ñ— open" +141 "Команду open ще не реалізовано" +142 "ЗахиÑÑ‚ файлу неможливий" +143 "Файл захищено" +144 "%s розширивÑÑ Ð² надто велику кількіÑть імен файлів" +145 "Прочитати можна тільки проÑтий файл чи іменований канал(pipe)" +146 "%s: Ð±Ð»Ð¾ÐºÑƒÐ²Ð°Ð½Ð½Ñ Ð²Ñ–Ð´ Ñ‡Ð¸Ñ‚Ð°Ð½Ð½Ñ Ð½ÐµÐ¼Ð¾Ð¶Ð»Ð¸Ð²Ðµ" +147 "Зчитую..." +148 "%s: %lu Ñ€Ñдків, %lu Ñимволів" +149 "Ðемає тіньових вікон" +150 "Команда script доÑтупна лише в режимі vi" +151 "Ðема команди Ð´Ð»Ñ Ð²Ð¸ÐºÐ¾Ð½Ð°Ð½Ð½Ñ" +152 "Опцію shiftwidth вÑтановлено в 0" +153 "ÐŸÐµÑ€ÐµÐ¿Ð¾Ð²Ð½ÐµÐ½Ð½Ñ Ð»Ñ–Ñ‡Ð¸Ð»ÑŒÐ½Ð¸ÐºÐ°" +154 "Цикл виконано не до кінцÑ" +155 "Вказано регулÑрний вираз; параметр 'r' не має ÑенÑу" +156 "Параметри #, l та p не можна комбінувати з параметром 'c' в режимі vi" +157 "Співпадань немає" +158 "Tег відÑутній" +159 "Менше ніж %s запиÑів у Ñтеку тегів; викориÑтайте :display t[ags]" +160 "Ðемає файлу %s в Ñтеку тегів Ð´Ð»Ñ Ð¿Ð¾Ð²ÐµÑ€Ð½ÐµÐ½Ð½Ñ; викориÑтайте :display t[ags]" +161 "ÐатиÑніть ENTER Ð´Ð»Ñ Ð¿Ñ€Ð¾Ð´Ð¾Ð²Ð¶ÐµÐ½Ð½Ñ: " +162 "%s: тег не знайдено" +163 "%s: зіпÑований тег в %s" +164 "%s: номер Ñ€Ñдку тега знаходитьÑÑ Ð·Ð° кінцем файлу" +165 "Стек тегів порожній" +166 "%s: зразок пошуку не знайдено" +167 "ЗалишилоÑÑŒ %d файлів Ð´Ð»Ñ Ñ€ÐµÐ´Ð°Ð³ÑƒÐ²Ð°Ð½Ð½Ñ" +168 "Буфер %s порожній" +169 "Змінити? [n]" +170 "Перервано" +171 "Ðемає буферу Ð´Ð»Ñ Ð²Ð¸ÐºÐ¾Ñ€Ð¸ÑтаннÑ" +172 "Ðемає попереднього регулÑрного виразу" +173 "Команда %s вимагає вже прочитаний файл" +174 "ВикориÑтаннÑ: %s" +175 "Команда visual вимагає вÑÑ‚Ð°Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Ð¾Ð¿Ñ†Ñ–Ñ— open" +177 "ПуÑтий файл" +178 "Ðемає попереднього пошуку F, f, T, чи t" +179 "%s не знайдено" +180 "Ðемає попереднього файлу Ð´Ð»Ñ Ñ€ÐµÐ´Ð°Ð³ÑƒÐ²Ð°Ð½Ð½Ñ" +181 "КурÑор Ñтоїть не на цифрі" +182 "Oтримане чиÑло надто велике" +183 "Oтримане чиÑло надто мале" +184 "Ðемає відповідного Ñимволу в цьому Ñ€Ñдку" +185 "Відповідний Ñимвол не знайдено" +186 "Ðемає Ñимволів Ð´Ð»Ñ Ð·Ð°Ð¼Ñ–Ð½Ð¸" +187 "Ðемає іншого вікна" +188 "Символи піÑÐ»Ñ Ñ€Ñдку Ð´Ð»Ñ Ð¿Ð¾ÑˆÑƒÐºÑƒ, Ð·Ð¼Ñ–Ñ‰ÐµÐ½Ð½Ñ Ñ€Ñдку та/чи команди z" +189 "Ðемає попереднього зразку пошуку" +190 "Пошук закінчивÑÑ Ð½Ð° початковій позиції" +191 "Ðбревіатура перевищила ліміт розширеннÑ: Ñимволи з'ігноровано" +192 "Ðедозволений Ñимвол: задайте в дужках" +193 "Вже на початку вÑтавки" +194 "Ðемає Ñимволів Ð´Ð»Ñ Ð²Ð¸Ð´Ð°Ð»ÐµÐ½Ð½Ñ" +195 "Рух за кінець файлу" +196 "Рух за кінець Ñ€Ñдку" +197 "КурÑор не переміщувавÑÑ" +198 "Вже на початку файлу" +199 "Рух курÑору за початок файлу" +200 "Вже в першій колонці" +201 "Буфер треба вказувати перед командою" +202 "Вже на кінці файлу" +203 "Вже на кінці Ñ€Ñдку" +204 "%s не команда Vi" +205 "ВикориÑтаннÑ: %s" +206 "Ðемає Ñимволів Ð´Ð»Ñ Ð²Ð¸Ð´Ð°Ð»ÐµÐ½Ð½Ñ" +207 "Команда Q вимагає інтерфейÑу ex" +208 "Ðемає команди Ð´Ð»Ñ Ð¿Ð¾Ð²Ñ‚Ð¾Ñ€ÐµÐ½Ð½Ñ" +209 "Файл пуÑтий" +210 "Команду %s не можна викориÑтати Ð´Ð»Ñ Ð¿ÐµÑ€ÐµÐ¼Ñ–Ñ‰ÐµÐ½Ð½Ñ" +211 "Вже в командному режимі" +212 "КурÑор Ñтоїть не на Ñлові" +214 "Ð—Ð½Ð°Ñ‡Ð½ÐµÐ½Ð½Ñ Ð¾Ð¿Ñ†Ñ–Ñ— Windows надто велике, макÑимум = %u" +215 "ДопиÑати" +216 "Змінити" +217 "Команда" +218 "Ð’Ñтавити" +219 "Замінити" +220 "Рух курÑору за кінець Ñкрану" +221 "Рух курÑору за початок Ñкрану" +222 "Ð”Ð»Ñ Ñ€Ð¾Ð·Ð±Ð¸Ñ‚Ñ‚Ñ Ð²Ñ–ÐºÐ½Ð¾ має міÑтити більше ніж %d Ñ€Ñдків" +223 "Тіньових вікон немає" +224 "Ðемає тіньового вікна з редагуваннÑм файлу %s" +225 "Ðе можна зробити тіньовим єдине вікно" +226 "Вікно можна ÑтиÑнути лише до %d Ñ€Ñдків" +227 "Вікно не можна ÑтиÑнути" +228 "Вікно не можна розширити" +230 "Це вікно не можна призупинити" +231 "Перервано: відображені клавіші з'ігноровано" +232 "vi: тимчаÑовий буфур не вивільнено" +233 "Цей тип терміналу немає клавіші %s" +234 "Можна вказати лише один буфер" +235 "ЧиÑло більше, ніж %lu" +236 "Перервано" +237 "Ðеможлу Ñтворити тимчаÑовий файл" +238 "УВÐГÐ: %s Ñпеціальний файл" +239 "%s вже заблоковано, доÑтупний тільки Ð´Ð»Ñ Ñ‡Ð¸Ñ‚Ð°Ð½Ð½Ñ" +240 "%s: Ñтерто" +241 "%s: закрито" +242 "%s: Ñтерто" +243 "%s: Ñтерто" +244 "Файл тільки Ð´Ð»Ñ Ñ‡Ð¸Ñ‚Ð°Ð½Ð½Ñ, не запиÑано: ВикориÑтайте ! Ð´Ð»Ñ Ð¾Ð±Ñ…Ð¾Ð´Ñƒ" +245 "Файл тільки Ð´Ð»Ñ Ñ‡Ð¸Ñ‚Ð°Ð½Ð½Ñ, не запиÑано" +246 "%s Ñ–Ñнує, не запиÑано; викориÑтайте ! Ð´Ð»Ñ Ð¾Ð±Ñ…Ð¾Ð´Ñƒ" +247 "%s Ñ–Ñнує, не запиÑано" +248 "ВикориÑтайте ! Ð´Ð»Ñ Ñ‡Ð°Ñткового запиÑу файлу" +249 "ЧаÑтина файлу, файл не запиÑано" +250 "%s: Файл змінювавÑÑ Ð¿Ñ–Ð·Ð½Ñ–ÑˆÐµ, ніж Ñ†Ñ ÐºÐ¾Ð¿Ñ–Ñ: викориÑтайте ! Ð´Ð»Ñ Ð¾Ð±Ñ…Ð¾Ð´Ñƒ" +251 "%s: Файл змінювавÑÑ Ð¿Ñ–Ð·Ð½Ñ–ÑˆÐµ, ніж Ñ†Ñ ÐºÐ¾Ð¿Ñ–Ñ" +252 "%s: захиÑÑ‚ від запиÑу недоÑтупний" +253 "ЗапиÑ..." +254 "%s: УВÐГÐ: файл обрізано" +255 "Вже на першому тегу в цій групі" +256 "%s: новий файл: %lu Ñ€Ñдків, %lu Ñимволів" +257 "%s: %lu Ñ€Ñдків, %lu Ñимволів" +258 "%s розширивÑÑ Ð² надто велику кількіÑть імен файлів" +259 "%s: Ñпеціальний файл" +260 "%s: не належить Вам" +261 "%s: доÑтупний не лише Вам +262 "Файл модифіковано піÑÐ»Ñ Ð¾Ñтанньої команди запиÑу: збережіть чи викориÑтайте ! Ð´Ð»Ñ Ð¾Ð±Ñ…Ð¾Ð´Ñƒ" +263 "Файл модифіковано піÑÐ»Ñ Ð¾Ñтанньої команди запиÑу: збережіть чи викориÑтайте :edit Ð´Ð»Ñ Ð¾Ð±Ñ…Ð¾Ð´Ñƒ" +264 "Файл модифіковано піÑÐ»Ñ Ð¾Ñтанньої команди запиÑу: збережіть чи викориÑтайте ! Ð´Ð»Ñ Ð¾Ð±Ñ…Ð¾Ð´Ñƒ" +265 "ТимчаÑовий файл: вихід зітре зміни" +266 "Файл тільки Ð´Ð»Ñ Ñ‡Ð¸Ñ‚Ð°Ð½Ð½Ñ, зміни не запишутьÑÑ Ð°Ð²Ñ‚Ð¾Ð¼Ð°Ñ‚Ð¸Ñ‡Ð½Ð¾" +267 "ЗапиÑи початі заново" +268 "Підтверджуєте? [ynq]" +269 "ÐатиÑніть ENTER Ð´Ð»Ñ Ð¿Ñ€Ð¾Ð´Ð¾Ð²Ð¶ÐµÐ½Ð½Ñ: " +270 "ÐатиÑніть ENTER Ð´Ð»Ñ Ð¿Ñ€Ð¾Ð´Ð¾Ð²Ð¶ÐµÐ½Ð½Ñ [: Ð´Ð»Ñ Ñ–Ð½ÑˆÐ¸Ñ… команд] " +271 "ÐатиÑніть ENTER Ð´Ð»Ñ Ð¿Ñ€Ð¾Ð´Ð¾Ð²Ð¶ÐµÐ½Ð½Ñ [q Ð´Ð»Ñ Ð²Ð¸Ñ…Ð¾Ð´Ñƒ]: " +272 "Tака форма %s вимагає інтерфейÑу ex" +273 "Входим в режим Ð²Ð²ÐµÐ´ÐµÐ½Ð½Ñ ex" +274 "Збій команди, файл не прочитано" +275 "Продовжити?" +276 "Ðеочікувана Ñимвольна подіÑ" +277 "Ðеочікувана Ð¿Ð¾Ð´Ñ–Ñ ÐºÑ–Ð½Ñ†Ñ Ñ„Ð°Ð¹Ð»Ñƒ" +278 "Ðемає Ñпівпадань за запитом" +279 "Ðеочікувана Ð¿Ð¾Ð´Ñ–Ñ Ð¿ÐµÑ€ÐµÑ€Ð¸Ð²Ð°Ð½Ð½Ñ" +280 "Ðеочікувана Ð¿Ð¾Ð´Ñ–Ñ Ð²Ð¸Ñ…Ð¾Ð´Ñƒ" +281 "Ðеочікувана Ð¿Ð¾Ð´Ñ–Ñ Ð¿ÐµÑ€ÐµÑ€Ð¸Ñовки" +282 "Вже на оÑтанньму Тегу в цій групі" +283 "Команда %s вимагає інтерфейÑу ex" +284 "Tака форма %s не дозволÑєтьÑÑ Ð¿Ñ€Ð¸ вÑтановленій опції secure edit" +285 "Ðеочікувана Ð¿Ð¾Ð´Ñ–Ñ Ñ€Ñдку" +286 "Ðеочікувана Ð¿Ð¾Ð´Ñ–Ñ Ñ‚Ð°Ð¹Ð¼-ауту" +287 "Ðеочікувана Ð¿Ð¾Ð´Ñ–Ñ Ð·Ð°Ð¿Ð¸Ñу" +289 "Shell'івÑке Ð´Ð¾Ð¿Ð¾Ð²Ð½ÐµÐ½Ð½Ñ Ð½Ðµ дозволÑєтьÑÑ Ð¿Ñ€Ð¸ вÑтановленій опції secure edit" +290 "Команда %s не дозволÑєтьÑÑ Ð¿Ñ€Ð¸ вÑтановленій опції secure edit" +291 "set: опцію %s неможна виÑтавити вимкненою" +292 "Екран надто малий." +293 "додано" +294 "змінено" +295 "Ñтерто" +296 "об'єднано" +297 "переміщено" +298 "здвинуто" +299 "вÑтавлено" +300 "Ñ€Ñдок" +301 "Ñ€Ñдків" +302 "Vi завантажено без інтерпретатора Tcl" +303 "Файл модифіковано піÑÐ»Ñ Ð¾Ñтанньої команди запиÑу." +304 "Ðевдача shell'івÑького доповненнÑ" +305 "Oпції Ñ€ÐµÐ´Ð°Ð³ÑƒÐ²Ð°Ð½Ð½Ñ %s не вказано" +306 "Vi завантажено без інтерпретатора Perl" +307 "Ðемає команди ex Ð´Ð»Ñ Ð²Ð¸ÐºÐ¾Ð½Ð°Ð½Ð½Ñ" +308 "ÐатиÑніть ENTER щоб виконати команду, q щоб вийти" +309 "Введіть 'cscope help' Ð´Ð»Ñ Ñ–Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ñ–Ñ—" +310 "Ðемає cscope з'єднаннь" +311 "%s: невідомий тип пошуку: викориÑтовуйте один з %s" +312 "%d: немає такої ÑеÑÑ–Ñ— cscope" +313 "set: опцію %s неможна виÑтавити увімкненою" +314 "set: опцію %s неможна виÑтавити в 0" +315 "%s: додано: %lu Ñ€Ñдків, %lu Ñимволів" +316 "Ðеочікувана Ð¿Ð¾Ð´Ñ–Ñ Ð·Ð¼Ñ–Ð½Ð¸ розміру" +317 "%d файлів Ð´Ð»Ñ Ñ€ÐµÐ´Ð°Ð³ÑƒÐ²Ð°Ð½Ð½Ñ" diff --git a/usr.bin/vi/catalog/zh_CN.UTF-8.base b/usr.bin/vi/catalog/zh_CN.UTF-8.base new file mode 100644 index 00000000000..c51a7c37349 --- /dev/null +++ b/usr.bin/vi/catalog/zh_CN.UTF-8.base @@ -0,0 +1,311 @@ +002 "行长度溢出" +003 "无法删除第 %lu 行" +004 "无法å‘第 %lu 行追加" +005 "无法å‘第 %lu 行æ’å…¥" +006 "无法存储第 %lu 行" +007 "无法获得最åŽä¸€è¡Œ" +008 "错误:无法获å–第 %lu 行" +009 "日志文件" +010 "日志未记录,无法撤销" +011 "未åšä¿®æ”¹ï¼Œæ— æ³•撤销" +012 "日志未记录,无法撤销" +013 "日志未记录,无法回滚" +014 "未åšä¿®æ”¹ï¼Œæ— æ³•é‡åš" +015 "%s/%d: 日志错误" +016 "Vi 的标准输入和输出必须是终端" +017 "标记 %s: 未设置" +018 "标记 %s: 该行被删除" +019 "标记 %s: 光标ä½ç½®ä¸å­˜åœ¨" +020 "错误:" +021 "新文件" +022 "文件åå˜æ›´" +023 "已修改" +024 "未修改" +025 "已解é”" +026 "åªè¯»" +027 "第 %lu 行ï¼å…± %lu 行 [%ld%%]" +028 "空文件" +029 "第 %lu 行" +030 "文件 %s 䏿˜¯æ¶ˆæ¯ç›®å½•" +031 "无法设置选项 %s 的默认值" +032 "用法 %s" +033 "set: ä¸å­˜åœ¨é€‰é¡¹ %s;用 'set all' 查看所有选项的值" +034 "set: 选项 [no]%s 䏿ޥå—赋值" +035 "set: 选项 %s 䏿˜¯å¼€å…³" +036 "set: 选项 %s: %s" +037 "set: 选项 %s: %s: 值溢出" +038 "set: 选项 %s: %s æ˜¯ä¸€ä¸ªéžæ³•æ•°å­—" +039 "set: 选项 %s 䏿˜¯å¼€å…³" +040 "å±å¹•太窄,列宽å°äºŽ %d" +041 "å±å¹•太宽,列宽大于 %d" +042 "å±å¹•太矮,行高å°äºŽ %d" +043 "å±å¹•太高,行高大于 %d" +044 "lisp 选项未实现" +045 "æ¶ˆæ¯æœªå…³é—­ï¼š%s" +046 "æ¶ˆæ¯æœªæ‰“开:%s" +047 "%s 选项必须æ¯ä¸¤ä¸ªå­—符一组" +053 "默认缓冲区为空" +054 "缓冲区 %s 为空" +055 "文件å中有æ¢è¡Œçš„æ–‡ä»¶æ˜¯æ— æ³•æ¢å¤çš„" +056 "一旦会è¯å¤±è´¥ï¼Œä¿®æ”¹æ— æ³•æ¢å¤" +057 "正在备份文件……" +058 "ä¿å­˜å¤±è´¥ï¼š%s" +059 "一旦会è¯å¤±è´¥ï¼Œä¿®æ”¹æ— æ³•æ¢å¤" +060 "文件备份失败:%s" +061 "正在备份文件……" +062 "ID 为 %u 的用户未找到" +063 "无法给æ¢å¤æ–‡ä»¶åŠ é”" +064 "æ¢å¤æ–‡ä»¶ç¼“冲区溢出" +065 "æ¢å¤æ–‡ä»¶" +066 "%s: æ¢å¤æ–‡ä»¶æ ¼å¼å¼‚常" +067 "%s: æ¢å¤æ–‡ä»¶æ ¼å¼å¼‚常" +068 "ä¸å­˜åœ¨å为 %s 的,å¯è¯»å¹¶éœ€è¦æ¢å¤çš„æ–‡ä»¶" +069 "å­˜åœ¨æ­¤æ–‡ä»¶çš„æ—§ç‰ˆæœ¬éœ€è¦æ‚¨æ¢å¤" +070 "å­˜åœ¨å…¶å®ƒéœ€è¦æ‚¨æ¢å¤çš„æ–‡ä»¶" +071 "邮件未å‘出:%s" +072 "文件为空,无法æœç´¢" +073 "到达文件尾,未找到模å¼" +074 "没有上一个æœç´¢æ¨¡å¼" +075 "æ¨¡å¼æœªæ‰¾åˆ°" +076 "到达文件头,未找到模å¼" +077 "æœç´¢å›žè½¬" +078 "正在æœç´¢â€¦â€¦" +079 "未找到ä¸å¯æ‰“å°å­—符" +080 "命令å䏿˜Ž" +082 "%s: 命令在 ex 模å¼ä¸‹ä¸å¯ç”¨" +083 "命令计数ä¸å¯ä¸º 0" +084 "%s: 错误的行æè¿°" +085 "内部语法表错误 (%s: %s)" +086 "用法:%s" +087 "%s: 临时缓冲区未释放" +088 "修饰符åç§»é‡åœ¨ç¬¬ä¸€è¡Œä¹‹å‰" +089 "修饰符åç§»é‡è¶…过最åŽä¸€è¡Œ" +090 "文件ï¼å±å¹•æ”¹å˜æ—¶æœ‰å¸¦èŒƒå›´çš„ @ 命令正在è¿è¡Œ" +091 "文件ï¼å±å¹•æ”¹å˜æ—¶æœ‰å…¨å±€å‘½ä»¤ï¼v 命令正在è¿è¡Œ" +092 "Ex 命令失败:等待中的命令已丢弃" +093 "Ex 命令失败:键映射已丢弃" +094 "第二地å€å°äºŽç¬¬ä¸€åœ°å€" +095 "未æä¾›æ ‡è®°å" +096 "\\ 没有跟在 / 或 ? åŽé¢" +097 "引用了å°äºŽ 0 的行å·" +098 "%s 命令䏿˜Ž" +099 "地å€å€¼æº¢å‡º" +100 "地å€å€¼ä¸‹æº¢" +101 "éžæ³•的地å€ç»„åˆ" +102 "éžæ³•地å€ï¼šæ–‡ä»¶åªæœ‰ %lu 行" +103 "éžæ³•地å€ï¼šæ–‡ä»¶ä¸ºç©º" +104 "命令 %s ä¸å…许地å€ä¸º 0" +105 "æ²¡æœ‰å¯æ˜¾ç¤ºçš„缩写" +106 "缩写必须以「å•è¯ã€å­—符结æŸ" +107 "缩写ä¸èƒ½åŒ…å«åˆ¶è¡¨ç¬¦æˆ–空格" +108 "缩写ä¸èƒ½åœ¨ç»“尾之外的地方混用å•è¯ï¼éžå•è¯å­—符" +109 "\"%s\" 䏿˜¯ç¼©å†™" +110 "Vi 命令失败:键映射已丢弃" +111 "没有剩余的待编辑文件" +112 "没有上一个待编辑文件" +113 "æ²¡æœ‰ä¸Šä¸€ä¸ªéœ€é‡æ–°ç¼–辑的文件" +114 "æ²¡æœ‰å¯æ˜¾ç¤ºçš„æ–‡ä»¶åˆ—表" +115 "ç¼ºå°‘ç”¨äºŽæ›¿æ¢ \"!\" 的上一æ¡å‘½ä»¤" +116 "ç¼ºå°‘ç”¨äºŽæ›¿æ¢ %% 的文件å" +117 "ç¼ºå°‘ç”¨äºŽæ›¿æ¢ # 的文件å" +118 "错误:execl: %s" +119 "输入ï¼è¾“出错误:%s" +120 "文件在上一次完整写入åŽè¢«ä¿®æ”¹ï¼›å†™å…¥æ–‡ä»¶ï¼Œæˆ–使用 ! 强制切æ¢ç›®å½•" +121 "无法找到用户起始目录" +122 "新的当å‰ç›®å½•:%s" +123 "æ²¡æœ‰å¯æ˜¾ç¤ºçš„剪切缓冲区" +124 "%s 命令ä¸èƒ½è¢«ç”¨ä½œå…¨å±€å‘½ä»¤æˆ– v 命令的一部分" +125 "%s/%s: 未引入:ä¸å±žäºŽæ‚¨æˆ–根用户" +126 "%s/%s: 未引入:ä¸å±žäºŽæ‚¨" +127 "%s/%s: 未引入:å¯ä»¥è¢«æ–‡ä»¶å±žä¸»ä»¥å¤–的用户写入" +128 "%s: 未引入:ä¸å±žäºŽæ‚¨æˆ–根用户" +129 "%s: 未引入:ä¸å±žäºŽæ‚¨" +130 "%s: 未引入:å¯ä»¥è¢«æ–‡ä»¶å±žä¸»ä»¥å¤–的用户写入" +131 "缺少å¯ä»¥åˆå¹¶çš„行" +132 "缺少输入映射" +133 "缺少命令映射" +134 "%s 字符ä¸èƒ½è¢«é‡æ–°æ˜ å°„" +135 "\"%s\" ç›®å‰æœªè¢«æ˜ å°„" +136 "标记å必须是å•个字符" +137 "%s 已存在,未写入;用 ! 覆盖文件" +138 "新建 exrc 文件:%s" +139 "目标行在移动范围之内" +140 "open è¦æ±‚å¼€å¯ open 选项" +141 "open 命令未实现" +142 "æ— æ³•ä¿æŒæ­¤æ–‡ä»¶" +143 "æ–‡ä»¶å·²ä¿æŒ" +144 "%s: 展开的文件å过多" +145 "åªèƒ½è¯»å–常规文件和具å管é“" +146 "%s: 读é”ä¸å¯ç”¨" +147 "正在读å–……" +148 "%s: %lu 行,%lu 个字符" +149 "æ²¡æœ‰å¯æ˜¾ç¤ºçš„åŽå°å±å¹•" +150 "script 命令仅在 vi 模å¼ä¸‹å¯ç”¨" +151 "æ²¡æœ‰è¦æ‰§è¡Œçš„命令" +152 "shiftwidth 选项被设为 0" +153 "命令计数溢出" +154 "命令计数下溢" +155 "正则表达å¼å·²æŒ‡å®šï¼›r 修饰符无效" +156 "#, l å’Œ p 修饰符在 vi 模å¼ä¸‹ä¸èƒ½ä¸Ž c 修饰符组åˆ" +157 "未找到匹é…" +158 "没有上一次进入的 tag" +159 "tags 栈上的记录少于 %s æ¡ï¼›ç”¨ :display t[ags] 显示它们" +160 "tags 栈上ä¸å­˜åœ¨æ–‡ä»¶ %s,无法返回;用 :display t[ags] 查看" +161 "按回车键继续:" +162 "%s: tag 未找到" +163 "%s: æŸåçš„ tag 在 %s 中" +164 "%s: tag 的行å·è¶…过了文件尾" +165 "tags 栈为空" +166 "%s: æœç´¢æ¨¡å¼æœªæ‰¾åˆ°" +167 "还有 %d 个待编辑文件" +168 "缓冲区 %s 为空" +169 "确认修改?[n]" +170 "已中断" +171 "没有上一个å¯ä¾›æ‰§è¡Œçš„缓冲区" +172 "æ²¡æœ‰ä¸Šä¸€æ¡æ­£åˆ™è¡¨è¾¾å¼" +173 "%s è¦æ±‚存在一个已读入的文件" +174 "用法:%s" +175 "visual å‘½ä»¤è¦æ±‚å¼€å¯ open 选项" +177 "空文件" +178 "没有上一次 F, f, T 或 t æœç´¢" +179 "%s 未找到" +180 "没有上一个待编辑文件" +181 "光标ä¸åœ¨æ•°å­—上" +182 "结果数字过大" +183 "结果数字过å°" +184 "这一行上没有匹é…的字符" +185 "匹é…字符未找到" +186 "æ²¡æœ‰å¯æ›¿æ¢çš„字符" +187 "没有其它å±å¹•å¯ä»¥åˆ‡æ¢" +188 "在æœç´¢å­—符串ã€è¡Œåç§»é‡æˆ– z å‘½ä»¤ä¹‹åŽæœ‰å¤šä½™å­—符" +189 "没有上一个æœç´¢æ¨¡å¼" +190 "æœç´¢å›žè½¬åˆ°äº†åˆå§‹ä½ç½®" +191 "缩写展开超过é™åˆ¶ï¼šå­—符已丢弃" +192 "éžæ³•å­—ç¬¦ï¼›ç”¨å¼•å·æ‹¬èµ·æ¥å†è¾“å…¥" +193 "已到æ’入点的开始" +194 "æ²¡æœ‰å¯æ“¦é™¤çš„剩余字符" +195 "移动超过文件尾" +196 "移动超过行末" +197 "光标未移动" +198 "已到文件头" +199 "移动超过文件头" +200 "å·²ä½äºŽç¬¬ä¸€åˆ—" +201 "ç¼“å†²åŒºåº”åœ¨å‘½ä»¤å‰æŒ‡å®š" +202 "已到文件尾" +203 "已到行末" +204 "%s 䏿˜¯ vi 命令" +205 "用法 %s" +206 "没有å¯åˆ é™¤çš„字符" +207 "Q å‘½ä»¤éœ€è¦ ex 终端界é¢" +208 "没有å¯é‡å¤çš„命令" +209 "文件为空" +210 "%s ä¸èƒ½è¢«ç”¨ä½œç§»åЍ命令" +211 "已处于命令模å¼" +212 "光标ä¸åœ¨å•è¯ä¸Š" +214 "窗å£é€‰é¡¹çš„值过大,最大值 %u" +215 "追加" +216 "修改" +217 "命令" +218 "æ’å…¥" +219 "替æ¢" +220 "移动超过å±å¹•终点" +221 "移动超过å±å¹•起点" +222 "分å±éœ€è¦å¤šäºŽ %d 行" +223 "没有åŽå°å±å¹•" +224 "ä¸å­˜åœ¨æ­£åœ¨ç¼–辑文件 %s çš„åŽå°å±å¹•" +225 "ä¸èƒ½æŠŠæ‚¨å”¯ä¸€çš„å±å¹•置于åŽå°" +226 "å±å¹•åªèƒ½æ”¶ç¼©åˆ° %d 行" +227 "å±å¹•ä¸èƒ½æ”¶ç¼©" +228 "å±å¹•ä¸èƒ½å¢žé«˜" +230 "ä¸èƒ½æŒ‚èµ·æ­¤å±å¹•" +231 "已中断:键映射已丢弃" +232 "vi: 临时缓冲区未释放" +233 "此终端没有 %s é”®" +234 "åªèƒ½æŒ‡å®šä¸€ä¸ªç¼“冲区" +235 "数字大于 %lu" +236 "已中断" +237 "无法创建临时文件" +238 "警告:%s 䏿˜¯å¸¸è§„文件" +239 "%s 已加é”,会è¯ä¸ºåªè¯»" +240 "%s: 移除" +241 "%s: 关闭" +242 "%s: 移除" +243 "%s: 移除" +244 "åªè¯»æ–‡ä»¶ï¼Œæœªå†™å…¥ï¼›ç”¨ ! 强制写入" +245 "åªè¯»æ–‡ä»¶ï¼Œæœªå†™å…¥" +246 "%s 已存在,未写入;用 ! 覆盖文件" +247 "%s 已存在,未写入" +248 "是已存在文件的一部分,未写入;用 ! 强制写入" +249 "是已存在文件的一部分,未写入" +250 "%s: æ–‡ä»¶è¢«ä¿®æ”¹ï¼Œæ–°äºŽå½“å‰æ‹·è´ï¼›ç”¨ ! 强制写入" +251 "%s: æ–‡ä»¶è¢«ä¿®æ”¹ï¼Œæ–°äºŽå½“å‰æ‹·è´" +252 "%s: 写é”ä¸å¯ç”¨" +253 "正在写入……" +254 "%s: 警告:文件被截断" +255 "å·²ä½äºŽè¿™ç»„ tags 中的第一个" +256 "%s: 新文件:%lu 行,%lu 个字符" +257 "%s: %lu 行,%lu 个字符" +258 "%s: 展开的文件å过多" +259 "%s: 䏿˜¯å¸¸è§„文件" +260 "%s: ä¸å±žäºŽæ‚¨" +261 "%s: å¯è¢«æ–‡ä»¶å±žä¸»ä»¥å¤–的用户访问" +262 "文件在上一次完整写入åŽè¢«ä¿®æ”¹ï¼›å†™å…¥æ–‡ä»¶ï¼Œæˆ–使用 ! 强制载入" +263 "文件在上一次完整写入åŽè¢«ä¿®æ”¹ï¼›å†™å…¥æ–‡ä»¶ï¼Œæˆ–使用 :edit! 强制编辑" +264 "文件在上一次完整写入åŽè¢«ä¿®æ”¹ï¼›å†™å…¥æ–‡ä»¶ï¼Œæˆ–使用 ! 强制执行" +265 "临时文件,退出将丢弃全部修改" +266 "文件åªè¯»ï¼Œä¿®æ”¹æœªè¢«è‡ªåЍ写入" +267 "日志é‡å¯" +268 "确认?[ynq]" +269 "按任æ„键继续:" +270 "按任æ„键继续[按 : 键输入 ex 命令]:" +271 "按任æ„键继续[按 q 键推出]:" +272 "该形å¼çš„ %s å‘½ä»¤éœ€è¦ ex 终端界é¢" +273 "进入 ex 输入模å¼" +274 "命令失败,尚未读入文件" +275 " 继续?" +276 "æ„外的字符事件" +277 "以外的文件尾事件" +278 "查询未找到匹é…" +279 "æ„外的中断事件" +280 "æ„外的退出事件" +281 "æ„外的é‡ç»˜äº‹ä»¶" +282 "å·²ä½äºŽè¿™ç»„ tags 中的最åŽä¸€ä¸ª" +283 "%s å‘½ä»¤éœ€è¦ ex 终端界é¢" +284 "secure ç¼–è¾‘é€‰é¡¹å¼€å¯æ—¶ä¸æ”¯æŒè¯¥å½¢å¼çš„ %s 命令" +285 "æ„外的字符串事件" +286 "æ„外的超时事件" +287 "æ„外的写入事件" +288 "分å±éœ€è¦å¤šäºŽ %d 列" +289 "secure ç¼–è¾‘é€‰é¡¹å¼€å¯æ—¶ä¸æ”¯æŒ shell 展开" +290 "secure ç¼–è¾‘é€‰é¡¹å¼€å¯æ—¶ä¸æ”¯æŒ %s 命令" +291 "set: 选项 %s ä¸èƒ½å…³é—­" +292 "显示空间太å°" +293 "已添加" +294 "已修改" +295 "已删除" +296 "å·²åˆå¹¶" +297 "已移动" +298 "å·²åç§»" +299 "å·²å¤åˆ¶" +300 "行" +301 "行" +303 "文件在上一次写入åŽè¢«ä¿®æ”¹" +304 "Shell 展开失败" +305 "未指定 %s 编辑选项" +307 "æ²¡æœ‰è¦æ‰§è¡Œçš„ ex 命令" +308 "键入回车执行一æ¡å‘½ä»¤ï¼Œ:q 退出" +309 "使用 \"cscope help\" 查看帮助" +310 "没有正在è¿è¡Œçš„ cscope 连接" +311 "%s: æœç´¢ç±»åž‹ä¸æ˜Žï¼šä½¿ç”¨ %s 中的一个" +312 "%d: æ— æ­¤ cscope 会è¯" +313 "set: 选项 %s ç»å¯¹ä¸èƒ½å¼€å¯" +314 "set: 选项 %s ç»å¯¹ä¸èƒ½è¢«è®¾ä¸º 0" +315 "%s: 已追加:%lu 行,%lu 个字符" +316 "æ„外的大å°è°ƒæ•´äº‹ä»¶" +317 "%d 个待编辑的文件" +319 "%d 个åŽå°å±å¹•;用 :display 列出它们" +320 "光标ä½ç½®ä¸æ˜Ž" +321 "䏿”¯æŒæ–‡ä»¶ç¼–ç è½¬æ¢" +322 "䏿”¯æŒè¾“入编ç è½¬æ¢" +323 "无效输入,已截断" +324 "第 %d 行上有转æ¢é”™è¯¯" diff --git a/usr.bin/vi/config.h b/usr.bin/vi/config.h index a8bee6775f8..94e780807fb 100644 --- a/usr.bin/vi/config.h +++ b/usr.bin/vi/config.h @@ -1,195 +1,23 @@ -/* config.h. Generated automatically by configure. */ -/* config.h.in. Generated automatically from configure.in by autoheader. */ +/* $Id: config.h.in,v 9.5 2013/03/11 01:20:53 zy Exp $ */ /* $FreeBSD$ */ -/* Define to empty if the keyword does not work. */ -/* #undef const */ - -/* Define if you have a working `mmap' system call. */ -#define HAVE_MMAP 1 - -/* Define if your struct stat has st_blksize. */ -#define HAVE_ST_BLKSIZE 1 - -/* Define if you have . */ -/* #undef HAVE_VFORK_H */ - -/* Define to `int' if doesn't define. */ -/* #undef mode_t */ - -/* Define to `long' if doesn't define. */ -/* #undef off_t */ - -/* Define to `int' if doesn't define. */ -/* #undef pid_t */ - -/* Define to `unsigned' if doesn't define. */ -/* #undef size_t */ - -/* Define if you have the ANSI C header files. */ -#define STDC_HEADERS 1 - -/* Define if your declares struct tm. */ -/* #undef TM_IN_SYS_TIME */ - -/* Define vfork as fork if vfork does not work. */ -/* #undef vfork */ - -/* Define if your processor stores words with the most significant - byte first (like Motorola and SPARC, unlike Intel and VAX). */ -/* #undef WORDS_BIGENDIAN */ - -/* Define to `int' if doesn't define. */ -/* #undef ssize_t */ - /* Define if you want a debugging version. */ /* #undef DEBUG */ -/* Define if you have a System V-style (broken) gettimeofday. */ -/* #undef HAVE_BROKEN_GETTIMEOFDAY */ +/* Define when using wide characters */ +/* #define USE_WIDECHAR set by Makefile */ -/* Define if you have a Ultrix-style (broken) vdisable. */ -/* #undef HAVE_BROKEN_VDISABLE */ +/* Define when iconv can be used */ +/* #define USE_ICONV set by Makefile */ -/* Define if you have a BSD version of curses. */ -#ifndef SYSV_CURSES -#define HAVE_BSD_CURSES 1 -#endif +/* Define when the 2nd argument of iconv(3) is not const */ +/* #undef ICONV_TRADITIONAL */ -/* Define if you have the curses(3) addnstr function. */ -#define HAVE_CURSES_ADDNSTR 1 +/* Define if you have */ +#define HAVE_LIBUTIL_H -/* Define if you have the curses(3) beep function. */ -#ifdef SYSV_CURSES -#define HAVE_CURSES_BEEP 1 -#endif +/* Define if you have */ +#define HAVE_NCURSES_H -/* Define if you have the curses(3) flash function. */ -#ifdef SYSV_CURSES -#define HAVE_CURSES_FLASH 1 -#endif - -/* Define if you have the curses(3) idlok function. */ -#define HAVE_CURSES_IDLOK 1 - -/* Define if you have the curses(3) keypad function. */ -#ifdef SYSV_CURSES -#define HAVE_CURSES_KEYPAD 1 -#endif - -/* Define if you have the curses(3) newterm function. */ -#ifdef SYSV_CURSES -#define HAVE_CURSES_NEWTERM 1 -#endif - -/* Define if you have the curses(3) setupterm function. */ -#ifdef SYSV_CURSES -#define HAVE_CURSES_SETUPTERM 1 -#endif - -/* Define if you have the curses(3) tigetstr/tigetnum functions. */ -#ifdef SYSV_CURSES -#define HAVE_CURSES_TIGETSTR 1 -#endif - -/* Define if you have the chsize(2) system call. */ -/* #undef HAVE_FTRUNCATE_CHSIZE */ - -/* Define if you have the ftruncate(2) system call. */ -#define HAVE_FTRUNCATE_FTRUNCATE 1 - -/* Define if you have fcntl(2) style locking. */ -/* #undef HAVE_LOCK_FCNTL */ - -/* Define if you have flock(2) style locking. */ -#define HAVE_LOCK_FLOCK 1 - -/* Define if you want to compile in the Perl interpreter. */ -/* #undef HAVE_PERL_INTERP */ /* XXX: SET IN Makefile CFLAGS */ - -/* Define if your Perl is at least 5.003_01. */ -/* #undef HAVE_PERL_5_003_01 */ /* XXX: SET IN Makefile CFLAGS */ - -/* Define if you have the Berkeley style revoke(2) system call. */ -#define HAVE_REVOKE 1 - -/* Define if you have */ -#define HAVE_SYS_MMAN_H 1 - -/* Define if you have */ -/* #undef HAVE_SYS_SELECT_H 1 */ - -/* Define if you have the System V style pty calls. */ -/* #undef HAVE_SYS5_PTY */ - -/* Define if you want to compile in the Tcl interpreter. */ -/* #define HAVE_TCL_INTERP */ /* XXX: SET IN Makefile CFLAGS */ - -/* Define if your sprintf returns a pointer, not a length. */ -/* #undef SPRINTF_RET_CHARPNT */ - -/* Define if you have the bsearch function. */ -#define HAVE_BSEARCH 1 - -/* Define if you have the gethostname function. */ -#define HAVE_GETHOSTNAME 1 - -/* Define if you have the getopt function. */ -#define HAVE_GETOPT 1 - -/* Define if you have the getpagesize function. */ -#define HAVE_GETPAGESIZE 1 - -/* Define if you have the memchr function. */ -#define HAVE_MEMCHR 1 - -/* Define if you have the memcpy function. */ -#define HAVE_MEMCPY 1 - -/* Define if you have the memmove function. */ -#define HAVE_MEMMOVE 1 - -/* Define if you have the memset function. */ -#define HAVE_MEMSET 1 - -/* Define if you have the mkstemp function. */ -#define HAVE_MKSTEMP 1 - -/* Define if you have the mmap function. */ -#define HAVE_MMAP 1 - -/* Define if you have the select function. */ -#define HAVE_SELECT 1 - -/* Define if you have the setenv function. */ -#define HAVE_SETENV 1 - -/* Define if you have the snprintf function. */ -#define HAVE_SNPRINTF 1 - -/* Define if you have the strdup function. */ -#define HAVE_STRDUP 1 - -/* Define if you have the strerror function. */ -#define HAVE_STRERROR 1 - -/* Define if you have the strpbrk function. */ -#define HAVE_STRPBRK 1 - -/* Define if you have the strsep function. */ -#define HAVE_STRSEP 1 - -/* Define if you have the strtol function. */ -#define HAVE_STRTOL 1 - -/* Define if you have the strtoul function. */ -#define HAVE_STRTOUL 1 - -/* Define if you have the unsetenv function. */ -#define HAVE_UNSETENV 1 - -/* Define if you have the valloc function. */ -#define HAVE_VALLOC 1 - -/* Define if you have the vsnprintf function. */ -#define HAVE_VSNPRINTF 1 +/* Define if you have */ +#define HAVE_TERM_H diff --git a/usr.bin/vi/pathnames.h b/usr.bin/vi/pathnames.h index 72c8bb3daa4..49d58fe8349 100644 --- a/usr.bin/vi/pathnames.h +++ b/usr.bin/vi/pathnames.h @@ -1,19 +1,15 @@ -/* @(#)pathnames.h.in 8.4 (Berkeley) 6/26/96 */ +/* $Id: pathnames.h.in,v 8.7 2012/04/23 08:34:52 zy Exp $ */ /* $FreeBSD$ */ /* Read standard system paths first. */ #include -#ifndef _PATH_BSHELL -#define _PATH_BSHELL "/bin/sh" -#endif - #ifndef _PATH_EXRC #define _PATH_EXRC ".exrc" #endif #ifndef _PATH_MSGCAT -#define _PATH_MSGCAT "/usr/share/vi/catalog/" +#define _PATH_MSGCAT "/usr/share/vi/catalog/" #endif #ifndef _PATH_NEXRC @@ -21,15 +17,7 @@ #endif #ifndef _PATH_PRESERVE -#define _PATH_PRESERVE "/var/tmp/vi.recover" -#endif - -#ifndef _PATH_SYSV_PTY -#define _PATH_SYSV_PTY "/dev/ptmx" -#endif - -#ifndef _PATH_SENDMAIL -#define _PATH_SENDMAIL "/usr/sbin/sendmail" +#define _PATH_PRESERVE "/var/tmp/vi.recover/" #endif #ifndef _PATH_SYSEXRC @@ -39,11 +27,3 @@ #ifndef _PATH_TAGS #define _PATH_TAGS "tags" #endif - -#ifndef _PATH_TMP -#define _PATH_TMP "/tmp" -#endif - -#ifndef _PATH_TTY -#define _PATH_TTY "/dev/tty" -#endif diff --git a/usr.bin/vi/port.h b/usr.bin/vi/port.h deleted file mode 100644 index bdd5faf2cf5..00000000000 --- a/usr.bin/vi/port.h +++ /dev/null @@ -1,170 +0,0 @@ -/* @(#)port.h.in 8.13 (Berkeley) 6/12/96 */ - -/* $FreeBSD$ */ - -/* - * Declare the basic types, if they aren't already declared. Named and - * some system's db.h files protect them with __BIT_TYPES_DEFINED__. - */ -#ifndef __BIT_TYPES_DEFINED__ -#define __BIT_TYPES_DEFINED__ - - - - - -#endif - -/* - * XXX - * Some versions of System V changed the number of arguments to gettimeofday - * without changing the name. - */ -#ifdef HAVE_BROKEN_GETTIMEOFDAY -#define gettimeofday(tv, tz) gettimeofday(tv) -#endif - -/* - * XXX - * If we don't have mmap, we fake it with read and write, but we'll - * still need the header information. - */ -#ifndef HAVE_SYS_MMAN_H -#define MAP_SHARED 1 /* share changes */ -#define MAP_PRIVATE 2 /* changes are private */ -#define PROT_READ 0x1 /* pages can be read */ -#define PROT_WRITE 0x2 /* pages can be written */ -#define PROT_EXEC 0x4 /* pages can be executed */ -#endif - -/* - * XXX - * POSIX 1003.1 names for file descriptors. - */ -#ifndef STDERR_FILENO -#define STDIN_FILENO 0 /* ANSI C #defines */ -#define STDOUT_FILENO 1 -#define STDERR_FILENO 2 -#endif - -/* - * XXX - * POSIX 1003.1 names for seek settings. - */ -#ifndef SEEK_END -#define SEEK_SET 0 /* POSIX 1003.1 seek values */ -#define SEEK_CUR 1 -#define SEEK_END 2 -#endif - -/* - * Hack _POSIX_VDISABLE to \377 since Ultrix doesn't honor _POSIX_VDISABLE - * (treats it as ^@). The symptom is that the ^@ keystroke immediately - * drops core. - */ -#ifdef HAVE_BROKEN_VDISABLE -#undef _POSIX_VDISABLE -#define _POSIX_VDISABLE ((unsigned char)'\377') -#endif - -/* - * XXX - * POSIX 1003.1 tty disabling character. - */ -#ifndef _POSIX_VDISABLE -#define _POSIX_VDISABLE 0 /* Some systems used 0. */ -#endif - -/* - * XXX - * 4.4BSD extension to only set the software termios bits. - */ -#ifndef TCSASOFT /* 4.4BSD extension. */ -#define TCSASOFT 0 -#endif - -/* - * XXX - * POSIX 1003.1 maximum path length. - */ -#ifndef MAXPATHLEN -#ifdef PATH_MAX -#define MAXPATHLEN PATH_MAX -#else -#define MAXPATHLEN 1024 -#endif -#endif - -/* - * XXX - * MIN, MAX, historically in - */ -#ifndef MAX -#define MAX(_a,_b) ((_a)<(_b)?(_b):(_a)) -#endif -#ifndef MIN -#define MIN(_a,_b) ((_a)<(_b)?(_a):(_b)) -#endif - -/* - * XXX - * "DB" isn't always portable, and we want the private information. - */ -#define DB L__DB -#undef pgno_t /* IRIX has its own version. */ -#define pgno_t L__db_pgno_t - -/* - * XXX - * 4.4BSD extension to provide lock values in the open(2) call. - */ -#ifndef O_EXLOCK -#define O_EXLOCK 0 -#endif - -#ifndef O_SHLOCK -#define O_SHLOCK 0 -#endif - -/* - * XXX - * POSIX 1003.1 bad file format errno. - */ -#ifndef EFTYPE -#define EFTYPE EINVAL -#endif - -/* - * XXX - * POSIX 1003.2 RE length limit. - */ -#ifndef _POSIX2_RE_DUP_MAX -#define _POSIX2_RE_DUP_MAX 255 -#endif - -/* - * XXX - * 4.4BSD extension to determine if a program dropped core from the exit - * status. - */ -#ifndef WCOREDUMP -#define WCOREDUMP(a) 0 -#endif - -/* - * XXX - * Endian-ness of the machine. - */ -#if !defined(LITTLE_ENDIAN) -#define LITTLE_ENDIAN 1234 -#endif -#if !defined(BIG_ENDIAN) -#define BIG_ENDIAN 4321 -#endif -#if !defined(BYTE_ORDER) -#if WORDS_BIGENDIAN == 1 -#define BYTE_ORDER BIG_ENDIAN -#else -#define BYTE_ORDER LITTLE_ENDIAN -#endif -#endif diff --git a/usr.sbin/Makefile b/usr.sbin/Makefile index acdc6131f1f..102593b6de4 100644 --- a/usr.sbin/Makefile +++ b/usr.sbin/Makefile @@ -17,6 +17,7 @@ SUBDIR= adduser \ crashinfo \ cron \ ctladm \ + ctld \ daemon \ dconschat \ devinfo \ @@ -35,6 +36,7 @@ SUBDIR= adduser \ ifmcstat \ inetd \ iostat \ + iscsid \ isfctl \ kldxref \ mailwrapper \ @@ -129,6 +131,7 @@ SUBDIR+= dnssec-keygen SUBDIR+= dnssec-revoke SUBDIR+= dnssec-settime SUBDIR+= dnssec-signzone +SUBDIR+= dnssec-verify .endif .if ${MK_BIND_NAMED} != "no" SUBDIR+= arpaname @@ -314,6 +317,10 @@ SUBDIR+= config SUBDIR+= crunch .endif +.if ${MK_UNBOUND} != "no" +SUBDIR+= unbound +.endif + .if ${MK_USB} != "no" SUBDIR+= uathload SUBDIR+= uhsoctl diff --git a/usr.sbin/bhyve/acpi.c b/usr.sbin/bhyve/acpi.c index ed0c545e5f8..89c11bce3cb 100644 --- a/usr.sbin/bhyve/acpi.c +++ b/usr.sbin/bhyve/acpi.c @@ -241,8 +241,9 @@ basl_fwrite_madt(FILE *fp) for (i = 0; i < basl_ncpu; i++) { EFPRINTF(fp, "[0001]\t\tSubtable Type : 00\n"); EFPRINTF(fp, "[0001]\t\tLength : 08\n"); - EFPRINTF(fp, "[0001]\t\tProcessor ID : %02d\n", i); - EFPRINTF(fp, "[0001]\t\tLocal Apic ID : %02d\n", i); + /* iasl expects hex values for the proc and apic id's */ + EFPRINTF(fp, "[0001]\t\tProcessor ID : %02x\n", i); + EFPRINTF(fp, "[0001]\t\tLocal Apic ID : %02x\n", i); EFPRINTF(fp, "[0004]\t\tFlags (decoded below) : 00000001\n"); EFPRINTF(fp, "\t\t\tProcessor Enabled : 1\n"); EFPRINTF(fp, "\n"); @@ -251,7 +252,8 @@ basl_fwrite_madt(FILE *fp) /* Always a single IOAPIC entry, with ID ncpu+1 */ EFPRINTF(fp, "[0001]\t\tSubtable Type : 01\n"); EFPRINTF(fp, "[0001]\t\tLength : 0C\n"); - EFPRINTF(fp, "[0001]\t\tI/O Apic ID : %02d\n", basl_ncpu); + /* iasl expects a hex value for the i/o apic id */ + EFPRINTF(fp, "[0001]\t\tI/O Apic ID : %02x\n", basl_ncpu); EFPRINTF(fp, "[0001]\t\tReserved : 00\n"); EFPRINTF(fp, "[0004]\t\tAddress : fec00000\n"); EFPRINTF(fp, "[0004]\t\tInterrupt : 00000000\n"); diff --git a/usr.sbin/bhyve/ioapic.c b/usr.sbin/bhyve/ioapic.c index c712692c937..aeb008ea2c2 100644 --- a/usr.sbin/bhyve/ioapic.c +++ b/usr.sbin/bhyve/ioapic.c @@ -101,13 +101,13 @@ ioapic_set_pinstate(struct vmctx *ctx, int pin, bool newstate) * XXX * We only deal with: * - edge triggered interrupts - * - physical destination mode * - fixed delivery mode + * Level-triggered sources will work so long as there is + * no sharing. */ low = ioapic->redtbl[pin]; high = ioapic->redtbl[pin] >> 32; if ((low & IOART_INTMASK) == IOART_INTMCLR && - (low & IOART_TRGRMOD) == IOART_TRGREDG && (low & IOART_DESTMOD) == IOART_DESTPHY && (low & IOART_DELMOD) == IOART_DELFIXED) { vector = low & IOART_INTVEC; diff --git a/usr.sbin/bhyve/mevent.c b/usr.sbin/bhyve/mevent.c index a6109dbbd94..eff061017f4 100644 --- a/usr.sbin/bhyve/mevent.c +++ b/usr.sbin/bhyve/mevent.c @@ -59,12 +59,15 @@ __FBSDID("$FreeBSD$"); extern char *vmname; static pthread_t mevent_tid; +static int mevent_timid = 43; static int mevent_pipefd[2]; static pthread_mutex_t mevent_lmutex = PTHREAD_MUTEX_INITIALIZER; struct mevent { void (*me_func)(int, enum ev_type, void *); +#define me_msecs me_fd int me_fd; + int me_timid; enum ev_type me_type; void *me_param; int me_cq; @@ -129,6 +132,9 @@ mevent_kq_filter(struct mevent *mevp) if (mevp->me_type == EVF_WRITE) retval = EVFILT_WRITE; + if (mevp->me_type == EVF_TIMER) + retval = EVFILT_TIMER; + return (retval); } @@ -140,6 +146,8 @@ mevent_kq_flags(struct mevent *mevp) switch (mevp->me_state) { case MEV_ENABLE: ret = EV_ADD; + if (mevp->me_type == EVF_TIMER) + ret |= EV_ENABLE; break; case MEV_DISABLE: ret = EV_DISABLE; @@ -177,11 +185,16 @@ mevent_build(int mfd, struct kevent *kev) */ close(mevp->me_fd); } else { - kev[i].ident = mevp->me_fd; + if (mevp->me_type == EVF_TIMER) { + kev[i].ident = mevp->me_timid; + kev[i].data = mevp->me_msecs; + } else { + kev[i].ident = mevp->me_fd; + kev[i].data = 0; + } kev[i].filter = mevent_kq_filter(mevp); kev[i].flags = mevent_kq_flags(mevp); kev[i].fflags = mevent_kq_fflags(mevp); - kev[i].data = 0; kev[i].udata = mevp; i++; } @@ -219,12 +232,12 @@ mevent_handle(struct kevent *kev, int numev) } struct mevent * -mevent_add(int fd, enum ev_type type, +mevent_add(int tfd, enum ev_type type, void (*func)(int, enum ev_type, void *), void *param) { struct mevent *lp, *mevp; - if (fd < 0 || func == NULL) { + if (tfd < 0 || func == NULL) { return (NULL); } @@ -236,13 +249,15 @@ mevent_add(int fd, enum ev_type type, * Verify that the fd/type tuple is not present in any list */ LIST_FOREACH(lp, &global_head, me_list) { - if (lp->me_fd == fd && lp->me_type == type) { + if (type != EVF_TIMER && lp->me_fd == tfd && + lp->me_type == type) { goto exit; } } LIST_FOREACH(lp, &change_head, me_list) { - if (lp->me_fd == fd && lp->me_type == type) { + if (type != EVF_TIMER && lp->me_fd == tfd && + lp->me_type == type) { goto exit; } } @@ -256,7 +271,11 @@ mevent_add(int fd, enum ev_type type, } memset(mevp, 0, sizeof(struct mevent)); - mevp->me_fd = fd; + if (type == EVF_TIMER) { + mevp->me_msecs = tfd; + mevp->me_timid = mevent_timid++; + } else + mevp->me_fd = tfd; mevp->me_type = type; mevp->me_func = func; mevp->me_param = param; diff --git a/usr.sbin/bhyve/mevent.h b/usr.sbin/bhyve/mevent.h index 32a9d74ab56..6c0f6564801 100644 --- a/usr.sbin/bhyve/mevent.h +++ b/usr.sbin/bhyve/mevent.h @@ -31,7 +31,8 @@ enum ev_type { EVF_READ, - EVF_WRITE + EVF_WRITE, + EVF_TIMER }; struct mevent; diff --git a/usr.sbin/bhyve/mevent_test.c b/usr.sbin/bhyve/mevent_test.c index c72a4971818..9c68ff7874a 100644 --- a/usr.sbin/bhyve/mevent_test.c +++ b/usr.sbin/bhyve/mevent_test.c @@ -34,12 +34,16 @@ */ #include +#include +#include #include #include +#include #include #include #include +#include #include "mevent.h" @@ -48,8 +52,62 @@ static pthread_mutex_t accept_mutex = PTHREAD_MUTEX_INITIALIZER; static pthread_cond_t accept_condvar = PTHREAD_COND_INITIALIZER; +static struct mevent *tevp; + +char *vmname = "test vm"; + + #define MEVENT_ECHO +/* Number of timer events to capture */ +#define TEVSZ 4096 +uint64_t tevbuf[TEVSZ]; + +static void +timer_print(void) +{ + uint64_t min, max, diff, sum, tsc_freq; + size_t len; + int j; + + min = UINT64_MAX; + max = 0; + sum = 0; + + len = sizeof(tsc_freq); + sysctlbyname("machdep.tsc_freq", &tsc_freq, &len, NULL, 0); + + for (j = 1; j < TEVSZ; j++) { + /* Convert a tsc diff into microseconds */ + diff = (tevbuf[j] - tevbuf[j-1]) * 1000000 / tsc_freq; + sum += diff; + if (min > diff) + min = diff; + if (max < diff) + max = diff; + } + + printf("timers done: usecs, min %ld, max %ld, mean %ld\n", min, max, + sum/(TEVSZ - 1)); +} + +static void +timer_callback(int fd, enum ev_type type, void *param) +{ + static int i; + + if (i >= TEVSZ) + abort(); + + tevbuf[i++] = rdtsc(); + + if (i == TEVSZ) { + mevent_delete(tevp); + timer_print(); + } +} + + #ifdef MEVENT_ECHO struct esync { pthread_mutex_t e_mt; @@ -101,6 +159,8 @@ echoer(void *param) pthread_mutex_unlock(&sync.e_mt); pthread_mutex_destroy(&sync.e_mt); pthread_cond_destroy(&sync.e_cond); + + return (NULL); } #else @@ -115,6 +175,8 @@ echoer(void *param) while ((len = read(fd, buf, sizeof(buf))) > 0) { write(1, buf, len); } + + return (NULL); } #endif /* MEVENT_ECHO */ @@ -133,6 +195,7 @@ acceptor(void *param) pthread_t tid; int news; int s; + static int first; if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) { perror("socket"); @@ -163,11 +226,24 @@ acceptor(void *param) if (news < 0) { perror("accept error"); } else { + static int first = 1; + + if (first) { + /* + * Start a timer + */ + first = 0; + tevp = mevent_add(1, EVF_TIMER, timer_callback, + NULL); + } + printf("incoming connection, spawning thread\n"); pthread_create(&tid, NULL, echoer, (void *)(uintptr_t)news); } } + + return (NULL); } main() diff --git a/usr.sbin/bhyve/pci_emul.c b/usr.sbin/bhyve/pci_emul.c index 00adf622b09..626dfdf0144 100644 --- a/usr.sbin/bhyve/pci_emul.c +++ b/usr.sbin/bhyve/pci_emul.c @@ -245,8 +245,12 @@ pci_emul_msix_tread(struct pci_devinst *pi, uint64_t offset, int size) int tab_index; uint64_t retval = ~0; - /* support only 4 or 8 byte reads */ - if (size != 4 && size != 8) + /* + * The PCI standard only allows 4 and 8 byte accesses to the MSI-X + * table but we also allow 1 byte access to accomodate reads from + * ddb. + */ + if (size != 1 && size != 4 && size != 8) return (retval); msix_entry_offset = offset % MSIX_TABLE_ENTRY_SIZE; @@ -263,7 +267,9 @@ pci_emul_msix_tread(struct pci_devinst *pi, uint64_t offset, int size) dest = (char *)(pi->pi_msix.table + tab_index); dest += msix_entry_offset; - if (size == 4) + if (size == 1) + retval = *((uint8_t *)dest); + else if (size == 4) retval = *((uint32_t *)dest); else retval = *((uint64_t *)dest); @@ -1008,6 +1014,16 @@ init_pci(struct vmctx *ctx) pci_emul_membase32 = vm_get_lowmem_limit(ctx); pci_emul_membase64 = PCI_EMUL_MEMBASE64; + /* + * Allow ISA IRQs 5,10,11,12, and 15 to be available for + * generic use + */ + lirq[5].li_generic = 1; + lirq[10].li_generic = 1; + lirq[11].li_generic = 1; + lirq[12].li_generic = 1; + lirq[15].li_generic = 1; + for (slot = 0; slot < MAXSLOTS; slot++) { for (func = 0; func < MAXFUNCS; func++) { si = &pci_slotinfo[slot][func]; @@ -1022,16 +1038,6 @@ init_pci(struct vmctx *ctx) } } - /* - * Allow ISA IRQs 5,10,11,12, and 15 to be available for - * generic use - */ - lirq[5].li_generic = 1; - lirq[10].li_generic = 1; - lirq[11].li_generic = 1; - lirq[12].li_generic = 1; - lirq[15].li_generic = 1; - /* * The guest physical memory map looks like the following: * [0, lowmem) guest system memory diff --git a/usr.sbin/bhyve/pci_virtio_block.c b/usr.sbin/bhyve/pci_virtio_block.c index 439541011a3..76f681b4e21 100644 --- a/usr.sbin/bhyve/pci_virtio_block.c +++ b/usr.sbin/bhyve/pci_virtio_block.c @@ -156,7 +156,7 @@ pci_vtblk_proc(struct pci_vtblk_softc *sc, struct vqueue_info *vq) * XXX - note - this fails on crash dump, which does a * VIRTIO_BLK_T_FLUSH with a zero transfer length */ - assert (n >= 3 && n < VTBLK_MAXSEGS + 2); + assert (n >= 3 && n <= VTBLK_MAXSEGS + 2); assert((flags[0] & VRING_DESC_F_WRITE) == 0); assert(iov[0].iov_len == sizeof(struct virtio_blk_hdr)); diff --git a/usr.sbin/bhyve/pit_8254.c b/usr.sbin/bhyve/pit_8254.c index c96596a9f6f..44d0ccac32a 100644 --- a/usr.sbin/bhyve/pit_8254.c +++ b/usr.sbin/bhyve/pit_8254.c @@ -29,15 +29,23 @@ #include __FBSDID("$FreeBSD$"); +#include #include +#include #include -#include #include +#include +#include +#include + +#include #include "bhyverun.h" #include "inout.h" +#include "ioapic.h" +#include "mevent.h" #include "pit_8254.h" #define TIMER_SEL_MASK 0xc0 @@ -51,14 +59,19 @@ __FBSDID("$FreeBSD$"); static const int nsecs_per_tick = 1000000000 / PIT_8254_FREQ; struct counter { + struct vmctx *ctx; + struct mevent *tevp; struct timeval tv; /* uptime when counter was loaded */ + int mode; uint16_t initial; /* initial counter value */ uint8_t cr[2]; uint8_t ol[2]; int crbyte; int olbyte; + int frbyte; }; + static void timevalfix(struct timeval *t1) { @@ -82,16 +95,55 @@ timevalsub(struct timeval *t1, const struct timeval *t2) timevalfix(t1); } +static uint64_t pit_mev_count; + static void -latch(struct counter *c) +pit_mevent_cb(int fd, enum ev_type type, void *param) +{ + struct counter *c; + + c = param; + + pit_mev_count++; + + ioapic_assert_pin(c->ctx, 0); + ioapic_deassert_pin(c->ctx, 0); + + /* + * Delete the timer for one-shots + */ + if (c->mode != TIMER_RATEGEN) { + mevent_delete(c->tevp); + c->tevp = NULL; + } +} + +static void +pit_timer_start(struct vmctx *ctx, struct counter *c) +{ + int msecs; + + if (c->initial != 0) { + msecs = c->initial * nsecs_per_tick / 1000000; + if (msecs == 0) + msecs = 1; + + if (c->tevp == NULL) + c->tevp = mevent_add(msecs, EVF_TIMER, pit_mevent_cb, + c); + } +} + +static uint16_t +pit_update_counter(struct counter *c, int latch) { struct timeval tv2; uint16_t lval; uint64_t delta_nsecs, delta_ticks; /* cannot latch a new value until the old one has been consumed */ - if (c->olbyte != 0) - return; + if (latch && c->olbyte != 0) + return (0); if (c->initial == 0 || c->initial == 1) { /* @@ -114,9 +166,14 @@ latch(struct counter *c) delta_ticks = delta_nsecs / nsecs_per_tick; lval = c->initial - delta_ticks % c->initial; - c->olbyte = 2; - c->ol[1] = lval; /* LSB */ - c->ol[0] = lval >> 8; /* MSB */ + + if (latch) { + c->olbyte = 2; + c->ol[1] = lval; /* LSB */ + c->ol[0] = lval >> 8; /* MSB */ + } + + return (lval); } static int @@ -150,13 +207,18 @@ pit_8254_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes, * Counter mode is not affected when issuing a * latch command. */ - if (mode != TIMER_RATEGEN && mode != TIMER_SQWAVE) + if (mode != TIMER_INTTC && + mode != TIMER_RATEGEN && + mode != TIMER_SQWAVE && + mode != TIMER_SWSTROBE) return (-1); } c = &counter[sel >> 6]; + c->ctx = ctx; + c->mode = mode; if (rw == TIMER_LATCH) - latch(c); + pit_update_counter(c, 1); else c->olbyte = 0; /* reset latch after reprogramming */ @@ -169,20 +231,32 @@ pit_8254_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes, if (in) { /* - * XXX * The spec says that once the output latch is completely - * read it should revert to "following" the counter. We don't - * do this because it is hard and any reasonable OS should - * always latch the counter before trying to read it. + * read it should revert to "following" the counter. Use + * the free running counter for this case (i.e. Linux + * TSC calibration). Assuming the access mode is 16-bit, + * toggle the MSB/LSB bit on each read. */ - if (c->olbyte == 0) - c->olbyte = 2; - *eax = c->ol[--c->olbyte]; + if (c->olbyte == 0) { + uint16_t tmp; + + tmp = pit_update_counter(c, 0); + if (c->frbyte) + tmp >>= 8; + tmp &= 0xff; + *eax = tmp; + c->frbyte ^= 1; + } else + *eax = c->ol[--c->olbyte]; } else { c->cr[c->crbyte++] = *eax; if (c->crbyte == 2) { + c->frbyte = 0; c->crbyte = 0; c->initial = c->cr[0] | (uint16_t)c->cr[1] << 8; + /* Start an interval timer for counter 0 */ + if (port == 0x40) + pit_timer_start(ctx, c); if (c->initial == 0) c->initial = 0xffff; gettimeofday(&c->tv, NULL); diff --git a/usr.sbin/bhyve/rtc.c b/usr.sbin/bhyve/rtc.c index a0e2c6a5c5c..e2b9f30f2e5 100644 --- a/usr.sbin/bhyve/rtc.c +++ b/usr.sbin/bhyve/rtc.c @@ -46,8 +46,11 @@ __FBSDID("$FreeBSD$"); #define IO_RTC 0x70 #define RTC_SEC 0x00 /* seconds */ +#define RTC_SEC_ALARM 0x01 #define RTC_MIN 0x02 +#define RTC_MIN_ALARM 0x03 #define RTC_HRS 0x04 +#define RTC_HRS_ALARM 0x05 #define RTC_WDAY 0x06 #define RTC_DAY 0x07 #define RTC_MONTH 0x08 @@ -94,6 +97,12 @@ static uint8_t rtc_nvram[RTC_NVRAM_SZ]; /* XXX initialize these to default values as they would be from BIOS */ static uint8_t status_a, status_b; +static struct { + uint8_t hours; + uint8_t mins; + uint8_t secs; +} rtc_alarm; + static u_char const bin2bcd_data[] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, @@ -148,8 +157,11 @@ rtc_addr_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes, switch (*eax & 0x7f) { case RTC_SEC: + case RTC_SEC_ALARM: case RTC_MIN: + case RTC_MIN_ALARM: case RTC_HRS: + case RTC_HRS_ALARM: case RTC_WDAY: case RTC_DAY: case RTC_MONTH: @@ -199,6 +211,15 @@ rtc_data_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes, if (in) { switch (addr) { + case RTC_SEC_ALARM: + *eax = rtc_alarm.secs; + break; + case RTC_MIN_ALARM: + *eax = rtc_alarm.mins; + break; + case RTC_HRS_ALARM: + *eax = rtc_alarm.hours; + break; case RTC_SEC: *eax = rtcout(tm.tm_sec); return (0); @@ -266,6 +287,15 @@ rtc_data_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes, case RTC_STATUSD: /* ignore write */ break; + case RTC_SEC_ALARM: + rtc_alarm.secs = *eax; + break; + case RTC_MIN_ALARM: + rtc_alarm.mins = *eax; + break; + case RTC_HRS_ALARM: + rtc_alarm.hours = *eax; + break; case RTC_SEC: case RTC_MIN: case RTC_HRS: @@ -322,7 +352,7 @@ rtc_init(struct vmctx *ctx) himem /= m_64KB; rtc_nvram[nvoff(RTC_HMEM_LSB)] = himem; rtc_nvram[nvoff(RTC_HMEM_SB)] = himem >> 8; - rtc_nvram[nvoff(RTC_NVRAM_START)] = himem >> 16; + rtc_nvram[nvoff(RTC_HMEM_MSB)] = himem >> 16; } } diff --git a/usr.sbin/bhyve/virtio.c b/usr.sbin/bhyve/virtio.c index cdc92280cdf..c26272bc071 100644 --- a/usr.sbin/bhyve/virtio.c +++ b/usr.sbin/bhyve/virtio.c @@ -139,7 +139,8 @@ vi_intr_init(struct virtio_softc *vs, int barnum, int use_msix) return (1); } else { vs->vs_flags &= ~VIRTIO_USE_MSIX; - pci_emul_add_msicap(vs->vs_pi, barnum); + /* Only 1 MSI vector for bhyve */ + pci_emul_add_msicap(vs->vs_pi, 1); } return (0); } diff --git a/usr.sbin/boot98cfg/boot98cfg.c b/usr.sbin/boot98cfg/boot98cfg.c index f1ae832922b..0ed1b170c66 100644 --- a/usr.sbin/boot98cfg/boot98cfg.c +++ b/usr.sbin/boot98cfg/boot98cfg.c @@ -271,7 +271,7 @@ write_boot(const char *disk, u_char *boot) warnx("%s: %s", disk, q); gctl_free(grq); - for (i = 0; i < NDOSPART; i++) { + for (i = 0; i < PC98_NPARTS; i++) { snprintf(buf, sizeof(buf), "%ss%d", disk, i + 1); fd = open(buf, O_RDONLY); if (fd < 0) diff --git a/usr.sbin/bsdconfig/examples/browse_packages.sh b/usr.sbin/bsdconfig/examples/browse_packages.sh index 6396f467730..1deb562481e 100755 --- a/usr.sbin/bsdconfig/examples/browse_packages.sh +++ b/usr.sbin/bsdconfig/examples/browse_packages.sh @@ -17,9 +17,8 @@ if [ ! -e "$TMPDIR/packages/INDEX" ]; then # For older releases, use ftp://ftp-archive.freebsd.org mediaSetFTP mediaOpen - f_show_info "Downloading packages/INDEX from %s" "$_ftpPath" + f_show_info "Downloading packages/INDEX from\n %s" "$_ftpPath" f_device_get media packages/INDEX > $TMPDIR/packages/INDEX - mediaClose fi _directoryPath=$TMPDIR mediaSetDirectory diff --git a/usr.sbin/bsdinstall/scripts/mirrorselect b/usr.sbin/bsdinstall/scripts/mirrorselect index aa6c97279aa..0776a4d8821 100755 --- a/usr.sbin/bsdinstall/scripts/mirrorselect +++ b/usr.sbin/bsdinstall/scripts/mirrorselect @@ -162,7 +162,7 @@ exec 3>&- _UNAME_R=`uname -r` case ${_UNAME_R} in - *-CURRENT|*-STABLE|*-BETA*|*-PRERELEASE|*-RC*) + *-CURRENT|*-STABLE|*-PRERELEASE) RELDIR="snapshots" ;; *) diff --git a/usr.sbin/crashinfo/crashinfo.sh b/usr.sbin/crashinfo/crashinfo.sh index b1bfc06cfb8..cf60a62ae8f 100755 --- a/usr.sbin/crashinfo/crashinfo.sh +++ b/usr.sbin/crashinfo/crashinfo.sh @@ -268,9 +268,9 @@ netstat -M $VMCORE -N $KERNEL -m echo echo "------------------------------------------------------------------------" -echo "netstat -id" +echo "netstat -idW" echo -netstat -M $VMCORE -N $KERNEL -id +netstat -M $VMCORE -N $KERNEL -idW echo echo "------------------------------------------------------------------------" diff --git a/usr.sbin/ctladm/ctladm.8 b/usr.sbin/ctladm/ctladm.8 index 68e79ff9816..b9d4e61ae83 100644 --- a/usr.sbin/ctladm/ctladm.8 +++ b/usr.sbin/ctladm/ctladm.8 @@ -197,6 +197,16 @@ .Nm .Ic dumpstructs .Nm +.Ic islist +.Op Fl v +.Op Fl x +.Nm +.Ic islogout +.Aq Fl a | Fl h Ar host | Fl c Ar connection-id | Fl i Ar name +.Nm +.Ic isterminate +.Aq Fl a | Fl h Ar host | Fl c Ar connection-id | Fl i Ar name +.Nm .Ic help .Sh DESCRIPTION The @@ -883,6 +893,41 @@ If you specify .Fl x , the entire LUN database is displayed in XML format. .El +.It Ic islist +Get a list of currently running iSCSI connections. +This includes initiator and target names and the unique connection IDs. +.Bl -tag -width 11n +.It Fl v +Verbose mode. +.It Fl x +Dump the raw XML. +The connections list information from the kernel comes in XML format, and this +option allows the display of the raw XML data. +.El +.It Ic islogout +Ask the initiator to log out iSCSI connections matching criteria. +.Bl -tag -width 11n +.It Fl a +Log out all connections. +.It Fl h +Specify initiator IP address. +.It Fl c +Specify connection ID. +.It Fl i +Specify initiator name. +.El +.It Ic isterminate +Forcibly terminate iSCSI connections matching criteria. +.Bl -tag -width 11n +.It Fl a +Terminate all connections. +.It Fl h +Specify initiator IP address. +.It Fl c +Specify connection ID. +.It Fl i +Specify initiator name. +.El .It Ic help Display .Nm @@ -977,7 +1022,8 @@ This will result in a sense key of NOT READY (0x02), and an ASC/ASCQ of .Xr cam 4 , .Xr ctl 4 , .Xr xpt 4 , -.Xr camcontrol 8 +.Xr camcontrol 8 , +.Xr ctld 8 .Sh HISTORY The .Nm diff --git a/usr.sbin/ctladm/ctladm.c b/usr.sbin/ctladm/ctladm.c index 17c1148e83f..d26bf72d140 100644 --- a/usr.sbin/ctladm/ctladm.c +++ b/usr.sbin/ctladm/ctladm.c @@ -117,7 +117,10 @@ typedef enum { CTLADM_CMD_PRES_OUT, CTLADM_CMD_INQ_VPD_DEVID, CTLADM_CMD_RTPG, - CTLADM_CMD_MODIFY + CTLADM_CMD_MODIFY, + CTLADM_CMD_ISLIST, + CTLADM_CMD_ISLOGOUT, + CTLADM_CMD_ISTERMINATE } ctladm_cmdfunction; typedef enum { @@ -180,6 +183,9 @@ static struct ctladm_opts option_table[] = { {"help", CTLADM_CMD_HELP, CTLADM_ARG_NONE, NULL}, {"inject", CTLADM_CMD_ERR_INJECT, CTLADM_ARG_NEED_TL, "cd:i:p:r:s:"}, {"inquiry", CTLADM_CMD_INQUIRY, CTLADM_ARG_NEED_TL, NULL}, + {"islist", CTLADM_CMD_ISLIST, CTLADM_ARG_NONE, "vx"}, + {"islogout", CTLADM_CMD_ISLOGOUT, CTLADM_ARG_NONE, "ah:c:i:"}, + {"isterminate", CTLADM_CMD_ISTERMINATE, CTLADM_ARG_NONE, "ah:c:i:"}, {"lunlist", CTLADM_CMD_LUNLIST, CTLADM_ARG_NONE, NULL}, {"modesense", CTLADM_CMD_MODESENSE, CTLADM_ARG_NEED_TL, "P:S:dlm:c:"}, {"modify", CTLADM_CMD_MODIFY, CTLADM_ARG_NONE, "b:l:s:"}, @@ -489,6 +495,9 @@ retry: case CTL_PORT_ISC: type = "ISC"; break; + case CTL_PORT_ISCSI: + type = "ISCSI"; + break; default: type = "UNKNOWN"; break; @@ -578,6 +587,7 @@ static struct ctladm_opts cctl_fe_table[] = { {"fc", CTL_PORT_FC, CTLADM_ARG_NONE, NULL}, {"scsi", CTL_PORT_SCSI, CTLADM_ARG_NONE, NULL}, {"internal", CTL_PORT_INTERNAL, CTLADM_ARG_NONE, NULL}, + {"iscsi", CTL_PORT_ISCSI, CTLADM_ARG_NONE, NULL}, {"all", CTL_PORT_ALL, CTLADM_ARG_NONE, NULL}, {NULL, 0, 0, NULL} }; @@ -3399,6 +3409,403 @@ bailout: return (retval); } +struct cctl_islist_conn { + int connection_id; + char *initiator; + char *initiator_addr; + char *initiator_alias; + char *target; + char *target_alias; + char *header_digest; + char *data_digest; + char *max_data_segment_length;; + int immediate_data; + int iser; + STAILQ_ENTRY(cctl_islist_conn) links; +}; + +struct cctl_islist_data { + int num_conns; + STAILQ_HEAD(,cctl_islist_conn) conn_list; + struct cctl_islist_conn *cur_conn; + int level; + struct sbuf *cur_sb[32]; +}; + +static void +cctl_islist_start_element(void *user_data, const char *name, const char **attr) +{ + int i; + struct cctl_islist_data *islist; + struct cctl_islist_conn *cur_conn; + + islist = (struct cctl_islist_data *)user_data; + cur_conn = islist->cur_conn; + islist->level++; + if ((u_int)islist->level > (sizeof(islist->cur_sb) / + sizeof(islist->cur_sb[0]))) + errx(1, "%s: too many nesting levels, %zd max", __func__, + sizeof(islist->cur_sb) / sizeof(islist->cur_sb[0])); + + islist->cur_sb[islist->level] = sbuf_new_auto(); + if (islist->cur_sb[islist->level] == NULL) + err(1, "%s: Unable to allocate sbuf", __func__); + + if (strcmp(name, "connection") == 0) { + if (cur_conn != NULL) + errx(1, "%s: improper connection element nesting", + __func__); + + cur_conn = calloc(1, sizeof(*cur_conn)); + if (cur_conn == NULL) + err(1, "%s: cannot allocate %zd bytes", __func__, + sizeof(*cur_conn)); + + islist->num_conns++; + islist->cur_conn = cur_conn; + + STAILQ_INSERT_TAIL(&islist->conn_list, cur_conn, links); + + for (i = 0; attr[i] != NULL; i += 2) { + if (strcmp(attr[i], "id") == 0) { + cur_conn->connection_id = + strtoull(attr[i+1], NULL, 0); + } else { + errx(1, + "%s: invalid connection attribute %s = %s", + __func__, attr[i], attr[i+1]); + } + } + } +} + +static void +cctl_islist_end_element(void *user_data, const char *name) +{ + struct cctl_islist_data *islist; + struct cctl_islist_conn *cur_conn; + char *str; + + islist = (struct cctl_islist_data *)user_data; + cur_conn = islist->cur_conn; + + if ((cur_conn == NULL) + && (strcmp(name, "ctlislist") != 0)) + errx(1, "%s: cur_conn == NULL! (name = %s)", __func__, name); + + if (islist->cur_sb[islist->level] == NULL) + errx(1, "%s: no valid sbuf at level %d (name %s)", __func__, + islist->level, name); + + sbuf_finish(islist->cur_sb[islist->level]); + str = strdup(sbuf_data(islist->cur_sb[islist->level])); + if (str == NULL) + err(1, "%s can't allocate %zd bytes for string", __func__, + sbuf_len(islist->cur_sb[islist->level])); + + sbuf_delete(islist->cur_sb[islist->level]); + islist->cur_sb[islist->level] = NULL; + islist->level--; + + if (strcmp(name, "initiator") == 0) { + cur_conn->initiator = str; + str = NULL; + } else if (strcmp(name, "initiator_addr") == 0) { + cur_conn->initiator_addr = str; + str = NULL; + } else if (strcmp(name, "initiator_alias") == 0) { + cur_conn->initiator_alias = str; + str = NULL; + } else if (strcmp(name, "target") == 0) { + cur_conn->target = str; + str = NULL; + } else if (strcmp(name, "target_alias") == 0) { + cur_conn->target_alias = str; + str = NULL; + } else if (strcmp(name, "header_digest") == 0) { + cur_conn->header_digest = str; + str = NULL; + } else if (strcmp(name, "data_digest") == 0) { + cur_conn->data_digest = str; + str = NULL; + } else if (strcmp(name, "max_data_segment_length") == 0) { + cur_conn->max_data_segment_length = str; + str = NULL; + } else if (strcmp(name, "immediate_data") == 0) { + cur_conn->immediate_data = atoi(str); + } else if (strcmp(name, "iser") == 0) { + cur_conn->iser = atoi(str); + } else if (strcmp(name, "connection") == 0) { + islist->cur_conn = NULL; + } else if (strcmp(name, "ctlislist") == 0) { + } else + errx(1, "unknown element %s", name); + + free(str); +} + +static void +cctl_islist_char_handler(void *user_data, const XML_Char *str, int len) +{ + struct cctl_islist_data *islist; + + islist = (struct cctl_islist_data *)user_data; + + sbuf_bcat(islist->cur_sb[islist->level], str, len); +} + +static int +cctl_islist(int fd, int argc, char **argv, char *combinedopt) +{ + struct ctl_iscsi req; + struct cctl_islist_data islist; + struct cctl_islist_conn *conn; + XML_Parser parser; + char *conn_str; + int conn_len; + int dump_xml = 0; + int c, retval, verbose = 0; + + retval = 0; + conn_len = 4096; + + bzero(&islist, sizeof(islist)); + STAILQ_INIT(&islist.conn_list); + + while ((c = getopt(argc, argv, combinedopt)) != -1) { + switch (c) { + case 'v': + verbose = 1; + break; + case 'x': + dump_xml = 1; + break; + default: + break; + } + } + +retry: + conn_str = malloc(conn_len); + + bzero(&req, sizeof(req)); + req.type = CTL_ISCSI_LIST; + req.data.list.alloc_len = conn_len; + req.data.list.conn_xml = conn_str; + + if (ioctl(fd, CTL_ISCSI, &req) == -1) { + warn("%s: error issuing CTL_ISCSI ioctl", __func__); + retval = 1; + goto bailout; + } + + if (req.status == CTL_ISCSI_ERROR) { + warnx("%s: error returned from CTL_ISCSI ioctl:\n%s", + __func__, req.error_str); + } else if (req.status == CTL_ISCSI_LIST_NEED_MORE_SPACE) { + conn_len = conn_len << 1; + goto retry; + } + + if (dump_xml != 0) { + printf("%s", conn_str); + goto bailout; + } + + parser = XML_ParserCreate(NULL); + if (parser == NULL) { + warn("%s: Unable to create XML parser", __func__); + retval = 1; + goto bailout; + } + + XML_SetUserData(parser, &islist); + XML_SetElementHandler(parser, cctl_islist_start_element, + cctl_islist_end_element); + XML_SetCharacterDataHandler(parser, cctl_islist_char_handler); + + retval = XML_Parse(parser, conn_str, strlen(conn_str), 1); + XML_ParserFree(parser); + if (retval != 1) { + retval = 1; + goto bailout; + } + + if (verbose != 0) { + STAILQ_FOREACH(conn, &islist.conn_list, links) { + printf("Session ID: %d\n", conn->connection_id); + printf("Initiator name: %s\n", conn->initiator); + printf("Initiator addr: %s\n", conn->initiator_addr); + printf("Initiator alias: %s\n", conn->initiator_alias); + printf("Target name: %s\n", conn->target); + printf("Target alias: %s\n", conn->target_alias); + printf("Header digest: %s\n", conn->header_digest); + printf("Data digest: %s\n", conn->data_digest); + printf("DataSegmentLen: %s\n", conn->max_data_segment_length); + printf("ImmediateData: %s\n", conn->immediate_data ? "Yes" : "No"); + printf("iSER (RDMA): %s\n", conn->iser ? "Yes" : "No"); + printf("\n"); + } + } else { + printf("%4s %-16s %-36s %-36s\n", "ID", "Address", "Initiator name", + "Target name"); + STAILQ_FOREACH(conn, &islist.conn_list, links) { + printf("%4u %-16s %-36s %-36s\n", + conn->connection_id, conn->initiator_addr, conn->initiator, + conn->target); + } + } +bailout: + free(conn_str); + + return (retval); +} + +static int +cctl_islogout(int fd, int argc, char **argv, char *combinedopt) +{ + struct ctl_iscsi req; + int retval = 0, c; + int all = 0, connection_id = -1, nargs = 0; + char *initiator_name = NULL, *initiator_addr = NULL; + + while ((c = getopt(argc, argv, combinedopt)) != -1) { + switch (c) { + case 'a': + all = 1; + nargs++; + break; + case 'h': + initiator_addr = strdup(optarg); + if (initiator_addr == NULL) + err(1, "%s: strdup", __func__); + nargs++; + break; + case 'c': + connection_id = strtoul(optarg, NULL, 0); + nargs++; + break; + case 'i': + initiator_name = strdup(optarg); + if (initiator_name == NULL) + err(1, "%s: strdup", __func__); + nargs++; + break; + default: + break; + } + } + + if (nargs == 0) + errx(1, "%s: either -a, -h, -c, or -i must be specified", + __func__); + if (nargs > 1) + errx(1, "%s: only one of -a, -h, -c, or -i may be specified", + __func__); + + bzero(&req, sizeof(req)); + req.type = CTL_ISCSI_LOGOUT; + req.data.logout.connection_id = connection_id; + if (initiator_addr != NULL) + strlcpy(req.data.logout.initiator_addr, + initiator_addr, sizeof(req.data.logout.initiator_addr)); + if (initiator_name != NULL) + strlcpy(req.data.logout.initiator_name, + initiator_name, sizeof(req.data.logout.initiator_name)); + if (all != 0) + req.data.logout.all = 1; + + if (ioctl(fd, CTL_ISCSI, &req) == -1) { + warn("%s: error issuing CTL_ISCSI ioctl", __func__); + retval = 1; + goto bailout; + } + + if (req.status != CTL_ISCSI_OK) { + warnx("%s: error returned from CTL iSCSI logout request:\n%s", + __func__, req.error_str); + retval = 1; + goto bailout; + } + + printf("iSCSI logout requests submitted\n"); + +bailout: + return (retval); +} + +static int +cctl_isterminate(int fd, int argc, char **argv, char *combinedopt) +{ + struct ctl_iscsi req; + int retval = 0, c; + int all = 0, connection_id = -1, nargs = 0; + char *initiator_name = NULL, *initiator_addr = NULL; + + while ((c = getopt(argc, argv, combinedopt)) != -1) { + switch (c) { + case 'a': + all = 1; + nargs++; + break; + case 'h': + initiator_addr = strdup(optarg); + if (initiator_addr == NULL) + err(1, "%s: strdup", __func__); + nargs++; + break; + case 'c': + connection_id = strtoul(optarg, NULL, 0); + nargs++; + break; + case 'i': + initiator_name = strdup(optarg); + if (initiator_name == NULL) + err(1, "%s: strdup", __func__); + nargs++; + break; + default: + break; + } + } + + if (nargs == 0) + errx(1, "%s: either -a, -h, -c, or -i must be specified", + __func__); + if (nargs > 1) + errx(1, "%s: only one of -a, -h, -c, or -i may be specified", + __func__); + + bzero(&req, sizeof(req)); + req.type = CTL_ISCSI_TERMINATE; + req.data.terminate.connection_id = connection_id; + if (initiator_addr != NULL) + strlcpy(req.data.terminate.initiator_addr, + initiator_addr, sizeof(req.data.terminate.initiator_addr)); + if (initiator_name != NULL) + strlcpy(req.data.terminate.initiator_name, + initiator_name, sizeof(req.data.terminate.initiator_name)); + if (all != 0) + req.data.terminate.all = 1; + + if (ioctl(fd, CTL_ISCSI, &req) == -1) { + warn("%s: error issuing CTL_ISCSI ioctl", __func__); + retval = 1; + goto bailout; + } + + if (req.status != CTL_ISCSI_OK) { + warnx("%s: error returned from CTL iSCSI connection " + "termination request:\n%s", __func__, req.error_str); + retval = 1; + goto bailout; + } + + printf("iSCSI connections terminated\n"); + +bailout: + return (retval); +} /* * Name/value pair used for per-LUN attributes. @@ -3713,6 +4120,9 @@ usage(int error) " [-s len fmt [args]] [-c] [-d delete_id]\n" " ctladm port <-l | -o | [-w wwnn][-W wwpn]>\n" " [-p targ_port] [-t port_type] [-q] [-x]\n" +" ctladm islist [-v | -x]\n" +" ctladm islogout <-A | -a addr | -c connection-id | -n name>\n" +" ctladm isterminate <-A | -a addr | -c connection-id | -n name>\n" " ctladm dumpooa\n" " ctladm dumpstructs\n" " ctladm help\n" @@ -4093,6 +4503,15 @@ main(int argc, char **argv) case CTLADM_CMD_MODIFY: retval = cctl_modify_lun(fd, argc, argv, combinedopt); break; + case CTLADM_CMD_ISLIST: + retval = cctl_islist(fd, argc, argv, combinedopt); + break; + case CTLADM_CMD_ISLOGOUT: + retval = cctl_islogout(fd, argc, argv, combinedopt); + break; + case CTLADM_CMD_ISTERMINATE: + retval = cctl_isterminate(fd, argc, argv, combinedopt); + break; case CTLADM_CMD_HELP: default: usage(retval); diff --git a/usr.sbin/ctld/Makefile b/usr.sbin/ctld/Makefile new file mode 100644 index 00000000000..e6c292d4256 --- /dev/null +++ b/usr.sbin/ctld/Makefile @@ -0,0 +1,21 @@ +# $FreeBSD$ + +PROG= ctld +SRCS= ctld.c discovery.c kernel.c keys.c log.c login.c parse.y pdu.c token.l y.tab.h +CFLAGS+= -I${.CURDIR} +CFLAGS+= -I${.CURDIR}/../../sys +CFLAGS+= -I${.CURDIR}/../../sys/cam/ctl +CFLAGS+= -I${.CURDIR}/../../sys/dev/iscsi +#CFLAGS+= -DICL_KERNEL_PROXY +MAN= ctld.8 ctl.conf.5 + +DPADD= ${LIBCAM} ${LIBSBUF} ${LIBBSDXML} ${LIBUTIL} +LDADD= -lbsdxml -lcam -lcrypto -lfl -lsbuf -lssl -lutil + +YFLAGS+= -v +CLEANFILES= y.tab.c y.tab.h y.output + +WARNS= 6 +NO_WMISSING_VARIABLE_DECLARATIONS= + +.include diff --git a/usr.sbin/ctld/ctl.conf.5 b/usr.sbin/ctld/ctl.conf.5 new file mode 100644 index 00000000000..fb8bc2a4f83 --- /dev/null +++ b/usr.sbin/ctld/ctl.conf.5 @@ -0,0 +1,244 @@ +.\" Copyright (c) 2012 The FreeBSD Foundation +.\" All rights reserved. +.\" +.\" This software was developed by Edward Tomasz Napierala under sponsorship +.\" from the FreeBSD Foundation. +.\" +.\" 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. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHORS 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 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. +.\" +.\" $FreeBSD$ +.\" +.Dd April 24, 2013 +.Dt CTL.CONF 5 +.Os +.Sh NAME +.Nm ctl.conf +.Nd CAM Target Layer / iSCSI target daemon configuration file +.Sh DESCRIPTION +The +.Nm +configuration file is used by the +.Xr ctld 8 +daemon. +Lines starting with +.Ql # +and empty lines are interpreted as comments. +The general syntax of the +.Nm +file is: +.Bd -literal -offset indent +pidfile + +auth-group { + chap + ... +} + +portal-group { + listen

+ listen-iser
+ discovery-auth-group + ... +} + +target { + auth-group + portal-group + lun { + path + } + ... +} +.Ed +.Ss global level +The following statements are available at the global level: +.Bl -tag -width indent +.It Ic auth-group Aq Ar name +Opens an auth-group section, defining an authentication group, +which can then be assigned to any number of targets. +.It Ic debug Aq Ar level +Specifies debug level. +The default is 0. +.It Ic maxproc Aq Ar number +Specifies limit for concurrently running child processes handling +incoming connections. +The default is 30. +Setting it to 0 disables the limit. +.It Ic pidfile Aq Ar path +Specifies path to pidfile. +The default is +.Pa /var/run/ctld.pid . +.It Ic portal-group Aq Ar name +Opens a portal-group section, defining a portal group, +which can then be assigned to any number of targets. +.It Ic target Aq Ar name +Opens a target configuration section. +.It Ic timeout Aq Ar seconds +Specifies timeout for login session, after which the connection +will be forcibly terminated. +The default is 60. +Setting it to 0 disables the timeout. +.El +.Ss auth-grup level +The following statements are available at the auth-group level: +.Bl -tag -width indent +.It Ic chap Ao Ar user Ac Aq Ar secret +Specifies CHAP authentication credentials. +.It Ic chap-mutual Ao Ar user Ac Ao Ar secret Ac Ao Ar mutualuser Ac Aq Ar mutualsecret +Specifies mutual CHAP authentication credentials. +Note that for any auth-group, configuration may contain either chap, +or chap-mutual entries; it's an error to mix them. +.El +.Ss portal-group level +The following statements are available at the portal-group level: +.Bl -tag -width indent +.It Ic discovery-auth-group Aq Ar name +Assigns previously defined authentication group to that portal group, +to be used for target discovery. +By default, the discovery will be denied. +A special auth-group, "no-authentication", may be used to allow for discovery +without authentication. +.It Ic listen Aq Ar address +Specifies IPv4 or IPv6 address and port to listen on for incoming connections. +.It Ic listen-iser Aq Ar address +Specifies IPv4 or IPv6 address and port to listen on for incoming connections +using iSER (iSCSI over RDMA) protocol. +.El +.Ss target level: +The following statements are available at the target level: +.Bl -tag -width indent +.It Ic alias Aq Ar text +Assigns human-readable description to that target. +There is no default. +.It Ic auth-group Aq Ar name +Assigns previously defined authentication group to that target. +There is no default; every target must use either auth-group, +or chap, or chap-mutual statements. +A special auth-group, "no-authentication", may be used to permit access +without authentication. +.It Ic chap Ao Ar user Ac Aq Ar secret +Specifies CHAP authentication credentials. +Note that targets must use either auth-group, or chap, +or chap-mutual clauses; it's a configuration error to mix them in one target. +.It Ic chap-mutual Ao Ar user Ac Ao Ar secret Ac Ao Ar mutualuser Ac Aq Ar mutualsecret +Specifies mutual CHAP authentication credentials. +Note that targets must use either auth-group, chap, +chap-mutual clauses; it's a configuration error to mix them in one target. +.It Ic portal-group Aq Ar name +Assigns previously defined portal group to that target. +Default portal group is "default", which makes the target available +on TCP port 3260 on all configured IPv4 and IPv6 addresses. +.It Ic lun Aq Ar number +Opens a lun configuration section, defining LUN exported by a target. +.El +.Ss lun level +The following statements are available at the lun level: +.Bl -tag -width indent +.It Ic backend Ao Ar block | Ar ramdisk Ac +Specifies the CTL backend to use for a given LUN. +Valid choices are +.Dq block +and +.Dq ramdisk ; +block is used for LUNs backed +by files in the filesystem; ramdisk is a bitsink device, used mostly for +testing. +The default backend is block. +.It Ic blocksize Aq Ar size +Specifies blocksize visible to the initiator. +The default blocksize is 512. +.It Ic device-id Aq Ar string +Specifies SCSI Device Identification string presented to the initiator. +.It Ic option Ao Ar name Ac Aq Ar value +Specifies CTL-specific options passed to the kernel. +.It Ic path Aq Ar path +Specifies path to file used to back the LUN. +.It Ic serial Aq Ar string +Specifies SCSI serial number presented to the initiator. +.It Ic size Aq Ar size +Specifies LUN size, in bytes. +.El +.Sh FILES +.Bl -tag -width ".Pa /etc/ctl.conf" -compact +.It Pa /etc/ctl.conf +The default location of the +.Xr ctld 8 +configuration file. +.El +.Sh EXAMPLES +.Bd -literal +pidfile /var/run/ctld.pid + +auth-group example2 { + chap-mutual "user" "secret" "mutualuser" "mutualsecret" + chap-mutual "user2" "secret2" "mutualuser" "mutualsecret" +} + +portal-group example2 { + discovery-auth-group no-authentication + listen 127.0.0.1 + listen 0.0.0.0:3261 + listen [::]:3261 + listen [fe80::be:ef] +} + +target iqn.2012-06.com.example:target0 { + alias "Testing target" + auth-group no-authentication + lun 0 { + path /dev/zvol/example_0 + blocksize 4096 + size 4G + } +} + +target iqn.2012-06.com.example:target3 { + chap chapuser chapsecret + lun 0 { + path /dev/zvol/example_3 + } +} + +target iqn.2012-06.com.example:target2 { + auth-group example2 + portal-group example2 + lun 0 { + path /dev/zvol/example2_0 + } + lun 1 { + path /dev/zvol/example2_1 + option foo bar + } +} +.Ed +.Sh SEE ALSO +.Xr ctl 4 , +.Xr ctladm 8 , +.Xr ctld 8 +.Sh AUTHORS +The +.Nm +configuration file functionality for +.Xr ctld 8 +was developed by +.An Edward Tomasz Napierala Aq trasz@FreeBSD.org +under sponsorship from the FreeBSD Foundation. diff --git a/usr.sbin/ctld/ctld.8 b/usr.sbin/ctld/ctld.8 new file mode 100644 index 00000000000..60ffbfcf478 --- /dev/null +++ b/usr.sbin/ctld/ctld.8 @@ -0,0 +1,113 @@ +.\" Copyright (c) 2012 The FreeBSD Foundation +.\" All rights reserved. +.\" +.\" This software was developed by Edward Tomasz Napierala under sponsorship +.\" from the FreeBSD Foundation. +.\" +.\" 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. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHORS 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 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. +.\" +.\" $FreeBSD$ +.\" +.Dd September 20, 2012 +.Dt CTLD 8 +.Os +.Sh NAME +.Nm ctld +.Nd CAM Target Layer / iSCSI target daemon +.Sh SYNOPSIS +.Nm +.Op Fl d +.Op Fl f Ar config-file +.Sh DESCRIPTION +The +.Nm +daemon is responsible for managing the CAM Target Layer configuration, +accepting incoming iSCSI connections, performing authentication and +passing connections to the kernel part of the native iSCSI target. +.Pp +Upon startup, the +.Nm +daemon parses the configuration file and exits, if it encounters any errors. +Then it compares the configuration with the kernel list of LUNs managed +by previously running +.Nm +instances, removes LUNs no longer existing in the configuration file, +and creates new LUNs as neccessary. +After that it listens for the incoming iSCSI connections, performs +authentication, and, if successful, passes the connections to the kernel part +of CTL iSCSI target, which handles it from that point. +.Pp +When it receives a SIGHUP signal, the +.Nm +reloads its configuration and applies the changes to the kernel. +Changes are applied in a way that avoids unneccessary disruptions; +for example removing one LUN does not affect other LUNs. +.Pp +When exiting gracefully, the +.Nm +daemon removes LUNs it managed and forcibly disconnects all the clients. +Otherwise - e.g. when killed with SIGKILL - LUNs stay configured +and clients remain connected. +.Pp +To perform administrative actions that apply to already connected +sessions, such as forcing termination, use +.Xr ctladm 8 . +.Pp +The following options are available: +.Bl -tag -width ".Fl P Ar pidfile" +.It Fl f Ar config-file +Specifies the name of the configuration file. +The default is +.Pa /etc/ctl.conf . +.It Fl d +Debug mode. +The server sends verbose debug output to standard error, and does not +put itself in the background. +The server will also not fork and will exit after processing one connection. +This option is only intended for debugging the target. +.El +.Sh FILES +.Bl -tag -width ".Pa /var/run/ctld.pid" -compact +.It Pa /etc/ctl.conf +The configuration file for +.Nm . +The file format and configuration options are described in +.Xr ctl.conf 5 . +.It Pa /var/run/ctld.pid +The default location of the +.Nm +PID file. +.El +.Sh EXIT STATUS +The +.Nm +utility exits 0 on success, and >0 if an error occurs. +.Sh SEE ALSO +.Xr ctl 4 , +.Xr ctl.conf 5 , +.Xr ctladm 8 +.Sh AUTHORS +The +.Nm +was developed by +.An Edward Tomasz Napierala Aq trasz@FreeBSD.org +under sponsorship from the FreeBSD Foundation. diff --git a/usr.sbin/ctld/ctld.c b/usr.sbin/ctld/ctld.c new file mode 100644 index 00000000000..a5472abbfbf --- /dev/null +++ b/usr.sbin/ctld/ctld.c @@ -0,0 +1,1713 @@ +/*- + * Copyright (c) 2012 The FreeBSD Foundation + * All rights reserved. + * + * This software was developed by Edward Tomasz Napierala under sponsorship + * from the FreeBSD Foundation. + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + * $FreeBSD$ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ctld.h" + +static volatile bool sighup_received = false; +static volatile bool sigterm_received = false; +static volatile bool sigalrm_received = false; + +static int nchildren = 0; + +static void +usage(void) +{ + + fprintf(stderr, "usage: ctld [-d][-f config-file]\n"); + exit(1); +} + +char * +checked_strdup(const char *s) +{ + char *c; + + c = strdup(s); + if (c == NULL) + log_err(1, "strdup"); + return (c); +} + +struct conf * +conf_new(void) +{ + struct conf *conf; + + conf = calloc(1, sizeof(*conf)); + if (conf == NULL) + log_err(1, "calloc"); + TAILQ_INIT(&conf->conf_targets); + TAILQ_INIT(&conf->conf_auth_groups); + TAILQ_INIT(&conf->conf_portal_groups); + + conf->conf_debug = 0; + conf->conf_timeout = 60; + conf->conf_maxproc = 30; + + return (conf); +} + +void +conf_delete(struct conf *conf) +{ + struct target *targ, *tmp; + struct auth_group *ag, *cagtmp; + struct portal_group *pg, *cpgtmp; + + assert(conf->conf_pidfh == NULL); + + TAILQ_FOREACH_SAFE(targ, &conf->conf_targets, t_next, tmp) + target_delete(targ); + TAILQ_FOREACH_SAFE(ag, &conf->conf_auth_groups, ag_next, cagtmp) + auth_group_delete(ag); + TAILQ_FOREACH_SAFE(pg, &conf->conf_portal_groups, pg_next, cpgtmp) + portal_group_delete(pg); + free(conf->conf_pidfile_path); + free(conf); +} + +static struct auth * +auth_new(struct auth_group *ag) +{ + struct auth *auth; + + auth = calloc(1, sizeof(*auth)); + if (auth == NULL) + log_err(1, "calloc"); + auth->a_auth_group = ag; + TAILQ_INSERT_TAIL(&ag->ag_auths, auth, a_next); + return (auth); +} + +static void +auth_delete(struct auth *auth) +{ + TAILQ_REMOVE(&auth->a_auth_group->ag_auths, auth, a_next); + + free(auth->a_user); + free(auth->a_secret); + free(auth->a_mutual_user); + free(auth->a_mutual_secret); + free(auth); +} + +const struct auth * +auth_find(struct auth_group *ag, const char *user) +{ + const struct auth *auth; + + TAILQ_FOREACH(auth, &ag->ag_auths, a_next) { + if (strcmp(auth->a_user, user) == 0) + return (auth); + } + + return (NULL); +} + +struct auth_group * +auth_group_new(struct conf *conf, const char *name) +{ + struct auth_group *ag; + + if (name != NULL) { + ag = auth_group_find(conf, name); + if (ag != NULL) { + log_warnx("duplicated auth-group \"%s\"", name); + return (NULL); + } + } + + ag = calloc(1, sizeof(*ag)); + if (ag == NULL) + log_err(1, "calloc"); + if (name != NULL) + ag->ag_name = checked_strdup(name); + TAILQ_INIT(&ag->ag_auths); + ag->ag_conf = conf; + TAILQ_INSERT_TAIL(&conf->conf_auth_groups, ag, ag_next); + + return (ag); +} + +void +auth_group_delete(struct auth_group *ag) +{ + struct auth *auth, *tmp; + + TAILQ_REMOVE(&ag->ag_conf->conf_auth_groups, ag, ag_next); + + TAILQ_FOREACH_SAFE(auth, &ag->ag_auths, a_next, tmp) + auth_delete(auth); + free(ag->ag_name); + free(ag); +} + +struct auth_group * +auth_group_find(struct conf *conf, const char *name) +{ + struct auth_group *ag; + + TAILQ_FOREACH(ag, &conf->conf_auth_groups, ag_next) { + if (ag->ag_name != NULL && strcmp(ag->ag_name, name) == 0) + return (ag); + } + + return (NULL); +} + +static void +auth_check_secret_length(struct auth *auth) +{ + size_t len; + + len = strlen(auth->a_secret); + if (len > 16) { + if (auth->a_auth_group->ag_name != NULL) + log_warnx("secret for user \"%s\", auth-group \"%s\", " + "is too long; it should be at most 16 characters " + "long", auth->a_user, auth->a_auth_group->ag_name); + else + log_warnx("secret for user \"%s\", target \"%s\", " + "is too long; it should be at most 16 characters " + "long", auth->a_user, + auth->a_auth_group->ag_target->t_iqn); + } + if (len < 12) { + if (auth->a_auth_group->ag_name != NULL) + log_warnx("secret for user \"%s\", auth-group \"%s\", " + "is too short; it should be at least 12 characters " + "long", auth->a_user, + auth->a_auth_group->ag_name); + else + log_warnx("secret for user \"%s\", target \"%s\", " + "is too short; it should be at least 16 characters " + "long", auth->a_user, + auth->a_auth_group->ag_target->t_iqn); + } + + if (auth->a_mutual_secret != NULL) { + len = strlen(auth->a_secret); + if (len > 16) { + if (auth->a_auth_group->ag_name != NULL) + log_warnx("mutual secret for user \"%s\", " + "auth-group \"%s\", is too long; it should " + "be at most 16 characters long", + auth->a_user, auth->a_auth_group->ag_name); + else + log_warnx("mutual secret for user \"%s\", " + "target \"%s\", is too long; it should " + "be at most 16 characters long", + auth->a_user, + auth->a_auth_group->ag_target->t_iqn); + } + if (len < 12) { + if (auth->a_auth_group->ag_name != NULL) + log_warnx("mutual secret for user \"%s\", " + "auth-group \"%s\", is too short; it " + "should be at least 12 characters long", + auth->a_user, auth->a_auth_group->ag_name); + else + log_warnx("mutual secret for user \"%s\", " + "target \"%s\", is too short; it should be " + "at least 16 characters long", + auth->a_user, + auth->a_auth_group->ag_target->t_iqn); + } + } +} + +const struct auth * +auth_new_chap(struct auth_group *ag, const char *user, + const char *secret) +{ + struct auth *auth; + + if (ag->ag_type == AG_TYPE_UNKNOWN) + ag->ag_type = AG_TYPE_CHAP; + if (ag->ag_type != AG_TYPE_CHAP) { + if (ag->ag_name != NULL) + log_warnx("cannot mix \"chap\" authentication with " + "other types for auth-group \"%s\"", ag->ag_name); + else + log_warnx("cannot mix \"chap\" authentication with " + "other types for target \"%s\"", + ag->ag_target->t_iqn); + return (NULL); + } + + auth = auth_new(ag); + auth->a_user = checked_strdup(user); + auth->a_secret = checked_strdup(secret); + + auth_check_secret_length(auth); + + return (auth); +} + +const struct auth * +auth_new_chap_mutual(struct auth_group *ag, const char *user, + const char *secret, const char *user2, const char *secret2) +{ + struct auth *auth; + + if (ag->ag_type == AG_TYPE_UNKNOWN) + ag->ag_type = AG_TYPE_CHAP_MUTUAL; + if (ag->ag_type != AG_TYPE_CHAP_MUTUAL) { + if (ag->ag_name != NULL) + log_warnx("cannot mix \"chap-mutual\" authentication " + "with other types for auth-group \"%s\"", + ag->ag_name); + else + log_warnx("cannot mix \"chap-mutual\" authentication " + "with other types for target \"%s\"", + ag->ag_target->t_iqn); + return (NULL); + } + + auth = auth_new(ag); + auth->a_user = checked_strdup(user); + auth->a_secret = checked_strdup(secret); + auth->a_mutual_user = checked_strdup(user2); + auth->a_mutual_secret = checked_strdup(secret2); + + auth_check_secret_length(auth); + + return (auth); +} + +static struct portal * +portal_new(struct portal_group *pg) +{ + struct portal *portal; + + portal = calloc(1, sizeof(*portal)); + if (portal == NULL) + log_err(1, "calloc"); + TAILQ_INIT(&portal->p_targets); + portal->p_portal_group = pg; + TAILQ_INSERT_TAIL(&pg->pg_portals, portal, p_next); + return (portal); +} + +static void +portal_delete(struct portal *portal) +{ + TAILQ_REMOVE(&portal->p_portal_group->pg_portals, portal, p_next); + freeaddrinfo(portal->p_ai); + free(portal->p_listen); + free(portal); +} + +struct portal_group * +portal_group_new(struct conf *conf, const char *name) +{ + struct portal_group *pg; + + pg = portal_group_find(conf, name); + if (pg != NULL) { + log_warnx("duplicated portal-group \"%s\"", name); + return (NULL); + } + + pg = calloc(1, sizeof(*pg)); + if (pg == NULL) + log_err(1, "calloc"); + pg->pg_name = checked_strdup(name); + TAILQ_INIT(&pg->pg_portals); + pg->pg_conf = conf; + conf->conf_last_portal_group_tag++; + pg->pg_tag = conf->conf_last_portal_group_tag; + TAILQ_INSERT_TAIL(&conf->conf_portal_groups, pg, pg_next); + + return (pg); +} + +void +portal_group_delete(struct portal_group *pg) +{ + struct portal *portal, *tmp; + + TAILQ_REMOVE(&pg->pg_conf->conf_portal_groups, pg, pg_next); + + TAILQ_FOREACH_SAFE(portal, &pg->pg_portals, p_next, tmp) + portal_delete(portal); + free(pg->pg_name); + free(pg); +} + +struct portal_group * +portal_group_find(struct conf *conf, const char *name) +{ + struct portal_group *pg; + + TAILQ_FOREACH(pg, &conf->conf_portal_groups, pg_next) { + if (strcmp(pg->pg_name, name) == 0) + return (pg); + } + + return (NULL); +} + +int +portal_group_add_listen(struct portal_group *pg, const char *value, bool iser) +{ + struct addrinfo hints; + struct portal *portal; + char *addr, *ch, *arg; + const char *port; + int error, colons = 0; + +#ifndef ICL_KERNEL_PROXY + if (iser) { + log_warnx("ctld(8) compiled without ICL_KERNEL_PROXY " + "does not support iSER protocol"); + return (-1); + } +#endif + + portal = portal_new(pg); + portal->p_listen = checked_strdup(value); + portal->p_iser = iser; + + arg = portal->p_listen; + if (arg[0] == '\0') { + log_warnx("empty listen address"); + free(portal->p_listen); + free(portal); + return (1); + } + if (arg[0] == '[') { + /* + * IPv6 address in square brackets, perhaps with port. + */ + arg++; + addr = strsep(&arg, "]"); + if (arg == NULL) { + log_warnx("invalid listen address %s", + portal->p_listen); + free(portal->p_listen); + free(portal); + return (1); + } + if (arg[0] == '\0') { + port = "3260"; + } else if (arg[0] == ':') { + port = arg + 1; + } else { + log_warnx("invalid listen address %s", + portal->p_listen); + free(portal->p_listen); + free(portal); + return (1); + } + } else { + /* + * Either IPv6 address without brackets - and without + * a port - or IPv4 address. Just count the colons. + */ + for (ch = arg; *ch != '\0'; ch++) { + if (*ch == ':') + colons++; + } + if (colons > 1) { + addr = arg; + port = "3260"; + } else { + addr = strsep(&arg, ":"); + if (arg == NULL) + port = "3260"; + else + port = arg; + } + } + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = PF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_PASSIVE; + + error = getaddrinfo(addr, port, &hints, &portal->p_ai); + if (error != 0) { + log_warnx("getaddrinfo for %s failed: %s", + portal->p_listen, gai_strerror(error)); + free(portal->p_listen); + free(portal); + return (1); + } + + /* + * XXX: getaddrinfo(3) may return multiple addresses; we should turn + * those into multiple portals. + */ + + return (0); +} + +static bool +valid_hex(const char ch) +{ + switch (ch) { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case 'a': + case 'A': + case 'b': + case 'B': + case 'c': + case 'C': + case 'd': + case 'D': + case 'e': + case 'E': + case 'f': + case 'F': + return (true); + default: + return (false); + } +} + +bool +valid_iscsi_name(const char *name) +{ + int i; + + if (strlen(name) >= MAX_NAME_LEN) { + log_warnx("overlong name for target \"%s\"; max length allowed " + "by iSCSI specification is %d characters", + name, MAX_NAME_LEN); + return (false); + } + + /* + * In the cases below, we don't return an error, just in case the admin + * was right, and we're wrong. + */ + if (strncasecmp(name, "iqn.", strlen("iqn.")) == 0) { + for (i = strlen("iqn."); name[i] != '\0'; i++) { + /* + * XXX: We should verify UTF-8 normalisation, as defined + * by 3.2.6.2: iSCSI Name Encoding. + */ + if (isalnum(name[i])) + continue; + if (name[i] == '-' || name[i] == '.' || name[i] == ':') + continue; + log_warnx("invalid character \"%c\" in target name " + "\"%s\"; allowed characters are letters, digits, " + "'-', '.', and ':'", name[i], name); + break; + } + /* + * XXX: Check more stuff: valid date and a valid reversed domain. + */ + } else if (strncasecmp(name, "eui.", strlen("eui.")) == 0) { + if (strlen(name) != strlen("eui.") + 16) + log_warnx("invalid target name \"%s\"; the \"eui.\" " + "should be followed by exactly 16 hexadecimal " + "digits", name); + for (i = strlen("eui."); name[i] != '\0'; i++) { + if (!valid_hex(name[i])) { + log_warnx("invalid character \"%c\" in target " + "name \"%s\"; allowed characters are 1-9 " + "and A-F", name[i], name); + break; + } + } + } else if (strncasecmp(name, "naa.", strlen("naa.")) == 0) { + if (strlen(name) > strlen("naa.") + 32) + log_warnx("invalid target name \"%s\"; the \"naa.\" " + "should be followed by at most 32 hexadecimal " + "digits", name); + for (i = strlen("naa."); name[i] != '\0'; i++) { + if (!valid_hex(name[i])) { + log_warnx("invalid character \"%c\" in target " + "name \"%s\"; allowed characters are 1-9 " + "and A-F", name[i], name); + break; + } + } + } else { + log_warnx("invalid target name \"%s\"; should start with " + "either \".iqn\", \"eui.\", or \"naa.\"", + name); + } + return (true); +} + +struct target * +target_new(struct conf *conf, const char *iqn) +{ + struct target *targ; + int i, len; + + targ = target_find(conf, iqn); + if (targ != NULL) { + log_warnx("duplicated target \"%s\"", iqn); + return (NULL); + } + if (valid_iscsi_name(iqn) == false) { + log_warnx("target name \"%s\" is invalid", iqn); + return (NULL); + } + targ = calloc(1, sizeof(*targ)); + if (targ == NULL) + log_err(1, "calloc"); + targ->t_iqn = checked_strdup(iqn); + + /* + * RFC 3722 requires us to normalize the name to lowercase. + */ + len = strlen(iqn); + for (i = 0; i < len; i++) + targ->t_iqn[i] = tolower(targ->t_iqn[i]); + + TAILQ_INIT(&targ->t_luns); + targ->t_conf = conf; + TAILQ_INSERT_TAIL(&conf->conf_targets, targ, t_next); + + return (targ); +} + +void +target_delete(struct target *targ) +{ + struct lun *lun, *tmp; + + TAILQ_REMOVE(&targ->t_conf->conf_targets, targ, t_next); + + TAILQ_FOREACH_SAFE(lun, &targ->t_luns, l_next, tmp) + lun_delete(lun); + free(targ->t_iqn); + free(targ); +} + +struct target * +target_find(struct conf *conf, const char *iqn) +{ + struct target *targ; + + TAILQ_FOREACH(targ, &conf->conf_targets, t_next) { + if (strcasecmp(targ->t_iqn, iqn) == 0) + return (targ); + } + + return (NULL); +} + +struct lun * +lun_new(struct target *targ, int lun_id) +{ + struct lun *lun; + + lun = lun_find(targ, lun_id); + if (lun != NULL) { + log_warnx("duplicated lun %d for target \"%s\"", + lun_id, targ->t_iqn); + return (NULL); + } + + lun = calloc(1, sizeof(*lun)); + if (lun == NULL) + log_err(1, "calloc"); + lun->l_lun = lun_id; + TAILQ_INIT(&lun->l_options); + lun->l_target = targ; + TAILQ_INSERT_TAIL(&targ->t_luns, lun, l_next); + + return (lun); +} + +void +lun_delete(struct lun *lun) +{ + struct lun_option *lo, *tmp; + + TAILQ_REMOVE(&lun->l_target->t_luns, lun, l_next); + + TAILQ_FOREACH_SAFE(lo, &lun->l_options, lo_next, tmp) + lun_option_delete(lo); + free(lun->l_backend); + free(lun->l_device_id); + free(lun->l_path); + free(lun->l_serial); + free(lun); +} + +struct lun * +lun_find(struct target *targ, int lun_id) +{ + struct lun *lun; + + TAILQ_FOREACH(lun, &targ->t_luns, l_next) { + if (lun->l_lun == lun_id) + return (lun); + } + + return (NULL); +} + +void +lun_set_backend(struct lun *lun, const char *value) +{ + free(lun->l_backend); + lun->l_backend = checked_strdup(value); +} + +void +lun_set_blocksize(struct lun *lun, size_t value) +{ + + lun->l_blocksize = value; +} + +void +lun_set_device_id(struct lun *lun, const char *value) +{ + free(lun->l_device_id); + lun->l_device_id = checked_strdup(value); +} + +void +lun_set_path(struct lun *lun, const char *value) +{ + free(lun->l_path); + lun->l_path = checked_strdup(value); +} + +void +lun_set_serial(struct lun *lun, const char *value) +{ + free(lun->l_serial); + lun->l_serial = checked_strdup(value); +} + +void +lun_set_size(struct lun *lun, size_t value) +{ + + lun->l_size = value; +} + +void +lun_set_ctl_lun(struct lun *lun, uint32_t value) +{ + + lun->l_ctl_lun = value; +} + +struct lun_option * +lun_option_new(struct lun *lun, const char *name, const char *value) +{ + struct lun_option *lo; + + lo = lun_option_find(lun, name); + if (lo != NULL) { + log_warnx("duplicated lun option %s for lun %d, target \"%s\"", + name, lun->l_lun, lun->l_target->t_iqn); + return (NULL); + } + + lo = calloc(1, sizeof(*lo)); + if (lo == NULL) + log_err(1, "calloc"); + lo->lo_name = checked_strdup(name); + lo->lo_value = checked_strdup(value); + lo->lo_lun = lun; + TAILQ_INSERT_TAIL(&lun->l_options, lo, lo_next); + + return (lo); +} + +void +lun_option_delete(struct lun_option *lo) +{ + + TAILQ_REMOVE(&lo->lo_lun->l_options, lo, lo_next); + + free(lo->lo_name); + free(lo->lo_value); + free(lo); +} + +struct lun_option * +lun_option_find(struct lun *lun, const char *name) +{ + struct lun_option *lo; + + TAILQ_FOREACH(lo, &lun->l_options, lo_next) { + if (strcmp(lo->lo_name, name) == 0) + return (lo); + } + + return (NULL); +} + +void +lun_option_set(struct lun_option *lo, const char *value) +{ + + free(lo->lo_value); + lo->lo_value = checked_strdup(value); +} + +static struct connection * +connection_new(struct portal *portal, int fd, const char *host) +{ + struct connection *conn; + + conn = calloc(1, sizeof(*conn)); + if (conn == NULL) + log_err(1, "calloc"); + conn->conn_portal = portal; + conn->conn_socket = fd; + conn->conn_initiator_addr = checked_strdup(host); + + /* + * Default values, from RFC 3720, section 12. + */ + conn->conn_max_data_segment_length = 8192; + conn->conn_max_burst_length = 262144; + conn->conn_immediate_data = true; + + return (conn); +} + +#if 0 +static void +conf_print(struct conf *conf) +{ + struct auth_group *ag; + struct auth *auth; + struct portal_group *pg; + struct portal *portal; + struct target *targ; + struct lun *lun; + struct lun_option *lo; + + TAILQ_FOREACH(ag, &conf->conf_auth_groups, ag_next) { + fprintf(stderr, "auth-group %s {\n", ag->ag_name); + TAILQ_FOREACH(auth, &ag->ag_auths, a_next) + fprintf(stderr, "\t chap-mutual %s %s %s %s\n", + auth->a_user, auth->a_secret, + auth->a_mutual_user, auth->a_mutual_secret); + fprintf(stderr, "}\n"); + } + TAILQ_FOREACH(pg, &conf->conf_portal_groups, pg_next) { + fprintf(stderr, "portal-group %s {\n", pg->pg_name); + TAILQ_FOREACH(portal, &pg->pg_portals, p_next) + fprintf(stderr, "\t listen %s\n", portal->p_listen); + fprintf(stderr, "}\n"); + } + TAILQ_FOREACH(targ, &conf->conf_targets, t_next) { + fprintf(stderr, "target %s {\n", targ->t_iqn); + if (targ->t_alias != NULL) + fprintf(stderr, "\t alias %s\n", targ->t_alias); + TAILQ_FOREACH(lun, &targ->t_luns, l_next) { + fprintf(stderr, "\tlun %d {\n", lun->l_lun); + fprintf(stderr, "\t\tpath %s\n", lun->l_path); + TAILQ_FOREACH(lo, &lun->l_options, lo_next) + fprintf(stderr, "\t\toption %s %s\n", + lo->lo_name, lo->lo_value); + fprintf(stderr, "\t}\n"); + } + fprintf(stderr, "}\n"); + } +} +#endif + +int +conf_verify(struct conf *conf) +{ + struct auth_group *ag; + struct portal_group *pg; + struct target *targ; + struct lun *lun, *lun2; + bool found_lun0; + + if (conf->conf_pidfile_path == NULL) + conf->conf_pidfile_path = checked_strdup(DEFAULT_PIDFILE); + + TAILQ_FOREACH(targ, &conf->conf_targets, t_next) { + if (targ->t_auth_group == NULL) { + log_warnx("missing authentication for target \"%s\"; " + "must specify either \"auth-group\", \"chap\", " + "or \"chap-mutual\"", targ->t_iqn); + return (1); + } + if (targ->t_portal_group == NULL) { + targ->t_portal_group = portal_group_find(conf, + "default"); + assert(targ->t_portal_group != NULL); + } + found_lun0 = false; + TAILQ_FOREACH(lun, &targ->t_luns, l_next) { + if (lun->l_lun == 0) + found_lun0 = true; + if (lun->l_backend == NULL) + lun_set_backend(lun, "block"); + if (strcmp(lun->l_backend, "block") == 0 && + lun->l_path == NULL) { + log_warnx("missing path for lun %d, " + "target \"%s\"", lun->l_lun, targ->t_iqn); + return (1); + } + if (strcmp(lun->l_backend, "ramdisk") == 0) { + if (lun->l_size == 0) { + log_warnx("missing size for " + "ramdisk-backed lun %d, " + "target \"%s\"", + lun->l_lun, targ->t_iqn); + return (1); + } + if (lun->l_path != NULL) { + log_warnx("path must not be specified " + "for ramdisk-backed lun %d, " + "target \"%s\"", + lun->l_lun, targ->t_iqn); + return (1); + } + } + if (lun->l_lun < 0 || lun->l_lun > 255) { + log_warnx("invalid lun number for lun %d, " + "target \"%s\"; must be between 0 and 255", + lun->l_lun, targ->t_iqn); + return (1); + } +#if 1 /* Should we? */ + TAILQ_FOREACH(lun2, &targ->t_luns, l_next) { + if (lun == lun2) + continue; + if (lun->l_path != NULL && + lun2->l_path != NULL && + strcmp(lun->l_path, lun2->l_path) == 0) + log_debugx("WARNING: duplicate path " + "for lun %d, target \"%s\"", + lun->l_lun, targ->t_iqn); + } +#endif + if (lun->l_blocksize == 0) { + lun_set_blocksize(lun, DEFAULT_BLOCKSIZE); + } else if (lun->l_blocksize <= 0) { + log_warnx("invalid blocksize for lun %d, " + "target \"%s\"; must be larger than 0", + lun->l_lun, targ->t_iqn); + return (1); + } + if (lun->l_size != 0 && + lun->l_size % lun->l_blocksize != 0) { + log_warnx("invalid size for lun %d, target " + "\"%s\"; must be multiple of blocksize", + lun->l_lun, targ->t_iqn); + return (1); + } + } + if (!found_lun0) { + log_warnx("mandatory LUN 0 not configured " + "for target \"%s\"", targ->t_iqn); + return (1); + } + } + TAILQ_FOREACH(pg, &conf->conf_portal_groups, pg_next) { + assert(pg->pg_name != NULL); + if (pg->pg_discovery_auth_group == NULL) { + pg->pg_discovery_auth_group = + auth_group_find(conf, "no-access"); + assert(pg->pg_discovery_auth_group != NULL); + } + + TAILQ_FOREACH(targ, &conf->conf_targets, t_next) { + if (targ->t_portal_group == pg) + break; + } + if (targ == NULL) { + if (strcmp(pg->pg_name, "default") != 0) + log_warnx("portal-group \"%s\" not assigned " + "to any target", pg->pg_name); + pg->pg_unassigned = true; + } else + pg->pg_unassigned = false; + } + TAILQ_FOREACH(ag, &conf->conf_auth_groups, ag_next) { + if (ag->ag_name == NULL) + assert(ag->ag_target != NULL); + else + assert(ag->ag_target == NULL); + + TAILQ_FOREACH(targ, &conf->conf_targets, t_next) { + if (targ->t_auth_group == ag) + break; + } + if (targ == NULL && ag->ag_name != NULL && + strcmp(ag->ag_name, "no-authentication") != 0 && + strcmp(ag->ag_name, "no-access") != 0) { + log_warnx("auth-group \"%s\" not assigned " + "to any target", ag->ag_name); + } + } + + return (0); +} + +static int +conf_apply(struct conf *oldconf, struct conf *newconf) +{ + struct target *oldtarg, *newtarg, *tmptarg; + struct lun *oldlun, *newlun, *tmplun; + struct portal_group *oldpg, *newpg; + struct portal *oldp, *newp; + pid_t otherpid; + int changed, cumulated_error = 0, error; +#ifndef ICL_KERNEL_PROXY + int one = 1; +#endif + + if (oldconf->conf_debug != newconf->conf_debug) { + log_debugx("changing debug level to %d", newconf->conf_debug); + log_init(newconf->conf_debug); + } + + if (oldconf->conf_pidfh != NULL) { + assert(oldconf->conf_pidfile_path != NULL); + if (newconf->conf_pidfile_path != NULL && + strcmp(oldconf->conf_pidfile_path, + newconf->conf_pidfile_path) == 0) { + newconf->conf_pidfh = oldconf->conf_pidfh; + oldconf->conf_pidfh = NULL; + } else { + log_debugx("removing pidfile %s", + oldconf->conf_pidfile_path); + pidfile_remove(oldconf->conf_pidfh); + oldconf->conf_pidfh = NULL; + } + } + + if (newconf->conf_pidfh == NULL && newconf->conf_pidfile_path != NULL) { + log_debugx("opening pidfile %s", newconf->conf_pidfile_path); + newconf->conf_pidfh = + pidfile_open(newconf->conf_pidfile_path, 0600, &otherpid); + if (newconf->conf_pidfh == NULL) { + if (errno == EEXIST) + log_errx(1, "daemon already running, pid: %jd.", + (intmax_t)otherpid); + log_err(1, "cannot open or create pidfile \"%s\"", + newconf->conf_pidfile_path); + } + } + + TAILQ_FOREACH_SAFE(oldtarg, &oldconf->conf_targets, t_next, tmptarg) { + /* + * First, remove any targets present in the old configuration + * and missing in the new one. + */ + newtarg = target_find(newconf, oldtarg->t_iqn); + if (newtarg == NULL) { + TAILQ_FOREACH_SAFE(oldlun, &oldtarg->t_luns, l_next, + tmplun) { + log_debugx("target %s not found in the " + "configuration file; removing its lun %d, " + "backed by CTL lun %d", + oldtarg->t_iqn, oldlun->l_lun, + oldlun->l_ctl_lun); + error = kernel_lun_remove(oldlun); + if (error != 0) { + log_warnx("failed to remove lun %d, " + "target %s, CTL lun %d", + oldlun->l_lun, oldtarg->t_iqn, + oldlun->l_ctl_lun); + cumulated_error++; + } + lun_delete(oldlun); + } + target_delete(oldtarg); + continue; + } + + /* + * Second, remove any LUNs present in the old target + * and missing in the new one. + */ + TAILQ_FOREACH_SAFE(oldlun, &oldtarg->t_luns, l_next, tmplun) { + newlun = lun_find(newtarg, oldlun->l_lun); + if (newlun == NULL) { + log_debugx("lun %d, target %s, CTL lun %d " + "not found in the configuration file; " + "removing", oldlun->l_lun, oldtarg->t_iqn, + oldlun->l_ctl_lun); + error = kernel_lun_remove(oldlun); + if (error != 0) { + log_warnx("failed to remove lun %d, " + "target %s, CTL lun %d", + oldlun->l_lun, oldtarg->t_iqn, + oldlun->l_ctl_lun); + cumulated_error++; + } + lun_delete(oldlun); + continue; + } + + /* + * Also remove the LUNs changed by more than size. + */ + changed = 0; + assert(oldlun->l_backend != NULL); + assert(newlun->l_backend != NULL); + if (strcmp(newlun->l_backend, oldlun->l_backend) != 0) { + log_debugx("backend for lun %d, target %s, " + "CTL lun %d changed; removing", + oldlun->l_lun, oldtarg->t_iqn, + oldlun->l_ctl_lun); + changed = 1; + } + if (oldlun->l_blocksize != newlun->l_blocksize) { + log_debugx("blocksize for lun %d, target %s, " + "CTL lun %d changed; removing", + oldlun->l_lun, oldtarg->t_iqn, + oldlun->l_ctl_lun); + changed = 1; + } + if (newlun->l_device_id != NULL && + (oldlun->l_device_id == NULL || + strcmp(oldlun->l_device_id, newlun->l_device_id) != + 0)) { + log_debugx("device-id for lun %d, target %s, " + "CTL lun %d changed; removing", + oldlun->l_lun, oldtarg->t_iqn, + oldlun->l_ctl_lun); + changed = 1; + } + if (newlun->l_path != NULL && + (oldlun->l_path == NULL || + strcmp(oldlun->l_path, newlun->l_path) != 0)) { + log_debugx("path for lun %d, target %s, " + "CTL lun %d, changed; removing", + oldlun->l_lun, oldtarg->t_iqn, + oldlun->l_ctl_lun); + changed = 1; + } + if (newlun->l_serial != NULL && + (oldlun->l_serial == NULL || + strcmp(oldlun->l_serial, newlun->l_serial) != 0)) { + log_debugx("serial for lun %d, target %s, " + "CTL lun %d changed; removing", + oldlun->l_lun, oldtarg->t_iqn, + oldlun->l_ctl_lun); + changed = 1; + } + if (changed) { + error = kernel_lun_remove(oldlun); + if (error != 0) { + log_warnx("failed to remove lun %d, " + "target %s, CTL lun %d", + oldlun->l_lun, oldtarg->t_iqn, + oldlun->l_ctl_lun); + cumulated_error++; + } + lun_delete(oldlun); + continue; + } + + lun_set_ctl_lun(newlun, oldlun->l_ctl_lun); + } + } + + /* + * Now add new targets or modify existing ones. + */ + TAILQ_FOREACH(newtarg, &newconf->conf_targets, t_next) { + oldtarg = target_find(oldconf, newtarg->t_iqn); + + TAILQ_FOREACH(newlun, &newtarg->t_luns, l_next) { + if (oldtarg != NULL) { + oldlun = lun_find(oldtarg, newlun->l_lun); + if (oldlun != NULL) { + if (newlun->l_size != oldlun->l_size) { + log_debugx("resizing lun %d, " + "target %s, CTL lun %d", + newlun->l_lun, + newtarg->t_iqn, + newlun->l_ctl_lun); + error = + kernel_lun_resize(newlun); + if (error != 0) { + log_warnx("failed to " + "resize lun %d, " + "target %s, " + "CTL lun %d", + newlun->l_lun, + newtarg->t_iqn, + newlun->l_lun); + cumulated_error++; + } + } + continue; + } + } + log_debugx("adding lun %d, target %s", + newlun->l_lun, newtarg->t_iqn); + error = kernel_lun_add(newlun); + if (error != 0) { + log_warnx("failed to add lun %d, target %s", + newlun->l_lun, newtarg->t_iqn); + cumulated_error++; + } + } + } + + /* + * Go through the new portals, opening the sockets as neccessary. + */ + TAILQ_FOREACH(newpg, &newconf->conf_portal_groups, pg_next) { + if (newpg->pg_unassigned) { + log_debugx("not listening on portal-group \"%s\", " + "not assigned to any target", + newpg->pg_name); + continue; + } + TAILQ_FOREACH(newp, &newpg->pg_portals, p_next) { + /* + * Try to find already open portal and reuse + * the listening socket. We don't care about + * what portal or portal group that was, what + * matters is the listening address. + */ + TAILQ_FOREACH(oldpg, &oldconf->conf_portal_groups, + pg_next) { + TAILQ_FOREACH(oldp, &oldpg->pg_portals, + p_next) { + if (strcmp(newp->p_listen, + oldp->p_listen) == 0 && + oldp->p_socket > 0) { + newp->p_socket = + oldp->p_socket; + oldp->p_socket = 0; + break; + } + } + } + if (newp->p_socket > 0) { + /* + * We're done with this portal. + */ + continue; + } + +#ifdef ICL_KERNEL_PROXY + log_debugx("listening on %s, portal-group \"%s\" using ICL proxy", + newp->p_listen, newpg->pg_name); + kernel_listen(newp->p_ai, newp->p_iser); +#else + assert(newp->p_iser == false); + + log_debugx("listening on %s, portal-group \"%s\"", + newp->p_listen, newpg->pg_name); + newp->p_socket = socket(newp->p_ai->ai_family, + newp->p_ai->ai_socktype, + newp->p_ai->ai_protocol); + if (newp->p_socket < 0) { + log_warn("socket(2) failed for %s", + newp->p_listen); + cumulated_error++; + continue; + } + error = setsockopt(newp->p_socket, SOL_SOCKET, + SO_REUSEADDR, &one, sizeof(one)); + if (error != 0) { + log_warn("setsockopt(SO_REUSEADDR) failed " + "for %s", newp->p_listen); + close(newp->p_socket); + newp->p_socket = 0; + cumulated_error++; + continue; + } + error = bind(newp->p_socket, newp->p_ai->ai_addr, + newp->p_ai->ai_addrlen); + if (error != 0) { + log_warn("bind(2) failed for %s", + newp->p_listen); + close(newp->p_socket); + newp->p_socket = 0; + cumulated_error++; + continue; + } + error = listen(newp->p_socket, -1); + if (error != 0) { + log_warn("listen(2) failed for %s", + newp->p_listen); + close(newp->p_socket); + newp->p_socket = 0; + cumulated_error++; + continue; + } +#endif /* !ICL_KERNEL_PROXY */ + } + } + + /* + * Go through the no longer used sockets, closing them. + */ + TAILQ_FOREACH(oldpg, &oldconf->conf_portal_groups, pg_next) { + TAILQ_FOREACH(oldp, &oldpg->pg_portals, p_next) { + if (oldp->p_socket <= 0) + continue; + log_debugx("closing socket for %s, portal-group \"%s\"", + oldp->p_listen, oldpg->pg_name); + close(oldp->p_socket); + oldp->p_socket = 0; + } + } + + return (cumulated_error); +} + +bool +timed_out(void) +{ + + return (sigalrm_received); +} + +static void +sigalrm_handler(int dummy __unused) +{ + /* + * It would be easiest to just log an error and exit. We can't + * do this, though, because log_errx() is not signal safe, since + * it calls syslog(3). Instead, set a flag checked by pdu_send() + * and pdu_receive(), to call log_errx() there. Should they fail + * to notice, we'll exit here one second later. + */ + if (sigalrm_received) { + /* + * Oh well. Just give up and quit. + */ + _exit(2); + } + + sigalrm_received = true; +} + +static void +set_timeout(const struct conf *conf) +{ + struct sigaction sa; + struct itimerval itv; + int error; + + if (conf->conf_timeout <= 0) { + log_debugx("session timeout disabled"); + return; + } + + bzero(&sa, sizeof(sa)); + sa.sa_handler = sigalrm_handler; + sigfillset(&sa.sa_mask); + error = sigaction(SIGALRM, &sa, NULL); + if (error != 0) + log_err(1, "sigaction"); + + /* + * First SIGALRM will arive after conf_timeout seconds. + * If we do nothing, another one will arrive a second later. + */ + bzero(&itv, sizeof(itv)); + itv.it_interval.tv_sec = 1; + itv.it_value.tv_sec = conf->conf_timeout; + + log_debugx("setting session timeout to %d seconds", + conf->conf_timeout); + error = setitimer(ITIMER_REAL, &itv, NULL); + if (error != 0) + log_err(1, "setitimer"); +} + +static int +wait_for_children(bool block) +{ + pid_t pid; + int status; + int num = 0; + + for (;;) { + /* + * If "block" is true, wait for at least one process. + */ + if (block && num == 0) + pid = wait4(-1, &status, 0, NULL); + else + pid = wait4(-1, &status, WNOHANG, NULL); + if (pid <= 0) + break; + if (WIFSIGNALED(status)) { + log_warnx("child process %d terminated with signal %d", + pid, WTERMSIG(status)); + } else if (WEXITSTATUS(status) != 0) { + log_warnx("child process %d terminated with exit status %d", + pid, WEXITSTATUS(status)); + } else { + log_debugx("child process %d terminated gracefully", pid); + } + num++; + } + + return (num); +} + +static void +handle_connection(struct portal *portal, int fd, bool dont_fork) +{ + struct connection *conn; +#ifndef ICL_KERNEL_PROXY + struct sockaddr_storage ss; + socklen_t sslen = sizeof(ss); + int error; +#endif + pid_t pid; + char host[NI_MAXHOST + 1]; + struct conf *conf; + + conf = portal->p_portal_group->pg_conf; + + if (dont_fork) { + log_debugx("incoming connection; not forking due to -d flag"); + } else { + nchildren -= wait_for_children(false); + assert(nchildren >= 0); + + while (conf->conf_maxproc > 0 && nchildren >= conf->conf_maxproc) { + log_debugx("maxproc limit of %d child processes hit; " + "waiting for child process to exit", conf->conf_maxproc); + nchildren -= wait_for_children(true); + assert(nchildren >= 0); + } + log_debugx("incoming connection; forking child process #%d", + nchildren); + nchildren++; + pid = fork(); + if (pid < 0) + log_err(1, "fork"); + if (pid > 0) { + close(fd); + return; + } + } + pidfile_close(conf->conf_pidfh); + +#ifdef ICL_KERNEL_PROXY + /* + * XXX + */ + log_set_peer_addr("XXX"); +#else + error = getpeername(fd, (struct sockaddr *)&ss, &sslen); + if (error != 0) + log_err(1, "getpeername"); + error = getnameinfo((struct sockaddr *)&ss, sslen, + host, sizeof(host), NULL, 0, NI_NUMERICHOST); + if (error != 0) + log_errx(1, "getaddrinfo: %s", gai_strerror(error)); + + log_debugx("accepted connection from %s; portal group \"%s\"", + host, portal->p_portal_group->pg_name); + log_set_peer_addr(host); + setproctitle("%s", host); +#endif + + conn = connection_new(portal, fd, host); + set_timeout(conf); + kernel_capsicate(); + login(conn); + if (conn->conn_session_type == CONN_SESSION_TYPE_NORMAL) { + kernel_handoff(conn); + log_debugx("connection handed off to the kernel"); + } else { + assert(conn->conn_session_type == CONN_SESSION_TYPE_DISCOVERY); + discovery(conn); + } + log_debugx("nothing more to do; exiting"); + exit(0); +} + +#ifndef ICL_KERNEL_PROXY +static int +fd_add(int fd, fd_set *fdset, int nfds) +{ + + /* + * Skip sockets which we failed to bind. + */ + if (fd <= 0) + return (nfds); + + FD_SET(fd, fdset); + if (fd > nfds) + nfds = fd; + return (nfds); +} +#endif + +static void +main_loop(struct conf *conf, bool dont_fork) +{ + struct portal_group *pg; + struct portal *portal; +#ifdef ICL_KERNEL_PROXY + int connection_id; +#else + fd_set fdset; + int error, nfds, client_fd; +#endif + + pidfile_write(conf->conf_pidfh); + + for (;;) { + if (sighup_received || sigterm_received) + return; + +#ifdef ICL_KERNEL_PROXY + connection_id = kernel_accept(); + if (connection_id == 0) + continue; + + /* + * XXX: This is obviously temporary. + */ + pg = TAILQ_FIRST(&conf->conf_portal_groups); + portal = TAILQ_FIRST(&pg->pg_portals); + + handle_connection(portal, connection_id, dont_fork); +#else + FD_ZERO(&fdset); + nfds = 0; + TAILQ_FOREACH(pg, &conf->conf_portal_groups, pg_next) { + TAILQ_FOREACH(portal, &pg->pg_portals, p_next) + nfds = fd_add(portal->p_socket, &fdset, nfds); + } + error = select(nfds + 1, &fdset, NULL, NULL, NULL); + if (error <= 0) { + if (errno == EINTR) + return; + log_err(1, "select"); + } + TAILQ_FOREACH(pg, &conf->conf_portal_groups, pg_next) { + TAILQ_FOREACH(portal, &pg->pg_portals, p_next) { + if (!FD_ISSET(portal->p_socket, &fdset)) + continue; + client_fd = accept(portal->p_socket, NULL, 0); + if (client_fd < 0) + log_err(1, "accept"); + handle_connection(portal, client_fd, dont_fork); + break; + } + } +#endif /* !ICL_KERNEL_PROXY */ + } +} + +static void +sighup_handler(int dummy __unused) +{ + + sighup_received = true; +} + +static void +sigterm_handler(int dummy __unused) +{ + + sigterm_received = true; +} + +static void +register_signals(void) +{ + struct sigaction sa; + int error; + + bzero(&sa, sizeof(sa)); + sa.sa_handler = sighup_handler; + sigfillset(&sa.sa_mask); + error = sigaction(SIGHUP, &sa, NULL); + if (error != 0) + log_err(1, "sigaction"); + + sa.sa_handler = sigterm_handler; + error = sigaction(SIGTERM, &sa, NULL); + if (error != 0) + log_err(1, "sigaction"); + + sa.sa_handler = sigterm_handler; + error = sigaction(SIGINT, &sa, NULL); + if (error != 0) + log_err(1, "sigaction"); +} + +int +main(int argc, char **argv) +{ + struct conf *oldconf, *newconf, *tmpconf; + const char *config_path = DEFAULT_CONFIG_PATH; + int debug = 0, ch, error; + bool dont_daemonize = false; + + while ((ch = getopt(argc, argv, "df:")) != -1) { + switch (ch) { + case 'd': + dont_daemonize = true; + debug++; + break; + case 'f': + config_path = optarg; + break; + case '?': + default: + usage(); + } + } + argc -= optind; + if (argc != 0) + usage(); + + log_init(debug); + kernel_init(); + + oldconf = conf_new_from_kernel(); + newconf = conf_new_from_file(config_path); + if (newconf == NULL) + log_errx(1, "configuration error, exiting"); + if (debug > 0) { + oldconf->conf_debug = debug; + newconf->conf_debug = debug; + } + + if (dont_daemonize == false) { + if (daemon(0, 0) == -1) { + log_warn("cannot daemonize"); + pidfile_remove(newconf->conf_pidfh); + exit(1); + } + } + +#ifdef ICL_KERNEL_PROXY + log_debugx("enabling CTL iSCSI port"); + error = kernel_port_on(); + if (error != 0) + log_errx(1, "failed to enable CTL iSCSI port, exiting"); +#endif + + error = conf_apply(oldconf, newconf); + if (error != 0) + log_errx(1, "failed to apply configuration, exiting"); + conf_delete(oldconf); + oldconf = NULL; + + register_signals(); + +#ifndef ICL_KERNEL_PROXY + log_debugx("enabling CTL iSCSI port"); + error = kernel_port_on(); + if (error != 0) + log_errx(1, "failed to enable CTL iSCSI port, exiting"); +#endif + + for (;;) { + main_loop(newconf, dont_daemonize); + if (sighup_received) { + sighup_received = false; + log_debugx("received SIGHUP, reloading configuration"); + tmpconf = conf_new_from_file(config_path); + if (tmpconf == NULL) { + log_warnx("configuration error, " + "continuing with old configuration"); + } else { + if (debug > 0) + tmpconf->conf_debug = debug; + oldconf = newconf; + newconf = tmpconf; + error = conf_apply(oldconf, newconf); + if (error != 0) + log_warnx("failed to reload " + "configuration"); + conf_delete(oldconf); + oldconf = NULL; + } + } else if (sigterm_received) { + log_debugx("exiting on signal; " + "reloading empty configuration"); + + log_debugx("disabling CTL iSCSI port " + "and terminating all connections"); + error = kernel_port_off(); + if (error != 0) + log_warnx("failed to disable CTL iSCSI port"); + + oldconf = newconf; + newconf = conf_new(); + if (debug > 0) + newconf->conf_debug = debug; + error = conf_apply(oldconf, newconf); + if (error != 0) + log_warnx("failed to apply configuration"); + + log_warnx("exiting on signal"); + exit(0); + } else { + nchildren -= wait_for_children(false); + assert(nchildren >= 0); + } + } + /* NOTREACHED */ +} diff --git a/usr.sbin/ctld/ctld.h b/usr.sbin/ctld/ctld.h new file mode 100644 index 00000000000..3557978ab8b --- /dev/null +++ b/usr.sbin/ctld/ctld.h @@ -0,0 +1,277 @@ +/*- + * Copyright (c) 2012 The FreeBSD Foundation + * All rights reserved. + * + * This software was developed by Edward Tomasz Napierala under sponsorship + * from the FreeBSD Foundation. + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + * $FreeBSD$ + */ + +#ifndef CTLD_H +#define CTLD_H + +#include +#include +#include + +#define DEFAULT_CONFIG_PATH "/etc/ctl.conf" +#define DEFAULT_PIDFILE "/var/run/ctld.pid" +#define DEFAULT_BLOCKSIZE 512 + +#define MAX_NAME_LEN 223 +#define MAX_DATA_SEGMENT_LENGTH (128 * 1024) +#define MAX_BURST_LENGTH 16776192 + +struct auth { + TAILQ_ENTRY(auth) a_next; + struct auth_group *a_auth_group; + char *a_user; + char *a_secret; + char *a_mutual_user; + char *a_mutual_secret; +}; + +#define AG_TYPE_UNKNOWN 0 +#define AG_TYPE_NO_AUTHENTICATION 1 +#define AG_TYPE_CHAP 2 +#define AG_TYPE_CHAP_MUTUAL 3 + +struct auth_group { + TAILQ_ENTRY(auth_group) ag_next; + struct conf *ag_conf; + char *ag_name; + struct target *ag_target; + int ag_type; + TAILQ_HEAD(, auth) ag_auths; +}; + +struct portal { + TAILQ_ENTRY(portal) p_next; + struct portal_group *p_portal_group; + bool p_iser; + char *p_listen; + struct addrinfo *p_ai; + + TAILQ_HEAD(, target) p_targets; + int p_socket; +}; + +struct portal_group { + TAILQ_ENTRY(portal_group) pg_next; + struct conf *pg_conf; + char *pg_name; + struct auth_group *pg_discovery_auth_group; + bool pg_unassigned; + TAILQ_HEAD(, portal) pg_portals; + + uint16_t pg_tag; +}; + +struct lun_option { + TAILQ_ENTRY(lun_option) lo_next; + struct lun *lo_lun; + char *lo_name; + char *lo_value; +}; + +struct lun { + TAILQ_ENTRY(lun) l_next; + TAILQ_HEAD(, lun_option) l_options; + struct target *l_target; + int l_lun; + char *l_backend; + int l_blocksize; + char *l_device_id; + char *l_path; + char *l_serial; + int64_t l_size; + + int l_ctl_lun; +}; + +struct target { + TAILQ_ENTRY(target) t_next; + TAILQ_HEAD(, lun) t_luns; + struct conf *t_conf; + struct auth_group *t_auth_group; + struct portal_group *t_portal_group; + char *t_iqn; + char *t_alias; +}; + +struct conf { + char *conf_pidfile_path; + TAILQ_HEAD(, target) conf_targets; + TAILQ_HEAD(, auth_group) conf_auth_groups; + TAILQ_HEAD(, portal_group) conf_portal_groups; + int conf_debug; + int conf_timeout; + int conf_maxproc; + + uint16_t conf_last_portal_group_tag; + struct pidfh *conf_pidfh; +}; + +#define CONN_SESSION_TYPE_NONE 0 +#define CONN_SESSION_TYPE_DISCOVERY 1 +#define CONN_SESSION_TYPE_NORMAL 2 + +#define CONN_DIGEST_NONE 0 +#define CONN_DIGEST_CRC32C 1 + +struct connection { + struct portal *conn_portal; + struct target *conn_target; + int conn_socket; + int conn_session_type; + char *conn_initiator_name; + char *conn_initiator_addr; + char *conn_initiator_alias; + uint32_t conn_cmdsn; + uint32_t conn_statsn; + size_t conn_max_data_segment_length; + size_t conn_max_burst_length; + int conn_immediate_data; + int conn_header_digest; + int conn_data_digest; +}; + +struct pdu { + struct connection *pdu_connection; + struct iscsi_bhs *pdu_bhs; + char *pdu_data; + size_t pdu_data_len; +}; + +#define KEYS_MAX 1024 + +struct keys { + char *keys_names[KEYS_MAX]; + char *keys_values[KEYS_MAX]; + char *keys_data; + size_t keys_data_len; +}; + +struct conf *conf_new(void); +struct conf *conf_new_from_file(const char *path); +struct conf *conf_new_from_kernel(void); +void conf_delete(struct conf *conf); +int conf_verify(struct conf *conf); + +struct auth_group *auth_group_new(struct conf *conf, const char *name); +void auth_group_delete(struct auth_group *ag); +struct auth_group *auth_group_find(struct conf *conf, const char *name); + +const struct auth *auth_new_chap(struct auth_group *ag, + const char *user, const char *secret); +const struct auth *auth_new_chap_mutual(struct auth_group *ag, + const char *user, const char *secret, + const char *user2, const char *secret2); +const struct auth *auth_find(struct auth_group *ag, + const char *user); + +struct portal_group *portal_group_new(struct conf *conf, const char *name); +void portal_group_delete(struct portal_group *pg); +struct portal_group *portal_group_find(struct conf *conf, const char *name); +int portal_group_add_listen(struct portal_group *pg, + const char *listen, bool iser); + +struct target *target_new(struct conf *conf, const char *iqn); +void target_delete(struct target *target); +struct target *target_find(struct conf *conf, + const char *iqn); + +struct lun *lun_new(struct target *target, int lun_id); +void lun_delete(struct lun *lun); +struct lun *lun_find(struct target *target, int lun_id); +void lun_set_backend(struct lun *lun, const char *value); +void lun_set_blocksize(struct lun *lun, size_t value); +void lun_set_device_id(struct lun *lun, const char *value); +void lun_set_path(struct lun *lun, const char *value); +void lun_set_serial(struct lun *lun, const char *value); +void lun_set_size(struct lun *lun, size_t value); +void lun_set_ctl_lun(struct lun *lun, uint32_t value); + +struct lun_option *lun_option_new(struct lun *lun, + const char *name, const char *value); +void lun_option_delete(struct lun_option *clo); +struct lun_option *lun_option_find(struct lun *lun, const char *name); +void lun_option_set(struct lun_option *clo, + const char *value); + +void kernel_init(void); +int kernel_lun_add(struct lun *lun); +int kernel_lun_resize(struct lun *lun); +int kernel_lun_remove(struct lun *lun); +void kernel_handoff(struct connection *conn); +int kernel_port_on(void); +int kernel_port_off(void); +void kernel_capsicate(void); + +/* + * ICL_KERNEL_PROXY + */ +void kernel_listen(struct addrinfo *ai, bool iser); +int kernel_accept(void); +void kernel_send(struct pdu *pdu); +void kernel_receive(struct pdu *pdu); + +struct keys *keys_new(void); +void keys_delete(struct keys *keys); +void keys_load(struct keys *keys, const struct pdu *pdu); +void keys_save(struct keys *keys, struct pdu *pdu); +const char *keys_find(struct keys *keys, const char *name); +int keys_find_int(struct keys *keys, const char *name); +void keys_add(struct keys *keys, + const char *name, const char *value); +void keys_add_int(struct keys *keys, + const char *name, int value); + +struct pdu *pdu_new(struct connection *conn); +struct pdu *pdu_new_response(struct pdu *request); +void pdu_delete(struct pdu *pdu); +void pdu_receive(struct pdu *request); +void pdu_send(struct pdu *response); + +void login(struct connection *conn); + +void discovery(struct connection *conn); + +void log_init(int level); +void log_set_peer_name(const char *name); +void log_set_peer_addr(const char *addr); +void log_err(int, const char *, ...) + __dead2 __printf0like(2, 3); +void log_errx(int, const char *, ...) + __dead2 __printf0like(2, 3); +void log_warn(const char *, ...) __printf0like(1, 2); +void log_warnx(const char *, ...) __printflike(1, 2); +void log_debugx(const char *, ...) __printf0like(1, 2); + +char *checked_strdup(const char *); +bool valid_iscsi_name(const char *name); +bool timed_out(void); + +#endif /* !CTLD_H */ diff --git a/usr.sbin/ctld/discovery.c b/usr.sbin/ctld/discovery.c new file mode 100644 index 00000000000..bef7da69bbb --- /dev/null +++ b/usr.sbin/ctld/discovery.c @@ -0,0 +1,221 @@ +/*- + * Copyright (c) 2012 The FreeBSD Foundation + * All rights reserved. + * + * This software was developed by Edward Tomasz Napierala under sponsorship + * from the FreeBSD Foundation. + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + * $FreeBSD$ + */ + +#include +#include +#include +#include +#include +#include + +#include "ctld.h" +#include "iscsi_proto.h" + +static struct pdu * +text_receive(struct connection *conn) +{ + struct pdu *request; + struct iscsi_bhs_text_request *bhstr; + + request = pdu_new(conn); + pdu_receive(request); + if ((request->pdu_bhs->bhs_opcode & ~ISCSI_BHS_OPCODE_IMMEDIATE) != + ISCSI_BHS_OPCODE_TEXT_REQUEST) + log_errx(1, "protocol error: received invalid opcode 0x%x", + request->pdu_bhs->bhs_opcode); + bhstr = (struct iscsi_bhs_text_request *)request->pdu_bhs; +#if 0 + if ((bhstr->bhstr_flags & ISCSI_BHSTR_FLAGS_FINAL) == 0) + log_errx(1, "received Text PDU without the \"F\" flag"); +#endif + /* + * XXX: Implement the C flag some day. + */ + if ((bhstr->bhstr_flags & BHSTR_FLAGS_CONTINUE) != 0) + log_errx(1, "received Text PDU with unsupported \"C\" flag"); + if (request->pdu_data_len == 0) + log_errx(1, "received Text PDU with empty data segment"); + + if (ntohl(bhstr->bhstr_cmdsn) < conn->conn_cmdsn) { + log_errx(1, "received Text PDU with decreasing CmdSN: " + "was %d, is %d", conn->conn_cmdsn, ntohl(bhstr->bhstr_cmdsn)); + } + if (ntohl(bhstr->bhstr_expstatsn) != conn->conn_statsn) { + log_errx(1, "received Text PDU with wrong StatSN: " + "is %d, should be %d", ntohl(bhstr->bhstr_expstatsn), + conn->conn_statsn); + } + conn->conn_cmdsn = ntohl(bhstr->bhstr_cmdsn); + + return (request); +} + +static struct pdu * +text_new_response(struct pdu *request) +{ + struct pdu *response; + struct connection *conn; + struct iscsi_bhs_text_request *bhstr; + struct iscsi_bhs_text_response *bhstr2; + + bhstr = (struct iscsi_bhs_text_request *)request->pdu_bhs; + conn = request->pdu_connection; + + response = pdu_new_response(request); + bhstr2 = (struct iscsi_bhs_text_response *)response->pdu_bhs; + bhstr2->bhstr_opcode = ISCSI_BHS_OPCODE_TEXT_RESPONSE; + bhstr2->bhstr_flags = BHSTR_FLAGS_FINAL; + bhstr2->bhstr_lun = bhstr->bhstr_lun; + bhstr2->bhstr_initiator_task_tag = bhstr->bhstr_initiator_task_tag; + bhstr2->bhstr_target_transfer_tag = bhstr->bhstr_target_transfer_tag; + bhstr2->bhstr_statsn = htonl(conn->conn_statsn++); + bhstr2->bhstr_expcmdsn = htonl(conn->conn_cmdsn); + bhstr2->bhstr_maxcmdsn = htonl(conn->conn_cmdsn); + + return (response); +} + +static struct pdu * +logout_receive(struct connection *conn) +{ + struct pdu *request; + struct iscsi_bhs_logout_request *bhslr; + + request = pdu_new(conn); + pdu_receive(request); + if ((request->pdu_bhs->bhs_opcode & ~ISCSI_BHS_OPCODE_IMMEDIATE) != + ISCSI_BHS_OPCODE_LOGOUT_REQUEST) + log_errx(1, "protocol error: received invalid opcode 0x%x", + request->pdu_bhs->bhs_opcode); + bhslr = (struct iscsi_bhs_logout_request *)request->pdu_bhs; + if ((bhslr->bhslr_reason & 0x7f) != BHSLR_REASON_CLOSE_SESSION) + log_debugx("received Logout PDU with invalid reason 0x%x; " + "continuing anyway", bhslr->bhslr_reason & 0x7f); + if (ntohl(bhslr->bhslr_cmdsn) < conn->conn_cmdsn) { + log_errx(1, "received Logout PDU with decreasing CmdSN: " + "was %d, is %d", conn->conn_cmdsn, + ntohl(bhslr->bhslr_cmdsn)); + } + if (ntohl(bhslr->bhslr_expstatsn) != conn->conn_statsn) { + log_errx(1, "received Logout PDU with wrong StatSN: " + "is %d, should be %d", ntohl(bhslr->bhslr_expstatsn), + conn->conn_statsn); + } + conn->conn_cmdsn = ntohl(bhslr->bhslr_cmdsn); + + return (request); +} + +static struct pdu * +logout_new_response(struct pdu *request) +{ + struct pdu *response; + struct connection *conn; + struct iscsi_bhs_logout_request *bhslr; + struct iscsi_bhs_logout_response *bhslr2; + + bhslr = (struct iscsi_bhs_logout_request *)request->pdu_bhs; + conn = request->pdu_connection; + + response = pdu_new_response(request); + bhslr2 = (struct iscsi_bhs_logout_response *)response->pdu_bhs; + bhslr2->bhslr_opcode = ISCSI_BHS_OPCODE_LOGOUT_RESPONSE; + bhslr2->bhslr_flags = 0x80; + bhslr2->bhslr_response = BHSLR_RESPONSE_CLOSED_SUCCESSFULLY; + bhslr2->bhslr_initiator_task_tag = bhslr->bhslr_initiator_task_tag; + bhslr2->bhslr_statsn = htonl(conn->conn_statsn++); + bhslr2->bhslr_expcmdsn = htonl(conn->conn_cmdsn); + bhslr2->bhslr_maxcmdsn = htonl(conn->conn_cmdsn); + + return (response); +} + +void +discovery(struct connection *conn) +{ + struct pdu *request, *response; + struct keys *request_keys, *response_keys; + struct target *targ; + const char *send_targets; + + log_debugx("beginning discovery session; waiting for Text PDU"); + request = text_receive(conn); + request_keys = keys_new(); + keys_load(request_keys, request); + + send_targets = keys_find(request_keys, "SendTargets"); + if (send_targets == NULL) + log_errx(1, "received Text PDU without SendTargets"); + + response = text_new_response(request); + response_keys = keys_new(); + + if (strcmp(send_targets, "All") == 0) { + TAILQ_FOREACH(targ, + &conn->conn_portal->p_portal_group->pg_conf->conf_targets, + t_next) { + if (targ->t_portal_group != + conn->conn_portal->p_portal_group) { + log_debugx("not returning target \"%s\"; " + "belongs to a different portal group", + targ->t_iqn); + continue; + } + keys_add(response_keys, "TargetName", targ->t_iqn); + } + } else { + targ = target_find(conn->conn_portal->p_portal_group->pg_conf, + send_targets); + if (targ == NULL) { + log_debugx("initiator requested information on unknown " + "target \"%s\"; returning nothing", send_targets); + } else { + keys_add(response_keys, "TargetName", targ->t_iqn); + } + } + keys_save(response_keys, response); + + pdu_send(response); + pdu_delete(response); + keys_delete(response_keys); + pdu_delete(request); + keys_delete(request_keys); + + log_debugx("done sending targets; waiting for Logout PDU"); + request = logout_receive(conn); + response = logout_new_response(request); + + pdu_send(response); + pdu_delete(response); + pdu_delete(request); + + log_debugx("discovery session done"); +} diff --git a/usr.sbin/ctld/kernel.c b/usr.sbin/ctld/kernel.c new file mode 100644 index 00000000000..0e002042192 --- /dev/null +++ b/usr.sbin/ctld/kernel.c @@ -0,0 +1,782 @@ +/*- + * Copyright (c) 2003, 2004 Silicon Graphics International Corp. + * Copyright (c) 1997-2007 Kenneth D. Merry + * Copyright (c) 2012 The FreeBSD Foundation + * All rights reserved. + * + * Portions of this software were developed by Edward Tomasz Napierala + * under sponsorship from the FreeBSD Foundation. + * + * 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, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES. + * + * $FreeBSD$ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef ICL_KERNEL_PROXY +#include +#endif + +#include "ctld.h" + +static int ctl_fd = 0; + +void +kernel_init(void) +{ + int retval, saved_errno; + + ctl_fd = open(CTL_DEFAULT_DEV, O_RDWR); + if (ctl_fd < 0 && errno == ENOENT) { + saved_errno = errno; + retval = kldload("ctl"); + if (retval != -1) + ctl_fd = open(CTL_DEFAULT_DEV, O_RDWR); + else + errno = saved_errno; + } + if (ctl_fd < 0) + log_err(1, "failed to open %s", CTL_DEFAULT_DEV); +} + +/* + * Name/value pair used for per-LUN attributes. + */ +struct cctl_lun_nv { + char *name; + char *value; + STAILQ_ENTRY(cctl_lun_nv) links; +}; + +/* + * Backend LUN information. + */ +struct cctl_lun { + uint64_t lun_id; + char *backend_type; + uint64_t size_blocks; + uint32_t blocksize; + char *serial_number; + char *device_id; + char *cfiscsi_target; + char *cfiscsi_target_alias; + int cfiscsi_lun; + STAILQ_HEAD(,cctl_lun_nv) attr_list; + STAILQ_ENTRY(cctl_lun) links; +}; + +struct cctl_devlist_data { + int num_luns; + STAILQ_HEAD(,cctl_lun) lun_list; + struct cctl_lun *cur_lun; + int level; + struct sbuf *cur_sb[32]; +}; + +static void +cctl_start_element(void *user_data, const char *name, const char **attr) +{ + int i; + struct cctl_devlist_data *devlist; + struct cctl_lun *cur_lun; + + devlist = (struct cctl_devlist_data *)user_data; + cur_lun = devlist->cur_lun; + devlist->level++; + if ((u_int)devlist->level > (sizeof(devlist->cur_sb) / + sizeof(devlist->cur_sb[0]))) + log_errx(1, "%s: too many nesting levels, %zd max", __func__, + sizeof(devlist->cur_sb) / sizeof(devlist->cur_sb[0])); + + devlist->cur_sb[devlist->level] = sbuf_new_auto(); + if (devlist->cur_sb[devlist->level] == NULL) + log_err(1, "%s: unable to allocate sbuf", __func__); + + if (strcmp(name, "lun") == 0) { + if (cur_lun != NULL) + log_errx(1, "%s: improper lun element nesting", + __func__); + + cur_lun = calloc(1, sizeof(*cur_lun)); + if (cur_lun == NULL) + log_err(1, "%s: cannot allocate %zd bytes", __func__, + sizeof(*cur_lun)); + + devlist->num_luns++; + devlist->cur_lun = cur_lun; + + STAILQ_INIT(&cur_lun->attr_list); + STAILQ_INSERT_TAIL(&devlist->lun_list, cur_lun, links); + + for (i = 0; attr[i] != NULL; i += 2) { + if (strcmp(attr[i], "id") == 0) { + cur_lun->lun_id = strtoull(attr[i+1], NULL, 0); + } else { + log_errx(1, "%s: invalid LUN attribute %s = %s", + __func__, attr[i], attr[i+1]); + } + } + } +} + +static void +cctl_end_element(void *user_data, const char *name) +{ + struct cctl_devlist_data *devlist; + struct cctl_lun *cur_lun; + char *str; + + devlist = (struct cctl_devlist_data *)user_data; + cur_lun = devlist->cur_lun; + + if ((cur_lun == NULL) + && (strcmp(name, "ctllunlist") != 0)) + log_errx(1, "%s: cur_lun == NULL! (name = %s)", __func__, name); + + if (devlist->cur_sb[devlist->level] == NULL) + log_errx(1, "%s: no valid sbuf at level %d (name %s)", __func__, + devlist->level, name); + + sbuf_finish(devlist->cur_sb[devlist->level]); + str = checked_strdup(sbuf_data(devlist->cur_sb[devlist->level])); + + if (strlen(str) == 0) { + free(str); + str = NULL; + } + + sbuf_delete(devlist->cur_sb[devlist->level]); + devlist->cur_sb[devlist->level] = NULL; + devlist->level--; + + if (strcmp(name, "backend_type") == 0) { + cur_lun->backend_type = str; + str = NULL; + } else if (strcmp(name, "size") == 0) { + cur_lun->size_blocks = strtoull(str, NULL, 0); + } else if (strcmp(name, "blocksize") == 0) { + cur_lun->blocksize = strtoul(str, NULL, 0); + } else if (strcmp(name, "serial_number") == 0) { + cur_lun->serial_number = str; + str = NULL; + } else if (strcmp(name, "device_id") == 0) { + cur_lun->device_id = str; + str = NULL; + } else if (strcmp(name, "cfiscsi_target") == 0) { + cur_lun->cfiscsi_target = str; + str = NULL; + } else if (strcmp(name, "cfiscsi_target_alias") == 0) { + cur_lun->cfiscsi_target_alias = str; + str = NULL; + } else if (strcmp(name, "cfiscsi_lun") == 0) { + cur_lun->cfiscsi_lun = strtoul(str, NULL, 0); + } else if (strcmp(name, "lun") == 0) { + devlist->cur_lun = NULL; + } else if (strcmp(name, "ctllunlist") == 0) { + + } else { + struct cctl_lun_nv *nv; + + nv = calloc(1, sizeof(*nv)); + if (nv == NULL) + log_err(1, "%s: can't allocate %zd bytes for nv pair", + __func__, sizeof(*nv)); + + nv->name = checked_strdup(name); + + nv->value = str; + str = NULL; + STAILQ_INSERT_TAIL(&cur_lun->attr_list, nv, links); + } + + free(str); +} + +static void +cctl_char_handler(void *user_data, const XML_Char *str, int len) +{ + struct cctl_devlist_data *devlist; + + devlist = (struct cctl_devlist_data *)user_data; + + sbuf_bcat(devlist->cur_sb[devlist->level], str, len); +} + +struct conf * +conf_new_from_kernel(void) +{ + struct conf *conf = NULL; + struct target *targ; + struct lun *cl; + struct lun_option *lo; + struct ctl_lun_list list; + struct cctl_devlist_data devlist; + struct cctl_lun *lun; + XML_Parser parser; + char *lun_str = NULL; + int lun_len; + int retval; + + lun_len = 4096; + + bzero(&devlist, sizeof(devlist)); + STAILQ_INIT(&devlist.lun_list); + + log_debugx("obtaining previously configured CTL luns from the kernel"); + +retry: + lun_str = realloc(lun_str, lun_len); + if (lun_str == NULL) + log_err(1, "realloc"); + + bzero(&list, sizeof(list)); + list.alloc_len = lun_len; + list.status = CTL_LUN_LIST_NONE; + list.lun_xml = lun_str; + + if (ioctl(ctl_fd, CTL_LUN_LIST, &list) == -1) { + log_warn("error issuing CTL_LUN_LIST ioctl"); + free(lun_str); + return (NULL); + } + + if (list.status == CTL_LUN_LIST_ERROR) { + log_warnx("error returned from CTL_LUN_LIST ioctl: %s", + list.error_str); + free(lun_str); + return (NULL); + } + + if (list.status == CTL_LUN_LIST_NEED_MORE_SPACE) { + lun_len = lun_len << 1; + goto retry; + } + + parser = XML_ParserCreate(NULL); + if (parser == NULL) { + log_warnx("unable to create XML parser"); + free(lun_str); + return (NULL); + } + + XML_SetUserData(parser, &devlist); + XML_SetElementHandler(parser, cctl_start_element, cctl_end_element); + XML_SetCharacterDataHandler(parser, cctl_char_handler); + + retval = XML_Parse(parser, lun_str, strlen(lun_str), 1); + XML_ParserFree(parser); + free(lun_str); + if (retval != 1) { + log_warnx("XML_Parse failed"); + return (NULL); + } + + conf = conf_new(); + + STAILQ_FOREACH(lun, &devlist.lun_list, links) { + struct cctl_lun_nv *nv; + + if (lun->cfiscsi_target == NULL) { + log_debugx("CTL lun %ju wasn't managed by ctld; " + "ignoring", (uintmax_t)lun->lun_id); + continue; + } + + targ = target_find(conf, lun->cfiscsi_target); + if (targ == NULL) { +#if 0 + log_debugx("found new kernel target %s for CTL lun %ld", + lun->cfiscsi_target, lun->lun_id); +#endif + targ = target_new(conf, lun->cfiscsi_target); + if (targ == NULL) { + log_warnx("target_new failed"); + continue; + } + } + + cl = lun_find(targ, lun->cfiscsi_lun); + if (cl != NULL) { + log_warnx("found CTL lun %ju, backing lun %d, target " + "%s, also backed by CTL lun %d; ignoring", + (uintmax_t) lun->lun_id, cl->l_lun, + cl->l_target->t_iqn, cl->l_ctl_lun); + continue; + } + + log_debugx("found CTL lun %ju, backing lun %d, target %s", + (uintmax_t)lun->lun_id, lun->cfiscsi_lun, lun->cfiscsi_target); + + cl = lun_new(targ, lun->cfiscsi_lun); + if (cl == NULL) { + log_warnx("lun_new failed"); + continue; + } + lun_set_backend(cl, lun->backend_type); + lun_set_blocksize(cl, lun->blocksize); + lun_set_device_id(cl, lun->device_id); + lun_set_serial(cl, lun->serial_number); + lun_set_size(cl, lun->size_blocks * cl->l_blocksize); + lun_set_ctl_lun(cl, lun->lun_id); + + STAILQ_FOREACH(nv, &lun->attr_list, links) { + if (strcmp(nv->name, "file") == 0 || + strcmp(nv->name, "dev") == 0) { + lun_set_path(cl, nv->value); + continue; + } + lo = lun_option_new(cl, nv->name, nv->value); + if (lo == NULL) + log_warnx("unable to add CTL lun option %s " + "for CTL lun %ju for lun %d, target %s", + nv->name, (uintmax_t) lun->lun_id, + cl->l_lun, cl->l_target->t_iqn); + } + } + + return (conf); +} + +int +kernel_lun_add(struct lun *lun) +{ + struct lun_option *lo; + struct ctl_lun_req req; + char *tmp; + int error, i, num_options; + + bzero(&req, sizeof(req)); + + strlcpy(req.backend, lun->l_backend, sizeof(req.backend)); + req.reqtype = CTL_LUNREQ_CREATE; + + req.reqdata.create.blocksize_bytes = lun->l_blocksize; + + if (lun->l_size != 0) + req.reqdata.create.lun_size_bytes = lun->l_size; + + req.reqdata.create.flags |= CTL_LUN_FLAG_DEV_TYPE; + req.reqdata.create.device_type = T_DIRECT; + + if (lun->l_serial != NULL) { + strlcpy(req.reqdata.create.serial_num, lun->l_serial, + sizeof(req.reqdata.create.serial_num)); + req.reqdata.create.flags |= CTL_LUN_FLAG_SERIAL_NUM; + } + + if (lun->l_device_id != NULL) { + strlcpy(req.reqdata.create.device_id, lun->l_device_id, + sizeof(req.reqdata.create.device_id)); + req.reqdata.create.flags |= CTL_LUN_FLAG_DEVID; + } + + if (lun->l_path != NULL) { + lo = lun_option_find(lun, "file"); + if (lo != NULL) { + lun_option_set(lo, lun->l_path); + } else { + lo = lun_option_new(lun, "file", lun->l_path); + assert(lo != NULL); + } + } + + lo = lun_option_find(lun, "cfiscsi_target"); + if (lo != NULL) { + lun_option_set(lo, lun->l_target->t_iqn); + } else { + lo = lun_option_new(lun, "cfiscsi_target", + lun->l_target->t_iqn); + assert(lo != NULL); + } + + if (lun->l_target->t_alias != NULL) { + lo = lun_option_find(lun, "cfiscsi_target_alias"); + if (lo != NULL) { + lun_option_set(lo, lun->l_target->t_alias); + } else { + lo = lun_option_new(lun, "cfiscsi_target_alias", + lun->l_target->t_alias); + assert(lo != NULL); + } + } + + asprintf(&tmp, "%d", lun->l_lun); + if (tmp == NULL) + log_errx(1, "asprintf"); + lo = lun_option_find(lun, "cfiscsi_lun"); + if (lo != NULL) { + lun_option_set(lo, tmp); + free(tmp); + } else { + lo = lun_option_new(lun, "cfiscsi_lun", tmp); + free(tmp); + assert(lo != NULL); + } + + num_options = 0; + TAILQ_FOREACH(lo, &lun->l_options, lo_next) + num_options++; + + req.num_be_args = num_options; + if (num_options > 0) { + req.be_args = malloc(num_options * sizeof(*req.be_args)); + if (req.be_args == NULL) { + log_warn("error allocating %zd bytes", + num_options * sizeof(*req.be_args)); + return (1); + } + + i = 0; + TAILQ_FOREACH(lo, &lun->l_options, lo_next) { + /* + * +1 for the terminating '\0' + */ + req.be_args[i].namelen = strlen(lo->lo_name) + 1; + req.be_args[i].name = lo->lo_name; + req.be_args[i].vallen = strlen(lo->lo_value) + 1; + req.be_args[i].value = lo->lo_value; + req.be_args[i].flags = CTL_BEARG_ASCII | CTL_BEARG_RD; + i++; + } + assert(i == num_options); + } + + error = ioctl(ctl_fd, CTL_LUN_REQ, &req); + free(req.be_args); + if (error != 0) { + log_warn("error issuing CTL_LUN_REQ ioctl"); + return (1); + } + + if (req.status == CTL_LUN_ERROR) { + log_warnx("error returned from LUN creation request: %s", + req.error_str); + return (1); + } + + if (req.status != CTL_LUN_OK) { + log_warnx("unknown LUN creation request status %d", + req.status); + return (1); + } + + lun_set_ctl_lun(lun, req.reqdata.create.req_lun_id); + + return (0); +} + +int +kernel_lun_resize(struct lun *lun) +{ + struct ctl_lun_req req; + + bzero(&req, sizeof(req)); + + strlcpy(req.backend, lun->l_backend, sizeof(req.backend)); + req.reqtype = CTL_LUNREQ_MODIFY; + + req.reqdata.modify.lun_id = lun->l_ctl_lun; + req.reqdata.modify.lun_size_bytes = lun->l_size; + + if (ioctl(ctl_fd, CTL_LUN_REQ, &req) == -1) { + log_warn("error issuing CTL_LUN_REQ ioctl"); + return (1); + } + + if (req.status == CTL_LUN_ERROR) { + log_warnx("error returned from LUN modification request: %s", + req.error_str); + return (1); + } + + if (req.status != CTL_LUN_OK) { + log_warnx("unknown LUN modification request status %d", + req.status); + return (1); + } + + return (0); +} + +int +kernel_lun_remove(struct lun *lun) +{ + struct ctl_lun_req req; + + bzero(&req, sizeof(req)); + + strlcpy(req.backend, lun->l_backend, sizeof(req.backend)); + req.reqtype = CTL_LUNREQ_RM; + + req.reqdata.rm.lun_id = lun->l_ctl_lun; + + if (ioctl(ctl_fd, CTL_LUN_REQ, &req) == -1) { + log_warn("error issuing CTL_LUN_REQ ioctl"); + return (1); + } + + if (req.status == CTL_LUN_ERROR) { + log_warnx("error returned from LUN removal request: %s", + req.error_str); + return (1); + } + + if (req.status != CTL_LUN_OK) { + log_warnx("unknown LUN removal request status %d", req.status); + return (1); + } + + return (0); +} + +void +kernel_handoff(struct connection *conn) +{ + struct ctl_iscsi req; + + bzero(&req, sizeof(req)); + + req.type = CTL_ISCSI_HANDOFF; + strlcpy(req.data.handoff.initiator_name, + conn->conn_initiator_name, sizeof(req.data.handoff.initiator_name)); + strlcpy(req.data.handoff.initiator_addr, + conn->conn_initiator_addr, sizeof(req.data.handoff.initiator_addr)); + if (conn->conn_initiator_alias != NULL) { + strlcpy(req.data.handoff.initiator_alias, + conn->conn_initiator_alias, sizeof(req.data.handoff.initiator_alias)); + } + strlcpy(req.data.handoff.target_name, + conn->conn_target->t_iqn, sizeof(req.data.handoff.target_name)); + req.data.handoff.socket = conn->conn_socket; + req.data.handoff.portal_group_tag = + conn->conn_portal->p_portal_group->pg_tag; + if (conn->conn_header_digest == CONN_DIGEST_CRC32C) + req.data.handoff.header_digest = CTL_ISCSI_DIGEST_CRC32C; + if (conn->conn_data_digest == CONN_DIGEST_CRC32C) + req.data.handoff.data_digest = CTL_ISCSI_DIGEST_CRC32C; + req.data.handoff.cmdsn = conn->conn_cmdsn; + req.data.handoff.statsn = conn->conn_statsn; + req.data.handoff.max_recv_data_segment_length = + conn->conn_max_data_segment_length; + req.data.handoff.max_burst_length = conn->conn_max_burst_length; + req.data.handoff.immediate_data = conn->conn_immediate_data; + + if (ioctl(ctl_fd, CTL_ISCSI, &req) == -1) + log_err(1, "error issuing CTL_ISCSI ioctl; " + "dropping connection"); + + if (req.status != CTL_ISCSI_OK) + log_errx(1, "error returned from CTL iSCSI handoff request: " + "%s; dropping connection", req.error_str); +} + +int +kernel_port_on(void) +{ + struct ctl_port_entry entry; + int error; + + bzero(&entry, sizeof(entry)); + + entry.port_type = CTL_PORT_ISCSI; + entry.targ_port = -1; + + error = ioctl(ctl_fd, CTL_ENABLE_PORT, &entry); + if (error != 0) { + log_warn("CTL_ENABLE_PORT ioctl failed"); + return (-1); + } + + return (0); +} + +int +kernel_port_off(void) +{ + struct ctl_port_entry entry; + int error; + + bzero(&entry, sizeof(entry)); + + entry.port_type = CTL_PORT_ISCSI; + entry.targ_port = -1; + + error = ioctl(ctl_fd, CTL_DISABLE_PORT, &entry); + if (error != 0) { + log_warn("CTL_DISABLE_PORT ioctl failed"); + return (-1); + } + + return (0); +} + +#ifdef ICL_KERNEL_PROXY +void +kernel_listen(struct addrinfo *ai, bool iser) +{ + struct ctl_iscsi req; + + bzero(&req, sizeof(req)); + + req.type = CTL_ISCSI_LISTEN; + req.data.listen.iser = iser; + req.data.listen.domain = ai->ai_family; + req.data.listen.socktype = ai->ai_socktype; + req.data.listen.protocol = ai->ai_protocol; + req.data.listen.addr = ai->ai_addr; + req.data.listen.addrlen = ai->ai_addrlen; + + if (ioctl(ctl_fd, CTL_ISCSI, &req) == -1) + log_warn("error issuing CTL_ISCSI_LISTEN ioctl"); +} + +int +kernel_accept(void) +{ + struct ctl_iscsi req; + + bzero(&req, sizeof(req)); + + req.type = CTL_ISCSI_ACCEPT; + + if (ioctl(ctl_fd, CTL_ISCSI, &req) == -1) { + log_warn("error issuing CTL_ISCSI_LISTEN ioctl"); + return (0); + } + + return (req.data.accept.connection_id); +} + +void +kernel_send(struct pdu *pdu) +{ + struct ctl_iscsi req; + + bzero(&req, sizeof(req)); + + req.type = CTL_ISCSI_SEND; + req.data.send.connection_id = pdu->pdu_connection->conn_socket; + req.data.send.bhs = pdu->pdu_bhs; + req.data.send.data_segment_len = pdu->pdu_data_len; + req.data.send.data_segment = pdu->pdu_data; + + if (ioctl(ctl_fd, CTL_ISCSI, &req) == -1) + log_err(1, "error issuing CTL_ISCSI ioctl; " + "dropping connection"); + + if (req.status != CTL_ISCSI_OK) + log_errx(1, "error returned from CTL iSCSI send: " + "%s; dropping connection", req.error_str); +} + +void +kernel_receive(struct pdu *pdu) +{ + struct ctl_iscsi req; + + pdu->pdu_data = malloc(MAX_DATA_SEGMENT_LENGTH); + if (pdu->pdu_data == NULL) + log_err(1, "malloc"); + + bzero(&req, sizeof(req)); + + req.type = CTL_ISCSI_RECEIVE; + req.data.receive.connection_id = pdu->pdu_connection->conn_socket; + req.data.receive.bhs = pdu->pdu_bhs; + req.data.receive.data_segment_len = MAX_DATA_SEGMENT_LENGTH; + req.data.receive.data_segment = pdu->pdu_data; + + if (ioctl(ctl_fd, CTL_ISCSI, &req) == -1) + log_err(1, "error issuing CTL_ISCSI ioctl; " + "dropping connection"); + + if (req.status != CTL_ISCSI_OK) + log_errx(1, "error returned from CTL iSCSI receive: " + "%s; dropping connection", req.error_str); + +} + +#endif /* ICL_KERNEL_PROXY */ + +/* + * XXX: I CANT INTO LATIN + */ +void +kernel_capsicate(void) +{ + int error; + cap_rights_t rights; + const unsigned long cmds[] = { CTL_ISCSI }; + + cap_rights_init(&rights, CAP_IOCTL); + error = cap_rights_limit(ctl_fd, &rights); + if (error != 0 && errno != ENOSYS) + log_err(1, "cap_rights_limit"); + + error = cap_ioctls_limit(ctl_fd, cmds, + sizeof(cmds) / sizeof(cmds[0])); + if (error != 0 && errno != ENOSYS) + log_err(1, "cap_ioctls_limit"); + + error = cap_enter(); + if (error != 0 && errno != ENOSYS) + log_err(1, "cap_enter"); + + if (cap_sandboxed()) + log_debugx("Capsicum capability mode enabled"); + else + log_warnx("Capsicum capability mode not supported"); +} + diff --git a/usr.sbin/ctld/keys.c b/usr.sbin/ctld/keys.c new file mode 100644 index 00000000000..0ea4aa0ae3b --- /dev/null +++ b/usr.sbin/ctld/keys.c @@ -0,0 +1,216 @@ +/*- + * Copyright (c) 2012 The FreeBSD Foundation + * All rights reserved. + * + * This software was developed by Edward Tomasz Napierala under sponsorship + * from the FreeBSD Foundation. + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + * $FreeBSD$ + */ + +#include +#include +#include +#include +#include +#include "ctld.h" + +struct keys * +keys_new(void) +{ + struct keys *keys; + + keys = calloc(sizeof(*keys), 1); + if (keys == NULL) + log_err(1, "calloc"); + + return (keys); +} + +void +keys_delete(struct keys *keys) +{ + + free(keys->keys_data); + free(keys); +} + +void +keys_load(struct keys *keys, const struct pdu *pdu) +{ + int i; + char *pair; + size_t pair_len; + + if (pdu->pdu_data_len == 0) + log_errx(1, "protocol error: empty data segment"); + + if (pdu->pdu_data[pdu->pdu_data_len - 1] != '\0') + log_errx(1, "protocol error: key not NULL-terminated\n"); + + assert(keys->keys_data == NULL); + keys->keys_data_len = pdu->pdu_data_len; + keys->keys_data = malloc(keys->keys_data_len); + if (keys->keys_data == NULL) + log_err(1, "malloc"); + memcpy(keys->keys_data, pdu->pdu_data, keys->keys_data_len); + + /* + * XXX: Review this carefully. + */ + pair = keys->keys_data; + for (i = 0;; i++) { + if (i >= KEYS_MAX) + log_errx(1, "too many keys received"); + + pair_len = strlen(pair); + + keys->keys_values[i] = pair; + keys->keys_names[i] = strsep(&keys->keys_values[i], "="); + if (keys->keys_names[i] == NULL || keys->keys_values[i] == NULL) + log_errx(1, "malformed keys"); + log_debugx("key received: \"%s=%s\"", + keys->keys_names[i], keys->keys_values[i]); + + pair += pair_len + 1; /* +1 to skip the terminating '\0'. */ + if (pair == keys->keys_data + keys->keys_data_len) + break; + assert(pair < keys->keys_data + keys->keys_data_len); + } +} + +void +keys_save(struct keys *keys, struct pdu *pdu) +{ + char *data; + size_t len; + int i; + + /* + * XXX: Not particularly efficient. + */ + len = 0; + for (i = 0; i < KEYS_MAX; i++) { + if (keys->keys_names[i] == NULL) + break; + /* + * +1 for '=', +1 for '\0'. + */ + len += strlen(keys->keys_names[i]) + + strlen(keys->keys_values[i]) + 2; + } + + if (len == 0) + return; + + data = malloc(len); + if (data == NULL) + log_err(1, "malloc"); + + pdu->pdu_data = data; + pdu->pdu_data_len = len; + + for (i = 0; i < KEYS_MAX; i++) { + if (keys->keys_names[i] == NULL) + break; + data += sprintf(data, "%s=%s", + keys->keys_names[i], keys->keys_values[i]); + data += 1; /* for '\0'. */ + } +} + +const char * +keys_find(struct keys *keys, const char *name) +{ + int i; + + /* + * Note that we don't handle duplicated key names here, + * as they are not supposed to happen in requests, and if they do, + * it's an initiator error. + */ + for (i = 0; i < KEYS_MAX; i++) { + if (keys->keys_names[i] == NULL) + return (NULL); + if (strcmp(keys->keys_names[i], name) == 0) + return (keys->keys_values[i]); + } + return (NULL); +} + +int +keys_find_int(struct keys *keys, const char *name) +{ + const char *str; + char *endptr; + int num; + + str = keys_find(keys, name); + if (str == NULL) + return (-1); + + num = strtoul(str, &endptr, 10); + if (*endptr != '\0') { + log_debugx("invalid numeric value \"%s\"", str); + return (-1); + } + + return (num); +} + +void +keys_add(struct keys *keys, const char *name, const char *value) +{ + int i; + + log_debugx("key to send: \"%s=%s\"", name, value); + + /* + * Note that we don't check for duplicates here, as they are perfectly + * fine in responses, e.g. the "TargetName" keys in discovery sesion + * response. + */ + for (i = 0; i < KEYS_MAX; i++) { + if (keys->keys_names[i] == NULL) { + keys->keys_names[i] = checked_strdup(name); + keys->keys_values[i] = checked_strdup(value); + return; + } + } + log_errx(1, "too many keys"); +} + +void +keys_add_int(struct keys *keys, const char *name, int value) +{ + char *str; + int ret; + + ret = asprintf(&str, "%d", value); + if (ret <= 0) + log_err(1, "asprintf"); + + keys_add(keys, name, str); + free(str); +} diff --git a/usr.sbin/ctld/log.c b/usr.sbin/ctld/log.c new file mode 100644 index 00000000000..7b8ba71be5f --- /dev/null +++ b/usr.sbin/ctld/log.c @@ -0,0 +1,196 @@ +/*- + * Copyright (c) 2012 The FreeBSD Foundation + * All rights reserved. + * + * This software was developed by Edward Tomasz Napierala under sponsorship + * from the FreeBSD Foundation. + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + * $FreeBSD$ + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "ctld.h" + +static int log_level = 0; +static char *peer_name = NULL; +static char *peer_addr = NULL; + +#define MSGBUF_LEN 1024 + +void +log_init(int level) +{ + + log_level = level; + openlog(getprogname(), LOG_NDELAY | LOG_PID, LOG_DAEMON); +} + +void +log_set_peer_name(const char *name) +{ + + /* + * XXX: Turn it into assertion? + */ + if (peer_name != NULL) + log_errx(1, "%s called twice", __func__); + if (peer_addr == NULL) + log_errx(1, "%s called before log_set_peer_addr", __func__); + + peer_name = checked_strdup(name); +} + +void +log_set_peer_addr(const char *addr) +{ + + /* + * XXX: Turn it into assertion? + */ + if (peer_addr != NULL) + log_errx(1, "%s called twice", __func__); + + peer_addr = checked_strdup(addr); +} + +static void +log_common(int priority, int log_errno, const char *fmt, va_list ap) +{ + static char msgbuf[MSGBUF_LEN]; + static char msgbuf_strvised[MSGBUF_LEN * 4 + 1]; + int ret; + + ret = vsnprintf(msgbuf, sizeof(msgbuf), fmt, ap); + if (ret < 0) { + fprintf(stderr, "%s: snprintf failed", getprogname()); + syslog(LOG_CRIT, "snprintf failed"); + exit(1); + } + + ret = strnvis(msgbuf_strvised, sizeof(msgbuf_strvised), msgbuf, VIS_NL); + if (ret < 0) { + fprintf(stderr, "%s: strnvis failed", getprogname()); + syslog(LOG_CRIT, "strnvis failed"); + exit(1); + } + + if (log_errno == -1) { + if (peer_name != NULL) { + fprintf(stderr, "%s: %s (%s): %s\n", getprogname(), + peer_addr, peer_name, msgbuf_strvised); + syslog(priority, "%s (%s): %s", + peer_addr, peer_name, msgbuf_strvised); + } else if (peer_addr != NULL) { + fprintf(stderr, "%s: %s: %s\n", getprogname(), + peer_addr, msgbuf_strvised); + syslog(priority, "%s: %s", + peer_addr, msgbuf_strvised); + } else { + fprintf(stderr, "%s: %s\n", getprogname(), msgbuf_strvised); + syslog(priority, "%s", msgbuf_strvised); + } + + } else { + if (peer_name != NULL) { + fprintf(stderr, "%s: %s (%s): %s: %s\n", getprogname(), + peer_addr, peer_name, msgbuf_strvised, strerror(errno)); + syslog(priority, "%s (%s): %s: %s", + peer_addr, peer_name, msgbuf_strvised, strerror(errno)); + } else if (peer_addr != NULL) { + fprintf(stderr, "%s: %s: %s: %s\n", getprogname(), + peer_addr, msgbuf_strvised, strerror(errno)); + syslog(priority, "%s: %s: %s", + peer_addr, msgbuf_strvised, strerror(errno)); + } else { + fprintf(stderr, "%s: %s: %s\n", getprogname(), + msgbuf_strvised, strerror(errno)); + syslog(priority, "%s: %s", + msgbuf_strvised, strerror(errno)); + } + } +} + +void +log_err(int eval, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + log_common(LOG_CRIT, errno, fmt, ap); + va_end(ap); + + exit(eval); +} + +void +log_errx(int eval, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + log_common(LOG_CRIT, -1, fmt, ap); + va_end(ap); + + exit(eval); +} + +void +log_warn(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + log_common(LOG_WARNING, errno, fmt, ap); + va_end(ap); +} + +void +log_warnx(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + log_common(LOG_WARNING, -1, fmt, ap); + va_end(ap); +} + +void +log_debugx(const char *fmt, ...) +{ + va_list ap; + + if (log_level == 0) + return; + + va_start(ap, fmt); + log_common(LOG_DEBUG, -1, fmt, ap); + va_end(ap); +} diff --git a/usr.sbin/ctld/login.c b/usr.sbin/ctld/login.c new file mode 100644 index 00000000000..4ee7c9798e2 --- /dev/null +++ b/usr.sbin/ctld/login.c @@ -0,0 +1,1051 @@ +/*- + * Copyright (c) 2012 The FreeBSD Foundation + * All rights reserved. + * + * This software was developed by Edward Tomasz Napierala under sponsorship + * from the FreeBSD Foundation. + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + * $FreeBSD$ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ctld.h" +#include "iscsi_proto.h" + +static void login_send_error(struct pdu *request, + char class, char detail); + +static void +login_set_nsg(struct pdu *response, int nsg) +{ + struct iscsi_bhs_login_response *bhslr; + + assert(nsg == BHSLR_STAGE_SECURITY_NEGOTIATION || + nsg == BHSLR_STAGE_OPERATIONAL_NEGOTIATION || + nsg == BHSLR_STAGE_FULL_FEATURE_PHASE); + + bhslr = (struct iscsi_bhs_login_response *)response->pdu_bhs; + + bhslr->bhslr_flags &= 0xFC; + bhslr->bhslr_flags |= nsg; +} + +static int +login_csg(const struct pdu *request) +{ + struct iscsi_bhs_login_request *bhslr; + + bhslr = (struct iscsi_bhs_login_request *)request->pdu_bhs; + + return ((bhslr->bhslr_flags & 0x0C) >> 2); +} + +static void +login_set_csg(struct pdu *response, int csg) +{ + struct iscsi_bhs_login_response *bhslr; + + assert(csg == BHSLR_STAGE_SECURITY_NEGOTIATION || + csg == BHSLR_STAGE_OPERATIONAL_NEGOTIATION || + csg == BHSLR_STAGE_FULL_FEATURE_PHASE); + + bhslr = (struct iscsi_bhs_login_response *)response->pdu_bhs; + + bhslr->bhslr_flags &= 0xF3; + bhslr->bhslr_flags |= csg << 2; +} + +static struct pdu * +login_receive(struct connection *conn, bool initial) +{ + struct pdu *request; + struct iscsi_bhs_login_request *bhslr; + + request = pdu_new(conn); + pdu_receive(request); + if ((request->pdu_bhs->bhs_opcode & ~ISCSI_BHS_OPCODE_IMMEDIATE) != + ISCSI_BHS_OPCODE_LOGIN_REQUEST) { + /* + * The first PDU in session is special - if we receive any PDU + * different than login request, we have to drop the connection + * without sending response ("A target receiving any PDU + * except a Login request before the Login Phase is started MUST + * immediately terminate the connection on which the PDU + * was received.") + */ + if (initial == false) + login_send_error(request, 0x02, 0x0b); + log_errx(1, "protocol error: received invalid opcode 0x%x", + request->pdu_bhs->bhs_opcode); + } + bhslr = (struct iscsi_bhs_login_request *)request->pdu_bhs; + /* + * XXX: Implement the C flag some day. + */ + if ((bhslr->bhslr_flags & BHSLR_FLAGS_CONTINUE) != 0) { + login_send_error(request, 0x03, 0x00); + log_errx(1, "received Login PDU with unsupported \"C\" flag"); + } + if (bhslr->bhslr_version_max != 0x00) { + login_send_error(request, 0x02, 0x05); + log_errx(1, "received Login PDU with unsupported " + "Version-max 0x%x", bhslr->bhslr_version_max); + } + if (bhslr->bhslr_version_min != 0x00) { + login_send_error(request, 0x02, 0x05); + log_errx(1, "received Login PDU with unsupported " + "Version-min 0x%x", bhslr->bhslr_version_min); + } + if (request->pdu_data_len == 0) { + login_send_error(request, 0x02, 0x00); + log_errx(1, "received Login PDU with empty data segment"); + } + if (ntohl(bhslr->bhslr_cmdsn) < conn->conn_cmdsn) { + login_send_error(request, 0x02, 0x05); + log_errx(1, "received Login PDU with decreasing CmdSN: " + "was %d, is %d", conn->conn_cmdsn, + ntohl(bhslr->bhslr_cmdsn)); + } + if (initial == false && + ntohl(bhslr->bhslr_expstatsn) != conn->conn_statsn) { + login_send_error(request, 0x02, 0x05); + log_errx(1, "received Login PDU with wrong ExpStatSN: " + "is %d, should be %d", ntohl(bhslr->bhslr_expstatsn), + conn->conn_statsn); + } + conn->conn_cmdsn = ntohl(bhslr->bhslr_cmdsn); + + return (request); +} + +static struct pdu * +login_new_response(struct pdu *request) +{ + struct pdu *response; + struct connection *conn; + struct iscsi_bhs_login_request *bhslr; + struct iscsi_bhs_login_response *bhslr2; + + bhslr = (struct iscsi_bhs_login_request *)request->pdu_bhs; + conn = request->pdu_connection; + + response = pdu_new_response(request); + bhslr2 = (struct iscsi_bhs_login_response *)response->pdu_bhs; + bhslr2->bhslr_opcode = ISCSI_BHS_OPCODE_LOGIN_RESPONSE; + login_set_csg(response, BHSLR_STAGE_SECURITY_NEGOTIATION); + memcpy(bhslr2->bhslr_isid, + bhslr->bhslr_isid, sizeof(bhslr2->bhslr_isid)); + bhslr2->bhslr_initiator_task_tag = bhslr->bhslr_initiator_task_tag; + bhslr2->bhslr_statsn = htonl(conn->conn_statsn++); + bhslr2->bhslr_expcmdsn = htonl(conn->conn_cmdsn); + bhslr2->bhslr_maxcmdsn = htonl(conn->conn_cmdsn); + + return (response); +} + +static void +login_send_error(struct pdu *request, char class, char detail) +{ + struct pdu *response; + struct iscsi_bhs_login_response *bhslr2; + + log_debugx("sending Login Response PDU with failure class 0x%x/0x%x; " + "see next line for reason", class, detail); + response = login_new_response(request); + bhslr2 = (struct iscsi_bhs_login_response *)response->pdu_bhs; + bhslr2->bhslr_status_class = class; + bhslr2->bhslr_status_detail = detail; + + pdu_send(response); + pdu_delete(response); +} + +static int +login_list_contains(const char *list, const char *what) +{ + char *tofree, *str, *token; + + tofree = str = checked_strdup(list); + + while ((token = strsep(&str, ",")) != NULL) { + if (strcmp(token, what) == 0) { + free(tofree); + return (1); + } + } + free(tofree); + return (0); +} + +static int +login_list_prefers(const char *list, + const char *choice1, const char *choice2) +{ + char *tofree, *str, *token; + + tofree = str = checked_strdup(list); + + while ((token = strsep(&str, ",")) != NULL) { + if (strcmp(token, choice1) == 0) { + free(tofree); + return (1); + } + if (strcmp(token, choice2) == 0) { + free(tofree); + return (2); + } + } + free(tofree); + return (-1); +} + +static int +login_hex2int(const char hex) +{ + switch (hex) { + case '0': + return (0x00); + case '1': + return (0x01); + case '2': + return (0x02); + case '3': + return (0x03); + case '4': + return (0x04); + case '5': + return (0x05); + case '6': + return (0x06); + case '7': + return (0x07); + case '8': + return (0x08); + case '9': + return (0x09); + case 'a': + case 'A': + return (0x0a); + case 'b': + case 'B': + return (0x0b); + case 'c': + case 'C': + return (0x0c); + case 'd': + case 'D': + return (0x0d); + case 'e': + case 'E': + return (0x0e); + case 'f': + case 'F': + return (0x0f); + default: + return (-1); + } +} + +/* + * XXX: Review this _carefully_. + */ +static int +login_hex2bin(const char *hex, char **binp, size_t *bin_lenp) +{ + int i, hex_len, nibble; + bool lo = true; /* As opposed to 'hi'. */ + char *bin; + size_t bin_off, bin_len; + + if (strncasecmp(hex, "0x", strlen("0x")) != 0) { + log_warnx("malformed variable, should start with \"0x\""); + return (-1); + } + + hex += strlen("0x"); + hex_len = strlen(hex); + if (hex_len < 1) { + log_warnx("malformed variable; doesn't contain anything " + "but \"0x\""); + return (-1); + } + + bin_len = hex_len / 2 + hex_len % 2; + bin = calloc(bin_len, 1); + if (bin == NULL) + log_err(1, "calloc"); + + bin_off = bin_len - 1; + for (i = hex_len - 1; i >= 0; i--) { + nibble = login_hex2int(hex[i]); + if (nibble < 0) { + log_warnx("malformed variable, invalid char \"%c\"", + hex[i]); + return (-1); + } + + assert(bin_off < bin_len); + if (lo) { + bin[bin_off] = nibble; + lo = false; + } else { + bin[bin_off] |= nibble << 4; + bin_off--; + lo = true; + } + } + + *binp = bin; + *bin_lenp = bin_len; + return (0); +} + +static char * +login_bin2hex(const char *bin, size_t bin_len) +{ + unsigned char *hex, *tmp, ch; + size_t hex_len; + size_t i; + + hex_len = bin_len * 2 + 3; /* +2 for "0x", +1 for '\0'. */ + hex = malloc(hex_len); + if (hex == NULL) + log_err(1, "malloc"); + + tmp = hex; + tmp += sprintf(tmp, "0x"); + for (i = 0; i < bin_len; i++) { + ch = bin[i]; + tmp += sprintf(tmp, "%02x", ch); + } + + return (hex); +} + +static void +login_compute_md5(const char id, const char *secret, + const void *challenge, size_t challenge_len, void *response, + size_t response_len) +{ + MD5_CTX ctx; + int rv; + + assert(response_len == MD5_DIGEST_LENGTH); + + MD5_Init(&ctx); + MD5_Update(&ctx, &id, sizeof(id)); + MD5_Update(&ctx, secret, strlen(secret)); + MD5_Update(&ctx, challenge, challenge_len); + rv = MD5_Final(response, &ctx); + if (rv != 1) + log_errx(1, "MD5_Final"); +} + +#define LOGIN_CHALLENGE_LEN 1024 + +static struct pdu * +login_receive_chap_a(struct connection *conn) +{ + struct pdu *request; + struct keys *request_keys; + const char *chap_a; + + request = login_receive(conn, false); + request_keys = keys_new(); + keys_load(request_keys, request); + + chap_a = keys_find(request_keys, "CHAP_A"); + if (chap_a == NULL) { + login_send_error(request, 0x02, 0x07); + log_errx(1, "received CHAP Login PDU without CHAP_A"); + } + if (login_list_contains(chap_a, "5") == 0) { + login_send_error(request, 0x02, 0x01); + log_errx(1, "received CHAP Login PDU with unsupported CHAP_A " + "\"%s\"", chap_a); + } + keys_delete(request_keys); + + return (request); +} + +static void +login_send_chap_c(struct pdu *request, const unsigned char id, + const void *challenge, const size_t challenge_len) +{ + struct pdu *response; + struct keys *response_keys; + char *chap_c, chap_i[4]; + + chap_c = login_bin2hex(challenge, challenge_len); + snprintf(chap_i, sizeof(chap_i), "%d", id); + + response = login_new_response(request); + response_keys = keys_new(); + keys_add(response_keys, "CHAP_A", "5"); + keys_add(response_keys, "CHAP_I", chap_i); + keys_add(response_keys, "CHAP_C", chap_c); + free(chap_c); + keys_save(response_keys, response); + keys_delete(response_keys); + pdu_send(response); +} + +static struct pdu * +login_receive_chap_r(struct connection *conn, + struct auth_group *ag, const unsigned char id, const void *challenge, + const size_t challenge_len, const struct auth **cap) +{ + struct pdu *request; + struct keys *request_keys; + const char *chap_n, *chap_r; + char *response_bin, expected_response_bin[MD5_DIGEST_LENGTH]; + size_t response_bin_len; + const struct auth *auth; + int error; + + request = login_receive(conn, false); + request_keys = keys_new(); + keys_load(request_keys, request); + + chap_n = keys_find(request_keys, "CHAP_N"); + if (chap_n == NULL) { + login_send_error(request, 0x02, 0x07); + log_errx(1, "received CHAP Login PDU without CHAP_N"); + } + chap_r = keys_find(request_keys, "CHAP_R"); + if (chap_r == NULL) { + login_send_error(request, 0x02, 0x07); + log_errx(1, "received CHAP Login PDU without CHAP_R"); + } + error = login_hex2bin(chap_r, &response_bin, &response_bin_len); + if (error != 0) { + login_send_error(request, 0x02, 0x07); + log_errx(1, "received CHAP Login PDU with malformed CHAP_R"); + } + + /* + * Verify the response. + */ + assert(ag->ag_type == AG_TYPE_CHAP || + ag->ag_type == AG_TYPE_CHAP_MUTUAL); + auth = auth_find(ag, chap_n); + if (auth == NULL) { + login_send_error(request, 0x02, 0x01); + log_errx(1, "received CHAP Login with invalid user \"%s\"", + chap_n); + } + + assert(auth->a_secret != NULL); + assert(strlen(auth->a_secret) > 0); + login_compute_md5(id, auth->a_secret, challenge, + challenge_len, expected_response_bin, + sizeof(expected_response_bin)); + + if (memcmp(response_bin, expected_response_bin, + sizeof(expected_response_bin)) != 0) { + login_send_error(request, 0x02, 0x01); + log_errx(1, "CHAP authentication failed for user \"%s\"", + auth->a_user); + } + + keys_delete(request_keys); + free(response_bin); + + *cap = auth; + return (request); +} + +static void +login_send_chap_success(struct pdu *request, + const struct auth *auth) +{ + struct pdu *response; + struct keys *request_keys, *response_keys; + struct iscsi_bhs_login_response *bhslr2; + const char *chap_i, *chap_c; + char *chap_r, *challenge, response_bin[MD5_DIGEST_LENGTH]; + size_t challenge_len; + unsigned char id; + int error; + + response = login_new_response(request); + bhslr2 = (struct iscsi_bhs_login_response *)response->pdu_bhs; + bhslr2->bhslr_flags |= BHSLR_FLAGS_TRANSIT; + login_set_nsg(response, BHSLR_STAGE_OPERATIONAL_NEGOTIATION); + + /* + * Actually, one more thing: mutual authentication. + */ + request_keys = keys_new(); + keys_load(request_keys, request); + chap_i = keys_find(request_keys, "CHAP_I"); + chap_c = keys_find(request_keys, "CHAP_C"); + if (chap_i != NULL || chap_c != NULL) { + if (chap_i == NULL) { + login_send_error(request, 0x02, 0x07); + log_errx(1, "initiator requested target " + "authentication, but didn't send CHAP_I"); + } + if (chap_c == NULL) { + login_send_error(request, 0x02, 0x07); + log_errx(1, "initiator requested target " + "authentication, but didn't send CHAP_C"); + } + if (auth->a_auth_group->ag_type != AG_TYPE_CHAP_MUTUAL) { + login_send_error(request, 0x02, 0x01); + log_errx(1, "initiator requests target authentication " + "for user \"%s\", but mutual user/secret " + "is not set", auth->a_user); + } + + id = strtoul(chap_i, NULL, 10); + error = login_hex2bin(chap_c, &challenge, &challenge_len); + if (error != 0) { + login_send_error(request, 0x02, 0x07); + log_errx(1, "received CHAP Login PDU with malformed " + "CHAP_C"); + } + + log_debugx("performing mutual authentication as user \"%s\"", + auth->a_mutual_user); + login_compute_md5(id, auth->a_mutual_secret, challenge, + challenge_len, response_bin, sizeof(response_bin)); + + chap_r = login_bin2hex(response_bin, + sizeof(response_bin)); + response_keys = keys_new(); + keys_add(response_keys, "CHAP_N", auth->a_mutual_user); + keys_add(response_keys, "CHAP_R", chap_r); + free(chap_r); + keys_save(response_keys, response); + keys_delete(response_keys); + } else { + log_debugx("initiator did not request target authentication"); + } + + keys_delete(request_keys); + pdu_send(response); +} + +static void +login_chap(struct connection *conn, struct auth_group *ag) +{ + const struct auth *auth; + struct pdu *request; + char challenge_bin[LOGIN_CHALLENGE_LEN]; + unsigned char id; + int rv; + + /* + * Receive CHAP_A PDU. + */ + log_debugx("beginning CHAP authentication; waiting for CHAP_A"); + request = login_receive_chap_a(conn); + + /* + * Generate the challenge. + */ + rv = RAND_bytes(challenge_bin, sizeof(challenge_bin)); + if (rv != 1) { + login_send_error(request, 0x03, 0x02); + log_errx(1, "RAND_bytes failed: %s", + ERR_error_string(ERR_get_error(), NULL)); + } + rv = RAND_bytes(&id, sizeof(id)); + if (rv != 1) { + login_send_error(request, 0x03, 0x02); + log_errx(1, "RAND_bytes failed: %s", + ERR_error_string(ERR_get_error(), NULL)); + } + + /* + * Send the challenge. + */ + log_debugx("sending CHAP_C, binary challenge size is %zd bytes", + sizeof(challenge_bin)); + login_send_chap_c(request, id, challenge_bin, + sizeof(challenge_bin)); + pdu_delete(request); + + /* + * Receive CHAP_N/CHAP_R PDU and authenticate. + */ + log_debugx("waiting for CHAP_N/CHAP_R"); + request = login_receive_chap_r(conn, ag, id, challenge_bin, + sizeof(challenge_bin), &auth); + + /* + * Yay, authentication succeeded! + */ + log_debugx("authentication succeeded for user \"%s\"; " + "transitioning to Negotiation Phase", auth->a_user); + login_send_chap_success(request, auth); + pdu_delete(request); +} + +static void +login_negotiate_key(struct pdu *request, const char *name, + const char *value, bool skipped_security, struct keys *response_keys) +{ + int which, tmp; + struct connection *conn; + + conn = request->pdu_connection; + + if (strcmp(name, "InitiatorName") == 0) { + if (!skipped_security) + log_errx(1, "initiator resent InitiatorName"); + } else if (strcmp(name, "SessionType") == 0) { + if (!skipped_security) + log_errx(1, "initiator resent SessionType"); + } else if (strcmp(name, "TargetName") == 0) { + if (!skipped_security) + log_errx(1, "initiator resent TargetName"); + } else if (strcmp(name, "InitiatorAlias") == 0) { + if (conn->conn_initiator_alias != NULL) + free(conn->conn_initiator_alias); + conn->conn_initiator_alias = checked_strdup(value); + } else if (strcmp(value, "Irrelevant") == 0) { + /* Ignore. */ + } else if (strcmp(name, "HeaderDigest") == 0) { + /* + * We don't handle digests for discovery sessions. + */ + if (conn->conn_session_type == CONN_SESSION_TYPE_DISCOVERY) { + log_debugx("discovery session; digests disabled"); + keys_add(response_keys, name, "None"); + return; + } + + which = login_list_prefers(value, "CRC32C", "None"); + switch (which) { + case 1: + log_debugx("initiator prefers CRC32C " + "for header digest; we'll use it"); + conn->conn_header_digest = CONN_DIGEST_CRC32C; + keys_add(response_keys, name, "CRC32C"); + break; + case 2: + log_debugx("initiator prefers not to do " + "header digest; we'll comply"); + keys_add(response_keys, name, "None"); + break; + default: + log_warnx("initiator sent unrecognized " + "HeaderDigest value \"%s\"; will use None", value); + keys_add(response_keys, name, "None"); + break; + } + } else if (strcmp(name, "DataDigest") == 0) { + if (conn->conn_session_type == CONN_SESSION_TYPE_DISCOVERY) { + log_debugx("discovery session; digests disabled"); + keys_add(response_keys, name, "None"); + return; + } + + which = login_list_prefers(value, "CRC32C", "None"); + switch (which) { + case 1: + log_debugx("initiator prefers CRC32C " + "for data digest; we'll use it"); + conn->conn_data_digest = CONN_DIGEST_CRC32C; + keys_add(response_keys, name, "CRC32C"); + break; + case 2: + log_debugx("initiator prefers not to do " + "data digest; we'll comply"); + keys_add(response_keys, name, "None"); + break; + default: + log_warnx("initiator sent unrecognized " + "DataDigest value \"%s\"; will use None", value); + keys_add(response_keys, name, "None"); + break; + } + } else if (strcmp(name, "MaxConnections") == 0) { + keys_add(response_keys, name, "1"); + } else if (strcmp(name, "InitialR2T") == 0) { + keys_add(response_keys, name, "Yes"); + } else if (strcmp(name, "ImmediateData") == 0) { + if (conn->conn_session_type == CONN_SESSION_TYPE_DISCOVERY) { + log_debugx("discovery session; ImmediateData irrelevant"); + keys_add(response_keys, name, "Irrelevant"); + } else { + if (strcmp(value, "Yes") == 0) { + conn->conn_immediate_data = true; + keys_add(response_keys, name, "Yes"); + } else { + conn->conn_immediate_data = false; + keys_add(response_keys, name, "No"); + } + } + } else if (strcmp(name, "MaxRecvDataSegmentLength") == 0) { + tmp = strtoul(value, NULL, 10); + if (tmp <= 0) { + login_send_error(request, 0x02, 0x00); + log_errx(1, "received invalid " + "MaxRecvDataSegmentLength"); + } + if (tmp > MAX_DATA_SEGMENT_LENGTH) { + log_debugx("capping MaxDataSegmentLength from %d to %d", + tmp, MAX_DATA_SEGMENT_LENGTH); + tmp = MAX_DATA_SEGMENT_LENGTH; + } + conn->conn_max_data_segment_length = tmp; + keys_add_int(response_keys, name, tmp); + } else if (strcmp(name, "MaxBurstLength") == 0) { + tmp = strtoul(value, NULL, 10); + if (tmp <= 0) { + login_send_error(request, 0x02, 0x00); + log_errx(1, "received invalid MaxBurstLength"); + } + if (tmp > MAX_BURST_LENGTH) { + log_debugx("capping MaxBurstLength from %d to %d", + tmp, MAX_BURST_LENGTH); + tmp = MAX_BURST_LENGTH; + } + conn->conn_max_burst_length = tmp; + keys_add(response_keys, name, value); + } else if (strcmp(name, "FirstBurstLength") == 0) { + tmp = strtoul(value, NULL, 10); + if (tmp <= 0) { + login_send_error(request, 0x02, 0x00); + log_errx(1, "received invalid " + "FirstBurstLength"); + } + if (tmp > MAX_DATA_SEGMENT_LENGTH) { + log_debugx("capping FirstBurstLength from %d to %d", + tmp, MAX_DATA_SEGMENT_LENGTH); + tmp = MAX_DATA_SEGMENT_LENGTH; + } + /* + * We don't pass the value to the kernel; it only enforces + * hardcoded limit anyway. + */ + keys_add_int(response_keys, name, tmp); + } else if (strcmp(name, "DefaultTime2Wait") == 0) { + keys_add(response_keys, name, value); + } else if (strcmp(name, "DefaultTime2Retain") == 0) { + keys_add(response_keys, name, "0"); + } else if (strcmp(name, "MaxOutstandingR2T") == 0) { + keys_add(response_keys, name, "1"); + } else if (strcmp(name, "DataPDUInOrder") == 0) { + keys_add(response_keys, name, "Yes"); + } else if (strcmp(name, "DataSequenceInOrder") == 0) { + keys_add(response_keys, name, "Yes"); + } else if (strcmp(name, "ErrorRecoveryLevel") == 0) { + keys_add(response_keys, name, "0"); + } else if (strcmp(name, "OFMarker") == 0) { + keys_add(response_keys, name, "No"); + } else if (strcmp(name, "IFMarker") == 0) { + keys_add(response_keys, name, "No"); + } else { + log_debugx("unknown key \"%s\"; responding " + "with NotUnderstood", name); + keys_add(response_keys, name, "NotUnderstood"); + } +} + +static void +login_negotiate(struct connection *conn, struct pdu *request) +{ + struct pdu *response; + struct iscsi_bhs_login_response *bhslr2; + struct keys *request_keys, *response_keys; + int i; + bool skipped_security; + + if (request == NULL) { + log_debugx("beginning parameter negotiation; " + "waiting for Login PDU"); + request = login_receive(conn, false); + skipped_security = false; + } else + skipped_security = true; + + request_keys = keys_new(); + keys_load(request_keys, request); + + response = login_new_response(request); + bhslr2 = (struct iscsi_bhs_login_response *)response->pdu_bhs; + bhslr2->bhslr_flags |= BHSLR_FLAGS_TRANSIT; + bhslr2->bhslr_tsih = htons(0xbadd); + login_set_csg(response, BHSLR_STAGE_OPERATIONAL_NEGOTIATION); + login_set_nsg(response, BHSLR_STAGE_FULL_FEATURE_PHASE); + response_keys = keys_new(); + for (i = 0; i < KEYS_MAX; i++) { + if (request_keys->keys_names[i] == NULL) + break; + + login_negotiate_key(request, request_keys->keys_names[i], + request_keys->keys_values[i], skipped_security, + response_keys); + } + + log_debugx("parameter negotiation done; " + "transitioning to Full Feature Phase"); + + keys_save(response_keys, response); + pdu_send(response); + pdu_delete(response); + keys_delete(response_keys); + pdu_delete(request); + keys_delete(request_keys); +} + +void +login(struct connection *conn) +{ + struct pdu *request, *response; + struct iscsi_bhs_login_request *bhslr; + struct iscsi_bhs_login_response *bhslr2; + struct keys *request_keys, *response_keys; + struct auth_group *ag; + const char *initiator_name, *initiator_alias, *session_type, + *target_name, *auth_method; + char *portal_group_tag; + int rv; + + /* + * Handle the initial Login Request - figure out required authentication + * method and either transition to the next phase, if no authentication + * is required, or call appropriate authentication code. + */ + log_debugx("beginning Login Phase; waiting for Login PDU"); + request = login_receive(conn, true); + bhslr = (struct iscsi_bhs_login_request *)request->pdu_bhs; + if (bhslr->bhslr_tsih != 0) { + login_send_error(request, 0x02, 0x0a); + log_errx(1, "received Login PDU with non-zero TSIH"); + } + + /* + * XXX: Implement the C flag some day. + */ + request_keys = keys_new(); + keys_load(request_keys, request); + + assert(conn->conn_initiator_name == NULL); + initiator_name = keys_find(request_keys, "InitiatorName"); + if (initiator_name == NULL) { + login_send_error(request, 0x02, 0x07); + log_errx(1, "received Login PDU without InitiatorName"); + } + if (valid_iscsi_name(initiator_name) == false) { + login_send_error(request, 0x02, 0x00); + log_errx(1, "received Login PDU with invalid InitiatorName"); + } + conn->conn_initiator_name = checked_strdup(initiator_name); + log_set_peer_name(conn->conn_initiator_name); + /* + * XXX: This doesn't work (does nothing) because of Capsicum. + */ + setproctitle("%s (%s)", conn->conn_initiator_addr, conn->conn_initiator_name); + + initiator_alias = keys_find(request_keys, "InitiatorAlias"); + if (initiator_alias != NULL) + conn->conn_initiator_alias = checked_strdup(initiator_alias); + + assert(conn->conn_session_type == CONN_SESSION_TYPE_NONE); + session_type = keys_find(request_keys, "SessionType"); + if (session_type != NULL) { + if (strcmp(session_type, "Normal") == 0) { + conn->conn_session_type = CONN_SESSION_TYPE_NORMAL; + } else if (strcmp(session_type, "Discovery") == 0) { + conn->conn_session_type = CONN_SESSION_TYPE_DISCOVERY; + } else { + login_send_error(request, 0x02, 0x00); + log_errx(1, "received Login PDU with invalid " + "SessionType \"%s\"", session_type); + } + } else + conn->conn_session_type = CONN_SESSION_TYPE_NORMAL; + + assert(conn->conn_target == NULL); + if (conn->conn_session_type == CONN_SESSION_TYPE_NORMAL) { + target_name = keys_find(request_keys, "TargetName"); + if (target_name == NULL) { + login_send_error(request, 0x02, 0x07); + log_errx(1, "received Login PDU without TargetName"); + } + + conn->conn_target = + target_find(conn->conn_portal->p_portal_group->pg_conf, + target_name); + if (conn->conn_target == NULL) { + login_send_error(request, 0x02, 0x03); + log_errx(1, "requested target \"%s\" not found", + target_name); + } + } + + /* + * At this point we know what kind of authentication we need. + */ + if (conn->conn_session_type == CONN_SESSION_TYPE_NORMAL) { + ag = conn->conn_target->t_auth_group; + if (ag->ag_name != NULL) { + log_debugx("initiator requests to connect " + "to target \"%s\"; auth-group \"%s\"", + conn->conn_target->t_iqn, + conn->conn_target->t_auth_group->ag_name); + } else { + log_debugx("initiator requests to connect " + "to target \"%s\"", conn->conn_target->t_iqn); + } + } else { + assert(conn->conn_session_type == CONN_SESSION_TYPE_DISCOVERY); + ag = conn->conn_portal->p_portal_group->pg_discovery_auth_group; + if (ag->ag_name != NULL) { + log_debugx("initiator requests " + "discovery session; auth-group \"%s\"", ag->ag_name); + } else { + log_debugx("initiator requests discovery session"); + } + } + + /* + * Let's see if the initiator intends to do any kind of authentication + * at all. + */ + if (login_csg(request) == BHSLR_STAGE_OPERATIONAL_NEGOTIATION) { + if (ag->ag_type != AG_TYPE_NO_AUTHENTICATION) { + login_send_error(request, 0x02, 0x01); + log_errx(1, "initiator skipped the authentication, " + "but authentication is required"); + } + + keys_delete(request_keys); + + log_debugx("initiator skipped the authentication, " + "and we don't need it; proceeding with negotiation"); + login_negotiate(conn, request); + return; + } + + if (ag->ag_type == AG_TYPE_NO_AUTHENTICATION) { + /* + * Initiator might want to to authenticate, + * but we don't need it. + */ + log_debugx("authentication not required; " + "transitioning to parameter negotiation"); + + if ((bhslr->bhslr_flags & BHSLR_FLAGS_TRANSIT) == 0) + log_warnx("initiator did not set the \"T\" flag; " + "transitioning anyway"); + + response = login_new_response(request); + bhslr2 = (struct iscsi_bhs_login_response *)response->pdu_bhs; + bhslr2->bhslr_flags |= BHSLR_FLAGS_TRANSIT; + login_set_nsg(response, + BHSLR_STAGE_OPERATIONAL_NEGOTIATION); + response_keys = keys_new(); + /* + * Required by Linux initiator. + */ + auth_method = keys_find(request_keys, "AuthMethod"); + if (auth_method != NULL && + login_list_contains(auth_method, "None")) + keys_add(response_keys, "AuthMethod", "None"); + + if (conn->conn_session_type == CONN_SESSION_TYPE_NORMAL) { + if (conn->conn_target->t_alias != NULL) + keys_add(response_keys, + "TargetAlias", conn->conn_target->t_alias); + rv = asprintf(&portal_group_tag, "%d", + conn->conn_portal->p_portal_group->pg_tag); + if (rv <= 0) + log_err(1, "asprintf"); + keys_add(response_keys, + "TargetPortalGroupTag", portal_group_tag); + free(portal_group_tag); + } + keys_save(response_keys, response); + pdu_send(response); + pdu_delete(response); + keys_delete(response_keys); + pdu_delete(request); + keys_delete(request_keys); + + login_negotiate(conn, NULL); + return; + } + + log_debugx("CHAP authentication required"); + + auth_method = keys_find(request_keys, "AuthMethod"); + if (auth_method == NULL) { + login_send_error(request, 0x02, 0x07); + log_errx(1, "received Login PDU without AuthMethod"); + } + /* + * XXX: This should be Reject, not just a login failure (5.3.2). + */ + if (login_list_contains(auth_method, "CHAP") == 0) { + login_send_error(request, 0x02, 0x01); + log_errx(1, "initiator requests unsupported AuthMethod \"%s\" " + "instead of \"CHAP\"", auth_method); + } + + response = login_new_response(request); + + response_keys = keys_new(); + keys_add(response_keys, "AuthMethod", "CHAP"); + if (conn->conn_session_type == CONN_SESSION_TYPE_NORMAL) { + rv = asprintf(&portal_group_tag, "%d", + conn->conn_portal->p_portal_group->pg_tag); + if (rv <= 0) + log_err(1, "asprintf"); + keys_add(response_keys, + "TargetPortalGroupTag", portal_group_tag); + free(portal_group_tag); + if (conn->conn_target->t_alias != NULL) + keys_add(response_keys, + "TargetAlias", conn->conn_target->t_alias); + } + keys_save(response_keys, response); + + pdu_send(response); + pdu_delete(response); + keys_delete(response_keys); + pdu_delete(request); + keys_delete(request_keys); + + login_chap(conn, ag); + + login_negotiate(conn, NULL); +} diff --git a/usr.sbin/ctld/parse.y b/usr.sbin/ctld/parse.y new file mode 100644 index 00000000000..333e9642aa5 --- /dev/null +++ b/usr.sbin/ctld/parse.y @@ -0,0 +1,621 @@ +%{ +/*- + * Copyright (c) 2012 The FreeBSD Foundation + * All rights reserved. + * + * This software was developed by Edward Tomasz Napierala under sponsorship + * from the FreeBSD Foundation. + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + * $FreeBSD$ + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ctld.h" + +extern FILE *yyin; +extern char *yytext; +extern int lineno; + +static struct conf *conf = NULL; +static struct auth_group *auth_group = NULL; +static struct portal_group *portal_group = NULL; +static struct target *target = NULL; +static struct lun *lun = NULL; + +extern void yyerror(const char *); +extern int yylex(void); +extern void yyrestart(FILE *); + +%} + +%token ALIAS AUTH_GROUP BACKEND BLOCKSIZE CHAP CHAP_MUTUAL CLOSING_BRACKET +%token DEBUG DEVICE_ID DISCOVERY_AUTH_GROUP LISTEN LISTEN_ISER LUN MAXPROC NUM +%token OPENING_BRACKET OPTION PATH PIDFILE PORTAL_GROUP SERIAL SIZE STR TARGET +%token TIMEOUT + +%union +{ + uint64_t num; + char *str; +} + +%token NUM +%token STR + +%% + +statements: + | + statements statement + ; + +statement: + debug_statement + | + timeout_statement + | + maxproc_statement + | + pidfile_statement + | + auth_group_definition + | + portal_group_definition + | + target_statement + ; + +debug_statement: DEBUG NUM + { + conf->conf_debug = $2; + } + ; + +timeout_statement: TIMEOUT NUM + { + conf->conf_timeout = $2; + } + ; + +maxproc_statement: MAXPROC NUM + { + conf->conf_maxproc = $2; + } + ; + +pidfile_statement: PIDFILE STR + { + if (conf->conf_pidfile_path != NULL) { + log_warnx("pidfile specified more than once"); + free($2); + return (1); + } + conf->conf_pidfile_path = $2; + } + ; + +auth_group_definition: AUTH_GROUP auth_group_name + OPENING_BRACKET auth_group_entries CLOSING_BRACKET + { + auth_group = NULL; + } + ; + +auth_group_name: STR + { + auth_group = auth_group_new(conf, $1); + free($1); + if (auth_group == NULL) + return (1); + } + ; + +auth_group_entries: + | + auth_group_entries auth_group_entry + ; + +auth_group_entry: + auth_group_chap + | + auth_group_chap_mutual + ; + +auth_group_chap: CHAP STR STR + { + const struct auth *ca; + + ca = auth_new_chap(auth_group, $2, $3); + free($2); + free($3); + if (ca == NULL) + return (1); + } + ; + +auth_group_chap_mutual: CHAP_MUTUAL STR STR STR STR + { + const struct auth *ca; + + ca = auth_new_chap_mutual(auth_group, $2, $3, $4, $5); + free($2); + free($3); + free($4); + free($5); + if (ca == NULL) + return (1); + } + ; + +portal_group_definition: PORTAL_GROUP portal_group_name + OPENING_BRACKET portal_group_entries CLOSING_BRACKET + { + portal_group = NULL; + } + ; + +portal_group_name: STR + { + portal_group = portal_group_new(conf, $1); + free($1); + if (portal_group == NULL) + return (1); + } + ; + +portal_group_entries: + | + portal_group_entries portal_group_entry + ; + +portal_group_entry: + portal_group_discovery_auth_group + | + portal_group_listen + | + portal_group_listen_iser + ; + +portal_group_discovery_auth_group: DISCOVERY_AUTH_GROUP STR + { + if (portal_group->pg_discovery_auth_group != NULL) { + log_warnx("discovery-auth-group for portal-group " + "\"%s\" specified more than once", + portal_group->pg_name); + return (1); + } + portal_group->pg_discovery_auth_group = + auth_group_find(conf, $2); + if (portal_group->pg_discovery_auth_group == NULL) { + log_warnx("unknown discovery-auth-group \"%s\" " + "for portal-group \"%s\"", + $2, portal_group->pg_name); + return (1); + } + free($2); + } + ; + +portal_group_listen: LISTEN STR + { + int error; + + error = portal_group_add_listen(portal_group, $2, false); + free($2); + if (error != 0) + return (1); + } + ; + +portal_group_listen_iser: LISTEN_ISER STR + { + int error; + + error = portal_group_add_listen(portal_group, $2, true); + free($2); + if (error != 0) + return (1); + } + ; + +target_statement: TARGET target_iqn + OPENING_BRACKET target_entries CLOSING_BRACKET + { + target = NULL; + } + ; + +target_iqn: STR + { + target = target_new(conf, $1); + free($1); + if (target == NULL) + return (1); + } + ; + +target_entries: + | + target_entries target_entry + ; + +target_entry: + alias_statement + | + auth_group_statement + | + chap_statement + | + chap_mutual_statement + | + portal_group_statement + | + lun_statement + ; + +alias_statement: ALIAS STR + { + if (target->t_alias != NULL) { + log_warnx("alias for target \"%s\" " + "specified more than once", target->t_iqn); + return (1); + } + target->t_alias = $2; + } + ; + +auth_group_statement: AUTH_GROUP STR + { + if (target->t_auth_group != NULL) { + if (target->t_auth_group->ag_name != NULL) + log_warnx("auth-group for target \"%s\" " + "specified more than once", target->t_iqn); + else + log_warnx("cannot mix auth-grup with explicit " + "authorisations for target \"%s\"", + target->t_iqn); + return (1); + } + target->t_auth_group = auth_group_find(conf, $2); + if (target->t_auth_group == NULL) { + log_warnx("unknown auth-group \"%s\" for target " + "\"%s\"", $2, target->t_iqn); + return (1); + } + free($2); + } + ; + +chap_statement: CHAP STR STR + { + const struct auth *ca; + + if (target->t_auth_group != NULL) { + if (target->t_auth_group->ag_name != NULL) { + log_warnx("cannot mix auth-grup with explicit " + "authorisations for target \"%s\"", + target->t_iqn); + free($2); + free($3); + return (1); + } + } else { + target->t_auth_group = auth_group_new(conf, NULL); + if (target->t_auth_group == NULL) { + free($2); + free($3); + return (1); + } + target->t_auth_group->ag_target = target; + } + ca = auth_new_chap(target->t_auth_group, $2, $3); + free($2); + free($3); + if (ca == NULL) + return (1); + } + ; + +chap_mutual_statement: CHAP_MUTUAL STR STR STR STR + { + const struct auth *ca; + + if (target->t_auth_group != NULL) { + if (target->t_auth_group->ag_name != NULL) { + log_warnx("cannot mix auth-grup with explicit " + "authorisations for target \"%s\"", + target->t_iqn); + free($2); + free($3); + free($4); + free($5); + return (1); + } + } else { + target->t_auth_group = auth_group_new(conf, NULL); + if (target->t_auth_group == NULL) { + free($2); + free($3); + free($4); + free($5); + return (1); + } + target->t_auth_group->ag_target = target; + } + ca = auth_new_chap_mutual(target->t_auth_group, + $2, $3, $4, $5); + free($2); + free($3); + free($4); + free($5); + if (ca == NULL) + return (1); + } + ; + +portal_group_statement: PORTAL_GROUP STR + { + if (target->t_portal_group != NULL) { + log_warnx("portal-group for target \"%s\" " + "specified more than once", target->t_iqn); + free($2); + return (1); + } + target->t_portal_group = portal_group_find(conf, $2); + if (target->t_portal_group == NULL) { + log_warnx("unknown portal-group \"%s\" for target " + "\"%s\"", $2, target->t_iqn); + free($2); + return (1); + } + free($2); + } + ; + +lun_statement: LUN lun_number + OPENING_BRACKET lun_statement_entries CLOSING_BRACKET + { + lun = NULL; + } + ; + +lun_number: NUM + { + lun = lun_new(target, $1); + if (lun == NULL) + return (1); + } + ; + +lun_statement_entries: + | + lun_statement_entries lun_statement_entry + ; + +lun_statement_entry: + backend_statement + | + blocksize_statement + | + device_id_statement + | + option_statement + | + path_statement + | + serial_statement + | + size_statement + ; + +backend_statement: BACKEND STR + { + if (lun->l_backend != NULL) { + log_warnx("backend for lun %d, target \"%s\" " + "specified more than once", + lun->l_lun, target->t_iqn); + free($2); + return (1); + } + lun_set_backend(lun, $2); + free($2); + } + ; + +blocksize_statement: BLOCKSIZE NUM + { + if (lun->l_blocksize != 0) { + log_warnx("blocksize for lun %d, target \"%s\" " + "specified more than once", + lun->l_lun, target->t_iqn); + return (1); + } + lun_set_blocksize(lun, $2); + } + ; + +device_id_statement: DEVICE_ID STR + { + if (lun->l_device_id != NULL) { + log_warnx("device_id for lun %d, target \"%s\" " + "specified more than once", + lun->l_lun, target->t_iqn); + free($2); + return (1); + } + lun_set_device_id(lun, $2); + free($2); + } + ; + +option_statement: OPTION STR STR + { + struct lun_option *clo; + + clo = lun_option_new(lun, $2, $3); + free($2); + free($3); + if (clo == NULL) + return (1); + } + ; + +path_statement: PATH STR + { + if (lun->l_path != NULL) { + log_warnx("path for lun %d, target \"%s\" " + "specified more than once", + lun->l_lun, target->t_iqn); + free($2); + return (1); + } + lun_set_path(lun, $2); + free($2); + } + ; + +serial_statement: SERIAL STR + { + if (lun->l_serial != NULL) { + log_warnx("serial for lun %d, target \"%s\" " + "specified more than once", + lun->l_lun, target->t_iqn); + free($2); + return (1); + } + lun_set_serial(lun, $2); + free($2); + } + ; + +size_statement: SIZE NUM + { + if (lun->l_size != 0) { + log_warnx("size for lun %d, target \"%s\" " + "specified more than once", + lun->l_lun, target->t_iqn); + return (1); + } + lun_set_size(lun, $2); + } + ; +%% + +void +yyerror(const char *str) +{ + + log_warnx("error in configuration file at line %d near '%s': %s", + lineno, yytext, str); +} + +static void +check_perms(const char *path) +{ + struct stat sb; + int error; + + error = stat(path, &sb); + if (error != 0) { + log_warn("stat"); + return; + } + if (sb.st_mode & S_IWOTH) { + log_warnx("%s is world-writable", path); + } else if (sb.st_mode & S_IROTH) { + log_warnx("%s is world-readable", path); + } else if (sb.st_mode & S_IXOTH) { + /* + * Ok, this one doesn't matter, but still do it, + * just for consistency. + */ + log_warnx("%s is world-executable", path); + } + + /* + * XXX: Should we also check for owner != 0? + */ +} + +struct conf * +conf_new_from_file(const char *path) +{ + struct auth_group *ag; + struct portal_group *pg; + int error; + + log_debugx("obtaining configuration from %s", path); + + conf = conf_new(); + + ag = auth_group_new(conf, "no-authentication"); + ag->ag_type = AG_TYPE_NO_AUTHENTICATION; + + /* + * Here, the type doesn't really matter, as the group doesn't contain + * any entries and thus will always deny access. + */ + ag = auth_group_new(conf, "no-access"); + ag->ag_type = AG_TYPE_CHAP; + + pg = portal_group_new(conf, "default"); + portal_group_add_listen(pg, "0.0.0.0:3260", false); + portal_group_add_listen(pg, "[::]:3260", false); + + yyin = fopen(path, "r"); + if (yyin == NULL) { + log_warn("unable to open configuration file %s", path); + conf_delete(conf); + return (NULL); + } + check_perms(path); + lineno = 0; + yyrestart(yyin); + error = yyparse(); + auth_group = NULL; + portal_group = NULL; + target = NULL; + lun = NULL; + fclose(yyin); + if (error != 0) { + conf_delete(conf); + return (NULL); + } + + error = conf_verify(conf); + if (error != 0) { + conf_delete(conf); + return (NULL); + } + + return (conf); +} diff --git a/usr.sbin/ctld/pdu.c b/usr.sbin/ctld/pdu.c new file mode 100644 index 00000000000..eb8921e3335 --- /dev/null +++ b/usr.sbin/ctld/pdu.c @@ -0,0 +1,246 @@ +/*- + * Copyright (c) 2012 The FreeBSD Foundation + * All rights reserved. + * + * This software was developed by Edward Tomasz Napierala under sponsorship + * from the FreeBSD Foundation. + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + * $FreeBSD$ + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "ctld.h" +#include "iscsi_proto.h" + +#ifdef ICL_KERNEL_PROXY +#include +#endif + +static int +pdu_ahs_length(const struct pdu *pdu) +{ + + return (pdu->pdu_bhs->bhs_total_ahs_len * 4); +} + +static int +pdu_data_segment_length(const struct pdu *pdu) +{ + uint32_t len = 0; + + len += pdu->pdu_bhs->bhs_data_segment_len[0]; + len <<= 8; + len += pdu->pdu_bhs->bhs_data_segment_len[1]; + len <<= 8; + len += pdu->pdu_bhs->bhs_data_segment_len[2]; + + return (len); +} + +static void +pdu_set_data_segment_length(struct pdu *pdu, uint32_t len) +{ + + pdu->pdu_bhs->bhs_data_segment_len[2] = len; + pdu->pdu_bhs->bhs_data_segment_len[1] = len >> 8; + pdu->pdu_bhs->bhs_data_segment_len[0] = len >> 16; +} + +struct pdu * +pdu_new(struct connection *conn) +{ + struct pdu *pdu; + + pdu = calloc(sizeof(*pdu), 1); + if (pdu == NULL) + log_err(1, "calloc"); + + pdu->pdu_bhs = calloc(sizeof(*pdu->pdu_bhs), 1); + if (pdu->pdu_bhs == NULL) + log_err(1, "calloc"); + + pdu->pdu_connection = conn; + + return (pdu); +} + +struct pdu * +pdu_new_response(struct pdu *request) +{ + + return (pdu_new(request->pdu_connection)); +} + +#ifdef ICL_KERNEL_PROXY + +void +pdu_receive(struct pdu *pdu) +{ + size_t len; + + kernel_receive(pdu); + + len = pdu_ahs_length(pdu); + if (len > 0) + log_errx(1, "protocol error: non-empty AHS"); + + len = pdu_data_segment_length(pdu); + assert(len <= MAX_DATA_SEGMENT_LENGTH); + pdu->pdu_data_len = len; +} + +void +pdu_send(struct pdu *pdu) +{ + + pdu_set_data_segment_length(pdu, pdu->pdu_data_len); + kernel_send(pdu); +} + +#else /* !ICL_KERNEL_PROXY */ + +static size_t +pdu_padding(const struct pdu *pdu) +{ + + if ((pdu->pdu_data_len % 4) != 0) + return (4 - (pdu->pdu_data_len % 4)); + + return (0); +} + +static void +pdu_read(int fd, char *data, size_t len) +{ + ssize_t ret; + + while (len > 0) { + ret = read(fd, data, len); + if (ret < 0) { + if (timed_out()) + log_errx(1, "exiting due to timeout"); + log_err(1, "read"); + } else if (ret == 0) + log_errx(1, "read: connection lost"); + len -= ret; + data += ret; + } +} + +void +pdu_receive(struct pdu *pdu) +{ + size_t len, padding; + char dummy[4]; + + pdu_read(pdu->pdu_connection->conn_socket, + (char *)pdu->pdu_bhs, sizeof(*pdu->pdu_bhs)); + + len = pdu_ahs_length(pdu); + if (len > 0) + log_errx(1, "protocol error: non-empty AHS"); + + len = pdu_data_segment_length(pdu); + if (len > 0) { + if (len > MAX_DATA_SEGMENT_LENGTH) { + log_errx(1, "protocol error: received PDU " + "with DataSegmentLength exceeding %d", + MAX_DATA_SEGMENT_LENGTH); + } + + pdu->pdu_data_len = len; + pdu->pdu_data = malloc(len); + if (pdu->pdu_data == NULL) + log_err(1, "malloc"); + + pdu_read(pdu->pdu_connection->conn_socket, + (char *)pdu->pdu_data, pdu->pdu_data_len); + + padding = pdu_padding(pdu); + if (padding != 0) { + assert(padding < sizeof(dummy)); + pdu_read(pdu->pdu_connection->conn_socket, + (char *)dummy, padding); + } + } +} + +void +pdu_send(struct pdu *pdu) +{ + ssize_t ret, total_len; + size_t padding; + uint32_t zero = 0; + struct iovec iov[3]; + int iovcnt; + + pdu_set_data_segment_length(pdu, pdu->pdu_data_len); + iov[0].iov_base = pdu->pdu_bhs; + iov[0].iov_len = sizeof(*pdu->pdu_bhs); + total_len = iov[0].iov_len; + iovcnt = 1; + + if (pdu->pdu_data_len > 0) { + iov[1].iov_base = pdu->pdu_data; + iov[1].iov_len = pdu->pdu_data_len; + total_len += iov[1].iov_len; + iovcnt = 2; + + padding = pdu_padding(pdu); + if (padding > 0) { + assert(padding < sizeof(zero)); + iov[2].iov_base = &zero; + iov[2].iov_len = padding; + total_len += iov[2].iov_len; + iovcnt = 3; + } + } + + ret = writev(pdu->pdu_connection->conn_socket, iov, iovcnt); + if (ret < 0) { + if (timed_out()) + log_errx(1, "exiting due to timeout"); + log_err(1, "writev"); + } + if (ret != total_len) + log_errx(1, "short write"); +} + +#endif /* !ICL_KERNEL_PROXY */ + +void +pdu_delete(struct pdu *pdu) +{ + + free(pdu->pdu_data); + free(pdu->pdu_bhs); + free(pdu); +} diff --git a/usr.sbin/ctld/token.l b/usr.sbin/ctld/token.l new file mode 100644 index 00000000000..f968d97987f --- /dev/null +++ b/usr.sbin/ctld/token.l @@ -0,0 +1,85 @@ +%{ +/*- + * Copyright (c) 2012 The FreeBSD Foundation + * All rights reserved. + * + * This software was developed by Edward Tomasz Napierala under sponsorship + * from the FreeBSD Foundation. + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + * $FreeBSD$ + */ + +#include +#include +#include + +#include "ctld.h" +#include "y.tab.h" + +int lineno; + +#define YY_DECL int yylex(void) +extern int yylex(void); + +%} + +%option noinput +%option nounput + +%% +alias { return ALIAS; } +auth-group { return AUTH_GROUP; } +backend { return BACKEND; } +blocksize { return BLOCKSIZE; } +chap { return CHAP; } +chap-mutual { return CHAP_MUTUAL; } +debug { return DEBUG; } +device-id { return DEVICE_ID; } +discovery-auth-group { return DISCOVERY_AUTH_GROUP; } +listen { return LISTEN; } +listen-iser { return LISTEN_ISER; } +lun { return LUN; } +maxproc { return MAXPROC; } +option { return OPTION; } +path { return PATH; } +pidfile { return PIDFILE; } +portal-group { return PORTAL_GROUP; } +serial { return SERIAL; } +size { return SIZE; } +target { return TARGET; } +timeout { return TIMEOUT; } +[0-9]+[kKmMgGtTpPeE]? { if (expand_number(yytext, &yylval.num) == 0) + return NUM; + else + return STR; + } +\"[^"]+\" { yylval.str = strndup(yytext + 1, + strlen(yytext) - 2); return STR; } +[a-zA-Z0-9\.\-_/\:\[\]]+ { yylval.str = strdup(yytext); return STR; } +\{ { return OPENING_BRACKET; } +\} { return CLOSING_BRACKET; } +#.*$ /* ignore comments */; +\n { lineno++; } +[ \t]+ /* ignore whitespace */; +%% diff --git a/usr.sbin/daemon/daemon.8 b/usr.sbin/daemon/daemon.8 index 14b134af663..87cfd980aec 100644 --- a/usr.sbin/daemon/daemon.8 +++ b/usr.sbin/daemon/daemon.8 @@ -26,7 +26,7 @@ .\" .\" $FreeBSD$ .\" -.Dd June 4, 2012 +.Dd September 13, 2013 .Dt DAEMON 8 .Os .Sh NAME @@ -35,7 +35,8 @@ .Sh SYNOPSIS .Nm .Op Fl cfr -.Op Fl p Ar pidfile +.Op Fl p Ar child_pidfile +.Op Fl P Ar supervisor_pidfile .Op Fl u Ar user .Ar command arguments ... .Sh DESCRIPTION @@ -53,19 +54,39 @@ Change the current working directory to the root .It Fl f Redirect standard input, standard output and standard error to .Pa /dev/null . -.It Fl p Ar file +.It Fl p Ar child_pidfile Write the ID of the created process into the -.Ar file +.Ar child_pidfile using the .Xr pidfile 3 functionality. The program is executed in a spawned child process while the .Nm waits until it terminates to keep the -.Ar file +.Ar child_pidfile locked and removes it after the process exits. The -.Ar file +.Ar child_pidfile +owner is the user who runs the +.Nm +regardless of whether the +.Fl u +option is used or not. +.It Fl P Ar supervisor_pidfile +Write the ID of the +.Nm +process into the +.Ar supervisor_pidfile +using the +.Xr pidfile 3 +functionality. +The program is executed in a spawned child process while the +.Nm +waits until it terminates to keep the +.Ar supervisor_pidfile +locked and removes it after the process exits. +The +.Ar supervisor_pidfile owner is the user who runs the .Nm regardless of whether the @@ -79,27 +100,46 @@ Requires adequate superuser privileges. .El .Pp If the -.Fl p +.Fl p , +.Fl P or .Fl r option is specified the program is executed in a spawned child process. The .Nm -waits until it terminates to keep the pid file locked and removes it +waits until it terminates to keep the pid file(s) locked and removes them after the process exits or restarts the program. In this case if the monitoring .Nm receives software termination signal (SIGTERM) it forwards it to the spawned process. -Normally it will cause the child to exit followed by the termination -of the supervising process after removing the pidfile. +Normally it will cause the child to exit, remove the pidfile(s) +and then terminate. +.Pp +The +.Fl P +option is useful combined with the +.Fl r +option as +.Ar supervisor_pidfile +contains the ID of the supervisor +not the child. This is especially important if you use +.Fl r +in an rc script as the +.Fl p +option will give you the child's ID to signal when you attempt to +stop the service, causing +.Nm +to restart the child. .Sh EXIT STATUS The .Nm utility exits 1 if an error is returned by the .Xr daemon 3 -library routine, 2 if the -.Ar pidfile +library routine, 2 if +.Ar child_pidfile +or +.Ar supervisor_pidfile is requested, but cannot be opened, 3 if process is already running (pidfile exists and is locked), otherwise 0. diff --git a/usr.sbin/daemon/daemon.c b/usr.sbin/daemon/daemon.c index b012c882e42..5ae4fff92b0 100644 --- a/usr.sbin/daemon/daemon.c +++ b/usr.sbin/daemon/daemon.c @@ -53,16 +53,17 @@ static void usage(void); int main(int argc, char *argv[]) { - struct pidfh *pfh = NULL; + struct pidfh *ppfh, *pfh; sigset_t mask, oldmask; int ch, nochdir, noclose, restart; - const char *pidfile, *user; + const char *pidfile, *ppidfile, *user; pid_t otherpid, pid; nochdir = noclose = 1; restart = 0; - pidfile = user = NULL; - while ((ch = getopt(argc, argv, "cfp:ru:")) != -1) { + ppfh = pfh = NULL; + ppidfile = pidfile = user = NULL; + while ((ch = getopt(argc, argv, "cfp:P:ru:")) != -1) { switch (ch) { case 'c': nochdir = 0; @@ -73,6 +74,9 @@ main(int argc, char *argv[]) case 'p': pidfile = optarg; break; + case 'P': + ppidfile = optarg; + break; case 'r': restart = 1; break; @@ -89,7 +93,7 @@ main(int argc, char *argv[]) if (argc == 0) usage(); - pfh = NULL; + ppfh = pfh = NULL; /* * Try to open the pidfile before calling daemon(3), * to be able to report the error intelligently @@ -104,6 +108,18 @@ main(int argc, char *argv[]) err(2, "pidfile ``%s''", pidfile); } } + + /* do same for actual daemon process */ + if (ppidfile != NULL) { + ppfh = pidfile_open(ppidfile, 0600, &otherpid); + if (ppfh == NULL) { + if (errno == EEXIST) { + errx(3, "process already running, pid: %d", + otherpid); + } + err(2, "ppidfile ``%s''", ppidfile); + } + } if (daemon(nochdir, noclose) == -1) err(1, NULL); @@ -176,12 +192,17 @@ restart: */ err(1, "%s", argv[0]); } + /* write out parent pidfile if needed */ + if (ppidfile != NULL) + pidfile_write(ppfh); + setproctitle("%s[%d]", argv[0], pid); if (wait_child(pid, &mask) == 0 && restart) { sleep(1); goto restart; } pidfile_remove(pfh); + pidfile_remove(ppfh); exit(0); /* Exit status does not matter. */ } @@ -240,7 +261,7 @@ static void usage(void) { (void)fprintf(stderr, - "usage: daemon [-cfr] [-p pidfile] [-u user] command " - "arguments ...\n"); + "usage: daemon [-cfr] [-p child_pidfile] [-P supervisor_pidfile] " + "[-u user]\n command arguments ...\n"); exit(1); } diff --git a/usr.sbin/dnssec-verify/Makefile b/usr.sbin/dnssec-verify/Makefile new file mode 100644 index 00000000000..1d1dc9da9c2 --- /dev/null +++ b/usr.sbin/dnssec-verify/Makefile @@ -0,0 +1,25 @@ +# $FreeBSD$ + +BIND_DIR= ${.CURDIR}/../../contrib/bind9 +LIB_BIND_REL= ../../lib/bind +LIB_BIND_DIR= ${.CURDIR}/${LIB_BIND_REL} +SRCDIR= ${BIND_DIR}/bin/dnssec + +.include "${LIB_BIND_DIR}/config.mk" + +PROG= dnssec-verify + +.PATH: ${SRCDIR} +SRCS+= dnssec-verify.c dnssectool.c + +CFLAGS+= -I${SRCDIR}/unix/include -I${SRCDIR}/include +CFLAGS+= -fsigned-char + +DPADD+= ${BIND_DPADD} ${CRYPTO_DPADD} ${PTHREAD_DPADD} +LDADD+= ${BIND_LDADD} ${CRYPTO_LDADD} ${PTHREAD_LDADD} + +WARNS?= 3 + +MAN= dnssec-verify.8 + +.include diff --git a/usr.sbin/gpioctl/gpioctl.8 b/usr.sbin/gpioctl/gpioctl.8 index 32ae409836e..c1abb5e399f 100644 --- a/usr.sbin/gpioctl/gpioctl.8 +++ b/usr.sbin/gpioctl/gpioctl.8 @@ -27,7 +27,7 @@ .\" .\" $FreeBSD$ .\" -.Dd May 25, 2011 +.Dd August 13, 2013 .Dt GPIOCTL 1 .Os .Sh NAME @@ -36,20 +36,20 @@ .Sh SYNOPSIS .Nm .Cm -l -.Fl f Ar ctldev +.Op Fl f Ar ctldev .Op Fl v .Nm .Cm -t -.Fl f Ar ctldev +.Op Fl f Ar ctldev .Ar pin .Nm .Cm -c -.Fl f Ar ctldev +.Op Fl f Ar ctldev .Ar pin .Ar flag .Op flag ... .Nm -.Cm -f Ar ctldev +.Op Cm -f Ar ctldev .Ar pin .Ar [0|1] .Sh DESCRIPTION @@ -83,6 +83,8 @@ Inverted output pin .El .It Fl f Ar ctldev GPIO controller device to use +If not specified, defaults to +.Pa /dev/gpioc0 .It Fl l list available pins .It Fl t Ar pin @@ -105,6 +107,8 @@ Configure pin 12 to be input pin .Pp gpioctl -f /dev/gpioc0 -c 12 IN .El +.Sh SEE ALSO +.Xr gpio 4 .Sh HISTORY The .Nm diff --git a/usr.sbin/gpioctl/gpioctl.c b/usr.sbin/gpioctl/gpioctl.c index 9d76e1df63b..e4708a414ec 100644 --- a/usr.sbin/gpioctl/gpioctl.c +++ b/usr.sbin/gpioctl/gpioctl.c @@ -30,6 +30,7 @@ __FBSDID("$FreeBSD$"); #include #include +#include #include #include #include @@ -63,10 +64,10 @@ static void usage(void) { fprintf(stderr, "Usage:\n"); - fprintf(stderr, "\tgpioctl -f ctldev -l [-v]\n"); - fprintf(stderr, "\tgpioctl -f ctldev -t pin\n"); - fprintf(stderr, "\tgpioctl -f ctldev -c pin flag ...\n"); - fprintf(stderr, "\tgpioctl -f ctldev pin [0|1]\n"); + fprintf(stderr, "\tgpioctl [-f ctldev] -l [-v]\n"); + fprintf(stderr, "\tgpioctl [-f ctldev] -t pin\n"); + fprintf(stderr, "\tgpioctl [-f ctldev] -c pin flag ...\n"); + fprintf(stderr, "\tgpioctl [-f ctldev] pin [0|1]\n"); exit(1); } @@ -185,6 +186,7 @@ main(int argc, char **argv) int i; struct gpio_pin pin; struct gpio_req req; + char defctlfile[] = _PATH_DEVGPIOC "0"; char *ctlfile = NULL; int pinn, pinv, fd, ch; int flags, flag, ok; @@ -226,7 +228,7 @@ main(int argc, char **argv) printf("%d/%s\n", i, argv[i]); if (ctlfile == NULL) - fail("No gpioctl device provided\n"); + ctlfile = defctlfile; fd = open(ctlfile, O_RDONLY); if (fd < 0) { diff --git a/usr.sbin/iscsid/Makefile b/usr.sbin/iscsid/Makefile new file mode 100644 index 00000000000..468b2849bd3 --- /dev/null +++ b/usr.sbin/iscsid/Makefile @@ -0,0 +1,16 @@ +# $FreeBSD$ + +PROG= iscsid +SRCS= discovery.c iscsid.c keys.c log.c login.c pdu.c +CFLAGS+= -I${.CURDIR} +CFLAGS+= -I${.CURDIR}/../../sys/cam +CFLAGS+= -I${.CURDIR}/../../sys/dev/iscsi +#CFLAGS+= -DICL_KERNEL_PROXY +MAN= iscsid.8 + +DPADD= ${LIBUTIL} +LDADD= -lcrypto -lssl -lutil + +WARNS= 6 + +.include diff --git a/usr.sbin/iscsid/discovery.c b/usr.sbin/iscsid/discovery.c new file mode 100644 index 00000000000..639b71ac358 --- /dev/null +++ b/usr.sbin/iscsid/discovery.c @@ -0,0 +1,222 @@ +/*- + * Copyright (c) 2012 The FreeBSD Foundation + * All rights reserved. + * + * This software was developed by Edward Tomasz Napierala under sponsorship + * from the FreeBSD Foundation. + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + * $FreeBSD$ + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "iscsid.h" +#include "iscsi_proto.h" + +static struct pdu * +text_receive(struct connection *conn) +{ + struct pdu *response; + struct iscsi_bhs_text_response *bhstr; + + response = pdu_new(conn); + pdu_receive(response); + if (response->pdu_bhs->bhs_opcode != ISCSI_BHS_OPCODE_TEXT_RESPONSE) + log_errx(1, "protocol error: received invalid opcode 0x%x", + response->pdu_bhs->bhs_opcode); + bhstr = (struct iscsi_bhs_text_response *)response->pdu_bhs; +#if 0 + if ((bhstr->bhstr_flags & BHSTR_FLAGS_FINAL) == 0) + log_errx(1, "received Text PDU without the \"F\" flag"); +#endif + /* + * XXX: Implement the C flag some day. + */ + if ((bhstr->bhstr_flags & BHSTR_FLAGS_CONTINUE) != 0) + log_errx(1, "received Text PDU with unsupported \"C\" flag"); + if (response->pdu_data_len == 0) + log_errx(1, "received Text PDU with empty data segment"); + if (ntohl(bhstr->bhstr_statsn) != conn->conn_statsn + 1) { + log_errx(1, "received Text PDU with wrong StatSN: " + "is %d, should be %d", ntohl(bhstr->bhstr_statsn), + conn->conn_statsn + 1); + } + conn->conn_statsn = ntohl(bhstr->bhstr_statsn); + + return (response); +} + +static struct pdu * +text_new_request(struct connection *conn) +{ + struct pdu *request; + struct iscsi_bhs_text_request *bhstr; + + request = pdu_new(conn); + bhstr = (struct iscsi_bhs_text_request *)request->pdu_bhs; + bhstr->bhstr_opcode = ISCSI_BHS_OPCODE_TEXT_REQUEST | + ISCSI_BHS_OPCODE_IMMEDIATE; + bhstr->bhstr_flags = BHSTR_FLAGS_FINAL; + bhstr->bhstr_initiator_task_tag = 0; + bhstr->bhstr_target_transfer_tag = 0xffffffff; + + bhstr->bhstr_initiator_task_tag = 0; /* XXX */ + bhstr->bhstr_cmdsn = 0; /* XXX */ + bhstr->bhstr_expstatsn = htonl(conn->conn_statsn + 1); + + return (request); +} + +static struct pdu * +logout_receive(struct connection *conn) +{ + struct pdu *response; + struct iscsi_bhs_logout_response *bhslr; + + response = pdu_new(conn); + pdu_receive(response); + if (response->pdu_bhs->bhs_opcode != ISCSI_BHS_OPCODE_LOGOUT_RESPONSE) + log_errx(1, "protocol error: received invalid opcode 0x%x", + response->pdu_bhs->bhs_opcode); + bhslr = (struct iscsi_bhs_logout_response *)response->pdu_bhs; + if (ntohs(bhslr->bhslr_response) != BHSLR_RESPONSE_CLOSED_SUCCESSFULLY) + log_warnx("received Logout Response with reason %d", + ntohs(bhslr->bhslr_response)); + if (ntohl(bhslr->bhslr_statsn) != conn->conn_statsn + 1) { + log_errx(1, "received Logout PDU with wrong StatSN: " + "is %d, should be %d", ntohl(bhslr->bhslr_statsn), + conn->conn_statsn + 1); + } + conn->conn_statsn = ntohl(bhslr->bhslr_statsn); + + return (response); +} + +static struct pdu * +logout_new_request(struct connection *conn) +{ + struct pdu *request; + struct iscsi_bhs_logout_request *bhslr; + + request = pdu_new(conn); + bhslr = (struct iscsi_bhs_logout_request *)request->pdu_bhs; + bhslr->bhslr_opcode = ISCSI_BHS_OPCODE_LOGOUT_REQUEST | + ISCSI_BHS_OPCODE_IMMEDIATE; + bhslr->bhslr_reason = BHSLR_REASON_CLOSE_SESSION; + bhslr->bhslr_reason |= 0x80; + bhslr->bhslr_initiator_task_tag = 0; /* XXX */ + bhslr->bhslr_cmdsn = 0; /* XXX */ + bhslr->bhslr_expstatsn = htonl(conn->conn_statsn + 1); + + return (request); +} + +static void +kernel_add(const struct connection *conn, const char *target) +{ + struct iscsi_session_add isa; + int error; + + memset(&isa, 0, sizeof(isa)); + memcpy(&isa.isa_conf, &conn->conn_conf, sizeof(isa)); + strlcpy(isa.isa_conf.isc_target, target, + sizeof(isa.isa_conf.isc_target)); + isa.isa_conf.isc_discovery = 0; + error = ioctl(conn->conn_iscsi_fd, ISCSISADD, &isa); + if (error != 0) + log_warn("failed to add %s: ISCSISADD", target); +} + +static void +kernel_remove(const struct connection *conn) +{ + struct iscsi_session_remove isr; + int error; + + memset(&isr, 0, sizeof(isr)); + isr.isr_session_id = conn->conn_session_id; + error = ioctl(conn->conn_iscsi_fd, ISCSISREMOVE, &isr); + if (error != 0) + log_warn("ISCSISREMOVE"); +} + +void +discovery(struct connection *conn) +{ + struct pdu *request, *response; + struct keys *request_keys, *response_keys; + int i; + + log_debugx("beginning discovery session"); + request = text_new_request(conn); + request_keys = keys_new(); + keys_add(request_keys, "SendTargets", "All"); + keys_save(request_keys, request); + keys_delete(request_keys); + request_keys = NULL; + pdu_send(request); + pdu_delete(request); + request = NULL; + + log_debugx("waiting for Text Response"); + response = text_receive(conn); + response_keys = keys_new(); + keys_load(response_keys, response); + for (i = 0; i < KEYS_MAX; i++) { + if (response_keys->keys_names[i] == NULL) + break; + + if (strcmp(response_keys->keys_names[i], "TargetName") != 0) + continue; + + log_debugx("adding target %s", response_keys->keys_values[i]); + /* + * XXX: Validate the target name? + */ + kernel_add(conn, response_keys->keys_values[i]); + } + keys_delete(response_keys); + pdu_delete(response); + + log_debugx("removing temporary discovery session"); + kernel_remove(conn); + + log_debugx("discovery done; logging out"); + request = logout_new_request(conn); + pdu_send(request); + request = NULL; + + log_debugx("waiting for Logout Response"); + response = logout_receive(conn); + pdu_delete(response); + + log_debugx("discovery session done"); +} diff --git a/usr.sbin/iscsid/iscsid.8 b/usr.sbin/iscsid/iscsid.8 new file mode 100644 index 00000000000..89c7c257e7f --- /dev/null +++ b/usr.sbin/iscsid/iscsid.8 @@ -0,0 +1,113 @@ +.\" Copyright (c) 2012 The FreeBSD Foundation +.\" All rights reserved. +.\" +.\" This software was developed by Edward Tomasz Napierala under sponsorship +.\" from the FreeBSD Foundation. +.\" +.\" 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. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHORS 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 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. +.\" +.\" $FreeBSD$ +.\" +.Dd September 20, 2012 +.Dt ISCSID 8 +.Os +.Sh NAME +.Nm iscsid +.Nd iSCSI initiator daemon +.Sh SYNOPSIS +.Nm +.Op Fl P Ar pidfile +.Op Fl d +.Op Fl l Ar loglevel +.Op Fl m Ar maxproc +.Op Fl t Ar seconds +.Sh DESCRIPTION +The +.Nm +daemon is responsible for performing the Login Phase of iSCSI connections, +as well as performing SendTargets discovery. +.Pp +Upon startup, the +.Nm +daemon opens the iSCSI initiator device file and waits for kernel requests. +It doesn't use any configuration file; all the information it needs it gets +from the kernel. +.Pp +When the +.Nm +damon is not running, already established iSCSI connections continue +to work. +However, establishing new connections, or recovering existing ones in case +of connection error, is not possible. +.Pp +The following options are available: +.Bl -tag -width ".Fl P Ar pidfile" +.It Fl P Ar pidfile +Specify alternative location of a file where main process PID will be stored. +The default location is +.Pa /var/run/iscsid.pid . +.It Fl d +Debug mode. +The server sends verbose debug output to standard error, and does not +put itself in the background. +The server will also not fork and will exit after processing one connection. +This option is only intended for debugging the initiator. +.It Fl l Ar loglevel +Specifies debug level. +The default is 0. +.It Fl m Ar maxproc +Specifies limit for concurrently running child processes handling +connections. +The default is 30. +Setting it to 0 disables the limit. +.It Fl t Ar seconds +Specifies timeout for login session, after which the connection +will be forcibly terminated. +The default is 60. +Setting it to 0 disables the timeout. +.El +.Sh FILES +.Bl -tag -width ".Pa /var/run/iscsid.pid" -compact +.It Pa /dev/iscsi +The iSCSI initiator device file. +.It Pa /var/run/iscsid.pid +The default location of the +.Nm +PID file. +.El +.Sh EXIT STATUS +The +.Nm +utility exits 0 on success, and >0 if an error occurs. +.Sh SEE ALSO +.Xr iscsictl 8 +.Sh HISTORY +The +.Nm +command appeared in +.Fx 10.0 . +.Sh AUTHORS +The +.Nm +was developed by +.An Edward Tomasz Napierala Aq trasz@FreeBSD.org +under sponsorship from the FreeBSD Foundation. diff --git a/usr.sbin/iscsid/iscsid.c b/usr.sbin/iscsid/iscsid.c new file mode 100644 index 00000000000..c22d7fd737a --- /dev/null +++ b/usr.sbin/iscsid/iscsid.c @@ -0,0 +1,569 @@ +/*- + * Copyright (c) 2012 The FreeBSD Foundation + * All rights reserved. + * + * This software was developed by Edward Tomasz Napierala under sponsorship + * from the FreeBSD Foundation. + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + * $FreeBSD$ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "iscsid.h" + +static volatile bool sigalrm_received = false; + +static int nchildren = 0; + +static void +usage(void) +{ + + fprintf(stderr, "usage: iscsid [-P pidfile][-d][-m maxproc][-t timeout]\n"); + exit(1); +} + +char * +checked_strdup(const char *s) +{ + char *c; + + c = strdup(s); + if (c == NULL) + log_err(1, "strdup"); + return (c); +} + +static void +resolve_addr(const struct connection *conn, const char *address, + struct addrinfo **ai, bool initiator_side) +{ + struct addrinfo hints; + char *arg, *addr, *ch; + const char *port; + int error, colons = 0; + + arg = checked_strdup(address); + + if (arg[0] == '\0') { + fail(conn, "empty address"); + log_errx(1, "empty address"); + } + if (arg[0] == '[') { + /* + * IPv6 address in square brackets, perhaps with port. + */ + arg++; + addr = strsep(&arg, "]"); + if (arg == NULL) { + fail(conn, "malformed address"); + log_errx(1, "malformed address %s", address); + } + if (arg[0] == '\0') { + port = NULL; + } else if (arg[0] == ':') { + port = arg + 1; + } else { + fail(conn, "malformed address"); + log_errx(1, "malformed address %s", address); + } + } else { + /* + * Either IPv6 address without brackets - and without + * a port - or IPv4 address. Just count the colons. + */ + for (ch = arg; *ch != '\0'; ch++) { + if (*ch == ':') + colons++; + } + if (colons > 1) { + addr = arg; + port = NULL; + } else { + addr = strsep(&arg, ":"); + if (arg == NULL) + port = NULL; + else + port = arg; + } + } + + if (port == NULL && !initiator_side) + port = "3260"; + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = PF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_ADDRCONFIG | AI_NUMERICSERV; + if (initiator_side) + hints.ai_flags |= AI_PASSIVE; + + error = getaddrinfo(addr, port, &hints, ai); + if (error != 0) { + fail(conn, gai_strerror(error)); + log_errx(1, "getaddrinfo for %s failed: %s", + address, gai_strerror(error)); + } +} + +static struct connection * +connection_new(unsigned int session_id, const struct iscsi_session_conf *conf, + int iscsi_fd) +{ + struct connection *conn; + struct addrinfo *from_ai, *to_ai; + const char *from_addr, *to_addr; +#ifdef ICL_KERNEL_PROXY + struct iscsi_daemon_connect idc; +#endif + int error; + + conn = calloc(1, sizeof(*conn)); + if (conn == NULL) + log_err(1, "calloc"); + + /* + * Default values, from RFC 3720, section 12. + */ + conn->conn_header_digest = CONN_DIGEST_NONE; + conn->conn_data_digest = CONN_DIGEST_NONE; + conn->conn_initial_r2t = true; + conn->conn_immediate_data = true; + conn->conn_max_data_segment_length = 8192; + conn->conn_max_burst_length = 262144; + conn->conn_first_burst_length = 65536; + + conn->conn_session_id = session_id; + conn->conn_iscsi_fd = iscsi_fd; + + /* + * XXX: Should we sanitize this somehow? + */ + memcpy(&conn->conn_conf, conf, sizeof(conn->conn_conf)); + + from_addr = conn->conn_conf.isc_initiator_addr; + to_addr = conn->conn_conf.isc_target_addr; + + if (from_addr[0] != '\0') + resolve_addr(conn, from_addr, &from_ai, true); + else + from_ai = NULL; + + resolve_addr(conn, to_addr, &to_ai, false); + +#ifdef ICL_KERNEL_PROXY + + memset(&idc, 0, sizeof(idc)); + idc.idc_session_id = conn->conn_session_id; + if (conn->conn_conf.isc_iser) + idc.idc_iser = 1; + idc.idc_domain = to_ai->ai_family; + idc.idc_socktype = to_ai->ai_socktype; + idc.idc_protocol = to_ai->ai_protocol; + if (from_ai != NULL) { + idc.idc_from_addr = from_ai->ai_addr; + idc.idc_from_addrlen = from_ai->ai_addrlen; + } + idc.idc_to_addr = to_ai->ai_addr; + idc.idc_to_addrlen = to_ai->ai_addrlen; + + log_debugx("connecting to %s using ICL kernel proxy", to_addr); + error = ioctl(iscsi_fd, ISCSIDCONNECT, &idc); + if (error != 0) { + fail(conn, strerror(errno)); + log_err(1, "failed to connect to %s using ICL kernel proxy", + to_addr); + } + +#else /* !ICL_KERNEL_PROXY */ + + if (conn->conn_conf.isc_iser) { + fail(conn, "iSER not supported"); + log_errx(1, "iscsid(8) compiled without ICL_KERNEL_PROXY " + "does not support iSER"); + } + + conn->conn_socket = socket(to_ai->ai_family, to_ai->ai_socktype, + to_ai->ai_protocol); + if (conn->conn_socket < 0) { + fail(conn, strerror(errno)); + log_err(1, "failed to create socket for %s", from_addr); + } + if (from_ai != NULL) { + error = bind(conn->conn_socket, from_ai->ai_addr, + from_ai->ai_addrlen); + if (error != 0) { + fail(conn, strerror(errno)); + log_err(1, "failed to bind to %s", from_addr); + } + } + log_debugx("connecting to %s", to_addr); + error = connect(conn->conn_socket, to_ai->ai_addr, to_ai->ai_addrlen); + if (error != 0) { + fail(conn, strerror(errno)); + log_err(1, "failed to connect to %s", to_addr); + } + +#endif /* !ICL_KERNEL_PROXY */ + + return (conn); +} + +static void +handoff(struct connection *conn) +{ + struct iscsi_daemon_handoff idh; + int error; + + log_debugx("handing off connection to the kernel"); + + memset(&idh, 0, sizeof(idh)); + idh.idh_session_id = conn->conn_session_id; +#ifndef ICL_KERNEL_PROXY + idh.idh_socket = conn->conn_socket; +#endif + strlcpy(idh.idh_target_alias, conn->conn_target_alias, + sizeof(idh.idh_target_alias)); + memcpy(idh.idh_isid, conn->conn_isid, sizeof(idh.idh_isid)); + idh.idh_statsn = conn->conn_statsn; + idh.idh_header_digest = conn->conn_header_digest; + idh.idh_data_digest = conn->conn_data_digest; + idh.idh_initial_r2t = conn->conn_initial_r2t; + idh.idh_immediate_data = conn->conn_immediate_data; + idh.idh_max_data_segment_length = conn->conn_max_data_segment_length; + idh.idh_max_burst_length = conn->conn_max_burst_length; + idh.idh_first_burst_length = conn->conn_first_burst_length; + + error = ioctl(conn->conn_iscsi_fd, ISCSIDHANDOFF, &idh); + if (error != 0) + log_err(1, "ISCSIDHANDOFF"); +} + +void +fail(const struct connection *conn, const char *reason) +{ + struct iscsi_daemon_fail idf; + int error; + + memset(&idf, 0, sizeof(idf)); + idf.idf_session_id = conn->conn_session_id; + strlcpy(idf.idf_reason, reason, sizeof(idf.idf_reason)); + + error = ioctl(conn->conn_iscsi_fd, ISCSIDFAIL, &idf); + if (error != 0) + log_err(1, "ISCSIDFAIL"); +} + +/* + * XXX: I CANT INTO LATIN + */ +static void +capsicate(struct connection *conn) +{ + int error; + cap_rights_t rights; +#ifdef ICL_KERNEL_PROXY + const unsigned long cmds[] = { ISCSIDCONNECT, ISCSIDSEND, ISCSIDRECEIVE, + ISCSIDHANDOFF, ISCSIDFAIL, ISCSISADD, ISCSISREMOVE }; +#else + const unsigned long cmds[] = { ISCSIDHANDOFF, ISCSIDFAIL, ISCSISADD, + ISCSISREMOVE }; +#endif + + cap_rights_init(&rights, CAP_IOCTL); + error = cap_rights_limit(conn->conn_iscsi_fd, &rights); + if (error != 0 && errno != ENOSYS) + log_err(1, "cap_rights_limit"); + + error = cap_ioctls_limit(conn->conn_iscsi_fd, cmds, + sizeof(cmds) / sizeof(cmds[0])); + if (error != 0 && errno != ENOSYS) + log_err(1, "cap_ioctls_limit"); + + error = cap_enter(); + if (error != 0 && errno != ENOSYS) + log_err(1, "cap_enter"); + + if (cap_sandboxed()) + log_debugx("Capsicum capability mode enabled"); + else + log_warnx("Capsicum capability mode not supported"); +} + +bool +timed_out(void) +{ + + return (sigalrm_received); +} + +static void +sigalrm_handler(int dummy __unused) +{ + /* + * It would be easiest to just log an error and exit. We can't + * do this, though, because log_errx() is not signal safe, since + * it calls syslog(3). Instead, set a flag checked by pdu_send() + * and pdu_receive(), to call log_errx() there. Should they fail + * to notice, we'll exit here one second later. + */ + if (sigalrm_received) { + /* + * Oh well. Just give up and quit. + */ + _exit(2); + } + + sigalrm_received = true; +} + +static void +set_timeout(int timeout) +{ + struct sigaction sa; + struct itimerval itv; + int error; + + if (timeout <= 0) { + log_debugx("session timeout disabled"); + return; + } + + bzero(&sa, sizeof(sa)); + sa.sa_handler = sigalrm_handler; + sigfillset(&sa.sa_mask); + error = sigaction(SIGALRM, &sa, NULL); + if (error != 0) + log_err(1, "sigaction"); + + /* + * First SIGALRM will arive after conf_timeout seconds. + * If we do nothing, another one will arrive a second later. + */ + bzero(&itv, sizeof(itv)); + itv.it_interval.tv_sec = 1; + itv.it_value.tv_sec = timeout; + + log_debugx("setting session timeout to %d seconds", + timeout); + error = setitimer(ITIMER_REAL, &itv, NULL); + if (error != 0) + log_err(1, "setitimer"); +} + +static void +handle_request(int iscsi_fd, const struct iscsi_daemon_request *request, int timeout) +{ + struct connection *conn; + + log_set_peer_addr(request->idr_conf.isc_target_addr); + if (request->idr_conf.isc_target[0] != '\0') { + log_set_peer_name(request->idr_conf.isc_target); + setproctitle("%s (%s)", request->idr_conf.isc_target_addr, request->idr_conf.isc_target); + } else { + setproctitle("%s", request->idr_conf.isc_target_addr); + } + + conn = connection_new(request->idr_session_id, &request->idr_conf, iscsi_fd); + set_timeout(timeout); + capsicate(conn); + login(conn); + if (conn->conn_conf.isc_discovery != 0) + discovery(conn); + else + handoff(conn); + + log_debugx("nothing more to do; exiting"); + exit (0); +} + +static int +wait_for_children(bool block) +{ + pid_t pid; + int status; + int num = 0; + + for (;;) { + /* + * If "block" is true, wait for at least one process. + */ + if (block && num == 0) + pid = wait4(-1, &status, 0, NULL); + else + pid = wait4(-1, &status, WNOHANG, NULL); + if (pid <= 0) + break; + if (WIFSIGNALED(status)) { + log_warnx("child process %d terminated with signal %d", + pid, WTERMSIG(status)); + } else if (WEXITSTATUS(status) != 0) { + log_warnx("child process %d terminated with exit status %d", + pid, WEXITSTATUS(status)); + } else { + log_debugx("child process %d terminated gracefully", pid); + } + num++; + } + + return (num); +} + +int +main(int argc, char **argv) +{ + int ch, debug = 0, error, iscsi_fd, maxproc = 30, retval, saved_errno, + timeout = 60; + bool dont_daemonize = false; + struct pidfh *pidfh; + pid_t pid, otherpid; + const char *pidfile_path = DEFAULT_PIDFILE; + struct iscsi_daemon_request request; + + while ((ch = getopt(argc, argv, "P:dl:m:t:")) != -1) { + switch (ch) { + case 'P': + pidfile_path = optarg; + break; + case 'd': + dont_daemonize = true; + debug++; + break; + case 'l': + debug = atoi(optarg); + break; + case 'm': + maxproc = atoi(optarg); + break; + case 't': + timeout = atoi(optarg); + break; + case '?': + default: + usage(); + } + } + argc -= optind; + if (argc != 0) + usage(); + + log_init(debug); + + pidfh = pidfile_open(pidfile_path, 0600, &otherpid); + if (pidfh == NULL) { + if (errno == EEXIST) + log_errx(1, "daemon already running, pid: %jd.", + (intmax_t)otherpid); + log_err(1, "cannot open or create pidfile \"%s\"", + pidfile_path); + } + + iscsi_fd = open(ISCSI_PATH, O_RDWR); + if (iscsi_fd < 0 && errno == ENOENT) { + saved_errno = errno; + retval = kldload("iscsi"); + if (retval != -1) + iscsi_fd = open(ISCSI_PATH, O_RDWR); + else + errno = saved_errno; + } + if (iscsi_fd < 0) + log_err(1, "failed to open %s", ISCSI_PATH); + + if (dont_daemonize == false) { + if (daemon(0, 0) == -1) { + log_warn("cannot daemonize"); + pidfile_remove(pidfh); + exit(1); + } + } + + pidfile_write(pidfh); + + for (;;) { + log_debugx("waiting for request from the kernel"); + + memset(&request, 0, sizeof(request)); + error = ioctl(iscsi_fd, ISCSIDWAIT, &request); + if (error != 0) { + if (errno == EINTR) { + nchildren -= wait_for_children(false); + assert(nchildren >= 0); + continue; + } + + log_err(1, "ISCSIDWAIT"); + } + + if (dont_daemonize) { + log_debugx("not forking due to -d flag; " + "will exit after servicing a single request"); + } else { + nchildren -= wait_for_children(false); + assert(nchildren >= 0); + + while (maxproc > 0 && nchildren >= maxproc) { + log_debugx("maxproc limit of %d child processes hit; " + "waiting for child process to exit", maxproc); + nchildren -= wait_for_children(true); + assert(nchildren >= 0); + } + log_debugx("incoming connection; forking child process #%d", + nchildren); + nchildren++; + + pid = fork(); + if (pid < 0) + log_err(1, "fork"); + if (pid > 0) + continue; + } + + pidfile_close(pidfh); + handle_request(iscsi_fd, &request, timeout); + } + + return (0); +} diff --git a/usr.sbin/iscsid/iscsid.h b/usr.sbin/iscsid/iscsid.h new file mode 100644 index 00000000000..5af994ac873 --- /dev/null +++ b/usr.sbin/iscsid/iscsid.h @@ -0,0 +1,120 @@ +/*- + * Copyright (c) 2012 The FreeBSD Foundation + * All rights reserved. + * + * This software was developed by Edward Tomasz Napierala under sponsorship + * from the FreeBSD Foundation. + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + * $FreeBSD$ + */ + +#ifndef ISCSID_H +#define ISCSID_H + +#include +#include + +#include + +#define DEFAULT_PIDFILE "/var/run/iscsid.pid" + +#define CONN_DIGEST_NONE 0 +#define CONN_DIGEST_CRC32C 1 + +#define CONN_MUTUAL_CHALLENGE_LEN 1024 + +struct connection { + int conn_iscsi_fd; +#ifndef ICL_KERNEL_PROXY + int conn_socket; +#endif + unsigned int conn_session_id; + struct iscsi_session_conf conn_conf; + char conn_target_alias[ISCSI_ADDR_LEN]; + uint8_t conn_isid[6]; + uint32_t conn_statsn; + int conn_header_digest; + int conn_data_digest; + bool conn_initial_r2t; + bool conn_immediate_data; + size_t conn_max_data_segment_length; + size_t conn_max_burst_length; + size_t conn_first_burst_length; + char conn_mutual_challenge[CONN_MUTUAL_CHALLENGE_LEN]; + unsigned char conn_mutual_id; +}; + +struct pdu { + struct connection *pdu_connection; + struct iscsi_bhs *pdu_bhs; + char *pdu_data; + size_t pdu_data_len; +}; + +#define KEYS_MAX 1024 + +struct keys { + char *keys_names[KEYS_MAX]; + char *keys_values[KEYS_MAX]; + char *keys_data; + size_t keys_data_len; +}; + +struct keys *keys_new(void); +void keys_delete(struct keys *key); +void keys_load(struct keys *keys, const struct pdu *pdu); +void keys_save(struct keys *keys, struct pdu *pdu); +const char *keys_find(struct keys *keys, const char *name); +int keys_find_int(struct keys *keys, const char *name); +void keys_add(struct keys *keys, + const char *name, const char *value); +void keys_add_int(struct keys *keys, + const char *name, int value); + +struct pdu *pdu_new(struct connection *ic); +struct pdu *pdu_new_response(struct pdu *request); +void pdu_receive(struct pdu *request); +void pdu_send(struct pdu *response); +void pdu_delete(struct pdu *ip); + +void login(struct connection *ic); + +void discovery(struct connection *ic); + +void log_init(int level); +void log_set_peer_name(const char *name); +void log_set_peer_addr(const char *addr); +void log_err(int, const char *, ...) + __dead2 __printf0like(2, 3); +void log_errx(int, const char *, ...) + __dead2 __printf0like(2, 3); +void log_warn(const char *, ...) __printf0like(1, 2); +void log_warnx(const char *, ...) __printflike(1, 2); +void log_debugx(const char *, ...) __printf0like(1, 2); + +char *checked_strdup(const char *); +bool timed_out(void); +void fail(const struct connection *, const char *); + +#endif /* !ISCSID_H */ diff --git a/usr.sbin/iscsid/keys.c b/usr.sbin/iscsid/keys.c new file mode 100644 index 00000000000..d9055a2d8fb --- /dev/null +++ b/usr.sbin/iscsid/keys.c @@ -0,0 +1,217 @@ +/*- + * Copyright (c) 2012 The FreeBSD Foundation + * All rights reserved. + * + * This software was developed by Edward Tomasz Napierala under sponsorship + * from the FreeBSD Foundation. + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + * $FreeBSD$ + */ + +#include +#include +#include +#include +#include + +#include "iscsid.h" + +struct keys * +keys_new(void) +{ + struct keys *keys; + + keys = calloc(sizeof(*keys), 1); + if (keys == NULL) + log_err(1, "calloc"); + + return (keys); +} + +void +keys_delete(struct keys *keys) +{ + + free(keys->keys_data); + free(keys); +} + +void +keys_load(struct keys *keys, const struct pdu *pdu) +{ + int i; + char *pair; + size_t pair_len; + + if (pdu->pdu_data_len == 0) + log_errx(1, "protocol error: empty data segment"); + + if (pdu->pdu_data[pdu->pdu_data_len - 1] != '\0') + log_errx(1, "protocol error: key not NULL-terminated\n"); + + assert(keys->keys_data == NULL); + keys->keys_data_len = pdu->pdu_data_len; + keys->keys_data = malloc(keys->keys_data_len); + if (keys->keys_data == NULL) + log_err(1, "malloc"); + memcpy(keys->keys_data, pdu->pdu_data, keys->keys_data_len); + + /* + * XXX: Review this carefully. + */ + pair = keys->keys_data; + for (i = 0;; i++) { + if (i >= KEYS_MAX) + log_errx(1, "too many keys received"); + + pair_len = strlen(pair); + + keys->keys_values[i] = pair; + keys->keys_names[i] = strsep(&keys->keys_values[i], "="); + if (keys->keys_names[i] == NULL || keys->keys_values[i] == NULL) + log_errx(1, "malformed keys"); + log_debugx("key received: \"%s=%s\"", + keys->keys_names[i], keys->keys_values[i]); + + pair += pair_len + 1; /* +1 to skip the terminating '\0'. */ + if (pair == keys->keys_data + keys->keys_data_len) + break; + assert(pair < keys->keys_data + keys->keys_data_len); + } +} + +void +keys_save(struct keys *keys, struct pdu *pdu) +{ + char *data; + size_t len; + int i; + + /* + * XXX: Not particularly efficient. + */ + len = 0; + for (i = 0; i < KEYS_MAX; i++) { + if (keys->keys_names[i] == NULL) + break; + /* + * +1 for '=', +1 for '\0'. + */ + len += strlen(keys->keys_names[i]) + + strlen(keys->keys_values[i]) + 2; + } + + if (len == 0) + return; + + data = malloc(len); + if (data == NULL) + log_err(1, "malloc"); + + pdu->pdu_data = data; + pdu->pdu_data_len = len; + + for (i = 0; i < KEYS_MAX; i++) { + if (keys->keys_names[i] == NULL) + break; + data += sprintf(data, "%s=%s", + keys->keys_names[i], keys->keys_values[i]); + data += 1; /* for '\0'. */ + } +} + +const char * +keys_find(struct keys *keys, const char *name) +{ + int i; + + /* + * Note that we don't handle duplicated key names here, + * as they are not supposed to happen in requests, and if they do, + * it's an initiator error. + */ + for (i = 0; i < KEYS_MAX; i++) { + if (keys->keys_names[i] == NULL) + return (NULL); + if (strcmp(keys->keys_names[i], name) == 0) + return (keys->keys_values[i]); + } + return (NULL); +} + +int +keys_find_int(struct keys *keys, const char *name) +{ + const char *str; + char *endptr; + int num; + + str = keys_find(keys, name); + if (str == NULL) + return (-1); + + num = strtoul(str, &endptr, 10); + if (*endptr != '\0') { + log_debugx("invalid numeric value \"%s\"", str); + return (-1); + } + + return (num); +} + +void +keys_add(struct keys *keys, const char *name, const char *value) +{ + int i; + + log_debugx("key to send: \"%s=%s\"", name, value); + + /* + * Note that we don't check for duplicates here, as they are perfectly + * fine in responses, e.g. the "TargetName" keys in discovery sesion + * response. + */ + for (i = 0; i < KEYS_MAX; i++) { + if (keys->keys_names[i] == NULL) { + keys->keys_names[i] = checked_strdup(name); + keys->keys_values[i] = checked_strdup(value); + return; + } + } + log_errx(1, "too many keys"); +} + +void +keys_add_int(struct keys *keys, const char *name, int value) +{ + char *str; + int ret; + + ret = asprintf(&str, "%d", value); + if (ret <= 0) + log_err(1, "asprintf"); + + keys_add(keys, name, str); + free(str); +} diff --git a/usr.sbin/iscsid/log.c b/usr.sbin/iscsid/log.c new file mode 100644 index 00000000000..be3aaed02d5 --- /dev/null +++ b/usr.sbin/iscsid/log.c @@ -0,0 +1,196 @@ +/*- + * Copyright (c) 2012 The FreeBSD Foundation + * All rights reserved. + * + * This software was developed by Edward Tomasz Napierala under sponsorship + * from the FreeBSD Foundation. + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + * $FreeBSD$ + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "iscsid.h" + +static int log_level = 0; +static char *peer_name = NULL; +static char *peer_addr = NULL; + +#define MSGBUF_LEN 1024 + +void +log_init(int level) +{ + + log_level = level; + openlog(getprogname(), LOG_NDELAY | LOG_PID, LOG_DAEMON); +} + +void +log_set_peer_name(const char *name) +{ + + /* + * XXX: Turn it into assertion? + */ + if (peer_name != NULL) + log_errx(1, "%s called twice", __func__); + if (peer_addr == NULL) + log_errx(1, "%s called before log_set_peer_addr", __func__); + + peer_name = checked_strdup(name); +} + +void +log_set_peer_addr(const char *addr) +{ + + /* + * XXX: Turn it into assertion? + */ + if (peer_addr != NULL) + log_errx(1, "%s called twice", __func__); + + peer_addr = checked_strdup(addr); +} + +static void +log_common(int priority, int log_errno, const char *fmt, va_list ap) +{ + static char msgbuf[MSGBUF_LEN]; + static char msgbuf_strvised[MSGBUF_LEN * 4 + 1]; + int ret; + + ret = vsnprintf(msgbuf, sizeof(msgbuf), fmt, ap); + if (ret < 0) { + fprintf(stderr, "%s: snprintf failed", getprogname()); + syslog(LOG_CRIT, "snprintf failed"); + exit(1); + } + + ret = strnvis(msgbuf_strvised, sizeof(msgbuf_strvised), msgbuf, VIS_NL); + if (ret < 0) { + fprintf(stderr, "%s: strnvis failed", getprogname()); + syslog(LOG_CRIT, "strnvis failed"); + exit(1); + } + + if (log_errno == -1) { + if (peer_name != NULL) { + fprintf(stderr, "%s: %s (%s): %s\n", getprogname(), + peer_addr, peer_name, msgbuf_strvised); + syslog(priority, "%s (%s): %s", + peer_addr, peer_name, msgbuf_strvised); + } else if (peer_addr != NULL) { + fprintf(stderr, "%s: %s: %s\n", getprogname(), + peer_addr, msgbuf_strvised); + syslog(priority, "%s: %s", + peer_addr, msgbuf_strvised); + } else { + fprintf(stderr, "%s: %s\n", getprogname(), msgbuf_strvised); + syslog(priority, "%s", msgbuf_strvised); + } + + } else { + if (peer_name != NULL) { + fprintf(stderr, "%s: %s (%s): %s: %s\n", getprogname(), + peer_addr, peer_name, msgbuf_strvised, strerror(errno)); + syslog(priority, "%s (%s): %s: %s", + peer_addr, peer_name, msgbuf_strvised, strerror(errno)); + } else if (peer_addr != NULL) { + fprintf(stderr, "%s: %s: %s: %s\n", getprogname(), + peer_addr, msgbuf_strvised, strerror(errno)); + syslog(priority, "%s: %s: %s", + peer_addr, msgbuf_strvised, strerror(errno)); + } else { + fprintf(stderr, "%s: %s: %s\n", getprogname(), + msgbuf_strvised, strerror(errno)); + syslog(priority, "%s: %s", + msgbuf_strvised, strerror(errno)); + } + } +} + +void +log_err(int eval, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + log_common(LOG_CRIT, errno, fmt, ap); + va_end(ap); + + exit(eval); +} + +void +log_errx(int eval, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + log_common(LOG_CRIT, -1, fmt, ap); + va_end(ap); + + exit(eval); +} + +void +log_warn(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + log_common(LOG_WARNING, errno, fmt, ap); + va_end(ap); +} + +void +log_warnx(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + log_common(LOG_WARNING, -1, fmt, ap); + va_end(ap); +} + +void +log_debugx(const char *fmt, ...) +{ + va_list ap; + + if (log_level == 0) + return; + + va_start(ap, fmt); + log_common(LOG_DEBUG, -1, fmt, ap); + va_end(ap); +} diff --git a/usr.sbin/iscsid/login.c b/usr.sbin/iscsid/login.c new file mode 100644 index 00000000000..2ae36e82f66 --- /dev/null +++ b/usr.sbin/iscsid/login.c @@ -0,0 +1,908 @@ +/*- + * Copyright (c) 2012 The FreeBSD Foundation + * All rights reserved. + * + * This software was developed by Edward Tomasz Napierala under sponsorship + * from the FreeBSD Foundation. + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + * $FreeBSD$ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "iscsid.h" +#include "iscsi_proto.h" + +static int +login_nsg(const struct pdu *response) +{ + struct iscsi_bhs_login_response *bhslr; + + bhslr = (struct iscsi_bhs_login_response *)response->pdu_bhs; + + return (bhslr->bhslr_flags & 0x03); +} + +static void +login_set_nsg(struct pdu *request, int nsg) +{ + struct iscsi_bhs_login_request *bhslr; + + assert(nsg == BHSLR_STAGE_SECURITY_NEGOTIATION || + nsg == BHSLR_STAGE_OPERATIONAL_NEGOTIATION || + nsg == BHSLR_STAGE_FULL_FEATURE_PHASE); + + bhslr = (struct iscsi_bhs_login_request *)request->pdu_bhs; + + bhslr->bhslr_flags &= 0xFC; + bhslr->bhslr_flags |= nsg; +} + +static void +login_set_csg(struct pdu *request, int csg) +{ + struct iscsi_bhs_login_request *bhslr; + + assert(csg == BHSLR_STAGE_SECURITY_NEGOTIATION || + csg == BHSLR_STAGE_OPERATIONAL_NEGOTIATION || + csg == BHSLR_STAGE_FULL_FEATURE_PHASE); + + bhslr = (struct iscsi_bhs_login_request *)request->pdu_bhs; + + bhslr->bhslr_flags &= 0xF3; + bhslr->bhslr_flags |= csg << 2; +} + +static const char * +login_target_error_str(int class, int detail) +{ + static char msg[128]; + + /* + * RFC 3270, 10.13.5. Status-Class and Status-Detail + */ + switch (class) { + case 0x01: + switch (detail) { + case 0x01: + return ("Target moved temporarily"); + case 0x02: + return ("Target moved permanently"); + default: + snprintf(msg, sizeof(msg), "unknown redirection; " + "Status-Class 0x%x, Status-Detail 0x%x", + class, detail); + return (msg); + } + case 0x02: + switch (detail) { + case 0x00: + return ("Initiator error"); + case 0x01: + return ("Authentication failure"); + case 0x02: + return ("Authorization failure"); + case 0x03: + return ("Not found"); + case 0x04: + return ("Target removed"); + case 0x05: + return ("Unsupported version"); + case 0x06: + return ("Too many connections"); + case 0x07: + return ("Missing parameter"); + case 0x08: + return ("Can't include in session"); + case 0x09: + return ("Session type not supported"); + case 0x0a: + return ("Session does not exist"); + case 0x0b: + return ("Invalid during login"); + default: + snprintf(msg, sizeof(msg), "unknown initiator error; " + "Status-Class 0x%x, Status-Detail 0x%x", + class, detail); + return (msg); + } + case 0x03: + switch (detail) { + case 0x00: + return ("Target error"); + case 0x01: + return ("Service unavailable"); + case 0x02: + return ("Out of resources"); + default: + snprintf(msg, sizeof(msg), "unknown target error; " + "Status-Class 0x%x, Status-Detail 0x%x", + class, detail); + return (msg); + } + default: + snprintf(msg, sizeof(msg), "unknown error; " + "Status-Class 0x%x, Status-Detail 0x%x", + class, detail); + return (msg); + } +} + +static struct pdu * +login_receive(struct connection *conn, bool initial) +{ + struct pdu *response; + struct iscsi_bhs_login_response *bhslr; + const char *errorstr; + + response = pdu_new(conn); + pdu_receive(response); + if (response->pdu_bhs->bhs_opcode != ISCSI_BHS_OPCODE_LOGIN_RESPONSE) { + log_errx(1, "protocol error: received invalid opcode 0x%x", + response->pdu_bhs->bhs_opcode); + } + bhslr = (struct iscsi_bhs_login_response *)response->pdu_bhs; + /* + * XXX: Implement the C flag some day. + */ + if ((bhslr->bhslr_flags & BHSLR_FLAGS_CONTINUE) != 0) + log_errx(1, "received Login PDU with unsupported \"C\" flag"); + if (bhslr->bhslr_version_max != 0x00) + log_errx(1, "received Login PDU with unsupported " + "Version-max 0x%x", bhslr->bhslr_version_max); + if (bhslr->bhslr_version_active != 0x00) + log_errx(1, "received Login PDU with unsupported " + "Version-active 0x%x", bhslr->bhslr_version_active); + if (bhslr->bhslr_status_class != 0) { + errorstr = login_target_error_str(bhslr->bhslr_status_class, + bhslr->bhslr_status_detail); + fail(conn, errorstr); + log_errx(1, "target returned error: %s", errorstr); + } +#if 0 + if (response->pdu_data_len == 0) + log_errx(1, "received Login PDU with empty data segment"); +#endif + if (initial == false && + ntohl(bhslr->bhslr_statsn) != conn->conn_statsn + 1) { + /* + * It's a warning, not an error, to work around what seems + * to be bug in NetBSD iSCSI target. + */ + log_warnx("received Login PDU with wrong StatSN: " + "is %d, should be %d", ntohl(bhslr->bhslr_statsn), + conn->conn_statsn + 1); + } + conn->conn_statsn = ntohl(bhslr->bhslr_statsn); + + return (response); +} + +static struct pdu * +login_new_request(struct connection *conn) +{ + struct pdu *request; + struct iscsi_bhs_login_request *bhslr; + + request = pdu_new(conn); + bhslr = (struct iscsi_bhs_login_request *)request->pdu_bhs; + bhslr->bhslr_opcode = ISCSI_BHS_OPCODE_LOGIN_REQUEST | + ISCSI_BHS_OPCODE_IMMEDIATE; + bhslr->bhslr_flags = BHSLR_FLAGS_TRANSIT; + login_set_csg(request, BHSLR_STAGE_SECURITY_NEGOTIATION); + login_set_nsg(request, BHSLR_STAGE_OPERATIONAL_NEGOTIATION); + memcpy(bhslr->bhslr_isid, &conn->conn_isid, sizeof(bhslr->bhslr_isid)); + bhslr->bhslr_initiator_task_tag = 0; + bhslr->bhslr_cmdsn = 0; + bhslr->bhslr_expstatsn = htonl(conn->conn_statsn + 1); + + return (request); +} + +static int +login_list_prefers(const char *list, + const char *choice1, const char *choice2) +{ + char *tofree, *str, *token; + + tofree = str = checked_strdup(list); + + while ((token = strsep(&str, ",")) != NULL) { + if (strcmp(token, choice1) == 0) { + free(tofree); + return (1); + } + if (strcmp(token, choice2) == 0) { + free(tofree); + return (2); + } + } + free(tofree); + return (-1); +} + +static int +login_hex2int(const char hex) +{ + switch (hex) { + case '0': + return (0x00); + case '1': + return (0x01); + case '2': + return (0x02); + case '3': + return (0x03); + case '4': + return (0x04); + case '5': + return (0x05); + case '6': + return (0x06); + case '7': + return (0x07); + case '8': + return (0x08); + case '9': + return (0x09); + case 'a': + case 'A': + return (0x0a); + case 'b': + case 'B': + return (0x0b); + case 'c': + case 'C': + return (0x0c); + case 'd': + case 'D': + return (0x0d); + case 'e': + case 'E': + return (0x0e); + case 'f': + case 'F': + return (0x0f); + default: + return (-1); + } +} + +/* + * XXX: Review this _carefully_. + */ +static int +login_hex2bin(const char *hex, char **binp, size_t *bin_lenp) +{ + int i, hex_len, nibble; + bool lo = true; /* As opposed to 'hi'. */ + char *bin; + size_t bin_off, bin_len; + + if (strncasecmp(hex, "0x", strlen("0x")) != 0) { + log_warnx("malformed variable, should start with \"0x\""); + return (-1); + } + + hex += strlen("0x"); + hex_len = strlen(hex); + if (hex_len < 1) { + log_warnx("malformed variable; doesn't contain anything " + "but \"0x\""); + return (-1); + } + + bin_len = hex_len / 2 + hex_len % 2; + bin = calloc(bin_len, 1); + if (bin == NULL) + log_err(1, "calloc"); + + bin_off = bin_len - 1; + for (i = hex_len - 1; i >= 0; i--) { + nibble = login_hex2int(hex[i]); + if (nibble < 0) { + log_warnx("malformed variable, invalid char \"%c\"", + hex[i]); + return (-1); + } + + assert(bin_off < bin_len); + if (lo) { + bin[bin_off] = nibble; + lo = false; + } else { + bin[bin_off] |= nibble << 4; + bin_off--; + lo = true; + } + } + + *binp = bin; + *bin_lenp = bin_len; + return (0); +} + +static char * +login_bin2hex(const char *bin, size_t bin_len) +{ + unsigned char *hex, *tmp, ch; + size_t hex_len; + size_t i; + + hex_len = bin_len * 2 + 3; /* +2 for "0x", +1 for '\0'. */ + hex = malloc(hex_len); + if (hex == NULL) + log_err(1, "malloc"); + + tmp = hex; + tmp += sprintf(tmp, "0x"); + for (i = 0; i < bin_len; i++) { + ch = bin[i]; + tmp += sprintf(tmp, "%02x", ch); + } + + return (hex); +} + +static void +login_compute_md5(const char id, const char *secret, + const void *challenge, size_t challenge_len, void *response, + size_t response_len) +{ + MD5_CTX ctx; + int rv; + + assert(response_len == MD5_DIGEST_LENGTH); + + MD5_Init(&ctx); + MD5_Update(&ctx, &id, sizeof(id)); + MD5_Update(&ctx, secret, strlen(secret)); + MD5_Update(&ctx, challenge, challenge_len); + rv = MD5_Final(response, &ctx); + if (rv != 1) + log_errx(1, "MD5_Final"); +} + +static void +login_negotiate_key(struct connection *conn, const char *name, + const char *value) +{ + int which, tmp; + + if (strcmp(name, "TargetAlias") == 0) { + strlcpy(conn->conn_target_alias, value, + sizeof(conn->conn_target_alias)); + } else if (strcmp(value, "Irrelevant") == 0) { + /* Ignore. */ + } else if (strcmp(name, "HeaderDigest") == 0) { + which = login_list_prefers(value, "CRC32C", "None"); + switch (which) { + case 1: + log_debugx("target prefers CRC32C " + "for header digest; we'll use it"); + conn->conn_header_digest = CONN_DIGEST_CRC32C; + break; + case 2: + log_debugx("target prefers not to do " + "header digest; we'll comply"); + break; + default: + log_warnx("target sent unrecognized " + "HeaderDigest value \"%s\"; will use None", value); + break; + } + } else if (strcmp(name, "DataDigest") == 0) { + which = login_list_prefers(value, "CRC32C", "None"); + switch (which) { + case 1: + log_debugx("target prefers CRC32C " + "for data digest; we'll use it"); + conn->conn_data_digest = CONN_DIGEST_CRC32C; + break; + case 2: + log_debugx("target prefers not to do " + "data digest; we'll comply"); + break; + default: + log_warnx("target sent unrecognized " + "DataDigest value \"%s\"; will use None", value); + break; + } + } else if (strcmp(name, "MaxConnections") == 0) { + /* Ignore. */ + } else if (strcmp(name, "InitialR2T") == 0) { + if (strcmp(value, "Yes") == 0) + conn->conn_initial_r2t = true; + else + conn->conn_initial_r2t = false; + } else if (strcmp(name, "ImmediateData") == 0) { + if (strcmp(value, "Yes") == 0) + conn->conn_immediate_data = true; + else + conn->conn_immediate_data = false; + } else if (strcmp(name, "MaxRecvDataSegmentLength") == 0) { + tmp = strtoul(value, NULL, 10); + if (tmp <= 0) + log_errx(1, "received invalid " + "MaxRecvDataSegmentLength"); + conn->conn_max_data_segment_length = tmp; + } else if (strcmp(name, "MaxBurstLength") == 0) { + if (conn->conn_immediate_data) { + tmp = strtoul(value, NULL, 10); + if (tmp <= 0) + log_errx(1, "received invalid MaxBurstLength"); + conn->conn_max_burst_length = tmp; + } + } else if (strcmp(name, "FirstBurstLength") == 0) { + tmp = strtoul(value, NULL, 10); + if (tmp <= 0) + log_errx(1, "received invalid FirstBurstLength"); + conn->conn_first_burst_length = tmp; + } else if (strcmp(name, "DefaultTime2Wait") == 0) { + /* Ignore */ + } else if (strcmp(name, "DefaultTime2Retain") == 0) { + /* Ignore */ + } else if (strcmp(name, "MaxOutstandingR2T") == 0) { + /* Ignore */ + } else if (strcmp(name, "DataPDUInOrder") == 0) { + /* Ignore */ + } else if (strcmp(name, "DataSequenceInOrder") == 0) { + /* Ignore */ + } else if (strcmp(name, "ErrorRecoveryLevel") == 0) { + /* Ignore */ + } else if (strcmp(name, "OFMarker") == 0) { + /* Ignore */ + } else if (strcmp(name, "IFMarker") == 0) { + /* Ignore */ + } else if (strcmp(name, "TargetPortalGroupTag") == 0) { + /* Ignore */ + } else { + log_debugx("unknown key \"%s\"; ignoring", name); + } +} + +static void +login_negotiate(struct connection *conn) +{ + struct pdu *request, *response; + struct keys *request_keys, *response_keys; + struct iscsi_bhs_login_response *bhslr; + int i; + + log_debugx("beginning parameter negotiation"); + request = login_new_request(conn); + login_set_csg(request, BHSLR_STAGE_OPERATIONAL_NEGOTIATION); + login_set_nsg(request, BHSLR_STAGE_FULL_FEATURE_PHASE); + request_keys = keys_new(); + + /* + * The following keys are irrelevant for discovery sessions. + */ + if (conn->conn_conf.isc_discovery == 0) { + if (conn->conn_conf.isc_header_digest != 0) + keys_add(request_keys, "HeaderDigest", "CRC32C"); + else + keys_add(request_keys, "HeaderDigest", "None"); + if (conn->conn_conf.isc_data_digest != 0) + keys_add(request_keys, "DataDigest", "CRC32C"); + else + keys_add(request_keys, "DataDigest", "None"); + + keys_add(request_keys, "ImmediateData", "Yes"); + keys_add_int(request_keys, "MaxBurstLength", + ISCSI_MAX_DATA_SEGMENT_LENGTH); + keys_add_int(request_keys, "FirstBurstLength", + ISCSI_MAX_DATA_SEGMENT_LENGTH); + keys_add(request_keys, "InitialR2T", "Yes"); + } else { + keys_add(request_keys, "HeaderDigest", "None"); + keys_add(request_keys, "DataDigest", "None"); + } + + keys_add_int(request_keys, "MaxRecvDataSegmentLength", + ISCSI_MAX_DATA_SEGMENT_LENGTH); + keys_add(request_keys, "DefaultTime2Wait", "0"); + keys_add(request_keys, "DefaultTime2Retain", "0"); + keys_add(request_keys, "ErrorRecoveryLevel", "0"); + keys_add(request_keys, "MaxOutstandingR2T", "1"); + keys_save(request_keys, request); + keys_delete(request_keys); + request_keys = NULL; + pdu_send(request); + pdu_delete(request); + request = NULL; + + response = login_receive(conn, false); + response_keys = keys_new(); + keys_load(response_keys, response); + for (i = 0; i < KEYS_MAX; i++) { + if (response_keys->keys_names[i] == NULL) + break; + + login_negotiate_key(conn, + response_keys->keys_names[i], response_keys->keys_values[i]); + } + + bhslr = (struct iscsi_bhs_login_response *)response->pdu_bhs; + if ((bhslr->bhslr_flags & BHSLR_FLAGS_TRANSIT) == 0) + log_warnx("received final login response " + "without the \"T\" flag"); + else if (login_nsg(response) != BHSLR_STAGE_FULL_FEATURE_PHASE) + log_warnx("received final login response with wrong NSG 0x%x", + login_nsg(response)); + + log_debugx("parameter negotiation done; " + "transitioning to Full Feature phase"); + + keys_delete(response_keys); + pdu_delete(response); +} + +static void +login_send_chap_a(struct connection *conn) +{ + struct pdu *request; + struct keys *request_keys; + + request = login_new_request(conn); + request_keys = keys_new(); + keys_add(request_keys, "CHAP_A", "5"); + keys_save(request_keys, request); + keys_delete(request_keys); + pdu_send(request); + pdu_delete(request); +} + +static void +login_send_chap_r(struct pdu *response) +{ + struct connection *conn; + struct pdu *request; + struct keys *request_keys, *response_keys; + const char *chap_a, *chap_c, *chap_i; + char *chap_r, *challenge, response_bin[MD5_DIGEST_LENGTH]; + size_t challenge_len; + int error, rv; + unsigned char id; + char *mutual_chap_c, mutual_chap_i[4]; + + /* + * As in the rest of the initiator, 'request' means + * 'initiator -> target', and 'response' means 'target -> initiator', + * + * So, here the 'response' from the target is the packet that contains + * CHAP challenge; our CHAP response goes into 'request'. + */ + + conn = response->pdu_connection; + + response_keys = keys_new(); + keys_load(response_keys, response); + + /* + * First, compute the response. + */ + chap_a = keys_find(response_keys, "CHAP_A"); + if (chap_a == NULL) + log_errx(1, "received CHAP packet without CHAP_A"); + chap_c = keys_find(response_keys, "CHAP_C"); + if (chap_c == NULL) + log_errx(1, "received CHAP packet without CHAP_C"); + chap_i = keys_find(response_keys, "CHAP_I"); + if (chap_i == NULL) + log_errx(1, "received CHAP packet without CHAP_I"); + + if (strcmp(chap_a, "5") != 0) + log_errx(1, "received CHAP packet " + "with unsupported CHAP_A \"%s\"", chap_a); + id = strtoul(chap_i, NULL, 10); + error = login_hex2bin(chap_c, &challenge, &challenge_len); + if (error != 0) + log_errx(1, "received CHAP packet with malformed CHAP_C"); + login_compute_md5(id, conn->conn_conf.isc_secret, + challenge, challenge_len, response_bin, sizeof(response_bin)); + free(challenge); + chap_r = login_bin2hex(response_bin, sizeof(response_bin)); + + keys_delete(response_keys); + + request = login_new_request(conn); + request_keys = keys_new(); + keys_add(request_keys, "CHAP_N", conn->conn_conf.isc_user); + keys_add(request_keys, "CHAP_R", chap_r); + free(chap_r); + + /* + * If we want mutual authentication, we're expected to send + * our CHAP_I/CHAP_C now. + */ + if (conn->conn_conf.isc_mutual_user[0] != '\0') { + log_debugx("requesting mutual authentication; " + "binary challenge size is %zd bytes", + sizeof(conn->conn_mutual_challenge)); + + rv = RAND_bytes(conn->conn_mutual_challenge, + sizeof(conn->conn_mutual_challenge)); + if (rv != 1) { + log_errx(1, "RAND_bytes failed: %s", + ERR_error_string(ERR_get_error(), NULL)); + } + rv = RAND_bytes(&conn->conn_mutual_id, + sizeof(conn->conn_mutual_id)); + if (rv != 1) { + log_errx(1, "RAND_bytes failed: %s", + ERR_error_string(ERR_get_error(), NULL)); + } + mutual_chap_c = login_bin2hex(conn->conn_mutual_challenge, + sizeof(conn->conn_mutual_challenge)); + snprintf(mutual_chap_i, sizeof(mutual_chap_i), + "%d", conn->conn_mutual_id); + keys_add(request_keys, "CHAP_I", mutual_chap_i); + keys_add(request_keys, "CHAP_C", mutual_chap_c); + free(mutual_chap_c); + } + + keys_save(request_keys, request); + keys_delete(request_keys); + pdu_send(request); + pdu_delete(request); +} + +static void +login_verify_mutual(const struct pdu *response) +{ + struct connection *conn; + struct keys *response_keys; + const char *chap_n, *chap_r; + char *response_bin, expected_response_bin[MD5_DIGEST_LENGTH]; + size_t response_bin_len; + int error; + + conn = response->pdu_connection; + + response_keys = keys_new(); + keys_load(response_keys, response); + + chap_n = keys_find(response_keys, "CHAP_N"); + if (chap_n == NULL) + log_errx(1, "received CHAP Response PDU without CHAP_N"); + chap_r = keys_find(response_keys, "CHAP_R"); + if (chap_r == NULL) + log_errx(1, "received CHAP Response PDU without CHAP_R"); + error = login_hex2bin(chap_r, &response_bin, &response_bin_len); + if (error != 0) + log_errx(1, "received CHAP Response PDU with malformed CHAP_R"); + + if (strcmp(chap_n, conn->conn_conf.isc_mutual_user) != 0) { + fail(conn, "Mutual CHAP failed"); + log_errx(1, "mutual CHAP authentication failed: wrong user"); + } + + login_compute_md5(conn->conn_mutual_id, + conn->conn_conf.isc_mutual_secret, conn->conn_mutual_challenge, + sizeof(conn->conn_mutual_challenge), expected_response_bin, + sizeof(expected_response_bin)); + + if (memcmp(response_bin, expected_response_bin, + sizeof(expected_response_bin)) != 0) { + fail(conn, "Mutual CHAP failed"); + log_errx(1, "mutual CHAP authentication failed: wrong secret"); + } + + keys_delete(response_keys); + free(response_bin); + + log_debugx("mutual CHAP authentication succeeded"); +} + +static void +login_chap(struct connection *conn) +{ + struct pdu *response; + + log_debugx("beginning CHAP authentication; sending CHAP_A"); + login_send_chap_a(conn); + + log_debugx("waiting for CHAP_A/CHAP_C/CHAP_I"); + response = login_receive(conn, false); + + log_debugx("sending CHAP_N/CHAP_R"); + login_send_chap_r(response); + pdu_delete(response); + + /* + * XXX: Make sure this is not susceptible to MITM. + */ + + log_debugx("waiting for CHAP result"); + response = login_receive(conn, false); + if (conn->conn_conf.isc_mutual_user[0] != '\0') + login_verify_mutual(response); + pdu_delete(response); + + log_debugx("CHAP authentication done"); +} + +static void +login_create_isid(struct connection *conn) +{ + int rv; + + /* + * RFC 3720, 10.12.5: 10b, "Random" ISID. + * + */ + conn->conn_isid[0] = 0x80; + + rv = RAND_bytes(&conn->conn_isid[1], 3); + if (rv != 1) { + log_errx(1, "RAND_bytes failed: %s", + ERR_error_string(ERR_get_error(), NULL)); + } +} + +void +login(struct connection *conn) +{ + struct pdu *request, *response; + struct keys *request_keys, *response_keys; + struct iscsi_bhs_login_request *bhslr; + struct iscsi_bhs_login_response *bhslr2; + const char *auth_method; + int i; + + login_create_isid(conn); + + log_debugx("beginning Login phase; sending Login PDU"); + request = login_new_request(conn); + + bhslr = (struct iscsi_bhs_login_request *)request->pdu_bhs; + bhslr->bhslr_flags |= BHSLR_FLAGS_TRANSIT; + + request_keys = keys_new(); + if (conn->conn_conf.isc_mutual_user[0] != '\0') { + keys_add(request_keys, "AuthMethod", "CHAP"); + } else if (conn->conn_conf.isc_user[0] != '\0') { + /* + * Give target a chance to skip authentication if it + * doesn't feel like it. + * + * None is first, CHAP second; this is to work around + * what seems to be LIO (Linux target) bug: otherwise, + * if target is configured with no authentication, + * and we are configured to authenticate, the target + * will erroneously respond with AuthMethod=CHAP + * instead of AuthMethod=None, and will subsequently + * fail the connection. This usually happens with + * Discovery sessions, which default to no authentication. + */ + keys_add(request_keys, "AuthMethod", "None,CHAP"); + } else { + keys_add(request_keys, "AuthMethod", "None"); + } + keys_add(request_keys, "InitiatorName", + conn->conn_conf.isc_initiator); + if (conn->conn_conf.isc_initiator_alias[0] != '\0') { + keys_add(request_keys, "InitiatorAlias", + conn->conn_conf.isc_initiator_alias); + } + if (conn->conn_conf.isc_discovery == 0) { + keys_add(request_keys, "SessionType", "Normal"); + keys_add(request_keys, + "TargetName", conn->conn_conf.isc_target); + } else { + keys_add(request_keys, "SessionType", "Discovery"); + } + keys_save(request_keys, request); + keys_delete(request_keys); + pdu_send(request); + pdu_delete(request); + + response = login_receive(conn, true); + + response_keys = keys_new(); + keys_load(response_keys, response); + + for (i = 0; i < KEYS_MAX; i++) { + if (response_keys->keys_names[i] == NULL) + break; + + /* + * Not interested in AuthMethod at this point; we only need + * to parse things such as TargetAlias. + * + * XXX: This is somewhat ugly. We should have a way to apply + * all the keys to the session and use that by default + * instead of discarding them. + */ + if (strcmp(response_keys->keys_names[i], "AuthMethod") == 0) + continue; + + login_negotiate_key(conn, + response_keys->keys_names[i], response_keys->keys_values[i]); + } + + bhslr2 = (struct iscsi_bhs_login_response *)response->pdu_bhs; + if ((bhslr2->bhslr_flags & BHSLR_FLAGS_TRANSIT) != 0 && + login_nsg(response) == BHSLR_STAGE_OPERATIONAL_NEGOTIATION) { + if (conn->conn_conf.isc_mutual_user[0] != '\0') { + log_errx(1, "target requested transition " + "to operational negotiation, but we require " + "mutual CHAP"); + } + + log_debugx("target requested transition " + "to operational negotiation"); + keys_delete(response_keys); + pdu_delete(response); + login_negotiate(conn); + return; + } + + auth_method = keys_find(response_keys, "AuthMethod"); + if (auth_method == NULL) + log_errx(1, "received response without AuthMethod"); + if (strcmp(auth_method, "None") == 0) { + if (conn->conn_conf.isc_mutual_user[0] != '\0') { + log_errx(1, "target does not require authantication, " + "but we require mutual CHAP"); + } + + log_debugx("target does not require authentication"); + keys_delete(response_keys); + pdu_delete(response); + login_negotiate(conn); + return; + } + + if (strcmp(auth_method, "CHAP") != 0) { + fail(conn, "Unsupported AuthMethod"); + log_errx(1, "received response " + "with unsupported AuthMethod \"%s\"", auth_method); + } + + if (conn->conn_conf.isc_user[0] == '\0' || + conn->conn_conf.isc_secret[0] == '\0') { + fail(conn, "Authentication required"); + log_errx(1, "target requests CHAP authentication, but we don't " + "have user and secret"); + } + + keys_delete(response_keys); + response_keys = NULL; + pdu_delete(response); + response = NULL; + + login_chap(conn); + login_negotiate(conn); +} diff --git a/usr.sbin/iscsid/pdu.c b/usr.sbin/iscsid/pdu.c new file mode 100644 index 00000000000..24b63d79454 --- /dev/null +++ b/usr.sbin/iscsid/pdu.c @@ -0,0 +1,281 @@ +/*- + * Copyright (c) 2012 The FreeBSD Foundation + * All rights reserved. + * + * This software was developed by Edward Tomasz Napierala under sponsorship + * from the FreeBSD Foundation. + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + * $FreeBSD$ + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "iscsid.h" +#include "iscsi_proto.h" + +#ifdef ICL_KERNEL_PROXY +#include +#endif + +static int +pdu_ahs_length(const struct pdu *pdu) +{ + + return (pdu->pdu_bhs->bhs_total_ahs_len * 4); +} + +static int +pdu_data_segment_length(const struct pdu *pdu) +{ + uint32_t len = 0; + + len += pdu->pdu_bhs->bhs_data_segment_len[0]; + len <<= 8; + len += pdu->pdu_bhs->bhs_data_segment_len[1]; + len <<= 8; + len += pdu->pdu_bhs->bhs_data_segment_len[2]; + + return (len); +} + +static void +pdu_set_data_segment_length(struct pdu *pdu, uint32_t len) +{ + + pdu->pdu_bhs->bhs_data_segment_len[2] = len; + pdu->pdu_bhs->bhs_data_segment_len[1] = len >> 8; + pdu->pdu_bhs->bhs_data_segment_len[0] = len >> 16; +} + +struct pdu * +pdu_new(struct connection *conn) +{ + struct pdu *pdu; + + pdu = calloc(sizeof(*pdu), 1); + if (pdu == NULL) + log_err(1, "calloc"); + + pdu->pdu_bhs = calloc(sizeof(*pdu->pdu_bhs), 1); + if (pdu->pdu_bhs == NULL) + log_err(1, "calloc"); + + pdu->pdu_connection = conn; + + return (pdu); +} + +struct pdu * +pdu_new_response(struct pdu *request) +{ + + return (pdu_new(request->pdu_connection)); +} + +#ifdef ICL_KERNEL_PROXY + +void +pdu_receive(struct pdu *pdu) +{ + struct iscsi_daemon_receive *idr; + size_t len; + int error; + + pdu->pdu_data = malloc(ISCSI_MAX_DATA_SEGMENT_LENGTH); + if (pdu->pdu_data == NULL) + log_err(1, "malloc"); + + idr = calloc(1, sizeof(*idr)); + if (idr == NULL) + log_err(1, "calloc"); + + idr->idr_session_id = pdu->pdu_connection->conn_session_id; + idr->idr_bhs = pdu->pdu_bhs; + idr->idr_data_segment_len = ISCSI_MAX_DATA_SEGMENT_LENGTH; + idr->idr_data_segment = pdu->pdu_data; + + error = ioctl(pdu->pdu_connection->conn_iscsi_fd, ISCSIDRECEIVE, idr); + if (error != 0) + log_err(1, "ISCSIDRECEIVE"); + + len = pdu_ahs_length(pdu); + if (len > 0) + log_errx(1, "protocol error: non-empty AHS"); + + len = pdu_data_segment_length(pdu); + assert(len <= ISCSI_MAX_DATA_SEGMENT_LENGTH); + pdu->pdu_data_len = len; + + free(idr); +} + +void +pdu_send(struct pdu *pdu) +{ + struct iscsi_daemon_send *ids; + int error; + + pdu_set_data_segment_length(pdu, pdu->pdu_data_len); + + ids = calloc(1, sizeof(*ids)); + if (ids == NULL) + log_err(1, "calloc"); + + ids->ids_session_id = pdu->pdu_connection->conn_session_id; + ids->ids_bhs = pdu->pdu_bhs; + ids->ids_data_segment_len = pdu->pdu_data_len; + ids->ids_data_segment = pdu->pdu_data; + + error = ioctl(pdu->pdu_connection->conn_iscsi_fd, ISCSIDSEND, ids); + if (error != 0) + log_err(1, "ISCSIDSEND"); + + free(ids); +} + +#else /* !ICL_KERNEL_PROXY */ + +static size_t +pdu_padding(const struct pdu *pdu) +{ + + if ((pdu->pdu_data_len % 4) != 0) + return (4 - (pdu->pdu_data_len % 4)); + + return (0); +} + +static void +pdu_read(int fd, char *data, size_t len) +{ + ssize_t ret; + + while (len > 0) { + ret = read(fd, data, len); + if (ret < 0) { + if (timed_out()) + log_errx(1, "exiting due to timeout"); + log_err(1, "read"); + } else if (ret == 0) + log_errx(1, "read: connection lost"); + len -= ret; + data += ret; + } +} + +void +pdu_receive(struct pdu *pdu) +{ + size_t len, padding; + char dummy[4]; + + pdu_read(pdu->pdu_connection->conn_socket, + (char *)pdu->pdu_bhs, sizeof(*pdu->pdu_bhs)); + + len = pdu_ahs_length(pdu); + if (len > 0) + log_errx(1, "protocol error: non-empty AHS"); + + len = pdu_data_segment_length(pdu); + if (len > 0) { + if (len > ISCSI_MAX_DATA_SEGMENT_LENGTH) { + log_errx(1, "protocol error: received PDU " + "with DataSegmentLength exceeding %d", + ISCSI_MAX_DATA_SEGMENT_LENGTH); + } + + pdu->pdu_data_len = len; + pdu->pdu_data = malloc(len); + if (pdu->pdu_data == NULL) + log_err(1, "malloc"); + + pdu_read(pdu->pdu_connection->conn_socket, + (char *)pdu->pdu_data, pdu->pdu_data_len); + + padding = pdu_padding(pdu); + if (padding != 0) { + assert(padding < sizeof(dummy)); + pdu_read(pdu->pdu_connection->conn_socket, + (char *)dummy, padding); + } + } +} + +void +pdu_send(struct pdu *pdu) +{ + ssize_t ret, total_len; + size_t padding; + uint32_t zero = 0; + struct iovec iov[3]; + int iovcnt; + + pdu_set_data_segment_length(pdu, pdu->pdu_data_len); + iov[0].iov_base = pdu->pdu_bhs; + iov[0].iov_len = sizeof(*pdu->pdu_bhs); + total_len = iov[0].iov_len; + iovcnt = 1; + + if (pdu->pdu_data_len > 0) { + iov[1].iov_base = pdu->pdu_data; + iov[1].iov_len = pdu->pdu_data_len; + total_len += iov[1].iov_len; + iovcnt = 2; + + padding = pdu_padding(pdu); + if (padding > 0) { + assert(padding < sizeof(zero)); + iov[2].iov_base = &zero; + iov[2].iov_len = padding; + total_len += iov[2].iov_len; + iovcnt = 3; + } + } + + ret = writev(pdu->pdu_connection->conn_socket, iov, iovcnt); + if (ret < 0) { + if (timed_out()) + log_errx(1, "exiting due to timeout"); + log_err(1, "writev"); + } + if (ret != total_len) + log_errx(1, "short write"); +} + +#endif /* !ICL_KERNEL_PROXY */ + +void +pdu_delete(struct pdu *pdu) +{ + + free(pdu->pdu_data); + free(pdu->pdu_bhs); + free(pdu); +} diff --git a/usr.sbin/jail/jail.8 b/usr.sbin/jail/jail.8 index b96cfef24cc..88d69f99241 100644 --- a/usr.sbin/jail/jail.8 +++ b/usr.sbin/jail/jail.8 @@ -25,7 +25,7 @@ .\" .\" $FreeBSD$ .\" -.Dd September 15, 2012 +.Dd August 23, 2013 .Dt JAIL 8 .Os .Sh NAME @@ -545,6 +545,14 @@ This permission is effective only together with and if .Va enforce_statfs is set to a value lower than 2. +.It Va allow.mount.tmpfs +privileged users inside the jail will be able to mount and unmount the +tmpfs file system. +This permission is effective only together with +.Va allow.mount +and if +.Va enforce_statfs +is set to a value lower than 2. .It Va allow.mount.zfs privileged users inside the jail will be able to mount and unmount the ZFS file system. diff --git a/usr.sbin/makefs/makefs.8 b/usr.sbin/makefs/makefs.8 index 4d81e458b2e..51e536f030a 100644 --- a/usr.sbin/makefs/makefs.8 +++ b/usr.sbin/makefs/makefs.8 @@ -35,7 +35,7 @@ .\" .\" $FreeBSD$ .\" -.Dd August 22, 2012 +.Dd August 16, 2013 .Dt MAKEFS 8 .Os .Sh NAME @@ -43,7 +43,7 @@ .Nd create a file system image from a directory tree or a mtree manifest .Sh SYNOPSIS .Nm -.Op Fl Dpx +.Op Fl DxZ .Op Fl B Ar byte-order .Op Fl b Ar free-blocks .Op Fl d Ar debug-mask @@ -191,7 +191,10 @@ Set file system specific options. is a comma separated list of options. Valid file system specific options are detailed below. .It Fl p -Create the image as a sparse file. +Deprecated. +See the +.Fl Z +flag. .It Fl S Ar sector-size Set the file system sector size to .Ar sector-size . @@ -213,6 +216,8 @@ ISO 9660 file system. .El .It Fl x Exclude file system nodes not explicitly listed in the specfile. +.It Fl Z +Create the image as a sparse file. .El .Pp Where sizes are specified, a decimal number of bytes is expected. diff --git a/usr.sbin/makefs/makefs.c b/usr.sbin/makefs/makefs.c index 03ff1ac38b1..bf10034f00d 100644 --- a/usr.sbin/makefs/makefs.c +++ b/usr.sbin/makefs/makefs.c @@ -113,7 +113,7 @@ main(int argc, char *argv[]) start_time.tv_sec = start.tv_sec; start_time.tv_nsec = start.tv_usec * 1000; - while ((ch = getopt(argc, argv, "B:b:Dd:f:F:M:m:N:o:ps:S:t:x")) != -1) { + while ((ch = getopt(argc, argv, "B:b:Dd:f:F:M:m:N:o:ps:S:t:xZ")) != -1) { switch (ch) { case 'B': @@ -205,6 +205,7 @@ main(int argc, char *argv[]) break; } case 'p': + /* Deprecated in favor of 'Z' */ fsoptions.sparse = 1; break; @@ -233,6 +234,11 @@ main(int argc, char *argv[]) fsoptions.onlyspec = 1; break; + case 'Z': + /* Superscedes 'p' for compatibility with NetBSD makefs(8) */ + fsoptions.sparse = 1; + break; + case '?': default: usage(); @@ -354,7 +360,7 @@ usage(void) fprintf(stderr, "usage: %s [-t fs-type] [-o fs-options] [-d debug-mask] [-B endian]\n" "\t[-S sector-size] [-M minimum-size] [-m maximum-size] [-s image-size]\n" -"\t[-b free-blocks] [-f free-files] [-F mtree-specfile] [-px]\n" +"\t[-b free-blocks] [-f free-files] [-F mtree-specfile] [-xZ]\n" "\t[-N userdb-dir] image-file directory | manifest [extra-directory ...]\n", prog); exit(1); diff --git a/usr.sbin/mergemaster/mergemaster.sh b/usr.sbin/mergemaster/mergemaster.sh index 629d44fbd8e..361cef9146f 100755 --- a/usr.sbin/mergemaster/mergemaster.sh +++ b/usr.sbin/mergemaster/mergemaster.sh @@ -707,7 +707,7 @@ case "${RERUN}" in # Build the mtree database in a temporary location. case "${PRE_WORLD}" in '') MTREENEW=`mktemp -t mergemaster.mtree` - mtree -ci -p ${TEMPROOT} -k size,md5digest > ${MTREENEW} 2>/dev/null + mtree -nci -p ${TEMPROOT} -k size,md5digest > ${MTREENEW} 2>/dev/null ;; *) # We don't want to mess with the mtree database on a pre-world run or # when re-scanning a previously-built tree. diff --git a/usr.sbin/mfiutil/mfi_drive.c b/usr.sbin/mfiutil/mfi_drive.c index 1927facd67a..2bc902ea275 100644 --- a/usr.sbin/mfiutil/mfi_drive.c +++ b/usr.sbin/mfiutil/mfi_drive.c @@ -473,6 +473,20 @@ rebuild_drive(int ac, char **av) } MFI_COMMAND(top, rebuild, rebuild_drive); +static int +syspd_drive(int ac, char **av) +{ + + if (ac != 2) { + warnx("syspd: %s", ac > 2 ? "extra arguments" : + "drive required"); + return (EINVAL); + } + + return (drive_set_state(av[1], MFI_PD_STATE_SYSTEM)); +} +MFI_COMMAND(top, syspd, syspd_drive); + static int start_rebuild(int ac, char **av) { diff --git a/usr.sbin/mfiutil/mfiutil.8 b/usr.sbin/mfiutil/mfiutil.8 index 6dcc927e108..8346922c9e6 100644 --- a/usr.sbin/mfiutil/mfiutil.8 +++ b/usr.sbin/mfiutil/mfiutil.8 @@ -91,6 +91,9 @@ .Cm rebuild Ar drive .Nm .Op Fl u Ar unit +.Cm syspd Ar drive +.Nm +.Op Fl u Ar unit .Cm drive progress Ar drive .Nm .Op Fl u Ar unit @@ -372,6 +375,11 @@ Mark a failed that is still part of an array as a good drive suitable for a rebuild. The firmware should kick off an array rebuild on its own if a failed drive is marked as a rebuild drive. +.It Cm syspd Ar drive +Present the drive to the host operating system as a disk SYSPD block device in +the format /dev/mfisyspdX. Clear this flag with +.Cm good +.Ar drive .It Cm drive progress Ar drive Report the current progress and estimated completion time of drive operations such as rebuilds or patrol reads. @@ -679,6 +687,10 @@ Add the drive in slot 2 in the main chassis as a hot spare for volume mfid0: .Pp .Dl Nm Cm add s2 mfid0 .Pp +Reconfigure a disk as a SYSPD block device with no RAID +.Pp +.Dl Nm Cm syspd 0 +.Pp Configure the adapter to run periodic patrol reads once a week with the first patrol read starting in 5 minutes: .Pp diff --git a/usr.sbin/mfiutil/mfiutil.c b/usr.sbin/mfiutil/mfiutil.c index 2fb5adf522c..a8d9ef82644 100644 --- a/usr.sbin/mfiutil/mfiutil.c +++ b/usr.sbin/mfiutil/mfiutil.c @@ -66,8 +66,9 @@ usage(void) fprintf(stderr, " show patrol - display patrol read status\n"); fprintf(stderr, " show progress - display status of active operations\n"); fprintf(stderr, " fail - fail a physical drive\n"); - fprintf(stderr, " good - mark a bad physical drive as good\n"); + fprintf(stderr, " good - set a failed/SYSPD drive as UNCONFIGURED\n"); fprintf(stderr, " rebuild - mark failed drive ready for rebuild\n"); + fprintf(stderr, " syspd - set drive into use as SYSPD JBOD\n"); fprintf(stderr, " drive progress - display status of active operations\n"); fprintf(stderr, " drive clear - clear a drive with all 0x00\n"); fprintf(stderr, " start rebuild \n"); @@ -103,7 +104,7 @@ static int version(int ac __unused, char **av __unused) { - printf("mfiutil version 1.0.14"); + printf("mfiutil version 1.0.15"); #ifdef DEBUG printf(" (DEBUG)"); #endif diff --git a/usr.sbin/mtree/create.c b/usr.sbin/mtree/create.c index da72fc6beae..8be9b02173e 100644 --- a/usr.sbin/mtree/create.c +++ b/usr.sbin/mtree/create.c @@ -208,7 +208,7 @@ statf(int indent, FTSENT *p) output(indent, &offset, "mode=%#o", p->fts_statp->st_mode & MBITS); if (keys & F_NLINK && p->fts_statp->st_nlink != 1) output(indent, &offset, "nlink=%u", p->fts_statp->st_nlink); - if (keys & F_SIZE) + if (keys & F_SIZE && S_ISREG(p->fts_statp->st_mode)) output(indent, &offset, "size=%jd", (intmax_t)p->fts_statp->st_size); if (keys & F_TIME) diff --git a/usr.sbin/periodic/periodic.sh b/usr.sbin/periodic/periodic.sh index cfe61092e18..c27aeee1d30 100644 --- a/usr.sbin/periodic/periodic.sh +++ b/usr.sbin/periodic/periodic.sh @@ -21,7 +21,7 @@ output_pipe() case "$output" in /*) pipe="cat >>$output";; "") pipe=cat;; - *) pipe="mail -E -s '$host ${1##*/} run output' $output";; + *) pipe="mail -E -s '$host ${2}${2:+ }${1##*/} run output' $output";; esac eval $pipe } @@ -53,12 +53,13 @@ if [ $1 != "LOCKED" ]; then case $? in 0) ;; 73) #EX_CANTCREATE - echo "can't create ${lockfile}" | output_pipe $arg + echo "can't create ${lockfile}" | \ + output_pipe $arg "$PERIODIC" ret=1 ;; 75) #EX_TEMPFAIL echo "$host ${arg##*/} prior run still in progress" | \ - output_pipe $arg + output_pipe $arg "$PERIODIC" ret=1 ;; *) @@ -76,6 +77,8 @@ shift arg=$1 tmp_output=`mktemp ${TMPDIR:-/tmp}/periodic.XXXXXXXXXX` +context="$PERIODIC" +export PERIODIC="$arg${PERIODIC:+ }${PERIODIC}" # Execute each executable file in the directory list. If the x bit is not # set, assume the user didn't really want us to muck with it (it's a @@ -135,6 +138,6 @@ esac echo "" echo "-- End of $arg output --" fi -} | output_pipe ${arg} +} | output_pipe $arg "$context" rm -f $tmp_output diff --git a/usr.sbin/pkg/config.c b/usr.sbin/pkg/config.c index 142fd1bb737..01eaa9f7f94 100644 --- a/usr.sbin/pkg/config.c +++ b/usr.sbin/pkg/config.c @@ -32,6 +32,7 @@ __FBSDID("$FreeBSD$"); #include #include +#include #include #include #include @@ -100,6 +101,138 @@ elf_corres_to_string(struct _elf_corres *m, int e) return ("unknown"); } +static const char * +aeabi_parse_arm_attributes(void *data, size_t length) +{ + uint32_t sect_len; + uint8_t *section = data; + +#define MOVE(len) do { \ + assert(length >= (len)); \ + section += (len); \ + length -= (len); \ +} while (0) + + if (length == 0 || *section != 'A') + return (NULL); + + MOVE(1); + + /* Read the section length */ + if (length < sizeof(sect_len)) + return (NULL); + + memcpy(§_len, section, sizeof(sect_len)); + + /* + * The section length should be no longer than the section it is within + */ + if (sect_len > length) + return (NULL); + + MOVE(sizeof(sect_len)); + + /* Skip the vendor name */ + while (length != 0) { + if (*section == '\0') + break; + MOVE(1); + } + if (length == 0) + return (NULL); + MOVE(1); + + while (length != 0) { + uint32_t tag_length; + + switch(*section) { + case 1: /* Tag_File */ + MOVE(1); + if (length < sizeof(tag_length)) + return (NULL); + memcpy(&tag_length, section, sizeof(tag_length)); + break; + case 2: /* Tag_Section */ + case 3: /* Tag_Symbol */ + default: + return (NULL); + } + /* At least space for the tag and size */ + if (tag_length <= 5) + return (NULL); + tag_length--; + /* Check the tag fits */ + if (tag_length > length) + return (NULL); + +#define MOVE_TAG(len) do { \ + assert(tag_length >= (len)); \ + MOVE(len); \ + tag_length -= (len); \ +} while(0) + + MOVE(sizeof(tag_length)); + tag_length -= sizeof(tag_length); + + while (tag_length != 0) { + uint8_t tag; + + assert(tag_length >= length); + + tag = *section; + MOVE_TAG(1); + + /* + * These tag values come from: + * + * Addenda to, and Errata in, the ABI for the + * ARM Architecture. Release 2.08, section 2.3. + */ + if (tag == 6) { /* == Tag_CPU_arch */ + uint8_t val; + + val = *section; + /* + * We don't support values that require + * more than one byte. + */ + if (val & (1 << 7)) + return (NULL); + + /* We have an ARMv4 or ARMv5 */ + if (val <= 5) + return ("arm"); + else /* We have an ARMv6+ */ + return ("armv6"); + } else if (tag == 4 || tag == 5 || tag == 32 || + tag == 65 || tag == 67) { + while (*section != '\0' && length != 0) + MOVE_TAG(1); + if (tag_length == 0) + return (NULL); + /* Skip the last byte */ + MOVE_TAG(1); + } else if ((tag >= 7 && tag <= 31) || tag == 34 || + tag == 36 || tag == 38 || tag == 42 || tag == 44 || + tag == 64 || tag == 66 || tag == 68 || tag == 70) { + /* Skip the uleb128 data */ + while (*section & (1 << 7) && length != 0) + MOVE_TAG(1); + if (tag_length == 0) + return (NULL); + /* Skip the last byte */ + MOVE_TAG(1); + } else + return (NULL); +#undef MOVE_TAG + } + + break; + } + return (NULL); +#undef MOVE +} + static int pkg_get_myabi(char *dest, size_t sz) { @@ -108,7 +241,8 @@ pkg_get_myabi(char *dest, size_t sz) Elf_Note note; Elf_Scn *scn; char *src, *osname; - const char *abi, *fpu; + const char *arch, *abi, *fpu, *endian_corres_str; + const char *wordsize_corres_str; GElf_Ehdr elfhdr; GElf_Shdr shdr; int fd, i, ret; @@ -177,21 +311,72 @@ pkg_get_myabi(char *dest, size_t sz) for (i = 0; osname[i] != '\0'; i++) osname[i] = (char)tolower(osname[i]); - snprintf(dest, sz, "%s:%d:%s:%s", - osname, version / 100000, - elf_corres_to_string(mach_corres, (int)elfhdr.e_machine), - elf_corres_to_string(wordsize_corres, - (int)elfhdr.e_ident[EI_CLASS])); + wordsize_corres_str = elf_corres_to_string(wordsize_corres, + (int)elfhdr.e_ident[EI_CLASS]); + + arch = elf_corres_to_string(mach_corres, (int) elfhdr.e_machine); + + snprintf(dest, sz, "%s:%d", + osname, version / 100000); ret = 0; switch (elfhdr.e_machine) { case EM_ARM: + endian_corres_str = elf_corres_to_string(endian_corres, + (int)elfhdr.e_ident[EI_DATA]); + /* FreeBSD doesn't support the hard-float ABI yet */ fpu = "softfp"; if ((elfhdr.e_flags & 0xFF000000) != 0) { + const char *sh_name = NULL; + size_t shstrndx; + /* This is an EABI file, the conformance level is set */ abi = "eabi"; + /* Find which TARGET_ARCH we are building for. */ + elf_getshdrstrndx(elf, &shstrndx); + while ((scn = elf_nextscn(elf, scn)) != NULL) { + sh_name = NULL; + if (gelf_getshdr(scn, &shdr) != &shdr) { + scn = NULL; + break; + } + + sh_name = elf_strptr(elf, shstrndx, + shdr.sh_name); + if (sh_name == NULL) + continue; + if (strcmp(".ARM.attributes", sh_name) == 0) + break; + } + if (scn != NULL && sh_name != NULL) { + data = elf_getdata(scn, NULL); + /* + * Prior to FreeBSD 10.0 libelf would return + * NULL from elf_getdata on the .ARM.attributes + * section. As this was the first release to + * get armv6 support assume a NULL value means + * arm. + * + * This assumption can be removed when 9.x + * is unsupported. + */ + if (data != NULL) { + arch = aeabi_parse_arm_attributes( + data->d_buf, data->d_size); + if (arch == NULL) { + ret = 1; + warn("unknown ARM ARCH"); + goto cleanup; + } + } + } else { + ret = 1; + warn("Unable to find the .ARM.attributes " + "section"); + goto cleanup; + } } else if (elfhdr.e_ident[EI_OSABI] != ELFOSABI_NONE) { /* * EABI executables all have this field set to @@ -200,12 +385,12 @@ pkg_get_myabi(char *dest, size_t sz) abi = "oabi"; } else { ret = 1; + warn("unknown ARM ABI"); goto cleanup; } snprintf(dest + strlen(dest), sz - strlen(dest), - ":%s:%s:%s", elf_corres_to_string(endian_corres, - (int)elfhdr.e_ident[EI_DATA]), - abi, fpu); + ":%s:%s:%s:%s:%s", arch, wordsize_corres_str, + endian_corres_str, abi, fpu); break; case EM_MIPS: /* @@ -230,10 +415,15 @@ pkg_get_myabi(char *dest, size_t sz) abi = "n64"; break; } - snprintf(dest + strlen(dest), sz - strlen(dest), - ":%s:%s", elf_corres_to_string(endian_corres, - (int)elfhdr.e_ident[EI_DATA]), abi); + endian_corres_str = elf_corres_to_string(endian_corres, + (int)elfhdr.e_ident[EI_DATA]); + + snprintf(dest + strlen(dest), sz - strlen(dest), ":%s:%s:%s:%s", + arch, wordsize_corres_str, endian_corres_str, abi); break; + default: + snprintf(dest + strlen(dest), sz - strlen(dest), ":%s:%s", + arch, wordsize_corres_str); } cleanup: diff --git a/usr.sbin/pkg/elf_tables.h b/usr.sbin/pkg/elf_tables.h index d3d689a99dc..0589ecf9425 100644 --- a/usr.sbin/pkg/elf_tables.h +++ b/usr.sbin/pkg/elf_tables.h @@ -58,16 +58,12 @@ static struct _elf_corres endian_corres[] = { { -1, NULL} }; -#define EF_MIPS_ABI 0x0000F000 +#ifndef EF_MIPS_ABI +#define EF_MIPS_ABI 0x0000f000 +#endif #define E_MIPS_ABI_O32 0x00001000 #define E_MIPS_ABI_N32 0x00000020 -#define EF_ARM_NEW_ABI 0x80 -#define EF_ARM_OLD_ABI 0x100 - -#define EF_ARM_SOFT_FLOAT 0x200 -#define EF_ARM_VFP_FLOAT 0x400 - #define NT_VERSION 1 #define NT_ARCH 2 diff --git a/usr.sbin/pkg_install/add/main.c b/usr.sbin/pkg_install/add/main.c index c91c8b6da46..548f92e6bff 100644 --- a/usr.sbin/pkg_install/add/main.c +++ b/usr.sbin/pkg_install/add/main.c @@ -90,6 +90,7 @@ struct { { 803000, 803499, "/packages-8.3-release" }, { 900000, 900499, "/packages-9.0-release" }, { 901000, 901499, "/packages-9.1-release" }, + { 902000, 902499, "/packages-9.2-release" }, { 300000, 399000, "/packages-3-stable" }, { 400000, 499000, "/packages-4-stable" }, { 502100, 502128, "/packages-5-current" }, diff --git a/usr.sbin/rtadvd/config.c b/usr.sbin/rtadvd/config.c index bb4aaecf84a..f0e11a34608 100644 --- a/usr.sbin/rtadvd/config.c +++ b/usr.sbin/rtadvd/config.c @@ -296,10 +296,8 @@ rm_rainfo(struct rainfo *rai) if (rai->rai_ra_data != NULL) free(rai->rai_ra_data); - while ((pfx = TAILQ_FIRST(&rai->rai_prefix)) != NULL) { - TAILQ_REMOVE(&rai->rai_prefix, pfx, pfx_next); - free(pfx); - } + while ((pfx = TAILQ_FIRST(&rai->rai_prefix)) != NULL) + delete_prefix(pfx); while ((sol = TAILQ_FIRST(&rai->rai_soliciter)) != NULL) { TAILQ_REMOVE(&rai->rai_soliciter, sol, sol_next); free(sol); @@ -1125,9 +1123,9 @@ add_prefix(struct rainfo *rai, struct in6_prefixreq *ipr) pfx->pfx_onlinkflg = ipr->ipr_raf_onlink; pfx->pfx_autoconfflg = ipr->ipr_raf_auto; pfx->pfx_origin = PREFIX_FROM_DYNAMIC; + pfx->pfx_rainfo = rai; TAILQ_INSERT_TAIL(&rai->rai_prefix, pfx, pfx_next); - pfx->pfx_rainfo = rai; syslog(LOG_DEBUG, "<%s> new prefix %s/%d was added on %s", __func__, diff --git a/usr.sbin/rtadvd/if.c b/usr.sbin/rtadvd/if.c index b857356a258..5413cdf4b80 100644 --- a/usr.sbin/rtadvd/if.c +++ b/usr.sbin/rtadvd/if.c @@ -394,8 +394,8 @@ update_ifinfo_nd_flags(struct ifinfo *ifi) error = ioctl(s, SIOCGIFINFO_IN6, (caddr_t)&nd); if (error) { close(s); - syslog(LOG_ERR, - "<%s> ioctl() failed.", __func__); + if (errno != EPFNOSUPPORT) + syslog(LOG_ERR, "<%s> ioctl() failed.", __func__); return (1); } ifi->ifi_nd_flags = nd.ndi.flags; diff --git a/usr.sbin/rtsold/dump.c b/usr.sbin/rtsold/dump.c index ddf0d3c0d53..a0bf2c2744c 100644 --- a/usr.sbin/rtsold/dump.c +++ b/usr.sbin/rtsold/dump.c @@ -50,8 +50,6 @@ static FILE *fp; -extern struct ifinfo *iflist; - static void dump_interface_status(void); static const char * const ifstatstr[] = {"IDLE", "DELAY", "PROBE", "DOWN", "TENTATIVE"}; diff --git a/usr.sbin/rtsold/if.c b/usr.sbin/rtsold/if.c index 58ec514aa94..2bf946daf2a 100644 --- a/usr.sbin/rtsold/if.c +++ b/usr.sbin/rtsold/if.c @@ -61,7 +61,6 @@ #include #include "rtsold.h" -extern int rssock; static int ifsock; static int get_llflag(const char *); @@ -304,13 +303,13 @@ if_nametosdl(char *name) lim = buf + len; for (next = buf; next < lim; next += ifm->ifm_msglen) { - ifm = (struct if_msghdr *)next; + ifm = (struct if_msghdr *)(void *)next; if (ifm->ifm_type == RTM_IFINFO) { sa = (struct sockaddr *)(ifm + 1); get_rtaddrs(ifm->ifm_addrs, sa, rti_info); if ((sa = rti_info[RTAX_IFP]) != NULL) { if (sa->sa_family == AF_LINK) { - sdl = (struct sockaddr_dl *)sa; + sdl = (struct sockaddr_dl *)(void *)sa; if (strlen(name) != sdl->sdl_nlen) continue; /* not same len */ if (strncmp(&sdl->sdl_data[0], @@ -397,7 +396,7 @@ get_llflag(const char *name) continue; if (ifa->ifa_addr->sa_family != AF_INET6) continue; - sin6 = (struct sockaddr_in6 *)ifa->ifa_addr; + sin6 = (struct sockaddr_in6 *)(void *)ifa->ifa_addr; if (!IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) continue; diff --git a/usr.sbin/rtsold/probe.c b/usr.sbin/rtsold/probe.c index 657ae406cdf..5ec54aae5d0 100644 --- a/usr.sbin/rtsold/probe.c +++ b/usr.sbin/rtsold/probe.c @@ -35,6 +35,7 @@ #include #include #include +#include #include #include @@ -102,41 +103,51 @@ probe_init(void) void defrouter_probe(struct ifinfo *ifinfo) { - u_char ntopbuf[INET6_ADDRSTRLEN]; - struct in6_drlist dr; - int s, i; - int ifindex = ifinfo->sdl->sdl_index; + struct in6_defrouter *p, *ep; + int ifindex, mib[4]; + char *buf, ntopbuf[INET6_ADDRSTRLEN]; + size_t l; - if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) { - warnmsg(LOG_ERR, __func__, "socket: %s", strerror(errno)); + ifindex = ifinfo->sdl->sdl_index; + if (ifindex == 0) + return; + mib[0] = CTL_NET; + mib[1] = PF_INET6; + mib[2] = IPPROTO_ICMPV6; + mib[3] = ICMPV6CTL_ND6_DRLIST; + if (sysctl(mib, nitems(mib), NULL, &l, NULL, 0) < 0) { + warnmsg(LOG_ERR, __func__, "sysctl(ICMPV6CTL_ND6_DRLIST): %s", + strerror(errno)); return; } - memset(&dr, 0, sizeof(dr)); - strlcpy(dr.ifname, "lo0", sizeof dr.ifname); /* dummy interface */ - if (ioctl(s, SIOCGDRLST_IN6, (caddr_t)&dr) < 0) { - warnmsg(LOG_ERR, __func__, "ioctl(SIOCGDRLST_IN6): %s", + if (l == 0) + return; + buf = malloc(l); + if (buf == NULL) { + warnmsg(LOG_ERR, __func__, "malloc(): %s", strerror(errno)); + return; + } + if (sysctl(mib, nitems(mib), buf, &l, NULL, 0) < 0) { + warnmsg(LOG_ERR, __func__, "sysctl(ICMPV6CTL_ND6_DRLIST): %s", strerror(errno)); - goto closeandend; + free(buf); + return; } - - for (i = 0; i < DRLSTSIZ && dr.defrouter[i].if_index; i++) { - if (ifindex && dr.defrouter[i].if_index == ifindex) { - /* sanity check */ - if (!IN6_IS_ADDR_LINKLOCAL(&dr.defrouter[i].rtaddr)) { - warnmsg(LOG_ERR, __func__, - "default router list contains a " - "non-link-local address(%s)", - inet_ntop(AF_INET6, - &dr.defrouter[i].rtaddr, - ntopbuf, INET6_ADDRSTRLEN)); - continue; /* ignore the address */ - } - sendprobe(&dr.defrouter[i].rtaddr, ifinfo); + ep = (struct in6_defrouter *)(void *)(buf + l); + for (p = (struct in6_defrouter *)(void *)buf; p < ep; p++) { + if (ifindex != p->if_index) + continue; + if (!IN6_IS_ADDR_LINKLOCAL(&p->rtaddr.sin6_addr)) { + warnmsg(LOG_ERR, __func__, + "default router list contains a " + "non-link-local address(%s)", + inet_ntop(AF_INET6, &p->rtaddr.sin6_addr, ntopbuf, + INET6_ADDRSTRLEN)); + continue; /* ignore the address */ } + sendprobe(&p->rtaddr.sin6_addr, ifinfo); } - -closeandend: - close(s); + free(buf); } static void @@ -164,7 +175,7 @@ sendprobe(struct in6_addr *addr, struct ifinfo *ifinfo) cm->cmsg_level = IPPROTO_IPV6; cm->cmsg_type = IPV6_PKTINFO; cm->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo)); - pi = (struct in6_pktinfo *)CMSG_DATA(cm); + pi = (struct in6_pktinfo *)(void *)CMSG_DATA(cm); memset(&pi->ipi6_addr, 0, sizeof(pi->ipi6_addr)); /*XXX*/ pi->ipi6_ifindex = ifindex; diff --git a/usr.sbin/rtsold/rtsock.c b/usr.sbin/rtsold/rtsock.c index fe22bc78263..702ffe13f69 100644 --- a/usr.sbin/rtsold/rtsock.c +++ b/usr.sbin/rtsold/rtsock.c @@ -103,7 +103,7 @@ rtsock_input(int s) lim = msg + n; for (next = msg; next < lim; next += len) { - rtm = (struct rt_msghdr *)next; + rtm = (struct rt_msghdr *)(void *)next; if (lim - next < lenlim) break; len = rtm->rtm_msglen; @@ -138,7 +138,7 @@ static int rtsock_input_ifannounce(int s __unused, struct rt_msghdr *rtm, char *lim) { struct if_announcemsghdr *ifan; - struct ifinfo *ifinfo; + struct ifinfo *ifi; ifan = (struct if_announcemsghdr *)rtm; if ((char *)(ifan + 1) > lim) @@ -158,14 +158,14 @@ rtsock_input_ifannounce(int s __unused, struct rt_msghdr *rtm, char *lim) case IFAN_DEPARTURE: warnmsg(LOG_WARNING, __func__, "interface %s removed", ifan->ifan_name); - ifinfo = find_ifinfo(ifan->ifan_index); - if (ifinfo) { + ifi = find_ifinfo(ifan->ifan_index); + if (ifi) { if (dflag > 1) { warnmsg(LOG_INFO, __func__, "bring interface %s to DOWN state", ifan->ifan_name); } - ifinfo->state = IFS_DOWN; + ifi->state = IFS_DOWN; } break; } diff --git a/usr.sbin/rtsold/rtsol.c b/usr.sbin/rtsold/rtsol.c index f7210f66669..c9b3d44158a 100644 --- a/usr.sbin/rtsold/rtsol.c +++ b/usr.sbin/rtsold/rtsol.c @@ -205,7 +205,7 @@ sendpacket(struct ifinfo *ifi) cm->cmsg_level = IPPROTO_IPV6; cm->cmsg_type = IPV6_PKTINFO; cm->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo)); - pi = (struct in6_pktinfo *)CMSG_DATA(cm); + pi = (struct in6_pktinfo *)(void *)CMSG_DATA(cm); memset(&pi->ipi6_addr, 0, sizeof(pi->ipi6_addr)); /*XXX*/ pi->ipi6_ifindex = ifi->sdl->sdl_index; @@ -275,13 +275,13 @@ rtsol_input(int s) if (cm->cmsg_level == IPPROTO_IPV6 && cm->cmsg_type == IPV6_PKTINFO && cm->cmsg_len == CMSG_LEN(sizeof(struct in6_pktinfo))) { - pi = (struct in6_pktinfo *)(CMSG_DATA(cm)); + pi = (struct in6_pktinfo *)(void *)(CMSG_DATA(cm)); ifindex = pi->ipi6_ifindex; } if (cm->cmsg_level == IPPROTO_IPV6 && cm->cmsg_type == IPV6_HOPLIMIT && cm->cmsg_len == CMSG_LEN(sizeof(int))) - hlimp = (int *)CMSG_DATA(cm); + hlimp = (int *)(void *)CMSG_DATA(cm); } if (ifindex == 0) { @@ -417,7 +417,7 @@ rtsol_input(int s) break; } - addr = (struct in6_addr *)(raoptp + sizeof(*rdnss)); + addr = (struct in6_addr *)(void *)(raoptp + sizeof(*rdnss)); while ((char *)addr < (char *)RA_OPT_NEXT_HDR(raoptp)) { if (inet_ntop(AF_INET6, addr, ntopbuf, sizeof(ntopbuf)) == NULL) { diff --git a/usr.sbin/rtsold/rtsold.c b/usr.sbin/rtsold/rtsold.c index e62e9ff5ca9..a97b884cc58 100644 --- a/usr.sbin/rtsold/rtsold.c +++ b/usr.sbin/rtsold/rtsold.c @@ -67,7 +67,6 @@ #define RTSOL_DUMPFILE "/var/run/rtsold.dump"; #define RTSOL_PIDFILE "/var/run/rtsold.pid"; -struct ifinfo *iflist; struct timespec tm_max; static int log_upto = 999; static int fflag = 0; diff --git a/usr.sbin/rtsold/rtsold.h b/usr.sbin/rtsold/rtsold.h index 69f9c352fe9..56b4185eb0e 100644 --- a/usr.sbin/rtsold/rtsold.h +++ b/usr.sbin/rtsold/rtsold.h @@ -176,6 +176,7 @@ extern int getinet6sysctl(int); extern int setinet6sysctl(int, int); /* rtsol.c */ +extern int rssock; extern int sockopen(void); extern void sendpacket(struct ifinfo *); extern void rtsol_input(int); diff --git a/usr.sbin/rwhod/rwhod.c b/usr.sbin/rwhod/rwhod.c index 26b0500c800..87a41667be1 100644 --- a/usr.sbin/rwhod/rwhod.c +++ b/usr.sbin/rwhod/rwhod.c @@ -279,7 +279,12 @@ main(int argc, char *argv[]) } else if (pid_child_receiver > 0) { sender_process(); } else if (pid_child_receiver == -1) { - syslog(LOG_ERR, "pdfork: %m"); + if (errno == ENOSYS) { + syslog(LOG_ERR, + "The pdfork(2) system call is not available; recompile the kernel with options PROCDESC"); + } else { + syslog(LOG_ERR, "pdfork: %m"); + } exit(1); } } else { @@ -328,8 +333,11 @@ verify(char *name, int maxlen) size = 0; while (*name != '\0' && size < maxlen - 1) { - if (!isascii(*name) || !isalnum(*name) || ispunct(*name)) + if (!isascii((unsigned char)*name) || + !(isalnum((unsigned char)*name) || + ispunct((unsigned char)*name))) { return (0); + } name++; size++; } @@ -342,6 +350,7 @@ receiver_process(void) { struct sockaddr_in from; struct stat st; + cap_rights_t rights; char path[64]; int dirfd; struct whod wd; @@ -355,8 +364,9 @@ receiver_process(void) syslog(LOG_WARNING, "%s: %m", _PATH_RWHODIR); exit(1); } - if (cap_rights_limit(dirfd, CAP_CREATE | CAP_WRITE | CAP_FTRUNCATE | - CAP_SEEK | CAP_LOOKUP | CAP_FSTAT) < 0 && errno != ENOSYS) { + cap_rights_init(&rights, CAP_CREATE, CAP_FSTAT, CAP_FTRUNCATE, + CAP_LOOKUP, CAP_SEEK, CAP_WRITE); + if (cap_rights_limit(dirfd, &rights) < 0 && errno != ENOSYS) { syslog(LOG_WARNING, "cap_rights_limit: %m"); exit(1); } @@ -401,8 +411,8 @@ receiver_process(void) syslog(LOG_WARNING, "%s: %m", path); continue; } - if (cap_rights_limit(whod, CAP_WRITE | CAP_FTRUNCATE | - CAP_FSTAT) < 0 && errno != ENOSYS) { + cap_rights_init(&rights, CAP_FSTAT, CAP_FTRUNCATE, CAP_WRITE); + if (cap_rights_limit(whod, &rights) < 0 && errno != ENOSYS) { syslog(LOG_WARNING, "cap_rights_limit: %m"); exit(1); } diff --git a/usr.sbin/setfib/setfib.1 b/usr.sbin/setfib/setfib.1 index f0143db9340..5c36748343f 100644 --- a/usr.sbin/setfib/setfib.1 +++ b/usr.sbin/setfib/setfib.1 @@ -24,7 +24,7 @@ .\" .\" $FreeBSD$ .\" -.Dd April 9, 2008 +.Dd October 20, 2008 .Dt SETFIB 1 .Os .Sh NAME @@ -39,11 +39,11 @@ .Sh DESCRIPTION The .Nm -utility runs +utility runs another .Ar utility -with an different routing table. +with a different routing table. The table number -.Dq Ar fib +.Ar fib will be used by default for all sockets started by this process or descendants. .Sh ENVIRONMENT @@ -69,8 +69,8 @@ An exit status of 127 indicates .Ar utility could not be found. .Sh EXAMPLES -Execute utility -.Sq netstat +Run +.Xr netstat 1 to view the second routing table. .Pp .Dl "setfib -F 1 netstat -rn" @@ -86,8 +86,9 @@ The .Nm utility is a .Fx -specific extension, however many -.Ux +specific extension. +However, many +.Ux Ns - Ns like systems have an equivalent function. .Sh HISTORY diff --git a/usr.sbin/unbound/Makefile b/usr.sbin/unbound/Makefile new file mode 100644 index 00000000000..2672d237632 --- /dev/null +++ b/usr.sbin/unbound/Makefile @@ -0,0 +1,5 @@ +# $FreeBSD$ + +SUBDIR= daemon anchor checkconf control + +.include diff --git a/usr.sbin/unbound/Makefile.inc b/usr.sbin/unbound/Makefile.inc new file mode 100644 index 00000000000..425d7197851 --- /dev/null +++ b/usr.sbin/unbound/Makefile.inc @@ -0,0 +1,5 @@ +# $FreeBSD$ + +NO_WERROR= true + +.include "../Makefile.inc" diff --git a/usr.sbin/unbound/anchor/Makefile b/usr.sbin/unbound/anchor/Makefile new file mode 100644 index 00000000000..14047662b3b --- /dev/null +++ b/usr.sbin/unbound/anchor/Makefile @@ -0,0 +1,18 @@ +# $FreeBSD$ + +# Vendor sources and generated files +LDNSDIR= ${.CURDIR}/../../../contrib/ldns +UNBOUNDDIR= ${.CURDIR}/../../../contrib/unbound +EXPATDIR= ${.CURDIR}/../../../contrib/expat + +.PATH: ${UNBOUNDDIR} ${UNBOUNDDIR}/smallapp ${UNBOUNDDIR}/doc + +PROG= unbound-anchor +SRCS= unbound-anchor.c +CFLAGS= -I${UNBOUNDDIR} -I${LDNSDIR} -I${EXPATDIR}/lib +DPADD= ${LIBUNBOUND} ${LIBLDNS} ${LIBUTIL} ${LIBBSDXML} ${LIBSSL} ${LIBCRYPTO} ${LIBPTHREAD} +LDADD= -lunbound -lldns -lutil -lbsdxml -lssl -lcrypto -lpthread +USEPRIVATELIB= ldns +MAN= unbound-anchor.8 + +.include diff --git a/usr.sbin/unbound/checkconf/Makefile b/usr.sbin/unbound/checkconf/Makefile new file mode 100644 index 00000000000..6a588066cbd --- /dev/null +++ b/usr.sbin/unbound/checkconf/Makefile @@ -0,0 +1,17 @@ +# $FreeBSD$ + +# Vendor sources and generated files +LDNSDIR= ${.CURDIR}/../../../contrib/ldns +UNBOUNDDIR= ${.CURDIR}/../../../contrib/unbound + +.PATH: ${UNBOUNDDIR} ${UNBOUNDDIR}/smallapp ${UNBOUNDDIR}/doc + +PROG= unbound-checkconf +SRCS= unbound-checkconf.c worker_cb.c +CFLAGS= -I${UNBOUNDDIR} -I${LDNSDIR} +DPADD= ${LIBUNBOUND} ${LIBLDNS} ${LIBUTIL} ${LIBSSL} ${LIBCRYPTO} ${LIBPTHREAD} +LDADD= -lunbound -lldns -lutil -lssl -lcrypto -lpthread +USEPRIVATELIB= ldns +MAN= unbound-checkconf.8 + +.include diff --git a/usr.sbin/unbound/control/Makefile b/usr.sbin/unbound/control/Makefile new file mode 100644 index 00000000000..7056a9e24ed --- /dev/null +++ b/usr.sbin/unbound/control/Makefile @@ -0,0 +1,18 @@ +# $FreeBSD$ + +# Vendor sources and generated files +LDNSDIR= ${.CURDIR}/../../../contrib/ldns +UNBOUNDDIR= ${.CURDIR}/../../../contrib/unbound + +.PATH: ${UNBOUNDDIR} ${UNBOUNDDIR}/smallapp ${UNBOUNDDIR}/doc + +PROG= unbound-control +SCRIPTS= unbound-control-setup.sh +SRCS= unbound-control.c worker_cb.c +CFLAGS= -I${UNBOUNDDIR} -I${LDNSDIR} +DPADD= ${LIBUNBOUND} ${LIBLDNS} ${LIBUTIL} ${LIBSSL} ${LIBCRYPTO} ${LIBPTHREAD} +LDADD= -lunbound -lldns -lutil -lssl -lcrypto -lpthread +USEPRIVATELIB= ldns +MAN= unbound-control.8 + +.include diff --git a/usr.sbin/unbound/daemon/Makefile b/usr.sbin/unbound/daemon/Makefile new file mode 100644 index 00000000000..0f9044f885a --- /dev/null +++ b/usr.sbin/unbound/daemon/Makefile @@ -0,0 +1,17 @@ +# $FreeBSD$ + +# Vendor sources and generated files +LDNSDIR= ${.CURDIR}/../../../contrib/ldns +UNBOUNDDIR= ${.CURDIR}/../../../contrib/unbound + +.PATH: ${UNBOUNDDIR} ${UNBOUNDDIR}/daemon ${UNBOUNDDIR}/doc + +PROG= unbound +SRCS= acl_list.c cachedump.c daemon.c remote.c stats.c unbound.c worker.c +CFLAGS= -I${UNBOUNDDIR} -I${LDNSDIR} +DPADD= ${LIBUNBOUND} ${LIBLDNS} ${LIBUTIL} ${LIBSSL} ${LIBCRYPTO} ${LIBPTHREAD} +LDADD= -lunbound -lldns -lutil -lssl -lcrypto -lpthread +USEPRIVATELIB= ldns +MAN= unbound.8 unbound.conf.5 + +.include diff --git a/usr.sbin/watch/watch.c b/usr.sbin/watch/watch.c index e69534de55c..eecf0d32db2 100644 --- a/usr.sbin/watch/watch.c +++ b/usr.sbin/watch/watch.c @@ -110,7 +110,6 @@ set_tty(void) { struct termios ntty; - tcgetattr(std_in, &otty); ntty = otty; ntty.c_lflag &= ~ICANON; /* disable canonical operation */ ntty.c_lflag &= ~ECHO; @@ -324,6 +323,8 @@ main(int ac, char *av[]) usage(); } + tcgetattr(std_in, &otty); + if (modfind("snp") == -1) if (kldload("snp") == -1 || modfind("snp") == -1) warn("snp module not available"); diff --git a/usr.sbin/watchdogd/watchdogd.c b/usr.sbin/watchdogd/watchdogd.c index 9b4440c3b49..5fd16f56964 100644 --- a/usr.sbin/watchdogd/watchdogd.c +++ b/usr.sbin/watchdogd/watchdogd.c @@ -60,7 +60,8 @@ __FBSDID("$FreeBSD$"); #include -static long fetchtimeout(int opt, const char *longopt, const char *myoptarg); +static long fetchtimeout(int opt, + const char *longopt, const char *myoptarg, int zero_ok); static void parseargs(int, char *[]); static int seconds_to_pow2ns(int); static void sighandler(int); @@ -219,7 +220,7 @@ parse_timeout_to_pow2ns(char opt, const char *longopt, const char *myoptarg) if (!longopt) shortopt[1] = opt; - a = fetchtimeout(opt, longopt, myoptarg); + a = fetchtimeout(opt, longopt, myoptarg, 1); if (a == 0) rv = WD_TO_NEVER; @@ -487,7 +488,7 @@ usage(void) } static long -fetchtimeout(int opt, const char *longopt, const char *myoptarg) +fetchtimeout(int opt, const char *longopt, const char *myoptarg, int zero_ok) { const char *errstr; char *p; @@ -499,7 +500,7 @@ fetchtimeout(int opt, const char *longopt, const char *myoptarg) rv = strtol(myoptarg, &p, 0); if ((p != NULL && *p != '\0') || errno != 0) errstr = "is not a number"; - if (rv <= 0) + if (rv < 0 || (!zero_ok && rv == 0)) errstr = "must be greater than zero"; if (errstr) { if (longopt) @@ -721,7 +722,7 @@ parseargs(int argc, char *argv[]) break; #endif case 's': - nap = fetchtimeout(c, NULL, optarg); + nap = fetchtimeout(c, NULL, optarg, 0); break; case 'S': do_syslog = 0; @@ -734,7 +735,8 @@ parseargs(int argc, char *argv[]) timeout); break; case 'T': - carp_thresh_seconds = fetchtimeout(c, "NULL", optarg); + carp_thresh_seconds = + fetchtimeout(c, "NULL", optarg, 0); break; case 'w': do_timedog = 1; @@ -742,7 +744,7 @@ parseargs(int argc, char *argv[]) case 0: lopt = longopts[longindex].name; if (!strcmp(lopt, "pretimeout")) { - pretimeout = fetchtimeout(0, lopt, optarg); + pretimeout = fetchtimeout(0, lopt, optarg, 0); } else if (!strcmp(lopt, "pretimeout-action")) { pretimeout_act = timeout_act_str2int(lopt, optarg);